mirror of
https://github.com/hanzoai/js-sdk.git
synced 2026-10-30 15:02:31 +00:00
Every Hanzo SDK offers the same six capabilities in the same six words. Go and
Python hang them off a client that already existed; TypeScript had none, so it
gains one — `Client`, holding an IAM client id and secret and minting its own
access token (client_secret_basic, RFC 8707 `resource`, held until 60s before
expiry, re-minted once on a 401). It takes no bearer and reads no
HANZO_API_KEY: a credential the SDK is handed is one nobody rotates and one
that says nothing about who is calling.
A refused budget and a refused policy are ANSWERS. `Answer<T>` is a union
discriminated on a literal tag — ok{value} | denied{code, reason, product?,
cures[]} | held{id, clause, reason}, all three carrying the x-request-id that
joins a call to its audit row — so `a.value` does not typecheck until the union
is narrowed and a switch that forgets an arm does not compile. The mapping from
HTTP to arm lives in one function; no capability can grow a rule of its own.
Exceptions stay for outcomes with no decision in them.
The layer is hand-written because the routes it needs are the ones the document
states as an address and not as a shape: /v1/authz/check declares neither a
request body nor a response, /v1/billing/balance and /usage declare no
response. Those three are modelled from what the served handlers read and
write, measured. It sits in src/ beside the generated tree because .generated
is the driver's manifest of what it owns — a file it does not name is this
repo's own and a regeneration never touches it — and the package entry moves to
src/hanzo.ts, which re-exports the generated src/index.ts and adds the client
beside it. Capability types are namespaced (budget.Allowance, search.Hit,
audit.Event, graph.Fact) because thirteen of the contract's names already
belong to generated models.
policy.check sends {subject, verb, path}, which is what
hanzoai/[email protected] serve/use.go reads, not the {sub, obj, act} the route's
own openapi.Describe prose states. The caller still says (sub, act, obj). The
prose and the handler are one fact and should be made to agree upstream.
Gates: `npm run wire` runs the six against a scripted fetch, asserting the
exact request each sends and decoding a realistic answer including both of
cloud's 402 bodies, a 202 hold and a 403 that is not a refusal. examples/six
drives all six in one pass against the live API and is type-checked with the
rest. The agent and tools flows are repaired for the tag names the current
document singularized (AgentsApi -> AgentApi, getTools -> getTool).
Co-authored-by: Hanzo Dev <[email protected]>
47 lines
1.7 KiB
TypeScript
47 lines
1.7 KiB
TypeScript
// agent — define one, run it, read the run back.
|
|
//
|
|
// POST /v1/agents post_agents
|
|
// POST /v1/agents/{ref}/run post_agents_by_ref_run
|
|
// GET /v1/agents/{ref}/runs get_agents_by_ref_runs
|
|
//
|
|
// `ref` is the agent's public id (agent_…) OR its org-unique name, which is why
|
|
// the run and the read below both use the name we just sent instead of waiting
|
|
// for an id to come back.
|
|
//
|
|
// The read-back is the RUN list rather than the agent record: an agent you just
|
|
// created tells you nothing you did not just send, while its runs are the part
|
|
// the server produced.
|
|
import { AgentApi } from 'hanzoai';
|
|
import { config, fail } from '../client';
|
|
|
|
// A model id is a string, so the wrong one type-checks and then fails on the
|
|
// wire. `HANZO_MODEL` overrides it (zen5-mini, zen5-coder, enso, …).
|
|
const model = process.env.HANZO_MODEL ?? 'zen5';
|
|
// Org-unique: a fixed name collides with itself on the second run.
|
|
const name = `example-greeter-${Date.now()}`;
|
|
|
|
async function main() {
|
|
const agents = new AgentApi(config());
|
|
|
|
const { data: created } = await agents.postAgent({
|
|
createAgentIn: {
|
|
name,
|
|
model,
|
|
description: 'Created by the hanzoai SDK agent example.',
|
|
instructions: 'You greet the user in one short sentence.',
|
|
},
|
|
});
|
|
console.log(`created ${created.name} (${created.id}) on ${created.model}`);
|
|
|
|
await agents.postAgentByRefRun({ ref: name });
|
|
console.log('run started');
|
|
|
|
const { data: runs } = await agents.getAgentByRefRuns({ ref: name, limit: 5 });
|
|
console.log(`${runs.runs?.length ?? 0} run(s):`);
|
|
for (const r of runs.runs ?? []) {
|
|
console.log(` ${JSON.stringify(r)}`);
|
|
}
|
|
}
|
|
|
|
main().catch(fail);
|