@hotmeshio/long-tail 0.9.6 → 0.10.0

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 (255) hide show
  1. package/README.md +22 -93
  2. package/build/api/escalations/helpers.d.ts +27 -0
  3. package/build/api/escalations/helpers.js +34 -0
  4. package/build/api/escalations/list.d.ts +2 -0
  5. package/build/api/escalations/list.js +13 -3
  6. package/build/api/escalations/resolve.js +16 -4
  7. package/build/routes/escalations/list.js +2 -0
  8. package/build/services/escalation/crud.d.ts +1 -1
  9. package/build/services/escalation/crud.js +6 -2
  10. package/build/services/escalation/facet-sql.d.ts +8 -1
  11. package/build/services/escalation/facet-sql.js +29 -1
  12. package/build/types/facets.d.ts +9 -0
  13. package/dashboard/dist/assets/{AdminDashboard-DXFIKXO3.js → AdminDashboard-DQS74RL0.js} +2 -2
  14. package/dashboard/dist/assets/{AdminDashboard-DXFIKXO3.js.map → AdminDashboard-DQS74RL0.js.map} +1 -1
  15. package/dashboard/dist/assets/{AgentConfigPage-CQE1mfjk.js → AgentConfigPage-CSMCrFR4.js} +7 -7
  16. package/dashboard/dist/assets/{AgentConfigPage-CQE1mfjk.js.map → AgentConfigPage-CSMCrFR4.js.map} +1 -1
  17. package/dashboard/dist/assets/{AgentDetailPage-DM59ozP_.js → AgentDetailPage-CDiopQ7D.js} +3 -3
  18. package/dashboard/dist/assets/{AgentDetailPage-DM59ozP_.js.map → AgentDetailPage-CDiopQ7D.js.map} +1 -1
  19. package/dashboard/dist/assets/{AgentsPage-DSK2uXI5.js → AgentsPage-CyUXe9n4.js} +2 -2
  20. package/dashboard/dist/assets/{AgentsPage-DSK2uXI5.js.map → AgentsPage-CyUXe9n4.js.map} +1 -1
  21. package/dashboard/dist/assets/AvailableEscalationsPage-AiuiLcz-.js +2 -0
  22. package/dashboard/dist/assets/AvailableEscalationsPage-AiuiLcz-.js.map +1 -0
  23. package/dashboard/dist/assets/BotPicker-BuCGvEed.js +2 -0
  24. package/dashboard/dist/assets/BotPicker-BuCGvEed.js.map +1 -0
  25. package/dashboard/dist/assets/CapabilitiesPage-nTv4STcG.js +2 -0
  26. package/dashboard/dist/assets/{CapabilitiesPage-DFQryGBj.js.map → CapabilitiesPage-nTv4STcG.js.map} +1 -1
  27. package/dashboard/dist/assets/{CollapsibleSection-B7-d5O_x.js → CollapsibleSection-CkOks9Cf.js} +2 -2
  28. package/dashboard/dist/assets/{CollapsibleSection-B7-d5O_x.js.map → CollapsibleSection-CkOks9Cf.js.map} +1 -1
  29. package/dashboard/dist/assets/{ConfirmDeleteModal-D9_1b4MW.js → ConfirmDeleteModal-C0x0HcHX.js} +2 -2
  30. package/dashboard/dist/assets/{ConfirmDeleteModal-D9_1b4MW.js.map → ConfirmDeleteModal-C0x0HcHX.js.map} +1 -1
  31. package/dashboard/dist/assets/CountdownTimer-DdKL8Ryt.js +2 -0
  32. package/dashboard/dist/assets/CountdownTimer-DdKL8Ryt.js.map +1 -0
  33. package/dashboard/dist/assets/CredentialsPage-BwA7k9Cb.js +2 -0
  34. package/dashboard/dist/assets/{CredentialsPage-BWVgxRpZ.js.map → CredentialsPage-BwA7k9Cb.js.map} +1 -1
  35. package/dashboard/dist/assets/{CronLabel-DNnyNqXH.js → CronLabel-jC_i-Lh9.js} +2 -2
  36. package/dashboard/dist/assets/{CronLabel-DNnyNqXH.js.map → CronLabel-jC_i-Lh9.js.map} +1 -1
  37. package/dashboard/dist/assets/{CustomDurationPicker-SNsQZL_U.js → CustomDurationPicker-F6bR2Q5A.js} +2 -2
  38. package/dashboard/dist/assets/{CustomDurationPicker-SNsQZL_U.js.map → CustomDurationPicker-F6bR2Q5A.js.map} +1 -1
  39. package/dashboard/dist/assets/{DropZone-C1TpkVQG.js → DropZone-zxH-9B8c.js} +2 -2
  40. package/dashboard/dist/assets/{DropZone-C1TpkVQG.js.map → DropZone-zxH-9B8c.js.map} +1 -1
  41. package/dashboard/dist/assets/{ElapsedCell-C4r0IvUt.js → ElapsedCell-DZ3RTZFc.js} +2 -2
  42. package/dashboard/dist/assets/{ElapsedCell-C4r0IvUt.js.map → ElapsedCell-DZ3RTZFc.js.map} +1 -1
  43. package/dashboard/dist/assets/{EscalationListSchemaPage-D8Ec8Zhj.js → EscalationListSchemaPage-CaMydHIO.js} +3 -3
  44. package/dashboard/dist/assets/{EscalationListSchemaPage-D8Ec8Zhj.js.map → EscalationListSchemaPage-CaMydHIO.js.map} +1 -1
  45. package/dashboard/dist/assets/{EscalationSchemaPage-BmEv7qU2.js → EscalationSchemaPage-BlMx2WE6.js} +3 -3
  46. package/dashboard/dist/assets/{EscalationSchemaPage-BmEv7qU2.js.map → EscalationSchemaPage-BlMx2WE6.js.map} +1 -1
  47. package/dashboard/dist/assets/{EscalationsOverview-DkIy025e.js → EscalationsOverview-DcMkbiSo.js} +2 -2
  48. package/dashboard/dist/assets/{EscalationsOverview-DkIy025e.js.map → EscalationsOverview-DcMkbiSo.js.map} +1 -1
  49. package/dashboard/dist/assets/{EventTable-Co4RYeKO.js → EventTable-CuZ2Q_wH.js} +2 -2
  50. package/dashboard/dist/assets/{EventTable-Co4RYeKO.js.map → EventTable-CuZ2Q_wH.js.map} +1 -1
  51. package/dashboard/dist/assets/FilterBar-B5Vk5tBF.js +2 -0
  52. package/dashboard/dist/assets/FilterBar-B5Vk5tBF.js.map +1 -0
  53. package/dashboard/dist/assets/{GraphInvokePage-b7ukMy0z.js → GraphInvokePage-CXElQqcV.js} +2 -2
  54. package/dashboard/dist/assets/{GraphInvokePage-b7ukMy0z.js.map → GraphInvokePage-CXElQqcV.js.map} +1 -1
  55. package/dashboard/dist/assets/HomePage-DcWfjZA-.js +2 -0
  56. package/dashboard/dist/assets/HomePage-DcWfjZA-.js.map +1 -0
  57. package/dashboard/dist/assets/ListToolbar-Dmd1UiM-.js +2 -0
  58. package/dashboard/dist/assets/{ListToolbar-DDG6DXVF.js.map → ListToolbar-Dmd1UiM-.js.map} +1 -1
  59. package/dashboard/dist/assets/{McpOverview-P2o3b3mi.js → McpOverview-Cul2LFEw.js} +2 -2
  60. package/dashboard/dist/assets/{McpOverview-P2o3b3mi.js.map → McpOverview-Cul2LFEw.js.map} +1 -1
  61. package/dashboard/dist/assets/{McpQueryDetailPage-C2CeBLb0.js → McpQueryDetailPage-BVlQ0WBa.js} +2 -2
  62. package/dashboard/dist/assets/{McpQueryDetailPage-C2CeBLb0.js.map → McpQueryDetailPage-BVlQ0WBa.js.map} +1 -1
  63. package/dashboard/dist/assets/{McpQueryPage-D_N-3lp0.js → McpQueryPage-B7PmO_V-.js} +2 -2
  64. package/dashboard/dist/assets/{McpQueryPage-D_N-3lp0.js.map → McpQueryPage-B7PmO_V-.js.map} +1 -1
  65. package/dashboard/dist/assets/{McpRunDetailPage-uDw3MWR_.js → McpRunDetailPage-B1RhxxlU.js} +2 -2
  66. package/dashboard/dist/assets/{McpRunDetailPage-uDw3MWR_.js.map → McpRunDetailPage-B1RhxxlU.js.map} +1 -1
  67. package/dashboard/dist/assets/{McpRunsPage-CDTUxghb.js → McpRunsPage-DlSrpxiv.js} +2 -2
  68. package/dashboard/dist/assets/{McpRunsPage-CDTUxghb.js.map → McpRunsPage-DlSrpxiv.js.map} +1 -1
  69. package/dashboard/dist/assets/Modal-D4afnAjc.js +2 -0
  70. package/dashboard/dist/assets/Modal-D4afnAjc.js.map +1 -0
  71. package/dashboard/dist/assets/{NamespacePill--ikKtMvx.js → NamespacePill-BQNMfY0m.js} +2 -2
  72. package/dashboard/dist/assets/{NamespacePill--ikKtMvx.js.map → NamespacePill-BQNMfY0m.js.map} +1 -1
  73. package/dashboard/dist/assets/OperationsPage-vm_ByX41.js +2 -0
  74. package/dashboard/dist/assets/OperationsPage-vm_ByX41.js.map +1 -0
  75. package/dashboard/dist/assets/OperatorDashboard-zviIxH-0.js +2 -0
  76. package/dashboard/dist/assets/{OperatorDashboard-CVRD8nQH.js.map → OperatorDashboard-zviIxH-0.js.map} +1 -1
  77. package/dashboard/dist/assets/{PageHeader-CiFhzGc0.js → PageHeader-BIY1YvKR.js} +2 -2
  78. package/dashboard/dist/assets/{PageHeader-CiFhzGc0.js.map → PageHeader-BIY1YvKR.js.map} +1 -1
  79. package/dashboard/dist/assets/{PageHeaderWithStats-_zOnWAhq.js → PageHeaderWithStats-TUyglVW0.js} +2 -2
  80. package/dashboard/dist/assets/{PageHeaderWithStats-_zOnWAhq.js.map → PageHeaderWithStats-TUyglVW0.js.map} +1 -1
  81. package/dashboard/dist/assets/{ProcessDetailPage-Dt6PAK04.js → ProcessDetailPage-txiMHbMI.js} +2 -2
  82. package/dashboard/dist/assets/{ProcessDetailPage-Dt6PAK04.js.map → ProcessDetailPage-txiMHbMI.js.map} +1 -1
  83. package/dashboard/dist/assets/{ProcessesListPage-CMkjASgs.js → ProcessesListPage-CRttlU3L.js} +2 -2
  84. package/dashboard/dist/assets/{ProcessesListPage-CMkjASgs.js.map → ProcessesListPage-CRttlU3L.js.map} +1 -1
  85. package/dashboard/dist/assets/RoleDetailPage-DBIl_mhK.js +8 -0
  86. package/dashboard/dist/assets/RoleDetailPage-DBIl_mhK.js.map +1 -0
  87. package/dashboard/dist/assets/RolePill-4LO_CuO3.js +2 -0
  88. package/dashboard/dist/assets/RolePill-4LO_CuO3.js.map +1 -0
  89. package/dashboard/dist/assets/RolesPage-DzrSv6m-.js +2 -0
  90. package/dashboard/dist/assets/{RolesPage-DtHIjDss.js.map → RolesPage-DzrSv6m-.js.map} +1 -1
  91. package/dashboard/dist/assets/RunAsSelector-CiLjlmIE.js +2 -0
  92. package/dashboard/dist/assets/{RunAsSelector-C198CpFs.js.map → RunAsSelector-CiLjlmIE.js.map} +1 -1
  93. package/dashboard/dist/assets/{SlidePanel-BcWKelpn.js → SlidePanel-DK4sWXmp.js} +2 -2
  94. package/dashboard/dist/assets/{SlidePanel-BcWKelpn.js.map → SlidePanel-DK4sWXmp.js.map} +1 -1
  95. package/dashboard/dist/assets/StickyPagination-Di6b2GDE.js +2 -0
  96. package/dashboard/dist/assets/{StickyPagination-eZ6WhMrL.js.map → StickyPagination-Di6b2GDE.js.map} +1 -1
  97. package/dashboard/dist/assets/{StreamMessageDetail-7xq_dddO.js → StreamMessageDetail-BbkU45X_.js} +2 -2
  98. package/dashboard/dist/assets/{StreamMessageDetail-7xq_dddO.js.map → StreamMessageDetail-BbkU45X_.js.map} +1 -1
  99. package/dashboard/dist/assets/{SwimlaneTimeline-Yg5QT0nI.js → SwimlaneTimeline-BzsvdstG.js} +2 -2
  100. package/dashboard/dist/assets/{SwimlaneTimeline-Yg5QT0nI.js.map → SwimlaneTimeline-BzsvdstG.js.map} +1 -1
  101. package/dashboard/dist/assets/{TagInput-BaFJCq9A.js → TagInput-CK-JhBG2.js} +2 -2
  102. package/dashboard/dist/assets/{TagInput-BaFJCq9A.js.map → TagInput-CK-JhBG2.js.map} +1 -1
  103. package/dashboard/dist/assets/{TaskDetailPage-Wc3AFnqs.js → TaskDetailPage-Biikk9o_.js} +2 -2
  104. package/dashboard/dist/assets/{TaskDetailPage-Wc3AFnqs.js.map → TaskDetailPage-Biikk9o_.js.map} +1 -1
  105. package/dashboard/dist/assets/{TaskQueuePill-D9e16qkU.js → TaskQueuePill-C_Vo2Clc.js} +2 -2
  106. package/dashboard/dist/assets/{TaskQueuePill-D9e16qkU.js.map → TaskQueuePill-C_Vo2Clc.js.map} +1 -1
  107. package/dashboard/dist/assets/{TasksListPage-V-1J_B_L.js → TasksListPage-t9WAft9-.js} +2 -2
  108. package/dashboard/dist/assets/{TasksListPage-V-1J_B_L.js.map → TasksListPage-t9WAft9-.js.map} +1 -1
  109. package/dashboard/dist/assets/{TimeAgo-XdHghs5F.js → TimeAgo-I7X6jdne.js} +2 -2
  110. package/dashboard/dist/assets/{TimeAgo-XdHghs5F.js.map → TimeAgo-I7X6jdne.js.map} +1 -1
  111. package/dashboard/dist/assets/{TimestampCell-BrzctPWT.js → TimestampCell-Cex9nr0t.js} +2 -2
  112. package/dashboard/dist/assets/{TimestampCell-BrzctPWT.js.map → TimestampCell-Cex9nr0t.js.map} +1 -1
  113. package/dashboard/dist/assets/ToolPill-BCi101Jh.js +2 -0
  114. package/dashboard/dist/assets/{ToolPill-Enc3nyic.js.map → ToolPill-BCi101Jh.js.map} +1 -1
  115. package/dashboard/dist/assets/{ToolTestPanel-CGfGUsaq.js → ToolTestPanel--MGaDY-1.js} +2 -2
  116. package/dashboard/dist/assets/{ToolTestPanel-CGfGUsaq.js.map → ToolTestPanel--MGaDY-1.js.map} +1 -1
  117. package/dashboard/dist/assets/{TopicDetailPage-CcAgD2k5.js → TopicDetailPage-B8pHHweA.js} +3 -3
  118. package/dashboard/dist/assets/{TopicDetailPage-CcAgD2k5.js.map → TopicDetailPage-B8pHHweA.js.map} +1 -1
  119. package/dashboard/dist/assets/{TopicsPage-BOQG7s4P.js → TopicsPage-tiOEQz2m.js} +2 -2
  120. package/dashboard/dist/assets/{TopicsPage-BOQG7s4P.js.map → TopicsPage-tiOEQz2m.js.map} +1 -1
  121. package/dashboard/dist/assets/{UserName-BzxR0Ihp.js → UserName-Cdhyuv5K.js} +2 -2
  122. package/dashboard/dist/assets/{UserName-BzxR0Ihp.js.map → UserName-Cdhyuv5K.js.map} +1 -1
  123. package/dashboard/dist/assets/{WorkflowExecutionPage-hRsDMByj.js → WorkflowExecutionPage-DD1BKj5k.js} +2 -2
  124. package/dashboard/dist/assets/{WorkflowExecutionPage-hRsDMByj.js.map → WorkflowExecutionPage-DD1BKj5k.js.map} +1 -1
  125. package/dashboard/dist/assets/WorkflowPill-DHxrAkp3.js +2 -0
  126. package/dashboard/dist/assets/{WorkflowPill-S6VV2Eax.js.map → WorkflowPill-DHxrAkp3.js.map} +1 -1
  127. package/dashboard/dist/assets/{WorkflowsDashboard-ky0ulsUe.js → WorkflowsDashboard-BeWjDHYW.js} +2 -2
  128. package/dashboard/dist/assets/{WorkflowsDashboard-ky0ulsUe.js.map → WorkflowsDashboard-BeWjDHYW.js.map} +1 -1
  129. package/dashboard/dist/assets/{WorkflowsOverview-CzRJUV58.js → WorkflowsOverview-CkI-Pmj7.js} +2 -2
  130. package/dashboard/dist/assets/{WorkflowsOverview-CzRJUV58.js.map → WorkflowsOverview-CkI-Pmj7.js.map} +1 -1
  131. package/dashboard/dist/assets/{YamlWorkflowDetailPage-Beqzy9Ph.js → YamlWorkflowDetailPage-BQnO8-PQ.js} +2 -2
  132. package/dashboard/dist/assets/{YamlWorkflowDetailPage-Beqzy9Ph.js.map → YamlWorkflowDetailPage-BQnO8-PQ.js.map} +1 -1
  133. package/dashboard/dist/assets/{YamlWorkflowsPage-BzBUU2ug.js → YamlWorkflowsPage-DjmNTQ5r.js} +2 -2
  134. package/dashboard/dist/assets/{YamlWorkflowsPage-BzBUU2ug.js.map → YamlWorkflowsPage-DjmNTQ5r.js.map} +1 -1
  135. package/dashboard/dist/assets/{agents-Dlu4m6jy.js → agents-2DaoprdB.js} +2 -2
  136. package/dashboard/dist/assets/{agents-Dlu4m6jy.js.map → agents-2DaoprdB.js.map} +1 -1
  137. package/dashboard/dist/assets/{bots-C4G5U-6Q.js → bots-BdwLwMUg.js} +2 -2
  138. package/dashboard/dist/assets/{bots-C4G5U-6Q.js.map → bots-BdwLwMUg.js.map} +1 -1
  139. package/dashboard/dist/assets/{capabilities-BSFP_boq.js → capabilities-BsuFeK9J.js} +2 -2
  140. package/dashboard/dist/assets/{capabilities-BSFP_boq.js.map → capabilities-BsuFeK9J.js.map} +1 -1
  141. package/dashboard/dist/assets/{controlplane-qCqoP2ZV.js → controlplane-CxKOrcFO.js} +2 -2
  142. package/dashboard/dist/assets/{controlplane-qCqoP2ZV.js.map → controlplane-CxKOrcFO.js.map} +1 -1
  143. package/dashboard/dist/assets/escalation-columns-y5SQ2Ulh.js +2 -0
  144. package/dashboard/dist/assets/escalation-columns-y5SQ2Ulh.js.map +1 -0
  145. package/dashboard/dist/assets/index-BC1NxetR.js +2 -0
  146. package/dashboard/dist/assets/{index-C0KklOM4.js.map → index-BC1NxetR.js.map} +1 -1
  147. package/dashboard/dist/assets/index-BVZINC_p.js +2 -0
  148. package/dashboard/dist/assets/{index-QL26JxBF.js.map → index-BVZINC_p.js.map} +1 -1
  149. package/dashboard/dist/assets/{index-CGVqW6MI.js → index-BXbt5NHG.js} +2 -2
  150. package/dashboard/dist/assets/{index-CGVqW6MI.js.map → index-BXbt5NHG.js.map} +1 -1
  151. package/dashboard/dist/assets/{index-CuC_mNNA.js → index-BYEkfaUq.js} +2 -2
  152. package/dashboard/dist/assets/{index-CuC_mNNA.js.map → index-BYEkfaUq.js.map} +1 -1
  153. package/dashboard/dist/assets/index-B_68lp-B.js +5 -0
  154. package/dashboard/dist/assets/index-B_68lp-B.js.map +1 -0
  155. package/dashboard/dist/assets/index-BeivyVtT.css +1 -0
  156. package/dashboard/dist/assets/{index-DUhettdV.js → index-CRBXgMsh.js} +2 -2
  157. package/dashboard/dist/assets/{index-DUhettdV.js.map → index-CRBXgMsh.js.map} +1 -1
  158. package/dashboard/dist/assets/{index-BObWYPxd.js → index-Cfq3UKd8.js} +2 -2
  159. package/dashboard/dist/assets/{index-BObWYPxd.js.map → index-Cfq3UKd8.js.map} +1 -1
  160. package/dashboard/dist/assets/{index-D_FNaSoe.js → index-DJ5nhcGB.js} +2 -2
  161. package/dashboard/dist/assets/{index-D_FNaSoe.js.map → index-DJ5nhcGB.js.map} +1 -1
  162. package/dashboard/dist/assets/{index-DCTWUswT.js → index-DVLBkcoD.js} +4 -4
  163. package/dashboard/dist/assets/index-DVLBkcoD.js.map +1 -0
  164. package/dashboard/dist/assets/index-DjI2AUbn.js +5 -0
  165. package/dashboard/dist/assets/{index-DWSNtg28.js.map → index-DjI2AUbn.js.map} +1 -1
  166. package/dashboard/dist/assets/{index-Dzhn5MDY.js → index-DyS2DHqy.js} +2 -2
  167. package/dashboard/dist/assets/{index-Dzhn5MDY.js.map → index-DyS2DHqy.js.map} +1 -1
  168. package/dashboard/dist/assets/index-H6Vp-gPG.js +2 -0
  169. package/dashboard/dist/assets/{index-BtEWQNcP.js.map → index-H6Vp-gPG.js.map} +1 -1
  170. package/dashboard/dist/assets/index-YPLTjeli.js +63 -0
  171. package/dashboard/dist/assets/index-YPLTjeli.js.map +1 -0
  172. package/dashboard/dist/assets/{knowledge-CTdgLX5j.js → knowledge-CLnYcq0Y.js} +2 -2
  173. package/dashboard/dist/assets/{knowledge-CTdgLX5j.js.map → knowledge-CLnYcq0Y.js.map} +1 -1
  174. package/dashboard/dist/assets/{mcp-BEsMr678.js → mcp-C1-iXkg7.js} +2 -2
  175. package/dashboard/dist/assets/{mcp-BEsMr678.js.map → mcp-C1-iXkg7.js.map} +1 -1
  176. package/dashboard/dist/assets/{mcp-query-CmamHKIR.js → mcp-query-DcyDxuGY.js} +2 -2
  177. package/dashboard/dist/assets/{mcp-query-CmamHKIR.js.map → mcp-query-DcyDxuGY.js.map} +1 -1
  178. package/dashboard/dist/assets/{pipelines-taOXA2-y.js → pipelines-BvG5D6bV.js} +2 -2
  179. package/dashboard/dist/assets/{pipelines-taOXA2-y.js.map → pipelines-BvG5D6bV.js.map} +1 -1
  180. package/dashboard/dist/assets/{tasks-DEaDdyBh.js → tasks-qwh8q5q7.js} +2 -2
  181. package/dashboard/dist/assets/{tasks-DEaDdyBh.js.map → tasks-qwh8q5q7.js.map} +1 -1
  182. package/dashboard/dist/assets/{topics-2qRx68Ft.js → topics-MLKKA-Ql.js} +2 -2
  183. package/dashboard/dist/assets/{topics-2qRx68Ft.js.map → topics-MLKKA-Ql.js.map} +1 -1
  184. package/dashboard/dist/assets/{useEventHooks-oXjfKrbI.js → useEventHooks-B8uNFB-l.js} +2 -2
  185. package/dashboard/dist/assets/{useEventHooks-oXjfKrbI.js.map → useEventHooks-B8uNFB-l.js.map} +1 -1
  186. package/dashboard/dist/assets/{useNamespace-YdT1Zouk.js → useNamespace-DjZmztJM.js} +2 -2
  187. package/dashboard/dist/assets/{useNamespace-YdT1Zouk.js.map → useNamespace-DjZmztJM.js.map} +1 -1
  188. package/dashboard/dist/assets/{useYamlActivityEvents-Bt-RZ7Zl.js → useYamlActivityEvents-DIvK20uI.js} +2 -2
  189. package/dashboard/dist/assets/{useYamlActivityEvents-Bt-RZ7Zl.js.map → useYamlActivityEvents-DIvK20uI.js.map} +1 -1
  190. package/dashboard/dist/assets/{users-DbX5JZ_w.js → users-CVjajeDg.js} +2 -2
  191. package/dashboard/dist/assets/{users-DbX5JZ_w.js.map → users-CVjajeDg.js.map} +1 -1
  192. package/dashboard/dist/assets/{vendor-icons-DPmIjhZB.js → vendor-icons-Qk9qLjF_.js} +131 -126
  193. package/dashboard/dist/assets/vendor-icons-Qk9qLjF_.js.map +1 -0
  194. package/dashboard/dist/assets/{workflows-acMcPTC5.js → workflows-BziTcJNq.js} +2 -2
  195. package/dashboard/dist/assets/{workflows-acMcPTC5.js.map → workflows-BziTcJNq.js.map} +1 -1
  196. package/dashboard/dist/assets/{yaml-workflows-D6kzrMYx.js → yaml-workflows-Dr-ZcE_0.js} +2 -2
  197. package/dashboard/dist/assets/{yaml-workflows-D6kzrMYx.js.map → yaml-workflows-Dr-ZcE_0.js.map} +1 -1
  198. package/dashboard/dist/index.html +3 -3
  199. package/docs/dashboard.md +2 -2
  200. package/docs/faceted-routing.md +9 -0
  201. package/docs/hitl/escalation.md +206 -0
  202. package/docs/hitl/form.md +162 -0
  203. package/docs/hitl/iframe.md +170 -0
  204. package/docs/hitl/resolution.md +166 -0
  205. package/docs/hitl/roles.md +72 -0
  206. package/docs/hitl/x-lt-layout.md +167 -0
  207. package/docs/hitl/x-lt-list-schema.md +75 -0
  208. package/docs/hitl/x-lt-show-if.md +128 -0
  209. package/docs/hitl/x-lt-validation.md +168 -0
  210. package/docs/hitl/x-lt-widget.md +152 -0
  211. package/docs/hitl-guide.md +83 -1315
  212. package/docs/iam.md +1 -1
  213. package/package.json +2 -2
  214. package/dashboard/dist/assets/AvailableEscalationsPage-BVgMtftp.js +0 -2
  215. package/dashboard/dist/assets/AvailableEscalationsPage-BVgMtftp.js.map +0 -1
  216. package/dashboard/dist/assets/BotPicker-DJ0YYKZA.js +0 -2
  217. package/dashboard/dist/assets/BotPicker-DJ0YYKZA.js.map +0 -1
  218. package/dashboard/dist/assets/CapabilitiesPage-DFQryGBj.js +0 -2
  219. package/dashboard/dist/assets/CountdownTimer-BTJmh4Xw.js +0 -2
  220. package/dashboard/dist/assets/CountdownTimer-BTJmh4Xw.js.map +0 -1
  221. package/dashboard/dist/assets/CredentialsPage-BWVgxRpZ.js +0 -2
  222. package/dashboard/dist/assets/FilterBar-DDIn8TOs.js +0 -2
  223. package/dashboard/dist/assets/FilterBar-DDIn8TOs.js.map +0 -1
  224. package/dashboard/dist/assets/HomePage-K0U3yMdU.js +0 -2
  225. package/dashboard/dist/assets/HomePage-K0U3yMdU.js.map +0 -1
  226. package/dashboard/dist/assets/ListToolbar-DDG6DXVF.js +0 -2
  227. package/dashboard/dist/assets/Modal-CSrxpXeM.js +0 -2
  228. package/dashboard/dist/assets/Modal-CSrxpXeM.js.map +0 -1
  229. package/dashboard/dist/assets/OperationsPage-Daah4pNk.js +0 -2
  230. package/dashboard/dist/assets/OperationsPage-Daah4pNk.js.map +0 -1
  231. package/dashboard/dist/assets/OperatorDashboard-CVRD8nQH.js +0 -2
  232. package/dashboard/dist/assets/RoleDetailPage-6H1AY6sK.js +0 -8
  233. package/dashboard/dist/assets/RoleDetailPage-6H1AY6sK.js.map +0 -1
  234. package/dashboard/dist/assets/RolePill-CUIJSgRq.js +0 -2
  235. package/dashboard/dist/assets/RolePill-CUIJSgRq.js.map +0 -1
  236. package/dashboard/dist/assets/RolesPage-DtHIjDss.js +0 -2
  237. package/dashboard/dist/assets/RunAsSelector-C198CpFs.js +0 -2
  238. package/dashboard/dist/assets/StickyPagination-eZ6WhMrL.js +0 -2
  239. package/dashboard/dist/assets/ToolPill-Enc3nyic.js +0 -2
  240. package/dashboard/dist/assets/WorkflowPill-S6VV2Eax.js +0 -2
  241. package/dashboard/dist/assets/escalation-columns-Ck7ZOd7Z.js +0 -2
  242. package/dashboard/dist/assets/escalation-columns-Ck7ZOd7Z.js.map +0 -1
  243. package/dashboard/dist/assets/index-BtEWQNcP.js +0 -2
  244. package/dashboard/dist/assets/index-C0KklOM4.js +0 -2
  245. package/dashboard/dist/assets/index-CY3hpJR-.js +0 -5
  246. package/dashboard/dist/assets/index-CY3hpJR-.js.map +0 -1
  247. package/dashboard/dist/assets/index-DCTWUswT.js.map +0 -1
  248. package/dashboard/dist/assets/index-DWSNtg28.js +0 -5
  249. package/dashboard/dist/assets/index-DcQiD6OF.js +0 -63
  250. package/dashboard/dist/assets/index-DcQiD6OF.js.map +0 -1
  251. package/dashboard/dist/assets/index-QL26JxBF.js +0 -2
  252. package/dashboard/dist/assets/index-waKF7bKZ.css +0 -1
  253. package/dashboard/dist/assets/roles-BiUJAsVM.js +0 -2
  254. package/dashboard/dist/assets/roles-BiUJAsVM.js.map +0 -1
  255. package/dashboard/dist/assets/vendor-icons-DPmIjhZB.js.map +0 -1
