@hotmeshio/long-tail 0.5.10 → 0.5.12

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 (257) hide show
  1. package/build/api/controlplane.d.ts +1 -0
  2. package/build/api/controlplane.js +1 -0
  3. package/build/api/diagnostics.d.ts +8 -1
  4. package/build/api/diagnostics.js +5 -1
  5. package/build/api/escalations/list.d.ts +2 -0
  6. package/build/api/escalations/list.js +2 -0
  7. package/build/api/settings.d.ts +3 -2
  8. package/build/api/settings.js +13 -2
  9. package/build/api/workflows/invocation.d.ts +6 -0
  10. package/build/api/workflows/invocation.js +6 -2
  11. package/build/modules/features.d.ts +19 -0
  12. package/build/modules/features.js +16 -0
  13. package/build/modules/version.d.ts +16 -0
  14. package/build/modules/version.js +34 -0
  15. package/build/routes/controlplane.js +1 -0
  16. package/build/routes/diagnostics.js +32 -11
  17. package/build/routes/escalations/list.js +2 -0
  18. package/build/routes/workflows/invocation.js +12 -4
  19. package/build/sdk/index.d.ts +2 -0
  20. package/build/services/controlplane/index.js +3 -2
  21. package/build/services/controlplane/stream-messages-sql.d.ts +4 -3
  22. package/build/services/controlplane/stream-messages-sql.js +12 -7
  23. package/build/services/controlplane/types.d.ts +2 -0
  24. package/build/services/diagnostics/index.d.ts +53 -7
  25. package/build/services/diagnostics/index.js +124 -36
  26. package/build/services/diagnostics/patterns.d.ts +13 -3
  27. package/build/services/diagnostics/patterns.js +25 -14
  28. package/build/services/escalation/queries.d.ts +2 -0
  29. package/build/services/escalation/queries.js +84 -2
  30. package/build/services/escalation/sql.d.ts +7 -0
  31. package/build/services/escalation/sql.js +50 -1
  32. package/build/services/task/resolve.d.ts +5 -3
  33. package/build/services/task/resolve.js +9 -6
  34. package/build/services/task/sql.d.ts +6 -1
  35. package/build/services/task/sql.js +7 -1
  36. package/build/start/adapters.js +3 -0
  37. package/build/system/mcp-servers/admin/diagnostics.d.ts +11 -4
  38. package/build/system/mcp-servers/admin/diagnostics.js +34 -22
  39. package/build/system/mcp-servers/admin/escalations.d.ts +6 -0
  40. package/build/system/mcp-servers/admin/escalations.js +182 -54
  41. package/build/system/mcp-servers/admin/index.js +3 -3
  42. package/build/system/mcp-servers/admin/schemas.d.ts +101 -6
  43. package/build/system/mcp-servers/admin/schemas.js +43 -6
  44. package/build/system/mcp-servers/admin/workflows.js +23 -12
  45. package/build/system/seed/tool-manifests-admin.d.ts +1324 -562
  46. package/build/system/seed/tool-manifests-admin.js +10 -2
  47. package/build/tsconfig.tsbuildinfo +1 -1
  48. package/build/types/auth.d.ts +1 -1
  49. package/build/types/startup.d.ts +9 -0
  50. package/dashboard/dist/assets/{AdminDashboard-0LSslIQ7.js → AdminDashboard-LTiYLuzq.js} +2 -2
  51. package/dashboard/dist/assets/{AdminDashboard-0LSslIQ7.js.map → AdminDashboard-LTiYLuzq.js.map} +1 -1
  52. package/dashboard/dist/assets/{AgentConfigPage-hqBr1540.js → AgentConfigPage-nlOudfpZ.js} +7 -7
  53. package/dashboard/dist/assets/{AgentConfigPage-hqBr1540.js.map → AgentConfigPage-nlOudfpZ.js.map} +1 -1
  54. package/dashboard/dist/assets/AgentDetailPage-BSUnDvGp.js +4 -0
  55. package/dashboard/dist/assets/AgentDetailPage-BSUnDvGp.js.map +1 -0
  56. package/dashboard/dist/assets/AgentsPage-DpS0cCYe.js +2 -0
  57. package/dashboard/dist/assets/AgentsPage-DpS0cCYe.js.map +1 -0
  58. package/dashboard/dist/assets/AvailableEscalationsPage-BEylRqjP.js +2 -0
  59. package/dashboard/dist/assets/AvailableEscalationsPage-BEylRqjP.js.map +1 -0
  60. package/dashboard/dist/assets/{BotPicker-fFE4GAaO.js → BotPicker-BhKmq7M7.js} +2 -2
  61. package/dashboard/dist/assets/{BotPicker-fFE4GAaO.js.map → BotPicker-BhKmq7M7.js.map} +1 -1
  62. package/dashboard/dist/assets/CapabilitiesPage-DLIleX2L.js +2 -0
  63. package/dashboard/dist/assets/{CapabilitiesPage-DotZ7iQc.js.map → CapabilitiesPage-DLIleX2L.js.map} +1 -1
  64. package/dashboard/dist/assets/{CollapsibleSection-D97SYypF.js → CollapsibleSection-D7SVwNVk.js} +2 -2
  65. package/dashboard/dist/assets/{CollapsibleSection-D97SYypF.js.map → CollapsibleSection-D7SVwNVk.js.map} +1 -1
  66. package/dashboard/dist/assets/{CredentialsPage-C08U6dlv.js → CredentialsPage-fat5Z2U9.js} +2 -2
  67. package/dashboard/dist/assets/{CredentialsPage-C08U6dlv.js.map → CredentialsPage-fat5Z2U9.js.map} +1 -1
  68. package/dashboard/dist/assets/{CronLabel-C2TuCl4h.js → CronLabel-OOP4P2mJ.js} +2 -2
  69. package/dashboard/dist/assets/{CronLabel-C2TuCl4h.js.map → CronLabel-OOP4P2mJ.js.map} +1 -1
  70. package/dashboard/dist/assets/{CustomDurationPicker-C3zGsESx.js → CustomDurationPicker-COacSAXO.js} +2 -2
  71. package/dashboard/dist/assets/{CustomDurationPicker-C3zGsESx.js.map → CustomDurationPicker-COacSAXO.js.map} +1 -1
  72. package/dashboard/dist/assets/{DropZone-Dwpgbueb.js → DropZone-Bfw2YLVS.js} +2 -2
  73. package/dashboard/dist/assets/{DropZone-Dwpgbueb.js.map → DropZone-Bfw2YLVS.js.map} +1 -1
  74. package/dashboard/dist/assets/{ElapsedCell-DF4m0cv_.js → ElapsedCell-CkfCD-m7.js} +2 -2
  75. package/dashboard/dist/assets/{ElapsedCell-DF4m0cv_.js.map → ElapsedCell-CkfCD-m7.js.map} +1 -1
  76. package/dashboard/dist/assets/{EscalationsOverview-blGOYKtJ.js → EscalationsOverview-BmI8jxlh.js} +2 -2
  77. package/dashboard/dist/assets/{EscalationsOverview-blGOYKtJ.js.map → EscalationsOverview-BmI8jxlh.js.map} +1 -1
  78. package/dashboard/dist/assets/EventTable-CeKfFk3m.js +2 -0
  79. package/dashboard/dist/assets/EventTable-CeKfFk3m.js.map +1 -0
  80. package/dashboard/dist/assets/{GraphInvokePage-QVAUHmjI.js → GraphInvokePage-BZBPBCSq.js} +2 -2
  81. package/dashboard/dist/assets/{GraphInvokePage-QVAUHmjI.js.map → GraphInvokePage-BZBPBCSq.js.map} +1 -1
  82. package/dashboard/dist/assets/{HomePage-DvYgvCcO.js → HomePage-dM7U2tl6.js} +2 -2
  83. package/dashboard/dist/assets/{HomePage-DvYgvCcO.js.map → HomePage-dM7U2tl6.js.map} +1 -1
  84. package/dashboard/dist/assets/{ListToolbar-D6tQ3ZG2.js → ListToolbar-BJaCzAXS.js} +2 -2
  85. package/dashboard/dist/assets/{ListToolbar-D6tQ3ZG2.js.map → ListToolbar-BJaCzAXS.js.map} +1 -1
  86. package/dashboard/dist/assets/{McpOverview-BClbGYb8.js → McpOverview-CvnDG5OB.js} +2 -2
  87. package/dashboard/dist/assets/{McpOverview-BClbGYb8.js.map → McpOverview-CvnDG5OB.js.map} +1 -1
  88. package/dashboard/dist/assets/{McpQueryDetailPage-DHjfDRdm.js → McpQueryDetailPage-DwmSIcIL.js} +3 -3
  89. package/dashboard/dist/assets/{McpQueryDetailPage-DHjfDRdm.js.map → McpQueryDetailPage-DwmSIcIL.js.map} +1 -1
  90. package/dashboard/dist/assets/McpQueryPage-CU6FMEEk.js +2 -0
  91. package/dashboard/dist/assets/McpQueryPage-CU6FMEEk.js.map +1 -0
  92. package/dashboard/dist/assets/McpRunDetailPage-8a6lsehv.js +2 -0
  93. package/dashboard/dist/assets/{McpRunDetailPage-DRbIZMU2.js.map → McpRunDetailPage-8a6lsehv.js.map} +1 -1
  94. package/dashboard/dist/assets/McpRunsPage-CPMYWgm_.js +2 -0
  95. package/dashboard/dist/assets/McpRunsPage-CPMYWgm_.js.map +1 -0
  96. package/dashboard/dist/assets/{NamespacePill-DnJl4Lre.js → NamespacePill-D6btr6e8.js} +2 -2
  97. package/dashboard/dist/assets/{NamespacePill-DnJl4Lre.js.map → NamespacePill-D6btr6e8.js.map} +1 -1
  98. package/dashboard/dist/assets/OperatorDashboard-g9o7I925.js +2 -0
  99. package/dashboard/dist/assets/OperatorDashboard-g9o7I925.js.map +1 -0
  100. package/dashboard/dist/assets/{PageHeader-Bnt2iQQa.js → PageHeader-Dk2Pe44Q.js} +2 -2
  101. package/dashboard/dist/assets/{PageHeader-Bnt2iQQa.js.map → PageHeader-Dk2Pe44Q.js.map} +1 -1
  102. package/dashboard/dist/assets/{PageHeaderWithStats-BuKc9MKt.js → PageHeaderWithStats-DJQCZ0FR.js} +2 -2
  103. package/dashboard/dist/assets/{PageHeaderWithStats-BuKc9MKt.js.map → PageHeaderWithStats-DJQCZ0FR.js.map} +1 -1
  104. package/dashboard/dist/assets/{ProcessDetailPage-DjQuNERz.js → ProcessDetailPage-ByyWCs2A.js} +2 -2
  105. package/dashboard/dist/assets/{ProcessDetailPage-DjQuNERz.js.map → ProcessDetailPage-ByyWCs2A.js.map} +1 -1
  106. package/dashboard/dist/assets/ProcessesListPage-BTsXds4l.js +2 -0
  107. package/dashboard/dist/assets/ProcessesListPage-BTsXds4l.js.map +1 -0
  108. package/dashboard/dist/assets/{RolePill-DQTT1s9v.js → RolePill-Du-aNnjR.js} +2 -2
  109. package/dashboard/dist/assets/{RolePill-DQTT1s9v.js.map → RolePill-Du-aNnjR.js.map} +1 -1
  110. package/dashboard/dist/assets/{RolesPage-CFCN_mRF.js → RolesPage-B2Kdf-L9.js} +2 -2
  111. package/dashboard/dist/assets/{RolesPage-CFCN_mRF.js.map → RolesPage-B2Kdf-L9.js.map} +1 -1
  112. package/dashboard/dist/assets/{RunAsSelector-sBxAvlRf.js → RunAsSelector-BzkZXpSX.js} +2 -2
  113. package/dashboard/dist/assets/{RunAsSelector-sBxAvlRf.js.map → RunAsSelector-BzkZXpSX.js.map} +1 -1
  114. package/dashboard/dist/assets/StreamMessageDetail-B6ch-6SY.js +2 -0
  115. package/dashboard/dist/assets/StreamMessageDetail-B6ch-6SY.js.map +1 -0
  116. package/dashboard/dist/assets/SwimlaneTimeline-CjvfUkls.js +2 -0
  117. package/dashboard/dist/assets/SwimlaneTimeline-CjvfUkls.js.map +1 -0
  118. package/dashboard/dist/assets/{TagInput-B00uqhjX.js → TagInput-CZSeyesi.js} +2 -2
  119. package/dashboard/dist/assets/{TagInput-B00uqhjX.js.map → TagInput-CZSeyesi.js.map} +1 -1
  120. package/dashboard/dist/assets/{TaskDetailPage-CgFN5uWn.js → TaskDetailPage-BjHU6opi.js} +2 -2
  121. package/dashboard/dist/assets/{TaskDetailPage-CgFN5uWn.js.map → TaskDetailPage-BjHU6opi.js.map} +1 -1
  122. package/dashboard/dist/assets/{TaskQueuePill-CUsofm0X.js → TaskQueuePill-CQrXrSxk.js} +2 -2
  123. package/dashboard/dist/assets/{TaskQueuePill-CUsofm0X.js.map → TaskQueuePill-CQrXrSxk.js.map} +1 -1
  124. package/dashboard/dist/assets/{TasksListPage-qj25rRZL.js → TasksListPage-Bem44zd0.js} +2 -2
  125. package/dashboard/dist/assets/{TasksListPage-qj25rRZL.js.map → TasksListPage-Bem44zd0.js.map} +1 -1
  126. package/dashboard/dist/assets/{TimeAgo-C7LylVjP.js → TimeAgo-CUIYqSt_.js} +2 -2
  127. package/dashboard/dist/assets/{TimeAgo-C7LylVjP.js.map → TimeAgo-CUIYqSt_.js.map} +1 -1
  128. package/dashboard/dist/assets/{TimestampCell-CrCtoBlo.js → TimestampCell--tnreI1i.js} +2 -2
  129. package/dashboard/dist/assets/{TimestampCell-CrCtoBlo.js.map → TimestampCell--tnreI1i.js.map} +1 -1
  130. package/dashboard/dist/assets/{ToolPill-DjvedZSn.js → ToolPill-O09jsTQM.js} +2 -2
  131. package/dashboard/dist/assets/{ToolPill-DjvedZSn.js.map → ToolPill-O09jsTQM.js.map} +1 -1
  132. package/dashboard/dist/assets/{ToolTestPanel-DYGlB-QW.js → ToolTestPanel-OD99H7Pk.js} +2 -2
  133. package/dashboard/dist/assets/{ToolTestPanel-DYGlB-QW.js.map → ToolTestPanel-OD99H7Pk.js.map} +1 -1
  134. package/dashboard/dist/assets/{TopicDetailPage-DLY6X_R6.js → TopicDetailPage-rh_dsTJI.js} +3 -3
  135. package/dashboard/dist/assets/{TopicDetailPage-DLY6X_R6.js.map → TopicDetailPage-rh_dsTJI.js.map} +1 -1
  136. package/dashboard/dist/assets/{TopicsPage-C5nOLsLx.js → TopicsPage-CD_40_v-.js} +2 -2
  137. package/dashboard/dist/assets/{TopicsPage-C5nOLsLx.js.map → TopicsPage-CD_40_v-.js.map} +1 -1
  138. package/dashboard/dist/assets/{UserName-CgUZyiwm.js → UserName-C__c8BN0.js} +2 -2
  139. package/dashboard/dist/assets/{UserName-CgUZyiwm.js.map → UserName-C__c8BN0.js.map} +1 -1
  140. package/dashboard/dist/assets/WorkflowExecutionPage-Bw6QdtUO.js +2 -0
  141. package/dashboard/dist/assets/{WorkflowExecutionPage-CDr5CTM2.js.map → WorkflowExecutionPage-Bw6QdtUO.js.map} +1 -1
  142. package/dashboard/dist/assets/{WorkflowPill-F1oKUzmc.js → WorkflowPill-CS-IsR0U.js} +2 -2
  143. package/dashboard/dist/assets/{WorkflowPill-F1oKUzmc.js.map → WorkflowPill-CS-IsR0U.js.map} +1 -1
  144. package/dashboard/dist/assets/WorkflowsDashboard-BgI-iOP6.js +2 -0
  145. package/dashboard/dist/assets/WorkflowsDashboard-BgI-iOP6.js.map +1 -0
  146. package/dashboard/dist/assets/{WorkflowsOverview-CaMeZwde.js → WorkflowsOverview-BRc0QbA7.js} +2 -2
  147. package/dashboard/dist/assets/{WorkflowsOverview-CaMeZwde.js.map → WorkflowsOverview-BRc0QbA7.js.map} +1 -1
  148. package/dashboard/dist/assets/{YamlWorkflowDetailPage-C3uyCiIX.js → YamlWorkflowDetailPage-DdzspNQR.js} +2 -2
  149. package/dashboard/dist/assets/{YamlWorkflowDetailPage-C3uyCiIX.js.map → YamlWorkflowDetailPage-DdzspNQR.js.map} +1 -1
  150. package/dashboard/dist/assets/{YamlWorkflowsPage-DRnYgsvA.js → YamlWorkflowsPage-Djw5ZbgQ.js} +2 -2
  151. package/dashboard/dist/assets/{YamlWorkflowsPage-DRnYgsvA.js.map → YamlWorkflowsPage-Djw5ZbgQ.js.map} +1 -1
  152. package/dashboard/dist/assets/{agents-HMkTc425.js → agents-xstNkF6c.js} +2 -2
  153. package/dashboard/dist/assets/{agents-HMkTc425.js.map → agents-xstNkF6c.js.map} +1 -1
  154. package/dashboard/dist/assets/api-path-u2Jf9QXS.js +2 -0
  155. package/dashboard/dist/assets/api-path-u2Jf9QXS.js.map +1 -0
  156. package/dashboard/dist/assets/{bots-CDe2jXNx.js → bots-BztAI0s_.js} +2 -2
  157. package/dashboard/dist/assets/{bots-CDe2jXNx.js.map → bots-BztAI0s_.js.map} +1 -1
  158. package/dashboard/dist/assets/{capabilities-DhnM7Ztb.js → capabilities-CEDwtxNL.js} +2 -2
  159. package/dashboard/dist/assets/{capabilities-DhnM7Ztb.js.map → capabilities-CEDwtxNL.js.map} +1 -1
  160. package/dashboard/dist/assets/{controlplane-BwIJ9ZC_.js → controlplane-BlGEsRKe.js} +2 -2
  161. package/dashboard/dist/assets/{controlplane-BwIJ9ZC_.js.map → controlplane-BlGEsRKe.js.map} +1 -1
  162. package/dashboard/dist/assets/{escalation-columns-BKqSywVA.js → escalation-columns-nVYVt7Gl.js} +2 -2
  163. package/dashboard/dist/assets/{escalation-columns-BKqSywVA.js.map → escalation-columns-nVYVt7Gl.js.map} +1 -1
  164. package/dashboard/dist/assets/{escalation-Djk5XKmX.js → escalation-iI4QKlP-.js} +2 -2
  165. package/dashboard/dist/assets/{escalation-Djk5XKmX.js.map → escalation-iI4QKlP-.js.map} +1 -1
  166. package/dashboard/dist/assets/{index-BW-fIquS.js → index-B5qsp8lb.js} +2 -2
  167. package/dashboard/dist/assets/{index-BW-fIquS.js.map → index-B5qsp8lb.js.map} +1 -1
  168. package/dashboard/dist/assets/{index-DBi9Q9Hw.js → index-B7VC0vjf.js} +2 -2
  169. package/dashboard/dist/assets/{index-DBi9Q9Hw.js.map → index-B7VC0vjf.js.map} +1 -1
  170. package/dashboard/dist/assets/{index-CPIYW81T.js → index-BUmnJ4eT.js} +2 -2
  171. package/dashboard/dist/assets/{index-CPIYW81T.js.map → index-BUmnJ4eT.js.map} +1 -1
  172. package/dashboard/dist/assets/{index-BNkpns_E.js → index-CAqEy2Db.js} +2 -2
  173. package/dashboard/dist/assets/{index-BNkpns_E.js.map → index-CAqEy2Db.js.map} +1 -1
  174. package/dashboard/dist/assets/{index-BYpWOUEe.js → index-CMviPjXQ.js} +2 -2
  175. package/dashboard/dist/assets/{index-BYpWOUEe.js.map → index-CMviPjXQ.js.map} +1 -1
  176. package/dashboard/dist/assets/index-CP0JEQhZ.js +6 -0
  177. package/dashboard/dist/assets/{index-E2hQ8DIi.js.map → index-CP0JEQhZ.js.map} +1 -1
  178. package/dashboard/dist/assets/index-CUfgt3mV.js +2 -0
  179. package/dashboard/dist/assets/index-CUfgt3mV.js.map +1 -0
  180. package/dashboard/dist/assets/index-ClvYSNZd.css +1 -0
  181. package/dashboard/dist/assets/{index-BFQ2t-p1.js → index-D1xVY4JR.js} +27 -27
  182. package/dashboard/dist/assets/index-D1xVY4JR.js.map +1 -0
  183. package/dashboard/dist/assets/{index-rUST2YsO.js → index-DPLy-jwO.js} +2 -2
  184. package/dashboard/dist/assets/{index-rUST2YsO.js.map → index-DPLy-jwO.js.map} +1 -1
  185. package/dashboard/dist/assets/{index-DRIJM1L-.js → index-DjJ6VCuc.js} +2 -2
  186. package/dashboard/dist/assets/{index-DRIJM1L-.js.map → index-DjJ6VCuc.js.map} +1 -1
  187. package/dashboard/dist/assets/{index-Dhy2PVAw.js → index-Dsi5o7dx.js} +2 -2
  188. package/dashboard/dist/assets/{index-Dhy2PVAw.js.map → index-Dsi5o7dx.js.map} +1 -1
  189. package/dashboard/dist/assets/{index-mEQ6kCnt.js → index-DzVYjoex.js} +3 -3
  190. package/dashboard/dist/assets/{index-mEQ6kCnt.js.map → index-DzVYjoex.js.map} +1 -1
  191. package/dashboard/dist/assets/index-FuhiJuHL.js +2 -0
  192. package/dashboard/dist/assets/index-FuhiJuHL.js.map +1 -0
  193. package/dashboard/dist/assets/{knowledge-jQE3dBVi.js → knowledge-BKg0cr39.js} +2 -2
  194. package/dashboard/dist/assets/{knowledge-jQE3dBVi.js.map → knowledge-BKg0cr39.js.map} +1 -1
  195. package/dashboard/dist/assets/{mcp-DtEGfZiz.js → mcp-CVdHpKlz.js} +2 -2
  196. package/dashboard/dist/assets/{mcp-DtEGfZiz.js.map → mcp-CVdHpKlz.js.map} +1 -1
  197. package/dashboard/dist/assets/{mcp-query-CiTLVuDC.js → mcp-query-CYxZcbwQ.js} +2 -2
  198. package/dashboard/dist/assets/{mcp-query-CiTLVuDC.js.map → mcp-query-CYxZcbwQ.js.map} +1 -1
  199. package/dashboard/dist/assets/{pipelines-ClNdzuUK.js → pipelines-BOEv7P6O.js} +2 -2
  200. package/dashboard/dist/assets/{pipelines-ClNdzuUK.js.map → pipelines-BOEv7P6O.js.map} +1 -1
  201. package/dashboard/dist/assets/{roles-BmHbRaPT.js → roles-c5cV2QYQ.js} +2 -2
  202. package/dashboard/dist/assets/{roles-BmHbRaPT.js.map → roles-c5cV2QYQ.js.map} +1 -1
  203. package/dashboard/dist/assets/{tasks-BPONxLcq.js → tasks-JV3BjeEF.js} +2 -2
  204. package/dashboard/dist/assets/{tasks-BPONxLcq.js.map → tasks-JV3BjeEF.js.map} +1 -1
  205. package/dashboard/dist/assets/{topics-XREPRxwo.js → topics-BDaPnhnQ.js} +2 -2
  206. package/dashboard/dist/assets/{topics-XREPRxwo.js.map → topics-BDaPnhnQ.js.map} +1 -1
  207. package/dashboard/dist/assets/useDebouncedValue-BA5S7V54.js +2 -0
  208. package/dashboard/dist/assets/useDebouncedValue-BA5S7V54.js.map +1 -0
  209. package/dashboard/dist/assets/{useEventHooks-M7SaEfel.js → useEventHooks-a7Dnh8G4.js} +2 -2
  210. package/dashboard/dist/assets/{useEventHooks-M7SaEfel.js.map → useEventHooks-a7Dnh8G4.js.map} +1 -1
  211. package/dashboard/dist/assets/{useNamespace-BQHGkdBv.js → useNamespace-BSwEV0e9.js} +2 -2
  212. package/dashboard/dist/assets/{useNamespace-BQHGkdBv.js.map → useNamespace-BSwEV0e9.js.map} +1 -1
  213. package/dashboard/dist/assets/{useYamlActivityEvents-B7JtUGSE.js → useYamlActivityEvents-BuqvL-cn.js} +2 -2
  214. package/dashboard/dist/assets/{useYamlActivityEvents-B7JtUGSE.js.map → useYamlActivityEvents-BuqvL-cn.js.map} +1 -1
  215. package/dashboard/dist/assets/{users-BpjSNhQe.js → users-Pn85K_3C.js} +2 -2
  216. package/dashboard/dist/assets/{users-BpjSNhQe.js.map → users-Pn85K_3C.js.map} +1 -1
  217. package/dashboard/dist/assets/{vendor-icons-Dj2F0Jrb.js → vendor-icons-D5ycKPA2.js} +114 -109
  218. package/dashboard/dist/assets/vendor-icons-D5ycKPA2.js.map +1 -0
  219. package/dashboard/dist/assets/{workflows-k4jE0ECC.js → workflows-DF-9Voyi.js} +2 -2
  220. package/dashboard/dist/assets/{workflows-k4jE0ECC.js.map → workflows-DF-9Voyi.js.map} +1 -1
  221. package/dashboard/dist/assets/{yaml-workflows-DpviSLEL.js → yaml-workflows-2roKaSSK.js} +2 -2
  222. package/dashboard/dist/assets/{yaml-workflows-DpviSLEL.js.map → yaml-workflows-2roKaSSK.js.map} +1 -1
  223. package/dashboard/dist/index.html +3 -3
  224. package/docs/api/mcp/admin.md +242 -5
  225. package/docs/mcp.md +102 -0
  226. package/package.json +1 -1
  227. package/dashboard/dist/assets/AgentDetailPage-bnXR1ezD.js +0 -4
  228. package/dashboard/dist/assets/AgentDetailPage-bnXR1ezD.js.map +0 -1
  229. package/dashboard/dist/assets/AgentsPage-tJIlrnfM.js +0 -2
  230. package/dashboard/dist/assets/AgentsPage-tJIlrnfM.js.map +0 -1
  231. package/dashboard/dist/assets/AvailableEscalationsPage-B7gEfcw3.js +0 -2
  232. package/dashboard/dist/assets/AvailableEscalationsPage-B7gEfcw3.js.map +0 -1
  233. package/dashboard/dist/assets/CapabilitiesPage-DotZ7iQc.js +0 -2
  234. package/dashboard/dist/assets/EventTable-DzrMH-ux.js +0 -2
  235. package/dashboard/dist/assets/EventTable-DzrMH-ux.js.map +0 -1
  236. package/dashboard/dist/assets/McpQueryPage-qtEuMDrd.js +0 -2
  237. package/dashboard/dist/assets/McpQueryPage-qtEuMDrd.js.map +0 -1
  238. package/dashboard/dist/assets/McpRunDetailPage-DRbIZMU2.js +0 -2
  239. package/dashboard/dist/assets/McpRunsPage-CMfwXQ4R.js +0 -2
  240. package/dashboard/dist/assets/McpRunsPage-CMfwXQ4R.js.map +0 -1
  241. package/dashboard/dist/assets/OperatorDashboard-Dq2pfV68.js +0 -2
  242. package/dashboard/dist/assets/OperatorDashboard-Dq2pfV68.js.map +0 -1
  243. package/dashboard/dist/assets/ProcessesListPage-vJimii8-.js +0 -2
  244. package/dashboard/dist/assets/ProcessesListPage-vJimii8-.js.map +0 -1
  245. package/dashboard/dist/assets/SwimlaneTimeline-2Eet4OOL.js +0 -2
  246. package/dashboard/dist/assets/SwimlaneTimeline-2Eet4OOL.js.map +0 -1
  247. package/dashboard/dist/assets/WorkflowExecutionPage-CDr5CTM2.js +0 -2
  248. package/dashboard/dist/assets/WorkflowsDashboard-tCBP0i0Y.js +0 -2
  249. package/dashboard/dist/assets/WorkflowsDashboard-tCBP0i0Y.js.map +0 -1
  250. package/dashboard/dist/assets/index-BFQ2t-p1.js.map +0 -1
  251. package/dashboard/dist/assets/index-Cydgj_Co.js +0 -2
  252. package/dashboard/dist/assets/index-Cydgj_Co.js.map +0 -1
  253. package/dashboard/dist/assets/index-DSki_Ub6.css +0 -1
  254. package/dashboard/dist/assets/index-E2hQ8DIi.js +0 -6
  255. package/dashboard/dist/assets/index-u08vQUaw.js +0 -2
  256. package/dashboard/dist/assets/index-u08vQUaw.js.map +0 -1
  257. package/dashboard/dist/assets/vendor-icons-Dj2F0Jrb.js.map +0 -1
