diff --git a/agents/hermes/Dockerfile b/agents/hermes/Dockerfile index 35f78abd726..3c982233f17 100644 --- a/agents/hermes/Dockerfile +++ b/agents/hermes/Dockerfile @@ -94,6 +94,7 @@ COPY agents/hermes/patch-cron-restore-drain.py /opt/nemoclaw-hermes-config/patch COPY agents/hermes/patch-neutral-platform-env-activation.py /opt/nemoclaw-hermes-config/patch-neutral-platform-env-activation.py COPY agents/hermes/host/managed-tool-gateway-matrix.json /opt/nemoclaw-hermes-config/managed-tool-gateway-matrix.json COPY src/lib/hermes-managed-route.ts /src/lib/hermes-managed-route.ts +COPY src/lib/hermes-switchyard-routing.ts /src/lib/hermes-switchyard-routing.ts COPY src/lib/tool-disclosure.ts /src/lib/tool-disclosure.ts COPY src/lib/messaging/ /src/lib/messaging/ COPY scripts/lib/openclaw-npm-remediation.mts /scripts/lib/openclaw-npm-remediation.mts @@ -405,7 +406,7 @@ RUN chmod -R a+rX /opt/nemoclaw-hermes-plugin/ # read-only. RUN find /opt/nemoclaw-hermes-config -type d -exec chmod 755 {} + \ && find /opt/nemoclaw-hermes-config -type f -exec chmod 444 {} + \ - && chmod 444 /src/lib/hermes-managed-route.ts /src/lib/tool-disclosure.ts \ + && chmod 444 /src/lib/hermes-managed-route.ts /src/lib/hermes-switchyard-routing.ts /src/lib/tool-disclosure.ts \ && chmod 444 /scripts/lib/reviewed-npm-archive.mts /scripts/lib/bundled-npm-package.mts \ /scripts/lib/openclaw-npm-remediation.mts \ /scripts/patch-bundled-npm-brace-expansion.mts /scripts/lib/patch-bundled-npm-ip-address.mts \ @@ -704,7 +705,7 @@ RUN node --experimental-strip-types \ ARG NEMOCLAW_HERMES_WRAPPER_SHA256=f4276e9833638b7a620176c88bd329d6b6d4948538a3227b727a1397146a0e0e ARG NEMOCLAW_HERMES_CLI_ADAPTER_SHA256=989edf54a8c09c6efb348600a8aa2f264c0b71408eb9d7bcd579b92cbeccf9b1 ARG NEMOCLAW_HERMES_CLI_ADAPTER_VALIDATOR_SHA256=db4046e79e513eab67b069a8eda20167b8b65529cf26842531d2ad673c670330 -ARG NEMOCLAW_HERMES_VALIDATOR_SHA256=b355d1365fb1d15475e327f312ceb854ae96f9ebed28cf96bc8817f550df2688 +ARG NEMOCLAW_HERMES_VALIDATOR_SHA256=104443a6b509844e8c2cfda06634664dd608b786b8cc036343d10c50937a2adc ARG NEMOCLAW_HERMES_TIRITH_FINALIZER_SHA256=a1e6b1c53ab297569abb87c29d15c294d729e46005bfd022136b4c447a791819 ARG NEMOCLAW_HERMES_CRON_RESTORE_CONTROLLER_SHA256=e8593cf1580bffa4663e91c079ba0ce31c3d26391f5b1718872701138ce250b0 # hadolint ignore=DL4006 diff --git a/agents/hermes/config/build-env.ts b/agents/hermes/config/build-env.ts index 2f2b4e4f65c..1cf68444dff 100644 --- a/agents/hermes/config/build-env.ts +++ b/agents/hermes/config/build-env.ts @@ -2,7 +2,12 @@ // SPDX-License-Identifier: Apache-2.0 import { Buffer } from "node:buffer"; +import { TextDecoder } from "node:util"; +import { + type HermesSwitchyardRouting, + validateHermesSwitchyardRouting, +} from "../../../src/lib/hermes-switchyard-routing.ts"; import { normalizeProviderPlaceholderForEnvKey } from "../../../src/lib/messaging/provider-placeholders.ts"; import { readToolDisclosureEnv } from "../../../src/lib/tool-disclosure.ts"; import { isObjectRecord } from "./object-record.ts"; @@ -31,6 +36,7 @@ export type HermesBuildSettings = { brokerEnabled: boolean; presets: string[]; }; + switchyardRouting?: HermesSwitchyardRouting | null; }; /** Read and validate the environment consumed by the Hermes config generator. */ @@ -60,9 +66,33 @@ export function readHermesBuildSettings(env: NodeJS.ProcessEnv): HermesBuildSett brokerEnabled: env.NEMOCLAW_HERMES_TOOL_GATEWAY_BROKER === "1", presets: readBase64Json(env, "NEMOCLAW_HERMES_TOOL_GATEWAY_PRESETS_B64", "W10="), }, + switchyardRouting: readSwitchyardRouting(env), }; } +function readSwitchyardRouting(env: NodeJS.ProcessEnv): HermesSwitchyardRouting | null { + const encoded = env.NEMOCLAW_HERMES_SWITCHYARD_ROUTING_B64; + if (encoded === undefined || encoded === "") return null; + try { + if ( + encoded.length > 128 * 1024 || + !/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/u.test(encoded) + ) { + throw new Error("transport is not canonical base64"); + } + const bytes = Buffer.from(encoded, "base64"); + if (bytes.toString("base64") !== encoded) { + throw new Error("transport is not canonical base64"); + } + return validateHermesSwitchyardRouting( + JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes)) as unknown, + ); + } catch (error) { + const message = error instanceof Error ? error.message : "unknown validation failure"; + throw new Error(`NEMOCLAW_HERMES_SWITCHYARD_ROUTING_B64 is invalid: ${message}`); + } +} + function readBooleanBuildFlag(env: NodeJS.ProcessEnv, name: string): boolean { const value = env[name] ?? "0"; if (value !== "0" && value !== "1") { diff --git a/agents/hermes/config/generate.ts b/agents/hermes/config/generate.ts index c2ad8653190..5aa58b1a29a 100644 --- a/agents/hermes/config/generate.ts +++ b/agents/hermes/config/generate.ts @@ -48,11 +48,20 @@ export function generateHermesConfig({ const config = policy.config; const envLines = policy.env_lines; finalizeHermesPlatformToolsets(config, settings); - const written = writeHermesConfigFiles(config, envLines, policy, homeDir); + const written = writeHermesConfigFiles( + config, + envLines, + policy, + settings.switchyardRouting ?? null, + homeDir, + ); log(`[config] Wrote ${written.configPath} (model=${settings.model}, provider=custom)`); log(`[config] Wrote ${written.envPath} (${written.envEntryCount} entries)`); log(`[config] Wrote ${written.policyPath} (schema=${policy.schema_version})`); + if (written.relayPluginsPath !== null) { + log(`[config] Wrote ${written.relayPluginsPath} (native Relay/Switchyard configuration)`); + } return { settings, config, envLines, policy, written }; } diff --git a/agents/hermes/config/hermes-env.ts b/agents/hermes/config/hermes-env.ts index 7878b32da62..ae8ced1f934 100644 --- a/agents/hermes/config/hermes-env.ts +++ b/agents/hermes/config/hermes-env.ts @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 import type { HermesBuildSettings } from "./build-env.ts"; +import { HERMES_SWITCHYARD_RELAY_TOML } from "../../../src/lib/hermes-switchyard-routing.ts"; import { effectiveManagedToolGatewayPresets, loadManagedToolGatewayMatrix, @@ -15,6 +16,10 @@ export function buildHermesEnvLines( ): string[] { const envLines = ["API_SERVER_PORT=18642", "API_SERVER_HOST=127.0.0.1"]; + if (settings.switchyardRouting != null) { + envLines.push(`HERMES_NEMO_RELAY_PLUGINS_TOML=${HERMES_SWITCHYARD_RELAY_TOML}`); + } + for (const { envKey, placeholder } of settings.messagingCredentialPlaceholders) { envLines.push(`${envKey}=${placeholder}`); } diff --git a/agents/hermes/config/write-config.ts b/agents/hermes/config/write-config.ts index 9a17d5bb2ac..f652ecc53f6 100644 --- a/agents/hermes/config/write-config.ts +++ b/agents/hermes/config/write-config.ts @@ -1,10 +1,14 @@ // SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 -import { chmodSync, writeFileSync } from "node:fs"; +import { chmodSync, rmSync, writeFileSync } from "node:fs"; import { homedir } from "node:os"; import { join } from "node:path"; import type { HermesManagedPolicyV1 } from "./managed-policy.ts"; +import { + serializeHermesSwitchyardRelayToml, + type HermesSwitchyardRouting, +} from "../../../src/lib/hermes-switchyard-routing.ts"; import { buildHermesUpstreamHeader } from "./upstream-header.ts"; import { toYaml } from "./yaml.ts"; @@ -13,12 +17,14 @@ export type WrittenHermesConfig = { envPath: string; envEntryCount: number; policyPath: string; + relayPluginsPath: string | null; }; export function writeHermesConfigFiles( config: Record, envLines: string[], policy: HermesManagedPolicyV1, + switchyardRouting: HermesSwitchyardRouting | null, homeDir: string = homedir(), ): WrittenHermesConfig { const configPath = join(homeDir, ".hermes", "config.yaml"); @@ -33,10 +39,21 @@ export function writeHermesConfigFiles( writeFileSync(policyPath, `${JSON.stringify(policy, null, 2)}\n`); chmodSync(policyPath, 0o600); + const generatedRelayPluginsPath = join(homeDir, ".hermes", "relay-plugins.toml"); + let relayPluginsPath: string | null = null; + if (switchyardRouting === null) { + rmSync(generatedRelayPluginsPath, { force: true }); + } else { + writeFileSync(generatedRelayPluginsPath, serializeHermesSwitchyardRelayToml(switchyardRouting)); + chmodSync(generatedRelayPluginsPath, 0o600); + relayPluginsPath = generatedRelayPluginsPath; + } + return { configPath, envPath, envEntryCount: envLines.length, policyPath, + relayPluginsPath, }; } diff --git a/agents/hermes/validate-env-secret-boundary.py b/agents/hermes/validate-env-secret-boundary.py index e1a6fd71961..6686fc94ca4 100755 --- a/agents/hermes/validate-env-secret-boundary.py +++ b/agents/hermes/validate-env-secret-boundary.py @@ -19,6 +19,7 @@ import argparse import errno import grp +import json import os import pwd import re @@ -28,9 +29,15 @@ from contextlib import contextmanager from typing import Iterable, TextIO -SECRET_KEY_RE = re.compile(r"(^|_)(TOKEN|KEY|SECRET|PASSWORD|CREDENTIAL|API)(_|$)") +SECRET_KEY_RE = re.compile( + r"(^|_)(TOKEN|KEY|SECRET|PASSWORD|CREDENTIAL|API|AUTHORIZATION)(_|$)" +) PLACEHOLDER_RE = re.compile(r"^(xoxb|xapp)-OPENSHELL-RESOLVE-ENV-[A-Z0-9_]+$") +REVISION_BOUND_RESOLVER_RE = re.compile( + r"^openshell:resolve:env:v([0-9]{1,20})_([A-Z][A-Z0-9_]{0,127})$" +) KEY_NAME_RE = re.compile(r"[A-Za-z_][A-Za-z0-9_]*") +SWITCHYARD_ENV_KEY_RE = re.compile(r"SWITCHYARD_[A-Z][A-Z0-9_]{0,111}") API_SERVER_KEY_RE = re.compile(r"^[0-9a-f]{64}$") HERMES_API_PORT_RANGE_START = 8642 HERMES_API_PORT_RANGE_END = 8652 @@ -73,6 +80,10 @@ ) INSTALLED_ENV_ROOT = "/sandbox" INSTALLED_ENV_PATH = "/sandbox/.hermes/.env" +INSTALLED_SWITCHYARD_RUNTIME_BINDINGS = ( + "/usr/local/share/nemoclaw/hermes-switchyard-runtime-bindings.json" +) +MAX_SWITCHYARD_RUNTIME_BINDINGS_BYTES = 16 * 1024 class UnsafeEnvInputError(RuntimeError): @@ -470,6 +481,11 @@ def validate_env_file(path: str) -> int: key = key.strip() if not KEY_NAME_RE.fullmatch(key): continue + if key.startswith("SWITCHYARD_"): + violation_count += 1 + if len(violations) < MAX_VIOLATIONS: + violations.append(f"{key} (line {lineno})") + continue if key in OPENSHELL_SUPERVISOR_ONLY_ENV_KEYS: violation_count += 1 if len(violations) < MAX_VIOLATIONS: @@ -508,15 +524,289 @@ def validate_env_file(path: str) -> int: return 1 +def _is_installed_boundary_validator() -> bool: + return os.path.realpath(__file__) == INSTALLED_BOUNDARY_VALIDATOR + + +def _switchyard_installed_owner() -> tuple[int, int]: + return 0, 0 + + +def _validate_switchyard_directory_descriptor( + fd: int, installed_mode: bool +) -> tuple[int, int, int, int, int]: + st = os.fstat(fd) + if not stat.S_ISDIR(st.st_mode): + raise UnsafeEnvInputError( + "the Switchyard runtime bindings have an unsafe ancestor" + ) + mode = stat.S_IMODE(st.st_mode) + if installed_mode: + trusted = ( + (st.st_uid, st.st_gid) == _switchyard_installed_owner() + and mode & 0o022 == 0 + ) + else: + trusted = st.st_uid in _allowed_path_owner_uids() and not ( + mode & 0o002 and not mode & stat.S_ISVTX + ) + if not trusted: + raise UnsafeEnvInputError( + "the Switchyard runtime bindings have an unsafe ancestor" + ) + return _directory_identity(st) + + +def _verify_switchyard_path_chain( + file_fd: int, + expected_file_identity: tuple[int, ...], + final_directory_fd: int, + chain: list[tuple[int, str, int, tuple[int, int, int, int, int]]], + basename: str, +) -> None: + for parent_fd, component, child_fd, expected in chain: + if _directory_identity(os.fstat(child_fd)) != expected: + raise UnsafeEnvInputError( + "the Switchyard runtime bindings changed while they were read" + ) + current = os.stat(component, dir_fd=parent_fd, follow_symlinks=False) + if ( + not stat.S_ISDIR(current.st_mode) + or _directory_identity(current) != expected + ): + raise UnsafeEnvInputError( + "the Switchyard runtime bindings changed while they were read" + ) + current_file = os.stat( + basename, dir_fd=final_directory_fd, follow_symlinks=False + ) + if ( + not stat.S_ISREG(current_file.st_mode) + or _file_identity(current_file) != expected_file_identity + or _file_identity(os.fstat(file_fd)) != expected_file_identity + ): + raise UnsafeEnvInputError( + "the Switchyard runtime bindings changed while they were read" + ) + + +def _read_switchyard_runtime_bindings(path: str) -> bytes | None: + """Read the routing env-key manifest through a stable descriptor chain.""" + + installed_mode = _is_installed_boundary_validator() + if installed_mode and path != INSTALLED_SWITCHYARD_RUNTIME_BINDINGS: + raise UnsafeEnvInputError( + "the installed validator only accepts its canonical Switchyard runtime bindings" + ) + if not os.path.isabs(path): + raise UnsafeEnvInputError( + "the Switchyard runtime bindings path must be absolute" + ) + # Source-checkout tests may exercise an explicit temporary manifest. Resolve + # only that development parent before pinning every component descriptor; + # the installed validator never resolves or accepts an alternate path. + if installed_mode: + # The Hermes sandbox policy deliberately denies opening `/`. Anchor at + # the fixed image-owned directory and verify that descriptor plus the + # canonical basename instead of traversing from the filesystem root. + root_path = os.path.dirname(INSTALLED_SWITCHYARD_RUNTIME_BINDINGS) + components = [os.path.basename(INSTALLED_SWITCHYARD_RUNTIME_BINDINGS)] + else: + normalized = os.path.join( + os.path.realpath(os.path.dirname(path)), os.path.basename(path) + ) + root_path = os.sep + components = [component for component in normalized.split(os.sep) if component] + if not components: + raise UnsafeEnvInputError( + "the Switchyard runtime bindings path has no file component" + ) + + nofollow = getattr(os, "O_NOFOLLOW", 0) + cloexec = getattr(os, "O_CLOEXEC", 0) + directory_flags = os.O_RDONLY | os.O_DIRECTORY | nofollow | cloexec + file_flags = ( + os.O_RDONLY | getattr(os, "O_NONBLOCK", 0) | nofollow | cloexec + ) + directory_fds: list[int] = [] + chain: list[tuple[int, str, int, tuple[int, int, int, int, int]]] = [] + file_fd = -1 + try: + root_fd = os.open(root_path, directory_flags) # codeql[py/file-not-closed] + try: + directory_fds.append(root_fd) + except BaseException: + os.close(root_fd) + raise + root_identity = _validate_switchyard_directory_descriptor( + root_fd, installed_mode + ) + chain.append((root_fd, ".", root_fd, root_identity)) + current_fd = root_fd + for component in components[:-1]: + child_fd = os.open( # codeql[py/file-not-closed] + component, directory_flags, dir_fd=current_fd + ) + try: + directory_fds.append(child_fd) + except BaseException: + os.close(child_fd) + raise + identity = _validate_switchyard_directory_descriptor( + child_fd, installed_mode + ) + chain.append((current_fd, component, child_fd, identity)) + current_fd = child_fd + + basename = components[-1] + file_fd = os.open(basename, file_flags, dir_fd=current_fd) + before = os.fstat(file_fd) + mode = stat.S_IMODE(before.st_mode) + trusted_metadata = ( + (before.st_uid, before.st_gid) == _switchyard_installed_owner() + and mode == 0o444 + if installed_mode + else before.st_uid in _allowed_path_owner_uids() and mode & 0o022 == 0 + ) + if ( + not stat.S_ISREG(before.st_mode) + or before.st_nlink != 1 + or not trusted_metadata + or before.st_size < 1 + or before.st_size > MAX_SWITCHYARD_RUNTIME_BINDINGS_BYTES + ): + raise UnsafeEnvInputError( + "the installed Switchyard runtime bindings have unsafe metadata" + ) + chunks: list[bytes] = [] + remaining = before.st_size + while remaining > 0: + chunk = os.read(file_fd, min(remaining, 64 * 1024)) + if not chunk: + break + chunks.append(chunk) + remaining -= len(chunk) + if remaining != 0 or os.read(file_fd, 1): + raise UnsafeEnvInputError( + "the Switchyard runtime bindings changed while they were read" + ) + _verify_switchyard_path_chain( + file_fd, _file_identity(before), current_fd, chain, basename + ) + return b"".join(chunks) + except FileNotFoundError: + return None + except OSError as exc: + raise UnsafeEnvInputError( + "the installed Switchyard runtime bindings are unreadable or unsafe" + ) from exc + finally: + if file_fd != -1: + os.close(file_fd) + for fd in reversed(directory_fds): + os.close(fd) + + +def _switchyard_runtime_bindings( + path: str = INSTALLED_SWITCHYARD_RUNTIME_BINDINGS, +) -> list[str] | None: + raw = _read_switchyard_runtime_bindings(path) + if raw is None: + return None + try: + document = json.loads(raw.decode("utf-8")) + targets = document.get("targets") if isinstance(document, dict) else None + if document.get("schemaVersion") != 1 or not isinstance(targets, list): + raise ValueError("invalid schema") + bindings: list[str] = [] + seen_env_keys: set[str] = set() + if [target.get("role") for target in targets if isinstance(target, dict)] != [ + "judge", + "weak", + "strong", + ]: + raise ValueError("invalid roles") + for target in targets: + header_env = target.get("headerEnv") + if not isinstance(header_env, list) or not 1 <= len(header_env) <= 8: + raise ValueError("invalid header bindings") + for binding in header_env: + env_key = binding.get("envKey") if isinstance(binding, dict) else None + if ( + not isinstance(env_key, str) + or SWITCHYARD_ENV_KEY_RE.fullmatch(env_key) is None + or env_key in seen_env_keys + ): + raise ValueError("invalid env key") + seen_env_keys.add(env_key) + bindings.append(env_key) + return bindings + except (AttributeError, TypeError, UnicodeDecodeError, ValueError) as exc: + raise UnsafeEnvInputError( + "the installed Switchyard runtime bindings have an invalid contract" + ) from exc + + +def validate_switchyard_runtime_env( + env: dict[str, str], + routing_path: str = INSTALLED_SWITCHYARD_RUNTIME_BINDINGS, +) -> list[str]: + """Return redacted violations for the enabled Switchyard provider snapshot.""" + + bindings = _switchyard_runtime_bindings(routing_path) + if bindings is None: + return sorted(key for key in env if key.startswith("SWITCHYARD_")) + violations: list[str] = [] + revisions: set[str] = set() + expected_keys = set(bindings) + violations.extend( + key + for key in env + if key.startswith("SWITCHYARD_") and key not in expected_keys + ) + for env_key in bindings: + value = env.get(env_key) + match = REVISION_BOUND_RESOLVER_RE.fullmatch(value or "") + if match is None or match.group(2) != env_key: + violations.append(env_key) + continue + revisions.add(match.group(1)) + if len(revisions) > 1: + violations.extend(bindings) + return sorted(set(violations)) + + +def _emit_switchyard_contract_failure() -> None: + print( + "[SECURITY] Refusing Hermes startup because the Switchyard runtime " + "binding contract is missing, malformed, or unsafe.", + file=sys.stderr, + ) + + def validate_runtime_env(env: dict[str, str] | None = None) -> int: source = os.environ if env is None else env violations: list[str] = [] violation_count = 0 + try: + routing_violations = validate_switchyard_runtime_env(dict(source)) + except UnsafeEnvInputError: + _emit_switchyard_contract_failure() + return 1 + for key in routing_violations: + violation_count += 1 + if len(violations) < MAX_VIOLATIONS: + violations.append(key) if source.get("HERMES_LAZY_INSTALL_TARGET") != "/sandbox/.hermes/lazy-packages": violation_count += 1 if len(violations) < MAX_VIOLATIONS: violations.append("HERMES_LAZY_INSTALL_TARGET") for key, value in sorted(source.items()): + if key.startswith("SWITCHYARD_"): + # The routing contract above validates every expected, missing, or + # extra Switchyard binding. Do not count the same key again in the + # generic secret-shaped environment scan. + continue if key in OPENSHELL_SUPERVISOR_ONLY_ENV_KEYS: violation_count += 1 if len(violations) < MAX_VIOLATIONS: @@ -550,7 +840,8 @@ def validate_runtime_env(env: dict[str, str] | None = None) -> int: return 0 _emit_violations( "[SECURITY] Refusing Hermes startup because the process environment " - "contains raw secret-shaped values or OpenShell supervisor-only identity " + "contains raw secret-shaped values, an incomplete or mixed-revision " + "Switchyard provider snapshot, or OpenShell supervisor-only identity " "variables, or does not use the managed HERMES_LAZY_INSTALL_TARGET. " "Store credentials in OpenShell providers and keep only " "openshell resolver placeholders in the sandbox.", @@ -731,6 +1022,11 @@ def main(argv: list[str]) -> int: "runtime-env", help="Validate the current process environment", ) + switchyard_parser = sub.add_parser( + "switchyard-runtime-env", + help="Validate runtime Switchyard bindings from an explicit manifest", + ) + switchyard_parser.add_argument("path", help="Path to the runtime binding manifest") sub.add_parser( "mask-config-output", help="Mask secret-shaped fields on stdin; print to stdout", @@ -740,6 +1036,21 @@ def main(argv: list[str]) -> int: return validate_env_file(args.path) if args.mode == "mask-config-output": return mask_config_output(sys.stdin, sys.stdout) + if args.mode == "switchyard-runtime-env": + try: + violations = validate_switchyard_runtime_env(dict(os.environ), args.path) + except UnsafeEnvInputError: + _emit_switchyard_contract_failure() + return 1 + if not violations: + return 0 + _emit_violations( + "[SECURITY] Refusing Hermes startup because the process environment " + "contains an incomplete or mixed-revision Switchyard provider snapshot.", + violations[:MAX_VIOLATIONS], + max(0, len(violations) - MAX_VIOLATIONS), + ) + return 1 assert args.mode == "runtime-env", ( f"unreachable: argparse subparsers are required ({args.mode!r})" ) diff --git a/src/lib/hermes-switchyard-routing.test.ts b/src/lib/hermes-switchyard-routing.test.ts new file mode 100644 index 00000000000..09c305bff39 --- /dev/null +++ b/src/lib/hermes-switchyard-routing.test.ts @@ -0,0 +1,191 @@ +// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { describe, expect, it } from "vitest"; + +import { + type HermesSwitchyardRouting, + parseHermesSwitchyardRelayToml, + serializeHermesSwitchyardRelayToml, + validateHermesSwitchyardRouting, +} from "./hermes-switchyard-routing"; + +const ROUTING: HermesSwitchyardRouting = { + algorithm: "llm_classifier", + baseThreshold: 0.5, + targets: [ + { + role: "strong", + baseUrl: "https://quality.models.test/v1/", + model: "quality-model", + protocol: "openai_chat", + headerEnv: [ + { + headerName: "X-API-Key", + envKey: "SWITCHYARD_STRONG_X_API_KEY", + }, + { + headerName: "api-key", + envKey: "SWITCHYARD_STRONG_API_KEY", + }, + { + headerName: "Authorization", + envKey: "SWITCHYARD_STRONG_AUTHORIZATION", + }, + ], + }, + { + role: "judge", + baseUrl: "https://judge.models.test/v1", + model: "judge-model", + protocol: "openai_chat", + headerEnv: [ + { + headerName: "authorization", + envKey: "SWITCHYARD_JUDGE_AUTHORIZATION", + }, + ], + }, + { + role: "weak", + baseUrl: "https://fast.models.test/v1", + model: "fast-model", + protocol: "openai_chat", + headerEnv: [ + { + headerName: "authorization", + envKey: "SWITCHYARD_WEAK_AUTHORIZATION", + }, + ], + }, + ], +}; + +describe("Hermes Switchyard routing contract", () => { + it("canonicalizes exactly judge, weak, and strong and emits deterministic fail-closed TOML (#8886)", () => { + const canonical = validateHermesSwitchyardRouting(ROUTING); + const serialized = serializeHermesSwitchyardRelayToml(ROUTING); + + expect(canonical.targets.map(({ role }) => role)).toEqual(["judge", "weak", "strong"]); + expect(canonical.targets[2]?.baseUrl).toBe("https://quality.models.test/v1"); + expect(serializeHermesSwitchyardRelayToml(canonical)).toBe(serialized); + expect(parseHermesSwitchyardRelayToml(serialized).size).toBe(11); + expect(serialized).toContain('manifest = "/opt/switchyard-relay-plugin/relay-plugin.toml"'); + expect(serialized).toContain('failure_mode = "fail_closed"'); + expect(serialized).toContain("[plugins.dynamic.config.targets.strong.header_env]"); + expect(serialized).toContain('"authorization" = "SWITCHYARD_STRONG_AUTHORIZATION"'); + expect(canonical.targets[2]?.headerEnv.map(({ headerName }) => headerName)).toEqual([ + "api-key", + "authorization", + "x-api-key", + ]); + expect(serialized).not.toContain("openshell:resolve:env:"); + expect(serialized).not.toContain("QUALITY_API_KEY"); + }); + + it.each([ + ["zero", 0, "0"], + ["tiny exponent", 5e-7, "5e-7"], + ["minimum canonical decimal", 0.000001, "0.000001"], + ["one", 1, "1"], + ])( + "round-trips the %s base threshold through canonical TOML (#8886)", + (_name, threshold, text) => { + const serialized = serializeHermesSwitchyardRelayToml({ + ...ROUTING, + baseThreshold: threshold, + }); + const parsed = parseHermesSwitchyardRelayToml(serialized); + + expect(serialized).toContain(`base_threshold = ${text}\n`); + expect(parsed.get("plugins.dynamic.config.algorithm")?.get("base_threshold")).toBe(threshold); + }, + ); + + it.each([ + [ + "missing role", + { ...ROUTING, targets: ROUTING.targets.slice(0, 2) }, + "targets must contain exactly judge, weak, and strong", + ], + [ + "duplicate role", + { + ...ROUTING, + targets: [ROUTING.targets[0], ROUTING.targets[1], ROUTING.targets[1]], + }, + "targets contains duplicate role judge", + ], + [ + "HTTP URL", + { + ...ROUTING, + targets: ROUTING.targets.map((target) => + target.role === "weak" ? { ...target, baseUrl: "http://fast.models.test/v1" } : target, + ), + }, + "targets[2].baseUrl must be a credential-free HTTPS URL without query or fragment data", + ], + [ + "URL userinfo", + { + ...ROUTING, + targets: ROUTING.targets.map((target) => + target.role === "weak" + ? { ...target, baseUrl: "https://user:secret@fast.models.test/v1" } + : target, + ), + }, + "targets[2].baseUrl must be a credential-free HTTPS URL without query or fragment data", + ], + [ + "unsafe header env key", + { + ...ROUTING, + targets: ROUTING.targets.map((target) => + target.role === "weak" + ? { + ...target, + headerEnv: [{ ...target.headerEnv[0], envKey: "FAST_API_KEY" }], + } + : target, + ), + }, + "targets[2].headerEnv[0].envKey must be a SWITCHYARD_ prefixed environment key", + ], + [ + "duplicate model", + { + ...ROUTING, + targets: ROUTING.targets.map((target) => + target.role === "weak" ? { ...target, model: "quality-model" } : target, + ), + }, + "targets must use unique model IDs", + ], + [ + "duplicate dispatch URL", + { + ...ROUTING, + targets: ROUTING.targets.map((target) => + target.role === "weak" + ? { ...target, baseUrl: "https://quality.models.test/v1" } + : target, + ), + }, + "targets must use distinct dispatch base URLs", + ], + ])( + "rejects %s before generating native Relay configuration (#8887)", + (_name, candidate, errorFragment) => { + expect(() => validateHermesSwitchyardRouting(candidate)).toThrow(errorFragment); + }, + ); + + it("rejects malformed TOML before root promotion (#8886)", () => { + expect(() => parseHermesSwitchyardRelayToml("version = 1\nversion = 2\n")).toThrow( + /repeats key/, + ); + expect(() => parseHermesSwitchyardRelayToml("version=1\n")).toThrow(/unsupported syntax/); + }); +}); diff --git a/src/lib/hermes-switchyard-routing.ts b/src/lib/hermes-switchyard-routing.ts new file mode 100644 index 00000000000..0ea62e08fff --- /dev/null +++ b/src/lib/hermes-switchyard-routing.ts @@ -0,0 +1,343 @@ +// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { Buffer } from "node:buffer"; + +export const HERMES_SWITCHYARD_PLUGIN_MANIFEST = "/opt/switchyard-relay-plugin/relay-plugin.toml"; +export const HERMES_SWITCHYARD_RELAY_TOML = "/usr/local/share/nemoclaw/hermes-relay-plugins.toml"; +export const HERMES_SWITCHYARD_RUNTIME_BINDINGS = + "/usr/local/share/nemoclaw/hermes-switchyard-runtime-bindings.json"; + +export const HERMES_SWITCHYARD_TARGET_ROLES = ["judge", "weak", "strong"] as const; +export type HermesSwitchyardTargetRole = (typeof HERMES_SWITCHYARD_TARGET_ROLES)[number]; + +export interface HermesSwitchyardHeaderEnvironment { + readonly headerName: string; + readonly envKey: string; +} + +export interface HermesSwitchyardTarget { + readonly role: HermesSwitchyardTargetRole; + readonly baseUrl: string; + readonly model: string; + readonly protocol: "openai_chat"; + readonly headerEnv: readonly HermesSwitchyardHeaderEnvironment[]; +} + +export interface HermesSwitchyardRouting { + readonly algorithm: "llm_classifier"; + readonly baseThreshold: number; + readonly targets: readonly HermesSwitchyardTarget[]; +} + +export class HermesSwitchyardRoutingError extends Error { + constructor(message: string) { + super(`Invalid Hermes Switchyard routing: ${message}`); + this.name = "HermesSwitchyardRoutingError"; + } +} + +const ROUTING_KEYS = new Set(["algorithm", "baseThreshold", "targets"]); +const TARGET_KEYS = new Set(["role", "baseUrl", "model", "protocol", "headerEnv"]); +const HEADER_ENV_KEYS = new Set(["headerName", "envKey"]); +const ROLE_SET = new Set(HERMES_SWITCHYARD_TARGET_ROLES); +const CONTROL_CHARACTER_RE = /[\u0000-\u001f\u007f-\u009f]/u; +const HEADER_NAME_RE = /^[!#$%&'*+.^_`|~0-9A-Za-z-]{1,128}$/u; +const ALLOWED_PROVIDER_HEADER_NAMES = new Set(["api-key", "authorization", "x-api-key"]); +const HEADER_ENV_KEY_RE = /^SWITCHYARD_[A-Z][A-Z0-9_]{0,111}$/u; +const MAX_MODEL_BYTES = 1024; +const MAX_URL_BYTES = 2048; +const MAX_HEADER_ENV_BINDINGS = 8; + +function fail(message: string): never { + throw new HermesSwitchyardRoutingError(message); +} + +function isPlainObject(value: unknown): value is Record { + if (value === null || typeof value !== "object" || Array.isArray(value)) return false; + const prototype = Object.getPrototypeOf(value); + return prototype === Object.prototype || prototype === null; +} + +function record(value: unknown, where: string): Record { + if (!isPlainObject(value)) fail(`${where} must be an object`); + return value; +} + +function rejectUnknownKeys( + value: Record, + allowed: ReadonlySet, + where: string, +): void { + if (Object.keys(value).some((key) => !allowed.has(key))) { + fail(`${where} contains unsupported fields`); + } +} + +function boundedString(value: unknown, where: string, maxBytes: number): string { + if ( + typeof value !== "string" || + value.length === 0 || + value !== value.trim() || + Buffer.byteLength(value, "utf8") > maxBytes || + CONTROL_CHARACTER_RE.test(value) + ) { + fail(`${where} must be bounded non-empty text without control characters`); + } + return value; +} + +function httpsBaseUrl(value: unknown, where: string): string { + const raw = boundedString(value, where, MAX_URL_BYTES); + let parsed: URL; + try { + parsed = new URL(raw); + } catch { + fail(`${where} must be a valid HTTPS URL`); + } + if ( + parsed.protocol !== "https:" || + parsed.username !== "" || + parsed.password !== "" || + parsed.search !== "" || + parsed.hash !== "" + ) { + fail(`${where} must be a credential-free HTTPS URL without query or fragment data`); + } + const pathname = parsed.pathname.replace(/\/+$/u, ""); + return pathname === "" ? parsed.origin : `${parsed.origin}${pathname}`; +} + +function validateHeaderEnvironment( + value: unknown, + targetWhere: string, +): readonly HermesSwitchyardHeaderEnvironment[] { + if (!Array.isArray(value) || value.length < 1 || value.length > MAX_HEADER_ENV_BINDINGS) { + fail(`${targetWhere}.headerEnv must contain 1-${String(MAX_HEADER_ENV_BINDINGS)} bindings`); + } + const seenHeaders = new Set(); + const seenEnvironmentKeys = new Set(); + const bindings = value.map((candidate, index) => { + const where = `${targetWhere}.headerEnv[${String(index)}]`; + const binding = record(candidate, where); + rejectUnknownKeys(binding, HEADER_ENV_KEYS, where); + const rawHeaderName = boundedString(binding.headerName, `${where}.headerName`, 128); + if (!HEADER_NAME_RE.test(rawHeaderName)) fail(`${where}.headerName is not a safe HTTP header`); + const headerName = rawHeaderName.toLowerCase(); + if (!ALLOWED_PROVIDER_HEADER_NAMES.has(headerName)) { + fail(`${where}.headerName is not an allowed provider credential header`); + } + const envKey = boundedString(binding.envKey, `${where}.envKey`, 128); + if (!HEADER_ENV_KEY_RE.test(envKey)) { + fail(`${where}.envKey must be a SWITCHYARD_ prefixed environment key`); + } + if (seenHeaders.has(headerName)) fail(`${targetWhere}.headerEnv contains duplicate headers`); + if (seenEnvironmentKeys.has(envKey)) { + fail(`${targetWhere}.headerEnv contains duplicate environment keys`); + } + seenHeaders.add(headerName); + seenEnvironmentKeys.add(envKey); + return { headerName, envKey }; + }); + return bindings.sort((left, right) => + left.headerName < right.headerName ? -1 : left.headerName > right.headerName ? 1 : 0, + ); +} + +function validateTarget(value: unknown, index: number): HermesSwitchyardTarget { + const where = `targets[${String(index)}]`; + const target = record(value, where); + rejectUnknownKeys(target, TARGET_KEYS, where); + const role = boundedString(target.role, `${where}.role`, 16); + if (!ROLE_SET.has(role)) fail(`${where}.role is not supported`); + const model = boundedString(target.model, `${where}.model`, MAX_MODEL_BYTES); + if (target.protocol !== "openai_chat") fail(`${where}.protocol must be openai_chat`); + return { + role: role as HermesSwitchyardTargetRole, + baseUrl: httpsBaseUrl(target.baseUrl, `${where}.baseUrl`), + model, + protocol: "openai_chat", + headerEnv: validateHeaderEnvironment(target.headerEnv, where), + }; +} + +/** Validate and canonicalize the bounded, secret-free native routing contract. */ +export function validateHermesSwitchyardRouting(value: unknown): HermesSwitchyardRouting { + const routing = record(value, "routing"); + rejectUnknownKeys(routing, ROUTING_KEYS, "routing"); + if (routing.algorithm !== "llm_classifier") { + fail("algorithm must be llm_classifier"); + } + if ( + typeof routing.baseThreshold !== "number" || + !Number.isFinite(routing.baseThreshold) || + routing.baseThreshold < 0 || + routing.baseThreshold > 1 + ) { + fail("baseThreshold must be a finite number from 0 through 1"); + } + if (!Array.isArray(routing.targets) || routing.targets.length !== 3) { + fail("targets must contain exactly judge, weak, and strong"); + } + const byRole = new Map(); + const modelIds = new Set(); + const baseUrls = new Set(); + const environmentKeys = new Set(); + for (let index = 0; index < routing.targets.length; index += 1) { + const target = validateTarget(routing.targets[index], index); + if (byRole.has(target.role)) fail(`targets contains duplicate role ${target.role}`); + if (modelIds.has(target.model)) fail("targets must use unique model IDs"); + if (baseUrls.has(target.baseUrl)) fail("targets must use distinct dispatch base URLs"); + for (const binding of target.headerEnv) { + if (environmentKeys.has(binding.envKey)) { + fail("targets must not reuse header environment keys"); + } + environmentKeys.add(binding.envKey); + } + byRole.set(target.role, target); + modelIds.add(target.model); + baseUrls.add(target.baseUrl); + } + const targets = HERMES_SWITCHYARD_TARGET_ROLES.map((role) => { + const target = byRole.get(role); + if (!target) fail(`targets is missing role ${role}`); + return target; + }); + return { + algorithm: "llm_classifier", + baseThreshold: routing.baseThreshold, + targets, + }; +} + +function tomlString(value: string): string { + return JSON.stringify(value); +} + +export type HermesSwitchyardTomlScalar = string | number | boolean; + +/** Serialize the exact runtime env keys that the Hermes startup guard must validate. */ +export function serializeHermesSwitchyardRuntimeBindings(value: HermesSwitchyardRouting): string { + const routing = validateHermesSwitchyardRouting(value); + return `${JSON.stringify({ + schemaVersion: 1, + targets: routing.targets.map(({ headerEnv, role }) => ({ + headerEnv: headerEnv.map(({ envKey, headerName }) => ({ envKey, headerName })), + role, + })), + })}\n`; +} + +/** + * Parse the intentionally small TOML subset emitted below. Root promotion uses + * this syntax check before comparing the bytes with the profile-derived form. + */ +export function parseHermesSwitchyardRelayToml( + source: string, +): ReadonlyMap> { + if (source.length === 0 || source.includes("\r") || !source.endsWith("\n")) { + fail("Relay TOML must be non-empty canonical UTF-8 text ending in one newline"); + } + const sections = new Map>(); + let sectionName = ""; + sections.set(sectionName, new Map()); + for (const [index, line] of source.slice(0, -1).split("\n").entries()) { + if (line === "") continue; + const arrayTable = line.match(/^\[\[([A-Za-z0-9_.-]+)\]\]$/u); + const table = line.match(/^\[([A-Za-z0-9_.-]+)\]$/u); + if (arrayTable) { + const base = arrayTable[1] as string; + let instance = 0; + while (sections.has(`${base}#${String(instance)}`)) instance += 1; + sectionName = `${base}#${String(instance)}`; + sections.set(sectionName, new Map()); + continue; + } + if (table) { + sectionName = table[1] as string; + if (sections.has(sectionName)) fail(`Relay TOML repeats table ${sectionName}`); + sections.set(sectionName, new Map()); + continue; + } + const assignment = line.match(/^((?:[A-Za-z_][A-Za-z0-9_-]*)|(?:"(?:[^"\\]|\\.)+")) = (.+)$/u); + if (!assignment) fail(`Relay TOML has unsupported syntax on line ${String(index + 1)}`); + const rawKey = assignment[1] as string; + const rawValue = assignment[2] as string; + let key: string; + try { + key = rawKey.startsWith('"') ? (JSON.parse(rawKey) as string) : rawKey; + } catch { + fail(`Relay TOML has an invalid quoted key on line ${String(index + 1)}`); + } + let parsed: HermesSwitchyardTomlScalar; + if (rawValue.startsWith('"')) { + try { + parsed = JSON.parse(rawValue) as string; + } catch { + fail(`Relay TOML has an invalid string on line ${String(index + 1)}`); + } + if (typeof parsed !== "string") + fail(`Relay TOML string is malformed on line ${String(index + 1)}`); + } else if (rawValue === "true" || rawValue === "false") { + parsed = rawValue === "true"; + } else if (/^-?(?:0|[1-9][0-9]*)(?:\.[0-9]+)?(?:[eE][+-]?[0-9]+)?$/u.test(rawValue)) { + parsed = Number(rawValue); + if (!Number.isFinite(parsed)) + fail(`Relay TOML number is invalid on line ${String(index + 1)}`); + } else { + fail(`Relay TOML has an unsupported value on line ${String(index + 1)}`); + } + const section = sections.get(sectionName); + if (!section) fail("Relay TOML parser lost its current table"); + if (section.has(key)) fail(`Relay TOML repeats key ${key} in ${sectionName}`); + section.set(key, parsed); + } + return sections; +} + +/** Serialize the exact Switchyard-owned dynamic-plugin contract consumed by Relay. */ +export function serializeHermesSwitchyardRelayToml(value: HermesSwitchyardRouting): string { + const routing = validateHermesSwitchyardRouting(value); + const lines = [ + "version = 1", + "", + "[[plugins.dynamic]]", + `manifest = ${tomlString(HERMES_SWITCHYARD_PLUGIN_MANIFEST)}`, + "", + "[plugins.dynamic.config]", + "version = 2", + "priority = 0", + "max_retries = 3", + 'failure_mode = "fail_closed"', + "", + "[plugins.dynamic.config.algorithm]", + 'kind = "llm_classifier"', + 'mode = "capability"', + 'classifier_target = "judge"', + 'weak_target = "weak"', + 'strong_target = "strong"', + `base_threshold = ${String(routing.baseThreshold)}`, + "", + "[plugins.dynamic.config.default_targets]", + 'openai_chat = "weak"', + ]; + for (const target of routing.targets) { + const prefix = `plugins.dynamic.config.targets.${target.role}`; + lines.push( + "", + `[${prefix}]`, + `model = ${tomlString(target.model)}`, + `protocol = ${tomlString(target.protocol)}`, + 'endpoint = "/v1/chat/completions"', + `base_url = ${tomlString(target.baseUrl)}`, + "weight = 1", + "drop_caller_extra_body = true", + "", + `[${prefix}.header_env]`, + ...target.headerEnv.map( + ({ headerName, envKey }) => `${tomlString(headerName)} = ${tomlString(envKey)}`, + ), + ); + } + return `${lines.join("\n")}\n`; +} diff --git a/src/lib/onboard/managed-startup-agent-environment.test.ts b/src/lib/onboard/managed-startup-agent-environment.test.ts index 23272f8750e..6d46732199f 100644 --- a/src/lib/onboard/managed-startup-agent-environment.test.ts +++ b/src/lib/onboard/managed-startup-agent-environment.test.ts @@ -23,6 +23,22 @@ import { } from "./managed-startup/profile"; const CA_SHA256 = "a".repeat(64); +const HERMES_SWITCHYARD_ROUTING = { + algorithm: "llm_classifier" as const, + baseThreshold: 0.5, + targets: (["judge", "weak", "strong"] as const).map((role) => ({ + role, + baseUrl: `https://${role}.models.test/v1`, + model: `${role}-model`, + protocol: "openai_chat" as const, + headerEnv: [ + { + headerName: "authorization", + envKey: `SWITCHYARD_${role.toUpperCase()}_AUTHORIZATION`, + }, + ], + })), +}; const OPENCLAW_APPLICATION_RUNTIME_NAMES = [ "NEMOCLAW_AUTO_PAIR_DEADLINE_SECS", "NEMOCLAW_AUTO_PAIR_FAST_DEADLINE_SECS", @@ -34,6 +50,7 @@ const OPENCLAW_APPLICATION_RUNTIME_NAMES = [ const UNSUPPORTED_AGENT_RUNTIME_UNSETS = [ ...OPENCLAW_APPLICATION_RUNTIME_NAMES, "NEMOCLAW_DASHBOARD_BIND", + "NEMOCLAW_HERMES_SWITCHYARD_ROUTING_B64", "NEMOCLAW_MINIMAL_BOOTSTRAP", ] as const; @@ -356,7 +373,7 @@ describe("managed startup agent environment", () => { NEMOCLAW_AUTO_PAIR_RUN_TIMEOUT_SECS: "10", NEMOCLAW_AUTO_PAIR_SLOW_INTERVAL_SECS: "600", }, - unsetEnvironment: [], + unsetEnvironment: ["NEMOCLAW_HERMES_SWITCHYARD_ROUTING_B64"], }); expect(Object.isFrozen(result.applicationRuntime)).toBe(true); expect(Object.isFrozen(result.applicationRuntime.exportEnvironment)).toBe(true); @@ -471,7 +488,7 @@ describe("managed startup agent environment", () => { mapManagedStartupProfileToAgentEnvironment(openClawProfile()).applicationRuntime, ).toEqual({ exportEnvironment: {}, - unsetEnvironment: [], + unsetEnvironment: ["NEMOCLAW_HERMES_SWITCHYARD_ROUTING_B64"], }); } finally { delete process.env[name]; @@ -556,6 +573,43 @@ describe("managed startup agent environment", () => { }); }); + it("maps Hermes Switchyard intent only into the configuration phase (#8887)", () => { + const base = hermesProfile(); + const profile: ManagedStartupProfile = { + ...base, + agentConfig: { + agent: "hermes", + webSearch: { enabled: true, provider: "tavily" }, + switchyardRouting: HERMES_SWITCHYARD_ROUTING, + }, + }; + const result = mapManagedStartupProfileToAgentEnvironment(profile); + const encoded = result.configurationEnvironment.NEMOCLAW_HERMES_SWITCHYARD_ROUTING_B64; + + expect(decodeBase64Json(encoded ?? "")).toEqual(HERMES_SWITCHYARD_ROUTING); + expect(result.runtimeEnvironment).not.toHaveProperty( + "NEMOCLAW_HERMES_SWITCHYARD_ROUTING_B64", + ); + expect(readHermesBuildSettings(result.configurationEnvironment).switchyardRouting).toEqual( + HERMES_SWITCHYARD_ROUTING, + ); + expect(result.applicationRuntime.unsetEnvironment).not.toContain( + "NEMOCLAW_HERMES_SWITCHYARD_ROUTING_B64", + ); + }); + + it("removes ambient Switchyard configuration when Hermes routing is absent (#8887)", () => { + const result = mapManagedStartupProfileToAgentEnvironment(hermesProfile()); + + expect(result.configurationEnvironment).not.toHaveProperty( + "NEMOCLAW_HERMES_SWITCHYARD_ROUTING_B64", + ); + expect(result.applicationRuntime.unsetEnvironment).toContain( + "NEMOCLAW_HERMES_SWITCHYARD_ROUTING_B64", + ); + expect(readHermesBuildSettings(result.configurationEnvironment).switchyardRouting).toBeNull(); + }); + it("keeps DCode routing, provider identity, and auto-approval in root-owned files", () => { const result = mapManagedStartupProfileToAgentEnvironment(dcodeProfile(), { NEMOCLAW_AUTO_PAIR_FAST_REENTRY_INTERVAL_SECS: "not-a-number", diff --git a/src/lib/onboard/managed-startup-image-runtime-handoff.test.ts b/src/lib/onboard/managed-startup-image-runtime-handoff.test.ts index 0e6bb1d6011..781ef628fe7 100644 --- a/src/lib/onboard/managed-startup-image-runtime-handoff.test.ts +++ b/src/lib/onboard/managed-startup-image-runtime-handoff.test.ts @@ -427,6 +427,19 @@ describe("managed startup image runtime handoff and descriptor integrity", () => expect(ambient).toHaveProperty("NEMOCLAW_MINIMAL_BOOTSTRAP", "1"); }); + it("removes ambient Switchyard transport from routing-disabled Hermes children (#8887)", () => { + const mapped = mapManagedStartupProfileToAgentEnvironment(managedStartupE2eProfile("hermes")); + const child = applyManagedStartupCommandEnvironmentPlan( + { + NEMOCLAW_HERMES_SWITCHYARD_ROUTING_B64: "ambient-untrusted-routing", + PRESERVED: "yes", + }, + mapped.applicationRuntime, + ); + + expect(child).toEqual({ PRESERVED: "yes" }); + }); + it.each(["hermes", "langchain-deepagents-code"] as const)( "removes OpenClaw launch controls and cleanup obligations from %s children and runtime", (agent) => { diff --git a/src/lib/onboard/managed-startup-image-runtime-switchyard.test.ts b/src/lib/onboard/managed-startup-image-runtime-switchyard.test.ts new file mode 100644 index 00000000000..6103fd3e26f --- /dev/null +++ b/src/lib/onboard/managed-startup-image-runtime-switchyard.test.ts @@ -0,0 +1,266 @@ +// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +import fs from "node:fs"; +import os from "node:os"; +import path from "node:path"; +import { afterEach, describe, expect, it, vi } from "vitest"; +import { + type HermesSwitchyardRouting, + serializeHermesSwitchyardRelayToml, + serializeHermesSwitchyardRuntimeBindings, +} from "../hermes-switchyard-routing"; +import { + installHermesRelayPluginsConfiguration, + verifyHermesRelayPluginsConfiguration, +} from "./managed-startup/image-runtime"; + +const HERMES_SWITCHYARD_ROUTING = { + algorithm: "llm_classifier", + baseThreshold: 0.5, + targets: (["judge", "weak", "strong"] as const).map((role) => ({ + role, + baseUrl: `https://${role}.models.test/v1`, + model: `${role}-model`, + protocol: "openai_chat" as const, + headerEnv: [ + { + headerName: "authorization", + envKey: `SWITCHYARD_${role.toUpperCase()}_AUTHORIZATION`, + }, + ], + })), +} as const satisfies HermesSwitchyardRouting; + +describe("managed startup Hermes Switchyard runtime artifacts", () => { + const temporaryDirectories: string[] = []; + + function temporaryDirectory(): string { + const directory = fs.mkdtempSync(path.join(os.tmpdir(), "nemoclaw-switchyard-runtime-")); + temporaryDirectories.push(directory); + return directory; + } + + function mockRootOwnedStableFileSnapshots(): void { + const realFstatSync = fs.fstatSync.bind(fs); + vi.spyOn(fs, "fstatSync").mockImplementation(((descriptor: number, options?: object) => { + const stat = realFstatSync(descriptor, options as never); + return new Proxy(stat, { + get(inner, property) { + const value = + property === "uid" || property === "gid" + ? options + ? 0n + : 0 + : Reflect.get(inner, property); + return typeof value === "function" ? value.bind(inner) : value; + }, + }); + }) as typeof fs.fstatSync); + } + + function writeCommittedRelayArtifacts( + target: string, + runtimeBindings: string, + bindingsContents = serializeHermesSwitchyardRuntimeBindings(HERMES_SWITCHYARD_ROUTING), + ): void { + fs.writeFileSync(target, serializeHermesSwitchyardRelayToml(HERMES_SWITCHYARD_ROUTING), { + mode: 0o444, + }); + fs.writeFileSync(runtimeBindings, bindingsContents, { mode: 0o444 }); + } + + function mockRootOwnedRelayInstallPaths( + shareDirectory: string, + target: string, + runtimeBindings: string, + ): void { + const realLstatSync = fs.lstatSync.bind(fs); + const rootOwned = (stat: fs.Stats): fs.Stats => + new Proxy(stat, { + get(inner, property) { + const value = property === "uid" || property === "gid" ? 0 : Reflect.get(inner, property); + return typeof value === "function" ? value.bind(inner) : value; + }, + }); + vi.spyOn(fs, "lstatSync").mockImplementation((( + file: fs.PathLike, + options?: { bigint?: boolean }, + ) => { + const stat = options?.bigint ? realLstatSync(file, { bigint: true }) : realLstatSync(file); + const rootPath = + file.toString() === shareDirectory || + file.toString() === target || + file.toString() === runtimeBindings; + return rootPath && options?.bigint !== true ? rootOwned(stat as fs.Stats) : stat; + }) as typeof fs.lstatSync); + vi.spyOn(fs, "fchownSync").mockImplementation(() => undefined); + } + + afterEach(() => { + vi.restoreAllMocks(); + for (const directory of temporaryDirectories.splice(0)) { + fs.rmSync(directory, { recursive: true, force: true }); + } + }); + + it("parses, profile-binds, and promotes Hermes Relay TOML as root-owned state (#8886)", () => { + const directory = temporaryDirectory(); + const shareDirectory = path.join(directory, "share"); + const source = path.join(directory, "relay-plugins.toml"); + const target = path.join(shareDirectory, "hermes-relay-plugins.toml"); + const runtimeBindings = path.join(shareDirectory, "hermes-switchyard-bindings.json"); + const serialized = serializeHermesSwitchyardRelayToml(HERMES_SWITCHYARD_ROUTING); + fs.mkdirSync(shareDirectory); + fs.writeFileSync(source, serialized, { mode: 0o600 }); + mockRootOwnedRelayInstallPaths(shareDirectory, target, runtimeBindings); + + installHermesRelayPluginsConfiguration( + HERMES_SWITCHYARD_ROUTING, + source, + target, + runtimeBindings, + ); + + expect(fs.existsSync(source)).toBe(false); + expect(fs.readFileSync(target, "utf8")).toBe(serialized); + expect(fs.statSync(target).mode & 0o777).toBe(0o444); + expect(fs.readFileSync(runtimeBindings, "utf8")).toBe( + serializeHermesSwitchyardRuntimeBindings(HERMES_SWITCHYARD_ROUTING), + ); + expect(fs.statSync(runtimeBindings).mode & 0o777).toBe(0o444); + }); + + it("rejects generated Hermes Relay TOML that differs from the profile (#8886)", () => { + const directory = temporaryDirectory(); + const shareDirectory = path.join(directory, "share"); + const source = path.join(directory, "relay-plugins.toml"); + const target = path.join(shareDirectory, "hermes-relay-plugins.toml"); + const runtimeBindings = path.join(shareDirectory, "hermes-switchyard-bindings.json"); + const altered = serializeHermesSwitchyardRelayToml(HERMES_SWITCHYARD_ROUTING).replace( + 'model = "weak-model"', + 'model = "unattested-model"', + ); + fs.mkdirSync(shareDirectory); + fs.writeFileSync(source, altered, { mode: 0o600 }); + mockRootOwnedRelayInstallPaths(shareDirectory, target, runtimeBindings); + + expect(() => + installHermesRelayPluginsConfiguration( + HERMES_SWITCHYARD_ROUTING, + source, + target, + runtimeBindings, + ), + ).toThrow(/does not match the managed startup profile/u); + expect(fs.existsSync(source)).toBe(true); + expect(fs.existsSync(target)).toBe(false); + }); + + it("removes stale installed Relay TOML when Hermes routing is disabled (#8886)", () => { + const directory = temporaryDirectory(); + const shareDirectory = path.join(directory, "share"); + const source = path.join(directory, "relay-plugins.toml"); + const target = path.join(shareDirectory, "hermes-relay-plugins.toml"); + const runtimeBindings = path.join(shareDirectory, "hermes-switchyard-bindings.json"); + fs.mkdirSync(shareDirectory); + fs.writeFileSync(target, "stale\n", { mode: 0o444 }); + fs.writeFileSync(runtimeBindings, "stale\n", { mode: 0o444 }); + mockRootOwnedRelayInstallPaths(shareDirectory, target, runtimeBindings); + + installHermesRelayPluginsConfiguration(undefined, source, target, runtimeBindings); + + expect(fs.existsSync(target)).toBe(false); + expect(fs.existsSync(runtimeBindings)).toBe(false); + }); + + it("rejects a generated Relay TOML source when Hermes routing is disabled (#8886)", () => { + const directory = temporaryDirectory(); + const source = path.join(directory, "relay-plugins.toml"); + const target = path.join(directory, "hermes-relay-plugins.toml"); + const runtimeBindings = path.join(directory, "hermes-switchyard-bindings.json"); + fs.writeFileSync(source, serializeHermesSwitchyardRelayToml(HERMES_SWITCHYARD_ROUTING)); + fs.writeFileSync(target, "stale\n", { mode: 0o444 }); + fs.writeFileSync(runtimeBindings, "stale\n", { mode: 0o444 }); + + expect(() => + installHermesRelayPluginsConfiguration(undefined, source, target, runtimeBindings), + ).toThrow(/disabled Hermes routing left generated Relay TOML behind/u); + expect(fs.existsSync(source)).toBe(true); + expect(fs.existsSync(target)).toBe(true); + expect(fs.existsSync(runtimeBindings)).toBe(true); + }); + + it("verifies exact root-owned Relay and Switchyard runtime artifacts (#8886)", () => { + const directory = temporaryDirectory(); + const target = path.join(directory, "hermes-relay-plugins.toml"); + const runtimeBindings = path.join(directory, "hermes-switchyard-bindings.json"); + writeCommittedRelayArtifacts(target, runtimeBindings); + mockRootOwnedStableFileSnapshots(); + + expect(() => + verifyHermesRelayPluginsConfiguration(HERMES_SWITCHYARD_ROUTING, target, runtimeBindings), + ).not.toThrow(); + expect(fs.statSync(target).mode & 0o777).toBe(0o444); + expect(fs.statSync(runtimeBindings).mode & 0o777).toBe(0o444); + }); + + it("detects committed Switchyard runtime bindings drift independently (#8886)", () => { + const directory = temporaryDirectory(); + const target = path.join(directory, "hermes-relay-plugins.toml"); + const runtimeBindings = path.join(directory, "hermes-switchyard-bindings.json"); + const driftedBindings = serializeHermesSwitchyardRuntimeBindings( + HERMES_SWITCHYARD_ROUTING, + ).replace("SWITCHYARD_WEAK_AUTHORIZATION", "SWITCHYARD_WEAK_DRIFTED"); + writeCommittedRelayArtifacts(target, runtimeBindings, driftedBindings); + mockRootOwnedStableFileSnapshots(); + + expect(() => + verifyHermesRelayPluginsConfiguration(HERMES_SWITCHYARD_ROUTING, target, runtimeBindings), + ).toThrow(/committed Switchyard runtime bindings drifted/u); + }); + + it.each([ + [ + "Hermes Relay TOML", + "hermes-relay-plugins.toml", + /committed routing-disabled profile has stale Hermes Relay TOML/u, + ], + [ + "Switchyard runtime bindings", + "hermes-switchyard-bindings.json", + /committed routing-disabled profile has stale Switchyard runtime bindings/u, + ], + ])( + "rejects stale %s for a committed routing-disabled profile (#8886)", + (_label, staleArtifactName, expectedError) => { + const directory = temporaryDirectory(); + const target = path.join(directory, "hermes-relay-plugins.toml"); + const runtimeBindings = path.join(directory, "hermes-switchyard-bindings.json"); + fs.writeFileSync(path.join(directory, staleArtifactName), "stale\n", { mode: 0o444 }); + + expect(() => + verifyHermesRelayPluginsConfiguration(undefined, target, runtimeBindings), + ).toThrow(expectedError); + }, + ); + + it("detects committed Relay TOML metadata drift (#8886)", () => { + const directory = temporaryDirectory(); + const target = path.join(directory, "hermes-relay-plugins.toml"); + const runtimeBindings = path.join(directory, "hermes-switchyard-bindings.json"); + fs.writeFileSync(target, serializeHermesSwitchyardRelayToml(HERMES_SWITCHYARD_ROUTING), { + mode: 0o600, + }); + fs.writeFileSync( + runtimeBindings, + serializeHermesSwitchyardRuntimeBindings(HERMES_SWITCHYARD_ROUTING), + { mode: 0o600 }, + ); + mockRootOwnedStableFileSnapshots(); + + expect(() => + verifyHermesRelayPluginsConfiguration(HERMES_SWITCHYARD_ROUTING, target, runtimeBindings), + ).toThrow(/committed Hermes Relay TOML drifted/u); + }); +}); diff --git a/src/lib/onboard/managed-startup-profile-builder.test.ts b/src/lib/onboard/managed-startup-profile-builder.test.ts index 3bf50a0cea9..4b58c68eef8 100644 --- a/src/lib/onboard/managed-startup-profile-builder.test.ts +++ b/src/lib/onboard/managed-startup-profile-builder.test.ts @@ -37,6 +37,23 @@ function encodeJson(value: unknown): string { return Buffer.from(JSON.stringify(value), "utf8").toString("base64"); } +const HERMES_SWITCHYARD_ROUTING = { + algorithm: "llm_classifier", + baseThreshold: 0.5, + targets: (["judge", "weak", "strong"] as const).map((role) => ({ + role, + baseUrl: `https://${role}.models.test/v1`, + model: `${role}-model`, + protocol: "openai_chat" as const, + headerEnv: [ + { + headerName: "authorization", + envKey: `SWITCHYARD_${role.toUpperCase()}_AUTHORIZATION`, + }, + ], + })), +} as const; + function openClawInput( overrides: Partial = {}, ): ManagedStartupProfileBuilderInput { @@ -655,6 +672,18 @@ describe("buildManagedStartupProfile", () => { }); }); + it("builds internal Hermes Switchyard routing intent without a public environment knob (#8887)", () => { + const built = buildManagedStartupProfile( + hermesInput({ hermesSwitchyardRouting: HERMES_SWITCHYARD_ROUTING }), + ); + + expect(built.profile.agentConfig).toEqual({ + agent: "hermes", + switchyardRouting: HERMES_SWITCHYARD_ROUTING, + webSearch: { enabled: false, provider: "tavily" }, + }); + }); + it.each([ ["DCode messaging", dcodeInput({ messagingPlan: messagingPlan("openclaw") }), /messagingPlan/], [ @@ -667,6 +696,11 @@ describe("buildManagedStartupProfile", () => { openClawInput({ hermesToolGateways: ["nous-web"] }), /another agent/, ], + [ + "OpenClaw Hermes Switchyard routing", + openClawInput({ hermesSwitchyardRouting: HERMES_SWITCHYARD_ROUTING }), + /another agent/, + ], [ "Hermes Brave", hermesInput({ webSearch: { fetchEnabled: true, provider: "brave" } }), diff --git a/src/lib/onboard/managed-startup-profile.test.ts b/src/lib/onboard/managed-startup-profile.test.ts index de26d47edd3..a47dedbcd99 100644 --- a/src/lib/onboard/managed-startup-profile.test.ts +++ b/src/lib/onboard/managed-startup-profile.test.ts @@ -9,6 +9,7 @@ import path from "node:path"; import { describe, expect, it } from "vitest"; import { HERMES_API_PORT_RANGE_END, HERMES_API_PORT_RANGE_START } from "../core/ports"; +import type { HermesSwitchyardRouting } from "../hermes-switchyard-routing"; import { listMessagingCredentialEnvAssignments } from "../messaging/channels/metadata.ts"; import { decodeManagedStartupProfile, @@ -33,6 +34,23 @@ const HERMES_RESERVED_API_PORTS = [ 8_642, 8_643, 8_644, 8_645, 8_646, 8_647, 8_648, 8_649, 8_650, 8_651, 8_652, 18_642, ]; +const HERMES_SWITCHYARD_ROUTING = { + algorithm: "llm_classifier", + baseThreshold: 0.5, + targets: (["judge", "weak", "strong"] as const).map((role) => ({ + role, + baseUrl: `https://${role}.models.test/v1`, + model: `${role}-model`, + protocol: "openai_chat" as const, + headerEnv: [ + { + headerName: "authorization", + envKey: `SWITCHYARD_${role.toUpperCase()}_AUTHORIZATION`, + }, + ], + })), +} as const satisfies HermesSwitchyardRouting; + const MESSAGING_PLAN = { schemaVersion: 1, sandboxName: "demo", @@ -430,6 +448,70 @@ describe("managed startup profile", () => { expect(profile.messaging.plan).not.toBeNull(); }); + it("carries and fingerprints secret-free Hermes Switchyard topology (#8887)", () => { + const routedProfile = { + ...HERMES_PROFILE, + agentConfig: { + ...HERMES_PROFILE.agentConfig, + switchyardRouting: HERMES_SWITCHYARD_ROUTING, + }, + } as const satisfies ManagedStartupProfile; + const validated = validateManagedStartupProfile(routedProfile); + + expect( + validated.agentConfig.agent === "hermes" + ? validated.agentConfig.switchyardRouting?.targets.map(({ role }) => role) + : [], + ).toEqual(["judge", "weak", "strong"]); + const encoded = encodeManagedStartupProfile(routedProfile); + expect(decodeManagedStartupProfile(encoded)).toEqual(validated); + const decodedTransport = Buffer.from(encoded, "base64url").toString("utf8"); + expect(decodedTransport).not.toContain("openshell:resolve:env:v"); + expect(decodedTransport).not.toContain("providerEnvironmentRevision"); + expect(fingerprintManagedStartupProfile(routedProfile)).not.toBe( + fingerprintManagedStartupProfile({ + ...routedProfile, + agentConfig: { + ...routedProfile.agentConfig, + switchyardRouting: { + ...HERMES_SWITCHYARD_ROUTING, + targets: HERMES_SWITCHYARD_ROUTING.targets.map((target) => ({ + ...target, + model: target.role === "weak" ? "other-weak-model" : target.model, + })), + }, + }, + }), + ); + }); + + it("rejects credential values added to the secret-free Switchyard topology (#8887)", () => { + expect(() => + validateManagedStartupProfile({ + ...HERMES_PROFILE, + agentConfig: { + ...HERMES_PROFILE.agentConfig, + switchyardRouting: { + ...HERMES_SWITCHYARD_ROUTING, + targets: HERMES_SWITCHYARD_ROUTING.targets.map((target) => + target.role === "weak" + ? { + ...target, + headerEnv: [ + { + ...target.headerEnv[0], + placeholder: "Bearer sk-proj-rawcredentialmaterial", + }, + ], + } + : target, + ), + }, + }, + }), + ).toThrow(/credential-shaped string data/); + }); + it("round-trips langchain-deepagents-code upstream metadata, managed proxy, approval, and observability", () => { const profile = decodeManagedStartupProfile(encodeManagedStartupProfile(DCODE_PROFILE)); expect(profile).toMatchObject({ diff --git a/src/lib/onboard/managed-startup/agent-environment.ts b/src/lib/onboard/managed-startup/agent-environment.ts index 2b5dc2c1fa6..95a4a0b1944 100644 --- a/src/lib/onboard/managed-startup/agent-environment.ts +++ b/src/lib/onboard/managed-startup/agent-environment.ts @@ -144,6 +144,7 @@ const OPENCLAW_APPLICATION_RUNTIME_INPUTS = Object.freeze([ ["NEMOCLAW_AUTO_PAIR_RUN_TIMEOUT_SECS", "positive-finite-seconds"], ["NEMOCLAW_AUTO_PAIR_SLOW_INTERVAL_SECS", "positive-finite-seconds"], ] as const); +const HERMES_SWITCHYARD_ROUTING_TRANSPORT = "NEMOCLAW_HERMES_SWITCHYARD_ROUTING_B64"; function booleanFlag(value: boolean): "0" | "1" { return value ? "1" : "0"; @@ -220,6 +221,12 @@ function applicationRuntimePlan( unsetEnvironment.add(name); } } + if ( + profile.agentConfig.agent !== "hermes" || + profile.agentConfig.switchyardRouting === undefined + ) { + unsetEnvironment.add(HERMES_SWITCHYARD_ROUTING_TRANSPORT); + } return Object.freeze({ exportEnvironment: sortedEnvironment(exportEnvironment), unsetEnvironment: Object.freeze([...unsetEnvironment].sort()), @@ -436,9 +443,15 @@ function mapHermesProfile( NEMOCLAW_WEB_SEARCH_ENABLED: booleanFlag(profile.agentConfig.webSearch.enabled), NEMOCLAW_WEB_SEARCH_PROVIDER: profile.agentConfig.webSearch.provider, }; + if (profile.agentConfig.switchyardRouting !== undefined) { + configurationEnvironment.NEMOCLAW_HERMES_SWITCHYARD_ROUTING_B64 = encodeCanonicalJson( + profile.agentConfig.switchyardRouting, + ); + } const runtimeEnvironment: MutableEnvironment = { ...configurationEnvironment }; delete runtimeEnvironment.NEMOCLAW_MESSAGING_PLAN_B64; + delete runtimeEnvironment.NEMOCLAW_HERMES_SWITCHYARD_ROUTING_B64; runtimeEnvironment.NEMOCLAW_DASHBOARD_PORT = profile.dashboard.publicPort === null ? "" : String(profile.dashboard.publicPort); runtimeEnvironment.NEMOCLAW_HERMES_DASHBOARD = diff --git a/src/lib/onboard/managed-startup/image-runtime.ts b/src/lib/onboard/managed-startup/image-runtime.ts index cc499bdfa90..666ae639f88 100644 --- a/src/lib/onboard/managed-startup/image-runtime.ts +++ b/src/lib/onboard/managed-startup/image-runtime.ts @@ -5,8 +5,17 @@ import { spawnSync } from "node:child_process"; import { createHash, randomBytes, X509Certificate } from "node:crypto"; import fs from "node:fs"; import path from "node:path"; +import { TextDecoder } from "node:util"; import { PEM_CERTIFICATE_RE_GLOBAL } from "../corporate-ca-policy"; +import { + type HermesSwitchyardRouting, + HERMES_SWITCHYARD_RELAY_TOML, + HERMES_SWITCHYARD_RUNTIME_BINDINGS, + parseHermesSwitchyardRelayToml, + serializeHermesSwitchyardRelayToml, + serializeHermesSwitchyardRuntimeBindings, +} from "../../hermes-switchyard-routing"; import { type ManagedStartupAgentEnvironment, type ManagedStartupAgentMaterial, @@ -75,6 +84,12 @@ const HERMES_MANAGED_CONFIG_FILES = [ const HERMES_GENERATED_MANAGED_POLICY_FILE = "/sandbox/.hermes/managed-policy.json"; const HERMES_INSTALLED_MANAGED_POLICY_FILE = "/usr/local/share/nemoclaw/hermes-managed-policy.json"; const MAX_HERMES_MANAGED_POLICY_BYTES = 4 * 1024 * 1024; +export const HERMES_GENERATED_RELAY_PLUGINS_FILE = "/sandbox/.hermes/relay-plugins.toml"; +export const HERMES_INSTALLED_RELAY_PLUGINS_FILE = HERMES_SWITCHYARD_RELAY_TOML; +export const HERMES_INSTALLED_SWITCHYARD_RUNTIME_BINDINGS_FILE = + HERMES_SWITCHYARD_RUNTIME_BINDINGS; +const MAX_HERMES_RELAY_PLUGINS_BYTES = 128 * 1024; +const MAX_HERMES_SWITCHYARD_RUNTIME_BINDINGS_BYTES = 16 * 1024; const FIXED_PATH = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"; const SHA256_RE = /^[a-f0-9]{64}$/u; export const MANAGED_STARTUP_COMPLETION_SCHEMA_VERSION = 1; @@ -851,6 +866,104 @@ export function installHermesManagedPolicy( fs.unlinkSync(source); } +function requireAbsentFile(target: string, message: string): void { + try { + fs.lstatSync(target); + } catch (error) { + if ((error as NodeJS.ErrnoException).code === "ENOENT") return; + fail(`could not inspect ${target}`); + } + fail(message); +} + +function parseExpectedHermesRelayPlugins( + bytes: Buffer, + routing: HermesSwitchyardRouting, +): Buffer { + let source: string; + try { + source = new TextDecoder("utf-8", { fatal: true }).decode(bytes); + parseHermesSwitchyardRelayToml(source); + } catch (error) { + fail(`generated Hermes Relay TOML is invalid: ${(error as Error).message}`); + } + const expected = Buffer.from(serializeHermesSwitchyardRelayToml(routing), "utf8"); + if (!bytes.equals(expected)) { + fail("generated Hermes Relay TOML does not match the managed startup profile"); + } + return expected; +} + +/** Promote profile-bound native Relay configuration, or remove it when disabled. */ +export function installHermesRelayPluginsConfiguration( + routing: HermesSwitchyardRouting | undefined, + source = HERMES_GENERATED_RELAY_PLUGINS_FILE, + target = HERMES_INSTALLED_RELAY_PLUGINS_FILE, + runtimeBindingsTarget = HERMES_INSTALLED_SWITCHYARD_RUNTIME_BINDINGS_FILE, +): void { + if (routing === undefined) { + requireAbsentFile(source, "disabled Hermes routing left generated Relay TOML behind"); + removeSafeRootFile(target); + removeSafeRootFile(runtimeBindingsTarget); + return; + } + const generated = readStableRegularFileSnapshot(source, MAX_HERMES_RELAY_PLUGINS_BYTES); + const expected = parseExpectedHermesRelayPlugins(generated.bytes, routing); + atomicWriteRootFile(target, expected, 0o444); + atomicWriteRootFile( + runtimeBindingsTarget, + serializeHermesSwitchyardRuntimeBindings(routing), + 0o444, + ); + const current = fs.lstatSync(source, { bigint: true }); + if (!sameStableFileMetadata(generated.stat, current)) { + fail(`Hermes Relay TOML changed before source cleanup: ${source}`); + } + fs.unlinkSync(source); +} + +/** Verify the exact presence or absence of the committed native Relay configuration. */ +export function verifyHermesRelayPluginsConfiguration( + routing: HermesSwitchyardRouting | undefined, + target = HERMES_INSTALLED_RELAY_PLUGINS_FILE, + runtimeBindingsTarget = HERMES_INSTALLED_SWITCHYARD_RUNTIME_BINDINGS_FILE, +): void { + if (routing === undefined) { + requireAbsentFile(target, "committed routing-disabled profile has stale Hermes Relay TOML"); + requireAbsentFile( + runtimeBindingsTarget, + "committed routing-disabled profile has stale Switchyard runtime bindings", + ); + return; + } + const expected = Buffer.from(serializeHermesSwitchyardRelayToml(routing), "utf8"); + const installed = readStableRegularFileSnapshot(target, MAX_HERMES_RELAY_PLUGINS_BYTES); + parseExpectedHermesRelayPlugins(installed.bytes, routing); + if ( + installed.stat.uid !== 0n || + installed.stat.gid !== 0n || + installed.stat.nlink !== 1n || + Number(installed.stat.mode & 0o777n) !== 0o444 || + !installed.bytes.equals(expected) + ) { + fail("committed Hermes Relay TOML drifted"); + } + const bindings = readStableRegularFileSnapshot( + runtimeBindingsTarget, + MAX_HERMES_SWITCHYARD_RUNTIME_BINDINGS_BYTES, + ); + const expectedBindings = Buffer.from(serializeHermesSwitchyardRuntimeBindings(routing), "utf8"); + if ( + bindings.stat.uid !== 0n || + bindings.stat.gid !== 0n || + bindings.stat.nlink !== 1n || + Number(bindings.stat.mode & 0o777n) !== 0o444 || + !bindings.bytes.equals(expectedBindings) + ) { + fail("committed Switchyard runtime bindings drifted"); + } +} + /** * Restore the mutable Hermes image contract after its sandbox-side generator * atomically replaces config.yaml or .env with mode 0600. The mode transition @@ -1439,7 +1552,11 @@ function applyAdapter( sealOpenClawConfiguration(mapped.configurationEnvironment, mapped.applicationRuntime); break; case "hermes": + if (context.profile.agentConfig.agent !== "hermes") { + fail("Hermes profile has inconsistent agent configuration"); + } installHermesManagedPolicy(); + installHermesRelayPluginsConfiguration(context.profile.agentConfig.switchyardRouting); sealHermesConfiguration(mapped.configurationEnvironment, mapped.applicationRuntime); // Normalize before the coordinator commits a newly applied profile so // the durable transaction never records generator-created 0600 files as @@ -1504,6 +1621,10 @@ export async function applyManagedStartupImageProfile( // Committed startup replays still repair generator-created 0600 files, // while the descriptor guard preserves root-owned shields-up files. normalizeHermesManagedConfiguration(); + if (profile.agentConfig.agent !== "hermes") { + fail("committed Hermes profile has inconsistent agent configuration"); + } + verifyHermesRelayPluginsConfiguration(profile.agentConfig.switchyardRouting); } let corporateCaMerged: boolean; if (result.adapterApplied) { diff --git a/src/lib/onboard/managed-startup/profile-builder.ts b/src/lib/onboard/managed-startup/profile-builder.ts index b862ec41e48..1a287ce5f7b 100644 --- a/src/lib/onboard/managed-startup/profile-builder.ts +++ b/src/lib/onboard/managed-startup/profile-builder.ts @@ -25,6 +25,7 @@ import { type ManagedStartupDcodeAutoApprovalMode, type ManagedStartupExtraAgents, type ManagedStartupHermesToolGateway, + type ManagedStartupHermesSwitchyardRouting, type ManagedStartupInputModality, type ManagedStartupJsonObject, type ManagedStartupProfile, @@ -98,6 +99,8 @@ export interface ManagedStartupProfileBuilderInput { } | null; readonly toolDisclosure: ManagedStartupToolDisclosure; readonly hermesToolGateways: readonly string[]; + /** Internal, dependency-aware activation intent. No public onboarding surface emits it yet. */ + readonly hermesSwitchyardRouting?: ManagedStartupHermesSwitchyardRouting; readonly messagingPlan: unknown | null; readonly dcodeAutoApprovalMode: ManagedStartupDcodeAutoApprovalMode | null; readonly observabilityEnabled: boolean | null; @@ -532,6 +535,7 @@ function assertAgentSpecificInput(input: ManagedStartupProfileBuilderInput): voi if ( input.inference.upstreamEndpointUrl !== null || input.hermesToolGateways.length > 0 || + input.hermesSwitchyardRouting !== undefined || input.dcodeAutoApprovalMode !== null || input.observabilityEnabled !== null ) { @@ -572,6 +576,9 @@ function assertAgentSpecificInput(input: ManagedStartupProfileBuilderInput): voi if (input.hermesToolGateways.length > 0) { fail("langchain-deepagents-code does not support Hermes tool gateways"); } + if (input.hermesSwitchyardRouting !== undefined) { + fail("langchain-deepagents-code does not support Hermes Switchyard routing"); + } if (input.messagingPlan !== null) { fail("langchain-deepagents-code messagingPlan must be null"); } @@ -936,7 +943,13 @@ function buildCandidate(input: ManagedStartupProfileBuilderInput): { }; } else if (input.agent === "hermes") { if (!webSearch) fail("Hermes web-search state is missing"); - agentConfig = { agent: "hermes", webSearch }; + agentConfig = { + agent: "hermes", + webSearch, + ...(input.hermesSwitchyardRouting === undefined + ? {} + : { switchyardRouting: input.hermesSwitchyardRouting }), + }; tuning = { contextWindow: parsePositiveInteger(input.environment, "NEMOCLAW_CONTEXT_WINDOW", null, { minimum: MIN_HERMES_CONTEXT_WINDOW, diff --git a/src/lib/onboard/managed-startup/profile.ts b/src/lib/onboard/managed-startup/profile.ts index 76e6c6442db..3f3f5d859f2 100644 --- a/src/lib/onboard/managed-startup/profile.ts +++ b/src/lib/onboard/managed-startup/profile.ts @@ -8,6 +8,9 @@ import { listMessagingCredentialEnvAssignments } from "../../messaging/channels/ import { authorizeMessagingManagedStartupFields } from "../../messaging/managed-startup-placeholders.ts"; import { isValidDcodeUpstreamProvider } from "./dcode-upstream-provider.ts"; +import type { HermesSwitchyardRouting } from "../../hermes-switchyard-routing"; +import { validateHermesSwitchyardRouting } from "../../hermes-switchyard-routing.ts"; + /** * Versioned, bounded schema for managed-image startup intent. * Runtime-specific construction and activation stay outside this module. @@ -293,8 +296,12 @@ export interface ManagedStartupOpenClawConfig { export interface ManagedStartupHermesConfig { readonly agent: "hermes"; readonly webSearch: ManagedStartupWebSearch; + /** Absent keeps native Relay and Switchyard routing disabled. */ + readonly switchyardRouting?: HermesSwitchyardRouting; } +export type ManagedStartupHermesSwitchyardRouting = HermesSwitchyardRouting; + export interface ManagedStartupDcodeConfig { readonly agent: "langchain-deepagents-code"; readonly autoApprovalMode: ManagedStartupDcodeAutoApprovalMode; @@ -945,7 +952,7 @@ const OPENCLAW_CONFIG_KEYS = new Set([ "deviceAuth", "minimalBootstrap", ]); -const HERMES_CONFIG_KEYS = new Set(["agent", "webSearch"]); +const HERMES_CONFIG_KEYS = new Set(["agent", "webSearch", "switchyardRouting"]); const DCODE_CONFIG_KEYS = new Set(["agent", "autoApprovalMode", "observabilityEnabled"]); const PI_CONFIG_KEYS = new Set(["agent"]); const PI_DASHBOARD_KEYS = new Set(["agent", "mode"]); @@ -1855,7 +1862,16 @@ function validateAgentConfig( } if (agent === "hermes") { rejectUnknownKeys(config, HERMES_CONFIG_KEYS, "agentConfig"); - return { agent, webSearch: validateWebSearch(config.webSearch, agent) }; + const base = { agent, webSearch: validateWebSearch(config.webSearch, agent) } as const; + if (!Object.hasOwn(config, "switchyardRouting")) return base; + try { + return { + ...base, + switchyardRouting: validateHermesSwitchyardRouting(config.switchyardRouting), + }; + } catch (error) { + invalid(error instanceof Error ? error.message : "agentConfig.switchyardRouting is invalid"); + } } if (agent === "pi") { diff --git a/test/generate-hermes-config.test.ts b/test/generate-hermes-config.test.ts index 512f650a8b0..0e0ca200838 100644 --- a/test/generate-hermes-config.test.ts +++ b/test/generate-hermes-config.test.ts @@ -12,12 +12,17 @@ import { readHermesBuildSettings, } from "../agents/hermes/config/build-env.ts"; import { generateHermesConfig } from "../agents/hermes/config/generate.ts"; +import { buildHermesEnvLines } from "../agents/hermes/config/hermes-env.ts"; import { buildHermesManagedPolicy, MANAGED_IMAGE_HERMES_NEUTRAL_PLATFORMS, } from "../agents/hermes/config/managed-policy.ts"; import { discoverModelSpecificSetups } from "../agents/hermes/config/model-specific-setup.ts"; import { HERMES_PROXY_REWRITE_SENTINEL } from "../src/lib/hermes-managed-route"; +import { + type HermesSwitchyardRouting, + serializeHermesSwitchyardRelayToml, +} from "../src/lib/hermes-switchyard-routing"; import { applyCompatibleEndpointContextWindow, resetCompatibleEndpointContextWindowAutoState, @@ -48,6 +53,23 @@ const BASE_ENV: Record = { NEMOCLAW_WECHAT_CONFIG_B64: encodeJson({}), }; +const SWITCHYARD_ROUTING = { + algorithm: "llm_classifier", + baseThreshold: 0.5, + targets: (["judge", "weak", "strong"] as const).map((role) => ({ + role, + baseUrl: `https://${role}.models.test/v1`, + model: `${role}-model`, + protocol: "openai_chat" as const, + headerEnv: [ + { + headerName: "authorization", + envKey: `SWITCHYARD_${role.toUpperCase()}_AUTHORIZATION`, + }, + ], + })), +} as const satisfies HermesSwitchyardRouting; + const HERMES_STRUCTURED_TOOL_SEARCH = { enabled: "on", search_default_limit: 5, @@ -246,6 +268,10 @@ function copyConfigGeneratorFixture(fixtureRoot: string): string { path.join(import.meta.dirname, "..", "src", "lib", "hermes-managed-route.ts"), path.join(fixtureRoot, "src", "lib", "hermes-managed-route.ts"), ); + fs.copyFileSync( + path.join(import.meta.dirname, "..", "src", "lib", "hermes-switchyard-routing.ts"), + path.join(fixtureRoot, "src", "lib", "hermes-switchyard-routing.ts"), + ); return fixtureScriptPath; } @@ -256,7 +282,7 @@ function expectRemotePlatformToolsets(toolsets: unknown, extraToolsets: string[] } function findRawSecretEnvEntries(envFile: string): string[] { - const secretKey = /(^|_)(TOKEN|KEY|SECRET|PASSWORD|CREDENTIAL|API)(_|$)/; + const secretKey = /(^|_)(TOKEN|KEY|SECRET|PASSWORD|CREDENTIAL|API|AUTHORIZATION)(_|$)/; const slackAlias = /^(xoxb|xapp)-OPENSHELL-RESOLVE-ENV-[A-Z0-9_]+$/; const allowedNonsecretKeys = new Set(["API_SERVER_HOST", "API_SERVER_PORT"]); // Mirror ENV_FILE_ALLOWED_RAW_SECRET_KEYS in @@ -307,6 +333,53 @@ afterEach(() => { }); describe("agents/hermes/generate-config.ts", () => { + it("keeps native Relay disabled unless Switchyard routing is explicitly configured (#8886)", () => { + const { envFile } = generateBaseConfig(); + + expect(envFile).not.toContain("HERMES_NEMO_RELAY_PLUGINS_TOML"); + expect(fs.existsSync(path.join(tmpDir, ".hermes", "relay-plugins.toml"))).toBe(false); + }); + + it("keeps native Relay disabled for legacy settings without a routing field (#8886)", () => { + const { switchyardRouting: _switchyardRouting, ...legacySettings } = + readHermesBuildSettings(buildHermesTestEnv()); + + expect(buildHermesEnvLines(legacySettings)).not.toContain( + "HERMES_NEMO_RELAY_PLUGINS_TOML=/usr/local/share/nemoclaw/hermes-relay-plugins.toml", + ); + }); + + it("writes deterministic native Relay TOML without persisting provider placeholders (#8886)", () => { + const encoded = encodeJson(SWITCHYARD_ROUTING); + const { envFile } = generateBaseConfig({ + NEMOCLAW_HERMES_SWITCHYARD_ROUTING_B64: encoded, + }); + const relayPluginsPath = path.join(tmpDir, ".hermes", "relay-plugins.toml"); + const relayPlugins = fs.readFileSync(relayPluginsPath, "utf8"); + + expect(relayPlugins).toBe(serializeHermesSwitchyardRelayToml(SWITCHYARD_ROUTING)); + expect(fs.statSync(relayPluginsPath).mode & 0o777).toBe(0o600); + expect(envFile).toContain( + "HERMES_NEMO_RELAY_PLUGINS_TOML=/usr/local/share/nemoclaw/hermes-relay-plugins.toml\n", + ); + expect(envFile).not.toContain("SWITCHYARD_WEAK_AUTHORIZATION="); + expect(envFile).not.toContain("openshell:resolve:env:v"); + expect(envFile).not.toContain("providerEnvironmentRevision"); + expect(findRawSecretEnvEntries(envFile)).toEqual([]); + + generateBaseConfig(); + expect(fs.existsSync(relayPluginsPath)).toBe(false); + expect(readGeneratedConfig().envFile).not.toContain("HERMES_NEMO_RELAY_PLUGINS_TOML"); + }); + + it("rejects malformed native routing before writing Relay TOML (#8886)", () => { + expectGenerationError( + { NEMOCLAW_HERMES_SWITCHYARD_ROUTING_B64: encodeJson({ targets: [] }) }, + /NEMOCLAW_HERMES_SWITCHYARD_ROUTING_B64 is invalid/, + ); + expect(fs.existsSync(path.join(tmpDir, ".hermes", "relay-plugins.toml"))).toBe(false); + }); + it( "matches direct generation as a strip-types executable with an explicit gateway matrix", async () => { diff --git a/test/helpers/source-require-cache.ts b/test/helpers/source-require-cache.ts index e10d3565d5f..ff19c2aa5eb 100644 --- a/test/helpers/source-require-cache.ts +++ b/test/helpers/source-require-cache.ts @@ -36,6 +36,7 @@ export function loadSourceRequireCompilerOptions(repoRoot: string): ts.CompilerO inlineSources: true, noEmit: false, outDir: undefined, + rewriteRelativeImportExtensions: false, rootDir: undefined, sourceMap: false, }; diff --git a/test/hermes-env-secret-boundary-hardening.test.ts b/test/hermes-env-secret-boundary-hardening.test.ts index 28a3131b38d..794dcacfd9e 100644 --- a/test/hermes-env-secret-boundary-hardening.test.ts +++ b/test/hermes-env-secret-boundary-hardening.test.ts @@ -277,6 +277,70 @@ raise SystemExit(module.validate_env_file(sys.argv[3]))`, } }); + it("anchors installed Switchyard validation below root when Landlock denies opening root", () => { + const root = fs.mkdtempSync(path.join(os.tmpdir(), "nemoclaw-switchyard-landlock-root-")); + const bindings = path.join(root, "hermes-switchyard-runtime-bindings.json"); + const roles = ["judge", "weak", "strong"] as const; + fs.writeFileSync( + bindings, + `${JSON.stringify({ + schemaVersion: 1, + targets: roles.map((role) => ({ + role, + headerEnv: [ + { + envKey: `SWITCHYARD_${role.toUpperCase()}_AUTHORIZATION`, + headerName: "authorization", + }, + ], + })), + })}\n`, + { mode: 0o444 }, + ); + fs.chmodSync(bindings, 0o444); + try { + const result = spawnSync( + "python3", + [ + "-c", + `import errno, importlib.util, os, sys +spec = importlib.util.spec_from_file_location("validator", sys.argv[1]) +module = importlib.util.module_from_spec(spec) +spec.loader.exec_module(module) +module.__file__ = module.INSTALLED_BOUNDARY_VALIDATOR +module.INSTALLED_SWITCHYARD_RUNTIME_BINDINGS = sys.argv[2] +module._switchyard_installed_owner = lambda: (os.geteuid(), os.getegid()) +original_open = module.os.open +def landlock_open(path, *args, **kwargs): + if path == os.sep: + raise PermissionError(errno.EACCES, "Landlock denied root", path) + return original_open(path, *args, **kwargs) +module.os.open = landlock_open +raise SystemExit(module.main(["switchyard-runtime-env", sys.argv[2]]))`, + VALIDATOR, + bindings, + ], + { + encoding: "utf-8", + timeout: 5000, + env: { + PATH: process.env.PATH ?? "", + ...Object.fromEntries( + roles.map((role) => { + const envKey = `SWITCHYARD_${role.toUpperCase()}_AUTHORIZATION`; + return [envKey, `openshell:resolve:env:v7_${envKey}`]; + }), + ), + }, + }, + ); + expect(result.status, result.stderr).toBe(0); + expect(result.stderr).toBe(""); + } finally { + fs.rmSync(root, { recursive: true, force: true }); + } + }); + it("makes startup fail closed for a missing env or broken ancestor", () => { const root = fs.mkdtempSync(path.join(os.tmpdir(), "nemoclaw-env-missing-")); const missingEnvHome = path.join(root, "missing-env"); diff --git a/test/hermes-secret-boundary-api-key.test.ts b/test/hermes-secret-boundary-api-key.test.ts index c4c03712bc2..d2424bb80fd 100644 --- a/test/hermes-secret-boundary-api-key.test.ts +++ b/test/hermes-secret-boundary-api-key.test.ts @@ -20,6 +20,7 @@ const GENERATED_HEX_TOKEN = Array.from({ length: 64 }, (_value, index) => const INHERITED_HEX_TOKEN = Array.from({ length: 64 }, (_value, index) => (15 - (index % 16)).toString(16), ).join(""); +const MAX_REPORTED_VIOLATIONS = 64; function runEnvFileValidator(envFileContent: string) { const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "nemoclaw-hermes-api-key-boundary-")); @@ -39,8 +40,19 @@ function runEnvFileValidator(envFileContent: string) { } } -function runRuntimeEnvValidator(envOverrides: Record) { - return spawnSync("python3", [SECRET_BOUNDARY_VALIDATOR_SCRIPT, "runtime-env"], { +function runRuntimeEnvValidator( + envOverrides: Record, + switchyardBindings?: string, +) { + return spawnSync( + "python3", + [ + SECRET_BOUNDARY_VALIDATOR_SCRIPT, + ...(switchyardBindings === undefined + ? ["runtime-env"] + : ["switchyard-runtime-env", switchyardBindings]), + ], + { encoding: "utf-8", timeout: 5000, env: { @@ -49,7 +61,68 @@ function runRuntimeEnvValidator(envOverrides: Record) { HERMES_LAZY_INSTALL_TARGET: "/sandbox/.hermes/lazy-packages", ...envOverrides, }, - }); + }, + ); +} + +function runInstalledSwitchyardRuntimeValidator( + switchyardBindings: string, + envOverrides: Record = {}, +) { + const invokeInstalledMode = ` +import importlib.util +import sys + +spec = importlib.util.spec_from_file_location("nemoclaw_boundary", sys.argv[1]) +module = importlib.util.module_from_spec(spec) +assert spec.loader is not None +spec.loader.exec_module(module) +module.__file__ = module.INSTALLED_BOUNDARY_VALIDATOR +raise SystemExit(module.main(["switchyard-runtime-env", sys.argv[2]])) +`; + return spawnSync( + "python3", + ["-I", "-c", invokeInstalledMode, SECRET_BOUNDARY_VALIDATOR_SCRIPT, switchyardBindings], + { + encoding: "utf-8", + timeout: 5000, + env: { + HOME: os.tmpdir(), + PATH: process.env.PATH ?? "", + ...envOverrides, + }, + }, + ); +} + +function writeSwitchyardBindings(directory: string): string { + const bindings = path.join(directory, "bindings.json"); + fs.writeFileSync( + bindings, + `${JSON.stringify({ + schemaVersion: 1, + targets: (["judge", "weak", "strong"] as const).map((role) => ({ + role, + headerEnv: [ + { + envKey: `SWITCHYARD_${role.toUpperCase()}_AUTHORIZATION`, + headerName: "authorization", + }, + ], + })), + })}\n`, + { mode: 0o600 }, + ); + return bindings; +} + +function revisionBoundSwitchyardEnvironment(revision = "1234567890123456789") { + return Object.fromEntries( + (["JUDGE", "WEAK", "STRONG"] as const).map((role) => { + const key = `SWITCHYARD_${role}_AUTHORIZATION`; + return [key, `openshell:resolve:env:v${revision}_${key}`]; + }), + ); } describe("agents/hermes/validate-hermes-env-secret-boundary API_SERVER_KEY contract", () => { @@ -123,3 +196,232 @@ describe("agents/hermes/validate-hermes-env-secret-boundary API_SERVER_KEY contr expect(result.stderr).not.toContain(weakKey); }); }); + +describe("agents/hermes/validate-hermes-env-secret-boundary routing placeholders", () => { + it("preserves generic revisionless resolvers for non-Switchyard runtime credentials", () => { + const result = runRuntimeEnvValidator({ + DISCORD_BOT_TOKEN: "openshell:resolve:env:DISCORD_BOT_TOKEN", + }); + + expect(result.status, result.stderr).toBe(0); + expect(result.stderr).toBe(""); + }); + + it("rejects every Switchyard credential from persisted Hermes .env (#8887)", () => { + const result = runEnvFileValidator( + [ + "API_SERVER_PORT=18642", + "API_SERVER_HOST=127.0.0.1", + "SWITCHYARD_WEAK_AUTHORIZATION=openshell:resolve:env:v9_SWITCHYARD_WEAK_AUTHORIZATION", + "", + ].join("\n"), + ); + + expect(result.status).toBe(1); + expect(result.stderr).toContain("SWITCHYARD_WEAK_AUTHORIZATION"); + }); + + it.each([ + ["revisionless Bearer marker", "Bearer openshell:resolve:env:FAST_API_KEY"], + ["raw Bearer credential", "Bearer sk-proj-rawcredentialmaterial"], + ] as const)( + "rejects an unsafe %s routing header without printing it (#8887)", + (_scenario, value) => { + const result = runEnvFileValidator( + [ + "API_SERVER_PORT=18642", + "API_SERVER_HOST=127.0.0.1", + `SWITCHYARD_WEAK_AUTHORIZATION=${value}`, + "", + ].join("\n"), + ); + + expect(result.status).toBe(1); + expect(result.stderr).toContain("SWITCHYARD_WEAK_AUTHORIZATION"); + expect(result.stderr).not.toContain(value); + }, + ); + + it("requires exact same-revision OpenShell markers for every enabled runtime binding (#8887)", () => { + const directory = fs.mkdtempSync(path.join(os.tmpdir(), "nemoclaw-switchyard-bindings-")); + const bindings = writeSwitchyardBindings(directory); + const revision = "1234567890123456789"; + const valid = revisionBoundSwitchyardEnvironment(revision); + + try { + expect(runRuntimeEnvValidator(valid, bindings).status).toBe(0); + const extra = runRuntimeEnvValidator( + { ...valid, SWITCHYARD_STALE_AUTHORIZATION: "raw-secret" }, + bindings, + ); + expect(extra.status).toBe(1); + expect(extra.stderr).toContain("SWITCHYARD_STALE_AUTHORIZATION"); + expect(extra.stderr).not.toContain("raw-secret"); + for (const [label, overrides] of [ + ["missing", { ...valid, SWITCHYARD_WEAK_AUTHORIZATION: "" }], + ["raw", { ...valid, SWITCHYARD_WEAK_AUTHORIZATION: "raw-secret" }], + [ + "unversioned", + { + ...valid, + SWITCHYARD_WEAK_AUTHORIZATION: + "openshell:resolve:env:SWITCHYARD_WEAK_AUTHORIZATION", + }, + ], + [ + "wrong suffix", + { + ...valid, + SWITCHYARD_WEAK_AUTHORIZATION: + `openshell:resolve:env:v${revision}_SWITCHYARD_STRONG_AUTHORIZATION`, + }, + ], + [ + "mixed revision", + { + ...valid, + SWITCHYARD_WEAK_AUTHORIZATION: + "openshell:resolve:env:v7_SWITCHYARD_WEAK_AUTHORIZATION", + }, + ], + ] as const) { + const result = runRuntimeEnvValidator(overrides, bindings); + expect(result.status, `${label}: ${result.stderr}`).toBe(1); + expect(result.stderr).not.toContain("raw-secret"); + } + } finally { + fs.rmSync(directory, { recursive: true, force: true }); + } + }); + + it("redacts malformed temporary binding manifests without a traceback (#8887)", () => { + const directory = fs.mkdtempSync( + path.join(os.tmpdir(), "nemoclaw-switchyard-sensitive-path-"), + ); + const bindings = path.join(directory, "raw-secret-marker.json"); + fs.writeFileSync(bindings, '{"raw-secret-marker":', { mode: 0o600 }); + + try { + const result = runRuntimeEnvValidator({}, bindings); + + expect(result.status).toBe(1); + expect(result.stderr).toContain( + "Switchyard runtime binding contract is missing, malformed, or unsafe", + ); + expect(result.stderr).not.toContain(directory); + expect(result.stderr).not.toContain("raw-secret-marker"); + expect(result.stderr).not.toContain("Traceback"); + } finally { + fs.rmSync(directory, { recursive: true, force: true }); + } + }); + + it("rejects alternate paths when the installed validator identity is active (#8887)", () => { + const directory = fs.mkdtempSync(path.join(os.tmpdir(), "nemoclaw-switchyard-alternate-")); + const bindings = writeSwitchyardBindings(directory); + + try { + const result = runInstalledSwitchyardRuntimeValidator( + bindings, + revisionBoundSwitchyardEnvironment(), + ); + + expect(result.status).toBe(1); + expect(result.stderr).toContain( + "Switchyard runtime binding contract is missing, malformed, or unsafe", + ); + expect(result.stderr).not.toContain(bindings); + expect(result.stderr).not.toContain("Traceback"); + } finally { + fs.rmSync(directory, { recursive: true, force: true }); + } + }); + + it("rejects an untrusted temporary manifest ancestor without revealing it (#8887)", () => { + const directory = fs.mkdtempSync(path.join(os.tmpdir(), "nemoclaw-switchyard-untrusted-")); + const bindings = writeSwitchyardBindings(directory); + fs.chmodSync(directory, 0o777); + + try { + const result = runRuntimeEnvValidator(revisionBoundSwitchyardEnvironment(), bindings); + + expect(result.status).toBe(1); + expect(result.stderr).toContain( + "Switchyard runtime binding contract is missing, malformed, or unsafe", + ); + expect(result.stderr).not.toContain(directory); + expect(result.stderr).not.toContain("Traceback"); + } finally { + fs.rmSync(directory, { recursive: true, force: true }); + } + }); + + it("caps redacted Switchyard diagnostics and reports the omitted count (#8887)", () => { + const directory = fs.mkdtempSync(path.join(os.tmpdir(), "nemoclaw-switchyard-cap-")); + const bindings = writeSwitchyardBindings(directory); + const extraBindings = Object.fromEntries( + Array.from({ length: 70 }, (_value, index) => [ + `SWITCHYARD_STALE_${index.toString().padStart(2, "0")}_AUTHORIZATION`, + "raw-secret-marker", + ]), + ); + + try { + const result = runRuntimeEnvValidator( + { ...revisionBoundSwitchyardEnvironment(), ...extraBindings }, + bindings, + ); + const reportedBindings = result.stderr + .split("\n") + .filter((line) => line.startsWith("[SECURITY] SWITCHYARD_")); + + expect(result.status).toBe(1); + expect(reportedBindings).toHaveLength(MAX_REPORTED_VIOLATIONS); + expect(result.stderr).toContain("6 additional violation(s) omitted"); + expect(result.stderr).not.toContain("raw-secret-marker"); + } finally { + fs.rmSync(directory, { recursive: true, force: true }); + } + }); + + it("counts each Switchyard violation once in the full runtime boundary (#8887)", () => { + const envKeys = Array.from( + { length: 70 }, + (_value, index) => + `SWITCHYARD_STALE_${index.toString().padStart(2, "0")}_AUTHORIZATION`, + ); + const invokeRuntimeMode = ` +import importlib.util +import os +import sys + +spec = importlib.util.spec_from_file_location("nemoclaw_boundary", sys.argv[1]) +module = importlib.util.module_from_spec(spec) +assert spec.loader is not None +spec.loader.exec_module(module) +module._switchyard_runtime_bindings = lambda path: [] +raise SystemExit(module.validate_runtime_env(dict(os.environ))) +`; + const result = spawnSync( + "python3", + ["-I", "-c", invokeRuntimeMode, SECRET_BOUNDARY_VALIDATOR_SCRIPT], + { + encoding: "utf-8", + timeout: 5000, + env: { + PATH: process.env.PATH ?? "", + HERMES_LAZY_INSTALL_TARGET: "/sandbox/.hermes/lazy-packages", + ...Object.fromEntries(envKeys.map((key) => [key, "raw-secret-marker"])), + }, + }, + ); + const reportedBindings = result.stderr + .split("\n") + .filter((line) => line.startsWith("[SECURITY] SWITCHYARD_")); + + expect(result.status).toBe(1); + expect(reportedBindings).toHaveLength(MAX_REPORTED_VIOLATIONS); + expect(result.stderr).toContain("6 additional violation(s) omitted"); + expect(result.stderr).not.toContain("raw-secret-marker"); + }); +}); diff --git a/test/mcp-tool-discovery-image-contract.test.ts b/test/mcp-tool-discovery-image-contract.test.ts index 385e1475fd2..e8161d2b866 100644 --- a/test/mcp-tool-discovery-image-contract.test.ts +++ b/test/mcp-tool-discovery-image-contract.test.ts @@ -210,7 +210,7 @@ describe("MCP tool discovery image contract", () => { ); const expectedHashes = { "managed-startup-image-runtime.bundle": - "296a54f8d7d2ff63ba82254d83797891bd18e7dc7724acd0f2d7deb92435d43a", + "1110428daf3d43f7866668ed0ee077996dfa736c308050e0496702c05a460b4c", "mcp-tool-discovery/BUNDLED_PACKAGES.json": "df5dc8f167101085a8e73c444aa56854b2a4716a0bb7de9886fec4e50f402601", "mcp-tool-discovery/THIRD_PARTY_LICENSES.txt": diff --git a/test/source-require-loader.test.ts b/test/source-require-loader.test.ts index 31fc80e654a..7d20d79bd20 100644 --- a/test/source-require-loader.test.ts +++ b/test/source-require-loader.test.ts @@ -116,6 +116,10 @@ function waitForFile(filename: string, timeoutMs = 2_000): void { } describe("source require loader", () => { + it("keeps TypeScript specifiers intact for direct source loading", () => { + expect(compilerOptions.rewriteRelativeImportExtensions).toBe(false); + }); + it("emits opt-in cache statistics and reuses a cross-process cache entry (#6237)", () => { const root = fs.mkdtempSync(path.join(os.tmpdir(), "nemoclaw-source-require-")); roots.push(root); diff --git a/tools/mcp-tool-discovery-runtime/reviewed-runtime-bundle/managed-startup-image-runtime.bundle b/tools/mcp-tool-discovery-runtime/reviewed-runtime-bundle/managed-startup-image-runtime.bundle index 3a55a288561..4b85564d0d5 100644 --- a/tools/mcp-tool-discovery-runtime/reviewed-runtime-bundle/managed-startup-image-runtime.bundle +++ b/tools/mcp-tool-discovery-runtime/reviewed-runtime-bundle/managed-startup-image-runtime.bundle @@ -1,13 +1,15 @@ -var __create=Object.create;var __defProp=Object.defineProperty;var __getOwnPropDesc=Object.getOwnPropertyDescriptor;var __getOwnPropNames=Object.getOwnPropertyNames;var __getProtoOf=Object.getPrototypeOf;var __hasOwnProp=Object.prototype.hasOwnProperty;var __export=(target,all)=>{for(var name in all)__defProp(target,name,{get:all[name],enumerable:true})};var __copyProps=(to,from,except,desc)=>{if(from&&typeof from==="object"||typeof from==="function"){for(let key of __getOwnPropNames(from))if(!__hasOwnProp.call(to,key)&&key!==except)__defProp(to,key,{get:()=>from[key],enumerable:!(desc=__getOwnPropDesc(from,key))||desc.enumerable})}return to};var __toESM=(mod,isNodeMode,target)=>(target=mod!=null?__create(__getProtoOf(mod)):{},__copyProps(isNodeMode||!mod||!mod.__esModule?__defProp(target,"default",{value:mod,enumerable:true}):target,mod));var __toCommonJS=mod=>__copyProps(__defProp({},"__esModule",{value:true}),mod);var image_runtime_exports={};__export(image_runtime_exports,{applyManagedBootstrapEnvelope:()=>applyManagedBootstrapEnvelope,main:()=>main2,managedBootstrapEnvelopeClaimPaths:()=>managedBootstrapEnvelopeClaimPaths,readManagedBootstrapEnvelope:()=>readManagedBootstrapEnvelope,recoverManagedBootstrapEnvelopeClaim:()=>recoverManagedBootstrapEnvelopeClaim,verifyManagedBootstrapImageCompletion:()=>verifyManagedBootstrapImageCompletion,waitForManagedBootstrapImageCompletion:()=>waitForManagedBootstrapImageCompletion});module.exports=__toCommonJS(image_runtime_exports);var import_node_fs4=__toESM(require("node:fs"));var import_node_path4=__toESM(require("node:path"));var import_node_child_process=require("node:child_process");var import_node_crypto6=require("node:crypto");var import_node_fs3=__toESM(require("node:fs"));var import_node_path3=__toESM(require("node:path"));var MAX_CORPORATE_CA_BYTES=128*1024;var PEM_CERTIFICATE_RE_GLOBAL=/-----BEGIN CERTIFICATE-----[\s\S]+?-----END CERTIFICATE-----/g;var import_node_buffer2=require("node:buffer");function isObjectRecord(value){return typeof value==="object"&&value!==null&&!Array.isArray(value)}var ChannelManifestRegistry=class{manifests=new Map;constructor(manifests=[]){for(const manifest of manifests){this.register(manifest)}}register(manifest){if(this.manifests.has(manifest.id)){throw new Error(`Duplicate channel manifest id '${manifest.id}'`)}this.manifests.set(manifest.id,manifest);return this}get(channelId){return this.manifests.get(channelId)}list(){return Array.from(this.manifests.values())}listAvailable(ctx={}){const supportedChannelIds=Array.isArray(ctx.supportedChannelIds)?new Set(ctx.supportedChannelIds):null;return this.list().filter(manifest=>{if(ctx.agent&&!manifest.supportedAgents.includes(ctx.agent)){return false}if(supportedChannelIds&&!supportedChannelIds.has(manifest.id)){return false}return true})}};function createChannelManifestRegistry(manifests=[]){return new ChannelManifestRegistry(manifests)}var discordManifest={schemaVersion:1,id:"discord",displayName:"Discord",description:"Discord bot messaging",supportedAgents:["openclaw","hermes"],auth:{mode:"token-paste"},inputs:[{id:"botToken",kind:"secret",required:true,envKey:"DISCORD_BOT_TOKEN",prompt:{label:"Discord Bot Token",help:"Discord Developer Portal \u2192 Applications \u2192 Bot \u2192 Reset/Copy Token."}},{id:"serverId",kind:"config",required:false,envKey:"DISCORD_SERVER_ID",statePath:"discordGuilds.serverId",prompt:{label:"Discord Server ID (for guild workspace access)",help:"Enable Developer Mode in Discord, then right-click your server and copy the Server ID.",emptyValueMessage:"guild channels stay disabled"}},{id:"requireMention",kind:"config",required:false,envKey:"DISCORD_REQUIRE_MENTION",statePath:"discordGuilds.requireMention",promptWhenInput:"serverId",validValues:["0","1"],defaultValue:"1",prompt:{label:"Discord mention mode",help:"Choose whether the bot should reply only when @mentioned or to all messages in this server."}},{id:"userId",kind:"config",required:false,envKey:"DISCORD_USER_ID",statePath:"discordGuilds.userIds",promptWhenInput:"serverId",prompt:{label:"Discord User ID (optional guild allowlist)",help:"Optional: enable Developer Mode in Discord, then right-click your user/avatar and copy the User ID. Leave blank to allow any member of the configured server to message the bot.",emptyValueMessage:"any member in the configured server can message the bot"}}],credentials:[{id:"discordBotToken",sourceInput:"botToken",providerName:"{sandboxName}-discord-bridge",providerEnvKey:"DISCORD_BOT_TOKEN",placeholder:"openshell:resolve:env:DISCORD_BOT_TOKEN"}],policyPresets:[{name:"discord",validationWarningLines:["For Discord preset validation, do not use curl as the success signal:","curl is not in the preset binary allowlist, so curl probes can fail even","when the policy is working. Use Node HTTPS against","https://discord.com/api/v10/gateway or validate the configured",'messaging bridge/gateway path. DNS-only checks such as dns.resolve("gateway.discord.gg")',"can also be inconclusive behind a proxy."]}],render:[{id:"discord-openclaw-channel",kind:"json-fragment",agent:"openclaw",target:"openclaw.json",fragment:{path:"channels.discord",value:{enabled:true,accounts:{default:{token:"{{credential.discordBotToken.placeholder}}",enabled:true,healthMonitor:{enabled:false},proxy:"{{discordProxyUrl}}",dmPolicy:"{{discord.allowedUsers.dmPolicy}}",allowFrom:"{{discord.allowedUsers.values}}"}}}}},{id:"discord-openclaw-guilds",kind:"json-fragment",agent:"openclaw",target:"openclaw.json",when:"{{discord.hasGuilds}}",fragment:{path:"channels.discord",value:{groupPolicy:"allowlist",guilds:"{{discord.guilds}}"}}},{id:"discord-openclaw-plugin",kind:"json-fragment",agent:"openclaw",target:"openclaw.json",fragment:{path:"plugins.entries.discord",value:{enabled:true}}},{id:"discord-hermes-env",kind:"env-lines",agent:"hermes",target:"~/.hermes/.env",lines:["DISCORD_BOT_TOKEN={{credential.discordBotToken.placeholder}}","NEMOCLAW_DISCORD_GUILD_IDS={{discord.guildIds.csv}}","DISCORD_ALLOWED_USERS={{discord.allowedUsers.csv}}","DISCORD_ALLOW_ALL_USERS={{discord.allowAllUsers}}"]},{id:"discord-hermes-config",kind:"json-fragment",agent:"hermes",target:"~/.hermes/config.yaml",fragment:{path:"discord",value:{require_mention:"{{discord.requireMention}}",free_response_channels:"",allowed_channels:"",auto_thread:true,reactions:true,channel_prompts:{}}}},{id:"discord-hermes-platform",kind:"json-fragment",agent:"hermes",target:"~/.hermes/config.yaml",fragment:{path:"platforms.discord",value:{enabled:true}}}],runtime:{openclaw:{channelName:"discord",visibility:{configKeys:["discord"],logPatterns:["discord"]}}},agentPackages:[{id:"openclawPluginPackage",agent:"openclaw",manager:"openclaw-plugin",spec:"npm:@openclaw/discord@{{openclaw.version}}",pin:true,integrityByVersion:{"2026.7.1":"sha512-tZfdC1YA8oVLvc2BK1w0F6rUljS5ugCOp2uWe0vPsbG1fbzVVIO4V32RoqZznGHe5u2R9u4n1aV5Z/qa1m2oFg=="},tarballUrlByVersion:{"2026.7.1":"https://registry.npmjs.org/@openclaw/discord/-/discord-2026.7.1.tgz"},required:true}],hooks:[{id:"discord-openclaw-bridge-health",phase:"health-check",handler:"discord.openclawBridgeHealth",agents:["openclaw"],onFailure:"abort"},{id:"discord-token-paste",phase:"enroll",handler:"common.tokenPaste",outputs:[{id:"botToken",kind:"secret",required:true}],onFailure:"skip-channel"},{id:"discord-config-prompt",phase:"enroll",handler:"common.configPrompt",outputs:[{id:"serverId",kind:"config"},{id:"requireMention",kind:"config"},{id:"userId",kind:"config"}]}]};var googlechatManifest={schemaVersion:1,id:"googlechat",displayName:"Google Chat",description:"Google Chat (Chat API) bot messaging (experimental)",supportedAgents:["openclaw","hermes"],auth:{mode:"token-paste"},inputs:[{id:"serviceAccount",kind:"secret",required:true,envKey:"GOOGLECHAT_SERVICE_ACCOUNT",maskCap:40,formatHint:"Paste the entire service-account JSON key on one line (minified) \u2014 the whole downloaded JSON file.",maxTokenAttempts:3,prompt:{label:"Google Chat service account JSON",help:["\u2503 GOOGLE CHAT \u2014 service account key","\u2503","\u2503 Google Cloud Console \u2192 IAM & Admin \u2192 Service Accounts","\u2503 \u2192 your bot's SA \u2192 Keys \u2192 Add key \u2192 Create new key \u2192 JSON","\u2503","\u2503 A .json file downloads. Paste its contents below as ONE line (minified).",""].join("\n")}},{id:"audienceType",kind:"config",required:false,envKey:"GOOGLECHAT_AUDIENCE_TYPE",statePath:"googlechatConfig.audienceType",validValues:["app-url","project-number"],defaultValue:"app-url"},{id:"audience",kind:"config",required:false,envKey:"GOOGLECHAT_AUDIENCE",statePath:"googlechatConfig.audience",prompt:{label:"Google Chat webhook audience",help:"Usually filled automatically from the public tunnel URL. For audienceType 'project-number', enter your GCP project number instead.",emptyValueMessage:"inbound webhook verification will be unconfigured"}},{id:"appPrincipal",kind:"config",required:false,envKey:"GOOGLECHAT_APP_PRINCIPAL",statePath:"googlechatConfig.appPrincipal",formatPattern:"^[0-9]{6,32}$",formatHint:"appPrincipal is the add-on's numeric OAuth client ID (uniqueId, ~21 digits), not an email.",prompt:{label:"Google Chat appPrincipal",help:[" Workspace account \u2192 leave blank, done."," Personal Gmail \u2192 needs the add-on's ~21-digit ID (not an email), stable across rebuilds.",""," If you already know it, paste it at the prompt and you're done."," If not, leave it blank \u2014 the first DM reveals it once the sandbox is live:",""," 1. Watch the gateway log:",' nemoclaw logs --follow | grep "unexpected add-on principal"'," 2. DM the bot once \u2014 it won't reply yet, that's expected. The log prints:"," unexpected add-on principal: "," 3. Save that and rebuild:"," GOOGLECHAT_APP_PRINCIPAL= nemoclaw channels add googlechat"," nemoclaw rebuild --yes"].join("\n"),emptyValueMessage:"Workspace accounts do not need it; personal accounts must set it later"}},{id:"allowFrom",kind:"config",required:false,envKey:"GOOGLECHAT_ALLOWED_USERS",statePath:"allowedIds.googlechat",prompt:{label:"Google Chat DM allowlist (comma-separated)",help:["Optional: restrict who can DM the bot."," OpenClaw: users/NNN (emails ignored)"," Hermes: email (users/NNN ignored)"," Blank: pairing mode (recommended) \u2014 OpenClaw's pairing reply shows your users/NNN"," Filling this switches DM policy to allowlist \u2014 a wrong-form entry is dropped silently, with no pairing code."].join("\n"),emptyValueMessage:"bot will require manual pairing"}},{id:"projectId",kind:"config",required:false,envKey:"GOOGLE_CHAT_PROJECT_ID",statePath:"googlechatConfig.projectId",prompt:{label:"Google Chat GCP project ID (Hermes Pub/Sub pull)",help:"The Google Cloud project that owns the Pub/Sub subscription Hermes pulls Chat events from. OpenClaw ignores this.",emptyValueMessage:"required for the Hermes Google Chat channel"}},{id:"subscriptionName",kind:"config",required:false,envKey:"GOOGLE_CHAT_SUBSCRIPTION_NAME",statePath:"googlechatConfig.subscriptionName",prompt:{label:"Google Chat Pub/Sub subscription (projects/

/subscriptions/)",help:["The pull subscription bound to the Chat events topic. Hermes pulls from it over the Pub/Sub REST API; the gateway-minted token is scoped to both chat.bot and pubsub."," Its topic must grant roles/pubsub.publisher to the app's push account:"," Interactive features service-@gcp-sa-gsuiteaddons.iam.gserviceaccount.com"," Classic bot chat-api-push@system.gserviceaccount.com"," Shown at Chat API \u2192 Configuration \u2192 Connection settings"," Missing it channel connects, no event arrives, Chat says the bot is not responding"].join("\n"),emptyValueMessage:"required for the Hermes Google Chat channel"}}],credentials:[],policyPresets:[{name:"googlechat",policyKeys:["googlechat"],agentPolicyKeys:{hermes:["googlechat_hermes"]}}],render:[{id:"googlechat-openclaw-channel",kind:"json-fragment",agent:"openclaw",target:"openclaw.json",fragment:{path:"channels.googlechat",value:{enabled:true,serviceAccountFile:"/nonexistent/googlechat-gateway-minted-no-service-account-file",audienceType:"{{googlechatConfig.audienceType}}",audience:"{{googlechatConfig.audience}}",appPrincipal:"{{googlechatConfig.appPrincipal}}",webhookPath:"/googlechat",healthMonitor:{enabled:false},dm:{policy:"{{allowedIds.googlechat.dmPolicy}}",allowFrom:"{{allowedIds.googlechat.values}}"}}}},{id:"googlechat-openclaw-plugin",kind:"json-fragment",agent:"openclaw",target:"openclaw.json",fragment:{path:"plugins.entries.googlechat",value:{enabled:true}}},{id:"googlechat-openclaw-gateway-reload-off",kind:"json-fragment",agent:"openclaw",target:"openclaw.json",fragment:{path:"gateway.reload",value:{mode:"off"}}},{id:"googlechat-hermes-env",kind:"env-lines",agent:"hermes",target:"~/.hermes/.env",lines:["GOOGLE_CHAT_PROJECT_ID={{googlechatConfig.projectId}}","GOOGLE_CHAT_SUBSCRIPTION_NAME={{googlechatConfig.subscriptionName}}","GOOGLE_CHAT_ALLOWED_USERS={{allowedIds.googlechat.csv}}"]},{id:"googlechat-hermes-platform",kind:"json-fragment",agent:"hermes",target:"~/.hermes/config.yaml",fragment:{path:"platforms.google_chat",value:{enabled:true}}}],runtime:{openclaw:{channelName:"googlechat",visibility:{configKeys:["googlechat"],logPatterns:["googlechat"]},nodePreloads:[{module:"googlechat-trusted-proxy-fetch",injectInto:["boot"],optional:false,installMessage:"[channels] Installing Google Chat trusted-proxy-fetch patch (route googleapis via trusted env proxy)",installedMessage:"[channels] Google Chat trusted-proxy-fetch patch installed (NODE_OPTIONS updated)"},{module:"googlechat-outbound-auth",injectInto:["boot"],optional:false,installMessage:"[channels] Installing Google Chat outbound-auth patch (gateway-minted bearer)",installedMessage:"[channels] Google Chat outbound-auth patch installed (NODE_OPTIONS updated)"}],secretScans:[{path:"/sandbox/.openclaw/openclaw.json",pattern:"-----BEGIN (?:RSA )?PRIVATE KEY-----",message:"[SECURITY] Google Chat service account private key leaked into {path} - refusing to serve",exitCode:78}]}},agentPackages:[{id:"openclawPluginPackage",agent:"openclaw",manager:"openclaw-plugin",spec:"npm:@openclaw/googlechat@{{openclaw.version}}",pin:true,integrityByVersion:{"2026.7.1":"sha512-Dv0xOmcxAThEr6hoK+ioofHNu18hfbIceQrEHX3AHZPpOUiTJvToVpA5eX87NQINewwfSJf0gVhE6kSbSk2Aew=="},tarballUrlByVersion:{"2026.7.1":"https://registry.npmjs.org/@openclaw/googlechat/-/googlechat-2026.7.1.tgz"},required:true},{id:"hermesGooglePubsubPackage",agent:"hermes",manager:"hermes-uv-pip",spec:"google-cloud-pubsub==2.39.0",required:true},{id:"hermesGoogleApiClientPackage",agent:"hermes",manager:"hermes-uv-pip",spec:"google-api-python-client==2.194.0",required:true},{id:"hermesGoogleAuthPackage",agent:"hermes",manager:"hermes-uv-pip",spec:"google-auth==2.55.1",required:true}],hooks:[{id:"googlechat-tunnel-audience-gate",phase:"enroll",handler:"googlechat.tunnelAudienceGate",agents:["openclaw"],inputs:["audienceType","audience"],outputs:[{id:"audience",kind:"config"}],onFailure:"skip-channel"},{id:"googlechat-service-account",phase:"enroll",handler:"googlechat.tokenPaste",outputs:[{id:"serviceAccount",kind:"secret",required:true}],onFailure:"skip-channel"},{id:"googlechat-config-prompt",phase:"enroll",handler:"common.configPrompt",outputs:[{id:"allowFrom",kind:"config"}]},{id:"googlechat-openclaw-config-prompt",phase:"enroll",handler:"common.configPrompt",agents:["openclaw"],outputs:[{id:"appPrincipal",kind:"config"}]},{id:"googlechat-hermes-config-prompt",phase:"enroll",handler:"common.configPrompt",agents:["hermes"],outputs:[{id:"projectId",kind:"config"},{id:"subscriptionName",kind:"config"}]}]};var slackRuntimeEnvAliases=[{envKey:"SLACK_BOT_TOKEN",match:"^openshell:resolve:env:(v[0-9]+_)?SLACK_BOT_TOKEN$",value:"xoxb-OPENSHELL-RESOLVE-ENV-SLACK_BOT_TOKEN",message:"[channels] Normalized SLACK_BOT_TOKEN runtime placeholder to the Bolt-compatible alias"},{envKey:"SLACK_APP_TOKEN",match:"^openshell:resolve:env:(v[0-9]+_)?SLACK_APP_TOKEN$",value:"xapp-OPENSHELL-RESOLVE-ENV-SLACK_APP_TOKEN",message:"[channels] Normalized SLACK_APP_TOKEN runtime placeholder to the Bolt-compatible alias"}];var slackManifest={schemaVersion:1,id:"slack",displayName:"Slack",description:"Slack bot messaging",supportedAgents:["openclaw","hermes"],auth:{mode:"token-paste"},inputs:[{id:"botToken",kind:"secret",required:true,envKey:"SLACK_BOT_TOKEN",formatPattern:"^xoxb-[A-Za-z0-9_-]+$",formatHint:"Slack bot tokens start with 'xoxb-' (e.g. xoxb---).",prompt:{label:"Slack Bot Token",help:"Slack API \u2192 Your Apps \u2192 OAuth & Permissions \u2192 Bot User OAuth Token (xoxb-...)."}},{id:"appToken",kind:"secret",required:true,envKey:"SLACK_APP_TOKEN",formatPattern:"^xapp-[A-Za-z0-9_-]+$",formatHint:"Slack app tokens start with 'xapp-' (e.g. xapp----).",prompt:{label:"Slack App Token (Socket Mode)",help:"Slack API \u2192 Your Apps \u2192 Basic Information \u2192 App-Level Tokens (xapp-...)."}},{id:"allowedUsers",kind:"config",required:false,envKey:"SLACK_ALLOWED_USERS",statePath:"allowedIds.slack",prompt:{label:"Slack Member IDs (comma-separated allowlist)",help:"In Slack, open each allowed human user's profile -> More -> Copy member ID. Enter one or more comma-separated member IDs, not the app or bot user ID. Member IDs look like U01ABC2DEF3.",emptyValueMessage:"bot will require manual pairing"}},{id:"allowedChannels",kind:"config",required:false,envKey:"SLACK_ALLOWED_CHANNELS",statePath:"slackConfig.allowedChannels",prompt:{label:"Slack Channel IDs (comma-separated allowlist)",help:"Optional: enter comma-separated Slack channel IDs where the bot may answer @mentions. Channel IDs look like C012AB3CD.",emptyValueMessage:"channel @mentions stay unrestricted by channel ID"}}],credentials:[{id:"slackBotToken",sourceInput:"botToken",providerName:"{sandboxName}-slack-bridge",providerEnvKey:"SLACK_BOT_TOKEN",placeholder:"xoxb-OPENSHELL-RESOLVE-ENV-SLACK_BOT_TOKEN",primary:true},{id:"slackAppToken",sourceInput:"appToken",providerName:"{sandboxName}-slack-app",providerEnvKey:"SLACK_APP_TOKEN",placeholder:"xapp-OPENSHELL-RESOLVE-ENV-SLACK_APP_TOKEN"}],policyPresets:[{name:"slack",requiredAtCreate:true}],render:[{id:"slack-openclaw-channel",kind:"json-fragment",agent:"openclaw",target:"openclaw.json",fragment:{path:"channels.slack",value:{enabled:true,accounts:{default:{botToken:"{{credential.slackBotToken.placeholder}}",appToken:"{{credential.slackAppToken.placeholder}}",enabled:true,healthMonitor:{enabled:false},dmPolicy:"{{allowedIds.slack.dmPolicy}}",allowFrom:"{{allowedIds.slack.values}}",groupPolicy:"{{allowedIds.slack.groupPolicy}}",channels:"{{allowedIds.slack.channels}}"}}}}},{id:"slack-openclaw-plugin",kind:"json-fragment",agent:"openclaw",target:"openclaw.json",fragment:{path:"plugins.entries.slack",value:{enabled:true}}},{id:"slack-hermes-env",kind:"env-lines",agent:"hermes",target:"~/.hermes/.env",lines:["SLACK_BOT_TOKEN={{credential.slackBotToken.placeholder}}","SLACK_APP_TOKEN={{credential.slackAppToken.placeholder}}","SLACK_ALLOWED_USERS={{allowedIds.slack.csv}}","SLACK_ALLOWED_CHANNELS={{slackConfig.allowedChannels.csv}}"]},{id:"slack-hermes-platform",kind:"json-fragment",agent:"hermes",target:"~/.hermes/config.yaml",fragment:{path:"platforms.slack",value:{enabled:true,extra:{rich_blocks:true}}}}],runtime:{openclaw:{channelName:"slack",visibility:{configKeys:["slack"],logPatterns:["slack"]},envAliases:slackRuntimeEnvAliases,nodePreloads:[{module:"slack-channel-guard",injectInto:["boot","connect"],optional:false,installMessage:"[channels] Installing Slack channel guard (unhandled-rejection safety net)",installedMessage:"[channels] Slack channel guard installed (NODE_OPTIONS updated)"}],secretScans:[{path:"/sandbox/.openclaw/openclaw.json",pattern:"(?:xoxb|xapp)-(?!OPENSHELL-RESOLVE-ENV-)",message:"[SECURITY] Slack token leaked into {path} - refusing to serve",exitCode:78}]},hermes:{envAliases:slackRuntimeEnvAliases}},agentPackages:[{id:"openclawPluginPackage",agent:"openclaw",manager:"openclaw-plugin",spec:"npm:@openclaw/slack@{{openclaw.version}}",pin:true,integrityByVersion:{"2026.7.1":"sha512-dwVGEVCmoTQrOIeZaSCIOPg8pT7hB883QQEXdp9EZUDzTGuvSc+KxH2iERSOV/59hROQctYdcobGn/vdB1H4XA=="},tarballUrlByVersion:{"2026.7.1":"https://registry.npmjs.org/@openclaw/slack/-/slack-2026.7.1.tgz"},required:true}],hooks:[{id:"slack-socket-mode-gateway-conflict",phase:"pre-enable",handler:"slack.socketModeGatewayConflict",onFailure:"abort"},{id:"slack-openclaw-bridge-health",phase:"health-check",handler:"slack.openclawBridgeHealth",agents:["openclaw"],onFailure:"abort"},{id:"slack-socket-mode-gateway-status",phase:"status",handler:"slack.socketModeGatewayStatus",outputs:[{id:"gatewayOverlaps",kind:"status"}]},{id:"slack-status-health",phase:"status",handler:"slack.statusHealth",providesReadiness:true,agents:["openclaw"],outputs:[{id:"channelHealth",kind:"status"}]},{id:"slack-token-paste",phase:"enroll",handler:"common.tokenPaste",outputs:[{id:"botToken",kind:"secret",required:true},{id:"appToken",kind:"secret",required:true}],onFailure:"skip-channel"},{id:"slack-config-prompt",phase:"enroll",handler:"common.configPrompt",outputs:[{id:"allowedUsers",kind:"config"},{id:"allowedChannels",kind:"config"}]},{id:"slack-credential-validation",phase:"reachability-check",handler:"slack.validateCredentials",inputs:["botToken","appToken"],onFailure:"skip-channel"}]};var TEAMS_OPENCLAW_WEBHOOK_RENDER_CONTRACT={channelId:"teams",renderId:"teams-openclaw-channel",hookId:"teams-openclaw-channel",handlerId:"common.staticOutputs",kind:"json-fragment",agent:"openclaw",target:"openclaw.json",configPath:"channels.msteams",webhookPath:"/api/messages"};function authorizeTeamsOpenClawWebhookField(entry){if(!isPlainDataObject(entry))return[];const contract=TEAMS_OPENCLAW_WEBHOOK_RENDER_CONTRACT;if(ownDataPropertyValue(entry,"channelId")!==contract.channelId||ownDataPropertyValue(entry,"renderId")!==contract.renderId||ownDataPropertyValue(entry,"hookId")!==contract.hookId||ownDataPropertyValue(entry,"handler")!==contract.handlerId||ownDataPropertyValue(entry,"kind")!==contract.kind||ownDataPropertyValue(entry,"agent")!==contract.agent||ownDataPropertyValue(entry,"target")!==contract.target||ownDataPropertyValue(entry,"path")!==contract.configPath){return[]}const value=ownDataPropertyValue(entry,"value");if(!isPlainDataObject(value))return[];const webhook=ownDataPropertyValue(value,"webhook");if(!isPlainDataObject(webhook)||!hasExactlyOwnDataProperties(webhook,["path","port"])||!isTcpPort(ownDataPropertyValue(webhook,"port"))||ownDataPropertyValue(webhook,"path")!==contract.webhookPath){return[]}return[{path:["value","webhook"],value:webhook}]}function isPlainDataObject(value){if(value===null||typeof value!=="object"||Array.isArray(value))return false;const prototype=Object.getPrototypeOf(value);return prototype===Object.prototype||prototype===null}function ownDataPropertyValue(value,key){const descriptor=Object.getOwnPropertyDescriptor(value,key);return descriptor&&"value"in descriptor?descriptor.value:void 0}function hasExactlyOwnDataProperties(value,expected){const actual=Object.getOwnPropertyNames(value).sort();return actual.length===expected.length&&actual.every((key,index)=>key===expected[index])}function isTcpPort(value){return Number.isInteger(value)&&value>=1&&value<=65535}var teamsManifest={schemaVersion:1,id:"teams",displayName:"Microsoft Teams",description:"Microsoft Teams bot messaging (experimental)",enrollmentNotes:["Microsoft Teams requires a public HTTPS webhook endpoint at /api/messages; expose the configured Teams webhook port before installing the Teams app.","Use Azure AD object IDs in TEAMS_ALLOWED_USERS so only authorized users can interact with the bot."],supportedAgents:["openclaw","hermes"],auth:{mode:"token-paste"},inputs:[{id:"appId",kind:"config",required:true,envKey:"MSTEAMS_APP_ID",statePath:"teamsConfig.appId",prompt:{label:"Microsoft Teams Client ID",help:"Run `teams app create --endpoint https:///api/messages`, then copy CLIENT_ID."}},{id:"clientSecret",kind:"secret",required:true,envKey:"MSTEAMS_APP_PASSWORD",prompt:{label:"Microsoft Teams Client Secret",help:"Use the CLIENT_SECRET printed by `teams app create`. It is shown once; rotate it in Entra ID if it was lost."}},{id:"tenantId",kind:"config",required:true,envKey:"MSTEAMS_TENANT_ID",statePath:"teamsConfig.tenantId",prompt:{label:"Microsoft Teams Tenant ID",help:"Use the TENANT_ID printed by `teams app create` or shown by `teams status --verbose`."}},{id:"allowedUsers",kind:"config",required:false,envKey:"TEAMS_ALLOWED_USERS",statePath:"allowedIds.teams",prompt:{label:"Microsoft Teams AAD Object IDs (comma-separated allowlist)",help:"Recommended: run `teams status --verbose` and enter the Azure AD object IDs allowed to use the bot."}},{id:"webhookPort",kind:"config",required:false,envKey:"MSTEAMS_PORT",statePath:"teamsConfig.webhookPort",defaultValue:"3978",prompt:{label:"Microsoft Teams webhook port",help:"Local bot webhook port to expose publicly. Defaults to 3978 and serves /api/messages."}},{id:"requireMention",kind:"config",required:false,envKey:"TEAMS_REQUIRE_MENTION",statePath:"teamsConfig.requireMention",validValues:["0","1"],defaultValue:"1",prompt:{label:"Microsoft Teams mention mode",help:"Controls OpenClaw group and channel behavior only. Direct messages are unaffected."}}],credentials:[{id:"teamsClientSecret",sourceInput:"clientSecret",providerName:"{sandboxName}-teams-bridge",providerEnvKey:"MSTEAMS_APP_PASSWORD",placeholder:"openshell:resolve:env:MSTEAMS_APP_PASSWORD",primary:true}],policyPresets:[{name:"teams",policyKeys:["teams"]}],hostForward:{port:"{{teamsConfig.webhookPort}}",label:"Microsoft Teams webhook"},render:[{id:TEAMS_OPENCLAW_WEBHOOK_RENDER_CONTRACT.renderId,kind:TEAMS_OPENCLAW_WEBHOOK_RENDER_CONTRACT.kind,agent:TEAMS_OPENCLAW_WEBHOOK_RENDER_CONTRACT.agent,target:TEAMS_OPENCLAW_WEBHOOK_RENDER_CONTRACT.target,fragment:{path:TEAMS_OPENCLAW_WEBHOOK_RENDER_CONTRACT.configPath,value:{enabled:true,appId:"{{teamsConfig.appId}}",appPassword:"{{credential.teamsClientSecret.placeholder}}",tenantId:"{{teamsConfig.tenantId}}",webhook:{port:"{{teamsConfig.webhookPort}}",path:TEAMS_OPENCLAW_WEBHOOK_RENDER_CONTRACT.webhookPath},healthMonitor:{enabled:false},streaming:{mode:"off"},dmPolicy:"{{allowedIds.teams.dmPolicy}}",allowFrom:"{{allowedIds.teams.values}}",groupPolicy:"open",requireMention:"{{teamsConfig.requireMention}}"}}},{id:"teams-openclaw-plugin",kind:"json-fragment",agent:"openclaw",target:"openclaw.json",fragment:{path:"plugins.entries.msteams",value:{enabled:true}}},{id:"teams-hermes-env",kind:"env-lines",agent:"hermes",target:"~/.hermes/.env",lines:["TEAMS_CLIENT_ID={{teamsConfig.appId}}","TEAMS_CLIENT_SECRET={{credential.teamsClientSecret.placeholder}}","TEAMS_TENANT_ID={{teamsConfig.tenantId}}","TEAMS_ALLOWED_USERS={{allowedIds.teams.csv}}","TEAMS_PORT={{teamsConfig.webhookPort}}"]},{id:"teams-hermes-platform",kind:"json-fragment",agent:"hermes",target:"~/.hermes/config.yaml",fragment:{path:"platforms.teams",value:{enabled:true}}}],runtime:{openclaw:{channelName:"msteams",visibility:{configKeys:["msteams"],logPatterns:["msteams","teams"]},nodePreloads:[{module:"msteams-message-hints",injectInto:["boot","connect"],optional:false,installMessage:"[channels] Installing Microsoft Teams message hint patch (native mentions)",installedMessage:"[channels] Microsoft Teams message hint patch installed (NODE_OPTIONS updated)"}]}},agentPackages:[{id:"openclawPluginPackage",agent:"openclaw",manager:"openclaw-plugin",spec:"npm:@openclaw/msteams@{{openclaw.version}}",pin:true,integrityByVersion:{"2026.7.1":"sha512-gG/Yk6HZAguHwrmKjsqdONbFz5WNy126PEAXQWNW/TulO1kIifQ6tktM16BQPNLnkmWqLbj+TrrO55Cjas1aFg=="},tarballUrlByVersion:{"2026.7.1":"https://registry.npmjs.org/@openclaw/msteams/-/msteams-2026.7.1.tgz"},required:true},{id:"hermesTeamsAppsPackage",agent:"hermes",manager:"hermes-uv-pip",spec:"microsoft-teams-apps==2.0.13.4",required:true},{id:"hermesAiohttpPackage",agent:"hermes",manager:"hermes-uv-pip",spec:"aiohttp==3.14.3",required:true}],hooks:[{id:"teams-host-forward-port-conflict",phase:"pre-enable",handler:"teams.hostForwardPortConflict",inputs:["webhookPort"],onFailure:"abort"},{id:"teams-host-forward-port-status",phase:"status",handler:"teams.hostForwardPortStatus",outputs:[{id:"hostForwardPortOverlaps",kind:"status"}]},{id:"teams-token-paste",phase:"enroll",handler:"common.tokenPaste",outputs:[{id:"clientSecret",kind:"secret",required:true}],onFailure:"skip-channel"},{id:"teams-config-prompt",phase:"enroll",handler:"common.configPrompt",outputs:[{id:"appId",kind:"config",required:true},{id:"tenantId",kind:"config",required:true},{id:"allowedUsers",kind:"config"},{id:"webhookPort",kind:"config"},{id:"requireMention",kind:"config"}]}]};var telegramManifest={schemaVersion:1,id:"telegram",displayName:"Telegram",description:"Telegram bot messaging",diagnosticsProbe:"log-tail",enrollmentNotes:["For Telegram group chats, disable privacy mode in @BotFather (/setprivacy -> your bot -> Disable).","After changing privacy mode, remove and re-add the bot to each group before testing @mentions."],supportedAgents:["openclaw","hermes"],auth:{mode:"token-paste"},inputs:[{id:"botToken",kind:"secret",required:true,envKey:"TELEGRAM_BOT_TOKEN",prompt:{label:"Telegram Bot Token",help:"Create a bot via @BotFather on Telegram, then copy the token."}},{id:"allowedIds",kind:"config",required:false,envKey:"TELEGRAM_ALLOWED_IDS",statePath:"allowedIds.telegram",prompt:{label:"Telegram User ID (for DM access)",help:"Send /start to @userinfobot on Telegram to get your numeric user ID.",emptyValueMessage:"bot will require manual pairing"}},{id:"requireMention",kind:"config",required:false,envKey:"TELEGRAM_REQUIRE_MENTION",statePath:"telegramConfig.requireMention",validValues:["0","1"],defaultValue:"1",prompt:{label:"Telegram group mention mode",help:"Controls Telegram group-chat behavior only \u2014 reply only when @mentioned vs. to all group messages. Direct messages are unaffected by this setting and remain subject to pairing and TELEGRAM_ALLOWED_IDS."}},{id:"groupPolicy",kind:"config",required:false,envKey:"TELEGRAM_GROUP_POLICY",statePath:"telegramConfig.groupPolicy",validValues:["open","allowlist","disabled"],defaultValue:"open",prompt:{label:"Telegram group policy",help:"Controls OpenClaw Telegram group access. Hermes does not expose an equivalent disable-groups policy."}}],credentials:[{id:"telegramBotToken",sourceInput:"botToken",providerName:"{sandboxName}-telegram-bridge",providerEnvKey:"TELEGRAM_BOT_TOKEN",placeholder:"openshell:resolve:env:TELEGRAM_BOT_TOKEN"}],policyPresets:[{name:"telegram",policyKeys:["telegram_bot"],agentPolicyKeys:{hermes:["telegram"]}}],render:[{id:"telegram-openclaw-channel",kind:"json-fragment",agent:"openclaw",target:"openclaw.json",fragment:{path:"channels.telegram",value:{enabled:true,accounts:{default:{botToken:"{{credential.telegramBotToken.placeholder}}",enabled:true,healthMonitor:{enabled:false},proxy:"{{proxyUrl}}",groupPolicy:"{{telegramConfig.groupPolicy}}",dmPolicy:"{{allowedIds.telegram.dmPolicy}}",allowFrom:"{{allowedIds.telegram.values}}"}}}}},{id:"telegram-openclaw-groups",kind:"json-fragment",agent:"openclaw",target:"openclaw.json",when:"{{telegramConfig.openclawGroups}}",fragment:{path:"channels.telegram.groups",value:"{{telegramConfig.openclawGroups}}"}},{id:"telegram-openclaw-plugin",kind:"json-fragment",agent:"openclaw",target:"openclaw.json",fragment:{path:"plugins.entries.telegram",value:{enabled:true}}},{id:"telegram-hermes-env",kind:"env-lines",agent:"hermes",target:"~/.hermes/.env",lines:["TELEGRAM_BOT_TOKEN={{credential.telegramBotToken.placeholder}}","TELEGRAM_ALLOWED_USERS={{allowedIds.telegram.csv}}"]},{id:"telegram-hermes-config",kind:"json-fragment",agent:"hermes",target:"~/.hermes/config.yaml",fragment:{path:"telegram",value:{require_mention:"{{telegramConfig.requireMention}}"}}},{id:"telegram-hermes-platform",kind:"json-fragment",agent:"hermes",target:"~/.hermes/config.yaml",fragment:{path:"platforms.telegram",value:{enabled:true}}}],runtime:{openclaw:{channelName:"telegram",visibility:{configKeys:["telegram"],logPatterns:["telegram"]},nodePreloads:[{module:"telegram-diagnostics",injectInto:["boot","connect"],optional:false,installMessage:"[channels] Installing Telegram diagnostics (provider readiness + inference errors)",installedMessage:"[channels] Telegram diagnostics installed (NODE_OPTIONS updated)"}]}},hooks:[{id:"telegram-token-paste",phase:"enroll",handler:"common.tokenPaste",outputs:[{id:"botToken",kind:"secret",required:true}],onFailure:"skip-channel"},{id:"telegram-allowlist-aliases",phase:"enroll",handler:"telegram.allowlistAliases",outputs:[{id:"allowedIds",kind:"config"}]},{id:"telegram-config-prompt",phase:"enroll",handler:"common.configPrompt",outputs:[{id:"requireMention",kind:"config"},{id:"allowedIds",kind:"config"}]},{id:"telegram-openclaw-config-prompt",phase:"enroll",handler:"common.configPrompt",agents:["openclaw"],outputs:[{id:"groupPolicy",kind:"config"}]},{id:"telegram-get-me-reachability",phase:"reachability-check",handler:"telegram.getMeReachability",inputs:["botToken"],onFailure:"skip-channel"},{id:"telegram-openclaw-bridge-health",phase:"health-check",handler:"telegram.openclawBridgeHealth",agents:["openclaw"],onFailure:"abort"},{id:"telegram-gateway-conflict-status",phase:"status",handler:"telegram.gatewayConflictStatus",outputs:[{id:"bridgeHealth",kind:"status"}]},{id:"telegram-status-health",phase:"status",handler:"telegram.statusHealth",agents:["openclaw"],outputs:[{id:"channelHealth",kind:"status"}]}]};var WECHAT_OPENCLAW_ACCOUNT_FILE_CONTRACT={channelId:"wechat",planHookId:"wechat-seed-openclaw-account",handlerId:"wechat.seedOpenClawAccount",outputId:"openclawWeixinAccountFile",kind:"build-file",required:true,mode:"0600"};var WECHAT_SEED_OPENCLAW_ACCOUNT_HOOK_ID=WECHAT_OPENCLAW_ACCOUNT_FILE_CONTRACT.handlerId;var WECHAT_SEED_OPENCLAW_ACCOUNT_PLAN_HOOK_ID=WECHAT_OPENCLAW_ACCOUNT_FILE_CONTRACT.planHookId;var WECHAT_OPENCLAW_ACCOUNT_FILE_OUTPUT_ID=WECHAT_OPENCLAW_ACCOUNT_FILE_CONTRACT.outputId;var WECHAT_TOKEN_PLACEHOLDER="openshell:resolve:env:WECHAT_BOT_TOKEN";function authorizeWechatAccountFilePlaceholders(value){const content=isPlainDataObject2(value)?ownDataPropertyValue2(value,"content"):void 0;if(!isPlainDataObject2(value)||!hasExactlyOwnDataProperties2(value,["content","mode","path"])||!isWechatAccountFilePath(ownDataPropertyValue2(value,"path"))||ownDataPropertyValue2(value,"mode")!==WECHAT_OPENCLAW_ACCOUNT_FILE_CONTRACT.mode||!isPlainDataObject2(content)||!hasOnlyOwnDataProperties(content,["baseUrl","savedAt","token","userId"])||!hasOwnDataProperty(content,"savedAt")||!hasOwnDataProperty(content,"token")||ownDataPropertyValue2(content,"token")!==WECHAT_TOKEN_PLACEHOLDER||!isNonEmptyString(ownDataPropertyValue2(content,"savedAt"))||!isOptionalNonEmptyString(content,"baseUrl")||!isOptionalNonEmptyString(content,"userId")){return[]}return[{path:["content","token"],value:WECHAT_TOKEN_PLACEHOLDER}]}function isWechatAccountFilePath(value){if(typeof value!=="string")return false;const prefix="openclaw-weixin/accounts/";const suffix=".json";if(!value.startsWith(prefix)||!value.endsWith(suffix))return false;const accountId=value.slice(prefix.length,-suffix.length);return accountId===accountId.trim()&&isSafeWechatAccountId(accountId)}function isSafeWechatAccountId(accountId){return accountId.length>0&&accountId!=="."&&accountId!==".."&&!/[\\/\0-\x1F\x7F]/.test(accountId)&&!accountId.includes("..")}function isPlainDataObject2(value){if(value===null||typeof value!=="object"||Array.isArray(value))return false;const prototype=Object.getPrototypeOf(value);return prototype===Object.prototype||prototype===null}function ownDataPropertyValue2(value,key){const descriptor=Object.getOwnPropertyDescriptor(value,key);return descriptor&&"value"in descriptor?descriptor.value:void 0}function hasOwnDataProperty(value,key){const descriptor=Object.getOwnPropertyDescriptor(value,key);return descriptor!==void 0&&"value"in descriptor}function hasExactlyOwnDataProperties2(value,expected){const actual=Object.getOwnPropertyNames(value).sort();return actual.length===expected.length&&actual.every((key,index)=>key===expected[index])}function hasOnlyOwnDataProperties(value,allowed){return Object.getOwnPropertyNames(value).every(key=>allowed.includes(key))}function isNonEmptyString(value){return typeof value==="string"&&value.length>0}function isOptionalNonEmptyString(value,key){return!hasOwnDataProperty(value,key)||isNonEmptyString(ownDataPropertyValue2(value,key))}var wechatManifest={schemaVersion:1,id:"wechat",displayName:"WeChat",description:"WeChat (personal) bot messaging",enrollmentHelp:"Captured automatically via a host-side QR scan during onboard \u2014 pair the bot by scanning the QR with WeChat on your phone (Discover \u2192 Scan). DM-only.",supportedAgents:["openclaw","hermes"],auth:{mode:"host-qr"},inputs:[{id:"botToken",kind:"secret",required:true,envKey:"WECHAT_BOT_TOKEN",prompt:{label:"WeChat Bot Token",help:"Captured automatically via a host-side QR scan during onboard \u2014 pair the bot by scanning the QR with WeChat on your phone (Discover \u2192 Scan). DM-only."}},{id:"accountId",kind:"config",required:true,envKey:"WECHAT_ACCOUNT_ID",statePath:"wechatConfig.accountId"},{id:"baseUrl",kind:"config",required:false,envKey:"WECHAT_BASE_URL",statePath:"wechatConfig.baseUrl"},{id:"userId",kind:"config",required:false,envKey:"WECHAT_USER_ID",statePath:"wechatConfig.userId"},{id:"allowedIds",kind:"config",required:false,envKey:"WECHAT_ALLOWED_IDS",statePath:"allowedIds.wechat",prompt:{label:"WeChat User ID(s) (DM allowlist)",help:"Optional: restrict who can DM the bot. The WeChat user id of the operator who scanned is added automatically; supply additional ids as a comma-separated list.",emptyValueMessage:"bot will require manual pairing"}}],credentials:[{id:"wechatBotToken",sourceInput:"botToken",providerName:"{sandboxName}-wechat-bridge",providerEnvKey:"WECHAT_BOT_TOKEN",placeholder:"openshell:resolve:env:WECHAT_BOT_TOKEN"}],policyPresets:[{name:"wechat",policyKeys:["wechat_bridge"]}],render:[{id:"wechat-openclaw-plugin",kind:"json-fragment",agent:"openclaw",target:"openclaw.json",fragment:{path:"plugins.entries.openclaw-weixin",value:{enabled:true}}},{id:"wechat-openclaw-channel",kind:"json-fragment",agent:"openclaw",target:"openclaw.json",fragment:{path:"channels.openclaw-weixin",value:{}}},{id:"wechat-hermes-env",kind:"env-lines",agent:"hermes",target:"~/.hermes/.env",lines:["WEIXIN_TOKEN={{credential.wechatBotToken.placeholder}}","WEIXIN_ACCOUNT_ID={{wechatConfig.accountId}}","WEIXIN_BASE_URL={{wechatConfig.baseUrl}}","WEIXIN_ALLOWED_USERS={{allowedIds.wechat.csv}}"]},{id:"wechat-hermes-platform",kind:"json-fragment",agent:"hermes",target:"~/.hermes/config.yaml",fragment:{path:"platforms.weixin",value:{enabled:true}}}],runtime:{openclaw:{channelName:"openclaw-weixin",visibility:{configKeys:["openclaw-weixin"],logPatterns:["wechat","openclaw-weixin"]},nodePreloads:[{module:"wechat-diagnostics",injectInto:["boot","connect"],optional:false,installMessage:"[channels] Installing WeChat diagnostics (provider readiness + inference errors)",installedMessage:"[channels] WeChat diagnostics installed (NODE_OPTIONS updated)"}]}},agentPackages:[{id:"openclawPluginPackage",agent:"openclaw",manager:"openclaw-plugin",spec:"npm:@tencent-weixin/openclaw-weixin@2.4.3",pin:true,integrity:"sha512-dPQbidUNWigC6V10vGW4i+GLH09x+6zUhafZRjuxkJ9GDu8o62WBsnUTojp4KqUH756hz+t2v9khiCRSi0dBDw==",tarballUrl:"https://registry.npmjs.org/@tencent-weixin/openclaw-weixin/-/openclaw-weixin-2.4.3.tgz",runtimeLock:{cachePath:"/usr/local/share/nemoclaw/wechat-npm-cache",installCacheEnvKey:"NEMOCLAW_WECHAT_NPM_INSTALL_CACHE",lockFile:"/usr/local/lib/nemoclaw/wechat-runtime/package-lock.json",projectsRoot:"/sandbox/.openclaw/npm/projects",verifierPath:"/usr/local/lib/nemoclaw/verify-wechat-runtime-lock.mts",offline:true,legacyPeerDeps:true},required:true}],hooks:[{id:"wechat-host-qr",phase:"enroll",handler:"wechat.ilinkLogin",inputs:["allowedIds"],outputs:[{id:"botToken",kind:"secret",required:true},{id:"accountId",kind:"config",required:true},{id:"baseUrl",kind:"config"},{id:"userId",kind:"config"},{id:"allowedIds",kind:"config"}],onFailure:"skip-channel"},{id:"wechat-config-prompt",phase:"enroll",handler:"common.configPrompt",outputs:[{id:"allowedIds",kind:"config"}]},{id:WECHAT_OPENCLAW_ACCOUNT_FILE_CONTRACT.planHookId,phase:"post-agent-install",handler:WECHAT_OPENCLAW_ACCOUNT_FILE_CONTRACT.handlerId,agents:["openclaw"],inputs:["wechatConfig.accountId","wechatConfig.baseUrl","wechatConfig.userId","credential.wechatBotToken.placeholder"],outputs:[{id:"openclawWeixinAccountsIndex",kind:"build-file",required:true},{id:WECHAT_OPENCLAW_ACCOUNT_FILE_CONTRACT.outputId,kind:WECHAT_OPENCLAW_ACCOUNT_FILE_CONTRACT.kind,required:WECHAT_OPENCLAW_ACCOUNT_FILE_CONTRACT.required},{id:"openclawConfigPatch",kind:"build-file",required:true}],onFailure:"abort"},{id:"wechat-health-check",phase:"health-check",handler:"wechat.healthCheck",inputs:["wechatConfig.accountId"],onFailure:"abort"}]};var whatsappManifest={schemaVersion:1,id:"whatsapp",displayName:"WhatsApp",description:"WhatsApp Web messaging (QR pairing)",enrollmentHelp:"WhatsApp Web pairs via QR code scanned with your phone \u2014 no host-side token. After the sandbox is running, run `openshell term` and then use `openclaw channels login --channel whatsapp` for OpenClaw or `hermes whatsapp` for Hermes to display the QR.",enrollmentNotes:["After pairing, run `nemoclaw channels status --channel whatsapp`. OpenClaw reports inbound delivery evidence; Hermes reports gateway and dashboard session-path diagnostics."],supportedAgents:["openclaw","hermes"],auth:{mode:"in-sandbox-qr"},inputs:[{id:"mode",kind:"config",required:false,envKey:"WHATSAPP_MODE",statePath:"whatsappConfig.mode",validValues:["self-chat","bot"],defaultValue:"self-chat",prompt:{label:"WhatsApp reply mode",help:"self-chat replies only to messages the paired account sends to itself. bot replies to other senders and stops replying to that self-chat: an unknown sender receives a pairing code you approve with `hermes pairing approve whatsapp `, unless you set WHATSAPP_ALLOWED_IDS to a fixed sender list before this command.",emptyValueMessage:"the sandbox replies only in your own self-chat"}},{id:"allowedIds",kind:"config",required:false,envKey:"WHATSAPP_ALLOWED_IDS",statePath:"allowedIds.whatsapp"}],credentials:[],policyPresets:["whatsapp"],render:[{id:"whatsapp-openclaw-channel",kind:"json-fragment",agent:"openclaw",target:"openclaw.json",fragment:{path:"channels.whatsapp",value:{enabled:true,accounts:{default:{enabled:true,healthMonitor:{enabled:false}}}}}},{id:"whatsapp-openclaw-plugin",kind:"json-fragment",agent:"openclaw",target:"openclaw.json",fragment:{path:"plugins.entries.whatsapp",value:{enabled:true}}},{id:"whatsapp-hermes-env",kind:"env-lines",agent:"hermes",target:"~/.hermes/.env",lines:["WHATSAPP_ENABLED=true","WHATSAPP_MODE={{whatsappConfig.mode}}","WHATSAPP_DM_POLICY={{whatsappConfig.dmPolicy}}","WHATSAPP_ALLOWED_USERS={{allowedIds.whatsapp.csv}}"]},{id:"whatsapp-hermes-platform",kind:"json-fragment",agent:"hermes",target:"~/.hermes/config.yaml",fragment:{path:"platforms.whatsapp",value:{enabled:true}}}],runtime:{openclaw:{channelName:"whatsapp",visibility:{configKeys:["whatsapp"],logPatterns:["whatsapp"]},nodePreloads:[{module:"whatsapp-qr-compact",injectInto:["connect"],optional:true,installMessage:"[channels] Installing WhatsApp compact-QR renderer (scan-friendly pairing)"}]}},agentPackages:[{id:"openclawPluginPackage",agent:"openclaw",manager:"openclaw-plugin",spec:"npm:@openclaw/whatsapp@{{openclaw.version}}",pin:true,integrityByVersion:{"2026.7.1":"sha512-wLY/Omc5fleRpl2lKGN8sxt/8hYfHGwLRezmWsk8oCbea5pRKUPE6ZX+wJO1O52NOJkAGCuiXvS7x0qIeKxXbQ=="},tarballUrlByVersion:{"2026.7.1":"https://registry.npmjs.org/@openclaw/whatsapp/-/whatsapp-2026.7.1.tgz"},required:true}],hooks:[{id:"whatsapp-config-prompt",phase:"enroll",handler:"common.configPrompt",agents:["hermes"],outputs:[{id:"mode",kind:"config"}]},{id:"whatsapp-status-health",phase:"status",handler:"whatsapp.statusHealth",agents:["openclaw","hermes"],outputs:[{id:"channelHealth",kind:"status"}]}]};var BUILT_IN_CHANNEL_MANIFESTS=[telegramManifest,discordManifest,wechatManifest,slackManifest,whatsappManifest,teamsManifest,googlechatManifest];function createBuiltInChannelManifestRegistry(){return createChannelManifestRegistry(BUILT_IN_CHANNEL_MANIFESTS)}var EXACT_TEMPLATE_PATTERN=/^\{\{\s*([^}]+?)\s*\}\}$/;var TEMPLATE_REFERENCE_PATTERN=/\{\{\s*([^}]+?)\s*\}\}/g;function resolvedRenderTemplateReference(value){return{matched:true,value}}function resolveSandboxNameTemplate(value,sandboxName){return value.replaceAll("{sandboxName}",sandboxName)}function resolveRenderTemplatesInValue(value,context){if(typeof value==="string")return resolveRenderTemplatesInString(value,context);if(Array.isArray(value)){if(value.length===0)return value;const resolved=value.map(entry=>resolveRenderTemplatesInValue(entry,context)).filter(entry=>entry!==void 0);return resolved.length>0?resolved:void 0}if(value&&typeof value==="object"){const sourceEntries=Object.entries(value);if(sourceEntries.length===0)return value;const entries=sourceEntries.map(([key,entry])=>[key,resolveRenderTemplatesInValue(entry,context)]).filter(entry=>entry[1]!==void 0);return entries.length>0?Object.fromEntries(entries):void 0}return value}function isTruthyRenderTemplate(value,context){if(!value)return true;const resolved=resolveRenderTemplatesInString(value,context);if(resolved===void 0||resolved===null||resolved===false)return false;if(Array.isArray(resolved))return resolved.length>0;if(typeof resolved==="object")return Object.keys(resolved).length>0;if(typeof resolved==="string")return resolved.trim().length>0;return true}function resolveRenderTemplatesInString(value,context){const exact=value.match(EXACT_TEMPLATE_PATTERN);if(exact?.[1])return resolveTemplateReference(exact[1].trim(),context);let omitted=false;const resolved=value.replace(TEMPLATE_REFERENCE_PATTERN,(match,reference)=>{const replacement=resolveTemplateReference(reference.trim(),context);if(replacement===void 0||replacement===null){omitted=true;return""}if(Array.isArray(replacement))return replacement.map(String).join(",");if(typeof replacement==="object")return JSON.stringify(replacement);return String(replacement)});return omitted?void 0:resolved}function resolveTemplateReference(reference,context){const resolved=context.referenceResolver?.(reference,context);return resolved?.matched?resolved.value:"{{"+reference+"}}"}function allowedIds(context,channel){return parseList(stateValue(context,`allowedIds.${channel}`))}function stateValue(context,path5){const stateInput=context.inputs.find(input=>input.statePath===path5);if(stateInput?.value!==void 0)return stateInput.value;const inputId=path5.split(".").at(-1);return context.inputs.find(input=>input.inputId===inputId)?.value}function parseList(value){if(Array.isArray(value))return unique(value.map(String).map(cleanString).filter(Boolean));const text=cleanString(value);if(!text)return[];return unique(text.split(",").map(cleanString).filter(Boolean))}function parseBoolean(value){if(typeof value==="boolean")return value;const text=cleanString(value)?.toLowerCase();if(text==="1"||text==="true"||text==="yes"||text==="on")return true;if(text==="0"||text==="false"||text==="no"||text==="off")return false;return void 0}function nonEmptyString(value){return cleanString(value)||void 0}function cleanString(value){const text=String(value??"");if(/[\r\n]/.test(text)){throw new Error("Messaging template values must not contain line breaks.")}return text.trim()}function nonEmptyArray(values){return values.length>0?[...values]:void 0}function nonEmptyCsv(values){return values.length>0?values.join(","):void 0}function nonEmptyObject(value){return Object.keys(value).length>0?value:void 0}function unique(values){return[...new Set(values)]}var resolveDiscordTemplateReference=(reference,context)=>{if(reference==="discordProxyUrl")return resolvedRenderTemplateReference(void 0);switch(reference){case"discord.guilds":return resolvedRenderTemplateReference(nonEmptyObject(discordGuilds(context)));case"discord.hasGuilds":return resolvedRenderTemplateReference(Object.keys(discordGuilds(context)).length>0);case"discord.guildIds.csv":return resolvedRenderTemplateReference(nonEmptyCsv(Object.keys(discordGuilds(context))));case"discord.allowedUsers.values":return resolvedRenderTemplateReference(nonEmptyArray(discordAllowedUsers(context)));case"discord.allowedUsers.csv":return resolvedRenderTemplateReference(nonEmptyCsv(discordAllowedUsers(context)));case"discord.allowedUsers.dmPolicy":return resolvedRenderTemplateReference(discordAllowedUsers(context).length>0?"allowlist":void 0);case"discord.allowAllUsers":return resolvedRenderTemplateReference(Object.keys(discordGuilds(context)).length>0&&discordAllowedUsers(context).length===0?true:void 0);case"discord.requireMention":return resolvedRenderTemplateReference(discordRequireMention(context));default:return void 0}};function discordGuilds(context){const serverIds=parseList(stateValue(context,"discordGuilds.serverId"));if(serverIds.length===0)return{};const users=parseList(stateValue(context,"discordGuilds.userIds"));const requireMention=parseBoolean(stateValue(context,"discordGuilds.requireMention"))??true;return Object.fromEntries(serverIds.map(serverId=>[serverId,{requireMention,...users.length>0?{users}:{}}]))}function discordAllowedUsers(context){const users=new Set(allowedIds(context,"discord"));for(const guild of Object.values(discordGuilds(context))){for(const user of guild.users??[])users.add(String(user))}return[...users]}function discordRequireMention(context){for(const guild of Object.values(discordGuilds(context))){if(typeof guild.requireMention==="boolean")return guild.requireMention}return true}var DEFAULT_AUDIENCE_TYPE="app-url";var APP_PRINCIPAL_DISCOVERY_SENTINEL="000000000000000000000";var resolveGooglechatTemplateReference=(reference,context)=>{switch(reference){case"googlechatConfig.audienceType":return resolvedRenderTemplateReference(nonEmptyString(stateValue(context,"googlechatConfig.audienceType"))??DEFAULT_AUDIENCE_TYPE);case"googlechatConfig.audience":return resolvedRenderTemplateReference(nonEmptyString(stateValue(context,"googlechatConfig.audience")));case"googlechatConfig.appPrincipal":return resolvedRenderTemplateReference(nonEmptyString(stateValue(context,"googlechatConfig.appPrincipal"))??APP_PRINCIPAL_DISCOVERY_SENTINEL);case"googlechatConfig.projectId":return resolvedRenderTemplateReference(nonEmptyString(stateValue(context,"googlechatConfig.projectId")));case"googlechatConfig.subscriptionName":return resolvedRenderTemplateReference(nonEmptyString(stateValue(context,"googlechatConfig.subscriptionName")));default:break}const allowReference=reference.match(/^allowedIds[.]googlechat[.](values|dmPolicy|csv)$/);if(!allowReference?.[1])return void 0;const ids=allowedIds(context,"googlechat");switch(allowReference[1]){case"values":return resolvedRenderTemplateReference(nonEmptyArray(ids));case"dmPolicy":return resolvedRenderTemplateReference(ids.length>0?"allowlist":void 0);case"csv":return resolvedRenderTemplateReference(ids.length>0?ids.join(","):void 0);default:return void 0}};var resolveSlackTemplateReference=(reference,context)=>{if(reference==="slackConfig.allowedChannels.csv"){return resolvedRenderTemplateReference(nonEmptyCsv(slackAllowedChannels(context)))}const allowedIdsReference=reference.match(/^allowedIds[.]slack[.](values|csv|dmPolicy|groupPolicy|channels)$/);if(!allowedIdsReference?.[1])return void 0;const ids=allowedIds(context,"slack");switch(allowedIdsReference[1]){case"values":return resolvedRenderTemplateReference(nonEmptyArray(ids));case"csv":return resolvedRenderTemplateReference(nonEmptyCsv(ids));case"dmPolicy":return resolvedRenderTemplateReference(ids.length>0?"allowlist":void 0);case"groupPolicy":return resolvedRenderTemplateReference(ids.length>0||slackAllowedChannels(context).length>0?"allowlist":void 0);case"channels":return resolvedRenderTemplateReference(slackChannelConfig(context,ids));default:return void 0}};function slackChannelConfig(context,users){const allowedChannels=slackAllowedChannels(context);const entry={enabled:true,requireMention:true,...users.length>0?{users:[...users]}:{}};if(allowedChannels.length>0){return Object.fromEntries(allowedChannels.map(channelId=>[channelId,{...entry}]))}return users.length>0?{"*":entry}:void 0}function slackAllowedChannels(context){return parseList(stateValue(context,"slackConfig.allowedChannels"))}var DEFAULT_TEAMS_WEBHOOK_PORT=3978;var resolveTeamsTemplateReference=(reference,context)=>{switch(reference){case"teamsConfig.appId":return resolvedRenderTemplateReference(nonEmptyString(stateValue(context,"teamsConfig.appId")));case"teamsConfig.tenantId":return resolvedRenderTemplateReference(nonEmptyString(stateValue(context,"teamsConfig.tenantId")));case"teamsConfig.webhookPort":return resolvedRenderTemplateReference(teamsWebhookPort(context));case"teamsConfig.requireMention":return resolvedRenderTemplateReference(parseBoolean(stateValue(context,"teamsConfig.requireMention")));default:break}const allowedIdsReference=reference.match(/^allowedIds[.]teams[.](values|csv|dmPolicy)$/);if(!allowedIdsReference?.[1])return void 0;const ids=allowedIds(context,"teams");switch(allowedIdsReference[1]){case"values":return resolvedRenderTemplateReference(nonEmptyArray(ids));case"csv":return resolvedRenderTemplateReference(nonEmptyCsv(ids));case"dmPolicy":return resolvedRenderTemplateReference(ids.length>0?"allowlist":void 0);default:return void 0}};function teamsWebhookPort(context){const raw=nonEmptyString(stateValue(context,"teamsConfig.webhookPort"));if(!raw)return DEFAULT_TEAMS_WEBHOOK_PORT;const port=Number(raw);if(!Number.isInteger(port)||port<1||port>65535){throw new Error("Microsoft Teams webhook port must be an integer TCP port between 1 and 65535.")}return port}var DEFAULT_PROXY_HOST="10.200.0.1";var DEFAULT_PROXY_PORT="3128";var DEFAULT_TELEGRAM_GROUP_POLICY="open";var TELEGRAM_GROUP_POLICIES=new Set(["open","allowlist","disabled"]);var resolveTelegramTemplateReference=(reference,context)=>{if(reference==="proxyUrl")return resolvedRenderTemplateReference(proxyUrl(context.env));if(reference==="telegramConfig.groupPolicy"){return resolvedRenderTemplateReference(telegramGroupPolicy(context))}if(reference==="telegramConfig.openclawGroups"){return resolvedRenderTemplateReference(telegramOpenClawGroups(context))}if(reference==="telegramConfig.requireMention"){return resolvedRenderTemplateReference(parseBoolean(stateValue(context,"telegramConfig.requireMention")))}const allowedIdsReference=reference.match(/^allowedIds[.]telegram[.](values|csv|dmPolicy)$/);if(!allowedIdsReference?.[1])return void 0;const ids=allowedIds(context,"telegram");switch(allowedIdsReference[1]){case"values":return resolvedRenderTemplateReference(nonEmptyArray(ids));case"csv":return resolvedRenderTemplateReference(nonEmptyCsv(ids));case"dmPolicy":return resolvedRenderTemplateReference(ids.length>0?"allowlist":void 0);default:return void 0}};function proxyUrl(env){const host=nonEmptyString(env?.NEMOCLAW_PROXY_HOST)??DEFAULT_PROXY_HOST;const port=nonEmptyString(env?.NEMOCLAW_PROXY_PORT)??DEFAULT_PROXY_PORT;return`http://${host}:${port}`}function telegramGroupPolicy(context){const value=nonEmptyString(stateValue(context,"telegramConfig.groupPolicy"));return value&&TELEGRAM_GROUP_POLICIES.has(value)?value:DEFAULT_TELEGRAM_GROUP_POLICY}function telegramOpenClawGroups(context){if(telegramGroupPolicy(context)!=="open")return void 0;const requireMention=parseBoolean(stateValue(context,"telegramConfig.requireMention"));return requireMention===true?{"*":{requireMention:true}}:void 0}var WECHAT_ILINK_HOSTS=new Set(["ilinkai.weixin.qq.com","ilinkai.wechat.com"]);var WECHAT_ILINK_IDC_HOST_PATTERN=/^idc-[0-9]+[.]weixin[.]qq[.]com$/;function normalizeWechatIlinkBaseUrl(value){const raw=String(value??"");if(/[\r\n]/.test(raw)){throw new Error("WeChat baseUrl must not contain line breaks.")}const text=raw.trim();if(!text)return void 0;let url;try{url=new URL(text)}catch{throw new Error("WeChat baseUrl must be a valid URL.")}if(url.protocol!=="https:"){throw new Error("WeChat baseUrl must use HTTPS.")}if(url.username||url.password){throw new Error("WeChat baseUrl must not include credentials.")}if(!isWechatIlinkHost(url.hostname)){throw new Error("WeChat baseUrl must use an expected iLink host.")}if(url.pathname&&url.pathname!=="/"||url.search||url.hash){throw new Error("WeChat baseUrl must be an iLink origin URL.")}return url.origin}function isWechatIlinkHost(hostname){const normalized=hostname.toLowerCase();return WECHAT_ILINK_HOSTS.has(normalized)||WECHAT_ILINK_IDC_HOST_PATTERN.test(normalized)}var resolveWechatTemplateReference=(reference,context)=>{const wechatConfig=reference.match(/^wechatConfig[.](accountId|baseUrl|userId)$/);if(wechatConfig?.[1]){if(wechatConfig[1]==="baseUrl"){return resolvedRenderTemplateReference(normalizeWechatIlinkBaseUrl(stateValue(context,"wechatConfig.baseUrl")))}return resolvedRenderTemplateReference(nonEmptyString(stateValue(context,"wechatConfig."+wechatConfig[1])))}const allowedIdsReference=reference.match(/^allowedIds[.]wechat[.](values|csv|dmPolicy)$/);if(!allowedIdsReference?.[1])return void 0;const ids=wechatAllowedIds(context);switch(allowedIdsReference[1]){case"values":return resolvedRenderTemplateReference(nonEmptyArray(ids));case"csv":return resolvedRenderTemplateReference(nonEmptyCsv(ids));case"dmPolicy":return resolvedRenderTemplateReference(ids.length>0?"allowlist":void 0);default:return void 0}};function wechatAllowedIds(context){const ids=allowedIds(context,"wechat");const userId=nonEmptyString(stateValue(context,"wechatConfig.userId"));return userId&&!ids.includes(userId)?[userId,...ids]:ids}var DEFAULT_WHATSAPP_MODE="self-chat";var BOT_WHATSAPP_MODE="bot";var WHATSAPP_MODES=new Set([DEFAULT_WHATSAPP_MODE,BOT_WHATSAPP_MODE]);var resolveWhatsappTemplateReference=(reference,context)=>{if(reference==="whatsappConfig.mode"){return resolvedRenderTemplateReference(whatsappMode(context))}if(reference==="whatsappConfig.dmPolicy"){return resolvedRenderTemplateReference(whatsappDmPolicy(context))}const allowedIdsReference=reference.match(/^allowedIds[.]whatsapp[.](values|csv)$/);if(!allowedIdsReference?.[1])return void 0;const ids=allowedIds(context,"whatsapp");switch(allowedIdsReference[1]){case"values":return resolvedRenderTemplateReference(nonEmptyArray(ids));case"csv":return resolvedRenderTemplateReference(nonEmptyCsv(ids));default:return void 0}};function whatsappMode(context){const value=nonEmptyString(stateValue(context,"whatsappConfig.mode"));return value&&WHATSAPP_MODES.has(value)?value:DEFAULT_WHATSAPP_MODE}function whatsappDmPolicy(context){if(whatsappMode(context)!==BOT_WHATSAPP_MODE)return void 0;return allowedIds(context,"whatsapp").length>0?"allowlist":"pairing"}var BUILT_IN_TEMPLATE_REFERENCE_RESOLVERS=[resolveTelegramTemplateReference,resolveDiscordTemplateReference,resolveWechatTemplateReference,resolveSlackTemplateReference,resolveWhatsappTemplateReference,resolveTeamsTemplateReference,resolveGooglechatTemplateReference];function createBuiltInRenderTemplateResolver(){return(reference,context)=>{for(const resolver of BUILT_IN_TEMPLATE_REFERENCE_RESOLVERS){const resolved=resolver(reference,context);if(resolved)return resolved}return void 0}}var import_node_crypto=__toESM(require("node:crypto"));function hashCredential(value){const normalized=String(value??"").trim();if(!normalized)return null;return import_node_crypto.default.createHash("sha256").update(normalized).digest("hex")}function planCredentialBindings(manifest,context,inputs,environment=process.env){return manifest.credentials.map(credential=>{const sourceInput=inputs.find(input=>input.inputId===credential.sourceInput);const credentialAvailable=sourceInput?.credentialAvailable===true||context.credentialAvailability?.[credential.id]===true||context.credentialAvailability?.[`${manifest.id}.${credential.id}`]===true;const envKey=sourceInput?.sourceEnv??credential.providerEnvKey;const credentialHash=credentialAvailable?hashCredential(environment[envKey])??void 0:void 0;return{channelId:manifest.id,credentialId:credential.id,sourceInput:credential.sourceInput,providerName:resolveSandboxNameTemplate(credential.providerName,context.sandboxName),providerEnvKey:credential.providerEnvKey,placeholder:credential.placeholder,credentialAvailable,...credentialHash!==void 0?{credentialHash}:{}}})}function planHostForward(manifest,inputs,active,referenceResolver,environment=process.env){if(!active||!manifest.hostForward)return void 0;const context={inputs,env:environment,referenceResolver};if(!isTruthyRenderTemplate(manifest.hostForward.when,context))return void 0;const portValue=resolveRenderTemplatesInValue(manifest.hostForward.port,context);const port=normalizeForwardPort(manifest.id,portValue);return{channelId:manifest.id,port,label:manifest.hostForward.label}}function normalizeForwardPort(channelId,value){const port=typeof value==="number"?value:Number(String(value??"").trim());if(!Number.isInteger(port)||port<1||port>65535){throw new Error(`Channel manifest '${channelId}' declares invalid host forward port '${String(value)}'.`)}return port}var OPENSHELL_ENV_PLACEHOLDER_PREFIX="openshell:resolve:env:";var OPENSHELL_ALIAS_PLACEHOLDER_RE=/^[A-Za-z0-9]+-OPENSHELL-RESOLVE-ENV-(.+)$/;function normalizeProviderPlaceholderForEnvKey(value,envKey){if(value.startsWith(OPENSHELL_ENV_PLACEHOLDER_PREFIX)){return placeholderSuffixMatchesEnvKey(value.slice(OPENSHELL_ENV_PLACEHOLDER_PREFIX.length),envKey)?`${OPENSHELL_ENV_PLACEHOLDER_PREFIX}${envKey}`:null}const aliasMatch=value.match(OPENSHELL_ALIAS_PLACEHOLDER_RE);if(!aliasMatch||!placeholderSuffixMatchesEnvKey(aliasMatch[1],envKey)){return null}return value.replace(/-OPENSHELL-RESOLVE-ENV-.+$/,`-OPENSHELL-RESOLVE-ENV-${envKey}`)}function placeholderSuffixMatchesEnvKey(suffix,envKey){if(suffix===envKey)return true;const revisionMatch=suffix.match(/^v[0-9]+_(.+)$/);return revisionMatch?.[1]===envKey}function hasFullPersistedCredentialBindingShape(binding){return typeof binding.channelId==="string"&&typeof binding.credentialId==="string"&&typeof binding.sourceInput==="string"&&typeof binding.providerName==="string"&&typeof binding.providerEnvKey==="string"&&typeof binding.placeholder==="string"&&typeof binding.credentialAvailable==="boolean"}function normalizeFullPersistedCredentialBindings(bindings){return bindings.map(binding=>({channelId:binding.channelId,credentialId:binding.credentialId,sourceInput:binding.sourceInput,providerName:binding.providerName,providerEnvKey:binding.providerEnvKey,placeholder:normalizeProviderPlaceholderForEnvKey(binding.placeholder,binding.providerEnvKey)??binding.placeholder,credentialAvailable:binding.credentialAvailable===true,...typeof binding.credentialHash==="string"?{credentialHash:binding.credentialHash}:{}}))}function normalizePersistedAgentCredentialPlaceholders(render,credentialBindings){const credentialEnvKeys=new Set(credentialBindings.map(binding=>binding.providerEnvKey).filter(Boolean));if(credentialEnvKeys.size===0)return[...render];return render.map(entry=>{if(entry.kind!=="env-lines")return entry;return{...entry,lines:entry.lines.map(line=>normalizeCredentialEnvLine(line,credentialEnvKeys))}})}function normalizeCredentialEnvLine(line,credentialEnvKeys){const index=line.indexOf("=");if(index<=0)return line;const envKey=line.slice(0,index).trim();if(!credentialEnvKeys.has(envKey))return line;const value=line.slice(index+1);const normalized=normalizeProviderPlaceholderForEnvKey(value,envKey);return normalized?`${envKey}=${normalized}`:line}function normalizePersistedSandboxMessagingPlanShape(plan,environment=process.env){const manifestRegistry=createBuiltInChannelManifestRegistry();const disabledChannels=plan.disabledChannels.filter(channelId=>typeof channelId==="string");const disabledSet=new Set(disabledChannels);const channels=plan.channels.map(channel=>normalizePersistedChannel(channel,disabledSet,manifestRegistry.get(channel.channelId),environment));const credentialBindings=normalizePersistedCredentialBindings(plan,channels,manifestRegistry,environment);const normalizedPlan={...plan,channels,disabledChannels,credentialBindings,networkPolicy:plan.networkPolicy&&Array.isArray(plan.networkPolicy.entries)?plan.networkPolicy:{presets:[],entries:[]},agentRender:normalizePersistedAgentCredentialPlaceholders(Array.isArray(plan.agentRender)?[...plan.agentRender]:[],credentialBindings),buildSteps:Array.isArray(plan.buildSteps)?[...plan.buildSteps]:[],...plan.runtimeSetup!==void 0?{runtimeSetup:normalizeRuntimeSetup(plan.runtimeSetup)}:{},stateUpdates:Array.isArray(plan.stateUpdates)?[...plan.stateUpdates]:[],healthChecks:Array.isArray(plan.healthChecks)?[...plan.healthChecks]:[]};return normalizedPlan}function normalizePersistedChannel(channel,disabledSet,manifest,environment){const disabled=channel.disabled??disabledSet.has(channel.channelId);const configured=channel.configured??true;const hasFullShape=hasFullChannelShape(channel);const inputs=hasFullShape?normalizeFullInputs(channel.channelId,channel.inputs??[]):normalizePersistedInputs(channel,manifest);const active=channel.active??(configured&&!disabled&&requiredInputsAvailable(manifest,inputs));const hostForward=manifest?planHostForward(manifest,inputs,active&&!disabled,createBuiltInRenderTemplateResolver(),environment):void 0;return{channelId:channel.channelId,displayName:channel.displayName??manifest?.displayName??channel.channelId,authMode:channel.authMode??manifest?.auth.mode??"none",active,selected:channel.selected??configured,configured,disabled,inputs,...hostForward?{hostForward}:{},hooks:Array.isArray(channel.hooks)?[...channel.hooks]:[]}}function normalizePersistedInputs(channel,manifest){const persistedById=new Map((channel.inputs??[]).filter(input=>typeof input.inputId==="string").map(input=>[input.inputId,input]));const fromManifest=(manifest?.inputs??[]).map(input=>inputReferenceFromManifest(channel.channelId,input,persistedById.get(input.id)));const manifestInputIds=new Set((manifest?.inputs??[]).map(input=>input.id));const unknownInputs=[...persistedById.values()].flatMap(input=>{if(!input.inputId||manifestInputIds.has(input.inputId))return[];return[normalizeUnknownInput(channel.channelId,input)]});return[...fromManifest,...unknownInputs]}function normalizeFullInputs(channelId,inputs){return inputs.filter(input=>typeof input.inputId==="string").map(input=>({channelId:typeof input.channelId==="string"?input.channelId:channelId,inputId:input.inputId,kind:input.kind==="secret"||input.kind==="config"?input.kind:"config",required:typeof input.required==="boolean"?input.required:false,...typeof input.sourceEnv==="string"?{sourceEnv:input.sourceEnv}:{},...typeof input.statePath==="string"?{statePath:input.statePath}:{},...input.credentialAvailable!==void 0?{credentialAvailable:input.credentialAvailable}:{},...input.value!==void 0?{value:input.value}:{}}))}function inputReferenceFromManifest(channelId,input,persisted){return{channelId,inputId:input.id,kind:input.kind,required:input.required,...input.envKey?{sourceEnv:input.envKey}:{},...input.kind==="config"&&input.statePath?{statePath:input.statePath}:{},...persisted?.credentialAvailable!==void 0?{credentialAvailable:persisted.credentialAvailable}:{},...persisted?.value!==void 0?{value:persisted.value}:{}}}function normalizeUnknownInput(channelId,input){const kind=input.kind==="secret"||input.kind==="config"?input.kind:"config";return{channelId,inputId:input.inputId,kind,required:input.required===true,...typeof input.sourceEnv==="string"?{sourceEnv:input.sourceEnv}:{},...typeof input.statePath==="string"?{statePath:input.statePath}:{},...input.credentialAvailable!==void 0?{credentialAvailable:input.credentialAvailable}:{},...input.value!==void 0?{value:input.value}:{}}}function requiredInputsAvailable(manifest,inputs){if(!manifest)return true;return manifest.inputs.every(manifestInput=>{if(!manifestInput.required)return true;const input=inputs.find(entry=>entry.inputId===manifestInput.id);if(!input)return false;if(input.kind==="secret")return input.credentialAvailable===true;if(input.value===void 0)return false;return typeof input.value==="string"?input.value.trim().length>0:true})}function normalizePersistedCredentialBindings(plan,channels,manifestRegistry,environment){const persisted=plan.credentialBindings??[];if(Array.isArray(plan.credentialBindings)&&plan.channels.every(hasFullChannelShape)&&persisted.every(hasFullPersistedCredentialBindingShape)){return normalizeFullPersistedCredentialBindings(persisted)}const manifests=channels.flatMap(channel=>{const manifest=manifestRegistry.get(channel.channelId);return manifest?[manifest]:[]});const planForBindings={...plan,channels,credentialBindings:[],networkPolicy:{presets:[],entries:[]},agentRender:[],buildSteps:[],runtimeSetup:{nodePreloads:[],envAliases:[],secretScans:[]},stateUpdates:[],healthChecks:[]};const generated=credentialBindingsFromManifests(planForBindings,manifests,new Map(channels.map(channel=>[channel.channelId,channel.inputs])),environment);return generated.map(binding=>overlayPersistedCredentialBinding(binding,persisted))}function credentialBindingsFromManifests(plan,manifests,inputRegistry,environment){const context=compilerContext(plan);return manifests.flatMap(manifest=>planCredentialBindings(manifest,context,inputRegistry.get(manifest.id)??[],environment).map(binding=>overlayPersistedCredentialBinding(binding,plan.credentialBindings)))}function overlayPersistedCredentialBinding(binding,persisted){const match=persisted.find(candidate=>credentialBindingMatches(binding,candidate));if(!match)return binding;return{...binding,credentialAvailable:typeof match.credentialAvailable==="boolean"?match.credentialAvailable:binding.credentialAvailable,...typeof match.credentialHash==="string"&&match.credentialHash.length>0?{credentialHash:match.credentialHash}:binding.credentialHash?{credentialHash:binding.credentialHash}:{}}}function credentialBindingMatches(binding,candidate){if(candidate.channelId&&candidate.channelId!==binding.channelId)return false;if(candidate.providerEnvKey&&candidate.providerEnvKey===binding.providerEnvKey)return true;if(candidate.credentialId&&candidate.credentialId===binding.credentialId)return true;if(candidate.sourceInput&&candidate.sourceInput===binding.sourceInput)return true;return false}function hasFullChannelShape(channel){return typeof channel.displayName==="string"&&typeof channel.authMode==="string"&&typeof channel.active==="boolean"&&typeof channel.selected==="boolean"&&typeof channel.configured==="boolean"&&typeof channel.disabled==="boolean"&&Array.isArray(channel.inputs)}function normalizeRuntimeSetup(setup){return{nodePreloads:Array.isArray(setup?.nodePreloads)?[...setup.nodePreloads]:[],envAliases:Array.isArray(setup?.envAliases)?[...setup.envAliases]:[],secretScans:Array.isArray(setup?.secretScans)?[...setup.secretScans]:[]}}function compilerContext(plan){return{sandboxName:plan.sandboxName,agent:plan.agent,workflow:plan.workflow,isInteractive:false,configuredChannels:plan.channels.map(channel=>channel.channelId),disabledChannels:plan.disabledChannels,credentialAvailability:credentialAvailabilityFromPlan(plan)}}function credentialAvailabilityFromPlan(plan){const availability={};for(const channel of plan.channels){for(const input of channel.inputs){if(input.kind!=="secret"||input.credentialAvailable!==true)continue;availability[`${channel.channelId}.${input.inputId}`]=true;if(input.sourceEnv)availability[input.sourceEnv]=true}}for(const credential of plan.credentialBindings){if(!credential.credentialAvailable)continue;availability[credential.credentialId]=true;availability[`${credential.channelId}.${credential.credentialId}`]=true;availability[`${credential.channelId}.${credential.sourceInput}`]=true;availability[credential.providerEnvKey]=true}return availability}function normalizeMessagingChannelId(channelId){return channelId.trim().toLowerCase()}function enabledPlanChannels(plan){const disabled=new Set((plan.disabledChannels??[]).map(normalizeMessagingChannelId).filter(Boolean));return plan.channels.filter(channel=>{const channelId=normalizeMessagingChannelId(channel.channelId);return channelId.length>0&&channel.active&&!channel.disabled&&!disabled.has(channelId)})}function selectActiveMessagingChannelIds(plan){const seen=new Set;const channels=[];for(const item of enabledPlanChannels(plan)){const channel=normalizeMessagingChannelId(item.channelId);if(!channel||seen.has(channel))continue;seen.add(channel);channels.push(channel)}return channels}function selectEnabledMessagingAgentRender(plan){const active=new Set(selectActiveMessagingChannelIds(plan));return plan.agentRender.filter(render=>render.agent===plan.agent&&active.has(normalizeMessagingChannelId(render.channelId)))}function selectEnabledPostAgentInstallBuildFiles(plan){const active=new Set(selectActiveMessagingChannelIds(plan));const channels=enabledPlanChannels(plan);return plan.buildSteps.filter(step=>{const channelId=normalizeMessagingChannelId(step.channelId);if(!active.has(channelId)||step.kind!=="build-file")return false;if(!step.hookId)return true;const matchingChannels=channels.filter(channel=>normalizeMessagingChannelId(channel.channelId)===channelId);if(matchingChannels.length!==1)return false;const matchedHook=matchingChannels[0]?.hooks?.find(hook=>hook.id===step.hookId);return matchedHook!==void 0&&matchedHook.phase==="post-agent-install"})}function parseSandboxMessagingPlan(value,options={}){if(!isObjectRecord(value)||value.schemaVersion!==1||typeof value.sandboxName!=="string"||typeof value.agent!=="string"||typeof value.workflow!=="string"||!Array.isArray(value.channels)||!Array.isArray(value.disabledChannels)||!isOptionalObjectArray(value,"credentialBindings")||Object.hasOwn(value,"networkPolicy")&&!isObjectRecord(value.networkPolicy)||!isOptionalObjectArray(value,"agentRender")||!isOptionalObjectArray(value,"buildSteps")||!isRuntimeSetup(value.runtimeSetup)||!isOptionalObjectArray(value,"stateUpdates")||!isOptionalObjectArray(value,"healthChecks")){return null}if(options.sandboxName&&value.sandboxName!==options.sandboxName)return null;if(options.agent&&value.agent!==options.agent)return null;const supported=Array.isArray(options.supportedChannelIds)?new Set(options.supportedChannelIds):null;const normalizedChannelIds=new Set;for(const channel of value.channels){if(!isObjectRecord(channel)||typeof channel.channelId!=="string")return null;const normalizedChannelId=normalizeMessagingChannelId(channel.channelId);if(!normalizedChannelId||normalizedChannelId!==channel.channelId||normalizedChannelIds.has(normalizedChannelId)){return null}if(Object.hasOwn(channel,"configured")&&typeof channel.configured!=="boolean"){return null}if(Object.hasOwn(channel,"active")&&typeof channel.active!=="boolean")return null;if(Object.hasOwn(channel,"disabled")&&typeof channel.disabled!=="boolean")return null;if(Object.hasOwn(channel,"inputs")&&!Array.isArray(channel.inputs))return null;if(Object.hasOwn(channel,"hostForward")&&!isHostForward(channel.hostForward))return null;if(Object.hasOwn(channel,"hooks")&&!Array.isArray(channel.hooks))return null;if(Array.isArray(channel.inputs)&&channel.inputs.some(input=>!isObjectRecord(input)||typeof input.inputId!=="string"||Object.hasOwn(input,"channelId")&&input.channelId!==normalizedChannelId)){return null}if(Array.isArray(channel.hooks)&&channel.hooks.some(hook=>!isObjectRecord(hook)||Object.hasOwn(hook,"channelId")&&hook.channelId!==normalizedChannelId)){return null}if(Object.hasOwn(channel,"hostForward")&&isObjectRecord(channel.hostForward)&&channel.hostForward.channelId!==normalizedChannelId){return null}if(supported&&!supported.has(channel.channelId))return null;normalizedChannelIds.add(normalizedChannelId)}if(!value.disabledChannels.every(isCanonicalMessagingChannelId))return null;const disabledChannelIds=new Set(value.disabledChannels);if(disabledChannelIds.size!==value.disabledChannels.length||[...disabledChannelIds].some(channelId=>!normalizedChannelIds.has(channelId))||value.channels.some(channel=>isObjectRecord(channel)&&channel.disabled===true!==disabledChannelIds.has(String(channel.channelId)))){return null}if(!hasCanonicalChannelReferences(value.credentialBindings)||!hasMatchingAgentRenderEntries(value.agentRender,value.agent)||!hasCanonicalChannelReferences(value.agentRender)||!hasCanonicalChannelReferences(value.buildSteps)||!hasCanonicalChannelReferences(value.stateUpdates)||!hasCanonicalChannelReferences(value.healthChecks)||!hasCanonicalNetworkPolicyReferences(value.networkPolicy)||!hasCanonicalRuntimeSetupReferences(value.runtimeSetup)){return null}return cloneSandboxMessagingPlan(normalizePersistedSandboxMessagingPlanShape(value,options.environment))}function hasMatchingAgentRenderEntries(value,agent){return!Array.isArray(value)||value.every(render=>isObjectRecord(render)&&render.agent===agent)}function cloneSandboxMessagingPlan(plan){return JSON.parse(JSON.stringify(plan))}function isOptionalObjectArray(value,key){if(!Object.hasOwn(value,key))return true;const entries=value[key];return Array.isArray(entries)&&entries.every(isObjectRecord)}function isHostForward(value){return isObjectRecord(value)&&typeof value.channelId==="string"&&typeof value.port==="number"&&Number.isInteger(value.port)&&value.port>=1&&value.port<=65535&&typeof value.label==="string"}function isRuntimeSetup(value){if(value===void 0)return true;return isObjectRecord(value)&&Array.isArray(value.nodePreloads)&&Array.isArray(value.envAliases)&&Array.isArray(value.secretScans)&&value.nodePreloads.every(isObjectRecord)&&value.envAliases.every(isObjectRecord)&&value.secretScans.every(isObjectRecord)}function isCanonicalMessagingChannelId(value){return typeof value==="string"&&value.length>0&&normalizeMessagingChannelId(value)===value}function hasCanonicalChannelReferences(value){return value===void 0||Array.isArray(value)&&value.every(entry=>isObjectRecord(entry)&&isCanonicalMessagingChannelId(entry.channelId))}function hasCanonicalNetworkPolicyReferences(value){if(!isObjectRecord(value)||!Object.hasOwn(value,"entries"))return true;return hasCanonicalChannelReferences(value.entries)}function hasCanonicalRuntimeSetupReferences(value){if(value===void 0)return true;if(!isObjectRecord(value))return false;return["nodePreloads","envAliases","secretScans"].every(field=>hasCanonicalChannelReferences(value[field]))}var import_node_buffer=require("node:buffer");var import_node_crypto2=require("node:crypto");var import_node_util=require("node:util");function listMessagingCredentialEnvAssignments(options={}){return selectManifests(options).flatMap(manifest=>{const credentialsByTemplate=new Map(manifest.credentials.map(credential=>[`{{credential.${credential.id}.placeholder}}`,credential]));return manifest.render.flatMap(render=>{if(options.agent&&render.agent!==options.agent)return[];if(render.kind!=="env-lines")return[];return render.lines.flatMap(line=>{const separator=line.indexOf("=");if(separator<=0)return[];const credential=credentialsByTemplate.get(line.slice(separator+1));if(!credential)return[];return[{channelId:manifest.id,agent:render.agent,sourceEnvKey:credential.providerEnvKey,targetEnvKey:line.slice(0,separator),placeholder:credential.placeholder}]})})})}function selectManifests(options){const manifests=options.manifests??BUILT_IN_CHANNEL_MANIFESTS;const agent=options.agent;const selected=agent?manifests.filter(manifest=>manifest.supportedAgents.includes(agent)):manifests;return[...selected]}function authorizeMessagingManagedStartupFields(entry,section){if(section==="agentRender")return authorizeTeamsOpenClawWebhookField(entry);if(!isPlainDataObject3(entry))return[];const contract=WECHAT_OPENCLAW_ACCOUNT_FILE_CONTRACT;if(ownDataPropertyValue3(entry,"channelId")!==contract.channelId||ownDataPropertyValue3(entry,"hookId")!==contract.planHookId||ownDataPropertyValue3(entry,"handler")!==contract.handlerId||ownDataPropertyValue3(entry,"outputId")!==contract.outputId||ownDataPropertyValue3(entry,"kind")!==contract.kind||ownDataPropertyValue3(entry,"required")!==contract.required){return[]}return authorizeWechatAccountFilePlaceholders(ownDataPropertyValue3(entry,"value")).map(authorization=>({...authorization,path:["value",...authorization.path]}))}function isPlainDataObject3(value){if(value===null||typeof value!=="object"||Array.isArray(value))return false;const prototype=Object.getPrototypeOf(value);return prototype===Object.prototype||prototype===null}function ownDataPropertyValue3(value,key){const descriptor=Object.getOwnPropertyDescriptor(value,key);return descriptor&&"value"in descriptor?descriptor.value:void 0}var DCODE_UPSTREAM_PROVIDER_RE=/^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/u;function isValidDcodeUpstreamProvider(value){return DCODE_UPSTREAM_PROVIDER_RE.test(value)}var MANAGED_STARTUP_PROFILE_SCHEMA_VERSION=1;var MANAGED_STARTUP_PROFILE_MAX_BYTES=64*1024;var MANAGED_STARTUP_PROFILE_MAX_ENCODED_BYTES=Math.ceil(MANAGED_STARTUP_PROFILE_MAX_BYTES/3)*4;var MAX_IDENTIFIER_BYTES=256;var MAX_MODEL_BYTES=1024;var MAX_URL_BYTES=2048;var MAX_LIST_ITEMS=128;var MAX_JSON_NODES=4096;var MAX_JSON_DEPTH=32;var MAX_TUNING_INTEGER=1e9;var MIN_HERMES_CONTEXT_WINDOW=64e3;var SHA256_RE=/^[a-f0-9]{64}$/;var CONTROL_CHARACTER_RE=/[\u0000-\u001f\u007f-\u009f]/u;var BASE64URL_RE=/^[A-Za-z0-9_-]+$/;var RAW_CA_PEM_RE=/-----BEGIN (?:TRUSTED )?CERTIFICATE-----/iu;var RAW_CA_PEM_BASE64_RE=/^LS0tLS1CRUdJTi(?:BDRVJUSUZJQ0FURS0tLS0t|BUlVTVEVEIENFUlRJRklDQVRFLS0tLS0)/u;var RAW_CA_DER_BASE64_RE=/^MII[A-Za-z0-9+/=\r\n]{253,}$/u;var RAW_CA_DATA_URI_RE=/data:application\/(?:pkix-cert|x-x509-ca-cert);base64,MII[A-Za-z0-9+/=]{253,}/iu;var URL_CANDIDATE_RE=/[A-Za-z][A-Za-z0-9+.-]*:\/\/[^\s"'<>]+/gu;var UTF8_DECODER=new import_node_util.TextDecoder("utf-8",{fatal:true});var CREDENTIAL_SHAPED_NAME_PATTERN=/(?:^|[_-])(?:api[_-]?key|access[_-]?key|secret[_-]?key|auth[_-]?token|refresh[_-]?token|access[_-]?token|client[_-]?secret|private[_-]?key|pass[_-]?code|personal[_-]?access[_-]?token|connection[_-]?string|webhook(?:[_-]?url)?|key|secret|token|password|passwd|passcode|auth|authorization|credential|credentials|bearer|bearer[_-]?token|cookie|cookies|pat|private|privatekey|pin|webhookurl|dsn|connectionstring)(?:$|[_-])/iu;var CREDENTIAL_COMPOUND_NAME_PATTERN=/^(?:access|refresh|client|bearer|auth|api|private|signing|session|bot|app|resolved)(?:token|key|secret|password)$/iu;var CREDENTIAL_CAMEL_SUFFIX_PATTERN=/(?:apiKey|accessKey|secretKey|authToken|refreshToken|accessToken|clientSecret|privateKey|passcode|password|passwd|passphrase|bearerToken|botToken|appToken|sessionToken|signingKey|secretPublicKey|personalAccessToken|connectionString|webhookUrl)$/iu;var CREDENTIAL_CAMEL_BOUNDARY_PATTERN=/[a-z0-9](?:Token|Key|Secret|Password|Passphrase|Pat)$/u;var CREDENTIAL_ENV_NAME_PATTERN=/^(?:[A-Z0-9]+_)*(?:TOKEN|KEY|SECRET|PASSWORD|PASSWD|PASS|PASSPHRASE|CREDENTIAL)S?$/u;var CREDENTIAL_HEADER_NAME_PATTERN=/^(?:authorization|proxy-authorization|cookie|set-cookie|.+-(?:key|token|secret|password|passphrase|credential|auth)s?)$/iu;var PUBLIC_KEY_NAME_PATTERN=/^public[-_]?keys?$/iu;var PASS_CREDENTIAL_NAME_PATTERN=/(?:^|[-_])pass(?:wd)?$/iu;var NON_SECRET_KEY_METADATA_NAMES=new Set(["envKey","installCacheEnvKey","providerEnvKey","stateKey"]);var MESSAGING_CREDENTIAL_PLACEHOLDER_RE=/^(?:openshell:resolve:env:|[A-Za-z0-9]+-OPENSHELL-RESOLVE-ENV-)(?:v[0-9]+_)?[A-Z][A-Z0-9_]*$/u;var MESSAGING_CREDENTIAL_ENV_ALIASES=new Set(listMessagingCredentialEnvAssignments().filter(({sourceEnvKey,targetEnvKey})=>sourceEnvKey!==targetEnvKey).map(({agent,sourceEnvKey,targetEnvKey})=>`${agent}\0${sourceEnvKey}\0${targetEnvKey}`));var JSON_ARRAY_INDEX_SEGMENT_RE=/^\[(?:0|[1-9][0-9]*)\]$/u;var SECRET_VALUE_PATTERNS=[/nvapi-[A-Za-z0-9_-]{10,}/u,/nvcf-[A-Za-z0-9_-]{10,}/u,/ghp_[A-Za-z0-9_-]{10,}/u,/github_pat_[A-Za-z0-9_]{30,}/u,/sk-(?:proj-|ant-)?[A-Za-z0-9_-]{10,}/u,/(?:xox[bpas]|xapp)-[A-Za-z0-9-]{10,}/u,/A(?:K|S)IA[A-Z0-9]{16}/u,/hf_[A-Za-z0-9]{10,}/u,/glpat-[A-Za-z0-9_-]{10,}/u,/gsk_[A-Za-z0-9]{10,}/u,/pypi-[A-Za-z0-9_-]{10,}/u,/tvly-[A-Za-z0-9_-]{10,}/u,/lsv2_(?:pt|sk)_[A-Za-z0-9]{10,}(?:_[A-Za-z0-9]+)*/u,/\bbot\d{8,10}:[A-Za-z0-9_-]{35}\b/u,/\b\d{8,10}:[A-Za-z0-9_-]{35}\b/u,/\b[A-Za-z0-9]{24}\.[A-Za-z0-9_-]{6}\.[A-Za-z0-9_-]{27,}\b/u,/\beyJ[A-Za-z0-9_-]{5,}\.[A-Za-z0-9_-]{2,}\.[A-Za-z0-9_-]{10,}\b/u,/\bBearer\s+[A-Za-z0-9_.+/=-]{10,}/iu,/-----BEGIN (?:[A-Z0-9]+ )?PRIVATE KEY-----/u];var MANAGED_STARTUP_INFERENCE_APIS=["openai-completions","openai-responses","anthropic-messages"];var MANAGED_STARTUP_REASONING_EFFORTS=["default","low","medium","high"];var MANAGED_STARTUP_DCODE_AUTO_APPROVAL_MODES=["disabled","thread-opt-in"];var MANAGED_STARTUP_HERMES_TOOL_GATEWAYS=["nous-web","nous-image","nous-audio","nous-browser","nous-code"];var MANAGED_STARTUP_AGENTS=["openclaw","hermes","langchain-deepagents-code","pi"];var MANAGED_STARTUP_MESSAGING_AGENTS=["openclaw","hermes"];function freezeAgentCapabilities(capabilities){return Object.freeze({...capabilities,inferenceApis:Object.freeze([...capabilities.inferenceApis]),dashboardModes:Object.freeze([...capabilities.dashboardModes]),inputModalities:Object.freeze([...capabilities.inputModalities]),webSearchProviders:Object.freeze([...capabilities.webSearchProviders]),toolGateways:Object.freeze([...capabilities.toolGateways]),tuningFields:Object.freeze([...capabilities.tuningFields])})}var PROFILE_CAPABILITIES={openclaw:{inferenceApis:[...MANAGED_STARTUP_INFERENCE_APIS],dashboardModes:["loopback","remote"],inputModalities:["text","image"],webSearchProviders:["brave","tavily"],toolGateways:[],tuningFields:["contextWindow","maxTokens","reasoning","reasoningEffort"],supportsMessaging:true,supportsInferenceCompatibility:true,supportsUpstreamEndpoint:false,supportsHostProxyIntent:true,supportsPrimaryModelRef:true,supportsAgentTimeout:true,supportsHeartbeat:true,supportsExtraAgents:true,supportsDeviceAuth:true,observability:"openclaw-otel",supportsMinimalBootstrap:true},hermes:{inferenceApis:[...MANAGED_STARTUP_INFERENCE_APIS],dashboardModes:["disabled","loopback-forwarded"],inputModalities:[],webSearchProviders:["tavily"],toolGateways:[...MANAGED_STARTUP_HERMES_TOOL_GATEWAYS],tuningFields:["contextWindow"],supportsMessaging:true,supportsInferenceCompatibility:false,supportsUpstreamEndpoint:false,supportsHostProxyIntent:true,supportsPrimaryModelRef:false,supportsAgentTimeout:false,supportsHeartbeat:false,supportsExtraAgents:false,supportsDeviceAuth:false,observability:"none",supportsMinimalBootstrap:false},"langchain-deepagents-code":{inferenceApis:["openai-completions"],dashboardModes:["disabled"],inputModalities:[],webSearchProviders:[],toolGateways:[],tuningFields:["reasoningEffort"],supportsMessaging:false,supportsInferenceCompatibility:false,supportsUpstreamEndpoint:true,supportsHostProxyIntent:true,supportsPrimaryModelRef:false,supportsAgentTimeout:false,supportsHeartbeat:false,supportsExtraAgents:false,supportsDeviceAuth:false,observability:"dcode-marker",supportsMinimalBootstrap:false},pi:{inferenceApis:["openai-completions"],dashboardModes:["disabled"],inputModalities:[],webSearchProviders:[],toolGateways:[],tuningFields:["contextWindow","maxTokens","reasoning"],supportsMessaging:false,supportsInferenceCompatibility:false,supportsUpstreamEndpoint:false,supportsHostProxyIntent:true,supportsPrimaryModelRef:false,supportsAgentTimeout:false,supportsHeartbeat:false,supportsExtraAgents:false,supportsDeviceAuth:false,observability:"none",supportsMinimalBootstrap:false}};for(const agent of MANAGED_STARTUP_AGENTS){Object.defineProperty(PROFILE_CAPABILITIES,agent,{configurable:false,enumerable:true,value:freezeAgentCapabilities(PROFILE_CAPABILITIES[agent]),writable:false})}var MANAGED_STARTUP_PROFILE_CAPABILITIES=Object.freeze(PROFILE_CAPABILITIES);function affordance(input,profilePath,source="docker-arg",representation="value"){return{input,profilePath,source,representation}}var HOST_PROXY_AFFORDANCES=[affordance("HTTP_PROXY","proxy.hostHttpUrl","runtime-env"),affordance("http_proxy","proxy.hostHttpUrl","runtime-env","derived"),affordance("HTTPS_PROXY","proxy.hostHttpsUrl","runtime-env"),affordance("https_proxy","proxy.hostHttpsUrl","runtime-env","derived"),affordance("NO_PROXY","proxy.hostNoProxy","runtime-env"),affordance("no_proxy","proxy.hostNoProxy","runtime-env","derived")];var MANAGED_STARTUP_PROFILE_AFFORDANCE_INVENTORY={openclaw:[affordance("NEMOCLAW_MODEL","inference.model"),affordance("NEMOCLAW_INFERENCE_PROVIDER_ID","inference.routeProvider"),affordance("NEMOCLAW_UPSTREAM_PROVIDER","inference.upstreamProvider"),affordance("NEMOCLAW_PRIMARY_MODEL_REF","inference.primaryModelRef"),affordance("NEMOCLAW_INFERENCE_BASE_URL","inference.routedBaseUrl"),affordance("NEMOCLAW_INFERENCE_API","inference.api"),affordance("NEMOCLAW_INFERENCE_COMPAT_B64","inference.compatibility"),affordance("NEMOCLAW_INFERENCE_INPUTS","inference.inputModalities"),affordance("NEMOCLAW_CONTEXT_WINDOW","tuning.contextWindow"),affordance("NEMOCLAW_MAX_TOKENS","tuning.maxTokens"),affordance("NEMOCLAW_REASONING","tuning.reasoning"),affordance("NEMOCLAW_REASONING_EFFORT","tuning.reasoningEffort"),affordance("NEMOCLAW_TOOL_DISCLOSURE","tools.disclosure"),affordance("NEMOCLAW_AGENT_TIMEOUT","agentConfig.agentTimeoutSeconds"),affordance("NEMOCLAW_AGENT_HEARTBEAT_EVERY","agentConfig.heartbeatEvery"),affordance("NEMOCLAW_EXTRA_AGENTS_JSON_B64","agentConfig.extraAgents"),affordance("NEMOCLAW_DISABLE_DEVICE_AUTH","agentConfig.deviceAuth.disabled"),affordance("NEMOCLAW_DEVICE_AUTH_OPT_OUT_SOURCE","agentConfig.deviceAuth.optOutSource"),affordance("NEMOCLAW_WEB_SEARCH_ENABLED","agentConfig.webSearch.enabled"),affordance("NEMOCLAW_WEB_SEARCH_PROVIDER","agentConfig.webSearch.provider"),affordance("NEMOCLAW_OPENCLAW_OTEL","agentConfig.otel.enabled"),affordance("NEMOCLAW_OPENCLAW_OTEL_ENDPOINT","agentConfig.otel.endpointUrl"),affordance("NEMOCLAW_OPENCLAW_OTEL_SERVICE_NAME","agentConfig.otel.serviceName"),affordance("NEMOCLAW_OPENCLAW_OTEL_SAMPLE_RATE","agentConfig.otel.sampleRate"),affordance("CHAT_UI_URL","dashboard.url"),affordance("NEMOCLAW_DASHBOARD_BIND","dashboard.bindAddress"),affordance("NEMOCLAW_WSL_DASHBOARD_EXPOSURE","dashboard.wslExposure"),affordance("NEMOCLAW_DASHBOARD_PORT","dashboard.port","runtime-env"),affordance("NEMOCLAW_PROXY_HOST","proxy.managedHost"),affordance("NEMOCLAW_PROXY_PORT","proxy.managedPort"),affordance("NEMOCLAW_MESSAGING_PLAN_B64","messaging.plan"),affordance("NEMOCLAW_MINIMAL_BOOTSTRAP","agentConfig.minimalBootstrap","runtime-env"),affordance("NEMOCLAW_CORPORATE_CA_B64","corporateCa.bundleSha256","host-material","digest-handoff"),...HOST_PROXY_AFFORDANCES],hermes:[affordance("NEMOCLAW_MODEL","inference.model"),affordance("NEMOCLAW_INFERENCE_PROVIDER_ID","inference.routeProvider"),affordance("NEMOCLAW_UPSTREAM_PROVIDER","inference.upstreamProvider"),affordance("NEMOCLAW_INFERENCE_BASE_URL","inference.routedBaseUrl"),affordance("NEMOCLAW_INFERENCE_API","inference.api"),affordance("NEMOCLAW_CONTEXT_WINDOW","tuning.contextWindow"),affordance("NEMOCLAW_TOOL_DISCLOSURE","tools.disclosure"),affordance("NEMOCLAW_HERMES_TOOL_GATEWAY_BROKER","tools.enabledGateways","docker-arg","derived"),affordance("NEMOCLAW_HERMES_TOOL_GATEWAY_PRESETS_B64","tools.enabledGateways"),affordance("NEMOCLAW_WEB_SEARCH_ENABLED","agentConfig.webSearch.enabled"),affordance("NEMOCLAW_WEB_SEARCH_PROVIDER","agentConfig.webSearch.provider"),affordance("NEMOCLAW_MESSAGING_PLAN_B64","messaging.plan"),affordance("CHAT_UI_URL","dashboard.url"),affordance("NEMOCLAW_DASHBOARD_PORT","dashboard.publicPort","runtime-env"),affordance("NEMOCLAW_HERMES_DASHBOARD","dashboard.mode","runtime-env"),affordance("NEMOCLAW_HERMES_DASHBOARD_PORT","dashboard.publicPort","runtime-env"),affordance("NEMOCLAW_HERMES_DASHBOARD_INTERNAL_PORT","dashboard.internalPort","runtime-env"),affordance("NEMOCLAW_HERMES_DASHBOARD_TUI","dashboard.tuiEnabled","runtime-env"),affordance("NEMOCLAW_PROXY_HOST","proxy.managedHost","runtime-env"),affordance("NEMOCLAW_PROXY_PORT","proxy.managedPort","runtime-env"),affordance("NEMOCLAW_CORPORATE_CA_B64","corporateCa.bundleSha256","host-material","digest-handoff"),...HOST_PROXY_AFFORDANCES],"langchain-deepagents-code":[affordance("NEMOCLAW_MODEL","inference.model"),affordance("NEMOCLAW_INFERENCE_PROVIDER_ID","inference.routeProvider"),affordance("NEMOCLAW_UPSTREAM_PROVIDER","inference.upstreamProvider"),affordance("NEMOCLAW_UPSTREAM_ENDPOINT_URL","inference.upstreamEndpointUrl"),affordance("NEMOCLAW_INFERENCE_BASE_URL","inference.routedBaseUrl"),affordance("NEMOCLAW_INFERENCE_API","inference.api"),affordance("NEMOCLAW_REASONING_EFFORT","tuning.reasoningEffort"),affordance("NEMOCLAW_TOOL_DISCLOSURE","tools.disclosure"),affordance("NEMOCLAW_DCODE_AUTO_APPROVAL","agentConfig.autoApprovalMode"),affordance("NEMOCLAW_PROXY_HOST","proxy.managedHost"),affordance("NEMOCLAW_PROXY_PORT","proxy.managedPort"),affordance("NEMOCLAW_OBSERVABILITY","agentConfig.observabilityEnabled","runtime-env"),affordance("NEMOCLAW_CORPORATE_CA_B64","corporateCa.bundleSha256","host-material","digest-handoff"),...HOST_PROXY_AFFORDANCES],pi:[affordance("NEMOCLAW_MODEL","inference.model"),affordance("NEMOCLAW_INFERENCE_PROVIDER_ID","inference.routeProvider"),affordance("NEMOCLAW_UPSTREAM_PROVIDER","inference.upstreamProvider"),affordance("NEMOCLAW_INFERENCE_BASE_URL","inference.routedBaseUrl"),affordance("NEMOCLAW_INFERENCE_API","inference.api"),affordance("NEMOCLAW_CONTEXT_WINDOW","tuning.contextWindow"),affordance("NEMOCLAW_MAX_TOKENS","tuning.maxTokens"),affordance("NEMOCLAW_REASONING","tuning.reasoning"),affordance("NEMOCLAW_TOOL_DISCLOSURE","tools.disclosure"),affordance("NEMOCLAW_PROXY_HOST","proxy.managedHost"),affordance("NEMOCLAW_PROXY_PORT","proxy.managedPort"),affordance("NEMOCLAW_CORPORATE_CA_B64","corporateCa.bundleSha256","host-material","digest-handoff"),...HOST_PROXY_AFFORDANCES]};function deferredRuntimeInput(input,owner,reason,admission="managed-launch-forwarded"){return Object.freeze({input,owner,admission,reason})}var MANAGED_STARTUP_PROFILE_DEFERRED_RUNTIME_INPUTS=Object.freeze({openclaw:Object.freeze([deferredRuntimeInput("NEMOCLAW_AUTO_PAIR_DEADLINE_SECS","application-environment","operator scheduler tuning is applied by the application environment transaction"),deferredRuntimeInput("NEMOCLAW_AUTO_PAIR_FAST_DEADLINE_SECS","application-environment","operator scheduler tuning is applied by the application environment transaction"),deferredRuntimeInput("NEMOCLAW_AUTO_PAIR_FAST_REENTRY_INTERVAL_SECS","application-environment","operator scheduler tuning is applied by the application environment transaction"),deferredRuntimeInput("NEMOCLAW_AUTO_PAIR_FAST_REENTRY_POLLS","application-environment","operator scheduler tuning is applied by the application environment transaction"),deferredRuntimeInput("NEMOCLAW_AUTO_PAIR_RUN_TIMEOUT_SECS","application-environment","operator scheduler tuning is applied by the application environment transaction"),deferredRuntimeInput("NEMOCLAW_AUTO_PAIR_SLOW_INTERVAL_SECS","application-environment","operator scheduler tuning is applied by the application environment transaction"),deferredRuntimeInput("NEMOCLAW_MCP_SHADOW_DIAGNOSTICS","application-environment","operator shadow-diagnostics tuning is applied by the application environment transaction"),deferredRuntimeInput("NEMOCLAW_MCP_TOOLS_LIST_TIMEOUT_MS","application-environment","operator MCP discovery timeout tuning is applied by the application environment transaction"),deferredRuntimeInput("OPENCLAW_HOME","fixed-image-contract","the managed image and agent definition own this fixed runtime layout path"),deferredRuntimeInput("OPENCLAW_STATE_DIR","fixed-image-contract","the managed image and agent definition own this fixed runtime layout path"),deferredRuntimeInput("OPENCLAW_WORKSPACE_DIR","fixed-image-contract","the managed image and agent definition own this fixed runtime layout path"),deferredRuntimeInput("NEMOCLAW_EXTRA_PLACEHOLDER_KEYS","credential-plumbing","credential provider construction owns key metadata outside the secret-free profile")]),hermes:Object.freeze([deferredRuntimeInput("NEMOCLAW_EXTRA_PLACEHOLDER_KEYS","credential-plumbing","credential provider construction owns key metadata outside the secret-free profile")]),"langchain-deepagents-code":Object.freeze([deferredRuntimeInput("NEMOCLAW_SANDBOX_NAME","engine-identity","the lifecycle engine owns instance identity outside reusable startup intent"),deferredRuntimeInput("NEMOCLAW_EXTRA_PLACEHOLDER_KEYS","credential-plumbing","credential provider construction owns key metadata outside the secret-free profile")]),pi:Object.freeze([deferredRuntimeInput("NEMOCLAW_EXTRA_PLACEHOLDER_KEYS","credential-plumbing","credential provider construction owns key metadata outside the secret-free profile")])});function runtimeCleanupObligation(input,emittedFor,supportedFor,reason){return Object.freeze({input,emittedFor:Object.freeze([...emittedFor]),supportedFor:Object.freeze([...supportedFor]),owner:"application-environment",reason})}var MANAGED_STARTUP_RUNTIME_CLEANUP_OBLIGATIONS=Object.freeze([runtimeCleanupObligation("NEMOCLAW_DASHBOARD_BIND",["hermes"],["openclaw"],"generic managed-dashboard construction currently emits the OpenClaw-only bind control for Hermes"),runtimeCleanupObligation("NEMOCLAW_MINIMAL_BOOTSTRAP",["hermes","langchain-deepagents-code"],["openclaw"],"generic host-proxy construction currently emits the OpenClaw-only bootstrap control for other agents")]);var ManagedStartupProfileError=class extends Error{constructor(message){super(`Invalid managed startup profile: ${message}`);this.name="ManagedStartupProfileError"}};var PROFILE_KEYS=new Set(["schemaVersion","agent","agentConfig","inference","proxy","dashboard","tools","messaging","tuning","corporateCa"]);var INFERENCE_KEYS=new Set(["routeProvider","upstreamProvider","model","routedBaseUrl","upstreamEndpointUrl","api","primaryModelRef","compatibility","inputModalities"]);var PROXY_KEYS=new Set(["managedHost","managedPort","hostHttpUrl","hostHttpsUrl","hostNoProxy"]);var OPENCLAW_DASHBOARD_KEYS=new Set(["agent","mode","url","port","bindAddress","wslExposure"]);var HERMES_DASHBOARD_KEYS=new Set(["agent","mode","url","publicPort","internalPort","tuiEnabled"]);var DCODE_DASHBOARD_KEYS=new Set(["agent","mode"]);var TOOLS_KEYS=new Set(["disclosure","enabledGateways"]);var MESSAGING_KEYS=new Set(["plan"]);var TUNING_FIELD_ORDER=["contextWindow","maxTokens","reasoning","reasoningEffort"];var TUNING_KEYS=new Set(TUNING_FIELD_ORDER);var CORPORATE_CA_KEYS=new Set(["bundleSha256"]);var OPENCLAW_CONFIG_KEYS=new Set(["agent","webSearch","otel","agentTimeoutSeconds","heartbeatEvery","extraAgents","deviceAuth","minimalBootstrap"]);var HERMES_CONFIG_KEYS=new Set(["agent","webSearch"]);var DCODE_CONFIG_KEYS=new Set(["agent","autoApprovalMode","observabilityEnabled"]);var PI_CONFIG_KEYS=new Set(["agent"]);var PI_DASHBOARD_KEYS=new Set(["agent","mode"]);var WEB_SEARCH_KEYS=new Set(["enabled","provider"]);var OTEL_KEYS=new Set(["enabled","endpointUrl","serviceName","sampleRate"]);var DEVICE_AUTH_KEYS=new Set(["disabled","optOutSource"]);var EXTRA_AGENTS_KEYS=new Set(["agents","defaults","main"]);var MANAGED_STARTUP_AGENT_SET=new Set(MANAGED_STARTUP_AGENTS);var DCODE_AUTO_APPROVAL_MODE_SET=new Set(MANAGED_STARTUP_DCODE_AUTO_APPROVAL_MODES);var REASONING_EFFORT_SET=new Set(MANAGED_STARTUP_REASONING_EFFORTS);var HERMES_INTERNAL_API_PORT=18642;var HERMES_API_PORT_RANGE_START=8642;var HERMES_API_PORT_RANGE_END=8652;function isHermesApiPort(port){return port>=HERMES_API_PORT_RANGE_START&&port<=HERMES_API_PORT_RANGE_END}function isHermesReservedApiPort(port){return port===HERMES_INTERNAL_API_PORT||isHermesApiPort(port)}var HERMES_RESERVED_API_PORT_LABEL=`${HERMES_API_PORT_RANGE_START}-${HERMES_API_PORT_RANGE_END} or ${HERMES_INTERNAL_API_PORT}`;function isPlainObject(value){if(typeof value!=="object"||value===null||Array.isArray(value))return false;const prototype=Object.getPrototypeOf(value);return prototype===Object.prototype||prototype===null}function isCredentialShapedName(name){if(PUBLIC_KEY_NAME_PATTERN.test(name)||NON_SECRET_KEY_METADATA_NAMES.has(name))return false;return CREDENTIAL_SHAPED_NAME_PATTERN.test(name)||CREDENTIAL_COMPOUND_NAME_PATTERN.test(name)||CREDENTIAL_CAMEL_SUFFIX_PATTERN.test(name)||CREDENTIAL_CAMEL_BOUNDARY_PATTERN.test(name)||CREDENTIAL_ENV_NAME_PATTERN.test(name)||CREDENTIAL_HEADER_NAME_PATTERN.test(name)||PASS_CREDENTIAL_NAME_PATTERN.test(name)}function valueLooksLikeSecret(value){for(let index=0;index=5&&path5[0]==="messaging"&&path5[1]==="plan"&&path5[2]==="agentRender"&&JSON_ARRAY_INDEX_SEGMENT_RE.test(path5[3]??"")&&path5[4]==="value";const isAuthorizedBuildStepPlaceholder=allowedBuildStepPlaceholders.has(buildStepPlaceholderKey(path5,value));return isCredentialBindingPlaceholder||isAgentRenderValuePlaceholder||isAuthorizedBuildStepPlaceholder}function requiresMessagingSchemaFieldAuthorization(path5){const fieldName=path5[path5.length-1];return fieldName==="webhook"}function messagingAuthorizedFieldKey(path5){return JSON.stringify(path5)}function buildStepPlaceholderKey(path5,value){return JSON.stringify([path5,value])}function messagingCredentialPlaceholderEnvKey(value){if(!MESSAGING_CREDENTIAL_PLACEHOLDER_RE.test(value))return null;const marker=value.startsWith("openshell:resolve:env:")?"openshell:resolve:env:":"-OPENSHELL-RESOLVE-ENV-";const key=value.slice(value.indexOf(marker)+marker.length);return key.replace(/^v[0-9]+_/u,"")}function containsMessagingCredentialPlaceholder(value){return value.includes("openshell:resolve:env:")||value.includes("-OPENSHELL-RESOLVE-ENV-")}function isMessagingCredentialPlaceholderAssignment(selectedAgent,path5,value){if(path5.length!==6||path5[0]!=="messaging"||path5[1]!=="plan"||path5[2]!=="agentRender"||!JSON_ARRAY_INDEX_SEGMENT_RE.test(path5[3]??"")||path5[4]!=="lines"||!JSON_ARRAY_INDEX_SEGMENT_RE.test(path5[5]??"")){return false}const separator=value.indexOf("=");if(separator<=0||value.indexOf("=",separator+1)!==-1)return false;const envKey=value.slice(0,separator);const placeholder=value.slice(separator+1);const placeholderEnvKey=messagingCredentialPlaceholderEnvKey(placeholder);return CREDENTIAL_ENV_NAME_PATTERN.test(envKey)&&placeholderEnvKey!==null&&(envKey===placeholderEnvKey||typeof selectedAgent==="string"&&MESSAGING_CREDENTIAL_ENV_ALIASES.has(`${selectedAgent}\0${placeholderEnvKey}\0${envKey}`))}function isMessagingRuntimeEnvAliasPath(path5){return path5.length===5&&path5[0]==="messaging"&&path5[1]==="plan"&&path5[2]==="runtimeSetup"&&path5[3]==="envAliases"&&JSON_ARRAY_INDEX_SEGMENT_RE.test(path5[4]??"")}function ownDataPropertyValue4(value,key){const descriptor=Object.getOwnPropertyDescriptor(value,key);return descriptor&&"value"in descriptor?descriptor.value:void 0}function isStockTeamsOpenClawWebhook(root,path5,value){if(path5.length!==6||path5[0]!=="messaging"||path5[1]!=="plan"||path5[2]!=="agentRender"||!JSON_ARRAY_INDEX_SEGMENT_RE.test(path5[3]??"")||path5[4]!=="value"||path5[5]!=="webhook"||!isPlainObject(root)||ownDataPropertyValue4(root,"agent")!=="openclaw"){return false}const messaging=ownDataPropertyValue4(root,"messaging");if(!isPlainObject(messaging))return false;const plan=ownDataPropertyValue4(messaging,"plan");if(!isPlainObject(plan)||ownDataPropertyValue4(plan,"agent")!=="openclaw")return false;const agentRender=ownDataPropertyValue4(plan,"agentRender");if(!Array.isArray(agentRender))return false;const entryIndex=path5[3].slice(1,-1);const entryDescriptor=Object.getOwnPropertyDescriptor(agentRender,entryIndex);const entry=entryDescriptor&&"value"in entryDescriptor?entryDescriptor.value:void 0;if(!isPlainObject(entry))return false;const renderValue=ownDataPropertyValue4(entry,"value");if(!isPlainObject(renderValue)||ownDataPropertyValue4(renderValue,"webhook")!==value){return false}if(ownDataPropertyValue4(entry,"channelId")!=="teams"||ownDataPropertyValue4(entry,"renderId")!=="teams-openclaw-channel"||ownDataPropertyValue4(entry,"hookId")!=="teams-openclaw-channel"||ownDataPropertyValue4(entry,"handler")!=="common.staticOutputs"||ownDataPropertyValue4(entry,"kind")!=="json-fragment"||ownDataPropertyValue4(entry,"agent")!=="openclaw"||ownDataPropertyValue4(entry,"target")!=="openclaw.json"||ownDataPropertyValue4(entry,"path")!=="channels.msteams"||!isPlainObject(value)){return false}const keys=Object.getOwnPropertyNames(value);if(keys.length!==2||!keys.includes("port")||!keys.includes("path"))return false;const port=ownDataPropertyValue4(value,"port");return typeof port==="number"&&Number.isInteger(port)&&port>=1&&port<=65535&&ownDataPropertyValue4(value,"path")==="/api/messages"}function isCanonicalMessagingRuntimeEnvAlias(path5,value){if(!isMessagingRuntimeEnvAliasPath(path5))return false;const envKey=ownDataPropertyValue4(value,"envKey");const match=ownDataPropertyValue4(value,"match");const placeholder=ownDataPropertyValue4(value,"value");return typeof envKey==="string"&&CREDENTIAL_ENV_NAME_PATTERN.test(envKey)&&match===`^openshell:resolve:env:(v[0-9]+_)?${envKey}$`&&typeof placeholder==="string"&&messagingCredentialPlaceholderEnvKey(placeholder)===envKey}function isAllowedMessagingRuntimeAliasStringPath(path5,allowedAliasIndexes){return path5.length===6&&path5[0]==="messaging"&&path5[1]==="plan"&&path5[2]==="runtimeSetup"&&path5[3]==="envAliases"&&allowedAliasIndexes.has(path5[4]??"")&&(path5[5]==="match"||path5[5]==="value")}function isMessagingPackagePin(path5,value){return path5.length===6&&path5[0]==="messaging"&&path5[1]==="plan"&&path5[2]==="buildSteps"&&JSON_ARRAY_INDEX_SEGMENT_RE.test(path5[3]??"")&&path5[4]==="value"&&path5[5]==="pin"&&typeof value==="boolean"}function containsUrlWithCredentialMaterial(value){const candidates=value.match(URL_CANDIDATE_RE)??[];for(let index=0;index{if(isCredentialShapedName(key))credentialQuery=true});const fragment=url.hash.startsWith("#")?url.hash.slice(1):url.hash;const queryStart=fragment.indexOf("?");const fragmentParameters=new URLSearchParams(queryStart>=0?fragment.slice(queryStart+1):fragment);let credentialFragment=false;fragmentParameters.forEach((_fragmentValue,key)=>{if(isCredentialShapedName(key))credentialFragment=true});if(url.username||url.password||credentialQuery||credentialFragment)return true}catch{}}return false}function invalid(reason){throw new ManagedStartupProfileError(reason)}function payloadPath(path5){return path5.reduce((result,segment)=>segment.startsWith("[")?`${result}${segment}`:`${result}${result?".":""}${segment}`,"")}function mapArrayByIndex(values,mapper){const mapped=[];for(let index=0;index0&&values[insertion-1]>selected){Object.defineProperty(values,String(insertion),{configurable:true,enumerable:true,value:values[insertion-1],writable:true});insertion-=1}Object.defineProperty(values,String(insertion),{configurable:true,enumerable:true,value:selected,writable:true})}return values}function requireRecord(value,where){if(!isPlainObject(value))invalid(`${where} must be an object`);return value}function rejectUnknownKeys(value,allowed,where){const keys=Object.keys(value);for(let index=0;indexmaxBytes||CONTROL_CHARACTER_RE.test(value)){invalid(`${where} must be a bounded, non-empty string without control characters`)}return value}function requireStringEnum(value,allowed,where){const normalized=requireBoundedString(value,where);if(!allowed.has(normalized))invalid(`${where} is not supported`);return normalized}function requireNullablePositiveInteger(value,where){if(value===null)return null;if(typeof value!=="number"||!Number.isSafeInteger(value)||value<1||value>MAX_TUNING_INTEGER){invalid(`${where} must be null or a bounded positive integer`)}return value}function requirePositiveInteger(value,where,maximum=MAX_TUNING_INTEGER){if(typeof value!=="number"||!Number.isSafeInteger(value)||value<1||value>maximum){invalid(`${where} must be a bounded positive integer`)}return value}function requirePort(value,where,minimum=1){if(typeof value!=="number"||!Number.isInteger(value)||value<1||value>65535){invalid(`${where} must be a valid TCP port`)}if(valueMAX_LIST_ITEMS){invalid(`${where} must be a bounded string list`)}const items=mapArrayByIndex(value,item=>requireBoundedString(item,`${where} item`));const unique2=new Set;for(let index=0;index{if(depth>MAX_JSON_DEPTH)invalid(`${where} exceeds the JSON depth limit`);if(current===null||typeof current==="string"||typeof current==="boolean"){return current}if(typeof current==="number"){if(!Number.isFinite(current))invalid(`${where} contains a non-finite number`);return current}if(Array.isArray(current)){return mapArrayByIndex(current,item=>clone(item,depth+1))}if(!isPlainObject(current))invalid(`${where} contains a non-JSON value`);const result=options.nullPrototypeObjects?Object.create(null):{};const keys=Object.getOwnPropertyNames(current);for(let index=0;indexMAX_IDENTIFIER_BYTES||CONTROL_CHARACTER_RE.test(key)){invalid(`${where} contains an invalid object key`)}const descriptor=Object.getOwnPropertyDescriptor(current,key);if(!descriptor||!("value"in descriptor)){invalid(`${where} contains a non-JSON value`)}Object.defineProperty(result,key,{configurable:true,enumerable:true,value:clone(descriptor.value,depth+1),writable:true})}return result};return clone(value,0)}function requireJsonObjectOrNull(value,where){if(value===null)return null;if(!isPlainObject(value))invalid(`${where} must be null or a plain JSON object`);return cloneJsonValue(value,where,{nullPrototypeObjects:true})}function requireJsonObject(value,where){const object=requireJsonObjectOrNull(value,where);if(object===null)invalid(`${where} must be a plain JSON object`);return object}function requireHttpUrl(value,where){const raw=requireBoundedString(value,where,MAX_URL_BYTES);let parsed;try{parsed=new URL(raw)}catch{invalid(`${where} must be a valid HTTP(S) URL`)}if(parsed.protocol!=="http:"&&parsed.protocol!=="https:"||parsed.username||parsed.password||parsed.search||parsed.hash){invalid(`${where} must be a credential-free HTTP(S) URL without query or fragment data`)}const pathname=parsed.pathname.replace(/\/+$/u,"");return pathname===""?parsed.origin:`${parsed.origin}${pathname}`}function requireProxyUrl(value,allowedSchemes,where){if(value===null)return null;const raw=requireBoundedString(value,where,MAX_URL_BYTES);let parsed;try{parsed=new URL(raw)}catch{invalid(`${where} must be a valid HTTP(S) proxy URL`)}if(!allowedSchemes.has(parsed.protocol)||parsed.username||parsed.password||parsed.pathname!=="/"||parsed.search||parsed.hash){invalid(`${where} must be a credential-free HTTP(S) proxy origin`)}return parsed.origin}function requireManagedProxyHost(value,where){const host=requireBoundedString(value,where);if(!/^[A-Za-z0-9._-]+$/u.test(host)){invalid(`${where} must be a hostname or IPv4 address without a scheme or separators`)}return host}function isLoopbackUrl(value){const hostname=new URL(value).hostname.toLowerCase();return hostname==="localhost"||hostname==="127.0.0.1"||hostname==="::1"||hostname==="[::1]"}function configuredDashboardPort(value){const explicit=new URL(value).port;return explicit===""?18789:Number(explicit)}function requireSampleRate(value,where){if(typeof value!=="number"||!Number.isFinite(value)||value<0||value>1){invalid(`${where} must be a number between 0 and 1`)}return value}function assertPayloadStructureAndCredentialShapes(root){const pending=[{value:root,depth:0,path:[]}];const allowedRuntimeAliasIndexes=new Set;const allowedMessagingCredentialFields=new Set;const allowedBuildStepPlaceholders=new Set;const selectedAgent=isPlainObject(root)?ownDataPropertyValue4(root,"agent"):void 0;let discoveredNodes=1;let observedBytes=0;const observeText=value=>{observedBytes+=import_node_buffer.Buffer.byteLength(value,"utf8");if(observedBytes>MANAGED_STARTUP_PROFILE_MAX_BYTES){invalid(`payload exceeds ${String(MANAGED_STARTUP_PROFILE_MAX_BYTES)} bytes`)}};const reserveNode=depth=>{discoveredNodes+=1;if(discoveredNodes>MAX_JSON_NODES||depth>MAX_JSON_DEPTH){invalid("payload structure exceeds the complexity limit")}observedBytes+=1};while(pending.length>0){const current=pending.pop();if(!current)break;if(current.depth>MAX_JSON_DEPTH){invalid("payload structure exceeds the complexity limit")}if(typeof current.value==="string"){observeText(current.value);if(!isAllowedMessagingRuntimeAliasStringPath(current.path,allowedRuntimeAliasIndexes)&&!isMessagingCredentialPlaceholder(current.path,current.value,allowedBuildStepPlaceholders,allowedMessagingCredentialFields)&&!isMessagingCredentialPlaceholderAssignment(selectedAgent,current.path,current.value)&&(valueLooksLikeSecret(current.value)||containsMessagingCredentialPlaceholder(current.value))){invalid(`payload field ${payloadPath(current.path)} contains credential-shaped string data`)}if(RAW_CA_PEM_RE.test(current.value)||RAW_CA_PEM_BASE64_RE.test(current.value)||RAW_CA_DER_BASE64_RE.test(current.value)||RAW_CA_DATA_URI_RE.test(current.value)){invalid(`payload field ${payloadPath(current.path)} contains raw certificate data; provide only the CA SHA-256 digest`)}if(containsUrlWithCredentialMaterial(current.value)){invalid(`payload field ${payloadPath(current.path)} contains a URL with embedded credentials`)}continue}if(Array.isArray(current.value)){if(Object.getPrototypeOf(current.value)!==Array.prototype){invalid("payload arrays must use the standard JSON prototype")}if("toJSON"in current.value){invalid("payload must not define a custom JSON serializer")}if(Object.getOwnPropertySymbols(current.value).length>0||Object.getOwnPropertyNames(current.value).length!==current.value.length+1){invalid("payload arrays must contain only indexed JSON values")}for(let index=0;index0||discoveredNodes+keys.length>MAX_JSON_NODES){invalid("payload structure exceeds the complexity limit")}for(let index=0;indexMANAGED_STARTUP_PROFILE_MAX_BYTES){invalid(`payload exceeds ${String(MANAGED_STARTUP_PROFILE_MAX_BYTES)} bytes`)}}function validateWebSearch(value,agent){const webSearch=requireRecord(value,"agentConfig.webSearch");rejectUnknownKeys(webSearch,WEB_SEARCH_KEYS,"agentConfig.webSearch");const provider=requireStringEnum(webSearch.provider,new Set(MANAGED_STARTUP_PROFILE_CAPABILITIES[agent].webSearchProviders),"agentConfig.webSearch.provider");return{enabled:requireBoolean(webSearch.enabled,"agentConfig.webSearch.enabled"),provider}}function validateOpenClawOtel(value){const otel=requireRecord(value,"agentConfig.otel");rejectUnknownKeys(otel,OTEL_KEYS,"agentConfig.otel");return{enabled:requireBoolean(otel.enabled,"agentConfig.otel.enabled"),endpointUrl:requireHttpUrl(otel.endpointUrl,"agentConfig.otel.endpointUrl"),serviceName:requireBoundedString(otel.serviceName,"agentConfig.otel.serviceName",MAX_IDENTIFIER_BYTES),sampleRate:requireSampleRate(otel.sampleRate,"agentConfig.otel.sampleRate")}}function validateExtraAgents(value){const extraAgents=requireRecord(value,"agentConfig.extraAgents");rejectUnknownKeys(extraAgents,EXTRA_AGENTS_KEYS,"agentConfig.extraAgents");if(!Array.isArray(extraAgents.agents)||extraAgents.agents.length>MAX_LIST_ITEMS){invalid("agentConfig.extraAgents.agents must be a bounded JSON object list")}return{agents:mapArrayByIndex(extraAgents.agents,(agent,index)=>requireJsonObject(agent,`agentConfig.extraAgents.agents[${String(index)}]`)),defaults:requireJsonObject(extraAgents.defaults,"agentConfig.extraAgents.defaults"),main:requireJsonObject(extraAgents.main,"agentConfig.extraAgents.main")}}function validateDeviceAuth(value){const deviceAuth=requireRecord(value,"agentConfig.deviceAuth");rejectUnknownKeys(deviceAuth,DEVICE_AUTH_KEYS,"agentConfig.deviceAuth");return{disabled:requireBoolean(deviceAuth.disabled,"agentConfig.deviceAuth.disabled"),optOutSource:requireStringEnum(deviceAuth.optOutSource,new Set(["operator","managed-onboard"]),"agentConfig.deviceAuth.optOutSource")}}function validateAgentConfig(value,expectedAgent){const config=requireRecord(value,"agentConfig");const agent=requireStringEnum(config.agent,MANAGED_STARTUP_AGENT_SET,"agentConfig.agent");if(agent!==expectedAgent)invalid("agentConfig.agent must match agent");if(agent==="openclaw"){rejectUnknownKeys(config,OPENCLAW_CONFIG_KEYS,"agentConfig");const heartbeatEvery=config.heartbeatEvery===null?null:requireBoundedString(config.heartbeatEvery,"agentConfig.heartbeatEvery",MAX_IDENTIFIER_BYTES);if(heartbeatEvery!==null&&!/^\d+(?:s|m|h)$/u.test(heartbeatEvery)){invalid("agentConfig.heartbeatEvery must be null or a duration ending in s, m, or h")}return{agent,webSearch:validateWebSearch(config.webSearch,agent),otel:validateOpenClawOtel(config.otel),agentTimeoutSeconds:requirePositiveInteger(config.agentTimeoutSeconds,"agentConfig.agentTimeoutSeconds"),heartbeatEvery,extraAgents:validateExtraAgents(config.extraAgents),deviceAuth:validateDeviceAuth(config.deviceAuth),minimalBootstrap:requireBoolean(config.minimalBootstrap,"agentConfig.minimalBootstrap")}}if(agent==="hermes"){rejectUnknownKeys(config,HERMES_CONFIG_KEYS,"agentConfig");return{agent,webSearch:validateWebSearch(config.webSearch,agent)}}if(agent==="pi"){rejectUnknownKeys(config,PI_CONFIG_KEYS,"agentConfig");return{agent}}rejectUnknownKeys(config,DCODE_CONFIG_KEYS,"agentConfig");return{agent,autoApprovalMode:requireStringEnum(config.autoApprovalMode,DCODE_AUTO_APPROVAL_MODE_SET,"agentConfig.autoApprovalMode"),observabilityEnabled:requireBoolean(config.observabilityEnabled,"agentConfig.observabilityEnabled")}}function validateDashboard(value,expectedAgent){const dashboard=requireRecord(value,"dashboard");const agent=requireStringEnum(dashboard.agent,MANAGED_STARTUP_AGENT_SET,"dashboard.agent");if(agent!==expectedAgent)invalid("dashboard.agent must match agent");if(agent==="openclaw"){rejectUnknownKeys(dashboard,OPENCLAW_DASHBOARD_KEYS,"dashboard");const mode=requireStringEnum(dashboard.mode,new Set(MANAGED_STARTUP_PROFILE_CAPABILITIES[agent].dashboardModes),"dashboard.mode");const url=requireHttpUrl(dashboard.url,"dashboard.url");const bindAddress=requireStringEnum(dashboard.bindAddress,new Set(["127.0.0.1","0.0.0.0"]),"dashboard.bindAddress");const wslExposure=requireBoolean(dashboard.wslExposure,"dashboard.wslExposure");const hasRemoteExposure=!isLoopbackUrl(url)||bindAddress==="0.0.0.0"||wslExposure;if(mode==="remote"!==hasRemoteExposure){invalid("OpenClaw dashboard.mode must reflect its URL, bind address, and WSL exposure")}const port=requirePort(dashboard.port,"dashboard.port",1024);if(isHermesApiPort(port))invalid(`OpenClaw dashboard.port must not use a reserved Hermes API port (${HERMES_API_PORT_RANGE_START}-${HERMES_API_PORT_RANGE_END})`);if(configuredDashboardPort(url)!==port){invalid("OpenClaw dashboard.port must match dashboard.url")}return{agent,mode,url,port,bindAddress,wslExposure}}if(agent==="hermes"){rejectUnknownKeys(dashboard,HERMES_DASHBOARD_KEYS,"dashboard");const mode=requireStringEnum(dashboard.mode,new Set(MANAGED_STARTUP_PROFILE_CAPABILITIES[agent].dashboardModes),"dashboard.mode");const url=requireHttpUrl(dashboard.url,"dashboard.url");if(!isLoopbackUrl(url)){invalid("Hermes dashboard.url must remain loopback; OpenShell owns the host forward")}if(mode==="disabled"){if(dashboard.publicPort!==null||dashboard.internalPort!==null||dashboard.tuiEnabled!==false){invalid("disabled Hermes dashboard must not configure ports or TUI")}return{agent,mode,url,publicPort:null,internalPort:null,tuiEnabled:false}}const publicPort=requirePort(dashboard.publicPort,"dashboard.publicPort",1024);const internalPort=requirePort(dashboard.internalPort,"dashboard.internalPort",1024);if(publicPort===internalPort){invalid("Hermes dashboard publicPort and internalPort must differ")}if(isHermesReservedApiPort(publicPort)||isHermesReservedApiPort(internalPort)){invalid(`Hermes dashboard ports must not use reserved API ports ${HERMES_RESERVED_API_PORT_LABEL}`)}if(configuredDashboardPort(url)!==publicPort){invalid("Hermes dashboard.publicPort must match dashboard.url")}return{agent,mode,url,publicPort,internalPort,tuiEnabled:requireBoolean(dashboard.tuiEnabled,"dashboard.tuiEnabled")}}if(agent==="pi"){rejectUnknownKeys(dashboard,PI_DASHBOARD_KEYS,"dashboard");if(dashboard.mode!=="disabled")invalid("pi dashboard.mode must be disabled");return{agent,mode:"disabled"}}rejectUnknownKeys(dashboard,DCODE_DASHBOARD_KEYS,"dashboard");if(dashboard.mode!=="disabled"){invalid("langchain-deepagents-code dashboard.mode must be disabled")}return{agent,mode:"disabled"}}function validateInference(value,agent){const inference=requireRecord(value,"inference");rejectUnknownKeys(inference,INFERENCE_KEYS,"inference");const routeProvider=requireBoundedString(inference.routeProvider,"inference.routeProvider");const upstreamProvider=requireBoundedString(inference.upstreamProvider,"inference.upstreamProvider");const model=requireBoundedString(inference.model,"inference.model",MAX_MODEL_BYTES);const api=requireStringEnum(inference.api,new Set(MANAGED_STARTUP_PROFILE_CAPABILITIES[agent].inferenceApis),"inference.api");const upstreamEndpointUrl=inference.upstreamEndpointUrl===null?null:requireHttpUrl(inference.upstreamEndpointUrl,"inference.upstreamEndpointUrl");const primaryModelRef=inference.primaryModelRef===null?null:requireBoundedString(inference.primaryModelRef,"inference.primaryModelRef",MAX_MODEL_BYTES);const compatibility=requireJsonObjectOrNull(inference.compatibility,"inference.compatibility");const inputModalities=inference.inputModalities===null?null:requireEnumList(inference.inputModalities,new Set(MANAGED_STARTUP_PROFILE_CAPABILITIES[agent].inputModalities),"inference.inputModalities",{allowEmpty:false});if(upstreamEndpointUrl!==null&&!MANAGED_STARTUP_PROFILE_CAPABILITIES[agent].supportsUpstreamEndpoint){invalid(`inference.upstreamEndpointUrl must be null for ${agent}`)}if(agent==="openclaw"){if(primaryModelRef===null||inputModalities===null){invalid("openclaw requires primaryModelRef and inputModalities")}if(primaryModelRef!==`${routeProvider}/${model}`){invalid("openclaw primaryModelRef must match routeProvider and model")}}else{if(primaryModelRef!==null||compatibility!==null||inputModalities!==null){invalid(`${agent} does not support primaryModelRef, compatibility, or inputModalities`)}if(agent==="langchain-deepagents-code"&&!isValidDcodeUpstreamProvider(upstreamProvider)){invalid("inference.upstreamProvider must start with an ASCII letter or digit and contain 1-64 ASCII letters, digits, dots, underscores, or hyphens for DCode")}}return{routeProvider,upstreamProvider,model,routedBaseUrl:requireHttpUrl(inference.routedBaseUrl,"inference.routedBaseUrl"),upstreamEndpointUrl,api,primaryModelRef,compatibility,inputModalities}}function validateProxy(value,agent){const proxy=requireRecord(value,"proxy");rejectUnknownKeys(proxy,PROXY_KEYS,"proxy");const hostHttpUrl=requireProxyUrl(proxy.hostHttpUrl,new Set(["http:"]),"proxy.hostHttpUrl");const hostHttpsUrl=requireProxyUrl(proxy.hostHttpsUrl,new Set(["http:","https:"]),"proxy.hostHttpsUrl");const hostNoProxy=requireStringList(proxy.hostNoProxy,"proxy.hostNoProxy");if(!MANAGED_STARTUP_PROFILE_CAPABILITIES[agent].supportsHostProxyIntent&&(hostHttpUrl!==null||hostHttpsUrl!==null||hostNoProxy.length>0)){invalid(`${agent} rejects host proxy intent and accepts only its root-owned managed route`)}return{managedHost:requireManagedProxyHost(proxy.managedHost,"proxy.managedHost"),managedPort:requirePort(proxy.managedPort,"proxy.managedPort"),hostHttpUrl,hostHttpsUrl,hostNoProxy}}function validateTools(value,agent){const tools=requireRecord(value,"tools");rejectUnknownKeys(tools,TOOLS_KEYS,"tools");const enabledGateways=requireEnumList(tools.enabledGateways,new Set(MANAGED_STARTUP_PROFILE_CAPABILITIES[agent].toolGateways),"tools.enabledGateways",{allowEmpty:true});return{disclosure:requireStringEnum(tools.disclosure,new Set(["progressive","direct"]),"tools.disclosure"),enabledGateways}}function validateTuning(value,agent){const tuning=requireRecord(value,"tuning");rejectUnknownKeys(tuning,TUNING_KEYS,"tuning");const result={contextWindow:requireNullablePositiveInteger(tuning.contextWindow,"tuning.contextWindow"),maxTokens:requireNullablePositiveInteger(tuning.maxTokens,"tuning.maxTokens"),reasoning:requireNullableBoolean(tuning.reasoning,"tuning.reasoning"),reasoningEffort:tuning.reasoningEffort===null?null:requireStringEnum(tuning.reasoningEffort,REASONING_EFFORT_SET,"tuning.reasoningEffort")};const advertised=new Set(MANAGED_STARTUP_PROFILE_CAPABILITIES[agent].tuningFields);const unsupported=TUNING_FIELD_ORDER.filter(field=>result[field]!==null&&!advertised.has(field));if(unsupported.length>0){invalid(`${agent} does not support startup tuning fields: ${unsupported.join(", ")}`)}if(agent==="openclaw"){const missing=TUNING_FIELD_ORDER.filter(field=>advertised.has(field)&&result[field]===null);if(missing.length>0){invalid(`openclaw requires ${missing.join(", ")} tuning`)}}if(agent==="hermes"&&result.contextWindow!==null&&result.contextWindowcanonicalizeJson(item));if(!isPlainObject(value))return value;const result={};const keys=sortStrings(Object.keys(value));for(let index=0;indexMANAGED_STARTUP_PROFILE_MAX_BYTES){invalid(`canonical payload exceeds ${String(MANAGED_STARTUP_PROFILE_MAX_BYTES)} bytes`)}return serialized}function decodeManagedStartupProfile(encoded){if(typeof encoded!=="string"||encoded.length===0||import_node_buffer.Buffer.byteLength(encoded,"ascii")>MANAGED_STARTUP_PROFILE_MAX_ENCODED_BYTES||!BASE64URL_RE.test(encoded)||encoded.length%4===1){invalid("encoded payload is malformed or exceeds the size limit")}const bytes=import_node_buffer.Buffer.from(encoded,"base64url");if(bytes.length===0||bytes.length>MANAGED_STARTUP_PROFILE_MAX_BYTES||bytes.toString("base64url")!==encoded){invalid("encoded payload is malformed or exceeds the size limit")}let raw;try{raw=UTF8_DECODER.decode(bytes)}catch{invalid("payload is not valid UTF-8")}let parsed;try{parsed=JSON.parse(raw)}catch{invalid("payload is not valid JSON")}const profile=validateManagedStartupProfile(parsed);if(serializeManagedStartupProfile(profile)!==raw){invalid("payload is not in canonical form")}return profile}function fingerprintManagedStartupProfile(profile){return(0,import_node_crypto2.createHash)("sha256").update(serializeManagedStartupProfile(profile),"utf8").digest("hex")}var ManagedStartupAgentEnvironmentError=class extends Error{constructor(message){super(`Cannot map managed startup profile: ${message}`);this.name="ManagedStartupAgentEnvironmentError"}};var EMPTY_APPLICATION_ENVIRONMENT=Object.freeze({});var OPENCLAW_APPLICATION_RUNTIME_INPUTS=Object.freeze([["NEMOCLAW_AUTO_PAIR_DEADLINE_SECS","positive-finite-seconds"],["NEMOCLAW_AUTO_PAIR_FAST_DEADLINE_SECS","positive-finite-seconds"],["NEMOCLAW_AUTO_PAIR_FAST_REENTRY_INTERVAL_SECS","positive-finite-seconds"],["NEMOCLAW_AUTO_PAIR_FAST_REENTRY_POLLS","positive-safe-integer"],["NEMOCLAW_AUTO_PAIR_RUN_TIMEOUT_SECS","positive-finite-seconds"],["NEMOCLAW_AUTO_PAIR_SLOW_INTERVAL_SECS","positive-finite-seconds"]]);function booleanFlag(value){return value?"1":"0"}function canonicalizeJson2(value){if(Array.isArray(value))return value.map(item=>canonicalizeJson2(item));if(value===null||typeof value!=="object")return value;const record=value;return Object.fromEntries(Object.keys(record).sort().map(key=>[key,canonicalizeJson2(record[key])]))}function encodeCanonicalJson(value){return import_node_buffer2.Buffer.from(JSON.stringify(canonicalizeJson2(value)),"utf8").toString("base64")}function sortedEnvironment(environment){return Object.freeze(Object.fromEntries(Object.entries(environment).sort(([left],[right])=>leftright?1:0)))}function canonicalApplicationRuntimeValue(name,raw,kind){if(raw.includes("\0")||/[\r\n]/u.test(raw)){throw new ManagedStartupAgentEnvironmentError(`${name} must be single-line text`)}const value=Number(raw.trim());const valid=kind==="positive-safe-integer"?Number.isSafeInteger(value)&&value>0:Number.isFinite(value)&&value>0;if(!valid){throw new ManagedStartupAgentEnvironmentError(`${name} must be ${kind==="positive-safe-integer"?"a positive safe integer":"finite positive seconds"}`)}return String(value)}function applicationRuntimePlan(profile,environment){const exportEnvironment={};if(profile.agent==="openclaw"){for(const[name,kind]of OPENCLAW_APPLICATION_RUNTIME_INPUTS){const raw=environment[name];if(raw!==void 0){exportEnvironment[name]=canonicalApplicationRuntimeValue(name,raw,kind)}}}const unsetEnvironment=new Set(MANAGED_STARTUP_RUNTIME_CLEANUP_OBLIGATIONS.filter(({supportedFor})=>!supportedFor.includes(profile.agent)).map(({input})=>input));if(profile.agent!=="openclaw"){for(const[name]of OPENCLAW_APPLICATION_RUNTIME_INPUTS){unsetEnvironment.add(name)}}return Object.freeze({exportEnvironment:sortedEnvironment(exportEnvironment),unsetEnvironment:Object.freeze([...unsetEnvironment].sort())})}function commonConfigurationEnvironment(profile){return{NEMOCLAW_INFERENCE_API:profile.inference.api,NEMOCLAW_INFERENCE_BASE_URL:profile.inference.routedBaseUrl,NEMOCLAW_INFERENCE_PROVIDER_ID:profile.inference.routeProvider,NEMOCLAW_MODEL:profile.inference.model,NEMOCLAW_TOOL_DISCLOSURE:profile.tools.disclosure,NEMOCLAW_UPSTREAM_PROVIDER:profile.inference.upstreamProvider}}function appendHostProxyEnvironment(environment,profile,options={}){if(options.preserveAmbientWhenAbsent===true&&profile.proxy.hostHttpUrl===null&&profile.proxy.hostHttpsUrl===null&&profile.proxy.hostNoProxy.length===0){return}const httpProxy=profile.proxy.hostHttpUrl??"";const httpsProxy=profile.proxy.hostHttpsUrl??"";const noProxy=profile.proxy.hostNoProxy.join(",");environment.HTTP_PROXY=httpProxy;environment.HTTPS_PROXY=httpsProxy;environment.NO_PROXY=noProxy;environment.http_proxy=httpProxy;environment.https_proxy=httpsProxy;environment.no_proxy=noProxy}function messagingEnvironment(profile,expectedAgent){if(profile.messaging.plan===null)return{};const plan=parseSandboxMessagingPlan(profile.messaging.plan,{agent:expectedAgent});if(!plan){throw new ManagedStartupAgentEnvironmentError(`messaging.plan must contain a validated ${expectedAgent} messaging plan`)}const{workflow:_workflow,...imageBuildPlan}=plan;return{NEMOCLAW_MESSAGING_PLAN_B64:encodeCanonicalJson(imageBuildPlan)}}function corporateCaMaterial(profile){return Object.freeze({kind:"corporate-ca-handoff",legacyInput:"NEMOCLAW_CORPORATE_CA_B64",expectedSha256:profile.corporateCa.bundleSha256})}function rootOwnedFile(legacyInput,path5,value){return Object.freeze({kind:"root-owned-file",legacyInput,path:path5,contents:`${value} -`,owner:"root",group:"root",mode:292})}function dashboardAction(dashboard){return Object.freeze({kind:"configure-dashboard",dashboard:Object.freeze(structuredClone(dashboard))})}function applicationActions(profile,messagingAgent){const actions=[];if(messagingAgent!==null){actions.push(Object.freeze({kind:"apply-messaging-plan",agent:messagingAgent,mode:profile.messaging.plan===null?"clear":"apply",phase:"runtime-setup",runAs:"root"}))}actions.push(Object.freeze({kind:"generate-agent-config",agent:profile.agent,runAs:"sandbox"}));if(messagingAgent!==null){actions.push(Object.freeze({kind:"apply-messaging-plan",agent:messagingAgent,mode:profile.messaging.plan===null?"clear":"apply",phase:"post-agent-install",runAs:"sandbox"}))}actions.push(dashboardAction(profile.dashboard));return Object.freeze(actions)}function mapOpenClawProfile(profile,environment){if(profile.agent!=="openclaw"||profile.agentConfig.agent!=="openclaw"||profile.dashboard.agent!=="openclaw"||profile.inference.primaryModelRef===null||profile.inference.inputModalities===null||profile.tuning.contextWindow===null||profile.tuning.maxTokens===null||profile.tuning.reasoning===null||profile.tuning.reasoningEffort===null){throw new ManagedStartupAgentEnvironmentError("OpenClaw profile state is inconsistent")}const configurationEnvironment={...commonConfigurationEnvironment(profile),...messagingEnvironment(profile,"openclaw"),CHAT_UI_URL:profile.dashboard.url,NEMOCLAW_AGENT_HEARTBEAT_EVERY:profile.agentConfig.heartbeatEvery??"",NEMOCLAW_AGENT_TIMEOUT:String(profile.agentConfig.agentTimeoutSeconds),NEMOCLAW_CONTEXT_WINDOW:String(profile.tuning.contextWindow),NEMOCLAW_DASHBOARD_BIND:profile.dashboard.bindAddress==="0.0.0.0"?profile.dashboard.bindAddress:"",NEMOCLAW_DISABLE_DEVICE_AUTH:booleanFlag(profile.agentConfig.deviceAuth.disabled),NEMOCLAW_DEVICE_AUTH_OPT_OUT_SOURCE:profile.agentConfig.deviceAuth.optOutSource,NEMOCLAW_EXTRA_AGENTS_JSON_B64:encodeCanonicalJson(profile.agentConfig.extraAgents),NEMOCLAW_INFERENCE_COMPAT_B64:encodeCanonicalJson(profile.inference.compatibility),NEMOCLAW_INFERENCE_INPUTS:profile.inference.inputModalities.join(","),NEMOCLAW_MAX_TOKENS:String(profile.tuning.maxTokens),NEMOCLAW_OPENCLAW_OTEL:booleanFlag(profile.agentConfig.otel.enabled),NEMOCLAW_OPENCLAW_OTEL_ENDPOINT:profile.agentConfig.otel.endpointUrl,NEMOCLAW_OPENCLAW_OTEL_SAMPLE_RATE:String(profile.agentConfig.otel.sampleRate),NEMOCLAW_OPENCLAW_OTEL_SERVICE_NAME:profile.agentConfig.otel.serviceName,NEMOCLAW_PRIMARY_MODEL_REF:profile.inference.primaryModelRef,NEMOCLAW_PROXY_HOST:profile.proxy.managedHost,NEMOCLAW_PROXY_PORT:String(profile.proxy.managedPort),NEMOCLAW_REASONING:String(profile.tuning.reasoning),NEMOCLAW_REASONING_EFFORT:profile.tuning.reasoningEffort,NEMOCLAW_WEB_SEARCH_ENABLED:booleanFlag(profile.agentConfig.webSearch.enabled),NEMOCLAW_WEB_SEARCH_PROVIDER:profile.agentConfig.webSearch.provider,NEMOCLAW_WSL_DASHBOARD_EXPOSURE:booleanFlag(profile.dashboard.wslExposure)};const runtimeEnvironment={...configurationEnvironment};delete runtimeEnvironment.NEMOCLAW_MESSAGING_PLAN_B64;runtimeEnvironment.NEMOCLAW_DASHBOARD_PORT=String(profile.dashboard.port);runtimeEnvironment.NEMOCLAW_MINIMAL_BOOTSTRAP=booleanFlag(profile.agentConfig.minimalBootstrap);appendHostProxyEnvironment(runtimeEnvironment,profile,{preserveAmbientWhenAbsent:true});return Object.freeze({schemaVersion:profile.schemaVersion,agent:profile.agent,configurationEnvironment:sortedEnvironment(configurationEnvironment),runtimeEnvironment:sortedEnvironment(runtimeEnvironment),applicationRuntime:applicationRuntimePlan(profile,environment),materials:Object.freeze([corporateCaMaterial(profile)]),actions:applicationActions(profile,"openclaw")})}function mapHermesProfile(profile,environment){if(profile.agent!=="hermes"||profile.agentConfig.agent!=="hermes"||profile.dashboard.agent!=="hermes"){throw new ManagedStartupAgentEnvironmentError("Hermes profile state is inconsistent")}const configurationEnvironment={...commonConfigurationEnvironment(profile),...messagingEnvironment(profile,"hermes"),CHAT_UI_URL:profile.dashboard.url,NEMOCLAW_CONTEXT_WINDOW:profile.tuning.contextWindow===null?"":String(profile.tuning.contextWindow),NEMOCLAW_HERMES_TOOL_GATEWAY_BROKER:booleanFlag(profile.tools.enabledGateways.length>0),NEMOCLAW_HERMES_TOOL_GATEWAY_PRESETS_B64:encodeCanonicalJson(profile.tools.enabledGateways),NEMOCLAW_WEB_SEARCH_ENABLED:booleanFlag(profile.agentConfig.webSearch.enabled),NEMOCLAW_WEB_SEARCH_PROVIDER:profile.agentConfig.webSearch.provider};const runtimeEnvironment={...configurationEnvironment};delete runtimeEnvironment.NEMOCLAW_MESSAGING_PLAN_B64;runtimeEnvironment.NEMOCLAW_DASHBOARD_PORT=profile.dashboard.publicPort===null?"":String(profile.dashboard.publicPort);runtimeEnvironment.NEMOCLAW_HERMES_DASHBOARD=profile.dashboard.mode==="loopback-forwarded"?"1":"0";runtimeEnvironment.NEMOCLAW_HERMES_DASHBOARD_INTERNAL_PORT=profile.dashboard.internalPort===null?"":String(profile.dashboard.internalPort);runtimeEnvironment.NEMOCLAW_HERMES_DASHBOARD_PORT=profile.dashboard.publicPort===null?"":String(profile.dashboard.publicPort);runtimeEnvironment.NEMOCLAW_HERMES_DASHBOARD_TUI=booleanFlag(profile.dashboard.tuiEnabled);runtimeEnvironment.NEMOCLAW_PROXY_HOST=profile.proxy.managedHost;runtimeEnvironment.NEMOCLAW_PROXY_PORT=String(profile.proxy.managedPort);appendHostProxyEnvironment(runtimeEnvironment,profile,{preserveAmbientWhenAbsent:true});return Object.freeze({schemaVersion:profile.schemaVersion,agent:profile.agent,configurationEnvironment:sortedEnvironment(configurationEnvironment),runtimeEnvironment:sortedEnvironment(runtimeEnvironment),applicationRuntime:applicationRuntimePlan(profile,environment),materials:Object.freeze([corporateCaMaterial(profile)]),actions:applicationActions(profile,"hermes")})}function mapDcodeProfile(profile,environment){if(profile.agent!=="langchain-deepagents-code"||profile.agentConfig.agent!=="langchain-deepagents-code"||profile.dashboard.agent!=="langchain-deepagents-code"||profile.messaging.plan!==null){throw new ManagedStartupAgentEnvironmentError("LangChain Deep Agents Code profile state is inconsistent")}const reasoningEffort=profile.tuning.reasoningEffort===null||profile.tuning.reasoningEffort==="default"?"":profile.tuning.reasoningEffort;const configurationEnvironment={...commonConfigurationEnvironment(profile),NEMOCLAW_REASONING_EFFORT:reasoningEffort,NEMOCLAW_UPSTREAM_ENDPOINT_URL:profile.inference.upstreamEndpointUrl??""};appendHostProxyEnvironment(configurationEnvironment,profile);const runtimeEnvironment={...configurationEnvironment,NEMOCLAW_OBSERVABILITY:booleanFlag(profile.agentConfig.observabilityEnabled)};delete runtimeEnvironment.NEMOCLAW_INFERENCE_BASE_URL;delete runtimeEnvironment.NEMOCLAW_REASONING_EFFORT;delete runtimeEnvironment.NEMOCLAW_UPSTREAM_PROVIDER;for(const name of["HTTP_PROXY","HTTPS_PROXY","NO_PROXY","http_proxy","https_proxy","no_proxy"]){delete runtimeEnvironment[name]}const materials=Object.freeze([corporateCaMaterial(profile),rootOwnedFile("NEMOCLAW_DCODE_AUTO_APPROVAL","/usr/local/share/nemoclaw/dcode-auto-approval",profile.agentConfig.autoApprovalMode),rootOwnedFile("NEMOCLAW_INFERENCE_BASE_URL","/usr/local/share/nemoclaw/dcode-inference-base-url",profile.inference.routedBaseUrl),rootOwnedFile("NEMOCLAW_UPSTREAM_PROVIDER","/usr/local/share/nemoclaw/dcode-upstream-provider",profile.inference.upstreamProvider),rootOwnedFile("NEMOCLAW_PROXY_HOST","/usr/local/share/nemoclaw/dcode-proxy-host",profile.proxy.managedHost),rootOwnedFile("NEMOCLAW_PROXY_PORT","/usr/local/share/nemoclaw/dcode-proxy-port",String(profile.proxy.managedPort)),rootOwnedFile("NEMOCLAW_REASONING_EFFORT","/usr/local/share/nemoclaw/dcode-reasoning-effort",reasoningEffort)]);return Object.freeze({schemaVersion:profile.schemaVersion,agent:profile.agent,configurationEnvironment:sortedEnvironment(configurationEnvironment),runtimeEnvironment:sortedEnvironment(runtimeEnvironment),applicationRuntime:applicationRuntimePlan(profile,environment),materials,actions:applicationActions(profile,null)})}function mapPiProfile(profile,environment){if(profile.agent!=="pi"||profile.agentConfig.agent!=="pi"||profile.dashboard.agent!=="pi"||profile.messaging.plan!==null){throw new ManagedStartupAgentEnvironmentError("Pi profile state is inconsistent")}const configurationEnvironment={...commonConfigurationEnvironment(profile),NEMOCLAW_CONTEXT_WINDOW:profile.tuning.contextWindow===null?"":String(profile.tuning.contextWindow),NEMOCLAW_MAX_TOKENS:profile.tuning.maxTokens===null?"":String(profile.tuning.maxTokens),NEMOCLAW_REASONING:profile.tuning.reasoning===null?"":String(profile.tuning.reasoning)};appendHostProxyEnvironment(configurationEnvironment,profile);const runtimeEnvironment={...configurationEnvironment};delete runtimeEnvironment.NEMOCLAW_INFERENCE_BASE_URL;delete runtimeEnvironment.NEMOCLAW_CONTEXT_WINDOW;delete runtimeEnvironment.NEMOCLAW_MAX_TOKENS;delete runtimeEnvironment.NEMOCLAW_REASONING;for(const name of["HTTP_PROXY","HTTPS_PROXY","NO_PROXY","http_proxy","https_proxy","no_proxy"]){delete runtimeEnvironment[name]}const materials=Object.freeze([corporateCaMaterial(profile),rootOwnedFile("NEMOCLAW_PROXY_HOST","/usr/local/share/nemoclaw/pi-proxy-host",profile.proxy.managedHost),rootOwnedFile("NEMOCLAW_PROXY_PORT","/usr/local/share/nemoclaw/pi-proxy-port",String(profile.proxy.managedPort))]);return Object.freeze({schemaVersion:profile.schemaVersion,agent:profile.agent,configurationEnvironment:sortedEnvironment(configurationEnvironment),runtimeEnvironment:sortedEnvironment(runtimeEnvironment),applicationRuntime:applicationRuntimePlan(profile,environment),materials,actions:applicationActions(profile,null)})}function mapManagedStartupProfileToAgentEnvironment(profile,environment=EMPTY_APPLICATION_ENVIRONMENT){const validated=validateManagedStartupProfile(profile);switch(validated.agent){case"openclaw":return mapOpenClawProfile(validated,environment);case"hermes":return mapHermesProfile(validated,environment);case"langchain-deepagents-code":return mapDcodeProfile(validated,environment);case"pi":return mapPiProfile(validated,environment)}}var import_node_buffer3=require("node:buffer");var import_node_crypto3=require("node:crypto");var import_node_fs=__toESM(require("node:fs"));var import_node_path=__toESM(require("node:path"));var import_node_util2=require("node:util");var MANAGED_STARTUP_APPLICATION_STATE_DIR="/var/lib/nemoclaw/startup-profile";var MANAGED_STARTUP_CA_MAX_BYTES=128*1024;var MANAGED_STARTUP_CA_MAX_CERTIFICATES=24;var STATE_SCHEMA_VERSION=1;var STATE_DIRECTORY_MODE=448;var STATE_FILE_MODE=384;var MAX_CONTROL_FILE_BYTES=512;var MAX_STATE_ENTRIES=32;var SHA256_RE2=/^[a-f0-9]{64}$/u;var GENERATION_RE=/^generation-([a-f0-9]{64})$/u;var PREPARE_TEMP_RE=/^\.prepare-[0-9]+-[a-f0-9]{24}$/u;var CONTROL_TEMP_RE=/^\.(?:committed|pending)\.json-[a-f0-9]{24}\.tmp$/u;var PEM_CERTIFICATE_RE=/-----BEGIN CERTIFICATE-----\r?\n[A-Za-z0-9+/=\r\n]+?-----END CERTIFICATE-----/gu;var UTF8_DECODER2=new import_node_util2.TextDecoder("utf-8",{fatal:true});var DEFAULT_RUNTIME={rootUid:0,rootGid:0};var ManagedStartupApplicationError=class extends Error{constructor(message){super(`Managed startup application failed: ${message}`);this.name="ManagedStartupApplicationError"}};function fail(message){throw new ManagedStartupApplicationError(message)}function runtimeFor(override){return override??DEFAULT_RUNTIME}function requireContainerRoot(){if(process.geteuid?.()!==0){fail("the image-side applicator must run with effective uid 0")}}function modeOf(stat){return stat.mode&511}function requireOwner(stat,target,runtime){if(stat.uid!==runtime.rootUid||stat.gid!==runtime.rootGid){fail(`${target} must be owned by root:root`)}}function requireSecureDirectory(target,runtime,exactMode){let stat;try{stat=import_node_fs.default.lstatSync(target)}catch{fail(`state directory component is missing or unreadable: ${target}`)}if(stat.isSymbolicLink()||!stat.isDirectory()){fail(`state directory component must be a real directory: ${target}`)}const runtimeOwned=stat.uid===runtime.rootUid&&stat.gid===runtime.rootGid;const systemRootOwned=stat.uid===0&&stat.gid===0;if(exactMode){requireOwner(stat,target,runtime)}else if(!runtimeOwned&&!systemRootOwned){fail(`state directory ancestor is not owned by a trusted identity: ${target}`)}const mode=modeOf(stat);const writableByUntrustedIdentity=(mode&18)!==0;const trustedStickyRoot=(stat.mode&512)!==0&&(runtimeOwned||systemRootOwned);if(exactMode&&mode!==STATE_DIRECTORY_MODE||!exactMode&&writableByUntrustedIdentity&&!trustedStickyRoot){fail(exactMode?`${target} must have mode 0700`:`${target} is a replaceable group- or world-writable ancestor`)}}function requireSecureAncestors(target,runtime){const root=import_node_path.default.parse(target).root;let current=root;requireSecureDirectory(current,runtime,false);for(const segment of import_node_path.default.relative(root,target).split(import_node_path.default.sep).filter(Boolean)){current=import_node_path.default.join(current,segment);let stat;try{stat=import_node_fs.default.lstatSync(current)}catch{fail(`state directory component is missing or unreadable: ${current}`)}if(stat.isSymbolicLink()){const runtimeOwned=stat.uid===runtime.rootUid&&stat.gid===runtime.rootGid;const systemRootOwned=stat.uid===0&&stat.gid===0;if(!runtimeOwned&&!systemRootOwned){fail(`state directory ancestor is a replaceable symlink: ${current}`)}let resolved;try{resolved=import_node_fs.default.realpathSync(current)}catch{fail(`state directory symlink is missing or unreadable: ${current}`)}requireSecureAncestors(resolved,runtime);continue}requireSecureDirectory(current,runtime,false)}}function ensureStateDirectory(rawStateDirectory,runtime){const stateDirectory=rawStateDirectory??MANAGED_STARTUP_APPLICATION_STATE_DIR;if(!import_node_path.default.isAbsolute(stateDirectory)||stateDirectory.includes("\0")){fail("stateDirectory must be an absolute path")}const normalized=import_node_path.default.resolve(stateDirectory);const parent=import_node_path.default.dirname(normalized);requireSecureAncestors(parent,runtime);try{import_node_fs.default.mkdirSync(normalized,{mode:STATE_DIRECTORY_MODE});import_node_fs.default.chownSync(normalized,runtime.rootUid,runtime.rootGid);import_node_fs.default.chmodSync(normalized,STATE_DIRECTORY_MODE)}catch(error){if(error.code!=="EEXIST"){fail(`could not create the managed startup state directory: ${normalized}`)}}requireSecureDirectory(normalized,runtime,true);return normalized}function requireSecureRegularFileStat(stat,target,runtime){if(!stat.isFile()||stat.isSymbolicLink()){fail(`${target} must be a regular file`)}if(stat.nlink!==1){fail(`${target} must not be hardlinked`)}requireOwner(stat,target,runtime);if(modeOf(stat)!==STATE_FILE_MODE){fail(`${target} must have mode 0600`)}}function readSecureFile(target,maxBytes,runtime){let descriptor;try{descriptor=import_node_fs.default.openSync(target,import_node_fs.default.constants.O_RDONLY|import_node_fs.default.constants.O_NOFOLLOW)}catch{fail(`state file is missing, unreadable, or a symlink: ${target}`)}try{const stat=import_node_fs.default.fstatSync(descriptor);requireSecureRegularFileStat(stat,target,runtime);if(stat.size<1||stat.size>maxBytes){fail(`${target} is empty or exceeds its size limit`)}const content=import_node_fs.default.readFileSync(descriptor);if(content.length!==stat.size){fail(`${target} changed while it was being read`)}return content}finally{import_node_fs.default.closeSync(descriptor)}}function writeSecureNewFile(target,content,runtime){let descriptor;try{descriptor=import_node_fs.default.openSync(target,import_node_fs.default.constants.O_CREAT|import_node_fs.default.constants.O_EXCL|import_node_fs.default.constants.O_WRONLY|import_node_fs.default.constants.O_NOFOLLOW,STATE_FILE_MODE)}catch{fail(`refused to replace an existing state file: ${target}`)}try{import_node_fs.default.fchownSync(descriptor,runtime.rootUid,runtime.rootGid);import_node_fs.default.fchmodSync(descriptor,STATE_FILE_MODE);import_node_fs.default.writeFileSync(descriptor,content);import_node_fs.default.fsyncSync(descriptor)}finally{import_node_fs.default.closeSync(descriptor)}}function syncDirectory(target){const descriptor=import_node_fs.default.openSync(target,import_node_fs.default.constants.O_RDONLY);try{import_node_fs.default.fsyncSync(descriptor)}finally{import_node_fs.default.closeSync(descriptor)}}function randomToken(){return(0,import_node_crypto3.randomBytes)(12).toString("hex")}function stateControl(fingerprint){return{schemaVersion:STATE_SCHEMA_VERSION,fingerprint,generation:`generation-${fingerprint}`}}function serializeStateControl(control){return JSON.stringify({fingerprint:control.fingerprint,generation:control.generation,schemaVersion:control.schemaVersion})}function parseStateControl(target,runtime){const bytes=readSecureFile(target,MAX_CONTROL_FILE_BYTES,runtime);let raw;try{raw=UTF8_DECODER2.decode(bytes)}catch{fail(`${target} is not valid UTF-8`)}let parsed;try{parsed=JSON.parse(raw)}catch{fail(`${target} is not valid JSON`)}if(typeof parsed!=="object"||parsed===null||Array.isArray(parsed)){fail(`${target} does not contain a valid state control`)}const record=parsed;if(Object.keys(record).sort().join(",")!=="fingerprint,generation,schemaVersion"||record.schemaVersion!==STATE_SCHEMA_VERSION||typeof record.fingerprint!=="string"||!SHA256_RE2.test(record.fingerprint)||record.generation!==`generation-${record.fingerprint}`){fail(`${target} does not contain a valid state control`)}const control=stateControl(record.fingerprint);if(serializeStateControl(control)!==raw){fail(`${target} is not in canonical form`)}return control}function publishStateControlIfAbsent(stateDirectory,basename,control,runtime){const target=import_node_path.default.join(stateDirectory,basename);const temporary=import_node_path.default.join(stateDirectory,`.${basename}-${randomToken()}.tmp`);writeSecureNewFile(temporary,serializeStateControl(control),runtime);try{import_node_fs.default.linkSync(temporary,target)}catch(error){try{unlinkSecureControlOrTemp(temporary,runtime)}catch{}if(error.code==="EEXIST"){return{control:parseStateControl(target,runtime),created:false}}fail(`could not atomically publish ${basename}`)}try{import_node_fs.default.unlinkSync(temporary)}catch(error){if(error.code!=="ENOENT"){fail(`could not finalize atomic publication of ${basename}`)}}syncDirectory(stateDirectory);return{control,created:true}}function validateCorporateCaBytes(bytes){if(bytes.length<1||bytes.length>MANAGED_STARTUP_CA_MAX_BYTES){fail(`corporate CA bundle must contain 1-${String(MANAGED_STARTUP_CA_MAX_BYTES)} bytes`)}let pem;try{pem=UTF8_DECODER2.decode(bytes)}catch{fail("corporate CA bundle must be valid UTF-8 PEM")}const matches=[...pem.matchAll(PEM_CERTIFICATE_RE)];if(matches.length<1||matches.length>MANAGED_STARTUP_CA_MAX_CERTIFICATES||matches[0]?.index!==0){fail(`corporate CA bundle must contain 1-${String(MANAGED_STARTUP_CA_MAX_CERTIFICATES)} PEM CA certificates`)}let cursor=0;for(const match of matches){const index=match.index;if(index===void 0||!/^(?:\r?\n)+$/u.test(pem.slice(cursor,index))&&index!==0){fail("corporate CA bundle contains non-PEM material between certificates")}const block=match[0];let certificate;try{certificate=new import_node_crypto3.X509Certificate(block)}catch{fail("corporate CA bundle contains an invalid X.509 certificate")}if(!certificate.ca){fail("corporate CA bundle contains a certificate without basicConstraints CA:TRUE")}cursor=index+block.length}if(!/^(?:\r?\n)?$/u.test(pem.slice(cursor))){fail("corporate CA bundle contains trailing non-PEM material")}}function validateManagedStartupCorporateCaTransport(encoded,profile){const expectedDigest=profile.corporateCa.bundleSha256;if(expectedDigest===null){if(encoded!==void 0){fail("corporate CA transport must be absent when the profile has no CA digest")}return null}if(typeof encoded!=="string"||encoded.length===0||encoded.length>Math.ceil(MANAGED_STARTUP_CA_MAX_BYTES/3)*4||!/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/u.test(encoded)){fail("corporate CA transport must be canonical standard base64")}const bytes=import_node_buffer3.Buffer.from(encoded,"base64");if(bytes.toString("base64")!==encoded){fail("corporate CA transport must be canonical standard base64")}validateCorporateCaBytes(bytes);const actualDigest=(0,import_node_crypto3.createHash)("sha256").update(bytes).digest("hex");if(actualDigest!==expectedDigest){fail("corporate CA bundle does not match the profile SHA-256 digest")}return bytes}function readCanonicalProfile(profilePath,runtime){const bytes=readSecureFile(profilePath,MANAGED_STARTUP_PROFILE_MAX_BYTES,runtime);let raw;try{raw=UTF8_DECODER2.decode(bytes)}catch{fail(`${profilePath} is not valid UTF-8`)}let parsed;try{parsed=JSON.parse(raw)}catch{fail(`${profilePath} is not valid JSON`)}let profile;try{profile=validateManagedStartupProfile(parsed)}catch(error){fail(`${profilePath} is invalid: ${error.message}`)}if(serializeManagedStartupProfile(profile)!==raw){fail(`${profilePath} is not a canonical managed startup profile`)}return{profile,fingerprint:fingerprintManagedStartupProfile(profile)}}function validateGeneration(stateDirectory,control,runtime,expectedAgent){if(!GENERATION_RE.test(control.generation)){fail("state control names an invalid generation")}const directory=import_node_path.default.join(stateDirectory,control.generation);requireSecureDirectory(directory,runtime,true);const entries=import_node_fs.default.readdirSync(directory).sort();if(entries.some(entry=>entry!=="profile.json"&&entry!=="corporate-ca.pem")||!entries.includes("profile.json")){fail(`${directory} contains missing or unsupported state files`)}const profilePath=import_node_path.default.join(directory,"profile.json");const{profile,fingerprint}=readCanonicalProfile(profilePath,runtime);if(fingerprint!==control.fingerprint){fail(`${directory} does not match its recorded profile fingerprint`)}if(expectedAgent!==void 0&&profile.agent!==expectedAgent){fail(`managed startup profile targets ${profile.agent}, expected ${expectedAgent}`)}const caPath=import_node_path.default.join(directory,"corporate-ca.pem");let corporateCaPath=null;if(profile.corporateCa.bundleSha256===null){if(entries.includes("corporate-ca.pem")){fail(`${directory} contains a CA bundle that is absent from the profile`)}}else{if(!entries.includes("corporate-ca.pem")){fail(`${directory} is missing the CA bundle recorded by the profile`)}const caBytes=readSecureFile(caPath,MANAGED_STARTUP_CA_MAX_BYTES,runtime);validateCorporateCaBytes(caBytes);if((0,import_node_crypto3.createHash)("sha256").update(caBytes).digest("hex")!==profile.corporateCa.bundleSha256){fail(`${directory} contains a CA bundle with the wrong SHA-256 digest`)}corporateCaPath=caPath}return{directory,profilePath,corporateCaPath,profile,fingerprint}}function validateDisposableDirectory(target,runtime){requireSecureDirectory(target,runtime,true);const entries=import_node_fs.default.readdirSync(target);if(entries.length>2||entries.some(entry=>entry!=="profile.json"&&entry!=="corporate-ca.pem")){fail(`${target} is not a recognized disposable generation`)}for(const entry of entries){const file=import_node_path.default.join(target,entry);const stat=import_node_fs.default.lstatSync(file);requireSecureRegularFileStat(stat,file,runtime)}}function discardDirectory(target,runtime){validateDisposableDirectory(target,runtime);import_node_fs.default.rmSync(target,{recursive:true})}function discardDirectoryIfPresent(target,runtime){try{import_node_fs.default.lstatSync(target)}catch(error){if(error.code==="ENOENT")return false;fail(`could not inspect disposable generation ${target}`)}discardDirectory(target,runtime);return true}function unlinkSecureControlOrTemp(target,runtime){const stat=import_node_fs.default.lstatSync(target);requireSecureRegularFileStat(stat,target,runtime);if(stat.size>MAX_CONTROL_FILE_BYTES){fail(`${target} exceeds the state-control size limit`)}import_node_fs.default.unlinkSync(target)}function listStateEntries(stateDirectory){const entries=import_node_fs.default.readdirSync(stateDirectory).sort();if(entries.length>MAX_STATE_ENTRIES){fail(`state directory exceeds ${String(MAX_STATE_ENTRIES)} entries`)}return entries}function unlinkRecoverableControlTemp(stateDirectory,entry,runtime){const temporary=import_node_path.default.join(stateDirectory,entry);const stat=import_node_fs.default.lstatSync(temporary);if(stat.nlink===1){unlinkSecureControlOrTemp(temporary,runtime);return}const basename=entry.startsWith(".committed.json-")?"committed.json":entry.startsWith(".pending.json-")?"pending.json":null;const target=basename===null?null:import_node_path.default.join(stateDirectory,basename);let targetStat=null;try{targetStat=target===null?null:import_node_fs.default.lstatSync(target)}catch{fail(`refused to remove an unpaired atomic-control temporary file: ${temporary}`)}if(stat.nlink!==2||targetStat===null||stat.dev!==targetStat.dev||stat.ino!==targetStat.ino||!stat.isFile()||stat.isSymbolicLink()||modeOf(stat)!==STATE_FILE_MODE||stat.size<1||stat.size>MAX_CONTROL_FILE_BYTES){fail(`refused to remove an unpaired atomic-control temporary file: ${temporary}`)}requireOwner(stat,temporary,runtime);requireOwner(targetStat,target,runtime);import_node_fs.default.unlinkSync(temporary)}function cleanAtomicTemps(stateDirectory,entries,runtime){let changed=false;for(const entry of entries){const target=import_node_path.default.join(stateDirectory,entry);if(PREPARE_TEMP_RE.test(entry)){discardDirectory(target,runtime);changed=true}else if(CONTROL_TEMP_RE.test(entry)){unlinkRecoverableControlTemp(stateDirectory,entry,runtime);changed=true}}if(changed)syncDirectory(stateDirectory)}function requireKnownStateEntries(stateDirectory,entries){for(const entry of entries){if(entry==="committed.json"||entry==="pending.json"||GENERATION_RE.test(entry)||PREPARE_TEMP_RE.test(entry)||CONTROL_TEMP_RE.test(entry)){continue}fail(`${stateDirectory} contains unsupported state component ${entry}`)}}function discardGenerationsExcept(stateDirectory,keepGeneration,runtime){for(const entry of listStateEntries(stateDirectory)){if(GENERATION_RE.test(entry)&&entry!==keepGeneration){discardDirectoryIfPresent(import_node_path.default.join(stateDirectory,entry),runtime)}}}function optionalStateControl(stateDirectory,basename,runtime){const target=import_node_path.default.join(stateDirectory,basename);try{import_node_fs.default.lstatSync(target)}catch(error){if(error.code==="ENOENT")return null;fail(`could not inspect ${target}`)}return parseStateControl(target,runtime)}function removePendingControl(stateDirectory,runtime){try{unlinkSecureControlOrTemp(import_node_path.default.join(stateDirectory,"pending.json"),runtime)}catch(error){if(error.code==="ENOENT")return;throw error}syncDirectory(stateDirectory)}function stateControlsMatch(left,right){return left.fingerprint===right.fingerprint&&left.generation===right.generation}function recoverCommittedState(stateDirectory,committedControl,pendingControl,requested,expectedAgent,runtime){const committed=validateGeneration(stateDirectory,committedControl,runtime,expectedAgent);if(pendingControl)removePendingControl(stateDirectory,runtime);discardGenerationsExcept(stateDirectory,committedControl.generation,runtime);syncDirectory(stateDirectory);if(!stateControlsMatch(committedControl,requested)){fail("a different startup profile is already committed; recreate the sandbox to change it")}return committed}function recoverState(stateDirectory,requested,expectedAgent,runtime){const initialEntries=listStateEntries(stateDirectory);requireKnownStateEntries(stateDirectory,initialEntries);cleanAtomicTemps(stateDirectory,initialEntries,runtime);const initiallyCommittedControl=optionalStateControl(stateDirectory,"committed.json",runtime);const pendingControl=optionalStateControl(stateDirectory,"pending.json",runtime);const committedAfterPendingRead=optionalStateControl(stateDirectory,"committed.json",runtime);const committedControl=committedAfterPendingRead??initiallyCommittedControl;if(committedControl){return{committed:recoverCommittedState(stateDirectory,committedControl,pendingControl,requested,expectedAgent,runtime),pending:null}}if(pendingControl){if(stateControlsMatch(pendingControl,requested)){const pending=validateGeneration(stateDirectory,pendingControl,runtime,expectedAgent);const committedAfterPendingValidation=optionalStateControl(stateDirectory,"committed.json",runtime);if(committedAfterPendingValidation){return{committed:recoverCommittedState(stateDirectory,committedAfterPendingValidation,pendingControl,requested,expectedAgent,runtime),pending:null}}discardGenerationsExcept(stateDirectory,pendingControl.generation,runtime);return{committed:null,pending}}fail("a different startup profile is already pending; wait for it to commit or recreate")}return{committed:null,pending:null}}function createGeneration(stateDirectory,control,profileJson,corporateCa,runtime){const temporaryName=`.prepare-${String(process.pid)}-${randomToken()}`;const temporary=import_node_path.default.join(stateDirectory,temporaryName);const generation=import_node_path.default.join(stateDirectory,control.generation);let renameAttempted=false;try{import_node_fs.default.mkdirSync(temporary,{mode:STATE_DIRECTORY_MODE});import_node_fs.default.chownSync(temporary,runtime.rootUid,runtime.rootGid);import_node_fs.default.chmodSync(temporary,STATE_DIRECTORY_MODE);writeSecureNewFile(import_node_path.default.join(temporary,"profile.json"),profileJson,runtime);if(corporateCa){writeSecureNewFile(import_node_path.default.join(temporary,"corporate-ca.pem"),corporateCa,runtime)}syncDirectory(temporary);renameAttempted=true;import_node_fs.default.renameSync(temporary,generation);syncDirectory(stateDirectory)}catch(error){try{import_node_fs.default.lstatSync(temporary);discardDirectory(temporary,runtime)}catch{}if(error instanceof ManagedStartupApplicationError)throw error;if(renameAttempted&&(error.code==="EEXIST"||error.code==="ENOTEMPTY")){return validateGeneration(stateDirectory,control,runtime)}fail(`could not atomically prepare generation ${control.generation}`)}return validateGeneration(stateDirectory,control,runtime)}function toPrepared(status,stateDirectory,generation,expectedAgent){return{status,stateDirectory,generationDirectory:generation.directory,profilePath:generation.profilePath,corporateCaPath:generation.corporateCaPath,fingerprint:generation.fingerprint,expectedAgent,profile:generation.profile}}function prepareManagedStartupApplication(input,testRuntime){const runtime=runtimeFor(testRuntime);requireContainerRoot();let profile;try{profile=decodeManagedStartupProfile(input.encodedProfile)}catch(error){fail(error.message)}if(profile.agent!==input.expectedAgent){fail(`managed startup profile targets ${profile.agent}, expected ${input.expectedAgent}`)}const corporateCa=validateManagedStartupCorporateCaTransport(input.corporateCaB64,profile);const profileJson=serializeManagedStartupProfile(profile);const control=stateControl(fingerprintManagedStartupProfile(profile));const stateDirectory=ensureStateDirectory(input.stateDirectory,runtime);const recovered=recoverState(stateDirectory,control,input.expectedAgent,runtime);if(recovered.committed){return toPrepared("already-committed",stateDirectory,recovered.committed,input.expectedAgent)}if(recovered.pending){return toPrepared("prepared",stateDirectory,recovered.pending,input.expectedAgent)}const generation=createGeneration(stateDirectory,control,profileJson,corporateCa,runtime);const publication=publishStateControlIfAbsent(stateDirectory,"pending.json",control,runtime);if(publication.control.fingerprint!==control.fingerprint||publication.control.generation!==control.generation){discardDirectoryIfPresent(generation.directory,runtime);syncDirectory(stateDirectory);fail("a different startup profile won the pending-state transaction")}const committedAfterPublication=optionalStateControl(stateDirectory,"committed.json",runtime);if(committedAfterPublication){if(committedAfterPublication.fingerprint!==control.fingerprint||committedAfterPublication.generation!==control.generation){if(publication.created){removePendingControl(stateDirectory,runtime);discardDirectoryIfPresent(generation.directory,runtime);syncDirectory(stateDirectory)}fail("a different startup profile committed during pending-state publication")}const committedGeneration=validateGeneration(stateDirectory,committedAfterPublication,runtime,input.expectedAgent);removePendingControl(stateDirectory,runtime);discardGenerationsExcept(stateDirectory,committedAfterPublication.generation,runtime);return toPrepared("already-committed",stateDirectory,committedGeneration,input.expectedAgent)}const activeGeneration=publication.created?generation:validateGeneration(stateDirectory,publication.control,runtime,input.expectedAgent);return toPrepared("prepared",stateDirectory,activeGeneration,input.expectedAgent)}function validatePreparedHandle(handle){if(!import_node_path.default.isAbsolute(handle.stateDirectory)||!SHA256_RE2.test(handle.fingerprint)||handle.generationDirectory!==import_node_path.default.join(handle.stateDirectory,`generation-${handle.fingerprint}`)||handle.profilePath!==import_node_path.default.join(handle.generationDirectory,"profile.json")||handle.corporateCaPath!==null&&handle.corporateCaPath!==import_node_path.default.join(handle.generationDirectory,"corporate-ca.pem")){fail("prepared startup handle is malformed")}return stateControl(handle.fingerprint)}function commitManagedStartupApplication(prepared,testRuntime){const runtime=runtimeFor(testRuntime);requireContainerRoot();const requested=validatePreparedHandle(prepared);const stateDirectory=ensureStateDirectory(prepared.stateDirectory,runtime);const committedControl=optionalStateControl(stateDirectory,"committed.json",runtime);if(committedControl){if(committedControl.fingerprint!==requested.fingerprint||committedControl.generation!==requested.generation){fail("a different startup profile is already committed")}const generation2=validateGeneration(stateDirectory,committedControl,runtime,prepared.expectedAgent);return{...toPrepared("already-committed",stateDirectory,generation2,prepared.expectedAgent),status:"committed"}}const pendingControl=optionalStateControl(stateDirectory,"pending.json",runtime);if(!pendingControl||pendingControl.fingerprint!==requested.fingerprint||pendingControl.generation!==requested.generation){fail("the prepared startup generation is not the active pending generation")}const generation=validateGeneration(stateDirectory,pendingControl,runtime,prepared.expectedAgent);const publication=publishStateControlIfAbsent(stateDirectory,"committed.json",pendingControl,runtime);if(publication.control.fingerprint!==requested.fingerprint||publication.control.generation!==requested.generation){fail("a different startup profile won the committed-state transaction")}removePendingControl(stateDirectory,runtime);discardGenerationsExcept(stateDirectory,publication.control.generation,runtime);syncDirectory(stateDirectory);return{...toPrepared("already-committed",stateDirectory,generation,prepared.expectedAgent),status:"committed"}}var SHIPPED_AGENT_SET=new Set(MANAGED_STARTUP_AGENTS);var DEFAULT_DEPENDENCIES={prepareApplication:input=>prepareManagedStartupApplication(input),commitApplication:prepared=>commitManagedStartupApplication(prepared)};var ManagedStartupCoordinatorError=class extends Error{constructor(message){super(`Managed startup coordination failed: ${message}`);this.name="ManagedStartupCoordinatorError"}};function fail2(message){throw new ManagedStartupCoordinatorError(message)}function createAdapterRegistry(adapters2){const byAgent=new Map;for(const adapter of adapters2){if(typeof adapter!=="object"||adapter===null||!SHIPPED_AGENT_SET.has(adapter.agent)||typeof adapter.apply!=="function"){fail2("every adapter must identify one shipped agent and provide an apply function")}if(byAgent.has(adapter.agent)){fail2(`duplicate adapter registered for ${adapter.agent}`)}byAgent.set(adapter.agent,adapter)}const missing=MANAGED_STARTUP_AGENTS.filter(agent=>!byAgent.has(agent));if(missing.length>0){fail2(`missing adapter for ${missing.join(", ")}`)}if(byAgent.size!==MANAGED_STARTUP_AGENTS.length){fail2("adapter registry must contain exactly the shipped agents")}return Object.freeze(Object.fromEntries(MANAGED_STARTUP_AGENTS.map(agent=>{const adapter=byAgent.get(agent);if(!adapter)fail2(`missing adapter for ${agent}`);return[agent,adapter]})))}function requirePreparedIdentity(prepared,requestedAgent){if(prepared.expectedAgent!==requestedAgent||prepared.profile.agent!==requestedAgent){fail2(`prepared profile targets ${prepared.profile.agent}, expected ${requestedAgent}`)}}function adapterContext(prepared){return Object.freeze({agent:prepared.profile.agent,profile:prepared.profile,fingerprint:prepared.fingerprint,generationDirectory:prepared.generationDirectory,profilePath:prepared.profilePath,corporateCaPath:prepared.corporateCaPath})}async function coordinateManagedStartupApplication(input,adapters2,dependencies=DEFAULT_DEPENDENCIES){const registry=createAdapterRegistry(adapters2);const prepared=await dependencies.prepareApplication(input);requirePreparedIdentity(prepared,input.expectedAgent);if(prepared.status==="already-committed"){return{adapterApplied:false,application:await dependencies.commitApplication(prepared)}}const adapter=registry[prepared.profile.agent];if(adapter.agent!==prepared.profile.agent){fail2(`adapter registry cross-dispatch detected for ${prepared.profile.agent}`)}await adapter.apply(adapterContext(prepared));return{adapterApplied:true,application:await dependencies.commitApplication(prepared)}}var import_node_crypto4=require("node:crypto");var MANAGED_STARTUP_ROOT_APPLY_SCHEMA_VERSION=1;var MANAGED_STARTUP_ROOT_APPLY_MAX_BYTES=320*1024;var MAX_CORPORATE_CA_ENCODED_BYTES=4*Math.ceil(128*1024/3);var SHA256_RE3=/^[a-f0-9]{64}$/u;var STANDARD_BASE64_RE=/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/u;var MCP_SHADOW_DIAGNOSTICS_ENV="NEMOCLAW_MCP_SHADOW_DIAGNOSTICS";var MANAGED_STARTUP_APPLICATION_RUNTIME_ENV_KEYS=Object.freeze(MANAGED_STARTUP_PROFILE_DEFERRED_RUNTIME_INPUTS.openclaw.filter(({admission,owner})=>admission==="managed-launch-forwarded"&&owner==="application-environment").map(({input})=>input));function selectManagedStartupApplicationRuntimeEnvironment(environment){const selected={};for(const name of MANAGED_STARTUP_APPLICATION_RUNTIME_ENV_KEYS){const value=environment[name];if(name===MCP_SHADOW_DIAGNOSTICS_ENV){if(value?.trim()==="1")selected[name]="1";continue}if(value!==void 0)selected[name]=value}return Object.freeze(selected)}function fail3(message){throw new Error(`Managed startup root application request is invalid: ${message}`)}function exactAgent(value){if(typeof value==="string"&&MANAGED_STARTUP_AGENTS.includes(value)){return value}return fail3("agent is unsupported")}function createManagedStartupRootApplyRequest(input){const agent=exactAgent(input.agent);if(input.encodedProfile.length===0||input.encodedProfile.length>MANAGED_STARTUP_PROFILE_MAX_ENCODED_BYTES){fail3("encoded profile exceeds its bounded transport")}const profile=decodeManagedStartupProfile(input.encodedProfile);if(profile.agent!==agent){fail3(`profile targets ${profile.agent}, expected ${agent}`)}const corporateCaB64=input.corporateCaB64??null;if(corporateCaB64!==null&&(corporateCaB64.length===0||corporateCaB64.length>MAX_CORPORATE_CA_ENCODED_BYTES||!STANDARD_BASE64_RE.test(corporateCaB64)||Buffer.from(corporateCaB64,"base64").toString("base64")!==corporateCaB64)){fail3("corporate CA is not canonical bounded base64")}if(profile.corporateCa.bundleSha256!==null!==(corporateCaB64!==null)){fail3("corporate CA transport does not match the profile")}if(corporateCaB64!==null&&(0,import_node_crypto4.createHash)("sha256").update(Buffer.from(corporateCaB64,"base64")).digest("hex")!==profile.corporateCa.bundleSha256){fail3("corporate CA does not match the profile digest")}return Object.freeze({schemaVersion:MANAGED_STARTUP_ROOT_APPLY_SCHEMA_VERSION,agent,encodedProfile:input.encodedProfile,profileFingerprint:fingerprintManagedStartupProfile(profile),corporateCaB64})}function serializeManagedStartupRootApplyRequest(request){const normalized=createManagedStartupRootApplyRequest({agent:request.agent,encodedProfile:request.encodedProfile,...request.corporateCaB64===null?{}:{corporateCaB64:request.corporateCaB64}});if(request.schemaVersion!==MANAGED_STARTUP_ROOT_APPLY_SCHEMA_VERSION||request.profileFingerprint!==normalized.profileFingerprint||!SHA256_RE3.test(request.profileFingerprint)){fail3("schema version or profile fingerprint is invalid")}const serialized=`${JSON.stringify({agent:normalized.agent,corporateCaB64:normalized.corporateCaB64,encodedProfile:normalized.encodedProfile,profileFingerprint:normalized.profileFingerprint,schemaVersion:normalized.schemaVersion})} -`;if(Buffer.byteLength(serialized,"utf8")>MANAGED_STARTUP_ROOT_APPLY_MAX_BYTES){fail3("serialized request exceeds its bounded transport")}return serialized}function parseManagedStartupRootApplyRequest(text){if(text.length===0||Buffer.byteLength(text,"utf8")>MANAGED_STARTUP_ROOT_APPLY_MAX_BYTES){fail3("serialized request is empty or too large")}let parsed;try{parsed=JSON.parse(text)}catch{fail3("serialized request is not valid JSON")}if(typeof parsed!=="object"||parsed===null||Array.isArray(parsed)){fail3("serialized request must be an object")}const record=parsed;const expectedKeys=["agent","corporateCaB64","encodedProfile","profileFingerprint","schemaVersion"];if(Object.keys(record).sort().join(",")!==expectedKeys.sort().join(",")||record.schemaVersion!==MANAGED_STARTUP_ROOT_APPLY_SCHEMA_VERSION||typeof record.encodedProfile!=="string"||typeof record.profileFingerprint!=="string"||record.corporateCaB64!==null&&typeof record.corporateCaB64!=="string"){fail3("serialized request has an invalid schema")}const request=createManagedStartupRootApplyRequest({agent:exactAgent(record.agent),encodedProfile:record.encodedProfile,...record.corporateCaB64===null?{}:{corporateCaB64:record.corporateCaB64}});if(record.profileFingerprint!==request.profileFingerprint||!SHA256_RE3.test(record.profileFingerprint)){fail3("profile fingerprint does not match the encoded profile")}if(serializeManagedStartupRootApplyRequest(request)!==text){fail3("serialized request is not canonical")}return request}var import_node_crypto5=require("node:crypto");var import_node_fs2=__toESM(require("node:fs"));var import_node_path2=__toESM(require("node:path"));var TRANSACTION_SCHEMA_VERSION=1;var MAX_TRANSACTION_FILES=128;var MAX_TRANSACTION_FILE_BYTES=8*1024*1024;var MAX_TRANSACTION_TOTAL_BYTES=32*1024*1024;var MAX_MANIFEST_BYTES=256*1024;var MAX_COMMIT_RECEIPT_BYTES=4096;var TRANSACTION_PARENT_DIRECTORY_MODE=493;var TRANSACTION_DIRECTORY_MODE=448;var TRANSACTION_FILE_MODE=256;var ATOMIC_TEMPORARY_FILE_MODE=384;var MANAGED_STARTUP_SHARED_TRANSACTION_DIRECTORY="/var/lib/nemoclaw/managed-startup-shared-state-transaction-v1";var MANAGED_STARTUP_SHARED_ROLLBACK_RECEIPT_DIRECTORY="/run/nemoclaw/managed-startup-shared-rollback-receipt-v1";var MANAGED_STARTUP_SHARED_COMMIT_RECEIPT_DIRECTORY="/var/lib/nemoclaw/managed-startup-shared-state-commit-v1";var MANAGED_STARTUP_SHARED_COMMIT_RECEIPT_FILE="receipt.json";function fail4(message){throw new Error(`Managed startup shared-state transaction failed: ${message}`)}function resolveOptions(options={}){const sandboxRoot=import_node_path2.default.resolve(options.sandboxRoot??"/sandbox");const transactionDirectory=import_node_path2.default.resolve(options.transactionDirectory??MANAGED_STARTUP_SHARED_TRANSACTION_DIRECTORY);const commitReceiptDirectory=import_node_path2.default.resolve(options.commitReceiptDirectory??(options.transactionDirectory?import_node_path2.default.join(import_node_path2.default.dirname(transactionDirectory),import_node_path2.default.basename(MANAGED_STARTUP_SHARED_COMMIT_RECEIPT_DIRECTORY)):MANAGED_STARTUP_SHARED_COMMIT_RECEIPT_DIRECTORY));if(transactionDirectory===sandboxRoot||transactionDirectory.startsWith(`${sandboxRoot}${import_node_path2.default.sep}`)||commitReceiptDirectory===sandboxRoot||commitReceiptDirectory.startsWith(`${sandboxRoot}${import_node_path2.default.sep}`)||import_node_path2.default.dirname(commitReceiptDirectory)!==import_node_path2.default.dirname(transactionDirectory)||commitReceiptDirectory===transactionDirectory){fail4("transaction and commit receipts require distinct paths outside sandbox-shared state")}const bootstrapIdentity=options.bootstrapIdentity??null;if(bootstrapIdentity!==null&&!/^[a-f0-9]{64}$/u.test(bootstrapIdentity)){fail4("bootstrap identity must encode 32 lowercase-hex bytes")}return{sandboxRoot,transactionParentDirectory:import_node_path2.default.dirname(transactionDirectory),transactionDirectory,backupDirectory:import_node_path2.default.join(transactionDirectory,"backups"),manifestFile:import_node_path2.default.join(transactionDirectory,"manifest.json"),commitReceiptDirectory,commitReceiptFile:import_node_path2.default.join(commitReceiptDirectory,MANAGED_STARTUP_SHARED_COMMIT_RECEIPT_FILE),trustedUid:options.trustedUid??0,trustedGid:options.trustedGid??0,readOnlyReceipt:options.readOnlyReceipt??false,bootstrapIdentity}}function modeOf2(stat){if(typeof stat.mode==="bigint"){return Number(stat.mode&0o7777n)}return stat.mode&4095}function requireTransactionIdentity(options){const expectedUid=options.readOnlyReceipt?0:options.trustedUid;const expectedGid=options.readOnlyReceipt?0:options.trustedGid;if(process.geteuid?.()!==expectedUid||process.getegid?.()!==expectedGid){fail4("transaction control requires the trusted effective identity")}}function pathExistsNoFollow(target){try{import_node_fs2.default.lstatSync(target);return true}catch(error){if(error.code==="ENOENT")return false;fail4(`could not inspect ${target}`)}}function requireDirectory(target,options,expectedMode=null){let stat;try{stat=import_node_fs2.default.lstatSync(target)}catch{fail4(`required directory is missing: ${target}`)}if(stat.isSymbolicLink()||!stat.isDirectory()){fail4(`required directory is unsafe: ${target}`)}if(expectedMode!==null&&(stat.uid!==options.trustedUid||stat.gid!==options.trustedGid||modeOf2(stat)!==expectedMode)){fail4(`${target} must be ${options.trustedUid}:${options.trustedGid} mode ${expectedMode.toString(8)}`)}return stat}function requireTransactionBoundaries(options){requireDirectory(options.sandboxRoot,options);requireDirectory(options.transactionParentDirectory,options,TRANSACTION_PARENT_DIRECTORY_MODE)}function sameStableMetadata(left,right){return left.dev===right.dev&&left.ino===right.ino&&left.mode===right.mode&&left.nlink===right.nlink&&left.uid===right.uid&&left.gid===right.gid&&left.size===right.size&&left.mtimeNs===right.mtimeNs&&left.ctimeNs===right.ctimeNs}function readStableFile(target,maxBytes){const noFollow=import_node_fs2.default.constants.O_NOFOLLOW;if(typeof noFollow!=="number")fail4("O_NOFOLLOW is unavailable");let descriptor;try{descriptor=import_node_fs2.default.openSync(target,import_node_fs2.default.constants.O_RDONLY|noFollow)}catch{fail4(`could not safely open ${target}`)}try{const before=import_node_fs2.default.fstatSync(descriptor,{bigint:true});if(!before.isFile()||before.nlink!==1n||before.size<0n||before.size>BigInt(maxBytes)){fail4(`refusing unsafe or oversized transaction file ${target}`)}const bytes=Buffer.alloc(Number(before.size));let offset=0;while(offset!segment||segment==="."||segment==="..")){fail4(`unsafe transaction path ${JSON.stringify(value)}`)}return segments.join("/")}function absoluteTarget(relativePath,options){const safe=safeRelativePath(relativePath);const target=import_node_path2.default.resolve(options.sandboxRoot,safe);if(!target.startsWith(`${options.sandboxRoot}${import_node_path2.default.sep}`)){fail4(`transaction target escapes the sandbox root: ${relativePath}`)}return target}function relativeTarget(target,options){return safeRelativePath(import_node_path2.default.relative(options.sandboxRoot,target))}function validateExistingAncestors(target,expectedAgent,options){const relative=relativeTarget(target,options);const sandboxStat=requireDirectory(options.sandboxRoot,options);const outputRoot=agentRoot(expectedAgent,options.sandboxRoot);if(target!==outputRoot&&!target.startsWith(`${outputRoot}${import_node_path2.default.sep}`)){fail4(`transaction target escapes the ${expectedAgent} state root: ${target}`)}let current=options.sandboxRoot;let expectedDevice=sandboxStat.dev;const segments=relative.split("/").slice(0,-1);for(const segment of segments){current=import_node_path2.default.join(current,segment);let stat;try{stat=import_node_fs2.default.lstatSync(current)}catch(error){if(error.code==="ENOENT")return;fail4(`could not inspect transaction path ancestor ${current}`)}if(stat.isSymbolicLink()||!stat.isDirectory()){fail4(`transaction path ancestor is unsafe: ${current}`)}if(current===outputRoot&&expectedAgent==="hermes"){expectedDevice=stat.dev}else if(stat.dev!==expectedDevice){fail4(`transaction path crosses a nested filesystem mount: ${current}`)}}}function managedOutputDevice(expectedAgent,options){const sandboxStat=requireDirectory(options.sandboxRoot,options);const outputRoot=agentRoot(expectedAgent,options.sandboxRoot);let stat;try{stat=import_node_fs2.default.lstatSync(outputRoot)}catch(error){if(error.code==="ENOENT")return sandboxStat.dev;fail4(`could not inspect managed output root ${outputRoot}`)}if(stat.isSymbolicLink()||!stat.isDirectory()){fail4(`managed output root is unsafe: ${outputRoot}`)}if(expectedAgent!=="hermes"&&stat.dev!==sandboxStat.dev){fail4(`managed output root crosses a nested filesystem mount: ${outputRoot}`)}return stat.dev}function agentRoot(agent,sandboxRoot){switch(agent){case"openclaw":return import_node_path2.default.join(sandboxRoot,".openclaw");case"hermes":return import_node_path2.default.join(sandboxRoot,".hermes");case"langchain-deepagents-code":return import_node_path2.default.join(sandboxRoot,".deepagents");case"pi":return import_node_path2.default.join(sandboxRoot,".pi")}}function resolveUnderAgentRoot(root,relativePath){const safe=safeRelativePath(relativePath);const target=import_node_path2.default.resolve(root,safe);if(!target.startsWith(`${root}${import_node_path2.default.sep}`)){fail4(`managed output escapes the agent root: ${relativePath}`)}return target}function renderTarget(root,agent,target){if(agent==="openclaw"&&target==="openclaw.json"){return import_node_path2.default.join(root,"openclaw.json")}const prefix=agent==="openclaw"?"~/.openclaw/":agent==="hermes"?"~/.hermes/":null;if(!prefix||!target.startsWith(prefix)){fail4(`unsupported managed messaging render target ${JSON.stringify(target)}`)}return resolveUnderAgentRoot(root,target.slice(prefix.length))}function managedOutputTargets(profile,options){const root=agentRoot(profile.agent,options.sandboxRoot);const files=new Set;const directories=new Set([root]);switch(profile.agent){case"openclaw":files.add(import_node_path2.default.join(root,"openclaw.json"));files.add(import_node_path2.default.join(root,".config-hash"));break;case"hermes":files.add(import_node_path2.default.join(root,"config.yaml"));files.add(import_node_path2.default.join(root,".env"));files.add(import_node_path2.default.join(root,".config-hash"));break;case"langchain-deepagents-code":files.add(import_node_path2.default.join(root,"config.toml"));directories.add(import_node_path2.default.join(root,".state"));directories.add(import_node_path2.default.join(root,"skills"));break;case"pi":directories.add(import_node_path2.default.join(root,"agent"));files.add(import_node_path2.default.join(root,"agent","models.json"));break}if(profile.messaging.plan!==null){const plan=parseSandboxMessagingPlan(profile.messaging.plan,{agent:profile.agent});if(!plan)fail4("managed messaging plan is invalid");for(const render of selectEnabledMessagingAgentRender(plan)){if(typeof render.target!=="string")continue;files.add(renderTarget(root,profile.agent,render.target))}for(const step of selectEnabledPostAgentInstallBuildFiles(plan)){if(typeof step.value!=="object"||step.value===null){continue}const outputPath=step.value.path;if(typeof outputPath==="string"){files.add(resolveUnderAgentRoot(root,outputPath))}}}for(const file of files){let parent=import_node_path2.default.dirname(file);while(parent!==options.sandboxRoot&&parent.startsWith(`${root}${import_node_path2.default.sep}`)){directories.add(parent);if(parent===root)break;parent=import_node_path2.default.dirname(parent)}}return{files:[...files].sort(),directories:[...directories].sort((left,right)=>left.split(import_node_path2.default.sep).length-right.split(import_node_path2.default.sep).length)}}function snapshotFile(target,index,expectedAgent,options){validateExistingAncestors(target,expectedAgent,options);let stat;try{stat=import_node_fs2.default.lstatSync(target)}catch(error){if(error.code==="ENOENT"){return{receipt:{path:relativeTarget(target,options),state:"absent"},bytes:null}}fail4(`could not inspect managed output ${target}`)}if(stat.isSymbolicLink()||!stat.isFile()||stat.nlink!==1){fail4(`managed output is not a safe regular file: ${target}`)}if(stat.dev!==managedOutputDevice(expectedAgent,options)){fail4(`managed output crosses a nested filesystem mount: ${target}`)}const stable=readStableFile(target,MAX_TRANSACTION_FILE_BYTES);const size=Number(stable.stat.size);const backup=`${String(index).padStart(3,"0")}.bin`;return{receipt:{path:relativeTarget(target,options),state:"file",backup,sha256:(0,import_node_crypto5.createHash)("sha256").update(stable.bytes).digest("hex"),size,uid:Number(stable.stat.uid),gid:Number(stable.stat.gid),mode:Number(stable.stat.mode&0o7777n)},bytes:stable.bytes}}function snapshotDirectory(target,expectedAgent,options){validateExistingAncestors(import_node_path2.default.join(target,".receipt"),expectedAgent,options);let stat;try{stat=import_node_fs2.default.lstatSync(target)}catch(error){if(error.code==="ENOENT"){return{path:relativeTarget(target,options),state:"absent"}}fail4(`could not inspect managed output directory ${target}`)}if(stat.isSymbolicLink()||!stat.isDirectory()){fail4(`managed output directory is unsafe: ${target}`)}if(stat.dev!==managedOutputDevice(expectedAgent,options)){fail4(`managed output directory crosses a nested filesystem mount: ${target}`)}return{path:relativeTarget(target,options),state:"directory",uid:stat.uid,gid:stat.gid,mode:modeOf2(stat)}}function atomicWriteTrustedFile(target,contents,mode,uid,gid){const parent=import_node_path2.default.dirname(target);const temporary=import_node_path2.default.join(parent,`.${import_node_path2.default.basename(target)}.${(0,import_node_crypto5.randomBytes)(12).toString("hex")}`);let descriptor;try{descriptor=import_node_fs2.default.openSync(temporary,import_node_fs2.default.constants.O_CREAT|import_node_fs2.default.constants.O_EXCL|import_node_fs2.default.constants.O_WRONLY|import_node_fs2.default.constants.O_NOFOLLOW,384);import_node_fs2.default.writeFileSync(descriptor,contents);import_node_fs2.default.fchownSync(descriptor,uid,gid);import_node_fs2.default.fchmodSync(descriptor,mode);import_node_fs2.default.fsyncSync(descriptor);import_node_fs2.default.closeSync(descriptor);descriptor=void 0;import_node_fs2.default.renameSync(temporary,target)}catch(error){if(descriptor!==void 0)import_node_fs2.default.closeSync(descriptor);try{import_node_fs2.default.unlinkSync(temporary)}catch{}fail4(`could not atomically write ${target}: ${error.message}`)}}function fsyncDirectory(directory){const descriptor=import_node_fs2.default.openSync(directory,import_node_fs2.default.constants.O_RDONLY);try{import_node_fs2.default.fsyncSync(descriptor)}finally{import_node_fs2.default.closeSync(descriptor)}}function canonicalManifest(manifest){return`${JSON.stringify(manifest,null,2)} +var __create=Object.create;var __defProp=Object.defineProperty;var __getOwnPropDesc=Object.getOwnPropertyDescriptor;var __getOwnPropNames=Object.getOwnPropertyNames;var __getProtoOf=Object.getPrototypeOf;var __hasOwnProp=Object.prototype.hasOwnProperty;var __export=(target,all)=>{for(var name in all)__defProp(target,name,{get:all[name],enumerable:true})};var __copyProps=(to,from,except,desc)=>{if(from&&typeof from==="object"||typeof from==="function"){for(let key of __getOwnPropNames(from))if(!__hasOwnProp.call(to,key)&&key!==except)__defProp(to,key,{get:()=>from[key],enumerable:!(desc=__getOwnPropDesc(from,key))||desc.enumerable})}return to};var __toESM=(mod,isNodeMode,target)=>(target=mod!=null?__create(__getProtoOf(mod)):{},__copyProps(isNodeMode||!mod||!mod.__esModule?__defProp(target,"default",{value:mod,enumerable:true}):target,mod));var __toCommonJS=mod=>__copyProps(__defProp({},"__esModule",{value:true}),mod);var image_runtime_exports={};__export(image_runtime_exports,{applyManagedBootstrapEnvelope:()=>applyManagedBootstrapEnvelope,main:()=>main2,managedBootstrapEnvelopeClaimPaths:()=>managedBootstrapEnvelopeClaimPaths,readManagedBootstrapEnvelope:()=>readManagedBootstrapEnvelope,recoverManagedBootstrapEnvelopeClaim:()=>recoverManagedBootstrapEnvelopeClaim,verifyManagedBootstrapImageCompletion:()=>verifyManagedBootstrapImageCompletion,waitForManagedBootstrapImageCompletion:()=>waitForManagedBootstrapImageCompletion});module.exports=__toCommonJS(image_runtime_exports);var import_node_fs4=__toESM(require("node:fs"));var import_node_path4=__toESM(require("node:path"));var import_node_child_process=require("node:child_process");var import_node_crypto6=require("node:crypto");var import_node_fs3=__toESM(require("node:fs"));var import_node_path3=__toESM(require("node:path"));var import_node_util3=require("node:util");var MAX_CORPORATE_CA_BYTES=128*1024;var PEM_CERTIFICATE_RE_GLOBAL=/-----BEGIN CERTIFICATE-----[\s\S]+?-----END CERTIFICATE-----/g;var import_node_buffer=require("node:buffer");var HERMES_SWITCHYARD_PLUGIN_MANIFEST="/opt/switchyard-relay-plugin/relay-plugin.toml";var HERMES_SWITCHYARD_RELAY_TOML="/usr/local/share/nemoclaw/hermes-relay-plugins.toml";var HERMES_SWITCHYARD_RUNTIME_BINDINGS="/usr/local/share/nemoclaw/hermes-switchyard-runtime-bindings.json";var HERMES_SWITCHYARD_TARGET_ROLES=["judge","weak","strong"];var HermesSwitchyardRoutingError=class extends Error{constructor(message){super(`Invalid Hermes Switchyard routing: ${message}`);this.name="HermesSwitchyardRoutingError"}};var ROUTING_KEYS=new Set(["algorithm","baseThreshold","targets"]);var TARGET_KEYS=new Set(["role","baseUrl","model","protocol","headerEnv"]);var HEADER_ENV_KEYS=new Set(["headerName","envKey"]);var ROLE_SET=new Set(HERMES_SWITCHYARD_TARGET_ROLES);var CONTROL_CHARACTER_RE=/[\u0000-\u001f\u007f-\u009f]/u;var HEADER_NAME_RE=/^[!#$%&'*+.^_`|~0-9A-Za-z-]{1,128}$/u;var ALLOWED_PROVIDER_HEADER_NAMES=new Set(["api-key","authorization","x-api-key"]);var HEADER_ENV_KEY_RE=/^SWITCHYARD_[A-Z][A-Z0-9_]{0,111}$/u;var MAX_MODEL_BYTES=1024;var MAX_URL_BYTES=2048;var MAX_HEADER_ENV_BINDINGS=8;function fail(message){throw new HermesSwitchyardRoutingError(message)}function isPlainObject(value){if(value===null||typeof value!=="object"||Array.isArray(value))return false;const prototype=Object.getPrototypeOf(value);return prototype===Object.prototype||prototype===null}function record(value,where){if(!isPlainObject(value))fail(`${where} must be an object`);return value}function rejectUnknownKeys(value,allowed,where){if(Object.keys(value).some(key=>!allowed.has(key))){fail(`${where} contains unsupported fields`)}}function boundedString(value,where,maxBytes){if(typeof value!=="string"||value.length===0||value!==value.trim()||import_node_buffer.Buffer.byteLength(value,"utf8")>maxBytes||CONTROL_CHARACTER_RE.test(value)){fail(`${where} must be bounded non-empty text without control characters`)}return value}function httpsBaseUrl(value,where){const raw=boundedString(value,where,MAX_URL_BYTES);let parsed;try{parsed=new URL(raw)}catch{fail(`${where} must be a valid HTTPS URL`)}if(parsed.protocol!=="https:"||parsed.username!==""||parsed.password!==""||parsed.search!==""||parsed.hash!==""){fail(`${where} must be a credential-free HTTPS URL without query or fragment data`)}const pathname=parsed.pathname.replace(/\/+$/u,"");return pathname===""?parsed.origin:`${parsed.origin}${pathname}`}function validateHeaderEnvironment(value,targetWhere){if(!Array.isArray(value)||value.length<1||value.length>MAX_HEADER_ENV_BINDINGS){fail(`${targetWhere}.headerEnv must contain 1-${String(MAX_HEADER_ENV_BINDINGS)} bindings`)}const seenHeaders=new Set;const seenEnvironmentKeys=new Set;const bindings=value.map((candidate,index)=>{const where=`${targetWhere}.headerEnv[${String(index)}]`;const binding=record(candidate,where);rejectUnknownKeys(binding,HEADER_ENV_KEYS,where);const rawHeaderName=boundedString(binding.headerName,`${where}.headerName`,128);if(!HEADER_NAME_RE.test(rawHeaderName))fail(`${where}.headerName is not a safe HTTP header`);const headerName=rawHeaderName.toLowerCase();if(!ALLOWED_PROVIDER_HEADER_NAMES.has(headerName)){fail(`${where}.headerName is not an allowed provider credential header`)}const envKey=boundedString(binding.envKey,`${where}.envKey`,128);if(!HEADER_ENV_KEY_RE.test(envKey)){fail(`${where}.envKey must be a SWITCHYARD_ prefixed environment key`)}if(seenHeaders.has(headerName))fail(`${targetWhere}.headerEnv contains duplicate headers`);if(seenEnvironmentKeys.has(envKey)){fail(`${targetWhere}.headerEnv contains duplicate environment keys`)}seenHeaders.add(headerName);seenEnvironmentKeys.add(envKey);return{headerName,envKey}});return bindings.sort((left,right)=>left.headerNameright.headerName?1:0)}function validateTarget(value,index){const where=`targets[${String(index)}]`;const target=record(value,where);rejectUnknownKeys(target,TARGET_KEYS,where);const role=boundedString(target.role,`${where}.role`,16);if(!ROLE_SET.has(role))fail(`${where}.role is not supported`);const model=boundedString(target.model,`${where}.model`,MAX_MODEL_BYTES);if(target.protocol!=="openai_chat")fail(`${where}.protocol must be openai_chat`);return{role,baseUrl:httpsBaseUrl(target.baseUrl,`${where}.baseUrl`),model,protocol:"openai_chat",headerEnv:validateHeaderEnvironment(target.headerEnv,where)}}function validateHermesSwitchyardRouting(value){const routing=record(value,"routing");rejectUnknownKeys(routing,ROUTING_KEYS,"routing");if(routing.algorithm!=="llm_classifier"){fail("algorithm must be llm_classifier")}if(typeof routing.baseThreshold!=="number"||!Number.isFinite(routing.baseThreshold)||routing.baseThreshold<0||routing.baseThreshold>1){fail("baseThreshold must be a finite number from 0 through 1")}if(!Array.isArray(routing.targets)||routing.targets.length!==3){fail("targets must contain exactly judge, weak, and strong")}const byRole=new Map;const modelIds=new Set;const baseUrls=new Set;const environmentKeys=new Set;for(let index=0;index{const target=byRole.get(role);if(!target)fail(`targets is missing role ${role}`);return target});return{algorithm:"llm_classifier",baseThreshold:routing.baseThreshold,targets}}function tomlString(value){return JSON.stringify(value)}function serializeHermesSwitchyardRuntimeBindings(value){const routing=validateHermesSwitchyardRouting(value);return`${JSON.stringify({schemaVersion:1,targets:routing.targets.map(({headerEnv,role})=>({headerEnv:headerEnv.map(({envKey,headerName})=>({envKey,headerName})),role}))})} +`}function parseHermesSwitchyardRelayToml(source){if(source.length===0||source.includes("\r")||!source.endsWith("\n")){fail("Relay TOML must be non-empty canonical UTF-8 text ending in one newline")}const sections=new Map;let sectionName="";sections.set(sectionName,new Map);for(const[index,line]of source.slice(0,-1).split("\n").entries()){if(line==="")continue;const arrayTable=line.match(/^\[\[([A-Za-z0-9_.-]+)\]\]$/u);const table=line.match(/^\[([A-Za-z0-9_.-]+)\]$/u);if(arrayTable){const base=arrayTable[1];let instance=0;while(sections.has(`${base}#${String(instance)}`))instance+=1;sectionName=`${base}#${String(instance)}`;sections.set(sectionName,new Map);continue}if(table){sectionName=table[1];if(sections.has(sectionName))fail(`Relay TOML repeats table ${sectionName}`);sections.set(sectionName,new Map);continue}const assignment=line.match(/^((?:[A-Za-z_][A-Za-z0-9_-]*)|(?:"(?:[^"\\]|\\.)+")) = (.+)$/u);if(!assignment)fail(`Relay TOML has unsupported syntax on line ${String(index+1)}`);const rawKey=assignment[1];const rawValue=assignment[2];let key;try{key=rawKey.startsWith('"')?JSON.parse(rawKey):rawKey}catch{fail(`Relay TOML has an invalid quoted key on line ${String(index+1)}`)}let parsed;if(rawValue.startsWith('"')){try{parsed=JSON.parse(rawValue)}catch{fail(`Relay TOML has an invalid string on line ${String(index+1)}`)}if(typeof parsed!=="string")fail(`Relay TOML string is malformed on line ${String(index+1)}`)}else if(rawValue==="true"||rawValue==="false"){parsed=rawValue==="true"}else if(/^-?(?:0|[1-9][0-9]*)(?:\.[0-9]+)?(?:[eE][+-]?[0-9]+)?$/u.test(rawValue)){parsed=Number(rawValue);if(!Number.isFinite(parsed))fail(`Relay TOML number is invalid on line ${String(index+1)}`)}else{fail(`Relay TOML has an unsupported value on line ${String(index+1)}`)}const section=sections.get(sectionName);if(!section)fail("Relay TOML parser lost its current table");if(section.has(key))fail(`Relay TOML repeats key ${key} in ${sectionName}`);section.set(key,parsed)}return sections}function serializeHermesSwitchyardRelayToml(value){const routing=validateHermesSwitchyardRouting(value);const lines=["version = 1","","[[plugins.dynamic]]",`manifest = ${tomlString(HERMES_SWITCHYARD_PLUGIN_MANIFEST)}`,"","[plugins.dynamic.config]","version = 2","priority = 0","max_retries = 3",'failure_mode = "fail_closed"',"","[plugins.dynamic.config.algorithm]",'kind = "llm_classifier"','mode = "capability"','classifier_target = "judge"','weak_target = "weak"','strong_target = "strong"',`base_threshold = ${String(routing.baseThreshold)}`,"","[plugins.dynamic.config.default_targets]",'openai_chat = "weak"'];for(const target of routing.targets){const prefix=`plugins.dynamic.config.targets.${target.role}`;lines.push("",`[${prefix}]`,`model = ${tomlString(target.model)}`,`protocol = ${tomlString(target.protocol)}`,'endpoint = "/v1/chat/completions"',`base_url = ${tomlString(target.baseUrl)}`,"weight = 1","drop_caller_extra_body = true","",`[${prefix}.header_env]`,...target.headerEnv.map(({headerName,envKey})=>`${tomlString(headerName)} = ${tomlString(envKey)}`))}return`${lines.join("\n")} +`}var import_node_buffer3=require("node:buffer");function isObjectRecord(value){return typeof value==="object"&&value!==null&&!Array.isArray(value)}var ChannelManifestRegistry=class{manifests=new Map;constructor(manifests=[]){for(const manifest of manifests){this.register(manifest)}}register(manifest){if(this.manifests.has(manifest.id)){throw new Error(`Duplicate channel manifest id '${manifest.id}'`)}this.manifests.set(manifest.id,manifest);return this}get(channelId){return this.manifests.get(channelId)}list(){return Array.from(this.manifests.values())}listAvailable(ctx={}){const supportedChannelIds=Array.isArray(ctx.supportedChannelIds)?new Set(ctx.supportedChannelIds):null;return this.list().filter(manifest=>{if(ctx.agent&&!manifest.supportedAgents.includes(ctx.agent)){return false}if(supportedChannelIds&&!supportedChannelIds.has(manifest.id)){return false}return true})}};function createChannelManifestRegistry(manifests=[]){return new ChannelManifestRegistry(manifests)}var discordManifest={schemaVersion:1,id:"discord",displayName:"Discord",description:"Discord bot messaging",supportedAgents:["openclaw","hermes"],auth:{mode:"token-paste"},inputs:[{id:"botToken",kind:"secret",required:true,envKey:"DISCORD_BOT_TOKEN",prompt:{label:"Discord Bot Token",help:"Discord Developer Portal \u2192 Applications \u2192 Bot \u2192 Reset/Copy Token."}},{id:"serverId",kind:"config",required:false,envKey:"DISCORD_SERVER_ID",statePath:"discordGuilds.serverId",prompt:{label:"Discord Server ID (for guild workspace access)",help:"Enable Developer Mode in Discord, then right-click your server and copy the Server ID.",emptyValueMessage:"guild channels stay disabled"}},{id:"requireMention",kind:"config",required:false,envKey:"DISCORD_REQUIRE_MENTION",statePath:"discordGuilds.requireMention",promptWhenInput:"serverId",validValues:["0","1"],defaultValue:"1",prompt:{label:"Discord mention mode",help:"Choose whether the bot should reply only when @mentioned or to all messages in this server."}},{id:"userId",kind:"config",required:false,envKey:"DISCORD_USER_ID",statePath:"discordGuilds.userIds",promptWhenInput:"serverId",prompt:{label:"Discord User ID (optional guild allowlist)",help:"Optional: enable Developer Mode in Discord, then right-click your user/avatar and copy the User ID. Leave blank to allow any member of the configured server to message the bot.",emptyValueMessage:"any member in the configured server can message the bot"}}],credentials:[{id:"discordBotToken",sourceInput:"botToken",providerName:"{sandboxName}-discord-bridge",providerEnvKey:"DISCORD_BOT_TOKEN",placeholder:"openshell:resolve:env:DISCORD_BOT_TOKEN"}],policyPresets:[{name:"discord",validationWarningLines:["For Discord preset validation, do not use curl as the success signal:","curl is not in the preset binary allowlist, so curl probes can fail even","when the policy is working. Use Node HTTPS against","https://discord.com/api/v10/gateway or validate the configured",'messaging bridge/gateway path. DNS-only checks such as dns.resolve("gateway.discord.gg")',"can also be inconclusive behind a proxy."]}],render:[{id:"discord-openclaw-channel",kind:"json-fragment",agent:"openclaw",target:"openclaw.json",fragment:{path:"channels.discord",value:{enabled:true,accounts:{default:{token:"{{credential.discordBotToken.placeholder}}",enabled:true,healthMonitor:{enabled:false},proxy:"{{discordProxyUrl}}",dmPolicy:"{{discord.allowedUsers.dmPolicy}}",allowFrom:"{{discord.allowedUsers.values}}"}}}}},{id:"discord-openclaw-guilds",kind:"json-fragment",agent:"openclaw",target:"openclaw.json",when:"{{discord.hasGuilds}}",fragment:{path:"channels.discord",value:{groupPolicy:"allowlist",guilds:"{{discord.guilds}}"}}},{id:"discord-openclaw-plugin",kind:"json-fragment",agent:"openclaw",target:"openclaw.json",fragment:{path:"plugins.entries.discord",value:{enabled:true}}},{id:"discord-hermes-env",kind:"env-lines",agent:"hermes",target:"~/.hermes/.env",lines:["DISCORD_BOT_TOKEN={{credential.discordBotToken.placeholder}}","NEMOCLAW_DISCORD_GUILD_IDS={{discord.guildIds.csv}}","DISCORD_ALLOWED_USERS={{discord.allowedUsers.csv}}","DISCORD_ALLOW_ALL_USERS={{discord.allowAllUsers}}"]},{id:"discord-hermes-config",kind:"json-fragment",agent:"hermes",target:"~/.hermes/config.yaml",fragment:{path:"discord",value:{require_mention:"{{discord.requireMention}}",free_response_channels:"",allowed_channels:"",auto_thread:true,reactions:true,channel_prompts:{}}}},{id:"discord-hermes-platform",kind:"json-fragment",agent:"hermes",target:"~/.hermes/config.yaml",fragment:{path:"platforms.discord",value:{enabled:true}}}],runtime:{openclaw:{channelName:"discord",visibility:{configKeys:["discord"],logPatterns:["discord"]}}},agentPackages:[{id:"openclawPluginPackage",agent:"openclaw",manager:"openclaw-plugin",spec:"npm:@openclaw/discord@{{openclaw.version}}",pin:true,integrityByVersion:{"2026.7.1":"sha512-tZfdC1YA8oVLvc2BK1w0F6rUljS5ugCOp2uWe0vPsbG1fbzVVIO4V32RoqZznGHe5u2R9u4n1aV5Z/qa1m2oFg=="},tarballUrlByVersion:{"2026.7.1":"https://registry.npmjs.org/@openclaw/discord/-/discord-2026.7.1.tgz"},required:true}],hooks:[{id:"discord-openclaw-bridge-health",phase:"health-check",handler:"discord.openclawBridgeHealth",agents:["openclaw"],onFailure:"abort"},{id:"discord-token-paste",phase:"enroll",handler:"common.tokenPaste",outputs:[{id:"botToken",kind:"secret",required:true}],onFailure:"skip-channel"},{id:"discord-config-prompt",phase:"enroll",handler:"common.configPrompt",outputs:[{id:"serverId",kind:"config"},{id:"requireMention",kind:"config"},{id:"userId",kind:"config"}]}]};var googlechatManifest={schemaVersion:1,id:"googlechat",displayName:"Google Chat",description:"Google Chat (Chat API) bot messaging (experimental)",supportedAgents:["openclaw","hermes"],auth:{mode:"token-paste"},inputs:[{id:"serviceAccount",kind:"secret",required:true,envKey:"GOOGLECHAT_SERVICE_ACCOUNT",maskCap:40,formatHint:"Paste the entire service-account JSON key on one line (minified) \u2014 the whole downloaded JSON file.",maxTokenAttempts:3,prompt:{label:"Google Chat service account JSON",help:["\u2503 GOOGLE CHAT \u2014 service account key","\u2503","\u2503 Google Cloud Console \u2192 IAM & Admin \u2192 Service Accounts","\u2503 \u2192 your bot's SA \u2192 Keys \u2192 Add key \u2192 Create new key \u2192 JSON","\u2503","\u2503 A .json file downloads. Paste its contents below as ONE line (minified).",""].join("\n")}},{id:"audienceType",kind:"config",required:false,envKey:"GOOGLECHAT_AUDIENCE_TYPE",statePath:"googlechatConfig.audienceType",validValues:["app-url","project-number"],defaultValue:"app-url"},{id:"audience",kind:"config",required:false,envKey:"GOOGLECHAT_AUDIENCE",statePath:"googlechatConfig.audience",prompt:{label:"Google Chat webhook audience",help:"Usually filled automatically from the public tunnel URL. For audienceType 'project-number', enter your GCP project number instead.",emptyValueMessage:"inbound webhook verification will be unconfigured"}},{id:"appPrincipal",kind:"config",required:false,envKey:"GOOGLECHAT_APP_PRINCIPAL",statePath:"googlechatConfig.appPrincipal",formatPattern:"^[0-9]{6,32}$",formatHint:"appPrincipal is the add-on's numeric OAuth client ID (uniqueId, ~21 digits), not an email.",prompt:{label:"Google Chat appPrincipal",help:[" Workspace account \u2192 leave blank, done."," Personal Gmail \u2192 needs the add-on's ~21-digit ID (not an email), stable across rebuilds.",""," If you already know it, paste it at the prompt and you're done."," If not, leave it blank \u2014 the first DM reveals it once the sandbox is live:",""," 1. Watch the gateway log:",' nemoclaw logs --follow | grep "unexpected add-on principal"'," 2. DM the bot once \u2014 it won't reply yet, that's expected. The log prints:"," unexpected add-on principal: "," 3. Save that and rebuild:"," GOOGLECHAT_APP_PRINCIPAL= nemoclaw channels add googlechat"," nemoclaw rebuild --yes"].join("\n"),emptyValueMessage:"Workspace accounts do not need it; personal accounts must set it later"}},{id:"allowFrom",kind:"config",required:false,envKey:"GOOGLECHAT_ALLOWED_USERS",statePath:"allowedIds.googlechat",prompt:{label:"Google Chat DM allowlist (comma-separated)",help:["Optional: restrict who can DM the bot."," OpenClaw: users/NNN (emails ignored)"," Hermes: email (users/NNN ignored)"," Blank: pairing mode (recommended) \u2014 OpenClaw's pairing reply shows your users/NNN"," Filling this switches DM policy to allowlist \u2014 a wrong-form entry is dropped silently, with no pairing code."].join("\n"),emptyValueMessage:"bot will require manual pairing"}},{id:"projectId",kind:"config",required:false,envKey:"GOOGLE_CHAT_PROJECT_ID",statePath:"googlechatConfig.projectId",prompt:{label:"Google Chat GCP project ID (Hermes Pub/Sub pull)",help:"The Google Cloud project that owns the Pub/Sub subscription Hermes pulls Chat events from. OpenClaw ignores this.",emptyValueMessage:"required for the Hermes Google Chat channel"}},{id:"subscriptionName",kind:"config",required:false,envKey:"GOOGLE_CHAT_SUBSCRIPTION_NAME",statePath:"googlechatConfig.subscriptionName",prompt:{label:"Google Chat Pub/Sub subscription (projects/

/subscriptions/)",help:["The pull subscription bound to the Chat events topic. Hermes pulls from it over the Pub/Sub REST API; the gateway-minted token is scoped to both chat.bot and pubsub."," Its topic must grant roles/pubsub.publisher to the app's push account:"," Interactive features service-@gcp-sa-gsuiteaddons.iam.gserviceaccount.com"," Classic bot chat-api-push@system.gserviceaccount.com"," Shown at Chat API \u2192 Configuration \u2192 Connection settings"," Missing it channel connects, no event arrives, Chat says the bot is not responding"].join("\n"),emptyValueMessage:"required for the Hermes Google Chat channel"}}],credentials:[],policyPresets:[{name:"googlechat",policyKeys:["googlechat"],agentPolicyKeys:{hermes:["googlechat_hermes"]}}],render:[{id:"googlechat-openclaw-channel",kind:"json-fragment",agent:"openclaw",target:"openclaw.json",fragment:{path:"channels.googlechat",value:{enabled:true,serviceAccountFile:"/nonexistent/googlechat-gateway-minted-no-service-account-file",audienceType:"{{googlechatConfig.audienceType}}",audience:"{{googlechatConfig.audience}}",appPrincipal:"{{googlechatConfig.appPrincipal}}",webhookPath:"/googlechat",healthMonitor:{enabled:false},dm:{policy:"{{allowedIds.googlechat.dmPolicy}}",allowFrom:"{{allowedIds.googlechat.values}}"}}}},{id:"googlechat-openclaw-plugin",kind:"json-fragment",agent:"openclaw",target:"openclaw.json",fragment:{path:"plugins.entries.googlechat",value:{enabled:true}}},{id:"googlechat-openclaw-gateway-reload-off",kind:"json-fragment",agent:"openclaw",target:"openclaw.json",fragment:{path:"gateway.reload",value:{mode:"off"}}},{id:"googlechat-hermes-env",kind:"env-lines",agent:"hermes",target:"~/.hermes/.env",lines:["GOOGLE_CHAT_PROJECT_ID={{googlechatConfig.projectId}}","GOOGLE_CHAT_SUBSCRIPTION_NAME={{googlechatConfig.subscriptionName}}","GOOGLE_CHAT_ALLOWED_USERS={{allowedIds.googlechat.csv}}"]},{id:"googlechat-hermes-platform",kind:"json-fragment",agent:"hermes",target:"~/.hermes/config.yaml",fragment:{path:"platforms.google_chat",value:{enabled:true}}}],runtime:{openclaw:{channelName:"googlechat",visibility:{configKeys:["googlechat"],logPatterns:["googlechat"]},nodePreloads:[{module:"googlechat-trusted-proxy-fetch",injectInto:["boot"],optional:false,installMessage:"[channels] Installing Google Chat trusted-proxy-fetch patch (route googleapis via trusted env proxy)",installedMessage:"[channels] Google Chat trusted-proxy-fetch patch installed (NODE_OPTIONS updated)"},{module:"googlechat-outbound-auth",injectInto:["boot"],optional:false,installMessage:"[channels] Installing Google Chat outbound-auth patch (gateway-minted bearer)",installedMessage:"[channels] Google Chat outbound-auth patch installed (NODE_OPTIONS updated)"}],secretScans:[{path:"/sandbox/.openclaw/openclaw.json",pattern:"-----BEGIN (?:RSA )?PRIVATE KEY-----",message:"[SECURITY] Google Chat service account private key leaked into {path} - refusing to serve",exitCode:78}]}},agentPackages:[{id:"openclawPluginPackage",agent:"openclaw",manager:"openclaw-plugin",spec:"npm:@openclaw/googlechat@{{openclaw.version}}",pin:true,integrityByVersion:{"2026.7.1":"sha512-Dv0xOmcxAThEr6hoK+ioofHNu18hfbIceQrEHX3AHZPpOUiTJvToVpA5eX87NQINewwfSJf0gVhE6kSbSk2Aew=="},tarballUrlByVersion:{"2026.7.1":"https://registry.npmjs.org/@openclaw/googlechat/-/googlechat-2026.7.1.tgz"},required:true},{id:"hermesGooglePubsubPackage",agent:"hermes",manager:"hermes-uv-pip",spec:"google-cloud-pubsub==2.39.0",required:true},{id:"hermesGoogleApiClientPackage",agent:"hermes",manager:"hermes-uv-pip",spec:"google-api-python-client==2.194.0",required:true},{id:"hermesGoogleAuthPackage",agent:"hermes",manager:"hermes-uv-pip",spec:"google-auth==2.55.1",required:true}],hooks:[{id:"googlechat-tunnel-audience-gate",phase:"enroll",handler:"googlechat.tunnelAudienceGate",agents:["openclaw"],inputs:["audienceType","audience"],outputs:[{id:"audience",kind:"config"}],onFailure:"skip-channel"},{id:"googlechat-service-account",phase:"enroll",handler:"googlechat.tokenPaste",outputs:[{id:"serviceAccount",kind:"secret",required:true}],onFailure:"skip-channel"},{id:"googlechat-config-prompt",phase:"enroll",handler:"common.configPrompt",outputs:[{id:"allowFrom",kind:"config"}]},{id:"googlechat-openclaw-config-prompt",phase:"enroll",handler:"common.configPrompt",agents:["openclaw"],outputs:[{id:"appPrincipal",kind:"config"}]},{id:"googlechat-hermes-config-prompt",phase:"enroll",handler:"common.configPrompt",agents:["hermes"],outputs:[{id:"projectId",kind:"config"},{id:"subscriptionName",kind:"config"}]}]};var slackRuntimeEnvAliases=[{envKey:"SLACK_BOT_TOKEN",match:"^openshell:resolve:env:(v[0-9]+_)?SLACK_BOT_TOKEN$",value:"xoxb-OPENSHELL-RESOLVE-ENV-SLACK_BOT_TOKEN",message:"[channels] Normalized SLACK_BOT_TOKEN runtime placeholder to the Bolt-compatible alias"},{envKey:"SLACK_APP_TOKEN",match:"^openshell:resolve:env:(v[0-9]+_)?SLACK_APP_TOKEN$",value:"xapp-OPENSHELL-RESOLVE-ENV-SLACK_APP_TOKEN",message:"[channels] Normalized SLACK_APP_TOKEN runtime placeholder to the Bolt-compatible alias"}];var slackManifest={schemaVersion:1,id:"slack",displayName:"Slack",description:"Slack bot messaging",supportedAgents:["openclaw","hermes"],auth:{mode:"token-paste"},inputs:[{id:"botToken",kind:"secret",required:true,envKey:"SLACK_BOT_TOKEN",formatPattern:"^xoxb-[A-Za-z0-9_-]+$",formatHint:"Slack bot tokens start with 'xoxb-' (e.g. xoxb---).",prompt:{label:"Slack Bot Token",help:"Slack API \u2192 Your Apps \u2192 OAuth & Permissions \u2192 Bot User OAuth Token (xoxb-...)."}},{id:"appToken",kind:"secret",required:true,envKey:"SLACK_APP_TOKEN",formatPattern:"^xapp-[A-Za-z0-9_-]+$",formatHint:"Slack app tokens start with 'xapp-' (e.g. xapp----).",prompt:{label:"Slack App Token (Socket Mode)",help:"Slack API \u2192 Your Apps \u2192 Basic Information \u2192 App-Level Tokens (xapp-...)."}},{id:"allowedUsers",kind:"config",required:false,envKey:"SLACK_ALLOWED_USERS",statePath:"allowedIds.slack",prompt:{label:"Slack Member IDs (comma-separated allowlist)",help:"In Slack, open each allowed human user's profile -> More -> Copy member ID. Enter one or more comma-separated member IDs, not the app or bot user ID. Member IDs look like U01ABC2DEF3.",emptyValueMessage:"bot will require manual pairing"}},{id:"allowedChannels",kind:"config",required:false,envKey:"SLACK_ALLOWED_CHANNELS",statePath:"slackConfig.allowedChannels",prompt:{label:"Slack Channel IDs (comma-separated allowlist)",help:"Optional: enter comma-separated Slack channel IDs where the bot may answer @mentions. Channel IDs look like C012AB3CD.",emptyValueMessage:"channel @mentions stay unrestricted by channel ID"}}],credentials:[{id:"slackBotToken",sourceInput:"botToken",providerName:"{sandboxName}-slack-bridge",providerEnvKey:"SLACK_BOT_TOKEN",placeholder:"xoxb-OPENSHELL-RESOLVE-ENV-SLACK_BOT_TOKEN",primary:true},{id:"slackAppToken",sourceInput:"appToken",providerName:"{sandboxName}-slack-app",providerEnvKey:"SLACK_APP_TOKEN",placeholder:"xapp-OPENSHELL-RESOLVE-ENV-SLACK_APP_TOKEN"}],policyPresets:[{name:"slack",requiredAtCreate:true}],render:[{id:"slack-openclaw-channel",kind:"json-fragment",agent:"openclaw",target:"openclaw.json",fragment:{path:"channels.slack",value:{enabled:true,accounts:{default:{botToken:"{{credential.slackBotToken.placeholder}}",appToken:"{{credential.slackAppToken.placeholder}}",enabled:true,healthMonitor:{enabled:false},dmPolicy:"{{allowedIds.slack.dmPolicy}}",allowFrom:"{{allowedIds.slack.values}}",groupPolicy:"{{allowedIds.slack.groupPolicy}}",channels:"{{allowedIds.slack.channels}}"}}}}},{id:"slack-openclaw-plugin",kind:"json-fragment",agent:"openclaw",target:"openclaw.json",fragment:{path:"plugins.entries.slack",value:{enabled:true}}},{id:"slack-hermes-env",kind:"env-lines",agent:"hermes",target:"~/.hermes/.env",lines:["SLACK_BOT_TOKEN={{credential.slackBotToken.placeholder}}","SLACK_APP_TOKEN={{credential.slackAppToken.placeholder}}","SLACK_ALLOWED_USERS={{allowedIds.slack.csv}}","SLACK_ALLOWED_CHANNELS={{slackConfig.allowedChannels.csv}}"]},{id:"slack-hermes-platform",kind:"json-fragment",agent:"hermes",target:"~/.hermes/config.yaml",fragment:{path:"platforms.slack",value:{enabled:true,extra:{rich_blocks:true}}}}],runtime:{openclaw:{channelName:"slack",visibility:{configKeys:["slack"],logPatterns:["slack"]},envAliases:slackRuntimeEnvAliases,nodePreloads:[{module:"slack-channel-guard",injectInto:["boot","connect"],optional:false,installMessage:"[channels] Installing Slack channel guard (unhandled-rejection safety net)",installedMessage:"[channels] Slack channel guard installed (NODE_OPTIONS updated)"}],secretScans:[{path:"/sandbox/.openclaw/openclaw.json",pattern:"(?:xoxb|xapp)-(?!OPENSHELL-RESOLVE-ENV-)",message:"[SECURITY] Slack token leaked into {path} - refusing to serve",exitCode:78}]},hermes:{envAliases:slackRuntimeEnvAliases}},agentPackages:[{id:"openclawPluginPackage",agent:"openclaw",manager:"openclaw-plugin",spec:"npm:@openclaw/slack@{{openclaw.version}}",pin:true,integrityByVersion:{"2026.7.1":"sha512-dwVGEVCmoTQrOIeZaSCIOPg8pT7hB883QQEXdp9EZUDzTGuvSc+KxH2iERSOV/59hROQctYdcobGn/vdB1H4XA=="},tarballUrlByVersion:{"2026.7.1":"https://registry.npmjs.org/@openclaw/slack/-/slack-2026.7.1.tgz"},required:true}],hooks:[{id:"slack-socket-mode-gateway-conflict",phase:"pre-enable",handler:"slack.socketModeGatewayConflict",onFailure:"abort"},{id:"slack-openclaw-bridge-health",phase:"health-check",handler:"slack.openclawBridgeHealth",agents:["openclaw"],onFailure:"abort"},{id:"slack-socket-mode-gateway-status",phase:"status",handler:"slack.socketModeGatewayStatus",outputs:[{id:"gatewayOverlaps",kind:"status"}]},{id:"slack-status-health",phase:"status",handler:"slack.statusHealth",providesReadiness:true,agents:["openclaw"],outputs:[{id:"channelHealth",kind:"status"}]},{id:"slack-token-paste",phase:"enroll",handler:"common.tokenPaste",outputs:[{id:"botToken",kind:"secret",required:true},{id:"appToken",kind:"secret",required:true}],onFailure:"skip-channel"},{id:"slack-config-prompt",phase:"enroll",handler:"common.configPrompt",outputs:[{id:"allowedUsers",kind:"config"},{id:"allowedChannels",kind:"config"}]},{id:"slack-credential-validation",phase:"reachability-check",handler:"slack.validateCredentials",inputs:["botToken","appToken"],onFailure:"skip-channel"}]};var TEAMS_OPENCLAW_WEBHOOK_RENDER_CONTRACT={channelId:"teams",renderId:"teams-openclaw-channel",hookId:"teams-openclaw-channel",handlerId:"common.staticOutputs",kind:"json-fragment",agent:"openclaw",target:"openclaw.json",configPath:"channels.msteams",webhookPath:"/api/messages"};function authorizeTeamsOpenClawWebhookField(entry){if(!isPlainDataObject(entry))return[];const contract=TEAMS_OPENCLAW_WEBHOOK_RENDER_CONTRACT;if(ownDataPropertyValue(entry,"channelId")!==contract.channelId||ownDataPropertyValue(entry,"renderId")!==contract.renderId||ownDataPropertyValue(entry,"hookId")!==contract.hookId||ownDataPropertyValue(entry,"handler")!==contract.handlerId||ownDataPropertyValue(entry,"kind")!==contract.kind||ownDataPropertyValue(entry,"agent")!==contract.agent||ownDataPropertyValue(entry,"target")!==contract.target||ownDataPropertyValue(entry,"path")!==contract.configPath){return[]}const value=ownDataPropertyValue(entry,"value");if(!isPlainDataObject(value))return[];const webhook=ownDataPropertyValue(value,"webhook");if(!isPlainDataObject(webhook)||!hasExactlyOwnDataProperties(webhook,["path","port"])||!isTcpPort(ownDataPropertyValue(webhook,"port"))||ownDataPropertyValue(webhook,"path")!==contract.webhookPath){return[]}return[{path:["value","webhook"],value:webhook}]}function isPlainDataObject(value){if(value===null||typeof value!=="object"||Array.isArray(value))return false;const prototype=Object.getPrototypeOf(value);return prototype===Object.prototype||prototype===null}function ownDataPropertyValue(value,key){const descriptor=Object.getOwnPropertyDescriptor(value,key);return descriptor&&"value"in descriptor?descriptor.value:void 0}function hasExactlyOwnDataProperties(value,expected){const actual=Object.getOwnPropertyNames(value).sort();return actual.length===expected.length&&actual.every((key,index)=>key===expected[index])}function isTcpPort(value){return Number.isInteger(value)&&value>=1&&value<=65535}var teamsManifest={schemaVersion:1,id:"teams",displayName:"Microsoft Teams",description:"Microsoft Teams bot messaging (experimental)",enrollmentNotes:["Microsoft Teams requires a public HTTPS webhook endpoint at /api/messages; expose the configured Teams webhook port before installing the Teams app.","Use Azure AD object IDs in TEAMS_ALLOWED_USERS so only authorized users can interact with the bot."],supportedAgents:["openclaw","hermes"],auth:{mode:"token-paste"},inputs:[{id:"appId",kind:"config",required:true,envKey:"MSTEAMS_APP_ID",statePath:"teamsConfig.appId",prompt:{label:"Microsoft Teams Client ID",help:"Run `teams app create --endpoint https:///api/messages`, then copy CLIENT_ID."}},{id:"clientSecret",kind:"secret",required:true,envKey:"MSTEAMS_APP_PASSWORD",prompt:{label:"Microsoft Teams Client Secret",help:"Use the CLIENT_SECRET printed by `teams app create`. It is shown once; rotate it in Entra ID if it was lost."}},{id:"tenantId",kind:"config",required:true,envKey:"MSTEAMS_TENANT_ID",statePath:"teamsConfig.tenantId",prompt:{label:"Microsoft Teams Tenant ID",help:"Use the TENANT_ID printed by `teams app create` or shown by `teams status --verbose`."}},{id:"allowedUsers",kind:"config",required:false,envKey:"TEAMS_ALLOWED_USERS",statePath:"allowedIds.teams",prompt:{label:"Microsoft Teams AAD Object IDs (comma-separated allowlist)",help:"Recommended: run `teams status --verbose` and enter the Azure AD object IDs allowed to use the bot."}},{id:"webhookPort",kind:"config",required:false,envKey:"MSTEAMS_PORT",statePath:"teamsConfig.webhookPort",defaultValue:"3978",prompt:{label:"Microsoft Teams webhook port",help:"Local bot webhook port to expose publicly. Defaults to 3978 and serves /api/messages."}},{id:"requireMention",kind:"config",required:false,envKey:"TEAMS_REQUIRE_MENTION",statePath:"teamsConfig.requireMention",validValues:["0","1"],defaultValue:"1",prompt:{label:"Microsoft Teams mention mode",help:"Controls OpenClaw group and channel behavior only. Direct messages are unaffected."}}],credentials:[{id:"teamsClientSecret",sourceInput:"clientSecret",providerName:"{sandboxName}-teams-bridge",providerEnvKey:"MSTEAMS_APP_PASSWORD",placeholder:"openshell:resolve:env:MSTEAMS_APP_PASSWORD",primary:true}],policyPresets:[{name:"teams",policyKeys:["teams"]}],hostForward:{port:"{{teamsConfig.webhookPort}}",label:"Microsoft Teams webhook"},render:[{id:TEAMS_OPENCLAW_WEBHOOK_RENDER_CONTRACT.renderId,kind:TEAMS_OPENCLAW_WEBHOOK_RENDER_CONTRACT.kind,agent:TEAMS_OPENCLAW_WEBHOOK_RENDER_CONTRACT.agent,target:TEAMS_OPENCLAW_WEBHOOK_RENDER_CONTRACT.target,fragment:{path:TEAMS_OPENCLAW_WEBHOOK_RENDER_CONTRACT.configPath,value:{enabled:true,appId:"{{teamsConfig.appId}}",appPassword:"{{credential.teamsClientSecret.placeholder}}",tenantId:"{{teamsConfig.tenantId}}",webhook:{port:"{{teamsConfig.webhookPort}}",path:TEAMS_OPENCLAW_WEBHOOK_RENDER_CONTRACT.webhookPath},healthMonitor:{enabled:false},streaming:{mode:"off"},dmPolicy:"{{allowedIds.teams.dmPolicy}}",allowFrom:"{{allowedIds.teams.values}}",groupPolicy:"open",requireMention:"{{teamsConfig.requireMention}}"}}},{id:"teams-openclaw-plugin",kind:"json-fragment",agent:"openclaw",target:"openclaw.json",fragment:{path:"plugins.entries.msteams",value:{enabled:true}}},{id:"teams-hermes-env",kind:"env-lines",agent:"hermes",target:"~/.hermes/.env",lines:["TEAMS_CLIENT_ID={{teamsConfig.appId}}","TEAMS_CLIENT_SECRET={{credential.teamsClientSecret.placeholder}}","TEAMS_TENANT_ID={{teamsConfig.tenantId}}","TEAMS_ALLOWED_USERS={{allowedIds.teams.csv}}","TEAMS_PORT={{teamsConfig.webhookPort}}"]},{id:"teams-hermes-platform",kind:"json-fragment",agent:"hermes",target:"~/.hermes/config.yaml",fragment:{path:"platforms.teams",value:{enabled:true}}}],runtime:{openclaw:{channelName:"msteams",visibility:{configKeys:["msteams"],logPatterns:["msteams","teams"]},nodePreloads:[{module:"msteams-message-hints",injectInto:["boot","connect"],optional:false,installMessage:"[channels] Installing Microsoft Teams message hint patch (native mentions)",installedMessage:"[channels] Microsoft Teams message hint patch installed (NODE_OPTIONS updated)"}]}},agentPackages:[{id:"openclawPluginPackage",agent:"openclaw",manager:"openclaw-plugin",spec:"npm:@openclaw/msteams@{{openclaw.version}}",pin:true,integrityByVersion:{"2026.7.1":"sha512-gG/Yk6HZAguHwrmKjsqdONbFz5WNy126PEAXQWNW/TulO1kIifQ6tktM16BQPNLnkmWqLbj+TrrO55Cjas1aFg=="},tarballUrlByVersion:{"2026.7.1":"https://registry.npmjs.org/@openclaw/msteams/-/msteams-2026.7.1.tgz"},required:true},{id:"hermesTeamsAppsPackage",agent:"hermes",manager:"hermes-uv-pip",spec:"microsoft-teams-apps==2.0.13.4",required:true},{id:"hermesAiohttpPackage",agent:"hermes",manager:"hermes-uv-pip",spec:"aiohttp==3.14.3",required:true}],hooks:[{id:"teams-host-forward-port-conflict",phase:"pre-enable",handler:"teams.hostForwardPortConflict",inputs:["webhookPort"],onFailure:"abort"},{id:"teams-host-forward-port-status",phase:"status",handler:"teams.hostForwardPortStatus",outputs:[{id:"hostForwardPortOverlaps",kind:"status"}]},{id:"teams-token-paste",phase:"enroll",handler:"common.tokenPaste",outputs:[{id:"clientSecret",kind:"secret",required:true}],onFailure:"skip-channel"},{id:"teams-config-prompt",phase:"enroll",handler:"common.configPrompt",outputs:[{id:"appId",kind:"config",required:true},{id:"tenantId",kind:"config",required:true},{id:"allowedUsers",kind:"config"},{id:"webhookPort",kind:"config"},{id:"requireMention",kind:"config"}]}]};var telegramManifest={schemaVersion:1,id:"telegram",displayName:"Telegram",description:"Telegram bot messaging",diagnosticsProbe:"log-tail",enrollmentNotes:["For Telegram group chats, disable privacy mode in @BotFather (/setprivacy -> your bot -> Disable).","After changing privacy mode, remove and re-add the bot to each group before testing @mentions."],supportedAgents:["openclaw","hermes"],auth:{mode:"token-paste"},inputs:[{id:"botToken",kind:"secret",required:true,envKey:"TELEGRAM_BOT_TOKEN",prompt:{label:"Telegram Bot Token",help:"Create a bot via @BotFather on Telegram, then copy the token."}},{id:"allowedIds",kind:"config",required:false,envKey:"TELEGRAM_ALLOWED_IDS",statePath:"allowedIds.telegram",prompt:{label:"Telegram User ID (for DM access)",help:"Send /start to @userinfobot on Telegram to get your numeric user ID.",emptyValueMessage:"bot will require manual pairing"}},{id:"requireMention",kind:"config",required:false,envKey:"TELEGRAM_REQUIRE_MENTION",statePath:"telegramConfig.requireMention",validValues:["0","1"],defaultValue:"1",prompt:{label:"Telegram group mention mode",help:"Controls Telegram group-chat behavior only \u2014 reply only when @mentioned vs. to all group messages. Direct messages are unaffected by this setting and remain subject to pairing and TELEGRAM_ALLOWED_IDS."}},{id:"groupPolicy",kind:"config",required:false,envKey:"TELEGRAM_GROUP_POLICY",statePath:"telegramConfig.groupPolicy",validValues:["open","allowlist","disabled"],defaultValue:"open",prompt:{label:"Telegram group policy",help:"Controls OpenClaw Telegram group access. Hermes does not expose an equivalent disable-groups policy."}}],credentials:[{id:"telegramBotToken",sourceInput:"botToken",providerName:"{sandboxName}-telegram-bridge",providerEnvKey:"TELEGRAM_BOT_TOKEN",placeholder:"openshell:resolve:env:TELEGRAM_BOT_TOKEN"}],policyPresets:[{name:"telegram",policyKeys:["telegram_bot"],agentPolicyKeys:{hermes:["telegram"]}}],render:[{id:"telegram-openclaw-channel",kind:"json-fragment",agent:"openclaw",target:"openclaw.json",fragment:{path:"channels.telegram",value:{enabled:true,accounts:{default:{botToken:"{{credential.telegramBotToken.placeholder}}",enabled:true,healthMonitor:{enabled:false},proxy:"{{proxyUrl}}",groupPolicy:"{{telegramConfig.groupPolicy}}",dmPolicy:"{{allowedIds.telegram.dmPolicy}}",allowFrom:"{{allowedIds.telegram.values}}"}}}}},{id:"telegram-openclaw-groups",kind:"json-fragment",agent:"openclaw",target:"openclaw.json",when:"{{telegramConfig.openclawGroups}}",fragment:{path:"channels.telegram.groups",value:"{{telegramConfig.openclawGroups}}"}},{id:"telegram-openclaw-plugin",kind:"json-fragment",agent:"openclaw",target:"openclaw.json",fragment:{path:"plugins.entries.telegram",value:{enabled:true}}},{id:"telegram-hermes-env",kind:"env-lines",agent:"hermes",target:"~/.hermes/.env",lines:["TELEGRAM_BOT_TOKEN={{credential.telegramBotToken.placeholder}}","TELEGRAM_ALLOWED_USERS={{allowedIds.telegram.csv}}"]},{id:"telegram-hermes-config",kind:"json-fragment",agent:"hermes",target:"~/.hermes/config.yaml",fragment:{path:"telegram",value:{require_mention:"{{telegramConfig.requireMention}}"}}},{id:"telegram-hermes-platform",kind:"json-fragment",agent:"hermes",target:"~/.hermes/config.yaml",fragment:{path:"platforms.telegram",value:{enabled:true}}}],runtime:{openclaw:{channelName:"telegram",visibility:{configKeys:["telegram"],logPatterns:["telegram"]},nodePreloads:[{module:"telegram-diagnostics",injectInto:["boot","connect"],optional:false,installMessage:"[channels] Installing Telegram diagnostics (provider readiness + inference errors)",installedMessage:"[channels] Telegram diagnostics installed (NODE_OPTIONS updated)"}]}},hooks:[{id:"telegram-token-paste",phase:"enroll",handler:"common.tokenPaste",outputs:[{id:"botToken",kind:"secret",required:true}],onFailure:"skip-channel"},{id:"telegram-allowlist-aliases",phase:"enroll",handler:"telegram.allowlistAliases",outputs:[{id:"allowedIds",kind:"config"}]},{id:"telegram-config-prompt",phase:"enroll",handler:"common.configPrompt",outputs:[{id:"requireMention",kind:"config"},{id:"allowedIds",kind:"config"}]},{id:"telegram-openclaw-config-prompt",phase:"enroll",handler:"common.configPrompt",agents:["openclaw"],outputs:[{id:"groupPolicy",kind:"config"}]},{id:"telegram-get-me-reachability",phase:"reachability-check",handler:"telegram.getMeReachability",inputs:["botToken"],onFailure:"skip-channel"},{id:"telegram-openclaw-bridge-health",phase:"health-check",handler:"telegram.openclawBridgeHealth",agents:["openclaw"],onFailure:"abort"},{id:"telegram-gateway-conflict-status",phase:"status",handler:"telegram.gatewayConflictStatus",outputs:[{id:"bridgeHealth",kind:"status"}]},{id:"telegram-status-health",phase:"status",handler:"telegram.statusHealth",agents:["openclaw"],outputs:[{id:"channelHealth",kind:"status"}]}]};var WECHAT_OPENCLAW_ACCOUNT_FILE_CONTRACT={channelId:"wechat",planHookId:"wechat-seed-openclaw-account",handlerId:"wechat.seedOpenClawAccount",outputId:"openclawWeixinAccountFile",kind:"build-file",required:true,mode:"0600"};var WECHAT_SEED_OPENCLAW_ACCOUNT_HOOK_ID=WECHAT_OPENCLAW_ACCOUNT_FILE_CONTRACT.handlerId;var WECHAT_SEED_OPENCLAW_ACCOUNT_PLAN_HOOK_ID=WECHAT_OPENCLAW_ACCOUNT_FILE_CONTRACT.planHookId;var WECHAT_OPENCLAW_ACCOUNT_FILE_OUTPUT_ID=WECHAT_OPENCLAW_ACCOUNT_FILE_CONTRACT.outputId;var WECHAT_TOKEN_PLACEHOLDER="openshell:resolve:env:WECHAT_BOT_TOKEN";function authorizeWechatAccountFilePlaceholders(value){const content=isPlainDataObject2(value)?ownDataPropertyValue2(value,"content"):void 0;if(!isPlainDataObject2(value)||!hasExactlyOwnDataProperties2(value,["content","mode","path"])||!isWechatAccountFilePath(ownDataPropertyValue2(value,"path"))||ownDataPropertyValue2(value,"mode")!==WECHAT_OPENCLAW_ACCOUNT_FILE_CONTRACT.mode||!isPlainDataObject2(content)||!hasOnlyOwnDataProperties(content,["baseUrl","savedAt","token","userId"])||!hasOwnDataProperty(content,"savedAt")||!hasOwnDataProperty(content,"token")||ownDataPropertyValue2(content,"token")!==WECHAT_TOKEN_PLACEHOLDER||!isNonEmptyString(ownDataPropertyValue2(content,"savedAt"))||!isOptionalNonEmptyString(content,"baseUrl")||!isOptionalNonEmptyString(content,"userId")){return[]}return[{path:["content","token"],value:WECHAT_TOKEN_PLACEHOLDER}]}function isWechatAccountFilePath(value){if(typeof value!=="string")return false;const prefix="openclaw-weixin/accounts/";const suffix=".json";if(!value.startsWith(prefix)||!value.endsWith(suffix))return false;const accountId=value.slice(prefix.length,-suffix.length);return accountId===accountId.trim()&&isSafeWechatAccountId(accountId)}function isSafeWechatAccountId(accountId){return accountId.length>0&&accountId!=="."&&accountId!==".."&&!/[\\/\0-\x1F\x7F]/.test(accountId)&&!accountId.includes("..")}function isPlainDataObject2(value){if(value===null||typeof value!=="object"||Array.isArray(value))return false;const prototype=Object.getPrototypeOf(value);return prototype===Object.prototype||prototype===null}function ownDataPropertyValue2(value,key){const descriptor=Object.getOwnPropertyDescriptor(value,key);return descriptor&&"value"in descriptor?descriptor.value:void 0}function hasOwnDataProperty(value,key){const descriptor=Object.getOwnPropertyDescriptor(value,key);return descriptor!==void 0&&"value"in descriptor}function hasExactlyOwnDataProperties2(value,expected){const actual=Object.getOwnPropertyNames(value).sort();return actual.length===expected.length&&actual.every((key,index)=>key===expected[index])}function hasOnlyOwnDataProperties(value,allowed){return Object.getOwnPropertyNames(value).every(key=>allowed.includes(key))}function isNonEmptyString(value){return typeof value==="string"&&value.length>0}function isOptionalNonEmptyString(value,key){return!hasOwnDataProperty(value,key)||isNonEmptyString(ownDataPropertyValue2(value,key))}var wechatManifest={schemaVersion:1,id:"wechat",displayName:"WeChat",description:"WeChat (personal) bot messaging",enrollmentHelp:"Captured automatically via a host-side QR scan during onboard \u2014 pair the bot by scanning the QR with WeChat on your phone (Discover \u2192 Scan). DM-only.",supportedAgents:["openclaw","hermes"],auth:{mode:"host-qr"},inputs:[{id:"botToken",kind:"secret",required:true,envKey:"WECHAT_BOT_TOKEN",prompt:{label:"WeChat Bot Token",help:"Captured automatically via a host-side QR scan during onboard \u2014 pair the bot by scanning the QR with WeChat on your phone (Discover \u2192 Scan). DM-only."}},{id:"accountId",kind:"config",required:true,envKey:"WECHAT_ACCOUNT_ID",statePath:"wechatConfig.accountId"},{id:"baseUrl",kind:"config",required:false,envKey:"WECHAT_BASE_URL",statePath:"wechatConfig.baseUrl"},{id:"userId",kind:"config",required:false,envKey:"WECHAT_USER_ID",statePath:"wechatConfig.userId"},{id:"allowedIds",kind:"config",required:false,envKey:"WECHAT_ALLOWED_IDS",statePath:"allowedIds.wechat",prompt:{label:"WeChat User ID(s) (DM allowlist)",help:"Optional: restrict who can DM the bot. The WeChat user id of the operator who scanned is added automatically; supply additional ids as a comma-separated list.",emptyValueMessage:"bot will require manual pairing"}}],credentials:[{id:"wechatBotToken",sourceInput:"botToken",providerName:"{sandboxName}-wechat-bridge",providerEnvKey:"WECHAT_BOT_TOKEN",placeholder:"openshell:resolve:env:WECHAT_BOT_TOKEN"}],policyPresets:[{name:"wechat",policyKeys:["wechat_bridge"]}],render:[{id:"wechat-openclaw-plugin",kind:"json-fragment",agent:"openclaw",target:"openclaw.json",fragment:{path:"plugins.entries.openclaw-weixin",value:{enabled:true}}},{id:"wechat-openclaw-channel",kind:"json-fragment",agent:"openclaw",target:"openclaw.json",fragment:{path:"channels.openclaw-weixin",value:{}}},{id:"wechat-hermes-env",kind:"env-lines",agent:"hermes",target:"~/.hermes/.env",lines:["WEIXIN_TOKEN={{credential.wechatBotToken.placeholder}}","WEIXIN_ACCOUNT_ID={{wechatConfig.accountId}}","WEIXIN_BASE_URL={{wechatConfig.baseUrl}}","WEIXIN_ALLOWED_USERS={{allowedIds.wechat.csv}}"]},{id:"wechat-hermes-platform",kind:"json-fragment",agent:"hermes",target:"~/.hermes/config.yaml",fragment:{path:"platforms.weixin",value:{enabled:true}}}],runtime:{openclaw:{channelName:"openclaw-weixin",visibility:{configKeys:["openclaw-weixin"],logPatterns:["wechat","openclaw-weixin"]},nodePreloads:[{module:"wechat-diagnostics",injectInto:["boot","connect"],optional:false,installMessage:"[channels] Installing WeChat diagnostics (provider readiness + inference errors)",installedMessage:"[channels] WeChat diagnostics installed (NODE_OPTIONS updated)"}]}},agentPackages:[{id:"openclawPluginPackage",agent:"openclaw",manager:"openclaw-plugin",spec:"npm:@tencent-weixin/openclaw-weixin@2.4.3",pin:true,integrity:"sha512-dPQbidUNWigC6V10vGW4i+GLH09x+6zUhafZRjuxkJ9GDu8o62WBsnUTojp4KqUH756hz+t2v9khiCRSi0dBDw==",tarballUrl:"https://registry.npmjs.org/@tencent-weixin/openclaw-weixin/-/openclaw-weixin-2.4.3.tgz",runtimeLock:{cachePath:"/usr/local/share/nemoclaw/wechat-npm-cache",installCacheEnvKey:"NEMOCLAW_WECHAT_NPM_INSTALL_CACHE",lockFile:"/usr/local/lib/nemoclaw/wechat-runtime/package-lock.json",projectsRoot:"/sandbox/.openclaw/npm/projects",verifierPath:"/usr/local/lib/nemoclaw/verify-wechat-runtime-lock.mts",offline:true,legacyPeerDeps:true},required:true}],hooks:[{id:"wechat-host-qr",phase:"enroll",handler:"wechat.ilinkLogin",inputs:["allowedIds"],outputs:[{id:"botToken",kind:"secret",required:true},{id:"accountId",kind:"config",required:true},{id:"baseUrl",kind:"config"},{id:"userId",kind:"config"},{id:"allowedIds",kind:"config"}],onFailure:"skip-channel"},{id:"wechat-config-prompt",phase:"enroll",handler:"common.configPrompt",outputs:[{id:"allowedIds",kind:"config"}]},{id:WECHAT_OPENCLAW_ACCOUNT_FILE_CONTRACT.planHookId,phase:"post-agent-install",handler:WECHAT_OPENCLAW_ACCOUNT_FILE_CONTRACT.handlerId,agents:["openclaw"],inputs:["wechatConfig.accountId","wechatConfig.baseUrl","wechatConfig.userId","credential.wechatBotToken.placeholder"],outputs:[{id:"openclawWeixinAccountsIndex",kind:"build-file",required:true},{id:WECHAT_OPENCLAW_ACCOUNT_FILE_CONTRACT.outputId,kind:WECHAT_OPENCLAW_ACCOUNT_FILE_CONTRACT.kind,required:WECHAT_OPENCLAW_ACCOUNT_FILE_CONTRACT.required},{id:"openclawConfigPatch",kind:"build-file",required:true}],onFailure:"abort"},{id:"wechat-health-check",phase:"health-check",handler:"wechat.healthCheck",inputs:["wechatConfig.accountId"],onFailure:"abort"}]};var whatsappManifest={schemaVersion:1,id:"whatsapp",displayName:"WhatsApp",description:"WhatsApp Web messaging (QR pairing)",enrollmentHelp:"WhatsApp Web pairs via QR code scanned with your phone \u2014 no host-side token. After the sandbox is running, run `openshell term` and then use `openclaw channels login --channel whatsapp` for OpenClaw or `hermes whatsapp` for Hermes to display the QR.",enrollmentNotes:["After pairing, run `nemoclaw channels status --channel whatsapp`. OpenClaw reports inbound delivery evidence; Hermes reports gateway and dashboard session-path diagnostics."],supportedAgents:["openclaw","hermes"],auth:{mode:"in-sandbox-qr"},inputs:[{id:"mode",kind:"config",required:false,envKey:"WHATSAPP_MODE",statePath:"whatsappConfig.mode",validValues:["self-chat","bot"],defaultValue:"self-chat",prompt:{label:"WhatsApp reply mode",help:"self-chat replies only to messages the paired account sends to itself. bot replies to other senders and stops replying to that self-chat: an unknown sender receives a pairing code you approve with `hermes pairing approve whatsapp `, unless you set WHATSAPP_ALLOWED_IDS to a fixed sender list before this command.",emptyValueMessage:"the sandbox replies only in your own self-chat"}},{id:"allowedIds",kind:"config",required:false,envKey:"WHATSAPP_ALLOWED_IDS",statePath:"allowedIds.whatsapp"}],credentials:[],policyPresets:["whatsapp"],render:[{id:"whatsapp-openclaw-channel",kind:"json-fragment",agent:"openclaw",target:"openclaw.json",fragment:{path:"channels.whatsapp",value:{enabled:true,accounts:{default:{enabled:true,healthMonitor:{enabled:false}}}}}},{id:"whatsapp-openclaw-plugin",kind:"json-fragment",agent:"openclaw",target:"openclaw.json",fragment:{path:"plugins.entries.whatsapp",value:{enabled:true}}},{id:"whatsapp-hermes-env",kind:"env-lines",agent:"hermes",target:"~/.hermes/.env",lines:["WHATSAPP_ENABLED=true","WHATSAPP_MODE={{whatsappConfig.mode}}","WHATSAPP_DM_POLICY={{whatsappConfig.dmPolicy}}","WHATSAPP_ALLOWED_USERS={{allowedIds.whatsapp.csv}}"]},{id:"whatsapp-hermes-platform",kind:"json-fragment",agent:"hermes",target:"~/.hermes/config.yaml",fragment:{path:"platforms.whatsapp",value:{enabled:true}}}],runtime:{openclaw:{channelName:"whatsapp",visibility:{configKeys:["whatsapp"],logPatterns:["whatsapp"]},nodePreloads:[{module:"whatsapp-qr-compact",injectInto:["connect"],optional:true,installMessage:"[channels] Installing WhatsApp compact-QR renderer (scan-friendly pairing)"}]}},agentPackages:[{id:"openclawPluginPackage",agent:"openclaw",manager:"openclaw-plugin",spec:"npm:@openclaw/whatsapp@{{openclaw.version}}",pin:true,integrityByVersion:{"2026.7.1":"sha512-wLY/Omc5fleRpl2lKGN8sxt/8hYfHGwLRezmWsk8oCbea5pRKUPE6ZX+wJO1O52NOJkAGCuiXvS7x0qIeKxXbQ=="},tarballUrlByVersion:{"2026.7.1":"https://registry.npmjs.org/@openclaw/whatsapp/-/whatsapp-2026.7.1.tgz"},required:true}],hooks:[{id:"whatsapp-config-prompt",phase:"enroll",handler:"common.configPrompt",agents:["hermes"],outputs:[{id:"mode",kind:"config"}]},{id:"whatsapp-status-health",phase:"status",handler:"whatsapp.statusHealth",agents:["openclaw","hermes"],outputs:[{id:"channelHealth",kind:"status"}]}]};var BUILT_IN_CHANNEL_MANIFESTS=[telegramManifest,discordManifest,wechatManifest,slackManifest,whatsappManifest,teamsManifest,googlechatManifest];function createBuiltInChannelManifestRegistry(){return createChannelManifestRegistry(BUILT_IN_CHANNEL_MANIFESTS)}var EXACT_TEMPLATE_PATTERN=/^\{\{\s*([^}]+?)\s*\}\}$/;var TEMPLATE_REFERENCE_PATTERN=/\{\{\s*([^}]+?)\s*\}\}/g;function resolvedRenderTemplateReference(value){return{matched:true,value}}function resolveSandboxNameTemplate(value,sandboxName){return value.replaceAll("{sandboxName}",sandboxName)}function resolveRenderTemplatesInValue(value,context){if(typeof value==="string")return resolveRenderTemplatesInString(value,context);if(Array.isArray(value)){if(value.length===0)return value;const resolved=value.map(entry=>resolveRenderTemplatesInValue(entry,context)).filter(entry=>entry!==void 0);return resolved.length>0?resolved:void 0}if(value&&typeof value==="object"){const sourceEntries=Object.entries(value);if(sourceEntries.length===0)return value;const entries=sourceEntries.map(([key,entry])=>[key,resolveRenderTemplatesInValue(entry,context)]).filter(entry=>entry[1]!==void 0);return entries.length>0?Object.fromEntries(entries):void 0}return value}function isTruthyRenderTemplate(value,context){if(!value)return true;const resolved=resolveRenderTemplatesInString(value,context);if(resolved===void 0||resolved===null||resolved===false)return false;if(Array.isArray(resolved))return resolved.length>0;if(typeof resolved==="object")return Object.keys(resolved).length>0;if(typeof resolved==="string")return resolved.trim().length>0;return true}function resolveRenderTemplatesInString(value,context){const exact=value.match(EXACT_TEMPLATE_PATTERN);if(exact?.[1])return resolveTemplateReference(exact[1].trim(),context);let omitted=false;const resolved=value.replace(TEMPLATE_REFERENCE_PATTERN,(match,reference)=>{const replacement=resolveTemplateReference(reference.trim(),context);if(replacement===void 0||replacement===null){omitted=true;return""}if(Array.isArray(replacement))return replacement.map(String).join(",");if(typeof replacement==="object")return JSON.stringify(replacement);return String(replacement)});return omitted?void 0:resolved}function resolveTemplateReference(reference,context){const resolved=context.referenceResolver?.(reference,context);return resolved?.matched?resolved.value:"{{"+reference+"}}"}function allowedIds(context,channel){return parseList(stateValue(context,`allowedIds.${channel}`))}function stateValue(context,path5){const stateInput=context.inputs.find(input=>input.statePath===path5);if(stateInput?.value!==void 0)return stateInput.value;const inputId=path5.split(".").at(-1);return context.inputs.find(input=>input.inputId===inputId)?.value}function parseList(value){if(Array.isArray(value))return unique(value.map(String).map(cleanString).filter(Boolean));const text=cleanString(value);if(!text)return[];return unique(text.split(",").map(cleanString).filter(Boolean))}function parseBoolean(value){if(typeof value==="boolean")return value;const text=cleanString(value)?.toLowerCase();if(text==="1"||text==="true"||text==="yes"||text==="on")return true;if(text==="0"||text==="false"||text==="no"||text==="off")return false;return void 0}function nonEmptyString(value){return cleanString(value)||void 0}function cleanString(value){const text=String(value??"");if(/[\r\n]/.test(text)){throw new Error("Messaging template values must not contain line breaks.")}return text.trim()}function nonEmptyArray(values){return values.length>0?[...values]:void 0}function nonEmptyCsv(values){return values.length>0?values.join(","):void 0}function nonEmptyObject(value){return Object.keys(value).length>0?value:void 0}function unique(values){return[...new Set(values)]}var resolveDiscordTemplateReference=(reference,context)=>{if(reference==="discordProxyUrl")return resolvedRenderTemplateReference(void 0);switch(reference){case"discord.guilds":return resolvedRenderTemplateReference(nonEmptyObject(discordGuilds(context)));case"discord.hasGuilds":return resolvedRenderTemplateReference(Object.keys(discordGuilds(context)).length>0);case"discord.guildIds.csv":return resolvedRenderTemplateReference(nonEmptyCsv(Object.keys(discordGuilds(context))));case"discord.allowedUsers.values":return resolvedRenderTemplateReference(nonEmptyArray(discordAllowedUsers(context)));case"discord.allowedUsers.csv":return resolvedRenderTemplateReference(nonEmptyCsv(discordAllowedUsers(context)));case"discord.allowedUsers.dmPolicy":return resolvedRenderTemplateReference(discordAllowedUsers(context).length>0?"allowlist":void 0);case"discord.allowAllUsers":return resolvedRenderTemplateReference(Object.keys(discordGuilds(context)).length>0&&discordAllowedUsers(context).length===0?true:void 0);case"discord.requireMention":return resolvedRenderTemplateReference(discordRequireMention(context));default:return void 0}};function discordGuilds(context){const serverIds=parseList(stateValue(context,"discordGuilds.serverId"));if(serverIds.length===0)return{};const users=parseList(stateValue(context,"discordGuilds.userIds"));const requireMention=parseBoolean(stateValue(context,"discordGuilds.requireMention"))??true;return Object.fromEntries(serverIds.map(serverId=>[serverId,{requireMention,...users.length>0?{users}:{}}]))}function discordAllowedUsers(context){const users=new Set(allowedIds(context,"discord"));for(const guild of Object.values(discordGuilds(context))){for(const user of guild.users??[])users.add(String(user))}return[...users]}function discordRequireMention(context){for(const guild of Object.values(discordGuilds(context))){if(typeof guild.requireMention==="boolean")return guild.requireMention}return true}var DEFAULT_AUDIENCE_TYPE="app-url";var APP_PRINCIPAL_DISCOVERY_SENTINEL="000000000000000000000";var resolveGooglechatTemplateReference=(reference,context)=>{switch(reference){case"googlechatConfig.audienceType":return resolvedRenderTemplateReference(nonEmptyString(stateValue(context,"googlechatConfig.audienceType"))??DEFAULT_AUDIENCE_TYPE);case"googlechatConfig.audience":return resolvedRenderTemplateReference(nonEmptyString(stateValue(context,"googlechatConfig.audience")));case"googlechatConfig.appPrincipal":return resolvedRenderTemplateReference(nonEmptyString(stateValue(context,"googlechatConfig.appPrincipal"))??APP_PRINCIPAL_DISCOVERY_SENTINEL);case"googlechatConfig.projectId":return resolvedRenderTemplateReference(nonEmptyString(stateValue(context,"googlechatConfig.projectId")));case"googlechatConfig.subscriptionName":return resolvedRenderTemplateReference(nonEmptyString(stateValue(context,"googlechatConfig.subscriptionName")));default:break}const allowReference=reference.match(/^allowedIds[.]googlechat[.](values|dmPolicy|csv)$/);if(!allowReference?.[1])return void 0;const ids=allowedIds(context,"googlechat");switch(allowReference[1]){case"values":return resolvedRenderTemplateReference(nonEmptyArray(ids));case"dmPolicy":return resolvedRenderTemplateReference(ids.length>0?"allowlist":void 0);case"csv":return resolvedRenderTemplateReference(ids.length>0?ids.join(","):void 0);default:return void 0}};var resolveSlackTemplateReference=(reference,context)=>{if(reference==="slackConfig.allowedChannels.csv"){return resolvedRenderTemplateReference(nonEmptyCsv(slackAllowedChannels(context)))}const allowedIdsReference=reference.match(/^allowedIds[.]slack[.](values|csv|dmPolicy|groupPolicy|channels)$/);if(!allowedIdsReference?.[1])return void 0;const ids=allowedIds(context,"slack");switch(allowedIdsReference[1]){case"values":return resolvedRenderTemplateReference(nonEmptyArray(ids));case"csv":return resolvedRenderTemplateReference(nonEmptyCsv(ids));case"dmPolicy":return resolvedRenderTemplateReference(ids.length>0?"allowlist":void 0);case"groupPolicy":return resolvedRenderTemplateReference(ids.length>0||slackAllowedChannels(context).length>0?"allowlist":void 0);case"channels":return resolvedRenderTemplateReference(slackChannelConfig(context,ids));default:return void 0}};function slackChannelConfig(context,users){const allowedChannels=slackAllowedChannels(context);const entry={enabled:true,requireMention:true,...users.length>0?{users:[...users]}:{}};if(allowedChannels.length>0){return Object.fromEntries(allowedChannels.map(channelId=>[channelId,{...entry}]))}return users.length>0?{"*":entry}:void 0}function slackAllowedChannels(context){return parseList(stateValue(context,"slackConfig.allowedChannels"))}var DEFAULT_TEAMS_WEBHOOK_PORT=3978;var resolveTeamsTemplateReference=(reference,context)=>{switch(reference){case"teamsConfig.appId":return resolvedRenderTemplateReference(nonEmptyString(stateValue(context,"teamsConfig.appId")));case"teamsConfig.tenantId":return resolvedRenderTemplateReference(nonEmptyString(stateValue(context,"teamsConfig.tenantId")));case"teamsConfig.webhookPort":return resolvedRenderTemplateReference(teamsWebhookPort(context));case"teamsConfig.requireMention":return resolvedRenderTemplateReference(parseBoolean(stateValue(context,"teamsConfig.requireMention")));default:break}const allowedIdsReference=reference.match(/^allowedIds[.]teams[.](values|csv|dmPolicy)$/);if(!allowedIdsReference?.[1])return void 0;const ids=allowedIds(context,"teams");switch(allowedIdsReference[1]){case"values":return resolvedRenderTemplateReference(nonEmptyArray(ids));case"csv":return resolvedRenderTemplateReference(nonEmptyCsv(ids));case"dmPolicy":return resolvedRenderTemplateReference(ids.length>0?"allowlist":void 0);default:return void 0}};function teamsWebhookPort(context){const raw=nonEmptyString(stateValue(context,"teamsConfig.webhookPort"));if(!raw)return DEFAULT_TEAMS_WEBHOOK_PORT;const port=Number(raw);if(!Number.isInteger(port)||port<1||port>65535){throw new Error("Microsoft Teams webhook port must be an integer TCP port between 1 and 65535.")}return port}var DEFAULT_PROXY_HOST="10.200.0.1";var DEFAULT_PROXY_PORT="3128";var DEFAULT_TELEGRAM_GROUP_POLICY="open";var TELEGRAM_GROUP_POLICIES=new Set(["open","allowlist","disabled"]);var resolveTelegramTemplateReference=(reference,context)=>{if(reference==="proxyUrl")return resolvedRenderTemplateReference(proxyUrl(context.env));if(reference==="telegramConfig.groupPolicy"){return resolvedRenderTemplateReference(telegramGroupPolicy(context))}if(reference==="telegramConfig.openclawGroups"){return resolvedRenderTemplateReference(telegramOpenClawGroups(context))}if(reference==="telegramConfig.requireMention"){return resolvedRenderTemplateReference(parseBoolean(stateValue(context,"telegramConfig.requireMention")))}const allowedIdsReference=reference.match(/^allowedIds[.]telegram[.](values|csv|dmPolicy)$/);if(!allowedIdsReference?.[1])return void 0;const ids=allowedIds(context,"telegram");switch(allowedIdsReference[1]){case"values":return resolvedRenderTemplateReference(nonEmptyArray(ids));case"csv":return resolvedRenderTemplateReference(nonEmptyCsv(ids));case"dmPolicy":return resolvedRenderTemplateReference(ids.length>0?"allowlist":void 0);default:return void 0}};function proxyUrl(env){const host=nonEmptyString(env?.NEMOCLAW_PROXY_HOST)??DEFAULT_PROXY_HOST;const port=nonEmptyString(env?.NEMOCLAW_PROXY_PORT)??DEFAULT_PROXY_PORT;return`http://${host}:${port}`}function telegramGroupPolicy(context){const value=nonEmptyString(stateValue(context,"telegramConfig.groupPolicy"));return value&&TELEGRAM_GROUP_POLICIES.has(value)?value:DEFAULT_TELEGRAM_GROUP_POLICY}function telegramOpenClawGroups(context){if(telegramGroupPolicy(context)!=="open")return void 0;const requireMention=parseBoolean(stateValue(context,"telegramConfig.requireMention"));return requireMention===true?{"*":{requireMention:true}}:void 0}var WECHAT_ILINK_HOSTS=new Set(["ilinkai.weixin.qq.com","ilinkai.wechat.com"]);var WECHAT_ILINK_IDC_HOST_PATTERN=/^idc-[0-9]+[.]weixin[.]qq[.]com$/;function normalizeWechatIlinkBaseUrl(value){const raw=String(value??"");if(/[\r\n]/.test(raw)){throw new Error("WeChat baseUrl must not contain line breaks.")}const text=raw.trim();if(!text)return void 0;let url;try{url=new URL(text)}catch{throw new Error("WeChat baseUrl must be a valid URL.")}if(url.protocol!=="https:"){throw new Error("WeChat baseUrl must use HTTPS.")}if(url.username||url.password){throw new Error("WeChat baseUrl must not include credentials.")}if(!isWechatIlinkHost(url.hostname)){throw new Error("WeChat baseUrl must use an expected iLink host.")}if(url.pathname&&url.pathname!=="/"||url.search||url.hash){throw new Error("WeChat baseUrl must be an iLink origin URL.")}return url.origin}function isWechatIlinkHost(hostname){const normalized=hostname.toLowerCase();return WECHAT_ILINK_HOSTS.has(normalized)||WECHAT_ILINK_IDC_HOST_PATTERN.test(normalized)}var resolveWechatTemplateReference=(reference,context)=>{const wechatConfig=reference.match(/^wechatConfig[.](accountId|baseUrl|userId)$/);if(wechatConfig?.[1]){if(wechatConfig[1]==="baseUrl"){return resolvedRenderTemplateReference(normalizeWechatIlinkBaseUrl(stateValue(context,"wechatConfig.baseUrl")))}return resolvedRenderTemplateReference(nonEmptyString(stateValue(context,"wechatConfig."+wechatConfig[1])))}const allowedIdsReference=reference.match(/^allowedIds[.]wechat[.](values|csv|dmPolicy)$/);if(!allowedIdsReference?.[1])return void 0;const ids=wechatAllowedIds(context);switch(allowedIdsReference[1]){case"values":return resolvedRenderTemplateReference(nonEmptyArray(ids));case"csv":return resolvedRenderTemplateReference(nonEmptyCsv(ids));case"dmPolicy":return resolvedRenderTemplateReference(ids.length>0?"allowlist":void 0);default:return void 0}};function wechatAllowedIds(context){const ids=allowedIds(context,"wechat");const userId=nonEmptyString(stateValue(context,"wechatConfig.userId"));return userId&&!ids.includes(userId)?[userId,...ids]:ids}var DEFAULT_WHATSAPP_MODE="self-chat";var BOT_WHATSAPP_MODE="bot";var WHATSAPP_MODES=new Set([DEFAULT_WHATSAPP_MODE,BOT_WHATSAPP_MODE]);var resolveWhatsappTemplateReference=(reference,context)=>{if(reference==="whatsappConfig.mode"){return resolvedRenderTemplateReference(whatsappMode(context))}if(reference==="whatsappConfig.dmPolicy"){return resolvedRenderTemplateReference(whatsappDmPolicy(context))}const allowedIdsReference=reference.match(/^allowedIds[.]whatsapp[.](values|csv)$/);if(!allowedIdsReference?.[1])return void 0;const ids=allowedIds(context,"whatsapp");switch(allowedIdsReference[1]){case"values":return resolvedRenderTemplateReference(nonEmptyArray(ids));case"csv":return resolvedRenderTemplateReference(nonEmptyCsv(ids));default:return void 0}};function whatsappMode(context){const value=nonEmptyString(stateValue(context,"whatsappConfig.mode"));return value&&WHATSAPP_MODES.has(value)?value:DEFAULT_WHATSAPP_MODE}function whatsappDmPolicy(context){if(whatsappMode(context)!==BOT_WHATSAPP_MODE)return void 0;return allowedIds(context,"whatsapp").length>0?"allowlist":"pairing"}var BUILT_IN_TEMPLATE_REFERENCE_RESOLVERS=[resolveTelegramTemplateReference,resolveDiscordTemplateReference,resolveWechatTemplateReference,resolveSlackTemplateReference,resolveWhatsappTemplateReference,resolveTeamsTemplateReference,resolveGooglechatTemplateReference];function createBuiltInRenderTemplateResolver(){return(reference,context)=>{for(const resolver of BUILT_IN_TEMPLATE_REFERENCE_RESOLVERS){const resolved=resolver(reference,context);if(resolved)return resolved}return void 0}}var import_node_crypto=__toESM(require("node:crypto"));function hashCredential(value){const normalized=String(value??"").trim();if(!normalized)return null;return import_node_crypto.default.createHash("sha256").update(normalized).digest("hex")}function planCredentialBindings(manifest,context,inputs,environment=process.env){return manifest.credentials.map(credential=>{const sourceInput=inputs.find(input=>input.inputId===credential.sourceInput);const credentialAvailable=sourceInput?.credentialAvailable===true||context.credentialAvailability?.[credential.id]===true||context.credentialAvailability?.[`${manifest.id}.${credential.id}`]===true;const envKey=sourceInput?.sourceEnv??credential.providerEnvKey;const credentialHash=credentialAvailable?hashCredential(environment[envKey])??void 0:void 0;return{channelId:manifest.id,credentialId:credential.id,sourceInput:credential.sourceInput,providerName:resolveSandboxNameTemplate(credential.providerName,context.sandboxName),providerEnvKey:credential.providerEnvKey,placeholder:credential.placeholder,credentialAvailable,...credentialHash!==void 0?{credentialHash}:{}}})}function planHostForward(manifest,inputs,active,referenceResolver,environment=process.env){if(!active||!manifest.hostForward)return void 0;const context={inputs,env:environment,referenceResolver};if(!isTruthyRenderTemplate(manifest.hostForward.when,context))return void 0;const portValue=resolveRenderTemplatesInValue(manifest.hostForward.port,context);const port=normalizeForwardPort(manifest.id,portValue);return{channelId:manifest.id,port,label:manifest.hostForward.label}}function normalizeForwardPort(channelId,value){const port=typeof value==="number"?value:Number(String(value??"").trim());if(!Number.isInteger(port)||port<1||port>65535){throw new Error(`Channel manifest '${channelId}' declares invalid host forward port '${String(value)}'.`)}return port}var OPENSHELL_ENV_PLACEHOLDER_PREFIX="openshell:resolve:env:";var OPENSHELL_ALIAS_PLACEHOLDER_RE=/^[A-Za-z0-9]+-OPENSHELL-RESOLVE-ENV-(.+)$/;function normalizeProviderPlaceholderForEnvKey(value,envKey){if(value.startsWith(OPENSHELL_ENV_PLACEHOLDER_PREFIX)){return placeholderSuffixMatchesEnvKey(value.slice(OPENSHELL_ENV_PLACEHOLDER_PREFIX.length),envKey)?`${OPENSHELL_ENV_PLACEHOLDER_PREFIX}${envKey}`:null}const aliasMatch=value.match(OPENSHELL_ALIAS_PLACEHOLDER_RE);if(!aliasMatch||!placeholderSuffixMatchesEnvKey(aliasMatch[1],envKey)){return null}return value.replace(/-OPENSHELL-RESOLVE-ENV-.+$/,`-OPENSHELL-RESOLVE-ENV-${envKey}`)}function placeholderSuffixMatchesEnvKey(suffix,envKey){if(suffix===envKey)return true;const revisionMatch=suffix.match(/^v[0-9]+_(.+)$/);return revisionMatch?.[1]===envKey}function hasFullPersistedCredentialBindingShape(binding){return typeof binding.channelId==="string"&&typeof binding.credentialId==="string"&&typeof binding.sourceInput==="string"&&typeof binding.providerName==="string"&&typeof binding.providerEnvKey==="string"&&typeof binding.placeholder==="string"&&typeof binding.credentialAvailable==="boolean"}function normalizeFullPersistedCredentialBindings(bindings){return bindings.map(binding=>({channelId:binding.channelId,credentialId:binding.credentialId,sourceInput:binding.sourceInput,providerName:binding.providerName,providerEnvKey:binding.providerEnvKey,placeholder:normalizeProviderPlaceholderForEnvKey(binding.placeholder,binding.providerEnvKey)??binding.placeholder,credentialAvailable:binding.credentialAvailable===true,...typeof binding.credentialHash==="string"?{credentialHash:binding.credentialHash}:{}}))}function normalizePersistedAgentCredentialPlaceholders(render,credentialBindings){const credentialEnvKeys=new Set(credentialBindings.map(binding=>binding.providerEnvKey).filter(Boolean));if(credentialEnvKeys.size===0)return[...render];return render.map(entry=>{if(entry.kind!=="env-lines")return entry;return{...entry,lines:entry.lines.map(line=>normalizeCredentialEnvLine(line,credentialEnvKeys))}})}function normalizeCredentialEnvLine(line,credentialEnvKeys){const index=line.indexOf("=");if(index<=0)return line;const envKey=line.slice(0,index).trim();if(!credentialEnvKeys.has(envKey))return line;const value=line.slice(index+1);const normalized=normalizeProviderPlaceholderForEnvKey(value,envKey);return normalized?`${envKey}=${normalized}`:line}function normalizePersistedSandboxMessagingPlanShape(plan,environment=process.env){const manifestRegistry=createBuiltInChannelManifestRegistry();const disabledChannels=plan.disabledChannels.filter(channelId=>typeof channelId==="string");const disabledSet=new Set(disabledChannels);const channels=plan.channels.map(channel=>normalizePersistedChannel(channel,disabledSet,manifestRegistry.get(channel.channelId),environment));const credentialBindings=normalizePersistedCredentialBindings(plan,channels,manifestRegistry,environment);const normalizedPlan={...plan,channels,disabledChannels,credentialBindings,networkPolicy:plan.networkPolicy&&Array.isArray(plan.networkPolicy.entries)?plan.networkPolicy:{presets:[],entries:[]},agentRender:normalizePersistedAgentCredentialPlaceholders(Array.isArray(plan.agentRender)?[...plan.agentRender]:[],credentialBindings),buildSteps:Array.isArray(plan.buildSteps)?[...plan.buildSteps]:[],...plan.runtimeSetup!==void 0?{runtimeSetup:normalizeRuntimeSetup(plan.runtimeSetup)}:{},stateUpdates:Array.isArray(plan.stateUpdates)?[...plan.stateUpdates]:[],healthChecks:Array.isArray(plan.healthChecks)?[...plan.healthChecks]:[]};return normalizedPlan}function normalizePersistedChannel(channel,disabledSet,manifest,environment){const disabled=channel.disabled??disabledSet.has(channel.channelId);const configured=channel.configured??true;const hasFullShape=hasFullChannelShape(channel);const inputs=hasFullShape?normalizeFullInputs(channel.channelId,channel.inputs??[]):normalizePersistedInputs(channel,manifest);const active=channel.active??(configured&&!disabled&&requiredInputsAvailable(manifest,inputs));const hostForward=manifest?planHostForward(manifest,inputs,active&&!disabled,createBuiltInRenderTemplateResolver(),environment):void 0;return{channelId:channel.channelId,displayName:channel.displayName??manifest?.displayName??channel.channelId,authMode:channel.authMode??manifest?.auth.mode??"none",active,selected:channel.selected??configured,configured,disabled,inputs,...hostForward?{hostForward}:{},hooks:Array.isArray(channel.hooks)?[...channel.hooks]:[]}}function normalizePersistedInputs(channel,manifest){const persistedById=new Map((channel.inputs??[]).filter(input=>typeof input.inputId==="string").map(input=>[input.inputId,input]));const fromManifest=(manifest?.inputs??[]).map(input=>inputReferenceFromManifest(channel.channelId,input,persistedById.get(input.id)));const manifestInputIds=new Set((manifest?.inputs??[]).map(input=>input.id));const unknownInputs=[...persistedById.values()].flatMap(input=>{if(!input.inputId||manifestInputIds.has(input.inputId))return[];return[normalizeUnknownInput(channel.channelId,input)]});return[...fromManifest,...unknownInputs]}function normalizeFullInputs(channelId,inputs){return inputs.filter(input=>typeof input.inputId==="string").map(input=>({channelId:typeof input.channelId==="string"?input.channelId:channelId,inputId:input.inputId,kind:input.kind==="secret"||input.kind==="config"?input.kind:"config",required:typeof input.required==="boolean"?input.required:false,...typeof input.sourceEnv==="string"?{sourceEnv:input.sourceEnv}:{},...typeof input.statePath==="string"?{statePath:input.statePath}:{},...input.credentialAvailable!==void 0?{credentialAvailable:input.credentialAvailable}:{},...input.value!==void 0?{value:input.value}:{}}))}function inputReferenceFromManifest(channelId,input,persisted){return{channelId,inputId:input.id,kind:input.kind,required:input.required,...input.envKey?{sourceEnv:input.envKey}:{},...input.kind==="config"&&input.statePath?{statePath:input.statePath}:{},...persisted?.credentialAvailable!==void 0?{credentialAvailable:persisted.credentialAvailable}:{},...persisted?.value!==void 0?{value:persisted.value}:{}}}function normalizeUnknownInput(channelId,input){const kind=input.kind==="secret"||input.kind==="config"?input.kind:"config";return{channelId,inputId:input.inputId,kind,required:input.required===true,...typeof input.sourceEnv==="string"?{sourceEnv:input.sourceEnv}:{},...typeof input.statePath==="string"?{statePath:input.statePath}:{},...input.credentialAvailable!==void 0?{credentialAvailable:input.credentialAvailable}:{},...input.value!==void 0?{value:input.value}:{}}}function requiredInputsAvailable(manifest,inputs){if(!manifest)return true;return manifest.inputs.every(manifestInput=>{if(!manifestInput.required)return true;const input=inputs.find(entry=>entry.inputId===manifestInput.id);if(!input)return false;if(input.kind==="secret")return input.credentialAvailable===true;if(input.value===void 0)return false;return typeof input.value==="string"?input.value.trim().length>0:true})}function normalizePersistedCredentialBindings(plan,channels,manifestRegistry,environment){const persisted=plan.credentialBindings??[];if(Array.isArray(plan.credentialBindings)&&plan.channels.every(hasFullChannelShape)&&persisted.every(hasFullPersistedCredentialBindingShape)){return normalizeFullPersistedCredentialBindings(persisted)}const manifests=channels.flatMap(channel=>{const manifest=manifestRegistry.get(channel.channelId);return manifest?[manifest]:[]});const planForBindings={...plan,channels,credentialBindings:[],networkPolicy:{presets:[],entries:[]},agentRender:[],buildSteps:[],runtimeSetup:{nodePreloads:[],envAliases:[],secretScans:[]},stateUpdates:[],healthChecks:[]};const generated=credentialBindingsFromManifests(planForBindings,manifests,new Map(channels.map(channel=>[channel.channelId,channel.inputs])),environment);return generated.map(binding=>overlayPersistedCredentialBinding(binding,persisted))}function credentialBindingsFromManifests(plan,manifests,inputRegistry,environment){const context=compilerContext(plan);return manifests.flatMap(manifest=>planCredentialBindings(manifest,context,inputRegistry.get(manifest.id)??[],environment).map(binding=>overlayPersistedCredentialBinding(binding,plan.credentialBindings)))}function overlayPersistedCredentialBinding(binding,persisted){const match=persisted.find(candidate=>credentialBindingMatches(binding,candidate));if(!match)return binding;return{...binding,credentialAvailable:typeof match.credentialAvailable==="boolean"?match.credentialAvailable:binding.credentialAvailable,...typeof match.credentialHash==="string"&&match.credentialHash.length>0?{credentialHash:match.credentialHash}:binding.credentialHash?{credentialHash:binding.credentialHash}:{}}}function credentialBindingMatches(binding,candidate){if(candidate.channelId&&candidate.channelId!==binding.channelId)return false;if(candidate.providerEnvKey&&candidate.providerEnvKey===binding.providerEnvKey)return true;if(candidate.credentialId&&candidate.credentialId===binding.credentialId)return true;if(candidate.sourceInput&&candidate.sourceInput===binding.sourceInput)return true;return false}function hasFullChannelShape(channel){return typeof channel.displayName==="string"&&typeof channel.authMode==="string"&&typeof channel.active==="boolean"&&typeof channel.selected==="boolean"&&typeof channel.configured==="boolean"&&typeof channel.disabled==="boolean"&&Array.isArray(channel.inputs)}function normalizeRuntimeSetup(setup){return{nodePreloads:Array.isArray(setup?.nodePreloads)?[...setup.nodePreloads]:[],envAliases:Array.isArray(setup?.envAliases)?[...setup.envAliases]:[],secretScans:Array.isArray(setup?.secretScans)?[...setup.secretScans]:[]}}function compilerContext(plan){return{sandboxName:plan.sandboxName,agent:plan.agent,workflow:plan.workflow,isInteractive:false,configuredChannels:plan.channels.map(channel=>channel.channelId),disabledChannels:plan.disabledChannels,credentialAvailability:credentialAvailabilityFromPlan(plan)}}function credentialAvailabilityFromPlan(plan){const availability={};for(const channel of plan.channels){for(const input of channel.inputs){if(input.kind!=="secret"||input.credentialAvailable!==true)continue;availability[`${channel.channelId}.${input.inputId}`]=true;if(input.sourceEnv)availability[input.sourceEnv]=true}}for(const credential of plan.credentialBindings){if(!credential.credentialAvailable)continue;availability[credential.credentialId]=true;availability[`${credential.channelId}.${credential.credentialId}`]=true;availability[`${credential.channelId}.${credential.sourceInput}`]=true;availability[credential.providerEnvKey]=true}return availability}function normalizeMessagingChannelId(channelId){return channelId.trim().toLowerCase()}function enabledPlanChannels(plan){const disabled=new Set((plan.disabledChannels??[]).map(normalizeMessagingChannelId).filter(Boolean));return plan.channels.filter(channel=>{const channelId=normalizeMessagingChannelId(channel.channelId);return channelId.length>0&&channel.active&&!channel.disabled&&!disabled.has(channelId)})}function selectActiveMessagingChannelIds(plan){const seen=new Set;const channels=[];for(const item of enabledPlanChannels(plan)){const channel=normalizeMessagingChannelId(item.channelId);if(!channel||seen.has(channel))continue;seen.add(channel);channels.push(channel)}return channels}function selectEnabledMessagingAgentRender(plan){const active=new Set(selectActiveMessagingChannelIds(plan));return plan.agentRender.filter(render=>render.agent===plan.agent&&active.has(normalizeMessagingChannelId(render.channelId)))}function selectEnabledPostAgentInstallBuildFiles(plan){const active=new Set(selectActiveMessagingChannelIds(plan));const channels=enabledPlanChannels(plan);return plan.buildSteps.filter(step=>{const channelId=normalizeMessagingChannelId(step.channelId);if(!active.has(channelId)||step.kind!=="build-file")return false;if(!step.hookId)return true;const matchingChannels=channels.filter(channel=>normalizeMessagingChannelId(channel.channelId)===channelId);if(matchingChannels.length!==1)return false;const matchedHook=matchingChannels[0]?.hooks?.find(hook=>hook.id===step.hookId);return matchedHook!==void 0&&matchedHook.phase==="post-agent-install"})}function parseSandboxMessagingPlan(value,options={}){if(!isObjectRecord(value)||value.schemaVersion!==1||typeof value.sandboxName!=="string"||typeof value.agent!=="string"||typeof value.workflow!=="string"||!Array.isArray(value.channels)||!Array.isArray(value.disabledChannels)||!isOptionalObjectArray(value,"credentialBindings")||Object.hasOwn(value,"networkPolicy")&&!isObjectRecord(value.networkPolicy)||!isOptionalObjectArray(value,"agentRender")||!isOptionalObjectArray(value,"buildSteps")||!isRuntimeSetup(value.runtimeSetup)||!isOptionalObjectArray(value,"stateUpdates")||!isOptionalObjectArray(value,"healthChecks")){return null}if(options.sandboxName&&value.sandboxName!==options.sandboxName)return null;if(options.agent&&value.agent!==options.agent)return null;const supported=Array.isArray(options.supportedChannelIds)?new Set(options.supportedChannelIds):null;const normalizedChannelIds=new Set;for(const channel of value.channels){if(!isObjectRecord(channel)||typeof channel.channelId!=="string")return null;const normalizedChannelId=normalizeMessagingChannelId(channel.channelId);if(!normalizedChannelId||normalizedChannelId!==channel.channelId||normalizedChannelIds.has(normalizedChannelId)){return null}if(Object.hasOwn(channel,"configured")&&typeof channel.configured!=="boolean"){return null}if(Object.hasOwn(channel,"active")&&typeof channel.active!=="boolean")return null;if(Object.hasOwn(channel,"disabled")&&typeof channel.disabled!=="boolean")return null;if(Object.hasOwn(channel,"inputs")&&!Array.isArray(channel.inputs))return null;if(Object.hasOwn(channel,"hostForward")&&!isHostForward(channel.hostForward))return null;if(Object.hasOwn(channel,"hooks")&&!Array.isArray(channel.hooks))return null;if(Array.isArray(channel.inputs)&&channel.inputs.some(input=>!isObjectRecord(input)||typeof input.inputId!=="string"||Object.hasOwn(input,"channelId")&&input.channelId!==normalizedChannelId)){return null}if(Array.isArray(channel.hooks)&&channel.hooks.some(hook=>!isObjectRecord(hook)||Object.hasOwn(hook,"channelId")&&hook.channelId!==normalizedChannelId)){return null}if(Object.hasOwn(channel,"hostForward")&&isObjectRecord(channel.hostForward)&&channel.hostForward.channelId!==normalizedChannelId){return null}if(supported&&!supported.has(channel.channelId))return null;normalizedChannelIds.add(normalizedChannelId)}if(!value.disabledChannels.every(isCanonicalMessagingChannelId))return null;const disabledChannelIds=new Set(value.disabledChannels);if(disabledChannelIds.size!==value.disabledChannels.length||[...disabledChannelIds].some(channelId=>!normalizedChannelIds.has(channelId))||value.channels.some(channel=>isObjectRecord(channel)&&channel.disabled===true!==disabledChannelIds.has(String(channel.channelId)))){return null}if(!hasCanonicalChannelReferences(value.credentialBindings)||!hasMatchingAgentRenderEntries(value.agentRender,value.agent)||!hasCanonicalChannelReferences(value.agentRender)||!hasCanonicalChannelReferences(value.buildSteps)||!hasCanonicalChannelReferences(value.stateUpdates)||!hasCanonicalChannelReferences(value.healthChecks)||!hasCanonicalNetworkPolicyReferences(value.networkPolicy)||!hasCanonicalRuntimeSetupReferences(value.runtimeSetup)){return null}return cloneSandboxMessagingPlan(normalizePersistedSandboxMessagingPlanShape(value,options.environment))}function hasMatchingAgentRenderEntries(value,agent){return!Array.isArray(value)||value.every(render=>isObjectRecord(render)&&render.agent===agent)}function cloneSandboxMessagingPlan(plan){return JSON.parse(JSON.stringify(plan))}function isOptionalObjectArray(value,key){if(!Object.hasOwn(value,key))return true;const entries=value[key];return Array.isArray(entries)&&entries.every(isObjectRecord)}function isHostForward(value){return isObjectRecord(value)&&typeof value.channelId==="string"&&typeof value.port==="number"&&Number.isInteger(value.port)&&value.port>=1&&value.port<=65535&&typeof value.label==="string"}function isRuntimeSetup(value){if(value===void 0)return true;return isObjectRecord(value)&&Array.isArray(value.nodePreloads)&&Array.isArray(value.envAliases)&&Array.isArray(value.secretScans)&&value.nodePreloads.every(isObjectRecord)&&value.envAliases.every(isObjectRecord)&&value.secretScans.every(isObjectRecord)}function isCanonicalMessagingChannelId(value){return typeof value==="string"&&value.length>0&&normalizeMessagingChannelId(value)===value}function hasCanonicalChannelReferences(value){return value===void 0||Array.isArray(value)&&value.every(entry=>isObjectRecord(entry)&&isCanonicalMessagingChannelId(entry.channelId))}function hasCanonicalNetworkPolicyReferences(value){if(!isObjectRecord(value)||!Object.hasOwn(value,"entries"))return true;return hasCanonicalChannelReferences(value.entries)}function hasCanonicalRuntimeSetupReferences(value){if(value===void 0)return true;if(!isObjectRecord(value))return false;return["nodePreloads","envAliases","secretScans"].every(field=>hasCanonicalChannelReferences(value[field]))}var import_node_buffer2=require("node:buffer");var import_node_crypto2=require("node:crypto");var import_node_util=require("node:util");function listMessagingCredentialEnvAssignments(options={}){return selectManifests(options).flatMap(manifest=>{const credentialsByTemplate=new Map(manifest.credentials.map(credential=>[`{{credential.${credential.id}.placeholder}}`,credential]));return manifest.render.flatMap(render=>{if(options.agent&&render.agent!==options.agent)return[];if(render.kind!=="env-lines")return[];return render.lines.flatMap(line=>{const separator=line.indexOf("=");if(separator<=0)return[];const credential=credentialsByTemplate.get(line.slice(separator+1));if(!credential)return[];return[{channelId:manifest.id,agent:render.agent,sourceEnvKey:credential.providerEnvKey,targetEnvKey:line.slice(0,separator),placeholder:credential.placeholder}]})})})}function selectManifests(options){const manifests=options.manifests??BUILT_IN_CHANNEL_MANIFESTS;const agent=options.agent;const selected=agent?manifests.filter(manifest=>manifest.supportedAgents.includes(agent)):manifests;return[...selected]}function authorizeMessagingManagedStartupFields(entry,section){if(section==="agentRender")return authorizeTeamsOpenClawWebhookField(entry);if(!isPlainDataObject3(entry))return[];const contract=WECHAT_OPENCLAW_ACCOUNT_FILE_CONTRACT;if(ownDataPropertyValue3(entry,"channelId")!==contract.channelId||ownDataPropertyValue3(entry,"hookId")!==contract.planHookId||ownDataPropertyValue3(entry,"handler")!==contract.handlerId||ownDataPropertyValue3(entry,"outputId")!==contract.outputId||ownDataPropertyValue3(entry,"kind")!==contract.kind||ownDataPropertyValue3(entry,"required")!==contract.required){return[]}return authorizeWechatAccountFilePlaceholders(ownDataPropertyValue3(entry,"value")).map(authorization=>({...authorization,path:["value",...authorization.path]}))}function isPlainDataObject3(value){if(value===null||typeof value!=="object"||Array.isArray(value))return false;const prototype=Object.getPrototypeOf(value);return prototype===Object.prototype||prototype===null}function ownDataPropertyValue3(value,key){const descriptor=Object.getOwnPropertyDescriptor(value,key);return descriptor&&"value"in descriptor?descriptor.value:void 0}var DCODE_UPSTREAM_PROVIDER_RE=/^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/u;function isValidDcodeUpstreamProvider(value){return DCODE_UPSTREAM_PROVIDER_RE.test(value)}var MANAGED_STARTUP_PROFILE_SCHEMA_VERSION=1;var MANAGED_STARTUP_PROFILE_MAX_BYTES=64*1024;var MANAGED_STARTUP_PROFILE_MAX_ENCODED_BYTES=Math.ceil(MANAGED_STARTUP_PROFILE_MAX_BYTES/3)*4;var MAX_IDENTIFIER_BYTES=256;var MAX_MODEL_BYTES2=1024;var MAX_URL_BYTES2=2048;var MAX_LIST_ITEMS=128;var MAX_JSON_NODES=4096;var MAX_JSON_DEPTH=32;var MAX_TUNING_INTEGER=1e9;var MIN_HERMES_CONTEXT_WINDOW=64e3;var SHA256_RE=/^[a-f0-9]{64}$/;var CONTROL_CHARACTER_RE2=/[\u0000-\u001f\u007f-\u009f]/u;var BASE64URL_RE=/^[A-Za-z0-9_-]+$/;var RAW_CA_PEM_RE=/-----BEGIN (?:TRUSTED )?CERTIFICATE-----/iu;var RAW_CA_PEM_BASE64_RE=/^LS0tLS1CRUdJTi(?:BDRVJUSUZJQ0FURS0tLS0t|BUlVTVEVEIENFUlRJRklDQVRFLS0tLS0)/u;var RAW_CA_DER_BASE64_RE=/^MII[A-Za-z0-9+/=\r\n]{253,}$/u;var RAW_CA_DATA_URI_RE=/data:application\/(?:pkix-cert|x-x509-ca-cert);base64,MII[A-Za-z0-9+/=]{253,}/iu;var URL_CANDIDATE_RE=/[A-Za-z][A-Za-z0-9+.-]*:\/\/[^\s"'<>]+/gu;var UTF8_DECODER=new import_node_util.TextDecoder("utf-8",{fatal:true});var CREDENTIAL_SHAPED_NAME_PATTERN=/(?:^|[_-])(?:api[_-]?key|access[_-]?key|secret[_-]?key|auth[_-]?token|refresh[_-]?token|access[_-]?token|client[_-]?secret|private[_-]?key|pass[_-]?code|personal[_-]?access[_-]?token|connection[_-]?string|webhook(?:[_-]?url)?|key|secret|token|password|passwd|passcode|auth|authorization|credential|credentials|bearer|bearer[_-]?token|cookie|cookies|pat|private|privatekey|pin|webhookurl|dsn|connectionstring)(?:$|[_-])/iu;var CREDENTIAL_COMPOUND_NAME_PATTERN=/^(?:access|refresh|client|bearer|auth|api|private|signing|session|bot|app|resolved)(?:token|key|secret|password)$/iu;var CREDENTIAL_CAMEL_SUFFIX_PATTERN=/(?:apiKey|accessKey|secretKey|authToken|refreshToken|accessToken|clientSecret|privateKey|passcode|password|passwd|passphrase|bearerToken|botToken|appToken|sessionToken|signingKey|secretPublicKey|personalAccessToken|connectionString|webhookUrl)$/iu;var CREDENTIAL_CAMEL_BOUNDARY_PATTERN=/[a-z0-9](?:Token|Key|Secret|Password|Passphrase|Pat)$/u;var CREDENTIAL_ENV_NAME_PATTERN=/^(?:[A-Z0-9]+_)*(?:TOKEN|KEY|SECRET|PASSWORD|PASSWD|PASS|PASSPHRASE|CREDENTIAL)S?$/u;var CREDENTIAL_HEADER_NAME_PATTERN=/^(?:authorization|proxy-authorization|cookie|set-cookie|.+-(?:key|token|secret|password|passphrase|credential|auth)s?)$/iu;var PUBLIC_KEY_NAME_PATTERN=/^public[-_]?keys?$/iu;var PASS_CREDENTIAL_NAME_PATTERN=/(?:^|[-_])pass(?:wd)?$/iu;var NON_SECRET_KEY_METADATA_NAMES=new Set(["envKey","installCacheEnvKey","providerEnvKey","stateKey"]);var MESSAGING_CREDENTIAL_PLACEHOLDER_RE=/^(?:openshell:resolve:env:|[A-Za-z0-9]+-OPENSHELL-RESOLVE-ENV-)(?:v[0-9]+_)?[A-Z][A-Z0-9_]*$/u;var MESSAGING_CREDENTIAL_ENV_ALIASES=new Set(listMessagingCredentialEnvAssignments().filter(({sourceEnvKey,targetEnvKey})=>sourceEnvKey!==targetEnvKey).map(({agent,sourceEnvKey,targetEnvKey})=>`${agent}\0${sourceEnvKey}\0${targetEnvKey}`));var JSON_ARRAY_INDEX_SEGMENT_RE=/^\[(?:0|[1-9][0-9]*)\]$/u;var SECRET_VALUE_PATTERNS=[/nvapi-[A-Za-z0-9_-]{10,}/u,/nvcf-[A-Za-z0-9_-]{10,}/u,/ghp_[A-Za-z0-9_-]{10,}/u,/github_pat_[A-Za-z0-9_]{30,}/u,/sk-(?:proj-|ant-)?[A-Za-z0-9_-]{10,}/u,/(?:xox[bpas]|xapp)-[A-Za-z0-9-]{10,}/u,/A(?:K|S)IA[A-Z0-9]{16}/u,/hf_[A-Za-z0-9]{10,}/u,/glpat-[A-Za-z0-9_-]{10,}/u,/gsk_[A-Za-z0-9]{10,}/u,/pypi-[A-Za-z0-9_-]{10,}/u,/tvly-[A-Za-z0-9_-]{10,}/u,/lsv2_(?:pt|sk)_[A-Za-z0-9]{10,}(?:_[A-Za-z0-9]+)*/u,/\bbot\d{8,10}:[A-Za-z0-9_-]{35}\b/u,/\b\d{8,10}:[A-Za-z0-9_-]{35}\b/u,/\b[A-Za-z0-9]{24}\.[A-Za-z0-9_-]{6}\.[A-Za-z0-9_-]{27,}\b/u,/\beyJ[A-Za-z0-9_-]{5,}\.[A-Za-z0-9_-]{2,}\.[A-Za-z0-9_-]{10,}\b/u,/\bBearer\s+[A-Za-z0-9_.+/=-]{10,}/iu,/-----BEGIN (?:[A-Z0-9]+ )?PRIVATE KEY-----/u];var MANAGED_STARTUP_INFERENCE_APIS=["openai-completions","openai-responses","anthropic-messages"];var MANAGED_STARTUP_REASONING_EFFORTS=["default","low","medium","high"];var MANAGED_STARTUP_DCODE_AUTO_APPROVAL_MODES=["disabled","thread-opt-in"];var MANAGED_STARTUP_HERMES_TOOL_GATEWAYS=["nous-web","nous-image","nous-audio","nous-browser","nous-code"];var MANAGED_STARTUP_AGENTS=["openclaw","hermes","langchain-deepagents-code","pi"];var MANAGED_STARTUP_MESSAGING_AGENTS=["openclaw","hermes"];function freezeAgentCapabilities(capabilities){return Object.freeze({...capabilities,inferenceApis:Object.freeze([...capabilities.inferenceApis]),dashboardModes:Object.freeze([...capabilities.dashboardModes]),inputModalities:Object.freeze([...capabilities.inputModalities]),webSearchProviders:Object.freeze([...capabilities.webSearchProviders]),toolGateways:Object.freeze([...capabilities.toolGateways]),tuningFields:Object.freeze([...capabilities.tuningFields])})}var PROFILE_CAPABILITIES={openclaw:{inferenceApis:[...MANAGED_STARTUP_INFERENCE_APIS],dashboardModes:["loopback","remote"],inputModalities:["text","image"],webSearchProviders:["brave","tavily"],toolGateways:[],tuningFields:["contextWindow","maxTokens","reasoning","reasoningEffort"],supportsMessaging:true,supportsInferenceCompatibility:true,supportsUpstreamEndpoint:false,supportsHostProxyIntent:true,supportsPrimaryModelRef:true,supportsAgentTimeout:true,supportsHeartbeat:true,supportsExtraAgents:true,supportsDeviceAuth:true,observability:"openclaw-otel",supportsMinimalBootstrap:true},hermes:{inferenceApis:[...MANAGED_STARTUP_INFERENCE_APIS],dashboardModes:["disabled","loopback-forwarded"],inputModalities:[],webSearchProviders:["tavily"],toolGateways:[...MANAGED_STARTUP_HERMES_TOOL_GATEWAYS],tuningFields:["contextWindow"],supportsMessaging:true,supportsInferenceCompatibility:false,supportsUpstreamEndpoint:false,supportsHostProxyIntent:true,supportsPrimaryModelRef:false,supportsAgentTimeout:false,supportsHeartbeat:false,supportsExtraAgents:false,supportsDeviceAuth:false,observability:"none",supportsMinimalBootstrap:false},"langchain-deepagents-code":{inferenceApis:["openai-completions"],dashboardModes:["disabled"],inputModalities:[],webSearchProviders:[],toolGateways:[],tuningFields:["reasoningEffort"],supportsMessaging:false,supportsInferenceCompatibility:false,supportsUpstreamEndpoint:true,supportsHostProxyIntent:true,supportsPrimaryModelRef:false,supportsAgentTimeout:false,supportsHeartbeat:false,supportsExtraAgents:false,supportsDeviceAuth:false,observability:"dcode-marker",supportsMinimalBootstrap:false},pi:{inferenceApis:["openai-completions"],dashboardModes:["disabled"],inputModalities:[],webSearchProviders:[],toolGateways:[],tuningFields:["contextWindow","maxTokens","reasoning"],supportsMessaging:false,supportsInferenceCompatibility:false,supportsUpstreamEndpoint:false,supportsHostProxyIntent:true,supportsPrimaryModelRef:false,supportsAgentTimeout:false,supportsHeartbeat:false,supportsExtraAgents:false,supportsDeviceAuth:false,observability:"none",supportsMinimalBootstrap:false}};for(const agent of MANAGED_STARTUP_AGENTS){Object.defineProperty(PROFILE_CAPABILITIES,agent,{configurable:false,enumerable:true,value:freezeAgentCapabilities(PROFILE_CAPABILITIES[agent]),writable:false})}var MANAGED_STARTUP_PROFILE_CAPABILITIES=Object.freeze(PROFILE_CAPABILITIES);function affordance(input,profilePath,source="docker-arg",representation="value"){return{input,profilePath,source,representation}}var HOST_PROXY_AFFORDANCES=[affordance("HTTP_PROXY","proxy.hostHttpUrl","runtime-env"),affordance("http_proxy","proxy.hostHttpUrl","runtime-env","derived"),affordance("HTTPS_PROXY","proxy.hostHttpsUrl","runtime-env"),affordance("https_proxy","proxy.hostHttpsUrl","runtime-env","derived"),affordance("NO_PROXY","proxy.hostNoProxy","runtime-env"),affordance("no_proxy","proxy.hostNoProxy","runtime-env","derived")];var MANAGED_STARTUP_PROFILE_AFFORDANCE_INVENTORY={openclaw:[affordance("NEMOCLAW_MODEL","inference.model"),affordance("NEMOCLAW_INFERENCE_PROVIDER_ID","inference.routeProvider"),affordance("NEMOCLAW_UPSTREAM_PROVIDER","inference.upstreamProvider"),affordance("NEMOCLAW_PRIMARY_MODEL_REF","inference.primaryModelRef"),affordance("NEMOCLAW_INFERENCE_BASE_URL","inference.routedBaseUrl"),affordance("NEMOCLAW_INFERENCE_API","inference.api"),affordance("NEMOCLAW_INFERENCE_COMPAT_B64","inference.compatibility"),affordance("NEMOCLAW_INFERENCE_INPUTS","inference.inputModalities"),affordance("NEMOCLAW_CONTEXT_WINDOW","tuning.contextWindow"),affordance("NEMOCLAW_MAX_TOKENS","tuning.maxTokens"),affordance("NEMOCLAW_REASONING","tuning.reasoning"),affordance("NEMOCLAW_REASONING_EFFORT","tuning.reasoningEffort"),affordance("NEMOCLAW_TOOL_DISCLOSURE","tools.disclosure"),affordance("NEMOCLAW_AGENT_TIMEOUT","agentConfig.agentTimeoutSeconds"),affordance("NEMOCLAW_AGENT_HEARTBEAT_EVERY","agentConfig.heartbeatEvery"),affordance("NEMOCLAW_EXTRA_AGENTS_JSON_B64","agentConfig.extraAgents"),affordance("NEMOCLAW_DISABLE_DEVICE_AUTH","agentConfig.deviceAuth.disabled"),affordance("NEMOCLAW_DEVICE_AUTH_OPT_OUT_SOURCE","agentConfig.deviceAuth.optOutSource"),affordance("NEMOCLAW_WEB_SEARCH_ENABLED","agentConfig.webSearch.enabled"),affordance("NEMOCLAW_WEB_SEARCH_PROVIDER","agentConfig.webSearch.provider"),affordance("NEMOCLAW_OPENCLAW_OTEL","agentConfig.otel.enabled"),affordance("NEMOCLAW_OPENCLAW_OTEL_ENDPOINT","agentConfig.otel.endpointUrl"),affordance("NEMOCLAW_OPENCLAW_OTEL_SERVICE_NAME","agentConfig.otel.serviceName"),affordance("NEMOCLAW_OPENCLAW_OTEL_SAMPLE_RATE","agentConfig.otel.sampleRate"),affordance("CHAT_UI_URL","dashboard.url"),affordance("NEMOCLAW_DASHBOARD_BIND","dashboard.bindAddress"),affordance("NEMOCLAW_WSL_DASHBOARD_EXPOSURE","dashboard.wslExposure"),affordance("NEMOCLAW_DASHBOARD_PORT","dashboard.port","runtime-env"),affordance("NEMOCLAW_PROXY_HOST","proxy.managedHost"),affordance("NEMOCLAW_PROXY_PORT","proxy.managedPort"),affordance("NEMOCLAW_MESSAGING_PLAN_B64","messaging.plan"),affordance("NEMOCLAW_MINIMAL_BOOTSTRAP","agentConfig.minimalBootstrap","runtime-env"),affordance("NEMOCLAW_CORPORATE_CA_B64","corporateCa.bundleSha256","host-material","digest-handoff"),...HOST_PROXY_AFFORDANCES],hermes:[affordance("NEMOCLAW_MODEL","inference.model"),affordance("NEMOCLAW_INFERENCE_PROVIDER_ID","inference.routeProvider"),affordance("NEMOCLAW_UPSTREAM_PROVIDER","inference.upstreamProvider"),affordance("NEMOCLAW_INFERENCE_BASE_URL","inference.routedBaseUrl"),affordance("NEMOCLAW_INFERENCE_API","inference.api"),affordance("NEMOCLAW_CONTEXT_WINDOW","tuning.contextWindow"),affordance("NEMOCLAW_TOOL_DISCLOSURE","tools.disclosure"),affordance("NEMOCLAW_HERMES_TOOL_GATEWAY_BROKER","tools.enabledGateways","docker-arg","derived"),affordance("NEMOCLAW_HERMES_TOOL_GATEWAY_PRESETS_B64","tools.enabledGateways"),affordance("NEMOCLAW_WEB_SEARCH_ENABLED","agentConfig.webSearch.enabled"),affordance("NEMOCLAW_WEB_SEARCH_PROVIDER","agentConfig.webSearch.provider"),affordance("NEMOCLAW_MESSAGING_PLAN_B64","messaging.plan"),affordance("CHAT_UI_URL","dashboard.url"),affordance("NEMOCLAW_DASHBOARD_PORT","dashboard.publicPort","runtime-env"),affordance("NEMOCLAW_HERMES_DASHBOARD","dashboard.mode","runtime-env"),affordance("NEMOCLAW_HERMES_DASHBOARD_PORT","dashboard.publicPort","runtime-env"),affordance("NEMOCLAW_HERMES_DASHBOARD_INTERNAL_PORT","dashboard.internalPort","runtime-env"),affordance("NEMOCLAW_HERMES_DASHBOARD_TUI","dashboard.tuiEnabled","runtime-env"),affordance("NEMOCLAW_PROXY_HOST","proxy.managedHost","runtime-env"),affordance("NEMOCLAW_PROXY_PORT","proxy.managedPort","runtime-env"),affordance("NEMOCLAW_CORPORATE_CA_B64","corporateCa.bundleSha256","host-material","digest-handoff"),...HOST_PROXY_AFFORDANCES],"langchain-deepagents-code":[affordance("NEMOCLAW_MODEL","inference.model"),affordance("NEMOCLAW_INFERENCE_PROVIDER_ID","inference.routeProvider"),affordance("NEMOCLAW_UPSTREAM_PROVIDER","inference.upstreamProvider"),affordance("NEMOCLAW_UPSTREAM_ENDPOINT_URL","inference.upstreamEndpointUrl"),affordance("NEMOCLAW_INFERENCE_BASE_URL","inference.routedBaseUrl"),affordance("NEMOCLAW_INFERENCE_API","inference.api"),affordance("NEMOCLAW_REASONING_EFFORT","tuning.reasoningEffort"),affordance("NEMOCLAW_TOOL_DISCLOSURE","tools.disclosure"),affordance("NEMOCLAW_DCODE_AUTO_APPROVAL","agentConfig.autoApprovalMode"),affordance("NEMOCLAW_PROXY_HOST","proxy.managedHost"),affordance("NEMOCLAW_PROXY_PORT","proxy.managedPort"),affordance("NEMOCLAW_OBSERVABILITY","agentConfig.observabilityEnabled","runtime-env"),affordance("NEMOCLAW_CORPORATE_CA_B64","corporateCa.bundleSha256","host-material","digest-handoff"),...HOST_PROXY_AFFORDANCES],pi:[affordance("NEMOCLAW_MODEL","inference.model"),affordance("NEMOCLAW_INFERENCE_PROVIDER_ID","inference.routeProvider"),affordance("NEMOCLAW_UPSTREAM_PROVIDER","inference.upstreamProvider"),affordance("NEMOCLAW_INFERENCE_BASE_URL","inference.routedBaseUrl"),affordance("NEMOCLAW_INFERENCE_API","inference.api"),affordance("NEMOCLAW_CONTEXT_WINDOW","tuning.contextWindow"),affordance("NEMOCLAW_MAX_TOKENS","tuning.maxTokens"),affordance("NEMOCLAW_REASONING","tuning.reasoning"),affordance("NEMOCLAW_TOOL_DISCLOSURE","tools.disclosure"),affordance("NEMOCLAW_PROXY_HOST","proxy.managedHost"),affordance("NEMOCLAW_PROXY_PORT","proxy.managedPort"),affordance("NEMOCLAW_CORPORATE_CA_B64","corporateCa.bundleSha256","host-material","digest-handoff"),...HOST_PROXY_AFFORDANCES]};function deferredRuntimeInput(input,owner,reason,admission="managed-launch-forwarded"){return Object.freeze({input,owner,admission,reason})}var MANAGED_STARTUP_PROFILE_DEFERRED_RUNTIME_INPUTS=Object.freeze({openclaw:Object.freeze([deferredRuntimeInput("NEMOCLAW_AUTO_PAIR_DEADLINE_SECS","application-environment","operator scheduler tuning is applied by the application environment transaction"),deferredRuntimeInput("NEMOCLAW_AUTO_PAIR_FAST_DEADLINE_SECS","application-environment","operator scheduler tuning is applied by the application environment transaction"),deferredRuntimeInput("NEMOCLAW_AUTO_PAIR_FAST_REENTRY_INTERVAL_SECS","application-environment","operator scheduler tuning is applied by the application environment transaction"),deferredRuntimeInput("NEMOCLAW_AUTO_PAIR_FAST_REENTRY_POLLS","application-environment","operator scheduler tuning is applied by the application environment transaction"),deferredRuntimeInput("NEMOCLAW_AUTO_PAIR_RUN_TIMEOUT_SECS","application-environment","operator scheduler tuning is applied by the application environment transaction"),deferredRuntimeInput("NEMOCLAW_AUTO_PAIR_SLOW_INTERVAL_SECS","application-environment","operator scheduler tuning is applied by the application environment transaction"),deferredRuntimeInput("NEMOCLAW_MCP_SHADOW_DIAGNOSTICS","application-environment","operator shadow-diagnostics tuning is applied by the application environment transaction"),deferredRuntimeInput("NEMOCLAW_MCP_TOOLS_LIST_TIMEOUT_MS","application-environment","operator MCP discovery timeout tuning is applied by the application environment transaction"),deferredRuntimeInput("OPENCLAW_HOME","fixed-image-contract","the managed image and agent definition own this fixed runtime layout path"),deferredRuntimeInput("OPENCLAW_STATE_DIR","fixed-image-contract","the managed image and agent definition own this fixed runtime layout path"),deferredRuntimeInput("OPENCLAW_WORKSPACE_DIR","fixed-image-contract","the managed image and agent definition own this fixed runtime layout path"),deferredRuntimeInput("NEMOCLAW_EXTRA_PLACEHOLDER_KEYS","credential-plumbing","credential provider construction owns key metadata outside the secret-free profile")]),hermes:Object.freeze([deferredRuntimeInput("NEMOCLAW_EXTRA_PLACEHOLDER_KEYS","credential-plumbing","credential provider construction owns key metadata outside the secret-free profile")]),"langchain-deepagents-code":Object.freeze([deferredRuntimeInput("NEMOCLAW_SANDBOX_NAME","engine-identity","the lifecycle engine owns instance identity outside reusable startup intent"),deferredRuntimeInput("NEMOCLAW_EXTRA_PLACEHOLDER_KEYS","credential-plumbing","credential provider construction owns key metadata outside the secret-free profile")]),pi:Object.freeze([deferredRuntimeInput("NEMOCLAW_EXTRA_PLACEHOLDER_KEYS","credential-plumbing","credential provider construction owns key metadata outside the secret-free profile")])});function runtimeCleanupObligation(input,emittedFor,supportedFor,reason){return Object.freeze({input,emittedFor:Object.freeze([...emittedFor]),supportedFor:Object.freeze([...supportedFor]),owner:"application-environment",reason})}var MANAGED_STARTUP_RUNTIME_CLEANUP_OBLIGATIONS=Object.freeze([runtimeCleanupObligation("NEMOCLAW_DASHBOARD_BIND",["hermes"],["openclaw"],"generic managed-dashboard construction currently emits the OpenClaw-only bind control for Hermes"),runtimeCleanupObligation("NEMOCLAW_MINIMAL_BOOTSTRAP",["hermes","langchain-deepagents-code"],["openclaw"],"generic host-proxy construction currently emits the OpenClaw-only bootstrap control for other agents")]);var ManagedStartupProfileError=class extends Error{constructor(message){super(`Invalid managed startup profile: ${message}`);this.name="ManagedStartupProfileError"}};var PROFILE_KEYS=new Set(["schemaVersion","agent","agentConfig","inference","proxy","dashboard","tools","messaging","tuning","corporateCa"]);var INFERENCE_KEYS=new Set(["routeProvider","upstreamProvider","model","routedBaseUrl","upstreamEndpointUrl","api","primaryModelRef","compatibility","inputModalities"]);var PROXY_KEYS=new Set(["managedHost","managedPort","hostHttpUrl","hostHttpsUrl","hostNoProxy"]);var OPENCLAW_DASHBOARD_KEYS=new Set(["agent","mode","url","port","bindAddress","wslExposure"]);var HERMES_DASHBOARD_KEYS=new Set(["agent","mode","url","publicPort","internalPort","tuiEnabled"]);var DCODE_DASHBOARD_KEYS=new Set(["agent","mode"]);var TOOLS_KEYS=new Set(["disclosure","enabledGateways"]);var MESSAGING_KEYS=new Set(["plan"]);var TUNING_FIELD_ORDER=["contextWindow","maxTokens","reasoning","reasoningEffort"];var TUNING_KEYS=new Set(TUNING_FIELD_ORDER);var CORPORATE_CA_KEYS=new Set(["bundleSha256"]);var OPENCLAW_CONFIG_KEYS=new Set(["agent","webSearch","otel","agentTimeoutSeconds","heartbeatEvery","extraAgents","deviceAuth","minimalBootstrap"]);var HERMES_CONFIG_KEYS=new Set(["agent","webSearch","switchyardRouting"]);var DCODE_CONFIG_KEYS=new Set(["agent","autoApprovalMode","observabilityEnabled"]);var PI_CONFIG_KEYS=new Set(["agent"]);var PI_DASHBOARD_KEYS=new Set(["agent","mode"]);var WEB_SEARCH_KEYS=new Set(["enabled","provider"]);var OTEL_KEYS=new Set(["enabled","endpointUrl","serviceName","sampleRate"]);var DEVICE_AUTH_KEYS=new Set(["disabled","optOutSource"]);var EXTRA_AGENTS_KEYS=new Set(["agents","defaults","main"]);var MANAGED_STARTUP_AGENT_SET=new Set(MANAGED_STARTUP_AGENTS);var DCODE_AUTO_APPROVAL_MODE_SET=new Set(MANAGED_STARTUP_DCODE_AUTO_APPROVAL_MODES);var REASONING_EFFORT_SET=new Set(MANAGED_STARTUP_REASONING_EFFORTS);var HERMES_INTERNAL_API_PORT=18642;var HERMES_API_PORT_RANGE_START=8642;var HERMES_API_PORT_RANGE_END=8652;function isHermesApiPort(port){return port>=HERMES_API_PORT_RANGE_START&&port<=HERMES_API_PORT_RANGE_END}function isHermesReservedApiPort(port){return port===HERMES_INTERNAL_API_PORT||isHermesApiPort(port)}var HERMES_RESERVED_API_PORT_LABEL=`${HERMES_API_PORT_RANGE_START}-${HERMES_API_PORT_RANGE_END} or ${HERMES_INTERNAL_API_PORT}`;function isPlainObject2(value){if(typeof value!=="object"||value===null||Array.isArray(value))return false;const prototype=Object.getPrototypeOf(value);return prototype===Object.prototype||prototype===null}function isCredentialShapedName(name){if(PUBLIC_KEY_NAME_PATTERN.test(name)||NON_SECRET_KEY_METADATA_NAMES.has(name))return false;return CREDENTIAL_SHAPED_NAME_PATTERN.test(name)||CREDENTIAL_COMPOUND_NAME_PATTERN.test(name)||CREDENTIAL_CAMEL_SUFFIX_PATTERN.test(name)||CREDENTIAL_CAMEL_BOUNDARY_PATTERN.test(name)||CREDENTIAL_ENV_NAME_PATTERN.test(name)||CREDENTIAL_HEADER_NAME_PATTERN.test(name)||PASS_CREDENTIAL_NAME_PATTERN.test(name)}function valueLooksLikeSecret(value){for(let index=0;index=5&&path5[0]==="messaging"&&path5[1]==="plan"&&path5[2]==="agentRender"&&JSON_ARRAY_INDEX_SEGMENT_RE.test(path5[3]??"")&&path5[4]==="value";const isAuthorizedBuildStepPlaceholder=allowedBuildStepPlaceholders.has(buildStepPlaceholderKey(path5,value));return isCredentialBindingPlaceholder||isAgentRenderValuePlaceholder||isAuthorizedBuildStepPlaceholder}function requiresMessagingSchemaFieldAuthorization(path5){const fieldName=path5[path5.length-1];return fieldName==="webhook"}function messagingAuthorizedFieldKey(path5){return JSON.stringify(path5)}function buildStepPlaceholderKey(path5,value){return JSON.stringify([path5,value])}function messagingCredentialPlaceholderEnvKey(value){if(!MESSAGING_CREDENTIAL_PLACEHOLDER_RE.test(value))return null;const marker=value.startsWith("openshell:resolve:env:")?"openshell:resolve:env:":"-OPENSHELL-RESOLVE-ENV-";const key=value.slice(value.indexOf(marker)+marker.length);return key.replace(/^v[0-9]+_/u,"")}function containsMessagingCredentialPlaceholder(value){return value.includes("openshell:resolve:env:")||value.includes("-OPENSHELL-RESOLVE-ENV-")}function isMessagingCredentialPlaceholderAssignment(selectedAgent,path5,value){if(path5.length!==6||path5[0]!=="messaging"||path5[1]!=="plan"||path5[2]!=="agentRender"||!JSON_ARRAY_INDEX_SEGMENT_RE.test(path5[3]??"")||path5[4]!=="lines"||!JSON_ARRAY_INDEX_SEGMENT_RE.test(path5[5]??"")){return false}const separator=value.indexOf("=");if(separator<=0||value.indexOf("=",separator+1)!==-1)return false;const envKey=value.slice(0,separator);const placeholder=value.slice(separator+1);const placeholderEnvKey=messagingCredentialPlaceholderEnvKey(placeholder);return CREDENTIAL_ENV_NAME_PATTERN.test(envKey)&&placeholderEnvKey!==null&&(envKey===placeholderEnvKey||typeof selectedAgent==="string"&&MESSAGING_CREDENTIAL_ENV_ALIASES.has(`${selectedAgent}\0${placeholderEnvKey}\0${envKey}`))}function isMessagingRuntimeEnvAliasPath(path5){return path5.length===5&&path5[0]==="messaging"&&path5[1]==="plan"&&path5[2]==="runtimeSetup"&&path5[3]==="envAliases"&&JSON_ARRAY_INDEX_SEGMENT_RE.test(path5[4]??"")}function ownDataPropertyValue4(value,key){const descriptor=Object.getOwnPropertyDescriptor(value,key);return descriptor&&"value"in descriptor?descriptor.value:void 0}function isStockTeamsOpenClawWebhook(root,path5,value){if(path5.length!==6||path5[0]!=="messaging"||path5[1]!=="plan"||path5[2]!=="agentRender"||!JSON_ARRAY_INDEX_SEGMENT_RE.test(path5[3]??"")||path5[4]!=="value"||path5[5]!=="webhook"||!isPlainObject2(root)||ownDataPropertyValue4(root,"agent")!=="openclaw"){return false}const messaging=ownDataPropertyValue4(root,"messaging");if(!isPlainObject2(messaging))return false;const plan=ownDataPropertyValue4(messaging,"plan");if(!isPlainObject2(plan)||ownDataPropertyValue4(plan,"agent")!=="openclaw")return false;const agentRender=ownDataPropertyValue4(plan,"agentRender");if(!Array.isArray(agentRender))return false;const entryIndex=path5[3].slice(1,-1);const entryDescriptor=Object.getOwnPropertyDescriptor(agentRender,entryIndex);const entry=entryDescriptor&&"value"in entryDescriptor?entryDescriptor.value:void 0;if(!isPlainObject2(entry))return false;const renderValue=ownDataPropertyValue4(entry,"value");if(!isPlainObject2(renderValue)||ownDataPropertyValue4(renderValue,"webhook")!==value){return false}if(ownDataPropertyValue4(entry,"channelId")!=="teams"||ownDataPropertyValue4(entry,"renderId")!=="teams-openclaw-channel"||ownDataPropertyValue4(entry,"hookId")!=="teams-openclaw-channel"||ownDataPropertyValue4(entry,"handler")!=="common.staticOutputs"||ownDataPropertyValue4(entry,"kind")!=="json-fragment"||ownDataPropertyValue4(entry,"agent")!=="openclaw"||ownDataPropertyValue4(entry,"target")!=="openclaw.json"||ownDataPropertyValue4(entry,"path")!=="channels.msteams"||!isPlainObject2(value)){return false}const keys=Object.getOwnPropertyNames(value);if(keys.length!==2||!keys.includes("port")||!keys.includes("path"))return false;const port=ownDataPropertyValue4(value,"port");return typeof port==="number"&&Number.isInteger(port)&&port>=1&&port<=65535&&ownDataPropertyValue4(value,"path")==="/api/messages"}function isCanonicalMessagingRuntimeEnvAlias(path5,value){if(!isMessagingRuntimeEnvAliasPath(path5))return false;const envKey=ownDataPropertyValue4(value,"envKey");const match=ownDataPropertyValue4(value,"match");const placeholder=ownDataPropertyValue4(value,"value");return typeof envKey==="string"&&CREDENTIAL_ENV_NAME_PATTERN.test(envKey)&&match===`^openshell:resolve:env:(v[0-9]+_)?${envKey}$`&&typeof placeholder==="string"&&messagingCredentialPlaceholderEnvKey(placeholder)===envKey}function isAllowedMessagingRuntimeAliasStringPath(path5,allowedAliasIndexes){return path5.length===6&&path5[0]==="messaging"&&path5[1]==="plan"&&path5[2]==="runtimeSetup"&&path5[3]==="envAliases"&&allowedAliasIndexes.has(path5[4]??"")&&(path5[5]==="match"||path5[5]==="value")}function isMessagingPackagePin(path5,value){return path5.length===6&&path5[0]==="messaging"&&path5[1]==="plan"&&path5[2]==="buildSteps"&&JSON_ARRAY_INDEX_SEGMENT_RE.test(path5[3]??"")&&path5[4]==="value"&&path5[5]==="pin"&&typeof value==="boolean"}function containsUrlWithCredentialMaterial(value){const candidates=value.match(URL_CANDIDATE_RE)??[];for(let index=0;index{if(isCredentialShapedName(key))credentialQuery=true});const fragment=url.hash.startsWith("#")?url.hash.slice(1):url.hash;const queryStart=fragment.indexOf("?");const fragmentParameters=new URLSearchParams(queryStart>=0?fragment.slice(queryStart+1):fragment);let credentialFragment=false;fragmentParameters.forEach((_fragmentValue,key)=>{if(isCredentialShapedName(key))credentialFragment=true});if(url.username||url.password||credentialQuery||credentialFragment)return true}catch{}}return false}function invalid(reason){throw new ManagedStartupProfileError(reason)}function payloadPath(path5){return path5.reduce((result,segment)=>segment.startsWith("[")?`${result}${segment}`:`${result}${result?".":""}${segment}`,"")}function mapArrayByIndex(values,mapper){const mapped=[];for(let index=0;index0&&values[insertion-1]>selected){Object.defineProperty(values,String(insertion),{configurable:true,enumerable:true,value:values[insertion-1],writable:true});insertion-=1}Object.defineProperty(values,String(insertion),{configurable:true,enumerable:true,value:selected,writable:true})}return values}function requireRecord(value,where){if(!isPlainObject2(value))invalid(`${where} must be an object`);return value}function rejectUnknownKeys2(value,allowed,where){const keys=Object.keys(value);for(let index=0;indexmaxBytes||CONTROL_CHARACTER_RE2.test(value)){invalid(`${where} must be a bounded, non-empty string without control characters`)}return value}function requireStringEnum(value,allowed,where){const normalized=requireBoundedString(value,where);if(!allowed.has(normalized))invalid(`${where} is not supported`);return normalized}function requireNullablePositiveInteger(value,where){if(value===null)return null;if(typeof value!=="number"||!Number.isSafeInteger(value)||value<1||value>MAX_TUNING_INTEGER){invalid(`${where} must be null or a bounded positive integer`)}return value}function requirePositiveInteger(value,where,maximum=MAX_TUNING_INTEGER){if(typeof value!=="number"||!Number.isSafeInteger(value)||value<1||value>maximum){invalid(`${where} must be a bounded positive integer`)}return value}function requirePort(value,where,minimum=1){if(typeof value!=="number"||!Number.isInteger(value)||value<1||value>65535){invalid(`${where} must be a valid TCP port`)}if(valueMAX_LIST_ITEMS){invalid(`${where} must be a bounded string list`)}const items=mapArrayByIndex(value,item=>requireBoundedString(item,`${where} item`));const unique2=new Set;for(let index=0;index{if(depth>MAX_JSON_DEPTH)invalid(`${where} exceeds the JSON depth limit`);if(current===null||typeof current==="string"||typeof current==="boolean"){return current}if(typeof current==="number"){if(!Number.isFinite(current))invalid(`${where} contains a non-finite number`);return current}if(Array.isArray(current)){return mapArrayByIndex(current,item=>clone(item,depth+1))}if(!isPlainObject2(current))invalid(`${where} contains a non-JSON value`);const result=options.nullPrototypeObjects?Object.create(null):{};const keys=Object.getOwnPropertyNames(current);for(let index=0;indexMAX_IDENTIFIER_BYTES||CONTROL_CHARACTER_RE2.test(key)){invalid(`${where} contains an invalid object key`)}const descriptor=Object.getOwnPropertyDescriptor(current,key);if(!descriptor||!("value"in descriptor)){invalid(`${where} contains a non-JSON value`)}Object.defineProperty(result,key,{configurable:true,enumerable:true,value:clone(descriptor.value,depth+1),writable:true})}return result};return clone(value,0)}function requireJsonObjectOrNull(value,where){if(value===null)return null;if(!isPlainObject2(value))invalid(`${where} must be null or a plain JSON object`);return cloneJsonValue(value,where,{nullPrototypeObjects:true})}function requireJsonObject(value,where){const object=requireJsonObjectOrNull(value,where);if(object===null)invalid(`${where} must be a plain JSON object`);return object}function requireHttpUrl(value,where){const raw=requireBoundedString(value,where,MAX_URL_BYTES2);let parsed;try{parsed=new URL(raw)}catch{invalid(`${where} must be a valid HTTP(S) URL`)}if(parsed.protocol!=="http:"&&parsed.protocol!=="https:"||parsed.username||parsed.password||parsed.search||parsed.hash){invalid(`${where} must be a credential-free HTTP(S) URL without query or fragment data`)}const pathname=parsed.pathname.replace(/\/+$/u,"");return pathname===""?parsed.origin:`${parsed.origin}${pathname}`}function requireProxyUrl(value,allowedSchemes,where){if(value===null)return null;const raw=requireBoundedString(value,where,MAX_URL_BYTES2);let parsed;try{parsed=new URL(raw)}catch{invalid(`${where} must be a valid HTTP(S) proxy URL`)}if(!allowedSchemes.has(parsed.protocol)||parsed.username||parsed.password||parsed.pathname!=="/"||parsed.search||parsed.hash){invalid(`${where} must be a credential-free HTTP(S) proxy origin`)}return parsed.origin}function requireManagedProxyHost(value,where){const host=requireBoundedString(value,where);if(!/^[A-Za-z0-9._-]+$/u.test(host)){invalid(`${where} must be a hostname or IPv4 address without a scheme or separators`)}return host}function isLoopbackUrl(value){const hostname=new URL(value).hostname.toLowerCase();return hostname==="localhost"||hostname==="127.0.0.1"||hostname==="::1"||hostname==="[::1]"}function configuredDashboardPort(value){const explicit=new URL(value).port;return explicit===""?18789:Number(explicit)}function requireSampleRate(value,where){if(typeof value!=="number"||!Number.isFinite(value)||value<0||value>1){invalid(`${where} must be a number between 0 and 1`)}return value}function assertPayloadStructureAndCredentialShapes(root){const pending=[{value:root,depth:0,path:[]}];const allowedRuntimeAliasIndexes=new Set;const allowedMessagingCredentialFields=new Set;const allowedBuildStepPlaceholders=new Set;const selectedAgent=isPlainObject2(root)?ownDataPropertyValue4(root,"agent"):void 0;let discoveredNodes=1;let observedBytes=0;const observeText=value=>{observedBytes+=import_node_buffer2.Buffer.byteLength(value,"utf8");if(observedBytes>MANAGED_STARTUP_PROFILE_MAX_BYTES){invalid(`payload exceeds ${String(MANAGED_STARTUP_PROFILE_MAX_BYTES)} bytes`)}};const reserveNode=depth=>{discoveredNodes+=1;if(discoveredNodes>MAX_JSON_NODES||depth>MAX_JSON_DEPTH){invalid("payload structure exceeds the complexity limit")}observedBytes+=1};while(pending.length>0){const current=pending.pop();if(!current)break;if(current.depth>MAX_JSON_DEPTH){invalid("payload structure exceeds the complexity limit")}if(typeof current.value==="string"){observeText(current.value);if(!isAllowedMessagingRuntimeAliasStringPath(current.path,allowedRuntimeAliasIndexes)&&!isMessagingCredentialPlaceholder(current.path,current.value,allowedBuildStepPlaceholders,allowedMessagingCredentialFields)&&!isMessagingCredentialPlaceholderAssignment(selectedAgent,current.path,current.value)&&(valueLooksLikeSecret(current.value)||containsMessagingCredentialPlaceholder(current.value))){invalid(`payload field ${payloadPath(current.path)} contains credential-shaped string data`)}if(RAW_CA_PEM_RE.test(current.value)||RAW_CA_PEM_BASE64_RE.test(current.value)||RAW_CA_DER_BASE64_RE.test(current.value)||RAW_CA_DATA_URI_RE.test(current.value)){invalid(`payload field ${payloadPath(current.path)} contains raw certificate data; provide only the CA SHA-256 digest`)}if(containsUrlWithCredentialMaterial(current.value)){invalid(`payload field ${payloadPath(current.path)} contains a URL with embedded credentials`)}continue}if(Array.isArray(current.value)){if(Object.getPrototypeOf(current.value)!==Array.prototype){invalid("payload arrays must use the standard JSON prototype")}if("toJSON"in current.value){invalid("payload must not define a custom JSON serializer")}if(Object.getOwnPropertySymbols(current.value).length>0||Object.getOwnPropertyNames(current.value).length!==current.value.length+1){invalid("payload arrays must contain only indexed JSON values")}for(let index=0;index0||discoveredNodes+keys.length>MAX_JSON_NODES){invalid("payload structure exceeds the complexity limit")}for(let index=0;indexMANAGED_STARTUP_PROFILE_MAX_BYTES){invalid(`payload exceeds ${String(MANAGED_STARTUP_PROFILE_MAX_BYTES)} bytes`)}}function validateWebSearch(value,agent){const webSearch=requireRecord(value,"agentConfig.webSearch");rejectUnknownKeys2(webSearch,WEB_SEARCH_KEYS,"agentConfig.webSearch");const provider=requireStringEnum(webSearch.provider,new Set(MANAGED_STARTUP_PROFILE_CAPABILITIES[agent].webSearchProviders),"agentConfig.webSearch.provider");return{enabled:requireBoolean(webSearch.enabled,"agentConfig.webSearch.enabled"),provider}}function validateOpenClawOtel(value){const otel=requireRecord(value,"agentConfig.otel");rejectUnknownKeys2(otel,OTEL_KEYS,"agentConfig.otel");return{enabled:requireBoolean(otel.enabled,"agentConfig.otel.enabled"),endpointUrl:requireHttpUrl(otel.endpointUrl,"agentConfig.otel.endpointUrl"),serviceName:requireBoundedString(otel.serviceName,"agentConfig.otel.serviceName",MAX_IDENTIFIER_BYTES),sampleRate:requireSampleRate(otel.sampleRate,"agentConfig.otel.sampleRate")}}function validateExtraAgents(value){const extraAgents=requireRecord(value,"agentConfig.extraAgents");rejectUnknownKeys2(extraAgents,EXTRA_AGENTS_KEYS,"agentConfig.extraAgents");if(!Array.isArray(extraAgents.agents)||extraAgents.agents.length>MAX_LIST_ITEMS){invalid("agentConfig.extraAgents.agents must be a bounded JSON object list")}return{agents:mapArrayByIndex(extraAgents.agents,(agent,index)=>requireJsonObject(agent,`agentConfig.extraAgents.agents[${String(index)}]`)),defaults:requireJsonObject(extraAgents.defaults,"agentConfig.extraAgents.defaults"),main:requireJsonObject(extraAgents.main,"agentConfig.extraAgents.main")}}function validateDeviceAuth(value){const deviceAuth=requireRecord(value,"agentConfig.deviceAuth");rejectUnknownKeys2(deviceAuth,DEVICE_AUTH_KEYS,"agentConfig.deviceAuth");return{disabled:requireBoolean(deviceAuth.disabled,"agentConfig.deviceAuth.disabled"),optOutSource:requireStringEnum(deviceAuth.optOutSource,new Set(["operator","managed-onboard"]),"agentConfig.deviceAuth.optOutSource")}}function validateAgentConfig(value,expectedAgent){const config=requireRecord(value,"agentConfig");const agent=requireStringEnum(config.agent,MANAGED_STARTUP_AGENT_SET,"agentConfig.agent");if(agent!==expectedAgent)invalid("agentConfig.agent must match agent");if(agent==="openclaw"){rejectUnknownKeys2(config,OPENCLAW_CONFIG_KEYS,"agentConfig");const heartbeatEvery=config.heartbeatEvery===null?null:requireBoundedString(config.heartbeatEvery,"agentConfig.heartbeatEvery",MAX_IDENTIFIER_BYTES);if(heartbeatEvery!==null&&!/^\d+(?:s|m|h)$/u.test(heartbeatEvery)){invalid("agentConfig.heartbeatEvery must be null or a duration ending in s, m, or h")}return{agent,webSearch:validateWebSearch(config.webSearch,agent),otel:validateOpenClawOtel(config.otel),agentTimeoutSeconds:requirePositiveInteger(config.agentTimeoutSeconds,"agentConfig.agentTimeoutSeconds"),heartbeatEvery,extraAgents:validateExtraAgents(config.extraAgents),deviceAuth:validateDeviceAuth(config.deviceAuth),minimalBootstrap:requireBoolean(config.minimalBootstrap,"agentConfig.minimalBootstrap")}}if(agent==="hermes"){rejectUnknownKeys2(config,HERMES_CONFIG_KEYS,"agentConfig");const base={agent,webSearch:validateWebSearch(config.webSearch,agent)};if(!Object.hasOwn(config,"switchyardRouting"))return base;try{return{...base,switchyardRouting:validateHermesSwitchyardRouting(config.switchyardRouting)}}catch(error){invalid(error instanceof Error?error.message:"agentConfig.switchyardRouting is invalid")}}if(agent==="pi"){rejectUnknownKeys2(config,PI_CONFIG_KEYS,"agentConfig");return{agent}}rejectUnknownKeys2(config,DCODE_CONFIG_KEYS,"agentConfig");return{agent,autoApprovalMode:requireStringEnum(config.autoApprovalMode,DCODE_AUTO_APPROVAL_MODE_SET,"agentConfig.autoApprovalMode"),observabilityEnabled:requireBoolean(config.observabilityEnabled,"agentConfig.observabilityEnabled")}}function validateDashboard(value,expectedAgent){const dashboard=requireRecord(value,"dashboard");const agent=requireStringEnum(dashboard.agent,MANAGED_STARTUP_AGENT_SET,"dashboard.agent");if(agent!==expectedAgent)invalid("dashboard.agent must match agent");if(agent==="openclaw"){rejectUnknownKeys2(dashboard,OPENCLAW_DASHBOARD_KEYS,"dashboard");const mode=requireStringEnum(dashboard.mode,new Set(MANAGED_STARTUP_PROFILE_CAPABILITIES[agent].dashboardModes),"dashboard.mode");const url=requireHttpUrl(dashboard.url,"dashboard.url");const bindAddress=requireStringEnum(dashboard.bindAddress,new Set(["127.0.0.1","0.0.0.0"]),"dashboard.bindAddress");const wslExposure=requireBoolean(dashboard.wslExposure,"dashboard.wslExposure");const hasRemoteExposure=!isLoopbackUrl(url)||bindAddress==="0.0.0.0"||wslExposure;if(mode==="remote"!==hasRemoteExposure){invalid("OpenClaw dashboard.mode must reflect its URL, bind address, and WSL exposure")}const port=requirePort(dashboard.port,"dashboard.port",1024);if(isHermesApiPort(port))invalid(`OpenClaw dashboard.port must not use a reserved Hermes API port (${HERMES_API_PORT_RANGE_START}-${HERMES_API_PORT_RANGE_END})`);if(configuredDashboardPort(url)!==port){invalid("OpenClaw dashboard.port must match dashboard.url")}return{agent,mode,url,port,bindAddress,wslExposure}}if(agent==="hermes"){rejectUnknownKeys2(dashboard,HERMES_DASHBOARD_KEYS,"dashboard");const mode=requireStringEnum(dashboard.mode,new Set(MANAGED_STARTUP_PROFILE_CAPABILITIES[agent].dashboardModes),"dashboard.mode");const url=requireHttpUrl(dashboard.url,"dashboard.url");if(!isLoopbackUrl(url)){invalid("Hermes dashboard.url must remain loopback; OpenShell owns the host forward")}if(mode==="disabled"){if(dashboard.publicPort!==null||dashboard.internalPort!==null||dashboard.tuiEnabled!==false){invalid("disabled Hermes dashboard must not configure ports or TUI")}return{agent,mode,url,publicPort:null,internalPort:null,tuiEnabled:false}}const publicPort=requirePort(dashboard.publicPort,"dashboard.publicPort",1024);const internalPort=requirePort(dashboard.internalPort,"dashboard.internalPort",1024);if(publicPort===internalPort){invalid("Hermes dashboard publicPort and internalPort must differ")}if(isHermesReservedApiPort(publicPort)||isHermesReservedApiPort(internalPort)){invalid(`Hermes dashboard ports must not use reserved API ports ${HERMES_RESERVED_API_PORT_LABEL}`)}if(configuredDashboardPort(url)!==publicPort){invalid("Hermes dashboard.publicPort must match dashboard.url")}return{agent,mode,url,publicPort,internalPort,tuiEnabled:requireBoolean(dashboard.tuiEnabled,"dashboard.tuiEnabled")}}if(agent==="pi"){rejectUnknownKeys2(dashboard,PI_DASHBOARD_KEYS,"dashboard");if(dashboard.mode!=="disabled")invalid("pi dashboard.mode must be disabled");return{agent,mode:"disabled"}}rejectUnknownKeys2(dashboard,DCODE_DASHBOARD_KEYS,"dashboard");if(dashboard.mode!=="disabled"){invalid("langchain-deepagents-code dashboard.mode must be disabled")}return{agent,mode:"disabled"}}function validateInference(value,agent){const inference=requireRecord(value,"inference");rejectUnknownKeys2(inference,INFERENCE_KEYS,"inference");const routeProvider=requireBoundedString(inference.routeProvider,"inference.routeProvider");const upstreamProvider=requireBoundedString(inference.upstreamProvider,"inference.upstreamProvider");const model=requireBoundedString(inference.model,"inference.model",MAX_MODEL_BYTES2);const api=requireStringEnum(inference.api,new Set(MANAGED_STARTUP_PROFILE_CAPABILITIES[agent].inferenceApis),"inference.api");const upstreamEndpointUrl=inference.upstreamEndpointUrl===null?null:requireHttpUrl(inference.upstreamEndpointUrl,"inference.upstreamEndpointUrl");const primaryModelRef=inference.primaryModelRef===null?null:requireBoundedString(inference.primaryModelRef,"inference.primaryModelRef",MAX_MODEL_BYTES2);const compatibility=requireJsonObjectOrNull(inference.compatibility,"inference.compatibility");const inputModalities=inference.inputModalities===null?null:requireEnumList(inference.inputModalities,new Set(MANAGED_STARTUP_PROFILE_CAPABILITIES[agent].inputModalities),"inference.inputModalities",{allowEmpty:false});if(upstreamEndpointUrl!==null&&!MANAGED_STARTUP_PROFILE_CAPABILITIES[agent].supportsUpstreamEndpoint){invalid(`inference.upstreamEndpointUrl must be null for ${agent}`)}if(agent==="openclaw"){if(primaryModelRef===null||inputModalities===null){invalid("openclaw requires primaryModelRef and inputModalities")}if(primaryModelRef!==`${routeProvider}/${model}`){invalid("openclaw primaryModelRef must match routeProvider and model")}}else{if(primaryModelRef!==null||compatibility!==null||inputModalities!==null){invalid(`${agent} does not support primaryModelRef, compatibility, or inputModalities`)}if(agent==="langchain-deepagents-code"&&!isValidDcodeUpstreamProvider(upstreamProvider)){invalid("inference.upstreamProvider must start with an ASCII letter or digit and contain 1-64 ASCII letters, digits, dots, underscores, or hyphens for DCode")}}return{routeProvider,upstreamProvider,model,routedBaseUrl:requireHttpUrl(inference.routedBaseUrl,"inference.routedBaseUrl"),upstreamEndpointUrl,api,primaryModelRef,compatibility,inputModalities}}function validateProxy(value,agent){const proxy=requireRecord(value,"proxy");rejectUnknownKeys2(proxy,PROXY_KEYS,"proxy");const hostHttpUrl=requireProxyUrl(proxy.hostHttpUrl,new Set(["http:"]),"proxy.hostHttpUrl");const hostHttpsUrl=requireProxyUrl(proxy.hostHttpsUrl,new Set(["http:","https:"]),"proxy.hostHttpsUrl");const hostNoProxy=requireStringList(proxy.hostNoProxy,"proxy.hostNoProxy");if(!MANAGED_STARTUP_PROFILE_CAPABILITIES[agent].supportsHostProxyIntent&&(hostHttpUrl!==null||hostHttpsUrl!==null||hostNoProxy.length>0)){invalid(`${agent} rejects host proxy intent and accepts only its root-owned managed route`)}return{managedHost:requireManagedProxyHost(proxy.managedHost,"proxy.managedHost"),managedPort:requirePort(proxy.managedPort,"proxy.managedPort"),hostHttpUrl,hostHttpsUrl,hostNoProxy}}function validateTools(value,agent){const tools=requireRecord(value,"tools");rejectUnknownKeys2(tools,TOOLS_KEYS,"tools");const enabledGateways=requireEnumList(tools.enabledGateways,new Set(MANAGED_STARTUP_PROFILE_CAPABILITIES[agent].toolGateways),"tools.enabledGateways",{allowEmpty:true});return{disclosure:requireStringEnum(tools.disclosure,new Set(["progressive","direct"]),"tools.disclosure"),enabledGateways}}function validateTuning(value,agent){const tuning=requireRecord(value,"tuning");rejectUnknownKeys2(tuning,TUNING_KEYS,"tuning");const result={contextWindow:requireNullablePositiveInteger(tuning.contextWindow,"tuning.contextWindow"),maxTokens:requireNullablePositiveInteger(tuning.maxTokens,"tuning.maxTokens"),reasoning:requireNullableBoolean(tuning.reasoning,"tuning.reasoning"),reasoningEffort:tuning.reasoningEffort===null?null:requireStringEnum(tuning.reasoningEffort,REASONING_EFFORT_SET,"tuning.reasoningEffort")};const advertised=new Set(MANAGED_STARTUP_PROFILE_CAPABILITIES[agent].tuningFields);const unsupported=TUNING_FIELD_ORDER.filter(field=>result[field]!==null&&!advertised.has(field));if(unsupported.length>0){invalid(`${agent} does not support startup tuning fields: ${unsupported.join(", ")}`)}if(agent==="openclaw"){const missing=TUNING_FIELD_ORDER.filter(field=>advertised.has(field)&&result[field]===null);if(missing.length>0){invalid(`openclaw requires ${missing.join(", ")} tuning`)}}if(agent==="hermes"&&result.contextWindow!==null&&result.contextWindowcanonicalizeJson(item));if(!isPlainObject2(value))return value;const result={};const keys=sortStrings(Object.keys(value));for(let index=0;indexMANAGED_STARTUP_PROFILE_MAX_BYTES){invalid(`canonical payload exceeds ${String(MANAGED_STARTUP_PROFILE_MAX_BYTES)} bytes`)}return serialized}function decodeManagedStartupProfile(encoded){if(typeof encoded!=="string"||encoded.length===0||import_node_buffer2.Buffer.byteLength(encoded,"ascii")>MANAGED_STARTUP_PROFILE_MAX_ENCODED_BYTES||!BASE64URL_RE.test(encoded)||encoded.length%4===1){invalid("encoded payload is malformed or exceeds the size limit")}const bytes=import_node_buffer2.Buffer.from(encoded,"base64url");if(bytes.length===0||bytes.length>MANAGED_STARTUP_PROFILE_MAX_BYTES||bytes.toString("base64url")!==encoded){invalid("encoded payload is malformed or exceeds the size limit")}let raw;try{raw=UTF8_DECODER.decode(bytes)}catch{invalid("payload is not valid UTF-8")}let parsed;try{parsed=JSON.parse(raw)}catch{invalid("payload is not valid JSON")}const profile=validateManagedStartupProfile(parsed);if(serializeManagedStartupProfile(profile)!==raw){invalid("payload is not in canonical form")}return profile}function fingerprintManagedStartupProfile(profile){return(0,import_node_crypto2.createHash)("sha256").update(serializeManagedStartupProfile(profile),"utf8").digest("hex")}var ManagedStartupAgentEnvironmentError=class extends Error{constructor(message){super(`Cannot map managed startup profile: ${message}`);this.name="ManagedStartupAgentEnvironmentError"}};var EMPTY_APPLICATION_ENVIRONMENT=Object.freeze({});var OPENCLAW_APPLICATION_RUNTIME_INPUTS=Object.freeze([["NEMOCLAW_AUTO_PAIR_DEADLINE_SECS","positive-finite-seconds"],["NEMOCLAW_AUTO_PAIR_FAST_DEADLINE_SECS","positive-finite-seconds"],["NEMOCLAW_AUTO_PAIR_FAST_REENTRY_INTERVAL_SECS","positive-finite-seconds"],["NEMOCLAW_AUTO_PAIR_FAST_REENTRY_POLLS","positive-safe-integer"],["NEMOCLAW_AUTO_PAIR_RUN_TIMEOUT_SECS","positive-finite-seconds"],["NEMOCLAW_AUTO_PAIR_SLOW_INTERVAL_SECS","positive-finite-seconds"]]);var HERMES_SWITCHYARD_ROUTING_TRANSPORT="NEMOCLAW_HERMES_SWITCHYARD_ROUTING_B64";function booleanFlag(value){return value?"1":"0"}function canonicalizeJson2(value){if(Array.isArray(value))return value.map(item=>canonicalizeJson2(item));if(value===null||typeof value!=="object")return value;const record2=value;return Object.fromEntries(Object.keys(record2).sort().map(key=>[key,canonicalizeJson2(record2[key])]))}function encodeCanonicalJson(value){return import_node_buffer3.Buffer.from(JSON.stringify(canonicalizeJson2(value)),"utf8").toString("base64")}function sortedEnvironment(environment){return Object.freeze(Object.fromEntries(Object.entries(environment).sort(([left],[right])=>leftright?1:0)))}function canonicalApplicationRuntimeValue(name,raw,kind){if(raw.includes("\0")||/[\r\n]/u.test(raw)){throw new ManagedStartupAgentEnvironmentError(`${name} must be single-line text`)}const value=Number(raw.trim());const valid=kind==="positive-safe-integer"?Number.isSafeInteger(value)&&value>0:Number.isFinite(value)&&value>0;if(!valid){throw new ManagedStartupAgentEnvironmentError(`${name} must be ${kind==="positive-safe-integer"?"a positive safe integer":"finite positive seconds"}`)}return String(value)}function applicationRuntimePlan(profile,environment){const exportEnvironment={};if(profile.agent==="openclaw"){for(const[name,kind]of OPENCLAW_APPLICATION_RUNTIME_INPUTS){const raw=environment[name];if(raw!==void 0){exportEnvironment[name]=canonicalApplicationRuntimeValue(name,raw,kind)}}}const unsetEnvironment=new Set(MANAGED_STARTUP_RUNTIME_CLEANUP_OBLIGATIONS.filter(({supportedFor})=>!supportedFor.includes(profile.agent)).map(({input})=>input));if(profile.agent!=="openclaw"){for(const[name]of OPENCLAW_APPLICATION_RUNTIME_INPUTS){unsetEnvironment.add(name)}}if(profile.agentConfig.agent!=="hermes"||profile.agentConfig.switchyardRouting===void 0){unsetEnvironment.add(HERMES_SWITCHYARD_ROUTING_TRANSPORT)}return Object.freeze({exportEnvironment:sortedEnvironment(exportEnvironment),unsetEnvironment:Object.freeze([...unsetEnvironment].sort())})}function commonConfigurationEnvironment(profile){return{NEMOCLAW_INFERENCE_API:profile.inference.api,NEMOCLAW_INFERENCE_BASE_URL:profile.inference.routedBaseUrl,NEMOCLAW_INFERENCE_PROVIDER_ID:profile.inference.routeProvider,NEMOCLAW_MODEL:profile.inference.model,NEMOCLAW_TOOL_DISCLOSURE:profile.tools.disclosure,NEMOCLAW_UPSTREAM_PROVIDER:profile.inference.upstreamProvider}}function appendHostProxyEnvironment(environment,profile,options={}){if(options.preserveAmbientWhenAbsent===true&&profile.proxy.hostHttpUrl===null&&profile.proxy.hostHttpsUrl===null&&profile.proxy.hostNoProxy.length===0){return}const httpProxy=profile.proxy.hostHttpUrl??"";const httpsProxy=profile.proxy.hostHttpsUrl??"";const noProxy=profile.proxy.hostNoProxy.join(",");environment.HTTP_PROXY=httpProxy;environment.HTTPS_PROXY=httpsProxy;environment.NO_PROXY=noProxy;environment.http_proxy=httpProxy;environment.https_proxy=httpsProxy;environment.no_proxy=noProxy}function messagingEnvironment(profile,expectedAgent){if(profile.messaging.plan===null)return{};const plan=parseSandboxMessagingPlan(profile.messaging.plan,{agent:expectedAgent});if(!plan){throw new ManagedStartupAgentEnvironmentError(`messaging.plan must contain a validated ${expectedAgent} messaging plan`)}const{workflow:_workflow,...imageBuildPlan}=plan;return{NEMOCLAW_MESSAGING_PLAN_B64:encodeCanonicalJson(imageBuildPlan)}}function corporateCaMaterial(profile){return Object.freeze({kind:"corporate-ca-handoff",legacyInput:"NEMOCLAW_CORPORATE_CA_B64",expectedSha256:profile.corporateCa.bundleSha256})}function rootOwnedFile(legacyInput,path5,value){return Object.freeze({kind:"root-owned-file",legacyInput,path:path5,contents:`${value} +`,owner:"root",group:"root",mode:292})}function dashboardAction(dashboard){return Object.freeze({kind:"configure-dashboard",dashboard:Object.freeze(structuredClone(dashboard))})}function applicationActions(profile,messagingAgent){const actions=[];if(messagingAgent!==null){actions.push(Object.freeze({kind:"apply-messaging-plan",agent:messagingAgent,mode:profile.messaging.plan===null?"clear":"apply",phase:"runtime-setup",runAs:"root"}))}actions.push(Object.freeze({kind:"generate-agent-config",agent:profile.agent,runAs:"sandbox"}));if(messagingAgent!==null){actions.push(Object.freeze({kind:"apply-messaging-plan",agent:messagingAgent,mode:profile.messaging.plan===null?"clear":"apply",phase:"post-agent-install",runAs:"sandbox"}))}actions.push(dashboardAction(profile.dashboard));return Object.freeze(actions)}function mapOpenClawProfile(profile,environment){if(profile.agent!=="openclaw"||profile.agentConfig.agent!=="openclaw"||profile.dashboard.agent!=="openclaw"||profile.inference.primaryModelRef===null||profile.inference.inputModalities===null||profile.tuning.contextWindow===null||profile.tuning.maxTokens===null||profile.tuning.reasoning===null||profile.tuning.reasoningEffort===null){throw new ManagedStartupAgentEnvironmentError("OpenClaw profile state is inconsistent")}const configurationEnvironment={...commonConfigurationEnvironment(profile),...messagingEnvironment(profile,"openclaw"),CHAT_UI_URL:profile.dashboard.url,NEMOCLAW_AGENT_HEARTBEAT_EVERY:profile.agentConfig.heartbeatEvery??"",NEMOCLAW_AGENT_TIMEOUT:String(profile.agentConfig.agentTimeoutSeconds),NEMOCLAW_CONTEXT_WINDOW:String(profile.tuning.contextWindow),NEMOCLAW_DASHBOARD_BIND:profile.dashboard.bindAddress==="0.0.0.0"?profile.dashboard.bindAddress:"",NEMOCLAW_DISABLE_DEVICE_AUTH:booleanFlag(profile.agentConfig.deviceAuth.disabled),NEMOCLAW_DEVICE_AUTH_OPT_OUT_SOURCE:profile.agentConfig.deviceAuth.optOutSource,NEMOCLAW_EXTRA_AGENTS_JSON_B64:encodeCanonicalJson(profile.agentConfig.extraAgents),NEMOCLAW_INFERENCE_COMPAT_B64:encodeCanonicalJson(profile.inference.compatibility),NEMOCLAW_INFERENCE_INPUTS:profile.inference.inputModalities.join(","),NEMOCLAW_MAX_TOKENS:String(profile.tuning.maxTokens),NEMOCLAW_OPENCLAW_OTEL:booleanFlag(profile.agentConfig.otel.enabled),NEMOCLAW_OPENCLAW_OTEL_ENDPOINT:profile.agentConfig.otel.endpointUrl,NEMOCLAW_OPENCLAW_OTEL_SAMPLE_RATE:String(profile.agentConfig.otel.sampleRate),NEMOCLAW_OPENCLAW_OTEL_SERVICE_NAME:profile.agentConfig.otel.serviceName,NEMOCLAW_PRIMARY_MODEL_REF:profile.inference.primaryModelRef,NEMOCLAW_PROXY_HOST:profile.proxy.managedHost,NEMOCLAW_PROXY_PORT:String(profile.proxy.managedPort),NEMOCLAW_REASONING:String(profile.tuning.reasoning),NEMOCLAW_REASONING_EFFORT:profile.tuning.reasoningEffort,NEMOCLAW_WEB_SEARCH_ENABLED:booleanFlag(profile.agentConfig.webSearch.enabled),NEMOCLAW_WEB_SEARCH_PROVIDER:profile.agentConfig.webSearch.provider,NEMOCLAW_WSL_DASHBOARD_EXPOSURE:booleanFlag(profile.dashboard.wslExposure)};const runtimeEnvironment={...configurationEnvironment};delete runtimeEnvironment.NEMOCLAW_MESSAGING_PLAN_B64;runtimeEnvironment.NEMOCLAW_DASHBOARD_PORT=String(profile.dashboard.port);runtimeEnvironment.NEMOCLAW_MINIMAL_BOOTSTRAP=booleanFlag(profile.agentConfig.minimalBootstrap);appendHostProxyEnvironment(runtimeEnvironment,profile,{preserveAmbientWhenAbsent:true});return Object.freeze({schemaVersion:profile.schemaVersion,agent:profile.agent,configurationEnvironment:sortedEnvironment(configurationEnvironment),runtimeEnvironment:sortedEnvironment(runtimeEnvironment),applicationRuntime:applicationRuntimePlan(profile,environment),materials:Object.freeze([corporateCaMaterial(profile)]),actions:applicationActions(profile,"openclaw")})}function mapHermesProfile(profile,environment){if(profile.agent!=="hermes"||profile.agentConfig.agent!=="hermes"||profile.dashboard.agent!=="hermes"){throw new ManagedStartupAgentEnvironmentError("Hermes profile state is inconsistent")}const configurationEnvironment={...commonConfigurationEnvironment(profile),...messagingEnvironment(profile,"hermes"),CHAT_UI_URL:profile.dashboard.url,NEMOCLAW_CONTEXT_WINDOW:profile.tuning.contextWindow===null?"":String(profile.tuning.contextWindow),NEMOCLAW_HERMES_TOOL_GATEWAY_BROKER:booleanFlag(profile.tools.enabledGateways.length>0),NEMOCLAW_HERMES_TOOL_GATEWAY_PRESETS_B64:encodeCanonicalJson(profile.tools.enabledGateways),NEMOCLAW_WEB_SEARCH_ENABLED:booleanFlag(profile.agentConfig.webSearch.enabled),NEMOCLAW_WEB_SEARCH_PROVIDER:profile.agentConfig.webSearch.provider};if(profile.agentConfig.switchyardRouting!==void 0){configurationEnvironment.NEMOCLAW_HERMES_SWITCHYARD_ROUTING_B64=encodeCanonicalJson(profile.agentConfig.switchyardRouting)}const runtimeEnvironment={...configurationEnvironment};delete runtimeEnvironment.NEMOCLAW_MESSAGING_PLAN_B64;delete runtimeEnvironment.NEMOCLAW_HERMES_SWITCHYARD_ROUTING_B64;runtimeEnvironment.NEMOCLAW_DASHBOARD_PORT=profile.dashboard.publicPort===null?"":String(profile.dashboard.publicPort);runtimeEnvironment.NEMOCLAW_HERMES_DASHBOARD=profile.dashboard.mode==="loopback-forwarded"?"1":"0";runtimeEnvironment.NEMOCLAW_HERMES_DASHBOARD_INTERNAL_PORT=profile.dashboard.internalPort===null?"":String(profile.dashboard.internalPort);runtimeEnvironment.NEMOCLAW_HERMES_DASHBOARD_PORT=profile.dashboard.publicPort===null?"":String(profile.dashboard.publicPort);runtimeEnvironment.NEMOCLAW_HERMES_DASHBOARD_TUI=booleanFlag(profile.dashboard.tuiEnabled);runtimeEnvironment.NEMOCLAW_PROXY_HOST=profile.proxy.managedHost;runtimeEnvironment.NEMOCLAW_PROXY_PORT=String(profile.proxy.managedPort);appendHostProxyEnvironment(runtimeEnvironment,profile,{preserveAmbientWhenAbsent:true});return Object.freeze({schemaVersion:profile.schemaVersion,agent:profile.agent,configurationEnvironment:sortedEnvironment(configurationEnvironment),runtimeEnvironment:sortedEnvironment(runtimeEnvironment),applicationRuntime:applicationRuntimePlan(profile,environment),materials:Object.freeze([corporateCaMaterial(profile)]),actions:applicationActions(profile,"hermes")})}function mapDcodeProfile(profile,environment){if(profile.agent!=="langchain-deepagents-code"||profile.agentConfig.agent!=="langchain-deepagents-code"||profile.dashboard.agent!=="langchain-deepagents-code"||profile.messaging.plan!==null){throw new ManagedStartupAgentEnvironmentError("LangChain Deep Agents Code profile state is inconsistent")}const reasoningEffort=profile.tuning.reasoningEffort===null||profile.tuning.reasoningEffort==="default"?"":profile.tuning.reasoningEffort;const configurationEnvironment={...commonConfigurationEnvironment(profile),NEMOCLAW_REASONING_EFFORT:reasoningEffort,NEMOCLAW_UPSTREAM_ENDPOINT_URL:profile.inference.upstreamEndpointUrl??""};appendHostProxyEnvironment(configurationEnvironment,profile);const runtimeEnvironment={...configurationEnvironment,NEMOCLAW_OBSERVABILITY:booleanFlag(profile.agentConfig.observabilityEnabled)};delete runtimeEnvironment.NEMOCLAW_INFERENCE_BASE_URL;delete runtimeEnvironment.NEMOCLAW_REASONING_EFFORT;delete runtimeEnvironment.NEMOCLAW_UPSTREAM_PROVIDER;for(const name of["HTTP_PROXY","HTTPS_PROXY","NO_PROXY","http_proxy","https_proxy","no_proxy"]){delete runtimeEnvironment[name]}const materials=Object.freeze([corporateCaMaterial(profile),rootOwnedFile("NEMOCLAW_DCODE_AUTO_APPROVAL","/usr/local/share/nemoclaw/dcode-auto-approval",profile.agentConfig.autoApprovalMode),rootOwnedFile("NEMOCLAW_INFERENCE_BASE_URL","/usr/local/share/nemoclaw/dcode-inference-base-url",profile.inference.routedBaseUrl),rootOwnedFile("NEMOCLAW_UPSTREAM_PROVIDER","/usr/local/share/nemoclaw/dcode-upstream-provider",profile.inference.upstreamProvider),rootOwnedFile("NEMOCLAW_PROXY_HOST","/usr/local/share/nemoclaw/dcode-proxy-host",profile.proxy.managedHost),rootOwnedFile("NEMOCLAW_PROXY_PORT","/usr/local/share/nemoclaw/dcode-proxy-port",String(profile.proxy.managedPort)),rootOwnedFile("NEMOCLAW_REASONING_EFFORT","/usr/local/share/nemoclaw/dcode-reasoning-effort",reasoningEffort)]);return Object.freeze({schemaVersion:profile.schemaVersion,agent:profile.agent,configurationEnvironment:sortedEnvironment(configurationEnvironment),runtimeEnvironment:sortedEnvironment(runtimeEnvironment),applicationRuntime:applicationRuntimePlan(profile,environment),materials,actions:applicationActions(profile,null)})}function mapPiProfile(profile,environment){if(profile.agent!=="pi"||profile.agentConfig.agent!=="pi"||profile.dashboard.agent!=="pi"||profile.messaging.plan!==null){throw new ManagedStartupAgentEnvironmentError("Pi profile state is inconsistent")}const configurationEnvironment={...commonConfigurationEnvironment(profile),NEMOCLAW_CONTEXT_WINDOW:profile.tuning.contextWindow===null?"":String(profile.tuning.contextWindow),NEMOCLAW_MAX_TOKENS:profile.tuning.maxTokens===null?"":String(profile.tuning.maxTokens),NEMOCLAW_REASONING:profile.tuning.reasoning===null?"":String(profile.tuning.reasoning)};appendHostProxyEnvironment(configurationEnvironment,profile);const runtimeEnvironment={...configurationEnvironment};delete runtimeEnvironment.NEMOCLAW_INFERENCE_BASE_URL;delete runtimeEnvironment.NEMOCLAW_CONTEXT_WINDOW;delete runtimeEnvironment.NEMOCLAW_MAX_TOKENS;delete runtimeEnvironment.NEMOCLAW_REASONING;for(const name of["HTTP_PROXY","HTTPS_PROXY","NO_PROXY","http_proxy","https_proxy","no_proxy"]){delete runtimeEnvironment[name]}const materials=Object.freeze([corporateCaMaterial(profile),rootOwnedFile("NEMOCLAW_PROXY_HOST","/usr/local/share/nemoclaw/pi-proxy-host",profile.proxy.managedHost),rootOwnedFile("NEMOCLAW_PROXY_PORT","/usr/local/share/nemoclaw/pi-proxy-port",String(profile.proxy.managedPort))]);return Object.freeze({schemaVersion:profile.schemaVersion,agent:profile.agent,configurationEnvironment:sortedEnvironment(configurationEnvironment),runtimeEnvironment:sortedEnvironment(runtimeEnvironment),applicationRuntime:applicationRuntimePlan(profile,environment),materials,actions:applicationActions(profile,null)})}function mapManagedStartupProfileToAgentEnvironment(profile,environment=EMPTY_APPLICATION_ENVIRONMENT){const validated=validateManagedStartupProfile(profile);switch(validated.agent){case"openclaw":return mapOpenClawProfile(validated,environment);case"hermes":return mapHermesProfile(validated,environment);case"langchain-deepagents-code":return mapDcodeProfile(validated,environment);case"pi":return mapPiProfile(validated,environment)}}var import_node_buffer4=require("node:buffer");var import_node_crypto3=require("node:crypto");var import_node_fs=__toESM(require("node:fs"));var import_node_path=__toESM(require("node:path"));var import_node_util2=require("node:util");var MANAGED_STARTUP_APPLICATION_STATE_DIR="/var/lib/nemoclaw/startup-profile";var MANAGED_STARTUP_CA_MAX_BYTES=128*1024;var MANAGED_STARTUP_CA_MAX_CERTIFICATES=24;var STATE_SCHEMA_VERSION=1;var STATE_DIRECTORY_MODE=448;var STATE_FILE_MODE=384;var MAX_CONTROL_FILE_BYTES=512;var MAX_STATE_ENTRIES=32;var SHA256_RE2=/^[a-f0-9]{64}$/u;var GENERATION_RE=/^generation-([a-f0-9]{64})$/u;var PREPARE_TEMP_RE=/^\.prepare-[0-9]+-[a-f0-9]{24}$/u;var CONTROL_TEMP_RE=/^\.(?:committed|pending)\.json-[a-f0-9]{24}\.tmp$/u;var PEM_CERTIFICATE_RE=/-----BEGIN CERTIFICATE-----\r?\n[A-Za-z0-9+/=\r\n]+?-----END CERTIFICATE-----/gu;var UTF8_DECODER2=new import_node_util2.TextDecoder("utf-8",{fatal:true});var DEFAULT_RUNTIME={rootUid:0,rootGid:0};var ManagedStartupApplicationError=class extends Error{constructor(message){super(`Managed startup application failed: ${message}`);this.name="ManagedStartupApplicationError"}};function fail2(message){throw new ManagedStartupApplicationError(message)}function runtimeFor(override){return override??DEFAULT_RUNTIME}function requireContainerRoot(){if(process.geteuid?.()!==0){fail2("the image-side applicator must run with effective uid 0")}}function modeOf(stat){return stat.mode&511}function requireOwner(stat,target,runtime){if(stat.uid!==runtime.rootUid||stat.gid!==runtime.rootGid){fail2(`${target} must be owned by root:root`)}}function requireSecureDirectory(target,runtime,exactMode){let stat;try{stat=import_node_fs.default.lstatSync(target)}catch{fail2(`state directory component is missing or unreadable: ${target}`)}if(stat.isSymbolicLink()||!stat.isDirectory()){fail2(`state directory component must be a real directory: ${target}`)}const runtimeOwned=stat.uid===runtime.rootUid&&stat.gid===runtime.rootGid;const systemRootOwned=stat.uid===0&&stat.gid===0;if(exactMode){requireOwner(stat,target,runtime)}else if(!runtimeOwned&&!systemRootOwned){fail2(`state directory ancestor is not owned by a trusted identity: ${target}`)}const mode=modeOf(stat);const writableByUntrustedIdentity=(mode&18)!==0;const trustedStickyRoot=(stat.mode&512)!==0&&(runtimeOwned||systemRootOwned);if(exactMode&&mode!==STATE_DIRECTORY_MODE||!exactMode&&writableByUntrustedIdentity&&!trustedStickyRoot){fail2(exactMode?`${target} must have mode 0700`:`${target} is a replaceable group- or world-writable ancestor`)}}function requireSecureAncestors(target,runtime){const root=import_node_path.default.parse(target).root;let current=root;requireSecureDirectory(current,runtime,false);for(const segment of import_node_path.default.relative(root,target).split(import_node_path.default.sep).filter(Boolean)){current=import_node_path.default.join(current,segment);let stat;try{stat=import_node_fs.default.lstatSync(current)}catch{fail2(`state directory component is missing or unreadable: ${current}`)}if(stat.isSymbolicLink()){const runtimeOwned=stat.uid===runtime.rootUid&&stat.gid===runtime.rootGid;const systemRootOwned=stat.uid===0&&stat.gid===0;if(!runtimeOwned&&!systemRootOwned){fail2(`state directory ancestor is a replaceable symlink: ${current}`)}let resolved;try{resolved=import_node_fs.default.realpathSync(current)}catch{fail2(`state directory symlink is missing or unreadable: ${current}`)}requireSecureAncestors(resolved,runtime);continue}requireSecureDirectory(current,runtime,false)}}function ensureStateDirectory(rawStateDirectory,runtime){const stateDirectory=rawStateDirectory??MANAGED_STARTUP_APPLICATION_STATE_DIR;if(!import_node_path.default.isAbsolute(stateDirectory)||stateDirectory.includes("\0")){fail2("stateDirectory must be an absolute path")}const normalized=import_node_path.default.resolve(stateDirectory);const parent=import_node_path.default.dirname(normalized);requireSecureAncestors(parent,runtime);try{import_node_fs.default.mkdirSync(normalized,{mode:STATE_DIRECTORY_MODE});import_node_fs.default.chownSync(normalized,runtime.rootUid,runtime.rootGid);import_node_fs.default.chmodSync(normalized,STATE_DIRECTORY_MODE)}catch(error){if(error.code!=="EEXIST"){fail2(`could not create the managed startup state directory: ${normalized}`)}}requireSecureDirectory(normalized,runtime,true);return normalized}function requireSecureRegularFileStat(stat,target,runtime){if(!stat.isFile()||stat.isSymbolicLink()){fail2(`${target} must be a regular file`)}if(stat.nlink!==1){fail2(`${target} must not be hardlinked`)}requireOwner(stat,target,runtime);if(modeOf(stat)!==STATE_FILE_MODE){fail2(`${target} must have mode 0600`)}}function readSecureFile(target,maxBytes,runtime){let descriptor;try{descriptor=import_node_fs.default.openSync(target,import_node_fs.default.constants.O_RDONLY|import_node_fs.default.constants.O_NOFOLLOW)}catch{fail2(`state file is missing, unreadable, or a symlink: ${target}`)}try{const stat=import_node_fs.default.fstatSync(descriptor);requireSecureRegularFileStat(stat,target,runtime);if(stat.size<1||stat.size>maxBytes){fail2(`${target} is empty or exceeds its size limit`)}const content=import_node_fs.default.readFileSync(descriptor);if(content.length!==stat.size){fail2(`${target} changed while it was being read`)}return content}finally{import_node_fs.default.closeSync(descriptor)}}function writeSecureNewFile(target,content,runtime){let descriptor;try{descriptor=import_node_fs.default.openSync(target,import_node_fs.default.constants.O_CREAT|import_node_fs.default.constants.O_EXCL|import_node_fs.default.constants.O_WRONLY|import_node_fs.default.constants.O_NOFOLLOW,STATE_FILE_MODE)}catch{fail2(`refused to replace an existing state file: ${target}`)}try{import_node_fs.default.fchownSync(descriptor,runtime.rootUid,runtime.rootGid);import_node_fs.default.fchmodSync(descriptor,STATE_FILE_MODE);import_node_fs.default.writeFileSync(descriptor,content);import_node_fs.default.fsyncSync(descriptor)}finally{import_node_fs.default.closeSync(descriptor)}}function syncDirectory(target){const descriptor=import_node_fs.default.openSync(target,import_node_fs.default.constants.O_RDONLY);try{import_node_fs.default.fsyncSync(descriptor)}finally{import_node_fs.default.closeSync(descriptor)}}function randomToken(){return(0,import_node_crypto3.randomBytes)(12).toString("hex")}function stateControl(fingerprint){return{schemaVersion:STATE_SCHEMA_VERSION,fingerprint,generation:`generation-${fingerprint}`}}function serializeStateControl(control){return JSON.stringify({fingerprint:control.fingerprint,generation:control.generation,schemaVersion:control.schemaVersion})}function parseStateControl(target,runtime){const bytes=readSecureFile(target,MAX_CONTROL_FILE_BYTES,runtime);let raw;try{raw=UTF8_DECODER2.decode(bytes)}catch{fail2(`${target} is not valid UTF-8`)}let parsed;try{parsed=JSON.parse(raw)}catch{fail2(`${target} is not valid JSON`)}if(typeof parsed!=="object"||parsed===null||Array.isArray(parsed)){fail2(`${target} does not contain a valid state control`)}const record2=parsed;if(Object.keys(record2).sort().join(",")!=="fingerprint,generation,schemaVersion"||record2.schemaVersion!==STATE_SCHEMA_VERSION||typeof record2.fingerprint!=="string"||!SHA256_RE2.test(record2.fingerprint)||record2.generation!==`generation-${record2.fingerprint}`){fail2(`${target} does not contain a valid state control`)}const control=stateControl(record2.fingerprint);if(serializeStateControl(control)!==raw){fail2(`${target} is not in canonical form`)}return control}function publishStateControlIfAbsent(stateDirectory,basename,control,runtime){const target=import_node_path.default.join(stateDirectory,basename);const temporary=import_node_path.default.join(stateDirectory,`.${basename}-${randomToken()}.tmp`);writeSecureNewFile(temporary,serializeStateControl(control),runtime);try{import_node_fs.default.linkSync(temporary,target)}catch(error){try{unlinkSecureControlOrTemp(temporary,runtime)}catch{}if(error.code==="EEXIST"){return{control:parseStateControl(target,runtime),created:false}}fail2(`could not atomically publish ${basename}`)}try{import_node_fs.default.unlinkSync(temporary)}catch(error){if(error.code!=="ENOENT"){fail2(`could not finalize atomic publication of ${basename}`)}}syncDirectory(stateDirectory);return{control,created:true}}function validateCorporateCaBytes(bytes){if(bytes.length<1||bytes.length>MANAGED_STARTUP_CA_MAX_BYTES){fail2(`corporate CA bundle must contain 1-${String(MANAGED_STARTUP_CA_MAX_BYTES)} bytes`)}let pem;try{pem=UTF8_DECODER2.decode(bytes)}catch{fail2("corporate CA bundle must be valid UTF-8 PEM")}const matches=[...pem.matchAll(PEM_CERTIFICATE_RE)];if(matches.length<1||matches.length>MANAGED_STARTUP_CA_MAX_CERTIFICATES||matches[0]?.index!==0){fail2(`corporate CA bundle must contain 1-${String(MANAGED_STARTUP_CA_MAX_CERTIFICATES)} PEM CA certificates`)}let cursor=0;for(const match of matches){const index=match.index;if(index===void 0||!/^(?:\r?\n)+$/u.test(pem.slice(cursor,index))&&index!==0){fail2("corporate CA bundle contains non-PEM material between certificates")}const block=match[0];let certificate;try{certificate=new import_node_crypto3.X509Certificate(block)}catch{fail2("corporate CA bundle contains an invalid X.509 certificate")}if(!certificate.ca){fail2("corporate CA bundle contains a certificate without basicConstraints CA:TRUE")}cursor=index+block.length}if(!/^(?:\r?\n)?$/u.test(pem.slice(cursor))){fail2("corporate CA bundle contains trailing non-PEM material")}}function validateManagedStartupCorporateCaTransport(encoded,profile){const expectedDigest=profile.corporateCa.bundleSha256;if(expectedDigest===null){if(encoded!==void 0){fail2("corporate CA transport must be absent when the profile has no CA digest")}return null}if(typeof encoded!=="string"||encoded.length===0||encoded.length>Math.ceil(MANAGED_STARTUP_CA_MAX_BYTES/3)*4||!/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/u.test(encoded)){fail2("corporate CA transport must be canonical standard base64")}const bytes=import_node_buffer4.Buffer.from(encoded,"base64");if(bytes.toString("base64")!==encoded){fail2("corporate CA transport must be canonical standard base64")}validateCorporateCaBytes(bytes);const actualDigest=(0,import_node_crypto3.createHash)("sha256").update(bytes).digest("hex");if(actualDigest!==expectedDigest){fail2("corporate CA bundle does not match the profile SHA-256 digest")}return bytes}function readCanonicalProfile(profilePath,runtime){const bytes=readSecureFile(profilePath,MANAGED_STARTUP_PROFILE_MAX_BYTES,runtime);let raw;try{raw=UTF8_DECODER2.decode(bytes)}catch{fail2(`${profilePath} is not valid UTF-8`)}let parsed;try{parsed=JSON.parse(raw)}catch{fail2(`${profilePath} is not valid JSON`)}let profile;try{profile=validateManagedStartupProfile(parsed)}catch(error){fail2(`${profilePath} is invalid: ${error.message}`)}if(serializeManagedStartupProfile(profile)!==raw){fail2(`${profilePath} is not a canonical managed startup profile`)}return{profile,fingerprint:fingerprintManagedStartupProfile(profile)}}function validateGeneration(stateDirectory,control,runtime,expectedAgent){if(!GENERATION_RE.test(control.generation)){fail2("state control names an invalid generation")}const directory=import_node_path.default.join(stateDirectory,control.generation);requireSecureDirectory(directory,runtime,true);const entries=import_node_fs.default.readdirSync(directory).sort();if(entries.some(entry=>entry!=="profile.json"&&entry!=="corporate-ca.pem")||!entries.includes("profile.json")){fail2(`${directory} contains missing or unsupported state files`)}const profilePath=import_node_path.default.join(directory,"profile.json");const{profile,fingerprint}=readCanonicalProfile(profilePath,runtime);if(fingerprint!==control.fingerprint){fail2(`${directory} does not match its recorded profile fingerprint`)}if(expectedAgent!==void 0&&profile.agent!==expectedAgent){fail2(`managed startup profile targets ${profile.agent}, expected ${expectedAgent}`)}const caPath=import_node_path.default.join(directory,"corporate-ca.pem");let corporateCaPath=null;if(profile.corporateCa.bundleSha256===null){if(entries.includes("corporate-ca.pem")){fail2(`${directory} contains a CA bundle that is absent from the profile`)}}else{if(!entries.includes("corporate-ca.pem")){fail2(`${directory} is missing the CA bundle recorded by the profile`)}const caBytes=readSecureFile(caPath,MANAGED_STARTUP_CA_MAX_BYTES,runtime);validateCorporateCaBytes(caBytes);if((0,import_node_crypto3.createHash)("sha256").update(caBytes).digest("hex")!==profile.corporateCa.bundleSha256){fail2(`${directory} contains a CA bundle with the wrong SHA-256 digest`)}corporateCaPath=caPath}return{directory,profilePath,corporateCaPath,profile,fingerprint}}function validateDisposableDirectory(target,runtime){requireSecureDirectory(target,runtime,true);const entries=import_node_fs.default.readdirSync(target);if(entries.length>2||entries.some(entry=>entry!=="profile.json"&&entry!=="corporate-ca.pem")){fail2(`${target} is not a recognized disposable generation`)}for(const entry of entries){const file=import_node_path.default.join(target,entry);const stat=import_node_fs.default.lstatSync(file);requireSecureRegularFileStat(stat,file,runtime)}}function discardDirectory(target,runtime){validateDisposableDirectory(target,runtime);import_node_fs.default.rmSync(target,{recursive:true})}function discardDirectoryIfPresent(target,runtime){try{import_node_fs.default.lstatSync(target)}catch(error){if(error.code==="ENOENT")return false;fail2(`could not inspect disposable generation ${target}`)}discardDirectory(target,runtime);return true}function unlinkSecureControlOrTemp(target,runtime){const stat=import_node_fs.default.lstatSync(target);requireSecureRegularFileStat(stat,target,runtime);if(stat.size>MAX_CONTROL_FILE_BYTES){fail2(`${target} exceeds the state-control size limit`)}import_node_fs.default.unlinkSync(target)}function listStateEntries(stateDirectory){const entries=import_node_fs.default.readdirSync(stateDirectory).sort();if(entries.length>MAX_STATE_ENTRIES){fail2(`state directory exceeds ${String(MAX_STATE_ENTRIES)} entries`)}return entries}function unlinkRecoverableControlTemp(stateDirectory,entry,runtime){const temporary=import_node_path.default.join(stateDirectory,entry);const stat=import_node_fs.default.lstatSync(temporary);if(stat.nlink===1){unlinkSecureControlOrTemp(temporary,runtime);return}const basename=entry.startsWith(".committed.json-")?"committed.json":entry.startsWith(".pending.json-")?"pending.json":null;const target=basename===null?null:import_node_path.default.join(stateDirectory,basename);let targetStat=null;try{targetStat=target===null?null:import_node_fs.default.lstatSync(target)}catch{fail2(`refused to remove an unpaired atomic-control temporary file: ${temporary}`)}if(stat.nlink!==2||targetStat===null||stat.dev!==targetStat.dev||stat.ino!==targetStat.ino||!stat.isFile()||stat.isSymbolicLink()||modeOf(stat)!==STATE_FILE_MODE||stat.size<1||stat.size>MAX_CONTROL_FILE_BYTES){fail2(`refused to remove an unpaired atomic-control temporary file: ${temporary}`)}requireOwner(stat,temporary,runtime);requireOwner(targetStat,target,runtime);import_node_fs.default.unlinkSync(temporary)}function cleanAtomicTemps(stateDirectory,entries,runtime){let changed=false;for(const entry of entries){const target=import_node_path.default.join(stateDirectory,entry);if(PREPARE_TEMP_RE.test(entry)){discardDirectory(target,runtime);changed=true}else if(CONTROL_TEMP_RE.test(entry)){unlinkRecoverableControlTemp(stateDirectory,entry,runtime);changed=true}}if(changed)syncDirectory(stateDirectory)}function requireKnownStateEntries(stateDirectory,entries){for(const entry of entries){if(entry==="committed.json"||entry==="pending.json"||GENERATION_RE.test(entry)||PREPARE_TEMP_RE.test(entry)||CONTROL_TEMP_RE.test(entry)){continue}fail2(`${stateDirectory} contains unsupported state component ${entry}`)}}function discardGenerationsExcept(stateDirectory,keepGeneration,runtime){for(const entry of listStateEntries(stateDirectory)){if(GENERATION_RE.test(entry)&&entry!==keepGeneration){discardDirectoryIfPresent(import_node_path.default.join(stateDirectory,entry),runtime)}}}function optionalStateControl(stateDirectory,basename,runtime){const target=import_node_path.default.join(stateDirectory,basename);try{import_node_fs.default.lstatSync(target)}catch(error){if(error.code==="ENOENT")return null;fail2(`could not inspect ${target}`)}return parseStateControl(target,runtime)}function removePendingControl(stateDirectory,runtime){try{unlinkSecureControlOrTemp(import_node_path.default.join(stateDirectory,"pending.json"),runtime)}catch(error){if(error.code==="ENOENT")return;throw error}syncDirectory(stateDirectory)}function stateControlsMatch(left,right){return left.fingerprint===right.fingerprint&&left.generation===right.generation}function recoverCommittedState(stateDirectory,committedControl,pendingControl,requested,expectedAgent,runtime){const committed=validateGeneration(stateDirectory,committedControl,runtime,expectedAgent);if(pendingControl)removePendingControl(stateDirectory,runtime);discardGenerationsExcept(stateDirectory,committedControl.generation,runtime);syncDirectory(stateDirectory);if(!stateControlsMatch(committedControl,requested)){fail2("a different startup profile is already committed; recreate the sandbox to change it")}return committed}function recoverState(stateDirectory,requested,expectedAgent,runtime){const initialEntries=listStateEntries(stateDirectory);requireKnownStateEntries(stateDirectory,initialEntries);cleanAtomicTemps(stateDirectory,initialEntries,runtime);const initiallyCommittedControl=optionalStateControl(stateDirectory,"committed.json",runtime);const pendingControl=optionalStateControl(stateDirectory,"pending.json",runtime);const committedAfterPendingRead=optionalStateControl(stateDirectory,"committed.json",runtime);const committedControl=committedAfterPendingRead??initiallyCommittedControl;if(committedControl){return{committed:recoverCommittedState(stateDirectory,committedControl,pendingControl,requested,expectedAgent,runtime),pending:null}}if(pendingControl){if(stateControlsMatch(pendingControl,requested)){const pending=validateGeneration(stateDirectory,pendingControl,runtime,expectedAgent);const committedAfterPendingValidation=optionalStateControl(stateDirectory,"committed.json",runtime);if(committedAfterPendingValidation){return{committed:recoverCommittedState(stateDirectory,committedAfterPendingValidation,pendingControl,requested,expectedAgent,runtime),pending:null}}discardGenerationsExcept(stateDirectory,pendingControl.generation,runtime);return{committed:null,pending}}fail2("a different startup profile is already pending; wait for it to commit or recreate")}return{committed:null,pending:null}}function createGeneration(stateDirectory,control,profileJson,corporateCa,runtime){const temporaryName=`.prepare-${String(process.pid)}-${randomToken()}`;const temporary=import_node_path.default.join(stateDirectory,temporaryName);const generation=import_node_path.default.join(stateDirectory,control.generation);let renameAttempted=false;try{import_node_fs.default.mkdirSync(temporary,{mode:STATE_DIRECTORY_MODE});import_node_fs.default.chownSync(temporary,runtime.rootUid,runtime.rootGid);import_node_fs.default.chmodSync(temporary,STATE_DIRECTORY_MODE);writeSecureNewFile(import_node_path.default.join(temporary,"profile.json"),profileJson,runtime);if(corporateCa){writeSecureNewFile(import_node_path.default.join(temporary,"corporate-ca.pem"),corporateCa,runtime)}syncDirectory(temporary);renameAttempted=true;import_node_fs.default.renameSync(temporary,generation);syncDirectory(stateDirectory)}catch(error){try{import_node_fs.default.lstatSync(temporary);discardDirectory(temporary,runtime)}catch{}if(error instanceof ManagedStartupApplicationError)throw error;if(renameAttempted&&(error.code==="EEXIST"||error.code==="ENOTEMPTY")){return validateGeneration(stateDirectory,control,runtime)}fail2(`could not atomically prepare generation ${control.generation}`)}return validateGeneration(stateDirectory,control,runtime)}function toPrepared(status,stateDirectory,generation,expectedAgent){return{status,stateDirectory,generationDirectory:generation.directory,profilePath:generation.profilePath,corporateCaPath:generation.corporateCaPath,fingerprint:generation.fingerprint,expectedAgent,profile:generation.profile}}function prepareManagedStartupApplication(input,testRuntime){const runtime=runtimeFor(testRuntime);requireContainerRoot();let profile;try{profile=decodeManagedStartupProfile(input.encodedProfile)}catch(error){fail2(error.message)}if(profile.agent!==input.expectedAgent){fail2(`managed startup profile targets ${profile.agent}, expected ${input.expectedAgent}`)}const corporateCa=validateManagedStartupCorporateCaTransport(input.corporateCaB64,profile);const profileJson=serializeManagedStartupProfile(profile);const control=stateControl(fingerprintManagedStartupProfile(profile));const stateDirectory=ensureStateDirectory(input.stateDirectory,runtime);const recovered=recoverState(stateDirectory,control,input.expectedAgent,runtime);if(recovered.committed){return toPrepared("already-committed",stateDirectory,recovered.committed,input.expectedAgent)}if(recovered.pending){return toPrepared("prepared",stateDirectory,recovered.pending,input.expectedAgent)}const generation=createGeneration(stateDirectory,control,profileJson,corporateCa,runtime);const publication=publishStateControlIfAbsent(stateDirectory,"pending.json",control,runtime);if(publication.control.fingerprint!==control.fingerprint||publication.control.generation!==control.generation){discardDirectoryIfPresent(generation.directory,runtime);syncDirectory(stateDirectory);fail2("a different startup profile won the pending-state transaction")}const committedAfterPublication=optionalStateControl(stateDirectory,"committed.json",runtime);if(committedAfterPublication){if(committedAfterPublication.fingerprint!==control.fingerprint||committedAfterPublication.generation!==control.generation){if(publication.created){removePendingControl(stateDirectory,runtime);discardDirectoryIfPresent(generation.directory,runtime);syncDirectory(stateDirectory)}fail2("a different startup profile committed during pending-state publication")}const committedGeneration=validateGeneration(stateDirectory,committedAfterPublication,runtime,input.expectedAgent);removePendingControl(stateDirectory,runtime);discardGenerationsExcept(stateDirectory,committedAfterPublication.generation,runtime);return toPrepared("already-committed",stateDirectory,committedGeneration,input.expectedAgent)}const activeGeneration=publication.created?generation:validateGeneration(stateDirectory,publication.control,runtime,input.expectedAgent);return toPrepared("prepared",stateDirectory,activeGeneration,input.expectedAgent)}function validatePreparedHandle(handle){if(!import_node_path.default.isAbsolute(handle.stateDirectory)||!SHA256_RE2.test(handle.fingerprint)||handle.generationDirectory!==import_node_path.default.join(handle.stateDirectory,`generation-${handle.fingerprint}`)||handle.profilePath!==import_node_path.default.join(handle.generationDirectory,"profile.json")||handle.corporateCaPath!==null&&handle.corporateCaPath!==import_node_path.default.join(handle.generationDirectory,"corporate-ca.pem")){fail2("prepared startup handle is malformed")}return stateControl(handle.fingerprint)}function commitManagedStartupApplication(prepared,testRuntime){const runtime=runtimeFor(testRuntime);requireContainerRoot();const requested=validatePreparedHandle(prepared);const stateDirectory=ensureStateDirectory(prepared.stateDirectory,runtime);const committedControl=optionalStateControl(stateDirectory,"committed.json",runtime);if(committedControl){if(committedControl.fingerprint!==requested.fingerprint||committedControl.generation!==requested.generation){fail2("a different startup profile is already committed")}const generation2=validateGeneration(stateDirectory,committedControl,runtime,prepared.expectedAgent);return{...toPrepared("already-committed",stateDirectory,generation2,prepared.expectedAgent),status:"committed"}}const pendingControl=optionalStateControl(stateDirectory,"pending.json",runtime);if(!pendingControl||pendingControl.fingerprint!==requested.fingerprint||pendingControl.generation!==requested.generation){fail2("the prepared startup generation is not the active pending generation")}const generation=validateGeneration(stateDirectory,pendingControl,runtime,prepared.expectedAgent);const publication=publishStateControlIfAbsent(stateDirectory,"committed.json",pendingControl,runtime);if(publication.control.fingerprint!==requested.fingerprint||publication.control.generation!==requested.generation){fail2("a different startup profile won the committed-state transaction")}removePendingControl(stateDirectory,runtime);discardGenerationsExcept(stateDirectory,publication.control.generation,runtime);syncDirectory(stateDirectory);return{...toPrepared("already-committed",stateDirectory,generation,prepared.expectedAgent),status:"committed"}}var SHIPPED_AGENT_SET=new Set(MANAGED_STARTUP_AGENTS);var DEFAULT_DEPENDENCIES={prepareApplication:input=>prepareManagedStartupApplication(input),commitApplication:prepared=>commitManagedStartupApplication(prepared)};var ManagedStartupCoordinatorError=class extends Error{constructor(message){super(`Managed startup coordination failed: ${message}`);this.name="ManagedStartupCoordinatorError"}};function fail3(message){throw new ManagedStartupCoordinatorError(message)}function createAdapterRegistry(adapters2){const byAgent=new Map;for(const adapter of adapters2){if(typeof adapter!=="object"||adapter===null||!SHIPPED_AGENT_SET.has(adapter.agent)||typeof adapter.apply!=="function"){fail3("every adapter must identify one shipped agent and provide an apply function")}if(byAgent.has(adapter.agent)){fail3(`duplicate adapter registered for ${adapter.agent}`)}byAgent.set(adapter.agent,adapter)}const missing=MANAGED_STARTUP_AGENTS.filter(agent=>!byAgent.has(agent));if(missing.length>0){fail3(`missing adapter for ${missing.join(", ")}`)}if(byAgent.size!==MANAGED_STARTUP_AGENTS.length){fail3("adapter registry must contain exactly the shipped agents")}return Object.freeze(Object.fromEntries(MANAGED_STARTUP_AGENTS.map(agent=>{const adapter=byAgent.get(agent);if(!adapter)fail3(`missing adapter for ${agent}`);return[agent,adapter]})))}function requirePreparedIdentity(prepared,requestedAgent){if(prepared.expectedAgent!==requestedAgent||prepared.profile.agent!==requestedAgent){fail3(`prepared profile targets ${prepared.profile.agent}, expected ${requestedAgent}`)}}function adapterContext(prepared){return Object.freeze({agent:prepared.profile.agent,profile:prepared.profile,fingerprint:prepared.fingerprint,generationDirectory:prepared.generationDirectory,profilePath:prepared.profilePath,corporateCaPath:prepared.corporateCaPath})}async function coordinateManagedStartupApplication(input,adapters2,dependencies=DEFAULT_DEPENDENCIES){const registry=createAdapterRegistry(adapters2);const prepared=await dependencies.prepareApplication(input);requirePreparedIdentity(prepared,input.expectedAgent);if(prepared.status==="already-committed"){return{adapterApplied:false,application:await dependencies.commitApplication(prepared)}}const adapter=registry[prepared.profile.agent];if(adapter.agent!==prepared.profile.agent){fail3(`adapter registry cross-dispatch detected for ${prepared.profile.agent}`)}await adapter.apply(adapterContext(prepared));return{adapterApplied:true,application:await dependencies.commitApplication(prepared)}}var import_node_crypto4=require("node:crypto");var MANAGED_STARTUP_ROOT_APPLY_SCHEMA_VERSION=1;var MANAGED_STARTUP_ROOT_APPLY_MAX_BYTES=320*1024;var MAX_CORPORATE_CA_ENCODED_BYTES=4*Math.ceil(128*1024/3);var SHA256_RE3=/^[a-f0-9]{64}$/u;var STANDARD_BASE64_RE=/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/u;var MCP_SHADOW_DIAGNOSTICS_ENV="NEMOCLAW_MCP_SHADOW_DIAGNOSTICS";var MANAGED_STARTUP_APPLICATION_RUNTIME_ENV_KEYS=Object.freeze(MANAGED_STARTUP_PROFILE_DEFERRED_RUNTIME_INPUTS.openclaw.filter(({admission,owner})=>admission==="managed-launch-forwarded"&&owner==="application-environment").map(({input})=>input));function selectManagedStartupApplicationRuntimeEnvironment(environment){const selected={};for(const name of MANAGED_STARTUP_APPLICATION_RUNTIME_ENV_KEYS){const value=environment[name];if(name===MCP_SHADOW_DIAGNOSTICS_ENV){if(value?.trim()==="1")selected[name]="1";continue}if(value!==void 0)selected[name]=value}return Object.freeze(selected)}function fail4(message){throw new Error(`Managed startup root application request is invalid: ${message}`)}function exactAgent(value){if(typeof value==="string"&&MANAGED_STARTUP_AGENTS.includes(value)){return value}return fail4("agent is unsupported")}function createManagedStartupRootApplyRequest(input){const agent=exactAgent(input.agent);if(input.encodedProfile.length===0||input.encodedProfile.length>MANAGED_STARTUP_PROFILE_MAX_ENCODED_BYTES){fail4("encoded profile exceeds its bounded transport")}const profile=decodeManagedStartupProfile(input.encodedProfile);if(profile.agent!==agent){fail4(`profile targets ${profile.agent}, expected ${agent}`)}const corporateCaB64=input.corporateCaB64??null;if(corporateCaB64!==null&&(corporateCaB64.length===0||corporateCaB64.length>MAX_CORPORATE_CA_ENCODED_BYTES||!STANDARD_BASE64_RE.test(corporateCaB64)||Buffer.from(corporateCaB64,"base64").toString("base64")!==corporateCaB64)){fail4("corporate CA is not canonical bounded base64")}if(profile.corporateCa.bundleSha256!==null!==(corporateCaB64!==null)){fail4("corporate CA transport does not match the profile")}if(corporateCaB64!==null&&(0,import_node_crypto4.createHash)("sha256").update(Buffer.from(corporateCaB64,"base64")).digest("hex")!==profile.corporateCa.bundleSha256){fail4("corporate CA does not match the profile digest")}return Object.freeze({schemaVersion:MANAGED_STARTUP_ROOT_APPLY_SCHEMA_VERSION,agent,encodedProfile:input.encodedProfile,profileFingerprint:fingerprintManagedStartupProfile(profile),corporateCaB64})}function serializeManagedStartupRootApplyRequest(request){const normalized=createManagedStartupRootApplyRequest({agent:request.agent,encodedProfile:request.encodedProfile,...request.corporateCaB64===null?{}:{corporateCaB64:request.corporateCaB64}});if(request.schemaVersion!==MANAGED_STARTUP_ROOT_APPLY_SCHEMA_VERSION||request.profileFingerprint!==normalized.profileFingerprint||!SHA256_RE3.test(request.profileFingerprint)){fail4("schema version or profile fingerprint is invalid")}const serialized=`${JSON.stringify({agent:normalized.agent,corporateCaB64:normalized.corporateCaB64,encodedProfile:normalized.encodedProfile,profileFingerprint:normalized.profileFingerprint,schemaVersion:normalized.schemaVersion})} +`;if(Buffer.byteLength(serialized,"utf8")>MANAGED_STARTUP_ROOT_APPLY_MAX_BYTES){fail4("serialized request exceeds its bounded transport")}return serialized}function parseManagedStartupRootApplyRequest(text){if(text.length===0||Buffer.byteLength(text,"utf8")>MANAGED_STARTUP_ROOT_APPLY_MAX_BYTES){fail4("serialized request is empty or too large")}let parsed;try{parsed=JSON.parse(text)}catch{fail4("serialized request is not valid JSON")}if(typeof parsed!=="object"||parsed===null||Array.isArray(parsed)){fail4("serialized request must be an object")}const record2=parsed;const expectedKeys=["agent","corporateCaB64","encodedProfile","profileFingerprint","schemaVersion"];if(Object.keys(record2).sort().join(",")!==expectedKeys.sort().join(",")||record2.schemaVersion!==MANAGED_STARTUP_ROOT_APPLY_SCHEMA_VERSION||typeof record2.encodedProfile!=="string"||typeof record2.profileFingerprint!=="string"||record2.corporateCaB64!==null&&typeof record2.corporateCaB64!=="string"){fail4("serialized request has an invalid schema")}const request=createManagedStartupRootApplyRequest({agent:exactAgent(record2.agent),encodedProfile:record2.encodedProfile,...record2.corporateCaB64===null?{}:{corporateCaB64:record2.corporateCaB64}});if(record2.profileFingerprint!==request.profileFingerprint||!SHA256_RE3.test(record2.profileFingerprint)){fail4("profile fingerprint does not match the encoded profile")}if(serializeManagedStartupRootApplyRequest(request)!==text){fail4("serialized request is not canonical")}return request}var import_node_crypto5=require("node:crypto");var import_node_fs2=__toESM(require("node:fs"));var import_node_path2=__toESM(require("node:path"));var TRANSACTION_SCHEMA_VERSION=1;var MAX_TRANSACTION_FILES=128;var MAX_TRANSACTION_FILE_BYTES=8*1024*1024;var MAX_TRANSACTION_TOTAL_BYTES=32*1024*1024;var MAX_MANIFEST_BYTES=256*1024;var MAX_COMMIT_RECEIPT_BYTES=4096;var TRANSACTION_PARENT_DIRECTORY_MODE=493;var TRANSACTION_DIRECTORY_MODE=448;var TRANSACTION_FILE_MODE=256;var ATOMIC_TEMPORARY_FILE_MODE=384;var MANAGED_STARTUP_SHARED_TRANSACTION_DIRECTORY="/var/lib/nemoclaw/managed-startup-shared-state-transaction-v1";var MANAGED_STARTUP_SHARED_ROLLBACK_RECEIPT_DIRECTORY="/run/nemoclaw/managed-startup-shared-rollback-receipt-v1";var MANAGED_STARTUP_SHARED_COMMIT_RECEIPT_DIRECTORY="/var/lib/nemoclaw/managed-startup-shared-state-commit-v1";var MANAGED_STARTUP_SHARED_COMMIT_RECEIPT_FILE="receipt.json";function fail5(message){throw new Error(`Managed startup shared-state transaction failed: ${message}`)}function resolveOptions(options={}){const sandboxRoot=import_node_path2.default.resolve(options.sandboxRoot??"/sandbox");const transactionDirectory=import_node_path2.default.resolve(options.transactionDirectory??MANAGED_STARTUP_SHARED_TRANSACTION_DIRECTORY);const commitReceiptDirectory=import_node_path2.default.resolve(options.commitReceiptDirectory??(options.transactionDirectory?import_node_path2.default.join(import_node_path2.default.dirname(transactionDirectory),import_node_path2.default.basename(MANAGED_STARTUP_SHARED_COMMIT_RECEIPT_DIRECTORY)):MANAGED_STARTUP_SHARED_COMMIT_RECEIPT_DIRECTORY));if(transactionDirectory===sandboxRoot||transactionDirectory.startsWith(`${sandboxRoot}${import_node_path2.default.sep}`)||commitReceiptDirectory===sandboxRoot||commitReceiptDirectory.startsWith(`${sandboxRoot}${import_node_path2.default.sep}`)||import_node_path2.default.dirname(commitReceiptDirectory)!==import_node_path2.default.dirname(transactionDirectory)||commitReceiptDirectory===transactionDirectory){fail5("transaction and commit receipts require distinct paths outside sandbox-shared state")}const bootstrapIdentity=options.bootstrapIdentity??null;if(bootstrapIdentity!==null&&!/^[a-f0-9]{64}$/u.test(bootstrapIdentity)){fail5("bootstrap identity must encode 32 lowercase-hex bytes")}return{sandboxRoot,transactionParentDirectory:import_node_path2.default.dirname(transactionDirectory),transactionDirectory,backupDirectory:import_node_path2.default.join(transactionDirectory,"backups"),manifestFile:import_node_path2.default.join(transactionDirectory,"manifest.json"),commitReceiptDirectory,commitReceiptFile:import_node_path2.default.join(commitReceiptDirectory,MANAGED_STARTUP_SHARED_COMMIT_RECEIPT_FILE),trustedUid:options.trustedUid??0,trustedGid:options.trustedGid??0,readOnlyReceipt:options.readOnlyReceipt??false,bootstrapIdentity}}function modeOf2(stat){if(typeof stat.mode==="bigint"){return Number(stat.mode&0o7777n)}return stat.mode&4095}function requireTransactionIdentity(options){const expectedUid=options.readOnlyReceipt?0:options.trustedUid;const expectedGid=options.readOnlyReceipt?0:options.trustedGid;if(process.geteuid?.()!==expectedUid||process.getegid?.()!==expectedGid){fail5("transaction control requires the trusted effective identity")}}function pathExistsNoFollow(target){try{import_node_fs2.default.lstatSync(target);return true}catch(error){if(error.code==="ENOENT")return false;fail5(`could not inspect ${target}`)}}function requireDirectory(target,options,expectedMode=null){let stat;try{stat=import_node_fs2.default.lstatSync(target)}catch{fail5(`required directory is missing: ${target}`)}if(stat.isSymbolicLink()||!stat.isDirectory()){fail5(`required directory is unsafe: ${target}`)}if(expectedMode!==null&&(stat.uid!==options.trustedUid||stat.gid!==options.trustedGid||modeOf2(stat)!==expectedMode)){fail5(`${target} must be ${options.trustedUid}:${options.trustedGid} mode ${expectedMode.toString(8)}`)}return stat}function requireTransactionBoundaries(options){requireDirectory(options.sandboxRoot,options);requireDirectory(options.transactionParentDirectory,options,TRANSACTION_PARENT_DIRECTORY_MODE)}function sameStableMetadata(left,right){return left.dev===right.dev&&left.ino===right.ino&&left.mode===right.mode&&left.nlink===right.nlink&&left.uid===right.uid&&left.gid===right.gid&&left.size===right.size&&left.mtimeNs===right.mtimeNs&&left.ctimeNs===right.ctimeNs}function readStableFile(target,maxBytes){const noFollow=import_node_fs2.default.constants.O_NOFOLLOW;if(typeof noFollow!=="number")fail5("O_NOFOLLOW is unavailable");let descriptor;try{descriptor=import_node_fs2.default.openSync(target,import_node_fs2.default.constants.O_RDONLY|noFollow)}catch{fail5(`could not safely open ${target}`)}try{const before=import_node_fs2.default.fstatSync(descriptor,{bigint:true});if(!before.isFile()||before.nlink!==1n||before.size<0n||before.size>BigInt(maxBytes)){fail5(`refusing unsafe or oversized transaction file ${target}`)}const bytes=Buffer.alloc(Number(before.size));let offset=0;while(offset!segment||segment==="."||segment==="..")){fail5(`unsafe transaction path ${JSON.stringify(value)}`)}return segments.join("/")}function absoluteTarget(relativePath,options){const safe=safeRelativePath(relativePath);const target=import_node_path2.default.resolve(options.sandboxRoot,safe);if(!target.startsWith(`${options.sandboxRoot}${import_node_path2.default.sep}`)){fail5(`transaction target escapes the sandbox root: ${relativePath}`)}return target}function relativeTarget(target,options){return safeRelativePath(import_node_path2.default.relative(options.sandboxRoot,target))}function validateExistingAncestors(target,expectedAgent,options){const relative=relativeTarget(target,options);const sandboxStat=requireDirectory(options.sandboxRoot,options);const outputRoot=agentRoot(expectedAgent,options.sandboxRoot);if(target!==outputRoot&&!target.startsWith(`${outputRoot}${import_node_path2.default.sep}`)){fail5(`transaction target escapes the ${expectedAgent} state root: ${target}`)}let current=options.sandboxRoot;let expectedDevice=sandboxStat.dev;const segments=relative.split("/").slice(0,-1);for(const segment of segments){current=import_node_path2.default.join(current,segment);let stat;try{stat=import_node_fs2.default.lstatSync(current)}catch(error){if(error.code==="ENOENT")return;fail5(`could not inspect transaction path ancestor ${current}`)}if(stat.isSymbolicLink()||!stat.isDirectory()){fail5(`transaction path ancestor is unsafe: ${current}`)}if(current===outputRoot&&expectedAgent==="hermes"){expectedDevice=stat.dev}else if(stat.dev!==expectedDevice){fail5(`transaction path crosses a nested filesystem mount: ${current}`)}}}function managedOutputDevice(expectedAgent,options){const sandboxStat=requireDirectory(options.sandboxRoot,options);const outputRoot=agentRoot(expectedAgent,options.sandboxRoot);let stat;try{stat=import_node_fs2.default.lstatSync(outputRoot)}catch(error){if(error.code==="ENOENT")return sandboxStat.dev;fail5(`could not inspect managed output root ${outputRoot}`)}if(stat.isSymbolicLink()||!stat.isDirectory()){fail5(`managed output root is unsafe: ${outputRoot}`)}if(expectedAgent!=="hermes"&&stat.dev!==sandboxStat.dev){fail5(`managed output root crosses a nested filesystem mount: ${outputRoot}`)}return stat.dev}function agentRoot(agent,sandboxRoot){switch(agent){case"openclaw":return import_node_path2.default.join(sandboxRoot,".openclaw");case"hermes":return import_node_path2.default.join(sandboxRoot,".hermes");case"langchain-deepagents-code":return import_node_path2.default.join(sandboxRoot,".deepagents");case"pi":return import_node_path2.default.join(sandboxRoot,".pi")}}function resolveUnderAgentRoot(root,relativePath){const safe=safeRelativePath(relativePath);const target=import_node_path2.default.resolve(root,safe);if(!target.startsWith(`${root}${import_node_path2.default.sep}`)){fail5(`managed output escapes the agent root: ${relativePath}`)}return target}function renderTarget(root,agent,target){if(agent==="openclaw"&&target==="openclaw.json"){return import_node_path2.default.join(root,"openclaw.json")}const prefix=agent==="openclaw"?"~/.openclaw/":agent==="hermes"?"~/.hermes/":null;if(!prefix||!target.startsWith(prefix)){fail5(`unsupported managed messaging render target ${JSON.stringify(target)}`)}return resolveUnderAgentRoot(root,target.slice(prefix.length))}function managedOutputTargets(profile,options){const root=agentRoot(profile.agent,options.sandboxRoot);const files=new Set;const directories=new Set([root]);switch(profile.agent){case"openclaw":files.add(import_node_path2.default.join(root,"openclaw.json"));files.add(import_node_path2.default.join(root,".config-hash"));break;case"hermes":files.add(import_node_path2.default.join(root,"config.yaml"));files.add(import_node_path2.default.join(root,".env"));files.add(import_node_path2.default.join(root,".config-hash"));break;case"langchain-deepagents-code":files.add(import_node_path2.default.join(root,"config.toml"));directories.add(import_node_path2.default.join(root,".state"));directories.add(import_node_path2.default.join(root,"skills"));break;case"pi":directories.add(import_node_path2.default.join(root,"agent"));files.add(import_node_path2.default.join(root,"agent","models.json"));break}if(profile.messaging.plan!==null){const plan=parseSandboxMessagingPlan(profile.messaging.plan,{agent:profile.agent});if(!plan)fail5("managed messaging plan is invalid");for(const render of selectEnabledMessagingAgentRender(plan)){if(typeof render.target!=="string")continue;files.add(renderTarget(root,profile.agent,render.target))}for(const step of selectEnabledPostAgentInstallBuildFiles(plan)){if(typeof step.value!=="object"||step.value===null){continue}const outputPath=step.value.path;if(typeof outputPath==="string"){files.add(resolveUnderAgentRoot(root,outputPath))}}}for(const file of files){let parent=import_node_path2.default.dirname(file);while(parent!==options.sandboxRoot&&parent.startsWith(`${root}${import_node_path2.default.sep}`)){directories.add(parent);if(parent===root)break;parent=import_node_path2.default.dirname(parent)}}return{files:[...files].sort(),directories:[...directories].sort((left,right)=>left.split(import_node_path2.default.sep).length-right.split(import_node_path2.default.sep).length)}}function snapshotFile(target,index,expectedAgent,options){validateExistingAncestors(target,expectedAgent,options);let stat;try{stat=import_node_fs2.default.lstatSync(target)}catch(error){if(error.code==="ENOENT"){return{receipt:{path:relativeTarget(target,options),state:"absent"},bytes:null}}fail5(`could not inspect managed output ${target}`)}if(stat.isSymbolicLink()||!stat.isFile()||stat.nlink!==1){fail5(`managed output is not a safe regular file: ${target}`)}if(stat.dev!==managedOutputDevice(expectedAgent,options)){fail5(`managed output crosses a nested filesystem mount: ${target}`)}const stable=readStableFile(target,MAX_TRANSACTION_FILE_BYTES);const size=Number(stable.stat.size);const backup=`${String(index).padStart(3,"0")}.bin`;return{receipt:{path:relativeTarget(target,options),state:"file",backup,sha256:(0,import_node_crypto5.createHash)("sha256").update(stable.bytes).digest("hex"),size,uid:Number(stable.stat.uid),gid:Number(stable.stat.gid),mode:Number(stable.stat.mode&0o7777n)},bytes:stable.bytes}}function snapshotDirectory(target,expectedAgent,options){validateExistingAncestors(import_node_path2.default.join(target,".receipt"),expectedAgent,options);let stat;try{stat=import_node_fs2.default.lstatSync(target)}catch(error){if(error.code==="ENOENT"){return{path:relativeTarget(target,options),state:"absent"}}fail5(`could not inspect managed output directory ${target}`)}if(stat.isSymbolicLink()||!stat.isDirectory()){fail5(`managed output directory is unsafe: ${target}`)}if(stat.dev!==managedOutputDevice(expectedAgent,options)){fail5(`managed output directory crosses a nested filesystem mount: ${target}`)}return{path:relativeTarget(target,options),state:"directory",uid:stat.uid,gid:stat.gid,mode:modeOf2(stat)}}function atomicWriteTrustedFile(target,contents,mode,uid,gid){const parent=import_node_path2.default.dirname(target);const temporary=import_node_path2.default.join(parent,`.${import_node_path2.default.basename(target)}.${(0,import_node_crypto5.randomBytes)(12).toString("hex")}`);let descriptor;try{descriptor=import_node_fs2.default.openSync(temporary,import_node_fs2.default.constants.O_CREAT|import_node_fs2.default.constants.O_EXCL|import_node_fs2.default.constants.O_WRONLY|import_node_fs2.default.constants.O_NOFOLLOW,384);import_node_fs2.default.writeFileSync(descriptor,contents);import_node_fs2.default.fchownSync(descriptor,uid,gid);import_node_fs2.default.fchmodSync(descriptor,mode);import_node_fs2.default.fsyncSync(descriptor);import_node_fs2.default.closeSync(descriptor);descriptor=void 0;import_node_fs2.default.renameSync(temporary,target)}catch(error){if(descriptor!==void 0)import_node_fs2.default.closeSync(descriptor);try{import_node_fs2.default.unlinkSync(temporary)}catch{}fail5(`could not atomically write ${target}: ${error.message}`)}}function fsyncDirectory(directory){const descriptor=import_node_fs2.default.openSync(directory,import_node_fs2.default.constants.O_RDONLY);try{import_node_fs2.default.fsyncSync(descriptor)}finally{import_node_fs2.default.closeSync(descriptor)}}function canonicalManifest(manifest){return`${JSON.stringify(manifest,null,2)} `}function canonicalLegacyManifest(manifest){return`${JSON.stringify({schemaVersion:manifest.schemaVersion,agent:manifest.agent,profileFingerprint:manifest.profileFingerprint,files:manifest.files,directories:manifest.directories},null,2)} `}function canonicalCommitReceipt(receipt){return`${JSON.stringify(receipt,null,2)} -`}function requireExactKeys(record,keys){if(Object.keys(record).sort().join(",")!==[...keys].sort().join(",")){fail4("transaction manifest contains unexpected fields")}}function parseCommitReceipt(text){let parsed;try{parsed=JSON.parse(text)}catch{fail4("commit receipt is not valid JSON")}if(typeof parsed!=="object"||parsed===null||Array.isArray(parsed)){fail4("commit receipt must be an object")}const record=parsed;requireExactKeys(record,["agent","bootstrapIdentity","profileFingerprint","schemaVersion"]);if(record.schemaVersion!==TRANSACTION_SCHEMA_VERSION||!MANAGED_STARTUP_AGENTS.includes(String(record.agent))||typeof record.profileFingerprint!=="string"||!/^[a-f0-9]{64}$/u.test(record.profileFingerprint)||typeof record.bootstrapIdentity!=="string"||!/^[a-f0-9]{64}$/u.test(record.bootstrapIdentity)){fail4("commit receipt has an invalid envelope")}const receipt={schemaVersion:TRANSACTION_SCHEMA_VERSION,agent:record.agent,profileFingerprint:record.profileFingerprint,bootstrapIdentity:record.bootstrapIdentity};if(canonicalCommitReceipt(receipt)!==text){fail4("commit receipt is not canonical")}return receipt}function safeMetadata(value){return Number.isSafeInteger(value)&&value>=0}function parseManifest(text){let parsed;try{parsed=JSON.parse(text)}catch{fail4("transaction manifest is not valid JSON")}if(typeof parsed!=="object"||parsed===null||Array.isArray(parsed)){fail4("transaction manifest must be an object")}const record=parsed;const hasBootstrapIdentity=Object.hasOwn(record,"bootstrapIdentity");requireExactKeys(record,hasBootstrapIdentity?["agent","bootstrapIdentity","directories","files","profileFingerprint","schemaVersion"]:["agent","directories","files","profileFingerprint","schemaVersion"]);const bootstrapIdentity=hasBootstrapIdentity?record.bootstrapIdentity:null;if(record.schemaVersion!==TRANSACTION_SCHEMA_VERSION||!MANAGED_STARTUP_AGENTS.includes(String(record.agent))||typeof record.profileFingerprint!=="string"||!/^[a-f0-9]{64}$/u.test(record.profileFingerprint)||!(bootstrapIdentity===null||typeof bootstrapIdentity==="string"&&/^[a-f0-9]{64}$/u.test(bootstrapIdentity))||!Array.isArray(record.files)||!Array.isArray(record.directories)||record.files.length>MAX_TRANSACTION_FILES||record.directories.length>MAX_TRANSACTION_FILES*4){fail4("transaction manifest has an invalid envelope")}const files=record.files.map(value=>{if(typeof value!=="object"||value===null||Array.isArray(value)){return fail4("transaction file receipt must be an object")}const receipt=value;if(typeof receipt.path!=="string"){return fail4("transaction file receipt path must be a string")}const receiptPath=safeRelativePath(receipt.path);if(receipt.state==="absent"){requireExactKeys(receipt,["path","state"]);return{path:receiptPath,state:"absent"}}requireExactKeys(receipt,["backup","gid","mode","path","sha256","size","state","uid"]);if(receipt.state!=="file"||typeof receipt.backup!=="string"||!/^[0-9]{3}\.bin$/u.test(receipt.backup)||typeof receipt.sha256!=="string"||!/^[a-f0-9]{64}$/u.test(receipt.sha256)||!safeMetadata(receipt.size)||receipt.size>MAX_TRANSACTION_FILE_BYTES||!safeMetadata(receipt.uid)||!safeMetadata(receipt.gid)||!safeMetadata(receipt.mode)||receipt.mode>4095){return fail4("transaction file receipt is invalid")}return{path:receiptPath,state:"file",backup:receipt.backup,sha256:receipt.sha256,size:receipt.size,uid:receipt.uid,gid:receipt.gid,mode:receipt.mode}});const directories=record.directories.map(value=>{if(typeof value!=="object"||value===null||Array.isArray(value)){return fail4("transaction directory receipt must be an object")}const receipt=value;if(typeof receipt.path!=="string"){return fail4("transaction directory receipt path must be a string")}const receiptPath=safeRelativePath(receipt.path);if(receipt.state==="absent"){requireExactKeys(receipt,["path","state"]);return{path:receiptPath,state:"absent"}}requireExactKeys(receipt,["gid","mode","path","state","uid"]);if(receipt.state!=="directory"||!safeMetadata(receipt.uid)||!safeMetadata(receipt.gid)||!safeMetadata(receipt.mode)||receipt.mode>4095){return fail4("transaction directory receipt is invalid")}return{path:receiptPath,state:"directory",uid:receipt.uid,gid:receipt.gid,mode:receipt.mode}});const filePaths=files.map(receipt=>receipt.path);const directoryPaths=directories.map(receipt=>receipt.path);const backupNames=files.filter(receipt=>receipt.state==="file").map(receipt=>receipt.backup);if(new Set(filePaths).size!==filePaths.length||new Set(directoryPaths).size!==directoryPaths.length||new Set(backupNames).size!==backupNames.length){fail4("transaction manifest contains duplicate receipts")}const manifest={schemaVersion:TRANSACTION_SCHEMA_VERSION,agent:record.agent,profileFingerprint:record.profileFingerprint,bootstrapIdentity,files,directories};const canonical=hasBootstrapIdentity?canonicalManifest(manifest):canonicalLegacyManifest(manifest);if(canonical!==text){fail4("transaction manifest is not canonical")}return manifest}function requireTrustedTransactionPath(target,mode,options){const stat=import_node_fs2.default.lstatSync(target);if(stat.isSymbolicLink()||(mode===TRANSACTION_DIRECTORY_MODE?!stat.isDirectory():!stat.isFile())||!options.readOnlyReceipt&&(stat.uid!==options.trustedUid||stat.gid!==options.trustedGid)||modeOf2(stat)!==mode){fail4(`transaction artifact has unsafe metadata: ${target}`)}}function requireReadOnlyReceiptMount(target,options){if(!options.readOnlyReceipt)return;const probe=import_node_path2.default.join(target,".nemoclaw-write-probe");let descriptor;try{descriptor=import_node_fs2.default.openSync(probe,import_node_fs2.default.constants.O_CREAT|import_node_fs2.default.constants.O_EXCL|import_node_fs2.default.constants.O_WRONLY|import_node_fs2.default.constants.O_NOFOLLOW,384);import_node_fs2.default.closeSync(descriptor);descriptor=void 0;import_node_fs2.default.unlinkSync(probe)}catch(error){if(descriptor!==void 0)import_node_fs2.default.closeSync(descriptor);if(error.code==="EROFS")return;fail4("copied receipt must be mounted on a read-only filesystem")}fail4("copied receipt mount is writable")}function loadManifest(options){requireTransactionBoundaries(options);if(!pathExistsNoFollow(options.transactionDirectory))return null;requireTrustedTransactionPath(options.transactionDirectory,TRANSACTION_DIRECTORY_MODE,options);requireReadOnlyReceiptMount(options.transactionDirectory,options);requireTrustedTransactionPath(options.backupDirectory,TRANSACTION_DIRECTORY_MODE,options);requireTrustedTransactionPath(options.manifestFile,TRANSACTION_FILE_MODE,options);const stable=readStableFile(options.manifestFile,MAX_MANIFEST_BYTES);if(!options.readOnlyReceipt&&(Number(stable.stat.uid)!==options.trustedUid||Number(stable.stat.gid)!==options.trustedGid)||Number(stable.stat.mode&0o7777n)!==TRANSACTION_FILE_MODE){fail4("transaction manifest ownership changed while it was read")}return parseManifest(stable.bytes.toString("utf8"))}function transactionOptionsAt(options,transactionDirectory){return{...options,transactionDirectory,backupDirectory:import_node_path2.default.join(transactionDirectory,"backups"),manifestFile:import_node_path2.default.join(transactionDirectory,"manifest.json")}}function loadCommitReceipt(options){requireTransactionBoundaries(options);if(!pathExistsNoFollow(options.commitReceiptDirectory))return null;requireTrustedTransactionPath(options.commitReceiptDirectory,TRANSACTION_DIRECTORY_MODE,options);if(pathExistsNoFollow(options.commitReceiptFile)){requireReadOnlyReceiptMount(options.commitReceiptDirectory,options);requireTrustedTransactionPath(options.commitReceiptFile,TRANSACTION_FILE_MODE,options);const stable=readStableFile(options.commitReceiptFile,MAX_COMMIT_RECEIPT_BYTES);if(!options.readOnlyReceipt&&(Number(stable.stat.uid)!==options.trustedUid||Number(stable.stat.gid)!==options.trustedGid)||Number(stable.stat.mode&0o7777n)!==TRANSACTION_FILE_MODE){fail4("commit receipt ownership changed while it was read")}return{receipt:parseCommitReceipt(stable.bytes.toString("utf8")),compact:true}}const stagedOptions=transactionOptionsAt(options,options.commitReceiptDirectory);const staged=loadManifest(stagedOptions);if(!staged||staged.bootstrapIdentity===null){fail4("durable commit staging receipt is incomplete")}verifyAllBackups(staged.files,stagedOptions);return{receipt:{schemaVersion:TRANSACTION_SCHEMA_VERSION,agent:staged.agent,profileFingerprint:staged.profileFingerprint,bootstrapIdentity:staged.bootstrapIdentity},compact:false}}function verifyBackup(receipt,options){const backupPath=import_node_path2.default.join(options.backupDirectory,receipt.backup);requireTrustedTransactionPath(backupPath,TRANSACTION_FILE_MODE,options);const stable=readStableFile(backupPath,MAX_TRANSACTION_FILE_BYTES);const digest=(0,import_node_crypto5.createHash)("sha256").update(stable.bytes).digest("hex");if(stable.bytes.length!==receipt.size||digest!==receipt.sha256){fail4(`transaction backup does not match its receipt: ${receipt.path}`)}return stable.bytes}function verifyAllBackups(receipts,options){const backups=new Map;for(const receipt of receipts){if(receipt.state==="file"){backups.set(receipt.path,verifyBackup(receipt,options))}}return backups}function fileMatchesReceipt(target,receipt){let stat;try{stat=import_node_fs2.default.lstatSync(target)}catch(error){if(error.code==="ENOENT")return false;fail4(`could not inspect managed output ${target}`)}if(stat.isSymbolicLink()||!stat.isFile()||stat.nlink!==1)return false;const stable=readStableFile(target,MAX_TRANSACTION_FILE_BYTES);return stable.bytes.length===receipt.size&&(0,import_node_crypto5.createHash)("sha256").update(stable.bytes).digest("hex")===receipt.sha256&&Number(stable.stat.uid)===receipt.uid&&Number(stable.stat.gid)===receipt.gid&&Number(stable.stat.mode&0o7777n)===receipt.mode}function directoryMatchesReceipt(target,receipt){let stat;try{stat=import_node_fs2.default.lstatSync(target)}catch(error){if(error.code==="ENOENT")return false;fail4(`could not inspect managed output directory ${target}`)}return!stat.isSymbolicLink()&&stat.isDirectory()&&stat.uid===receipt.uid&&stat.gid===receipt.gid&&modeOf2(stat)===receipt.mode}function removeTransactionDirectory(options){requireTrustedTransactionPath(options.transactionDirectory,TRANSACTION_DIRECTORY_MODE,options);import_node_fs2.default.rmSync(options.transactionDirectory,{force:false,recursive:true});fsyncDirectory(options.transactionParentDirectory);if(pathExistsNoFollow(options.transactionDirectory)){fail4("transaction directory remained after cleanup")}}function assertCommitReceiptMatches(receipt,expected){if(receipt.agent!==expected.agent||expected.profileFingerprint!==void 0&&receipt.profileFingerprint!==expected.profileFingerprint||receipt.bootstrapIdentity!==expected.bootstrapIdentity){fail4("durable commit receipt belongs to a different bootstrap attempt")}}function loadCommitStagingManifest(options){if(!pathExistsNoFollow(options.manifestFile))return null;requireTrustedTransactionPath(options.manifestFile,TRANSACTION_FILE_MODE,options);const stable=readStableFile(options.manifestFile,MAX_MANIFEST_BYTES);if(Number(stable.stat.uid)!==options.trustedUid||Number(stable.stat.gid)!==options.trustedGid||Number(stable.stat.mode&0o7777n)!==TRANSACTION_FILE_MODE){fail4("durable commit staging manifest ownership changed while it was read")}return parseManifest(stable.bytes.toString("utf8"))}function retireInterruptedCommitReceiptWrites(receipt,options){const temporaryPattern=new RegExp(`^\\.${MANAGED_STARTUP_SHARED_COMMIT_RECEIPT_FILE.replace(".","\\.")}\\.[a-f0-9]{24}$`,"u");for(const entry of import_node_fs2.default.readdirSync(options.commitReceiptDirectory)){if(!temporaryPattern.test(entry))continue;const target=import_node_path2.default.join(options.commitReceiptDirectory,entry);const stat=import_node_fs2.default.lstatSync(target);if(stat.isSymbolicLink()||!stat.isFile()||stat.nlink!==1||stat.uid!==options.trustedUid||stat.gid!==options.trustedGid||![ATOMIC_TEMPORARY_FILE_MODE,TRANSACTION_FILE_MODE].includes(modeOf2(stat))){fail4("interrupted durable commit receipt write has unsafe metadata")}const stable=readStableFile(target,MAX_COMMIT_RECEIPT_BYTES);const mode=Number(stable.stat.mode&0o7777n);if(Number(stable.stat.uid)!==options.trustedUid||Number(stable.stat.gid)!==options.trustedGid||![ATOMIC_TEMPORARY_FILE_MODE,TRANSACTION_FILE_MODE].includes(mode)){fail4("interrupted durable commit receipt write changed during verification")}if(stable.bytes.length>0){let interruptedReceipt=null;try{interruptedReceipt=parseCommitReceipt(stable.bytes.toString("utf8"))}catch{}if(interruptedReceipt)assertCommitReceiptMatches(interruptedReceipt,receipt)}import_node_fs2.default.unlinkSync(target);fsyncDirectory(options.commitReceiptDirectory)}}function compactDurableCommitReceipt(state,options){if(!state.compact){atomicWriteTrustedFile(options.commitReceiptFile,canonicalCommitReceipt(state.receipt),TRANSACTION_FILE_MODE,options.trustedUid,options.trustedGid);fsyncDirectory(options.commitReceiptDirectory)}retireInterruptedCommitReceiptWrites(state.receipt,options);const stagedOptions=transactionOptionsAt(options,options.commitReceiptDirectory);const manifestExists=pathExistsNoFollow(stagedOptions.manifestFile);const backupsExist=pathExistsNoFollow(stagedOptions.backupDirectory);const unexpectedBeforeCleanup=import_node_fs2.default.readdirSync(options.commitReceiptDirectory).filter(entry=>![MANAGED_STARTUP_SHARED_COMMIT_RECEIPT_FILE,import_node_path2.default.basename(stagedOptions.backupDirectory),import_node_path2.default.basename(stagedOptions.manifestFile)].includes(entry));if(unexpectedBeforeCleanup.length!==0){fail4("durable commit receipt directory contains unexpected artifacts")}if(manifestExists){const staged=loadCommitStagingManifest(stagedOptions);if(!staged||staged.bootstrapIdentity===null){fail4("durable commit staging receipt disappeared during cleanup")}assertCommitReceiptMatches(state.receipt,{agent:staged.agent,profileFingerprint:staged.profileFingerprint,bootstrapIdentity:staged.bootstrapIdentity})}if(backupsExist){requireTrustedTransactionPath(stagedOptions.backupDirectory,TRANSACTION_DIRECTORY_MODE,options);import_node_fs2.default.rmSync(stagedOptions.backupDirectory,{force:false,recursive:true});fsyncDirectory(options.commitReceiptDirectory)}if(manifestExists){requireTrustedTransactionPath(stagedOptions.manifestFile,TRANSACTION_FILE_MODE,options);import_node_fs2.default.unlinkSync(stagedOptions.manifestFile);fsyncDirectory(options.commitReceiptDirectory)}const unexpected=import_node_fs2.default.readdirSync(options.commitReceiptDirectory).filter(entry=>entry!==MANAGED_STARTUP_SHARED_COMMIT_RECEIPT_FILE);if(unexpected.length!==0){fail4("durable commit receipt directory contains unexpected artifacts")}const verified=loadCommitReceipt(options);if(!verified?.compact)fail4("durable commit receipt did not compact successfully");assertCommitReceiptMatches(verified.receipt,state.receipt)}function beginManagedStartupSharedStateTransaction(profile,inputOptions={}){const options=resolveOptions(inputOptions);requireTransactionIdentity(options);if(options.readOnlyReceipt){fail4("cannot begin a transaction from a read-only rollback receipt")}requireTransactionBoundaries(options);const profileFingerprint=fingerprintManagedStartupProfile(profile);const committed=loadCommitReceipt(options);if(committed){if(options.bootstrapIdentity===null){fail4("a durable managed bootstrap commit receipt already exists")}assertCommitReceiptMatches(committed.receipt,{agent:profile.agent,profileFingerprint,bootstrapIdentity:options.bootstrapIdentity});fail4("this managed bootstrap attempt is already durably committed")}const pending=loadManifest(options);if(pending){if(pending.agent!==profile.agent||pending.profileFingerprint!==profileFingerprint||pending.bootstrapIdentity!==options.bootstrapIdentity){fail4("a pending managed startup transaction belongs to a different agent, profile fingerprint, or bootstrap attempt")}verifyAllBackups(pending.files,options);return false}const targets=managedOutputTargets(profile,options);if(targets.files.length>MAX_TRANSACTION_FILES){fail4("managed startup transaction has too many file targets")}const snapshots=targets.files.map((target,index)=>snapshotFile(target,index,profile.agent,options));const totalBytes=snapshots.reduce((sum,snapshot)=>sum+(snapshot.bytes?.length??0),0);if(totalBytes>MAX_TRANSACTION_TOTAL_BYTES){fail4("managed startup transaction backup exceeds the total size limit")}const directories=targets.directories.map(target=>snapshotDirectory(target,profile.agent,options));const manifest={schemaVersion:TRANSACTION_SCHEMA_VERSION,agent:profile.agent,profileFingerprint,bootstrapIdentity:options.bootstrapIdentity,files:snapshots.map(({receipt})=>receipt),directories};let createdTransactionIdentity;try{import_node_fs2.default.mkdirSync(options.transactionDirectory,{mode:TRANSACTION_DIRECTORY_MODE});const created=import_node_fs2.default.lstatSync(options.transactionDirectory,{bigint:true});if(!created.isDirectory()||created.isSymbolicLink()){fail4("new transaction path is not a directory")}createdTransactionIdentity={dev:created.dev,ino:created.ino,uid:created.uid,gid:created.gid};import_node_fs2.default.chownSync(options.transactionDirectory,options.trustedUid,options.trustedGid);import_node_fs2.default.chmodSync(options.transactionDirectory,TRANSACTION_DIRECTORY_MODE);fsyncDirectory(options.transactionParentDirectory);import_node_fs2.default.mkdirSync(options.backupDirectory,{mode:TRANSACTION_DIRECTORY_MODE});import_node_fs2.default.chownSync(options.backupDirectory,options.trustedUid,options.trustedGid);import_node_fs2.default.chmodSync(options.backupDirectory,TRANSACTION_DIRECTORY_MODE);fsyncDirectory(options.transactionDirectory);for(const snapshot of snapshots){if(snapshot.receipt.state!=="file"||snapshot.bytes===null)continue;atomicWriteTrustedFile(import_node_path2.default.join(options.backupDirectory,snapshot.receipt.backup),snapshot.bytes,TRANSACTION_FILE_MODE,options.trustedUid,options.trustedGid)}fsyncDirectory(options.backupDirectory);atomicWriteTrustedFile(options.manifestFile,canonicalManifest(manifest),TRANSACTION_FILE_MODE,options.trustedUid,options.trustedGid);fsyncDirectory(options.transactionDirectory);loadManifest(options)}catch(error){try{if(createdTransactionIdentity&&pathExistsNoFollow(options.transactionDirectory)){const current=import_node_fs2.default.lstatSync(options.transactionDirectory,{bigint:true});if(!current.isSymbolicLink()&¤t.isDirectory()&¤t.dev===createdTransactionIdentity.dev&¤t.ino===createdTransactionIdentity.ino&¤t.uid===createdTransactionIdentity.uid&¤t.gid===createdTransactionIdentity.gid){import_node_fs2.default.chmodSync(options.transactionDirectory,TRANSACTION_DIRECTORY_MODE);import_node_fs2.default.chownSync(options.transactionDirectory,options.trustedUid,options.trustedGid)}requireTrustedTransactionPath(options.transactionDirectory,TRANSACTION_DIRECTORY_MODE,options);import_node_fs2.default.rmSync(options.transactionDirectory,{force:true,recursive:true})}}catch{}throw error}return true}function ensureOriginalDirectories(receipts,expectedAgent,options){for(const receipt of receipts){if(receipt.state!=="directory")continue;const target=absoluteTarget(receipt.path,options);validateExistingAncestors(import_node_path2.default.join(target,".restore"),expectedAgent,options);let stat=null;try{stat=import_node_fs2.default.lstatSync(target)}catch(error){if(error.code!=="ENOENT"){fail4(`could not inspect restore directory ${target}`)}}if(stat&&(stat.isSymbolicLink()||!stat.isDirectory())){fail4(`restore directory is unsafe: ${target}`)}if(stat&&directoryMatchesReceipt(target,receipt))continue;if(!stat)import_node_fs2.default.mkdirSync(target,{mode:receipt.mode});import_node_fs2.default.chownSync(target,receipt.uid,receipt.gid);import_node_fs2.default.chmodSync(target,receipt.mode)}}function restoreFiles(receipts,backups,expectedAgent,options){for(const receipt of receipts){const target=absoluteTarget(receipt.path,options);validateExistingAncestors(target,expectedAgent,options);if(receipt.state==="absent"){let stat;try{stat=import_node_fs2.default.lstatSync(target)}catch(error){if(error.code==="ENOENT")continue;fail4(`could not inspect new managed output ${target}`)}if(stat.isDirectory()){fail4(`new managed output unexpectedly became a directory: ${target}`)}import_node_fs2.default.unlinkSync(target);continue}if(fileMatchesReceipt(target,receipt))continue;const bytes=backups.get(receipt.path);if(!bytes)fail4(`verified transaction backup is missing: ${receipt.path}`);let current=null;try{current=import_node_fs2.default.lstatSync(target)}catch(error){if(error.code!=="ENOENT"){fail4(`could not inspect managed output before restore: ${target}`)}}if(current?.isDirectory()){fail4(`managed output unexpectedly became a directory: ${target}`)}atomicWriteTrustedFile(target,bytes,receipt.mode,receipt.uid,receipt.gid)}}function restoreDirectoryMetadata(receipts,options){for(const receipt of[...receipts].reverse()){const target=absoluteTarget(receipt.path,options);if(receipt.state==="absent"){try{import_node_fs2.default.rmdirSync(target)}catch(error){if(error.code==="ENOENT")continue;fail4(`could not remove newly created managed directory ${target}`)}continue}if(directoryMatchesReceipt(target,receipt))continue;const stat=import_node_fs2.default.lstatSync(target);if(stat.isSymbolicLink()||!stat.isDirectory()){fail4(`managed directory changed type during restore: ${target}`)}import_node_fs2.default.chownSync(target,receipt.uid,receipt.gid);import_node_fs2.default.chmodSync(target,receipt.mode)}}function verifyRestoration(manifest,options){for(const receipt of manifest.files){const target=absoluteTarget(receipt.path,options);if(receipt.state==="absent"){if(pathExistsNoFollow(target)){fail4(`new managed output remained after rollback: ${target}`)}continue}const stable=readStableFile(target,MAX_TRANSACTION_FILE_BYTES);if(stable.bytes.length!==receipt.size||(0,import_node_crypto5.createHash)("sha256").update(stable.bytes).digest("hex")!==receipt.sha256||Number(stable.stat.uid)!==receipt.uid||Number(stable.stat.gid)!==receipt.gid||Number(stable.stat.mode&0o7777n)!==receipt.mode){fail4(`managed output was not restored exactly: ${target}`)}}for(const receipt of manifest.directories){const target=absoluteTarget(receipt.path,options);if(receipt.state==="absent"){if(pathExistsNoFollow(target)){fail4(`new managed directory remained after rollback: ${target}`)}continue}const stat=import_node_fs2.default.lstatSync(target);if(stat.isSymbolicLink()||!stat.isDirectory()||stat.uid!==receipt.uid||stat.gid!==receipt.gid||modeOf2(stat)!==receipt.mode){fail4(`managed directory metadata was not restored exactly: ${target}`)}}}function rollbackManagedStartupSharedStateTransaction(expectedAgent,inputOptions={}){const options=resolveOptions(inputOptions);requireTransactionIdentity(options);const committed=loadCommitReceipt(options);if(committed){if(options.bootstrapIdentity===null){fail4("shared state is already durably committed")}assertCommitReceiptMatches(committed.receipt,{agent:expectedAgent,bootstrapIdentity:options.bootstrapIdentity});fail4("shared state is already durably committed and cannot be rolled back")}const manifest=loadManifest(options);if(!manifest)return false;if(manifest.agent!==expectedAgent){fail4(`pending transaction targets ${manifest.agent}, expected ${expectedAgent}`)}if(manifest.bootstrapIdentity!==options.bootstrapIdentity){fail4("pending transaction belongs to a different bootstrap attempt")}const backups=verifyAllBackups(manifest.files,options);ensureOriginalDirectories(manifest.directories,expectedAgent,options);restoreFiles(manifest.files,backups,expectedAgent,options);restoreDirectoryMetadata(manifest.directories,options);verifyRestoration(manifest,options);if(!options.readOnlyReceipt){removeTransactionDirectory(options)}return true}function commitManagedStartupSharedStateTransaction(expectedAgent,inputOptions={}){const options=resolveOptions(inputOptions);requireTransactionIdentity(options);if(options.readOnlyReceipt){fail4("cannot commit a read-only rollback receipt")}const committed=loadCommitReceipt(options);if(committed){if(options.bootstrapIdentity===null){fail4("durable commit receipt is missing its expected bootstrap identity")}assertCommitReceiptMatches(committed.receipt,{agent:expectedAgent,bootstrapIdentity:options.bootstrapIdentity});compactDurableCommitReceipt(committed,options);return true}const manifest=loadManifest(options);if(!manifest)return false;if(manifest.agent!==expectedAgent){fail4(`pending transaction targets ${manifest.agent}, expected ${expectedAgent}`)}if(manifest.bootstrapIdentity!==options.bootstrapIdentity){fail4("pending transaction belongs to a different bootstrap attempt")}if(manifest.bootstrapIdentity===null){removeTransactionDirectory(options);return true}verifyAllBackups(manifest.files,options);if(pathExistsNoFollow(options.commitReceiptDirectory)){fail4("durable commit receipt path appeared before transaction commit")}try{import_node_fs2.default.renameSync(options.transactionDirectory,options.commitReceiptDirectory);fsyncDirectory(options.transactionParentDirectory)}catch(error){fail4(`could not atomically establish durable commit state: ${error.message}`)}const renamed=loadCommitReceipt(options);if(!renamed)fail4("durable commit state disappeared after atomic rename");assertCommitReceiptMatches(renamed.receipt,{agent:expectedAgent,profileFingerprint:manifest.profileFingerprint,bootstrapIdentity:manifest.bootstrapIdentity});compactDurableCommitReceipt(renamed,options);return true}function clearManagedStartupSharedStateCommitReceipt(expectedAgent,inputOptions={}){const options=resolveOptions(inputOptions);requireTransactionIdentity(options);if(options.readOnlyReceipt){fail4("cannot clear a durable commit from a read-only receipt")}if(options.bootstrapIdentity===null){fail4("durable commit cleanup requires its bootstrap identity")}const committed=loadCommitReceipt(options);if(!committed)return false;assertCommitReceiptMatches(committed.receipt,{agent:expectedAgent,bootstrapIdentity:options.bootstrapIdentity});compactDurableCommitReceipt(committed,options);requireTrustedTransactionPath(options.commitReceiptDirectory,TRANSACTION_DIRECTORY_MODE,options);requireTrustedTransactionPath(options.commitReceiptFile,TRANSACTION_FILE_MODE,options);const entries=import_node_fs2.default.readdirSync(options.commitReceiptDirectory);if(entries.length!==1||entries[0]!==MANAGED_STARTUP_SHARED_COMMIT_RECEIPT_FILE){fail4("durable commit receipt directory contains unexpected artifacts")}import_node_fs2.default.rmSync(options.commitReceiptDirectory,{force:false,recursive:true});fsyncDirectory(options.transactionParentDirectory);if(pathExistsNoFollow(options.commitReceiptDirectory)){fail4("durable commit receipt remained after cleanup")}return true}function getManagedStartupSharedStateTransactionStatus(expected,inputOptions={}){const options=resolveOptions({...inputOptions,bootstrapIdentity:expected.bootstrapIdentity});requireTransactionIdentity(options);const manifest=loadManifest(options);if(manifest){if(manifest.agent!==expected.agent||manifest.profileFingerprint!==expected.profileFingerprint||manifest.bootstrapIdentity!==expected.bootstrapIdentity){fail4("pending transaction does not match the expected agent, profile fingerprint, or bootstrap identity")}verifyAllBackups(manifest.files,options);return"pending"}const committed=loadCommitReceipt(options);if(!committed)return"none";assertCommitReceiptMatches(committed.receipt,expected);return"committed"}var MANAGED_STARTUP_PROFILE_ENV="NEMOCLAW_STARTUP_PROFILE_B64";var MANAGED_STARTUP_CA_ENV="NEMOCLAW_CORPORATE_CA_B64";var MANAGED_STARTUP_RUNTIME_ENV_FILE="/run/nemoclaw/managed-startup-runtime.env";var MANAGED_STARTUP_RUNTIME_EXECUTABLE="/usr/local/lib/nemoclaw/managed-startup-image-runtime.cjs";var MANAGED_STARTUP_MERGED_CA_FILE="/run/nemoclaw/managed-startup-ca-bundle.pem";var MANAGED_STARTUP_COMPLETION_FILE="/run/nemoclaw/managed-startup-complete.json";var MANAGED_STARTUP_CORPORATE_CA_FILE="/usr/local/share/nemoclaw/corporate-ca.pem";var MANAGED_STARTUP_SYSTEM_CA_ANCHOR_DIRECTORY="/usr/local/share/ca-certificates";var MANAGED_STARTUP_SYSTEM_CA_ANCHOR_RE=/^nemoclaw-corporate-ca-[0-9]{2}\.crt$/u;var SYSTEM_CA_BUNDLE_FILE="/etc/ssl/certs/ca-certificates.crt";var UPDATE_CA_CERTIFICATES_EXECUTABLE="/usr/sbin/update-ca-certificates";var MANAGED_STARTUP_TLS_ENV_NAMES=new Set(["CURL_CA_BUNDLE","GIT_SSL_CAINFO","NODE_EXTRA_CA_CERTS","REQUESTS_CA_BUNDLE","SSL_CERT_FILE"]);var MESSAGING_RUNTIME_PLAN_FILE="/usr/local/share/nemoclaw/messaging-runtime-plan.json";var ROOT_STATE_PARENT="/var/lib/nemoclaw";var ROOT_RUNTIME_DIRECTORY="/run/nemoclaw";var ROOT_OWNED_DIRECTORY_MODE=493;var MAX_TRUST_BUNDLE_BYTES=4*1024*1024;var HERMES_MANAGED_CONFIG_FILES=["/sandbox/.hermes/config.yaml","/sandbox/.hermes/.env"];var HERMES_GENERATED_MANAGED_POLICY_FILE="/sandbox/.hermes/managed-policy.json";var HERMES_INSTALLED_MANAGED_POLICY_FILE="/usr/local/share/nemoclaw/hermes-managed-policy.json";var MAX_HERMES_MANAGED_POLICY_BYTES=4*1024*1024;var FIXED_PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin";var SHA256_RE4=/^[a-f0-9]{64}$/u;var MANAGED_STARTUP_COMPLETION_SCHEMA_VERSION=1;var MAX_MANAGED_STARTUP_COMPLETION_BYTES=4096;var MAX_MANAGED_STARTUP_RUNTIME_ENVIRONMENT_BYTES=512*1024;var ManagedStartupImageActionPlanError=class extends Error{constructor(message){super(`Cannot build managed startup image action plan: ${message}`);this.name="ManagedStartupImageActionPlanError"}};var ManagedStartupImageRuntimeError=class extends Error{constructor(message){super(`Managed startup image application failed: ${message}`);this.name="ManagedStartupImageRuntimeError"}};function failActionPlan(message){throw new ManagedStartupImageActionPlanError(message)}function exactActionPlanAgent(value){if(MANAGED_STARTUP_AGENTS.includes(value)){return value}return failActionPlan(`unsupported agent ${JSON.stringify(value)}`)}function fail5(message){throw new ManagedStartupImageRuntimeError(message)}function validateManagedStartupApplicationRuntimePlan(plan){if(typeof plan!=="object"||plan===null){return fail5("application runtime plan must be an object")}const exportEnvironment=plan.exportEnvironment;const unsetEnvironment=plan.unsetEnvironment;if(typeof exportEnvironment!=="object"||exportEnvironment===null||Array.isArray(exportEnvironment)||!Array.isArray(unsetEnvironment)){return fail5("application runtime plan must contain exports and unsets")}const exports2={};for(const[name,value]of Object.entries(exportEnvironment)){if(!/^[A-Za-z_][A-Za-z0-9_]*$/u.test(name)){return fail5(`invalid application runtime environment key ${JSON.stringify(name)}`)}if(typeof value!=="string"||value.includes("\0")||/[\r\n]/u.test(value)){return fail5(`application runtime environment value for ${name} must be single-line text`)}exports2[name]=value}const unsets=new Set;for(const name of unsetEnvironment){if(typeof name!=="string"||!/^[A-Za-z_][A-Za-z0-9_]*$/u.test(name)){return fail5(`invalid application runtime unset ${JSON.stringify(name)}`)}if(unsets.has(name)){return fail5(`duplicate application runtime unset ${name}`)}if(Object.hasOwn(exports2,name)){return fail5(`application runtime cannot both export and unset ${name}`)}unsets.add(name)}return Object.freeze({exportEnvironment:Object.freeze(Object.fromEntries(Object.entries(exports2).sort(([left],[right])=>left.localeCompare(right)))),unsetEnvironment:Object.freeze([...unsets].sort())})}function applyManagedStartupCommandEnvironmentPlan(environment,plan){const validated=validateManagedStartupApplicationRuntimePlan(plan);const applied={...environment};for(const name of[...Object.keys(validated.exportEnvironment),...validated.unsetEnvironment]){delete applied[name]}return applied}function exactAgent2(value){if(MANAGED_STARTUP_AGENTS.includes(value)){return value}return fail5(`unsupported agent ${JSON.stringify(value)}`)}function managedTransactionProfile(expectedAgentInput,env=process.env){requireRoot();const expectedAgent=exactAgent2(expectedAgentInput);if(env.NEMOCLAW_MANAGED_IMAGE_CAPABILITY_UNION!=="1"){fail5("shared-state transactions require a complete managed image")}const encodedProfile=env[MANAGED_STARTUP_PROFILE_ENV];if(!encodedProfile)fail5(`${MANAGED_STARTUP_PROFILE_ENV} is required`);const profile=decodeManagedStartupProfile(encodedProfile);if(profile.agent!==expectedAgent){fail5(`shared-state transaction profile targets ${profile.agent}, expected ${expectedAgent}`)}return profile}function requireRoot(){if(process.geteuid?.()!==0){fail5("managed startup requires container effective uid 0")}}function modeOf3(stat){return stat.mode&511}function requireRootOwnedDirectory(target,mode){let stat;try{stat=import_node_fs3.default.lstatSync(target)}catch{fail5(`required root-owned directory is missing: ${target}`)}if(stat.isSymbolicLink()||!stat.isDirectory()||stat.uid!==0||stat.gid!==0||modeOf3(stat)!==mode){fail5(`${target} must be a root:root directory with mode ${mode.toString(8)}`)}}function ensureRootOwnedDirectory(target,mode=ROOT_OWNED_DIRECTORY_MODE){const parent=import_node_path3.default.dirname(target);const parentStat=import_node_fs3.default.lstatSync(parent);if(parentStat.isSymbolicLink()||!parentStat.isDirectory()||parentStat.uid!==0||parentStat.gid!==0||(modeOf3(parentStat)&18)!==0){fail5(`refusing unsafe parent directory for ${target}`)}try{import_node_fs3.default.mkdirSync(target,{mode});import_node_fs3.default.chownSync(target,0,0);import_node_fs3.default.chmodSync(target,mode)}catch(error){if(error.code!=="EEXIST"){fail5(`could not create ${target}`)}}requireRootOwnedDirectory(target,mode)}function requireSafeExistingRootTarget(target){let stat;try{stat=import_node_fs3.default.lstatSync(target)}catch(error){if(error.code==="ENOENT")return;fail5(`could not inspect ${target}`)}if(stat.isSymbolicLink()||!stat.isFile()||stat.nlink!==1||stat.uid!==0||stat.gid!==0){fail5(`refusing to replace unsafe root-owned file ${target}`)}}function atomicWriteRootFile(target,contents,mode){const parent=import_node_path3.default.dirname(target);const parentStat=import_node_fs3.default.lstatSync(parent);if(parentStat.isSymbolicLink()||!parentStat.isDirectory()||parentStat.uid!==0||parentStat.gid!==0||(modeOf3(parentStat)&18)!==0){fail5(`refusing unsafe root-owned file parent ${parent}`)}requireSafeExistingRootTarget(target);const temporary=import_node_path3.default.join(parent,`.${import_node_path3.default.basename(target)}.${(0,import_node_crypto6.randomBytes)(12).toString("hex")}`);let descriptor;try{descriptor=import_node_fs3.default.openSync(temporary,import_node_fs3.default.constants.O_CREAT|import_node_fs3.default.constants.O_EXCL|import_node_fs3.default.constants.O_WRONLY|import_node_fs3.default.constants.O_NOFOLLOW,384);import_node_fs3.default.fchownSync(descriptor,0,0);import_node_fs3.default.writeFileSync(descriptor,contents);import_node_fs3.default.fchmodSync(descriptor,mode);import_node_fs3.default.fsyncSync(descriptor);import_node_fs3.default.closeSync(descriptor);descriptor=void 0;import_node_fs3.default.renameSync(temporary,target)}catch(error){if(descriptor!==void 0)import_node_fs3.default.closeSync(descriptor);try{import_node_fs3.default.unlinkSync(temporary)}catch{}fail5(`could not atomically write ${target}: ${error.message}`)}const stat=import_node_fs3.default.lstatSync(target);if(stat.isSymbolicLink()||!stat.isFile()||stat.nlink!==1||stat.uid!==0||stat.gid!==0||modeOf3(stat)!==mode){fail5(`root-owned output failed metadata verification: ${target}`)}}function removeSafeRootFile(target){requireSafeExistingRootTarget(target);try{import_node_fs3.default.unlinkSync(target)}catch(error){if(error.code!=="ENOENT"){fail5(`could not remove ${target}`)}}}function trustedExecutable(target){try{const stat=import_node_fs3.default.lstatSync(target);return!stat.isSymbolicLink()&&stat.isFile()&&stat.uid===0&&stat.gid===0&&(modeOf3(stat)&18)===0&&(modeOf3(stat)&73)!==0}catch{return false}}function readSandboxIdentity(){const readId=flag=>{const result=(0,import_node_child_process.spawnSync)("/usr/bin/id",[flag,"sandbox"],{encoding:"utf8",env:{PATH:FIXED_PATH}});const value=result.stdout.trim();if(result.status!==0||!/^[1-9][0-9]*$/u.test(value)){fail5("could not resolve the sandbox account")}return value};return{uid:readId("-u"),gid:readId("-g")}}function managedStartupSandboxPrefix(){if(trustedExecutable("/usr/bin/setpriv")){const identity=readSandboxIdentity();return["/usr/bin/setpriv",`--reuid=${identity.uid}`,`--regid=${identity.gid}`,"--init-groups","--"]}return fail5("a trusted setpriv executable is required")}function commandEnvironment(configurationEnvironment,applicationRuntime){const env=applyManagedStartupCommandEnvironmentPlan({...process.env,...configurationEnvironment,HOME:"/sandbox",PATH:FIXED_PATH,NPM_CONFIG_OFFLINE:"true",npm_config_offline:"true",PIP_DISABLE_PIP_VERSION_CHECK:"1",PIP_NO_INDEX:"1",UV_OFFLINE:"1"},applicationRuntime);delete env[MANAGED_STARTUP_PROFILE_ENV];delete env[MANAGED_STARTUP_CA_ENV];return env}function execute(argv,runAs,configurationEnvironment,applicationRuntime,capture=false){if(argv.length===0)fail5("refusing an empty managed startup command");const command=runAs==="sandbox"?[...managedStartupSandboxPrefix(),...argv]:[...argv];const result=(0,import_node_child_process.spawnSync)(command[0],command.slice(1),{encoding:"utf8",env:commandEnvironment(configurationEnvironment,applicationRuntime),stdio:capture?"pipe":"inherit"});if(result.error){fail5(`could not execute ${argv[0]}: ${result.error.message}`)}if(result.status!==0){const detail=capture?`: ${(result.stderr||result.stdout).trim()}`:"";fail5(`${argv[0]} exited with status ${String(result.status??"unknown")}${detail}`)}return{status:result.status,stdout:result.stdout??"",stderr:result.stderr??""}}function generatorCommand(agent){switch(agent){case"openclaw":return["/usr/local/bin/node","--experimental-strip-types","/scripts/generate-openclaw-config.mts"];case"hermes":return["/usr/local/bin/node","--experimental-strip-types","/opt/nemoclaw-hermes-config/generate-config.ts"];case"langchain-deepagents-code":return["/usr/local/bin/node","--experimental-strip-types","/opt/nemoclaw-deepagents-code/generate-config.ts"];case"pi":return["/usr/local/bin/node","--experimental-strip-types","/opt/nemoclaw-pi/generate-config.ts"]}}function messagingCommand(agent,phase,mode){return["/usr/local/bin/node","--experimental-strip-types","/src/lib/messaging/applier/build/messaging-build-applier.mts","--agent",agent,"--phase",phase,"--mode",mode,...phase==="post-agent-install"?["--managed-startup-runtime"]:[]]}function assertActionAgent(inputAgent,actionAgent){if(inputAgent!==actionAgent){failActionPlan(`action for ${actionAgent} cannot be used by ${inputAgent}`)}}function buildManagedStartupImageActionPlan(input){const inputAgent=exactActionPlanAgent(input.agent);const commands=[];let dashboardActions=0;let generateActions=0;let runtimeMessagingActions=0;let postMessagingActions=0;for(const action of input.actions){switch(action.kind){case"configure-dashboard":{if(action.dashboard.agent!==input.agent){failActionPlan(`dashboard for ${action.dashboard.agent} cannot be used by ${input.agent}`)}dashboardActions+=1;break}case"generate-agent-config":{assertActionAgent(inputAgent,exactActionPlanAgent(action.agent));if(action.runAs!=="sandbox"){failActionPlan("agent configuration generation must run as sandbox")}generateActions+=1;commands.push({action:"generate-agent-config",runAs:action.runAs,argv:generatorCommand(action.agent)});break}case"apply-messaging-plan":{assertActionAgent(inputAgent,exactActionPlanAgent(action.agent));if(action.mode!=="apply"&&action.mode!=="clear"){failActionPlan("messaging intent must be apply or clear")}if(action.phase==="runtime-setup"){if(action.runAs!=="root"){failActionPlan("messaging runtime setup must run as root")}runtimeMessagingActions+=1;commands.push({action:"messaging-runtime-setup",runAs:action.runAs,argv:messagingCommand(action.agent,action.phase,action.mode)})}else if(action.phase==="post-agent-install"){if(action.runAs!=="sandbox"){failActionPlan("messaging post-agent configuration must run as sandbox")}postMessagingActions+=1;commands.push({action:"messaging-post-agent-install",runAs:action.runAs,argv:messagingCommand(action.agent,action.phase,action.mode)})}else{failActionPlan("unsupported messaging construction phase")}break}default:failActionPlan("unsupported managed startup construction action")}}if(dashboardActions!==1){failActionPlan("exactly one dashboard construction action is required")}if(generateActions!==1){failActionPlan("exactly one agent config construction action is required")}const supportsMessaging=MANAGED_STARTUP_MESSAGING_AGENTS.includes(inputAgent);const expectedMessagingActions=supportsMessaging?1:0;if(runtimeMessagingActions!==expectedMessagingActions||postMessagingActions!==expectedMessagingActions){failActionPlan(`${inputAgent} requires ${String(expectedMessagingActions)} action for each messaging phase`)}const expectedOrder=supportsMessaging?["messaging-runtime-setup","generate-agent-config","messaging-post-agent-install"]:["generate-agent-config"];if(commands.some((command,index)=>command.action!==expectedOrder[index])){failActionPlan(`${inputAgent} image actions are not in the required construction order`)}return Object.freeze(commands.map(command=>Object.freeze({...command,argv:Object.freeze([...command.argv])})))}function prepareMessagingRuntimeTarget(mode){if(mode==="clear"){removeSafeRootFile(MESSAGING_RUNTIME_PLAN_FILE);return}requireSafeExistingRootTarget(MESSAGING_RUNTIME_PLAN_FILE);try{import_node_fs3.default.unlinkSync(MESSAGING_RUNTIME_PLAN_FILE)}catch(error){if(error.code!=="ENOENT"){fail5("could not prepare the messaging runtime-plan target")}}}function verifyMessagingRuntimeTarget(mode){if(mode==="clear"){if(import_node_fs3.default.existsSync(MESSAGING_RUNTIME_PLAN_FILE)){fail5("clear messaging profile left a runtime-plan artifact")}return}const stat=import_node_fs3.default.lstatSync(MESSAGING_RUNTIME_PLAN_FILE);if(stat.isSymbolicLink()||!stat.isFile()||stat.nlink!==1||stat.uid!==0||stat.gid!==0||modeOf3(stat)!==420){fail5("messaging runtime-plan artifact failed root ownership validation")}}function runInternalSandboxAction(action,configurationEnvironment,applicationRuntime,extraEnvironment={}){execute(["/usr/local/bin/node",MANAGED_STARTUP_RUNTIME_EXECUTABLE,`--internal-${action}`],"sandbox",{...configurationEnvironment,...extraEnvironment},applicationRuntime)}function sealOpenClawConfiguration(configurationEnvironment,applicationRuntime){const validation=execute(["/usr/local/bin/openclaw","config","validate","--json"],"sandbox",{...configurationEnvironment,OPENCLAW_CONFIG_PATH:"/sandbox/.openclaw/openclaw.json"},applicationRuntime,true);let parsed;try{parsed=JSON.parse(validation.stdout)}catch{fail5("OpenClaw config validation did not emit JSON")}if(typeof parsed!=="object"||parsed===null||parsed.valid!==true){fail5("OpenClaw rejected the generated managed startup config")}runInternalSandboxAction("write-openclaw-hash",configurationEnvironment,applicationRuntime)}function sameStableFileMetadata(left,right){return left.dev===right.dev&&left.ino===right.ino&&left.mode===right.mode&&left.nlink===right.nlink&&left.uid===right.uid&&left.gid===right.gid&&left.size===right.size&&left.mtimeNs===right.mtimeNs&&left.ctimeNs===right.ctimeNs}function readStableRegularFileSnapshot(target,maxBytes){if(typeof import_node_fs3.default.constants.O_NOFOLLOW!=="number"){fail5("O_NOFOLLOW is unavailable for managed startup file reads")}const nonblock=typeof import_node_fs3.default.constants.O_NONBLOCK==="number"?import_node_fs3.default.constants.O_NONBLOCK:0;let descriptor;try{descriptor=import_node_fs3.default.openSync(target,import_node_fs3.default.constants.O_RDONLY|import_node_fs3.default.constants.O_NOFOLLOW|nonblock)}catch(error){if(error.code==="ENOENT")throw error;fail5(`refusing unsafe or unreadable file ${target}`)}try{const before=import_node_fs3.default.fstatSync(descriptor,{bigint:true});if(!before.isFile()||before.nlink!==1n||before.size<1n||before.size>BigInt(maxBytes)){fail5(`refusing unsafe or oversized file ${target}`)}const bytes=Buffer.alloc(Number(before.size));let offset=0;while(offset`${block.trim()} -`)}function managedSystemCaAnchorNames(){try{import_node_fs3.default.lstatSync(MANAGED_STARTUP_SYSTEM_CA_ANCHOR_DIRECTORY)}catch(error){if(error.code==="ENOENT")return[];fail5("could not inspect the managed system CA anchor directory")}requireRootOwnedDirectory(MANAGED_STARTUP_SYSTEM_CA_ANCHOR_DIRECTORY,ROOT_OWNED_DIRECTORY_MODE);try{return import_node_fs3.default.readdirSync(MANAGED_STARTUP_SYSTEM_CA_ANCHOR_DIRECTORY).filter(name=>MANAGED_STARTUP_SYSTEM_CA_ANCHOR_RE.test(name)).sort()}catch(error){fail5("could not inspect the managed system CA anchors")}}function refreshSystemCaBundle(){if(!trustedExecutable(UPDATE_CA_CERTIFICATES_EXECUTABLE)){fail5(`a trusted ${UPDATE_CA_CERTIFICATES_EXECUTABLE} executable is required`)}const result=(0,import_node_child_process.spawnSync)(UPDATE_CA_CERTIFICATES_EXECUTABLE,[],{encoding:"utf8",env:{PATH:FIXED_PATH},stdio:"inherit"});if(result.error){fail5(`could not execute ${UPDATE_CA_CERTIFICATES_EXECUTABLE}: ${result.error.message}`)}if(result.status!==0){fail5(`${UPDATE_CA_CERTIFICATES_EXECUTABLE} exited with status ${String(result.status??"unknown")}`)}}function requireSystemCaBundleContains(blocks){const systemBundle=safeTrustBundle(SYSTEM_CA_BUNDLE_FILE);if(systemBundle===null)fail5("the refreshed system CA bundle is missing");const systemBlocks=systemBundle.toString("utf8").match(PEM_CERTIFICATE_RE_GLOBAL)??[];const systemFingerprints=new Set;for(const block of systemBlocks){try{systemFingerprints.add(new import_node_crypto6.X509Certificate(block).fingerprint256)}catch{fail5("the refreshed system CA bundle contains an invalid certificate")}}for(const block of blocks){if(!systemFingerprints.has(new import_node_crypto6.X509Certificate(block).fingerprint256)){fail5("the refreshed system CA bundle does not contain the corporate CA")}}}function installCorporateCaSystemAnchors(corporateCaPath){const existingNames=managedSystemCaAnchorNames();if(corporateCaPath===null){for(const name of existingNames){removeSafeRootFile(import_node_path3.default.join(MANAGED_STARTUP_SYSTEM_CA_ANCHOR_DIRECTORY,name))}refreshSystemCaBundle();return}ensureRootOwnedDirectory(MANAGED_STARTUP_SYSTEM_CA_ANCHOR_DIRECTORY);const blocks=corporateCaCertificateBlocks(corporateCaPath);const expectedNames=blocks.map((_block,index)=>`nemoclaw-corporate-ca-${String(index+1).padStart(2,"0")}.crt`);for(const name of existingNames){if(!expectedNames.includes(name)){removeSafeRootFile(import_node_path3.default.join(MANAGED_STARTUP_SYSTEM_CA_ANCHOR_DIRECTORY,name))}}for(const[index,name]of expectedNames.entries()){atomicWriteRootFile(import_node_path3.default.join(MANAGED_STARTUP_SYSTEM_CA_ANCHOR_DIRECTORY,name),blocks[index],292)}refreshSystemCaBundle();requireSystemCaBundleContains(blocks)}function safeTrustBundle(target){try{const{bytes,stat}=readStableRegularFileSnapshot(target,MAX_TRUST_BUNDLE_BYTES);if(Number(stat.mode&0o022n)!==0){fail5(`refusing unsafe trust bundle ${target}`)}return bytes}catch(error){if(error.code==="ENOENT")return null;throw error}}function mergeCorporateCa(corporateCaPath){if(corporateCaPath===null){removeSafeRootFile(MANAGED_STARTUP_MERGED_CA_FILE);return false}const corporate=readStableRegularFile(corporateCaPath,128*1024);const candidates=["/etc/openshell-tls/ca-bundle.pem",process.env.SSL_CERT_FILE??"","/etc/ssl/certs/ca-certificates.crt"].filter((candidate,index,values)=>candidate&&candidate!==MANAGED_STARTUP_MERGED_CA_FILE&&values.indexOf(candidate)===index);let base=null;for(const candidate of candidates){base=safeTrustBundle(candidate);if(base)break}const merged=Buffer.concat([...base?[base,Buffer.from("\n","utf8")]:[],corporate,...corporate.at(-1)===10?[]:[Buffer.from("\n","utf8")]]);atomicWriteRootFile(MANAGED_STARTUP_MERGED_CA_FILE,merged,292);return true}function shellSingleQuote(value){if(value.includes("\0")||/[\r\n]/u.test(value)){fail5("runtime environment values must be single-line text")}return`'${value.replaceAll("'",`'"'"'`)}'`}function serializeManagedStartupRuntimeEnvironment(environment,corporateCaMerged,configurationEnvironment={},applicationRuntime={exportEnvironment:{},unsetEnvironment:[]}){const{output,unsetNames}=materializeManagedStartupRuntimeEnvironment(environment,corporateCaMerged,configurationEnvironment,applicationRuntime);const unsetLines=unsetNames.map(name=>`unset ${name}`);const exportLines=Object.entries(output).sort(([left],[right])=>left.localeCompare(right)).map(([name,value])=>`export ${name}=${shellSingleQuote(value)}`);return`${[...unsetLines,...exportLines].join("\n")} -`}function materializeManagedStartupRuntimeEnvironment(environment,corporateCaMerged,configurationEnvironment={},applicationRuntime={exportEnvironment:{},unsetEnvironment:[]}){const validatedApplicationRuntime=validateManagedStartupApplicationRuntimePlan(applicationRuntime);const output={...environment,...validatedApplicationRuntime.exportEnvironment,NEMOCLAW_MANAGED_STARTUP_APPLIED:"1"};if(corporateCaMerged){for(const name of MANAGED_STARTUP_TLS_ENV_NAMES){delete output[name]}output._NEMOCLAW_CORPORATE_CA_MERGED="1"}for(const name of[...Object.keys(configurationEnvironment),...Object.keys(output)]){if(!/^[A-Za-z_][A-Za-z0-9_]*$/u.test(name)){fail5(`invalid runtime environment key ${JSON.stringify(name)}`)}}const unsetNames=new Set([...Object.keys(configurationEnvironment).filter(name=>!Object.hasOwn(output,name)&&(!corporateCaMerged||!MANAGED_STARTUP_TLS_ENV_NAMES.has(name))),...validatedApplicationRuntime.unsetEnvironment.filter(name=>!corporateCaMerged||!MANAGED_STARTUP_TLS_ENV_NAMES.has(name))]);for(const name of validatedApplicationRuntime.unsetEnvironment){if(Object.hasOwn(output,name)){fail5(`runtime environment cannot both export and unset ${name}`)}}for(const name of unsetNames){if(!/^[A-Za-z_][A-Za-z0-9_]*$/u.test(name)){fail5(`invalid runtime environment key ${JSON.stringify(name)}`)}}return{output,unsetNames:[...unsetNames].sort()}}function serializeManagedStartupCompletionMarker(marker){if(marker.schemaVersion!==MANAGED_STARTUP_COMPLETION_SCHEMA_VERSION||!MANAGED_STARTUP_AGENTS.includes(marker.agent)||!SHA256_RE4.test(marker.profileFingerprint)||!SHA256_RE4.test(marker.runtimeEnvironmentSha256)||typeof marker.corporateCaMerged!=="boolean"){fail5("managed startup completion marker is invalid")}return`${JSON.stringify({agent:marker.agent,corporateCaMerged:marker.corporateCaMerged,profileFingerprint:marker.profileFingerprint,runtimeEnvironmentSha256:marker.runtimeEnvironmentSha256,schemaVersion:marker.schemaVersion})} -`}function parseManagedStartupCompletionMarker(text){let parsed;try{parsed=JSON.parse(text)}catch{fail5("managed startup completion marker is not valid JSON")}if(typeof parsed!=="object"||parsed===null||Array.isArray(parsed)){fail5("managed startup completion marker must be an object")}const record=parsed;const expectedKeys=["agent","corporateCaMerged","profileFingerprint","runtimeEnvironmentSha256","schemaVersion"];if(Object.keys(record).sort().join(",")!==expectedKeys.sort().join(",")||record.schemaVersion!==MANAGED_STARTUP_COMPLETION_SCHEMA_VERSION||typeof record.agent!=="string"||!MANAGED_STARTUP_AGENTS.includes(record.agent)||typeof record.profileFingerprint!=="string"||!SHA256_RE4.test(record.profileFingerprint)||typeof record.runtimeEnvironmentSha256!=="string"||!SHA256_RE4.test(record.runtimeEnvironmentSha256)||typeof record.corporateCaMerged!=="boolean"){fail5("managed startup completion marker has an invalid schema")}const marker={schemaVersion:MANAGED_STARTUP_COMPLETION_SCHEMA_VERSION,agent:record.agent,profileFingerprint:record.profileFingerprint,runtimeEnvironmentSha256:record.runtimeEnvironmentSha256,corporateCaMerged:record.corporateCaMerged};if(serializeManagedStartupCompletionMarker(marker)!==text){fail5("managed startup completion marker is not canonical")}return marker}function verifyManagedStartupImageCompletion(expectedAgentInput,expectedFingerprint,completionFile=MANAGED_STARTUP_COMPLETION_FILE,runtimeEnvironmentFile=MANAGED_STARTUP_RUNTIME_ENV_FILE){const expectedAgent=exactAgent2(expectedAgentInput);if(!SHA256_RE4.test(expectedFingerprint)){fail5("startup completion expected profile fingerprint is invalid")}const{bytes,stat}=readStableRegularFileSnapshot(completionFile,MAX_MANAGED_STARTUP_COMPLETION_BYTES);if(stat.nlink!==1n||stat.uid!==0n||stat.gid!==0n||Number(stat.mode&0o777n)!==292){fail5("managed startup completion marker must be root:root mode 0444")}const marker=parseManagedStartupCompletionMarker(bytes.toString("utf8"));if(marker.agent!==expectedAgent||marker.profileFingerprint!==expectedFingerprint){fail5("managed startup completion marker does not match the requested profile")}const runtimeEnvironment=readStableRegularFileSnapshot(runtimeEnvironmentFile,MAX_MANAGED_STARTUP_RUNTIME_ENVIRONMENT_BYTES);if(runtimeEnvironment.stat.nlink!==1n||runtimeEnvironment.stat.uid!==0n||runtimeEnvironment.stat.gid!==0n||Number(runtimeEnvironment.stat.mode&0o777n)!==292){fail5("managed startup runtime environment must be root:root mode 0444")}const runtimeEnvironmentSha256=(0,import_node_crypto6.createHash)("sha256").update(runtimeEnvironment.bytes).digest("hex");if(runtimeEnvironmentSha256!==marker.runtimeEnvironmentSha256){fail5("managed startup completion marker runtime environment digest mismatch")}return{agent:expectedAgent,fingerprint:expectedFingerprint}}function waitForManagedStartupImageCompletion(expectedAgentInput,expectedFingerprint,timeoutSeconds=600){if(!Number.isSafeInteger(timeoutSeconds)||timeoutSeconds<1||timeoutSeconds>3600){fail5("startup completion wait timeout must be an integer from 1 to 3600 seconds")}const deadline=Date.now()+timeoutSeconds*1e3;while(true){try{return verifyManagedStartupImageCompletion(expectedAgentInput,expectedFingerprint)}catch(error){if(error.code!=="ENOENT")throw error;if(Date.now()>=deadline){fail5(`startup completion was not published within ${String(timeoutSeconds)} seconds`)}Atomics.wait(new Int32Array(new SharedArrayBuffer(4)),0,0,250)}}}function applyAdapter(context,mapped){if(mapped.agent!==context.agent){fail5(`mapped ${mapped.agent} environment for ${context.agent}`)}const commandPlan=buildManagedStartupImageActionPlan({agent:mapped.agent,actions:mapped.actions});let commandIndex=0;for(const action of mapped.actions){if(action.kind==="configure-dashboard")continue;const command=commandPlan[commandIndex];if(!command)fail5(`missing image command for ${action.kind}`);commandIndex+=1;if(action.kind==="apply-messaging-plan"){if(action.phase==="runtime-setup"){prepareMessagingRuntimeTarget(action.mode)}execute(command.argv,command.runAs,mapped.configurationEnvironment,mapped.applicationRuntime);if(action.phase==="runtime-setup"){verifyMessagingRuntimeTarget(action.mode)}continue}execute(command.argv,command.runAs,mapped.configurationEnvironment,mapped.applicationRuntime)}if(commandIndex!==commandPlan.length){fail5("image action plan contains an unmatched command")}switch(context.agent){case"openclaw":sealOpenClawConfiguration(mapped.configurationEnvironment,mapped.applicationRuntime);break;case"hermes":installHermesManagedPolicy();sealHermesConfiguration(mapped.configurationEnvironment,mapped.applicationRuntime);normalizeHermesManagedConfiguration();break;case"langchain-deepagents-code":break}installRootOwnedMaterials(mapped.materials);installCorporateCa(context.corporateCaPath);installCorporateCaSystemAnchors(context.corporateCaPath);mergeCorporateCa(context.corporateCaPath)}function adapters(mapped){return MANAGED_STARTUP_AGENTS.map(agent=>({agent,apply:context=>applyAdapter(context,mapped)}))}async function applyManagedStartupImageProfile(expectedAgentInput,env=process.env){requireRoot();const expectedAgent=exactAgent2(expectedAgentInput);if(env.NEMOCLAW_MANAGED_IMAGE_CAPABILITY_UNION!=="1"){fail5("startup profiles require a complete managed image")}const encodedProfile=env[MANAGED_STARTUP_PROFILE_ENV];if(!encodedProfile)fail5(`${MANAGED_STARTUP_PROFILE_ENV} is required`);let profile;try{profile=decodeManagedStartupProfile(encodedProfile)}catch(error){fail5(error.message)}if(profile.agent!==expectedAgent){fail5(`managed startup profile targets ${profile.agent}, expected ${expectedAgent}`)}const mapped=mapManagedStartupProfileToAgentEnvironment(profile,env);validateManagedStartupApplicationRuntimePlan(mapped.applicationRuntime);ensureRootOwnedDirectory(ROOT_STATE_PARENT);ensureRootOwnedDirectory(ROOT_RUNTIME_DIRECTORY);const result=await coordinateManagedStartupApplication({encodedProfile,expectedAgent,...env[MANAGED_STARTUP_CA_ENV]===void 0?{}:{corporateCaB64:env[MANAGED_STARTUP_CA_ENV]}},adapters(mapped));if(mapped.agent!==result.application.profile.agent){fail5(`mapped ${mapped.agent} environment for ${result.application.profile.agent}`)}if(expectedAgent==="hermes"&&!result.adapterApplied){normalizeHermesManagedConfiguration()}let corporateCaMerged;if(result.adapterApplied){corporateCaMerged=result.application.corporateCaPath!==null}else{verifyRootOwnedMaterials(mapped.materials);if(result.application.corporateCaPath===null){if(import_node_fs3.default.existsSync(MANAGED_STARTUP_CORPORATE_CA_FILE)){fail5("committed profile without a corporate CA has a stale CA material")}}else{const expected=readStableRegularFile(result.application.corporateCaPath,128*1024);const installed=readStableRegularFile(MANAGED_STARTUP_CORPORATE_CA_FILE,128*1024);if(!expected.equals(installed)){fail5("committed corporate CA material drifted")}}installCorporateCaSystemAnchors(result.application.corporateCaPath);corporateCaMerged=mergeCorporateCa(result.application.corporateCaPath)}const runtimeEnvironment=serializeManagedStartupRuntimeEnvironment(mapped.runtimeEnvironment,corporateCaMerged,mapped.configurationEnvironment,mapped.applicationRuntime);atomicWriteRootFile(MANAGED_STARTUP_RUNTIME_ENV_FILE,runtimeEnvironment,292);atomicWriteRootFile(MANAGED_STARTUP_COMPLETION_FILE,serializeManagedStartupCompletionMarker({schemaVersion:MANAGED_STARTUP_COMPLETION_SCHEMA_VERSION,agent:expectedAgent,profileFingerprint:result.application.fingerprint,runtimeEnvironmentSha256:(0,import_node_crypto6.createHash)("sha256").update(runtimeEnvironment,"utf8").digest("hex"),corporateCaMerged}),292);return{agent:expectedAgent,adapterApplied:result.adapterApplied,fingerprint:result.application.fingerprint,runtimeEnvironmentFile:MANAGED_STARTUP_RUNTIME_ENV_FILE}}function completionAlreadyPublished(request){try{verifyManagedStartupImageCompletion(request.agent,request.profileFingerprint);return true}catch(error){if(error.code==="ENOENT")return false;throw error}}async function applyManagedStartupRootRequest(request,env=process.env,options={}){requireRoot();const profile=decodeManagedStartupProfile(request.encodedProfile);if(profile.agent!==request.agent||fingerprintManagedStartupProfile(profile)!==request.profileFingerprint){fail5("root application request identity does not match its profile")}const imageEnvironment={HOME:"/root",PATH:FIXED_PATH,NEMOCLAW_MANAGED_IMAGE_CAPABILITY_UNION:"1",...selectManagedStartupApplicationRuntimeEnvironment(env),[MANAGED_STARTUP_PROFILE_ENV]:request.encodedProfile,...request.corporateCaB64===null?{}:{[MANAGED_STARTUP_CA_ENV]:request.corporateCaB64}};mapManagedStartupProfileToAgentEnvironment(profile,imageEnvironment);const alreadyPublished=completionAlreadyPublished(request);const bootstrapIdentity=options.bootstrapIdentity??null;const transactionStatus=alreadyPublished&&bootstrapIdentity!==null?getManagedStartupSharedStateTransactionStatus({agent:request.agent,profileFingerprint:request.profileFingerprint,bootstrapIdentity}):null;if(transactionStatus==="none"){fail5("completed startup profile has no shared-state authority for this bootstrap attempt")}if(!alreadyPublished){ensureRootOwnedDirectory(ROOT_STATE_PARENT);beginManagedStartupSharedStateTransaction(profile,{bootstrapIdentity})}const result=await applyManagedStartupImageProfile(request.agent,imageEnvironment);return{...result,transactionPending:!alreadyPublished||transactionStatus==="pending"}}function readBoundedRootApplyStdin(){const chunks=[];let total=0;while(true){const chunk=Buffer.alloc(16*1024);const read=import_node_fs3.default.readSync(0,chunk,0,chunk.length,null);if(read===0)break;total+=read;if(total>MANAGED_STARTUP_ROOT_APPLY_MAX_BYTES){fail5("root application stdin exceeds its bounded transport")}chunks.push(chunk.subarray(0,read))}const bytes=Buffer.concat(chunks,total);const text=bytes.toString("utf8");if(text.includes("\0")||!Buffer.from(text,"utf8").equals(bytes)){fail5("root application stdin must be valid UTF-8 without NUL bytes")}return text}function writeSandboxFileAtomically(target,contents,mode){const parent=import_node_path3.default.dirname(target);const parentStat=import_node_fs3.default.lstatSync(parent);if(parentStat.isSymbolicLink()||!parentStat.isDirectory()||parentStat.uid!==process.geteuid?.()||parentStat.gid!==process.getegid?.()){fail5(`refusing unsafe sandbox-owned directory ${parent}`)}const temporary=import_node_path3.default.join(parent,`.${import_node_path3.default.basename(target)}.${(0,import_node_crypto6.randomBytes)(12).toString("hex")}`);let descriptor;try{descriptor=import_node_fs3.default.openSync(temporary,import_node_fs3.default.constants.O_CREAT|import_node_fs3.default.constants.O_EXCL|import_node_fs3.default.constants.O_WRONLY|import_node_fs3.default.constants.O_NOFOLLOW,384);import_node_fs3.default.writeFileSync(descriptor,contents);import_node_fs3.default.fchmodSync(descriptor,mode);import_node_fs3.default.fsyncSync(descriptor);import_node_fs3.default.closeSync(descriptor);descriptor=void 0;import_node_fs3.default.renameSync(temporary,target)}catch(error){if(descriptor!==void 0)import_node_fs3.default.closeSync(descriptor);try{import_node_fs3.default.unlinkSync(temporary)}catch{}fail5(`could not write sandbox-owned file ${target}: ${error.message}`)}}function internalWriteOpenClawHash(){if(process.geteuid?.()===0)fail5("sandbox hash writer must not run as root");const configPath="/sandbox/.openclaw/openclaw.json";const config=readStableRegularFile(configPath,16*1024*1024);const text=`${(0,import_node_crypto6.createHash)("sha256").update(config).digest("hex")} openclaw.json -`;writeSandboxFileAtomically("/sandbox/.openclaw/.config-hash",text,432)}function internalWriteHermesCompatHash(){if(process.geteuid?.()===0)fail5("sandbox hash writer must not run as root");const encoded=process.env.NEMOCLAW_MANAGED_HERMES_HASH_B64??"";if(encoded.length===0||encoded.length>4096||!/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/u.test(encoded)){fail5("Hermes compatibility hash transport is invalid")}const decoded=Buffer.from(encoded,"base64");if(decoded.toString("base64")!==encoded){fail5("Hermes compatibility hash transport is non-canonical")}writeSandboxFileAtomically("/sandbox/.hermes/.config-hash",decoded.toString("utf8"),416)}function readCliAgent(argv,expectedLength=2){const index=argv.indexOf("--agent");if(index<0||index+1>=argv.length||argv.length!==expectedLength){fail5("usage: managed-startup-image-runtime [--apply-root-stdin|--wait-for-completion|--verify-completion|--begin-shared-state-transaction|--commit-shared-state-transaction|--clear-shared-state-commit-receipt|--shared-state-transaction-status] --agent ")}return argv[index+1]}function readCliFingerprint(argv){const index=argv.indexOf("--profile-fingerprint");if(index<0||index+1>=argv.length){fail5("managed startup profile fingerprint argument is missing")}return argv[index+1]}function readCliBootstrapIdentity(argv){const index=argv.indexOf("--bootstrap-identity");if(index<0||index+1>=argv.length||!SHA256_RE4.test(String(argv[index+1]??""))){fail5("managed bootstrap identity argument is missing or invalid")}return argv[index+1]}async function main(argv=process.argv.slice(2)){if(argv.length===1&&argv[0]==="--internal-write-openclaw-hash"){internalWriteOpenClawHash();return}if(argv.length===1&&argv[0]==="--internal-write-hermes-compat-hash"){internalWriteHermesCompatHash();return}if(argv.length===3&&argv[0]==="--apply-root-stdin"){const expectedAgent=exactAgent2(readCliAgent(argv,3));const request=parseManagedStartupRootApplyRequest(readBoundedRootApplyStdin());if(request.agent!==expectedAgent){fail5(`root application request targets ${request.agent}, expected ${expectedAgent}`)}const result2=await applyManagedStartupRootRequest(request);console.log(result2.transactionPending?`[managed-startup] applied ${result2.agent} profile ${result2.fingerprint}; transaction pending`:`[managed-startup] ${result2.agent} profile ${result2.fingerprint} was already complete`);return}if(argv.length===5&&(argv[0]==="--verify-completion"||argv[0]==="--wait-for-completion")){const agent=readCliAgent(argv,5);const fingerprint=readCliFingerprint(argv);const result2=argv[0]==="--wait-for-completion"?waitForManagedStartupImageCompletion(agent,fingerprint):verifyManagedStartupImageCompletion(agent,fingerprint);console.log(`[managed-startup] verified ${result2.agent} profile ${result2.fingerprint} completion`);return}if(argv.length===3&&argv[0]==="--begin-shared-state-transaction"){const profile=managedTransactionProfile(readCliAgent(argv,3));ensureRootOwnedDirectory(ROOT_STATE_PARENT);const created=beginManagedStartupSharedStateTransaction(profile);process.stdout.write(created?"created\n":"pending\n");return}if((argv.length===4||argv.length===6)&&argv[0]==="--rollback-shared-state-transaction"&&argv[argv.length-1]==="--read-only-receipt"){requireRoot();const agent=exactAgent2(readCliAgent(argv,argv.length));const rolledBack=rollbackManagedStartupSharedStateTransaction(agent,{transactionDirectory:MANAGED_STARTUP_SHARED_ROLLBACK_RECEIPT_DIRECTORY,readOnlyReceipt:true,bootstrapIdentity:argv.length===6?readCliBootstrapIdentity(argv):null});if(!rolledBack)fail5("read-only shared-state rollback receipt is missing");console.log(`[managed-startup] verified and restored ${agent} shared state`);return}if((argv.length===3||argv.length===5)&&argv[0]==="--commit-shared-state-transaction"){requireRoot();const agent=exactAgent2(readCliAgent(argv,argv.length));if(!commitManagedStartupSharedStateTransaction(agent,{bootstrapIdentity:argv.length===5?readCliBootstrapIdentity(argv):null})){fail5("managed startup transaction is missing at commit")}console.log(`[managed-startup] committed ${agent} shared state`);return}if(argv.length===5&&argv[0]==="--clear-shared-state-commit-receipt"){requireRoot();const agent=exactAgent2(readCliAgent(argv,5));const bootstrapIdentity=readCliBootstrapIdentity(argv);if(!clearManagedStartupSharedStateCommitReceipt(agent,{bootstrapIdentity})){fail5("managed startup durable commit receipt is missing at cleanup")}console.log(`[managed-startup] cleared ${agent} durable shared-state commit receipt`);return}if(argv.length===8&&argv[0]==="--shared-state-transaction-status"&&argv[7]==="--read-only-receipt"){requireRoot();const agent=exactAgent2(readCliAgent(argv,8));const profileFingerprint=readCliFingerprint(argv);const bootstrapIdentity=readCliBootstrapIdentity(argv);process.stdout.write(`${getManagedStartupSharedStateTransactionStatus({agent,profileFingerprint,bootstrapIdentity},{readOnlyReceipt:true})} -`);return}const result=await applyManagedStartupImageProfile(readCliAgent(argv));console.log(result.adapterApplied?`[managed-startup] applied ${result.agent} profile ${result.fingerprint}`:`[managed-startup] ${result.agent} profile ${result.fingerprint} is already committed`)}var MANAGED_BOOTSTRAP_ENVELOPE_SCHEMA_VERSION=1;var MANAGED_BOOTSTRAP_REQUEST_FILE="/var/lib/nemoclaw-managed-bootstrap-request.json";var MANAGED_BOOTSTRAP_REQUEST_TAR_PATH=MANAGED_BOOTSTRAP_REQUEST_FILE.replace(/^\/+/,"");var MANAGED_BOOTSTRAP_COMPLETION_FILE="/run/nemoclaw/managed-bootstrap-completion.json";var MANAGED_BOOTSTRAP_ENVELOPE_MAX_BYTES=Math.ceil(MANAGED_STARTUP_ROOT_APPLY_MAX_BYTES/3)*4+1024;var MANAGED_BOOTSTRAP_COMPLETION_MAX_BYTES=1024;var BOOTSTRAP_IDENTITY_RE=/^[a-f0-9]{64}$/u;var STANDARD_BASE64_RE2=/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/u;function fail6(message){throw new Error(`Managed bootstrap envelope is invalid: ${message}`)}function serializeManagedBootstrapEnvelope(input){if(!BOOTSTRAP_IDENTITY_RE.test(input.bootstrapIdentity)){fail6("bootstrap identity must be 32 random bytes encoded as lowercase hex")}const request=Buffer.from(serializeManagedStartupRootApplyRequest(input.rootApplyRequest),"utf8").toString("base64");const serialized=`${JSON.stringify({bootstrapIdentity:input.bootstrapIdentity,rootApplyRequestB64:request,schemaVersion:MANAGED_BOOTSTRAP_ENVELOPE_SCHEMA_VERSION})} -`;if(Buffer.byteLength(serialized,"utf8")>MANAGED_BOOTSTRAP_ENVELOPE_MAX_BYTES){fail6("serialized envelope exceeds its bounded transport")}return serialized}function parseManagedBootstrapEnvelope(text){if(text.includes("\0"))fail6("serialized envelope contains NUL");if(text.length===0||Buffer.byteLength(text,"utf8")>MANAGED_BOOTSTRAP_ENVELOPE_MAX_BYTES){fail6("serialized envelope is empty or too large")}let parsed;try{parsed=JSON.parse(text)}catch{fail6("serialized envelope is not valid JSON")}if(typeof parsed!=="object"||parsed===null||Array.isArray(parsed)){fail6("serialized envelope must be an object")}const record=parsed;if(Object.keys(record).sort().join(",")!==["bootstrapIdentity","rootApplyRequestB64","schemaVersion"].sort().join(",")||record.schemaVersion!==MANAGED_BOOTSTRAP_ENVELOPE_SCHEMA_VERSION||typeof record.bootstrapIdentity!=="string"||!BOOTSTRAP_IDENTITY_RE.test(record.bootstrapIdentity)||typeof record.rootApplyRequestB64!=="string"||!STANDARD_BASE64_RE2.test(record.rootApplyRequestB64)){fail6("serialized envelope has an invalid schema")}const requestBytes=Buffer.from(record.rootApplyRequestB64,"base64");if(requestBytes.toString("base64")!==record.rootApplyRequestB64){fail6("root application request transport is non-canonical")}const rootApplyRequest=parseManagedStartupRootApplyRequest(requestBytes.toString("utf8"));const envelope=Object.freeze({schemaVersion:MANAGED_BOOTSTRAP_ENVELOPE_SCHEMA_VERSION,bootstrapIdentity:record.bootstrapIdentity,rootApplyRequest});if(serializeManagedBootstrapEnvelope(envelope)!==text){fail6("serialized envelope is not canonical")}return envelope}function serializeManagedBootstrapImageCompletion(completion){if(!BOOTSTRAP_IDENTITY_RE.test(completion.bootstrapIdentity)||!BOOTSTRAP_IDENTITY_RE.test(completion.profileFingerprint)){fail6("image completion identity is invalid")}if(!["openclaw","hermes","langchain-deepagents-code"].includes(completion.agent)){fail6("image completion agent is invalid")}if(typeof completion.transactionPending!=="boolean"){fail6("image completion transaction state is invalid")}return`${JSON.stringify({agent:completion.agent,bootstrapIdentity:completion.bootstrapIdentity,profileFingerprint:completion.profileFingerprint,schemaVersion:MANAGED_BOOTSTRAP_ENVELOPE_SCHEMA_VERSION,transactionPending:completion.transactionPending})} -`}function parseManagedBootstrapImageCompletion(text){if(text.includes("\0"))fail6("image completion contains NUL");if(text.length===0||Buffer.byteLength(text,"utf8")>MANAGED_BOOTSTRAP_COMPLETION_MAX_BYTES){fail6("image completion is empty or too large")}let parsed;try{parsed=JSON.parse(text)}catch{fail6("image completion is not valid JSON")}if(typeof parsed!=="object"||parsed===null||Array.isArray(parsed)){fail6("image completion must be an object")}const completion=parsed;if(Object.keys(completion).sort().join(",")!==["agent","bootstrapIdentity","profileFingerprint","schemaVersion","transactionPending"].sort().join(",")||completion.schemaVersion!==MANAGED_BOOTSTRAP_ENVELOPE_SCHEMA_VERSION||typeof completion.agent!=="string"||!["openclaw","hermes","langchain-deepagents-code"].includes(completion.agent)||typeof completion.bootstrapIdentity!=="string"||!BOOTSTRAP_IDENTITY_RE.test(completion.bootstrapIdentity)||typeof completion.profileFingerprint!=="string"||!BOOTSTRAP_IDENTITY_RE.test(completion.profileFingerprint)||typeof completion.transactionPending!=="boolean"){fail6("image completion schema is invalid")}const normalized=Object.freeze({schemaVersion:MANAGED_BOOTSTRAP_ENVELOPE_SCHEMA_VERSION,bootstrapIdentity:completion.bootstrapIdentity,agent:completion.agent,profileFingerprint:completion.profileFingerprint,transactionPending:completion.transactionPending});if(serializeManagedBootstrapImageCompletion(normalized)!==text){fail6("image completion is not canonical")}return normalized}var SHA256_RE5=/^[a-f0-9]{64}$/u;function fail7(message){throw new ManagedStartupImageRuntimeError(message)}function requireRoot2(){if(process.geteuid?.()!==0){fail7("managed bootstrap image runtime requires container effective uid 0")}}function exactAgent3(value){if(!MANAGED_STARTUP_AGENTS.includes(value)){fail7(`unsupported agent ${JSON.stringify(value)}`)}return value}function readExpected(argv){if(argv.length!==7){fail7("usage: managed-startup-image-runtime [--recover-bootstrap-claim|--apply-bootstrap-file|--verify-bootstrap-completion|--wait-for-completion] --agent --profile-fingerprint --bootstrap-identity ")}const valueAfter=flag=>{const index=argv.indexOf(flag);if(index<0||index+1>=argv.length)fail7(`managed bootstrap ${flag} argument is missing`);return argv[index+1]};const profileFingerprint=valueAfter("--profile-fingerprint");const bootstrapIdentity=valueAfter("--bootstrap-identity");if(!SHA256_RE5.test(profileFingerprint)||!SHA256_RE5.test(bootstrapIdentity)){fail7("managed bootstrap image runtime identities must encode 32 lowercase-hex bytes")}return{agent:exactAgent3(valueAfter("--agent")),profileFingerprint,bootstrapIdentity}}function readProtectedManagedBootstrapEnvelopeSnapshot(requestFile=MANAGED_BOOTSTRAP_REQUEST_FILE){requireRoot2();const{bytes,stat}=readStableRegularFileSnapshot(requestFile,MANAGED_BOOTSTRAP_ENVELOPE_MAX_BYTES);if(!isProtectedManagedBootstrapFile(stat)){fail7("managed bootstrap envelope must be root:root mode 0400 with one link")}const envelope=parseManagedBootstrapEnvelope(bytes.toString("utf8"));return{bootstrapIdentity:envelope.bootstrapIdentity,bytes,request:envelope.rootApplyRequest,stat}}function managedBootstrapEnvelopeMatchesExpected(snapshot,expected){return snapshot.bootstrapIdentity===expected.bootstrapIdentity&&snapshot.request.agent===expected.agent&&snapshot.request.profileFingerprint===expected.profileFingerprint}function readManagedBootstrapEnvelopeSnapshot(expected,requestFile=MANAGED_BOOTSTRAP_REQUEST_FILE){const snapshot=readProtectedManagedBootstrapEnvelopeSnapshot(requestFile);if(!managedBootstrapEnvelopeMatchesExpected(snapshot,expected)){fail7("managed bootstrap envelope identity does not match the replacement")}return snapshot}function readManagedBootstrapEnvelope(expected,requestFile=MANAGED_BOOTSTRAP_REQUEST_FILE){return readManagedBootstrapEnvelopeSnapshot(expected,requestFile).request}function managedBootstrapEnvelopeClaimPaths(requestFile=MANAGED_BOOTSTRAP_REQUEST_FILE){if(!import_node_path4.default.isAbsolute(requestFile))fail7("managed bootstrap request path must be absolute");const directory=import_node_path4.default.join(import_node_path4.default.dirname(requestFile),`.${import_node_path4.default.basename(requestFile)}.nemoclaw-claim`);return{directory,file:import_node_path4.default.join(directory,"request"),requestFile}}function lstatManagedBootstrapPath(target){try{return import_node_fs4.default.lstatSync(target,{bigint:true})}catch(error){if(error.code==="ENOENT")return null;fail7(`could not inspect managed bootstrap path ${target}`)}}function sameStableManagedBootstrapFile(left,right){return sameClaimedManagedBootstrapFile(left,right)&&left.ctimeNs===right.ctimeNs}function sameClaimedManagedBootstrapFile(left,right){return left.dev===right.dev&&left.ino===right.ino&&left.mode===right.mode&&left.nlink===right.nlink&&left.uid===right.uid&&left.gid===right.gid&&left.size===right.size&&left.mtimeNs===right.mtimeNs}function isProtectedManagedBootstrapFile(stat,expectedLinks=1n){return stat.isFile()&&stat.nlink===expectedLinks&&stat.uid===0n&&stat.gid===0n&&Number(stat.mode&0o777n)===256}function requirePrivateManagedBootstrapClaimDirectory(directory){const stat=lstatManagedBootstrapPath(directory);if(stat===null||!stat.isDirectory()||stat.isSymbolicLink()||stat.uid!==0n||stat.gid!==0n||Number(stat.mode&0o777n)!==448){fail7("managed bootstrap claim directory must be root:root mode 0700")}}function removeManagedBootstrapClaimDirectory(directory){try{import_node_fs4.default.rmdirSync(directory)}catch{fail7("could not remove managed bootstrap claim directory")}}function requireSafeManagedBootstrapClaimParent(directory){const parent=lstatManagedBootstrapPath(import_node_path4.default.dirname(directory));if(parent===null||!parent.isDirectory()||parent.isSymbolicLink()||parent.uid!==0n||parent.gid!==0n||Number(parent.mode&0o022n)!==0){fail7("managed bootstrap claim parent must be a protected root-owned directory")}}function managedBootstrapClaimEntries(directory){let entries;try{entries=import_node_fs4.default.readdirSync(directory)}catch{fail7("could not inspect managed bootstrap claim directory contents")}return entries.sort()}function ensurePrivateManagedBootstrapClaimDirectory(directory){requireSafeManagedBootstrapClaimParent(directory);const current=lstatManagedBootstrapPath(directory);if(current!==null){requirePrivateManagedBootstrapClaimDirectory(directory);return}try{import_node_fs4.default.mkdirSync(directory,{mode:448});import_node_fs4.default.chownSync(directory,0,0);import_node_fs4.default.chmodSync(directory,448)}catch(error){if(error.code!=="EEXIST"){fail7("could not create private managed bootstrap envelope claim")}}requirePrivateManagedBootstrapClaimDirectory(directory)}function openManagedBootstrapEnvelopeSnapshot(expected,target){requireRoot2();if(typeof import_node_fs4.default.constants.O_NOFOLLOW!=="number"){fail7("O_NOFOLLOW is unavailable for managed bootstrap envelope reads")}const nonblock=typeof import_node_fs4.default.constants.O_NONBLOCK==="number"?import_node_fs4.default.constants.O_NONBLOCK:0;let descriptor;try{descriptor=import_node_fs4.default.openSync(target,import_node_fs4.default.constants.O_RDONLY|import_node_fs4.default.constants.O_NOFOLLOW|nonblock)}catch{fail7(`refusing unsafe or unreadable managed bootstrap envelope ${target}`)}try{const before=import_node_fs4.default.fstatSync(descriptor,{bigint:true});if(!isProtectedManagedBootstrapFile(before)||before.size<1n||before.size>BigInt(MANAGED_BOOTSTRAP_ENVELOPE_MAX_BYTES)){fail7("managed bootstrap envelope must be a bounded root:root mode 0400 file with one link")}const bytes=Buffer.alloc(Number(before.size));let offset=0;while(offset3600){fail7("managed bootstrap completion wait timeout must be an integer from 1 to 3600 seconds")}const deadline=Date.now()+timeoutSeconds*1e3;while(true){try{return verifyManagedBootstrapImageCompletion(expected,completionFile,startupCompletionFile,runtimeEnvironmentFile)}catch(error){if(error.code!=="ENOENT")throw error;if(Date.now()>=deadline){fail7(`managed bootstrap completion was not published within ${String(timeoutSeconds)} seconds`)}Atomics.wait(new Int32Array(new SharedArrayBuffer(4)),0,0,250)}}}async function main2(argv=process.argv.slice(2)){if(argv[0]==="--recover-bootstrap-claim"){const expected=readExpected(argv);const pending=recoverManagedBootstrapEnvelopeClaim(expected);console.log(pending?`[managed-startup] found pending ${expected.agent} bootstrap request claim`:`[managed-startup] no pending ${expected.agent} bootstrap request claim`);return}if(argv[0]==="--apply-bootstrap-file"){const expected=readExpected(argv);const result=await applyManagedBootstrapEnvelope(expected);console.log(result.transactionPending?`[managed-startup] applied ${result.agent} profile ${result.fingerprint}; transaction pending`:`[managed-startup] ${result.agent} profile ${result.fingerprint} was already complete`);return}if(argv[0]==="--verify-bootstrap-completion"){const expected=readExpected(argv);const completion=verifyManagedBootstrapImageCompletion(expected);console.log(`[managed-startup] verified ${expected.agent} profile ${expected.profileFingerprint} bootstrap ${expected.bootstrapIdentity}${completion.transactionPending?"; transaction pending":""}`);return}if(argv[0]==="--wait-for-completion"){const expected=readExpected(argv);const completion=waitForManagedBootstrapImageCompletion(expected);console.log(`[managed-startup] verified ${expected.agent} profile ${expected.profileFingerprint} bootstrap ${expected.bootstrapIdentity}${completion.transactionPending?"; transaction pending":""}`);return}await main(argv)}if(typeof require!=="undefined"&&typeof module!=="undefined"&&require.main===module){main2().catch(error=>{console.error(error instanceof Error?error.message:String(error));process.exitCode=1})}0&&(module.exports={applyManagedBootstrapEnvelope,main,managedBootstrapEnvelopeClaimPaths,readManagedBootstrapEnvelope,recoverManagedBootstrapEnvelopeClaim,verifyManagedBootstrapImageCompletion,waitForManagedBootstrapImageCompletion}); +`}function requireExactKeys(record2,keys){if(Object.keys(record2).sort().join(",")!==[...keys].sort().join(",")){fail5("transaction manifest contains unexpected fields")}}function parseCommitReceipt(text){let parsed;try{parsed=JSON.parse(text)}catch{fail5("commit receipt is not valid JSON")}if(typeof parsed!=="object"||parsed===null||Array.isArray(parsed)){fail5("commit receipt must be an object")}const record2=parsed;requireExactKeys(record2,["agent","bootstrapIdentity","profileFingerprint","schemaVersion"]);if(record2.schemaVersion!==TRANSACTION_SCHEMA_VERSION||!MANAGED_STARTUP_AGENTS.includes(String(record2.agent))||typeof record2.profileFingerprint!=="string"||!/^[a-f0-9]{64}$/u.test(record2.profileFingerprint)||typeof record2.bootstrapIdentity!=="string"||!/^[a-f0-9]{64}$/u.test(record2.bootstrapIdentity)){fail5("commit receipt has an invalid envelope")}const receipt={schemaVersion:TRANSACTION_SCHEMA_VERSION,agent:record2.agent,profileFingerprint:record2.profileFingerprint,bootstrapIdentity:record2.bootstrapIdentity};if(canonicalCommitReceipt(receipt)!==text){fail5("commit receipt is not canonical")}return receipt}function safeMetadata(value){return Number.isSafeInteger(value)&&value>=0}function parseManifest(text){let parsed;try{parsed=JSON.parse(text)}catch{fail5("transaction manifest is not valid JSON")}if(typeof parsed!=="object"||parsed===null||Array.isArray(parsed)){fail5("transaction manifest must be an object")}const record2=parsed;const hasBootstrapIdentity=Object.hasOwn(record2,"bootstrapIdentity");requireExactKeys(record2,hasBootstrapIdentity?["agent","bootstrapIdentity","directories","files","profileFingerprint","schemaVersion"]:["agent","directories","files","profileFingerprint","schemaVersion"]);const bootstrapIdentity=hasBootstrapIdentity?record2.bootstrapIdentity:null;if(record2.schemaVersion!==TRANSACTION_SCHEMA_VERSION||!MANAGED_STARTUP_AGENTS.includes(String(record2.agent))||typeof record2.profileFingerprint!=="string"||!/^[a-f0-9]{64}$/u.test(record2.profileFingerprint)||!(bootstrapIdentity===null||typeof bootstrapIdentity==="string"&&/^[a-f0-9]{64}$/u.test(bootstrapIdentity))||!Array.isArray(record2.files)||!Array.isArray(record2.directories)||record2.files.length>MAX_TRANSACTION_FILES||record2.directories.length>MAX_TRANSACTION_FILES*4){fail5("transaction manifest has an invalid envelope")}const files=record2.files.map(value=>{if(typeof value!=="object"||value===null||Array.isArray(value)){return fail5("transaction file receipt must be an object")}const receipt=value;if(typeof receipt.path!=="string"){return fail5("transaction file receipt path must be a string")}const receiptPath=safeRelativePath(receipt.path);if(receipt.state==="absent"){requireExactKeys(receipt,["path","state"]);return{path:receiptPath,state:"absent"}}requireExactKeys(receipt,["backup","gid","mode","path","sha256","size","state","uid"]);if(receipt.state!=="file"||typeof receipt.backup!=="string"||!/^[0-9]{3}\.bin$/u.test(receipt.backup)||typeof receipt.sha256!=="string"||!/^[a-f0-9]{64}$/u.test(receipt.sha256)||!safeMetadata(receipt.size)||receipt.size>MAX_TRANSACTION_FILE_BYTES||!safeMetadata(receipt.uid)||!safeMetadata(receipt.gid)||!safeMetadata(receipt.mode)||receipt.mode>4095){return fail5("transaction file receipt is invalid")}return{path:receiptPath,state:"file",backup:receipt.backup,sha256:receipt.sha256,size:receipt.size,uid:receipt.uid,gid:receipt.gid,mode:receipt.mode}});const directories=record2.directories.map(value=>{if(typeof value!=="object"||value===null||Array.isArray(value)){return fail5("transaction directory receipt must be an object")}const receipt=value;if(typeof receipt.path!=="string"){return fail5("transaction directory receipt path must be a string")}const receiptPath=safeRelativePath(receipt.path);if(receipt.state==="absent"){requireExactKeys(receipt,["path","state"]);return{path:receiptPath,state:"absent"}}requireExactKeys(receipt,["gid","mode","path","state","uid"]);if(receipt.state!=="directory"||!safeMetadata(receipt.uid)||!safeMetadata(receipt.gid)||!safeMetadata(receipt.mode)||receipt.mode>4095){return fail5("transaction directory receipt is invalid")}return{path:receiptPath,state:"directory",uid:receipt.uid,gid:receipt.gid,mode:receipt.mode}});const filePaths=files.map(receipt=>receipt.path);const directoryPaths=directories.map(receipt=>receipt.path);const backupNames=files.filter(receipt=>receipt.state==="file").map(receipt=>receipt.backup);if(new Set(filePaths).size!==filePaths.length||new Set(directoryPaths).size!==directoryPaths.length||new Set(backupNames).size!==backupNames.length){fail5("transaction manifest contains duplicate receipts")}const manifest={schemaVersion:TRANSACTION_SCHEMA_VERSION,agent:record2.agent,profileFingerprint:record2.profileFingerprint,bootstrapIdentity,files,directories};const canonical=hasBootstrapIdentity?canonicalManifest(manifest):canonicalLegacyManifest(manifest);if(canonical!==text){fail5("transaction manifest is not canonical")}return manifest}function requireTrustedTransactionPath(target,mode,options){const stat=import_node_fs2.default.lstatSync(target);if(stat.isSymbolicLink()||(mode===TRANSACTION_DIRECTORY_MODE?!stat.isDirectory():!stat.isFile())||!options.readOnlyReceipt&&(stat.uid!==options.trustedUid||stat.gid!==options.trustedGid)||modeOf2(stat)!==mode){fail5(`transaction artifact has unsafe metadata: ${target}`)}}function requireReadOnlyReceiptMount(target,options){if(!options.readOnlyReceipt)return;const probe=import_node_path2.default.join(target,".nemoclaw-write-probe");let descriptor;try{descriptor=import_node_fs2.default.openSync(probe,import_node_fs2.default.constants.O_CREAT|import_node_fs2.default.constants.O_EXCL|import_node_fs2.default.constants.O_WRONLY|import_node_fs2.default.constants.O_NOFOLLOW,384);import_node_fs2.default.closeSync(descriptor);descriptor=void 0;import_node_fs2.default.unlinkSync(probe)}catch(error){if(descriptor!==void 0)import_node_fs2.default.closeSync(descriptor);if(error.code==="EROFS")return;fail5("copied receipt must be mounted on a read-only filesystem")}fail5("copied receipt mount is writable")}function loadManifest(options){requireTransactionBoundaries(options);if(!pathExistsNoFollow(options.transactionDirectory))return null;requireTrustedTransactionPath(options.transactionDirectory,TRANSACTION_DIRECTORY_MODE,options);requireReadOnlyReceiptMount(options.transactionDirectory,options);requireTrustedTransactionPath(options.backupDirectory,TRANSACTION_DIRECTORY_MODE,options);requireTrustedTransactionPath(options.manifestFile,TRANSACTION_FILE_MODE,options);const stable=readStableFile(options.manifestFile,MAX_MANIFEST_BYTES);if(!options.readOnlyReceipt&&(Number(stable.stat.uid)!==options.trustedUid||Number(stable.stat.gid)!==options.trustedGid)||Number(stable.stat.mode&0o7777n)!==TRANSACTION_FILE_MODE){fail5("transaction manifest ownership changed while it was read")}return parseManifest(stable.bytes.toString("utf8"))}function transactionOptionsAt(options,transactionDirectory){return{...options,transactionDirectory,backupDirectory:import_node_path2.default.join(transactionDirectory,"backups"),manifestFile:import_node_path2.default.join(transactionDirectory,"manifest.json")}}function loadCommitReceipt(options){requireTransactionBoundaries(options);if(!pathExistsNoFollow(options.commitReceiptDirectory))return null;requireTrustedTransactionPath(options.commitReceiptDirectory,TRANSACTION_DIRECTORY_MODE,options);if(pathExistsNoFollow(options.commitReceiptFile)){requireReadOnlyReceiptMount(options.commitReceiptDirectory,options);requireTrustedTransactionPath(options.commitReceiptFile,TRANSACTION_FILE_MODE,options);const stable=readStableFile(options.commitReceiptFile,MAX_COMMIT_RECEIPT_BYTES);if(!options.readOnlyReceipt&&(Number(stable.stat.uid)!==options.trustedUid||Number(stable.stat.gid)!==options.trustedGid)||Number(stable.stat.mode&0o7777n)!==TRANSACTION_FILE_MODE){fail5("commit receipt ownership changed while it was read")}return{receipt:parseCommitReceipt(stable.bytes.toString("utf8")),compact:true}}const stagedOptions=transactionOptionsAt(options,options.commitReceiptDirectory);const staged=loadManifest(stagedOptions);if(!staged||staged.bootstrapIdentity===null){fail5("durable commit staging receipt is incomplete")}verifyAllBackups(staged.files,stagedOptions);return{receipt:{schemaVersion:TRANSACTION_SCHEMA_VERSION,agent:staged.agent,profileFingerprint:staged.profileFingerprint,bootstrapIdentity:staged.bootstrapIdentity},compact:false}}function verifyBackup(receipt,options){const backupPath=import_node_path2.default.join(options.backupDirectory,receipt.backup);requireTrustedTransactionPath(backupPath,TRANSACTION_FILE_MODE,options);const stable=readStableFile(backupPath,MAX_TRANSACTION_FILE_BYTES);const digest=(0,import_node_crypto5.createHash)("sha256").update(stable.bytes).digest("hex");if(stable.bytes.length!==receipt.size||digest!==receipt.sha256){fail5(`transaction backup does not match its receipt: ${receipt.path}`)}return stable.bytes}function verifyAllBackups(receipts,options){const backups=new Map;for(const receipt of receipts){if(receipt.state==="file"){backups.set(receipt.path,verifyBackup(receipt,options))}}return backups}function fileMatchesReceipt(target,receipt){let stat;try{stat=import_node_fs2.default.lstatSync(target)}catch(error){if(error.code==="ENOENT")return false;fail5(`could not inspect managed output ${target}`)}if(stat.isSymbolicLink()||!stat.isFile()||stat.nlink!==1)return false;const stable=readStableFile(target,MAX_TRANSACTION_FILE_BYTES);return stable.bytes.length===receipt.size&&(0,import_node_crypto5.createHash)("sha256").update(stable.bytes).digest("hex")===receipt.sha256&&Number(stable.stat.uid)===receipt.uid&&Number(stable.stat.gid)===receipt.gid&&Number(stable.stat.mode&0o7777n)===receipt.mode}function directoryMatchesReceipt(target,receipt){let stat;try{stat=import_node_fs2.default.lstatSync(target)}catch(error){if(error.code==="ENOENT")return false;fail5(`could not inspect managed output directory ${target}`)}return!stat.isSymbolicLink()&&stat.isDirectory()&&stat.uid===receipt.uid&&stat.gid===receipt.gid&&modeOf2(stat)===receipt.mode}function removeTransactionDirectory(options){requireTrustedTransactionPath(options.transactionDirectory,TRANSACTION_DIRECTORY_MODE,options);import_node_fs2.default.rmSync(options.transactionDirectory,{force:false,recursive:true});fsyncDirectory(options.transactionParentDirectory);if(pathExistsNoFollow(options.transactionDirectory)){fail5("transaction directory remained after cleanup")}}function assertCommitReceiptMatches(receipt,expected){if(receipt.agent!==expected.agent||expected.profileFingerprint!==void 0&&receipt.profileFingerprint!==expected.profileFingerprint||receipt.bootstrapIdentity!==expected.bootstrapIdentity){fail5("durable commit receipt belongs to a different bootstrap attempt")}}function loadCommitStagingManifest(options){if(!pathExistsNoFollow(options.manifestFile))return null;requireTrustedTransactionPath(options.manifestFile,TRANSACTION_FILE_MODE,options);const stable=readStableFile(options.manifestFile,MAX_MANIFEST_BYTES);if(Number(stable.stat.uid)!==options.trustedUid||Number(stable.stat.gid)!==options.trustedGid||Number(stable.stat.mode&0o7777n)!==TRANSACTION_FILE_MODE){fail5("durable commit staging manifest ownership changed while it was read")}return parseManifest(stable.bytes.toString("utf8"))}function retireInterruptedCommitReceiptWrites(receipt,options){const temporaryPattern=new RegExp(`^\\.${MANAGED_STARTUP_SHARED_COMMIT_RECEIPT_FILE.replace(".","\\.")}\\.[a-f0-9]{24}$`,"u");for(const entry of import_node_fs2.default.readdirSync(options.commitReceiptDirectory)){if(!temporaryPattern.test(entry))continue;const target=import_node_path2.default.join(options.commitReceiptDirectory,entry);const stat=import_node_fs2.default.lstatSync(target);if(stat.isSymbolicLink()||!stat.isFile()||stat.nlink!==1||stat.uid!==options.trustedUid||stat.gid!==options.trustedGid||![ATOMIC_TEMPORARY_FILE_MODE,TRANSACTION_FILE_MODE].includes(modeOf2(stat))){fail5("interrupted durable commit receipt write has unsafe metadata")}const stable=readStableFile(target,MAX_COMMIT_RECEIPT_BYTES);const mode=Number(stable.stat.mode&0o7777n);if(Number(stable.stat.uid)!==options.trustedUid||Number(stable.stat.gid)!==options.trustedGid||![ATOMIC_TEMPORARY_FILE_MODE,TRANSACTION_FILE_MODE].includes(mode)){fail5("interrupted durable commit receipt write changed during verification")}if(stable.bytes.length>0){let interruptedReceipt=null;try{interruptedReceipt=parseCommitReceipt(stable.bytes.toString("utf8"))}catch{}if(interruptedReceipt)assertCommitReceiptMatches(interruptedReceipt,receipt)}import_node_fs2.default.unlinkSync(target);fsyncDirectory(options.commitReceiptDirectory)}}function compactDurableCommitReceipt(state,options){if(!state.compact){atomicWriteTrustedFile(options.commitReceiptFile,canonicalCommitReceipt(state.receipt),TRANSACTION_FILE_MODE,options.trustedUid,options.trustedGid);fsyncDirectory(options.commitReceiptDirectory)}retireInterruptedCommitReceiptWrites(state.receipt,options);const stagedOptions=transactionOptionsAt(options,options.commitReceiptDirectory);const manifestExists=pathExistsNoFollow(stagedOptions.manifestFile);const backupsExist=pathExistsNoFollow(stagedOptions.backupDirectory);const unexpectedBeforeCleanup=import_node_fs2.default.readdirSync(options.commitReceiptDirectory).filter(entry=>![MANAGED_STARTUP_SHARED_COMMIT_RECEIPT_FILE,import_node_path2.default.basename(stagedOptions.backupDirectory),import_node_path2.default.basename(stagedOptions.manifestFile)].includes(entry));if(unexpectedBeforeCleanup.length!==0){fail5("durable commit receipt directory contains unexpected artifacts")}if(manifestExists){const staged=loadCommitStagingManifest(stagedOptions);if(!staged||staged.bootstrapIdentity===null){fail5("durable commit staging receipt disappeared during cleanup")}assertCommitReceiptMatches(state.receipt,{agent:staged.agent,profileFingerprint:staged.profileFingerprint,bootstrapIdentity:staged.bootstrapIdentity})}if(backupsExist){requireTrustedTransactionPath(stagedOptions.backupDirectory,TRANSACTION_DIRECTORY_MODE,options);import_node_fs2.default.rmSync(stagedOptions.backupDirectory,{force:false,recursive:true});fsyncDirectory(options.commitReceiptDirectory)}if(manifestExists){requireTrustedTransactionPath(stagedOptions.manifestFile,TRANSACTION_FILE_MODE,options);import_node_fs2.default.unlinkSync(stagedOptions.manifestFile);fsyncDirectory(options.commitReceiptDirectory)}const unexpected=import_node_fs2.default.readdirSync(options.commitReceiptDirectory).filter(entry=>entry!==MANAGED_STARTUP_SHARED_COMMIT_RECEIPT_FILE);if(unexpected.length!==0){fail5("durable commit receipt directory contains unexpected artifacts")}const verified=loadCommitReceipt(options);if(!verified?.compact)fail5("durable commit receipt did not compact successfully");assertCommitReceiptMatches(verified.receipt,state.receipt)}function beginManagedStartupSharedStateTransaction(profile,inputOptions={}){const options=resolveOptions(inputOptions);requireTransactionIdentity(options);if(options.readOnlyReceipt){fail5("cannot begin a transaction from a read-only rollback receipt")}requireTransactionBoundaries(options);const profileFingerprint=fingerprintManagedStartupProfile(profile);const committed=loadCommitReceipt(options);if(committed){if(options.bootstrapIdentity===null){fail5("a durable managed bootstrap commit receipt already exists")}assertCommitReceiptMatches(committed.receipt,{agent:profile.agent,profileFingerprint,bootstrapIdentity:options.bootstrapIdentity});fail5("this managed bootstrap attempt is already durably committed")}const pending=loadManifest(options);if(pending){if(pending.agent!==profile.agent||pending.profileFingerprint!==profileFingerprint||pending.bootstrapIdentity!==options.bootstrapIdentity){fail5("a pending managed startup transaction belongs to a different agent, profile fingerprint, or bootstrap attempt")}verifyAllBackups(pending.files,options);return false}const targets=managedOutputTargets(profile,options);if(targets.files.length>MAX_TRANSACTION_FILES){fail5("managed startup transaction has too many file targets")}const snapshots=targets.files.map((target,index)=>snapshotFile(target,index,profile.agent,options));const totalBytes=snapshots.reduce((sum,snapshot)=>sum+(snapshot.bytes?.length??0),0);if(totalBytes>MAX_TRANSACTION_TOTAL_BYTES){fail5("managed startup transaction backup exceeds the total size limit")}const directories=targets.directories.map(target=>snapshotDirectory(target,profile.agent,options));const manifest={schemaVersion:TRANSACTION_SCHEMA_VERSION,agent:profile.agent,profileFingerprint,bootstrapIdentity:options.bootstrapIdentity,files:snapshots.map(({receipt})=>receipt),directories};let createdTransactionIdentity;try{import_node_fs2.default.mkdirSync(options.transactionDirectory,{mode:TRANSACTION_DIRECTORY_MODE});const created=import_node_fs2.default.lstatSync(options.transactionDirectory,{bigint:true});if(!created.isDirectory()||created.isSymbolicLink()){fail5("new transaction path is not a directory")}createdTransactionIdentity={dev:created.dev,ino:created.ino,uid:created.uid,gid:created.gid};import_node_fs2.default.chownSync(options.transactionDirectory,options.trustedUid,options.trustedGid);import_node_fs2.default.chmodSync(options.transactionDirectory,TRANSACTION_DIRECTORY_MODE);fsyncDirectory(options.transactionParentDirectory);import_node_fs2.default.mkdirSync(options.backupDirectory,{mode:TRANSACTION_DIRECTORY_MODE});import_node_fs2.default.chownSync(options.backupDirectory,options.trustedUid,options.trustedGid);import_node_fs2.default.chmodSync(options.backupDirectory,TRANSACTION_DIRECTORY_MODE);fsyncDirectory(options.transactionDirectory);for(const snapshot of snapshots){if(snapshot.receipt.state!=="file"||snapshot.bytes===null)continue;atomicWriteTrustedFile(import_node_path2.default.join(options.backupDirectory,snapshot.receipt.backup),snapshot.bytes,TRANSACTION_FILE_MODE,options.trustedUid,options.trustedGid)}fsyncDirectory(options.backupDirectory);atomicWriteTrustedFile(options.manifestFile,canonicalManifest(manifest),TRANSACTION_FILE_MODE,options.trustedUid,options.trustedGid);fsyncDirectory(options.transactionDirectory);loadManifest(options)}catch(error){try{if(createdTransactionIdentity&&pathExistsNoFollow(options.transactionDirectory)){const current=import_node_fs2.default.lstatSync(options.transactionDirectory,{bigint:true});if(!current.isSymbolicLink()&¤t.isDirectory()&¤t.dev===createdTransactionIdentity.dev&¤t.ino===createdTransactionIdentity.ino&¤t.uid===createdTransactionIdentity.uid&¤t.gid===createdTransactionIdentity.gid){import_node_fs2.default.chmodSync(options.transactionDirectory,TRANSACTION_DIRECTORY_MODE);import_node_fs2.default.chownSync(options.transactionDirectory,options.trustedUid,options.trustedGid)}requireTrustedTransactionPath(options.transactionDirectory,TRANSACTION_DIRECTORY_MODE,options);import_node_fs2.default.rmSync(options.transactionDirectory,{force:true,recursive:true})}}catch{}throw error}return true}function ensureOriginalDirectories(receipts,expectedAgent,options){for(const receipt of receipts){if(receipt.state!=="directory")continue;const target=absoluteTarget(receipt.path,options);validateExistingAncestors(import_node_path2.default.join(target,".restore"),expectedAgent,options);let stat=null;try{stat=import_node_fs2.default.lstatSync(target)}catch(error){if(error.code!=="ENOENT"){fail5(`could not inspect restore directory ${target}`)}}if(stat&&(stat.isSymbolicLink()||!stat.isDirectory())){fail5(`restore directory is unsafe: ${target}`)}if(stat&&directoryMatchesReceipt(target,receipt))continue;if(!stat)import_node_fs2.default.mkdirSync(target,{mode:receipt.mode});import_node_fs2.default.chownSync(target,receipt.uid,receipt.gid);import_node_fs2.default.chmodSync(target,receipt.mode)}}function restoreFiles(receipts,backups,expectedAgent,options){for(const receipt of receipts){const target=absoluteTarget(receipt.path,options);validateExistingAncestors(target,expectedAgent,options);if(receipt.state==="absent"){let stat;try{stat=import_node_fs2.default.lstatSync(target)}catch(error){if(error.code==="ENOENT")continue;fail5(`could not inspect new managed output ${target}`)}if(stat.isDirectory()){fail5(`new managed output unexpectedly became a directory: ${target}`)}import_node_fs2.default.unlinkSync(target);continue}if(fileMatchesReceipt(target,receipt))continue;const bytes=backups.get(receipt.path);if(!bytes)fail5(`verified transaction backup is missing: ${receipt.path}`);let current=null;try{current=import_node_fs2.default.lstatSync(target)}catch(error){if(error.code!=="ENOENT"){fail5(`could not inspect managed output before restore: ${target}`)}}if(current?.isDirectory()){fail5(`managed output unexpectedly became a directory: ${target}`)}atomicWriteTrustedFile(target,bytes,receipt.mode,receipt.uid,receipt.gid)}}function restoreDirectoryMetadata(receipts,options){for(const receipt of[...receipts].reverse()){const target=absoluteTarget(receipt.path,options);if(receipt.state==="absent"){try{import_node_fs2.default.rmdirSync(target)}catch(error){if(error.code==="ENOENT")continue;fail5(`could not remove newly created managed directory ${target}`)}continue}if(directoryMatchesReceipt(target,receipt))continue;const stat=import_node_fs2.default.lstatSync(target);if(stat.isSymbolicLink()||!stat.isDirectory()){fail5(`managed directory changed type during restore: ${target}`)}import_node_fs2.default.chownSync(target,receipt.uid,receipt.gid);import_node_fs2.default.chmodSync(target,receipt.mode)}}function verifyRestoration(manifest,options){for(const receipt of manifest.files){const target=absoluteTarget(receipt.path,options);if(receipt.state==="absent"){if(pathExistsNoFollow(target)){fail5(`new managed output remained after rollback: ${target}`)}continue}const stable=readStableFile(target,MAX_TRANSACTION_FILE_BYTES);if(stable.bytes.length!==receipt.size||(0,import_node_crypto5.createHash)("sha256").update(stable.bytes).digest("hex")!==receipt.sha256||Number(stable.stat.uid)!==receipt.uid||Number(stable.stat.gid)!==receipt.gid||Number(stable.stat.mode&0o7777n)!==receipt.mode){fail5(`managed output was not restored exactly: ${target}`)}}for(const receipt of manifest.directories){const target=absoluteTarget(receipt.path,options);if(receipt.state==="absent"){if(pathExistsNoFollow(target)){fail5(`new managed directory remained after rollback: ${target}`)}continue}const stat=import_node_fs2.default.lstatSync(target);if(stat.isSymbolicLink()||!stat.isDirectory()||stat.uid!==receipt.uid||stat.gid!==receipt.gid||modeOf2(stat)!==receipt.mode){fail5(`managed directory metadata was not restored exactly: ${target}`)}}}function rollbackManagedStartupSharedStateTransaction(expectedAgent,inputOptions={}){const options=resolveOptions(inputOptions);requireTransactionIdentity(options);const committed=loadCommitReceipt(options);if(committed){if(options.bootstrapIdentity===null){fail5("shared state is already durably committed")}assertCommitReceiptMatches(committed.receipt,{agent:expectedAgent,bootstrapIdentity:options.bootstrapIdentity});fail5("shared state is already durably committed and cannot be rolled back")}const manifest=loadManifest(options);if(!manifest)return false;if(manifest.agent!==expectedAgent){fail5(`pending transaction targets ${manifest.agent}, expected ${expectedAgent}`)}if(manifest.bootstrapIdentity!==options.bootstrapIdentity){fail5("pending transaction belongs to a different bootstrap attempt")}const backups=verifyAllBackups(manifest.files,options);ensureOriginalDirectories(manifest.directories,expectedAgent,options);restoreFiles(manifest.files,backups,expectedAgent,options);restoreDirectoryMetadata(manifest.directories,options);verifyRestoration(manifest,options);if(!options.readOnlyReceipt){removeTransactionDirectory(options)}return true}function commitManagedStartupSharedStateTransaction(expectedAgent,inputOptions={}){const options=resolveOptions(inputOptions);requireTransactionIdentity(options);if(options.readOnlyReceipt){fail5("cannot commit a read-only rollback receipt")}const committed=loadCommitReceipt(options);if(committed){if(options.bootstrapIdentity===null){fail5("durable commit receipt is missing its expected bootstrap identity")}assertCommitReceiptMatches(committed.receipt,{agent:expectedAgent,bootstrapIdentity:options.bootstrapIdentity});compactDurableCommitReceipt(committed,options);return true}const manifest=loadManifest(options);if(!manifest)return false;if(manifest.agent!==expectedAgent){fail5(`pending transaction targets ${manifest.agent}, expected ${expectedAgent}`)}if(manifest.bootstrapIdentity!==options.bootstrapIdentity){fail5("pending transaction belongs to a different bootstrap attempt")}if(manifest.bootstrapIdentity===null){removeTransactionDirectory(options);return true}verifyAllBackups(manifest.files,options);if(pathExistsNoFollow(options.commitReceiptDirectory)){fail5("durable commit receipt path appeared before transaction commit")}try{import_node_fs2.default.renameSync(options.transactionDirectory,options.commitReceiptDirectory);fsyncDirectory(options.transactionParentDirectory)}catch(error){fail5(`could not atomically establish durable commit state: ${error.message}`)}const renamed=loadCommitReceipt(options);if(!renamed)fail5("durable commit state disappeared after atomic rename");assertCommitReceiptMatches(renamed.receipt,{agent:expectedAgent,profileFingerprint:manifest.profileFingerprint,bootstrapIdentity:manifest.bootstrapIdentity});compactDurableCommitReceipt(renamed,options);return true}function clearManagedStartupSharedStateCommitReceipt(expectedAgent,inputOptions={}){const options=resolveOptions(inputOptions);requireTransactionIdentity(options);if(options.readOnlyReceipt){fail5("cannot clear a durable commit from a read-only receipt")}if(options.bootstrapIdentity===null){fail5("durable commit cleanup requires its bootstrap identity")}const committed=loadCommitReceipt(options);if(!committed)return false;assertCommitReceiptMatches(committed.receipt,{agent:expectedAgent,bootstrapIdentity:options.bootstrapIdentity});compactDurableCommitReceipt(committed,options);requireTrustedTransactionPath(options.commitReceiptDirectory,TRANSACTION_DIRECTORY_MODE,options);requireTrustedTransactionPath(options.commitReceiptFile,TRANSACTION_FILE_MODE,options);const entries=import_node_fs2.default.readdirSync(options.commitReceiptDirectory);if(entries.length!==1||entries[0]!==MANAGED_STARTUP_SHARED_COMMIT_RECEIPT_FILE){fail5("durable commit receipt directory contains unexpected artifacts")}import_node_fs2.default.rmSync(options.commitReceiptDirectory,{force:false,recursive:true});fsyncDirectory(options.transactionParentDirectory);if(pathExistsNoFollow(options.commitReceiptDirectory)){fail5("durable commit receipt remained after cleanup")}return true}function getManagedStartupSharedStateTransactionStatus(expected,inputOptions={}){const options=resolveOptions({...inputOptions,bootstrapIdentity:expected.bootstrapIdentity});requireTransactionIdentity(options);const manifest=loadManifest(options);if(manifest){if(manifest.agent!==expected.agent||manifest.profileFingerprint!==expected.profileFingerprint||manifest.bootstrapIdentity!==expected.bootstrapIdentity){fail5("pending transaction does not match the expected agent, profile fingerprint, or bootstrap identity")}verifyAllBackups(manifest.files,options);return"pending"}const committed=loadCommitReceipt(options);if(!committed)return"none";assertCommitReceiptMatches(committed.receipt,expected);return"committed"}var MANAGED_STARTUP_PROFILE_ENV="NEMOCLAW_STARTUP_PROFILE_B64";var MANAGED_STARTUP_CA_ENV="NEMOCLAW_CORPORATE_CA_B64";var MANAGED_STARTUP_RUNTIME_ENV_FILE="/run/nemoclaw/managed-startup-runtime.env";var MANAGED_STARTUP_RUNTIME_EXECUTABLE="/usr/local/lib/nemoclaw/managed-startup-image-runtime.cjs";var MANAGED_STARTUP_MERGED_CA_FILE="/run/nemoclaw/managed-startup-ca-bundle.pem";var MANAGED_STARTUP_COMPLETION_FILE="/run/nemoclaw/managed-startup-complete.json";var MANAGED_STARTUP_CORPORATE_CA_FILE="/usr/local/share/nemoclaw/corporate-ca.pem";var MANAGED_STARTUP_SYSTEM_CA_ANCHOR_DIRECTORY="/usr/local/share/ca-certificates";var MANAGED_STARTUP_SYSTEM_CA_ANCHOR_RE=/^nemoclaw-corporate-ca-[0-9]{2}\.crt$/u;var SYSTEM_CA_BUNDLE_FILE="/etc/ssl/certs/ca-certificates.crt";var UPDATE_CA_CERTIFICATES_EXECUTABLE="/usr/sbin/update-ca-certificates";var MANAGED_STARTUP_TLS_ENV_NAMES=new Set(["CURL_CA_BUNDLE","GIT_SSL_CAINFO","NODE_EXTRA_CA_CERTS","REQUESTS_CA_BUNDLE","SSL_CERT_FILE"]);var MESSAGING_RUNTIME_PLAN_FILE="/usr/local/share/nemoclaw/messaging-runtime-plan.json";var ROOT_STATE_PARENT="/var/lib/nemoclaw";var ROOT_RUNTIME_DIRECTORY="/run/nemoclaw";var ROOT_OWNED_DIRECTORY_MODE=493;var MAX_TRUST_BUNDLE_BYTES=4*1024*1024;var HERMES_MANAGED_CONFIG_FILES=["/sandbox/.hermes/config.yaml","/sandbox/.hermes/.env"];var HERMES_GENERATED_MANAGED_POLICY_FILE="/sandbox/.hermes/managed-policy.json";var HERMES_INSTALLED_MANAGED_POLICY_FILE="/usr/local/share/nemoclaw/hermes-managed-policy.json";var MAX_HERMES_MANAGED_POLICY_BYTES=4*1024*1024;var HERMES_GENERATED_RELAY_PLUGINS_FILE="/sandbox/.hermes/relay-plugins.toml";var HERMES_INSTALLED_RELAY_PLUGINS_FILE=HERMES_SWITCHYARD_RELAY_TOML;var HERMES_INSTALLED_SWITCHYARD_RUNTIME_BINDINGS_FILE=HERMES_SWITCHYARD_RUNTIME_BINDINGS;var MAX_HERMES_RELAY_PLUGINS_BYTES=128*1024;var MAX_HERMES_SWITCHYARD_RUNTIME_BINDINGS_BYTES=16*1024;var FIXED_PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin";var SHA256_RE4=/^[a-f0-9]{64}$/u;var MANAGED_STARTUP_COMPLETION_SCHEMA_VERSION=1;var MAX_MANAGED_STARTUP_COMPLETION_BYTES=4096;var MAX_MANAGED_STARTUP_RUNTIME_ENVIRONMENT_BYTES=512*1024;var ManagedStartupImageActionPlanError=class extends Error{constructor(message){super(`Cannot build managed startup image action plan: ${message}`);this.name="ManagedStartupImageActionPlanError"}};var ManagedStartupImageRuntimeError=class extends Error{constructor(message){super(`Managed startup image application failed: ${message}`);this.name="ManagedStartupImageRuntimeError"}};function failActionPlan(message){throw new ManagedStartupImageActionPlanError(message)}function exactActionPlanAgent(value){if(MANAGED_STARTUP_AGENTS.includes(value)){return value}return failActionPlan(`unsupported agent ${JSON.stringify(value)}`)}function fail6(message){throw new ManagedStartupImageRuntimeError(message)}function validateManagedStartupApplicationRuntimePlan(plan){if(typeof plan!=="object"||plan===null){return fail6("application runtime plan must be an object")}const exportEnvironment=plan.exportEnvironment;const unsetEnvironment=plan.unsetEnvironment;if(typeof exportEnvironment!=="object"||exportEnvironment===null||Array.isArray(exportEnvironment)||!Array.isArray(unsetEnvironment)){return fail6("application runtime plan must contain exports and unsets")}const exports2={};for(const[name,value]of Object.entries(exportEnvironment)){if(!/^[A-Za-z_][A-Za-z0-9_]*$/u.test(name)){return fail6(`invalid application runtime environment key ${JSON.stringify(name)}`)}if(typeof value!=="string"||value.includes("\0")||/[\r\n]/u.test(value)){return fail6(`application runtime environment value for ${name} must be single-line text`)}exports2[name]=value}const unsets=new Set;for(const name of unsetEnvironment){if(typeof name!=="string"||!/^[A-Za-z_][A-Za-z0-9_]*$/u.test(name)){return fail6(`invalid application runtime unset ${JSON.stringify(name)}`)}if(unsets.has(name)){return fail6(`duplicate application runtime unset ${name}`)}if(Object.hasOwn(exports2,name)){return fail6(`application runtime cannot both export and unset ${name}`)}unsets.add(name)}return Object.freeze({exportEnvironment:Object.freeze(Object.fromEntries(Object.entries(exports2).sort(([left],[right])=>left.localeCompare(right)))),unsetEnvironment:Object.freeze([...unsets].sort())})}function applyManagedStartupCommandEnvironmentPlan(environment,plan){const validated=validateManagedStartupApplicationRuntimePlan(plan);const applied={...environment};for(const name of[...Object.keys(validated.exportEnvironment),...validated.unsetEnvironment]){delete applied[name]}return applied}function exactAgent2(value){if(MANAGED_STARTUP_AGENTS.includes(value)){return value}return fail6(`unsupported agent ${JSON.stringify(value)}`)}function managedTransactionProfile(expectedAgentInput,env=process.env){requireRoot();const expectedAgent=exactAgent2(expectedAgentInput);if(env.NEMOCLAW_MANAGED_IMAGE_CAPABILITY_UNION!=="1"){fail6("shared-state transactions require a complete managed image")}const encodedProfile=env[MANAGED_STARTUP_PROFILE_ENV];if(!encodedProfile)fail6(`${MANAGED_STARTUP_PROFILE_ENV} is required`);const profile=decodeManagedStartupProfile(encodedProfile);if(profile.agent!==expectedAgent){fail6(`shared-state transaction profile targets ${profile.agent}, expected ${expectedAgent}`)}return profile}function requireRoot(){if(process.geteuid?.()!==0){fail6("managed startup requires container effective uid 0")}}function modeOf3(stat){return stat.mode&511}function requireRootOwnedDirectory(target,mode){let stat;try{stat=import_node_fs3.default.lstatSync(target)}catch{fail6(`required root-owned directory is missing: ${target}`)}if(stat.isSymbolicLink()||!stat.isDirectory()||stat.uid!==0||stat.gid!==0||modeOf3(stat)!==mode){fail6(`${target} must be a root:root directory with mode ${mode.toString(8)}`)}}function ensureRootOwnedDirectory(target,mode=ROOT_OWNED_DIRECTORY_MODE){const parent=import_node_path3.default.dirname(target);const parentStat=import_node_fs3.default.lstatSync(parent);if(parentStat.isSymbolicLink()||!parentStat.isDirectory()||parentStat.uid!==0||parentStat.gid!==0||(modeOf3(parentStat)&18)!==0){fail6(`refusing unsafe parent directory for ${target}`)}try{import_node_fs3.default.mkdirSync(target,{mode});import_node_fs3.default.chownSync(target,0,0);import_node_fs3.default.chmodSync(target,mode)}catch(error){if(error.code!=="EEXIST"){fail6(`could not create ${target}`)}}requireRootOwnedDirectory(target,mode)}function requireSafeExistingRootTarget(target){let stat;try{stat=import_node_fs3.default.lstatSync(target)}catch(error){if(error.code==="ENOENT")return;fail6(`could not inspect ${target}`)}if(stat.isSymbolicLink()||!stat.isFile()||stat.nlink!==1||stat.uid!==0||stat.gid!==0){fail6(`refusing to replace unsafe root-owned file ${target}`)}}function atomicWriteRootFile(target,contents,mode){const parent=import_node_path3.default.dirname(target);const parentStat=import_node_fs3.default.lstatSync(parent);if(parentStat.isSymbolicLink()||!parentStat.isDirectory()||parentStat.uid!==0||parentStat.gid!==0||(modeOf3(parentStat)&18)!==0){fail6(`refusing unsafe root-owned file parent ${parent}`)}requireSafeExistingRootTarget(target);const temporary=import_node_path3.default.join(parent,`.${import_node_path3.default.basename(target)}.${(0,import_node_crypto6.randomBytes)(12).toString("hex")}`);let descriptor;try{descriptor=import_node_fs3.default.openSync(temporary,import_node_fs3.default.constants.O_CREAT|import_node_fs3.default.constants.O_EXCL|import_node_fs3.default.constants.O_WRONLY|import_node_fs3.default.constants.O_NOFOLLOW,384);import_node_fs3.default.fchownSync(descriptor,0,0);import_node_fs3.default.writeFileSync(descriptor,contents);import_node_fs3.default.fchmodSync(descriptor,mode);import_node_fs3.default.fsyncSync(descriptor);import_node_fs3.default.closeSync(descriptor);descriptor=void 0;import_node_fs3.default.renameSync(temporary,target)}catch(error){if(descriptor!==void 0)import_node_fs3.default.closeSync(descriptor);try{import_node_fs3.default.unlinkSync(temporary)}catch{}fail6(`could not atomically write ${target}: ${error.message}`)}const stat=import_node_fs3.default.lstatSync(target);if(stat.isSymbolicLink()||!stat.isFile()||stat.nlink!==1||stat.uid!==0||stat.gid!==0||modeOf3(stat)!==mode){fail6(`root-owned output failed metadata verification: ${target}`)}}function removeSafeRootFile(target){requireSafeExistingRootTarget(target);try{import_node_fs3.default.unlinkSync(target)}catch(error){if(error.code!=="ENOENT"){fail6(`could not remove ${target}`)}}}function trustedExecutable(target){try{const stat=import_node_fs3.default.lstatSync(target);return!stat.isSymbolicLink()&&stat.isFile()&&stat.uid===0&&stat.gid===0&&(modeOf3(stat)&18)===0&&(modeOf3(stat)&73)!==0}catch{return false}}function readSandboxIdentity(){const readId=flag=>{const result=(0,import_node_child_process.spawnSync)("/usr/bin/id",[flag,"sandbox"],{encoding:"utf8",env:{PATH:FIXED_PATH}});const value=result.stdout.trim();if(result.status!==0||!/^[1-9][0-9]*$/u.test(value)){fail6("could not resolve the sandbox account")}return value};return{uid:readId("-u"),gid:readId("-g")}}function managedStartupSandboxPrefix(){if(trustedExecutable("/usr/bin/setpriv")){const identity=readSandboxIdentity();return["/usr/bin/setpriv",`--reuid=${identity.uid}`,`--regid=${identity.gid}`,"--init-groups","--"]}return fail6("a trusted setpriv executable is required")}function commandEnvironment(configurationEnvironment,applicationRuntime){const env=applyManagedStartupCommandEnvironmentPlan({...process.env,...configurationEnvironment,HOME:"/sandbox",PATH:FIXED_PATH,NPM_CONFIG_OFFLINE:"true",npm_config_offline:"true",PIP_DISABLE_PIP_VERSION_CHECK:"1",PIP_NO_INDEX:"1",UV_OFFLINE:"1"},applicationRuntime);delete env[MANAGED_STARTUP_PROFILE_ENV];delete env[MANAGED_STARTUP_CA_ENV];return env}function execute(argv,runAs,configurationEnvironment,applicationRuntime,capture=false){if(argv.length===0)fail6("refusing an empty managed startup command");const command=runAs==="sandbox"?[...managedStartupSandboxPrefix(),...argv]:[...argv];const result=(0,import_node_child_process.spawnSync)(command[0],command.slice(1),{encoding:"utf8",env:commandEnvironment(configurationEnvironment,applicationRuntime),stdio:capture?"pipe":"inherit"});if(result.error){fail6(`could not execute ${argv[0]}: ${result.error.message}`)}if(result.status!==0){const detail=capture?`: ${(result.stderr||result.stdout).trim()}`:"";fail6(`${argv[0]} exited with status ${String(result.status??"unknown")}${detail}`)}return{status:result.status,stdout:result.stdout??"",stderr:result.stderr??""}}function generatorCommand(agent){switch(agent){case"openclaw":return["/usr/local/bin/node","--experimental-strip-types","/scripts/generate-openclaw-config.mts"];case"hermes":return["/usr/local/bin/node","--experimental-strip-types","/opt/nemoclaw-hermes-config/generate-config.ts"];case"langchain-deepagents-code":return["/usr/local/bin/node","--experimental-strip-types","/opt/nemoclaw-deepagents-code/generate-config.ts"];case"pi":return["/usr/local/bin/node","--experimental-strip-types","/opt/nemoclaw-pi/generate-config.ts"]}}function messagingCommand(agent,phase,mode){return["/usr/local/bin/node","--experimental-strip-types","/src/lib/messaging/applier/build/messaging-build-applier.mts","--agent",agent,"--phase",phase,"--mode",mode,...phase==="post-agent-install"?["--managed-startup-runtime"]:[]]}function assertActionAgent(inputAgent,actionAgent){if(inputAgent!==actionAgent){failActionPlan(`action for ${actionAgent} cannot be used by ${inputAgent}`)}}function buildManagedStartupImageActionPlan(input){const inputAgent=exactActionPlanAgent(input.agent);const commands=[];let dashboardActions=0;let generateActions=0;let runtimeMessagingActions=0;let postMessagingActions=0;for(const action of input.actions){switch(action.kind){case"configure-dashboard":{if(action.dashboard.agent!==input.agent){failActionPlan(`dashboard for ${action.dashboard.agent} cannot be used by ${input.agent}`)}dashboardActions+=1;break}case"generate-agent-config":{assertActionAgent(inputAgent,exactActionPlanAgent(action.agent));if(action.runAs!=="sandbox"){failActionPlan("agent configuration generation must run as sandbox")}generateActions+=1;commands.push({action:"generate-agent-config",runAs:action.runAs,argv:generatorCommand(action.agent)});break}case"apply-messaging-plan":{assertActionAgent(inputAgent,exactActionPlanAgent(action.agent));if(action.mode!=="apply"&&action.mode!=="clear"){failActionPlan("messaging intent must be apply or clear")}if(action.phase==="runtime-setup"){if(action.runAs!=="root"){failActionPlan("messaging runtime setup must run as root")}runtimeMessagingActions+=1;commands.push({action:"messaging-runtime-setup",runAs:action.runAs,argv:messagingCommand(action.agent,action.phase,action.mode)})}else if(action.phase==="post-agent-install"){if(action.runAs!=="sandbox"){failActionPlan("messaging post-agent configuration must run as sandbox")}postMessagingActions+=1;commands.push({action:"messaging-post-agent-install",runAs:action.runAs,argv:messagingCommand(action.agent,action.phase,action.mode)})}else{failActionPlan("unsupported messaging construction phase")}break}default:failActionPlan("unsupported managed startup construction action")}}if(dashboardActions!==1){failActionPlan("exactly one dashboard construction action is required")}if(generateActions!==1){failActionPlan("exactly one agent config construction action is required")}const supportsMessaging=MANAGED_STARTUP_MESSAGING_AGENTS.includes(inputAgent);const expectedMessagingActions=supportsMessaging?1:0;if(runtimeMessagingActions!==expectedMessagingActions||postMessagingActions!==expectedMessagingActions){failActionPlan(`${inputAgent} requires ${String(expectedMessagingActions)} action for each messaging phase`)}const expectedOrder=supportsMessaging?["messaging-runtime-setup","generate-agent-config","messaging-post-agent-install"]:["generate-agent-config"];if(commands.some((command,index)=>command.action!==expectedOrder[index])){failActionPlan(`${inputAgent} image actions are not in the required construction order`)}return Object.freeze(commands.map(command=>Object.freeze({...command,argv:Object.freeze([...command.argv])})))}function prepareMessagingRuntimeTarget(mode){if(mode==="clear"){removeSafeRootFile(MESSAGING_RUNTIME_PLAN_FILE);return}requireSafeExistingRootTarget(MESSAGING_RUNTIME_PLAN_FILE);try{import_node_fs3.default.unlinkSync(MESSAGING_RUNTIME_PLAN_FILE)}catch(error){if(error.code!=="ENOENT"){fail6("could not prepare the messaging runtime-plan target")}}}function verifyMessagingRuntimeTarget(mode){if(mode==="clear"){if(import_node_fs3.default.existsSync(MESSAGING_RUNTIME_PLAN_FILE)){fail6("clear messaging profile left a runtime-plan artifact")}return}const stat=import_node_fs3.default.lstatSync(MESSAGING_RUNTIME_PLAN_FILE);if(stat.isSymbolicLink()||!stat.isFile()||stat.nlink!==1||stat.uid!==0||stat.gid!==0||modeOf3(stat)!==420){fail6("messaging runtime-plan artifact failed root ownership validation")}}function runInternalSandboxAction(action,configurationEnvironment,applicationRuntime,extraEnvironment={}){execute(["/usr/local/bin/node",MANAGED_STARTUP_RUNTIME_EXECUTABLE,`--internal-${action}`],"sandbox",{...configurationEnvironment,...extraEnvironment},applicationRuntime)}function sealOpenClawConfiguration(configurationEnvironment,applicationRuntime){const validation=execute(["/usr/local/bin/openclaw","config","validate","--json"],"sandbox",{...configurationEnvironment,OPENCLAW_CONFIG_PATH:"/sandbox/.openclaw/openclaw.json"},applicationRuntime,true);let parsed;try{parsed=JSON.parse(validation.stdout)}catch{fail6("OpenClaw config validation did not emit JSON")}if(typeof parsed!=="object"||parsed===null||parsed.valid!==true){fail6("OpenClaw rejected the generated managed startup config")}runInternalSandboxAction("write-openclaw-hash",configurationEnvironment,applicationRuntime)}function sameStableFileMetadata(left,right){return left.dev===right.dev&&left.ino===right.ino&&left.mode===right.mode&&left.nlink===right.nlink&&left.uid===right.uid&&left.gid===right.gid&&left.size===right.size&&left.mtimeNs===right.mtimeNs&&left.ctimeNs===right.ctimeNs}function readStableRegularFileSnapshot(target,maxBytes){if(typeof import_node_fs3.default.constants.O_NOFOLLOW!=="number"){fail6("O_NOFOLLOW is unavailable for managed startup file reads")}const nonblock=typeof import_node_fs3.default.constants.O_NONBLOCK==="number"?import_node_fs3.default.constants.O_NONBLOCK:0;let descriptor;try{descriptor=import_node_fs3.default.openSync(target,import_node_fs3.default.constants.O_RDONLY|import_node_fs3.default.constants.O_NOFOLLOW|nonblock)}catch(error){if(error.code==="ENOENT")throw error;fail6(`refusing unsafe or unreadable file ${target}`)}try{const before=import_node_fs3.default.fstatSync(descriptor,{bigint:true});if(!before.isFile()||before.nlink!==1n||before.size<1n||before.size>BigInt(maxBytes)){fail6(`refusing unsafe or oversized file ${target}`)}const bytes=Buffer.alloc(Number(before.size));let offset=0;while(offset`${block.trim()} +`)}function managedSystemCaAnchorNames(){try{import_node_fs3.default.lstatSync(MANAGED_STARTUP_SYSTEM_CA_ANCHOR_DIRECTORY)}catch(error){if(error.code==="ENOENT")return[];fail6("could not inspect the managed system CA anchor directory")}requireRootOwnedDirectory(MANAGED_STARTUP_SYSTEM_CA_ANCHOR_DIRECTORY,ROOT_OWNED_DIRECTORY_MODE);try{return import_node_fs3.default.readdirSync(MANAGED_STARTUP_SYSTEM_CA_ANCHOR_DIRECTORY).filter(name=>MANAGED_STARTUP_SYSTEM_CA_ANCHOR_RE.test(name)).sort()}catch(error){fail6("could not inspect the managed system CA anchors")}}function refreshSystemCaBundle(){if(!trustedExecutable(UPDATE_CA_CERTIFICATES_EXECUTABLE)){fail6(`a trusted ${UPDATE_CA_CERTIFICATES_EXECUTABLE} executable is required`)}const result=(0,import_node_child_process.spawnSync)(UPDATE_CA_CERTIFICATES_EXECUTABLE,[],{encoding:"utf8",env:{PATH:FIXED_PATH},stdio:"inherit"});if(result.error){fail6(`could not execute ${UPDATE_CA_CERTIFICATES_EXECUTABLE}: ${result.error.message}`)}if(result.status!==0){fail6(`${UPDATE_CA_CERTIFICATES_EXECUTABLE} exited with status ${String(result.status??"unknown")}`)}}function requireSystemCaBundleContains(blocks){const systemBundle=safeTrustBundle(SYSTEM_CA_BUNDLE_FILE);if(systemBundle===null)fail6("the refreshed system CA bundle is missing");const systemBlocks=systemBundle.toString("utf8").match(PEM_CERTIFICATE_RE_GLOBAL)??[];const systemFingerprints=new Set;for(const block of systemBlocks){try{systemFingerprints.add(new import_node_crypto6.X509Certificate(block).fingerprint256)}catch{fail6("the refreshed system CA bundle contains an invalid certificate")}}for(const block of blocks){if(!systemFingerprints.has(new import_node_crypto6.X509Certificate(block).fingerprint256)){fail6("the refreshed system CA bundle does not contain the corporate CA")}}}function installCorporateCaSystemAnchors(corporateCaPath){const existingNames=managedSystemCaAnchorNames();if(corporateCaPath===null){for(const name of existingNames){removeSafeRootFile(import_node_path3.default.join(MANAGED_STARTUP_SYSTEM_CA_ANCHOR_DIRECTORY,name))}refreshSystemCaBundle();return}ensureRootOwnedDirectory(MANAGED_STARTUP_SYSTEM_CA_ANCHOR_DIRECTORY);const blocks=corporateCaCertificateBlocks(corporateCaPath);const expectedNames=blocks.map((_block,index)=>`nemoclaw-corporate-ca-${String(index+1).padStart(2,"0")}.crt`);for(const name of existingNames){if(!expectedNames.includes(name)){removeSafeRootFile(import_node_path3.default.join(MANAGED_STARTUP_SYSTEM_CA_ANCHOR_DIRECTORY,name))}}for(const[index,name]of expectedNames.entries()){atomicWriteRootFile(import_node_path3.default.join(MANAGED_STARTUP_SYSTEM_CA_ANCHOR_DIRECTORY,name),blocks[index],292)}refreshSystemCaBundle();requireSystemCaBundleContains(blocks)}function safeTrustBundle(target){try{const{bytes,stat}=readStableRegularFileSnapshot(target,MAX_TRUST_BUNDLE_BYTES);if(Number(stat.mode&0o022n)!==0){fail6(`refusing unsafe trust bundle ${target}`)}return bytes}catch(error){if(error.code==="ENOENT")return null;throw error}}function mergeCorporateCa(corporateCaPath){if(corporateCaPath===null){removeSafeRootFile(MANAGED_STARTUP_MERGED_CA_FILE);return false}const corporate=readStableRegularFile(corporateCaPath,128*1024);const candidates=["/etc/openshell-tls/ca-bundle.pem",process.env.SSL_CERT_FILE??"","/etc/ssl/certs/ca-certificates.crt"].filter((candidate,index,values)=>candidate&&candidate!==MANAGED_STARTUP_MERGED_CA_FILE&&values.indexOf(candidate)===index);let base=null;for(const candidate of candidates){base=safeTrustBundle(candidate);if(base)break}const merged=Buffer.concat([...base?[base,Buffer.from("\n","utf8")]:[],corporate,...corporate.at(-1)===10?[]:[Buffer.from("\n","utf8")]]);atomicWriteRootFile(MANAGED_STARTUP_MERGED_CA_FILE,merged,292);return true}function shellSingleQuote(value){if(value.includes("\0")||/[\r\n]/u.test(value)){fail6("runtime environment values must be single-line text")}return`'${value.replaceAll("'",`'"'"'`)}'`}function serializeManagedStartupRuntimeEnvironment(environment,corporateCaMerged,configurationEnvironment={},applicationRuntime={exportEnvironment:{},unsetEnvironment:[]}){const{output,unsetNames}=materializeManagedStartupRuntimeEnvironment(environment,corporateCaMerged,configurationEnvironment,applicationRuntime);const unsetLines=unsetNames.map(name=>`unset ${name}`);const exportLines=Object.entries(output).sort(([left],[right])=>left.localeCompare(right)).map(([name,value])=>`export ${name}=${shellSingleQuote(value)}`);return`${[...unsetLines,...exportLines].join("\n")} +`}function materializeManagedStartupRuntimeEnvironment(environment,corporateCaMerged,configurationEnvironment={},applicationRuntime={exportEnvironment:{},unsetEnvironment:[]}){const validatedApplicationRuntime=validateManagedStartupApplicationRuntimePlan(applicationRuntime);const output={...environment,...validatedApplicationRuntime.exportEnvironment,NEMOCLAW_MANAGED_STARTUP_APPLIED:"1"};if(corporateCaMerged){for(const name of MANAGED_STARTUP_TLS_ENV_NAMES){delete output[name]}output._NEMOCLAW_CORPORATE_CA_MERGED="1"}for(const name of[...Object.keys(configurationEnvironment),...Object.keys(output)]){if(!/^[A-Za-z_][A-Za-z0-9_]*$/u.test(name)){fail6(`invalid runtime environment key ${JSON.stringify(name)}`)}}const unsetNames=new Set([...Object.keys(configurationEnvironment).filter(name=>!Object.hasOwn(output,name)&&(!corporateCaMerged||!MANAGED_STARTUP_TLS_ENV_NAMES.has(name))),...validatedApplicationRuntime.unsetEnvironment.filter(name=>!corporateCaMerged||!MANAGED_STARTUP_TLS_ENV_NAMES.has(name))]);for(const name of validatedApplicationRuntime.unsetEnvironment){if(Object.hasOwn(output,name)){fail6(`runtime environment cannot both export and unset ${name}`)}}for(const name of unsetNames){if(!/^[A-Za-z_][A-Za-z0-9_]*$/u.test(name)){fail6(`invalid runtime environment key ${JSON.stringify(name)}`)}}return{output,unsetNames:[...unsetNames].sort()}}function serializeManagedStartupCompletionMarker(marker){if(marker.schemaVersion!==MANAGED_STARTUP_COMPLETION_SCHEMA_VERSION||!MANAGED_STARTUP_AGENTS.includes(marker.agent)||!SHA256_RE4.test(marker.profileFingerprint)||!SHA256_RE4.test(marker.runtimeEnvironmentSha256)||typeof marker.corporateCaMerged!=="boolean"){fail6("managed startup completion marker is invalid")}return`${JSON.stringify({agent:marker.agent,corporateCaMerged:marker.corporateCaMerged,profileFingerprint:marker.profileFingerprint,runtimeEnvironmentSha256:marker.runtimeEnvironmentSha256,schemaVersion:marker.schemaVersion})} +`}function parseManagedStartupCompletionMarker(text){let parsed;try{parsed=JSON.parse(text)}catch{fail6("managed startup completion marker is not valid JSON")}if(typeof parsed!=="object"||parsed===null||Array.isArray(parsed)){fail6("managed startup completion marker must be an object")}const record2=parsed;const expectedKeys=["agent","corporateCaMerged","profileFingerprint","runtimeEnvironmentSha256","schemaVersion"];if(Object.keys(record2).sort().join(",")!==expectedKeys.sort().join(",")||record2.schemaVersion!==MANAGED_STARTUP_COMPLETION_SCHEMA_VERSION||typeof record2.agent!=="string"||!MANAGED_STARTUP_AGENTS.includes(record2.agent)||typeof record2.profileFingerprint!=="string"||!SHA256_RE4.test(record2.profileFingerprint)||typeof record2.runtimeEnvironmentSha256!=="string"||!SHA256_RE4.test(record2.runtimeEnvironmentSha256)||typeof record2.corporateCaMerged!=="boolean"){fail6("managed startup completion marker has an invalid schema")}const marker={schemaVersion:MANAGED_STARTUP_COMPLETION_SCHEMA_VERSION,agent:record2.agent,profileFingerprint:record2.profileFingerprint,runtimeEnvironmentSha256:record2.runtimeEnvironmentSha256,corporateCaMerged:record2.corporateCaMerged};if(serializeManagedStartupCompletionMarker(marker)!==text){fail6("managed startup completion marker is not canonical")}return marker}function verifyManagedStartupImageCompletion(expectedAgentInput,expectedFingerprint,completionFile=MANAGED_STARTUP_COMPLETION_FILE,runtimeEnvironmentFile=MANAGED_STARTUP_RUNTIME_ENV_FILE){const expectedAgent=exactAgent2(expectedAgentInput);if(!SHA256_RE4.test(expectedFingerprint)){fail6("startup completion expected profile fingerprint is invalid")}const{bytes,stat}=readStableRegularFileSnapshot(completionFile,MAX_MANAGED_STARTUP_COMPLETION_BYTES);if(stat.nlink!==1n||stat.uid!==0n||stat.gid!==0n||Number(stat.mode&0o777n)!==292){fail6("managed startup completion marker must be root:root mode 0444")}const marker=parseManagedStartupCompletionMarker(bytes.toString("utf8"));if(marker.agent!==expectedAgent||marker.profileFingerprint!==expectedFingerprint){fail6("managed startup completion marker does not match the requested profile")}const runtimeEnvironment=readStableRegularFileSnapshot(runtimeEnvironmentFile,MAX_MANAGED_STARTUP_RUNTIME_ENVIRONMENT_BYTES);if(runtimeEnvironment.stat.nlink!==1n||runtimeEnvironment.stat.uid!==0n||runtimeEnvironment.stat.gid!==0n||Number(runtimeEnvironment.stat.mode&0o777n)!==292){fail6("managed startup runtime environment must be root:root mode 0444")}const runtimeEnvironmentSha256=(0,import_node_crypto6.createHash)("sha256").update(runtimeEnvironment.bytes).digest("hex");if(runtimeEnvironmentSha256!==marker.runtimeEnvironmentSha256){fail6("managed startup completion marker runtime environment digest mismatch")}return{agent:expectedAgent,fingerprint:expectedFingerprint}}function waitForManagedStartupImageCompletion(expectedAgentInput,expectedFingerprint,timeoutSeconds=600){if(!Number.isSafeInteger(timeoutSeconds)||timeoutSeconds<1||timeoutSeconds>3600){fail6("startup completion wait timeout must be an integer from 1 to 3600 seconds")}const deadline=Date.now()+timeoutSeconds*1e3;while(true){try{return verifyManagedStartupImageCompletion(expectedAgentInput,expectedFingerprint)}catch(error){if(error.code!=="ENOENT")throw error;if(Date.now()>=deadline){fail6(`startup completion was not published within ${String(timeoutSeconds)} seconds`)}Atomics.wait(new Int32Array(new SharedArrayBuffer(4)),0,0,250)}}}function applyAdapter(context,mapped){if(mapped.agent!==context.agent){fail6(`mapped ${mapped.agent} environment for ${context.agent}`)}const commandPlan=buildManagedStartupImageActionPlan({agent:mapped.agent,actions:mapped.actions});let commandIndex=0;for(const action of mapped.actions){if(action.kind==="configure-dashboard")continue;const command=commandPlan[commandIndex];if(!command)fail6(`missing image command for ${action.kind}`);commandIndex+=1;if(action.kind==="apply-messaging-plan"){if(action.phase==="runtime-setup"){prepareMessagingRuntimeTarget(action.mode)}execute(command.argv,command.runAs,mapped.configurationEnvironment,mapped.applicationRuntime);if(action.phase==="runtime-setup"){verifyMessagingRuntimeTarget(action.mode)}continue}execute(command.argv,command.runAs,mapped.configurationEnvironment,mapped.applicationRuntime)}if(commandIndex!==commandPlan.length){fail6("image action plan contains an unmatched command")}switch(context.agent){case"openclaw":sealOpenClawConfiguration(mapped.configurationEnvironment,mapped.applicationRuntime);break;case"hermes":if(context.profile.agentConfig.agent!=="hermes"){fail6("Hermes profile has inconsistent agent configuration")}installHermesManagedPolicy();installHermesRelayPluginsConfiguration(context.profile.agentConfig.switchyardRouting);sealHermesConfiguration(mapped.configurationEnvironment,mapped.applicationRuntime);normalizeHermesManagedConfiguration();break;case"langchain-deepagents-code":break}installRootOwnedMaterials(mapped.materials);installCorporateCa(context.corporateCaPath);installCorporateCaSystemAnchors(context.corporateCaPath);mergeCorporateCa(context.corporateCaPath)}function adapters(mapped){return MANAGED_STARTUP_AGENTS.map(agent=>({agent,apply:context=>applyAdapter(context,mapped)}))}async function applyManagedStartupImageProfile(expectedAgentInput,env=process.env){requireRoot();const expectedAgent=exactAgent2(expectedAgentInput);if(env.NEMOCLAW_MANAGED_IMAGE_CAPABILITY_UNION!=="1"){fail6("startup profiles require a complete managed image")}const encodedProfile=env[MANAGED_STARTUP_PROFILE_ENV];if(!encodedProfile)fail6(`${MANAGED_STARTUP_PROFILE_ENV} is required`);let profile;try{profile=decodeManagedStartupProfile(encodedProfile)}catch(error){fail6(error.message)}if(profile.agent!==expectedAgent){fail6(`managed startup profile targets ${profile.agent}, expected ${expectedAgent}`)}const mapped=mapManagedStartupProfileToAgentEnvironment(profile,env);validateManagedStartupApplicationRuntimePlan(mapped.applicationRuntime);ensureRootOwnedDirectory(ROOT_STATE_PARENT);ensureRootOwnedDirectory(ROOT_RUNTIME_DIRECTORY);const result=await coordinateManagedStartupApplication({encodedProfile,expectedAgent,...env[MANAGED_STARTUP_CA_ENV]===void 0?{}:{corporateCaB64:env[MANAGED_STARTUP_CA_ENV]}},adapters(mapped));if(mapped.agent!==result.application.profile.agent){fail6(`mapped ${mapped.agent} environment for ${result.application.profile.agent}`)}if(expectedAgent==="hermes"&&!result.adapterApplied){normalizeHermesManagedConfiguration();if(profile.agentConfig.agent!=="hermes"){fail6("committed Hermes profile has inconsistent agent configuration")}verifyHermesRelayPluginsConfiguration(profile.agentConfig.switchyardRouting)}let corporateCaMerged;if(result.adapterApplied){corporateCaMerged=result.application.corporateCaPath!==null}else{verifyRootOwnedMaterials(mapped.materials);if(result.application.corporateCaPath===null){if(import_node_fs3.default.existsSync(MANAGED_STARTUP_CORPORATE_CA_FILE)){fail6("committed profile without a corporate CA has a stale CA material")}}else{const expected=readStableRegularFile(result.application.corporateCaPath,128*1024);const installed=readStableRegularFile(MANAGED_STARTUP_CORPORATE_CA_FILE,128*1024);if(!expected.equals(installed)){fail6("committed corporate CA material drifted")}}installCorporateCaSystemAnchors(result.application.corporateCaPath);corporateCaMerged=mergeCorporateCa(result.application.corporateCaPath)}const runtimeEnvironment=serializeManagedStartupRuntimeEnvironment(mapped.runtimeEnvironment,corporateCaMerged,mapped.configurationEnvironment,mapped.applicationRuntime);atomicWriteRootFile(MANAGED_STARTUP_RUNTIME_ENV_FILE,runtimeEnvironment,292);atomicWriteRootFile(MANAGED_STARTUP_COMPLETION_FILE,serializeManagedStartupCompletionMarker({schemaVersion:MANAGED_STARTUP_COMPLETION_SCHEMA_VERSION,agent:expectedAgent,profileFingerprint:result.application.fingerprint,runtimeEnvironmentSha256:(0,import_node_crypto6.createHash)("sha256").update(runtimeEnvironment,"utf8").digest("hex"),corporateCaMerged}),292);return{agent:expectedAgent,adapterApplied:result.adapterApplied,fingerprint:result.application.fingerprint,runtimeEnvironmentFile:MANAGED_STARTUP_RUNTIME_ENV_FILE}}function completionAlreadyPublished(request){try{verifyManagedStartupImageCompletion(request.agent,request.profileFingerprint);return true}catch(error){if(error.code==="ENOENT")return false;throw error}}async function applyManagedStartupRootRequest(request,env=process.env,options={}){requireRoot();const profile=decodeManagedStartupProfile(request.encodedProfile);if(profile.agent!==request.agent||fingerprintManagedStartupProfile(profile)!==request.profileFingerprint){fail6("root application request identity does not match its profile")}const imageEnvironment={HOME:"/root",PATH:FIXED_PATH,NEMOCLAW_MANAGED_IMAGE_CAPABILITY_UNION:"1",...selectManagedStartupApplicationRuntimeEnvironment(env),[MANAGED_STARTUP_PROFILE_ENV]:request.encodedProfile,...request.corporateCaB64===null?{}:{[MANAGED_STARTUP_CA_ENV]:request.corporateCaB64}};mapManagedStartupProfileToAgentEnvironment(profile,imageEnvironment);const alreadyPublished=completionAlreadyPublished(request);const bootstrapIdentity=options.bootstrapIdentity??null;const transactionStatus=alreadyPublished&&bootstrapIdentity!==null?getManagedStartupSharedStateTransactionStatus({agent:request.agent,profileFingerprint:request.profileFingerprint,bootstrapIdentity}):null;if(transactionStatus==="none"){fail6("completed startup profile has no shared-state authority for this bootstrap attempt")}if(!alreadyPublished){ensureRootOwnedDirectory(ROOT_STATE_PARENT);beginManagedStartupSharedStateTransaction(profile,{bootstrapIdentity})}const result=await applyManagedStartupImageProfile(request.agent,imageEnvironment);return{...result,transactionPending:!alreadyPublished||transactionStatus==="pending"}}function readBoundedRootApplyStdin(){const chunks=[];let total=0;while(true){const chunk=Buffer.alloc(16*1024);const read=import_node_fs3.default.readSync(0,chunk,0,chunk.length,null);if(read===0)break;total+=read;if(total>MANAGED_STARTUP_ROOT_APPLY_MAX_BYTES){fail6("root application stdin exceeds its bounded transport")}chunks.push(chunk.subarray(0,read))}const bytes=Buffer.concat(chunks,total);const text=bytes.toString("utf8");if(text.includes("\0")||!Buffer.from(text,"utf8").equals(bytes)){fail6("root application stdin must be valid UTF-8 without NUL bytes")}return text}function writeSandboxFileAtomically(target,contents,mode){const parent=import_node_path3.default.dirname(target);const parentStat=import_node_fs3.default.lstatSync(parent);if(parentStat.isSymbolicLink()||!parentStat.isDirectory()||parentStat.uid!==process.geteuid?.()||parentStat.gid!==process.getegid?.()){fail6(`refusing unsafe sandbox-owned directory ${parent}`)}const temporary=import_node_path3.default.join(parent,`.${import_node_path3.default.basename(target)}.${(0,import_node_crypto6.randomBytes)(12).toString("hex")}`);let descriptor;try{descriptor=import_node_fs3.default.openSync(temporary,import_node_fs3.default.constants.O_CREAT|import_node_fs3.default.constants.O_EXCL|import_node_fs3.default.constants.O_WRONLY|import_node_fs3.default.constants.O_NOFOLLOW,384);import_node_fs3.default.writeFileSync(descriptor,contents);import_node_fs3.default.fchmodSync(descriptor,mode);import_node_fs3.default.fsyncSync(descriptor);import_node_fs3.default.closeSync(descriptor);descriptor=void 0;import_node_fs3.default.renameSync(temporary,target)}catch(error){if(descriptor!==void 0)import_node_fs3.default.closeSync(descriptor);try{import_node_fs3.default.unlinkSync(temporary)}catch{}fail6(`could not write sandbox-owned file ${target}: ${error.message}`)}}function internalWriteOpenClawHash(){if(process.geteuid?.()===0)fail6("sandbox hash writer must not run as root");const configPath="/sandbox/.openclaw/openclaw.json";const config=readStableRegularFile(configPath,16*1024*1024);const text=`${(0,import_node_crypto6.createHash)("sha256").update(config).digest("hex")} openclaw.json +`;writeSandboxFileAtomically("/sandbox/.openclaw/.config-hash",text,432)}function internalWriteHermesCompatHash(){if(process.geteuid?.()===0)fail6("sandbox hash writer must not run as root");const encoded=process.env.NEMOCLAW_MANAGED_HERMES_HASH_B64??"";if(encoded.length===0||encoded.length>4096||!/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/u.test(encoded)){fail6("Hermes compatibility hash transport is invalid")}const decoded=Buffer.from(encoded,"base64");if(decoded.toString("base64")!==encoded){fail6("Hermes compatibility hash transport is non-canonical")}writeSandboxFileAtomically("/sandbox/.hermes/.config-hash",decoded.toString("utf8"),416)}function readCliAgent(argv,expectedLength=2){const index=argv.indexOf("--agent");if(index<0||index+1>=argv.length||argv.length!==expectedLength){fail6("usage: managed-startup-image-runtime [--apply-root-stdin|--wait-for-completion|--verify-completion|--begin-shared-state-transaction|--commit-shared-state-transaction|--clear-shared-state-commit-receipt|--shared-state-transaction-status] --agent ")}return argv[index+1]}function readCliFingerprint(argv){const index=argv.indexOf("--profile-fingerprint");if(index<0||index+1>=argv.length){fail6("managed startup profile fingerprint argument is missing")}return argv[index+1]}function readCliBootstrapIdentity(argv){const index=argv.indexOf("--bootstrap-identity");if(index<0||index+1>=argv.length||!SHA256_RE4.test(String(argv[index+1]??""))){fail6("managed bootstrap identity argument is missing or invalid")}return argv[index+1]}async function main(argv=process.argv.slice(2)){if(argv.length===1&&argv[0]==="--internal-write-openclaw-hash"){internalWriteOpenClawHash();return}if(argv.length===1&&argv[0]==="--internal-write-hermes-compat-hash"){internalWriteHermesCompatHash();return}if(argv.length===3&&argv[0]==="--apply-root-stdin"){const expectedAgent=exactAgent2(readCliAgent(argv,3));const request=parseManagedStartupRootApplyRequest(readBoundedRootApplyStdin());if(request.agent!==expectedAgent){fail6(`root application request targets ${request.agent}, expected ${expectedAgent}`)}const result2=await applyManagedStartupRootRequest(request);console.log(result2.transactionPending?`[managed-startup] applied ${result2.agent} profile ${result2.fingerprint}; transaction pending`:`[managed-startup] ${result2.agent} profile ${result2.fingerprint} was already complete`);return}if(argv.length===5&&(argv[0]==="--verify-completion"||argv[0]==="--wait-for-completion")){const agent=readCliAgent(argv,5);const fingerprint=readCliFingerprint(argv);const result2=argv[0]==="--wait-for-completion"?waitForManagedStartupImageCompletion(agent,fingerprint):verifyManagedStartupImageCompletion(agent,fingerprint);console.log(`[managed-startup] verified ${result2.agent} profile ${result2.fingerprint} completion`);return}if(argv.length===3&&argv[0]==="--begin-shared-state-transaction"){const profile=managedTransactionProfile(readCliAgent(argv,3));ensureRootOwnedDirectory(ROOT_STATE_PARENT);const created=beginManagedStartupSharedStateTransaction(profile);process.stdout.write(created?"created\n":"pending\n");return}if((argv.length===4||argv.length===6)&&argv[0]==="--rollback-shared-state-transaction"&&argv[argv.length-1]==="--read-only-receipt"){requireRoot();const agent=exactAgent2(readCliAgent(argv,argv.length));const rolledBack=rollbackManagedStartupSharedStateTransaction(agent,{transactionDirectory:MANAGED_STARTUP_SHARED_ROLLBACK_RECEIPT_DIRECTORY,readOnlyReceipt:true,bootstrapIdentity:argv.length===6?readCliBootstrapIdentity(argv):null});if(!rolledBack)fail6("read-only shared-state rollback receipt is missing");console.log(`[managed-startup] verified and restored ${agent} shared state`);return}if((argv.length===3||argv.length===5)&&argv[0]==="--commit-shared-state-transaction"){requireRoot();const agent=exactAgent2(readCliAgent(argv,argv.length));if(!commitManagedStartupSharedStateTransaction(agent,{bootstrapIdentity:argv.length===5?readCliBootstrapIdentity(argv):null})){fail6("managed startup transaction is missing at commit")}console.log(`[managed-startup] committed ${agent} shared state`);return}if(argv.length===5&&argv[0]==="--clear-shared-state-commit-receipt"){requireRoot();const agent=exactAgent2(readCliAgent(argv,5));const bootstrapIdentity=readCliBootstrapIdentity(argv);if(!clearManagedStartupSharedStateCommitReceipt(agent,{bootstrapIdentity})){fail6("managed startup durable commit receipt is missing at cleanup")}console.log(`[managed-startup] cleared ${agent} durable shared-state commit receipt`);return}if(argv.length===8&&argv[0]==="--shared-state-transaction-status"&&argv[7]==="--read-only-receipt"){requireRoot();const agent=exactAgent2(readCliAgent(argv,8));const profileFingerprint=readCliFingerprint(argv);const bootstrapIdentity=readCliBootstrapIdentity(argv);process.stdout.write(`${getManagedStartupSharedStateTransactionStatus({agent,profileFingerprint,bootstrapIdentity},{readOnlyReceipt:true})} +`);return}const result=await applyManagedStartupImageProfile(readCliAgent(argv));console.log(result.adapterApplied?`[managed-startup] applied ${result.agent} profile ${result.fingerprint}`:`[managed-startup] ${result.agent} profile ${result.fingerprint} is already committed`)}var MANAGED_BOOTSTRAP_ENVELOPE_SCHEMA_VERSION=1;var MANAGED_BOOTSTRAP_REQUEST_FILE="/var/lib/nemoclaw-managed-bootstrap-request.json";var MANAGED_BOOTSTRAP_REQUEST_TAR_PATH=MANAGED_BOOTSTRAP_REQUEST_FILE.replace(/^\/+/,"");var MANAGED_BOOTSTRAP_COMPLETION_FILE="/run/nemoclaw/managed-bootstrap-completion.json";var MANAGED_BOOTSTRAP_ENVELOPE_MAX_BYTES=Math.ceil(MANAGED_STARTUP_ROOT_APPLY_MAX_BYTES/3)*4+1024;var MANAGED_BOOTSTRAP_COMPLETION_MAX_BYTES=1024;var BOOTSTRAP_IDENTITY_RE=/^[a-f0-9]{64}$/u;var STANDARD_BASE64_RE2=/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/u;function fail7(message){throw new Error(`Managed bootstrap envelope is invalid: ${message}`)}function serializeManagedBootstrapEnvelope(input){if(!BOOTSTRAP_IDENTITY_RE.test(input.bootstrapIdentity)){fail7("bootstrap identity must be 32 random bytes encoded as lowercase hex")}const request=Buffer.from(serializeManagedStartupRootApplyRequest(input.rootApplyRequest),"utf8").toString("base64");const serialized=`${JSON.stringify({bootstrapIdentity:input.bootstrapIdentity,rootApplyRequestB64:request,schemaVersion:MANAGED_BOOTSTRAP_ENVELOPE_SCHEMA_VERSION})} +`;if(Buffer.byteLength(serialized,"utf8")>MANAGED_BOOTSTRAP_ENVELOPE_MAX_BYTES){fail7("serialized envelope exceeds its bounded transport")}return serialized}function parseManagedBootstrapEnvelope(text){if(text.includes("\0"))fail7("serialized envelope contains NUL");if(text.length===0||Buffer.byteLength(text,"utf8")>MANAGED_BOOTSTRAP_ENVELOPE_MAX_BYTES){fail7("serialized envelope is empty or too large")}let parsed;try{parsed=JSON.parse(text)}catch{fail7("serialized envelope is not valid JSON")}if(typeof parsed!=="object"||parsed===null||Array.isArray(parsed)){fail7("serialized envelope must be an object")}const record2=parsed;if(Object.keys(record2).sort().join(",")!==["bootstrapIdentity","rootApplyRequestB64","schemaVersion"].sort().join(",")||record2.schemaVersion!==MANAGED_BOOTSTRAP_ENVELOPE_SCHEMA_VERSION||typeof record2.bootstrapIdentity!=="string"||!BOOTSTRAP_IDENTITY_RE.test(record2.bootstrapIdentity)||typeof record2.rootApplyRequestB64!=="string"||!STANDARD_BASE64_RE2.test(record2.rootApplyRequestB64)){fail7("serialized envelope has an invalid schema")}const requestBytes=Buffer.from(record2.rootApplyRequestB64,"base64");if(requestBytes.toString("base64")!==record2.rootApplyRequestB64){fail7("root application request transport is non-canonical")}const rootApplyRequest=parseManagedStartupRootApplyRequest(requestBytes.toString("utf8"));const envelope=Object.freeze({schemaVersion:MANAGED_BOOTSTRAP_ENVELOPE_SCHEMA_VERSION,bootstrapIdentity:record2.bootstrapIdentity,rootApplyRequest});if(serializeManagedBootstrapEnvelope(envelope)!==text){fail7("serialized envelope is not canonical")}return envelope}function serializeManagedBootstrapImageCompletion(completion){if(!BOOTSTRAP_IDENTITY_RE.test(completion.bootstrapIdentity)||!BOOTSTRAP_IDENTITY_RE.test(completion.profileFingerprint)){fail7("image completion identity is invalid")}if(!["openclaw","hermes","langchain-deepagents-code"].includes(completion.agent)){fail7("image completion agent is invalid")}if(typeof completion.transactionPending!=="boolean"){fail7("image completion transaction state is invalid")}return`${JSON.stringify({agent:completion.agent,bootstrapIdentity:completion.bootstrapIdentity,profileFingerprint:completion.profileFingerprint,schemaVersion:MANAGED_BOOTSTRAP_ENVELOPE_SCHEMA_VERSION,transactionPending:completion.transactionPending})} +`}function parseManagedBootstrapImageCompletion(text){if(text.includes("\0"))fail7("image completion contains NUL");if(text.length===0||Buffer.byteLength(text,"utf8")>MANAGED_BOOTSTRAP_COMPLETION_MAX_BYTES){fail7("image completion is empty or too large")}let parsed;try{parsed=JSON.parse(text)}catch{fail7("image completion is not valid JSON")}if(typeof parsed!=="object"||parsed===null||Array.isArray(parsed)){fail7("image completion must be an object")}const completion=parsed;if(Object.keys(completion).sort().join(",")!==["agent","bootstrapIdentity","profileFingerprint","schemaVersion","transactionPending"].sort().join(",")||completion.schemaVersion!==MANAGED_BOOTSTRAP_ENVELOPE_SCHEMA_VERSION||typeof completion.agent!=="string"||!["openclaw","hermes","langchain-deepagents-code"].includes(completion.agent)||typeof completion.bootstrapIdentity!=="string"||!BOOTSTRAP_IDENTITY_RE.test(completion.bootstrapIdentity)||typeof completion.profileFingerprint!=="string"||!BOOTSTRAP_IDENTITY_RE.test(completion.profileFingerprint)||typeof completion.transactionPending!=="boolean"){fail7("image completion schema is invalid")}const normalized=Object.freeze({schemaVersion:MANAGED_BOOTSTRAP_ENVELOPE_SCHEMA_VERSION,bootstrapIdentity:completion.bootstrapIdentity,agent:completion.agent,profileFingerprint:completion.profileFingerprint,transactionPending:completion.transactionPending});if(serializeManagedBootstrapImageCompletion(normalized)!==text){fail7("image completion is not canonical")}return normalized}var SHA256_RE5=/^[a-f0-9]{64}$/u;function fail8(message){throw new ManagedStartupImageRuntimeError(message)}function requireRoot2(){if(process.geteuid?.()!==0){fail8("managed bootstrap image runtime requires container effective uid 0")}}function exactAgent3(value){if(!MANAGED_STARTUP_AGENTS.includes(value)){fail8(`unsupported agent ${JSON.stringify(value)}`)}return value}function readExpected(argv){if(argv.length!==7){fail8("usage: managed-startup-image-runtime [--recover-bootstrap-claim|--apply-bootstrap-file|--verify-bootstrap-completion|--wait-for-completion] --agent --profile-fingerprint --bootstrap-identity ")}const valueAfter=flag=>{const index=argv.indexOf(flag);if(index<0||index+1>=argv.length)fail8(`managed bootstrap ${flag} argument is missing`);return argv[index+1]};const profileFingerprint=valueAfter("--profile-fingerprint");const bootstrapIdentity=valueAfter("--bootstrap-identity");if(!SHA256_RE5.test(profileFingerprint)||!SHA256_RE5.test(bootstrapIdentity)){fail8("managed bootstrap image runtime identities must encode 32 lowercase-hex bytes")}return{agent:exactAgent3(valueAfter("--agent")),profileFingerprint,bootstrapIdentity}}function readProtectedManagedBootstrapEnvelopeSnapshot(requestFile=MANAGED_BOOTSTRAP_REQUEST_FILE){requireRoot2();const{bytes,stat}=readStableRegularFileSnapshot(requestFile,MANAGED_BOOTSTRAP_ENVELOPE_MAX_BYTES);if(!isProtectedManagedBootstrapFile(stat)){fail8("managed bootstrap envelope must be root:root mode 0400 with one link")}const envelope=parseManagedBootstrapEnvelope(bytes.toString("utf8"));return{bootstrapIdentity:envelope.bootstrapIdentity,bytes,request:envelope.rootApplyRequest,stat}}function managedBootstrapEnvelopeMatchesExpected(snapshot,expected){return snapshot.bootstrapIdentity===expected.bootstrapIdentity&&snapshot.request.agent===expected.agent&&snapshot.request.profileFingerprint===expected.profileFingerprint}function readManagedBootstrapEnvelopeSnapshot(expected,requestFile=MANAGED_BOOTSTRAP_REQUEST_FILE){const snapshot=readProtectedManagedBootstrapEnvelopeSnapshot(requestFile);if(!managedBootstrapEnvelopeMatchesExpected(snapshot,expected)){fail8("managed bootstrap envelope identity does not match the replacement")}return snapshot}function readManagedBootstrapEnvelope(expected,requestFile=MANAGED_BOOTSTRAP_REQUEST_FILE){return readManagedBootstrapEnvelopeSnapshot(expected,requestFile).request}function managedBootstrapEnvelopeClaimPaths(requestFile=MANAGED_BOOTSTRAP_REQUEST_FILE){if(!import_node_path4.default.isAbsolute(requestFile))fail8("managed bootstrap request path must be absolute");const directory=import_node_path4.default.join(import_node_path4.default.dirname(requestFile),`.${import_node_path4.default.basename(requestFile)}.nemoclaw-claim`);return{directory,file:import_node_path4.default.join(directory,"request"),requestFile}}function lstatManagedBootstrapPath(target){try{return import_node_fs4.default.lstatSync(target,{bigint:true})}catch(error){if(error.code==="ENOENT")return null;fail8(`could not inspect managed bootstrap path ${target}`)}}function sameStableManagedBootstrapFile(left,right){return sameClaimedManagedBootstrapFile(left,right)&&left.ctimeNs===right.ctimeNs}function sameClaimedManagedBootstrapFile(left,right){return left.dev===right.dev&&left.ino===right.ino&&left.mode===right.mode&&left.nlink===right.nlink&&left.uid===right.uid&&left.gid===right.gid&&left.size===right.size&&left.mtimeNs===right.mtimeNs}function isProtectedManagedBootstrapFile(stat,expectedLinks=1n){return stat.isFile()&&stat.nlink===expectedLinks&&stat.uid===0n&&stat.gid===0n&&Number(stat.mode&0o777n)===256}function requirePrivateManagedBootstrapClaimDirectory(directory){const stat=lstatManagedBootstrapPath(directory);if(stat===null||!stat.isDirectory()||stat.isSymbolicLink()||stat.uid!==0n||stat.gid!==0n||Number(stat.mode&0o777n)!==448){fail8("managed bootstrap claim directory must be root:root mode 0700")}}function removeManagedBootstrapClaimDirectory(directory){try{import_node_fs4.default.rmdirSync(directory)}catch{fail8("could not remove managed bootstrap claim directory")}}function requireSafeManagedBootstrapClaimParent(directory){const parent=lstatManagedBootstrapPath(import_node_path4.default.dirname(directory));if(parent===null||!parent.isDirectory()||parent.isSymbolicLink()||parent.uid!==0n||parent.gid!==0n||Number(parent.mode&0o022n)!==0){fail8("managed bootstrap claim parent must be a protected root-owned directory")}}function managedBootstrapClaimEntries(directory){let entries;try{entries=import_node_fs4.default.readdirSync(directory)}catch{fail8("could not inspect managed bootstrap claim directory contents")}return entries.sort()}function ensurePrivateManagedBootstrapClaimDirectory(directory){requireSafeManagedBootstrapClaimParent(directory);const current=lstatManagedBootstrapPath(directory);if(current!==null){requirePrivateManagedBootstrapClaimDirectory(directory);return}try{import_node_fs4.default.mkdirSync(directory,{mode:448});import_node_fs4.default.chownSync(directory,0,0);import_node_fs4.default.chmodSync(directory,448)}catch(error){if(error.code!=="EEXIST"){fail8("could not create private managed bootstrap envelope claim")}}requirePrivateManagedBootstrapClaimDirectory(directory)}function openManagedBootstrapEnvelopeSnapshot(expected,target){requireRoot2();if(typeof import_node_fs4.default.constants.O_NOFOLLOW!=="number"){fail8("O_NOFOLLOW is unavailable for managed bootstrap envelope reads")}const nonblock=typeof import_node_fs4.default.constants.O_NONBLOCK==="number"?import_node_fs4.default.constants.O_NONBLOCK:0;let descriptor;try{descriptor=import_node_fs4.default.openSync(target,import_node_fs4.default.constants.O_RDONLY|import_node_fs4.default.constants.O_NOFOLLOW|nonblock)}catch{fail8(`refusing unsafe or unreadable managed bootstrap envelope ${target}`)}try{const before=import_node_fs4.default.fstatSync(descriptor,{bigint:true});if(!isProtectedManagedBootstrapFile(before)||before.size<1n||before.size>BigInt(MANAGED_BOOTSTRAP_ENVELOPE_MAX_BYTES)){fail8("managed bootstrap envelope must be a bounded root:root mode 0400 file with one link")}const bytes=Buffer.alloc(Number(before.size));let offset=0;while(offset3600){fail8("managed bootstrap completion wait timeout must be an integer from 1 to 3600 seconds")}const deadline=Date.now()+timeoutSeconds*1e3;while(true){try{return verifyManagedBootstrapImageCompletion(expected,completionFile,startupCompletionFile,runtimeEnvironmentFile)}catch(error){if(error.code!=="ENOENT")throw error;if(Date.now()>=deadline){fail8(`managed bootstrap completion was not published within ${String(timeoutSeconds)} seconds`)}Atomics.wait(new Int32Array(new SharedArrayBuffer(4)),0,0,250)}}}async function main2(argv=process.argv.slice(2)){if(argv[0]==="--recover-bootstrap-claim"){const expected=readExpected(argv);const pending=recoverManagedBootstrapEnvelopeClaim(expected);console.log(pending?`[managed-startup] found pending ${expected.agent} bootstrap request claim`:`[managed-startup] no pending ${expected.agent} bootstrap request claim`);return}if(argv[0]==="--apply-bootstrap-file"){const expected=readExpected(argv);const result=await applyManagedBootstrapEnvelope(expected);console.log(result.transactionPending?`[managed-startup] applied ${result.agent} profile ${result.fingerprint}; transaction pending`:`[managed-startup] ${result.agent} profile ${result.fingerprint} was already complete`);return}if(argv[0]==="--verify-bootstrap-completion"){const expected=readExpected(argv);const completion=verifyManagedBootstrapImageCompletion(expected);console.log(`[managed-startup] verified ${expected.agent} profile ${expected.profileFingerprint} bootstrap ${expected.bootstrapIdentity}${completion.transactionPending?"; transaction pending":""}`);return}if(argv[0]==="--wait-for-completion"){const expected=readExpected(argv);const completion=waitForManagedBootstrapImageCompletion(expected);console.log(`[managed-startup] verified ${expected.agent} profile ${expected.profileFingerprint} bootstrap ${expected.bootstrapIdentity}${completion.transactionPending?"; transaction pending":""}`);return}await main(argv)}if(typeof require!=="undefined"&&typeof module!=="undefined"&&require.main===module){main2().catch(error=>{console.error(error instanceof Error?error.message:String(error));process.exitCode=1})}0&&(module.exports={applyManagedBootstrapEnvelope,main,managedBootstrapEnvelopeClaimPaths,readManagedBootstrapEnvelope,recoverManagedBootstrapEnvelopeClaim,verifyManagedBootstrapImageCompletion,waitForManagedBootstrapImageCompletion}); diff --git a/tsconfig.src.json b/tsconfig.src.json index 82552a7cb46..d29fdeff2d0 100644 --- a/tsconfig.src.json +++ b/tsconfig.src.json @@ -9,6 +9,7 @@ "skipLibCheck": true, "forceConsistentCasingInFileNames": true, "resolveJsonModule": true, + "rewriteRelativeImportExtensions": true, "declaration": true, "declarationMap": true, "sourceMap": true,