package/README.md CHANGED
@@ -1,6 +1,12 @@
1
1
  # Long Tail
2
2
 
3
- Durable workflow for humans in the loop.
3
+ **The queue durable systems forgot.**
4
+
5
+ Durable platforms are built around determinism — that is their strength and their blind spot. They give you rich, first-class queues for deterministic work (retry, timeout, `backoffCoefficient`, exactly-once) and an `await condition()` to park a workflow until a signal arrives. All of it assumes the thing you are waiting on will, eventually, behave.
6
+
7
+ Most of the work that decides an outcome behaves on its own schedule: an approval, a person, a shipment, a machine that needs service. You cannot set a `backoffCoefficient` on a person — retrying someone who has not answered just asks twice. This is why "human-in-the-loop" keeps getting bolted on as a feature: the platform perfected the deterministic queue and left the non-deterministic one for you to improvise.
8
+
9
+ Long Tail makes it a primitive. The same `condition()` wait — except the act of waiting mints a row that is searchable, claimable, deadlined, and role-gated on a shared metadata surface. Machines answer some rows, people answer others; the workflow resumes either way, exactly where it paused.
4
10
 
5
11
  ```bash
6
12
  npm install @hotmeshio/long-tail
@@ -8,7 +14,7 @@ npm install @hotmeshio/long-tail
8
14
 
9
15
  ## How it works
10
16
 
11
- Route work to machines or people interchangeably with ease.
17
+ One workflow, both queues: `proxyActivities` for the deterministic one, `condition()` for the one reality answers.
12
18
 
13
19
  ```typescript
