The round was the only writer, so a surface that streams its turn through
/v1/chat/completions had nowhere to put what was said: an answer, and a thread
list that stays empty however much you talk.
Streaming is transport and the transcript is a store. POST {prefix}/conversations
takes the same two store calls the round takes, in the same order, under the same
per-org isolation — one store, one notion of a thread, readable through the two
GETs already beside it and continuable by the round.
The four routes were written at the literal /v1/agent, which is fine for the
standalone daemon and wrong for anyone else: a host whose router already spends
that address had no way to take this surface at all. It now composes off one
prefix — MountAt(app, prefix, ...) registers there, Mount(app, ...) is
MountAt at DefaultPrefix, so the daemon is unchanged and the host says where.
A prefix that is not an absolute path is refused at mount rather than
registering routes nothing can reach, and a trailing slash is the same address.
Nothing in the round reads the prefix, so the doc comments that named the old
literal now name the route.
Every workflow in this repo was deleted and nothing replaced them, so the sole
producer of ghcr.io/hanzoai/agent no longer existed anywhere and there was no CI
at all. That is silent staleness: no failing run, just an artifact that quietly
stops being rebuilt.
.github/workflows now holds sync.yml and nothing else, copied verbatim from
universe infra/ci/sync.yml. It replaces a mirror-nudge file that POSTed
/api/v1/repos/.../mirror-sync and described git.hanzo.ai as a downstream pull
mirror; the forge is canonical and refs are pushed to it. Without it a
.hanzo/workflows file has nothing to trigger it.
deploy.yml restores docker-publish.yml. Two things could not come across as-is:
- Its body was one `uses: hanzoai/.github/.github/workflows/docker-build.yml@main`.
A private cross-repo reusable workflow does not resolve on the forge, so the
amd64 build/push is vendored inline, as hanzoai/bot already does.
- It built `context: control-plane`, and no control-plane directory exists on
main; the orchestrator was hoisted to the root module in v0.1.1 and the
directory survives only on the unmerged consolidate/held-work branch.
Restoring that context restores a build that cannot run. It builds
Dockerfile.sdk — the one Dockerfile on main whose context exists, previously
referenced by nothing despite sdk-* tags being live on the image — and tags it
sdk-* so the two Dockerfiles cannot collide in one tag namespace.
The tag rule is shell rather than docker/metadata-action, so it is two readable
lines with no action semantics to guess at, and it was tested standalone:
v* -> :sdk-X.Y.Z + :sdk-latest, main -> :sdk-sha-<sha7> + :sdk-latest.
ci.yml replaces tests.yml and test.yml, which checked the same things twice, the
second with `|| true` on most steps so it could not fail. It gates what is
verifiable on main today — gofmt/build/vet/test for the Go module, and the
locked install plus `import agents` for Python, which is what Dockerfile.sdk's
builder stage and HEALTHCHECK do. It deliberately does not run ruff, mypy or
pytest: `make sync` cannot resolve (requires-python >=3.9, a transitive numpy
needs >=3.10), ruff reports 2337 errors, and pytest INTERNALERRORs at collection
because a test module sys.exit(1)s at import. tests.yml ran `make sync` first in
all five jobs, so none of them could have gone green. Those four are written down
in LLM.md as work rather than shipped as a permanently red check, which gets read
as broken CI instead of broken code.
gofmt over eight files so that gate is real instead of omitted; build, vet and
test re-run green afterwards. Alignment whitespace only.
Not restored, with reasons in LLM.md: docs.yml (mkdocs gh-deploy is GitHub Pages,
never published anything, and Pages is not how we serve sites), publish.yml
(pointed at the PyPI package hanzoai/python-sdk owns; deliberately removed
earlier), issues.yml (upstream's stale-issue bot).
LLM.md documents the chain, records the four broken Python checks, and states
the open question this repo actually has: nothing in hanzoai/universe references
ghcr.io/hanzoai/agent, so the image has no consumer and no App CR.
.gitignore ignored .hanzo/ under an "# IDE" heading, which silently dropped
both new workflows from the commit — the same silent-staleness shape this is
meant to remove, one level down. Narrowed to .hanzo/* with .hanzo/workflows/
re-included.
Co-authored-by: Hanzo Dev <[email protected]>
The Completer interface + preset/round/agent exposed sashabaranov/go-openai
types, forcing every embedder (cloud) to stay on upstream go-openai or hit a
type mismatch at the hz.Mount boundary. Import the fork (github.com/hanzoai/
go-openai v1.41.0) directly so the whole stack shares ONE openai type set — no
replace, no per-consumer copy. Build + tests green.
Co-authored-by: Hanzo Dev <[email protected]>
The round masked every completion failure as a 502 "agent: completion: ...", so a
caller with no credits saw an opaque gateway error instead of the real billing
message. Add UpstreamError{Status,Body}: a Completer returns it for a caller-facing
4xx, and the round passes the status + body through unchanged (a client shows "add
credits"). A non-4xx failure (5xx/transport) stays a 502 — that IS a gateway fault.
Tests: 402 verbatim passthrough + 5xx-stays-502.
The agent IS github.com/hanzoai/agent — package agent at repo root. POST /v1/agent
+ presets + per-org conversation history (zip+orm). Importable directly by ai/cloud.