@wopr-network/defcon 0.2.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 (243) hide show
  1. package/README.md +274 -0
  2. package/dist/api/router.d.ts +24 -0
  3. package/dist/api/router.js +44 -0
  4. package/dist/api/server.d.ts +13 -0
  5. package/dist/api/server.js +280 -0
  6. package/dist/api/wire-types.d.ts +46 -0
  7. package/dist/api/wire-types.js +5 -0
  8. package/dist/config/db-path.d.ts +1 -0
  9. package/dist/config/db-path.js +1 -0
  10. package/dist/config/exporter.d.ts +3 -0
  11. package/dist/config/exporter.js +87 -0
  12. package/dist/config/index.d.ts +4 -0
  13. package/dist/config/index.js +4 -0
  14. package/dist/config/seed-loader.d.ts +10 -0
  15. package/dist/config/seed-loader.js +108 -0
  16. package/dist/config/zod-schemas.d.ts +165 -0
  17. package/dist/config/zod-schemas.js +283 -0
  18. package/dist/cors.d.ts +8 -0
  19. package/dist/cors.js +21 -0
  20. package/dist/engine/constants.d.ts +1 -0
  21. package/dist/engine/constants.js +1 -0
  22. package/dist/engine/engine.d.ts +69 -0
  23. package/dist/engine/engine.js +485 -0
  24. package/dist/engine/event-emitter.d.ts +9 -0
  25. package/dist/engine/event-emitter.js +19 -0
  26. package/dist/engine/event-types.d.ts +105 -0
  27. package/dist/engine/event-types.js +1 -0
  28. package/dist/engine/flow-spawner.d.ts +8 -0
  29. package/dist/engine/flow-spawner.js +28 -0
  30. package/dist/engine/gate-command-validator.d.ts +6 -0
  31. package/dist/engine/gate-command-validator.js +46 -0
  32. package/dist/engine/gate-evaluator.d.ts +12 -0
  33. package/dist/engine/gate-evaluator.js +233 -0
  34. package/dist/engine/handlebars.d.ts +9 -0
  35. package/dist/engine/handlebars.js +51 -0
  36. package/dist/engine/index.d.ts +12 -0
  37. package/dist/engine/index.js +7 -0
  38. package/dist/engine/invocation-builder.d.ts +18 -0
  39. package/dist/engine/invocation-builder.js +58 -0
  40. package/dist/engine/on-enter.d.ts +8 -0
  41. package/dist/engine/on-enter.js +102 -0
  42. package/dist/engine/ssrf-guard.d.ts +22 -0
  43. package/dist/engine/ssrf-guard.js +159 -0
  44. package/dist/engine/state-machine.d.ts +12 -0
  45. package/dist/engine/state-machine.js +74 -0
  46. package/dist/execution/active-runner.d.ts +45 -0
  47. package/dist/execution/active-runner.js +165 -0
  48. package/dist/execution/admin-schemas.d.ts +116 -0
  49. package/dist/execution/admin-schemas.js +125 -0
  50. package/dist/execution/cli.d.ts +57 -0
  51. package/dist/execution/cli.js +498 -0
  52. package/dist/execution/handlers/admin.d.ts +67 -0
  53. package/dist/execution/handlers/admin.js +200 -0
  54. package/dist/execution/handlers/flow.d.ts +25 -0
  55. package/dist/execution/handlers/flow.js +289 -0
  56. package/dist/execution/handlers/query.d.ts +31 -0
  57. package/dist/execution/handlers/query.js +64 -0
  58. package/dist/execution/index.d.ts +4 -0
  59. package/dist/execution/index.js +3 -0
  60. package/dist/execution/mcp-helpers.d.ts +42 -0
  61. package/dist/execution/mcp-helpers.js +23 -0
  62. package/dist/execution/mcp-server.d.ts +33 -0
  63. package/dist/execution/mcp-server.js +1020 -0
  64. package/dist/execution/provision-worktree.d.ts +16 -0
  65. package/dist/execution/provision-worktree.js +123 -0
  66. package/dist/execution/tool-schemas.d.ts +40 -0
  67. package/dist/execution/tool-schemas.js +44 -0
  68. package/dist/gates/blocking-graph.d.ts +26 -0
  69. package/dist/gates/blocking-graph.js +102 -0
  70. package/dist/gates/test/bad-return-gate.d.ts +1 -0
  71. package/dist/gates/test/bad-return-gate.js +4 -0
  72. package/dist/gates/test/passing-gate.d.ts +2 -0
  73. package/dist/gates/test/passing-gate.js +3 -0
  74. package/dist/gates/test/slow-gate.d.ts +2 -0
  75. package/dist/gates/test/slow-gate.js +5 -0
  76. package/dist/gates/test/throwing-gate.d.ts +1 -0
  77. package/dist/gates/test/throwing-gate.js +3 -0
  78. package/dist/logger.d.ts +8 -0
  79. package/dist/logger.js +12 -0
  80. package/dist/main.d.ts +14 -0
  81. package/dist/main.js +28 -0
  82. package/dist/repositories/drizzle/entity.repo.d.ts +27 -0
  83. package/dist/repositories/drizzle/entity.repo.js +190 -0
  84. package/dist/repositories/drizzle/event.repo.d.ts +12 -0
  85. package/dist/repositories/drizzle/event.repo.js +24 -0
  86. package/dist/repositories/drizzle/flow.repo.d.ts +22 -0
  87. package/dist/repositories/drizzle/flow.repo.js +364 -0
  88. package/dist/repositories/drizzle/gate.repo.d.ts +16 -0
  89. package/dist/repositories/drizzle/gate.repo.js +98 -0
  90. package/dist/repositories/drizzle/index.d.ts +6 -0
  91. package/dist/repositories/drizzle/index.js +7 -0
  92. package/dist/repositories/drizzle/invocation.repo.d.ts +23 -0
  93. package/dist/repositories/drizzle/invocation.repo.js +199 -0
  94. package/dist/repositories/drizzle/schema.d.ts +1932 -0
  95. package/dist/repositories/drizzle/schema.js +155 -0
  96. package/dist/repositories/drizzle/transition-log.repo.d.ts +11 -0
  97. package/dist/repositories/drizzle/transition-log.repo.js +42 -0
  98. package/dist/repositories/interfaces.d.ts +321 -0
  99. package/dist/repositories/interfaces.js +2 -0
  100. package/dist/src/api/router.d.ts +24 -0
  101. package/dist/src/api/router.js +44 -0
  102. package/dist/src/api/server.d.ts +13 -0
  103. package/dist/src/api/server.js +280 -0
  104. package/dist/src/api/wire-types.d.ts +46 -0
  105. package/dist/src/api/wire-types.js +5 -0
  106. package/dist/src/config/db-path.d.ts +1 -0
  107. package/dist/src/config/db-path.js +1 -0
  108. package/dist/src/config/exporter.d.ts +3 -0
  109. package/dist/src/config/exporter.js +87 -0
  110. package/dist/src/config/index.d.ts +4 -0
  111. package/dist/src/config/index.js +4 -0
  112. package/dist/src/config/seed-loader.d.ts +14 -0
  113. package/dist/src/config/seed-loader.js +131 -0
  114. package/dist/src/config/zod-schemas.d.ts +165 -0
  115. package/dist/src/config/zod-schemas.js +283 -0
  116. package/dist/src/cors.d.ts +8 -0
  117. package/dist/src/cors.js +21 -0
  118. package/dist/src/engine/constants.d.ts +1 -0
  119. package/dist/src/engine/constants.js +1 -0
  120. package/dist/src/engine/engine.d.ts +69 -0
  121. package/dist/src/engine/engine.js +485 -0
  122. package/dist/src/engine/event-emitter.d.ts +9 -0
  123. package/dist/src/engine/event-emitter.js +19 -0
  124. package/dist/src/engine/event-types.d.ts +105 -0
  125. package/dist/src/engine/event-types.js +1 -0
  126. package/dist/src/engine/flow-spawner.d.ts +8 -0
  127. package/dist/src/engine/flow-spawner.js +28 -0
  128. package/dist/src/engine/gate-command-validator.d.ts +6 -0
  129. package/dist/src/engine/gate-command-validator.js +46 -0
  130. package/dist/src/engine/gate-evaluator.d.ts +12 -0
  131. package/dist/src/engine/gate-evaluator.js +233 -0
  132. package/dist/src/engine/handlebars.d.ts +9 -0
  133. package/dist/src/engine/handlebars.js +51 -0
  134. package/dist/src/engine/index.d.ts +12 -0
  135. package/dist/src/engine/index.js +7 -0
  136. package/dist/src/engine/invocation-builder.d.ts +18 -0
  137. package/dist/src/engine/invocation-builder.js +58 -0
  138. package/dist/src/engine/on-enter.d.ts +8 -0
  139. package/dist/src/engine/on-enter.js +102 -0
  140. package/dist/src/engine/ssrf-guard.d.ts +22 -0
  141. package/dist/src/engine/ssrf-guard.js +159 -0
  142. package/dist/src/engine/state-machine.d.ts +12 -0
  143. package/dist/src/engine/state-machine.js +74 -0
  144. package/dist/src/execution/active-runner.d.ts +45 -0
  145. package/dist/src/execution/active-runner.js +165 -0
  146. package/dist/src/execution/admin-schemas.d.ts +116 -0
  147. package/dist/src/execution/admin-schemas.js +125 -0
  148. package/dist/src/execution/cli.d.ts +57 -0
  149. package/dist/src/execution/cli.js +501 -0
  150. package/dist/src/execution/handlers/admin.d.ts +67 -0
  151. package/dist/src/execution/handlers/admin.js +200 -0
  152. package/dist/src/execution/handlers/flow.d.ts +25 -0
  153. package/dist/src/execution/handlers/flow.js +289 -0
  154. package/dist/src/execution/handlers/query.d.ts +31 -0
  155. package/dist/src/execution/handlers/query.js +64 -0
  156. package/dist/src/execution/index.d.ts +4 -0
  157. package/dist/src/execution/index.js +3 -0
  158. package/dist/src/execution/mcp-helpers.d.ts +42 -0
  159. package/dist/src/execution/mcp-helpers.js +23 -0
  160. package/dist/src/execution/mcp-server.d.ts +33 -0
  161. package/dist/src/execution/mcp-server.js +1020 -0
  162. package/dist/src/execution/provision-worktree.d.ts +16 -0
  163. package/dist/src/execution/provision-worktree.js +123 -0
  164. package/dist/src/execution/tool-schemas.d.ts +40 -0
  165. package/dist/src/execution/tool-schemas.js +44 -0
  166. package/dist/src/logger.d.ts +8 -0
  167. package/dist/src/logger.js +12 -0
  168. package/dist/src/main.d.ts +14 -0
  169. package/dist/src/main.js +28 -0
  170. package/dist/src/repositories/drizzle/entity.repo.d.ts +27 -0
  171. package/dist/src/repositories/drizzle/entity.repo.js +190 -0
  172. package/dist/src/repositories/drizzle/event.repo.d.ts +12 -0
  173. package/dist/src/repositories/drizzle/event.repo.js +24 -0
  174. package/dist/src/repositories/drizzle/flow.repo.d.ts +22 -0
  175. package/dist/src/repositories/drizzle/flow.repo.js +364 -0
  176. package/dist/src/repositories/drizzle/gate.repo.d.ts +16 -0
  177. package/dist/src/repositories/drizzle/gate.repo.js +98 -0
  178. package/dist/src/repositories/drizzle/index.d.ts +6 -0
  179. package/dist/src/repositories/drizzle/index.js +7 -0
  180. package/dist/src/repositories/drizzle/invocation.repo.d.ts +23 -0
  181. package/dist/src/repositories/drizzle/invocation.repo.js +199 -0
  182. package/dist/src/repositories/drizzle/schema.d.ts +1932 -0
  183. package/dist/src/repositories/drizzle/schema.js +155 -0
  184. package/dist/src/repositories/drizzle/transition-log.repo.d.ts +11 -0
  185. package/dist/src/repositories/drizzle/transition-log.repo.js +42 -0
  186. package/dist/src/repositories/interfaces.d.ts +321 -0
  187. package/dist/src/repositories/interfaces.js +2 -0
  188. package/dist/src/utils/redact.d.ts +2 -0
  189. package/dist/src/utils/redact.js +62 -0
  190. package/dist/utils/redact.d.ts +2 -0
  191. package/dist/utils/redact.js +62 -0
  192. package/drizzle/.gitkeep +0 -0
  193. package/drizzle/0000_simple_surge.sql +144 -0
  194. package/drizzle/0001_peaceful_marvel_apes.sql +18 -0
  195. package/drizzle/0002_add_invocations_created_at.sql +1 -0
  196. package/drizzle/0003_drop_integration_config.sql +1 -0
  197. package/drizzle/0004_add_flow_discipline.sql +2 -0
  198. package/drizzle/0004_lucky_silverclaw.sql +5 -0
  199. package/drizzle/0005_old_blue_shield.sql +2 -0
  200. package/drizzle/0006_solid_magik.sql +2 -0
  201. package/drizzle/0007_fancy_luke_cage.sql +1 -0
  202. package/drizzle/0008_thick_dark_beast.sql +1 -0
  203. package/drizzle/0009_brief_midnight.sql +1 -0
  204. package/drizzle/0010_amusing_bastion.sql +1 -0
  205. package/drizzle/meta/0000_snapshot.json +996 -0
  206. package/drizzle/meta/0004_snapshot.json +1008 -0
  207. package/drizzle/meta/0005_snapshot.json +1023 -0
  208. package/drizzle/meta/0006_snapshot.json +1037 -0
  209. package/drizzle/meta/0007_snapshot.json +1044 -0
  210. package/drizzle/meta/0008_snapshot.json +1051 -0
  211. package/drizzle/meta/0009_snapshot.json +1058 -0
  212. package/drizzle/meta/0010_snapshot.json +1065 -0
  213. package/drizzle/meta/_journal.json +83 -0
  214. package/gates/.gitkeep +0 -0
  215. package/gates/blocking-graph.d.ts +26 -0
  216. package/gates/blocking-graph.js +102 -0
  217. package/gates/blocking-graph.ts +121 -0
  218. package/gates/check-design-posted.sh +39 -0
  219. package/gates/check-merge.sh +51 -0
  220. package/gates/check-pr-capacity.sh +17 -0
  221. package/gates/check-review-ready.sh +47 -0
  222. package/gates/check-spec-posted.sh +34 -0
  223. package/gates/check-unblocked.sh +56 -0
  224. package/gates/ci-green.sh +9 -0
  225. package/gates/merge-queue.sh +14 -0
  226. package/gates/review-bots-ready.sh +9 -0
  227. package/gates/spec-posted.sh +31 -0
  228. package/gates/test/bad-return-gate.d.ts +1 -0
  229. package/gates/test/bad-return-gate.js +4 -0
  230. package/gates/test/bad-return-gate.ts +4 -0
  231. package/gates/test/passing-gate.d.ts +2 -0
  232. package/gates/test/passing-gate.js +3 -0
  233. package/gates/test/passing-gate.ts +5 -0
  234. package/gates/test/slow-gate.d.ts +2 -0
  235. package/gates/test/slow-gate.js +5 -0
  236. package/gates/test/slow-gate.ts +7 -0
  237. package/gates/test/throwing-gate.d.ts +1 -0
  238. package/gates/test/throwing-gate.js +3 -0
  239. package/gates/test/throwing-gate.ts +3 -0
  240. package/gates/test-fail.sh +2 -0
  241. package/gates/test-pass.sh +2 -0
  242. package/gates/timeout-gate-script.sh +3 -0
  243. package/package.json +64 -0
