@hotmeshio/long-tail 0.21.2 → 0.22.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 (268) hide show
  1. package/build/api/escalations/accumulate.d.ts +10 -1
  2. package/build/api/escalations/accumulate.js +83 -15
  3. package/build/api/escalations/metadata.d.ts +5 -0
  4. package/build/api/escalations/metadata.js +6 -2
  5. package/build/api/scan-codes/choice.js +3 -2
  6. package/build/api/scan-codes/verbs.d.ts +6 -1
  7. package/build/api/scan-codes/verbs.js +13 -3
  8. package/build/api/workflows/invocation.d.ts +8 -4
  9. package/build/api/workflows/invocation.js +26 -9
  10. package/build/lib/cli/commands/workflows.js +9 -4
  11. package/build/lib/db/index.d.ts +8 -3
  12. package/build/lib/db/index.js +24 -2
  13. package/build/routes/escalations/accumulate.js +13 -6
  14. package/build/sdk/index.d.ts +3 -0
  15. package/build/services/escalation/crud.d.ts +2 -2
  16. package/build/services/escalation/crud.js +4 -2
  17. package/build/services/escalation/facet-sql.js +8 -0
  18. package/build/services/escalation/facets.js +4 -1
  19. package/build/services/escalation/sql.d.ts +4 -2
  20. package/build/services/escalation/sql.js +10 -2
  21. package/build/services/export/index.d.ts +8 -3
  22. package/build/services/export/index.js +15 -21
  23. package/build/services/pipelines/events.js +2 -1
  24. package/build/services/pipelines/execution-builder.js +2 -1
  25. package/build/services/pipelines/queries.js +2 -1
  26. package/build/services/scan-code/validate.js +19 -0
  27. package/build/services/workflow-state.d.ts +21 -0
  28. package/build/services/workflow-state.js +39 -0
  29. package/build/shared/workflow-state.d.ts +17 -0
  30. package/build/shared/workflow-state.js +29 -0
  31. package/build/system/mcp-servers/admin/escalations.js +8 -4
  32. package/build/system/mcp-servers/admin/schemas.d.ts +40 -2
  33. package/build/system/mcp-servers/admin/schemas.js +4 -0
  34. package/build/system/mcp-servers/admin/workflows.js +20 -31
  35. package/build/system/seed/tool-manifests-admin.d.ts +94 -0
  36. package/build/system/seed/tool-manifests-admin.js +3 -3
  37. package/build/tsconfig.tsbuildinfo +1 -1
  38. package/build/types/facets.d.ts +14 -0
  39. package/build/types/index.d.ts +1 -1
  40. package/build/types/scan-code.d.ts +10 -1
  41. package/build/types/scan-code.js +0 -11
  42. package/dashboard/dist/assets/{AdminDashboard-Covdam48.js → AdminDashboard-BbWve-Ax.js} +2 -2
  43. package/dashboard/dist/assets/{AdminDashboard-Covdam48.js.map → AdminDashboard-BbWve-Ax.js.map} +1 -1
  44. package/dashboard/dist/assets/{AgentConfigPage-CE1k6J5r.js → AgentConfigPage-M-9eTKhP.js} +2 -2
  45. package/dashboard/dist/assets/{AgentConfigPage-CE1k6J5r.js.map → AgentConfigPage-M-9eTKhP.js.map} +1 -1
  46. package/dashboard/dist/assets/{AgentDetailPage-CWJZIZP1.js → AgentDetailPage-CsakSlx5.js} +3 -3
  47. package/dashboard/dist/assets/{AgentDetailPage-CWJZIZP1.js.map → AgentDetailPage-CsakSlx5.js.map} +1 -1
  48. package/dashboard/dist/assets/{AgentsPage---Dg5ufF.js → AgentsPage-Cj_cRTT1.js} +2 -2
  49. package/dashboard/dist/assets/{AgentsPage---Dg5ufF.js.map → AgentsPage-Cj_cRTT1.js.map} +1 -1
  50. package/dashboard/dist/assets/AvailableEscalationsPage-BXJmMBM1.js +2 -0
  51. package/dashboard/dist/assets/AvailableEscalationsPage-BXJmMBM1.js.map +1 -0
  52. package/dashboard/dist/assets/{BotPicker-D3drO3-T.js → BotPicker-CGB16rYU.js} +2 -2
  53. package/dashboard/dist/assets/{BotPicker-D3drO3-T.js.map → BotPicker-CGB16rYU.js.map} +1 -1
  54. package/dashboard/dist/assets/{BulkAssignModal-BOAWsViq.js → BulkAssignModal-MjxcU7N1.js} +2 -2
  55. package/dashboard/dist/assets/{BulkAssignModal-BOAWsViq.js.map → BulkAssignModal-MjxcU7N1.js.map} +1 -1
  56. package/dashboard/dist/assets/{CapabilitiesPage-RGgD3YZO.js → CapabilitiesPage-BxadotXn.js} +2 -2
  57. package/dashboard/dist/assets/{CapabilitiesPage-RGgD3YZO.js.map → CapabilitiesPage-BxadotXn.js.map} +1 -1
  58. package/dashboard/dist/assets/{CollapsibleSection-DNkZn87n.js → CollapsibleSection-BBNpP-6h.js} +2 -2
  59. package/dashboard/dist/assets/{CollapsibleSection-DNkZn87n.js.map → CollapsibleSection-BBNpP-6h.js.map} +1 -1
  60. package/dashboard/dist/assets/{ConfirmDeleteModal-BqzOwhPr.js → ConfirmDeleteModal-D_kAbT_D.js} +2 -2
  61. package/dashboard/dist/assets/{ConfirmDeleteModal-BqzOwhPr.js.map → ConfirmDeleteModal-D_kAbT_D.js.map} +1 -1
  62. package/dashboard/dist/assets/{CountdownTimer-BaexQMK6.js → CountdownTimer-CI_JOAvk.js} +2 -2
  63. package/dashboard/dist/assets/{CountdownTimer-BaexQMK6.js.map → CountdownTimer-CI_JOAvk.js.map} +1 -1
  64. package/dashboard/dist/assets/{CredentialsPage-D7abp0aS.js → CredentialsPage-C9OA1Q2J.js} +2 -2
  65. package/dashboard/dist/assets/{CredentialsPage-D7abp0aS.js.map → CredentialsPage-C9OA1Q2J.js.map} +1 -1
  66. package/dashboard/dist/assets/{CronLabel-DZiWmTwJ.js → CronLabel-sxLLBf2I.js} +2 -2
  67. package/dashboard/dist/assets/{CronLabel-DZiWmTwJ.js.map → CronLabel-sxLLBf2I.js.map} +1 -1
  68. package/dashboard/dist/assets/{DropZone-f1o-GLM8.js → DropZone-B1Tdfddc.js} +2 -2
  69. package/dashboard/dist/assets/{DropZone-f1o-GLM8.js.map → DropZone-B1Tdfddc.js.map} +1 -1
  70. package/dashboard/dist/assets/{ElapsedCell-BdGO_w4v.js → ElapsedCell-jH9m8Dgl.js} +2 -2
  71. package/dashboard/dist/assets/{ElapsedCell-BdGO_w4v.js.map → ElapsedCell-jH9m8Dgl.js.map} +1 -1
  72. package/dashboard/dist/assets/{EntityTimelinePanel-DIDcF5Yu.js → EntityTimelinePanel-Dl3Nr2kk.js} +2 -2
  73. package/dashboard/dist/assets/{EntityTimelinePanel-DIDcF5Yu.js.map → EntityTimelinePanel-Dl3Nr2kk.js.map} +1 -1
  74. package/dashboard/dist/assets/{EscalationListSchemaPage-C28rn9XC.js → EscalationListSchemaPage-DAK6It5J.js} +3 -3
  75. package/dashboard/dist/assets/{EscalationListSchemaPage-C28rn9XC.js.map → EscalationListSchemaPage-DAK6It5J.js.map} +1 -1
  76. package/dashboard/dist/assets/{EscalationListView-CkBnkpDJ.js → EscalationListView-CAHbdxKR.js} +2 -2
  77. package/dashboard/dist/assets/{EscalationListView-CkBnkpDJ.js.map → EscalationListView-CAHbdxKR.js.map} +1 -1
  78. package/dashboard/dist/assets/EscalationSchemaPage-DotfAMCL.js +9 -0
  79. package/dashboard/dist/assets/EscalationSchemaPage-DotfAMCL.js.map +1 -0
  80. package/dashboard/dist/assets/EscalationTimeline-DqmFydnU.js +2 -0
  81. package/dashboard/dist/assets/EscalationTimeline-DqmFydnU.js.map +1 -0
  82. package/dashboard/dist/assets/{EscalationTitleSelect-CzKfavri.js → EscalationTitleSelect-BpRquguO.js} +2 -2
  83. package/dashboard/dist/assets/{EscalationTitleSelect-CzKfavri.js.map → EscalationTitleSelect-BpRquguO.js.map} +1 -1
  84. package/dashboard/dist/assets/{EscalationsOverview-Tdu3Yx2c.js → EscalationsOverview-DtGOqnP4.js} +2 -2
  85. package/dashboard/dist/assets/{EscalationsOverview-Tdu3Yx2c.js.map → EscalationsOverview-DtGOqnP4.js.map} +1 -1
  86. package/dashboard/dist/assets/{EventTable-DiIxOJwO.js → EventTable-DSHqKyX2.js} +2 -2
  87. package/dashboard/dist/assets/{EventTable-DiIxOJwO.js.map → EventTable-DSHqKyX2.js.map} +1 -1
  88. package/dashboard/dist/assets/{FilterBar-CvMBCVIo.js → FilterBar-Dxd0wNSW.js} +2 -2
  89. package/dashboard/dist/assets/{FilterBar-CvMBCVIo.js.map → FilterBar-Dxd0wNSW.js.map} +1 -1
  90. package/dashboard/dist/assets/{GraphInvokePage-DO-yyhi6.js → GraphInvokePage-ChqqGkro.js} +2 -2
  91. package/dashboard/dist/assets/{GraphInvokePage-DO-yyhi6.js.map → GraphInvokePage-ChqqGkro.js.map} +1 -1
  92. package/dashboard/dist/assets/{HomePage-DSS3eHR2.js → HomePage-ByKesG8w.js} +2 -2
  93. package/dashboard/dist/assets/{HomePage-DSS3eHR2.js.map → HomePage-ByKesG8w.js.map} +1 -1
  94. package/dashboard/dist/assets/{ListToolbar-C3-Tqqub.js → ListToolbar-Ddz_S2av.js} +2 -2
  95. package/dashboard/dist/assets/{ListToolbar-C3-Tqqub.js.map → ListToolbar-Ddz_S2av.js.map} +1 -1
  96. package/dashboard/dist/assets/{McpOverview-DEJmsdVi.js → McpOverview-CVe13tEv.js} +2 -2
  97. package/dashboard/dist/assets/{McpOverview-DEJmsdVi.js.map → McpOverview-CVe13tEv.js.map} +1 -1
  98. package/dashboard/dist/assets/McpQueryDetailPage-BQYF2Mjq.js +5 -0
  99. package/dashboard/dist/assets/McpQueryDetailPage-BQYF2Mjq.js.map +1 -0
  100. package/dashboard/dist/assets/{McpQueryPage-BBcG_UuU.js → McpQueryPage-DZi5lm7s.js} +2 -2
  101. package/dashboard/dist/assets/{McpQueryPage-BBcG_UuU.js.map → McpQueryPage-DZi5lm7s.js.map} +1 -1
  102. package/dashboard/dist/assets/{McpRunDetailPage-C6xw0kBg.js → McpRunDetailPage-DreARTE1.js} +2 -2
  103. package/dashboard/dist/assets/{McpRunDetailPage-C6xw0kBg.js.map → McpRunDetailPage-DreARTE1.js.map} +1 -1
  104. package/dashboard/dist/assets/{McpRunsPage-CRQqrF4Q.js → McpRunsPage-DmO0YVhT.js} +2 -2
  105. package/dashboard/dist/assets/{McpRunsPage-CRQqrF4Q.js.map → McpRunsPage-DmO0YVhT.js.map} +1 -1
  106. package/dashboard/dist/assets/{NamespacePill-DADRJOUl.js → NamespacePill-DwM5pNA4.js} +2 -2
  107. package/dashboard/dist/assets/{NamespacePill-DADRJOUl.js.map → NamespacePill-DwM5pNA4.js.map} +1 -1
  108. package/dashboard/dist/assets/{OperationsPage-Dlfwzf-0.js → OperationsPage-DpjCO85T.js} +2 -2
  109. package/dashboard/dist/assets/{OperationsPage-Dlfwzf-0.js.map → OperationsPage-DpjCO85T.js.map} +1 -1
  110. package/dashboard/dist/assets/{OperatorDashboard-DoiDGZ9R.js → OperatorDashboard-Vv_MEBkv.js} +2 -2
  111. package/dashboard/dist/assets/{OperatorDashboard-DoiDGZ9R.js.map → OperatorDashboard-Vv_MEBkv.js.map} +1 -1
  112. package/dashboard/dist/assets/{PageHeader-Csq434-v.js → PageHeader-CMd3f4bz.js} +2 -2
  113. package/dashboard/dist/assets/{PageHeader-Csq434-v.js.map → PageHeader-CMd3f4bz.js.map} +1 -1
  114. package/dashboard/dist/assets/{PageHeaderWithStats-CSnk1p5A.js → PageHeaderWithStats-DHj8Mqox.js} +2 -2
  115. package/dashboard/dist/assets/{PageHeaderWithStats-CSnk1p5A.js.map → PageHeaderWithStats-DHj8Mqox.js.map} +1 -1
  116. package/dashboard/dist/assets/{PersonaDetailPage-BzbA4Prr.js → PersonaDetailPage-CW-2uBHY.js} +2 -2
  117. package/dashboard/dist/assets/{PersonaDetailPage-BzbA4Prr.js.map → PersonaDetailPage-CW-2uBHY.js.map} +1 -1
  118. package/dashboard/dist/assets/{PersonasPage-B36yn4hU.js → PersonasPage-CO6yYQdm.js} +2 -2
  119. package/dashboard/dist/assets/{PersonasPage-B36yn4hU.js.map → PersonasPage-CO6yYQdm.js.map} +1 -1
  120. package/dashboard/dist/assets/{PortalPage-BkD4YfDf.js → PortalPage-DcNqhBlg.js} +2 -2
  121. package/dashboard/dist/assets/{PortalPage-BkD4YfDf.js.map → PortalPage-DcNqhBlg.js.map} +1 -1
  122. package/dashboard/dist/assets/{ProcessDetailPage-BT0eiBWY.js → ProcessDetailPage-BuipGZcC.js} +2 -2
  123. package/dashboard/dist/assets/{ProcessDetailPage-BT0eiBWY.js.map → ProcessDetailPage-BuipGZcC.js.map} +1 -1
  124. package/dashboard/dist/assets/{ProcessesListPage-BEqeeNae.js → ProcessesListPage-D9DYntvl.js} +2 -2
  125. package/dashboard/dist/assets/{ProcessesListPage-BEqeeNae.js.map → ProcessesListPage-D9DYntvl.js.map} +1 -1
  126. package/dashboard/dist/assets/ResolverForm-BVG8X0sS.js +8 -0
  127. package/dashboard/dist/assets/ResolverForm-BVG8X0sS.js.map +1 -0
  128. package/dashboard/dist/assets/{RoleDetailPage-BFuLNJXa.js → RoleDetailPage-KCXDqoJz.js} +3 -3
  129. package/dashboard/dist/assets/{RoleDetailPage-BFuLNJXa.js.map → RoleDetailPage-KCXDqoJz.js.map} +1 -1
  130. package/dashboard/dist/assets/{RolePill-BafrASDX.js → RolePill-Di2CAHuD.js} +2 -2
  131. package/dashboard/dist/assets/{RolePill-BafrASDX.js.map → RolePill-Di2CAHuD.js.map} +1 -1
  132. package/dashboard/dist/assets/{RolesPage-80quJU5-.js → RolesPage-0QJOBSzF.js} +2 -2
  133. package/dashboard/dist/assets/{RolesPage-80quJU5-.js.map → RolesPage-0QJOBSzF.js.map} +1 -1
  134. package/dashboard/dist/assets/{RunAsSelector-BoePhWVD.js → RunAsSelector-Dh4lyyRo.js} +2 -2
  135. package/dashboard/dist/assets/{RunAsSelector-BoePhWVD.js.map → RunAsSelector-Dh4lyyRo.js.map} +1 -1
  136. package/dashboard/dist/assets/{ScanCodesPage-Cu1bKEix.js → ScanCodesPage-CUhCz-Yr.js} +2 -2
  137. package/dashboard/dist/assets/{ScanCodesPage-Cu1bKEix.js.map → ScanCodesPage-CUhCz-Yr.js.map} +1 -1
  138. package/dashboard/dist/assets/{ScanSchemeDetailPage-D9AbS6UY.js → ScanSchemeDetailPage-qzXKNqJK.js} +2 -2
  139. package/dashboard/dist/assets/{ScanSchemeDetailPage-D9AbS6UY.js.map → ScanSchemeDetailPage-qzXKNqJK.js.map} +1 -1
  140. package/dashboard/dist/assets/{ScanStationPage-gcZGefvT.js → ScanStationPage-D8e8cG3t.js} +2 -2
  141. package/dashboard/dist/assets/{ScanStationPage-gcZGefvT.js.map → ScanStationPage-D8e8cG3t.js.map} +1 -1
  142. package/dashboard/dist/assets/{ScopeBadge-b6MIBiYD.js → ScopeBadge-CSItPVOH.js} +2 -2
  143. package/dashboard/dist/assets/{ScopeBadge-b6MIBiYD.js.map → ScopeBadge-CSItPVOH.js.map} +1 -1
  144. package/dashboard/dist/assets/{StickyPagination-9ITrei_d.js → StickyPagination-BSUyf5ja.js} +2 -2
  145. package/dashboard/dist/assets/{StickyPagination-9ITrei_d.js.map → StickyPagination-BSUyf5ja.js.map} +1 -1
  146. package/dashboard/dist/assets/{StreamMessageDetail-BkYvEHFY.js → StreamMessageDetail--tp7l2Sd.js} +2 -2
  147. package/dashboard/dist/assets/{StreamMessageDetail-BkYvEHFY.js.map → StreamMessageDetail--tp7l2Sd.js.map} +1 -1
  148. package/dashboard/dist/assets/{SwimlaneTimeline-B1rotFix.js → SwimlaneTimeline-CxBZ2THh.js} +2 -2
  149. package/dashboard/dist/assets/{SwimlaneTimeline-B1rotFix.js.map → SwimlaneTimeline-CxBZ2THh.js.map} +1 -1
  150. package/dashboard/dist/assets/{TagInput-ZrIIrEUP.js → TagInput-CAgdLN_b.js} +2 -2
  151. package/dashboard/dist/assets/{TagInput-ZrIIrEUP.js.map → TagInput-CAgdLN_b.js.map} +1 -1
  152. package/dashboard/dist/assets/{TaskDetailPage-zhn_U_eC.js → TaskDetailPage-BJ1QrmPH.js} +2 -2
  153. package/dashboard/dist/assets/{TaskDetailPage-zhn_U_eC.js.map → TaskDetailPage-BJ1QrmPH.js.map} +1 -1
  154. package/dashboard/dist/assets/{TaskQueuePill-DkQ1uc6y.js → TaskQueuePill-h0oorVMe.js} +2 -2
  155. package/dashboard/dist/assets/{TaskQueuePill-DkQ1uc6y.js.map → TaskQueuePill-h0oorVMe.js.map} +1 -1
  156. package/dashboard/dist/assets/{TasksListPage-CNbNeagx.js → TasksListPage-5BydGWmt.js} +2 -2
  157. package/dashboard/dist/assets/{TasksListPage-CNbNeagx.js.map → TasksListPage-5BydGWmt.js.map} +1 -1
  158. package/dashboard/dist/assets/{TimeAgo-0YVMBwls.js → TimeAgo-CCnz84Y4.js} +2 -2
  159. package/dashboard/dist/assets/{TimeAgo-0YVMBwls.js.map → TimeAgo-CCnz84Y4.js.map} +1 -1
  160. package/dashboard/dist/assets/{TimestampCell-DB7r3np8.js → TimestampCell-D0ru7Lya.js} +2 -2
  161. package/dashboard/dist/assets/{TimestampCell-DB7r3np8.js.map → TimestampCell-D0ru7Lya.js.map} +1 -1
  162. package/dashboard/dist/assets/{ToolPill-Baxm02L6.js → ToolPill-CQCsVdzD.js} +2 -2
  163. package/dashboard/dist/assets/{ToolPill-Baxm02L6.js.map → ToolPill-CQCsVdzD.js.map} +1 -1
  164. package/dashboard/dist/assets/{ToolTestPanel-BLfA02wv.js → ToolTestPanel-3Kpofmen.js} +2 -2
  165. package/dashboard/dist/assets/{ToolTestPanel-BLfA02wv.js.map → ToolTestPanel-3Kpofmen.js.map} +1 -1
  166. package/dashboard/dist/assets/{TopicDetailPage-eAqPKShq.js → TopicDetailPage-BaBXD_AK.js} +4 -4
  167. package/dashboard/dist/assets/{TopicDetailPage-eAqPKShq.js.map → TopicDetailPage-BaBXD_AK.js.map} +1 -1
  168. package/dashboard/dist/assets/{TopicsPage-BNNWD-zl.js → TopicsPage-Cnm1AAjh.js} +2 -2
  169. package/dashboard/dist/assets/{TopicsPage-BNNWD-zl.js.map → TopicsPage-Cnm1AAjh.js.map} +1 -1
  170. package/dashboard/dist/assets/{UserName-Pw46tO_k.js → UserName-CXsdPiMZ.js} +2 -2
  171. package/dashboard/dist/assets/{UserName-Pw46tO_k.js.map → UserName-CXsdPiMZ.js.map} +1 -1
  172. package/dashboard/dist/assets/{WorkflowExecutionPage-DvA51lT3.js → WorkflowExecutionPage-zx3AUJFd.js} +2 -2
  173. package/dashboard/dist/assets/{WorkflowExecutionPage-DvA51lT3.js.map → WorkflowExecutionPage-zx3AUJFd.js.map} +1 -1
  174. package/dashboard/dist/assets/{WorkflowPill-ZepbO_ny.js → WorkflowPill-Dg7BDTyM.js} +2 -2
  175. package/dashboard/dist/assets/{WorkflowPill-ZepbO_ny.js.map → WorkflowPill-Dg7BDTyM.js.map} +1 -1
  176. package/dashboard/dist/assets/{WorkflowsDashboard-DFDTwnhn.js → WorkflowsDashboard-DfAGK1l7.js} +2 -2
  177. package/dashboard/dist/assets/{WorkflowsDashboard-DFDTwnhn.js.map → WorkflowsDashboard-DfAGK1l7.js.map} +1 -1
  178. package/dashboard/dist/assets/{WorkflowsOverview-5i2Tcy-p.js → WorkflowsOverview-QIYvoa13.js} +2 -2
  179. package/dashboard/dist/assets/{WorkflowsOverview-5i2Tcy-p.js.map → WorkflowsOverview-QIYvoa13.js.map} +1 -1
  180. package/dashboard/dist/assets/{YamlWorkflowDetailPage-p4VEYZS0.js → YamlWorkflowDetailPage-DGgO3hyB.js} +2 -2
  181. package/dashboard/dist/assets/{YamlWorkflowDetailPage-p4VEYZS0.js.map → YamlWorkflowDetailPage-DGgO3hyB.js.map} +1 -1
  182. package/dashboard/dist/assets/{YamlWorkflowsPage-Cq-hMVxl.js → YamlWorkflowsPage-CEsHm0B9.js} +2 -2
  183. package/dashboard/dist/assets/{YamlWorkflowsPage-Cq-hMVxl.js.map → YamlWorkflowsPage-CEsHm0B9.js.map} +1 -1
  184. package/dashboard/dist/assets/{agents-EyBPJMYa.js → agents-v_HH3Rji.js} +2 -2
  185. package/dashboard/dist/assets/{agents-EyBPJMYa.js.map → agents-v_HH3Rji.js.map} +1 -1
  186. package/dashboard/dist/assets/{bots-DX6jjqtF.js → bots-bwvWYrXz.js} +2 -2
  187. package/dashboard/dist/assets/{bots-DX6jjqtF.js.map → bots-bwvWYrXz.js.map} +1 -1
  188. package/dashboard/dist/assets/{capabilities-ZdLv9xBQ.js → capabilities-D4vaw_nQ.js} +2 -2
  189. package/dashboard/dist/assets/{capabilities-ZdLv9xBQ.js.map → capabilities-D4vaw_nQ.js.map} +1 -1
  190. package/dashboard/dist/assets/{controlplane-CpvZaJuw.js → controlplane-CiiZ6LJF.js} +2 -2
  191. package/dashboard/dist/assets/{controlplane-CpvZaJuw.js.map → controlplane-CiiZ6LJF.js.map} +1 -1
  192. package/dashboard/dist/assets/{escalation-columns-Mvsze5eV.js → escalation-columns-Dv-MB3eC.js} +2 -2
  193. package/dashboard/dist/assets/{escalation-columns-Mvsze5eV.js.map → escalation-columns-Dv-MB3eC.js.map} +1 -1
  194. package/dashboard/dist/assets/{index-oFKUFGQL.js → index-2tzsxFat.js} +2 -2
  195. package/dashboard/dist/assets/{index-oFKUFGQL.js.map → index-2tzsxFat.js.map} +1 -1
  196. package/dashboard/dist/assets/{index-DZhtP2Ok.js → index-BLB77tc8.js} +2 -2
  197. package/dashboard/dist/assets/{index-DZhtP2Ok.js.map → index-BLB77tc8.js.map} +1 -1
  198. package/dashboard/dist/assets/{index-PfSRKerc.js → index-C3WutFpT.js} +2 -2
  199. package/dashboard/dist/assets/{index-PfSRKerc.js.map → index-C3WutFpT.js.map} +1 -1
  200. package/dashboard/dist/assets/{index-DkrBFFSO.js → index-C4Lce2ET.js} +2 -2
  201. package/dashboard/dist/assets/{index-DkrBFFSO.js.map → index-C4Lce2ET.js.map} +1 -1
  202. package/dashboard/dist/assets/index-CT-FN6CW.js +3 -0
  203. package/dashboard/dist/assets/index-CT-FN6CW.js.map +1 -0
  204. package/dashboard/dist/assets/{index-Eo-l3Lqo.js → index-C_ISDOWJ.js} +2 -2
  205. package/dashboard/dist/assets/{index-Eo-l3Lqo.js.map → index-C_ISDOWJ.js.map} +1 -1
  206. package/dashboard/dist/assets/{index-Bhb89oHH.js → index-CcuvGdvf.js} +2 -2
  207. package/dashboard/dist/assets/{index-Bhb89oHH.js.map → index-CcuvGdvf.js.map} +1 -1
  208. package/dashboard/dist/assets/{index-BbCjGO3M.js → index-CjenRVX_.js} +2 -2
  209. package/dashboard/dist/assets/{index-BbCjGO3M.js.map → index-CjenRVX_.js.map} +1 -1
  210. package/dashboard/dist/assets/{index-DGmzBY9D.js → index-CzjUgFt_.js} +2 -2
  211. package/dashboard/dist/assets/{index-DGmzBY9D.js.map → index-CzjUgFt_.js.map} +1 -1
  212. package/dashboard/dist/assets/{index-D-Xs4rAW.js → index-D2VKBMng.js} +2 -2
  213. package/dashboard/dist/assets/{index-D-Xs4rAW.js.map → index-D2VKBMng.js.map} +1 -1
  214. package/dashboard/dist/assets/{index-z5Hpeidu.js → index-D6pBLZFu.js} +2 -2
  215. package/dashboard/dist/assets/{index-z5Hpeidu.js.map → index-D6pBLZFu.js.map} +1 -1
  216. package/dashboard/dist/assets/{index-Cn8uJsd7.js → index-IJmtbuLh.js} +2 -2
  217. package/dashboard/dist/assets/{index-Cn8uJsd7.js.map → index-IJmtbuLh.js.map} +1 -1
  218. package/dashboard/dist/assets/{index-CgUcYveM.js → index-N71QQ3J5.js} +5 -5
  219. package/dashboard/dist/assets/index-N71QQ3J5.js.map +1 -0
  220. package/dashboard/dist/assets/{knowledge-CgloEkaf.js → knowledge-D1YP2NzS.js} +2 -2
  221. package/dashboard/dist/assets/{knowledge-CgloEkaf.js.map → knowledge-D1YP2NzS.js.map} +1 -1
  222. package/dashboard/dist/assets/{mcp-Bix7QYO7.js → mcp-Bb5psC61.js} +2 -2
  223. package/dashboard/dist/assets/{mcp-Bix7QYO7.js.map → mcp-Bb5psC61.js.map} +1 -1
  224. package/dashboard/dist/assets/{mcp-query-o-sGX2ST.js → mcp-query-D5jUdZwN.js} +2 -2
  225. package/dashboard/dist/assets/{mcp-query-o-sGX2ST.js.map → mcp-query-D5jUdZwN.js.map} +1 -1
  226. package/dashboard/dist/assets/{personas-30azkJ_s.js → personas-CDndbkp4.js} +2 -2
  227. package/dashboard/dist/assets/{personas-30azkJ_s.js.map → personas-CDndbkp4.js.map} +1 -1
  228. package/dashboard/dist/assets/{pipelines-Bfk2GFK7.js → pipelines-Dq3kNhKu.js} +2 -2
  229. package/dashboard/dist/assets/{pipelines-Bfk2GFK7.js.map → pipelines-Dq3kNhKu.js.map} +1 -1
  230. package/dashboard/dist/assets/{tasks-CpdopewW.js → tasks-BlpDsWCy.js} +2 -2
  231. package/dashboard/dist/assets/{tasks-CpdopewW.js.map → tasks-BlpDsWCy.js.map} +1 -1
  232. package/dashboard/dist/assets/{topics--bvCV9Yp.js → topics-C0YaR9fu.js} +2 -2
  233. package/dashboard/dist/assets/{topics--bvCV9Yp.js.map → topics-C0YaR9fu.js.map} +1 -1
  234. package/dashboard/dist/assets/{useDebouncedValue-Da0lPmj3.js → useDebouncedValue-B-KC5ba3.js} +2 -2
  235. package/dashboard/dist/assets/{useDebouncedValue-Da0lPmj3.js.map → useDebouncedValue-B-KC5ba3.js.map} +1 -1
  236. package/dashboard/dist/assets/{useNamespace-DD_Esl6n.js → useNamespace-Dkr5g2Ac.js} +2 -2
  237. package/dashboard/dist/assets/{useNamespace-DD_Esl6n.js.map → useNamespace-Dkr5g2Ac.js.map} +1 -1
  238. package/dashboard/dist/assets/{useYamlActivityEvents-AeAhZJJk.js → useYamlActivityEvents-BANBOshA.js} +2 -2
  239. package/dashboard/dist/assets/{useYamlActivityEvents-AeAhZJJk.js.map → useYamlActivityEvents-BANBOshA.js.map} +1 -1
  240. package/dashboard/dist/assets/{vendor-icons-B7OcUM6b.js → vendor-icons-CeVtUKyP.js} +2 -2
  241. package/dashboard/dist/assets/{vendor-icons-B7OcUM6b.js.map → vendor-icons-CeVtUKyP.js.map} +1 -1
  242. package/dashboard/dist/assets/{yaml-workflows-BHAOOGvn.js → yaml-workflows-BLYoJfmB.js} +2 -2
  243. package/dashboard/dist/assets/{yaml-workflows-BHAOOGvn.js.map → yaml-workflows-BLYoJfmB.js.map} +1 -1
  244. package/dashboard/dist/index.html +2 -2
  245. package/docs/accumulator.md +37 -1
  246. package/docs/api/mcp/admin.md +3 -2
  247. package/docs/api/sdk/escalations.md +6 -0
  248. package/docs/api/sdk/exports.md +1 -1
  249. package/docs/api/sdk/workflows.md +23 -4
  250. package/docs/cli.md +1 -1
  251. package/docs/dashboard.md +1 -1
  252. package/docs/faceted-routing.md +2 -0
  253. package/docs/hitl/x-lt-widget.md +5 -0
  254. package/docs/scan-codes.md +40 -7
  255. package/package.json +2 -2
  256. package/dashboard/dist/assets/AvailableEscalationsPage-CpbC8oTN.js +0 -2
  257. package/dashboard/dist/assets/AvailableEscalationsPage-CpbC8oTN.js.map +0 -1
  258. package/dashboard/dist/assets/EscalationSchemaPage-W5r0A1kj.js +0 -9
  259. package/dashboard/dist/assets/EscalationSchemaPage-W5r0A1kj.js.map +0 -1
  260. package/dashboard/dist/assets/EscalationTimeline-DKyTz6sM.js +0 -2
  261. package/dashboard/dist/assets/EscalationTimeline-DKyTz6sM.js.map +0 -1
  262. package/dashboard/dist/assets/McpQueryDetailPage-DYJr16l7.js +0 -5
  263. package/dashboard/dist/assets/McpQueryDetailPage-DYJr16l7.js.map +0 -1
  264. package/dashboard/dist/assets/ResolverForm-BwgjIX97.js +0 -7
  265. package/dashboard/dist/assets/ResolverForm-BwgjIX97.js.map +0 -1
  266. package/dashboard/dist/assets/index-B_GT23TE.js +0 -3
  267. package/dashboard/dist/assets/index-B_GT23TE.js.map +0 -1
  268. package/dashboard/dist/assets/index-CgUcYveM.js.map +0 -1
