Source code for scitex_agent_container.config

"""YAML config loading and validation for agent definitions.

Public API:
    AgentConfig, load_config, validate_config, resolve_config
    ContainerSpec, ClaudeSpec, HealthSpec, WatchdogSpec, RestartSpec,
    SkillsSpec, StartupCommand

``RemoteSpec`` and the ``spec.remote`` field were deleted in WI-6
(handoff §6, 2026-05-20). Cross-host placement is via ``spec.host``;
the old SSH-dispatch path is retired.
"""

from __future__ import annotations

from pathlib import Path

import yaml

from ._host import resolve_hostname, substitute_hostnames
from ._loaders import compose_effective_name, load_v3
from ._provider_types import ProviderSpec
from ._proxy_types import ProxySpec
from ._resolve import resolve_config
from ._types import (
    AgentConfig,
    ClaudeSpec,
    ContainerSpec,
    ContextManagementConfig,
    HealthSpec,
    HookSpec,
    HostsSpec,
    ListenPort,
    RestartSpec,
    SchedulingSpec,
    SkillsSpec,
    StartupCommand,
    WatchdogSpec,
)
from ._validation import validate_config, validate_raw

__all__ = [
    "AgentConfig",
    "ClaudeSpec",
    "ContainerSpec",
    "ContextManagementConfig",
    "HealthSpec",
    "HookSpec",
    "HostsSpec",
    "ListenPort",
    "ProviderSpec",
    "ProxySpec",
    "RestartSpec",
    "SchedulingSpec",
    "SkillsSpec",
    "StartupCommand",
    "WatchdogSpec",
    "compose_effective_name",
    "load_config",
    "resolve_config",
    "resolve_hostname",
    "substitute_hostnames",
    "validate_config",
]


[docs] def load_config(path: str | Path) -> AgentConfig: """Load and validate a YAML config, returning an AgentConfig. Only ``scitex-agent-container/v3`` is accepted. Older apiVersions (v1, v2) raise loud validation errors — no backward compatibility. """ path = Path(path).resolve() with open(path) as f: raw = yaml.safe_load(f) errors = validate_raw(raw, str(path)) if errors: raise ValueError( f"Config validation failed for {path}:\n" + "\n".join(f" - {e}" for e in errors) ) config = load_v3(raw, path) _warn_if_assigned_account_missing(config) return config
def _warn_if_assigned_account_missing(config: AgentConfig) -> None: """Soft-WARN (never fail) when ``spec.claude.account`` names an account whose snapshot dir is absent at load time. Accounts may be created later or live on another host, so a missing snapshot is not a hard error — but surfacing it at load time catches typos before the agent silently falls back to the host live file at start. Best-effort: any resolution hiccup is swallowed. """ acct = getattr(getattr(config, "claude", None), "account", "") or "" if not acct: return # stx-allow: fallback (reason: store-path resolution is best-effort # advisory only; a hiccup must never break config loading.) try: import warnings from .._state.account_store import _store_path store = _store_path(None, Path.home()) if not (store / acct).is_dir(): warnings.warn( f"spec.claude.account='{acct}' has no saved-account " f"snapshot at {store / acct}; the agent will fall back " "to the host live ~/.claude/.credentials.json at start. " "Create it with `sac account save {acct}` (on the host " "that holds those credentials), or ignore if the account " "is provisioned on the target host.", stacklevel=2, ) except Exception: # stx-allow: fallback (reason: see inline comment) pass