npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

owenloop

v0.3.1

Published

Deterministic rails for agentic workflows. Declare steps and dependencies; the engine guarantees order, redoes what changes invalidate, and stops what keeps failing — instead of hoping the agent follows through.

Readme

owenloop

CI License: Apache 2.0

owenloop is deterministic rails for agentic workflows.

Most agent workflows today run on hope. You write careful instructions in a prompt or a skill — update the state file, run the verifier, stop when it's done — hand them to an agent, and trust it to follow through. Sometimes it does.

owenloop replaces the hope with guarantees. You declare the steps and what each depends on; the engine enforces the rest. A step runs only when everything it needs is actually done. When a result changes, everything built on it is invalidated and redone. When a step keeps failing, it's stopped and flagged for a human instead of retried forever. The agents stay probabilistic — that's what makes them useful. The workflow around them doesn't.

See it work

A research pipeline: a researcher gathers findings, a writer turns them into a report, and an independent reviewer must sign off before the report counts. Every clause of that sentence is enforced below, not requested:

# workflows/report.yaml
name: report
inputs:
  - name: question
    seedOwed: true
steps:
  - name: researcher
    consumes: [question]
    produces:
      - name: findings
        schema: { type: object, required: [claims, sources] }
    body: Research the question. Every claim needs a source.
  - name: writer
    consumes: [findings]
    produces:
      - name: report
        judges:
          - name: reviewer
            body: Reject the report if any claim lacks a citation
                  or drifts from the findings. Otherwise approve.
    maxAttempts: 3
    body: Write the report from the findings, citations inline.

You don't drive this by hand. An orchestrator — an agent skill, a plain while loop, your own code — ticks the engine, hands each job to an agent, and reports results back. Here's what the engine enforces as that loop runs:

  1. The writer cannot start early. The first tick emits exactly one order — the researcher's. There is no writer job to hand out, so no eager agent can write a report from findings that don't exist. The hope version is a prompt: "wait until research is complete before writing."

  2. Malformed output never enters the pipeline. The researcher reports findings missing sources — the engine refuses the commit at the schema, and the retry order carries the validation errors as feedback. The hope version: the bad output flows downstream and fails somewhere confusing.

  3. The report can't approve itself. When the writer commits report, it lands in submitted, not done. The reviewer is a separate order to a separate agent — one that never saw the writer's reasoning. A rejection re-arms the writer with the reviewer's reasons attached to its next job. The hope version: "review your work before finishing" — the fox auditing the henhouse.

  4. Failure has a floor. Third rejected attempt and report stalls: the engine stops issuing jobs for it and flags it for a human, instead of letting an agent grind the same mistake all night on your API bill.

  5. "Done" stays honest. Sharpen the question after the run finishes and findings and report fall back to owed — the finished workflow un-does itself, automatically, rather than standing on inputs that no longer exist.

None of this depends on an agent reading carefully, remembering instructions, or being honest about its own work. And notice that you never defined a state or a transition — only what each step consumes and produces. This isn't a state machine an agent is asked to role-play from a prompt; the states live in the engine, and the engine doesn't negotiate. The agents stay probabilistic; the bookkeeping around them never is. The Quick start gets you running in two commands, and Driving it with a loop covers orchestrators — including the shipped skills that do it for you.


Why it exists

Agents are good at doing one task. They're bad at the bookkeeping around a task: remembering what's already done, noticing when an earlier step's output changed, retrying the right number of times, and knowing when to stop. Wire a few agents together by hand and you end up writing a pile of glue — who runs next, what to re-run when something upstream moves, when to give up and ask a human.

owenloop is that glue, written once and tested hard. You declare the steps; it handles the three things that are tedious to get right:

  • What runs next. A step is ready the moment everything it depends on is accepted and it still owes an output. That's the whole scheduler — there's no status field to flip, nothing to sequence by hand.
  • What to re-run. Change an early step's output and everything built on it automatically falls back to "not done." No manual invalidation, no stale results slipping through.
  • When to stop. If a step keeps getting rejected past its limit, owenloop stops re-running it and flags it for a human — instead of looping forever burning tokens.

Hope is not a control flow.

The mental model: owed, not done

The checklist your agents can't cheat.

owenloop doesn't track whether a step is "running" or "done." It tracks what each step owes. Every output is in one of six states:

