@hotmeshio/long-tail 0.8.3 → 0.8.5

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 (217) hide show
  1. package/build/api/escalations/create.js +19 -60
  2. package/build/api/escalations/index.d.ts +1 -1
  3. package/build/api/escalations/index.js +2 -1
  4. package/build/api/escalations/resolve.d.ts +28 -0
  5. package/build/api/escalations/resolve.js +110 -4
  6. package/build/api/escalations/single.js +8 -4
  7. package/build/api/roles.d.ts +1 -0
  8. package/build/lib/db/index.js +12 -1
  9. package/build/lib/db/migrate.js +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/scripts/twin-farm.d.ts +12 -13
  14. package/build/scripts/twin-farm.js +61 -75
  15. package/build/sdk/index.d.ts +8 -0
  16. package/build/sdk/index.js +1 -0
  17. package/build/services/escalation/crud.d.ts +36 -0
  18. package/build/services/escalation/crud.js +53 -0
  19. package/build/services/escalation/queries.d.ts +13 -0
  20. package/build/services/escalation/queries.js +27 -0
  21. package/build/services/maintenance/index.js +22 -0
  22. package/build/services/orchestrator/condition.d.ts +18 -12
  23. package/build/services/orchestrator/condition.js +11 -8
  24. package/build/services/role/index.d.ts +1 -2
  25. package/build/services/role/index.js +1 -2
  26. package/build/services/role/schema-validator.d.ts +9 -0
  27. package/build/services/role/schema-validator.js +42 -0
  28. package/build/services/role/sql.d.ts +1 -1
  29. package/build/services/role/sql.js +2 -2
  30. package/build/services/role/types.d.ts +8 -6
  31. package/build/system/mcp-servers/admin/schemas.js +4 -2
  32. package/build/system/mcp-servers/human-queue-schemas.js +6 -2
  33. package/build/system/mcp-servers/human-queue.js +11 -3
  34. package/build/tsconfig.tsbuildinfo +1 -1
  35. package/build/types/config.d.ts +2 -0
  36. package/build/types/maintenance.d.ts +6 -1
  37. package/build/types/startup.d.ts +5 -1
  38. package/dashboard/dist/assets/{AdminDashboard-Dhr7sUq6.js → AdminDashboard-RrSvb8d-.js} +2 -2
  39. package/dashboard/dist/assets/{AdminDashboard-Dhr7sUq6.js.map → AdminDashboard-RrSvb8d-.js.map} +1 -1
  40. package/dashboard/dist/assets/{AgentConfigPage-CUPm8FjH.js → AgentConfigPage-5RMdI0ku.js} +2 -2
  41. package/dashboard/dist/assets/{AgentConfigPage-CUPm8FjH.js.map → AgentConfigPage-5RMdI0ku.js.map} +1 -1
  42. package/dashboard/dist/assets/{AgentDetailPage-C9Tmmhhc.js → AgentDetailPage-C_-sIBcr.js} +2 -2
  43. package/dashboard/dist/assets/{AgentDetailPage-C9Tmmhhc.js.map → AgentDetailPage-C_-sIBcr.js.map} +1 -1
  44. package/dashboard/dist/assets/{AgentsPage-Pj47ZJ2U.js → AgentsPage-CxNNtSsf.js} +2 -2
  45. package/dashboard/dist/assets/{AgentsPage-Pj47ZJ2U.js.map → AgentsPage-CxNNtSsf.js.map} +1 -1
  46. package/dashboard/dist/assets/{AvailableEscalationsPage-BxakNqha.js → AvailableEscalationsPage-BcbhA6NE.js} +2 -2
  47. package/dashboard/dist/assets/{AvailableEscalationsPage-BxakNqha.js.map → AvailableEscalationsPage-BcbhA6NE.js.map} +1 -1
  48. package/dashboard/dist/assets/{BotPicker-CCIo5ic0.js → BotPicker-BQVz6bye.js} +2 -2
  49. package/dashboard/dist/assets/{BotPicker-CCIo5ic0.js.map → BotPicker-BQVz6bye.js.map} +1 -1
  50. package/dashboard/dist/assets/{CapabilitiesPage-XFVq4iPX.js → CapabilitiesPage-DlZ8SgO3.js} +2 -2
  51. package/dashboard/dist/assets/{CapabilitiesPage-XFVq4iPX.js.map → CapabilitiesPage-DlZ8SgO3.js.map} +1 -1
  52. package/dashboard/dist/assets/{CollapsibleSection-CBKICsCu.js → CollapsibleSection-lo8Qx4zb.js} +2 -2
  53. package/dashboard/dist/assets/{CollapsibleSection-CBKICsCu.js.map → CollapsibleSection-lo8Qx4zb.js.map} +1 -1
  54. package/dashboard/dist/assets/{CredentialsPage-BN_d7ybc.js → CredentialsPage-DJ9DyXsO.js} +2 -2
  55. package/dashboard/dist/assets/{CredentialsPage-BN_d7ybc.js.map → CredentialsPage-DJ9DyXsO.js.map} +1 -1
  56. package/dashboard/dist/assets/{CronLabel-4V_EXMUg.js → CronLabel-o0dbisw1.js} +2 -2
  57. package/dashboard/dist/assets/{CronLabel-4V_EXMUg.js.map → CronLabel-o0dbisw1.js.map} +1 -1
  58. package/dashboard/dist/assets/{CustomDurationPicker-BdurQj-B.js → CustomDurationPicker-CGiwKSVJ.js} +2 -2
  59. package/dashboard/dist/assets/{CustomDurationPicker-BdurQj-B.js.map → CustomDurationPicker-CGiwKSVJ.js.map} +1 -1
  60. package/dashboard/dist/assets/{ElapsedCell-CO8XMSJX.js → ElapsedCell-N22STQcg.js} +2 -2
  61. package/dashboard/dist/assets/{ElapsedCell-CO8XMSJX.js.map → ElapsedCell-N22STQcg.js.map} +1 -1
  62. package/dashboard/dist/assets/EscalationSchemaPage-Zsmewko2.js +9 -0
  63. package/dashboard/dist/assets/EscalationSchemaPage-Zsmewko2.js.map +1 -0
  64. package/dashboard/dist/assets/{EscalationsOverview-qlo0mxxa.js → EscalationsOverview-CE2p70Ct.js} +2 -2
  65. package/dashboard/dist/assets/{EscalationsOverview-qlo0mxxa.js.map → EscalationsOverview-CE2p70Ct.js.map} +1 -1
  66. package/dashboard/dist/assets/{EventTable-D9RjAT_a.js → EventTable-B3Gj7Ayg.js} +2 -2
  67. package/dashboard/dist/assets/{EventTable-D9RjAT_a.js.map → EventTable-B3Gj7Ayg.js.map} +1 -1
  68. package/dashboard/dist/assets/{GraphInvokePage-CU3r2tzN.js → GraphInvokePage-DywWVSdj.js} +2 -2
  69. package/dashboard/dist/assets/{GraphInvokePage-CU3r2tzN.js.map → GraphInvokePage-DywWVSdj.js.map} +1 -1
  70. package/dashboard/dist/assets/{HomePage-B7pb4-Gx.js → HomePage-CxtBiULd.js} +2 -2
  71. package/dashboard/dist/assets/{HomePage-B7pb4-Gx.js.map → HomePage-CxtBiULd.js.map} +1 -1
  72. package/dashboard/dist/assets/{ListToolbar-C_64Y22Z.js → ListToolbar-Ok2O0wmC.js} +2 -2
  73. package/dashboard/dist/assets/{ListToolbar-C_64Y22Z.js.map → ListToolbar-Ok2O0wmC.js.map} +1 -1
  74. package/dashboard/dist/assets/{McpOverview-D-4eSOTc.js → McpOverview-CTCYZXIp.js} +2 -2
  75. package/dashboard/dist/assets/{McpOverview-D-4eSOTc.js.map → McpOverview-CTCYZXIp.js.map} +1 -1
  76. package/dashboard/dist/assets/{McpQueryDetailPage-BYwDb8LC.js → McpQueryDetailPage-BOaXf0bZ.js} +2 -2
  77. package/dashboard/dist/assets/{McpQueryDetailPage-BYwDb8LC.js.map → McpQueryDetailPage-BOaXf0bZ.js.map} +1 -1
  78. package/dashboard/dist/assets/{McpQueryPage-CIijB5t2.js → McpQueryPage-BbYojoHu.js} +2 -2
  79. package/dashboard/dist/assets/{McpQueryPage-CIijB5t2.js.map → McpQueryPage-BbYojoHu.js.map} +1 -1
  80. package/dashboard/dist/assets/{McpRunDetailPage-CQmhfgju.js → McpRunDetailPage-CXGCVmPq.js} +2 -2
  81. package/dashboard/dist/assets/{McpRunDetailPage-CQmhfgju.js.map → McpRunDetailPage-CXGCVmPq.js.map} +1 -1
  82. package/dashboard/dist/assets/{McpRunsPage-CdDyeOdi.js → McpRunsPage-Df1GqxUg.js} +2 -2
  83. package/dashboard/dist/assets/{McpRunsPage-CdDyeOdi.js.map → McpRunsPage-Df1GqxUg.js.map} +1 -1
  84. package/dashboard/dist/assets/OperationsPage-DgZYi-BC.js +2 -0
  85. package/dashboard/dist/assets/OperationsPage-DgZYi-BC.js.map +1 -0
  86. package/dashboard/dist/assets/{OperatorDashboard-DFORmS46.js → OperatorDashboard-DIR7XDeD.js} +2 -2
  87. package/dashboard/dist/assets/{OperatorDashboard-DFORmS46.js.map → OperatorDashboard-DIR7XDeD.js.map} +1 -1
  88. package/dashboard/dist/assets/{ProcessDetailPage-DwypVFE2.js → ProcessDetailPage-BuSi654J.js} +2 -2
  89. package/dashboard/dist/assets/{ProcessDetailPage-DwypVFE2.js.map → ProcessDetailPage-BuSi654J.js.map} +1 -1
  90. package/dashboard/dist/assets/{ProcessesListPage-tNLvALwT.js → ProcessesListPage-Dhik7XpW.js} +2 -2
  91. package/dashboard/dist/assets/{ProcessesListPage-tNLvALwT.js.map → ProcessesListPage-Dhik7XpW.js.map} +1 -1
  92. package/dashboard/dist/assets/RoleDetailPage-BfzqQxMU.js +8 -0
  93. package/dashboard/dist/assets/RoleDetailPage-BfzqQxMU.js.map +1 -0
  94. package/dashboard/dist/assets/{RolesPage-txueySPI.js → RolesPage-BRicD5mx.js} +2 -2
  95. package/dashboard/dist/assets/{RolesPage-txueySPI.js.map → RolesPage-BRicD5mx.js.map} +1 -1
  96. package/dashboard/dist/assets/{RunAsSelector-Bvefon9B.js → RunAsSelector-DipyBf_T.js} +2 -2
  97. package/dashboard/dist/assets/{RunAsSelector-Bvefon9B.js.map → RunAsSelector-DipyBf_T.js.map} +1 -1
  98. package/dashboard/dist/assets/{StickyPagination-CeQCzVIA.js → StickyPagination-CD4W450h.js} +2 -2
  99. package/dashboard/dist/assets/{StickyPagination-CeQCzVIA.js.map → StickyPagination-CD4W450h.js.map} +1 -1
  100. package/dashboard/dist/assets/{StreamMessageDetail-6wEm6o6t.js → StreamMessageDetail-BbDdWDUY.js} +2 -2
  101. package/dashboard/dist/assets/{StreamMessageDetail-6wEm6o6t.js.map → StreamMessageDetail-BbDdWDUY.js.map} +1 -1
  102. package/dashboard/dist/assets/{SwimlaneTimeline-C97Rldf5.js → SwimlaneTimeline-Bktv2vi5.js} +2 -2
  103. package/dashboard/dist/assets/{SwimlaneTimeline-C97Rldf5.js.map → SwimlaneTimeline-Bktv2vi5.js.map} +1 -1
  104. package/dashboard/dist/assets/{TaskDetailPage-B09mLOCE.js → TaskDetailPage-pSU3xuG9.js} +2 -2
  105. package/dashboard/dist/assets/{TaskDetailPage-B09mLOCE.js.map → TaskDetailPage-pSU3xuG9.js.map} +1 -1
  106. package/dashboard/dist/assets/{TasksListPage-CQhiSGkN.js → TasksListPage-M6ok-Nfd.js} +2 -2
  107. package/dashboard/dist/assets/{TasksListPage-CQhiSGkN.js.map → TasksListPage-M6ok-Nfd.js.map} +1 -1
  108. package/dashboard/dist/assets/{TimeAgo-BkyeBcMF.js → TimeAgo-DVWhRNbi.js} +2 -2
  109. package/dashboard/dist/assets/{TimeAgo-BkyeBcMF.js.map → TimeAgo-DVWhRNbi.js.map} +1 -1
  110. package/dashboard/dist/assets/{TimestampCell-DVp_UdEB.js → TimestampCell-rG7pmIkW.js} +2 -2
  111. package/dashboard/dist/assets/{TimestampCell-DVp_UdEB.js.map → TimestampCell-rG7pmIkW.js.map} +1 -1
  112. package/dashboard/dist/assets/{ToolTestPanel-CZOuRP_i.js → ToolTestPanel-CLcMA8OE.js} +2 -2
  113. package/dashboard/dist/assets/{ToolTestPanel-CZOuRP_i.js.map → ToolTestPanel-CLcMA8OE.js.map} +1 -1
  114. package/dashboard/dist/assets/{TopicDetailPage-0mvQde8R.js → TopicDetailPage-C9gvoWYu.js} +2 -2
  115. package/dashboard/dist/assets/{TopicDetailPage-0mvQde8R.js.map → TopicDetailPage-C9gvoWYu.js.map} +1 -1
  116. package/dashboard/dist/assets/{TopicsPage-DkvsHLLe.js → TopicsPage-CIGi3TdT.js} +2 -2
  117. package/dashboard/dist/assets/{TopicsPage-DkvsHLLe.js.map → TopicsPage-CIGi3TdT.js.map} +1 -1
  118. package/dashboard/dist/assets/{UserName-EaD_Tz9y.js → UserName-CifbOahk.js} +2 -2
  119. package/dashboard/dist/assets/{UserName-EaD_Tz9y.js.map → UserName-CifbOahk.js.map} +1 -1
  120. package/dashboard/dist/assets/{WorkflowExecutionPage-CjMSQ9Bb.js → WorkflowExecutionPage-BR0oVx_p.js} +2 -2
  121. package/dashboard/dist/assets/{WorkflowExecutionPage-CjMSQ9Bb.js.map → WorkflowExecutionPage-BR0oVx_p.js.map} +1 -1
  122. package/dashboard/dist/assets/{WorkflowsDashboard-BgUz_jO6.js → WorkflowsDashboard-CSYrYQsO.js} +2 -2
  123. package/dashboard/dist/assets/{WorkflowsDashboard-BgUz_jO6.js.map → WorkflowsDashboard-CSYrYQsO.js.map} +1 -1
  124. package/dashboard/dist/assets/{WorkflowsOverview-BzUjUfAz.js → WorkflowsOverview-CaXWYZuw.js} +2 -2
  125. package/dashboard/dist/assets/{WorkflowsOverview-BzUjUfAz.js.map → WorkflowsOverview-CaXWYZuw.js.map} +1 -1
  126. package/dashboard/dist/assets/{YamlWorkflowDetailPage-C48Ui513.js → YamlWorkflowDetailPage-CRBXahFJ.js} +2 -2
  127. package/dashboard/dist/assets/{YamlWorkflowDetailPage-C48Ui513.js.map → YamlWorkflowDetailPage-CRBXahFJ.js.map} +1 -1
  128. package/dashboard/dist/assets/{YamlWorkflowsPage-CQ-dk84N.js → YamlWorkflowsPage-D5zc8Lta.js} +2 -2
  129. package/dashboard/dist/assets/{YamlWorkflowsPage-CQ-dk84N.js.map → YamlWorkflowsPage-D5zc8Lta.js.map} +1 -1
  130. package/dashboard/dist/assets/{agents-O1GlQE9p.js → agents-D7vYXeLj.js} +2 -2
  131. package/dashboard/dist/assets/{agents-O1GlQE9p.js.map → agents-D7vYXeLj.js.map} +1 -1
  132. package/dashboard/dist/assets/{bots-DB5Z7MEk.js → bots-CKaYN2fL.js} +2 -2
  133. package/dashboard/dist/assets/{bots-DB5Z7MEk.js.map → bots-CKaYN2fL.js.map} +1 -1
  134. package/dashboard/dist/assets/{capabilities-Du9EfNdT.js → capabilities-BknM6oFm.js} +2 -2
  135. package/dashboard/dist/assets/{capabilities-Du9EfNdT.js.map → capabilities-BknM6oFm.js.map} +1 -1
  136. package/dashboard/dist/assets/{controlplane-ku4V2nVk.js → controlplane-BodNGCr-.js} +2 -2
  137. package/dashboard/dist/assets/{controlplane-ku4V2nVk.js.map → controlplane-BodNGCr-.js.map} +1 -1
  138. package/dashboard/dist/assets/{escalation-oVVz7Lw2.js → escalation-CH_XK2XM.js} +2 -2
  139. package/dashboard/dist/assets/{escalation-oVVz7Lw2.js.map → escalation-CH_XK2XM.js.map} +1 -1
  140. package/dashboard/dist/assets/{escalation-columns-C-xaR_Qg.js → escalation-columns-CcbLKRI4.js} +2 -2
  141. package/dashboard/dist/assets/{escalation-columns-C-xaR_Qg.js.map → escalation-columns-CcbLKRI4.js.map} +1 -1
  142. package/dashboard/dist/assets/index--xNp5rx6.css +1 -0
  143. package/dashboard/dist/assets/{index-aHhMmZ0W.js → index-B-LQ8fsJ.js} +2 -2
  144. package/dashboard/dist/assets/{index-aHhMmZ0W.js.map → index-B-LQ8fsJ.js.map} +1 -1
  145. package/dashboard/dist/assets/{index-CyXilW90.js → index-BH67e1Ah.js} +2 -2
  146. package/dashboard/dist/assets/{index-CyXilW90.js.map → index-BH67e1Ah.js.map} +1 -1
  147. package/dashboard/dist/assets/{index-MbEPoyTe.js → index-B_aFcGv9.js} +4 -4
  148. package/dashboard/dist/assets/{index-MbEPoyTe.js.map → index-B_aFcGv9.js.map} +1 -1
  149. package/dashboard/dist/assets/{index-DsKK1fCc.js → index-Bwofyfi8.js} +2 -2
  150. package/dashboard/dist/assets/{index-DsKK1fCc.js.map → index-Bwofyfi8.js.map} +1 -1
  151. package/dashboard/dist/assets/{index-61yKjTkk.js → index-C3QKGY5C.js} +2 -2
  152. package/dashboard/dist/assets/{index-61yKjTkk.js.map → index-C3QKGY5C.js.map} +1 -1
  153. package/dashboard/dist/assets/{index-BwXtXaw8.js → index-CJpywoA8.js} +2 -2
  154. package/dashboard/dist/assets/{index-BwXtXaw8.js.map → index-CJpywoA8.js.map} +1 -1
  155. package/dashboard/dist/assets/index-CV8soXIM.js +5 -0
  156. package/dashboard/dist/assets/index-CV8soXIM.js.map +1 -0
  157. package/dashboard/dist/assets/{index-CxlsLEr1.js → index-CdN-Z8-r.js} +2 -2
  158. package/dashboard/dist/assets/{index-CxlsLEr1.js.map → index-CdN-Z8-r.js.map} +1 -1
  159. package/dashboard/dist/assets/{index-DcZDyLHC.js → index-D7NHjvA4.js} +2 -2
  160. package/dashboard/dist/assets/{index-DcZDyLHC.js.map → index-D7NHjvA4.js.map} +1 -1
  161. package/dashboard/dist/assets/{index-DkZnXWTA.js → index-DHhz71yv.js} +2 -2
  162. package/dashboard/dist/assets/{index-DkZnXWTA.js.map → index-DHhz71yv.js.map} +1 -1
  163. package/dashboard/dist/assets/{index-DgAmkmtv.js → index-Gt2Rebs9.js} +2 -2
  164. package/dashboard/dist/assets/{index-DgAmkmtv.js.map → index-Gt2Rebs9.js.map} +1 -1
  165. package/dashboard/dist/assets/index-IIq5Tkqs.js +6 -0
  166. package/dashboard/dist/assets/index-IIq5Tkqs.js.map +1 -0
  167. package/dashboard/dist/assets/{index-Bcgsf2yD.js → index-_gh2K9DU.js} +2 -2
  168. package/dashboard/dist/assets/{index-Bcgsf2yD.js.map → index-_gh2K9DU.js.map} +1 -1
  169. package/dashboard/dist/assets/{knowledge-CTjJVvIG.js → knowledge-CE6gjN3G.js} +2 -2
  170. package/dashboard/dist/assets/{knowledge-CTjJVvIG.js.map → knowledge-CE6gjN3G.js.map} +1 -1
  171. package/dashboard/dist/assets/{mcp-CsHkygRS.js → mcp-B4F-T-7R.js} +2 -2
  172. package/dashboard/dist/assets/{mcp-CsHkygRS.js.map → mcp-B4F-T-7R.js.map} +1 -1
  173. package/dashboard/dist/assets/{mcp-query-BCt4tpjO.js → mcp-query-CYOarL1K.js} +2 -2
  174. package/dashboard/dist/assets/{mcp-query-BCt4tpjO.js.map → mcp-query-CYOarL1K.js.map} +1 -1
  175. package/dashboard/dist/assets/{pipelines-BpGmuS5V.js → pipelines-B4gB9qMx.js} +2 -2
  176. package/dashboard/dist/assets/{pipelines-BpGmuS5V.js.map → pipelines-B4gB9qMx.js.map} +1 -1
  177. package/dashboard/dist/assets/{roles-lHLgLwOY.js → roles-LZf98ePg.js} +2 -2
  178. package/dashboard/dist/assets/{roles-lHLgLwOY.js.map → roles-LZf98ePg.js.map} +1 -1
  179. package/dashboard/dist/assets/{tasks-BHfimh-E.js → tasks-GG8c_U9w.js} +2 -2
  180. package/dashboard/dist/assets/{tasks-BHfimh-E.js.map → tasks-GG8c_U9w.js.map} +1 -1
  181. package/dashboard/dist/assets/{topics-BW2cS79q.js → topics-C7-6oOgA.js} +2 -2
  182. package/dashboard/dist/assets/{topics-BW2cS79q.js.map → topics-C7-6oOgA.js.map} +1 -1
  183. package/dashboard/dist/assets/{useEventHooks-CeOIM1Ix.js → useEventHooks-82IiMmOc.js} +2 -2
  184. package/dashboard/dist/assets/{useEventHooks-CeOIM1Ix.js.map → useEventHooks-82IiMmOc.js.map} +1 -1
  185. package/dashboard/dist/assets/{useNamespace-CEVLPGGY.js → useNamespace-DQEqD2LA.js} +2 -2
  186. package/dashboard/dist/assets/{useNamespace-CEVLPGGY.js.map → useNamespace-DQEqD2LA.js.map} +1 -1
  187. package/dashboard/dist/assets/{useYamlActivityEvents-DdRNcy7s.js → useYamlActivityEvents-Bm0-ewLx.js} +2 -2
  188. package/dashboard/dist/assets/{useYamlActivityEvents-DdRNcy7s.js.map → useYamlActivityEvents-Bm0-ewLx.js.map} +1 -1
  189. package/dashboard/dist/assets/{users-CAih5Fty.js → users-DHx2ium7.js} +2 -2
  190. package/dashboard/dist/assets/{users-CAih5Fty.js.map → users-DHx2ium7.js.map} +1 -1
  191. package/dashboard/dist/assets/{workflows-C_gCwB9q.js → workflows-qIbkeQ_5.js} +2 -2
  192. package/dashboard/dist/assets/{workflows-C_gCwB9q.js.map → workflows-qIbkeQ_5.js.map} +1 -1
  193. package/dashboard/dist/assets/{yaml-workflows-CPabf8Sf.js → yaml-workflows-FibyeHpo.js} +2 -2
  194. package/dashboard/dist/assets/{yaml-workflows-CPabf8Sf.js.map → yaml-workflows-FibyeHpo.js.map} +1 -1
  195. package/dashboard/dist/index.html +2 -2
  196. package/docs/api/http/escalations.md +28 -4
  197. package/docs/api/http/roles.md +1 -1
  198. package/docs/api/http/workflows.md +1 -1
  199. package/docs/api/sdk/escalations.md +40 -5
  200. package/docs/api/sdk/workflows.md +1 -1
  201. package/docs/dashboard.md +3 -3
  202. package/docs/data.md +2 -2
  203. package/docs/hitl-guide.md +114 -6
  204. package/docs/maintenance.md +6 -3
  205. package/docs/oauth-and-delegation.md +1 -1
  206. package/package.json +2 -2
  207. package/dashboard/dist/assets/EscalationSchemaPage-hzFtuyha.js +0 -9
  208. package/dashboard/dist/assets/EscalationSchemaPage-hzFtuyha.js.map +0 -1
  209. package/dashboard/dist/assets/OperationsPage-Cz_0K8tj.js +0 -2
  210. package/dashboard/dist/assets/OperationsPage-Cz_0K8tj.js.map +0 -1
  211. package/dashboard/dist/assets/RoleDetailPage-BPsrUqIa.js +0 -11
  212. package/dashboard/dist/assets/RoleDetailPage-BPsrUqIa.js.map +0 -1
  213. package/dashboard/dist/assets/index-BdqKh0UK.css +0 -1
  214. package/dashboard/dist/assets/index-CAzFU1rn.js +0 -9
  215. package/dashboard/dist/assets/index-CAzFU1rn.js.map +0 -1
  216. package/dashboard/dist/assets/index-D0bDVU2a.js +0 -6
  217. package/dashboard/dist/assets/index-D0bDVU2a.js.map +0 -1
