@gigaai/newton
v0.4.2
Published
Deterministic multi-agent workflow executor. Write workflows in plain JavaScript; Newton drives coding agents over the Agent Client Protocol.
Downloads
113
Readme
Newton
Newton runs deterministic multi-agent workflows. A workflow is a plain JavaScript file; the control flow (loops, fan-out, conditionals) is your code, and the non-deterministic parts are delegated to coding agents over the Agent Client Protocol — Codex, Claude Code, Gemini CLI, or any other ACP-speaking agent.
No config files, no server, no accounts. newton run workflow.js in a
workspace and it works.
Install
npm install -g @gigaai/newton # or: npx @gigaai/newtonOr grab a standalone binary from Releases — no Node required.
Agents authenticate themselves: openai uses your existing codex login (or
OPENAI_API_KEY / CODEX_API_KEY). Check your environment with newton doctor.
Quick start
// hello.js
export const meta = {
name: 'hello',
description: 'Two scouts in parallel, then a structured synthesis.',
}
phase('Scout')
const notes = await parallel([
() => agent('Describe the layout of this repository.', { provider: 'openai', model: 'gpt-5.5', access: 'read' }),
() => agent('Find the entry points of this repository.', { provider: 'openai', model: 'gpt-5.5', access: 'read' }),
])
phase('Synthesize')
const report = await agent(`Merge these notes:\n\n${notes.filter(Boolean).join('\n\n')}`, {
provider: 'openai',
model: 'gpt-5.5',
access: 'read',
schema: { type: 'object', required: ['summary'], properties: { summary: { type: 'string' } } },
})
return { report }newton run hello.jsProgress streams to stderr; the result JSON prints to stdout. Pass --json
for NDJSON events instead — built for driving Newton from another agent.
Workflow API
A workflow file is export const meta = { name, description } followed by a
script body. The body runs as an async function with these in scope:
| Primitive | What it does |
|---|---|
| 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). |
| parallel(thunks) | Run () => … tasks concurrently; a failed task resolves to null. Barrier: waits for all. |
| pipeline(items, ...stages) | Each item flows through the stages independently — no barrier between stages. Stage signature: (prev, item, index). |
| phase(title) | Group subsequent agents in progress output. |
| log(message) | Narrator line in progress output and the journal. |
| workflow(nameOrPath, args?) | Run one child workflow. Bare names resolve from .newton/workflows/<name>.js; paths resolve relative to the calling workflow. Nesting is limited to one child level. |
| args | Whatever you passed via --args (JSON-parsed when possible). |
| budget | { total, spent(), remaining() } for the --budget output-token ceiling. |
agent() options: provider (see below), model, effort (low–xhigh),
access (read | write | full — sandbox posture), schema (JSON Schema
for structured output, validated with automatic retries), label, phase,
cwd, timeoutMs (wall-clock kill; default --timeout, 7200s),
idleTimeoutMs (kill after this long with no streamed activity; default
--idle-timeout, 900s, 0 disables), retryTimeouts (timed-out agents are
terminal by default; true retries them like transient errors),
isolation: 'worktree' for disposable per-agent git
worktrees, mcp (true for all .mcp.json servers, or a list of names/inline
servers), config (ACP configId to string value), and command — an escape
hatch to run any ACP agent, e.g. command: ['my-agent', '--acp'].
Workflow scripts run in a sandboxed JavaScript realm: Node globals such as
process, fetch, and setTimeout are not available, and console.log()
routes through log() into progress output and the journal.
Providers
| Provider | Aliases | Model override | Runs via |
|---|---|---|---|
| codex | openai | yes | bundled codex-acp adapter |
| claude | anthropic | yes | claude-agent-acp adapter |
| gemini | google | yes | gemini --experimental-acp |
| qwen | qwen-code | yes | qwen --acp |
| cursor | cursor-agent | no | cursor-agent-acp adapter |
| opencode | | no | opencode acp |
| goose | | no | goose acp |
| copilot | github-copilot | no | copilot-language-server --acp |
Resolution order per provider: newton's own node_modules → PATH → npx -y.
Agents authenticate themselves with their normal logins; newton agents
detects which ones are installed and signed in on this machine.
Model selection works two ways: providers with a CLI/env knob get it passed
directly, and for everything else Newton matches model: against the models
the agent advertises over ACP (session config options) and selects it
protocol-side. newton models <provider> shows what an agent exposes;
requesting a model an agent doesn't offer fails with the available list.
Date.now(), argless new Date(), and Math.random() also throw inside
workflows: nondeterminism would silently break --resume replay. Pass
timestamps in via --args.
Runs, traces, resume
Every run writes durable artifacts to .newton/runs/<runId>/ in the
workspace:
journal.jsonl— every lifecycle event, with each agent's full result;agent_startentries carry apromptSha+ 200-char preview.agents/<n>.jsonl— the complete ACP wire trace per agent: every message chunk, tool call, and permission decision (including the full prompt).prompts/<sha>.txt— each distinct prompt stored once; a shared task brief is not duplicated into every agent event.
newton run workflow.js --resume <runId> replays successful agent results
from a previous run's journal; without --args it reuses the resumed run's
journaled args, so the replay cache can't be busted by args drift. The default
--resume-mode prefix replays by call position: each call whose position and
call-key match the prior run returns the cached result; a call that failed
in the prior run re-runs live without breaking the prefix; the first call
whose key differs breaks it, and everything after runs live.
--resume-mode hash uses the legacy hash replay: identical calls return
instantly no matter where they appear.
Runs record their own death. Every run heartbeats status.json (~10s) and
journals a workflow_died event on SIGHUP/SIGINT/SIGTERM or a crash, so
status.json never claims running after a survivable death; a SIGKILL is
inferred from a dead pid + stale heartbeat.
Long runs can be detached and controlled from another shell:
newton run workflow.js --detachstarts in the background and prints{ runId, pid }.newton runs [--json]lists.newton/runs/*with status, agent/failed counts, output tokens, and pid, plus a forensic line for each dead run (cause, or the last journal event before a SIGKILL).newton watch <runId> [--json]replays the journal from the beginning and keeps tailing until the run ends — exit0ok,1workflow error,3the run died (including deaths watch detects via pid liveness). With--json,--events workflow_end,agent_end,…filters the stream.newton stop <runId>SIGTERMs a live run (it journalsworkflow_diedand reaps its agents), escalates to SIGKILL if ignored, and reaps orphaned agent process trees left behind by an earlier death.newton skip <runId> <agentId>asks a queued or live agent to cancel;agent()returnsnulland the workflow continues.
Agents run in their own process groups; a workflow's exit (normal or fatal)
reaps the entire agent subprocess tree, and each run's status.json records
live agent pids so newton stop can clean up even after the run itself is
gone. At the end of a run, any agent failures are summarized (N agent(s)
failed …) and counted in workflow_end.failed / the run result.
CLI
newton run <workflow.js> --args <json> --cwd <dir> --resume <runId> --json
--resume-mode <prefix|hash> --detach
--budget <tokens> --max-agents <n> --timeout <seconds>
--idle-timeout <seconds> --concurrency <n>
newton runs [--json] list runs under .newton/runs
newton watch <runId> [--json] [--events <a,b>] replay/tail a run's journal
newton stop <runId> stop a run; reap its agent process trees
newton skip <runId> <agentId> cancel one agent; workflow continues
newton validate <workflow.js> parse + compile without spawning agents
newton agents [--json] supported agents: installed? signed in? (alias: doctor)
newton models <provider> ask an agent which models/modes it exposesExit codes: 0 success, 1 workflow error, 2 usage error, 3 (watch) the
run died without finishing.
Development
npm install
npm run check # build + validate the example
node dist/cli.js run examples/hello.jsStandalone binaries are built with bun build --compile (see
.github/workflows/release.yml); pushing a v* tag publishes to npm and
attaches binaries to a GitHub Release.
