@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
package/src/Health.ts ADDED
@@ -0,0 +1,591 @@
1
+ /** Observational health contracts shared by durable runs and native sessions.
2
+ * @since 1.0.0
3
+ */
4
+
5
+ import * as Sha256 from "@smthrs/crypto/Sha256"
6
+ import * as Fault from "@smthrs/flow/Fault"
7
+ import type * as Evaluator from "@smthrs/model/Evaluator"
8
+ import { Cause, Effect, Metric, Schema } from "effect"
9
+ import type { ControlEvent, RunSummary } from "./ControlSchema.ts"
10
+ import { makeJevSessionChecker } from "./JevSessionChecker.ts"
11
+
12
+ /** Authoritative subject lifecycle.
13
+ * @category schemas
14
+ * @since 1.0.0
15
+ */
16
+ export const SubjectState = Schema.Literals([
17
+ "accepted",
18
+ "running",
19
+ "parked",
20
+ "waiting-approval",
21
+ "completed",
22
+ "failed",
23
+ "cancelled",
24
+ "spawning",
25
+ "exited"
26
+ ])
27
+ /** Authoritative subject lifecycle.
28
+ * @category models
29
+ * @since 1.0.0
30
+ */
31
+ export type SubjectState = typeof SubjectState.Type
32
+ /** Health vocabulary shared with Monitor.
33
+ * @category schemas
34
+ * @since 1.0.0
35
+ */
36
+ export const HealthState = Schema.Literals([
37
+ "healthy",
38
+ "stalled",
39
+ "wedged-node",
40
+ "runaway-loop",
41
+ "awaiting-human",
42
+ "failing",
43
+ "unknown"
44
+ ])
45
+ /** Derived subject health.
46
+ * @category models
47
+ * @since 1.0.0
48
+ */
49
+ export type HealthState = typeof HealthState.Type
50
+ /** Only explicit semantic checks establish activity.
51
+ * @category schemas
52
+ * @since 1.0.0
53
+ */
54
+ export const Activity = Schema.Literals(["working", "idle", "needs-input", "unknown"])
55
+ /** Semantic activity.
56
+ * @category models
57
+ * @since 1.0.0
58
+ */
59
+ export type Activity = typeof Activity.Type
60
+ /** Attention never confers permission to act. `needs-resume` is a run parked over
61
+ * executions its owner released: nothing restarts them until `smthrs runs resume`.
62
+ * @category schemas
63
+ * @since 1.0.0
64
+ */
65
+ export const Attention = Schema.Literals(["none", "awaiting-approval", "needs-input", "needs-resume", "unhealthy"])
66
+ /** Human attention.
67
+ * @category models
68
+ * @since 1.0.0
69
+ */
70
+ export type Attention = typeof Attention.Type
71
+ /** Whether a successful reading still describes this subject.
72
+ * @category schemas
73
+ * @since 1.0.0
74
+ */
75
+ export const Freshness = Schema.Literals(["fresh", "stale", "unobserved"])
76
+ /** Reading freshness.
77
+ * @category models
78
+ * @since 1.0.0
79
+ */
80
+ export type Freshness = typeof Freshness.Type
81
+ /** Public reasons; arbitrary exception text and terminal bytes never cross this boundary.
82
+ * @category schemas
83
+ * @since 1.0.0
84
+ */
85
+ export const ReasonCode = Schema.Literals([
86
+ "ok",
87
+ "no-progress",
88
+ "awaiting-reply",
89
+ "prompt-detected",
90
+ "quota-wait",
91
+ "timer-wait",
92
+ "event-wait",
93
+ "released",
94
+ "unreachable",
95
+ "probe-timeout",
96
+ "probe-error",
97
+ "owner-changed"
98
+ ])
99
+ /** Public reason code.
100
+ * @category models
101
+ * @since 1.0.0
102
+ */
103
+ export type ReasonCode = typeof ReasonCode.Type
104
+ /** Validated checker output, containing no authority or ordering fields.
105
+ * @category schemas
106
+ * @since 1.0.0
107
+ */
108
+ export const ProbeReport = Schema.Struct({ activity: Activity, reason: Schema.optional(ReasonCode) })
109
+ /** Validated checker output.
110
+ * @category models
111
+ * @since 1.0.0
112
+ */
113
+ export type ProbeReport = typeof ProbeReport.Type
114
+ const Counter = Schema.Int.pipe(Schema.check(Schema.isGreaterThanOrEqualTo(0)))
115
+
116
+ /** A host-stamped observation; its journal sequence is supplied by the durable writer.
117
+ * @category schemas
118
+ * @since 1.0.0
119
+ */
120
+ export const HealthObservation = Schema.Struct({
121
+ subjectId: Schema.String,
122
+ state: SubjectState,
123
+ checkerId: Schema.String,
124
+ monitorId: Schema.String,
125
+ incarnation: Schema.String,
126
+ evidenceSeq: Counter,
127
+ observedAt: Counter,
128
+ expiresAt: Counter,
129
+ durationMs: Counter,
130
+ baseHealth: Schema.optional(HealthState),
131
+ outcome: Schema.Literals(["ok", "timeout", "error", "interrupted", "discarded"]),
132
+ report: Schema.optional(ProbeReport),
133
+ reason: Schema.optional(ReasonCode)
134
+ })
135
+ /** A host-stamped observation.
136
+ * @category models
137
+ * @since 1.0.0
138
+ */
139
+ export type HealthObservation = typeof HealthObservation.Type
140
+ /** One observation with its authoritative publication order.
141
+ * @category models
142
+ * @since 1.0.0
143
+ */
144
+ export interface RecordedObservation {
145
+ readonly observation: HealthObservation
146
+ readonly sequence: number
147
+ }
148
+ /** The additive wire contract rendered by every status surface.
149
+ * @category schemas
150
+ * @since 1.0.0
151
+ */
152
+ export const StatusRollup = Schema.Struct({
153
+ subjectId: Schema.String,
154
+ state: SubjectState,
155
+ activity: Activity,
156
+ health: HealthState,
157
+ attention: Attention,
158
+ freshness: Freshness,
159
+ reason: Schema.optional(ReasonCode),
160
+ provenance: Schema.optional(Schema.Struct({
161
+ checkerId: Schema.String,
162
+ monitorId: Schema.String,
163
+ observedAt: Counter,
164
+ expiresAt: Counter,
165
+ evidenceSeq: Counter,
166
+ incarnation: Schema.String,
167
+ version: Counter
168
+ })),
169
+ updatedAt: Counter
170
+ })
171
+ /** The additive wire contract.
172
+ * @category models
173
+ * @since 1.0.0
174
+ */
175
+ export type StatusRollup = typeof StatusRollup.Type
176
+ /** Durable event type for observational health.
177
+ * @category constants
178
+ * @since 1.0.0
179
+ */
180
+ export const statusObservedEventType = "control.status.observed"
181
+ /** Opaque identity of the authoritative run ownership and lifecycle point being observed.
182
+ * Equality is meaningful; lexical or numeric ordering is not.
183
+ * @category getters
184
+ * @since 1.0.0
185
+ */
186
+ export const runIncarnation = (run: RunSummary): string =>
187
+ Sha256.digestSync(JSON.stringify([
188
+ run.runId,
189
+ run.createdAt,
190
+ run.updatedAt,
191
+ run.ownerId ?? null,
192
+ run.parkedBy ?? null,
193
+ run.status,
194
+ run.waitingReason ?? null
195
+ ]))
196
+
197
+ /** Read-only evidence, deliberately excluding mutation ports and terminal text by default.
198
+ * Checkers are trusted host TypeScript, not a sandbox: captured ambient authority remains their author's responsibility.
199
+ * @category models
200
+ * @since 1.0.0
201
+ */
202
+ export interface ProbeContext {
203
+ readonly subjectId: string
204
+ readonly state: SubjectState
205
+ readonly events: ReadonlyArray<ControlEvent>
206
+ readonly summary?: RunSummary | undefined
207
+ readonly session?: {
208
+ readonly alive: boolean
209
+ readonly exitCode: number | null
210
+ readonly outputCursor: number
211
+ readonly outputTail?: string | undefined
212
+ } | undefined
213
+ readonly sinceCursor: number
214
+ }
215
+ /** Bounded polling, expiry and failure backoff.
216
+ * @category models
217
+ * @since 1.0.0
218
+ */
219
+ export interface CheckPolicy {
220
+ readonly intervalMs: number
221
+ readonly timeoutMs: number
222
+ readonly ttlMs: number
223
+ /** Grace for silent work before the flow host considers missing progress unhealthy. */
224
+ readonly stallAfterMs: number
225
+ readonly backoff: { readonly initialMs: number; readonly maxMs: number; readonly factor: number }
226
+ }
227
+ /** Trusted host checker; supply service requirements around its Effect before registration.
228
+ * @category models
229
+ * @since 1.0.0
230
+ */
231
+ export interface HealthChecker<C = unknown> {
232
+ readonly id: string
233
+ readonly configSchema?: Schema.Codec<C, unknown> | undefined
234
+ readonly defaults?: Partial<CheckPolicy> | undefined
235
+ readonly probe: (context: ProbeContext, config: C) => Effect.Effect<ProbeReport, unknown>
236
+ }
237
+ /** Data may select a registered checker but never executable code.
238
+ * @category models
239
+ * @since 1.0.0
240
+ */
241
+ export interface HealthBinding {
242
+ readonly checkerId: string
243
+ readonly config?: unknown
244
+ readonly policy?: Partial<CheckPolicy> | undefined
245
+ readonly exposeOutput?: boolean | undefined
246
+ }
247
+ /** Trusted host health configuration.
248
+ * @category models
249
+ * @since 1.0.0
250
+ */
251
+ export interface HealthConfig {
252
+ // A heterogeneous registry erases C only after each binding is decoded by its own schema.
253
+ readonly checkers?: ReadonlyArray<HealthChecker<any>> | undefined
254
+ readonly bindings?: Readonly<Record<string, HealthBinding>> | undefined
255
+ readonly limits?: { readonly maxSubjects?: number; readonly maxConcurrentProbes?: number } | undefined
256
+ }
257
+ /** Fully admitted checker binding.
258
+ * @category models
259
+ * @since 1.0.0
260
+ */
261
+ export interface ResolvedCheck {
262
+ readonly checker: HealthChecker<any>
263
+ readonly config: unknown
264
+ readonly policy: CheckPolicy
265
+ readonly exposeOutput: boolean
266
+ }
267
+ /** Host registry, built once before admitting subjects.
268
+ * @category models
269
+ * @since 1.0.0
270
+ */
271
+ export interface Registry {
272
+ readonly resolve: (key: string) => ResolvedCheck
273
+ readonly limits: { readonly maxSubjects: number; readonly maxConcurrentProbes: number }
274
+ }
275
+ /** Invalid trusted configuration fails startup instead of silently running another policy.
276
+ * @category errors
277
+ * @since 1.0.0
278
+ */
279
+ export class HealthConfigurationError
280
+ extends Schema.TaggedError<HealthConfigurationError>()("HealthConfigurationError", {
281
+ reason: Schema.Literals(["invalid-policy", "invalid-checker", "invalid-config"])
282
+ })
283
+ {}
284
+ Fault.register("HealthConfigurationError", "bug")
285
+ /** Safe cadence used by unconfigured hosts.
286
+ * @category constants
287
+ * @since 1.0.0
288
+ */
289
+ export const defaultPolicy: CheckPolicy = {
290
+ intervalMs: 5_000,
291
+ timeoutMs: 2_000,
292
+ ttlMs: 20_000,
293
+ stallAfterMs: 120_000,
294
+ backoff: { initialMs: 5_000, maxMs: 60_000, factor: 2 }
295
+ }
296
+ /** A known engine wait is a lifecycle fact, not missing progress.
297
+ * @category getters
298
+ * @since 1.0.0
299
+ */
300
+ export const waitReason = (state: SubjectState, waitingReason?: string): ReasonCode | undefined =>
301
+ state !== "parked" ? undefined : waitingReason === "quota" ?
302
+ "quota-wait"
303
+ : waitingReason === "timer"
304
+ ? "timer-wait"
305
+ : waitingReason === "event"
306
+ ? "event-wait"
307
+ : undefined
308
+ /** Flow default observes lifecycle without inventing semantic activity.
309
+ * @category constants
310
+ * @since 1.0.0
311
+ */
312
+ export const lifecycleRunChecker: HealthChecker = {
313
+ id: "lifecycle.run",
314
+ probe: (context) =>
315
+ Effect.succeed({ activity: "unknown", reason: waitReason(context.state, context.summary?.waitingReason) ?? "ok" })
316
+ }
317
+ /** Session default never interprets silence or output movement as task activity.
318
+ * @category constants
319
+ * @since 1.0.0
320
+ */
321
+ export const lifecycleSessionChecker: HealthChecker = {
322
+ id: "lifecycle.session",
323
+ probe: () => Effect.succeed({ activity: "unknown", reason: "ok" })
324
+ }
325
+ const hostCheckers = (evaluator: Evaluator.Evaluator | undefined): ReadonlyArray<HealthChecker<any>> => [
326
+ makeJevSessionChecker({ evaluator })
327
+ ]
328
+ /** Checkers every host may bind by id, beyond the lifecycle default it always has.
329
+ *
330
+ * Registration is not a binding. A host opts in by naming one of these as a
331
+ * `HealthBinding.checkerId`; a host that names none keeps the lifecycle
332
+ * default, and no entry here is ever consulted for an unbound subject.
333
+ *
334
+ * @category constants
335
+ * @since 1.0.0
336
+ */
337
+ export const registeredCheckers: ReadonlyArray<HealthChecker<any>> = hostCheckers(undefined)
338
+ const bounded = (value: number, min: number, max: number) => Number.isSafeInteger(value) && value >= min && value <= max
339
+ const invalid = (reason: HealthConfigurationError["reason"]): never => {
340
+ throw new HealthConfigurationError({ reason })
341
+ }
342
+ /** Admit a host registry and all configured bindings. Unknown checker ids use the lifecycle default.
343
+ * @category constructors
344
+ * @since 1.0.0
345
+ */
346
+ export const makeRegistry = (
347
+ config: HealthConfig = {},
348
+ kind: "run" | "session" = "run",
349
+ evaluator?: Evaluator.Evaluator
350
+ ): Registry => {
351
+ const fallback = kind === "run" ? lifecycleRunChecker : lifecycleSessionChecker
352
+ const checkers = new Map<string, ResolvedCheck["checker"]>([
353
+ [fallback.id, fallback],
354
+ ...hostCheckers(evaluator).map((checker) => [checker.id, checker] as const)
355
+ ])
356
+ for (const checker of config.checkers ?? []) {
357
+ if (!/^[a-zA-Z0-9][a-zA-Z0-9._/-]{0,127}$/.test(checker.id) || checkers.has(checker.id)) invalid("invalid-checker")
358
+ checkers.set(checker.id, checker)
359
+ }
360
+ const resolve = (binding?: HealthBinding): ResolvedCheck => {
361
+ const checker = binding === undefined ? fallback : checkers.get(binding.checkerId) ?? fallback
362
+ const merged = { ...defaultPolicy, ...checker.defaults, ...binding?.policy }
363
+ if (merged.backoff == null || typeof merged.backoff !== "object") invalid("invalid-policy")
364
+ const policy = { ...merged, backoff: { ...merged.backoff } }
365
+ if (
366
+ !bounded(policy.intervalMs, 10, 3_600_000) || !bounded(policy.timeoutMs, 1, 60_000) ||
367
+ !bounded(policy.ttlMs, policy.timeoutMs, 86_400_000) ||
368
+ !bounded(policy.stallAfterMs, policy.intervalMs, 86_400_000) ||
369
+ !bounded(policy.backoff.initialMs, 10, 3_600_000) ||
370
+ !bounded(policy.backoff.maxMs, policy.backoff.initialMs, 3_600_000) ||
371
+ !Number.isFinite(policy.backoff.factor) || policy.backoff.factor < 1 || policy.backoff.factor > 10
372
+ ) invalid("invalid-policy")
373
+ let decoded: unknown = undefined
374
+ if (checker !== fallback || binding?.checkerId === fallback.id) {
375
+ if (checker.configSchema !== undefined) {
376
+ try {
377
+ decoded = Schema.decodeUnknownSync(checker.configSchema)(binding?.config)
378
+ } catch {
379
+ invalid("invalid-config")
380
+ }
381
+ } else if (binding?.config !== undefined) invalid("invalid-config")
382
+ }
383
+ return { checker, config: decoded, policy, exposeOutput: binding?.exposeOutput === true }
384
+ }
385
+ const defaults = resolve()
386
+ const bindings = new Map(Object.entries(config.bindings ?? {}).map(([key, value]) => [key, resolve(value)]))
387
+ const maxSubjects = config.limits?.maxSubjects ?? 128
388
+ const maxConcurrentProbes = config.limits?.maxConcurrentProbes ?? 8
389
+ if (!bounded(maxSubjects, 1, 500) || !bounded(maxConcurrentProbes, 1, 64)) invalid("invalid-policy")
390
+ return { resolve: (key) => bindings.get(key) ?? defaults, limits: { maxSubjects, maxConcurrentProbes } }
391
+ }
392
+ /** Failure delay, bounded even when a subject has failed for a long time.
393
+ * @category getters
394
+ * @since 1.0.0
395
+ */
396
+ export const nextDelay = (policy: CheckPolicy, consecutiveFailures: number): number =>
397
+ consecutiveFailures <= 0 ? policy.intervalMs : Math.max(
398
+ policy.intervalMs,
399
+ Math.min(
400
+ policy.backoff.maxMs,
401
+ policy.backoff.initialMs * policy.backoff.factor ** Math.min(consecutiveFailures - 1, 32)
402
+ )
403
+ )
404
+ /** Host-only ordering stamp captured before a probe.
405
+ * @category models
406
+ * @since 1.0.0
407
+ */
408
+ export interface ObservationStamp {
409
+ readonly monitorId: string
410
+ readonly incarnation: string
411
+ readonly evidenceSeq: number
412
+ }
413
+ const outcomes = Metric.counter("smithers.health.probes")
414
+ const durations = Metric.histogram("smithers.health.probe_duration_ms", {
415
+ boundaries: [1, 10, 100, 1_000, 5_000, 60_000]
416
+ })
417
+ const decodeReport = Schema.decodeUnknownSync(ProbeReport)
418
+ /** Evaluate one trusted checker with bounded timeout and safe failure values. Scope interruption propagates.
419
+ * The host must still compare current ownership/lifecycle before durably publishing the returned observation.
420
+ * @category constructors
421
+ * @since 1.0.0
422
+ */
423
+ export const evaluate = (
424
+ check: ResolvedCheck,
425
+ context: ProbeContext,
426
+ stamp: ObservationStamp
427
+ ): Effect.Effect<HealthObservation> =>
428
+ Effect.gen(function*() {
429
+ const subjectId = context.subjectId
430
+ const state = context.state
431
+ const captured = { ...stamp }
432
+ const started = yield* Effect.clockWith((clock) => clock.currentTimeMillis)
433
+ const result = yield* Effect.suspend(() => check.checker.probe(context, check.config)).pipe(
434
+ Effect.flatMap((candidate) => Effect.try({ try: () => decodeReport(candidate), catch: () => "invalid-report" })),
435
+ Effect.map((report) => ({ outcome: "ok" as const, report })),
436
+ Effect.catchCause((cause) =>
437
+ Cause.hasInterruptsOnly(cause)
438
+ ? Effect.interrupt
439
+ : Effect.succeed({ outcome: "error" as const, reason: "probe-error" as const })
440
+ ),
441
+ Effect.timeoutOrElse({
442
+ duration: check.policy.timeoutMs,
443
+ orElse: () => Effect.succeed({ outcome: "timeout" as const, reason: "probe-timeout" as const })
444
+ }),
445
+ Effect.tap((outcome) =>
446
+ Effect.annotateCurrentSpan({
447
+ outcome: outcome.outcome,
448
+ activity: "report" in outcome ? outcome.report.activity : "unknown"
449
+ })
450
+ ),
451
+ Effect.withSpan("smithers.health.probe", {
452
+ attributes: {
453
+ subjectKind: context.session === undefined ? "run" : "session",
454
+ subjectId,
455
+ checkerId: check.checker.id,
456
+ monitorId: captured.monitorId
457
+ }
458
+ })
459
+ )
460
+ const observedAt = yield* Effect.clockWith((clock) => clock.currentTimeMillis)
461
+ const durationMs = Math.max(0, observedAt - started)
462
+ yield* Metric.update(outcomes.pipe(Metric.withAttributes({ outcome: result.outcome })), 1)
463
+ yield* Metric.update(durations, durationMs)
464
+ return {
465
+ subjectId,
466
+ state,
467
+ checkerId: check.checker.id,
468
+ ...captured,
469
+ observedAt,
470
+ expiresAt: observedAt + check.policy.ttlMs,
471
+ durationMs,
472
+ ...result
473
+ }
474
+ })
475
+
476
+ /** Select evidence for the current opaque owner incarnation, breaking equal evidence ties by durable sequence.
477
+ * @category projections
478
+ * @since 1.0.0
479
+ */
480
+ export const latestObservation = (
481
+ candidates: ReadonlyArray<RecordedObservation>,
482
+ subjectId: string,
483
+ incarnation: string
484
+ ): RecordedObservation | undefined => {
485
+ let selected: RecordedObservation | undefined
486
+ for (const candidate of candidates) {
487
+ const observation = candidate.observation
488
+ if (
489
+ observation.subjectId !== subjectId || observation.incarnation !== incarnation ||
490
+ observation.outcome === "discarded"
491
+ ) continue
492
+ if (
493
+ selected === undefined || observation.evidenceSeq > selected.observation.evidenceSeq ||
494
+ observation.evidenceSeq === selected.observation.evidenceSeq && candidate.sequence > selected.sequence
495
+ ) selected = candidate
496
+ }
497
+ return selected
498
+ }
499
+ /** Authoritative facts supplied independently of checker output.
500
+ * @category models
501
+ * @since 1.0.0
502
+ */
503
+ export interface RollupInput {
504
+ readonly subjectId: string
505
+ readonly state: SubjectState
506
+ readonly incarnation: string
507
+ readonly waitingReason?: string | undefined
508
+ readonly exitCode?: number | null | undefined
509
+ readonly baseHealth?: HealthState | undefined
510
+ readonly latest?: RecordedObservation | undefined
511
+ readonly evidenceSeq?: number | undefined
512
+ readonly now: number
513
+ readonly updatedAt: number
514
+ }
515
+ /** Pure precedence: lifecycle and approvals win, and stale probes never establish activity.
516
+ * @category projections
517
+ * @since 1.0.0
518
+ */
519
+ export const rollup = (input: RollupInput): StatusRollup => {
520
+ const latest = input.latest
521
+ const reading = latest?.observation
522
+ const freshness = reading === undefined ?
523
+ "unobserved" :
524
+ reading.subjectId !== input.subjectId || reading.incarnation !== input.incarnation ||
525
+ reading.state !== input.state ||
526
+ reading.outcome !== "ok" || reading.report === undefined || input.now < reading.observedAt ||
527
+ input.now >= reading.expiresAt ||
528
+ (input.evidenceSeq !== undefined && reading.evidenceSeq < input.evidenceSeq) ?
529
+ "stale" :
530
+ "fresh"
531
+ const terminal = input.state === "completed" || input.state === "failed" || input.state === "cancelled" ||
532
+ input.state === "exited"
533
+ const approval = input.state === "waiting-approval" || input.state === "parked" && input.waitingReason === "approval"
534
+ // Released executions stay parked until an operator resumes the run (#3328).
535
+ const released = input.state === "parked" && input.waitingReason === "released"
536
+ const waiting = waitReason(input.state, input.waitingReason)
537
+ let activity: typeof Activity.Type = freshness === "fresh" && !terminal && !approval && !released
538
+ ? reading!.report!.activity
539
+ : "unknown"
540
+ let health: HealthState = input.state === "failed" ? "failing" : input.state === "exited" ?
541
+ input.exitCode === 0 ? "healthy" : input.exitCode == null ? "unknown" : "failing" :
542
+ terminal ?
543
+ "healthy" :
544
+ approval || released ?
545
+ "awaiting-human" :
546
+ waiting !== undefined ?
547
+ "healthy" :
548
+ input.state === "parked" && input.waitingReason === undefined ?
549
+ "awaiting-human" :
550
+ freshness !== "fresh"
551
+ ? "unknown"
552
+ : input.baseHealth ?? "unknown"
553
+ const reason = approval
554
+ ? "awaiting-reply"
555
+ : released
556
+ ? "released"
557
+ : waiting ?? (freshness === "fresh" ? reading?.report?.reason : reading?.reason)
558
+ if (!terminal && !approval && !released && waiting === undefined && freshness === "fresh") {
559
+ if (reason === "unreachable" && health !== "awaiting-human") health = "failing"
560
+ else if (reason === "no-progress" && health === "healthy") health = "stalled"
561
+ }
562
+ if (waiting !== undefined) activity = "unknown"
563
+ const attention: typeof Attention.Type = approval ? "awaiting-approval" : released ?
564
+ "needs-resume" :
565
+ activity === "needs-input" ?
566
+ "needs-input" :
567
+ ["stalled", "wedged-node", "runaway-loop", "failing"].includes(health)
568
+ ? "unhealthy"
569
+ : "none"
570
+ return {
571
+ subjectId: input.subjectId,
572
+ state: input.state,
573
+ activity,
574
+ health,
575
+ attention,
576
+ freshness,
577
+ ...(reason === undefined ? {} : { reason }),
578
+ ...(reading === undefined ? {} : {
579
+ provenance: {
580
+ checkerId: reading.checkerId,
581
+ monitorId: reading.monitorId,
582
+ observedAt: reading.observedAt,
583
+ expiresAt: reading.expiresAt,
584
+ evidenceSeq: reading.evidenceSeq,
585
+ incarnation: reading.incarnation,
586
+ version: latest!.sequence
587
+ }
588
+ }),
589
+ updatedAt: Math.max(input.updatedAt, reading?.observedAt ?? 0)
590
+ }
591
+ }