@@ -140,7 +140,7 @@ PUT /api/workflows/:type/config
140
140
  | `execute_as` | `string \| null` | `null` | Service account `external_id` to run as (overrides invoker identity) |
141
141
  | `tool_tags` | `string[]` | `[]` | MCP tool tags for scoped tool discovery |
142
142
  | `envelope_schema` | `object \| null` | `null` | JSON Schema for the workflow input envelope |
143
- | `resolver_schema` | `object \| null` | `null` | JSON Schema for the escalation resolver payload |
143
+ | `resolver_schema` | `object \| null` | `null` | **Deprecated** legacy fallback only. The escalation form is owned by the target role as a versioned `form_schema`. |
144
144
  | `cron_schedule` | `string \| null` | `null` | Cron expression for scheduled execution (e.g., `"0 9 * * *"`) |
145
145
 
146
146
  **Example request:**
@@ -387,7 +387,7 @@ export async function stationWorker(envelope: LTEnvelope) {
387
387
  workflowType: 'stationWorker',
388
388
  metadata: { orderId: envelope.data.orderId, station: 'qc' },
389
389
  envelope: { instructions: 'Review and approve or reject' },
390
- timeout: '24h', // SLA for this worklist row (hotmesh 0.25.1+)
390
+ timeout: '24h', // SLA for this worklist row
391
391
  });
