@hotmeshio/long-tail 0.5.6 → 0.5.9

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 (197) hide show
  1. package/build/api/diagnostics.d.ts +15 -0
  2. package/build/api/diagnostics.js +33 -0
  3. package/build/api/escalations/cancel.d.ts +20 -0
  4. package/build/api/escalations/cancel.js +93 -0
  5. package/build/api/escalations/index.d.ts +1 -0
  6. package/build/api/escalations/index.js +4 -1
  7. package/build/api/escalations/resolve.js +2 -0
  8. package/build/api/workflows/invocation.js +6 -0
  9. package/build/lib/events/publish.d.ts +1 -1
  10. package/build/routes/diagnostics.d.ts +2 -0
  11. package/build/routes/diagnostics.js +88 -0
  12. package/build/routes/escalations/bulk.js +4 -0
  13. package/build/routes/escalations/single.js +9 -0
  14. package/build/routes/index.js +2 -0
  15. package/build/services/diagnostics/index.d.ts +52 -0
  16. package/build/services/diagnostics/index.js +169 -0
  17. package/build/services/diagnostics/patterns.d.ts +19 -0
  18. package/build/services/diagnostics/patterns.js +147 -0
  19. package/build/services/escalation/bulk.d.ts +9 -0
  20. package/build/services/escalation/bulk.js +21 -0
  21. package/build/services/escalation/crud.d.ts +17 -0
  22. package/build/services/escalation/crud.js +57 -0
  23. package/build/services/orchestrator/condition.d.ts +1 -1
  24. package/build/services/orchestrator/condition.js +3 -0
  25. package/build/services/yaml-workflow/deployer.js +5 -0
  26. package/build/services/yaml-workflow/pipe-validator.d.ts +44 -0
  27. package/build/services/yaml-workflow/pipe-validator.js +215 -0
  28. package/build/system/mcp-servers/admin/diagnostics.d.ts +10 -0
  29. package/build/system/mcp-servers/admin/diagnostics.js +94 -0
  30. package/build/system/mcp-servers/admin/index.js +4 -2
  31. package/build/system/mcp-servers/admin/schemas.d.ts +36 -0
  32. package/build/system/mcp-servers/admin/schemas.js +16 -1
  33. package/build/system/workflows/mcp-workflow-builder/index.js +15 -2
  34. package/build/tsconfig.tsbuildinfo +1 -1
  35. package/dashboard/dist/assets/AdminDashboard-0LSslIQ7.js +2 -0
  36. package/dashboard/dist/assets/{AdminDashboard-DIQEViVY.js.map → AdminDashboard-0LSslIQ7.js.map} +1 -1
  37. package/dashboard/dist/assets/{AgentConfigPage-Caf5-th-.js → AgentConfigPage-hqBr1540.js} +2 -2
  38. package/dashboard/dist/assets/{AgentConfigPage-Caf5-th-.js.map → AgentConfigPage-hqBr1540.js.map} +1 -1
  39. package/dashboard/dist/assets/{AgentDetailPage-_a4tKaM3.js → AgentDetailPage-bnXR1ezD.js} +2 -2
  40. package/dashboard/dist/assets/{AgentDetailPage-_a4tKaM3.js.map → AgentDetailPage-bnXR1ezD.js.map} +1 -1
  41. package/dashboard/dist/assets/{AgentsPage-BNlAkEIK.js → AgentsPage-tJIlrnfM.js} +2 -2
  42. package/dashboard/dist/assets/{AgentsPage-BNlAkEIK.js.map → AgentsPage-tJIlrnfM.js.map} +1 -1
  43. package/dashboard/dist/assets/AvailableEscalationsPage-B7gEfcw3.js +2 -0
  44. package/dashboard/dist/assets/AvailableEscalationsPage-B7gEfcw3.js.map +1 -0
  45. package/dashboard/dist/assets/{BotPicker-DBvd1yFL.js → BotPicker-fFE4GAaO.js} +2 -2
  46. package/dashboard/dist/assets/{BotPicker-DBvd1yFL.js.map → BotPicker-fFE4GAaO.js.map} +1 -1
  47. package/dashboard/dist/assets/{CapabilitiesPage-BIdG9RTG.js → CapabilitiesPage-DotZ7iQc.js} +2 -2
  48. package/dashboard/dist/assets/{CapabilitiesPage-BIdG9RTG.js.map → CapabilitiesPage-DotZ7iQc.js.map} +1 -1
  49. package/dashboard/dist/assets/{CollapsibleSection-CggyKhyZ.js → CollapsibleSection-D97SYypF.js} +2 -2
  50. package/dashboard/dist/assets/{CollapsibleSection-CggyKhyZ.js.map → CollapsibleSection-D97SYypF.js.map} +1 -1
  51. package/dashboard/dist/assets/{CredentialsPage-B4sXGLRY.js → CredentialsPage-C08U6dlv.js} +2 -2
  52. package/dashboard/dist/assets/{CredentialsPage-B4sXGLRY.js.map → CredentialsPage-C08U6dlv.js.map} +1 -1
  53. package/dashboard/dist/assets/{CronLabel-GcS5qzZr.js → CronLabel-C2TuCl4h.js} +2 -2
  54. package/dashboard/dist/assets/{CronLabel-GcS5qzZr.js.map → CronLabel-C2TuCl4h.js.map} +1 -1
  55. package/dashboard/dist/assets/CustomDurationPicker-C3zGsESx.js +2 -0
  56. package/dashboard/dist/assets/CustomDurationPicker-C3zGsESx.js.map +1 -0
  57. package/dashboard/dist/assets/{ElapsedCell-BFOMsgwF.js → ElapsedCell-DF4m0cv_.js} +2 -2
  58. package/dashboard/dist/assets/{ElapsedCell-BFOMsgwF.js.map → ElapsedCell-DF4m0cv_.js.map} +1 -1
  59. package/dashboard/dist/assets/{EscalationsOverview-BfjW6haL.js → EscalationsOverview-blGOYKtJ.js} +2 -2
  60. package/dashboard/dist/assets/{EscalationsOverview-BfjW6haL.js.map → EscalationsOverview-blGOYKtJ.js.map} +1 -1
  61. package/dashboard/dist/assets/{EventTable-N0XmLk3C.js → EventTable-DzrMH-ux.js} +2 -2
  62. package/dashboard/dist/assets/{EventTable-N0XmLk3C.js.map → EventTable-DzrMH-ux.js.map} +1 -1
  63. package/dashboard/dist/assets/{GraphInvokePage-DHiGHiCk.js → GraphInvokePage-QVAUHmjI.js} +2 -2
  64. package/dashboard/dist/assets/{GraphInvokePage-DHiGHiCk.js.map → GraphInvokePage-QVAUHmjI.js.map} +1 -1
  65. package/dashboard/dist/assets/{HomePage-jckQolbx.js → HomePage-DvYgvCcO.js} +2 -2
  66. package/dashboard/dist/assets/{HomePage-jckQolbx.js.map → HomePage-DvYgvCcO.js.map} +1 -1
  67. package/dashboard/dist/assets/{ListToolbar-B71DqaXr.js → ListToolbar-D6tQ3ZG2.js} +2 -2
  68. package/dashboard/dist/assets/{ListToolbar-B71DqaXr.js.map → ListToolbar-D6tQ3ZG2.js.map} +1 -1
  69. package/dashboard/dist/assets/{McpOverview-CPD6NRGt.js → McpOverview-BClbGYb8.js} +2 -2
  70. package/dashboard/dist/assets/{McpOverview-CPD6NRGt.js.map → McpOverview-BClbGYb8.js.map} +1 -1
  71. package/dashboard/dist/assets/{McpQueryDetailPage-YyqXw6Dt.js → McpQueryDetailPage-DHjfDRdm.js} +2 -2
  72. package/dashboard/dist/assets/{McpQueryDetailPage-YyqXw6Dt.js.map → McpQueryDetailPage-DHjfDRdm.js.map} +1 -1
  73. package/dashboard/dist/assets/{McpQueryPage-BWf45EYD.js → McpQueryPage-qtEuMDrd.js} +2 -2
  74. package/dashboard/dist/assets/{McpQueryPage-BWf45EYD.js.map → McpQueryPage-qtEuMDrd.js.map} +1 -1
  75. package/dashboard/dist/assets/{McpRunDetailPage-Cv43tkjz.js → McpRunDetailPage-DRbIZMU2.js} +2 -2
  76. package/dashboard/dist/assets/{McpRunDetailPage-Cv43tkjz.js.map → McpRunDetailPage-DRbIZMU2.js.map} +1 -1
  77. package/dashboard/dist/assets/{McpRunsPage-BFards4K.js → McpRunsPage-CMfwXQ4R.js} +2 -2
  78. package/dashboard/dist/assets/{McpRunsPage-BFards4K.js.map → McpRunsPage-CMfwXQ4R.js.map} +1 -1
  79. package/dashboard/dist/assets/{OperatorDashboard-BJZxuOKI.js → OperatorDashboard-Dq2pfV68.js} +2 -2
  80. package/dashboard/dist/assets/{OperatorDashboard-BJZxuOKI.js.map → OperatorDashboard-Dq2pfV68.js.map} +1 -1
  81. package/dashboard/dist/assets/{ProcessDetailPage-BjkLfyrm.js → ProcessDetailPage-DjQuNERz.js} +2 -2
  82. package/dashboard/dist/assets/{ProcessDetailPage-BjkLfyrm.js.map → ProcessDetailPage-DjQuNERz.js.map} +1 -1
  83. package/dashboard/dist/assets/{ProcessesListPage-DOzeduP3.js → ProcessesListPage-vJimii8-.js} +2 -2
  84. package/dashboard/dist/assets/{ProcessesListPage-DOzeduP3.js.map → ProcessesListPage-vJimii8-.js.map} +1 -1
  85. package/dashboard/dist/assets/{RolesPage-DSSQrGTh.js → RolesPage-CFCN_mRF.js} +2 -2
  86. package/dashboard/dist/assets/{RolesPage-DSSQrGTh.js.map → RolesPage-CFCN_mRF.js.map} +1 -1
  87. package/dashboard/dist/assets/{RunAsSelector-CM7lk1YB.js → RunAsSelector-sBxAvlRf.js} +2 -2
  88. package/dashboard/dist/assets/{RunAsSelector-CM7lk1YB.js.map → RunAsSelector-sBxAvlRf.js.map} +1 -1
  89. package/dashboard/dist/assets/{StatusBadge-XQlNFwmH.js → StatusBadge-Bl3FXVmF.js} +2 -2
  90. package/dashboard/dist/assets/{StatusBadge-XQlNFwmH.js.map → StatusBadge-Bl3FXVmF.js.map} +1 -1
  91. package/dashboard/dist/assets/{SwimlaneTimeline-DtCgJNpV.js → SwimlaneTimeline-2Eet4OOL.js} +2 -2
  92. package/dashboard/dist/assets/{SwimlaneTimeline-DtCgJNpV.js.map → SwimlaneTimeline-2Eet4OOL.js.map} +1 -1
  93. package/dashboard/dist/assets/{TaskDetailPage-BC9xnyLT.js → TaskDetailPage-CgFN5uWn.js} +2 -2
  94. package/dashboard/dist/assets/{TaskDetailPage-BC9xnyLT.js.map → TaskDetailPage-CgFN5uWn.js.map} +1 -1
  95. package/dashboard/dist/assets/{TasksListPage-DtbLfUS-.js → TasksListPage-qj25rRZL.js} +2 -2
  96. package/dashboard/dist/assets/{TasksListPage-DtbLfUS-.js.map → TasksListPage-qj25rRZL.js.map} +1 -1
  97. package/dashboard/dist/assets/{TimeAgo-xrDlFnPy.js → TimeAgo-C7LylVjP.js} +2 -2
  98. package/dashboard/dist/assets/{TimeAgo-xrDlFnPy.js.map → TimeAgo-C7LylVjP.js.map} +1 -1
  99. package/dashboard/dist/assets/{TimestampCell-BneATvjR.js → TimestampCell-CrCtoBlo.js} +2 -2
  100. package/dashboard/dist/assets/{TimestampCell-BneATvjR.js.map → TimestampCell-CrCtoBlo.js.map} +1 -1
  101. package/dashboard/dist/assets/{ToolTestPanel-1lQ_GQod.js → ToolTestPanel-DYGlB-QW.js} +2 -2
  102. package/dashboard/dist/assets/{ToolTestPanel-1lQ_GQod.js.map → ToolTestPanel-DYGlB-QW.js.map} +1 -1
  103. package/dashboard/dist/assets/{TopicDetailPage-CDtlXyut.js → TopicDetailPage-DLY6X_R6.js} +2 -2
  104. package/dashboard/dist/assets/{TopicDetailPage-CDtlXyut.js.map → TopicDetailPage-DLY6X_R6.js.map} +1 -1
  105. package/dashboard/dist/assets/{TopicsPage-COX2kDKK.js → TopicsPage-C5nOLsLx.js} +2 -2
  106. package/dashboard/dist/assets/{TopicsPage-COX2kDKK.js.map → TopicsPage-C5nOLsLx.js.map} +1 -1
  107. package/dashboard/dist/assets/{UserName-Cb2WvhCN.js → UserName-CgUZyiwm.js} +2 -2
  108. package/dashboard/dist/assets/{UserName-Cb2WvhCN.js.map → UserName-CgUZyiwm.js.map} +1 -1
  109. package/dashboard/dist/assets/{WorkflowExecutionPage-BWCTOTXF.js → WorkflowExecutionPage-CDr5CTM2.js} +2 -2
  110. package/dashboard/dist/assets/{WorkflowExecutionPage-BWCTOTXF.js.map → WorkflowExecutionPage-CDr5CTM2.js.map} +1 -1
  111. package/dashboard/dist/assets/{WorkflowsDashboard-DOaDSC98.js → WorkflowsDashboard-tCBP0i0Y.js} +2 -2
  112. package/dashboard/dist/assets/{WorkflowsDashboard-DOaDSC98.js.map → WorkflowsDashboard-tCBP0i0Y.js.map} +1 -1
  113. package/dashboard/dist/assets/{WorkflowsOverview-CeE7a51J.js → WorkflowsOverview-CaMeZwde.js} +2 -2
  114. package/dashboard/dist/assets/{WorkflowsOverview-CeE7a51J.js.map → WorkflowsOverview-CaMeZwde.js.map} +1 -1
  115. package/dashboard/dist/assets/{YamlWorkflowDetailPage-cNpR0PHg.js → YamlWorkflowDetailPage-C3uyCiIX.js} +2 -2
  116. package/dashboard/dist/assets/{YamlWorkflowDetailPage-cNpR0PHg.js.map → YamlWorkflowDetailPage-C3uyCiIX.js.map} +1 -1
  117. package/dashboard/dist/assets/{YamlWorkflowsPage-DWvNfzji.js → YamlWorkflowsPage-DRnYgsvA.js} +2 -2
  118. package/dashboard/dist/assets/{YamlWorkflowsPage-DWvNfzji.js.map → YamlWorkflowsPage-DRnYgsvA.js.map} +1 -1
  119. package/dashboard/dist/assets/{agents-BvTiBKBQ.js → agents-HMkTc425.js} +2 -2
  120. package/dashboard/dist/assets/{agents-BvTiBKBQ.js.map → agents-HMkTc425.js.map} +1 -1
  121. package/dashboard/dist/assets/{bots-CeMR1Lnp.js → bots-CDe2jXNx.js} +2 -2
  122. package/dashboard/dist/assets/{bots-CeMR1Lnp.js.map → bots-CDe2jXNx.js.map} +1 -1
  123. package/dashboard/dist/assets/{capabilities-D7uJlnxj.js → capabilities-DhnM7Ztb.js} +2 -2
  124. package/dashboard/dist/assets/{capabilities-D7uJlnxj.js.map → capabilities-DhnM7Ztb.js.map} +1 -1
  125. package/dashboard/dist/assets/{controlplane-Cf6lZsSF.js → controlplane-BwIJ9ZC_.js} +2 -2
  126. package/dashboard/dist/assets/{controlplane-Cf6lZsSF.js.map → controlplane-BwIJ9ZC_.js.map} +1 -1
  127. package/dashboard/dist/assets/{escalation-BCFBWGP-.js → escalation-Djk5XKmX.js} +2 -2
  128. package/dashboard/dist/assets/{escalation-BCFBWGP-.js.map → escalation-Djk5XKmX.js.map} +1 -1
  129. package/dashboard/dist/assets/{escalation-columns-Do7k4fte.js → escalation-columns-BKqSywVA.js} +2 -2
  130. package/dashboard/dist/assets/{escalation-columns-Do7k4fte.js.map → escalation-columns-BKqSywVA.js.map} +1 -1
  131. package/dashboard/dist/assets/{index-eWNx4UQ8.js → index-BFQ2t-p1.js} +22 -22
  132. package/dashboard/dist/assets/index-BFQ2t-p1.js.map +1 -0
  133. package/dashboard/dist/assets/{index-D3SjB3UM.js → index-BNkpns_E.js} +2 -2
  134. package/dashboard/dist/assets/{index-D3SjB3UM.js.map → index-BNkpns_E.js.map} +1 -1
  135. package/dashboard/dist/assets/{index-BS5pcmBR.js → index-BW-fIquS.js} +2 -2
  136. package/dashboard/dist/assets/{index-BS5pcmBR.js.map → index-BW-fIquS.js.map} +1 -1
  137. package/dashboard/dist/assets/{index-C51Jzvoz.js → index-BYpWOUEe.js} +2 -2
  138. package/dashboard/dist/assets/{index-C51Jzvoz.js.map → index-BYpWOUEe.js.map} +1 -1
  139. package/dashboard/dist/assets/{index-BDhEpETz.js → index-CPIYW81T.js} +2 -2
  140. package/dashboard/dist/assets/{index-BDhEpETz.js.map → index-CPIYW81T.js.map} +1 -1
  141. package/dashboard/dist/assets/{index-B216fPN_.js → index-Cydgj_Co.js} +2 -2
  142. package/dashboard/dist/assets/{index-B216fPN_.js.map → index-Cydgj_Co.js.map} +1 -1
  143. package/dashboard/dist/assets/{index-C1p_rvkY.js → index-DBi9Q9Hw.js} +2 -2
  144. package/dashboard/dist/assets/{index-C1p_rvkY.js.map → index-DBi9Q9Hw.js.map} +1 -1
  145. package/dashboard/dist/assets/{index-8C9YLM_u.js → index-DRIJM1L-.js} +2 -2
  146. package/dashboard/dist/assets/{index-8C9YLM_u.js.map → index-DRIJM1L-.js.map} +1 -1
  147. package/dashboard/dist/assets/index-DSki_Ub6.css +1 -0
  148. package/dashboard/dist/assets/{index-gKm3HAgO.js → index-Dhy2PVAw.js} +2 -2
  149. package/dashboard/dist/assets/{index-gKm3HAgO.js.map → index-Dhy2PVAw.js.map} +1 -1
  150. package/dashboard/dist/assets/index-E2hQ8DIi.js +6 -0
  151. package/dashboard/dist/assets/index-E2hQ8DIi.js.map +1 -0
  152. package/dashboard/dist/assets/{index-D0fjUKvx.js → index-mEQ6kCnt.js} +2 -2
  153. package/dashboard/dist/assets/{index-D0fjUKvx.js.map → index-mEQ6kCnt.js.map} +1 -1
  154. package/dashboard/dist/assets/{index-oulO44Iy.js → index-rUST2YsO.js} +2 -2
  155. package/dashboard/dist/assets/{index-oulO44Iy.js.map → index-rUST2YsO.js.map} +1 -1
  156. package/dashboard/dist/assets/{index-DSjbScPX.js → index-u08vQUaw.js} +2 -2
  157. package/dashboard/dist/assets/{index-DSjbScPX.js.map → index-u08vQUaw.js.map} +1 -1
  158. package/dashboard/dist/assets/{knowledge-Bje8_ndK.js → knowledge-jQE3dBVi.js} +2 -2
  159. package/dashboard/dist/assets/{knowledge-Bje8_ndK.js.map → knowledge-jQE3dBVi.js.map} +1 -1
  160. package/dashboard/dist/assets/{mcp-Bpl6p11J.js → mcp-DtEGfZiz.js} +2 -2
  161. package/dashboard/dist/assets/{mcp-Bpl6p11J.js.map → mcp-DtEGfZiz.js.map} +1 -1
  162. package/dashboard/dist/assets/{mcp-query-DMXj-t5f.js → mcp-query-CiTLVuDC.js} +2 -2
  163. package/dashboard/dist/assets/{mcp-query-DMXj-t5f.js.map → mcp-query-CiTLVuDC.js.map} +1 -1
  164. package/dashboard/dist/assets/{pipelines-CFh9nBxn.js → pipelines-ClNdzuUK.js} +2 -2
  165. package/dashboard/dist/assets/{pipelines-CFh9nBxn.js.map → pipelines-ClNdzuUK.js.map} +1 -1
  166. package/dashboard/dist/assets/{roles-CtjQQWSQ.js → roles-BmHbRaPT.js} +2 -2
  167. package/dashboard/dist/assets/{roles-CtjQQWSQ.js.map → roles-BmHbRaPT.js.map} +1 -1
  168. package/dashboard/dist/assets/{tasks-Nz9lVCT1.js → tasks-BPONxLcq.js} +2 -2
  169. package/dashboard/dist/assets/{tasks-Nz9lVCT1.js.map → tasks-BPONxLcq.js.map} +1 -1
  170. package/dashboard/dist/assets/{topics-CiG2N8Z_.js → topics-XREPRxwo.js} +2 -2
  171. package/dashboard/dist/assets/{topics-CiG2N8Z_.js.map → topics-XREPRxwo.js.map} +1 -1
  172. package/dashboard/dist/assets/{useEventHooks-Bimqt7hU.js → useEventHooks-M7SaEfel.js} +2 -2
  173. package/dashboard/dist/assets/{useEventHooks-Bimqt7hU.js.map → useEventHooks-M7SaEfel.js.map} +1 -1
  174. package/dashboard/dist/assets/{useNamespace-FrG0Monz.js → useNamespace-BQHGkdBv.js} +2 -2
  175. package/dashboard/dist/assets/{useNamespace-FrG0Monz.js.map → useNamespace-BQHGkdBv.js.map} +1 -1
  176. package/dashboard/dist/assets/{useYamlActivityEvents-JmD4dH5n.js → useYamlActivityEvents-B7JtUGSE.js} +2 -2
  177. package/dashboard/dist/assets/{useYamlActivityEvents-JmD4dH5n.js.map → useYamlActivityEvents-B7JtUGSE.js.map} +1 -1
  178. package/dashboard/dist/assets/{users-DgyonYq6.js → users-BpjSNhQe.js} +2 -2
  179. package/dashboard/dist/assets/{users-DgyonYq6.js.map → users-BpjSNhQe.js.map} +1 -1
  180. package/dashboard/dist/assets/{workflows-BOwGINtF.js → workflows-k4jE0ECC.js} +2 -2
  181. package/dashboard/dist/assets/{workflows-BOwGINtF.js.map → workflows-k4jE0ECC.js.map} +1 -1
  182. package/dashboard/dist/assets/{yaml-workflows-I1tcjCFK.js → yaml-workflows-DpviSLEL.js} +2 -2
  183. package/dashboard/dist/assets/{yaml-workflows-I1tcjCFK.js.map → yaml-workflows-DpviSLEL.js.map} +1 -1
  184. package/dashboard/dist/index.html +2 -2
  185. package/docs/api/http/escalations.md +89 -5
  186. package/docs/api/sdk/escalations.md +60 -1
  187. package/docs/hitl-guide.md +65 -1
  188. package/package.json +3 -2
  189. package/dashboard/dist/assets/AdminDashboard-DIQEViVY.js +0 -2
  190. package/dashboard/dist/assets/AvailableEscalationsPage-Cqy5ECm4.js +0 -2
  191. package/dashboard/dist/assets/AvailableEscalationsPage-Cqy5ECm4.js.map +0 -1
  192. package/dashboard/dist/assets/CustomDurationPicker-CUYE36-K.js +0 -2
  193. package/dashboard/dist/assets/CustomDurationPicker-CUYE36-K.js.map +0 -1
  194. package/dashboard/dist/assets/index-BCfzac3h.css +0 -1
  195. package/dashboard/dist/assets/index-BUu3WKvk.js +0 -6
  196. package/dashboard/dist/assets/index-BUu3WKvk.js.map +0 -1
  197. package/dashboard/dist/assets/index-eWNx4UQ8.js.map +0 -1
