Skip to content

Build

build

Builds L0 (base dev) and L1 (agent CLI) container images via podman.

Owns the L0 (base dev) and L1 (agent CLI) Dockerfile templates, resource staging, image naming, and podman build invocation.

Image layer architecture::

L0  (base)   — Distro + dev tools + init script + dev user
L1  (agent)  — All AI agent CLIs, shell environment, ACP wrappers
               L1 is self-sufficient for standalone use — all user
               config (repo URL, SSH, branch, gate) is runtime.
─── boundary: above owned by terok-executor, below by terok ───
L2  (project)— Optional: user Dockerfile snippet (custom packages)
               Only built when project has docker snippet config.

terok-executor run claude . launches directly on the L1 image — no L2 build needed. terok adds L2 only for project-specific image customisation.

Usage as a library::

from terok_executor import build_base_images

images = build_base_images("fedora:44")
# images.l0 = "terok-l0:fedora-44"
# images.l1 = "terok-l1-cli:fedora-44"

The L0/L1 templates select between Debian/Ubuntu (apt) and Fedora-like (dnf) package managers via a family Jinja2 variable resolved by detect_family from the base image name (or an explicit override).

L1 is roster-driven: each agent's install steps live in its YAML file (install.run_as_root / install.run_as_dev), and the L1 template loops over the resolved selection. Build emits an OCI label ai.terok.agents=<csv>, an in-container manifest /etc/terok/installed.env, pre-rendered hilfe dev-tool help fragments, and a baked per-agent facts map (agent-protocols.json — wire protocol and banner label) that the in-container readiness check and the hilfe agent banner consume — all derived from the same selection.

DEFAULT_BASE_IMAGE = 'fedora:44' module-attribute

Default base OS image when none is specified.

AGENTS_LABEL = 'ai.terok.agents' module-attribute

OCI label naming the roster entries baked into an L1 image.

INSTALLED_ENV_PATH = '/etc/terok/installed.env' module-attribute

In-container env file that scripts source to learn what's installed.

AGENT_PROTOCOLS_PATH = '/usr/local/share/terok/agent-protocols.json' module-attribute

In-container map of each installed agent to the roster facts the readiness check and the hilfe banner need.

Each entry carries the agent's wire protocol (which authenticated providers it can talk to — null for entries with no fixed protocol) and its banner label. These are the pieces of roster knowledge the container environment does not already carry. Usability turns on the protocol alone — any authenticated provider serving it works — so no provider name is recorded. Baked at L1 build time and read back by the in-container manifest generator (terok-agents).

The filename is historical (it once held only protocols); the contents are now a small per-agent record. Producer (stage_agent_facts) and consumer ship in the same wheel, baked into and read from the same image, so the shape is free to evolve in lockstep.

PROVIDER_PROTOCOLS_PATH = '/usr/local/share/terok/provider-protocols.json' module-attribute

In-container map of each wire protocol to the providers that serve it.

The complement of AGENT_PROTOCOLS_PATH: agents say which protocol they speak, this says which providers speak it. Lets hilfe list, under the providers section, the candidate providers a user could authenticate to enable a dimmed agent — including providers not yet authenticated, which the container environment does not carry. Baked at L1 build time by stage_provider_protocols.

BuildError

Bases: RuntimeError

Raised when base-image construction cannot complete.

The CLI maps this to a user-facing error message; library callers can catch it without being terminated by SystemExit.

ImageSet(l0, l1, l1_sidecar=None) dataclass

L0 + L1 image tags produced by a build.

l0 instance-attribute

L0 base dev image tag (e.g. terok-l0:fedora-44).

l1 instance-attribute

L1 agent CLI image tag (e.g. terok-l1-cli:fedora-44).

l1_sidecar = None class-attribute instance-attribute

L1 sidecar image tag, if built (e.g. terok-l1-sidecar:fedora-44).

ImageBuilder(base_image=DEFAULT_BASE_IMAGE, family=None) dataclass

Build pipeline for terok agent container images.

Holds the (base_image, family) the L0/L1/L2 build stack is anchored on. Build operations are instance-bound; pure family detection, image introspection, and resource staging stay as static methods — they don't depend on the builder's state.

Two scopes of operations:

  • Instance methods — apply self.base_image and self.family to a podman build (build_base, build_sidecar, ensure_default_l1), tag computations (l0_tag, l1_tag, l1_sidecar_tag), and Dockerfile rendering (render_l0, render_l1, render_l1_sidecar).
  • Static methods — pure helpers that operate on arbitrary inputs: detect_family, image_agents, stage_scripts, stage_tmux_config, stage_toad_agents.

base_image = DEFAULT_BASE_IMAGE class-attribute instance-attribute

Base OS image the L0 layer FROMs (e.g. fedora:44).

family = None class-attribute instance-attribute

Package family override ("deb" / "rpm") — auto-detected when None.

l0_tag property

L0 image tag for self.base_image.

l1_sidecar_tag property

L1 sidecar image tag for self.base_image.

build_base(*, agents='all', rebuild=False, full_rebuild=False, build_dir=None, tag_as_default=False)

Build L0 + L1 images for agents; returns the resulting tag pair.

See module-level build_base_images for the parameter contract.

Source code in src/terok_executor/container/build.py
def build_base(
    self,
    *,
    agents: str | tuple[str, ...] = "all",
    rebuild: bool = False,
    full_rebuild: bool = False,
    build_dir: Path | None = None,
    tag_as_default: bool = False,
) -> ImageSet:
    """Build L0 + L1 images for *agents*; returns the resulting tag pair.

    See module-level ``build_base_images`` for the parameter contract.
    """
    return build_base_images(
        self.base_image,
        family=self.family,
        agents=agents,
        rebuild=rebuild,
        full_rebuild=full_rebuild,
        build_dir=build_dir,
        tag_as_default=tag_as_default,
    )

build_sidecar(*, tool_name='coderabbit', rebuild=False, full_rebuild=False, build_dir=None)

Build the L1 sidecar image variant for tool_name; returns the tag.

Source code in src/terok_executor/container/build.py
def build_sidecar(
    self,
    *,
    tool_name: str = "coderabbit",
    rebuild: bool = False,
    full_rebuild: bool = False,
    build_dir: Path | None = None,
) -> str:
    """Build the L1 sidecar image variant for *tool_name*; returns the tag."""
    return build_sidecar_image(
        self.base_image,
        family=self.family,
        tool_name=tool_name,
        rebuild=rebuild,
        full_rebuild=full_rebuild,
        build_dir=build_dir,
    )

