hanzoai 8.5.623 → 8.5.640

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (168) hide show
  1. package/README.md +64 -9
  2. package/dist/answer.js +20 -10
  3. package/dist/api/ai-api.d.ts +280 -14
  4. package/dist/api/ai-api.js +315 -29
  5. package/dist/api/auto-api.d.ts +64 -0
  6. package/dist/api/auto-api.js +77 -0
  7. package/dist/api/billing-api.d.ts +4 -4
  8. package/dist/api/billing-api.js +4 -4
  9. package/dist/api/git-api.d.ts +30 -0
  10. package/dist/api/git-api.js +59 -0
  11. package/dist/api/task-api.d.ts +55 -0
  12. package/dist/api/task-api.js +71 -0
  13. package/dist/client.d.ts +3 -2
  14. package/dist/client.js +4 -3
  15. package/dist/esm/answer.js +20 -10
  16. package/dist/esm/api/ai-api.d.ts +280 -14
  17. package/dist/esm/api/ai-api.js +315 -29
  18. package/dist/esm/api/auto-api.d.ts +64 -0
  19. package/dist/esm/api/auto-api.js +77 -0
  20. package/dist/esm/api/billing-api.d.ts +4 -4
  21. package/dist/esm/api/billing-api.js +4 -4
  22. package/dist/esm/api/git-api.d.ts +30 -0
  23. package/dist/esm/api/git-api.js +59 -0
  24. package/dist/esm/api/task-api.d.ts +55 -0
  25. package/dist/esm/api/task-api.js +71 -0
  26. package/dist/esm/client.d.ts +3 -2
  27. package/dist/esm/client.js +3 -2
  28. package/dist/esm/hanzo.d.ts +3 -0
  29. package/dist/esm/hanzo.js +6 -0
  30. package/dist/esm/models/agent-target-view.d.ts +6 -0
  31. package/dist/esm/models/ai-chat-in.d.ts +42 -0
  32. package/dist/esm/models/ai-chat.d.ts +37 -0
  33. package/dist/esm/models/ai-decide-in.d.ts +50 -0
  34. package/dist/esm/models/ai-decide-in.js +14 -0
  35. package/dist/esm/models/ai-decision-sides.d.ts +4 -5
  36. package/dist/esm/models/{ai-decision-sides-false.d.ts → ai-decision.d.ts} +15 -2
  37. package/dist/esm/models/ai-decision.js +14 -0
  38. package/dist/esm/models/ai-decisions-answer.d.ts +2 -3
  39. package/dist/esm/models/ai-decisions-choice.d.ts +4 -8
  40. package/dist/esm/models/ai-decisions-noul.d.ts +2 -3
  41. package/dist/esm/models/ai-decisions-request.d.ts +2 -3
  42. package/dist/esm/models/ai-decisions-score.d.ts +4 -5
  43. package/dist/esm/models/ai-model.d.ts +73 -0
  44. package/dist/esm/models/ai-model.js +14 -0
  45. package/dist/esm/models/{ai-decisions-choice-criteria-value.d.ts → ai-models.d.ts} +15 -2
  46. package/dist/esm/models/ai-models.js +14 -0
  47. package/dist/esm/models/ai-price.d.ts +48 -0
  48. package/dist/esm/models/ai-price.js +14 -0
  49. package/dist/esm/models/ai-receipt.d.ts +60 -0
  50. package/dist/esm/models/ai-receipt.js +14 -0
  51. package/dist/esm/models/ai-routing-reward-request.d.ts +54 -0
  52. package/dist/esm/models/ai-routing-reward-request.js +24 -0
  53. package/dist/esm/models/auto-automation-in.d.ts +6 -0
  54. package/dist/esm/models/auto-automation-patch.d.ts +6 -0
  55. package/dist/esm/models/auto-automation-run.d.ts +14 -1
  56. package/dist/esm/models/auto-automation.d.ts +6 -0
  57. package/dist/esm/models/auto-post-result.d.ts +36 -0
  58. package/dist/esm/models/auto-post-result.js +14 -0
  59. package/dist/esm/models/auto-review-in.d.ts +36 -0
  60. package/dist/esm/models/auto-review-in.js +14 -0
  61. package/dist/esm/models/git-workspace-view.d.ts +36 -0
  62. package/dist/esm/models/git-workspace-view.js +14 -0
  63. package/dist/esm/models/iam-user.d.ts +1 -1
  64. package/dist/esm/models/index.d.ts +14 -2
  65. package/dist/esm/models/index.js +14 -2
  66. package/dist/esm/models/provider-github-import-out.d.ts +6 -0
  67. package/dist/esm/models/provider-github-repo-item.d.ts +6 -0
  68. package/dist/esm/models/task-comment-list.d.ts +31 -0
  69. package/dist/esm/models/task-comment-list.js +14 -0
  70. package/dist/esm/models/task-comment.d.ts +42 -0
  71. package/dist/esm/models/task-comment.js +14 -0
  72. package/dist/esm/transport.d.ts +4 -0
  73. package/dist/esm/transport.js +188 -0
  74. package/dist/esm/usage.d.ts +98 -0
  75. package/dist/esm/usage.js +161 -0
  76. package/dist/hanzo.d.ts +3 -0
  77. package/dist/hanzo.js +16 -1
  78. package/dist/models/agent-target-view.d.ts +6 -0
  79. package/dist/models/ai-chat-in.d.ts +42 -0
  80. package/dist/models/ai-chat.d.ts +37 -0
  81. package/dist/models/ai-decide-in.d.ts +50 -0
  82. package/dist/models/ai-decide-in.js +15 -0
  83. package/dist/models/ai-decision-sides.d.ts +4 -5
  84. package/dist/models/{ai-decision-sides-false.d.ts → ai-decision.d.ts} +15 -2
  85. package/dist/models/ai-decision.js +15 -0
  86. package/dist/models/ai-decisions-answer.d.ts +2 -3
  87. package/dist/models/ai-decisions-choice.d.ts +4 -8
  88. package/dist/models/ai-decisions-noul.d.ts +2 -3
  89. package/dist/models/ai-decisions-request.d.ts +2 -3
  90. package/dist/models/ai-decisions-score.d.ts +4 -5
  91. package/dist/models/ai-model.d.ts +73 -0
  92. package/dist/models/ai-model.js +15 -0
  93. package/dist/models/{ai-decisions-choice-criteria-value.d.ts → ai-models.d.ts} +15 -2
  94. package/dist/models/ai-models.js +15 -0
  95. package/dist/models/ai-price.d.ts +48 -0
  96. package/dist/models/ai-price.js +15 -0
  97. package/dist/models/ai-receipt.d.ts +60 -0
  98. package/dist/models/ai-receipt.js +15 -0
  99. package/dist/models/ai-routing-reward-request.d.ts +54 -0
  100. package/dist/models/ai-routing-reward-request.js +27 -0
  101. package/dist/models/auto-automation-in.d.ts +6 -0
  102. package/dist/models/auto-automation-patch.d.ts +6 -0
  103. package/dist/models/auto-automation-run.d.ts +14 -1
  104. package/dist/models/auto-automation.d.ts +6 -0
  105. package/dist/models/auto-post-result.d.ts +36 -0
  106. package/dist/models/auto-post-result.js +15 -0
  107. package/dist/models/auto-review-in.d.ts +36 -0
  108. package/dist/models/auto-review-in.js +15 -0
  109. package/dist/models/git-workspace-view.d.ts +36 -0
  110. package/dist/models/git-workspace-view.js +15 -0
  111. package/dist/models/iam-user.d.ts +1 -1
  112. package/dist/models/index.d.ts +14 -2
  113. package/dist/models/index.js +14 -2
  114. package/dist/models/provider-github-import-out.d.ts +6 -0
  115. package/dist/models/provider-github-repo-item.d.ts +6 -0
  116. package/dist/models/task-comment-list.d.ts +31 -0
  117. package/dist/models/task-comment-list.js +15 -0
  118. package/dist/models/task-comment.d.ts +42 -0
  119. package/dist/models/task-comment.js +15 -0
  120. package/dist/transport.d.ts +4 -0
  121. package/dist/transport.js +195 -0
  122. package/dist/usage.d.ts +98 -0
  123. package/dist/usage.js +174 -0
  124. package/package.json +1 -1
  125. package/src/answer.ts +23 -10
  126. package/src/api/ai-api.ts +503 -29
  127. package/src/api/auto-api.ts +117 -0
  128. package/src/api/billing-api.ts +4 -4
  129. package/src/api/git-api.ts +68 -0
  130. package/src/api/task-api.ts +101 -0
  131. package/src/client.ts +3 -2
  132. package/src/hanzo.ts +18 -0
  133. package/src/models/agent-target-view.ts +6 -0
  134. package/src/models/ai-chat-in.ts +48 -0
  135. package/src/models/ai-chat.ts +45 -0
  136. package/src/models/ai-decide-in.ts +54 -0
  137. package/src/models/ai-decision-sides.ts +4 -7
  138. package/src/models/{ai-decision-sides-false.ts → ai-decision.ts} +17 -2
  139. package/src/models/ai-decisions-answer.ts +2 -5
  140. package/src/models/ai-decisions-choice.ts +4 -10
  141. package/src/models/ai-decisions-noul.ts +2 -5
  142. package/src/models/ai-decisions-question.ts +0 -3
  143. package/src/models/ai-decisions-request.ts +2 -5
  144. package/src/models/ai-decisions-score.ts +4 -7
  145. package/src/models/ai-model.ts +81 -0
  146. package/src/models/{ai-decisions-choice-criteria-value.ts → ai-models.ts} +17 -2
  147. package/src/models/ai-price.ts +54 -0
  148. package/src/models/ai-receipt.ts +66 -0
  149. package/src/models/ai-routing-reward-request.ts +63 -0
  150. package/src/models/auto-automation-in.ts +6 -0
  151. package/src/models/auto-automation-patch.ts +6 -0
  152. package/src/models/auto-automation-run.ts +16 -1
  153. package/src/models/auto-automation.ts +6 -0
  154. package/src/models/auto-post-result.ts +42 -0
  155. package/src/models/auto-review-in.ts +42 -0
  156. package/src/models/git-workspace-view.ts +42 -0
  157. package/src/models/iam-user.ts +1 -1
  158. package/src/models/index.ts +14 -2
  159. package/src/models/provider-github-import-out.ts +6 -0
  160. package/src/models/provider-github-repo-item.ts +6 -0
  161. package/src/models/task-comment-list.ts +39 -0
  162. package/src/models/task-comment.ts +48 -0
  163. package/src/transport.ts +190 -0
  164. package/src/usage.ts +203 -0
  165. /package/dist/esm/models/{ai-decision-sides-false.js → ai-chat-in.js} +0 -0
  166. /package/dist/esm/models/{ai-decisions-choice-criteria-value.js → ai-chat.js} +0 -0
  167. /package/dist/models/{ai-decision-sides-false.js → ai-chat-in.js} +0 -0
  168. /package/dist/models/{ai-decisions-choice-criteria-value.js → ai-chat.js} +0 -0
