@parcha/agentrun-jev 0.1.0-beta.17 → 0.1.0-beta.18

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/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  Optional [TypeSafe Jev](https://docs.typesafe.ai/) support for AgentRun DSL. Jev answers focused questions with typed values and probability distributions. The workflow decides how to use them.
4
4
 
5
- Beta: `0.1.0-beta.4`. Requires Node.js 22.19 or later.
5
+ Beta, on npm under the `beta` dist-tag (`npm view @parcha/agentrun-jev dist-tags`). Requires Node.js 22.19 or later.
6
6
 
7
7
  ```sh
8
8
  npm install @parcha/agentrun-dsl@beta @parcha/agentrun-jev@beta
package/dist/index.js CHANGED
@@ -82,6 +82,16 @@ function wait(ms, signal, error) {
82
82
  abort();
83
83
  });
84
84
  }
85
+ /** A server's Retry-After in milliseconds from now, given as delta-seconds or an HTTP date; undefined when absent or unparseable. */
86
+ function retryAfterMs(headers) {
87
+ const value = headers instanceof Headers ? headers.get("retry-after")?.trim() : undefined;
88
+ if (!value)
89
+ return undefined;
90
+ if (/^\d+$/.test(value))
91
+ return Number(value) * 1000;
92
+ const date = Date.parse(value);
93
+ return Number.isNaN(date) ? undefined : Math.max(0, date - Date.now());
94
+ }
85
95
  export function createJevRunner(options = {}) {
86
96
  const timeoutMs = options.timeoutMs === null ? null : (options.timeoutMs ?? 30_000);
87
97
  const maxStateBytes = options.maxStateBytes ?? null;
@@ -200,6 +210,8 @@ export function createJevRunner(options = {}) {
200
210
  body = JSON.stringify({ ...JSON.parse(body), assets: assetDigests });
201
211
  }
202
212
  const headers = carriesImage || hasBox ? { [IMAGE_HEADER]: "true" } : undefined;
213
+ // The first attempt that failed in a way the server may have billed (a lost answer, a 408, a 5xx). A later 429 does not make the call free: the give-up reports this one.
214
+ let billable;
203
215
  for (attempt = 1;; attempt++) {
204
216
  if (signal.aborted)
205
217
  throw abortError();
@@ -214,10 +226,23 @@ export function createJevRunner(options = {}) {
214
226
  throw abortError();
215
227
  const status = error instanceof APIError ? error.status : undefined;
216
228
  const retryable = status === 408 || status === 429 || (status !== undefined && status >= 500 && status <= 599) || error instanceof APIConnectionError;
229
+ if (retryable && status !== 429 && billable === undefined)
230
+ billable = status === undefined ? {} : { status };
217
231
  if (retryable && attempt < maxAttempts) {
218
- await wait(Math.min(retryMaxMs, retryBaseMs * 2 ** (attempt - 1)), signal, abortError);
219
- continue;
232
+ const backoff = Math.min(retryMaxMs, retryBaseMs * 2 ** (attempt - 1));
233
+ const asked = error instanceof APIError && (status === 429 || status === 503) ? retryAfterMs(error.headers) : undefined;
234
+ const delay = asked === undefined ? backoff : Math.max(asked, backoff);
235
+ // A server's wait is kept in full or the loop gives up now: it never runs past the deadline, or past the longest timer when there is none, so no
236
+ // retry is sent with no time left to answer (a request that times out has a cost no one knows).
237
+ const left = timeoutMs === null ? 2_147_483_647 : timeoutMs - (Date.now() - started);
238
+ if (asked === undefined || delay < left) {
239
+ await wait(delay, signal, abortError);
240
+ continue;
241
+ }
220
242
  }
243
+ // HTTP 429 means every attempt was refused unanswered; after one that may have been billed, the give-up names that one instead.
244
+ if (status === 429 && billable)
245
+ throw billable.status === undefined ? new JevError("connection", "Jev transport failed.", attempt) : new JevError("http", `Jev returned HTTP ${billable.status}.`, attempt, billable.status);
221
246
  if (status !== undefined) {
222
247
  const safeStatus = Number.isInteger(status) && status >= 100 && status <= 599 ? status : undefined;
223
248
  const capacity = error instanceof APIError && record(error.body) && record(error.body.detail)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@parcha/agentrun-jev",
3
- "version": "0.1.0-beta.17",
3
+ "version": "0.1.0-beta.18",
4
4
  "description": "Optional Jev system one decisions for AgentRun workflows",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",
@@ -27,7 +27,7 @@
27
27
  "test": "node --test test/*.test.mjs"
28
28
  },
29
29
  "dependencies": {
30
- "@parcha/agentrun-dsl": "0.1.0-beta.17",
30
+ "@parcha/agentrun-dsl": "0.1.0-beta.18",
31
31
  "@typesafe-ai/sdk": "0.7.0"
32
32
  },
33
33
  "repository": {