@hotmeshio/long-tail 0.20.0 → 0.21.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 (361) hide show
  1. package/README.md +4 -4
  2. package/build/api/escalations/accumulate.d.ts +104 -0
  3. package/build/api/escalations/accumulate.js +541 -0
  4. package/build/api/escalations/index.d.ts +1 -0
  5. package/build/api/escalations/index.js +11 -1
  6. package/build/api/roles.d.ts +4 -5
  7. package/build/api/roles.js +53 -4
  8. package/build/api/scan-codes/context.d.ts +14 -1
  9. package/build/api/scan-codes/context.js +30 -2
  10. package/build/api/scan-codes/execute.js +2 -0
  11. package/build/api/scan-codes/identity.js +1 -0
  12. package/build/api/scan-codes/verbs.d.ts +21 -0
  13. package/build/api/scan-codes/verbs.js +122 -3
  14. package/build/index.d.ts +2 -1
  15. package/build/index.js +63 -2
  16. package/build/lib/db/schemas/036_role_portals.sql +12 -0
  17. package/build/routes/escalations/accumulate.d.ts +7 -0
  18. package/build/routes/escalations/accumulate.js +158 -0
  19. package/build/routes/escalations/index.js +5 -0
  20. package/build/sdk/index.d.ts +28 -0
  21. package/build/sdk/index.js +9 -0
  22. package/build/services/escalation/accumulate.d.ts +81 -0
  23. package/build/services/escalation/accumulate.js +154 -0
  24. package/build/services/escalation/index.d.ts +1 -0
  25. package/build/services/escalation/index.js +1 -0
  26. package/build/services/mcp/playwright-server/schemas.d.ts +2 -2
  27. package/build/services/orchestrator/condition.d.ts +40 -3
  28. package/build/services/orchestrator/condition.js +42 -2
  29. package/build/services/role/index.js +1 -0
  30. package/build/services/role/seed.js +6 -1
  31. package/build/services/role/sql.d.ts +6 -3
  32. package/build/services/role/sql.js +7 -2
  33. package/build/services/role/types.d.ts +38 -10
  34. package/build/services/role/types.js +10 -1
  35. package/build/services/scan-code/index.d.ts +1 -1
  36. package/build/services/scan-code/index.js +3 -1
  37. package/build/services/scan-code/parse.d.ts +17 -3
  38. package/build/services/scan-code/parse.js +37 -4
  39. package/build/services/scan-code/validate.js +35 -0
  40. package/build/shared/form-validation/display-only-widgets.d.ts +16 -0
  41. package/build/shared/form-validation/display-only-widgets.js +25 -0
  42. package/build/shared/form-validation/index.d.ts +2 -0
  43. package/build/shared/form-validation/index.js +2 -0
  44. package/build/shared/form-validation/x-lt-bind.d.ts +2 -14
  45. package/build/shared/form-validation/x-lt-bind.js +12 -8
  46. package/build/shared/form-validation/x-lt-invoke.d.ts +38 -0
  47. package/build/shared/form-validation/x-lt-invoke.js +103 -0
  48. package/build/system/mcp-servers/admin/escalations.js +44 -0
  49. package/build/system/mcp-servers/admin/schemas.d.ts +195 -13
  50. package/build/system/mcp-servers/admin/schemas.js +40 -3
  51. package/build/system/mcp-servers/admin/users.js +1 -0
  52. package/build/system/mcp-servers/docs.js +4 -0
  53. package/build/system/mcp-servers/human-queue-schemas.d.ts +85 -0
  54. package/build/system/mcp-servers/human-queue-schemas.js +19 -1
  55. package/build/system/mcp-servers/human-queue.d.ts +2 -0
  56. package/build/system/mcp-servers/human-queue.js +77 -0
  57. package/build/system/seed/tool-manifests-admin.d.ts +1108 -100
  58. package/build/system/seed/tool-manifests-admin.js +4 -1
  59. package/build/system/seed/tool-manifests-escalation.d.ts +174 -0
  60. package/build/system/seed/tool-manifests-escalation.js +56 -0
  61. package/build/tsconfig.tsbuildinfo +1 -1
  62. package/build/types/escalation.d.ts +52 -0
  63. package/build/types/escalation.js +29 -1
  64. package/build/types/index.d.ts +4 -4
  65. package/build/types/index.js +4 -1
  66. package/build/types/scan-code.d.ts +55 -1
  67. package/build/types/scan-code.js +29 -2
  68. package/build/types/startup.d.ts +20 -0
  69. package/dashboard/dist/assets/{AdminDashboard-DplvDBti.js → AdminDashboard-Covdam48.js} +2 -2
  70. package/dashboard/dist/assets/{AdminDashboard-DplvDBti.js.map → AdminDashboard-Covdam48.js.map} +1 -1
  71. package/dashboard/dist/assets/{AgentConfigPage-D8Bf6YxO.js → AgentConfigPage-CE1k6J5r.js} +6 -6
  72. package/dashboard/dist/assets/{AgentConfigPage-D8Bf6YxO.js.map → AgentConfigPage-CE1k6J5r.js.map} +1 -1
  73. package/dashboard/dist/assets/{AgentDetailPage-BN8g0Y2G.js → AgentDetailPage-CWJZIZP1.js} +3 -3
  74. package/dashboard/dist/assets/{AgentDetailPage-BN8g0Y2G.js.map → AgentDetailPage-CWJZIZP1.js.map} +1 -1
  75. package/dashboard/dist/assets/AgentsPage---Dg5ufF.js +2 -0
  76. package/dashboard/dist/assets/{AgentsPage-7JJBHku8.js.map → AgentsPage---Dg5ufF.js.map} +1 -1
  77. package/dashboard/dist/assets/AvailableEscalationsPage-CpbC8oTN.js +2 -0
  78. package/dashboard/dist/assets/AvailableEscalationsPage-CpbC8oTN.js.map +1 -0
  79. package/dashboard/dist/assets/{BotPicker-CGCnIz8I.js → BotPicker-D3drO3-T.js} +2 -2
  80. package/dashboard/dist/assets/{BotPicker-CGCnIz8I.js.map → BotPicker-D3drO3-T.js.map} +1 -1
  81. package/dashboard/dist/assets/BulkAssignModal-BOAWsViq.js +2 -0
  82. package/dashboard/dist/assets/{BulkAssignModal-NJBcoj-f.js.map → BulkAssignModal-BOAWsViq.js.map} +1 -1
  83. package/dashboard/dist/assets/CapabilitiesPage-RGgD3YZO.js +2 -0
  84. package/dashboard/dist/assets/{CapabilitiesPage-F8dpNn9B.js.map → CapabilitiesPage-RGgD3YZO.js.map} +1 -1
  85. package/dashboard/dist/assets/{CollapsibleSection-BZW6VpCU.js → CollapsibleSection-DNkZn87n.js} +2 -2
  86. package/dashboard/dist/assets/{CollapsibleSection-BZW6VpCU.js.map → CollapsibleSection-DNkZn87n.js.map} +1 -1
  87. package/dashboard/dist/assets/{ConfirmDeleteModal-64adXLdq.js → ConfirmDeleteModal-BqzOwhPr.js} +2 -2
  88. package/dashboard/dist/assets/{ConfirmDeleteModal-64adXLdq.js.map → ConfirmDeleteModal-BqzOwhPr.js.map} +1 -1
  89. package/dashboard/dist/assets/{CountdownTimer-BxDub9d2.js → CountdownTimer-BaexQMK6.js} +2 -2
  90. package/dashboard/dist/assets/{CountdownTimer-BxDub9d2.js.map → CountdownTimer-BaexQMK6.js.map} +1 -1
  91. package/dashboard/dist/assets/{CredentialsPage-CnLzopLL.js → CredentialsPage-D7abp0aS.js} +2 -2
  92. package/dashboard/dist/assets/{CredentialsPage-CnLzopLL.js.map → CredentialsPage-D7abp0aS.js.map} +1 -1
  93. package/dashboard/dist/assets/{CronLabel-CAD5UYDT.js → CronLabel-DZiWmTwJ.js} +2 -2
  94. package/dashboard/dist/assets/{CronLabel-CAD5UYDT.js.map → CronLabel-DZiWmTwJ.js.map} +1 -1
  95. package/dashboard/dist/assets/DataTable-spX1dFyb.js +2 -0
  96. package/dashboard/dist/assets/DataTable-spX1dFyb.js.map +1 -0
  97. package/dashboard/dist/assets/{DropZone-XlcY8hEH.js → DropZone-f1o-GLM8.js} +2 -2
  98. package/dashboard/dist/assets/{DropZone-XlcY8hEH.js.map → DropZone-f1o-GLM8.js.map} +1 -1
  99. package/dashboard/dist/assets/{ElapsedCell-CwO4ufd0.js → ElapsedCell-BdGO_w4v.js} +2 -2
  100. package/dashboard/dist/assets/{ElapsedCell-CwO4ufd0.js.map → ElapsedCell-BdGO_w4v.js.map} +1 -1
  101. package/dashboard/dist/assets/EntityTimelinePanel-DIDcF5Yu.js +2 -0
  102. package/dashboard/dist/assets/{EntityTimelinePanel-CkF-oW-m.js.map → EntityTimelinePanel-DIDcF5Yu.js.map} +1 -1
  103. package/dashboard/dist/assets/{EscalationListSchemaPage-h9JO_q5f.js → EscalationListSchemaPage-C28rn9XC.js} +3 -3
  104. package/dashboard/dist/assets/{EscalationListSchemaPage-h9JO_q5f.js.map → EscalationListSchemaPage-C28rn9XC.js.map} +1 -1
  105. package/dashboard/dist/assets/EscalationListView-CkBnkpDJ.js +2 -0
  106. package/dashboard/dist/assets/EscalationListView-CkBnkpDJ.js.map +1 -0
  107. package/dashboard/dist/assets/{EscalationSchemaPage-mJFL5_Se.js → EscalationSchemaPage-W5r0A1kj.js} +3 -3
  108. package/dashboard/dist/assets/{EscalationSchemaPage-mJFL5_Se.js.map → EscalationSchemaPage-W5r0A1kj.js.map} +1 -1
  109. package/dashboard/dist/assets/EscalationTimeline-DKyTz6sM.js +2 -0
  110. package/dashboard/dist/assets/EscalationTimeline-DKyTz6sM.js.map +1 -0
  111. package/dashboard/dist/assets/EscalationTitleSelect-CzKfavri.js +2 -0
  112. package/dashboard/dist/assets/EscalationTitleSelect-CzKfavri.js.map +1 -0
  113. package/dashboard/dist/assets/{EscalationsOverview-DlXUFqWJ.js → EscalationsOverview-Tdu3Yx2c.js} +2 -2
  114. package/dashboard/dist/assets/{EscalationsOverview-DlXUFqWJ.js.map → EscalationsOverview-Tdu3Yx2c.js.map} +1 -1
  115. package/dashboard/dist/assets/{EventTable-Ike2xchn.js → EventTable-DiIxOJwO.js} +2 -2
  116. package/dashboard/dist/assets/{EventTable-Ike2xchn.js.map → EventTable-DiIxOJwO.js.map} +1 -1
  117. package/dashboard/dist/assets/{FilterBar-DMOR-O1c.js → FilterBar-CvMBCVIo.js} +2 -2
  118. package/dashboard/dist/assets/{FilterBar-DMOR-O1c.js.map → FilterBar-CvMBCVIo.js.map} +1 -1
  119. package/dashboard/dist/assets/GraphInvokePage-DO-yyhi6.js +2 -0
  120. package/dashboard/dist/assets/{GraphInvokePage-CKtvWowP.js.map → GraphInvokePage-DO-yyhi6.js.map} +1 -1
  121. package/dashboard/dist/assets/HomePage-DSS3eHR2.js +2 -0
  122. package/dashboard/dist/assets/{HomePage-BQ3Q791Q.js.map → HomePage-DSS3eHR2.js.map} +1 -1
  123. package/dashboard/dist/assets/ListToolbar-C3-Tqqub.js +2 -0
  124. package/dashboard/dist/assets/{ListToolbar-DdJzj472.js.map → ListToolbar-C3-Tqqub.js.map} +1 -1
  125. package/dashboard/dist/assets/{McpOverview-BbtrduWn.js → McpOverview-DEJmsdVi.js} +2 -2
  126. package/dashboard/dist/assets/{McpOverview-BbtrduWn.js.map → McpOverview-DEJmsdVi.js.map} +1 -1
  127. package/dashboard/dist/assets/{McpQueryDetailPage-C1L4T-Wt.js → McpQueryDetailPage-DYJr16l7.js} +2 -2
  128. package/dashboard/dist/assets/{McpQueryDetailPage-C1L4T-Wt.js.map → McpQueryDetailPage-DYJr16l7.js.map} +1 -1
  129. package/dashboard/dist/assets/McpQueryPage-BBcG_UuU.js +2 -0
  130. package/dashboard/dist/assets/{McpQueryPage-CfJy7HZ7.js.map → McpQueryPage-BBcG_UuU.js.map} +1 -1
  131. package/dashboard/dist/assets/{McpRunDetailPage-CA9yv7he.js → McpRunDetailPage-C6xw0kBg.js} +2 -2
  132. package/dashboard/dist/assets/{McpRunDetailPage-CA9yv7he.js.map → McpRunDetailPage-C6xw0kBg.js.map} +1 -1
  133. package/dashboard/dist/assets/McpRunsPage-CRQqrF4Q.js +2 -0
  134. package/dashboard/dist/assets/{McpRunsPage-D7QXjW9z.js.map → McpRunsPage-CRQqrF4Q.js.map} +1 -1
  135. package/dashboard/dist/assets/{NamespacePill-Bb3revqD.js → NamespacePill-DADRJOUl.js} +2 -2
  136. package/dashboard/dist/assets/{NamespacePill-Bb3revqD.js.map → NamespacePill-DADRJOUl.js.map} +1 -1
  137. package/dashboard/dist/assets/OperationsPage-Dlfwzf-0.js +3 -0
  138. package/dashboard/dist/assets/OperationsPage-Dlfwzf-0.js.map +1 -0
  139. package/dashboard/dist/assets/OperatorDashboard-DoiDGZ9R.js +2 -0
  140. package/dashboard/dist/assets/{OperatorDashboard-sXNDh1Ph.js.map → OperatorDashboard-DoiDGZ9R.js.map} +1 -1
  141. package/dashboard/dist/assets/{PageHeader-Di5Sz3an.js → PageHeader-Csq434-v.js} +2 -2
  142. package/dashboard/dist/assets/{PageHeader-Di5Sz3an.js.map → PageHeader-Csq434-v.js.map} +1 -1
  143. package/dashboard/dist/assets/{PageHeaderWithStats-DutKJ-h2.js → PageHeaderWithStats-CSnk1p5A.js} +2 -2
  144. package/dashboard/dist/assets/{PageHeaderWithStats-DutKJ-h2.js.map → PageHeaderWithStats-CSnk1p5A.js.map} +1 -1
  145. package/dashboard/dist/assets/{PersonaDetailPage-NeHD-Gra.js → PersonaDetailPage-BzbA4Prr.js} +2 -2
  146. package/dashboard/dist/assets/{PersonaDetailPage-NeHD-Gra.js.map → PersonaDetailPage-BzbA4Prr.js.map} +1 -1
  147. package/dashboard/dist/assets/{PersonasPage-DavKXRWX.js → PersonasPage-B36yn4hU.js} +2 -2
  148. package/dashboard/dist/assets/{PersonasPage-DavKXRWX.js.map → PersonasPage-B36yn4hU.js.map} +1 -1
  149. package/dashboard/dist/assets/PortalPage-BkD4YfDf.js +2 -0
  150. package/dashboard/dist/assets/PortalPage-BkD4YfDf.js.map +1 -0
  151. package/dashboard/dist/assets/{ProcessDetailPage-DkdKJGe6.js → ProcessDetailPage-BT0eiBWY.js} +2 -2
  152. package/dashboard/dist/assets/{ProcessDetailPage-DkdKJGe6.js.map → ProcessDetailPage-BT0eiBWY.js.map} +1 -1
  153. package/dashboard/dist/assets/ProcessesListPage-BEqeeNae.js +2 -0
  154. package/dashboard/dist/assets/{ProcessesListPage-CjwVKe32.js.map → ProcessesListPage-BEqeeNae.js.map} +1 -1
  155. package/dashboard/dist/assets/ResolverForm-BwgjIX97.js +7 -0
  156. package/dashboard/dist/assets/ResolverForm-BwgjIX97.js.map +1 -0
  157. package/dashboard/dist/assets/RoleDetailPage-BFuLNJXa.js +8 -0
  158. package/dashboard/dist/assets/RoleDetailPage-BFuLNJXa.js.map +1 -0
  159. package/dashboard/dist/assets/{RolePill-DhnVPK4q.js → RolePill-BafrASDX.js} +2 -2
  160. package/dashboard/dist/assets/{RolePill-DhnVPK4q.js.map → RolePill-BafrASDX.js.map} +1 -1
  161. package/dashboard/dist/assets/{RolesPage-OM9KM8Pg.js → RolesPage-80quJU5-.js} +2 -2
  162. package/dashboard/dist/assets/{RolesPage-OM9KM8Pg.js.map → RolesPage-80quJU5-.js.map} +1 -1
  163. package/dashboard/dist/assets/RunAsSelector-BoePhWVD.js +2 -0
  164. package/dashboard/dist/assets/{RunAsSelector-fFMt1bGe.js.map → RunAsSelector-BoePhWVD.js.map} +1 -1
  165. package/dashboard/dist/assets/{ScanCodesPage-AujH-pPQ.js → ScanCodesPage-Cu1bKEix.js} +2 -2
  166. package/dashboard/dist/assets/{ScanCodesPage-AujH-pPQ.js.map → ScanCodesPage-Cu1bKEix.js.map} +1 -1
  167. package/dashboard/dist/assets/ScanSchemeDetailPage-D9AbS6UY.js +2 -0
  168. package/dashboard/dist/assets/ScanSchemeDetailPage-D9AbS6UY.js.map +1 -0
  169. package/dashboard/dist/assets/ScanStationPage-gcZGefvT.js +2 -0
  170. package/dashboard/dist/assets/{ScanStationPage-C1_ppDoZ.js.map → ScanStationPage-gcZGefvT.js.map} +1 -1
  171. package/dashboard/dist/assets/{ScopeBadge-CXUg0FHo.js → ScopeBadge-b6MIBiYD.js} +2 -2
  172. package/dashboard/dist/assets/{ScopeBadge-CXUg0FHo.js.map → ScopeBadge-b6MIBiYD.js.map} +1 -1
  173. package/dashboard/dist/assets/{StickyPagination-B91Pk9xh.js → StickyPagination-9ITrei_d.js} +2 -2
  174. package/dashboard/dist/assets/{StickyPagination-B91Pk9xh.js.map → StickyPagination-9ITrei_d.js.map} +1 -1
  175. package/dashboard/dist/assets/{StreamMessageDetail-C7SPqaJt.js → StreamMessageDetail-BkYvEHFY.js} +2 -2
  176. package/dashboard/dist/assets/{StreamMessageDetail-C7SPqaJt.js.map → StreamMessageDetail-BkYvEHFY.js.map} +1 -1
  177. package/dashboard/dist/assets/{SwimlaneTimeline-BcHR8ylf.js → SwimlaneTimeline-B1rotFix.js} +2 -2
  178. package/dashboard/dist/assets/{SwimlaneTimeline-BcHR8ylf.js.map → SwimlaneTimeline-B1rotFix.js.map} +1 -1
  179. package/dashboard/dist/assets/{TagInput-BZ8vb2oW.js → TagInput-ZrIIrEUP.js} +2 -2
  180. package/dashboard/dist/assets/{TagInput-BZ8vb2oW.js.map → TagInput-ZrIIrEUP.js.map} +1 -1
  181. package/dashboard/dist/assets/{TaskDetailPage-Bqj0I_I0.js → TaskDetailPage-zhn_U_eC.js} +2 -2
  182. package/dashboard/dist/assets/{TaskDetailPage-Bqj0I_I0.js.map → TaskDetailPage-zhn_U_eC.js.map} +1 -1
  183. package/dashboard/dist/assets/{TaskQueuePill-Bxir8WRl.js → TaskQueuePill-DkQ1uc6y.js} +2 -2
  184. package/dashboard/dist/assets/{TaskQueuePill-Bxir8WRl.js.map → TaskQueuePill-DkQ1uc6y.js.map} +1 -1
  185. package/dashboard/dist/assets/TasksListPage-CNbNeagx.js +2 -0
  186. package/dashboard/dist/assets/{TasksListPage-BB1f79lL.js.map → TasksListPage-CNbNeagx.js.map} +1 -1
  187. package/dashboard/dist/assets/{TimeAgo-BFnODmNe.js → TimeAgo-0YVMBwls.js} +2 -2
  188. package/dashboard/dist/assets/{TimeAgo-BFnODmNe.js.map → TimeAgo-0YVMBwls.js.map} +1 -1
  189. package/dashboard/dist/assets/{TimestampCell-BXgLOBsr.js → TimestampCell-DB7r3np8.js} +2 -2
  190. package/dashboard/dist/assets/{TimestampCell-BXgLOBsr.js.map → TimestampCell-DB7r3np8.js.map} +1 -1
  191. package/dashboard/dist/assets/{ToolPill-FkgTXIhq.js → ToolPill-Baxm02L6.js} +2 -2
  192. package/dashboard/dist/assets/{ToolPill-FkgTXIhq.js.map → ToolPill-Baxm02L6.js.map} +1 -1
  193. package/dashboard/dist/assets/ToolTestPanel-BLfA02wv.js +2 -0
  194. package/dashboard/dist/assets/{ToolTestPanel-Bwe3J5lj.js.map → ToolTestPanel-BLfA02wv.js.map} +1 -1
  195. package/dashboard/dist/assets/{TopicDetailPage-DAF07vZ9.js → TopicDetailPage-eAqPKShq.js} +3 -3
  196. package/dashboard/dist/assets/{TopicDetailPage-DAF07vZ9.js.map → TopicDetailPage-eAqPKShq.js.map} +1 -1
  197. package/dashboard/dist/assets/TopicsPage-BNNWD-zl.js +2 -0
  198. package/dashboard/dist/assets/{TopicsPage-C1gxBUap.js.map → TopicsPage-BNNWD-zl.js.map} +1 -1
  199. package/dashboard/dist/assets/{UserName-BzXJUE6q.js → UserName-Pw46tO_k.js} +2 -2
  200. package/dashboard/dist/assets/{UserName-BzXJUE6q.js.map → UserName-Pw46tO_k.js.map} +1 -1
  201. package/dashboard/dist/assets/{WorkflowExecutionPage-SkRU5cRX.js → WorkflowExecutionPage-DvA51lT3.js} +2 -2
  202. package/dashboard/dist/assets/{WorkflowExecutionPage-SkRU5cRX.js.map → WorkflowExecutionPage-DvA51lT3.js.map} +1 -1
  203. package/dashboard/dist/assets/{WorkflowPill--AYMW7u_.js → WorkflowPill-ZepbO_ny.js} +2 -2
  204. package/dashboard/dist/assets/{WorkflowPill--AYMW7u_.js.map → WorkflowPill-ZepbO_ny.js.map} +1 -1
  205. package/dashboard/dist/assets/WorkflowsDashboard-DFDTwnhn.js +2 -0
  206. package/dashboard/dist/assets/{WorkflowsDashboard-CZAqRk1N.js.map → WorkflowsDashboard-DFDTwnhn.js.map} +1 -1
  207. package/dashboard/dist/assets/{WorkflowsOverview-BoN44VTE.js → WorkflowsOverview-5i2Tcy-p.js} +2 -2
  208. package/dashboard/dist/assets/{WorkflowsOverview-BoN44VTE.js.map → WorkflowsOverview-5i2Tcy-p.js.map} +1 -1
  209. package/dashboard/dist/assets/{YamlWorkflowDetailPage-CqjhtdJf.js → YamlWorkflowDetailPage-p4VEYZS0.js} +11 -11
  210. package/dashboard/dist/assets/{YamlWorkflowDetailPage-CqjhtdJf.js.map → YamlWorkflowDetailPage-p4VEYZS0.js.map} +1 -1
  211. package/dashboard/dist/assets/{YamlWorkflowsPage-CwnJkzyb.js → YamlWorkflowsPage-Cq-hMVxl.js} +2 -2
  212. package/dashboard/dist/assets/{YamlWorkflowsPage-CwnJkzyb.js.map → YamlWorkflowsPage-Cq-hMVxl.js.map} +1 -1
  213. package/dashboard/dist/assets/{agents-gV0Ys-sY.js → agents-EyBPJMYa.js} +2 -2
  214. package/dashboard/dist/assets/{agents-gV0Ys-sY.js.map → agents-EyBPJMYa.js.map} +1 -1
  215. package/dashboard/dist/assets/{bots-B97itB3I.js → bots-DX6jjqtF.js} +2 -2
  216. package/dashboard/dist/assets/{bots-B97itB3I.js.map → bots-DX6jjqtF.js.map} +1 -1
  217. package/dashboard/dist/assets/{capabilities-CR9xZgGd.js → capabilities-ZdLv9xBQ.js} +2 -2
  218. package/dashboard/dist/assets/{capabilities-CR9xZgGd.js.map → capabilities-ZdLv9xBQ.js.map} +1 -1
  219. package/dashboard/dist/assets/{constants-BHkpVaqx.js → constants-CeBBTzf5.js} +2 -2
  220. package/dashboard/dist/assets/{constants-BHkpVaqx.js.map → constants-CeBBTzf5.js.map} +1 -1
  221. package/dashboard/dist/assets/{controlplane-YxvP1KCa.js → controlplane-CpvZaJuw.js} +2 -2
  222. package/dashboard/dist/assets/{controlplane-YxvP1KCa.js.map → controlplane-CpvZaJuw.js.map} +1 -1
  223. package/dashboard/dist/assets/escalation-columns-Mvsze5eV.js +2 -0
  224. package/dashboard/dist/assets/escalation-columns-Mvsze5eV.js.map +1 -0
  225. package/dashboard/dist/assets/index-B_GT23TE.js +3 -0
  226. package/dashboard/dist/assets/index-B_GT23TE.js.map +1 -0
  227. package/dashboard/dist/assets/index-BbCjGO3M.js +2 -0
  228. package/dashboard/dist/assets/{index-CFbo9FU9.js.map → index-BbCjGO3M.js.map} +1 -1
  229. package/dashboard/dist/assets/{index-DMeaiKgf.js → index-Bhb89oHH.js} +2 -2
  230. package/dashboard/dist/assets/{index-DMeaiKgf.js.map → index-Bhb89oHH.js.map} +1 -1
  231. package/dashboard/dist/assets/index-CgUcYveM.js +67 -0
  232. package/dashboard/dist/assets/index-CgUcYveM.js.map +1 -0
  233. package/dashboard/dist/assets/index-Cn8uJsd7.js +2 -0
  234. package/dashboard/dist/assets/{index-BQpV-DTG.js.map → index-Cn8uJsd7.js.map} +1 -1
  235. package/dashboard/dist/assets/index-D-Xs4rAW.js +2 -0
  236. package/dashboard/dist/assets/{index-Bp9KBLT5.js.map → index-D-Xs4rAW.js.map} +1 -1
  237. package/dashboard/dist/assets/{index-DwOnWeu8.js → index-DGmzBY9D.js} +2 -2
  238. package/dashboard/dist/assets/{index-DwOnWeu8.js.map → index-DGmzBY9D.js.map} +1 -1
  239. package/dashboard/dist/assets/{index-CIpHj4di.js → index-DZhtP2Ok.js} +2 -2
  240. package/dashboard/dist/assets/{index-CIpHj4di.js.map → index-DZhtP2Ok.js.map} +1 -1
  241. package/dashboard/dist/assets/index-Dh8MJl3L.css +1 -0
  242. package/dashboard/dist/assets/index-DkrBFFSO.js +13 -0
  243. package/dashboard/dist/assets/{index-Cfvkl2J0.js.map → index-DkrBFFSO.js.map} +1 -1
  244. package/dashboard/dist/assets/{index-B9BBCqLI.js → index-Eo-l3Lqo.js} +2 -2
  245. package/dashboard/dist/assets/{index-B9BBCqLI.js.map → index-Eo-l3Lqo.js.map} +1 -1
  246. package/dashboard/dist/assets/index-PfSRKerc.js +2 -0
  247. package/dashboard/dist/assets/{index-CUZ6LYye.js.map → index-PfSRKerc.js.map} +1 -1
  248. package/dashboard/dist/assets/{index-ipqwPavs.js → index-oFKUFGQL.js} +2 -2
  249. package/dashboard/dist/assets/{index-ipqwPavs.js.map → index-oFKUFGQL.js.map} +1 -1
  250. package/dashboard/dist/assets/index-z5Hpeidu.js +2 -0
  251. package/dashboard/dist/assets/index-z5Hpeidu.js.map +1 -0
  252. package/dashboard/dist/assets/{knowledge-XX6cW4Ob.js → knowledge-CgloEkaf.js} +2 -2
  253. package/dashboard/dist/assets/{knowledge-XX6cW4Ob.js.map → knowledge-CgloEkaf.js.map} +1 -1
  254. package/dashboard/dist/assets/{mcp-CH13hNH2.js → mcp-Bix7QYO7.js} +2 -2
  255. package/dashboard/dist/assets/{mcp-CH13hNH2.js.map → mcp-Bix7QYO7.js.map} +1 -1
  256. package/dashboard/dist/assets/{mcp-query-Duydd3JA.js → mcp-query-o-sGX2ST.js} +2 -2
  257. package/dashboard/dist/assets/{mcp-query-Duydd3JA.js.map → mcp-query-o-sGX2ST.js.map} +1 -1
  258. package/dashboard/dist/assets/metadata-display-COo3mMCr.js +2 -0
  259. package/dashboard/dist/assets/metadata-display-COo3mMCr.js.map +1 -0
  260. package/dashboard/dist/assets/{personas-mDk4lLQS.js → personas-30azkJ_s.js} +2 -2
  261. package/dashboard/dist/assets/{personas-mDk4lLQS.js.map → personas-30azkJ_s.js.map} +1 -1
  262. package/dashboard/dist/assets/{pipelines-B5GcgCeF.js → pipelines-Bfk2GFK7.js} +2 -2
  263. package/dashboard/dist/assets/{pipelines-B5GcgCeF.js.map → pipelines-Bfk2GFK7.js.map} +1 -1
  264. package/dashboard/dist/assets/{tasks-CItmNsRp.js → tasks-CpdopewW.js} +2 -2
  265. package/dashboard/dist/assets/{tasks-CItmNsRp.js.map → tasks-CpdopewW.js.map} +1 -1
  266. package/dashboard/dist/assets/{topics-CJYG7BX5.js → topics--bvCV9Yp.js} +2 -2
  267. package/dashboard/dist/assets/{topics-CJYG7BX5.js.map → topics--bvCV9Yp.js.map} +1 -1
  268. package/dashboard/dist/assets/useContainerWidth-DSLp1gEm.js +2 -0
  269. package/dashboard/dist/assets/useContainerWidth-DSLp1gEm.js.map +1 -0
  270. package/dashboard/dist/assets/{useDebouncedValue-BrwgXcp8.js → useDebouncedValue-Da0lPmj3.js} +2 -2
  271. package/dashboard/dist/assets/{useDebouncedValue-BrwgXcp8.js.map → useDebouncedValue-Da0lPmj3.js.map} +1 -1
  272. package/dashboard/dist/assets/{useNamespace-CHty0HAS.js → useNamespace-DD_Esl6n.js} +2 -2
  273. package/dashboard/dist/assets/{useNamespace-CHty0HAS.js.map → useNamespace-DD_Esl6n.js.map} +1 -1
  274. package/dashboard/dist/assets/{useYamlActivityEvents-CoTsia7t.js → useYamlActivityEvents-AeAhZJJk.js} +2 -2
  275. package/dashboard/dist/assets/{useYamlActivityEvents-CoTsia7t.js.map → useYamlActivityEvents-AeAhZJJk.js.map} +1 -1
  276. package/dashboard/dist/assets/{vendor-icons-DeBdvyKJ.js → vendor-icons-B7OcUM6b.js} +2 -2
  277. package/dashboard/dist/assets/{vendor-icons-DeBdvyKJ.js.map → vendor-icons-B7OcUM6b.js.map} +1 -1
  278. package/dashboard/dist/assets/{yaml-workflows-CiWwx5rM.js → yaml-workflows-BHAOOGvn.js} +2 -2
  279. package/dashboard/dist/assets/{yaml-workflows-CiWwx5rM.js.map → yaml-workflows-BHAOOGvn.js.map} +1 -1
  280. package/dashboard/dist/index.html +3 -3
  281. package/docs/accumulator.md +113 -0
  282. package/docs/api/http/escalations.md +90 -0
  283. package/docs/api/http/roles.md +1 -0
  284. package/docs/api/sdk/escalations.md +92 -0
  285. package/docs/api/sdk/roles.md +2 -0
  286. package/docs/architecture.md +1 -1
  287. package/docs/code-owned-configuration.md +2 -1
  288. package/docs/dashboard.md +10 -4
  289. package/docs/hitl/design.md +1 -1
  290. package/docs/hitl/escalation.md +37 -1
  291. package/docs/hitl/invoke-form.md +4 -0
  292. package/docs/hitl/pinned-views.md +4 -0
  293. package/docs/hitl/portal.md +97 -0
  294. package/docs/hitl/resolution.md +41 -0
  295. package/docs/hitl/x-lt-embed.md +1 -1
  296. package/docs/hitl/x-lt-invoke.md +95 -0
  297. package/docs/hitl/x-lt-list-schema.md +3 -1
  298. package/docs/hitl/x-lt-widget.md +1 -0
  299. package/docs/hitl-guide.md +8 -1
  300. package/docs/scan-codes.md +33 -9
  301. package/docs/sdk.md +1 -1
  302. package/docs/workflows.md +2 -2
  303. package/package.json +4 -2
  304. package/dashboard/dist/assets/AgentsPage-7JJBHku8.js +0 -2
  305. package/dashboard/dist/assets/AvailableEscalationsPage-lz5qwSjg.js +0 -2
  306. package/dashboard/dist/assets/AvailableEscalationsPage-lz5qwSjg.js.map +0 -1
  307. package/dashboard/dist/assets/BulkAssignModal-NJBcoj-f.js +0 -2
  308. package/dashboard/dist/assets/CapabilitiesPage-F8dpNn9B.js +0 -2
  309. package/dashboard/dist/assets/DataTable-7C7riavf.js +0 -2
  310. package/dashboard/dist/assets/DataTable-7C7riavf.js.map +0 -1
  311. package/dashboard/dist/assets/EntityTimelinePanel-CkF-oW-m.js +0 -2
  312. package/dashboard/dist/assets/EscalationListView-AHW2xtlr.js +0 -2
  313. package/dashboard/dist/assets/EscalationListView-AHW2xtlr.js.map +0 -1
  314. package/dashboard/dist/assets/GraphInvokePage-CKtvWowP.js +0 -2
  315. package/dashboard/dist/assets/HomePage-BQ3Q791Q.js +0 -2
  316. package/dashboard/dist/assets/ListToolbar-DdJzj472.js +0 -2
  317. package/dashboard/dist/assets/McpQueryPage-CfJy7HZ7.js +0 -2
  318. package/dashboard/dist/assets/McpRunsPage-D7QXjW9z.js +0 -2
  319. package/dashboard/dist/assets/OperationsPage-aLHHAP2X.js +0 -3
  320. package/dashboard/dist/assets/OperationsPage-aLHHAP2X.js.map +0 -1
  321. package/dashboard/dist/assets/OperatorDashboard-sXNDh1Ph.js +0 -2
  322. package/dashboard/dist/assets/ProcessesListPage-CjwVKe32.js +0 -2
  323. package/dashboard/dist/assets/ResolverForm-Bk-wDkN-.js +0 -3
  324. package/dashboard/dist/assets/ResolverForm-Bk-wDkN-.js.map +0 -1
  325. package/dashboard/dist/assets/RoleDetailPage-WQt0y5l7.js +0 -8
  326. package/dashboard/dist/assets/RoleDetailPage-WQt0y5l7.js.map +0 -1
  327. package/dashboard/dist/assets/RunAsSelector-fFMt1bGe.js +0 -2
  328. package/dashboard/dist/assets/ScanSchemeDetailPage-eKQpmsC_.js +0 -2
  329. package/dashboard/dist/assets/ScanSchemeDetailPage-eKQpmsC_.js.map +0 -1
  330. package/dashboard/dist/assets/ScanStationPage-C1_ppDoZ.js +0 -2
  331. package/dashboard/dist/assets/TasksListPage-BB1f79lL.js +0 -2
  332. package/dashboard/dist/assets/ToolTestPanel-Bwe3J5lj.js +0 -2
  333. package/dashboard/dist/assets/TopicsPage-C1gxBUap.js +0 -2
  334. package/dashboard/dist/assets/WorkflowsDashboard-CZAqRk1N.js +0 -2
  335. package/dashboard/dist/assets/escalation-columns-uq-IV2aj.js +0 -2
  336. package/dashboard/dist/assets/escalation-columns-uq-IV2aj.js.map +0 -1
  337. package/dashboard/dist/assets/index-3ezfWLkE.js +0 -6
  338. package/dashboard/dist/assets/index-3ezfWLkE.js.map +0 -1
  339. package/dashboard/dist/assets/index-BQpV-DTG.js +0 -2
  340. package/dashboard/dist/assets/index-Bp9KBLT5.js +0 -2
  341. package/dashboard/dist/assets/index-Bpvfr4OW.css +0 -1
  342. package/dashboard/dist/assets/index-CFbo9FU9.js +0 -2
  343. package/dashboard/dist/assets/index-CIwlVfvV.js +0 -67
  344. package/dashboard/dist/assets/index-CIwlVfvV.js.map +0 -1
  345. package/dashboard/dist/assets/index-CUZ6LYye.js +0 -2
  346. package/dashboard/dist/assets/index-Cfvkl2J0.js +0 -13
  347. package/dashboard/dist/assets/index-SGyrXGp8.js +0 -3
  348. package/dashboard/dist/assets/index-SGyrXGp8.js.map +0 -1
  349. package/dashboard/dist/assets/invoke-context-DrVyUEfs.js +0 -2
  350. package/dashboard/dist/assets/invoke-context-DrVyUEfs.js.map +0 -1
  351. package/dashboard/dist/assets/metadata-display--TzK0REC.js +0 -2
  352. package/dashboard/dist/assets/metadata-display--TzK0REC.js.map +0 -1
  353. package/dashboard/dist/assets/resolver-payload-m9AGKZ3a.js +0 -2
  354. package/dashboard/dist/assets/resolver-payload-m9AGKZ3a.js.map +0 -1
  355. package/docs/compilation.md +0 -152
  356. package/docs/compiler.md +0 -311
  357. package/docs/contributing.md +0 -56
  358. package/docs/design-principles.md +0 -245
  359. package/docs/schema-exchange.md +0 -173
  360. package/docs/self-test.md +0 -106
  361. package/docs/story.md +0 -184
