@hotmeshio/long-tail 0.5.10 → 0.5.11

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 (187) 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 +3 -1
  4. package/build/api/diagnostics.js +1 -1
  5. package/build/api/settings.d.ts +3 -2
  6. package/build/api/settings.js +11 -2
  7. package/build/modules/version.d.ts +16 -0
  8. package/build/modules/version.js +34 -0
  9. package/build/routes/controlplane.js +1 -0
  10. package/build/routes/diagnostics.js +18 -10
  11. package/build/services/controlplane/index.js +3 -2
  12. package/build/services/controlplane/stream-messages-sql.d.ts +4 -3
  13. package/build/services/controlplane/stream-messages-sql.js +12 -7
  14. package/build/services/controlplane/types.d.ts +2 -0
  15. package/build/services/diagnostics/index.d.ts +15 -3
  16. package/build/services/diagnostics/index.js +71 -32
  17. package/build/services/diagnostics/patterns.d.ts +13 -3
  18. package/build/services/diagnostics/patterns.js +25 -14
  19. package/build/system/mcp-servers/admin/diagnostics.d.ts +9 -3
  20. package/build/system/mcp-servers/admin/diagnostics.js +31 -21
  21. package/build/system/mcp-servers/admin/schemas.d.ts +9 -3
  22. package/build/system/mcp-servers/admin/schemas.js +5 -3
  23. package/build/tsconfig.tsbuildinfo +1 -1
  24. package/build/types/auth.d.ts +1 -1
  25. package/dashboard/dist/assets/{AdminDashboard-0LSslIQ7.js → AdminDashboard-DZpduYdW.js} +2 -2
  26. package/dashboard/dist/assets/{AdminDashboard-0LSslIQ7.js.map → AdminDashboard-DZpduYdW.js.map} +1 -1
  27. package/dashboard/dist/assets/{AgentConfigPage-hqBr1540.js → AgentConfigPage-SFnHMIP6.js} +2 -2
  28. package/dashboard/dist/assets/{AgentConfigPage-hqBr1540.js.map → AgentConfigPage-SFnHMIP6.js.map} +1 -1
  29. package/dashboard/dist/assets/{AgentDetailPage-bnXR1ezD.js → AgentDetailPage-C-2iSaDs.js} +2 -2
  30. package/dashboard/dist/assets/{AgentDetailPage-bnXR1ezD.js.map → AgentDetailPage-C-2iSaDs.js.map} +1 -1
  31. package/dashboard/dist/assets/{AgentsPage-tJIlrnfM.js → AgentsPage-C3FztFd_.js} +2 -2
  32. package/dashboard/dist/assets/{AgentsPage-tJIlrnfM.js.map → AgentsPage-C3FztFd_.js.map} +1 -1
  33. package/dashboard/dist/assets/{AvailableEscalationsPage-B7gEfcw3.js → AvailableEscalationsPage-DIqdBCwe.js} +2 -2
  34. package/dashboard/dist/assets/{AvailableEscalationsPage-B7gEfcw3.js.map → AvailableEscalationsPage-DIqdBCwe.js.map} +1 -1
  35. package/dashboard/dist/assets/{BotPicker-fFE4GAaO.js → BotPicker-Dse2jW4b.js} +2 -2
  36. package/dashboard/dist/assets/{BotPicker-fFE4GAaO.js.map → BotPicker-Dse2jW4b.js.map} +1 -1
  37. package/dashboard/dist/assets/{CapabilitiesPage-DotZ7iQc.js → CapabilitiesPage-DTfKCzBQ.js} +2 -2
  38. package/dashboard/dist/assets/{CapabilitiesPage-DotZ7iQc.js.map → CapabilitiesPage-DTfKCzBQ.js.map} +1 -1
  39. package/dashboard/dist/assets/{CollapsibleSection-D97SYypF.js → CollapsibleSection-8vf38J6K.js} +2 -2
  40. package/dashboard/dist/assets/{CollapsibleSection-D97SYypF.js.map → CollapsibleSection-8vf38J6K.js.map} +1 -1
  41. package/dashboard/dist/assets/{CredentialsPage-C08U6dlv.js → CredentialsPage-7C4M-hrH.js} +2 -2
  42. package/dashboard/dist/assets/{CredentialsPage-C08U6dlv.js.map → CredentialsPage-7C4M-hrH.js.map} +1 -1
  43. package/dashboard/dist/assets/{CronLabel-C2TuCl4h.js → CronLabel-DrGWtaSY.js} +2 -2
  44. package/dashboard/dist/assets/{CronLabel-C2TuCl4h.js.map → CronLabel-DrGWtaSY.js.map} +1 -1
  45. package/dashboard/dist/assets/{CustomDurationPicker-C3zGsESx.js → CustomDurationPicker-Vcmr4LiR.js} +2 -2
  46. package/dashboard/dist/assets/{CustomDurationPicker-C3zGsESx.js.map → CustomDurationPicker-Vcmr4LiR.js.map} +1 -1
  47. package/dashboard/dist/assets/{ElapsedCell-DF4m0cv_.js → ElapsedCell-BNexhR5d.js} +2 -2
  48. package/dashboard/dist/assets/{ElapsedCell-DF4m0cv_.js.map → ElapsedCell-BNexhR5d.js.map} +1 -1
  49. package/dashboard/dist/assets/{EscalationsOverview-blGOYKtJ.js → EscalationsOverview-BI3DEwYP.js} +2 -2
  50. package/dashboard/dist/assets/{EscalationsOverview-blGOYKtJ.js.map → EscalationsOverview-BI3DEwYP.js.map} +1 -1
  51. package/dashboard/dist/assets/EventTable-CwvX_L9t.js +2 -0
  52. package/dashboard/dist/assets/EventTable-CwvX_L9t.js.map +1 -0
  53. package/dashboard/dist/assets/{GraphInvokePage-QVAUHmjI.js → GraphInvokePage-C-3b8WK0.js} +2 -2
  54. package/dashboard/dist/assets/{GraphInvokePage-QVAUHmjI.js.map → GraphInvokePage-C-3b8WK0.js.map} +1 -1
  55. package/dashboard/dist/assets/{HomePage-DvYgvCcO.js → HomePage-BS_-jcHr.js} +2 -2
  56. package/dashboard/dist/assets/{HomePage-DvYgvCcO.js.map → HomePage-BS_-jcHr.js.map} +1 -1
  57. package/dashboard/dist/assets/{ListToolbar-D6tQ3ZG2.js → ListToolbar-BH9rwQVE.js} +2 -2
  58. package/dashboard/dist/assets/{ListToolbar-D6tQ3ZG2.js.map → ListToolbar-BH9rwQVE.js.map} +1 -1
  59. package/dashboard/dist/assets/{McpOverview-BClbGYb8.js → McpOverview-C9iOdE7E.js} +2 -2
  60. package/dashboard/dist/assets/{McpOverview-BClbGYb8.js.map → McpOverview-C9iOdE7E.js.map} +1 -1
  61. package/dashboard/dist/assets/{McpQueryDetailPage-DHjfDRdm.js → McpQueryDetailPage-DMN-yqvJ.js} +3 -3
  62. package/dashboard/dist/assets/{McpQueryDetailPage-DHjfDRdm.js.map → McpQueryDetailPage-DMN-yqvJ.js.map} +1 -1
  63. package/dashboard/dist/assets/{McpQueryPage-qtEuMDrd.js → McpQueryPage-Db1LUKVs.js} +2 -2
  64. package/dashboard/dist/assets/{McpQueryPage-qtEuMDrd.js.map → McpQueryPage-Db1LUKVs.js.map} +1 -1
  65. package/dashboard/dist/assets/McpRunDetailPage-Wb47sRvo.js +2 -0
  66. package/dashboard/dist/assets/{McpRunDetailPage-DRbIZMU2.js.map → McpRunDetailPage-Wb47sRvo.js.map} +1 -1
  67. package/dashboard/dist/assets/{McpRunsPage-CMfwXQ4R.js → McpRunsPage--tgT5uo7.js} +2 -2
  68. package/dashboard/dist/assets/{McpRunsPage-CMfwXQ4R.js.map → McpRunsPage--tgT5uo7.js.map} +1 -1
  69. package/dashboard/dist/assets/{OperatorDashboard-Dq2pfV68.js → OperatorDashboard-BWVUbJkZ.js} +2 -2
  70. package/dashboard/dist/assets/{OperatorDashboard-Dq2pfV68.js.map → OperatorDashboard-BWVUbJkZ.js.map} +1 -1
  71. package/dashboard/dist/assets/{ProcessDetailPage-DjQuNERz.js → ProcessDetailPage-CvPx-h8y.js} +2 -2
  72. package/dashboard/dist/assets/{ProcessDetailPage-DjQuNERz.js.map → ProcessDetailPage-CvPx-h8y.js.map} +1 -1
  73. package/dashboard/dist/assets/{ProcessesListPage-vJimii8-.js → ProcessesListPage-CWiRM_h3.js} +2 -2
  74. package/dashboard/dist/assets/{ProcessesListPage-vJimii8-.js.map → ProcessesListPage-CWiRM_h3.js.map} +1 -1
  75. package/dashboard/dist/assets/{RolesPage-CFCN_mRF.js → RolesPage-W5ZjOHpg.js} +2 -2
  76. package/dashboard/dist/assets/{RolesPage-CFCN_mRF.js.map → RolesPage-W5ZjOHpg.js.map} +1 -1
  77. package/dashboard/dist/assets/{RunAsSelector-sBxAvlRf.js → RunAsSelector-CvxGTNGT.js} +2 -2
  78. package/dashboard/dist/assets/{RunAsSelector-sBxAvlRf.js.map → RunAsSelector-CvxGTNGT.js.map} +1 -1
  79. package/dashboard/dist/assets/StreamMessageDetail-BF0tM0cd.js +2 -0
  80. package/dashboard/dist/assets/StreamMessageDetail-BF0tM0cd.js.map +1 -0
  81. package/dashboard/dist/assets/SwimlaneTimeline-Bv-KHY9s.js +2 -0
  82. package/dashboard/dist/assets/SwimlaneTimeline-Bv-KHY9s.js.map +1 -0
  83. package/dashboard/dist/assets/{TaskDetailPage-CgFN5uWn.js → TaskDetailPage-Bzb_fuLy.js} +2 -2
  84. package/dashboard/dist/assets/{TaskDetailPage-CgFN5uWn.js.map → TaskDetailPage-Bzb_fuLy.js.map} +1 -1
  85. package/dashboard/dist/assets/{TasksListPage-qj25rRZL.js → TasksListPage-CBezWaZL.js} +2 -2
  86. package/dashboard/dist/assets/{TasksListPage-qj25rRZL.js.map → TasksListPage-CBezWaZL.js.map} +1 -1
  87. package/dashboard/dist/assets/{TimeAgo-C7LylVjP.js → TimeAgo-CKHfTzat.js} +2 -2
  88. package/dashboard/dist/assets/{TimeAgo-C7LylVjP.js.map → TimeAgo-CKHfTzat.js.map} +1 -1
  89. package/dashboard/dist/assets/{TimestampCell-CrCtoBlo.js → TimestampCell-Dsje8GH-.js} +2 -2
  90. package/dashboard/dist/assets/{TimestampCell-CrCtoBlo.js.map → TimestampCell-Dsje8GH-.js.map} +1 -1
  91. package/dashboard/dist/assets/{ToolTestPanel-DYGlB-QW.js → ToolTestPanel-3OcG18e5.js} +2 -2
  92. package/dashboard/dist/assets/{ToolTestPanel-DYGlB-QW.js.map → ToolTestPanel-3OcG18e5.js.map} +1 -1
  93. package/dashboard/dist/assets/{TopicDetailPage-DLY6X_R6.js → TopicDetailPage-DCtv91hH.js} +2 -2
  94. package/dashboard/dist/assets/{TopicDetailPage-DLY6X_R6.js.map → TopicDetailPage-DCtv91hH.js.map} +1 -1
  95. package/dashboard/dist/assets/{TopicsPage-C5nOLsLx.js → TopicsPage-DVGSNc_E.js} +2 -2
  96. package/dashboard/dist/assets/{TopicsPage-C5nOLsLx.js.map → TopicsPage-DVGSNc_E.js.map} +1 -1
  97. package/dashboard/dist/assets/{UserName-CgUZyiwm.js → UserName-xHAgkk8M.js} +2 -2
  98. package/dashboard/dist/assets/{UserName-CgUZyiwm.js.map → UserName-xHAgkk8M.js.map} +1 -1
  99. package/dashboard/dist/assets/WorkflowExecutionPage-CWNxsS8a.js +2 -0
  100. package/dashboard/dist/assets/{WorkflowExecutionPage-CDr5CTM2.js.map → WorkflowExecutionPage-CWNxsS8a.js.map} +1 -1
  101. package/dashboard/dist/assets/{WorkflowsDashboard-tCBP0i0Y.js → WorkflowsDashboard-M13lIaw7.js} +2 -2
  102. package/dashboard/dist/assets/{WorkflowsDashboard-tCBP0i0Y.js.map → WorkflowsDashboard-M13lIaw7.js.map} +1 -1
  103. package/dashboard/dist/assets/{WorkflowsOverview-CaMeZwde.js → WorkflowsOverview-e7jWSj_n.js} +2 -2
  104. package/dashboard/dist/assets/{WorkflowsOverview-CaMeZwde.js.map → WorkflowsOverview-e7jWSj_n.js.map} +1 -1
  105. package/dashboard/dist/assets/{YamlWorkflowDetailPage-C3uyCiIX.js → YamlWorkflowDetailPage-CdAeam76.js} +2 -2
  106. package/dashboard/dist/assets/{YamlWorkflowDetailPage-C3uyCiIX.js.map → YamlWorkflowDetailPage-CdAeam76.js.map} +1 -1
  107. package/dashboard/dist/assets/{YamlWorkflowsPage-DRnYgsvA.js → YamlWorkflowsPage-S_gKs2Di.js} +2 -2
  108. package/dashboard/dist/assets/{YamlWorkflowsPage-DRnYgsvA.js.map → YamlWorkflowsPage-S_gKs2Di.js.map} +1 -1
  109. package/dashboard/dist/assets/{agents-HMkTc425.js → agents-JE4aL07D.js} +2 -2
  110. package/dashboard/dist/assets/{agents-HMkTc425.js.map → agents-JE4aL07D.js.map} +1 -1
  111. package/dashboard/dist/assets/{bots-CDe2jXNx.js → bots-cAzKkDtQ.js} +2 -2
  112. package/dashboard/dist/assets/{bots-CDe2jXNx.js.map → bots-cAzKkDtQ.js.map} +1 -1
  113. package/dashboard/dist/assets/{capabilities-DhnM7Ztb.js → capabilities-RBFXLAXF.js} +2 -2
  114. package/dashboard/dist/assets/{capabilities-DhnM7Ztb.js.map → capabilities-RBFXLAXF.js.map} +1 -1
  115. package/dashboard/dist/assets/{controlplane-BwIJ9ZC_.js → controlplane-DGhv3rwI.js} +2 -2
  116. package/dashboard/dist/assets/{controlplane-BwIJ9ZC_.js.map → controlplane-DGhv3rwI.js.map} +1 -1
  117. package/dashboard/dist/assets/{escalation-columns-BKqSywVA.js → escalation-columns-DqTkKbCv.js} +2 -2
  118. package/dashboard/dist/assets/{escalation-columns-BKqSywVA.js.map → escalation-columns-DqTkKbCv.js.map} +1 -1
  119. package/dashboard/dist/assets/{escalation-Djk5XKmX.js → escalation-ne7dIaPa.js} +2 -2
  120. package/dashboard/dist/assets/{escalation-Djk5XKmX.js.map → escalation-ne7dIaPa.js.map} +1 -1
  121. package/dashboard/dist/assets/{index-DRIJM1L-.js → index-BTqhAT6p.js} +2 -2
  122. package/dashboard/dist/assets/{index-DRIJM1L-.js.map → index-BTqhAT6p.js.map} +1 -1
  123. package/dashboard/dist/assets/{index-BW-fIquS.js → index-BbJuC9kg.js} +2 -2
  124. package/dashboard/dist/assets/{index-BW-fIquS.js.map → index-BbJuC9kg.js.map} +1 -1
  125. package/dashboard/dist/assets/{index-mEQ6kCnt.js → index-C2l4GcZ7.js} +2 -2
  126. package/dashboard/dist/assets/{index-mEQ6kCnt.js.map → index-C2l4GcZ7.js.map} +1 -1
  127. package/dashboard/dist/assets/{index-BYpWOUEe.js → index-CByjpYlU.js} +2 -2
  128. package/dashboard/dist/assets/{index-BYpWOUEe.js.map → index-CByjpYlU.js.map} +1 -1
  129. package/dashboard/dist/assets/{index-rUST2YsO.js → index-CEpw_Fh8.js} +2 -2
  130. package/dashboard/dist/assets/{index-rUST2YsO.js.map → index-CEpw_Fh8.js.map} +1 -1
  131. package/dashboard/dist/assets/{index-CPIYW81T.js → index-CQDn-bLr.js} +2 -2
  132. package/dashboard/dist/assets/{index-CPIYW81T.js.map → index-CQDn-bLr.js.map} +1 -1
  133. package/dashboard/dist/assets/{index-DBi9Q9Hw.js → index-CgyucK0e.js} +2 -2
  134. package/dashboard/dist/assets/{index-DBi9Q9Hw.js.map → index-CgyucK0e.js.map} +1 -1
  135. package/dashboard/dist/assets/index-ClvYSNZd.css +1 -0
  136. package/dashboard/dist/assets/{index-E2hQ8DIi.js → index-CucXk58o.js} +2 -2
  137. package/dashboard/dist/assets/{index-E2hQ8DIi.js.map → index-CucXk58o.js.map} +1 -1
  138. package/dashboard/dist/assets/{index-BFQ2t-p1.js → index-DPJW96d2.js} +21 -21
  139. package/dashboard/dist/assets/index-DPJW96d2.js.map +1 -0
  140. package/dashboard/dist/assets/index-DeZhNCf5.js +2 -0
  141. package/dashboard/dist/assets/index-DeZhNCf5.js.map +1 -0
  142. package/dashboard/dist/assets/{index-BNkpns_E.js → index-DgzgRTNg.js} +2 -2
  143. package/dashboard/dist/assets/{index-BNkpns_E.js.map → index-DgzgRTNg.js.map} +1 -1
  144. package/dashboard/dist/assets/{index-Cydgj_Co.js → index-Do8Xp3pa.js} +2 -2
  145. package/dashboard/dist/assets/{index-Cydgj_Co.js.map → index-Do8Xp3pa.js.map} +1 -1
  146. package/dashboard/dist/assets/{index-Dhy2PVAw.js → index-QxAYCJ1S.js} +2 -2
  147. package/dashboard/dist/assets/{index-Dhy2PVAw.js.map → index-QxAYCJ1S.js.map} +1 -1
  148. package/dashboard/dist/assets/{knowledge-jQE3dBVi.js → knowledge-DzRpsm3D.js} +2 -2
  149. package/dashboard/dist/assets/{knowledge-jQE3dBVi.js.map → knowledge-DzRpsm3D.js.map} +1 -1
  150. package/dashboard/dist/assets/{mcp-DtEGfZiz.js → mcp-B1lc6nL4.js} +2 -2
  151. package/dashboard/dist/assets/{mcp-DtEGfZiz.js.map → mcp-B1lc6nL4.js.map} +1 -1
  152. package/dashboard/dist/assets/{mcp-query-CiTLVuDC.js → mcp-query-CXuweaN3.js} +2 -2
  153. package/dashboard/dist/assets/{mcp-query-CiTLVuDC.js.map → mcp-query-CXuweaN3.js.map} +1 -1
  154. package/dashboard/dist/assets/{pipelines-ClNdzuUK.js → pipelines-D4VLWDvu.js} +2 -2
  155. package/dashboard/dist/assets/{pipelines-ClNdzuUK.js.map → pipelines-D4VLWDvu.js.map} +1 -1
  156. package/dashboard/dist/assets/{roles-BmHbRaPT.js → roles-CSpHRRu8.js} +2 -2
  157. package/dashboard/dist/assets/{roles-BmHbRaPT.js.map → roles-CSpHRRu8.js.map} +1 -1
  158. package/dashboard/dist/assets/{tasks-BPONxLcq.js → tasks-CWUsKhM4.js} +2 -2
  159. package/dashboard/dist/assets/{tasks-BPONxLcq.js.map → tasks-CWUsKhM4.js.map} +1 -1
  160. package/dashboard/dist/assets/{topics-XREPRxwo.js → topics-BXwstrzB.js} +2 -2
  161. package/dashboard/dist/assets/{topics-XREPRxwo.js.map → topics-BXwstrzB.js.map} +1 -1
  162. package/dashboard/dist/assets/{useEventHooks-M7SaEfel.js → useEventHooks-CjygrbYy.js} +2 -2
  163. package/dashboard/dist/assets/{useEventHooks-M7SaEfel.js.map → useEventHooks-CjygrbYy.js.map} +1 -1
  164. package/dashboard/dist/assets/{useNamespace-BQHGkdBv.js → useNamespace-CsepDyR5.js} +2 -2
  165. package/dashboard/dist/assets/{useNamespace-BQHGkdBv.js.map → useNamespace-CsepDyR5.js.map} +1 -1
  166. package/dashboard/dist/assets/{useYamlActivityEvents-B7JtUGSE.js → useYamlActivityEvents-gWIG6akB.js} +2 -2
  167. package/dashboard/dist/assets/{useYamlActivityEvents-B7JtUGSE.js.map → useYamlActivityEvents-gWIG6akB.js.map} +1 -1
  168. package/dashboard/dist/assets/{users-BpjSNhQe.js → users-DvhHbR0R.js} +2 -2
  169. package/dashboard/dist/assets/{users-BpjSNhQe.js.map → users-DvhHbR0R.js.map} +1 -1
  170. package/dashboard/dist/assets/{workflows-k4jE0ECC.js → workflows-C7Cam2Ok.js} +2 -2
  171. package/dashboard/dist/assets/{workflows-k4jE0ECC.js.map → workflows-C7Cam2Ok.js.map} +1 -1
  172. package/dashboard/dist/assets/{yaml-workflows-DpviSLEL.js → yaml-workflows-ChUOE22W.js} +2 -2
  173. package/dashboard/dist/assets/{yaml-workflows-DpviSLEL.js.map → yaml-workflows-ChUOE22W.js.map} +1 -1
  174. package/dashboard/dist/index.html +2 -2
  175. package/docs/api/mcp/admin.md +94 -1
  176. package/docs/mcp.md +102 -0
  177. package/package.json +1 -1
  178. package/dashboard/dist/assets/EventTable-DzrMH-ux.js +0 -2
  179. package/dashboard/dist/assets/EventTable-DzrMH-ux.js.map +0 -1
  180. package/dashboard/dist/assets/McpRunDetailPage-DRbIZMU2.js +0 -2
  181. package/dashboard/dist/assets/SwimlaneTimeline-2Eet4OOL.js +0 -2
  182. package/dashboard/dist/assets/SwimlaneTimeline-2Eet4OOL.js.map +0 -1
  183. package/dashboard/dist/assets/WorkflowExecutionPage-CDr5CTM2.js +0 -2
  184. package/dashboard/dist/assets/index-BFQ2t-p1.js.map +0 -1
  185. package/dashboard/dist/assets/index-DSki_Ub6.css +0 -1
  186. package/dashboard/dist/assets/index-u08vQUaw.js +0 -2
  187. package/dashboard/dist/assets/index-u08vQUaw.js.map +0 -1
