@hotmeshio/long-tail 0.27.0 → 0.28.1

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 (386) hide show
  1. package/README.md +9 -2
  2. package/build/api/files.d.ts +5 -0
  3. package/build/api/files.js +19 -2
  4. package/build/api/knowledge.d.ts +3 -1
  5. package/build/api/settings.d.ts +4 -1
  6. package/build/api/settings.js +7 -1
  7. package/build/index.js +9 -4
  8. package/build/lib/db/config-lock.d.ts +10 -0
  9. package/build/lib/db/config-lock.js +28 -0
  10. package/build/lib/db/schemas/041_machine_lines.sql +26 -0
  11. package/build/lib/db/schemas/042_knowledge_edition_labels.sql +9 -0
  12. package/build/lib/events/browser-view.js +8 -2
  13. package/build/lib/events/publish.d.ts +33 -0
  14. package/build/lib/events/publish.js +52 -1
  15. package/build/lib/machines/line.d.ts +7 -1
  16. package/build/lib/machines/line.js +25 -2
  17. package/build/lib/machines/protocol/codec.d.ts +14 -0
  18. package/build/lib/machines/protocol/codec.js +10 -1
  19. package/build/lib/machines/protocol/constants.d.ts +1 -1
  20. package/build/lib/machines/protocol/constants.js +1 -1
  21. package/build/lib/machines/protocol/contract.d.ts +2 -0
  22. package/build/lib/machines/protocol/contract.js +13 -8
  23. package/build/lib/machines/site/mosquitto-conf.d.ts +9 -4
  24. package/build/lib/machines/site/mosquitto-conf.js +32 -22
  25. package/build/lib/machines/site/site-conf.d.ts +13 -3
  26. package/build/lib/machines/site/site-conf.js +31 -8
  27. package/build/lib/machines/transport/callback-feed.d.ts +1 -0
  28. package/build/lib/machines/transport/callback-feed.js +4 -1
  29. package/build/lib/machines/transport/memory.d.ts +2 -0
  30. package/build/lib/machines/transport/memory.js +6 -0
  31. package/build/lib/machines/transport/mqtt-bus.d.ts +2 -0
  32. package/build/lib/machines/transport/mqtt-bus.js +4 -0
  33. package/build/lib/machines/transport/nats-bus.d.ts +3 -0
  34. package/build/lib/machines/transport/nats-bus.js +3 -0
  35. package/build/lib/machines/transport/nats-feed.d.ts +1 -0
  36. package/build/lib/machines/transport/nats-feed.js +4 -1
  37. package/build/lib/machines/transport/pubsub-feed.d.ts +1 -0
  38. package/build/lib/machines/transport/pubsub-feed.js +4 -1
  39. package/build/lib/machines/transport/stream-bus.d.ts +7 -1
  40. package/build/lib/machines/transport/stream-bus.js +17 -5
  41. package/build/lib/machines/transport/streams.d.ts +10 -2
  42. package/build/lib/machines/transport/streams.js +32 -5
  43. package/build/lib/messaging/memory.js +3 -0
  44. package/build/lib/messaging/nats/streams.d.ts +3 -0
  45. package/build/lib/messaging/nats/streams.js +16 -0
  46. package/build/lib/storage/local.d.ts +2 -0
  47. package/build/lib/storage/local.js +3 -0
  48. package/build/lib/storage/s3.d.ts +3 -2
  49. package/build/lib/storage/s3.js +6 -1
  50. package/build/lib/storage/types.d.ts +7 -1
  51. package/build/modules/machines.d.ts +8 -0
  52. package/build/modules/machines.js +16 -0
  53. package/build/routes/file-browser.js +3 -1
  54. package/build/routes/index.js +2 -0
  55. package/build/routes/knowledge.js +6 -5
  56. package/build/routes/machines.d.ts +2 -0
  57. package/build/routes/machines.js +253 -0
  58. package/build/services/knowledge/index.d.ts +4 -2
  59. package/build/services/knowledge/index.js +8 -2
  60. package/build/services/knowledge/lookup-cache.d.ts +8 -0
  61. package/build/services/knowledge/lookup-cache.js +30 -3
  62. package/build/services/knowledge/lookup-refs.js +1 -1
  63. package/build/services/knowledge/sql.d.ts +4 -2
  64. package/build/services/knowledge/sql.js +8 -3
  65. package/build/services/machines/commands.d.ts +5 -5
  66. package/build/services/machines/commands.js +3 -2
  67. package/build/services/machines/config.d.ts +4 -1
  68. package/build/services/machines/config.js +2 -1
  69. package/build/services/machines/contracts.d.ts +28 -0
  70. package/build/services/machines/contracts.js +15 -1
  71. package/build/services/machines/forms.d.ts +26 -1
  72. package/build/services/machines/forms.js +17 -1
  73. package/build/services/machines/gateway/actions.d.ts +14 -1
  74. package/build/services/machines/gateway/admission.d.ts +30 -4
  75. package/build/services/machines/gateway/admission.js +76 -14
  76. package/build/services/machines/gateway/api-escalations.js +4 -0
  77. package/build/services/machines/gateway/dispatcher.d.ts +12 -4
  78. package/build/services/machines/gateway/dispatcher.js +53 -17
  79. package/build/services/machines/gateway/executor.d.ts +13 -2
  80. package/build/services/machines/gateway/executor.js +84 -29
  81. package/build/services/machines/gateway/gateway.d.ts +21 -1
  82. package/build/services/machines/gateway/gateway.js +65 -25
  83. package/build/services/machines/gateway/index.d.ts +3 -0
  84. package/build/services/machines/gateway/index.js +3 -0
  85. package/build/services/machines/gateway/inspect.d.ts +17 -0
  86. package/build/services/machines/gateway/inspect.js +68 -0
  87. package/build/services/machines/gateway/log.d.ts +10 -0
  88. package/build/services/machines/gateway/log.js +20 -0
  89. package/build/services/machines/gateway/lt-http.js +11 -2
  90. package/build/services/machines/gateway/pacing.js +2 -1
  91. package/build/services/machines/gateway/plan-escalation.js +8 -3
  92. package/build/services/machines/gateway/plan-machine.js +41 -16
  93. package/build/services/machines/gateway/plan-shared.d.ts +5 -3
  94. package/build/services/machines/gateway/plan-shared.js +6 -4
  95. package/build/services/machines/gateway/project.d.ts +9 -0
  96. package/build/services/machines/gateway/project.js +55 -0
  97. package/build/services/machines/gateway/receipts.d.ts +3 -0
  98. package/build/services/machines/gateway/receipts.js +3 -1
  99. package/build/services/machines/gateway/reconcile.js +3 -3
  100. package/build/services/machines/gateway/registration.js +1 -1
  101. package/build/services/machines/gateway/reply-waiter.d.ts +8 -3
  102. package/build/services/machines/gateway/reply-waiter.js +37 -16
  103. package/build/services/machines/gateway/state.d.ts +53 -4
  104. package/build/services/machines/gateway/state.js +128 -13
  105. package/build/services/machines/gateway/wire-window.d.ts +21 -0
  106. package/build/services/machines/gateway/wire-window.js +61 -0
  107. package/build/services/machines/gateway/wire.d.ts +58 -0
  108. package/build/services/machines/gateway/wire.js +172 -0
  109. package/build/services/machines/index.d.ts +3 -0
  110. package/build/services/machines/index.js +2 -0
  111. package/build/services/machines/line-index.d.ts +16 -0
  112. package/build/services/machines/line-index.js +39 -0
  113. package/build/services/machines/registry-sql.d.ts +31 -0
  114. package/build/services/machines/registry-sql.js +110 -0
  115. package/build/services/machines/registry-sync.d.ts +31 -0
  116. package/build/services/machines/registry-sync.js +81 -0
  117. package/build/services/machines/registry.d.ts +77 -0
  118. package/build/services/machines/registry.js +158 -0
  119. package/build/services/machines/roles.d.ts +5 -4
  120. package/build/services/machines/roles.js +13 -4
  121. package/build/services/machines/rows.js +10 -1
  122. package/build/services/machines/select.d.ts +3 -2
  123. package/build/services/machines/select.js +4 -3
  124. package/build/services/machines/surface.d.ts +54 -5
  125. package/build/services/machines/surface.js +113 -17
  126. package/build/start/apply-report.d.ts +1 -1
  127. package/build/start/apply-report.js +3 -25
  128. package/build/start/index.js +16 -4
  129. package/build/system/activities/knowledge.d.ts +4 -0
  130. package/build/system/activities/knowledge.js +15 -0
  131. package/build/system/activities/sql.d.ts +2 -0
  132. package/build/system/activities/sql.js +6 -1
  133. package/build/system/index.js +13 -0
  134. package/build/system/mcp-servers/admin/schemas.d.ts +22 -22
  135. package/build/system/mcp-servers/human-queue-schemas.d.ts +7 -7
  136. package/build/system/mcp-servers/knowledge.js +4 -2
  137. package/build/system/mcp-servers/machines-schemas.d.ts +35 -0
  138. package/build/system/mcp-servers/machines-schemas.js +35 -0
  139. package/build/system/mcp-servers/machines.d.ts +20 -0
  140. package/build/system/mcp-servers/machines.js +154 -0
  141. package/build/system/seed/tool-manifests-machines.d.ts +2 -0
  142. package/build/system/seed/tool-manifests-machines.js +88 -0
  143. package/build/tsconfig.tsbuildinfo +1 -1
  144. package/build/types/escalation.d.ts +9 -6
  145. package/build/types/escalation.js +8 -2
  146. package/build/types/events.d.ts +6 -1
  147. package/build/types/index.d.ts +1 -0
  148. package/build/types/machines-view.d.ts +175 -0
  149. package/build/types/machines-view.js +7 -0
  150. package/build/types/machines.d.ts +5 -0
  151. package/build/types/messaging.d.ts +2 -0
  152. package/build/types/startup.d.ts +2 -0
  153. package/dashboard/dist/assets/{AdminDashboard-DlItqxFS.js → AdminDashboard-Bm7VG65H.js} +2 -2
  154. package/dashboard/dist/assets/{AdminDashboard-DlItqxFS.js.map → AdminDashboard-Bm7VG65H.js.map} +1 -1
  155. package/dashboard/dist/assets/{AgentConfigPage-BUmT4a7n.js → AgentConfigPage-aJF7Sdbc.js} +6 -6
  156. package/dashboard/dist/assets/{AgentConfigPage-BUmT4a7n.js.map → AgentConfigPage-aJF7Sdbc.js.map} +1 -1
  157. package/dashboard/dist/assets/{AgentDetailPage-Cww8rlBi.js → AgentDetailPage-GwwghhxT.js} +3 -3
  158. package/dashboard/dist/assets/{AgentDetailPage-Cww8rlBi.js.map → AgentDetailPage-GwwghhxT.js.map} +1 -1
  159. package/dashboard/dist/assets/AgentsPage-DVvCzZyg.js +2 -0
  160. package/dashboard/dist/assets/{AgentsPage-C9UuUKhr.js.map → AgentsPage-DVvCzZyg.js.map} +1 -1
  161. package/dashboard/dist/assets/AvailableEscalationsPage-Cxmcji_K.js +2 -0
  162. package/dashboard/dist/assets/{AvailableEscalationsPage-DrvfXz70.js.map → AvailableEscalationsPage-Cxmcji_K.js.map} +1 -1
  163. package/dashboard/dist/assets/{BotPicker-CDq9XRMp.js → BotPicker-CofRxk4L.js} +2 -2
  164. package/dashboard/dist/assets/{BotPicker-CDq9XRMp.js.map → BotPicker-CofRxk4L.js.map} +1 -1
  165. package/dashboard/dist/assets/{BulkAssignModal-jsvqtrEA.js → BulkAssignModal-BARWuz4L.js} +2 -2
  166. package/dashboard/dist/assets/{BulkAssignModal-jsvqtrEA.js.map → BulkAssignModal-BARWuz4L.js.map} +1 -1
  167. package/dashboard/dist/assets/{CapabilitiesPage-CSCrI6B1.js → CapabilitiesPage-BJAWVNEx.js} +2 -2
  168. package/dashboard/dist/assets/{CapabilitiesPage-CSCrI6B1.js.map → CapabilitiesPage-BJAWVNEx.js.map} +1 -1
  169. package/dashboard/dist/assets/{CollapsibleSection-uD9mn-CQ.js → CollapsibleSection-CtgoaG75.js} +2 -2
  170. package/dashboard/dist/assets/{CollapsibleSection-uD9mn-CQ.js.map → CollapsibleSection-CtgoaG75.js.map} +1 -1
  171. package/dashboard/dist/assets/{ConfirmDeleteModal-BRmx2hA4.js → ConfirmDeleteModal-BYXXBWs6.js} +2 -2
  172. package/dashboard/dist/assets/{ConfirmDeleteModal-BRmx2hA4.js.map → ConfirmDeleteModal-BYXXBWs6.js.map} +1 -1
  173. package/dashboard/dist/assets/{ConnectedAppsPage-DlMzE5a-.js → ConnectedAppsPage-D0JkzWa7.js} +2 -2
  174. package/dashboard/dist/assets/{ConnectedAppsPage-DlMzE5a-.js.map → ConnectedAppsPage-D0JkzWa7.js.map} +1 -1
  175. package/dashboard/dist/assets/{CountdownTimer-sRuM4eM5.js → CountdownTimer-BcFxdZYN.js} +2 -2
  176. package/dashboard/dist/assets/{CountdownTimer-sRuM4eM5.js.map → CountdownTimer-BcFxdZYN.js.map} +1 -1
  177. package/dashboard/dist/assets/{CredentialsPage-bLYydwqa.js → CredentialsPage-DPSY20rV.js} +2 -2
  178. package/dashboard/dist/assets/{CredentialsPage-bLYydwqa.js.map → CredentialsPage-DPSY20rV.js.map} +1 -1
  179. package/dashboard/dist/assets/{CronLabel-D_ATDL2N.js → CronLabel-Dg_Oew_Y.js} +2 -2
  180. package/dashboard/dist/assets/{CronLabel-D_ATDL2N.js.map → CronLabel-Dg_Oew_Y.js.map} +1 -1
  181. package/dashboard/dist/assets/{DropZone-u1JGfR9G.js → DropZone-Cl_Flb16.js} +2 -2
  182. package/dashboard/dist/assets/{DropZone-u1JGfR9G.js.map → DropZone-Cl_Flb16.js.map} +1 -1
  183. package/dashboard/dist/assets/{ElapsedCell-groSMfqB.js → ElapsedCell-Dm3G5fK2.js} +2 -2
  184. package/dashboard/dist/assets/{ElapsedCell-groSMfqB.js.map → ElapsedCell-Dm3G5fK2.js.map} +1 -1
  185. package/dashboard/dist/assets/{EntityTimelinePanel-D-F3_A9m.js → EntityTimelinePanel-CrEEP04e.js} +2 -2
  186. package/dashboard/dist/assets/{EntityTimelinePanel-D-F3_A9m.js.map → EntityTimelinePanel-CrEEP04e.js.map} +1 -1
  187. package/dashboard/dist/assets/{EscalationListSchemaPage-Bad6Jrmr.js → EscalationListSchemaPage-wOInXcjs.js} +3 -3
  188. package/dashboard/dist/assets/{EscalationListSchemaPage-Bad6Jrmr.js.map → EscalationListSchemaPage-wOInXcjs.js.map} +1 -1
  189. package/dashboard/dist/assets/{EscalationSchemaPage-BKi_npI9.js → EscalationSchemaPage-CrIj_g9d.js} +3 -3
  190. package/dashboard/dist/assets/{EscalationSchemaPage-BKi_npI9.js.map → EscalationSchemaPage-CrIj_g9d.js.map} +1 -1
  191. package/dashboard/dist/assets/{EscalationTimeline-DQUUm3b1.js → EscalationTimeline-CeAsl5IT.js} +2 -2
  192. package/dashboard/dist/assets/{EscalationTimeline-DQUUm3b1.js.map → EscalationTimeline-CeAsl5IT.js.map} +1 -1
  193. package/dashboard/dist/assets/{EscalationTitleSelect-3mhXh77a.js → EscalationTitleSelect-D8qpCYiy.js} +2 -2
  194. package/dashboard/dist/assets/{EscalationTitleSelect-3mhXh77a.js.map → EscalationTitleSelect-D8qpCYiy.js.map} +1 -1
  195. package/dashboard/dist/assets/{EscalationsOverview-Qaov-uML.js → EscalationsOverview-CvaqVPsr.js} +2 -2
  196. package/dashboard/dist/assets/{EscalationsOverview-Qaov-uML.js.map → EscalationsOverview-CvaqVPsr.js.map} +1 -1
  197. package/dashboard/dist/assets/{EventTable-V4RF_n58.js → EventTable-Be0JuEl6.js} +2 -2
  198. package/dashboard/dist/assets/{EventTable-V4RF_n58.js.map → EventTable-Be0JuEl6.js.map} +1 -1
  199. package/dashboard/dist/assets/FilterBar-BVyAjIgv.js +2 -0
  200. package/dashboard/dist/assets/FilterBar-BVyAjIgv.js.map +1 -0
  201. package/dashboard/dist/assets/{GraphInvokePage-Djslhf96.js → GraphInvokePage-D_QigJeD.js} +2 -2
  202. package/dashboard/dist/assets/{GraphInvokePage-Djslhf96.js.map → GraphInvokePage-D_QigJeD.js.map} +1 -1
  203. package/dashboard/dist/assets/{HomePage-EjcNxlKx.js → HomePage-C1haeuNy.js} +2 -2
  204. package/dashboard/dist/assets/{HomePage-EjcNxlKx.js.map → HomePage-C1haeuNy.js.map} +1 -1
  205. package/dashboard/dist/assets/LinePage-CWD5pv1g.js +5 -0
  206. package/dashboard/dist/assets/LinePage-CWD5pv1g.js.map +1 -0
  207. package/dashboard/dist/assets/LinesPage-NqChsKcO.js +3 -0
  208. package/dashboard/dist/assets/LinesPage-NqChsKcO.js.map +1 -0
  209. package/dashboard/dist/assets/{ListToolbar-RrEAePOX.js → ListToolbar-BZCNrTh6.js} +2 -2
  210. package/dashboard/dist/assets/{ListToolbar-RrEAePOX.js.map → ListToolbar-BZCNrTh6.js.map} +1 -1
  211. package/dashboard/dist/assets/{McpOverview-8gqiL3Vj.js → McpOverview-yJTYnh1z.js} +2 -2
  212. package/dashboard/dist/assets/{McpOverview-8gqiL3Vj.js.map → McpOverview-yJTYnh1z.js.map} +1 -1
  213. package/dashboard/dist/assets/{McpQueryDetailPage-COyn6Ak5.js → McpQueryDetailPage-ChuLpJ4u.js} +3 -3
  214. package/dashboard/dist/assets/{McpQueryDetailPage-COyn6Ak5.js.map → McpQueryDetailPage-ChuLpJ4u.js.map} +1 -1
  215. package/dashboard/dist/assets/{McpQueryPage-Dj6Qj72w.js → McpQueryPage-9S-v9soF.js} +2 -2
  216. package/dashboard/dist/assets/{McpQueryPage-Dj6Qj72w.js.map → McpQueryPage-9S-v9soF.js.map} +1 -1
  217. package/dashboard/dist/assets/McpRunDetailPage-BhrcfxD5.js +2 -0
  218. package/dashboard/dist/assets/{McpRunDetailPage-80fQn9iX.js.map → McpRunDetailPage-BhrcfxD5.js.map} +1 -1
  219. package/dashboard/dist/assets/{McpRunsPage-DaSsI-EM.js → McpRunsPage-C5_i8_Zx.js} +2 -2
  220. package/dashboard/dist/assets/{McpRunsPage-DaSsI-EM.js.map → McpRunsPage-C5_i8_Zx.js.map} +1 -1
  221. package/dashboard/dist/assets/{NamespacePill-C5epAgq6.js → NamespacePill-OTZD2hzt.js} +2 -2
  222. package/dashboard/dist/assets/{NamespacePill-C5epAgq6.js.map → NamespacePill-OTZD2hzt.js.map} +1 -1
  223. package/dashboard/dist/assets/{OperationsPage-BSu1o2gB.js → OperationsPage-C03CeU5P.js} +2 -2
  224. package/dashboard/dist/assets/{OperationsPage-BSu1o2gB.js.map → OperationsPage-C03CeU5P.js.map} +1 -1
  225. package/dashboard/dist/assets/{OperatorDashboard-HFI4WGIV.js → OperatorDashboard-BzC134Jt.js} +2 -2
  226. package/dashboard/dist/assets/{OperatorDashboard-HFI4WGIV.js.map → OperatorDashboard-BzC134Jt.js.map} +1 -1
  227. package/dashboard/dist/assets/{PageHeader-BSWIspNB.js → PageHeader-Hx8APPwE.js} +2 -2
  228. package/dashboard/dist/assets/{PageHeader-BSWIspNB.js.map → PageHeader-Hx8APPwE.js.map} +1 -1
  229. package/dashboard/dist/assets/{PageHeaderWithStats-DDjopQ-k.js → PageHeaderWithStats-BEQYYBJv.js} +2 -2
  230. package/dashboard/dist/assets/{PageHeaderWithStats-DDjopQ-k.js.map → PageHeaderWithStats-BEQYYBJv.js.map} +1 -1
  231. package/dashboard/dist/assets/{PersonaDetailPage-D0qlBuGi.js → PersonaDetailPage-D0mY9L6j.js} +2 -2
  232. package/dashboard/dist/assets/{PersonaDetailPage-D0qlBuGi.js.map → PersonaDetailPage-D0mY9L6j.js.map} +1 -1
  233. package/dashboard/dist/assets/{PersonasPage-BJClu-5H.js → PersonasPage-FFlm5Eug.js} +2 -2
  234. package/dashboard/dist/assets/{PersonasPage-BJClu-5H.js.map → PersonasPage-FFlm5Eug.js.map} +1 -1
  235. package/dashboard/dist/assets/{PortalPage-CucNZn_w.js → PortalPage-Cpc25uO-.js} +2 -2
  236. package/dashboard/dist/assets/{PortalPage-CucNZn_w.js.map → PortalPage-Cpc25uO-.js.map} +1 -1
  237. package/dashboard/dist/assets/{ProcessDetailPage-B9wMw0RV.js → ProcessDetailPage-Di2J7KwY.js} +2 -2
  238. package/dashboard/dist/assets/{ProcessDetailPage-B9wMw0RV.js.map → ProcessDetailPage-Di2J7KwY.js.map} +1 -1
  239. package/dashboard/dist/assets/{ProcessesListPage-B6k4RpmR.js → ProcessesListPage-BEXwU3Z0.js} +2 -2
  240. package/dashboard/dist/assets/{ProcessesListPage-B6k4RpmR.js.map → ProcessesListPage-BEXwU3Z0.js.map} +1 -1
  241. package/dashboard/dist/assets/{ResolverForm-ZVufJ4G-.js → ResolverForm-2ks-3jXC.js} +6 -6
  242. package/dashboard/dist/assets/{ResolverForm-ZVufJ4G-.js.map → ResolverForm-2ks-3jXC.js.map} +1 -1
  243. package/dashboard/dist/assets/{RoleDetailPage-CTbq0mz4.js → RoleDetailPage-DqoOk0xc.js} +3 -3
  244. package/dashboard/dist/assets/{RoleDetailPage-CTbq0mz4.js.map → RoleDetailPage-DqoOk0xc.js.map} +1 -1
  245. package/dashboard/dist/assets/{RolePill-CoETDpjF.js → RolePill-R5_ctGFy.js} +2 -2
  246. package/dashboard/dist/assets/{RolePill-CoETDpjF.js.map → RolePill-R5_ctGFy.js.map} +1 -1
  247. package/dashboard/dist/assets/{RolesPage-DBKxeam4.js → RolesPage-CPmo8Srr.js} +2 -2
  248. package/dashboard/dist/assets/{RolesPage-DBKxeam4.js.map → RolesPage-CPmo8Srr.js.map} +1 -1
  249. package/dashboard/dist/assets/{RosterWidget-BkLAwEYB.js → RosterWidget-D2yovCKw.js} +2 -2
  250. package/dashboard/dist/assets/{RosterWidget-BkLAwEYB.js.map → RosterWidget-D2yovCKw.js.map} +1 -1
  251. package/dashboard/dist/assets/{RunAsSelector-D2DC4iDf.js → RunAsSelector-CMv96On6.js} +2 -2
  252. package/dashboard/dist/assets/{RunAsSelector-D2DC4iDf.js.map → RunAsSelector-CMv96On6.js.map} +1 -1
  253. package/dashboard/dist/assets/{ScanCodesPage-BlKleaT0.js → ScanCodesPage-CA6cTT-M.js} +2 -2
  254. package/dashboard/dist/assets/{ScanCodesPage-BlKleaT0.js.map → ScanCodesPage-CA6cTT-M.js.map} +1 -1
  255. package/dashboard/dist/assets/{ScanSchemeDetailPage-CTW6gofi.js → ScanSchemeDetailPage-DnctN5-H.js} +2 -2
  256. package/dashboard/dist/assets/{ScanSchemeDetailPage-CTW6gofi.js.map → ScanSchemeDetailPage-DnctN5-H.js.map} +1 -1
  257. package/dashboard/dist/assets/{ScanStationPage-DPRjSPyT.js → ScanStationPage-Bk8RwkKL.js} +2 -2
  258. package/dashboard/dist/assets/{ScanStationPage-DPRjSPyT.js.map → ScanStationPage-Bk8RwkKL.js.map} +1 -1
  259. package/dashboard/dist/assets/{ScopeBadge-CGsTtqFr.js → ScopeBadge-BXZ084nD.js} +2 -2
  260. package/dashboard/dist/assets/{ScopeBadge-CGsTtqFr.js.map → ScopeBadge-BXZ084nD.js.map} +1 -1
  261. package/dashboard/dist/assets/{StickyPagination-CkmX3-Gu.js → StickyPagination-B0FSuUgs.js} +2 -2
  262. package/dashboard/dist/assets/{StickyPagination-CkmX3-Gu.js.map → StickyPagination-B0FSuUgs.js.map} +1 -1
  263. package/dashboard/dist/assets/{StreamMessageDetail-Xm_n510s.js → StreamMessageDetail-BwZEvMpP.js} +2 -2
  264. package/dashboard/dist/assets/{StreamMessageDetail-Xm_n510s.js.map → StreamMessageDetail-BwZEvMpP.js.map} +1 -1
  265. package/dashboard/dist/assets/{SwimlaneTimeline-De48aJan.js → SwimlaneTimeline-BDVQEaMw.js} +2 -2
  266. package/dashboard/dist/assets/{SwimlaneTimeline-De48aJan.js.map → SwimlaneTimeline-BDVQEaMw.js.map} +1 -1
  267. package/dashboard/dist/assets/{TagInput-S7UuVT_M.js → TagInput-DjPUDp2M.js} +2 -2
  268. package/dashboard/dist/assets/{TagInput-S7UuVT_M.js.map → TagInput-DjPUDp2M.js.map} +1 -1
  269. package/dashboard/dist/assets/{TaskDetailPage-HycMx3O9.js → TaskDetailPage-BOlv3iGB.js} +2 -2
  270. package/dashboard/dist/assets/{TaskDetailPage-HycMx3O9.js.map → TaskDetailPage-BOlv3iGB.js.map} +1 -1
  271. package/dashboard/dist/assets/{TaskQueuePill-BlHNpYiZ.js → TaskQueuePill-D-4Ynf3K.js} +2 -2
  272. package/dashboard/dist/assets/{TaskQueuePill-BlHNpYiZ.js.map → TaskQueuePill-D-4Ynf3K.js.map} +1 -1
  273. package/dashboard/dist/assets/{TasksListPage-OIcFFp1C.js → TasksListPage-e2teY0bD.js} +2 -2
  274. package/dashboard/dist/assets/{TasksListPage-OIcFFp1C.js.map → TasksListPage-e2teY0bD.js.map} +1 -1
  275. package/dashboard/dist/assets/{TimeAgo-OTC_55oZ.js → TimeAgo-Cvkpf_b3.js} +2 -2
  276. package/dashboard/dist/assets/{TimeAgo-OTC_55oZ.js.map → TimeAgo-Cvkpf_b3.js.map} +1 -1
  277. package/dashboard/dist/assets/{TimestampCell-CAwIj_AI.js → TimestampCell-B3btAV0N.js} +2 -2
  278. package/dashboard/dist/assets/{TimestampCell-CAwIj_AI.js.map → TimestampCell-B3btAV0N.js.map} +1 -1
  279. package/dashboard/dist/assets/{ToolPill-D40uLM_v.js → ToolPill-GsKQ80CO.js} +2 -2
  280. package/dashboard/dist/assets/{ToolPill-D40uLM_v.js.map → ToolPill-GsKQ80CO.js.map} +1 -1
  281. package/dashboard/dist/assets/{ToolTestPanel-8Vf6pfw4.js → ToolTestPanel-B03nebR7.js} +2 -2
  282. package/dashboard/dist/assets/{ToolTestPanel-8Vf6pfw4.js.map → ToolTestPanel-B03nebR7.js.map} +1 -1
  283. package/dashboard/dist/assets/{TopicDetailPage-iZW8rK9L.js → TopicDetailPage-DBRWne3O.js} +3 -3
  284. package/dashboard/dist/assets/{TopicDetailPage-iZW8rK9L.js.map → TopicDetailPage-DBRWne3O.js.map} +1 -1
  285. package/dashboard/dist/assets/{TopicsPage-CqiEcj-W.js → TopicsPage-CVe2M9HD.js} +2 -2
  286. package/dashboard/dist/assets/{TopicsPage-CqiEcj-W.js.map → TopicsPage-CVe2M9HD.js.map} +1 -1
  287. package/dashboard/dist/assets/{UserName-DQFjuoib.js → UserName-B_q_qp6V.js} +2 -2
  288. package/dashboard/dist/assets/{UserName-DQFjuoib.js.map → UserName-B_q_qp6V.js.map} +1 -1
  289. package/dashboard/dist/assets/WorkflowExecutionPage-Bh_dr9tM.js +2 -0
  290. package/dashboard/dist/assets/{WorkflowExecutionPage-Di08wCbs.js.map → WorkflowExecutionPage-Bh_dr9tM.js.map} +1 -1
  291. package/dashboard/dist/assets/{WorkflowPill-DJLcK_CM.js → WorkflowPill-D7244fKp.js} +2 -2
  292. package/dashboard/dist/assets/{WorkflowPill-DJLcK_CM.js.map → WorkflowPill-D7244fKp.js.map} +1 -1
  293. package/dashboard/dist/assets/{WorkflowsDashboard-Bb8HlrTJ.js → WorkflowsDashboard-DA6Mj7qH.js} +2 -2
  294. package/dashboard/dist/assets/{WorkflowsDashboard-Bb8HlrTJ.js.map → WorkflowsDashboard-DA6Mj7qH.js.map} +1 -1
  295. package/dashboard/dist/assets/{WorkflowsOverview-Dta1kurk.js → WorkflowsOverview-B1kBLj-a.js} +2 -2
  296. package/dashboard/dist/assets/{WorkflowsOverview-Dta1kurk.js.map → WorkflowsOverview-B1kBLj-a.js.map} +1 -1
  297. package/dashboard/dist/assets/{YamlWorkflowDetailPage-CfAajg68.js → YamlWorkflowDetailPage-6GJM8QIX.js} +8 -8
  298. package/dashboard/dist/assets/{YamlWorkflowDetailPage-CfAajg68.js.map → YamlWorkflowDetailPage-6GJM8QIX.js.map} +1 -1
  299. package/dashboard/dist/assets/{YamlWorkflowsPage-CQFUzT4t.js → YamlWorkflowsPage-Bu811C7f.js} +2 -2
  300. package/dashboard/dist/assets/{YamlWorkflowsPage-CQFUzT4t.js.map → YamlWorkflowsPage-Bu811C7f.js.map} +1 -1
  301. package/dashboard/dist/assets/{agents-PuftRTB2.js → agents-MPk_GnYz.js} +2 -2
  302. package/dashboard/dist/assets/{agents-PuftRTB2.js.map → agents-MPk_GnYz.js.map} +1 -1
  303. package/dashboard/dist/assets/{bots-BmEiPAWY.js → bots-2RihE2Tc.js} +2 -2
  304. package/dashboard/dist/assets/{bots-BmEiPAWY.js.map → bots-2RihE2Tc.js.map} +1 -1
  305. package/dashboard/dist/assets/{capabilities-CRii4Soa.js → capabilities-CC4iwJ1P.js} +2 -2
  306. package/dashboard/dist/assets/{capabilities-CRii4Soa.js.map → capabilities-CC4iwJ1P.js.map} +1 -1
  307. package/dashboard/dist/assets/{controlplane-DO_czaYo.js → controlplane-Bmkic8BH.js} +2 -2
  308. package/dashboard/dist/assets/{controlplane-DO_czaYo.js.map → controlplane-Bmkic8BH.js.map} +1 -1
  309. package/dashboard/dist/assets/{escalation-columns-CzCquCPh.js → escalation-columns-B0lhUUrQ.js} +2 -2
  310. package/dashboard/dist/assets/{escalation-columns-CzCquCPh.js.map → escalation-columns-B0lhUUrQ.js.map} +1 -1
  311. package/dashboard/dist/assets/{escalation-items-Cy0ZgJqV.js → escalation-items-7WFpPo46.js} +2 -2
  312. package/dashboard/dist/assets/{escalation-items-Cy0ZgJqV.js.map → escalation-items-7WFpPo46.js.map} +1 -1
  313. package/dashboard/dist/assets/index-B9uv-euR.css +1 -0
  314. package/dashboard/dist/assets/{index-DhSCCpqM.js → index-B_XV1rwu.js} +2 -2
  315. package/dashboard/dist/assets/{index-DhSCCpqM.js.map → index-B_XV1rwu.js.map} +1 -1
  316. package/dashboard/dist/assets/{index-Cx8xb-oU.js → index-C2CLfGwL.js} +2 -2
  317. package/dashboard/dist/assets/{index-Cx8xb-oU.js.map → index-C2CLfGwL.js.map} +1 -1
  318. package/dashboard/dist/assets/{index-Cga5IW3D.js → index-C3AeFihz.js} +2 -2
  319. package/dashboard/dist/assets/{index-Cga5IW3D.js.map → index-C3AeFihz.js.map} +1 -1
  320. package/dashboard/dist/assets/{index-DiaRlFYI.js → index-CBri8fYM.js} +2 -2
  321. package/dashboard/dist/assets/{index-DiaRlFYI.js.map → index-CBri8fYM.js.map} +1 -1
  322. package/dashboard/dist/assets/{index-CL_1VpGb.js → index-CRWsB7ZC.js} +2 -2
  323. package/dashboard/dist/assets/{index-CL_1VpGb.js.map → index-CRWsB7ZC.js.map} +1 -1
  324. package/dashboard/dist/assets/{index-XntldBd0.js → index-CTOv3G7a.js} +2 -2
  325. package/dashboard/dist/assets/{index-XntldBd0.js.map → index-CTOv3G7a.js.map} +1 -1
  326. package/dashboard/dist/assets/{index-CfP2nKRc.js → index-CcArGkzI.js} +3 -3
  327. package/dashboard/dist/assets/{index-CfP2nKRc.js.map → index-CcArGkzI.js.map} +1 -1
  328. package/dashboard/dist/assets/{index-Dz6R8-Dw.js → index-CkMHS-hG.js} +25 -25
  329. package/dashboard/dist/assets/index-CkMHS-hG.js.map +1 -0
  330. package/dashboard/dist/assets/index-CmsmwCyv.js +2 -0
  331. package/dashboard/dist/assets/{index-4YaDUcKd.js.map → index-CmsmwCyv.js.map} +1 -1
  332. package/dashboard/dist/assets/{index-2SM2_onB.js → index-CvJsvqIc.js} +2 -2
  333. package/dashboard/dist/assets/{index-2SM2_onB.js.map → index-CvJsvqIc.js.map} +1 -1
  334. package/dashboard/dist/assets/{index-Rwqrflm-.js → index-D-ilRyfy.js} +2 -2
  335. package/dashboard/dist/assets/{index-Rwqrflm-.js.map → index-D-ilRyfy.js.map} +1 -1
  336. package/dashboard/dist/assets/{index-CZ7A3AcD.js → index-DSUtfDXx.js} +2 -2
  337. package/dashboard/dist/assets/{index-CZ7A3AcD.js.map → index-DSUtfDXx.js.map} +1 -1
  338. package/dashboard/dist/assets/{index-jL2K6us4.js → index-Da6ycKyf.js} +2 -2
  339. package/dashboard/dist/assets/{index-jL2K6us4.js.map → index-Da6ycKyf.js.map} +1 -1
  340. package/dashboard/dist/assets/{knowledge-CeUqGPuo.js → knowledge-Bpbg20DC.js} +2 -2
  341. package/dashboard/dist/assets/{knowledge-CeUqGPuo.js.map → knowledge-Bpbg20DC.js.map} +1 -1
  342. package/dashboard/dist/assets/machines-BqOZZU-3.js +2 -0
  343. package/dashboard/dist/assets/machines-BqOZZU-3.js.map +1 -0
  344. package/dashboard/dist/assets/{mcp-Cx96NTeD.js → mcp-CmcUXYce.js} +2 -2
  345. package/dashboard/dist/assets/{mcp-Cx96NTeD.js.map → mcp-CmcUXYce.js.map} +1 -1
  346. package/dashboard/dist/assets/{mcp-query-XAzWl0gm.js → mcp-query-C8buQxN9.js} +2 -2
  347. package/dashboard/dist/assets/{mcp-query-XAzWl0gm.js.map → mcp-query-C8buQxN9.js.map} +1 -1
  348. package/dashboard/dist/assets/{personas--4kjwyOw.js → personas-C2n5oDmY.js} +2 -2
  349. package/dashboard/dist/assets/{personas--4kjwyOw.js.map → personas-C2n5oDmY.js.map} +1 -1
  350. package/dashboard/dist/assets/{pipelines-PgkwvG7f.js → pipelines-RKpSkFz7.js} +2 -2
  351. package/dashboard/dist/assets/{pipelines-PgkwvG7f.js.map → pipelines-RKpSkFz7.js.map} +1 -1
  352. package/dashboard/dist/assets/{tasks-CcYxdBa3.js → tasks-Dab4hExW.js} +2 -2
  353. package/dashboard/dist/assets/{tasks-CcYxdBa3.js.map → tasks-Dab4hExW.js.map} +1 -1
  354. package/dashboard/dist/assets/{topics-_lik7cNY.js → topics-FTMNP7r3.js} +2 -2
  355. package/dashboard/dist/assets/{topics-_lik7cNY.js.map → topics-FTMNP7r3.js.map} +1 -1
  356. package/dashboard/dist/assets/{useDebouncedValue-Bm_AoxVx.js → useDebouncedValue-B_1u1YIk.js} +2 -2
  357. package/dashboard/dist/assets/{useDebouncedValue-Bm_AoxVx.js.map → useDebouncedValue-B_1u1YIk.js.map} +1 -1
  358. package/dashboard/dist/assets/{useNamespace-DgXXKdx5.js → useNamespace-HUeAI7Qb.js} +2 -2
  359. package/dashboard/dist/assets/{useNamespace-DgXXKdx5.js.map → useNamespace-HUeAI7Qb.js.map} +1 -1
  360. package/dashboard/dist/assets/{useYamlActivityEvents-Dj2qtGSd.js → useYamlActivityEvents-Cax54qHH.js} +2 -2
  361. package/dashboard/dist/assets/{useYamlActivityEvents-Dj2qtGSd.js.map → useYamlActivityEvents-Cax54qHH.js.map} +1 -1
  362. package/dashboard/dist/assets/{validate-FlXCBBT6.js → validate-DxOaV7QS.js} +2 -2
  363. package/dashboard/dist/assets/{validate-FlXCBBT6.js.map → validate-DxOaV7QS.js.map} +1 -1
  364. package/dashboard/dist/assets/{vendor-icons-DUYcKTjP.js → vendor-icons-fxBvvHBi.js} +187 -182
  365. package/dashboard/dist/assets/vendor-icons-fxBvvHBi.js.map +1 -0
  366. package/dashboard/dist/assets/{yaml-workflows-B0_JxRYQ.js → yaml-workflows-BgY1XHJx.js} +2 -2
  367. package/dashboard/dist/assets/{yaml-workflows-B0_JxRYQ.js.map → yaml-workflows-BgY1XHJx.js.map} +1 -1
  368. package/dashboard/dist/index.html +3 -3
  369. package/docs/architecture.md +1 -0
  370. package/docs/code-owned-configuration.md +6 -0
  371. package/docs/dashboard.md +2 -1
  372. package/docs/events.md +34 -1
  373. package/docs/hitl/lookups.md +6 -4
  374. package/docs/machines.md +114 -28
  375. package/docs/mcp.md +19 -0
  376. package/package.json +1 -1
  377. package/dashboard/dist/assets/AgentsPage-C9UuUKhr.js +0 -2
  378. package/dashboard/dist/assets/AvailableEscalationsPage-DrvfXz70.js +0 -2
  379. package/dashboard/dist/assets/FilterBar-BygWrnB1.js +0 -2
  380. package/dashboard/dist/assets/FilterBar-BygWrnB1.js.map +0 -1
  381. package/dashboard/dist/assets/McpRunDetailPage-80fQn9iX.js +0 -2
  382. package/dashboard/dist/assets/WorkflowExecutionPage-Di08wCbs.js +0 -2
  383. package/dashboard/dist/assets/index-4YaDUcKd.js +0 -2
  384. package/dashboard/dist/assets/index-CnU7Jjz3.css +0 -1
  385. package/dashboard/dist/assets/index-Dz6R8-Dw.js.map +0 -1
  386. package/dashboard/dist/assets/vendor-icons-DUYcKTjP.js.map +0 -1