@@ -1,4 +1,4 @@
1
- import type { AccumulatedItem, LTEscalationRecord } from '../../types';
1
+ import type { AccumulatedItem, ContainerSelector, LTEscalationRecord } from '../../types';
2
2
  import type { LTApiAuth, LTApiResult } from '../../types/sdk';
3
3
  /** Names the second row of a reciprocal add or removal. Exactly one selector. */
4
4
  export interface ReciprocalInput {
@@ -16,6 +16,11 @@ export interface AccumulateItemInput {
16
16
  metadata?: Record<string, any>;
17
17
  assertClaim?: boolean;
18
18
  reciprocal?: ReciprocalInput;
19
+ /**
20
+ * The person who caused the add, when a trusted service performs it for
21
+ * them. Recorded as the entry's actor; the caller's rights still gate the write.
22
+ */
23
+ initiatedBy?: string;
19
24
  }
20
25
  /**
21
26
  * Add ONE item to an accumulator escalation (a `conditional` wait declared
@@ -59,10 +64,14 @@ export declare function accumulateItemByMetadata(input: {
59
64
  key: string;
60
65
  value: string;
61
66
  restrictRoles?: string[];
67
+ /** Narrows the container beyond the facet: type, subtype, extra facet guards. */
68
+ container?: ContainerSelector;
62
69
  } & Omit<AccumulateItemInput, 'assertClaim'>, auth: LTApiAuth): Promise<LTApiResult>;