@@ -15,34 +15,56 @@ const ESCALATION_BY_JOB = `
15
15
  WHERE workflow_id = $1
16
16
  ORDER BY created_at DESC LIMIT 5
17
17
  `;
18
+ // "No recent status change" is NOT the same as "broken": HotMesh only bumps
19
+ // jobs.updated_at when status changes, so a frozen updated_at is the normal
20
+ // signature of a workflow waiting at a condition()/waitFor()/sleepFor(). We
21
+ // classify each row with `has_open_escalation` (a cheap, workflow_id-indexed
22
+ // EXISTS bounded by the outer LIMIT) so callers can tell a healthy wait
23
+ // (`likely = waiting`) from a candidate worth a closer look (`no_recent_progress`).
18
24
  const STALLED_JOBS = (appId) => `
19
25
  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
26
+ q.*,
27
+ CASE WHEN q.has_open_escalation THEN 'waiting' ELSE 'no_recent_progress' END AS likely
28
+ FROM (
29
+ SELECT
30
+ REPLACE(j.key, $1, '') AS workflow_id,
31
+ j.entity AS workflow_type,
32
+ j.status,
33
+ j.created_at,
34
+ j.updated_at,
35
+ EXTRACT(EPOCH FROM (NOW() - j.updated_at)) * 1000 AS idle_ms,
36
+ EXISTS (
37
+ SELECT 1 FROM public.hmsh_escalations e
38
+ WHERE e.workflow_id = REPLACE(j.key, $1, '') AND e.status = 'pending'
39
+ ) AS has_open_escalation
40
+ FROM "${appId}".jobs j
41
+ WHERE j.status > 0
42
+ AND j.is_live = TRUE
43
+ AND j.updated_at < NOW() - ($2 || ' minutes')::INTERVAL
44
+ AND ($3::text IS NULL OR j.entity = $3)
45
+ ORDER BY j.updated_at ASC
46
+ LIMIT $4
47
+ ) q
33
48
  `;