package/README.md CHANGED
@@ -67,7 +67,10 @@ type Completion = { choices?: Array<{ message?: { content?: string } }> };
67
67
 
68
68
  async function main() {
69
69
  const { data } = await new AiApi(config).postChatCompletions({
70
- data: { model: 'zen5', messages: [{ role: 'user', content: 'Say hello in five words.' }] },
70
+ openaiChatCompletionRequest: {
71
+ model: 'zen5',
72
+ messages: [{ role: 'user', content: 'Say hello in five words.' }],
73
+ },
71
74
  });
72
75
  const reply = data as unknown as Completion;
73
76
  console.log(reply.choices?.[0]?.message?.content);
@@ -82,8 +85,8 @@ those, construct a `Configuration` with no `accessToken` and the client sends no
82
85
  `Authorization` header.
83
86
 
84
87
  A generated call that gets a status outside 2xx throws axios's `AxiosError`,
85
- with the status on `err.response.status`, after one request; nothing is
86
- retried. A 200 whose body is an error object resolves like any other 200, and a
88
+ with the status on `err.response.status`, after one request — or, for a usage
89
+ refusal, its own class (below). Nothing is retried. A 200 whose body is an error object resolves like any other 200, and a
87
90
  completion's content comes back as the server sent it, `<think>` blocks
88
91
  included.
89
92
 
@@ -94,6 +97,55 @@ environment, and mints its own token rather than being handed one.
94
97
  `GET /v1/iam/oauth/userinfo` is how you check a token: it answers the token's
95
98
  identity, or `401 {"error":"invalid_token"}`. That is the `hello` flow below.
96
99
 
100
+ ## Plan usage
101
+
102
+ Every AI answer says how it was paid for in `X-Hanzo-*` headers. `readUsage`
103
+ reads them off an axios response, a fetch `Response` or a plain record; an
104
+ absent header reads `undefined`.
105
+
106
+ ```ts
107
+ import { AiApi, Configuration, ModelCapError, UsageLimitError, readUsage } from 'hanzoai';
108
+
109
+ const ai = new AiApi(new Configuration({ accessToken: process.env.HANZO_API_KEY }));
110
+ try {
111
+ const res = await ai.postChatCompletions({
112
+ openaiChatCompletionRequest: { model: 'anthropic/claude-sonnet-4.5', messages },
113
+ });
114
+ readUsage(res.headers); // { usage: 'ok', usageClass: 'premium', paidBy: 'credits', served: '…', fallback: undefined, reason: undefined }
115
+ } catch (err) {
116
+ if (err instanceof ModelCapError) retry(err.fallback);
117
+ else if (err instanceof UsageLimitError) offer(err.actions);
118
+ else throw err;
119
+ }
120
+ ```
121
+
122
+ A 402 or 429 refusal nothing may pay for throws a subclass of
123
+ `UsageLimitError`, which carries `status`, `code`, `type`, `usageClass`,
124
+ `model`, `fallback`, `resetsAt`, `upgradeUrl`, `actions`, `request` (the
125
+ `x-request-id`) and, from a generated call, `cause` (the `AxiosError`):
126
+
127
+ | class | status | `code` |
128
+ |---|---|---|
129
+ | `PlanAllowanceUsedError` | 402 | `plan_allowance_used` |
130
+ | `PaidPlanRequiredError` | 402 | `paid_plan_required` |
131
+ | `FreePlanCapError` | 429 | `free_plan_cap` |
132
+ | `ModelCapError` | 402 | `model_cap` |
133
+ | `UsageCapExceededError` | 429 | `usage_cap_exceeded` |
134
+ | `InsufficientBalanceError` | 402 | `insufficient_balance` |
135
+
136
+ Any other refusal stays an `AxiosError`, as does a refusal through an `*Api`
137
+ built without the package's `Configuration` or with an axios instance of your
138
+ own. A `responseType: 'stream'` JSON body is read for the code, within the
139
+ request's `signal` and `timeout`, and left whole on `err.cause.response.data`. `Client`'s six throw
140
+ these where they would throw `answer.Fault`; a 402 there is still the `denied`
141
+ arm.
142
+
143
+ The routes are generated: `aiLimits()` and `aiSetLimits({ aiLimitsSet: {
144
+ creditsAfterAllowance } })` on `/v1/ai/limits`; `SyncApi`'s `getSync`,
145
+ `getSyncById`, `postSync` and `postSyncByIdRun` ("sync now") on `/v1/sync`;
146
+ `postDecisions`; and `getModels`, whose rows carry `class` (premium, ours or
147
+ free), `family` and `pricing.variable`.
148
+
97
149
  ## The six capabilities
98
150
 
99
151
  Budget, policy, audit, search, kb and graph — the same six words in the Go,
@@ -166,7 +218,7 @@ Types are namespaced by capability: `budget.Allowance`, `search.Hit`,
166
218
 
167
219
  ## Examples
168
220
 
169
- Eight flows under [`examples/`](examples), one directory each, every one a
221
+ Nine flows under [`examples/`](examples), one directory each, every one a
170
222
  complete program:
171
223
 
172
224
  | flow | what it does | routes |
@@ -179,6 +231,7 @@ complete program:
179
231
  | [`agent`](examples/agent) | create + run + read | `POST /v1/agent`, `POST /v1/agent/{ref}/run`, `GET /v1/agent/{ref}/runs` |
180
232
  | [`tools`](examples/tools) | tool catalog | `GET /v1/tool` |
181
233
  | [`six`](examples/six) | budget → policy → search → kb → graph → audit, in one pass | the six capabilities |
234
+ | [`limits`](examples/limits) | plan, catalog by class, who paid, a typed refusal, repo links | `GET /v1/ai/limits`, `GET /v1/models`, `POST /v1/chat/completions`, `POST /v1/decisions`, `GET /v1/sync` |
182
235
 
183
236
  `models` runs with nothing exported:
184
237
 
@@ -189,8 +242,8 @@ npm ci && npm run build && npx tsx examples/models/index.ts
189
242
  (`npm run build` first because the examples import `hanzoai` by name and the
190
243
  package resolves its own name through `exports`, which points at `dist/`.)
191
244
 
192
- Six of the others read `HANZO_API_KEY` and `six` reads
193
- `HANZO_CLIENT_ID`/`HANZO_CLIENT_SECRET`; all eight talk to
245
+ Seven of the others read `HANZO_API_KEY` and `six` reads
246
+ `HANZO_CLIENT_ID`/`HANZO_CLIENT_SECRET`; all nine talk to
194
247
  `https://api.hanzo.ai` unless `HANZO_BASE_URL` says otherwise:
195
248
 
196
249
  ```bash
@@ -202,8 +255,9 @@ A token the server refuses answers `HTTP 401:
202
255
  {"error":"invalid_token","error_description":"the access token is invalid or
203
256
  revoked"}`.
204
257
 
205
- `npm run examples` type-checks all eight against the client, `npm run wire`
206
- runs the six against a scripted `fetch`, and `hanzo.yml` makes both a CI gate.
258
+ `npm run examples` type-checks all nine against the client, `npm run wire`
259
+ runs the six and the usage refusals against a scripted `fetch`, and `hanzo.yml`
260
+ makes both a CI gate.
207
261
 
208
262
  ## The API surface
209
263
 
@@ -247,7 +301,8 @@ knob lives once, in that repo's `generate.py` and `sdks.yaml`.
247
301
 
248
302
  `.generated` lists what the driver owns. A file it does not name is this repo's
249
303
  own and a regeneration never touches it — which is where `hanzo.ts`,
250
- `client.ts`, `answer.ts` and the six capabilities live.
304
+ `client.ts`, `answer.ts`, `usage.ts`, `transport.ts` and the six capabilities
305
+ live.
251
306
 
252
307
  Requires java 17+ and [uv](https://docs.astral.sh/uv/).
253
308
 
package/dist/answer.js CHANGED
@@ -15,6 +15,7 @@ exports.arm = arm;
15
15
  exports.value = value;
16
16
  exports.page = page;
17
17
  const read_1 = require("./read");
18
+ const usage_1 = require("./usage");
18
19
  /**
19
20
  * An outcome with no decision in it.
20
21
  *
@@ -58,15 +59,17 @@ const refusals = new Set(['spend_cap_exceeded', 'insufficient_balance']);
58
59
  function refusal(body) {
59
60
  const b = (0, read_1.obj)(body);
60
61
  // RFC 9457 spells the code `code` and the sentence `detail`; the spend gate's
61
- // own body spells them `error` and `message` and adds `product` and `cure[]`.
62
- // Its own `reason` — "unpaid", "unresolved" — names the admit leg that failed
63
- // rather than explaining anything to a person, so it is not read. One reader,
64
- // named fallbacks — not two paths — so it keeps working unchanged when cloud
65
- // settles on the envelope alone.
62
+ // own body spells them `error` and `message` and adds `product` and `cure[]`;
63
+ // the AI gateway's OpenAI envelope nests `code` and `message` under `error`.
64
+ // The spend gate's `reason` — "unpaid", "unresolved" — names the admit leg that
65
+ // failed rather than explaining anything to a person, so it is not read. One
66
+ // reader, named fallbacks — not three paths — so it keeps working unchanged
67
+ // when cloud settles on one envelope.
68
+ const e = (0, read_1.obj)(b['error']);
66
69
  const product = (0, read_1.str)(b['product']);
67
70
  return {
68
- code: (0, read_1.str)(b['code']) || (0, read_1.str)(b['error']),
69
- reason: (0, read_1.str)(b['detail']) || (0, read_1.str)(b['message']),
71
+ code: (0, read_1.str)(b['code']) || (0, read_1.str)(b['error']) || (0, read_1.str)(e['code']),
72
+ reason: (0, read_1.str)(b['detail']) || (0, read_1.str)(b['message']) || (0, read_1.str)(e['message']),
70
73
  ...(product ? { product } : {}),
71
74
  cures: (0, read_1.list)(b['cure']).map((c) => {
72
75
  const cure = (0, read_1.obj)(c);
@@ -107,14 +110,21 @@ function arm(r, read) {
107
110
  if (r.status === 402 || (r.status === 403 && refusals.has(said.code))) {
108
111
  return { status: 'denied', ...said, request: r.request };
109
112
  }
110
- throw new Fault(r.status, said.code, said.reason || `HTTP ${r.status}`, r.request);
113
+ throw fault(r, said);
111
114
  }
112
115
  /** A read no gate refuses: the value, or a [Fault] because nothing was decided. */
113
116
  function value(r, read) {
114
117
  if (r.status >= 200 && r.status < 300)
115
118
  return read(r.body);
116
- const said = refusal(r.body);
117
- throw new Fault(r.status, said.code, said.reason || `HTTP ${r.status}`, r.request);
119
+ throw fault(r, refusal(r.body));
120
+ }
121
+ /**
122
+ * What a call that decided nothing throws: the usage refusal the body names
123
+ * (usage.ts), or a [Fault]. What [arm] reads as [Denied] never reaches here.
124
+ */
125
+ function fault(r, said) {
126
+ return ((0, usage_1.limited)(r.status, r.body, r.request || undefined) ??
127
+ new Fault(r.status, said.code, said.reason || `HTTP ${r.status}`, r.request));
118
128
  }
119
129
  /** `{data, total}` — the page shape every listing on this wire answers with. */
120
130
  function page(body, read) {