63
70
  export interface RemoveItemInput {
64
71
  itemKey: string;
65
72
  reciprocal?: Omit<ReciprocalInput, 'payload'>;
73
+ /** The person who caused the removal; see {@link AccumulateItemInput.initiatedBy}. */
74
+ initiatedBy?: string;
66
75
  }
67
76
  /**
68
77
  * Remove ONE held item from a pending accumulator escalation, one guarded
@@ -44,6 +44,8 @@ exports.getEscalationItems = getEscalationItems;
44
44
  exports.getEscalationItemsBySignalKey = getEscalationItemsBySignalKey;
45
45
  const escalationService = __importStar(require("../../services/escalation"));
46
46
  const resolver_validation_1 = require("../../services/escalation/resolver-validation");
47
+ const userService = __importStar(require("../../services/user"));
48
+ const uuid_1 = require("../../lib/uuid");
47
49
  const enforcement_cache_1 = require("../../services/role/enforcement-cache");
48
50
  const escalation_1 = require("../../types/escalation");
49
51
  const helpers_1 = require("./helpers");
@@ -69,20 +71,61 @@ const NO_ACCUMULATOR_FOR_FACET = 'No pending accumulator found for this metadata
69
71
  /**
70
72
  * The container a facet names: the highest priority pending row that carries
71
73
  * the facet AND the accumulator declaration, within the given roles (null =
72
- * unscoped). A release or remediation row sharing the facet is never a
73
- * candidate, so a by-facet add lands on the open container or nowhere.
74
+ * unscoped) and the selector's guards. A release or remediation row sharing
75
+ * the facet is never a candidate, and neither is the add's own reciprocal row,
76
+ * so a by-facet add lands on the open container or nowhere.
74
77
  */