| state | still owed? | meaning | |-------------|:-----------:|------------------------------------------------------------------| | owed | yes | declared but not produced yet, or re-armed — the step owes it | | green | no | accepted; satisfies everything downstream that depends on it | | rejected | yes | produced, then judged unfit (or knocked back by a change) — a debt | | retracted | no | a member dropped from a collection; gone for good | | skipped | no | a step declined its own output on a dead branch | | submitted | no* | produced, awaiting sign-off from one or more declared judges |

* submitted isn't a producer debt — the producer already did its job — but the workflow isn't done while it sits there either. See judges.

A step is eligible to run when it owes a debt (an owed or rejected output) and every input it consumes is green. Status is never stored — it's computed from these states on every read, so it can't drift out of sync.

Two things make this more than running steps in dependency order:

  • Outputs stay honest as inputs move. A green output counts as done only while the inputs it was built from are still green and unchanged. Re-run an early step and everything built on it quietly falls back to a debt — no code required to invalidate it.
  • Rejections carry reasons. When a reviewer rejects an output, the text rides along. The next job for the producer shows why it's being asked again, so the agent has the feedback in hand. (Three flavors: a reviewer's judgment, the engine's own schema refusal of a malformed value, and structural knock-backs from a change cascading downstream.)

What owenloop is not

  • Not a scheduler with its own clock. cadence: and maxRunsPerDay: cap how often an eligible step can fire, but nothing in owenloop wakes up on a timer — the outer loop (see below) is what initiates every tick.
  • Not shared state across instances. Every workflow instance is its own island — artifacts, tasks, and runs are all scoped to one instance. The one deliberate exception is the calls:/producedBy link between a parent instance and the child it explicitly spawned.
  • Not a dynamic graph at runtime. Collections give a workflow dynamic width — a producer can emit any number of elements — but the wiring graph itself (which steps exist, what each consumes and produces) is fixed when the definition loads, not mutable while an instance runs.
  • Not a command runner. worker:/command: label which kind of executor an order is for and carry a command string through untouched — the engine never shells out, executes, or interprets it. Actually running anything is always the dispatcher's job, on the other side of tick.

It scales up when you do

Each of these is a small addition to the base model above — most workflows use only a few. Skim the ones that sound relevant; every entry links to the full reference.

Judges — enforced independent verification

A produces: entry can declare one or more judges: deterministic quality bars an artifact must clear before it counts as green. This is the independent-verifier pattern, enforced structurally instead of remembered as a convention: the artifact lands in submitted, not green, and cannot move further until a separate order — with no view of the producer's reasoning — signs off. A rejection carries its reason back to the producer's next job, the same as any other knock-back. See docs/authoring.md and judged-research.yaml.

Durable by default

State lives in a single SQLite file, not in a session or a context window. Kill the process, come back next week, run owenloop tick — the engine knows exactly what's owed and picks up where it left off. The workflow outlives the process, the session, and the model that's driving it. See Storage.

Stall detection — the token-burn stopper

If an output is rejected more times than its maxAttempts, the engine stops re-arming it. It stays a debt, but produces no more jobs — the step has demonstrably failed, and it's flagged for a human instead of looping forever. owenloop retry clears the stall and resets the counter, optionally with new guidance. maxAttempts (and maxSchemaFailures below) is set on the step as a default for all its outputs, but any single produces: entry can override either cap for itself when one output needs a different bound than its siblings. See docs/cli.md and docs/design.md §6.

Schema refusal

A produces: entry can carry a JSON Schema; a green/emit/seal whose value fails it is refused at the engine, not silently accepted and discovered downstream. Repeated schema failures trip the same stall mechanism as judgment rejections. See docs/authoring.md.

Cascade invalidation

Think of it like a build system: change a header file and make knows every object file that includes it needs recompiling, without you tracking that by hand. owenloop does the same thing for agent outputs — change an early step's result and everything built on it automatically falls back to "not done," and gets redone the next time its inputs are green. No manual invalidation code, no stale results slipping downstream. See docs/design.md §7.

Collections — fan-out/fan-in

A step can emit any number of elements at runtime; a map step runs once per element, and a reduce step runs once after they're all in (or, with a suffixed reduce, once every element's own per-element output is in). See docs/authoring.md and research.yaml.

Composition — include: and calls:

Build a workflow out of other workflows two ways: include: splices another def's steps directly into the parent at load time (one flat graph); calls: delegates to a separate child instance at runtime, keeping its internals hidden as a black box. See docs/authoring.md.