@@ -99,4 +99,5 @@ export declare function listStreamMessages(input: {
99
99
  workflow_name?: string | null;
100
100
  jid?: string | null;
101
101
  aid?: string | null;
102
+ dad?: string | null;
102
103
  }): Promise<LTApiResult>;
@@ -196,6 +196,7 @@ async function listStreamMessages(input) {
196
196
  workflow_name: input.workflow_name,
197
197
  jid: input.jid,
198
198
  aid: input.aid,
199
+ dad: input.dad,
199
200
  });
200
201
  return { status: 200, data: result };
201
202
  }
@@ -2,14 +2,16 @@ import type { LTApiResult } from '../types/sdk';
2
2
  export declare function diagnose(input: {
3
3
  workflowId: string;
4
4
  appId?: string;
5
+ maxEvents?: number;
5
6
  }): Promise<LTApiResult>;
6
7
  export declare function stalledJobs(input: {
7
8
  appId?: string;
8
- stalledMinutes?: number;
9
+ idleMinutes?: number;
9
10
  workflowType?: string;
10
11
  limit?: number;
11
12
  }): Promise<LTApiResult>;
12
13
  export declare function orphanedSignals(input: {
13
14
  appId?: string;
15
+ withinHours?: number;
14
16
  limit?: number;
15
17
  }): Promise<LTApiResult>;
