@autonome-research/thread-phase 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (263) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +226 -0
  3. package/dist/agent/index.d.ts +28 -0
  4. package/dist/agent/index.d.ts.map +1 -0
  5. package/dist/agent/index.js +28 -0
  6. package/dist/agent/index.js.map +1 -0
  7. package/dist/agent/openai-adapter.d.ts +15 -0
  8. package/dist/agent/openai-adapter.d.ts.map +1 -0
  9. package/dist/agent/openai-adapter.js +57 -0
  10. package/dist/agent/openai-adapter.js.map +1 -0
  11. package/dist/agent/parse-json.d.ts +12 -0
  12. package/dist/agent/parse-json.d.ts.map +1 -0
  13. package/dist/agent/parse-json.js +31 -0
  14. package/dist/agent/parse-json.js.map +1 -0
  15. package/dist/agent/retry.d.ts +15 -0
  16. package/dist/agent/retry.d.ts.map +1 -0
  17. package/dist/agent/retry.js +35 -0
  18. package/dist/agent/retry.js.map +1 -0
  19. package/dist/agent/runner.d.ts +25 -0
  20. package/dist/agent/runner.d.ts.map +1 -0
  21. package/dist/agent/runner.js +270 -0
  22. package/dist/agent/runner.js.map +1 -0
  23. package/dist/agent/stream-consumer.d.ts +57 -0
  24. package/dist/agent/stream-consumer.d.ts.map +1 -0
  25. package/dist/agent/stream-consumer.js +126 -0
  26. package/dist/agent/stream-consumer.js.map +1 -0
  27. package/dist/agent/types.d.ts +135 -0
  28. package/dist/agent/types.d.ts.map +1 -0
  29. package/dist/agent/types.js +9 -0
  30. package/dist/agent/types.js.map +1 -0
  31. package/dist/agent-runner.d.ts +10 -0
  32. package/dist/agent-runner.d.ts.map +1 -0
  33. package/dist/agent-runner.js +10 -0
  34. package/dist/agent-runner.js.map +1 -0
  35. package/dist/agents/capability.d.ts +36 -0
  36. package/dist/agents/capability.d.ts.map +1 -0
  37. package/dist/agents/capability.js +51 -0
  38. package/dist/agents/capability.js.map +1 -0
  39. package/dist/agents/event-bus.d.ts +20 -0
  40. package/dist/agents/event-bus.d.ts.map +1 -0
  41. package/dist/agents/event-bus.js +40 -0
  42. package/dist/agents/event-bus.js.map +1 -0
  43. package/dist/agents/index.d.ts +23 -0
  44. package/dist/agents/index.d.ts.map +1 -0
  45. package/dist/agents/index.js +33 -0
  46. package/dist/agents/index.js.map +1 -0
  47. package/dist/agents/inference-adapter.d.ts +52 -0
  48. package/dist/agents/inference-adapter.d.ts.map +1 -0
  49. package/dist/agents/inference-adapter.js +209 -0
  50. package/dist/agents/inference-adapter.js.map +1 -0
  51. package/dist/agents/job-store-bridge.d.ts +44 -0
  52. package/dist/agents/job-store-bridge.d.ts.map +1 -0
  53. package/dist/agents/job-store-bridge.js +58 -0
  54. package/dist/agents/job-store-bridge.js.map +1 -0
  55. package/dist/agents/memory.d.ts +40 -0
  56. package/dist/agents/memory.d.ts.map +1 -0
  57. package/dist/agents/memory.js +14 -0
  58. package/dist/agents/memory.js.map +1 -0
  59. package/dist/agents/protocol.d.ts +302 -0
  60. package/dist/agents/protocol.d.ts.map +1 -0
  61. package/dist/agents/protocol.js +36 -0
  62. package/dist/agents/protocol.js.map +1 -0
  63. package/dist/agents/run-helpers.d.ts +70 -0
  64. package/dist/agents/run-helpers.d.ts.map +1 -0
  65. package/dist/agents/run-helpers.js +131 -0
  66. package/dist/agents/run-helpers.js.map +1 -0
  67. package/dist/agents/serialize-error.d.ts +18 -0
  68. package/dist/agents/serialize-error.d.ts.map +1 -0
  69. package/dist/agents/serialize-error.js +27 -0
  70. package/dist/agents/serialize-error.js.map +1 -0
  71. package/dist/agents/structured-output.d.ts +90 -0
  72. package/dist/agents/structured-output.d.ts.map +1 -0
  73. package/dist/agents/structured-output.js +101 -0
  74. package/dist/agents/structured-output.js.map +1 -0
  75. package/dist/agents/test-utils/conformance.d.ts +59 -0
  76. package/dist/agents/test-utils/conformance.d.ts.map +1 -0
  77. package/dist/agents/test-utils/conformance.js +207 -0
  78. package/dist/agents/test-utils/conformance.js.map +1 -0
  79. package/dist/agents/test-utils/index.d.ts +12 -0
  80. package/dist/agents/test-utils/index.d.ts.map +1 -0
  81. package/dist/agents/test-utils/index.js +12 -0
  82. package/dist/agents/test-utils/index.js.map +1 -0
  83. package/dist/agents/test-utils/mock-agent.d.ts +66 -0
  84. package/dist/agents/test-utils/mock-agent.d.ts.map +1 -0
  85. package/dist/agents/test-utils/mock-agent.js +244 -0
  86. package/dist/agents/test-utils/mock-agent.js.map +1 -0
  87. package/dist/agents/thread.d.ts +57 -0
  88. package/dist/agents/thread.d.ts.map +1 -0
  89. package/dist/agents/thread.js +128 -0
  90. package/dist/agents/thread.js.map +1 -0
  91. package/dist/agents/turn-accumulator.d.ts +94 -0
  92. package/dist/agents/turn-accumulator.d.ts.map +1 -0
  93. package/dist/agents/turn-accumulator.js +150 -0
  94. package/dist/agents/turn-accumulator.js.map +1 -0
  95. package/dist/agents/with-memory.d.ts +55 -0
  96. package/dist/agents/with-memory.d.ts.map +1 -0
  97. package/dist/agents/with-memory.js +155 -0
  98. package/dist/agents/with-memory.js.map +1 -0
  99. package/dist/agents/with-thread.d.ts +45 -0
  100. package/dist/agents/with-thread.d.ts.map +1 -0
  101. package/dist/agents/with-thread.js +70 -0
  102. package/dist/agents/with-thread.js.map +1 -0
  103. package/dist/cache.d.ts +47 -0
  104. package/dist/cache.d.ts.map +1 -0
  105. package/dist/cache.js +81 -0
  106. package/dist/cache.js.map +1 -0
  107. package/dist/context/compressor.d.ts +36 -0
  108. package/dist/context/compressor.d.ts.map +1 -0
  109. package/dist/context/compressor.js +158 -0
  110. package/dist/context/compressor.js.map +1 -0
  111. package/dist/context/index.d.ts +4 -0
  112. package/dist/context/index.d.ts.map +1 -0
  113. package/dist/context/index.js +4 -0
  114. package/dist/context/index.js.map +1 -0
  115. package/dist/context/result-capper.d.ts +32 -0
  116. package/dist/context/result-capper.d.ts.map +1 -0
  117. package/dist/context/result-capper.js +50 -0
  118. package/dist/context/result-capper.js.map +1 -0
  119. package/dist/context/token-budget.d.ts +81 -0
  120. package/dist/context/token-budget.d.ts.map +1 -0
  121. package/dist/context/token-budget.js +99 -0
  122. package/dist/context/token-budget.js.map +1 -0
  123. package/dist/helpers/caller.d.ts +18 -0
  124. package/dist/helpers/caller.d.ts.map +1 -0
  125. package/dist/helpers/caller.js +40 -0
  126. package/dist/helpers/caller.js.map +1 -0
  127. package/dist/helpers/hook.d.ts +73 -0
  128. package/dist/helpers/hook.d.ts.map +1 -0
  129. package/dist/helpers/hook.js +244 -0
  130. package/dist/helpers/hook.js.map +1 -0
  131. package/dist/helpers/index.d.ts +12 -0
  132. package/dist/helpers/index.d.ts.map +1 -0
  133. package/dist/helpers/index.js +11 -0
  134. package/dist/helpers/index.js.map +1 -0
  135. package/dist/helpers/one-shot.d.ts +27 -0
  136. package/dist/helpers/one-shot.d.ts.map +1 -0
  137. package/dist/helpers/one-shot.js +43 -0
  138. package/dist/helpers/one-shot.js.map +1 -0
  139. package/dist/helpers/schedule.d.ts +59 -0
  140. package/dist/helpers/schedule.d.ts.map +1 -0
  141. package/dist/helpers/schedule.js +118 -0
  142. package/dist/helpers/schedule.js.map +1 -0
  143. package/dist/helpers/types.d.ts +34 -0
  144. package/dist/helpers/types.d.ts.map +1 -0
  145. package/dist/helpers/types.js +11 -0
  146. package/dist/helpers/types.js.map +1 -0
  147. package/dist/index.d.ts +26 -0
  148. package/dist/index.d.ts.map +1 -0
  149. package/dist/index.js +37 -0
  150. package/dist/index.js.map +1 -0
  151. package/dist/inference.d.ts +27 -0
  152. package/dist/inference.d.ts.map +1 -0
  153. package/dist/inference.js +34 -0
  154. package/dist/inference.js.map +1 -0
  155. package/dist/messages.d.ts +64 -0
  156. package/dist/messages.d.ts.map +1 -0
  157. package/dist/messages.js +17 -0
  158. package/dist/messages.js.map +1 -0
  159. package/dist/orchestrator.d.ts +56 -0
  160. package/dist/orchestrator.d.ts.map +1 -0
  161. package/dist/orchestrator.js +62 -0
  162. package/dist/orchestrator.js.map +1 -0
  163. package/dist/patterns/bounded-fanout-of.d.ts +61 -0
  164. package/dist/patterns/bounded-fanout-of.d.ts.map +1 -0
  165. package/dist/patterns/bounded-fanout-of.js +142 -0
  166. package/dist/patterns/bounded-fanout-of.js.map +1 -0
  167. package/dist/patterns/bounded-fanout.d.ts +111 -0
  168. package/dist/patterns/bounded-fanout.d.ts.map +1 -0
  169. package/dist/patterns/bounded-fanout.js +151 -0
  170. package/dist/patterns/bounded-fanout.js.map +1 -0
  171. package/dist/patterns/index.d.ts +14 -0
  172. package/dist/patterns/index.d.ts.map +1 -0
  173. package/dist/patterns/index.js +13 -0
  174. package/dist/patterns/index.js.map +1 -0
  175. package/dist/patterns/intent-gate.d.ts +27 -0
  176. package/dist/patterns/intent-gate.d.ts.map +1 -0
  177. package/dist/patterns/intent-gate.js +32 -0
  178. package/dist/patterns/intent-gate.js.map +1 -0
  179. package/dist/patterns/match.d.ts +30 -0
  180. package/dist/patterns/match.d.ts.map +1 -0
  181. package/dist/patterns/match.js +58 -0
  182. package/dist/patterns/match.js.map +1 -0
  183. package/dist/patterns/parallel-fanout.d.ts +28 -0
  184. package/dist/patterns/parallel-fanout.d.ts.map +1 -0
  185. package/dist/patterns/parallel-fanout.js +24 -0
  186. package/dist/patterns/parallel-fanout.js.map +1 -0
  187. package/dist/patterns/parallel-phases.d.ts +27 -0
  188. package/dist/patterns/parallel-phases.d.ts.map +1 -0
  189. package/dist/patterns/parallel-phases.js +77 -0
  190. package/dist/patterns/parallel-phases.js.map +1 -0
  191. package/dist/patterns/preflight-confidence.d.ts +20 -0
  192. package/dist/patterns/preflight-confidence.d.ts.map +1 -0
  193. package/dist/patterns/preflight-confidence.js +38 -0
  194. package/dist/patterns/preflight-confidence.js.map +1 -0
  195. package/dist/patterns/spot-check.d.ts +19 -0
  196. package/dist/patterns/spot-check.d.ts.map +1 -0
  197. package/dist/patterns/spot-check.js +33 -0
  198. package/dist/patterns/spot-check.js.map +1 -0
  199. package/dist/patterns/sub-pipeline.d.ts +84 -0
  200. package/dist/patterns/sub-pipeline.d.ts.map +1 -0
  201. package/dist/patterns/sub-pipeline.js +90 -0
  202. package/dist/patterns/sub-pipeline.js.map +1 -0
  203. package/dist/patterns/synthesize-with-followup.d.ts +35 -0
  204. package/dist/patterns/synthesize-with-followup.d.ts.map +1 -0
  205. package/dist/patterns/synthesize-with-followup.js +45 -0
  206. package/dist/patterns/synthesize-with-followup.js.map +1 -0
  207. package/dist/patterns/while-condition.d.ts +31 -0
  208. package/dist/patterns/while-condition.d.ts.map +1 -0
  209. package/dist/patterns/while-condition.js +59 -0
  210. package/dist/patterns/while-condition.js.map +1 -0
  211. package/dist/patterns/with-retry.d.ts +37 -0
  212. package/dist/patterns/with-retry.d.ts.map +1 -0
  213. package/dist/patterns/with-retry.js +73 -0
  214. package/dist/patterns/with-retry.js.map +1 -0
  215. package/dist/phase.d.ts +78 -0
  216. package/dist/phase.d.ts.map +1 -0
  217. package/dist/phase.js +36 -0
  218. package/dist/phase.js.map +1 -0
  219. package/dist/session/index.d.ts +5 -0
  220. package/dist/session/index.d.ts.map +1 -0
  221. package/dist/session/index.js +4 -0
  222. package/dist/session/index.js.map +1 -0
  223. package/dist/session/job-runner.d.ts +67 -0
  224. package/dist/session/job-runner.d.ts.map +1 -0
  225. package/dist/session/job-runner.js +131 -0
  226. package/dist/session/job-runner.js.map +1 -0
  227. package/dist/session/job-store.d.ts +98 -0
  228. package/dist/session/job-store.d.ts.map +1 -0
  229. package/dist/session/job-store.js +37 -0
  230. package/dist/session/job-store.js.map +1 -0
  231. package/dist/session/sqlite-job-store.d.ts +40 -0
  232. package/dist/session/sqlite-job-store.d.ts.map +1 -0
  233. package/dist/session/sqlite-job-store.js +200 -0
  234. package/dist/session/sqlite-job-store.js.map +1 -0
  235. package/dist/session/sse.d.ts +60 -0
  236. package/dist/session/sse.d.ts.map +1 -0
  237. package/dist/session/sse.js +97 -0
  238. package/dist/session/sse.js.map +1 -0
  239. package/dist/tools/index.d.ts +2 -0
  240. package/dist/tools/index.d.ts.map +1 -0
  241. package/dist/tools/index.js +2 -0
  242. package/dist/tools/index.js.map +1 -0
  243. package/dist/tools/registry.d.ts +44 -0
  244. package/dist/tools/registry.d.ts.map +1 -0
  245. package/dist/tools/registry.js +74 -0
  246. package/dist/tools/registry.js.map +1 -0
  247. package/dist/triggers/index.d.ts +15 -0
  248. package/dist/triggers/index.d.ts.map +1 -0
  249. package/dist/triggers/index.js +14 -0
  250. package/dist/triggers/index.js.map +1 -0
  251. package/dist/triggers/run-trigger.d.ts +86 -0
  252. package/dist/triggers/run-trigger.d.ts.map +1 -0
  253. package/dist/triggers/run-trigger.js +146 -0
  254. package/dist/triggers/run-trigger.js.map +1 -0
  255. package/dist/triggers/timer-trigger.d.ts +46 -0
  256. package/dist/triggers/timer-trigger.d.ts.map +1 -0
  257. package/dist/triggers/timer-trigger.js +74 -0
  258. package/dist/triggers/timer-trigger.js.map +1 -0
  259. package/dist/triggers/types.d.ts +61 -0
  260. package/dist/triggers/types.d.ts.map +1 -0
  261. package/dist/triggers/types.js +23 -0
  262. package/dist/triggers/types.js.map +1 -0
  263. package/package.json +64 -0
