@hotmeshio/long-tail 0.9.6 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (255) hide show
  1. package/README.md +22 -93
  2. package/build/api/escalations/helpers.d.ts +27 -0
  3. package/build/api/escalations/helpers.js +34 -0
  4. package/build/api/escalations/list.d.ts +2 -0
  5. package/build/api/escalations/list.js +13 -3
  6. package/build/api/escalations/resolve.js +16 -4
  7. package/build/routes/escalations/list.js +2 -0
  8. package/build/services/escalation/crud.d.ts +1 -1
  9. package/build/services/escalation/crud.js +6 -2
  10. package/build/services/escalation/facet-sql.d.ts +8 -1
  11. package/build/services/escalation/facet-sql.js +29 -1
  12. package/build/types/facets.d.ts +9 -0
  13. package/dashboard/dist/assets/{AdminDashboard-DXFIKXO3.js → AdminDashboard-DQS74RL0.js} +2 -2
  14. package/dashboard/dist/assets/{AdminDashboard-DXFIKXO3.js.map → AdminDashboard-DQS74RL0.js.map} +1 -1
  15. package/dashboard/dist/assets/{AgentConfigPage-CQE1mfjk.js → AgentConfigPage-CSMCrFR4.js} +7 -7
  16. package/dashboard/dist/assets/{AgentConfigPage-CQE1mfjk.js.map → AgentConfigPage-CSMCrFR4.js.map} +1 -1
  17. package/dashboard/dist/assets/{AgentDetailPage-DM59ozP_.js → AgentDetailPage-CDiopQ7D.js} +3 -3
  18. package/dashboard/dist/assets/{AgentDetailPage-DM59ozP_.js.map → AgentDetailPage-CDiopQ7D.js.map} +1 -1
  19. package/dashboard/dist/assets/{AgentsPage-DSK2uXI5.js → AgentsPage-CyUXe9n4.js} +2 -2
  20. package/dashboard/dist/assets/{AgentsPage-DSK2uXI5.js.map → AgentsPage-CyUXe9n4.js.map} +1 -1
  21. package/dashboard/dist/assets/AvailableEscalationsPage-AiuiLcz-.js +2 -0
  22. package/dashboard/dist/assets/AvailableEscalationsPage-AiuiLcz-.js.map +1 -0
  23. package/dashboard/dist/assets/BotPicker-BuCGvEed.js +2 -0
  24. package/dashboard/dist/assets/BotPicker-BuCGvEed.js.map +1 -0
  25. package/dashboard/dist/assets/CapabilitiesPage-nTv4STcG.js +2 -0
  26. package/dashboard/dist/assets/{CapabilitiesPage-DFQryGBj.js.map → CapabilitiesPage-nTv4STcG.js.map} +1 -1
  27. package/dashboard/dist/assets/{CollapsibleSection-B7-d5O_x.js → CollapsibleSection-CkOks9Cf.js} +2 -2
  28. package/dashboard/dist/assets/{CollapsibleSection-B7-d5O_x.js.map → CollapsibleSection-CkOks9Cf.js.map} +1 -1
  29. package/dashboard/dist/assets/{ConfirmDeleteModal-D9_1b4MW.js → ConfirmDeleteModal-C0x0HcHX.js} +2 -2
  30. package/dashboard/dist/assets/{ConfirmDeleteModal-D9_1b4MW.js.map → ConfirmDeleteModal-C0x0HcHX.js.map} +1 -1
  31. package/dashboard/dist/assets/CountdownTimer-DdKL8Ryt.js +2 -0
  32. package/dashboard/dist/assets/CountdownTimer-DdKL8Ryt.js.map +1 -0
  33. package/dashboard/dist/assets/CredentialsPage-BwA7k9Cb.js +2 -0
  34. package/dashboard/dist/assets/{CredentialsPage-BWVgxRpZ.js.map → CredentialsPage-BwA7k9Cb.js.map} +1 -1
  35. package/dashboard/dist/assets/{CronLabel-DNnyNqXH.js → CronLabel-jC_i-Lh9.js} +2 -2
  36. package/dashboard/dist/assets/{CronLabel-DNnyNqXH.js.map → CronLabel-jC_i-Lh9.js.map} +1 -1
  37. package/dashboard/dist/assets/{CustomDurationPicker-SNsQZL_U.js → CustomDurationPicker-F6bR2Q5A.js} +2 -2
  38. package/dashboard/dist/assets/{CustomDurationPicker-SNsQZL_U.js.map → CustomDurationPicker-F6bR2Q5A.js.map} +1 -1
  39. package/dashboard/dist/assets/{DropZone-C1TpkVQG.js → DropZone-zxH-9B8c.js} +2 -2
  40. package/dashboard/dist/assets/{DropZone-C1TpkVQG.js.map → DropZone-zxH-9B8c.js.map} +1 -1
  41. package/dashboard/dist/assets/{ElapsedCell-C4r0IvUt.js → ElapsedCell-DZ3RTZFc.js} +2 -2
  42. package/dashboard/dist/assets/{ElapsedCell-C4r0IvUt.js.map → ElapsedCell-DZ3RTZFc.js.map} +1 -1
  43. package/dashboard/dist/assets/{EscalationListSchemaPage-D8Ec8Zhj.js → EscalationListSchemaPage-CaMydHIO.js} +3 -3
  44. package/dashboard/dist/assets/{EscalationListSchemaPage-D8Ec8Zhj.js.map → EscalationListSchemaPage-CaMydHIO.js.map} +1 -1
  45. package/dashboard/dist/assets/{EscalationSchemaPage-BmEv7qU2.js → EscalationSchemaPage-BlMx2WE6.js} +3 -3
  46. package/dashboard/dist/assets/{EscalationSchemaPage-BmEv7qU2.js.map → EscalationSchemaPage-BlMx2WE6.js.map} +1 -1
  47. package/dashboard/dist/assets/{EscalationsOverview-DkIy025e.js → EscalationsOverview-DcMkbiSo.js} +2 -2
  48. package/dashboard/dist/assets/{EscalationsOverview-DkIy025e.js.map → EscalationsOverview-DcMkbiSo.js.map} +1 -1
  49. package/dashboard/dist/assets/{EventTable-Co4RYeKO.js → EventTable-CuZ2Q_wH.js} +2 -2
  50. package/dashboard/dist/assets/{EventTable-Co4RYeKO.js.map → EventTable-CuZ2Q_wH.js.map} +1 -1
  51. package/dashboard/dist/assets/FilterBar-B5Vk5tBF.js +2 -0
  52. package/dashboard/dist/assets/FilterBar-B5Vk5tBF.js.map +1 -0
  53. package/dashboard/dist/assets/{GraphInvokePage-b7ukMy0z.js → GraphInvokePage-CXElQqcV.js} +2 -2
  54. package/dashboard/dist/assets/{GraphInvokePage-b7ukMy0z.js.map → GraphInvokePage-CXElQqcV.js.map} +1 -1
  55. package/dashboard/dist/assets/HomePage-DcWfjZA-.js +2 -0
  56. package/dashboard/dist/assets/HomePage-DcWfjZA-.js.map +1 -0
  57. package/dashboard/dist/assets/ListToolbar-Dmd1UiM-.js +2 -0
  58. package/dashboard/dist/assets/{ListToolbar-DDG6DXVF.js.map → ListToolbar-Dmd1UiM-.js.map} +1 -1
  59. package/dashboard/dist/assets/{McpOverview-P2o3b3mi.js → McpOverview-Cul2LFEw.js} +2 -2
  60. package/dashboard/dist/assets/{McpOverview-P2o3b3mi.js.map → McpOverview-Cul2LFEw.js.map} +1 -1
  61. package/dashboard/dist/assets/{McpQueryDetailPage-C2CeBLb0.js → McpQueryDetailPage-BVlQ0WBa.js} +2 -2
  62. package/dashboard/dist/assets/{McpQueryDetailPage-C2CeBLb0.js.map → McpQueryDetailPage-BVlQ0WBa.js.map} +1 -1
  63. package/dashboard/dist/assets/{McpQueryPage-D_N-3lp0.js → McpQueryPage-B7PmO_V-.js} +2 -2
  64. package/dashboard/dist/assets/{McpQueryPage-D_N-3lp0.js.map → McpQueryPage-B7PmO_V-.js.map} +1 -1
  65. package/dashboard/dist/assets/{McpRunDetailPage-uDw3MWR_.js → McpRunDetailPage-B1RhxxlU.js} +2 -2
  66. package/dashboard/dist/assets/{McpRunDetailPage-uDw3MWR_.js.map → McpRunDetailPage-B1RhxxlU.js.map} +1 -1
  67. package/dashboard/dist/assets/{McpRunsPage-CDTUxghb.js → McpRunsPage-DlSrpxiv.js} +2 -2
  68. package/dashboard/dist/assets/{McpRunsPage-CDTUxghb.js.map → McpRunsPage-DlSrpxiv.js.map} +1 -1
  69. package/dashboard/dist/assets/Modal-D4afnAjc.js +2 -0
  70. package/dashboard/dist/assets/Modal-D4afnAjc.js.map +1 -0
  71. package/dashboard/dist/assets/{NamespacePill--ikKtMvx.js → NamespacePill-BQNMfY0m.js} +2 -2
  72. package/dashboard/dist/assets/{NamespacePill--ikKtMvx.js.map → NamespacePill-BQNMfY0m.js.map} +1 -1
  73. package/dashboard/dist/assets/OperationsPage-vm_ByX41.js +2 -0
  74. package/dashboard/dist/assets/OperationsPage-vm_ByX41.js.map +1 -0
  75. package/dashboard/dist/assets/OperatorDashboard-zviIxH-0.js +2 -0
  76. package/dashboard/dist/assets/{OperatorDashboard-CVRD8nQH.js.map → OperatorDashboard-zviIxH-0.js.map} +1 -1
  77. package/dashboard/dist/assets/{PageHeader-CiFhzGc0.js → PageHeader-BIY1YvKR.js} +2 -2
  78. package/dashboard/dist/assets/{PageHeader-CiFhzGc0.js.map → PageHeader-BIY1YvKR.js.map} +1 -1
  79. package/dashboard/dist/assets/{PageHeaderWithStats-_zOnWAhq.js → PageHeaderWithStats-TUyglVW0.js} +2 -2
  80. package/dashboard/dist/assets/{PageHeaderWithStats-_zOnWAhq.js.map → PageHeaderWithStats-TUyglVW0.js.map} +1 -1
  81. package/dashboard/dist/assets/{ProcessDetailPage-Dt6PAK04.js → ProcessDetailPage-txiMHbMI.js} +2 -2
  82. package/dashboard/dist/assets/{ProcessDetailPage-Dt6PAK04.js.map → ProcessDetailPage-txiMHbMI.js.map} +1 -1
  83. package/dashboard/dist/assets/{ProcessesListPage-CMkjASgs.js → ProcessesListPage-CRttlU3L.js} +2 -2
  84. package/dashboard/dist/assets/{ProcessesListPage-CMkjASgs.js.map → ProcessesListPage-CRttlU3L.js.map} +1 -1
  85. package/dashboard/dist/assets/RoleDetailPage-DBIl_mhK.js +8 -0
  86. package/dashboard/dist/assets/RoleDetailPage-DBIl_mhK.js.map +1 -0
  87. package/dashboard/dist/assets/RolePill-4LO_CuO3.js +2 -0
  88. package/dashboard/dist/assets/RolePill-4LO_CuO3.js.map +1 -0
  89. package/dashboard/dist/assets/RolesPage-DzrSv6m-.js +2 -0
  90. package/dashboard/dist/assets/{RolesPage-DtHIjDss.js.map → RolesPage-DzrSv6m-.js.map} +1 -1
  91. package/dashboard/dist/assets/RunAsSelector-CiLjlmIE.js +2 -0
  92. package/dashboard/dist/assets/{RunAsSelector-C198CpFs.js.map → RunAsSelector-CiLjlmIE.js.map} +1 -1
  93. package/dashboard/dist/assets/{SlidePanel-BcWKelpn.js → SlidePanel-DK4sWXmp.js} +2 -2
  94. package/dashboard/dist/assets/{SlidePanel-BcWKelpn.js.map → SlidePanel-DK4sWXmp.js.map} +1 -1
  95. package/dashboard/dist/assets/StickyPagination-Di6b2GDE.js +2 -0
  96. package/dashboard/dist/assets/{StickyPagination-eZ6WhMrL.js.map → StickyPagination-Di6b2GDE.js.map} +1 -1
  97. package/dashboard/dist/assets/{StreamMessageDetail-7xq_dddO.js → StreamMessageDetail-BbkU45X_.js} +2 -2
  98. package/dashboard/dist/assets/{StreamMessageDetail-7xq_dddO.js.map → StreamMessageDetail-BbkU45X_.js.map} +1 -1
  99. package/dashboard/dist/assets/{SwimlaneTimeline-Yg5QT0nI.js → SwimlaneTimeline-BzsvdstG.js} +2 -2
  100. package/dashboard/dist/assets/{SwimlaneTimeline-Yg5QT0nI.js.map → SwimlaneTimeline-BzsvdstG.js.map} +1 -1
  101. package/dashboard/dist/assets/{TagInput-BaFJCq9A.js → TagInput-CK-JhBG2.js} +2 -2
  102. package/dashboard/dist/assets/{TagInput-BaFJCq9A.js.map → TagInput-CK-JhBG2.js.map} +1 -1
  103. package/dashboard/dist/assets/{TaskDetailPage-Wc3AFnqs.js → TaskDetailPage-Biikk9o_.js} +2 -2
  104. package/dashboard/dist/assets/{TaskDetailPage-Wc3AFnqs.js.map → TaskDetailPage-Biikk9o_.js.map} +1 -1
  105. package/dashboard/dist/assets/{TaskQueuePill-D9e16qkU.js → TaskQueuePill-C_Vo2Clc.js} +2 -2
  106. package/dashboard/dist/assets/{TaskQueuePill-D9e16qkU.js.map → TaskQueuePill-C_Vo2Clc.js.map} +1 -1
  107. package/dashboard/dist/assets/{TasksListPage-V-1J_B_L.js → TasksListPage-t9WAft9-.js} +2 -2
  108. package/dashboard/dist/assets/{TasksListPage-V-1J_B_L.js.map → TasksListPage-t9WAft9-.js.map} +1 -1
  109. package/dashboard/dist/assets/{TimeAgo-XdHghs5F.js → TimeAgo-I7X6jdne.js} +2 -2
  110. package/dashboard/dist/assets/{TimeAgo-XdHghs5F.js.map → TimeAgo-I7X6jdne.js.map} +1 -1
  111. package/dashboard/dist/assets/{TimestampCell-BrzctPWT.js → TimestampCell-Cex9nr0t.js} +2 -2
  112. package/dashboard/dist/assets/{TimestampCell-BrzctPWT.js.map → TimestampCell-Cex9nr0t.js.map} +1 -1
  113. package/dashboard/dist/assets/ToolPill-BCi101Jh.js +2 -0
  114. package/dashboard/dist/assets/{ToolPill-Enc3nyic.js.map → ToolPill-BCi101Jh.js.map} +1 -1
  115. package/dashboard/dist/assets/{ToolTestPanel-CGfGUsaq.js → ToolTestPanel--MGaDY-1.js} +2 -2
  116. package/dashboard/dist/assets/{ToolTestPanel-CGfGUsaq.js.map → ToolTestPanel--MGaDY-1.js.map} +1 -1
  117. package/dashboard/dist/assets/{TopicDetailPage-CcAgD2k5.js → TopicDetailPage-B8pHHweA.js} +3 -3
  118. package/dashboard/dist/assets/{TopicDetailPage-CcAgD2k5.js.map → TopicDetailPage-B8pHHweA.js.map} +1 -1
  119. package/dashboard/dist/assets/{TopicsPage-BOQG7s4P.js → TopicsPage-tiOEQz2m.js} +2 -2
  120. package/dashboard/dist/assets/{TopicsPage-BOQG7s4P.js.map → TopicsPage-tiOEQz2m.js.map} +1 -1
  121. package/dashboard/dist/assets/{UserName-BzxR0Ihp.js → UserName-Cdhyuv5K.js} +2 -2
  122. package/dashboard/dist/assets/{UserName-BzxR0Ihp.js.map → UserName-Cdhyuv5K.js.map} +1 -1
  123. package/dashboard/dist/assets/{WorkflowExecutionPage-hRsDMByj.js → WorkflowExecutionPage-DD1BKj5k.js} +2 -2
  124. package/dashboard/dist/assets/{WorkflowExecutionPage-hRsDMByj.js.map → WorkflowExecutionPage-DD1BKj5k.js.map} +1 -1
  125. package/dashboard/dist/assets/WorkflowPill-DHxrAkp3.js +2 -0
  126. package/dashboard/dist/assets/{WorkflowPill-S6VV2Eax.js.map → WorkflowPill-DHxrAkp3.js.map} +1 -1
  127. package/dashboard/dist/assets/{WorkflowsDashboard-ky0ulsUe.js → WorkflowsDashboard-BeWjDHYW.js} +2 -2
  128. package/dashboard/dist/assets/{WorkflowsDashboard-ky0ulsUe.js.map → WorkflowsDashboard-BeWjDHYW.js.map} +1 -1
  129. package/dashboard/dist/assets/{WorkflowsOverview-CzRJUV58.js → WorkflowsOverview-CkI-Pmj7.js} +2 -2
  130. package/dashboard/dist/assets/{WorkflowsOverview-CzRJUV58.js.map → WorkflowsOverview-CkI-Pmj7.js.map} +1 -1
  131. package/dashboard/dist/assets/{YamlWorkflowDetailPage-Beqzy9Ph.js → YamlWorkflowDetailPage-BQnO8-PQ.js} +2 -2
  132. package/dashboard/dist/assets/{YamlWorkflowDetailPage-Beqzy9Ph.js.map → YamlWorkflowDetailPage-BQnO8-PQ.js.map} +1 -1
  133. package/dashboard/dist/assets/{YamlWorkflowsPage-BzBUU2ug.js → YamlWorkflowsPage-DjmNTQ5r.js} +2 -2
  134. package/dashboard/dist/assets/{YamlWorkflowsPage-BzBUU2ug.js.map → YamlWorkflowsPage-DjmNTQ5r.js.map} +1 -1
  135. package/dashboard/dist/assets/{agents-Dlu4m6jy.js → agents-2DaoprdB.js} +2 -2
  136. package/dashboard/dist/assets/{agents-Dlu4m6jy.js.map → agents-2DaoprdB.js.map} +1 -1
  137. package/dashboard/dist/assets/{bots-C4G5U-6Q.js → bots-BdwLwMUg.js} +2 -2
  138. package/dashboard/dist/assets/{bots-C4G5U-6Q.js.map → bots-BdwLwMUg.js.map} +1 -1
  139. package/dashboard/dist/assets/{capabilities-BSFP_boq.js → capabilities-BsuFeK9J.js} +2 -2
  140. package/dashboard/dist/assets/{capabilities-BSFP_boq.js.map → capabilities-BsuFeK9J.js.map} +1 -1
  141. package/dashboard/dist/assets/{controlplane-qCqoP2ZV.js → controlplane-CxKOrcFO.js} +2 -2
  142. package/dashboard/dist/assets/{controlplane-qCqoP2ZV.js.map → controlplane-CxKOrcFO.js.map} +1 -1
  143. package/dashboard/dist/assets/escalation-columns-y5SQ2Ulh.js +2 -0
  144. package/dashboard/dist/assets/escalation-columns-y5SQ2Ulh.js.map +1 -0
  145. package/dashboard/dist/assets/index-BC1NxetR.js +2 -0
  146. package/dashboard/dist/assets/{index-C0KklOM4.js.map → index-BC1NxetR.js.map} +1 -1
  147. package/dashboard/dist/assets/index-BVZINC_p.js +2 -0
  148. package/dashboard/dist/assets/{index-QL26JxBF.js.map → index-BVZINC_p.js.map} +1 -1
  149. package/dashboard/dist/assets/{index-CGVqW6MI.js → index-BXbt5NHG.js} +2 -2
  150. package/dashboard/dist/assets/{index-CGVqW6MI.js.map → index-BXbt5NHG.js.map} +1 -1
  151. package/dashboard/dist/assets/{index-CuC_mNNA.js → index-BYEkfaUq.js} +2 -2
  152. package/dashboard/dist/assets/{index-CuC_mNNA.js.map → index-BYEkfaUq.js.map} +1 -1
  153. package/dashboard/dist/assets/index-B_68lp-B.js +5 -0
  154. package/dashboard/dist/assets/index-B_68lp-B.js.map +1 -0
  155. package/dashboard/dist/assets/index-BeivyVtT.css +1 -0
  156. package/dashboard/dist/assets/{index-DUhettdV.js → index-CRBXgMsh.js} +2 -2
  157. package/dashboard/dist/assets/{index-DUhettdV.js.map → index-CRBXgMsh.js.map} +1 -1
  158. package/dashboard/dist/assets/{index-BObWYPxd.js → index-Cfq3UKd8.js} +2 -2
  159. package/dashboard/dist/assets/{index-BObWYPxd.js.map → index-Cfq3UKd8.js.map} +1 -1
  160. package/dashboard/dist/assets/{index-D_FNaSoe.js → index-DJ5nhcGB.js} +2 -2
  161. package/dashboard/dist/assets/{index-D_FNaSoe.js.map → index-DJ5nhcGB.js.map} +1 -1
  162. package/dashboard/dist/assets/{index-DCTWUswT.js → index-DVLBkcoD.js} +4 -4
  163. package/dashboard/dist/assets/index-DVLBkcoD.js.map +1 -0
  164. package/dashboard/dist/assets/index-DjI2AUbn.js +5 -0
  165. package/dashboard/dist/assets/{index-DWSNtg28.js.map → index-DjI2AUbn.js.map} +1 -1
  166. package/dashboard/dist/assets/{index-Dzhn5MDY.js → index-DyS2DHqy.js} +2 -2
  167. package/dashboard/dist/assets/{index-Dzhn5MDY.js.map → index-DyS2DHqy.js.map} +1 -1
  168. package/dashboard/dist/assets/index-H6Vp-gPG.js +2 -0
  169. package/dashboard/dist/assets/{index-BtEWQNcP.js.map → index-H6Vp-gPG.js.map} +1 -1
  170. package/dashboard/dist/assets/index-YPLTjeli.js +63 -0
  171. package/dashboard/dist/assets/index-YPLTjeli.js.map +1 -0
  172. package/dashboard/dist/assets/{knowledge-CTdgLX5j.js → knowledge-CLnYcq0Y.js} +2 -2
  173. package/dashboard/dist/assets/{knowledge-CTdgLX5j.js.map → knowledge-CLnYcq0Y.js.map} +1 -1
  174. package/dashboard/dist/assets/{mcp-BEsMr678.js → mcp-C1-iXkg7.js} +2 -2
  175. package/dashboard/dist/assets/{mcp-BEsMr678.js.map → mcp-C1-iXkg7.js.map} +1 -1
  176. package/dashboard/dist/assets/{mcp-query-CmamHKIR.js → mcp-query-DcyDxuGY.js} +2 -2
  177. package/dashboard/dist/assets/{mcp-query-CmamHKIR.js.map → mcp-query-DcyDxuGY.js.map} +1 -1
  178. package/dashboard/dist/assets/{pipelines-taOXA2-y.js → pipelines-BvG5D6bV.js} +2 -2
  179. package/dashboard/dist/assets/{pipelines-taOXA2-y.js.map → pipelines-BvG5D6bV.js.map} +1 -1
  180. package/dashboard/dist/assets/{tasks-DEaDdyBh.js → tasks-qwh8q5q7.js} +2 -2
  181. package/dashboard/dist/assets/{tasks-DEaDdyBh.js.map → tasks-qwh8q5q7.js.map} +1 -1
  182. package/dashboard/dist/assets/{topics-2qRx68Ft.js → topics-MLKKA-Ql.js} +2 -2
  183. package/dashboard/dist/assets/{topics-2qRx68Ft.js.map → topics-MLKKA-Ql.js.map} +1 -1
  184. package/dashboard/dist/assets/{useEventHooks-oXjfKrbI.js → useEventHooks-B8uNFB-l.js} +2 -2
  185. package/dashboard/dist/assets/{useEventHooks-oXjfKrbI.js.map → useEventHooks-B8uNFB-l.js.map} +1 -1
  186. package/dashboard/dist/assets/{useNamespace-YdT1Zouk.js → useNamespace-DjZmztJM.js} +2 -2
  187. package/dashboard/dist/assets/{useNamespace-YdT1Zouk.js.map → useNamespace-DjZmztJM.js.map} +1 -1
  188. package/dashboard/dist/assets/{useYamlActivityEvents-Bt-RZ7Zl.js → useYamlActivityEvents-DIvK20uI.js} +2 -2
  189. package/dashboard/dist/assets/{useYamlActivityEvents-Bt-RZ7Zl.js.map → useYamlActivityEvents-DIvK20uI.js.map} +1 -1
  190. package/dashboard/dist/assets/{users-DbX5JZ_w.js → users-CVjajeDg.js} +2 -2
  191. package/dashboard/dist/assets/{users-DbX5JZ_w.js.map → users-CVjajeDg.js.map} +1 -1
  192. package/dashboard/dist/assets/{vendor-icons-DPmIjhZB.js → vendor-icons-Qk9qLjF_.js} +131 -126
  193. package/dashboard/dist/assets/vendor-icons-Qk9qLjF_.js.map +1 -0
  194. package/dashboard/dist/assets/{workflows-acMcPTC5.js → workflows-BziTcJNq.js} +2 -2
  195. package/dashboard/dist/assets/{workflows-acMcPTC5.js.map → workflows-BziTcJNq.js.map} +1 -1
  196. package/dashboard/dist/assets/{yaml-workflows-D6kzrMYx.js → yaml-workflows-Dr-ZcE_0.js} +2 -2
  197. package/dashboard/dist/assets/{yaml-workflows-D6kzrMYx.js.map → yaml-workflows-Dr-ZcE_0.js.map} +1 -1
  198. package/dashboard/dist/index.html +3 -3
  199. package/docs/dashboard.md +2 -2
  200. package/docs/faceted-routing.md +9 -0
  201. package/docs/hitl/escalation.md +206 -0
  202. package/docs/hitl/form.md +162 -0
  203. package/docs/hitl/iframe.md +170 -0
  204. package/docs/hitl/resolution.md +166 -0
  205. package/docs/hitl/roles.md +72 -0
  206. package/docs/hitl/x-lt-layout.md +167 -0
  207. package/docs/hitl/x-lt-list-schema.md +75 -0
  208. package/docs/hitl/x-lt-show-if.md +128 -0
  209. package/docs/hitl/x-lt-validation.md +168 -0
  210. package/docs/hitl/x-lt-widget.md +152 -0
  211. package/docs/hitl-guide.md +83 -1315
  212. package/docs/iam.md +1 -1
  213. package/package.json +2 -2
  214. package/dashboard/dist/assets/AvailableEscalationsPage-BVgMtftp.js +0 -2
  215. package/dashboard/dist/assets/AvailableEscalationsPage-BVgMtftp.js.map +0 -1
  216. package/dashboard/dist/assets/BotPicker-DJ0YYKZA.js +0 -2
  217. package/dashboard/dist/assets/BotPicker-DJ0YYKZA.js.map +0 -1
  218. package/dashboard/dist/assets/CapabilitiesPage-DFQryGBj.js +0 -2
  219. package/dashboard/dist/assets/CountdownTimer-BTJmh4Xw.js +0 -2
  220. package/dashboard/dist/assets/CountdownTimer-BTJmh4Xw.js.map +0 -1
  221. package/dashboard/dist/assets/CredentialsPage-BWVgxRpZ.js +0 -2
  222. package/dashboard/dist/assets/FilterBar-DDIn8TOs.js +0 -2
  223. package/dashboard/dist/assets/FilterBar-DDIn8TOs.js.map +0 -1
  224. package/dashboard/dist/assets/HomePage-K0U3yMdU.js +0 -2
  225. package/dashboard/dist/assets/HomePage-K0U3yMdU.js.map +0 -1
  226. package/dashboard/dist/assets/ListToolbar-DDG6DXVF.js +0 -2
  227. package/dashboard/dist/assets/Modal-CSrxpXeM.js +0 -2
  228. package/dashboard/dist/assets/Modal-CSrxpXeM.js.map +0 -1
  229. package/dashboard/dist/assets/OperationsPage-Daah4pNk.js +0 -2
  230. package/dashboard/dist/assets/OperationsPage-Daah4pNk.js.map +0 -1
  231. package/dashboard/dist/assets/OperatorDashboard-CVRD8nQH.js +0 -2
  232. package/dashboard/dist/assets/RoleDetailPage-6H1AY6sK.js +0 -8
  233. package/dashboard/dist/assets/RoleDetailPage-6H1AY6sK.js.map +0 -1
  234. package/dashboard/dist/assets/RolePill-CUIJSgRq.js +0 -2
  235. package/dashboard/dist/assets/RolePill-CUIJSgRq.js.map +0 -1
  236. package/dashboard/dist/assets/RolesPage-DtHIjDss.js +0 -2
  237. package/dashboard/dist/assets/RunAsSelector-C198CpFs.js +0 -2
  238. package/dashboard/dist/assets/StickyPagination-eZ6WhMrL.js +0 -2
  239. package/dashboard/dist/assets/ToolPill-Enc3nyic.js +0 -2
  240. package/dashboard/dist/assets/WorkflowPill-S6VV2Eax.js +0 -2
  241. package/dashboard/dist/assets/escalation-columns-Ck7ZOd7Z.js +0 -2
  242. package/dashboard/dist/assets/escalation-columns-Ck7ZOd7Z.js.map +0 -1
  243. package/dashboard/dist/assets/index-BtEWQNcP.js +0 -2
  244. package/dashboard/dist/assets/index-C0KklOM4.js +0 -2
  245. package/dashboard/dist/assets/index-CY3hpJR-.js +0 -5
  246. package/dashboard/dist/assets/index-CY3hpJR-.js.map +0 -1
  247. package/dashboard/dist/assets/index-DCTWUswT.js.map +0 -1
  248. package/dashboard/dist/assets/index-DWSNtg28.js +0 -5
  249. package/dashboard/dist/assets/index-DcQiD6OF.js +0 -63
  250. package/dashboard/dist/assets/index-DcQiD6OF.js.map +0 -1
  251. package/dashboard/dist/assets/index-QL26JxBF.js +0 -2
  252. package/dashboard/dist/assets/index-waKF7bKZ.css +0 -1
  253. package/dashboard/dist/assets/roles-BiUJAsVM.js +0 -2
  254. package/dashboard/dist/assets/roles-BiUJAsVM.js.map +0 -1
  255. package/dashboard/dist/assets/vendor-icons-DPmIjhZB.js.map +0 -1
