@gigaai/newton 0.4.0 → 0.4.1

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
@@ -61,7 +61,7 @@ script body. The body runs as an async function with these in scope:
61
61
 
62
62
  | Primitive | What it does |
63
63
  |---|---|
64
- | `agent(prompt, opts?)` | Run one agent turn; returns its final text, or a schema-validated object when `opts.schema` is set. Returns `null` on agent failure — filter with `.filter(Boolean)`. |
64
+ | `agent(prompt, opts?)` | Run one agent turn; returns its final text, or a schema-validated object when `opts.schema` is set. Transient failures (provider 5xx/overload, rate limits, dropped connections) retry up to twice with backoff; returns `null` on lasting failure — filter with `.filter(Boolean)`. |
65
65
  | `parallel(thunks)` | Run `() => …` tasks concurrently; a failed task resolves to `null`. Barrier: waits for all. |
66
66
  | `pipeline(items, ...stages)` | Each item flows through the stages independently — no barrier between stages. Stage signature: `(prev, item, index)`. |
67
67
  | `phase(title)` | Group subsequent agents in progress output. |
package/dist/executor.js CHANGED
@@ -9,6 +9,32 @@ import { checkCompile, createRealm } from "./sandbox.js";
9
9
  import { balancedSlice, extractJson, INVALID, structuredOutputInstruction, structuredOutputRetryPrompt, validateAgainstSchema, } from "./schema.js";
10
10
  import { createWorktree, finishWorktree } from "./worktree.js";
11
11
  const SCRIPT_PARAMS = ["agent", "parallel", "pipeline", "phase", "log", "args", "budget", "workflow"];
12
+ const AGENT_MAX_ATTEMPTS = 3;
13
+ const AGENT_RETRY_BASE_MS = 2_000;
14
+ /**
15
+ * Failures worth a fresh attempt: provider 5xx/overload/rate-limit responses
16
+ * and dropped connections. Deterministic failures (auth, missing binaries,
17
+ * bad model ids, structured-output exhaustion) return null without retrying.
18
+ */
19
+ const TRANSIENT_AGENT_ERROR = new RegExp([
20
+ "\\b(?:429|500|502|503|504|529)\\b",
21
+ "overloaded",
22
+ "rate.?limit",
23
+ "too many requests",
24
+ "internal server error",
25
+ "service unavailable",
26
+ "bad gateway",
27
+ "gateway timeout",
28
+ "econn(?:reset|refused|aborted)",
29
+ "etimedout",
30
+ "epipe",
31
+ "eai_again",
32
+ "socket hang.?up",
33
+ "connection (?:reset|refused|closed|error)",
34
+ "fetch failed",
35
+ "network error",
36
+ "stream (?:closed|disconnected)",
37
+ ].join("|"), "i");
12
38
  function errorMessage(error) {
13
39
  return typeof error === "object" &&
14
40
  error !== null &&
@@ -257,45 +283,64 @@ export async function runWorkflow(options) {
257
283
  : callOptions.cwd
258
284
  ? resolve(workspace, callOptions.cwd)
259
285
  : workspace;
260
- session = new AcpAgentSession({
261
- spec,
262
- prompt: fullPrompt,
263
- cwd,
264
- timeoutMs: callOptions.timeoutMs ?? agentTimeoutMs,
265
- mcpServers,
266
- config,
267
- trace,
268
- });
269
- liveSessions.set(id, session);
270
- let turn = await session.prompt();
271
- let result = turn.text;
272
- let failure = turn.error;
273
- if (!failure && callOptions.schema) {
274
- for (let attempt = 0;; attempt++) {
275
- const parsed = extractJson(turn.text);
276
- const errors = parsed === INVALID
277
- ? ["response contained no parseable JSON"]
278
- : validateAgainstSchema(parsed, callOptions.schema);
279
- if (errors.length === 0) {
280
- result = parsed;
281
- break;
282
- }
283
- if (attempt >= 2) {
284
- failure = `structured output failed after ${attempt + 1} attempts: ${errors.join("; ")}`;
285
- break;
286
- }
287
- turn = await session.promptAgain(structuredOutputRetryPrompt(errors, callOptions.schema));
288
- if (turn.error) {
289
- failure = turn.error;
290
- break;
286
+ let turn;
287
+ let result;
288
+ let failure;
289
+ for (let attempt = 1;; attempt++) {
290
+ session = new AcpAgentSession({
291
+ spec,
292
+ prompt: fullPrompt,
293
+ cwd,
294
+ timeoutMs: callOptions.timeoutMs ?? agentTimeoutMs,
295
+ mcpServers,
296
+ config,
297
+ trace,
298
+ });
299
+ liveSessions.set(id, session);
300
+ turn = await session.prompt();
301
+ result = turn.text;
302
+ failure = turn.error;
303
+ if (!failure && callOptions.schema) {
304
+ for (let retry = 0;; retry++) {
305
+ const parsed = extractJson(turn.text);
306
+ const errors = parsed === INVALID
307
+ ? ["response contained no parseable JSON"]
308
+ : validateAgainstSchema(parsed, callOptions.schema);
309
+ if (errors.length === 0) {
310
+ result = parsed;
311
+ break;
312
+ }
313
+ if (retry >= 2) {
314
+ failure = `structured output failed after ${retry + 1} attempts: ${errors.join("; ")}`;
315
+ break;
316
+ }
317
+ turn = await session.promptAgain(structuredOutputRetryPrompt(errors, callOptions.schema));
318
+ if (turn.error) {
319
+ failure = turn.error;
320
+ break;
321
+ }
291
322
  }
292
323
  }
324
+ liveSessions.delete(id);
325
+ session.close();
326
+ outputTokens += turn.usage?.outputTokens ?? 0;
327
+ if (!failure ||
328
+ attempt >= AGENT_MAX_ATTEMPTS ||
329
+ turn.stopReason === "timeout" ||
330
+ turn.stopReason === "cancelled" ||
331
+ !TRANSIENT_AGENT_ERROR.test(failure)) {
332
+ break;
333
+ }
334
+ const delayMs = AGENT_RETRY_BASE_MS * 4 ** (attempt - 1);
335
+ journal.append({ type: "agent_retry", id, key, label, phase, attempt, error: failure, delayMs });
336
+ reporter.log(`↻ agent #${id} transient error, retry ${attempt}/${AGENT_MAX_ATTEMPTS - 1} in ${delayMs / 1000}s: ${failure.split("\n")[0]}`);
337
+ await new Promise((resolveDelay) => setTimeout(resolveDelay, delayMs));
338
+ if (skipped.has(id))
339
+ break;
293
340
  }
294
- session.close();
295
341
  const worktree = await finishCreatedWorktree();
296
342
  const seconds = Math.round((Date.now() - agentStartedAt) / 1000);
297
343
  const turnTokens = turn.usage?.outputTokens ?? 0;
298
- outputTokens += turnTokens;
299
344
  if (failure) {
300
345
  journal.append({
301
346
  type: "agent_end",
package/dist/version.js CHANGED
@@ -1 +1 @@
1
- export const VERSION = "0.4.0";
1
+ export const VERSION = "0.4.1";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gigaai/newton",
3
- "version": "0.4.0",
3
+ "version": "0.4.1",
4
4
  "description": "Deterministic multi-agent workflow executor. Write workflows in plain JavaScript; Newton drives coding agents over the Agent Client Protocol.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {