@particle-academy/fancy-flow 0.65.2 → 0.66.0
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 +100 -3
- package/dist/{FlowViewer-BI_AyyvU.d.ts → FlowViewer-BCc_BvuD.d.ts} +1 -1
- package/dist/{FlowViewer-DABx-6A-.d.cts → FlowViewer-CoAJ3o52.d.cts} +1 -1
- package/dist/{HumanPrompt-Bpp3JiVH.d.cts → HumanPrompt--pXDOyYC.d.cts} +2 -2
- package/dist/{HumanPrompt-BKThiyjB.d.ts → HumanPrompt-DXm_FP9z.d.ts} +2 -2
- package/dist/chunk-2QXKDLGT.js +58 -0
- package/dist/chunk-2QXKDLGT.js.map +1 -0
- package/dist/{chunk-77V4QC6Y.js → chunk-B7HKJJVV.js} +3 -3
- package/dist/{chunk-77V4QC6Y.js.map → chunk-B7HKJJVV.js.map} +1 -1
- package/dist/{chunk-MBVX4ZRB.js → chunk-FNYLKSFJ.js} +3 -3
- package/dist/{chunk-MBVX4ZRB.js.map → chunk-FNYLKSFJ.js.map} +1 -1
- package/dist/{chunk-JF6WCRBU.js → chunk-HQGOFGAL.js} +861 -40
- package/dist/chunk-HQGOFGAL.js.map +1 -0
- package/dist/{chunk-5H54OTKT.js → chunk-IEYCNFXZ.js} +3 -3
- package/dist/{chunk-5H54OTKT.js.map → chunk-IEYCNFXZ.js.map} +1 -1
- package/dist/{chunk-W5DPKJY4.js → chunk-MTONBM53.js} +4 -4
- package/dist/chunk-MTONBM53.js.map +1 -0
- package/dist/{chunk-RIAFHQT5.js → chunk-PNHZHEWE.js} +3 -3
- package/dist/{chunk-RIAFHQT5.js.map → chunk-PNHZHEWE.js.map} +1 -1
- package/dist/{chunk-EO6444T2.js → chunk-UCCTXJXL.js} +4 -4
- package/dist/{chunk-EO6444T2.js.map → chunk-UCCTXJXL.js.map} +1 -1
- package/dist/connectors.d.cts +2 -2
- package/dist/connectors.d.ts +2 -2
- package/dist/durable/index.d.cts +2 -2
- package/dist/durable/index.d.ts +2 -2
- package/dist/durable.cjs +837 -47
- package/dist/durable.cjs.map +1 -1
- package/dist/durable.js +2 -3
- package/dist/durable.js.map +1 -1
- package/dist/engine.cjs +843 -132
- package/dist/engine.cjs.map +1 -1
- package/dist/engine.d.cts +6 -7
- package/dist/engine.d.ts +6 -7
- package/dist/engine.js +5 -6
- package/dist/engine.js.map +1 -1
- package/dist/fields/react-fancy.d.cts +3 -3
- package/dist/fields/react-fancy.d.ts +3 -3
- package/dist/index.cjs +853 -140
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +65 -36
- package/dist/index.d.ts +65 -36
- package/dist/index.js +19 -18
- package/dist/index.js.map +1 -1
- package/dist/layout/index.d.cts +1 -1
- package/dist/layout/index.d.ts +1 -1
- package/dist/llm/prism.cjs.map +1 -1
- package/dist/llm/prism.d.cts +1 -2
- package/dist/llm/prism.d.ts +1 -2
- package/dist/llm/prism.js +1 -1
- package/dist/llm/vercel-ai.cjs.map +1 -1
- package/dist/llm/vercel-ai.d.cts +1 -2
- package/dist/llm/vercel-ai.d.ts +1 -2
- package/dist/llm/vercel-ai.js +1 -1
- package/dist/registry/index.d.cts +5 -6
- package/dist/registry/index.d.ts +5 -6
- package/dist/{registry-CntAdUcY.d.cts → registry-BcgllSk2.d.cts} +1 -1
- package/dist/{registry-DYoV-MQi.d.ts → registry-h-2QswZ5.d.ts} +1 -1
- package/dist/registry.cjs +867 -37
- package/dist/registry.cjs.map +1 -1
- package/dist/registry.js +3 -3
- package/dist/{run-cohort-C1nRh_mi.d.ts → run-cohort-CjUBWg6X.d.ts} +2 -2
- package/dist/{run-cohort-CBRAUj-z.d.cts → run-cohort-DL9JzzPU.d.cts} +2 -2
- package/dist/{run-flow-2XsHBwpd.d.cts → run-flow-B_8hgO5_.d.cts} +1 -1
- package/dist/{run-flow-D7AOCcfE.d.ts → run-flow-CxEGBOxd.d.ts} +1 -1
- package/dist/runtime/index.d.cts +5 -5
- package/dist/runtime/index.d.ts +5 -5
- package/dist/runtime.cjs +837 -36
- package/dist/runtime.cjs.map +1 -1
- package/dist/runtime.js +4 -4
- package/dist/schema/index.d.cts +19 -6
- package/dist/schema/index.d.ts +19 -6
- package/dist/schema.cjs +837 -36
- package/dist/schema.cjs.map +1 -1
- package/dist/schema.js +4 -4
- package/dist/screens.cjs +837 -36
- package/dist/screens.cjs.map +1 -1
- package/dist/screens.d.cts +2 -2
- package/dist/screens.d.ts +2 -2
- package/dist/screens.js +5 -5
- package/dist/terminal/fancy-term-host.cjs +99 -0
- package/dist/terminal/fancy-term-host.cjs.map +1 -0
- package/dist/terminal/fancy-term-host.d.cts +76 -0
- package/dist/terminal/fancy-term-host.d.ts +76 -0
- package/dist/terminal/fancy-term-host.js +92 -0
- package/dist/terminal/fancy-term-host.js.map +1 -0
- package/dist/types-B-Syk9-M.d.cts +620 -0
- package/dist/types-B-Syk9-M.d.ts +620 -0
- package/dist/{types-D71SKA5A.d.cts → types-C2ATTmjN.d.cts} +1 -1
- package/dist/{types-Ckhz-YwC.d.ts → types-VunLGqrn.d.ts} +1 -1
- package/dist/ux.cjs +837 -36
- package/dist/ux.cjs.map +1 -1
- package/dist/ux.d.cts +2 -2
- package/dist/ux.d.ts +2 -2
- package/dist/ux.js +2 -2
- package/package.json +11 -1
- package/dist/capabilities-BYa5p5jw.d.cts +0 -112
- package/dist/capabilities-COOXRiNL.d.ts +0 -112
- package/dist/chunk-JF6WCRBU.js.map +0 -1
- package/dist/chunk-UM4C46AF.js +0 -103
- package/dist/chunk-UM4C46AF.js.map +0 -1
- package/dist/chunk-USL4FMFU.js +0 -41
- package/dist/chunk-USL4FMFU.js.map +0 -1
- package/dist/chunk-W5DPKJY4.js.map +0 -1
- package/dist/types-JFYjPJAG.d.cts +0 -333
- package/dist/types-JFYjPJAG.d.ts +0 -333
package/dist/durable.js
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import { truthy } from './chunk-UM4C46AF.js';
|
|
2
1
|
import { pauseForHuman, decodePause } from './chunk-UEOE6B52.js';
|
|
3
|
-
import { getNodeKind, runFlow, resolveKindId, kindIds, RunIdentity } from './chunk-
|
|
2
|
+
import { getNodeKind, runFlow, resolveKindId, kindIds, truthy, RunIdentity } from './chunk-HQGOFGAL.js';
|
|
4
3
|
import './chunk-F5RPRB7A.js';
|
|
5
|
-
import './chunk-
|
|
4
|
+
import './chunk-2QXKDLGT.js';
|
|
6
5
|
|
|
7
6
|
// src/durable/state.ts
|
|
8
7
|
var NodeRunStatus = {
|
package/dist/durable.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/durable/state.ts","../src/durable/frontier.ts","../src/durable/replay.ts","../src/durable/retry.ts","../src/durable/human.ts","../src/durable/coordinator.ts"],"names":[],"mappings":"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","file":"durable.js","sourcesContent":["/**\n * What a durable run remembers, and the seam a real database plugs into.\n *\n * A durable run is bookkeeping plus one hard requirement: **the claim is a\n * unique constraint, not a check.** Two workers racing for the same node must\n * produce a no-op, not a double run, and only the storage layer can promise\n * that. So {@link NodeClaimStore} is an interface with exactly the operations a\n * driver needs, and an adapter implements it over `INSERT … ON CONFLICT DO\n * NOTHING` (or its dialect's spelling).\n *\n * {@link InMemoryClaimStore} is the reference implementation and is genuinely\n * useful: it makes the whole per-node driver testable, and it is correct for a\n * single-process durable run.\n *\n * This is a port of `fancy_flow.durable.state`, which is itself the Python twin\n * of `fancy-flow-php`'s `NodeClaims` + `workflow_run_nodes`. Three runtimes,\n * one design — see `.ai/plans/fancy-flow-run-identity-and-steps.md`.\n */\n\n/** Where one node of one run has got to. */\nexport const NodeRunStatus = {\n CLAIMED: \"claimed\",\n COMPLETED: \"completed\",\n SKIPPED: \"skipped\",\n FAILED: \"failed\",\n PAUSED: \"paused\",\n} as const;\n\nexport type NodeRunStatusValue = (typeof NodeRunStatus)[keyof typeof NodeRunStatus];\n\n/**\n * A node the frontier may treat as decided.\n *\n * A FAILED node is settled too — it will never publish, so its successors skip\n * rather than wait forever.\n */\nexport const SETTLED: readonly NodeRunStatusValue[] = [\n NodeRunStatus.COMPLETED,\n NodeRunStatus.SKIPPED,\n NodeRunStatus.FAILED,\n];\n\n/**\n * One node's row.\n *\n * `ports` are the ports the engine's own `node-output` events reported. They\n * are STORED, never recomputed: a second copy of the routing table would agree\n * for a year and then disagree on one branch.\n */\nexport type NodeState = {\n status: NodeRunStatusValue;\n ports: readonly string[];\n output?: unknown;\n error?: string | null;\n owner?: string | null;\n /** 1-based, incremented when an owner re-enters its own claim. */\n attempts: number;\n /** ISO-8601 UTC of the FIRST claim. Never updated — it is the retry clock. */\n firstAttemptAt: string;\n};\n\n/**\n * The persistence a per-node driver needs.\n *\n * Six operations. An adapter over Postgres, SQLite or Redis implements these\n * and nothing else; every rule about WHICH node may run lives in `frontier.ts`,\n * which reads only {@link state}.\n */\nexport interface NodeClaimStore {\n /**\n * Take exclusive ownership of one node of one run.\n *\n * MUST be atomic against concurrent callers, and MUST return `true` for a\n * caller re-entering its OWN claim — that is what lets a job's retry resume\n * instead of deadlocking against the row it wrote itself.\n */\n claim(runKey: string, nodeId: string, owner: string): boolean | Promise<boolean>;\n state(runKey: string): Record<string, NodeState> | Promise<Record<string, NodeState>>;\n complete(\n runKey: string,\n nodeId: string,\n output: unknown,\n ports: readonly string[],\n ): void | Promise<void>;\n skip(runKey: string, nodeId: string): void | Promise<void>;\n fail(runKey: string, nodeId: string, error: string): void | Promise<void>;\n pause(runKey: string, nodeId: string, reason: string): void | Promise<void>;\n}\n\n/**\n * A correct, single-process {@link NodeClaimStore}.\n *\n * JavaScript's single-threaded event loop makes the claim atomic for free —\n * there is no interleaving point inside a synchronous method. It is NOT durable\n * across a restart, which is the honest limit: use it for tests, for a CLI run,\n * and for a worker that genuinely owns the whole run.\n */\nexport class InMemoryClaimStore implements NodeClaimStore {\n private readonly runs = new Map<string, Map<string, NodeState>>();\n\n claim(runKey: string, nodeId: string, owner: string): boolean {\n const run = this.run(runKey);\n const existing = run.get(nodeId);\n\n if (!existing) {\n run.set(nodeId, {\n status: NodeRunStatus.CLAIMED,\n ports: [],\n owner,\n attempts: 1,\n firstAttemptAt: new Date().toISOString(),\n });\n return true;\n }\n\n // Re-entering our own claim is how a retry gets back in. Anything else —\n // another owner, or a settled node — is a lost race, and a lost race is a\n // NO-OP rather than a duplicate run.\n const ownClaim =\n (existing.status === NodeRunStatus.CLAIMED || existing.status === NodeRunStatus.PAUSED) &&\n existing.owner === owner;\n\n if (ownClaim) {\n existing.attempts += 1;\n existing.status = NodeRunStatus.CLAIMED;\n return true;\n }\n return false;\n }\n\n state(runKey: string): Record<string, NodeState> {\n const out: Record<string, NodeState> = {};\n for (const [nodeId, entry] of this.run(runKey)) out[nodeId] = { ...entry, ports: [...entry.ports] };\n return out;\n }\n\n complete(runKey: string, nodeId: string, output: unknown, ports: readonly string[]): void {\n const entry = this.entry(runKey, nodeId);\n entry.status = NodeRunStatus.COMPLETED;\n entry.output = output;\n entry.ports = [...ports];\n entry.error = null;\n }\n\n skip(runKey: string, nodeId: string): void {\n const entry = this.entry(runKey, nodeId);\n entry.status = NodeRunStatus.SKIPPED;\n entry.ports = [];\n }\n\n fail(runKey: string, nodeId: string, error: string): void {\n const entry = this.entry(runKey, nodeId);\n entry.status = NodeRunStatus.FAILED;\n entry.error = error;\n entry.ports = [];\n }\n\n pause(runKey: string, nodeId: string, reason: string): void {\n const entry = this.entry(runKey, nodeId);\n entry.status = NodeRunStatus.PAUSED;\n entry.error = reason;\n entry.ports = [];\n }\n\n /**\n * Drop a paused node's claim so a recorded answer can re-run it.\n *\n * Not part of the interface: resuming a human gate is the host's decision and\n * its storage's business. Provided here because the in-memory store is also\n * what the tests resume through.\n */\n release(runKey: string, nodeId: string): void {\n this.run(runKey).delete(nodeId);\n }\n\n private run(runKey: string): Map<string, NodeState> {\n let run = this.runs.get(runKey);\n if (!run) {\n run = new Map();\n this.runs.set(runKey, run);\n }\n return run;\n }\n\n /** The row for one node, created as CLAIMED if a driver never claimed it. */\n private entry(runKey: string, nodeId: string): NodeState {\n const run = this.run(runKey);\n let entry = run.get(nodeId);\n if (!entry) {\n entry = {\n status: NodeRunStatus.CLAIMED,\n ports: [],\n attempts: 1,\n firstAttemptAt: new Date().toISOString(),\n };\n run.set(nodeId, entry);\n }\n return entry;\n }\n}\n\nexport function isSettled(status: string | undefined): boolean {\n return status !== undefined && (SETTLED as readonly string[]).includes(status);\n}\n","/**\n * Which nodes can run RIGHT NOW, given what has already settled.\n *\n * ## Why this is not a second engine\n *\n * `runFlow` walks a Kahn topological order and, at each node, runs it when at\n * least one incoming edge is active. That is a total order because one process\n * executes every node. Split the graph across jobs and the same rule has to be\n * asked the other way round — not \"what is next\" but \"what is unblocked\" —\n * which is this module.\n *\n * The rule is the engine's, restated:\n *\n * - every direct predecessor has SETTLED (in topological order the engine has\n * already settled all of them by the time it reaches a node);\n * - and either the node has no incoming edges, or at least one incoming edge is\n * ACTIVE — its source completed and published on that edge's source handle.\n *\n * A node whose predecessors have all settled with no active edge is what the\n * engine reports as `idle/skipped`. Skipping SETTLES it, which can in turn\n * unblock — or skip — its own successors, so the pass repeats until nothing\n * changes. That cascade is how a dead branch collapses without leaving the run\n * stuck.\n *\n * ## The one thing it does NOT decide\n *\n * Which ports a result activated. Those rules (`__port`, `branch`, declared\n * outputs, the kind's ports, the `out` fallback) live in the engine and stay\n * there: this reads the ports back off the `node-output` events the engine\n * emitted when the node ran, stored on the claim row.\n */\n\nimport { getNodeKind, kindIds } from \"../registry/registry\";\nimport type { FlowEdge, FlowGraph } from \"../types\";\nimport { NodeRunStatus, isSettled, type NodeClaimStore, type NodeState } from \"./state\";\n\nexport type FrontierResult = {\n ready: string[];\n skipped: string[];\n};\n\nfunction isNote(type: string | undefined): boolean {\n if (!type) return false;\n if (type === \"note\") return true;\n const kind = getNodeKind(type);\n return kind ? kindIds(kind).includes(\"note\") || kind.category === \"annotation\" : false;\n}\n\nexport const Frontier = {\n compute(graph: FlowGraph, state: Record<string, NodeState>): FrontierResult {\n const incoming = new Map<string, FlowEdge[]>();\n for (const edge of graph.edges) {\n const list = incoming.get(edge.target) ?? [];\n list.push(edge);\n incoming.set(edge.target, list);\n }\n\n // Settled nodes and the ports they lit. A skipped node is settled with NO\n // ports, which is precisely what makes its successors skip too.\n const settled = new Map<string, readonly string[]>();\n const held = new Set<string>();\n for (const [nodeId, entry] of Object.entries(state)) {\n if (entry.status === NodeRunStatus.COMPLETED) settled.set(nodeId, entry.ports);\n else if (isSettled(entry.status)) settled.set(nodeId, []);\n else held.add(nodeId);\n }\n\n const ready: string[] = [];\n const skipped: string[] = [];\n\n let changed = true;\n while (changed) {\n changed = false;\n\n for (const node of graph.nodes) {\n const nodeId = node.id;\n if (settled.has(nodeId) || held.has(nodeId) || ready.includes(nodeId)) continue;\n\n const edges = incoming.get(nodeId) ?? [];\n let blocked = false;\n let active = false;\n\n for (const edge of edges) {\n if (!settled.has(edge.source)) {\n blocked = true;\n break;\n }\n // The engine's port key: an edge with no source handle reads the\n // source's `out` port.\n if (settled.get(edge.source)!.includes(edge.sourceHandle ?? \"out\")) active = true;\n }\n\n if (blocked) continue;\n\n // Reached, but down a branch that never lit.\n if (edges.length > 0 && !active) {\n settled.set(nodeId, []);\n skipped.push(nodeId);\n changed = true;\n continue;\n }\n\n // Annotations and layout are never executed. Settling them here rather\n // than dispatching a job saves a queue round trip per sticky note — and\n // a graph can carry a lot of sticky notes.\n if (isNote(node.type) || getNodeKind(node.type ?? \"\")?.category === \"layout\") {\n settled.set(nodeId, []);\n skipped.push(nodeId);\n changed = true;\n continue;\n }\n\n ready.push(nodeId);\n }\n }\n\n return { ready, skipped };\n },\n\n /** Has every node settled? The run is finished when it has. */\n isComplete(graph: FlowGraph, state: Record<string, NodeState>): boolean {\n return graph.nodes.every((node) => isSettled(state[node.id]?.status));\n },\n\n /**\n * Is any node still held by a worker, or parked for a person?\n *\n * An empty frontier means something different depending on this: with work in\n * flight the run is simply waiting, and whichever job finishes will advance\n * it. With nothing in flight and nodes still unsettled, the graph cannot\n * progress at all — which is a stuck run, and must be reported rather than\n * waited on.\n */\n hasWorkInFlight(state: Record<string, NodeState>): boolean {\n return Object.values(state).some(\n (entry) => entry.status === NodeRunStatus.CLAIMED || entry.status === NodeRunStatus.PAUSED,\n );\n },\n\n /** Persist the skip cascade so the next pass does not recompute it. */\n async settleSkips(\n store: NodeClaimStore,\n runKey: string,\n skipped: readonly string[],\n ): Promise<void> {\n for (const nodeId of skipped) await store.skip(runKey, nodeId);\n },\n};\n","/**\n * Run ONE node of a graph — through the real engine, not around it.\n *\n * ## The problem this solves\n *\n * A per-node driver has to hand a node exactly the inputs it would have\n * received mid-run: the right values, on the right target handles, from the\n * right *active* edges. Those rules are the engine's (`collectInputs`,\n * `activatedPorts`, the merge-after-decision contract, the `out` fallbacks),\n * and they are the reason the three runtimes agree. Re-implementing them here\n * would be a second engine wearing a driver's clothes, and the two would drift.\n *\n * ## What it does instead\n *\n * It replays the graph with `runFlow` untouched:\n *\n * - every node already completed is fed back as `resumeOutputs`, so the engine\n * republishes it on the same ports and routes exactly as it did the first\n * time;\n * - every node EXCEPT the target is bound, by node id, to a boundary executor\n * that aborts — and a node-id binding outranks every kind binding and the\n * `*` fallback, so the fence holds whatever a host registered;\n * - so the engine walks its own topological order, skips its own dead branches,\n * collects the target's inputs its own way, runs the target — and stops at\n * the next thing it would have run.\n *\n * The target's output is `result.outputs[nodeId]`, and the ports it activated\n * arrive as the engine's own `node-output` events. Nothing about routing is\n * recomputed here.\n *\n * ## The cost, stated plainly\n *\n * Replaying the completed prefix is O(nodes) per node, so a run is O(nodes²) in\n * bookkeeping. The republish executes nothing — it re-publishes stored values —\n * so for the graph sizes workflows actually have this is noise next to a single\n * queue round trip. It buys exact fidelity to the engine, which is not\n * negotiable, and one implementation of the routing rules instead of two.\n */\n\nimport { runFlow, type RunResult } from \"../runtime/run-flow\";\nimport type { RunIdentity } from \"../runtime/run-identity\";\nimport type { ExecutorRegistry, FlowGraph, RunEvent } from \"../types\";\n\n/**\n * The abort reason the boundary executor uses.\n *\n * Not a failure: it is the engine telling us it reached a node this job is not\n * responsible for.\n */\nexport const BOUNDARY = \"fancy-flow:node-boundary\";\n\nexport type ReplayResult = {\n result: RunResult;\n /** node id -> the ports its output activated, from the engine's own events. */\n ports: Record<string, string[]>;\n outputOf(nodeId: string): unknown;\n portsOf(nodeId: string): string[];\n};\n\nexport type ReplayOptions = {\n resumeOutputs?: Record<string, unknown>;\n initialInputs?: Record<string, Record<string, unknown>>;\n onEvent?: (event: RunEvent) => void;\n depth?: number;\n run?: RunIdentity;\n};\n\n/**\n * Replay `graph` up to and through `nodeId`.\n *\n * Pass `nodeId = null` to PROBE: every node is a boundary, so nothing executes\n * and the engine reports only what it can determine structurally — a cycle, and\n * the ports each resumed output republishes on.\n */\nexport async function replayUpTo(\n graph: FlowGraph,\n nodeId: string | null,\n executors: ExecutorRegistry,\n options: ReplayOptions = {},\n): Promise<ReplayResult> {\n const fenced: ExecutorRegistry = { ...executors };\n for (const node of graph.nodes) {\n if (node.id !== nodeId) {\n fenced[node.id] = (ctx) => ctx.abort(BOUNDARY);\n }\n }\n\n const ports: Record<string, string[]> = {};\n const collect = (event: RunEvent): void => {\n if (event.type === \"node-output\") {\n (ports[event.nodeId] ??= []).push(event.portId);\n }\n options.onEvent?.(event);\n };\n\n const result = await runFlow(graph, fenced, collect, {\n initialInputs: options.initialInputs ?? {},\n resumeOutputs: options.resumeOutputs ?? {},\n depth: options.depth ?? 0,\n run: options.run,\n });\n\n return {\n result,\n ports,\n outputOf: (id) => result.outputs[id],\n portsOf: (id) => ports[id] ?? [],\n };\n}\n\n/** True when a replay ended because it reached a node it does not own. */\nexport function isBoundary(error: string | null | undefined): boolean {\n return error === BOUNDARY;\n}\n","/**\n * How many times a single node may be attempted.\n *\n * ## Why this cannot be one number\n *\n * A run-wide `tries` setting forces every workflow to pick between two bad\n * answers. At 1, a single flaky LLM or HTTP call takes the whole run down.\n * Above 1, the retry replays from the last checkpoint and everything already\n * done runs again — including the nodes that must not: `git_pr_open` opens a\n * second pull request.\n *\n * Per-node jobs make the question per node, which is where it always belonged.\n * A node declaring `sideEffects: unsafe-to-replay` is pinned to ONE attempt and\n * no backoff. Everything else takes the configured tries, or a per-kind\n * override.\n *\n * Undeclared side effects are treated as the configured default rather than\n * assumed safe: this decides retries, and inventing a safety claim on a node\n * author's behalf is how a retry loop ends up posting the same webhook twice.\n */\n\nimport { getNodeKind, kindIds, resolveKindId } from \"../registry/registry\";\nimport type { FlowNode } from \"../types\";\n\n/** A node that is not safe to run twice. Same vocabulary as the node manifest. */\nexport const UNSAFE_TO_REPLAY = \"unsafe-to-replay\";\n\nexport type RetryPolicyOptions = {\n tries?: number;\n backoffSeconds?: number;\n /** kind id -> tries. Keyed by any spelling; every id is checked. */\n perKind?: Record<string, number>;\n};\n\nexport class RetryPolicy {\n readonly tries: number;\n readonly backoffSeconds: number;\n readonly perKind: Record<string, number>;\n\n constructor(options: RetryPolicyOptions = {}) {\n this.tries = options.tries ?? 1;\n this.backoffSeconds = options.backoffSeconds ?? 0;\n this.perKind = options.perKind ?? {};\n }\n\n triesFor(node: FlowNode): number {\n if (RetryPolicy.isUnsafeToReplay(node)) return 1;\n\n for (const id of idsFor(node)) {\n if (id in this.perKind) return Math.max(1, Math.trunc(this.perKind[id]!));\n }\n\n return Math.max(1, Math.trunc(this.tries));\n }\n\n backoffFor(node: FlowNode): number {\n // Nothing to back off from: the node gets one attempt.\n if (RetryPolicy.isUnsafeToReplay(node)) return 0;\n return Math.max(0, this.backoffSeconds);\n }\n\n static isUnsafeToReplay(node: FlowNode): boolean {\n if (!node.type) return false;\n return getNodeKind(node.type)?.sideEffects === UNSAFE_TO_REPLAY;\n }\n}\n\n/**\n * Every id a per-kind override for this node could be keyed under.\n *\n * Canonical ids are namespaced while a host almost certainly writes the bare\n * one; keying on only the literal string would make the override silently stop\n * applying the day a kind is renamed.\n */\nfunction idsFor(node: FlowNode): string[] {\n if (!node.type) return [];\n const kind = getNodeKind(node.type);\n const canonical = resolveKindId(node.type);\n const ordered = [node.type, ...(canonical ? [canonical] : []), ...(kind ? kindIds(kind) : [])];\n return [...new Set(ordered)];\n}\n","/**\n * Human gates that cannot be walked past — and that hold no worker.\n *\n * ## The directive these implement\n *\n * A node that waits on a person must not occupy a worker, a connection or a\n * process while it waits. So the job for a human gate does its work by\n * **emitting the request** and then **finishing**: the run is persisted as\n * parked, the job returns, the worker takes the next thing off the queue. When\n * the person answers, *that* enqueues the continuation.\n *\n * Mechanically the gate aborts with an encoded pause, which is a *return*, not\n * a block. Nothing in this file sleeps, polls or awaits a person.\n *\n * ## Fail closed, and why\n *\n * A gate pauses because it **is** a human node, not because its input port\n * happens to be empty. This is not a preference; it is a fix. Both peer\n * runtimes once decided whether to pause by reading their own input, so a\n * pre-filled `values` or `approved` value ran the flow straight past the person\n * it was waiting for — silently, with the run reporting success.\n *\n * Restoring the old behaviour is possible, explicit, and per node:\n * `autoAnswerFromInput`. Turn it on for a step that is a form when a human is\n * present and a pass-through when an upstream node already produced the answer.\n * On an approval node, weigh it harder: it means the graph, not a person, can\n * approve.\n *\n * ## The other half of the fix\n *\n * Recording an answer for a node the run is not parked on THROWS rather than\n * queueing a write nobody reads. See {@link Submissions.record}.\n */\n\nimport { pauseForHuman } from \"../registry/pause\";\nimport { truthy } from \"../expressions/expr\";\nimport type { NodeExecutor } from \"../types\";\n\n/** An answer was recorded for a node the run is not waiting on. */\nexport class NotAwaitingHuman extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"NotAwaitingHuman\";\n }\n}\n\n/**\n * Answers recorded for human gates, keyed by node id.\n *\n * Deliberately separate from the run's inputs. Keeping them in one bag is\n * precisely what let a pre-filled input satisfy a gate.\n */\nexport class Submissions {\n private readonly answers = new Map<string, unknown>();\n /** The node the run is currently parked on, if any. */\n awaiting: string | null = null;\n\n /**\n * Record an answer for the node the run is parked on.\n *\n * Throws when the run is not waiting on that node. A queued answer for a node\n * that never paused is a write nobody reads — and it looks, from the outside,\n * exactly like a submission that worked.\n */\n record(nodeId: string, value: unknown): void {\n if (this.awaiting !== null && this.awaiting !== nodeId) {\n throw new NotAwaitingHuman(\n `This run is waiting on \"${this.awaiting}\", not \"${nodeId}\". Recording an answer for a ` +\n \"node the run is not parked on would be stored and never read.\",\n );\n }\n if (this.awaiting === null) {\n throw new NotAwaitingHuman(\n `This run is not waiting for anyone, so an answer for \"${nodeId}\" has nothing to resume.`,\n );\n }\n this.answers.set(nodeId, value);\n this.awaiting = null;\n }\n\n answered(nodeId: string): boolean {\n return this.answers.has(nodeId);\n }\n\n answer(nodeId: string): unknown {\n return this.answers.get(nodeId);\n }\n\n park(nodeId: string): void {\n this.awaiting = nodeId;\n }\n}\n\nfunction option(ctx: { node: { data?: unknown } }, key: string, fallback?: unknown): unknown {\n const config = ((ctx.node.data as any)?.config ?? {}) as Record<string, unknown>;\n return config[key] ?? fallback;\n}\n\n/** `user_input` — pauses until a submission for THIS node is recorded. */\nexport function durableUserInput(submissions: Submissions): NodeExecutor {\n return (ctx) => {\n if (submissions.answered(ctx.node.id)) return submissions.answer(ctx.node.id);\n\n if (option(ctx, \"autoAnswerFromInput\") === true) {\n const values = (ctx.inputs as Record<string, unknown>).values;\n if (values !== undefined) return values;\n }\n\n submissions.park(ctx.node.id);\n // Emits the request and RETURNS. A host listening to the feed sends the\n // email; the worker is free the moment this throws.\n return pauseForHuman(ctx, \"input\", {\n title: option(ctx, \"title\", \"Need your input\"),\n fields: option(ctx, \"fields\", []),\n });\n };\n}\n\n/** `human_approval` — pauses until a decision for THIS node is recorded. */\nexport function durableApproval(submissions: Submissions): NodeExecutor {\n return (ctx) => {\n const passthrough = (ctx.inputs as Record<string, unknown>).in ?? ctx.inputs;\n\n if (submissions.answered(ctx.node.id)) {\n return { branch: truthy(submissions.answer(ctx.node.id)) ? \"approved\" : \"denied\", value: passthrough };\n }\n\n if (option(ctx, \"autoAnswerFromInput\") === true) {\n const decision = (ctx.inputs as Record<string, unknown>).approved;\n if (decision !== undefined) {\n return { branch: truthy(decision) ? \"approved\" : \"denied\", value: passthrough };\n }\n }\n\n submissions.park(ctx.node.id);\n return pauseForHuman(ctx, \"approval\", {\n title: option(ctx, \"title\", \"Approve action\"),\n description: option(ctx, \"description\"),\n });\n };\n}\n","/**\n * The per-node driver, with the queue left out.\n *\n * This is the whole of \"how a queued run branches\", minus the transport. It\n * owns two operations and nothing else:\n *\n * `advance()`\n * Ask the frontier what is unblocked, settle the skip cascade, and report the\n * ready node ids. A queue adapter dispatches one job per id.\n *\n * `runNode()`\n * Claim one node, replay the graph through the real engine fenced to that\n * node, and checkpoint the output plus the ports the ENGINE said it\n * activated.\n *\n * Everything a queue library would add — enqueue, retry scheduling, worker\n * lifecycle — sits outside. That separation is the point: it makes the subtle\n * part (which node may run, and with what inputs) testable in-process, with no\n * broker, and identical under every adapter. **A queue adapter therefore\n * contains no workflow logic at all.** That is the test: if an adapter needs to\n * know what a port is, the seam is in the wrong place.\n *\n * `runToCompletion` drives both in one process. It is a real durable runner,\n * not a toy: with a persistent `NodeClaimStore` it survives a crash exactly as\n * a queued run does, because the crash-resume behaviour lives in the\n * checkpoints rather than in the loop.\n *\n * ## No worker waits on a person\n *\n * A human gate returns `paused`. `runToCompletion` returns immediately when it\n * sees one — it does not spin, sleep or poll. The run is parked in the store,\n * the process is free, and a recorded answer is what starts the next job.\n */\n\nimport { decodePause, type PauseSignal } from \"../registry/pause\";\nimport type { RunResult } from \"../runtime/run-flow\";\nimport { RunIdentity, type RunIdentityJson } from \"../runtime/run-identity\";\nimport type { ExecutorRegistry, FlowGraph, RunEvent } from \"../types\";\nimport { Frontier } from \"./frontier\";\nimport { isBoundary, replayUpTo } from \"./replay\";\nimport { RetryPolicy } from \"./retry\";\nimport {\n InMemoryClaimStore,\n NodeRunStatus,\n type NodeClaimStore,\n type NodeState,\n} from \"./state\";\n\n/** What happened to one node. */\nexport type NodeOutcome = {\n nodeId: string;\n status: \"not-claimed\" | \"completed\" | \"skipped\" | \"failed\" | \"paused\";\n output?: unknown;\n ports?: string[];\n error?: string;\n pause?: PauseSignal;\n /** False when another worker got there first. A lost race is a NO-OP. */\n claimed: boolean;\n /** 1-based attempt this execution ran as. */\n attempt: number;\n /**\n * `true` when this attempt failed and the policy still allows another.\n *\n * The claim row is left CLAIMED in that case, deliberately: a queue adapter\n * re-dispatches the job with the SAME owner token and the retry re-enters the\n * claim it already holds. Recording FAILED here instead would settle the\n * node, which SKIPS everything downstream — a run reporting a tidy finish\n * having done half its work.\n */\n retryable?: boolean;\n};\n\nexport type DurableRunResult = {\n ok: boolean;\n outputs: Record<string, unknown>;\n error?: string;\n pause?: PauseSignal;\n paused: boolean;\n};\n\nexport type CoordinatorOptions = {\n graph: FlowGraph;\n executors: ExecutorRegistry;\n /**\n * The run's stable identity. A bare string is taken as the run key.\n *\n * Required — not defaulted. A durable run without a stable key cannot key an\n * idempotent write, and minting one per construction would hand a retrying\n * host a different key each time.\n */\n run: string | RunIdentity | RunIdentityJson;\n store?: NodeClaimStore;\n initialInputs?: Record<string, Record<string, unknown>>;\n retry?: RetryPolicy;\n onEvent?: (event: RunEvent) => void;\n};\n\nexport class Coordinator {\n readonly graph: FlowGraph;\n readonly executors: ExecutorRegistry;\n readonly run: RunIdentity;\n readonly store: NodeClaimStore;\n readonly initialInputs: Record<string, Record<string, unknown>>;\n readonly retry: RetryPolicy;\n private readonly onEvent?: (event: RunEvent) => void;\n\n constructor(options: CoordinatorOptions) {\n this.graph = options.graph;\n this.executors = options.executors;\n this.run = RunIdentity.from(options.run);\n this.store = options.store ?? new InMemoryClaimStore();\n this.initialInputs = options.initialInputs ?? {};\n this.retry = options.retry ?? new RetryPolicy();\n this.onEvent = options.onEvent;\n }\n\n get runKey(): string {\n return this.run.runKey;\n }\n\n // -- the two operations ----------------------------------------------\n\n /**\n * Which nodes may be dispatched right now.\n *\n * Also settles the skip cascade, because a skip is a decision the frontier\n * just made and a second caller must not make it again.\n */\n async advance(): Promise<string[]> {\n const frontier = Frontier.compute(this.graph, await this.store.state(this.runKey));\n await Frontier.settleSkips(this.store, this.runKey, frontier.skipped);\n return frontier.ready;\n }\n\n /**\n * Claim, execute and checkpoint one node.\n *\n * The claim is taken FIRST. Two workers racing for the same node produce one\n * execution and one no-op, and the loser learns that from the store rather\n * than from a duplicate side effect.\n *\n * `owner` is the token that lets a job's own retry re-enter its claim instead\n * of deadlocking against the row it wrote itself — pass the SAME token across\n * a job's attempts.\n */\n async runNode(nodeId: string, owner?: string): Promise<NodeOutcome> {\n const token = owner ?? cryptoRandom();\n if (!(await this.store.claim(this.runKey, nodeId, token))) {\n return { nodeId, status: \"not-claimed\", claimed: false, attempt: 0 };\n }\n\n const row = (await this.store.state(this.runKey))[nodeId];\n const identity = this.identityFor(row);\n\n const replay = await replayUpTo(this.graph, nodeId, this.executors, {\n resumeOutputs: await this.completedOutputs(),\n initialInputs: this.initialInputs,\n onEvent: this.forward(nodeId),\n run: identity,\n });\n const result = replay.result;\n const attempt = identity.attempt;\n\n if (Object.prototype.hasOwnProperty.call(result.outputs, nodeId)) {\n const output = result.outputs[nodeId];\n const ports = replay.portsOf(nodeId);\n await this.store.complete(this.runKey, nodeId, output, ports);\n return { nodeId, status: \"completed\", output, ports, claimed: true, attempt };\n }\n\n // The node did not produce an output. Three reasons, and they are NOT\n // interchangeable.\n const pause = decodePause(result.error);\n if (pause && pause.nodeId === nodeId) {\n await this.store.pause(this.runKey, nodeId, result.error ?? \"\");\n return { nodeId, status: \"paused\", pause, claimed: true, attempt };\n }\n\n if (isBoundary(result.error)) {\n // The engine stopped at a node this job does not own BEFORE reaching the\n // target — so the target was never actually unblocked. That is a frontier\n // bug, not a node failure, and it must not be recorded as one: a FAILED\n // node settles, and settling it would silently skip everything\n // downstream.\n await this.store.skip(this.runKey, nodeId);\n return {\n nodeId,\n status: \"skipped\",\n error: \"replay stopped before reaching this node\",\n claimed: true,\n attempt,\n };\n }\n\n const error = result.error ?? `node ${nodeId} produced no output and no error`;\n const node = this.graph.nodes.find((n) => n.id === nodeId);\n const tries = node ? this.retry.triesFor(node) : 1;\n\n if (attempt < tries) {\n // Leave the row CLAIMED so the same owner can re-enter it. This is what\n // `fancy-flow-php` does by only marking FAILED from the job's `failed()`\n // hook — the row a worker still holds must not settle mid-retry.\n return { nodeId, status: \"failed\", error, claimed: true, attempt, retryable: true };\n }\n\n await this.store.fail(this.runKey, nodeId, error);\n return { nodeId, status: \"failed\", error, claimed: true, attempt, retryable: false };\n }\n\n // -- an in-process driver over the two ------------------------------\n\n /**\n * Drive the graph here, in this process, one node at a time.\n *\n * Every checkpoint is written exactly as a queued run writes it, so a crash\n * mid-loop resumes from the same place a crashed worker would.\n *\n * Retries honour {@link RetryPolicy}: a node declaring `unsafe-to-replay`\n * gets one attempt whatever the policy says, and the retry re-enters the same\n * claim with the same owner token — so the step key it derives is unchanged,\n * which is what makes the retry idempotent rather than duplicative.\n */\n async runToCompletion(maxPasses = 10_000): Promise<DurableRunResult> {\n for (let pass = 0; pass < maxPasses; pass++) {\n const ready = await this.advance();\n if (ready.length === 0) break;\n\n for (const nodeId of ready) {\n const outcome = await this.runNodeWithRetries(nodeId);\n\n if (outcome.status === \"paused\") {\n // A pause parks the RUN, not just the node: continuing would run the\n // human gate's siblings while a person is still deciding. The cohort\n // waits. Nothing here blocks — this RETURNS.\n return {\n ok: false,\n outputs: await this.outputs(),\n pause: outcome.pause,\n paused: true,\n };\n }\n if (outcome.status === \"failed\") {\n return { ok: false, outputs: await this.outputs(), error: outcome.error, paused: false };\n }\n }\n }\n\n const state = await this.store.state(this.runKey);\n if (!Frontier.isComplete(this.graph, state)) {\n if (Frontier.hasWorkInFlight(state)) {\n return {\n ok: false,\n outputs: await this.outputs(),\n error: \"the run is waiting on work held elsewhere\",\n paused: false,\n };\n }\n const unsettled = this.graph.nodes\n .filter((n) => ![\"completed\", \"skipped\", \"failed\"].includes(state[n.id]?.status ?? \"\"))\n .map((n) => n.id);\n return {\n ok: false,\n outputs: await this.outputs(),\n error: `the run cannot progress; unsettled nodes: ${unsettled.join(\", \")}`,\n paused: false,\n };\n }\n\n const failed = Object.values(state)\n .filter((e) => e.status === NodeRunStatus.FAILED)\n .map((e) => e.error ?? \"node failed\");\n\n return {\n ok: failed.length === 0,\n outputs: await this.outputs(),\n error: failed[0],\n paused: false,\n };\n }\n\n /**\n * Checkpointed outputs, in the graph's own node order.\n *\n * Ordered by the graph rather than by completion so two runs of the same\n * workflow produce comparable output maps even when nodes finished in a\n * different order.\n */\n async outputs(): Promise<Record<string, unknown>> {\n const state = await this.store.state(this.runKey);\n const out: Record<string, unknown> = {};\n for (const node of this.graph.nodes) {\n if (state[node.id]?.status === NodeRunStatus.COMPLETED) out[node.id] = state[node.id]!.output;\n }\n return out;\n }\n\n /** The checkpointed run, in the shape a single-process run returns. */\n async asRunResult(): Promise<RunResult> {\n const outcome = await this.runToCompletion();\n return outcome.error === undefined\n ? { ok: outcome.ok, outputs: outcome.outputs }\n : { ok: outcome.ok, outputs: outcome.outputs, error: outcome.error };\n }\n\n // -- internals -------------------------------------------------------\n\n private async runNodeWithRetries(nodeId: string): Promise<NodeOutcome> {\n // One owner token for every attempt of this node — that is what re-enters\n // the claim rather than losing the race to itself, and it is why the step\n // key a retrying node derives is the same one its first attempt sent.\n const owner = cryptoRandom();\n\n let outcome = await this.runNode(nodeId, owner);\n while (outcome.retryable) {\n outcome = await this.runNode(nodeId, owner);\n }\n return outcome;\n }\n\n /**\n * The identity handed to the node about to execute.\n *\n * `attempt` and `firstAttemptAt` come off the CLAIM ROW, so they describe\n * this step rather than the run — which is what makes the retry window check\n * exact instead of conservative.\n */\n private identityFor(row: NodeState | undefined): RunIdentity {\n if (!row) return this.run;\n return this.run.withAttempt(row.attempts, row.firstAttemptAt);\n }\n\n private async completedOutputs(): Promise<Record<string, unknown>> {\n const state = await this.store.state(this.runKey);\n const out: Record<string, unknown> = {};\n for (const [nodeId, entry] of Object.entries(state)) {\n if (entry.status === NodeRunStatus.COMPLETED) out[nodeId] = entry.output;\n }\n return out;\n }\n\n /**\n * Forward only the events the target node produced.\n *\n * A replay re-emits the whole completed prefix. Passing that through would\n * show a consumer every node running again on every job — the run feed would\n * report a 20-node workflow as 200 status changes.\n */\n private forward(nodeId: string): ((event: RunEvent) => void) | undefined {\n const sink = this.onEvent;\n if (!sink) return undefined;\n return (event) => {\n const id = \"nodeId\" in event ? event.nodeId : undefined;\n if (id === undefined || id === nodeId) sink(event);\n };\n }\n}\n\nfunction cryptoRandom(): string {\n const g = globalThis as { crypto?: { randomUUID?: () => string } };\n return g.crypto?.randomUUID?.() ?? `own_${Math.random().toString(36).slice(2)}${Date.now()}`;\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/durable/state.ts","../src/durable/frontier.ts","../src/durable/replay.ts","../src/durable/retry.ts","../src/durable/human.ts","../src/durable/coordinator.ts"],"names":[],"mappings":"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","file":"durable.js","sourcesContent":["/**\n * What a durable run remembers, and the seam a real database plugs into.\n *\n * A durable run is bookkeeping plus one hard requirement: **the claim is a\n * unique constraint, not a check.** Two workers racing for the same node must\n * produce a no-op, not a double run, and only the storage layer can promise\n * that. So {@link NodeClaimStore} is an interface with exactly the operations a\n * driver needs, and an adapter implements it over `INSERT … ON CONFLICT DO\n * NOTHING` (or its dialect's spelling).\n *\n * {@link InMemoryClaimStore} is the reference implementation and is genuinely\n * useful: it makes the whole per-node driver testable, and it is correct for a\n * single-process durable run.\n *\n * This is a port of `fancy_flow.durable.state`, which is itself the Python twin\n * of `fancy-flow-php`'s `NodeClaims` + `workflow_run_nodes`. Three runtimes,\n * one design — see `.ai/plans/fancy-flow-run-identity-and-steps.md`.\n */\n\n/** Where one node of one run has got to. */\nexport const NodeRunStatus = {\n CLAIMED: \"claimed\",\n COMPLETED: \"completed\",\n SKIPPED: \"skipped\",\n FAILED: \"failed\",\n PAUSED: \"paused\",\n} as const;\n\nexport type NodeRunStatusValue = (typeof NodeRunStatus)[keyof typeof NodeRunStatus];\n\n/**\n * A node the frontier may treat as decided.\n *\n * A FAILED node is settled too — it will never publish, so its successors skip\n * rather than wait forever.\n */\nexport const SETTLED: readonly NodeRunStatusValue[] = [\n NodeRunStatus.COMPLETED,\n NodeRunStatus.SKIPPED,\n NodeRunStatus.FAILED,\n];\n\n/**\n * One node's row.\n *\n * `ports` are the ports the engine's own `node-output` events reported. They\n * are STORED, never recomputed: a second copy of the routing table would agree\n * for a year and then disagree on one branch.\n */\nexport type NodeState = {\n status: NodeRunStatusValue;\n ports: readonly string[];\n output?: unknown;\n error?: string | null;\n owner?: string | null;\n /** 1-based, incremented when an owner re-enters its own claim. */\n attempts: number;\n /** ISO-8601 UTC of the FIRST claim. Never updated — it is the retry clock. */\n firstAttemptAt: string;\n};\n\n/**\n * The persistence a per-node driver needs.\n *\n * Six operations. An adapter over Postgres, SQLite or Redis implements these\n * and nothing else; every rule about WHICH node may run lives in `frontier.ts`,\n * which reads only {@link state}.\n */\nexport interface NodeClaimStore {\n /**\n * Take exclusive ownership of one node of one run.\n *\n * MUST be atomic against concurrent callers, and MUST return `true` for a\n * caller re-entering its OWN claim — that is what lets a job's retry resume\n * instead of deadlocking against the row it wrote itself.\n */\n claim(runKey: string, nodeId: string, owner: string): boolean | Promise<boolean>;\n state(runKey: string): Record<string, NodeState> | Promise<Record<string, NodeState>>;\n complete(\n runKey: string,\n nodeId: string,\n output: unknown,\n ports: readonly string[],\n ): void | Promise<void>;\n skip(runKey: string, nodeId: string): void | Promise<void>;\n fail(runKey: string, nodeId: string, error: string): void | Promise<void>;\n pause(runKey: string, nodeId: string, reason: string): void | Promise<void>;\n}\n\n/**\n * A correct, single-process {@link NodeClaimStore}.\n *\n * JavaScript's single-threaded event loop makes the claim atomic for free —\n * there is no interleaving point inside a synchronous method. It is NOT durable\n * across a restart, which is the honest limit: use it for tests, for a CLI run,\n * and for a worker that genuinely owns the whole run.\n */\nexport class InMemoryClaimStore implements NodeClaimStore {\n private readonly runs = new Map<string, Map<string, NodeState>>();\n\n claim(runKey: string, nodeId: string, owner: string): boolean {\n const run = this.run(runKey);\n const existing = run.get(nodeId);\n\n if (!existing) {\n run.set(nodeId, {\n status: NodeRunStatus.CLAIMED,\n ports: [],\n owner,\n attempts: 1,\n firstAttemptAt: new Date().toISOString(),\n });\n return true;\n }\n\n // Re-entering our own claim is how a retry gets back in. Anything else —\n // another owner, or a settled node — is a lost race, and a lost race is a\n // NO-OP rather than a duplicate run.\n const ownClaim =\n (existing.status === NodeRunStatus.CLAIMED || existing.status === NodeRunStatus.PAUSED) &&\n existing.owner === owner;\n\n if (ownClaim) {\n existing.attempts += 1;\n existing.status = NodeRunStatus.CLAIMED;\n return true;\n }\n return false;\n }\n\n state(runKey: string): Record<string, NodeState> {\n const out: Record<string, NodeState> = {};\n for (const [nodeId, entry] of this.run(runKey)) out[nodeId] = { ...entry, ports: [...entry.ports] };\n return out;\n }\n\n complete(runKey: string, nodeId: string, output: unknown, ports: readonly string[]): void {\n const entry = this.entry(runKey, nodeId);\n entry.status = NodeRunStatus.COMPLETED;\n entry.output = output;\n entry.ports = [...ports];\n entry.error = null;\n }\n\n skip(runKey: string, nodeId: string): void {\n const entry = this.entry(runKey, nodeId);\n entry.status = NodeRunStatus.SKIPPED;\n entry.ports = [];\n }\n\n fail(runKey: string, nodeId: string, error: string): void {\n const entry = this.entry(runKey, nodeId);\n entry.status = NodeRunStatus.FAILED;\n entry.error = error;\n entry.ports = [];\n }\n\n pause(runKey: string, nodeId: string, reason: string): void {\n const entry = this.entry(runKey, nodeId);\n entry.status = NodeRunStatus.PAUSED;\n entry.error = reason;\n entry.ports = [];\n }\n\n /**\n * Drop a paused node's claim so a recorded answer can re-run it.\n *\n * Not part of the interface: resuming a human gate is the host's decision and\n * its storage's business. Provided here because the in-memory store is also\n * what the tests resume through.\n */\n release(runKey: string, nodeId: string): void {\n this.run(runKey).delete(nodeId);\n }\n\n private run(runKey: string): Map<string, NodeState> {\n let run = this.runs.get(runKey);\n if (!run) {\n run = new Map();\n this.runs.set(runKey, run);\n }\n return run;\n }\n\n /** The row for one node, created as CLAIMED if a driver never claimed it. */\n private entry(runKey: string, nodeId: string): NodeState {\n const run = this.run(runKey);\n let entry = run.get(nodeId);\n if (!entry) {\n entry = {\n status: NodeRunStatus.CLAIMED,\n ports: [],\n attempts: 1,\n firstAttemptAt: new Date().toISOString(),\n };\n run.set(nodeId, entry);\n }\n return entry;\n }\n}\n\nexport function isSettled(status: string | undefined): boolean {\n return status !== undefined && (SETTLED as readonly string[]).includes(status);\n}\n","/**\n * Which nodes can run RIGHT NOW, given what has already settled.\n *\n * ## Why this is not a second engine\n *\n * `runFlow` walks a Kahn topological order and, at each node, runs it when at\n * least one incoming edge is active. That is a total order because one process\n * executes every node. Split the graph across jobs and the same rule has to be\n * asked the other way round — not \"what is next\" but \"what is unblocked\" —\n * which is this module.\n *\n * The rule is the engine's, restated:\n *\n * - every direct predecessor has SETTLED (in topological order the engine has\n * already settled all of them by the time it reaches a node);\n * - and either the node has no incoming edges, or at least one incoming edge is\n * ACTIVE — its source completed and published on that edge's source handle.\n *\n * A node whose predecessors have all settled with no active edge is what the\n * engine reports as `idle/skipped`. Skipping SETTLES it, which can in turn\n * unblock — or skip — its own successors, so the pass repeats until nothing\n * changes. That cascade is how a dead branch collapses without leaving the run\n * stuck.\n *\n * ## The one thing it does NOT decide\n *\n * Which ports a result activated. Those rules (`__port`, `branch`, declared\n * outputs, the kind's ports, the `out` fallback) live in the engine and stay\n * there: this reads the ports back off the `node-output` events the engine\n * emitted when the node ran, stored on the claim row.\n */\n\nimport { getNodeKind, kindIds } from \"../registry/registry\";\nimport type { FlowEdge, FlowGraph } from \"../types\";\nimport { NodeRunStatus, isSettled, type NodeClaimStore, type NodeState } from \"./state\";\n\nexport type FrontierResult = {\n ready: string[];\n skipped: string[];\n};\n\nfunction isNote(type: string | undefined): boolean {\n if (!type) return false;\n if (type === \"note\") return true;\n const kind = getNodeKind(type);\n return kind ? kindIds(kind).includes(\"note\") || kind.category === \"annotation\" : false;\n}\n\nexport const Frontier = {\n compute(graph: FlowGraph, state: Record<string, NodeState>): FrontierResult {\n const incoming = new Map<string, FlowEdge[]>();\n for (const edge of graph.edges) {\n const list = incoming.get(edge.target) ?? [];\n list.push(edge);\n incoming.set(edge.target, list);\n }\n\n // Settled nodes and the ports they lit. A skipped node is settled with NO\n // ports, which is precisely what makes its successors skip too.\n const settled = new Map<string, readonly string[]>();\n const held = new Set<string>();\n for (const [nodeId, entry] of Object.entries(state)) {\n if (entry.status === NodeRunStatus.COMPLETED) settled.set(nodeId, entry.ports);\n else if (isSettled(entry.status)) settled.set(nodeId, []);\n else held.add(nodeId);\n }\n\n const ready: string[] = [];\n const skipped: string[] = [];\n\n let changed = true;\n while (changed) {\n changed = false;\n\n for (const node of graph.nodes) {\n const nodeId = node.id;\n if (settled.has(nodeId) || held.has(nodeId) || ready.includes(nodeId)) continue;\n\n const edges = incoming.get(nodeId) ?? [];\n let blocked = false;\n let active = false;\n\n for (const edge of edges) {\n if (!settled.has(edge.source)) {\n blocked = true;\n break;\n }\n // The engine's port key: an edge with no source handle reads the\n // source's `out` port.\n if (settled.get(edge.source)!.includes(edge.sourceHandle ?? \"out\")) active = true;\n }\n\n if (blocked) continue;\n\n // Reached, but down a branch that never lit.\n if (edges.length > 0 && !active) {\n settled.set(nodeId, []);\n skipped.push(nodeId);\n changed = true;\n continue;\n }\n\n // Annotations and layout are never executed. Settling them here rather\n // than dispatching a job saves a queue round trip per sticky note — and\n // a graph can carry a lot of sticky notes.\n if (isNote(node.type) || getNodeKind(node.type ?? \"\")?.category === \"layout\") {\n settled.set(nodeId, []);\n skipped.push(nodeId);\n changed = true;\n continue;\n }\n\n ready.push(nodeId);\n }\n }\n\n return { ready, skipped };\n },\n\n /** Has every node settled? The run is finished when it has. */\n isComplete(graph: FlowGraph, state: Record<string, NodeState>): boolean {\n return graph.nodes.every((node) => isSettled(state[node.id]?.status));\n },\n\n /**\n * Is any node still held by a worker, or parked for a person?\n *\n * An empty frontier means something different depending on this: with work in\n * flight the run is simply waiting, and whichever job finishes will advance\n * it. With nothing in flight and nodes still unsettled, the graph cannot\n * progress at all — which is a stuck run, and must be reported rather than\n * waited on.\n */\n hasWorkInFlight(state: Record<string, NodeState>): boolean {\n return Object.values(state).some(\n (entry) => entry.status === NodeRunStatus.CLAIMED || entry.status === NodeRunStatus.PAUSED,\n );\n },\n\n /** Persist the skip cascade so the next pass does not recompute it. */\n async settleSkips(\n store: NodeClaimStore,\n runKey: string,\n skipped: readonly string[],\n ): Promise<void> {\n for (const nodeId of skipped) await store.skip(runKey, nodeId);\n },\n};\n","/**\n * Run ONE node of a graph — through the real engine, not around it.\n *\n * ## The problem this solves\n *\n * A per-node driver has to hand a node exactly the inputs it would have\n * received mid-run: the right values, on the right target handles, from the\n * right *active* edges. Those rules are the engine's (`collectInputs`,\n * `activatedPorts`, the merge-after-decision contract, the `out` fallbacks),\n * and they are the reason the three runtimes agree. Re-implementing them here\n * would be a second engine wearing a driver's clothes, and the two would drift.\n *\n * ## What it does instead\n *\n * It replays the graph with `runFlow` untouched:\n *\n * - every node already completed is fed back as `resumeOutputs`, so the engine\n * republishes it on the same ports and routes exactly as it did the first\n * time;\n * - every node EXCEPT the target is bound, by node id, to a boundary executor\n * that aborts — and a node-id binding outranks every kind binding and the\n * `*` fallback, so the fence holds whatever a host registered;\n * - so the engine walks its own topological order, skips its own dead branches,\n * collects the target's inputs its own way, runs the target — and stops at\n * the next thing it would have run.\n *\n * The target's output is `result.outputs[nodeId]`, and the ports it activated\n * arrive as the engine's own `node-output` events. Nothing about routing is\n * recomputed here.\n *\n * ## The cost, stated plainly\n *\n * Replaying the completed prefix is O(nodes) per node, so a run is O(nodes²) in\n * bookkeeping. The republish executes nothing — it re-publishes stored values —\n * so for the graph sizes workflows actually have this is noise next to a single\n * queue round trip. It buys exact fidelity to the engine, which is not\n * negotiable, and one implementation of the routing rules instead of two.\n */\n\nimport { runFlow, type RunResult } from \"../runtime/run-flow\";\nimport type { RunIdentity } from \"../runtime/run-identity\";\nimport type { ExecutorRegistry, FlowGraph, RunEvent } from \"../types\";\n\n/**\n * The abort reason the boundary executor uses.\n *\n * Not a failure: it is the engine telling us it reached a node this job is not\n * responsible for.\n */\nexport const BOUNDARY = \"fancy-flow:node-boundary\";\n\nexport type ReplayResult = {\n result: RunResult;\n /** node id -> the ports its output activated, from the engine's own events. */\n ports: Record<string, string[]>;\n outputOf(nodeId: string): unknown;\n portsOf(nodeId: string): string[];\n};\n\nexport type ReplayOptions = {\n resumeOutputs?: Record<string, unknown>;\n initialInputs?: Record<string, Record<string, unknown>>;\n onEvent?: (event: RunEvent) => void;\n depth?: number;\n run?: RunIdentity;\n};\n\n/**\n * Replay `graph` up to and through `nodeId`.\n *\n * Pass `nodeId = null` to PROBE: every node is a boundary, so nothing executes\n * and the engine reports only what it can determine structurally — a cycle, and\n * the ports each resumed output republishes on.\n */\nexport async function replayUpTo(\n graph: FlowGraph,\n nodeId: string | null,\n executors: ExecutorRegistry,\n options: ReplayOptions = {},\n): Promise<ReplayResult> {\n const fenced: ExecutorRegistry = { ...executors };\n for (const node of graph.nodes) {\n if (node.id !== nodeId) {\n fenced[node.id] = (ctx) => ctx.abort(BOUNDARY);\n }\n }\n\n const ports: Record<string, string[]> = {};\n const collect = (event: RunEvent): void => {\n if (event.type === \"node-output\") {\n (ports[event.nodeId] ??= []).push(event.portId);\n }\n options.onEvent?.(event);\n };\n\n const result = await runFlow(graph, fenced, collect, {\n initialInputs: options.initialInputs ?? {},\n resumeOutputs: options.resumeOutputs ?? {},\n depth: options.depth ?? 0,\n run: options.run,\n });\n\n return {\n result,\n ports,\n outputOf: (id) => result.outputs[id],\n portsOf: (id) => ports[id] ?? [],\n };\n}\n\n/** True when a replay ended because it reached a node it does not own. */\nexport function isBoundary(error: string | null | undefined): boolean {\n return error === BOUNDARY;\n}\n","/**\n * How many times a single node may be attempted.\n *\n * ## Why this cannot be one number\n *\n * A run-wide `tries` setting forces every workflow to pick between two bad\n * answers. At 1, a single flaky LLM or HTTP call takes the whole run down.\n * Above 1, the retry replays from the last checkpoint and everything already\n * done runs again — including the nodes that must not: `git_pr_open` opens a\n * second pull request.\n *\n * Per-node jobs make the question per node, which is where it always belonged.\n * A node declaring `sideEffects: unsafe-to-replay` is pinned to ONE attempt and\n * no backoff. Everything else takes the configured tries, or a per-kind\n * override.\n *\n * Undeclared side effects are treated as the configured default rather than\n * assumed safe: this decides retries, and inventing a safety claim on a node\n * author's behalf is how a retry loop ends up posting the same webhook twice.\n */\n\nimport { getNodeKind, kindIds, resolveKindId } from \"../registry/registry\";\nimport type { FlowNode } from \"../types\";\n\n/** A node that is not safe to run twice. Same vocabulary as the node manifest. */\nexport const UNSAFE_TO_REPLAY = \"unsafe-to-replay\";\n\nexport type RetryPolicyOptions = {\n tries?: number;\n backoffSeconds?: number;\n /** kind id -> tries. Keyed by any spelling; every id is checked. */\n perKind?: Record<string, number>;\n};\n\nexport class RetryPolicy {\n readonly tries: number;\n readonly backoffSeconds: number;\n readonly perKind: Record<string, number>;\n\n constructor(options: RetryPolicyOptions = {}) {\n this.tries = options.tries ?? 1;\n this.backoffSeconds = options.backoffSeconds ?? 0;\n this.perKind = options.perKind ?? {};\n }\n\n triesFor(node: FlowNode): number {\n if (RetryPolicy.isUnsafeToReplay(node)) return 1;\n\n for (const id of idsFor(node)) {\n if (id in this.perKind) return Math.max(1, Math.trunc(this.perKind[id]!));\n }\n\n return Math.max(1, Math.trunc(this.tries));\n }\n\n backoffFor(node: FlowNode): number {\n // Nothing to back off from: the node gets one attempt.\n if (RetryPolicy.isUnsafeToReplay(node)) return 0;\n return Math.max(0, this.backoffSeconds);\n }\n\n static isUnsafeToReplay(node: FlowNode): boolean {\n if (!node.type) return false;\n return getNodeKind(node.type)?.sideEffects === UNSAFE_TO_REPLAY;\n }\n}\n\n/**\n * Every id a per-kind override for this node could be keyed under.\n *\n * Canonical ids are namespaced while a host almost certainly writes the bare\n * one; keying on only the literal string would make the override silently stop\n * applying the day a kind is renamed.\n */\nfunction idsFor(node: FlowNode): string[] {\n if (!node.type) return [];\n const kind = getNodeKind(node.type);\n const canonical = resolveKindId(node.type);\n const ordered = [node.type, ...(canonical ? [canonical] : []), ...(kind ? kindIds(kind) : [])];\n return [...new Set(ordered)];\n}\n","/**\n * Human gates that cannot be walked past — and that hold no worker.\n *\n * ## The directive these implement\n *\n * A node that waits on a person must not occupy a worker, a connection or a\n * process while it waits. So the job for a human gate does its work by\n * **emitting the request** and then **finishing**: the run is persisted as\n * parked, the job returns, the worker takes the next thing off the queue. When\n * the person answers, *that* enqueues the continuation.\n *\n * Mechanically the gate aborts with an encoded pause, which is a *return*, not\n * a block. Nothing in this file sleeps, polls or awaits a person.\n *\n * ## Fail closed, and why\n *\n * A gate pauses because it **is** a human node, not because its input port\n * happens to be empty. This is not a preference; it is a fix. Both peer\n * runtimes once decided whether to pause by reading their own input, so a\n * pre-filled `values` or `approved` value ran the flow straight past the person\n * it was waiting for — silently, with the run reporting success.\n *\n * Restoring the old behaviour is possible, explicit, and per node:\n * `autoAnswerFromInput`. Turn it on for a step that is a form when a human is\n * present and a pass-through when an upstream node already produced the answer.\n * On an approval node, weigh it harder: it means the graph, not a person, can\n * approve.\n *\n * ## The other half of the fix\n *\n * Recording an answer for a node the run is not parked on THROWS rather than\n * queueing a write nobody reads. See {@link Submissions.record}.\n */\n\nimport { pauseForHuman } from \"../registry/pause\";\nimport { truthy } from \"../expressions/expr\";\nimport type { NodeExecutor } from \"../types\";\n\n/** An answer was recorded for a node the run is not waiting on. */\nexport class NotAwaitingHuman extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"NotAwaitingHuman\";\n }\n}\n\n/**\n * Answers recorded for human gates, keyed by node id.\n *\n * Deliberately separate from the run's inputs. Keeping them in one bag is\n * precisely what let a pre-filled input satisfy a gate.\n */\nexport class Submissions {\n private readonly answers = new Map<string, unknown>();\n /** The node the run is currently parked on, if any. */\n awaiting: string | null = null;\n\n /**\n * Record an answer for the node the run is parked on.\n *\n * Throws when the run is not waiting on that node. A queued answer for a node\n * that never paused is a write nobody reads — and it looks, from the outside,\n * exactly like a submission that worked.\n */\n record(nodeId: string, value: unknown): void {\n if (this.awaiting !== null && this.awaiting !== nodeId) {\n throw new NotAwaitingHuman(\n `This run is waiting on \"${this.awaiting}\", not \"${nodeId}\". Recording an answer for a ` +\n \"node the run is not parked on would be stored and never read.\",\n );\n }\n if (this.awaiting === null) {\n throw new NotAwaitingHuman(\n `This run is not waiting for anyone, so an answer for \"${nodeId}\" has nothing to resume.`,\n );\n }\n this.answers.set(nodeId, value);\n this.awaiting = null;\n }\n\n answered(nodeId: string): boolean {\n return this.answers.has(nodeId);\n }\n\n answer(nodeId: string): unknown {\n return this.answers.get(nodeId);\n }\n\n park(nodeId: string): void {\n this.awaiting = nodeId;\n }\n}\n\nfunction option(ctx: { node: { data?: unknown } }, key: string, fallback?: unknown): unknown {\n const config = ((ctx.node.data as any)?.config ?? {}) as Record<string, unknown>;\n return config[key] ?? fallback;\n}\n\n/** `user_input` — pauses until a submission for THIS node is recorded. */\nexport function durableUserInput(submissions: Submissions): NodeExecutor {\n return (ctx) => {\n if (submissions.answered(ctx.node.id)) return submissions.answer(ctx.node.id);\n\n if (option(ctx, \"autoAnswerFromInput\") === true) {\n const values = (ctx.inputs as Record<string, unknown>).values;\n if (values !== undefined) return values;\n }\n\n submissions.park(ctx.node.id);\n // Emits the request and RETURNS. A host listening to the feed sends the\n // email; the worker is free the moment this throws.\n return pauseForHuman(ctx, \"input\", {\n title: option(ctx, \"title\", \"Need your input\"),\n fields: option(ctx, \"fields\", []),\n });\n };\n}\n\n/** `human_approval` — pauses until a decision for THIS node is recorded. */\nexport function durableApproval(submissions: Submissions): NodeExecutor {\n return (ctx) => {\n const passthrough = (ctx.inputs as Record<string, unknown>).in ?? ctx.inputs;\n\n if (submissions.answered(ctx.node.id)) {\n return { branch: truthy(submissions.answer(ctx.node.id)) ? \"approved\" : \"denied\", value: passthrough };\n }\n\n if (option(ctx, \"autoAnswerFromInput\") === true) {\n const decision = (ctx.inputs as Record<string, unknown>).approved;\n if (decision !== undefined) {\n return { branch: truthy(decision) ? \"approved\" : \"denied\", value: passthrough };\n }\n }\n\n submissions.park(ctx.node.id);\n return pauseForHuman(ctx, \"approval\", {\n title: option(ctx, \"title\", \"Approve action\"),\n description: option(ctx, \"description\"),\n });\n };\n}\n","/**\n * The per-node driver, with the queue left out.\n *\n * This is the whole of \"how a queued run branches\", minus the transport. It\n * owns two operations and nothing else:\n *\n * `advance()`\n * Ask the frontier what is unblocked, settle the skip cascade, and report the\n * ready node ids. A queue adapter dispatches one job per id.\n *\n * `runNode()`\n * Claim one node, replay the graph through the real engine fenced to that\n * node, and checkpoint the output plus the ports the ENGINE said it\n * activated.\n *\n * Everything a queue library would add — enqueue, retry scheduling, worker\n * lifecycle — sits outside. That separation is the point: it makes the subtle\n * part (which node may run, and with what inputs) testable in-process, with no\n * broker, and identical under every adapter. **A queue adapter therefore\n * contains no workflow logic at all.** That is the test: if an adapter needs to\n * know what a port is, the seam is in the wrong place.\n *\n * `runToCompletion` drives both in one process. It is a real durable runner,\n * not a toy: with a persistent `NodeClaimStore` it survives a crash exactly as\n * a queued run does, because the crash-resume behaviour lives in the\n * checkpoints rather than in the loop.\n *\n * ## No worker waits on a person\n *\n * A human gate returns `paused`. `runToCompletion` returns immediately when it\n * sees one — it does not spin, sleep or poll. The run is parked in the store,\n * the process is free, and a recorded answer is what starts the next job.\n */\n\nimport { decodePause, type PauseSignal } from \"../registry/pause\";\nimport type { RunResult } from \"../runtime/run-flow\";\nimport { RunIdentity, type RunIdentityJson } from \"../runtime/run-identity\";\nimport type { ExecutorRegistry, FlowGraph, RunEvent } from \"../types\";\nimport { Frontier } from \"./frontier\";\nimport { isBoundary, replayUpTo } from \"./replay\";\nimport { RetryPolicy } from \"./retry\";\nimport {\n InMemoryClaimStore,\n NodeRunStatus,\n type NodeClaimStore,\n type NodeState,\n} from \"./state\";\n\n/** What happened to one node. */\nexport type NodeOutcome = {\n nodeId: string;\n status: \"not-claimed\" | \"completed\" | \"skipped\" | \"failed\" | \"paused\";\n output?: unknown;\n ports?: string[];\n error?: string;\n pause?: PauseSignal;\n /** False when another worker got there first. A lost race is a NO-OP. */\n claimed: boolean;\n /** 1-based attempt this execution ran as. */\n attempt: number;\n /**\n * `true` when this attempt failed and the policy still allows another.\n *\n * The claim row is left CLAIMED in that case, deliberately: a queue adapter\n * re-dispatches the job with the SAME owner token and the retry re-enters the\n * claim it already holds. Recording FAILED here instead would settle the\n * node, which SKIPS everything downstream — a run reporting a tidy finish\n * having done half its work.\n */\n retryable?: boolean;\n};\n\nexport type DurableRunResult = {\n ok: boolean;\n outputs: Record<string, unknown>;\n error?: string;\n pause?: PauseSignal;\n paused: boolean;\n};\n\nexport type CoordinatorOptions = {\n graph: FlowGraph;\n executors: ExecutorRegistry;\n /**\n * The run's stable identity. A bare string is taken as the run key.\n *\n * Required — not defaulted. A durable run without a stable key cannot key an\n * idempotent write, and minting one per construction would hand a retrying\n * host a different key each time.\n */\n run: string | RunIdentity | RunIdentityJson;\n store?: NodeClaimStore;\n initialInputs?: Record<string, Record<string, unknown>>;\n retry?: RetryPolicy;\n onEvent?: (event: RunEvent) => void;\n};\n\nexport class Coordinator {\n readonly graph: FlowGraph;\n readonly executors: ExecutorRegistry;\n readonly run: RunIdentity;\n readonly store: NodeClaimStore;\n readonly initialInputs: Record<string, Record<string, unknown>>;\n readonly retry: RetryPolicy;\n private readonly onEvent?: (event: RunEvent) => void;\n\n constructor(options: CoordinatorOptions) {\n this.graph = options.graph;\n this.executors = options.executors;\n this.run = RunIdentity.from(options.run);\n this.store = options.store ?? new InMemoryClaimStore();\n this.initialInputs = options.initialInputs ?? {};\n this.retry = options.retry ?? new RetryPolicy();\n this.onEvent = options.onEvent;\n }\n\n get runKey(): string {\n return this.run.runKey;\n }\n\n // -- the two operations ----------------------------------------------\n\n /**\n * Which nodes may be dispatched right now.\n *\n * Also settles the skip cascade, because a skip is a decision the frontier\n * just made and a second caller must not make it again.\n */\n async advance(): Promise<string[]> {\n const frontier = Frontier.compute(this.graph, await this.store.state(this.runKey));\n await Frontier.settleSkips(this.store, this.runKey, frontier.skipped);\n return frontier.ready;\n }\n\n /**\n * Claim, execute and checkpoint one node.\n *\n * The claim is taken FIRST. Two workers racing for the same node produce one\n * execution and one no-op, and the loser learns that from the store rather\n * than from a duplicate side effect.\n *\n * `owner` is the token that lets a job's own retry re-enter its claim instead\n * of deadlocking against the row it wrote itself — pass the SAME token across\n * a job's attempts.\n */\n async runNode(nodeId: string, owner?: string): Promise<NodeOutcome> {\n const token = owner ?? cryptoRandom();\n if (!(await this.store.claim(this.runKey, nodeId, token))) {\n return { nodeId, status: \"not-claimed\", claimed: false, attempt: 0 };\n }\n\n const row = (await this.store.state(this.runKey))[nodeId];\n const identity = this.identityFor(row);\n\n const replay = await replayUpTo(this.graph, nodeId, this.executors, {\n resumeOutputs: await this.completedOutputs(),\n initialInputs: this.initialInputs,\n onEvent: this.forward(nodeId),\n run: identity,\n });\n const result = replay.result;\n const attempt = identity.attempt;\n\n if (Object.prototype.hasOwnProperty.call(result.outputs, nodeId)) {\n const output = result.outputs[nodeId];\n const ports = replay.portsOf(nodeId);\n await this.store.complete(this.runKey, nodeId, output, ports);\n return { nodeId, status: \"completed\", output, ports, claimed: true, attempt };\n }\n\n // The node did not produce an output. Three reasons, and they are NOT\n // interchangeable.\n const pause = decodePause(result.error);\n if (pause && pause.nodeId === nodeId) {\n await this.store.pause(this.runKey, nodeId, result.error ?? \"\");\n return { nodeId, status: \"paused\", pause, claimed: true, attempt };\n }\n\n if (isBoundary(result.error)) {\n // The engine stopped at a node this job does not own BEFORE reaching the\n // target — so the target was never actually unblocked. That is a frontier\n // bug, not a node failure, and it must not be recorded as one: a FAILED\n // node settles, and settling it would silently skip everything\n // downstream.\n await this.store.skip(this.runKey, nodeId);\n return {\n nodeId,\n status: \"skipped\",\n error: \"replay stopped before reaching this node\",\n claimed: true,\n attempt,\n };\n }\n\n const error = result.error ?? `node ${nodeId} produced no output and no error`;\n const node = this.graph.nodes.find((n) => n.id === nodeId);\n const tries = node ? this.retry.triesFor(node) : 1;\n\n if (attempt < tries) {\n // Leave the row CLAIMED so the same owner can re-enter it. This is what\n // `fancy-flow-php` does by only marking FAILED from the job's `failed()`\n // hook — the row a worker still holds must not settle mid-retry.\n return { nodeId, status: \"failed\", error, claimed: true, attempt, retryable: true };\n }\n\n await this.store.fail(this.runKey, nodeId, error);\n return { nodeId, status: \"failed\", error, claimed: true, attempt, retryable: false };\n }\n\n // -- an in-process driver over the two ------------------------------\n\n /**\n * Drive the graph here, in this process, one node at a time.\n *\n * Every checkpoint is written exactly as a queued run writes it, so a crash\n * mid-loop resumes from the same place a crashed worker would.\n *\n * Retries honour {@link RetryPolicy}: a node declaring `unsafe-to-replay`\n * gets one attempt whatever the policy says, and the retry re-enters the same\n * claim with the same owner token — so the step key it derives is unchanged,\n * which is what makes the retry idempotent rather than duplicative.\n */\n async runToCompletion(maxPasses = 10_000): Promise<DurableRunResult> {\n for (let pass = 0; pass < maxPasses; pass++) {\n const ready = await this.advance();\n if (ready.length === 0) break;\n\n for (const nodeId of ready) {\n const outcome = await this.runNodeWithRetries(nodeId);\n\n if (outcome.status === \"paused\") {\n // A pause parks the RUN, not just the node: continuing would run the\n // human gate's siblings while a person is still deciding. The cohort\n // waits. Nothing here blocks — this RETURNS.\n return {\n ok: false,\n outputs: await this.outputs(),\n pause: outcome.pause,\n paused: true,\n };\n }\n if (outcome.status === \"failed\") {\n return { ok: false, outputs: await this.outputs(), error: outcome.error, paused: false };\n }\n }\n }\n\n const state = await this.store.state(this.runKey);\n if (!Frontier.isComplete(this.graph, state)) {\n if (Frontier.hasWorkInFlight(state)) {\n return {\n ok: false,\n outputs: await this.outputs(),\n error: \"the run is waiting on work held elsewhere\",\n paused: false,\n };\n }\n const unsettled = this.graph.nodes\n .filter((n) => ![\"completed\", \"skipped\", \"failed\"].includes(state[n.id]?.status ?? \"\"))\n .map((n) => n.id);\n return {\n ok: false,\n outputs: await this.outputs(),\n error: `the run cannot progress; unsettled nodes: ${unsettled.join(\", \")}`,\n paused: false,\n };\n }\n\n const failed = Object.values(state)\n .filter((e) => e.status === NodeRunStatus.FAILED)\n .map((e) => e.error ?? \"node failed\");\n\n return {\n ok: failed.length === 0,\n outputs: await this.outputs(),\n error: failed[0],\n paused: false,\n };\n }\n\n /**\n * Checkpointed outputs, in the graph's own node order.\n *\n * Ordered by the graph rather than by completion so two runs of the same\n * workflow produce comparable output maps even when nodes finished in a\n * different order.\n */\n async outputs(): Promise<Record<string, unknown>> {\n const state = await this.store.state(this.runKey);\n const out: Record<string, unknown> = {};\n for (const node of this.graph.nodes) {\n if (state[node.id]?.status === NodeRunStatus.COMPLETED) out[node.id] = state[node.id]!.output;\n }\n return out;\n }\n\n /** The checkpointed run, in the shape a single-process run returns. */\n async asRunResult(): Promise<RunResult> {\n const outcome = await this.runToCompletion();\n return outcome.error === undefined\n ? { ok: outcome.ok, outputs: outcome.outputs }\n : { ok: outcome.ok, outputs: outcome.outputs, error: outcome.error };\n }\n\n // -- internals -------------------------------------------------------\n\n private async runNodeWithRetries(nodeId: string): Promise<NodeOutcome> {\n // One owner token for every attempt of this node — that is what re-enters\n // the claim rather than losing the race to itself, and it is why the step\n // key a retrying node derives is the same one its first attempt sent.\n const owner = cryptoRandom();\n\n let outcome = await this.runNode(nodeId, owner);\n while (outcome.retryable) {\n outcome = await this.runNode(nodeId, owner);\n }\n return outcome;\n }\n\n /**\n * The identity handed to the node about to execute.\n *\n * `attempt` and `firstAttemptAt` come off the CLAIM ROW, so they describe\n * this step rather than the run — which is what makes the retry window check\n * exact instead of conservative.\n */\n private identityFor(row: NodeState | undefined): RunIdentity {\n if (!row) return this.run;\n return this.run.withAttempt(row.attempts, row.firstAttemptAt);\n }\n\n private async completedOutputs(): Promise<Record<string, unknown>> {\n const state = await this.store.state(this.runKey);\n const out: Record<string, unknown> = {};\n for (const [nodeId, entry] of Object.entries(state)) {\n if (entry.status === NodeRunStatus.COMPLETED) out[nodeId] = entry.output;\n }\n return out;\n }\n\n /**\n * Forward only the events the target node produced.\n *\n * A replay re-emits the whole completed prefix. Passing that through would\n * show a consumer every node running again on every job — the run feed would\n * report a 20-node workflow as 200 status changes.\n */\n private forward(nodeId: string): ((event: RunEvent) => void) | undefined {\n const sink = this.onEvent;\n if (!sink) return undefined;\n return (event) => {\n const id = \"nodeId\" in event ? event.nodeId : undefined;\n if (id === undefined || id === nodeId) sink(event);\n };\n }\n}\n\nfunction cryptoRandom(): string {\n const g = globalThis as { crypto?: { randomUUID?: () => string } };\n return g.crypto?.randomUUID?.() ?? `own_${Math.random().toString(36).slice(2)}${Date.now()}`;\n}\n"]}
|