@@ -1,2 +1,2 @@
1
- import{u as l,b as a,c as s}from"./vendor-query-DvWWuNIB.js";import{m as r}from"./index-CIwlVfvV.js";function f(e={}){const o=new URLSearchParams;return e.status&&o.set("status",e.status),e.graph_topic&&o.set("graph_topic",e.graph_topic),e.app_id&&o.set("app_id",e.app_id),e.search&&o.set("search",e.search),e.set_id&&o.set("set_id",e.set_id),e.limit&&o.set("limit",String(e.limit)),e.offset!==void 0&&o.set("offset",String(e.offset)),l({queryKey:["yamlWorkflows",e],queryFn:()=>r(`/yaml-workflows?${o}`)})}function m(){return l({queryKey:["yamlWorkflowAppIds"],queryFn:()=>r("/yaml-workflows/app-ids")})}function c(e){return l({queryKey:["yamlWorkflows",e],queryFn:()=>r(`/yaml-workflows/${e}`),enabled:!!e})}function w(){const e=a();return s({mutationFn:o=>r("/yaml-workflows",{method:"POST",body:JSON.stringify(o)}),onSuccess:()=>{e.invalidateQueries({queryKey:["yamlWorkflows"],refetchType:"all"})}})}function k(){const e=a();return s({mutationFn:o=>r(`/yaml-workflows/${o}/deploy`,{method:"POST"}),onSuccess:()=>{e.invalidateQueries({queryKey:["yamlWorkflows"],refetchType:"all"})}})}function p(){const e=a();return s({mutationFn:o=>r(`/yaml-workflows/${o}/activate`,{method:"POST"}),onSuccess:()=>{e.invalidateQueries({queryKey:["yamlWorkflows"],refetchType:"all"})}})}function d(){const e=a();return s({mutationFn:({id:o,data:t,sync:n,execute_as:u})=>r(`/yaml-workflows/${o}/invoke`,{method:"POST",body:JSON.stringify({data:t,sync:n,...u?{execute_as:u}:{}})}),onSuccess:()=>{e.invalidateQueries({queryKey:["yamlWorkflows"],refetchType:"all"})}})}function h(){const e=a();return s({mutationFn:({id:o,task_queue:t,compilation_feedback:n})=>r(`/yaml-workflows/${o}/regenerate`,{method:"POST",body:JSON.stringify({task_queue:t,compilation_feedback:n})}),onSuccess:()=>{e.invalidateQueries({queryKey:["yamlWorkflows"],refetchType:"all"})}})}function q(){const e=a();return s({mutationFn:o=>r(`/yaml-workflows/${o}/archive`,{method:"POST"}),onSuccess:()=>{e.invalidateQueries({queryKey:["yamlWorkflows"],refetchType:"all"})}})}function W(){const e=a();return s({mutationFn:o=>r(`/yaml-workflows/${o}/restore`,{method:"POST"}),onSuccess:()=>{e.invalidateQueries({queryKey:["yamlWorkflows"],refetchType:"all"}),e.invalidateQueries({queryKey:["yamlWorkflow"],refetchType:"all"})}})}function S(){const e=a();return s({mutationFn:({id:o,...t})=>r(`/yaml-workflows/${o}`,{method:"PUT",body:JSON.stringify(t)}),onSuccess:()=>{e.invalidateQueries({queryKey:["yamlWorkflows"],refetchType:"all"})}})}function v(){const e=a();return s({mutationFn:o=>r(`/yaml-workflows/${o}`,{method:"DELETE"}),onSuccess:()=>{e.invalidateQueries({queryKey:["yamlWorkflows"],refetchType:"all"})}})}function T(e){return l({queryKey:["yamlWorkflows",e,"versions"],queryFn:()=>r(`/yaml-workflows/${e}/versions`),enabled:!!e})}function F(e,o){return l({queryKey:["yamlWorkflows",e,"versions",o],queryFn:()=>r(`/yaml-workflows/${e}/versions/${o}`),enabled:!!e&&o!==null})}export{m as a,S as b,c,T as d,k as e,p as f,q as g,W as h,v as i,h as j,F as k,d as l,w as m,f as u};
2
- //# sourceMappingURL=yaml-workflows-CiWwx5rM.js.map
1
+ import{u as l,b as a,c as s}from"./vendor-query-DvWWuNIB.js";import{m as r}from"./index-CgUcYveM.js";function f(e={}){const o=new URLSearchParams;return e.status&&o.set("status",e.status),e.graph_topic&&o.set("graph_topic",e.graph_topic),e.app_id&&o.set("app_id",e.app_id),e.search&&o.set("search",e.search),e.set_id&&o.set("set_id",e.set_id),e.limit&&o.set("limit",String(e.limit)),e.offset!==void 0&&o.set("offset",String(e.offset)),l({queryKey:["yamlWorkflows",e],queryFn:()=>r(`/yaml-workflows?${o}`)})}function m(){return l({queryKey:["yamlWorkflowAppIds"],queryFn:()=>r("/yaml-workflows/app-ids")})}function c(e){return l({queryKey:["yamlWorkflows",e],queryFn:()=>r(`/yaml-workflows/${e}`),enabled:!!e})}function w(){const e=a();return s({mutationFn:o=>r("/yaml-workflows",{method:"POST",body:JSON.stringify(o)}),onSuccess:()=>{e.invalidateQueries({queryKey:["yamlWorkflows"],refetchType:"all"})}})}function k(){const e=a();return s({mutationFn:o=>r(`/yaml-workflows/${o}/deploy`,{method:"POST"}),onSuccess:()=>{e.invalidateQueries({queryKey:["yamlWorkflows"],refetchType:"all"})}})}function p(){const e=a();return s({mutationFn:o=>r(`/yaml-workflows/${o}/activate`,{method:"POST"}),onSuccess:()=>{e.invalidateQueries({queryKey:["yamlWorkflows"],refetchType:"all"})}})}function d(){const e=a();return s({mutationFn:({id:o,data:t,sync:n,execute_as:u})=>r(`/yaml-workflows/${o}/invoke`,{method:"POST",body:JSON.stringify({data:t,sync:n,...u?{execute_as:u}:{}})}),onSuccess:()=>{e.invalidateQueries({queryKey:["yamlWorkflows"],refetchType:"all"})}})}function h(){const e=a();return s({mutationFn:({id:o,task_queue:t,compilation_feedback:n})=>r(`/yaml-workflows/${o}/regenerate`,{method:"POST",body:JSON.stringify({task_queue:t,compilation_feedback:n})}),onSuccess:()=>{e.invalidateQueries({queryKey:["yamlWorkflows"],refetchType:"all"})}})}function q(){const e=a();return s({mutationFn:o=>r(`/yaml-workflows/${o}/archive`,{method:"POST"}),onSuccess:()=>{e.invalidateQueries({queryKey:["yamlWorkflows"],refetchType:"all"})}})}function W(){const e=a();return s({mutationFn:o=>r(`/yaml-workflows/${o}/restore`,{method:"POST"}),onSuccess:()=>{e.invalidateQueries({queryKey:["yamlWorkflows"],refetchType:"all"}),e.invalidateQueries({queryKey:["yamlWorkflow"],refetchType:"all"})}})}function S(){const e=a();return s({mutationFn:({id:o,...t})=>r(`/yaml-workflows/${o}`,{method:"PUT",body:JSON.stringify(t)}),onSuccess:()=>{e.invalidateQueries({queryKey:["yamlWorkflows"],refetchType:"all"})}})}function v(){const e=a();return s({mutationFn:o=>r(`/yaml-workflows/${o}`,{method:"DELETE"}),onSuccess:()=>{e.invalidateQueries({queryKey:["yamlWorkflows"],refetchType:"all"})}})}function T(e){return l({queryKey:["yamlWorkflows",e,"versions"],queryFn:()=>r(`/yaml-workflows/${e}/versions`),enabled:!!e})}function F(e,o){return l({queryKey:["yamlWorkflows",e,"versions",o],queryFn:()=>r(`/yaml-workflows/${e}/versions/${o}`),enabled:!!e&&o!==null})}export{m as a,S as b,c,T as d,k as e,p as f,q as g,W as h,v as i,h as j,F as k,d as l,w as m,f as u};
2
+ //# sourceMappingURL=yaml-workflows-BHAOOGvn.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"yaml-workflows-CiWwx5rM.js","sources":["../../src/api/yaml-workflows.ts"],"sourcesContent":["import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';\nimport { apiFetch } from './client';\nimport type { LTYamlWorkflowRecord, LTYamlWorkflowStatus, LTYamlWorkflowVersion } from './types';\n\ninterface YamlWorkflowListResponse {\n workflows: LTYamlWorkflowRecord[];\n total: number;\n}\n\ninterface YamlWorkflowFilters {\n status?: LTYamlWorkflowStatus;\n graph_topic?: string;\n app_id?: string;\n search?: string;\n set_id?: string;\n limit?: number;\n offset?: number;\n}\n\nexport function useYamlWorkflows(filters: YamlWorkflowFilters = {}) {\n const params = new URLSearchParams();\n if (filters.status) params.set('status', filters.status);\n if (filters.graph_topic) params.set('graph_topic', filters.graph_topic);\n if (filters.app_id) params.set('app_id', filters.app_id);\n if (filters.search) params.set('search', filters.search);\n if (filters.set_id) params.set('set_id', filters.set_id);\n if (filters.limit) params.set('limit', String(filters.limit));\n if (filters.offset !== undefined) params.set('offset', String(filters.offset));\n\n return useQuery<YamlWorkflowListResponse>({\n queryKey: ['yamlWorkflows', filters],\n queryFn: () => apiFetch(`/yaml-workflows?${params}`),\n });\n}\n\nexport function useYamlWorkflowAppIds() {\n return useQuery<{ app_ids: string[] }>({\n queryKey: ['yamlWorkflowAppIds'],\n queryFn: () => apiFetch('/yaml-workflows/app-ids'),\n });\n}\n\nexport function useYamlWorkflowByTopic(graphTopic: string | undefined, appId?: string) {\n return useQuery<YamlWorkflowListResponse>({\n queryKey: ['yamlWorkflows', 'byTopic', graphTopic, appId],\n queryFn: () => {\n const params = new URLSearchParams({ graph_topic: graphTopic!, limit: '1' });\n if (appId) params.set('app_id', appId);\n return apiFetch(`/yaml-workflows?${params}`);\n },\n enabled: !!graphTopic,\n });\n}\n\nexport function useYamlWorkflow(id: string) {\n return useQuery<LTYamlWorkflowRecord>({\n queryKey: ['yamlWorkflows', id],\n queryFn: () => apiFetch(`/yaml-workflows/${id}`),\n enabled: !!id,\n });\n}\n\nexport function useCreateYamlWorkflow() {\n const queryClient = useQueryClient();\n return useMutation({\n mutationFn: (input: {\n workflow_id: string;\n task_queue: string;\n workflow_name: string;\n name: string;\n description?: string;\n app_id?: string;\n subscribes?: string;\n tags?: string[];\n compilation_feedback?: string;\n }) =>\n apiFetch<LTYamlWorkflowRecord>('/yaml-workflows', {\n method: 'POST',\n body: JSON.stringify(input),\n }),\n onSuccess: () => {\n queryClient.invalidateQueries({ queryKey: ['yamlWorkflows'], refetchType: 'all' });\n },\n });\n}\n\nexport function useDeployYamlWorkflow() {\n const queryClient = useQueryClient();\n return useMutation({\n mutationFn: (id: string) =>\n apiFetch<LTYamlWorkflowRecord>(`/yaml-workflows/${id}/deploy`, {\n method: 'POST',\n }),\n onSuccess: () => {\n queryClient.invalidateQueries({ queryKey: ['yamlWorkflows'], refetchType: 'all' });\n },\n });\n}\n\nexport function useActivateYamlWorkflow() {\n const queryClient = useQueryClient();\n return useMutation({\n mutationFn: (id: string) =>\n apiFetch<LTYamlWorkflowRecord>(`/yaml-workflows/${id}/activate`, {\n method: 'POST',\n }),\n onSuccess: () => {\n queryClient.invalidateQueries({ queryKey: ['yamlWorkflows'], refetchType: 'all' });\n },\n });\n}\n\nexport function useInvokeYamlWorkflow() {\n const queryClient = useQueryClient();\n return useMutation({\n mutationFn: ({ id, data, sync, execute_as }: { id: string; data: Record<string, unknown>; sync?: boolean; execute_as?: string }) =>\n apiFetch<{ result?: unknown; job_id?: string }>(`/yaml-workflows/${id}/invoke`, {\n method: 'POST',\n body: JSON.stringify({ data, sync, ...(execute_as ? { execute_as } : {}) }),\n }),\n onSuccess: () => {\n queryClient.invalidateQueries({ queryKey: ['yamlWorkflows'], refetchType: 'all' });\n },\n });\n}\n\nexport function useRegenerateYamlWorkflow() {\n const queryClient = useQueryClient();\n return useMutation({\n mutationFn: ({ id, task_queue, compilation_feedback }: { id: string; task_queue?: string; compilation_feedback?: string }) =>\n apiFetch<LTYamlWorkflowRecord>(`/yaml-workflows/${id}/regenerate`, {\n method: 'POST',\n body: JSON.stringify({ task_queue, compilation_feedback }),\n }),\n onSuccess: () => {\n queryClient.invalidateQueries({ queryKey: ['yamlWorkflows'], refetchType: 'all' });\n },\n });\n}\n\nexport function useArchiveYamlWorkflow() {\n const queryClient = useQueryClient();\n return useMutation({\n mutationFn: (id: string) =>\n apiFetch<LTYamlWorkflowRecord>(`/yaml-workflows/${id}/archive`, {\n method: 'POST',\n }),\n onSuccess: () => {\n queryClient.invalidateQueries({ queryKey: ['yamlWorkflows'], refetchType: 'all' });\n },\n });\n}\n\nexport function useRestoreYamlWorkflow() {\n const queryClient = useQueryClient();\n return useMutation({\n mutationFn: (id: string) =>\n apiFetch<LTYamlWorkflowRecord>(`/yaml-workflows/${id}/restore`, {\n method: 'POST',\n }),\n onSuccess: () => {\n queryClient.invalidateQueries({ queryKey: ['yamlWorkflows'], refetchType: 'all' });\n queryClient.invalidateQueries({ queryKey: ['yamlWorkflow'], refetchType: 'all' });\n },\n });\n}\n\nexport function useUpdateYamlWorkflow() {\n const queryClient = useQueryClient();\n return useMutation({\n mutationFn: ({ id, ...updates }: { id: string; yaml_content?: string; name?: string; description?: string; input_schema?: Record<string, unknown>; output_schema?: Record<string, unknown>; tags?: string[]; app_id?: string; graph_topic?: string }) =>\n apiFetch<LTYamlWorkflowRecord>(`/yaml-workflows/${id}`, {\n method: 'PUT',\n body: JSON.stringify(updates),\n }),\n onSuccess: () => {\n queryClient.invalidateQueries({ queryKey: ['yamlWorkflows'], refetchType: 'all' });\n },\n });\n}\n\nexport function useDeleteYamlWorkflow() {\n const queryClient = useQueryClient();\n return useMutation({\n mutationFn: (id: string) =>\n apiFetch<{ deleted: boolean }>(`/yaml-workflows/${id}`, {\n method: 'DELETE',\n }),\n onSuccess: () => {\n queryClient.invalidateQueries({ queryKey: ['yamlWorkflows'], refetchType: 'all' });\n },\n });\n}\n\n// ── Cron scheduling ─────────────────────────────────────────────\n\nexport function useSetYamlCron() {\n const queryClient = useQueryClient();\n return useMutation({\n mutationFn: ({ id, cron_schedule, cron_envelope, execute_as }: {\n id: string;\n cron_schedule: string;\n cron_envelope?: Record<string, unknown> | null;\n execute_as?: string | null;\n }) =>\n apiFetch<LTYamlWorkflowRecord>(`/yaml-workflows/${id}/cron`, {\n method: 'PUT',\n body: JSON.stringify({ cron_schedule, cron_envelope, execute_as }),\n }),\n onSuccess: () => {\n queryClient.invalidateQueries({ queryKey: ['yamlWorkflows'], refetchType: 'all' });\n },\n });\n}\n\nexport function useClearYamlCron() {\n const queryClient = useQueryClient();\n return useMutation({\n mutationFn: (id: string) =>\n apiFetch<LTYamlWorkflowRecord>(`/yaml-workflows/${id}/cron`, {\n method: 'DELETE',\n }),\n onSuccess: () => {\n queryClient.invalidateQueries({ queryKey: ['yamlWorkflows'], refetchType: 'all' });\n },\n });\n}\n\n// ── Version history ─────────────────────────────────────────────\n\nexport function useYamlWorkflowVersions(id: string) {\n return useQuery<{ versions: LTYamlWorkflowVersion[]; total: number }>({\n queryKey: ['yamlWorkflows', id, 'versions'],\n queryFn: () => apiFetch(`/yaml-workflows/${id}/versions`),\n enabled: !!id,\n });\n}\n\nexport function useYamlWorkflowVersion(id: string, version: number | null) {\n return useQuery<LTYamlWorkflowVersion>({\n queryKey: ['yamlWorkflows', id, 'versions', version],\n queryFn: () => apiFetch(`/yaml-workflows/${id}/versions/${version}`),\n enabled: !!id && version !== null,\n });\n}\n"],"names":["useYamlWorkflows","filters","params","useQuery","apiFetch","useYamlWorkflowAppIds","useYamlWorkflow","id","useCreateYamlWorkflow","queryClient","useQueryClient","useMutation","input","useDeployYamlWorkflow","useActivateYamlWorkflow","useInvokeYamlWorkflow","data","sync","execute_as","useRegenerateYamlWorkflow","task_queue","compilation_feedback","useArchiveYamlWorkflow","useRestoreYamlWorkflow","useUpdateYamlWorkflow","updates","useDeleteYamlWorkflow","useYamlWorkflowVersions","useYamlWorkflowVersion","version"],"mappings":"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"}
1
+ {"version":3,"file":"yaml-workflows-BHAOOGvn.js","sources":["../../src/api/yaml-workflows.ts"],"sourcesContent":["import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';\nimport { apiFetch } from './client';\nimport type { LTYamlWorkflowRecord, LTYamlWorkflowStatus, LTYamlWorkflowVersion } from './types';\n\ninterface YamlWorkflowListResponse {\n workflows: LTYamlWorkflowRecord[];\n total: number;\n}\n\ninterface YamlWorkflowFilters {\n status?: LTYamlWorkflowStatus;\n graph_topic?: string;\n app_id?: string;\n search?: string;\n set_id?: string;\n limit?: number;\n offset?: number;\n}\n\nexport function useYamlWorkflows(filters: YamlWorkflowFilters = {}) {\n const params = new URLSearchParams();\n if (filters.status) params.set('status', filters.status);\n if (filters.graph_topic) params.set('graph_topic', filters.graph_topic);\n if (filters.app_id) params.set('app_id', filters.app_id);\n if (filters.search) params.set('search', filters.search);\n if (filters.set_id) params.set('set_id', filters.set_id);\n if (filters.limit) params.set('limit', String(filters.limit));\n if (filters.offset !== undefined) params.set('offset', String(filters.offset));\n\n return useQuery<YamlWorkflowListResponse>({\n queryKey: ['yamlWorkflows', filters],\n queryFn: () => apiFetch(`/yaml-workflows?${params}`),\n });\n}\n\nexport function useYamlWorkflowAppIds() {\n return useQuery<{ app_ids: string[] }>({\n queryKey: ['yamlWorkflowAppIds'],\n queryFn: () => apiFetch('/yaml-workflows/app-ids'),\n });\n}\n\nexport function useYamlWorkflowByTopic(graphTopic: string | undefined, appId?: string) {\n return useQuery<YamlWorkflowListResponse>({\n queryKey: ['yamlWorkflows', 'byTopic', graphTopic, appId],\n queryFn: () => {\n const params = new URLSearchParams({ graph_topic: graphTopic!, limit: '1' });\n if (appId) params.set('app_id', appId);\n return apiFetch(`/yaml-workflows?${params}`);\n },\n enabled: !!graphTopic,\n });\n}\n\nexport function useYamlWorkflow(id: string) {\n return useQuery<LTYamlWorkflowRecord>({\n queryKey: ['yamlWorkflows', id],\n queryFn: () => apiFetch(`/yaml-workflows/${id}`),\n enabled: !!id,\n });\n}\n\nexport function useCreateYamlWorkflow() {\n const queryClient = useQueryClient();\n return useMutation({\n mutationFn: (input: {\n workflow_id: string;\n task_queue: string;\n workflow_name: string;\n name: string;\n description?: string;\n app_id?: string;\n subscribes?: string;\n tags?: string[];\n compilation_feedback?: string;\n }) =>\n apiFetch<LTYamlWorkflowRecord>('/yaml-workflows', {\n method: 'POST',\n body: JSON.stringify(input),\n }),\n onSuccess: () => {\n queryClient.invalidateQueries({ queryKey: ['yamlWorkflows'], refetchType: 'all' });\n },\n });\n}\n\nexport function useDeployYamlWorkflow() {\n const queryClient = useQueryClient();\n return useMutation({\n mutationFn: (id: string) =>\n apiFetch<LTYamlWorkflowRecord>(`/yaml-workflows/${id}/deploy`, {\n method: 'POST',\n }),\n onSuccess: () => {\n queryClient.invalidateQueries({ queryKey: ['yamlWorkflows'], refetchType: 'all' });\n },\n });\n}\n\nexport function useActivateYamlWorkflow() {\n const queryClient = useQueryClient();\n return useMutation({\n mutationFn: (id: string) =>\n apiFetch<LTYamlWorkflowRecord>(`/yaml-workflows/${id}/activate`, {\n method: 'POST',\n }),\n onSuccess: () => {\n queryClient.invalidateQueries({ queryKey: ['yamlWorkflows'], refetchType: 'all' });\n },\n });\n}\n\nexport function useInvokeYamlWorkflow() {\n const queryClient = useQueryClient();\n return useMutation({\n mutationFn: ({ id, data, sync, execute_as }: { id: string; data: Record<string, unknown>; sync?: boolean; execute_as?: string }) =>\n apiFetch<{ result?: unknown; job_id?: string }>(`/yaml-workflows/${id}/invoke`, {\n method: 'POST',\n body: JSON.stringify({ data, sync, ...(execute_as ? { execute_as } : {}) }),\n }),\n onSuccess: () => {\n queryClient.invalidateQueries({ queryKey: ['yamlWorkflows'], refetchType: 'all' });\n },\n });\n}\n\nexport function useRegenerateYamlWorkflow() {\n const queryClient = useQueryClient();\n return useMutation({\n mutationFn: ({ id, task_queue, compilation_feedback }: { id: string; task_queue?: string; compilation_feedback?: string }) =>\n apiFetch<LTYamlWorkflowRecord>(`/yaml-workflows/${id}/regenerate`, {\n method: 'POST',\n body: JSON.stringify({ task_queue, compilation_feedback }),\n }),\n onSuccess: () => {\n queryClient.invalidateQueries({ queryKey: ['yamlWorkflows'], refetchType: 'all' });\n },\n });\n}\n\nexport function useArchiveYamlWorkflow() {\n const queryClient = useQueryClient();\n return useMutation({\n mutationFn: (id: string) =>\n apiFetch<LTYamlWorkflowRecord>(`/yaml-workflows/${id}/archive`, {\n method: 'POST',\n }),\n onSuccess: () => {\n queryClient.invalidateQueries({ queryKey: ['yamlWorkflows'], refetchType: 'all' });\n },\n });\n}\n\nexport function useRestoreYamlWorkflow() {\n const queryClient = useQueryClient();\n return useMutation({\n mutationFn: (id: string) =>\n apiFetch<LTYamlWorkflowRecord>(`/yaml-workflows/${id}/restore`, {\n method: 'POST',\n }),\n onSuccess: () => {\n queryClient.invalidateQueries({ queryKey: ['yamlWorkflows'], refetchType: 'all' });\n queryClient.invalidateQueries({ queryKey: ['yamlWorkflow'], refetchType: 'all' });\n },\n });\n}\n\nexport function useUpdateYamlWorkflow() {\n const queryClient = useQueryClient();\n return useMutation({\n mutationFn: ({ id, ...updates }: { id: string; yaml_content?: string; name?: string; description?: string; input_schema?: Record<string, unknown>; output_schema?: Record<string, unknown>; tags?: string[]; app_id?: string; graph_topic?: string }) =>\n apiFetch<LTYamlWorkflowRecord>(`/yaml-workflows/${id}`, {\n method: 'PUT',\n body: JSON.stringify(updates),\n }),\n onSuccess: () => {\n queryClient.invalidateQueries({ queryKey: ['yamlWorkflows'], refetchType: 'all' });\n },\n });\n}\n\nexport function useDeleteYamlWorkflow() {\n const queryClient = useQueryClient();\n return useMutation({\n mutationFn: (id: string) =>\n apiFetch<{ deleted: boolean }>(`/yaml-workflows/${id}`, {\n method: 'DELETE',\n }),\n onSuccess: () => {\n queryClient.invalidateQueries({ queryKey: ['yamlWorkflows'], refetchType: 'all' });\n },\n });\n}\n\n// ── Cron scheduling ─────────────────────────────────────────────\n\nexport function useSetYamlCron() {\n const queryClient = useQueryClient();\n return useMutation({\n mutationFn: ({ id, cron_schedule, cron_envelope, execute_as }: {\n id: string;\n cron_schedule: string;\n cron_envelope?: Record<string, unknown> | null;\n execute_as?: string | null;\n }) =>\n apiFetch<LTYamlWorkflowRecord>(`/yaml-workflows/${id}/cron`, {\n method: 'PUT',\n body: JSON.stringify({ cron_schedule, cron_envelope, execute_as }),\n }),\n onSuccess: () => {\n queryClient.invalidateQueries({ queryKey: ['yamlWorkflows'], refetchType: 'all' });\n },\n });\n}\n\nexport function useClearYamlCron() {\n const queryClient = useQueryClient();\n return useMutation({\n mutationFn: (id: string) =>\n apiFetch<LTYamlWorkflowRecord>(`/yaml-workflows/${id}/cron`, {\n method: 'DELETE',\n }),\n onSuccess: () => {\n queryClient.invalidateQueries({ queryKey: ['yamlWorkflows'], refetchType: 'all' });\n },\n });\n}\n\n// ── Version history ─────────────────────────────────────────────\n\nexport function useYamlWorkflowVersions(id: string) {\n return useQuery<{ versions: LTYamlWorkflowVersion[]; total: number }>({\n queryKey: ['yamlWorkflows', id, 'versions'],\n queryFn: () => apiFetch(`/yaml-workflows/${id}/versions`),\n enabled: !!id,\n });\n}\n\nexport function useYamlWorkflowVersion(id: string, version: number | null) {\n return useQuery<LTYamlWorkflowVersion>({\n queryKey: ['yamlWorkflows', id, 'versions', version],\n queryFn: () => apiFetch(`/yaml-workflows/${id}/versions/${version}`),\n enabled: !!id && version !== null,\n });\n}\n"],"names":["useYamlWorkflows","filters","params","useQuery","apiFetch","useYamlWorkflowAppIds","useYamlWorkflow","id","useCreateYamlWorkflow","queryClient","useQueryClient","useMutation","input","useDeployYamlWorkflow","useActivateYamlWorkflow","useInvokeYamlWorkflow","data","sync","execute_as","useRegenerateYamlWorkflow","task_queue","compilation_feedback","useArchiveYamlWorkflow","useRestoreYamlWorkflow","useUpdateYamlWorkflow","updates","useDeleteYamlWorkflow","useYamlWorkflowVersions","useYamlWorkflowVersion","version"],"mappings":"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"}
@@ -8,11 +8,11 @@
8
8
  <link rel="preconnect" href="https://fonts.googleapis.com" />
