@hotmeshio/long-tail 0.8.4 → 0.9.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 (257) hide show
  1. package/build/api/escalations/index.d.ts +1 -1
  2. package/build/api/escalations/index.js +2 -1
  3. package/build/api/escalations/resolve.d.ts +28 -0
  4. package/build/api/escalations/resolve.js +98 -0
  5. package/build/api/roles.d.ts +21 -0
  6. package/build/api/roles.js +50 -0
  7. package/build/lib/db/index.js +12 -1
  8. package/build/lib/db/migrate.js +27 -0
  9. package/build/lib/db/schemas/021_role_list_schema.sql +27 -0
  10. package/build/modules/defaults.d.ts +2 -0
  11. package/build/modules/defaults.js +3 -1
  12. package/build/routes/escalations/resolve.js +18 -0
  13. package/build/routes/roles.js +20 -0
  14. package/build/sdk/index.d.ts +8 -0
  15. package/build/sdk/index.js +1 -0
  16. package/build/services/escalation/crud.d.ts +36 -0
  17. package/build/services/escalation/crud.js +53 -0
  18. package/build/services/maintenance/index.js +22 -0
  19. package/build/services/role/index.d.ts +13 -1
  20. package/build/services/role/index.js +27 -0
  21. package/build/services/role/sql.d.ts +14 -2
  22. package/build/services/role/sql.js +51 -3
  23. package/build/services/role/types.d.ts +27 -0
  24. package/build/types/maintenance.d.ts +6 -1
  25. package/dashboard/dist/assets/{AdminDashboard-B-JptT54.js → AdminDashboard-5bWcBxyS.js} +2 -2
  26. package/dashboard/dist/assets/{AdminDashboard-B-JptT54.js.map → AdminDashboard-5bWcBxyS.js.map} +1 -1
  27. package/dashboard/dist/assets/{AgentConfigPage-B1Jc6iJk.js → AgentConfigPage-CVg_tFbf.js} +6 -6
  28. package/dashboard/dist/assets/{AgentConfigPage-B1Jc6iJk.js.map → AgentConfigPage-CVg_tFbf.js.map} +1 -1
  29. package/dashboard/dist/assets/{AgentDetailPage-CWpclCOY.js → AgentDetailPage-CLlkIoRQ.js} +4 -4
  30. package/dashboard/dist/assets/{AgentDetailPage-CWpclCOY.js.map → AgentDetailPage-CLlkIoRQ.js.map} +1 -1
  31. package/dashboard/dist/assets/{AgentsPage-DyAf5GA2.js → AgentsPage-CjruF_ek.js} +2 -2
  32. package/dashboard/dist/assets/{AgentsPage-DyAf5GA2.js.map → AgentsPage-CjruF_ek.js.map} +1 -1
  33. package/dashboard/dist/assets/AvailableEscalationsPage-h6awiuVk.js +2 -0
  34. package/dashboard/dist/assets/AvailableEscalationsPage-h6awiuVk.js.map +1 -0
  35. package/dashboard/dist/assets/{BotPicker-B1RdaNuY.js → BotPicker-CGEFHHDE.js} +2 -2
  36. package/dashboard/dist/assets/{BotPicker-B1RdaNuY.js.map → BotPicker-CGEFHHDE.js.map} +1 -1
  37. package/dashboard/dist/assets/{CapabilitiesPage-CxHDnyjX.js → CapabilitiesPage-BI1tNYun.js} +2 -2
  38. package/dashboard/dist/assets/{CapabilitiesPage-CxHDnyjX.js.map → CapabilitiesPage-BI1tNYun.js.map} +1 -1
  39. package/dashboard/dist/assets/{CollapsibleSection-Ds0e8cCO.js → CollapsibleSection-B9f52Z2K.js} +2 -2
  40. package/dashboard/dist/assets/{CollapsibleSection-Ds0e8cCO.js.map → CollapsibleSection-B9f52Z2K.js.map} +1 -1
  41. package/dashboard/dist/assets/{CopyableId-blg-gZvT.js → CopyableId-uot2mE_g.js} +2 -2
  42. package/dashboard/dist/assets/{CopyableId-blg-gZvT.js.map → CopyableId-uot2mE_g.js.map} +1 -1
  43. package/dashboard/dist/assets/CountdownTimer-C71erQ67.js +2 -0
  44. package/dashboard/dist/assets/CountdownTimer-C71erQ67.js.map +1 -0
  45. package/dashboard/dist/assets/{CredentialsPage-D8UVO-3A.js → CredentialsPage-smk5H_mP.js} +2 -2
  46. package/dashboard/dist/assets/{CredentialsPage-D8UVO-3A.js.map → CredentialsPage-smk5H_mP.js.map} +1 -1
  47. package/dashboard/dist/assets/{CronLabel-ZesYXxCH.js → CronLabel-CCJLmkIq.js} +2 -2
  48. package/dashboard/dist/assets/{CronLabel-ZesYXxCH.js.map → CronLabel-CCJLmkIq.js.map} +1 -1
  49. package/dashboard/dist/assets/CustomDurationPicker-DQ28g5GB.js +2 -0
  50. package/dashboard/dist/assets/CustomDurationPicker-DQ28g5GB.js.map +1 -0
  51. package/dashboard/dist/assets/DataTable-Byn3oyT9.js +2 -0
  52. package/dashboard/dist/assets/DataTable-Byn3oyT9.js.map +1 -0
  53. package/dashboard/dist/assets/{DropZone-SgiK1WOl.js → DropZone-C_Mc4UrO.js} +2 -2
  54. package/dashboard/dist/assets/{DropZone-SgiK1WOl.js.map → DropZone-C_Mc4UrO.js.map} +1 -1
  55. package/dashboard/dist/assets/{ElapsedCell-CO8S6vdb.js → ElapsedCell-Cw1uvJKx.js} +2 -2
  56. package/dashboard/dist/assets/{ElapsedCell-CO8S6vdb.js.map → ElapsedCell-Cw1uvJKx.js.map} +1 -1
  57. package/dashboard/dist/assets/EscalationListSchemaPage-3v2tiCnq.js +20 -0
  58. package/dashboard/dist/assets/EscalationListSchemaPage-3v2tiCnq.js.map +1 -0
  59. package/dashboard/dist/assets/EscalationSchemaPage-DIq2pMED.js +9 -0
  60. package/dashboard/dist/assets/EscalationSchemaPage-DIq2pMED.js.map +1 -0
  61. package/dashboard/dist/assets/{EscalationsOverview-DMpRvVql.js → EscalationsOverview-BqOZ1v8w.js} +2 -2
  62. package/dashboard/dist/assets/{EscalationsOverview-DMpRvVql.js.map → EscalationsOverview-BqOZ1v8w.js.map} +1 -1
  63. package/dashboard/dist/assets/EventTable-CyfHfOWj.js +2 -0
  64. package/dashboard/dist/assets/EventTable-CyfHfOWj.js.map +1 -0
  65. package/dashboard/dist/assets/{FilterBar-B4Mk2PvT.js → FilterBar-DDIn8TOs.js} +2 -2
  66. package/dashboard/dist/assets/{FilterBar-B4Mk2PvT.js.map → FilterBar-DDIn8TOs.js.map} +1 -1
  67. package/dashboard/dist/assets/{GraphInvokePage-CTuul-av.js → GraphInvokePage-DbNUaWhH.js} +2 -2
  68. package/dashboard/dist/assets/{GraphInvokePage-CTuul-av.js.map → GraphInvokePage-DbNUaWhH.js.map} +1 -1
  69. package/dashboard/dist/assets/{HomePage-BKFpOaHX.js → HomePage-Cj9sLt4Z.js} +2 -2
  70. package/dashboard/dist/assets/{HomePage-BKFpOaHX.js.map → HomePage-Cj9sLt4Z.js.map} +1 -1
  71. package/dashboard/dist/assets/{ListToolbar-Dz2_neVF.js → ListToolbar-D7PoQOZC.js} +2 -2
  72. package/dashboard/dist/assets/{ListToolbar-Dz2_neVF.js.map → ListToolbar-D7PoQOZC.js.map} +1 -1
  73. package/dashboard/dist/assets/{McpOverview-Do-vaEqV.js → McpOverview-D9g1iJyn.js} +2 -2
  74. package/dashboard/dist/assets/{McpOverview-Do-vaEqV.js.map → McpOverview-D9g1iJyn.js.map} +1 -1
  75. package/dashboard/dist/assets/{McpQueryDetailPage-HwDGNSbN.js → McpQueryDetailPage-DwS1pXdT.js} +2 -2
  76. package/dashboard/dist/assets/{McpQueryDetailPage-HwDGNSbN.js.map → McpQueryDetailPage-DwS1pXdT.js.map} +1 -1
  77. package/dashboard/dist/assets/{McpQueryPage-DxMUpQ7R.js → McpQueryPage-DZQ1z6-H.js} +2 -2
  78. package/dashboard/dist/assets/{McpQueryPage-DxMUpQ7R.js.map → McpQueryPage-DZQ1z6-H.js.map} +1 -1
  79. package/dashboard/dist/assets/McpRunDetailPage-Bx6w39lr.js +2 -0
  80. package/dashboard/dist/assets/{McpRunDetailPage-BzzZIXIk.js.map → McpRunDetailPage-Bx6w39lr.js.map} +1 -1
  81. package/dashboard/dist/assets/{McpRunsPage-D3J42d2i.js → McpRunsPage-C-oS_tTh.js} +2 -2
  82. package/dashboard/dist/assets/{McpRunsPage-D3J42d2i.js.map → McpRunsPage-C-oS_tTh.js.map} +1 -1
  83. package/dashboard/dist/assets/{NamespacePill-SErKwgkb.js → NamespacePill-DhgC3EUO.js} +2 -2
  84. package/dashboard/dist/assets/{NamespacePill-SErKwgkb.js.map → NamespacePill-DhgC3EUO.js.map} +1 -1
  85. package/dashboard/dist/assets/OperationsPage-CQzX6jM1.js +2 -0
  86. package/dashboard/dist/assets/OperationsPage-CQzX6jM1.js.map +1 -0
  87. package/dashboard/dist/assets/OperatorDashboard-Cqf87ojd.js +2 -0
  88. package/dashboard/dist/assets/{OperatorDashboard-BqJn8wEZ.js.map → OperatorDashboard-Cqf87ojd.js.map} +1 -1
  89. package/dashboard/dist/assets/PageHeader-sGNTmSXX.js +2 -0
  90. package/dashboard/dist/assets/PageHeader-sGNTmSXX.js.map +1 -0
  91. package/dashboard/dist/assets/{PageHeaderWithStats-W3akV6nB.js → PageHeaderWithStats-87STTAJX.js} +2 -2
  92. package/dashboard/dist/assets/{PageHeaderWithStats-W3akV6nB.js.map → PageHeaderWithStats-87STTAJX.js.map} +1 -1
  93. package/dashboard/dist/assets/{ProcessDetailPage-C8sT2xuE.js → ProcessDetailPage-CplnDXkz.js} +2 -2
  94. package/dashboard/dist/assets/{ProcessDetailPage-C8sT2xuE.js.map → ProcessDetailPage-CplnDXkz.js.map} +1 -1
  95. package/dashboard/dist/assets/{ProcessesListPage-eA8buUgd.js → ProcessesListPage-DFOJC27f.js} +2 -2
  96. package/dashboard/dist/assets/{ProcessesListPage-eA8buUgd.js.map → ProcessesListPage-DFOJC27f.js.map} +1 -1
  97. package/dashboard/dist/assets/RoleDetailPage-VrWOaPlg.js +8 -0
  98. package/dashboard/dist/assets/RoleDetailPage-VrWOaPlg.js.map +1 -0
  99. package/dashboard/dist/assets/{RolePill-DHWixsQR.js → RolePill-C91qbDOW.js} +2 -2
  100. package/dashboard/dist/assets/{RolePill-DHWixsQR.js.map → RolePill-C91qbDOW.js.map} +1 -1
  101. package/dashboard/dist/assets/{RolesPage-BQNQxsMY.js → RolesPage-DdI1rVAP.js} +2 -2
  102. package/dashboard/dist/assets/{RolesPage-BQNQxsMY.js.map → RolesPage-DdI1rVAP.js.map} +1 -1
  103. package/dashboard/dist/assets/{RunAsSelector-mhusrlYE.js → RunAsSelector-C6EozzKV.js} +2 -2
  104. package/dashboard/dist/assets/{RunAsSelector-mhusrlYE.js.map → RunAsSelector-C6EozzKV.js.map} +1 -1
  105. package/dashboard/dist/assets/SlidePanel-BpWveV1T.js +2 -0
  106. package/dashboard/dist/assets/SlidePanel-BpWveV1T.js.map +1 -0
  107. package/dashboard/dist/assets/{StatusBadge-DZ9137bI.js → StatusBadge-CEQMRmSQ.js} +2 -2
  108. package/dashboard/dist/assets/StatusBadge-CEQMRmSQ.js.map +1 -0
  109. package/dashboard/dist/assets/{StickyPagination-CqT9Q1I6.js → StickyPagination-BEgkYJ0-.js} +2 -2
  110. package/dashboard/dist/assets/{StickyPagination-CqT9Q1I6.js.map → StickyPagination-BEgkYJ0-.js.map} +1 -1
  111. package/dashboard/dist/assets/{StreamMessageDetail-jOJaoupp.js → StreamMessageDetail-DWVPtzj7.js} +2 -2
  112. package/dashboard/dist/assets/{StreamMessageDetail-jOJaoupp.js.map → StreamMessageDetail-DWVPtzj7.js.map} +1 -1
  113. package/dashboard/dist/assets/{SwimlaneTimeline-BrfmWgAj.js → SwimlaneTimeline-4NrDJNYc.js} +2 -2
  114. package/dashboard/dist/assets/{SwimlaneTimeline-BrfmWgAj.js.map → SwimlaneTimeline-4NrDJNYc.js.map} +1 -1
  115. package/dashboard/dist/assets/{TagInput-CGGC3DTR.js → TagInput-Csm36_pS.js} +2 -2
  116. package/dashboard/dist/assets/{TagInput-CGGC3DTR.js.map → TagInput-Csm36_pS.js.map} +1 -1
  117. package/dashboard/dist/assets/{TaskDetailPage-Cus_JHIC.js → TaskDetailPage-BvdaAUtF.js} +2 -2
  118. package/dashboard/dist/assets/{TaskDetailPage-Cus_JHIC.js.map → TaskDetailPage-BvdaAUtF.js.map} +1 -1
  119. package/dashboard/dist/assets/{TaskQueuePill-D6omQJNY.js → TaskQueuePill-5XdNaqwM.js} +2 -2
  120. package/dashboard/dist/assets/{TaskQueuePill-D6omQJNY.js.map → TaskQueuePill-5XdNaqwM.js.map} +1 -1
  121. package/dashboard/dist/assets/{TasksListPage-B7GK8AoX.js → TasksListPage-BxA161LH.js} +2 -2
  122. package/dashboard/dist/assets/{TasksListPage-B7GK8AoX.js.map → TasksListPage-BxA161LH.js.map} +1 -1
  123. package/dashboard/dist/assets/{TimeAgo-C606LzT3.js → TimeAgo-ChwJ4fjZ.js} +2 -2
  124. package/dashboard/dist/assets/{TimeAgo-C606LzT3.js.map → TimeAgo-ChwJ4fjZ.js.map} +1 -1
  125. package/dashboard/dist/assets/{TimestampCell-hfh6zvH-.js → TimestampCell-C7KzmvyC.js} +2 -2
  126. package/dashboard/dist/assets/{TimestampCell-hfh6zvH-.js.map → TimestampCell-C7KzmvyC.js.map} +1 -1
  127. package/dashboard/dist/assets/{ToolPill-BW0iRwZu.js → ToolPill-CAwj7EPw.js} +2 -2
  128. package/dashboard/dist/assets/{ToolPill-BW0iRwZu.js.map → ToolPill-CAwj7EPw.js.map} +1 -1
  129. package/dashboard/dist/assets/{ToolTestPanel-CsPsyvQq.js → ToolTestPanel-ISkuXtkT.js} +2 -2
  130. package/dashboard/dist/assets/{ToolTestPanel-CsPsyvQq.js.map → ToolTestPanel-ISkuXtkT.js.map} +1 -1
  131. package/dashboard/dist/assets/{TopicDetailPage-Diz6r7zr.js → TopicDetailPage-D8uZ8Wbs.js} +3 -3
  132. package/dashboard/dist/assets/{TopicDetailPage-Diz6r7zr.js.map → TopicDetailPage-D8uZ8Wbs.js.map} +1 -1
  133. package/dashboard/dist/assets/TopicsPage-rj88vA_1.js +2 -0
  134. package/dashboard/dist/assets/{TopicsPage-B6fn7fFh.js.map → TopicsPage-rj88vA_1.js.map} +1 -1
  135. package/dashboard/dist/assets/{UserName-Cc-5QH5q.js → UserName-D03Lh6Aj.js} +2 -2
  136. package/dashboard/dist/assets/{UserName-Cc-5QH5q.js.map → UserName-D03Lh6Aj.js.map} +1 -1
  137. package/dashboard/dist/assets/WorkflowExecutionPage-LD9WzPee.js +2 -0
  138. package/dashboard/dist/assets/WorkflowExecutionPage-LD9WzPee.js.map +1 -0
  139. package/dashboard/dist/assets/{WorkflowPill-BtNBSplz.js → WorkflowPill-fodIWnT3.js} +2 -2
  140. package/dashboard/dist/assets/{WorkflowPill-BtNBSplz.js.map → WorkflowPill-fodIWnT3.js.map} +1 -1
  141. package/dashboard/dist/assets/{WorkflowsDashboard-CHCM6hUa.js → WorkflowsDashboard-DH0jitAb.js} +2 -2
  142. package/dashboard/dist/assets/{WorkflowsDashboard-CHCM6hUa.js.map → WorkflowsDashboard-DH0jitAb.js.map} +1 -1
  143. package/dashboard/dist/assets/{WorkflowsOverview-WeOvl8-_.js → WorkflowsOverview-D6BVaH9e.js} +2 -2
  144. package/dashboard/dist/assets/{WorkflowsOverview-WeOvl8-_.js.map → WorkflowsOverview-D6BVaH9e.js.map} +1 -1
  145. package/dashboard/dist/assets/{YamlWorkflowDetailPage-6GfhVDpv.js → YamlWorkflowDetailPage-D_CfNEqW.js} +2 -2
  146. package/dashboard/dist/assets/{YamlWorkflowDetailPage-6GfhVDpv.js.map → YamlWorkflowDetailPage-D_CfNEqW.js.map} +1 -1
  147. package/dashboard/dist/assets/{YamlWorkflowsPage-DqcY3ocP.js → YamlWorkflowsPage-BMJgtcAq.js} +2 -2
  148. package/dashboard/dist/assets/{YamlWorkflowsPage-DqcY3ocP.js.map → YamlWorkflowsPage-BMJgtcAq.js.map} +1 -1
  149. package/dashboard/dist/assets/{agents-DlzvjNxE.js → agents-Ci0tZDO3.js} +2 -2
  150. package/dashboard/dist/assets/{agents-DlzvjNxE.js.map → agents-Ci0tZDO3.js.map} +1 -1
  151. package/dashboard/dist/assets/{bots-CWMN-KUI.js → bots-DZyt1oO4.js} +2 -2
  152. package/dashboard/dist/assets/{bots-CWMN-KUI.js.map → bots-DZyt1oO4.js.map} +1 -1
  153. package/dashboard/dist/assets/{capabilities-_WSZ7cxO.js → capabilities-DzVKeWyC.js} +2 -2
  154. package/dashboard/dist/assets/{capabilities-_WSZ7cxO.js.map → capabilities-DzVKeWyC.js.map} +1 -1
  155. package/dashboard/dist/assets/{controlplane-_k9nWZy3.js → controlplane-CQzrn-rD.js} +2 -2
  156. package/dashboard/dist/assets/{controlplane-_k9nWZy3.js.map → controlplane-CQzrn-rD.js.map} +1 -1
  157. package/dashboard/dist/assets/escalation-columns-s29Q4mrF.js +2 -0
  158. package/dashboard/dist/assets/{escalation-columns-Dyrf6Tdp.js.map → escalation-columns-s29Q4mrF.js.map} +1 -1
  159. package/dashboard/dist/assets/{index-kKufOPLo.js → index-3SrgIA7w.js} +2 -2
  160. package/dashboard/dist/assets/{index-kKufOPLo.js.map → index-3SrgIA7w.js.map} +1 -1
  161. package/dashboard/dist/assets/{index-Dd4Si1Rl.js → index-B2dB90aC.js} +2 -2
  162. package/dashboard/dist/assets/{index-Dd4Si1Rl.js.map → index-B2dB90aC.js.map} +1 -1
  163. package/dashboard/dist/assets/index-BSOcWfeR.js +5 -0
  164. package/dashboard/dist/assets/index-BSOcWfeR.js.map +1 -0
  165. package/dashboard/dist/assets/{index-uUYXmvFf.js → index-BWy4UvaT.js} +2 -2
  166. package/dashboard/dist/assets/{index-uUYXmvFf.js.map → index-BWy4UvaT.js.map} +1 -1
  167. package/dashboard/dist/assets/{index-C3h0qGzG.js → index-BqKxJ2fB.js} +2 -2
  168. package/dashboard/dist/assets/{index-C3h0qGzG.js.map → index-BqKxJ2fB.js.map} +1 -1
  169. package/dashboard/dist/assets/{index-DwWP-Z_c.js → index-C9moIiIq.js} +5 -5
  170. package/dashboard/dist/assets/{index-DwWP-Z_c.js.map → index-C9moIiIq.js.map} +1 -1
  171. package/dashboard/dist/assets/{index-Dc-Ye2wk.js → index-CcKXMU-s.js} +2 -2
  172. package/dashboard/dist/assets/{index-Dc-Ye2wk.js.map → index-CcKXMU-s.js.map} +1 -1
  173. package/dashboard/dist/assets/index-DIPe82gq.css +1 -0
  174. package/dashboard/dist/assets/{index-D8GcHxNJ.js → index-DOHGHMP7.js} +2 -2
  175. package/dashboard/dist/assets/{index-D8GcHxNJ.js.map → index-DOHGHMP7.js.map} +1 -1
  176. package/dashboard/dist/assets/{index-C5Yc3TT7.js → index-DTn4XzYN.js} +2 -2
  177. package/dashboard/dist/assets/{index-C5Yc3TT7.js.map → index-DTn4XzYN.js.map} +1 -1
  178. package/dashboard/dist/assets/index-Dv8mzG9B.js +2 -0
  179. package/dashboard/dist/assets/index-Dv8mzG9B.js.map +1 -0
  180. package/dashboard/dist/assets/{index-Ex5tEte8.js → index-LwodswlU.js} +2 -2
  181. package/dashboard/dist/assets/{index-Ex5tEte8.js.map → index-LwodswlU.js.map} +1 -1
  182. package/dashboard/dist/assets/{index-C7VXLKJs.js → index-kPipMHZx.js} +2 -2
  183. package/dashboard/dist/assets/{index-C7VXLKJs.js.map → index-kPipMHZx.js.map} +1 -1
  184. package/dashboard/dist/assets/{index-BiApvBEz.js → index-sdc9ecdW.js} +2 -2
  185. package/dashboard/dist/assets/{index-BiApvBEz.js.map → index-sdc9ecdW.js.map} +1 -1
  186. package/dashboard/dist/assets/{knowledge-fYoEF4Lx.js → knowledge-fi5z5YLq.js} +2 -2
  187. package/dashboard/dist/assets/{knowledge-fYoEF4Lx.js.map → knowledge-fi5z5YLq.js.map} +1 -1
  188. package/dashboard/dist/assets/{mcp-1lzlgtS8.js → mcp-CH8Tj9Pw.js} +2 -2
  189. package/dashboard/dist/assets/{mcp-1lzlgtS8.js.map → mcp-CH8Tj9Pw.js.map} +1 -1
  190. package/dashboard/dist/assets/{mcp-query-nlrAz7m_.js → mcp-query-6alwNh7K.js} +2 -2
  191. package/dashboard/dist/assets/{mcp-query-nlrAz7m_.js.map → mcp-query-6alwNh7K.js.map} +1 -1
  192. package/dashboard/dist/assets/{pipelines-Ihi7EKXi.js → pipelines-Dqh9PLUw.js} +2 -2
  193. package/dashboard/dist/assets/{pipelines-Ihi7EKXi.js.map → pipelines-Dqh9PLUw.js.map} +1 -1
  194. package/dashboard/dist/assets/roles-dJKBZUBh.js +2 -0
  195. package/dashboard/dist/assets/roles-dJKBZUBh.js.map +1 -0
  196. package/dashboard/dist/assets/{tasks-Crk9Y3C4.js → tasks-BYz490un.js} +2 -2
  197. package/dashboard/dist/assets/{tasks-Crk9Y3C4.js.map → tasks-BYz490un.js.map} +1 -1
  198. package/dashboard/dist/assets/{topics-CtfcCyw9.js → topics-Dt975EWi.js} +2 -2
  199. package/dashboard/dist/assets/{topics-CtfcCyw9.js.map → topics-Dt975EWi.js.map} +1 -1
  200. package/dashboard/dist/assets/{useEventHooks-Dn_vN2xE.js → useEventHooks-ZT6RV2Gj.js} +2 -2
  201. package/dashboard/dist/assets/{useEventHooks-Dn_vN2xE.js.map → useEventHooks-ZT6RV2Gj.js.map} +1 -1
  202. package/dashboard/dist/assets/{useNamespace-Cfs3vKWk.js → useNamespace-B_G1iE9i.js} +2 -2
  203. package/dashboard/dist/assets/{useNamespace-Cfs3vKWk.js.map → useNamespace-B_G1iE9i.js.map} +1 -1
  204. package/dashboard/dist/assets/{useYamlActivityEvents-ymobxH47.js → useYamlActivityEvents-DGkvIadL.js} +2 -2
  205. package/dashboard/dist/assets/{useYamlActivityEvents-ymobxH47.js.map → useYamlActivityEvents-DGkvIadL.js.map} +1 -1
  206. package/dashboard/dist/assets/{users-DngGIiC_.js → users-C2jopvM1.js} +2 -2
  207. package/dashboard/dist/assets/{users-DngGIiC_.js.map → users-C2jopvM1.js.map} +1 -1
  208. package/dashboard/dist/assets/{vendor-icons-DOIDKgo6.js → vendor-icons-BXhWQC7l.js} +161 -121
  209. package/dashboard/dist/assets/vendor-icons-BXhWQC7l.js.map +1 -0
  210. package/dashboard/dist/assets/{workflows-DuUspMAl.js → workflows-By8Wgvig.js} +2 -2
  211. package/dashboard/dist/assets/{workflows-DuUspMAl.js.map → workflows-By8Wgvig.js.map} +1 -1
  212. package/dashboard/dist/assets/{yaml-workflows-DXQCiOsP.js → yaml-workflows-Cq_vp41d.js} +2 -2
  213. package/dashboard/dist/assets/{yaml-workflows-DXQCiOsP.js.map → yaml-workflows-Cq_vp41d.js.map} +1 -1
  214. package/dashboard/dist/index.html +3 -3
  215. package/docs/api/http/escalations.md +20 -0
  216. package/docs/api/sdk/escalations.md +37 -2
  217. package/docs/dashboard.md +16 -3
  218. package/docs/data.md +1 -1
  219. package/docs/hitl-guide.md +240 -21
  220. package/docs/maintenance.md +6 -3
  221. package/docs/oauth-and-delegation.md +1 -1
  222. package/package.json +3 -3
  223. package/dashboard/dist/assets/AvailableEscalationsPage-DDwAgklw.js +0 -2
  224. package/dashboard/dist/assets/AvailableEscalationsPage-DDwAgklw.js.map +0 -1
  225. package/dashboard/dist/assets/CustomDurationPicker-OKlwH5a_.js +0 -2
  226. package/dashboard/dist/assets/CustomDurationPicker-OKlwH5a_.js.map +0 -1
  227. package/dashboard/dist/assets/DataTable-ZWgVQg7F.js +0 -2
  228. package/dashboard/dist/assets/DataTable-ZWgVQg7F.js.map +0 -1
  229. package/dashboard/dist/assets/EscalationSchemaPage-Br5sD8-N.js +0 -9
  230. package/dashboard/dist/assets/EscalationSchemaPage-Br5sD8-N.js.map +0 -1
  231. package/dashboard/dist/assets/EventTable-BARJeiS2.js +0 -2
  232. package/dashboard/dist/assets/EventTable-BARJeiS2.js.map +0 -1
  233. package/dashboard/dist/assets/McpRunDetailPage-BzzZIXIk.js +0 -2
  234. package/dashboard/dist/assets/OperationsPage-BaM-QDhy.js +0 -2
  235. package/dashboard/dist/assets/OperationsPage-BaM-QDhy.js.map +0 -1
  236. package/dashboard/dist/assets/OperatorDashboard-BqJn8wEZ.js +0 -2
  237. package/dashboard/dist/assets/PageHeader-DJo7yMUR.js +0 -2
  238. package/dashboard/dist/assets/PageHeader-DJo7yMUR.js.map +0 -1
  239. package/dashboard/dist/assets/RoleDetailPage-aR-ror5v.js +0 -11
  240. package/dashboard/dist/assets/RoleDetailPage-aR-ror5v.js.map +0 -1
  241. package/dashboard/dist/assets/StatusBadge-DZ9137bI.js.map +0 -1
  242. package/dashboard/dist/assets/TopicsPage-B6fn7fFh.js +0 -2
  243. package/dashboard/dist/assets/WorkflowExecutionPage-BFq7UdhA.js +0 -2
  244. package/dashboard/dist/assets/WorkflowExecutionPage-BFq7UdhA.js.map +0 -1
  245. package/dashboard/dist/assets/escalation-CK8ElYzD.js +0 -2
  246. package/dashboard/dist/assets/escalation-CK8ElYzD.js.map +0 -1
  247. package/dashboard/dist/assets/escalation-columns-Dyrf6Tdp.js +0 -2
  248. package/dashboard/dist/assets/index-BdqKh0UK.css +0 -1
  249. package/dashboard/dist/assets/index-OV1JoB_P.js +0 -6
  250. package/dashboard/dist/assets/index-OV1JoB_P.js.map +0 -1
  251. package/dashboard/dist/assets/index-ZBj6Xgim.js +0 -2
  252. package/dashboard/dist/assets/index-ZBj6Xgim.js.map +0 -1
  253. package/dashboard/dist/assets/roles-D4Wn7kE9.js +0 -2
  254. package/dashboard/dist/assets/roles-D4Wn7kE9.js.map +0 -1
  255. package/dashboard/dist/assets/useCollapsedSections-BU5HULGs.js +0 -2
  256. package/dashboard/dist/assets/useCollapsedSections-BU5HULGs.js.map +0 -1
  257. package/dashboard/dist/assets/vendor-icons-DOIDKgo6.js.map +0 -1