14
20
  import { Durable } from '@hotmeshio/hotmesh';
@@ -54,6 +60,19 @@ export async function analyzeContent(content: string) {
54
60
  }
55
61
  ```
56
62
 
63
+ ## What the primitive buys
64
+
65
+ The non-deterministic queue has qualities the deterministic one never needed:
66
+
67
+ - **RBAC is intrinsic.** The queue is a role; permission lives in the queue itself. Any member answers — a person in the dashboard, a service account through the API — and the workflow is indifferent to which.
68
+ - **The form is data.** A versioned JSON Schema on the role renders the resolver's UI — fields, validation, conditionals, layout — with zero frontend code. Assign an escalation to one named user with self scope and you have a just-in-time form: one person, one item, one auditable edition.
69
+ - **Claims are locks.** A claim is a TTL window with an extend prompt before it lapses and a resolve guard behind it — the platform rejects a submission against an expired claim atomically, in the same statement that settles the row. Stale work cannot land.
70
+ - **Cancel is home.** Cancellation is a first-class settlement, not an exception. When the pressure no longer applies, the waiting flow sees `null` and reconciles to a modeled resting state. Handled is broader than resolved — timeout, cancel, and hop-onward are all endings the loop was built to reach.
71
+ - **One shared surface.** Every actor that touches an item writes to the same metadata-keyed row. The contact point is shared by construction, so there is one place to search, one place to claim, one place to audit.
72
+ - **History accretes.** The surface only ever adds: intent at creation, outcome at settlement, every crossing in between. The object's whole history sits there to replay — frames the queue kept for you.
73
+ - **Objects get lifecycles.** Give an object this queue and a `while` loop and it lives — a digital twin: it advertises when free, asks when it needs service, waits for reality's answer, reconciles when reality gives one. An order, a document only compliance may edit, a machine on a line — all the same shape, an object looping at a role.
74
+ - **What repeats, stops needing people.** Every settled row is a worked example. Recurring patterns become deterministic tools — problems that once required a person, then required AI reasoning, eventually require neither.
75
+
57
76
  ## Start
58
77
 
59
78
  Point at Postgres. Everything else is optional.
@@ -69,31 +88,9 @@ const lt = await start({
69
88
 
70
89
  Dashboard at [http://localhost:3000](http://localhost:3000). The [boilerplate](https://github.com/hotmeshio/long-tail-boilerplate) has a working project with workflows, MCP servers, and MinIO.
71
90
 
72
- ## The pattern
73
-
74
- Four moves, from a plan you write to a plan that keeps up with reality.
75
-
76
- **Step 1 — Author a durable workflow.** Your function checkpoints to Postgres. It can sleep, branch, call child workflows, wait for signals. Standard durable execution.
77
-
78
- **Step 2 — Certify it.** Promotion to certified adds interceptor guarantees: failures escalate instead of throwing, escalation chains route through RBAC-scoped roles, and every error is either handled or surfaced. It cannot silently fail.
79
-
80
- ```bash
81
- curl -X PUT http://localhost:3000/api/workflows/reviewContent/config \
82
- -H "Authorization: Bearer $TOKEN" \
83
- -d '{ "invocable": true, "task_queue": "default", "default_role": "reviewer" }'
84
- ```
85
-
86
- **Step 3 — React to events.** Signals are reality reporting back. Workflows publish topics; agents subscribe. When `activity.failed` fires, an automation can re-run the step, notify a team, or trigger a different workflow. The choreography is dynamic — add subscribers through the dashboard without changing code.
87
-
88
- **Step 4 — Compile what repeats.** The same workflow has two forms. What you wrote in Step 1 is the *procedural* form — readable, Temporal-like, emulated atop the graph: cheap to maintain, heavier to run. The Designer compiles a working execution into the *graph* form — the same durable workflow as a deterministic DAG: no LLM at runtime, no replay overhead, typed in and out, roughly 3x faster. Every procedural pattern has a graph equivalent and the reverse; you pick readability or speed without giving up durability, escalation, or transactional guarantees. It deploys as a reusable tool that any workflow or API call can invoke.
89
-
90
- Over time, the system accumulates compiled tools. Problems that once required a human, then required AI reasoning, eventually require neither.
91
-
92
- These four steps map to how the dashboard is organized. **React** is the reactive side (Step 3) — topics, subscriptions, automations. **Orchestrate** is the orchestrated side (Steps 1 and 4) — procedural and graph flows side by side, both durable and pull-based under the hood. **Design** is the optional bridge: with an `ANTHROPIC_API_KEY` it turns a description or a tool run into a graph flow; without one, choreography and orchestration stand on their own, no tradeoff.
93
-
94
91
  ## Register MCP tools
95
92
 
96
- Long Tail connects to any MCP server. Registered tools become durable activities and are available to the Pipeline Designer.
93
+ Long Tail connects to any MCP server. Registered tools become durable activities.
97
94
 
98
95
  **Existing package — no code:**
99
96
 
@@ -147,72 +144,6 @@ const lt = await start({
147
144
 
148
145
  All three paths produce the same outcome: tools callable as durable activities. See the [MCP guide](https://github.com/hotmeshio/long-tail/blob/main/docs/mcp.md).
149
146
 
150
- ## Compile workflows
151
-
152
- The `ltc` compiler scans TypeScript workflow files and compiles them to YAML DAGs — like `tsc` for workflows.
153
-
154
- ```bash
155
- export ANTHROPIC_API_KEY=sk-ant-...
156
- npx ltc compile workflows/
157
- ```
158
-
159
- The source is the spec. The compiled YAML is the optimized execution. Both live in the repo. See the [Compiler Guide](https://github.com/hotmeshio/long-tail/blob/main/docs/compiler.md).
160
-
161
- ## Register a graph flow by hand
162
-
163
- `graphWorkflows` is the graph-form peer of `workers`: hand-author the HotMesh YAML and it's created, deployed, and activated at startup. The same human surface is declarative here — a `hook` with an `escalation:` block. When the flow reaches it, the workflow suspends and writes the escalation row; a person resolves it and the flow continues:
164
-
165
- ```typescript
166
- const lt = await start({
167
- database: { connectionString: process.env.DATABASE_URL },
168
- graphWorkflows: [{
169
- name: 'order_approval',
170
- namespace: 'graph',
171
- inputSchema: { type: 'object', properties: { orderId: { type: 'string' }, region: { type: 'string' } }, required: ['orderId'] },
172
- yaml: `
173
- app:
174
- id: graph
175
- version: '1'
176
- graphs:
177
- - subscribes: order_approval
178
- publishes: order_approval.done
179
- input: { schema: { type: object, properties: { orderId: { type: string }, region: { type: string } } } }
180
- output: { schema: { type: object, properties: { approved: { type: boolean } } } }
181
- activities:
182
- trigger:
183
- type: trigger
184
- review:
185
- type: hook
186
- escalation:
187
- role: approver
188
- type: order-approval
189
- priority: 2
190
- description: Approve order for dispatch
191
- metadata:
192
- orderId: '{trigger.output.data.orderId}'
193
- region: '{trigger.output.data.region}'
194
- envelope:
195
- instructions: Review and approve or reject
196
- job:
197
- maps:
198
- approved: '{review.hook.data.approved}'
199
- transitions:
200
- trigger:
201
- - to: review
202
- hooks:
203
- order_approval.approve:
204
- - to: review
205
- conditions:
206
- match:
207
- - expected: '{$job.metadata.jid}'
208
- actual: '{$self.hook.data.id}'
209
- `,
210
- }],
211
- });
212
- ```
213
-
214
- It appears under **Orchestrate › Graph** and routes to a person exactly like the procedural `condition()` above — the same claim-and-resolve surface, declared in YAML. The `metadata` expressions resolve against the live job at suspension time, so the row carries the real order values.
215
-
216
147
  ## Full configuration
217
148
 
218
149
  ```typescript