49
+ // Bounded by a recent-time window ($1 = within_hours) so this never degenerates
50
+ // into a full-history scan of the HASH-partitioned worker_streams table. The
51
+ // `aid LIKE '%/waiter'` predicate is a suffix wildcard (not index-backed), so
52
+ // the time window is what keeps the intermediate scan small; widening it trades
53
+ // cost for reach. The NOT EXISTS anti-join on hmsh_escalations (workflow_id
54
+ // indexed) is the high-value signal — a suspended waiter with no escalation row.
34
55
  const ORPHANED_SIGNALS = (appId) => `
35
56
  WITH committed_waiters AS (
36
57
  SELECT
37
- ws.jid AS job_id,
38
- ws.message->'data'->>'signalId' AS signal_id,
39
- ws.message->'data'->>'queueConfig' AS queue_config,
58
+ ws.jid AS job_id,
59
+ ws.message::jsonb->'data'->>'signalId' AS signal_id,
60
+ ws.message::jsonb->'data'->>'queueConfig' AS queue_config,
40
61
  ws.workflow_name,
41
- ws.created_at AS suspended_at
62
+ ws.created_at AS suspended_at
42
63
  FROM "${appId}".worker_streams ws
43
64
  WHERE ws.aid LIKE '%/waiter'
44
65
  AND ws.expired_at IS NOT NULL
45
66
  AND ws.dead_lettered_at IS NULL
67
+ AND ws.created_at > NOW() - ($1 || ' hours')::INTERVAL
46
68
  ),
47
69
  live_jobs AS (
48
70
  SELECT REPLACE(key, 'hmsh:${appId}:j:', '') AS wid
@@ -54,7 +76,7 @@ const ORPHANED_SIGNALS = (appId) => `
54
76
  cw.signal_id,