ensure_default_l1(agents='all')

Return the default-alias L1 tag, building the default L1 if absent.

Source code in src/terok_executor/container/build.py
def ensure_default_l1(self, agents: str | tuple[str, ...] = "all") -> str:
    """Return the default-alias L1 tag, building the default L1 if absent."""
    return ensure_default_l1(self.base_image, family=self.family, agents=agents)

l1_tag(agents=None)

L1 image tag for agents under self.base_image (alias when None).

Source code in src/terok_executor/container/build.py
def l1_tag(self, agents: tuple[str, ...] | None = None) -> str:
    """L1 image tag for *agents* under ``self.base_image`` (alias when ``None``)."""
    return l1_image_tag(self.base_image, agents)

render_l0()

Render the L0 Dockerfile for this base.

Instance-bound because L0 is anchored on self.base_image.

Source code in src/terok_executor/container/build.py
def render_l0(self) -> str:
    """Render the L0 Dockerfile for this base.

    Instance-bound because L0 is anchored on ``self.base_image``.
    """
    return render_l0(self.base_image, family=self._family)

render_l1(l0_tag, *, family, agents='all', cache_bust='0') staticmethod

Render the L1 CLI Dockerfile for agents on top of l0_tag.

Static because the L1 stage depends only on the L0 tag and the package family — it never touches self.base_image. Callers that already have an ImageBuilder can pass builder._family to thread the resolved family through.

Source code in src/terok_executor/container/build.py
@staticmethod
def render_l1(
    l0_tag: str,
    *,
    family: str,
    agents: tuple[str, ...] | str = "all",
    cache_bust: str = "0",
) -> str:
    """Render the L1 CLI Dockerfile for *agents* on top of *l0_tag*.

    Static because the L1 stage depends only on the L0 tag and the
    package family — it never touches ``self.base_image``.  Callers
    that already have an [`ImageBuilder`][terok_executor.container.build.ImageBuilder]
    can pass ``builder._family`` to thread the resolved family through.
    """
    return render_l1(l0_tag, family=family, agents=agents, cache_bust=cache_bust)

render_l1_sidecar(l0_tag, *, family, tool_name='coderabbit', cache_bust='0') staticmethod

Render the L1 sidecar Dockerfile for tool_name on top of l0_tag.

Static for the same reason as render_l1.

Source code in src/terok_executor/container/build.py
@staticmethod
def render_l1_sidecar(
    l0_tag: str,
    *,
    family: str,
    tool_name: str = "coderabbit",
    cache_bust: str = "0",
) -> str:
    """Render the L1 sidecar Dockerfile for *tool_name* on top of *l0_tag*.

    Static for the same reason as [`render_l1`][terok_executor.container.build.ImageBuilder.render_l1].
    """
    return render_l1_sidecar(l0_tag, family=family, tool_name=tool_name, cache_bust=cache_bust)

detect_family(base_image, override=None) staticmethod

Resolve the package family ("deb" / "rpm") for base_image.

Source code in src/terok_executor/container/build.py
@staticmethod
def detect_family(base_image: str, override: str | None = None) -> str:
    """Resolve the package family (``"deb"`` / ``"rpm"``) for *base_image*."""
    return detect_family(base_image, override)

image_agents(image) staticmethod

Return roster agent names from an L1 image's ai.terok.agents label.

Source code in src/terok_executor/container/build.py
@staticmethod
def image_agents(image: str) -> set[str]:
    """Return roster agent names from an L1 image's ``ai.terok.agents`` label."""
    return image_agents(image)

stage_scripts(dest) staticmethod

Stage shell helper scripts (hilfe, terok-*) into dest.

Source code in src/terok_executor/container/build.py
@staticmethod
def stage_scripts(dest: Path) -> None:
    """Stage shell helper scripts (``hilfe``, ``terok-*``) into *dest*."""
    stage_scripts(dest)

stage_tmux_config(dest) staticmethod

Stage the tmux config into dest.

Source code in src/terok_executor/container/build.py
@staticmethod
def stage_tmux_config(dest: Path) -> None:
    """Stage the tmux config into *dest*."""
    stage_tmux_config(dest)

stage_toad_agents(dest) staticmethod

Stage toad agent metadata into dest.

Source code in src/terok_executor/container/build.py
@staticmethod
def stage_toad_agents(dest: Path) -> None:
    """Stage toad agent metadata into *dest*."""
    stage_toad_agents(dest)

detect_family(base_image, override=None)

Resolve the package family (deb or rpm) for base_image.

override — when set, must be "deb" or "rpm" and wins over detection (used to support unknown bases via project config).

Detection matches a small allowlist of known image prefixes (Ubuntu/Debian, Fedora, the official Podman container, NVIDIA CUDA/HPC SDK). NVIDIA images are inspected at the tag level so UBI variants (e.g. …:13.0.0-devel-ubi9) resolve to rpm while Ubuntu variants resolve to deb. Unknown images raise BuildError with a hint to set family: explicitly.

Source code in src/terok_executor/container/build.py
def detect_family(base_image: str, override: str | None = None) -> str:
    """Resolve the package family (``deb`` or ``rpm``) for *base_image*.

    *override* — when set, must be ``"deb"`` or ``"rpm"`` and wins over
    detection (used to support unknown bases via project config).

    Detection matches a small allowlist of known image prefixes
    (Ubuntu/Debian, Fedora, the official Podman container, NVIDIA CUDA/HPC
    SDK).  NVIDIA images are inspected at the tag level so UBI variants
    (e.g. ``…:13.0.0-devel-ubi9``) resolve to ``rpm`` while Ubuntu
    variants resolve to ``deb``.  Unknown images raise [`BuildError`][terok_executor.container.build.BuildError]
    with a hint to set ``family:`` explicitly.
    """
    if override is not None:
        if override not in {"deb", "rpm"}:
            raise BuildError(f"family must be 'deb' or 'rpm', got {override!r}")
        return override
    name, tag = _split_image_ref(_normalize_base_image(base_image))
    name_lc = name.lower()
    for prefix, fam in _KNOWN_FAMILIES:
        if name_lc == prefix or name_lc.startswith(prefix + "/"):
            return fam(tag) if callable(fam) else fam
    raise BuildError(
        f"Cannot infer package family for base image {base_image!r}. "
        "Set `family: deb` or `family: rpm` under image: in project.yml."
    )

build_project_image(*, dockerfile, context_dir, target_tag, extra_tags=(), build_args=None, labels=None, no_cache=False, pull_always=False)

