@hotmeshio/long-tail 0.10.14 → 0.11.1

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 (344) hide show
  1. package/build/api/escalations/analytics.d.ts +6 -0
  2. package/build/api/escalations/analytics.js +123 -0
  3. package/build/api/escalations/index.d.ts +1 -0
  4. package/build/api/escalations/index.js +4 -1
  5. package/build/api/roles.d.ts +4 -0
  6. package/build/api/roles.js +8 -0
  7. package/build/api/scan-codes/choice.d.ts +11 -0
  8. package/build/api/scan-codes/choice.js +136 -0
  9. package/build/api/scan-codes/context.d.ts +24 -0
  10. package/build/api/scan-codes/context.js +86 -0
  11. package/build/api/scan-codes/execute.d.ts +1 -0
  12. package/build/api/scan-codes/execute.js +49 -244
  13. package/build/api/scan-codes/identity.d.ts +18 -0
  14. package/build/api/scan-codes/identity.js +116 -0
  15. package/build/api/scan-codes/index.d.ts +2 -0
  16. package/build/api/scan-codes/index.js +6 -1
  17. package/build/api/scan-codes/locate.d.ts +14 -0
  18. package/build/api/scan-codes/locate.js +127 -0
  19. package/build/api/scan-codes/present.d.ts +9 -0
  20. package/build/api/scan-codes/present.js +59 -0
  21. package/build/api/scan-codes/verbs.d.ts +15 -0
  22. package/build/api/scan-codes/verbs.js +205 -0
  23. package/build/bin/ltc.js +33 -0
  24. package/build/lib/cli/commands/escalations.d.ts +31 -0
  25. package/build/lib/cli/commands/escalations.js +88 -0
  26. package/build/lib/db/schemas/028_role_entity_facet.sql +18 -0
  27. package/build/lib/db/schemas/029_scan_scheme_kind.sql +31 -0
  28. package/build/modules/config.d.ts +5 -0
  29. package/build/modules/config.js +12 -0
  30. package/build/routes/escalations/acting.d.ts +12 -0
  31. package/build/routes/escalations/acting.js +25 -0
  32. package/build/routes/escalations/analytics.d.ts +8 -0
  33. package/build/routes/escalations/analytics.js +63 -0
  34. package/build/routes/escalations/index.js +3 -0
  35. package/build/routes/escalations/resolve.js +8 -1
  36. package/build/routes/escalations/single.js +13 -4
  37. package/build/routes/mcp-endpoint.js +4 -1
  38. package/build/routes/roles.js +1 -1
  39. package/build/routes/scan-codes.js +28 -2
  40. package/build/scripts/ortho-populate.js +14 -0
  41. package/build/scripts/printer-cycle.d.ts +25 -0
  42. package/build/scripts/printer-cycle.js +169 -0
  43. package/build/scripts/twin-farm.js +19 -5
  44. package/build/sdk/index.d.ts +2 -0
  45. package/build/sdk/index.js +3 -0
  46. package/build/services/escalation/aggregate-sql.d.ts +36 -0
  47. package/build/services/escalation/aggregate-sql.js +253 -0
  48. package/build/services/escalation/aggregate-validate.d.ts +57 -0
  49. package/build/services/escalation/aggregate-validate.js +224 -0
  50. package/build/services/escalation/aggregates.d.ts +26 -0
  51. package/build/services/escalation/aggregates.js +204 -0
  52. package/build/services/escalation/client.js +57 -21
  53. package/build/services/escalation/facet-sql.js +27 -0
  54. package/build/services/escalation/index.d.ts +2 -0
  55. package/build/services/escalation/index.js +8 -1
  56. package/build/services/escalation/metrics-cache.d.ts +18 -0
  57. package/build/services/escalation/metrics-cache.js +47 -1
  58. package/build/services/escalation/queries.js +2 -0
  59. package/build/services/iam/acting-identity.d.ts +16 -0
  60. package/build/services/iam/acting-identity.js +60 -0
  61. package/build/services/mcp/external-server.js +34 -26
  62. package/build/services/role/index.js +2 -0
  63. package/build/services/role/sql.d.ts +10 -2
  64. package/build/services/role/sql.js +14 -1
  65. package/build/services/role/types.d.ts +16 -0
  66. package/build/services/scan-code/read.js +4 -0
  67. package/build/services/scan-code/sql.d.ts +4 -4
  68. package/build/services/scan-code/sql.js +14 -8
  69. package/build/services/scan-code/validate.d.ts +2 -0
  70. package/build/services/scan-code/validate.js +86 -0
  71. package/build/services/scan-code/write.d.ts +4 -0
  72. package/build/services/scan-code/write.js +16 -2
  73. package/build/services/user/crud.d.ts +6 -0
  74. package/build/services/user/crud.js +16 -0
  75. package/build/services/user/index.d.ts +1 -1
  76. package/build/services/user/index.js +2 -1
  77. package/build/services/user/sql.d.ts +8 -0
  78. package/build/services/user/sql.js +9 -1
  79. package/build/system/index.js +17 -0
  80. package/build/system/mcp-servers/admin/escalations.js +34 -0
  81. package/build/system/mcp-servers/admin/index.js +2 -4
  82. package/build/system/mcp-servers/admin/scan-codes.js +17 -1
  83. package/build/system/mcp-servers/admin/schemas.d.ts +643 -25
  84. package/build/system/mcp-servers/admin/schemas.js +127 -19
  85. package/build/system/mcp-servers/admin/users.js +3 -1
  86. package/build/system/mcp-servers/human-queue-schemas.d.ts +2 -2
  87. package/build/system/seed/tool-manifests-admin.d.ts +2473 -1327
  88. package/build/system/seed/tool-manifests-admin.js +8 -10
  89. package/build/tsconfig.tsbuildinfo +1 -1
  90. package/build/types/analytics.d.ts +154 -0
  91. package/build/types/analytics.js +14 -0
  92. package/build/types/facets.d.ts +21 -0
  93. package/build/types/index.d.ts +5 -2
  94. package/build/types/index.js +5 -1
  95. package/build/types/scan-choice.d.ts +49 -0
  96. package/build/types/scan-choice.js +9 -0
  97. package/build/types/scan-code.d.ts +72 -1
  98. package/build/types/scan-code.js +30 -1
  99. package/dashboard/dist/assets/{AdminDashboard-Chg69wlx.js → AdminDashboard-D4ebrcNR.js} +2 -2
  100. package/dashboard/dist/assets/{AdminDashboard-Chg69wlx.js.map → AdminDashboard-D4ebrcNR.js.map} +1 -1
  101. package/dashboard/dist/assets/{AgentConfigPage-DRTFwbfC.js → AgentConfigPage-DP4KUuCX.js} +2 -2
  102. package/dashboard/dist/assets/{AgentConfigPage-DRTFwbfC.js.map → AgentConfigPage-DP4KUuCX.js.map} +1 -1
  103. package/dashboard/dist/assets/{AgentDetailPage-JKByfFIi.js → AgentDetailPage-ktYiR1kq.js} +3 -3
  104. package/dashboard/dist/assets/{AgentDetailPage-JKByfFIi.js.map → AgentDetailPage-ktYiR1kq.js.map} +1 -1
  105. package/dashboard/dist/assets/{AgentsPage-BlgEmKhA.js → AgentsPage-B4iqm4-h.js} +2 -2
  106. package/dashboard/dist/assets/{AgentsPage-BlgEmKhA.js.map → AgentsPage-B4iqm4-h.js.map} +1 -1
  107. package/dashboard/dist/assets/AvailableEscalationsPage-tL3mM1RG.js +2 -0
  108. package/dashboard/dist/assets/AvailableEscalationsPage-tL3mM1RG.js.map +1 -0
  109. package/dashboard/dist/assets/{BotPicker-CCVcz4Cq.js → BotPicker-CLrJuHnW.js} +2 -2
  110. package/dashboard/dist/assets/{BotPicker-CCVcz4Cq.js.map → BotPicker-CLrJuHnW.js.map} +1 -1
  111. package/dashboard/dist/assets/CapabilitiesPage-DG5V0tMT.js +2 -0
  112. package/dashboard/dist/assets/CapabilitiesPage-DG5V0tMT.js.map +1 -0
  113. package/dashboard/dist/assets/{CollapsibleSection-8YE965QX.js → CollapsibleSection-CvLHvVfU.js} +2 -2
  114. package/dashboard/dist/assets/{CollapsibleSection-8YE965QX.js.map → CollapsibleSection-CvLHvVfU.js.map} +1 -1
  115. package/dashboard/dist/assets/{CountdownTimer-Bxvq3D7j.js → CountdownTimer-Co57-iLY.js} +2 -2
  116. package/dashboard/dist/assets/{CountdownTimer-Bxvq3D7j.js.map → CountdownTimer-Co57-iLY.js.map} +1 -1
  117. package/dashboard/dist/assets/{CredentialsPage-Bn7_SX6-.js → CredentialsPage-qCJZ3rdi.js} +2 -2
  118. package/dashboard/dist/assets/{CredentialsPage-Bn7_SX6-.js.map → CredentialsPage-qCJZ3rdi.js.map} +1 -1
  119. package/dashboard/dist/assets/{CronLabel-Bn4mX_Yn.js → CronLabel-C3iKifDN.js} +2 -2
  120. package/dashboard/dist/assets/{CronLabel-Bn4mX_Yn.js.map → CronLabel-C3iKifDN.js.map} +1 -1
  121. package/dashboard/dist/assets/{CustomDurationPicker-pMcrFyW6.js → CustomDurationPicker-C-Szl4rU.js} +2 -2
  122. package/dashboard/dist/assets/{CustomDurationPicker-pMcrFyW6.js.map → CustomDurationPicker-C-Szl4rU.js.map} +1 -1
  123. package/dashboard/dist/assets/{DropZone-zCEGjjk-.js → DropZone-BWD9aGOz.js} +2 -2
  124. package/dashboard/dist/assets/{DropZone-zCEGjjk-.js.map → DropZone-BWD9aGOz.js.map} +1 -1
  125. package/dashboard/dist/assets/{ElapsedCell-BGO7IXEg.js → ElapsedCell-CamZk5o1.js} +2 -2
  126. package/dashboard/dist/assets/{ElapsedCell-BGO7IXEg.js.map → ElapsedCell-CamZk5o1.js.map} +1 -1
  127. package/dashboard/dist/assets/EntityTimelinePanel-CABCW2dS.js +2 -0
  128. package/dashboard/dist/assets/EntityTimelinePanel-CABCW2dS.js.map +1 -0
  129. package/dashboard/dist/assets/{EscalationListSchemaPage-CFDxbd50.js → EscalationListSchemaPage-BddDSfjl.js} +3 -3
  130. package/dashboard/dist/assets/{EscalationListSchemaPage-CFDxbd50.js.map → EscalationListSchemaPage-BddDSfjl.js.map} +1 -1
  131. package/dashboard/dist/assets/{EscalationSchemaPage-DnXPnDkz.js → EscalationSchemaPage-BRyCtw9R.js} +3 -3
  132. package/dashboard/dist/assets/{EscalationSchemaPage-DnXPnDkz.js.map → EscalationSchemaPage-BRyCtw9R.js.map} +1 -1
  133. package/dashboard/dist/assets/{EscalationsOverview-BZMVXrkK.js → EscalationsOverview-DUWYxJyD.js} +2 -2
  134. package/dashboard/dist/assets/{EscalationsOverview-BZMVXrkK.js.map → EscalationsOverview-DUWYxJyD.js.map} +1 -1
  135. package/dashboard/dist/assets/{EventTable-Bfi-f8as.js → EventTable-CtVuCgq6.js} +2 -2
  136. package/dashboard/dist/assets/{EventTable-Bfi-f8as.js.map → EventTable-CtVuCgq6.js.map} +1 -1
  137. package/dashboard/dist/assets/FilterBar-BYsBgOiE.js +2 -0
  138. package/dashboard/dist/assets/{FilterBar-Do7yR4xM.js.map → FilterBar-BYsBgOiE.js.map} +1 -1
  139. package/dashboard/dist/assets/{GraphInvokePage-d1ie4T7f.js → GraphInvokePage-CA4skPN2.js} +2 -2
  140. package/dashboard/dist/assets/{GraphInvokePage-d1ie4T7f.js.map → GraphInvokePage-CA4skPN2.js.map} +1 -1
  141. package/dashboard/dist/assets/HomePage-DwG-AnjQ.js +2 -0
  142. package/dashboard/dist/assets/{HomePage-BEeodImU.js.map → HomePage-DwG-AnjQ.js.map} +1 -1
  143. package/dashboard/dist/assets/ListToolbar-D86IN_hQ.js +2 -0
  144. package/dashboard/dist/assets/{ListToolbar-CLtw-1QN.js.map → ListToolbar-D86IN_hQ.js.map} +1 -1
  145. package/dashboard/dist/assets/{McpOverview-DIsy9TB3.js → McpOverview-CoEHGseD.js} +2 -2
  146. package/dashboard/dist/assets/{McpOverview-DIsy9TB3.js.map → McpOverview-CoEHGseD.js.map} +1 -1
  147. package/dashboard/dist/assets/{McpQueryDetailPage-6PomdsyD.js → McpQueryDetailPage-C9hMgJe1.js} +2 -2
  148. package/dashboard/dist/assets/{McpQueryDetailPage-6PomdsyD.js.map → McpQueryDetailPage-C9hMgJe1.js.map} +1 -1
  149. package/dashboard/dist/assets/{McpQueryPage-YMdQCXm9.js → McpQueryPage-B20Zn4QX.js} +2 -2
  150. package/dashboard/dist/assets/{McpQueryPage-YMdQCXm9.js.map → McpQueryPage-B20Zn4QX.js.map} +1 -1
  151. package/dashboard/dist/assets/{McpRunDetailPage-DsD1Gm2o.js → McpRunDetailPage-DDKHf-3R.js} +2 -2
  152. package/dashboard/dist/assets/{McpRunDetailPage-DsD1Gm2o.js.map → McpRunDetailPage-DDKHf-3R.js.map} +1 -1
  153. package/dashboard/dist/assets/{McpRunsPage--a5Jgb33.js → McpRunsPage-Puyn3iJr.js} +2 -2
  154. package/dashboard/dist/assets/{McpRunsPage--a5Jgb33.js.map → McpRunsPage-Puyn3iJr.js.map} +1 -1
  155. package/dashboard/dist/assets/{NamespacePill-wV6L4VW1.js → NamespacePill-C09QuOEz.js} +2 -2
  156. package/dashboard/dist/assets/{NamespacePill-wV6L4VW1.js.map → NamespacePill-C09QuOEz.js.map} +1 -1
  157. package/dashboard/dist/assets/OperationsPage-Bsrt4XTL.js +3 -0
  158. package/dashboard/dist/assets/OperationsPage-Bsrt4XTL.js.map +1 -0
  159. package/dashboard/dist/assets/OperatorDashboard-CXBbFLAL.js +2 -0
  160. package/dashboard/dist/assets/{OperatorDashboard-CkLfbxEh.js.map → OperatorDashboard-CXBbFLAL.js.map} +1 -1
  161. package/dashboard/dist/assets/{PageHeader-BtIr6r-8.js → PageHeader-B7YJxa4o.js} +2 -2
  162. package/dashboard/dist/assets/{PageHeader-BtIr6r-8.js.map → PageHeader-B7YJxa4o.js.map} +1 -1
  163. package/dashboard/dist/assets/{PageHeaderWithStats-C2sGqFQG.js → PageHeaderWithStats-gT-5ovJh.js} +2 -2
  164. package/dashboard/dist/assets/{PageHeaderWithStats-C2sGqFQG.js.map → PageHeaderWithStats-gT-5ovJh.js.map} +1 -1
  165. package/dashboard/dist/assets/{PersonaDetailPage-CCmOgKQb.js → PersonaDetailPage-BN5xNDDX.js} +2 -2
  166. package/dashboard/dist/assets/{PersonaDetailPage-CCmOgKQb.js.map → PersonaDetailPage-BN5xNDDX.js.map} +1 -1
  167. package/dashboard/dist/assets/PersonasPage-Bdtv6HPZ.js +2 -0
  168. package/dashboard/dist/assets/PersonasPage-Bdtv6HPZ.js.map +1 -0
  169. package/dashboard/dist/assets/{ProcessDetailPage-DT9BlJXK.js → ProcessDetailPage-BMg70ZAJ.js} +2 -2
  170. package/dashboard/dist/assets/{ProcessDetailPage-DT9BlJXK.js.map → ProcessDetailPage-BMg70ZAJ.js.map} +1 -1
  171. package/dashboard/dist/assets/{ProcessesListPage-DgSGzTDT.js → ProcessesListPage-_KvdxGhL.js} +2 -2
  172. package/dashboard/dist/assets/{ProcessesListPage-DgSGzTDT.js.map → ProcessesListPage-_KvdxGhL.js.map} +1 -1
  173. package/dashboard/dist/assets/RoleDetailPage-BM5BXc7j.js +8 -0
  174. package/dashboard/dist/assets/RoleDetailPage-BM5BXc7j.js.map +1 -0
  175. package/dashboard/dist/assets/{RolePill-BBYjhAPj.js → RolePill-CtYI1_k1.js} +2 -2
  176. package/dashboard/dist/assets/{RolePill-BBYjhAPj.js.map → RolePill-CtYI1_k1.js.map} +1 -1
  177. package/dashboard/dist/assets/RolesPage-Bv23VmUi.js +2 -0
  178. package/dashboard/dist/assets/RolesPage-Bv23VmUi.js.map +1 -0
  179. package/dashboard/dist/assets/{RunAsSelector-CateuvcC.js → RunAsSelector-D-Sjr7uB.js} +2 -2
  180. package/dashboard/dist/assets/{RunAsSelector-CateuvcC.js.map → RunAsSelector-D-Sjr7uB.js.map} +1 -1
  181. package/dashboard/dist/assets/ScanCodesPage-s3OEMJno.js +2 -0
  182. package/dashboard/dist/assets/ScanCodesPage-s3OEMJno.js.map +1 -0
  183. package/dashboard/dist/assets/ScanSchemeDetailPage-D8WQkZmw.js +2 -0
  184. package/dashboard/dist/assets/ScanSchemeDetailPage-D8WQkZmw.js.map +1 -0
  185. package/dashboard/dist/assets/ScanStationPage-CkRQaH5G.js +2 -0
  186. package/dashboard/dist/assets/ScanStationPage-CkRQaH5G.js.map +1 -0
  187. package/dashboard/dist/assets/{ScopeBadge-xs2Nhjy4.js → ScopeBadge-B76rOtA0.js} +2 -2
  188. package/dashboard/dist/assets/{ScopeBadge-xs2Nhjy4.js.map → ScopeBadge-B76rOtA0.js.map} +1 -1
  189. package/dashboard/dist/assets/{StickyPagination-Cg-QdYWL.js → StickyPagination-BpTIMOL5.js} +2 -2
  190. package/dashboard/dist/assets/{StickyPagination-Cg-QdYWL.js.map → StickyPagination-BpTIMOL5.js.map} +1 -1
  191. package/dashboard/dist/assets/{StreamMessageDetail-DEeuh4_-.js → StreamMessageDetail-ChoWrZzO.js} +2 -2
  192. package/dashboard/dist/assets/{StreamMessageDetail-DEeuh4_-.js.map → StreamMessageDetail-ChoWrZzO.js.map} +1 -1
  193. package/dashboard/dist/assets/{SwimlaneTimeline-DMY0jL2F.js → SwimlaneTimeline-nmIqEuEH.js} +2 -2
  194. package/dashboard/dist/assets/{SwimlaneTimeline-DMY0jL2F.js.map → SwimlaneTimeline-nmIqEuEH.js.map} +1 -1
  195. package/dashboard/dist/assets/{TagInput-BMmiV-MD.js → TagInput-BQAPOp17.js} +2 -2
  196. package/dashboard/dist/assets/{TagInput-BMmiV-MD.js.map → TagInput-BQAPOp17.js.map} +1 -1
  197. package/dashboard/dist/assets/{TaskDetailPage-CPbkIvfF.js → TaskDetailPage-KajZD2Cp.js} +2 -2
  198. package/dashboard/dist/assets/{TaskDetailPage-CPbkIvfF.js.map → TaskDetailPage-KajZD2Cp.js.map} +1 -1
  199. package/dashboard/dist/assets/{TaskQueuePill-DLgYaw1x.js → TaskQueuePill-C2NImrwl.js} +2 -2
  200. package/dashboard/dist/assets/{TaskQueuePill-DLgYaw1x.js.map → TaskQueuePill-C2NImrwl.js.map} +1 -1
  201. package/dashboard/dist/assets/{TasksListPage-CtHudKhQ.js → TasksListPage-CiqSfapP.js} +2 -2
  202. package/dashboard/dist/assets/{TasksListPage-CtHudKhQ.js.map → TasksListPage-CiqSfapP.js.map} +1 -1
  203. package/dashboard/dist/assets/{TimeAgo-BdHOizWg.js → TimeAgo-ClauexJj.js} +2 -2
  204. package/dashboard/dist/assets/{TimeAgo-BdHOizWg.js.map → TimeAgo-ClauexJj.js.map} +1 -1
  205. package/dashboard/dist/assets/{TimestampCell-FVukzKRe.js → TimestampCell-CXmbCnAJ.js} +2 -2
  206. package/dashboard/dist/assets/{TimestampCell-FVukzKRe.js.map → TimestampCell-CXmbCnAJ.js.map} +1 -1
  207. package/dashboard/dist/assets/{ToolPill-BvS53hxP.js → ToolPill-FOZm67TS.js} +2 -2
  208. package/dashboard/dist/assets/{ToolPill-BvS53hxP.js.map → ToolPill-FOZm67TS.js.map} +1 -1
  209. package/dashboard/dist/assets/{ToolTestPanel-HN0OCYTL.js → ToolTestPanel-DFUU0zGv.js} +2 -2
  210. package/dashboard/dist/assets/{ToolTestPanel-HN0OCYTL.js.map → ToolTestPanel-DFUU0zGv.js.map} +1 -1
  211. package/dashboard/dist/assets/{TopicDetailPage-DHdSpN61.js → TopicDetailPage-DuN5BrsI.js} +2 -2
  212. package/dashboard/dist/assets/{TopicDetailPage-DHdSpN61.js.map → TopicDetailPage-DuN5BrsI.js.map} +1 -1
  213. package/dashboard/dist/assets/TopicsPage-DZ1NQdYG.js +2 -0
  214. package/dashboard/dist/assets/{TopicsPage-Clr6Tl5P.js.map → TopicsPage-DZ1NQdYG.js.map} +1 -1
  215. package/dashboard/dist/assets/{UserName-BUVYyIAS.js → UserName-BPHG_MQs.js} +2 -2
  216. package/dashboard/dist/assets/{UserName-BUVYyIAS.js.map → UserName-BPHG_MQs.js.map} +1 -1
  217. package/dashboard/dist/assets/{WorkflowExecutionPage-kncWFfe5.js → WorkflowExecutionPage-D_Z-tIjR.js} +2 -2
  218. package/dashboard/dist/assets/{WorkflowExecutionPage-kncWFfe5.js.map → WorkflowExecutionPage-D_Z-tIjR.js.map} +1 -1
  219. package/dashboard/dist/assets/{WorkflowPill-BPC_Dc_I.js → WorkflowPill-C5TEWlfO.js} +2 -2
  220. package/dashboard/dist/assets/{WorkflowPill-BPC_Dc_I.js.map → WorkflowPill-C5TEWlfO.js.map} +1 -1
  221. package/dashboard/dist/assets/{WorkflowsDashboard-4ypPDGKx.js → WorkflowsDashboard-VMBW1WuR.js} +2 -2
  222. package/dashboard/dist/assets/{WorkflowsDashboard-4ypPDGKx.js.map → WorkflowsDashboard-VMBW1WuR.js.map} +1 -1
  223. package/dashboard/dist/assets/{WorkflowsOverview-CeQlZkkd.js → WorkflowsOverview-CUOXXcpg.js} +2 -2
  224. package/dashboard/dist/assets/{WorkflowsOverview-CeQlZkkd.js.map → WorkflowsOverview-CUOXXcpg.js.map} +1 -1
  225. package/dashboard/dist/assets/{YamlWorkflowDetailPage-DLAlEzwT.js → YamlWorkflowDetailPage-CsfzbboC.js} +2 -2
  226. package/dashboard/dist/assets/{YamlWorkflowDetailPage-DLAlEzwT.js.map → YamlWorkflowDetailPage-CsfzbboC.js.map} +1 -1
  227. package/dashboard/dist/assets/{YamlWorkflowsPage-DQhenKFT.js → YamlWorkflowsPage-BpgKLSGT.js} +2 -2
  228. package/dashboard/dist/assets/{YamlWorkflowsPage-DQhenKFT.js.map → YamlWorkflowsPage-BpgKLSGT.js.map} +1 -1
  229. package/dashboard/dist/assets/{agents-s3feet3W.js → agents-Cg2FVYQP.js} +2 -2
  230. package/dashboard/dist/assets/{agents-s3feet3W.js.map → agents-Cg2FVYQP.js.map} +1 -1
  231. package/dashboard/dist/assets/{bots-CkSd2rn9.js → bots-vtVLRKGt.js} +2 -2
  232. package/dashboard/dist/assets/{bots-CkSd2rn9.js.map → bots-vtVLRKGt.js.map} +1 -1
  233. package/dashboard/dist/assets/{capabilities-DA_r09tM.js → capabilities-DLsXhwlD.js} +2 -2
  234. package/dashboard/dist/assets/{capabilities-DA_r09tM.js.map → capabilities-DLsXhwlD.js.map} +1 -1
  235. package/dashboard/dist/assets/{controlplane-C9c25q21.js → controlplane-Js7dxpX4.js} +2 -2
  236. package/dashboard/dist/assets/{controlplane-C9c25q21.js.map → controlplane-Js7dxpX4.js.map} +1 -1
  237. package/dashboard/dist/assets/escalation-columns-h0NLAJss.js +2 -0
  238. package/dashboard/dist/assets/escalation-columns-h0NLAJss.js.map +1 -0
  239. package/dashboard/dist/assets/index-7c7FEe5S.js +3 -0
  240. package/dashboard/dist/assets/index-7c7FEe5S.js.map +1 -0
  241. package/dashboard/dist/assets/{index-WxE3ONia.js → index-BTzhV9ST.js} +2 -2
  242. package/dashboard/dist/assets/{index-WxE3ONia.js.map → index-BTzhV9ST.js.map} +1 -1
  243. package/dashboard/dist/assets/{index-BIcYbDVf.js → index-Bcv5k8Dw.js} +2 -2
  244. package/dashboard/dist/assets/{index-BIcYbDVf.js.map → index-Bcv5k8Dw.js.map} +1 -1
  245. package/dashboard/dist/assets/{index-BU5FFAJo.js → index-BjfoWzJL.js} +2 -2
  246. package/dashboard/dist/assets/{index-BU5FFAJo.js.map → index-BjfoWzJL.js.map} +1 -1
  247. package/dashboard/dist/assets/{index-C-MmR-jf.js → index-BvVJ538E.js} +2 -2
  248. package/dashboard/dist/assets/{index-C-MmR-jf.js.map → index-BvVJ538E.js.map} +1 -1
  249. package/dashboard/dist/assets/{index-Cw9poaez.js → index-CSpTj5DB.js} +2 -2
  250. package/dashboard/dist/assets/{index-Cw9poaez.js.map → index-CSpTj5DB.js.map} +1 -1
  251. package/dashboard/dist/assets/index-CcjdA7jA.js +65 -0
  252. package/dashboard/dist/assets/index-CcjdA7jA.js.map +1 -0
  253. package/dashboard/dist/assets/{index-S13oPvuS.js → index-DQi6CjqF.js} +2 -2
  254. package/dashboard/dist/assets/{index-S13oPvuS.js.map → index-DQi6CjqF.js.map} +1 -1
  255. package/dashboard/dist/assets/index-DglcqBHn.js +6 -0
  256. package/dashboard/dist/assets/index-DglcqBHn.js.map +1 -0
  257. package/dashboard/dist/assets/{index-DvoMXQqO.js → index-DlO3bCfu.js} +2 -2
  258. package/dashboard/dist/assets/{index-DvoMXQqO.js.map → index-DlO3bCfu.js.map} +1 -1
  259. package/dashboard/dist/assets/{index-Bo5TtX9l.js → index-FYQ9yPhO.js} +2 -2
  260. package/dashboard/dist/assets/{index-Bo5TtX9l.js.map → index-FYQ9yPhO.js.map} +1 -1
  261. package/dashboard/dist/assets/{index-Cmxzk4ay.js → index-KcaVfpWn.js} +2 -2
  262. package/dashboard/dist/assets/{index-Cmxzk4ay.js.map → index-KcaVfpWn.js.map} +1 -1
  263. package/dashboard/dist/assets/index-cFHoANSG.css +1 -0
  264. package/dashboard/dist/assets/{index--7Vdkzwd.js → index-vAkZ5dZe.js} +2 -2
  265. package/dashboard/dist/assets/{index--7Vdkzwd.js.map → index-vAkZ5dZe.js.map} +1 -1
  266. package/dashboard/dist/assets/{knowledge-Bpln0zy_.js → knowledge-Bkvl3cIz.js} +2 -2
  267. package/dashboard/dist/assets/{knowledge-Bpln0zy_.js.map → knowledge-Bkvl3cIz.js.map} +1 -1
  268. package/dashboard/dist/assets/{mcp-BqDq3RZH.js → mcp-DEtDLeIQ.js} +2 -2
  269. package/dashboard/dist/assets/{mcp-BqDq3RZH.js.map → mcp-DEtDLeIQ.js.map} +1 -1
  270. package/dashboard/dist/assets/{mcp-query-DGue75R6.js → mcp-query-BCRgnt0K.js} +2 -2
  271. package/dashboard/dist/assets/{mcp-query-DGue75R6.js.map → mcp-query-BCRgnt0K.js.map} +1 -1
  272. package/dashboard/dist/assets/{personas-B8z_sTiJ.js → personas-CABKq-0L.js} +2 -2
  273. package/dashboard/dist/assets/{personas-B8z_sTiJ.js.map → personas-CABKq-0L.js.map} +1 -1
  274. package/dashboard/dist/assets/{pipelines-DYgNd9nB.js → pipelines-fuN8dVNl.js} +2 -2
  275. package/dashboard/dist/assets/{pipelines-DYgNd9nB.js.map → pipelines-fuN8dVNl.js.map} +1 -1
  276. package/dashboard/dist/assets/{tasks-CJdjjhgO.js → tasks-u3YPNJBo.js} +2 -2
  277. package/dashboard/dist/assets/{tasks-CJdjjhgO.js.map → tasks-u3YPNJBo.js.map} +1 -1
  278. package/dashboard/dist/assets/{topics-DPrL8Dtq.js → topics-D37_RAIT.js} +2 -2
  279. package/dashboard/dist/assets/{topics-DPrL8Dtq.js.map → topics-D37_RAIT.js.map} +1 -1
  280. package/dashboard/dist/assets/useEventHooks-BbXfrFbq.js +2 -0
  281. package/dashboard/dist/assets/{useEventHooks-DXCgDgiu.js.map → useEventHooks-BbXfrFbq.js.map} +1 -1
  282. package/dashboard/dist/assets/{useNamespace-DnFZfy7N.js → useNamespace-DvuoFTda.js} +2 -2
  283. package/dashboard/dist/assets/{useNamespace-DnFZfy7N.js.map → useNamespace-DvuoFTda.js.map} +1 -1
  284. package/dashboard/dist/assets/{useYamlActivityEvents-kx5CLENv.js → useYamlActivityEvents-CL-Uf2cb.js} +2 -2
  285. package/dashboard/dist/assets/{useYamlActivityEvents-kx5CLENv.js.map → useYamlActivityEvents-CL-Uf2cb.js.map} +1 -1
  286. package/dashboard/dist/assets/{users-DKng7vAG.js → users-CMxw_HzK.js} +2 -2
  287. package/dashboard/dist/assets/{users-DKng7vAG.js.map → users-CMxw_HzK.js.map} +1 -1
  288. package/dashboard/dist/assets/{vendor-icons-CN-bAo9U.js → vendor-icons-Aw7bzBxa.js} +157 -137
  289. package/dashboard/dist/assets/vendor-icons-Aw7bzBxa.js.map +1 -0
  290. package/dashboard/dist/assets/{workflows-C4V0rMkM.js → workflows-Bez-o6fu.js} +2 -2
  291. package/dashboard/dist/assets/{workflows-C4V0rMkM.js.map → workflows-Bez-o6fu.js.map} +1 -1
  292. package/dashboard/dist/assets/{yaml-workflows-D3PAiJ9d.js → yaml-workflows-NrXcLsNm.js} +2 -2
  293. package/dashboard/dist/assets/{yaml-workflows-D3PAiJ9d.js.map → yaml-workflows-NrXcLsNm.js.map} +1 -1
  294. package/dashboard/dist/index.html +3 -3
  295. package/docs/api/http/escalations.md +133 -0
  296. package/docs/api/http/roles.md +6 -0
  297. package/docs/api/mcp/admin.md +53 -0
  298. package/docs/api/sdk/escalations.md +58 -0
  299. package/docs/cli.md +11 -0
  300. package/docs/dashboard.md +22 -20
  301. package/docs/data.md +12 -4
  302. package/docs/escalation-analytics.md +251 -0
  303. package/docs/hitl/roles.md +1 -1
  304. package/docs/hitl/x-lt-footer.md +1 -2
  305. package/docs/hitl-guide.md +3 -3
  306. package/docs/iam.md +12 -0
  307. package/docs/operations.md +17 -0
  308. package/docs/scan-codes.md +112 -0
  309. package/docs/sdk.md +1 -1
  310. package/package.json +2 -1
  311. package/build/system/mcp-servers/admin/ortho.d.ts +0 -10
  312. package/build/system/mcp-servers/admin/ortho.js +0 -242
  313. package/dashboard/dist/assets/AvailableEscalationsPage-C_7y692Q.js +0 -2
  314. package/dashboard/dist/assets/AvailableEscalationsPage-C_7y692Q.js.map +0 -1
  315. package/dashboard/dist/assets/CapabilitiesPage-Czi9jUwD.js +0 -2
  316. package/dashboard/dist/assets/CapabilitiesPage-Czi9jUwD.js.map +0 -1
  317. package/dashboard/dist/assets/FilterBar-Do7yR4xM.js +0 -2
  318. package/dashboard/dist/assets/HomePage-BEeodImU.js +0 -2
  319. package/dashboard/dist/assets/ListToolbar-CLtw-1QN.js +0 -2
  320. package/dashboard/dist/assets/OperationsPage-B2v9k3DT.js +0 -2
  321. package/dashboard/dist/assets/OperationsPage-B2v9k3DT.js.map +0 -1
  322. package/dashboard/dist/assets/OperatorDashboard-CkLfbxEh.js +0 -2
  323. package/dashboard/dist/assets/PersonasPage-BJkSuDTN.js +0 -2
  324. package/dashboard/dist/assets/PersonasPage-BJkSuDTN.js.map +0 -1
  325. package/dashboard/dist/assets/RoleDetailPage-DMphTvro.js +0 -8
  326. package/dashboard/dist/assets/RoleDetailPage-DMphTvro.js.map +0 -1
  327. package/dashboard/dist/assets/RolesPage-gi_kaot1.js +0 -2
  328. package/dashboard/dist/assets/RolesPage-gi_kaot1.js.map +0 -1
  329. package/dashboard/dist/assets/ScanCodesPage-CQ8RxMug.js +0 -2
  330. package/dashboard/dist/assets/ScanCodesPage-CQ8RxMug.js.map +0 -1
  331. package/dashboard/dist/assets/ScanSchemeDetailPage-puSbuxcV.js +0 -2
  332. package/dashboard/dist/assets/ScanSchemeDetailPage-puSbuxcV.js.map +0 -1
  333. package/dashboard/dist/assets/TopicsPage-Clr6Tl5P.js +0 -2
  334. package/dashboard/dist/assets/escalation-columns-Cp3UrT48.js +0 -2
  335. package/dashboard/dist/assets/escalation-columns-Cp3UrT48.js.map +0 -1
  336. package/dashboard/dist/assets/index--DG-qQBT.js +0 -65
  337. package/dashboard/dist/assets/index--DG-qQBT.js.map +0 -1
  338. package/dashboard/dist/assets/index-C7ABSOU2.js +0 -6
  339. package/dashboard/dist/assets/index-C7ABSOU2.js.map +0 -1
  340. package/dashboard/dist/assets/index-DEbY57_c.js +0 -3
  341. package/dashboard/dist/assets/index-DEbY57_c.js.map +0 -1
  342. package/dashboard/dist/assets/index-DnV6LSeY.css +0 -1
  343. package/dashboard/dist/assets/useEventHooks-DXCgDgiu.js +0 -2
  344. package/dashboard/dist/assets/vendor-icons-CN-bAo9U.js.map +0 -1