@@ -221,10 +152,8 @@ const lt = await start({
221
152
  workers: [{ taskQueue: 'default', workflow: reviewContent }],
222
153
 
223
154
  // Everything below is optional
224
- graphWorkflows: [{ name: 'hello_world', namespace: 'graph', yaml: helloWorldYaml }],
225
155
  seed: { admin: { externalId: 'admin', password: process.env.ADMIN_PASSWORD } },
226
156
  mcp: { server: { enabled: true }, serverFactories: { 'my-tools': createMyToolsServer } },
227
- escalation: { strategy: 'mcp' },
228
157
  auth: { secret: process.env.JWT_SECRET },
229
158
  telemetry: { honeycomb: { apiKey: process.env.HNY } },
230
159
  logging: { pino: { level: 'info' } },
@@ -59,6 +59,33 @@ export declare function assertWriteAccess(userId: string, escalation: {
59
59
  status: number;
60
60
  error: string;
61
61
  } | null>;
62
+ /**
63
+ * Claim-liveness gate for resolve, aligned with `isEffectivelyClaimed`: a
64
+ * claim is a work LOCK that exists only while a TTL window (`assigned_until`)
65
+ * is active. Blocks exactly two states:
66
+ * - another principal holds a live window (the lock is theirs), and
67
+ * - the caller's own window has lapsed (stale work — re-claim to resolve).
68
+ *
69
+ * Everything else passes: unclaimed rows (system resolvers act on them by
70
+ * design), durable pre-assignments (`assigned_to` with no window — the
71
+ * one-time-user JIT-form shape), and rows whose window lapsed under a
72
+ * different assignee (the lock is gone; the row is back in the pool).
73
+ *
74
+ * Applies to every principal, including global access — the claim is a work
75
+ * lock, not an authorization scope, so superadmins are equally bound by it.
76
+ *
77
+ * Advisory (read-then-check): the signal-key and notification resolve paths
78
+ * re-assert the same predicate atomically inside the SDK's guarded resolve
79
+ * UPDATE (`assertClaim`); this pre-check exists so the signal/triage/re-run
80
+ * paths reject BEFORE their side effects fire.
81
+ */
82
+ export declare function assertLiveClaimant(userId: string, escalation: {
83
+ assigned_to?: string | null;
84
+ assigned_until?: Date | string | null;
85
+ }): {
86
+ status: number;
87
+ error: string;
88
+ } | null;
62
89
  /**
63
90
  * Require write_scope='all' (or global) for a queue-management verb — release and
64
91
  * escalate move an item out of the user's hands, so self-scope owners (who only
@@ -39,6 +39,7 @@ exports.validateIds = validateIds;
39
39
  exports.getEscalationWriteScope = getEscalationWriteScope;
40
40
  exports.assertReadAccess = assertReadAccess;
41
41
  exports.assertWriteAccess = assertWriteAccess;
42
+ exports.assertLiveClaimant = assertLiveClaimant;
42
43
  exports.assertQueueManageAccess = assertQueueManageAccess;
43
44
  exports.checkBulkPermission = checkBulkPermission;
44
45
  exports.resolveAssignee = resolveAssignee;
@@ -130,6 +131,39 @@ async function assertWriteAccess(userId, escalation) {
130
131
  }
131
132
  return { status: 403, error: `You do not have write access to the "${escalation.role}" role` };
132
133
  }
134
+ /**
135
+ * Claim-liveness gate for resolve, aligned with `isEffectivelyClaimed`: a
136
+ * claim is a work LOCK that exists only while a TTL window (`assigned_until`)
137
+ * is active. Blocks exactly two states:
138
+ * - another principal holds a live window (the lock is theirs), and
139
+ * - the caller's own window has lapsed (stale work — re-claim to resolve).
140
+ *
141
+ * Everything else passes: unclaimed rows (system resolvers act on them by
142
+ * design), durable pre-assignments (`assigned_to` with no window — the
143
+ * one-time-user JIT-form shape), and rows whose window lapsed under a
144
+ * different assignee (the lock is gone; the row is back in the pool).
145
+ *
146
+ * Applies to every principal, including global access — the claim is a work
147
+ * lock, not an authorization scope, so superadmins are equally bound by it.
148
+ *
149
+ * Advisory (read-then-check): the signal-key and notification resolve paths
150
+ * re-assert the same predicate atomically inside the SDK's guarded resolve
151
+ * UPDATE (`assertClaim`); this pre-check exists so the signal/triage/re-run
152
+ * paths reject BEFORE their side effects fire.
153
+ */
154
+ function assertLiveClaimant(userId, escalation) {
155
+ if (!escalation.assigned_to || !escalation.assigned_until)
156
+ return null;
157
+ const live = new Date(escalation.assigned_until).getTime() > Date.now();
158
+ const mine = escalation.assigned_to === userId;
159
+ if (live && !mine) {
160
+ return { status: 409, error: 'Escalation is claimed by another user' };
161
+ }
162
+ if (!live && mine) {
163
+ return { status: 409, error: 'Your claim has expired — re-claim this escalation to resolve it' };
164
+ }
165
+ return null;
166
+ }
133
167
  /**
134
168
  * Require write_scope='all' (or global) for a queue-management verb — release and
135
169
  * escalate move an item out of the user's hands, so self-scope owners (who only
@@ -8,6 +8,8 @@ export interface FacetInput {
8
8
  range?: FacetRange[];
9
9
  exists?: string[];
10
10
  available?: boolean;
11
+ /** Only rows past their role's priority threshold — see FacetQuery.jeopardy. */
12
+ jeopardy?: boolean;
11
13
  orderBy?: FacetOrder[];
12
14
  }
13
15
  /**
@@ -49,7 +49,8 @@ function hasFacetQuery(i) {
49
49
  i.exists?.length ||
50
50
  i.orderBy?.length ||
51
51
  i.roles?.length ||
52
- i.available != null);
52
+ i.available != null ||
53
+ i.jeopardy === true);
53
54
  }
54
55
  /**
55
56
  * List escalations with optional filters.
@@ -94,6 +95,7 @@ async function listEscalations(input, auth) {
94
95
  block: input.block,
95
96
  range: input.range,
96
97
  exists: input.exists,
98
+ jeopardy: input.jeopardy,
97
99
  orderBy: input.orderBy,
98
100
  },
99
101
  type: input.type,
@@ -168,6 +170,7 @@ async function listAvailableEscalations(input, auth) {
168
170
  block: input.block,
169
171
  range: input.range,
170
172
  exists: input.exists,
173
+ jeopardy: input.jeopardy,
171
174
  orderBy: input.orderBy,
172
175
  },
173
176
  type: input.type,
@@ -273,11 +276,18 @@ async function getEscalationStats(input, auth) {
273
276
  }
274
277
  async function getStationMetrics(input, auth) {
275
278
  try {
279
+ // Station metrics are aggregate queue-SHAPE counts (pending/claimed/resolved
280
+ // + jeopardy) per role — the depth of a lane, not the disclosure of any one
281
+ // item. Membership in a role entitles you to see its shape, so this view
282
+ // spans every role the user belongs to, including read_scope='self' roles
283
+ // (whose per-item lists stay narrowed elsewhere). Without the selfRoles
284
+ // union, a self-scoped operator would see an empty board on their own lanes.
276
285
  const scope = await (0, helpers_1.getEscalationReadScope)(auth.userId);
277
- if (!scope.global && scope.allRoles.length === 0) {
286
+ const memberRoles = [...scope.allRoles, ...scope.selfRoles];
287
+ if (!scope.global && memberRoles.length === 0) {
278
288
  return { status: 200, data: { stations: [] } };
279
289
  }
280
- const stations = await escalationService.getStationMetrics(scope.global ? undefined : scope.allRoles, input.period);
290
+ const stations = await escalationService.getStationMetrics(scope.global ? undefined : memberRoles, input.period);
281
291
  return { status: 200, data: { stations } };
282
292
  }
283
293
  catch (err) {
@@ -83,6 +83,13 @@ async function resolveEscalation(input, auth) {
83
83
  const denied = await (0, helpers_1.assertWriteAccess)(auth.userId, escalation);
84
84
  if (denied)
85
85
  return denied;
86
+ // Claim-liveness gate: a claim-tracked row resolves only under a live claim
87
+ // held by the caller. Advisory here (rejects the signal/triage/re-run paths
88
+ // before their side effects); the signal-key and notification paths below
89
+ // re-assert the same predicate atomically inside the guarded resolve UPDATE.
90
+ const stale = (0, helpers_1.assertLiveClaimant)(auth.userId, escalation);
91
+ if (stale)
92
+ return stale;
86
93
  // The resolver payload is stored exactly as submitted — its shape is the
87
94
  // workflow's contract, formed by the caller (the React app maps the form to
88
95
  // the payload via x-lt-bind before submitting). No server-side transform.
@@ -106,7 +113,7 @@ async function resolveEscalation(input, auth) {
106
113
  // `condition(signalId, config)`. The SDK's resolve marks it resolved AND
107
114
  // delivers the signal to `signal_key`, resuming THIS job — no re-run.
108
115
  if (escalation.signal_key) {
109
- return resolveViaSignalKey(escalation, resolverPayload, metadata);
116
+ return resolveViaSignalKey(escalation, resolverPayload, metadata, auth.userId);
110
117
  }
111
118
  // Path C: escalation strategy may redirect to triage
112
119
  const envelope = await reconstructEnvelope(escalation);
@@ -119,7 +126,10 @@ async function resolveEscalation(input, auth) {
119
126
  }
120
127
  // Path D: notification-only — no workflow to restart. One atomic resolve.
121
128
  if (!escalation.workflow_type || !escalation.task_queue) {
122
- await escalationService.resolveEscalation(escalation.id, resolverPayload, metadata);
129
+ const resolved = await escalationService.resolveEscalation(escalation.id, resolverPayload, metadata, auth.userId);
130
+ if (!resolved) {
131
+ return { status: 409, error: 'Escalation not available for resolution' };
132
+ }
123
133
  return { status: 200, data: { acknowledged: true, escalationId: escalation.id } };
124
134
  }
125
135
  // Path E: standard re-run
@@ -323,11 +333,13 @@ async function resolveViaConditionSignal(escalation, resolverPayload, metadata)
323
333
  * transaction, so the original job resumes in place (no re-run, no separate
324
334
  * resolve activity). Password fields are redacted before they enter the signal.
325
335
  */
326
- async function resolveViaSignalKey(escalation, resolverPayload, metadata) {
336
+ async function resolveViaSignalKey(escalation, resolverPayload, metadata, assertClaim) {
327
337
  const signalPayload = await redactPasswords(resolverPayload, escalation.metadata?.form_schema);
328
338
  // One atomic call: status→resolved, signal delivered, and the outcome patch merged
329
339
  // into the GIN-indexed metadata — all inside the single WHERE-guarded UPDATE.
330
- const resolved = await escalationService.resolveEscalation(escalation.id, signalPayload, metadata);
340
+ // `assertClaim` (interactive path only) makes the claim-liveness gate part of
341
+ // that same UPDATE; the webhook path (resolveBySignalKey) stays claim-agnostic.
342
+ const resolved = await escalationService.resolveEscalation(escalation.id, signalPayload, metadata, assertClaim);
331
343
  if (!resolved) {
332
344
  return { status: 409, error: 'Escalation not available for resolution' };
333
345
  }
@@ -107,6 +107,7 @@ function registerListRoutes(router) {
107
107
  exists: jsonParam(req.query.exists),
108
108
  orderBy: jsonParam(req.query.orderBy),
109
109
  available: req.query.available != null ? req.query.available === 'true' : undefined,
110
+ jeopardy: req.query.jeopardy === '1' || req.query.jeopardy === 'true' || undefined,
110
111
  }, req.auth);
111
112
  res.status(result.status).json(result.data ?? { error: result.error });
112
113
  });
@@ -133,6 +134,7 @@ function registerListRoutes(router) {
133
134
  range: jsonParam(req.query.range),
134
135
  exists: jsonParam(req.query.exists),
135
136
  orderBy: jsonParam(req.query.orderBy),
137
+ jeopardy: req.query.jeopardy === '1' || req.query.jeopardy === 'true' || undefined,
136
138
  }, req.auth);