Side-effect policies — effect:

Most steps are safe to re-derive when their inputs move — that's what the cascade assumes by default. A step with an irreversible side effect (a deploy, a publish, an external write) can declare effect: { idempotent: false, onInvalidate: … } to tell the engine to pin the old result, escalate to a human, or run a compensating step instead of silently re-firing. See docs/authoring.md.

Model tiers

model: fast | standard | strong | strongest declares intent, not a vendor id — the engine passes it through untouched to whatever dispatches your workers. A portable workflow says "this step needs strong judgment"; the host binds that to whatever model it runs on. strong is a high-capability workhorse tier, not the host's single most capable model — that's what the opt-in strongest tier is for, reserved for the rare step where nothing less will do. See docs/authoring.md.

Worker dispatch

worker: agent | command | … declares which kind of executor a step's order is for — the default (agent, silent when omitted) is unaffected; every def written before this feature stays byte for byte the same. Opt a step into worker: command and give it a command: string to switch it to a deterministic executor instead of an LLM — the engine never runs it, only carries it through on the order for your dispatcher to branch on. An optional spec: map carries further opaque config (a timeout, a working directory), and a judge entry accepts the same fields, so a quality gate can be a script's exit code instead of a verdict. See docs/authoring.md and command-worker.yaml.

Event subscription — for embedding

Driving the engine in-process doesn't require polling: engine.subscribe(...) pushes a typed event the instant a mutation commits, so a host can react instead of ticking on a timer. See Embedding it and docs/embedding.md.


Driving it with a loop

owenloop never runs anything itself. It hands out jobs and waits to hear back — something has to tick it, run the work, and report the result. That something can be as simple as a while loop around an agent. The Ralph loop — keep an agent ticking with a fresh context each pass — is exactly this kind of outer loop, and owenloop is the half it's missing: the persistent state and the brakes. The loop keeps going; owenloop remembers what's owed, what failed and why, and when the whole thing is actually done. They work side by side — the loop is the muscle, owenloop is the memory.

The outer loop is deliberately not owenloop's business, which means it can be anything that can run a CLI command or call a function. In practice that looks like:

  • Your own harness — a while loop, a cron job, a CI stage: tick, run each order with whatever executes your work (an agent CLI, an API call, a script), report, repeat. Fully deterministic dispatch if you want it — see Embedding it for the in-process version. A reference dispatcher/conductor, owenwork, is available separately — the engine core ships no host harness of its own.
  • An agent as the orchestrator — point any tool-using agent (Claude Code, Codex, Gemini CLI, anything that can run a shell command) at the CLI and tell it to drive the instance to done. A slash command or skill that wraps this turns "run the release workflow" into one line.
  • An agent structuring its own work, inline — mid-task, an agent authors a throwaway workflow, drives itself through it, and deletes it: the engine as scratch discipline rather than standing infrastructure.

The engine doesn't know or care which of these is ticking it — an order is an order. For Claude Code specifically, three shipped skills implement these patterns ready-made:

  • owenloop-conduct — drive an existing workflow instance to done: tick, dispatch each order to a fresh subagent, report honestly.
  • owenloop-author — turn a plain-English goal into a validated workflow def, interactively, then drive it.
  • owenloop-ephemeral — author and drive a throwaway workflow to structure an agent's own mid-task work, then delete it.

Quick start

Install the owenloop skills for whatever agent you use — Claude Code, Codex, Cursor, and most others:

npx skills add typicalday/owenloop

Then ask your agent for what you want:

Use owenloop-author to build me a workflow that researches a topic, writes a report, and doesn't accept it until an independent reviewer signs off — then run it on "tidepools".

The skill interviews you for anything missing, writes and validates the YAML definition, then conducts the instance to done — dispatching each order to a fresh subagent, relaying knock-backs, and escalating stalls to you. Already have a def? "Conduct the report workflow" hands it to owenloop-conduct. The engine itself arrives via npx owenloop (Node ≥ 22.13 required) — no clone, no build, no environment variables, no CLI verbs to memorize.

Want to see or drive the machinery yourself? Everything above goes through the same small CLI (create, tick, green, reject, …). docs/cli.md has the full command reference and a hand-driven walkthrough of a pipeline — including a rejection knock-back and a stall — and examples/workflows has seven runnable defs, from a minimal review loop (delivery) to a full production line (ship) and a collections-heavy research pipeline (research). Full YAML grammar: docs/authoring.md. Driving it from your own code: Embedding it.