55
77
  cw.workflow_name,
56
78
  cw.suspended_at,
57
- EXTRACT(EPOCH FROM (NOW() - cw.suspended_at)) * 1000 AS stalled_ms,
79
+ EXTRACT(EPOCH FROM (NOW() - cw.suspended_at)) * 1000 AS waiting_ms,
58
80
  cw.queue_config IS NULL AS missing_queue_config
59
81
  FROM committed_waiters cw
60
82
  JOIN live_jobs lj ON lj.wid = cw.job_id
@@ -62,11 +84,43 @@ const ORPHANED_SIGNALS = (appId) => `
62
84
  SELECT 1 FROM public.hmsh_escalations e WHERE e.workflow_id = cw.job_id
63
85
  )
64
86
  ORDER BY cw.suspended_at ASC
65
- LIMIT $1
87
+ LIMIT $2
66
88
  `;
89
+ /** Default cap on events returned in the payload — keeps a high-activity workflow from returning a huge response. */
90
+ const DEFAULT_MAX_EVENTS = 500;
91
+ /** Strings larger than this (UTF-8 bytes) are summarized rather than returned in full. */
92
+ const LARGE_STRING_BYTES = 1024;
93
+ /** How many leading characters of a summarized string to keep as a preview. */
94
+ const PREVIEW_CHARS = 200;
95
+ /** Recursion depth cap for summarizing nested event attributes. */
96
+ const SUMMARIZE_MAX_DEPTH = 6;
97
+ /** Replace an over-threshold string with a bounded summary; pass small strings through unchanged. */
98
+ function summarizeString(value) {
99
+ const bytes = Buffer.byteLength(value, 'utf8');
100
+ if (bytes <= LARGE_STRING_BYTES)
101
+ return value;
102
+ return { bytes, preview: value.slice(0, PREVIEW_CHARS), truncated: true };
103
+ }
104
+ /** Recursively summarize large string leaves in an event attribute tree (result/output/input/error payloads). */
105
+ function summarizeDeep(value, depth = 0) {
106
+ if (typeof value === 'string')
107
+ return summarizeString(value);
108
+ if (value === null || typeof value !== 'object' || depth >= SUMMARIZE_MAX_DEPTH)
109
+ return value;
110
+ if (Array.isArray(value))
111
+ return value.map((v) => summarizeDeep(v, depth + 1));
112
+ const out = {};
113
+ for (const [k, v] of Object.entries(value)) {
114
+ out[k] = summarizeDeep(v, depth + 1);
115
+ }
116
+ return out;
117
+ }
67
118
  // ── diagnoseJob ──────────────────────────────────────────────────────────────