@@ -3,7 +3,7 @@ export { listEscalations, listAvailableEscalations, listDistinctTypes, listFacet
3
3
  export { getEscalation, getEscalationsByWorkflowId, escalateToRole } from './single';
4
4
  export { claimEscalation, releaseEscalation } from './claim';
5
5
  export { releaseExpiredClaims, updatePriority, bulkClaim, bulkAssign, bulkEscalate, bulkTriage } from './bulk';
6
- export { resolveEscalation, resolveBySignalKey, resolveByIds } from './resolve';
6
+ export { resolveEscalation, resolveBySignalKey, resolveByIds, resolveAllOrNone } from './resolve';
7
7
  export { findByMetadata, claimByMetadata, resolveByMetadata } from './metadata';
8
8
  export { cancelSingleEscalation, bulkCancel } from './cancel';
9
9
  export { searchByFacets, claimGroups, claimByFacets } from './facets';
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.claimByFacets = exports.claimGroups = exports.searchByFacets = exports.bulkCancel = exports.cancelSingleEscalation = exports.resolveByMetadata = exports.claimByMetadata = exports.findByMetadata = exports.resolveByIds = exports.resolveBySignalKey = exports.resolveEscalation = exports.bulkTriage = exports.bulkEscalate = exports.bulkAssign = exports.bulkClaim = exports.updatePriority = exports.releaseExpiredClaims = exports.releaseEscalation = exports.claimEscalation = exports.escalateToRole = exports.getEscalationsByWorkflowId = exports.getEscalation = exports.getStationMetrics = exports.getEscalationStats = exports.listFacetKeys = exports.listDistinctTypes = exports.listAvailableEscalations = exports.listEscalations = exports.createEscalation = void 0;
3
+ exports.claimByFacets = exports.claimGroups = exports.searchByFacets = exports.bulkCancel = exports.cancelSingleEscalation = exports.resolveByMetadata = exports.claimByMetadata = exports.findByMetadata = exports.resolveAllOrNone = exports.resolveByIds = exports.resolveBySignalKey = exports.resolveEscalation = exports.bulkTriage = exports.bulkEscalate = exports.bulkAssign = exports.bulkClaim = exports.updatePriority = exports.releaseExpiredClaims = exports.releaseEscalation = exports.claimEscalation = exports.escalateToRole = exports.getEscalationsByWorkflowId = exports.getEscalation = exports.getStationMetrics = exports.getEscalationStats = exports.listFacetKeys = exports.listDistinctTypes = exports.listAvailableEscalations = exports.listEscalations = exports.createEscalation = void 0;
4
4
  var create_1 = require("./create");
5
5
  Object.defineProperty(exports, "createEscalation", { enumerable: true, get: function () { return create_1.createEscalation; } });
6
6
  var list_1 = require("./list");
@@ -28,6 +28,7 @@ var resolve_1 = require("./resolve");
28
28
  Object.defineProperty(exports, "resolveEscalation", { enumerable: true, get: function () { return resolve_1.resolveEscalation; } });
29
29
  Object.defineProperty(exports, "resolveBySignalKey", { enumerable: true, get: function () { return resolve_1.resolveBySignalKey; } });
30
30
  Object.defineProperty(exports, "resolveByIds", { enumerable: true, get: function () { return resolve_1.resolveByIds; } });
31
+ Object.defineProperty(exports, "resolveAllOrNone", { enumerable: true, get: function () { return resolve_1.resolveAllOrNone; } });
31
32
  var metadata_1 = require("./metadata");
32
33
  Object.defineProperty(exports, "findByMetadata", { enumerable: true, get: function () { return metadata_1.findByMetadata; } });
33
34
  Object.defineProperty(exports, "claimByMetadata", { enumerable: true, get: function () { return metadata_1.claimByMetadata; } });
@@ -44,3 +44,31 @@ export declare function resolveByIds(input: {
44
44
  resolverPayload: Record<string, any>;
45
45
  metadata?: Record<string, any>;
46
46
  }, auth: LTApiAuth): Promise<LTApiResult>;
47
+ /**
48
+ * Atomic all-or-none bulk resolve with PER-ROW payloads — the gang-handoff
49
+ * sibling of {@link resolveByIds}. Every listed escalation must be pending and
50
+ * writable by the caller, or NOTHING resolves. Unlike resolveByIds, rows
51
+ * backing a live `condition()` waiter are first-class: each is woken with its
52
+ * own payload inside the single atomic statement (same wake contract as
53
+ * single resolve).
54
+ *
55
+ * RBAC matches resolveByIds: per-item write scope, 404 non-disclosure when any
56
+ * id is missing or out of scope, nothing resolved. `requireClaimed` adds a
57
+ * SQL-level assertion that every row is currently assigned to the caller —
58
+ * for claim-then-resolve flows (a broker resolving its claimed gang), this
59
+ * closes the window where another principal re-claims a member mid-flight.
60
+ *
61
+ * Rows whose resolution requires a non-atomic path (legacy signal routing via
62
+ * `metadata.signal_id` / `metadata.signal_routing`, which need a separate
63
+ * workflow signal) block the batch with 409 — bulk-resolving them would strand
64
+ * their workflows. Re-run/triage rows (workflow_type without any signal shape)
65
+ * resolve as bookkeeping acknowledgements, matching resolveByIds semantics.
66
+ */
67
+ export declare function resolveAllOrNone(input: {
68
+ items: Array<{
69
+ id: string;
70
+ resolverPayload: Record<string, any>;
71
+ }>;
72
+ metadata?: Record<string, any>;
73
+ requireClaimed?: boolean;
74
+ }, auth: LTApiAuth): Promise<LTApiResult>;
@@ -36,6 +36,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.resolveEscalation = resolveEscalation;
37
37
  exports.resolveBySignalKey = resolveBySignalKey;
38
38
  exports.resolveByIds = resolveByIds;
39
+ exports.resolveAllOrNone = resolveAllOrNone;
39
40
  const escalationService = __importStar(require("../../services/escalation"));
40
41
  const taskService = __importStar(require("../../services/task"));
41
42
  const escalation_strategy_1 = require("../../services/escalation-strategy");
@@ -202,6 +203,103 @@ async function resolveByIds(input, auth) {
202
203
  return { status: 500, error: err.message };
203
204
  }
204
205
  }
