@hotmeshio/long-tail 0.26.0 → 0.28.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (392) hide show
  1. package/README.md +9 -2
  2. package/build/api/settings.d.ts +4 -1
  3. package/build/api/settings.js +7 -1
  4. package/build/index.d.ts +4 -0
  5. package/build/index.js +35 -8
  6. package/build/lib/db/config-lock.d.ts +10 -0
  7. package/build/lib/db/config-lock.js +28 -0
  8. package/build/lib/db/schemas/041_machine_lines.sql +26 -0
  9. package/build/lib/events/browser-view.js +8 -2
  10. package/build/lib/events/nats.d.ts +11 -0
  11. package/build/lib/events/nats.js +33 -2
  12. package/build/lib/events/publish.d.ts +33 -0
  13. package/build/lib/events/publish.js +52 -1
  14. package/build/lib/machines/index.d.ts +1 -0
  15. package/build/lib/machines/index.js +1 -0
  16. package/build/lib/machines/line.d.ts +4 -1
  17. package/build/lib/machines/line.js +18 -1
  18. package/build/lib/machines/protocol/codec.d.ts +24 -0
  19. package/build/lib/machines/protocol/codec.js +10 -0
  20. package/build/lib/machines/protocol/constants.d.ts +1 -1
  21. package/build/lib/machines/protocol/constants.js +1 -1
  22. package/build/lib/machines/protocol/contract.js +12 -3
  23. package/build/lib/machines/site/edge-account.d.ts +11 -0
  24. package/build/lib/machines/site/edge-account.js +24 -0
  25. package/build/lib/machines/site/mosquitto-conf.d.ts +5 -4
  26. package/build/lib/machines/site/mosquitto-conf.js +31 -16
  27. package/build/lib/machines/site/site-conf.d.ts +16 -6
  28. package/build/lib/machines/site/site-conf.js +39 -13
  29. package/build/lib/machines/transport/callback-feed.d.ts +1 -0
  30. package/build/lib/machines/transport/callback-feed.js +4 -1
  31. package/build/lib/machines/transport/delivery.d.ts +3 -4
  32. package/build/lib/machines/transport/delivery.js +3 -8
  33. package/build/lib/machines/transport/index.d.ts +2 -0
  34. package/build/lib/machines/transport/index.js +2 -0
  35. package/build/lib/machines/transport/memory.d.ts +2 -0
  36. package/build/lib/machines/transport/memory.js +6 -0
  37. package/build/lib/machines/transport/mqtt-bus.d.ts +2 -0
  38. package/build/lib/machines/transport/mqtt-bus.js +4 -0
  39. package/build/lib/machines/transport/nats-bus.d.ts +8 -13
  40. package/build/lib/machines/transport/nats-bus.js +26 -43
  41. package/build/lib/machines/transport/nats-feed.d.ts +1 -0
  42. package/build/lib/machines/transport/nats-feed.js +6 -3
  43. package/build/lib/machines/transport/pubsub-feed.d.ts +18 -0
  44. package/build/lib/machines/transport/pubsub-feed.js +42 -0
  45. package/build/lib/machines/transport/stream-bus.d.ts +42 -0
  46. package/build/lib/machines/transport/stream-bus.js +61 -0
  47. package/build/lib/machines/transport/streams.d.ts +10 -14
  48. package/build/lib/machines/transport/streams.js +18 -61
  49. package/build/lib/messaging/config.d.ts +25 -0
  50. package/build/lib/messaging/config.js +55 -0
  51. package/build/lib/messaging/constants.d.ts +26 -0
  52. package/build/lib/messaging/constants.js +26 -0
  53. package/build/lib/messaging/index.d.ts +24 -0
  54. package/build/lib/messaging/index.js +65 -0
  55. package/build/lib/messaging/memory.d.ts +16 -0
  56. package/build/lib/messaging/memory.js +147 -0
  57. package/build/lib/messaging/nats/permissions.d.ts +13 -0
  58. package/build/lib/messaging/nats/permissions.js +22 -0
  59. package/build/lib/messaging/nats/provider.d.ts +30 -0
  60. package/build/lib/messaging/nats/provider.js +77 -0
  61. package/build/lib/messaging/nats/render-conf.d.ts +68 -0
  62. package/build/lib/messaging/nats/render-conf.js +58 -0
  63. package/build/lib/messaging/nats/streams.d.ts +26 -0
  64. package/build/lib/messaging/nats/streams.js +97 -0
  65. package/build/lib/messaging/subjects.d.ts +8 -0
  66. package/build/lib/messaging/subjects.js +25 -0
  67. package/build/modules/machines.d.ts +8 -0
  68. package/build/modules/machines.js +16 -0
  69. package/build/routes/index.js +2 -0
  70. package/build/routes/machines.d.ts +2 -0
  71. package/build/routes/machines.js +220 -0
  72. package/build/scripts/bambu-boat.d.ts +17 -0
  73. package/build/scripts/bambu-boat.js +134 -0
  74. package/build/scripts/bambu-print.d.ts +18 -0
  75. package/build/scripts/bambu-print.js +114 -0
  76. package/build/services/machines/commands.d.ts +5 -5
  77. package/build/services/machines/commands.js +3 -2
  78. package/build/services/machines/config.d.ts +8 -1
  79. package/build/services/machines/config.js +17 -7
  80. package/build/services/machines/contracts.d.ts +29 -2
  81. package/build/services/machines/contracts.js +12 -3
  82. package/build/services/machines/gateway/actions.d.ts +2 -1
  83. package/build/services/machines/gateway/admission.d.ts +15 -3
  84. package/build/services/machines/gateway/admission.js +29 -14
  85. package/build/services/machines/gateway/dispatcher.d.ts +19 -3
  86. package/build/services/machines/gateway/dispatcher.js +61 -13
  87. package/build/services/machines/gateway/executor.d.ts +18 -4
  88. package/build/services/machines/gateway/executor.js +99 -34
  89. package/build/services/machines/gateway/gateway.d.ts +22 -1
  90. package/build/services/machines/gateway/gateway.js +83 -28
  91. package/build/services/machines/gateway/index.d.ts +3 -0
  92. package/build/services/machines/gateway/index.js +3 -0
  93. package/build/services/machines/gateway/inspect.d.ts +17 -0
  94. package/build/services/machines/gateway/inspect.js +68 -0
  95. package/build/services/machines/gateway/lifecycle.d.ts +21 -0
  96. package/build/services/machines/gateway/lifecycle.js +89 -0
  97. package/build/services/machines/gateway/log.d.ts +8 -0
  98. package/build/services/machines/gateway/log.js +18 -0
  99. package/build/services/machines/gateway/pacing.d.ts +14 -0
  100. package/build/services/machines/gateway/pacing.js +27 -2
  101. package/build/services/machines/gateway/plan-escalation.js +21 -7
  102. package/build/services/machines/gateway/plan-machine.d.ts +5 -4
  103. package/build/services/machines/gateway/plan-machine.js +70 -19
  104. package/build/services/machines/gateway/project.d.ts +9 -0
  105. package/build/services/machines/gateway/project.js +55 -0
  106. package/build/services/machines/gateway/receipts.d.ts +3 -0
  107. package/build/services/machines/gateway/receipts.js +3 -1
  108. package/build/services/machines/gateway/reconcile.d.ts +2 -0
  109. package/build/services/machines/gateway/reconcile.js +16 -3
  110. package/build/services/machines/gateway/registration.js +1 -1
  111. package/build/services/machines/gateway/reply-waiter.d.ts +6 -3
  112. package/build/services/machines/gateway/reply-waiter.js +37 -16
  113. package/build/services/machines/gateway/state.d.ts +50 -4
  114. package/build/services/machines/gateway/state.js +77 -10
  115. package/build/services/machines/gateway/wire.d.ts +58 -0
  116. package/build/services/machines/gateway/wire.js +171 -0
  117. package/build/services/machines/index.d.ts +3 -0
  118. package/build/services/machines/index.js +2 -0
  119. package/build/services/machines/leadership.d.ts +17 -4
  120. package/build/services/machines/leadership.js +43 -18
  121. package/build/services/machines/line-index.d.ts +16 -0
  122. package/build/services/machines/line-index.js +39 -0
  123. package/build/services/machines/registry-sql.d.ts +31 -0
  124. package/build/services/machines/registry-sql.js +110 -0
  125. package/build/services/machines/registry-sync.d.ts +31 -0
  126. package/build/services/machines/registry-sync.js +81 -0
  127. package/build/services/machines/registry.d.ts +77 -0
  128. package/build/services/machines/registry.js +158 -0
  129. package/build/services/machines/rows.js +26 -2
  130. package/build/services/machines/select.d.ts +9 -2
  131. package/build/services/machines/select.js +20 -7
  132. package/build/services/machines/surface.d.ts +45 -5
  133. package/build/services/machines/surface.js +102 -16
  134. package/build/services/machines/testing/fake-long-tail.d.ts +1 -0
  135. package/build/services/machines/testing/fake-long-tail.js +6 -3
  136. package/build/start/adapters.js +11 -2
  137. package/build/start/apply-report.d.ts +1 -1
  138. package/build/start/apply-report.js +3 -25
  139. package/build/start/index.js +23 -5
  140. package/build/system/index.js +13 -0
  141. package/build/system/mcp-servers/admin/schemas.d.ts +22 -22
  142. package/build/system/mcp-servers/human-queue-schemas.d.ts +7 -7
  143. package/build/system/mcp-servers/machines-schemas.d.ts +35 -0
  144. package/build/system/mcp-servers/machines-schemas.js +35 -0
  145. package/build/system/mcp-servers/machines.d.ts +20 -0
  146. package/build/system/mcp-servers/machines.js +154 -0
  147. package/build/system/seed/tool-manifests-machines.d.ts +2 -0
  148. package/build/system/seed/tool-manifests-machines.js +88 -0
  149. package/build/tsconfig.tsbuildinfo +1 -1
  150. package/build/types/events.d.ts +6 -1
  151. package/build/types/index.d.ts +2 -0
  152. package/build/types/machines-view.d.ts +159 -0
  153. package/build/types/machines-view.js +7 -0
  154. package/build/types/machines.d.ts +6 -1
  155. package/build/types/messaging.d.ts +72 -0
  156. package/build/types/messaging.js +11 -0
  157. package/build/types/startup.d.ts +26 -1
  158. package/dashboard/dist/assets/{AdminDashboard-DlItqxFS.js → AdminDashboard-Bm7VG65H.js} +2 -2
  159. package/dashboard/dist/assets/{AdminDashboard-DlItqxFS.js.map → AdminDashboard-Bm7VG65H.js.map} +1 -1
  160. package/dashboard/dist/assets/{AgentConfigPage-BUmT4a7n.js → AgentConfigPage-aJF7Sdbc.js} +6 -6
  161. package/dashboard/dist/assets/{AgentConfigPage-BUmT4a7n.js.map → AgentConfigPage-aJF7Sdbc.js.map} +1 -1
  162. package/dashboard/dist/assets/{AgentDetailPage-Cww8rlBi.js → AgentDetailPage-GwwghhxT.js} +3 -3
  163. package/dashboard/dist/assets/{AgentDetailPage-Cww8rlBi.js.map → AgentDetailPage-GwwghhxT.js.map} +1 -1
  164. package/dashboard/dist/assets/AgentsPage-DVvCzZyg.js +2 -0
  165. package/dashboard/dist/assets/{AgentsPage-C9UuUKhr.js.map → AgentsPage-DVvCzZyg.js.map} +1 -1
  166. package/dashboard/dist/assets/AvailableEscalationsPage-Cxmcji_K.js +2 -0
  167. package/dashboard/dist/assets/{AvailableEscalationsPage-DrvfXz70.js.map → AvailableEscalationsPage-Cxmcji_K.js.map} +1 -1
  168. package/dashboard/dist/assets/{BotPicker-CDq9XRMp.js → BotPicker-CofRxk4L.js} +2 -2
  169. package/dashboard/dist/assets/{BotPicker-CDq9XRMp.js.map → BotPicker-CofRxk4L.js.map} +1 -1
  170. package/dashboard/dist/assets/{BulkAssignModal-jsvqtrEA.js → BulkAssignModal-BARWuz4L.js} +2 -2
  171. package/dashboard/dist/assets/{BulkAssignModal-jsvqtrEA.js.map → BulkAssignModal-BARWuz4L.js.map} +1 -1
  172. package/dashboard/dist/assets/{CapabilitiesPage-CSCrI6B1.js → CapabilitiesPage-BJAWVNEx.js} +2 -2
  173. package/dashboard/dist/assets/{CapabilitiesPage-CSCrI6B1.js.map → CapabilitiesPage-BJAWVNEx.js.map} +1 -1
  174. package/dashboard/dist/assets/{CollapsibleSection-uD9mn-CQ.js → CollapsibleSection-CtgoaG75.js} +2 -2
  175. package/dashboard/dist/assets/{CollapsibleSection-uD9mn-CQ.js.map → CollapsibleSection-CtgoaG75.js.map} +1 -1
  176. package/dashboard/dist/assets/{ConfirmDeleteModal-BRmx2hA4.js → ConfirmDeleteModal-BYXXBWs6.js} +2 -2
  177. package/dashboard/dist/assets/{ConfirmDeleteModal-BRmx2hA4.js.map → ConfirmDeleteModal-BYXXBWs6.js.map} +1 -1
  178. package/dashboard/dist/assets/{ConnectedAppsPage-DlMzE5a-.js → ConnectedAppsPage-D0JkzWa7.js} +2 -2
  179. package/dashboard/dist/assets/{ConnectedAppsPage-DlMzE5a-.js.map → ConnectedAppsPage-D0JkzWa7.js.map} +1 -1
  180. package/dashboard/dist/assets/{CountdownTimer-sRuM4eM5.js → CountdownTimer-BcFxdZYN.js} +2 -2
  181. package/dashboard/dist/assets/{CountdownTimer-sRuM4eM5.js.map → CountdownTimer-BcFxdZYN.js.map} +1 -1
  182. package/dashboard/dist/assets/{CredentialsPage-bLYydwqa.js → CredentialsPage-DPSY20rV.js} +2 -2
  183. package/dashboard/dist/assets/{CredentialsPage-bLYydwqa.js.map → CredentialsPage-DPSY20rV.js.map} +1 -1
  184. package/dashboard/dist/assets/{CronLabel-D_ATDL2N.js → CronLabel-Dg_Oew_Y.js} +2 -2
  185. package/dashboard/dist/assets/{CronLabel-D_ATDL2N.js.map → CronLabel-Dg_Oew_Y.js.map} +1 -1
  186. package/dashboard/dist/assets/{DropZone-u1JGfR9G.js → DropZone-Cl_Flb16.js} +2 -2
  187. package/dashboard/dist/assets/{DropZone-u1JGfR9G.js.map → DropZone-Cl_Flb16.js.map} +1 -1
  188. package/dashboard/dist/assets/{ElapsedCell-groSMfqB.js → ElapsedCell-Dm3G5fK2.js} +2 -2
  189. package/dashboard/dist/assets/{ElapsedCell-groSMfqB.js.map → ElapsedCell-Dm3G5fK2.js.map} +1 -1
  190. package/dashboard/dist/assets/{EntityTimelinePanel-D-F3_A9m.js → EntityTimelinePanel-CrEEP04e.js} +2 -2
  191. package/dashboard/dist/assets/{EntityTimelinePanel-D-F3_A9m.js.map → EntityTimelinePanel-CrEEP04e.js.map} +1 -1
  192. package/dashboard/dist/assets/{EscalationListSchemaPage-Bad6Jrmr.js → EscalationListSchemaPage-wOInXcjs.js} +3 -3
  193. package/dashboard/dist/assets/{EscalationListSchemaPage-Bad6Jrmr.js.map → EscalationListSchemaPage-wOInXcjs.js.map} +1 -1
  194. package/dashboard/dist/assets/{EscalationSchemaPage-BKi_npI9.js → EscalationSchemaPage-CrIj_g9d.js} +3 -3
  195. package/dashboard/dist/assets/{EscalationSchemaPage-BKi_npI9.js.map → EscalationSchemaPage-CrIj_g9d.js.map} +1 -1
  196. package/dashboard/dist/assets/{EscalationTimeline-DQUUm3b1.js → EscalationTimeline-CeAsl5IT.js} +2 -2
  197. package/dashboard/dist/assets/{EscalationTimeline-DQUUm3b1.js.map → EscalationTimeline-CeAsl5IT.js.map} +1 -1
  198. package/dashboard/dist/assets/{EscalationTitleSelect-3mhXh77a.js → EscalationTitleSelect-D8qpCYiy.js} +2 -2
  199. package/dashboard/dist/assets/{EscalationTitleSelect-3mhXh77a.js.map → EscalationTitleSelect-D8qpCYiy.js.map} +1 -1
  200. package/dashboard/dist/assets/{EscalationsOverview-Qaov-uML.js → EscalationsOverview-CvaqVPsr.js} +2 -2
  201. package/dashboard/dist/assets/{EscalationsOverview-Qaov-uML.js.map → EscalationsOverview-CvaqVPsr.js.map} +1 -1
  202. package/dashboard/dist/assets/{EventTable-V4RF_n58.js → EventTable-Be0JuEl6.js} +2 -2
  203. package/dashboard/dist/assets/{EventTable-V4RF_n58.js.map → EventTable-Be0JuEl6.js.map} +1 -1
  204. package/dashboard/dist/assets/FilterBar-BVyAjIgv.js +2 -0
  205. package/dashboard/dist/assets/FilterBar-BVyAjIgv.js.map +1 -0
  206. package/dashboard/dist/assets/{GraphInvokePage-Djslhf96.js → GraphInvokePage-D_QigJeD.js} +2 -2
  207. package/dashboard/dist/assets/{GraphInvokePage-Djslhf96.js.map → GraphInvokePage-D_QigJeD.js.map} +1 -1
  208. package/dashboard/dist/assets/{HomePage-EjcNxlKx.js → HomePage-C1haeuNy.js} +2 -2
  209. package/dashboard/dist/assets/{HomePage-EjcNxlKx.js.map → HomePage-C1haeuNy.js.map} +1 -1
  210. package/dashboard/dist/assets/LinePage-CWD5pv1g.js +5 -0
  211. package/dashboard/dist/assets/LinePage-CWD5pv1g.js.map +1 -0
  212. package/dashboard/dist/assets/LinesPage-NqChsKcO.js +3 -0
  213. package/dashboard/dist/assets/LinesPage-NqChsKcO.js.map +1 -0
  214. package/dashboard/dist/assets/{ListToolbar-RrEAePOX.js → ListToolbar-BZCNrTh6.js} +2 -2
  215. package/dashboard/dist/assets/{ListToolbar-RrEAePOX.js.map → ListToolbar-BZCNrTh6.js.map} +1 -1
  216. package/dashboard/dist/assets/{McpOverview-8gqiL3Vj.js → McpOverview-yJTYnh1z.js} +2 -2
  217. package/dashboard/dist/assets/{McpOverview-8gqiL3Vj.js.map → McpOverview-yJTYnh1z.js.map} +1 -1
  218. package/dashboard/dist/assets/{McpQueryDetailPage-COyn6Ak5.js → McpQueryDetailPage-ChuLpJ4u.js} +3 -3
  219. package/dashboard/dist/assets/{McpQueryDetailPage-COyn6Ak5.js.map → McpQueryDetailPage-ChuLpJ4u.js.map} +1 -1
  220. package/dashboard/dist/assets/{McpQueryPage-Dj6Qj72w.js → McpQueryPage-9S-v9soF.js} +2 -2
  221. package/dashboard/dist/assets/{McpQueryPage-Dj6Qj72w.js.map → McpQueryPage-9S-v9soF.js.map} +1 -1
  222. package/dashboard/dist/assets/McpRunDetailPage-BhrcfxD5.js +2 -0
  223. package/dashboard/dist/assets/{McpRunDetailPage-80fQn9iX.js.map → McpRunDetailPage-BhrcfxD5.js.map} +1 -1
  224. package/dashboard/dist/assets/{McpRunsPage-DaSsI-EM.js → McpRunsPage-C5_i8_Zx.js} +2 -2
  225. package/dashboard/dist/assets/{McpRunsPage-DaSsI-EM.js.map → McpRunsPage-C5_i8_Zx.js.map} +1 -1
  226. package/dashboard/dist/assets/{NamespacePill-C5epAgq6.js → NamespacePill-OTZD2hzt.js} +2 -2
  227. package/dashboard/dist/assets/{NamespacePill-C5epAgq6.js.map → NamespacePill-OTZD2hzt.js.map} +1 -1
  228. package/dashboard/dist/assets/{OperationsPage-BSu1o2gB.js → OperationsPage-C03CeU5P.js} +2 -2
  229. package/dashboard/dist/assets/{OperationsPage-BSu1o2gB.js.map → OperationsPage-C03CeU5P.js.map} +1 -1
  230. package/dashboard/dist/assets/{OperatorDashboard-HFI4WGIV.js → OperatorDashboard-BzC134Jt.js} +2 -2
  231. package/dashboard/dist/assets/{OperatorDashboard-HFI4WGIV.js.map → OperatorDashboard-BzC134Jt.js.map} +1 -1
  232. package/dashboard/dist/assets/{PageHeader-BSWIspNB.js → PageHeader-Hx8APPwE.js} +2 -2
  233. package/dashboard/dist/assets/{PageHeader-BSWIspNB.js.map → PageHeader-Hx8APPwE.js.map} +1 -1
  234. package/dashboard/dist/assets/{PageHeaderWithStats-DDjopQ-k.js → PageHeaderWithStats-BEQYYBJv.js} +2 -2
  235. package/dashboard/dist/assets/{PageHeaderWithStats-DDjopQ-k.js.map → PageHeaderWithStats-BEQYYBJv.js.map} +1 -1
  236. package/dashboard/dist/assets/{PersonaDetailPage-D0qlBuGi.js → PersonaDetailPage-D0mY9L6j.js} +2 -2
  237. package/dashboard/dist/assets/{PersonaDetailPage-D0qlBuGi.js.map → PersonaDetailPage-D0mY9L6j.js.map} +1 -1
  238. package/dashboard/dist/assets/{PersonasPage-BJClu-5H.js → PersonasPage-FFlm5Eug.js} +2 -2
  239. package/dashboard/dist/assets/{PersonasPage-BJClu-5H.js.map → PersonasPage-FFlm5Eug.js.map} +1 -1
  240. package/dashboard/dist/assets/{PortalPage-CucNZn_w.js → PortalPage-Cpc25uO-.js} +2 -2
  241. package/dashboard/dist/assets/{PortalPage-CucNZn_w.js.map → PortalPage-Cpc25uO-.js.map} +1 -1
  242. package/dashboard/dist/assets/{ProcessDetailPage-B9wMw0RV.js → ProcessDetailPage-Di2J7KwY.js} +2 -2
  243. package/dashboard/dist/assets/{ProcessDetailPage-B9wMw0RV.js.map → ProcessDetailPage-Di2J7KwY.js.map} +1 -1
  244. package/dashboard/dist/assets/{ProcessesListPage-B6k4RpmR.js → ProcessesListPage-BEXwU3Z0.js} +2 -2
  245. package/dashboard/dist/assets/{ProcessesListPage-B6k4RpmR.js.map → ProcessesListPage-BEXwU3Z0.js.map} +1 -1
  246. package/dashboard/dist/assets/{ResolverForm-ZVufJ4G-.js → ResolverForm-2ks-3jXC.js} +6 -6
  247. package/dashboard/dist/assets/{ResolverForm-ZVufJ4G-.js.map → ResolverForm-2ks-3jXC.js.map} +1 -1
  248. package/dashboard/dist/assets/{RoleDetailPage-CTbq0mz4.js → RoleDetailPage-DqoOk0xc.js} +3 -3
  249. package/dashboard/dist/assets/{RoleDetailPage-CTbq0mz4.js.map → RoleDetailPage-DqoOk0xc.js.map} +1 -1
  250. package/dashboard/dist/assets/{RolePill-CoETDpjF.js → RolePill-R5_ctGFy.js} +2 -2
  251. package/dashboard/dist/assets/{RolePill-CoETDpjF.js.map → RolePill-R5_ctGFy.js.map} +1 -1
  252. package/dashboard/dist/assets/{RolesPage-DBKxeam4.js → RolesPage-CPmo8Srr.js} +2 -2
  253. package/dashboard/dist/assets/{RolesPage-DBKxeam4.js.map → RolesPage-CPmo8Srr.js.map} +1 -1
  254. package/dashboard/dist/assets/{RosterWidget-BkLAwEYB.js → RosterWidget-D2yovCKw.js} +2 -2
  255. package/dashboard/dist/assets/{RosterWidget-BkLAwEYB.js.map → RosterWidget-D2yovCKw.js.map} +1 -1
  256. package/dashboard/dist/assets/{RunAsSelector-D2DC4iDf.js → RunAsSelector-CMv96On6.js} +2 -2
  257. package/dashboard/dist/assets/{RunAsSelector-D2DC4iDf.js.map → RunAsSelector-CMv96On6.js.map} +1 -1
  258. package/dashboard/dist/assets/{ScanCodesPage-BlKleaT0.js → ScanCodesPage-CA6cTT-M.js} +2 -2
  259. package/dashboard/dist/assets/{ScanCodesPage-BlKleaT0.js.map → ScanCodesPage-CA6cTT-M.js.map} +1 -1
  260. package/dashboard/dist/assets/{ScanSchemeDetailPage-CTW6gofi.js → ScanSchemeDetailPage-DnctN5-H.js} +2 -2
  261. package/dashboard/dist/assets/{ScanSchemeDetailPage-CTW6gofi.js.map → ScanSchemeDetailPage-DnctN5-H.js.map} +1 -1
  262. package/dashboard/dist/assets/{ScanStationPage-DPRjSPyT.js → ScanStationPage-Bk8RwkKL.js} +2 -2
  263. package/dashboard/dist/assets/{ScanStationPage-DPRjSPyT.js.map → ScanStationPage-Bk8RwkKL.js.map} +1 -1
  264. package/dashboard/dist/assets/{ScopeBadge-CGsTtqFr.js → ScopeBadge-BXZ084nD.js} +2 -2
  265. package/dashboard/dist/assets/{ScopeBadge-CGsTtqFr.js.map → ScopeBadge-BXZ084nD.js.map} +1 -1
  266. package/dashboard/dist/assets/{StickyPagination-CkmX3-Gu.js → StickyPagination-B0FSuUgs.js} +2 -2
  267. package/dashboard/dist/assets/{StickyPagination-CkmX3-Gu.js.map → StickyPagination-B0FSuUgs.js.map} +1 -1
  268. package/dashboard/dist/assets/{StreamMessageDetail-Xm_n510s.js → StreamMessageDetail-BwZEvMpP.js} +2 -2
  269. package/dashboard/dist/assets/{StreamMessageDetail-Xm_n510s.js.map → StreamMessageDetail-BwZEvMpP.js.map} +1 -1
  270. package/dashboard/dist/assets/{SwimlaneTimeline-De48aJan.js → SwimlaneTimeline-BDVQEaMw.js} +2 -2
  271. package/dashboard/dist/assets/{SwimlaneTimeline-De48aJan.js.map → SwimlaneTimeline-BDVQEaMw.js.map} +1 -1
  272. package/dashboard/dist/assets/{TagInput-S7UuVT_M.js → TagInput-DjPUDp2M.js} +2 -2
  273. package/dashboard/dist/assets/{TagInput-S7UuVT_M.js.map → TagInput-DjPUDp2M.js.map} +1 -1
  274. package/dashboard/dist/assets/{TaskDetailPage-HycMx3O9.js → TaskDetailPage-BOlv3iGB.js} +2 -2
  275. package/dashboard/dist/assets/{TaskDetailPage-HycMx3O9.js.map → TaskDetailPage-BOlv3iGB.js.map} +1 -1
  276. package/dashboard/dist/assets/{TaskQueuePill-BlHNpYiZ.js → TaskQueuePill-D-4Ynf3K.js} +2 -2
  277. package/dashboard/dist/assets/{TaskQueuePill-BlHNpYiZ.js.map → TaskQueuePill-D-4Ynf3K.js.map} +1 -1
  278. package/dashboard/dist/assets/{TasksListPage-OIcFFp1C.js → TasksListPage-e2teY0bD.js} +2 -2
  279. package/dashboard/dist/assets/{TasksListPage-OIcFFp1C.js.map → TasksListPage-e2teY0bD.js.map} +1 -1
  280. package/dashboard/dist/assets/{TimeAgo-OTC_55oZ.js → TimeAgo-Cvkpf_b3.js} +2 -2
  281. package/dashboard/dist/assets/{TimeAgo-OTC_55oZ.js.map → TimeAgo-Cvkpf_b3.js.map} +1 -1
  282. package/dashboard/dist/assets/{TimestampCell-CAwIj_AI.js → TimestampCell-B3btAV0N.js} +2 -2
  283. package/dashboard/dist/assets/{TimestampCell-CAwIj_AI.js.map → TimestampCell-B3btAV0N.js.map} +1 -1
  284. package/dashboard/dist/assets/{ToolPill-D40uLM_v.js → ToolPill-GsKQ80CO.js} +2 -2
  285. package/dashboard/dist/assets/{ToolPill-D40uLM_v.js.map → ToolPill-GsKQ80CO.js.map} +1 -1
  286. package/dashboard/dist/assets/{ToolTestPanel-8Vf6pfw4.js → ToolTestPanel-B03nebR7.js} +2 -2
  287. package/dashboard/dist/assets/{ToolTestPanel-8Vf6pfw4.js.map → ToolTestPanel-B03nebR7.js.map} +1 -1
  288. package/dashboard/dist/assets/{TopicDetailPage-iZW8rK9L.js → TopicDetailPage-DBRWne3O.js} +3 -3
  289. package/dashboard/dist/assets/{TopicDetailPage-iZW8rK9L.js.map → TopicDetailPage-DBRWne3O.js.map} +1 -1
  290. package/dashboard/dist/assets/{TopicsPage-CqiEcj-W.js → TopicsPage-CVe2M9HD.js} +2 -2
  291. package/dashboard/dist/assets/{TopicsPage-CqiEcj-W.js.map → TopicsPage-CVe2M9HD.js.map} +1 -1
  292. package/dashboard/dist/assets/{UserName-DQFjuoib.js → UserName-B_q_qp6V.js} +2 -2
  293. package/dashboard/dist/assets/{UserName-DQFjuoib.js.map → UserName-B_q_qp6V.js.map} +1 -1
  294. package/dashboard/dist/assets/WorkflowExecutionPage-Bh_dr9tM.js +2 -0
  295. package/dashboard/dist/assets/{WorkflowExecutionPage-Di08wCbs.js.map → WorkflowExecutionPage-Bh_dr9tM.js.map} +1 -1
  296. package/dashboard/dist/assets/{WorkflowPill-DJLcK_CM.js → WorkflowPill-D7244fKp.js} +2 -2
  297. package/dashboard/dist/assets/{WorkflowPill-DJLcK_CM.js.map → WorkflowPill-D7244fKp.js.map} +1 -1
  298. package/dashboard/dist/assets/{WorkflowsDashboard-Bb8HlrTJ.js → WorkflowsDashboard-DA6Mj7qH.js} +2 -2
  299. package/dashboard/dist/assets/{WorkflowsDashboard-Bb8HlrTJ.js.map → WorkflowsDashboard-DA6Mj7qH.js.map} +1 -1
  300. package/dashboard/dist/assets/{WorkflowsOverview-Dta1kurk.js → WorkflowsOverview-B1kBLj-a.js} +2 -2
  301. package/dashboard/dist/assets/{WorkflowsOverview-Dta1kurk.js.map → WorkflowsOverview-B1kBLj-a.js.map} +1 -1
  302. package/dashboard/dist/assets/{YamlWorkflowDetailPage-CfAajg68.js → YamlWorkflowDetailPage-6GJM8QIX.js} +8 -8
  303. package/dashboard/dist/assets/{YamlWorkflowDetailPage-CfAajg68.js.map → YamlWorkflowDetailPage-6GJM8QIX.js.map} +1 -1
  304. package/dashboard/dist/assets/{YamlWorkflowsPage-CQFUzT4t.js → YamlWorkflowsPage-Bu811C7f.js} +2 -2
  305. package/dashboard/dist/assets/{YamlWorkflowsPage-CQFUzT4t.js.map → YamlWorkflowsPage-Bu811C7f.js.map} +1 -1
  306. package/dashboard/dist/assets/{agents-PuftRTB2.js → agents-MPk_GnYz.js} +2 -2
  307. package/dashboard/dist/assets/{agents-PuftRTB2.js.map → agents-MPk_GnYz.js.map} +1 -1
  308. package/dashboard/dist/assets/{bots-BmEiPAWY.js → bots-2RihE2Tc.js} +2 -2
  309. package/dashboard/dist/assets/{bots-BmEiPAWY.js.map → bots-2RihE2Tc.js.map} +1 -1
  310. package/dashboard/dist/assets/{capabilities-CRii4Soa.js → capabilities-CC4iwJ1P.js} +2 -2
  311. package/dashboard/dist/assets/{capabilities-CRii4Soa.js.map → capabilities-CC4iwJ1P.js.map} +1 -1
  312. package/dashboard/dist/assets/{controlplane-DO_czaYo.js → controlplane-Bmkic8BH.js} +2 -2
  313. package/dashboard/dist/assets/{controlplane-DO_czaYo.js.map → controlplane-Bmkic8BH.js.map} +1 -1
  314. package/dashboard/dist/assets/{escalation-columns-CzCquCPh.js → escalation-columns-B0lhUUrQ.js} +2 -2
  315. package/dashboard/dist/assets/{escalation-columns-CzCquCPh.js.map → escalation-columns-B0lhUUrQ.js.map} +1 -1
  316. package/dashboard/dist/assets/{escalation-items-Cy0ZgJqV.js → escalation-items-7WFpPo46.js} +2 -2
  317. package/dashboard/dist/assets/{escalation-items-Cy0ZgJqV.js.map → escalation-items-7WFpPo46.js.map} +1 -1
  318. package/dashboard/dist/assets/index-B9uv-euR.css +1 -0
  319. package/dashboard/dist/assets/{index-DhSCCpqM.js → index-B_XV1rwu.js} +2 -2
  320. package/dashboard/dist/assets/{index-DhSCCpqM.js.map → index-B_XV1rwu.js.map} +1 -1
  321. package/dashboard/dist/assets/{index-Cx8xb-oU.js → index-C2CLfGwL.js} +2 -2
  322. package/dashboard/dist/assets/{index-Cx8xb-oU.js.map → index-C2CLfGwL.js.map} +1 -1
  323. package/dashboard/dist/assets/{index-Cga5IW3D.js → index-C3AeFihz.js} +2 -2
  324. package/dashboard/dist/assets/{index-Cga5IW3D.js.map → index-C3AeFihz.js.map} +1 -1
  325. package/dashboard/dist/assets/{index-DiaRlFYI.js → index-CBri8fYM.js} +2 -2
  326. package/dashboard/dist/assets/{index-DiaRlFYI.js.map → index-CBri8fYM.js.map} +1 -1
  327. package/dashboard/dist/assets/{index-CL_1VpGb.js → index-CRWsB7ZC.js} +2 -2
  328. package/dashboard/dist/assets/{index-CL_1VpGb.js.map → index-CRWsB7ZC.js.map} +1 -1
  329. package/dashboard/dist/assets/{index-XntldBd0.js → index-CTOv3G7a.js} +2 -2
  330. package/dashboard/dist/assets/{index-XntldBd0.js.map → index-CTOv3G7a.js.map} +1 -1
  331. package/dashboard/dist/assets/{index-CfP2nKRc.js → index-CcArGkzI.js} +3 -3
  332. package/dashboard/dist/assets/{index-CfP2nKRc.js.map → index-CcArGkzI.js.map} +1 -1
  333. package/dashboard/dist/assets/{index-Dz6R8-Dw.js → index-CkMHS-hG.js} +25 -25
  334. package/dashboard/dist/assets/index-CkMHS-hG.js.map +1 -0
  335. package/dashboard/dist/assets/index-CmsmwCyv.js +2 -0
  336. package/dashboard/dist/assets/{index-4YaDUcKd.js.map → index-CmsmwCyv.js.map} +1 -1
  337. package/dashboard/dist/assets/{index-2SM2_onB.js → index-CvJsvqIc.js} +2 -2
  338. package/dashboard/dist/assets/{index-2SM2_onB.js.map → index-CvJsvqIc.js.map} +1 -1
  339. package/dashboard/dist/assets/{index-Rwqrflm-.js → index-D-ilRyfy.js} +2 -2
  340. package/dashboard/dist/assets/{index-Rwqrflm-.js.map → index-D-ilRyfy.js.map} +1 -1
  341. package/dashboard/dist/assets/{index-CZ7A3AcD.js → index-DSUtfDXx.js} +2 -2
  342. package/dashboard/dist/assets/{index-CZ7A3AcD.js.map → index-DSUtfDXx.js.map} +1 -1
  343. package/dashboard/dist/assets/{index-jL2K6us4.js → index-Da6ycKyf.js} +2 -2
  344. package/dashboard/dist/assets/{index-jL2K6us4.js.map → index-Da6ycKyf.js.map} +1 -1
  345. package/dashboard/dist/assets/{knowledge-CeUqGPuo.js → knowledge-Bpbg20DC.js} +2 -2
  346. package/dashboard/dist/assets/{knowledge-CeUqGPuo.js.map → knowledge-Bpbg20DC.js.map} +1 -1
  347. package/dashboard/dist/assets/machines-BqOZZU-3.js +2 -0
  348. package/dashboard/dist/assets/machines-BqOZZU-3.js.map +1 -0
  349. package/dashboard/dist/assets/{mcp-Cx96NTeD.js → mcp-CmcUXYce.js} +2 -2
  350. package/dashboard/dist/assets/{mcp-Cx96NTeD.js.map → mcp-CmcUXYce.js.map} +1 -1
  351. package/dashboard/dist/assets/{mcp-query-XAzWl0gm.js → mcp-query-C8buQxN9.js} +2 -2
  352. package/dashboard/dist/assets/{mcp-query-XAzWl0gm.js.map → mcp-query-C8buQxN9.js.map} +1 -1
  353. package/dashboard/dist/assets/{personas--4kjwyOw.js → personas-C2n5oDmY.js} +2 -2
  354. package/dashboard/dist/assets/{personas--4kjwyOw.js.map → personas-C2n5oDmY.js.map} +1 -1
  355. package/dashboard/dist/assets/{pipelines-PgkwvG7f.js → pipelines-RKpSkFz7.js} +2 -2
  356. package/dashboard/dist/assets/{pipelines-PgkwvG7f.js.map → pipelines-RKpSkFz7.js.map} +1 -1
  357. package/dashboard/dist/assets/{tasks-CcYxdBa3.js → tasks-Dab4hExW.js} +2 -2
  358. package/dashboard/dist/assets/{tasks-CcYxdBa3.js.map → tasks-Dab4hExW.js.map} +1 -1
  359. package/dashboard/dist/assets/{topics-_lik7cNY.js → topics-FTMNP7r3.js} +2 -2
  360. package/dashboard/dist/assets/{topics-_lik7cNY.js.map → topics-FTMNP7r3.js.map} +1 -1
  361. package/dashboard/dist/assets/{useDebouncedValue-Bm_AoxVx.js → useDebouncedValue-B_1u1YIk.js} +2 -2
  362. package/dashboard/dist/assets/{useDebouncedValue-Bm_AoxVx.js.map → useDebouncedValue-B_1u1YIk.js.map} +1 -1
  363. package/dashboard/dist/assets/{useNamespace-DgXXKdx5.js → useNamespace-HUeAI7Qb.js} +2 -2
  364. package/dashboard/dist/assets/{useNamespace-DgXXKdx5.js.map → useNamespace-HUeAI7Qb.js.map} +1 -1
  365. package/dashboard/dist/assets/{useYamlActivityEvents-Dj2qtGSd.js → useYamlActivityEvents-Cax54qHH.js} +2 -2
  366. package/dashboard/dist/assets/{useYamlActivityEvents-Dj2qtGSd.js.map → useYamlActivityEvents-Cax54qHH.js.map} +1 -1
  367. package/dashboard/dist/assets/{validate-FlXCBBT6.js → validate-DxOaV7QS.js} +2 -2
  368. package/dashboard/dist/assets/{validate-FlXCBBT6.js.map → validate-DxOaV7QS.js.map} +1 -1
  369. package/dashboard/dist/assets/{vendor-icons-DUYcKTjP.js → vendor-icons-fxBvvHBi.js} +187 -182
  370. package/dashboard/dist/assets/vendor-icons-fxBvvHBi.js.map +1 -0
  371. package/dashboard/dist/assets/{yaml-workflows-B0_JxRYQ.js → yaml-workflows-BgY1XHJx.js} +2 -2
  372. package/dashboard/dist/assets/{yaml-workflows-B0_JxRYQ.js.map → yaml-workflows-BgY1XHJx.js.map} +1 -1
  373. package/dashboard/dist/index.html +3 -3
  374. package/docs/architecture.md +1 -0
  375. package/docs/code-owned-configuration.md +6 -0
  376. package/docs/dashboard.md +1 -0
  377. package/docs/events.md +83 -4
  378. package/docs/machines.md +129 -23
  379. package/docs/mcp.md +19 -0
  380. package/package.json +3 -1
  381. package/build/lib/machines/transport/nats-durable.d.ts +0 -11
  382. package/build/lib/machines/transport/nats-durable.js +0 -28
  383. package/dashboard/dist/assets/AgentsPage-C9UuUKhr.js +0 -2
  384. package/dashboard/dist/assets/AvailableEscalationsPage-DrvfXz70.js +0 -2
  385. package/dashboard/dist/assets/FilterBar-BygWrnB1.js +0 -2
  386. package/dashboard/dist/assets/FilterBar-BygWrnB1.js.map +0 -1
  387. package/dashboard/dist/assets/McpRunDetailPage-80fQn9iX.js +0 -2
  388. package/dashboard/dist/assets/WorkflowExecutionPage-Di08wCbs.js +0 -2
  389. package/dashboard/dist/assets/index-4YaDUcKd.js +0 -2
  390. package/dashboard/dist/assets/index-CnU7Jjz3.css +0 -1
  391. package/dashboard/dist/assets/index-Dz6R8-Dw.js.map +0 -1
  392. package/dashboard/dist/assets/vendor-icons-DUYcKTjP.js.map +0 -1