@@ -0,0 +1,251 @@
1
+ # Escalation Analytics — Instrumenting a Process
2
+
3
+ Every escalation is one interval an entity spends in one state: it opens at
4
+ `created_at` and closes the instant the row leaves the live set. The
5
+ escalation queue is therefore a complete state time-series for every process
6
+ the platform runs, and the analytics surface reads it three ways — the
7
+ aggregate, the categorical slice, and the individual — from vocabulary the
8
+ workflow already writes plus two dials on the role.
9
+
10
+ This page is the whole authoring contract. The worked example is the seeded
11
+ printer scenario (`examples/seed-fleet-sim.ts`), which a fresh install
12
+ produces automatically; every call below runs verbatim against it.
13
+
14
+ ## The sixty-second contract
15
+
16
+ **1. The workflow parks every state as an escalation.** `role` is the queue
17
+ that attends the state. `subtype` names the state *within* the role, for
18
+ roles that hold several. `metadata` carries the entity key (stored as a JSON
19
+ string) plus any categorical facts worth slicing by.
20
+
21
+ ```typescript
22
+ // Inside a durable workflow: park the printer in the fleet queue, printing.
23
+ const result = await condition(signalId, {
24
+ activity: ltCreateEscalation,
25
+ config: {
26
+ type: 'fleet',
27
+ subtype: 'printing', // the state, within the printer-fleet role
28
+ role: 'printer-fleet', // the queue that attends it
29
+ metadata: {
30
+ serialNumber: 'PRN-001', // the entity
31
+ model: 'h2s', // categorical facts, sliceable
32
+ pdac: true,
33
+ foaming: false,
34
+ },
35
+ },
36
+ });
37
+ ```
38
+
39
+ **2. Each role declares two dials** (in the role admin page's Pace Board
40
+ section, beside the priority dials, or via `PATCH /api/roles/:role`):
41
+
42
+ | Dial | Meaning |
43
+ |------|---------|
44
+ | **Entity** (`entity_facet`) | The metadata key naming what moves through this role — `serialNumber` here. Roles sharing an entity key form that entity's **system**; the system is derived, never maintained. |
45
+ | **States from** (`entity_state_source`) | How the role names its contribution to the entity's state space. `'role'` (default): being in this queue is one state — a harvest or service bay. `'subtype'`: the role's subtypes are its states — a fleet role whose rows park as `idle` / `printing`. |
46
+
47
+ The printer scenario declares three roles:
48
+
49
+ ```
50
+ printer-fleet entity_facet: serialNumber states from: subtype (idle, printing)
51
+ printer-harvest entity_facet: serialNumber states from: role
52
+ printer-service entity_facet: serialNumber states from: role
53
+ ```
54
+
55
+ That is the entire contract. Everything below is reading it back.
56
+
57
+ ## The three questions
58
+
59
+ ### Q1 — the fleet: how did all printers spend their time?
60
+
61
+ One call: scope to the entity's system, group by the derived state, measure
62
+ dwell (open-seconds, clipped to the window).
63
+
64
+ ```bash
65
+ ltc esc aggregate-facets --entity serialNumber --group-state \
66
+ --window '{"from":"2026-08-01T00:00:00Z","to":"2026-08-01T12:00:00Z"}'
67
+ ```
68
+
69
+ ```typescript
70
+ const { data } = await client.escalations.aggregateByFacets({
71
+ query: { entity: 'serialNumber' },
72
+ groupBy: { state: true },
73
+ measure: { kind: 'dwell', window: { from, to } },
74
+ });
75
+ // → groups: [{ state: 'idle', dwellSeconds, ... }, { state: 'printing', ... },
76
+ // { state: 'printer-harvest', ... }, { state: 'printer-service', ... }]
77
+ ```
78
+
79
+ ```bash
80
+ curl -s -X POST http://localhost:3000/api/escalations/aggregate-by-facets \
81
+ -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
82
+ -d '{"query":{"entity":"serialNumber"},"groupBy":{"state":true},
83
+ "measure":{"kind":"dwell","window":{"from":"2026-08-01T00:00:00Z","to":"2026-08-01T12:00:00Z"}}}'
84
+ ```
85
+
86
+ Each role contributes states per its dial: the fleet's subtypes (`idle`,
87
+ `printing`) plus the harvest and service roles themselves.
88
+
89
+ **Membership** is the other measure — rows (or, with `distinctBy`, distinct
90
+ entities) open at an instant. Omit `asOf` for now; a past `asOf` reconstructs
91
+ the live set at that moment.
92
+
93
+ ```typescript
94
+ // How many printers are in each state right now?
95
+ await client.escalations.aggregateByFacets({
96
+ query: { entity: 'serialNumber' },
97
+ groupBy: { state: true },
98
+ measure: { kind: 'membership' },
99
+ distinctBy: 'serialNumber',
100
+ });
101
+ // → one group per state, count = printers (not rows)
102
+ ```
103
+
104
+ ### Q2 — the slice: the same, compared by any facet
105
+
106
+ Q1 plus `groupBy.facets` — each value gets an independent state split, and a
107
+ NULL group key is a real group (rows missing the facet).
108
+
109
+ ```bash
110
+ ltc esc aggregate-facets --entity serialNumber --group-state --group-facets model \
111
+ --window '{"from":"2026-08-01T00:00:00Z","to":"2026-08-01T12:00:00Z"}'
112
+ # → p1s and h2s, each with its own idle/printing/harvest/service dwell
113
+ ```
114
+
115
+ Filtering works the same way: `--facets '{"pdac":true}'` restricts the whole
116
+ aggregate to the pdac printers.
117
+
118
+ ### Q3 — the individual: how did printer X spend its day?
119
+
120
+ The ordered interval sequence across the whole system, durations included.
121
+ Gaps between intervals are untracked time and are preserved — the settle
122
+ latency between queues is a first-class signal.
123
+
124
+ ```bash
125
+ ltc esc timeline serialNumber PRN-001 --entity serialNumber
126
+ ```
127
+
128
+ ```typescript
129
+ const { data } = await client.escalations.timelineByFacet({
130
+ facet: { key: 'serialNumber', value: 'PRN-001' },
131
+ query: { entity: 'serialNumber' },
132
+ });
133
+ // → intervals: [{ role, subtype, startedAt, endedAt, durationSeconds }, ...]
134
+ // open intervals report endedAt: null; gaps appear as time between spans
135
+ ```
136
+
137
+ ```bash
138
+ curl -s -X POST http://localhost:3000/api/escalations/timeline-by-facet \
139
+ -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
140
+ -d '{"facet":{"key":"serialNumber","value":"PRN-001"},"query":{"entity":"serialNumber"}}'
141
+ ```
142
+
143
+ ## Where the answers appear in the dashboard
144
+
145
+ - **Q1** — `/operations`: the view selector offers every entity system beside
146
+ the station view; the system band shows state dwell over the selected
147
+ period with membership-now counts and the distinct-entity headline.
148
+ - **Q2** — the entity view's **Slice by** control: pick any metadata key the
149
+ rows carry and each value renders its own band, side by side.
150
+ - **Q3** — the entity table's timeline action, or the history affordance on
151
+ any metadata value in any escalation list: the full cross-queue journey in
152
+ the side panel, untracked gaps included.
153
+
154
+ Every surface refreshes from escalation events — resolve something and the
155
+ band moves within seconds.
156
+
157
+ ## The interval, precisely
158
+
159
+ `[created_at, ended_at)`. `ended_at` derives from the terminal transition:
160
+ `resolved_at` for resolved rows, `updated_at` for cancelled or expired rows
161
+ (both stamped inside their status-guarded transitions), NULL while the row is
162
+ live. The `lt_escalations` view exposes it as a computed column, and a
163
+ partial index on the terminal end instant keeps history scans bounded — one
164
+ index write per row, at its terminal transition.
165
+
166
+ `liveStatuses` (default `['pending']`) declares which statuses count as
167
+ live; everything else is terminal.
168
+
169
+ ## Locating one entity among thousands
170
+
171
+ Two filter fields make the individual findable at fleet scale, and both ride
172
+ the scans the aggregate already bounds — no additional indexes:
173
+
174
+ - **`prefix`** — case-insensitive prefix match on facet values
175
+ (`metadata->>key ILIKE 'value%'`, wildcards escaped). Type or scan the
176
+ leading characters of a serial and the entity table narrows:
177
+
178
+ ```bash
179
+ ltc esc aggregate-facets --entity serialNumber --group-facets serialNumber \
180
+ --prefix '{"serialNumber":"PRN-00"}' --window '{"from":"...","to":"..."}'
181
+ ```
182
+
183
+ - **`anyOf`** — the row carries at least one of the given facet sets
184
+ (`metadata @> ANY(...)`, GIN-served; max 200 entries). Targets an explicit
185
+ entity set — the current table page — in one query:
186
+
187
+ ```json
188
+ { "anyOf": [{ "serialNumber": "PRN-001" }, { "serialNumber": "PRN-002" }] }
189
+ ```
190
+
191
+ Result groups page with `orderBy` + `limit`/`offset`; `overflow: true` means
192
+ another page exists. In the dashboard, every entity row is deep-linkable:
193
+ `/operations?lens=serialNumber&entity=PRN-003` opens the fleet view focused
194
+ on that printer with its timeline panel raised, and `&find=PRN-00` restores
195
+ a narrowed table.
196
+
197
+ ## Paging a long timeline
198
+
199
+ Timelines order by start instant. The default `asc` reads the journey
200
+ top-down; a long history pages recent-first with `order: 'desc'` and the
201
+ `before` cursor (a strict upper bound on `startedAt`):
202
+
203
+ ```bash
204
+ ltc esc timeline serialNumber PRN-001 --entity serialNumber \
205
+ --order desc --limit 100
206
+ # then, for the next-older page:
207
+ ltc esc timeline serialNumber PRN-001 --entity serialNumber \
208
+ --order desc --limit 100 --before 2026-08-01T09:15:00Z
209
+ ```
210
+
211
+ The dashboard's timeline panel loads the most recent 100 intervals and
212
+ offers "Load earlier" until the history is exhausted.
213
+
214
+ ## Fail-loud validation
215
+
216
+ The filter takes the WHAT of a facet query only — `role`/`roles` (or
217
+ `entity`), `facets`, `block`, `range`, `exists`. Everything that would
218
+ silently change what an aggregate means is rejected with a 400:
219
+
220
+ - `status`, `available`, `jeopardy` on the filter — liveness derives from
221
+ the interval and the measure
222
+ - `orderBy`/`limit`/`offset` on the filter — paging applies to result groups
223
+ - `entity` together with `role`/`roles` — one scoping mechanism at a time
224
+ - an entity key no role declares — the response names the configuration gap
225
+ - `groupBy.state` together with `states[]` — one labeling mechanism
226
+ - a window wider than `LT_ANALYTICS_MAX_WINDOW_DAYS`, a future `asOf`,
227
+ malformed facet keys, unknown statuses
228
+
229
+ Result groups are capped (`LT_ANALYTICS_MAX_GROUPS`); when more exist the
230
+ response carries `overflow: true`.
231
+
232
+ ## Access
233
+
234
+ Aggregates and timelines require `read_all` on every role in scope —
235
+ entity-derived roles included. A query with no role scope spans every queue
236
+ and requires a global principal. With the public Pace Board flag on,
237
+ counts-only aggregates (no facet keys among the group keys) are readable by
238
+ any login — the same data class the station metrics expose; facet-keyed
239
+ groupings and timelines always take the full gate. See [iam.md](iam.md).
240
+
241
+ ## Reference
242
+
243
+ - HTTP: [api/http/escalations.md](api/http/escalations.md)
244
+ - SDK: `client.escalations.aggregateByFacets` / `timelineByFacet`
245
+ - MCP: `aggregate_by_facets` / `timeline_by_facet` on the admin server
246
+ - CLI: `ltc esc aggregate-facets`, `ltc esc timeline` — [cli.md](cli.md)
247
+ - Config: `LT_ANALYTICS_MAX_GROUPS`, `LT_ANALYTICS_MAX_WINDOW_DAYS`,
248
+ `LT_ANALYTICS_CACHE_TTL_MS`, `LT_ANALYTICS_PAST_CACHE_TTL_MS`,
249
+ `LT_ANALYTICS_CACHE_MAX_ENTRIES`
250
+
251
+ To watch the seeded fleet move live: `npm run printers:cycle`.
@@ -57,7 +57,7 @@ const decision = await conditionLT<{ confirmed: boolean; address: string }>(sign
57
57
 
58
58
  ## Escalation Chains
59
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:
60
+ Chains are role-to-role routing edges, configured via the roles API (`POST /api/roles/escalation-chains`). Each role lists which other roles it can route work to, and the edges scope the programmatic re-routing surfaces — the `escalate_and_wait` MCP tool and the escalate APIs (HTTP, SDK, MCP, CLI). Common shape:
61
61
 
62
62
  ```
63
63
  Analyst → Senior Analyst → Manager → VP
@@ -23,7 +23,6 @@ to render in their place:
23
23
  | `claim` | Claim | The claim button (unclaimed). |
24
24
  | `cancel` | Cancel / Cancel escalation | The cancel control, both states. |
25
25
  | `submit` | Submit / Acknowledge | The resolve button (claimed). |
26
- | `escalate` | Escalate | The escalate tab and button. |
27
26
  | `release` | Release | The release tab. |
28
27
 
29
28
  ```jsonc
@@ -34,7 +33,7 @@ to render in their place:
34
33
  }