Want someone else's workflow defs instead of writing your own? owenloop add <owner>/<repo> fetches a public GitHub repo's workflows/**, validates every def, and installs them locally with pinned provenance — see the add entry in docs/cli.md. An installed def's steps later run with your workers' full privileges and owenloop never executes them itself, so install only sources you trust, and pin a commit SHA (@<sha>) for anything you re-add — the trust model is spelled out there.

Publishing your defs to a hosted hub instead? owenloop login authenticates the CLI (loopback OAuth, or a pasted token via --with-token; the credential goes into the macOS Keychain or a 0600 file, never the repo, and is verified against the hub before it's ever stored), owenloop connect binds the project to a hub, and owenloop push publishes your defs — idempotent against the hub's own def hashes, so an unchanged re-push is a no-op. See the Hub section in docs/cli.md.


Requirements

  • Node ≥ 22.13. Storage is Node's built-in node:sqlite, which is available unflagged from 22.13 onward (it still prints an experimental warning until it stabilises in Node 24.15 / 25.7). owenloop is an ESM-only package. CI runs the full check on Node 22 (active LTS) and 24 (current).
  • No native dependencies. node:sqlite is built in, so there's nothing to compile. The only runtime deps are yaml (parsing defs) and @cfworker/json-schema (optional per-artifact schema validation).
npm install owenloop
import { createEngine } from 'owenloop';   // see "Embedding it" below

Upgrading from 0.2.1

Upgrading a project from the 0.2.1 release pulls in everything that has landed since. The changes that need operator or embedder attention:

  • Deep recursive ticking. One tick now advances the whole calls: tree by default, so a single call can drive many workflow instances at once and their execution timing shifts. Embedders that key per-workflow logic — checkpoints, transactions, counters — must key on order.workflow, not the id passed to tick (--shallow / { deep: false } is the single-instance escape hatch). See Migrating from shallow ticking and Deep tick and order.workflow; the full guidance lives there.
  • Node ≥ 22.13 required. The engines field pins Node ≥ 22.13, and CI runs the full check on Node 22 and 24 — see Requirements just above.
  • Database schema v9. The first open by a new binary migrates the database in a single transaction that rolls back on failure; a database whose stored version is newer than the binary is refused rather than opened. Migrated databases do not get backfilled artifact-history payload snapshots — durable payload history begins after migration (legacy lifecycle reasons are carried over exactly once). Copy the SQLite database (.owenloop/state.db plus its -wal/-shm sidecars) before upgrading; downgrading means restoring that copy, because an older binary refuses a v9 database.
  • New retained plaintext data. Issued order packets and artifact history now persist in the database, so rotating and disposing of that data is the operator's job — see Retention and disposal.
  • New network-facing commands and credential storage. owenloop login / connect / push (and add, for installing shared defs) reach the network and store a credential in the macOS Keychain or a 0600 file — see the Hub section and the add trust model.
  • Max-lease cap is now opt-in. There is no default lease ceiling; anyone who relied on the brief post-0.2.1 default cap must set maxLeaseMs (or a per-step maxLease) explicitly.

Embedding it

The CLI is a thin adapter: it maps argv to engine calls and prints JSON. The engine is an ordinary class, so you can drive it in-process and get typed objects back (Order, CommitResult, WorkflowStatus) — no subprocess, no JSON parsing.

import { createEngine } from 'owenloop';

const { engine, store } = createEngine({
  db: '.owenloop/state.db',         // or ':memory:' for an ephemeral instance
  defsDir: 'workflows',             // load YAML defs from a dir … or pass `defs: [myDef]`
});

// start an instance (proposal is seeded as owed, so provide it up front)
const wf = engine.createInstance('delivery', {
  provide: { proposal: { text: 'add dark mode' } },
});

// the worker loop: tick → run → report
const { orders } = engine.tick(wf);
for (const order of orders) {
  const result = await runYourAgent(order);              // ← your domain
  engine.green(wf, order.run, order.outputs[0], result); // typed CommitResult back
  engine.close(wf, order.run);
}

engine.status(wf);   // typed WorkflowStatus: done / debts / eligible / blocked
store.close();        // on shutdown

Prefer to react instead of poll? engine.subscribe(listener) (or createEngine({ onEvent })) pushes a typed event the instant a mutation commits — so you can re-tick only when there's new work, or resolve a promise when the workflow is done. See examples/events.ts.

The engine/store pair is meant to be long-lived (one per database). Concurrency is the store's job: node:sqlite is synchronous and single-writer-per-process, and cross-process safety comes from a commit fingerprint check (described under Storage). See docs/embedding.md for the full surface, lifecycle, and trade-offs.


How it's built

owenloop is small and split along a pure-core / imperative-shell line:

| module | responsibility | |---|---| | src/types.ts | shared types: the six-state lifecycle, reason threads, def shapes | | src/paths.ts | parse/match the src[$i] / src[*] / src[] path grammar | | src/defs.ts | load YAML → validated WorkflowDef (the static wiring checks) | | src/schema.ts | JSON Schema validation of artifact values, via @cfworker/json-schema | | src/model.ts | the pure core: what's eligible, the cascade, status, stall detection | | src/store.ts | node:sqlite persistence; transactions; the commit check | | src/engine.ts | the imperative shell: tick/green/reject/… → mutate → settle() | | src/cli.ts | argv → engine calls, JSON on stdout |

Invariant: every engine mutation ends with settle() — materialize owed outputs and run the cascade to a fixpoint — so status() is a pure read over artifact state and never lies.

Storage

State lives in a single SQLite database via Node's built-in node:sqlite in WAL mode — no native module to compile, no separate graph engine. The flat artifact/task/run tables are the graph; the dependency structure is recomputed from the definition on each tick. Concurrent advancement is made safe by a commit fingerprint check: a run records the version of every input it claimed, and its commit is rejected ("born-rejected") if any of those inputs moved underneath it. Each artifact carries a monotonic version, so the engine can always ask "is this green output still resting on the inputs it was built from?".

Retention and disposal. That file is the system of record and it keeps everything: every artifact version's value and every run's issued order packet — including any sensitive values that flowed through an input — persist after a workflow finishes. The default location is .owenloop/state.db (plus its WAL -wal/-shm sidecar files), or wherever --db / OWENLOOP_DB / the db: embed option points. Rotating or scrubbing that data is the operator's job: owenloop delete <wf> removes an instance's rows, but SQLite frees those pages without erasing or shrinking the file on disk, so for reliable disposal delete the database file together with its -wal/-shm sidecars.

Testing

npm test          # node --test, spec reporter
npm run typecheck # tsc --noEmit (type-checks the source)
npm run check     # both
npm run build     # compile src/ → dist/ (also runs automatically on npm pack/publish)

The suite spans unit tests (paths, store, model, defs, schema, util, cli), engine integration tests (the cascade, the stall, schema validation, the concurrency check, judges: sign-off/CAS/throttling in test/judges.test.ts), and end-to-end tests that spawn the real bin/owenloop.mjs binary and drive the example workflows through their full lifecycles.

Two e2e files carry most of the weight, by opposite intent. test/edge.e2e.test.ts is an edge battery aimed at the corners the design is most particular about: cascade invalidation, terminal completion surviving an upstream reject, empty / fully-retracted collections, the commit check, cadence and daily-budget gating, the skip-cascade, and CLI robustness against malformed input. test/scenarios.e2e.test.ts takes the opposite tack — multi-step positive stories that confirm the documented behaviors hold end to end: the map parallel cap, map and reduce firing as concurrent branches, the reason thread riding the next job, stall → retry → re-stall, and the cascade re-firing on a re-provided input while leaving a healthy graph and a terminal output untouched. test/schema.e2e.test.ts drives schema validation end to end: a malformed value is rejected rather than greened, a corrected value greens on the same open job, repeated failures trip the stall and a retry clears it.


Design reference

owenloop is a faithful, decoupled implementation of a dataflow-engine spec. docs/design.md is a self-contained walkthrough — the lifecycle, firing rule, forward cascade, the reject kinds, the liveness rules, and the concurrency model — cross-referenced from the source. docs/cli.md has the full command reference, and docs/authoring.md has the full YAML grammar.


License

Apache-2.0 © Typical Day LLC.

owenloop is permissively licensed — use, modify, self-host, and redistribute it, including in proprietary or closed-source products, under the terms of the Apache License 2.0.

Contributing

Contributions are welcome — see CONTRIBUTING.md. Note that owenloop requires every contributor to sign a Contributor License Agreement that assigns copyright in contributions to Typical Day LLC, so the project can be maintained — and relicensed in the future if ever needed — under one clear owner. The process is a one-time comment on your first pull request.