@@ -4,6 +4,7 @@
4
4
  * reads escalation events (a new handoff row means "dispatch"), reads every
5
5
  * machine message on the line (a receipt means "resolve"), and performs the
6
6
  * verbs a person would, as the machine. Neither side knows about the other.
7
+ * Every message read or sent is recorded on the wire (wire.ts).
7
8
  */
8
9
  Object.defineProperty(exports, "__esModule", { value: true });
9
10
  exports.MachineGateway = void 0;
@@ -15,14 +16,19 @@ const address_book_1 = require("./address-book");
15
16
  const admission_1 = require("./admission");
16
17
  const protocol_1 = require("../../../lib/machines/protocol");
17
18
  const transport_1 = require("../../../lib/machines/transport");
19
+ const publish_1 = require("../../../lib/events/publish");
18
20
  const reply_waiter_1 = require("./reply-waiter");
19
21
  const executor_1 = require("./executor");
22
+ const dispatcher_1 = require("./dispatcher");
23
+ const inspect_1 = require("./inspect");
20
24
  const log_1 = require("./log");
21
25
  const plan_escalation_1 = require("./plan-escalation");
22
26
  const pacing_1 = require("./pacing");
23
27
  const plan_machine_1 = require("./plan-machine");
24
28
  const state_1 = require("./state");
