@smthrs/control 0.0.0-stage → 1.0.0-rc.3

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 (418) hide show
  1. package/CHANGELOG.md +194 -0
  2. package/LICENSE +21 -0
  3. package/README.md +168 -2
  4. package/dist/cjs/ApprovalAuthority.d.ts +73 -0
  5. package/dist/cjs/ApprovalAuthority.d.ts.map +1 -0
  6. package/dist/cjs/ApprovalAuthority.js +62 -0
  7. package/dist/cjs/ApprovalAuthority.js.map +7 -0
  8. package/dist/cjs/Cancellation.d.ts +107 -0
  9. package/dist/cjs/Cancellation.d.ts.map +1 -0
  10. package/dist/cjs/Cancellation.js +72 -0
  11. package/dist/cjs/Cancellation.js.map +7 -0
  12. package/dist/cjs/Channels.d.ts +170 -0
  13. package/dist/cjs/Channels.d.ts.map +1 -0
  14. package/dist/cjs/Channels.js +278 -0
  15. package/dist/cjs/Channels.js.map +7 -0
  16. package/dist/cjs/Control.d.ts +202 -0
  17. package/dist/cjs/Control.d.ts.map +1 -0
  18. package/dist/cjs/Control.js +47 -0
  19. package/dist/cjs/Control.js.map +7 -0
  20. package/dist/cjs/ControlClient.d.ts +52 -0
  21. package/dist/cjs/ControlClient.d.ts.map +1 -0
  22. package/dist/cjs/ControlClient.js +191 -0
  23. package/dist/cjs/ControlClient.js.map +7 -0
  24. package/dist/cjs/ControlError.d.ts +318 -0
  25. package/dist/cjs/ControlError.d.ts.map +1 -0
  26. package/dist/cjs/ControlError.js +249 -0
  27. package/dist/cjs/ControlError.js.map +7 -0
  28. package/dist/cjs/ControlExecutor.d.ts +372 -0
  29. package/dist/cjs/ControlExecutor.d.ts.map +1 -0
  30. package/dist/cjs/ControlExecutor.js +123 -0
  31. package/dist/cjs/ControlExecutor.js.map +7 -0
  32. package/dist/cjs/ControlFacts.d.ts +454 -0
  33. package/dist/cjs/ControlFacts.d.ts.map +1 -0
  34. package/dist/cjs/ControlFacts.js +261 -0
  35. package/dist/cjs/ControlFacts.js.map +7 -0
  36. package/dist/cjs/ControlLive.d.ts +23 -0
  37. package/dist/cjs/ControlLive.d.ts.map +1 -0
  38. package/dist/cjs/ControlLive.js +1280 -0
  39. package/dist/cjs/ControlLive.js.map +7 -0
  40. package/dist/cjs/ControlRpcs.d.ts +1204 -0
  41. package/dist/cjs/ControlRpcs.d.ts.map +1 -0
  42. package/dist/cjs/ControlRpcs.js +247 -0
  43. package/dist/cjs/ControlRpcs.js.map +7 -0
  44. package/dist/cjs/ControlRuntime.d.ts +635 -0
  45. package/dist/cjs/ControlRuntime.d.ts.map +1 -0
  46. package/dist/cjs/ControlRuntime.js +740 -0
  47. package/dist/cjs/ControlRuntime.js.map +7 -0
  48. package/dist/cjs/ControlSchema.d.ts +2642 -0
  49. package/dist/cjs/ControlSchema.d.ts.map +1 -0
  50. package/dist/cjs/ControlSchema.js +634 -0
  51. package/dist/cjs/ControlSchema.js.map +7 -0
  52. package/dist/cjs/ControlServer.d.ts +51 -0
  53. package/dist/cjs/ControlServer.d.ts.map +1 -0
  54. package/dist/cjs/ControlServer.js +121 -0
  55. package/dist/cjs/ControlServer.js.map +7 -0
  56. package/dist/cjs/Credential.d.ts +136 -0
  57. package/dist/cjs/Credential.d.ts.map +1 -0
  58. package/dist/cjs/Credential.js +168 -0
  59. package/dist/cjs/Credential.js.map +7 -0
  60. package/dist/cjs/CredentialCipher.d.ts +90 -0
  61. package/dist/cjs/CredentialCipher.d.ts.map +1 -0
  62. package/dist/cjs/CredentialCipher.js +45 -0
  63. package/dist/cjs/CredentialCipher.js.map +7 -0
  64. package/dist/cjs/CredentialStore.d.ts +97 -0
  65. package/dist/cjs/CredentialStore.d.ts.map +1 -0
  66. package/dist/cjs/CredentialStore.js +81 -0
  67. package/dist/cjs/CredentialStore.js.map +7 -0
  68. package/dist/cjs/DispatchReader.d.ts +112 -0
  69. package/dist/cjs/DispatchReader.d.ts.map +1 -0
  70. package/dist/cjs/DispatchReader.js +45 -0
  71. package/dist/cjs/DispatchReader.js.map +7 -0
  72. package/dist/cjs/Health.d.ts +333 -0
  73. package/dist/cjs/Health.d.ts.map +1 -0
  74. package/dist/cjs/Health.js +311 -0
  75. package/dist/cjs/Health.js.map +7 -0
  76. package/dist/cjs/JevSessionChecker.d.ts +57 -0
  77. package/dist/cjs/JevSessionChecker.d.ts.map +1 -0
  78. package/dist/cjs/JevSessionChecker.js +113 -0
  79. package/dist/cjs/JevSessionChecker.js.map +7 -0
  80. package/dist/cjs/Lineage.d.ts +131 -0
  81. package/dist/cjs/Lineage.d.ts.map +1 -0
  82. package/dist/cjs/Lineage.js +81 -0
  83. package/dist/cjs/Lineage.js.map +7 -0
  84. package/dist/cjs/Migrations.d.ts +34 -0
  85. package/dist/cjs/Migrations.d.ts.map +1 -0
  86. package/dist/cjs/Migrations.js +60 -0
  87. package/dist/cjs/Migrations.js.map +7 -0
  88. package/dist/cjs/Monitor.d.ts +282 -0
  89. package/dist/cjs/Monitor.d.ts.map +1 -0
  90. package/dist/cjs/Monitor.js +283 -0
  91. package/dist/cjs/Monitor.js.map +7 -0
  92. package/dist/cjs/ScopedToken.d.ts +193 -0
  93. package/dist/cjs/ScopedToken.d.ts.map +1 -0
  94. package/dist/cjs/ScopedToken.js +135 -0
  95. package/dist/cjs/ScopedToken.js.map +7 -0
  96. package/dist/cjs/SqlControlRuntime.d.ts +161 -0
  97. package/dist/cjs/SqlControlRuntime.d.ts.map +1 -0
  98. package/dist/cjs/SqlControlRuntime.js +1521 -0
  99. package/dist/cjs/SqlControlRuntime.js.map +7 -0
  100. package/dist/cjs/SqlCredentialStore.d.ts +43 -0
  101. package/dist/cjs/SqlCredentialStore.d.ts.map +1 -0
  102. package/dist/cjs/SqlCredentialStore.js +113 -0
  103. package/dist/cjs/SqlCredentialStore.js.map +7 -0
  104. package/dist/cjs/Steering.d.ts +69 -0
  105. package/dist/cjs/Steering.d.ts.map +1 -0
  106. package/dist/cjs/Steering.js +49 -0
  107. package/dist/cjs/Steering.js.map +7 -0
  108. package/dist/cjs/SystemFlows.d.ts +223 -0
  109. package/dist/cjs/SystemFlows.d.ts.map +1 -0
  110. package/dist/cjs/SystemFlows.js +195 -0
  111. package/dist/cjs/SystemFlows.js.map +7 -0
  112. package/dist/cjs/WebCryptoCipher.d.ts +49 -0
  113. package/dist/cjs/WebCryptoCipher.d.ts.map +1 -0
  114. package/dist/cjs/WebCryptoCipher.js +129 -0
  115. package/dist/cjs/WebCryptoCipher.js.map +7 -0
  116. package/dist/cjs/WebhookChannel.d.ts +113 -0
  117. package/dist/cjs/WebhookChannel.d.ts.map +1 -0
  118. package/dist/cjs/WebhookChannel.js +98 -0
  119. package/dist/cjs/WebhookChannel.js.map +7 -0
  120. package/dist/cjs/index.d.ts +160 -0
  121. package/dist/cjs/index.d.ts.map +1 -0
  122. package/dist/cjs/index.js +91 -0
  123. package/dist/cjs/index.js.map +7 -0
  124. package/dist/cjs/internal/MutationBoundary.d.ts +27 -0
  125. package/dist/cjs/internal/MutationBoundary.d.ts.map +1 -0
  126. package/dist/cjs/internal/MutationBoundary.js +50 -0
  127. package/dist/cjs/internal/MutationBoundary.js.map +7 -0
  128. package/dist/cjs/internal/activeFibers.d.ts +12 -0
  129. package/dist/cjs/internal/activeFibers.d.ts.map +1 -0
  130. package/dist/cjs/internal/activeFibers.js +30 -0
  131. package/dist/cjs/internal/activeFibers.js.map +7 -0
  132. package/dist/cjs/internal/issues.d.ts +28 -0
  133. package/dist/cjs/internal/issues.d.ts.map +1 -0
  134. package/dist/cjs/internal/issues.js +34 -0
  135. package/dist/cjs/internal/issues.js.map +7 -0
  136. package/dist/cjs/internal/planning.d.ts +347 -0
  137. package/dist/cjs/internal/planning.d.ts.map +1 -0
  138. package/dist/cjs/internal/planning.js +137 -0
  139. package/dist/cjs/internal/planning.js.map +7 -0
  140. package/dist/cjs/internal/sqlSchemaErrors.d.ts +18 -0
  141. package/dist/cjs/internal/sqlSchemaErrors.d.ts.map +1 -0
  142. package/dist/cjs/internal/sqlSchemaErrors.js +40 -0
  143. package/dist/cjs/internal/sqlSchemaErrors.js.map +7 -0
  144. package/dist/cjs/migrations/0001_control_tables.d.ts +18 -0
  145. package/dist/cjs/migrations/0001_control_tables.d.ts.map +1 -0
  146. package/dist/cjs/migrations/0001_control_tables.js +115 -0
  147. package/dist/cjs/migrations/0001_control_tables.js.map +7 -0
  148. package/dist/cjs/migrations/0002_run_keys.d.ts +15 -0
  149. package/dist/cjs/migrations/0002_run_keys.d.ts.map +1 -0
  150. package/dist/cjs/migrations/0002_run_keys.js +44 -0
  151. package/dist/cjs/migrations/0002_run_keys.js.map +7 -0
  152. package/dist/cjs/migrations/0003_signal_commands.d.ts +13 -0
  153. package/dist/cjs/migrations/0003_signal_commands.d.ts.map +1 -0
  154. package/dist/cjs/migrations/0003_signal_commands.js +50 -0
  155. package/dist/cjs/migrations/0003_signal_commands.js.map +7 -0
  156. package/dist/cjs/migrations/0004_approval_decisions.d.ts +13 -0
  157. package/dist/cjs/migrations/0004_approval_decisions.d.ts.map +1 -0
  158. package/dist/cjs/migrations/0004_approval_decisions.js +45 -0
  159. package/dist/cjs/migrations/0004_approval_decisions.js.map +7 -0
  160. package/dist/cjs/migrations/0005_signal_principals.d.ts +18 -0
  161. package/dist/cjs/migrations/0005_signal_principals.d.ts.map +1 -0
  162. package/dist/cjs/migrations/0005_signal_principals.js +45 -0
  163. package/dist/cjs/migrations/0005_signal_principals.js.map +7 -0
  164. package/dist/cjs/migrations/0006_run_principals.d.ts +18 -0
  165. package/dist/cjs/migrations/0006_run_principals.d.ts.map +1 -0
  166. package/dist/cjs/migrations/0006_run_principals.js +49 -0
  167. package/dist/cjs/migrations/0006_run_principals.js.map +7 -0
  168. package/dist/cjs/migrations/0007_resume_consent.d.ts +18 -0
  169. package/dist/cjs/migrations/0007_resume_consent.d.ts.map +1 -0
  170. package/dist/cjs/migrations/0007_resume_consent.js +46 -0
  171. package/dist/cjs/migrations/0007_resume_consent.js.map +7 -0
  172. package/dist/cjs/package.json +1 -0
  173. package/dist/cjs/test/TestControl.d.ts +19 -0
  174. package/dist/cjs/test/TestControl.d.ts.map +1 -0
  175. package/dist/cjs/test/TestControl.js +62 -0
  176. package/dist/cjs/test/TestControl.js.map +7 -0
  177. package/dist/esm/ApprovalAuthority.d.ts +73 -0
  178. package/dist/esm/ApprovalAuthority.d.ts.map +1 -0
  179. package/dist/esm/ApprovalAuthority.js +72 -0
  180. package/dist/esm/ApprovalAuthority.js.map +1 -0
  181. package/dist/esm/Cancellation.d.ts +107 -0
  182. package/dist/esm/Cancellation.d.ts.map +1 -0
  183. package/dist/esm/Cancellation.js +116 -0
  184. package/dist/esm/Cancellation.js.map +1 -0
  185. package/dist/esm/Channels.d.ts +170 -0
  186. package/dist/esm/Channels.d.ts.map +1 -0
  187. package/dist/esm/Channels.js +312 -0
  188. package/dist/esm/Channels.js.map +1 -0
  189. package/dist/esm/Control.d.ts +202 -0
  190. package/dist/esm/Control.d.ts.map +1 -0
  191. package/dist/esm/Control.js +42 -0
  192. package/dist/esm/Control.js.map +1 -0
  193. package/dist/esm/ControlClient.d.ts +52 -0
  194. package/dist/esm/ControlClient.d.ts.map +1 -0
  195. package/dist/esm/ControlClient.js +217 -0
  196. package/dist/esm/ControlClient.js.map +1 -0
  197. package/dist/esm/ControlError.d.ts +318 -0
  198. package/dist/esm/ControlError.d.ts.map +1 -0
  199. package/dist/esm/ControlError.js +359 -0
  200. package/dist/esm/ControlError.js.map +1 -0
  201. package/dist/esm/ControlExecutor.d.ts +372 -0
  202. package/dist/esm/ControlExecutor.d.ts.map +1 -0
  203. package/dist/esm/ControlExecutor.js +212 -0
  204. package/dist/esm/ControlExecutor.js.map +1 -0
  205. package/dist/esm/ControlFacts.d.ts +454 -0
  206. package/dist/esm/ControlFacts.d.ts.map +1 -0
  207. package/dist/esm/ControlFacts.js +324 -0
  208. package/dist/esm/ControlFacts.js.map +1 -0
  209. package/dist/esm/ControlLive.d.ts +23 -0
  210. package/dist/esm/ControlLive.d.ts.map +1 -0
  211. package/dist/esm/ControlLive.js +1585 -0
  212. package/dist/esm/ControlLive.js.map +1 -0
  213. package/dist/esm/ControlRpcs.d.ts +1204 -0
  214. package/dist/esm/ControlRpcs.d.ts.map +1 -0
  215. package/dist/esm/ControlRpcs.js +299 -0
  216. package/dist/esm/ControlRpcs.js.map +1 -0
  217. package/dist/esm/ControlRuntime.d.ts +635 -0
  218. package/dist/esm/ControlRuntime.d.ts.map +1 -0
  219. package/dist/esm/ControlRuntime.js +807 -0
  220. package/dist/esm/ControlRuntime.js.map +1 -0
  221. package/dist/esm/ControlSchema.d.ts +2642 -0
  222. package/dist/esm/ControlSchema.d.ts.map +1 -0
  223. package/dist/esm/ControlSchema.js +1030 -0
  224. package/dist/esm/ControlSchema.js.map +1 -0
  225. package/dist/esm/ControlServer.d.ts +51 -0
  226. package/dist/esm/ControlServer.d.ts.map +1 -0
  227. package/dist/esm/ControlServer.js +145 -0
  228. package/dist/esm/ControlServer.js.map +1 -0
  229. package/dist/esm/Credential.d.ts +136 -0
  230. package/dist/esm/Credential.d.ts.map +1 -0
  231. package/dist/esm/Credential.js +190 -0
  232. package/dist/esm/Credential.js.map +1 -0
  233. package/dist/esm/CredentialCipher.d.ts +90 -0
  234. package/dist/esm/CredentialCipher.d.ts.map +1 -0
  235. package/dist/esm/CredentialCipher.js +56 -0
  236. package/dist/esm/CredentialCipher.js.map +1 -0
  237. package/dist/esm/CredentialStore.d.ts +97 -0
  238. package/dist/esm/CredentialStore.d.ts.map +1 -0
  239. package/dist/esm/CredentialStore.js +101 -0
  240. package/dist/esm/CredentialStore.js.map +1 -0
  241. package/dist/esm/DispatchReader.d.ts +112 -0
  242. package/dist/esm/DispatchReader.d.ts.map +1 -0
  243. package/dist/esm/DispatchReader.js +76 -0
  244. package/dist/esm/DispatchReader.js.map +1 -0
  245. package/dist/esm/Health.d.ts +333 -0
  246. package/dist/esm/Health.d.ts.map +1 -0
  247. package/dist/esm/Health.js +400 -0
  248. package/dist/esm/Health.js.map +1 -0
  249. package/dist/esm/JevSessionChecker.d.ts +57 -0
  250. package/dist/esm/JevSessionChecker.d.ts.map +1 -0
  251. package/dist/esm/JevSessionChecker.js +108 -0
  252. package/dist/esm/JevSessionChecker.js.map +1 -0
  253. package/dist/esm/Lineage.d.ts +131 -0
  254. package/dist/esm/Lineage.d.ts.map +1 -0
  255. package/dist/esm/Lineage.js +174 -0
  256. package/dist/esm/Lineage.js.map +1 -0
  257. package/dist/esm/Migrations.d.ts +34 -0
  258. package/dist/esm/Migrations.d.ts.map +1 -0
  259. package/dist/esm/Migrations.js +53 -0
  260. package/dist/esm/Migrations.js.map +1 -0
  261. package/dist/esm/Monitor.d.ts +282 -0
  262. package/dist/esm/Monitor.d.ts.map +1 -0
  263. package/dist/esm/Monitor.js +415 -0
  264. package/dist/esm/Monitor.js.map +1 -0
  265. package/dist/esm/ScopedToken.d.ts +193 -0
  266. package/dist/esm/ScopedToken.d.ts.map +1 -0
  267. package/dist/esm/ScopedToken.js +224 -0
  268. package/dist/esm/ScopedToken.js.map +1 -0
  269. package/dist/esm/SqlControlRuntime.d.ts +161 -0
  270. package/dist/esm/SqlControlRuntime.d.ts.map +1 -0
  271. package/dist/esm/SqlControlRuntime.js +1756 -0
  272. package/dist/esm/SqlControlRuntime.js.map +1 -0
  273. package/dist/esm/SqlCredentialStore.d.ts +43 -0
  274. package/dist/esm/SqlCredentialStore.d.ts.map +1 -0
  275. package/dist/esm/SqlCredentialStore.js +97 -0
  276. package/dist/esm/SqlCredentialStore.js.map +1 -0
  277. package/dist/esm/Steering.d.ts +69 -0
  278. package/dist/esm/Steering.d.ts.map +1 -0
  279. package/dist/esm/Steering.js +89 -0
  280. package/dist/esm/Steering.js.map +1 -0
  281. package/dist/esm/SystemFlows.d.ts +223 -0
  282. package/dist/esm/SystemFlows.d.ts.map +1 -0
  283. package/dist/esm/SystemFlows.js +198 -0
  284. package/dist/esm/SystemFlows.js.map +1 -0
  285. package/dist/esm/WebCryptoCipher.d.ts +49 -0
  286. package/dist/esm/WebCryptoCipher.d.ts.map +1 -0
  287. package/dist/esm/WebCryptoCipher.js +123 -0
  288. package/dist/esm/WebCryptoCipher.js.map +1 -0
  289. package/dist/esm/WebhookChannel.d.ts +113 -0
  290. package/dist/esm/WebhookChannel.d.ts.map +1 -0
  291. package/dist/esm/WebhookChannel.js +109 -0
  292. package/dist/esm/WebhookChannel.js.map +1 -0
  293. package/dist/esm/index.d.ts +160 -0
  294. package/dist/esm/index.d.ts.map +1 -0
  295. package/dist/esm/index.js +160 -0
  296. package/dist/esm/index.js.map +1 -0
  297. package/dist/esm/internal/MutationBoundary.d.ts +27 -0
  298. package/dist/esm/internal/MutationBoundary.d.ts.map +1 -0
  299. package/dist/esm/internal/MutationBoundary.js +40 -0
  300. package/dist/esm/internal/MutationBoundary.js.map +1 -0
  301. package/dist/esm/internal/activeFibers.d.ts +12 -0
  302. package/dist/esm/internal/activeFibers.d.ts.map +1 -0
  303. package/dist/esm/internal/activeFibers.js +17 -0
  304. package/dist/esm/internal/activeFibers.js.map +1 -0
  305. package/dist/esm/internal/issues.d.ts +28 -0
  306. package/dist/esm/internal/issues.d.ts.map +1 -0
  307. package/dist/esm/internal/issues.js +35 -0
  308. package/dist/esm/internal/issues.js.map +1 -0
  309. package/dist/esm/internal/planning.d.ts +347 -0
  310. package/dist/esm/internal/planning.d.ts.map +1 -0
  311. package/dist/esm/internal/planning.js +199 -0
  312. package/dist/esm/internal/planning.js.map +1 -0
  313. package/dist/esm/internal/sqlSchemaErrors.d.ts +18 -0
  314. package/dist/esm/internal/sqlSchemaErrors.d.ts.map +1 -0
  315. package/dist/esm/internal/sqlSchemaErrors.js +38 -0
  316. package/dist/esm/internal/sqlSchemaErrors.js.map +1 -0
  317. package/dist/esm/migrations/0001_control_tables.d.ts +18 -0
  318. package/dist/esm/migrations/0001_control_tables.d.ts.map +1 -0
  319. package/dist/esm/migrations/0001_control_tables.js +96 -0
  320. package/dist/esm/migrations/0001_control_tables.js.map +1 -0
  321. package/dist/esm/migrations/0002_run_keys.d.ts +15 -0
  322. package/dist/esm/migrations/0002_run_keys.d.ts.map +1 -0
  323. package/dist/esm/migrations/0002_run_keys.js +22 -0
  324. package/dist/esm/migrations/0002_run_keys.js.map +1 -0
  325. package/dist/esm/migrations/0003_signal_commands.d.ts +13 -0
  326. package/dist/esm/migrations/0003_signal_commands.d.ts.map +1 -0
  327. package/dist/esm/migrations/0003_signal_commands.js +26 -0
  328. package/dist/esm/migrations/0003_signal_commands.js.map +1 -0
  329. package/dist/esm/migrations/0004_approval_decisions.d.ts +13 -0
  330. package/dist/esm/migrations/0004_approval_decisions.d.ts.map +1 -0
  331. package/dist/esm/migrations/0004_approval_decisions.js +25 -0
  332. package/dist/esm/migrations/0004_approval_decisions.js.map +1 -0
  333. package/dist/esm/migrations/0005_signal_principals.d.ts +18 -0
  334. package/dist/esm/migrations/0005_signal_principals.d.ts.map +1 -0
  335. package/dist/esm/migrations/0005_signal_principals.js +27 -0
  336. package/dist/esm/migrations/0005_signal_principals.js.map +1 -0
  337. package/dist/esm/migrations/0006_run_principals.d.ts +18 -0
  338. package/dist/esm/migrations/0006_run_principals.d.ts.map +1 -0
  339. package/dist/esm/migrations/0006_run_principals.js +30 -0
  340. package/dist/esm/migrations/0006_run_principals.js.map +1 -0
  341. package/dist/esm/migrations/0007_resume_consent.d.ts +18 -0
  342. package/dist/esm/migrations/0007_resume_consent.d.ts.map +1 -0
  343. package/dist/esm/migrations/0007_resume_consent.js +27 -0
  344. package/dist/esm/migrations/0007_resume_consent.js.map +1 -0
  345. package/dist/esm/test/TestControl.d.ts +19 -0
  346. package/dist/esm/test/TestControl.d.ts.map +1 -0
  347. package/dist/esm/test/TestControl.js +30 -0
  348. package/dist/esm/test/TestControl.js.map +1 -0
  349. package/docs/README.md +189 -0
  350. package/docs/api.md +982 -0
  351. package/docs/concepts/authority.md +109 -0
  352. package/docs/concepts/cancellation.md +129 -0
  353. package/docs/concepts/lineage.md +132 -0
  354. package/docs/concepts/ownership.md +139 -0
  355. package/docs/concepts/projections.md +203 -0
  356. package/docs/concepts/receipts.md +128 -0
  357. package/docs/guides/approvals.md +284 -0
  358. package/docs/guides/cancel-and-resume.md +162 -0
  359. package/docs/guides/durable-storage.md +147 -0
  360. package/docs/guides/implement-an-executor.md +173 -0
  361. package/docs/guides/ingest-a-webhook.md +177 -0
  362. package/docs/guides/list-runs.md +160 -0
  363. package/docs/guides/monitor-runs.md +176 -0
  364. package/docs/guides/observe-health.md +147 -0
  365. package/docs/guides/postgres-tests.md +5 -0
  366. package/docs/guides/serve-over-rpc.md +220 -0
  367. package/docs/guides/signal-a-run.md +53 -0
  368. package/docs/guides/steer-a-run.md +138 -0
  369. package/docs/guides/store-credentials.md +164 -0
  370. package/docs/guides/testing.md +139 -0
  371. package/docs/guides/watch-a-run.md +154 -0
  372. package/docs/installation.md +106 -0
  373. package/docs/quickstart.md +163 -0
  374. package/docs/troubleshooting.md +208 -0
  375. package/package.json +405 -3
  376. package/src/ApprovalAuthority.ts +114 -0
  377. package/src/Cancellation.ts +172 -0
  378. package/src/Channels.ts +493 -0
  379. package/src/Control.ts +337 -0
  380. package/src/ControlClient.ts +319 -0
  381. package/src/ControlError.ts +378 -0
  382. package/src/ControlExecutor.ts +490 -0
  383. package/src/ControlFacts.ts +383 -0
  384. package/src/ControlLive.ts +2113 -0
  385. package/src/ControlRpcs.ts +443 -0
  386. package/src/ControlRuntime.ts +1597 -0
  387. package/src/ControlSchema.ts +1380 -0
  388. package/src/ControlServer.ts +182 -0
  389. package/src/Credential.ts +310 -0
  390. package/src/CredentialCipher.ts +110 -0
  391. package/src/CredentialStore.ts +152 -0
  392. package/src/DispatchReader.ts +122 -0
  393. package/src/Health.ts +591 -0
  394. package/src/JevSessionChecker.ts +127 -0
  395. package/src/Lineage.ts +203 -0
  396. package/src/Migrations.ts +56 -0
  397. package/src/Monitor.ts +600 -0
  398. package/src/ScopedToken.ts +306 -0
  399. package/src/SqlControlRuntime.ts +2476 -0
  400. package/src/SqlCredentialStore.ts +148 -0
  401. package/src/Steering.ts +96 -0
  402. package/src/SystemFlows.ts +225 -0
  403. package/src/WebCryptoCipher.ts +169 -0
  404. package/src/WebhookChannel.ts +166 -0
  405. package/src/index.ts +188 -0
  406. package/src/internal/MutationBoundary.ts +46 -0
  407. package/src/internal/activeFibers.ts +22 -0
  408. package/src/internal/issues.ts +40 -0
  409. package/src/internal/planning.ts +262 -0
  410. package/src/internal/sqlSchemaErrors.ts +37 -0
  411. package/src/migrations/0001_control_tables.ts +99 -0
  412. package/src/migrations/0002_run_keys.ts +23 -0
  413. package/src/migrations/0003_signal_commands.ts +27 -0
  414. package/src/migrations/0004_approval_decisions.ts +25 -0
  415. package/src/migrations/0005_signal_principals.ts +27 -0
  416. package/src/migrations/0006_run_principals.ts +31 -0
  417. package/src/migrations/0007_resume_consent.ts +28 -0
  418. package/src/test/TestControl.ts +47 -0