35
34
  ```
36
35
 
37
- Only the five known targets are read; unknown keys and non-string values are
36
+ Only the known targets are read; unknown keys and non-string values are
38
37
  ignored. Any target the schema omits keeps its default, so a schema overrides
39
38
  just the controls it cares about. The pending, triage, and confirmation states
40
39
  ("Claiming…", "Send to Triage", "Yes, Release") keep their own copy.
@@ -61,12 +61,12 @@ When you author a HITL-backed workflow, the platform handles:
61
61
  - **Accessible forms** — generated controls carry label association, error announcements, and keyboard-correct locking (see [form.md](hitl/form.md#accessibility))
62
62
  - **Side panel** — help, AI analysis, metadata, context, and raw-record views beside the form
63
63
  - **Section state persistence** — collapsed sections remembered across navigation
64
- - **Escalation chains** — users can re-route work to other roles
64
+ - **Escalation chains** — role-to-role routing edges that scope programmatic re-routing (API, MCP, CLI)
65
65
  - **AI triage** — optional auto-resolution for common patterns
66
66
  - **Credential security** — password fields use ephemeral tokens, never stored in plain text
67
67
  - **Schema enforcement** — roles with `enforce_schema` validate every resolver payload server-side (dashboard, API, MCP, CLI alike) with the same pass the form runs, rejecting violations as a structured 422 (see [schema-enforcement.md](schema-enforcement.md))
68
68
  - **Telemetry** — trace IDs link escalations to OpenTelemetry traces
69
- - **Bulk operations** — bulk claim, assign, escalate, triage, and cancel for queue management
69
+ - **Bulk operations** — bulk claim, assign, triage, and cancel for queue management
70
70
  - **Cancellation** — cancel pending escalations from the API or dashboard
71
71
 
72
72
  You write the workflow and the schema. Everything else is provided.
@@ -124,7 +124,7 @@ Ordered as a learning path — each file adds one capability to the same form:
124
124
  | `x-lt-viewport` | schema | Replace the generated form with a custom iframe UI |
125
125
  | `x-lt-submit-guard` | schema | Block the resolve while an embedded escalation query returns rows; `autoResolveWhenEmpty` auto-closes the claimed parent once it drains. Enforced isomorphically for `enforce_schema` roles |
126
126
  | `x-lt-submit-on-claim` | schema | Claiming also resolves the form's seeded defaults in one gesture |
127
- | `x-lt-labels` | schema | Per-target labels for the footer controls (`claim`, `cancel`, `submit`, `escalate`, `release`) |
127
+ | `x-lt-labels` | schema | Per-target labels for the footer controls (`claim`, `cancel`, `submit`, `release`) |
128
128
  | `x-lt-transition` | schema | Pause on a wait screen after resolve and hand off to the born-assigned follow-on |
129
129
  | `x-lt-columns` | schema (list) | Column definitions for `facet-table` layout |
130
130
  | `x-lt-row-action` | schema (list) | Per-row list action: `claim` / `view`, or `submitOnClaim` to claim, submit, and transition straight from the list |
package/docs/iam.md CHANGED
@@ -92,6 +92,12 @@ HTTP surface: `GET/POST /api/personas`, `GET/PATCH/DELETE /api/personas/:key`, `
92
92
 