25
29
  const receipts_1 = require("./receipts");
30
+ const wire_1 = require("./wire");
31
+ const wire_window_1 = require("./wire-window");
26
32
  class MachineGateway {
27
33
  constructor(deps) {
28
34
  this.deps = deps;
@@ -32,18 +38,22 @@ class MachineGateway {
32
38
  this.codec = deps.codec ?? (0, protocol_1.genericCodec)({ root: deps.settings.topicRoot, line: deps.line });
33
39
  this.state.unitsPerDispatch = this.codec.unitsPerDispatch;
34
40
  this.state.dispatchOnArrival = this.codec.dispatchOn === protocol_1.DISPATCH_ON.ARRIVAL;
41
+ this.state.rowOps = (0, protocol_1.declaredRowOps)(this.codec);
35
42
  this.state.lifecycle = deps.settings.lifecycle === true;
36
43
  this.state.retainedAlarms = this.codec.retainedAlarms === true;
37
44
  this.state.progressStep = deps.settings.progressStep ?? this.state.progressStep;
38
45
  this.addresses = deps.addresses ?? new address_book_1.MemoryAddressBook();
46
+ this.window = new wire_window_1.WireWindow(deps.settings.wireWindow);
47
+ const sink = deps.wire ?? publish_1.publishMachineWireEvent;
48
+ this.wire = new wire_1.WireRecorder(deps.line, (event) => { this.window.push(event); sink(event); }, { telemetry: deps.settings.wireTelemetry === true });
39
49
  this.executor = new executor_1.Executor({
40
- addresses: this.addresses,
50
+ addresses: this.addresses, wire: this.wire,
41
51
  bus: deps.bus, lt: deps.lt, replies: new reply_waiter_1.ReplyWaiter(), state: this.state, line: deps.line,
42
52
  codec: this.codec, replyTimeoutMs: deps.settings.replyTimeoutMs,
43
53
  dispatchAttempts: deps.settings.dispatchAttempts, offlineDebounceMs: deps.settings.offlineDebounceMs, log: this.log,
44
54
  });
45
- this.admission = { state: this.state, line: deps.line, executor: this.executor, log: this.log, pace: () => this.pace() };
46
- this.receipts = new receipts_1.ReceiptBatcher({ bus: deps.bus, codec: this.codec, delayMs: deps.settings.receiptDelayMs ?? DEFAULT_RECEIPT_DELAY_MS, log: this.log });
55
+ this.admission = { state: this.state, line: deps.line, executor: this.executor, log: this.log, pace: () => this.pace(), openRows: (q) => deps.lt.listOpen(q) };
56
+ this.receipts = new receipts_1.ReceiptBatcher({ bus: deps.bus, codec: this.codec, delayMs: deps.settings.receiptDelayMs ?? DEFAULT_RECEIPT_DELAY_MS, log: this.log, wire: this.wire });
47
57
  }
48
58
  async start() {
49
59
  await this.deps.bus.connect();
@@ -61,10 +71,17 @@ class MachineGateway {
61
71
  }
62
72
  this.log.info(log_1.GATEWAY_LOG.STARTED, { site: this.deps.line.siteId, line: this.deps.line.lineId });
63
73
  }
74
+ /**
75
+ * Hand the line over cleanly: nothing new is raised or sent, a message
76
+ * still being handled is nak'd for the next leader if it would make a row,
77
+ * receipts for what was applied go out, then the sessions close.
78
+ */
64
79
  async stop() {
65
80
  if (this.quietTimer)
66
81
  clearInterval(this.quietTimer);
67
82
  this.executor.stop();
83
+ await this.deps.feed.unsubscribe();
84
+ await this.deps.bus.unsubscribe();
68
85
  await this.executor.idle();
69
86
  await this.receipts.flush();
70
87
  await this.deps.feed.close();
@@ -81,6 +98,10 @@ class MachineGateway {
81
98
  idle() {
82
99
  return this.executor.idle();
83
100
  }
101
+ /** A dry run of one message on a copy of the state: nothing is applied (inspect.ts). */
102
+ inspect(msg) {
103
+ return (0, inspect_1.dryRun)({ state: this.state, line: this.deps.line, codec: this.codec, dispatchTtlMs: this.deps.settings.dispatchTtlMs }, { ...msg, deliveries: 1 });
104
+ }
84
105
  /**
85
106
  * On taking the line: open handoff and command rows (a dispatch or command
86
107
  * interrupted by a restart is sent again with the same cmdId), the gateway's
@@ -138,16 +159,21 @@ class MachineGateway {
138
159
  const active = new Set();
139
160
  this.state.readingRetained = true;
140
161
  try {
141
- for (const msg of await this.deps.bus.retained((0, protocol_1.lineFilter)(this.codec.root, line.siteId, line.lineId))) {
142
- for (const raw of this.decode(msg.topic, msg.payload, true)) {
162
+ for (const read of await this.deps.bus.retained((0, protocol_1.lineFilter)(this.codec.root, line.siteId, line.lineId))) {
163
+ const msg = { ...read, retained: true };
164
+ for (const raw of this.decode(msg)) {
143
165
  if (!raw.parsed)
144
166
  continue;
145
- const seen = await (0, admission_1.admitSender)(this.admission, msg.topic, raw);
146
- if (!('parsed' in seen) || !seen.parsed)
167
+ const admitted = await (0, admission_1.admitSender)(this.admission, msg.topic, raw);
168
+ if (!('seen' in admitted)) {
169
+ this.wire.inbound(msg, raw, admitted, 1, admitted.sender);
147
170
  continue;
171
+ }
172
+ const { seen } = admitted;
148
173
  if (seen.parsed.kind === protocol_1.MESSAGE_KIND.ALARM && seen.body?.body?.active)
149
174
  active.add(this.state.alarmKey(seen.parsed.ident.machineId, seen.parsed.key));
150
- await this.applyMachine(msg.topic, seen.parsed, seen.body);
175
+ const applied = await this.applyMachine(msg.topic, seen.parsed, seen.body);
176
+ this.wire.inbound(msg, seen, { ...applied, actions: [...admitted.actions, ...applied.actions] }, 1);
151
177
  }
152
178
  }
153
179
  }
@@ -170,7 +196,8 @@ class MachineGateway {
170
196
  return transport_1.DISPOSITION.ACK;
171
197
  }
172
198
  catch (err) {
173
- this.log.error(log_1.GATEWAY_LOG.VERB_FAILED, { event: event.type, error: err.message });
199
+ if (!(err instanceof dispatcher_1.GatewayStopped))
200
+ this.log.error(log_1.GATEWAY_LOG.VERB_FAILED, { event: event.type, error: err.message });
174
201
  return transport_1.DISPOSITION.NAK;
175
202
  }
176
203
  }
@@ -188,31 +215,39 @@ class MachineGateway {
188
215
  return { ...event, role: row.role, data: row };
189
216
  }
190
217
  async onMachine(msg) {
191
- const decoded = this.decode(msg.topic, msg.payload, msg.retained);
218
+ const decoded = this.decode(msg);
192
219
  const acted = [];
193
220
  let outcome = transport_1.DISPOSITION.TERM;
194
221
  let failed = false;
195
222
  for (const raw of decoded) {
196
223
  if (!raw.parsed)
197
224
  continue;
198
- const seen = await (0, admission_1.admitSender)(this.admission, msg.topic, raw);
199
- if (!('parsed' in seen)) {
200
- failed ||= seen.failed;
201
- outcome = worse(outcome, seen.disposition);
225
+ const admitted = await (0, admission_1.admitSender)(this.admission, msg.topic, raw);
226
+ if (!('seen' in admitted)) {
227
+ failed ||= admitted.failed;
228
+ outcome = worse(outcome, admitted.disposition);
229
+ this.wire.inbound(msg, raw, admitted, msg.deliveries, admitted.sender);
202
230
  continue;
203
231
  }
232
+ const { seen } = admitted;
204
233
  acted.push(seen);
205
234
  if (seen.violations.length && msg.deliveries === 1) {
206
235
  this.log.warn(log_1.GATEWAY_LOG.CONTRACT_VIOLATION, { topic: msg.topic, violations: seen.violations });
207
236
  }
208
- const applied = seen.violations.length && this.codec.strict
237
+ let applied = seen.violations.length && this.codec.strict
209
238
  ? await (0, admission_1.holdViolation)(this.admission, msg, seen)
210
239
  : await this.applyMachine(msg.topic, seen.parsed, seen.body);
240
+ // An arrival still waiting for its row after every delivery is shown to a person instead; one that read no id at once.
241
+ const waited = msg.deliveries >= wire_1.MAX_DELIVERIES || (0, admission_1.isUnreadableArrival)(seen);
242
+ if (applied.disposition === transport_1.DISPOSITION.NAK && !applied.failed && waited && (0, admission_1.isArrival)(seen)) {
243
+ applied = await (0, admission_1.holdArrival)(this.admission, msg, seen);
244
+ }
211
245
  failed ||= applied.failed;
212
246
  outcome = worse(outcome, applied.disposition);
247
+ this.wire.inbound(msg, seen, { ...applied, actions: [...admitted.actions, ...applied.actions] }, msg.deliveries);
213
248
  }
214
249
  // No row has claimed it after this many tries, so it is not racing its row: stop. A failed verb always retries.
215
- if (outcome === transport_1.DISPOSITION.NAK && !failed && msg.deliveries >= MAX_DELIVERIES) {
250
+ if (outcome === transport_1.DISPOSITION.NAK && !failed && msg.deliveries >= wire_1.MAX_DELIVERIES) {
216
251
  this.log.warn(log_1.GATEWAY_LOG.UNMATCHED, { topic: msg.topic, deliveries: msg.deliveries });
217
252
  outcome = transport_1.DISPOSITION.TERM;
218
253
  }
@@ -221,14 +256,19 @@ class MachineGateway {
221
256
  return outcome;
222
257
  }
223
258
  /** A message the codec cannot read is not retried: it is logged and has no effect. */
224
- decode(topic, payload, retained) {
259
+ decode(msg) {
260
+ let decoded;
225
261
  try {
226
- return this.codec.decode(topic, payload, retained);
262
+ decoded = this.codec.decode(msg.topic, msg.payload, msg.retained);
227
263
  }
228
264
  catch (err) {
229
- this.log.warn(log_1.GATEWAY_LOG.UNREADABLE, { topic, error: err.message });
265
+ this.log.warn(log_1.GATEWAY_LOG.UNREADABLE, { topic: msg.topic, error: err.message });
266
+ this.wire.unreadable(msg, err.message);
230
267
  return [];
231
268
  }
269
+ if (!decoded.length)
270
+ this.wire.unreadable(msg, 'not a machine message', 'none');
271
+ return decoded;
232
272
  }
233
273
  /** Done with the message: confirm each id its codec asked to confirm. */
234
274
  confirm(decoded) {
@@ -247,21 +287,21 @@ class MachineGateway {
247
287
  }
248
288
  catch (err) {
249
289
  this.log.warn(log_1.GATEWAY_LOG.UNREADABLE, { topic, error: err.message });
250
- return { disposition: transport_1.DISPOSITION.TERM, failed: false };
290
+ return { disposition: transport_1.DISPOSITION.TERM, failed: false, actions: [{ verb: 'unreadable', detail: err.message }] };
251
291
  }
252
292
  try {
253
- await this.executor.apply(plan.actions);
293
+ const actions = await this.executor.apply(plan.actions);
254
294
  await this.pace();
255
- return { disposition: plan.disposition, failed: false };
295
+ return { disposition: plan.disposition, failed: false, actions };
256
296
  }
257
297
  catch (err) {
258
- this.log.error(log_1.GATEWAY_LOG.VERB_FAILED, { topic, error: err.message });
259
- return { disposition: transport_1.DISPOSITION.NAK, failed: true };
298
+ if (!(err instanceof dispatcher_1.GatewayStopped))
299
+ this.log.error(log_1.GATEWAY_LOG.VERB_FAILED, { topic, error: err.message });
300
+ return { disposition: transport_1.DISPOSITION.NAK, failed: true, actions: [] };
260
301
  }
261
302
  }
262
303
  }
263
304
  exports.MachineGateway = MachineGateway;
264
- const MAX_DELIVERIES = 30;
265
305
  const DEFAULT_RECEIPT_DELAY_MS = 250;
266
306
  const RANK = { [transport_1.DISPOSITION.TERM]: 0, [transport_1.DISPOSITION.ACK]: 1, [transport_1.DISPOSITION.NAK]: 2 };
267
307
  /** One wire message may decode to several; any that must be retried retries the message. */
@@ -8,3 +8,6 @@ export * from './provision';
8
8
  export * from './state';
9
9
  export * from './address-book';
10
10
  export * from './registration';
11
+ export * from './wire';
12
+ export * from './project';
13
+ export * from './inspect';
@@ -24,3 +24,6 @@ __exportStar(require("./provision"), exports);
24
24
  __exportStar(require("./state"), exports);
25
25
  __exportStar(require("./address-book"), exports);
26
26
  __exportStar(require("./registration"), exports);
27
+ __exportStar(require("./wire"), exports);
28
+ __exportStar(require("./project"), exports);
29
+ __exportStar(require("./inspect"), exports);
@@ -0,0 +1,17 @@
1
+ /**
2
+ * A dry run of one wire message: what the codec reads and what the gateway
3
+ * would do, planned on a copy of the state so the line is untouched and no
4
+ * row is created or changed.
5
+ */
6
+ import type { LineDef } from '../../../lib/machines/line';
7
+ import type { LineCodec } from '../../../lib/machines/protocol';
8
+ import type { InboundMessage } from '../../../lib/machines/transport';
9
+ import type { InspectResult } from '../../../types/machines-view';
10
+ import type { GatewayState } from './state';
11
+ export interface DryRunContext {
12
+ state: GatewayState;
13
+ line: LineDef;
14
+ codec: LineCodec;
15
+ dispatchTtlMs: number;
16
+ }
17
+ export declare function dryRun(ctx: DryRunContext, msg: InboundMessage): InspectResult;
@@ -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
+ }
@@ -1,11 +1,14 @@
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";
4
5
  readonly DISPATCHED: "machine_gateway.dispatch.accepted";
5
6
  readonly DISPATCH_REFUSED: "machine_gateway.dispatch.refused";
6
7
  readonly DISPATCH_READY: "machine_gateway.dispatch.ready";
8
+ readonly DISPATCH_PREPARING: "machine_gateway.dispatch.preparing";
7
9
  readonly DISPATCH_FAILED: "machine_gateway.dispatch.failed";
8
10
  readonly DISPATCH_RECONCILED: "machine_gateway.dispatch.reconciled";
11
+ readonly DISPATCH_UNRECOVERABLE: "machine_gateway.dispatch.unrecoverable";
9
12
  readonly VERB_INVALID: "machine_gateway.verb.invalid";
10
13
  readonly VERB_FAILED: "machine_gateway.verb.failed";
11
14
  readonly ROW_NOT_VISIBLE: "machine_gateway.verb.row_not_visible";
@@ -39,3 +42,10 @@ export interface GatewayLog {
39
42
  }
40
43
  export declare const consoleLog: GatewayLog;
41
44
  export declare const silentLog: GatewayLog;
45
+ /** Each entry published as `machine.log.<site>.<line>.<event>` with its context, for the line's dashboard view. */
46
+ export declare function eventsLog(line: {
47
+ siteId: string;
48
+ lineId: string;
49
+ }, publish?: typeof publishMachineLogEvent): GatewayLog;
50
+ /** Every entry goes to each log in turn. */
51
+ export declare function composeLogs(...logs: GatewayLog[]): GatewayLog;
@@ -1,14 +1,19 @@
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',
7
10
  DISPATCHED: 'machine_gateway.dispatch.accepted',
8
11
  DISPATCH_REFUSED: 'machine_gateway.dispatch.refused',
9
12
  DISPATCH_READY: 'machine_gateway.dispatch.ready',
13
+ DISPATCH_PREPARING: 'machine_gateway.dispatch.preparing',
10
14
  DISPATCH_FAILED: 'machine_gateway.dispatch.failed',
11
15
  DISPATCH_RECONCILED: 'machine_gateway.dispatch.reconciled',
16
+ DISPATCH_UNRECOVERABLE: 'machine_gateway.dispatch.unrecoverable',
12
17
  VERB_INVALID: 'machine_gateway.verb.invalid',
13
18
  VERB_FAILED: 'machine_gateway.verb.failed',
14
19
  ROW_NOT_VISIBLE: 'machine_gateway.verb.row_not_visible',
@@ -40,3 +45,18 @@ exports.consoleLog = {
40
45
  error: (event, context) => console.error(JSON.stringify({ level: 'error', event, ...context })),
41
46
  };
42
47
  exports.silentLog = { info: () => undefined, warn: () => undefined, error: () => undefined };
48
+ /** Each entry published as `machine.log.<site>.<line>.<event>` with its context, for the line's dashboard view. */
49
+ function eventsLog(line, publish = publish_1.publishMachineLogEvent) {
50
+ const emit = (level) => (event, context) => {
51
+ publish({ siteId: line.siteId, lineId: line.lineId, level, event, context }).catch(() => undefined);
52
+ };
53
+ return { info: emit('info'), warn: emit('warn'), error: emit('error') };
54
+ }
55
+ /** Every entry goes to each log in turn. */
56
+ function composeLogs(...logs) {
57
+ const each = (level) => (event, context) => {
58
+ for (const log of logs)
59
+ log[level](event, context);
60
+ };
61
+ return { info: each('info'), warn: each('warn'), error: each('error') };
62
+ }
@@ -56,6 +56,8 @@ class LtHttp {
56
56
  }
57
57
  }
58
58
  exports.LtHttp = LtHttp;
59
+ /** A row that was never given items (one parked by hand) answers 400; it holds none. */
60
+ const NO_ITEMS_STATUS = 400;
59
61
  /** Escalation verbs over HTTP, each run with the bearer token of the actor it runs as. */
60
62
  class HttpLineEscalations {
61
63
  constructor(http, tokenFor, claimMinutes = 24 * 60) {
@@ -80,8 +82,15 @@ class HttpLineEscalations {
80
82
  return (await this.items(id)).map((i) => i.itemKey);
81
83
  }
82
84
  async items(id) {
83
- const view = await this.http.request('GET', `/escalations/${id}/items`, this.tokenFor(actions_1.CONTROLLER));
84
- return view.items.map((i) => ({ itemKey: i.itemKey, payload: i.payload ?? {} }));
85
+ try {
86
+ const view = await this.http.request('GET', `/escalations/${id}/items`, this.tokenFor(actions_1.CONTROLLER));
87
+ return view.items.map((i) => ({ itemKey: i.itemKey, payload: i.payload ?? {} }));
88
+ }
89
+ catch (err) {
90
+ if (err instanceof LtHttpError && err.status === NO_ITEMS_STATUS)
91
+ return [];
92
+ throw err;
93
+ }
85
94
  }
86
95
  async get(id) {
87
96
  if (!(0, uuid_1.isUuid)(id))
@@ -39,6 +39,7 @@ function dispatchArgs(row, expiresAt) {
39
39
  expiresAt: (0, protocol_1.isoTs)(expiresAt),
40
40
  ...(row.artifactUrl ? { artifact: { url: row.artifactUrl, sha256: row.artifactSha256 } } : {}),
41
41
  ...(row.parameters ? { parameters: row.parameters } : {}),
42
+ ...(row.refs ? { refs: row.refs } : {}),
42
43
  };
43
44
  }
44
45
  /**
@@ -65,7 +66,7 @@ function dispatchAction(row, machineId, dispatchTtlMs) {
65
66
  row.createdAt ??= now;
66
67
  const { round, expiresAt } = dispatchWindow(row.dueAt ?? row.createdAt, now, dispatchTtlMs);
67
68
  return {
68
- kind: 'dispatch', rowId: row.id, machineId, cmdId: (0, contracts_1.dispatchCommandId)(row.id, round), args: dispatchArgs(row, expiresAt),
69
+ kind: 'dispatch', rowId: row.id, machineId, cmdId: (0, contracts_1.dispatchCommandId)(row.id, round), args: dispatchArgs(row, expiresAt), issuedAt: expiresAt - dispatchTtlMs,
69
70
  ...(row.dispatch === state_1.DISPATCH_STATE.ARRIVING ? { receivedOnAccept: true } : {}),
70
71
  };
71
72
  }
@@ -93,8 +93,9 @@ function commandRefusal(state, line, c, role) {
93
93
  const own = (0, line_1.machineRole)((0, line_1.machineById)(line, c.machineId));
94
94
  if (role !== own)
95
95
  return `commands for ${c.machineId} are parked on ${own}, not ${role}`;
96
- if (!protocol_1.CONTROL_OPS.includes(c.op))
97
- 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(', ')}`;
98
99
  if (c.op === protocol_1.OP.CANCEL && typeof c.args.jobId !== 'string')
99
100
  return `${protocol_1.OP.CANCEL} needs args.jobId, the job to withdraw or stop`;
100
101
  if (!(0, registration_1.reachable)(state, line, c.machineId))
@@ -110,7 +111,7 @@ function planCommandRow(state, line, event) {
110
111
  if (event.verb !== VERB.CREATED && event.verb !== VERB.CLAIMED)
111
112
  return [];
112
113
  const c = (0, commands_1.readCommandFacets)(event.data?.metadata);
113
- if (!c || !state.trackCommand(id))
114
+ if (!c || !state.trackCommand(id, c.machineId))
114
115
  return [];
115
116
  const refusal = commandRefusal(state, line, c, event.role);
116
117
  if (refusal) {
@@ -119,6 +120,10 @@ function planCommandRow(state, line, event) {
119
120
  const createdAt = event.data?.created_at ? new Date(event.data.created_at).getTime() : NaN;
120
121
  return [{ kind: 'control', rowId: id, machineId: c.machineId, op: c.op, args: c.args, ...(Number.isFinite(createdAt) ? { createdAt } : {}) }];
121
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];
126
+ }
122
127
  function onLine(line, machineId) {
123
128
  return !!machineId && line.machines.some((m) => m.machineId === machineId);
124
129
  }
@@ -36,7 +36,10 @@ function planMachineMessage(state, parsed, env, dispatchTtlMs) {
36
36
  function planByKind(state, m, parsed, env, b, ttl) {
37
37
  switch (parsed.kind) {
38
38
  case protocol_1.MESSAGE_KIND.REPLY:
39
- return ack([{ kind: 'settleReply', cmdId: b.cmdId, status: b.status, reason: b.reason }]);
39
+ return ack([{
40
+ kind: 'settleReply', cmdId: b.cmdId, status: b.status, reason: b.reason,
41
+ ...(typeof b.code === 'string' ? { code: b.code } : {}), ...(b.preparing === true ? { preparing: true } : {}),
42
+ }]);
40
43
  case protocol_1.MESSAGE_KIND.JOB:
41
44
  return planJob(state, m, b, env?.at, ttl);
42
45
  case protocol_1.MESSAGE_KIND.ALARM:
@@ -139,7 +142,8 @@ function planJob(state, m, b, at, ttl) {
139
142
  if (b.result === protocol_1.JOB_RESULT.OK)
140
143
  return ack([released]);
141
144
  return ack([released, ...(0, plan_shared_1.failHandoff)(state, row, m, {
142
- received: false, unitId, errorCode: contracts_1.HANDOFF_ERROR.SENDER_REJECTED, reason: b.reason ?? null, detail: `${m} finished ${unitId}: ${b.result}${b.reason ? ` (${b.reason})` : ''}`,
145
+ received: false, unitId, errorCode: contracts_1.HANDOFF_ERROR.SENDER_REJECTED, result: b.result, reason: b.reason ?? null,
146
+ detail: `${m} finished ${unitId}: ${b.result}${b.reason ? ` (${b.reason})` : ''}`,
143
147
  })]);
144
148
  }
145
149
  if (b.event === protocol_1.JOB_EVENT.TRANSFER_FAILED) {
@@ -190,7 +194,9 @@ function planAlarm(state, parsed, alarmId, env) {
190
194
  payload: { decision: contracts_1.DECISION.CLEARED, clearedBy: b.clearedBy ?? null, note: `cleared at ${m}${b.clearedBy ? ` by ${b.clearedBy}` : ''}` },
191
195
  }]);
192
196
  }
193
- if (!protocol_1.STOPPING_LEVELS.includes(b.level))
197
+ // A stopping level needs a person; so does any alarm the machine flags, at the level it said.
198
+ const stopping = protocol_1.STOPPING_LEVELS.includes(b.level);
199
+ if (!stopping && b.needsPerson !== true)
194
200
  return ack();
195
201
  const known = state.alarms.get(key);
196
202
  if (known && (!known.raisedAt || !b.raisedAt || known.raisedAt === b.raisedAt))
@@ -198,9 +204,9 @@ function planAlarm(state, parsed, alarmId, env) {
198
204
  const superseded = known?.rowId && known.open
199
205
  ? [{ kind: 'resolve', as: actions_1.CONTROLLER, rowId: known.rowId, payload: { decision: contracts_1.DECISION.CLEARED, note: `${m} raised ${alarmId} again; this one ended` } }]
200
206
  : [];
201
- const track = { machineId: m, alarmId, raisedAt: b.raisedAt, open: true };
207
+ const track = { machineId: m, alarmId, raisedAt: b.raisedAt, level: b.level, open: true };
202
208
  state.alarms.set(key, track);
203
- return ack([...superseded, ...(0, lifecycle_1.notePause)(state, track, b), {
209
+ return ack([...superseded, ...(stopping ? (0, lifecycle_1.notePause)(state, track, b) : []), {
204
210
  kind: 'createAlarmRow', machineId: m, alarmId,
205
211
  row: {
206
212
  type: contracts_1.ROW_TYPE.ALARM, subtype: b.level, role: contracts_1.LINE_ROLE.OPERATOR, priority: b.level === protocol_1.LEVEL.EMERGENCY ? 1 : 2,
@@ -222,10 +228,12 @@ function catalogEnvelope(catalog) {
222
228
  }
223
229
  function planPresence(state, m, b) {
224
230
  const link = state.link(m);
231
+ const session = typeof b.sessionId === 'string' ? b.sessionId : undefined;
225
232
  if (b.online !== true) {
226
233
  if (link.online)
227
234
  link.since = Date.now();
228
235
  link.online = false;
236
+ link.reason = typeof b.reason === 'string' ? b.reason : undefined;
229
237
  if (link.rowId || link.watching)
230
238
  return ack();
231
239
  link.watching = true;
@@ -234,37 +242,54 @@ function planPresence(state, m, b) {
234
242
  if (b.software)
235
243
  link.software = b.software;
236
244
  const wasOffline = !link.online;
245
+ // A new session while online: the machine reconnected before its offline presence was heard.
246
+ const reconnected = !!session && !!link.sessionId && session !== link.sessionId;
237
247
  link.online = true;
238
248
  link.watching = false;
249
+ link.reason = undefined;
250
+ if (session)
251
+ link.sessionId = session;
239
252
  const actions = [];
240
253
  // Back from a drop: ask for the full state rather than wait for the next heartbeat.
241
- if (wasOffline)
242
- actions.push({ kind: 'command', machineId: m, op: protocol_1.OP.SNAPSHOT, cmdId: `${m}:resync:${link.since}`, args: {} });
254
+ if (wasOffline || reconnected)
255
+ actions.push({ kind: 'command', machineId: m, op: protocol_1.OP.SNAPSHOT, cmdId: `${m}:resync:${session ?? link.since}`, args: {} });
243
256
  if (link.rowId) {
244
257
  actions.push({ kind: 'resolve', as: actions_1.CONTROLLER, rowId: link.rowId, payload: { decision: contracts_1.DECISION.RESTORED, note: `${m} back online` } });
245
258
  link.rowId = undefined;
246
259
  }
247
260
  return ack(actions);
248
261
  }
249
- /** A new boot id means the machine restarted and lost its queue: send its open dispatches again. */
262
+ /**
263
+ * A new boot id means the machine restarted and lost its queue: send its open
264
+ * dispatches again, and the jobs it had ready for units it still holds, from
265
+ * the rows that sent them. A lost job nothing is remembered for is logged.
266
+ */
250
267
  function planSnapshot(state, m, b, ttl) {
251
268
  const link = state.link(m);
252
269
  if (b.software)
253
270
  link.software = b.software;
254
271
  const jobIds = Array.isArray(b.jobs) ? b.jobs.map((j) => j?.jobId).filter((id) => typeof id === 'string') : [];
255
- if (Array.isArray(b.jobs))
256
- state.keepHeld(m, new Set(jobIds));
272
+ // A snapshot without a boot id says nothing about restarts.
273
+ const rebooted = typeof b.bootId === 'string' && !!link.bootId && link.bootId !== b.bootId;
274
+ const held = new Set(jobIds);
275
+ const lost = rebooted ? state.lostAfterReboot(m, held) : { resend: [], unknown: [] };
276
+ if (Array.isArray(b.jobs)) {
277
+ state.keepHeld(m, new Set([...jobIds, ...lost.resend.map((l) => l.jobId)]));
278
+ link.jobs = b.jobs;
279
+ }
280
+ if (typeof b.bootId === 'string')
281
+ link.bootId = b.bootId;
257
282
  const fresh = !state.retainedAlarms && !state.readingRetained && Array.isArray(b.alarms);
258
283
  const cleared = fresh ? alarmsNoLongerListed(state, m, b.alarms) : [];
259
284
  const answer = { kind: 'settleSnapshot', machineId: m, jobIds };
260
- const previous = link.bootId;
261
- link.bootId = b.bootId;
262
- if (!previous || previous === b.bootId)
285
+ if (!rebooted)
263
286
  return ack([...cleared, answer]);
264
- const held = new Set(jobIds);
265
287
  const missing = (r) => (0, jobs_1.dispatchJobs)(r.jobId, r.units, state.unitsPerDispatch).some((j) => !held.has(j.jobId));
266
- const lost = state.open().filter((r) => r.toMachine === m && r.dispatch === state_1.DISPATCH_STATE.ACCEPTED && missing(r));
267
- return ack([...cleared, answer, ...lost.map((r) => (0, pacing_1.dispatchAction)(r, m, ttl))]);
288
+ const open = state.open().filter((r) => r.toMachine === m && r.dispatch === state_1.DISPATCH_STATE.ACCEPTED && missing(r));
289
+ const rows = new Map(lost.resend.map((l) => [l.row.id, l.row]));
290
+ const again = [...rows.values()].map((r) => ({ ...(0, pacing_1.dispatchAction)(r, m, ttl), ended: true }));
291
+ const unknown = lost.unknown.length ? [{ kind: 'unrecoverable', machineId: m, jobs: lost.unknown }] : [];
292
+ return ack([...cleared, answer, ...open.map((r) => (0, pacing_1.dispatchAction)(r, m, ttl)), ...again, ...unknown]);
268
293
  }
269
294
  /** Alarm rows for `m` whose alarm its snapshot no longer lists: they cleared at the machine. */
270
295
  function alarmsNoLongerListed(state, m, listed) {
@@ -5,9 +5,11 @@ import { type GatewayState, type HandoffRow } from './state';
5
5
  /** A cancel for each job a handoff's units became at `machineId`, or for those `keep` leaves out. */
6
6
  export declare function cancelJobs(state: GatewayState, row: HandoffRow, machineId: string, jobId: string, tag?: string, keep?: (job: DispatchJob) => boolean): Action[];
7
7
  /**
8
- * Withdraw a dispatch that may still sit in the receiver's queue. A job some
9
- * of whose units already reached the receiver is under way: it stays, and
10
- * what happens to it is the workflow's next decision (a machine-command row).
8
+ * Withdraw a dispatch that may still sit in the receiver's queue: one sent or
9
+ * accepted. A job that never went out (the row waiting for room, or for its
10
+ * units to arrive) leaves the receiver nothing to withdraw. A job some of whose
11
+ * units already reached the receiver is under way: it stays, and what happens
12
+ * to it is the workflow's next decision (a machine-command row).
11
13
  */
12
14
  export declare function cancelDispatch(state: GatewayState, row: HandoffRow): Action[];
13
15
  /**
@@ -14,13 +14,15 @@ function cancelJobs(state, row, machineId, jobId, tag = '', keep = () => false)
14
14
  }));
15
15
  }
16
16
  /**
17
- * Withdraw a dispatch that may still sit in the receiver's queue. A job some
18
- * of whose units already reached the receiver is under way: it stays, and
19
- * what happens to it is the workflow's next decision (a machine-command row).
17
+ * Withdraw a dispatch that may still sit in the receiver's queue: one sent or
18
+ * accepted. A job that never went out (the row waiting for room, or for its
19
+ * units to arrive) leaves the receiver nothing to withdraw. A job some of whose
20
+ * units already reached the receiver is under way: it stays, and what happens
21
+ * to it is the workflow's next decision (a machine-command row).
20
22
  */
21
23
  function cancelDispatch(state, row) {
22
24
  const to = row.toMachine;
23
- if (!to || row.dispatch === state_1.DISPATCH_STATE.NONE || row.dispatch === state_1.DISPATCH_STATE.REFUSED)
25
+ if (!to || (row.dispatch !== state_1.DISPATCH_STATE.PENDING && row.dispatch !== state_1.DISPATCH_STATE.ACCEPTED))
24
26
  return [];
25
27
  return cancelJobs(state, row, to, row.jobId, '', (job) => job.units.some((u) => state.heldJob(to, u.unitId) === job.jobId));
26
28
  }
@@ -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;