@@ -0,0 +1,166 @@
1
+ # Resolution
2
+
3
+ ## Claim Lifecycle
4
+
5
+ A claim is a **work lock**: `assigned_to` names the holder and `assigned_until` bounds the window. The lock exists only while the window is active — this is what `isEffectivelyClaimed` means throughout the platform. Two related shapes use the same columns without being locks:
6
+
7
+ - **Unclaimed** (`assigned_to` null) — available to the whole role queue, and resolvable by system code.
8
+ - **Durable pre-assignment** (`assigned_to` set, no window) — a workflow targeting a named user (the one-time-user JIT-form shape, see [roles.md](roles.md#one-time-and-pre-assigned-escalations)). Routing, not a lock.
9
+
10
+ ### Claiming and extending
11
+
12
+ Claiming takes a duration (configurable options via `LT_CLAIM_DURATION_OPTIONS`; a custom duration is always available). Re-claiming a row you already hold extends the window — the claim endpoint is idempotent per assignee and reports `isExtension: true`.
13
+
14
+ In the dashboard, 90 seconds before the window lapses a **Claim Expiring** dialog offers the same duration options to extend. Dismissing it lets the window run out: at expiry the form locks, the action bar returns to its claim state, and the item is available to the queue again. Typed input survives — see draft persistence in [form.md](form.md#draft-persistence) — so re-claiming picks up exactly where the resolver left off.
15
+
16
+ ### The claim-liveness rule
17
+
18
+ A resolve presented **by escalation id** must not act against a claim lock. The rule blocks exactly two states:
19
+
20
+ | Row state | Resolve by id |
21
+ |-----------|---------------|
22
+ | Unclaimed | Allowed (system resolvers act on unclaimed rows by design) |
23
+ | Durable pre-assignment (no window) | Allowed |
24
+ | Live window, held by the caller | Allowed |
25
+ | Live window, held by someone else | **409** — the lock is theirs |
26
+ | Caller's own window, lapsed | **409** — stale work; re-claim to resolve |
27
+ | Someone else's window, lapsed | Allowed — the lock is gone, the row is back in the pool |
28
+
29
+ The rule applies to every principal, including superadmins: the claim is a work lock, not an authorization scope. It is enforced twice — an advisory check before any resolution side effects fire, and atomically inside the SDK's guarded resolve UPDATE (`assertClaim`), so a claim that lapses mid-request still cannot land a stale resolution.
30
+
31
+ `resolveBySignalKey` and `resolveByMetadata` are claim-agnostic: they are system ingress surfaces (webhooks, domain events) that resolve on behalf of the process, not a claimant.
32
+
33
+ ---
34
+
35
+ ## Resolving from System Code
36
+
37
+ When a backend service (not the dashboard UI) needs to resolve an escalation — for example, an ingress handler that receives a webhook or processes a domain event — use the escalation SDK methods directly.
38
+
39
+ ### By escalation ID
40
+
41
+ Use when you already have the escalation UUID (e.g. stored in your own DB alongside the order):
42
+
43
+ ```typescript
44
+ const result = await lt.escalations.resolve({
45
+ id: escalationId,
46
+ resolverPayload: { approved: true, targetStatus: 'ready' },
47
+ });
48
+ ```
49
+
50
+ This routes through the full resolution path for all escalation types.
51
+
52
+ ### By metadata key-value pair
53
+
54
+ Use when you know a domain identifier (e.g. `orderId`) but not the escalation UUID. `resolveByMetadata` finds the highest-priority pending escalation matching the key-value pair and resolves it atomically — no pre-flight lookup, no TOCTOU:
55
+
56
+ ```typescript
57
+ const result = await lt.escalations.resolveByMetadata({
58
+ key: 'orderId',
59
+ value: orderId,
60
+ resolverPayload: { approved: true, targetStatus: 'ready' },
61
+ });
62
+
63
+ if (result.status === 404) {
64
+ // No pending escalation for this orderId
65
+ }
66
+ ```
67
+
68
+ ### By signal key
69
+
70
+ When the signal key is deterministic and known to the caller (e.g. `station-done-${workflowId}`), use the signal-key path to skip the metadata lookup:
71
+
72
+ ```typescript
73
+ await lt.escalations.resolveBySignalKey({
74
+ signalKey: `station-done-${workflowId}`,
75
+ resolverPayload: { approved: true },
76
+ });
77
+ ```
78
+
79
+ ### Resolving a set atomically
80
+
81
+ When one decision settles a set of waits — each with its own payload — use `resolveAllOrNone`:
82
+
83
+ ```typescript
84
+ await lt.escalations.resolveAllOrNone({ items: [
85
+ { id: 'esc-aaa', resolverPayload: { decision: 'approve' } },
86
+ { id: 'esc-bbb', resolverPayload: { decision: 'reject' } },
87
+ ] });
88
+ ```
89
+
90
+ Every listed row resolves with its own payload in one SQL statement, waking each parked workflow with its own value — or nothing resolves. A 409 names exactly the rows that blocked (`failedIds` + reasons). Pass `requireClaimed: true` to assert, inside the same statement, that every row is still assigned to the caller. See the [SDK reference](../api/sdk/escalations.md#resolveallornone) for the full contract.
91
+
92
+ ---
93
+
94
+ ## Recording the Outcome
95
+
96
+ An escalation row carries **intent** — what was asked, who it routed to. Resolving it stamps the **outcome** onto the same row: every resolve surface takes an optional `metadata` patch merged into the row's GIN-indexed metadata.
97
+
98
+ | Surface | How to pass it |
99
+ |---------|----------------|
100
+ | HTTP | `metadata` in the resolve body (`POST /api/escalations/:id/resolve`) |
101
+ | SDK facade | `lt.escalations.resolve({ id, resolverPayload, metadata })` |
102
+ | MCP | `metadata` arg on `claim_and_resolve` / `resolve_escalation` |
103
+ | In-process library | `resolveEscalation(id, payload, metadata)` |
104
+
105
+ ```typescript
106
+ await lt.escalations.resolve({
107
+ id,
108
+ resolverPayload: { approved: true }, // resumes the workflow; not indexed
109
+ metadata: { outcome: 'approved', reviewedBy: 'alice', durationMs: elapsed },
110
+ });
111
+ ```
112
+
113
+ The metadata patch is the durable, queryable record on the row. The resolver payload resumes the paused workflow and is not indexed separately. Use the metadata patch for audit trail and analytics — disposition, reviewer, time-to-resolve — so the escalation table answers *what was asked, what was decided, and how long it took* without a parallel log.
114
+
115
+ ---
116
+
117
+ ## Cancelling Escalations
118
+
119
+ Cancellation is terminal — a cancelled escalation cannot be re-opened.
120
+
121
+ ### When cancellation happens
122
+
123
+ - **Workflow termination** — `POST /api/workflows/:workflowId/terminate` automatically cancels any pending escalations tied to it. The waiting `conditionLT` call returns `null`.
124
+ - **Explicit cancel** — cancel a single escalation via the API or from the dashboard. Any workflow waiting on that escalation via `conditionLT` receives `null`.
125
+
126
+ ### API
127
+
128
+ ```
129
+ POST /api/escalations/:id/cancel # single
130
+ POST /api/escalations/bulk-cancel # { "ids": [...] }
131
+ ```
132
+
133
+ Returns 409 if the escalation is already resolved or cancelled.
134
+
135
+ ### Dashboard
136
+
137
+ - **Escalations list** — select one or more rows and click **Cancel** in the bulk action bar.
138
+ - **Escalation detail page** — a Cancel link appears in the action bar when the escalation is in `available` or `claimed_by_me` state.
139
+
140
+ ### Handling cancellation in workflows
141
+
142
+ `conditionLT` returns `T | false | null`. Always guard before accessing the payload:
143
+
144
+ ```typescript
145
+ const decision = await conditionLT<{ approved: boolean }>(signalId, escalationConfig);
146
+
147
+ if (decision === null) {
148
+ // Escalation was cancelled
149
+ return { type: 'return' as const, data: { cancelled: true } };
150
+ }
151
+ if (decision === false) {
152
+ // Escalation timed out (SLA)
153
+ return { type: 'return' as const, data: { timedOut: true } };
154
+ }
155
+
156
+ // Normal path — decision is the resolver's payload
157
+ if (decision.approved) { ... }
158
+ ```
159
+
160
+ The `!decision` shorthand handles both cases when you don't need to distinguish between them:
161
+
162
+ ```typescript
163
+ if (!decision) {
164
+ return { type: 'return' as const, data: { cancelled: true } };
165
+ }
166
+ ```
@@ -0,0 +1,72 @@
1
+ # Role Routing and RBAC
2
+
3
+ ## Role-Based Routing
4
+
5
+ Escalations are routed by role, and any member of the role can handle them — a person working the queue in the dashboard, or a service account claiming and resolving through the API or MCP tools. Members only see escalations for roles they hold. Set `role` on every `conditionLT` config to name the target queue:
6
+
7
+ ```typescript
8
+ const decision = await conditionLT<{ approved: boolean }>(signalId, {
9
+ role: 'finance-reviewer', // only users with this role see it
10
+ description: `Approve spend of $${amount}`,
11
+ // ...
12
+ });
13
+ ```
14
+
15
+ ---
16
+
17
+ ## Work-Surface Scope
18
+
19
+ A `member` of a role carries a work-surface scope that narrows what they see and act on within that queue:
20
+
21
+ | Scope field | Values | Meaning |
22
+ |-------------|--------|---------|
23
+ | `read_scope` | `self` / `all` | Which escalations the member sees |
24
+ | `write_scope` | `none` / `self` / `all` | Which escalations the member can claim, resolve, or cancel |
25
+
26
+ `self` means escalations assigned to that member (`assigned_to = user`); `all` means the whole role queue. `admin` and `superadmin` always work the whole queue.
27
+
28
+ Scope is a property of the **membership** (`lt_user_roles`), not of the escalation row. The escalation engine is unchanged — `conditionLT` and `ltCreateEscalation` write rows exactly as before. Scope is resolved at read time when a user lists or acts on the queue.
29
+
30
+ See the [Roles API](../api/http/roles.md#work-surface-scope) for the five member profiles and the assignment contract.
31
+
32
+ ---
33
+
34
+ ## One-Time and Pre-Assigned Escalations
35
+
36
+ To route a single item to a named person, assign the escalation to them and provision them with `read_scope=self` + `write_scope=self`. The workflow sets `assigned_to` to the person's user ID when it creates the escalation, then provisions or updates that user as a `member` with self/self scope on the target role. They land directly on that one item — a just-in-time form scoped by RBAC — with no access to the rest of the queue.
37
+
38
+ ```typescript
39
+ const decision = await conditionLT<{ confirmed: boolean; address: string }>(signalId, {
40
+ role: 'customer-triage',
41
+ assigned_to: userId, // pre-claim
42
+ description: 'Confirm your shipping address',
43
+ metadata: {
44
+ form_schema: {
45
+ title: 'Confirm Address',
46
+ properties: {
47
+ address: { type: 'string' },
48
+ confirmed: { type: 'boolean' },
49
+ },
50
+ required: ['confirmed'],
51
+ },
52
+ },
53
+ });
54
+ ```
55
+
56
+ ---
57
+
58
+ ## Escalation Chains
59
+
60
+ Users can escalate to other roles via the "Escalate" tab on the detail page. Configure chain targets in Admin > Roles. Each role lists which other roles it can escalate to. Common shape:
61
+
62
+ ```
63
+ Analyst → Senior Analyst → Manager → VP
64
+ ```
65
+
66
+ Chains are runtime RBAC — they control who can receive an escalation handed off from this role. `parent_role` and `ops_visible` are the process graph Operations renders.
67
+
68
+ ---
69
+
70
+ ## Schema Versioning
71
+
72
+ See [escalation.md](escalation.md#versioned-role-schemas) for the full versioning contract — how to pin a schema version on an escalation row, how to inspect versions, and how to save a new version.
@@ -0,0 +1,167 @@
1
+ # Layout, Ordering, Sections, Binding, and Help
2
+
3
+ ---
4
+
5
+ ## Two-Column Layout (`x-lt-layout`)
6
+
7
+ Set `"x-lt-layout": "two-column"` at the schema root to arrange fields in a 2-column grid. Use `"x-lt-span": 2` on a field to span the full width.
8
+
9
+ ```json
10
+ {
11
+ "x-lt-layout": "two-column",
12
+ "properties": {
13
+ "first_name": { "type": "string" },
14
+ "last_name": { "type": "string" },
15
+ "notes": { "type": "string", "format": "textarea", "x-lt-span": 2 }
16
+ }
17
+ }
18
+ ```
19
+
20
+ ---
21
+
22
+ ## Field Ordering (`x-lt-order`)
23
+
24
+ By default, fields render in JSON key order. `x-lt-order` sets an explicit sequence. Fields not listed in `x-lt-order` render after the listed ones in their original key order.
25
+
26
+ ```json
27
+ {
28
+ "x-lt-order": ["priority", "decision", "notes"],
29
+ "properties": {
30
+ "notes": { "type": "string" },
31
+ "decision": { "type": "string", "enum": ["approve", "reject", "defer"] },
32
+ "priority": { "type": "number" }
33
+ }
34
+ }
35
+ ```
36
+
37
+ ---
38
+
39
+ ## Section Groups (`x-lt-section`)
40
+
41
+ Group related fields under a labeled section by adding `"x-lt-section": "Label"` to each property. Fields that share the same consecutive section name are collected into one group with a header.
42
+
43
+ Sections are ordered by the first field that carries the section name (respects `x-lt-order`). A field without `x-lt-section` belongs to an unnamed group rendered without a header. Named sections render with a left accent line, a small icon, and the label in uppercase.
44
+
45
+ ```json
46
+ {
47
+ "x-lt-layout": "two-column",
48
+ "x-lt-order": ["patient_id", "heel_cup", "pdac", "approved", "notes"],
49
+ "properties": {
50
+ "patient_id": { "type": "string", "readOnly": true, "x-lt-section": "Facts", "x-lt-hide-if-empty": true },
51
+ "heel_cup": { "type": "string", "readOnly": true, "x-lt-section": "Facts", "x-lt-hide-if-empty": true },
52
+ "pdac": { "type": "boolean","readOnly": true, "x-lt-section": "Facts", "x-lt-hide-if-empty": true },
53
+ "approved": { "type": "string", "enum": ["yes", "no"], "x-lt-section": "Action" },
54
+ "notes": { "type": "string", "format": "textarea", "x-lt-span": 2, "x-lt-section": "Action" }
55
+ }
56
+ }
57
+ ```
58
+
59
+ ---
60
+
61
+ ## Payload Binding (`x-lt-bind`)
62
+
63
+ The form is flat; the payload the workflow consumes rarely is. A field may declare `x-lt-bind` — the path its value occupies in the resolver payload (dot keys, optional `[n]` indices). The dashboard maps the flat form through the binds on submit, and reverse-maps `envelope.formDefaults` through them to prefill. A field with no bind lands at its own name at the payload root.
64
+
65
+ ```json
66
+ {
67
+ "properties": {
68
+ "customer_name": { "type": "string", "x-lt-bind": "customer.name" },
69
+ "contact_email": { "type": "string", "format": "email", "x-lt-bind": "customer.email" },
70
+ "tier": { "type": "string", "enum": ["starter", "professional"], "x-lt-bind": "contract.tier" },
71
+ "notes": { "type": "string", "format": "textarea" }
72
+ }
73
+ }
74
+ ```
75
+
76
+ Submitting `{ customer_name, contact_email, tier, notes }` produces:
77
+
78
+ ```json
79
+ {
80
+ "customer": { "name": "…", "email": "…" },
81
+ "contract": { "tier": "…" },
82
+ "notes": "…"
83
+ }
84
+ ```
85
+
86
+ Only the form is versioned on the role — the payload shape is the workflow's own contract, produced by the binds. Evolve the form and its binds together, and the workflow's resolver type in the same commit.
87
+
88
+ ---
89
+
90
+ ## Help Panel (`x-lt-help`)
91
+
92
+ Schema-level `x-lt-help` carries the form's guidance — checklists, tier tables, callouts, links — as markdown. The dashboard renders it in the side panel beside the form, so the form itself stays a clean title-and-fields surface while the SOP sits one glance to the right. The help text versions with the form: it lives in the same `form_schema` snapshot in `lt_role_schemas`.
93
+
94
+ ```json
95
+ {
96
+ "title": "Customer Intake",
97
+ "x-lt-help": "### Review checklist\n\n1. Confirm the **legal name** matches.\n2. Send a test message before approving.\n\nThis escalation is **{{escalation.status}}** in the **{{escalation.role}}** queue.",
98
+ "properties": { }
99
+ }
100
+ ```
101
+
102
+ ### `{{domain.path}}` tokens
103
+
104
+ `{{domain.path}}` tokens in `x-lt-help` interpolate live values from the escalation record. Five domains are available:
105
+
106
+ | Domain | Resolves against |
107
+ |--------|-----------------|
108
+ | `escalation` | The escalation row (`{{escalation.role}}`, `{{escalation.status}}`) |
109
+ | `metadata` | The row's metadata dict (`{{metadata.schema_version}}`) |
110
+ | `envelope` | The workflow-sent input envelope (`{{envelope.min_score}}`) |
111
+ | `payload` | The escalation context payload (`{{payload.category}}`) |
112
+ | `resolver` | The submitted resolver payload (`{{resolver.notes}}`) |
113
+
114
+ A missing value renders as an em dash. Links whose `href` starts with `/` navigate inside the dashboard.
115
+
116
+ ### Help fallback chain
117
+
118
+ The side panel's Help view resolves in order: `x-lt-help` → `x-lt-context` (plain text) → a state-aware hint ("Claim this escalation to enable the form", "Fill out the form and submit to resolve it", and so on). The panel always gives the resolver a prompt.
119
+
120
+ The panel's other views surface the record itself:
121
+
122
+ | View | Shows | Available to |
123
+ |------|-------|--------------|
124
+ | **Help** | `x-lt-help` markdown, or a state-aware hint | Everyone |
125
+ | **Details** | Status, role, priority, claim provenance, timestamps | Everyone |
126
+ | **AI Analysis** | Triage diagnosis and corrections | When AI is enabled and triage data is present |
127
+ | **Metadata** | The row's metadata values | Everyone |
128
+ | **Context** | Input envelope, escalation context, resolver payload | Everyone |
129
+ | **Errors** | Pre-submission validation failures with click-to-focus | When submit is blocked |
130
+ | **Record** | The raw escalation JSON | Builders (admins, superadmins, engineers) |
131
+
132
+ ---
133
+
134
+ ## Complete Layout Example
135
+
136
+ Two-column intake form with sections, help panel, ordering, and bindings:
137
+
138
+ ```json
139
+ {
140
+ "title": "Customer Intake",
141
+ "description": "Complete all required fields then submit.",
142
+ "x-lt-layout": "two-column",
143
+ "x-lt-order": ["first_name", "last_name", "email", "phone", "tier", "notes"],
144
+ "x-lt-help": "### Intake checklist\n\n1. Confirm the **legal name** matches the ID on file.\n2. Verify the email is reachable — send a test message.\n3. Select tier based on the sales order.\n\n> Escalate non-standard contract language to legal.",
145
+ "required": ["first_name", "last_name", "email", "tier"],
146
+ "properties": {
147
+ "first_name": { "type": "string", "x-lt-section": "Contact" },
148
+ "last_name": { "type": "string", "x-lt-section": "Contact" },
149
+ "email": { "type": "string", "format": "email", "x-lt-section": "Contact", "x-lt-bind": "customer.email" },
150
+ "phone": { "type": "string", "x-lt-section": "Contact" },
151
+ "tier": {
152
+ "type": "string",
153
+ "enum": ["free", "pro", "enterprise"],
154
+ "description": "Select the customer tier",
155
+ "x-lt-section": "Contract",
156
+ "x-lt-bind": "contract.tier"
157
+ },
158
+ "notes": {
159
+ "type": "string",
160
+ "format": "textarea",
161
+ "x-lt-span": 2,
162
+ "description": "Additional notes",
163
+ "x-lt-section": "Contract"
164
+ }
165
+ }
166
+ }
167
+ ```
@@ -0,0 +1,75 @@
1
+ # List Schema (`x-lt-list-schema`)
2
+
3
+ The form schema formats one escalation on the detail page. A role can also own a `list_schema` that formats its whole list page — the list-page analog of the resolve form. It is opt-in and applies only when the list is scoped to exactly one role (`/escalations/available?role=<role>`). Absent, the list renders the standard engineer table; present, a rich role-authored view renders with a "Table view" toggle one click away.
4
+
5
+ The list schema is versioned independently of the form schema — a list edit never bumps the form version. Edit it at `/admin/roles/:role/list-schema`. The list always renders the latest version.
6
+
7
+ ---
8
+
9
+ ## Vocabulary
10
+
11
+ Every string is a markdown/text template run through the same `{{domain.path}}` token binding as `x-lt-help` (domains: `escalation | metadata | envelope | payload | resolver`, evaluated against each row). `body` strings render through the markdown renderer.
12
+
13
+ | Key | Level | Purpose |
14
+ |-----|-------|---------|
15
+ | `x-lt-layout` | schema | `"active-history"`, `"active"`, `"facet-table"`, or `"table"` |
16
+ | `x-lt-help` | schema | Optional markdown header, interpolated with the active row |
17
+ | `x-lt-active` | schema | The live item card: `{ title, subtitle?, body?, fields?: [{ label, value }] }` |
18
+ | `x-lt-history` | schema | History column: `{ row: { title, subtitle?, meta? }, limit?, status? }` |
19
+ | `x-lt-columns` | schema | Column definitions for `facet-table` layout: `[{ label: string, value: string }]` |
20
+
21
+ The **active** item is the first non-terminal escalation. The **history** column is lazy-loaded — a "Load full history" link fetches resolved items on demand (`status` defaults to `"resolved"`, `limit` to 25). Unknown or absent `x-lt-layout` is a safe no-op that falls back to the table.
22
+
23
+ ---
24
+
25
+ ## Layouts
26
+
27
+ ### `"active-history"` — document workflow
28
+
29
+ Use when the queue contains exactly one live item at a time and a history column is meaningful — a policy document, a looped review, a sequential approval chain.
30
+
31
+ ```json
32
+ {
33
+ "x-lt-layout": "active-history",
34
+ "x-lt-help": "# {{metadata.title}}\nThe authoritative policy. One revision is live at a time.",
35
+ "x-lt-active": {
36
+ "title": "{{metadata.title}}",
37
+ "subtitle": "Revision {{metadata.revision}} · effective {{metadata.effective_date}}",
38
+ "body": "{{metadata.document_markdown}}",
39
+ "fields": [
40
+ { "label": "Owner", "value": "{{metadata.owner}}" },
41
+ { "label": "Claimed by", "value": "{{escalation.assigned_to}}" }
42
+ ]
43
+ },
44
+ "x-lt-history": {
45
+ "row": { "title": "{{metadata.title}} — revision {{metadata.revision}}" },
46
+ "limit": 25
47
+ }
48
+ }
49
+ ```
50
+
51
+ The reference: `examples/workflows/policy-document/` (role seeded by `examples/seed-policy-document.ts`) — a looped workflow keeps exactly one escalation live and each resolution folds into the next revision.
52
+
53
+ ### `"active"` — current item card only
54
+
55
+ Use when there is one live item but no history view is needed — a single-request approval or a just-in-time form.
56
+
57
+ ### `"facet-table"` — scannable queue
58
+
59
+ Use when the queue contains many concurrent rows and the role's context is best expressed as a table — a print farm, order queue, or batch-processing pond. Every pending escalation is a row; columns are defined by `x-lt-columns`.
60
+
61
+ ```json
62
+ {
63
+ "x-lt-layout": "facet-table",
64
+ "x-lt-columns": [
65
+ { "label": "Patient", "value": "{{metadata.patientId}}" },
66
+ { "label": "Heel cup", "value": "{{metadata.heelCup}}" },
67
+ { "label": "PDAC", "value": "{{metadata.pdac}}" },
68
+ { "label": "Station", "value": "{{metadata.station}}" },
69
+ { "label": "Priority", "value": "{{escalation.priority}}" },
70
+ { "label": "Created", "value": "{{escalation.created_at}}" }
71
+ ]
72
+ }
73
+ ```
74
+
75
+ A status dot precedes the first column automatically. ISO datetime values render as a readable relative date with a full-timestamp tooltip. Missing token values render as an em dash. Clicking any row navigates to the detail page. `x-lt-help` and `x-lt-active` are ignored in this layout.
@@ -0,0 +1,128 @@
1
+ # Conditional Visibility
2
+
3
+ ## `x-lt-showIf`
4
+
5
+ A field can be shown or hidden based on a value in the escalation context. Apply `x-lt-showIf` to any property:
6
+
7
+ ```json
8
+ "x-lt-showIf": "domain.path"
9
+ ```
10
+
11
+ The value at `domain.path` is evaluated for truthiness. If truthy, the field shows; if absent, null, false, or an empty string, it is hidden. Prefix `!` to invert — show when the value is absent or falsy.
12
+
13
+ ### Domains
14
+
15
+ | Domain | Resolves against |
16
+ |--------|-----------------|
17
+ | `metadata` | The row's metadata dict |
18
+ | `payload` | The escalation context payload (`escalation_payload`) |
19
+ | `envelope` | The workflow-sent input envelope |
20
+ | `escalation` | Top-level escalation row fields (`role`, `status`, `priority`, …) |
21
+ | `resolver` | The submitted resolver payload — **live**: reacts in real time as the user edits the form |
22
+
23
+ Conditions based on `metadata`, `payload`, `envelope`, and `escalation` are static (read from the stored row). Conditions based on `resolver` are dynamic — the field appears or disappears immediately as the user edits a sibling field, without a page reload.
24
+
25
+ ### Item type branching
26
+
27
+ A queue that receives both regular items and a special signal type. The payload carries `crew_pill` to distinguish them:
28
+
29
+ ```json
30
+ {
31
+ "title": "Worker Station",
32
+ "properties": {
33
+ "action_taken": {
34
+ "type": "string",
35
+ "enum": ["completed", "deferred", "escalated"],
36
+ "description": "Outcome for this work item",
37
+ "x-lt-showIf": "!payload.crew_pill"
38
+ },
39
+ "notes": {
40
+ "type": "string",
41
+ "format": "textarea",
42
+ "x-lt-showIf": "!payload.crew_pill"
43
+ },
44
+ "shutdown_ack": {
45
+ "type": "boolean",
46
+ "title": "Acknowledge shutdown",
47
+ "description": "Confirm you are stopping work and clearing the station",
48
+ "x-lt-showIf": "payload.crew_pill"
49
+ }
50
+ }
51
+ }
52
+ ```
53
+
54
+ ### Approval/rejection — live conditional required
55
+
56
+ `rejection_reason` is hidden when `approved` is checked. Because the condition targets `resolver.approved`, the field appears immediately when the user unchecks the toggle — no submit needed. The `required` array marks `rejection_reason` as required — the required check is skipped for fields hidden by `x-lt-showIf`:
57
+
58
+ ```json
59
+ {
60
+ "title": "Quality Review",
61
+ "x-lt-order": ["approved", "rejection_reason", "notes"],
62
+ "required": ["rejection_reason"],
63
+ "properties": {
64
+ "approved": {
65
+ "type": "boolean",
66
+ "description": "Approve this submission"
67
+ },
68
+ "rejection_reason": {
69
+ "type": "string",
70
+ "format": "textarea",
71
+ "description": "Explain why this submission is not approved",
72
+ "x-lt-showIf": "!resolver.approved"
73
+ },
74
+ "notes": {
75
+ "type": "string",
76
+ "format": "textarea",
77
+ "description": "Reviewer notes"
78
+ }
79
+ }
80
+ }
81
+ ```
82
+
83
+ In the workflow, guard the payload fields before accessing them:
84
+
85
+ ```typescript
86
+ const decision = await conditionLT<{
87
+ approved: boolean;
88
+ rejection_reason?: string;
89
+ notes?: string;
90
+ }>(signalId, { role: 'quality-reviewer', /* ... */ });
91
+
92
+ if (!decision.approved) {
93
+ await sendForRework({ reason: decision.rejection_reason, notes: decision.notes });
94
+ } else {
95
+ await advanceOrder();
96
+ }
97
+ ```
98
+
99
+ ### Submission behavior
100
+
101
+ Hidden fields are not rendered but their values (if any) remain in form state and are submitted only if they were filled before being hidden. Required validation is not applied to fields hidden at submission time.
102
+
103
+ ---
104
+
105
+ ## `x-lt-hide-if-empty`
106
+
107
+ A field with `"x-lt-hide-if-empty": true` is suppressed entirely when its value is null, an empty string, `false`, or `0`. This is primarily useful for `readOnly` fact fields that are only present on some escalations:
108
+
109
+ ```json
110
+ {
111
+ "properties": {
112
+ "heel_raise": {
113
+ "type": "string",
114
+ "readOnly": true,
115
+ "x-lt-hide-if-empty": true,
116
+ "description": "Heel raise specification"
117
+ },
118
+ "notes": {
119
+ "type": "string",
120
+ "readOnly": true,
121
+ "x-lt-hide-if-empty": true,
122
+ "description": "Additional processing notes"
123
+ }
124
+ }
125
+ }
126
+ ```
127
+
128
+ `x-lt-hide-if-empty` is evaluated against the field's current value in form state. It is distinct from `x-lt-showIf`, which evaluates against a path in the escalation context.