Input formats
Read a spec in from another wire format, the input mirror of an encoder. Docker's agent spec ships as a worked example.
An input format is the input mirror of an encoder. Where an encoder renders the run's event stream into another wire format on the way out, an input format reads the spec document in from another wire format before anything runs. JSON and YAML are not baked into heddle: they are the two builtin formats in the same registry a plugin writes into.
A format is one function: raw text in, an Agent Spec document out, in the
component_type / $component_ref vocabulary flows are written in.
Everything past that point (validation, compilation, execution) never learns which
format the bytes arrived in. That is also the escape hatch for a format whose native
schema is not Agent Spec at all: its parse translates to that vocabulary, and heddle
needs nothing else.
Three things select one, all naming the same registry:
- A file extension.
.jsonand.yaml/.ymlare claimed by the builtins; a plugin's format claims its own. An unclaimed extension is read as JSON, as it always was. - A name on the CLI.
--format <name>onheddle runandheddle validateoverrides the extension. - A field in the request.
"format"beside a string"flow"or a"flowPath"inPOST /v1/runsand/v1/validate.GET /v1/capabilitieslists what a server accepts underformats.
Writing one
A format is declared by an in-process plugin, an ES module passed to --plugin:
import { parse as parseToml } from "./toml.js";
export default {
name: "toml-format",
version: "1.0.0",
formats: [
{
name: "toml",
extensions: [".toml"],
parse: (text) => parseToml(text), // → an Agent Spec document
},
],
};heddle run flow.toml --plugin ./toml-format.mjs
heddle run flow.txt --plugin ./toml-format.mjs --format tomlIn-process only, deliberately: parse sits on the loading path, before any plugin
process could usefully exist, so a manifest-declared subprocess
plugin has no way to provide one.
A different spec, not just a different encoding
parse is not required to be a decoder. It takes text and returns an Agent Spec
document, and nothing says the text had to be Agent Spec in another syntax. A format for a
foreign agent spec is a translator, and
examples/docker-agent
is a worked one: it reads a
Docker agent file
(cagent's YAML configuration), with its named agents, models table and instruction,
and emits the Agent Spec flow it describes:
heddle validate examples/docker-agent/agent.yaml \
--format docker-agent --plugin ./examples/docker-agent/format.mjsThe example also shows where translation should stop: a construct it cannot carry
(toolsets, sub_agents, a provider heddle has no client for) is refused by name
rather than dropped, because a capability silently discarded would run a different agent
than the file describes. The way past those refusals is the rest of the plugin: the same
module can declare tools, providers and custom component types beside its format, and
the translator emits them.
Names and extensions are claims, checked at load. json and yaml and their extensions
are reserved, because what a .yaml file means may not depend on which plugins are
loaded, and two plugins claiming the same name or extension are refused with both names
rather than resolved by load order.