93
93
  Scope folds into the SQL, so it is atomic with no TOCTOU window. Read scope becomes part of the escalation search query — a member sees a row when `role ∈ allRoles OR (role ∈ selfRoles AND assigned_to = me)`. Write scope folds into the atomic resolve-by-metadata query and gates the by-id claim/resolve/cancel paths. `assigned_to` is indexed, so `self` scope scales to large queues.
94
94
 
95
+ ### Analytics gate
96
+
97
+ The escalation analytics surfaces (`aggregate-by-facets`, `timeline-by-facet`) are reads that aggregate other operators' items, and they may span roles — so the gate is `read_all` on **every** role in the query's scope. When the filter uses `entity`, the scope is the derived system: every role declaring that entity facet. A query with no role scope at all is inherently cross-role and requires a global principal (superadmin/admin).
98
+
99
+ While the `features.publicPaceBoard` flag stands (default on), counts-only aggregates — groupings with no `groupBy.facets` keys — are readable by any login: they emit counts and seconds, the same data class station metrics already expose to everyone. Facet-keyed groupings emit facet values (entity ids) as group keys, and a timeline is one entity's movement history — item-level disclosure — so both always take the full `read_all`-per-role gate.
100
+
95
101
  ## Workflow Types and IAM
96
102
 
97
103
  Long Tail has three workflow types (see the [Workflows Guide](workflows.md#three-workflow-types) for full details). IAM applies to all three:
@@ -239,6 +245,12 @@ If a token is expired or exhausted, the opaque string passes through unchanged.
239
245
 
240
246
  When an activity calls `getCredential('anthropic')` and no credential exists in the cascade (principal → initiator → env var), `MissingCredentialError` is thrown. The interceptor catches this and creates a credential-focused escalation with `category: 'missing_credential'`. The escalation form can include a password field so the human provides the credential ephemerally, without it being stored permanently.
241
247
 
248
+ ### Acting identity (badge grants)
249
+
250
+ The same keystore carries **acting-identity grants** for shared station devices. A device signs in once as a station account — `read_scope: 'all'`, `write_scope: 'none'` on the queues it fronts — so reads are free and every mutation is structurally blocked for the account itself. A badge scan under an identity-kind scan scheme resolves the badge token against a server-side binding (`lt_users.metadata`, e.g. `badge_id`) and mints `eph:v1:acting_identity:<uuid>` under the scheme's TTL/max-uses policy.
251
+
252
+ The grant rides scan requests as `actingToken` and is exchanged server-side before anything reads or writes: the verbs run **as the badged person under their own live RBAC**. The grant confers attribution, never privilege — a badged user without write scope on the queue is still denied. Mutations attribute to the person (`assigned_to`, `resolved_by`) with the device recorded beside them (`scanStation` provenance). A dead grant is a loud `not_primed` outcome, never a silent execution as the device. See [scan-codes.md](scan-codes.md#identity-schemes-and-acting-identity).
253
+
242
254
  ## Dashboard
243
255
 
244
256
  The dashboard surfaces IAM across four pages:
@@ -90,6 +90,23 @@ A 340 px right rail that slides open when a row or chart circle is clicked. Thre
90
90
 
91
91
  Close the panel with × or by clicking another row.
92
92
 
93
+ ## Entity lens
94
+
95
+ The board carries one view selector. When any station declares an entity facet, a lens strip appears above the chart: **Stations** (the default station-first board) plus one lens per entity facet the visible roles declare, labeled `by <key>` (e.g. `by serialNumber`). The active lens is deep-linked (`?lens=<key>`). Selecting a lens flips the page from station-first to entity-first: roles sharing the facet form the entity's **system**, and each role contributes states per its `entity_state_source`. Three tiers, aggregate → individual:
96
+
97
+ - **The system band** — how the fleet's time splits across states over the selected period: one stacked dwell band with a per-state legend (duration, percentage, and a `· N now` membership count where entities currently sit), headlined by the distinct-entity count in queue now (e.g. `6 serialNumber in queue now`).
98
+ - **Slice by** — the same band per value of any metadata key, chosen from a select of the known facet keys (e.g. `model` splits the band into `p1s` vs `h2s`). Small-multiple bands, ranked by total dwell, top 8 values.
99
+ - **The entity table** — one row per entity, ranked by tracked time: the entity value, its current state, its own dwell band, its total tracked time, and a timeline action that opens the entity's cross-queue interval timeline in the right panel.
100
+
101
+ The lens is driven by two dials on the role's Pace Board section ([Role Detail](dashboard.md#role-detail)):
102
+
103
+ | Dial | Description |
104
+ |------|-------------|
105
+ | `entity_facet` | The `lt_escalations.metadata` key naming the entity that moves through the role (e.g. `serialNumber`, `orderId`). Roles sharing a key form that entity's system. Blank = the role has no entity notion. |
106
+ | `entity_state_source` | How the role names the entity's state: **Station** (`'role'` — being in this role is one state, e.g. a servicing queue) or **Subtypes** (`'subtype'` — the one role holds several states named by each escalation's subtype, e.g. a fleet role parking `idle` / `printing`). Default `'role'`. |
107
+
108
+ The band tiers are counts-only aggregates, readable by any login while the public board flag stands; the slice and per-entity tiers group by facet values and require full (`read_all`) access to the system's queues. Data comes from `POST /api/escalations/aggregate-by-facets` and `POST /api/escalations/timeline-by-facet` — see [escalation-analytics.md](escalation-analytics.md) for the query contract.
109
+
93
110
  ## Data source
94
111
 
95
112
  All station metrics come from `GET /api/escalations/station-metrics?period=<period>`. The endpoint runs two queries against `public.hmsh_escalations` joined to `lt_roles` (for the priority dials and `target_per_hour`): a live-counts pass over the pending backlog, and a window-bounded percentile pass over resolved rows (`PERCENTILE_CONT` in Postgres, served by the `idx_hmsh_esc_resolved_cover` index). Updates reach the page as push events — every escalation write invalidates the metrics through the Socket.IO event system.
@@ -22,6 +22,9 @@ canonical action surface. A scan is an **ECA rule** over that surface:
22
22
  - [Schemes](#schemes)
23
23
  - [Rules and steps](#rules-and-steps)
24
24
  - [Confirmation](#confirmation)
25
+ - [Info-choice screens](#info-choice-screens)
26
+ - [Identity schemes and acting identity](#identity-schemes-and-acting-identity)
27
+ - [Station deployments](#station-deployments)
25
28
  - [The fallback screen](#the-fallback-screen)
26
29
  - [Executing a scan](#executing-a-scan)
27
30
  - [Capture on the dashboard](#capture-on-the-dashboard)
@@ -122,6 +125,115 @@ rule's prompt ("Cancel this printer's current state and send it home to
122
125
  servicing?"). Confirming fires the standard per-id endpoint — the same
123
126
  guarded call every other surface uses.
124
127
 
128
+ ## Info-choice screens
129
+
130
+ Some objects carry one code for their whole life — an item tag, an order
131
+ label. One code, many possible intents, and the right one depends on where
132
+ the object is in its journey. The `present` verb closes that gap: the step
133
+ locates the row, states its reality, and returns a configured, labeled
134
+ choice set for the human to pick from. Upper half — what is true; lower
135
+ half — what you may do about it.
136
+
137
+ ```jsonc
138
+ {
139
+ "query": { "roles": ["printer-fleet", "printer-harvest", "printer-service"] },
140
+ "verb": "present",
141
+ "choices": [
142
+ { "label": "Claim & Start", "verb": "claim", "requireActingIdentity": true, "code": "CLAIM" },
143
+ { "label": "Complete", "verb": "resolve", "requireActingIdentity": true,
144
+ "confirm": { "prompt": "Mark this item complete?" },
145
+ "params": { "resolverPayload": { "outcome": "complete" } } },
146
+ { "label": "View Details", "verb": "show-detail" }
147
+ ]
148
+ }
149
+ ```
150
+
151
+ The scan answers `choices` with the located escalation and the choice list;
152
+ each choice carries `withheld: true` when its identity requirement is
153
+ unsatisfied. Picking one calls `POST /api/scan-codes/execute-choice` with a
154
+ pointer (scheme, category, step index, choice index, escalation id) — and a
155
+ pointer is never authority: the server re-reads live config, re-locates the
156
+ row under the step's query, re-applies the identity gate, and runs the verb
157
+ through the same atomic executors a direct scan uses. A row that moved on
158
+ between render and tap answers `conflict`, exactly as a lost double-scan.
159
+
160
+ A choice's `code` is a short printable token (letters, digits, underscore,
161
+ dash) enabling double-scan selection: scan the object, then scan an action
162
+ card. The station screen matches the second scan against the presented
163
+ choices before treating it as a new code.
164
+
165
+ A claim choice lands on the work: after `claim` or `claim-show-detail`
166
+ executes, the station navigates to the escalation's detail page — the same
167
+ form every operator uses to resolve, reject, or conclude the item.
168
+
169
+ **One scan, one action.** A step with exactly one confirm-less choice can
170
+ set `autoSelectSingle: true`: the scan executes the choice directly instead
171
+ of presenting a one-button screen. The worker scans the item that just
172
+ arrived at their bench and it is theirs, claimed for the configured
173
+ duration, form on screen. An unsatisfied identity requirement never
174
+ auto-fires as the wrong actor — the scan stops over at the badge screen and
175
+ completes on its own once a badge primes.
176
+
177
+ ## Identity schemes and acting identity
178
+
179
+ A scheme with `kind: "identity"` is the badge layer. Its `target_facet`
180
+ names the `lt_users.metadata` key the scanned badge token matches (the demo
181
+ binds `badge_id`) — resolution is only ever that metadata equality, so a
182
+ printed username can never impersonate. Identity rules carry no steps;
183
+ their `fallback` is the unknown-badge screen.
184
+
185
+ A matching badge scan answers `identity_primed` with the person's display
186
+ name, an expiry, and an **acting grant** — an ephemeral token
187
+ (`eph:v1:acting_identity:…`) minted through the internal keystore under the
188
+ scheme's policy:
189
+
190
+ | Scheme field | Meaning |
191
+ |---|---|
192
+ | `grant_ttl_seconds` | How long the grant lives (1–86400). |
193
+ | `grant_max_uses` | `0` = TTL-bound; `n` = the grant covers n scan requests (a strict one-scan policy is `1`). |
194
+
195
+ The grant rides subsequent scans as `actingToken`. Verbs then run **as the
196
+ badged person under their own live RBAC** — the grant confers attribution,
197
+ never privilege. A dead grant (expired, exhausted, revoked) is a loud
198
+ `not_primed`, never a silent execution as the device. Scanning the next
199
+ badge replaces the session's grant; passing `previousActingToken` revokes
200
+ the outgoing one immediately.
201
+
202
+ Steps and choices opt in with `requireActingIdentity: true`: the effective
203
+ actor must be a real acting identity — a badge grant, or an authenticated
204
+ user whose own write scope covers the step. When unsatisfied, the outcome
205
+ is `not_primed` with the rule's `notPrimed` screen (a sibling of
206
+ `fallback`): the "scan your badge" message, never a silent or misattributed
207
+ action.
208
+
209
+ ## Station deployments
210
+
211
+ A shared floor device (tablet + tethered scanner) signs in once as a
212
+ station account — an ordinary user with `read_scope: 'all'` and
213
+ `write_scope: 'none'` on the queues it fronts. Reads are free: the station
214
+ holds a real seat with a queue view and an audit identity. Every mutation
215
+ takes the badge layer, and `write_scope: 'none'` is the structural
216
+ backstop — even a misconfigured rule cannot mutate as the station, because
217
+ the RBAC write gate denies the account itself.
218
+
219
+ Mutations attribute to the badged person (`assigned_to`, `resolved_by`)
220
+ with the device recorded beside them (`scanStation` in the scan
221
+ provenance) — every action individually owned and geographically placed.
222
+ The dashboard's `/scan/station` route is the full-screen experience: idle
223
+ ("scan your badge / scan an item"), the primed chrome with the grant
224
+ countdown, the info-choice screen, and the **badge stop-over** — the one
225
+ identity moment the flow ever surfaces. When an action needs a person and
226
+ none is primed (a withheld choice tapped, an auto-select scan, a grant that
227
+ lapsed mid-flow), the screen becomes a single clear prompt: scan your badge
228
+ to continue. The badge scan primes the session and the pending action
229
+ completes on its own. With a live grant the stop-over never appears — the
230
+ identity layer is invisible when all is well.
231
+
232
+ Badge tokens are printed credentials: seed them as long random strings,
233
+ bind them server-side (`lt_users.metadata`), and treat badge possession
234
+ with the same physical policy as any badge system. Unknown badges answer
235
+ `identity_unknown` and are auditable.
236
+
125
237
  ## The fallback screen
126
238
 
127
239
  When no step matches, the response carries the rule's `fallback`: markdown
package/docs/sdk.md CHANGED
@@ -63,7 +63,7 @@ The client mirrors the REST route structure:
63
63
  | Namespace | REST equivalent | Key operations |
64
64
  |-----------|----------------|----------------|
65
65
  | `lt.tasks` | `/api/tasks` | `list`, `get`, `listProcesses`, `getProcess`, `getProcessStats` |
66
- | `lt.escalations` | `/api/escalations` | `list`, `listAvailable`, `get`, `claim`, `release`, `resolve`, `escalate`, `bulkClaim`, `bulkAssign`, `findByMetadata`, `claimByMetadata`, `resolveByMetadata` |
66
+ | `lt.escalations` | `/api/escalations` | `list`, `listAvailable`, `get`, `claim`, `release`, `resolve`, `escalate`, `bulkClaim`, `bulkAssign`, `findByMetadata`, `claimByMetadata`, `resolveByMetadata`, `aggregateByFacets`, `timelineByFacet` — see the [Escalations SDK reference](api/sdk/escalations.md) |
67
67
  | `lt.workflows` | `/api/workflows` | `invoke`, `getStatus`, `getResult`, `terminate`, `listWorkers`, `listDiscovered`, `listConfigs`, `getConfig`, `upsertConfig` |
68
68
  | `lt.yamlWorkflows` | `/api/yaml-workflows` | `list`, `get`, `create`, `deploy`, `activate`, `invoke`, `archive` |
69
69
  | `lt.users` | `/api/users` | `list`, `get`, `create`, `update`, `delete`, `getRoles`, `addRole`, `removeRole` |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hotmeshio/long-tail",
3
- "version": "0.10.14",
3
+ "version": "0.11.1",
4
4
  "description": "Long Tail Workflows — Durable AI workflows with human-in-the-loop escalation. Powered by PostgreSQL.",
5
5
  "main": "./build/index.js",
6
6
  "types": "./build/index.d.ts",
@@ -45,6 +45,7 @@
45
45
  "demo": "bash scripts/demo.sh",
46
46
  "ortho:populate": "ts-node scripts/ortho-populate.ts",
47
47
  "twin:farm": "ts-node scripts/twin-farm.ts",
48
+ "printers:cycle": "ts-node scripts/printer-cycle.ts",
48
49
  "dashboard:install": "cd dashboard && npm install",
49
50
  "dashboard:dev": "cd dashboard && npm run dev",
50
51
  "dashboard:build": "cd dashboard && npm run build",
@@ -1,10 +0,0 @@
1
- /**
2
- * Ortho pipeline MCP tools — AI-operable interface for the 8-stage manufacturing workflow.
3
- *
4
- * ortho_submit — start a new order through the orthoPipeline workflow
5
- * ortho_pending — list open stage escalations waiting for completion
6
- * ortho_complete_stage — claim and resolve one stage escalation (advances the pipeline)
7
- * ortho_status — get the current status and results for an order workflow
8
- */
9
- import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
10
- export declare function registerOrthoTools(server: McpServer): void;