206
+ /**
207
+ * Atomic all-or-none bulk resolve with PER-ROW payloads — the gang-handoff
208
+ * sibling of {@link resolveByIds}. Every listed escalation must be pending and
209
+ * writable by the caller, or NOTHING resolves. Unlike resolveByIds, rows
210
+ * backing a live `condition()` waiter are first-class: each is woken with its
211
+ * own payload inside the single atomic statement (same wake contract as
212
+ * single resolve).
213
+ *
214
+ * RBAC matches resolveByIds: per-item write scope, 404 non-disclosure when any
215
+ * id is missing or out of scope, nothing resolved. `requireClaimed` adds a
216
+ * SQL-level assertion that every row is currently assigned to the caller —
217
+ * for claim-then-resolve flows (a broker resolving its claimed gang), this
218
+ * closes the window where another principal re-claims a member mid-flight.
219
+ *
220
+ * Rows whose resolution requires a non-atomic path (legacy signal routing via
221
+ * `metadata.signal_id` / `metadata.signal_routing`, which need a separate
222
+ * workflow signal) block the batch with 409 — bulk-resolving them would strand
223
+ * their workflows. Re-run/triage rows (workflow_type without any signal shape)
224
+ * resolve as bookkeeping acknowledgements, matching resolveByIds semantics.
225
+ */
226
+ async function resolveAllOrNone(input, auth) {
227
+ try {
228
+ const { items, metadata, requireClaimed } = input;
229
+ if (!Array.isArray(items) || items.length === 0) {
230
+ return { status: 400, error: 'items must be a non-empty array' };
231
+ }
232
+ if (items.length > defaults_1.ESCALATION_BULK_RESOLVE_MAX) {
233
+ return { status: 400, error: `items exceeds the maximum of ${defaults_1.ESCALATION_BULK_RESOLVE_MAX}` };
234
+ }
235
+ const wellFormed = items.every((i) => i && typeof i.id === 'string' && i.id.length > 0
236
+ && i.resolverPayload && typeof i.resolverPayload === 'object');
237
+ if (!wellFormed) {
238
+ return { status: 400, error: 'every item requires an id and a resolverPayload object' };
239
+ }
240
+ const ids = items.map((i) => i.id);
241
+ if (new Set(ids).size !== ids.length) {
242
+ return { status: 400, error: 'items must not repeat ids' };
243
+ }
244
+ // One indexed read serves every pre-flight gate below (scope, path shape,
245
+ // redaction schema). Role and signal routing are immutable per escalation,
246
+ // so none of these checks race the guarded statement that follows — and
247
+ // row STATE (pending, assignee) is re-asserted inside that statement.
248
+ const rows = await escalationService.getEscalationsByIds(ids);
249
+ const rowById = new Map(rows.map((r) => [r.id, r]));
250
+ // Per-item write scope, same rule as resolveByIds: write_all roles may
251
+ // resolve any item in the role; write_self only items assigned to the
252
+ // caller. A missing id or any out-of-scope item → 404 (non-disclosure),
253
+ // nothing resolved. Global bypasses; the store then reports precise
254
+ // per-row blockers (including not-found) via the 409 below.
255
+ const writeScope = await (0, helpers_1.getEscalationWriteScope)(auth.userId);
256
+ if (!writeScope.global) {
257
+ const allSet = new Set(writeScope.allRoles);
258
+ const selfSet = new Set(writeScope.selfRoles);
259
+ const writable = (r) => (!!r.role && allSet.has(r.role)) ||
260
+ (!!r.role && selfSet.has(r.role) && r.assigned_to === auth.userId);
261
+ if (rows.length !== ids.length || !rows.every(writable)) {
262
+ return { status: 404, error: 'One or more escalations not found' };
263
+ }
264
+ }
265
+ // Fail loud on rows the atomic statement cannot wake: legacy signal shapes
266
+ // resume their workflow via a separate handle.signal, so bulk-resolving the
267
+ // row would mark it settled while the workflow stays parked forever.
268
+ const unsupported = rows.filter((r) => {
269
+ const meta = (r.metadata ?? {});
270
+ return !r.signal_key && (meta.signal_id || meta.signal_routing?.signalId);
271
+ });
272
+ if (unsupported.length > 0) {
273
+ const failed = unsupported.map((r) => ({ id: r.id, reason: 'unsupported-resolution-path' }));
274
+ // error rides inside data too: routes serialize `data ?? { error }`, and
275
+ // the 409 body must carry BOTH the message and the blocking ids
276
+ const error = 'One or more escalations require the single-resolve path (legacy signal routing)';
277
+ return { status: 409, error, data: { error, failedIds: failed.map((f) => f.id), failed } };
278
+ }
279
+ // Per-row password redaction against each row's own form schema — the
280
+ // payload enters the signal store and the audit record; plaintext never.
281
+ const serviceItems = await Promise.all(items.map(async (item) => ({
282
+ id: item.id,
283
+ resolverPayload: await redactPasswords(item.resolverPayload, rowById.get(item.id)?.metadata?.form_schema),
284
+ })));
285
+ const result = await escalationService.resolveEscalationsAllOrNone(serviceItems, metadata, requireClaimed ? auth.userId : undefined);
286
+ if (!result.ok) {
287
+ const error = 'One or more escalations blocked the batch; nothing was resolved';
288
+ return {
289
+ status: 409,
290
+ error,
291
+ data: { error, failedIds: result.failed.map((f) => f.id), failed: result.failed },
292
+ };
293
+ }
294
+ return {
295
+ status: 200,
296
+ data: { resolved: result.escalations.length, escalationIds: result.escalations.map((e) => e.id) },
297
+ };
298
+ }
299
+ catch (err) {
300
+ return { status: 500, error: err.message };
301
+ }
302
+ }
205
303
  // ── Resolution paths ─────────────────────────────────────────────────────