137
139
  res.status(result.status).json(result.data ?? { error: result.error });
138
140
  });
@@ -20,7 +20,7 @@ export declare function claimEscalation(id: string, userId: string, durationMinu
20
20
  * ("what was intended"). Distinct from `resolverPayload`, which is delivered to
21
21
  * the waiting workflow as `condition()`'s return value and is not GIN-indexed.
22
22
  */
23
- export declare function resolveEscalation(id: string, resolverPayload: Record<string, any>, metadata?: Record<string, any>): Promise<LTEscalationRecord | null>;
23
+ export declare function resolveEscalation(id: string, resolverPayload: Record<string, any>, metadata?: Record<string, any>, assertClaim?: string): Promise<LTEscalationRecord | null>;
24
24
  /**
25
25
  * Resolve a SET of escalations by id in ONE guarded statement
26
26
  * (`UPDATE … WHERE id = ANY($ids) AND status='pending'`). Returns the rows that
@@ -115,9 +115,13 @@ async function claimEscalation(id, userId, durationMinutes = 30) {
115
115
  * ("what was intended"). Distinct from `resolverPayload`, which is delivered to
116
116
  * the waiting workflow as `condition()`'s return value and is not GIN-indexed.
117
117
  */
118
- async function resolveEscalation(id, resolverPayload, metadata) {
118
+ async function resolveEscalation(id, resolverPayload, metadata, assertClaim) {
119
119
  const client = await (0, client_1.escalations)();
120
- const result = await client.resolve({ id, resolverPayload, metadata });
120
+ // `assertClaim` rides the SDK's guarded UPDATE: no active claim lock may
121
+ // stand against this assignee, or the resolve blocks (claim-expired /
122
+ // claimed-by-other). The API layer's advisory pre-check (assertLiveClaimant)
123
+ // rejects early; this assertion is the atomic arbiter.
124
+ const result = await client.resolve({ id, resolverPayload, metadata, assertClaim });
121
125
  if (!result.ok)
122
126
  return null;
123
127
  const escalation = (0, map_1.toEscalationRecord)(result.entry);
@@ -4,8 +4,15 @@ export declare const FACET_KEY: RegExp;
4
4
  * Build the WHERE clause for a faceted query, pushing bound params into `params`.
5
5
  * Every value is parameterized; only validated column names / metadata keys and
6
6
  * a fixed operator set are interpolated. Returns `TRUE` when empty.
7
+ *
8
+ * `opts.tableRef` names the outer table for correlated clauses (the jeopardy
9
+ * predicate joins lt_roles, whose own `role` column would otherwise shadow the
10
+ * outer row's). Callers querying `public.hmsh_escalations` pass its name; the
11
+ * default matches the `public.lt_escalations` view the list paths read.
7
12
  */
8
- export declare function buildFacetWhere(q: FacetQuery, params: unknown[]): string;
13
+ export declare function buildFacetWhere(q: FacetQuery, params: unknown[], opts?: {
14
+ tableRef?: string;
15
+ }): string;
9
16
  /** Build an injection-safe ORDER BY mixing top-level columns and metadata facets. */
10
17
  export declare function buildFacetOrder(orderBy?: FacetOrder[]): string;
11
18
  /**
@@ -26,8 +26,14 @@ function metaExpr(key, numeric = false) {
26
26
  * Build the WHERE clause for a faceted query, pushing bound params into `params`.
27
27
  * Every value is parameterized; only validated column names / metadata keys and
28
28
  * a fixed operator set are interpolated. Returns `TRUE` when empty.
29
+ *
30
+ * `opts.tableRef` names the outer table for correlated clauses (the jeopardy
31
+ * predicate joins lt_roles, whose own `role` column would otherwise shadow the
32
+ * outer row's). Callers querying `public.hmsh_escalations` pass its name; the
33
+ * default matches the `public.lt_escalations` view the list paths read.
29
34
  */
30
- function buildFacetWhere(q, params) {
35
+ function buildFacetWhere(q, params, opts = {}) {
36
+ const ref = opts.tableRef ?? 'lt_escalations';
31
37
  const clauses = [];
32
38
  if (q.role) {
33
39
  params.push(q.role);
@@ -66,6 +72,28 @@ function buildFacetWhere(q, params) {
66
72
  else if (q.available === false) {
67
73
  clauses.push(`(assigned_to IS NOT NULL AND assigned_until > NOW())`);
68
74
  }
75
+ // Jeopardy: the row is past its role's priority threshold — the SAME
76
+ // expression the Pace Board's priority_count uses (services/escalation/
77
+ // sql.ts live_counts), so a jeopardy list's total always equals the pill.
78
+ // Age origin: the role's priority_facet metadata timestamp (created_at when
79
+ // unset), guarded by pg_input_is_valid so malformed values never throw.
80
+ // Threshold: priority_threshold_minutes, falling back to sla_minutes. A role
81
+ // with neither configured (or a missing lt_roles row) yields NULL → false →
82
+ // contributes no rows, matching a pill that never rendered. Purely the age
83
+ // predicate — compose `available: true` for the unclaimed pool the pill
84
+ // counts. The per-row scalar subquery is a PK lookup on the small, cached
85
+ // lt_roles table (same cost shape as the count query); if a fixed-facet
86
+ // deployment ever needs more, an expression index on the cast is the
87
+ // escape hatch.
88
+ if (q.jeopardy === true) {
89
+ clauses.push(`(SELECT CASE
90
+ WHEN rt.priority_facet IS NULL THEN ${ref}.created_at
91
+ WHEN pg_input_is_valid(${ref}.metadata->>rt.priority_facet, 'timestamptz')
92
+ THEN (${ref}.metadata->>rt.priority_facet)::timestamptz
93
+ ELSE NULL
94
+ END < NOW() - (COALESCE(rt.priority_threshold_minutes, rt.sla_minutes) * INTERVAL '1 minute')
95
+ FROM lt_roles rt WHERE rt.role = ${ref}.role)`);
96
+ }
69
97
  return clauses.length ? clauses.join('\n AND ') : 'TRUE';
70
98
  }
71
99
  const DEFAULT_ORDER = 'priority ASC, created_at ASC';
@@ -33,6 +33,15 @@ export interface FacetQuery {
33
33
  status?: string;
34
34
  /** true = only available (unclaimed/expired); false = only held now. */
35
35
  available?: boolean;
36
+ /**
37
+ * true = only rows past their role's priority threshold — the same predicate
38
+ * that produces the Pace Board's priority_count, so a jeopardy list's total
39
+ * always equals the pill's count. Age is measured from the role's
40
+ * priority_facet metadata timestamp (created_at when unset) against
41
+ * priority_threshold_minutes (sla_minutes when unset); roles with no
42
+ * threshold configured contribute no rows.
43
+ */
44
+ jeopardy?: boolean;
36
45
  orderBy?: FacetOrder[];
37
46
  limit?: number;
38
47
  offset?: number;
@@ -1,2 +1,2 @@
1
- import{j as o}from"./vendor-query-B2UbickB.js";import{c as n}from"./tasks-DEaDdyBh.js";import{j as m}from"./index-DcQiD6OF.js";import{f as c}from"./useEventHooks-oXjfKrbI.js";import{u}from"./mcp-BEsMr678.js";import{P as d}from"./PageHeaderWithStats-_zOnWAhq.js";import"./vendor-react-CXumBFUA.js";import"./vendor-icons-DPmIjhZB.js";function S(){var a;c();const{data:s}=n({limit:1}),{data:e}=m({status:"pending",limit:1}),{data:t}=u(),r=((a=t==null?void 0:t.servers)==null?void 0:a.filter(i=>i.status==="connected").length)??0,l=t?`${r}/${t.total}`:"—";return o.jsx("div",{children:o.jsx(d,{title:"Admin",stats:[{label:"Tasks",value:(s==null?void 0:s.total)??"—"},{label:"Pending Escalations",value:(e==null?void 0:e.total)??"—",dotClass:"bg-status-pending"},{label:"MCP Servers",value:l},{label:"System",value:"Healthy",dotClass:"bg-status-success"}]})})}export{S as AdminDashboard};
2
- //# sourceMappingURL=AdminDashboard-DXFIKXO3.js.map
1
+ import{j as o}from"./vendor-query-B2UbickB.js";import{c as n}from"./tasks-qwh8q5q7.js";import{k as m}from"./index-YPLTjeli.js";import{f as c}from"./useEventHooks-B8uNFB-l.js";import{u}from"./mcp-C1-iXkg7.js";import{P as d}from"./PageHeaderWithStats-TUyglVW0.js";import"./vendor-react-CXumBFUA.js";import"./vendor-icons-Qk9qLjF_.js";function j(){var a;c();const{data:s}=n({limit:1}),{data:e}=m({status:"pending",limit:1}),{data:t}=u(),r=((a=t==null?void 0:t.servers)==null?void 0:a.filter(i=>i.status==="connected").length)??0,l=t?`${r}/${t.total}`:"—";return o.jsx("div",{children:o.jsx(d,{title:"Admin",stats:[{label:"Tasks",value:(s==null?void 0:s.total)??"—"},{label:"Pending Escalations",value:(e==null?void 0:e.total)??"—",dotClass:"bg-status-pending"},{label:"MCP Servers",value:l},{label:"System",value:"Healthy",dotClass:"bg-status-success"}]})})}export{j as AdminDashboard};
2
+ //# sourceMappingURL=AdminDashboard-DQS74RL0.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"AdminDashboard-DXFIKXO3.js","sources":["../../src/pages/admin/AdminDashboard.tsx"],"sourcesContent":["import { useTasks } from '../../api/tasks';\nimport { useEscalations } from '../../api/escalations';\nimport { useEscalationListEvents } from '../../hooks/useEventHooks';\nimport { useMcpServers } from '../../api/mcp';\nimport { PageHeaderWithStats } from '../../components/common/layout/PageHeaderWithStats';\n\nexport function AdminDashboard() {\n useEscalationListEvents();\n const { data: taskData } = useTasks({ limit: 1 });\n const { data: pendingEsc } = useEscalations({ status: 'pending', limit: 1 });\n const { data: mcpData } = useMcpServers();\n\n const connectedServers =\n mcpData?.servers?.filter((s) => s.status === 'connected').length ?? 0;\n const mcpLabel = mcpData ? `${connectedServers}/${mcpData.total}` : '—';\n\n return (\n <div>\n <PageHeaderWithStats\n title=\"Admin\"\n stats={[\n { label: 'Tasks', value: taskData?.total ?? '—' },\n { label: 'Pending Escalations', value: pendingEsc?.total ?? '—', dotClass: 'bg-status-pending' },\n { label: 'MCP Servers', value: mcpLabel },\n { label: 'System', value: 'Healthy', dotClass: 'bg-status-success' },\n ]}\n />\n </div>\n );\n}\n"],"names":["AdminDashboard","useEscalationListEvents","taskData","useTasks","pendingEsc","useEscalations","mcpData","useMcpServers","connectedServers","_a","s","mcpLabel","jsx","PageHeaderWithStats"],"mappings":"4UAMO,SAASA,GAAiB,OAC/BC,EAAA,EACA,KAAM,CAAE,KAAMC,CAAA,EAAaC,EAAS,CAAE,MAAO,EAAG,EAC1C,CAAE,KAAMC,CAAA,EAAeC,EAAe,CAAE,OAAQ,UAAW,MAAO,EAAG,EACrE,CAAE,KAAMC,CAAA,EAAYC,EAAA,EAEpBC,IACJC,EAAAH,GAAA,YAAAA,EAAS,UAAT,YAAAG,EAAkB,OAAQC,GAAMA,EAAE,SAAW,aAAa,SAAU,EAChEC,EAAWL,EAAU,GAAGE,CAAgB,IAAIF,EAAQ,KAAK,GAAK,IAEpE,aACG,MAAA,CACC,SAAAM,EAAAA,IAACC,EAAA,CACC,MAAM,QACN,MAAO,CACL,CAAE,MAAO,QAAS,OAAOX,GAAA,YAAAA,EAAU,QAAS,GAAA,EAC5C,CAAE,MAAO,sBAAuB,OAAOE,GAAA,YAAAA,EAAY,QAAS,IAAK,SAAU,mBAAA,EAC3E,CAAE,MAAO,cAAe,MAAOO,CAAA,EAC/B,CAAE,MAAO,SAAU,MAAO,UAAW,SAAU,mBAAA,CAAoB,CACrE,CAAA,EAEJ,CAEJ"}
1
+ {"version":3,"file":"AdminDashboard-DQS74RL0.js","sources":["../../src/pages/admin/AdminDashboard.tsx"],"sourcesContent":["import { useTasks } from '../../api/tasks';\nimport { useEscalations } from '../../api/escalations';\nimport { useEscalationListEvents } from '../../hooks/useEventHooks';\nimport { useMcpServers } from '../../api/mcp';\nimport { PageHeaderWithStats } from '../../components/common/layout/PageHeaderWithStats';\n\nexport function AdminDashboard() {\n useEscalationListEvents();\n const { data: taskData } = useTasks({ limit: 1 });\n const { data: pendingEsc } = useEscalations({ status: 'pending', limit: 1 });\n const { data: mcpData } = useMcpServers();\n\n const connectedServers =\n mcpData?.servers?.filter((s) => s.status === 'connected').length ?? 0;\n const mcpLabel = mcpData ? `${connectedServers}/${mcpData.total}` : '—';\n\n return (\n <div>\n <PageHeaderWithStats\n title=\"Admin\"\n stats={[\n { label: 'Tasks', value: taskData?.total ?? '—' },\n { label: 'Pending Escalations', value: pendingEsc?.total ?? '—', dotClass: 'bg-status-pending' },\n { label: 'MCP Servers', value: mcpLabel },\n { label: 'System', value: 'Healthy', dotClass: 'bg-status-success' },\n ]}\n />\n </div>\n );\n}\n"],"names":["AdminDashboard","useEscalationListEvents","taskData","useTasks","pendingEsc","useEscalations","mcpData","useMcpServers","connectedServers","_a","s","mcpLabel","jsx","PageHeaderWithStats"],"mappings":"4UAMO,SAASA,GAAiB,OAC/BC,EAAA,EACA,KAAM,CAAE,KAAMC,CAAA,EAAaC,EAAS,CAAE,MAAO,EAAG,EAC1C,CAAE,KAAMC,CAAA,EAAeC,EAAe,CAAE,OAAQ,UAAW,MAAO,EAAG,EACrE,CAAE,KAAMC,CAAA,EAAYC,EAAA,EAEpBC,IACJC,EAAAH,GAAA,YAAAA,EAAS,UAAT,YAAAG,EAAkB,OAAQC,GAAMA,EAAE,SAAW,aAAa,SAAU,EAChEC,EAAWL,EAAU,GAAGE,CAAgB,IAAIF,EAAQ,KAAK,GAAK,IAEpE,aACG,MAAA,CACC,SAAAM,EAAAA,IAACC,EAAA,CACC,MAAM,QACN,MAAO,CACL,CAAE,MAAO,QAAS,OAAOX,GAAA,YAAAA,EAAU,QAAS,GAAA,EAC5C,CAAE,MAAO,sBAAuB,OAAOE,GAAA,YAAAA,EAAY,QAAS,IAAK,SAAU,mBAAA,EAC3E,CAAE,MAAO,cAAe,MAAOO,CAAA,EAC/B,CAAE,MAAO,SAAU,MAAO,UAAW,SAAU,mBAAA,CAAoB,CACrE,CAAA,EAEJ,CAEJ"}