@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,15 @@
1
+ import type { LTApiResult } from '../types/sdk';
2
+ export declare function diagnose(input: {
3
+ workflowId: string;
4
+ appId?: string;
5
+ }): Promise<LTApiResult>;
6
+ export declare function stalledJobs(input: {
7
+ appId?: string;
8
+ stalledMinutes?: number;
9
+ workflowType?: string;
10
+ limit?: number;
11
+ }): Promise<LTApiResult>;
12
+ export declare function orphanedSignals(input: {
13
+ appId?: string;
14
+ limit?: number;
15
+ }): Promise<LTApiResult>;
@@ -0,0 +1,33 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.diagnose = diagnose;
4
+ exports.stalledJobs = stalledJobs;
5
+ exports.orphanedSignals = orphanedSignals;
6
+ const diagnostics_1 = require("../services/diagnostics");
7
+ async function diagnose(input) {
8
+ try {
9
+ const diagnosis = await (0, diagnostics_1.diagnoseJob)(input.workflowId, input.appId);
10
+ return { status: 200, data: diagnosis };
11
+ }
12
+ catch (err) {
13
+ return { status: 500, error: err.message };
14
+ }
15
+ }
16
+ async function stalledJobs(input) {
17
+ try {
18
+ const result = await (0, diagnostics_1.findStalledJobs)(input);
19
+ return { status: 200, data: result };
20
+ }
21
+ catch (err) {
22
+ return { status: 500, error: err.message };
23
+ }
24
+ }
25
+ async function orphanedSignals(input) {
26
+ try {
27
+ const result = await (0, diagnostics_1.findOrphanedSignals)(input);
28
+ return { status: 200, data: result };
29
+ }
30
+ catch (err) {
31
+ return { status: 500, error: err.message };
32
+ }
33
+ }
@@ -0,0 +1,20 @@
1
+ import type { LTApiResult, LTApiAuth } from '../../types/sdk';
2
+ /**
3
+ * Cancel a single escalation. The caller must hold global escalation access
4
+ * or the escalation's role.
5
+ *
6
+ * @returns `{ status: 200, data: { cancelled: true } }` or 403/404/409/500
7
+ */
8
+ export declare function cancelSingleEscalation(input: {
9
+ id: string;
10
+ }, auth: LTApiAuth): Promise<LTApiResult>;
11
+ /**
12
+ * Cancel multiple escalations at once.
13
+ *
14
+ * @param input.ids — array of escalation UUIDs
15
+ * @param auth — authenticated user context (admin required)
16
+ * @returns `{ status: 200, data: { cancelled, skipped } }`
17
+ */
18
+ export declare function bulkCancel(input: {
19
+ ids: string[];
20
+ }, auth: LTApiAuth): Promise<LTApiResult>;
@@ -0,0 +1,93 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.cancelSingleEscalation = cancelSingleEscalation;
37
+ exports.bulkCancel = bulkCancel;
38
+ const escalationService = __importStar(require("../../services/escalation"));
39
+ const helpers_1 = require("./helpers");
40
+ const userService = __importStar(require("../../services/user"));
41
+ /**
42
+ * Cancel a single escalation. The caller must hold global escalation access
43
+ * or the escalation's role.
44
+ *
45
+ * @returns `{ status: 200, data: { cancelled: true } }` or 403/404/409/500
46
+ */
47
+ async function cancelSingleEscalation(input, auth) {
48
+ try {
49
+ const { id } = input;
50
+ if (!id)
51
+ return { status: 400, error: 'id is required' };
52
+ const escalation = await escalationService.getEscalation(id);
53
+ if (!escalation)
54
+ return { status: 404, error: 'Escalation not found' };
55
+ const hasGlobal = await (0, helpers_1.hasGlobalEscalationAccess)(auth.userId);
56
+ if (!hasGlobal) {
57
+ const hasRole = await userService.hasRole(auth.userId, escalation.role);
58
+ if (!hasRole)
59
+ return { status: 403, error: 'Forbidden' };
60
+ }
61
+ const cancelled = await escalationService.cancelEscalation(id);
62
+ if (!cancelled) {
63
+ return { status: 409, error: 'Escalation is not cancellable (already terminal)' };
64
+ }
65
+ return { status: 200, data: { cancelled: true, escalationId: id } };
66
+ }
67
+ catch (err) {
68
+ return { status: 500, error: err.message };
69
+ }
70
+ }
71
+ /**
72
+ * Cancel multiple escalations at once.
73
+ *
74
+ * @param input.ids — array of escalation UUIDs
75
+ * @param auth — authenticated user context (admin required)
76
+ * @returns `{ status: 200, data: { cancelled, skipped } }`
77
+ */
78
+ async function bulkCancel(input, auth) {
79
+ try {
80
+ const { ids } = input;
81
+ if (!(0, helpers_1.validateIds)(ids)) {
82
+ return { status: 400, error: 'ids must be a non-empty array' };
83
+ }
84
+ const perm = await (0, helpers_1.checkBulkPermission)(auth.userId, ids);
85
+ if (!perm.allowed)
86
+ return perm;
87
+ const result = await escalationService.bulkCancelEscalations(ids);
88
+ return { status: 200, data: result };
89
+ }
90
+ catch (err) {
91
+ return { status: 500, error: err.message };
92
+ }
93
+ }
@@ -5,3 +5,4 @@ export { claimEscalation, releaseEscalation } from './claim';
5
5
  export { releaseExpiredClaims, updatePriority, bulkClaim, bulkAssign, bulkEscalate, bulkTriage } from './bulk';