@@ -0,0 +1,68 @@
1
+ "use strict";
2
+ /**
3
+ * A dry run of one wire message: what the codec reads and what the gateway
4
+ * would do, planned on a copy of the state so the line is untouched and no
5
+ * row is created or changed.
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.dryRun = dryRun;
9
+ const plan_machine_1 = require("./plan-machine");
10
+ const registration_1 = require("./registration");
11
+ const wire_1 = require("./wire");
12
+ /** What admitting the sender would do, or null when the message would be acted on. */
13
+ function admission(state, line, raw) {
14
+ const sender = (0, registration_1.resolveSender)(state, line, raw);
15
+ if (sender.status === registration_1.SENDER.BOUND)
16
+ return { seen: sender.seen, actions: [{ verb: 'ack', detail: `bind ${sender.machineId} to ${sender.address}` }] };
17
+ if (sender.seen)
18
+ return { seen: sender.seen, actions: [] };
19
+ if (sender.status === registration_1.SENDER.UNKNOWN)
20
+ return { seen: null, actions: [{ verb: 'none', detail: `${sender.address} claims no station on this line` }] };
21
+ const raised = state.claimRaised(sender.address, sender.machineId);
22
+ return {
23
+ seen: null,
24
+ actions: [raised
25
+ ? { verb: 'none', detail: `${sender.address} says it is ${sender.machineId}; waiting for a person` }
26
+ : { verb: 'create-row', detail: `${sender.address} says it is ${sender.machineId}; a person decides` }],
27
+ };
28
+ }
29
+ function planOne(ctx, state, raw) {
30
+ const admitted = admission(state, ctx.line, raw);
31
+ if (!admitted.seen)
32
+ return admitted.actions;
33
+ if (raw.violations.length && ctx.codec.strict)
34
+ return [...admitted.actions, { verb: 'hold', detail: raw.violations.join('; ') }];
35
+ try {
36
+ const plan = (0, plan_machine_1.planMachineMessage)(state, admitted.seen.parsed, admitted.seen.body, ctx.dispatchTtlMs);
37
+ const actions = [...admitted.actions, ...plan.actions.map(wire_1.plannedAction)];
38
+ return (0, wire_1.appliedActions)({ disposition: plan.disposition, failed: false, actions }, 1);
39
+ }
40
+ catch (err) {
41
+ return [...admitted.actions, { verb: 'unreadable', detail: err.message }];
42
+ }
43
+ }
44
+ function dryRun(ctx, msg) {
45
+ let decoded;
46
+ try {
47
+ decoded = ctx.codec.decode(msg.topic, msg.payload, msg.retained);
48
+ }
49
+ catch (err) {
50
+ return { kind: null, event: null, summary: err.message, inspections: [], violations: [], actions: [{ verb: 'unreadable', detail: err.message }] };
51
+ }
52
+ if (!decoded.length) {
53
+ return { kind: null, event: null, summary: 'not a machine message', inspections: [], violations: [], actions: [{ verb: 'none', detail: 'not a machine message' }] };
54
+ }
55
+ const state = ctx.state.clone();
56
+ const actions = [];
57
+ const violations = [];
58
+ for (const raw of decoded) {
59
+ violations.push(...raw.violations);
60
+ if (raw.parsed)
61
+ actions.push(...planOne(ctx, state, raw));
62
+ }
63
+ const [first] = decoded;
64
+ return {
65
+ kind: first.parsed?.kind ?? null, event: (0, wire_1.eventOf)(first), summary: (0, wire_1.summarize)(first),
66
+ inspections: decoded, violations, actions: actions.length ? actions : [{ verb: 'unreadable', detail: 'unknown topic' }],
67
+ };
68
+ }
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Job lifecycle on the row. A machine's started and progress events, and the
3
+ * alarms that stop it, are noted as items on the open row that holds the job:
4
+ * the accepted dispatch into the machine until its units arrive, then the
5
+ * handoff out of it. Progress is noted only when it crosses a step, or reaches
6
+ * a named stage it has not reached before, so a row carries a handful of items
7
+ * while the stream keeps every report.
8
+ *
9
+ * Off unless the line turns it on (MACHINE_LIFECYCLE=on). A row parked with a
10
+ * limit on its items gets no notes, so they never use a slot its units need.
11
+ */
12
+ import type { Action } from './actions';
13
+ import { type AlarmTrack, type GatewayState } from './state';
14
+ /** The progress step `percent` has reached; null below the first step, at 100, or when steps are off. */
15
+ export declare function progressStepOf(percent: unknown, step: number): number | null;
16
+ /** A started or progress event, noted on the row that holds the job. */
17
+ export declare function noteJobEvent(state: GatewayState, m: string, b: Record<string, any>, at: string | undefined): Action[];
18
+ /** Rows a stopping alarm pauses: those running or leaving with its job, or every row the machine holds. */
19
+ export declare function notePause(state: GatewayState, track: AlarmTrack, b: Record<string, any>): Action[];
20
+ /** The machine cleared an alarm: the rows it paused, still open, note the resume. */
21
+ export declare function noteResume(state: GatewayState, track: AlarmTrack, b: Record<string, any>): Action[];
@@ -0,0 +1,89 @@
1
+ "use strict";
2
+ /**
3
+ * Job lifecycle on the row. A machine's started and progress events, and the
4
+ * alarms that stop it, are noted as items on the open row that holds the job:
5
+ * the accepted dispatch into the machine until its units arrive, then the
6
+ * handoff out of it. Progress is noted only when it crosses a step, or reaches
7
+ * a named stage it has not reached before, so a row carries a handful of items
8
+ * while the stream keeps every report.
9
+ *
10
+ * Off unless the line turns it on (MACHINE_LIFECYCLE=on). A row parked with a
11
+ * limit on its items gets no notes, so they never use a slot its units need.
12
+ */
13
+ Object.defineProperty(exports, "__esModule", { value: true });
14
+ exports.progressStepOf = progressStepOf;
15
+ exports.noteJobEvent = noteJobEvent;
16
+ exports.notePause = notePause;
17
+ exports.noteResume = noteResume;
18
+ const contracts_1 = require("../contracts");
19
+ const protocol_1 = require("../../../lib/machines/protocol");
20
+ const jobs_1 = require("./jobs");
21
+ const state_1 = require("./state");
22
+ const owns = (jobId, reported) => !jobId || typeof reported !== 'string' || (0, jobs_1.jobOwns)(jobId, reported);
23
+ const runningAt = (r, m, jobId) => r.toMachine === m && r.dispatch === state_1.DISPATCH_STATE.ACCEPTED && owns(r.jobId, jobId);
24
+ const leavingFrom = (r, m, jobId) => r.fromMachine === m && !!r.senderJobId && owns(r.senderJobId, jobId);
25
+ /** The open row holding `unitId` at `m`: its accepted dispatch, else the handoff out of `m`. */
26
+ function holderRow(state, m, unitId, jobId) {
27
+ return state.openFor(unitId, (r) => runningAt(r, m, jobId)) ?? state.openFor(unitId, (r) => leavingFrom(r, m, jobId));
28
+ }
29
+ /** The progress step `percent` has reached; null below the first step, at 100, or when steps are off. */
30
+ function progressStepOf(percent, step) {
31
+ if (step <= 0 || typeof percent !== 'number' || !Number.isFinite(percent))
32
+ return null;
33
+ const reached = Math.floor(percent / step) * step;
34
+ return reached > 0 && reached < 100 ? reached : null;
35
+ }
36
+ /** An item the row does not hold yet, added as the machine. */
37
+ function note(row, m, itemKey, payload, metadata) {
38
+ if (row.capped || row.noted?.includes(itemKey))
39
+ return [];
40
+ row.noted = [...(row.noted ?? []), itemKey];
41
+ return [{ kind: 'accumulate', as: m, rowId: row.id, itemKey, payload, ...(metadata ? { metadata } : {}) }];
42
+ }
43
+ /** A started or progress event, noted on the row that holds the job. */
44
+ function noteJobEvent(state, m, b, at) {
45
+ const unitId = b.unitId;
46
+ if (!state.lifecycle || typeof unitId !== 'string')
47
+ return [];
48
+ const row = holderRow(state, m, unitId, b.jobId);
49
+ if (!row)
50
+ return [];
51
+ const when = at ?? new Date().toISOString();
52
+ if (b.event === protocol_1.JOB_EVENT.STARTED) {
53
+ return note(row, m, contracts_1.itemKeys.started(unitId), { unitId, jobId: b.jobId ?? null, at: when });
54
+ }
55
+ const step = progressStepOf(b.percent, state.progressStep);
56
+ const stage = typeof b.stage === 'string' && b.stage ? b.stage : null;
57
+ if (step === null && stage === null)
58
+ return [];
59
+ const progress = {
60
+ ...(typeof b.percent === 'number' ? { percent: b.percent } : {}), remainingSec: b.remainingSec ?? null, ...(stage ? { stage } : {}), at: when,
61
+ ...(b.detail && typeof b.detail === 'object' ? { detail: b.detail } : {}),
62
+ };
63
+ const facet = { [contracts_1.LIFECYCLE_FACET.PROGRESS]: progress };
64
+ const job = { unitId, jobId: b.jobId ?? null };
65
+ return [
66
+ ...(step === null ? [] : note(row, m, contracts_1.itemKeys.progress(unitId, step), { ...job, step, ...progress }, facet)),
67
+ ...(stage === null ? [] : note(row, m, contracts_1.itemKeys.stage(unitId, stage), { ...job, ...progress }, facet)),
68
+ ];
69
+ }
70
+ /** Rows a stopping alarm pauses: those running or leaving with its job, or every row the machine holds. */
71
+ function notePause(state, track, b) {
72
+ if (!state.lifecycle)
73
+ return [];
74
+ const m = track.machineId;
75
+ const jobId = typeof b.jobId === 'string' ? b.jobId : undefined;
76
+ const rows = state.open().filter((r) => runningAt(r, m, jobId) || leavingFrom(r, m, jobId));
77
+ track.noted = rows.map((r) => r.id);
78
+ const payload = { alarmId: track.alarmId, code: b.code ?? null, level: b.level ?? null, text: b.text ?? null, raisedAt: b.raisedAt ?? null };
79
+ return rows.flatMap((r) => note(r, m, contracts_1.itemKeys.paused(track.alarmId, String(b.raisedAt ?? '')), payload));
80
+ }
81
+ /** The machine cleared an alarm: the rows it paused, still open, note the resume. */
82
+ function noteResume(state, track, b) {
83
+ const key = contracts_1.itemKeys.resumed(track.alarmId, String(track.raisedAt ?? ''));
84
+ const payload = { alarmId: track.alarmId, clearedAt: b.clearedAt ?? new Date().toISOString(), clearedBy: b.clearedBy ?? null };
85
+ return (track.noted ?? [])
86
+ .map((id) => state.get(id))
87
+ .filter((r) => !!r && !r.ended)
88
+ .flatMap((r) => note(r, track.machineId, key, payload));
89
+ }
@@ -1,3 +1,4 @@
1
+ import { publishMachineLogEvent } from '../../../lib/events/publish';
1
2
  export declare const GATEWAY_LOG: {
2
3
  readonly STARTED: "machine_gateway.started";
3
4
  readonly RECONCILED: "machine_gateway.reconciled";
@@ -39,3 +40,10 @@ export interface GatewayLog {
39
40
  }
40
41
  export declare const consoleLog: GatewayLog;
41
42
  export declare const silentLog: GatewayLog;
43
+ /** Each entry published as `machine.log.<site>.<line>.<event>` with its context, for the line's dashboard view. */
44
+ export declare function eventsLog(line: {
45
+ siteId: string;
46
+ lineId: string;
47
+ }, publish?: typeof publishMachineLogEvent): GatewayLog;
48
+ /** Every entry goes to each log in turn. */
49
+ export declare function composeLogs(...logs: GatewayLog[]): GatewayLog;
@@ -1,6 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.silentLog = exports.consoleLog = exports.GATEWAY_LOG = void 0;
4
+ exports.eventsLog = eventsLog;
5
+ exports.composeLogs = composeLogs;
6
+ const publish_1 = require("../../../lib/events/publish");
4
7
  exports.GATEWAY_LOG = {
5
8
  STARTED: 'machine_gateway.started',
6
9
  RECONCILED: 'machine_gateway.reconciled',
@@ -40,3 +43,18 @@ exports.consoleLog = {
40
43
  error: (event, context) => console.error(JSON.stringify({ level: 'error', event, ...context })),
41
44
  };
42
45
  exports.silentLog = { info: () => undefined, warn: () => undefined, error: () => undefined };
46
+ /** Each entry published as `machine.log.<site>.<line>.<event>` with its context, for the line's dashboard view. */
47
+ function eventsLog(line, publish = publish_1.publishMachineLogEvent) {
48
+ const emit = (level) => (event, context) => {
49
+ publish({ siteId: line.siteId, lineId: line.lineId, level, event, context }).catch(() => undefined);
50
+ };
51
+ return { info: emit('info'), warn: emit('warn'), error: emit('error') };
52
+ }
53
+ /** Every entry goes to each log in turn. */
54
+ function composeLogs(...logs) {
55
+ const each = (level) => (event, context) => {
56
+ for (const log of logs)
57
+ log[level](event, context);
58
+ };
59
+ return { info: each('info'), warn: each('warn'), error: each('error') };
60
+ }
@@ -11,6 +11,20 @@ import { type Action } from './actions';
11
11
  import { type GatewayState, type HandoffRow } from './state';
12
12
  /** Dispatch arguments for a handoff row. */
13
13
  export declare function dispatchArgs(row: HandoffRow, expiresAt: number): Record<string, any>;
14
+ /**
15
+ * The validity round a dispatch belongs to. Rounds of one validity period count
16
+ * from when the dispatch became due, and a round begins only when the one
17
+ * before it has expired, so a machine never holds two valid commands for one
18
+ * row. Within a round a dispatch sent again, by a retry, a restarted machine or
19
+ * a restarted gateway, is the same command with the same arguments; a new round
20
+ * is a new command, never an old one with changed arguments.
21
+ */
22
+ export declare function dispatchWindow(dueAt: number, now: number, ttlMs: number): {
23
+ round: number;
24
+ expiresAt: number;
25
+ };
26
+ /** The later of a row's due time and an arrival's time; an unreadable time leaves it as it was. */
27
+ export declare function latestArrival(dueAt: number | undefined, at: unknown): number | undefined;
14
28
  export declare function dispatchAction(row: HandoffRow, machineId: string, dispatchTtlMs: number): Action;
15
29
  /** A machine on a version the line does not allow is refused before any dispatch. */
16
30
  export declare function softwareRefusal(state: GatewayState, line: LineDef, machineId: string): string | null;
@@ -9,6 +9,8 @@
9
9
  */
10
10
  Object.defineProperty(exports, "__esModule", { value: true });
11
11
  exports.dispatchArgs = dispatchArgs;
12
+ exports.dispatchWindow = dispatchWindow;
13
+ exports.latestArrival = latestArrival;
12
14
  exports.dispatchAction = dispatchAction;
13
15
  exports.softwareRefusal = softwareRefusal;
14
16
  exports.artifactRefusal = artifactRefusal;
@@ -37,11 +39,34 @@ function dispatchArgs(row, expiresAt) {
37
39
  expiresAt: (0, protocol_1.isoTs)(expiresAt),
38
40
  ...(row.artifactUrl ? { artifact: { url: row.artifactUrl, sha256: row.artifactSha256 } } : {}),
39
41
  ...(row.parameters ? { parameters: row.parameters } : {}),
42
+ ...(row.refs ? { refs: row.refs } : {}),
40
43
  };
41
44
  }
45
+ /**
46
+ * The validity round a dispatch belongs to. Rounds of one validity period count
47
+ * from when the dispatch became due, and a round begins only when the one
48
+ * before it has expired, so a machine never holds two valid commands for one
49
+ * row. Within a round a dispatch sent again, by a retry, a restarted machine or
50
+ * a restarted gateway, is the same command with the same arguments; a new round
51
+ * is a new command, never an old one with changed arguments.
52
+ */
53
+ function dispatchWindow(dueAt, now, ttlMs) {
54
+ const round = Math.max(1, Math.floor((now - dueAt) / ttlMs) + 1);
55
+ return { round, expiresAt: dueAt + round * ttlMs };
56
+ }
57
+ /** The later of a row's due time and an arrival's time; an unreadable time leaves it as it was. */
58
+ function latestArrival(dueAt, at) {
59
+ const t = typeof at === 'string' ? Date.parse(at) : NaN;
60
+ if (!Number.isFinite(t))
61
+ return dueAt;
62
+ return dueAt === undefined ? t : Math.max(dueAt, t);
63
+ }
42
64
  function dispatchAction(row, machineId, dispatchTtlMs) {
65
+ const now = Date.now();
66
+ row.createdAt ??= now;
67
+ const { round, expiresAt } = dispatchWindow(row.dueAt ?? row.createdAt, now, dispatchTtlMs);
43
68
  return {
44
- kind: 'dispatch', rowId: row.id, machineId, cmdId: (0, contracts_1.dispatchCommandId)(row.id), args: dispatchArgs(row, Date.now() + dispatchTtlMs),
69
+ kind: 'dispatch', rowId: row.id, machineId, cmdId: (0, contracts_1.dispatchCommandId)(row.id, round), args: dispatchArgs(row, expiresAt),
45
70
  ...(row.dispatch === state_1.DISPATCH_STATE.ARRIVING ? { receivedOnAccept: true } : {}),
46
71
  };
47
72
  }
@@ -57,7 +82,7 @@ function softwareRefusal(state, line, machineId) {
57
82
  function artifactRefusal(line, row) {
58
83
  if (!(0, line_1.machineById)(line, row.toMachine).requireArtifact || (row.artifactUrl && row.artifactSha256))
59
84
  return null;
60
- if (row.units.every((u) => u.artifact))
85
+ if (row.units.every((u) => u.artifact || u.artifacts?.length))
61
86
  return null;
62
87
  return `${row.toMachine} requires an artifact (url and sha256), for the job or for every unit; the row has none`;
63
88
  }
@@ -29,9 +29,17 @@ const VERB = { CREATED: 'created', CLAIMED: 'claimed', RESOLVED: 'resolved', CAN
29
29
  const STATUS_FOR_VERB = {
30
30
  [VERB.CREATED]: 'pending', [VERB.CLAIMED]: 'pending', [VERB.RESOLVED]: 'resolved', [VERB.CANCELLED]: 'cancelled', [VERB.EXPIRED]: 'expired',
31
31
  };
32
+ /** The metadata key an accumulator row parked with an item limit carries. */
33
+ const ACCUMULATE_MAX = 'accumulate_max';
32
34
  const LINE_ROW_TYPES = [contracts_1.ROW_TYPE.ALARM, contracts_1.ROW_TYPE.MACHINE_OFFLINE, contracts_1.ROW_TYPE.MACHINE_QUIET, contracts_1.ROW_TYPE.MACHINE_COMMAND, contracts_1.ROW_TYPE.MACHINE_REGISTRATION];
33
- function handoffRow(id, role, facets, metadata) {
34
- return { ...facets, id, role, dispatch: state_1.DISPATCH_STATE.NONE, trace: (0, trace_1.readTrace)(metadata) };
35
+ function handoffRow(id, role, facets, data) {
36
+ const metadata = data?.metadata;
37
+ const capped = metadata?.[ACCUMULATE_MAX] != null;
38
+ const createdAt = data?.created_at ? new Date(data.created_at).getTime() : NaN;
39
+ return {
40
+ ...facets, id, role, dispatch: state_1.DISPATCH_STATE.NONE, trace: (0, trace_1.readTrace)(metadata),
41
+ ...(capped ? { capped } : {}), ...(Number.isFinite(createdAt) ? { createdAt } : {}),
42
+ };
35
43
  }
36
44
  /** Rows for another line belong to that line's gateway. */
37
45
  function onThisLine(line, at) {
@@ -85,8 +93,9 @@ function commandRefusal(state, line, c, role) {
85
93
  const own = (0, line_1.machineRole)((0, line_1.machineById)(line, c.machineId));
86
94
  if (role !== own)
87
95
  return `commands for ${c.machineId} are parked on ${own}, not ${role}`;
88
- if (!protocol_1.CONTROL_OPS.includes(c.op))
89
- return `${c.op} is not a command a row may ask for; one of ${protocol_1.CONTROL_OPS.join(', ')}`;
96
+ const allowed = rowOps(state);
97
+ if (!allowed.includes(c.op))
98
+ return `${c.op} is not a command a row may ask for; one of ${allowed.join(', ')}`;
90
99
  if (c.op === protocol_1.OP.CANCEL && typeof c.args.jobId !== 'string')
91
100
  return `${protocol_1.OP.CANCEL} needs args.jobId, the job to withdraw or stop`;
92
101
  if (!(0, registration_1.reachable)(state, line, c.machineId))
@@ -102,13 +111,18 @@ function planCommandRow(state, line, event) {
102
111
  if (event.verb !== VERB.CREATED && event.verb !== VERB.CLAIMED)
103
112
  return [];
104
113
  const c = (0, commands_1.readCommandFacets)(event.data?.metadata);
105
- if (!c || !state.trackCommand(id))
114
+ if (!c || !state.trackCommand(id, c.machineId))
106
115
  return [];
107
116
  const refusal = commandRefusal(state, line, c, event.role);
108
117
  if (refusal) {
109
118
  return [{ kind: 'resolve', as: actions_1.CONTROLLER, rowId: id, payload: { accepted: false, machineId: c.machineId, op: c.op, status: contracts_1.COMMAND_STATUS.UNSUPPORTED, reason: refusal } }];
110
119
  }
111
- return [{ kind: 'control', rowId: id, machineId: c.machineId, op: c.op, cmdId: (0, contracts_1.controlCommandId)(id), args: c.args }];
120
+ const createdAt = event.data?.created_at ? new Date(event.data.created_at).getTime() : NaN;
121
+ return [{ kind: 'control', rowId: id, machineId: c.machineId, op: c.op, args: c.args, ...(Number.isFinite(createdAt) ? { createdAt } : {}) }];
122
+ }
123
+ /** The ops a machine-command row may ask for: the control ops, a snapshot, and the codec's own. */
124
+ function rowOps(state) {
125
+ return [...protocol_1.CONTROL_OPS, protocol_1.OP.SNAPSHOT, ...state.rowOps];
112
126
  }
113
127
  function onLine(line, machineId) {
114
128
  return !!machineId && line.machines.some((m) => m.machineId === machineId);
@@ -122,7 +136,7 @@ function planHandoff(state, line, event, facets, dispatchTtlMs) {
122
136
  if (event.verb === VERB.CREATED || event.verb === VERB.CLAIMED) {
123
137
  if (state.isClosed(id) || !facets)
124
138
  return [];
125
- state.index(handoffRow(id, event.role, facets, event.data?.metadata));
139
+ state.index(handoffRow(id, event.role, facets, event.data));
126
140
  const row = state.get(id);
127
141
  // The units now wait on the sender as this row's sender job, when the sender holds them under it.
128
142
  if (row.fromMachine)
@@ -2,10 +2,11 @@
2
2
  * Machine messages to gateway actions: the escalation verbs a person would
3
3
  * perform, performed for the machine. Each unit's arrival and the sender's
4
4
  * finished record accumulate on the handoff row, and the last expected item
5
- * resolves it. A failure on either side resolves it with an error. Alarms that
6
- * stop work and machines that go dark or quiet become rows for people. A
7
- * restarted machine gets back the dispatches it lost. Everything else stays
8
- * in the stream.
5
+ * resolves it. A failure on either side resolves it with an error. A job's
6
+ * start, its progress steps, and the alarms that pause it are noted on the row
7
+ * that holds it (lifecycle.ts). Alarms that stop work and machines that go
8
+ * dark or quiet become rows for people. A restarted machine gets back the
9
+ * dispatches it lost. Everything else stays in the stream.
9
10
  *
10
11
  * A message whose row does not exist yet is nak'd and arrives again; one whose
11
12
  * row already closed is acked, so a replay never resolves anything twice.
@@ -3,10 +3,11 @@
3
3
  * Machine messages to gateway actions: the escalation verbs a person would
4
4
  * perform, performed for the machine. Each unit's arrival and the sender's
5
5
  * finished record accumulate on the handoff row, and the last expected item
6
- * resolves it. A failure on either side resolves it with an error. Alarms that
7
- * stop work and machines that go dark or quiet become rows for people. A
8
- * restarted machine gets back the dispatches it lost. Everything else stays
9
- * in the stream.
6
+ * resolves it. A failure on either side resolves it with an error. A job's
7
+ * start, its progress steps, and the alarms that pause it are noted on the row
8
+ * that holds it (lifecycle.ts). Alarms that stop work and machines that go
9
+ * dark or quiet become rows for people. A restarted machine gets back the
10
+ * dispatches it lost. Everything else stays in the stream.
10
11
  *
11
12
  * A message whose row does not exist yet is nak'd and arrives again; one whose
12
13
  * row already closed is acked, so a replay never resolves anything twice.
@@ -21,6 +22,7 @@ const actions_1 = require("./actions");
21
22
  const pacing_1 = require("./pacing");
22
23
  const plan_shared_1 = require("./plan-shared");
23
24
  const jobs_1 = require("./jobs");
25
+ const lifecycle_1 = require("./lifecycle");
24
26
  const trace_1 = require("./trace");
25
27
  const state_1 = require("./state");
26
28
  const ack = (actions = []) => ({ actions, disposition: transport_1.DISPOSITION.ACK });
@@ -34,7 +36,7 @@ function planMachineMessage(state, parsed, env, dispatchTtlMs) {
34
36
  function planByKind(state, m, parsed, env, b, ttl) {
35
37
  switch (parsed.kind) {
36
38
  case protocol_1.MESSAGE_KIND.REPLY:
37
- return ack([{ kind: 'settleReply', cmdId: b.cmdId, status: b.status, reason: b.reason }]);
39
+ return ack([{ kind: 'settleReply', cmdId: b.cmdId, status: b.status, reason: b.reason, ...(typeof b.code === 'string' ? { code: b.code } : {}) }]);
38
40
  case protocol_1.MESSAGE_KIND.JOB:
39
41
  return planJob(state, m, b, env?.at, ttl);
40
42
  case protocol_1.MESSAGE_KIND.ALARM:
@@ -68,12 +70,29 @@ function ownsJob(jobId, reported) {
68
70
  return (0, jobs_1.jobOwns)(jobId, reported);
69
71
  }
70
72
  /** Keep the receiver's arrival event for the unit, to go out with its dispatch. */
71
- function withArrival(row, unitId, b) {
73
+ function withArrival(row, unitId, b, at) {
72
74
  row.arrivals = { ...row.arrivals, [unitId]: b };
75
+ row.dueAt = (0, pacing_1.latestArrival)(row.dueAt, at);
73
76
  return row;
74
77
  }
78
+ /** The unit an open handoff into `m` carries on `carrierId`, for a scan that read only the carrier. */
79
+ function unitOnCarrier(state, m, carrierId) {
80
+ if (typeof carrierId !== 'string')
81
+ return undefined;
82
+ for (const r of state.open()) {
83
+ if (r.toMachine !== m)
84
+ continue;
85
+ const unit = r.units.find((u) => u.carrierId === carrierId);
86
+ if (unit)
87
+ return unit.unitId;
88
+ }
89
+ return undefined;
90
+ }
75
91
  function planJob(state, m, b, at, ttl) {
76
- const unitId = b.unitId;
92
+ const unitId = typeof b.unitId === 'string' ? b.unitId : unitOnCarrier(state, m, b.carrierId);
93
+ // A carrier no open handoff here carries yet: its row may still be on the way.
94
+ if (unitId === undefined)
95
+ return nak;
77
96
  // An event from an earlier attempt's job never touches a later attempt's row.
78
97
  const toHere = (r) => r.toMachine === m && ownsJob(r.jobId, b.jobId);
79
98
  const fromHere = (r) => r.fromMachine === m && ownsJob(r.senderJobId, b.jobId);
@@ -95,13 +114,14 @@ function planJob(state, m, b, at, ttl) {
95
114
  if (row.fromMachine)
96
115
  state.letGo(row.fromMachine, unitId);
97
116
  const units = row.units.map((u) => u.unitId);
117
+ const arrivedAt = at ?? new Date().toISOString();
98
118
  const complete = row.dispatch === state_1.DISPATCH_STATE.ARRIVING
99
- ? { keys: units.map(contracts_1.itemKeys.arrived), then: [(0, pacing_1.dispatchAction)(withArrival(row, unitId, b), m, ttl)] }
119
+ ? { keys: units.map(contracts_1.itemKeys.arrived), then: [(0, pacing_1.dispatchAction)(withArrival(row, unitId, b, arrivedAt), m, ttl)] }
100
120
  : { keys: units.map(contracts_1.itemKeys.arrived), payload: { received: true, receiver: m, units } };
101
121
  return ack([...early, {
102
122
  kind: 'accumulate', as: m, rowId: row.id, itemKey: contracts_1.itemKeys.arrived(unitId),
103
123
  payload: {
104
- unitId, carrierId: b.carrierId ?? null, from: b.peer ?? null, at: at ?? new Date().toISOString(),
124
+ unitId, carrierId: b.carrierId ?? null, from: b.peer ?? null, at: arrivedAt,
105
125
  ...(row.dispatch === state_1.DISPATCH_STATE.ARRIVING ? { arrival: b } : {}),
106
126
  },
107
127
  complete,
@@ -119,7 +139,8 @@ function planJob(state, m, b, at, ttl) {
119
139
  if (b.result === protocol_1.JOB_RESULT.OK)
120
140
  return ack([released]);
121
141
  return ack([released, ...(0, plan_shared_1.failHandoff)(state, row, m, {
122
- received: false, unitId, errorCode: contracts_1.HANDOFF_ERROR.SENDER_REJECTED, reason: b.reason ?? null, detail: `${m} finished ${unitId}: ${b.result}${b.reason ? ` (${b.reason})` : ''}`,
142
+ received: false, unitId, errorCode: contracts_1.HANDOFF_ERROR.SENDER_REJECTED, result: b.result, reason: b.reason ?? null,
143
+ detail: `${m} finished ${unitId}: ${b.result}${b.reason ? ` (${b.reason})` : ''}`,
123
144
  })]);
124
145
  }
125
146
  if (b.event === protocol_1.JOB_EVENT.TRANSFER_FAILED) {
@@ -140,6 +161,8 @@ function planJob(state, m, b, at, ttl) {
140
161
  return ack();
141
162
  return ack((0, plan_shared_1.failHandoff)(state, row, m, { received: false, unitId, errorCode: contracts_1.HANDOFF_ERROR.WITHDRAWN, detail: `${m} gave up ${b.jobId}` }));
142
163
  }
164
+ if (b.event === protocol_1.JOB_EVENT.STARTED || b.event === protocol_1.JOB_EVENT.PROGRESS)
165
+ return ack((0, lifecycle_1.noteJobEvent)(state, m, b, at));
143
166
  return ack();
144
167
  }
145
168
  function releasedAction(row, m, b) {
@@ -160,9 +183,10 @@ function planAlarm(state, parsed, alarmId, env) {
160
183
  if (!env || b.active === false) {
161
184
  const track = state.alarms.get(key);
162
185
  state.alarms.delete(key);
186
+ const resumed = track ? (0, lifecycle_1.noteResume)(state, track, b) : [];
163
187
  if (!track?.rowId || !track.open)
164
- return ack();
165
- return ack([{
188
+ return ack(resumed);
189
+ return ack([...resumed, {
166
190
  kind: 'resolve', as: actions_1.CONTROLLER, rowId: track.rowId,
167
191
  payload: { decision: contracts_1.DECISION.CLEARED, clearedBy: b.clearedBy ?? null, note: `cleared at ${m}${b.clearedBy ? ` by ${b.clearedBy}` : ''}` },
168
192
  }]);
@@ -175,8 +199,9 @@ function planAlarm(state, parsed, alarmId, env) {
175
199
  const superseded = known?.rowId && known.open
176
200
  ? [{ kind: 'resolve', as: actions_1.CONTROLLER, rowId: known.rowId, payload: { decision: contracts_1.DECISION.CLEARED, note: `${m} raised ${alarmId} again; this one ended` } }]
177
201
  : [];
178
- state.alarms.set(key, { machineId: m, alarmId, raisedAt: b.raisedAt, open: true });
179
- return ack([...superseded, {
202
+ const track = { machineId: m, alarmId, raisedAt: b.raisedAt, level: b.level, open: true };
203
+ state.alarms.set(key, track);
204
+ return ack([...superseded, ...(0, lifecycle_1.notePause)(state, track, b), {
180
205
  kind: 'createAlarmRow', machineId: m, alarmId,
181
206
  row: {
182
207
  type: contracts_1.ROW_TYPE.ALARM, subtype: b.level, role: contracts_1.LINE_ROLE.OPERATOR, priority: b.level === protocol_1.LEVEL.EMERGENCY ? 1 : 2,
@@ -198,10 +223,12 @@ function catalogEnvelope(catalog) {
198
223
  }
199
224
  function planPresence(state, m, b) {
200
225
  const link = state.link(m);
226
+ const session = typeof b.sessionId === 'string' ? b.sessionId : undefined;
201
227
  if (b.online !== true) {
202
228
  if (link.online)
203
229
  link.since = Date.now();
204
230
  link.online = false;
231
+ link.reason = typeof b.reason === 'string' ? b.reason : undefined;
205
232
  if (link.rowId || link.watching)
206
233
  return ack();
207
234
  link.watching = true;
@@ -210,12 +237,17 @@ function planPresence(state, m, b) {
210
237
  if (b.software)
211
238
  link.software = b.software;
212
239
  const wasOffline = !link.online;
240
+ // A new session while online: the machine reconnected before its offline presence was heard.
241
+ const reconnected = !!session && !!link.sessionId && session !== link.sessionId;
213
242
  link.online = true;
214
243
  link.watching = false;
244
+ link.reason = undefined;
245
+ if (session)
246
+ link.sessionId = session;
215
247
  const actions = [];
216
248
  // Back from a drop: ask for the full state rather than wait for the next heartbeat.
217
- if (wasOffline)
218
- actions.push({ kind: 'command', machineId: m, op: protocol_1.OP.SNAPSHOT, cmdId: `${m}:resync:${link.since}`, args: {} });
249
+ if (wasOffline || reconnected)
250
+ actions.push({ kind: 'command', machineId: m, op: protocol_1.OP.SNAPSHOT, cmdId: `${m}:resync:${session ?? link.since}`, args: {} });
219
251
  if (link.rowId) {
220
252
  actions.push({ kind: 'resolve', as: actions_1.CONTROLLER, rowId: link.rowId, payload: { decision: contracts_1.DECISION.RESTORED, note: `${m} back online` } });
221
253
  link.rowId = undefined;
@@ -228,15 +260,34 @@ function planSnapshot(state, m, b, ttl) {
228
260
  if (b.software)
229
261
  link.software = b.software;
230
262
  const jobIds = Array.isArray(b.jobs) ? b.jobs.map((j) => j?.jobId).filter((id) => typeof id === 'string') : [];
231
- if (Array.isArray(b.jobs))
263
+ if (Array.isArray(b.jobs)) {
232
264
  state.keepHeld(m, new Set(jobIds));
265
+ link.jobs = b.jobs;
266
+ }
267
+ const fresh = !state.retainedAlarms && !state.readingRetained && Array.isArray(b.alarms);
268
+ const cleared = fresh ? alarmsNoLongerListed(state, m, b.alarms) : [];
233
269
  const answer = { kind: 'settleSnapshot', machineId: m, jobIds };
234
270
  const previous = link.bootId;
235
271
  link.bootId = b.bootId;
236
272
  if (!previous || previous === b.bootId)
237
- return ack([answer]);
273
+ return ack([...cleared, answer]);
238
274
  const held = new Set(jobIds);
239
275
  const missing = (r) => (0, jobs_1.dispatchJobs)(r.jobId, r.units, state.unitsPerDispatch).some((j) => !held.has(j.jobId));
240
276
  const lost = state.open().filter((r) => r.toMachine === m && r.dispatch === state_1.DISPATCH_STATE.ACCEPTED && missing(r));
241
- return ack([answer, ...lost.map((r) => (0, pacing_1.dispatchAction)(r, m, ttl))]);
277
+ return ack([...cleared, answer, ...lost.map((r) => (0, pacing_1.dispatchAction)(r, m, ttl))]);
278
+ }
279
+ /** Alarm rows for `m` whose alarm its snapshot no longer lists: they cleared at the machine. */
280
+ function alarmsNoLongerListed(state, m, listed) {
281
+ const active = new Set(listed.filter((id) => typeof id === 'string'));
282
+ const actions = [];
283
+ for (const [key, track] of [...state.alarms.entries()]) {
284
+ if (track.machineId !== m || active.has(track.alarmId))
285
+ continue;
286
+ state.alarms.delete(key);
287
+ actions.push(...(0, lifecycle_1.noteResume)(state, track, {}));
288
+ if (track.open && track.rowId) {
289
+ actions.push({ kind: 'resolve', as: actions_1.CONTROLLER, rowId: track.rowId, payload: { decision: contracts_1.DECISION.CLEARED, note: `cleared at ${m}; its snapshot no longer lists it` } });
290
+ }
291
+ }
292
+ return actions;
242
293
  }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * The line as its leader sees it, for the dashboard: each machine's link,
3
+ * held units, open rows and alarms from the gateway state, and the claims
4
+ * waiting for a person. Pure: reads the state and the line, changes nothing.
5
+ */
6
+ import { type LineDef } from '../../../lib/machines/line';
7
+ import type { LineView } from '../../../types/machines-view';
8
+ import type { GatewayState } from './state';
9
+ export declare function projectLine(state: GatewayState, line: LineDef): LineView;
@@ -0,0 +1,55 @@
1
+ "use strict";
2
+ /**
3
+ * The line as its leader sees it, for the dashboard: each machine's link,
4
+ * held units, open rows and alarms from the gateway state, and the claims
5
+ * waiting for a person. Pure: reads the state and the line, changes nothing.
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.projectLine = projectLine;
9
+ const line_1 = require("../../../lib/machines/line");
10
+ const protocol_1 = require("../../../lib/machines/protocol");
11
+ const at = (ms) => (ms ? (0, protocol_1.isoTs)(ms) : null);
12
+ function addressOf(state, line, def) {
13
+ if ((0, line_1.learnsAddress)(line, def.machineId))
14
+ return state.learnedAddress(def.machineId) ?? null;
15
+ return def.address ?? def.machineId;
16
+ }
17
+ function alarmsOf(state, machineId) {
18
+ return [...state.alarms.values()]
19
+ .filter((track) => track.machineId === machineId)
20
+ .map((track) => ({ alarmId: track.alarmId, level: track.level ?? '', raisedAt: track.raisedAt ?? '', rowId: track.rowId ?? null }));
21
+ }
22
+ /** The handoff that brought a unit into a machine: open first, then the recently closed one. */
23
+ function rowCarrying(state, machineId, unitId) {
24
+ const into = (r) => r.toMachine === machineId;
25
+ return state.openFor(unitId, into) ?? state.closedFor(unitId, into);
26
+ }
27
+ function machineView(state, line, def) {
28
+ const m = def.machineId;
29
+ const link = state.links.get(m);
30
+ const open = state.open();
31
+ return {
32
+ machineId: m, process: def.process, role: (0, line_1.machineRole)(def), address: addressOf(state, line, def),
33
+ online: link ? link.online : null, since: at(link?.since), lastSeen: at(link?.lastSeen),
34
+ software: link?.software ?? null, bootId: link?.bootId ?? null, sessionId: link?.sessionId ?? null,
35
+ heldUnits: state.heldUnitsAt(m).map(({ unitId }) => {
36
+ const row = rowCarrying(state, m, unitId);
37
+ return { unitId, carrierId: row?.units.find((u) => u.unitId === unitId)?.carrierId ?? null, rowId: row?.id ?? '' };
38
+ }),
39
+ rows: {
40
+ handoff: open.filter((r) => r.toMachine === m).length,
41
+ command: state.openCommandsFor(m),
42
+ alarm: [...state.alarms.values()].filter((t) => t.machineId === m && t.open && !!t.rowId).length,
43
+ offlineRowId: link?.rowId ?? null, quietRowId: link?.quietRowId ?? null,
44
+ },
45
+ alarms: alarmsOf(state, m),
46
+ jobs: link?.jobs ?? [],
47
+ };
48
+ }
49
+ function projectLine(state, line) {
50
+ return {
51
+ siteId: line.siteId, lineId: line.lineId, leading: true, takenAt: at(state.takenAt),
52
+ machines: line.machines.map((def) => machineView(state, line, def)),
53
+ contested: state.contested(),
54
+ };
55
+ }