The example asked about "graph:hanzoai/python-sdk". The route parses `path` and
refuses one whose first segment is not the caller's own org, so that question
could only ever come back refused. It now reads the org off the wallet account
the budget step already named — the same construction go-sdk and js-sdk use —
and asks about "<org>/graph".
Co-authored-by: Hanzo Dev <[email protected]>
Six capabilities hang off the client as first-class methods rather than as
generated operations a caller assembles for itself: budget (allowance, wallet,
tier, charged ledger), policy (one authorization question), audit (the org's
trail, read-only by construction), search (one ranked set over every corpus),
kb (the corpus you write and the connectors that fill it), and graph
(assertions with provenance and time). The same six words, the same method
names and the same answer type as the Go and TypeScript clients.
A refused budget and a refused policy are answers. Answer is a union of three
frozen types with a discriminant — Ok, Denied, Held — so a caller reads
"denied, and here is why" without catching, and reading .value off a refusal
raises it rather than handing back a half-answer. One rule maps HTTP to an arm:
a 202 whose body says held is a hold, 402 and the four refusal codes on 403 are
refusals, and a status that decided nothing raises. Every arm carries the
x-request-id, which is the word audit.Event.request uses too — the join that
lets a caller find the row the server wrote for the call it just made.
Auth is IAM and nothing else. The client exchanges its own clientId and
clientSecret for a short-lived access token (client_credentials,
client_secret_basic, RFC 8707 resource), holds it until shortly before expiry,
and re-mints once on a 401. It no longer accepts a bearer, so HANZO_API_KEY,
Configuration(access_token=) construction and result() are gone rather than
deprecated: two ways to hold one credential is the defect this removes. as_
still scopes to a subject and now mints from the client's own token.
hanzoai resolves its public names on first use, and the capability layer is
written over the wire rather than over the generated client, so importing the
six costs milliseconds instead of 2,700 generated modules.
ruff and ruff format are clean, mypy is clean over the thirteen new and changed
modules, and 106 tests pass. The two failures in test_smoke are the ToolsApi
name the current document no longer carries, and they fail at HEAD too. The
mint was exercised against the live hanzo.id, which answers invalid_client for
a credential it does not know; the six were exercised against a stub asserting
the exact request and decoding the shapes cloud's own schemas declare.
examples/six runs the whole thing as a caller would.
Co-authored-by: Hanzo Dev <[email protected]>