It keeps a log, not a transcript
dal init agent wires evolve.md by default — a conversation and action log the agent reads back each turn. Your session becomes its memory instead of dying with the tab.
Vibes live on in agentic.
A chat session ends and takes the work with it. An agent does not. Same
vibe coding — you describe what you want and let a model build it — but
the result is a folder on your disk with a name your coordinator can
call tomorrow. Distributed Agent Language is what makes
that difference structural: its dal toolchain scaffolds a
real agent project in two commands, and everything after that is you
describing the work.
Your agents live in ./my-agents/ on the host, beside your other folders — bind-mounted read-write so the agent can keep its own runtime state. The folder name is the project root COO uses, so name it like the job: my_shopper, prep_lists, lead_digger.
cd my-agents mkdir prep_lists cd prep_lists
dal init agent wires evolve.md by default — a conversation and action log the agent reads back each turn. Your session becomes its memory instead of dying with the tab.
The folder is yours, mounted into the container. Swap the COO image on the next version and your agent is still sitting there, unchanged.
Once it has a capability_id, the coordinator invokes it by name. You never re-explain what it is — to a chat window or to a new model.
Personality can live in copy, tone, and presentation. Authority should live in explicit actions, durable files, and clear contracts.
The vibe is not dead. It is finally getting a contract.
Read that as build instructions and you get this page. The separation
it argues for is the same one dal init hands you: your
taste stays in what the agent is for and how it talks, while its
authority is pinned to things you can open — a port, a health check, a
route with a named input, a log of what it did. So the rest of this
page is the contract, in its smallest honest form.
dal init builds a working DAL project. It does not know
about your coordinator, so you add the parts that make it invokable —
this is the “and we go from there” of the flow, and it is exactly
what to hand the LLM.
capability.json — capability_id, a label, dispatch_kind: external_http, and a default_agent_portPOST /task route in server.dal — COO posts {"description": "…"} there, and reads GET /health to know you are upscripts/start.sh — COO lazy-starts this path; copy the shape from my-agents/README.md so the hub environment stays isolated
Pick a port first. The serve scaffold defaults to
4040 — which is COO itself. Bundled agents already hold
8092, 8094, 8095, 8098 and 8099. Choose something else (8101 is a
fine start) and set it in the agent's own .env.
COO merges each capability.json into the invoke registry
when it loads, so a new agent needs a restart before it exists:
docker compose -f docker-compose.dist.yml \
restart coo
curl -s http://127.0.0.1:4040/api/targets
curl -s -X POST \
http://127.0.0.1:4040/api/coordinator/invoke \
-H 'Content-Type: application/json' \
-d '{"capability_id":"prep_lists",
"input":{"description":"Draft Friday prep"}}'
Your agent cannot take over a bundled id. Rows from
my-agents/ are merged in, never replacements — so
nothing you write can quietly shadow a shipped capability.
I have a DAL project inmy-agents/prep_lists—dal init agentanddal init servehave both run, so there is anagent.dal, anagent.tomlwith evolve wired, and aserver.dalwith@routehandlers. Build me an agent that takes a service date and turns our standing prep notes into one packet the opening crew can read. Keep its own notes in the project folder. Then make it callable from my coordinator: add acapability.jsonwith capability idprep_listson port 8101, add aPOST /taskroute that accepts{"description": "…"}, keepGET /health, and addscripts/start.shfollowing the shape inmy-agents/README.md. Do not touch the bundled agents.
Same shape as any good setup prompt: what the work is, where it keeps state, and the limits. The difference is that this one ends with a process that has a name — so the next time you want Friday's packet, you call it instead of re-describing it.