9
9
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
10
10
  <link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet" />
11
- <script type="module" crossorigin src="./assets/index-CIwlVfvV.js"></script>
11
+ <script type="module" crossorigin src="./assets/index-CgUcYveM.js"></script>
12
12
  <link rel="modulepreload" crossorigin href="./assets/vendor-query-DvWWuNIB.js">
13
13
  <link rel="modulepreload" crossorigin href="./assets/vendor-react-_9U4mKFr.js">
14
- <link rel="modulepreload" crossorigin href="./assets/vendor-icons-DeBdvyKJ.js">
15
- <link rel="stylesheet" crossorigin href="./assets/index-Bpvfr4OW.css">
14
+ <link rel="modulepreload" crossorigin href="./assets/vendor-icons-B7OcUM6b.js">
15
+ <link rel="stylesheet" crossorigin href="./assets/index-Dh8MJl3L.css">
16
16
  </head>
17
17
  <body>
18
18
  <div id="root"></div>
@@ -0,0 +1,113 @@
1
+ # Open accumulator
2
+
3
+ One escalation row is a container that fills over time. Items join it one at a time, the row stays pending while they do, and the waiting workflow resumes with the ordered collection when the container is full, when its window closes, or when someone closes it by hand. Every terminal path except cancel delivers the collection, so a timeout is a delivery window, never a failure.
4
+
5
+ This page is the contract. The narrative lives in [Creating Escalations](hitl/escalation.md#open-accumulation--items-arrive-over-time) and [Resolution](hitl/resolution.md#accumulator-items); the endpoints in the [HTTP](api/http/escalations.md#accumulate-items) and [SDK](api/sdk/escalations.md#accumulate--accumulatebysignalkey--accumulatebymetadata) references.
6
+
7
+ ## Declaration
8
+
9
+ ```typescript
10
+ const bin = await conditionalAccumulator<ItemPayload, ResolverPayload>(signalId, {
11
+ role: 'bin',
12
+ metadata: { binKey },
13
+ timeout: '4h',
14
+ accumulate: {
15
+ max?: number; // count trigger; absent = unbounded
16
+ resolveAtMax?: boolean; // default true; false makes max a cap only
17
+ unique?: boolean; // default true; false replaces a repeated key in place
18
+ },
19
+ });
20
+ // AccumulatorResult<P, R> | null
21
+ ```
22
+
23
+ `conditional` accepts the same `accumulate` field and returns `T | false | null`; `conditionalAccumulator` removes the `false` branch from the type because an accumulator never resumes with it. `accumulate` and `batch` are mutually exclusive on one wait.
24
+
25
+ ## Delivered value
26
+
27
+ ```typescript
28
+ type AccumulatorResult<P, R> = R & {
29
+ $accumulated: Array<{ itemKey: string; payload?: P; at: string; actor?: string; reciprocalId?: string }>;
30
+ $trigger: 'count' | 'timeout' | 'resolve';
31
+ $resolution?: EscalationResolution;
32
+ };
33
+ ```
34
+
35
+ `$accumulated` is ordered by each entry's `at` (the database clock at the add), then by key. `R` is present only when `$trigger` is `'resolve'`.
36
+
37
+ | Path | `$trigger` | Row status | `resolver_payload` stored | Wait resumes with |
38
+ |------|------------|------------|---------------------------|-------------------|
39
+ | The add that reaches `max` (with `resolveAtMax`) | `count` | `resolved` | `{ $accumulated, $trigger }` | the same, plus `$resolution` when the adder carried identity |
40
+ | The `timeout` timer fires first | `timeout` | `expired` | `{ $accumulated, $trigger }` | the same |
41
+ | `resolve` / `resolveByMetadata` / `resolveAllOrNone` | `resolve` | `resolved` | `{ $accumulated, $trigger, ...resolverPayload }` | the same, plus `$resolution` |
42
+ | `cancel` | none | `cancelled` | untouched | `null` (items stay on the row) |
43
+
44
+ ## Reserved keys
45
+
46
+ Written by the fold at creation, inside the row's Leg1 commit, and maintained by every add and remove:
47
+
48
+ | Surface | Key | Meaning |
49
+ |---------|-----|---------|
50
+ | `metadata` (GIN-indexed) | `accumulate_count` | items held right now |
51
+ | `metadata` | `accumulate_max` | the count trigger, or `null` when unbounded |
52
+ | `metadata` | `accumulate_keys` | held item keys; `metadata @> '{"accumulate_keys":["ORD-9"]}'` finds the container holding that order |
53
+ | `envelope` | `accumulate_items` | `Record<itemKey, { payload?, at, actor?, reciprocalId? }>` |
54
+ | `envelope` | `accumulate_config` | the folded `{ unique, resolveAtMax }` |
55
+
56
+ A batch wait may add `partialOnTimeout: true`; it stores `envelope.batch_partial_on_timeout` and its timer delivers `{ ...filledItems, $trigger: 'timeout' }` instead of `false`.
57
+
58
+ ## Operations
59
+
60
+ | Operation | HTTP | SDK | MCP |
61
+ |-----------|------|-----|-----|
62
+ | Add by id | `POST /api/escalations/:id/accumulate` | `lt.escalations.accumulate` | `accumulate_item` |
63
+ | Add by signal key | `POST /api/escalations/accumulate-by-signal-key` | `accumulateBySignalKey` | |
64
+ | Add by facet | `POST /api/escalations/accumulate-by-metadata` | `accumulateByMetadata` | |
65
+ | Remove by id | `POST /api/escalations/:id/remove-item` | `removeItem` | `remove_item` |
66
+ | Remove by signal key / facet | `POST /api/escalations/remove-item-by-signal-key`, `/remove-item-by-metadata` | `removeItemBySignalKey`, `removeItemByMetadata` | |
67
+ | Read the collection | `GET /api/escalations/:id/items`, `GET /api/escalations/items-by-signal-key` | `getItems`, `getItemsBySignalKey` | `get_escalation_items` (admin; `id` or `signalKey`) |
68
+ | Scan | the `accumulate` verb; item mode answers `no_open_container` when the container closed; templates read `{claim.<facet>}` and `{item.<facet>}` | | |
69
+
70
+ An add takes `itemKey` (1 to 128 characters), an optional `payload` (validated against the container role's versioned form when present), an optional `metadata` patch (reserved keys rejected), `assertClaim` (by id), and an optional `reciprocal`.
71
+
72
+ ### Outcomes
73
+
74
+ | Outcome | HTTP | Meaning |
75
+ |---------|------|---------|
76
+ | `accepted` | 200 | the item landed; `count`, `remaining` (`null` when unbounded) |
77
+ | `completed` | 200 | this add reached `max`; the row resolved and the waiter woke, `signaled`, `workflowId` |
78
+ | `duplicate-item` | 409 | the key is already held (`unique`), row untouched |
79
+ | `full` | 409 | `max` held and the key is new (`resolveAtMax: false`, or a race with the completing add) |
80
+ | `not-accumulator` | 400 | the row carries no `accumulate` declaration |
81
+ | `not-found` | 404 | unknown row, or outside the caller's scope on the ingress forms |
82
+ | `already-resolved`, `already-expired`, `already-cancelled` | 409 | the row is terminal |
83
+ | `claimed-by-other`, `claim-expired` | 409 | the `assertClaim` assertion failed |
84
+ | `reciprocal-not-found` | 404 | the reciprocal selector matched nothing the caller may act on |
85
+ | `reciprocal-not-accumulator` | 400 | the reciprocal row carries no declaration |
86
+ | `reciprocal-duplicate`, `reciprocal-full`, `reciprocal-terminal` | 409 | the reciprocal blocked the add; the container is untouched |
87
+
88
+ Removals answer `removed` (200, with the new `count`), `item-absent` (404), `reciprocal-absent` (404), and the row and reciprocal states above.
89
+
90
+ ## Reciprocal adds
91
+
92
+ A reciprocal names a second accumulator row (`id`, `signalKey`, or `key` + `value`), typically the item's own escalation declared `accumulate: { max: 1 }`. Both rows are locked in one ordered statement, both guards are evaluated before either write, and both rows change or neither. The container gains the item under `itemKey`; the reciprocal gains the container's id as its item key; each entry carries the other row's id as `reciprocalId`. A side that reaches its `max` resolves and wakes in that same statement. Removal with a reciprocal removes from both rows or neither.
93
+
94
+ ## Guarantees
95
+
96
+ - **One statement per write.** An add or a removal is a single guarded UPDATE; the resolve-at-max and the wake enqueue ride inside it. There is no read-then-write anywhere on the path; the escalation layer's lookups only pick rows and gate access, and the statement re-checks every predicate under lock.
97
+ - **Exactly one completion.** Two adds racing for the last slot yield one `completed` and one terminal answer.
98
+ - **Row truth equals delivered value.** Whatever the workflow receives is what the row stores as `resolver_payload`, on every path.
99
+ - **Timeout is settled once.** The expiry statement and every resolve lock the row and re-check `pending`; if a resolve won, the timer path skips the resume entirely.
100
+ - **Facets are recomputed, never patched.** `accumulate_count` is derived from `accumulate_keys` in the same statement, so the two never drift.
101
+ - **Additive.** Existing `conditional`, `batch`, `resolve`, and `cancel` callers are unchanged; `partialOnTimeout` is opt-in.
102
+
103
+ ## Events
104
+
105
+ Every accepted add and removal publishes `escalation.updated` for the container (and for the reciprocal) with `item_key`, `count`, `remaining`, `actor`, `reciprocal_id`, and `removed: true` on a removal. A completing add publishes `escalation.resolved`. The rows are the ledger: entries carry `at`, `actor`, and `reciprocalId`; `accumulate_keys` answers membership from the GIN index; `accumulate_count` and `accumulate_max` are numeric facets for aggregates and range queries.
106
+
107
+ ## Deferred
108
+
109
+ `idleTimeoutSeconds` (the window re-arms on each add) and `armTimeoutOnFirst` (the window starts at the first add) need a re-armable engine timer; today the window is a fixed delayed message written at park. A cross-row item event table is also deferred; the rows carry the per-item history. Neither changes the contract above.
110
+
111
+ ## Versions
112
+
113
+ HotMesh 0.29.0 (`accumulate`, `partialOnTimeout`, timeout delivery, reciprocal statement) and long-tail 0.21.1 (root export of `conditionalAccumulator`, `no_open_container`, claim and item template tokens, items by signal key).
@@ -1122,6 +1122,96 @@ Single atomic query finds the pending escalation by metadata, auto-claims if unc
1122
1122
  }
1123
1123
  ```
1124
1124
 
1125
+ ## Batch items
1126
+
1127
+ ```
1128
+ POST /api/escalations/:id/resolve-batch-item
1129
+ POST /api/escalations/resolve-batch-item-by-signal-key
1130
+ POST /api/escalations/resolve-batch-item-by-metadata
1131
+ ```
1132
+
1133
+ Submit ONE declared item of a batch escalation (a `conditional` wait created with `batch: [...]`). Interim items answer `{ outcome: 'accepted', remaining }`; the LAST item answers `{ outcome: 'completed', remaining: 0, signaled, escalationId, workflowId }` and the waiting workflow resumes with the full collection, in the same statement. Each item validates against the row's versioned role form (canonical 422). Duplicates answer 409 (`Batch item already submitted`), undeclared keys 400, a non-batch row 400.
1134
+
1135
+ **Body (by id):** `{ itemKey, resolverPayload, metadata?, assertClaim? }`. **By signal key:** `{ signalKey, itemKey, resolverPayload, metadata? }`. **By metadata:** `{ key, value, itemKey, resolverPayload, metadata?, restrictRoles? }`.
1136
+
1137
+ Claim semantics: claim-agnostic by default; `assertClaim: true` (by id) requires the caller's own live claim, asserted atomically. RBAC mirrors the single-resolve forms: the by-id form answers 404 for rows outside the caller's read scope and 403 inside it but outside write scope; the ingress forms collapse a write denial to 404.
1138
+
1139
+ ## Accumulate items
1140
+
1141
+ ```
1142
+ POST /api/escalations/:id/accumulate
1143
+ POST /api/escalations/accumulate-by-signal-key
1144
+ POST /api/escalations/accumulate-by-metadata
1145
+ ```
1146
+
1147
+ Add ONE item to an open accumulator escalation (a `conditionalAccumulator` wait declared with `accumulate: {...}`). Interim adds are atomic row appends; the add that reaches `max` completes the escalation and the waiting workflow resumes with the ordered collection, in the same statement.
1148
+
1149
+ **Body (by id):**
1150
+
1151
+ ```json
1152
+ {
1153
+ "itemKey": "ORD-9",
1154
+ "payload": { "weight": 2 },
1155
+ "metadata": { "lane": "A" },
1156
+ "assertClaim": false,
1157
+ "reciprocal": { "id": "…" }
1158
+ }
1159
+ ```
1160
+
1161
+ | Field | Type | Description |
1162
+ |-------|------|-------------|
1163
+ | `itemKey` | `string` | **Required.** The key the item is held under (1 to 128 characters) |
1164
+ | `payload` | `object` | Stored on the entry and delivered inside `$accumulated`; validates against the row's versioned role form when present (canonical 422) |
1165
+ | `metadata` | `object` | Merge patch for the container's GIN-indexed metadata, same statement; reserved `accumulate_*` keys are rejected |
1166
+ | `assertClaim` | `boolean` | By id only. Require the caller's own live claim, asserted atomically |
1167
+ | `reciprocal` | `object` | A second accumulator row written in the same statement, both or neither. Exactly one of `id`, `signalKey`, or `key` + `value`; optional `payload` stored on its entry |
1168
+
1169
+ **By signal key:** `{ signalKey, itemKey, payload?, metadata?, reciprocal? }`. **By metadata:** `{ key, value, itemKey, payload?, metadata?, restrictRoles?, reciprocal? }` selects the highest priority pending accumulator whose metadata contains the key/value, within the caller's write scope intersected with `restrictRoles`.
1170
+
1171
+ **Response 200 (interim):**
1172
+
1173
+ ```json
1174
+ { "outcome": "accepted", "count": 3, "remaining": 9, "escalationId": "…",
1175
+ "reciprocal": { "outcome": "completed", "count": 1, "remaining": 0, "escalationId": "…", "signaled": true } }
1176
+ ```
1177
+
1178
+ `remaining` is `null` on an unbounded accumulator. `reciprocal` is present only when one was named.
1179
+
1180
+ **Response 200 (completed):**
1181
+
1182
+ ```json
1183
+ { "outcome": "completed", "count": 12, "remaining": 0, "signaled": true, "escalationId": "…", "workflowId": "…" }
1184
+ ```
1185
+
1186
+ **Errors:** 400 `itemKey is required`, `Escalation is not an accumulator`, `payload must be an object`, `reciprocal requires exactly one of id, signalKey, or key/value`; 404 `Escalation not found` (unknown, or outside the caller's scope on the ingress forms), `Reciprocal escalation not found`; 409 `Item already held` (`{ itemKey }`), `Accumulator is full`, `Escalation is claimed by another user`, `Your claim has expired`, `Escalation not available for accumulation` (resolved, cancelled, or expired), and the reciprocal blocks `Reciprocal escalation already holds this container`, `Reciprocal accumulator is full`, `Reciprocal escalation is no longer pending` (each with `outcome`); 422 payload violations.
1187
+
1188
+ ```
1189
+ POST /api/escalations/:id/remove-item
1190
+ POST /api/escalations/remove-item-by-signal-key
1191
+ POST /api/escalations/remove-item-by-metadata
1192
+ ```
1193
+
1194
+ Remove ONE held item from a pending accumulator in one guarded statement. The row stays pending and the waiter never wakes. **Body:** `{ itemKey, reciprocal? }` (plus the selector fields of the ingress forms). **Response 200:** `{ "outcome": "removed", "count": 2, "escalationId": "…", "reciprocal"?: { "count", "escalationId" } }`. **Errors:** 404 `Item not held by this escalation` (`{ itemKey }`), `Reciprocal escalation does not hold this container`; 400 and 409 as above.
1195
+
1196
+ ```
1197
+ GET /api/escalations/:id/items
1198
+ GET /api/escalations/items-by-signal-key?signalKey=…
1199
+ ```
1200
+
1201
+ The held items of an accumulator or batch row in arrival order, by row id or by the `signal_key` a workflow already knows. Read access; 404 non-disclosure.
1202
+
1203
+ ```json
1204
+ {
1205
+ "escalationId": "…", "kind": "accumulate", "status": "pending", "count": 2, "max": 12,
1206
+ "items": [
1207
+ { "itemKey": "ORD-9", "payload": { "weight": 2 }, "at": "2026-09-18T14:02:11.402Z", "actor": "…", "reciprocalId": "…" },
1208
+ { "itemKey": "ORD-12", "at": "2026-09-18T14:03:40.118Z", "actor": "…" }
1209
+ ]
1210
+ }
1211
+ ```
1212
+
1213
+ A batch row answers `kind: "batch"`, `max` = the declared size, and `pending` = the keys still awaiting submission. A row that holds no items answers 400 `Escalation holds no items`.
1214
+
1125
1215
  ## Resolve a set of escalations
1126
1216
 
1127
1217
  ```
@@ -383,6 +383,7 @@ When the update changes `form_schema` or `metadata_schema`, the new pair is snap
383
383
  | `upstream_roles` | `string[] \| null` | Replace the set of roles this station draws input from across other Operations sequences (omitted = preserve; `null` or `[]` = clear). Every entry must name an existing role other than this one. Distinct from `parent_role`, which places the role in its own sequence. |
384
384
  | `list_schema` | `object \| null` | Rich formatting (x-lt-* markup) for this role's escalation LIST page. Versions independently of `form_schema`; the list always renders the latest |
385
385
  | `default_pins` | `array \| null` | Pinned-view seeds for members: `[{ label, url, badge? }]` with dashboard-relative `url`s. Members promote, hide, or reorder them via their own preferences (`/api/me/preferences`). `null` clears |
386
+ | `portals` | `array \| null` | The role's named portals: `[{ key, label, rows, counts? }]`, `key` a slug unique within the role, `rows` 1..4 rows of 1..6 pin cells `{ label, url, badge? }`, `counts` up to 8 tiles `{ label, url, blurb? }` shown above the panels, each portal rendered as one page at `/portal/<role>/<key>`. Up to 12. `null` clears |
386
387
  | `change_summary` | `string` | Label recorded on the schema version snapshot when this update changes a schema field |
387
388
 
388
389
  **Example request** — configure a role as a station in the ops view:
@@ -436,6 +436,25 @@ fires first resumes the workflow with `false` and expires the row in a guarded
436
436
  UPDATE, so operators can never resolve into a workflow that already moved on.
437
437
  `expiresAt`, by contrast, is display metadata on the row — it arms nothing.
438
438
 
439
+ ### Open accumulation (`accumulate`)
440
+
441
+ `conditionalAccumulator<P, R>(signalId, config)` is `conditional` typed for an open accumulator: the config carries `accumulate: { max?, resolveAtMax?, unique? }`, and the wait resolves with `AccumulatorResult<P, R>` (`{ $accumulated: AccumulatedItem<P>[], $trigger: 'count' | 'timeout' | 'resolve', ...R }`) on every terminal path except cancel (`null`). `false` is not in the return type because a timeout delivers the collection. `P` types each item's payload; `R` types the resolver payload merged in when a person closes the container by hand.
442
+
443
+ ```typescript
444
+ import { conditionalAccumulator } from '@hotmeshio/long-tail';
445
+
446
+ const bin = await conditionalAccumulator<BagV1, { shippedBy?: string }>(signalId, {
447
+ role: 'bin',
448
+ metadata: { binKey },
449
+ accumulate: { max: 12 },
450
+ timeout: '4h',
451
+ });
452
+ if (bin === null) return cancelled();
453
+ ship(bin.$accumulated.map((item) => item.itemKey), bin.$trigger);
454
+ ```
455
+
456
+ Requires hotmesh 0.29.0+. `conditional` accepts the same `accumulate` field and returns `T | false | null`; `partialOnTimeout: true` on a `batch` wait likewise makes the timer deliver the filled items with `$trigger: 'timeout'`. See [Creating Escalations](../../hitl/escalation.md#open-accumulation--items-arrive-over-time).
457
+
439
458
  ### Two-step form
440
459
 
441
460
  Create the escalation first (e.g. to enrich routing metadata), then wait:
@@ -826,6 +845,79 @@ const result = await lt.escalations.resolveByMetadata({
826
845
 
827
846
  **Auth:** Required
828
847
 
848
+ ## resolveBatchItem / resolveBatchItemBySignalKey / resolveBatchItemByMetadata
849
+
850
+ Submit ONE declared item of a batch escalation (`conditional` with `batch: [...]`). Interim items return `{ outcome: 'accepted', remaining, escalationId }`; the LAST item returns `{ outcome: 'completed', remaining: 0, signaled, escalationId, workflowId }` and the waiting workflow resumes with the full collection. See [Resolution — Batch items](../../hitl/resolution.md#batch-items).
851
+
852
+ ```typescript
853
+ await lt.escalations.resolveBatchItem({ id, itemKey: 'weld', resolverPayload: { ok: true }, metadata?, assertClaim? });
854
+ await lt.escalations.resolveBatchItemBySignalKey({ signalKey, itemKey, resolverPayload, metadata? });
855
+ await lt.escalations.resolveBatchItemByMetadata({ key, value, itemKey, resolverPayload, metadata?, restrictRoles? });
856
+ ```
857
+
858
+ **Returns:** `LTApiResult<{ outcome, remaining, escalationId, signaled?, workflowId? }>`. 409 on a duplicate item, 400 on an undeclared key or a non-batch row.
859
+
860
+ **Auth:** Required
861
+
862
+ ## accumulate / accumulateBySignalKey / accumulateByMetadata
863
+
864
+ Add ONE item to an open accumulator escalation (`conditionalAccumulator` with `accumulate: {...}`). Interim adds return `accepted` with the count held and slots remaining; the add that reaches `max` returns `completed` and the waiting workflow resumes with the ordered collection, in the same statement. See [Resolution — Accumulator items](../../hitl/resolution.md#accumulator-items).
865
+
866
+ ```typescript
867
+ const added = await lt.escalations.accumulate({
868
+ id: binId,
869
+ itemKey: orderId,
870
+ payload: { weight: 2 },
871
+ reciprocal: { id: bagEscalationId },
872
+ });
873
+ // added.data → { outcome: 'accepted', count: 1, remaining: 11, escalationId,
874
+ // reciprocal: { outcome: 'completed', count: 1, remaining: 0, escalationId, signaled: true } }
875
+
876
+ await lt.escalations.accumulateBySignalKey({ signalKey, itemKey });
877
+ await lt.escalations.accumulateByMetadata({ key: 'binKey', value: binKey, itemKey, restrictRoles: ['bin'] });
878
+ ```
879
+
880
+ **Parameters:**
881
+
882
+ | Field | Type | Required | Description |
883
+ |-------|------|----------|-------------|
884
+ | `id` / `signalKey` / `key` + `value` | `string` | Yes | The container selector, one per method |
885
+ | `itemKey` | `string` | Yes | The key the item is held under (1 to 128 characters) |
886
+ | `payload` | `object` | No | Stored on the entry and delivered inside `$accumulated`; validates against the container's versioned role form |
887
+ | `metadata` | `object` | No | Merge patch for the container metadata, same statement; reserved `accumulate_*` keys rejected |
888
+ | `assertClaim` | `boolean` | No | By id only: require the caller's own live claim |
889
+ | `reciprocal` | `object` | No | `{ id \| signalKey \| key + value, payload? }`: a second accumulator row written in the same statement, both or neither |
890
+ | `restrictRoles` | `string[]` | No | By metadata only: expected queues, intersected with the caller's write scope |
891
+
892
+ **Returns:** `LTApiResult<{ outcome: 'accepted' \| 'completed', count, remaining, escalationId, signaled?, workflowId?, reciprocal? }>`. `remaining` is `null` on an unbounded accumulator. 409 on `Item already held`, `Accumulator is full`, claim blocks, terminal rows, and reciprocal blocks; 400 on a non-accumulator; 404 non-disclosure.
893
+
894
+ **Auth:** Required
895
+
896
+ ## removeItem / removeItemBySignalKey / removeItemByMetadata
897
+
898
+ Remove ONE held item from a pending accumulator in one guarded statement. The row stays pending and the waiter never wakes.
899
+
900
+ ```typescript
901
+ await lt.escalations.removeItem({ id: binId, itemKey: orderId, reciprocal: { id: bagEscalationId } });
902
+ // → { outcome: 'removed', count: 11, escalationId, reciprocal?: { count, escalationId } }
903
+ ```
904
+
905
+ **Returns:** `LTApiResult<{ outcome: 'removed', count, escalationId, reciprocal? }>`. 404 `Item not held by this escalation`.
906
+
907
+ **Auth:** Required
908
+
909
+ ## getItems / getItemsBySignalKey
910
+
911
+ The held items of an accumulator or batch row in arrival order, by row id or by the `signal_key` the parked workflow knows.
912
+
913
+ ```typescript
914
+ const { data } = await lt.escalations.getItems({ id: binId });
915
+ const same = await lt.escalations.getItemsBySignalKey({ signalKey: `bin-${binKey}` });
916
+ // data → { escalationId, kind: 'accumulate' | 'batch', status, count, max, items: [{ itemKey, payload?, at, actor?, reciprocalId? }], pending? }
917
+ ```
918
+
919
+ **Auth:** Required (read access; 404 non-disclosure)
920
+
829
921
  ## resolveByIds
830
922
 
831
923
  Resolve a set of escalations by id in one guarded statement (the set-based sibling of `resolve`). For bookkeeping rows woken collectively — it does not deliver a per-row signal, and the store enforces that: rows backing a live `condition()` waiter (`signal_key` set) stay `pending` and are excluded from the result. Settle waiter rows individually with `resolve`, which carries the wake. RBAC: a scoped caller may only resolve rows whose role they hold.
@@ -34,6 +34,7 @@ interface RoleDetail {
34
34
  priority_facet: string | null;
35
35
  list_schema: Record<string, any> | null;
36
36
  default_pins: { label: string; url: string; badge?: boolean }[] | null;
37
+ portals: { key: string; label: string; rows: { label: string; url: string; badge?: boolean }[][]; counts?: { label: string; url: string; blurb?: string }[] }[] | null;
37
38
  upstream_roles: string[];
38
39
  user_count: number;
39
40
  chain_count: number;
@@ -281,6 +282,7 @@ const result = await lt.roles.update({
281
282
  | `upstream_roles` | `string[] \| null` | No | Replace the set of roles this station draws input from across other Operations sequences (omitted = preserve; `null` or `[]` = clear). Distinct from `parent_role`, which places the role in its own sequence |
282
283
  | `list_schema` | `object \| null` | No | Rich formatting (x-lt-* markup) for this role's escalation LIST page; versions independently of `form_schema` |
283
284
  | `default_pins` | `array \| null` | No | Pinned-view seeds for members: `[{ label, url, badge? }]` with dashboard-relative `url`s. Members promote, hide, or reorder via their own preferences (`lt.me`) |
285
+ | `portals` | `array \| null` | No | The role's named portals `[{ key, label, rows, counts? }]`, each 1..4 rows of 1..6 pin cells rendered as one page at `/portal/<role>/<key>`, with up to 8 count tiles above |
284
286
  | `change_summary` | `string` | No | Label recorded on the schema version snapshot when this update changes a schema field |
285
287
 
286
288
  When the update changes `form_schema` or `metadata_schema`, the new pair is snapshotted into the role's version history and `current_schema_version` advances.
@@ -132,7 +132,7 @@ A pipeline workflow is a compiled deterministic workflow generated from a succes
132
132
 
133
133
  Pipeline workflows are stored in `lt_yaml_workflows` with status lifecycle: `draft` → `deployed` → `active` → `archived`. In the dashboard, pipeline workflows display a **Wand2** (magic wand) icon in purple.
134
134
 
135
- See the [Compilation Pipeline](compilation.md) guide for the full lifecycle and the [Workflows Guide](workflows.md) for detailed coverage of all three types.
135
+ See the [Compilation Pipeline](https://github.com/hotmeshio/long-tail/blob/main/docs/donotpublish/compilation.md) guide for the full lifecycle and the [Workflows Guide](workflows.md) for detailed coverage of all three types.
136
136
 
137
137
  ## System Capabilities
138
138
 
@@ -75,7 +75,8 @@ they always have.)
75
75
  ## Roles: first-class declarations
76
76
 
77
77
  `roles[]` registers roles with titles, versioned form/metadata/list schemas,
78
- escalation targets, upstream inputs, dials, and pins:
78
+ escalation targets, upstream inputs, dials, pins, and portals
79
+ (`portals`, named matrices of pin cells, each rendered as one page):
79
80
 
80
81
  ```typescript
81
82
  roles: [
package/docs/dashboard.md CHANGED
@@ -166,7 +166,7 @@ The MCP Tool Designer page lists previous tool design sessions and provides entr
166
166
 
167
167
  **Composer mode** — manual tool design for building tools from scratch using the visual DAG editor.
168
168
 
169
- Steps unlock sequentially in each wizard. Compiled tools appear in **Graph Workflows** and **Graph Executions**. See the [Compilation Pipeline](compilation.md) guide for details.
169
+ Steps unlock sequentially in each wizard. Compiled tools appear in **Graph Workflows** and **Graph Executions**. See the [Compilation Pipeline](https://github.com/hotmeshio/long-tail/blob/main/docs/donotpublish/compilation.md) guide for details.
170
170
 
171
171
  ### MCP Server Tools
172
172
 
@@ -278,7 +278,9 @@ Accessible at `/admin/roles/:role`. One page per role — a quiet header carryin
278
278
 
279
279
  **Pins** — two live-save groups. **Link Variables** leads: the facet names members bind per device (`properties.link_variables` — name, optional label, optional default). A pin below may reference a variable as a facet value, `facets={"facility":"{lt:facility}"}`; each member device opens it scoped to its own binding (set from the user menu → Link variables), falling back to the declared default or, unbound, applying no filter. A caption under each templated pin previews the binding on the viewing device (`facility = 'soleful'` / `facility = <empty>`). **Default Pins** follows — the pinned-view seeds this role hands its members (`default_pins`): label, dashboard-relative URL, optional badge. Members see them in their Pinned nav section from first login (marked role-provided) and may promote, hide, or reorder them via preferences; promoting a templated pin copies the template, so it keeps following the device's bindings. See [Faceted Routing — Link variables](faceted-routing.md#link-variables).
280
280
 
281
- **API:** `PATCH /api/roles/:role` updates identity, Pace Board dials, placement, schemas, and pins.
281
+ **Portal** the role's named portals, each its pinned views laid out as one page. The editor mirrors the page: sheets, title bands, cell panels, dashed slots for adding. Name a portal and start it from a pin; then rows of cells, each cell a pin picked from the role's default pins or the admin's own pins, or typed as a label and URL, with editable panel titles (`portals`, up to 12 portals of 4 rows by 6). Edits join the page draft and **Save** writes them. A sketch shows each shape; **Open** opens `/portal/<role>/<key>`. See [Portal](hitl/portal.md).
282
+
283
+ **API:** `PATCH /api/roles/:role` updates identity, Pace Board dials, placement, schemas, pins, and the portal.
282
284
 
283
285
  ### Escalation Schema
284
286
 
@@ -377,7 +379,7 @@ Accessible at `/escalations/available`. The central queue for escalation activit
377
379
  - **Columns:** a **Summary** cell (status dot + description or type), **Assignee** (claimant, or `—`), **Role** pill, **Priority**, and **Created**. The status dot distinguishes pending, claimed, resolved, cancelled, and expired; notification escalations show a bell.
378
380
  - **Faceted query** — beyond the filter bar, the queue takes a structured metadata query, all URL-backed so a query copy-pastes: `?facets={"orderId":"..."}` (equality, AND-ed), `?block=` (exclude), `?range=` (numeric bounds), `?exists=` (key present), `?roles=` (union of role queues), and `?orderBy=` (JSON sort). `?jeopardy=1` narrows to rows past the role's age threshold. List rows omit the envelope by default; `?include=envelope` pulls the full envelope and payload columns for rich views.
379
381
  - **Metadata value affordances** — each metadata key/value row carries a three-icon triad on hover: **filter** (scope the current role's queue to `key = value`), **search** (the same facet match across all roles), and **history** (open the entity's cross-queue interval timeline in the right panel — every role this value moved through, with durations and gaps). Filter-present, search-present, history-past. History renders for string values, since the timeline's GIN containment match serves JSON-string facets.
380
- - **View modes** (`?view=`) — `table` (the columns above), `timeline` (100 rows per page of cross-queue movement), and `rich`. When a role owns a [list schema](#escalations-list-schema), the list defaults to the role-authored `rich` view (the live item as a card, plus a load-on-demand history) with a **Table view** toggle back.
382
+ - **View modes** (`?view=`) — `table` (the columns above), `timeline` (100 rows per page of cross-queue movement), and `rich`. A `?layout=` hint shapes the table: `compact` stays a tight table however narrow, holding non-identity columns until the container clears the split width; `cards` folds into console cards on purpose; absent, the table folds on its own below the card threshold (the same hint a portal pin carries, see [Portal](hitl/portal.md)). When a role owns a [list schema](#escalations-list-schema), the list defaults to the role-authored `rich` view (the live item as a card, plus a load-on-demand history) with a **Table view** toggle back.
381
383
  - **Claim** — the claim action locks an item to your user for a claim window (default 30 min). Only members of the role see its pending items; a member scoped `read_self` lands directly on their own assigned item rather than browsing the queue. Re-claiming your own expired item extends it rather than failing.
382
384
  - **Bulk actions (admin)** — row checkboxes surface a selection bar: **Claim**, **Assign**, **Unassign**, **Escalate** (move to another role), and **Cancel**. Assign opens a modal for the target user and claim duration; rows under a live claim are skipped unless the modal's takeover checkbox (shown when the selection includes live claims) reassigns them — admin/superadmin only. Unassign returns claimed rows to the pool. Each response reports how many rows were skipped and why. Scoped to a single role, the Cancel verb and its confirmation speak the role form's `x-lt-labels.cancel` vocabulary (`false` hides it). Assign also accepts a faceted query instead of an id list, so "claim everything matching this facet" is one call.
383
385
 
@@ -387,6 +389,10 @@ Accessible at `/escalations/available`. The central queue for escalation activit
387
389
 
388
390
  Accessible at `/escalations/queue`. The personal inbox — the items currently assigned to you across your roles. It carries the same title-as-role-selector, the same rich/table views, and the same metadata affordances as All Escalations, minus the bulk selection bar (every row is already yours). A **Claim expiry** column counts down each item's remaining window. The shell also auto-navigates here on hand-off: when a workflow assigns a follow-on escalation to you (`assigned_to = you`), the dashboard opens that item's detail directly.
389
391
 
392
+ ### Portal
393
+
394
+ Accessible at `/portal/<role>/<key>` to any signed-in user, from the **Portals** section of the user menu, and from the rows that lead the role's group in the nav. Each of the role's `portals` renders as a grid: one row per declared row, each dividing its width among its cells, every cell a bounded panel of a pinned list view drawn with the same rich, table, and timeline components as All Escalations; the left nav steps aside on portal pages as it does in kiosk mode. Panels show the pin label, the live count, a view toggle and sort direction for the visit, a refresh, and a link to the full view; rows open the item detail. A kiosk role lands on its first portal as home. See [Portal](hitl/portal.md).
395
+
390
396
  ### Escalation Detail & Resolve Form
391
397
 
392
398
  Accessible at `/escalations/detail/:id`. Where a person completes the work the workflow is waiting on. The main column is the resolve form; a switchable side panel carries context.
@@ -399,7 +405,7 @@ Accessible at `/escalations/detail/:id`. Where a person completes the work the w
399
405
  - **Submit guard** — `x-lt-submit-guard` gates a parent on a child query (e.g. "every item in this batch must resolve first"): the dashboard disables Submit with a live count while rows remain, and the server re-checks the guard atomically inside the resolving `UPDATE` so no race slips through. `autoResolveWhenEmpty` submits the parent the moment the query empties.
400
406
  - **Transition hand-off** — `x-lt-transition` replaces the return-to-previous-page jump with a short wait screen: the workflow assigns a follow-on escalation back to the submitter, the screen detects it and navigates onward (or falls back to `x-lt-transition-done` after a bounded wait). This is the submit → side-effect → next-step chain (submit, a label prints, the harvest task opens).
401
407
  - **Batch resolution** — a `conditional({ batch })` escalation accumulates N items on one row; `POST /api/escalations/:id/resolve-batch-item` submits each (claim-agnostic by default, `assertClaim` to require your own live claim), and the last item wakes the workflow with the full collection.
402
- - **Side panel** — switchable views selected by the icon strip: **Help** (the form's `x-lt-help` markdown, `{{domain.path}}`-interpolated against the live record — escalation, metadata, envelope, payload, resolver, and `lookup.*` — or a state-aware hint like "Claim this escalation to enable the form"), **Details** (status, role, priority, claim provenance, timestamps, and for builders the identifier links), **AI Analysis** (what triage diagnosed and corrected, when AI is on), **Metadata** (the row's facets, with the same triad), **Context** (input envelope, escalation context, resolver payload), **Record** (the raw JSON, builders only), and **Errors** (the last submit's validation failures). The panel and form share the width as a flex set; it opens expanded on Help when the form carries `x-lt-help`.
408
+ - **Side panel** — switchable views selected by the icon strip: **Help** (the form's `x-lt-help` markdown, `{{domain.path}}`-interpolated against the live record — escalation, metadata, envelope, payload, resolver, and `lookup.*` — or a state-aware hint like "Claim this escalation to enable the form"), **Details** (status, role, priority, claim provenance, timestamps, and for builders the identifier links), **AI Analysis** (what triage diagnosed and corrected, when AI is on), **Metadata** (the row's facets, with the same triad), **Items** (on an accumulator or batch row: the held items in arrival order with the count against max, each with its payload summary, arrival time, actor, and a link to its reciprocal row; on a pending accumulator the actor adds an item by key with an optional JSON payload or removes one, each through the same guarded statement the API runs), **Context** (input envelope, escalation context, resolver payload), **Record** (the raw JSON, builders only), and **Errors** (the last submit's validation failures). The panel and form share the width as a flex set; it opens expanded on Help when the form carries `x-lt-help`.
403
409
  - **Admin claim override** — when an item is held by someone else, admins and superadmins see **Reassign…** (hand the claim to another user, takeover implied) and **Return to queue** (unassign) in the action bar.
404
410
 
405
411
  **API:** `POST /api/escalations/:id/claim` claims, `/release` releases, `/resolve` resolves; `/resolve-batch-item` submits one batch item; `/escalate` moves; `/cancel` cancels. The role's form and its versions come from `GET /api/roles/:role/schema`.
@@ -8,7 +8,7 @@ author schemas and copy, the rules the renderer applies to errors, and how a
8
8
  deployment plugs its own stylesheet in through static config.
9
9
 
10
10
  The full internal doctrine lives in
11
- [`docs/design-principles.md`](../design-principles.md); the reference form
11
+ [`docs/donotpublish/design-principles.md`](https://github.com/hotmeshio/long-tail/blob/main/docs/donotpublish/design-principles.md); the reference form
12
12
  implementation is `examples/workflows/acme-stations`.
13
13
 
14
14
  ---
@@ -103,7 +103,43 @@ The declaration folds into the row at creation, inside the same Leg1 commit:
103
103
 
104
104
  Items are submitted via `POST /api/escalations/:id/resolve-batch-item`, `POST /api/escalations/resolve-batch-item-by-signal-key` (the deterministic home signal id — no UUID lookup, no facet duplication), or `POST /api/escalations/resolve-batch-item-by-metadata` (see [Resolution — Batch items](resolution.md)). Each item validates against the same versioned role form a single-item resolver gets, `schemaVersion` pins included. Each fill is one guarded statement: the payload lands only while its key is still pending (duplicates return `duplicate-item` untouched), and the LAST fill resolves the row, stores the assembled collection as `resolver_payload`, and wakes this wait — atomically. The completing submission's `$resolution` rides the delivered collection.
105
105
 
106
- `timeout` and cancellation keep their standard semantics (`false` / `null`); a plain resolve on a batch row remains an admin override that resolves the whole row with the payload given.
106
+ `timeout` and cancellation keep their standard semantics (`false` / `null`); a plain resolve on a batch row remains an admin override that resolves the whole row with the payload given. Add `partialOnTimeout: true` (hotmesh 0.29.0+) and the timer instead resumes the wait with the items filled so far plus `$trigger: 'timeout'`, the same value the expired row stores as `resolver_payload`.
107
+
108
+ ### Open accumulation — items arrive over time
109
+
110
+ Declare `accumulate` and the escalation becomes an open container: items join it one at a time through `accumulateItem` while the row stays pending, and the wait resumes with the ordered collection when the container is full, when its window closes, or when someone resolves it by hand. A timeout is a delivery, never a failure, so the wait has no `false` branch; `conditionalAccumulator` types that contract.
111
+
112
+ ```typescript
113
+ import { conditionalAccumulator } from '@hotmeshio/long-tail';
114
+
115
+ const bin = await conditionalAccumulator<BagV1, { shippedBy?: string }>(signalId, {
116
+ role: 'bin',
117
+ description: 'Scan each bag into the bin',
118
+ metadata: { binKey },
119
+ accumulate: { max: 12 }, // count trigger; omit for an unbounded window
120
+ timeout: '4h', // the window
121
+ });
122
+ if (bin === null) { /* cancelled */ }
123
+ bin.$trigger; // 'count' | 'timeout' | 'resolve'
124
+ bin.$accumulated; // [{ itemKey, payload?, at, actor?, reciprocalId? }, ...] in arrival order
125
+ bin.shippedBy; // present only when $trigger is 'resolve' (the resolver's payload merges in)
126
+ ```
127
+
128
+ `accumulate` takes three dials. `max` is the count trigger; absent, only the timer or a manual resolve ends the wait. `resolveAtMax: false` makes `max` a cap: the last slot answers `accepted`, further adds answer `full`, and the wait still ends by timer or resolve. `unique: false` lets a repeated item key replace its entry in place; the default answers `duplicate-item` and leaves the row untouched.
129
+
130
+ The declaration folds into the row at creation, inside the same Leg1 commit:
131
+
132
+ - `metadata.accumulate_count` — items held right now, recomputed in every add and remove
133
+ - `metadata.accumulate_max` — the count trigger, or `null` when unbounded
134
+ - `metadata.accumulate_keys` — the held item keys (`@>`-queryable: `{"accumulate_keys":["ORD-9"]}` finds the container holding that order)
135
+ - `envelope.accumulate_items` — `Record<itemKey, { payload?, at, actor?, reciprocalId? }>`, with `at` stamped by the database clock in the add statement
136
+ - `envelope.accumulate_config` — the folded `{ unique, resolveAtMax }`
137
+
138
+ Items are added via `POST /api/escalations/:id/accumulate`, `POST /api/escalations/accumulate-by-signal-key`, or `POST /api/escalations/accumulate-by-metadata`, removed via the matching `remove-item` forms, and read in arrival order via `GET /api/escalations/:id/items` (see [Resolution — Accumulator items](resolution.md#accumulator-items)). Each add is one guarded statement: the entry lands only while the row is pending and the key is not already held, the facets recompute, and the add that reaches `max` resolves the row, stores `{ $accumulated, $trigger: 'count' }` as `resolver_payload`, and wakes this wait, atomically. When the timer fires first the expiry statement stores and delivers `{ $accumulated, $trigger: 'timeout' }` and the row is `expired`; a manual `resolve` stores and delivers the collection merged with the resolver's payload under `$trigger: 'resolve'`. Cancellation still yields `null` with the held items preserved on the row.
139
+
140
+ A **reciprocal** add writes two accumulator rows in that same statement, both or neither: the container gains the item, and a second row (the item's own escalation, declared `accumulate: { max: 1 }`) gains the container's id as its item key. Each entry carries the other row's id as `reciprocalId`, so "which bin holds this bag" reads from the bag's row and "which bags are in this bin" from the bin's, both as row truth. A blocked reciprocal (already resolved, full, already holding this container) answers `reciprocal-*` and the container is untouched. The `rollupBin` and `rollupMember` examples show both sides.
141
+
142
+ `accumulate` and `batch` are mutually exclusive on one wait. The full contract, guarantees, and what is deferred are collected in [Open accumulator](../accumulator.md).
107
143
 
108
144
  ### Row completeness guarantee
109
145
 
@@ -70,6 +70,10 @@ An array of conditions requires every one, so the prompt belongs to its tool and
70
70
 
71
71
  `x-lt-help` on the schema root is the longer reference for the side panel: markdown, tables, and `{{input.*}}` tokens that re-interpolate as the operator types.
72
72
 
73
+ ## From an escalation form
74
+
75
+ A role's `form_schema` can offer the same invoke from inside the work item: an `x-lt-invoke` field names the workflow and maps the escalation's values into its payload, then either posts directly or opens this form in a dialog with those values in place. Visibility follows the same per-caller list, the payload meets the same gate, and the run reports over the same events. See [x-lt-invoke.md](x-lt-invoke.md).
76
+
73
77
  ## The Invoke Tool page
74
78
 
75
79
  The page is open to anyone the server lists an invokable workflow for. Builders reach **Invoke Tool** under Orchestrate; every other persona gets a **Tools** section in the nav that appears only when there is something to invoke. Each invokable workflow is a tool; the list sits on the left, grouped by task queue, with the first workflow preselected; the form takes the rest of the row. Below 1280px the list folds into a select and the form takes the full width.