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

@razroo/iso-orchestrator

v0.2.0

Published

Durable workflow primitives for AI-agent harnesses: resumable steps, keyed mutexes, and bounded parallel fan-out with file-backed state.

Downloads

695

Readme

@razroo/iso-orchestrator

Durable workflow primitives for AI-agent harnesses.

This package is for the layer above a single agent session. An agent runtime already knows how to think, call tools, and emit text. @razroo/iso-orchestrator adds the deterministic parts that should not live only in prompt prose:

  • resumable, idempotent step() execution with file-backed records
  • bounded parallel fan-out with forEach(..., { maxParallel })
  • keyed mutexes so "same entity" work never runs concurrently
  • worker heartbeats plus renewable leases for liveness / ownership tracking
  • append-only-ish workflow events and durable state snapshots on local disk

It is intentionally generic. There is no built-in "dispatch a Codex worker" or "spawn an OpenCode task" primitive yet. Domain packages bring their own adapter for that part and use this package to enforce invariants around it.

Install

npm install @razroo/iso-orchestrator

What it stores

By default the package writes under .iso-orchestrator/ in the current working directory:

.iso-orchestrator/
  workflows/
    my-flow-<hash>.json
  locks/
    my-flow-<hash>/
      record.lock/
      mutex/
        company-role-<hash>.lock/

The workflow record contains:

  • current workflow status (idle, running, completed, failed)
  • durable JSON state
  • step attempt counts and cached step results
  • optional heartbeat snapshots and lease ownership records
  • event history (workflow.running, step.started, step.completed, ...)

Quick example

import { runWorkflow } from '@razroo/iso-orchestrator';

const { value, record } = await runWorkflow(
  {
    workflowId: 'apply-batch-2026-04-25',
    dir: '.jobforge-runtime',
    initialState: { applied: 0, skipped: 0 },
  },
  async (workflow) => {
    await workflow.step('cleanup-geometra', async () => ({ ok: true }));

    const jobs = [
      { company: 'Anthropic', role: 'Staff Engineer' },
      { company: 'OpenAI', role: 'Member of Technical Staff' },
      { company: 'Anthropic', role: 'Staff Engineer' },
    ];

    const summary = await workflow.forEach(
      jobs,
      async (job) => {
        return workflow.step(
          `apply:${job.company}:${job.role}`,
          async () => {
            // Your own task-dispatch adapter goes here.
            return { outcome: 'APPLIED', company: job.company, role: job.role };
          },
          { idempotencyKey: `${job.company}:${job.role}` },
        );
      },
      {
        maxParallel: 2,
        mutexKey: (job) => `${job.company}:${job.role}`,
      },
    );

    await workflow.updateState((state) => ({
      ...state,
      applied: summary.fulfilled,
      skipped: summary.rejected,
    }));

    return summary;
  },
);

console.log(value.fulfilled, record.status);

API

runWorkflow(options, fn)

Creates or re-opens a workflow record, marks it running, executes fn, and marks the workflow completed or failed.

Options:

  • workflowId: durable identifier for the logical workflow
  • initialState: JSON value written only when the workflow record does not exist yet
  • dir: optional storage root (defaults to .iso-orchestrator)
  • now: optional clock injection for tests

openWorkflow(options)

Opens the workflow context without automatically running a top-level callback. Useful when you want finer control over lifecycle or only need inspection.

workflow.step(name, fn, options?)

Runs one load-bearing step and persists its result.

  • If the same step already completed, the cached JSON result is returned.
  • idempotencyKey lets one logical step name be reused safely across runs.
  • retry can be a number (3) or { attempts, shouldRetry }.

Results must be JSON-serializable because they are persisted in the record.

workflow.withMutex(key, fn, options?)

Runs fn while holding a process-safe filesystem lock for key. Useful when two parallel tasks must never touch the same entity at once.

Options:

  • timeoutMs: how long to wait for the lock
  • pollMs: wait interval while the lock is held elsewhere
  • staleAfterMs: optional stale-lock eviction threshold

workflow.heartbeat(key, detail?)

Persists the latest heartbeat for a named worker or phase. This is useful when you want external inspectors to tell whether a background task is still progressing even if it is not currently holding a mutex.

workflow.touchLease(key, { holder, ttlMs, detail? })

Acquires or renews a renewable lease for key.

  • a missing, expired, or released lease is acquired
  • the current holder can renew in place
  • a different holder gets a WorkflowLeaseConflictError while the lease is active

Use this for "one worker owns this slot until its heartbeat expires" semantics without introducing a harness-specific queue.

workflow.releaseLease(key, holder?)

Marks a lease released. Passing holder is optional but recommended so a worker cannot accidentally release another worker's active lease.

workflow.forEach(items, fn, options?)

Bounded fan-out over a list.

  • maxParallel controls concurrency
  • mutexKey(item) optionally serializes related items
  • stopOnError defaults to true

Returns a summary with results, fulfilled, and rejected.

workflow.updateState(updater)

Replaces the durable JSON state. Accepts either a full state object or a functional updater.

workflow.appendEvent(input) / workflow.getRecord()

Low-level helpers for custom lifecycle tracking and inspection.

Scope boundary

This package does not try to be Temporal.

What it does:

  • local durable records
  • load-bearing step caching
  • process-safe local mutexes
  • simple bounded concurrency

What it does not do yet:

  • distributed queues
  • remote workers
  • harness-specific task dispatch APIs
  • cron / scheduling

That narrower scope is deliberate. The goal is to let packages like JobForge stop expressing orchestration invariants only in prompt prose or Bash without forcing a heavyweight control plane.