@demlik/tea 0.1.0 → 0.1.2

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.
Files changed (243) hide show
  1. package/LICENSE +21 -0
  2. package/dist/agent/index.d.ts +907 -0
  3. package/dist/agent/index.js +20 -0
  4. package/dist/agent/index.js.map +1 -0
  5. package/dist/authed-call/index.d.ts +119 -0
  6. package/dist/authed-call/index.js +148 -0
  7. package/dist/authed-call/index.js.map +1 -0
  8. package/dist/await-terminal/index.d.ts +99 -0
  9. package/dist/await-terminal/index.js +75 -0
  10. package/dist/await-terminal/index.js.map +1 -0
  11. package/dist/batch-window/index.d.ts +259 -0
  12. package/dist/batch-window/index.js +43 -0
  13. package/dist/batch-window/index.js.map +1 -0
  14. package/dist/cache/index.d.ts +2 -2
  15. package/dist/cache/index.js +2 -50
  16. package/dist/cache/index.js.map +1 -1
  17. package/dist/chunk-27N2SPTN.js +222 -0
  18. package/dist/chunk-27N2SPTN.js.map +1 -0
  19. package/dist/chunk-2YOE5PIS.js +45 -0
  20. package/dist/chunk-2YOE5PIS.js.map +1 -0
  21. package/dist/chunk-3545BOYL.js +65 -0
  22. package/dist/chunk-3545BOYL.js.map +1 -0
  23. package/dist/chunk-3XJWC7VW.js +61 -0
  24. package/dist/chunk-3XJWC7VW.js.map +1 -0
  25. package/dist/chunk-5DEQ5I3U.js +132 -0
  26. package/dist/chunk-5DEQ5I3U.js.map +1 -0
  27. package/dist/chunk-5VNWZADP.js +49 -0
  28. package/dist/chunk-5VNWZADP.js.map +1 -0
  29. package/dist/chunk-6RGBKODV.js +268 -0
  30. package/dist/chunk-6RGBKODV.js.map +1 -0
  31. package/dist/{chunk-ZRUST6BK.js → chunk-7FYQGDMF.js} +2 -2
  32. package/dist/chunk-7FYQGDMF.js.map +1 -0
  33. package/dist/chunk-7W4LMPUW.js +186 -0
  34. package/dist/chunk-7W4LMPUW.js.map +1 -0
  35. package/dist/chunk-BYQUGDD3.js +16 -0
  36. package/dist/chunk-BYQUGDD3.js.map +1 -0
  37. package/dist/{chunk-PYBQREFU.js → chunk-DILMKOZ7.js} +22 -26
  38. package/dist/chunk-DILMKOZ7.js.map +1 -0
  39. package/dist/chunk-EHIX7NYZ.js +93 -0
  40. package/dist/chunk-EHIX7NYZ.js.map +1 -0
  41. package/dist/chunk-HCEVHFJ7.js +54 -0
  42. package/dist/chunk-HCEVHFJ7.js.map +1 -0
  43. package/dist/chunk-HJO6VNQI.js +15 -0
  44. package/dist/chunk-HJO6VNQI.js.map +1 -0
  45. package/dist/chunk-IN33Q4VJ.js +52 -0
  46. package/dist/chunk-IN33Q4VJ.js.map +1 -0
  47. package/dist/chunk-J6527VRQ.js +56 -0
  48. package/dist/chunk-J6527VRQ.js.map +1 -0
  49. package/dist/chunk-JELSZN25.js +47 -0
  50. package/dist/chunk-JELSZN25.js.map +1 -0
  51. package/dist/chunk-K575PT4S.js +58 -0
  52. package/dist/chunk-K575PT4S.js.map +1 -0
  53. package/dist/chunk-K7Q23EQ4.js +20 -0
  54. package/dist/chunk-K7Q23EQ4.js.map +1 -0
  55. package/dist/chunk-LEC4U6CX.js +91 -0
  56. package/dist/chunk-LEC4U6CX.js.map +1 -0
  57. package/dist/{chunk-PYOCUBQJ.js → chunk-LP7VQBRG.js} +22 -15
  58. package/dist/chunk-LP7VQBRG.js.map +1 -0
  59. package/dist/{chunk-CMG2KMWX.js → chunk-MMH7CW5K.js} +220 -189
  60. package/dist/chunk-MMH7CW5K.js.map +1 -0
  61. package/dist/{chunk-Y5JM3UUH.js → chunk-NSDFA6M6.js} +75 -3
  62. package/dist/chunk-NSDFA6M6.js.map +1 -0
  63. package/dist/chunk-ON24RKSE.js +60 -0
  64. package/dist/chunk-ON24RKSE.js.map +1 -0
  65. package/dist/chunk-PS23H6KQ.js +87 -0
  66. package/dist/chunk-PS23H6KQ.js.map +1 -0
  67. package/dist/chunk-QLA76GIT.js +162 -0
  68. package/dist/chunk-QLA76GIT.js.map +1 -0
  69. package/dist/chunk-S3FQN4SL.js +506 -0
  70. package/dist/chunk-S3FQN4SL.js.map +1 -0
  71. package/dist/chunk-SC3FAFZN.js +53 -0
  72. package/dist/chunk-SC3FAFZN.js.map +1 -0
  73. package/dist/chunk-SINXE47Y.js +55 -0
  74. package/dist/chunk-SINXE47Y.js.map +1 -0
  75. package/dist/chunk-SQTCS44W.js +33 -0
  76. package/dist/chunk-SQTCS44W.js.map +1 -0
  77. package/dist/chunk-TGG26XMK.js +52 -0
  78. package/dist/chunk-TGG26XMK.js.map +1 -0
  79. package/dist/chunk-UV6UWGEO.js +147 -0
  80. package/dist/chunk-UV6UWGEO.js.map +1 -0
  81. package/dist/chunk-UYWUVEP6.js +158 -0
  82. package/dist/chunk-UYWUVEP6.js.map +1 -0
  83. package/dist/chunk-VLGARN6H.js +202 -0
  84. package/dist/chunk-VLGARN6H.js.map +1 -0
  85. package/dist/circuit-breaker/index.js +1 -51
  86. package/dist/circuit-breaker/index.js.map +1 -1
  87. package/dist/core-BoB6t7dB.d.ts +188 -0
  88. package/dist/deadline/index.d.ts +24 -5
  89. package/dist/deadline/index.js +4 -18
  90. package/dist/deadline/index.js.map +1 -1
  91. package/dist/debounce/index.d.ts +6 -0
  92. package/dist/debounce/index.js +1 -54
  93. package/dist/debounce/index.js.map +1 -1
  94. package/dist/devtools/index.d.ts +1 -1
  95. package/dist/devtools/index.js +18 -6
  96. package/dist/devtools/index.js.map +1 -1
  97. package/dist/do/index.d.ts +1723 -92
  98. package/dist/do/index.js +673 -31
  99. package/dist/do/index.js.map +1 -1
  100. package/dist/durable-effects-CO9Uh971.d.ts +180 -0
  101. package/dist/extension/index.d.ts +31 -13
  102. package/dist/extension/index.js +5 -3
  103. package/dist/extension/index.js.map +1 -1
  104. package/dist/extension/react.js +8 -3
  105. package/dist/extension/react.js.map +1 -1
  106. package/dist/extension/subs/index.d.ts +2 -2
  107. package/dist/extension/subs/index.js.map +1 -1
  108. package/dist/extension/test-utils.js +11 -3
  109. package/dist/extension/test-utils.js.map +1 -1
  110. package/dist/fan-out/index.d.ts +239 -0
  111. package/dist/fan-out/index.js +5 -0
  112. package/dist/fan-out/index.js.map +1 -0
  113. package/dist/idempotency/adapter.d.ts +62 -0
  114. package/dist/idempotency/adapter.js +4 -0
  115. package/dist/idempotency/adapter.js.map +1 -0
  116. package/dist/idempotency/index.js +1 -56
  117. package/dist/idempotency/index.js.map +1 -1
  118. package/dist/idempotent-intake/index.d.ts +186 -0
  119. package/dist/idempotent-intake/index.js +7 -0
  120. package/dist/idempotent-intake/index.js.map +1 -0
  121. package/dist/index.d.ts +341 -191
  122. package/dist/index.js +2 -1
  123. package/dist/llm-call/index.d.ts +309 -0
  124. package/dist/llm-call/index.js +14 -0
  125. package/dist/llm-call/index.js.map +1 -0
  126. package/dist/machine-viz/index.d.ts +4 -4
  127. package/dist/machine-viz/index.js +5 -9
  128. package/dist/machine-viz/index.js.map +1 -1
  129. package/dist/mem/index.d.ts +1 -0
  130. package/dist/monitored-run/index.d.ts +253 -0
  131. package/dist/monitored-run/index.js +10 -0
  132. package/dist/monitored-run/index.js.map +1 -0
  133. package/dist/node/index.d.ts +5 -5
  134. package/dist/node/index.js.map +1 -1
  135. package/dist/paginated-walk/index.d.ts +234 -0
  136. package/dist/paginated-walk/index.js +15 -0
  137. package/dist/paginated-walk/index.js.map +1 -0
  138. package/dist/paginator/index.d.ts +271 -0
  139. package/dist/paginator/index.js +3 -0
  140. package/dist/paginator/index.js.map +1 -0
  141. package/dist/parity/index.d.ts +92 -0
  142. package/dist/parity/index.js +78 -0
  143. package/dist/parity/index.js.map +1 -0
  144. package/dist/pbt/arbitraries/index.d.ts +1 -1
  145. package/dist/pbt/arbitraries/index.js +1 -1
  146. package/dist/pbt/index.d.ts +15 -8
  147. package/dist/pbt/index.js +6 -11
  148. package/dist/pbt/index.js.map +1 -1
  149. package/dist/pbt/runners/index.d.ts +1 -1
  150. package/dist/pbt/runners/index.js +3 -2
  151. package/dist/poller/index.d.ts +288 -0
  152. package/dist/poller/index.js +79 -0
  153. package/dist/poller/index.js.map +1 -0
  154. package/dist/prediction/index.d.ts +130 -0
  155. package/dist/prediction/index.js +4 -0
  156. package/dist/prediction/index.js.map +1 -0
  157. package/dist/protocol-B5YmJr0d.d.ts +45 -0
  158. package/dist/pure/index.d.ts +2 -0
  159. package/dist/pure/index.js +4 -0
  160. package/dist/pure/index.js.map +1 -0
  161. package/dist/raft/demo.d.ts +105 -0
  162. package/dist/raft/demo.js +172 -0
  163. package/dist/raft/demo.js.map +1 -0
  164. package/dist/raft/index.d.ts +548 -0
  165. package/dist/raft/index.js +7 -0
  166. package/dist/raft/index.js.map +1 -0
  167. package/dist/raft/sim.d.ts +222 -0
  168. package/dist/raft/sim.js +8 -0
  169. package/dist/raft/sim.js.map +1 -0
  170. package/dist/raft/timeline.d.ts +46 -0
  171. package/dist/raft/timeline.js +62 -0
  172. package/dist/raft/timeline.js.map +1 -0
  173. package/dist/rate-limit/index.js +1 -43
  174. package/dist/rate-limit/index.js.map +1 -1
  175. package/dist/react/index.d.ts +2 -1
  176. package/dist/react/index.js +43 -20
  177. package/dist/react/index.js.map +1 -1
  178. package/dist/reconciler/index.d.ts +192 -0
  179. package/dist/reconciler/index.js +166 -0
  180. package/dist/reconciler/index.js.map +1 -0
  181. package/dist/recorder/index.d.ts +7 -3
  182. package/dist/recorder/index.js +1 -155
  183. package/dist/recorder/index.js.map +1 -1
  184. package/dist/resilient-call/index.d.ts +294 -0
  185. package/dist/resilient-call/index.js +13 -0
  186. package/dist/resilient-call/index.js.map +1 -0
  187. package/dist/retry-backoff/index.d.ts +42 -5
  188. package/dist/retry-backoff/index.js +1 -35
  189. package/dist/retry-backoff/index.js.map +1 -1
  190. package/dist/retry-to-success/index.d.ts +109 -0
  191. package/dist/retry-to-success/index.js +35 -0
  192. package/dist/retry-to-success/index.js.map +1 -0
  193. package/dist/saga/index.d.ts +238 -0
  194. package/dist/saga/index.js +150 -0
  195. package/dist/saga/index.js.map +1 -0
  196. package/dist/snapshot/index.d.ts +334 -0
  197. package/dist/snapshot/index.js +5 -0
  198. package/dist/snapshot/index.js.map +1 -0
  199. package/dist/subs/index.d.ts +48 -10
  200. package/dist/subs/index.js +1 -1
  201. package/dist/testing/index.d.ts +107 -2
  202. package/dist/testing/index.js +204 -6
  203. package/dist/testing/index.js.map +1 -1
  204. package/dist/throttle/index.d.ts +9 -0
  205. package/dist/throttle/index.js +1 -52
  206. package/dist/throttle/index.js.map +1 -1
  207. package/dist/throttled-input/index.d.ts +380 -0
  208. package/dist/throttled-input/index.js +88 -0
  209. package/dist/throttled-input/index.js.map +1 -0
  210. package/dist/token-refresh/index.d.ts +215 -0
  211. package/dist/token-refresh/index.js +6 -0
  212. package/dist/token-refresh/index.js.map +1 -0
  213. package/dist/trace-replay/index.d.ts +11 -2
  214. package/dist/trace-replay/index.js +3 -55
  215. package/dist/trace-replay/index.js.map +1 -1
  216. package/dist/{types-CwBC5wMn.d.ts → types-B-8bcAp8.d.ts} +1 -1
  217. package/dist/with-deadline/index.d.ts +196 -0
  218. package/dist/with-deadline/index.js +111 -0
  219. package/dist/with-deadline/index.js.map +1 -0
  220. package/dist/with-resilience/index.d.ts +205 -0
  221. package/dist/with-resilience/index.js +251 -0
  222. package/dist/with-resilience/index.js.map +1 -0
  223. package/dist/with-telemetry/index.d.ts +149 -0
  224. package/dist/with-telemetry/index.js +70 -0
  225. package/dist/with-telemetry/index.js.map +1 -0
  226. package/dist/work-queue/adapter.d.ts +82 -0
  227. package/dist/work-queue/adapter.js +4 -0
  228. package/dist/work-queue/adapter.js.map +1 -0
  229. package/dist/work-queue/index.d.ts +25 -11
  230. package/dist/work-queue/index.js +1 -46
  231. package/dist/work-queue/index.js.map +1 -1
  232. package/dist/work-queue/ops.d.ts +69 -0
  233. package/dist/work-queue/ops.js +3 -0
  234. package/dist/work-queue/ops.js.map +1 -0
  235. package/dist/workflow/index.d.ts +450 -0
  236. package/dist/workflow/index.js +225 -0
  237. package/dist/workflow/index.js.map +1 -0
  238. package/package.json +168 -25
  239. package/dist/chunk-CMG2KMWX.js.map +0 -1
  240. package/dist/chunk-PYBQREFU.js.map +0 -1
  241. package/dist/chunk-PYOCUBQJ.js.map +0 -1
  242. package/dist/chunk-Y5JM3UUH.js.map +0 -1
  243. package/dist/chunk-ZRUST6BK.js.map +0 -1
