Skip to content

Acp

acp

Per-task host-side ACP (Agent Client Protocol) aggregator.

Bridges a single ACP client (Zed, Toad, …) to one of several in-container agents (claude, codex, copilot, …) by namespacing models as agent:model (e.g. claude:opus-4.6) under ACP's standard category: "model" configOption.

Module map:

  • daemon — Unix-socket server, container lifecycle supervision, and the standalone terok-executor acp entry point. Owns serve_acp and the acp_socket_is_live probe used to distinguish live daemons from stale socket files.
  • roster — per-task aggregation: walks the image's ai.terok.agents label, probes each agent, and answers "what models does this container offer?" Owns ACPRoster and the vault-side list_authenticated_agents.
  • proxy — the typed bidirectional ACP mediator: implements both acp.Agent (toward the connected client) and acp.Client (toward the bound backend wrapper) on one object. Drives the bind handshake on first model pick.
  • probe — the minimal initialize + session/new handshake that extracts an agent's model roster.
  • cache — thread-safe per-agent model cache; survives reconnects, invalidated on credential rotation.
  • endpoint — the ACPEndpointStatus enum the host CLI uses to classify endpoints in terok acp list.
  • model_options — the agent:model namespace vocabulary and the typed builders + rewriter that keep the proxy's frames schema-valid.

Bind-trigger surfaces: explicit session/set_model / session/set_config_option(configId="model"), or — for clients that trust the advertised currentModelId — lazily on the first backend-needing method (e.g. session/prompt). Cross-agent switching mid-session is out of scope for v1; subsequent picks against a different agent are rejected at the protocol level.

The exports below are re-exported from terok_executor so the host-side caller (terok) doesn't have to reach into the submodules.

__all__ = list(_LAZY) module-attribute

__getattr__(name)

Resolve a re-exported name to its defining submodule on first access (PEP 562).

Source code in src/terok_executor/acp/__init__.py
def __getattr__(name: str) -> object:
    """Resolve a re-exported name to its defining submodule on first access (PEP 562)."""
    try:
        module_path = _LAZY[name]
    except KeyError:
        raise AttributeError(f"module {__name__!r} has no attribute {name!r}") from None
    value = getattr(importlib.import_module(module_path, __name__), name)
    globals()[name] = value
    return value

__dir__()

Expose the lazy names to dir() / autocompletion.

Source code in src/terok_executor/acp/__init__.py
def __dir__() -> list[str]:
    """Expose the lazy names to ``dir()`` / autocompletion."""
    return sorted({*globals(), *_LAZY})