
Security News
Happy Birthday, Shai-Hulud
It has been one year since Shai-Hulud made its first appearance on npm.
@agent-pattern-labs/iso-orchestrator
Advanced tools
Durable workflow primitives for AI-agent harnesses: resumable steps, keyed mutexes, and bounded parallel fan-out with file-backed state.
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. @agent-pattern-labs/iso-orchestrator
adds the deterministic parts that should not live only in prompt prose:
step() execution with file-backed recordsforEach(..., { maxParallel })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.
npm install @agent-pattern-labs/iso-orchestrator
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:
idle, running, completed, failed)workflow.running, step.started, step.completed, ...)import { runWorkflow } from '@agent-pattern-labs/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);
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 workflowinitialState: JSON value written only when the workflow record does not exist yetdir: optional storage root (defaults to .iso-orchestrator)now: optional clock injection for testsopenWorkflow(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.
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 lockpollMs: wait interval while the lock is held elsewherestaleAfterMs: optional stale-lock eviction thresholdworkflow.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.
WorkflowLeaseConflictError while the lease is activeUse 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 concurrencymutexKey(item) optionally serializes related itemsstopOnError defaults to trueReturns 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.
This package does not try to be Temporal.
What it does:
What it does not do yet:
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.
FAQs
Durable workflow primitives for AI-agent harnesses: resumable steps, keyed mutexes, and bounded parallel fan-out with file-backed state.
We found that @agent-pattern-labs/iso-orchestrator demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.

Security News
It has been one year since Shai-Hulud made its first appearance on npm.

Research
/Security News
Operators behind PolinRider used a compromised GitHub account to plant malware in four development versions of a Packagist package with 700,000+ downloads.

Security News
GitHub Actions now supports cache-mode, a least-privilege control on the Actions cache aimed at the cache poisoning technique behind recent compromises.