@@ -0,0 +1,147 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.matchPatterns = matchPatterns;
4
+ /**
5
+ * Match event stream + stream messages against known failure patterns.
6
+ * Returns findings ordered by severity (critical first).
7
+ */
8
+ function matchPatterns(execution, workerMessages, engineMessages, escalationRow) {
9
+ const findings = [];
10
+ const events = execution.events ?? [];
11
+ const now = Date.now();
12
+ // ── Derive open signals ──────────────────────────────────────────────────
13
+ const signalCompletions = new Set(events
14
+ .filter(e => e.event_type === 'workflow_execution_signaled')
15
+ .map(e => e.attributes.timeline_key)
16
+ .filter(Boolean));
17
+ const openSignals = events.filter(e => e.event_type === 'signal_wait_started' &&
18
+ !signalCompletions.has(e.attributes.timeline_key));
19
+ // ── Dead-lettered messages ────────────────────────────────────────────────
20
+ const deadWorker = workerMessages.filter(m => m.dead_lettered_at);
21
+ const deadEngine = engineMessages.filter(m => m.dead_lettered_at);
22
+ // ── Reservation leaks: claimed but not ACK'd for > 30s ───────────────────
23
+ const leaks = workerMessages.filter(m => m.reserved_at && !m.expired_at && !m.dead_lettered_at &&
24
+ now - new Date(m.reserved_at).getTime() > 30_000);
25
+ // ── Pattern: orphaned_signal ──────────────────────────────────────────────
26
+ if (openSignals.length > 0 && !escalationRow) {
27
+ const workerMsg = workerMessages.find(m => m.expired_at && m.aid?.endsWith('/worker'));
28
+ const payload = safeParseJson(workerMsg?.message);
29
+ const queueConfig = payload?.data?.queueConfig;
30
+ const signalId = payload?.data?.signalId;
31
+ findings.push({
32
+ condition: 'orphaned_signal',
33
+ confidence: 0.98,
34
+ severity: 'critical',
35
+ evidence: [
36
+ `${openSignals.length} open signal(s) with no matching resolution`,
37
+ `No escalation row in hmsh_escalations for this workflow`,
38
+ queueConfig == null
39
+ ? `Worker result missing queueConfig — escalation INSERT was skipped (pre-0.22 SDK)`
40
+ : `Worker result has queueConfig but escalation INSERT did not complete`,
41
+ ...(signalId ? [`Signal key: ${signalId}`] : []),
42
+ ],
43
+ treatment: [
44
+ { action: 'create_escalation_row', note: 'INSERT missing row into public.hmsh_escalations' },
45
+ ...(signalId ? [{ action: 'resolve_by_signal_key', signal_key: signalId, note: 'POST /api/escalations/resolve-by-signal-key after inserting escalation row' }] : []),
46
+ ],
47
+ });
48
+ }
49
+ // ── Pattern: normal_wait ──────────────────────────────────────────────────
50
+ // Two paths reach here:
51
+ // 1. Open signal in event stream (signal_wait_started) + pending escalation
52
+ // 2. No terminal event + pending escalation (conditionLT path — no signal_wait_started in export)
53
+ const isRunning = !events.some(e => e.event_type === 'workflow_execution_completed' || e.event_type === 'workflow_execution_failed');
54
+ const isNormalWait = (openSignals.length > 0 && escalationRow?.status === 'pending') ||
55
+ (isRunning && events.length > 0 && escalationRow?.status === 'pending');
56
+ if (isNormalWait) {
57
+ findings.push({
58
+ condition: 'normal_wait',
59
+ confidence: 0.99,
60
+ severity: 'info',
61
+ evidence: [
62
+ `Workflow suspended — escalation exists (status: pending)`,
63
+ ...(escalationRow.role ? [`Role: ${escalationRow.role}, Type: ${escalationRow.type}`] : []),
64
+ ...(escalationRow.id ? [`Escalation id: ${escalationRow.id}`] : []),
65
+ ],
66
+ treatment: [{ action: 'none', note: 'Waiting for human to claim and resolve escalation' }],
67
+ });
68
+ }
69
+ // ── Pattern: dead_lettered_activity ──────────────────────────────────────
70
+ if (deadWorker.length > 0 || deadEngine.length > 0) {
71
+ const samples = [...deadWorker, ...deadEngine].slice(0, 3);
72
+ findings.push({
73
+ condition: 'dead_lettered_activity',
74
+ confidence: 0.99,
75
+ severity: 'critical',
76
+ evidence: [
77
+ `${deadWorker.length} worker message(s) dead-lettered, ${deadEngine.length} engine message(s) dead-lettered`,
78
+ ...samples.map(m => `aid: ${m.aid ?? 'engine'}, retries: ${m.retry_attempt}/${m.max_retry_attempts}, stream: ${m.stream_name}`),
79
+ ],
80
+ treatment: [{
81
+ action: 'investigate_dead_letter',
82
+ count: deadWorker.length + deadEngine.length,
83
+ note: 'Message exhausted retries — check worker health and DB connectivity',
84
+ }],
85
+ });
86
+ }
87
+ // ── Pattern: reservation_leak ─────────────────────────────────────────────
88
+ if (leaks.length > 0) {
89
+ findings.push({
90
+ condition: 'reservation_leak',
91
+ confidence: 0.85,
92
+ severity: 'warning',
93
+ evidence: leaks.map(m => `aid: ${m.aid}, claimed by: ${m.reserved_by ?? 'unknown'}, open for ${Math.round((now - new Date(m.reserved_at).getTime()) / 1000)}s`),
94
+ treatment: [{ action: 'check_worker_health', note: 'Worker claimed message but never ACKd — check for crashed workers' }],
95
+ });
96
+ }
97
+ // ── Pattern: terminal_failure ─────────────────────────────────────────────
98
+ const lastEvent = events.at(-1);
99
+ if (lastEvent?.event_type === 'workflow_execution_failed') {
100
+ const attrs = lastEvent.attributes;
101
+ findings.push({
102
+ condition: 'terminal_failure',
103
+ confidence: 0.99,
104
+ severity: 'critical',
105
+ evidence: [
106
+ `Workflow terminated with failure`,
107
+ ...(attrs?.error ? [`Error: ${attrs.error}`] : []),
108
+ ],
109
+ treatment: [{ action: 'none', note: 'Workflow terminated — review error and restart if needed' }],
110
+ });
111
+ }
112
+ // ── Pattern: never_started ────────────────────────────────────────────────
113
+ const nonStartEvents = events.filter(e => e.event_type !== 'workflow_execution_started');
114
+ if (events.length > 0 && nonStartEvents.length === 0) {
115
+ findings.push({
116
+ condition: 'never_started',
117
+ confidence: 0.90,
118
+ severity: 'critical',
119
+ evidence: [`Workflow created but no activities have run — engine may not be consuming this stream`],
120
+ treatment: [{ action: 'check_engine_health', note: 'Check engine health and stream backlog for this workflow type' }],
121
+ });
122
+ }
123
+ // ── Default: healthy ──────────────────────────────────────────────────────
124
+ if (findings.length === 0) {
125
+ const isCompleted = events.some(e => e.event_type === 'workflow_execution_completed' || e.event_type === 'workflow_execution_failed');
126
+ findings.push({
127
+ condition: isCompleted ? 'completed' : 'running',
128
+ confidence: 0.95,
129
+ severity: 'info',
130
+ evidence: [isCompleted ? 'Workflow completed normally' : 'Workflow is running — no anomalies detected'],
131
+ treatment: [{ action: 'none', note: 'No intervention required' }],
132
+ });
133
+ }
134
+ // Critical first
135
+ const order = { critical: 0, warning: 1, info: 2 };
136
+ return findings.sort((a, b) => order[a.severity] - order[b.severity]);
137
+ }
138
+ function safeParseJson(raw) {
139
+ if (!raw)
140
+ return null;
141
+ try {
142
+ return JSON.parse(raw);
143
+ }
144
+ catch {
145
+ return null;
146
+ }
147
+ }
@@ -20,6 +20,15 @@ export declare function bulkAssignEscalations(ids: string[], targetUserId: strin
20
20
  * Clears assignment on all affected rows.
21
21
  */
22
22
  export declare function bulkEscalateToRole(ids: string[], targetRole: string): Promise<number>;
23
+ /**
24
+ * Bulk cancel escalations. Each row is cancelled individually; rows already in
25
+ * a terminal state are silently skipped by the SDK. Returns the count
26
+ * successfully cancelled.
27
+ */
28
+ export declare function bulkCancelEscalations(ids: string[]): Promise<{
29
+ cancelled: number;
30
+ skipped: number;
31
+ }>;
23
32
  /**
24
33
  * Bulk resolve escalations for AI triage.
25
34
  * Returns full records so the caller can start triage workflows. No signal is
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.bulkClaimEscalations = bulkClaimEscalations;
4
4
  exports.bulkAssignEscalations = bulkAssignEscalations;
5
5
  exports.bulkEscalateToRole = bulkEscalateToRole;
6
+ exports.bulkCancelEscalations = bulkCancelEscalations;
6
7
  exports.bulkResolveForTriage = bulkResolveForTriage;
7
8
  const client_1 = require("./client");
8
9
  const map_1 = require("./map");
@@ -41,6 +42,26 @@ async function bulkEscalateToRole(ids, targetRole) {
41
42
  const client = await (0, client_1.escalations)();
42
43
  return client.escalateManyToRole({ ids, targetRole });
43
44
  }
45
+ /**
46
+ * Bulk cancel escalations. Each row is cancelled individually; rows already in
47
+ * a terminal state are silently skipped by the SDK. Returns the count
48
+ * successfully cancelled.
49
+ */
50
+ async function bulkCancelEscalations(ids) {
51
+ if (ids.length === 0)
52
+ return { cancelled: 0, skipped: 0 };
53
+ const client = await (0, client_1.escalations)();
54
+ let cancelled = 0;
55
+ let skipped = 0;
56
+ await Promise.all(ids.map(async (id) => {
57
+ const result = await client.cancel(id);
58
+ if (result.ok)
59
+ cancelled++;
60
+ else
61
+ skipped++;
62
+ }));
63
+ return { cancelled, skipped };
64
+ }
44
65
  /**
45
66
  * Bulk resolve escalations for AI triage.
46
67
  * Returns full records so the caller can start triage workflows. No signal is
@@ -32,6 +32,13 @@ export declare function getEscalationBySignalKey(signalKey: string): Promise<LTE
32
32
  * pre-check (that would be a TOCTOU window) — the atomic resolve is the arbiter.
33
33
  */
34
34
  export declare function resolveEscalationBySignalKey(signalKey: string, resolverPayload: Record<string, any>): Promise<LTEscalationRecord | null>;
35
+ /**
36
+ * Mark an escalation cancelled — used when the tied workflow has terminated and
37
+ * can never receive the resolution signal. The escalation is removed from the
38
+ * active queue but preserved for audit. Returns null when the row is missing or
39
+ * already terminal.
40
+ */
41
+ export declare function cancelEscalation(id: string): Promise<LTEscalationRecord | null>;
35
42
  /**
36
43
  * Bulk update priority for a set of escalations. Only pending escalations are
37
44
  * updated.
@@ -62,6 +69,16 @@ export declare function escalateToRole(id: string, targetRole: string): Promise<
62
69
  export declare function getEscalation(id: string): Promise<LTEscalationRecord | null>;
63
70
  export declare function getEscalationsByTaskId(taskId: string): Promise<LTEscalationRecord[]>;
64
71
  export declare function getEscalationsByWorkflowId(workflowId: string): Promise<LTEscalationRecord[]>;
72
+ /**
73
+ * Cancel all pending escalations tied to a workflow in one atomic UPDATE.
74
+ * Called after workflow termination so escalations don't remain in the queue.
75
+ *
76
+ * HotMesh's interrupt() is supposed to do this atomically, but it filters by
77
+ * `app_id = <engine-appId>`. Long-tail's Durable client uses app_id='durable'
78
+ * while escalations are stored with app_id='hmsh' — the WHERE never matches.
79
+ * This single UPDATE bypasses the SDK entirely and closes that gap.
80
+ */
81
+ export declare function cancelEscalationsByWorkflowId(workflowId: string): Promise<number>;
65
82
  export declare function updateEscalationMetadata(id: string, patch: Record<string, any>): Promise<LTEscalationRecord | null>;
66
83
  export declare function enrichEscalationRouting(id: string, metadataPatch: Record<string, any>, workflowFields: {
67
84
  workflowType?: string;
@@ -5,6 +5,7 @@ exports.claimEscalation = claimEscalation;
5
5
  exports.resolveEscalation = resolveEscalation;
6
6
  exports.getEscalationBySignalKey = getEscalationBySignalKey;
7
7
  exports.resolveEscalationBySignalKey = resolveEscalationBySignalKey;
8
+ exports.cancelEscalation = cancelEscalation;
8
9
  exports.updateEscalationsPriority = updateEscalationsPriority;
9
10
  exports.getEscalationRoles = getEscalationRoles;
10
11
  exports.releaseEscalation = releaseEscalation;
@@ -13,6 +14,7 @@ exports.escalateToRole = escalateToRole;
13
14
  exports.getEscalation = getEscalation;
14
15
  exports.getEscalationsByTaskId = getEscalationsByTaskId;
15
16
  exports.getEscalationsByWorkflowId = getEscalationsByWorkflowId;
17
+ exports.cancelEscalationsByWorkflowId = cancelEscalationsByWorkflowId;
16
18
  exports.updateEscalationMetadata = updateEscalationMetadata;
17
19
  exports.enrichEscalationRouting = enrichEscalationRouting;
18
20
  exports.getEscalationsByOriginId = getEscalationsByOriginId;
@@ -138,6 +140,30 @@ async function resolveEscalationBySignalKey(signalKey, resolverPayload) {
138
140
  return null;
139
141
  return resolveEscalation(escalation.id, resolverPayload);
140
142
  }
143
+ /**
144
+ * Mark an escalation cancelled — used when the tied workflow has terminated and
145
+ * can never receive the resolution signal. The escalation is removed from the
146
+ * active queue but preserved for audit. Returns null when the row is missing or
147
+ * already terminal.
148
+ */
149
+ async function cancelEscalation(id) {
150
+ const client = await (0, client_1.escalations)();
151
+ const result = await client.cancel(id);
152
+ if (!result.ok)
153
+ return null;
154
+ const cancelled = (0, map_1.toEscalationRecord)(result.entry);
155
+ (0, publish_1.publishEscalationEvent)({
156
+ type: 'escalation.cancelled',
157
+ source: 'service',
158
+ workflowId: cancelled.workflow_id || '',
159
+ workflowName: cancelled.workflow_type || '',
160
+ taskQueue: cancelled.task_queue || '',
161
+ escalationId: cancelled.id,
162
+ status: 'cancelled',
163
+ data: { reason: 'workflow_terminated' },
164
+ });
165
+ return cancelled;
166
+ }
141
167
  /**
142
168
  * Bulk update priority for a set of escalations. Only pending escalations are
143
169
  * updated.
@@ -227,6 +253,37 @@ async function getEscalationsByWorkflowId(workflowId) {
227
253
  });
228
254
  return (0, map_1.toEscalationRecords)(rows);
229
255
  }
256
+ /**
257
+ * Cancel all pending escalations tied to a workflow in one atomic UPDATE.
258
+ * Called after workflow termination so escalations don't remain in the queue.
259
+ *
260
+ * HotMesh's interrupt() is supposed to do this atomically, but it filters by
261
+ * `app_id = <engine-appId>`. Long-tail's Durable client uses app_id='durable'
262
+ * while escalations are stored with app_id='hmsh' — the WHERE never matches.
263
+ * This single UPDATE bypasses the SDK entirely and closes that gap.
264
+ */
265
+ async function cancelEscalationsByWorkflowId(workflowId) {
266
+ await (0, client_1.ensureEscalationCompatView)();
267
+ const pool = (0, db_1.getPool)();
268
+ const { rows } = await pool.query(`UPDATE public.hmsh_escalations
269
+ SET status = 'cancelled', updated_at = NOW()
270
+ WHERE workflow_id = $1
271
+ AND status = 'pending'
272
+ RETURNING id, workflow_id, workflow_type, task_queue`, [workflowId]);
273
+ for (const row of rows) {
274
+ (0, publish_1.publishEscalationEvent)({
275
+ type: 'escalation.cancelled',
276
+ source: 'service',
277
+ workflowId: row.workflow_id || '',
278
+ workflowName: row.workflow_type || '',
279
+ taskQueue: row.task_queue || '',
280
+ escalationId: row.id,
281
+ status: 'cancelled',
282
+ data: { reason: 'workflow_terminated' },
283
+ });
284
+ }
285
+ return rows.length;
286
+ }
230
287
  async function updateEscalationMetadata(id, patch) {
231
288
  const client = await (0, client_1.escalations)();
232
289
  const entry = await client.update({ id, metadata: patch });
@@ -36,4 +36,4 @@ import type { Types } from '@hotmeshio/hotmesh';
36
36
  * const decision = await conditionLT<{ approved: boolean }>(signalId);
37
37
  * ```
38
38
  */
39
- export declare function conditionLT<T = Record<string, any>>(signalId: string, escalation?: Types.ConditionQueueConfig): Promise<T>;
39
+ export declare function conditionLT<T = Record<string, any>>(signalId: string, escalation?: Types.ConditionQueueConfig): Promise<T | false | null>;
@@ -76,6 +76,9 @@ const LT_ACTIVITY_QUEUE = 'lt-interceptor';
76
76
  */
77
77
  async function conditionLT(signalId, escalation) {
78
78
  const raw = await hotmesh_1.Durable.workflow.condition(signalId, escalation);
79
+ // false = timeout, null = escalation was cancelled — propagate both as-is
80
+ if (raw === null || raw === false)
81
+ return raw;
79
82
  const escalationId = raw.$escalation_id;
80
83
  if (escalationId) {
81
84
  // Resolve the escalation as a durable activity (crash-safe)
@@ -49,6 +49,7 @@ const logger_1 = require("../../lib/logger");
49
49
  const system_events_1 = require("../../lib/events/system-events");
50
50
  const namespaceService = __importStar(require("../namespace"));
51
51
  const deployer_helpers_1 = require("./deployer-helpers");
52
+ const pipe_validator_1 = require("./pipe-validator");
52
53
  // Re-export helpers so existing `import * from './deployer'` consumers keep working
53
54
  var deployer_helpers_2 = require("./deployer-helpers");
54
55
  Object.defineProperty(exports, "buildMergedYaml", { enumerable: true, get: function () { return deployer_helpers_2.buildMergedYaml; } });
@@ -90,6 +91,10 @@ async function deployAppId(appId, version) {
90
91
  await namespaceService.registerNamespace(appId);
91
92
  const mergedYaml = await (0, deployer_helpers_1.buildMergedYaml)(appId, version);
92
93
  logger_1.loggerRegistry.debug(`[yaml-workflow] merged YAML for ${appId} v${version}:\n${mergedYaml}`);
94
+ // Structural guard: reject YAML with malformed @pipe rows before they reach
95
+ // the HotMesh engine. A bad pipe passes deploy() silently but causes
96
+ // collation errors on every execution, poisoning the stream.
97
+ (0, pipe_validator_1.validatePipeStructure)(mergedYaml);
93
98
  const engine = await getEngine(appId);
94
99
  try {
95
100
  const manifest = await engine.deploy(mergedYaml);
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Structural validator for HotMesh YAML pipe syntax.
3
+ *
4
+ * HotMesh collation errors are silent at deploy time but catastrophic at
5
+ * runtime — a single malformed @pipe in an activated workflow poisons the
6
+ * engine's stream, causing every execution attempt to fail with a
7
+ * collation-error until the database is wiped.
8
+ *
9
+ * This module provides:
10
+ * validatePipeStructure — throws on any structurally invalid @pipe
11
+ * repairPipeStructure — auto-repairs safe-to-fix patterns, then validates
12
+ *
13
+ * Call repairPipeStructure before storing or deploying any YAML.
14
+ *
15
+ * ## What constitutes a valid @pipe row
16
+ *
17
+ * An @pipe value is a YAML sequence. Each element (row) must be one of:
18
+ * 1. A YAML sequence (array) — operands row OR function call row
19
+ * 2. A YAML mapping with '@pipe' key — nested sub-pipe
20
+ *
21
+ * Invalid row types that cause collation errors:
22
+ * - A bare string: '{@object.create}' → must be ['{@object.create}']
23
+ * - A number/bool: 42, true → must be wrapped in an array
24
+ * - A double-nested sequence: [[...]] → row itself is an array of arrays
25
+ */
26
+ export interface PipeViolation {
27
+ path: string;
28
+ rowIndex: number;
29
+ rowValue: unknown;
30
+ message: string;
31
+ repairable: boolean;
32
+ }
33
+ /**
34
+ * Parse the YAML and check every @pipe for structural violations.
35
+ * Throws a descriptive error listing all violations if any are found.
36
+ */
37
+ export declare function validatePipeStructure(yamlContent: string): void;
38
+ /**
39
+ * Auto-repair safe violations (bare strings/scalars → array rows), then
40
+ * validate that no unfixable violations remain.
41
+ *
42
+ * Returns the repaired YAML string. Throws if unfixable violations exist.
43
+ */
44
+ export declare function repairPipeStructure(yamlContent: string): string;
@@ -0,0 +1,215 @@
1
+ "use strict";
2
+ /**
3
+ * Structural validator for HotMesh YAML pipe syntax.
4
+ *
5
+ * HotMesh collation errors are silent at deploy time but catastrophic at
6
+ * runtime — a single malformed @pipe in an activated workflow poisons the
7
+ * engine's stream, causing every execution attempt to fail with a
8
+ * collation-error until the database is wiped.
9
+ *
10
+ * This module provides:
11
+ * validatePipeStructure — throws on any structurally invalid @pipe
12
+ * repairPipeStructure — auto-repairs safe-to-fix patterns, then validates
13
+ *
14
+ * Call repairPipeStructure before storing or deploying any YAML.
15
+ *
16
+ * ## What constitutes a valid @pipe row
17
+ *
18
+ * An @pipe value is a YAML sequence. Each element (row) must be one of:
19
+ * 1. A YAML sequence (array) — operands row OR function call row
20
+ * 2. A YAML mapping with '@pipe' key — nested sub-pipe
21
+ *
22
+ * Invalid row types that cause collation errors:
23
+ * - A bare string: '{@object.create}' → must be ['{@object.create}']
24
+ * - A number/bool: 42, true → must be wrapped in an array
25
+ * - A double-nested sequence: [[...]] → row itself is an array of arrays
26
+ */
27
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
28
+ if (k2 === undefined) k2 = k;
29
+ var desc = Object.getOwnPropertyDescriptor(m, k);
30
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
31
+ desc = { enumerable: true, get: function() { return m[k]; } };
32
+ }
33
+ Object.defineProperty(o, k2, desc);
34
+ }) : (function(o, m, k, k2) {
35
+ if (k2 === undefined) k2 = k;
36
+ o[k2] = m[k];
37
+ }));
38
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
39
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
40
+ }) : function(o, v) {
41
+ o["default"] = v;
42
+ });
43
+ var __importStar = (this && this.__importStar) || (function () {
44
+ var ownKeys = function(o) {
45
+ ownKeys = Object.getOwnPropertyNames || function (o) {
46
+ var ar = [];
47
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
48
+ return ar;
49
+ };
50
+ return ownKeys(o);
51
+ };
52
+ return function (mod) {
53
+ if (mod && mod.__esModule) return mod;
54
+ var result = {};
55
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
56
+ __setModuleDefault(result, mod);
57
+ return result;
58
+ };
59
+ })();
60
+ Object.defineProperty(exports, "__esModule", { value: true });
61
+ exports.validatePipeStructure = validatePipeStructure;
62
+ exports.repairPipeStructure = repairPipeStructure;
63
+ const jsYaml = __importStar(require("js-yaml"));
64
+ // ── internal traversal ────────────────────────────────────────────────────────
65
+ function isPlainObject(v) {
66
+ return typeof v === 'object' && v !== null && !Array.isArray(v);
67
+ }
68
+ function collectPipeViolations(node, path, violations) {
69
+ if (Array.isArray(node)) {
70
+ node.forEach((item, i) => collectPipeViolations(item, `${path}[${i}]`, violations));
71
+ return;
72
+ }
73
+ if (!isPlainObject(node))
74
+ return;
75
+ for (const [key, value] of Object.entries(node)) {
76
+ const childPath = `${path}.${key}`;
77
+ if (key === '@pipe') {
78
+ if (!Array.isArray(value)) {
79
+ violations.push({
80
+ path: childPath,
81
+ rowIndex: -1,
82
+ rowValue: value,
83
+ message: `@pipe value must be a sequence (array), got ${typeof value}`,
84
+ repairable: false,
85
+ });
86
+ continue;
87
+ }
88
+ value.forEach((row, i) => {
89
+ if (Array.isArray(row)) {
90
+ // Valid row — but check for double-nested arrays: [[...]]
91
+ if (row.length > 0 && row.every((el) => Array.isArray(el))) {
92
+ violations.push({
93
+ path: childPath,
94
+ rowIndex: i,
95
+ rowValue: row,
96
+ message: `row ${i} is a double-nested array (array of arrays) — each @pipe row must be a flat array`,
97
+ repairable: false,
98
+ });
99
+ }
100
+ }
101
+ else if (isPlainObject(row) && '@pipe' in row) {
102
+ // Valid nested sub-pipe — recurse into it
103
+ collectPipeViolations(row, `${childPath}[${i}]`, violations);
104
+ }
105
+ else if (typeof row === 'string') {
106
+ // '{@fn}' as bare string — repairable: wrap in array
107
+ violations.push({
108
+ path: childPath,
109
+ rowIndex: i,
110
+ rowValue: row,
111
+ message: `row ${i} is a bare string "${row}" — must be an array row like ['${row}']`,
112
+ repairable: true,
113
+ });
114
+ }
115
+ else if (typeof row === 'number' || typeof row === 'boolean') {
116
+ violations.push({
117
+ path: childPath,
118
+ rowIndex: i,
119
+ rowValue: row,
120
+ message: `row ${i} is a bare scalar ${JSON.stringify(row)} — must be wrapped in an array`,
121
+ repairable: true,
122
+ });
123
+ }
124
+ else {
125
+ violations.push({
126
+ path: childPath,
127
+ rowIndex: i,
128
+ rowValue: row,
129
+ message: `row ${i} has unexpected type ${typeof row}`,
130
+ repairable: false,
131
+ });
132
+ }
133
+ });
134
+ }
135
+ else {
136
+ collectPipeViolations(value, childPath, violations);
137
+ }
138
+ }
139
+ }
140
+ // ── public API ────────────────────────────────────────────────────────────────
141
+ /**
142
+ * Parse the YAML and check every @pipe for structural violations.
143
+ * Throws a descriptive error listing all violations if any are found.
144
+ */
145
+ function validatePipeStructure(yamlContent) {
146
+ let parsed;
147
+ try {
148
+ parsed = jsYaml.load(yamlContent);
149
+ }
150
+ catch (err) {
151
+ throw new Error(`YAML parse error: ${err.message}`);
152
+ }
153
+ const violations = [];
154
+ collectPipeViolations(parsed, 'root', violations);
155
+ if (violations.length > 0) {
156
+ const detail = violations
157
+ .map((v) => ` • ${v.path}[${v.rowIndex}]: ${v.message}`)
158
+ .join('\n');
159
+ throw new Error(`Malformed @pipe structure — ${violations.length} violation(s) detected. ` +
160
+ `This YAML will cause HotMesh collation errors at runtime and must not be deployed.\n${detail}`);
161
+ }
162
+ }
163
+ /**
164
+ * Auto-repair safe violations (bare strings/scalars → array rows), then
165
+ * validate that no unfixable violations remain.
166
+ *
167
+ * Returns the repaired YAML string. Throws if unfixable violations exist.
168
+ */
169
+ function repairPipeStructure(yamlContent) {
170
+ let parsed;
171
+ try {
172
+ parsed = jsYaml.load(yamlContent);
173
+ }
174
+ catch (err) {
175
+ throw new Error(`YAML parse error: ${err.message}`);
176
+ }
177
+ const repairCount = { n: 0 };
178
+ repairNode(parsed, repairCount);
179
+ if (repairCount.n > 0) {
180
+ // Re-serialize — use lineWidth: -1 to prevent wrapping
181
+ yamlContent = jsYaml.dump(parsed, { lineWidth: -1, noRefs: true, quotingType: "'" });
182
+ }
183
+ // Final validation pass — throws if unfixable violations remain
184
+ validatePipeStructure(yamlContent);
185
+ return yamlContent;
186
+ }
187
+ function repairNode(node, count) {
188
+ if (Array.isArray(node)) {
189
+ node.forEach((item) => repairNode(item, count));
190
+ return;
191
+ }
192
+ if (!isPlainObject(node))
193
+ return;
194
+ for (const [key, value] of Object.entries(node)) {
195
+ if (key === '@pipe' && Array.isArray(value)) {
196
+ const rows = value;
197
+ for (let i = 0; i < rows.length; i++) {
198
+ const row = rows[i];
199
+ if (typeof row === 'string' || typeof row === 'number' || typeof row === 'boolean') {
200
+ rows[i] = [row];
201
+ count.n++;
202
+ }
203
+ else if (isPlainObject(row) && '@pipe' in row) {
204
+ repairNode(row, count);
205
+ }
206
+ else if (Array.isArray(row)) {
207
+ repairNode(row, count);
208
+ }
209
+ }
210
+ }
211
+ else {
212
+ repairNode(value, count);
213
+ }
214
+ }
215
+ }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Diagnostics tools — mirrors routes/diagnostics.ts
3
+ *
4
+ * Three tools:
5
+ * diagnose_job — full per-job diagnosis: events + stream messages + findings
6
+ * find_stalled_jobs — fleet-level: running jobs with no recent progress
7
+ * find_orphaned_signals — fleet-level: suspended jobs with no escalation row
8
+ */
9
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
10
+ export declare function registerDiagnosticsTools(server: McpServer): void;