The same fact lived in nine hand-kept places and they disagreed about 58
proposals. This makes the file the only authority and everything else a
projection of it.
ONE VOCABULARY, in vocabulary.json, read by the lint, the generator and the
doc site. Three words, each answering "can I rely on this?" and nothing else:
Draft nothing implements it yet, or its own text says the work is ahead
Final the thing it specifies exists in the code
Living governance and process: amended in place, never finished
Eight spellings folded in. `Active` was the one doing real damage: on four Meta
and Process HIPs it meant living, on 48 others it meant shipped, and one word
doing two jobs is why nothing agreed. `Review` and `Last Call` described a
queue rather than a document, and a proposal under review is still one you
cannot build against.
STATUS IS EARNED, not declared. Every Standards Track proposal was checked
against the estate: 235 have their implementation, and eight do not and say so.
HIP-0095 carries a twelve-week plan ending at "mainnet launch" and has no code.
HIP-0525 specifies an all-machine meeting while apps/meet refuses a machine a
seat. HIP-0078 names a prover at a "planned location" that does not exist.
HIP-0114 is the cog.* procedure set, and that procedure set returns nothing.
HIP-0003, HIP-0102, HIP-0305 and HIP-0901 likewise: looked, found nothing.
SIXTEEN TOMBSTONES DELETED rather than statused. A dead document kept alive in
the index is one concept wearing two names. Each successor exists and carries
the text, and the 90 references that pointed at a tombstone now point at it --
whole markdown links at once, so link text cannot outlive its target. Three of
the sixteen were pure compatibility: HIP-0028 specified a migration off a
Bitnami chart, HIP-0029 dual-engine MySQL, HIP-0030 a schema-deprecation
policy. We ship no backwards compatibility, so we do not specify it.
Nine links were already broken and had been since the corpus was renumbered:
short-form ./hip-5.md paths that never existed. Two named the wrong proposal --
one cited "HIP-1: Hanzo Multimodal Models" where multimodal is HIP-0003.
THE GENERATOR REFUSES more often than it writes, because a short table looks
exactly as authoritative as a complete one. Rows must equal files; a shrinking
index must say by how much; a section whose input is missing is an error, not
an omission; nothing is written until all of it validates. Its self-test breaks
the corpus eleven ways and requires each to refuse AND leave README byte-
identical -- a guard that reports after half-writing has protected nothing.
The lint's second implementation of the same comparison is gone.
The site rendered 52 proposals with a fallback grey badge and counted them
toward none of its three stat tiles, because docs/lib/source.ts declared a
status union by hand: it admitted a `Stagnant` no file has ever carried and
omitted the one 52 files did. Five components each kept their own copy of the
colours. All six now read vocabulary.json.
6.7 KiB
hip, title, author, type, category, status, created, requires, capability
| hip | title | author | type | category | status | created | requires | capability |
|---|---|---|---|---|---|---|---|---|
| 1210 | Agents — Define, Run, Keep the Run | Hanzo AI | Standards Track | Application | Final | 2026-08-20 | HIP-0139, HIP-0026, HIP-0106 | agents |
HIP-1210: Agents — Define, Run, Keep the Run
Abstract
/v1/agents is autonomous agents for an org: define one as a model, a system
prompt and a set of tool names; run it through the same in-process AI client the
rest of the console uses; keep every run (apps/agents/agents.go:1-14). The
same capability carries the tool-calling conversation surface and the coding
engine — one autonomous run against a repository, from prompt to pushed branch.
It is implemented in hanzoai/cloud at apps/agents.
Motivation
An agent that can be defined but whose runs evaporate is a demo. This capability
exists so a run — its input, its steps, its tool calls, its outcome — is a
record the org owns, in the org's own file, attributable to a principal and a
price. The conversation and coding surfaces were once separate apps beside it;
the code has already merged them (apps/agents/conversation.go:123), and this
HIP states the one surface that merge settles.
Specification
The key words MUST, MUST NOT, SHOULD, SHOULD NOT and MAY are to be interpreted as in RFC 2119.
The surface
Every address is under /v1/agents. The definition-and-run collection is typed:
list/create at the root, get/update/delete at /:ref, POST /:ref/run,
GET /:ref/runs (apps/agents/agents.go:17-27), beside the typed session
surface (apps/agents/sessions.go). The conversation group — one tool-calling
round, its presets, its threads — answers at /v1/agents/chat,
/v1/agents/chat/presets and /v1/agents/chat/conversations[/:id]: UNDER the
collection root, not at it. POST /v1/agents is already the typed create, and
one address MUST NOT answer two operations, so the round takes a sub-path and
the collection keeps its root. Its four operations stay untyped until the
upstream seams they name land, and each declares its contract in prose beside
the wire (apps/agents/conversation.go, const chat). POST /v1/agents/coding
starts one autonomous coding run, typed, answering 202 with the run's handle
(plugin/agents/coding.go:133).
Both folds have landed and hanzoai/cloud openapi/misfiled.txt carries no
line for this capability. The coding endpoint moved off /v1/coding together with
the chat server's dispatch of it, in one release, because that op's reachability
is the only way a chat turn gets to a sandbox (plugin/agents/coding.go:128-131).
The conversation surface moved off /v1/agent when the orchestrator stopped
owning its own address (see Upstreams); the manifest row is one prefix again.
One store, one org, one file
The capability owns the agents per-org store: each org's agents, runs,
sessions, events, targets and claim keys live in that org's own SQLite at
{DataDir}/orgs/{slug}/agents.db, opened through cloud.OrgStore
(apps/agents/agents.go:353, apps/agents/store.go:112). The file is named
from the gateway-minted org (HIP-0026) and nothing else; every route into
storage begins at the validated principal, and tenancy.go is the only file
that resolves a store (apps/agents/tenancy.go:13-30). A request without a
non-empty org MUST be refused; the org is never read from a body. The store
holds definitions and run I/O only — tool credentials live in KMS by reference.
The run is the billed unit
An agent run is metered as a flat per-run fee — $1.00 by default, set by
CLOUD_AGENT_FEE_CENTS, zero making runs free — gated before the run and
debited after it through the shared per-org resource meter into the commerce
ledger, attributed under kind agent (apps/agents/agents.go:99-111,971-975, 1025). The fee is per run and not per token, deliberately: the in-process AI
client returns no token counts, so token pricing here would be fabricated. The
token spend of a run's completions is ai's account, joined to this run by its
id (types.ChatRequest.RunID).
Events and observability
The capability publishes no events on the tenant bus, so a customer's webhooks
receive nothing from it. Live session activity is an in-process stream a client
subscribes to over the session surface (apps/agents/sessions_stream.go:60) —
a rendezvous, not a queue; the durable owner of a routed run is the tasks
engine (apps/agents/mailbox.go:13-20). Beyond the request span every route
gets, it emits the per-run trace: an agent.run root span with nested
agent.step and agent.tool spans, shipped over ZAP to o11y
(apps/agents/agents.go:66-68).
Stage
ga. The manifest row carries no stage field, which is ga by HIP-0139 §8.
Upstreams
The conversation orchestrator embeds github.com/hanzoai/agent v1.0.6 (MIT;
its LICENSE carries OpenAI's copyright — the agents-SDK lineage the module
forks), which registers the conversation routes and owns their per-org
history (apps/agents/conversation.go). The module composes those four routes
off ONE prefix: MountAt(app, prefix, ...) registers under the prefix it is
given and Mount(app, ...) is MountAt at DefaultPrefix — so the standalone
daemon keeps /v1/agent and whoever composes the router chooses the address
cloud serves. Nothing in the round reads the prefix. Nothing else here derives
from an OSS upstream.
Rationale
The alternative the codebase actually had was three apps — agent, agents,
coding — one concept under a pair of names a reader could not tell apart, plus
an endpoint whose app held no store of its own. HIP-0139 §2.4 decides the number
(the collection is plural), and §7.1 decides the folds: a coding run is an agent
run, recorded in the same per-org file, billed by the same meter, so a second
root was a second name for one thing.
Security Considerations
A wrong tenant resolution here is worse than a data leak: a coding run holds a
sandbox with a push credential, so cross-tenant reach is cross-tenant write
access to repositories. Three properties close it. Isolation is the file — a
mis-resolved store fails closed as an empty read, never a neighbour's rows
(apps/agents/store.go). The routed-run mailbox keys every offer by
(org, target), so a claim for one tenant's machine cannot surface another's
run (apps/agents/mailbox.go:22-25). And a routed run crosses to the executing
machine carrying no credential at all: the machine authenticates with its own
already-held keys, and the process gives run credentials back at shutdown
(apps/agents/mailbox.go:27-31, plugin/agents/coding.go).
References
- HIP-0026 — Identity and Access Management
- HIP-0106 — Hanzo Plugin Contract
- HIP-0139 — Capability
- HIP-1062 — Tasks — The Durable Run
Copyright
Released under CC0 1.0 Universal Public Domain Dedication.