replyonce 0.1.0 → 0.1.1

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 CHANGED
@@ -5,6 +5,8 @@
5
5
  [![CI](https://github.com/shmindmaster/replyonce/actions/workflows/ci.yml/badge.svg)](https://github.com/shmindmaster/replyonce/actions/workflows/ci.yml)
6
6
  [![License: Apache-2.0](https://img.shields.io/badge/license-Apache--2.0-blue.svg)](LICENSE)
7
7
 
8
+ ![80 duplicate customer replies without replyonce, 0 with it, across 100 simulated support conversations under injected faults](docs/media/replyonce-demo.gif)
9
+
8
10
  Agents retry tool calls. Queues redeliver jobs. Webhooks arrive twice. A deploy kills a
9
11
  run halfway, and a customer's second message starts a new run while the first one is still
10
12
  answering. The customer gets two replies, or two refunds, or two tickets, and the agent
@@ -32,18 +34,19 @@ against real Postgres and Redis on all three.
32
34
  ```text
33
35
  $ npx replyonce demo
34
36
  replyonce demo: 100 simulated support conversations under injected faults
35
- without: workerCrash=9, duplicateDelivery=14, customerBurst=22, timeoutAfterSend=29, timeoutBeforeSend=15
36
- with: workerCrash=10, duplicateDelivery=15, customerBurst=22, timeoutAfterSend=18, timeoutBeforeSend=12
37
+ without: workerCrash=12, duplicateDelivery=16, customerBurst=22, timeoutBeforeSend=14, timeoutAfterSend=20
38
+ with: workerCrash=10, duplicateDelivery=15, customerBurst=22, timeoutBeforeSend=10, timeoutAfterSend=21
37
39
 
38
40
  messages replies duplicates unanswered
39
- without 122 174 74 0
40
- with replyonce 122 120 0 0
41
+ without 122 170 72 0
42
+ with replyonce 122 122 0 0
41
43
 
42
44
  No customer message was answered twice with replyonce.
43
45
  ```
44
46
 
45
- The demo is a simulation, so the counts vary from run to run. CI runs it on every push
46
- and fails if a single duplicate gets through.
47
+ The demo runs on simulated time, so a seed always gives the same counts: `--seed N` (default 7)
48
+ picks another run. CI runs it on every push and fails if a single duplicate gets through.
49
+ Set `FORCE_COLOR=1` to keep its colour when the output is piped or recorded.
47
50
 
48
51
  ## 30-second quickstart
49
52
 
package/dist/cli.js CHANGED
@@ -353,6 +353,7 @@ var ReplyOnce = class {
353
353
  reconciler;
354
354
  emit;
355
355
  now;
356
+ sleep;
356
357
  constructor(options) {
357
358
  if (!options || !options.store) throw new TypeError("replyonce: a store is required");
358
359
  this.store = options.store;
@@ -376,6 +377,7 @@ var ReplyOnce = class {
376
377
  } : () => {
377
378
  };
378
379
  this.now = options.clock ?? Date.now;
380
+ this.sleep = options.sleep ?? sleep;
379
381
  }
380
382
  /** Resolve key parts to the ledger key used for them. */
381
383
  key(parts) {
@@ -648,14 +650,14 @@ var ReplyOnce = class {
648
650
  return await this.store.transact(scope, (s) => settle(s, { ...input, now }, this.policy));
649
651
  } catch (err) {
650
652
  lastError = err;
651
- await sleep(25 * 4 ** i);
653
+ await this.sleep(25 * 4 ** i);
652
654
  }
653
655
  }
654
656
  throw lastError;
655
657
  }
656
658
  async waitForSettle(scope, key, until, intervalMs) {
657
659
  while (this.now() < until) {
658
- await sleep(Math.min(intervalMs, Math.max(1, until - this.now())));
660
+ await this.sleep(Math.min(intervalMs, Math.max(1, until - this.now())));
659
661
  const e = (await this.store.read(scope))?.effects[key];
660
662
  if (!e || effectiveStatus(e, this.now(), this.policy) !== "pending") return;
661
663
  }
@@ -791,16 +793,41 @@ function rng(seed) {
791
793
  return ((t ^ t >>> 14) >>> 0) / 4294967296;
792
794
  };
793
795
  }
794
- var sleep2 = (ms) => new Promise((r) => setTimeout(r, ms));
795
796
  var hang = () => new Promise(() => {
796
797
  });
798
+ var wallClock = { now: Date.now, sleep: (ms) => new Promise((r) => setTimeout(r, ms)) };
799
+ var SimulatedClock = class {
800
+ t = Date.UTC(2026, 0, 1);
801
+ seq = 0;
802
+ timers = [];
803
+ now = () => this.t;
804
+ sleep = (ms) => new Promise((fire) => {
805
+ this.timers.push({ at: this.t + Math.max(0, ms), seq: this.seq++, fire });
806
+ });
807
+ async run(work) {
808
+ let settled = false;
809
+ work.then(
810
+ () => settled = true,
811
+ () => settled = true
812
+ );
813
+ for (; ; ) {
814
+ await new Promise((r) => setImmediate(r));
815
+ if (settled) return work;
816
+ if (this.timers.length === 0) throw new Error("replyonce demo: the simulation stalled");
817
+ this.timers.sort((a, b) => a.at - b.at || a.seq - b.seq);
818
+ const next = this.timers.shift();
819
+ this.t = next.at;
820
+ next.fire();
821
+ }
822
+ }
823
+ };
797
824
  function answered(world, conv) {
798
825
  const s = /* @__PURE__ */ new Set();
799
826
  for (const r of world.sent) if (r.conv === conv) r.answers.forEach((a) => s.add(a));
800
827
  return s;
801
828
  }
802
829
  async function providerSend(world, reply) {
803
- await sleep2(1 + world.rand() * 4);
830
+ await world.clock.sleep(1 + world.rand() * 4);
804
831
  const roll = world.rand();
805
832
  if (roll < 0.15) {
806
833
  world.sent.push(reply);
@@ -815,7 +842,7 @@ async function providerSend(world, reply) {
815
842
  }
816
843
  function naiveAgent(world) {
817
844
  return async (job, crash) => {
818
- await sleep2(2 + world.rand() * 10);
845
+ await world.clock.sleep(2 + world.rand() * 10);
819
846
  const todo = job.inboundAtEnqueue.filter((m) => !job.answeredAtEnqueue.has(m));
820
847
  if (todo.length === 0) return;
821
848
  for (let attempt = 0; attempt < 5; attempt++) {
@@ -834,6 +861,8 @@ function guardedAgent(world, store) {
834
861
  pendingTtlMs: 2e3,
835
862
  maxAttempts: 6,
836
863
  leaseTtlMs: 5e3,
864
+ clock: world.clock.now,
865
+ sleep: world.clock.sleep,
837
866
  // A settle after the claim was given up on means pendingTtlMs was too short for this host.
838
867
  onEvent: (e) => {
839
868
  if (e.type === "late_settle") world.faults.lateSettle = (world.faults.lateSettle ?? 0) + 1;
@@ -845,13 +874,13 @@ function guardedAgent(world, store) {
845
874
  }
846
875
  });
847
876
  return async (job, crash) => {
848
- await sleep2(2 + world.rand() * 10);
877
+ await world.clock.sleep(2 + world.rand() * 10);
849
878
  const run = await ro.begin(job.conv, { runId: job.id, contextAt: job.contextAt });
850
879
  if (!run.canWrite) return;
851
880
  let inbound = job.inboundAtEnqueue;
852
881
  let done = job.answeredAtEnqueue;
853
- const giveUpAt = Date.now() + 15e3;
854
- while (Date.now() < giveUpAt) {
882
+ const giveUpAt = world.clock.now() + 15e3;
883
+ while (world.clock.now() < giveUpAt) {
855
884
  const todo = inbound.filter((m) => !done.has(m));
856
885
  if (todo.length === 0) break;
857
886
  const identity = { answers: todo };
@@ -866,20 +895,26 @@ function guardedAgent(world, store) {
866
895
  );
867
896
  if (res.ok) break;
868
897
  if (res.status === "refused" && res.reason === "conversation_changed") {
869
- await sleep2(res.conflicts?.some((c) => c.status === "pending") ? 25 : 5);
898
+ await world.clock.sleep(res.conflicts?.some((c) => c.status === "pending") ? 25 : 5);
870
899
  inbound = world.inbound.get(job.conv) ?? [];
871
900
  done = answered(world, job.conv);
872
901
  continue;
873
902
  }
874
903
  if (res.status === "refused") break;
875
- await sleep2(10);
904
+ await world.clock.sleep(10);
876
905
  }
877
906
  await run.end();
878
907
  };
879
908
  }
880
- async function simulate(conversations, seed, makeAgent) {
881
- const world = { inbound: /* @__PURE__ */ new Map(), sent: [], faults: {}, rand: rng(seed) };
882
- const agent = makeAgent(world);
909
+ function simulate(conversations, seed, realTime, makeAgent) {
910
+ const clock = realTime ? wallClock : new SimulatedClock();
911
+ const world = { inbound: /* @__PURE__ */ new Map(), sent: [], faults: {}, rand: rng(seed), clock };
912
+ const ns = realTime ? Math.random().toString(36).slice(2, 10) : `seed${seed}`;
913
+ const play = () => playOut(world, conversations, ns, makeAgent(world));
914
+ return clock instanceof SimulatedClock ? clock.run(play()) : play();
915
+ }
916
+ async function playOut(world, conversations, ns, agent) {
917
+ const { sleep: sleep2 } = world.clock;
883
918
  const tasks = [];
884
919
  let jobSeq = 0;
885
920
  const deliver = (job) => {
@@ -908,10 +943,9 @@ async function simulate(conversations, seed, makeAgent) {
908
943
  msgId,
909
944
  answeredAtEnqueue: answered(world, conv),
910
945
  inboundAtEnqueue: [...list],
911
- contextAt: Date.now()
946
+ contextAt: world.clock.now()
912
947
  });
913
948
  };
914
- const ns = Math.random().toString(36).slice(2, 10);
915
949
  for (let i = 0; i < conversations; i++) {
916
950
  const conv = `demo-${ns}-conversation-${i}`;
917
951
  enqueue(conv, `${conv}/m1`);
@@ -948,16 +982,28 @@ async function simulate(conversations, seed, makeAgent) {
948
982
  async function runDemo(options = {}) {
949
983
  const conversations = options.conversations ?? 100;
950
984
  const seed = options.seed ?? 7;
951
- const { faults: withoutFaults, ...without } = await simulate(conversations, seed, naiveAgent);
985
+ const realTime = options.realTime ?? options.store !== void 0;
986
+ const { faults: withoutFaults, ...without } = await simulate(conversations, seed, realTime, naiveAgent);
952
987
  const store = options.store ?? new MemoryStore();
953
- const { faults: withFaults, ...guarded } = await simulate(conversations, seed, (w) => guardedAgent(w, store));
988
+ const { faults: withFaults, ...guarded } = await simulate(conversations, seed, realTime, (w) => guardedAgent(w, store));
954
989
  return { without, with: guarded, faults: { without: withoutFaults, with: withFaults } };
955
990
  }
956
- function formatReport(r) {
957
- const row = (label, s) => ` ${label.padEnd(16)} ${String(s.customerMessages).padStart(8)} ${String(s.repliesSent).padStart(7)} ${String(s.duplicateAnswers).padStart(10)} ${String(s.unanswered).padStart(11)}`;
991
+ function shouldColor(env = process.env, isTTY = process.stdout.isTTY) {
992
+ const force = env.FORCE_COLOR;
993
+ if (force !== void 0) return !["0", "false"].includes(force.trim().toLowerCase());
994
+ if (env.NO_COLOR) return false;
995
+ return Boolean(isTTY);
996
+ }
997
+ function formatReport(r, options = {}) {
998
+ const paint = (code) => (s) => options.color ? `\x1B[${code}m${s}\x1B[0m` : s;
999
+ const red = paint(31);
1000
+ const green = paint(32);
1001
+ const bold = paint(1);
1002
+ const count = (n, width) => (n === 0 ? green : red)(String(n).padStart(width));
1003
+ const row = (label, s) => ` ${label.padEnd(16)} ${String(s.customerMessages).padStart(8)} ${String(s.repliesSent).padStart(7)} ${count(s.duplicateAnswers, 10)} ${count(s.unanswered, 11)}`;
958
1004
  const faults = (f) => Object.entries(f).map(([k, v]) => `${k}=${v}`).join(", ");
959
1005
  return [
960
- `replyonce demo: ${r.without.conversations} simulated support conversations under injected faults`,
1006
+ bold(`replyonce demo: ${r.without.conversations} simulated support conversations under injected faults`),
961
1007
  ` without: ${faults(r.faults.without)}`,
962
1008
  ` with: ${faults(r.faults.with)}`,
963
1009
  "",
@@ -965,7 +1011,9 @@ function formatReport(r) {
965
1011
  row("without", r.without),
966
1012
  row("with replyonce", r.with),
967
1013
  "",
968
- r.with.duplicateAnswers === 0 ? " No customer message was answered twice with replyonce." : r.faults.with.lateSettle ? " Duplicates happened after this host stalled past pendingTtlMs (see lateSettle): the limit described in the README." : " Duplicates remained with replyonce. Please report this: https://github.com/shmindmaster/replyonce/issues"
1014
+ r.with.duplicateAnswers === 0 ? green(" No customer message was answered twice with replyonce.") : red(
1015
+ r.faults.with.lateSettle ? " Duplicates happened after this host stalled past pendingTtlMs (see lateSettle): the limit described in the README." : " Duplicates remained with replyonce. Please report this: https://github.com/shmindmaster/replyonce/issues"
1016
+ )
969
1017
  ].join("\n");
970
1018
  }
971
1019
 
@@ -973,7 +1021,8 @@ function formatReport(r) {
973
1021
  var HELP = `Usage: replyonce demo [--conversations N] [--seed N]
974
1022
 
975
1023
  Runs a chaos simulation of a support agent with and without replyonce and prints
976
- how many customer messages were answered twice.`;
1024
+ how many customer messages were answered twice. The same --seed always gives the
1025
+ same counts. Set FORCE_COLOR=1 for colour when output is piped or recorded.`;
977
1026
  async function main(argv) {
978
1027
  const [command, ...rest] = argv;
979
1028
  if (command !== "demo") {
@@ -987,7 +1036,7 @@ async function main(argv) {
987
1036
  return v;
988
1037
  };
989
1038
  const report = await runDemo({ conversations: opt("conversations", 100, 1), seed: opt("seed", 7, 0) });
990
- console.log(formatReport(report));
1039
+ console.log(formatReport(report, { color: shouldColor() }));
991
1040
  return report.with.duplicateAnswers === 0 ? 0 : 2;
992
1041
  }
993
1042
  main(process.argv.slice(2)).then(
package/dist/index.cjs CHANGED
@@ -388,6 +388,7 @@ var ReplyOnce = class {
388
388
  reconciler;
389
389
  emit;
390
390
  now;
391
+ sleep;
391
392
  constructor(options) {
392
393
  if (!options || !options.store) throw new TypeError("replyonce: a store is required");
393
394
  this.store = options.store;
@@ -411,6 +412,7 @@ var ReplyOnce = class {
411
412
  } : () => {
412
413
  };
413
414
  this.now = options.clock ?? Date.now;
415
+ this.sleep = options.sleep ?? sleep;
414
416
  }
415
417
  /** Resolve key parts to the ledger key used for them. */
416
418
  key(parts) {
@@ -683,14 +685,14 @@ var ReplyOnce = class {
683
685
  return await this.store.transact(scope, (s) => settle(s, { ...input, now }, this.policy));
684
686
  } catch (err) {
685
687
  lastError = err;
686
- await sleep(25 * 4 ** i);
688
+ await this.sleep(25 * 4 ** i);
687
689
  }
688
690
  }
689
691
  throw lastError;
690
692
  }
691
693
  async waitForSettle(scope, key, until, intervalMs) {
692
694
  while (this.now() < until) {
693
- await sleep(Math.min(intervalMs, Math.max(1, until - this.now())));
695
+ await this.sleep(Math.min(intervalMs, Math.max(1, until - this.now())));
694
696
  const e = (await this.store.read(scope))?.effects[key];
695
697
  if (!e || effectiveStatus(e, this.now(), this.policy) !== "pending") return;
696
698
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/replyonce.ts","../src/key.ts","../src/logic.ts","../src/stores/memory.ts"],"sourcesContent":["export {\n ReplyOnce,\n Run,\n NotApplied,\n ReplyOnceError,\n createReplyOnce,\n defaultClassifyError,\n unwrap,\n} from './replyonce.js';\nexport type {\n BeginOptions,\n CheckResult,\n Effect,\n KeyParts,\n OnceOptions,\n Reconciler,\n ReplyOnceOptions,\n RunOnceOptions,\n RunState,\n} from './replyonce.js';\nexport { MemoryStore } from './stores/memory.js';\nexport { canonicalJson, effectKey, hashPayload, providerIdempotencyKey } from './key.js';\nexport type { ConflictMode } from './logic.js';\nexport type {\n ConflictInfo,\n EffectContext,\n EffectRecord,\n EffectStatus,\n ErrorClass,\n Json,\n OnceResult,\n ReconcileVerdict,\n RefusalReason,\n ReplyOnceEvent,\n RunEndReason,\n RunMode,\n RunRecord,\n ScopeState,\n Store,\n TransactResult,\n} from './types.js';\n","import { randomUUID } from 'node:crypto';\nimport { effectKey, providerIdempotencyKey } from './key.js';\nimport * as logic from './logic.js';\nimport type { BeginOutcome, ClaimOutcome, ConflictMode, Policy } from './logic.js';\nimport type {\n EffectContext,\n EffectRecord,\n EffectStatus,\n ErrorClass,\n Json,\n OnceResult,\n ReconcileVerdict,\n ReplyOnceEvent,\n RunEndReason,\n RunMode,\n RunRecord,\n Store,\n} from './types.js';\n\n/** Throw this (or anything with `replyonce: 'not_applied'`) when the effect definitely did not happen. */\nexport class NotApplied extends Error {\n readonly replyonce = 'not_applied' as const;\n constructor(message = 'effect not applied', options?: { cause?: unknown }) {\n super(message);\n this.name = 'NotApplied';\n if (options && 'cause' in options) (this as { cause?: unknown }).cause = options.cause;\n }\n}\n\nexport function defaultClassifyError(err: unknown): ErrorClass {\n if (err instanceof NotApplied) return 'not_applied';\n if (err && typeof err === 'object' && (err as { replyonce?: unknown }).replyonce === 'not_applied') {\n return 'not_applied';\n }\n return 'ambiguous';\n}\n\nexport interface KeyParts {\n /** Usually the conversation or thread id. All fencing happens within a scope. */\n scope: string;\n /** What the effect is: `reply`, `refund`, `create_ticket`. */\n intent: string;\n /**\n * The business identity of the effect, hashed into the key. Use what makes it the same\n * action (the inbound message being answered, the order being refunded), never\n * model-generated text or a model tool-call id.\n */\n payload?: unknown;\n}\n\nexport type Reconciler = (entry: EffectRecord) => Promise<ReconcileVerdict>;\n\nexport interface ReplyOnceOptions {\n store: Store;\n /** How long a claim may stay pending before it is treated as abandoned (ambiguous). Default 15 min. */\n pendingTtlMs?: number;\n /** Attempts per key, counting only attempts known not to have applied. Default 3. */\n maxAttempts?: number;\n /** How long settled effects and ended runs are remembered. Default 7 days. */\n retentionMs?: number;\n /** Run lease length; renewed on every claim and heartbeat. Default 60 s. */\n leaseTtlMs?: number;\n /** Decide whether a thrown error means \"did not happen\" or \"might have happened\". */\n classifyError?: (err: unknown) => ErrorClass;\n /** Ask the outside world what happened to an ambiguous effect. */\n reconcile?: Reconciler;\n onEvent?: (event: ReplyOnceEvent) => void;\n clock?: () => number;\n}\n\nexport interface OnceOptions {\n /** Short human-readable description, shown to other runs in conflict reports. */\n summary?: string;\n classifyError?: (err: unknown) => ErrorClass;\n reconcile?: Reconciler;\n /** Wait for an in-flight duplicate to settle instead of returning `in_flight` immediately. */\n wait?: { timeoutMs: number; intervalMs?: number };\n}\n\nexport interface RunOnceOptions extends OnceOptions {\n /**\n * `any` (default): refuse if another run wrote anything on this scope since this run last\n * looked. `same-intent`: only effects with the same intent conflict. `none`: per-key\n * idempotency and supersession only.\n */\n conflict?: ConflictMode;\n /** Record that this run has now seen the conflicts, so its next attempt can proceed. Default true. */\n ack?: boolean;\n}\n\nexport interface CheckResult {\n /** `true` only when the effect is known to have happened. */\n done: boolean;\n status: EffectStatus | 'none';\n entry: EffectRecord | null;\n}\n\nexport interface BeginOptions {\n /** Stable id for this unit of work. Reuse it on redelivery (for example the queue job id). */\n runId?: string;\n mode?: RunMode;\n leaseTtlMs?: number;\n /**\n * Epoch ms when this run's context (the conversation it will act on) was read, for\n * example when the webhook payload was captured. Writes by other runs that landed\n * after this point are reported as `conversation_changed`. Default: now.\n */\n contextAt?: number;\n}\n\nexport type Effect<T> = (ctx: EffectContext) => Promise<T> | T;\n\nconst MAX_ROUNDS = 5;\n\nexport class ReplyOnce {\n readonly store: Store;\n readonly policy: Policy;\n readonly leaseTtlMs: number;\n private readonly classify: (err: unknown) => ErrorClass;\n private readonly reconciler: Reconciler | undefined;\n private readonly emit: (event: ReplyOnceEvent) => void;\n private readonly now: () => number;\n\n constructor(options: ReplyOnceOptions) {\n if (!options || !options.store) throw new TypeError('replyonce: a store is required');\n this.store = options.store;\n this.policy = {\n pendingTtlMs: options.pendingTtlMs ?? 15 * 60_000,\n maxAttempts: options.maxAttempts ?? 3,\n retentionMs: options.retentionMs ?? 7 * 24 * 3_600_000,\n };\n if (this.policy.pendingTtlMs <= 0 || this.policy.maxAttempts < 1 || this.policy.retentionMs <= 0) {\n throw new RangeError('replyonce: pendingTtlMs, maxAttempts and retentionMs must be positive');\n }\n this.leaseTtlMs = options.leaseTtlMs ?? 60_000;\n this.classify = options.classifyError ?? defaultClassifyError;\n this.reconciler = options.reconcile;\n const onEvent = options.onEvent;\n this.emit = onEvent\n ? (e) => {\n try {\n onEvent(e);\n } catch {\n // an observer must never change the outcome\n }\n }\n : () => {};\n this.now = options.clock ?? Date.now;\n }\n\n /** Resolve key parts to the ledger key used for them. */\n key(parts: KeyParts): string {\n return effectKey(parts.intent, parts.payload);\n }\n\n /**\n * Run `effect` at most once for this key. A string key is its own scope; key parts give\n * a scope (conversation) plus an intent and business identity.\n */\n once<T>(key: string | KeyParts, effect: Effect<T>, options: OnceOptions = {}): Promise<OnceResult<T>> {\n const { scope, ledgerKey, intent } = this.resolveKey(key);\n return this.execute(scope, ledgerKey, intent, null, effect, { ...options, conflict: 'none', ack: false });\n }\n\n /** \"Was this already done?\" — safe to expose to a model as a read-only tool. */\n async check(key: string | KeyParts): Promise<CheckResult> {\n const { scope, ledgerKey } = this.resolveKey(key);\n const state = await this.store.read(scope);\n const entry = state?.effects[ledgerKey] ?? null;\n if (!entry) return { done: false, status: 'none', entry: null };\n const status = logic.effectiveStatus(entry, this.now(), this.policy);\n return { done: status === 'committed', status, entry };\n }\n\n /** Every effect recorded on a scope, oldest first. */\n async history(scope: string): Promise<EffectRecord[]> {\n const state = await this.store.read(scope);\n if (!state) return [];\n const now = this.now();\n return Object.values(state.effects)\n .map((e) => ({ ...e, status: logic.effectiveStatus(e, now, this.policy) }))\n .sort((a, b) => a.createdAt - b.createdAt);\n }\n\n /** Record what an operator or reconciler found out about an ambiguous effect. */\n async resolve(\n key: string | KeyParts,\n verdict: { outcome: 'committed'; result?: Json } | { outcome: 'not_applied' },\n ): Promise<EffectRecord | null> {\n const { scope, ledgerKey } = this.resolveKey(key);\n const result = 'result' in verdict ? verdict.result : undefined;\n const now = this.now();\n const entry = await this.store.transact(scope, (s) =>\n logic.resolve(s, { key: ledgerKey, outcome: verdict.outcome, result, now }, this.policy),\n );\n if (entry) this.emit({ type: 'reconciled', scope, key: ledgerKey, outcome: verdict.outcome });\n return entry;\n }\n\n /**\n * Start (or, with the same `runId`, resume) a unit of work on a scope. The run gets a\n * fencing token; its writes are refused once a newer run owns the scope.\n */\n async begin(scope: string, options: BeginOptions = {}): Promise<Run> {\n assertScope(scope);\n const runId = options.runId ?? randomUUID();\n const leaseTtlMs = options.leaseTtlMs ?? this.leaseTtlMs;\n const now = this.now();\n const outcome = await this.store.transact(scope, (s) =>\n logic.begin(\n s,\n {\n runId,\n mode: options.mode ?? 'supersede',\n leaseTtlMs,\n now,\n ...(options.contextAt !== undefined ? { contextAt: options.contextAt } : {}),\n },\n this.policy,\n ),\n );\n return new Run(this, scope, runId, leaseTtlMs, outcome);\n }\n\n /** Cancel a run: its future writes are refused with `run_cancelled`. */\n async cancel(scope: string, runId: string): Promise<boolean> {\n const now = this.now();\n const r = await this.store.transact(scope, (s) =>\n logic.endRun(s, { runId, reason: 'cancelled', now }, this.policy),\n );\n return r !== null;\n }\n\n /** @internal */\n async renewRun(scope: string, runId: string, token: number, leaseTtlMs: number): Promise<boolean> {\n const now = this.now();\n const r = await this.store.transact(scope, (s) =>\n logic.renew(s, { runId, token, leaseTtlMs, now }, this.policy),\n );\n return r.ok;\n }\n\n /** @internal */\n async endRun(scope: string, runId: string, token: number, reason: RunEndReason): Promise<void> {\n const now = this.now();\n await this.store.transact(scope, (s) => logic.endRun(s, { runId, token, reason, now }, this.policy));\n }\n\n /** @internal */\n resolveKey(key: string | KeyParts): { scope: string; ledgerKey: string; intent: string } {\n if (typeof key === 'string') {\n assertScope(key);\n return { scope: key, ledgerKey: key, intent: 'once' };\n }\n assertScope(key.scope);\n return { scope: key.scope, ledgerKey: effectKey(key.intent, key.payload), intent: key.intent };\n }\n\n /** @internal */\n async execute<T>(\n scope: string,\n key: string,\n intent: string,\n run: { runId: string; token: number; leaseTtlMs: number } | null,\n effect: Effect<T>,\n options: RunOnceOptions,\n ): Promise<OnceResult<T>> {\n const runId = run?.runId ?? null;\n const token = run?.token ?? null;\n const classify = options.classifyError ?? this.classify;\n const reconcile = options.reconcile ?? this.reconciler;\n const waitUntil = options.wait ? this.now() + options.wait.timeoutMs : 0;\n let reconciledOnce = false;\n\n for (let round = 0; round < MAX_ROUNDS; round++) {\n const attemptId = randomUUID();\n const now = this.now();\n const claimed: ClaimOutcome = await this.store.transact(scope, (s) =>\n logic.claim(\n s,\n scope,\n {\n key,\n intent,\n summary: options.summary ?? null,\n runId,\n token,\n attemptId,\n conflict: options.conflict ?? 'any',\n ack: options.ack ?? true,\n leaseTtlMs: run?.leaseTtlMs ?? this.leaseTtlMs,\n now,\n },\n this.policy,\n ),\n );\n\n switch (claimed.kind) {\n case 'replay':\n this.emit({ type: 'replayed', scope, key });\n return { status: 'replayed', ok: true, value: claimed.entry.result as T, key, entry: claimed.entry };\n\n case 'refused':\n this.emit({ type: 'refused', scope, key, reason: claimed.reason, runId });\n return {\n status: 'refused',\n ok: false,\n key,\n reason: claimed.reason,\n ...(claimed.conflicts ? { conflicts: claimed.conflicts } : {}),\n ...(claimed.entry ? { entry: claimed.entry } : {}),\n };\n\n case 'in_flight': {\n if (options.wait && this.now() < waitUntil) {\n await this.waitForSettle(scope, key, waitUntil, options.wait.intervalMs ?? 100);\n continue;\n }\n this.emit({ type: 'in_flight', scope, key });\n return { status: 'in_flight', ok: false, key, entry: claimed.entry };\n }\n\n case 'ambiguous': {\n if (reconcile && !reconciledOnce) {\n reconciledOnce = true;\n let verdict: ReconcileVerdict;\n try {\n verdict = await reconcile(claimed.entry);\n } catch {\n verdict = { outcome: 'unknown' };\n }\n if (verdict.outcome !== 'unknown') {\n const at = this.now();\n const resolved = await this.store.transact(scope, (s) =>\n logic.resolve(\n s,\n {\n key,\n outcome: verdict.outcome as 'committed' | 'not_applied',\n result: verdict.outcome === 'committed' ? verdict.result : undefined,\n attemptId: claimed.entry.attemptId,\n now: at,\n },\n this.policy,\n ),\n );\n if (resolved) this.emit({ type: 'reconciled', scope, key, outcome: verdict.outcome });\n continue;\n }\n }\n this.emit({ type: 'ambiguous', scope, key });\n return { status: 'ambiguous', ok: false, key, entry: claimed.entry };\n }\n\n case 'acquired':\n return this.runAcquired(scope, key, claimed.entry, effect, classify);\n }\n }\n const entry = (await this.store.read(scope))?.effects[key];\n if (entry && entry.status === 'committed') {\n return { status: 'replayed', ok: true, value: entry.result as T, key, entry };\n }\n if (entry) {\n this.emit({ type: 'in_flight', scope, key });\n return { status: 'in_flight', ok: false, key, entry };\n }\n throw new Error('replyonce: could not claim key after several rounds');\n }\n\n private async runAcquired<T>(\n scope: string,\n key: string,\n entry: EffectRecord,\n effect: Effect<T>,\n classify: (err: unknown) => ErrorClass,\n ): Promise<OnceResult<T>> {\n const { attempt, attemptId, runId } = entry;\n this.emit({ type: 'claimed', scope, key, attempt, runId });\n const remaining = Math.max(1, entry.claimedAt + this.policy.pendingTtlMs - this.now());\n const ctx: EffectContext = {\n key,\n scope,\n intent: entry.intent,\n attempt,\n idempotencyKey: providerIdempotencyKey(scope, key),\n fencingToken: entry.token,\n signal: AbortSignal.timeout(remaining),\n };\n\n let value: T;\n try {\n value = await effect(ctx);\n } catch (error) {\n const cls = safeClassify(classify, error);\n const outcome = cls === 'not_applied' ? 'failed' : 'ambiguous';\n await this.settleWithRetry(scope, {\n key,\n attemptId,\n outcome,\n error: describeError(error),\n now: 0,\n }).catch(() => undefined);\n if (outcome === 'failed') {\n this.emit({ type: 'failed', scope, key, attempt, error });\n return {\n status: 'failed',\n ok: false,\n key,\n attempt,\n error,\n retryable: attempt < this.policy.maxAttempts,\n };\n }\n this.emit({ type: 'ambiguous', scope, key, error });\n const current = (await this.store.read(scope).catch(() => null))?.effects[key] ?? entry;\n return { status: 'ambiguous', ok: false, key, entry: current, error };\n }\n\n let stored: Json | undefined;\n try {\n stored = value === undefined ? null : (JSON.parse(JSON.stringify(value)) as Json);\n } catch {\n stored = null;\n this.emit({ type: 'unserializable_result', scope, key });\n }\n try {\n const settled = await this.settleWithRetry(scope, {\n key,\n attemptId,\n outcome: 'committed',\n result: stored,\n now: 0,\n });\n this.emit({ type: 'committed', scope, key, attempt, runId });\n if (settled.kind === 'stale' || settled.wasAbandoned) {\n this.emit({ type: 'late_settle', scope, key, attempt });\n return { status: 'executed', ok: true, value, key, attempt, lateSettle: true };\n }\n } catch {\n // The effect happened but the ledger could not record it. The claim stays pending and\n // later reads treat it as ambiguous, which fails safe.\n this.emit({ type: 'late_settle', scope, key, attempt });\n return { status: 'executed', ok: true, value, key, attempt, lateSettle: true };\n }\n return { status: 'executed', ok: true, value, key, attempt };\n }\n\n private async settleWithRetry(scope: string, input: logic.SettleInput): Promise<logic.SettleOutcome> {\n let lastError: unknown;\n for (let i = 0; i < 4; i++) {\n try {\n const now = this.now();\n return await this.store.transact(scope, (s) => logic.settle(s, { ...input, now }, this.policy));\n } catch (err) {\n lastError = err;\n await sleep(25 * 4 ** i);\n }\n }\n throw lastError;\n }\n\n private async waitForSettle(scope: string, key: string, until: number, intervalMs: number): Promise<void> {\n while (this.now() < until) {\n await sleep(Math.min(intervalMs, Math.max(1, until - this.now())));\n const e = (await this.store.read(scope))?.effects[key];\n if (!e || logic.effectiveStatus(e, this.now(), this.policy) !== 'pending') return;\n }\n }\n}\n\nexport type RunState = BeginOutcome['state'];\n\n/** A unit of agent work on one scope, holding a fencing token. */\nexport class Run {\n readonly scope: string;\n readonly runId: string;\n /** `new`, `resumed` (same runId seen before), `superseded`, `ended`, or `busy` (exclusive mode). */\n readonly state: RunState;\n /** Monotonic per scope. `null` when the run could not start (`busy`). */\n readonly fencingToken: number | null;\n /** Effects this runId already claimed before (non-empty on redelivery). */\n readonly priorEffects: EffectRecord[];\n readonly busyHolder: { runId: string; token: number; leaseExpiresAt: number } | null;\n private readonly ro: ReplyOnce;\n private readonly leaseTtlMs: number;\n\n constructor(ro: ReplyOnce, scope: string, runId: string, leaseTtlMs: number, outcome: BeginOutcome) {\n this.ro = ro;\n this.scope = scope;\n this.runId = runId;\n this.leaseTtlMs = leaseTtlMs;\n this.state = outcome.state;\n if (outcome.state === 'busy') {\n this.fencingToken = null;\n this.priorEffects = [];\n this.busyHolder = outcome.holder;\n } else {\n this.fencingToken = (outcome.run as RunRecord).token;\n this.priorEffects = outcome.priorEffects;\n this.busyHolder = null;\n }\n }\n\n /** `true` when this run may write: it is new or resumed, not superseded, ended or busy. */\n get canWrite(): boolean {\n return this.state === 'new' || this.state === 'resumed';\n }\n\n /**\n * `true` when a previous delivery of this run already claimed a write that may have\n * reached the outside world (pending, committed or ambiguous).\n */\n get alreadyWrote(): boolean {\n return this.priorEffects.some((e) => e.status !== 'failed');\n }\n\n /** Run `effect` at most once per (scope, intent, payload), fenced to this run. */\n once<T>(intent: string, payload: unknown, effect: Effect<T>, options: RunOnceOptions = {}): Promise<OnceResult<T>> {\n const key = effectKey(intent, payload);\n if (this.fencingToken === null) {\n return Promise.resolve({ status: 'refused', ok: false, key, reason: 'busy' });\n }\n const self = { runId: this.runId, token: this.fencingToken, leaseTtlMs: this.leaseTtlMs };\n return this.ro.execute(this.scope, key, intent, self, effect, options);\n }\n\n check(intent: string, payload?: unknown): Promise<CheckResult> {\n return this.ro.check({ scope: this.scope, intent, payload });\n }\n\n history(): Promise<EffectRecord[]> {\n return this.ro.history(this.scope);\n }\n\n /**\n * Renew the lease. `false` means stop: the run was superseded (by a newer run or a later\n * delivery of this one), cancelled or ended.\n */\n heartbeat(): Promise<boolean> {\n if (this.fencingToken === null) return Promise.resolve(false);\n return this.ro.renewRun(this.scope, this.runId, this.fencingToken, this.leaseTtlMs);\n }\n\n /** Mark the run finished. A no-op if a later delivery of the same run has taken over. */\n end(reason: RunEndReason = 'completed'): Promise<void> {\n if (this.fencingToken === null) return Promise.resolve();\n return this.ro.endRun(this.scope, this.runId, this.fencingToken, reason);\n }\n}\n\nexport function createReplyOnce(options: ReplyOnceOptions): ReplyOnce {\n return new ReplyOnce(options);\n}\n\n/** Return the value of a successful result, or throw a `ReplyOnceError` describing why not. */\nexport function unwrap<T>(result: OnceResult<T>): T {\n if (result.ok) return result.value;\n throw new ReplyOnceError(result);\n}\n\nexport class ReplyOnceError extends Error {\n readonly result: Exclude<OnceResult<unknown>, { ok: true }>;\n constructor(result: Exclude<OnceResult<unknown>, { ok: true }>) {\n const detail = result.status === 'refused' ? `${result.status}: ${result.reason}` : result.status;\n super(`replyonce: effect ${result.key} not executed (${detail})`);\n this.name = 'ReplyOnceError';\n this.result = result;\n }\n}\n\nfunction assertScope(scope: string): void {\n if (typeof scope !== 'string' || scope.length === 0 || scope.length > 512) {\n throw new TypeError('replyonce: scope must be a non-empty string of at most 512 characters');\n }\n}\n\nfunction safeClassify(classify: (err: unknown) => ErrorClass, err: unknown): ErrorClass {\n try {\n return classify(err) === 'not_applied' ? 'not_applied' : 'ambiguous';\n } catch {\n return 'ambiguous';\n }\n}\n\nfunction describeError(err: unknown): string {\n const text = err instanceof Error ? `${err.name}: ${err.message}` : String(err);\n return text.slice(0, 500);\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((r) => setTimeout(r, ms));\n}\n","import { createHash } from 'node:crypto';\n\n/** Deterministic JSON: object keys sorted, `undefined` members dropped. */\nexport function canonicalJson(value: unknown): string {\n return JSON.stringify(normalize(value, new Set()));\n}\n\nfunction normalize(value: unknown, seen: Set<unknown>): unknown {\n if (value === null || value === undefined) return null;\n if (typeof value === 'bigint') return value.toString();\n if (typeof value !== 'object') {\n if (typeof value === 'number' && !Number.isFinite(value)) return String(value);\n if (typeof value === 'function' || typeof value === 'symbol') return null;\n return value;\n }\n if (value instanceof Date) return value.toISOString();\n if (seen.has(value)) throw new TypeError('replyonce: cannot hash a circular payload');\n seen.add(value);\n let out: unknown;\n if (Array.isArray(value)) {\n out = value.map((v) => normalize(v, seen));\n } else {\n const obj: Record<string, unknown> = {};\n for (const k of Object.keys(value as Record<string, unknown>).sort()) {\n const v = (value as Record<string, unknown>)[k];\n if (v === undefined) continue;\n obj[k] = normalize(v, seen);\n }\n out = obj;\n }\n seen.delete(value);\n return out;\n}\n\nexport function sha256Hex(input: string): string {\n return createHash('sha256').update(input).digest('hex');\n}\n\nexport function hashPayload(payload: unknown): string {\n return sha256Hex(canonicalJson(payload)).slice(0, 32);\n}\n\nconst SAFE_INTENT = /^[A-Za-z0-9_.:-]{1,100}$/;\n\n/** Ledger key for an intent plus the business identity of the payload. */\nexport function effectKey(intent: string, payload: unknown): string {\n if (!SAFE_INTENT.test(intent)) {\n throw new TypeError(\n `replyonce: intent must match ${SAFE_INTENT} (got ${JSON.stringify(intent)})`,\n );\n }\n return payload === undefined ? intent : `${intent}:${hashPayload(payload)}`;\n}\n\n/** Stable idempotency key to hand to a provider (Stripe-style `Idempotency-Key`). */\nexport function providerIdempotencyKey(scope: string, key: string): string {\n return `ro_${sha256Hex(`${scope}\\u0000${key}`).slice(0, 40)}`;\n}\n","import type {\n ConflictInfo,\n EffectRecord,\n Json,\n RefusalReason,\n RunEndReason,\n RunMode,\n RunRecord,\n ScopeState,\n TransactResult,\n} from './types.js';\n\n/**\n * The replyonce state machine. Every function here is pure: it takes a scope's state and\n * returns the next state plus a decision. Stores only make each call atomic.\n */\n\nexport interface Policy {\n pendingTtlMs: number;\n maxAttempts: number;\n retentionMs: number;\n}\n\nexport type ConflictMode = 'any' | 'same-intent' | 'none';\n\nexport const SUMMARY_MAX = 2000;\n\nexport function emptyState(): ScopeState {\n return { v: 1, seq: 0, tokenSeq: 0, runs: {}, effects: {} };\n}\n\nfunction load(state: ScopeState | null, now: number, policy: Policy): ScopeState {\n const s = state ? (structuredClone(state) as ScopeState) : emptyState();\n if (s.v !== 1) throw new Error(`replyonce: unsupported state version ${String(s.v)}`);\n const horizon = now - policy.retentionMs;\n for (const [k, e] of Object.entries(s.effects)) {\n if ((e.status === 'committed' || e.status === 'failed') && (e.settledAt ?? e.claimedAt) < horizon) {\n delete s.effects[k];\n }\n }\n for (const [id, r] of Object.entries(s.runs)) {\n if ((r.endedAt ?? r.leaseExpiresAt) < horizon) delete s.runs[id];\n }\n return s;\n}\n\n/** Advance the scope's logical clock for a change being made now. */\nfunction tick(s: ScopeState): number {\n s.seq += 1;\n return s.seq;\n}\n\nexport function isAbandoned(e: EffectRecord, now: number, policy: Policy): boolean {\n return e.status === 'pending' && now - e.claimedAt >= policy.pendingTtlMs;\n}\n\n/** The status a reader should act on: a pending claim past its TTL is treated as ambiguous. */\nexport function effectiveStatus(e: EffectRecord, now: number, policy: Policy): EffectRecord['status'] {\n return isAbandoned(e, now, policy) ? 'ambiguous' : e.status;\n}\n\nfunction isLive(r: RunRecord, now: number): boolean {\n return r.endedAt === null && r.leaseExpiresAt > now;\n}\n\n/**\n * Is `a` newer than `b`? Exclusive leases order by token; otherwise by context recency. Never\n * by token there: a redelivery bumps an old run's token. Equal context times order by when\n * the run first began only if neither run gave a `contextAt`, because then each read its\n * context as it began. Otherwise neither is newer: begin order says nothing about whose\n * context is fresher, and fencing out the wrong one would leave its message unanswered.\n * Both may then write; conflict checks keep them from answering the same thing twice.\n */\nfunction isNewer(a: RunRecord, b: RunRecord): boolean {\n if (a.mode === 'exclusive' && b.mode === 'exclusive') return a.token > b.token;\n if (a.orderAt !== b.orderAt) return a.orderAt > b.orderAt;\n if (a.baseContextAt !== null || b.baseContextAt !== null) return false;\n return a.baseSeq > b.baseSeq;\n}\n\n/**\n * A newer run owns the scope if it is live, or if it already wrote (it may have answered).\n * A newer run that ended without writing, or was cancelled, does not.\n */\nexport function supersedingRun(s: ScopeState, run: RunRecord, now: number): RunRecord | null {\n let best: RunRecord | null = null;\n for (const r of Object.values(s.runs)) {\n if (r.runId === run.runId || r.endReason === 'cancelled' || !isNewer(r, run)) continue;\n if (r.wrote || isLive(r, now)) {\n if (!best || isNewer(r, best)) best = r;\n }\n }\n return best;\n}\n\nfunction markSeen(run: RunRecord, seq: number, now: number): void {\n run.seenSeq = Math.max(run.seenSeq, seq);\n run.seenAt = Math.max(run.seenAt, now);\n run.contextAt = null;\n}\n\n// ---------------------------------------------------------------- begin\n\nexport interface BeginInput {\n runId: string;\n mode: RunMode;\n leaseTtlMs: number;\n now: number;\n /** When the run's view of the scope was read. Writes that landed after it are conflicts. */\n contextAt?: number;\n}\n\nexport type BeginOutcome =\n | { state: 'new' | 'resumed' | 'superseded' | 'ended'; run: RunRecord; priorEffects: EffectRecord[] }\n | { state: 'busy'; holder: { runId: string; token: number; leaseExpiresAt: number } };\n\nexport function begin(\n state: ScopeState | null,\n input: BeginInput,\n policy: Policy,\n): TransactResult<BeginOutcome> {\n const { now } = input;\n const s = load(state, now, policy);\n const existing = s.runs[input.runId];\n if (existing) {\n const priorEffects = Object.values(s.effects).filter((e) => e.runId === input.runId);\n if (existing.endedAt !== null) {\n return { next: null, result: { state: 'ended', run: existing, priorEffects } };\n }\n existing.leaseExpiresAt = Math.max(existing.leaseExpiresAt, now + input.leaseTtlMs);\n tick(s);\n // A redelivery takes the run over; any earlier delivery still executing is fenced out.\n s.tokenSeq += 1;\n existing.token = s.tokenSeq;\n // It acts on the run's original context, so it has not seen what earlier deliveries saw,\n // unless it says it re-read the scope.\n existing.seenSeq = existing.baseSeq;\n existing.seenAt = existing.baseContextAt ?? existing.startedAt;\n existing.contextAt = existing.baseContextAt;\n if (input.contextAt !== undefined && input.contextAt > existing.seenAt) {\n const fresh = Math.min(input.contextAt, now);\n for (const e of Object.values(s.effects)) {\n if (e.settleSeq !== null && e.settledAt !== null && e.settledAt < fresh) {\n existing.seenSeq = Math.max(existing.seenSeq, e.settleSeq);\n }\n }\n existing.seenAt = fresh;\n existing.contextAt = fresh;\n }\n const newer = supersedingRun(s, existing, now);\n return { next: s, result: { state: newer ? 'superseded' : 'resumed', run: existing, priorEffects } };\n }\n if (input.mode === 'exclusive') {\n let holder: RunRecord | undefined;\n for (const r of Object.values(s.runs)) {\n if (isLive(r, now) && (!holder || r.token > holder.token)) holder = r;\n }\n if (holder) {\n return {\n next: null,\n result: {\n state: 'busy',\n holder: { runId: holder.runId, token: holder.token, leaseExpiresAt: holder.leaseExpiresAt },\n },\n };\n }\n }\n const seq = tick(s);\n s.tokenSeq += 1;\n const contextAt = input.contextAt === undefined ? null : Math.min(input.contextAt, now);\n const run: RunRecord = {\n runId: input.runId,\n token: s.tokenSeq,\n mode: input.mode,\n startedAt: now,\n orderAt: input.mode === 'exclusive' ? now : (contextAt ?? now),\n leaseExpiresAt: now + input.leaseTtlMs,\n seenSeq: seq,\n seenAt: contextAt ?? now,\n contextAt,\n baseSeq: seq,\n baseContextAt: contextAt,\n wrote: false,\n endedAt: null,\n endReason: null,\n };\n s.runs[input.runId] = run;\n const newer = supersedingRun(s, run, now);\n return { next: s, result: { state: newer ? 'superseded' : 'new', run, priorEffects: [] } };\n}\n\n// ---------------------------------------------------------------- heartbeat / end\n\nexport type RenewOutcome =\n | { ok: true; run: RunRecord }\n | { ok: false; reason: 'unknown_run' | 'run_ended' | 'run_cancelled' | 'superseded' };\n\nexport function renew(\n state: ScopeState | null,\n input: { runId: string; token: number; leaseTtlMs: number; now: number },\n policy: Policy,\n): TransactResult<RenewOutcome> {\n const s = load(state, input.now, policy);\n const r = s.runs[input.runId];\n if (!r) return { next: null, result: { ok: false, reason: 'unknown_run' } };\n if (r.token !== input.token) return { next: null, result: { ok: false, reason: 'superseded' } };\n if (r.endReason === 'cancelled') return { next: null, result: { ok: false, reason: 'run_cancelled' } };\n if (r.endedAt !== null) return { next: null, result: { ok: false, reason: 'run_ended' } };\n if (supersedingRun(s, r, input.now)) return { next: null, result: { ok: false, reason: 'superseded' } };\n r.leaseExpiresAt = Math.max(r.leaseExpiresAt, input.now + input.leaseTtlMs);\n tick(s);\n return { next: s, result: { ok: true, run: r } };\n}\n\nexport function endRun(\n state: ScopeState | null,\n input: { runId: string; reason: RunEndReason; now: number; /** Ignore if a later delivery holds the run. */ token?: number },\n policy: Policy,\n): TransactResult<RunRecord | null> {\n const s = load(state, input.now, policy);\n const r = s.runs[input.runId];\n if (!r) return { next: null, result: null };\n if (input.token !== undefined && r.token !== input.token) return { next: null, result: null };\n if (r.endedAt !== null && !(input.reason === 'cancelled' && r.endReason !== 'cancelled')) {\n return { next: null, result: r };\n }\n r.endedAt = r.endedAt ?? input.now;\n r.endReason = input.reason;\n tick(s);\n return { next: s, result: r };\n}\n\n// ---------------------------------------------------------------- claim\n\nexport interface ClaimInput {\n key: string;\n intent: string;\n summary: string | null;\n runId: string | null;\n /** The claiming delivery's fencing token. A stale token is refused as superseded. */\n token: number | null;\n attemptId: string;\n conflict: ConflictMode;\n ack: boolean;\n leaseTtlMs: number;\n now: number;\n}\n\nexport type ClaimOutcome =\n | { kind: 'acquired'; entry: EffectRecord }\n | { kind: 'replay'; entry: EffectRecord }\n | { kind: 'in_flight'; entry: EffectRecord }\n | { kind: 'ambiguous'; entry: EffectRecord }\n | { kind: 'refused'; reason: RefusalReason; conflicts?: ConflictInfo[]; entry?: EffectRecord };\n\n/** Writes by others on the scope that `run` has not seen yet, or that may still land. */\nexport function conflictsFor(\n s: ScopeState,\n run: RunRecord,\n key: string,\n intent: string,\n mode: ConflictMode,\n now: number,\n policy: Policy,\n): ConflictInfo[] {\n if (mode === 'none') return [];\n const out: ConflictInfo[] = [];\n for (const e of Object.values(s.effects)) {\n if (e.key === key) continue;\n if (e.runId === run.runId) {\n // This delivery knows its own writes. An earlier delivery's writes matter only when\n // this one is about to do the same kind of thing differently (a reply worded anew).\n if (e.token === run.token || e.intent !== intent) continue;\n }\n if (mode === 'same-intent' && e.intent !== intent) continue;\n const abandoned = isAbandoned(e, now, policy);\n let conflict = false;\n if (e.status === 'pending' && !abandoned) {\n conflict = true;\n } else if (abandoned) {\n // It may have landed at any time up to when it became abandoned.\n conflict = e.claimedAt + policy.pendingTtlMs >= run.seenAt;\n } else if (e.status === 'committed' || e.status === 'ambiguous') {\n conflict =\n (e.settleSeq ?? 0) > run.seenSeq || (run.contextAt !== null && (e.settledAt ?? 0) >= run.contextAt);\n }\n if (conflict) {\n out.push({\n key: e.key,\n intent: e.intent,\n status: abandoned ? 'ambiguous' : e.status,\n runId: e.runId,\n summary: e.summary,\n at: e.settledAt ?? e.claimedAt,\n });\n }\n }\n return out.sort((a, b) => a.at - b.at).slice(0, 10);\n}\n\nexport function claim(\n state: ScopeState | null,\n scope: string,\n input: ClaimInput,\n policy: Policy,\n): TransactResult<ClaimOutcome> {\n const { now } = input;\n const s = load(state, now, policy);\n const prior = s.effects[input.key];\n\n if (prior) {\n if (prior.status === 'committed') return { next: null, result: { kind: 'replay', entry: prior } };\n if (prior.status === 'pending') {\n if (!isAbandoned(prior, now, policy)) {\n return { next: null, result: { kind: 'in_flight', entry: prior } };\n }\n const seq = tick(s);\n prior.status = 'ambiguous';\n prior.settledAt = prior.claimedAt + policy.pendingTtlMs;\n prior.settleSeq = seq;\n prior.error = prior.error ?? 'abandoned: not settled within pendingTtlMs';\n return { next: s, result: { kind: 'ambiguous', entry: prior } };\n }\n if (prior.status === 'ambiguous') return { next: null, result: { kind: 'ambiguous', entry: prior } };\n if (prior.attempt >= policy.maxAttempts) {\n return { next: null, result: { kind: 'refused', reason: 'exhausted', entry: prior } };\n }\n }\n\n let run: RunRecord | undefined;\n if (input.runId !== null) {\n run = s.runs[input.runId];\n if (!run) return { next: null, result: { kind: 'refused', reason: 'unknown_run' } };\n if (run.endReason === 'cancelled') return { next: null, result: { kind: 'refused', reason: 'run_cancelled' } };\n if (run.endedAt !== null) return { next: null, result: { kind: 'refused', reason: 'run_ended' } };\n if ((input.token !== null && run.token !== input.token) || supersedingRun(s, run, now)) {\n return { next: null, result: { kind: 'refused', reason: 'superseded' } };\n }\n\n const conflicts = conflictsFor(s, run, input.key, input.intent, input.conflict, now, policy);\n if (conflicts.length > 0) {\n if (!input.ack) {\n return { next: null, result: { kind: 'refused', reason: 'conversation_changed', conflicts } };\n }\n markSeen(run, tick(s), now);\n return { next: s, result: { kind: 'refused', reason: 'conversation_changed', conflicts } };\n }\n }\n\n const seq = tick(s);\n const entry: EffectRecord = {\n key: input.key,\n scope,\n intent: input.intent,\n status: 'pending',\n attempt: (prior?.attempt ?? 0) + 1,\n attemptId: input.attemptId,\n runId: input.runId,\n token: run?.token ?? null,\n summary: input.summary === null ? null : input.summary.slice(0, SUMMARY_MAX),\n error: null,\n createdAt: prior?.createdAt ?? now,\n claimedAt: now,\n settledAt: null,\n claimSeq: seq,\n settleSeq: null,\n };\n s.effects[input.key] = entry;\n if (run) {\n // A claim does not mark anything seen: writes this check deliberately skipped (other\n // intents, or `none`) must still be reported to a later check that covers them.\n run.wrote = true;\n run.leaseExpiresAt = Math.max(run.leaseExpiresAt, now + input.leaseTtlMs);\n }\n return { next: s, result: { kind: 'acquired', entry } };\n}\n\n// ---------------------------------------------------------------- settle / resolve\n\nexport interface SettleInput {\n key: string;\n attemptId: string;\n outcome: 'committed' | 'failed' | 'ambiguous';\n result?: Json;\n error?: string | null;\n now: number;\n}\n\nexport type SettleOutcome =\n | { kind: 'settled'; entry: EffectRecord; wasAbandoned: boolean }\n | { kind: 'stale'; entry: EffectRecord | null };\n\nexport function settle(\n state: ScopeState | null,\n input: SettleInput,\n policy: Policy,\n): TransactResult<SettleOutcome> {\n const s = load(state, input.now, policy);\n const e = s.effects[input.key];\n // A late \"committed\" from the attempt a reconciler judged not applied is still the truth.\n const lateTruth = e?.status === 'failed' && input.outcome === 'committed';\n if (\n !e ||\n e.attemptId !== input.attemptId ||\n (e.status !== 'pending' && e.status !== 'ambiguous' && !lateTruth)\n ) {\n return { next: null, result: { kind: 'stale', entry: e ?? null } };\n }\n const wasAbandoned = e.status !== 'pending' || isAbandoned(e, input.now, policy);\n e.status = input.outcome;\n e.settledAt = input.now;\n e.settleSeq = tick(s);\n e.error = input.error ?? null;\n if (input.outcome === 'committed' && input.result !== undefined) e.result = input.result;\n return { next: s, result: { kind: 'settled', entry: e, wasAbandoned } };\n}\n\nexport interface ResolveInput {\n key: string;\n outcome: 'committed' | 'not_applied';\n result?: Json;\n /** Only resolve if the ledger still holds this attempt. */\n attemptId?: string;\n now: number;\n}\n\n/** Resolve an ambiguous (or abandoned) effect after checking the outside world. */\nexport function resolve(\n state: ScopeState | null,\n input: ResolveInput,\n policy: Policy,\n): TransactResult<EffectRecord | null> {\n const s = load(state, input.now, policy);\n const e = s.effects[input.key];\n if (!e) return { next: null, result: null };\n if (input.attemptId !== undefined && e.attemptId !== input.attemptId) return { next: null, result: null };\n if (effectiveStatus(e, input.now, policy) !== 'ambiguous') return { next: null, result: null };\n e.settledAt = input.now;\n e.settleSeq = tick(s);\n if (input.outcome === 'committed') {\n e.status = 'committed';\n e.error = null;\n if (input.result !== undefined) e.result = input.result;\n } else {\n e.status = 'failed';\n e.error = 'reconciled: not applied';\n }\n return { next: s, result: e };\n}\n","import type { ScopeState, Store, TransactResult } from '../types.js';\n\n/**\n * Single-process store. Atomic because `transact` reads, decides and writes in one\n * synchronous step. State is kept as JSON so it behaves like the shared stores.\n * Use it for tests, demos and single-instance deployments; it is lost on restart.\n */\nexport class MemoryStore implements Store {\n private readonly data = new Map<string, string>();\n\n async transact<T>(scope: string, fn: (state: ScopeState | null) => TransactResult<T>): Promise<T> {\n const raw = this.data.get(scope);\n const { next, result } = fn(raw === undefined ? null : (JSON.parse(raw) as ScopeState));\n if (next) this.data.set(scope, JSON.stringify(next));\n return result;\n }\n\n async read(scope: string): Promise<ScopeState | null> {\n const raw = this.data.get(scope);\n return raw === undefined ? null : (JSON.parse(raw) as ScopeState);\n }\n\n /** Number of scopes held. */\n get size(): number {\n return this.data.size;\n }\n\n clear(): void {\n this.data.clear();\n }\n}\n"],"mappings":"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","names":["import_node_crypto","newer","seq"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/replyonce.ts","../src/key.ts","../src/logic.ts","../src/stores/memory.ts"],"sourcesContent":["export {\n ReplyOnce,\n Run,\n NotApplied,\n ReplyOnceError,\n createReplyOnce,\n defaultClassifyError,\n unwrap,\n} from './replyonce.js';\nexport type {\n BeginOptions,\n CheckResult,\n Effect,\n KeyParts,\n OnceOptions,\n Reconciler,\n ReplyOnceOptions,\n RunOnceOptions,\n RunState,\n} from './replyonce.js';\nexport { MemoryStore } from './stores/memory.js';\nexport { canonicalJson, effectKey, hashPayload, providerIdempotencyKey } from './key.js';\nexport type { ConflictMode } from './logic.js';\nexport type {\n ConflictInfo,\n EffectContext,\n EffectRecord,\n EffectStatus,\n ErrorClass,\n Json,\n OnceResult,\n ReconcileVerdict,\n RefusalReason,\n ReplyOnceEvent,\n RunEndReason,\n RunMode,\n RunRecord,\n ScopeState,\n Store,\n TransactResult,\n} from './types.js';\n","import { randomUUID } from 'node:crypto';\nimport { effectKey, providerIdempotencyKey } from './key.js';\nimport * as logic from './logic.js';\nimport type { BeginOutcome, ClaimOutcome, ConflictMode, Policy } from './logic.js';\nimport type {\n EffectContext,\n EffectRecord,\n EffectStatus,\n ErrorClass,\n Json,\n OnceResult,\n ReconcileVerdict,\n ReplyOnceEvent,\n RunEndReason,\n RunMode,\n RunRecord,\n Store,\n} from './types.js';\n\n/** Throw this (or anything with `replyonce: 'not_applied'`) when the effect definitely did not happen. */\nexport class NotApplied extends Error {\n readonly replyonce = 'not_applied' as const;\n constructor(message = 'effect not applied', options?: { cause?: unknown }) {\n super(message);\n this.name = 'NotApplied';\n if (options && 'cause' in options) (this as { cause?: unknown }).cause = options.cause;\n }\n}\n\nexport function defaultClassifyError(err: unknown): ErrorClass {\n if (err instanceof NotApplied) return 'not_applied';\n if (err && typeof err === 'object' && (err as { replyonce?: unknown }).replyonce === 'not_applied') {\n return 'not_applied';\n }\n return 'ambiguous';\n}\n\nexport interface KeyParts {\n /** Usually the conversation or thread id. All fencing happens within a scope. */\n scope: string;\n /** What the effect is: `reply`, `refund`, `create_ticket`. */\n intent: string;\n /**\n * The business identity of the effect, hashed into the key. Use what makes it the same\n * action (the inbound message being answered, the order being refunded), never\n * model-generated text or a model tool-call id.\n */\n payload?: unknown;\n}\n\nexport type Reconciler = (entry: EffectRecord) => Promise<ReconcileVerdict>;\n\nexport interface ReplyOnceOptions {\n store: Store;\n /** How long a claim may stay pending before it is treated as abandoned (ambiguous). Default 15 min. */\n pendingTtlMs?: number;\n /** Attempts per key, counting only attempts known not to have applied. Default 3. */\n maxAttempts?: number;\n /** How long settled effects and ended runs are remembered. Default 7 days. */\n retentionMs?: number;\n /** Run lease length; renewed on every claim and heartbeat. Default 60 s. */\n leaseTtlMs?: number;\n /** Decide whether a thrown error means \"did not happen\" or \"might have happened\". */\n classifyError?: (err: unknown) => ErrorClass;\n /** Ask the outside world what happened to an ambiguous effect. */\n reconcile?: Reconciler;\n onEvent?: (event: ReplyOnceEvent) => void;\n clock?: () => number;\n /** Wait between settle retries and `wait` polls. Pass with `clock` to run on simulated time. */\n sleep?: (ms: number) => Promise<void>;\n}\n\nexport interface OnceOptions {\n /** Short human-readable description, shown to other runs in conflict reports. */\n summary?: string;\n classifyError?: (err: unknown) => ErrorClass;\n reconcile?: Reconciler;\n /** Wait for an in-flight duplicate to settle instead of returning `in_flight` immediately. */\n wait?: { timeoutMs: number; intervalMs?: number };\n}\n\nexport interface RunOnceOptions extends OnceOptions {\n /**\n * `any` (default): refuse if another run wrote anything on this scope since this run last\n * looked. `same-intent`: only effects with the same intent conflict. `none`: per-key\n * idempotency and supersession only.\n */\n conflict?: ConflictMode;\n /** Record that this run has now seen the conflicts, so its next attempt can proceed. Default true. */\n ack?: boolean;\n}\n\nexport interface CheckResult {\n /** `true` only when the effect is known to have happened. */\n done: boolean;\n status: EffectStatus | 'none';\n entry: EffectRecord | null;\n}\n\nexport interface BeginOptions {\n /** Stable id for this unit of work. Reuse it on redelivery (for example the queue job id). */\n runId?: string;\n mode?: RunMode;\n leaseTtlMs?: number;\n /**\n * Epoch ms when this run's context (the conversation it will act on) was read, for\n * example when the webhook payload was captured. Writes by other runs that landed\n * after this point are reported as `conversation_changed`. Default: now.\n */\n contextAt?: number;\n}\n\nexport type Effect<T> = (ctx: EffectContext) => Promise<T> | T;\n\nconst MAX_ROUNDS = 5;\n\nexport class ReplyOnce {\n readonly store: Store;\n readonly policy: Policy;\n readonly leaseTtlMs: number;\n private readonly classify: (err: unknown) => ErrorClass;\n private readonly reconciler: Reconciler | undefined;\n private readonly emit: (event: ReplyOnceEvent) => void;\n private readonly now: () => number;\n private readonly sleep: (ms: number) => Promise<void>;\n\n constructor(options: ReplyOnceOptions) {\n if (!options || !options.store) throw new TypeError('replyonce: a store is required');\n this.store = options.store;\n this.policy = {\n pendingTtlMs: options.pendingTtlMs ?? 15 * 60_000,\n maxAttempts: options.maxAttempts ?? 3,\n retentionMs: options.retentionMs ?? 7 * 24 * 3_600_000,\n };\n if (this.policy.pendingTtlMs <= 0 || this.policy.maxAttempts < 1 || this.policy.retentionMs <= 0) {\n throw new RangeError('replyonce: pendingTtlMs, maxAttempts and retentionMs must be positive');\n }\n this.leaseTtlMs = options.leaseTtlMs ?? 60_000;\n this.classify = options.classifyError ?? defaultClassifyError;\n this.reconciler = options.reconcile;\n const onEvent = options.onEvent;\n this.emit = onEvent\n ? (e) => {\n try {\n onEvent(e);\n } catch {\n // an observer must never change the outcome\n }\n }\n : () => {};\n this.now = options.clock ?? Date.now;\n this.sleep = options.sleep ?? sleep;\n }\n\n /** Resolve key parts to the ledger key used for them. */\n key(parts: KeyParts): string {\n return effectKey(parts.intent, parts.payload);\n }\n\n /**\n * Run `effect` at most once for this key. A string key is its own scope; key parts give\n * a scope (conversation) plus an intent and business identity.\n */\n once<T>(key: string | KeyParts, effect: Effect<T>, options: OnceOptions = {}): Promise<OnceResult<T>> {\n const { scope, ledgerKey, intent } = this.resolveKey(key);\n return this.execute(scope, ledgerKey, intent, null, effect, { ...options, conflict: 'none', ack: false });\n }\n\n /** \"Was this already done?\" — safe to expose to a model as a read-only tool. */\n async check(key: string | KeyParts): Promise<CheckResult> {\n const { scope, ledgerKey } = this.resolveKey(key);\n const state = await this.store.read(scope);\n const entry = state?.effects[ledgerKey] ?? null;\n if (!entry) return { done: false, status: 'none', entry: null };\n const status = logic.effectiveStatus(entry, this.now(), this.policy);\n return { done: status === 'committed', status, entry };\n }\n\n /** Every effect recorded on a scope, oldest first. */\n async history(scope: string): Promise<EffectRecord[]> {\n const state = await this.store.read(scope);\n if (!state) return [];\n const now = this.now();\n return Object.values(state.effects)\n .map((e) => ({ ...e, status: logic.effectiveStatus(e, now, this.policy) }))\n .sort((a, b) => a.createdAt - b.createdAt);\n }\n\n /** Record what an operator or reconciler found out about an ambiguous effect. */\n async resolve(\n key: string | KeyParts,\n verdict: { outcome: 'committed'; result?: Json } | { outcome: 'not_applied' },\n ): Promise<EffectRecord | null> {\n const { scope, ledgerKey } = this.resolveKey(key);\n const result = 'result' in verdict ? verdict.result : undefined;\n const now = this.now();\n const entry = await this.store.transact(scope, (s) =>\n logic.resolve(s, { key: ledgerKey, outcome: verdict.outcome, result, now }, this.policy),\n );\n if (entry) this.emit({ type: 'reconciled', scope, key: ledgerKey, outcome: verdict.outcome });\n return entry;\n }\n\n /**\n * Start (or, with the same `runId`, resume) a unit of work on a scope. The run gets a\n * fencing token; its writes are refused once a newer run owns the scope.\n */\n async begin(scope: string, options: BeginOptions = {}): Promise<Run> {\n assertScope(scope);\n const runId = options.runId ?? randomUUID();\n const leaseTtlMs = options.leaseTtlMs ?? this.leaseTtlMs;\n const now = this.now();\n const outcome = await this.store.transact(scope, (s) =>\n logic.begin(\n s,\n {\n runId,\n mode: options.mode ?? 'supersede',\n leaseTtlMs,\n now,\n ...(options.contextAt !== undefined ? { contextAt: options.contextAt } : {}),\n },\n this.policy,\n ),\n );\n return new Run(this, scope, runId, leaseTtlMs, outcome);\n }\n\n /** Cancel a run: its future writes are refused with `run_cancelled`. */\n async cancel(scope: string, runId: string): Promise<boolean> {\n const now = this.now();\n const r = await this.store.transact(scope, (s) =>\n logic.endRun(s, { runId, reason: 'cancelled', now }, this.policy),\n );\n return r !== null;\n }\n\n /** @internal */\n async renewRun(scope: string, runId: string, token: number, leaseTtlMs: number): Promise<boolean> {\n const now = this.now();\n const r = await this.store.transact(scope, (s) =>\n logic.renew(s, { runId, token, leaseTtlMs, now }, this.policy),\n );\n return r.ok;\n }\n\n /** @internal */\n async endRun(scope: string, runId: string, token: number, reason: RunEndReason): Promise<void> {\n const now = this.now();\n await this.store.transact(scope, (s) => logic.endRun(s, { runId, token, reason, now }, this.policy));\n }\n\n /** @internal */\n resolveKey(key: string | KeyParts): { scope: string; ledgerKey: string; intent: string } {\n if (typeof key === 'string') {\n assertScope(key);\n return { scope: key, ledgerKey: key, intent: 'once' };\n }\n assertScope(key.scope);\n return { scope: key.scope, ledgerKey: effectKey(key.intent, key.payload), intent: key.intent };\n }\n\n /** @internal */\n async execute<T>(\n scope: string,\n key: string,\n intent: string,\n run: { runId: string; token: number; leaseTtlMs: number } | null,\n effect: Effect<T>,\n options: RunOnceOptions,\n ): Promise<OnceResult<T>> {\n const runId = run?.runId ?? null;\n const token = run?.token ?? null;\n const classify = options.classifyError ?? this.classify;\n const reconcile = options.reconcile ?? this.reconciler;\n const waitUntil = options.wait ? this.now() + options.wait.timeoutMs : 0;\n let reconciledOnce = false;\n\n for (let round = 0; round < MAX_ROUNDS; round++) {\n const attemptId = randomUUID();\n const now = this.now();\n const claimed: ClaimOutcome = await this.store.transact(scope, (s) =>\n logic.claim(\n s,\n scope,\n {\n key,\n intent,\n summary: options.summary ?? null,\n runId,\n token,\n attemptId,\n conflict: options.conflict ?? 'any',\n ack: options.ack ?? true,\n leaseTtlMs: run?.leaseTtlMs ?? this.leaseTtlMs,\n now,\n },\n this.policy,\n ),\n );\n\n switch (claimed.kind) {\n case 'replay':\n this.emit({ type: 'replayed', scope, key });\n return { status: 'replayed', ok: true, value: claimed.entry.result as T, key, entry: claimed.entry };\n\n case 'refused':\n this.emit({ type: 'refused', scope, key, reason: claimed.reason, runId });\n return {\n status: 'refused',\n ok: false,\n key,\n reason: claimed.reason,\n ...(claimed.conflicts ? { conflicts: claimed.conflicts } : {}),\n ...(claimed.entry ? { entry: claimed.entry } : {}),\n };\n\n case 'in_flight': {\n if (options.wait && this.now() < waitUntil) {\n await this.waitForSettle(scope, key, waitUntil, options.wait.intervalMs ?? 100);\n continue;\n }\n this.emit({ type: 'in_flight', scope, key });\n return { status: 'in_flight', ok: false, key, entry: claimed.entry };\n }\n\n case 'ambiguous': {\n if (reconcile && !reconciledOnce) {\n reconciledOnce = true;\n let verdict: ReconcileVerdict;\n try {\n verdict = await reconcile(claimed.entry);\n } catch {\n verdict = { outcome: 'unknown' };\n }\n if (verdict.outcome !== 'unknown') {\n const at = this.now();\n const resolved = await this.store.transact(scope, (s) =>\n logic.resolve(\n s,\n {\n key,\n outcome: verdict.outcome as 'committed' | 'not_applied',\n result: verdict.outcome === 'committed' ? verdict.result : undefined,\n attemptId: claimed.entry.attemptId,\n now: at,\n },\n this.policy,\n ),\n );\n if (resolved) this.emit({ type: 'reconciled', scope, key, outcome: verdict.outcome });\n continue;\n }\n }\n this.emit({ type: 'ambiguous', scope, key });\n return { status: 'ambiguous', ok: false, key, entry: claimed.entry };\n }\n\n case 'acquired':\n return this.runAcquired(scope, key, claimed.entry, effect, classify);\n }\n }\n const entry = (await this.store.read(scope))?.effects[key];\n if (entry && entry.status === 'committed') {\n return { status: 'replayed', ok: true, value: entry.result as T, key, entry };\n }\n if (entry) {\n this.emit({ type: 'in_flight', scope, key });\n return { status: 'in_flight', ok: false, key, entry };\n }\n throw new Error('replyonce: could not claim key after several rounds');\n }\n\n private async runAcquired<T>(\n scope: string,\n key: string,\n entry: EffectRecord,\n effect: Effect<T>,\n classify: (err: unknown) => ErrorClass,\n ): Promise<OnceResult<T>> {\n const { attempt, attemptId, runId } = entry;\n this.emit({ type: 'claimed', scope, key, attempt, runId });\n const remaining = Math.max(1, entry.claimedAt + this.policy.pendingTtlMs - this.now());\n const ctx: EffectContext = {\n key,\n scope,\n intent: entry.intent,\n attempt,\n idempotencyKey: providerIdempotencyKey(scope, key),\n fencingToken: entry.token,\n signal: AbortSignal.timeout(remaining),\n };\n\n let value: T;\n try {\n value = await effect(ctx);\n } catch (error) {\n const cls = safeClassify(classify, error);\n const outcome = cls === 'not_applied' ? 'failed' : 'ambiguous';\n await this.settleWithRetry(scope, {\n key,\n attemptId,\n outcome,\n error: describeError(error),\n now: 0,\n }).catch(() => undefined);\n if (outcome === 'failed') {\n this.emit({ type: 'failed', scope, key, attempt, error });\n return {\n status: 'failed',\n ok: false,\n key,\n attempt,\n error,\n retryable: attempt < this.policy.maxAttempts,\n };\n }\n this.emit({ type: 'ambiguous', scope, key, error });\n const current = (await this.store.read(scope).catch(() => null))?.effects[key] ?? entry;\n return { status: 'ambiguous', ok: false, key, entry: current, error };\n }\n\n let stored: Json | undefined;\n try {\n stored = value === undefined ? null : (JSON.parse(JSON.stringify(value)) as Json);\n } catch {\n stored = null;\n this.emit({ type: 'unserializable_result', scope, key });\n }\n try {\n const settled = await this.settleWithRetry(scope, {\n key,\n attemptId,\n outcome: 'committed',\n result: stored,\n now: 0,\n });\n this.emit({ type: 'committed', scope, key, attempt, runId });\n if (settled.kind === 'stale' || settled.wasAbandoned) {\n this.emit({ type: 'late_settle', scope, key, attempt });\n return { status: 'executed', ok: true, value, key, attempt, lateSettle: true };\n }\n } catch {\n // The effect happened but the ledger could not record it. The claim stays pending and\n // later reads treat it as ambiguous, which fails safe.\n this.emit({ type: 'late_settle', scope, key, attempt });\n return { status: 'executed', ok: true, value, key, attempt, lateSettle: true };\n }\n return { status: 'executed', ok: true, value, key, attempt };\n }\n\n private async settleWithRetry(scope: string, input: logic.SettleInput): Promise<logic.SettleOutcome> {\n let lastError: unknown;\n for (let i = 0; i < 4; i++) {\n try {\n const now = this.now();\n return await this.store.transact(scope, (s) => logic.settle(s, { ...input, now }, this.policy));\n } catch (err) {\n lastError = err;\n await this.sleep(25 * 4 ** i);\n }\n }\n throw lastError;\n }\n\n private async waitForSettle(scope: string, key: string, until: number, intervalMs: number): Promise<void> {\n while (this.now() < until) {\n await this.sleep(Math.min(intervalMs, Math.max(1, until - this.now())));\n const e = (await this.store.read(scope))?.effects[key];\n if (!e || logic.effectiveStatus(e, this.now(), this.policy) !== 'pending') return;\n }\n }\n}\n\nexport type RunState = BeginOutcome['state'];\n\n/** A unit of agent work on one scope, holding a fencing token. */\nexport class Run {\n readonly scope: string;\n readonly runId: string;\n /** `new`, `resumed` (same runId seen before), `superseded`, `ended`, or `busy` (exclusive mode). */\n readonly state: RunState;\n /** Monotonic per scope. `null` when the run could not start (`busy`). */\n readonly fencingToken: number | null;\n /** Effects this runId already claimed before (non-empty on redelivery). */\n readonly priorEffects: EffectRecord[];\n readonly busyHolder: { runId: string; token: number; leaseExpiresAt: number } | null;\n private readonly ro: ReplyOnce;\n private readonly leaseTtlMs: number;\n\n constructor(ro: ReplyOnce, scope: string, runId: string, leaseTtlMs: number, outcome: BeginOutcome) {\n this.ro = ro;\n this.scope = scope;\n this.runId = runId;\n this.leaseTtlMs = leaseTtlMs;\n this.state = outcome.state;\n if (outcome.state === 'busy') {\n this.fencingToken = null;\n this.priorEffects = [];\n this.busyHolder = outcome.holder;\n } else {\n this.fencingToken = (outcome.run as RunRecord).token;\n this.priorEffects = outcome.priorEffects;\n this.busyHolder = null;\n }\n }\n\n /** `true` when this run may write: it is new or resumed, not superseded, ended or busy. */\n get canWrite(): boolean {\n return this.state === 'new' || this.state === 'resumed';\n }\n\n /**\n * `true` when a previous delivery of this run already claimed a write that may have\n * reached the outside world (pending, committed or ambiguous).\n */\n get alreadyWrote(): boolean {\n return this.priorEffects.some((e) => e.status !== 'failed');\n }\n\n /** Run `effect` at most once per (scope, intent, payload), fenced to this run. */\n once<T>(intent: string, payload: unknown, effect: Effect<T>, options: RunOnceOptions = {}): Promise<OnceResult<T>> {\n const key = effectKey(intent, payload);\n if (this.fencingToken === null) {\n return Promise.resolve({ status: 'refused', ok: false, key, reason: 'busy' });\n }\n const self = { runId: this.runId, token: this.fencingToken, leaseTtlMs: this.leaseTtlMs };\n return this.ro.execute(this.scope, key, intent, self, effect, options);\n }\n\n check(intent: string, payload?: unknown): Promise<CheckResult> {\n return this.ro.check({ scope: this.scope, intent, payload });\n }\n\n history(): Promise<EffectRecord[]> {\n return this.ro.history(this.scope);\n }\n\n /**\n * Renew the lease. `false` means stop: the run was superseded (by a newer run or a later\n * delivery of this one), cancelled or ended.\n */\n heartbeat(): Promise<boolean> {\n if (this.fencingToken === null) return Promise.resolve(false);\n return this.ro.renewRun(this.scope, this.runId, this.fencingToken, this.leaseTtlMs);\n }\n\n /** Mark the run finished. A no-op if a later delivery of the same run has taken over. */\n end(reason: RunEndReason = 'completed'): Promise<void> {\n if (this.fencingToken === null) return Promise.resolve();\n return this.ro.endRun(this.scope, this.runId, this.fencingToken, reason);\n }\n}\n\nexport function createReplyOnce(options: ReplyOnceOptions): ReplyOnce {\n return new ReplyOnce(options);\n}\n\n/** Return the value of a successful result, or throw a `ReplyOnceError` describing why not. */\nexport function unwrap<T>(result: OnceResult<T>): T {\n if (result.ok) return result.value;\n throw new ReplyOnceError(result);\n}\n\nexport class ReplyOnceError extends Error {\n readonly result: Exclude<OnceResult<unknown>, { ok: true }>;\n constructor(result: Exclude<OnceResult<unknown>, { ok: true }>) {\n const detail = result.status === 'refused' ? `${result.status}: ${result.reason}` : result.status;\n super(`replyonce: effect ${result.key} not executed (${detail})`);\n this.name = 'ReplyOnceError';\n this.result = result;\n }\n}\n\nfunction assertScope(scope: string): void {\n if (typeof scope !== 'string' || scope.length === 0 || scope.length > 512) {\n throw new TypeError('replyonce: scope must be a non-empty string of at most 512 characters');\n }\n}\n\nfunction safeClassify(classify: (err: unknown) => ErrorClass, err: unknown): ErrorClass {\n try {\n return classify(err) === 'not_applied' ? 'not_applied' : 'ambiguous';\n } catch {\n return 'ambiguous';\n }\n}\n\nfunction describeError(err: unknown): string {\n const text = err instanceof Error ? `${err.name}: ${err.message}` : String(err);\n return text.slice(0, 500);\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((r) => setTimeout(r, ms));\n}\n","import { createHash } from 'node:crypto';\n\n/** Deterministic JSON: object keys sorted, `undefined` members dropped. */\nexport function canonicalJson(value: unknown): string {\n return JSON.stringify(normalize(value, new Set()));\n}\n\nfunction normalize(value: unknown, seen: Set<unknown>): unknown {\n if (value === null || value === undefined) return null;\n if (typeof value === 'bigint') return value.toString();\n if (typeof value !== 'object') {\n if (typeof value === 'number' && !Number.isFinite(value)) return String(value);\n if (typeof value === 'function' || typeof value === 'symbol') return null;\n return value;\n }\n if (value instanceof Date) return value.toISOString();\n if (seen.has(value)) throw new TypeError('replyonce: cannot hash a circular payload');\n seen.add(value);\n let out: unknown;\n if (Array.isArray(value)) {\n out = value.map((v) => normalize(v, seen));\n } else {\n const obj: Record<string, unknown> = {};\n for (const k of Object.keys(value as Record<string, unknown>).sort()) {\n const v = (value as Record<string, unknown>)[k];\n if (v === undefined) continue;\n obj[k] = normalize(v, seen);\n }\n out = obj;\n }\n seen.delete(value);\n return out;\n}\n\nexport function sha256Hex(input: string): string {\n return createHash('sha256').update(input).digest('hex');\n}\n\nexport function hashPayload(payload: unknown): string {\n return sha256Hex(canonicalJson(payload)).slice(0, 32);\n}\n\nconst SAFE_INTENT = /^[A-Za-z0-9_.:-]{1,100}$/;\n\n/** Ledger key for an intent plus the business identity of the payload. */\nexport function effectKey(intent: string, payload: unknown): string {\n if (!SAFE_INTENT.test(intent)) {\n throw new TypeError(\n `replyonce: intent must match ${SAFE_INTENT} (got ${JSON.stringify(intent)})`,\n );\n }\n return payload === undefined ? intent : `${intent}:${hashPayload(payload)}`;\n}\n\n/** Stable idempotency key to hand to a provider (Stripe-style `Idempotency-Key`). */\nexport function providerIdempotencyKey(scope: string, key: string): string {\n return `ro_${sha256Hex(`${scope}\\u0000${key}`).slice(0, 40)}`;\n}\n","import type {\n ConflictInfo,\n EffectRecord,\n Json,\n RefusalReason,\n RunEndReason,\n RunMode,\n RunRecord,\n ScopeState,\n TransactResult,\n} from './types.js';\n\n/**\n * The replyonce state machine. Every function here is pure: it takes a scope's state and\n * returns the next state plus a decision. Stores only make each call atomic.\n */\n\nexport interface Policy {\n pendingTtlMs: number;\n maxAttempts: number;\n retentionMs: number;\n}\n\nexport type ConflictMode = 'any' | 'same-intent' | 'none';\n\nexport const SUMMARY_MAX = 2000;\n\nexport function emptyState(): ScopeState {\n return { v: 1, seq: 0, tokenSeq: 0, runs: {}, effects: {} };\n}\n\nfunction load(state: ScopeState | null, now: number, policy: Policy): ScopeState {\n const s = state ? (structuredClone(state) as ScopeState) : emptyState();\n if (s.v !== 1) throw new Error(`replyonce: unsupported state version ${String(s.v)}`);\n const horizon = now - policy.retentionMs;\n for (const [k, e] of Object.entries(s.effects)) {\n if ((e.status === 'committed' || e.status === 'failed') && (e.settledAt ?? e.claimedAt) < horizon) {\n delete s.effects[k];\n }\n }\n for (const [id, r] of Object.entries(s.runs)) {\n if ((r.endedAt ?? r.leaseExpiresAt) < horizon) delete s.runs[id];\n }\n return s;\n}\n\n/** Advance the scope's logical clock for a change being made now. */\nfunction tick(s: ScopeState): number {\n s.seq += 1;\n return s.seq;\n}\n\nexport function isAbandoned(e: EffectRecord, now: number, policy: Policy): boolean {\n return e.status === 'pending' && now - e.claimedAt >= policy.pendingTtlMs;\n}\n\n/** The status a reader should act on: a pending claim past its TTL is treated as ambiguous. */\nexport function effectiveStatus(e: EffectRecord, now: number, policy: Policy): EffectRecord['status'] {\n return isAbandoned(e, now, policy) ? 'ambiguous' : e.status;\n}\n\nfunction isLive(r: RunRecord, now: number): boolean {\n return r.endedAt === null && r.leaseExpiresAt > now;\n}\n\n/**\n * Is `a` newer than `b`? Exclusive leases order by token; otherwise by context recency. Never\n * by token there: a redelivery bumps an old run's token. Equal context times order by when\n * the run first began only if neither run gave a `contextAt`, because then each read its\n * context as it began. Otherwise neither is newer: begin order says nothing about whose\n * context is fresher, and fencing out the wrong one would leave its message unanswered.\n * Both may then write; conflict checks keep them from answering the same thing twice.\n */\nfunction isNewer(a: RunRecord, b: RunRecord): boolean {\n if (a.mode === 'exclusive' && b.mode === 'exclusive') return a.token > b.token;\n if (a.orderAt !== b.orderAt) return a.orderAt > b.orderAt;\n if (a.baseContextAt !== null || b.baseContextAt !== null) return false;\n return a.baseSeq > b.baseSeq;\n}\n\n/**\n * A newer run owns the scope if it is live, or if it already wrote (it may have answered).\n * A newer run that ended without writing, or was cancelled, does not.\n */\nexport function supersedingRun(s: ScopeState, run: RunRecord, now: number): RunRecord | null {\n let best: RunRecord | null = null;\n for (const r of Object.values(s.runs)) {\n if (r.runId === run.runId || r.endReason === 'cancelled' || !isNewer(r, run)) continue;\n if (r.wrote || isLive(r, now)) {\n if (!best || isNewer(r, best)) best = r;\n }\n }\n return best;\n}\n\nfunction markSeen(run: RunRecord, seq: number, now: number): void {\n run.seenSeq = Math.max(run.seenSeq, seq);\n run.seenAt = Math.max(run.seenAt, now);\n run.contextAt = null;\n}\n\n// ---------------------------------------------------------------- begin\n\nexport interface BeginInput {\n runId: string;\n mode: RunMode;\n leaseTtlMs: number;\n now: number;\n /** When the run's view of the scope was read. Writes that landed after it are conflicts. */\n contextAt?: number;\n}\n\nexport type BeginOutcome =\n | { state: 'new' | 'resumed' | 'superseded' | 'ended'; run: RunRecord; priorEffects: EffectRecord[] }\n | { state: 'busy'; holder: { runId: string; token: number; leaseExpiresAt: number } };\n\nexport function begin(\n state: ScopeState | null,\n input: BeginInput,\n policy: Policy,\n): TransactResult<BeginOutcome> {\n const { now } = input;\n const s = load(state, now, policy);\n const existing = s.runs[input.runId];\n if (existing) {\n const priorEffects = Object.values(s.effects).filter((e) => e.runId === input.runId);\n if (existing.endedAt !== null) {\n return { next: null, result: { state: 'ended', run: existing, priorEffects } };\n }\n existing.leaseExpiresAt = Math.max(existing.leaseExpiresAt, now + input.leaseTtlMs);\n tick(s);\n // A redelivery takes the run over; any earlier delivery still executing is fenced out.\n s.tokenSeq += 1;\n existing.token = s.tokenSeq;\n // It acts on the run's original context, so it has not seen what earlier deliveries saw,\n // unless it says it re-read the scope.\n existing.seenSeq = existing.baseSeq;\n existing.seenAt = existing.baseContextAt ?? existing.startedAt;\n existing.contextAt = existing.baseContextAt;\n if (input.contextAt !== undefined && input.contextAt > existing.seenAt) {\n const fresh = Math.min(input.contextAt, now);\n for (const e of Object.values(s.effects)) {\n if (e.settleSeq !== null && e.settledAt !== null && e.settledAt < fresh) {\n existing.seenSeq = Math.max(existing.seenSeq, e.settleSeq);\n }\n }\n existing.seenAt = fresh;\n existing.contextAt = fresh;\n }\n const newer = supersedingRun(s, existing, now);\n return { next: s, result: { state: newer ? 'superseded' : 'resumed', run: existing, priorEffects } };\n }\n if (input.mode === 'exclusive') {\n let holder: RunRecord | undefined;\n for (const r of Object.values(s.runs)) {\n if (isLive(r, now) && (!holder || r.token > holder.token)) holder = r;\n }\n if (holder) {\n return {\n next: null,\n result: {\n state: 'busy',\n holder: { runId: holder.runId, token: holder.token, leaseExpiresAt: holder.leaseExpiresAt },\n },\n };\n }\n }\n const seq = tick(s);\n s.tokenSeq += 1;\n const contextAt = input.contextAt === undefined ? null : Math.min(input.contextAt, now);\n const run: RunRecord = {\n runId: input.runId,\n token: s.tokenSeq,\n mode: input.mode,\n startedAt: now,\n orderAt: input.mode === 'exclusive' ? now : (contextAt ?? now),\n leaseExpiresAt: now + input.leaseTtlMs,\n seenSeq: seq,\n seenAt: contextAt ?? now,\n contextAt,\n baseSeq: seq,\n baseContextAt: contextAt,\n wrote: false,\n endedAt: null,\n endReason: null,\n };\n s.runs[input.runId] = run;\n const newer = supersedingRun(s, run, now);\n return { next: s, result: { state: newer ? 'superseded' : 'new', run, priorEffects: [] } };\n}\n\n// ---------------------------------------------------------------- heartbeat / end\n\nexport type RenewOutcome =\n | { ok: true; run: RunRecord }\n | { ok: false; reason: 'unknown_run' | 'run_ended' | 'run_cancelled' | 'superseded' };\n\nexport function renew(\n state: ScopeState | null,\n input: { runId: string; token: number; leaseTtlMs: number; now: number },\n policy: Policy,\n): TransactResult<RenewOutcome> {\n const s = load(state, input.now, policy);\n const r = s.runs[input.runId];\n if (!r) return { next: null, result: { ok: false, reason: 'unknown_run' } };\n if (r.token !== input.token) return { next: null, result: { ok: false, reason: 'superseded' } };\n if (r.endReason === 'cancelled') return { next: null, result: { ok: false, reason: 'run_cancelled' } };\n if (r.endedAt !== null) return { next: null, result: { ok: false, reason: 'run_ended' } };\n if (supersedingRun(s, r, input.now)) return { next: null, result: { ok: false, reason: 'superseded' } };\n r.leaseExpiresAt = Math.max(r.leaseExpiresAt, input.now + input.leaseTtlMs);\n tick(s);\n return { next: s, result: { ok: true, run: r } };\n}\n\nexport function endRun(\n state: ScopeState | null,\n input: { runId: string; reason: RunEndReason; now: number; /** Ignore if a later delivery holds the run. */ token?: number },\n policy: Policy,\n): TransactResult<RunRecord | null> {\n const s = load(state, input.now, policy);\n const r = s.runs[input.runId];\n if (!r) return { next: null, result: null };\n if (input.token !== undefined && r.token !== input.token) return { next: null, result: null };\n if (r.endedAt !== null && !(input.reason === 'cancelled' && r.endReason !== 'cancelled')) {\n return { next: null, result: r };\n }\n r.endedAt = r.endedAt ?? input.now;\n r.endReason = input.reason;\n tick(s);\n return { next: s, result: r };\n}\n\n// ---------------------------------------------------------------- claim\n\nexport interface ClaimInput {\n key: string;\n intent: string;\n summary: string | null;\n runId: string | null;\n /** The claiming delivery's fencing token. A stale token is refused as superseded. */\n token: number | null;\n attemptId: string;\n conflict: ConflictMode;\n ack: boolean;\n leaseTtlMs: number;\n now: number;\n}\n\nexport type ClaimOutcome =\n | { kind: 'acquired'; entry: EffectRecord }\n | { kind: 'replay'; entry: EffectRecord }\n | { kind: 'in_flight'; entry: EffectRecord }\n | { kind: 'ambiguous'; entry: EffectRecord }\n | { kind: 'refused'; reason: RefusalReason; conflicts?: ConflictInfo[]; entry?: EffectRecord };\n\n/** Writes by others on the scope that `run` has not seen yet, or that may still land. */\nexport function conflictsFor(\n s: ScopeState,\n run: RunRecord,\n key: string,\n intent: string,\n mode: ConflictMode,\n now: number,\n policy: Policy,\n): ConflictInfo[] {\n if (mode === 'none') return [];\n const out: ConflictInfo[] = [];\n for (const e of Object.values(s.effects)) {\n if (e.key === key) continue;\n if (e.runId === run.runId) {\n // This delivery knows its own writes. An earlier delivery's writes matter only when\n // this one is about to do the same kind of thing differently (a reply worded anew).\n if (e.token === run.token || e.intent !== intent) continue;\n }\n if (mode === 'same-intent' && e.intent !== intent) continue;\n const abandoned = isAbandoned(e, now, policy);\n let conflict = false;\n if (e.status === 'pending' && !abandoned) {\n conflict = true;\n } else if (abandoned) {\n // It may have landed at any time up to when it became abandoned.\n conflict = e.claimedAt + policy.pendingTtlMs >= run.seenAt;\n } else if (e.status === 'committed' || e.status === 'ambiguous') {\n conflict =\n (e.settleSeq ?? 0) > run.seenSeq || (run.contextAt !== null && (e.settledAt ?? 0) >= run.contextAt);\n }\n if (conflict) {\n out.push({\n key: e.key,\n intent: e.intent,\n status: abandoned ? 'ambiguous' : e.status,\n runId: e.runId,\n summary: e.summary,\n at: e.settledAt ?? e.claimedAt,\n });\n }\n }\n return out.sort((a, b) => a.at - b.at).slice(0, 10);\n}\n\nexport function claim(\n state: ScopeState | null,\n scope: string,\n input: ClaimInput,\n policy: Policy,\n): TransactResult<ClaimOutcome> {\n const { now } = input;\n const s = load(state, now, policy);\n const prior = s.effects[input.key];\n\n if (prior) {\n if (prior.status === 'committed') return { next: null, result: { kind: 'replay', entry: prior } };\n if (prior.status === 'pending') {\n if (!isAbandoned(prior, now, policy)) {\n return { next: null, result: { kind: 'in_flight', entry: prior } };\n }\n const seq = tick(s);\n prior.status = 'ambiguous';\n prior.settledAt = prior.claimedAt + policy.pendingTtlMs;\n prior.settleSeq = seq;\n prior.error = prior.error ?? 'abandoned: not settled within pendingTtlMs';\n return { next: s, result: { kind: 'ambiguous', entry: prior } };\n }\n if (prior.status === 'ambiguous') return { next: null, result: { kind: 'ambiguous', entry: prior } };\n if (prior.attempt >= policy.maxAttempts) {\n return { next: null, result: { kind: 'refused', reason: 'exhausted', entry: prior } };\n }\n }\n\n let run: RunRecord | undefined;\n if (input.runId !== null) {\n run = s.runs[input.runId];\n if (!run) return { next: null, result: { kind: 'refused', reason: 'unknown_run' } };\n if (run.endReason === 'cancelled') return { next: null, result: { kind: 'refused', reason: 'run_cancelled' } };\n if (run.endedAt !== null) return { next: null, result: { kind: 'refused', reason: 'run_ended' } };\n if ((input.token !== null && run.token !== input.token) || supersedingRun(s, run, now)) {\n return { next: null, result: { kind: 'refused', reason: 'superseded' } };\n }\n\n const conflicts = conflictsFor(s, run, input.key, input.intent, input.conflict, now, policy);\n if (conflicts.length > 0) {\n if (!input.ack) {\n return { next: null, result: { kind: 'refused', reason: 'conversation_changed', conflicts } };\n }\n markSeen(run, tick(s), now);\n return { next: s, result: { kind: 'refused', reason: 'conversation_changed', conflicts } };\n }\n }\n\n const seq = tick(s);\n const entry: EffectRecord = {\n key: input.key,\n scope,\n intent: input.intent,\n status: 'pending',\n attempt: (prior?.attempt ?? 0) + 1,\n attemptId: input.attemptId,\n runId: input.runId,\n token: run?.token ?? null,\n summary: input.summary === null ? null : input.summary.slice(0, SUMMARY_MAX),\n error: null,\n createdAt: prior?.createdAt ?? now,\n claimedAt: now,\n settledAt: null,\n claimSeq: seq,\n settleSeq: null,\n };\n s.effects[input.key] = entry;\n if (run) {\n // A claim does not mark anything seen: writes this check deliberately skipped (other\n // intents, or `none`) must still be reported to a later check that covers them.\n run.wrote = true;\n run.leaseExpiresAt = Math.max(run.leaseExpiresAt, now + input.leaseTtlMs);\n }\n return { next: s, result: { kind: 'acquired', entry } };\n}\n\n// ---------------------------------------------------------------- settle / resolve\n\nexport interface SettleInput {\n key: string;\n attemptId: string;\n outcome: 'committed' | 'failed' | 'ambiguous';\n result?: Json;\n error?: string | null;\n now: number;\n}\n\nexport type SettleOutcome =\n | { kind: 'settled'; entry: EffectRecord; wasAbandoned: boolean }\n | { kind: 'stale'; entry: EffectRecord | null };\n\nexport function settle(\n state: ScopeState | null,\n input: SettleInput,\n policy: Policy,\n): TransactResult<SettleOutcome> {\n const s = load(state, input.now, policy);\n const e = s.effects[input.key];\n // A late \"committed\" from the attempt a reconciler judged not applied is still the truth.\n const lateTruth = e?.status === 'failed' && input.outcome === 'committed';\n if (\n !e ||\n e.attemptId !== input.attemptId ||\n (e.status !== 'pending' && e.status !== 'ambiguous' && !lateTruth)\n ) {\n return { next: null, result: { kind: 'stale', entry: e ?? null } };\n }\n const wasAbandoned = e.status !== 'pending' || isAbandoned(e, input.now, policy);\n e.status = input.outcome;\n e.settledAt = input.now;\n e.settleSeq = tick(s);\n e.error = input.error ?? null;\n if (input.outcome === 'committed' && input.result !== undefined) e.result = input.result;\n return { next: s, result: { kind: 'settled', entry: e, wasAbandoned } };\n}\n\nexport interface ResolveInput {\n key: string;\n outcome: 'committed' | 'not_applied';\n result?: Json;\n /** Only resolve if the ledger still holds this attempt. */\n attemptId?: string;\n now: number;\n}\n\n/** Resolve an ambiguous (or abandoned) effect after checking the outside world. */\nexport function resolve(\n state: ScopeState | null,\n input: ResolveInput,\n policy: Policy,\n): TransactResult<EffectRecord | null> {\n const s = load(state, input.now, policy);\n const e = s.effects[input.key];\n if (!e) return { next: null, result: null };\n if (input.attemptId !== undefined && e.attemptId !== input.attemptId) return { next: null, result: null };\n if (effectiveStatus(e, input.now, policy) !== 'ambiguous') return { next: null, result: null };\n e.settledAt = input.now;\n e.settleSeq = tick(s);\n if (input.outcome === 'committed') {\n e.status = 'committed';\n e.error = null;\n if (input.result !== undefined) e.result = input.result;\n } else {\n e.status = 'failed';\n e.error = 'reconciled: not applied';\n }\n return { next: s, result: e };\n}\n","import type { ScopeState, Store, TransactResult } from '../types.js';\n\n/**\n * Single-process store. Atomic because `transact` reads, decides and writes in one\n * synchronous step. State is kept as JSON so it behaves like the shared stores.\n * Use it for tests, demos and single-instance deployments; it is lost on restart.\n */\nexport class MemoryStore implements Store {\n private readonly data = new Map<string, string>();\n\n async transact<T>(scope: string, fn: (state: ScopeState | null) => TransactResult<T>): Promise<T> {\n const raw = this.data.get(scope);\n const { next, result } = fn(raw === undefined ? null : (JSON.parse(raw) as ScopeState));\n if (next) this.data.set(scope, JSON.stringify(next));\n return result;\n }\n\n async read(scope: string): Promise<ScopeState | null> {\n const raw = this.data.get(scope);\n return raw === undefined ? null : (JSON.parse(raw) as ScopeState);\n }\n\n /** Number of scopes held. */\n get size(): number {\n return this.data.size;\n }\n\n clear(): void {\n this.data.clear();\n }\n}\n"],"mappings":"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","names":["import_node_crypto","newer","seq"]}
package/dist/index.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- export { B as BeginOptions, b as CheckResult, C as ConflictMode, E as Effect, K as KeyParts, N as NotApplied, O as OnceOptions, c as Reconciler, a as ReplyOnce, d as ReplyOnceError, e as ReplyOnceOptions, R as Run, f as RunOnceOptions, g as RunState, h as createReplyOnce, i as defaultClassifyError, u as unwrap } from './replyonce-CGwSjxB2.cjs';
1
+ export { B as BeginOptions, b as CheckResult, C as ConflictMode, E as Effect, K as KeyParts, N as NotApplied, O as OnceOptions, c as Reconciler, a as ReplyOnce, d as ReplyOnceError, e as ReplyOnceOptions, R as Run, f as RunOnceOptions, g as RunState, h as createReplyOnce, i as defaultClassifyError, u as unwrap } from './replyonce-C-Qhsfsw.cjs';
2
2
  import { S as Store, a as ScopeState, T as TransactResult } from './types-Dn7TSEnu.cjs';
3
3
  export { C as ConflictInfo, E as EffectContext, b as EffectRecord, c as EffectStatus, d as ErrorClass, J as Json, O as OnceResult, e as ReconcileVerdict, f as RefusalReason, g as ReplyOnceEvent, h as RunEndReason, R as RunMode, i as RunRecord } from './types-Dn7TSEnu.cjs';
4
4
 
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export { B as BeginOptions, b as CheckResult, C as ConflictMode, E as Effect, K as KeyParts, N as NotApplied, O as OnceOptions, c as Reconciler, a as ReplyOnce, d as ReplyOnceError, e as ReplyOnceOptions, R as Run, f as RunOnceOptions, g as RunState, h as createReplyOnce, i as defaultClassifyError, u as unwrap } from './replyonce-DNRS4FWY.js';
1
+ export { B as BeginOptions, b as CheckResult, C as ConflictMode, E as Effect, K as KeyParts, N as NotApplied, O as OnceOptions, c as Reconciler, a as ReplyOnce, d as ReplyOnceError, e as ReplyOnceOptions, R as Run, f as RunOnceOptions, g as RunState, h as createReplyOnce, i as defaultClassifyError, u as unwrap } from './replyonce-C9AavZdk.js';
2
2
  import { S as Store, a as ScopeState, T as TransactResult } from './types-Dn7TSEnu.js';
3
3
  export { C as ConflictInfo, E as EffectContext, b as EffectRecord, c as EffectStatus, d as ErrorClass, J as Json, O as OnceResult, e as ReconcileVerdict, f as RefusalReason, g as ReplyOnceEvent, h as RunEndReason, R as RunMode, i as RunRecord } from './types-Dn7TSEnu.js';
4
4
 
package/dist/index.js CHANGED
@@ -351,6 +351,7 @@ var ReplyOnce = class {
351
351
  reconciler;
352
352
  emit;
353
353
  now;
354
+ sleep;
354
355
  constructor(options) {
355
356
  if (!options || !options.store) throw new TypeError("replyonce: a store is required");
356
357
  this.store = options.store;
@@ -374,6 +375,7 @@ var ReplyOnce = class {
374
375
  } : () => {
375
376
  };
376
377
  this.now = options.clock ?? Date.now;
378
+ this.sleep = options.sleep ?? sleep;
377
379
  }
378
380
  /** Resolve key parts to the ledger key used for them. */
379
381
  key(parts) {
@@ -646,14 +648,14 @@ var ReplyOnce = class {
646
648
  return await this.store.transact(scope, (s) => settle(s, { ...input, now }, this.policy));
647
649
  } catch (err) {
648
650
  lastError = err;
649
- await sleep(25 * 4 ** i);
651
+ await this.sleep(25 * 4 ** i);
650
652
  }
651
653
  }
652
654
  throw lastError;
653
655
  }
654
656
  async waitForSettle(scope, key, until, intervalMs) {
655
657
  while (this.now() < until) {
656
- await sleep(Math.min(intervalMs, Math.max(1, until - this.now())));
658
+ await this.sleep(Math.min(intervalMs, Math.max(1, until - this.now())));
657
659
  const e = (await this.store.read(scope))?.effects[key];
658
660
  if (!e || effectiveStatus(e, this.now(), this.policy) !== "pending") return;
659
661
  }
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/replyonce.ts","../src/key.ts","../src/logic.ts","../src/stores/memory.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto';\nimport { effectKey, providerIdempotencyKey } from './key.js';\nimport * as logic from './logic.js';\nimport type { BeginOutcome, ClaimOutcome, ConflictMode, Policy } from './logic.js';\nimport type {\n EffectContext,\n EffectRecord,\n EffectStatus,\n ErrorClass,\n Json,\n OnceResult,\n ReconcileVerdict,\n ReplyOnceEvent,\n RunEndReason,\n RunMode,\n RunRecord,\n Store,\n} from './types.js';\n\n/** Throw this (or anything with `replyonce: 'not_applied'`) when the effect definitely did not happen. */\nexport class NotApplied extends Error {\n readonly replyonce = 'not_applied' as const;\n constructor(message = 'effect not applied', options?: { cause?: unknown }) {\n super(message);\n this.name = 'NotApplied';\n if (options && 'cause' in options) (this as { cause?: unknown }).cause = options.cause;\n }\n}\n\nexport function defaultClassifyError(err: unknown): ErrorClass {\n if (err instanceof NotApplied) return 'not_applied';\n if (err && typeof err === 'object' && (err as { replyonce?: unknown }).replyonce === 'not_applied') {\n return 'not_applied';\n }\n return 'ambiguous';\n}\n\nexport interface KeyParts {\n /** Usually the conversation or thread id. All fencing happens within a scope. */\n scope: string;\n /** What the effect is: `reply`, `refund`, `create_ticket`. */\n intent: string;\n /**\n * The business identity of the effect, hashed into the key. Use what makes it the same\n * action (the inbound message being answered, the order being refunded), never\n * model-generated text or a model tool-call id.\n */\n payload?: unknown;\n}\n\nexport type Reconciler = (entry: EffectRecord) => Promise<ReconcileVerdict>;\n\nexport interface ReplyOnceOptions {\n store: Store;\n /** How long a claim may stay pending before it is treated as abandoned (ambiguous). Default 15 min. */\n pendingTtlMs?: number;\n /** Attempts per key, counting only attempts known not to have applied. Default 3. */\n maxAttempts?: number;\n /** How long settled effects and ended runs are remembered. Default 7 days. */\n retentionMs?: number;\n /** Run lease length; renewed on every claim and heartbeat. Default 60 s. */\n leaseTtlMs?: number;\n /** Decide whether a thrown error means \"did not happen\" or \"might have happened\". */\n classifyError?: (err: unknown) => ErrorClass;\n /** Ask the outside world what happened to an ambiguous effect. */\n reconcile?: Reconciler;\n onEvent?: (event: ReplyOnceEvent) => void;\n clock?: () => number;\n}\n\nexport interface OnceOptions {\n /** Short human-readable description, shown to other runs in conflict reports. */\n summary?: string;\n classifyError?: (err: unknown) => ErrorClass;\n reconcile?: Reconciler;\n /** Wait for an in-flight duplicate to settle instead of returning `in_flight` immediately. */\n wait?: { timeoutMs: number; intervalMs?: number };\n}\n\nexport interface RunOnceOptions extends OnceOptions {\n /**\n * `any` (default): refuse if another run wrote anything on this scope since this run last\n * looked. `same-intent`: only effects with the same intent conflict. `none`: per-key\n * idempotency and supersession only.\n */\n conflict?: ConflictMode;\n /** Record that this run has now seen the conflicts, so its next attempt can proceed. Default true. */\n ack?: boolean;\n}\n\nexport interface CheckResult {\n /** `true` only when the effect is known to have happened. */\n done: boolean;\n status: EffectStatus | 'none';\n entry: EffectRecord | null;\n}\n\nexport interface BeginOptions {\n /** Stable id for this unit of work. Reuse it on redelivery (for example the queue job id). */\n runId?: string;\n mode?: RunMode;\n leaseTtlMs?: number;\n /**\n * Epoch ms when this run's context (the conversation it will act on) was read, for\n * example when the webhook payload was captured. Writes by other runs that landed\n * after this point are reported as `conversation_changed`. Default: now.\n */\n contextAt?: number;\n}\n\nexport type Effect<T> = (ctx: EffectContext) => Promise<T> | T;\n\nconst MAX_ROUNDS = 5;\n\nexport class ReplyOnce {\n readonly store: Store;\n readonly policy: Policy;\n readonly leaseTtlMs: number;\n private readonly classify: (err: unknown) => ErrorClass;\n private readonly reconciler: Reconciler | undefined;\n private readonly emit: (event: ReplyOnceEvent) => void;\n private readonly now: () => number;\n\n constructor(options: ReplyOnceOptions) {\n if (!options || !options.store) throw new TypeError('replyonce: a store is required');\n this.store = options.store;\n this.policy = {\n pendingTtlMs: options.pendingTtlMs ?? 15 * 60_000,\n maxAttempts: options.maxAttempts ?? 3,\n retentionMs: options.retentionMs ?? 7 * 24 * 3_600_000,\n };\n if (this.policy.pendingTtlMs <= 0 || this.policy.maxAttempts < 1 || this.policy.retentionMs <= 0) {\n throw new RangeError('replyonce: pendingTtlMs, maxAttempts and retentionMs must be positive');\n }\n this.leaseTtlMs = options.leaseTtlMs ?? 60_000;\n this.classify = options.classifyError ?? defaultClassifyError;\n this.reconciler = options.reconcile;\n const onEvent = options.onEvent;\n this.emit = onEvent\n ? (e) => {\n try {\n onEvent(e);\n } catch {\n // an observer must never change the outcome\n }\n }\n : () => {};\n this.now = options.clock ?? Date.now;\n }\n\n /** Resolve key parts to the ledger key used for them. */\n key(parts: KeyParts): string {\n return effectKey(parts.intent, parts.payload);\n }\n\n /**\n * Run `effect` at most once for this key. A string key is its own scope; key parts give\n * a scope (conversation) plus an intent and business identity.\n */\n once<T>(key: string | KeyParts, effect: Effect<T>, options: OnceOptions = {}): Promise<OnceResult<T>> {\n const { scope, ledgerKey, intent } = this.resolveKey(key);\n return this.execute(scope, ledgerKey, intent, null, effect, { ...options, conflict: 'none', ack: false });\n }\n\n /** \"Was this already done?\" — safe to expose to a model as a read-only tool. */\n async check(key: string | KeyParts): Promise<CheckResult> {\n const { scope, ledgerKey } = this.resolveKey(key);\n const state = await this.store.read(scope);\n const entry = state?.effects[ledgerKey] ?? null;\n if (!entry) return { done: false, status: 'none', entry: null };\n const status = logic.effectiveStatus(entry, this.now(), this.policy);\n return { done: status === 'committed', status, entry };\n }\n\n /** Every effect recorded on a scope, oldest first. */\n async history(scope: string): Promise<EffectRecord[]> {\n const state = await this.store.read(scope);\n if (!state) return [];\n const now = this.now();\n return Object.values(state.effects)\n .map((e) => ({ ...e, status: logic.effectiveStatus(e, now, this.policy) }))\n .sort((a, b) => a.createdAt - b.createdAt);\n }\n\n /** Record what an operator or reconciler found out about an ambiguous effect. */\n async resolve(\n key: string | KeyParts,\n verdict: { outcome: 'committed'; result?: Json } | { outcome: 'not_applied' },\n ): Promise<EffectRecord | null> {\n const { scope, ledgerKey } = this.resolveKey(key);\n const result = 'result' in verdict ? verdict.result : undefined;\n const now = this.now();\n const entry = await this.store.transact(scope, (s) =>\n logic.resolve(s, { key: ledgerKey, outcome: verdict.outcome, result, now }, this.policy),\n );\n if (entry) this.emit({ type: 'reconciled', scope, key: ledgerKey, outcome: verdict.outcome });\n return entry;\n }\n\n /**\n * Start (or, with the same `runId`, resume) a unit of work on a scope. The run gets a\n * fencing token; its writes are refused once a newer run owns the scope.\n */\n async begin(scope: string, options: BeginOptions = {}): Promise<Run> {\n assertScope(scope);\n const runId = options.runId ?? randomUUID();\n const leaseTtlMs = options.leaseTtlMs ?? this.leaseTtlMs;\n const now = this.now();\n const outcome = await this.store.transact(scope, (s) =>\n logic.begin(\n s,\n {\n runId,\n mode: options.mode ?? 'supersede',\n leaseTtlMs,\n now,\n ...(options.contextAt !== undefined ? { contextAt: options.contextAt } : {}),\n },\n this.policy,\n ),\n );\n return new Run(this, scope, runId, leaseTtlMs, outcome);\n }\n\n /** Cancel a run: its future writes are refused with `run_cancelled`. */\n async cancel(scope: string, runId: string): Promise<boolean> {\n const now = this.now();\n const r = await this.store.transact(scope, (s) =>\n logic.endRun(s, { runId, reason: 'cancelled', now }, this.policy),\n );\n return r !== null;\n }\n\n /** @internal */\n async renewRun(scope: string, runId: string, token: number, leaseTtlMs: number): Promise<boolean> {\n const now = this.now();\n const r = await this.store.transact(scope, (s) =>\n logic.renew(s, { runId, token, leaseTtlMs, now }, this.policy),\n );\n return r.ok;\n }\n\n /** @internal */\n async endRun(scope: string, runId: string, token: number, reason: RunEndReason): Promise<void> {\n const now = this.now();\n await this.store.transact(scope, (s) => logic.endRun(s, { runId, token, reason, now }, this.policy));\n }\n\n /** @internal */\n resolveKey(key: string | KeyParts): { scope: string; ledgerKey: string; intent: string } {\n if (typeof key === 'string') {\n assertScope(key);\n return { scope: key, ledgerKey: key, intent: 'once' };\n }\n assertScope(key.scope);\n return { scope: key.scope, ledgerKey: effectKey(key.intent, key.payload), intent: key.intent };\n }\n\n /** @internal */\n async execute<T>(\n scope: string,\n key: string,\n intent: string,\n run: { runId: string; token: number; leaseTtlMs: number } | null,\n effect: Effect<T>,\n options: RunOnceOptions,\n ): Promise<OnceResult<T>> {\n const runId = run?.runId ?? null;\n const token = run?.token ?? null;\n const classify = options.classifyError ?? this.classify;\n const reconcile = options.reconcile ?? this.reconciler;\n const waitUntil = options.wait ? this.now() + options.wait.timeoutMs : 0;\n let reconciledOnce = false;\n\n for (let round = 0; round < MAX_ROUNDS; round++) {\n const attemptId = randomUUID();\n const now = this.now();\n const claimed: ClaimOutcome = await this.store.transact(scope, (s) =>\n logic.claim(\n s,\n scope,\n {\n key,\n intent,\n summary: options.summary ?? null,\n runId,\n token,\n attemptId,\n conflict: options.conflict ?? 'any',\n ack: options.ack ?? true,\n leaseTtlMs: run?.leaseTtlMs ?? this.leaseTtlMs,\n now,\n },\n this.policy,\n ),\n );\n\n switch (claimed.kind) {\n case 'replay':\n this.emit({ type: 'replayed', scope, key });\n return { status: 'replayed', ok: true, value: claimed.entry.result as T, key, entry: claimed.entry };\n\n case 'refused':\n this.emit({ type: 'refused', scope, key, reason: claimed.reason, runId });\n return {\n status: 'refused',\n ok: false,\n key,\n reason: claimed.reason,\n ...(claimed.conflicts ? { conflicts: claimed.conflicts } : {}),\n ...(claimed.entry ? { entry: claimed.entry } : {}),\n };\n\n case 'in_flight': {\n if (options.wait && this.now() < waitUntil) {\n await this.waitForSettle(scope, key, waitUntil, options.wait.intervalMs ?? 100);\n continue;\n }\n this.emit({ type: 'in_flight', scope, key });\n return { status: 'in_flight', ok: false, key, entry: claimed.entry };\n }\n\n case 'ambiguous': {\n if (reconcile && !reconciledOnce) {\n reconciledOnce = true;\n let verdict: ReconcileVerdict;\n try {\n verdict = await reconcile(claimed.entry);\n } catch {\n verdict = { outcome: 'unknown' };\n }\n if (verdict.outcome !== 'unknown') {\n const at = this.now();\n const resolved = await this.store.transact(scope, (s) =>\n logic.resolve(\n s,\n {\n key,\n outcome: verdict.outcome as 'committed' | 'not_applied',\n result: verdict.outcome === 'committed' ? verdict.result : undefined,\n attemptId: claimed.entry.attemptId,\n now: at,\n },\n this.policy,\n ),\n );\n if (resolved) this.emit({ type: 'reconciled', scope, key, outcome: verdict.outcome });\n continue;\n }\n }\n this.emit({ type: 'ambiguous', scope, key });\n return { status: 'ambiguous', ok: false, key, entry: claimed.entry };\n }\n\n case 'acquired':\n return this.runAcquired(scope, key, claimed.entry, effect, classify);\n }\n }\n const entry = (await this.store.read(scope))?.effects[key];\n if (entry && entry.status === 'committed') {\n return { status: 'replayed', ok: true, value: entry.result as T, key, entry };\n }\n if (entry) {\n this.emit({ type: 'in_flight', scope, key });\n return { status: 'in_flight', ok: false, key, entry };\n }\n throw new Error('replyonce: could not claim key after several rounds');\n }\n\n private async runAcquired<T>(\n scope: string,\n key: string,\n entry: EffectRecord,\n effect: Effect<T>,\n classify: (err: unknown) => ErrorClass,\n ): Promise<OnceResult<T>> {\n const { attempt, attemptId, runId } = entry;\n this.emit({ type: 'claimed', scope, key, attempt, runId });\n const remaining = Math.max(1, entry.claimedAt + this.policy.pendingTtlMs - this.now());\n const ctx: EffectContext = {\n key,\n scope,\n intent: entry.intent,\n attempt,\n idempotencyKey: providerIdempotencyKey(scope, key),\n fencingToken: entry.token,\n signal: AbortSignal.timeout(remaining),\n };\n\n let value: T;\n try {\n value = await effect(ctx);\n } catch (error) {\n const cls = safeClassify(classify, error);\n const outcome = cls === 'not_applied' ? 'failed' : 'ambiguous';\n await this.settleWithRetry(scope, {\n key,\n attemptId,\n outcome,\n error: describeError(error),\n now: 0,\n }).catch(() => undefined);\n if (outcome === 'failed') {\n this.emit({ type: 'failed', scope, key, attempt, error });\n return {\n status: 'failed',\n ok: false,\n key,\n attempt,\n error,\n retryable: attempt < this.policy.maxAttempts,\n };\n }\n this.emit({ type: 'ambiguous', scope, key, error });\n const current = (await this.store.read(scope).catch(() => null))?.effects[key] ?? entry;\n return { status: 'ambiguous', ok: false, key, entry: current, error };\n }\n\n let stored: Json | undefined;\n try {\n stored = value === undefined ? null : (JSON.parse(JSON.stringify(value)) as Json);\n } catch {\n stored = null;\n this.emit({ type: 'unserializable_result', scope, key });\n }\n try {\n const settled = await this.settleWithRetry(scope, {\n key,\n attemptId,\n outcome: 'committed',\n result: stored,\n now: 0,\n });\n this.emit({ type: 'committed', scope, key, attempt, runId });\n if (settled.kind === 'stale' || settled.wasAbandoned) {\n this.emit({ type: 'late_settle', scope, key, attempt });\n return { status: 'executed', ok: true, value, key, attempt, lateSettle: true };\n }\n } catch {\n // The effect happened but the ledger could not record it. The claim stays pending and\n // later reads treat it as ambiguous, which fails safe.\n this.emit({ type: 'late_settle', scope, key, attempt });\n return { status: 'executed', ok: true, value, key, attempt, lateSettle: true };\n }\n return { status: 'executed', ok: true, value, key, attempt };\n }\n\n private async settleWithRetry(scope: string, input: logic.SettleInput): Promise<logic.SettleOutcome> {\n let lastError: unknown;\n for (let i = 0; i < 4; i++) {\n try {\n const now = this.now();\n return await this.store.transact(scope, (s) => logic.settle(s, { ...input, now }, this.policy));\n } catch (err) {\n lastError = err;\n await sleep(25 * 4 ** i);\n }\n }\n throw lastError;\n }\n\n private async waitForSettle(scope: string, key: string, until: number, intervalMs: number): Promise<void> {\n while (this.now() < until) {\n await sleep(Math.min(intervalMs, Math.max(1, until - this.now())));\n const e = (await this.store.read(scope))?.effects[key];\n if (!e || logic.effectiveStatus(e, this.now(), this.policy) !== 'pending') return;\n }\n }\n}\n\nexport type RunState = BeginOutcome['state'];\n\n/** A unit of agent work on one scope, holding a fencing token. */\nexport class Run {\n readonly scope: string;\n readonly runId: string;\n /** `new`, `resumed` (same runId seen before), `superseded`, `ended`, or `busy` (exclusive mode). */\n readonly state: RunState;\n /** Monotonic per scope. `null` when the run could not start (`busy`). */\n readonly fencingToken: number | null;\n /** Effects this runId already claimed before (non-empty on redelivery). */\n readonly priorEffects: EffectRecord[];\n readonly busyHolder: { runId: string; token: number; leaseExpiresAt: number } | null;\n private readonly ro: ReplyOnce;\n private readonly leaseTtlMs: number;\n\n constructor(ro: ReplyOnce, scope: string, runId: string, leaseTtlMs: number, outcome: BeginOutcome) {\n this.ro = ro;\n this.scope = scope;\n this.runId = runId;\n this.leaseTtlMs = leaseTtlMs;\n this.state = outcome.state;\n if (outcome.state === 'busy') {\n this.fencingToken = null;\n this.priorEffects = [];\n this.busyHolder = outcome.holder;\n } else {\n this.fencingToken = (outcome.run as RunRecord).token;\n this.priorEffects = outcome.priorEffects;\n this.busyHolder = null;\n }\n }\n\n /** `true` when this run may write: it is new or resumed, not superseded, ended or busy. */\n get canWrite(): boolean {\n return this.state === 'new' || this.state === 'resumed';\n }\n\n /**\n * `true` when a previous delivery of this run already claimed a write that may have\n * reached the outside world (pending, committed or ambiguous).\n */\n get alreadyWrote(): boolean {\n return this.priorEffects.some((e) => e.status !== 'failed');\n }\n\n /** Run `effect` at most once per (scope, intent, payload), fenced to this run. */\n once<T>(intent: string, payload: unknown, effect: Effect<T>, options: RunOnceOptions = {}): Promise<OnceResult<T>> {\n const key = effectKey(intent, payload);\n if (this.fencingToken === null) {\n return Promise.resolve({ status: 'refused', ok: false, key, reason: 'busy' });\n }\n const self = { runId: this.runId, token: this.fencingToken, leaseTtlMs: this.leaseTtlMs };\n return this.ro.execute(this.scope, key, intent, self, effect, options);\n }\n\n check(intent: string, payload?: unknown): Promise<CheckResult> {\n return this.ro.check({ scope: this.scope, intent, payload });\n }\n\n history(): Promise<EffectRecord[]> {\n return this.ro.history(this.scope);\n }\n\n /**\n * Renew the lease. `false` means stop: the run was superseded (by a newer run or a later\n * delivery of this one), cancelled or ended.\n */\n heartbeat(): Promise<boolean> {\n if (this.fencingToken === null) return Promise.resolve(false);\n return this.ro.renewRun(this.scope, this.runId, this.fencingToken, this.leaseTtlMs);\n }\n\n /** Mark the run finished. A no-op if a later delivery of the same run has taken over. */\n end(reason: RunEndReason = 'completed'): Promise<void> {\n if (this.fencingToken === null) return Promise.resolve();\n return this.ro.endRun(this.scope, this.runId, this.fencingToken, reason);\n }\n}\n\nexport function createReplyOnce(options: ReplyOnceOptions): ReplyOnce {\n return new ReplyOnce(options);\n}\n\n/** Return the value of a successful result, or throw a `ReplyOnceError` describing why not. */\nexport function unwrap<T>(result: OnceResult<T>): T {\n if (result.ok) return result.value;\n throw new ReplyOnceError(result);\n}\n\nexport class ReplyOnceError extends Error {\n readonly result: Exclude<OnceResult<unknown>, { ok: true }>;\n constructor(result: Exclude<OnceResult<unknown>, { ok: true }>) {\n const detail = result.status === 'refused' ? `${result.status}: ${result.reason}` : result.status;\n super(`replyonce: effect ${result.key} not executed (${detail})`);\n this.name = 'ReplyOnceError';\n this.result = result;\n }\n}\n\nfunction assertScope(scope: string): void {\n if (typeof scope !== 'string' || scope.length === 0 || scope.length > 512) {\n throw new TypeError('replyonce: scope must be a non-empty string of at most 512 characters');\n }\n}\n\nfunction safeClassify(classify: (err: unknown) => ErrorClass, err: unknown): ErrorClass {\n try {\n return classify(err) === 'not_applied' ? 'not_applied' : 'ambiguous';\n } catch {\n return 'ambiguous';\n }\n}\n\nfunction describeError(err: unknown): string {\n const text = err instanceof Error ? `${err.name}: ${err.message}` : String(err);\n return text.slice(0, 500);\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((r) => setTimeout(r, ms));\n}\n","import { createHash } from 'node:crypto';\n\n/** Deterministic JSON: object keys sorted, `undefined` members dropped. */\nexport function canonicalJson(value: unknown): string {\n return JSON.stringify(normalize(value, new Set()));\n}\n\nfunction normalize(value: unknown, seen: Set<unknown>): unknown {\n if (value === null || value === undefined) return null;\n if (typeof value === 'bigint') return value.toString();\n if (typeof value !== 'object') {\n if (typeof value === 'number' && !Number.isFinite(value)) return String(value);\n if (typeof value === 'function' || typeof value === 'symbol') return null;\n return value;\n }\n if (value instanceof Date) return value.toISOString();\n if (seen.has(value)) throw new TypeError('replyonce: cannot hash a circular payload');\n seen.add(value);\n let out: unknown;\n if (Array.isArray(value)) {\n out = value.map((v) => normalize(v, seen));\n } else {\n const obj: Record<string, unknown> = {};\n for (const k of Object.keys(value as Record<string, unknown>).sort()) {\n const v = (value as Record<string, unknown>)[k];\n if (v === undefined) continue;\n obj[k] = normalize(v, seen);\n }\n out = obj;\n }\n seen.delete(value);\n return out;\n}\n\nexport function sha256Hex(input: string): string {\n return createHash('sha256').update(input).digest('hex');\n}\n\nexport function hashPayload(payload: unknown): string {\n return sha256Hex(canonicalJson(payload)).slice(0, 32);\n}\n\nconst SAFE_INTENT = /^[A-Za-z0-9_.:-]{1,100}$/;\n\n/** Ledger key for an intent plus the business identity of the payload. */\nexport function effectKey(intent: string, payload: unknown): string {\n if (!SAFE_INTENT.test(intent)) {\n throw new TypeError(\n `replyonce: intent must match ${SAFE_INTENT} (got ${JSON.stringify(intent)})`,\n );\n }\n return payload === undefined ? intent : `${intent}:${hashPayload(payload)}`;\n}\n\n/** Stable idempotency key to hand to a provider (Stripe-style `Idempotency-Key`). */\nexport function providerIdempotencyKey(scope: string, key: string): string {\n return `ro_${sha256Hex(`${scope}\\u0000${key}`).slice(0, 40)}`;\n}\n","import type {\n ConflictInfo,\n EffectRecord,\n Json,\n RefusalReason,\n RunEndReason,\n RunMode,\n RunRecord,\n ScopeState,\n TransactResult,\n} from './types.js';\n\n/**\n * The replyonce state machine. Every function here is pure: it takes a scope's state and\n * returns the next state plus a decision. Stores only make each call atomic.\n */\n\nexport interface Policy {\n pendingTtlMs: number;\n maxAttempts: number;\n retentionMs: number;\n}\n\nexport type ConflictMode = 'any' | 'same-intent' | 'none';\n\nexport const SUMMARY_MAX = 2000;\n\nexport function emptyState(): ScopeState {\n return { v: 1, seq: 0, tokenSeq: 0, runs: {}, effects: {} };\n}\n\nfunction load(state: ScopeState | null, now: number, policy: Policy): ScopeState {\n const s = state ? (structuredClone(state) as ScopeState) : emptyState();\n if (s.v !== 1) throw new Error(`replyonce: unsupported state version ${String(s.v)}`);\n const horizon = now - policy.retentionMs;\n for (const [k, e] of Object.entries(s.effects)) {\n if ((e.status === 'committed' || e.status === 'failed') && (e.settledAt ?? e.claimedAt) < horizon) {\n delete s.effects[k];\n }\n }\n for (const [id, r] of Object.entries(s.runs)) {\n if ((r.endedAt ?? r.leaseExpiresAt) < horizon) delete s.runs[id];\n }\n return s;\n}\n\n/** Advance the scope's logical clock for a change being made now. */\nfunction tick(s: ScopeState): number {\n s.seq += 1;\n return s.seq;\n}\n\nexport function isAbandoned(e: EffectRecord, now: number, policy: Policy): boolean {\n return e.status === 'pending' && now - e.claimedAt >= policy.pendingTtlMs;\n}\n\n/** The status a reader should act on: a pending claim past its TTL is treated as ambiguous. */\nexport function effectiveStatus(e: EffectRecord, now: number, policy: Policy): EffectRecord['status'] {\n return isAbandoned(e, now, policy) ? 'ambiguous' : e.status;\n}\n\nfunction isLive(r: RunRecord, now: number): boolean {\n return r.endedAt === null && r.leaseExpiresAt > now;\n}\n\n/**\n * Is `a` newer than `b`? Exclusive leases order by token; otherwise by context recency. Never\n * by token there: a redelivery bumps an old run's token. Equal context times order by when\n * the run first began only if neither run gave a `contextAt`, because then each read its\n * context as it began. Otherwise neither is newer: begin order says nothing about whose\n * context is fresher, and fencing out the wrong one would leave its message unanswered.\n * Both may then write; conflict checks keep them from answering the same thing twice.\n */\nfunction isNewer(a: RunRecord, b: RunRecord): boolean {\n if (a.mode === 'exclusive' && b.mode === 'exclusive') return a.token > b.token;\n if (a.orderAt !== b.orderAt) return a.orderAt > b.orderAt;\n if (a.baseContextAt !== null || b.baseContextAt !== null) return false;\n return a.baseSeq > b.baseSeq;\n}\n\n/**\n * A newer run owns the scope if it is live, or if it already wrote (it may have answered).\n * A newer run that ended without writing, or was cancelled, does not.\n */\nexport function supersedingRun(s: ScopeState, run: RunRecord, now: number): RunRecord | null {\n let best: RunRecord | null = null;\n for (const r of Object.values(s.runs)) {\n if (r.runId === run.runId || r.endReason === 'cancelled' || !isNewer(r, run)) continue;\n if (r.wrote || isLive(r, now)) {\n if (!best || isNewer(r, best)) best = r;\n }\n }\n return best;\n}\n\nfunction markSeen(run: RunRecord, seq: number, now: number): void {\n run.seenSeq = Math.max(run.seenSeq, seq);\n run.seenAt = Math.max(run.seenAt, now);\n run.contextAt = null;\n}\n\n// ---------------------------------------------------------------- begin\n\nexport interface BeginInput {\n runId: string;\n mode: RunMode;\n leaseTtlMs: number;\n now: number;\n /** When the run's view of the scope was read. Writes that landed after it are conflicts. */\n contextAt?: number;\n}\n\nexport type BeginOutcome =\n | { state: 'new' | 'resumed' | 'superseded' | 'ended'; run: RunRecord; priorEffects: EffectRecord[] }\n | { state: 'busy'; holder: { runId: string; token: number; leaseExpiresAt: number } };\n\nexport function begin(\n state: ScopeState | null,\n input: BeginInput,\n policy: Policy,\n): TransactResult<BeginOutcome> {\n const { now } = input;\n const s = load(state, now, policy);\n const existing = s.runs[input.runId];\n if (existing) {\n const priorEffects = Object.values(s.effects).filter((e) => e.runId === input.runId);\n if (existing.endedAt !== null) {\n return { next: null, result: { state: 'ended', run: existing, priorEffects } };\n }\n existing.leaseExpiresAt = Math.max(existing.leaseExpiresAt, now + input.leaseTtlMs);\n tick(s);\n // A redelivery takes the run over; any earlier delivery still executing is fenced out.\n s.tokenSeq += 1;\n existing.token = s.tokenSeq;\n // It acts on the run's original context, so it has not seen what earlier deliveries saw,\n // unless it says it re-read the scope.\n existing.seenSeq = existing.baseSeq;\n existing.seenAt = existing.baseContextAt ?? existing.startedAt;\n existing.contextAt = existing.baseContextAt;\n if (input.contextAt !== undefined && input.contextAt > existing.seenAt) {\n const fresh = Math.min(input.contextAt, now);\n for (const e of Object.values(s.effects)) {\n if (e.settleSeq !== null && e.settledAt !== null && e.settledAt < fresh) {\n existing.seenSeq = Math.max(existing.seenSeq, e.settleSeq);\n }\n }\n existing.seenAt = fresh;\n existing.contextAt = fresh;\n }\n const newer = supersedingRun(s, existing, now);\n return { next: s, result: { state: newer ? 'superseded' : 'resumed', run: existing, priorEffects } };\n }\n if (input.mode === 'exclusive') {\n let holder: RunRecord | undefined;\n for (const r of Object.values(s.runs)) {\n if (isLive(r, now) && (!holder || r.token > holder.token)) holder = r;\n }\n if (holder) {\n return {\n next: null,\n result: {\n state: 'busy',\n holder: { runId: holder.runId, token: holder.token, leaseExpiresAt: holder.leaseExpiresAt },\n },\n };\n }\n }\n const seq = tick(s);\n s.tokenSeq += 1;\n const contextAt = input.contextAt === undefined ? null : Math.min(input.contextAt, now);\n const run: RunRecord = {\n runId: input.runId,\n token: s.tokenSeq,\n mode: input.mode,\n startedAt: now,\n orderAt: input.mode === 'exclusive' ? now : (contextAt ?? now),\n leaseExpiresAt: now + input.leaseTtlMs,\n seenSeq: seq,\n seenAt: contextAt ?? now,\n contextAt,\n baseSeq: seq,\n baseContextAt: contextAt,\n wrote: false,\n endedAt: null,\n endReason: null,\n };\n s.runs[input.runId] = run;\n const newer = supersedingRun(s, run, now);\n return { next: s, result: { state: newer ? 'superseded' : 'new', run, priorEffects: [] } };\n}\n\n// ---------------------------------------------------------------- heartbeat / end\n\nexport type RenewOutcome =\n | { ok: true; run: RunRecord }\n | { ok: false; reason: 'unknown_run' | 'run_ended' | 'run_cancelled' | 'superseded' };\n\nexport function renew(\n state: ScopeState | null,\n input: { runId: string; token: number; leaseTtlMs: number; now: number },\n policy: Policy,\n): TransactResult<RenewOutcome> {\n const s = load(state, input.now, policy);\n const r = s.runs[input.runId];\n if (!r) return { next: null, result: { ok: false, reason: 'unknown_run' } };\n if (r.token !== input.token) return { next: null, result: { ok: false, reason: 'superseded' } };\n if (r.endReason === 'cancelled') return { next: null, result: { ok: false, reason: 'run_cancelled' } };\n if (r.endedAt !== null) return { next: null, result: { ok: false, reason: 'run_ended' } };\n if (supersedingRun(s, r, input.now)) return { next: null, result: { ok: false, reason: 'superseded' } };\n r.leaseExpiresAt = Math.max(r.leaseExpiresAt, input.now + input.leaseTtlMs);\n tick(s);\n return { next: s, result: { ok: true, run: r } };\n}\n\nexport function endRun(\n state: ScopeState | null,\n input: { runId: string; reason: RunEndReason; now: number; /** Ignore if a later delivery holds the run. */ token?: number },\n policy: Policy,\n): TransactResult<RunRecord | null> {\n const s = load(state, input.now, policy);\n const r = s.runs[input.runId];\n if (!r) return { next: null, result: null };\n if (input.token !== undefined && r.token !== input.token) return { next: null, result: null };\n if (r.endedAt !== null && !(input.reason === 'cancelled' && r.endReason !== 'cancelled')) {\n return { next: null, result: r };\n }\n r.endedAt = r.endedAt ?? input.now;\n r.endReason = input.reason;\n tick(s);\n return { next: s, result: r };\n}\n\n// ---------------------------------------------------------------- claim\n\nexport interface ClaimInput {\n key: string;\n intent: string;\n summary: string | null;\n runId: string | null;\n /** The claiming delivery's fencing token. A stale token is refused as superseded. */\n token: number | null;\n attemptId: string;\n conflict: ConflictMode;\n ack: boolean;\n leaseTtlMs: number;\n now: number;\n}\n\nexport type ClaimOutcome =\n | { kind: 'acquired'; entry: EffectRecord }\n | { kind: 'replay'; entry: EffectRecord }\n | { kind: 'in_flight'; entry: EffectRecord }\n | { kind: 'ambiguous'; entry: EffectRecord }\n | { kind: 'refused'; reason: RefusalReason; conflicts?: ConflictInfo[]; entry?: EffectRecord };\n\n/** Writes by others on the scope that `run` has not seen yet, or that may still land. */\nexport function conflictsFor(\n s: ScopeState,\n run: RunRecord,\n key: string,\n intent: string,\n mode: ConflictMode,\n now: number,\n policy: Policy,\n): ConflictInfo[] {\n if (mode === 'none') return [];\n const out: ConflictInfo[] = [];\n for (const e of Object.values(s.effects)) {\n if (e.key === key) continue;\n if (e.runId === run.runId) {\n // This delivery knows its own writes. An earlier delivery's writes matter only when\n // this one is about to do the same kind of thing differently (a reply worded anew).\n if (e.token === run.token || e.intent !== intent) continue;\n }\n if (mode === 'same-intent' && e.intent !== intent) continue;\n const abandoned = isAbandoned(e, now, policy);\n let conflict = false;\n if (e.status === 'pending' && !abandoned) {\n conflict = true;\n } else if (abandoned) {\n // It may have landed at any time up to when it became abandoned.\n conflict = e.claimedAt + policy.pendingTtlMs >= run.seenAt;\n } else if (e.status === 'committed' || e.status === 'ambiguous') {\n conflict =\n (e.settleSeq ?? 0) > run.seenSeq || (run.contextAt !== null && (e.settledAt ?? 0) >= run.contextAt);\n }\n if (conflict) {\n out.push({\n key: e.key,\n intent: e.intent,\n status: abandoned ? 'ambiguous' : e.status,\n runId: e.runId,\n summary: e.summary,\n at: e.settledAt ?? e.claimedAt,\n });\n }\n }\n return out.sort((a, b) => a.at - b.at).slice(0, 10);\n}\n\nexport function claim(\n state: ScopeState | null,\n scope: string,\n input: ClaimInput,\n policy: Policy,\n): TransactResult<ClaimOutcome> {\n const { now } = input;\n const s = load(state, now, policy);\n const prior = s.effects[input.key];\n\n if (prior) {\n if (prior.status === 'committed') return { next: null, result: { kind: 'replay', entry: prior } };\n if (prior.status === 'pending') {\n if (!isAbandoned(prior, now, policy)) {\n return { next: null, result: { kind: 'in_flight', entry: prior } };\n }\n const seq = tick(s);\n prior.status = 'ambiguous';\n prior.settledAt = prior.claimedAt + policy.pendingTtlMs;\n prior.settleSeq = seq;\n prior.error = prior.error ?? 'abandoned: not settled within pendingTtlMs';\n return { next: s, result: { kind: 'ambiguous', entry: prior } };\n }\n if (prior.status === 'ambiguous') return { next: null, result: { kind: 'ambiguous', entry: prior } };\n if (prior.attempt >= policy.maxAttempts) {\n return { next: null, result: { kind: 'refused', reason: 'exhausted', entry: prior } };\n }\n }\n\n let run: RunRecord | undefined;\n if (input.runId !== null) {\n run = s.runs[input.runId];\n if (!run) return { next: null, result: { kind: 'refused', reason: 'unknown_run' } };\n if (run.endReason === 'cancelled') return { next: null, result: { kind: 'refused', reason: 'run_cancelled' } };\n if (run.endedAt !== null) return { next: null, result: { kind: 'refused', reason: 'run_ended' } };\n if ((input.token !== null && run.token !== input.token) || supersedingRun(s, run, now)) {\n return { next: null, result: { kind: 'refused', reason: 'superseded' } };\n }\n\n const conflicts = conflictsFor(s, run, input.key, input.intent, input.conflict, now, policy);\n if (conflicts.length > 0) {\n if (!input.ack) {\n return { next: null, result: { kind: 'refused', reason: 'conversation_changed', conflicts } };\n }\n markSeen(run, tick(s), now);\n return { next: s, result: { kind: 'refused', reason: 'conversation_changed', conflicts } };\n }\n }\n\n const seq = tick(s);\n const entry: EffectRecord = {\n key: input.key,\n scope,\n intent: input.intent,\n status: 'pending',\n attempt: (prior?.attempt ?? 0) + 1,\n attemptId: input.attemptId,\n runId: input.runId,\n token: run?.token ?? null,\n summary: input.summary === null ? null : input.summary.slice(0, SUMMARY_MAX),\n error: null,\n createdAt: prior?.createdAt ?? now,\n claimedAt: now,\n settledAt: null,\n claimSeq: seq,\n settleSeq: null,\n };\n s.effects[input.key] = entry;\n if (run) {\n // A claim does not mark anything seen: writes this check deliberately skipped (other\n // intents, or `none`) must still be reported to a later check that covers them.\n run.wrote = true;\n run.leaseExpiresAt = Math.max(run.leaseExpiresAt, now + input.leaseTtlMs);\n }\n return { next: s, result: { kind: 'acquired', entry } };\n}\n\n// ---------------------------------------------------------------- settle / resolve\n\nexport interface SettleInput {\n key: string;\n attemptId: string;\n outcome: 'committed' | 'failed' | 'ambiguous';\n result?: Json;\n error?: string | null;\n now: number;\n}\n\nexport type SettleOutcome =\n | { kind: 'settled'; entry: EffectRecord; wasAbandoned: boolean }\n | { kind: 'stale'; entry: EffectRecord | null };\n\nexport function settle(\n state: ScopeState | null,\n input: SettleInput,\n policy: Policy,\n): TransactResult<SettleOutcome> {\n const s = load(state, input.now, policy);\n const e = s.effects[input.key];\n // A late \"committed\" from the attempt a reconciler judged not applied is still the truth.\n const lateTruth = e?.status === 'failed' && input.outcome === 'committed';\n if (\n !e ||\n e.attemptId !== input.attemptId ||\n (e.status !== 'pending' && e.status !== 'ambiguous' && !lateTruth)\n ) {\n return { next: null, result: { kind: 'stale', entry: e ?? null } };\n }\n const wasAbandoned = e.status !== 'pending' || isAbandoned(e, input.now, policy);\n e.status = input.outcome;\n e.settledAt = input.now;\n e.settleSeq = tick(s);\n e.error = input.error ?? null;\n if (input.outcome === 'committed' && input.result !== undefined) e.result = input.result;\n return { next: s, result: { kind: 'settled', entry: e, wasAbandoned } };\n}\n\nexport interface ResolveInput {\n key: string;\n outcome: 'committed' | 'not_applied';\n result?: Json;\n /** Only resolve if the ledger still holds this attempt. */\n attemptId?: string;\n now: number;\n}\n\n/** Resolve an ambiguous (or abandoned) effect after checking the outside world. */\nexport function resolve(\n state: ScopeState | null,\n input: ResolveInput,\n policy: Policy,\n): TransactResult<EffectRecord | null> {\n const s = load(state, input.now, policy);\n const e = s.effects[input.key];\n if (!e) return { next: null, result: null };\n if (input.attemptId !== undefined && e.attemptId !== input.attemptId) return { next: null, result: null };\n if (effectiveStatus(e, input.now, policy) !== 'ambiguous') return { next: null, result: null };\n e.settledAt = input.now;\n e.settleSeq = tick(s);\n if (input.outcome === 'committed') {\n e.status = 'committed';\n e.error = null;\n if (input.result !== undefined) e.result = input.result;\n } else {\n e.status = 'failed';\n e.error = 'reconciled: not applied';\n }\n return { next: s, result: e };\n}\n","import type { ScopeState, Store, TransactResult } from '../types.js';\n\n/**\n * Single-process store. Atomic because `transact` reads, decides and writes in one\n * synchronous step. State is kept as JSON so it behaves like the shared stores.\n * Use it for tests, demos and single-instance deployments; it is lost on restart.\n */\nexport class MemoryStore implements Store {\n private readonly data = new Map<string, string>();\n\n async transact<T>(scope: string, fn: (state: ScopeState | null) => TransactResult<T>): Promise<T> {\n const raw = this.data.get(scope);\n const { next, result } = fn(raw === undefined ? null : (JSON.parse(raw) as ScopeState));\n if (next) this.data.set(scope, JSON.stringify(next));\n return result;\n }\n\n async read(scope: string): Promise<ScopeState | null> {\n const raw = this.data.get(scope);\n return raw === undefined ? null : (JSON.parse(raw) as ScopeState);\n }\n\n /** Number of scopes held. */\n get size(): number {\n return this.data.size;\n }\n\n clear(): void {\n this.data.clear();\n }\n}\n"],"mappings":"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","names":["newer","seq"]}
1
+ {"version":3,"sources":["../src/replyonce.ts","../src/key.ts","../src/logic.ts","../src/stores/memory.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto';\nimport { effectKey, providerIdempotencyKey } from './key.js';\nimport * as logic from './logic.js';\nimport type { BeginOutcome, ClaimOutcome, ConflictMode, Policy } from './logic.js';\nimport type {\n EffectContext,\n EffectRecord,\n EffectStatus,\n ErrorClass,\n Json,\n OnceResult,\n ReconcileVerdict,\n ReplyOnceEvent,\n RunEndReason,\n RunMode,\n RunRecord,\n Store,\n} from './types.js';\n\n/** Throw this (or anything with `replyonce: 'not_applied'`) when the effect definitely did not happen. */\nexport class NotApplied extends Error {\n readonly replyonce = 'not_applied' as const;\n constructor(message = 'effect not applied', options?: { cause?: unknown }) {\n super(message);\n this.name = 'NotApplied';\n if (options && 'cause' in options) (this as { cause?: unknown }).cause = options.cause;\n }\n}\n\nexport function defaultClassifyError(err: unknown): ErrorClass {\n if (err instanceof NotApplied) return 'not_applied';\n if (err && typeof err === 'object' && (err as { replyonce?: unknown }).replyonce === 'not_applied') {\n return 'not_applied';\n }\n return 'ambiguous';\n}\n\nexport interface KeyParts {\n /** Usually the conversation or thread id. All fencing happens within a scope. */\n scope: string;\n /** What the effect is: `reply`, `refund`, `create_ticket`. */\n intent: string;\n /**\n * The business identity of the effect, hashed into the key. Use what makes it the same\n * action (the inbound message being answered, the order being refunded), never\n * model-generated text or a model tool-call id.\n */\n payload?: unknown;\n}\n\nexport type Reconciler = (entry: EffectRecord) => Promise<ReconcileVerdict>;\n\nexport interface ReplyOnceOptions {\n store: Store;\n /** How long a claim may stay pending before it is treated as abandoned (ambiguous). Default 15 min. */\n pendingTtlMs?: number;\n /** Attempts per key, counting only attempts known not to have applied. Default 3. */\n maxAttempts?: number;\n /** How long settled effects and ended runs are remembered. Default 7 days. */\n retentionMs?: number;\n /** Run lease length; renewed on every claim and heartbeat. Default 60 s. */\n leaseTtlMs?: number;\n /** Decide whether a thrown error means \"did not happen\" or \"might have happened\". */\n classifyError?: (err: unknown) => ErrorClass;\n /** Ask the outside world what happened to an ambiguous effect. */\n reconcile?: Reconciler;\n onEvent?: (event: ReplyOnceEvent) => void;\n clock?: () => number;\n /** Wait between settle retries and `wait` polls. Pass with `clock` to run on simulated time. */\n sleep?: (ms: number) => Promise<void>;\n}\n\nexport interface OnceOptions {\n /** Short human-readable description, shown to other runs in conflict reports. */\n summary?: string;\n classifyError?: (err: unknown) => ErrorClass;\n reconcile?: Reconciler;\n /** Wait for an in-flight duplicate to settle instead of returning `in_flight` immediately. */\n wait?: { timeoutMs: number; intervalMs?: number };\n}\n\nexport interface RunOnceOptions extends OnceOptions {\n /**\n * `any` (default): refuse if another run wrote anything on this scope since this run last\n * looked. `same-intent`: only effects with the same intent conflict. `none`: per-key\n * idempotency and supersession only.\n */\n conflict?: ConflictMode;\n /** Record that this run has now seen the conflicts, so its next attempt can proceed. Default true. */\n ack?: boolean;\n}\n\nexport interface CheckResult {\n /** `true` only when the effect is known to have happened. */\n done: boolean;\n status: EffectStatus | 'none';\n entry: EffectRecord | null;\n}\n\nexport interface BeginOptions {\n /** Stable id for this unit of work. Reuse it on redelivery (for example the queue job id). */\n runId?: string;\n mode?: RunMode;\n leaseTtlMs?: number;\n /**\n * Epoch ms when this run's context (the conversation it will act on) was read, for\n * example when the webhook payload was captured. Writes by other runs that landed\n * after this point are reported as `conversation_changed`. Default: now.\n */\n contextAt?: number;\n}\n\nexport type Effect<T> = (ctx: EffectContext) => Promise<T> | T;\n\nconst MAX_ROUNDS = 5;\n\nexport class ReplyOnce {\n readonly store: Store;\n readonly policy: Policy;\n readonly leaseTtlMs: number;\n private readonly classify: (err: unknown) => ErrorClass;\n private readonly reconciler: Reconciler | undefined;\n private readonly emit: (event: ReplyOnceEvent) => void;\n private readonly now: () => number;\n private readonly sleep: (ms: number) => Promise<void>;\n\n constructor(options: ReplyOnceOptions) {\n if (!options || !options.store) throw new TypeError('replyonce: a store is required');\n this.store = options.store;\n this.policy = {\n pendingTtlMs: options.pendingTtlMs ?? 15 * 60_000,\n maxAttempts: options.maxAttempts ?? 3,\n retentionMs: options.retentionMs ?? 7 * 24 * 3_600_000,\n };\n if (this.policy.pendingTtlMs <= 0 || this.policy.maxAttempts < 1 || this.policy.retentionMs <= 0) {\n throw new RangeError('replyonce: pendingTtlMs, maxAttempts and retentionMs must be positive');\n }\n this.leaseTtlMs = options.leaseTtlMs ?? 60_000;\n this.classify = options.classifyError ?? defaultClassifyError;\n this.reconciler = options.reconcile;\n const onEvent = options.onEvent;\n this.emit = onEvent\n ? (e) => {\n try {\n onEvent(e);\n } catch {\n // an observer must never change the outcome\n }\n }\n : () => {};\n this.now = options.clock ?? Date.now;\n this.sleep = options.sleep ?? sleep;\n }\n\n /** Resolve key parts to the ledger key used for them. */\n key(parts: KeyParts): string {\n return effectKey(parts.intent, parts.payload);\n }\n\n /**\n * Run `effect` at most once for this key. A string key is its own scope; key parts give\n * a scope (conversation) plus an intent and business identity.\n */\n once<T>(key: string | KeyParts, effect: Effect<T>, options: OnceOptions = {}): Promise<OnceResult<T>> {\n const { scope, ledgerKey, intent } = this.resolveKey(key);\n return this.execute(scope, ledgerKey, intent, null, effect, { ...options, conflict: 'none', ack: false });\n }\n\n /** \"Was this already done?\" — safe to expose to a model as a read-only tool. */\n async check(key: string | KeyParts): Promise<CheckResult> {\n const { scope, ledgerKey } = this.resolveKey(key);\n const state = await this.store.read(scope);\n const entry = state?.effects[ledgerKey] ?? null;\n if (!entry) return { done: false, status: 'none', entry: null };\n const status = logic.effectiveStatus(entry, this.now(), this.policy);\n return { done: status === 'committed', status, entry };\n }\n\n /** Every effect recorded on a scope, oldest first. */\n async history(scope: string): Promise<EffectRecord[]> {\n const state = await this.store.read(scope);\n if (!state) return [];\n const now = this.now();\n return Object.values(state.effects)\n .map((e) => ({ ...e, status: logic.effectiveStatus(e, now, this.policy) }))\n .sort((a, b) => a.createdAt - b.createdAt);\n }\n\n /** Record what an operator or reconciler found out about an ambiguous effect. */\n async resolve(\n key: string | KeyParts,\n verdict: { outcome: 'committed'; result?: Json } | { outcome: 'not_applied' },\n ): Promise<EffectRecord | null> {\n const { scope, ledgerKey } = this.resolveKey(key);\n const result = 'result' in verdict ? verdict.result : undefined;\n const now = this.now();\n const entry = await this.store.transact(scope, (s) =>\n logic.resolve(s, { key: ledgerKey, outcome: verdict.outcome, result, now }, this.policy),\n );\n if (entry) this.emit({ type: 'reconciled', scope, key: ledgerKey, outcome: verdict.outcome });\n return entry;\n }\n\n /**\n * Start (or, with the same `runId`, resume) a unit of work on a scope. The run gets a\n * fencing token; its writes are refused once a newer run owns the scope.\n */\n async begin(scope: string, options: BeginOptions = {}): Promise<Run> {\n assertScope(scope);\n const runId = options.runId ?? randomUUID();\n const leaseTtlMs = options.leaseTtlMs ?? this.leaseTtlMs;\n const now = this.now();\n const outcome = await this.store.transact(scope, (s) =>\n logic.begin(\n s,\n {\n runId,\n mode: options.mode ?? 'supersede',\n leaseTtlMs,\n now,\n ...(options.contextAt !== undefined ? { contextAt: options.contextAt } : {}),\n },\n this.policy,\n ),\n );\n return new Run(this, scope, runId, leaseTtlMs, outcome);\n }\n\n /** Cancel a run: its future writes are refused with `run_cancelled`. */\n async cancel(scope: string, runId: string): Promise<boolean> {\n const now = this.now();\n const r = await this.store.transact(scope, (s) =>\n logic.endRun(s, { runId, reason: 'cancelled', now }, this.policy),\n );\n return r !== null;\n }\n\n /** @internal */\n async renewRun(scope: string, runId: string, token: number, leaseTtlMs: number): Promise<boolean> {\n const now = this.now();\n const r = await this.store.transact(scope, (s) =>\n logic.renew(s, { runId, token, leaseTtlMs, now }, this.policy),\n );\n return r.ok;\n }\n\n /** @internal */\n async endRun(scope: string, runId: string, token: number, reason: RunEndReason): Promise<void> {\n const now = this.now();\n await this.store.transact(scope, (s) => logic.endRun(s, { runId, token, reason, now }, this.policy));\n }\n\n /** @internal */\n resolveKey(key: string | KeyParts): { scope: string; ledgerKey: string; intent: string } {\n if (typeof key === 'string') {\n assertScope(key);\n return { scope: key, ledgerKey: key, intent: 'once' };\n }\n assertScope(key.scope);\n return { scope: key.scope, ledgerKey: effectKey(key.intent, key.payload), intent: key.intent };\n }\n\n /** @internal */\n async execute<T>(\n scope: string,\n key: string,\n intent: string,\n run: { runId: string; token: number; leaseTtlMs: number } | null,\n effect: Effect<T>,\n options: RunOnceOptions,\n ): Promise<OnceResult<T>> {\n const runId = run?.runId ?? null;\n const token = run?.token ?? null;\n const classify = options.classifyError ?? this.classify;\n const reconcile = options.reconcile ?? this.reconciler;\n const waitUntil = options.wait ? this.now() + options.wait.timeoutMs : 0;\n let reconciledOnce = false;\n\n for (let round = 0; round < MAX_ROUNDS; round++) {\n const attemptId = randomUUID();\n const now = this.now();\n const claimed: ClaimOutcome = await this.store.transact(scope, (s) =>\n logic.claim(\n s,\n scope,\n {\n key,\n intent,\n summary: options.summary ?? null,\n runId,\n token,\n attemptId,\n conflict: options.conflict ?? 'any',\n ack: options.ack ?? true,\n leaseTtlMs: run?.leaseTtlMs ?? this.leaseTtlMs,\n now,\n },\n this.policy,\n ),\n );\n\n switch (claimed.kind) {\n case 'replay':\n this.emit({ type: 'replayed', scope, key });\n return { status: 'replayed', ok: true, value: claimed.entry.result as T, key, entry: claimed.entry };\n\n case 'refused':\n this.emit({ type: 'refused', scope, key, reason: claimed.reason, runId });\n return {\n status: 'refused',\n ok: false,\n key,\n reason: claimed.reason,\n ...(claimed.conflicts ? { conflicts: claimed.conflicts } : {}),\n ...(claimed.entry ? { entry: claimed.entry } : {}),\n };\n\n case 'in_flight': {\n if (options.wait && this.now() < waitUntil) {\n await this.waitForSettle(scope, key, waitUntil, options.wait.intervalMs ?? 100);\n continue;\n }\n this.emit({ type: 'in_flight', scope, key });\n return { status: 'in_flight', ok: false, key, entry: claimed.entry };\n }\n\n case 'ambiguous': {\n if (reconcile && !reconciledOnce) {\n reconciledOnce = true;\n let verdict: ReconcileVerdict;\n try {\n verdict = await reconcile(claimed.entry);\n } catch {\n verdict = { outcome: 'unknown' };\n }\n if (verdict.outcome !== 'unknown') {\n const at = this.now();\n const resolved = await this.store.transact(scope, (s) =>\n logic.resolve(\n s,\n {\n key,\n outcome: verdict.outcome as 'committed' | 'not_applied',\n result: verdict.outcome === 'committed' ? verdict.result : undefined,\n attemptId: claimed.entry.attemptId,\n now: at,\n },\n this.policy,\n ),\n );\n if (resolved) this.emit({ type: 'reconciled', scope, key, outcome: verdict.outcome });\n continue;\n }\n }\n this.emit({ type: 'ambiguous', scope, key });\n return { status: 'ambiguous', ok: false, key, entry: claimed.entry };\n }\n\n case 'acquired':\n return this.runAcquired(scope, key, claimed.entry, effect, classify);\n }\n }\n const entry = (await this.store.read(scope))?.effects[key];\n if (entry && entry.status === 'committed') {\n return { status: 'replayed', ok: true, value: entry.result as T, key, entry };\n }\n if (entry) {\n this.emit({ type: 'in_flight', scope, key });\n return { status: 'in_flight', ok: false, key, entry };\n }\n throw new Error('replyonce: could not claim key after several rounds');\n }\n\n private async runAcquired<T>(\n scope: string,\n key: string,\n entry: EffectRecord,\n effect: Effect<T>,\n classify: (err: unknown) => ErrorClass,\n ): Promise<OnceResult<T>> {\n const { attempt, attemptId, runId } = entry;\n this.emit({ type: 'claimed', scope, key, attempt, runId });\n const remaining = Math.max(1, entry.claimedAt + this.policy.pendingTtlMs - this.now());\n const ctx: EffectContext = {\n key,\n scope,\n intent: entry.intent,\n attempt,\n idempotencyKey: providerIdempotencyKey(scope, key),\n fencingToken: entry.token,\n signal: AbortSignal.timeout(remaining),\n };\n\n let value: T;\n try {\n value = await effect(ctx);\n } catch (error) {\n const cls = safeClassify(classify, error);\n const outcome = cls === 'not_applied' ? 'failed' : 'ambiguous';\n await this.settleWithRetry(scope, {\n key,\n attemptId,\n outcome,\n error: describeError(error),\n now: 0,\n }).catch(() => undefined);\n if (outcome === 'failed') {\n this.emit({ type: 'failed', scope, key, attempt, error });\n return {\n status: 'failed',\n ok: false,\n key,\n attempt,\n error,\n retryable: attempt < this.policy.maxAttempts,\n };\n }\n this.emit({ type: 'ambiguous', scope, key, error });\n const current = (await this.store.read(scope).catch(() => null))?.effects[key] ?? entry;\n return { status: 'ambiguous', ok: false, key, entry: current, error };\n }\n\n let stored: Json | undefined;\n try {\n stored = value === undefined ? null : (JSON.parse(JSON.stringify(value)) as Json);\n } catch {\n stored = null;\n this.emit({ type: 'unserializable_result', scope, key });\n }\n try {\n const settled = await this.settleWithRetry(scope, {\n key,\n attemptId,\n outcome: 'committed',\n result: stored,\n now: 0,\n });\n this.emit({ type: 'committed', scope, key, attempt, runId });\n if (settled.kind === 'stale' || settled.wasAbandoned) {\n this.emit({ type: 'late_settle', scope, key, attempt });\n return { status: 'executed', ok: true, value, key, attempt, lateSettle: true };\n }\n } catch {\n // The effect happened but the ledger could not record it. The claim stays pending and\n // later reads treat it as ambiguous, which fails safe.\n this.emit({ type: 'late_settle', scope, key, attempt });\n return { status: 'executed', ok: true, value, key, attempt, lateSettle: true };\n }\n return { status: 'executed', ok: true, value, key, attempt };\n }\n\n private async settleWithRetry(scope: string, input: logic.SettleInput): Promise<logic.SettleOutcome> {\n let lastError: unknown;\n for (let i = 0; i < 4; i++) {\n try {\n const now = this.now();\n return await this.store.transact(scope, (s) => logic.settle(s, { ...input, now }, this.policy));\n } catch (err) {\n lastError = err;\n await this.sleep(25 * 4 ** i);\n }\n }\n throw lastError;\n }\n\n private async waitForSettle(scope: string, key: string, until: number, intervalMs: number): Promise<void> {\n while (this.now() < until) {\n await this.sleep(Math.min(intervalMs, Math.max(1, until - this.now())));\n const e = (await this.store.read(scope))?.effects[key];\n if (!e || logic.effectiveStatus(e, this.now(), this.policy) !== 'pending') return;\n }\n }\n}\n\nexport type RunState = BeginOutcome['state'];\n\n/** A unit of agent work on one scope, holding a fencing token. */\nexport class Run {\n readonly scope: string;\n readonly runId: string;\n /** `new`, `resumed` (same runId seen before), `superseded`, `ended`, or `busy` (exclusive mode). */\n readonly state: RunState;\n /** Monotonic per scope. `null` when the run could not start (`busy`). */\n readonly fencingToken: number | null;\n /** Effects this runId already claimed before (non-empty on redelivery). */\n readonly priorEffects: EffectRecord[];\n readonly busyHolder: { runId: string; token: number; leaseExpiresAt: number } | null;\n private readonly ro: ReplyOnce;\n private readonly leaseTtlMs: number;\n\n constructor(ro: ReplyOnce, scope: string, runId: string, leaseTtlMs: number, outcome: BeginOutcome) {\n this.ro = ro;\n this.scope = scope;\n this.runId = runId;\n this.leaseTtlMs = leaseTtlMs;\n this.state = outcome.state;\n if (outcome.state === 'busy') {\n this.fencingToken = null;\n this.priorEffects = [];\n this.busyHolder = outcome.holder;\n } else {\n this.fencingToken = (outcome.run as RunRecord).token;\n this.priorEffects = outcome.priorEffects;\n this.busyHolder = null;\n }\n }\n\n /** `true` when this run may write: it is new or resumed, not superseded, ended or busy. */\n get canWrite(): boolean {\n return this.state === 'new' || this.state === 'resumed';\n }\n\n /**\n * `true` when a previous delivery of this run already claimed a write that may have\n * reached the outside world (pending, committed or ambiguous).\n */\n get alreadyWrote(): boolean {\n return this.priorEffects.some((e) => e.status !== 'failed');\n }\n\n /** Run `effect` at most once per (scope, intent, payload), fenced to this run. */\n once<T>(intent: string, payload: unknown, effect: Effect<T>, options: RunOnceOptions = {}): Promise<OnceResult<T>> {\n const key = effectKey(intent, payload);\n if (this.fencingToken === null) {\n return Promise.resolve({ status: 'refused', ok: false, key, reason: 'busy' });\n }\n const self = { runId: this.runId, token: this.fencingToken, leaseTtlMs: this.leaseTtlMs };\n return this.ro.execute(this.scope, key, intent, self, effect, options);\n }\n\n check(intent: string, payload?: unknown): Promise<CheckResult> {\n return this.ro.check({ scope: this.scope, intent, payload });\n }\n\n history(): Promise<EffectRecord[]> {\n return this.ro.history(this.scope);\n }\n\n /**\n * Renew the lease. `false` means stop: the run was superseded (by a newer run or a later\n * delivery of this one), cancelled or ended.\n */\n heartbeat(): Promise<boolean> {\n if (this.fencingToken === null) return Promise.resolve(false);\n return this.ro.renewRun(this.scope, this.runId, this.fencingToken, this.leaseTtlMs);\n }\n\n /** Mark the run finished. A no-op if a later delivery of the same run has taken over. */\n end(reason: RunEndReason = 'completed'): Promise<void> {\n if (this.fencingToken === null) return Promise.resolve();\n return this.ro.endRun(this.scope, this.runId, this.fencingToken, reason);\n }\n}\n\nexport function createReplyOnce(options: ReplyOnceOptions): ReplyOnce {\n return new ReplyOnce(options);\n}\n\n/** Return the value of a successful result, or throw a `ReplyOnceError` describing why not. */\nexport function unwrap<T>(result: OnceResult<T>): T {\n if (result.ok) return result.value;\n throw new ReplyOnceError(result);\n}\n\nexport class ReplyOnceError extends Error {\n readonly result: Exclude<OnceResult<unknown>, { ok: true }>;\n constructor(result: Exclude<OnceResult<unknown>, { ok: true }>) {\n const detail = result.status === 'refused' ? `${result.status}: ${result.reason}` : result.status;\n super(`replyonce: effect ${result.key} not executed (${detail})`);\n this.name = 'ReplyOnceError';\n this.result = result;\n }\n}\n\nfunction assertScope(scope: string): void {\n if (typeof scope !== 'string' || scope.length === 0 || scope.length > 512) {\n throw new TypeError('replyonce: scope must be a non-empty string of at most 512 characters');\n }\n}\n\nfunction safeClassify(classify: (err: unknown) => ErrorClass, err: unknown): ErrorClass {\n try {\n return classify(err) === 'not_applied' ? 'not_applied' : 'ambiguous';\n } catch {\n return 'ambiguous';\n }\n}\n\nfunction describeError(err: unknown): string {\n const text = err instanceof Error ? `${err.name}: ${err.message}` : String(err);\n return text.slice(0, 500);\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((r) => setTimeout(r, ms));\n}\n","import { createHash } from 'node:crypto';\n\n/** Deterministic JSON: object keys sorted, `undefined` members dropped. */\nexport function canonicalJson(value: unknown): string {\n return JSON.stringify(normalize(value, new Set()));\n}\n\nfunction normalize(value: unknown, seen: Set<unknown>): unknown {\n if (value === null || value === undefined) return null;\n if (typeof value === 'bigint') return value.toString();\n if (typeof value !== 'object') {\n if (typeof value === 'number' && !Number.isFinite(value)) return String(value);\n if (typeof value === 'function' || typeof value === 'symbol') return null;\n return value;\n }\n if (value instanceof Date) return value.toISOString();\n if (seen.has(value)) throw new TypeError('replyonce: cannot hash a circular payload');\n seen.add(value);\n let out: unknown;\n if (Array.isArray(value)) {\n out = value.map((v) => normalize(v, seen));\n } else {\n const obj: Record<string, unknown> = {};\n for (const k of Object.keys(value as Record<string, unknown>).sort()) {\n const v = (value as Record<string, unknown>)[k];\n if (v === undefined) continue;\n obj[k] = normalize(v, seen);\n }\n out = obj;\n }\n seen.delete(value);\n return out;\n}\n\nexport function sha256Hex(input: string): string {\n return createHash('sha256').update(input).digest('hex');\n}\n\nexport function hashPayload(payload: unknown): string {\n return sha256Hex(canonicalJson(payload)).slice(0, 32);\n}\n\nconst SAFE_INTENT = /^[A-Za-z0-9_.:-]{1,100}$/;\n\n/** Ledger key for an intent plus the business identity of the payload. */\nexport function effectKey(intent: string, payload: unknown): string {\n if (!SAFE_INTENT.test(intent)) {\n throw new TypeError(\n `replyonce: intent must match ${SAFE_INTENT} (got ${JSON.stringify(intent)})`,\n );\n }\n return payload === undefined ? intent : `${intent}:${hashPayload(payload)}`;\n}\n\n/** Stable idempotency key to hand to a provider (Stripe-style `Idempotency-Key`). */\nexport function providerIdempotencyKey(scope: string, key: string): string {\n return `ro_${sha256Hex(`${scope}\\u0000${key}`).slice(0, 40)}`;\n}\n","import type {\n ConflictInfo,\n EffectRecord,\n Json,\n RefusalReason,\n RunEndReason,\n RunMode,\n RunRecord,\n ScopeState,\n TransactResult,\n} from './types.js';\n\n/**\n * The replyonce state machine. Every function here is pure: it takes a scope's state and\n * returns the next state plus a decision. Stores only make each call atomic.\n */\n\nexport interface Policy {\n pendingTtlMs: number;\n maxAttempts: number;\n retentionMs: number;\n}\n\nexport type ConflictMode = 'any' | 'same-intent' | 'none';\n\nexport const SUMMARY_MAX = 2000;\n\nexport function emptyState(): ScopeState {\n return { v: 1, seq: 0, tokenSeq: 0, runs: {}, effects: {} };\n}\n\nfunction load(state: ScopeState | null, now: number, policy: Policy): ScopeState {\n const s = state ? (structuredClone(state) as ScopeState) : emptyState();\n if (s.v !== 1) throw new Error(`replyonce: unsupported state version ${String(s.v)}`);\n const horizon = now - policy.retentionMs;\n for (const [k, e] of Object.entries(s.effects)) {\n if ((e.status === 'committed' || e.status === 'failed') && (e.settledAt ?? e.claimedAt) < horizon) {\n delete s.effects[k];\n }\n }\n for (const [id, r] of Object.entries(s.runs)) {\n if ((r.endedAt ?? r.leaseExpiresAt) < horizon) delete s.runs[id];\n }\n return s;\n}\n\n/** Advance the scope's logical clock for a change being made now. */\nfunction tick(s: ScopeState): number {\n s.seq += 1;\n return s.seq;\n}\n\nexport function isAbandoned(e: EffectRecord, now: number, policy: Policy): boolean {\n return e.status === 'pending' && now - e.claimedAt >= policy.pendingTtlMs;\n}\n\n/** The status a reader should act on: a pending claim past its TTL is treated as ambiguous. */\nexport function effectiveStatus(e: EffectRecord, now: number, policy: Policy): EffectRecord['status'] {\n return isAbandoned(e, now, policy) ? 'ambiguous' : e.status;\n}\n\nfunction isLive(r: RunRecord, now: number): boolean {\n return r.endedAt === null && r.leaseExpiresAt > now;\n}\n\n/**\n * Is `a` newer than `b`? Exclusive leases order by token; otherwise by context recency. Never\n * by token there: a redelivery bumps an old run's token. Equal context times order by when\n * the run first began only if neither run gave a `contextAt`, because then each read its\n * context as it began. Otherwise neither is newer: begin order says nothing about whose\n * context is fresher, and fencing out the wrong one would leave its message unanswered.\n * Both may then write; conflict checks keep them from answering the same thing twice.\n */\nfunction isNewer(a: RunRecord, b: RunRecord): boolean {\n if (a.mode === 'exclusive' && b.mode === 'exclusive') return a.token > b.token;\n if (a.orderAt !== b.orderAt) return a.orderAt > b.orderAt;\n if (a.baseContextAt !== null || b.baseContextAt !== null) return false;\n return a.baseSeq > b.baseSeq;\n}\n\n/**\n * A newer run owns the scope if it is live, or if it already wrote (it may have answered).\n * A newer run that ended without writing, or was cancelled, does not.\n */\nexport function supersedingRun(s: ScopeState, run: RunRecord, now: number): RunRecord | null {\n let best: RunRecord | null = null;\n for (const r of Object.values(s.runs)) {\n if (r.runId === run.runId || r.endReason === 'cancelled' || !isNewer(r, run)) continue;\n if (r.wrote || isLive(r, now)) {\n if (!best || isNewer(r, best)) best = r;\n }\n }\n return best;\n}\n\nfunction markSeen(run: RunRecord, seq: number, now: number): void {\n run.seenSeq = Math.max(run.seenSeq, seq);\n run.seenAt = Math.max(run.seenAt, now);\n run.contextAt = null;\n}\n\n// ---------------------------------------------------------------- begin\n\nexport interface BeginInput {\n runId: string;\n mode: RunMode;\n leaseTtlMs: number;\n now: number;\n /** When the run's view of the scope was read. Writes that landed after it are conflicts. */\n contextAt?: number;\n}\n\nexport type BeginOutcome =\n | { state: 'new' | 'resumed' | 'superseded' | 'ended'; run: RunRecord; priorEffects: EffectRecord[] }\n | { state: 'busy'; holder: { runId: string; token: number; leaseExpiresAt: number } };\n\nexport function begin(\n state: ScopeState | null,\n input: BeginInput,\n policy: Policy,\n): TransactResult<BeginOutcome> {\n const { now } = input;\n const s = load(state, now, policy);\n const existing = s.runs[input.runId];\n if (existing) {\n const priorEffects = Object.values(s.effects).filter((e) => e.runId === input.runId);\n if (existing.endedAt !== null) {\n return { next: null, result: { state: 'ended', run: existing, priorEffects } };\n }\n existing.leaseExpiresAt = Math.max(existing.leaseExpiresAt, now + input.leaseTtlMs);\n tick(s);\n // A redelivery takes the run over; any earlier delivery still executing is fenced out.\n s.tokenSeq += 1;\n existing.token = s.tokenSeq;\n // It acts on the run's original context, so it has not seen what earlier deliveries saw,\n // unless it says it re-read the scope.\n existing.seenSeq = existing.baseSeq;\n existing.seenAt = existing.baseContextAt ?? existing.startedAt;\n existing.contextAt = existing.baseContextAt;\n if (input.contextAt !== undefined && input.contextAt > existing.seenAt) {\n const fresh = Math.min(input.contextAt, now);\n for (const e of Object.values(s.effects)) {\n if (e.settleSeq !== null && e.settledAt !== null && e.settledAt < fresh) {\n existing.seenSeq = Math.max(existing.seenSeq, e.settleSeq);\n }\n }\n existing.seenAt = fresh;\n existing.contextAt = fresh;\n }\n const newer = supersedingRun(s, existing, now);\n return { next: s, result: { state: newer ? 'superseded' : 'resumed', run: existing, priorEffects } };\n }\n if (input.mode === 'exclusive') {\n let holder: RunRecord | undefined;\n for (const r of Object.values(s.runs)) {\n if (isLive(r, now) && (!holder || r.token > holder.token)) holder = r;\n }\n if (holder) {\n return {\n next: null,\n result: {\n state: 'busy',\n holder: { runId: holder.runId, token: holder.token, leaseExpiresAt: holder.leaseExpiresAt },\n },\n };\n }\n }\n const seq = tick(s);\n s.tokenSeq += 1;\n const contextAt = input.contextAt === undefined ? null : Math.min(input.contextAt, now);\n const run: RunRecord = {\n runId: input.runId,\n token: s.tokenSeq,\n mode: input.mode,\n startedAt: now,\n orderAt: input.mode === 'exclusive' ? now : (contextAt ?? now),\n leaseExpiresAt: now + input.leaseTtlMs,\n seenSeq: seq,\n seenAt: contextAt ?? now,\n contextAt,\n baseSeq: seq,\n baseContextAt: contextAt,\n wrote: false,\n endedAt: null,\n endReason: null,\n };\n s.runs[input.runId] = run;\n const newer = supersedingRun(s, run, now);\n return { next: s, result: { state: newer ? 'superseded' : 'new', run, priorEffects: [] } };\n}\n\n// ---------------------------------------------------------------- heartbeat / end\n\nexport type RenewOutcome =\n | { ok: true; run: RunRecord }\n | { ok: false; reason: 'unknown_run' | 'run_ended' | 'run_cancelled' | 'superseded' };\n\nexport function renew(\n state: ScopeState | null,\n input: { runId: string; token: number; leaseTtlMs: number; now: number },\n policy: Policy,\n): TransactResult<RenewOutcome> {\n const s = load(state, input.now, policy);\n const r = s.runs[input.runId];\n if (!r) return { next: null, result: { ok: false, reason: 'unknown_run' } };\n if (r.token !== input.token) return { next: null, result: { ok: false, reason: 'superseded' } };\n if (r.endReason === 'cancelled') return { next: null, result: { ok: false, reason: 'run_cancelled' } };\n if (r.endedAt !== null) return { next: null, result: { ok: false, reason: 'run_ended' } };\n if (supersedingRun(s, r, input.now)) return { next: null, result: { ok: false, reason: 'superseded' } };\n r.leaseExpiresAt = Math.max(r.leaseExpiresAt, input.now + input.leaseTtlMs);\n tick(s);\n return { next: s, result: { ok: true, run: r } };\n}\n\nexport function endRun(\n state: ScopeState | null,\n input: { runId: string; reason: RunEndReason; now: number; /** Ignore if a later delivery holds the run. */ token?: number },\n policy: Policy,\n): TransactResult<RunRecord | null> {\n const s = load(state, input.now, policy);\n const r = s.runs[input.runId];\n if (!r) return { next: null, result: null };\n if (input.token !== undefined && r.token !== input.token) return { next: null, result: null };\n if (r.endedAt !== null && !(input.reason === 'cancelled' && r.endReason !== 'cancelled')) {\n return { next: null, result: r };\n }\n r.endedAt = r.endedAt ?? input.now;\n r.endReason = input.reason;\n tick(s);\n return { next: s, result: r };\n}\n\n// ---------------------------------------------------------------- claim\n\nexport interface ClaimInput {\n key: string;\n intent: string;\n summary: string | null;\n runId: string | null;\n /** The claiming delivery's fencing token. A stale token is refused as superseded. */\n token: number | null;\n attemptId: string;\n conflict: ConflictMode;\n ack: boolean;\n leaseTtlMs: number;\n now: number;\n}\n\nexport type ClaimOutcome =\n | { kind: 'acquired'; entry: EffectRecord }\n | { kind: 'replay'; entry: EffectRecord }\n | { kind: 'in_flight'; entry: EffectRecord }\n | { kind: 'ambiguous'; entry: EffectRecord }\n | { kind: 'refused'; reason: RefusalReason; conflicts?: ConflictInfo[]; entry?: EffectRecord };\n\n/** Writes by others on the scope that `run` has not seen yet, or that may still land. */\nexport function conflictsFor(\n s: ScopeState,\n run: RunRecord,\n key: string,\n intent: string,\n mode: ConflictMode,\n now: number,\n policy: Policy,\n): ConflictInfo[] {\n if (mode === 'none') return [];\n const out: ConflictInfo[] = [];\n for (const e of Object.values(s.effects)) {\n if (e.key === key) continue;\n if (e.runId === run.runId) {\n // This delivery knows its own writes. An earlier delivery's writes matter only when\n // this one is about to do the same kind of thing differently (a reply worded anew).\n if (e.token === run.token || e.intent !== intent) continue;\n }\n if (mode === 'same-intent' && e.intent !== intent) continue;\n const abandoned = isAbandoned(e, now, policy);\n let conflict = false;\n if (e.status === 'pending' && !abandoned) {\n conflict = true;\n } else if (abandoned) {\n // It may have landed at any time up to when it became abandoned.\n conflict = e.claimedAt + policy.pendingTtlMs >= run.seenAt;\n } else if (e.status === 'committed' || e.status === 'ambiguous') {\n conflict =\n (e.settleSeq ?? 0) > run.seenSeq || (run.contextAt !== null && (e.settledAt ?? 0) >= run.contextAt);\n }\n if (conflict) {\n out.push({\n key: e.key,\n intent: e.intent,\n status: abandoned ? 'ambiguous' : e.status,\n runId: e.runId,\n summary: e.summary,\n at: e.settledAt ?? e.claimedAt,\n });\n }\n }\n return out.sort((a, b) => a.at - b.at).slice(0, 10);\n}\n\nexport function claim(\n state: ScopeState | null,\n scope: string,\n input: ClaimInput,\n policy: Policy,\n): TransactResult<ClaimOutcome> {\n const { now } = input;\n const s = load(state, now, policy);\n const prior = s.effects[input.key];\n\n if (prior) {\n if (prior.status === 'committed') return { next: null, result: { kind: 'replay', entry: prior } };\n if (prior.status === 'pending') {\n if (!isAbandoned(prior, now, policy)) {\n return { next: null, result: { kind: 'in_flight', entry: prior } };\n }\n const seq = tick(s);\n prior.status = 'ambiguous';\n prior.settledAt = prior.claimedAt + policy.pendingTtlMs;\n prior.settleSeq = seq;\n prior.error = prior.error ?? 'abandoned: not settled within pendingTtlMs';\n return { next: s, result: { kind: 'ambiguous', entry: prior } };\n }\n if (prior.status === 'ambiguous') return { next: null, result: { kind: 'ambiguous', entry: prior } };\n if (prior.attempt >= policy.maxAttempts) {\n return { next: null, result: { kind: 'refused', reason: 'exhausted', entry: prior } };\n }\n }\n\n let run: RunRecord | undefined;\n if (input.runId !== null) {\n run = s.runs[input.runId];\n if (!run) return { next: null, result: { kind: 'refused', reason: 'unknown_run' } };\n if (run.endReason === 'cancelled') return { next: null, result: { kind: 'refused', reason: 'run_cancelled' } };\n if (run.endedAt !== null) return { next: null, result: { kind: 'refused', reason: 'run_ended' } };\n if ((input.token !== null && run.token !== input.token) || supersedingRun(s, run, now)) {\n return { next: null, result: { kind: 'refused', reason: 'superseded' } };\n }\n\n const conflicts = conflictsFor(s, run, input.key, input.intent, input.conflict, now, policy);\n if (conflicts.length > 0) {\n if (!input.ack) {\n return { next: null, result: { kind: 'refused', reason: 'conversation_changed', conflicts } };\n }\n markSeen(run, tick(s), now);\n return { next: s, result: { kind: 'refused', reason: 'conversation_changed', conflicts } };\n }\n }\n\n const seq = tick(s);\n const entry: EffectRecord = {\n key: input.key,\n scope,\n intent: input.intent,\n status: 'pending',\n attempt: (prior?.attempt ?? 0) + 1,\n attemptId: input.attemptId,\n runId: input.runId,\n token: run?.token ?? null,\n summary: input.summary === null ? null : input.summary.slice(0, SUMMARY_MAX),\n error: null,\n createdAt: prior?.createdAt ?? now,\n claimedAt: now,\n settledAt: null,\n claimSeq: seq,\n settleSeq: null,\n };\n s.effects[input.key] = entry;\n if (run) {\n // A claim does not mark anything seen: writes this check deliberately skipped (other\n // intents, or `none`) must still be reported to a later check that covers them.\n run.wrote = true;\n run.leaseExpiresAt = Math.max(run.leaseExpiresAt, now + input.leaseTtlMs);\n }\n return { next: s, result: { kind: 'acquired', entry } };\n}\n\n// ---------------------------------------------------------------- settle / resolve\n\nexport interface SettleInput {\n key: string;\n attemptId: string;\n outcome: 'committed' | 'failed' | 'ambiguous';\n result?: Json;\n error?: string | null;\n now: number;\n}\n\nexport type SettleOutcome =\n | { kind: 'settled'; entry: EffectRecord; wasAbandoned: boolean }\n | { kind: 'stale'; entry: EffectRecord | null };\n\nexport function settle(\n state: ScopeState | null,\n input: SettleInput,\n policy: Policy,\n): TransactResult<SettleOutcome> {\n const s = load(state, input.now, policy);\n const e = s.effects[input.key];\n // A late \"committed\" from the attempt a reconciler judged not applied is still the truth.\n const lateTruth = e?.status === 'failed' && input.outcome === 'committed';\n if (\n !e ||\n e.attemptId !== input.attemptId ||\n (e.status !== 'pending' && e.status !== 'ambiguous' && !lateTruth)\n ) {\n return { next: null, result: { kind: 'stale', entry: e ?? null } };\n }\n const wasAbandoned = e.status !== 'pending' || isAbandoned(e, input.now, policy);\n e.status = input.outcome;\n e.settledAt = input.now;\n e.settleSeq = tick(s);\n e.error = input.error ?? null;\n if (input.outcome === 'committed' && input.result !== undefined) e.result = input.result;\n return { next: s, result: { kind: 'settled', entry: e, wasAbandoned } };\n}\n\nexport interface ResolveInput {\n key: string;\n outcome: 'committed' | 'not_applied';\n result?: Json;\n /** Only resolve if the ledger still holds this attempt. */\n attemptId?: string;\n now: number;\n}\n\n/** Resolve an ambiguous (or abandoned) effect after checking the outside world. */\nexport function resolve(\n state: ScopeState | null,\n input: ResolveInput,\n policy: Policy,\n): TransactResult<EffectRecord | null> {\n const s = load(state, input.now, policy);\n const e = s.effects[input.key];\n if (!e) return { next: null, result: null };\n if (input.attemptId !== undefined && e.attemptId !== input.attemptId) return { next: null, result: null };\n if (effectiveStatus(e, input.now, policy) !== 'ambiguous') return { next: null, result: null };\n e.settledAt = input.now;\n e.settleSeq = tick(s);\n if (input.outcome === 'committed') {\n e.status = 'committed';\n e.error = null;\n if (input.result !== undefined) e.result = input.result;\n } else {\n e.status = 'failed';\n e.error = 'reconciled: not applied';\n }\n return { next: s, result: e };\n}\n","import type { ScopeState, Store, TransactResult } from '../types.js';\n\n/**\n * Single-process store. Atomic because `transact` reads, decides and writes in one\n * synchronous step. State is kept as JSON so it behaves like the shared stores.\n * Use it for tests, demos and single-instance deployments; it is lost on restart.\n */\nexport class MemoryStore implements Store {\n private readonly data = new Map<string, string>();\n\n async transact<T>(scope: string, fn: (state: ScopeState | null) => TransactResult<T>): Promise<T> {\n const raw = this.data.get(scope);\n const { next, result } = fn(raw === undefined ? null : (JSON.parse(raw) as ScopeState));\n if (next) this.data.set(scope, JSON.stringify(next));\n return result;\n }\n\n async read(scope: string): Promise<ScopeState | null> {\n const raw = this.data.get(scope);\n return raw === undefined ? null : (JSON.parse(raw) as ScopeState);\n }\n\n /** Number of scopes held. */\n get size(): number {\n return this.data.size;\n }\n\n clear(): void {\n this.data.clear();\n }\n}\n"],"mappings":"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","names":["newer","seq"]}
@@ -60,6 +60,8 @@ interface ReplyOnceOptions {
60
60
  reconcile?: Reconciler;
61
61
  onEvent?: (event: ReplyOnceEvent) => void;
62
62
  clock?: () => number;
63
+ /** Wait between settle retries and `wait` polls. Pass with `clock` to run on simulated time. */
64
+ sleep?: (ms: number) => Promise<void>;
63
65
  }
64
66
  interface OnceOptions {
65
67
  /** Short human-readable description, shown to other runs in conflict reports. */
@@ -109,6 +111,7 @@ declare class ReplyOnce {
109
111
  private readonly reconciler;
110
112
  private readonly emit;
111
113
  private readonly now;
114
+ private readonly sleep;
112
115
  constructor(options: ReplyOnceOptions);
113
116
  /** Resolve key parts to the ledger key used for them. */
114
117
  key(parts: KeyParts): string;
@@ -60,6 +60,8 @@ interface ReplyOnceOptions {
60
60
  reconcile?: Reconciler;
61
61
  onEvent?: (event: ReplyOnceEvent) => void;
62
62
  clock?: () => number;
63
+ /** Wait between settle retries and `wait` polls. Pass with `clock` to run on simulated time. */
64
+ sleep?: (ms: number) => Promise<void>;
63
65
  }
64
66
  interface OnceOptions {
65
67
  /** Short human-readable description, shown to other runs in conflict reports. */
@@ -109,6 +111,7 @@ declare class ReplyOnce {
109
111
  private readonly reconciler;
110
112
  private readonly emit;
111
113
  private readonly now;
114
+ private readonly sleep;
112
115
  constructor(options: ReplyOnceOptions);
113
116
  /** Resolve key parts to the ledger key used for them. */
114
117
  key(parts: KeyParts): string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "replyonce",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Stop your agent from replying, refunding or ticketing twice. Idempotency keys, per-conversation write fencing and an effect ledger for tool-calling agents, webhook handlers and queue workers.",
5
5
  "license": "Apache-2.0",
6
6
  "author": "Sarosh Hussain (https://github.com/shmindmaster)",