@hotmeshio/long-tail 0.30.3 → 0.30.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 (261) hide show
  1. package/build/api/escalations/index.d.ts +1 -0
  2. package/build/api/escalations/index.js +5 -2
  3. package/build/api/escalations/roster-action.d.ts +11 -0
  4. package/build/api/escalations/roster-action.js +47 -30
  5. package/build/api/escalations/roster-scan.d.ts +26 -0
  6. package/build/api/escalations/roster-scan.js +114 -0
  7. package/build/api/scan-codes/context.d.ts +9 -1
  8. package/build/api/scan-codes/context.js +16 -1
  9. package/build/api/scan-codes/execute.js +6 -1
  10. package/build/api/scan-codes/fill-labels.d.ts +8 -0
  11. package/build/api/scan-codes/fill-labels.js +23 -2
  12. package/build/api/scan-codes/hold.js +2 -0
  13. package/build/api/scan-codes/located.d.ts +10 -0
  14. package/build/api/scan-codes/located.js +74 -0
  15. package/build/api/scan-codes/open.d.ts +9 -0
  16. package/build/api/scan-codes/open.js +99 -0
  17. package/build/api/scan-codes/present.js +1 -0
  18. package/build/api/scan-codes/verb-fill.js +1 -1
  19. package/build/api/scan-codes/verbs-subject.d.ts +3 -1
  20. package/build/api/scan-codes/verbs-subject.js +7 -4
  21. package/build/routes/escalations/resolve.js +18 -0
  22. package/build/services/role/index.d.ts +2 -0
  23. package/build/services/role/index.js +7 -0
  24. package/build/services/role/scan-done.js +4 -1
  25. package/build/services/role/sql.d.ts +1 -0
  26. package/build/services/role/sql.js +3 -1
  27. package/build/services/role/types.d.ts +2 -0
  28. package/build/services/scan-code/index.d.ts +1 -1
  29. package/build/services/scan-code/index.js +2 -1
  30. package/build/services/scan-code/parse.d.ts +5 -1
  31. package/build/services/scan-code/parse.js +8 -2
  32. package/build/services/scan-code/validate-station.d.ts +13 -0
  33. package/build/services/scan-code/validate-station.js +81 -0
  34. package/build/services/scan-code/validate-subject.js +33 -6
  35. package/build/services/scan-code/validate.js +12 -18
  36. package/build/system/mcp-servers/admin/schemas.d.ts +471 -4
  37. package/build/system/mcp-servers/admin/schemas.js +32 -4
  38. package/build/system/mcp-servers/human-queue-lookups.d.ts +7 -0
  39. package/build/system/mcp-servers/human-queue-lookups.js +31 -0
  40. package/build/system/mcp-servers/human-queue-schemas.d.ts +7 -0
  41. package/build/system/mcp-servers/human-queue-schemas.js +4 -1
  42. package/build/system/mcp-servers/human-queue.d.ts +1 -0
  43. package/build/system/mcp-servers/human-queue.js +3 -0
  44. package/build/system/seed/tool-manifests-escalation.js +2 -2
  45. package/build/tsconfig.tsbuildinfo +1 -1
  46. package/build/types/index.d.ts +1 -1
  47. package/build/types/scan-choice.d.ts +2 -4
  48. package/build/types/scan-code/constants.d.ts +1 -0
  49. package/build/types/scan-code/constants.js +1 -0
  50. package/build/types/scan-code/response.d.ts +22 -4
  51. package/build/types/scan-code/rule.d.ts +62 -4
  52. package/dashboard/dist/assets/{AdminDashboard-VQ1fhhJP.js → AdminDashboard-D12QiMM5.js} +2 -2
  53. package/dashboard/dist/assets/{AdminDashboard-VQ1fhhJP.js.map → AdminDashboard-D12QiMM5.js.map} +1 -1
  54. package/dashboard/dist/assets/{AgentConfigPage-BOe_W2Ij.js → AgentConfigPage-BKQd9b8m.js} +2 -2
  55. package/dashboard/dist/assets/{AgentConfigPage-BOe_W2Ij.js.map → AgentConfigPage-BKQd9b8m.js.map} +1 -1
  56. package/dashboard/dist/assets/{AgentDetailPage-5ov5D9V2.js → AgentDetailPage-ie-DxsIj.js} +3 -3
  57. package/dashboard/dist/assets/{AgentDetailPage-5ov5D9V2.js.map → AgentDetailPage-ie-DxsIj.js.map} +1 -1
  58. package/dashboard/dist/assets/{AgentsPage-DDFMKHhI.js → AgentsPage-Nqm2DxCP.js} +2 -2
  59. package/dashboard/dist/assets/{AgentsPage-DDFMKHhI.js.map → AgentsPage-Nqm2DxCP.js.map} +1 -1
  60. package/dashboard/dist/assets/{AvailableEscalationsPage-GFn8Sgg1.js → AvailableEscalationsPage-BRKyG5qZ.js} +2 -2
  61. package/dashboard/dist/assets/{AvailableEscalationsPage-GFn8Sgg1.js.map → AvailableEscalationsPage-BRKyG5qZ.js.map} +1 -1
  62. package/dashboard/dist/assets/{BotPicker-Dqpo36dM.js → BotPicker-BizdmeJp.js} +2 -2
  63. package/dashboard/dist/assets/{BotPicker-Dqpo36dM.js.map → BotPicker-BizdmeJp.js.map} +1 -1
  64. package/dashboard/dist/assets/{BulkAssignModal-CmLVxkz-.js → BulkAssignModal-BWfzPrRk.js} +2 -2
  65. package/dashboard/dist/assets/{BulkAssignModal-CmLVxkz-.js.map → BulkAssignModal-BWfzPrRk.js.map} +1 -1
  66. package/dashboard/dist/assets/{CapabilitiesPage-Dfkip6_b.js → CapabilitiesPage-DT8q7Zlh.js} +2 -2
  67. package/dashboard/dist/assets/{CapabilitiesPage-Dfkip6_b.js.map → CapabilitiesPage-DT8q7Zlh.js.map} +1 -1
  68. package/dashboard/dist/assets/{CollapsibleSection-CYNYMYSa.js → CollapsibleSection-al5mqR4O.js} +2 -2
  69. package/dashboard/dist/assets/{CollapsibleSection-CYNYMYSa.js.map → CollapsibleSection-al5mqR4O.js.map} +1 -1
  70. package/dashboard/dist/assets/{ConfirmDeleteModal-CpJNP-8w.js → ConfirmDeleteModal-BrlVktv2.js} +2 -2
  71. package/dashboard/dist/assets/{ConfirmDeleteModal-CpJNP-8w.js.map → ConfirmDeleteModal-BrlVktv2.js.map} +1 -1
  72. package/dashboard/dist/assets/{ConnectedAppsPage-DXIp2Dpl.js → ConnectedAppsPage-CLAsJ2yQ.js} +2 -2
  73. package/dashboard/dist/assets/{ConnectedAppsPage-DXIp2Dpl.js.map → ConnectedAppsPage-CLAsJ2yQ.js.map} +1 -1
  74. package/dashboard/dist/assets/{CountdownTimer-CuPY7zkb.js → CountdownTimer-D2D68exl.js} +2 -2
  75. package/dashboard/dist/assets/{CountdownTimer-CuPY7zkb.js.map → CountdownTimer-D2D68exl.js.map} +1 -1
  76. package/dashboard/dist/assets/{CredentialsPage-CtrnkHcj.js → CredentialsPage-DWrZbEI0.js} +2 -2
  77. package/dashboard/dist/assets/{CredentialsPage-CtrnkHcj.js.map → CredentialsPage-DWrZbEI0.js.map} +1 -1
  78. package/dashboard/dist/assets/{CronLabel-DVjTw5Ri.js → CronLabel-DuXR8iWq.js} +2 -2
  79. package/dashboard/dist/assets/{CronLabel-DVjTw5Ri.js.map → CronLabel-DuXR8iWq.js.map} +1 -1
  80. package/dashboard/dist/assets/{ElapsedCell-gY_mGMTF.js → ElapsedCell-ronqG5-E.js} +2 -2
  81. package/dashboard/dist/assets/{ElapsedCell-gY_mGMTF.js.map → ElapsedCell-ronqG5-E.js.map} +1 -1
  82. package/dashboard/dist/assets/{EntityTimelinePanel-DJN_ZEQa.js → EntityTimelinePanel-CKsktd84.js} +2 -2
  83. package/dashboard/dist/assets/{EntityTimelinePanel-DJN_ZEQa.js.map → EntityTimelinePanel-CKsktd84.js.map} +1 -1
  84. package/dashboard/dist/assets/{EscalationListSchemaPage-A4KpTdJA.js → EscalationListSchemaPage-C954ngy3.js} +3 -3
  85. package/dashboard/dist/assets/{EscalationListSchemaPage-A4KpTdJA.js.map → EscalationListSchemaPage-C954ngy3.js.map} +1 -1
  86. package/dashboard/dist/assets/{EscalationSchemaPage-nfCn42hT.js → EscalationSchemaPage-Dij9K0ON.js} +2 -2
  87. package/dashboard/dist/assets/{EscalationSchemaPage-nfCn42hT.js.map → EscalationSchemaPage-Dij9K0ON.js.map} +1 -1
  88. package/dashboard/dist/assets/{EscalationTimeline-CLvSr1SD.js → EscalationTimeline-DT2Owdl0.js} +2 -2
  89. package/dashboard/dist/assets/{EscalationTimeline-CLvSr1SD.js.map → EscalationTimeline-DT2Owdl0.js.map} +1 -1
  90. package/dashboard/dist/assets/{EscalationTitleSelect-CT7mYHkb.js → EscalationTitleSelect-BdbfMA98.js} +2 -2
  91. package/dashboard/dist/assets/{EscalationTitleSelect-CT7mYHkb.js.map → EscalationTitleSelect-BdbfMA98.js.map} +1 -1
  92. package/dashboard/dist/assets/{EscalationsOverview-CKaGRh-E.js → EscalationsOverview-niahs84P.js} +2 -2
  93. package/dashboard/dist/assets/{EscalationsOverview-CKaGRh-E.js.map → EscalationsOverview-niahs84P.js.map} +1 -1
  94. package/dashboard/dist/assets/{EventTable-2ywJsOLy.js → EventTable-DuLMBrSx.js} +2 -2
  95. package/dashboard/dist/assets/{EventTable-2ywJsOLy.js.map → EventTable-DuLMBrSx.js.map} +1 -1
  96. package/dashboard/dist/assets/{FilterBar-Dy0b7wwf.js → FilterBar-jtY6KNpL.js} +2 -2
  97. package/dashboard/dist/assets/{FilterBar-Dy0b7wwf.js.map → FilterBar-jtY6KNpL.js.map} +1 -1
  98. package/dashboard/dist/assets/{GraphInvokePage-yOVpraAy.js → GraphInvokePage-DSRAI0Zt.js} +2 -2
  99. package/dashboard/dist/assets/{GraphInvokePage-yOVpraAy.js.map → GraphInvokePage-DSRAI0Zt.js.map} +1 -1
  100. package/dashboard/dist/assets/{HomePage-67xXjKjN.js → HomePage-BUlSnCg8.js} +2 -2
  101. package/dashboard/dist/assets/{HomePage-67xXjKjN.js.map → HomePage-BUlSnCg8.js.map} +1 -1
  102. package/dashboard/dist/assets/{LinePage-CMsYtukW.js → LinePage-BJKhVM9Y.js} +3 -3
  103. package/dashboard/dist/assets/{LinePage-CMsYtukW.js.map → LinePage-BJKhVM9Y.js.map} +1 -1
  104. package/dashboard/dist/assets/{LinesPage-pwL46mOS.js → LinesPage-CrpJua3_.js} +3 -3
  105. package/dashboard/dist/assets/{LinesPage-pwL46mOS.js.map → LinesPage-CrpJua3_.js.map} +1 -1
  106. package/dashboard/dist/assets/{ListToolbar-CJmvGy2B.js → ListToolbar-BN0UPt0L.js} +2 -2
  107. package/dashboard/dist/assets/{ListToolbar-CJmvGy2B.js.map → ListToolbar-BN0UPt0L.js.map} +1 -1
  108. package/dashboard/dist/assets/{McpOverview-DA9LLvNG.js → McpOverview-BgbJXYPh.js} +2 -2
  109. package/dashboard/dist/assets/{McpOverview-DA9LLvNG.js.map → McpOverview-BgbJXYPh.js.map} +1 -1
  110. package/dashboard/dist/assets/{McpQueryDetailPage-N7qLmfE8.js → McpQueryDetailPage-BNjm2nrc.js} +2 -2
  111. package/dashboard/dist/assets/{McpQueryDetailPage-N7qLmfE8.js.map → McpQueryDetailPage-BNjm2nrc.js.map} +1 -1
  112. package/dashboard/dist/assets/{McpQueryPage-CpsP3w7x.js → McpQueryPage-DplR8wP9.js} +2 -2
  113. package/dashboard/dist/assets/{McpQueryPage-CpsP3w7x.js.map → McpQueryPage-DplR8wP9.js.map} +1 -1
  114. package/dashboard/dist/assets/{McpRunDetailPage-qHRQ1pEY.js → McpRunDetailPage-BHVEcKV5.js} +2 -2
  115. package/dashboard/dist/assets/{McpRunDetailPage-qHRQ1pEY.js.map → McpRunDetailPage-BHVEcKV5.js.map} +1 -1
  116. package/dashboard/dist/assets/{McpRunsPage-BRDDYpTM.js → McpRunsPage-CtlLagcs.js} +2 -2
  117. package/dashboard/dist/assets/{McpRunsPage-BRDDYpTM.js.map → McpRunsPage-CtlLagcs.js.map} +1 -1
  118. package/dashboard/dist/assets/{OperationsPage-zUaab-CL.js → OperationsPage-DH67bcW9.js} +3 -3
  119. package/dashboard/dist/assets/{OperationsPage-zUaab-CL.js.map → OperationsPage-DH67bcW9.js.map} +1 -1
  120. package/dashboard/dist/assets/{OperatorDashboard-el5BquXN.js → OperatorDashboard-CMkgMewd.js} +2 -2
  121. package/dashboard/dist/assets/{OperatorDashboard-el5BquXN.js.map → OperatorDashboard-CMkgMewd.js.map} +1 -1
  122. package/dashboard/dist/assets/{PersonaDetailPage-B_tb8gfl.js → PersonaDetailPage-Cs1FFdNq.js} +2 -2
  123. package/dashboard/dist/assets/{PersonaDetailPage-B_tb8gfl.js.map → PersonaDetailPage-Cs1FFdNq.js.map} +1 -1
  124. package/dashboard/dist/assets/{PersonasPage-BPI7mqM8.js → PersonasPage-CHTRdneF.js} +2 -2
  125. package/dashboard/dist/assets/{PersonasPage-BPI7mqM8.js.map → PersonasPage-CHTRdneF.js.map} +1 -1
  126. package/dashboard/dist/assets/{PortalPage-e_rBb1do.js → PortalPage-ChB5pBTZ.js} +2 -2
  127. package/dashboard/dist/assets/{PortalPage-e_rBb1do.js.map → PortalPage-ChB5pBTZ.js.map} +1 -1
  128. package/dashboard/dist/assets/{ProcessDetailPage-D35RX1bB.js → ProcessDetailPage-CzyqSfal.js} +2 -2
  129. package/dashboard/dist/assets/{ProcessDetailPage-D35RX1bB.js.map → ProcessDetailPage-CzyqSfal.js.map} +1 -1
  130. package/dashboard/dist/assets/{ProcessesListPage-CT81miI5.js → ProcessesListPage-rOwNkaU9.js} +2 -2
  131. package/dashboard/dist/assets/{ProcessesListPage-CT81miI5.js.map → ProcessesListPage-rOwNkaU9.js.map} +1 -1
  132. package/dashboard/dist/assets/{ResolverForm-BxJkoKAO.js → ResolverForm-DibnlZ-A.js} +2 -2
  133. package/dashboard/dist/assets/{ResolverForm-BxJkoKAO.js.map → ResolverForm-DibnlZ-A.js.map} +1 -1
  134. package/dashboard/dist/assets/{RoleDetailPage-DtU7vsJm.js → RoleDetailPage-Bv9R7iQC.js} +3 -3
  135. package/dashboard/dist/assets/{RoleDetailPage-DtU7vsJm.js.map → RoleDetailPage-Bv9R7iQC.js.map} +1 -1
  136. package/dashboard/dist/assets/{RolesPage-BE7RWt9q.js → RolesPage-CUP7EH5U.js} +2 -2
  137. package/dashboard/dist/assets/{RolesPage-BE7RWt9q.js.map → RolesPage-CUP7EH5U.js.map} +1 -1
  138. package/dashboard/dist/assets/{RosterWidget-BJMx0a8Z.js → RosterWidget-DZtmQcjU.js} +2 -2
  139. package/dashboard/dist/assets/RosterWidget-DZtmQcjU.js.map +1 -0
  140. package/dashboard/dist/assets/{RunAsSelector-Co6s9bqA.js → RunAsSelector-B9-1vgZr.js} +2 -2
  141. package/dashboard/dist/assets/{RunAsSelector-Co6s9bqA.js.map → RunAsSelector-B9-1vgZr.js.map} +1 -1
  142. package/dashboard/dist/assets/{ScanCodesPage-C2TETaj5.js → ScanCodesPage-KN_e2rJl.js} +2 -2
  143. package/dashboard/dist/assets/{ScanCodesPage-C2TETaj5.js.map → ScanCodesPage-KN_e2rJl.js.map} +1 -1
  144. package/dashboard/dist/assets/{ScanSchemeDetailPage-C2UgU8l-.js → ScanSchemeDetailPage-CqItQ67Y.js} +4 -4
  145. package/dashboard/dist/assets/{ScanSchemeDetailPage-C2UgU8l-.js.map → ScanSchemeDetailPage-CqItQ67Y.js.map} +1 -1
  146. package/dashboard/dist/assets/ScanStationPage-B0qbUOwc.js +2 -0
  147. package/dashboard/dist/assets/ScanStationPage-B0qbUOwc.js.map +1 -0
  148. package/dashboard/dist/assets/{SchemeMatchFields-9H7SmbED.js → SchemeMatchFields-CL7TKnw3.js} +2 -2
  149. package/dashboard/dist/assets/{SchemeMatchFields-9H7SmbED.js.map → SchemeMatchFields-CL7TKnw3.js.map} +1 -1
  150. package/dashboard/dist/assets/{StickyPagination-miUdVAKw.js → StickyPagination-CUwxfBQH.js} +2 -2
  151. package/dashboard/dist/assets/{StickyPagination-miUdVAKw.js.map → StickyPagination-CUwxfBQH.js.map} +1 -1
  152. package/dashboard/dist/assets/{StreamMessageDetail-D5Rk7pn7.js → StreamMessageDetail-DOjcO7d2.js} +2 -2
  153. package/dashboard/dist/assets/{StreamMessageDetail-D5Rk7pn7.js.map → StreamMessageDetail-DOjcO7d2.js.map} +1 -1
  154. package/dashboard/dist/assets/{SwimlaneTimeline-B1QY1ZzH.js → SwimlaneTimeline-SPMpoLHP.js} +2 -2
  155. package/dashboard/dist/assets/{SwimlaneTimeline-B1QY1ZzH.js.map → SwimlaneTimeline-SPMpoLHP.js.map} +1 -1
  156. package/dashboard/dist/assets/{TaskDetailPage-DKUM37o8.js → TaskDetailPage-DvfzR2IQ.js} +2 -2
  157. package/dashboard/dist/assets/{TaskDetailPage-DKUM37o8.js.map → TaskDetailPage-DvfzR2IQ.js.map} +1 -1
  158. package/dashboard/dist/assets/{TasksListPage-CUulwvm-.js → TasksListPage-Jp6wmL7t.js} +2 -2
  159. package/dashboard/dist/assets/{TasksListPage-CUulwvm-.js.map → TasksListPage-Jp6wmL7t.js.map} +1 -1
  160. package/dashboard/dist/assets/{TimeAgo-CHTelacB.js → TimeAgo-BCQA0Gb_.js} +2 -2
  161. package/dashboard/dist/assets/{TimeAgo-CHTelacB.js.map → TimeAgo-BCQA0Gb_.js.map} +1 -1
  162. package/dashboard/dist/assets/{TimestampCell-NanLWLKo.js → TimestampCell-xhdMHDQt.js} +2 -2
  163. package/dashboard/dist/assets/{TimestampCell-NanLWLKo.js.map → TimestampCell-xhdMHDQt.js.map} +1 -1
  164. package/dashboard/dist/assets/{ToolTestPanel-CF1CHzgz.js → ToolTestPanel-D8M3ITrJ.js} +2 -2
  165. package/dashboard/dist/assets/{ToolTestPanel-CF1CHzgz.js.map → ToolTestPanel-D8M3ITrJ.js.map} +1 -1
  166. package/dashboard/dist/assets/{TopicDetailPage-CGO8XV_a.js → TopicDetailPage-DdBJQp31.js} +2 -2
  167. package/dashboard/dist/assets/{TopicDetailPage-CGO8XV_a.js.map → TopicDetailPage-DdBJQp31.js.map} +1 -1
  168. package/dashboard/dist/assets/{TopicsPage-Bt8gOKYr.js → TopicsPage-vpvsynD9.js} +2 -2
  169. package/dashboard/dist/assets/{TopicsPage-Bt8gOKYr.js.map → TopicsPage-vpvsynD9.js.map} +1 -1
  170. package/dashboard/dist/assets/{UserName-DKXnK7DD.js → UserName-BK6Lzsdw.js} +2 -2
  171. package/dashboard/dist/assets/{UserName-DKXnK7DD.js.map → UserName-BK6Lzsdw.js.map} +1 -1
  172. package/dashboard/dist/assets/{WorkflowExecutionPage-Crf7AIFc.js → WorkflowExecutionPage-DkM1TS4I.js} +2 -2
  173. package/dashboard/dist/assets/{WorkflowExecutionPage-Crf7AIFc.js.map → WorkflowExecutionPage-DkM1TS4I.js.map} +1 -1
  174. package/dashboard/dist/assets/{WorkflowsDashboard--isjt06C.js → WorkflowsDashboard-g_N4y3QS.js} +2 -2
  175. package/dashboard/dist/assets/{WorkflowsDashboard--isjt06C.js.map → WorkflowsDashboard-g_N4y3QS.js.map} +1 -1
  176. package/dashboard/dist/assets/{WorkflowsOverview-D7Oqf4-V.js → WorkflowsOverview-Drjo-Lu-.js} +2 -2
  177. package/dashboard/dist/assets/{WorkflowsOverview-D7Oqf4-V.js.map → WorkflowsOverview-Drjo-Lu-.js.map} +1 -1
  178. package/dashboard/dist/assets/{YamlWorkflowDetailPage-B0wxgrGP.js → YamlWorkflowDetailPage-CLltNHjZ.js} +2 -2
  179. package/dashboard/dist/assets/{YamlWorkflowDetailPage-B0wxgrGP.js.map → YamlWorkflowDetailPage-CLltNHjZ.js.map} +1 -1
  180. package/dashboard/dist/assets/{YamlWorkflowsPage-BVdLnm6K.js → YamlWorkflowsPage-CxQCB-Zi.js} +2 -2
  181. package/dashboard/dist/assets/{YamlWorkflowsPage-BVdLnm6K.js.map → YamlWorkflowsPage-CxQCB-Zi.js.map} +1 -1
  182. package/dashboard/dist/assets/{agents-CHjbjHXA.js → agents-Dk25jjhj.js} +2 -2
  183. package/dashboard/dist/assets/{agents-CHjbjHXA.js.map → agents-Dk25jjhj.js.map} +1 -1
  184. package/dashboard/dist/assets/{bots-WrUhEPWs.js → bots-bRkCqhlq.js} +2 -2
  185. package/dashboard/dist/assets/{bots-WrUhEPWs.js.map → bots-bRkCqhlq.js.map} +1 -1
  186. package/dashboard/dist/assets/{capabilities-CunMmkj8.js → capabilities-DARawLry.js} +2 -2
  187. package/dashboard/dist/assets/{capabilities-CunMmkj8.js.map → capabilities-DARawLry.js.map} +1 -1
  188. package/dashboard/dist/assets/{controlplane-D6IZIZoh.js → controlplane-DKccxxs6.js} +2 -2
  189. package/dashboard/dist/assets/{controlplane-D6IZIZoh.js.map → controlplane-DKccxxs6.js.map} +1 -1
  190. package/dashboard/dist/assets/{escalation-columns-Bvo6JI4Z.js → escalation-columns-BaYuzMYf.js} +2 -2
  191. package/dashboard/dist/assets/{escalation-columns-Bvo6JI4Z.js.map → escalation-columns-BaYuzMYf.js.map} +1 -1
  192. package/dashboard/dist/assets/{escalation-items-DFGRwo49.js → escalation-items-CwbNFnsH.js} +2 -2
  193. package/dashboard/dist/assets/{escalation-items-DFGRwo49.js.map → escalation-items-CwbNFnsH.js.map} +1 -1
  194. package/dashboard/dist/assets/{index-B8qrHwp9.js → index-BZgtzkkG.js} +2 -2
  195. package/dashboard/dist/assets/{index-B8qrHwp9.js.map → index-BZgtzkkG.js.map} +1 -1
  196. package/dashboard/dist/assets/{index-DRw0FXdX.js → index-Bx9HVzuT.js} +2 -2
  197. package/dashboard/dist/assets/{index-DRw0FXdX.js.map → index-Bx9HVzuT.js.map} +1 -1
  198. package/dashboard/dist/assets/{index-ygxyAKcc.js → index-CU2UW2y6.js} +14 -14
  199. package/dashboard/dist/assets/index-CU2UW2y6.js.map +1 -0
  200. package/dashboard/dist/assets/{index-OuKQ_0eB.js → index-Cf6qCgTf.js} +2 -2
  201. package/dashboard/dist/assets/{index-OuKQ_0eB.js.map → index-Cf6qCgTf.js.map} +1 -1
  202. package/dashboard/dist/assets/{index-DRK7M1_9.js → index-CtrSYqmL.js} +2 -2
  203. package/dashboard/dist/assets/{index-DRK7M1_9.js.map → index-CtrSYqmL.js.map} +1 -1
  204. package/dashboard/dist/assets/{index-Bo8QU31k.js → index-D1uliPqS.js} +2 -2
  205. package/dashboard/dist/assets/{index-Bo8QU31k.js.map → index-D1uliPqS.js.map} +1 -1
  206. package/dashboard/dist/assets/{index-B95LbYhW.js → index-DU-1gMks.js} +2 -2
  207. package/dashboard/dist/assets/{index-B95LbYhW.js.map → index-DU-1gMks.js.map} +1 -1
  208. package/dashboard/dist/assets/{index-CpCchhQv.js → index-DU2QCbgH.js} +2 -2
  209. package/dashboard/dist/assets/{index-CpCchhQv.js.map → index-DU2QCbgH.js.map} +1 -1
  210. package/dashboard/dist/assets/{index-BZFSXEkr.js → index-DgpL_Lgc.js} +2 -2
  211. package/dashboard/dist/assets/{index-BZFSXEkr.js.map → index-DgpL_Lgc.js.map} +1 -1
  212. package/dashboard/dist/assets/{index-boT612Jo.js → index-E_hJ7vtX.js} +2 -2
  213. package/dashboard/dist/assets/{index-boT612Jo.js.map → index-E_hJ7vtX.js.map} +1 -1
  214. package/dashboard/dist/assets/{index-DCJ483vt.js → index-dShcvdg3.js} +2 -2
  215. package/dashboard/dist/assets/{index-DCJ483vt.js.map → index-dShcvdg3.js.map} +1 -1
  216. package/dashboard/dist/assets/{index-C7EIu1mG.js → index-hKOlahpE.js} +2 -2
  217. package/dashboard/dist/assets/{index-C7EIu1mG.js.map → index-hKOlahpE.js.map} +1 -1
  218. package/dashboard/dist/assets/index-kzifJWbM.js +3 -0
  219. package/dashboard/dist/assets/index-kzifJWbM.js.map +1 -0
  220. package/dashboard/dist/assets/{knowledge-BubJBTwg.js → knowledge-DOe5ks-E.js} +2 -2
  221. package/dashboard/dist/assets/{knowledge-BubJBTwg.js.map → knowledge-DOe5ks-E.js.map} +1 -1
  222. package/dashboard/dist/assets/{machines-DSm5NFgs.js → machines-Zq_00v7k.js} +2 -2
  223. package/dashboard/dist/assets/{machines-DSm5NFgs.js.map → machines-Zq_00v7k.js.map} +1 -1
  224. package/dashboard/dist/assets/{mcp-D6MckqOV.js → mcp-BL8kD0oh.js} +2 -2
  225. package/dashboard/dist/assets/{mcp-D6MckqOV.js.map → mcp-BL8kD0oh.js.map} +1 -1
  226. package/dashboard/dist/assets/{mcp-query-VyCdOag6.js → mcp-query-BpokmJWi.js} +2 -2
  227. package/dashboard/dist/assets/{mcp-query-VyCdOag6.js.map → mcp-query-BpokmJWi.js.map} +1 -1
  228. package/dashboard/dist/assets/{personas-CyJc10xe.js → personas-BubCnQZ8.js} +2 -2
  229. package/dashboard/dist/assets/{personas-CyJc10xe.js.map → personas-BubCnQZ8.js.map} +1 -1
  230. package/dashboard/dist/assets/{pipelines-B2inDtx3.js → pipelines--DUoMx7K.js} +2 -2
  231. package/dashboard/dist/assets/{pipelines-B2inDtx3.js.map → pipelines--DUoMx7K.js.map} +1 -1
  232. package/dashboard/dist/assets/{tasks-DOP4d3M6.js → tasks-CJXN4OB4.js} +2 -2
  233. package/dashboard/dist/assets/{tasks-DOP4d3M6.js.map → tasks-CJXN4OB4.js.map} +1 -1
  234. package/dashboard/dist/assets/{topics-CNOjSWpX.js → topics-CAG08Ruo.js} +2 -2
  235. package/dashboard/dist/assets/{topics-CNOjSWpX.js.map → topics-CAG08Ruo.js.map} +1 -1
  236. package/dashboard/dist/assets/{useDebouncedValue-CgO0TGMd.js → useDebouncedValue-DQ2D8cm2.js} +2 -2
  237. package/dashboard/dist/assets/{useDebouncedValue-CgO0TGMd.js.map → useDebouncedValue-DQ2D8cm2.js.map} +1 -1
  238. package/dashboard/dist/assets/{useNamespace-BWHvUKtK.js → useNamespace-DjEVAlJQ.js} +2 -2
  239. package/dashboard/dist/assets/{useNamespace-BWHvUKtK.js.map → useNamespace-DjEVAlJQ.js.map} +1 -1
  240. package/dashboard/dist/assets/{useYamlActivityEvents-BZQ__ZGD.js → useYamlActivityEvents-etcpfIwo.js} +2 -2
  241. package/dashboard/dist/assets/{useYamlActivityEvents-BZQ__ZGD.js.map → useYamlActivityEvents-etcpfIwo.js.map} +1 -1
  242. package/dashboard/dist/assets/{validate-CAin6WM_.js → validate-YJK_6a7m.js} +2 -2
  243. package/dashboard/dist/assets/{validate-CAin6WM_.js.map → validate-YJK_6a7m.js.map} +1 -1
  244. package/dashboard/dist/assets/{yaml-workflows-Dyln3od-.js → yaml-workflows-D97Bz6li.js} +2 -2
  245. package/dashboard/dist/assets/{yaml-workflows-Dyln3od-.js.map → yaml-workflows-D97Bz6li.js.map} +1 -1
  246. package/dashboard/dist/index.html +1 -1
  247. package/docs/api/http/escalations.md +15 -0
  248. package/docs/api/mcp/human-queue.md +5 -1
  249. package/docs/dashboard.md +3 -3
  250. package/docs/hitl/lookups.md +2 -0
  251. package/docs/hitl/x-lt-roster.md +2 -0
  252. package/docs/hitl/x-lt-scan.md +12 -0
  253. package/docs/mcp.md +2 -0
  254. package/docs/scan-codes.md +87 -5
  255. package/package.json +1 -1
  256. package/dashboard/dist/assets/RosterWidget-BJMx0a8Z.js.map +0 -1
  257. package/dashboard/dist/assets/ScanStationPage-nWYz-OD3.js +0 -2
  258. package/dashboard/dist/assets/ScanStationPage-nWYz-OD3.js.map +0 -1
  259. package/dashboard/dist/assets/index-P-DlgAWj.js +0 -3
  260. package/dashboard/dist/assets/index-P-DlgAWj.js.map +0 -1
  261. package/dashboard/dist/assets/index-ygxyAKcc.js.map +0 -1