68
- async function diagnoseJob(workflowId, appId = 'durable') {
119
+ async function diagnoseJob(workflowId, appId = 'durable', options = {}) {
69
120
  const pool = (0, db_1.getPool)();
121
+ const maxEvents = Math.max(options.maxEvents ?? DEFAULT_MAX_EVENTS, 1);
122
+ const includeEvents = options.verbosity === 'full' || !!options.include?.includes('events');
123
+ const includeStreams = options.verbosity === 'full' || !!options.include?.includes('streams');
70
124
  let resolved;
71
125
  try {
72
126
  resolved = await (0, resolve_1.resolveWorkflowHandle)(workflowId);
@@ -91,9 +145,14 @@ async function diagnoseJob(workflowId, appId = 'durable') {
91
145
  const isFailed = events.some(e => e.event_type === 'workflow_execution_failed');
92
146
  const status = isCompleted ? 'completed' : isFailed ? 'failed' : 'running';
93
147
  const lastEventAt = lastEvent?.event_time ?? null;
94
- const stalledMs = status === 'running' && lastEventAt
148
+ const idleMs = status === 'running' && lastEventAt
95
149
  ? now - new Date(lastEventAt).getTime()
96
150
  : null;
151
+ // Cap the events returned in the payload (keep the most recent) so a
152
+ // high-activity workflow can't produce a huge response. Pattern matching
153
+ // still runs over the full in-memory event set.
154
+ const eventsTruncated = events.length > maxEvents;
155
+ const cappedEvents = eventsTruncated ? events.slice(-maxEvents) : events;
97
156
  const allMessages = [...workerMessages, ...engineMessages];
98
157
  const streamSummary = {
99
158
  worker_total: workerMessages.length,
@@ -104,12 +163,12 @@ async function diagnoseJob(workflowId, appId = 'durable') {
104
163
  };
105
164
  const findings = execution
106
165
  ? (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 {
166
+ : [{ condition: 'export_unavailable', confidence: 0.9, severity: 'warning', evidence: ['Execution export unavailable — job may have expired or HotMesh engine is not running'], guidance: [] }];
167
+ const diagnosis = {
109
168
  workflow_id: workflowId,
110
169
  app_id: appId,
111
170
  status,
112
- stalled_for_ms: stalledMs,
171
+ idle_for_ms: idleMs,
113
172
  workflow_type: resolved.workflowName,
114
173
  last_event_at: lastEventAt,
115
174
  stream_summary: streamSummary,
@@ -123,33 +182,62 @@ async function diagnoseJob(workflowId, appId = 'durable') {
123
182
  created_at: escalationRow?.created_at ?? null,
124
183
  },
125
184
  findings,
126
- execution_events: events,
127
- stream_messages: { worker: workerMessages, engine: engineMessages },
185
+ total_events: events.length,
186
+ events_truncated: eventsTruncated,
128
187
  };
188
+ // Heavy sections are opt-in. Large string payloads are summarized either way
189
+ // so an included section can't reintroduce the 100–200KB blow-up.
190
+ if (includeEvents) {
191
+ diagnosis.execution_events = cappedEvents.map((e) => e && typeof e === 'object'
192
+ ? { ...e, attributes: summarizeDeep(e.attributes) }
193
+ : e);
194
+ }
195
+ if (includeStreams) {
196
+ diagnosis.stream_messages = {
197
+ worker: summarizeMessages(workerMessages),
198
+ engine: summarizeMessages(engineMessages),
199
+ };
200
+ }
201
+ else {
202
+ diagnosis.raw_messages = {
203
+ hint: 'Use list_stream_messages (Control Plane) filtered by jid (and aid/dad) for the full raw message payloads.',
204
+ jid: workflowId,
205
+ };
206
+ }
207
+ return diagnosis;
208
+ }
209
+ /** Summarize the large `message` payload of each stream message; everything else passes through. */
210
+ function summarizeMessages(messages) {
211
+ return messages.map((m) => ({ ...m, message: summarizeString(m.message) }));
129
212
  }
130
213
  // ── findStalledJobs ──────────────────────────────────────────────────────────
131
214
  async function findStalledJobs(params) {
132
215
  const appId = params.appId ?? 'durable';
133
- const stalledMinutes = params.stalledMinutes ?? 5;
216
+ const idleMinutes = params.idleMinutes ?? 5;
134
217
  const limit = Math.min(params.limit ?? 50, 200);
135
218
  const keyPrefix = `hmsh:${appId}:j:`;
219
+ // Fail loud: let a query error surface (the api layer maps it to a 500). A
220
+ // silent empty result would hide a missing schema or a broken query.
136
221
  const pool = (0, db_1.getPool)();
137
222
  const { rows } = await pool.query(STALLED_JOBS(appId), [
138
223
  keyPrefix,
139
- String(stalledMinutes),
224
+ String(idleMinutes),
140
225
  params.workflowType ?? null,
141
226
  limit,
142
- ]).catch(() => ({ rows: [] }));
227
+ ]);
143
228
  return { jobs: rows, total: rows.length };
144
229
  }
145
230
  // ── findOrphanedSignals ──────────────────────────────────────────────────────
146
231
  async function findOrphanedSignals(params) {
147
232
  const appId = params.appId ?? 'durable';
233
+ const withinHours = Math.min(Math.max(params.withinHours ?? 24, 1), 720);
148
234
  const limit = Math.min(params.limit ?? 100, 500);
235
+ // Fail loud: let a query error surface (the api layer maps it to a 500). A
236
+ // silent empty result would hide a missing schema or a broken query — exactly
237
+ // what previously masked a JSON-operator bug on the text `message` column.
149
238
  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 };
239
+ const { rows } = await pool.query(ORPHANED_SIGNALS(appId), [String(withinHours), limit]);
240
+ return { orphans: rows, total: rows.length, within_hours: withinHours };
153
241
  }
154
242
  // ── helpers ──────────────────────────────────────────────────────────────────
155
243
  function notFound(workflowId, appId) {
@@ -157,13 +245,13 @@ function notFound(workflowId, appId) {
157
245
  workflow_id: workflowId,
158
246
  app_id: appId,
159
247
  status: 'not_found',
160
- stalled_for_ms: null,
248
+ idle_for_ms: null,
161
249
  workflow_type: null,
162
250
  last_event_at: null,
163
251
  stream_summary: { worker_total: 0, engine_total: 0, dead_lettered: 0, pending: 0, in_flight: 0 },
164
252
  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: [] },
253
+ findings: [{ condition: 'not_found', confidence: 1, severity: 'critical', evidence: ['Workflow ID not found — no task record or job entity'], guidance: [] }],
254
+ total_events: 0,
255
+ events_truncated: false,
168
256
  };
169
257
  }
@@ -5,15 +5,25 @@ export interface Finding {
5
5
  confidence: number;
6
6
  severity: 'critical' | 'warning' | 'info';
7
7
  evidence: string[];
8
- treatment: RecoveryOption[];
8
+ /**
9
+ * Read-only diagnostic guidance: what the condition means and where a human
10
+ * should look next. This is NOT recovery — HotMesh state only moves forward
11
+ * and cannot be unwound, so guidance never prescribes mutating job state.
12
+ */
13
+ guidance: Guidance[];
9
14
  }
10
- export interface RecoveryOption {
15
+ export interface Guidance {
11
16
  action: string;
12
17
  note: string;
13
18
  [key: string]: unknown;
14
19
  }
15
20
  /**
16
- * Match event stream + stream messages against known failure patterns.
21
+ * Match event stream + stream messages against known workflow conditions.
17
22
  * Returns findings ordered by severity (critical first).
23
+ *
24
+ * Healthy long-waits are a first-class outcome here, not an omission: a
25
+ * workflow suspended at `condition()`/`waitFor()`/`sleepFor()` can sit for days
26
+ * legitimately, so we classify those explicitly rather than treating a frozen
27
+ * job as broken.
18
28
  */
19
29
  export declare function matchPatterns(execution: WorkflowExecution, workerMessages: StreamMessage[], engineMessages: StreamMessage[], escalationRow: Record<string, unknown> | null): Finding[];
@@ -2,8 +2,13 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.matchPatterns = matchPatterns;
4
4
  /**
5
- * Match event stream + stream messages against known failure patterns.
5
+ * Match event stream + stream messages against known workflow conditions.
6
6
  * Returns findings ordered by severity (critical first).
7
+ *
8
+ * Healthy long-waits are a first-class outcome here, not an omission: a
9
+ * workflow suspended at `condition()`/`waitFor()`/`sleepFor()` can sit for days
10
+ * legitimately, so we classify those explicitly rather than treating a frozen
11
+ * job as broken.
7
12
  */
8
13
  function matchPatterns(execution, workerMessages, engineMessages, escalationRow) {
9
14
  const findings = [];
@@ -36,13 +41,18 @@ function matchPatterns(execution, workerMessages, engineMessages, escalationRow)
36
41
  `${openSignals.length} open signal(s) with no matching resolution`,
37
42
  `No escalation row in hmsh_escalations for this workflow`,
38
43
  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}`] : []),
44
+ ? `Worker result missing queueConfig — no escalation row was written (pre-0.22 SDK)`
45
+ : `Worker result has queueConfig but no escalation row was written`,
46
+ ...(signalId ? [`Awaited signal key: ${signalId}`] : []),
42
47
  ],
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' }] : []),
48
+ guidance: [
49
+ {
50
+ action: 'inspect_worker_result',
51
+ note: signalId
52
+ ? `Workflow is parked on signal "${signalId}" with no escalation backing it, so the normal escalation lifecycle will not resume it. Inspect the worker result payload via the stream browser (list_stream_messages filtered by jid) to confirm the missing queueConfig.`
53
+ : 'Workflow is parked on a signal with no escalation backing it. Inspect the worker result payload via the stream browser (list_stream_messages filtered by jid).',
54
+ },
55
+ { action: 'escalate_to_engineering', note: 'Resolving an orphaned wait requires engineering review of the deploy/SDK version. Do not hand-edit escalation or job state — HotMesh state cannot be unwound.' },
46
56
  ],
47
57
  });
48
58
  }
@@ -59,11 +69,12 @@ function matchPatterns(execution, workerMessages, engineMessages, escalationRow)
59
69
  confidence: 0.99,
60
70
  severity: 'info',
61
71
  evidence: [
62
- `Workflow suspendedescalation exists (status: pending)`,
72
+ `Healthy waitworkflow suspended at a condition with a pending escalation`,
73
+ `This is NOT stalled: a workflow can wait here for days legitimately. A frozen updated_at is the normal signature of a wait, not a fault.`,
63
74
  ...(escalationRow.role ? [`Role: ${escalationRow.role}, Type: ${escalationRow.type}`] : []),
64
75
  ...(escalationRow.id ? [`Escalation id: ${escalationRow.id}`] : []),
65
76
  ],
66
- treatment: [{ action: 'none', note: 'Waiting for human to claim and resolve escalation' }],
77
+ guidance: [{ action: 'none', note: 'No action required — waiting for a human to claim and resolve the escalation through the normal lifecycle.' }],
67
78
  });
68
79
  }
69
80
  // ── Pattern: dead_lettered_activity ──────────────────────────────────────
@@ -77,7 +88,7 @@ function matchPatterns(execution, workerMessages, engineMessages, escalationRow)
77
88
  `${deadWorker.length} worker message(s) dead-lettered, ${deadEngine.length} engine message(s) dead-lettered`,
78
89
  ...samples.map(m => `aid: ${m.aid ?? 'engine'}, retries: ${m.retry_attempt}/${m.max_retry_attempts}, stream: ${m.stream_name}`),
79
90
  ],
80
- treatment: [{
91
+ guidance: [{
81
92
  action: 'investigate_dead_letter',
82
93
  count: deadWorker.length + deadEngine.length,
83
94
  note: 'Message exhausted retries — check worker health and DB connectivity',
@@ -91,7 +102,7 @@ function matchPatterns(execution, workerMessages, engineMessages, escalationRow)
91
102
  confidence: 0.85,
92
103
  severity: 'warning',
93
104
  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' }],
105
+ guidance: [{ action: 'check_worker_health', note: 'Worker claimed message but never ACKd — check for crashed workers' }],
95
106
  });
96
107
  }
97
108
  // ── Pattern: terminal_failure ─────────────────────────────────────────────
@@ -106,7 +117,7 @@ function matchPatterns(execution, workerMessages, engineMessages, escalationRow)
106
117
  `Workflow terminated with failure`,
107
118
  ...(attrs?.error ? [`Error: ${attrs.error}`] : []),
108
119
  ],
109
- treatment: [{ action: 'none', note: 'Workflow terminated — review error and restart if needed' }],
120
+ guidance: [{ action: 'none', note: 'Workflow terminated — review error and restart if needed' }],
110
121
  });
111
122
  }
112
123
  // ── Pattern: never_started ────────────────────────────────────────────────
@@ -117,7 +128,7 @@ function matchPatterns(execution, workerMessages, engineMessages, escalationRow)
117
128
  confidence: 0.90,
118
129
  severity: 'critical',
119
130
  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' }],
131
+ guidance: [{ action: 'check_engine_health', note: 'Check engine health and stream backlog for this workflow type' }],
121
132
  });
122
133
  }
123
134
  // ── Default: healthy ──────────────────────────────────────────────────────
@@ -128,7 +139,7 @@ function matchPatterns(execution, workerMessages, engineMessages, escalationRow)
128
139
  confidence: 0.95,
129
140
  severity: 'info',
130
141
  evidence: [isCompleted ? 'Workflow completed normally' : 'Workflow is running — no anomalies detected'],
131
- treatment: [{ action: 'none', note: 'No intervention required' }],
142
+ guidance: [{ action: 'none', note: 'No intervention required' }],
132
143
  });
133
144
  }
134
145
  // Critical first
@@ -15,6 +15,7 @@ export declare function listEscalations(filters: {
15
15
  visibleRoles?: string[];
16
16
  sort_by?: string;
17
17
  order?: string;
18
+ search?: string;
18
19
  }): Promise<{
19
20
  escalations: LTEscalationRecord[];
20
21
  total: number;
@@ -32,6 +33,7 @@ export declare function listAvailableEscalations(filters: {
32
33
  visibleRoles?: string[];
33
34
  sort_by?: string;
34
35
  order?: string;
36
+ search?: string;
35
37
  }): Promise<{
36
38
  escalations: LTEscalationRecord[];
37
39
  total: number;
@@ -4,9 +4,11 @@ exports.getEscalationStats = getEscalationStats;
4
4
  exports.listDistinctTypes = listDistinctTypes;
5
5
  exports.listEscalations = listEscalations;
6
6
  exports.listAvailableEscalations = listAvailableEscalations;
7
+ const db_1 = require("../../lib/db");
7
8
  const client_1 = require("./client");
8
9
  const map_1 = require("./map");
9
10
  const types_1 = require("./types");
11
+ const sql_1 = require("./sql");
10
12
  /**
11
13
  * Default sort is priority ASC, created_at ASC. A user-chosen `sort_by` maps to
12
14
  * a single column (DESC unless `order='asc'`), matching the legacy behavior.
@@ -20,6 +22,50 @@ function buildOrderBy(sortBy, order) {
20
22
  { column: 'created_at', direction: 'asc' },
21
23
  ];
22
24
  }
25
+ /**
26
+ * ORDER BY clause for the raw search query. `sortBy` is checked against the
27
+ * SORTABLE_COLUMNS whitelist before interpolation, so it is injection-safe.
28
+ */
29
+ function buildSearchOrderBy(sortBy, order) {
30
+ const dir = order === 'asc' ? 'ASC' : 'DESC';
31
+ if (sortBy && types_1.SORTABLE_COLUMNS.has(sortBy)) {
32
+ return `${sortBy} ${dir}`;
33
+ }
34
+ return 'priority ASC, created_at ASC';
35
+ }
36
+ /**
37
+ * Server-side free-text search over the `lt_escalations` view. Runs when a
38
+ * caller supplies a non-empty `search` term — the SDK `client.list()` cannot do
39
+ * free-text, so this is raw SQL on the shared table (see ./sql.ts). All other
40
+ * filters combine with the term (AND).
41
+ */
42
+ async function searchEscalations(params) {
43
+ await (0, client_1.ensureEscalationCompatView)();
44
+ const pool = (0, db_1.getPool)();
45
+ // Coerce empty strings to NULL so an absent filter (e.g. assigned_to='') does
46
+ // not become `column = ''` and match zero rows. Mirrors the SDK path's
47
+ // `if (filters.x)` truthiness guards.
48
+ const filterArgs = [
49
+ params.status || null,
50
+ params.role || null,
51
+ params.roles && params.roles.length ? params.roles : null,
52
+ params.type || null,
53
+ params.subtype || null,
54
+ params.priority ?? null,
55
+ params.assigned_to || null,
56
+ params.available ?? null,
57
+ params.search,
58
+ ];
59
+ const orderBy = buildSearchOrderBy(params.sort_by, params.order);
60
+ const [rows, countRows] = await Promise.all([
61
+ pool.query((0, sql_1.searchEscalationsQuery)(orderBy), [...filterArgs, params.limit, params.offset]),
62
+ pool.query(sql_1.COUNT_SEARCH_ESCALATIONS, filterArgs),
63
+ ]);
64
+ return {
65
+ escalations: (0, map_1.toEscalationRecords)(rows.rows),
66
+ total: countRows.rows[0]?.total ?? 0,
67
+ };
68
+ }
23
69
  async function getEscalationStats(visibleRoles, period) {
24
70
  const client = await (0, client_1.escalations)();
25
71
  return client.stats({
@@ -32,6 +78,26 @@ async function listDistinctTypes() {
32
78
  return client.listDistinctTypes();
33
79
  }
34
80
  async function listEscalations(filters) {
81
+ // Active claim semantics: assigned_to-active and `claimed` both mean "held now".
82
+ const heldNow = !!(filters.assigned_to || filters.claimed);
83
+ // Free-text search → server-side SQL path (the SDK list() has no free-text).
84
+ if (filters.search) {
85
+ return searchEscalations({
86
+ status: filters.status,
87
+ role: filters.role,
88
+ roles: filters.visibleRoles,
89
+ type: filters.type,
90
+ subtype: filters.subtype,
91
+ priority: filters.priority,
92
+ assigned_to: filters.assigned_to,
93
+ available: heldNow ? false : undefined,
94
+ search: filters.search,
95
+ limit: filters.limit ?? 50,
96
+ offset: filters.offset ?? 0,
97
+ sort_by: filters.sort_by,
98
+ order: filters.order,
99
+ });
100
+ }
35
101
  const client = await (0, client_1.escalations)();
36
102
  // Shared filter — passed to both list() and count() so totals stay in sync.
37
103
  const where = {
@@ -44,8 +110,7 @@ async function listEscalations(filters) {
44
110
  };
45
111
  if (filters.assigned_to)
46
112
  where.assignedTo = filters.assigned_to;
47
- // Active claim semantics: assigned_to-active and `claimed` both mean "held now".
48
- if (filters.assigned_to || filters.claimed)
113
+ if (heldNow)
49
114
  where.available = false;
50
115
  const [rows, total] = await Promise.all([
51
116
  client.list({
@@ -62,6 +127,23 @@ async function listEscalations(filters) {
62
127
  * List available escalations: pending AND (unassigned OR expired claim).
63
128
  */
64
129
  async function listAvailableEscalations(filters) {
130
+ // Free-text search → server-side SQL path (available = pending + no active claim).
131
+ if (filters.search) {
132
+ return searchEscalations({
133
+ status: 'pending',
134
+ role: filters.role,
135
+ roles: filters.visibleRoles,
136
+ type: filters.type,
137
+ subtype: filters.subtype,
138
+ priority: filters.priority,
139
+ available: true,
140
+ search: filters.search,
141
+ limit: filters.limit ?? 50,
142
+ offset: filters.offset ?? 0,
143
+ sort_by: filters.sort_by,
144
+ order: filters.order,
145
+ });
146
+ }
65
147
  const client = await (0, client_1.escalations)();
66
148
  const where = {
67
149
  status: 'pending',
@@ -8,6 +8,13 @@
8
8
  * table. Clears both `assigned_until` and `claim_expires_at`.
9
9
  */
10
10
  export declare const RELEASE_EXPIRED_CLAIMS = "UPDATE public.hmsh_escalations\nSET assigned_to = NULL,\n assigned_until = NULL,\n claimed_at = NULL,\n claim_expires_at = NULL,\n updated_at = NOW()\nWHERE status = 'pending'\n AND assigned_to IS NOT NULL\n AND assigned_until <= NOW()";
11
+ /** Count matching the search WHERE (params $1–$9). */
12
+ export declare const COUNT_SEARCH_ESCALATIONS = "SELECT COUNT(*)::int AS total\n FROM public.lt_escalations\n WHERE ($1::text IS NULL OR status = $1)\n AND ($2::text IS NULL OR role = $2)\n AND ($3::text[] IS NULL OR role = ANY($3))\n AND ($4::text IS NULL OR type = $4)\n AND ($5::text IS NULL OR subtype = $5)\n AND ($6::int IS NULL OR priority = $6)\n AND ($7::text IS NULL OR assigned_to = $7)\n AND ($8::boolean IS NULL OR available = $8)\n AND ($9::text IS NULL OR (\n description ILIKE '%' || $9 || '%'\n OR type ILIKE '%' || $9 || '%'\n OR subtype ILIKE '%' || $9 || '%'\n OR role ILIKE '%' || $9 || '%'\n OR workflow_id ILIKE '%' || $9 || '%'\n OR origin_id ILIKE '%' || $9 || '%'\n OR id::text ILIKE '%' || $9 || '%'\n OR metadata::text ILIKE '%' || $9 || '%'\n ))";
13
+ /**
14
+ * Build the search SELECT. `orderBy` is composed from a whitelist by the caller
15
+ * (never raw user input), so it is safe to interpolate; everything else is bound.
16
+ */
17
+ export declare function searchEscalationsQuery(orderBy: string): string;
11
18
  /**
12
19
  * Atomic resolve by metadata with signal guard.
13
20
  *
@@ -11,7 +11,8 @@
11
11
  // `hmsh_escalations` here rather than composed from generic SDK calls.
12
12
  // ---------------------------------------------------------------------------
13
13
  Object.defineProperty(exports, "__esModule", { value: true });
14
- exports.RESOLVE_BY_METADATA_ATOMIC = exports.RELEASE_EXPIRED_CLAIMS = void 0;
14
+ exports.RESOLVE_BY_METADATA_ATOMIC = exports.COUNT_SEARCH_ESCALATIONS = exports.RELEASE_EXPIRED_CLAIMS = void 0;
15
+ exports.searchEscalationsQuery = searchEscalationsQuery;
15
16
  /**
16
17
  * Sweep expired claims back to the available pool.
17
18
  *
@@ -31,6 +32,54 @@ SET assigned_to = NULL,
31
32
  WHERE status = 'pending'
32
33
  AND assigned_to IS NOT NULL
33
34
  AND assigned_until <= NOW()`;
35
+ /**
36
+ * Free-text escalation search.
37
+ *
38
+ * The SDK's `client.list()` filters by structured columns only — it has no
39
+ * free-text predicate. Long-tail's contract needs a search box that matches a
40
+ * correlation key (order id, ticket id) or any visible field across the WHOLE
41
+ * result set, not just the current page. That is server-side SQL here (over the
42
+ * `lt_escalations` view, which adds the computed `available` flag), following the
43
+ * same raw-SQL-on-the-shared-table pattern as the atomic resolve below — so the
44
+ * filter flows SQL→service→API→route→dashboard rather than being faked client-side.
45
+ *
46
+ * All structured filters are optional and combine with the search term (AND).
47
+ * `metadata::text ILIKE` scans the JSONB as text (the GIN index is containment-only
48
+ * and cannot serve ILIKE) — bounded in practice by the other filters and the page.
49
+ *
50
+ * $1 status, $2 role, $3 roles (text[] visibleRoles), $4 type, $5 subtype,
51
+ * $6 priority, $7 assigned_to, $8 available (bool), $9 search.
52
+ * The list query adds $10 limit, $11 offset.
53
+ */
54
+ const SEARCH_ESCALATIONS_WHERE = `\
55
+ FROM public.lt_escalations
56
+ WHERE ($1::text IS NULL OR status = $1)
57
+ AND ($2::text IS NULL OR role = $2)
58
+ AND ($3::text[] IS NULL OR role = ANY($3))
59
+ AND ($4::text IS NULL OR type = $4)
60
+ AND ($5::text IS NULL OR subtype = $5)
61
+ AND ($6::int IS NULL OR priority = $6)
62
+ AND ($7::text IS NULL OR assigned_to = $7)
63
+ AND ($8::boolean IS NULL OR available = $8)
64
+ AND ($9::text IS NULL OR (
65
+ description ILIKE '%' || $9 || '%'
66
+ OR type ILIKE '%' || $9 || '%'
67
+ OR subtype ILIKE '%' || $9 || '%'
68
+ OR role ILIKE '%' || $9 || '%'
69
+ OR workflow_id ILIKE '%' || $9 || '%'
70
+ OR origin_id ILIKE '%' || $9 || '%'
71
+ OR id::text ILIKE '%' || $9 || '%'
72
+ OR metadata::text ILIKE '%' || $9 || '%'
73
+ ))`;
74
+ /** Count matching the search WHERE (params $1–$9). */
75
+ exports.COUNT_SEARCH_ESCALATIONS = `SELECT COUNT(*)::int AS total\n${SEARCH_ESCALATIONS_WHERE}`;
76
+ /**
77
+ * Build the search SELECT. `orderBy` is composed from a whitelist by the caller
78
+ * (never raw user input), so it is safe to interpolate; everything else is bound.
79
+ */
80
+ function searchEscalationsQuery(orderBy) {
81
+ return `SELECT *\n${SEARCH_ESCALATIONS_WHERE}\n ORDER BY ${orderBy}\n LIMIT $10 OFFSET $11`;
82
+ }
34
83
  /**
35
84
  * Atomic resolve by metadata with signal guard.
36
85
  *
@@ -4,9 +4,11 @@ import type { ResolvedHandle } from './types';
4
4
  * HotMesh needs to get a workflow handle.
5
5
  *
6
6
  * 1. Look up lt_tasks by workflow_id — returns workflow_type and task_queue.
7
- * 2. If no task record (e.g., orchestrators/containers), fall back to
8
- * durable.jobs (entity) + lt_config_workflows (task_queue).
7
+ * 2. If no task record (e.g., orchestrators/containers, or a child running in
8
+ * another app), fall back to `<appId>.jobs` (entity) + lt_config_workflows
9
+ * (task_queue). `appId` selects the HotMesh namespace to scan — children of
10
+ * a workflow live under their own app's schema, not always `durable`.
9
11
  * 3. If task_queue is null (pre-migration record), fall back to lt_config_workflows.
10
12
  * 4. Throws if the workflow cannot be resolved.
11
13
  */
12
- export declare function resolveWorkflowHandle(workflowId: string): Promise<ResolvedHandle>;
14
+ export declare function resolveWorkflowHandle(workflowId: string, appId?: string): Promise<ResolvedHandle>;
@@ -9,12 +9,14 @@ const sql_1 = require("./sql");
9
9
  * HotMesh needs to get a workflow handle.
10
10
  *
11
11
  * 1. Look up lt_tasks by workflow_id — returns workflow_type and task_queue.
12
- * 2. If no task record (e.g., orchestrators/containers), fall back to
13
- * durable.jobs (entity) + lt_config_workflows (task_queue).
12
+ * 2. If no task record (e.g., orchestrators/containers, or a child running in
13
+ * another app), fall back to `<appId>.jobs` (entity) + lt_config_workflows
14
+ * (task_queue). `appId` selects the HotMesh namespace to scan — children of
15
+ * a workflow live under their own app's schema, not always `durable`.
14
16
  * 3. If task_queue is null (pre-migration record), fall back to lt_config_workflows.
15
17
  * 4. Throws if the workflow cannot be resolved.
16
18
  */
17
- async function resolveWorkflowHandle(workflowId) {
19
+ async function resolveWorkflowHandle(workflowId, appId = 'durable') {
18
20
  const pool = (0, db_1.getPool)();
19
21
  // 1. Try lt_tasks first (leaf workflows)
20
22
  const { rows } = await pool.query(sql_1.RESOLVE_TASK_BY_WORKFLOW_ID, [workflowId]);
@@ -29,9 +31,10 @@ async function resolveWorkflowHandle(workflowId) {
29
31
  return { taskQueue: configRows[0].task_queue, workflowName: workflow_type };
30
32
  }
31
33
  }
32
- // 2. Fall back to durable.jobs — handles orchestrators/containers that
33
- // have no lt_tasks record but do have a job with an entity tag.
34
- const { rows: jobRows } = await pool.query(sql_1.RESOLVE_JOB_ENTITY, [`hmsh:durable:j:${workflowId}`]);
34
+ // 2. Fall back to <appId>.jobs — handles orchestrators/containers that
35
+ // have no lt_tasks record but do have a job with an entity tag, and
36
+ // children running in a non-default namespace.
37
+ const { rows: jobRows } = await pool.query((0, sql_1.RESOLVE_JOB_ENTITY)(appId), [`hmsh:${appId}:j:${workflowId}`]);
35
38
  if (jobRows.length > 0 && jobRows[0].entity) {
36
39
  const entity = jobRows[0].entity;
37
40
  // 2a. Try lt_config_workflows for task_queue