@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,200 @@
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 Database from 'better-sqlite3';
16
+ import { randomUUID } from 'crypto';
17
+ const MIGRATIONS = [
18
+ {
19
+ version: 1,
20
+ up: `
21
+ CREATE TABLE IF NOT EXISTS job (
22
+ id TEXT PRIMARY KEY,
23
+ name TEXT NOT NULL,
24
+ input TEXT NOT NULL,
25
+ status TEXT NOT NULL DEFAULT 'PENDING',
26
+ result TEXT,
27
+ error TEXT,
28
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
29
+ started_at TEXT,
30
+ completed_at TEXT
31
+ );
32
+
33
+ CREATE INDEX IF NOT EXISTS idx_job_name_created
34
+ ON job (name, created_at DESC);
35
+
36
+ CREATE TABLE IF NOT EXISTS event (
37
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
38
+ job_id TEXT NOT NULL,
39
+ event_type TEXT NOT NULL,
40
+ data TEXT NOT NULL,
41
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
42
+ FOREIGN KEY (job_id) REFERENCES job(id)
43
+ );
44
+
45
+ CREATE INDEX IF NOT EXISTS idx_event_job_id ON event (job_id, id);
46
+ `,
47
+ },
48
+ ];
49
+ function defaultDbPath() {
50
+ return process.env.THREAD_PHASE_DB ?? './thread-phase.db';
51
+ }
52
+ function parseDate(s) {
53
+ return s ? new Date(s + 'Z') : null;
54
+ }
55
+ export class SqliteJobStore {
56
+ db;
57
+ constructor(dbPath = defaultDbPath()) {
58
+ this.db = new Database(dbPath);
59
+ this.db.pragma('journal_mode = WAL');
60
+ this.db.pragma('foreign_keys = ON');
61
+ this.runMigrations();
62
+ }
63
+ /**
64
+ * Apply any unapplied migrations. Reads `PRAGMA user_version`, applies
65
+ * each entry from `MIGRATIONS` whose version is greater, inside a
66
+ * transaction per step, then bumps user_version.
67
+ *
68
+ * Idempotent: running twice is a no-op once the schema is current.
69
+ */
70
+ runMigrations() {
71
+ const current = this.db.pragma('user_version', { simple: true }) ?? 0;
72
+ for (const m of MIGRATIONS) {
73
+ if (m.version <= current)
74
+ continue;
75
+ this.db.transaction(() => {
76
+ this.db.exec(m.up);
77
+ // user_version is an integer pragma; better-sqlite3 doesn't support
78
+ // parameter binding for pragmas, so interpolate the integer directly.
79
+ this.db.pragma(`user_version = ${m.version}`);
80
+ })();
81
+ }
82
+ }
83
+ // -------------------------------------------------------------------------
84
+ // Job lifecycle
85
+ // -------------------------------------------------------------------------
86
+ async createJob(name, input) {
87
+ const id = randomUUID();
88
+ this.db
89
+ .prepare(`INSERT INTO job (id, name, input) VALUES (?, ?, ?)`)
90
+ .run(id, name, JSON.stringify(input));
91
+ return id;
92
+ }
93
+ async acquireExclusive(name, input) {
94
+ // better-sqlite3's `db.transaction(fn)` wraps the callback in BEGIN…COMMIT
95
+ // (defaults to deferred, but the runtime upgrades to a write lock the
96
+ // moment we INSERT, which serializes concurrent acquireExclusive calls
97
+ // on the same DB file). The check + insert therefore happens atomically:
98
+ // a second caller racing on the same name will see the row we just
99
+ // inserted (status='RUNNING') and return null.
100
+ const tx = this.db.transaction((n, i) => {
101
+ const existing = this.db
102
+ .prepare(`SELECT id FROM job WHERE name = ? AND status = 'RUNNING' LIMIT 1`)
103
+ .get(n);
104
+ if (existing)
105
+ return null;
106
+ const id = randomUUID();
107
+ this.db
108
+ .prepare(`INSERT INTO job (id, name, input, status, started_at)
109
+ VALUES (?, ?, ?, 'RUNNING', datetime('now'))`)
110
+ .run(id, n, JSON.stringify(i));
111
+ return id;
112
+ });
113
+ return tx(name, input);
114
+ }
115
+ async setRunning(jobId) {
116
+ // COALESCE on started_at: idempotent w.r.t. acquireExclusive, which
117
+ // already sets status='RUNNING' and started_at at claim time.
118
+ this.db
119
+ .prepare(`UPDATE job SET status = 'RUNNING',
120
+ started_at = COALESCE(started_at, datetime('now'))
121
+ WHERE id = ?`)
122
+ .run(jobId);
123
+ }
124
+ async setCompleted(jobId, result) {
125
+ this.db
126
+ .prepare(`UPDATE job SET status = 'COMPLETED', result = ?, completed_at = datetime('now') WHERE id = ?`)
127
+ .run(JSON.stringify(result ?? null), jobId);
128
+ }
129
+ async setFailed(jobId, error) {
130
+ this.db
131
+ .prepare(`UPDATE job SET status = 'FAILED', error = ?, completed_at = datetime('now') WHERE id = ?`)
132
+ .run(error, jobId);
133
+ }
134
+ // -------------------------------------------------------------------------
135
+ // Job reads
136
+ // -------------------------------------------------------------------------
137
+ async getJob(jobId) {
138
+ const row = this.db
139
+ .prepare(`SELECT j.*, (SELECT COUNT(*) FROM event WHERE job_id = j.id) AS event_count
140
+ FROM job j WHERE j.id = ?`)
141
+ .get(jobId);
142
+ return row ? this.toJobRecord(row) : null;
143
+ }
144
+ async listJobs(options = {}) {
145
+ const limit = options.limit ?? 50;
146
+ // Build one parameterized query — `name IS NULL OR j.name = name` lets a
147
+ // null parameter act as a no-filter wildcard, removing the duplicated
148
+ // SELECT/ORDER/LIMIT clauses that were previously copied per branch.
149
+ const sql = `
150
+ SELECT j.*, (SELECT COUNT(*) FROM event WHERE job_id = j.id) AS event_count
151
+ FROM job j
152
+ WHERE (? IS NULL OR j.name = ?)
153
+ ORDER BY j.created_at DESC
154
+ LIMIT ?
155
+ `;
156
+ const name = options.name ?? null;
157
+ const rows = this.db.prepare(sql).all(name, name, limit);
158
+ return rows.map((r) => this.toJobRecord(r));
159
+ }
160
+ toJobRecord(row) {
161
+ return {
162
+ id: row.id,
163
+ name: row.name,
164
+ input: JSON.parse(row.input),
165
+ status: row.status,
166
+ result: row.result ? JSON.parse(row.result) : null,
167
+ error: row.error,
168
+ eventCount: row.event_count,
169
+ createdAt: parseDate(row.created_at),
170
+ startedAt: parseDate(row.started_at),
171
+ completedAt: parseDate(row.completed_at),
172
+ };
173
+ }
174
+ // -------------------------------------------------------------------------
175
+ // Events — append-only log, resumable via afterId
176
+ // -------------------------------------------------------------------------
177
+ async appendEvent(jobId, event) {
178
+ const result = this.db
179
+ .prepare(`INSERT INTO event (job_id, event_type, data) VALUES (?, ?, ?)`)
180
+ .run(jobId, event.type, JSON.stringify(event));
181
+ return Number(result.lastInsertRowid);
182
+ }
183
+ async getEvents(jobId, afterId = 0) {
184
+ const rows = this.db
185
+ .prepare(`SELECT id, job_id, event_type, data, created_at
186
+ FROM event WHERE job_id = ? AND id > ? ORDER BY id ASC`)
187
+ .all(jobId, afterId);
188
+ return rows.map((r) => ({
189
+ id: r.id,
190
+ jobId: r.job_id,
191
+ eventType: r.event_type,
192
+ data: JSON.parse(r.data),
193
+ createdAt: parseDate(r.created_at),
194
+ }));
195
+ }
196
+ close() {
197
+ this.db.close();
198
+ }
199
+ }
200
+ //# sourceMappingURL=sqlite-job-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sqlite-job-store.js","sourceRoot":"","sources":["../../src/session/sqlite-job-store.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Server-Sent Events helper — adapt a JobRunner live stream + replay log
3
+ * into an SSE wire format for HTTP consumers.
4
+ *
5
+ * Wire shape:
6
+ * - Each event is `id: <eventId>\nevent: <type>\ndata: <json>\n\n`.
7
+ * - The eventId is the JobStore's monotonic id, so disconnected clients
8
+ * can resume by reading `Last-Event-ID` and replaying via JobStore.
9
+ * - The connection closes after a `done` or `error` event.
10
+ *
11
+ * The helper is framework-agnostic: it writes to a minimal `SSEResponse`
12
+ * interface so it works with Node's `http.ServerResponse`, Express's
13
+ * `Response`, Fastify's reply (after .raw), etc.
14
+ *
15
+ * Typical usage:
16
+ *
17
+ * app.get('/jobs/:id/events', (req, res) => {
18
+ * const lastId = Number(req.headers['last-event-id'] ?? 0);
19
+ * res.writeHead(200, {
20
+ * 'Content-Type': 'text/event-stream',
21
+ * 'Cache-Control': 'no-cache',
22
+ * 'Connection': 'keep-alive',
23
+ * });
24
+ * streamToSSE({ runner, store, jobId: req.params.id, res, afterId: lastId });
25
+ * });
26
+ */
27
+ import type { JobStore } from './job-store.js';
28
+ import type { JobRunner } from './job-runner.js';
29
+ /**
30
+ * Minimal response interface — `http.ServerResponse` and Express's `Response`
31
+ * both satisfy this without modification.
32
+ */
33
+ export interface SSEResponse {
34
+ write(chunk: string): boolean;
35
+ end(): void;
36
+ on(event: 'close', listener: () => void): void;
37
+ }
38
+ export interface StreamToSSEOptions {
39
+ runner: JobRunner;
40
+ store: JobStore;
41
+ jobId: string;
42
+ res: SSEResponse;
43
+ /**
44
+ * Replay events with id > afterId before subscribing live. Use the value
45
+ * of the client's `Last-Event-ID` header to resume after disconnect.
46
+ */
47
+ afterId?: number;
48
+ /**
49
+ * Heartbeat interval in ms. Default 25s. Sends an SSE comment line to
50
+ * keep proxies and intermediaries from closing the connection. Set 0 to
51
+ * disable.
52
+ */
53
+ heartbeatMs?: number;
54
+ }
55
+ /**
56
+ * Pump a job's events to an SSE response. Resolves when the connection
57
+ * closes (either because the job ended or the client disconnected).
58
+ */
59
+ export declare function streamToSSE(options: StreamToSSEOptions): Promise<void>;
60
+ //# sourceMappingURL=sse.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sse.d.ts","sourceRoot":"","sources":["../../src/session/sse.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,KAAK,EAAE,SAAS,EAAa,MAAM,iBAAiB,CAAC;AAE5D;;;GAGG;AACH,MAAM,WAAW,WAAW;IAC1B,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;IAC9B,GAAG,IAAI,IAAI,CAAC;IACZ,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,IAAI,GAAG,IAAI,CAAC;CAChD;AAED,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,SAAS,CAAC;IAClB,KAAK,EAAE,QAAQ,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,WAAW,CAAC;IACjB;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;GAGG;AACH,wBAAsB,WAAW,CAAC,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAqE5E"}
@@ -0,0 +1,97 @@
1
+ /**
2
+ * Server-Sent Events helper — adapt a JobRunner live stream + replay log
3
+ * into an SSE wire format for HTTP consumers.
4
+ *
5
+ * Wire shape:
6
+ * - Each event is `id: <eventId>\nevent: <type>\ndata: <json>\n\n`.
7
+ * - The eventId is the JobStore's monotonic id, so disconnected clients
8
+ * can resume by reading `Last-Event-ID` and replaying via JobStore.
9
+ * - The connection closes after a `done` or `error` event.
10
+ *
11
+ * The helper is framework-agnostic: it writes to a minimal `SSEResponse`
12
+ * interface so it works with Node's `http.ServerResponse`, Express's
13
+ * `Response`, Fastify's reply (after .raw), etc.
14
+ *
15
+ * Typical usage:
16
+ *
17
+ * app.get('/jobs/:id/events', (req, res) => {
18
+ * const lastId = Number(req.headers['last-event-id'] ?? 0);
19
+ * res.writeHead(200, {
20
+ * 'Content-Type': 'text/event-stream',
21
+ * 'Cache-Control': 'no-cache',
22
+ * 'Connection': 'keep-alive',
23
+ * });
24
+ * streamToSSE({ runner, store, jobId: req.params.id, res, afterId: lastId });
25
+ * });
26
+ */
27
+ /**
28
+ * Pump a job's events to an SSE response. Resolves when the connection
29
+ * closes (either because the job ended or the client disconnected).
30
+ */
31
+ export async function streamToSSE(options) {
32
+ const { runner, store, jobId, res, afterId = 0 } = options;
33
+ const heartbeatMs = options.heartbeatMs ?? 25_000;
34
+ let closed = false;
35
+ const close = () => {
36
+ if (closed)
37
+ return;
38
+ closed = true;
39
+ res.end();
40
+ };
41
+ res.on('close', () => {
42
+ closed = true;
43
+ });
44
+ const writeFrame = (id, type, data) => {
45
+ if (closed)
46
+ return false;
47
+ const payload = `id: ${id}\n` +
48
+ `event: ${type}\n` +
49
+ `data: ${JSON.stringify(data)}\n\n`;
50
+ return res.write(payload);
51
+ };
52
+ // Step 1: replay anything the client has missed.
53
+ let lastId = afterId;
54
+ const replay = await store.getEvents(jobId, afterId);
55
+ for (const evt of replay) {
56
+ writeFrame(evt.id, evt.eventType, evt.data);
57
+ lastId = Math.max(lastId, evt.id);
58
+ }
59
+ // If the job is already finished and replay covered everything, close.
60
+ const job = await store.getJob(jobId);
61
+ if (job && (job.status === 'COMPLETED' || job.status === 'FAILED')) {
62
+ close();
63
+ return;
64
+ }
65
+ // Step 2: subscribe to live events. Buffer events that arrived during
66
+ // replay if their id is past lastId (otherwise they're already sent).
67
+ const channel = `job:${jobId}`;
68
+ const onLive = (evt) => {
69
+ if (closed)
70
+ return;
71
+ if (evt.id <= lastId)
72
+ return;
73
+ lastId = evt.id;
74
+ writeFrame(evt.id, evt.eventType, evt.data);
75
+ if (evt.eventType === 'done' || evt.eventType === 'error') {
76
+ close();
77
+ }
78
+ };
79
+ runner.on(channel, onLive);
80
+ // Heartbeat: comment line every N seconds to keep proxies from idling out.
81
+ let heartbeat = null;
82
+ if (heartbeatMs > 0) {
83
+ heartbeat = setInterval(() => {
84
+ if (closed)
85
+ return;
86
+ res.write(`: keepalive ${Date.now()}\n\n`);
87
+ }, heartbeatMs);
88
+ }
89
+ // Wait until the connection closes.
90
+ await new Promise((resolve) => {
91
+ res.on('close', resolve);
92
+ });
93
+ if (heartbeat)
94
+ clearInterval(heartbeat);
95
+ runner.off(channel, onLive);
96
+ }
97
+ //# sourceMappingURL=sse.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sse.js","sourceRoot":"","sources":["../../src/session/sse.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export { ToolRegistry, type ToolHandler, type ToolRegistryOptions, } from './registry.js';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,EACZ,KAAK,WAAW,EAChB,KAAK,mBAAmB,GACzB,MAAM,eAAe,CAAC"}
@@ -0,0 +1,2 @@
1
+ export { ToolRegistry, } from './registry.js';
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,GAGb,MAAM,eAAe,CAAC"}
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Tool registry — name → {definition, handler} dispatch with optional
3
+ * JSON-Schema validation of arguments.
4
+ *
5
+ * Implements `ToolExecutor` so it can be passed directly to
6
+ * `runAgentWithTools({ ..., toolExecutor: registry })`.
7
+ *
8
+ * Validation behaviour: when enabled (the default), arguments coming back
9
+ * from the model are checked against the tool's `inputSchema` before the
10
+ * handler runs. Failures return an error string to the agent rather than
11
+ * throwing — the model gets to read what went wrong and try again.
12
+ *
13
+ * Same policy for unknown tools and handler exceptions: errors become
14
+ * agent-readable strings, not thrown exceptions, so a single bad tool call
15
+ * doesn't kill the whole pipeline.
16
+ */
17
+ import type { ToolDefinition, ToolExecutor, ToolResult } from '../messages.js';
18
+ /**
19
+ * Handler signature — receives parsed args plus optional context, returns the
20
+ * tool's result content as a string. Anything string-shaped works (JSON,
21
+ * markdown, plain text); the agent sees it raw.
22
+ */
23
+ export type ToolHandler = (args: Record<string, unknown>, context: {
24
+ toolCallId: string;
25
+ }) => Promise<string>;
26
+ export interface ToolRegistryOptions {
27
+ /** When false, skip schema validation. Default: true. */
28
+ validate?: boolean;
29
+ }
30
+ export declare class ToolRegistry implements ToolExecutor {
31
+ private tools;
32
+ private ajv;
33
+ constructor(options?: ToolRegistryOptions);
34
+ /**
35
+ * Register a tool. Throws on duplicate names — pipelines should be aware of
36
+ * what they expose, not silently overwrite.
37
+ */
38
+ register(definition: ToolDefinition, handler: ToolHandler): this;
39
+ /** All registered tool definitions, in registration order. Hand to AgentConfig.tools. */
40
+ definitions(): ToolDefinition[];
41
+ has(name: string): boolean;
42
+ execute(name: string, toolCallId: string, args: Record<string, unknown>): Promise<ToolResult>;
43
+ }
44
+ //# sourceMappingURL=registry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/tools/registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAGH,OAAO,KAAK,EAAE,cAAc,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAE/E;;;;GAIG;AACH,MAAM,MAAM,WAAW,GAAG,CACxB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,OAAO,EAAE;IAAE,UAAU,EAAE,MAAM,CAAA;CAAE,KAC5B,OAAO,CAAC,MAAM,CAAC,CAAC;AAErB,MAAM,WAAW,mBAAmB;IAClC,yDAAyD;IACzD,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AASD,qBAAa,YAAa,YAAW,YAAY;IAC/C,OAAO,CAAC,KAAK,CAAoC;IACjD,OAAO,CAAC,GAAG,CAAa;gBAEZ,OAAO,GAAE,mBAAwB;IAI7C;;;OAGG;IACH,QAAQ,CAAC,UAAU,EAAE,cAAc,EAAE,OAAO,EAAE,WAAW,GAAG,IAAI;IAShE,yFAAyF;IACzF,WAAW,IAAI,cAAc,EAAE;IAI/B,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAIpB,OAAO,CACX,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC5B,OAAO,CAAC,UAAU,CAAC;CA+BvB"}
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Tool registry — name → {definition, handler} dispatch with optional
3
+ * JSON-Schema validation of arguments.
4
+ *
5
+ * Implements `ToolExecutor` so it can be passed directly to
6
+ * `runAgentWithTools({ ..., toolExecutor: registry })`.
7
+ *
8
+ * Validation behaviour: when enabled (the default), arguments coming back
9
+ * from the model are checked against the tool's `inputSchema` before the
10
+ * handler runs. Failures return an error string to the agent rather than
11
+ * throwing — the model gets to read what went wrong and try again.
12
+ *
13
+ * Same policy for unknown tools and handler exceptions: errors become
14
+ * agent-readable strings, not thrown exceptions, so a single bad tool call
15
+ * doesn't kill the whole pipeline.
16
+ */
17
+ import { Ajv } from 'ajv';
18
+ export class ToolRegistry {
19
+ tools = new Map();
20
+ ajv;
21
+ constructor(options = {}) {
22
+ this.ajv = options.validate === false ? null : new Ajv({ allErrors: true, strict: false });
23
+ }
24
+ /**
25
+ * Register a tool. Throws on duplicate names — pipelines should be aware of
26
+ * what they expose, not silently overwrite.
27
+ */
28
+ register(definition, handler) {
29
+ if (this.tools.has(definition.name)) {
30
+ throw new Error(`ToolRegistry: tool already registered: ${definition.name}`);
31
+ }
32
+ const validator = this.ajv ? this.ajv.compile(definition.inputSchema) : null;
33
+ this.tools.set(definition.name, { definition, handler, validator });
34
+ return this;
35
+ }
36
+ /** All registered tool definitions, in registration order. Hand to AgentConfig.tools. */
37
+ definitions() {
38
+ return [...this.tools.values()].map((e) => e.definition);
39
+ }
40
+ has(name) {
41
+ return this.tools.has(name);
42
+ }
43
+ async execute(name, toolCallId, args) {
44
+ const entry = this.tools.get(name);
45
+ if (!entry) {
46
+ return {
47
+ toolCallId,
48
+ content: `Error: unknown tool "${name}". Registered tools: ${[...this.tools.keys()].join(', ') || '(none)'}.`,
49
+ };
50
+ }
51
+ if (entry.validator) {
52
+ const ok = entry.validator(args);
53
+ if (!ok) {
54
+ const errs = entry.validator.errors ?? [];
55
+ const detail = errs
56
+ .map((e) => `${e.instancePath || '/'} ${e.message ?? 'invalid'}`)
57
+ .join('; ');
58
+ return {
59
+ toolCallId,
60
+ content: `Error: invalid arguments for "${name}": ${detail}`,
61
+ };
62
+ }
63
+ }
64
+ try {
65
+ const content = await entry.handler(args, { toolCallId });
66
+ return { toolCallId, content };
67
+ }
68
+ catch (err) {
69
+ const message = err instanceof Error ? err.message : String(err);
70
+ return { toolCallId, content: `Error: tool "${name}" threw: ${message}` };
71
+ }
72
+ }
73
+ }
74
+ //# sourceMappingURL=registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/tools/registry.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Triggers — entry-point abstraction for thread-phase pipelines.
3
+ *
4
+ * The `Trigger` interface is the protocol every signal source implements:
5
+ * timers, webhooks, queue consumers, file watchers, message brokers.
6
+ * Core ships only `TimerTrigger`; HTTP/queue/file-watch adapters live in
7
+ * `examples/triggers/` as recipes.
8
+ *
9
+ * `runTrigger` is the canonical consumer — reads events, dispatches
10
+ * pipelines, optionally persists through a `JobRunner`.
11
+ */
12
+ export type { Trigger, TriggerEvent } from './types.js';
13
+ export { TimerTrigger, type TimerTriggerOptions } from './timer-trigger.js';
14
+ export { runTrigger, type RunTriggerOptions, type RunTriggerHandle, } from './run-trigger.js';
15
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/triggers/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,YAAY,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AACxD,OAAO,EAAE,YAAY,EAAE,KAAK,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAC5E,OAAO,EACL,UAAU,EACV,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,GACtB,MAAM,kBAAkB,CAAC"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Triggers — entry-point abstraction for thread-phase pipelines.
3
+ *
4
+ * The `Trigger` interface is the protocol every signal source implements:
5
+ * timers, webhooks, queue consumers, file watchers, message brokers.
6
+ * Core ships only `TimerTrigger`; HTTP/queue/file-watch adapters live in
7
+ * `examples/triggers/` as recipes.
8
+ *
9
+ * `runTrigger` is the canonical consumer — reads events, dispatches
10
+ * pipelines, optionally persists through a `JobRunner`.
11
+ */
12
+ export { TimerTrigger } from './timer-trigger.js';
13
+ export { runTrigger, } from './run-trigger.js';
14
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/triggers/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAGH,OAAO,EAAE,YAAY,EAA4B,MAAM,oBAAoB,CAAC;AAC5E,OAAO,EACL,UAAU,GAGX,MAAM,kBAAkB,CAAC"}
@@ -0,0 +1,86 @@
1
+ /**
2
+ * runTrigger — the canonical Trigger consumer.
3
+ *
4
+ * Reads events from a `Trigger`, calls a user-supplied factory to produce
5
+ * `{ phases, ctx }` per event, and dispatches each pipeline. If a
6
+ * `JobRunner` is supplied, dispatch is persisted (job rows, event log,
7
+ * cancellation); otherwise the pipeline runs inline via
8
+ * `runPipelineToSummary` and events are discarded after dispatch.
9
+ *
10
+ * Resolves when the trigger generator exhausts, the abort signal fires,
11
+ * or `stop()` is called via the returned handle.
12
+ *
13
+ * Concurrency cap is a blocking semaphore. When `maxConcurrency` pipelines
14
+ * are in flight, the loop awaits a slot before pulling the next event
15
+ * from the trigger. The trigger's generator naturally pauses production
16
+ * — no events are dropped, no unbounded queue grows.
17
+ *
18
+ * Pipeline failures are isolated — one failing pipeline does not stop
19
+ * the trigger loop. Errors go through `onError` (or default stderr log)
20
+ * and the next event is still dispatched.
21
+ *
22
+ * Cancellation: each in-flight dispatch owns an `AbortController`. Call
23
+ * `handle.cancel(triggerEventId)` to abort that specific pipeline; the
24
+ * signal flows into `runPipelineToSummary` (inline) or `jobRunner.cancel`
25
+ * (persisted). Returns `true` if the pipeline was found and aborted,
26
+ * `false` if the event id is unknown or already completed.
27
+ *
28
+ * Observability: `onCapacityFull(event)` fires when an event arrives
29
+ * while the concurrency cap is full (the loop blocks on `Promise.race`
30
+ * before pulling the event, so this is the moment backpressure starts).
31
+ * `onDispatchStart(event)` fires immediately when a dispatch begins,
32
+ * before the user-supplied factory runs.
33
+ */
34
+ import type { JobRunner } from '../session/index.js';
35
+ import type { BasePipelineContext, Phase } from '../phase.js';
36
+ import type { Trigger, TriggerEvent } from './types.js';
37
+ export interface RunTriggerOptions<TInput, TCtx extends BasePipelineContext> {
38
+ /**
39
+ * Optional JobRunner. If provided, each event creates a persisted job
40
+ * row and pipelines run through `runner.run()` (events go to the
41
+ * event log, cancellation works via `runner.cancel(jobId)`). If
42
+ * omitted, pipelines run inline.
43
+ */
44
+ jobRunner?: JobRunner;
45
+ /** Name used for job rows when `jobRunner` is set. Default: `trigger.name`. */
46
+ pipelineName?: string;
47
+ /**
48
+ * Maximum concurrent in-flight pipelines from this trigger. When the
49
+ * cap is reached, the loop blocks before pulling the next event
50
+ * (backpressure flows back to the trigger). Default: 1.
51
+ */
52
+ maxConcurrency?: number;
53
+ /** Abort the run loop and call `trigger.stop()`. Outstanding pipelines complete. */
54
+ signal?: AbortSignal;
55
+ /** Called when a pipeline is about to start, after dispatch picks it up. */
56
+ onStart?: (event: TriggerEvent<TInput>, jobId?: string) => void;
57
+ /** Called when a pipeline completes successfully. */
58
+ onComplete?: (event: TriggerEvent<TInput>, jobId?: string) => void;
59
+ /** Called when a pipeline throws. Default: log to stderr. */
60
+ onError?: (event: TriggerEvent<TInput>, error: Error, jobId?: string) => void;
61
+ /**
62
+ * Called when an event arrives while the concurrency cap is full —
63
+ * the moment backpressure begins. The dispatch still happens once a
64
+ * slot frees; this hook only signals the wait.
65
+ */
66
+ onCapacityFull?: (event: TriggerEvent<TInput>) => void;
67
+ /** Called the moment dispatch starts, before the pipeline factory runs. */
68
+ onDispatchStart?: (event: TriggerEvent<TInput>) => void;
69
+ }
70
+ export interface RunTriggerHandle {
71
+ /** Resolves when the run loop has exited (trigger exhausted, signal fired, or `stop()` called). */
72
+ done: Promise<void>;
73
+ /** Stop the trigger and resolve `done` once outstanding pipelines complete. */
74
+ stop(): Promise<void>;
75
+ /**
76
+ * Abort a specific in-flight pipeline by its trigger event id. Returns
77
+ * `true` if the pipeline was found and cancellation was initiated;
78
+ * `false` if the event id is unknown or already completed.
79
+ */
80
+ cancel(triggerEventId: number): boolean;
81
+ }
82
+ export declare function runTrigger<TInput, TCtx extends BasePipelineContext>(trigger: Trigger<TInput>, pipelineFactory: (input: TInput, event: TriggerEvent<TInput>) => {
83
+ phases: ReadonlyArray<Phase<TCtx>>;
84
+ ctx: TCtx;
85
+ }, options?: RunTriggerOptions<TInput, TCtx>): RunTriggerHandle;
86
+ //# sourceMappingURL=run-trigger.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run-trigger.d.ts","sourceRoot":"","sources":["../../src/triggers/run-trigger.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAErD,OAAO,KAAK,EACV,mBAAmB,EACnB,KAAK,EACN,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAExD,MAAM,WAAW,iBAAiB,CAAC,MAAM,EAAE,IAAI,SAAS,mBAAmB;IACzE;;;;;OAKG;IACH,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,+EAA+E;IAC/E,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;;OAIG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,oFAAoF;IACpF,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,4EAA4E;IAC5E,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,YAAY,CAAC,MAAM,CAAC,EAAE,KAAK,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IAChE,qDAAqD;IACrD,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,YAAY,CAAC,MAAM,CAAC,EAAE,KAAK,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IACnE,6DAA6D;IAC7D,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,YAAY,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IAC9E;;;;OAIG;IACH,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE,YAAY,CAAC,MAAM,CAAC,KAAK,IAAI,CAAC;IACvD,2EAA2E;IAC3E,eAAe,CAAC,EAAE,CAAC,KAAK,EAAE,YAAY,CAAC,MAAM,CAAC,KAAK,IAAI,CAAC;CACzD;AAED,MAAM,WAAW,gBAAgB;IAC/B,mGAAmG;IACnG,IAAI,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;IACpB,+EAA+E;IAC/E,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACtB;;;;OAIG;IACH,MAAM,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC;CACzC;AAQD,wBAAgB,UAAU,CAAC,MAAM,EAAE,IAAI,SAAS,mBAAmB,EACjE,OAAO,EAAE,OAAO,CAAC,MAAM,CAAC,EACxB,eAAe,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,CAAC,MAAM,CAAC,KAAK;IAC/D,MAAM,EAAE,aAAa,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;IACnC,GAAG,EAAE,IAAI,CAAC;CACX,EACD,OAAO,GAAE,iBAAiB,CAAC,MAAM,EAAE,IAAI,CAAM,GAC5C,gBAAgB,CA+HlB"}