loom.ai.compiler

Agent compiler: offline, multi-error compilation of authored artifacts.

Turns a decoded AgentSpecV1 into an immutable AgentPlan — the only artifact-derived input every downstream stage reads (FR-014). Compilation is fully offline: no network, no credentials, no entry-point loading, and every problem found across every phase (and every spec, in AgentCompiler.compile_all()) is reported at once through a single AgentCompilationError with stable AgentErrorCode values.

Example

>>> compiler = AgentCompiler(
...     config=ai_config, registry=registry, supported_kinds=kinds
... )
>>> plan = compiler.compile(spec, source_path="agents/triage.agent.yaml")
class loom.ai.compiler.AgentCompiler(*, config, registry, supported_kinds, sql=None, native_tools=None)[source]

Bases: object

Compiles authored agent artifacts into immutable plans.

Runs every validation phase over every spec, accumulates all the issues found and raises a single AgentCompilationError, so a generator sees the whole picture at once (FR-011, SC-003).

Parameters:
  • config (AiConfig) – Deployment configuration of the AI pillar.

  • registry (UseCaseRegistry) – Use-case registry the usecase grants resolve against.

  • supported_kinds (frozenset[str]) – Capability kinds the configured engine serves, resolved by the bootstrap and passed as a plain value — the compiler never imports an engine.

  • sql (SqlConfig | None) – Data-layer configuration; None fails every sql grant with SQL_CONFIG_MISSING instead of skipping silently.

  • native_tools (NativeToolSupport | None) – Oracle answering which provider tools a model binding admits; resolved from the engine by the bootstrap, never imported here.

compile(spec, *, source_path=None)[source]

Validate one spec statically and return its immutable plan.

Performs no network access, requires no model credentials and spends no tokens.

Parameters:
  • spec (AgentSpecV1) – Decoded artifact to compile.

  • source_path (str | None) – Artifact provenance; when given, every issue points at it as its component.

Returns:

The compiled plan.

Raises:

AgentCompilationError – Aggregating one issue per problem found.

Return type:

AgentPlan

compile_all(specs)[source]

Compile a whole application, accumulating issues across specs.

Duplicate agent names are an application-level fault: a single spec is always unique by itself, so AGENT_NAME_DUPLICATE can only be detected — and is only reported — here.

Parameters:

specs (Sequence[AgentSpecV1 | DecodedSpec]) – Decoded artifacts of the application. Passing the DecodedSpec values as returned by load_specs keeps each artifact’s path, which a ./ skill library resolves against; a bare AgentSpecV1 has no path.

Returns:

One plan per spec, in input order.

Raises:

AgentCompilationError – Aggregating every issue of every spec.

Return type:

tuple[AgentPlan, …]

class loom.ai.compiler.AgentPlan(*, name, description, instructions, state=None, spec_version, inference, output, output_check=None, capabilities=(), policies, on_output=None, conversation=None, metadata, source_path=None)[source]

Bases: LoomFrozenStruct

Immutable compiled agent, the only input to every downstream stage.

Parameters:
name

Unique agent name within the application.

Type:

str

description

What the agent does; published in the A2A card.

Type:

str

instructions

Instruction blocks the agent follows, in authored order; never published.

Type:

tuple[loom.ai.compiler._plan.CompiledInstruction, …]

state

Shape of the artifact’s declared state, or None when the artifact declares neither deps_type nor deps_schema.

Type:

loom.ai.abc.StateShape | None

spec_version

Artifact format version, retained for self-description.

Type:

int

inference

Resolved model binding; one binding, no fallback (FR-019a).

Type:

loom.ai.inference.InferenceTarget

output

Structured-output contract with its built decoder.

Type:

loom.ai.compiler._plan.CompiledOutput

output_check

Resolved predicate over the answer the engine parsed, when the artifact declares output_check; None otherwise.

Type:

collections.abc.Callable[[collections.abc.Mapping[str, Any]], str | None] | None

capabilities

Compiled capabilities with resolved handles.

Type:

tuple[loom.ai.compiler._plan.CompiledUsecaseCapability | loom.ai.compiler._plan.CompiledSqlCapability | loom.ai.compiler._plan.CompiledMcpCapability | loom.ai.compiler._plan.CompiledSkillsCapability | loom.ai.compiler._plan.CompiledPythonCapability | loom.ai.compiler._plan.CompiledA2ACapability | loom.ai.compiler._plan.CompiledNativeCapability, …]

policies

Validated execution limits.

Type:

loom.ai.declarative._v1.PolicySpec

on_output

Output hook, when the artifact declares one.

Type:

loom.ai.compiler._plan.CompiledOutputHook | None

conversation

Conversation loader, when the artifact declares one.

Type:

loom.ai.compiler._plan.CompiledConversation | None

metadata

Free-form string labels carried alongside the agent.

Type:

collections.abc.Mapping[str, str]