6
6
  export { resolveEscalation, resolveBySignalKey } from './resolve';
7
7
  export { findByMetadata, claimByMetadata, resolveByMetadata } from './metadata';
8
+ export { cancelSingleEscalation, bulkCancel } from './cancel';
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.resolveByMetadata = exports.claimByMetadata = exports.findByMetadata = exports.resolveBySignalKey = exports.resolveEscalation = exports.bulkTriage = exports.bulkEscalate = exports.bulkAssign = exports.bulkClaim = exports.updatePriority = exports.releaseExpiredClaims = exports.releaseEscalation = exports.claimEscalation = exports.escalateToRole = exports.getEscalationsByWorkflowId = exports.getEscalation = exports.getEscalationStats = exports.listDistinctTypes = exports.listAvailableEscalations = exports.listEscalations = exports.createEscalation = void 0;
3
+ exports.bulkCancel = exports.cancelSingleEscalation = exports.resolveByMetadata = exports.claimByMetadata = exports.findByMetadata = exports.resolveBySignalKey = exports.resolveEscalation = exports.bulkTriage = exports.bulkEscalate = exports.bulkAssign = exports.bulkClaim = exports.updatePriority = exports.releaseExpiredClaims = exports.releaseEscalation = exports.claimEscalation = exports.escalateToRole = exports.getEscalationsByWorkflowId = exports.getEscalation = exports.getEscalationStats = exports.listDistinctTypes = exports.listAvailableEscalations = exports.listEscalations = exports.createEscalation = void 0;
4
4
  var create_1 = require("./create");
5
5
  Object.defineProperty(exports, "createEscalation", { enumerable: true, get: function () { return create_1.createEscalation; } });
6
6
  var list_1 = require("./list");
@@ -29,3 +29,6 @@ var metadata_1 = require("./metadata");
29
29
  Object.defineProperty(exports, "findByMetadata", { enumerable: true, get: function () { return metadata_1.findByMetadata; } });
30
30
  Object.defineProperty(exports, "claimByMetadata", { enumerable: true, get: function () { return metadata_1.claimByMetadata; } });
31
31
  Object.defineProperty(exports, "resolveByMetadata", { enumerable: true, get: function () { return metadata_1.resolveByMetadata; } });