392
392
 
393
393
  if (decision === false) {
@@ -405,7 +405,7 @@ export async function stationWorker(envelope: LTEnvelope) {
405
405
 
406
406
  ### SLA-gated waits (`timeout`)
407
407
 
408
- `ConditionQueueConfig.timeout` (hotmesh 0.25.1+) arms the same resume timer as
408
+ `ConditionQueueConfig.timeout` arms the same resume timer as
409
409
  `condition(signalId, '24h')` **in the same single Leg1 write** that creates the
410
410
  escalation row. The race is resolved atomically on both sides: a signal that
411
411
  arrives first resolves the row normally and the timer is inert; a timer that
@@ -472,9 +472,9 @@ If you use raw `Durable.workflow.condition()` instead, the `$escalation_id` fiel
472
472
 
473
473
  When a reviewer claims an escalation in the dashboard, a typed form is rendered instead of a raw JSON editor — if a schema is available. There are two ways to provide one:
474
474
 
475
- **Option 1 — Workflow config (static):** Set `resolver_schema` in the workflow registry wizard (Step 3, Certification). Every escalation from that workflow inherits the schema.
475
+ **Option 1 — Role `form_schema` (versioned):** The escalation form is owned by the target role as a versioned `form_schema`. A workflow pins a version through `conditionLT`'s `schemaVersion`; unpinned escalations resolve against the role's latest. Fields may carry `x-lt-bind` to map a form value to a path in the resolver payload. The deprecated workflow-config `resolver_schema` remains only as a legacy fallback.
476
476
 
477
- **Option 2 — Escalation metadata (dynamic):** Pass `form_schema` inside `metadata` when creating an escalation. This overrides any workflow-level schema.
477
+ **Option 2 — Escalation metadata (dynamic):** Pass `form_schema` inside `metadata` when creating an escalation. This overrides any role-level schema.
478
478
 
479
479
  ### Schema shape
480
480
 
@@ -511,7 +511,7 @@ Keys prefixed with `_` are stored in the payload but hidden from the form. `_for
511
511
 
512
512
  ### Priority
513
513
 
514
- `metadata.form_schema` takes precedence over `resolver_schema` from the workflow config.
514
+ The form is resolved by precedence: `metadata.form_schema` (legacy inline on the row) > the role's `form_schema` (resolved to the escalation's pinned `metadata.schema_version`, or the role's latest when unpinned) > the deprecated workflow-config `resolver_schema` legacy fallback.
515
515
 
516
516
  ---
517
517
 
@@ -823,6 +823,41 @@ const result = await lt.escalations.resolveByIds({
823
823
 
824
824
  **Returns:** `LTApiResult<{ resolved: number; escalationIds: string[] }>` — only still-`pending` rows are resolved.
825
825
 
826
+ ## resolveAllOrNone
827
+
828
+ Atomic bulk resolve with per-row payloads: every listed escalation resolves with its own `resolverPayload` in one SQL statement, or nothing resolves. Rows backing a live `condition()` waiter are first-class — each waiter's wake commits with its resolve, delivering that row's payload as the condition's return value (the same wake contract as `resolve`). Use it for gang handoffs where each member must receive a distinct mandate and a partial batch is unacceptable.
829
+
830
+ RBAC matches `resolveByIds`: per-item write scope, and any missing or out-of-scope id returns 404 with nothing resolved. `requireClaimed` additionally asserts — inside the same guarded statement — that every row is currently assigned to the caller, closing the window where another principal re-claims a member between the caller's claim and the resolve.
831
+
832
+ ```typescript
833
+ const result = await lt.escalations.resolveAllOrNone({
834
+ items: [
835
+ { id: 'esc_left', resolverPayload: { gcodeRef: 'gcode-left', unit: 'left' } },
836
+ { id: 'esc_right', resolverPayload: { gcodeRef: 'gcode-right', unit: 'right' } },
837
+ ],
838
+ metadata: { outcome: 'gang-dispatched' },
839
+ requireClaimed: true,
840
+ });
841
+
842
+ if (result.status === 409) {
843
+ // nothing resolved — result.data.failed names exactly the blocking rows
844
+ // (not-found | already-resolved | already-cancelled | already-expired |
845
+ // assignee-mismatch | unsupported-resolution-path); re-gang around them
846
+ }
847
+ ```
848
+
849
+ | Field | Type | Required | Description |
850
+ |-------|------|----------|-------------|
851
+ | `items` | `Array<{ id, resolverPayload }>` | Yes | The batch — each row resolves with its own payload. Ids must be unique; max 100 items (`LT_ESCALATION_BULK_RESOLVE_MAX`) |
852
+ | `metadata` | `Record<string, any>` | No | Shared outcome patch merged into every row's GIN-indexed metadata |
853
+ | `requireClaimed` | `boolean` | No | Assert every row is currently assigned to the caller, inside the atomic statement |
854
+
855
+ **Returns:** `LTApiResult<{ resolved: number; escalationIds: string[] }>` on success. On 409 the data carries `{ error, failedIds, failed: [{ id, reason }] }` — only the rows that blocked the batch are listed; resolvable members stay pending, untouched.
856
+
857
+ Rows that resolve through legacy signal routing (`metadata.signal_id` / `metadata.signal_routing`) require the single `resolve` path, which delivers their workflow signal; including one blocks the batch with reason `unsupported-resolution-path`. Password-format fields are redacted per row against that row's own `form_schema` before the payload enters the signal store.
858
+
859
+ **Auth:** Required
860
+
826
861
  ## resolveBySignalKey
827
862
 
828
863
  Resolve an efficient (atomic) escalation directly by its `signal_key` and resume the waiting workflow in place. For callers that know the deterministic signal id and want to skip the id lookup. RBAC-scoped to the escalation's role.
@@ -242,7 +242,7 @@ const result = await lt.workflows.upsertConfig({
242
242
  | `consumes` | `string[]` | No | Workflow types whose data this workflow consumes |
243
243
  | `tool_tags` | `string[]` | No | MCP tool tags for discovery |
244
244
  | `envelope_schema` | `any` | No | JSON Schema for envelope.data validation |
245
- | `resolver_schema` | `any` | No | JSON Schema for resolver payload validation |
245
+ | `resolver_schema` | `any` | No | **Deprecated** legacy fallback only. There is no resolver-payload validation; the escalation form is owned by the target role as a versioned `form_schema`. |
246
246
  | `cron_schedule` | `string \| null` | No | Cron expression for scheduled execution |
247
247
 
248
248
  **Returns:** `LTApiResult<LTWorkflowConfig>`
package/docs/dashboard.md CHANGED
@@ -6,7 +6,7 @@ The Long Tail dashboard is a React single-page application for managing procedur
6
6
 
7
7
  The sidebar organizes pages into six groups.
8
8
 
9
- ### React
9
+ ### Monitor
10
10
 
11
11
  The reactive, event-driven surface — where operations watch the floor and builders configure choreography.
12
12
 
@@ -110,7 +110,7 @@ workers: [
110
110
  certified: true,
111
111
  roles: ['reviewer', 'admin'],
112
112
  envelopeSchema: { data: { field1: '', field2: 0 } },
113
- resolverSchema: { approved: true, notes: '' },
113
+ resolverSchema: { approved: true, notes: '' }, // deprecated legacy fallback — the escalation form is owned by the target role as a versioned form_schema
114
114
  },
115
115
  },
116
116
  ]
@@ -308,7 +308,7 @@ The central queue for all escalation activity across every workflow.
308
308
  - **Filter bar** — filter by status (pending/claimed/resolved), role, workflow type, priority, and time window.
309
309
  - **Columns:** Escalation ID, workflow type, role, status, priority, created time, and claimed-by user.
310
310
  - **Claim** — click the claim action to lock an escalation to your user. Only users with matching roles see pending escalations. The queue list and aggregate stats reflect `read_all` memberships — a member scoped to `read_self` lands directly on their own assigned item in user mode rather than browsing the full queue.
311
- - **Resolve** — after claiming, submit a resolver payload (pre-filled from the workflow's `resolver_schema` if configured). Resolution triggers a workflow re-run with the resolver data injected. A `member` whose `write_scope` is `self` can resolve only items already assigned to them; `write_scope=none` is read-only.
311
+ - **Resolve** — after claiming, submit a resolver payload. The form is pre-filled from the role's versioned `form_schema` field defaults and from the workflow's seeded `envelope.formDefaults` (reverse-mapped through each field's `x-lt-bind`). The dashboard maps the flat form to the nested payload via `x-lt-bind` and the submitted payload is stored as-is. Resolution triggers a workflow re-run with the resolver data injected. A `member` whose `write_scope` is `self` can resolve only items already assigned to them; `write_scope=none` is read-only.
312
312
  - **Escalate** — forward a claimed escalation to a higher-tier role via the escalation chain.
313
313
 
314
314
  **API:** `GET /api/escalations` lists with filters. `POST /api/escalations/:id/claim` claims. `POST /api/escalations/:id/resolve` resolves.
package/docs/data.md CHANGED
@@ -104,7 +104,7 @@ Records human intervention requests. Created when a workflow returns
104
104
  workflow calls `condition(signalId, config)` / `conditionLT(signalId, config)`.
105
105
  Updated when claimed or resolved.
106
106
 
107
- Since v0.5.3 the storage is the **shared HotMesh table `public.hmsh_escalations`**
107
+ The storage is the **shared HotMesh table `public.hmsh_escalations`**
108
108
  (written by both long-tail and the HotMesh SDK). `lt_escalations` remains as a
109
109
  backward-compatible **view** over it — `SELECT *` plus a computed `available`
110
110
  column — so existing read queries and the public API are unchanged. Indexes are
@@ -263,7 +263,7 @@ Workflow registration. Every workflow that uses the LT interceptor must have a r
263
263
  | `consumes` | `TEXT[] NOT NULL` | `'{}'` | Array of workflow types whose completed data this workflow receives via `envelope.lt.providers` |
264
264
  | `tool_tags` | `TEXT[]` | `'{}'` | Tags for MCP tool discovery |
265
265
  | `envelope_schema` | `JSONB` | — | JSON Schema for the workflow input envelope |
266
- | `resolver_schema` | `JSONB` | — | JSON Schema for the escalation resolver payload |
266
+ | `resolver_schema` | `JSONB` | — | **Deprecated** legacy fallback only. The escalation form is owned by the target role as a versioned `form_schema`. |
267
267
  | `cron_schedule` | `TEXT` | — | Cron expression for scheduled execution |
268
268
  | `created_at` | `TIMESTAMPTZ NOT NULL` | `NOW()` | Row creation time |
269
269
  | `updated_at` | `TIMESTAMPTZ NOT NULL` | `NOW()` | Last modification |
@@ -83,9 +83,24 @@ export async function approvalWorkflow(envelope: LTEnvelope) {
83
83
  }
84
84
  ```
85
85
 
86
- The `timeout` field (hotmesh 0.25.1+) makes the wait SLA-gated in the same
87
- single Leg1 write: one `conditionLT` call yields the worklist row AND the
88
- resume timer. Omit it for an open-ended wait.
86
+ The `timeout` field makes the wait SLA-gated in the same single Leg1 write:
87
+ one `conditionLT` call yields the worklist row AND the resume timer. Omit it
88
+ for an open-ended wait.
89
+
90
+ Two engine contracts worth building on:
91
+
92
+ - **The row is complete from its first visible moment.** Every field of the
93
+ config — including `metadata` facets — commits inside the Leg1 checkpoint.
94
+ A claim-by-metadata router or a version-pinned facet (e.g. a
95
+ `schema_version` the resolver UI renders) can trust every row it reads;
96
+ there is no window where a row is visible but its metadata is still en route.
97
+ - **Early signals are buffered.** A resolve that races ahead of the
98
+ `condition()` registration (a fast webhook, a payload deposited before the
99
+ workflow starts) is held as a pending signal and delivered when the wait
100
+ registers — 10 minutes by default; pass `expire` to `signal()` (e.g. `'1h'`)
101
+ when signaling early on purpose. Fan-out (`Promise.all` over many waits)
102
+ scales the same way: buffering covers every signal that outruns its
103
+ registration.
89
104
 
90
105
  #### Two-step form
91
106
 
@@ -165,9 +180,8 @@ export async function reviewWorkflow(envelope: LTEnvelope) {
165
180
  The resolver form resolves in order, most specific first:
166
181
 
167
182
  1. **`metadata.form_schema`** — a full JSON Schema embedded on the escalation row. Use when different escalation points in the same workflow need different forms.
168
- 2. **`metadata.schema_version`** — a pin to a specific version of the role's schema (set via `schemaVersion` in the `conditionLT` config). The form renders exactly that snapshot, even after the role's schema changes.
169
- 3. **Workflow config `resolver_schema`** — default form for all escalations of this workflow type. Set in the workflow registry.
170
- 4. **The role's latest `form_schema`** — the fallback every role provides.
183
+ 2. **The role's `form_schema`** — the versioned form every role provides. When the row carries a `metadata.schema_version` pin (set via `schemaVersion` in the `conditionLT` config), the form renders exactly that snapshot even after the role's schema changes; without a pin, the role's latest applies.
184
+ 3. **Workflow config `resolver_schema`** — **deprecated** legacy fallback only, used when no role `form_schema` is available.
171
185
 
172
186
  ### Versioned Role Schemas
173
187
 
@@ -210,12 +224,47 @@ await lt.escalations.resolve({
210
224
 
211
225
  The patch is distinct from the resolver payload: the payload resumes the paused workflow and is not indexed; the metadata patch is the durable, queryable record on the row. Use it for the audit trail and analytics — disposition, reviewer, time-to-resolve — so the escalation table answers *what was asked, what was decided, and how long it took* without a parallel log.
212
226
 
227
+ ### Resolving a set atomically
228
+
229
+ When one decision settles a SET of waits — each with its own payload — use
230
+ `lt.escalations.resolveAllOrNone({ items })` (`POST /api/escalations/resolve-all-or-none`).
231
+ Every listed row resolves with its own `resolverPayload` in one SQL statement,
232
+ waking each parked workflow with its own value, or nothing resolves and the
233
+ 409 body names exactly the rows that blocked (`failedIds` + reasons). Pass
234
+ `requireClaimed: true` in claim-then-resolve flows to assert, inside the same
235
+ statement, that every row is still assigned to the caller. See the
236
+ [SDK reference](./api/sdk/escalations.md#resolveallornone) for the full contract.
237
+
213
238
  ---
214
239
 
215
240
  ## JSON Schema Form Authoring
216
241
 
217
242
  The dashboard renders forms automatically from JSON Schema. No frontend code needed.
218
243
 
244
+ The full custom vocabulary at a glance — every `x-lt-*` keyword and extension key the renderer honors:
245
+
246
+ | Keyword | Level | Purpose |
247
+ |---------|-------|---------|
248
+ | `x-lt-widget` | field | Rich control: `file-upload`, `code-editor`, `signature`, `rich-text`, `markdown` |
249
+ | `x-lt-language` | field | Syntax hint shown by the `code-editor` widget |
250
+ | `accept` | field | File-type filter for the `file-upload` widget (e.g. `".pdf,.png"`) |
251
+ | `x-lt-bind` | field | Path this field's value occupies in the resolver payload (e.g. `"customer.email"`) |
252
+ | `x-lt-span` | field | Column span in a `two-column` layout (`2` = full width) |
253
+ | `x-lt-order` | schema | Field render sequence |
254
+ | `x-lt-layout` | schema | `"two-column"` grid layout |
255
+ | `x-lt-context` | schema | Context panel text shown alongside the form in user mode |
256
+ | `x-lt-viewport` | schema | Replace the generated form with a custom iframe UI |
257
+ | `format` | field | Input specialization: `password`, `date`, `date-time`, `email`, `uri`, `textarea` |
258
+ | `readOnly` | field | Static display (or a rendered content block with the `markdown` widget) |
259
+ | `required` | schema | Fields that must be filled before submit |
260
+ | `title` / `description` | both | Section header / helper text |
261
+
262
+ The working reference is `examples/workflows/rich-form/` — the `intake-reviewer` role's
263
+ versioned `form_schema` (seeded by `examples/seed-rich-form.ts`) exercises the whole
264
+ vocabulary in one form: a markdown content block, two-column layout, ordering, date and
265
+ email formats, enum, file upload, spans, required fields, and `x-lt-bind` mapping into a
266
+ nested payload.
267
+
219
268
  ### Supported Field Types
220
269
 
221
270
  | JSON Type | Renders As |
@@ -261,6 +310,7 @@ For rich inputs beyond standard HTML types:
261
310
  | `"code-editor"` | Monospace textarea with tab-key support. Use `x-lt-language` for syntax hint. |
262
311
  | `"signature"` | HTML5 Canvas drawing pad. Outputs PNG data URL. |
263
312
  | `"rich-text"` | Tall textarea for formatted text input. |
313
+ | `"markdown"` | Markdown source, rendered with the same engine as the docs drawer (headings, tables, lists, code blocks, callouts). Editable fields get a Write/Preview toggle; with `readOnly: true` the field is a pure content block — see below. |
264
314
 
265
315
  ```json
266
316
  {
@@ -286,6 +336,64 @@ For rich inputs beyond standard HTML types:
286
336
  }
287
337
  ```
288
338
 
339
+ #### Markdown content blocks
340
+
341
+ `readOnly: true` + `x-lt-widget: "markdown"` turns a field into a rendered content
342
+ block: the markdown in its `default` displays as HTML inside the form — headings,
343
+ tables, checklists, callouts. The versioned schema carries the page source itself,
344
+ so review instructions and SOPs version with the form they belong to, and the
345
+ source rides along in the resolver payload like any read-only field.
346
+
347
+ ```json
348
+ {
349
+ "properties": {
350
+ "review_guide": {
351
+ "type": "string",
352
+ "readOnly": true,
353
+ "x-lt-widget": "markdown",
354
+ "x-lt-span": 2,
355
+ "default": "### Review checklist\n\n1. Confirm the **legal name** matches.\n2. Send a test message before approving.\n\n> Escalate non-standard contract language to legal."
356
+ }
357
+ }
358
+ }
359
+ ```
360
+
361
+ Without `readOnly`, the field is a markdown *editor* — the resolver writes source in
362
+ a Write/Preview toggle and the submitted value is the markdown text.
363
+
364
+ ### Payload Binding (`x-lt-bind`)
365
+
366
+ The form is flat; the payload the workflow consumes rarely is. A field may declare
367
+ `x-lt-bind` — the path its value occupies in the resolver payload (dot keys, optional
368
+ `[n]` indices). The dashboard maps the flat form through the binds on submit, and
369
+ reverse-maps workflow-seeded `envelope.formDefaults` through them to prefill. A field
370
+ with no bind lands at its own name at the payload root (1:1).
371
+
372
+ ```json
373
+ {
374
+ "properties": {
375
+ "customer_name": { "type": "string", "x-lt-bind": "customer.name" },
376
+ "contact_email": { "type": "string", "format": "email", "x-lt-bind": "customer.email" },
377
+ "tier": { "type": "string", "enum": ["starter", "professional"], "x-lt-bind": "contract.tier" },
378
+ "notes": { "type": "string", "format": "textarea" }
379
+ }
380
+ }
381
+ ```
382
+
383
+ Submitting `{ customer_name, contact_email, tier, notes }` stores:
384
+
385
+ ```json
386
+ {
387
+ "customer": { "name": "…", "email": "…" },
388
+ "contract": { "tier": "…" },
389
+ "notes": "…"
390
+ }
391
+ ```
392
+
393
+ Only the FORM is versioned on the role — the payload shape is the workflow's own
394
+ contract, produced by the binds. Evolve the form and its binds together, and the
395
+ workflow's resolver type in the same commit.
396
+
289
397
  ### Layout Options (`x-lt-layout`)
290
398
 
291
399
  Control how fields are arranged:
@@ -28,6 +28,7 @@ await start({
28
28
  { target: 'jobs', olderThan: '14 days', action: 'delete', hasEntity: false },
29
29
  { target: 'jobs', olderThan: '14 days', action: 'prune', hasEntity: true },
30
30
  { target: 'jobs', olderThan: '180 days', action: 'delete', pruned: true },
31
+ { target: 'escalations', olderThan: '180 days', action: 'delete' },
31
32
  ],
32
33
  },
33
34
  });
@@ -42,14 +43,15 @@ await start({
42
43
 
43
44
  ## What gets cleaned
44
45
 
45
- Four categories of data are subject to maintenance:
46
+ Five categories of data are subject to maintenance:
46
47
 
47
48
  | Category | Description |
48
49
  |---|---|
49
- | **Streams** | Redis-style stream messages used internally by HotMesh to coordinate activities. These are pure infrastructure; no user-facing data resides here. |
50
+ | **Streams** | Stream messages used internally by HotMesh to coordinate activities. These are pure infrastructure; no user-facing data resides here. |
50
51
  | **Transient jobs** | Job rows where the `entity` column is `NULL`. These represent activity executions, signal deliveries, and other bookkeeping that is not tied to a named workflow entity. They serve no purpose after execution completes. |
51
52
  | **Entity jobs** | Job rows where `entity` is set. These represent actual workflow instances -- the rows that back `Durable.Client.workflow.search()` calls and execution exports. Deleting them removes the workflow record entirely. |
52
53
  | **Pruned jobs** | Entity jobs that have already had their execution artifacts stripped (the `pruned_at` column is not `NULL`). These retain core data but no longer carry execution scaffolding. They can be hard-deleted after a longer retention window. |
54
+ | **Terminal escalations** | Escalation rows in a terminal status (`resolved`, `cancelled`, `expired`). Terminal rows are inert audit records -- every engine state transition guards on `status = 'pending'`, so aging them out is safe for live waiters, claims, and signal delivery. The engine-owned prune deletes in atomic batches until the retention horizon is drained. Pending rows are never touched. |
53
55
 
54
56
  ## Prune vs. delete
55
57
 
@@ -93,11 +95,12 @@ Defined in `types/maintenance.ts`. Each rule describes a single cleanup operatio
93
95
 
94
96
  | Field | Type | Required | Description |
95
97
  |---|---|---|---|
96
- | `target` | `'streams' \| 'jobs'` | Yes | The resource type to act on. |
98
+ | `target` | `'streams' \| 'jobs' \| 'escalations'` | Yes | The resource type to act on. |
97
99
  | `action` | `'delete' \| 'prune'` | Yes | Whether to remove entirely or strip execution artifacts. |
98
100
  | `olderThan` | `string` | Yes | A PostgreSQL interval expression: `'7 days'`, `'24 hours'`, `'90 days'`. |
99
101
  | `hasEntity` | `boolean` | No | When `target` is `'jobs'`: `true` selects entity jobs, `false` selects transient jobs (where `entity IS NULL`). |
100
102
  | `pruned` | `boolean` | No | When `true`, only targets jobs where `pruned_at IS NOT NULL` -- jobs that have already been pruned. |
103
+ | `statuses` | `Array<'resolved' \| 'cancelled' \| 'expired'>` | No | When `target` is `'escalations'`: which terminal statuses to age out. Defaults to all three. |
101
104
 
102
105
  ### `LTMaintenanceConfig`
103
106
 
@@ -143,7 +143,7 @@ if (ctx) {
143
143
  }
144
144
  ```
145
145
 
146
- The MCP client (`callServerTool`) also reads `ToolContext` automatically. When no explicit `authContext` is passed, it derives `_auth` from the ambient context. This closes the identity gap for YAML workflow workers, which previously had no auth propagation.
146
+ The MCP client (`callServerTool`) also reads `ToolContext` automatically. When no explicit `authContext` is passed, it derives `_auth` from the ambient context YAML workflow workers get auth propagation this way without any explicit wiring.
147
147
 
148
148
  ## Delegation Tokens
149
149
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hotmeshio/long-tail",
3
- "version": "0.8.3",
3
+ "version": "0.8.5",
4
4
  "description": "Long Tail Workflows — Durable AI workflows with human-in-the-loop escalation. Powered by PostgreSQL.",
5
5
  "main": "./build/index.js",
6
6
  "types": "./build/index.d.ts",
@@ -72,7 +72,7 @@
72
72
  "@anthropic-ai/sdk": "^0.92.0",
73
73
  "@aws-sdk/client-s3": "^3.1017.0",
74
74
  "@aws-sdk/s3-request-presigner": "^3.1045.0",
75
- "@hotmeshio/hotmesh": "^0.25.6",
75
+ "@hotmeshio/hotmesh": "^0.26.1",
76
76
  "@modelcontextprotocol/sdk": "^1.27.1",
77
77
  "@opentelemetry/exporter-trace-otlp-proto": "^0.215.0",
78
78
  "@opentelemetry/resources": "^2.5.1",
@@ -1,9 +0,0 @@
1
- import{a as l,j as e}from"./vendor-query-B2UbickB.js";import{b as S,f as w,j as _,c as E}from"./roles-lHLgLwOY.js";import{J as C}from"./index-MbEPoyTe.js";import{g as j,B as O,V as R,ah as q}from"./vendor-icons-DOIDKgo6.js";import{e as V}from"./vendor-react-CXumBFUA.js";const J=`{
2
- "type": "object",
3
- "properties": {
4
- "approved": { "type": "boolean", "title": "Approve?" },
5
- "notes": { "type": "string", "title": "Notes" }
6
- },
7
- "required": ["approved"]
8
- }`;function v(o){const t=o.trim();if(!t)return{ok:!0,value:null};try{const r=JSON.parse(t);return typeof r!="object"||Array.isArray(r)||r===null?{ok:!1}:{ok:!0,value:r}}catch{return{ok:!1}}}function F(){const{role:o}=V(),{data:t,isLoading:r}=S(),n=w(),s=((t==null?void 0:t.roles)??[]).find(a=>a.role===o),[c,i]=l.useState(""),[h,u]=l.useState(""),[N,d]=l.useState(!1),[g,x]=l.useState(!1),[p,m]=l.useState(!1),f=l.useRef(null);l.useEffect(()=>{!s||f.current===s.role||(f.current=s.role,i(s.form_schema?JSON.stringify(s.form_schema,null,2):""),u(""),d(!1),m(!1))},[s]);const y=a=>{i(a),d(!0),x(!1),m(!v(a).ok)},b=()=>{const a=v(c);if(!a.ok||!s){m(!a.ok);return}n.mutate({role:s.role,form_schema:a.value??null,change_summary:h.trim()||void 0},{onSuccess:()=>{d(!1),x(!0),u(""),window.setTimeout(()=>x(!1),2500)}})},k=a=>{i(a?JSON.stringify(a,null,2):""),d(!0),x(!1),m(!1)};return r?e.jsxs("div",{className:"animate-pulse space-y-4",children:[e.jsx("div",{className:"h-8 bg-surface-sunken rounded w-64"}),e.jsx("div",{className:"h-96 bg-surface-sunken rounded"})]}):s?e.jsxs("div",{children:[e.jsxs("div",{className:"flex items-start justify-between mb-10",children:[e.jsx("div",{children:e.jsxs("div",{className:"flex items-center gap-3 mb-1",children:[e.jsx(j,{className:"w-5 h-5 text-accent",strokeWidth:1.5}),e.jsx("h1",{className:"text-lg font-medium text-text-primary",children:"Escalation Schema"}),e.jsx("button",{onClick:()=>{window.location.hash="#docs:dashboard.md:escalation-schema"},className:"text-text-quaternary hover:text-accent transition-colors",title:"Open docs for this page",children:e.jsx(O,{className:"w-4 h-4",strokeWidth:1.5})}),e.jsx("span",{className:"font-mono text-sm text-text-tertiary",children:s.role}),s.current_schema_version!=null&&e.jsxs("span",{className:"text-[10px] font-mono text-text-quaternary",children:["v",s.current_schema_version," in use"]})]})}),e.jsxs("div",{className:"flex items-center gap-2 shrink-0",children:[g&&e.jsxs("span",{className:"flex items-center gap-1 text-xs text-status-success animate-page-enter",children:[e.jsx(R,{className:"w-3 h-3"})," Saved"]}),n.error&&e.jsx("span",{className:"text-xs text-status-error max-w-[220px] truncate",children:n.error.message}),e.jsx("button",{onClick:b,disabled:!N||p||n.isPending,className:"px-3 py-1.5 text-xs rounded-md bg-accent text-text-inverse hover:bg-accent-hover transition-colors disabled:opacity-40 disabled:cursor-not-allowed",children:n.isPending?"Saving…":"Save Version"})]})]}),e.jsxs("div",{className:"grid grid-cols-3 gap-16 items-start",children:[e.jsxs("div",{className:"col-span-2 space-y-6",children:[e.jsx("textarea",{value:c,onChange:a=>y(a.target.value),rows:28,spellCheck:!1,className:"input text-xs font-mono w-full resize-y leading-relaxed",placeholder:J}),p&&e.jsx("p",{className:"text-[10px] text-status-error -mt-4",children:"Invalid JSON"}),e.jsxs("div",{children:[e.jsx("label",{className:"block text-[10px] font-semibold uppercase tracking-widest text-text-tertiary mb-1.5",children:"Change Summary"}),e.jsx("input",{type:"text",value:h,onChange:a=>u(a.target.value),placeholder:"e.g., Added lotNumber field for shoe orders",className:"input text-sm w-full"}),e.jsx("p",{className:"text-[10px] text-text-quaternary mt-1.5",children:"Recorded on the version this save creates."})]})]}),e.jsxs("div",{children:[e.jsxs("div",{className:"flex items-center gap-2 mb-3 pb-2 border-b border-surface-border",children:[e.jsx(q,{className:"w-4 h-4 text-text-quaternary",strokeWidth:1.5}),e.jsx("h2",{className:"section-h2",children:"Versions"})]}),e.jsx(L,{role:s.role,onLoad:k})]})]})]}):e.jsxs("div",{className:"flex flex-col items-center justify-center py-20 text-center",children:[e.jsx(j,{className:"w-12 h-12 text-text-quaternary mb-4",strokeWidth:1}),e.jsx("h2",{className:"text-lg font-medium text-text-primary mb-2",children:"Role not found"})]})}function L({role:o,onLoad:t}){const{data:r}=_(o),n=(r==null?void 0:r.versions)??[];return n.length===0?e.jsx("p",{className:"text-[11px] text-text-tertiary leading-relaxed",children:"Versions appear here after the first save. Each schema change adds one; earlier versions stay viewable and loadable as an editing base."}):e.jsx("div",{className:"divide-y divide-surface-border/40",children:n.map(s=>e.jsx(P,{role:o,summary:s,onLoad:t},s.version))})}function P({role:o,summary:t,onLoad:r}){const[n,s]=l.useState(!1),c=E(o,t.version,n);return e.jsxs("div",{className:"py-2",children:[e.jsxs("button",{onClick:()=>s(i=>!i),className:"w-full text-left group",children:[e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsxs("span",{className:"text-xs font-mono text-text-secondary",children:["v",t.version]}),t.is_current&&e.jsx("span",{className:"text-[9px] font-bold uppercase tracking-wider text-accent",children:"current"}),e.jsx("span",{className:"flex-1"}),e.jsx("span",{className:"text-[10px] text-text-quaternary shrink-0",children:new Date(t.created_at).toLocaleDateString()})]}),t.change_summary&&e.jsx("p",{className:"mt-0.5 text-[10px] text-text-tertiary truncate",children:t.change_summary})]}),n&&e.jsx("div",{className:"mt-2 space-y-2",children:c.data?e.jsxs(e.Fragment,{children:[c.data.form_schema?e.jsx(C,{data:c.data.form_schema,defaultCollapsed:!0}):e.jsx("p",{className:"text-[10px] text-text-quaternary",children:"This version carries no form schema."}),e.jsx("button",{onClick:()=>r(c.data.form_schema??null),className:"text-[10px] text-accent hover:underline",children:"Load into editor"})]}):e.jsx("p",{className:"text-[10px] text-text-quaternary",children:"Loading…"})})]})}export{F as EscalationSchemaPage};
9
- //# sourceMappingURL=EscalationSchemaPage-hzFtuyha.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"EscalationSchemaPage-hzFtuyha.js","sources":["../../src/pages/admin/roles/EscalationSchemaPage.tsx"],"sourcesContent":["import { useState, useEffect, useRef } from 'react';\nimport { useParams } from 'react-router-dom';\nimport { Braces, Check, History, BookOpen } from 'lucide-react';\nimport {\n useRoleDetails,\n useRoleSchema,\n useRoleSchemaVersions,\n useUpdateRole,\n type RoleSchemaVersionSummary,\n} from '../../../api/roles';\nimport { JsonViewer } from '../../../components/common/data/JsonViewer';\n\n/**\n * The escalation form schema, on its own page (/admin/roles/:role/schema).\n * This is the form a person completes to resolve the role's escalations —\n * versioned, pinnable by workflows (conditionLT schemaVersion), and worth a\n * full editing surface. Saving here writes ONLY the schema: one PATCH with\n * form_schema (+ optional change summary); every actual change appends the\n * next immutable version.\n */\n\nconst PLACEHOLDER = `{\n \"type\": \"object\",\n \"properties\": {\n \"approved\": { \"type\": \"boolean\", \"title\": \"Approve?\" },\n \"notes\": { \"type\": \"string\", \"title\": \"Notes\" }\n },\n \"required\": [\"approved\"]\n}`;\n\nfunction safeParseObject(text: string): { ok: boolean; value?: Record<string, unknown> | null } {\n const trimmed = text.trim();\n if (!trimmed) return { ok: true, value: null };\n try {\n const parsed = JSON.parse(trimmed);\n if (typeof parsed !== 'object' || Array.isArray(parsed) || parsed === null) return { ok: false };\n return { ok: true, value: parsed };\n } catch {\n return { ok: false };\n }\n}\n\nexport function EscalationSchemaPage() {\n const { role: roleKey } = useParams<{ role: string }>();\n const { data, isLoading } = useRoleDetails();\n const updateRole = useUpdateRole();\n\n const role = (data?.roles ?? []).find((r) => r.role === roleKey);\n\n const [text, setText] = useState('');\n const [summary, setSummary] = useState('');\n const [dirty, setDirty] = useState(false);\n const [savedOk, setSavedOk] = useState(false);\n const [jsonError, setJsonError] = useState(false);\n\n // Seed the editor from the live schema exactly once per role — refetches\n // (cache invalidations, saves) must not clobber in-progress edits.\n const seededFor = useRef<string | null>(null);\n useEffect(() => {\n if (!role || seededFor.current === role.role) return;\n seededFor.current = role.role;\n setText(role.form_schema ? JSON.stringify(role.form_schema, null, 2) : '');\n setSummary('');\n setDirty(false);\n setJsonError(false);\n }, [role]);\n\n const handleEdit = (value: string) => {\n setText(value);\n setDirty(true);\n setSavedOk(false);\n setJsonError(!safeParseObject(value).ok);\n };\n\n const handleSave = () => {\n const parsed = safeParseObject(text);\n if (!parsed.ok || !role) { setJsonError(!parsed.ok); return; }\n // Saving just the schema — nothing else rides this PATCH.\n updateRole.mutate(\n { role: role.role, form_schema: parsed.value ?? null, change_summary: summary.trim() || undefined },\n {\n onSuccess: () => {\n setDirty(false);\n setSavedOk(true);\n setSummary('');\n window.setTimeout(() => setSavedOk(false), 2500);\n },\n },\n );\n };\n\n const loadIntoEditor = (schema: Record<string, unknown> | null) => {\n setText(schema ? JSON.stringify(schema, null, 2) : '');\n setDirty(true);\n setSavedOk(false);\n setJsonError(false);\n };\n\n if (isLoading) {\n return (\n <div className=\"animate-pulse space-y-4\">\n <div className=\"h-8 bg-surface-sunken rounded w-64\" />\n <div className=\"h-96 bg-surface-sunken rounded\" />\n </div>\n );\n }\n\n if (!role) {\n return (\n <div className=\"flex flex-col items-center justify-center py-20 text-center\">\n <Braces className=\"w-12 h-12 text-text-quaternary mb-4\" strokeWidth={1} />\n <h2 className=\"text-lg font-medium text-text-primary mb-2\">Role not found</h2>\n </div>\n );\n }\n\n return (\n <div>\n {/* ── Header ── */}\n <div className=\"flex items-start justify-between mb-10\">\n <div>\n <div className=\"flex items-center gap-3 mb-1\">\n <Braces className=\"w-5 h-5 text-accent\" strokeWidth={1.5} />\n <h1 className=\"text-lg font-medium text-text-primary\">Escalation Schema</h1>\n <button\n onClick={() => { window.location.hash = '#docs:dashboard.md:escalation-schema'; }}\n className=\"text-text-quaternary hover:text-accent transition-colors\"\n title=\"Open docs for this page\"\n >\n <BookOpen className=\"w-4 h-4\" strokeWidth={1.5} />\n </button>\n <span className=\"font-mono text-sm text-text-tertiary\">{role.role}</span>\n {role.current_schema_version != null && (\n <span className=\"text-[10px] font-mono text-text-quaternary\">\n v{role.current_schema_version} in use\n </span>\n )}\n </div>\n </div>\n\n <div className=\"flex items-center gap-2 shrink-0\">\n {savedOk && (\n <span className=\"flex items-center gap-1 text-xs text-status-success animate-page-enter\">\n <Check className=\"w-3 h-3\" /> Saved\n </span>\n )}\n {updateRole.error && (\n <span className=\"text-xs text-status-error max-w-[220px] truncate\">\n {(updateRole.error as Error).message}\n </span>\n )}\n <button\n onClick={handleSave}\n disabled={!dirty || jsonError || updateRole.isPending}\n className=\"px-3 py-1.5 text-xs rounded-md bg-accent text-text-inverse hover:bg-accent-hover transition-colors disabled:opacity-40 disabled:cursor-not-allowed\"\n >\n {updateRole.isPending ? 'Saving…' : 'Save Version'}\n </button>\n </div>\n </div>\n\n {/* ── Editor + version rail ── */}\n <div className=\"grid grid-cols-3 gap-16 items-start\">\n <div className=\"col-span-2 space-y-6\">\n <textarea\n value={text}\n onChange={(e) => handleEdit(e.target.value)}\n rows={28}\n spellCheck={false}\n className=\"input text-xs font-mono w-full resize-y leading-relaxed\"\n placeholder={PLACEHOLDER}\n />\n {jsonError && <p className=\"text-[10px] text-status-error -mt-4\">Invalid JSON</p>}\n\n <div>\n <label className=\"block text-[10px] font-semibold uppercase tracking-widest text-text-tertiary mb-1.5\">\n Change Summary\n </label>\n <input\n type=\"text\"\n value={summary}\n onChange={(e) => setSummary(e.target.value)}\n placeholder=\"e.g., Added lotNumber field for shoe orders\"\n className=\"input text-sm w-full\"\n />\n <p className=\"text-[10px] text-text-quaternary mt-1.5\">\n Recorded on the version this save creates.\n </p>\n </div>\n </div>\n\n {/* Version history — view any snapshot, load one as the editing base */}\n <div>\n <div className=\"flex items-center gap-2 mb-3 pb-2 border-b border-surface-border\">\n <History className=\"w-4 h-4 text-text-quaternary\" strokeWidth={1.5} />\n <h2 className=\"section-h2\">Versions</h2>\n </div>\n <VersionRail role={role.role} onLoad={loadIntoEditor} />\n </div>\n </div>\n </div>\n );\n}\n\nfunction VersionRail({\n role,\n onLoad,\n}: {\n role: string;\n onLoad: (schema: Record<string, unknown> | null) => void;\n}) {\n const { data } = useRoleSchemaVersions(role);\n const versions = data?.versions ?? [];\n\n if (versions.length === 0) {\n return (\n <p className=\"text-[11px] text-text-tertiary leading-relaxed\">\n Versions appear here after the first save. Each schema change adds one;\n earlier versions stay viewable and loadable as an editing base.\n </p>\n );\n }\n\n return (\n <div className=\"divide-y divide-surface-border/40\">\n {versions.map((v) => (\n <VersionEntry key={v.version} role={role} summary={v} onLoad={onLoad} />\n ))}\n </div>\n );\n}\n\nfunction VersionEntry({\n role,\n summary,\n onLoad,\n}: {\n role: string;\n summary: RoleSchemaVersionSummary;\n onLoad: (schema: Record<string, unknown> | null) => void;\n}) {\n const [open, setOpen] = useState(false);\n // Snapshot fetch is lazy — only when the entry is expanded.\n const snapshot = useRoleSchema(role, summary.version, open);\n\n return (\n <div className=\"py-2\">\n <button onClick={() => setOpen((o) => !o)} className=\"w-full text-left group\">\n <div className=\"flex items-center gap-2\">\n <span className=\"text-xs font-mono text-text-secondary\">v{summary.version}</span>\n {summary.is_current && (\n <span className=\"text-[9px] font-bold uppercase tracking-wider text-accent\">current</span>\n )}\n <span className=\"flex-1\" />\n <span className=\"text-[10px] text-text-quaternary shrink-0\">\n {new Date(summary.created_at).toLocaleDateString()}\n </span>\n </div>\n {summary.change_summary && (\n <p className=\"mt-0.5 text-[10px] text-text-tertiary truncate\">{summary.change_summary}</p>\n )}\n </button>\n {open && (\n <div className=\"mt-2 space-y-2\">\n {snapshot.data ? (\n <>\n {snapshot.data.form_schema\n ? <JsonViewer data={snapshot.data.form_schema} defaultCollapsed />\n : <p className=\"text-[10px] text-text-quaternary\">This version carries no form schema.</p>}\n <button\n onClick={() => onLoad((snapshot.data!.form_schema ?? null) as Record<string, unknown> | null)}\n className=\"text-[10px] text-accent hover:underline\"\n >\n Load into editor\n </button>\n </>\n ) : (\n <p className=\"text-[10px] text-text-quaternary\">Loading…</p>\n )}\n </div>\n )}\n </div>\n );\n}\n"],"names":["PLACEHOLDER","safeParseObject","text","trimmed","parsed","EscalationSchemaPage","roleKey","useParams","data","isLoading","useRoleDetails","updateRole","useUpdateRole","role","r","setText","useState","summary","setSummary","dirty","setDirty","savedOk","setSavedOk","jsonError","setJsonError","seededFor","useRef","useEffect","handleEdit","value","handleSave","loadIntoEditor","schema","jsxs","jsx","Braces","BookOpen","Check","e","History","VersionRail","onLoad","useRoleSchemaVersions","versions","v","VersionEntry","open","setOpen","snapshot","useRoleSchema","o","Fragment","JsonViewer"],"mappings":"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"}
@@ -1,2 +0,0 @@
1
- import{a as w,j as e}from"./vendor-query-B2UbickB.js";import{b as Pe}from"./roles-lHLgLwOY.js";import{O as ye}from"./index-MbEPoyTe.js";import{i as Se}from"./useEventHooks-CeOIM1Ix.js";import{P as Ce}from"./PageHeader-DJo7yMUR.js";import{X as Me,ae as ie,av as Le,ar as qe}from"./vendor-icons-DOIDKgo6.js";import{L as Q,f as Fe,c as Oe}from"./vendor-react-CXumBFUA.js";const ge=800,je=270,I=40,Ae=76,ce=24,Ee=72,oe=ge-I-Ae,be=je-ce-Ee,$=ce+be,he=I+oe,j="0.5s cubic-bezier(0.4, 0, 0.2, 1)",me=9;function We(t){return`M ${(t.x-me).toFixed(1)} ${t.y.toFixed(1)} L ${(t.x+me).toFixed(1)} ${t.y.toFixed(1)}`}function T(t){if(t.length===0)return"";if(t.length===1)return We(t[0]);const n=[];for(let l=0;l<t.length-1;l++){const i=t[Math.max(0,l-1)],c=t[l],p=t[l+1],a=t[Math.min(t.length-1,l+2)],d=(c.x+(p.x-i.x)/6).toFixed(1),u=(c.y+(p.y-i.y)/6).toFixed(1),o=(p.x-(a.x-c.x)/6).toFixed(1),x=(p.y-(a.y-c.y)/6).toFixed(1);l===0&&n.push(`M ${c.x.toFixed(1)} ${c.y.toFixed(1)}`),n.push(`C ${d} ${u}, ${o} ${x}, ${p.x.toFixed(1)} ${p.y.toFixed(1)}`)}return n.join(" ")}function Ie(t,n){if(t.length<2||n.length<2)return"";const l=T(t),i=[...n].reverse(),c=T(i),p=c.slice(c.indexOf("C"));return`${l} L ${i[0].x.toFixed(1)} ${i[0].y.toFixed(1)} ${p} Z`}function Te(t){return t>=1e4?`${Math.round(t/1e3)}k`:t>=1e3?`${(t/1e3).toFixed(1)}k`:String(t)}function ne(t){return t.length>0?t[t.length-1]:void 0}const O="#6366f1",D="#0ea5e9",q="#1E3A8A",X="#059669",ve="#b0e0e6",A="#5b93ad",De=13;function Be(t,n){const l=[...t].sort((a,d)=>a.y-d.y);let i=-1/0;const c=l.map(a=>{const d=Math.max(a.y+3,i+De);return i=d,{...a,labelY:d}}),p=c.length>0?c[c.length-1].labelY-n:0;if(p>0)for(const a of c)a.labelY-=p;return c}function ze({stations:t,selectedRole:n,onSelect:l,onUpstreamSelect:i,onPrioritySelect:c,periodHours:p}){const[a,d]=w.useState(null),u=t.length;if(u===0)return null;const o=s=>u===1?I+oe/2:I+s/(u-1)*oe,x=new Map(t.map((s,h)=>[s.role,o(h)])),y=t.map((s,h)=>{var P,V,G,U;const f=s.target_per_hour??null,C=f!=null?f*p:null;return{idx:h,x:o(h),expected:C,actual:((P=s.metric)==null?void 0:P.resolved)??0,active:((V=s.metric)==null?void 0:V.claimed)??0,pending:((G=s.metric)==null?void 0:G.pending)??0,priorityCount:((U=s.metric)==null?void 0:U.priority_count)??0}}),b=y.some(s=>s.expected!=null),v=Math.max(1,...y.map(s=>s.expected??0),...y.map(s=>s.actual),...y.map(s=>s.expected!=null?s.pending:0))*1.15,S=s=>ce+be*(1-Math.max(0,Math.min(s,v))/v),N=y.filter(s=>s.expected!=null),M=N.map(s=>({...s,y:S(s.expected)})),_=N.map(s=>({...s,y:S(s.actual)})),R=N.map(s=>({...s,y:S(s.active)})),k=N.map(s=>({...s,y:S(s.pending)})),B=T(M),z=T(_),H=T(R),Z=T(k),F=ne(_),K=ne(M),E=ne(R),J=ne(k),ee=_.length>=2&&F?`${z} L ${F.x.toFixed(1)} ${$} L ${_[0].x.toFixed(1)} ${$} Z`:"",W=N.some(s=>s.pending>0||s.active>0),te=W&&R.length>=2&&E?`${H} L ${E.x.toFixed(1)} ${$} L ${R[0].x.toFixed(1)} ${$} Z`:"",Y=W?Ie(k,R):"",r=N.some(s=>s.active>0),m=N.some(s=>s.pending>s.active),L=Be([K?{key:"target",text:"Target",color:X,y:K.y}:null,F?{key:"actual",text:"Resolved",color:q,y:F.y}:null,r&&E?{key:"active",text:"Claimed",color:O,y:E.y}:null,m&&J?{key:"queued",text:"Pending",color:D,y:J.y}:null].filter(Boolean),$+4);return e.jsxs("svg",{viewBox:`0 0 ${ge} ${je}`,className:"w-full h-full",preserveAspectRatio:"xMidYMid meet",style:{display:"block"},children:[t.map(s=>{if(!s.parent_role)return null;const h=x.get(s.parent_role);if(h==null)return null;const f=o(t.findIndex(C=>C.role===s.role));return e.jsx("line",{x1:h,y1:$,x2:f,y2:$,stroke:"#e2e8f0",strokeWidth:.75},`dep-${s.role}`)}),ee&&e.jsx("path",{d:ee,fill:q,opacity:.05,style:{transition:`d ${j}`}}),te&&e.jsx("path",{d:te,fill:O,opacity:.12,style:{transition:`d ${j}`}}),Y&&e.jsx("path",{d:Y,fill:D,opacity:.12,style:{transition:`d ${j}`}}),B&&e.jsx("path",{d:B,fill:"none",stroke:X,strokeWidth:.5,strokeLinejoin:"round",opacity:.9,style:{transition:`d ${j}`}}),M.map(s=>e.jsx("text",{x:s.x,y:s.y-3.5,textAnchor:"middle",fontSize:6.5,fill:X,fontFamily:"ui-monospace, monospace",fontWeight:"500",opacity:.7,style:{transition:`y ${j}`},children:Math.round(s.expected)},`tval-${s.idx}`)),W&&Z&&e.jsx("path",{d:Z,fill:"none",stroke:D,strokeWidth:.75,strokeDasharray:"2 3",strokeLinecap:"round",opacity:.55,style:{transition:`d ${j}`}}),H&&e.jsx("path",{d:H,fill:"none",stroke:O,strokeWidth:.75,strokeDasharray:"2 3",strokeLinecap:"round",opacity:.5,style:{transition:`d ${j}`}}),z&&e.jsx("path",{d:z,fill:"none",stroke:q,strokeWidth:.6,strokeLinejoin:"round",strokeLinecap:"round",opacity:.65,style:{transition:`d ${j}`}}),R.map((s,h)=>{if(s.pending<=0&&s.active<=0)return null;const f=Math.max(2.5,Math.min(5,2.5+s.active/10)),C=s.pending>s.active,P=Math.min(k[h].y-4,s.y-f-4);return e.jsxs("g",{children:[s.pending>0&&e.jsx("text",{x:s.x,y:P,textAnchor:"middle",fontSize:7.5,fill:C?D:"#64748b",fontFamily:"ui-monospace, monospace",fontWeight:"600",style:{transition:`y ${j}`},children:s.pending}),e.jsxs("g",{transform:`translate(${s.x} ${s.y})`,style:{transition:`transform ${j}`},children:[e.jsx("circle",{r:f,fill:O,opacity:.85}),s.active>0&&e.jsx("text",{y:f+9,textAnchor:"middle",fontSize:7.5,fill:O,fontFamily:"ui-monospace, monospace",fontWeight:"600",children:s.active})]})]},`active-${s.idx}`)}),y.map(s=>{var ue;const h=t[s.idx],f=s.x,C=s.expected!=null?S(s.actual):$,P=Math.max(2.25,Math.min(4,2.25+s.actual/160)),V=h.role===n,G=a===s.idx,U=s.expected==null?"idle · set a target rate to plot pace":`${s.actual} resolved · target ${Math.round(s.expected)}`+(s.pending>s.active?` · ${s.pending-s.active} pending`:"")+(s.active>0?` · ${s.active} claimed`:"")+(s.priorityCount>0?` · ${s.priorityCount} priority`:""),se=U.length*5.5+20,de=Math.max(I+se/2+4,Math.min(he-se/2-4,f)),pe=C-P-10;return e.jsxs("g",{onClick:()=>l(h.role),onMouseEnter:()=>d(s.idx),onMouseLeave:()=>d(null),style:{cursor:"pointer"},children:[e.jsxs("g",{transform:`translate(${f} ${C})`,style:{transition:`transform ${j}`},children:[e.jsx("circle",{r:22,fill:"transparent"}),e.jsx("circle",{r:P,fill:q,opacity:.9,style:{transition:`r ${j}`}}),s.expected!=null&&e.jsx("text",{y:-P-5,textAnchor:"middle",fontSize:9.5,fill:q,fontFamily:"ui-monospace, monospace",fontWeight:"500",children:Te(s.actual)}),G&&!V&&e.jsx("circle",{r:P+3,fill:"none",stroke:q,strokeWidth:1,opacity:.4}),V&&e.jsx("circle",{r:P+4,fill:"none",stroke:"#6366f1",strokeWidth:2})]}),s.priorityCount>0&&e.jsxs("g",{transform:`translate(${f+15} ${Math.max(C-15,12)})`,onClick:le=>{le.stopPropagation(),c==null||c(h.role)},style:{cursor:"pointer",transition:`transform ${j}`},children:[e.jsx("title",{children:`${s.priorityCount} past the priority threshold — click to pull oldest first`}),e.jsx("circle",{r:6.5+(String(s.priorityCount).length-1)*2,fill:ve,opacity:.9}),e.jsx("text",{y:2.5,textAnchor:"middle",fontSize:7,fill:A,fontFamily:"ui-monospace, monospace",fontWeight:"700",children:s.priorityCount})]}),(((ue=h.upstream_roles)==null?void 0:ue.length)??0)>0&&e.jsxs("g",{transform:`translate(${f-20} ${$})`,onClick:le=>{le.stopPropagation(),i==null||i(h.upstream_roles[0])},style:{cursor:"pointer"},children:[e.jsx("title",{children:`Fed by ${h.upstream_roles.join(", ")} — click to view that sequence`}),e.jsx("circle",{r:9,cy:-6,fill:"transparent"}),e.jsx("path",{d:"M -7 -14 C -3 -14 -1 -9 0 -2",fill:"none",stroke:"#94a3b8",strokeWidth:1,strokeDasharray:"2 2",strokeLinecap:"round"}),e.jsx("path",{d:"M 0 -2 L 8 -2",stroke:"#94a3b8",strokeWidth:1,strokeLinecap:"round"}),e.jsx("path",{d:"M 8 -2 l -3.5 -2 v 4 z",fill:"#94a3b8"})]}),e.jsx("text",{x:f,y:$+14,textAnchor:"middle",fontSize:9.5,fill:"#475569",fontFamily:"ui-sans-serif, sans-serif",fontWeight:"500",children:h.title??h.role}),e.jsx("text",{x:f,y:$+26,textAnchor:"middle",fontSize:8,fill:"#94a3b8",fontFamily:"ui-monospace, monospace",children:h.role}),G&&e.jsxs("g",{children:[e.jsx("rect",{x:de-se/2,y:pe-16,width:se,height:19,rx:3,fill:"#0f172a",opacity:.88}),e.jsx("text",{x:de,y:pe-3,textAnchor:"middle",fontSize:8.5,fill:"white",fontFamily:"ui-monospace, monospace",children:U})]})]},h.role)}),!b&&e.jsx("text",{x:I+oe/2,y:$+48,textAnchor:"middle",fontSize:8.5,fill:"#94a3b8",fontFamily:"ui-sans-serif, sans-serif",children:"Set target_per_hour on each role in Admin → Roles to enable the pace chart"}),L.map(s=>e.jsx("text",{x:he+6,y:s.labelY,fontSize:8,fill:s.color,fontFamily:"ui-sans-serif, sans-serif",fontWeight:s.key==="actual"?700:500,opacity:s.key==="active"?.85:1,style:{transition:`y ${j}`},children:s.text},s.key))]})}function xe(t){const n=`/escalations/available?role=${encodeURIComponent(t.role)}`;if(t.priority_facet){const l=JSON.stringify([{field:`metadata.${t.priority_facet}`,direction:"asc"}]);return`${n}&orderBy=${encodeURIComponent(l)}`}return`${n}&sort_by=created_at&order=asc`}const Ne=["15m","1h","24h","7d","30d"],He=t=>Ne.includes(t);function ae(t){if(t==null)return"—";if(t<1){const n=Math.round(t*60);return n<=0?"< 1s":`${n}s`}return t<60?`${t.toFixed(0)}m`:`${(t/60).toFixed(1)}h`}function re({label:t,value:n}){return e.jsxs("div",{className:"flex items-center justify-between py-1",children:[e.jsx("span",{className:"text-[10px] text-text-tertiary uppercase tracking-wider",children:t}),e.jsx("span",{className:"text-xs font-mono font-medium text-text-primary tabular-nums",children:n})]})}function Ye({period:t,onChange:n}){return e.jsx("div",{className:"flex items-center gap-1 mb-4",children:Ne.map(l=>e.jsx("button",{onClick:()=>n(l),className:`px-2 py-0.5 text-[10px] font-mono rounded transition-colors ${t===l?"bg-accent/10 text-accent font-semibold":"text-text-quaternary hover:text-text-secondary"}`,children:l},l))})}function Ve({role:t,globalPeriod:n,onClose:l}){const[i,c]=w.useState(He(n)?n:"24h"),{data:p}=ye(i),a=p==null?void 0:p.stations.find(x=>x.role===t.role),d=t.sla_minutes??void 0,u=t.target_per_hour??void 0,o=t.worker_count??void 0;return e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"flex items-start justify-between mb-1",children:[e.jsxs("div",{className:"min-w-0 flex-1",children:[e.jsxs("div",{className:"flex items-baseline gap-1.5 flex-wrap",children:[e.jsx("span",{className:"text-sm font-mono font-bold text-text-primary",children:t.role}),t.title&&e.jsx("span",{className:"text-[10px] text-text-secondary",children:t.title})]}),t.description&&e.jsx("p",{className:"text-[10px] text-text-tertiary mt-0.5 line-clamp-2 leading-relaxed",children:t.description})]}),e.jsx("button",{onClick:l,className:"text-text-quaternary hover:text-text-secondary transition-colors mt-0.5 shrink-0 ml-2",children:e.jsx(Me,{className:"w-4 h-4"})})]}),e.jsxs(Q,{to:`/admin/roles/${encodeURIComponent(t.role)}`,className:"flex items-center gap-1 text-[10px] text-accent hover:underline mb-4",children:[e.jsx(ie,{className:"w-2.5 h-2.5"}),"Edit in Roles"]}),e.jsx(Ye,{period:i,onChange:c}),(u||d||o)&&e.jsxs("div",{className:"flex items-center gap-4 mb-4 text-[10px]",children:[u&&e.jsxs("div",{children:[e.jsxs("div",{className:"font-mono font-semibold text-text-primary",children:[u,"/h"]}),e.jsx("div",{className:"text-text-quaternary uppercase tracking-wider text-[8.5px]",children:"target"})]}),d&&e.jsxs("div",{children:[e.jsxs("div",{className:"font-mono font-semibold text-text-primary",children:[d,"m"]}),e.jsx("div",{className:"text-text-quaternary uppercase tracking-wider text-[8.5px]",children:"SLA"})]}),o&&e.jsxs("div",{children:[e.jsx("div",{className:"font-mono font-semibold text-text-primary",children:o}),e.jsx("div",{className:"text-text-quaternary uppercase tracking-wider text-[8.5px]",children:"workers"})]})]}),e.jsxs("div",{className:"grid grid-cols-2 gap-x-3",children:[e.jsxs("div",{children:[e.jsx("p",{className:"text-[9px] text-text-quaternary uppercase tracking-wider mb-1.5",children:"Wait (queue)"}),e.jsx(re,{label:"P99",value:ae((a==null?void 0:a.wait.p99)??null)}),e.jsx(re,{label:"avg",value:ae((a==null?void 0:a.wait.avg)??null)})]}),e.jsxs("div",{children:[e.jsx("p",{className:"text-[9px] text-text-quaternary uppercase tracking-wider mb-1.5",children:"Work (proc.)"}),e.jsx(re,{label:"P99",value:ae((a==null?void 0:a.work.p99)??null)}),e.jsx(re,{label:"avg",value:ae((a==null?void 0:a.work.avg)??null)})]})]}),e.jsxs("div",{className:"border-t border-surface-border/40 pt-2 mt-3 space-y-1.5",children:[d&&e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsx("span",{className:"text-[10px] text-text-tertiary",children:"SLA target"}),e.jsxs("span",{className:"text-xs font-mono text-text-secondary",children:[d,"m"]})]}),a&&a.priority_count>0&&e.jsxs(Q,{to:xe(t),className:"flex items-center justify-between group",children:[e.jsxs("span",{className:"text-[10px]",style:{color:A},children:[a.priority_count," priority — pull oldest first"]}),e.jsx(ie,{className:"w-3 h-3 transition-colors",style:{color:A}})]})]}),e.jsx("div",{className:"mt-4 border-t border-surface-border/40 pt-3",children:e.jsxs(Q,{to:`/escalations/available?role=${encodeURIComponent(t.role)}`,className:"flex items-center gap-1.5 text-[10px] text-accent hover:underline",children:["View full queue",e.jsx(ie,{className:"w-2.5 h-2.5"})]})})]})}function Ge({allMetrics:t,orderedRoles:n,period:l}){const i=t.reduce((o,x)=>o+x.pending,0),c=t.reduce((o,x)=>o+x.resolved,0),p=t.reduce((o,x)=>o+x.priority_count,0),a=new Map(t.map(o=>[o.role,o])),d=t.filter(o=>o.priority_count>0).length,u=t.filter(o=>o.pending>0).length;return e.jsxs(e.Fragment,{children:[e.jsxs("p",{className:"text-[9px] text-text-quaternary uppercase tracking-wider mb-3",children:["Pipeline · ",l]}),e.jsx("div",{className:"mb-4",children:d>0?e.jsxs("p",{className:"text-[11px]",style:{color:A},children:[d," station",d>1?"s":""," with priority items"]}):u>0?e.jsxs("p",{className:"text-[11px] text-status-warning",children:[u," station",u>1?"s":""," with backlog"]}):c>0?e.jsx("p",{className:"text-[11px] text-status-success",children:"Flowing — queue clear"}):e.jsx("p",{className:"text-[11px] text-text-quaternary",children:"Quiet — awaiting work"})}),e.jsxs("div",{className:"space-y-1.5 mb-4",children:[e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsx("span",{className:"text-[9px] text-text-quaternary uppercase tracking-wider",children:"Pending"}),e.jsx("span",{className:`text-xs font-mono tabular-nums ${i>0?"text-text-primary font-semibold":"text-text-quaternary"}`,children:i})]}),e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsxs("span",{className:"text-[9px] text-text-quaternary uppercase tracking-wider",children:["Resolved · ",l]}),e.jsx("span",{className:"text-xs font-mono tabular-nums text-text-secondary",children:c})]}),p>0&&e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsx("span",{className:"text-[9px] text-text-quaternary uppercase tracking-wider",children:"Priority"}),e.jsx("span",{className:"text-xs font-mono tabular-nums font-semibold",style:{color:A},children:p})]})]}),e.jsx("div",{className:"border-t border-surface-border/40 pt-2 mb-1",children:e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx("span",{className:"text-[8px] text-text-quaternary uppercase tracking-wider flex-1",children:"Station"}),e.jsx("span",{className:"text-[8px] text-text-quaternary uppercase tracking-wider w-8 text-right shrink-0",children:"pend"}),e.jsx("span",{className:"text-[8px] text-text-quaternary uppercase tracking-wider w-8 text-right shrink-0",children:"clmd"}),e.jsx("span",{className:"text-[8px] text-text-quaternary uppercase tracking-wider w-8 text-right shrink-0",children:"res"})]})}),e.jsx("div",{className:"space-y-0.5",children:n.map(o=>{const x=a.get(o.role),y=(x==null?void 0:x.pending)??0,b=(x==null?void 0:x.claimed)??0,g=(x==null?void 0:x.resolved)??0,v=(x==null?void 0:x.priority_count)??0,S=o.title||o.role,N=v>0,M=y>0;return e.jsxs("div",{className:"flex items-center gap-2 py-0.5",children:[e.jsxs("span",{className:`text-[10px] font-mono flex-1 truncate ${M?"text-text-primary":"text-text-quaternary"}`,children:[S,N&&e.jsxs("span",{className:"ml-1 text-[8px] font-semibold",style:{color:A},children:[v," PRIORITY"]})]}),e.jsx("span",{className:`text-[10px] font-mono tabular-nums w-8 text-right shrink-0 ${M?"text-text-primary font-semibold":"text-text-quaternary"}`,children:y>0?y:"—"}),e.jsx("span",{className:`text-[10px] font-mono tabular-nums w-8 text-right shrink-0 ${b>0?"text-accent":"text-text-quaternary"}`,children:b>0?b:"—"}),e.jsx("span",{className:`text-[10px] font-mono tabular-nums w-8 text-right shrink-0 ${g>0?"text-text-secondary":"text-text-quaternary"}`,children:g>0?g:"—"})]},o.role)})}),e.jsx("p",{className:"text-[9px] text-text-quaternary mt-5",children:"Select a station for queue detail."})]})}function Ue({role:t,allMetrics:n,orderedRoles:l,globalPeriod:i,onClose:c}){return e.jsx("div",{className:"w-[280px] shrink-0 px-6 py-8 flex flex-col overflow-y-auto min-h-0",children:t?e.jsx(Ve,{role:t,globalPeriod:i,onClose:c}):e.jsx(Ge,{allMetrics:n,orderedRoles:l,period:i})})}const ke=`${X}14`,$e=`${D}14`,we=`${O}14`,_e=`${q}14`,Qe=["15m","1h","24h","7d","30d"],Xe={"15m":.25,"1h":1,"24h":24,"7d":168,"30d":720};function Re(t){const n=t.filter(a=>a.ops_visible),l=new Map(n.map(a=>[a.role,a])),i=new Set,p=n.filter(a=>!a.parent_role||!l.has(a.parent_role)).map(a=>{const d=[],u=[{role:a,depth:0}];for(;u.length>0;){const o=u.shift();i.has(o.role.role)||(i.add(o.role.role),d.push(o),n.filter(x=>x.parent_role===o.role.role&&!i.has(x.role)).forEach(x=>u.push({role:x,depth:o.depth+1})))}return{origin:a,stations:d}});return n.filter(a=>!i.has(a.role)).forEach(a=>p.push({origin:a,stations:[{role:a,depth:0}]})),p.sort((a,d)=>d.stations.length-a.stations.length||a.origin.role.localeCompare(d.origin.role))}function fe(t){if(t==null)return"—";if(t<1){const n=Math.round(t*60);return n<=0?"< 1s":`${n}s`}return t<60?`${t.toFixed(0)}m`:`${(t/60).toFixed(1)}h`}function Ze(t,n){if(!n||t===0)return{pct:null,color:"bg-surface-border"};const l=t/n,i=Math.round(l*100),c=l>1?"bg-status-warning":l<.2?"bg-status-draft":"bg-status-success";return{pct:i,color:c}}function Ke({role:t,metric:n,selected:l,onClick:i}){var b;const c=(n==null?void 0:n.pending)??0,p=(n==null?void 0:n.claimed)??0,a=(n==null?void 0:n.resolved)??0,d=(n==null?void 0:n.priority_count)??0,u=t.target_per_hour??null,{pct:o,color:x}=Ze(c,u),y=o!=null?Math.min(100,o):0;return e.jsxs(e.Fragment,{children:[e.jsxs("div",{role:"button",tabIndex:0,onClick:i,onKeyDown:g=>{g.key==="Enter"&&i()},className:`grid items-center gap-4 pr-3 cursor-pointer transition-colors ${l?"border-l-2 !border-l-accent pl-2.5":"pl-3"}`,style:{gridTemplateColumns:"1fr 64px 56px 56px 60px 72px 72px 104px"},children:[e.jsxs("div",{className:"flex items-center gap-1.5 min-w-0 py-1.5",children:[e.jsx("span",{className:"font-mono font-bold text-text-primary truncate text-[11px]",children:t.role}),t.title&&e.jsx("span",{className:"text-[10px] text-text-tertiary truncate",children:t.title}),(((b=t.upstream_roles)==null?void 0:b.length)??0)>0&&e.jsx("span",{title:`Fed by ${t.upstream_roles.join(", ")}`,className:"shrink-0 leading-none",children:e.jsx(qe,{className:"w-3 h-3 text-text-quaternary"})})]}),e.jsx("div",{className:"self-stretch flex items-center justify-end px-1.5",style:{backgroundColor:ke},children:e.jsx("span",{className:`text-xs font-mono tabular-nums ${u!=null?"text-text-secondary":"text-text-quaternary"}`,children:u??"—"})}),e.jsx("div",{className:"self-stretch flex items-center justify-end px-1.5",style:{backgroundColor:$e},children:e.jsx("span",{className:`text-xs font-mono tabular-nums ${c>0?"text-text-primary font-semibold":"text-text-quaternary"}`,children:c})}),e.jsx("div",{className:"self-stretch flex items-center justify-end px-1.5",style:{backgroundColor:we},children:e.jsx("span",{className:`text-xs font-mono tabular-nums ${p>0?"text-accent font-semibold":"text-text-quaternary"}`,children:p})}),e.jsx("div",{className:"self-stretch flex items-center justify-end px-1.5",style:{backgroundColor:_e},children:e.jsx("span",{className:"text-xs font-mono tabular-nums text-text-quaternary",children:a})}),e.jsx("span",{className:"text-xs font-mono tabular-nums text-right py-1.5 text-text-secondary",children:fe((n==null?void 0:n.wait.p99)??null)}),e.jsx("span",{className:"text-xs font-mono tabular-nums text-right py-1.5 text-text-secondary",children:fe((n==null?void 0:n.work.p99)??null)}),e.jsxs("div",{className:"flex items-center gap-2 py-1.5",children:[e.jsx("div",{className:"w-12 h-1.5 bg-surface-sunken rounded-full overflow-hidden shrink-0",children:e.jsx("div",{className:`h-full rounded-full ${x}`,style:{width:`${y}%`}})}),e.jsx("span",{className:`text-[10px] font-mono tabular-nums ${o!=null&&o>100?"text-status-warning font-semibold":"text-text-quaternary"}`,children:o!=null?`${o}%`:"—"})]})]}),d>0&&e.jsxs("div",{className:"flex items-center gap-1.5 pb-2 pl-3.5",children:[e.jsx("span",{className:"w-2.5 h-2.5 rounded-full dot-ring shrink-0",style:{backgroundColor:ve}}),e.jsxs(Q,{to:xe(t),className:"text-[10px] hover:underline",style:{color:A},onClick:g=>g.stopPropagation(),children:[d," priority — pull oldest first →"]})]})]})}function Je(){const t=[{label:"ROLE"},{label:"TARGET/H",band:ke,hue:X},{label:"PENDING",band:$e,hue:D},{label:"CLAIMED",band:we,hue:O},{label:"RESOLVED",band:_e,hue:q},{label:"P99 WAIT"},{label:"P99 WORK"},{label:"TREND"}];return e.jsx("div",{className:"grid items-center gap-4 px-3 border-b border-surface-border mb-0.5",style:{gridTemplateColumns:"1fr 64px 56px 56px 60px 72px 72px 104px"},children:t.map((n,l)=>n.band?e.jsx("div",{className:"self-stretch flex items-center justify-end px-1.5",style:{backgroundColor:n.band},children:e.jsx("span",{className:"text-[9px] font-semibold uppercase tracking-wider",style:{color:n.hue},children:n.label})},n.label):e.jsx("span",{className:`text-[9px] font-semibold uppercase tracking-wider text-text-quaternary py-1.5 ${l>0&&l<7?"text-right":""}`,children:n.label},n.label))})}function et(){var Y;const[t,n]=w.useState("1h"),[l,i]=w.useState(null),[c,p]=w.useState(!1);Se();const{data:a,isLoading:d,refetch:u}=Pe(),{data:o,isLoading:x,refetch:y}=ye(t),b=(a==null?void 0:a.roles)??[],g=(o==null?void 0:o.stations)??[],v=w.useMemo(()=>Re(b),[b]),[S,N]=Fe(),M=S.get("fragment"),_=v.find(r=>r.origin.role===M)??v[0]??null,R=w.useCallback(r=>{i(null),N(m=>{const L=new URLSearchParams(m);return L.set("fragment",r),L})},[N]),k=(_==null?void 0:_.stations)??[],B=w.useMemo(()=>new Set(k.map(({role:r})=>r.role)),[k]),z=w.useMemo(()=>g.filter(r=>B.has(r.role)),[g,B]),H=w.useMemo(()=>k.map(({role:r})=>({role:r.role,title:r.title,parent_role:r.parent_role,target_per_hour:r.target_per_hour??null,upstream_roles:r.upstream_roles??[],metric:g.find(m=>m.role===r.role)})),[k,g]),Z=w.useCallback(r=>{const m=v.find(L=>L.stations.some(({role:s})=>s.role===r));m&&R(m.origin.role)},[v,R]),F=Oe(),K=w.useCallback(r=>{const m=b.find(L=>L.role===r);m&&F(xe(m))},[b,F]),E=((Y=k.find(({role:r})=>r.role===l))==null?void 0:Y.role)??null,J=d||x,ee=async()=>{p(!0),await Promise.all([u(),y()]),p(!1)},W=r=>i(m=>m===r?null:r),te=e.jsx("div",{className:"flex items-center gap-0.5",children:Qe.map(r=>e.jsx("button",{onClick:()=>n(r),className:`px-2.5 py-1 text-[10px] font-mono rounded transition-colors ${t===r?"bg-accent/10 text-accent font-semibold":"text-text-quaternary hover:text-text-secondary"}`,children:r},r))});return e.jsxs("div",{className:"flex flex-col flex-1 min-h-0",children:[e.jsx(Ce,{title:"Pace Board",docsHash:"#docs:dashboard.md:pace-board",center:te,actions:e.jsx("button",{onClick:ee,disabled:c,className:"text-text-quaternary hover:text-text-secondary transition-colors disabled:opacity-40",children:e.jsx(Le,{className:`w-3.5 h-3.5 ${c?"animate-spin":""}`})})}),J?e.jsxs("div",{className:"animate-pulse space-y-6 mt-4",children:[e.jsx("div",{className:"h-8 bg-surface-sunken rounded w-64"}),e.jsx("div",{className:"h-64 bg-surface-sunken rounded w-full"}),e.jsx("div",{className:"space-y-3",children:[1,2,3].map(r=>e.jsx("div",{className:"h-8 bg-surface-sunken rounded w-full"},r))})]}):v.length===0?e.jsxs("div",{className:"mt-8",children:[e.jsx("p",{className:"text-sm text-text-secondary mb-2",children:"Stations appear here once roles are marked visible in Operations."}),e.jsxs("p",{className:"text-xs text-text-tertiary",children:["Go to"," ",e.jsx(Q,{to:"/admin/roles",className:"text-accent hover:underline",children:"Roles"})," ","and enable ",e.jsx("strong",{children:"Visible in Operations"})," on roles that represent pipeline stations."]})]}):e.jsxs("div",{className:"flex flex-col flex-1 min-h-0",children:[v.length>1&&e.jsx("div",{className:"flex items-center gap-0.5 px-4 pt-2",children:v.map(r=>{const m=r.origin.role===(_==null?void 0:_.origin.role);return e.jsxs("button",{onClick:()=>R(r.origin.role),className:`px-2.5 py-1 text-[10px] font-mono rounded transition-colors ${m?"bg-accent/10 text-accent font-semibold":"text-text-quaternary hover:text-text-secondary"}`,children:[r.origin.title??r.origin.role,e.jsx("span",{className:`ml-1.5 tabular-nums ${m?"text-accent/60":"text-text-quaternary/60"}`,children:r.stations.length})]},r.origin.role)})}),e.jsxs("div",{className:"flex-1 min-h-0 flex items-stretch overflow-hidden",children:[e.jsx("div",{className:"flex-1 min-w-0 min-h-0 flex flex-col justify-center overflow-hidden p-4",children:e.jsx(ze,{stations:H,selectedRole:l,onSelect:W,onUpstreamSelect:Z,onPrioritySelect:K,periodHours:Xe[t]})}),e.jsx("div",{className:"w-px bg-surface-border shrink-0 self-stretch"}),e.jsx(Ue,{role:E,allMetrics:z,orderedRoles:k.map(r=>r.role),globalPeriod:t,onClose:()=>i(null)})]}),e.jsxs("div",{className:"max-h-[45vh] flex-none flex flex-col border-t border-surface-border overflow-hidden",children:[e.jsx(Je,{}),e.jsx("div",{className:"flex-1 min-h-0 overflow-y-auto divide-y divide-surface-border/30",children:k.map(({role:r})=>e.jsx(Ke,{role:r,metric:g.find(m=>m.role===r.role),selected:l===r.role,onClick:()=>W(r.role)},r.role))})]})]})]})}const it=Object.freeze(Object.defineProperty({__proto__:null,OperationsPage:et,buildFragments:Re},Symbol.toStringTag,{value:"Module"}));export{it as O,ze as P,Re as b,xe as p};
2
- //# sourceMappingURL=OperationsPage-Cz_0K8tj.js.map