source_path

Artifact provenance for error messages, when known.

Type:

str | None

class loom.ai.compiler.CompiledA2ACapability(*, agent, url, headers_ref=None, auth=None, include=(), exclude=())[source]

Bases: LoomFrozenStruct

Remote-agent grant, resolved against ai.a2a_agents.

The card is fetched in __aenter__ and the filter applied against the skills it really advertises — one of the declared exceptions to “strings die at compile” (invariant 3).

Parameters:
agent

Configured agent name, carried for the self-description.

Type:

str

url

Validated https:// remote agent URL, free of credentials.

Type:

str

headers_ref

Reference to deployment-resolved headers; never a secret. Applied to every request the client makes, the card fetch included.

Type:

str | None

auth

Named authentication strategy, mutually exclusive with headers_ref; None when the agent needs no credential.

Type:

loom.ai.compiler._plan.CompiledRemoteAuth | None

include

Skill names or glob patterns to expose; empty means all.

Type:

tuple[str, …]

exclude

Skill names or glob patterns to omit, applied after include.

Type:

tuple[str, …]

class loom.ai.compiler.CompiledNativeCapability(*, tool)[source]

Bases: LoomFrozenStruct

Provider tool granted to an agent, already checked against its model.

The plan carries the stable loom name and not the engine’s class: the compiler never imports an engine, and the name was validated against the binding before reaching here.

Parameters:

tool (str)

tool

Provider tool, as NATIVE_TOOLS names it.

Type:

str

class loom.ai.compiler.CompiledConversation(*, usecase, use_case, accepted)[source]

Bases: LoomFrozenStruct

Use case executed before a run that carries a conversation_id.

The compiler proves that every required, user-supplied name of the use case’s Input is one of CONVERSATION_CONTEXT_FIELDS and that the Input declares conversation_id, so the loader always knows which conversation to load. accepted is the run-time filter, computed once as CompiledOutputHook.accepted is.

Parameters:
usecase

Use-case key as written in the artifact, for messages.

Type:

str

use_case

Registered use-case type.

Type:

type[loom.core.engine.compilable.Compilable]

accepted

Internal names the Input declares; the run-time filter.

Type:

frozenset[str]

class loom.ai.compiler.CompiledInstruction(*, text, name=None, template=None)[source]

Bases: LoomFrozenStruct

One instruction block in authored order, projected onto the plan.

A bare-string artifact and a one-block artifact compile to the same single-element tuple, so nothing downstream branches on which of the two forms an artifact declared (FR-003’s instruction-side counterpart).

Parameters:
  • text (str)

  • name (str | None)

  • template (str | None)

text

Instruction text. Literal unless template names a template engine, matching text.

Type:

str

name

Optional block name, carried for compilation issues and start-up diagnostics. Never an id the engine can address.

Type:

str | None

template

Template engine text is written for, or None for a literal block that reaches the model unrendered.

Type:

str | None

class loom.ai.compiler.CompiledMcpCapability(*, server, transport='http', url=None, headers_ref=None, auth=None, timeout_ms=20000, command=None, args=(), env=(), include=(), exclude=())[source]

Bases: LoomFrozenStruct

MCP server grant, resolved against ai.mcp_servers.

The artifact names a server; the plan carries the resolved handle — the transport with its address or command, the credential reference and the deadline — so nothing downstream re-reads configuration. The URL or the command resolves in __aenter__, over the network or by spawning a subprocess: it is one of the declared exceptions to “strings die at compile” (invariant 3), and so is the filter, which is applied against the server’s real tool list.

Configuration already proved the fields coherent with the transport, so url is set exactly under http and command exactly under stdio; the engine narrows by transport, never by field.

Parameters:
server

Configured server name, carried for the self-description.

Type:

str

transport

http for a remote endpoint, stdio for a subprocess of this worker.

Type:

str

url

Validated https:// server URL, free of inline credentials; None under stdio.

Type:

str | None

headers_ref

Reference to deployment-resolved headers; never a secret.

Type:

str | None

auth

Named authentication strategy, mutually exclusive with headers_ref; None when the server needs no credential.

Type:

loom.ai.compiler._plan.CompiledRemoteAuth | None

timeout_ms

Deadline of a single call to this server.

Type:

int

command

Executable that speaks MCP over its stdin/stdout; None under http.

Type:

str | None

args

Arguments passed to command.

Type:

tuple[str, …]

env

Environment handed to the subprocess as key-sorted pairs, so the plan stays a hashable value whatever order the operator wrote.

Type:

tuple[tuple[str, str], …]

include

Tool names or glob patterns to expose; empty means all.

Type:

tuple[str, …]

exclude

Tool names or glob patterns to omit, applied after include.

Type:

tuple[str, …]

class loom.ai.compiler.CompiledOutput(*, schema, decoder)[source]

Bases: LoomFrozenStruct

Structured-output contract with a decoder built at compile time.