@@ -0,0 +1,1280 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+ var ControlLive_exports = {};
30
+ __export(ControlLive_exports, {
31
+ layer: () => layer
32
+ });
33
+ module.exports = __toCommonJS(ControlLive_exports);
34
+ var Sha256 = __toESM(require("@smthrs/crypto/Sha256"), 1);
35
+ var Fault = __toESM(require("@smthrs/flow/Fault"), 1);
36
+ var import_journal = require("@smthrs/journal");
37
+ var import_notifications = require("@smthrs/notifications");
38
+ var SteerPayload = __toESM(require("@smthrs/notifications/SteerPayload"), 1);
39
+ var import_registry = require("@smthrs/registry");
40
+ var import_Descriptor = require("@smthrs/registry/Descriptor");
41
+ var import_effect = require("effect");
42
+ var Cancellation = __toESM(require("./Cancellation.js"), 1);
43
+ var import_Control = require("./Control.js");
44
+ var import_ControlError = require("./ControlError.js");
45
+ var import_ControlExecutor = require("./ControlExecutor.js");
46
+ var ControlFacts = __toESM(require("./ControlFacts.js"), 1);
47
+ var import_ControlRuntime = require("./ControlRuntime.js");
48
+ var import_ControlSchema = require("./ControlSchema.js");
49
+ var DispatchReader = __toESM(require("./DispatchReader.js"), 1);
50
+ var import_issues = require("./internal/issues.js");
51
+ var MutationBoundary = __toESM(require("./internal/MutationBoundary.js"), 1);
52
+ var import_planning = require("./internal/planning.js");
53
+ var Lineage = __toESM(require("./Lineage.js"), 1);
54
+ var Steering = __toESM(require("./Steering.js"), 1);
55
+ const sourceId = import_journal.JournalEvent.SourceId.make("/control");
56
+ const snapshotPageSize = 1024;
57
+ const snapshotPartitionConcurrency = 8;
58
+ const partitionPageSize = 100;
59
+ const unavailable = (feature) => new import_ControlError.Unavailable({ feature, ticket: "control-runtime-engine-integration" });
60
+ const watchReadFailed = (cause) => cause.code === "journal_closed" ? unavailable("watch") : new import_ControlError.PersistenceError({
61
+ operation: "watch",
62
+ message: `Reading the control journal failed (${cause.code})`,
63
+ cause
64
+ });
65
+ const accepted = (key, runId) => runId === void 0 ? { _tag: "Accepted", receiptId: key } : { _tag: "Accepted", receiptId: key, runId };
66
+ const terminal = (status) => status === "cancelled" || status === "completed" || status === "failed";
67
+ const live = (status) => status === "running" || status === "accepted";
68
+ const terminalOrAccepted = (key, run) => terminal(run.status) ? { _tag: "Terminal", runId: run.runId, status: run.status } : accepted(key, run.runId);
69
+ const withoutStampedPrincipal = (input) => {
70
+ if (input === null || typeof input !== "object") return input;
71
+ const { principal: _principal, ...rest } = input;
72
+ const message = rest["message"];
73
+ if (message === null || typeof message !== "object") return rest;
74
+ const { principal: _messagePrincipal, ...messageRest } = message;
75
+ return { ...rest, message: messageRest };
76
+ };
77
+ const fingerprint = (operation, principal, input) => `control-mutation:v2:${Sha256.digestSync((0, import_planning.canonical)({
78
+ operation,
79
+ actor: { id: principal.id, kind: principal.kind },
80
+ intent: withoutStampedPrincipal(input)
81
+ }))}`;
82
+ const json = (value) => JSON.parse(JSON.stringify(value));
83
+ const declaredTrigger = (decodedInput) => {
84
+ if (decodedInput === null || typeof decodedInput !== "object" || Array.isArray(decodedInput)) return void 0;
85
+ const held = decodedInput["trigger"];
86
+ return held !== null && typeof held === "object" && !Array.isArray(held) ? held : void 0;
87
+ };
88
+ const invalid = (issue) => new import_ControlError.InvalidInput({ issue });
89
+ const AttributedApprovalInput = import_effect.Schema.Struct({
90
+ ...import_ControlSchema.ApprovalInputSchema.fields,
91
+ principal: import_effect.Schema.optional(import_ControlSchema.Principal)
92
+ });
93
+ const AttributedReasonedMutationInput = import_effect.Schema.Struct({
94
+ ...import_ControlSchema.ReasonedMutationInputSchema.fields,
95
+ principal: import_effect.Schema.optional(import_ControlSchema.Principal)
96
+ });
97
+ const AttributedResumeInput = import_effect.Schema.Struct({
98
+ ...import_ControlSchema.ResumeInputSchema.fields,
99
+ principal: import_effect.Schema.optional(import_ControlSchema.Principal)
100
+ });
101
+ const AttributedRunInput = import_effect.Schema.Union([
102
+ import_effect.Schema.Struct({ ...import_ControlSchema.RunInputSchema.members[0].fields, principal: import_effect.Schema.optional(import_ControlSchema.Principal), reservedRunId: import_effect.Schema.optional(import_effect.Schema.NonEmptyString) }),
103
+ import_effect.Schema.Struct({ ...import_ControlSchema.RunInputSchema.members[1].fields, principal: import_effect.Schema.optional(import_ControlSchema.Principal) })
104
+ ]);
105
+ const AttributedSignalInput = import_effect.Schema.Struct({
106
+ ...import_ControlSchema.SignalInputSchema.fields,
107
+ principal: import_effect.Schema.optional(import_ControlSchema.Principal)
108
+ });
109
+ const validIdempotencyKey = (value) => {
110
+ if (value.length === 0 || value.length > 1024 || value.includes("\0")) return false;
111
+ for (let index = 0; index < value.length; index++) {
112
+ const unit = value.charCodeAt(index);
113
+ if (unit >= 55296 && unit <= 56319) {
114
+ const low = value.charCodeAt(++index);
115
+ if (!(low >= 56320 && low <= 57343)) return false;
116
+ continue;
117
+ }
118
+ if (unit >= 56320 && unit <= 57343) return false;
119
+ }
120
+ return true;
121
+ };
122
+ const snapshotMutation = (operation, decode, input) => import_effect.Effect.suspend(() => {
123
+ const admitted = MutationBoundary.admit(input);
124
+ if (!admitted.ok) return import_effect.Effect.fail(invalid(`${operation}: ${admitted.complaint}`));
125
+ return decode(admitted.value).pipe(
126
+ import_effect.Effect.mapError((error) => invalid(`${operation}: invalid mutation at ${(0, import_issues.schemaIssuePath)(error)}`)),
127
+ import_effect.Effect.flatMap(
128
+ (snapshot) => validIdempotencyKey(snapshot.idempotencyKey) ? import_effect.Effect.succeed(snapshot) : import_effect.Effect.fail(invalid(`${operation}.idempotencyKey: must be 1 to 1024 well-formed characters`))
129
+ )
130
+ );
131
+ });
132
+ const snapshotApproval = (operation, input) => snapshotMutation(operation, import_effect.Schema.decodeUnknownEffect(AttributedApprovalInput), input);
133
+ const snapshotReasonedMutation = (operation, input) => snapshotMutation(operation, import_effect.Schema.decodeUnknownEffect(AttributedReasonedMutationInput), input);
134
+ const snapshotResume = (input) => snapshotMutation("resume", import_effect.Schema.decodeUnknownEffect(AttributedResumeInput), input);
135
+ const snapshotRun = (input) => snapshotMutation("run", import_effect.Schema.decodeUnknownEffect(AttributedRunInput), input);
136
+ const snapshotSignal = (input) => snapshotMutation("signal", import_effect.Schema.decodeUnknownEffect(AttributedSignalInput), input);
137
+ const snapshotSteer = (input) => snapshotMutation("steer", import_effect.Schema.decodeUnknownEffect(import_ControlSchema.SteerInputSchema), input);
138
+ const pageBounds = (cursor, limit) => {
139
+ if (limit !== void 0 && (!Number.isSafeInteger(limit) || limit < 1 || limit > import_ControlSchema.maxPageSize)) {
140
+ return import_effect.Effect.fail(
141
+ invalid(`limit: must be an integer between 1 and ${import_ControlSchema.maxPageSize}, received ${String(limit)}`)
142
+ );
143
+ }
144
+ if (cursor === void 0) return import_effect.Effect.succeed({ start: 0, size: limit ?? import_ControlSchema.defaultPageSize });
145
+ const start = Number(cursor);
146
+ return Number.isSafeInteger(start) && start >= 0 ? import_effect.Effect.succeed({ start, size: limit ?? import_ControlSchema.defaultPageSize }) : import_effect.Effect.fail(invalid(`cursor: must be a cursor this listing returned, received ${JSON.stringify(cursor)}`));
147
+ };
148
+ const cursorNatural = import_effect.Schema.Number.check(
149
+ import_effect.Schema.isInt(),
150
+ import_effect.Schema.isGreaterThanOrEqualTo(0),
151
+ import_effect.Schema.isLessThanOrEqualTo(Number.MAX_SAFE_INTEGER)
152
+ );
153
+ const runCursor = import_effect.Schema.fromJsonString(import_effect.Schema.Struct({
154
+ version: import_effect.Schema.Literal(1),
155
+ filters: import_effect.Schema.String,
156
+ source: import_effect.Schema.Union([import_effect.Schema.Literal(0), import_effect.Schema.Literal(1)]),
157
+ sequence: cursorNatural,
158
+ createdAt: cursorNatural,
159
+ runId: import_effect.Schema.NonEmptyString
160
+ }));
161
+ const page = (values, bounds) => {
162
+ const items = values.slice(bounds.start, bounds.start + bounds.size);
163
+ const next = bounds.start + items.length;
164
+ return next < values.length ? { items, nextCursor: String(next) } : { items };
165
+ };
166
+ const eventFromEntry = (entry) => ({
167
+ sequence: entry.seq,
168
+ kind: entry.eventType,
169
+ runId: entry.runId,
170
+ occurredAt: entry.emittedAtMs,
171
+ payload: entry.payload
172
+ });
173
+ const layer = import_effect.Layer.effect(
174
+ import_Control.Control,
175
+ import_effect.Effect.gen(function* () {
176
+ const runtime = yield* import_ControlRuntime.ControlRuntime;
177
+ const journal = yield* import_journal.Journal.Journal;
178
+ const notifications = yield* import_notifications.NotificationQueue.NotificationQueue;
179
+ const registry = yield* import_registry.Registry.Registry;
180
+ const executor = yield* import_effect.Effect.serviceOption(import_ControlExecutor.ControlExecutor);
181
+ const dispatch = import_effect.Option.getOrElse(
182
+ yield* import_effect.Effect.serviceOption(DispatchReader.DispatchReader),
183
+ DispatchReader.makeNone
184
+ );
185
+ const observing = import_effect.Option.isSome(executor) && executor.value.readExecution !== void 0;
186
+ const observe = (run) => import_effect.Effect.gen(function* () {
187
+ if (import_effect.Option.isNone(executor) || executor.value.readExecution === void 0) return run;
188
+ const observed = yield* executor.value.readExecution(run.runId);
189
+ if (observed._tag === "Missing") return { ...run, executionObservation: "missing" };
190
+ return {
191
+ ...run,
192
+ executionObservation: "observed",
193
+ executionView: observed.executionView,
194
+ status: observed.status,
195
+ waitingReason: observed.waitingReason,
196
+ parentRunId: observed.parentRunId,
197
+ lineageId: observed.lineageId,
198
+ roundOrdinal: observed.roundOrdinal,
199
+ // The executor's answer replaces this plane's copy here as it does
200
+ // for every other observed field. It has to: a control plane over
201
+ // its own coordination database cannot see the executions a flow
202
+ // spawned, so a nested `HumanTask` park is a fact only the executor
203
+ // holds. Where the two share one database the runtime computes the
204
+ // same rows itself and the observation reproduces them.
205
+ pendingWaits: observed.pendingWaits
206
+ };
207
+ });
208
+ const getRun = (runId) => runtime.getRun(runId).pipe(import_effect.Effect.flatMap(observe));
209
+ const refuseCodeDrift = (runId) => runtime.codeDrift(runId).pipe(import_effect.Effect.flatMap((drift) => drift === void 0 ? import_effect.Effect.void : import_effect.Effect.fail(drift)));
210
+ const withCodeDrift = (run) => terminal(run.status) ? import_effect.Effect.succeed(run) : runtime.codeDrift(run.runId).pipe(
211
+ import_effect.Effect.map(
212
+ (drift) => drift === void 0 ? run : {
213
+ ...run,
214
+ codeDrift: {
215
+ ...drift.recorded === void 0 ? {} : { recorded: drift.recorded },
216
+ ...drift.current === void 0 ? {} : { current: drift.current },
217
+ ...drift.recordedEngine === void 0 ? {} : { recordedEngine: drift.recordedEngine },
218
+ ...drift.currentEngine === void 0 ? {} : { currentEngine: drift.currentEngine }
219
+ }
220
+ }
221
+ ),
222
+ import_effect.Effect.orElseSucceed(() => run)
223
+ );
224
+ const mutationSemaphore = yield* import_effect.Semaphore.make(1);
225
+ const record = (runId, eventType, payload) => (
226
+ // Unfenced: the control plane mutates runs it does not own — that is
227
+ // the point of a control plane — so its event records are
228
+ // first-writer-wins admissions, not owner-fenced lifecycle writes.
229
+ journal.emitDurableUnfenced(
230
+ new import_journal.JournalEvent.Input({
231
+ runId: import_journal.JournalEvent.RunId.make(runId),
232
+ sourceId,
233
+ eventType,
234
+ payload: json(payload)
235
+ })
236
+ ).pipe(
237
+ import_effect.Effect.map((receipt) => receipt.seq),
238
+ import_effect.Effect.mapError(
239
+ (cause) => new import_ControlError.PersistenceError({
240
+ operation: eventType,
241
+ message: `Failed to persist ${eventType}`,
242
+ cause
243
+ })
244
+ )
245
+ )
246
+ );
247
+ const emit = (runId, eventType, payload) => import_effect.Effect.asVoid(record(runId, eventType, payload));
248
+ const settleUnlaunched = (runId, cause, fault) => import_effect.Effect.gen(function* () {
249
+ const fence = yield* runtime.claimFence(runId);
250
+ const run = yield* runtime.writeStatus(runId, fence, "failed");
251
+ yield* emit(
252
+ runId,
253
+ "control.run.failed",
254
+ json({ runId, status: "failed", cause: cause.slice(0, 4096), fault, ...ControlFacts.runFact(run) })
255
+ );
256
+ }).pipe(
257
+ journal.transact,
258
+ import_effect.Effect.catchCause(
259
+ (failure) => import_effect.Effect.annotateLogs(
260
+ import_effect.Effect.logWarning("A refused launch could not be settled"),
261
+ { runId, cause: import_effect.Cause.pretty(failure) }
262
+ )
263
+ )
264
+ );
265
+ const transact = (operation, effect) => mutationSemaphore.withPermits(1)(
266
+ journal.transact(effect).pipe(
267
+ import_effect.Effect.mapError(
268
+ (cause) => cause instanceof import_journal.Journal.JournalError ? new import_ControlError.PersistenceError({
269
+ operation: `${operation}.idempotency`,
270
+ message: `Failed to commit ${operation} and its idempotency receipt atomically`,
271
+ cause
272
+ }) : cause
273
+ )
274
+ )
275
+ );
276
+ const mutate = (operation, key, principal, mutationFingerprint, effect, replay = true, claimRunKey = false) => transact(
277
+ operation,
278
+ import_effect.Effect.gen(function* () {
279
+ const durableKey = (0, import_planning.mutationKey)(operation, key, principal);
280
+ const prior = yield* runtime.lookupMutation(durableKey, mutationFingerprint);
281
+ if (prior !== void 0 && (replay || prior._tag === "Conflict")) {
282
+ return prior._tag === "AlreadyApplied" ? { ...prior, receiptId: key } : prior;
283
+ }
284
+ const claim = claimRunKey ? yield* runtime.claimRunKey(durableKey, mutationFingerprint) : void 0;
285
+ if (claim?._tag === "Raced") return (0, import_planning.alreadyApplied)(key, claim.receipt);
286
+ return yield* import_effect.Effect.gen(function* () {
287
+ const receipt = yield* effect;
288
+ if (receipt._tag === "Parked" && claimRunKey) {
289
+ yield* runtime.releaseRunKey(durableKey);
290
+ } else if (receipt._tag !== "Parked" && prior === void 0) {
291
+ yield* runtime.recordMutation(durableKey, mutationFingerprint, receipt);
292
+ }
293
+ return receipt;
294
+ }).pipe(import_effect.Effect.onExit(
295
+ (exit) => claim?._tag === "Claimed" && import_effect.Exit.isFailure(exit) ? runtime.releaseRunKey(durableKey) : import_effect.Effect.void
296
+ ));
297
+ })
298
+ );
299
+ const takeUpResume = (runId, sequence) => import_effect.Option.isNone(executor) ? import_effect.Effect.void : executor.value.resumeRun({ runId }).pipe(
300
+ import_effect.Effect.flatMap((uptake) => uptake === "resuming" ? runtime.clearResume(runId, sequence) : import_effect.Effect.void)
301
+ );
302
+ const hand = (launch) => import_effect.Effect.gen(function* () {
303
+ const acceptance = import_effect.Option.isSome(executor) ? yield* executor.value.launch(launch).pipe(
304
+ import_effect.Effect.tapError((error) => settleUnlaunched(error.runId, error.message, Fault.of(error)))
305
+ ) : "pending";
306
+ yield* transact(
307
+ "run.acceptance",
308
+ import_effect.Effect.gen(function* () {
309
+ const current = yield* runtime.getRun(launch.run.runId);
310
+ if (current.status !== "accepted") return;
311
+ const fence = yield* runtime.claimFence(current.runId);
312
+ const run = acceptance === "accepted" ? yield* runtime.writeStatus(current.runId, fence, "running") : yield* runtime.releasePending(current.runId, fence);
313
+ yield* emit(
314
+ run.runId,
315
+ acceptance === "accepted" ? "control.run.running" : "control.run.pending",
316
+ {
317
+ runId: run.runId,
318
+ status: run.status,
319
+ ...ControlFacts.runFact(run)
320
+ }
321
+ );
322
+ })
323
+ );
324
+ });
325
+ const relaunchStranded = (runId) => import_effect.Effect.gen(function* () {
326
+ const current = yield* runtime.getRun(runId);
327
+ if (current.status !== "accepted" || current.ownerId === void 0) return;
328
+ const ours = yield* runtime.claimFence(runId).pipe(
329
+ import_effect.Effect.as(true),
330
+ import_effect.Effect.catchTag("/control/ClaimLost", () => import_effect.Effect.succeed(false))
331
+ );
332
+ if (ours) return;
333
+ yield* refuseCodeDrift(runId);
334
+ const claimed = yield* runtime.resume(runId, { scope: "launched" }).pipe(
335
+ import_effect.Effect.catchTag("/control/ClaimLost", () => import_effect.Effect.succeed(void 0))
336
+ );
337
+ if (claimed === void 0 || claimed.status !== "accepted" || claimed.planId === void 0) return;
338
+ yield* hand({ plan: yield* runtime.getPlan(claimed.planId), run: claimed });
339
+ }).pipe(
340
+ // The recorded receipt is the retry's answer either way; a relaunch
341
+ // that fails leaves the run where it was for the next retry.
342
+ import_effect.Effect.catch(
343
+ (failure) => import_effect.Effect.annotateLogs(
344
+ import_effect.Effect.logWarning("A stranded run could not be relaunched"),
345
+ { runId, cause: failure.message }
346
+ )
347
+ )
348
+ );
349
+ const decide = (decision, submitted) => import_effect.Effect.gen(function* () {
350
+ const input = yield* snapshotApproval(decision, submitted);
351
+ const principal = yield* runtime.stampPrincipal(input.principal);
352
+ return yield* decideAs(decision, input, principal).pipe(
353
+ // Every refusal leaves a durable audit record: who asked, for which
354
+ // target, under which scope. The journal row's time is when. It is
355
+ // written after the refused mutation rolled back, so it is the only
356
+ // row the refusal adds; a record that cannot be written fails the
357
+ // call rather than leaving an unaudited refusal.
358
+ import_effect.Effect.catchTag("/control/Unauthorized", (refusal) => emit(
359
+ input.target._tag === "Plan" ? `plan:${input.target.planId}` : input.target.runId,
360
+ "control.approval.refused",
361
+ json({ decision, principal, scope: input.scope, target: input.target })
362
+ ).pipe(import_effect.Effect.andThen(import_effect.Effect.fail(refusal))))
363
+ );
364
+ });
365
+ const decideAs = (decision, input, principal) => import_effect.Effect.gen(function* () {
366
+ yield* runtime.authorizeApproval({ principal, target: input.target, decision, scope: input.scope });
367
+ if (input.target._tag === "Node") {
368
+ const current = yield* getRun(input.target.runId).pipe(
369
+ import_effect.Effect.catchTag("/control/RunNotFound", () => import_effect.Effect.succeed(void 0))
370
+ );
371
+ if (current !== void 0 && terminal(current.status)) {
372
+ const settled = { _tag: "Terminal", runId: current.runId, status: current.status };
373
+ return settled;
374
+ }
375
+ }
376
+ let delegated;
377
+ const receipt = yield* mutate(
378
+ decision,
379
+ input.idempotencyKey,
380
+ principal,
381
+ fingerprint(decision, principal, input),
382
+ import_effect.Effect.gen(function* () {
383
+ if (input.target._tag === "Node") yield* refuseCodeDrift(input.target.runId);
384
+ const token = yield* runtime.lookupApproval(input.target);
385
+ yield* runtime.resolveApproval(token, decision, principal, input.scope);
386
+ if (decision === "approved") {
387
+ yield* runtime.installBulkGrant(token, input.target.envelope, input.scope);
388
+ }
389
+ yield* emit(
390
+ input.target._tag === "Plan" ? `plan:${input.target.planId}` : input.target.runId,
391
+ `control.approval.${decision}`,
392
+ json({
393
+ ...ControlFacts.approvalDecisionFact(token.tokenId, input.target),
394
+ target: input.target._tag,
395
+ scope: input.scope,
396
+ envelope: input.target.envelope,
397
+ principal
398
+ })
399
+ );
400
+ if (input.target._tag === "Plan") return accepted(input.idempotencyKey);
401
+ const runId = input.target.runId;
402
+ delegated = yield* runtime.requestResume(runId);
403
+ yield* emit(runId, "control.run.resumed", { runId });
404
+ return accepted(input.idempotencyKey, runId);
405
+ })
406
+ );
407
+ if (input.target._tag === "Node" && delegated !== void 0) {
408
+ yield* takeUpResume(input.target.runId, delegated);
409
+ }
410
+ return receipt;
411
+ });
412
+ const runMutation = (submitted) => import_effect.Effect.gen(function* () {
413
+ const input = yield* snapshotResume(submitted);
414
+ const settled = yield* getRun(input.runId);
415
+ if (terminal(settled.status)) {
416
+ return { _tag: "Terminal", runId: settled.runId, status: settled.status };
417
+ }
418
+ const principal = yield* runtime.stampPrincipal(input.principal);
419
+ return yield* mutate(
420
+ "resume",
421
+ input.idempotencyKey,
422
+ principal,
423
+ fingerprint("resume", principal, input),
424
+ import_effect.Effect.gen(function* () {
425
+ const current = yield* getRun(input.runId);
426
+ if (terminal(current.status)) {
427
+ return { _tag: "Terminal", runId: current.runId, status: current.status };
428
+ }
429
+ if (input.allowCodeDrift !== true) yield* refuseCodeDrift(input.runId);
430
+ let handedTo;
431
+ const claimed = yield* (input.allowCodeDrift === true ? runtime.resumeAdopting(input.runId, { scope: "launched" }) : runtime.resume(input.runId, { scope: "launched" })).pipe(
432
+ import_effect.Effect.catchTag("/control/ClaimLost", (error) => {
433
+ if (error.parkedBy !== void 0) {
434
+ handedTo = error.parkedBy;
435
+ return import_effect.Effect.succeed(void 0);
436
+ }
437
+ if (error.reason !== void 0) return import_effect.Effect.fail(error);
438
+ return runtime.getRun(input.runId).pipe(import_effect.Effect.flatMap(
439
+ (stored) => (
440
+ // A retained fork is parked in control and pending in the
441
+ // engine. Its accepted read overlay is not a live claim.
442
+ // Never replace that engine continuation with control state.
443
+ live(stored.status) || live(current.status) && current.executionView?.current.status !== "pending" ? import_effect.Effect.fail(new import_ControlError.ClaimLost({ runId: input.runId })) : import_effect.Effect.succeed(void 0)
444
+ )
445
+ ));
446
+ })
447
+ );
448
+ const sequence = yield* record(
449
+ input.runId,
450
+ "control.run.resume",
451
+ json({
452
+ runId: input.runId,
453
+ status: (claimed ?? current).status,
454
+ ...claimed === void 0 ? {} : ControlFacts.runFact(claimed),
455
+ principal,
456
+ ...input.reason === void 0 ? {} : { reason: input.reason },
457
+ ...handedTo === void 0 ? {} : { handedTo }
458
+ })
459
+ );
460
+ if (handedTo !== void 0) {
461
+ yield* runtime.requestResume(input.runId, { consent: sequence });
462
+ const handedOff = {
463
+ _tag: "Accepted",
464
+ receiptId: input.idempotencyKey,
465
+ runId: input.runId,
466
+ handedTo
467
+ };
468
+ return handedOff;
469
+ }
470
+ return claimed === void 0 ? accepted(input.idempotencyKey, input.runId) : terminalOrAccepted(input.idempotencyKey, claimed);
471
+ })
472
+ );
473
+ });
474
+ const executorRequestCancel = (runId) => import_effect.Option.isNone(executor) ? import_effect.Effect.succeed("unknown") : executor.value.requestCancel({ runId });
475
+ const executorSettleCancelledPark = (runId) => import_effect.Option.isNone(executor) ? import_effect.Effect.void : executor.value.settleCancelledPark({ runId });
476
+ const reconcileTerminal = (runId, status) => import_effect.Effect.gen(function* () {
477
+ const current = yield* runtime.getRun(runId);
478
+ if (terminal(current.status)) return;
479
+ const fence = yield* runtime.claimFence(runId).pipe(
480
+ // A parked coordination row has released its fence. Claim that row
481
+ // before copying the engine's terminal fact; a live peer still wins.
482
+ import_effect.Effect.catchTag("/control/ClaimLost", () => runtime.resume(runId).pipe(import_effect.Effect.andThen(runtime.claimFence(runId))))
483
+ );
484
+ const run = yield* runtime.writeStatus(runId, fence, status);
485
+ yield* emit(runId, `control.run.${status}`, json({ runId, status, ...ControlFacts.runFact(run) }));
486
+ }).pipe(
487
+ journal.transact,
488
+ import_effect.Effect.catchCause(
489
+ (failure) => import_effect.Effect.annotateLogs(
490
+ import_effect.Effect.logWarning("A settled engine row could not be reconciled onto the control row"),
491
+ { runId, status, cause: import_effect.Cause.pretty(failure) }
492
+ )
493
+ )
494
+ );
495
+ const wake = (run, messageId) => {
496
+ if (run.status !== "parked") return import_effect.Effect.void;
497
+ if (run.waitingReason !== "event" && run.waitingReason !== "released") return import_effect.Effect.void;
498
+ return refuseCodeDrift(run.runId).pipe(
499
+ import_effect.Effect.andThen(runtime.resume(run.runId, { scope: "launched" })),
500
+ import_effect.Effect.flatMap(
501
+ (resumed) => emit(
502
+ run.runId,
503
+ "control.steer.woke",
504
+ {
505
+ runId: run.runId,
506
+ messageId,
507
+ status: resumed.status,
508
+ ...ControlFacts.runFact(resumed)
509
+ }
510
+ )
511
+ ),
512
+ // A detached live host keeps its fence. Delegate the admitted event
513
+ // wake to it, just as a node decision does, instead of leaving an idle
514
+ // retained module with input it will never get a boundary to drain.
515
+ import_effect.Effect.catchTag("/control/ClaimLost", (refusal) => refusal.parkedBy === void 0 ? import_effect.Effect.void : import_effect.Effect.gen(function* () {
516
+ const delegated = yield* runtime.requestResume(run.runId).pipe(
517
+ import_effect.Effect.catchTag("/control/InvalidInput", () => import_effect.Effect.succeed(void 0))
518
+ );
519
+ if (delegated !== void 0) yield* emit(run.runId, "control.run.resumed", { runId: run.runId });
520
+ })),
521
+ import_effect.Effect.catchTag("/control/RunNotFound", () => import_effect.Effect.void),
522
+ import_effect.Effect.catchTag(
523
+ "/control/CodeDrift",
524
+ (drift) => import_effect.Effect.annotateLogs(import_effect.Effect.logWarning("A steer did not wake a run whose code changed"), {
525
+ runId: run.runId,
526
+ cause: drift.message
527
+ })
528
+ )
529
+ );
530
+ };
531
+ const withSteering = (runs) => import_effect.Effect.forEach(runs, (run) => notifications.pending(run.runId).pipe(
532
+ import_effect.Effect.map((pending) => ({
533
+ ...run,
534
+ steering: {
535
+ pending: pending.filter((notification) => notification.delivery === "steer").length
536
+ }
537
+ })),
538
+ import_effect.Effect.catchTag("/notifications/NotificationError", () => import_effect.Effect.succeed(run)),
539
+ import_effect.Effect.mapError(
540
+ (cause) => new import_ControlError.PersistenceError({
541
+ operation: "control.list.steering",
542
+ message: `Failed to read pending steering for ${run.runId}`,
543
+ cause
544
+ })
545
+ )
546
+ ));
547
+ const triggerRuns = (triggerId) => import_effect.Effect.gen(function* () {
548
+ let start = 0;
549
+ while (true) {
550
+ const fires = yield* dispatch.fires({
551
+ _tag: "fires",
552
+ filters: { triggerId },
553
+ limit: import_ControlSchema.maxPageSize,
554
+ ...start === 0 ? {} : { cursor: String(start) }
555
+ });
556
+ if (fires.length <= start + import_ControlSchema.maxPageSize) {
557
+ return fires.flatMap((fire) => fire.triggerId === triggerId && fire.runId !== void 0 ? [fire.runId] : []);
558
+ }
559
+ start = Math.max(fires.length, start * 2);
560
+ }
561
+ });
562
+ const readerSees = (reader, runId) => runId.startsWith("plan:") ? import_effect.Effect.succeed(false) : runtime.getRun(runId).pipe(
563
+ import_effect.Effect.map((run) => (0, import_ControlRuntime.launchedByMatches)(run, reader)),
564
+ import_effect.Effect.catchTag("/control/RunNotFound", () => import_effect.Effect.succeed(false))
565
+ );
566
+ const confined = (submitted, mutation) => submitted.reader === void 0 ? mutation : import_effect.Effect.flatMap(
567
+ readerSees(submitted.reader, submitted.runId),
568
+ (visible) => visible ? mutation : import_effect.Effect.fail(new import_ControlError.RunNotFound({ runId: submitted.runId }))
569
+ );
570
+ const list = (request) => import_effect.Effect.gen(function* () {
571
+ if (request._tag === "executions") {
572
+ if (request.reader !== void 0 && !(yield* readerSees(request.reader, request.runId))) {
573
+ return yield* new import_ControlError.RunNotFound({ runId: request.runId });
574
+ }
575
+ yield* runtime.getRun(request.runId);
576
+ yield* import_effect.Schema.decodeUnknownEffect(import_ControlSchema.ListRequest)(request).pipe(
577
+ import_effect.Effect.mapError(() => invalid("executionIds: at most 200 nonempty execution IDs are admitted"))
578
+ );
579
+ const batch = import_effect.Option.isSome(executor) && executor.value.readExecutions !== void 0 ? yield* executor.value.readExecutions(request) : {
580
+ source: null,
581
+ revision: null,
582
+ snapshots: request.executionIds.map((executionId) => ({
583
+ _tag: "Unavailable",
584
+ executionId,
585
+ reason: "unsupported"
586
+ }))
587
+ };
588
+ return { _tag: "executions", source: batch.source, revision: batch.revision, items: batch.snapshots };
589
+ }
590
+ const bounds = yield* pageBounds(request._tag === "runs" ? void 0 : request.cursor, request.limit);
591
+ if (request._tag === "plans") {
592
+ if (request.reader !== void 0) return { _tag: "plans", items: [] };
593
+ const result = yield* runtime.queryPlans({
594
+ flowId: request.filters?.flowId,
595
+ decision: request.filters?.decision,
596
+ after: bounds.start,
597
+ limit: bounds.size
598
+ });
599
+ const items2 = result.plans.map((plan) => ({
600
+ card: plan.card,
601
+ // The JSON form the durable runtime stores, whichever runtime answers.
602
+ input: JSON.parse(JSON.stringify(plan.decodedInput ?? null)),
603
+ decision: plan.decision
604
+ }));
605
+ return result.next === void 0 ? { _tag: "plans", items: items2 } : { _tag: "plans", items: items2, nextCursor: String(result.next) };
606
+ }
607
+ if (request._tag === "flows") {
608
+ const [registered, warnings] = yield* import_effect.Effect.all([registry.list(), registry.warnings()]);
609
+ const available = registered.length > 0 ? registered.map((descriptor) => ({
610
+ flowId: descriptor.name,
611
+ description: descriptor.description,
612
+ ...(0, import_Descriptor.inputDocument)(descriptor.input) === void 0 ? {} : { inputSchema: (0, import_Descriptor.inputDocument)(descriptor.input) }
613
+ })) : yield* runtime.listFlows;
614
+ const result = page(available, bounds);
615
+ const diagnostics = warnings.length === 0 ? {} : { warnings };
616
+ return result.nextCursor === void 0 ? { _tag: "flows", items: result.items, ...diagnostics } : { _tag: "flows", items: result.items, ...diagnostics, nextCursor: result.nextCursor };
617
+ }
618
+ if (request._tag === "triggers") {
619
+ if (request.reader !== void 0) return { _tag: "triggers", items: [] };
620
+ let triggers = yield* dispatch.list(request);
621
+ if (request.filters?.triggerId !== void 0) {
622
+ triggers = triggers.filter((trigger) => trigger.triggerId === request.filters?.triggerId);
623
+ }
624
+ if (request.filters?.flowId !== void 0) {
625
+ triggers = triggers.filter((trigger) => trigger.flowId === request.filters?.flowId);
626
+ }
627
+ if (request.filters?.enabled !== void 0) {
628
+ triggers = triggers.filter((trigger) => trigger.enabled === request.filters?.enabled);
629
+ }
630
+ const result = page(triggers, bounds);
631
+ return result.nextCursor === void 0 ? { _tag: "triggers", items: result.items } : { _tag: "triggers", items: result.items, nextCursor: result.nextCursor };
632
+ }
633
+ if (request._tag === "fires") {
634
+ let fires = yield* dispatch.fires(request);
635
+ const reader2 = request.reader;
636
+ if (reader2 !== void 0) {
637
+ const seen = yield* import_effect.Effect.forEach(fires, (fire) => fire.runId === void 0 ? import_effect.Effect.succeed(false) : readerSees(reader2, fire.runId));
638
+ fires = fires.filter(
639
+ (_, index) => seen[index]
640
+ );
641
+ }
642
+ if (request.filters?.triggerId !== void 0) {
643
+ fires = fires.filter((fire) => fire.triggerId === request.filters?.triggerId);
644
+ }
645
+ if (request.filters?.runId !== void 0) {
646
+ fires = fires.filter((fire) => fire.runId === request.filters?.runId);
647
+ }
648
+ if (request.filters?.outcome !== void 0) {
649
+ fires = fires.filter((fire) => fire.outcome === request.filters?.outcome);
650
+ }
651
+ const result = page(fires, bounds);
652
+ return result.nextCursor === void 0 ? { _tag: "fires", items: result.items } : { _tag: "fires", items: result.items, nextCursor: result.nextCursor };
653
+ }
654
+ const filters = request.filters;
655
+ const reader = request.reader;
656
+ if (reader !== void 0 && filters?.principalId !== void 0 && filters.principalId !== reader.id) {
657
+ return { _tag: "runs", items: [] };
658
+ }
659
+ const launcher = reader !== void 0 ? { id: reader.id, kind: reader.kind } : filters?.principalId === void 0 ? void 0 : { id: filters.principalId };
660
+ const fingerprint2 = JSON.stringify([
661
+ filters?.runId ?? null,
662
+ filters?.flowId ?? null,
663
+ filters?.status ?? null,
664
+ filters?.parentRunId ?? null,
665
+ filters?.lineageId ?? null,
666
+ // Keep legacy cursor fingerprints byte-identical unless opting in.
667
+ ...filters?.terminal === void 0 && request.order === void 0 ? [] : [filters?.terminal ?? null, request.order ?? null],
668
+ ...filters?.since === void 0 && filters?.until === void 0 && filters?.triggerId === void 0 ? [] : [filters.since ?? null, filters.until ?? null, filters.triggerId ?? null],
669
+ ...launcher === void 0 ? [] : [launcher.id, launcher.kind ?? null]
670
+ ]);
671
+ const cursor = request.cursor === void 0 ? void 0 : yield* import_effect.Schema.decodeUnknownEffect(runCursor)(
672
+ request.cursor
673
+ ).pipe(import_effect.Effect.mapError(() => invalid("cursor: expected a run listing cursor")));
674
+ if (cursor !== void 0 && cursor.filters !== fingerprint2) {
675
+ return yield* invalid("cursor: belongs to different run filters");
676
+ }
677
+ if (filters?.runId !== void 0) {
678
+ let runs = yield* getRun(filters.runId).pipe(
679
+ import_effect.Effect.map((run) => [run]),
680
+ import_effect.Effect.catchTag("/control/RunNotFound", () => import_effect.Effect.succeed([]))
681
+ );
682
+ if (filters.flowId !== void 0) runs = runs.filter((run) => run.flowId === filters.flowId);
683
+ if (filters.status !== void 0) runs = runs.filter((run) => run.status === filters.status);
684
+ if (filters.terminal !== void 0) runs = runs.filter((run) => terminal(run.status) === filters.terminal);
685
+ if (filters.parentRunId !== void 0) runs = runs.filter((run) => run.parentRunId === filters.parentRunId);
686
+ if (filters.lineageId !== void 0) runs = runs.filter((run) => run.lineageId === filters.lineageId);
687
+ if (launcher !== void 0) runs = runs.filter((run) => (0, import_ControlRuntime.launchedByMatches)(run, launcher));
688
+ if (filters.since !== void 0) runs = runs.filter((run) => run.createdAt >= filters.since);
689
+ if (filters.until !== void 0) runs = runs.filter((run) => run.createdAt < filters.until);
690
+ if (filters.triggerId !== void 0) {
691
+ const started = yield* triggerRuns(filters.triggerId);
692
+ runs = runs.filter((run) => started.includes(run.runId));
693
+ }
694
+ return { _tag: "runs", items: yield* withSteering(yield* import_effect.Effect.forEach(runs, withCodeDrift)) };
695
+ }
696
+ const postFiltered = observing && (filters?.status !== void 0 || filters?.terminal !== void 0);
697
+ const { triggerId, principalId: _principalId, ...unowned } = filters ?? {};
698
+ const selected = launcher === void 0 ? unowned : { ...unowned, launchedBy: launcher };
699
+ const narrowed = triggerId === void 0 ? selected : { ...selected, runIds: yield* triggerRuns(triggerId) };
700
+ const sourceFilters = postFiltered ? Object.fromEntries(Object.entries(narrowed).filter(([key]) => key !== "status" && key !== "terminal")) : narrowed;
701
+ const collected = [];
702
+ let sourceCursor = cursor;
703
+ let sourceNext;
704
+ while (true) {
705
+ const result = yield* runtime.queryRuns({
706
+ filters: sourceFilters,
707
+ order: request.order,
708
+ cursor: sourceCursor,
709
+ limit: bounds.size - collected.length
710
+ });
711
+ const observed = yield* import_effect.Effect.forEach(result.items, observe);
712
+ for (const run of observed) {
713
+ if (!postFiltered || (filters?.status === void 0 || run.status === filters.status) && (filters?.terminal === void 0 || terminal(run.status) === filters.terminal)) collected.push(run);
714
+ }
715
+ sourceNext = result.nextCursor;
716
+ if (!postFiltered || sourceNext === void 0 || collected.length >= bounds.size) break;
717
+ sourceCursor = { version: 1, filters: fingerprint2, ...sourceNext };
718
+ }
719
+ const items = yield* withSteering(collected);
720
+ return sourceNext === void 0 ? { _tag: "runs", items } : {
721
+ _tag: "runs",
722
+ items,
723
+ nextCursor: JSON.stringify({ version: 1, filters: fingerprint2, ...sourceNext })
724
+ };
725
+ });
726
+ const streamForRun = (runId, filter) => journal.stream({
727
+ runId: import_journal.JournalEvent.RunId.make(runId),
728
+ ...filter.afterSequence === void 0 ? {} : { afterSequence: import_journal.JournalEvent.Seq.make(filter.afterSequence) }
729
+ }).pipe(
730
+ import_effect.Stream.map(eventFromEntry),
731
+ import_effect.Stream.mapError(watchReadFailed)
732
+ );
733
+ const snapshotHighWater = (runId) => journal.transact(
734
+ import_effect.Effect.gen(function* () {
735
+ const first = yield* journal.entries({ runId, limit: 1 });
736
+ const initial = first.entries[0];
737
+ if (initial === void 0) return void 0;
738
+ let lower = initial.seq;
739
+ let step = 1;
740
+ let upper = lower;
741
+ const maximumSequence = Number.MAX_SAFE_INTEGER - 1;
742
+ while (lower < maximumSequence) {
743
+ const probe = Math.min(maximumSequence, lower + step - 1);
744
+ const next = yield* journal.entries({
745
+ runId,
746
+ after: import_journal.JournalEvent.Seq.make(probe),
747
+ limit: 1
748
+ });
749
+ const entry = next.entries[0];
750
+ if (entry === void 0) {
751
+ upper = probe;
752
+ break;
753
+ }
754
+ lower = entry.seq;
755
+ if (lower === maximumSequence) return entry.seq;
756
+ step = Math.min(maximumSequence - lower, step * 2);
757
+ }
758
+ while (lower < upper) {
759
+ const middle = lower + Math.ceil((upper - lower) / 2);
760
+ const next = yield* journal.entries({
761
+ runId,
762
+ after: import_journal.JournalEvent.Seq.make(middle - 1),
763
+ limit: 1
764
+ });
765
+ const entry = next.entries[0];
766
+ if (entry === void 0) {
767
+ upper = middle - 1;
768
+ } else {
769
+ lower = entry.seq;
770
+ }
771
+ }
772
+ return import_journal.JournalEvent.Seq.make(lower);
773
+ })
774
+ ).pipe(import_effect.Effect.mapError(watchReadFailed));
775
+ const snapshotForRunAt = (runId, filter, highWater) => {
776
+ const journalRunId = import_journal.JournalEvent.RunId.make(runId);
777
+ const initialAfter = filter.afterSequence === void 0 ? void 0 : import_journal.JournalEvent.Seq.make(filter.afterSequence);
778
+ if (highWater === void 0 || initialAfter !== void 0 && initialAfter >= highWater) {
779
+ return import_effect.Stream.empty;
780
+ }
781
+ return import_effect.Stream.paginate(initialAfter, (after) => journal.entries({
782
+ runId: journalRunId,
783
+ ...after === void 0 ? {} : { after },
784
+ limit: snapshotPageSize
785
+ }).pipe(
786
+ import_effect.Effect.map((page2) => {
787
+ const entries2 = page2.entries.filter((entry) => entry.seq <= highWater);
788
+ const last = entries2.at(-1);
789
+ const next = last === void 0 || last.seq >= highWater || !page2.hasMore ? import_effect.Option.none() : import_effect.Option.some(last.seq);
790
+ return [entries2, next];
791
+ }),
792
+ import_effect.Effect.mapError(watchReadFailed)
793
+ )).pipe(import_effect.Stream.map(eventFromEntry));
794
+ };
795
+ const snapshotForRun = (runId, filter) => import_effect.Stream.unwrap(
796
+ import_effect.Effect.map(
797
+ snapshotHighWater(import_journal.JournalEvent.RunId.make(runId)),
798
+ (highWater) => snapshotForRunAt(runId, filter, highWater)
799
+ )
800
+ );
801
+ const inventory = (page2) => {
802
+ const first = {};
803
+ return import_effect.Stream.paginate(
804
+ first,
805
+ (cursor) => import_effect.Effect.map(
806
+ page2({ ...cursor, limit: partitionPageSize }),
807
+ (next) => [
808
+ next.ids,
809
+ next.next === void 0 ? import_effect.Option.none() : import_effect.Option.some({ after: next.next, through: next.through })
810
+ ]
811
+ )
812
+ );
813
+ };
814
+ const journalPartitions = import_effect.Stream.concat(
815
+ import_effect.Stream.map(inventory(runtime.pagePlanIds), (planId) => `plan:${planId}`),
816
+ inventory(runtime.pageRunIds)
817
+ );
818
+ const snapshot = (filter) => filter.runId !== void 0 ? snapshotForRun(filter.runId, filter) : import_effect.Stream.flatMap(journalPartitions, (partition) => snapshotForRun(partition, filter), {
819
+ concurrency: snapshotPartitionConcurrency
820
+ });
821
+ const entries = (filter) => filter.follow === false ? snapshot(filter) : filter.runId !== void 0 ? streamForRun(filter.runId, filter) : import_effect.Stream.unwrap(
822
+ import_effect.Effect.gen(function* () {
823
+ const subscription = yield* journal.changes;
824
+ const cutoffs = /* @__PURE__ */ new Map();
825
+ const pin = (partition) => {
826
+ const pinned = import_effect.Deferred.makeUnsafe();
827
+ cutoffs.set(partition, pinned);
828
+ return snapshotHighWater(import_journal.JournalEvent.RunId.make(partition)).pipe(
829
+ import_effect.Effect.onExit((exit) => import_effect.Deferred.done(pinned, exit))
830
+ );
831
+ };
832
+ const walk = journalPartitions.pipe(
833
+ import_effect.Stream.mapEffect(
834
+ (partition) => import_effect.Effect.suspend(
835
+ () => cutoffs.has(partition) ? import_effect.Effect.succeed(import_effect.Option.none()) : import_effect.Effect.map(pin(partition), (highWater) => import_effect.Option.some([partition, highWater]))
836
+ ),
837
+ { concurrency: snapshotPartitionConcurrency }
838
+ ),
839
+ import_effect.Stream.filter(import_effect.Option.isSome),
840
+ import_effect.Stream.map((pinned) => pinned.value),
841
+ import_effect.Stream.flatMap(([partition, highWater]) => snapshotForRunAt(partition, filter, highWater), {
842
+ concurrency: snapshotPartitionConcurrency
843
+ })
844
+ );
845
+ const cutoffFor = (partition) => import_effect.Effect.suspend(() => {
846
+ const known = cutoffs.get(partition);
847
+ if (known !== void 0) {
848
+ return import_effect.Effect.map(import_effect.Deferred.await(known), (highWater) => ({ highWater, claimed: false }));
849
+ }
850
+ return import_effect.Effect.map(pin(partition), (highWater) => ({ highWater, claimed: true }));
851
+ });
852
+ const seenByPartition = /* @__PURE__ */ new Map();
853
+ const gapCheck = (partition, expected, arrived) => journal.entries({
854
+ runId: import_journal.JournalEvent.RunId.make(partition),
855
+ ...expected === void 0 ? {} : { after: import_journal.JournalEvent.Seq.make(expected) },
856
+ limit: 1
857
+ }).pipe(
858
+ import_effect.Effect.mapError(watchReadFailed),
859
+ import_effect.Effect.flatMap((page2) => {
860
+ const missed = page2.entries[0];
861
+ if (missed === void 0 || missed.seq >= arrived) return import_effect.Effect.void;
862
+ return import_effect.Effect.fail(
863
+ new import_ControlError.PersistenceError({
864
+ operation: "watch",
865
+ message: `the live tail lost journal entries for ${partition}: sequence ${missed.seq} was committed but never delivered to this watcher`
866
+ })
867
+ );
868
+ })
869
+ );
870
+ const trackTail = (entry, cutoff) => {
871
+ const partition = String(entry.runId);
872
+ const expected = seenByPartition.get(partition) ?? cutoff;
873
+ if (expected !== void 0 && entry.seq <= expected) return import_effect.Effect.succeed(import_effect.Option.none());
874
+ seenByPartition.set(partition, entry.seq);
875
+ return expected !== void 0 && entry.seq === expected + 1 ? import_effect.Effect.succeed(import_effect.Option.some(entry)) : import_effect.Effect.map(gapCheck(partition, expected, entry.seq), () => import_effect.Option.some(entry));
876
+ };
877
+ const tail = import_effect.Stream.fromSubscription(subscription).pipe(
878
+ // Every committed entry names its partition. The first one from a
879
+ // partition nobody pinned yet pins it here and is preceded by that
880
+ // partition's snapshot, which also recovers any earlier notice the
881
+ // sliding buffer dropped.
882
+ import_effect.Stream.flatMap(
883
+ (entry) => import_effect.Stream.unwrap(import_effect.Effect.gen(function* () {
884
+ const partition = String(entry.runId);
885
+ const { highWater, claimed } = yield* cutoffFor(partition);
886
+ const history = claimed ? snapshotForRunAt(partition, filter, highWater) : import_effect.Stream.empty;
887
+ if (highWater !== void 0 && entry.seq <= highWater) return history;
888
+ const tracked = yield* trackTail(entry, highWater);
889
+ return import_effect.Option.isSome(tracked) ? import_effect.Stream.concat(history, import_effect.Stream.succeed(eventFromEntry(tracked.value))) : history;
890
+ }))
891
+ )
892
+ );
893
+ return import_effect.Stream.merge(walk, tail);
894
+ })
895
+ );
896
+ const watch = (filter) => {
897
+ const { reader, ...unrestricted } = filter;
898
+ if (reader === void 0) return watchAll(unrestricted);
899
+ if (filter.runId !== void 0) {
900
+ const runId = filter.runId;
901
+ return import_effect.Stream.unwrap(import_effect.Effect.map(
902
+ readerSees(reader, runId),
903
+ (visible) => visible ? watchAll(unrestricted) : import_effect.Stream.fail(new import_ControlError.RunNotFound({ runId }))
904
+ ));
905
+ }
906
+ const decided = /* @__PURE__ */ new Map();
907
+ return watchAll(unrestricted).pipe(
908
+ // A lost-tail failure names the partition and sequence it missed,
909
+ // which may be another principal's; a restricted reader learns only
910
+ // that its stream ended incomplete.
911
+ import_effect.Stream.mapError(
912
+ (error) => error._tag === "/control/PersistenceError" ? new import_ControlError.PersistenceError({ operation: "watch", message: "the watch lost journal entries and ended" }) : error
913
+ ),
914
+ import_effect.Stream.filterEffect((event) => {
915
+ const partition = event.runId;
916
+ const known = decided.get(partition);
917
+ if (known !== void 0) return import_effect.Effect.succeed(known);
918
+ return import_effect.Effect.tap(
919
+ readerSees(reader, partition),
920
+ (visible) => import_effect.Effect.sync(() => decided.set(partition, visible))
921
+ );
922
+ })
923
+ );
924
+ };
925
+ const watchAll = (filter) => {
926
+ if (filter.afterSequence !== void 0 && filter.runId === void 0) {
927
+ return import_effect.Stream.fail(invalid("afterSequence: a watch cursor resumes one run, so it requires runId"));
928
+ }
929
+ const cursor = filter.afterCursor;
930
+ if (cursor !== void 0) {
931
+ if (filter.runId === void 0) {
932
+ return import_effect.Stream.fail(invalid("afterCursor: a watch cursor resumes one run, so it requires runId"));
933
+ }
934
+ if (filter.afterSequence !== void 0) {
935
+ return import_effect.Stream.fail(invalid("afterCursor: cannot be combined with afterSequence"));
936
+ }
937
+ if (!import_effect.Schema.is(import_ControlSchema.WatchCursor)(cursor)) {
938
+ return import_effect.Stream.fail(invalid("afterCursor: sequence and offset must be nonnegative safe journal integers"));
939
+ }
940
+ }
941
+ const afterSequence = cursor === void 0 ? filter.afterSequence : cursor.offset === void 0 ? cursor.sequence : cursor.sequence === 0 ? void 0 : cursor.sequence - 1;
942
+ return entries({
943
+ ...filter,
944
+ afterSequence
945
+ }).pipe(
946
+ import_effect.Stream.map((event) => {
947
+ const lineage = Lineage.derive(event);
948
+ const expanded = [event, ...lineage === void 0 ? [] : [lineage], ...Steering.derive(event)];
949
+ const checkpointed = expanded.map((member, offset) => ({
950
+ ...member,
951
+ cursor: offset === expanded.length - 1 ? { sequence: event.sequence } : { sequence: event.sequence, offset }
952
+ }));
953
+ return cursor?.offset !== void 0 && event.sequence === cursor.sequence ? checkpointed.slice(cursor.offset + 1) : checkpointed;
954
+ }),
955
+ import_effect.Stream.flattenIterable
956
+ );
957
+ };
958
+ const service = {
959
+ plan: import_effect.Effect.fn("Control.plan")(
960
+ (input) => mutationSemaphore.withPermits(1)(
961
+ journal.transact(import_effect.Effect.gen(function* () {
962
+ const { card, created } = yield* runtime.plan(input);
963
+ const runId = import_journal.JournalEvent.RunId.make(`plan:${card.planId}`);
964
+ if (!created) {
965
+ let after;
966
+ while (true) {
967
+ const page2 = yield* journal.entries({
968
+ runId,
969
+ ...after === void 0 ? {} : { after },
970
+ limit: snapshotPageSize
971
+ });
972
+ if (page2.entries.some(
973
+ (entry) => entry.sourceId === sourceId && entry.eventType === "control.plan.created"
974
+ )) {
975
+ return card;
976
+ }
977
+ if (!page2.hasMore) break;
978
+ after = page2.entries[page2.entries.length - 1].seq;
979
+ }
980
+ }
981
+ yield* emit(runId, "control.plan.created", {
982
+ planId: card.planId,
983
+ flowId: card.flowId,
984
+ digest: card.digest
985
+ });
986
+ return card;
987
+ })).pipe(
988
+ import_effect.Effect.mapError(
989
+ (cause) => cause instanceof import_journal.Journal.JournalError ? new import_ControlError.PersistenceError({
990
+ operation: "plan",
991
+ message: "Failed to commit plan and its creation entry atomically",
992
+ cause
993
+ }) : cause
994
+ )
995
+ )
996
+ ).pipe(import_effect.Effect.flatMap(
997
+ (card) => registry.warnings().pipe(import_effect.Effect.map((all) => {
998
+ const warnings = all.filter((warning) => warning.name === input.flowId);
999
+ return warnings.length === 0 ? card : { warnings, ...card };
1000
+ }))
1001
+ ))
1002
+ ),
1003
+ run: import_effect.Effect.fn("Control.run")(
1004
+ (submitted) => import_effect.Effect.gen(function* () {
1005
+ const input = yield* snapshotRun(submitted);
1006
+ if (input._tag === "Resume") return yield* runMutation(input);
1007
+ const principal = yield* runtime.stampPrincipal(input.principal);
1008
+ let admitted;
1009
+ const receipt = yield* mutate(
1010
+ "run",
1011
+ input.idempotencyKey,
1012
+ principal,
1013
+ fingerprint("run", principal, input),
1014
+ import_effect.Effect.gen(function* () {
1015
+ const launched = yield* runtime.launch(input.planId, input.digest, input.envelope, principal, input.reservedRunId);
1016
+ if (launched._tag === "Parked") {
1017
+ return { ...launched.receipt, receiptId: input.idempotencyKey };
1018
+ }
1019
+ const plan = yield* runtime.getPlan(input.planId);
1020
+ const trigger = declaredTrigger(plan.decodedInput);
1021
+ yield* emit(
1022
+ launched.run.runId,
1023
+ "control.run.accepted",
1024
+ {
1025
+ runId: launched.run.runId,
1026
+ planId: input.planId,
1027
+ digest: input.digest,
1028
+ status: launched.run.status,
1029
+ ...trigger === void 0 ? {} : { trigger },
1030
+ ...ControlFacts.runFact(launched.run, "created")
1031
+ }
1032
+ );
1033
+ admitted = { plan, run: launched.run };
1034
+ return {
1035
+ _tag: "Accepted",
1036
+ receiptId: input.idempotencyKey,
1037
+ runId: launched.run.runId
1038
+ };
1039
+ }),
1040
+ true,
1041
+ true
1042
+ );
1043
+ if (receipt._tag === "Accepted" && admitted !== void 0) yield* hand(admitted);
1044
+ else if (receipt._tag === "AlreadyApplied" && receipt.runId !== void 0) {
1045
+ yield* relaunchStranded(receipt.runId);
1046
+ }
1047
+ return receipt;
1048
+ })
1049
+ ),
1050
+ approve: import_effect.Effect.fn("Control.approve")((input) => decide("approved", input)),
1051
+ deny: import_effect.Effect.fn("Control.deny")((input) => decide("denied", input)),
1052
+ steer: import_effect.Effect.fn("Control.steer")(
1053
+ (submitted) => confined(
1054
+ submitted,
1055
+ import_effect.Effect.flatMap(snapshotSteer(submitted), (input) => mutate(
1056
+ "steer",
1057
+ input.idempotencyKey,
1058
+ input.message.principal,
1059
+ fingerprint("steer", input.message.principal, input),
1060
+ import_effect.Effect.gen(function* () {
1061
+ if (input.message.runId !== input.runId) {
1062
+ return yield* import_effect.Effect.fail(
1063
+ invalid(
1064
+ `message.runId: must be ${JSON.stringify(input.runId)}, received ${JSON.stringify(input.message.runId)}`
1065
+ )
1066
+ );
1067
+ }
1068
+ const run = yield* getRun(input.runId);
1069
+ if (terminal(run.status)) return { _tag: "Terminal", runId: run.runId, status: run.status };
1070
+ const item = (0, import_ControlSchema.steerItem)(input.message);
1071
+ const admission = yield* notifications.admit(input.runId, {
1072
+ _tag: "human-steer",
1073
+ id: input.message.messageId,
1074
+ delivery: "steer",
1075
+ targetLineageId: input.runId,
1076
+ provenance: {
1077
+ sourceRunId: input.runId,
1078
+ sourceLineageId: input.runId,
1079
+ sourceTurn: 0,
1080
+ sourceActor: `${input.message.principal.kind}:${input.message.principal.id}`,
1081
+ ...input.message.attribution === void 0 ? {} : { attribution: input.message.attribution }
1082
+ },
1083
+ payload: SteerPayload.encode(item)
1084
+ }, input.version).pipe(
1085
+ import_effect.Effect.mapError(
1086
+ (cause) => cause instanceof import_notifications.NotificationQueue.NotificationError ? cause : new import_ControlError.PersistenceError({
1087
+ operation: "control.steer.notification",
1088
+ message: "Failed to admit steering notification",
1089
+ cause
1090
+ })
1091
+ )
1092
+ );
1093
+ const inputReceipt = () => {
1094
+ if (input.version === void 0 || item.kind !== "Message") {
1095
+ return accepted(input.idempotencyKey, input.runId);
1096
+ }
1097
+ const payload = admission.consumedNotification?.payload;
1098
+ return {
1099
+ _tag: "Accepted",
1100
+ receiptId: input.idempotencyKey,
1101
+ runId: input.runId,
1102
+ inputConsumed: admission.consumed === true,
1103
+ inputBody: payload?.body ?? item.body
1104
+ };
1105
+ };
1106
+ if (input.version !== void 0 && (admission.consumed || admission.duplicate)) {
1107
+ return inputReceipt();
1108
+ }
1109
+ if (admission.decision === "rejected-full") {
1110
+ return yield* import_effect.Effect.fail(
1111
+ new import_notifications.NotificationQueue.NotificationError({
1112
+ code: "notification_full",
1113
+ notificationId: input.message.messageId,
1114
+ message: "Steering queue is full; retry after pending notifications are delivered"
1115
+ })
1116
+ );
1117
+ }
1118
+ yield* emit(input.runId, Steering.enqueuedEventType, {
1119
+ runId: input.runId,
1120
+ messageId: input.message.messageId,
1121
+ kind: item.kind,
1122
+ createdAt: input.message.createdAt
1123
+ });
1124
+ yield* wake(run, input.message.messageId);
1125
+ return inputReceipt();
1126
+ })
1127
+ ))
1128
+ )
1129
+ ),
1130
+ signal: import_effect.Effect.fn("Control.signal")(
1131
+ (submitted) => confined(
1132
+ submitted,
1133
+ import_effect.Effect.flatMap(snapshotSignal(submitted), (input) => import_effect.Effect.gen(function* () {
1134
+ const principal = yield* runtime.stampPrincipal(input.principal);
1135
+ const key = fingerprint("signal", principal, input);
1136
+ const durableKey = (0, import_planning.mutationKey)("signal", input.idempotencyKey, principal);
1137
+ const receipt = yield* mutate(
1138
+ "signal",
1139
+ input.idempotencyKey,
1140
+ principal,
1141
+ key,
1142
+ import_effect.Effect.gen(function* () {
1143
+ const current = yield* getRun(input.runId);
1144
+ if (terminal(current.status)) {
1145
+ return { _tag: "Terminal", runId: current.runId, status: current.status };
1146
+ }
1147
+ yield* runtime.admitSignal(durableKey, input.runId, input.signal, principal);
1148
+ yield* emit(input.runId, "control.signal.admitted", {
1149
+ commandId: durableKey,
1150
+ runId: input.runId,
1151
+ name: input.signal.name
1152
+ });
1153
+ return accepted(input.idempotencyKey, input.runId);
1154
+ }),
1155
+ true,
1156
+ true
1157
+ );
1158
+ if (receipt._tag !== "Accepted" && receipt._tag !== "AlreadyApplied") return receipt;
1159
+ const command = yield* runtime.signalCommand(durableKey);
1160
+ if (command === void 0 || command.state === "delivered" || command.state === "terminal") return receipt;
1161
+ if (command.state === "rejected") {
1162
+ return yield* new import_ControlError.NoMatchingWait({ runId: input.runId, waitName: input.signal.name });
1163
+ }
1164
+ const delivery = import_effect.Option.isNone(executor) ? "unknown" : yield* executor.value.deliverSignal({ ...command });
1165
+ if (delivery === "no-match") {
1166
+ yield* runtime.settleSignal(durableKey, "rejected");
1167
+ return yield* new import_ControlError.NoMatchingWait({ runId: input.runId, waitName: input.signal.name });
1168
+ }
1169
+ if (delivery === "refused") {
1170
+ yield* runtime.settleSignal(durableKey, "rejected");
1171
+ return yield* new import_ControlError.Unauthorized({
1172
+ message: `This caller has no authority to answer "${input.signal.name}" on run ${input.runId}`
1173
+ });
1174
+ }
1175
+ if (delivery === "delivered") {
1176
+ yield* runtime.settleSignal(durableKey, "delivered");
1177
+ }
1178
+ return receipt;
1179
+ }))
1180
+ )
1181
+ ),
1182
+ cancel: import_effect.Effect.fn("Control.cancel")(
1183
+ (submitted) => confined(
1184
+ submitted,
1185
+ import_effect.Effect.flatMap(
1186
+ snapshotReasonedMutation("cancel", submitted),
1187
+ (input) => import_effect.Effect.flatMap(runtime.stampPrincipal(input.principal), (principal) => mutate(
1188
+ "cancel",
1189
+ input.idempotencyKey,
1190
+ principal,
1191
+ fingerprint("cancel", principal, input),
1192
+ import_effect.Effect.gen(function* () {
1193
+ const current = yield* getRun(input.runId);
1194
+ if (terminal(current.status)) {
1195
+ yield* reconcileTerminal(input.runId, current.status);
1196
+ return { _tag: "Terminal", runId: current.runId, status: current.status };
1197
+ }
1198
+ const record2 = yield* executorRequestCancel(input.runId);
1199
+ if (typeof record2 !== "string") {
1200
+ yield* reconcileTerminal(input.runId, record2.status);
1201
+ return { _tag: "Terminal", runId: input.runId, status: record2.status };
1202
+ }
1203
+ const prior = yield* runtime.lookupMutation(
1204
+ (0, import_planning.mutationKey)("cancel", input.idempotencyKey, principal),
1205
+ fingerprint("cancel", principal, input)
1206
+ );
1207
+ if (record2 !== "already-requested" && prior === void 0) {
1208
+ yield* emit(
1209
+ input.runId,
1210
+ Cancellation.requestedEventType,
1211
+ json({
1212
+ runId: input.runId,
1213
+ source: "control",
1214
+ principal,
1215
+ ...input.reason === void 0 ? {} : { reason: input.reason }
1216
+ })
1217
+ );
1218
+ }
1219
+ return accepted(input.idempotencyKey, input.runId);
1220
+ }),
1221
+ false
1222
+ ).pipe(
1223
+ import_effect.Effect.flatMap(
1224
+ (receipt) => import_effect.Effect.gen(function* () {
1225
+ if (receipt._tag !== "Accepted") return receipt;
1226
+ const settle = (effect) => transact(
1227
+ "cancel",
1228
+ import_effect.Effect.gen(function* () {
1229
+ const run2 = yield* effect;
1230
+ yield* emit(
1231
+ input.runId,
1232
+ `control.run.${run2.status}`,
1233
+ {
1234
+ runId: input.runId,
1235
+ status: run2.status,
1236
+ ...ControlFacts.runFact(run2)
1237
+ }
1238
+ );
1239
+ return run2;
1240
+ })
1241
+ );
1242
+ const run = yield* runtime.interrupt(input.runId, settle).pipe(
1243
+ import_effect.Effect.catchTag("/control/ClaimLost", () => import_effect.Effect.gen(function* () {
1244
+ const current = yield* getRun(input.runId);
1245
+ if (terminal(current.status)) return current;
1246
+ if (live(current.status) && current.ownerId !== void 0) return void 0;
1247
+ return yield* runtime.resume(input.runId).pipe(
1248
+ import_effect.Effect.andThen(runtime.interrupt(input.runId, settle)),
1249
+ import_effect.Effect.catchTag("/control/ClaimLost", () => import_effect.Effect.succeed(void 0))
1250
+ );
1251
+ }))
1252
+ );
1253
+ return run === void 0 ? receipt : terminalOrAccepted(input.idempotencyKey, run);
1254
+ })
1255
+ ),
1256
+ // Both rows, before the process that asked goes away. The engine row
1257
+ // carries the request the moment the mutation commits, but nothing
1258
+ // drives a parked run, so the row stayed `suspended` until some
1259
+ // later long-lived engine happened to sweep it: `gc` collected the
1260
+ // run in `control.db` and skipped it in `engine.db` for fifteen
1261
+ // seconds and six commands in the release validation.
1262
+ import_effect.Effect.tap(
1263
+ () => import_effect.Effect.gen(function* () {
1264
+ yield* executorSettleCancelledPark(input.runId);
1265
+ const current = yield* getRun(input.runId);
1266
+ if (terminal(current.status)) yield* reconcileTerminal(input.runId, current.status);
1267
+ })
1268
+ )
1269
+ ))
1270
+ )
1271
+ )
1272
+ ),
1273
+ resume: import_effect.Effect.fn("Control.resume")((input) => confined(input, runMutation(input))),
1274
+ list,
1275
+ watch
1276
+ };
1277
+ return import_Control.Control.of(service);
1278
+ })
1279
+ );
1280
+ //# sourceMappingURL=ControlLive.js.map