@@ -6,7 +6,7 @@ exports.orphanedSignals = orphanedSignals;
6
6
  const diagnostics_1 = require("../services/diagnostics");
7
7
  async function diagnose(input) {
8
8
  try {
9
- const diagnosis = await (0, diagnostics_1.diagnoseJob)(input.workflowId, input.appId);
9
+ const diagnosis = await (0, diagnostics_1.diagnoseJob)(input.workflowId, input.appId, { maxEvents: input.maxEvents });
10
10
  return { status: 200, data: diagnosis };
11
11
  }
12
12
  catch (err) {
@@ -4,9 +4,10 @@ import type { LTApiResult } from '../types/sdk';
4
4
  * Return platform settings for the current deployment.
5
5
  *
6
6
  * Includes telemetry configuration (trace URL), escalation claim duration
7
- * options, and event transport details (socket.io, NATS, or none).
7
+ * options, event transport details (socket.io, NATS, or none), and an
8
+ * environment block (long-tail + HotMesh versions, Node env/version).
8
9
  *
9
10
  * @param req — the incoming HTTP request, used to derive NATS WS URL from headers when proxying
10
- * @returns `{ status: 200, data: { telemetry, escalation, events } }`
11
+ * @returns `{ status: 200, data: { telemetry, escalation, events, auth, ai, environment } }`
11
12
  */
12
13
  export declare function getSettings(req?: IncomingMessage): Promise<LTApiResult>;
@@ -7,6 +7,7 @@ const nats_1 = require("../lib/events/nats");
7
7
  const socketio_1 = require("../lib/events/socketio");
8
8
  const nats_ws_proxy_1 = require("../lib/events/nats-ws-proxy");
9
9
  const config_1 = require("../modules/config");
10
+ const version_1 = require("../modules/version");
10
11
  const sso_1 = require("../modules/sso");
11
12
  const defaults_1 = require("../modules/defaults");
12
13
  const llm_1 = require("../services/llm");
@@ -33,10 +34,11 @@ function resolveNatsWsUrl(adapter, req) {
33
34
  * Return platform settings for the current deployment.
34
35
  *
35
36
  * Includes telemetry configuration (trace URL), escalation claim duration
36
- * options, and event transport details (socket.io, NATS, or none).
37
+ * options, event transport details (socket.io, NATS, or none), and an
38
+ * environment block (long-tail + HotMesh versions, Node env/version).
37
39
  *
38
40
  * @param req — the incoming HTTP request, used to derive NATS WS URL from headers when proxying
39
- * @returns `{ status: 200, data: { telemetry, escalation, events } }`
41
+ * @returns `{ status: 200, data: { telemetry, escalation, events, auth, ai, environment } }`
40
42
  */
41
43
  async function getSettings(req) {
42
44
  try {
@@ -68,6 +70,13 @@ async function getSettings(req) {
68
70
  ai: {
69
71
  enabled: (0, llm_1.hasLLMApiKey)(),
70
72
  },
73
+ environment: {
74
+ longTailVersion: version_1.LONG_TAIL_VERSION,
75
+ hotmeshVersion: version_1.HOTMESH_VERSION,
76
+ nodeEnv: config_1.config.NODE_ENV,
77
+ nodeVersion: process.version,
78
+ eventTransport: transport,
79
+ },
71
80
  },
72
81
  };
73
82
  }
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Runtime version constants for the environment block surfaced by /api/settings.
3
+ *
4
+ * The HotMesh SDK version is read from its package.json — node module
5
+ * resolution finds it in both dev (source) and prod (the runtime image's
6
+ * node_modules), and resolveJsonModule handles the static import at compile time.
7
+ */
8
+ export declare const HOTMESH_VERSION: string;
9
+ /**
10
+ * The long-tail app version. Read from the root package.json at runtime via the
11
+ * process working directory (which is the app root in both dev and the Docker
12
+ * image, where CMD runs `node build/index.js` from /app). A static
13
+ * `../package.json` import would resolve to build/package.json after compile —
14
+ * a path the Dockerfile does not copy — so this reads from cwd instead.
15
+ */
16
+ export declare const LONG_TAIL_VERSION: string;
@@ -0,0 +1,34 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.LONG_TAIL_VERSION = exports.HOTMESH_VERSION = void 0;
7
+ const fs_1 = require("fs");
8
+ const path_1 = require("path");
9
+ const package_json_1 = __importDefault(require("@hotmeshio/hotmesh/package.json"));
10
+ /**
11
+ * Runtime version constants for the environment block surfaced by /api/settings.
12
+ *
13
+ * The HotMesh SDK version is read from its package.json — node module
14
+ * resolution finds it in both dev (source) and prod (the runtime image's
15
+ * node_modules), and resolveJsonModule handles the static import at compile time.
16
+ */
17
+ exports.HOTMESH_VERSION = package_json_1.default.version ?? 'unknown';
18
+ /**
19
+ * The long-tail app version. Read from the root package.json at runtime via the
20
+ * process working directory (which is the app root in both dev and the Docker
21
+ * image, where CMD runs `node build/index.js` from /app). A static
22
+ * `../package.json` import would resolve to build/package.json after compile —
23
+ * a path the Dockerfile does not copy — so this reads from cwd instead.
24
+ */
25
+ exports.LONG_TAIL_VERSION = readLongTailVersion();
26
+ function readLongTailVersion() {
27
+ try {
28
+ const raw = (0, fs_1.readFileSync)((0, path_1.resolve)(process.cwd(), 'package.json'), 'utf8');
29
+ return JSON.parse(raw).version ?? 'unknown';
30
+ }
31
+ catch {
32
+ return 'unknown';
33
+ }
34
+ }
@@ -123,6 +123,7 @@ router.get('/stream-messages', auth_1.requireBuilder, async (req, res) => {
123
123
  workflow_name: req.query.workflow_name || undefined,
124
124
  jid: req.query.jid || undefined,
125
125
  aid: req.query.aid || undefined,
126
+ dad: req.query.dad || undefined,
126
127
  });
127
128
  res.status(result.status).json(result.data ?? { error: result.error });
128
129
  });
@@ -42,29 +42,33 @@ const router = (0, express_1.Router)();
42
42
  * Full diagnosis for a single workflow job.
43
43
  *
44
44
  * Query params:
45
- * app_id — HotMesh namespace (default: durable)
45
+ * app_id — HotMesh namespace (default: durable)
46
+ * max_events — cap on events returned (most recent kept; default: 500)
46
47
  */
47
48
  router.get('/jobs/:workflowId', auth_1.requireAdmin, async (req, res) => {
48
49
  const result = await api.diagnose({
49
50
  workflowId: req.params.workflowId,
50
51
  appId: typeof req.query.app_id === 'string' ? req.query.app_id : undefined,
52
+ maxEvents: req.query.max_events ? Number(req.query.max_events) : undefined,
51
53
  });
52
54
  res.status(result.status).json(result.data ?? { error: result.error });
53
55
  });
54
56
  /**
55
57
  * GET /api/diagnostics/stalled
56
- * Running jobs with no recent progress.
58
+ * Running jobs with no recent status change. A frozen updated_at is the NORMAL
59
+ * signature of a workflow waiting at a condition()/waitFor()/sleepFor() — each
60
+ * row is classified `likely: waiting | no_recent_progress` accordingly.
57
61
  *
58
62
  * 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
+ * app_id — HotMesh namespace (default: durable)
64
+ * idle_minutes — minimum minutes since last status change (default: 5)
65
+ * workflow_type — filter by workflow function name
66
+ * limit — max results (default: 50, max: 200)
63
67
  */
64
68
  router.get('/stalled', auth_1.requireAdmin, async (req, res) => {
65
69
  const result = await api.stalledJobs({
66
70
  appId: typeof req.query.app_id === 'string' ? req.query.app_id : undefined,
67
- stalledMinutes: req.query.stalled_minutes ? Number(req.query.stalled_minutes) : undefined,
71
+ idleMinutes: req.query.idle_minutes ? Number(req.query.idle_minutes) : undefined,
68
72
  workflowType: typeof req.query.workflow_type === 'string' ? req.query.workflow_type : undefined,
69
73
  limit: req.query.limit ? Number(req.query.limit) : undefined,
70
74
  });
@@ -72,15 +76,19 @@ router.get('/stalled', auth_1.requireAdmin, async (req, res) => {
72
76
  });
73
77
  /**
74
78
  * GET /api/diagnostics/orphaned-signals
75
- * Suspended jobs with no escalation row.
79
+ * Suspended waiters with no escalation row, within a recent time window. The
80
+ * window keeps this off a full-history scan of the partitioned worker_streams
81
+ * table — widen `within_hours` deliberately to reach older orphans.
76
82
  *
77
83
  * Query params:
78
- * app_id — HotMesh namespace (default: durable)
79
- * limit max results (default: 100, max: 500)
84
+ * app_id — HotMesh namespace (default: durable)
85
+ * within_hours recent window to scan (default: 24, max: 720)
86
+ * limit — max results (default: 100, max: 500)
80
87
  */
81
88
  router.get('/orphaned-signals', auth_1.requireAdmin, async (req, res) => {
82
89
  const result = await api.orphanedSignals({
83
90
  appId: typeof req.query.app_id === 'string' ? req.query.app_id : undefined,
91
+ withinHours: req.query.within_hours ? Number(req.query.within_hours) : undefined,
84
92
  limit: req.query.limit ? Number(req.query.limit) : undefined,
85
93
  });
86
94
  res.status(result.status).json(result.data ?? { error: result.error });
@@ -129,9 +129,10 @@ async function getStreamMessages(schema, params) {
129
129
  const workflowName = params.workflow_name ?? null;
130
130
  const jid = params.jid ?? null;
131
131
  const aid = params.aid ?? null;
132
+ const dad = params.dad ?? null;
132
133
  const pool = (0, db_1.getPool)();
133
- const queryParams = [streamName, status, msgType, topic, workflowName, jid, aid, limit, offset];
134
- const countParams = [streamName, status, msgType, topic, workflowName, jid, aid];
134
+ const queryParams = [streamName, status, msgType, topic, workflowName, jid, aid, dad, limit, offset];
135
+ const countParams = [streamName, status, msgType, topic, workflowName, jid, aid, dad];
135
136
  const [messagesRes, countRes] = await Promise.all([
136
137
  pool.query((0, stream_messages_sql_1.LIST_STREAM_MESSAGES)(schema, sortColumn, sortOrder, source), queryParams).catch(() => ({ rows: [] })),
137
138
  pool.query((0, stream_messages_sql_1.COUNT_STREAM_MESSAGES)(schema, source), countParams).catch(() => ({ rows: [{ count: 0 }] })),
@@ -9,13 +9,14 @@ export declare const VALID_SORT_ORDERS: Set<string>;
9
9
  * with different schemas and must never be commingled in a single query.
10
10
  *
11
11
  * Parameters: $1 = stream_name, $2 = status, $3 = msg_type,
12
- * $4 = topic, $5 = workflow_name, $6 = jid, $7 = aid,
13
- * $8 = limit, $9 = offset
12
+ * $4 = topic, $5 = workflow_name, $6 = jid, $7 = aid, $8 = dad,
13
+ * $9 = limit, $10 = offset
14
14
  */
15
15
  export declare function LIST_STREAM_MESSAGES(schema: string, sortColumn: string, sortOrder: string, source: 'engine' | 'worker'): string;
16
16
  /**
17
17
  * Count stream messages matching the filter criteria.
18
18
  *
19
- * Parameters: $1 = stream_name, $2 = status, $3 = msg_type
19
+ * Parameters: $1 = stream_name, $2 = status, $3 = msg_type,
20
+ * $4 = topic, $5 = workflow_name, $6 = jid, $7 = aid, $8 = dad
20
21
  */
21
22
  export declare function COUNT_STREAM_MESSAGES(schema: string, source: 'engine' | 'worker'): string;
@@ -42,8 +42,10 @@ const STATUS_CASE = `
42
42
  * $5 = workflow_name filter (NULL = all, worker-only)
43
43
  * $6 = jid filter (NULL = all, worker-only)
44
44
  * $7 = aid filter (NULL = all, worker-only)
45
+ * $8 = dad filter (NULL = all, worker-only — pins one execution dimension)
45
46
  */
46
- /** Engine WHERE: supports stream_name, status, and jid (v0.18.0). */
47
+ /** Engine WHERE: supports stream_name, status, and jid (v0.18.0). engine_streams
48
+ * has no aid/dad columns, so $7/$8 are accepted but inert here. */
47
49
  const ENGINE_WHERE_CLAUSE = `
48
50
  WHERE ($1::text IS NULL OR stream_name ILIKE $1)
49
51
  AND ($2::text IS NULL OR ${STATUS_CASE} = $2)
@@ -51,7 +53,8 @@ const ENGINE_WHERE_CLAUSE = `
51
53
  AND ($4::text IS NULL)
52
54
  AND ($5::text IS NULL)
53
55
  AND ($6::text IS NULL OR jid = $6)
54
- AND ($7::text IS NULL)`;
56
+ AND ($7::text IS NULL)
57
+ AND ($8::text IS NULL)`;
55
58
  const WORKER_WHERE_CLAUSE = `
56
59
  WHERE ($1::text IS NULL OR stream_name ILIKE $1)
57
60
  AND ($2::text IS NULL OR ${STATUS_CASE} = $2)
@@ -59,7 +62,8 @@ const WORKER_WHERE_CLAUSE = `
59
62
  AND ($4::text IS NULL OR topic = $4)
60
63
  AND ($5::text IS NULL OR workflow_name = $5)
61
64
  AND ($6::text IS NULL OR jid = $6)
62
- AND ($7::text IS NULL OR aid = $7)`;
65
+ AND ($7::text IS NULL OR aid = $7)
66
+ AND ($8::text IS NULL OR dad = $8)`;
63
67
  /**
64
68
  * List stream messages with pagination, filtering, and sorting.
65
69
  *
@@ -67,8 +71,8 @@ const WORKER_WHERE_CLAUSE = `
67
71
  * with different schemas and must never be commingled in a single query.
68
72
  *
69
73
  * Parameters: $1 = stream_name, $2 = status, $3 = msg_type,
70
- * $4 = topic, $5 = workflow_name, $6 = jid, $7 = aid,
71
- * $8 = limit, $9 = offset
74
+ * $4 = topic, $5 = workflow_name, $6 = jid, $7 = aid, $8 = dad,
75
+ * $9 = limit, $10 = offset
72
76
  */
73
77
  function LIST_STREAM_MESSAGES(schema, sortColumn, sortOrder, source) {
74
78
  const select = source === 'engine'
@@ -94,12 +98,13 @@ function LIST_STREAM_MESSAGES(schema, sortColumn, sortOrder, source) {
94
98
  return `
95
99
  SELECT * FROM (${select}) AS q
96
100
  ORDER BY ${sortColumn} ${sortOrder}, id DESC
97
- LIMIT $8::int OFFSET $9::int`;
101
+ LIMIT $9::int OFFSET $10::int`;
98
102
  }
99
103
  /**
100
104
  * Count stream messages matching the filter criteria.
101
105
  *
102
- * Parameters: $1 = stream_name, $2 = status, $3 = msg_type
106
+ * Parameters: $1 = stream_name, $2 = status, $3 = msg_type,
107
+ * $4 = topic, $5 = workflow_name, $6 = jid, $7 = aid, $8 = dad
103
108
  */
104
109
  function COUNT_STREAM_MESSAGES(schema, source) {
105
110
  const table = source === 'engine' ? 'engine_streams' : 'worker_streams';
@@ -51,6 +51,8 @@ export interface StreamMessagesParams {
51
51
  workflow_name?: string | null;
52
52
  jid?: string | null;
53
53
  aid?: string | null;
54
+ /** Dimension/ancestor path (worker-only) — pins one execution among siblings sharing an aid. */
55
+ dad?: string | null;
54
56
  }
55
57
  export interface StreamMessagesResult {
56
58
  messages: StreamMessage[];
@@ -21,22 +21,32 @@ export interface JobDiagnosis {
21
21
  workflow_id: string;
22
22
  app_id: string;
23
23
  status: 'running' | 'completed' | 'failed' | 'not_found';
24
- stalled_for_ms: number | null;
24
+ /**
25
+ * Milliseconds since the last execution event. NOT a fault signal on its own —
26
+ * a workflow can sit at a condition()/waitFor()/sleepFor() for days legitimately.
27
+ * Read `findings` for the actual interpretation.
28
+ */
29
+ idle_for_ms: number | null;
25
30
  workflow_type: string | null;
26
31
  last_event_at: string | null;
27
32
  stream_summary: StreamSummary;
28
33
  escalation: EscalationSummary;
29
34
  findings: Finding[];
30
35
  execution_events: unknown[];
36
+ /** Total events before any cap was applied (execution_events may be truncated to the most recent `maxEvents`). */
37
+ total_events: number;
38
+ events_truncated: boolean;
31
39
  stream_messages: {
32
40
  worker: StreamMessage[];
33
41
  engine: StreamMessage[];
34
42
  };
35
43
  }
36
- export declare function diagnoseJob(workflowId: string, appId?: string): Promise<JobDiagnosis>;
44
+ export declare function diagnoseJob(workflowId: string, appId?: string, options?: {
45
+ maxEvents?: number;
46
+ }): Promise<JobDiagnosis>;
37
47
  export declare function findStalledJobs(params: {
38
48
  appId?: string;
39
- stalledMinutes?: number;
49
+ idleMinutes?: number;
40
50
  workflowType?: string | null;
41
51
  limit?: number;
42
52
  }): Promise<{
@@ -45,8 +55,10 @@ export declare function findStalledJobs(params: {
45
55
  }>;
46
56
  export declare function findOrphanedSignals(params: {
47
57
  appId?: string;
58
+ withinHours?: number;
48
59
  limit?: number;
49
60
  }): Promise<{
50
61
  orphans: unknown[];
51
62
  total: number;
63
+ within_hours: number;
52
64
  }>;
@@ -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,14 @@ 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;
67
91
  // ── diagnoseJob ──────────────────────────────────────────────────────────────
68
- async function diagnoseJob(workflowId, appId = 'durable') {
92
+ async function diagnoseJob(workflowId, appId = 'durable', options = {}) {
69
93
  const pool = (0, db_1.getPool)();
94
+ const maxEvents = Math.max(options.maxEvents ?? DEFAULT_MAX_EVENTS, 1);
70
95
  let resolved;
71
96
  try {
72
97
  resolved = await (0, resolve_1.resolveWorkflowHandle)(workflowId);
@@ -91,9 +116,14 @@ async function diagnoseJob(workflowId, appId = 'durable') {
91
116
  const isFailed = events.some(e => e.event_type === 'workflow_execution_failed');
92
117
  const status = isCompleted ? 'completed' : isFailed ? 'failed' : 'running';
93
118
  const lastEventAt = lastEvent?.event_time ?? null;
94
- const stalledMs = status === 'running' && lastEventAt
119
+ const idleMs = status === 'running' && lastEventAt
95
120
  ? now - new Date(lastEventAt).getTime()
96
121
  : null;
122
+ // Cap the events returned in the payload (keep the most recent) so a
123
+ // high-activity workflow can't produce a huge response. Pattern matching
124
+ // still runs over the full in-memory event set.
125
+ const eventsTruncated = events.length > maxEvents;
126
+ const cappedEvents = eventsTruncated ? events.slice(-maxEvents) : events;
97
127
  const allMessages = [...workerMessages, ...engineMessages];
98
128
  const streamSummary = {
99
129
  worker_total: workerMessages.length,
@@ -104,12 +134,12 @@ async function diagnoseJob(workflowId, appId = 'durable') {
104
134
  };
105
135
  const findings = execution
106
136
  ? (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: [] }];
137
+ : [{ condition: 'export_unavailable', confidence: 0.9, severity: 'warning', evidence: ['Execution export unavailable — job may have expired or HotMesh engine is not running'], guidance: [] }];
108
138
  return {
109
139
  workflow_id: workflowId,
110
140
  app_id: appId,
111
141
  status,
112
- stalled_for_ms: stalledMs,
142
+ idle_for_ms: idleMs,
113
143
  workflow_type: resolved.workflowName,
114
144
  last_event_at: lastEventAt,
115
145
  stream_summary: streamSummary,
@@ -123,33 +153,40 @@ async function diagnoseJob(workflowId, appId = 'durable') {
123
153
  created_at: escalationRow?.created_at ?? null,
124
154
  },
125
155
  findings,
126
- execution_events: events,
156
+ execution_events: cappedEvents,
157
+ total_events: events.length,
158
+ events_truncated: eventsTruncated,
127
159
  stream_messages: { worker: workerMessages, engine: engineMessages },
128
160
  };
129
161
  }
130
162
  // ── findStalledJobs ──────────────────────────────────────────────────────────
131
163
  async function findStalledJobs(params) {
132
164
  const appId = params.appId ?? 'durable';
133
- const stalledMinutes = params.stalledMinutes ?? 5;
165
+ const idleMinutes = params.idleMinutes ?? 5;
134
166
  const limit = Math.min(params.limit ?? 50, 200);
135
167
  const keyPrefix = `hmsh:${appId}:j:`;
168
+ // Fail loud: let a query error surface (the api layer maps it to a 500). A
169
+ // silent empty result would hide a missing schema or a broken query.
136
170
  const pool = (0, db_1.getPool)();
137
171
  const { rows } = await pool.query(STALLED_JOBS(appId), [
138
172
  keyPrefix,
139
- String(stalledMinutes),
173
+ String(idleMinutes),
140
174
  params.workflowType ?? null,
141
175
  limit,
142
- ]).catch(() => ({ rows: [] }));
176
+ ]);
143
177
  return { jobs: rows, total: rows.length };
144
178
  }
145
179
  // ── findOrphanedSignals ──────────────────────────────────────────────────────
146
180
  async function findOrphanedSignals(params) {
147
181
  const appId = params.appId ?? 'durable';
182
+ const withinHours = Math.min(Math.max(params.withinHours ?? 24, 1), 720);
148
183
  const limit = Math.min(params.limit ?? 100, 500);
184
+ // Fail loud: let a query error surface (the api layer maps it to a 500). A
185
+ // silent empty result would hide a missing schema or a broken query — exactly
186
+ // what previously masked a JSON-operator bug on the text `message` column.
149
187
  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 };
188
+ const { rows } = await pool.query(ORPHANED_SIGNALS(appId), [String(withinHours), limit]);
189
+ return { orphans: rows, total: rows.length, within_hours: withinHours };
153
190
  }
154
191
  // ── helpers ──────────────────────────────────────────────────────────────────
155
192
  function notFound(workflowId, appId) {
@@ -157,13 +194,15 @@ function notFound(workflowId, appId) {
157
194
  workflow_id: workflowId,
158
195
  app_id: appId,
159
196
  status: 'not_found',
160
- stalled_for_ms: null,
197
+ idle_for_ms: null,
161
198
  workflow_type: null,
162
199
  last_event_at: null,
163
200
  stream_summary: { worker_total: 0, engine_total: 0, dead_lettered: 0, pending: 0, in_flight: 0 },
164
201
  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: [] }],
202
+ findings: [{ condition: 'not_found', confidence: 1, severity: 'critical', evidence: ['Workflow ID not found — no task record or job entity'], guidance: [] }],
166
203
  execution_events: [],
204
+ total_events: 0,
205
+ events_truncated: false,
167
206
  stream_messages: { worker: [], engine: [] },
168
207
  };
169
208
  }
@@ -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[];