@@ -0,0 +1,73 @@
1
+ /**
2
+ * with-retry — higher-order phase wrapper for retrying flaky work.
3
+ *
4
+ * Wraps any `Phase` and retries it on failure. Failure means either:
5
+ * - the inner phase throws an exception, OR
6
+ * - the inner phase yields then sets `ctx.stop` (a clean failure signal)
7
+ *
8
+ * Both default to retryable. Override with `isFailure(ctx, error?)` if a
9
+ * specific `ctx.stop.reason` should *not* trigger a retry (e.g. user
10
+ * cancellation, terminal validation error).
11
+ *
12
+ * Retries are bounded by `maxAttempts` (default 3) and spaced with
13
+ * exponential backoff starting at `baseDelayMs` (default 1000).
14
+ *
15
+ * Idempotence is the caller's responsibility. The wrapper does NOT
16
+ * snapshot/restore ctx between attempts — a partially-applied mutation
17
+ * from a failed attempt is visible to the retry. Pass `resetState` if
18
+ * you need to undo partial work before each retry.
19
+ *
20
+ * Events emitted by the inner phase pass through unmodified. The wrapper
21
+ * additionally emits `data` events with key `${phase.name}.attempt`.
22
+ */
23
+ const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
24
+ export function withRetry(phase, options = {}) {
25
+ const maxAttempts = options.maxAttempts ?? 3;
26
+ const baseDelay = options.baseDelayMs ?? 1000;
27
+ const isFailure = options.isFailure ?? ((ctx, error) => error !== undefined || ctx.stop !== undefined);
28
+ return {
29
+ name: phase.name,
30
+ async *run(ctx) {
31
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
32
+ yield {
33
+ type: 'data',
34
+ key: `${phase.name}.attempt`,
35
+ value: { attempt, maxAttempts },
36
+ };
37
+ const stopBefore = ctx.stop;
38
+ let thrown = undefined;
39
+ try {
40
+ // Clear ctx.stop so the inner phase has a clean slate; restore on
41
+ // failure-and-retry so we don't leak the previous attempt's signal
42
+ // to the predicate.
43
+ ctx.stop = undefined;
44
+ yield* phase.run(ctx);
45
+ }
46
+ catch (err) {
47
+ thrown = err;
48
+ }
49
+ const failed = isFailure(ctx, thrown);
50
+ if (!failed) {
51
+ // Success — but only if the inner phase didn't deliberately stop
52
+ // (an isFailure override may treat ctx.stop as success for some
53
+ // reasons; the caller knows best).
54
+ return;
55
+ }
56
+ if (attempt === maxAttempts) {
57
+ // Exhausted — propagate the failure mode the inner phase used.
58
+ if (thrown !== undefined) {
59
+ throw thrown;
60
+ }
61
+ // ctx.stop is already set by the inner phase; leave it.
62
+ return;
63
+ }
64
+ options.onRetry?.(ctx, attempt, thrown);
65
+ ctx.stop = stopBefore; // restore upstream stop, if any
66
+ options.resetState?.(ctx);
67
+ const delay = baseDelay * 2 ** (attempt - 1);
68
+ await sleep(delay);
69
+ }
70
+ },
71
+ };
72
+ }
73
+ //# sourceMappingURL=with-retry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"with-retry.js","sourceRoot":"","sources":["../../src/patterns/with-retry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAiBH,MAAM,KAAK,GAAG,CAAC,EAAU,EAAE,EAAE,CAAC,IAAI,OAAO,CAAO,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;AAE1E,MAAM,UAAU,SAAS,CACvB,KAAkB,EAClB,UAAkC,EAAE;IAEpC,MAAM,WAAW,GAAG,OAAO,CAAC,WAAW,IAAI,CAAC,CAAC;IAC7C,MAAM,SAAS,GAAG,OAAO,CAAC,WAAW,IAAI,IAAI,CAAC;IAC9C,MAAM,SAAS,GACb,OAAO,CAAC,SAAS,IAAI,CAAC,CAAC,GAAG,EAAE,KAAK,EAAE,EAAE,CAAC,KAAK,KAAK,SAAS,IAAI,GAAG,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC;IAEvF,OAAO;QACL,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,KAAK,CAAC,CAAC,GAAG,CAAC,GAAG;YACZ,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,IAAI,WAAW,EAAE,OAAO,EAAE,EAAE,CAAC;gBACxD,MAAM;oBACJ,IAAI,EAAE,MAAM;oBACZ,GAAG,EAAE,GAAG,KAAK,CAAC,IAAI,UAAU;oBAC5B,KAAK,EAAE,EAAE,OAAO,EAAE,WAAW,EAAE;iBAChC,CAAC;gBAEF,MAAM,UAAU,GAAG,GAAG,CAAC,IAAI,CAAC;gBAC5B,IAAI,MAAM,GAAY,SAAS,CAAC;gBAEhC,IAAI,CAAC;oBACH,kEAAkE;oBAClE,mEAAmE;oBACnE,oBAAoB;oBACpB,GAAG,CAAC,IAAI,GAAG,SAAS,CAAC;oBACrB,KAAK,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;gBACxB,CAAC;gBAAC,OAAO,GAAG,EAAE,CAAC;oBACb,MAAM,GAAG,GAAG,CAAC;gBACf,CAAC;gBAED,MAAM,MAAM,GAAG,SAAS,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;gBAEtC,IAAI,CAAC,MAAM,EAAE,CAAC;oBACZ,iEAAiE;oBACjE,gEAAgE;oBAChE,mCAAmC;oBACnC,OAAO;gBACT,CAAC;gBAED,IAAI,OAAO,KAAK,WAAW,EAAE,CAAC;oBAC5B,+DAA+D;oBAC/D,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;wBACzB,MAAM,MAAM,CAAC;oBACf,CAAC;oBACD,wDAAwD;oBACxD,OAAO;gBACT,CAAC;gBAED,OAAO,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;gBACxC,GAAG,CAAC,IAAI,GAAG,UAAU,CAAC,CAAC,gCAAgC;gBACvD,OAAO,CAAC,UAAU,EAAE,CAAC,GAAG,CAAC,CAAC;gBAE1B,MAAM,KAAK,GAAG,SAAS,GAAG,CAAC,IAAI,CAAC,OAAO,GAAG,CAAC,CAAC,CAAC;gBAC7C,MAAM,KAAK,CAAC,KAAK,CAAC,CAAC;YACrB,CAAC;QACH,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Phase framework — the core of thread-phase.
3
+ *
4
+ * A pipeline is an ordered list of phases. Each phase reads typed inputs from
5
+ * a shared `PipelineContext`, calls an agent (or pure code), writes typed
6
+ * outputs back to the context, and yields streamed events.
7
+ *
8
+ * Conventions:
9
+ * - Phases mutate `ctx` for results. Reads are advertised via `requireCtx` at
10
+ * the top of each phase, which throws loudly if a prerequisite phase did
11
+ * not run or did not populate the field.
12
+ * - A phase sets `ctx.stop = { reason }` to halt the rest of the pipeline.
13
+ * - Sub-flows call other phases directly: `yield* otherPhase.run(ctx)`. No
14
+ * DAG framework.
15
+ *
16
+ * Downstream apps extend `BasePipelineContext` with their own typed fields
17
+ * and parameterize `Phase` on that context type.
18
+ *
19
+ * Custom event types: `Phase` has a second optional type parameter `TEvent`
20
+ * for downstream apps that want a discriminated union of their own events
21
+ * instead of the generic `{ type: 'data', key, value }` shape. Default is
22
+ * the framework's `PipelineEvent`. The orchestrator and JobRunner are
23
+ * parameterized accordingly.
24
+ */
25
+ import type { PipelineCache } from './cache.js';
26
+ export interface BasePipelineContext {
27
+ readonly cache: PipelineCache;
28
+ /** Set by any phase to halt the rest of the pipeline. */
29
+ stop?: {
30
+ reason: string;
31
+ };
32
+ /**
33
+ * Optional AbortSignal observable by phases for mid-phase cancellation.
34
+ * Populated by `runTrigger` per dispatch; `runPipeline` checks it
35
+ * between phases as well. Phases doing long async work should pass
36
+ * this into `runAgentWithTools({ signal })` or observe it directly.
37
+ */
38
+ signal?: AbortSignal;
39
+ }
40
+ export type PipelineEvent = {
41
+ type: 'phase';
42
+ phase: string;
43
+ detail?: string;
44
+ counts?: Record<string, number>;
45
+ } | {
46
+ type: 'content';
47
+ content: string;
48
+ } | {
49
+ type: 'agent_activity';
50
+ agent: string;
51
+ action: string;
52
+ detail?: string;
53
+ } | {
54
+ type: 'tool_call';
55
+ toolName: string;
56
+ toolUseId: string;
57
+ args: Record<string, unknown>;
58
+ } | {
59
+ type: 'tool_result';
60
+ toolUseId: string;
61
+ content: string;
62
+ } | {
63
+ type: 'data';
64
+ key: string;
65
+ value: unknown;
66
+ } | {
67
+ type: 'done';
68
+ reason?: string;
69
+ } | {
70
+ type: 'error';
71
+ message: string;
72
+ };
73
+ export interface Phase<TCtx extends BasePipelineContext = BasePipelineContext, TEvent = PipelineEvent> {
74
+ readonly name: string;
75
+ run(ctx: TCtx): AsyncGenerator<TEvent, void>;
76
+ }
77
+ export declare function requireCtx<TCtx extends BasePipelineContext, K extends keyof TCtx>(ctx: TCtx, key: K, phaseName: string): NonNullable<TCtx[K]>;
78
+ //# sourceMappingURL=phase.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"phase.d.ts","sourceRoot":"","sources":["../src/phase.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAOhD,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC;IAC9B,yDAAyD;IACzD,IAAI,CAAC,EAAE;QAAE,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAC1B;;;;;OAKG;IACH,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAWD,MAAM,MAAM,aAAa,GACrB;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,GAClF;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GACpC;IAAE,IAAI,EAAE,gBAAgB,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GAC1E;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,GACzF;IAAE,IAAI,EAAE,aAAa,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAC3D;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAE,GAC7C;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GACjC;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAcvC,MAAM,WAAW,KAAK,CACpB,IAAI,SAAS,mBAAmB,GAAG,mBAAmB,EACtD,MAAM,GAAG,aAAa;IAEtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,GAAG,CAAC,GAAG,EAAE,IAAI,GAAG,cAAc,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;CAC9C;AAMD,wBAAgB,UAAU,CAAC,IAAI,SAAS,mBAAmB,EAAE,CAAC,SAAS,MAAM,IAAI,EAC/E,GAAG,EAAE,IAAI,EACT,GAAG,EAAE,CAAC,EACN,SAAS,EAAE,MAAM,GAChB,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAStB"}
package/dist/phase.js ADDED
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Phase framework — the core of thread-phase.
3
+ *
4
+ * A pipeline is an ordered list of phases. Each phase reads typed inputs from
5
+ * a shared `PipelineContext`, calls an agent (or pure code), writes typed
6
+ * outputs back to the context, and yields streamed events.
7
+ *
8
+ * Conventions:
9
+ * - Phases mutate `ctx` for results. Reads are advertised via `requireCtx` at
10
+ * the top of each phase, which throws loudly if a prerequisite phase did
11
+ * not run or did not populate the field.
12
+ * - A phase sets `ctx.stop = { reason }` to halt the rest of the pipeline.
13
+ * - Sub-flows call other phases directly: `yield* otherPhase.run(ctx)`. No
14
+ * DAG framework.
15
+ *
16
+ * Downstream apps extend `BasePipelineContext` with their own typed fields
17
+ * and parameterize `Phase` on that context type.
18
+ *
19
+ * Custom event types: `Phase` has a second optional type parameter `TEvent`
20
+ * for downstream apps that want a discriminated union of their own events
21
+ * instead of the generic `{ type: 'data', key, value }` shape. Default is
22
+ * the framework's `PipelineEvent`. The orchestrator and JobRunner are
23
+ * parameterized accordingly.
24
+ */
25
+ // ---------------------------------------------------------------------------
26
+ // Runtime precondition helper — fails loud on phase-reordering bugs.
27
+ // ---------------------------------------------------------------------------
28
+ export function requireCtx(ctx, key, phaseName) {
29
+ const value = ctx[key];
30
+ if (value === undefined || value === null) {
31
+ throw new Error(`[${phaseName}] precondition failed: ctx.${String(key)} is not set. ` +
32
+ `A prerequisite phase did not run or did not populate this field.`);
33
+ }
34
+ return value;
35
+ }
36
+ //# sourceMappingURL=phase.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"phase.js","sourceRoot":"","sources":["../src/phase.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AA6DH,8EAA8E;AAC9E,qEAAqE;AACrE,8EAA8E;AAE9E,MAAM,UAAU,UAAU,CACxB,GAAS,EACT,GAAM,EACN,SAAiB;IAEjB,MAAM,KAAK,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC;IACvB,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;QAC1C,MAAM,IAAI,KAAK,CACb,IAAI,SAAS,8BAA8B,MAAM,CAAC,GAAG,CAAC,eAAe;YACnE,kEAAkE,CACrE,CAAC;IACJ,CAAC;IACD,OAAO,KAA6B,CAAC;AACvC,CAAC"}
@@ -0,0 +1,5 @@
1
+ export { type JobStore, type JobRecord, type EventRecord, type JobStatus, type ListJobsOptions, } from './job-store.js';
2
+ export { SqliteJobStore } from './sqlite-job-store.js';
3
+ export { JobRunner, type LiveEvent, } from './job-runner.js';
4
+ export { streamToSSE, type SSEResponse, type StreamToSSEOptions, } from './sse.js';
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/session/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,QAAQ,EACb,KAAK,SAAS,EACd,KAAK,WAAW,EAChB,KAAK,SAAS,EACd,KAAK,eAAe,GACrB,MAAM,gBAAgB,CAAC;AAExB,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAEvD,OAAO,EACL,SAAS,EACT,KAAK,SAAS,GACf,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EACL,WAAW,EACX,KAAK,WAAW,EAChB,KAAK,kBAAkB,GACxB,MAAM,UAAU,CAAC"}
@@ -0,0 +1,4 @@
1
+ export { SqliteJobStore } from './sqlite-job-store.js';
2
+ export { JobRunner, } from './job-runner.js';
3
+ export { streamToSSE, } from './sse.js';
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/session/index.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAEvD,OAAO,EACL,SAAS,GAEV,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EACL,WAAW,GAGZ,MAAM,UAAU,CAAC"}
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Job runner — wraps a pipeline run with persistent event logging and live
3
+ * event emission.
4
+ *
5
+ * Three audiences for the same event stream:
6
+ * - The store (for resumability via JobStore.getEvents).
7
+ * - Live SSE-style listeners (subscribe via runner.on(`job:${id}`, ...)).
8
+ * - The caller's own AsyncGenerator consumer if they want to drive directly.
9
+ *
10
+ * Pipeline execution is decoupled from client connection: a job runs to
11
+ * completion regardless of who's listening. Late-attaching consumers replay
12
+ * via JobStore.getEvents.
13
+ *
14
+ * Cancellation: each in-flight job tracks an AbortController. Call
15
+ * `runner.cancel(jobId, reason?)` to abort the in-flight pipeline. The
16
+ * controller's signal is exposed via `runner.signalFor(jobId)` so callers
17
+ * (typically phase code that calls runAgentWithTools) can plumb it into
18
+ * the inference layer. Without that plumbing, cancellation only halts
19
+ * BETWEEN phases.
20
+ */
21
+ import { EventEmitter } from 'events';
22
+ import type { BasePipelineContext, Phase, PipelineEvent } from '../phase.js';
23
+ import { type PipelineSummary } from '../orchestrator.js';
24
+ import type { JobStore } from './job-store.js';
25
+ export interface LiveEvent {
26
+ id: number;
27
+ jobId: string;
28
+ eventType: string;
29
+ data: PipelineEvent;
30
+ createdAt: string;
31
+ }
32
+ export declare class JobRunner extends EventEmitter {
33
+ private readonly store;
34
+ private inflight;
35
+ constructor(store: JobStore);
36
+ /**
37
+ * Create a job row, return its id. Use `start()` to actually run it.
38
+ */
39
+ create(name: string, input: unknown): Promise<string>;
40
+ /**
41
+ * AbortSignal for a running job. Phase code should pass this through to
42
+ * `runAgentWithTools({ signal })` so cancellation reaches the inference
43
+ * call instead of just halting between phases.
44
+ *
45
+ * Returns `undefined` if the job isn't currently running on this runner.
46
+ */
47
+ signalFor(jobId: string): AbortSignal | undefined;
48
+ /**
49
+ * Request cancellation of an in-flight job. Aborts the controller (which
50
+ * propagates into any inference call wired to `signalFor(jobId)`) and
51
+ * lets the run-loop unwind. The job is marked FAILED with the given
52
+ * reason once unwinding completes. No-op if the job isn't running.
53
+ */
54
+ cancel(jobId: string, reason?: string): void;
55
+ /**
56
+ * Run a pipeline as job `jobId`. Persists every event, emits on `job:${id}`.
57
+ *
58
+ * Returns a `PipelineSummary` on success; rejects with the original error
59
+ * on phase failure (after the runner has marked the job FAILED, written a
60
+ * synthesized `error` event to the store, and emitted it to subscribers).
61
+ * Cancellation via `runner.cancel(jobId, reason)` rejects with an
62
+ * `AbortError`-shaped Error (`name === 'AbortError'`); the same FAILED
63
+ * persistence path runs first.
64
+ */
65
+ run<TCtx extends BasePipelineContext, TEvent extends PipelineEvent = PipelineEvent>(jobId: string, phases: ReadonlyArray<Phase<TCtx, TEvent>>, ctx: TCtx, finalResult?: () => unknown): Promise<PipelineSummary>;
66
+ }
67
+ //# sourceMappingURL=job-runner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"job-runner.d.ts","sourceRoot":"","sources":["../../src/session/job-runner.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,KAAK,EAAE,mBAAmB,EAAE,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC7E,OAAO,EAAe,KAAK,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACvE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAE/C,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,aAAa,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,qBAAa,SAAU,SAAQ,YAAY;IAG7B,OAAO,CAAC,QAAQ,CAAC,KAAK;IAFlC,OAAO,CAAC,QAAQ,CAAsC;gBAEzB,KAAK,EAAE,QAAQ;IAK5C;;OAEG;IACH,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC;IAIrD;;;;;;OAMG;IACH,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,WAAW,GAAG,SAAS;IAIjD;;;;;OAKG;IACH,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,GAAE,MAAoB,GAAG,IAAI;IASzD;;;;;;;;;OASG;IACG,GAAG,CAAC,IAAI,SAAS,mBAAmB,EAAE,MAAM,SAAS,aAAa,GAAG,aAAa,EACtF,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,aAAa,CAAC,KAAK,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,EAC1C,GAAG,EAAE,IAAI,EACT,WAAW,CAAC,EAAE,MAAM,OAAO,GAC1B,OAAO,CAAC,eAAe,CAAC;CA8D5B"}
@@ -0,0 +1,131 @@
1
+ /**
2
+ * Job runner — wraps a pipeline run with persistent event logging and live
3
+ * event emission.
4
+ *
5
+ * Three audiences for the same event stream:
6
+ * - The store (for resumability via JobStore.getEvents).
7
+ * - Live SSE-style listeners (subscribe via runner.on(`job:${id}`, ...)).
8
+ * - The caller's own AsyncGenerator consumer if they want to drive directly.
9
+ *
10
+ * Pipeline execution is decoupled from client connection: a job runs to
11
+ * completion regardless of who's listening. Late-attaching consumers replay
12
+ * via JobStore.getEvents.
13
+ *
14
+ * Cancellation: each in-flight job tracks an AbortController. Call
15
+ * `runner.cancel(jobId, reason?)` to abort the in-flight pipeline. The
16
+ * controller's signal is exposed via `runner.signalFor(jobId)` so callers
17
+ * (typically phase code that calls runAgentWithTools) can plumb it into
18
+ * the inference layer. Without that plumbing, cancellation only halts
19
+ * BETWEEN phases.
20
+ */
21
+ import { EventEmitter } from 'events';
22
+ import { runPipeline } from '../orchestrator.js';
23
+ export class JobRunner extends EventEmitter {
24
+ store;
25
+ inflight = new Map();
26
+ constructor(store) {
27
+ super();
28
+ this.store = store;
29
+ this.setMaxListeners(100);
30
+ }
31
+ /**
32
+ * Create a job row, return its id. Use `start()` to actually run it.
33
+ */
34
+ create(name, input) {
35
+ return this.store.createJob(name, input);
36
+ }
37
+ /**
38
+ * AbortSignal for a running job. Phase code should pass this through to
39
+ * `runAgentWithTools({ signal })` so cancellation reaches the inference
40
+ * call instead of just halting between phases.
41
+ *
42
+ * Returns `undefined` if the job isn't currently running on this runner.
43
+ */
44
+ signalFor(jobId) {
45
+ return this.inflight.get(jobId)?.signal;
46
+ }
47
+ /**
48
+ * Request cancellation of an in-flight job. Aborts the controller (which
49
+ * propagates into any inference call wired to `signalFor(jobId)`) and
50
+ * lets the run-loop unwind. The job is marked FAILED with the given
51
+ * reason once unwinding completes. No-op if the job isn't running.
52
+ */
53
+ cancel(jobId, reason = 'cancelled') {
54
+ const controller = this.inflight.get(jobId);
55
+ if (!controller)
56
+ return;
57
+ if (!controller.signal.aborted) {
58
+ // Node's AbortController accepts an optional reason on abort().
59
+ controller.abort(reason);
60
+ }
61
+ }
62
+ /**
63
+ * Run a pipeline as job `jobId`. Persists every event, emits on `job:${id}`.
64
+ *
65
+ * Returns a `PipelineSummary` on success; rejects with the original error
66
+ * on phase failure (after the runner has marked the job FAILED, written a
67
+ * synthesized `error` event to the store, and emitted it to subscribers).
68
+ * Cancellation via `runner.cancel(jobId, reason)` rejects with an
69
+ * `AbortError`-shaped Error (`name === 'AbortError'`); the same FAILED
70
+ * persistence path runs first.
71
+ */
72
+ async run(jobId, phases, ctx, finalResult) {
73
+ const controller = new AbortController();
74
+ this.inflight.set(jobId, controller);
75
+ await this.store.setRunning(jobId);
76
+ let eventCount = 0;
77
+ let stopReason;
78
+ const persistError = async (message) => {
79
+ await this.store.setFailed(jobId, message);
80
+ const errEvent = { type: 'error', message };
81
+ const eventId = await this.store.appendEvent(jobId, errEvent);
82
+ this.emit(`job:${jobId}`, {
83
+ id: eventId,
84
+ jobId,
85
+ eventType: 'error',
86
+ data: errEvent,
87
+ createdAt: new Date().toISOString(),
88
+ });
89
+ };
90
+ try {
91
+ for await (const event of runPipeline(phases, ctx)) {
92
+ const eventId = await this.store.appendEvent(jobId, event);
93
+ this.emit(`job:${jobId}`, {
94
+ id: eventId,
95
+ jobId,
96
+ eventType: event.type,
97
+ data: event,
98
+ createdAt: new Date().toISOString(),
99
+ });
100
+ eventCount++;
101
+ if (event.type === 'done') {
102
+ stopReason = event.reason;
103
+ }
104
+ if (controller.signal.aborted) {
105
+ const reason = controller.signal.reason ?? 'cancelled';
106
+ await persistError(`cancelled: ${reason}`);
107
+ const err = new Error(`cancelled: ${reason}`);
108
+ err.name = 'AbortError';
109
+ throw err;
110
+ }
111
+ }
112
+ await this.store.setCompleted(jobId, finalResult ? finalResult() : null);
113
+ return stopReason !== undefined
114
+ ? { status: 'stopped', reason: stopReason, eventCount }
115
+ : { status: 'completed', eventCount };
116
+ }
117
+ catch (err) {
118
+ // Re-throw cancellation rejections (already persisted above).
119
+ if (err instanceof Error && err.name === 'AbortError')
120
+ throw err;
121
+ // Phase exception: persist + rethrow.
122
+ const message = err instanceof Error ? err.message : String(err);
123
+ await persistError(message);
124
+ throw err;
125
+ }
126
+ finally {
127
+ this.inflight.delete(jobId);
128
+ }
129
+ }
130
+ }
131
+ //# sourceMappingURL=job-runner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"job-runner.js","sourceRoot":"","sources":["../../src/session/job-runner.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,98 @@
1
+ /**
2
+ * JobStore interface — the persistence boundary for thread-phase jobs and
3
+ * their event logs.
4
+ *
5
+ * SqliteJobStore is the bundled default (single-file, zero-config). Other
6
+ * backends (Postgres, Redis, custom file-based for embedded use) just
7
+ * need to implement this interface.
8
+ *
9
+ * # v3 interface: async by default
10
+ *
11
+ * Every method returns a Promise. This is the breaking change in v3.0.0:
12
+ * pre-v3 the interface was sync (sqlite hot path; fire-and-forget event
13
+ * writes) and that blocked any backend whose underlying I/O is async
14
+ * (Postgres, Redis, network-attached stores). Going async at the
15
+ * interface level unblocks those backends without forcing them to fake
16
+ * a sync boundary via in-process queues.
17
+ *
18
+ * Performance note for SqliteJobStore: better-sqlite3 stays sync
19
+ * internally; the bundled implementation just wraps its prepared-
20
+ * statement calls in `async` methods. The added cost is one microtask
21
+ * per call — sub-microsecond, swamped by the actual I/O on every other
22
+ * backend, and negligible on sqlite given the prior sub-millisecond
23
+ * write cost.
24
+ *
25
+ * Migration from v2: every `store.xxx(...)` call site needs `await`.
26
+ * `JobRunner` and `streamToSSE` handle this internally; user code that
27
+ * touched the store directly (custom dashboards, replay scripts) needs
28
+ * to add `await` and become `async` at the call site.
29
+ *
30
+ * `close()` is the one exception — it stays sync because closing isn't
31
+ * a perf-sensitive code path and several embedded backends (sqlite,
32
+ * in-memory) have no async work to do on close. Implementations whose
33
+ * close is genuinely async can still return a Promise; the type allows
34
+ * either (`void | Promise<void>` is the relaxation).
35
+ */
36
+ import type { PipelineEvent } from '../phase.js';
37
+ export type JobStatus = 'PENDING' | 'RUNNING' | 'COMPLETED' | 'FAILED';
38
+ export interface JobRecord {
39
+ id: string;
40
+ name: string;
41
+ input: unknown;
42
+ status: JobStatus;
43
+ result: unknown | null;
44
+ error: string | null;
45
+ eventCount: number;
46
+ createdAt: Date;
47
+ startedAt: Date | null;
48
+ completedAt: Date | null;
49
+ }
50
+ export interface EventRecord {
51
+ id: number;
52
+ jobId: string;
53
+ eventType: string;
54
+ data: PipelineEvent;
55
+ createdAt: Date;
56
+ }
57
+ export interface ListJobsOptions {
58
+ /** Filter to a single pipeline name. */
59
+ name?: string;
60
+ /** Page size cap. Default: 50. */
61
+ limit?: number;
62
+ }
63
+ export interface JobStore {
64
+ /** Insert a PENDING job row, return its id. */
65
+ createJob(name: string, input: unknown): Promise<string>;
66
+ /**
67
+ * Atomically claim a single-runner slot for `name`. If no job with this
68
+ * name is currently RUNNING, insert a new job row directly in RUNNING
69
+ * state and return its id. Otherwise return null.
70
+ *
71
+ * Use this for cron-driven pipelines that should never overlap with
72
+ * themselves (e.g. a 10-minute timer where a run can occasionally take
73
+ * longer than the interval). Implementations must perform the
74
+ * existence check + insert in a single transaction.
75
+ *
76
+ * `JobRunner.run` will still call setRunning on the returned id; that's
77
+ * a no-op state transition and (with the COALESCE in setRunning) leaves
78
+ * the original startedAt intact.
79
+ */
80
+ acquireExclusive(name: string, input: unknown): Promise<string | null>;
81
+ setRunning(jobId: string): Promise<void>;
82
+ setCompleted(jobId: string, result: unknown): Promise<void>;
83
+ setFailed(jobId: string, error: string): Promise<void>;
84
+ getJob(jobId: string): Promise<JobRecord | null>;
85
+ listJobs(options?: ListJobsOptions): Promise<JobRecord[]>;
86
+ /** Append one event to the log; returns its monotonic id (resume cursor). */
87
+ appendEvent(jobId: string, event: PipelineEvent): Promise<number>;
88
+ /** Read events. `afterId` is the resume cursor — use 0 for "from the start". */
89
+ getEvents(jobId: string, afterId?: number): Promise<EventRecord[]>;
90
+ /**
91
+ * Release any underlying resources (connections, file handles). May be
92
+ * sync or async — implementations whose close has no async work
93
+ * (sqlite, in-memory) may return void; networked backends should return
94
+ * a Promise that resolves when the connection is fully torn down.
95
+ */
96
+ close(): void | Promise<void>;
97
+ }
98
+ //# sourceMappingURL=job-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"job-store.d.ts","sourceRoot":"","sources":["../../src/session/job-store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAEjD,MAAM,MAAM,SAAS,GAAG,SAAS,GAAG,SAAS,GAAG,WAAW,GAAG,QAAQ,CAAC;AAEvE,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,EAAE,SAAS,CAAC;IAClB,MAAM,EAAE,OAAO,GAAG,IAAI,CAAC;IACvB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,IAAI,CAAC;IAChB,SAAS,EAAE,IAAI,GAAG,IAAI,CAAC;IACvB,WAAW,EAAE,IAAI,GAAG,IAAI,CAAC;CAC1B;AAED,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,aAAa,CAAC;IACpB,SAAS,EAAE,IAAI,CAAC;CACjB;AAED,MAAM,WAAW,eAAe;IAC9B,wCAAwC;IACxC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,kCAAkC;IAClC,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,QAAQ;IACvB,+CAA+C;IAC/C,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACzD;;;;;;;;;;;;;OAaG;IACH,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IACvE,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACzC,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5D,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEvD,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,CAAC;IACjD,QAAQ,CAAC,OAAO,CAAC,EAAE,eAAe,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC,CAAC;IAE1D,6EAA6E;IAC7E,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAClE,gFAAgF;IAChF,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC;IAEnE;;;;;OAKG;IACH,KAAK,IAAI,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC/B"}
@@ -0,0 +1,37 @@
1
+ /**
2
+ * JobStore interface — the persistence boundary for thread-phase jobs and
3
+ * their event logs.
4
+ *
5
+ * SqliteJobStore is the bundled default (single-file, zero-config). Other
6
+ * backends (Postgres, Redis, custom file-based for embedded use) just
7
+ * need to implement this interface.
8
+ *
9
+ * # v3 interface: async by default
10
+ *
11
+ * Every method returns a Promise. This is the breaking change in v3.0.0:
12
+ * pre-v3 the interface was sync (sqlite hot path; fire-and-forget event
13
+ * writes) and that blocked any backend whose underlying I/O is async
14
+ * (Postgres, Redis, network-attached stores). Going async at the
15
+ * interface level unblocks those backends without forcing them to fake
16
+ * a sync boundary via in-process queues.
17
+ *
18
+ * Performance note for SqliteJobStore: better-sqlite3 stays sync
19
+ * internally; the bundled implementation just wraps its prepared-
20
+ * statement calls in `async` methods. The added cost is one microtask
21
+ * per call — sub-microsecond, swamped by the actual I/O on every other
22
+ * backend, and negligible on sqlite given the prior sub-millisecond
23
+ * write cost.
24
+ *
25
+ * Migration from v2: every `store.xxx(...)` call site needs `await`.
26
+ * `JobRunner` and `streamToSSE` handle this internally; user code that
27
+ * touched the store directly (custom dashboards, replay scripts) needs
28
+ * to add `await` and become `async` at the call site.
29
+ *
30
+ * `close()` is the one exception — it stays sync because closing isn't
31
+ * a perf-sensitive code path and several embedded backends (sqlite,
32
+ * in-memory) have no async work to do on close. Implementations whose
33
+ * close is genuinely async can still return a Promise; the type allows
34
+ * either (`void | Promise<void>` is the relaxation).
35
+ */
36
+ export {};
37
+ //# sourceMappingURL=job-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"job-store.js","sourceRoot":"","sources":["../../src/session/job-store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG"}
@@ -0,0 +1,40 @@
1
+ /**
2
+ * sqlite-backed JobStore implementation — the bundled default.
3
+ *
4
+ * Two tables (job, event) on a single sqlite file. WAL journal for write
5
+ * concurrency, foreign keys enforced.
6
+ *
7
+ * The interface (`JobStore`) is async-by-default in v3.0.0; this
8
+ * implementation wraps its sync better-sqlite3 calls in `async` methods.
9
+ * better-sqlite3 stays sync internally — the only overhead is one
10
+ * microtask per call, which is negligible against sqlite's already
11
+ * sub-millisecond write cost. We pay this cost to keep one unified
12
+ * interface across all backends (Postgres, Redis, network stores)
13
+ * instead of two parallel sync + async hierarchies.
14
+ */
15
+ import type { PipelineEvent } from '../phase.js';
16
+ import type { EventRecord, JobRecord, JobStore, ListJobsOptions } from './job-store.js';
17
+ export declare class SqliteJobStore implements JobStore {
18
+ private db;
19
+ constructor(dbPath?: string);
20
+ /**
21
+ * Apply any unapplied migrations. Reads `PRAGMA user_version`, applies
22
+ * each entry from `MIGRATIONS` whose version is greater, inside a
23
+ * transaction per step, then bumps user_version.
24
+ *
25
+ * Idempotent: running twice is a no-op once the schema is current.
26
+ */
27
+ private runMigrations;
28
+ createJob(name: string, input: unknown): Promise<string>;
29
+ acquireExclusive(name: string, input: unknown): Promise<string | null>;
30
+ setRunning(jobId: string): Promise<void>;
31
+ setCompleted(jobId: string, result: unknown): Promise<void>;
32
+ setFailed(jobId: string, error: string): Promise<void>;
33
+ getJob(jobId: string): Promise<JobRecord | null>;
34
+ listJobs(options?: ListJobsOptions): Promise<JobRecord[]>;
35
+ private toJobRecord;
36
+ appendEvent(jobId: string, event: PipelineEvent): Promise<number>;
37
+ getEvents(jobId: string, afterId?: number): Promise<EventRecord[]>;
38
+ close(): void;
39
+ }
40
+ //# sourceMappingURL=sqlite-job-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sqlite-job-store.d.ts","sourceRoot":"","sources":["../../src/session/sqlite-job-store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAIH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,KAAK,EACV,WAAW,EACX,SAAS,EAET,QAAQ,EACR,eAAe,EAChB,MAAM,gBAAgB,CAAC;AAgFxB,qBAAa,cAAe,YAAW,QAAQ;IAC7C,OAAO,CAAC,EAAE,CAAK;gBAEH,MAAM,GAAE,MAAwB;IAO5C;;;;;;OAMG;IACH,OAAO,CAAC,aAAa;IAiBf,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC;IAQxD,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAwBtE,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAYxC,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAQ3D,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAYtD,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC;IAUhD,QAAQ,CAAC,OAAO,GAAE,eAAoB,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC;IAiBnE,OAAO,CAAC,WAAW;IAmBb,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC;IAOjE,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,GAAE,MAAU,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC;IAgB3E,KAAK,IAAI,IAAI;CAGd"}