Normandy.Behaviours.AgentTemplate behaviour (normandy v2.2.0)

View Source

Resolves the node-local, non-serializable half of an agent's config from a stable template_id: the toolset, before/after hooks, and a client_builder that turns a credential token into an LLM client struct. Optionally :mcp_servers (which may carry authorization tokens); ConfigTemplate.rebuild/3 requires it when the original config had it set. Optionally :pinned: the security settings this node fixes for the template, [policy: ref, guardrails: list, credential: ref, ...]. A stored template whose value differs is refused (see Normandy.Agents.ConfigTemplate's "Trust boundary").

The host registers a supplement per template_id on every node at boot (same code → same supplement). Combined with the persisted non-secret template (SessionStore.{save,load}_config_template) and the node-local CredentialProvider, this reconstructs a full %BaseAgentConfig{} on any node without moving secrets or closures across the cluster.

Summary

Callbacks

Returns {:ok, supplement}, the node-local half of the config registered under template_id, or :error when there is none.

Types

supplement()

@type supplement() :: %{
  :toolset => Normandy.Tools.Toolset.t() | nil,
  :before_hooks => [term()],
  :after_hooks => [term()],
  :client_builder => (String.t() -> struct()),
  optional(:mcp_servers) => [term()],
  optional(:pinned) => keyword()
}

Callbacks

fetch(handle, template_id)

@callback fetch(handle :: term(), template_id :: String.t()) ::
  {:ok, supplement()} | :error

Returns {:ok, supplement}, the node-local half of the config registered under template_id, or :error when there is none.

Turn.Server calls it when it rebuilds a session's config from its stored template. :error is reported like a store fault: the server waits :unavailable and retries, and run/2 replies with a :session_unavailable Normandy.TurnError whose reason is {:no_template_supplement, template_id}.