206
304
  /** Path A: lightweight conditionLT signal — inject $escalation_id and signal the running workflow. */
207
305
  async function resolveViaConditionSignal(escalation, resolverPayload, metadata) {
@@ -86,6 +86,8 @@ export declare function updateRole(input: {
86
86
  /** The escalation resolve FORM schema (the JIT UI). Fields may carry x-lt-bind. */
87
87
  form_schema?: Record<string, any> | null;
88
88
  metadata_schema?: Record<string, any> | null;
89
+ /** The escalation LIST schema — rich formatting for this role's list page. */
90
+ list_schema?: Record<string, any> | null;
89
91
  properties?: Record<string, any> | null;
90
92
  ops_visible?: boolean;
91
93
  parent_role?: string | null;
@@ -129,6 +131,25 @@ export declare function getRoleSchema(input: {
129
131
  export declare function listRoleSchemaVersions(input: {
130
132
  role: string;
131
133
  }): Promise<LTApiResult>;
134
+ /**
135
+ * Fetch a role's LIST schema (independent version lineage from the form schema).
136
+ * With `version`, returns that immutable snapshot — a missing version is a 404.
137
+ * Without `version`, returns the live (latest) list schema with its version.
138
+ *
139
+ * @returns `{ status: 200, data: RoleListSchemaVersion }` on success
140
+ */
141
+ export declare function getRoleListSchema(input: {
142
+ role: string;
143
+ version?: number;
144
+ }): Promise<LTApiResult>;
145
+ /**
146
+ * List a role's LIST schema version history, newest first.
147
+ *
148
+ * @returns `{ status: 200, data: { versions: RoleListSchemaVersionSummary[] } }`
149
+ */
150
+ export declare function listRoleListSchemaVersions(input: {
151
+ role: string;
152
+ }): Promise<LTApiResult>;
132
153
  /**
133
154
  * Delete a role from the system. Requires admin privileges.
134
155
  *
@@ -44,6 +44,8 @@ exports.replaceEscalationTargets = replaceEscalationTargets;
44
44
  exports.updateRole = updateRole;
45
45
  exports.getRoleSchema = getRoleSchema;
46
46
  exports.listRoleSchemaVersions = listRoleSchemaVersions;
47
+ exports.getRoleListSchema = getRoleListSchema;
48
+ exports.listRoleListSchemaVersions = listRoleListSchemaVersions;
47
49
  exports.deleteRole = deleteRole;
48
50
  const roleService = __importStar(require("../services/role"));
49
51
  const facet_sql_1 = require("../services/escalation/facet-sql");
@@ -244,6 +246,7 @@ async function updateRole(input) {
244
246
  description: input.description,
245
247
  form_schema: input.form_schema,
246
248
  metadata_schema: input.metadata_schema,
249
+ list_schema: input.list_schema,
247
250
  properties: input.properties,
248
251
  ops_visible: input.ops_visible,
249
252
  parent_role: input.parent_role,
@@ -320,6 +323,53 @@ async function listRoleSchemaVersions(input) {
320
323
  return { status: 500, error: err.message };
321
324
  }
322
325
  }
326
+ /**
327
+ * Fetch a role's LIST schema (independent version lineage from the form schema).
328
+ * With `version`, returns that immutable snapshot — a missing version is a 404.
329
+ * Without `version`, returns the live (latest) list schema with its version.
330
+ *
331
+ * @returns `{ status: 200, data: RoleListSchemaVersion }` on success
332
+ */
333
+ async function getRoleListSchema(input) {
334
+ try {
335
+ if (!input.role) {
336
+ return { status: 400, error: 'role is required' };
337
+ }
338
+ if (input.version !== undefined && (!Number.isInteger(input.version) || input.version < 1)) {
339
+ return { status: 400, error: 'version must be a positive integer' };
340
+ }
341
+ const schema = await roleService.getRoleListSchema(input.role, input.version);
342
+ if (!schema) {
343
+ return {
344
+ status: 404,
345
+ error: input.version !== undefined
346
+ ? `List schema version ${input.version} not found for role '${input.role}'`
347
+ : `Role '${input.role}' not found`,
348
+ };
349
+ }
350
+ return { status: 200, data: schema };
351
+ }
352
+ catch (err) {
353
+ return { status: 500, error: err.message };
354
+ }
355
+ }
356
+ /**
357
+ * List a role's LIST schema version history, newest first.
358
+ *
359
+ * @returns `{ status: 200, data: { versions: RoleListSchemaVersionSummary[] } }`
360
+ */
361
+ async function listRoleListSchemaVersions(input) {
362
+ try {
363
+ if (!input.role) {
364
+ return { status: 400, error: 'role is required' };
365
+ }
366
+ const versions = await roleService.listRoleListSchemaVersions(input.role);
367
+ return { status: 200, data: { versions } };
368
+ }
369
+ catch (err) {
370
+ return { status: 500, error: err.message };
371
+ }
372
+ }
323
373
  /**
324
374
  * Delete a role from the system. Requires admin privileges.
325
375
  *
@@ -8,7 +8,18 @@ const config_1 = require("../../modules/config");
8
8
  let pool = null;
9
9
  function getPool() {
10
10
  if (!pool) {
11
- pool = new pg_1.Pool(config_1.postgres_options);
11
+ pool = new pg_1.Pool({
12
+ ...config_1.postgres_options,
13
+ // Long-tail's shared tables live in public. Postgres's default
14
+ // search_path is `"$user", public`: if any schema shares the connecting
15
+ // role's name (e.g. a HotMesh app schema named after the DB user), every
16
+ // unqualified statement — including CREATE TABLE in migrations — silently
17
+ // resolves there instead of public, forking the data across two schemas.
18
+ // Pinning the session search_path makes resolution independent of role
19
+ // and schema naming. HotMesh connections (getConnection) are unaffected:
20
+ // the engine fully qualifies its app schemas.
21
+ options: '-c search_path=public',
22
+ });
12
23
  }
13
24
  return pool;
14
25
  }
@@ -47,7 +47,34 @@ async function migrate() {
47
47
  // Uses a dedicated client so the lock is held for the entire sequence.
48
48
  const client = await pool.connect();
49
49
  try {
50
+ // Migrations define the shared public tables, so resolution is pinned on
51
+ // this client even if a future caller wires a pool without the pool-level
52
+ // search_path option. Without this, a schema named after the DB role
53
+ // (default search_path `"$user", public`) captures every unqualified
54
+ // CREATE TABLE below.
55
+ await client.query('SET search_path TO public');
50
56
  await client.query('SELECT pg_advisory_lock(8675309)');
57
+ // Fail loudly on a pre-existing split: an lt_migrations table in any other
58
+ // schema means a prior boot ran with `"$user"` shadowing public and forked
59
+ // the tables. Booting through it would either fork the data further or
60
+ // re-run every migration against empty public tables while the live rows
61
+ // sit in the shadow schema. An operator must consolidate first (move the
62
+ // live lt_* tables to public, archive the stale copies).
63
+ const drift = await client.query(`
64
+ SELECT n.nspname
65
+ FROM pg_class c
66
+ JOIN pg_namespace n ON n.oid = c.relnamespace
67
+ WHERE c.relname = 'lt_migrations' AND c.relkind = 'r' AND n.nspname <> 'public'
68
+ `);
69
+ if (drift.rows.length > 0) {
70
+ const schemas = drift.rows.map((r) => r.nspname).join(', ');
71
+ throw new Error(`[migrate] lt_migrations exists in non-public schema(s): ${schemas}. ` +
72
+ 'Long-tail tables must live in the public schema. This usually means the ' +
73
+ 'database user shares a name with another schema, so the default ' +
74
+ 'search_path ("$user", public) captured earlier migrations. Consolidate ' +
75
+ 'the schemas (ALTER TABLE ... SET SCHEMA public for the live lt_* tables, ' +
76
+ 'archive stale public copies) before starting the app.');
77
+ }
51
78
  // ensure migration tracking table
52
79
  await client.query(`
53
80
  CREATE TABLE IF NOT EXISTS lt_migrations (
@@ -0,0 +1,27 @@
1
+ -- Migration 021: Role escalations LIST schema
2
+ --
3
+ -- list_schema is the list-page analog of form_schema: it richly formats the
4
+ -- escalation LIST page when the list is scoped to exactly one role that owns a
5
+ -- list_schema (opt-in — absent leaves the engineer table unchanged). Unlike
6
+ -- form_schema it is NOT pinned per-escalation; the list always renders the
7
+ -- role's latest.
8
+ --
9
+ -- It versions INDEPENDENTLY of form_schema / metadata_schema: its own snapshot
10
+ -- table (lt_role_list_schemas) and its own counter (current_list_schema_version),
11
+ -- so editing the list view never advances the resolve form's version. The write
12
+ -- rides the same atomic UPDATE_ROLE_METADATA statement (services/role/sql.ts),
13
+ -- which bumps current_list_schema_version and appends a snapshot only when the
14
+ -- provided list_schema actually differs.
15
+
16
+ ALTER TABLE lt_roles
17
+ ADD COLUMN IF NOT EXISTS list_schema JSONB,
18
+ ADD COLUMN IF NOT EXISTS current_list_schema_version INTEGER;
19
+
20
+ CREATE TABLE IF NOT EXISTS lt_role_list_schemas (
21
+ role TEXT NOT NULL REFERENCES lt_roles(role) ON DELETE CASCADE,
22
+ version INTEGER NOT NULL,
23
+ list_schema JSONB,
24
+ change_summary TEXT,
25
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
26
+ PRIMARY KEY (role, version)
27
+ );
@@ -49,6 +49,8 @@ export declare const QUERY_LIMIT_MAX: number;
49
49
  export declare const YAML_LIST_LIMIT: number;
50
50
  /** Default page size for YAML workflow version history. */
51
51
  export declare const YAML_VERSION_LIMIT: number;
52
+ /** Maximum items per atomic bulk resolve (POST /escalations/resolve-all-or-none). */
53
+ export declare const ESCALATION_BULK_RESOLVE_MAX: number;
52
54
  /**
53
55
  * Claim duration presets shown in the dashboard (minutes).
54
56
  * Override via JSON array, e.g. LT_CLAIM_DURATION_OPTIONS='[15,30,60,480]'
@@ -9,7 +9,7 @@
9
9
  * Grouped by domain so related knobs live together.
10
10
  */
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
- exports.YAML_LINE_WIDTH = exports.STOP_WORDS = exports.CLAIM_DURATION_OPTIONS = exports.YAML_VERSION_LIMIT = exports.YAML_LIST_LIMIT = exports.QUERY_LIMIT_MAX = exports.QUERY_LIMIT_DEFAULT = exports.CONFIG_CACHE_TTL_MS = exports.MCP_TOOL_TIMEOUT_MS = exports.WORKFLOW_SYNC_TIMEOUT_MS = exports.JOB_EXPIRE_SECS = exports.WORKFLOW_EXPIRE_SECS = exports.TOOL_ARG_LIMIT_CAP = exports.TOOL_ROUNDS_MCP_QUERY = exports.TOOL_ROUNDS_TRIAGE = exports.TOOL_RESULT_MAX_CHARS = exports.LLM_MAX_INPUT_CHARS = exports.LLM_MAX_ARRAY_ITEMS = exports.LLM_MAX_TOKENS_VISION = exports.LLM_MAX_TOKENS_JSON = exports.LLM_MAX_TOKENS_DEFAULT = exports.LLM_BASE_URL = exports.LLM_MODEL_SECONDARY = exports.LLM_MODEL_PRIMARY = void 0;
12
+ exports.YAML_LINE_WIDTH = exports.STOP_WORDS = exports.CLAIM_DURATION_OPTIONS = exports.ESCALATION_BULK_RESOLVE_MAX = exports.YAML_VERSION_LIMIT = exports.YAML_LIST_LIMIT = exports.QUERY_LIMIT_MAX = exports.QUERY_LIMIT_DEFAULT = exports.CONFIG_CACHE_TTL_MS = exports.MCP_TOOL_TIMEOUT_MS = exports.WORKFLOW_SYNC_TIMEOUT_MS = exports.JOB_EXPIRE_SECS = exports.WORKFLOW_EXPIRE_SECS = exports.TOOL_ARG_LIMIT_CAP = exports.TOOL_ROUNDS_MCP_QUERY = exports.TOOL_ROUNDS_TRIAGE = exports.TOOL_RESULT_MAX_CHARS = exports.LLM_MAX_INPUT_CHARS = exports.LLM_MAX_ARRAY_ITEMS = exports.LLM_MAX_TOKENS_VISION = exports.LLM_MAX_TOKENS_JSON = exports.LLM_MAX_TOKENS_DEFAULT = exports.LLM_BASE_URL = exports.LLM_MODEL_SECONDARY = exports.LLM_MODEL_PRIMARY = void 0;
13
13
  function envInt(key, fallback) {
14
14
  const v = process.env[key];
15
15
  if (v === undefined || v === '')
@@ -67,6 +67,8 @@ exports.YAML_LIST_LIMIT = envInt('LT_YAML_LIST_LIMIT', 50);
67
67
  /** Default page size for YAML workflow version history. */
68
68
  exports.YAML_VERSION_LIMIT = envInt('LT_YAML_VERSION_LIMIT', 20);
69
69
  // ── Escalation ──────────────────────────────────────────────────────
70
+ /** Maximum items per atomic bulk resolve (POST /escalations/resolve-all-or-none). */
71
+ exports.ESCALATION_BULK_RESOLVE_MAX = envInt('LT_ESCALATION_BULK_RESOLVE_MAX', 100);
70
72
  /**
71
73
  * Claim duration presets shown in the dashboard (minutes).
72
74
  * Override via JSON array, e.g. LT_CLAIM_DURATION_OPTIONS='[15,30,60,480]'
@@ -66,6 +66,24 @@ function registerResolveRoutes(router) {
66
66
  }, req.auth);
67
67
  res.status(result.status).json(result.data ?? { error: result.error });
68
68
  });
69
+ /**
70
+ * POST /api/escalations/resolve-all-or-none
71
+ * Atomic bulk resolve with per-row payloads: every listed escalation resolves
72
+ * with its own resolverPayload in one statement, or nothing resolves. Rows
73
+ * backing a live condition() waiter are woken with their own payload (same
74
+ * wake contract as /:id/resolve). 409 returns { error, failedIds, failed }
75
+ * naming exactly the rows that blocked the batch. Max 100 items per call.
76
+ * Body: { items: Array<{ id: string, resolverPayload: Record<string, any> }>,
77
+ * metadata?: Record<string, any>, requireClaimed?: boolean }
78
+ */
79
+ router.post('/resolve-all-or-none', async (req, res) => {
80
+ const result = await api.resolveAllOrNone({
81
+ items: req.body?.items,
82
+ metadata: req.body?.metadata,
83
+ requireClaimed: req.body?.requireClaimed,
84
+ }, req.auth);
85
+ res.status(result.status).json(result.data ?? { error: result.error });
86
+ });
69
87
  /**
70
88
  * POST /api/escalations/:id/resolve
71
89
  * Resolve a pending escalation with a human-provided payload. Routes by
@@ -123,6 +123,26 @@ router.get('/:role/schema/versions', async (req, res) => {
123
123
  const result = await api.listRoleSchemaVersions({ role: req.params.role });
124
124
  res.status(result.status).json(result.data ?? { error: result.error });
125
125
  });
126
+ /**
127
+ * GET /api/roles/:role/list-schema
128
+ * Fetch the role's LIST schema (rich list-page formatting). `?version=N` pins a
129
+ * snapshot; omitted, the live (latest) list schema is returned with its version.
130
+ * Versions independently from the resolve form schema.
131
+ */
132
+ router.get('/:role/list-schema', async (req, res) => {
133
+ const versionParam = req.query.version;
134
+ const version = versionParam !== undefined ? Number(versionParam) : undefined;
135
+ const result = await api.getRoleListSchema({ role: req.params.role, version });
136
+ res.status(result.status).json(result.data ?? { error: result.error });
137
+ });
138
+ /**
139
+ * GET /api/roles/:role/list-schema/versions
140
+ * List the role's LIST schema version history (newest first).
141
+ */
142
+ router.get('/:role/list-schema/versions', async (req, res) => {
143
+ const result = await api.listRoleListSchemaVersions({ role: req.params.role });
144
+ res.status(result.status).json(result.data ?? { error: result.error });
145
+ });
126
146
  /**
127
147
  * GET /api/roles/:role/escalation-targets
128
148
  * Get allowed escalation targets for a specific role.
@@ -153,6 +153,14 @@ export declare function createClient(options?: LTClientOptions): {
153
153
  resolverPayload: Record<string, any>;
154
154
  metadata?: Record<string, any>;
155
155
  }, auth?: LTApiAuth) => Promise<LTApiResult<any>>;
156
+ resolveAllOrNone: (input: {
157
+ items: Array<{
158
+ id: string;
159
+ resolverPayload: Record<string, any>;
160
+ }>;
161
+ metadata?: Record<string, any>;
162
+ requireClaimed?: boolean;
163
+ }, auth?: LTApiAuth) => Promise<LTApiResult<any>>;
156
164
  releaseExpired: typeof escalationsApi.releaseExpiredClaims;
157
165
  updatePriority: (input: {
158
166
  ids: string[];
@@ -128,6 +128,7 @@ function createClient(options = {}) {
128
128
  resolve: bindAuth(escalationsApi.resolveEscalation, auth),
129
129
  resolveBySignalKey: bindAuth(escalationsApi.resolveBySignalKey, auth),
130
130
  resolveByIds: bindAuth(escalationsApi.resolveByIds, auth),
131
+ resolveAllOrNone: bindAuth(escalationsApi.resolveAllOrNone, auth),
131
132
  releaseExpired: escalationsApi.releaseExpiredClaims,
132
133
  updatePriority: bindAuth(escalationsApi.updatePriority, auth),
133
134
  bulkClaim: bindAuth(escalationsApi.bulkClaim, auth),
@@ -36,6 +36,42 @@ export declare function resolveEscalation(id: string, resolverPayload: Record<st
36
36
  * `resolveEscalation` (which delivers the signal).
37
37
  */
38
38
  export declare function resolveEscalationsByIds(ids: string[], resolverPayload: Record<string, any>, metadata?: Record<string, any>): Promise<LTEscalationRecord[]>;
39
+ /**
40
+ * Resolve a SET of escalations atomically — every listed row resolves, each
41
+ * with its OWN resolverPayload, or none do. One SQL statement locks the rows
42
+ * in deterministic id order, applies per-row payloads, and commits each
43
+ * waiter's wake WITH its resolve. Unlike {@link resolveEscalationsByIds},
44
+ * rows backing a live `condition()` waiter are first-class here: each is
45
+ * woken with its own payload as the condition's return value.
46
+ *
47
+ * On failure NOTHING is written and `failed` names exactly the rows that
48
+ * blocked the batch (`not-found`, `already-resolved`, `already-cancelled`,
49
+ * `already-expired`, `assignee-mismatch`) — rows that were themselves
50
+ * resolvable stay pending and are not listed.
51
+ *
52
+ * `assertAssignee` (optional) additionally requires every row to be assigned
53
+ * to that principal, asserted inside the same guarded statement — closing the
54
+ * claim-race window for claim-then-resolve flows.
55
+ */
56
+ export declare function resolveEscalationsAllOrNone(items: Array<{
57
+ id: string;
58
+ resolverPayload: Record<string, any>;
59
+ }>, metadata?: Record<string, any>, assertAssignee?: string): Promise<{
60
+ ok: true;
61
+ escalations: LTEscalationRecord[];
62
+ } | {
63
+ ok: false;
64
+ failed: Array<{
65
+ id: string;
66
+ reason: string;
67
+ }>;
68
+ }>;
69
+ /**
70
+ * Fetch full records for a set of escalation IDs in one indexed query.
71
+ * Used by bulk orchestrators that need per-row context beyond the scope
72
+ * fields — signal routing shape, `metadata.form_schema` for redaction.
73
+ */
74
+ export declare function getEscalationsByIds(ids: string[]): Promise<LTEscalationRecord[]>;
39
75
  /**
40
76
  * Look up an efficient (atomic) escalation by its `signal_key` — the signal id
41
77
  * passed to `conditionLT(signalId, config)` / `condition(signalId, config)`.
@@ -4,6 +4,8 @@ exports.createEscalation = createEscalation;
4
4
  exports.claimEscalation = claimEscalation;
5
5
  exports.resolveEscalation = resolveEscalation;
6
6
  exports.resolveEscalationsByIds = resolveEscalationsByIds;
7
+ exports.resolveEscalationsAllOrNone = resolveEscalationsAllOrNone;
8
+ exports.getEscalationsByIds = getEscalationsByIds;
7
9
  exports.getEscalationBySignalKey = getEscalationBySignalKey;
8
10
  exports.resolveEscalationBySignalKey = resolveEscalationBySignalKey;
9
11
  exports.cancelEscalation = cancelEscalation;
@@ -165,6 +167,57 @@ async function resolveEscalationsByIds(ids, resolverPayload, metadata) {
165
167
  }
166
168
  return records;
167
169
  }
170
+ /**
171
+ * Resolve a SET of escalations atomically — every listed row resolves, each
172
+ * with its OWN resolverPayload, or none do. One SQL statement locks the rows
173
+ * in deterministic id order, applies per-row payloads, and commits each
174
+ * waiter's wake WITH its resolve. Unlike {@link resolveEscalationsByIds},
175
+ * rows backing a live `condition()` waiter are first-class here: each is
176
+ * woken with its own payload as the condition's return value.
177
+ *
178
+ * On failure NOTHING is written and `failed` names exactly the rows that
179
+ * blocked the batch (`not-found`, `already-resolved`, `already-cancelled`,
180
+ * `already-expired`, `assignee-mismatch`) — rows that were themselves
181
+ * resolvable stay pending and are not listed.
182
+ *
183
+ * `assertAssignee` (optional) additionally requires every row to be assigned
184
+ * to that principal, asserted inside the same guarded statement — closing the
185
+ * claim-race window for claim-then-resolve flows.
186
+ */
187
+ async function resolveEscalationsAllOrNone(items, metadata, assertAssignee) {
188
+ if (items.length === 0)
189
+ return { ok: true, escalations: [] };
190
+ const client = await (0, client_1.escalations)();
191
+ const result = await client.resolveAllOrNone({ items, metadata, assertAssignee });
192
+ if (!result.ok)
193
+ return { ok: false, failed: result.failed };
194
+ const records = (0, map_1.toEscalationRecords)(result.entries);
195
+ for (const escalation of records) {
196
+ publishEscalationChange({
197
+ type: 'escalation.resolved',
198
+ source: 'service',
199
+ workflowId: escalation.workflow_id || '',
200
+ workflowName: escalation.workflow_type || '',
201
+ taskQueue: escalation.task_queue || '',
202
+ escalationId: escalation.id,
203
+ status: 'resolved',
204
+ data: {},
205
+ });
206
+ }
207
+ return { ok: true, escalations: records };
208
+ }
209
+ /**
210
+ * Fetch full records for a set of escalation IDs in one indexed query.
211
+ * Used by bulk orchestrators that need per-row context beyond the scope
212
+ * fields — signal routing shape, `metadata.form_schema` for redaction.
213
+ */
214
+ async function getEscalationsByIds(ids) {
215
+ if (ids.length === 0)
216
+ return [];
217
+ const client = await (0, client_1.escalations)();
218
+ const rows = await client.list({ ids, limit: LOOKUP_LIMIT });
219
+ return (0, map_1.toEscalationRecords)(rows);
220
+ }
168
221
  /**
169
222
  * Look up an efficient (atomic) escalation by its `signal_key` — the signal id
170
223
  * passed to `conditionLT(signalId, config)` / `condition(signalId, config)`.
@@ -38,6 +38,7 @@ const hotmesh_1 = require("@hotmeshio/hotmesh");
38
38
  const db_1 = require("../../lib/db");
39
39
  const logger_1 = require("../../lib/logger");
40
40
  const dbaService = __importStar(require("../dba"));
41
+ const client_1 = require("../escalation/client");
41
42
  const CRON_TOPIC = 'lt.maintenance.prune';
42
43
  const CRON_ID = 'lt-maintenance-nightly';
43
44
  /**
@@ -84,6 +85,27 @@ async function executeRule(appId, rule) {
84
85
  streams: false,
85
86
  });
86
87
  break;
88
+ // Age out terminal escalation rows (resolved/cancelled/expired). The
89
+ // engine-owned prune deletes at most one batch per call in one atomic
90
+ // statement; loop until the horizon is drained. Pending rows and live
91
+ // condition() waiters are never touched — every engine state transition
92
+ // guards on status='pending', so terminal rows are inert audit records.
93
+ case rule.target === 'escalations' && rule.action === 'delete': {
94
+ const client = await (0, client_1.escalations)();
95
+ let deleted = 0;
96
+ let batch;
97
+ do {
98
+ ({ deleted: batch } = await client.prune({
99
+ olderThan: rule.olderThan,
100
+ statuses: rule.statuses,
101
+ }));
102
+ deleted += batch;
103
+ } while (batch > 0);
104
+ if (deleted > 0) {
105
+ logger_1.loggerRegistry.info(`[lt-maintenance] escalations pruned: ${deleted} (olderThan: ${rule.olderThan})`);
106
+ }
107
+ break;
108
+ }
87
109
  default:
88
110
  logger_1.loggerRegistry.warn(`[lt-maintenance] unknown rule, skipping: ${JSON.stringify(rule)}`);
89
111
  }
@@ -1,4 +1,4 @@
1
- import type { EscalationChain, RoleDetail, RoleSchemaVersion, RoleSchemaVersionSummary, UpdateRoleInput } from './types';
1
+ import type { EscalationChain, RoleDetail, RoleSchemaVersion, RoleSchemaVersionSummary, RoleListSchemaVersion, RoleListSchemaVersionSummary, UpdateRoleInput } from './types';
2
2
  /**
3
3
  * Get the roles a given source role can escalate to.
4
4
  */
@@ -60,6 +60,18 @@ export declare function listRoleSchemaVersions(role: string): Promise<RoleSchema
60
60
  * Returns null when the role (or the requested version) does not exist.
61
61
  */
62
62
  export declare function getRoleSchema(role: string, version?: number): Promise<RoleSchemaVersion | null>;
63
+ /**
64
+ * List the list-schema version history for a role (newest first). The schema is
65
+ * elided — presence flag only; fetch a full snapshot via getRoleListSchema.
66
+ */
67
+ export declare function listRoleListSchemaVersions(role: string): Promise<RoleListSchemaVersionSummary[]>;
68
+ /**
69
+ * Fetch a role's list schema. With a version, reads the immutable snapshot from
70
+ * lt_role_list_schemas; without one, the live (latest) column — a role that has
71
+ * never versioned its list schema still answers with version null. Returns null
72
+ * when the role (or the requested version) does not exist.
73
+ */
74
+ export declare function getRoleListSchema(role: string, version?: number): Promise<RoleListSchemaVersion | null>;
63
75
  /**
64
76
  * Fetch a role's live form_schema — the escalation form the dashboard renders.
65
77
  */