75
- async function pickAccumulatorByFacet(key, value, roles) {
78
+ async function pickAccumulatorByFacet(key, value, roles, selector, excludeId) {
76
79
  if (roles !== null && roles.length === 0)
77
80
  return null;
78
81
  const found = await escalationService.searchByFacets({
79
82
  roles: roles ?? undefined,
80
- facets: { [key]: value },
83
+ types: selector?.types,
84
+ subtypes: selector?.subtypes,
85
+ facets: { ...selector?.facets, [key]: value },
81
86
  status: 'pending',
82
87
  exists: [escalation_1.ESCALATION_ACCUMULATE_KEYS.COUNT],
83
- limit: 1,
88
+ limit: excludeId ? 2 : 1,
84
89
  });
85
- return found.escalations[0] ?? null;
90
+ return found.escalations.find((row) => row.id !== excludeId) ?? null;
91
+ }
92
+ const isStringList = (v) => Array.isArray(v) && v.every((s) => typeof s === 'string' && s.length > 0);
93
+ /** The selector's shape error, or null when it is absent or well formed. */
94
+ function containerSelectorError(selector) {
95
+ if (selector === undefined)
96
+ return null;
97
+ if (!selector || typeof selector !== 'object' || Array.isArray(selector))
98
+ return 'container must be an object';
99
+ if (selector.types !== undefined && !isStringList(selector.types))
100
+ return 'container.types must be an array of strings';
101
+ if (selector.subtypes !== undefined && !isStringList(selector.subtypes))
102
+ return 'container.subtypes must be an array of strings';
103
+ if (selector.facets !== undefined && (!selector.facets || typeof selector.facets !== 'object' || Array.isArray(selector.facets))) {
104
+ return 'container.facets must be an object';
105
+ }
106
+ return null;
107
+ }
108
+ function isNarrowed(selector) {
109
+ return !!(selector?.types?.length || selector?.subtypes?.length
110
+ || (selector?.facets && Object.keys(selector.facets).length));
111
+ }
112
+ /**
113
+ * The entry's actor: the caller, or the user a global-access caller names as
114
+ * the initiator. Attribution only; it never widens what the caller may write.
115
+ */
116
+ async function resolveActor(initiatedBy, auth) {
117
+ if (initiatedBy === undefined || initiatedBy === auth.userId)
118
+ return { actor: auth.userId };
119
+ if (typeof initiatedBy !== 'string' || !(0, uuid_1.isUuid)(initiatedBy)) {
120
+ return { error: { status: 400, error: 'initiatedBy must be a user id' } };
121
+ }
122
+ if (!(await userService.hasGlobalEscalationAccess(auth.userId))) {
123
+ return { error: { status: 403, error: 'initiatedBy requires global escalation access' } };
124
+ }
125
+ if (!(await userService.getUser(initiatedBy))) {
126
+ return { error: { status: 400, error: 'initiatedBy names no user' } };
127
+ }
128
+ return { actor: initiatedBy };
86
129
  }
87
130
  function reciprocalSelectorCount(r) {
88
131
  return (r.id ? 1 : 0) + (r.signalKey ? 1 : 0) + (r.key !== undefined ? 1 : 0);
@@ -158,6 +201,9 @@ async function accumulateItem(input, auth) {
158
201
  const { id, itemKey, metadata } = input;
159
202
  if (!itemKey)
160
203
  return { status: 400, error: 'itemKey is required' };
204
+ const initiator = await resolveActor(input.initiatedBy, auth);
205
+ if ('error' in initiator)
206
+ return initiator.error;
161
207
  const escalation = await escalationService.getEscalation(id);
162
208
  if (!escalation)
163
209
  return { status: 404, error: 'Escalation not found' };
@@ -183,7 +229,7 @@ async function accumulateItem(input, auth) {
183
229
  itemKey,
184
230
  payload: payload.payload,
185
231
  metadata: { ...metadata, resolved_by: auth.userId },
186
- actor: auth.userId,
232
+ actor: initiator.actor,
187
233
  assertClaim: input.assertClaim ? auth.userId : undefined,
188
234
  resolvedBy: await (0, resolve_1.resolverIdentity)(auth),
189
235
  reciprocal: reciprocal.reciprocal,
@@ -206,6 +252,9 @@ async function accumulateItemBySignalKey(input, auth) {
206
252
  return { status: 400, error: 'signalKey is required' };
207
253
  if (!itemKey)
208
254
  return { status: 400, error: 'itemKey is required' };
255
+ const initiator = await resolveActor(input.initiatedBy, auth);
256
+ if ('error' in initiator)
257
+ return initiator.error;
209
258
  const escalation = await escalationService.getEscalationBySignalKey(signalKey);
210
259
  if (!escalation)
211
260
  return { status: 404, error: 'Escalation not found' };
@@ -226,7 +275,7 @@ async function accumulateItemBySignalKey(input, auth) {
226
275
  itemKey,
227
276
  payload: payload.payload,
228
277
  metadata: { ...metadata, resolved_by: auth.userId },
229
- actor: auth.userId,
278
+ actor: initiator.actor,
230
279
  resolvedBy: await (0, resolve_1.resolverIdentity)(auth),
231
280
  reciprocal: reciprocal.reciprocal,
232
281
  });
@@ -254,6 +303,12 @@ async function accumulateItemByMetadata(input, auth) {
254
303
  return { status: 400, error: 'key and value are required' };
255
304
  if (!itemKey)
256
305
  return { status: 400, error: 'itemKey is required' };
306
+ const initiator = await resolveActor(input.initiatedBy, auth);
307
+ if ('error' in initiator)
308
+ return initiator.error;
309
+ const selectorError = containerSelectorError(input.container);
310
+ if (selectorError)
311
+ return { status: 400, error: selectorError };
257
312
  const writeScope = await (0, helpers_1.getEscalationWriteScope)(auth.userId);
258
313
  const allowedRoles = (0, metadata_1.restrictScopeRoles)(writeScope.allRoles, writeScope.global, input.restrictRoles);
259
314
  const reciprocal = await gateReciprocal(input.reciprocal, auth);
@@ -261,13 +316,17 @@ async function accumulateItemByMetadata(input, auth) {
261
316
  return reciprocal.error;
262
317
  const resolvedBy = await (0, resolve_1.resolverIdentity)(auth);
263
318
  const outcome = { ...metadata, resolved_by: auth.userId };
319
+ // A narrowed selector picks in Long Tail, then adds by id; the by-id
320
+ // statement re-checks pending under its lock, and type, subtype and the
321
+ // accumulator declaration are fixed at creation.
264
322
  const enforcing = await (0, enforcement_cache_1.getEnforcingRoles)();
265
- if (enforcing.size === 0 || input.payload === undefined) {
323
+ const narrowed = isNarrowed(input.container);
324
+ if (!narrowed && (enforcing.size === 0 || input.payload === undefined)) {
266
325
  const result = await escalationService.accumulateItemByMetadata(key, value, {
267
326
  itemKey,
268
327
  payload: input.payload,
269
328
  metadata: outcome,
270
- actor: auth.userId,
329
+ actor: initiator.actor,
271
330
  resolvedBy,
272
331
  roles: allowedRoles ?? undefined,
273
332
  reciprocal: reciprocal.reciprocal,
@@ -276,7 +335,7 @@ async function accumulateItemByMetadata(input, auth) {
276
335
  return { status: 404, error: NO_ACCUMULATOR_FOR_FACET };
277
336
  return accumulateOutcomeResult(result, itemKey, result.escalation?.workflow_id ?? null);
278
337
  }
279
- const row = await pickAccumulatorByFacet(key, value, allowedRoles);
338
+ const row = await pickAccumulatorByFacet(key, value, allowedRoles, input.container, reciprocal.reciprocal?.id);
280
339
  if (!row)
281
340
  return { status: 404, error: NO_ACCUMULATOR_FOR_FACET };
282
341
  if (!isAccumulator(row))
@@ -288,7 +347,7 @@ async function accumulateItemByMetadata(input, auth) {
288
347
  itemKey,
289
348
  payload: payload.payload,
290
349
  metadata: outcome,
291
- actor: auth.userId,
350
+ actor: initiator.actor,
292
351
  resolvedBy,
293
352
  reciprocal: reciprocal.reciprocal,
294
353
  });
@@ -311,6 +370,9 @@ async function removeItem(input, auth) {
311
370
  const { id, itemKey } = input;
312
371
  if (!itemKey)
313
372
  return { status: 400, error: 'itemKey is required' };
373
+ const initiator = await resolveActor(input.initiatedBy, auth);
374
+ if ('error' in initiator)
375
+ return initiator.error;
314
376
  const escalation = await escalationService.getEscalation(id);
315
377
  if (!escalation)
316
378
  return { status: 404, error: 'Escalation not found' };
@@ -328,7 +390,7 @@ async function removeItem(input, auth) {
328
390
  if ('error' in reciprocal)
329
391
  return reciprocal.error;
330
392
  const result = await escalationService.removeAccumulatedItem(escalation.id, {
331
- itemKey, actor: auth.userId, reciprocal: reciprocal.reciprocal,
393
+ itemKey, actor: initiator.actor, reciprocal: reciprocal.reciprocal,
332
394
  });
333
395
  return removeOutcomeResult(result, itemKey);
334
396
  }
@@ -343,6 +405,9 @@ async function removeItemBySignalKey(input, auth) {
343
405
  return { status: 400, error: 'signalKey is required' };
344
406
  if (!itemKey)
345
407
  return { status: 400, error: 'itemKey is required' };
408
+ const initiator = await resolveActor(input.initiatedBy, auth);
409
+ if ('error' in initiator)
410
+ return initiator.error;
346
411
  const escalation = await escalationService.getEscalationBySignalKey(signalKey);
347
412
  if (!escalation)
348
413
  return { status: 404, error: 'Escalation not found' };
@@ -357,7 +422,7 @@ async function removeItemBySignalKey(input, auth) {
357
422
  if ('error' in reciprocal)
358
423
  return reciprocal.error;
359
424
  const result = await escalationService.removeAccumulatedItemBySignalKey(signalKey, {
360
- itemKey, actor: auth.userId, reciprocal: reciprocal.reciprocal,
425
+ itemKey, actor: initiator.actor, reciprocal: reciprocal.reciprocal,
361
426
  });
362
427
  return removeOutcomeResult(result, itemKey);
363
428
  }
@@ -372,13 +437,16 @@ async function removeItemByMetadata(input, auth) {
372
437
  return { status: 400, error: 'key and value are required' };
373
438
  if (!itemKey)
374
439
  return { status: 400, error: 'itemKey is required' };
440
+ const initiator = await resolveActor(input.initiatedBy, auth);
441
+ if ('error' in initiator)
442
+ return initiator.error;
375
443
  const writeScope = await (0, helpers_1.getEscalationWriteScope)(auth.userId);
376
444
  const allowedRoles = (0, metadata_1.restrictScopeRoles)(writeScope.allRoles, writeScope.global, input.restrictRoles);
377
445
  const reciprocal = await gateReciprocal(input.reciprocal, auth);
378
446
  if ('error' in reciprocal)
379
447
  return reciprocal.error;
380
448
  const result = await escalationService.removeAccumulatedItemByMetadata(key, value, {
381
- itemKey, actor: auth.userId, roles: allowedRoles ?? undefined, reciprocal: reciprocal.reciprocal,
449
+ itemKey, actor: initiator.actor, roles: allowedRoles ?? undefined, reciprocal: reciprocal.reciprocal,
382
450
  });
383
451
  if (result.outcome === 'not-found')
384
452
  return { status: 404, error: NO_ACCUMULATOR_FOR_FACET };
@@ -1,5 +1,6 @@
1
1
  import { type ProvisionIfAbsent } from './helpers';
2
2
  import type { LTApiAuth, LTApiResult } from '../../types/sdk';
3
+ import type { ScanAvailability } from '../../types';
3
4
  /**
4
5
  * Intersect the caller's scope roles with an optional caller-supplied
5
6
  * restriction (e.g. a scan step's expected queues). Global scope with no
@@ -57,4 +58,8 @@ export declare function resolveByMetadata(input: {
57
58
  metadata?: Record<string, any>;
58
59
  restrictRoles?: string[];
59
60
  extraFacets?: Record<string, any>;
61
+ /** Narrows the pick to a claim state; 'mine' is the resolver's live claim. */
62
+ availability?: ScanAvailability;
63
+ /** Resolves only this row, when it still matches every other condition. */
64
+ assertId?: string;
60
65
  }, auth: LTApiAuth): Promise<LTApiResult>;
@@ -42,6 +42,7 @@ const helpers_1 = require("./helpers");
42
42
  const resolver_validation_1 = require("../../services/escalation/resolver-validation");
43
43
  const enforcement_cache_1 = require("../../services/role/enforcement-cache");
44
44
  const resolve_1 = require("./resolve");
45
+ const uuid_1 = require("../../lib/uuid");
45
46
  /**
46
47
  * Intersect the caller's scope roles with an optional caller-supplied
47
48
  * restriction (e.g. a scan step's expected queues). Global scope with no
@@ -138,6 +139,9 @@ async function resolveByMetadata(input, auth) {
138
139
  if (!input.resolverPayload) {
139
140
  return { status: 400, error: 'resolverPayload is required' };
140
141
  }
142
+ if (input.assertId !== undefined && !(0, uuid_1.isUuid)(input.assertId)) {
143
+ return { status: 404, error: 'No pending escalation found for this metadata, or insufficient role permissions' };
144
+ }
141
145
  const resolved = await (0, helpers_1.resolveAssignee)(input.assignee, auth);
142
146
  if ('error' in resolved)
143
147
  return resolved.error;
@@ -159,12 +163,12 @@ async function resolveByMetadata(input, auth) {
159
163
  // writing; the payload validates here, then a second asserted pass claims
160
164
  // + resolves the same row with pending re-checked inside the statement.
161
165
  const enforcing = await (0, enforcement_cache_1.getEnforcingRoles)();
162
- let result = await escalationService.resolveByMetadataAtomic(input.key, input.value, resolveUserId, input.resolverPayload, input.metadata, writeAllRoles, writeSelfRoles, enforcing.size > 0 ? [...enforcing] : null, null, input.extraFacets);
166
+ let result = await escalationService.resolveByMetadataAtomic(input.key, input.value, resolveUserId, input.resolverPayload, input.metadata, writeAllRoles, writeSelfRoles, enforcing.size > 0 ? [...enforcing] : null, input.assertId ?? null, input.extraFacets, input.availability);
163
167
  if (result.outcome === 'validation_required' && result.row) {
164
168
  const report = await (0, resolver_validation_1.checkResolverPayload)(result.row, input.resolverPayload, undefined, resolveUserId);
165
169
  if (report)
166
170
  return (0, resolve_1.validationFailure)(report);
167
- result = await escalationService.resolveByMetadataAtomic(input.key, input.value, resolveUserId, input.resolverPayload, input.metadata, writeAllRoles, writeSelfRoles, null, result.row.id, input.extraFacets);
171
+ result = await escalationService.resolveByMetadataAtomic(input.key, input.value, resolveUserId, input.resolverPayload, input.metadata, writeAllRoles, writeSelfRoles, null, result.row.id, input.extraFacets, input.availability);
168
172
  // The asserted row left pending between the two passes — a concurrent
169
173
  // resolution won the row; surface it as the conflict it is.
170
174
  if (result.outcome === 'not_found') {
@@ -110,12 +110,13 @@ async function executeScanChoice(input, auth) {
110
110
  // showed. A row that moved on (resolved, claimed away, re-routed) is the
111
111
  // same double-actor race a second scan loses.
112
112
  const located = await (0, locate_1.locateForStep)(step, ctx, 2);
113
- if (!located || !located.escalations.some((e) => e.id === input.escalationId)) {
113
+ const presented = located?.escalations.find((e) => e.id === input.escalationId);
114
+ if (!presented) {
114
115
  return decorate((0, context_1.conflict)('the item is no longer in the presented state'), ctx, input);
115
116
  }
116
117
  // 6. Dispatch through the same atomic executors a direct scan uses.
117
118
  const synthesized = { query: step.query, verb: choice.verb, params: choice.params };
118
- const result = await (0, verbs_1.dispatchChoiceVerb)(synthesized, ctx, located.escalations[0]);
119
+ const result = await (0, verbs_1.dispatchChoiceVerb)(synthesized, ctx, presented);
119
120
  return decorate(result, ctx, input);
120
121
  }
121
122
  catch (err) {
@@ -2,7 +2,12 @@ import { type ScanExecuteResponse, type ScanStep } from '../../types';
2
2
  import type { LTApiResult } from '../../types/sdk';
3
3
  import { type StepContext } from './context';
4
4
  export declare function claimStep(step: ScanStep, ctx: StepContext): Promise<LTApiResult<ScanExecuteResponse> | null>;
5
- export declare function resolveStep(step: ScanStep, ctx: StepContext): Promise<LTApiResult<ScanExecuteResponse> | null>;
5
+ /**
6
+ * Resolves the row the step's query names: the scanned target, the extra
7
+ * facets, and the claim state, all inside the atomic statement. A presented
8
+ * choice passes the row it showed, so the resolve lands on that row or misses.
9
+ */
10
+ export declare function resolveStep(step: ScanStep, ctx: StepContext, presentedId?: string): Promise<LTApiResult<ScanExecuteResponse> | null>;
6
11
  export declare function escalateStep(step: ScanStep, ctx: StepContext): Promise<LTApiResult<ScanExecuteResponse> | null>;
7
12
  export declare function releaseStep(step: ScanStep, ctx: StepContext): Promise<LTApiResult<ScanExecuteResponse> | null>;
8
13
  /**
@@ -88,7 +88,12 @@ async function claimStep(step, ctx) {
88
88
  return result;
89
89
  return (0, context_1.executed)(result.data.escalation, step);
90
90
  }
91
- async function resolveStep(step, ctx) {
91
+ /**
92
+ * Resolves the row the step's query names: the scanned target, the extra
93
+ * facets, and the claim state, all inside the atomic statement. A presented
94
+ * choice passes the row it showed, so the resolve lands on that row or misses.
95
+ */
96
+ async function resolveStep(step, ctx, presentedId) {
92
97
  const rendered = await templated(step, ctx, (tpl) => ({
93
98
  resolverPayload: scanCodeService.interpolateScanTemplate(step.params?.resolverPayload ?? {}, tpl),
94
99
  metadata: (0, context_1.interpolatedMetadata)(step, ctx, tpl),
@@ -102,6 +107,8 @@ async function resolveStep(step, ctx) {
102
107
  metadata: { ...rendered.metadata, ...(0, context_1.provenance)(ctx) },
103
108
  restrictRoles: step.query?.roles,
104
109
  extraFacets: step.query?.facets,
110
+ availability: step.query?.availability,
111
+ assertId: presentedId,
105
112
  }, ctx.auth);
106
113
  if (result.status === 404)
107
114
  return null;
@@ -189,7 +196,7 @@ async function dispatchChoiceVerb(step, ctx, row) {
189
196
  case types_1.SCAN_VERBS.CLAIM_SHOW_DETAIL:
190
197
  return (await claimStep(step, ctx)) ?? (await missReason(step, ctx, 'the item is no longer claimable'));
191
198
  case types_1.SCAN_VERBS.RESOLVE:
192
- return (await resolveStep(step, ctx)) ?? (await missReason(step, ctx, 'the item is no longer resolvable'));
199
+ return (await resolveStep(step, ctx, row.id)) ?? (await missReason(step, ctx, 'the item is no longer resolvable'));
193
200
  case types_1.SCAN_VERBS.ESCALATE:
194
201
  return (await escalateStep(step, ctx)) ?? (await missReason(step, ctx, 'the item already moved on'));
195
202
  case types_1.SCAN_VERBS.RELEASE:
@@ -283,7 +290,10 @@ async function accumulateStep(step, ctx) {
283
290
  itemKey: rendered.itemKey,
284
291
  payload: rendered.payload,
285
292
  metadata: rendered.metadata,
286
- restrictRoles: options.containerRoles,
293
+ restrictRoles: options.container?.roles ?? options.containerRoles,
294
+ container: options.container
295
+ ? { types: options.container.types, subtypes: options.container.subtypes, facets: options.container.facets }
296
+ : undefined,
287
297
  ...(options.reciprocal === false ? {} : { reciprocal: { id: item.id } }),
288
298
  };
289
299
  }
@@ -21,14 +21,16 @@ export declare function invokeWorkflow(input: {
21
21
  /**
22
22
  * Get the execution status of a workflow.
23
23
  *
24
- * Returns the HotMesh status code (0 = completed, 1 = running).
24
+ * Returns the HotMesh status code (above 0 running, 0 closed, below 0
25
+ * terminated) with the state it implies: `running`, `completed` or `failed`,
26
+ * `terminated` when an interrupt ended it, and `error` for a failed run.
25
27
  * Resolves the workflow handle via task record or worker registry.
26
28
  * `appId` selects the namespace for the job-entity fallback so a child
27
29
  * running in another app resolves instead of 404ing against `durable`.
28
30
  *
29
31
  * @param input.workflowId — HotMesh workflow ID
30
32
  * @param input.appId — HotMesh namespace for resolution (default: durable)
31
- * @returns `{ status: 200, data: { workflowId, status } }` or 404
33
+ * @returns `{ status: 200, data: { workflowId, status, state, terminated, error? } }` or 404
32
34
  */
33
35
  export declare function getWorkflowStatus(input: {
34
36
  workflowId: string;
@@ -38,11 +40,13 @@ export declare function getWorkflowStatus(input: {
38
40
  * Get the result of a completed workflow.
39
41
  *
40
42
  * Returns 202 if the workflow is still running, 200 with the result
41
- * payload when complete. Never blocks — always returns immediately.
43
+ * payload when complete, and 200 with `state: 'failed'` (plus `terminated`
44
+ * and `error` when known) when it failed or was terminated. Never blocks —
45
+ * always returns immediately.
42
46
  *
43
47
  * @param input.workflowId — HotMesh workflow ID
44
48
  * @param input.appId — HotMesh namespace for resolution (default: durable)
45
- * @returns `{ status: 200, data: { workflowId, result } }` or 202 if running
49
+ * @returns `{ status: 200, data: { workflowId, state, result } }` or 202 if running
46
50
  */
47
51
  export declare function getWorkflowResult(input: {
48
52
  workflowId: string;
@@ -41,6 +41,7 @@ exports.exportWorkflow = exportWorkflow;
41
41
  const workers_1 = require("../../workers");
42
42
  const exportService = __importStar(require("../../services/export"));
43
43
  const task_1 = require("../../services/task");
44
+ const workflow_state_1 = require("../../services/workflow-state");
44
45
  const workflow_invocation_1 = require("../../services/workflow-invocation");
45
46
  const crud_1 = require("../../services/escalation/crud");
46
47
  const configService = __importStar(require("../../services/config"));
@@ -91,24 +92,26 @@ async function invokeWorkflow(input, auth) {
91
92
  /**
92
93
  * Get the execution status of a workflow.
93
94
  *
94
- * Returns the HotMesh status code (0 = completed, 1 = running).
95
+ * Returns the HotMesh status code (above 0 running, 0 closed, below 0
96
+ * terminated) with the state it implies: `running`, `completed` or `failed`,
97
+ * `terminated` when an interrupt ended it, and `error` for a failed run.
95
98
  * Resolves the workflow handle via task record or worker registry.
96
99
  * `appId` selects the namespace for the job-entity fallback so a child
97
100
  * running in another app resolves instead of 404ing against `durable`.
98
101
  *
99
102
  * @param input.workflowId — HotMesh workflow ID
100
103
  * @param input.appId — HotMesh namespace for resolution (default: durable)
101
- * @returns `{ status: 200, data: { workflowId, status } }` or 404
104
+ * @returns `{ status: 200, data: { workflowId, status, state, terminated, error? } }` or 404
102
105
  */
103
106
  async function getWorkflowStatus(input) {
104
107
  try {
105
108
  const resolved = await (0, task_1.resolveWorkflowHandle)(input.workflowId, input.appId);
106
109
  const client = (0, workers_1.createClient)();
107
110
  const handle = await client.workflow.getHandle(resolved.taskQueue, resolved.workflowName, input.workflowId);
108
- const status = await handle.status();
111
+ const reading = await (0, workflow_state_1.readWorkflowState)(handle);
109
112
  return {
110
113
  status: 200,
111
- data: { workflowId: input.workflowId, status },
114
+ data: { workflowId: input.workflowId, ...reading },
112
115
  };
113
116
  }
114
117
  catch (err) {
@@ -121,28 +124,42 @@ async function getWorkflowStatus(input) {
121
124
  * Get the result of a completed workflow.
122
125
  *
123
126
  * Returns 202 if the workflow is still running, 200 with the result
124
- * payload when complete. Never blocks — always returns immediately.
127
+ * payload when complete, and 200 with `state: 'failed'` (plus `terminated`
128
+ * and `error` when known) when it failed or was terminated. Never blocks —
129
+ * always returns immediately.
125
130
  *
126
131
  * @param input.workflowId — HotMesh workflow ID
127
132
  * @param input.appId — HotMesh namespace for resolution (default: durable)
128
- * @returns `{ status: 200, data: { workflowId, result } }` or 202 if running
133
+ * @returns `{ status: 200, data: { workflowId, state, result } }` or 202 if running
129
134
  */
130
135
  async function getWorkflowResult(input) {
131
136
  try {
132
137
  const resolved = await (0, task_1.resolveWorkflowHandle)(input.workflowId, input.appId);
133
138
  const client = (0, workers_1.createClient)();
134
139
  const handle = await client.workflow.getHandle(resolved.taskQueue, resolved.workflowName, input.workflowId);
135
- const status = await handle.status();
136
- if (status !== 0) {
140
+ const reading = await (0, workflow_state_1.readWorkflowState)(handle);
141
+ if (reading.state === workflow_state_1.WORKFLOW_STATES.RUNNING) {
137
142
  return {
138
143
  status: 202,
139
144
  data: { workflowId: input.workflowId, status: 'running' },
140
145
  };
141
146
  }
147
+ if (reading.state === workflow_state_1.WORKFLOW_STATES.FAILED) {
148
+ return {
149
+ status: 200,
150
+ data: {
151
+ workflowId: input.workflowId,
152
+ state: reading.state,
153
+ terminated: reading.terminated,
154
+ ...(reading.error ? { error: reading.error } : {}),
155
+ result: null,
156
+ },
157
+ };
158
+ }
142
159
  const result = await handle.result();
143
160
  return {
144
161
  status: 200,
145
- data: { workflowId: input.workflowId, result },
162
+ data: { workflowId: input.workflowId, state: reading.state, result },
146
163
  };
147
164
  }
148
165
  catch (err) {
@@ -45,6 +45,7 @@ exports.terminateWorkflow = terminateWorkflow;
45
45
  const picocolors_1 = __importDefault(require("picocolors"));
46
46
  const client_1 = require("../client");
47
47
  const format_1 = require("../format");
48
+ const workflow_state_1 = require("../../../shared/workflow-state");
48
49
  const COLUMNS = [
49
50
  { key: 'workflow_type', label: 'Type', width: 24 },
50
51
  { key: 'task_queue', label: 'Queue', width: 16 },
@@ -72,13 +73,17 @@ async function invokeWorkflow(type, opts) {
72
73
  console.log(`\n ${picocolors_1.default.green('✓')} Started ${picocolors_1.default.bold(type)} → ${picocolors_1.default.dim(data.workflowId)}\n`);
73
74
  }
74
75
  async function getWorkflowStatus(id, opts) {
75
- const data = await (0, client_1.apiFetch)(`/workflows/status/${id}`);
76
+ const data = await (0, client_1.apiFetch)(`/workflows/${encodeURIComponent(id)}/status`);
76
77
  if (opts.json) {
77
78
  console.log(JSON.stringify(data, null, 2));
78
79
  return;
79
80
  }
80
- const status = data.status === 0 ? picocolors_1.default.green('completed') : picocolors_1.default.blue('running');
81
- console.log(`\n ${data.workflowId} ${status}\n`);
81
+ const state = data.state === workflow_state_1.WORKFLOW_STATES.COMPLETED
82
+ ? picocolors_1.default.green('completed')
83
+ : data.state === workflow_state_1.WORKFLOW_STATES.FAILED
84
+ ? picocolors_1.default.red(data.terminated ? 'failed (terminated)' : 'failed')
85
+ : picocolors_1.default.blue('running');
86
+ console.log(`\n ${data.workflowId} ${state}${data.error ? picocolors_1.default.dim(` ${data.error}`) : ''}\n`);
82
87
  }
83
88
  async function getWorkflowResult(id, opts) {
84
89
  const { server, token } = await (await Promise.resolve().then(() => __importStar(require('../auth')))).resolveAuth();
@@ -121,6 +126,6 @@ async function getWorkflowEnvelopes(id, opts) {
121
126
  console.log();
122
127
  }
123
128
  async function terminateWorkflow(id) {
124
- await (0, client_1.apiFetch)(`/workflows/terminate/${id}`, { method: 'POST' });
129
+ await (0, client_1.apiFetch)(`/workflows/${encodeURIComponent(id)}/terminate`, { method: 'POST' });
125
130
  console.log(`\n ${picocolors_1.default.green('✓')} Terminated ${picocolors_1.default.dim(id)}\n`);
126
131
  }
@@ -1,12 +1,17 @@
1
- import { Client, Pool } from 'pg';
1
+ import { Client, Pool, type ClientConfig } from 'pg';
2
+ /** `pg.Client` that carries an 'error' listener from construction. */
3
+ declare class GuardedClient extends Client {
4
+ constructor(config?: string | ClientConfig);
5
+ }
2
6
  export declare function getPool(): Pool;
3
7
  export declare function closePool(): Promise<void>;
4
8
  /**
5
- * HotMesh connection descriptor: `{ class: Client, options: postgres_options }`.
9
+ * HotMesh connection descriptor: `{ class: GuardedClient, options: postgres_options }`.
6
10
  * Use this everywhere HotMesh / Durable APIs need a connection config
7
11
  * instead of importing `pg` and `postgres_options` directly.
8
12
  */
9
13
  export declare function getConnection(): {
10
- class: typeof Client;
14
+ class: typeof GuardedClient;
11
15
  options: Record<string, unknown>;
12
16
  };
17
+ export {};
@@ -5,10 +5,29 @@ exports.closePool = closePool;
5
5
  exports.getConnection = getConnection;
6
6
  const pg_1 = require("pg");
7
7
  const config_1 = require("../../modules/config");
8
+ const logger_1 = require("../logger");
9
+ /** A new connection that cannot be established within this window fails instead of hanging. */
10
+ const CONNECT_TIMEOUT_MS = 10_000;
8
11
  let pool = null;
12
+ /** A lost socket fails the pending query; its 'error' event must never end the process. */
13
+ function onConnectionError(error) {
14
+ logger_1.loggerRegistry.debug(`[lt-db] connection error: ${error.message}`);
15
+ }
16
+ function onIdleConnectionLost(error) {
17
+ logger_1.loggerRegistry.warn(`[lt-db] pooled connection lost: ${error.message}`);
18
+ }
19
+ /** `pg.Client` that carries an 'error' listener from construction. */
20
+ class GuardedClient extends pg_1.Client {
21
+ constructor(config) {
22
+ super(config);
23
+ this.on('error', onConnectionError);
24
+ }
25
+ }
9
26
  function getPool() {
10
27
  if (!pool) {
11
28
  pool = new pg_1.Pool({
29
+ connectionTimeoutMillis: CONNECT_TIMEOUT_MS,
30
+ keepAlive: true,
12
31
  ...config_1.postgres_options,
13
32
  // Long-tail's shared tables live in public. Postgres's default
14
33
  // search_path is `"$user", public`: if any schema shares the connecting
@@ -20,6 +39,9 @@ function getPool() {
20
39
  // the engine fully qualifies its app schemas.
21
40
  options: '-c search_path=public',
22
41
  });
42
+ pool.on('error', onIdleConnectionLost);
43
+ // pg-pool drops its own listener while a client is checked out
44
+ pool.on('connect', (client) => client.on('error', onConnectionError));
23
45
  }
24
46
  return pool;
25
47
  }
@@ -30,10 +52,10 @@ async function closePool() {
30
52
  }
31
53
  }
32
54
  /**
33
- * HotMesh connection descriptor: `{ class: Client, options: postgres_options }`.
55
+ * HotMesh connection descriptor: `{ class: GuardedClient, options: postgres_options }`.
34
56
  * Use this everywhere HotMesh / Durable APIs need a connection config
35
57
  * instead of importing `pg` and `postgres_options` directly.
36
58
  */
37
59
  function getConnection() {
38
- return { class: pg_1.Client, options: config_1.postgres_options };
60
+ return { class: GuardedClient, options: config_1.postgres_options };
39
61
  }