ordainer 1.2.5 → 1.2.7
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.
- package/CHANGELOG.md +11 -2
- package/README.md +7 -23
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -3,13 +3,22 @@
|
|
|
3
3
|
Entries before 1.1.0 are written with the current names. Those versions used the product's former name for the
|
|
4
4
|
package, class, env vars, outage error code, User-Agent and key prefix: the full list is in 1.1.0 "Renamed".
|
|
5
5
|
|
|
6
|
-
## 1.2.
|
|
6
|
+
## 1.2.7 (2026-11-11)
|
|
7
|
+
|
|
8
|
+
README-only: queue-wait wording matches the live docs (#254). A call that waited in the key's line and ran out of time is `503 decide_timeout` (the SDK retries once, then `failMode`). `429 concurrency_limited` only when the line of 20 waiting is full. The code is the same as 1.2.6, byte for byte; only the README (and this changelog) changed.
|
|
9
|
+
|
|
10
|
+
## 1.2.6 (2026-11-09)
|
|
11
|
+
|
|
12
|
+
README-only: install with `npm install ordainer`. The code is the same as 1.2.5, byte for byte; only the install
|
|
13
|
+
instructions in this README changed.
|
|
14
|
+
|
|
15
|
+
## 1.2.5 (2026-11-09)
|
|
7
16
|
|
|
8
17
|
Changed: **the retry pause is jittered** (P89-L1), like the Ruby SDK: 50 ms ± 50% (25–75 ms) before the first retry,
|
|
9
18
|
doubling for each further one. Retry count, `retryBudgetMs` and which answers are retried are unchanged. When a server
|
|
10
19
|
sheds load (`503 decide_timeout`, "at capacity"), callers no longer all retry in one wave 50 ms later.
|
|
11
20
|
|
|
12
|
-
Changed: **Error messages reworded; codes, statuses and fields unchanged. Match on error.code, not the message.**
|
|
21
|
+
Changed: **Error messages reworded; codes, statuses and fields unchanged. Match on error.code, not the message.** `429 plan_limit_reached`: "This call wasn't run because the usage limit was reached (0 of 5 left). It resets at {resets_at}. Retrying before then won't work." The same sentence is used when the key's label is present: the message does not name the key. `error.label` is unchanged. The "It resets at" sentence is still omitted when `resets_at` is missing. A sticky deny reuses this sentence. `403 plan_denied` with `tool_not_in_plan`: "This call wasn't run because "<tool>" isn't included in your current access. If you need it, contact the provider of this service. (tool_not_in_plan)", with the same quoting rules as 1.2.3 (otherwise "this action"). Any other plan deny is unchanged. Status, `error.code`, `reason`, `remaining`, `limit`, `resets_at`, `request_id`, `label`, headers (`Retry-After`, `X-Request-Id`) and whether the call runs are unchanged.
|
|
13
22
|
|
|
14
23
|
This is the first Node SDK build that passes the deny `explanation` through to callers, so it should be published only after the matching API change (#207) is live.
|
|
15
24
|
|
package/README.md
CHANGED
|
@@ -5,26 +5,10 @@ A small wrapper that asks Ordainer (`POST /v1/decide`) before your tool handler
|
|
|
5
5
|
|
|
6
6
|
## Install
|
|
7
7
|
|
|
8
|
-
<!--
|
|
9
|
-
|
|
10
|
-
Version 1.2.5 is not published. npm already has 1.2.4, which is not this tree. Until 1.2.5 is published, install it
|
|
11
|
-
straight from our docs site:
|
|
12
|
-
|
|
13
|
-
```bash
|
|
14
|
-
npm install express
|
|
15
|
-
npm install https://docs.ordainer.dev/downloads/ordainer-node-1.2.5.tgz
|
|
16
|
-
```
|
|
17
|
-
|
|
18
|
-
Optional: to check the file came from us, compare its SHA-256 with the one in step 2 of
|
|
19
|
-
https://docs.ordainer.dev/get-started/node-express/ (also at
|
|
20
|
-
https://docs.ordainer.dev/downloads/ordainer-node-1.2.5.tgz.sha256):
|
|
21
|
-
|
|
8
|
+
<!-- Checked by test/sdk-pack.test.js. LAUNCH SWAP applied (Nov 9 2026): install is `npm install ordainer`.
|
|
9
|
+
Live docs already use that command. This README does not follow sites/docs SDK_INSTALL while SDK_ON_NPM is still false on main. -->
|
|
22
10
|
```bash
|
|
23
|
-
|
|
24
|
-
shasum -a 256 ordainer-node-1.2.5.tgz
|
|
25
|
-
sha256sum ordainer-node-1.2.5.tgz
|
|
26
|
-
npm install express
|
|
27
|
-
npm install ./ordainer-node-1.2.5.tgz
|
|
11
|
+
npm install ordainer
|
|
28
12
|
```
|
|
29
13
|
|
|
30
14
|
Requires an Ordainer agent key (`ORDAINER_KEY`) from the console. The default API host is
|
|
@@ -71,12 +55,12 @@ if (d.allowed) { /* run it */ }
|
|
|
71
55
|
| Ordainer says allow | Handler runs. `req.ordainer` holds the decision (`remaining`, `limit`, `requestId`, …). `remaining` and `limit` are `number \| null`: `null` on a plan with no cap (and when Ordainer didn't answer), so check before doing arithmetic. |
|
|
72
56
|
| Ordainer says deny, **enforce** | Handler does not run. The status follows the API `reason`. `quota_exceeded` (the plan allowance) is `429 plan_limit_reached` with `Retry-After` and a message you can show the agent as-is. `402` is only that quota deny, and only when `denyStatus` is set. `tool_not_in_plan`, `credits_exhausted` and `key_credits_exhausted` are `403 plan_denied`, not `402`. `test_limit_reached` is its own row below. `result.decision` is `'deny'`, `result.allowed` is `false` and `result.wouldDeny` is `false` (1.2.2: it's a real deny, not a would-deny). |
|
|
73
57
|
| Ordainer says deny, **shadow** | Handler runs. A `[ordainer] shadow: would deny …` warning is logged, `onDecision` gets `wouldDeny: true`, and the console shows "would deny (call ran)". |
|
|
74
|
-
| Timeout (`timeoutMs`), network error, 5xx, or a 200 that isn't a decision | **Fail open**: the handler runs and a warning is logged. This includes `503 decide_timeout` when the Ordainer server is at capacity (it refuses the call at once, before deciding, nothing counted; the message says "at capacity"): retried once (`retries`), then `failMode`. With `failMode: 'closed'` you get `503 ordainer_unavailable` instead. These are the **only** cases `failMode` covers (`403 subscription_inactive` follows `billingLapse`, below). |
|
|
58
|
+
| Timeout (`timeoutMs`), network error, 5xx, or a 200 that isn't a decision | **Fail open**: the handler runs and a warning is logged. This includes `503 decide_timeout` when the Ordainer server is at capacity (it refuses the call at once, before deciding, nothing counted; the message says "at capacity") and when a call waited in this key's line of 20 and ran out of time (nothing counted): retried once (`retries`), then `failMode`. With `failMode: 'closed'` you get `503 ordainer_unavailable` instead. These are the **only** cases `failMode` covers (`403 subscription_inactive` follows `billingLapse`, below). |
|
|
75
59
|
| Any other 4xx (`400`, `401 unknown_key`/`revoked_key`, `403 subscription_required`, `409 idempotency_conflict`, `429` other than `safety_limit_reached`, …) or no agent key configured | **Blocked in any `failMode`** (QA S2): Ordainer answered, and the answer isn't "allow", so the call is never let through unchecked. `plan_check_rejected` with **no `Retry-After`** (a retry won't fix it): `500` for a key, config, `400` or `409` problem, `403` for `subscription_required` (Q2; never `503`, which means "outage, retry"); or `429 rate_limited` with `Retry-After` for a rate limit; `result.rejected: true` (`failClosed` stays `false`), `result.error` has the code and status. A "rejected (call blocked in any failMode): <code>: <message> X-Request-Id=req_… tool=…" warning is logged. The warning is logged at most once a minute per failure mode and error code (`warningIntervalMs`), with "(N more since the last warning)", so an outage doesn't flood your logs; `onDecision` still sees every result. |
|
|
76
60
|
| `403 subscription_inactive`: your own Ordainer subscription is inactive | A **billing state, not an outage**. Follows the **billing-lapse setting `billingLapse`**, never `failMode` (Terms 10.3(b)): `'open'` (default) runs the handler, unchecked and uncounted (`result.allowed: true`, `result.failOpen: false`); `'closed'` (opt-in) blocks with `403 plan_check_rejected`, **no `Retry-After`** and a neutral message (`result.rejected: true`). Either way: its own warning at most once a minute (not the generic one), `result.billingInactive: true`, `result.billingLapse`, `result.reason: 'subscription_inactive'` and `onBillingInactive(result)` on every call. See below. |
|
|
77
61
|
| `403 subscription_required`: your Ordainer account has never had a paid subscription (live key) | **Blocked in any `failMode`**, like other 4xx (`result.rejected: true`). It gets its own billing warning (`[ordainer] subscription required: … no paid subscription yet … Subscribe …`, never "subscription inactive"), `result.billingInactive: true`, `result.reason: 'subscription_required'` and `onBillingInactive(result)`. |
|
|
78
62
|
| `429 rate_limited`: this agent key sent more than the per-key limit. About 100 requests a second, each server counts separately. | A **rate limit, not an outage**. Blocked in any `failMode` with `429 rate_limited` and the server's `Retry-After`. Never silent: its own warning at most once a minute, `result.rateLimited: true`, `result.error.limitScope: 'per_key'`, `result.retryAfterSec`, and `onRateLimited(result)` on every such call. |
|
|
79
|
-
| `429 concurrency_limited`: this agent key already has too many plan checks in flight at once on one Ordainer server (4 in flight at once per key on our hosted service; up to 20 more wait
|
|
63
|
+
| `429 concurrency_limited`: this agent key already has too many plan checks in flight at once on one Ordainer server (4 in flight at once per key on our hosted service; up to 20 more wait about 200 ms for a slot). `429 concurrency_limited` only when that line of 20 waiting is full (`limit_scope: "per_key_concurrency"`). A call that waited in that line and ran out of time gets `503 decide_timeout` (retried once, then `failMode`), not a 429 | Handled **exactly like `429 rate_limited`**: blocked in any `failMode` (never a fail-open; it is answered inside the timeout), `429 rate_limited` with `Retry-After` to the end user, `result.rateLimited: true`, `result.reason: 'concurrency_limited'`, `result.error.limitScope: 'per_key_concurrency'`, `onRateLimited(result)`, and its own "concurrency limited" warning at most once a minute. Send fewer calls in parallel per agent key. For abuse-prone tools consider `failMode: 'closed'`. |
|
|
80
64
|
| `429 too_many_failed_auth`: too many requests with a missing or unrecognized agent key came from your server's address (Ordainer's per-address limit; never sent for a valid key) | A **bad key, not a rate problem**: rejected like other 4xx (`result.rejected: true`, `result.rateLimited: false`, `result.error.limitScope: 'per_address'`; `onRateLimited` is not called). Its own warning: "too many requests with a missing or unrecognized agent key from this server's address (Ordainer's per-address limit …) Check the agent key …". Fix the key; retrying won't help. |
|
|
81
65
|
| Deny `test_limit_reached`: your account used its free test-key decisions for the month (test keys only) | **Enforce: blocks** regardless of `failMode`: `429 test_limit_reached` with `Retry-After` until the 1st (UTC). **Shadow (1.2.0): runs.** Shadow never blocks: the server counts the call and records a would-deny `test_limit_reached` (answer `mode: 'shadow'`), and the handler runs (`result.allowed: true`, `result.wouldDeny: true`). Not an outage and not your customer's plan. Both: `result.testLimitReached: true`, its own warning at most once a minute, and `onTestLimit(result)` on every such call. Live keys are never affected. |
|
|
82
66
|
| `429 safety_limit_reached` (1.2.0): shadow calls passed the safety ceiling, 10 times your account's monthly test limit | A **safety limit, not a deny**. The handler **runs in every `failMode`** (like `billingLapse: 'open'`), unchecked and not recorded, until your test usage resets at the start of next month (UTC). `result.allowed: true`, `result.safetyLimited: true`, `result.failOpen: false`, `result.rejected: false`, `result.error.limitScope: 'org_safety'`, `result.resetsAt`, `result.retryAfterSec`; its own warning at most once per `warningIntervalMs`, and `onSafetyLimit(result)` on every such call. |
|
|
@@ -213,7 +197,7 @@ decision id exists). Earlier builds used `Ordainer-Request-Id` and `error.type`;
|
|
|
213
197
|
| `test_limit_reached` | 429 | seconds until the reset (none if `resets_at` is unknown) | "This call wasn't run. This service's test calls for the month are used up. They reset on November 1 (UTC). Nothing is wrong on your side, and trying again won't help until they reset. If you need it sooner, contact the service's team. (test_limit_reached)" (the day is `resets_at` in UTC, as "<Month D>"; no "They reset on …" sentence unless `resets_at` is a valid ISO-8601 date-time) |
|
|
214
198
|
| Any other deny (today `tool_not_in_plan`) | 403 `plan_denied` | **none** | `tool_not_in_plan`: "This call wasn't run because "research.run" isn't included in your current access. If you need it, contact the provider of this service. (tool_not_in_plan)" (the tool you passed to `decide()`, in quotes only when it matches `^[A-Za-z0-9][A-Za-z0-9_.:/-]{0,63}$` and has no `//` and no leading `scheme:` (`^[A-Za-z][A-Za-z0-9+.-]*:`, so nothing URL-like is echoed); otherwise "this action"). Any other reason: "This call wasn't run because it isn't allowed on your account right now. If you need it, contact the service's team. (reason)" (the reason in brackets only when it looks like an API code: lowercase letters and `_`, up to 40 characters) |
|
|
215
199
|
| `429 rate_limited` | 429 `rate_limited` | the server's value | "The service is getting too many requests on this key right now, so this call was held back. Please wait a moment and try again. (rate_limited)" (the seconds are in `Retry-After` and `retry_after`) |
|
|
216
|
-
| `429 concurrency_limited` | 429 `rate_limited` | the server's value | "The service is handling too many calls on this key at once, so this call was held back. Please wait a moment and try again. (concurrency_limited)" (the seconds are in `Retry-After` and `retry_after`) |
|
|
200
|
+
| `429 concurrency_limited` (waiting line of 20 is full) | 429 `rate_limited` | the server's value | "The service is handling too many calls on this key at once, so this call was held back. Please wait a moment and try again. (concurrency_limited)" (the seconds are in `Retry-After` and `retry_after`) |
|
|
217
201
|
| Outage under `failMode: 'closed'` (timeout, network error, 5xx, malformed answer) | **503 `ordainer_unavailable`** (only here) | `5` | "This call wasn't run. The service couldn't finish a safety check it does before each call, so it stopped instead of going ahead without it. Nothing is wrong on your side. Please try again in a minute. (ordainer_unavailable)" |
|
|
218
202
|
| Billing: `subscription_required`, or the `subscription_inactive` lapse under `billingLapse: 'closed'` | **403 `plan_check_rejected`** | **none** | "This action isn't available right now. This is a setup issue on the service's side; retrying won't help." (neutral, Terms 10.3(b)) |
|
|
219
203
|
| Any other rejection: missing, unknown or revoked key, `too_many_failed_auth`, `400`, `409 idempotency_conflict`, other 4xx | **500 `plan_check_rejected`** | **none** | Plain words for the cause, then the API's code last, e.g. after `401 unknown_key`: "This call wasn't run. This is a setup issue on the service's side: one of its keys isn't recognized. It isn't something you did, and retrying won't help until the service fixes the key. (For the service's team: unknown_key.)" A revoked key says "one of its keys has been turned off", no key says "a key it needs is missing", anything else says "This call wasn't run because of a setup issue on the service's side." |
|
|
@@ -254,7 +238,7 @@ Now `503` means only a real outage.
|
|
|
254
238
|
| `billingInactive` | `403 subscription_inactive` or `subscription_required`; `reason` says which. |
|
|
255
239
|
| `billingLapse` | The billing-lapse setting applied to a `403 subscription_inactive` (`'open'`: ran unchecked; `'closed'`: blocked), else `null`. |
|
|
256
240
|
| `testLimitReached` | Denied with `test_limit_reached` (blocked in enforce; in shadow, 1.2.0 runs the call as a would-deny). |
|
|
257
|
-
| `rateLimited`, `retryAfterSec` | `429 rate_limited` (per key) or `429 concurrency_limited` (
|
|
241
|
+
| `rateLimited`, `retryAfterSec` | `429 rate_limited` (per key) or `429 concurrency_limited` (the key's line of 20 waiting is full; `reason` says which) and its `Retry-After` in seconds. |
|
|
258
242
|
| `safetyLimited` | 1.2.0: `429 safety_limit_reached` (shadow calls past the safety ceiling, 10 times the account's monthly test limit). The call ran (`allowed: true`, `failOpen: false`); `retryAfterSec` and `resetsAt` say when it ends. |
|
|
259
243
|
| `error` | `{ code, message, status?, limitScope? }` when there was no decision; `limitScope` is `'per_key'`, `'per_key_concurrency'` or `'per_address'` on a 429 (the server's `error.limit_scope`; not `limit`, which is the plan's cap). |
|
|
260
244
|
| `label` | The agent key's label (its name) when the API sent it on a deny, cleaned and at most 64 characters; `null` on allows, errors, older servers, or unless `exposeKeyLabel: true`. Named in the 429 `plan_limit_reached` (unreleased, DRAFT T18-P39). |
|