Build an OCI image from a pre-rendered Dockerfile.

The thin podman build invoker that the three opinionated factories in this module (build_base_images, build_sidecar_image, and terok's project/L2 build) share. Callers own Dockerfile rendering, tag naming, label computation, and build-context staging — this function only assembles flags and shells out.

Parameters:

Name Type Description Default
dockerfile Path

Path to the pre-rendered Dockerfile (-f).

required
context_dir Path

Build context directory (final positional argument).

required
target_tag str

Primary image tag (-t).

required
extra_tags tuple[str, ...]

Additional tags applied to the same build (each becomes another -t on the command line — podman builds once and tags the result multiple times).

()
build_args dict[str, str] | None

--build-arg KEY=VALUE pairs.

None
labels dict[str, str] | None

--label KEY=VALUE pairs recorded in the OCI config.

None
no_cache bool

Force full rebuild.

False
pull_always bool

Pull the base image even if a local copy exists.

False

Raises:

Type Description
BuildError

When podman is not on PATH or the build exits non-zero.

Source code in src/terok_executor/container/build.py
def build_project_image(
    *,
    dockerfile: Path,
    context_dir: Path,
    target_tag: str,
    extra_tags: tuple[str, ...] = (),
    build_args: dict[str, str] | None = None,
    labels: dict[str, str] | None = None,
    no_cache: bool = False,
    pull_always: bool = False,
) -> None:
    """Build an OCI image from a pre-rendered Dockerfile.

    The thin ``podman build`` invoker that the three opinionated factories
    in this module ([`build_base_images`][terok_executor.container.build.build_base_images], [`build_sidecar_image`][terok_executor.container.build.build_sidecar_image],
    and terok's project/L2 build) share.  Callers own Dockerfile
    rendering, tag naming, label computation, and build-context staging —
    this function only assembles flags and shells out.

    Args:
        dockerfile: Path to the pre-rendered Dockerfile (``-f``).
        context_dir: Build context directory (final positional argument).
        target_tag: Primary image tag (``-t``).
        extra_tags: Additional tags applied to the same build (each becomes
            another ``-t`` on the command line — podman builds once and
            tags the result multiple times).
        build_args: ``--build-arg KEY=VALUE`` pairs.
        labels: ``--label KEY=VALUE`` pairs recorded in the OCI config.
        no_cache: Force full rebuild.
        pull_always: Pull the base image even if a local copy exists.

    Raises:
        BuildError: When podman is not on PATH or the build exits non-zero.
    """
    cmd = ["podman", "build", "-f", str(dockerfile)]
    for key, value in (build_args or {}).items():
        cmd += ["--build-arg", f"{key}={value}"]
    for key, value in (labels or {}).items():
        cmd += ["--label", f"{key}={value}"]
    cmd += ["-t", target_tag]
    for tag in extra_tags:
        cmd += ["-t", tag]
    if no_cache:
        cmd.append("--no-cache")
    if pull_always:
        cmd.append("--pull=always")
    cmd.append(str(context_dir))

    print("$", shlex.join(cmd))
    try:
        subprocess.run(cmd, check=True)
    except FileNotFoundError as exc:
        raise BuildError("podman not found; please install podman") from exc
    except subprocess.CalledProcessError as exc:
        raise BuildError(f"Image build failed: {exc}") from exc

build_base_images(base_image=DEFAULT_BASE_IMAGE, *, family=None, agents='all', rebuild=False, full_rebuild=False, build_dir=None, tag_as_default=False)

Build L0 + L1 container images and return their tags.

Skips building if images already exist locally (unless rebuild or full_rebuild is set). Uses a temporary directory for the build context by default; pass build_dir to use a specific (empty or non-existent) directory instead.

Parameters:

Name Type Description Default
base_image str

Base OS image (e.g. fedora:44, nvidia/cuda:...).

DEFAULT_BASE_IMAGE
family str | None

Override for the package family ("deb" or "rpm"). None means detect from base_image via detect_family.

None
agents str | tuple[str, ...]

Roster entries to install, as the literal string "all" (every entry) or a tuple of names (transitively expanded by depends_on). Same selection drives the OCI label, the L1 tag suffix, the in-container manifest, and the help fragments.

'all'
rebuild bool

Force rebuild with cache bust (refreshes agent installs).

False
full_rebuild bool

Force rebuild with --no-cache --pull=always.

False
build_dir Path | None

Build context directory (must be empty or absent).

None
tag_as_default bool

When True, additionally tag the L1 with the unsuffixed default-alias l1_image_tag(base_image). Set by ensure_default_l1 when this build represents the user's configured default agent selection. Project / per-agent / partial builds leave it False so the alias keeps pointing at the user's default L1, not at whatever was last built.

False

Returns:

Type Description
ImageSet

ImageSet with the L0 and L1 image tags.

Raises:

Type Description
BuildError

If podman is missing, the family cannot be resolved, or a build step fails.

ValueError

If build_dir is a file or a non-empty directory, or if agents contains unknown roster entries.

Source code in src/terok_executor/container/build.py
def build_base_images(
    base_image: str = DEFAULT_BASE_IMAGE,
    *,
    family: str | None = None,
    agents: str | tuple[str, ...] = "all",
    rebuild: bool = False,
    full_rebuild: bool = False,
    build_dir: Path | None = None,
    tag_as_default: bool = False,
) -> ImageSet:
    """Build L0 + L1 container images and return their tags.

    Skips building if images already exist locally (unless *rebuild* or
    *full_rebuild* is set).  Uses a temporary directory for the build
    context by default; pass *build_dir* to use a specific (empty or
    non-existent) directory instead.

    Args:
        base_image: Base OS image (e.g. ``fedora:44``, ``nvidia/cuda:...``).
        family: Override for the package family (``"deb"`` or ``"rpm"``).
            ``None`` means detect from *base_image* via [`detect_family`][terok_executor.container.build.detect_family].
        agents: Roster entries to install, as the literal string ``"all"``
            (every entry) or a tuple of names (transitively expanded by
            ``depends_on``).  Same selection drives the OCI label, the L1
            tag suffix, the in-container manifest, and the help fragments.
        rebuild: Force rebuild with cache bust (refreshes agent installs).
        full_rebuild: Force rebuild with ``--no-cache --pull=always``.
        build_dir: Build context directory (must be empty or absent).
        tag_as_default: When ``True``, additionally tag the L1 with the
            unsuffixed default-alias [`l1_image_tag(base_image)`][terok_executor.container.build.l1_image_tag].
            Set by [`ensure_default_l1`][terok_executor.container.build.ensure_default_l1]
            when this build represents the user's *configured* default
            agent selection.  Project / per-agent / partial builds leave
            it ``False`` so the alias keeps pointing at the user's
            default L1, not at whatever was last built.

    Returns:
        [`ImageSet`][terok_executor.container.build.ImageSet] with the L0 and L1 image tags.

    Raises:
        BuildError: If podman is missing, the family cannot be resolved,
            or a build step fails.
        ValueError: If *build_dir* is a file or a non-empty directory,
            or if *agents* contains unknown roster entries.
    """
    from terok_executor.roster import AgentRoster

    _validate_build_dir(build_dir)
    _check_podman()

    base_image = _normalize_base_image(base_image)
    selected = AgentRoster.shared().resolve_selection(agents)

    l0_tag = l0_image_tag(base_image)
    l1_tag = l1_image_tag(base_image, selected)
    l1_alias = l1_image_tag(base_image)
    extra_tags: tuple[str, ...] = (l1_alias,) if tag_as_default else ()

    # Skip if both images exist and no forced rebuild — done before
    # detect_family() so cached images for unknown bases (built earlier
    # with explicit family) can still be reused without supplying it again.
    if not rebuild and not full_rebuild:
        if _image_exists(l0_tag) and _image_exists(l1_tag):
            return ImageSet(l0=l0_tag, l1=l1_tag)

    fam = detect_family(base_image, override=family)

    # Prepare build context in a safe directory
    import tempfile

    own_tmp = build_dir is None
    context = build_dir or Path(tempfile.mkdtemp(prefix="terok-executor-build-"))

    try:
        try:
            prepare_build_context(context)
            stage_help_fragments(context / "help.d", selected)
            stage_agent_facts(context / "agent-protocols.json", selected)
            stage_provider_protocols(context / "provider-protocols.json", selected)

            # Single timestamp for both render and build-arg consistency
            cache_bust = str(int(time.time()))

            # Render and write Dockerfiles into the build context
            (context / "L0.Dockerfile").write_text(render_l0(base_image, family=fam))
            (context / "L1.cli.Dockerfile").write_text(
                render_l1(l0_tag, family=fam, agents=selected, cache_bust=cache_bust)
            )
        except OSError as exc:
            raise BuildError(
                f"Image build failed preparing base build context for "
                f"{l1_tag!r} at {context}: {exc}"
            ) from exc

        # Build L0 — base dev image (Ubuntu + git + SSH + init script)
        build_project_image(
            dockerfile=context / "L0.Dockerfile",
            context_dir=context,
            target_tag=l0_tag,
            build_args={"BASE_IMAGE": base_image},
            no_cache=full_rebuild,
            pull_always=full_rebuild,
        )

        # The unsuffixed alias is reserved for the user's default L1
        # (whatever they have configured as ``image.agents``).  It must
        # not be retagged on partial / project / per-agent builds —
        # otherwise `terok auth <X>` would silently end up running
        # against an L1 missing X.  Callers that ARE building the
        # user's default selection pass ``tag_as_default=True``.
        build_project_image(
            dockerfile=context / "L1.cli.Dockerfile",
            context_dir=context,
            target_tag=l1_tag,
            extra_tags=extra_tags,
            build_args={"BASE_IMAGE": l0_tag, "AGENT_CACHE_BUST": cache_bust},
            no_cache=full_rebuild,
        )

    finally:
        if own_tmp:
            shutil.rmtree(context, ignore_errors=True)

    return ImageSet(l0=l0_tag, l1=l1_tag)

build_sidecar_image(base_image=DEFAULT_BASE_IMAGE, *, family=None, tool_name='coderabbit', rebuild=False, full_rebuild=False, build_dir=None)

Build the L1 sidecar image for a specific tool. Returns the image tag.

Ensures L0 exists first (builds it if missing), then builds the sidecar image FROM L0. The sidecar contains only the named tool — no agent CLIs, no LLMs.

Parameters:

Name Type Description Default
base_image str

Base OS image (passed through to L0 build).

DEFAULT_BASE_IMAGE
family str | None

Override for the package family ("deb" or "rpm"). None means detect from base_image via detect_family.

None
tool_name str

Tool to install (selects Jinja2 conditional in template).

'coderabbit'
rebuild bool

Force rebuild with cache bust.

False
full_rebuild bool

Force rebuild with --no-cache.

False
build_dir Path | None

Build context directory (must be empty or absent).

None

Returns:

Type Description
str

The sidecar image tag (e.g. terok-l1-sidecar:fedora-44).

Raises:

Type Description
BuildError

If podman is missing, the family cannot be resolved, or a build step fails.

ValueError

If build_dir is a file or a non-empty directory.

Source code in src/terok_executor/container/build.py
def build_sidecar_image(
    base_image: str = DEFAULT_BASE_IMAGE,
    *,
    family: str | None = None,
    tool_name: str = "coderabbit",
    rebuild: bool = False,
    full_rebuild: bool = False,
    build_dir: Path | None = None,
) -> str:
    """Build the L1 sidecar image for a specific tool. Returns the image tag.

    Ensures L0 exists first (builds it if missing), then builds the
    sidecar image FROM L0.  The sidecar contains only the named tool —
    no agent CLIs, no LLMs.

    Args:
        base_image: Base OS image (passed through to L0 build).
        family: Override for the package family (``"deb"`` or ``"rpm"``).
            ``None`` means detect from *base_image* via [`detect_family`][terok_executor.container.build.detect_family].
        tool_name: Tool to install (selects Jinja2 conditional in template).
        rebuild: Force rebuild with cache bust.
        full_rebuild: Force rebuild with ``--no-cache``.
        build_dir: Build context directory (must be empty or absent).

    Returns:
        The sidecar image tag (e.g. ``terok-l1-sidecar:fedora-44``).

    Raises:
        BuildError: If podman is missing, the family cannot be resolved,
            or a build step fails.
        ValueError: If *build_dir* is a file or a non-empty directory.
    """
    _validate_build_dir(build_dir)
    _check_podman()

    base_image = _normalize_base_image(base_image)
    l0_tag = l0_image_tag(base_image)
    sidecar_tag = l1_sidecar_image_tag(base_image)

    # Same fast-path as build_base_images: defer detect_family until we
    # know we actually need to render Dockerfiles, so cached sidecars
    # for unknown bases can be reused without re-supplying ``family``.
    if not rebuild and not full_rebuild and _image_exists(sidecar_tag) and _image_exists(l0_tag):
        return sidecar_tag

    fam = detect_family(base_image, override=family)

    # Ensure L0 exists (build if needed)
    if not _image_exists(l0_tag) or full_rebuild:
        build_base_images(base_image, family=fam, rebuild=rebuild, full_rebuild=full_rebuild)

    import tempfile

    own_tmp = build_dir is None
    context = build_dir or Path(tempfile.mkdtemp(prefix="terok-executor-sidecar-"))

    try:
        try:
            prepare_build_context(context)
            cache_bust = str(int(time.time()))

            (context / "L1.sidecar.Dockerfile").write_text(
                render_l1_sidecar(l0_tag, family=fam, tool_name=tool_name, cache_bust=cache_bust)
            )
        except OSError as exc:
            raise BuildError(
                f"Image build failed preparing sidecar build context for "
                f"{sidecar_tag!r} at {context}: {exc}"
            ) from exc

        build_project_image(
            dockerfile=context / "L1.sidecar.Dockerfile",
            context_dir=context,
            target_tag=sidecar_tag,
            build_args={"BASE_IMAGE": l0_tag, "TOOL_CACHE_BUST": cache_bust},
            no_cache=full_rebuild,
        )
    finally:
        if own_tmp:
            shutil.rmtree(context, ignore_errors=True)

    return sidecar_tag

prepare_build_context(dest)

Stage auxiliary resources into a build context directory.

After calling this, dest contains the resources that Dockerfile COPY directives reference:

  • scripts/ — container helper scripts (init, env, ACP wrappers)
  • toad-agents/ — ACP agent TOML definitions
  • tmux/ — container tmux config

Dockerfiles themselves are not written here — they are rendered and placed by build_base_images (which calls this function internally).

Source code in src/terok_executor/container/build.py
def prepare_build_context(dest: Path) -> None:
    """Stage auxiliary resources into a build context directory.

    After calling this, *dest* contains the resources that Dockerfile
    ``COPY`` directives reference:

    - ``scripts/``     — container helper scripts (init, env, ACP wrappers)
    - ``toad-agents/`` — ACP agent TOML definitions
    - ``tmux/``        — container tmux config

    Dockerfiles themselves are **not** written here — they are rendered
    and placed by [`build_base_images`][terok_executor.container.build.build_base_images] (which calls this function
    internally).
    """
    dest.mkdir(parents=True, exist_ok=True)
    stage_scripts(dest / "scripts")
    stage_toad_agents(dest / "toad-agents")
    stage_tmux_config(dest / "tmux")

render_l0(base_image=DEFAULT_BASE_IMAGE, *, family=None)

Render the L0 (base dev) Dockerfile.

The base_image is normalised before rendering so that blank or whitespace-only values produce a valid Dockerfile. family ("deb" or "rpm") selects the package-manager branch of the template; None resolves it via detect_family.

The rendered template ships a single vendor unit (sshd-terok.service) gated on a stock ConditionFileNotEmpty= over /etc/ssh/authorized_keys.d/terok. The trust file ships empty, so the unit is skipped at boot under crun (no keys ⇒ no listener); under krun terok bind-mounts the live host pubkey over it at launch, the condition flips to true, sshd starts on TCP 22, and the host reaches the guest via the per-task host port that podman's passt has forwarded into the guest namespace. One image, one build, two runtimes — and "off" is structural, not just inert.

Source code in src/terok_executor/container/build.py
def render_l0(base_image: str = DEFAULT_BASE_IMAGE, *, family: str | None = None) -> str:
    """Render the L0 (base dev) Dockerfile.

    The *base_image* is normalised before rendering so that blank or
    whitespace-only values produce a valid Dockerfile.  *family*
    (``"deb"`` or ``"rpm"``) selects the package-manager branch of the
    template; ``None`` resolves it via [`detect_family`][terok_executor.container.build.detect_family].

    The rendered template ships a single vendor unit
    (``sshd-terok.service``) gated on a stock ``ConditionFileNotEmpty=``
    over ``/etc/ssh/authorized_keys.d/terok``.  The trust file ships
    empty, so the unit is skipped at boot under crun (no keys ⇒ no
    listener); under krun terok bind-mounts the live host pubkey over
    it at launch, the condition flips to true, sshd starts on TCP 22,
    and the host reaches the guest via the per-task host port that
    podman's passt has forwarded into the guest namespace.  One image,
    one build, two runtimes — and "off" is structural, not just inert.
    """
    base_image = _normalize_base_image(base_image)
    fam = detect_family(base_image, override=family)
    return _render_template(
        "l0.dev.Dockerfile.template",
        {"BASE_IMAGE": base_image, "family": fam},
    )

render_l1(l0_image, *, family, agents='all', cache_bust='0')

Render the L1 (agent CLI) Dockerfile for the given agent selection.

l0_image is the tag of the L0 image to build on top of. family ("deb" or "rpm") selects the package-manager branch and is required — there is no L0 reference to detect from at this point, so callers must supply the value resolved at the L0 level (typically via detect_family). Each roster install snippet is itself rendered as a Jinja template with family in scope, so snippets can carry {% if family == "deb" %}…{% else %}…{% endif %} branches for package-manager-specific commands. agents is a tuple of already-resolved roster names (or the literal string "all"); the template loops over them and emits each one's install snippets. cache_bust invalidates the per-agent install layers when changed (typically set to a Unix timestamp).

Source code in src/terok_executor/container/build.py
def render_l1(
    l0_image: str,
    *,
    family: str,
    agents: tuple[str, ...] | str = "all",
    cache_bust: str = "0",
) -> str:
    """Render the L1 (agent CLI) Dockerfile for the given agent selection.

    *l0_image* is the tag of the L0 image to build on top of.  *family*
    (``"deb"`` or ``"rpm"``) selects the package-manager branch and is
    required — there is no L0 reference to detect from at this point, so
    callers must supply the value resolved at the L0 level (typically via
    [`detect_family`][terok_executor.container.build.detect_family]).  Each roster install snippet is itself rendered
    as a Jinja template with ``family`` in scope, so snippets can carry
    ``{% if family == "deb" %}…{% else %}…{% endif %}`` branches for
    package-manager-specific commands.  *agents* is a tuple of
    already-resolved roster names (or the literal string ``"all"``); the
    template loops over them and emits each one's install snippets.
    *cache_bust* invalidates the per-agent install layers when changed
    (typically set to a Unix timestamp).
    """
    from terok_executor.roster import AgentRoster

    roster = AgentRoster.shared()
    selected = roster.resolve_selection(agents)
    installs = roster.installs

    root_snippets = [
        _render_snippet(installs[n].run_as_root, family)
        for n in selected
        if installs[n].run_as_root
    ]
    dev_snippets = [
        _render_snippet(installs[n].run_as_dev, family) for n in selected if installs[n].run_as_dev
    ]

    return _render_template(
        "l1.agent-cli.Dockerfile.template",
        {
            "BASE_IMAGE": l0_image,
            "AGENT_CACHE_BUST": cache_bust,
            "family": family,
            "install_root_snippets": root_snippets,
            "install_dev_snippets": dev_snippets,
            "installed_agents_csv": ",".join(selected),
            "agents_label": AGENTS_LABEL,
            "installed_env_path": INSTALLED_ENV_PATH,
            "agent_protocols_path": AGENT_PROTOCOLS_PATH,
            "provider_protocols_path": PROVIDER_PROTOCOLS_PATH,
        },
    )

render_l1_sidecar(l0_image, *, family, tool_name='coderabbit', cache_bust='0')

Render the L1 sidecar (tool-only) Dockerfile.

The sidecar image is built FROM L0 (not L1) and installs a single tool binary — no agent CLIs, no LLMs. family (required) selects the package-manager branch; tool_name selects which tool install block to activate via Jinja2 conditional.

Source code in src/terok_executor/container/build.py
def render_l1_sidecar(
    l0_image: str,
    *,
    family: str,
    tool_name: str = "coderabbit",
    cache_bust: str = "0",
) -> str:
    """Render the L1 sidecar (tool-only) Dockerfile.

    The sidecar image is built FROM L0 (not L1) and installs a single
    tool binary — no agent CLIs, no LLMs.  *family* (required) selects
    the package-manager branch; *tool_name* selects which tool install
    block to activate via Jinja2 conditional.
    """
    return _render_template(
        "l1.sidecar.Dockerfile.template",
        {
            "BASE_IMAGE": l0_image,
            "TOOL_CACHE_BUST": cache_bust,
            "tool_name": tool_name,
            "family": family,
        },
    )

stage_scripts(dest)

Stage container helper scripts into dest.

Copies executor's own resources/scripts/ then overlays the socat-based bridge scripts that ship with terok_sandbox (ensure-bridges.sh + ssh-agent-bridge.sh). The bridges live in sandbox because they encode sandbox-level concerns with no executor-specific logic; executor still bundles them into the container image so the Dockerfile's COPY scripts/… lines keep finding them at their established names.

Raises BuildError when terok_sandbox is not importable — sandbox is a hard dependency in pyproject.toml, but a broken install would otherwise surface as a raw ModuleNotFoundError traceback.

Source code in src/terok_executor/container/build.py
def stage_scripts(dest: Path) -> None:
    """Stage container helper scripts into *dest*.

    Copies executor's own ``resources/scripts/`` then overlays the
    socat-based bridge scripts that ship with ``terok_sandbox``
    (``ensure-bridges.sh`` + ``ssh-agent-bridge.sh``).  The bridges
    live in sandbox because they encode sandbox-level concerns with
    no executor-specific logic; executor still bundles them into the
    container image so the Dockerfile's ``COPY scripts/…`` lines keep
    finding them at their established names.

    Raises [`BuildError`][terok_executor.container.build.BuildError]
    when ``terok_sandbox`` is not importable — sandbox is a hard
    dependency in ``pyproject.toml``, but a broken install would
    otherwise surface as a raw ``ModuleNotFoundError`` traceback.
    """
    if dest.exists():
        shutil.rmtree(dest)
    _copy_package_tree("terok_executor", "resources/scripts", dest)
    try:
        _copy_package_tree("terok_sandbox", "resources/bridges", dest)
    except ModuleNotFoundError as exc:
        raise BuildError(
            "terok_sandbox is not importable — its bridge resources "
            "(resources/bridges/) could not be staged into the build "
            "context.  Reinstall terok-executor's dependencies to fix."
        ) from exc
    _clean_packaging_artifacts(dest)

stage_toad_agents(dest)

Stage Toad ACP agent TOML definitions into dest.

These describe OpenCode-based agents (Blablador, KISSKI, etc.) that are injected into Toad's bundled agent directory at container build time.

Source code in src/terok_executor/container/build.py
def stage_toad_agents(dest: Path) -> None:
    """Stage Toad ACP agent TOML definitions into *dest*.

    These describe OpenCode-based agents (Blablador, KISSKI, etc.) that are
    injected into Toad's bundled agent directory at container build time.
    """
    if dest.exists():
        shutil.rmtree(dest)
    _copy_package_tree("terok_executor", "resources/toad-agents", dest)
    _clean_packaging_artifacts(dest)

stage_help_fragments(dest, agents)

Render the baked hilfe help fragments into dest.

Writes one file per bakeable section — only dev_tool (see the module-level _HELP_SECTION_FILES map) — holding the labels of the selected entries that have a help: block, with backslash escapes (\033[...]) interpreted into real ANSI so that hilfe only needs to cat them. The agent section is deliberately not baked: hilfe renders it at runtime so unusable agents can be dimmed (see stage_agent_facts). Empty sections are omitted; hilfe skips missing files.

Source code in src/terok_executor/container/build.py
def stage_help_fragments(dest: Path, agents: tuple[str, ...]) -> None:
    """Render the baked ``hilfe`` help fragments into *dest*.

    Writes one file per *bakeable* section — only ``dev_tool`` (see the
    module-level ``_HELP_SECTION_FILES`` map) — holding the labels of the
    selected entries that have a ``help:`` block, with backslash escapes
    (``\\033[...]``) interpreted into real ANSI so that ``hilfe`` only needs to
    ``cat`` them.  The ``agent`` section is deliberately not baked: ``hilfe``
    renders it at runtime so unusable agents can be dimmed (see
    [`stage_agent_facts`][terok_executor.container.build.stage_agent_facts]).  Empty sections are omitted;
    ``hilfe`` skips missing files.
    """
    from terok_executor.roster import AgentRoster

    helps = AgentRoster.shared().helps

    by_section: dict[str, list[str]] = {}
    for name in agents:
        spec = helps.get(name)
        if spec is None or not spec.label or spec.section not in _HELP_SECTION_FILES:
            continue
        by_section.setdefault(spec.section, []).append(spec.label)

    if dest.exists():
        shutil.rmtree(dest)
    dest.mkdir(parents=True, exist_ok=True)
    for section, lines in by_section.items():
        decoded = "".join(_decode_label_escapes(line) + "\n" for line in lines)
        (dest / _HELP_SECTION_FILES[section]).write_text(decoded, encoding="utf-8")

stage_agent_facts(dest_file, agents)

Bake the per-agent facts the in-container readiness layer needs, as JSON.

For every selected entry in the agent help section, records a small record — {"protocol", "label"} — so that terok-agents can decide, at runtime, whether each agent has an authenticated provider it can talk to, and hilfe can render it bright or dimmed:

  • protocol — the wire protocol the agent speaks (None for entries with no fixed protocol, e.g. the toad frontend). An agent is usable when any authenticated provider serves this protocol — not tied to a single default — so the protocol, not a provider name, is the fact that matters. The provider→protocol universe is baked separately by stage_provider_protocols.
  • label — the banner line, escapes already decoded to real ANSI.

These facts are the only roster knowledge the container environment cannot reconstruct, so they travel into the image alongside the help fragments. The file is always written — an empty object when the selection has no agent-section entries — so the Dockerfile COPY always has a source.

Source code in src/terok_executor/container/build.py
def stage_agent_facts(dest_file: Path, agents: tuple[str, ...]) -> None:
    """Bake the per-agent facts the in-container readiness layer needs, as JSON.

    For every selected entry in the ``agent`` help section, records a small
    record — ``{"protocol", "label"}`` — so that ``terok-agents`` can decide, at
    runtime, whether each agent has an authenticated provider it can talk to,
    and ``hilfe`` can render it bright or dimmed:

    - ``protocol`` — the wire protocol the agent speaks (``None`` for entries
      with no fixed protocol, e.g. the ``toad`` frontend).  An agent is usable
      when *any* authenticated provider serves this protocol — not tied to a
      single default — so the protocol, not a provider name, is the fact that
      matters.  The provider→protocol universe is baked separately by
      [`stage_provider_protocols`][terok_executor.container.build.stage_provider_protocols].
    - ``label`` — the banner line, escapes already decoded to real ANSI.

    These facts are the only roster knowledge the container environment cannot
    reconstruct, so they travel into the image alongside the help fragments.
    The file is always written — an empty object when the selection has no
    agent-section entries — so the Dockerfile ``COPY`` always has a source.
    """
    from terok_executor.roster import AgentRoster

    roster = AgentRoster.shared()
    agents_by_name = roster.agents
    helps = roster.helps

    facts: dict[str, dict[str, str | None]] = {}
    for name in agents:
        spec = helps.get(name)
        if spec is None or not spec.label or spec.section != "agent":
            continue
        agent = agents_by_name.get(name)
        facts[name] = {
            "protocol": agent.protocol if agent else None,
            "label": _decode_label_escapes(spec.label),
        }
    dest_file.parent.mkdir(parents=True, exist_ok=True)
    dest_file.write_text(json.dumps(facts, indent=2, sort_keys=True) + "\n", encoding="utf-8")

stage_provider_protocols(dest_file, agents)

Bake the wire-protocol → candidate-providers universe as JSON.

For every protocol any selected agent speaks, records the sorted list of providers whose serves covers it — i.e. the providers a user could authenticate to make those agents usable. This is what lets hilfe turn a dimmed agent's needs an openai-chat provider into an actionable list under the providers section, including providers not yet authenticated (which the container environment, carrying only authenticated providers, cannot enumerate on its own).

Scoped to the protocols actually in play for this image, so an L1 without a given agent doesn't advertise providers for a protocol nothing speaks. The file is always written — an empty object when no selected agent speaks a protocol — so the Dockerfile COPY always has a source.

Source code in src/terok_executor/container/build.py
def stage_provider_protocols(dest_file: Path, agents: tuple[str, ...]) -> None:
    """Bake the wire-protocol → candidate-providers universe as JSON.

    For every protocol any selected agent speaks, records the sorted list of
    providers whose ``serves`` covers it — i.e. the providers a user could
    authenticate to make those agents usable.  This is what lets ``hilfe`` turn
    a dimmed agent's ``needs an openai-chat provider`` into an actionable list
    under the providers section, including providers not yet authenticated
    (which the container environment, carrying only *authenticated* providers,
    cannot enumerate on its own).

    Scoped to the protocols actually in play for this image, so an L1 without a
    given agent doesn't advertise providers for a protocol nothing speaks.  The
    file is always written — an empty object when no selected agent speaks a
    protocol — so the Dockerfile ``COPY`` always has a source.
    """
    from terok_executor.roster import AgentRoster

    roster = AgentRoster.shared()
    agents_by_name = roster.agents
    providers = roster.providers

    protocols = {
        agents_by_name[name].protocol
        for name in agents
        if name in agents_by_name and agents_by_name[name].protocol
    }
    universe = {
        protocol: sorted(name for name, prov in providers.items() if protocol in prov.serves)
        for protocol in protocols
    }
    dest_file.parent.mkdir(parents=True, exist_ok=True)
    dest_file.write_text(json.dumps(universe, indent=2, sort_keys=True) + "\n", encoding="utf-8")

stage_tmux_config(dest)

Stage the container tmux configuration into dest.

Copies container-tmux.conf — the green-status-bar config that distinguishes container tmux sessions from host tmux.

Source code in src/terok_executor/container/build.py
def stage_tmux_config(dest: Path) -> None:
    """Stage the container tmux configuration into *dest*.

    Copies ``container-tmux.conf`` — the green-status-bar config that
    distinguishes container tmux sessions from host tmux.
    """
    if dest.exists():
        shutil.rmtree(dest)
    _copy_package_tree("terok_executor", "resources/tmux", dest)
    _clean_packaging_artifacts(dest)

l0_image_tag(base_image)

Return the L0 base dev image tag for base_image.

Source code in src/terok_executor/container/build.py
def l0_image_tag(base_image: str) -> str:
    """Return the L0 base dev image tag for *base_image*."""
    return f"terok-l0:{_base_tag(base_image)}"

l1_image_tag(base_image, agents=None)

Return the L1 agent CLI image tag for base_image and a selection.

When agents is None, returns the unsuffixed default-alias (e.g. terok-l1-cli:fedora-44). This alias points at whichever L1 was last built with tag_as_default=True — i.e. the L1 that holds the user's configured default agent selection. Project / per-agent / partial builds get only their suffixed tag and never touch the alias, so terok auth <provider> can rely on the alias actually containing every agent the user configured.

When agents is a tuple of names, appends a sorted -a-b-c suffix (- is the only spec-valid separator that _base_tag already uses) so multiple selections coexist in the local image store and stay individually addressable. Agent name fragments are passed through the same _base_tag sanitiser to keep the final tag within the OCI tag charset ([A-Za-z0-9_.-]).

The full tag (after :) is bounded by _MAX_TAG_LEN. When the readable base-a-b-c form would overflow, the agent portion is replaced with a SHA1 digest of the sorted selection — same collision-resistant fallback pattern _base_tag uses internally for overlong image names.

Source code in src/terok_executor/container/build.py
def l1_image_tag(base_image: str, agents: tuple[str, ...] | None = None) -> str:
    """Return the L1 agent CLI image tag for *base_image* and a selection.

    When *agents* is ``None``, returns the unsuffixed **default-alias**
    (e.g. ``terok-l1-cli:fedora-44``).  This alias points at whichever
    L1 was last built with ``tag_as_default=True`` — i.e. the L1 that
    holds the user's configured default agent selection.  Project /
    per-agent / partial builds get only their suffixed tag and never
    touch the alias, so ``terok auth <provider>`` can rely on the alias
    actually containing every agent the user configured.

    When *agents* is a tuple of names, appends a sorted ``-a-b-c``
    suffix (``-`` is the only spec-valid separator that ``_base_tag``
    already uses) so multiple selections coexist in the local image
    store and stay individually addressable.  Agent name fragments are
    passed through the same ``_base_tag`` sanitiser to keep the final
    tag within the OCI tag charset (``[A-Za-z0-9_.-]``).

    The full tag (after ``:``) is bounded by `_MAX_TAG_LEN`.  When
    the readable ``base-a-b-c`` form would overflow, the agent portion
    is replaced with a SHA1 digest of the sorted selection — same
    collision-resistant fallback pattern `_base_tag` uses
    internally for overlong image names.
    """
    base_tag = _base_tag(base_image)
    if agents is None:
        return f"terok-l1-cli:{base_tag}"
    readable_suffix = "-".join(_base_tag(a) for a in sorted(agents)) if agents else "empty"
    if len(base_tag) + 1 + len(readable_suffix) <= _MAX_TAG_LEN:
        return f"terok-l1-cli:{base_tag}-{readable_suffix}"
    suffix = hashlib.sha1(
        ",".join(sorted(agents)).encode("utf-8"), usedforsecurity=False
    ).hexdigest()[:_AGENT_DIGEST_LEN]
    # Pathological case: a base already near _MAX_TAG_LEN leaves no room
    # even for the digest.  Trim base_tag further — same collision-resistant
    # shape as the digest fallback itself.
    max_base = _MAX_TAG_LEN - 1 - _AGENT_DIGEST_LEN
    if len(base_tag) > max_base:
        base_tag = base_tag[:max_base]
    return f"terok-l1-cli:{base_tag}-{suffix}"

l1_sidecar_image_tag(base_image)

Return the L1 sidecar (tool-only) image tag for base_image.

Source code in src/terok_executor/container/build.py
def l1_sidecar_image_tag(base_image: str) -> str:
    """Return the L1 sidecar (tool-only) image tag for *base_image*."""
    return f"terok-l1-sidecar:{_base_tag(base_image)}"

image_agents(image)

Return the set of agent names installed in image.

Reads the ai.terok.agents OCI label baked into the L1 image at build time (see AGENTS_LABEL). Returns an empty set if the image is missing, has no label, or its label is empty — never raises on inspection failure, since callers use the result to make a "image good enough?" decision and an empty set always means "no, rebuild".

Source code in src/terok_executor/container/build.py
def image_agents(image: str) -> set[str]:
    """Return the set of agent names installed in *image*.

    Reads the ``ai.terok.agents`` OCI label baked into the L1 image at
    build time (see [`AGENTS_LABEL`][terok_executor.container.build.AGENTS_LABEL]).
    Returns an empty set if the image is missing, has no label, or its
    label is empty — never raises on inspection failure, since callers
    use the result to make a "image good enough?" decision and an
    empty set always means "no, rebuild".
    """
    try:
        result = subprocess.run(
            [
                "podman",
                "image",
                "inspect",
                image,
                "--format",
                f'{{{{ index .Config.Labels "{AGENTS_LABEL}" }}}}',
            ],
            capture_output=True,
            text=True,
            timeout=10,
            check=False,
        )
    except (FileNotFoundError, subprocess.TimeoutExpired):
        return set()
    if result.returncode != 0:
        return set()
    csv = result.stdout.strip()
    return {a for a in csv.split(",") if a} if csv else set()

ensure_default_l1(base_image=DEFAULT_BASE_IMAGE, *, family=None, agents='all')

Return the default-alias L1 tag, building the user's default L1 if absent.

Used by terok auth (and the equivalent standalone-executor flow) to resolve a host-wide L1 image that contains every agent the user has configured. If the alias already exists locally, it is trusted and returned as-is — the alias is reserved for the user's default selection (see l1_image_tag), so its contents are well-defined. When the alias is missing the function builds it via build_base_images with tag_as_default=True.

agents defaults to the literal string "all" so standalone callers get the whole roster. terok passes the user's configured image.agents value here so the alias means "every agent the user has enabled" rather than the implementation-default roster.

Source code in src/terok_executor/container/build.py
def ensure_default_l1(
    base_image: str = DEFAULT_BASE_IMAGE,
    *,
    family: str | None = None,
    agents: str | tuple[str, ...] = "all",
) -> str:
    """Return the default-alias L1 tag, building the user's default L1 if absent.

    Used by ``terok auth`` (and the equivalent standalone-executor flow)
    to resolve a host-wide L1 image that contains every agent the user
    has configured.  If the alias already exists locally, it is trusted
    and returned as-is — the alias is reserved for the user's default
    selection (see [`l1_image_tag`][terok_executor.container.build.l1_image_tag]),
    so its contents are well-defined.  When the alias is missing the
    function builds it via [`build_base_images`][terok_executor.container.build.build_base_images]
    with ``tag_as_default=True``.

    *agents* defaults to the literal string ``"all"`` so standalone
    callers get the whole roster.  terok passes the user's configured
    ``image.agents`` value here so the alias means *"every agent the
    user has enabled"* rather than the implementation-default roster.
    """
    alias = l1_image_tag(base_image)
    if _image_exists(alias):
        return alias
    build_base_images(
        base_image=base_image,
        family=family,
        agents=agents,
        tag_as_default=True,
    )
    return alias