@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,372 @@
1
+ /**
2
+ * Acceptance port from the control plane into a real run executor.
3
+ *
4
+ * Governing contract: `docs/pages/control/index.md`.
5
+ *
6
+ * @since 0.1.0
7
+ */
8
+ import type { ExecutionFact } from "@smthrs/journal";
9
+ import { Context, Effect, Layer } from "effect";
10
+ import type { LaunchFailed, PersistenceError } from "./ControlError.ts";
11
+ import type { StoredPlan } from "./ControlRuntime.ts";
12
+ import type { ApprovalTarget, ExecutionBatch, PendingWait, Principal, Receipt, RunId, RunSummary, SignalPayload } from "./ControlSchema.ts";
13
+ /**
14
+ * One stored plan and the run summary it is being started as.
15
+ *
16
+ * @category models
17
+ * @since 0.1.0
18
+ */
19
+ export interface Launch {
20
+ readonly plan: StoredPlan;
21
+ readonly run: RunSummary;
22
+ }
23
+ /**
24
+ * Whether the executor took the launch now or queued it.
25
+ *
26
+ * @category models
27
+ * @since 0.1.0
28
+ */
29
+ export type Acceptance = "accepted" | "pending";
30
+ /**
31
+ * One run whose cancellation has to become durable on the engine row.
32
+ *
33
+ * @category models
34
+ * @since 0.1.0
35
+ */
36
+ export interface CancelRequest {
37
+ readonly runId: RunId;
38
+ }
39
+ /**
40
+ * The engine row a cancel request arrived too late for.
41
+ *
42
+ * It carries the ENGINE's status rather than a bare marker because the control
43
+ * plane cannot read that row: in the shipped CLI the two `flows_runs` tables
44
+ * live in two files (`.flows/control.db` and `.flows/engine.db`), so the port
45
+ * is the only place the plane learns what actually became of the run.
46
+ *
47
+ * @category models
48
+ * @since 0.1.0
49
+ */
50
+ export interface CancelTerminal {
51
+ readonly _tag: "Terminal";
52
+ readonly status: "completed" | "failed" | "cancelled";
53
+ }
54
+ /**
55
+ * What the executor did with a cancel request.
56
+ *
57
+ * `recorded` means THIS call set `cancel_requested_at_ms` on the engine row, so
58
+ * the owning process stops the run at its next cancel poll whichever process
59
+ * asked. `already-requested` means the column was already set when this call
60
+ * arrived: the request is just as durable, and the cancellation it belongs to
61
+ * was somebody else's. `Control.cancel` keys its attribution event on that
62
+ * difference — the write is first-writer-wins and every repeat re-runs the
63
+ * whole mutation, so answering `recorded` to all of them left one journaled
64
+ * `control.run.cancel-requested` per ask for a single cancellation (release validation
65
+ * smoke: three `cancel` calls and one `down` against one parked run left four).
66
+ * `unknown` means this executor's engine has no row for the run at all, which
67
+ * is the honest answer for a run another composition launched into a database
68
+ * this one does not share. A {@link CancelTerminal} means the engine row has
69
+ * already settled, so there is nothing left to stop: recording intent on it
70
+ * would be a request no process can ever act on, and answering `recorded` let
71
+ * `Control.cancel` write a terminal control status the engine row does not have
72
+ * (triage B-11).
73
+ *
74
+ * @category models
75
+ * @since 0.1.0
76
+ */
77
+ export type CancelRecord = "recorded" | "already-requested" | "unknown" | CancelTerminal;
78
+ /**
79
+ * One parked run that has been told to resume.
80
+ *
81
+ * @category models
82
+ * @since 0.1.0
83
+ */
84
+ export interface ResumeRequest {
85
+ readonly runId: RunId;
86
+ }
87
+ /**
88
+ * What the executor did with a resume request.
89
+ *
90
+ * `resuming` means this executor hosts the run's execution, has taken the
91
+ * control row's fence, and is re-driving it: the caller can stop, and the run
92
+ * moves on its own. `unknown` means this executor drives no execution for the
93
+ * run, which is the answer an operator's CLI, a gateway, or any second process
94
+ * gives, and it is why the control plane records the delegation durably
95
+ * instead of treating its own journal entry as the delivery (triage B-15).
96
+ *
97
+ * @category models
98
+ * @since 0.1.0
99
+ */
100
+ export type ResumeUptake = "resuming" | "unknown";
101
+ /**
102
+ * One signal to deliver to a run's open wait point.
103
+ *
104
+ * @category models
105
+ * @since 0.1.0
106
+ */
107
+ export interface Signal {
108
+ /** Actor-scoped durable admission identity, present on control-plane delivery. */
109
+ readonly commandId?: string;
110
+ /** An existing immutable binding survives a crash before acknowledgment. */
111
+ readonly token?: string | null;
112
+ readonly runId: RunId;
113
+ readonly signal: SignalPayload;
114
+ /**
115
+ * Who admitted the signal. A human wait ({@link humanWaitReason}) completes
116
+ * only when `ApprovalAuthority` authorizes this principal to approve the
117
+ * wait's `Node` target; without one the signal is `refused`.
118
+ */
119
+ readonly principal?: Principal | undefined;
120
+ }
121
+ /**
122
+ * What the executor did with a signal.
123
+ *
124
+ * `delivered` means the `WaitFor` deferred the run is parked on was completed
125
+ * with the signal's payload and the run was woken. `no-match` means the run IS
126
+ * parked and is waiting for something else, which `Control.signal` refuses
127
+ * rather than recording a delivery nothing consumes. `unknown` means this
128
+ * executor is driving no execution for the run at all — another process may
129
+ * be, or none is yet — so the recorded message is the whole delivery and the
130
+ * executor that eventually drives the run replays it at its next start.
131
+ * `refused` means the signal named an open human wait and `ApprovalAuthority`
132
+ * did not authorize its principal to answer it: the wait stays open, and
133
+ * `Control.signal` fails `Unauthorized`.
134
+ *
135
+ * @category models
136
+ * @since 0.1.0
137
+ */
138
+ export type SignalDelivery = "delivered" | "no-match" | "refused" | "unknown";
139
+ /**
140
+ * Current engine observation. Missing execution is distinct from a running one.
141
+ * @category models
142
+ * @since 1.0.0
143
+ */
144
+ export type ExecutionObservation = {
145
+ readonly _tag: "Missing";
146
+ } | {
147
+ readonly _tag: "Observed";
148
+ readonly executionView?: ExecutionFact.View | undefined;
149
+ readonly status: "accepted" | "running" | "parked" | "waiting-approval" | "completed" | "failed" | "cancelled";
150
+ readonly waitingReason?: string | undefined;
151
+ readonly parentRunId?: string | undefined;
152
+ readonly lineageId?: string | undefined;
153
+ readonly roundOrdinal?: number | undefined;
154
+ /**
155
+ * Open human waits anywhere in this execution's tree.
156
+ *
157
+ * The control plane cannot compute these. It keeps its own coordination
158
+ * copy of `flows_runs`, and the executions a flow spawns — where a nested
159
+ * `HumanTask` actually parks — exist only in the executor's database, with
160
+ * the edges that link them. `readExecution` is the one place that reads
161
+ * both, so it is where a run tree's open questions become visible to
162
+ * anything else.
163
+ *
164
+ * Absent when nothing in the tree is waiting on a person. `status` rolls
165
+ * up with them: an execution parked while a descendant holds a human wait
166
+ * is observed as `waiting-approval`.
167
+ */
168
+ readonly pendingWaits?: ReadonlyArray<PendingWait> | undefined;
169
+ };
170
+ /**
171
+ * The waiting reason a human wait parks under.
172
+ *
173
+ * `HumanTask` and the agent's own `ask` both declare it
174
+ * (`@smthrs/flow` `FlowRuntime.annotateWaiting({ reason: "approval" })`), so
175
+ * it is the one value that separates "somebody has to answer this" from every
176
+ * other park.
177
+ *
178
+ * @category models
179
+ * @since 1.0.0
180
+ */
181
+ export declare const humanWaitReason = "approval";
182
+ /**
183
+ * One parked execution as a {@link PendingWait}, or nothing when it is not a
184
+ * wait a person or a named control signal can end.
185
+ *
186
+ * Shared by the two readers that produce these rows — the control plane's own
187
+ * SQL runtime, when it shares a database with the engine, and the executor's
188
+ * observation port, when it does not — so a wait reads the same either way.
189
+ *
190
+ * @category constructors
191
+ * @since 1.0.0
192
+ */
193
+ export declare const pendingWaitOf: (row: {
194
+ readonly runId: string;
195
+ readonly flowId?: string | undefined;
196
+ readonly reason: string;
197
+ readonly token: string | null | undefined;
198
+ readonly request?: unknown;
199
+ readonly createdAt: number;
200
+ }) => PendingWait | undefined;
201
+ /**
202
+ * The wait a `Node` approval target addresses, when it addresses one.
203
+ *
204
+ * Most gates are a capability the run wants allowed: the target's `digest` is
205
+ * the request's own digest, a decision grants or refuses it, and the control
206
+ * plane holds a registered token for it. A `HumanTask` gate has no such token
207
+ * — nothing registers one, because the run parked itself on a durable wait
208
+ * rather than asking the control plane for permission — and `lookupApproval`
209
+ * answers a target with no token by reporting the RUN as missing. That is what
210
+ * an operator saw when the app submitted an answer as an ordinary approval:
211
+ * `/control/RunNotFound` naming a run that was listed, rendered, and waiting
212
+ * (workspace 4bb93306, run-1).
213
+ *
214
+ * So a decision has to be able to tell the two apart from the payload alone.
215
+ * The approvals projection publishes a human wait with the durable wait token
216
+ * as the `digest` and the wait point's own name as the `requestId`, and a
217
+ * durable deferred token is self-describing: it decodes to a flow, an
218
+ * execution, and a deferred name under `WaitFor/`. Nothing else produces one,
219
+ * so a target carrying one is a question, and the name it answers is right
220
+ * there.
221
+ *
222
+ * @category constructors
223
+ * @since 1.0.0
224
+ */
225
+ export declare const answerableWait: (target: ApprovalTarget) => {
226
+ readonly name: string;
227
+ readonly token: string;
228
+ } | undefined;
229
+ /**
230
+ * The executor port: the control plane hands work over to a real run executor
231
+ * and learns only what the executor did with it.
232
+ *
233
+ * `launch` is acceptance. `requestCancel`, `deliverSignal`, and `resumeRun`
234
+ * are the three requests that have to reach the engine to mean anything: a
235
+ * cancel is durable on the engine row so that whichever process owns the run
236
+ * stops it, a signal completes the wait point a parked run is actually waiting
237
+ * on, and a resume re-drives the execution an approval decision unblocked.
238
+ * Without them the control plane records facts nobody reads — a cancel that
239
+ * answers `ClaimLost` to every process but the owner, a signal a parked run
240
+ * never sees, and a resume event published into an in-process hub no other
241
+ * process subscribes to (the release policy; triage B-10, B-13, B-15).
242
+ *
243
+ * @category services
244
+ * @since 0.1.0
245
+ */
246
+ export interface Service {
247
+ /** Read from the executor's database, never the control coordination copy. */
248
+ readonly readExecution?: (runId: RunId) => Effect.Effect<ExecutionObservation, PersistenceError>;
249
+ /** Exact observations scoped to the authorized control root; unrelated native rows are never exposed. */
250
+ readonly readExecutions?: (input: {
251
+ readonly runId: RunId;
252
+ readonly executionIds: ReadonlyArray<string>;
253
+ }) => Effect.Effect<ExecutionBatch, PersistenceError>;
254
+ readonly launch: (input: Launch) => Effect.Effect<Acceptance, LaunchFailed>;
255
+ /**
256
+ * Host-only close of a retained run, after its last native module completed.
257
+ * @since 1.0.0
258
+ */
259
+ readonly requestComplete?: ((input: {
260
+ readonly runId: RunId;
261
+ readonly receiptId: string;
262
+ }) => Effect.Effect<Receipt, PersistenceError>) | undefined;
263
+ /**
264
+ * Records a cancellation on the engine row, durably, regardless of which
265
+ * process owns the run.
266
+ */
267
+ readonly requestCancel: (input: CancelRequest) => Effect.Effect<CancelRecord, PersistenceError>;
268
+ /**
269
+ * Completes the run's open `WaitFor` wait point with the signal's payload.
270
+ */
271
+ readonly deliverSignal: (input: Signal) => Effect.Effect<SignalDelivery, PersistenceError>;
272
+ /**
273
+ * Takes up a resume: claims the control row and re-drives the execution,
274
+ * when this executor is the one hosting it.
275
+ */
276
+ readonly resumeRun: (input: ResumeRequest) => Effect.Effect<ResumeUptake, PersistenceError>;
277
+ /**
278
+ * Finishes a PARKED execution whose cancellation is already durable.
279
+ *
280
+ * A park has no owner — that is what makes it resumable — so nothing is
281
+ * driving the run and nothing reads the request {@link requestCancel} wrote.
282
+ * The engine's parked-run sweep does, once per heartbeat, but a `smithers
283
+ * cancel` process writes the request at the very end of its life and exits
284
+ * before that tick: the release validation watched an engine row stay
285
+ * `suspended` with `cancel_requested_at_ms` set through six more commands
286
+ * and fifteen seconds, so `gc` collected the run in `control.db` and
287
+ * skipped it in `engine.db`.
288
+ *
289
+ * Called AFTER the cancel mutation commits, never inside it: driving a run
290
+ * re-enters the engine, whose writes would wait on the writer the
291
+ * mutation's transaction holds. An executor that does not host the run, or
292
+ * one whose run is not parked, does nothing and leaves the durable request
293
+ * standing for the host that does.
294
+ */
295
+ readonly settleCancelledPark: (input: CancelRequest) => Effect.Effect<void, PersistenceError>;
296
+ }
297
+ declare const ControlExecutor_base: Context.ServiceClass<ControlExecutor, "/control/ControlExecutor", Service>;
298
+ /**
299
+ * The {@link Service} tag.
300
+ *
301
+ * @category services
302
+ * @since 0.1.0
303
+ */
304
+ export declare class ControlExecutor extends ControlExecutor_base {
305
+ }
306
+ /**
307
+ * Builds a {@link Service} from an implementation of its methods.
308
+ *
309
+ * @category constructors
310
+ * @since 0.1.0
311
+ */
312
+ export declare const make: (implementation: Service) => Service;
313
+ /**
314
+ * A {@link Service} that accepts every launch as `pending` and starts
315
+ * nothing. Overrides replace individual methods.
316
+ *
317
+ * @category constructors
318
+ * @since 0.1.0
319
+ */
320
+ export declare const makeNoop: (overrides?: Partial<Service>) => Service;
321
+ /**
322
+ * Provides {@link ControlExecutor} from an implementation.
323
+ *
324
+ * @category layers
325
+ * @since 0.1.0
326
+ */
327
+ export declare const layer: (implementation: Service) => Layer.Layer<ControlExecutor>;
328
+ /**
329
+ * The same executor with methods that drive a run refused.
330
+ *
331
+ * A host composed to observe runs rather than drive them has no completion
332
+ * judge, so it must not be able to start or resume one: without this a verb
333
+ * misclassified as a read would admit a run and lose it at its first
334
+ * completion, which is the outcome the boot-time judge requirement exists to
335
+ * prevent. Reaching either method is a composition defect, not an operator
336
+ * error, so it dies rather than failing.
337
+ *
338
+ * Observation, cancellation and signal delivery stay live. A cancel and a
339
+ * signal record a durable request that the process driving the run picks up,
340
+ * so none of them reaches a completion, and refusing them would stop an
341
+ * operator ending a run on a host with no gateway key, which is the whole
342
+ * reason a read host exists.
343
+ *
344
+ * @category constructors
345
+ * @since 1.0.0
346
+ */
347
+ export declare const makeObserving: (service: Service) => Service;
348
+ /**
349
+ * An executor that only reads the engine: every method that drives or
350
+ * changes a run dies.
351
+ *
352
+ * A host composed over read-only stores carries it. Unlike
353
+ * {@link makeObserving}, it records no cancel and delivers no signal, because
354
+ * such a host can write neither. Without `readExecution` a listing answers
355
+ * from the control plane's coordination copy alone.
356
+ *
357
+ * @category constructors
358
+ * @since 1.0.0
359
+ */
360
+ export declare const makeReadOnly: ({ readExecution, readExecutions }?: {
361
+ readonly readExecution?: Service["readExecution"];
362
+ readonly readExecutions?: Service["readExecutions"];
363
+ }) => Service;
364
+ /**
365
+ * Provides {@link makeNoop}.
366
+ *
367
+ * @category layers
368
+ * @since 0.1.0
369
+ */
370
+ export declare const layerNoop: (overrides?: Partial<Service>) => Layer.Layer<ControlExecutor>;
371
+ export {};
372
+ //# sourceMappingURL=ControlExecutor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ControlExecutor.d.ts","sourceRoot":"","sources":["../../src/ControlExecutor.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,212 @@
1
+ /**
2
+ * Acceptance port from the control plane into a real run executor.
3
+ *
4
+ * Governing contract: `docs/pages/control/index.md`.
5
+ *
6
+ * @since 0.1.0
7
+ */
8
+ import * as Sha256 from "@smthrs/crypto/Sha256";
9
+ import * as DurableDeferred from "@smthrs/flow/DurableDeferred";
10
+ import { Context, Effect, Layer, Option, Schema } from "effect";
11
+ /**
12
+ * The waiting reason a human wait parks under.
13
+ *
14
+ * `HumanTask` and the agent's own `ask` both declare it
15
+ * (`@smthrs/flow` `FlowRuntime.annotateWaiting({ reason: "approval" })`), so
16
+ * it is the one value that separates "somebody has to answer this" from every
17
+ * other park.
18
+ *
19
+ * @category models
20
+ * @since 1.0.0
21
+ */
22
+ export const humanWaitReason = "approval";
23
+ /**
24
+ * The wait point a durable token addresses, as a person reads it.
25
+ *
26
+ * A `HumanTask` attempt is its own wait point named `WaitFor/<task>#<attempt>`
27
+ * (`@smthrs/flow` `HumanTask`), and a plain `WaitFor` gate is `WaitFor/<name>`.
28
+ * Splitting the two out of the token is what lets a client address an answer by
29
+ * the name a person was asked under rather than by a base64 blob, and lets an
30
+ * inbox say which of three attempts is open.
31
+ *
32
+ * A token this plane cannot parse names nothing extra; the token itself still
33
+ * travels, so the wait stays answerable.
34
+ */
35
+ /** The engine's own decoder: tokens are UTF-8 JSON in base64url, which `atob` misreads. */
36
+ const decodeToken = Schema.decodeUnknownOption(DurableDeferred.TokenParsed.FromString);
37
+ const waitPointOf = (token) => {
38
+ const decoded = decodeToken(token);
39
+ if (Option.isNone(decoded))
40
+ return {};
41
+ const deferredName = decoded.value.deferredName;
42
+ if (!deferredName.startsWith("WaitFor/"))
43
+ return {};
44
+ const point = deferredName.slice("WaitFor/".length);
45
+ const marker = point.lastIndexOf("#");
46
+ if (marker < 0)
47
+ return { name: point };
48
+ const attempt = Number(point.slice(marker + 1));
49
+ return Number.isSafeInteger(attempt) && attempt > 0
50
+ ? { name: point.slice(0, marker), attempt }
51
+ : { name: point };
52
+ };
53
+ /**
54
+ * One parked execution as a {@link PendingWait}, or nothing when it is not a
55
+ * wait a person or a named control signal can end.
56
+ *
57
+ * Shared by the two readers that produce these rows — the control plane's own
58
+ * SQL runtime, when it shares a database with the engine, and the executor's
59
+ * observation port, when it does not — so a wait reads the same either way.
60
+ *
61
+ * @category constructors
62
+ * @since 1.0.0
63
+ */
64
+ export const pendingWaitOf = (row) => {
65
+ if (row.token === null || row.token === undefined)
66
+ return undefined;
67
+ const point = waitPointOf(row.token);
68
+ if (row.reason !== humanWaitReason && !(row.reason === "event" && point.name !== undefined))
69
+ return undefined;
70
+ return {
71
+ runId: row.runId,
72
+ reason: row.reason,
73
+ token: row.token,
74
+ tokenDigest: Sha256.digestSync(row.token),
75
+ createdAt: row.createdAt,
76
+ ...(row.flowId === undefined ? {} : { flowId: row.flowId }),
77
+ ...point,
78
+ ...(row.request === undefined ? {} : { request: row.request })
79
+ };
80
+ };
81
+ /**
82
+ * The wait a `Node` approval target addresses, when it addresses one.
83
+ *
84
+ * Most gates are a capability the run wants allowed: the target's `digest` is
85
+ * the request's own digest, a decision grants or refuses it, and the control
86
+ * plane holds a registered token for it. A `HumanTask` gate has no such token
87
+ * — nothing registers one, because the run parked itself on a durable wait
88
+ * rather than asking the control plane for permission — and `lookupApproval`
89
+ * answers a target with no token by reporting the RUN as missing. That is what
90
+ * an operator saw when the app submitted an answer as an ordinary approval:
91
+ * `/control/RunNotFound` naming a run that was listed, rendered, and waiting
92
+ * (workspace 4bb93306, run-1).
93
+ *
94
+ * So a decision has to be able to tell the two apart from the payload alone.
95
+ * The approvals projection publishes a human wait with the durable wait token
96
+ * as the `digest` and the wait point's own name as the `requestId`, and a
97
+ * durable deferred token is self-describing: it decodes to a flow, an
98
+ * execution, and a deferred name under `WaitFor/`. Nothing else produces one,
99
+ * so a target carrying one is a question, and the name it answers is right
100
+ * there.
101
+ *
102
+ * @category constructors
103
+ * @since 1.0.0
104
+ */
105
+ export const answerableWait = (target) => {
106
+ if (target._tag !== "Node")
107
+ return undefined;
108
+ const point = waitPointOf(target.digest);
109
+ return point.name === undefined ? undefined : { name: target.requestId, token: target.digest };
110
+ };
111
+ /**
112
+ * The {@link Service} tag.
113
+ *
114
+ * @category services
115
+ * @since 0.1.0
116
+ */
117
+ export class ControlExecutor extends Context.Service()("/control/ControlExecutor") {
118
+ }
119
+ /**
120
+ * Builds a {@link Service} from an implementation of its methods.
121
+ *
122
+ * @category constructors
123
+ * @since 0.1.0
124
+ */
125
+ export const make = (implementation) => ControlExecutor.of(implementation);
126
+ /**
127
+ * A {@link Service} that accepts every launch as `pending` and starts
128
+ * nothing. Overrides replace individual methods.
129
+ *
130
+ * @category constructors
131
+ * @since 0.1.0
132
+ */
133
+ export const makeNoop = (overrides = {}) => make({
134
+ launch: Effect.fn("ControlExecutor.launch")(() => Effect.succeed("pending")),
135
+ // An executor that starts nothing owns no engine row and no wait point, so
136
+ // it records no cancel and matches no signal. `Control.cancel` still writes
137
+ // its journal entry and interrupts a local fiber, and `Control.signal`
138
+ // still records the message: both are what the port's absence already did.
139
+ requestCancel: Effect.fn("ControlExecutor.requestCancel")(() => Effect.succeed("unknown")),
140
+ deliverSignal: Effect.fn("ControlExecutor.deliverSignal")(() => Effect.succeed("unknown")),
141
+ resumeRun: Effect.fn("ControlExecutor.resumeRun")(() => Effect.succeed("unknown")),
142
+ settleCancelledPark: Effect.fn("ControlExecutor.settleCancelledPark")(() => Effect.void),
143
+ ...overrides
144
+ });
145
+ /**
146
+ * Provides {@link ControlExecutor} from an implementation.
147
+ *
148
+ * @category layers
149
+ * @since 0.1.0
150
+ */
151
+ export const layer = (implementation) => Layer.succeed(ControlExecutor)(make(implementation));
152
+ /**
153
+ * The same executor with methods that drive a run refused.
154
+ *
155
+ * A host composed to observe runs rather than drive them has no completion
156
+ * judge, so it must not be able to start or resume one: without this a verb
157
+ * misclassified as a read would admit a run and lose it at its first
158
+ * completion, which is the outcome the boot-time judge requirement exists to
159
+ * prevent. Reaching either method is a composition defect, not an operator
160
+ * error, so it dies rather than failing.
161
+ *
162
+ * Observation, cancellation and signal delivery stay live. A cancel and a
163
+ * signal record a durable request that the process driving the run picks up,
164
+ * so none of them reaches a completion, and refusing them would stop an
165
+ * operator ending a run on a host with no gateway key, which is the whole
166
+ * reason a read host exists.
167
+ *
168
+ * @category constructors
169
+ * @since 1.0.0
170
+ */
171
+ export const makeObserving = (service) => {
172
+ const refuse = (method) => Effect.die(new Error(`This host observes runs and drives none, so ControlExecutor.${method} is unreachable on it. ` +
173
+ "Compose it with startsRuns to start or resume a run."));
174
+ return make({
175
+ ...service,
176
+ requestComplete: undefined,
177
+ launch: Effect.fn("ControlExecutor.launch")(() => refuse("launch")),
178
+ resumeRun: Effect.fn("ControlExecutor.resumeRun")(() => refuse("resumeRun"))
179
+ });
180
+ };
181
+ /**
182
+ * An executor that only reads the engine: every method that drives or
183
+ * changes a run dies.
184
+ *
185
+ * A host composed over read-only stores carries it. Unlike
186
+ * {@link makeObserving}, it records no cancel and delivers no signal, because
187
+ * such a host can write neither. Without `readExecution` a listing answers
188
+ * from the control plane's coordination copy alone.
189
+ *
190
+ * @category constructors
191
+ * @since 1.0.0
192
+ */
193
+ export const makeReadOnly = ({ readExecution, readExecutions } = {}) => {
194
+ const refuse = (method) => Effect.die(new Error(`This host only observes runs, so ControlExecutor.${method} is unreachable on it.`));
195
+ return make({
196
+ ...(readExecution === undefined ? {} : { readExecution }),
197
+ ...(readExecutions === undefined ? {} : { readExecutions }),
198
+ launch: Effect.fn("ControlExecutor.launch")(() => refuse("launch")),
199
+ requestCancel: Effect.fn("ControlExecutor.requestCancel")(() => refuse("requestCancel")),
200
+ deliverSignal: Effect.fn("ControlExecutor.deliverSignal")(() => refuse("deliverSignal")),
201
+ resumeRun: Effect.fn("ControlExecutor.resumeRun")(() => refuse("resumeRun")),
202
+ settleCancelledPark: Effect.fn("ControlExecutor.settleCancelledPark")(() => refuse("settleCancelledPark"))
203
+ });
204
+ };
205
+ /**
206
+ * Provides {@link makeNoop}.
207
+ *
208
+ * @category layers
209
+ * @since 0.1.0
210
+ */
211
+ export const layerNoop = (overrides = {}) => Layer.succeed(ControlExecutor)(makeNoop(overrides));
212
+ //# sourceMappingURL=ControlExecutor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ControlExecutor.js","sourceRoot":"","sources":["../../src/ControlExecutor.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Acceptance port from the control plane into a real run executor.\n *\n * Governing contract: `docs/pages/control/index.md`.\n *\n * @since 0.1.0\n */\n\nimport * as Sha256 from \"@smthrs/crypto/Sha256\"\nimport * as DurableDeferred from \"@smthrs/flow/DurableDeferred\"\nimport type { ExecutionFact } from \"@smthrs/journal\"\nimport { Context, Effect, Layer, Option, Schema } from \"effect\"\nimport type { LaunchFailed, PersistenceError } from \"./ControlError.ts\"\nimport type { StoredPlan } from \"./ControlRuntime.ts\"\nimport type {\n ApprovalTarget,\n ExecutionBatch,\n PendingWait,\n Principal,\n Receipt,\n RunId,\n RunSummary,\n SignalPayload\n} from \"./ControlSchema.ts\"\n\n/**\n * One stored plan and the run summary it is being started as.\n *\n * @category models\n * @since 0.1.0\n */\nexport interface Launch {\n readonly plan: StoredPlan\n readonly run: RunSummary\n}\n\n/**\n * Whether the executor took the launch now or queued it.\n *\n * @category models\n * @since 0.1.0\n */\nexport type Acceptance = \"accepted\" | \"pending\"\n\n/**\n * One run whose cancellation has to become durable on the engine row.\n *\n * @category models\n * @since 0.1.0\n */\nexport interface CancelRequest {\n readonly runId: RunId\n}\n\n/**\n * The engine row a cancel request arrived too late for.\n *\n * It carries the ENGINE's status rather than a bare marker because the control\n * plane cannot read that row: in the shipped CLI the two `flows_runs` tables\n * live in two files (`.flows/control.db` and `.flows/engine.db`), so the port\n * is the only place the plane learns what actually became of the run.\n *\n * @category models\n * @since 0.1.0\n */\nexport interface CancelTerminal {\n readonly _tag: \"Terminal\"\n readonly status: \"completed\" | \"failed\" | \"cancelled\"\n}\n\n/**\n * What the executor did with a cancel request.\n *\n * `recorded` means THIS call set `cancel_requested_at_ms` on the engine row, so\n * the owning process stops the run at its next cancel poll whichever process\n * asked. `already-requested` means the column was already set when this call\n * arrived: the request is just as durable, and the cancellation it belongs to\n * was somebody else's. `Control.cancel` keys its attribution event on that\n * difference — the write is first-writer-wins and every repeat re-runs the\n * whole mutation, so answering `recorded` to all of them left one journaled\n * `control.run.cancel-requested` per ask for a single cancellation (release validation\n * smoke: three `cancel` calls and one `down` against one parked run left four).\n * `unknown` means this executor's engine has no row for the run at all, which\n * is the honest answer for a run another composition launched into a database\n * this one does not share. A {@link CancelTerminal} means the engine row has\n * already settled, so there is nothing left to stop: recording intent on it\n * would be a request no process can ever act on, and answering `recorded` let\n * `Control.cancel` write a terminal control status the engine row does not have\n * (triage B-11).\n *\n * @category models\n * @since 0.1.0\n */\nexport type CancelRecord = \"recorded\" | \"already-requested\" | \"unknown\" | CancelTerminal\n\n/**\n * One parked run that has been told to resume.\n *\n * @category models\n * @since 0.1.0\n */\nexport interface ResumeRequest {\n readonly runId: RunId\n}\n\n/**\n * What the executor did with a resume request.\n *\n * `resuming` means this executor hosts the run's execution, has taken the\n * control row's fence, and is re-driving it: the caller can stop, and the run\n * moves on its own. `unknown` means this executor drives no execution for the\n * run, which is the answer an operator's CLI, a gateway, or any second process\n * gives, and it is why the control plane records the delegation durably\n * instead of treating its own journal entry as the delivery (triage B-15).\n *\n * @category models\n * @since 0.1.0\n */\nexport type ResumeUptake = \"resuming\" | \"unknown\"\n\n/**\n * One signal to deliver to a run's open wait point.\n *\n * @category models\n * @since 0.1.0\n */\nexport interface Signal {\n /** Actor-scoped durable admission identity, present on control-plane delivery. */\n readonly commandId?: string\n /** An existing immutable binding survives a crash before acknowledgment. */\n readonly token?: string | null\n readonly runId: RunId\n readonly signal: SignalPayload\n /**\n * Who admitted the signal. A human wait ({@link humanWaitReason}) completes\n * only when `ApprovalAuthority` authorizes this principal to approve the\n * wait's `Node` target; without one the signal is `refused`.\n */\n readonly principal?: Principal | undefined\n}\n\n/**\n * What the executor did with a signal.\n *\n * `delivered` means the `WaitFor` deferred the run is parked on was completed\n * with the signal's payload and the run was woken. `no-match` means the run IS\n * parked and is waiting for something else, which `Control.signal` refuses\n * rather than recording a delivery nothing consumes. `unknown` means this\n * executor is driving no execution for the run at all — another process may\n * be, or none is yet — so the recorded message is the whole delivery and the\n * executor that eventually drives the run replays it at its next start.\n * `refused` means the signal named an open human wait and `ApprovalAuthority`\n * did not authorize its principal to answer it: the wait stays open, and\n * `Control.signal` fails `Unauthorized`.\n *\n * @category models\n * @since 0.1.0\n */\nexport type SignalDelivery = \"delivered\" | \"no-match\" | \"refused\" | \"unknown\"\n\n/**\n * Current engine observation. Missing execution is distinct from a running one.\n * @category models\n * @since 1.0.0\n */\nexport type ExecutionObservation =\n | { readonly _tag: \"Missing\" }\n | {\n readonly _tag: \"Observed\"\n readonly executionView?: ExecutionFact.View | undefined\n readonly status: \"accepted\" | \"running\" | \"parked\" | \"waiting-approval\" | \"completed\" | \"failed\" | \"cancelled\"\n readonly waitingReason?: string | undefined\n readonly parentRunId?: string | undefined\n readonly lineageId?: string | undefined\n readonly roundOrdinal?: number | undefined\n /**\n * Open human waits anywhere in this execution's tree.\n *\n * The control plane cannot compute these. It keeps its own coordination\n * copy of `flows_runs`, and the executions a flow spawns — where a nested\n * `HumanTask` actually parks — exist only in the executor's database, with\n * the edges that link them. `readExecution` is the one place that reads\n * both, so it is where a run tree's open questions become visible to\n * anything else.\n *\n * Absent when nothing in the tree is waiting on a person. `status` rolls\n * up with them: an execution parked while a descendant holds a human wait\n * is observed as `waiting-approval`.\n */\n readonly pendingWaits?: ReadonlyArray<PendingWait> | undefined\n }\n\n/**\n * The waiting reason a human wait parks under.\n *\n * `HumanTask` and the agent's own `ask` both declare it\n * (`@smthrs/flow` `FlowRuntime.annotateWaiting({ reason: \"approval\" })`), so\n * it is the one value that separates \"somebody has to answer this\" from every\n * other park.\n *\n * @category models\n * @since 1.0.0\n */\nexport const humanWaitReason = \"approval\"\n\n/**\n * The wait point a durable token addresses, as a person reads it.\n *\n * A `HumanTask` attempt is its own wait point named `WaitFor/<task>#<attempt>`\n * (`@smthrs/flow` `HumanTask`), and a plain `WaitFor` gate is `WaitFor/<name>`.\n * Splitting the two out of the token is what lets a client address an answer by\n * the name a person was asked under rather than by a base64 blob, and lets an\n * inbox say which of three attempts is open.\n *\n * A token this plane cannot parse names nothing extra; the token itself still\n * travels, so the wait stays answerable.\n */\n/** The engine's own decoder: tokens are UTF-8 JSON in base64url, which `atob` misreads. */\nconst decodeToken = Schema.decodeUnknownOption(DurableDeferred.TokenParsed.FromString)\n\nconst waitPointOf = (token: string): { readonly name?: string; readonly attempt?: number } => {\n const decoded = decodeToken(token)\n if (Option.isNone(decoded)) return {}\n const deferredName = decoded.value.deferredName\n if (!deferredName.startsWith(\"WaitFor/\")) return {}\n const point = deferredName.slice(\"WaitFor/\".length)\n const marker = point.lastIndexOf(\"#\")\n if (marker < 0) return { name: point }\n const attempt = Number(point.slice(marker + 1))\n return Number.isSafeInteger(attempt) && attempt > 0\n ? { name: point.slice(0, marker), attempt }\n : { name: point }\n}\n\n/**\n * One parked execution as a {@link PendingWait}, or nothing when it is not a\n * wait a person or a named control signal can end.\n *\n * Shared by the two readers that produce these rows — the control plane's own\n * SQL runtime, when it shares a database with the engine, and the executor's\n * observation port, when it does not — so a wait reads the same either way.\n *\n * @category constructors\n * @since 1.0.0\n */\nexport const pendingWaitOf = (row: {\n readonly runId: string\n readonly flowId?: string | undefined\n readonly reason: string\n readonly token: string | null | undefined\n readonly request?: unknown\n readonly createdAt: number\n}): PendingWait | undefined => {\n if (row.token === null || row.token === undefined) return undefined\n const point = waitPointOf(row.token)\n if (row.reason !== humanWaitReason && !(row.reason === \"event\" && point.name !== undefined)) return undefined\n return {\n runId: row.runId,\n reason: row.reason,\n token: row.token,\n tokenDigest: Sha256.digestSync(row.token),\n createdAt: row.createdAt,\n ...(row.flowId === undefined ? {} : { flowId: row.flowId }),\n ...point,\n ...(row.request === undefined ? {} : { request: row.request as PendingWait[\"request\"] })\n }\n}\n\n/**\n * The wait a `Node` approval target addresses, when it addresses one.\n *\n * Most gates are a capability the run wants allowed: the target's `digest` is\n * the request's own digest, a decision grants or refuses it, and the control\n * plane holds a registered token for it. A `HumanTask` gate has no such token\n * — nothing registers one, because the run parked itself on a durable wait\n * rather than asking the control plane for permission — and `lookupApproval`\n * answers a target with no token by reporting the RUN as missing. That is what\n * an operator saw when the app submitted an answer as an ordinary approval:\n * `/control/RunNotFound` naming a run that was listed, rendered, and waiting\n * (workspace 4bb93306, run-1).\n *\n * So a decision has to be able to tell the two apart from the payload alone.\n * The approvals projection publishes a human wait with the durable wait token\n * as the `digest` and the wait point's own name as the `requestId`, and a\n * durable deferred token is self-describing: it decodes to a flow, an\n * execution, and a deferred name under `WaitFor/`. Nothing else produces one,\n * so a target carrying one is a question, and the name it answers is right\n * there.\n *\n * @category constructors\n * @since 1.0.0\n */\nexport const answerableWait = (\n target: ApprovalTarget\n): { readonly name: string; readonly token: string } | undefined => {\n if (target._tag !== \"Node\") return undefined\n const point = waitPointOf(target.digest)\n return point.name === undefined ? undefined : { name: target.requestId, token: target.digest }\n}\n\n/**\n * The executor port: the control plane hands work over to a real run executor\n * and learns only what the executor did with it.\n *\n * `launch` is acceptance. `requestCancel`, `deliverSignal`, and `resumeRun`\n * are the three requests that have to reach the engine to mean anything: a\n * cancel is durable on the engine row so that whichever process owns the run\n * stops it, a signal completes the wait point a parked run is actually waiting\n * on, and a resume re-drives the execution an approval decision unblocked.\n * Without them the control plane records facts nobody reads — a cancel that\n * answers `ClaimLost` to every process but the owner, a signal a parked run\n * never sees, and a resume event published into an in-process hub no other\n * process subscribes to (the release policy; triage B-10, B-13, B-15).\n *\n * @category services\n * @since 0.1.0\n */\nexport interface Service {\n /** Read from the executor's database, never the control coordination copy. */\n readonly readExecution?: (runId: RunId) => Effect.Effect<ExecutionObservation, PersistenceError>\n /** Exact observations scoped to the authorized control root; unrelated native rows are never exposed. */\n readonly readExecutions?: (input: {\n readonly runId: RunId\n readonly executionIds: ReadonlyArray<string>\n }) => Effect.Effect<ExecutionBatch, PersistenceError>\n readonly launch: (input: Launch) => Effect.Effect<Acceptance, LaunchFailed>\n /**\n * Host-only close of a retained run, after its last native module completed.\n * @since 1.0.0\n */\n readonly requestComplete?:\n | ((input: { readonly runId: RunId; readonly receiptId: string }) => Effect.Effect<Receipt, PersistenceError>)\n | undefined\n /**\n * Records a cancellation on the engine row, durably, regardless of which\n * process owns the run.\n */\n readonly requestCancel: (input: CancelRequest) => Effect.Effect<CancelRecord, PersistenceError>\n /**\n * Completes the run's open `WaitFor` wait point with the signal's payload.\n */\n readonly deliverSignal: (input: Signal) => Effect.Effect<SignalDelivery, PersistenceError>\n /**\n * Takes up a resume: claims the control row and re-drives the execution,\n * when this executor is the one hosting it.\n */\n readonly resumeRun: (input: ResumeRequest) => Effect.Effect<ResumeUptake, PersistenceError>\n /**\n * Finishes a PARKED execution whose cancellation is already durable.\n *\n * A park has no owner — that is what makes it resumable — so nothing is\n * driving the run and nothing reads the request {@link requestCancel} wrote.\n * The engine's parked-run sweep does, once per heartbeat, but a `smithers\n * cancel` process writes the request at the very end of its life and exits\n * before that tick: the release validation watched an engine row stay\n * `suspended` with `cancel_requested_at_ms` set through six more commands\n * and fifteen seconds, so `gc` collected the run in `control.db` and\n * skipped it in `engine.db`.\n *\n * Called AFTER the cancel mutation commits, never inside it: driving a run\n * re-enters the engine, whose writes would wait on the writer the\n * mutation's transaction holds. An executor that does not host the run, or\n * one whose run is not parked, does nothing and leaves the durable request\n * standing for the host that does.\n */\n readonly settleCancelledPark: (input: CancelRequest) => Effect.Effect<void, PersistenceError>\n}\n\n/**\n * The {@link Service} tag.\n *\n * @category services\n * @since 0.1.0\n */\nexport class ControlExecutor extends Context.Service<ControlExecutor, Service>()(\n \"/control/ControlExecutor\"\n) {}\n\n/**\n * Builds a {@link Service} from an implementation of its methods.\n *\n * @category constructors\n * @since 0.1.0\n */\nexport const make = (implementation: Service): Service => ControlExecutor.of(implementation)\n\n/**\n * A {@link Service} that accepts every launch as `pending` and starts\n * nothing. Overrides replace individual methods.\n *\n * @category constructors\n * @since 0.1.0\n */\nexport const makeNoop = (overrides: Partial<Service> = {}): Service =>\n make({\n launch: Effect.fn(\"ControlExecutor.launch\")(() => Effect.succeed(\"pending\" as const)),\n // An executor that starts nothing owns no engine row and no wait point, so\n // it records no cancel and matches no signal. `Control.cancel` still writes\n // its journal entry and interrupts a local fiber, and `Control.signal`\n // still records the message: both are what the port's absence already did.\n requestCancel: Effect.fn(\"ControlExecutor.requestCancel\")(() => Effect.succeed(\"unknown\" as const)),\n deliverSignal: Effect.fn(\"ControlExecutor.deliverSignal\")(() => Effect.succeed(\"unknown\" as const)),\n resumeRun: Effect.fn(\"ControlExecutor.resumeRun\")(() => Effect.succeed(\"unknown\" as const)),\n settleCancelledPark: Effect.fn(\"ControlExecutor.settleCancelledPark\")(() => Effect.void),\n ...overrides\n })\n\n/**\n * Provides {@link ControlExecutor} from an implementation.\n *\n * @category layers\n * @since 0.1.0\n */\nexport const layer = (implementation: Service): Layer.Layer<ControlExecutor> =>\n Layer.succeed(ControlExecutor)(make(implementation))\n\n/**\n * The same executor with methods that drive a run refused.\n *\n * A host composed to observe runs rather than drive them has no completion\n * judge, so it must not be able to start or resume one: without this a verb\n * misclassified as a read would admit a run and lose it at its first\n * completion, which is the outcome the boot-time judge requirement exists to\n * prevent. Reaching either method is a composition defect, not an operator\n * error, so it dies rather than failing.\n *\n * Observation, cancellation and signal delivery stay live. A cancel and a\n * signal record a durable request that the process driving the run picks up,\n * so none of them reaches a completion, and refusing them would stop an\n * operator ending a run on a host with no gateway key, which is the whole\n * reason a read host exists.\n *\n * @category constructors\n * @since 1.0.0\n */\nexport const makeObserving = (service: Service): Service => {\n const refuse = (method: string) =>\n Effect.die(\n new Error(\n `This host observes runs and drives none, so ControlExecutor.${method} is unreachable on it. ` +\n \"Compose it with startsRuns to start or resume a run.\"\n )\n )\n return make({\n ...service,\n requestComplete: undefined,\n launch: Effect.fn(\"ControlExecutor.launch\")(() => refuse(\"launch\")),\n resumeRun: Effect.fn(\"ControlExecutor.resumeRun\")(() => refuse(\"resumeRun\"))\n })\n}\n\n/**\n * An executor that only reads the engine: every method that drives or\n * changes a run dies.\n *\n * A host composed over read-only stores carries it. Unlike\n * {@link makeObserving}, it records no cancel and delivers no signal, because\n * such a host can write neither. Without `readExecution` a listing answers\n * from the control plane's coordination copy alone.\n *\n * @category constructors\n * @since 1.0.0\n */\nexport const makeReadOnly = (\n { readExecution, readExecutions }: {\n readonly readExecution?: Service[\"readExecution\"]\n readonly readExecutions?: Service[\"readExecutions\"]\n } = {}\n): Service => {\n const refuse = (method: string) =>\n Effect.die(new Error(`This host only observes runs, so ControlExecutor.${method} is unreachable on it.`))\n return make({\n ...(readExecution === undefined ? {} : { readExecution }),\n ...(readExecutions === undefined ? {} : { readExecutions }),\n launch: Effect.fn(\"ControlExecutor.launch\")(() => refuse(\"launch\")),\n requestCancel: Effect.fn(\"ControlExecutor.requestCancel\")(() => refuse(\"requestCancel\")),\n deliverSignal: Effect.fn(\"ControlExecutor.deliverSignal\")(() => refuse(\"deliverSignal\")),\n resumeRun: Effect.fn(\"ControlExecutor.resumeRun\")(() => refuse(\"resumeRun\")),\n settleCancelledPark: Effect.fn(\"ControlExecutor.settleCancelledPark\")(() => refuse(\"settleCancelledPark\"))\n })\n}\n\n/**\n * Provides {@link makeNoop}.\n *\n * @category layers\n * @since 0.1.0\n */\nexport const layerNoop = (overrides: Partial<Service> = {}): Layer.Layer<ControlExecutor> =>\n Layer.succeed(ControlExecutor)(makeNoop(overrides))\n"]}