package/dist/do/index.js CHANGED
@@ -1,54 +1,696 @@
1
- // src/do/index.ts
2
- var DEFAULT_STATE_KEY = "@@state";
3
- function doStore(storage, parse, key = DEFAULT_STATE_KEY) {
4
- return {
1
+ import { status, agentBootMsg, agentEvents } from '../chunk-S3FQN4SL.js';
2
+ import '../chunk-7W4LMPUW.js';
3
+ import '../chunk-PS23H6KQ.js';
4
+ export { applyEffectEvent, emptyLedger, foldLedger, isOwed, pendingEffectsLedger, survivingEffects } from '../chunk-3545BOYL.js';
5
+ import '../chunk-EHIX7NYZ.js';
6
+ import '../chunk-27N2SPTN.js';
7
+ import '../chunk-JELSZN25.js';
8
+ import '../chunk-UV6UWGEO.js';
9
+ import { createIntake } from '../chunk-LEC4U6CX.js';
10
+ import '../chunk-K7Q23EQ4.js';
11
+ import { defaultRetryPolicy, initRetry, recordFailure, shouldRetry, nextDelayMs } from '../chunk-5VNWZADP.js';
12
+ import '../chunk-2YOE5PIS.js';
13
+ import '../chunk-BYQUGDD3.js';
14
+ import '../chunk-K575PT4S.js';
15
+ import '../chunk-SC3FAFZN.js';
16
+ import '../chunk-IN33Q4VJ.js';
17
+ import '../chunk-HJO6VNQI.js';
18
+ import '../chunk-TGG26XMK.js';
19
+ import '../chunk-NSDFA6M6.js';
20
+ import '../chunk-2YFLOCWX.js';
21
+ import { run, replay } from '../chunk-MMH7CW5K.js';
22
+ import '../chunk-QLA76GIT.js';
23
+
24
+ // src/do/durable-timer.ts
25
+ function durableTimer(config) {
26
+ const { alarm, nextDeadline, onFire } = config;
27
+ const timer = {
28
+ async rearm() {
29
+ const at = await nextDeadline();
30
+ if (at !== null) await alarm.setAlarm(at);
31
+ },
32
+ async onAlarm() {
33
+ await onFire();
34
+ await timer.rearm();
35
+ }
36
+ };
37
+ return timer;
38
+ }
39
+
40
+ // src/do/event-sourced-store.ts
41
+ var DEFAULT_SNAPSHOT_KEY = "@@es/snapshot";
42
+ var DEFAULT_META_KEY = "@@es/meta";
43
+ var DEFAULT_EVENT_PREFIX = "@@es/evt/";
44
+ var SEQ_WIDTH = 16;
45
+ function eventKey(prefix, seq) {
46
+ return prefix + String(seq).padStart(SEQ_WIDTH, "0");
47
+ }
48
+ var defaultParse = (raw) => raw !== null && typeof raw === "object" && !Array.isArray(raw) ? raw : null;
49
+ var defaultParseEvent = (raw) => raw !== null && typeof raw === "object" && !Array.isArray(raw) && typeof raw.type === "string" ? raw : null;
50
+ function doEventSourcedStore(storage, machine, ctx, opts = {}) {
51
+ const snapshotEvery = opts.snapshotEvery ?? 100;
52
+ if (snapshotEvery < 1 || !Number.isInteger(snapshotEvery)) {
53
+ throw new Error(
54
+ `@demlik/tea/do: snapshotEvery must be an integer >= 1, got ${snapshotEvery}`
55
+ );
56
+ }
57
+ const parse = opts.parse ?? defaultParse;
58
+ const parseEvent = opts.parseEvent ?? defaultParseEvent;
59
+ const snapshotKey = opts.keys?.snapshot ?? DEFAULT_SNAPSHOT_KEY;
60
+ const metaKey = opts.keys?.meta ?? DEFAULT_META_KEY;
61
+ const eventPrefix = opts.keys?.eventPrefix ?? DEFAULT_EVENT_PREFIX;
62
+ let lastSeq = null;
63
+ let snapshotSeq = 0;
64
+ let latestState = null;
65
+ let readyFired = false;
66
+ async function readMeta() {
67
+ const raw = await storage.get(metaKey);
68
+ if (raw === void 0 || raw === null) return 0;
69
+ const meta = JSON.parse(raw);
70
+ return typeof meta.lastSeq === "number" ? meta.lastSeq : 0;
71
+ }
72
+ async function readSnapshot() {
73
+ const raw = await storage.get(snapshotKey);
74
+ if (raw === void 0 || raw === null) return { state: null, seq: 0 };
75
+ const cell = JSON.parse(raw);
76
+ const parsed = parse(cell.state);
77
+ if (parsed === null) return { state: null, seq: 0 };
78
+ const seq = typeof cell.seq === "number" ? cell.seq : 0;
79
+ return { state: parsed, seq };
80
+ }
81
+ async function* readEvents(range = {}) {
82
+ const fromSeq = range.fromSeq ?? Number.NEGATIVE_INFINITY;
83
+ const toSeq = range.toSeq ?? Number.POSITIVE_INFINITY;
84
+ const rows = await storage.list({ prefix: eventPrefix });
85
+ for (const [key, value] of rows) {
86
+ const seq = Number(key.slice(eventPrefix.length));
87
+ if (!Number.isFinite(seq) || seq < fromSeq || seq > toSeq) continue;
88
+ const decoded = parseEvent(JSON.parse(value));
89
+ if (decoded !== null) yield { seq, event: decoded };
90
+ }
91
+ }
92
+ async function readLog(afterSeq) {
93
+ const events = [];
94
+ for await (const { event } of readEvents({ fromSeq: afterSeq + 1 })) {
95
+ events.push(event);
96
+ }
97
+ return events;
98
+ }
99
+ const store = {
5
100
  async load() {
6
- const raw = await storage.get(key);
7
- if (raw === void 0 || raw === null) return null;
8
- return JSON.parse(raw);
101
+ const { state: snapshot, seq: snapSeq } = await readSnapshot();
102
+ snapshotSeq = snapSeq;
103
+ lastSeq = await readMeta();
104
+ const events = await readLog(snapSeq);
105
+ const { state } = replay(machine, {
106
+ msgs: events,
107
+ ctx,
108
+ loaded: snapshot
109
+ });
110
+ if (!readyFired) {
111
+ readyFired = true;
112
+ opts.onReady?.(state);
113
+ }
114
+ return state;
9
115
  },
10
116
  async save(state) {
11
- await storage.put(key, JSON.stringify(state));
117
+ latestState = state;
12
118
  },
13
119
  migrate(raw) {
14
120
  return parse(raw);
15
121
  }
16
122
  };
123
+ async function writeSnapshot(seq, state) {
124
+ const cell = { seq, state };
125
+ await storage.put(snapshotKey, JSON.stringify(cell));
126
+ snapshotSeq = seq;
127
+ }
128
+ async function append(msg) {
129
+ if (lastSeq === null) lastSeq = await readMeta();
130
+ const seq = lastSeq + 1;
131
+ lastSeq = seq;
132
+ await storage.put(eventKey(eventPrefix, seq), JSON.stringify(msg));
133
+ await storage.put(
134
+ metaKey,
135
+ JSON.stringify({ lastSeq: seq })
136
+ );
137
+ if (seq - snapshotSeq >= snapshotEvery && latestState !== null) {
138
+ await writeSnapshot(seq, latestState);
139
+ }
140
+ }
141
+ async function snapshotNow() {
142
+ if (lastSeq === null) lastSeq = await readMeta();
143
+ if (latestState === null || lastSeq <= snapshotSeq) return false;
144
+ await writeSnapshot(lastSeq, latestState);
145
+ return true;
146
+ }
147
+ return { store, append, snapshotNow, readEvents };
17
148
  }
18
- function doSubscribe() {
149
+
150
+ // src/do/host.ts
151
+ function deferredGateway() {
152
+ const pending = /* @__PURE__ */ new Map();
19
153
  return {
20
- do_alarm: (sub, ctx, _dispatch) => {
21
- ctx.alarmRegistry.set(sub.id, { firesAt: sub.firesAt, msg: sub.msg });
22
- const earliest = earliestFiresAt(ctx.alarmRegistry);
23
- if (earliest !== null) {
24
- ctx.state.storage.setAlarm(earliest).catch((err) => console.error("[tea-do] setAlarm failed", err));
154
+ await(callId, send, deadlineMs) {
155
+ const existing = pending.get(callId);
156
+ if (existing !== void 0) return existing.promise;
157
+ let resolve;
158
+ let reject;
159
+ const promise = new Promise((res, rej) => {
160
+ resolve = res;
161
+ reject = rej;
162
+ });
163
+ const timer = setTimeout(() => {
164
+ if (pending.delete(callId)) {
165
+ reject(new Error(`deadline_exceeded after ${deadlineMs}ms`));
166
+ }
167
+ }, deadlineMs);
168
+ pending.set(callId, {
169
+ resolve: (v) => {
170
+ clearTimeout(timer);
171
+ resolve(v);
172
+ },
173
+ reject: (e) => {
174
+ clearTimeout(timer);
175
+ reject(e);
176
+ },
177
+ promise
178
+ });
179
+ send();
180
+ return promise;
181
+ },
182
+ settle(callId, result) {
183
+ const entry = pending.get(callId);
184
+ if (entry === void 0) return;
185
+ pending.delete(callId);
186
+ entry.resolve(result);
187
+ },
188
+ fail(callId, reason) {
189
+ const entry = pending.get(callId);
190
+ if (entry === void 0) return;
191
+ pending.delete(callId);
192
+ entry.reject(new Error(reason));
193
+ },
194
+ inFlight() {
195
+ return [...pending.keys()];
196
+ }
197
+ };
198
+ }
199
+ function durableDeferredGateway(inner, recorder, hooks) {
200
+ const idByCallId = /* @__PURE__ */ new Map();
201
+ return {
202
+ recorder,
203
+ await(callId, send, deadlineMs) {
204
+ if (!idByCallId.has(callId)) {
205
+ const { id, event } = recorder.owe({ callId });
206
+ idByCallId.set(callId, id);
207
+ hooks.recordOwed(callId, event);
25
208
  }
26
- return () => {
27
- ctx.alarmRegistry.delete(sub.id);
28
- const nextEarliest = earliestFiresAt(ctx.alarmRegistry);
29
- if (nextEarliest === null) {
30
- ctx.state.storage.deleteAlarm().catch((err) => console.error("[tea-do] deleteAlarm failed", err));
31
- } else {
32
- ctx.state.storage.setAlarm(nextEarliest).catch((err) => console.error("[tea-do] setAlarm failed", err));
209
+ return inner.await(callId, send, deadlineMs);
210
+ },
211
+ settle(callId, result) {
212
+ confirm(callId);
213
+ inner.settle(callId, result);
214
+ },
215
+ fail(callId, reason) {
216
+ confirm(callId);
217
+ inner.fail(callId, reason);
218
+ },
219
+ inFlight() {
220
+ return inner.inFlight();
221
+ }
222
+ };
223
+ function confirm(callId) {
224
+ const id = idByCallId.get(callId);
225
+ if (id === void 0) return;
226
+ idByCallId.delete(callId);
227
+ const { event } = recorder.confirm(id);
228
+ hooks.recordConfirmed(callId, event);
229
+ }
230
+ }
231
+ async function bootResume(booting, port, now = Date.now) {
232
+ const runtime = await booting.ready;
233
+ if (port.isResumable(runtime.getState())) {
234
+ await runtime.dispatch(port.resumeMsg(now()));
235
+ }
236
+ }
237
+ function agentIsResumable(state) {
238
+ return status(state).kind === "suspended";
239
+ }
240
+ async function autoBoot(booting, now = Date.now) {
241
+ await bootResume(
242
+ booting,
243
+ { isResumable: agentIsResumable, resumeMsg: agentBootMsg },
244
+ now
245
+ );
246
+ }
247
+ function acceptCommandSocket(clients, onFrame) {
248
+ const pair = new WebSocketPair();
249
+ const [client, server] = [pair[0], pair[1]];
250
+ server.accept();
251
+ clients.add(server);
252
+ server.addEventListener("message", (ev) => {
253
+ if (typeof ev.data === "string") onFrame(ev.data);
254
+ });
255
+ server.addEventListener("close", () => clients.delete(server));
256
+ return new Response(null, { status: 101, webSocket: client });
257
+ }
258
+ function broadcast(clients, frame) {
259
+ const json = JSON.stringify(frame);
260
+ for (const ws of clients) {
261
+ try {
262
+ ws.send(json);
263
+ } catch {
264
+ }
265
+ }
266
+ }
267
+ function acceptDurableCommandSocket(ctx) {
268
+ const pair = new WebSocketPair();
269
+ const [client, server] = [pair[0], pair[1]];
270
+ ctx.acceptWebSocket(server);
271
+ return new Response(null, { status: 101, webSocket: client });
272
+ }
273
+ function broadcastHibernatable(ctx, frame) {
274
+ const json = JSON.stringify(frame);
275
+ for (const ws of ctx.getWebSockets()) {
276
+ try {
277
+ ws.send(json);
278
+ } catch {
279
+ }
280
+ }
281
+ }
282
+ function durableCommandCarrier(inner, recorder, hooks) {
283
+ return durableDeferredGateway(inner, recorder, hooks);
284
+ }
285
+ function reissueSurvivingEffects(carrier, reissue) {
286
+ const reemitted = [];
287
+ for (const { effect } of carrier.recorder.surviving()) {
288
+ reissue(effect.callId);
289
+ reemitted.push(effect.callId);
290
+ }
291
+ return reemitted;
292
+ }
293
+ function sseHub() {
294
+ const sinks = /* @__PURE__ */ new Set();
295
+ const encoder = new TextEncoder();
296
+ return {
297
+ emit(event) {
298
+ for (const sink of sinks) {
299
+ try {
300
+ sink(event);
301
+ } catch {
33
302
  }
34
- };
303
+ }
304
+ },
305
+ open() {
306
+ let sink;
307
+ const stream = new ReadableStream({
308
+ start: (controller) => {
309
+ sink = (event) => {
310
+ controller.enqueue(
311
+ encoder.encode(`data: ${JSON.stringify(event)}
312
+
313
+ `)
314
+ );
315
+ };
316
+ sinks.add(sink);
317
+ },
318
+ cancel: () => {
319
+ sinks.delete(sink);
320
+ }
321
+ });
322
+ return new Response(stream, {
323
+ headers: {
324
+ "content-type": "text/event-stream",
325
+ "cache-control": "no-cache",
326
+ connection: "keep-alive"
327
+ }
328
+ });
35
329
  },
36
- do_ws: (sub, ctx, _dispatch) => {
37
- ctx.wsRegistry.set(sub.id, { socketId: sub.socketId, msg: sub.msg });
330
+ register(sink) {
331
+ sinks.add(sink);
38
332
  return () => {
39
- ctx.wsRegistry.delete(sub.id);
333
+ sinks.delete(sink);
40
334
  };
41
335
  }
42
336
  };
43
337
  }
44
- function earliestFiresAt(registry) {
45
- let earliest = null;
46
- for (const { firesAt } of registry.values()) {
47
- if (earliest === null || firesAt < earliest) earliest = firesAt;
338
+ function sseProjection(hub, toEvent, id = {}) {
339
+ return {
340
+ id: { name: id.name ?? "sse", key: id.key ?? "main" },
341
+ initial: null,
342
+ apply(_view, update) {
343
+ return toEvent(update.msg, update.model);
344
+ },
345
+ emit(view) {
346
+ if (view !== null) hub.emit(view);
347
+ }
348
+ };
349
+ }
350
+ function sseFromAgentEvents(runtime, hub, toFrame) {
351
+ const push = (event) => {
352
+ const frame = toFrame(event);
353
+ if (frame !== null) hub.emit(frame);
354
+ };
355
+ const offs = [
356
+ runtime.on("TurnSettled", push),
357
+ runtime.on("ToolSettled", push),
358
+ runtime.on("RunDone", push)
359
+ ];
360
+ return () => {
361
+ for (const off of offs) off();
362
+ };
363
+ }
364
+ function createAgentHost(config) {
365
+ const sse = sseHub();
366
+ const now = config.now ?? Date.now;
367
+ const isTerminal = config.terminal ?? ((s) => s.run.phase === "done" || s.run.phase === "failed");
368
+ let cached = null;
369
+ async function runtime() {
370
+ if (cached !== null) return cached;
371
+ const booting = run(config.buildMachine(), {
372
+ ctx: config.ctx,
373
+ store: config.store,
374
+ events: agentEvents(),
375
+ terminal: isTerminal
376
+ });
377
+ sseFromAgentEvents(booting, sse, config.toSseFrame);
378
+ await autoBoot(booting, now);
379
+ cached = await booting.ready;
380
+ return cached;
381
+ }
382
+ return {
383
+ runtime,
384
+ async status() {
385
+ return status((await runtime()).getState());
386
+ },
387
+ async result() {
388
+ return (await runtime()).result();
389
+ },
390
+ sse,
391
+ async reset() {
392
+ if (cached === null) return;
393
+ await cached.stop();
394
+ cached = null;
395
+ }
396
+ };
397
+ }
398
+
399
+ // src/do/idempotent-effect.ts
400
+ function emptyApplied() {
401
+ return /* @__PURE__ */ new Set();
402
+ }
403
+ function applyAppliedEvent(applied, event) {
404
+ switch (event.type) {
405
+ case "effect_applied": {
406
+ if (applied.has(event.key)) return applied;
407
+ const next = new Set(applied);
408
+ next.add(event.key);
409
+ return next;
410
+ }
411
+ case "effect_forgotten": {
412
+ if (!applied.has(event.key)) return applied;
413
+ const next = new Set(applied);
414
+ next.delete(event.key);
415
+ return next;
416
+ }
417
+ }
418
+ }
419
+ function foldApplied(events) {
420
+ let applied = emptyApplied();
421
+ for (const event of events) applied = applyAppliedEvent(applied, event);
422
+ return applied;
423
+ }
424
+ function isApplied(applied, key) {
425
+ return applied.has(key);
426
+ }
427
+ function idempotentEffect(key, fn) {
428
+ return { key, fn };
429
+ }
430
+ function appliedEffects(restore) {
431
+ let mirror = restore?.events ? foldApplied(restore.events) : emptyApplied();
432
+ return {
433
+ async run(effect) {
434
+ if (mirror.has(effect.key)) {
435
+ return { ran: false, key: effect.key };
436
+ }
437
+ const result = await effect.fn();
438
+ const event = { type: "effect_applied", key: effect.key };
439
+ mirror = applyAppliedEvent(mirror, event);
440
+ return { ran: true, key: effect.key, result, event };
441
+ },
442
+ forget(key) {
443
+ const event = { type: "effect_forgotten", key };
444
+ mirror = applyAppliedEvent(mirror, event);
445
+ return { event };
446
+ },
447
+ applied() {
448
+ return mirror;
449
+ },
450
+ isApplied(key) {
451
+ return mirror.has(key);
452
+ }
453
+ };
454
+ }
455
+
456
+ // src/do/presence.ts
457
+ var WS_READY_STATE_OPEN = 1;
458
+ function broadcastFrame(sockets, frame, options) {
459
+ const serialize = options?.serialize ?? JSON.stringify;
460
+ const except = options?.except;
461
+ const json = serialize(frame);
462
+ let sent = 0;
463
+ let skipped = 0;
464
+ for (const ws of sockets) {
465
+ if (except !== void 0 && ws === except) {
466
+ skipped++;
467
+ continue;
468
+ }
469
+ if (ws.readyState !== void 0 && ws.readyState !== WS_READY_STATE_OPEN) {
470
+ skipped++;
471
+ continue;
472
+ }
473
+ try {
474
+ ws.send(json);
475
+ sent++;
476
+ } catch {
477
+ skipped++;
478
+ }
479
+ }
480
+ return { sent, skipped };
481
+ }
482
+ function registerHibernatableSocket(ctx, server, options) {
483
+ ctx.acceptWebSocket(
484
+ server,
485
+ options?.tags === void 0 ? void 0 : [...options.tags]
486
+ );
487
+ if (options?.attachment !== void 0) {
488
+ server.serializeAttachment(options.attachment);
489
+ }
490
+ }
491
+ function acceptPresenceSocket(ctx, options) {
492
+ const pair = new WebSocketPair();
493
+ const client = pair[0];
494
+ const server = pair[1];
495
+ registerHibernatableSocket(ctx, server, options);
496
+ return {
497
+ server,
498
+ response: new Response(null, { status: 101, webSocket: client })
499
+ };
500
+ }
501
+ function presenceCount(ctx, tag) {
502
+ return ctx.getWebSockets(tag).length;
503
+ }
504
+
505
+ // src/do/projection.ts
506
+ function projectionIdString(id) {
507
+ return `${id.name}-${id.key}`;
508
+ }
509
+ function runProjection(projection, startOffset = 0) {
510
+ let view = projection.initial;
511
+ let storedOffset = startOffset;
512
+ return {
513
+ id: projection.id,
514
+ view: () => view,
515
+ offset: () => storedOffset,
516
+ present(update) {
517
+ if (update.offset <= storedOffset) return false;
518
+ const next = projection.apply(view, update);
519
+ view = next;
520
+ storedOffset = update.offset;
521
+ projection.emit(next);
522
+ return true;
523
+ },
524
+ reset() {
525
+ view = projection.initial;
526
+ storedOffset = 0;
527
+ }
528
+ };
529
+ }
530
+ function projectionRegistry() {
531
+ const runners = /* @__PURE__ */ new Set();
532
+ let counter = -1;
533
+ return {
534
+ register(projection, startOffset) {
535
+ const runner = runProjection(projection, startOffset);
536
+ runners.add(runner);
537
+ return runner;
538
+ },
539
+ dispatch(msg, model) {
540
+ const offset = ++counter;
541
+ const update = { msg, model, offset };
542
+ for (const runner of runners) {
543
+ try {
544
+ runner.present(update);
545
+ } catch {
546
+ }
547
+ }
548
+ return offset;
549
+ },
550
+ offset: () => counter,
551
+ runners: () => [...runners]
552
+ };
553
+ }
554
+ function driveProjections(registry, runtime) {
555
+ const unboot = runtime.onBoot((model) => {
556
+ registry.dispatch(null, model);
557
+ });
558
+ const unobserve = runtime.observe((msg, model) => {
559
+ registry.dispatch(msg, model);
560
+ });
561
+ return () => {
562
+ unobserve();
563
+ unboot();
564
+ };
565
+ }
566
+ function rebuildProjection(projection, updates) {
567
+ const runner = runProjection(projection, 0);
568
+ updates.forEach((u, i) => {
569
+ runner.present({ msg: u.msg, model: u.model, offset: i + 1 });
570
+ });
571
+ return runner;
572
+ }
573
+
574
+ // src/do/step-host.ts
575
+ function mintRunToken(randomBytes = defaultRandomBytes) {
576
+ const bytes = randomBytes(32);
577
+ let hex = "";
578
+ for (const b of bytes) hex += b.toString(16).padStart(2, "0");
579
+ return hex;
580
+ }
581
+ function defaultRandomBytes(n) {
582
+ const out = new Uint8Array(n);
583
+ crypto.getRandomValues(out);
584
+ return out;
585
+ }
586
+ function constantTimeEqual(a, b) {
587
+ const lengthMatches = a.length === b.length;
588
+ const rhs = lengthMatches ? b : a;
589
+ let diff = 0;
590
+ for (let i = 0; i < a.length; i++) {
591
+ diff |= a.charCodeAt(i) ^ rhs.charCodeAt(i);
592
+ }
593
+ return diff === 0 && lengthMatches;
594
+ }
595
+ var DEFAULT_GIVE_UP_MS = 5 * 6e4;
596
+ function stepHost(ctx, engine, config = {}) {
597
+ const giveUpAfterMs = config.giveUpAfterMs ?? DEFAULT_GIVE_UP_MS;
598
+ const intake = createIntake({
599
+ keyOf: (posted) => posted.callId
600
+ });
601
+ let intakeState = intake.init();
602
+ async function handle(request, now) {
603
+ const stored = await engine.tokenFor(request.runId);
604
+ if (stored === null) {
605
+ return { status: 404, body: { error: "unknown run" } };
606
+ }
607
+ if (!constantTimeEqual(request.token, stored)) {
608
+ return { status: 401, body: { error: "invalid token" } };
609
+ }
610
+ await ctx.storage.setAlarm(now + giveUpAfterMs);
611
+ const posted = request.posted;
612
+ if (posted !== null) {
613
+ const [nextIntake, cmds] = intake.receive(
614
+ intakeState,
615
+ posted,
616
+ now,
617
+ posted.callId
618
+ );
619
+ intakeState = nextIntake;
620
+ for (const cmd of cmds) {
621
+ if (cmd.type === "intake:process") {
622
+ await engine.resume(request.runId, posted);
623
+ const [completed] = intake.complete(
624
+ intakeState,
625
+ cmd.key,
626
+ "settled",
627
+ now
628
+ );
629
+ intakeState = completed;
630
+ }
631
+ }
632
+ }
633
+ const next = await engine.nextStep(request.runId);
634
+ if (next === null) {
635
+ const output = await engine.terminalOutput(request.runId);
636
+ return { status: 200, body: { done: true, output } };
637
+ }
638
+ return { status: 200, body: { done: false, step: next } };
48
639
  }
49
- return earliest;
640
+ return { handle };
641
+ }
642
+ async function runStepLoop(token, runId, transport, execute, config) {
643
+ const policy = config.retry ?? defaultRetryPolicy;
644
+ const now = config.now ?? Date.now;
645
+ const sleep = config.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
646
+ const rng = config.rng;
647
+ const executed = /* @__PURE__ */ new Map();
648
+ let posted = null;
649
+ for (; ; ) {
650
+ if (now() >= config.deadlineMs) return { kind: "deadline_exceeded" };
651
+ let response;
652
+ let retry = initRetry();
653
+ for (; ; ) {
654
+ try {
655
+ response = await transport({ token, runId, posted });
656
+ break;
657
+ } catch (error) {
658
+ retry = recordFailure(retry, error);
659
+ if (!shouldRetry(retry, policy)) return { kind: "gave_up", error };
660
+ if (now() >= config.deadlineMs) return { kind: "deadline_exceeded" };
661
+ await sleep(nextDelayMs(retry, policy, rng));
662
+ }
663
+ }
664
+ if (response.done) return { kind: "done", output: response.output };
665
+ const step = response.step;
666
+ const cached = executed.get(step.callId);
667
+ const result = cached !== void 0 ? cached : await execute(step);
668
+ if (cached === void 0) executed.set(step.callId, result);
669
+ posted = { callId: step.callId, result };
670
+ }
671
+ }
672
+
673
+ // src/do/index.ts
674
+ var DEFAULT_STATE_KEY = "@@state";
675
+ function doStore(storage, parse, keyOrOptions = DEFAULT_STATE_KEY) {
676
+ const options = typeof keyOrOptions === "string" ? { key: keyOrOptions } : keyOrOptions;
677
+ const key = options.key ?? DEFAULT_STATE_KEY;
678
+ const serialize = options.serialize ?? ((state) => state);
679
+ return {
680
+ async load() {
681
+ const raw = await storage.get(key);
682
+ if (raw === void 0 || raw === null) return null;
683
+ return JSON.parse(raw);
684
+ },
685
+ async save(state) {
686
+ await storage.put(key, JSON.stringify(serialize(state)));
687
+ },
688
+ migrate(raw) {
689
+ return parse(raw);
690
+ }
691
+ };
50
692
  }
51
693
 
52
- export { doStore, doSubscribe };
694
+ export { WS_READY_STATE_OPEN, acceptCommandSocket, acceptDurableCommandSocket, acceptPresenceSocket, agentIsResumable, appliedEffects, applyAppliedEvent, autoBoot, bootResume, broadcast, broadcastFrame, broadcastHibernatable, constantTimeEqual, createAgentHost, deferredGateway, doEventSourcedStore, doStore, driveProjections, durableCommandCarrier, durableDeferredGateway, durableTimer, emptyApplied, foldApplied, idempotentEffect, isApplied, mintRunToken, presenceCount, projectionIdString, projectionRegistry, rebuildProjection, registerHibernatableSocket, reissueSurvivingEffects, runProjection, runStepLoop, sseFromAgentEvents, sseHub, sseProjection, stepHost };
53
695
  //# sourceMappingURL=index.js.map
54
696
  //# sourceMappingURL=index.js.map