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 +9 -6
- package/dist/cli.js +72 -23
- package/dist/index.cjs +4 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +4 -2
- package/dist/index.js.map +1 -1
- package/dist/{replyonce-CGwSjxB2.d.cts → replyonce-C-Qhsfsw.d.cts} +3 -0
- package/dist/{replyonce-DNRS4FWY.d.ts → replyonce-C9AavZdk.d.ts} +3 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
[](https://github.com/shmindmaster/replyonce/actions/workflows/ci.yml)
|
|
6
6
|
[](LICENSE)
|
|
7
7
|
|
|
8
|
+

|
|
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=
|
|
36
|
-
with: workerCrash=10, duplicateDelivery=15, customerBurst=22,
|
|
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
|
|
40
|
-
with replyonce 122
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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 =
|
|
854
|
-
while (
|
|
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
|
|
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
|
|
904
|
+
await world.clock.sleep(10);
|
|
876
905
|
}
|
|
877
906
|
await run.end();
|
|
878
907
|
};
|
|
879
908
|
}
|
|
880
|
-
|
|
881
|
-
const
|
|
882
|
-
const
|
|
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:
|
|
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
|
|
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
|
|
957
|
-
const
|
|
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." :
|
|
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
|
}
|
package/dist/index.cjs.map
CHANGED
|
@@ -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":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,IAAAA,sBAA2B;;;ACA3B,yBAA2B;AAGpB,SAAS,cAAc,OAAwB;AACpD,SAAO,KAAK,UAAU,UAAU,OAAO,oBAAI,IAAI,CAAC,CAAC;AACnD;AAEA,SAAS,UAAU,OAAgB,MAA6B;AAC9D,MAAI,UAAU,QAAQ,UAAU,OAAW,QAAO;AAClD,MAAI,OAAO,UAAU,SAAU,QAAO,MAAM,SAAS;AACrD,MAAI,OAAO,UAAU,UAAU;AAC7B,QAAI,OAAO,UAAU,YAAY,CAAC,OAAO,SAAS,KAAK,EAAG,QAAO,OAAO,KAAK;AAC7E,QAAI,OAAO,UAAU,cAAc,OAAO,UAAU,SAAU,QAAO;AACrE,WAAO;AAAA,EACT;AACA,MAAI,iBAAiB,KAAM,QAAO,MAAM,YAAY;AACpD,MAAI,KAAK,IAAI,KAAK,EAAG,OAAM,IAAI,UAAU,2CAA2C;AACpF,OAAK,IAAI,KAAK;AACd,MAAI;AACJ,MAAI,MAAM,QAAQ,KAAK,GAAG;AACxB,UAAM,MAAM,IAAI,CAAC,MAAM,UAAU,GAAG,IAAI,CAAC;AAAA,EAC3C,OAAO;AACL,UAAM,MAA+B,CAAC;AACtC,eAAW,KAAK,OAAO,KAAK,KAAgC,EAAE,KAAK,GAAG;AACpE,YAAM,IAAK,MAAkC,CAAC;AAC9C,UAAI,MAAM,OAAW;AACrB,UAAI,CAAC,IAAI,UAAU,GAAG,IAAI;AAAA,IAC5B;AACA,UAAM;AAAA,EACR;AACA,OAAK,OAAO,KAAK;AACjB,SAAO;AACT;AAEO,SAAS,UAAU,OAAuB;AAC/C,aAAO,+BAAW,QAAQ,EAAE,OAAO,KAAK,EAAE,OAAO,KAAK;AACxD;AAEO,SAAS,YAAY,SAA0B;AACpD,SAAO,UAAU,cAAc,OAAO,CAAC,EAAE,MAAM,GAAG,EAAE;AACtD;AAEA,IAAM,cAAc;AAGb,SAAS,UAAU,QAAgB,SAA0B;AAClE,MAAI,CAAC,YAAY,KAAK,MAAM,GAAG;AAC7B,UAAM,IAAI;AAAA,MACR,gCAAgC,WAAW,SAAS,KAAK,UAAU,MAAM,CAAC;AAAA,IAC5E;AAAA,EACF;AACA,SAAO,YAAY,SAAY,SAAS,GAAG,MAAM,IAAI,YAAY,OAAO,CAAC;AAC3E;AAGO,SAAS,uBAAuB,OAAe,KAAqB;AACzE,SAAO,MAAM,UAAU,GAAG,KAAK,KAAS,GAAG,EAAE,EAAE,MAAM,GAAG,EAAE,CAAC;AAC7D;;;AChCO,IAAM,cAAc;AAEpB,SAAS,aAAyB;AACvC,SAAO,EAAE,GAAG,GAAG,KAAK,GAAG,UAAU,GAAG,MAAM,CAAC,GAAG,SAAS,CAAC,EAAE;AAC5D;AAEA,SAAS,KAAK,OAA0B,KAAa,QAA4B;AAC/E,QAAM,IAAI,QAAS,gBAAgB,KAAK,IAAmB,WAAW;AACtE,MAAI,EAAE,MAAM,EAAG,OAAM,IAAI,MAAM,wCAAwC,OAAO,EAAE,CAAC,CAAC,EAAE;AACpF,QAAM,UAAU,MAAM,OAAO;AAC7B,aAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,EAAE,OAAO,GAAG;AAC9C,SAAK,EAAE,WAAW,eAAe,EAAE,WAAW,cAAc,EAAE,aAAa,EAAE,aAAa,SAAS;AACjG,aAAO,EAAE,QAAQ,CAAC;AAAA,IACpB;AAAA,EACF;AACA,aAAW,CAAC,IAAI,CAAC,KAAK,OAAO,QAAQ,EAAE,IAAI,GAAG;AAC5C,SAAK,EAAE,WAAW,EAAE,kBAAkB,QAAS,QAAO,EAAE,KAAK,EAAE;AAAA,EACjE;AACA,SAAO;AACT;AAGA,SAAS,KAAK,GAAuB;AACnC,IAAE,OAAO;AACT,SAAO,EAAE;AACX;AAEO,SAAS,YAAY,GAAiB,KAAa,QAAyB;AACjF,SAAO,EAAE,WAAW,aAAa,MAAM,EAAE,aAAa,OAAO;AAC/D;AAGO,SAAS,gBAAgB,GAAiB,KAAa,QAAwC;AACpG,SAAO,YAAY,GAAG,KAAK,MAAM,IAAI,cAAc,EAAE;AACvD;AAEA,SAAS,OAAO,GAAc,KAAsB;AAClD,SAAO,EAAE,YAAY,QAAQ,EAAE,iBAAiB;AAClD;AAUA,SAAS,QAAQ,GAAc,GAAuB;AACpD,MAAI,EAAE,SAAS,eAAe,EAAE,SAAS,YAAa,QAAO,EAAE,QAAQ,EAAE;AACzE,MAAI,EAAE,YAAY,EAAE,QAAS,QAAO,EAAE,UAAU,EAAE;AAClD,MAAI,EAAE,kBAAkB,QAAQ,EAAE,kBAAkB,KAAM,QAAO;AACjE,SAAO,EAAE,UAAU,EAAE;AACvB;AAMO,SAAS,eAAe,GAAe,KAAgB,KAA+B;AAC3F,MAAI,OAAyB;AAC7B,aAAW,KAAK,OAAO,OAAO,EAAE,IAAI,GAAG;AACrC,QAAI,EAAE,UAAU,IAAI,SAAS,EAAE,cAAc,eAAe,CAAC,QAAQ,GAAG,GAAG,EAAG;AAC9E,QAAI,EAAE,SAAS,OAAO,GAAG,GAAG,GAAG;AAC7B,UAAI,CAAC,QAAQ,QAAQ,GAAG,IAAI,EAAG,QAAO;AAAA,IACxC;AAAA,EACF;AACA,SAAO;AACT;AAEA,SAAS,SAAS,KAAgB,KAAa,KAAmB;AAChE,MAAI,UAAU,KAAK,IAAI,IAAI,SAAS,GAAG;AACvC,MAAI,SAAS,KAAK,IAAI,IAAI,QAAQ,GAAG;AACrC,MAAI,YAAY;AAClB;AAiBO,SAAS,MACd,OACA,OACA,QAC8B;AAC9B,QAAM,EAAE,IAAI,IAAI;AAChB,QAAM,IAAI,KAAK,OAAO,KAAK,MAAM;AACjC,QAAM,WAAW,EAAE,KAAK,MAAM,KAAK;AACnC,MAAI,UAAU;AACZ,UAAM,eAAe,OAAO,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,MAAM,EAAE,UAAU,MAAM,KAAK;AACnF,QAAI,SAAS,YAAY,MAAM;AAC7B,aAAO,EAAE,MAAM,MAAM,QAAQ,EAAE,OAAO,SAAS,KAAK,UAAU,aAAa,EAAE;AAAA,IAC/E;AACA,aAAS,iBAAiB,KAAK,IAAI,SAAS,gBAAgB,MAAM,MAAM,UAAU;AAClF,SAAK,CAAC;AAEN,MAAE,YAAY;AACd,aAAS,QAAQ,EAAE;AAGnB,aAAS,UAAU,SAAS;AAC5B,aAAS,SAAS,SAAS,iBAAiB,SAAS;AACrD,aAAS,YAAY,SAAS;AAC9B,QAAI,MAAM,cAAc,UAAa,MAAM,YAAY,SAAS,QAAQ;AACtE,YAAM,QAAQ,KAAK,IAAI,MAAM,WAAW,GAAG;AAC3C,iBAAW,KAAK,OAAO,OAAO,EAAE,OAAO,GAAG;AACxC,YAAI,EAAE,cAAc,QAAQ,EAAE,cAAc,QAAQ,EAAE,YAAY,OAAO;AACvE,mBAAS,UAAU,KAAK,IAAI,SAAS,SAAS,EAAE,SAAS;AAAA,QAC3D;AAAA,MACF;AACA,eAAS,SAAS;AAClB,eAAS,YAAY;AAAA,IACvB;AACA,UAAMC,SAAQ,eAAe,GAAG,UAAU,GAAG;AAC7C,WAAO,EAAE,MAAM,GAAG,QAAQ,EAAE,OAAOA,SAAQ,eAAe,WAAW,KAAK,UAAU,aAAa,EAAE;AAAA,EACrG;AACA,MAAI,MAAM,SAAS,aAAa;AAC9B,QAAI;AACJ,eAAW,KAAK,OAAO,OAAO,EAAE,IAAI,GAAG;AACrC,UAAI,OAAO,GAAG,GAAG,MAAM,CAAC,UAAU,EAAE,QAAQ,OAAO,OAAQ,UAAS;AAAA,IACtE;AACA,QAAI,QAAQ;AACV,aAAO;AAAA,QACL,MAAM;AAAA,QACN,QAAQ;AAAA,UACN,OAAO;AAAA,UACP,QAAQ,EAAE,OAAO,OAAO,OAAO,OAAO,OAAO,OAAO,gBAAgB,OAAO,eAAe;AAAA,QAC5F;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACA,QAAM,MAAM,KAAK,CAAC;AAClB,IAAE,YAAY;AACd,QAAM,YAAY,MAAM,cAAc,SAAY,OAAO,KAAK,IAAI,MAAM,WAAW,GAAG;AACtF,QAAM,MAAiB;AAAA,IACrB,OAAO,MAAM;AAAA,IACb,OAAO,EAAE;AAAA,IACT,MAAM,MAAM;AAAA,IACZ,WAAW;AAAA,IACX,SAAS,MAAM,SAAS,cAAc,MAAO,aAAa;AAAA,IAC1D,gBAAgB,MAAM,MAAM;AAAA,IAC5B,SAAS;AAAA,IACT,QAAQ,aAAa;AAAA,IACrB;AAAA,IACA,SAAS;AAAA,IACT,eAAe;AAAA,IACf,OAAO;AAAA,IACP,SAAS;AAAA,IACT,WAAW;AAAA,EACb;AACA,IAAE,KAAK,MAAM,KAAK,IAAI;AACtB,QAAM,QAAQ,eAAe,GAAG,KAAK,GAAG;AACxC,SAAO,EAAE,MAAM,GAAG,QAAQ,EAAE,OAAO,QAAQ,eAAe,OAAO,KAAK,cAAc,CAAC,EAAE,EAAE;AAC3F;AAQO,SAAS,MACd,OACA,OACA,QAC8B;AAC9B,QAAM,IAAI,KAAK,OAAO,MAAM,KAAK,MAAM;AACvC,QAAM,IAAI,EAAE,KAAK,MAAM,KAAK;AAC5B,MAAI,CAAC,EAAG,QAAO,EAAE,MAAM,MAAM,QAAQ,EAAE,IAAI,OAAO,QAAQ,cAAc,EAAE;AAC1E,MAAI,EAAE,UAAU,MAAM,MAAO,QAAO,EAAE,MAAM,MAAM,QAAQ,EAAE,IAAI,OAAO,QAAQ,aAAa,EAAE;AAC9F,MAAI,EAAE,cAAc,YAAa,QAAO,EAAE,MAAM,MAAM,QAAQ,EAAE,IAAI,OAAO,QAAQ,gBAAgB,EAAE;AACrG,MAAI,EAAE,YAAY,KAAM,QAAO,EAAE,MAAM,MAAM,QAAQ,EAAE,IAAI,OAAO,QAAQ,YAAY,EAAE;AACxF,MAAI,eAAe,GAAG,GAAG,MAAM,GAAG,EAAG,QAAO,EAAE,MAAM,MAAM,QAAQ,EAAE,IAAI,OAAO,QAAQ,aAAa,EAAE;AACtG,IAAE,iBAAiB,KAAK,IAAI,EAAE,gBAAgB,MAAM,MAAM,MAAM,UAAU;AAC1E,OAAK,CAAC;AACN,SAAO,EAAE,MAAM,GAAG,QAAQ,EAAE,IAAI,MAAM,KAAK,EAAE,EAAE;AACjD;AAEO,SAAS,OACd,OACA,OACA,QACkC;AAClC,QAAM,IAAI,KAAK,OAAO,MAAM,KAAK,MAAM;AACvC,QAAM,IAAI,EAAE,KAAK,MAAM,KAAK;AAC5B,MAAI,CAAC,EAAG,QAAO,EAAE,MAAM,MAAM,QAAQ,KAAK;AAC1C,MAAI,MAAM,UAAU,UAAa,EAAE,UAAU,MAAM,MAAO,QAAO,EAAE,MAAM,MAAM,QAAQ,KAAK;AAC5F,MAAI,EAAE,YAAY,QAAQ,EAAE,MAAM,WAAW,eAAe,EAAE,cAAc,cAAc;AACxF,WAAO,EAAE,MAAM,MAAM,QAAQ,EAAE;AAAA,EACjC;AACA,IAAE,UAAU,EAAE,WAAW,MAAM;AAC/B,IAAE,YAAY,MAAM;AACpB,OAAK,CAAC;AACN,SAAO,EAAE,MAAM,GAAG,QAAQ,EAAE;AAC9B;AA0BO,SAAS,aACd,GACA,KACA,KACA,QACA,MACA,KACA,QACgB;AAChB,MAAI,SAAS,OAAQ,QAAO,CAAC;AAC7B,QAAM,MAAsB,CAAC;AAC7B,aAAW,KAAK,OAAO,OAAO,EAAE,OAAO,GAAG;AACxC,QAAI,EAAE,QAAQ,IAAK;AACnB,QAAI,EAAE,UAAU,IAAI,OAAO;AAGzB,UAAI,EAAE,UAAU,IAAI,SAAS,EAAE,WAAW,OAAQ;AAAA,IACpD;AACA,QAAI,SAAS,iBAAiB,EAAE,WAAW,OAAQ;AACnD,UAAM,YAAY,YAAY,GAAG,KAAK,MAAM;AAC5C,QAAI,WAAW;AACf,QAAI,EAAE,WAAW,aAAa,CAAC,WAAW;AACxC,iBAAW;AAAA,IACb,WAAW,WAAW;AAEpB,iBAAW,EAAE,YAAY,OAAO,gBAAgB,IAAI;AAAA,IACtD,WAAW,EAAE,WAAW,eAAe,EAAE,WAAW,aAAa;AAC/D,kBACG,EAAE,aAAa,KAAK,IAAI,WAAY,IAAI,cAAc,SAAS,EAAE,aAAa,MAAM,IAAI;AAAA,IAC7F;AACA,QAAI,UAAU;AACZ,UAAI,KAAK;AAAA,QACP,KAAK,EAAE;AAAA,QACP,QAAQ,EAAE;AAAA,QACV,QAAQ,YAAY,cAAc,EAAE;AAAA,QACpC,OAAO,EAAE;AAAA,QACT,SAAS,EAAE;AAAA,QACX,IAAI,EAAE,aAAa,EAAE;AAAA,MACvB,CAAC;AAAA,IACH;AAAA,EACF;AACA,SAAO,IAAI,KAAK,CAAC,GAAG,MAAM,EAAE,KAAK,EAAE,EAAE,EAAE,MAAM,GAAG,EAAE;AACpD;AAEO,SAAS,MACd,OACA,OACA,OACA,QAC8B;AAC9B,QAAM,EAAE,IAAI,IAAI;AAChB,QAAM,IAAI,KAAK,OAAO,KAAK,MAAM;AACjC,QAAM,QAAQ,EAAE,QAAQ,MAAM,GAAG;AAEjC,MAAI,OAAO;AACT,QAAI,MAAM,WAAW,YAAa,QAAO,EAAE,MAAM,MAAM,QAAQ,EAAE,MAAM,UAAU,OAAO,MAAM,EAAE;AAChG,QAAI,MAAM,WAAW,WAAW;AAC9B,UAAI,CAAC,YAAY,OAAO,KAAK,MAAM,GAAG;AACpC,eAAO,EAAE,MAAM,MAAM,QAAQ,EAAE,MAAM,aAAa,OAAO,MAAM,EAAE;AAAA,MACnE;AACA,YAAMC,OAAM,KAAK,CAAC;AAClB,YAAM,SAAS;AACf,YAAM,YAAY,MAAM,YAAY,OAAO;AAC3C,YAAM,YAAYA;AAClB,YAAM,QAAQ,MAAM,SAAS;AAC7B,aAAO,EAAE,MAAM,GAAG,QAAQ,EAAE,MAAM,aAAa,OAAO,MAAM,EAAE;AAAA,IAChE;AACA,QAAI,MAAM,WAAW,YAAa,QAAO,EAAE,MAAM,MAAM,QAAQ,EAAE,MAAM,aAAa,OAAO,MAAM,EAAE;AACnG,QAAI,MAAM,WAAW,OAAO,aAAa;AACvC,aAAO,EAAE,MAAM,MAAM,QAAQ,EAAE,MAAM,WAAW,QAAQ,aAAa,OAAO,MAAM,EAAE;AAAA,IACtF;AAAA,EACF;AAEA,MAAI;AACJ,MAAI,MAAM,UAAU,MAAM;AACxB,UAAM,EAAE,KAAK,MAAM,KAAK;AACxB,QAAI,CAAC,IAAK,QAAO,EAAE,MAAM,MAAM,QAAQ,EAAE,MAAM,WAAW,QAAQ,cAAc,EAAE;AAClF,QAAI,IAAI,cAAc,YAAa,QAAO,EAAE,MAAM,MAAM,QAAQ,EAAE,MAAM,WAAW,QAAQ,gBAAgB,EAAE;AAC7G,QAAI,IAAI,YAAY,KAAM,QAAO,EAAE,MAAM,MAAM,QAAQ,EAAE,MAAM,WAAW,QAAQ,YAAY,EAAE;AAChG,QAAK,MAAM,UAAU,QAAQ,IAAI,UAAU,MAAM,SAAU,eAAe,GAAG,KAAK,GAAG,GAAG;AACtF,aAAO,EAAE,MAAM,MAAM,QAAQ,EAAE,MAAM,WAAW,QAAQ,aAAa,EAAE;AAAA,IACzE;AAEA,UAAM,YAAY,aAAa,GAAG,KAAK,MAAM,KAAK,MAAM,QAAQ,MAAM,UAAU,KAAK,MAAM;AAC3F,QAAI,UAAU,SAAS,GAAG;AACxB,UAAI,CAAC,MAAM,KAAK;AACd,eAAO,EAAE,MAAM,MAAM,QAAQ,EAAE,MAAM,WAAW,QAAQ,wBAAwB,UAAU,EAAE;AAAA,MAC9F;AACA,eAAS,KAAK,KAAK,CAAC,GAAG,GAAG;AAC1B,aAAO,EAAE,MAAM,GAAG,QAAQ,EAAE,MAAM,WAAW,QAAQ,wBAAwB,UAAU,EAAE;AAAA,IAC3F;AAAA,EACF;AAEA,QAAM,MAAM,KAAK,CAAC;AAClB,QAAM,QAAsB;AAAA,IAC1B,KAAK,MAAM;AAAA,IACX;AAAA,IACA,QAAQ,MAAM;AAAA,IACd,QAAQ;AAAA,IACR,UAAU,OAAO,WAAW,KAAK;AAAA,IACjC,WAAW,MAAM;AAAA,IACjB,OAAO,MAAM;AAAA,IACb,OAAO,KAAK,SAAS;AAAA,IACrB,SAAS,MAAM,YAAY,OAAO,OAAO,MAAM,QAAQ,MAAM,GAAG,WAAW;AAAA,IAC3E,OAAO;AAAA,IACP,WAAW,OAAO,aAAa;AAAA,IAC/B,WAAW;AAAA,IACX,WAAW;AAAA,IACX,UAAU;AAAA,IACV,WAAW;AAAA,EACb;AACA,IAAE,QAAQ,MAAM,GAAG,IAAI;AACvB,MAAI,KAAK;AAGP,QAAI,QAAQ;AACZ,QAAI,iBAAiB,KAAK,IAAI,IAAI,gBAAgB,MAAM,MAAM,UAAU;AAAA,EAC1E;AACA,SAAO,EAAE,MAAM,GAAG,QAAQ,EAAE,MAAM,YAAY,MAAM,EAAE;AACxD;AAiBO,SAAS,OACd,OACA,OACA,QAC+B;AAC/B,QAAM,IAAI,KAAK,OAAO,MAAM,KAAK,MAAM;AACvC,QAAM,IAAI,EAAE,QAAQ,MAAM,GAAG;AAE7B,QAAM,YAAY,GAAG,WAAW,YAAY,MAAM,YAAY;AAC9D,MACE,CAAC,KACD,EAAE,cAAc,MAAM,aACrB,EAAE,WAAW,aAAa,EAAE,WAAW,eAAe,CAAC,WACxD;AACA,WAAO,EAAE,MAAM,MAAM,QAAQ,EAAE,MAAM,SAAS,OAAO,KAAK,KAAK,EAAE;AAAA,EACnE;AACA,QAAM,eAAe,EAAE,WAAW,aAAa,YAAY,GAAG,MAAM,KAAK,MAAM;AAC/E,IAAE,SAAS,MAAM;AACjB,IAAE,YAAY,MAAM;AACpB,IAAE,YAAY,KAAK,CAAC;AACpB,IAAE,QAAQ,MAAM,SAAS;AACzB,MAAI,MAAM,YAAY,eAAe,MAAM,WAAW,OAAW,GAAE,SAAS,MAAM;AAClF,SAAO,EAAE,MAAM,GAAG,QAAQ,EAAE,MAAM,WAAW,OAAO,GAAG,aAAa,EAAE;AACxE;AAYO,SAAS,QACd,OACA,OACA,QACqC;AACrC,QAAM,IAAI,KAAK,OAAO,MAAM,KAAK,MAAM;AACvC,QAAM,IAAI,EAAE,QAAQ,MAAM,GAAG;AAC7B,MAAI,CAAC,EAAG,QAAO,EAAE,MAAM,MAAM,QAAQ,KAAK;AAC1C,MAAI,MAAM,cAAc,UAAa,EAAE,cAAc,MAAM,UAAW,QAAO,EAAE,MAAM,MAAM,QAAQ,KAAK;AACxG,MAAI,gBAAgB,GAAG,MAAM,KAAK,MAAM,MAAM,YAAa,QAAO,EAAE,MAAM,MAAM,QAAQ,KAAK;AAC7F,IAAE,YAAY,MAAM;AACpB,IAAE,YAAY,KAAK,CAAC;AACpB,MAAI,MAAM,YAAY,aAAa;AACjC,MAAE,SAAS;AACX,MAAE,QAAQ;AACV,QAAI,MAAM,WAAW,OAAW,GAAE,SAAS,MAAM;AAAA,EACnD,OAAO;AACL,MAAE,SAAS;AACX,MAAE,QAAQ;AAAA,EACZ;AACA,SAAO,EAAE,MAAM,GAAG,QAAQ,EAAE;AAC9B;;;AF5aO,IAAM,aAAN,cAAyB,MAAM;AAAA,EAC3B,YAAY;AAAA,EACrB,YAAY,UAAU,sBAAsB,SAA+B;AACzE,UAAM,OAAO;AACb,SAAK,OAAO;AACZ,QAAI,WAAW,WAAW,QAAS,CAAC,KAA6B,QAAQ,QAAQ;AAAA,EACnF;AACF;AAEO,SAAS,qBAAqB,KAA0B;AAC7D,MAAI,eAAe,WAAY,QAAO;AACtC,MAAI,OAAO,OAAO,QAAQ,YAAa,IAAgC,cAAc,eAAe;AAClG,WAAO;AAAA,EACT;AACA,SAAO;AACT;AA6EA,IAAM,aAAa;AAEZ,IAAM,YAAN,MAAgB;AAAA,EACZ;AAAA,EACA;AAAA,EACA;AAAA,EACQ;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAEjB,YAAY,SAA2B;AACrC,QAAI,CAAC,WAAW,CAAC,QAAQ,MAAO,OAAM,IAAI,UAAU,gCAAgC;AACpF,SAAK,QAAQ,QAAQ;AACrB,SAAK,SAAS;AAAA,MACZ,cAAc,QAAQ,gBAAgB,KAAK;AAAA,MAC3C,aAAa,QAAQ,eAAe;AAAA,MACpC,aAAa,QAAQ,eAAe,IAAI,KAAK;AAAA,IAC/C;AACA,QAAI,KAAK,OAAO,gBAAgB,KAAK,KAAK,OAAO,cAAc,KAAK,KAAK,OAAO,eAAe,GAAG;AAChG,YAAM,IAAI,WAAW,uEAAuE;AAAA,IAC9F;AACA,SAAK,aAAa,QAAQ,cAAc;AACxC,SAAK,WAAW,QAAQ,iBAAiB;AACzC,SAAK,aAAa,QAAQ;AAC1B,UAAM,UAAU,QAAQ;AACxB,SAAK,OAAO,UACR,CAAC,MAAM;AACL,UAAI;AACF,gBAAQ,CAAC;AAAA,MACX,QAAQ;AAAA,MAER;AAAA,IACF,IACA,MAAM;AAAA,IAAC;AACX,SAAK,MAAM,QAAQ,SAAS,KAAK;AAAA,EACnC;AAAA;AAAA,EAGA,IAAI,OAAyB;AAC3B,WAAO,UAAU,MAAM,QAAQ,MAAM,OAAO;AAAA,EAC9C;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,KAAQ,KAAwB,QAAmB,UAAuB,CAAC,GAA2B;AACpG,UAAM,EAAE,OAAO,WAAW,OAAO,IAAI,KAAK,WAAW,GAAG;AACxD,WAAO,KAAK,QAAQ,OAAO,WAAW,QAAQ,MAAM,QAAQ,EAAE,GAAG,SAAS,UAAU,QAAQ,KAAK,MAAM,CAAC;AAAA,EAC1G;AAAA;AAAA,EAGA,MAAM,MAAM,KAA8C;AACxD,UAAM,EAAE,OAAO,UAAU,IAAI,KAAK,WAAW,GAAG;AAChD,UAAM,QAAQ,MAAM,KAAK,MAAM,KAAK,KAAK;AACzC,UAAM,QAAQ,OAAO,QAAQ,SAAS,KAAK;AAC3C,QAAI,CAAC,MAAO,QAAO,EAAE,MAAM,OAAO,QAAQ,QAAQ,OAAO,KAAK;AAC9D,UAAM,SAAe,gBAAgB,OAAO,KAAK,IAAI,GAAG,KAAK,MAAM;AACnE,WAAO,EAAE,MAAM,WAAW,aAAa,QAAQ,MAAM;AAAA,EACvD;AAAA;AAAA,EAGA,MAAM,QAAQ,OAAwC;AACpD,UAAM,QAAQ,MAAM,KAAK,MAAM,KAAK,KAAK;AACzC,QAAI,CAAC,MAAO,QAAO,CAAC;AACpB,UAAM,MAAM,KAAK,IAAI;AACrB,WAAO,OAAO,OAAO,MAAM,OAAO,EAC/B,IAAI,CAAC,OAAO,EAAE,GAAG,GAAG,QAAc,gBAAgB,GAAG,KAAK,KAAK,MAAM,EAAE,EAAE,EACzE,KAAK,CAAC,GAAG,MAAM,EAAE,YAAY,EAAE,SAAS;AAAA,EAC7C;AAAA;AAAA,EAGA,MAAM,QACJ,KACA,SAC8B;AAC9B,UAAM,EAAE,OAAO,UAAU,IAAI,KAAK,WAAW,GAAG;AAChD,UAAM,SAAS,YAAY,UAAU,QAAQ,SAAS;AACtD,UAAM,MAAM,KAAK,IAAI;AACrB,UAAM,QAAQ,MAAM,KAAK,MAAM;AAAA,MAAS;AAAA,MAAO,CAAC,MACxC,QAAQ,GAAG,EAAE,KAAK,WAAW,SAAS,QAAQ,SAAS,QAAQ,IAAI,GAAG,KAAK,MAAM;AAAA,IACzF;AACA,QAAI,MAAO,MAAK,KAAK,EAAE,MAAM,cAAc,OAAO,KAAK,WAAW,SAAS,QAAQ,QAAQ,CAAC;AAC5F,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,MAAM,OAAe,UAAwB,CAAC,GAAiB;AACnE,gBAAY,KAAK;AACjB,UAAM,QAAQ,QAAQ,aAAS,gCAAW;AAC1C,UAAM,aAAa,QAAQ,cAAc,KAAK;AAC9C,UAAM,MAAM,KAAK,IAAI;AACrB,UAAM,UAAU,MAAM,KAAK,MAAM;AAAA,MAAS;AAAA,MAAO,CAAC,MAC1C;AAAA,QACJ;AAAA,QACA;AAAA,UACE;AAAA,UACA,MAAM,QAAQ,QAAQ;AAAA,UACtB;AAAA,UACA;AAAA,UACA,GAAI,QAAQ,cAAc,SAAY,EAAE,WAAW,QAAQ,UAAU,IAAI,CAAC;AAAA,QAC5E;AAAA,QACA,KAAK;AAAA,MACP;AAAA,IACF;AACA,WAAO,IAAI,IAAI,MAAM,OAAO,OAAO,YAAY,OAAO;AAAA,EACxD;AAAA;AAAA,EAGA,MAAM,OAAO,OAAe,OAAiC;AAC3D,UAAM,MAAM,KAAK,IAAI;AACrB,UAAM,IAAI,MAAM,KAAK,MAAM;AAAA,MAAS;AAAA,MAAO,CAAC,MACpC,OAAO,GAAG,EAAE,OAAO,QAAQ,aAAa,IAAI,GAAG,KAAK,MAAM;AAAA,IAClE;AACA,WAAO,MAAM;AAAA,EACf;AAAA;AAAA,EAGA,MAAM,SAAS,OAAe,OAAe,OAAe,YAAsC;AAChG,UAAM,MAAM,KAAK,IAAI;AACrB,UAAM,IAAI,MAAM,KAAK,MAAM;AAAA,MAAS;AAAA,MAAO,CAAC,MACpC,MAAM,GAAG,EAAE,OAAO,OAAO,YAAY,IAAI,GAAG,KAAK,MAAM;AAAA,IAC/D;AACA,WAAO,EAAE;AAAA,EACX;AAAA;AAAA,EAGA,MAAM,OAAO,OAAe,OAAe,OAAe,QAAqC;AAC7F,UAAM,MAAM,KAAK,IAAI;AACrB,UAAM,KAAK,MAAM,SAAS,OAAO,CAAC,MAAY,OAAO,GAAG,EAAE,OAAO,OAAO,QAAQ,IAAI,GAAG,KAAK,MAAM,CAAC;AAAA,EACrG;AAAA;AAAA,EAGA,WAAW,KAA8E;AACvF,QAAI,OAAO,QAAQ,UAAU;AAC3B,kBAAY,GAAG;AACf,aAAO,EAAE,OAAO,KAAK,WAAW,KAAK,QAAQ,OAAO;AAAA,IACtD;AACA,gBAAY,IAAI,KAAK;AACrB,WAAO,EAAE,OAAO,IAAI,OAAO,WAAW,UAAU,IAAI,QAAQ,IAAI,OAAO,GAAG,QAAQ,IAAI,OAAO;AAAA,EAC/F;AAAA;AAAA,EAGA,MAAM,QACJ,OACA,KACA,QACA,KACA,QACA,SACwB;AACxB,UAAM,QAAQ,KAAK,SAAS;AAC5B,UAAM,QAAQ,KAAK,SAAS;AAC5B,UAAM,WAAW,QAAQ,iBAAiB,KAAK;AAC/C,UAAM,YAAY,QAAQ,aAAa,KAAK;AAC5C,UAAM,YAAY,QAAQ,OAAO,KAAK,IAAI,IAAI,QAAQ,KAAK,YAAY;AACvE,QAAI,iBAAiB;AAErB,aAAS,QAAQ,GAAG,QAAQ,YAAY,SAAS;AAC/C,YAAM,gBAAY,gCAAW;AAC7B,YAAM,MAAM,KAAK,IAAI;AACrB,YAAM,UAAwB,MAAM,KAAK,MAAM;AAAA,QAAS;AAAA,QAAO,CAAC,MACxD;AAAA,UACJ;AAAA,UACA;AAAA,UACA;AAAA,YACE;AAAA,YACA;AAAA,YACA,SAAS,QAAQ,WAAW;AAAA,YAC5B;AAAA,YACA;AAAA,YACA;AAAA,YACA,UAAU,QAAQ,YAAY;AAAA,YAC9B,KAAK,QAAQ,OAAO;AAAA,YACpB,YAAY,KAAK,cAAc,KAAK;AAAA,YACpC;AAAA,UACF;AAAA,UACA,KAAK;AAAA,QACP;AAAA,MACF;AAEA,cAAQ,QAAQ,MAAM;AAAA,QACpB,KAAK;AACH,eAAK,KAAK,EAAE,MAAM,YAAY,OAAO,IAAI,CAAC;AAC1C,iBAAO,EAAE,QAAQ,YAAY,IAAI,MAAM,OAAO,QAAQ,MAAM,QAAa,KAAK,OAAO,QAAQ,MAAM;AAAA,QAErG,KAAK;AACH,eAAK,KAAK,EAAE,MAAM,WAAW,OAAO,KAAK,QAAQ,QAAQ,QAAQ,MAAM,CAAC;AACxE,iBAAO;AAAA,YACL,QAAQ;AAAA,YACR,IAAI;AAAA,YACJ;AAAA,YACA,QAAQ,QAAQ;AAAA,YAChB,GAAI,QAAQ,YAAY,EAAE,WAAW,QAAQ,UAAU,IAAI,CAAC;AAAA,YAC5D,GAAI,QAAQ,QAAQ,EAAE,OAAO,QAAQ,MAAM,IAAI,CAAC;AAAA,UAClD;AAAA,QAEF,KAAK,aAAa;AAChB,cAAI,QAAQ,QAAQ,KAAK,IAAI,IAAI,WAAW;AAC1C,kBAAM,KAAK,cAAc,OAAO,KAAK,WAAW,QAAQ,KAAK,cAAc,GAAG;AAC9E;AAAA,UACF;AACA,eAAK,KAAK,EAAE,MAAM,aAAa,OAAO,IAAI,CAAC;AAC3C,iBAAO,EAAE,QAAQ,aAAa,IAAI,OAAO,KAAK,OAAO,QAAQ,MAAM;AAAA,QACrE;AAAA,QAEA,KAAK,aAAa;AAChB,cAAI,aAAa,CAAC,gBAAgB;AAChC,6BAAiB;AACjB,gBAAI;AACJ,gBAAI;AACF,wBAAU,MAAM,UAAU,QAAQ,KAAK;AAAA,YACzC,QAAQ;AACN,wBAAU,EAAE,SAAS,UAAU;AAAA,YACjC;AACA,gBAAI,QAAQ,YAAY,WAAW;AACjC,oBAAM,KAAK,KAAK,IAAI;AACpB,oBAAM,WAAW,MAAM,KAAK,MAAM;AAAA,gBAAS;AAAA,gBAAO,CAAC,MAC3C;AAAA,kBACJ;AAAA,kBACA;AAAA,oBACE;AAAA,oBACA,SAAS,QAAQ;AAAA,oBACjB,QAAQ,QAAQ,YAAY,cAAc,QAAQ,SAAS;AAAA,oBAC3D,WAAW,QAAQ,MAAM;AAAA,oBACzB,KAAK;AAAA,kBACP;AAAA,kBACA,KAAK;AAAA,gBACP;AAAA,cACF;AACA,kBAAI,SAAU,MAAK,KAAK,EAAE,MAAM,cAAc,OAAO,KAAK,SAAS,QAAQ,QAAQ,CAAC;AACpF;AAAA,YACF;AAAA,UACF;AACA,eAAK,KAAK,EAAE,MAAM,aAAa,OAAO,IAAI,CAAC;AAC3C,iBAAO,EAAE,QAAQ,aAAa,IAAI,OAAO,KAAK,OAAO,QAAQ,MAAM;AAAA,QACrE;AAAA,QAEA,KAAK;AACH,iBAAO,KAAK,YAAY,OAAO,KAAK,QAAQ,OAAO,QAAQ,QAAQ;AAAA,MACvE;AAAA,IACF;AACA,UAAM,SAAS,MAAM,KAAK,MAAM,KAAK,KAAK,IAAI,QAAQ,GAAG;AACzD,QAAI,SAAS,MAAM,WAAW,aAAa;AACzC,aAAO,EAAE,QAAQ,YAAY,IAAI,MAAM,OAAO,MAAM,QAAa,KAAK,MAAM;AAAA,IAC9E;AACA,QAAI,OAAO;AACT,WAAK,KAAK,EAAE,MAAM,aAAa,OAAO,IAAI,CAAC;AAC3C,aAAO,EAAE,QAAQ,aAAa,IAAI,OAAO,KAAK,MAAM;AAAA,IACtD;AACA,UAAM,IAAI,MAAM,qDAAqD;AAAA,EACvE;AAAA,EAEA,MAAc,YACZ,OACA,KACA,OACA,QACA,UACwB;AACxB,UAAM,EAAE,SAAS,WAAW,MAAM,IAAI;AACtC,SAAK,KAAK,EAAE,MAAM,WAAW,OAAO,KAAK,SAAS,MAAM,CAAC;AACzD,UAAM,YAAY,KAAK,IAAI,GAAG,MAAM,YAAY,KAAK,OAAO,eAAe,KAAK,IAAI,CAAC;AACrF,UAAM,MAAqB;AAAA,MACzB;AAAA,MACA;AAAA,MACA,QAAQ,MAAM;AAAA,MACd;AAAA,MACA,gBAAgB,uBAAuB,OAAO,GAAG;AAAA,MACjD,cAAc,MAAM;AAAA,MACpB,QAAQ,YAAY,QAAQ,SAAS;AAAA,IACvC;AAEA,QAAI;AACJ,QAAI;AACF,cAAQ,MAAM,OAAO,GAAG;AAAA,IAC1B,SAAS,OAAO;AACd,YAAM,MAAM,aAAa,UAAU,KAAK;AACxC,YAAM,UAAU,QAAQ,gBAAgB,WAAW;AACnD,YAAM,KAAK,gBAAgB,OAAO;AAAA,QAChC;AAAA,QACA;AAAA,QACA;AAAA,QACA,OAAO,cAAc,KAAK;AAAA,QAC1B,KAAK;AAAA,MACP,CAAC,EAAE,MAAM,MAAM,MAAS;AACxB,UAAI,YAAY,UAAU;AACxB,aAAK,KAAK,EAAE,MAAM,UAAU,OAAO,KAAK,SAAS,MAAM,CAAC;AACxD,eAAO;AAAA,UACL,QAAQ;AAAA,UACR,IAAI;AAAA,UACJ;AAAA,UACA;AAAA,UACA;AAAA,UACA,WAAW,UAAU,KAAK,OAAO;AAAA,QACnC;AAAA,MACF;AACA,WAAK,KAAK,EAAE,MAAM,aAAa,OAAO,KAAK,MAAM,CAAC;AAClD,YAAM,WAAW,MAAM,KAAK,MAAM,KAAK,KAAK,EAAE,MAAM,MAAM,IAAI,IAAI,QAAQ,GAAG,KAAK;AAClF,aAAO,EAAE,QAAQ,aAAa,IAAI,OAAO,KAAK,OAAO,SAAS,MAAM;AAAA,IACtE;AAEA,QAAI;AACJ,QAAI;AACF,eAAS,UAAU,SAAY,OAAQ,KAAK,MAAM,KAAK,UAAU,KAAK,CAAC;AAAA,IACzE,QAAQ;AACN,eAAS;AACT,WAAK,KAAK,EAAE,MAAM,yBAAyB,OAAO,IAAI,CAAC;AAAA,IACzD;AACA,QAAI;AACF,YAAM,UAAU,MAAM,KAAK,gBAAgB,OAAO;AAAA,QAChD;AAAA,QACA;AAAA,QACA,SAAS;AAAA,QACT,QAAQ;AAAA,QACR,KAAK;AAAA,MACP,CAAC;AACD,WAAK,KAAK,EAAE,MAAM,aAAa,OAAO,KAAK,SAAS,MAAM,CAAC;AAC3D,UAAI,QAAQ,SAAS,WAAW,QAAQ,cAAc;AACpD,aAAK,KAAK,EAAE,MAAM,eAAe,OAAO,KAAK,QAAQ,CAAC;AACtD,eAAO,EAAE,QAAQ,YAAY,IAAI,MAAM,OAAO,KAAK,SAAS,YAAY,KAAK;AAAA,MAC/E;AAAA,IACF,QAAQ;AAGN,WAAK,KAAK,EAAE,MAAM,eAAe,OAAO,KAAK,QAAQ,CAAC;AACtD,aAAO,EAAE,QAAQ,YAAY,IAAI,MAAM,OAAO,KAAK,SAAS,YAAY,KAAK;AAAA,IAC/E;AACA,WAAO,EAAE,QAAQ,YAAY,IAAI,MAAM,OAAO,KAAK,QAAQ;AAAA,EAC7D;AAAA,EAEA,MAAc,gBAAgB,OAAe,OAAwD;AACnG,QAAI;AACJ,aAAS,IAAI,GAAG,IAAI,GAAG,KAAK;AAC1B,UAAI;AACF,cAAM,MAAM,KAAK,IAAI;AACrB,eAAO,MAAM,KAAK,MAAM,SAAS,OAAO,CAAC,MAAY,OAAO,GAAG,EAAE,GAAG,OAAO,IAAI,GAAG,KAAK,MAAM,CAAC;AAAA,MAChG,SAAS,KAAK;AACZ,oBAAY;AACZ,cAAM,MAAM,KAAK,KAAK,CAAC;AAAA,MACzB;AAAA,IACF;AACA,UAAM;AAAA,EACR;AAAA,EAEA,MAAc,cAAc,OAAe,KAAa,OAAe,YAAmC;AACxG,WAAO,KAAK,IAAI,IAAI,OAAO;AACzB,YAAM,MAAM,KAAK,IAAI,YAAY,KAAK,IAAI,GAAG,QAAQ,KAAK,IAAI,CAAC,CAAC,CAAC;AACjE,YAAM,KAAK,MAAM,KAAK,MAAM,KAAK,KAAK,IAAI,QAAQ,GAAG;AACrD,UAAI,CAAC,KAAW,gBAAgB,GAAG,KAAK,IAAI,GAAG,KAAK,MAAM,MAAM,UAAW;AAAA,IAC7E;AAAA,EACF;AACF;AAKO,IAAM,MAAN,MAAU;AAAA,EACN;AAAA,EACA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA,EACQ;AAAA,EACA;AAAA,EAEjB,YAAY,IAAe,OAAe,OAAe,YAAoB,SAAuB;AAClG,SAAK,KAAK;AACV,SAAK,QAAQ;AACb,SAAK,QAAQ;AACb,SAAK,aAAa;AAClB,SAAK,QAAQ,QAAQ;AACrB,QAAI,QAAQ,UAAU,QAAQ;AAC5B,WAAK,eAAe;AACpB,WAAK,eAAe,CAAC;AACrB,WAAK,aAAa,QAAQ;AAAA,IAC5B,OAAO;AACL,WAAK,eAAgB,QAAQ,IAAkB;AAC/C,WAAK,eAAe,QAAQ;AAC5B,WAAK,aAAa;AAAA,IACpB;AAAA,EACF;AAAA;AAAA,EAGA,IAAI,WAAoB;AACtB,WAAO,KAAK,UAAU,SAAS,KAAK,UAAU;AAAA,EAChD;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,IAAI,eAAwB;AAC1B,WAAO,KAAK,aAAa,KAAK,CAAC,MAAM,EAAE,WAAW,QAAQ;AAAA,EAC5D;AAAA;AAAA,EAGA,KAAQ,QAAgB,SAAkB,QAAmB,UAA0B,CAAC,GAA2B;AACjH,UAAM,MAAM,UAAU,QAAQ,OAAO;AACrC,QAAI,KAAK,iBAAiB,MAAM;AAC9B,aAAO,QAAQ,QAAQ,EAAE,QAAQ,WAAW,IAAI,OAAO,KAAK,QAAQ,OAAO,CAAC;AAAA,IAC9E;AACA,UAAM,OAAO,EAAE,OAAO,KAAK,OAAO,OAAO,KAAK,cAAc,YAAY,KAAK,WAAW;AACxF,WAAO,KAAK,GAAG,QAAQ,KAAK,OAAO,KAAK,QAAQ,MAAM,QAAQ,OAAO;AAAA,EACvE;AAAA,EAEA,MAAM,QAAgB,SAAyC;AAC7D,WAAO,KAAK,GAAG,MAAM,EAAE,OAAO,KAAK,OAAO,QAAQ,QAAQ,CAAC;AAAA,EAC7D;AAAA,EAEA,UAAmC;AACjC,WAAO,KAAK,GAAG,QAAQ,KAAK,KAAK;AAAA,EACnC;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,YAA8B;AAC5B,QAAI,KAAK,iBAAiB,KAAM,QAAO,QAAQ,QAAQ,KAAK;AAC5D,WAAO,KAAK,GAAG,SAAS,KAAK,OAAO,KAAK,OAAO,KAAK,cAAc,KAAK,UAAU;AAAA,EACpF;AAAA;AAAA,EAGA,IAAI,SAAuB,aAA4B;AACrD,QAAI,KAAK,iBAAiB,KAAM,QAAO,QAAQ,QAAQ;AACvD,WAAO,KAAK,GAAG,OAAO,KAAK,OAAO,KAAK,OAAO,KAAK,cAAc,MAAM;AAAA,EACzE;AACF;AAEO,SAAS,gBAAgB,SAAsC;AACpE,SAAO,IAAI,UAAU,OAAO;AAC9B;AAGO,SAAS,OAAU,QAA0B;AAClD,MAAI,OAAO,GAAI,QAAO,OAAO;AAC7B,QAAM,IAAI,eAAe,MAAM;AACjC;AAEO,IAAM,iBAAN,cAA6B,MAAM;AAAA,EAC/B;AAAA,EACT,YAAY,QAAoD;AAC9D,UAAM,SAAS,OAAO,WAAW,YAAY,GAAG,OAAO,MAAM,KAAK,OAAO,MAAM,KAAK,OAAO;AAC3F,UAAM,qBAAqB,OAAO,GAAG,kBAAkB,MAAM,GAAG;AAChE,SAAK,OAAO;AACZ,SAAK,SAAS;AAAA,EAChB;AACF;AAEA,SAAS,YAAY,OAAqB;AACxC,MAAI,OAAO,UAAU,YAAY,MAAM,WAAW,KAAK,MAAM,SAAS,KAAK;AACzE,UAAM,IAAI,UAAU,uEAAuE;AAAA,EAC7F;AACF;AAEA,SAAS,aAAa,UAAwC,KAA0B;AACtF,MAAI;AACF,WAAO,SAAS,GAAG,MAAM,gBAAgB,gBAAgB;AAAA,EAC3D,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,cAAc,KAAsB;AAC3C,QAAM,OAAO,eAAe,QAAQ,GAAG,IAAI,IAAI,KAAK,IAAI,OAAO,KAAK,OAAO,GAAG;AAC9E,SAAO,KAAK,MAAM,GAAG,GAAG;AAC1B;AAEA,SAAS,MAAM,IAA2B;AACxC,SAAO,IAAI,QAAQ,CAAC,MAAM,WAAW,GAAG,EAAE,CAAC;AAC7C;;;AGxkBO,IAAM,cAAN,MAAmC;AAAA,EACvB,OAAO,oBAAI,IAAoB;AAAA,EAEhD,MAAM,SAAY,OAAe,IAAiE;AAChG,UAAM,MAAM,KAAK,KAAK,IAAI,KAAK;AAC/B,UAAM,EAAE,MAAM,OAAO,IAAI,GAAG,QAAQ,SAAY,OAAQ,KAAK,MAAM,GAAG,CAAgB;AACtF,QAAI,KAAM,MAAK,KAAK,IAAI,OAAO,KAAK,UAAU,IAAI,CAAC;AACnD,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,KAAK,OAA2C;AACpD,UAAM,MAAM,KAAK,KAAK,IAAI,KAAK;AAC/B,WAAO,QAAQ,SAAY,OAAQ,KAAK,MAAM,GAAG;AAAA,EACnD;AAAA;AAAA,EAGA,IAAI,OAAe;AACjB,WAAO,KAAK,KAAK;AAAA,EACnB;AAAA,EAEA,QAAc;AACZ,SAAK,KAAK,MAAM;AAAA,EAClB;AACF;","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-
|
|
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-
|
|
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.
|
|
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)",
|