Interpreting the schema per response would be per-item reflection, so the decoder is constructed exactly once, at compile (research R-004, invariant 5). The decode is strict: unknown fields are rejected, which is what makes returning the validated bytes unchanged safe.

Parameters:
schema

JSON Schema object handed to the model.

Type:

collections.abc.Mapping[str, Any]

decoder

Built msgspec JSON decoder producing the answer type.

Type:

msgspec.json.Decoder[Any]

class loom.ai.compiler.CompiledOutputHook(*, usecase, use_case, accepted)[source]

Bases: LoomFrozenStruct

Use case executed once per completed run, resolved and proven feedable.

The compiler proves that every required, user-supplied name of the use case’s Input is one of HOOK_OUTPUT_FIELD or HOOK_CONTEXT_FIELDS, so the runtime never discovers a missing field at the end of a run. accepted is computed here, once, from msgspec.structs.fields: the runtime filters the offered dict to it before from_payload so a strict Command works without per-run reflection.

Parameters:
usecase

Use-case key as written in the artifact, for messages.

Type:

str

use_case

Registered use-case type, as CompiledUsecaseCapability.use_cases carries them.

Type:

type[loom.core.engine.compilable.Compilable]

accepted

Internal names the Input declares; the run-time filter.

Type:

frozenset[str]

class loom.ai.compiler.CompiledPythonCapability(*, factory_ref, factory, params=<factory>)[source]

Bases: LoomFrozenStruct

Application toolset factory resolved to an importable callable.

Parameters:
factory_ref

module:factory reference, for the self-description.

Type:

str

factory

Imported factory, called once at build as factory(context, **params) with a ToolsetContext first.

Type:

collections.abc.Callable[[…], object]

params

Keyword arguments the artifact declared for the factory. The names bind to the factory’s signature (checked at compile); the values are decoded YAML carried as-is.

Type:

collections.abc.Mapping[str, Any]

class loom.ai.compiler.CompiledRemoteAuth(*, kind, settings=())[source]

Bases: LoomFrozenStruct

Authentication strategy of one remote endpoint, resolved to name and settings.

One struct serves both outbound transports, MCP servers and A2A agents, as the one registry that builds it does: the strategy contract is httpx.Auth, which knows nothing of either protocol.

kind is separated from the rest of the auth block once, here, so the engine never re-reads configuration to find out which strategy to build. The settings are carried as ordered pairs rather than a mapping because the plan is a frozen, hashable value.

Parameters:
kind

Strategy name registered in the loom.ai.remote_auth group.

Type:

str

settings

The rest of the auth block, passed to the strategy as keyword arguments in declaration order.

Type:

tuple[tuple[str, str], …]

class loom.ai.compiler.CompiledSkillsCapability(*, library, directory, names)[source]

Bases: LoomFrozenStruct

Skill library resolved to a directory and a selected set of skill names.

Both globs and the library reference die at compile: the plan carries the absolute directory and the exact skill names granted, so the engine loads them without re-interpreting the artifact.

Parameters:
library

Library as written in the artifact, for the self-description.

Type:

str

directory

Absolute path the library resolved to.

Type:

str

names

Selected skill names, alphabetically ordered.

Type:

tuple[str, …]

class loom.ai.compiler.CompiledSqlCapability(*, connection, config, max_rows, max_result_bytes)[source]

Bases: LoomFrozenStruct

Read-only SQL access resolved to its connection configuration.

Parameters:
connection

Connection name, carried for the self-description.

Type:

str

config

Validated read-only connection configuration.

Type:

loom.core.sql.config.SqlConnectionConfig

max_rows

Maximum rows one query may return.

Type:

int

max_result_bytes

Maximum size of one query result.

Type:

int

class loom.ai.compiler.CompiledUsecaseCapability(*, keys, use_cases)[source]

Bases: LoomFrozenStruct

Granted business operations resolved against the use-case registry.

Parameters:
keys

Granted use-case keys, carried for the self-description.

Type:

tuple[str, …]

use_cases

Registered use-case types, one per key, in key order.

Type:

tuple[type[loom.core.engine.compilable.Compilable], …]

loom.ai.compiler.mcp_connection(capability)[source]

Return the connection identity of one mcp grant, its filters cleared.

A worker opens one client per MCP connection and every agent granted it works over that one client, so what makes two grants the same client is every fact the connection is made of — transport, address, credential reference, deadline, subprocess command, arguments and environment. include and exclude are per-agent views over the same connection, so they are emptied here and applied by the agent’s own toolset instead.

Parameters:

capability (CompiledMcpCapability) – Compiled grant of one agent.

Returns:

The same grant with include and exclude emptied. It is hashable, so it doubles as the key a shared client is stored under and as the capability that client is built from.

Return type:

CompiledMcpCapability

Example:

assert mcp_connection(read_only) == mcp_connection(read_write)