@@ -1,2 +1,2 @@
1
- import{j as p,a as U}from"./vendor-query-DvWWuNIB.js";import{a as j}from"./StatusBadge-DHVwxre2.js";import{r as k}from"./index-ygxyAKcc.js";import{b as P}from"./vendor-icons-en0_uSRD.js";import{u as q,a as Y}from"./vendor-react-_9U4mKFr.js";function v(t){return!!(t.assigned_to&&t.assigned_until&&new Date(t.assigned_until)>new Date)}function ft(t){return!t.workflow_type}function W(t){return t.replace(/[_\-.]+/g," ").replace(/([a-z\d])([A-Z])/g,"$1 $2").replace(/([A-Z]+)([A-Z][a-z])/g,"$1 $2").replace(/([a-zA-Z])(\d)/g,"$1 $2").trim().split(/\s+/).filter(Boolean).map(e=>e===e.toUpperCase()&&e.length>1?e:e.charAt(0).toUpperCase()+e.slice(1)).join(" ")}function mt(t){return t.status==="pending"?v(t)?"bg-status-claimed-graphic":"bg-status-active":j[t.status]??"bg-status-active"}function At(t){const s=e=>{if(!e)return null;try{const o=JSON.parse(e);return o&&typeof o=="object"?o:null}catch{return null}};return{escalation:t,metadata:t.metadata??null,envelope:s(t.envelope),payload:s(t.escalation_payload),resolver:s(t.resolver_payload)}}const pt="200px";function gt(t){return`${Math.max(6,Math.round(t.length*.55+3.5))}rem`}const X=30;function K(t){return t.status==="pending"&&!v(t)}function yt({row:t,def:s,onView:e,prominent:o,forceView:i}){const r=(s==null?void 0:s.action)??"claim",a=i?"view":r,l=r===a&&(s!=null&&s.label)?s.label:a==="claim"?"Claim":"View",A=k(),R=q(),L=Y(),[T,h]=U.useState(""),$=a==="claim"&&!K(t),D=I=>{if(I.stopPropagation(),a==="view"){e==null||e();return}if(s!=null&&s.submitOnClaim){R(`/escalations/detail/${t.id}`,{state:{autoStart:!0,durationMinutes:s==null?void 0:s.durationMinutes,from:L.pathname}});return}h(""),A.mutate({id:t.id,durationMinutes:(s==null?void 0:s.durationMinutes)??X},{onSuccess:()=>e==null?void 0:e(),onError:N=>h(N.message)})},C=o?"btn-primary text-xs shrink-0 disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:bg-accent":"px-2 py-0.5 rounded text-2xs font-medium text-accent/70 border border-accent/30 enabled:hover:text-accent enabled:hover:border-accent/60 enabled:hover:bg-accent/5 transition-colors disabled:opacity-50 disabled:cursor-not-allowed whitespace-nowrap";return p.jsxs("span",{className:"inline-flex flex-col items-end gap-0.5",children:[p.jsx("button",{type:"button",onClick:D,disabled:A.isPending||$,title:$?t.status==="pending"?"claimed":t.status:void 0,className:C,"data-testid":"row-action-button",children:A.isPending?"Claiming…":l}),T&&p.jsx("span",{className:"text-2xs text-status-error","data-testid":"row-action-error",children:T})]})}const Z=/^\{\{\s*metadata\.([a-zA-Z0-9_]+)\s*\}\}$/;function x(t){return typeof t=="string"&&t!==""||typeof t=="number"||typeof t=="boolean"}function Et(t,s){var r;const e=[],o=new Set,i=t.metadata??{};for(const a of s){const l=typeof a.value=="string"?(r=a.value.match(Z))==null?void 0:r[1]:void 0;if(!l||o.has(l))continue;const A=i[l];x(A)&&(o.add(l),e.push({key:l,label:a.label,value:A,bound:!0}))}for(const[a,l]of Object.entries(i))o.has(a)||!x(l)||(o.add(a),e.push({key:a,label:W(a),value:l}));return e}function bt({onOpen:t,className:s=""}){return p.jsx("button",{type:"button",onClick:e=>{e.stopPropagation(),t()},className:`p-1.5 rounded text-text-quaternary/70 hover:text-accent hover:bg-surface-hover transition-colors ${s}`,title:"Refine — filter or search by this row's values","aria-label":"Refine — filter or search by this row's values","data-testid":"row-refine",children:p.jsx(P,{className:"w-3.5 h-3.5",strokeWidth:1.5})})}const g={RELATED:"x-lt-related",COLUMNS:"x-lt-columns",ROW_DETAIL:"x-lt-row-detail",GROUP_BY:"x-lt-group-by",LAYOUT:"x-lt-layout"},f={ITEMS:"items",QUERY:"query"},E={COUNT:"count",MIN:"min",MAX:"max"},z={FIRST:"first",MIN:"min",MAX:"max",SUM:"sum",COUNT:"count",DISTINCT:"distinct"},u={ROLE:"role",METADATA:"metadata.",PAYLOAD:"payload.",RECIPROCAL_METADATA:"reciprocal.metadata."},d={MAX_SETS:4,MAX_QUERY_ROLES:12,MAX_ROW_DETAIL_SECTIONS:4,ROWS_MAX:100},G="facet-table",M="default",F=/^\{\{\s*([^{}]+?)\s*\}\}$/,_="item.",Q=/^[a-zA-Z0-9_]+$/,B=/^[a-zA-Z0-9_]+$/,H=/\{n\}/g;function b(t){return t===E.MIN||t===E.MAX}function O(t,s){var i;if(typeof s!="string")return null;const e=(i=s.match(F))==null?void 0:i[1];if(!e)return null;if(t===f.ITEMS){if(!e.startsWith(`${_}${u.PAYLOAD}`))return null;const r=e.slice(_.length+u.PAYLOAD.length);return r&&r.split(".").every(a=>Q.test(a))?`${u.PAYLOAD}${r}`:null}if(!e.startsWith(u.METADATA))return null;const o=e.slice(u.METADATA.length);return B.test(o)?e:null}function Tt(t,s){return b(t.aggregate)?`${t.aggregate}:${O(s,t.value)??""}`:t.splitBy??""}function ht(t,s){if(!t)return null;const e=t[String(s)]??t[M];return e===void 0?null:e.replace(H,String(s))}function $t(t,s){if(!s.length)return;const e=t===E.MIN;if(s.every(i=>typeof i=="number"))return e?Math.min(...s):Math.max(...s);const o=s.map(String).sort((i,r)=>i<r?-1:i>r?1:0);return e?o[0]:o[o.length-1]}const y=/^[a-zA-Z0-9_]+$/,J=/^[a-zA-Z][a-zA-Z0-9_-]{0,47}$/,w=/^[a-zA-Z0-9_]+$/,V=/^(0|[1-9][0-9]*)$/;class tt extends Error{}function n(t){throw new tt(t)}function c(t){return!!t&&typeof t=="object"&&!Array.isArray(t)}function m(t){return typeof t=="string"&&t.trim().length>0}function et(t){return t.length>0&&t.split(".").every(s=>w.test(s))}function st(t,s){return t===f.QUERY?s===u.ROLE||s.startsWith(u.METADATA)&&y.test(s.slice(u.METADATA.length))?null:`splitBy "${s}" on a query set must be "role" or "metadata.<key>"`:s.startsWith(u.PAYLOAD)&&et(s.slice(u.PAYLOAD.length))||s.startsWith(u.RECIPROCAL_METADATA)&&y.test(s.slice(u.RECIPROCAL_METADATA.length))?null:`splitBy "${s}" on an items set must be "payload.<path>" or "reciprocal.metadata.<key>"`}function rt(t,s){if(J.test(t)||n(`x-lt-related name "${t}" must start with a letter (letters, digits, _ or -)`),c(s)||n(`x-lt-related.${t} must be an object`),s.source===f.ITEMS)return s.query!==void 0&&n(`x-lt-related.${t}: an items set takes no query`),{source:f.ITEMS};s.source!==f.QUERY&&n(`x-lt-related.${t}.source must be "items" or "query"`);const e=s.query;c(e)||n(`x-lt-related.${t}.query is required for a query set`);const o=e.roles!==void 0,i=e.segment!==void 0;if(o===i&&n(`x-lt-related.${t}.query takes exactly one of roles or segment`),o){const r=e.roles;(!Array.isArray(r)||r.length===0||r.length>d.MAX_QUERY_ROLES||!r.every(m))&&n(`x-lt-related.${t}.query.roles must be 1..${d.MAX_QUERY_ROLES} role names`)}else m(e.segment)||n(`x-lt-related.${t}.query.segment must be a role name`);(!c(e.facets)||Object.keys(e.facets).length===0)&&n(`x-lt-related.${t}.query.facets must name at least one facet`);for(const[r,a]of Object.entries(e.facets))y.test(r)||n(`x-lt-related.${t}.query.facets key "${r}" must be letters, digits or _`),(a===null||typeof a=="object")&&n(`x-lt-related.${t}.query.facets.${r} must be a scalar or template`);if(e.narrow!==void 0){(!c(e.narrow)||Object.keys(e.narrow).length===0)&&n(`x-lt-related.${t}.query.narrow must name at least one facet`);for(const[r,a]of Object.entries(e.narrow))y.test(r)||n(`x-lt-related.${t}.query.narrow key "${r}" must be letters, digits or _`),r in e.facets&&n(`x-lt-related.${t}.query.narrow.${r} is already a required facet`),(a===null||typeof a=="object")&&n(`x-lt-related.${t}.query.narrow.${r} must be a scalar or template`)}return e.status!==void 0&&!m(e.status)&&n(`x-lt-related.${t}.query.status must be a status`),{source:f.QUERY,query:{...o?{roles:[...e.roles]}:{segment:e.segment},facets:{...e.facets},...e.narrow!==void 0?{narrow:{...e.narrow}}:{},...e.status!==void 0?{status:e.status}:{}}}}function at(t,s,e){const o=t.related;(!m(o)||!s[o])&&n(`${e}.related must name a set declared in x-lt-related`),t.aggregate!==void 0&&t.aggregate!==E.COUNT&&!b(t.aggregate)&&n(`${e}.aggregate must be "count", "min" or "max"`);const i=s[o].source;if(b(t.aggregate)?(O(i,t.value)||n(i===f.ITEMS?`${e}.value must be "{{item.payload.<path>}}" on an items set`:`${e}.value must be "{{metadata.<key>}}" on a query set`),t.splitBy!==void 0&&n(`${e}: a ${t.aggregate} column takes no splitBy`),t.display!==void 0&&n(`${e}: display applies to a count column`)):t.value!==void 0&&n(`${e}.value applies to a min or max column`),t.format!==void 0&&typeof t.format!="string"&&n(`${e}.format must be a format name`),t.display!==void 0&&(!c(t.display)||Object.keys(t.display).length===0||!Object.entries(t.display).every(([r,a])=>(r===M||V.test(r))&&typeof a=="string"))&&n(`${e}.display must map counts (or "default") to display text`),t.splitBy!==void 0){m(t.splitBy)||n(`${e}.splitBy must be a path`);const r=st(s[o].source,t.splitBy);r&&n(`${e}: ${r}`)}return t.labels!==void 0&&(!c(t.labels)||!Object.values(t.labels).every(r=>typeof r=="string"))&&n(`${e}.labels must map values to display text`),t}function nt(t,s){return t===void 0?[]:((!Array.isArray(t)||t.length===0||t.length>d.MAX_ROW_DETAIL_SECTIONS)&&n(`x-lt-row-detail must be 1..${d.MAX_ROW_DETAIL_SECTIONS} sections`),t.map((e,o)=>{const i=`x-lt-row-detail[${o}]`;c(e)||n(`${i} must be an object`),(!m(e.related)||!s[e.related])&&n(`${i}.related must name a set declared in x-lt-related`),(!Array.isArray(e.columns)||e.columns.length===0)&&n(`${i}.columns must list at least one column`);for(const[r,a]of e.columns.entries())(!c(a)||!m(a.label)||typeof a.value!="string")&&n(`${i}.columns[${r}] needs a label and a value template`);return e.limit!==void 0&&(!Number.isInteger(e.limit)||e.limit<1||e.limit>d.ROWS_MAX)&&n(`${i}.limit must be 1..${d.ROWS_MAX}`),e}))}function ot(t){const s=t[g.GROUP_BY];if(s===void 0||t[g.LAYOUT]!==G)return null;const e=typeof s=="string"&&s.startsWith(u.METADATA)?s.slice(u.METADATA.length):"";return y.test(e)||n('x-lt-group-by on a facet-table must be "metadata.<key>"'),e}const S=new Set(Object.values(z));function xt(t){const s={sets:{},columns:[],rowDetail:[],groupKey:null};if(!c(t))return s;const e=t[g.RELATED],o={};if(e!==void 0){c(e)||n("x-lt-related must map set names to declarations");const a=Object.keys(e);(a.length===0||a.length>d.MAX_SETS)&&n(`x-lt-related must declare 1..${d.MAX_SETS} sets`);for(const l of a)o[l]=rt(l,e[l])}const i=[],r=t[g.COLUMNS];return Array.isArray(r)&&r.forEach((a,l)=>{c(a)&&(a.related!==void 0&&i.push(at(a,o,`x-lt-columns[${l}]`)),a.group!==void 0&&(typeof a.group!="string"||!S.has(a.group))&&n(`x-lt-columns[${l}].group must be one of ${[...S].join(", ")}`))}),{sets:o,columns:i,rowDetail:nt(t[g.ROW_DETAIL],o),groupKey:ot(t)}}export{z as G,pt as I,Z as M,bt as R,u as S,W as a,xt as b,b as c,ht as d,mt as e,$t as f,Et as g,yt as h,v as i,gt as j,Tt as k,ft as l,At as r};
2
- //# sourceMappingURL=validate-CAin6WM_.js.map
1
+ import{j as p,a as U}from"./vendor-query-DvWWuNIB.js";import{a as j}from"./StatusBadge-DHVwxre2.js";import{r as k}from"./index-CU2UW2y6.js";import{b as P}from"./vendor-icons-en0_uSRD.js";import{u as q,a as Y}from"./vendor-react-_9U4mKFr.js";function v(t){return!!(t.assigned_to&&t.assigned_until&&new Date(t.assigned_until)>new Date)}function ft(t){return!t.workflow_type}function W(t){return t.replace(/[_\-.]+/g," ").replace(/([a-z\d])([A-Z])/g,"$1 $2").replace(/([A-Z]+)([A-Z][a-z])/g,"$1 $2").replace(/([a-zA-Z])(\d)/g,"$1 $2").trim().split(/\s+/).filter(Boolean).map(e=>e===e.toUpperCase()&&e.length>1?e:e.charAt(0).toUpperCase()+e.slice(1)).join(" ")}function mt(t){return t.status==="pending"?v(t)?"bg-status-claimed-graphic":"bg-status-active":j[t.status]??"bg-status-active"}function At(t){const s=e=>{if(!e)return null;try{const o=JSON.parse(e);return o&&typeof o=="object"?o:null}catch{return null}};return{escalation:t,metadata:t.metadata??null,envelope:s(t.envelope),payload:s(t.escalation_payload),resolver:s(t.resolver_payload)}}const pt="200px";function gt(t){return`${Math.max(6,Math.round(t.length*.55+3.5))}rem`}const X=30;function K(t){return t.status==="pending"&&!v(t)}function yt({row:t,def:s,onView:e,prominent:o,forceView:i}){const r=(s==null?void 0:s.action)??"claim",a=i?"view":r,l=r===a&&(s!=null&&s.label)?s.label:a==="claim"?"Claim":"View",A=k(),R=q(),L=Y(),[T,h]=U.useState(""),$=a==="claim"&&!K(t),D=I=>{if(I.stopPropagation(),a==="view"){e==null||e();return}if(s!=null&&s.submitOnClaim){R(`/escalations/detail/${t.id}`,{state:{autoStart:!0,durationMinutes:s==null?void 0:s.durationMinutes,from:L.pathname}});return}h(""),A.mutate({id:t.id,durationMinutes:(s==null?void 0:s.durationMinutes)??X},{onSuccess:()=>e==null?void 0:e(),onError:N=>h(N.message)})},C=o?"btn-primary text-xs shrink-0 disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:bg-accent":"px-2 py-0.5 rounded text-2xs font-medium text-accent/70 border border-accent/30 enabled:hover:text-accent enabled:hover:border-accent/60 enabled:hover:bg-accent/5 transition-colors disabled:opacity-50 disabled:cursor-not-allowed whitespace-nowrap";return p.jsxs("span",{className:"inline-flex flex-col items-end gap-0.5",children:[p.jsx("button",{type:"button",onClick:D,disabled:A.isPending||$,title:$?t.status==="pending"?"claimed":t.status:void 0,className:C,"data-testid":"row-action-button",children:A.isPending?"Claiming…":l}),T&&p.jsx("span",{className:"text-2xs text-status-error","data-testid":"row-action-error",children:T})]})}const Z=/^\{\{\s*metadata\.([a-zA-Z0-9_]+)\s*\}\}$/;function x(t){return typeof t=="string"&&t!==""||typeof t=="number"||typeof t=="boolean"}function Et(t,s){var r;const e=[],o=new Set,i=t.metadata??{};for(const a of s){const l=typeof a.value=="string"?(r=a.value.match(Z))==null?void 0:r[1]:void 0;if(!l||o.has(l))continue;const A=i[l];x(A)&&(o.add(l),e.push({key:l,label:a.label,value:A,bound:!0}))}for(const[a,l]of Object.entries(i))o.has(a)||!x(l)||(o.add(a),e.push({key:a,label:W(a),value:l}));return e}function bt({onOpen:t,className:s=""}){return p.jsx("button",{type:"button",onClick:e=>{e.stopPropagation(),t()},className:`p-1.5 rounded text-text-quaternary/70 hover:text-accent hover:bg-surface-hover transition-colors ${s}`,title:"Refine — filter or search by this row's values","aria-label":"Refine — filter or search by this row's values","data-testid":"row-refine",children:p.jsx(P,{className:"w-3.5 h-3.5",strokeWidth:1.5})})}const g={RELATED:"x-lt-related",COLUMNS:"x-lt-columns",ROW_DETAIL:"x-lt-row-detail",GROUP_BY:"x-lt-group-by",LAYOUT:"x-lt-layout"},f={ITEMS:"items",QUERY:"query"},E={COUNT:"count",MIN:"min",MAX:"max"},z={FIRST:"first",MIN:"min",MAX:"max",SUM:"sum",COUNT:"count",DISTINCT:"distinct"},u={ROLE:"role",METADATA:"metadata.",PAYLOAD:"payload.",RECIPROCAL_METADATA:"reciprocal.metadata."},d={MAX_SETS:4,MAX_QUERY_ROLES:12,MAX_ROW_DETAIL_SECTIONS:4,ROWS_MAX:100},G="facet-table",M="default",F=/^\{\{\s*([^{}]+?)\s*\}\}$/,_="item.",Q=/^[a-zA-Z0-9_]+$/,B=/^[a-zA-Z0-9_]+$/,H=/\{n\}/g;function b(t){return t===E.MIN||t===E.MAX}function O(t,s){var i;if(typeof s!="string")return null;const e=(i=s.match(F))==null?void 0:i[1];if(!e)return null;if(t===f.ITEMS){if(!e.startsWith(`${_}${u.PAYLOAD}`))return null;const r=e.slice(_.length+u.PAYLOAD.length);return r&&r.split(".").every(a=>Q.test(a))?`${u.PAYLOAD}${r}`:null}if(!e.startsWith(u.METADATA))return null;const o=e.slice(u.METADATA.length);return B.test(o)?e:null}function Tt(t,s){return b(t.aggregate)?`${t.aggregate}:${O(s,t.value)??""}`:t.splitBy??""}function ht(t,s){if(!t)return null;const e=t[String(s)]??t[M];return e===void 0?null:e.replace(H,String(s))}function $t(t,s){if(!s.length)return;const e=t===E.MIN;if(s.every(i=>typeof i=="number"))return e?Math.min(...s):Math.max(...s);const o=s.map(String).sort((i,r)=>i<r?-1:i>r?1:0);return e?o[0]:o[o.length-1]}const y=/^[a-zA-Z0-9_]+$/,J=/^[a-zA-Z][a-zA-Z0-9_-]{0,47}$/,w=/^[a-zA-Z0-9_]+$/,V=/^(0|[1-9][0-9]*)$/;class tt extends Error{}function n(t){throw new tt(t)}function c(t){return!!t&&typeof t=="object"&&!Array.isArray(t)}function m(t){return typeof t=="string"&&t.trim().length>0}function et(t){return t.length>0&&t.split(".").every(s=>w.test(s))}function st(t,s){return t===f.QUERY?s===u.ROLE||s.startsWith(u.METADATA)&&y.test(s.slice(u.METADATA.length))?null:`splitBy "${s}" on a query set must be "role" or "metadata.<key>"`:s.startsWith(u.PAYLOAD)&&et(s.slice(u.PAYLOAD.length))||s.startsWith(u.RECIPROCAL_METADATA)&&y.test(s.slice(u.RECIPROCAL_METADATA.length))?null:`splitBy "${s}" on an items set must be "payload.<path>" or "reciprocal.metadata.<key>"`}function rt(t,s){if(J.test(t)||n(`x-lt-related name "${t}" must start with a letter (letters, digits, _ or -)`),c(s)||n(`x-lt-related.${t} must be an object`),s.source===f.ITEMS)return s.query!==void 0&&n(`x-lt-related.${t}: an items set takes no query`),{source:f.ITEMS};s.source!==f.QUERY&&n(`x-lt-related.${t}.source must be "items" or "query"`);const e=s.query;c(e)||n(`x-lt-related.${t}.query is required for a query set`);const o=e.roles!==void 0,i=e.segment!==void 0;if(o===i&&n(`x-lt-related.${t}.query takes exactly one of roles or segment`),o){const r=e.roles;(!Array.isArray(r)||r.length===0||r.length>d.MAX_QUERY_ROLES||!r.every(m))&&n(`x-lt-related.${t}.query.roles must be 1..${d.MAX_QUERY_ROLES} role names`)}else m(e.segment)||n(`x-lt-related.${t}.query.segment must be a role name`);(!c(e.facets)||Object.keys(e.facets).length===0)&&n(`x-lt-related.${t}.query.facets must name at least one facet`);for(const[r,a]of Object.entries(e.facets))y.test(r)||n(`x-lt-related.${t}.query.facets key "${r}" must be letters, digits or _`),(a===null||typeof a=="object")&&n(`x-lt-related.${t}.query.facets.${r} must be a scalar or template`);if(e.narrow!==void 0){(!c(e.narrow)||Object.keys(e.narrow).length===0)&&n(`x-lt-related.${t}.query.narrow must name at least one facet`);for(const[r,a]of Object.entries(e.narrow))y.test(r)||n(`x-lt-related.${t}.query.narrow key "${r}" must be letters, digits or _`),r in e.facets&&n(`x-lt-related.${t}.query.narrow.${r} is already a required facet`),(a===null||typeof a=="object")&&n(`x-lt-related.${t}.query.narrow.${r} must be a scalar or template`)}return e.status!==void 0&&!m(e.status)&&n(`x-lt-related.${t}.query.status must be a status`),{source:f.QUERY,query:{...o?{roles:[...e.roles]}:{segment:e.segment},facets:{...e.facets},...e.narrow!==void 0?{narrow:{...e.narrow}}:{},...e.status!==void 0?{status:e.status}:{}}}}function at(t,s,e){const o=t.related;(!m(o)||!s[o])&&n(`${e}.related must name a set declared in x-lt-related`),t.aggregate!==void 0&&t.aggregate!==E.COUNT&&!b(t.aggregate)&&n(`${e}.aggregate must be "count", "min" or "max"`);const i=s[o].source;if(b(t.aggregate)?(O(i,t.value)||n(i===f.ITEMS?`${e}.value must be "{{item.payload.<path>}}" on an items set`:`${e}.value must be "{{metadata.<key>}}" on a query set`),t.splitBy!==void 0&&n(`${e}: a ${t.aggregate} column takes no splitBy`),t.display!==void 0&&n(`${e}: display applies to a count column`)):t.value!==void 0&&n(`${e}.value applies to a min or max column`),t.format!==void 0&&typeof t.format!="string"&&n(`${e}.format must be a format name`),t.display!==void 0&&(!c(t.display)||Object.keys(t.display).length===0||!Object.entries(t.display).every(([r,a])=>(r===M||V.test(r))&&typeof a=="string"))&&n(`${e}.display must map counts (or "default") to display text`),t.splitBy!==void 0){m(t.splitBy)||n(`${e}.splitBy must be a path`);const r=st(s[o].source,t.splitBy);r&&n(`${e}: ${r}`)}return t.labels!==void 0&&(!c(t.labels)||!Object.values(t.labels).every(r=>typeof r=="string"))&&n(`${e}.labels must map values to display text`),t}function nt(t,s){return t===void 0?[]:((!Array.isArray(t)||t.length===0||t.length>d.MAX_ROW_DETAIL_SECTIONS)&&n(`x-lt-row-detail must be 1..${d.MAX_ROW_DETAIL_SECTIONS} sections`),t.map((e,o)=>{const i=`x-lt-row-detail[${o}]`;c(e)||n(`${i} must be an object`),(!m(e.related)||!s[e.related])&&n(`${i}.related must name a set declared in x-lt-related`),(!Array.isArray(e.columns)||e.columns.length===0)&&n(`${i}.columns must list at least one column`);for(const[r,a]of e.columns.entries())(!c(a)||!m(a.label)||typeof a.value!="string")&&n(`${i}.columns[${r}] needs a label and a value template`);return e.limit!==void 0&&(!Number.isInteger(e.limit)||e.limit<1||e.limit>d.ROWS_MAX)&&n(`${i}.limit must be 1..${d.ROWS_MAX}`),e}))}function ot(t){const s=t[g.GROUP_BY];if(s===void 0||t[g.LAYOUT]!==G)return null;const e=typeof s=="string"&&s.startsWith(u.METADATA)?s.slice(u.METADATA.length):"";return y.test(e)||n('x-lt-group-by on a facet-table must be "metadata.<key>"'),e}const S=new Set(Object.values(z));function xt(t){const s={sets:{},columns:[],rowDetail:[],groupKey:null};if(!c(t))return s;const e=t[g.RELATED],o={};if(e!==void 0){c(e)||n("x-lt-related must map set names to declarations");const a=Object.keys(e);(a.length===0||a.length>d.MAX_SETS)&&n(`x-lt-related must declare 1..${d.MAX_SETS} sets`);for(const l of a)o[l]=rt(l,e[l])}const i=[],r=t[g.COLUMNS];return Array.isArray(r)&&r.forEach((a,l)=>{c(a)&&(a.related!==void 0&&i.push(at(a,o,`x-lt-columns[${l}]`)),a.group!==void 0&&(typeof a.group!="string"||!S.has(a.group))&&n(`x-lt-columns[${l}].group must be one of ${[...S].join(", ")}`))}),{sets:o,columns:i,rowDetail:nt(t[g.ROW_DETAIL],o),groupKey:ot(t)}}export{z as G,pt as I,Z as M,bt as R,u as S,W as a,xt as b,b as c,ht as d,mt as e,$t as f,Et as g,yt as h,v as i,gt as j,Tt as k,ft as l,At as r};
2
+ //# sourceMappingURL=validate-YJK_6a7m.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"validate-CAin6WM_.js","sources":["../../src/lib/escalation.ts","../../src/lib/identifier-label.ts","../../src/components/escalation/list-view/row-parts.tsx","../../../shared/list-schema/types.ts","../../../shared/list-schema/aggregate.ts","../../../shared/list-schema/validate.ts"],"sourcesContent":["import type { LTEscalationRecord } from '../api/types';\n\nexport function isEffectivelyClaimed(esc: LTEscalationRecord): boolean {\n return !!(\n esc.assigned_to &&\n esc.assigned_until &&\n new Date(esc.assigned_until) > new Date()\n );\n}\n\n/** An ACK escalation has no associated workflow — it's a notification, not a task. */\nexport function isAckEscalation(esc: LTEscalationRecord): boolean {\n return !esc.workflow_type;\n}\n\nexport function isAvailable(esc: LTEscalationRecord): boolean {\n return (\n esc.status === 'pending' &&\n (!esc.assigned_to ||\n !esc.assigned_until ||\n new Date(esc.assigned_until) <= new Date())\n );\n}\n","/**\n * A human label for a code identifier: camelCase, snake_case, kebab-case, and\n * digit runs become Title Case words. `fleetTools` → `Fleet Tools`,\n * `print_order2` → `Print Order 2`. Acronyms keep their capitals: `mcpQuery`\n * → `Mcp Query` is avoided by treating an all-caps run as one word.\n */\nexport function identifierToTitle(id: string): string {\n const words = id\n .replace(/[_\\-.]+/g, ' ')\n .replace(/([a-z\\d])([A-Z])/g, '$1 $2')\n .replace(/([A-Z]+)([A-Z][a-z])/g, '$1 $2')\n .replace(/([a-zA-Z])(\\d)/g, '$1 $2')\n .trim()\n .split(/\\s+/)\n .filter(Boolean);\n return words\n .map((w) => (w === w.toUpperCase() && w.length > 1 ? w : w.charAt(0).toUpperCase() + w.slice(1)))\n .join(' ');\n}\n","import { useState } from 'react';\nimport { useNavigate, useLocation } from 'react-router-dom';\nimport { Search } from 'lucide-react';\nimport type { HelpTokenContext } from '../../../lib/x-lt-help';\nimport { STATUS_DOT_STYLES } from '../../common/display/StatusBadge';\nimport { useClaimEscalation } from '../../../api/escalations';\nimport { isEffectivelyClaimed } from '../../../lib/escalation';\nimport { identifierToTitle } from '../../../lib/identifier-label';\nimport type { RefinePair } from '../RefineDialog';\nimport type { LTEscalationRecord } from '../../../api/types';\n\n/** The row-level pieces every list layout shares: column and action\n * declarations, the row's token context, the row action, and refine. */\n\n/** Dot colour matches the standard table view: blue = pending unclaimed,\n * orange = under a live claim; other statuses follow the shared map. */\nexport function escalationDotClass(e: LTEscalationRecord): string {\n if (e.status === 'pending') {\n return isEffectivelyClaimed(e) ? 'bg-status-claimed-graphic' : 'bg-status-active';\n }\n return STATUS_DOT_STYLES[e.status] ?? 'bg-status-active';\n}\n\nexport interface ColumnDef {\n label: string;\n value: string;\n /** \"age\" renders an ISO timestamp as a compact age with an absolute tooltip. */\n format?: string;\n /**\n * Card-fold behavior when the table folds at narrow widths (same contract\n * as the engineer table): 1 = identity — always visible, forms the card's\n * title line; 2 = folds into the card body as a label/value pair; 3 =\n * dropped in card mode. Undeclared, the first column is the identity and\n * the rest fold.\n */\n priority?: 1 | 2 | 3;\n /** CSS column width (\"40%\", \"12rem\"). Unset columns share the remainder;\n * when no column declares one, the first column holds a fixed 200px so\n * the identity stays legible and every other value keeps its room. */\n width?: string;\n}\n\n/**\n * The per-row action button — the queue-working gesture, always available on\n * every layout. Template-driven: the schema author sets the action, its text,\n * and (for claim) the hold time.\n */\nexport interface RowActionDef {\n /** \"claim\" fires a one-click claim then opens the detail page already\n * claimed; \"view\" just opens it. Default \"claim\". */\n action?: 'claim' | 'view';\n /** Button text. Defaults \"Claim\" / \"View\" by action. */\n label?: string;\n /** Claim hold time in minutes. Default 30 (the platform claim default). */\n durationMinutes?: number;\n /**\n * Launch the detail page's claim-and-submit flow instead of a plain claim:\n * the row is claimed, the form's seeded defaults are resolved, and — when the\n * form declares `x-lt-transition` — the wait screen bridges to the born-assigned\n * follow-on. One list gesture (\"Start Harvesting\") starts the work and lands\n * the person on the next step. The behavior lives on the detail page (form\n * defaults, validation, transition); the list only points there with intent.\n */\n submitOnClaim?: boolean;\n}\n\n/** Build the token context for one escalation row (payloads are JSON strings). */\nexport function rowContext(e: LTEscalationRecord): HelpTokenContext {\n const parse = (s: string | null | undefined): Record<string, unknown> | null => {\n if (!s) return null;\n try {\n const v = JSON.parse(s);\n return v && typeof v === 'object' ? v : null;\n } catch {\n return null;\n }\n };\n return {\n escalation: e as unknown as Record<string, unknown>,\n metadata: e.metadata ?? null,\n envelope: parse(e.envelope),\n payload: parse(e.escalation_payload),\n resolver: parse(e.resolver_payload),\n };\n}\n\n/** The identity column's width when no column declares one: room for a label, no more. */\nexport const IDENTITY_COLUMN_WIDTH = '200px';\n\n/** The action column sizes to its label so the button never wraps or truncates. */\nexport function actionColumnWidth(label: string): string {\n return `${Math.max(6, Math.round(label.length * 0.55 + 3.5))}rem`;\n}\n\nconst DEFAULT_CLAIM_MINUTES = 30;\n\n/** Claim targets pending rows without a live claim window. */\nfunction isClaimable(row: LTEscalationRecord): boolean {\n return row.status === 'pending' && !isEffectivelyClaimed(row);\n}\n\n/**\n * The x-lt-row-action button. `claim` is one click: claim for the template's\n * duration, then open the detail page already claimed — the fast path for\n * working a queue. `view` opens the detail page. Persistent (light accent at\n * rest, saturating on hover); a rejected claim surfaces its message inline.\n */\nexport function RowActionButton({ row, def, onView, prominent, forceView }: {\n row: LTEscalationRecord;\n def?: RowActionDef;\n onView?: () => void;\n /** Hero treatment for the active-card CTA; default is the compact row button. */\n prominent?: boolean;\n /** Render claim templates as view actions (see EscalationListView). */\n forceView?: boolean;\n}) {\n const authored = def?.action ?? 'claim';\n const action = forceView ? 'view' : authored;\n // The authored label describes the authored gesture. When view is forced\n // over a claim template, claim wording would mislead — use the view default.\n const label = authored === action && def?.label\n ? def.label\n : action === 'claim' ? 'Claim' : 'View';\n const claim = useClaimEscalation();\n const navigate = useNavigate();\n const location = useLocation();\n const [error, setError] = useState('');\n\n // A spent gesture stays visible — the disabled button keeps every row's\n // layout uniform instead of leaving a hole on resolved/held rows.\n const spent = action === 'claim' && !isClaimable(row);\n\n const fire = (e: React.MouseEvent) => {\n e.stopPropagation();\n if (action === 'view') {\n onView?.();\n return;\n }\n // Launch the detail page's claim → submit → transition flow. The detail\n // page owns the heavy lifting (seed defaults, validate, resolve, hand-off);\n // the list points there with the intent and the authored claim duration.\n if (def?.submitOnClaim) {\n navigate(`/escalations/detail/${row.id}`, {\n state: { autoStart: true, durationMinutes: def?.durationMinutes, from: location.pathname },\n });\n return;\n }\n setError('');\n claim.mutate(\n { id: row.id, durationMinutes: def?.durationMinutes ?? DEFAULT_CLAIM_MINUTES },\n {\n onSuccess: () => onView?.(),\n onError: (err) => setError((err as Error).message),\n },\n );\n };\n\n const classes = prominent\n ? 'btn-primary text-xs shrink-0 disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:bg-accent'\n : 'px-2 py-0.5 rounded text-2xs font-medium text-accent/70 border border-accent/30 enabled:hover:text-accent enabled:hover:border-accent/60 enabled:hover:bg-accent/5 transition-colors disabled:opacity-50 disabled:cursor-not-allowed whitespace-nowrap';\n\n return (\n <span className=\"inline-flex flex-col items-end gap-0.5\">\n <button\n type=\"button\"\n onClick={fire}\n disabled={claim.isPending || spent}\n title={spent ? (row.status === 'pending' ? 'claimed' : row.status) : undefined}\n className={classes}\n data-testid=\"row-action-button\"\n >\n {claim.isPending ? 'Claiming…' : label}\n </button>\n {error && <span className=\"text-2xs text-status-error\" data-testid=\"row-action-error\">{error}</span>}\n </span>\n );\n}\n\n/** A field value token that is a pure metadata binding — facet-linkable. */\nexport const METADATA_TOKEN = /^\\{\\{\\s*metadata\\.([a-zA-Z0-9_]+)\\s*\\}\\}$/;\n\n/** A metadata value the faceted query can match on: a scalar with something in it. */\nfunction isFacetValue(raw: unknown): raw is string | number | boolean {\n return (typeof raw === 'string' && raw !== '') || typeof raw === 'number' || typeof raw === 'boolean';\n}\n\n/**\n * The refinable facts one row offers: every metadata facet the row carries.\n * Columns the author bound to a metadata value lead, under their authored\n * labels; every other scalar facet follows under a label derived from its\n * key, so the dialog reaches the whole record, not only what the columns\n * show. Feeds the row's RefineDialog.\n */\nexport function rowRefinePairs(row: LTEscalationRecord, columnDefs: ColumnDef[]): RefinePair[] {\n const pairs: RefinePair[] = [];\n const seen = new Set<string>();\n const metadata = row.metadata ?? {};\n for (const col of columnDefs) {\n const bound = typeof col.value === 'string' ? col.value.match(METADATA_TOKEN)?.[1] : undefined;\n if (!bound || seen.has(bound)) continue;\n const raw = metadata[bound];\n if (!isFacetValue(raw)) continue;\n seen.add(bound);\n pairs.push({ key: bound, label: col.label, value: raw, bound: true });\n }\n for (const [key, raw] of Object.entries(metadata)) {\n if (seen.has(key) || !isFacetValue(raw)) continue;\n seen.add(key);\n pairs.push({ key, label: identifierToTitle(key), value: raw });\n }\n return pairs;\n}\n\n/** The always-tappable row/card refine trigger — one magnifier, dialog behind it. */\nexport function RefineTrigger({ onOpen, className = '' }: { onOpen: () => void; className?: string }) {\n return (\n <button\n type=\"button\"\n onClick={(e) => { e.stopPropagation(); onOpen(); }}\n className={`p-1.5 rounded text-text-quaternary/70 hover:text-accent hover:bg-surface-hover transition-colors ${className}`}\n title=\"Refine — filter or search by this row's values\"\n aria-label=\"Refine — filter or search by this row's values\"\n data-testid=\"row-refine\"\n >\n <Search className=\"w-3.5 h-3.5\" strokeWidth={1.5} />\n </button>\n );\n}\n","/**\n * Related sets on a role's list schema. A set is named once under\n * `x-lt-related`; columns (`related`) and row details (`x-lt-row-detail`)\n * reference it by name. The server reads the declaration from the stored\n * list schema, never from the caller.\n *\n * items the host row's accumulated or batch items, each read through its\n * payload or its reciprocal row\n * query rows in other roles that share facets with the host row; facet\n * values are `{{domain.path}}` templates over the host row\n */\n\nexport const LIST_SCHEMA_KEYS = {\n RELATED: 'x-lt-related',\n COLUMNS: 'x-lt-columns',\n ROW_DETAIL: 'x-lt-row-detail',\n GROUP_BY: 'x-lt-group-by',\n LAYOUT: 'x-lt-layout',\n} as const;\n\nexport const RELATED_SOURCES = {\n ITEMS: 'items',\n QUERY: 'query',\n} as const;\nexport type RelatedSource = (typeof RELATED_SOURCES)[keyof typeof RELATED_SOURCES];\n\nexport const RELATED_AGGREGATES = {\n COUNT: 'count',\n /** The least item or row value: numbers as numbers, everything else as text. */\n MIN: 'min',\n MAX: 'max',\n} as const;\nexport type RelatedAggregate = (typeof RELATED_AGGREGATES)[keyof typeof RELATED_AGGREGATES];\n\n/** How a grouped table folds a plain column across a group's rows. */\nexport const GROUP_FOLDS = {\n FIRST: 'first',\n MIN: 'min',\n MAX: 'max',\n SUM: 'sum',\n COUNT: 'count',\n /** The distinct values, in row order, joined: \"standard, express\". */\n DISTINCT: 'distinct',\n} as const;\nexport type GroupFold = (typeof GROUP_FOLDS)[keyof typeof GROUP_FOLDS];\n\n/** Split prefixes. `role` splits a query set by the related row's role. */\nexport const SPLIT_PATHS = {\n ROLE: 'role',\n METADATA: 'metadata.',\n PAYLOAD: 'payload.',\n RECIPROCAL_METADATA: 'reciprocal.metadata.',\n} as const;\n\nexport const RELATED_LIMITS = {\n MAX_SETS: 4,\n MAX_QUERY_ROLES: 12,\n MAX_ROW_DETAIL_SECTIONS: 4,\n MAX_HOSTS: 100,\n ROWS_DEFAULT: 25,\n ROWS_MAX: 100,\n} as const;\n\n/** The table layout that renders related columns, row details, and groups. */\nexport const FACET_TABLE_LAYOUT = 'facet-table';\n\nexport interface RelatedQueryDecl {\n /** Related roles, in display order. Exclusive with `segment`. */\n roles?: string[];\n /** A Pace Board sequence by its first role; resolves to the ordered chain. */\n segment?: string;\n /** Facet containment; string values may be `{{domain.path}}` templates. */\n facets?: Record<string, unknown>;\n /**\n * Facets that narrow the match when the host row carries a value, and\n * drop away when it does not: a lane narrows its tub's look-ahead to its\n * own service level, a tub without lanes keeps the whole destination.\n */\n narrow?: Record<string, unknown>;\n /** Default `pending`. */\n status?: string;\n}\n\nexport interface RelatedSetDecl {\n source: RelatedSource;\n query?: RelatedQueryDecl;\n}\n\n/** A count column's text keyed by count; `default` covers the rest, `{n}` reads the count. */\nexport const RELATED_DISPLAY_DEFAULT = 'default';\n\nexport interface RelatedColumnDecl {\n label: string;\n related: string;\n aggregate?: RelatedAggregate;\n /** min/max: `{{item.payload.<path>}}` on an items set, `{{metadata.<key>}}` on a query set. */\n value?: string;\n /** min/max: how the value renders, as on any facet-table column. */\n format?: string;\n /** count: text by count, e.g. `{ \"0\": \"Ready\", \"default\": \"Waiting on {n}\" }`. */\n display?: Record<string, string>;\n splitBy?: string;\n labels?: Record<string, string>;\n priority?: 1 | 2 | 3;\n /** CSS width, as on any facet-table column. */\n width?: string;\n}\n\nexport interface RowDetailColumnDecl {\n label: string;\n value: string;\n format?: string;\n priority?: 1 | 2 | 3;\n}\n\nexport interface RowDetailSectionDecl {\n related: string;\n label?: string;\n columns: RowDetailColumnDecl[];\n limit?: number;\n}\n\n/** The validated, normalized related declaration of one list schema. */\nexport interface ListSchemaRelations {\n sets: Record<string, RelatedSetDecl>;\n columns: RelatedColumnDecl[];\n rowDetail: RowDetailSectionDecl[];\n /** The metadata key a facet-table groups by, when declared. */\n groupKey: string | null;\n}\n\n/** Per-host aggregate for one (set, splitBy) pair, or one (set, min/max, value) triple. */\nexport interface RelatedHostAggregate {\n count: number;\n /** min/max: the extreme value among `count` values; absent when none carries one. */\n value?: string | number;\n /** Split value to count; a null value is keyed by the empty string. */\n splits?: Record<string, number>;\n /** Items whose reciprocal row the viewer cannot read. */\n hidden?: number;\n /** A host whose facet templates resolved to nothing; the set did not run. */\n unresolved?: boolean;\n}\n\nexport interface RelatedSetResult {\n source: RelatedSource;\n /**\n * The roles whose rows the set reads, which a list follows for live\n * changes: a query set's resolved roles in order, or the roles of an item\n * set's readable reciprocal rows when a column splits by them.\n */\n roles?: string[];\n /** Query sets: role titles, keyed by role. */\n roleLabels?: Record<string, string>;\n /** Query sets: declared roles outside the viewer's full read scope. */\n restricted?: string[];\n /** Keyed by relatedAggregateKey (the splitBy, '' when unsplit, or `min:<path>`), then host id. */\n aggregates: Record<string, Record<string, RelatedHostAggregate>>;\n}\n\nexport interface RelatedAggregatesResponse {\n version: number | null;\n sets: Record<string, RelatedSetResult>;\n}\n\nexport interface RelatedItemEntry {\n itemKey: string;\n payload?: unknown;\n at?: string;\n actor?: string;\n reciprocalId?: string;\n}\n\nexport interface RelatedRow {\n /** Items sets only. */\n item?: RelatedItemEntry;\n /** The reciprocal row (items) or the related row (query); null when unreadable. */\n escalation: Record<string, unknown> | null;\n}\n\nexport interface RelatedRowsResponse {\n source: RelatedSource;\n rows: RelatedRow[];\n total: number;\n restricted?: string[];\n roleLabels?: Record<string, string>;\n}\n","import {\n RELATED_AGGREGATES,\n RELATED_DISPLAY_DEFAULT,\n RELATED_SOURCES,\n SPLIT_PATHS,\n type RelatedAggregate,\n type RelatedColumnDecl,\n} from './types';\n\nconst VALUE_TOKEN = /^\\{\\{\\s*([^{}]+?)\\s*\\}\\}$/;\nconst ITEM_PREFIX = 'item.';\nconst PATH_SEGMENT = /^[a-zA-Z0-9_]+$/;\nconst METADATA_KEY = /^[a-zA-Z0-9_]+$/;\nconst COUNT_TOKEN = /\\{n\\}/g;\n\nexport type RelatedExtreme = typeof RELATED_AGGREGATES.MIN | typeof RELATED_AGGREGATES.MAX;\n\n/** True for the aggregates that pick one value rather than count. */\nexport function isExtremeAggregate(aggregate: unknown): aggregate is RelatedExtreme {\n return aggregate === RELATED_AGGREGATES.MIN || aggregate === RELATED_AGGREGATES.MAX;\n}\n\n/**\n * The stored path a min/max `value` template reads: `payload.<path>` for an\n * items set, `metadata.<key>` for a query set. Null when the template does\n * not fit the set's source.\n */\nexport function relatedValuePath(source: string, value: unknown): string | null {\n if (typeof value !== 'string') return null;\n const token = value.match(VALUE_TOKEN)?.[1];\n if (!token) return null;\n if (source === RELATED_SOURCES.ITEMS) {\n if (!token.startsWith(`${ITEM_PREFIX}${SPLIT_PATHS.PAYLOAD}`)) return null;\n const path = token.slice(ITEM_PREFIX.length + SPLIT_PATHS.PAYLOAD.length);\n return path && path.split('.').every((s) => PATH_SEGMENT.test(s)) ? `${SPLIT_PATHS.PAYLOAD}${path}` : null;\n }\n if (!token.startsWith(SPLIT_PATHS.METADATA)) return null;\n const key = token.slice(SPLIT_PATHS.METADATA.length);\n return METADATA_KEY.test(key) ? token : null;\n}\n\n/** The key a column's per-host aggregates are stored under in its set's result. */\nexport function relatedAggregateKey(col: Pick<RelatedColumnDecl, 'aggregate' | 'splitBy' | 'value'>, source: string): string {\n if (isExtremeAggregate(col.aggregate)) return `${col.aggregate}:${relatedValuePath(source, col.value) ?? ''}`;\n return col.splitBy ?? '';\n}\n\n/** A count column's display text, or null when the column declares none for this count. */\nexport function relatedCountText(display: Record<string, string> | undefined, count: number): string | null {\n if (!display) return null;\n const text = display[String(count)] ?? display[RELATED_DISPLAY_DEFAULT];\n return text === undefined ? null : text.replace(COUNT_TOKEN, String(count));\n}\n\n/**\n * The least or greatest of several values: numbers when every value is a\n * number, otherwise text in code-unit order (ISO instants order as text).\n */\nexport function foldExtreme(fold: RelatedAggregate, values: Array<string | number>): string | number | undefined {\n if (!values.length) return undefined;\n const pickMin = fold === RELATED_AGGREGATES.MIN;\n if (values.every((v) => typeof v === 'number')) {\n return pickMin ? Math.min(...(values as number[])) : Math.max(...(values as number[]));\n }\n const texts = values.map(String).sort((a, b) => (a < b ? -1 : a > b ? 1 : 0));\n return pickMin ? texts[0] : texts[texts.length - 1];\n}\n","import {\n FACET_TABLE_LAYOUT,\n GROUP_FOLDS,\n LIST_SCHEMA_KEYS,\n RELATED_AGGREGATES,\n RELATED_DISPLAY_DEFAULT,\n RELATED_LIMITS,\n RELATED_SOURCES,\n SPLIT_PATHS,\n type ListSchemaRelations,\n type RelatedColumnDecl,\n type RelatedSetDecl,\n type RowDetailSectionDecl,\n} from './types';\nimport { isExtremeAggregate, relatedValuePath } from './aggregate';\n\n/** A metadata key: interpolated as a JSON path, so strictly validated. */\nexport const METADATA_KEY = /^[a-zA-Z0-9_]+$/;\nconst SET_NAME = /^[a-zA-Z][a-zA-Z0-9_-]{0,47}$/;\nconst PATH_SEGMENT = /^[a-zA-Z0-9_]+$/;\nconst COUNT_KEY = /^(0|[1-9][0-9]*)$/;\n\ntype Obj = Record<string, unknown>;\n\nclass ListSchemaError extends Error {}\n\nfunction fail(message: string): never {\n throw new ListSchemaError(message);\n}\n\nfunction isObj(value: unknown): value is Obj {\n return !!value && typeof value === 'object' && !Array.isArray(value);\n}\n\nfunction isNonEmptyString(value: unknown): value is string {\n return typeof value === 'string' && value.trim().length > 0;\n}\n\n/** True when a dotted path has only plain segments. */\nfunction isPlainPath(path: string): boolean {\n return path.length > 0 && path.split('.').every((s) => PATH_SEGMENT.test(s));\n}\n\n/** The split paths a set's source accepts. */\nexport function splitPathError(source: string, splitBy: string): string | null {\n if (source === RELATED_SOURCES.QUERY) {\n if (splitBy === SPLIT_PATHS.ROLE) return null;\n if (splitBy.startsWith(SPLIT_PATHS.METADATA) && METADATA_KEY.test(splitBy.slice(SPLIT_PATHS.METADATA.length))) return null;\n return `splitBy \"${splitBy}\" on a query set must be \"role\" or \"metadata.<key>\"`;\n }\n if (splitBy.startsWith(SPLIT_PATHS.PAYLOAD) && isPlainPath(splitBy.slice(SPLIT_PATHS.PAYLOAD.length))) return null;\n if (splitBy.startsWith(SPLIT_PATHS.RECIPROCAL_METADATA)\n && METADATA_KEY.test(splitBy.slice(SPLIT_PATHS.RECIPROCAL_METADATA.length))) return null;\n return `splitBy \"${splitBy}\" on an items set must be \"payload.<path>\" or \"reciprocal.metadata.<key>\"`;\n}\n\nfunction readSet(name: string, raw: unknown): RelatedSetDecl {\n if (!SET_NAME.test(name)) fail(`x-lt-related name \"${name}\" must start with a letter (letters, digits, _ or -)`);\n if (!isObj(raw)) fail(`x-lt-related.${name} must be an object`);\n if (raw.source === RELATED_SOURCES.ITEMS) {\n if (raw.query !== undefined) fail(`x-lt-related.${name}: an items set takes no query`);\n return { source: RELATED_SOURCES.ITEMS };\n }\n if (raw.source !== RELATED_SOURCES.QUERY) fail(`x-lt-related.${name}.source must be \"items\" or \"query\"`);\n const q = raw.query;\n if (!isObj(q)) fail(`x-lt-related.${name}.query is required for a query set`);\n const hasRoles = q.roles !== undefined;\n const hasSegment = q.segment !== undefined;\n if (hasRoles === hasSegment) fail(`x-lt-related.${name}.query takes exactly one of roles or segment`);\n if (hasRoles) {\n const roles = q.roles;\n if (!Array.isArray(roles) || roles.length === 0 || roles.length > RELATED_LIMITS.MAX_QUERY_ROLES\n || !roles.every(isNonEmptyString)) {\n fail(`x-lt-related.${name}.query.roles must be 1..${RELATED_LIMITS.MAX_QUERY_ROLES} role names`);\n }\n } else if (!isNonEmptyString(q.segment)) {\n fail(`x-lt-related.${name}.query.segment must be a role name`);\n }\n if (!isObj(q.facets) || Object.keys(q.facets).length === 0) {\n fail(`x-lt-related.${name}.query.facets must name at least one facet`);\n }\n for (const [key, value] of Object.entries(q.facets)) {\n if (!METADATA_KEY.test(key)) fail(`x-lt-related.${name}.query.facets key \"${key}\" must be letters, digits or _`);\n if (value === null || typeof value === 'object') fail(`x-lt-related.${name}.query.facets.${key} must be a scalar or template`);\n }\n if (q.narrow !== undefined) {\n if (!isObj(q.narrow) || Object.keys(q.narrow).length === 0) fail(`x-lt-related.${name}.query.narrow must name at least one facet`);\n for (const [key, value] of Object.entries(q.narrow)) {\n if (!METADATA_KEY.test(key)) fail(`x-lt-related.${name}.query.narrow key \"${key}\" must be letters, digits or _`);\n if (key in q.facets) fail(`x-lt-related.${name}.query.narrow.${key} is already a required facet`);\n if (value === null || typeof value === 'object') fail(`x-lt-related.${name}.query.narrow.${key} must be a scalar or template`);\n }\n }\n if (q.status !== undefined && !isNonEmptyString(q.status)) fail(`x-lt-related.${name}.query.status must be a status`);\n return {\n source: RELATED_SOURCES.QUERY,\n query: {\n ...(hasRoles ? { roles: [...(q.roles as string[])] } : { segment: q.segment as string }),\n facets: { ...q.facets },\n ...(q.narrow !== undefined ? { narrow: { ...(q.narrow as Obj) } } : {}),\n ...(q.status !== undefined ? { status: q.status as string } : {}),\n },\n };\n}\n\nfunction readRelatedColumn(raw: Obj, sets: Record<string, RelatedSetDecl>, at: string): RelatedColumnDecl {\n const name = raw.related;\n if (!isNonEmptyString(name) || !sets[name]) fail(`${at}.related must name a set declared in x-lt-related`);\n if (raw.aggregate !== undefined && raw.aggregate !== RELATED_AGGREGATES.COUNT && !isExtremeAggregate(raw.aggregate)) {\n fail(`${at}.aggregate must be \"count\", \"min\" or \"max\"`);\n }\n const source = sets[name].source;\n if (isExtremeAggregate(raw.aggregate)) {\n if (!relatedValuePath(source, raw.value)) {\n fail(source === RELATED_SOURCES.ITEMS\n ? `${at}.value must be \"{{item.payload.<path>}}\" on an items set`\n : `${at}.value must be \"{{metadata.<key>}}\" on a query set`);\n }\n if (raw.splitBy !== undefined) fail(`${at}: a ${raw.aggregate} column takes no splitBy`);\n if (raw.display !== undefined) fail(`${at}: display applies to a count column`);\n } else if (raw.value !== undefined) {\n fail(`${at}.value applies to a min or max column`);\n }\n if (raw.format !== undefined && typeof raw.format !== 'string') fail(`${at}.format must be a format name`);\n if (raw.display !== undefined) {\n if (!isObj(raw.display) || Object.keys(raw.display).length === 0\n || !Object.entries(raw.display).every(([k, v]) => (k === RELATED_DISPLAY_DEFAULT || COUNT_KEY.test(k)) && typeof v === 'string')) {\n fail(`${at}.display must map counts (or \"default\") to display text`);\n }\n }\n if (raw.splitBy !== undefined) {\n if (!isNonEmptyString(raw.splitBy)) fail(`${at}.splitBy must be a path`);\n const err = splitPathError(sets[name].source, raw.splitBy);\n if (err) fail(`${at}: ${err}`);\n }\n if (raw.labels !== undefined && (!isObj(raw.labels) || !Object.values(raw.labels).every((v) => typeof v === 'string'))) {\n fail(`${at}.labels must map values to display text`);\n }\n return raw as unknown as RelatedColumnDecl;\n}\n\nfunction readRowDetail(raw: unknown, sets: Record<string, RelatedSetDecl>): RowDetailSectionDecl[] {\n if (raw === undefined) return [];\n if (!Array.isArray(raw) || raw.length === 0 || raw.length > RELATED_LIMITS.MAX_ROW_DETAIL_SECTIONS) {\n fail(`x-lt-row-detail must be 1..${RELATED_LIMITS.MAX_ROW_DETAIL_SECTIONS} sections`);\n }\n return raw.map((section, i) => {\n const at = `x-lt-row-detail[${i}]`;\n if (!isObj(section)) fail(`${at} must be an object`);\n if (!isNonEmptyString(section.related) || !sets[section.related]) fail(`${at}.related must name a set declared in x-lt-related`);\n if (!Array.isArray(section.columns) || section.columns.length === 0) fail(`${at}.columns must list at least one column`);\n for (const [j, col] of section.columns.entries()) {\n if (!isObj(col) || !isNonEmptyString(col.label) || typeof col.value !== 'string') {\n fail(`${at}.columns[${j}] needs a label and a value template`);\n }\n }\n if (section.limit !== undefined && (!Number.isInteger(section.limit) || (section.limit as number) < 1\n || (section.limit as number) > RELATED_LIMITS.ROWS_MAX)) {\n fail(`${at}.limit must be 1..${RELATED_LIMITS.ROWS_MAX}`);\n }\n return section as unknown as RowDetailSectionDecl;\n });\n}\n\nfunction readGroupKey(schema: Obj): string | null {\n const raw = schema[LIST_SCHEMA_KEYS.GROUP_BY];\n if (raw === undefined || schema[LIST_SCHEMA_KEYS.LAYOUT] !== FACET_TABLE_LAYOUT) return null;\n const key = typeof raw === 'string' && raw.startsWith(SPLIT_PATHS.METADATA) ? raw.slice(SPLIT_PATHS.METADATA.length) : '';\n if (!METADATA_KEY.test(key)) fail('x-lt-group-by on a facet-table must be \"metadata.<key>\"');\n return key;\n}\n\nconst FOLDS = new Set<string>(Object.values(GROUP_FOLDS));\n\n/**\n * Validate and normalize the related declaration of a list schema. Throws on\n * an invalid declaration; keys outside this vocabulary stay opaque.\n */\nexport function readListSchemaRelations(schema: unknown): ListSchemaRelations {\n const empty: ListSchemaRelations = { sets: {}, columns: [], rowDetail: [], groupKey: null };\n if (!isObj(schema)) return empty;\n const rawSets = schema[LIST_SCHEMA_KEYS.RELATED];\n const sets: Record<string, RelatedSetDecl> = {};\n if (rawSets !== undefined) {\n if (!isObj(rawSets)) fail('x-lt-related must map set names to declarations');\n const names = Object.keys(rawSets);\n if (names.length === 0 || names.length > RELATED_LIMITS.MAX_SETS) fail(`x-lt-related must declare 1..${RELATED_LIMITS.MAX_SETS} sets`);\n for (const name of names) sets[name] = readSet(name, rawSets[name]);\n }\n const columns: RelatedColumnDecl[] = [];\n const rawColumns = schema[LIST_SCHEMA_KEYS.COLUMNS];\n if (Array.isArray(rawColumns)) {\n rawColumns.forEach((col, i) => {\n if (!isObj(col)) return;\n if (col.related !== undefined) columns.push(readRelatedColumn(col, sets, `x-lt-columns[${i}]`));\n if (col.group !== undefined && (typeof col.group !== 'string' || !FOLDS.has(col.group))) {\n fail(`x-lt-columns[${i}].group must be one of ${[...FOLDS].join(', ')}`);\n }\n });\n }\n return { sets, columns, rowDetail: readRowDetail(schema[LIST_SCHEMA_KEYS.ROW_DETAIL], sets), groupKey: readGroupKey(schema) };\n}\n\n/** The declaration's first error, or null when valid. */\nexport function listSchemaRelationsError(schema: unknown): string | null {\n try {\n readListSchemaRelations(schema);\n return null;\n } catch (err) {\n if (err instanceof ListSchemaError) return err.message;\n throw err;\n }\n}\n"],"names":["isEffectivelyClaimed","esc","isAckEscalation","identifierToTitle","id","w","escalationDotClass","e","STATUS_DOT_STYLES","rowContext","parse","s","v","IDENTITY_COLUMN_WIDTH","actionColumnWidth","label","DEFAULT_CLAIM_MINUTES","isClaimable","row","RowActionButton","def","onView","prominent","forceView","authored","action","claim","useClaimEscalation","navigate","useNavigate","location","useLocation","error","setError","useState","spent","fire","err","classes","jsxs","jsx","METADATA_TOKEN","isFacetValue","raw","rowRefinePairs","columnDefs","pairs","seen","metadata","col","bound","_a","key","RefineTrigger","onOpen","className","Search","LIST_SCHEMA_KEYS","RELATED_SOURCES","RELATED_AGGREGATES","GROUP_FOLDS","SPLIT_PATHS","RELATED_LIMITS","FACET_TABLE_LAYOUT","RELATED_DISPLAY_DEFAULT","VALUE_TOKEN","ITEM_PREFIX","PATH_SEGMENT","METADATA_KEY","COUNT_TOKEN","isExtremeAggregate","aggregate","relatedValuePath","source","value","token","path","relatedAggregateKey","relatedCountText","display","count","text","foldExtreme","fold","values","pickMin","texts","a","b","SET_NAME","COUNT_KEY","ListSchemaError","fail","message","isObj","isNonEmptyString","isPlainPath","splitPathError","splitBy","readSet","name","q","hasRoles","hasSegment","roles","readRelatedColumn","sets","at","k","readRowDetail","section","i","j","readGroupKey","schema","FOLDS","readListSchemaRelations","empty","rawSets","names","columns","rawColumns"],"mappings":"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"}
1
+ {"version":3,"file":"validate-YJK_6a7m.js","sources":["../../src/lib/escalation.ts","../../src/lib/identifier-label.ts","../../src/components/escalation/list-view/row-parts.tsx","../../../shared/list-schema/types.ts","../../../shared/list-schema/aggregate.ts","../../../shared/list-schema/validate.ts"],"sourcesContent":["import type { LTEscalationRecord } from '../api/types';\n\nexport function isEffectivelyClaimed(esc: LTEscalationRecord): boolean {\n return !!(\n esc.assigned_to &&\n esc.assigned_until &&\n new Date(esc.assigned_until) > new Date()\n );\n}\n\n/** An ACK escalation has no associated workflow — it's a notification, not a task. */\nexport function isAckEscalation(esc: LTEscalationRecord): boolean {\n return !esc.workflow_type;\n}\n\nexport function isAvailable(esc: LTEscalationRecord): boolean {\n return (\n esc.status === 'pending' &&\n (!esc.assigned_to ||\n !esc.assigned_until ||\n new Date(esc.assigned_until) <= new Date())\n );\n}\n","/**\n * A human label for a code identifier: camelCase, snake_case, kebab-case, and\n * digit runs become Title Case words. `fleetTools` → `Fleet Tools`,\n * `print_order2` → `Print Order 2`. Acronyms keep their capitals: `mcpQuery`\n * → `Mcp Query` is avoided by treating an all-caps run as one word.\n */\nexport function identifierToTitle(id: string): string {\n const words = id\n .replace(/[_\\-.]+/g, ' ')\n .replace(/([a-z\\d])([A-Z])/g, '$1 $2')\n .replace(/([A-Z]+)([A-Z][a-z])/g, '$1 $2')\n .replace(/([a-zA-Z])(\\d)/g, '$1 $2')\n .trim()\n .split(/\\s+/)\n .filter(Boolean);\n return words\n .map((w) => (w === w.toUpperCase() && w.length > 1 ? w : w.charAt(0).toUpperCase() + w.slice(1)))\n .join(' ');\n}\n","import { useState } from 'react';\nimport { useNavigate, useLocation } from 'react-router-dom';\nimport { Search } from 'lucide-react';\nimport type { HelpTokenContext } from '../../../lib/x-lt-help';\nimport { STATUS_DOT_STYLES } from '../../common/display/StatusBadge';\nimport { useClaimEscalation } from '../../../api/escalations';\nimport { isEffectivelyClaimed } from '../../../lib/escalation';\nimport { identifierToTitle } from '../../../lib/identifier-label';\nimport type { RefinePair } from '../RefineDialog';\nimport type { LTEscalationRecord } from '../../../api/types';\n\n/** The row-level pieces every list layout shares: column and action\n * declarations, the row's token context, the row action, and refine. */\n\n/** Dot colour matches the standard table view: blue = pending unclaimed,\n * orange = under a live claim; other statuses follow the shared map. */\nexport function escalationDotClass(e: LTEscalationRecord): string {\n if (e.status === 'pending') {\n return isEffectivelyClaimed(e) ? 'bg-status-claimed-graphic' : 'bg-status-active';\n }\n return STATUS_DOT_STYLES[e.status] ?? 'bg-status-active';\n}\n\nexport interface ColumnDef {\n label: string;\n value: string;\n /** \"age\" renders an ISO timestamp as a compact age with an absolute tooltip. */\n format?: string;\n /**\n * Card-fold behavior when the table folds at narrow widths (same contract\n * as the engineer table): 1 = identity — always visible, forms the card's\n * title line; 2 = folds into the card body as a label/value pair; 3 =\n * dropped in card mode. Undeclared, the first column is the identity and\n * the rest fold.\n */\n priority?: 1 | 2 | 3;\n /** CSS column width (\"40%\", \"12rem\"). Unset columns share the remainder;\n * when no column declares one, the first column holds a fixed 200px so\n * the identity stays legible and every other value keeps its room. */\n width?: string;\n}\n\n/**\n * The per-row action button — the queue-working gesture, always available on\n * every layout. Template-driven: the schema author sets the action, its text,\n * and (for claim) the hold time.\n */\nexport interface RowActionDef {\n /** \"claim\" fires a one-click claim then opens the detail page already\n * claimed; \"view\" just opens it. Default \"claim\". */\n action?: 'claim' | 'view';\n /** Button text. Defaults \"Claim\" / \"View\" by action. */\n label?: string;\n /** Claim hold time in minutes. Default 30 (the platform claim default). */\n durationMinutes?: number;\n /**\n * Launch the detail page's claim-and-submit flow instead of a plain claim:\n * the row is claimed, the form's seeded defaults are resolved, and — when the\n * form declares `x-lt-transition` — the wait screen bridges to the born-assigned\n * follow-on. One list gesture (\"Start Harvesting\") starts the work and lands\n * the person on the next step. The behavior lives on the detail page (form\n * defaults, validation, transition); the list only points there with intent.\n */\n submitOnClaim?: boolean;\n}\n\n/** Build the token context for one escalation row (payloads are JSON strings). */\nexport function rowContext(e: LTEscalationRecord): HelpTokenContext {\n const parse = (s: string | null | undefined): Record<string, unknown> | null => {\n if (!s) return null;\n try {\n const v = JSON.parse(s);\n return v && typeof v === 'object' ? v : null;\n } catch {\n return null;\n }\n };\n return {\n escalation: e as unknown as Record<string, unknown>,\n metadata: e.metadata ?? null,\n envelope: parse(e.envelope),\n payload: parse(e.escalation_payload),\n resolver: parse(e.resolver_payload),\n };\n}\n\n/** The identity column's width when no column declares one: room for a label, no more. */\nexport const IDENTITY_COLUMN_WIDTH = '200px';\n\n/** The action column sizes to its label so the button never wraps or truncates. */\nexport function actionColumnWidth(label: string): string {\n return `${Math.max(6, Math.round(label.length * 0.55 + 3.5))}rem`;\n}\n\nconst DEFAULT_CLAIM_MINUTES = 30;\n\n/** Claim targets pending rows without a live claim window. */\nfunction isClaimable(row: LTEscalationRecord): boolean {\n return row.status === 'pending' && !isEffectivelyClaimed(row);\n}\n\n/**\n * The x-lt-row-action button. `claim` is one click: claim for the template's\n * duration, then open the detail page already claimed — the fast path for\n * working a queue. `view` opens the detail page. Persistent (light accent at\n * rest, saturating on hover); a rejected claim surfaces its message inline.\n */\nexport function RowActionButton({ row, def, onView, prominent, forceView }: {\n row: LTEscalationRecord;\n def?: RowActionDef;\n onView?: () => void;\n /** Hero treatment for the active-card CTA; default is the compact row button. */\n prominent?: boolean;\n /** Render claim templates as view actions (see EscalationListView). */\n forceView?: boolean;\n}) {\n const authored = def?.action ?? 'claim';\n const action = forceView ? 'view' : authored;\n // The authored label describes the authored gesture. When view is forced\n // over a claim template, claim wording would mislead — use the view default.\n const label = authored === action && def?.label\n ? def.label\n : action === 'claim' ? 'Claim' : 'View';\n const claim = useClaimEscalation();\n const navigate = useNavigate();\n const location = useLocation();\n const [error, setError] = useState('');\n\n // A spent gesture stays visible — the disabled button keeps every row's\n // layout uniform instead of leaving a hole on resolved/held rows.\n const spent = action === 'claim' && !isClaimable(row);\n\n const fire = (e: React.MouseEvent) => {\n e.stopPropagation();\n if (action === 'view') {\n onView?.();\n return;\n }\n // Launch the detail page's claim → submit → transition flow. The detail\n // page owns the heavy lifting (seed defaults, validate, resolve, hand-off);\n // the list points there with the intent and the authored claim duration.\n if (def?.submitOnClaim) {\n navigate(`/escalations/detail/${row.id}`, {\n state: { autoStart: true, durationMinutes: def?.durationMinutes, from: location.pathname },\n });\n return;\n }\n setError('');\n claim.mutate(\n { id: row.id, durationMinutes: def?.durationMinutes ?? DEFAULT_CLAIM_MINUTES },\n {\n onSuccess: () => onView?.(),\n onError: (err) => setError((err as Error).message),\n },\n );\n };\n\n const classes = prominent\n ? 'btn-primary text-xs shrink-0 disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:bg-accent'\n : 'px-2 py-0.5 rounded text-2xs font-medium text-accent/70 border border-accent/30 enabled:hover:text-accent enabled:hover:border-accent/60 enabled:hover:bg-accent/5 transition-colors disabled:opacity-50 disabled:cursor-not-allowed whitespace-nowrap';\n\n return (\n <span className=\"inline-flex flex-col items-end gap-0.5\">\n <button\n type=\"button\"\n onClick={fire}\n disabled={claim.isPending || spent}\n title={spent ? (row.status === 'pending' ? 'claimed' : row.status) : undefined}\n className={classes}\n data-testid=\"row-action-button\"\n >\n {claim.isPending ? 'Claiming…' : label}\n </button>\n {error && <span className=\"text-2xs text-status-error\" data-testid=\"row-action-error\">{error}</span>}\n </span>\n );\n}\n\n/** A field value token that is a pure metadata binding — facet-linkable. */\nexport const METADATA_TOKEN = /^\\{\\{\\s*metadata\\.([a-zA-Z0-9_]+)\\s*\\}\\}$/;\n\n/** A metadata value the faceted query can match on: a scalar with something in it. */\nfunction isFacetValue(raw: unknown): raw is string | number | boolean {\n return (typeof raw === 'string' && raw !== '') || typeof raw === 'number' || typeof raw === 'boolean';\n}\n\n/**\n * The refinable facts one row offers: every metadata facet the row carries.\n * Columns the author bound to a metadata value lead, under their authored\n * labels; every other scalar facet follows under a label derived from its\n * key, so the dialog reaches the whole record, not only what the columns\n * show. Feeds the row's RefineDialog.\n */\nexport function rowRefinePairs(row: LTEscalationRecord, columnDefs: ColumnDef[]): RefinePair[] {\n const pairs: RefinePair[] = [];\n const seen = new Set<string>();\n const metadata = row.metadata ?? {};\n for (const col of columnDefs) {\n const bound = typeof col.value === 'string' ? col.value.match(METADATA_TOKEN)?.[1] : undefined;\n if (!bound || seen.has(bound)) continue;\n const raw = metadata[bound];\n if (!isFacetValue(raw)) continue;\n seen.add(bound);\n pairs.push({ key: bound, label: col.label, value: raw, bound: true });\n }\n for (const [key, raw] of Object.entries(metadata)) {\n if (seen.has(key) || !isFacetValue(raw)) continue;\n seen.add(key);\n pairs.push({ key, label: identifierToTitle(key), value: raw });\n }\n return pairs;\n}\n\n/** The always-tappable row/card refine trigger — one magnifier, dialog behind it. */\nexport function RefineTrigger({ onOpen, className = '' }: { onOpen: () => void; className?: string }) {\n return (\n <button\n type=\"button\"\n onClick={(e) => { e.stopPropagation(); onOpen(); }}\n className={`p-1.5 rounded text-text-quaternary/70 hover:text-accent hover:bg-surface-hover transition-colors ${className}`}\n title=\"Refine — filter or search by this row's values\"\n aria-label=\"Refine — filter or search by this row's values\"\n data-testid=\"row-refine\"\n >\n <Search className=\"w-3.5 h-3.5\" strokeWidth={1.5} />\n </button>\n );\n}\n","/**\n * Related sets on a role's list schema. A set is named once under\n * `x-lt-related`; columns (`related`) and row details (`x-lt-row-detail`)\n * reference it by name. The server reads the declaration from the stored\n * list schema, never from the caller.\n *\n * items the host row's accumulated or batch items, each read through its\n * payload or its reciprocal row\n * query rows in other roles that share facets with the host row; facet\n * values are `{{domain.path}}` templates over the host row\n */\n\nexport const LIST_SCHEMA_KEYS = {\n RELATED: 'x-lt-related',\n COLUMNS: 'x-lt-columns',\n ROW_DETAIL: 'x-lt-row-detail',\n GROUP_BY: 'x-lt-group-by',\n LAYOUT: 'x-lt-layout',\n} as const;\n\nexport const RELATED_SOURCES = {\n ITEMS: 'items',\n QUERY: 'query',\n} as const;\nexport type RelatedSource = (typeof RELATED_SOURCES)[keyof typeof RELATED_SOURCES];\n\nexport const RELATED_AGGREGATES = {\n COUNT: 'count',\n /** The least item or row value: numbers as numbers, everything else as text. */\n MIN: 'min',\n MAX: 'max',\n} as const;\nexport type RelatedAggregate = (typeof RELATED_AGGREGATES)[keyof typeof RELATED_AGGREGATES];\n\n/** How a grouped table folds a plain column across a group's rows. */\nexport const GROUP_FOLDS = {\n FIRST: 'first',\n MIN: 'min',\n MAX: 'max',\n SUM: 'sum',\n COUNT: 'count',\n /** The distinct values, in row order, joined: \"standard, express\". */\n DISTINCT: 'distinct',\n} as const;\nexport type GroupFold = (typeof GROUP_FOLDS)[keyof typeof GROUP_FOLDS];\n\n/** Split prefixes. `role` splits a query set by the related row's role. */\nexport const SPLIT_PATHS = {\n ROLE: 'role',\n METADATA: 'metadata.',\n PAYLOAD: 'payload.',\n RECIPROCAL_METADATA: 'reciprocal.metadata.',\n} as const;\n\nexport const RELATED_LIMITS = {\n MAX_SETS: 4,\n MAX_QUERY_ROLES: 12,\n MAX_ROW_DETAIL_SECTIONS: 4,\n MAX_HOSTS: 100,\n ROWS_DEFAULT: 25,\n ROWS_MAX: 100,\n} as const;\n\n/** The table layout that renders related columns, row details, and groups. */\nexport const FACET_TABLE_LAYOUT = 'facet-table';\n\nexport interface RelatedQueryDecl {\n /** Related roles, in display order. Exclusive with `segment`. */\n roles?: string[];\n /** A Pace Board sequence by its first role; resolves to the ordered chain. */\n segment?: string;\n /** Facet containment; string values may be `{{domain.path}}` templates. */\n facets?: Record<string, unknown>;\n /**\n * Facets that narrow the match when the host row carries a value, and\n * drop away when it does not: a lane narrows its tub's look-ahead to its\n * own service level, a tub without lanes keeps the whole destination.\n */\n narrow?: Record<string, unknown>;\n /** Default `pending`. */\n status?: string;\n}\n\nexport interface RelatedSetDecl {\n source: RelatedSource;\n query?: RelatedQueryDecl;\n}\n\n/** A count column's text keyed by count; `default` covers the rest, `{n}` reads the count. */\nexport const RELATED_DISPLAY_DEFAULT = 'default';\n\nexport interface RelatedColumnDecl {\n label: string;\n related: string;\n aggregate?: RelatedAggregate;\n /** min/max: `{{item.payload.<path>}}` on an items set, `{{metadata.<key>}}` on a query set. */\n value?: string;\n /** min/max: how the value renders, as on any facet-table column. */\n format?: string;\n /** count: text by count, e.g. `{ \"0\": \"Ready\", \"default\": \"Waiting on {n}\" }`. */\n display?: Record<string, string>;\n splitBy?: string;\n labels?: Record<string, string>;\n priority?: 1 | 2 | 3;\n /** CSS width, as on any facet-table column. */\n width?: string;\n}\n\nexport interface RowDetailColumnDecl {\n label: string;\n value: string;\n format?: string;\n priority?: 1 | 2 | 3;\n}\n\nexport interface RowDetailSectionDecl {\n related: string;\n label?: string;\n columns: RowDetailColumnDecl[];\n limit?: number;\n}\n\n/** The validated, normalized related declaration of one list schema. */\nexport interface ListSchemaRelations {\n sets: Record<string, RelatedSetDecl>;\n columns: RelatedColumnDecl[];\n rowDetail: RowDetailSectionDecl[];\n /** The metadata key a facet-table groups by, when declared. */\n groupKey: string | null;\n}\n\n/** Per-host aggregate for one (set, splitBy) pair, or one (set, min/max, value) triple. */\nexport interface RelatedHostAggregate {\n count: number;\n /** min/max: the extreme value among `count` values; absent when none carries one. */\n value?: string | number;\n /** Split value to count; a null value is keyed by the empty string. */\n splits?: Record<string, number>;\n /** Items whose reciprocal row the viewer cannot read. */\n hidden?: number;\n /** A host whose facet templates resolved to nothing; the set did not run. */\n unresolved?: boolean;\n}\n\nexport interface RelatedSetResult {\n source: RelatedSource;\n /**\n * The roles whose rows the set reads, which a list follows for live\n * changes: a query set's resolved roles in order, or the roles of an item\n * set's readable reciprocal rows when a column splits by them.\n */\n roles?: string[];\n /** Query sets: role titles, keyed by role. */\n roleLabels?: Record<string, string>;\n /** Query sets: declared roles outside the viewer's full read scope. */\n restricted?: string[];\n /** Keyed by relatedAggregateKey (the splitBy, '' when unsplit, or `min:<path>`), then host id. */\n aggregates: Record<string, Record<string, RelatedHostAggregate>>;\n}\n\nexport interface RelatedAggregatesResponse {\n version: number | null;\n sets: Record<string, RelatedSetResult>;\n}\n\nexport interface RelatedItemEntry {\n itemKey: string;\n payload?: unknown;\n at?: string;\n actor?: string;\n reciprocalId?: string;\n}\n\nexport interface RelatedRow {\n /** Items sets only. */\n item?: RelatedItemEntry;\n /** The reciprocal row (items) or the related row (query); null when unreadable. */\n escalation: Record<string, unknown> | null;\n}\n\nexport interface RelatedRowsResponse {\n source: RelatedSource;\n rows: RelatedRow[];\n total: number;\n restricted?: string[];\n roleLabels?: Record<string, string>;\n}\n","import {\n RELATED_AGGREGATES,\n RELATED_DISPLAY_DEFAULT,\n RELATED_SOURCES,\n SPLIT_PATHS,\n type RelatedAggregate,\n type RelatedColumnDecl,\n} from './types';\n\nconst VALUE_TOKEN = /^\\{\\{\\s*([^{}]+?)\\s*\\}\\}$/;\nconst ITEM_PREFIX = 'item.';\nconst PATH_SEGMENT = /^[a-zA-Z0-9_]+$/;\nconst METADATA_KEY = /^[a-zA-Z0-9_]+$/;\nconst COUNT_TOKEN = /\\{n\\}/g;\n\nexport type RelatedExtreme = typeof RELATED_AGGREGATES.MIN | typeof RELATED_AGGREGATES.MAX;\n\n/** True for the aggregates that pick one value rather than count. */\nexport function isExtremeAggregate(aggregate: unknown): aggregate is RelatedExtreme {\n return aggregate === RELATED_AGGREGATES.MIN || aggregate === RELATED_AGGREGATES.MAX;\n}\n\n/**\n * The stored path a min/max `value` template reads: `payload.<path>` for an\n * items set, `metadata.<key>` for a query set. Null when the template does\n * not fit the set's source.\n */\nexport function relatedValuePath(source: string, value: unknown): string | null {\n if (typeof value !== 'string') return null;\n const token = value.match(VALUE_TOKEN)?.[1];\n if (!token) return null;\n if (source === RELATED_SOURCES.ITEMS) {\n if (!token.startsWith(`${ITEM_PREFIX}${SPLIT_PATHS.PAYLOAD}`)) return null;\n const path = token.slice(ITEM_PREFIX.length + SPLIT_PATHS.PAYLOAD.length);\n return path && path.split('.').every((s) => PATH_SEGMENT.test(s)) ? `${SPLIT_PATHS.PAYLOAD}${path}` : null;\n }\n if (!token.startsWith(SPLIT_PATHS.METADATA)) return null;\n const key = token.slice(SPLIT_PATHS.METADATA.length);\n return METADATA_KEY.test(key) ? token : null;\n}\n\n/** The key a column's per-host aggregates are stored under in its set's result. */\nexport function relatedAggregateKey(col: Pick<RelatedColumnDecl, 'aggregate' | 'splitBy' | 'value'>, source: string): string {\n if (isExtremeAggregate(col.aggregate)) return `${col.aggregate}:${relatedValuePath(source, col.value) ?? ''}`;\n return col.splitBy ?? '';\n}\n\n/** A count column's display text, or null when the column declares none for this count. */\nexport function relatedCountText(display: Record<string, string> | undefined, count: number): string | null {\n if (!display) return null;\n const text = display[String(count)] ?? display[RELATED_DISPLAY_DEFAULT];\n return text === undefined ? null : text.replace(COUNT_TOKEN, String(count));\n}\n\n/**\n * The least or greatest of several values: numbers when every value is a\n * number, otherwise text in code-unit order (ISO instants order as text).\n */\nexport function foldExtreme(fold: RelatedAggregate, values: Array<string | number>): string | number | undefined {\n if (!values.length) return undefined;\n const pickMin = fold === RELATED_AGGREGATES.MIN;\n if (values.every((v) => typeof v === 'number')) {\n return pickMin ? Math.min(...(values as number[])) : Math.max(...(values as number[]));\n }\n const texts = values.map(String).sort((a, b) => (a < b ? -1 : a > b ? 1 : 0));\n return pickMin ? texts[0] : texts[texts.length - 1];\n}\n","import {\n FACET_TABLE_LAYOUT,\n GROUP_FOLDS,\n LIST_SCHEMA_KEYS,\n RELATED_AGGREGATES,\n RELATED_DISPLAY_DEFAULT,\n RELATED_LIMITS,\n RELATED_SOURCES,\n SPLIT_PATHS,\n type ListSchemaRelations,\n type RelatedColumnDecl,\n type RelatedSetDecl,\n type RowDetailSectionDecl,\n} from './types';\nimport { isExtremeAggregate, relatedValuePath } from './aggregate';\n\n/** A metadata key: interpolated as a JSON path, so strictly validated. */\nexport const METADATA_KEY = /^[a-zA-Z0-9_]+$/;\nconst SET_NAME = /^[a-zA-Z][a-zA-Z0-9_-]{0,47}$/;\nconst PATH_SEGMENT = /^[a-zA-Z0-9_]+$/;\nconst COUNT_KEY = /^(0|[1-9][0-9]*)$/;\n\ntype Obj = Record<string, unknown>;\n\nclass ListSchemaError extends Error {}\n\nfunction fail(message: string): never {\n throw new ListSchemaError(message);\n}\n\nfunction isObj(value: unknown): value is Obj {\n return !!value && typeof value === 'object' && !Array.isArray(value);\n}\n\nfunction isNonEmptyString(value: unknown): value is string {\n return typeof value === 'string' && value.trim().length > 0;\n}\n\n/** True when a dotted path has only plain segments. */\nfunction isPlainPath(path: string): boolean {\n return path.length > 0 && path.split('.').every((s) => PATH_SEGMENT.test(s));\n}\n\n/** The split paths a set's source accepts. */\nexport function splitPathError(source: string, splitBy: string): string | null {\n if (source === RELATED_SOURCES.QUERY) {\n if (splitBy === SPLIT_PATHS.ROLE) return null;\n if (splitBy.startsWith(SPLIT_PATHS.METADATA) && METADATA_KEY.test(splitBy.slice(SPLIT_PATHS.METADATA.length))) return null;\n return `splitBy \"${splitBy}\" on a query set must be \"role\" or \"metadata.<key>\"`;\n }\n if (splitBy.startsWith(SPLIT_PATHS.PAYLOAD) && isPlainPath(splitBy.slice(SPLIT_PATHS.PAYLOAD.length))) return null;\n if (splitBy.startsWith(SPLIT_PATHS.RECIPROCAL_METADATA)\n && METADATA_KEY.test(splitBy.slice(SPLIT_PATHS.RECIPROCAL_METADATA.length))) return null;\n return `splitBy \"${splitBy}\" on an items set must be \"payload.<path>\" or \"reciprocal.metadata.<key>\"`;\n}\n\nfunction readSet(name: string, raw: unknown): RelatedSetDecl {\n if (!SET_NAME.test(name)) fail(`x-lt-related name \"${name}\" must start with a letter (letters, digits, _ or -)`);\n if (!isObj(raw)) fail(`x-lt-related.${name} must be an object`);\n if (raw.source === RELATED_SOURCES.ITEMS) {\n if (raw.query !== undefined) fail(`x-lt-related.${name}: an items set takes no query`);\n return { source: RELATED_SOURCES.ITEMS };\n }\n if (raw.source !== RELATED_SOURCES.QUERY) fail(`x-lt-related.${name}.source must be \"items\" or \"query\"`);\n const q = raw.query;\n if (!isObj(q)) fail(`x-lt-related.${name}.query is required for a query set`);\n const hasRoles = q.roles !== undefined;\n const hasSegment = q.segment !== undefined;\n if (hasRoles === hasSegment) fail(`x-lt-related.${name}.query takes exactly one of roles or segment`);\n if (hasRoles) {\n const roles = q.roles;\n if (!Array.isArray(roles) || roles.length === 0 || roles.length > RELATED_LIMITS.MAX_QUERY_ROLES\n || !roles.every(isNonEmptyString)) {\n fail(`x-lt-related.${name}.query.roles must be 1..${RELATED_LIMITS.MAX_QUERY_ROLES} role names`);\n }\n } else if (!isNonEmptyString(q.segment)) {\n fail(`x-lt-related.${name}.query.segment must be a role name`);\n }\n if (!isObj(q.facets) || Object.keys(q.facets).length === 0) {\n fail(`x-lt-related.${name}.query.facets must name at least one facet`);\n }\n for (const [key, value] of Object.entries(q.facets)) {\n if (!METADATA_KEY.test(key)) fail(`x-lt-related.${name}.query.facets key \"${key}\" must be letters, digits or _`);\n if (value === null || typeof value === 'object') fail(`x-lt-related.${name}.query.facets.${key} must be a scalar or template`);\n }\n if (q.narrow !== undefined) {\n if (!isObj(q.narrow) || Object.keys(q.narrow).length === 0) fail(`x-lt-related.${name}.query.narrow must name at least one facet`);\n for (const [key, value] of Object.entries(q.narrow)) {\n if (!METADATA_KEY.test(key)) fail(`x-lt-related.${name}.query.narrow key \"${key}\" must be letters, digits or _`);\n if (key in q.facets) fail(`x-lt-related.${name}.query.narrow.${key} is already a required facet`);\n if (value === null || typeof value === 'object') fail(`x-lt-related.${name}.query.narrow.${key} must be a scalar or template`);\n }\n }\n if (q.status !== undefined && !isNonEmptyString(q.status)) fail(`x-lt-related.${name}.query.status must be a status`);\n return {\n source: RELATED_SOURCES.QUERY,\n query: {\n ...(hasRoles ? { roles: [...(q.roles as string[])] } : { segment: q.segment as string }),\n facets: { ...q.facets },\n ...(q.narrow !== undefined ? { narrow: { ...(q.narrow as Obj) } } : {}),\n ...(q.status !== undefined ? { status: q.status as string } : {}),\n },\n };\n}\n\nfunction readRelatedColumn(raw: Obj, sets: Record<string, RelatedSetDecl>, at: string): RelatedColumnDecl {\n const name = raw.related;\n if (!isNonEmptyString(name) || !sets[name]) fail(`${at}.related must name a set declared in x-lt-related`);\n if (raw.aggregate !== undefined && raw.aggregate !== RELATED_AGGREGATES.COUNT && !isExtremeAggregate(raw.aggregate)) {\n fail(`${at}.aggregate must be \"count\", \"min\" or \"max\"`);\n }\n const source = sets[name].source;\n if (isExtremeAggregate(raw.aggregate)) {\n if (!relatedValuePath(source, raw.value)) {\n fail(source === RELATED_SOURCES.ITEMS\n ? `${at}.value must be \"{{item.payload.<path>}}\" on an items set`\n : `${at}.value must be \"{{metadata.<key>}}\" on a query set`);\n }\n if (raw.splitBy !== undefined) fail(`${at}: a ${raw.aggregate} column takes no splitBy`);\n if (raw.display !== undefined) fail(`${at}: display applies to a count column`);\n } else if (raw.value !== undefined) {\n fail(`${at}.value applies to a min or max column`);\n }\n if (raw.format !== undefined && typeof raw.format !== 'string') fail(`${at}.format must be a format name`);\n if (raw.display !== undefined) {\n if (!isObj(raw.display) || Object.keys(raw.display).length === 0\n || !Object.entries(raw.display).every(([k, v]) => (k === RELATED_DISPLAY_DEFAULT || COUNT_KEY.test(k)) && typeof v === 'string')) {\n fail(`${at}.display must map counts (or \"default\") to display text`);\n }\n }\n if (raw.splitBy !== undefined) {\n if (!isNonEmptyString(raw.splitBy)) fail(`${at}.splitBy must be a path`);\n const err = splitPathError(sets[name].source, raw.splitBy);\n if (err) fail(`${at}: ${err}`);\n }\n if (raw.labels !== undefined && (!isObj(raw.labels) || !Object.values(raw.labels).every((v) => typeof v === 'string'))) {\n fail(`${at}.labels must map values to display text`);\n }\n return raw as unknown as RelatedColumnDecl;\n}\n\nfunction readRowDetail(raw: unknown, sets: Record<string, RelatedSetDecl>): RowDetailSectionDecl[] {\n if (raw === undefined) return [];\n if (!Array.isArray(raw) || raw.length === 0 || raw.length > RELATED_LIMITS.MAX_ROW_DETAIL_SECTIONS) {\n fail(`x-lt-row-detail must be 1..${RELATED_LIMITS.MAX_ROW_DETAIL_SECTIONS} sections`);\n }\n return raw.map((section, i) => {\n const at = `x-lt-row-detail[${i}]`;\n if (!isObj(section)) fail(`${at} must be an object`);\n if (!isNonEmptyString(section.related) || !sets[section.related]) fail(`${at}.related must name a set declared in x-lt-related`);\n if (!Array.isArray(section.columns) || section.columns.length === 0) fail(`${at}.columns must list at least one column`);\n for (const [j, col] of section.columns.entries()) {\n if (!isObj(col) || !isNonEmptyString(col.label) || typeof col.value !== 'string') {\n fail(`${at}.columns[${j}] needs a label and a value template`);\n }\n }\n if (section.limit !== undefined && (!Number.isInteger(section.limit) || (section.limit as number) < 1\n || (section.limit as number) > RELATED_LIMITS.ROWS_MAX)) {\n fail(`${at}.limit must be 1..${RELATED_LIMITS.ROWS_MAX}`);\n }\n return section as unknown as RowDetailSectionDecl;\n });\n}\n\nfunction readGroupKey(schema: Obj): string | null {\n const raw = schema[LIST_SCHEMA_KEYS.GROUP_BY];\n if (raw === undefined || schema[LIST_SCHEMA_KEYS.LAYOUT] !== FACET_TABLE_LAYOUT) return null;\n const key = typeof raw === 'string' && raw.startsWith(SPLIT_PATHS.METADATA) ? raw.slice(SPLIT_PATHS.METADATA.length) : '';\n if (!METADATA_KEY.test(key)) fail('x-lt-group-by on a facet-table must be \"metadata.<key>\"');\n return key;\n}\n\nconst FOLDS = new Set<string>(Object.values(GROUP_FOLDS));\n\n/**\n * Validate and normalize the related declaration of a list schema. Throws on\n * an invalid declaration; keys outside this vocabulary stay opaque.\n */\nexport function readListSchemaRelations(schema: unknown): ListSchemaRelations {\n const empty: ListSchemaRelations = { sets: {}, columns: [], rowDetail: [], groupKey: null };\n if (!isObj(schema)) return empty;\n const rawSets = schema[LIST_SCHEMA_KEYS.RELATED];\n const sets: Record<string, RelatedSetDecl> = {};\n if (rawSets !== undefined) {\n if (!isObj(rawSets)) fail('x-lt-related must map set names to declarations');\n const names = Object.keys(rawSets);\n if (names.length === 0 || names.length > RELATED_LIMITS.MAX_SETS) fail(`x-lt-related must declare 1..${RELATED_LIMITS.MAX_SETS} sets`);\n for (const name of names) sets[name] = readSet(name, rawSets[name]);\n }\n const columns: RelatedColumnDecl[] = [];\n const rawColumns = schema[LIST_SCHEMA_KEYS.COLUMNS];\n if (Array.isArray(rawColumns)) {\n rawColumns.forEach((col, i) => {\n if (!isObj(col)) return;\n if (col.related !== undefined) columns.push(readRelatedColumn(col, sets, `x-lt-columns[${i}]`));\n if (col.group !== undefined && (typeof col.group !== 'string' || !FOLDS.has(col.group))) {\n fail(`x-lt-columns[${i}].group must be one of ${[...FOLDS].join(', ')}`);\n }\n });\n }\n return { sets, columns, rowDetail: readRowDetail(schema[LIST_SCHEMA_KEYS.ROW_DETAIL], sets), groupKey: readGroupKey(schema) };\n}\n\n/** The declaration's first error, or null when valid. */\nexport function listSchemaRelationsError(schema: unknown): string | null {\n try {\n readListSchemaRelations(schema);\n return null;\n } catch (err) {\n if (err instanceof ListSchemaError) return err.message;\n throw err;\n }\n}\n"],"names":["isEffectivelyClaimed","esc","isAckEscalation","identifierToTitle","id","w","escalationDotClass","e","STATUS_DOT_STYLES","rowContext","parse","s","v","IDENTITY_COLUMN_WIDTH","actionColumnWidth","label","DEFAULT_CLAIM_MINUTES","isClaimable","row","RowActionButton","def","onView","prominent","forceView","authored","action","claim","useClaimEscalation","navigate","useNavigate","location","useLocation","error","setError","useState","spent","fire","err","classes","jsxs","jsx","METADATA_TOKEN","isFacetValue","raw","rowRefinePairs","columnDefs","pairs","seen","metadata","col","bound","_a","key","RefineTrigger","onOpen","className","Search","LIST_SCHEMA_KEYS","RELATED_SOURCES","RELATED_AGGREGATES","GROUP_FOLDS","SPLIT_PATHS","RELATED_LIMITS","FACET_TABLE_LAYOUT","RELATED_DISPLAY_DEFAULT","VALUE_TOKEN","ITEM_PREFIX","PATH_SEGMENT","METADATA_KEY","COUNT_TOKEN","isExtremeAggregate","aggregate","relatedValuePath","source","value","token","path","relatedAggregateKey","relatedCountText","display","count","text","foldExtreme","fold","values","pickMin","texts","a","b","SET_NAME","COUNT_KEY","ListSchemaError","fail","message","isObj","isNonEmptyString","isPlainPath","splitPathError","splitBy","readSet","name","q","hasRoles","hasSegment","roles","readRelatedColumn","sets","at","k","readRowDetail","section","i","j","readGroupKey","schema","FOLDS","readListSchemaRelations","empty","rawSets","names","columns","rawColumns"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- import{u as l,b as a,c as s}from"./vendor-query-DvWWuNIB.js";import{m as r}from"./index-ygxyAKcc.js";function f(e={}){const o=new URLSearchParams;return e.status&&o.set("status",e.status),e.graph_topic&&o.set("graph_topic",e.graph_topic),e.app_id&&o.set("app_id",e.app_id),e.search&&o.set("search",e.search),e.set_id&&o.set("set_id",e.set_id),e.limit&&o.set("limit",String(e.limit)),e.offset!==void 0&&o.set("offset",String(e.offset)),l({queryKey:["yamlWorkflows",e],queryFn:()=>r(`/yaml-workflows?${o}`)})}function m(){return l({queryKey:["yamlWorkflowAppIds"],queryFn:()=>r("/yaml-workflows/app-ids")})}function c(e){return l({queryKey:["yamlWorkflows",e],queryFn:()=>r(`/yaml-workflows/${e}`),enabled:!!e})}function w(){const e=a();return s({mutationFn:o=>r("/yaml-workflows",{method:"POST",body:JSON.stringify(o)}),onSuccess:()=>{e.invalidateQueries({queryKey:["yamlWorkflows"],refetchType:"all"})}})}function k(){const e=a();return s({mutationFn:o=>r(`/yaml-workflows/${o}/deploy`,{method:"POST"}),onSuccess:()=>{e.invalidateQueries({queryKey:["yamlWorkflows"],refetchType:"all"})}})}function p(){const e=a();return s({mutationFn:o=>r(`/yaml-workflows/${o}/activate`,{method:"POST"}),onSuccess:()=>{e.invalidateQueries({queryKey:["yamlWorkflows"],refetchType:"all"})}})}function d(){const e=a();return s({mutationFn:({id:o,data:t,sync:n,execute_as:u})=>r(`/yaml-workflows/${o}/invoke`,{method:"POST",body:JSON.stringify({data:t,sync:n,...u?{execute_as:u}:{}})}),onSuccess:()=>{e.invalidateQueries({queryKey:["yamlWorkflows"],refetchType:"all"})}})}function h(){const e=a();return s({mutationFn:({id:o,task_queue:t,compilation_feedback:n})=>r(`/yaml-workflows/${o}/regenerate`,{method:"POST",body:JSON.stringify({task_queue:t,compilation_feedback:n})}),onSuccess:()=>{e.invalidateQueries({queryKey:["yamlWorkflows"],refetchType:"all"})}})}function q(){const e=a();return s({mutationFn:o=>r(`/yaml-workflows/${o}/archive`,{method:"POST"}),onSuccess:()=>{e.invalidateQueries({queryKey:["yamlWorkflows"],refetchType:"all"})}})}function W(){const e=a();return s({mutationFn:o=>r(`/yaml-workflows/${o}/restore`,{method:"POST"}),onSuccess:()=>{e.invalidateQueries({queryKey:["yamlWorkflows"],refetchType:"all"}),e.invalidateQueries({queryKey:["yamlWorkflow"],refetchType:"all"})}})}function S(){const e=a();return s({mutationFn:({id:o,...t})=>r(`/yaml-workflows/${o}`,{method:"PUT",body:JSON.stringify(t)}),onSuccess:()=>{e.invalidateQueries({queryKey:["yamlWorkflows"],refetchType:"all"})}})}function v(){const e=a();return s({mutationFn:o=>r(`/yaml-workflows/${o}`,{method:"DELETE"}),onSuccess:()=>{e.invalidateQueries({queryKey:["yamlWorkflows"],refetchType:"all"})}})}function T(e){return l({queryKey:["yamlWorkflows",e,"versions"],queryFn:()=>r(`/yaml-workflows/${e}/versions`),enabled:!!e})}function F(e,o){return l({queryKey:["yamlWorkflows",e,"versions",o],queryFn:()=>r(`/yaml-workflows/${e}/versions/${o}`),enabled:!!e&&o!==null})}export{m as a,S as b,c,T as d,k as e,p as f,q as g,W as h,v as i,h as j,F as k,d as l,w as m,f as u};
2
- //# sourceMappingURL=yaml-workflows-Dyln3od-.js.map
1
+ import{u as l,b as a,c as s}from"./vendor-query-DvWWuNIB.js";import{m as r}from"./index-CU2UW2y6.js";function f(e={}){const o=new URLSearchParams;return e.status&&o.set("status",e.status),e.graph_topic&&o.set("graph_topic",e.graph_topic),e.app_id&&o.set("app_id",e.app_id),e.search&&o.set("search",e.search),e.set_id&&o.set("set_id",e.set_id),e.limit&&o.set("limit",String(e.limit)),e.offset!==void 0&&o.set("offset",String(e.offset)),l({queryKey:["yamlWorkflows",e],queryFn:()=>r(`/yaml-workflows?${o}`)})}function m(){return l({queryKey:["yamlWorkflowAppIds"],queryFn:()=>r("/yaml-workflows/app-ids")})}function c(e){return l({queryKey:["yamlWorkflows",e],queryFn:()=>r(`/yaml-workflows/${e}`),enabled:!!e})}function w(){const e=a();return s({mutationFn:o=>r("/yaml-workflows",{method:"POST",body:JSON.stringify(o)}),onSuccess:()=>{e.invalidateQueries({queryKey:["yamlWorkflows"],refetchType:"all"})}})}function k(){const e=a();return s({mutationFn:o=>r(`/yaml-workflows/${o}/deploy`,{method:"POST"}),onSuccess:()=>{e.invalidateQueries({queryKey:["yamlWorkflows"],refetchType:"all"})}})}function p(){const e=a();return s({mutationFn:o=>r(`/yaml-workflows/${o}/activate`,{method:"POST"}),onSuccess:()=>{e.invalidateQueries({queryKey:["yamlWorkflows"],refetchType:"all"})}})}function d(){const e=a();return s({mutationFn:({id:o,data:t,sync:n,execute_as:u})=>r(`/yaml-workflows/${o}/invoke`,{method:"POST",body:JSON.stringify({data:t,sync:n,...u?{execute_as:u}:{}})}),onSuccess:()=>{e.invalidateQueries({queryKey:["yamlWorkflows"],refetchType:"all"})}})}function h(){const e=a();return s({mutationFn:({id:o,task_queue:t,compilation_feedback:n})=>r(`/yaml-workflows/${o}/regenerate`,{method:"POST",body:JSON.stringify({task_queue:t,compilation_feedback:n})}),onSuccess:()=>{e.invalidateQueries({queryKey:["yamlWorkflows"],refetchType:"all"})}})}function q(){const e=a();return s({mutationFn:o=>r(`/yaml-workflows/${o}/archive`,{method:"POST"}),onSuccess:()=>{e.invalidateQueries({queryKey:["yamlWorkflows"],refetchType:"all"})}})}function W(){const e=a();return s({mutationFn:o=>r(`/yaml-workflows/${o}/restore`,{method:"POST"}),onSuccess:()=>{e.invalidateQueries({queryKey:["yamlWorkflows"],refetchType:"all"}),e.invalidateQueries({queryKey:["yamlWorkflow"],refetchType:"all"})}})}function S(){const e=a();return s({mutationFn:({id:o,...t})=>r(`/yaml-workflows/${o}`,{method:"PUT",body:JSON.stringify(t)}),onSuccess:()=>{e.invalidateQueries({queryKey:["yamlWorkflows"],refetchType:"all"})}})}function v(){const e=a();return s({mutationFn:o=>r(`/yaml-workflows/${o}`,{method:"DELETE"}),onSuccess:()=>{e.invalidateQueries({queryKey:["yamlWorkflows"],refetchType:"all"})}})}function T(e){return l({queryKey:["yamlWorkflows",e,"versions"],queryFn:()=>r(`/yaml-workflows/${e}/versions`),enabled:!!e})}function F(e,o){return l({queryKey:["yamlWorkflows",e,"versions",o],queryFn:()=>r(`/yaml-workflows/${e}/versions/${o}`),enabled:!!e&&o!==null})}export{m as a,S as b,c,T as d,k as e,p as f,q as g,W as h,v as i,h as j,F as k,d as l,w as m,f as u};
2
+ //# sourceMappingURL=yaml-workflows-D97Bz6li.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"yaml-workflows-Dyln3od-.js","sources":["../../src/api/yaml-workflows.ts"],"sourcesContent":["import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';\nimport { apiFetch } from './client';\nimport type { LTYamlWorkflowRecord, LTYamlWorkflowStatus, LTYamlWorkflowVersion } from './types';\n\ninterface YamlWorkflowListResponse {\n workflows: LTYamlWorkflowRecord[];\n total: number;\n}\n\ninterface YamlWorkflowFilters {\n status?: LTYamlWorkflowStatus;\n graph_topic?: string;\n app_id?: string;\n search?: string;\n set_id?: string;\n limit?: number;\n offset?: number;\n}\n\nexport function useYamlWorkflows(filters: YamlWorkflowFilters = {}) {\n const params = new URLSearchParams();\n if (filters.status) params.set('status', filters.status);\n if (filters.graph_topic) params.set('graph_topic', filters.graph_topic);\n if (filters.app_id) params.set('app_id', filters.app_id);\n if (filters.search) params.set('search', filters.search);\n if (filters.set_id) params.set('set_id', filters.set_id);\n if (filters.limit) params.set('limit', String(filters.limit));\n if (filters.offset !== undefined) params.set('offset', String(filters.offset));\n\n return useQuery<YamlWorkflowListResponse>({\n queryKey: ['yamlWorkflows', filters],\n queryFn: () => apiFetch(`/yaml-workflows?${params}`),\n });\n}\n\nexport function useYamlWorkflowAppIds() {\n return useQuery<{ app_ids: string[] }>({\n queryKey: ['yamlWorkflowAppIds'],\n queryFn: () => apiFetch('/yaml-workflows/app-ids'),\n });\n}\n\nexport function useYamlWorkflowByTopic(graphTopic: string | undefined, appId?: string) {\n return useQuery<YamlWorkflowListResponse>({\n queryKey: ['yamlWorkflows', 'byTopic', graphTopic, appId],\n queryFn: () => {\n const params = new URLSearchParams({ graph_topic: graphTopic!, limit: '1' });\n if (appId) params.set('app_id', appId);\n return apiFetch(`/yaml-workflows?${params}`);\n },\n enabled: !!graphTopic,\n });\n}\n\nexport function useYamlWorkflow(id: string) {\n return useQuery<LTYamlWorkflowRecord>({\n queryKey: ['yamlWorkflows', id],\n queryFn: () => apiFetch(`/yaml-workflows/${id}`),\n enabled: !!id,\n });\n}\n\nexport function useCreateYamlWorkflow() {\n const queryClient = useQueryClient();\n return useMutation({\n mutationFn: (input: {\n workflow_id: string;\n task_queue: string;\n workflow_name: string;\n name: string;\n description?: string;\n app_id?: string;\n subscribes?: string;\n tags?: string[];\n compilation_feedback?: string;\n }) =>\n apiFetch<LTYamlWorkflowRecord>('/yaml-workflows', {\n method: 'POST',\n body: JSON.stringify(input),\n }),\n onSuccess: () => {\n queryClient.invalidateQueries({ queryKey: ['yamlWorkflows'], refetchType: 'all' });\n },\n });\n}\n\nexport function useDeployYamlWorkflow() {\n const queryClient = useQueryClient();\n return useMutation({\n mutationFn: (id: string) =>\n apiFetch<LTYamlWorkflowRecord>(`/yaml-workflows/${id}/deploy`, {\n method: 'POST',\n }),\n onSuccess: () => {\n queryClient.invalidateQueries({ queryKey: ['yamlWorkflows'], refetchType: 'all' });\n },\n });\n}\n\nexport function useActivateYamlWorkflow() {\n const queryClient = useQueryClient();\n return useMutation({\n mutationFn: (id: string) =>\n apiFetch<LTYamlWorkflowRecord>(`/yaml-workflows/${id}/activate`, {\n method: 'POST',\n }),\n onSuccess: () => {\n queryClient.invalidateQueries({ queryKey: ['yamlWorkflows'], refetchType: 'all' });\n },\n });\n}\n\nexport function useInvokeYamlWorkflow() {\n const queryClient = useQueryClient();\n return useMutation({\n mutationFn: ({ id, data, sync, execute_as }: { id: string; data: Record<string, unknown>; sync?: boolean; execute_as?: string }) =>\n apiFetch<{ result?: unknown; job_id?: string }>(`/yaml-workflows/${id}/invoke`, {\n method: 'POST',\n body: JSON.stringify({ data, sync, ...(execute_as ? { execute_as } : {}) }),\n }),\n onSuccess: () => {\n queryClient.invalidateQueries({ queryKey: ['yamlWorkflows'], refetchType: 'all' });\n },\n });\n}\n\nexport function useRegenerateYamlWorkflow() {\n const queryClient = useQueryClient();\n return useMutation({\n mutationFn: ({ id, task_queue, compilation_feedback }: { id: string; task_queue?: string; compilation_feedback?: string }) =>\n apiFetch<LTYamlWorkflowRecord>(`/yaml-workflows/${id}/regenerate`, {\n method: 'POST',\n body: JSON.stringify({ task_queue, compilation_feedback }),\n }),\n onSuccess: () => {\n queryClient.invalidateQueries({ queryKey: ['yamlWorkflows'], refetchType: 'all' });\n },\n });\n}\n\nexport function useArchiveYamlWorkflow() {\n const queryClient = useQueryClient();\n return useMutation({\n mutationFn: (id: string) =>\n apiFetch<LTYamlWorkflowRecord>(`/yaml-workflows/${id}/archive`, {\n method: 'POST',\n }),\n onSuccess: () => {\n queryClient.invalidateQueries({ queryKey: ['yamlWorkflows'], refetchType: 'all' });\n },\n });\n}\n\nexport function useRestoreYamlWorkflow() {\n const queryClient = useQueryClient();\n return useMutation({\n mutationFn: (id: string) =>\n apiFetch<LTYamlWorkflowRecord>(`/yaml-workflows/${id}/restore`, {\n method: 'POST',\n }),\n onSuccess: () => {\n queryClient.invalidateQueries({ queryKey: ['yamlWorkflows'], refetchType: 'all' });\n queryClient.invalidateQueries({ queryKey: ['yamlWorkflow'], refetchType: 'all' });\n },\n });\n}\n\nexport function useUpdateYamlWorkflow() {\n const queryClient = useQueryClient();\n return useMutation({\n mutationFn: ({ id, ...updates }: { id: string; yaml_content?: string; name?: string; description?: string; input_schema?: Record<string, unknown>; output_schema?: Record<string, unknown>; tags?: string[]; app_id?: string; graph_topic?: string }) =>\n apiFetch<LTYamlWorkflowRecord>(`/yaml-workflows/${id}`, {\n method: 'PUT',\n body: JSON.stringify(updates),\n }),\n onSuccess: () => {\n queryClient.invalidateQueries({ queryKey: ['yamlWorkflows'], refetchType: 'all' });\n },\n });\n}\n\nexport function useDeleteYamlWorkflow() {\n const queryClient = useQueryClient();\n return useMutation({\n mutationFn: (id: string) =>\n apiFetch<{ deleted: boolean }>(`/yaml-workflows/${id}`, {\n method: 'DELETE',\n }),\n onSuccess: () => {\n queryClient.invalidateQueries({ queryKey: ['yamlWorkflows'], refetchType: 'all' });\n },\n });\n}\n\n// ── Cron scheduling ─────────────────────────────────────────────\n\nexport function useSetYamlCron() {\n const queryClient = useQueryClient();\n return useMutation({\n mutationFn: ({ id, cron_schedule, cron_envelope, execute_as }: {\n id: string;\n cron_schedule: string;\n cron_envelope?: Record<string, unknown> | null;\n execute_as?: string | null;\n }) =>\n apiFetch<LTYamlWorkflowRecord>(`/yaml-workflows/${id}/cron`, {\n method: 'PUT',\n body: JSON.stringify({ cron_schedule, cron_envelope, execute_as }),\n }),\n onSuccess: () => {\n queryClient.invalidateQueries({ queryKey: ['yamlWorkflows'], refetchType: 'all' });\n },\n });\n}\n\nexport function useClearYamlCron() {\n const queryClient = useQueryClient();\n return useMutation({\n mutationFn: (id: string) =>\n apiFetch<LTYamlWorkflowRecord>(`/yaml-workflows/${id}/cron`, {\n method: 'DELETE',\n }),\n onSuccess: () => {\n queryClient.invalidateQueries({ queryKey: ['yamlWorkflows'], refetchType: 'all' });\n },\n });\n}\n\n// ── Version history ─────────────────────────────────────────────\n\nexport function useYamlWorkflowVersions(id: string) {\n return useQuery<{ versions: LTYamlWorkflowVersion[]; total: number }>({\n queryKey: ['yamlWorkflows', id, 'versions'],\n queryFn: () => apiFetch(`/yaml-workflows/${id}/versions`),\n enabled: !!id,\n });\n}\n\nexport function useYamlWorkflowVersion(id: string, version: number | null) {\n return useQuery<LTYamlWorkflowVersion>({\n queryKey: ['yamlWorkflows', id, 'versions', version],\n queryFn: () => apiFetch(`/yaml-workflows/${id}/versions/${version}`),\n enabled: !!id && version !== null,\n });\n}\n"],"names":["useYamlWorkflows","filters","params","useQuery","apiFetch","useYamlWorkflowAppIds","useYamlWorkflow","id","useCreateYamlWorkflow","queryClient","useQueryClient","useMutation","input","useDeployYamlWorkflow","useActivateYamlWorkflow","useInvokeYamlWorkflow","data","sync","execute_as","useRegenerateYamlWorkflow","task_queue","compilation_feedback","useArchiveYamlWorkflow","useRestoreYamlWorkflow","useUpdateYamlWorkflow","updates","useDeleteYamlWorkflow","useYamlWorkflowVersions","useYamlWorkflowVersion","version"],"mappings":"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"}
1
+ {"version":3,"file":"yaml-workflows-D97Bz6li.js","sources":["../../src/api/yaml-workflows.ts"],"sourcesContent":["import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';\nimport { apiFetch } from './client';\nimport type { LTYamlWorkflowRecord, LTYamlWorkflowStatus, LTYamlWorkflowVersion } from './types';\n\ninterface YamlWorkflowListResponse {\n workflows: LTYamlWorkflowRecord[];\n total: number;\n}\n\ninterface YamlWorkflowFilters {\n status?: LTYamlWorkflowStatus;\n graph_topic?: string;\n app_id?: string;\n search?: string;\n set_id?: string;\n limit?: number;\n offset?: number;\n}\n\nexport function useYamlWorkflows(filters: YamlWorkflowFilters = {}) {\n const params = new URLSearchParams();\n if (filters.status) params.set('status', filters.status);\n if (filters.graph_topic) params.set('graph_topic', filters.graph_topic);\n if (filters.app_id) params.set('app_id', filters.app_id);\n if (filters.search) params.set('search', filters.search);\n if (filters.set_id) params.set('set_id', filters.set_id);\n if (filters.limit) params.set('limit', String(filters.limit));\n if (filters.offset !== undefined) params.set('offset', String(filters.offset));\n\n return useQuery<YamlWorkflowListResponse>({\n queryKey: ['yamlWorkflows', filters],\n queryFn: () => apiFetch(`/yaml-workflows?${params}`),\n });\n}\n\nexport function useYamlWorkflowAppIds() {\n return useQuery<{ app_ids: string[] }>({\n queryKey: ['yamlWorkflowAppIds'],\n queryFn: () => apiFetch('/yaml-workflows/app-ids'),\n });\n}\n\nexport function useYamlWorkflowByTopic(graphTopic: string | undefined, appId?: string) {\n return useQuery<YamlWorkflowListResponse>({\n queryKey: ['yamlWorkflows', 'byTopic', graphTopic, appId],\n queryFn: () => {\n const params = new URLSearchParams({ graph_topic: graphTopic!, limit: '1' });\n if (appId) params.set('app_id', appId);\n return apiFetch(`/yaml-workflows?${params}`);\n },\n enabled: !!graphTopic,\n });\n}\n\nexport function useYamlWorkflow(id: string) {\n return useQuery<LTYamlWorkflowRecord>({\n queryKey: ['yamlWorkflows', id],\n queryFn: () => apiFetch(`/yaml-workflows/${id}`),\n enabled: !!id,\n });\n}\n\nexport function useCreateYamlWorkflow() {\n const queryClient = useQueryClient();\n return useMutation({\n mutationFn: (input: {\n workflow_id: string;\n task_queue: string;\n workflow_name: string;\n name: string;\n description?: string;\n app_id?: string;\n subscribes?: string;\n tags?: string[];\n compilation_feedback?: string;\n }) =>\n apiFetch<LTYamlWorkflowRecord>('/yaml-workflows', {\n method: 'POST',\n body: JSON.stringify(input),\n }),\n onSuccess: () => {\n queryClient.invalidateQueries({ queryKey: ['yamlWorkflows'], refetchType: 'all' });\n },\n });\n}\n\nexport function useDeployYamlWorkflow() {\n const queryClient = useQueryClient();\n return useMutation({\n mutationFn: (id: string) =>\n apiFetch<LTYamlWorkflowRecord>(`/yaml-workflows/${id}/deploy`, {\n method: 'POST',\n }),\n onSuccess: () => {\n queryClient.invalidateQueries({ queryKey: ['yamlWorkflows'], refetchType: 'all' });\n },\n });\n}\n\nexport function useActivateYamlWorkflow() {\n const queryClient = useQueryClient();\n return useMutation({\n mutationFn: (id: string) =>\n apiFetch<LTYamlWorkflowRecord>(`/yaml-workflows/${id}/activate`, {\n method: 'POST',\n }),\n onSuccess: () => {\n queryClient.invalidateQueries({ queryKey: ['yamlWorkflows'], refetchType: 'all' });\n },\n });\n}\n\nexport function useInvokeYamlWorkflow() {\n const queryClient = useQueryClient();\n return useMutation({\n mutationFn: ({ id, data, sync, execute_as }: { id: string; data: Record<string, unknown>; sync?: boolean; execute_as?: string }) =>\n apiFetch<{ result?: unknown; job_id?: string }>(`/yaml-workflows/${id}/invoke`, {\n method: 'POST',\n body: JSON.stringify({ data, sync, ...(execute_as ? { execute_as } : {}) }),\n }),\n onSuccess: () => {\n queryClient.invalidateQueries({ queryKey: ['yamlWorkflows'], refetchType: 'all' });\n },\n });\n}\n\nexport function useRegenerateYamlWorkflow() {\n const queryClient = useQueryClient();\n return useMutation({\n mutationFn: ({ id, task_queue, compilation_feedback }: { id: string; task_queue?: string; compilation_feedback?: string }) =>\n apiFetch<LTYamlWorkflowRecord>(`/yaml-workflows/${id}/regenerate`, {\n method: 'POST',\n body: JSON.stringify({ task_queue, compilation_feedback }),\n }),\n onSuccess: () => {\n queryClient.invalidateQueries({ queryKey: ['yamlWorkflows'], refetchType: 'all' });\n },\n });\n}\n\nexport function useArchiveYamlWorkflow() {\n const queryClient = useQueryClient();\n return useMutation({\n mutationFn: (id: string) =>\n apiFetch<LTYamlWorkflowRecord>(`/yaml-workflows/${id}/archive`, {\n method: 'POST',\n }),\n onSuccess: () => {\n queryClient.invalidateQueries({ queryKey: ['yamlWorkflows'], refetchType: 'all' });\n },\n });\n}\n\nexport function useRestoreYamlWorkflow() {\n const queryClient = useQueryClient();\n return useMutation({\n mutationFn: (id: string) =>\n apiFetch<LTYamlWorkflowRecord>(`/yaml-workflows/${id}/restore`, {\n method: 'POST',\n }),\n onSuccess: () => {\n queryClient.invalidateQueries({ queryKey: ['yamlWorkflows'], refetchType: 'all' });\n queryClient.invalidateQueries({ queryKey: ['yamlWorkflow'], refetchType: 'all' });\n },\n });\n}\n\nexport function useUpdateYamlWorkflow() {\n const queryClient = useQueryClient();\n return useMutation({\n mutationFn: ({ id, ...updates }: { id: string; yaml_content?: string; name?: string; description?: string; input_schema?: Record<string, unknown>; output_schema?: Record<string, unknown>; tags?: string[]; app_id?: string; graph_topic?: string }) =>\n apiFetch<LTYamlWorkflowRecord>(`/yaml-workflows/${id}`, {\n method: 'PUT',\n body: JSON.stringify(updates),\n }),\n onSuccess: () => {\n queryClient.invalidateQueries({ queryKey: ['yamlWorkflows'], refetchType: 'all' });\n },\n });\n}\n\nexport function useDeleteYamlWorkflow() {\n const queryClient = useQueryClient();\n return useMutation({\n mutationFn: (id: string) =>\n apiFetch<{ deleted: boolean }>(`/yaml-workflows/${id}`, {\n method: 'DELETE',\n }),\n onSuccess: () => {\n queryClient.invalidateQueries({ queryKey: ['yamlWorkflows'], refetchType: 'all' });\n },\n });\n}\n\n// ── Cron scheduling ─────────────────────────────────────────────\n\nexport function useSetYamlCron() {\n const queryClient = useQueryClient();\n return useMutation({\n mutationFn: ({ id, cron_schedule, cron_envelope, execute_as }: {\n id: string;\n cron_schedule: string;\n cron_envelope?: Record<string, unknown> | null;\n execute_as?: string | null;\n }) =>\n apiFetch<LTYamlWorkflowRecord>(`/yaml-workflows/${id}/cron`, {\n method: 'PUT',\n body: JSON.stringify({ cron_schedule, cron_envelope, execute_as }),\n }),\n onSuccess: () => {\n queryClient.invalidateQueries({ queryKey: ['yamlWorkflows'], refetchType: 'all' });\n },\n });\n}\n\nexport function useClearYamlCron() {\n const queryClient = useQueryClient();\n return useMutation({\n mutationFn: (id: string) =>\n apiFetch<LTYamlWorkflowRecord>(`/yaml-workflows/${id}/cron`, {\n method: 'DELETE',\n }),\n onSuccess: () => {\n queryClient.invalidateQueries({ queryKey: ['yamlWorkflows'], refetchType: 'all' });\n },\n });\n}\n\n// ── Version history ─────────────────────────────────────────────\n\nexport function useYamlWorkflowVersions(id: string) {\n return useQuery<{ versions: LTYamlWorkflowVersion[]; total: number }>({\n queryKey: ['yamlWorkflows', id, 'versions'],\n queryFn: () => apiFetch(`/yaml-workflows/${id}/versions`),\n enabled: !!id,\n });\n}\n\nexport function useYamlWorkflowVersion(id: string, version: number | null) {\n return useQuery<LTYamlWorkflowVersion>({\n queryKey: ['yamlWorkflows', id, 'versions', version],\n queryFn: () => apiFetch(`/yaml-workflows/${id}/versions/${version}`),\n enabled: !!id && version !== null,\n });\n}\n"],"names":["useYamlWorkflows","filters","params","useQuery","apiFetch","useYamlWorkflowAppIds","useYamlWorkflow","id","useCreateYamlWorkflow","queryClient","useQueryClient","useMutation","input","useDeployYamlWorkflow","useActivateYamlWorkflow","useInvokeYamlWorkflow","data","sync","execute_as","useRegenerateYamlWorkflow","task_queue","compilation_feedback","useArchiveYamlWorkflow","useRestoreYamlWorkflow","useUpdateYamlWorkflow","updates","useDeleteYamlWorkflow","useYamlWorkflowVersions","useYamlWorkflowVersion","version"],"mappings":"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"}
@@ -8,7 +8,7 @@
8
8
  <link rel="preconnect" href="https://fonts.googleapis.com" />
9
9
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
10
10
  <link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet" />
11
- <script type="module" crossorigin src="./assets/index-ygxyAKcc.js"></script>
11
+ <script type="module" crossorigin src="./assets/index-CU2UW2y6.js"></script>
12
12
  <link rel="modulepreload" crossorigin href="./assets/vendor-query-DvWWuNIB.js">
13
13
  <link rel="modulepreload" crossorigin href="./assets/vendor-react-_9U4mKFr.js">
14
14
  <link rel="modulepreload" crossorigin href="./assets/vendor-icons-en0_uSRD.js">
@@ -1168,6 +1168,21 @@ The request is a pointer. The server re-reads the action from the row's pinned f
1168
1168
  | 422 | `code_mismatch` | The entered code does not name the entry's code |
1169
1169
  | 409 | | The action or the entry is no longer offered |
1170
1170
 
1171
+ ```
1172
+ POST /api/escalations/:id/roster-scan
1173
+ ```
1174
+
1175
+ A scan into a batch [roster](../../hitl/x-lt-scan.md#a-batch-roster-takes-scans) on this row's form. **Body:** `{ field, code }`: the roster field and the code as scanned.
1176
+
1177
+ The field must be a batch roster that declares `x-lt-scan` accepting the code's scheme. The server re-reads the roster, fills the first open entry whose key is the code (a filled entry moves on to the next) with `{ scannedCode }` as its payload, as the person acting, under their RBAC. With `X-LT-Acting-Token`, the person acting is the badged person; one use is spent when the fill runs and returned when it does not land.
1178
+
1179
+ | Status | `outcome` | Meaning |
1180
+ |--------|-----------|---------|
1181
+ | 409 | `not_accepted` | The field takes no scans |
1182
+ | 422 | `not_accepted` | The field does not take this code's scheme |
1183
+ | 422 | `unexpected` | No open entry is this code (the error says when every one is already checked off) |
1184
+ | 401 | `not_primed` | The badge expired or was spent |
1185
+
1171
1186
  Otherwise the answer is the act's own.
1172
1187
 
1173
1188
  ## Accumulate items
@@ -13,6 +13,8 @@ Built-in escalation and human queue management. Exposes the escalation API as MC
13
13
 
14
14
  These tools act with full authority. At `/mcp` they are listed for superadmin accounts only. Other accounts work the queue through the role-checked escalation tools on [long-tail-admin](admin.md).
15
15
 
16
+ `get_escalation_lookups` is listed for every account. It runs as the caller, with the read check `GET /api/escalations/:id/lookups` runs.
17
+
16
18
  ## Compile Hints
17
19
 
18
20
  escalate_and_wait creates a durable pause point. The step AFTER escalate_and_wait is always a signal step (kind: "signal") that receives the human response. Fields from the signal step output (e.g., password) must be wired via data_flow edges to ALL downstream steps that need them.
@@ -55,7 +57,9 @@ Check the status of an escalation. Returns status and resolver payload if resolv
55
57
 
56
58
  ### get_escalation_lookups
57
59
 
58
- Resolve the versioned knowledge lookups pinned on an escalation (`envelope.lookups`). Each ref answers with its immutable edition; a ref whose snapshot does not exist answers with `missing: true`. Like the other tools on this server, the call carries no per-user identity — the MCP surface reads at service scope.
60
+ Resolve the versioned knowledge lookups pinned on an escalation (`envelope.lookups`). Each ref answers with its edition; a ref whose snapshot does not exist answers with `missing: true`. A closed row answers with the editions current when it closed.
61
+
62
+ Any caller who may read the escalation may call it: a superadmin, a member of the row's role with read scope `all`, or a read scope `self` member while the row is assigned to them. Others get `Not authorized to view this escalation` and no edition content. The refs on the row are the grant, so a member reads the editions their row names without a knowledge grant. An internal call reads as lt-system.
59
63
 
60
64
  | | |
61
65
  |---|---|
package/docs/dashboard.md CHANGED
@@ -275,7 +275,7 @@ Accessible at `/admin/roles/:role`. One page per role — a quiet header carryin
275
275
  - **Escalations List Schema** — shows the list-schema version in use and links to the [Escalations List Schema](#escalations-list-schema) editor page.
276
276
  - **Metadata Schema** — JSON Schema that validates each escalation's `metadata` at creation time. Its keys appear in faceted search autocomplete.
277
277
 
278
- **Members** — who holds the role: admins manage it; members work its queue according to their read/write work-surface scope (read = which items appear; write = which they can claim and resolve). A `member` grant carries a scope chosen from five named profiles: full worker (`all`/`all`, default), see-all-act-own (`all`/`self`), own-items-only (`self`/`self`), read-only auditor (`all`/`none`), and read-only own (`self`/`none`). The picker enforces **write ⊆ read**. `admin` and `superadmin` grants always work the whole queue. **Badge at this station** sets how a badge behaves on this role's devices (`properties.badge_grant`): how long a badge scan lasts and how many acts it covers, e.g. ten minutes and unlimited at a high-volume placing bench, one act per scan at a workstation. Empty fields keep the badge scheme's policy. See [Scan codes — identity schemes](scan-codes.md#identity-schemes-and-acting-identity). **After a scan completes** sets where a station locked to this role returns once a scan act completes its row (`properties.scan_done`): the role's home or a route, after a pause on the done copy. See [Scan codes — the return](scan-codes.md#after-a-scan-completes-the-return).
278
+ **Members** — who holds the role: admins manage it; members work its queue according to their read/write work-surface scope (read = which items appear; write = which they can claim and resolve). A `member` grant carries a scope chosen from five named profiles: full worker (`all`/`all`, default), see-all-act-own (`all`/`self`), own-items-only (`self`/`self`), read-only auditor (`all`/`none`), and read-only own (`self`/`none`). The picker enforces **write ⊆ read**. `admin` and `superadmin` grants always work the whole queue. **Badge at this station** sets how a badge behaves on this role's devices (`properties.badge_grant`): how long a badge scan lasts and how many acts it covers, e.g. ten minutes and unlimited at a high-volume placing bench, one act per scan at a workstation. Empty fields keep the badge scheme's policy. See [Scan codes — identity schemes](scan-codes.md#identity-schemes-and-acting-identity). **After a scan completes** sets where a station locked to this role returns once a scan act completes its row (`properties.scan_done`): the role's home or a route, after a pause on the done copy, with an optional button that returns at once. See [Scan codes — the return](scan-codes.md#after-a-scan-completes-the-return).
279
279
 
280
280
  **Pins** — two live-save groups. **Link Variables** leads: the facet names members bind per device (`properties.link_variables` — name, optional label, optional default). A pin below may reference a variable as a facet value, `facets={"facility":"{lt:facility}"}`; each member device opens it scoped to its own binding (set from the user menu → Link variables), falling back to the declared default or, unbound, applying no filter. A caption under each templated pin previews the binding on the viewing device (`facility = 'soleful'` / `facility = <empty>`). **Default Pins** follows — the pinned-view seeds this role hands its members (`default_pins`): label, dashboard-relative URL, optional badge. A badge counts the list its URL opens, with every list filter it carries (role, type, subtype, priority, search, facets, status), so two pins that differ only by subtype count differently. Members see them in their Pinned nav section from first login (marked role-provided) and may promote, hide, or reorder them via preferences; promoting a templated pin copies the template, so it keeps following the device's bindings. See [Faceted Routing — Link variables](faceted-routing.md#link-variables).
281
281
 
@@ -329,7 +329,7 @@ A code encodes **`version:category:target`** (e.g. `10:1:SN-12345`):
329
329
 
330
330
  **Admin config** lives at `/admin/scan-codes` (scheme list) and `/admin/scan-codes/:version` (a scheme with its rules) — role-manager gated (admin, engineer, superadmin). A scheme carries a name, its target facet, encoding, and — for identity schemes — a grant policy.
331
331
 
332
- **Station surface.** `/scan/station` is a full-screen scan surface with an idle prompt, an info/choice screen (the current reality plus labeled choices when a rule presents options), a badge prompt when identity is required, the hold card while an item is held, and a done screen once an act completes its row (a step's or a choice's `done` copy). A role's `scan_done` returns the station to its home or a route after the done screen, read from the chosen station queue or else from the completed row's role; a step's `follow` keeps the station on the item and holds its next row when it appears. In **kiosk mode** — a login holding exactly one role whose `properties.kiosk` is set, or a read-only station whose Station queue is chosen — the chrome falls away and only the role's list, escalation detail, and this scan screen are reachable. The header **scan** panel offers the same manual entry and capture settings on any page.
332
+ **Station surface.** `/scan/station` is a full-screen scan surface with an idle prompt, an info/choice screen (the current reality plus labeled choices when a rule presents options), a badge prompt when identity is required, the hold card while an item is held, and a done screen once an act completes its row (a step's or a choice's `done` copy, counting down to the return). A step's `open` puts an Open button on the hold card, done screen or refusal, leading into the row (claimed as the person acting when the rule asks) or a faceted list. A role's `scan_done` returns the station to its home or a route after the done screen, read from the chosen station queue or else from the completed row's role; a step's `follow` keeps the station on the item and holds its next row when it appears. In **kiosk mode** — a login holding exactly one role whose `properties.kiosk` is set, or a read-only station whose Station queue is chosen — the chrome falls away and only the role's list, escalation detail, and this scan screen are reachable. The header **scan** panel offers the same manual entry and capture settings on any page.
333
333
 
334
334
  **Badge scanning grants acting identity.** A scheme of kind `identity` binds a badge value to a person (via a facet on `lt_users.metadata`). Scanning a badge primes an **ephemeral acting grant** minted through the internal keystore; it rides subsequent scans as the `X-LT-Acting-Token` header, and verbs then run **as the badged person under their own RBAC** — attribution, never privilege escalation. A badge on a work form belongs to the submission: the form names who will submit, holds if the wrong badge scans, and consumes the single-use grant on the write. A dead grant (expired, exhausted, revoked) fails loudly rather than executing silently.
335
335
 
@@ -507,7 +507,7 @@ Accessible at `/knowledge` (builder-only — superadmin or the `engineer` role).
507
507
  - **Version rail** — the entry detail's version dropdown lists every edition, newest first, the current one marked. Selecting a past edition sets `?version=<N>` and shows an immutable, read-only snapshot with a "back to current" affordance.
508
508
  - **Field-level edits** — set or remove a value at a dot-path (`jsonb_set` / path delete) without clobbering sibling keys; each still mints an edition when the data changes.
509
509
 
510
- **How workflows and forms consume it.** A workflow pins knowledge onto an escalation as versioned lookup refs on the reserved `envelope.lookups` key — `{ domain, key, version, as? }`. Because the ref names an exact edition, the resolver always sees the data the workflow was written against, even as the entry later evolves. Resolving those refs (`GET /api/escalations/:id/lookups`, in-process LRU cached) exposes a `lookup.<key>` domain to the form: a field's `x-lt-options` reads `lookup.materials.items`, and cascades interpolate the live form — `lookup.geo.regions.{{resolver.country}}` narrows the second select from the first. The reader needs no separate knowledge grant; the ref on an escalation they can read *is* the grant. Agents reach the same data through the `get_knowledge` MCP tool (with an optional `version`).
510
+ **How workflows and forms consume it.** A workflow pins knowledge onto an escalation as versioned lookup refs on the reserved `envelope.lookups` key — `{ domain, key, version, as? }`. Because the ref names an exact edition, the resolver always sees the data the workflow was written against, even as the entry later evolves. Resolving those refs (`GET /api/escalations/:id/lookups`, in-process LRU cached) exposes a `lookup.<key>` domain to the form: a field's `x-lt-options` reads `lookup.materials.items`, and cascades interpolate the live form — `lookup.geo.regions.{{resolver.country}}` narrows the second select from the first. The reader needs no separate knowledge grant; the ref on an escalation they can read *is* the grant. Agents read a row's editions with the `get_escalation_lookups` MCP tool under the same grant; builders reach any entry through `get_knowledge` (with an optional `version`).
511
511
 
512
512
  **API:** `GET /api/knowledge/domains` and `/entries?domain=` list; `GET /api/knowledge/entry?domain=&key=&version=` fetches the live entry or a pinned edition; `GET /api/knowledge/entry/versions?domain=&key=` returns the lineage. `POST /api/knowledge/entry` creates or merges, `PUT /api/knowledge/field` sets a path, `DELETE /api/knowledge/field` removes one, `DELETE /api/knowledge/entry` deletes (cascading its editions). See [Knowledge HTTP API](api/http/knowledge.md).
513
513
 
@@ -88,6 +88,8 @@ GET /api/escalations/:id/lookups
88
88
 
89
89
  `version` is the edition served; `current: true` marks a ref that follows the newest edition.
90
90
 
91
+ An agent reads the same answer with the `get_escalation_lookups` MCP tool, as the account it signed in with and under the same read check.
92
+
91
93
  The general knowledge API (`/api/knowledge/*`) is a builder surface — superadmin or engineer. A member never queries the knowledge store directly; the escalation-scoped endpoint serves them precisely the editions their work item carries, and nothing else. A ref whose snapshot does not exist answers with `missing: true` for that ref — the batch never fails.
92
94
 
93
95
  ## Form Addressing
@@ -104,6 +104,8 @@ Each `x-lt-actions` entry is one act on the entry:
104
104
 
105
105
  **Roles and badges.** An action with `roles` shows to everyone. A person without one of the roles is asked for a badge, and the action runs as whoever badges in; a badge without the roles is refused with `refuse`, and the badge is not spent. On a read-only station login every action runs through a badge, as before. A role limit is an act control layered on RBAC: the person must also be allowed to write the row.
106
106
 
107
+ **Scans.** A batch roster that declares [`x-lt-scan`](x-lt-scan.md#a-batch-roster-takes-scans) checks its entries off from scans on the open form, as a station fill would, without a button.
108
+
107
109
  On a wait declared with `wakeOnFlag`, a flag ends the wait: the workflow wakes with the items filled so far, `$flags`, and `$trigger: "flag"`, and can route the order (for example, to a manager). Without it, the flag is recorded and the wait continues.
108
110
 
109
111
  Moving work to another person is a resolve with an outcome. "Send to a manager" above resolves the member row with `outcome: "needs-manager"`; the member's workflow opens a row in the manager's role, where only a manager can resolve it, and the manager's confirm adds the bag to the box.
@@ -45,6 +45,18 @@ An array scan field reads as a checklist: its prompt while empty, `n of N` besid
45
45
 
46
46
  The focused field, when it accepts the scan's scheme. Otherwise the first field in `x-lt-order` that accepts it and still has room. Otherwise the first field that accepts it.
47
47
 
48
+ ## A batch roster takes scans
49
+
50
+ A [roster](x-lt-roster.md) whose expected set is the row's batch (`expected: { batch: true }`) may declare `x-lt-scan` with its `schemes`. On the open, claimed form, a scan of one of those schemes checks the entry off on the server, with nothing focused: the row's first open entry whose key is the scanned code is filled as the person acting, with `{ scannedCode }` as the entry's payload (`POST /api/escalations/:id/roster-scan`). An entry the list does not expect, or one already checked off, is refused under the roster and nothing is written. On a read-only station login the first scan asks for a badge, and the badge stays primed for the scans that follow, within its grant's uses and lifetime.
51
+
52
+ ```json
53
+ "items": {
54
+ "type": "string", "readOnly": true, "x-lt-widget": "roster",
55
+ "x-lt-roster": { "expected": { "batch": true, "labels": "envelope.item_labels" } },
56
+ "x-lt-scan": { "schemes": [16] }
57
+ }
58
+ ```
59
+
48
60
  ## Expected values are checked on both sides
49
61
 
50
62
  A scan outside `expect` shows as an error directly under the field that refused it, and leaves the field as it was; an accepted scan shows a confirmation in the same place. Either way the field scrolls into view. The same check runs in the shared validator at submit, so a typed value outside `expect` is a field error in the form and a 422 from the API.
package/docs/mcp.md CHANGED
@@ -371,6 +371,8 @@ The Human Queue is a built-in MCP server that exposes Long Tail's escalation API
371
371
 
372
372
  These tools act with full authority, so at `/mcp` they are listed for superadmin accounts. Workflows and agents inside Long Tail call them directly. An external agent with any other role works the queue through the role-checked escalation tools: `find_escalations`, `claim_escalation`, `admin_resolve_escalation`.
373
373
 
374
+ `get_escalation_lookups` is listed for every account and runs as the caller: anyone who may read the escalation reads the knowledge editions it names, the same grant `GET /api/escalations/:id/lookups` serves.
375
+
374
376
  ### Tools
375
377
 
376
378
  | Tool | Purpose |