@@ -0,0 +1,364 @@
1
+ import { and, eq } from "drizzle-orm";
2
+ import { flowDefinitions, flowVersions, stateDefinitions, transitionRules } from "./schema.js";
3
+ function toDate(v) {
4
+ return v != null ? new Date(v) : null;
5
+ }
6
+ function rowToState(r) {
7
+ return {
8
+ id: r.id,
9
+ flowId: r.flowId,
10
+ name: r.name,
11
+ modelTier: r.modelTier ?? null,
12
+ mode: (r.mode ?? "passive"),
13
+ promptTemplate: r.promptTemplate ?? null,
14
+ constraints: r.constraints,
15
+ onEnter: r.onEnter ?? null,
16
+ };
17
+ }
18
+ function rowToTransition(r) {
19
+ return {
20
+ id: r.id,
21
+ flowId: r.flowId,
22
+ fromState: r.fromState,
23
+ toState: r.toState,
24
+ trigger: r.trigger,
25
+ gateId: r.gateId ?? null,
26
+ condition: r.condition ?? null,
27
+ priority: r.priority ?? 0,
28
+ spawnFlow: r.spawnFlow ?? null,
29
+ spawnTemplate: r.spawnTemplate ?? null,
30
+ createdAt: toDate(r.createdAt),
31
+ };
32
+ }
33
+ function rowToFlow(r, states, transitions) {
34
+ return {
35
+ id: r.id,
36
+ name: r.name,
37
+ description: r.description ?? null,
38
+ entitySchema: r.entitySchema,
39
+ initialState: r.initialState,
40
+ maxConcurrent: r.maxConcurrent ?? 0,
41
+ maxConcurrentPerRepo: r.maxConcurrentPerRepo ?? 0,
42
+ affinityWindowMs: r.affinityWindowMs ?? 300000,
43
+ gateTimeoutMs: r.gateTimeoutMs ?? null,
44
+ version: r.version ?? 1,
45
+ createdBy: r.createdBy ?? null,
46
+ discipline: r.discipline ?? null,
47
+ defaultModelTier: r.defaultModelTier ?? null,
48
+ timeoutPrompt: r.timeoutPrompt ?? null,
49
+ createdAt: toDate(r.createdAt),
50
+ updatedAt: toDate(r.updatedAt),
51
+ states,
52
+ transitions,
53
+ };
54
+ }
55
+ export class DrizzleFlowRepository {
56
+ db;
57
+ constructor(db) {
58
+ this.db = db;
59
+ }
60
+ hydrateFlow(row) {
61
+ const states = this.db
62
+ .select()
63
+ .from(stateDefinitions)
64
+ .where(eq(stateDefinitions.flowId, row.id))
65
+ .all()
66
+ .map(rowToState);
67
+ const transitions = this.db
68
+ .select()
69
+ .from(transitionRules)
70
+ .where(eq(transitionRules.flowId, row.id))
71
+ .all()
72
+ .map(rowToTransition);
73
+ return rowToFlow(row, states, transitions);
74
+ }
75
+ async create(input) {
76
+ const now = Date.now();
77
+ const id = crypto.randomUUID();
78
+ const row = {
79
+ id,
80
+ name: input.name,
81
+ description: input.description ?? null,
82
+ entitySchema: (input.entitySchema ?? null),
83
+ initialState: input.initialState,
84
+ maxConcurrent: input.maxConcurrent ?? 0,
85
+ maxConcurrentPerRepo: input.maxConcurrentPerRepo ?? 0,
86
+ affinityWindowMs: input.affinityWindowMs ?? 300000,
87
+ gateTimeoutMs: input.gateTimeoutMs ?? null,
88
+ version: 1,
89
+ createdBy: input.createdBy ?? null,
90
+ discipline: input.discipline ?? null,
91
+ defaultModelTier: input.defaultModelTier ?? null,
92
+ timeoutPrompt: input.timeoutPrompt ?? null,
93
+ createdAt: now,
94
+ updatedAt: now,
95
+ };
96
+ this.db.insert(flowDefinitions).values(row).run();
97
+ return rowToFlow(row, [], []);
98
+ }
99
+ async get(id) {
100
+ const rows = this.db.select().from(flowDefinitions).where(eq(flowDefinitions.id, id)).all();
101
+ if (rows.length === 0)
102
+ return null;
103
+ return this.hydrateFlow(rows[0]);
104
+ }
105
+ async getByName(name) {
106
+ const rows = this.db.select().from(flowDefinitions).where(eq(flowDefinitions.name, name)).all();
107
+ if (rows.length === 0)
108
+ return null;
109
+ return this.hydrateFlow(rows[0]);
110
+ }
111
+ async list() {
112
+ const rows = this.db.select().from(flowDefinitions).all();
113
+ return rows.map((row) => this.hydrateFlow(row));
114
+ }
115
+ async listAll() {
116
+ return this.list();
117
+ }
118
+ async update(id, changes) {
119
+ const now = Date.now();
120
+ const current = this.db.select().from(flowDefinitions).where(eq(flowDefinitions.id, id)).all();
121
+ if (current.length === 0)
122
+ throw new Error(`Flow not found: ${id}`);
123
+ const updateValues = { updatedAt: now };
124
+ if (changes.name !== undefined)
125
+ updateValues.name = changes.name;
126
+ if (changes.description !== undefined)
127
+ updateValues.description = changes.description;
128
+ if (changes.entitySchema !== undefined)
129
+ updateValues.entitySchema = changes.entitySchema;
130
+ if (changes.initialState !== undefined)
131
+ updateValues.initialState = changes.initialState;
132
+ if (changes.maxConcurrent !== undefined)
133
+ updateValues.maxConcurrent = changes.maxConcurrent;
134
+ if (changes.maxConcurrentPerRepo !== undefined)
135
+ updateValues.maxConcurrentPerRepo = changes.maxConcurrentPerRepo;
136
+ if (changes.affinityWindowMs !== undefined)
137
+ updateValues.affinityWindowMs = changes.affinityWindowMs;
138
+ if (changes.gateTimeoutMs !== undefined)
139
+ updateValues.gateTimeoutMs = changes.gateTimeoutMs;
140
+ if (changes.version !== undefined)
141
+ updateValues.version = changes.version;
142
+ if (changes.createdBy !== undefined)
143
+ updateValues.createdBy = changes.createdBy;
144
+ if (changes.discipline !== undefined)
145
+ updateValues.discipline = changes.discipline;
146
+ if (changes.defaultModelTier !== undefined)
147
+ updateValues.defaultModelTier = changes.defaultModelTier;
148
+ if (changes.timeoutPrompt !== undefined)
149
+ updateValues.timeoutPrompt = changes.timeoutPrompt;
150
+ this.db.update(flowDefinitions).set(updateValues).where(eq(flowDefinitions.id, id)).run();
151
+ const updated = this.db.select().from(flowDefinitions).where(eq(flowDefinitions.id, id)).all();
152
+ return this.hydrateFlow(updated[0]);
153
+ }
154
+ async addState(flowId, state) {
155
+ const id = crypto.randomUUID();
156
+ const row = {
157
+ id,
158
+ flowId,
159
+ name: state.name,
160
+ agentRole: null,
161
+ modelTier: state.modelTier ?? null,
162
+ mode: state.mode ?? "passive",
163
+ promptTemplate: state.promptTemplate ?? null,
164
+ constraints: (state.constraints ?? null),
165
+ onEnter: (state.onEnter ?? null),
166
+ };
167
+ this.db.transaction((tx) => {
168
+ tx.insert(stateDefinitions).values(row).run();
169
+ tx.update(flowDefinitions).set({ updatedAt: Date.now() }).where(eq(flowDefinitions.id, flowId)).run();
170
+ });
171
+ return rowToState(row);
172
+ }
173
+ async updateState(stateId, changes) {
174
+ const existing = this.db.select().from(stateDefinitions).where(eq(stateDefinitions.id, stateId)).all();
175
+ if (existing.length === 0)
176
+ throw new Error(`State not found: ${stateId}`);
177
+ const updateValues = {};
178
+ if (changes.name !== undefined)
179
+ updateValues.name = changes.name;
180
+ if (changes.modelTier !== undefined)
181
+ updateValues.modelTier = changes.modelTier;
182
+ if (changes.mode !== undefined)
183
+ updateValues.mode = changes.mode;
184
+ if (changes.promptTemplate !== undefined)
185
+ updateValues.promptTemplate = changes.promptTemplate;
186
+ if (changes.constraints !== undefined)
187
+ updateValues.constraints = changes.constraints;
188
+ if (changes.onEnter !== undefined)
189
+ updateValues.onEnter = changes.onEnter;
190
+ if (Object.keys(updateValues).length > 0) {
191
+ this.db.update(stateDefinitions).set(updateValues).where(eq(stateDefinitions.id, stateId)).run();
192
+ }
193
+ const rows = this.db.select().from(stateDefinitions).where(eq(stateDefinitions.id, stateId)).all();
194
+ this.db.update(flowDefinitions).set({ updatedAt: Date.now() }).where(eq(flowDefinitions.id, rows[0].flowId)).run();
195
+ return rowToState(rows[0]);
196
+ }
197
+ async addTransition(flowId, transition) {
198
+ const now = Date.now();
199
+ const id = crypto.randomUUID();
200
+ const row = {
201
+ id,
202
+ flowId,
203
+ fromState: transition.fromState,
204
+ toState: transition.toState,
205
+ trigger: transition.trigger,
206
+ gateId: transition.gateId ?? null,
207
+ condition: transition.condition ?? null,
208
+ priority: transition.priority ?? 0,
209
+ spawnFlow: transition.spawnFlow ?? null,
210
+ spawnTemplate: transition.spawnTemplate ?? null,
211
+ createdAt: now,
212
+ };
213
+ this.db.transaction((tx) => {
214
+ tx.insert(transitionRules).values(row).run();
215
+ tx.update(flowDefinitions).set({ updatedAt: now }).where(eq(flowDefinitions.id, flowId)).run();
216
+ });
217
+ return rowToTransition(row);
218
+ }
219
+ async updateTransition(transitionId, changes) {
220
+ const existing = this.db.select().from(transitionRules).where(eq(transitionRules.id, transitionId)).all();
221
+ if (existing.length === 0)
222
+ throw new Error(`Transition not found: ${transitionId}`);
223
+ const updateValues = {};
224
+ if (changes.fromState !== undefined)
225
+ updateValues.fromState = changes.fromState;
226
+ if (changes.toState !== undefined)
227
+ updateValues.toState = changes.toState;
228
+ if (changes.trigger !== undefined)
229
+ updateValues.trigger = changes.trigger;
230
+ if (changes.gateId !== undefined)
231
+ updateValues.gateId = changes.gateId;
232
+ if (changes.condition !== undefined)
233
+ updateValues.condition = changes.condition;
234
+ if (changes.priority !== undefined)
235
+ updateValues.priority = changes.priority;
236
+ if (changes.spawnFlow !== undefined)
237
+ updateValues.spawnFlow = changes.spawnFlow;
238
+ if (changes.spawnTemplate !== undefined)
239
+ updateValues.spawnTemplate = changes.spawnTemplate;
240
+ if (Object.keys(updateValues).length > 0) {
241
+ this.db.update(transitionRules).set(updateValues).where(eq(transitionRules.id, transitionId)).run();
242
+ }
243
+ const rows = this.db.select().from(transitionRules).where(eq(transitionRules.id, transitionId)).all();
244
+ this.db.update(flowDefinitions).set({ updatedAt: Date.now() }).where(eq(flowDefinitions.id, rows[0].flowId)).run();
245
+ return rowToTransition(rows[0]);
246
+ }
247
+ async snapshot(flowId) {
248
+ const flow = await this.get(flowId);
249
+ if (!flow)
250
+ throw new Error(`Flow not found: ${flowId}`);
251
+ const now = Date.now();
252
+ const id = crypto.randomUUID();
253
+ const snapshotData = {
254
+ id: flow.id,
255
+ name: flow.name,
256
+ description: flow.description,
257
+ entitySchema: flow.entitySchema,
258
+ initialState: flow.initialState,
259
+ maxConcurrent: flow.maxConcurrent,
260
+ maxConcurrentPerRepo: flow.maxConcurrentPerRepo,
261
+ affinityWindowMs: flow.affinityWindowMs,
262
+ gateTimeoutMs: flow.gateTimeoutMs,
263
+ version: flow.version,
264
+ createdBy: flow.createdBy,
265
+ timeoutPrompt: flow.timeoutPrompt,
266
+ createdAt: flow.createdAt,
267
+ updatedAt: flow.updatedAt,
268
+ discipline: flow.discipline,
269
+ defaultModelTier: flow.defaultModelTier,
270
+ states: flow.states,
271
+ transitions: flow.transitions,
272
+ };
273
+ const nextVersion = this.db.transaction((tx) => {
274
+ const existing = tx.select().from(flowVersions).where(eq(flowVersions.flowId, flowId)).all();
275
+ const maxVersion = existing.reduce((max, r) => Math.max(max, r.version), 0);
276
+ const version = maxVersion + 1;
277
+ tx.insert(flowVersions)
278
+ .values({
279
+ id,
280
+ flowId,
281
+ version,
282
+ snapshot: snapshotData,
283
+ changedBy: null,
284
+ changeReason: null,
285
+ createdAt: now,
286
+ })
287
+ .run();
288
+ return version;
289
+ });
290
+ return {
291
+ id,
292
+ flowId,
293
+ version: nextVersion,
294
+ snapshot: snapshotData,
295
+ changedBy: null,
296
+ changeReason: null,
297
+ createdAt: new Date(now),
298
+ };
299
+ }
300
+ async restore(flowId, version) {
301
+ const versionRows = this.db
302
+ .select()
303
+ .from(flowVersions)
304
+ .where(and(eq(flowVersions.flowId, flowId), eq(flowVersions.version, version)))
305
+ .all();
306
+ if (versionRows.length === 0)
307
+ throw new Error(`Version ${version} not found for flow ${flowId}`);
308
+ const snap = versionRows[0].snapshot;
309
+ this.db.transaction((tx) => {
310
+ tx.delete(transitionRules).where(eq(transitionRules.flowId, flowId)).run();
311
+ tx.delete(stateDefinitions).where(eq(stateDefinitions.flowId, flowId)).run();
312
+ for (const s of snap.states) {
313
+ tx.insert(stateDefinitions)
314
+ .values({
315
+ id: s.id,
316
+ flowId,
317
+ name: s.name,
318
+ modelTier: s.modelTier,
319
+ mode: s.mode ?? "passive",
320
+ promptTemplate: s.promptTemplate,
321
+ constraints: s.constraints,
322
+ onEnter: (s.onEnter ?? null),
323
+ })
324
+ .run();
325
+ }
326
+ for (const t of snap.transitions) {
327
+ tx.insert(transitionRules)
328
+ .values({
329
+ id: t.id,
330
+ flowId,
331
+ fromState: t.fromState,
332
+ toState: t.toState,
333
+ trigger: t.trigger,
334
+ gateId: t.gateId,
335
+ condition: t.condition,
336
+ priority: t.priority ?? 0,
337
+ spawnFlow: t.spawnFlow,
338
+ spawnTemplate: t.spawnTemplate,
339
+ createdAt: t.createdAt ? new Date(t.createdAt).getTime() : null,
340
+ })
341
+ .run();
342
+ }
343
+ tx.update(flowDefinitions)
344
+ .set({
345
+ name: snap.name,
346
+ description: snap.description,
347
+ entitySchema: snap.entitySchema,
348
+ initialState: snap.initialState,
349
+ maxConcurrent: snap.maxConcurrent,
350
+ maxConcurrentPerRepo: snap.maxConcurrentPerRepo,
351
+ affinityWindowMs: snap.affinityWindowMs,
352
+ gateTimeoutMs: snap.gateTimeoutMs ?? null,
353
+ version: snap.version,
354
+ createdBy: snap.createdBy,
355
+ discipline: snap.discipline,
356
+ defaultModelTier: snap.defaultModelTier ?? null,
357
+ timeoutPrompt: snap.timeoutPrompt,
358
+ updatedAt: Date.now(),
359
+ })
360
+ .where(eq(flowDefinitions.id, flowId))
361
+ .run();
362
+ });
363
+ }
364
+ }
@@ -0,0 +1,16 @@
1
+ import type { BetterSQLite3Database } from "drizzle-orm/better-sqlite3";
2
+ import type { CreateGateInput, Gate, GateResult, IGateRepository } from "../interfaces.js";
3
+ import type * as schema from "./schema.js";
4
+ type Db = BetterSQLite3Database<typeof schema>;
5
+ export declare class DrizzleGateRepository implements IGateRepository {
6
+ private db;
7
+ constructor(db: Db);
8
+ create(gate: CreateGateInput): Promise<Gate>;
9
+ get(id: string): Promise<Gate | null>;
10
+ getByName(name: string): Promise<Gate | null>;
11
+ listAll(): Promise<Gate[]>;
12
+ record(entityId: string, gateId: string, passed: boolean, output: string): Promise<GateResult>;
13
+ update(id: string, changes: Partial<Pick<Gate, "command" | "functionRef" | "apiConfig" | "timeoutMs" | "failurePrompt" | "timeoutPrompt">>): Promise<Gate>;
14
+ resultsFor(entityId: string): Promise<GateResult[]>;
15
+ }
16
+ export {};
@@ -0,0 +1,98 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { asc, eq, sql } from "drizzle-orm";
3
+ import { gateDefinitions, gateResults } from "./schema.js";
4
+ function toGate(row) {
5
+ return {
6
+ id: row.id,
7
+ name: row.name,
8
+ type: row.type,
9
+ command: row.command,
10
+ functionRef: row.functionRef,
11
+ apiConfig: row.apiConfig,
12
+ timeoutMs: row.timeoutMs ?? null,
13
+ failurePrompt: row.failurePrompt ?? null,
14
+ timeoutPrompt: row.timeoutPrompt ?? null,
15
+ };
16
+ }
17
+ function toGateResult(row) {
18
+ return {
19
+ id: row.id,
20
+ entityId: row.entityId,
21
+ gateId: row.gateId,
22
+ passed: !!row.passed,
23
+ output: row.output,
24
+ evaluatedAt: row.evaluatedAt != null ? new Date(row.evaluatedAt) : null,
25
+ };
26
+ }
27
+ export class DrizzleGateRepository {
28
+ db;
29
+ constructor(db) {
30
+ this.db = db;
31
+ }
32
+ async create(gate) {
33
+ const id = randomUUID();
34
+ const values = {
35
+ id,
36
+ name: gate.name,
37
+ type: gate.type,
38
+ command: gate.command ?? null,
39
+ functionRef: gate.functionRef ?? null,
40
+ apiConfig: gate.apiConfig ?? null,
41
+ ...(gate.timeoutMs != null ? { timeoutMs: gate.timeoutMs } : {}),
42
+ failurePrompt: gate.failurePrompt ?? null,
43
+ timeoutPrompt: gate.timeoutPrompt ?? null,
44
+ };
45
+ this.db.insert(gateDefinitions).values(values).run();
46
+ const row = this.db.select().from(gateDefinitions).where(eq(gateDefinitions.id, id)).get();
47
+ if (!row)
48
+ throw new Error(`Gate ${id} not found after insert`);
49
+ return toGate(row);
50
+ }
51
+ async get(id) {
52
+ const row = this.db.select().from(gateDefinitions).where(eq(gateDefinitions.id, id)).get();
53
+ return row ? toGate(row) : null;
54
+ }
55
+ async getByName(name) {
56
+ const row = this.db.select().from(gateDefinitions).where(eq(gateDefinitions.name, name)).get();
57
+ return row ? toGate(row) : null;
58
+ }
59
+ async listAll() {
60
+ const rows = this.db.select().from(gateDefinitions).all();
61
+ return rows.map(toGate);
62
+ }
63
+ async record(entityId, gateId, passed, output) {
64
+ const id = randomUUID();
65
+ const evaluatedAt = Date.now();
66
+ this.db
67
+ .insert(gateResults)
68
+ .values({
69
+ id,
70
+ entityId,
71
+ gateId,
72
+ passed: passed ? 1 : 0,
73
+ output,
74
+ evaluatedAt,
75
+ })
76
+ .run();
77
+ const row = this.db.select().from(gateResults).where(eq(gateResults.id, id)).get();
78
+ if (!row)
79
+ throw new Error(`GateResult ${id} not found after insert`);
80
+ return toGateResult(row);
81
+ }
82
+ async update(id, changes) {
83
+ this.db.update(gateDefinitions).set(changes).where(eq(gateDefinitions.id, id)).run();
84
+ const row = this.db.select().from(gateDefinitions).where(eq(gateDefinitions.id, id)).get();
85
+ if (!row)
86
+ throw new Error(`Gate ${id} not found after update`);
87
+ return toGate(row);
88
+ }
89
+ async resultsFor(entityId) {
90
+ const rows = this.db
91
+ .select()
92
+ .from(gateResults)
93
+ .where(eq(gateResults.entityId, entityId))
94
+ .orderBy(asc(gateResults.evaluatedAt), sql `rowid`)
95
+ .all();
96
+ return rows.map(toGateResult);
97
+ }
98
+ }
@@ -0,0 +1,6 @@
1
+ export { DrizzleEntityRepository } from "./entity.repo.js";
2
+ export { DrizzleEventRepository } from "./event.repo.js";
3
+ export { DrizzleFlowRepository } from "./flow.repo.js";
4
+ export { DrizzleGateRepository } from "./gate.repo.js";
5
+ export { DrizzleInvocationRepository } from "./invocation.repo.js";
6
+ export { DrizzleTransitionLogRepository } from "./transition-log.repo.js";
@@ -0,0 +1,7 @@
1
+ // Drizzle ORM implementations of repository interfaces
2
+ export { DrizzleEntityRepository } from "./entity.repo.js";
3
+ export { DrizzleEventRepository } from "./event.repo.js";
4
+ export { DrizzleFlowRepository } from "./flow.repo.js";
5
+ export { DrizzleGateRepository } from "./gate.repo.js";
6
+ export { DrizzleInvocationRepository } from "./invocation.repo.js";
7
+ export { DrizzleTransitionLogRepository } from "./transition-log.repo.js";
@@ -0,0 +1,23 @@
1
+ import type { BetterSQLite3Database } from "drizzle-orm/better-sqlite3";
2
+ import type { Artifacts, IInvocationRepository, Invocation, Mode } from "../interfaces.js";
3
+ import type * as schema from "./schema.js";
4
+ type Db = BetterSQLite3Database<typeof schema>;
5
+ export declare class DrizzleInvocationRepository implements IInvocationRepository {
6
+ private db;
7
+ constructor(db: Db);
8
+ create(entityId: string, stage: string, prompt: string, mode: Mode, ttlMs?: number, context?: Record<string, unknown>): Promise<Invocation>;
9
+ get(id: string): Promise<Invocation | null>;
10
+ claim(invocationId: string, agentId: string): Promise<Invocation | null>;
11
+ complete(id: string, signal: string, artifacts?: Artifacts): Promise<Invocation>;
12
+ fail(id: string, error: string): Promise<Invocation>;
13
+ releaseClaim(id: string): Promise<void>;
14
+ findByEntity(entityId: string): Promise<Invocation[]>;
15
+ findUnclaimedWithAffinity(flowId: string, role: string, workerId: string): Promise<Invocation[]>;
16
+ findUnclaimedByFlow(flowId: string): Promise<Invocation[]>;
17
+ findByFlow(flowId: string): Promise<Invocation[]>;
18
+ findUnclaimedActive(flowId?: string): Promise<Invocation[]>;
19
+ countActiveByFlow(flowId: string): Promise<number>;
20
+ countPendingByFlow(flowId: string): Promise<number>;
21
+ reapExpired(): Promise<Invocation[]>;
22
+ }
23
+ export {};