32
+ var cancel_1 = require("./cancel");
33
+ Object.defineProperty(exports, "cancelSingleEscalation", { enumerable: true, get: function () { return cancel_1.cancelSingleEscalation; } });
34
+ Object.defineProperty(exports, "bulkCancel", { enumerable: true, get: function () { return cancel_1.bulkCancel; } });
@@ -66,6 +66,8 @@ async function resolveEscalation(input, _auth) {
66
66
  const escalation = await escalationService.getEscalation(id);
67
67
  if (!escalation)
68
68
  return { status: 404, error: 'Escalation not found' };
69
+ if (escalation.status === 'cancelled')
70
+ return { status: 409, error: 'Escalation is cancelled' };
69
71
  if (escalation.status !== 'pending')
70
72
  return { status: 409, error: 'Escalation not available for resolution' };
71
73
  // Path A: conditionLT signal
@@ -42,6 +42,7 @@ const workers_1 = require("../../workers");
42
42
  const exportService = __importStar(require("../../services/export"));
43
43
  const task_1 = require("../../services/task");
44
44
  const workflow_invocation_1 = require("../../services/workflow-invocation");
45
+ const crud_1 = require("../../services/escalation/crud");
45
46
  function isResolveError(err) {
46
47
  return err?.message?.includes('Cannot resolve workflow');
47
48
  }
@@ -154,6 +155,11 @@ async function terminateWorkflow(input) {
154
155
  const client = (0, workers_1.createClient)();
155
156
  const handle = await client.workflow.getHandle(resolved.taskQueue, resolved.workflowName, input.workflowId);
156
157
  await handle.terminate();
158
+ // HotMesh's interrupt() cancels escalations in the same Postgres transaction,
159
+ // but only when the engine app_id matches the escalation row's app_id. The
160
+ // Durable client uses app_id='durable' while escalations are stored with
161
+ // app_id='hmsh', so the automatic cancel is a no-op. Cancel explicitly here.
162
+ await (0, crud_1.cancelEscalationsByWorkflowId)(input.workflowId);
157
163
  return {
158
164
  status: 200,
159
165
  data: { terminated: true, workflowId: input.workflowId },
@@ -35,7 +35,7 @@ export declare function publishTaskEvent(params: {
35
35
  * Subject: system.escalation.{escalationId}.{action}
36
36
  */
37
37
  export declare function publishEscalationEvent(params: {
38
- type: 'escalation.created' | 'escalation.resolved' | 'escalation.claimed' | 'escalation.released';
38
+ type: 'escalation.created' | 'escalation.resolved' | 'escalation.claimed' | 'escalation.released' | 'escalation.cancelled';
39
39
  source: string;
40
40
  workflowId: string;
41
41
  workflowName: string;
@@ -0,0 +1,2 @@
1
+ declare const router: import("express-serve-static-core").Router;
2
+ export default router;
@@ -0,0 +1,88 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ const express_1 = require("express");
37
+ const api = __importStar(require("../api/diagnostics"));
38
+ const auth_1 = require("../modules/auth");
39
+ const router = (0, express_1.Router)();
40
+ /**
41
+ * GET /api/diagnostics/jobs/:workflowId
42
+ * Full diagnosis for a single workflow job.
43
+ *
44
+ * Query params:
45
+ * app_id — HotMesh namespace (default: durable)
46
+ */
47
+ router.get('/jobs/:workflowId', auth_1.requireAdmin, async (req, res) => {
48
+ const result = await api.diagnose({
49
+ workflowId: req.params.workflowId,
50
+ appId: typeof req.query.app_id === 'string' ? req.query.app_id : undefined,
51
+ });
52
+ res.status(result.status).json(result.data ?? { error: result.error });
53
+ });
54
+ /**
55
+ * GET /api/diagnostics/stalled
56
+ * Running jobs with no recent progress.
57
+ *
58
+ * Query params:
59
+ * app_id — HotMesh namespace (default: durable)
60
+ * stalled_minutes — minimum minutes since last event (default: 5)
61
+ * workflow_type — filter by workflow function name
62
+ * limit — max results (default: 50, max: 200)
63
+ */
64
+ router.get('/stalled', auth_1.requireAdmin, async (req, res) => {
65
+ const result = await api.stalledJobs({
66
+ appId: typeof req.query.app_id === 'string' ? req.query.app_id : undefined,
67
+ stalledMinutes: req.query.stalled_minutes ? Number(req.query.stalled_minutes) : undefined,
68
+ workflowType: typeof req.query.workflow_type === 'string' ? req.query.workflow_type : undefined,
69
+ limit: req.query.limit ? Number(req.query.limit) : undefined,
70
+ });
71
+ res.status(result.status).json(result.data ?? { error: result.error });
72
+ });
73
+ /**
74
+ * GET /api/diagnostics/orphaned-signals
75
+ * Suspended jobs with no escalation row.
76
+ *
77
+ * Query params:
78
+ * app_id — HotMesh namespace (default: durable)
79
+ * limit — max results (default: 100, max: 500)
80
+ */
81
+ router.get('/orphaned-signals', auth_1.requireAdmin, async (req, res) => {
82
+ const result = await api.orphanedSignals({
83
+ appId: typeof req.query.app_id === 'string' ? req.query.app_id : undefined,
84
+ limit: req.query.limit ? Number(req.query.limit) : undefined,
85
+ });
86
+ res.status(result.status).json(result.data ?? { error: result.error });
87
+ });
88
+ exports.default = router;
@@ -64,4 +64,8 @@ function registerBulkRoutes(router) {
64
64
  const result = await api.bulkTriage({ ids: req.body?.ids, hint: req.body?.hint }, req.auth);
65
65
  res.status(result.status).json(result.data ?? { error: result.error });
66
66
  });
67
+ router.post('/bulk-cancel', async (req, res) => {
68
+ const result = await api.bulkCancel({ ids: req.body?.ids }, req.auth);
69
+ res.status(result.status).json(result.data ?? { error: result.error });
70
+ });
67
71
  }
@@ -83,4 +83,13 @@ function registerSingleRoutes(router) {
83
83
  const result = await api.releaseEscalation({ id: req.params.id }, req.auth);
84
84
  res.status(result.status).json(result.data ?? { error: result.error });
85
85
  });
86
+ /**
87
+ * POST /api/escalations/:id/cancel
88
+ * Permanently cancel an escalation. Caller must hold the escalation's role
89
+ * or have global escalation access. Terminal escalations return 409.
90
+ */
91
+ router.post('/:id/cancel', async (req, res) => {
92
+ const result = await api.cancelSingleEscalation({ id: req.params.id }, req.auth);
93
+ res.status(result.status).json(result.data ?? { error: result.error });
94
+ });
86
95
  }
@@ -70,4 +70,6 @@ router.use('/capabilities', capabilities_1.default);
70
70
  router.use('/topics', topics_1.default);
71
71
  const overview_1 = __importDefault(require("./overview"));
72
72
  router.use('/overview', overview_1.default);
73
+ const diagnostics_1 = __importDefault(require("./diagnostics"));
74
+ router.use('/diagnostics', diagnostics_1.default);
73
75
  exports.default = router;
@@ -0,0 +1,52 @@
1
+ import type { Finding } from './patterns';
2
+ import type { StreamMessage } from '../controlplane/types';
3
+ export type { Finding };
4
+ export interface EscalationSummary {
5
+ exists: boolean;
6
+ id: string | null;
7
+ status: string | null;
8
+ signal_key: string | null;
9
+ role: string | null;
10
+ type: string | null;
11
+ created_at: string | null;
12
+ }
13
+ export interface StreamSummary {
14
+ worker_total: number;
15
+ engine_total: number;
16
+ dead_lettered: number;
17
+ pending: number;
18
+ in_flight: number;
19
+ }
20
+ export interface JobDiagnosis {
21
+ workflow_id: string;
22
+ app_id: string;
23
+ status: 'running' | 'completed' | 'failed' | 'not_found';
24
+ stalled_for_ms: number | null;
25
+ workflow_type: string | null;
26
+ last_event_at: string | null;
27
+ stream_summary: StreamSummary;
28
+ escalation: EscalationSummary;
29
+ findings: Finding[];
30
+ execution_events: unknown[];
31
+ stream_messages: {
32
+ worker: StreamMessage[];
33
+ engine: StreamMessage[];
34
+ };
35
+ }
36
+ export declare function diagnoseJob(workflowId: string, appId?: string): Promise<JobDiagnosis>;
37
+ export declare function findStalledJobs(params: {
38
+ appId?: string;
39
+ stalledMinutes?: number;
40
+ workflowType?: string | null;
41
+ limit?: number;
42
+ }): Promise<{
43
+ jobs: unknown[];
44
+ total: number;
45
+ }>;
46
+ export declare function findOrphanedSignals(params: {
47
+ appId?: string;
48
+ limit?: number;
49
+ }): Promise<{
50
+ orphans: unknown[];
51
+ total: number;
52
+ }>;
@@ -0,0 +1,169 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.diagnoseJob = diagnoseJob;
4
+ exports.findStalledJobs = findStalledJobs;
5
+ exports.findOrphanedSignals = findOrphanedSignals;
6
+ const db_1 = require("../../lib/db");
7
+ const export_1 = require("../export");
8
+ const controlplane_1 = require("../controlplane");
9
+ const resolve_1 = require("../task/resolve");
10
+ const patterns_1 = require("./patterns");
11
+ // ── SQL ──────────────────────────────────────────────────────────────────────
12
+ const ESCALATION_BY_JOB = `
13
+ SELECT id, status, signal_key, role, type, assigned_to, created_at
14
+ FROM public.hmsh_escalations
15
+ WHERE workflow_id = $1
16
+ ORDER BY created_at DESC LIMIT 5
17
+ `;
18
+ const STALLED_JOBS = (appId) => `
19
+ SELECT
20
+ REPLACE(j.key, $1, '') AS workflow_id,
21
+ j.entity AS workflow_type,
22
+ j.status,
23
+ j.created_at,
24
+ j.updated_at,
25
+ EXTRACT(EPOCH FROM (NOW() - j.updated_at)) * 1000 AS stalled_ms
26
+ FROM "${appId}".jobs j
27
+ WHERE j.status > 0
28
+ AND j.is_live = TRUE
29
+ AND j.updated_at < NOW() - ($2 || ' minutes')::INTERVAL
30
+ AND ($3::text IS NULL OR j.entity = $3)
31
+ ORDER BY j.updated_at ASC
32
+ LIMIT $4
33
+ `;
34
+ const ORPHANED_SIGNALS = (appId) => `
35
+ WITH committed_waiters AS (
36
+ SELECT
37
+ ws.jid AS job_id,
38
+ ws.message->'data'->>'signalId' AS signal_id,
39
+ ws.message->'data'->>'queueConfig' AS queue_config,
40
+ ws.workflow_name,
41
+ ws.created_at AS suspended_at
42
+ FROM "${appId}".worker_streams ws
43
+ WHERE ws.aid LIKE '%/waiter'
44
+ AND ws.expired_at IS NOT NULL
45
+ AND ws.dead_lettered_at IS NULL
46
+ ),
47
+ live_jobs AS (
48
+ SELECT REPLACE(key, 'hmsh:${appId}:j:', '') AS wid
49
+ FROM "${appId}".jobs
50
+ WHERE status > 0 AND is_live = TRUE
51
+ )
52
+ SELECT
53
+ cw.job_id,
54
+ cw.signal_id,
55
+ cw.workflow_name,
56
+ cw.suspended_at,
57
+ EXTRACT(EPOCH FROM (NOW() - cw.suspended_at)) * 1000 AS stalled_ms,
58
+ cw.queue_config IS NULL AS missing_queue_config
59
+ FROM committed_waiters cw
60
+ JOIN live_jobs lj ON lj.wid = cw.job_id
61
+ WHERE NOT EXISTS (
62
+ SELECT 1 FROM public.hmsh_escalations e WHERE e.workflow_id = cw.job_id
63
+ )
64
+ ORDER BY cw.suspended_at ASC
65
+ LIMIT $1
66
+ `;
67
+ // ── diagnoseJob ──────────────────────────────────────────────────────────────
68
+ async function diagnoseJob(workflowId, appId = 'durable') {
69
+ const pool = (0, db_1.getPool)();
70
+ let resolved;
71
+ try {
72
+ resolved = await (0, resolve_1.resolveWorkflowHandle)(workflowId);
73
+ }
74
+ catch {
75
+ return notFound(workflowId, appId);
76
+ }
77
+ const [execution, workerResult, engineResult, escalationResult] = await Promise.all([
78
+ (0, export_1.exportWorkflowExecution)(workflowId, resolved.taskQueue, resolved.workflowName)
79
+ .catch(() => null),
80
+ (0, controlplane_1.getStreamMessages)(appId, { source: 'worker', jid: workflowId, limit: 100, order: 'asc' }),
81
+ (0, controlplane_1.getStreamMessages)(appId, { source: 'engine', jid: workflowId, limit: 100, order: 'asc' }),
82
+ pool.query(ESCALATION_BY_JOB, [workflowId]),
83
+ ]);
84
+ const escalationRow = escalationResult.rows[0] ?? null;
85
+ const workerMessages = workerResult.messages;
86
+ const engineMessages = engineResult.messages;
87
+ const events = execution?.events ?? [];
88
+ const lastEvent = events.at(-1);
89
+ const now = Date.now();
90
+ const isCompleted = events.some(e => e.event_type === 'workflow_execution_completed');
91
+ const isFailed = events.some(e => e.event_type === 'workflow_execution_failed');
92
+ const status = isCompleted ? 'completed' : isFailed ? 'failed' : 'running';
93
+ const lastEventAt = lastEvent?.event_time ?? null;
94
+ const stalledMs = status === 'running' && lastEventAt
95
+ ? now - new Date(lastEventAt).getTime()
96
+ : null;
97
+ const allMessages = [...workerMessages, ...engineMessages];
98
+ const streamSummary = {
99
+ worker_total: workerMessages.length,
100
+ engine_total: engineMessages.length,
101
+ dead_lettered: allMessages.filter(m => m.dead_lettered_at).length,
102
+ pending: allMessages.filter(m => !m.reserved_at && !m.expired_at && !m.dead_lettered_at).length,
103
+ in_flight: allMessages.filter(m => m.reserved_at && !m.expired_at && !m.dead_lettered_at).length,
104
+ };
105
+ const findings = execution
106
+ ? (0, patterns_1.matchPatterns)(execution, workerMessages, engineMessages, escalationRow)
107
+ : [{ condition: 'export_unavailable', confidence: 0.9, severity: 'warning', evidence: ['Execution export unavailable — job may have expired or HotMesh engine is not running'], treatment: [] }];
108
+ return {
109
+ workflow_id: workflowId,
110
+ app_id: appId,
111
+ status,
112
+ stalled_for_ms: stalledMs,
113
+ workflow_type: resolved.workflowName,
114
+ last_event_at: lastEventAt,
115
+ stream_summary: streamSummary,
116
+ escalation: {
117
+ exists: !!escalationRow,
118
+ id: escalationRow?.id ?? null,
119
+ status: escalationRow?.status ?? null,
120
+ signal_key: escalationRow?.signal_key ?? null,
121
+ role: escalationRow?.role ?? null,
122
+ type: escalationRow?.type ?? null,
123
+ created_at: escalationRow?.created_at ?? null,
124
+ },
125
+ findings,
126
+ execution_events: events,
127
+ stream_messages: { worker: workerMessages, engine: engineMessages },
128
+ };
129
+ }
130
+ // ── findStalledJobs ──────────────────────────────────────────────────────────
131
+ async function findStalledJobs(params) {
132
+ const appId = params.appId ?? 'durable';
133
+ const stalledMinutes = params.stalledMinutes ?? 5;
134
+ const limit = Math.min(params.limit ?? 50, 200);
135
+ const keyPrefix = `hmsh:${appId}:j:`;
136
+ const pool = (0, db_1.getPool)();
137
+ const { rows } = await pool.query(STALLED_JOBS(appId), [
138
+ keyPrefix,
139
+ String(stalledMinutes),
140
+ params.workflowType ?? null,
141
+ limit,
142
+ ]).catch(() => ({ rows: [] }));
143
+ return { jobs: rows, total: rows.length };
144
+ }
145
+ // ── findOrphanedSignals ──────────────────────────────────────────────────────
146
+ async function findOrphanedSignals(params) {
147
+ const appId = params.appId ?? 'durable';
148
+ const limit = Math.min(params.limit ?? 100, 500);
149
+ const pool = (0, db_1.getPool)();
150
+ const { rows } = await pool.query(ORPHANED_SIGNALS(appId), [limit])
151
+ .catch(() => ({ rows: [] }));
152
+ return { orphans: rows, total: rows.length };
153
+ }
154
+ // ── helpers ──────────────────────────────────────────────────────────────────
155
+ function notFound(workflowId, appId) {
156
+ return {
157
+ workflow_id: workflowId,
158
+ app_id: appId,
159
+ status: 'not_found',
160
+ stalled_for_ms: null,
161
+ workflow_type: null,
162
+ last_event_at: null,
163
+ stream_summary: { worker_total: 0, engine_total: 0, dead_lettered: 0, pending: 0, in_flight: 0 },
164
+ escalation: { exists: false, id: null, status: null, signal_key: null, role: null, type: null, created_at: null },
165
+ findings: [{ condition: 'not_found', confidence: 1, severity: 'critical', evidence: ['Workflow ID not found — no task record or job entity'], treatment: [] }],
166
+ execution_events: [],
167
+ stream_messages: { worker: [], engine: [] },
168
+ };
169
+ }
@@ -0,0 +1,19 @@
1
+ import type { WorkflowExecution } from '@hotmeshio/hotmesh/build/types/exporter';
2
+ import type { StreamMessage } from '../controlplane/types';
3
+ export interface Finding {
4
+ condition: string;
5
+ confidence: number;
6
+ severity: 'critical' | 'warning' | 'info';
7
+ evidence: string[];
8
+ treatment: RecoveryOption[];
9
+ }
10
+ export interface RecoveryOption {
11
+ action: string;
12
+ note: string;
13
+ [key: string]: unknown;
14
+ }
15
+ /**
16
+ * Match event stream + stream messages against known failure patterns.
17
+ * Returns findings ordered by severity (critical first).
18
+ */
19
+ export declare function matchPatterns(execution: WorkflowExecution, workerMessages: StreamMessage[], engineMessages: StreamMessage[], escalationRow: Record<string, unknown> | null): Finding[];