@hotmeshio/long-tail 0.7.1 → 0.7.3

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 (229) hide show
  1. package/build/api/escalations/create.js +30 -9
  2. package/build/api/roles.d.ts +32 -0
  3. package/build/api/roles.js +73 -0
  4. package/build/bin/ltc.js +23 -0
  5. package/build/lib/cli/commands/roles.d.ts +17 -0
  6. package/build/lib/cli/commands/roles.js +102 -0
  7. package/build/lib/db/schemas/017_role_schema_versions.sql +40 -0
  8. package/build/lib/db/schemas/018_role_upstreams.sql +21 -0
  9. package/build/routes/roles.js +20 -0
  10. package/build/scripts/ortho-populate.d.ts +80 -8
  11. package/build/scripts/ortho-populate.js +204 -52
  12. package/build/sdk/index.d.ts +2 -0
  13. package/build/sdk/index.js +2 -0
  14. package/build/services/orchestrator/condition.d.ts +25 -1
  15. package/build/services/orchestrator/condition.js +29 -1
  16. package/build/services/role/index.d.ts +15 -1
  17. package/build/services/role/index.js +45 -1
  18. package/build/services/role/sql.d.ts +23 -2
  19. package/build/services/role/sql.js +93 -16
  20. package/build/services/role/types.d.ts +41 -0
  21. package/build/system/mcp-servers/admin/index.js +3 -3
  22. package/build/system/mcp-servers/admin/schemas.d.ts +25 -2
  23. package/build/system/mcp-servers/admin/schemas.js +11 -2
  24. package/build/system/mcp-servers/admin/users.js +42 -0
  25. package/build/system/mcp-servers/human-queue-schemas.d.ts +6 -0
  26. package/build/system/mcp-servers/human-queue-schemas.js +4 -0
  27. package/build/system/mcp-servers/human-queue.js +10 -2
  28. package/build/system/seed/tool-manifests-admin.d.ts +371 -0
  29. package/build/system/seed/tool-manifests-admin.js +3 -1
  30. package/build/tsconfig.tsbuildinfo +1 -1
  31. package/build/types/escalation.d.ts +16 -0
  32. package/build/types/escalation.js +17 -0
  33. package/dashboard/dist/assets/{AdminDashboard-Bg1HgukI.js → AdminDashboard-C9eE1VEp.js} +2 -2
  34. package/dashboard/dist/assets/{AdminDashboard-Bg1HgukI.js.map → AdminDashboard-C9eE1VEp.js.map} +1 -1
  35. package/dashboard/dist/assets/{AgentConfigPage-HBe_YDU_.js → AgentConfigPage-Ba8QfXM2.js} +2 -2
  36. package/dashboard/dist/assets/{AgentConfigPage-HBe_YDU_.js.map → AgentConfigPage-Ba8QfXM2.js.map} +1 -1
  37. package/dashboard/dist/assets/{AgentDetailPage-BsaiYbwL.js → AgentDetailPage-DVmec0CP.js} +2 -2
  38. package/dashboard/dist/assets/{AgentDetailPage-BsaiYbwL.js.map → AgentDetailPage-DVmec0CP.js.map} +1 -1
  39. package/dashboard/dist/assets/{AgentsPage-DhN2lIBh.js → AgentsPage-DNiCYMh3.js} +2 -2
  40. package/dashboard/dist/assets/{AgentsPage-DhN2lIBh.js.map → AgentsPage-DNiCYMh3.js.map} +1 -1
  41. package/dashboard/dist/assets/{AvailableEscalationsPage-_xcWvlIz.js → AvailableEscalationsPage-gDIF8ocC.js} +2 -2
  42. package/dashboard/dist/assets/{AvailableEscalationsPage-_xcWvlIz.js.map → AvailableEscalationsPage-gDIF8ocC.js.map} +1 -1
  43. package/dashboard/dist/assets/{BotPicker-DY6iUVC9.js → BotPicker-3wBcBavT.js} +2 -2
  44. package/dashboard/dist/assets/{BotPicker-DY6iUVC9.js.map → BotPicker-3wBcBavT.js.map} +1 -1
  45. package/dashboard/dist/assets/{CapabilitiesPage-w1owqsmN.js → CapabilitiesPage-WoPkND2y.js} +2 -2
  46. package/dashboard/dist/assets/{CapabilitiesPage-w1owqsmN.js.map → CapabilitiesPage-WoPkND2y.js.map} +1 -1
  47. package/dashboard/dist/assets/{CollapsibleSection-DIMGjlJ-.js → CollapsibleSection-Cx99mqNN.js} +2 -2
  48. package/dashboard/dist/assets/{CollapsibleSection-DIMGjlJ-.js.map → CollapsibleSection-Cx99mqNN.js.map} +1 -1
  49. package/dashboard/dist/assets/{CredentialsPage-D1OmMHux.js → CredentialsPage-DbNaoPbF.js} +2 -2
  50. package/dashboard/dist/assets/{CredentialsPage-D1OmMHux.js.map → CredentialsPage-DbNaoPbF.js.map} +1 -1
  51. package/dashboard/dist/assets/{CronLabel-CQy3DrUQ.js → CronLabel-DLW4_Zg4.js} +2 -2
  52. package/dashboard/dist/assets/{CronLabel-CQy3DrUQ.js.map → CronLabel-DLW4_Zg4.js.map} +1 -1
  53. package/dashboard/dist/assets/{CustomDurationPicker-BoBkl9tw.js → CustomDurationPicker-BRUFjkiu.js} +2 -2
  54. package/dashboard/dist/assets/{CustomDurationPicker-BoBkl9tw.js.map → CustomDurationPicker-BRUFjkiu.js.map} +1 -1
  55. package/dashboard/dist/assets/{DropZone-QXbiuU0b.js → DropZone-DEX0BBqt.js} +2 -2
  56. package/dashboard/dist/assets/{DropZone-QXbiuU0b.js.map → DropZone-DEX0BBqt.js.map} +1 -1
  57. package/dashboard/dist/assets/{ElapsedCell-Donjm9gT.js → ElapsedCell-BtmDQaym.js} +2 -2
  58. package/dashboard/dist/assets/{ElapsedCell-Donjm9gT.js.map → ElapsedCell-BtmDQaym.js.map} +1 -1
  59. package/dashboard/dist/assets/EscalationSchemaPage-Bm4yBEGN.js +9 -0
  60. package/dashboard/dist/assets/EscalationSchemaPage-Bm4yBEGN.js.map +1 -0
  61. package/dashboard/dist/assets/{EscalationsOverview-CvHIJ_GG.js → EscalationsOverview-B3xZwTQA.js} +2 -2
  62. package/dashboard/dist/assets/{EscalationsOverview-CvHIJ_GG.js.map → EscalationsOverview-B3xZwTQA.js.map} +1 -1
  63. package/dashboard/dist/assets/{EventTable-DpAXMmT3.js → EventTable-Bh9FKXLI.js} +2 -2
  64. package/dashboard/dist/assets/{EventTable-DpAXMmT3.js.map → EventTable-Bh9FKXLI.js.map} +1 -1
  65. package/dashboard/dist/assets/{GraphInvokePage-De1XVx5d.js → GraphInvokePage-ov_ANlaE.js} +2 -2
  66. package/dashboard/dist/assets/{GraphInvokePage-De1XVx5d.js.map → GraphInvokePage-ov_ANlaE.js.map} +1 -1
  67. package/dashboard/dist/assets/{HomePage-BYR5VXXw.js → HomePage-D0DV6izb.js} +2 -2
  68. package/dashboard/dist/assets/{HomePage-BYR5VXXw.js.map → HomePage-D0DV6izb.js.map} +1 -1
  69. package/dashboard/dist/assets/{ListToolbar-Bx7jGzPA.js → ListToolbar-FpcvqNkN.js} +2 -2
  70. package/dashboard/dist/assets/{ListToolbar-Bx7jGzPA.js.map → ListToolbar-FpcvqNkN.js.map} +1 -1
  71. package/dashboard/dist/assets/{McpOverview-DXRKcJN0.js → McpOverview-DcuRiAff.js} +2 -2
  72. package/dashboard/dist/assets/{McpOverview-DXRKcJN0.js.map → McpOverview-DcuRiAff.js.map} +1 -1
  73. package/dashboard/dist/assets/{McpQueryDetailPage-CmW15w5e.js → McpQueryDetailPage-BcYl67KP.js} +2 -2
  74. package/dashboard/dist/assets/{McpQueryDetailPage-CmW15w5e.js.map → McpQueryDetailPage-BcYl67KP.js.map} +1 -1
  75. package/dashboard/dist/assets/{McpQueryPage-BE6I011z.js → McpQueryPage-C3GDzqZM.js} +2 -2
  76. package/dashboard/dist/assets/{McpQueryPage-BE6I011z.js.map → McpQueryPage-C3GDzqZM.js.map} +1 -1
  77. package/dashboard/dist/assets/{McpRunDetailPage-Dsa3G3XQ.js → McpRunDetailPage-SzOyhDBW.js} +2 -2
  78. package/dashboard/dist/assets/{McpRunDetailPage-Dsa3G3XQ.js.map → McpRunDetailPage-SzOyhDBW.js.map} +1 -1
  79. package/dashboard/dist/assets/{McpRunsPage-uTbym66Y.js → McpRunsPage-BwgB6mRV.js} +2 -2
  80. package/dashboard/dist/assets/{McpRunsPage-uTbym66Y.js.map → McpRunsPage-BwgB6mRV.js.map} +1 -1
  81. package/dashboard/dist/assets/{NamespacePill-BGf0jHwE.js → NamespacePill-CwKdoyRM.js} +2 -2
  82. package/dashboard/dist/assets/{NamespacePill-BGf0jHwE.js.map → NamespacePill-CwKdoyRM.js.map} +1 -1
  83. package/dashboard/dist/assets/OperationsPage-BCr21p8I.js +2 -0
  84. package/dashboard/dist/assets/OperationsPage-BCr21p8I.js.map +1 -0
  85. package/dashboard/dist/assets/{OperatorDashboard-i8cxaDlk.js → OperatorDashboard-DSXh-yYw.js} +2 -2
  86. package/dashboard/dist/assets/{OperatorDashboard-i8cxaDlk.js.map → OperatorDashboard-DSXh-yYw.js.map} +1 -1
  87. package/dashboard/dist/assets/{PageHeader-BZJsEVOC.js → PageHeader-BhaWTWcM.js} +2 -2
  88. package/dashboard/dist/assets/{PageHeader-BZJsEVOC.js.map → PageHeader-BhaWTWcM.js.map} +1 -1
  89. package/dashboard/dist/assets/{PageHeaderWithStats-BWuD7s9c.js → PageHeaderWithStats-yex5WmHB.js} +2 -2
  90. package/dashboard/dist/assets/{PageHeaderWithStats-BWuD7s9c.js.map → PageHeaderWithStats-yex5WmHB.js.map} +1 -1
  91. package/dashboard/dist/assets/{ProcessDetailPage-CP7GZhgs.js → ProcessDetailPage-iIB1rwEt.js} +2 -2
  92. package/dashboard/dist/assets/{ProcessDetailPage-CP7GZhgs.js.map → ProcessDetailPage-iIB1rwEt.js.map} +1 -1
  93. package/dashboard/dist/assets/{ProcessesListPage-btsU6hBR.js → ProcessesListPage-BrqWR2zr.js} +2 -2
  94. package/dashboard/dist/assets/{ProcessesListPage-btsU6hBR.js.map → ProcessesListPage-BrqWR2zr.js.map} +1 -1
  95. package/dashboard/dist/assets/RoleDetailPage-DS37tJhq.js +11 -0
  96. package/dashboard/dist/assets/RoleDetailPage-DS37tJhq.js.map +1 -0
  97. package/dashboard/dist/assets/{RolePill-Pb9TKSQN.js → RolePill-Cl71eDUA.js} +2 -2
  98. package/dashboard/dist/assets/{RolePill-Pb9TKSQN.js.map → RolePill-Cl71eDUA.js.map} +1 -1
  99. package/dashboard/dist/assets/{RolesPage-1DBoEMsb.js → RolesPage-Bzp9FhAl.js} +2 -2
  100. package/dashboard/dist/assets/{RolesPage-1DBoEMsb.js.map → RolesPage-Bzp9FhAl.js.map} +1 -1
  101. package/dashboard/dist/assets/{RunAsSelector-Cp6j3FEY.js → RunAsSelector-DXbHRxQs.js} +2 -2
  102. package/dashboard/dist/assets/{RunAsSelector-Cp6j3FEY.js.map → RunAsSelector-DXbHRxQs.js.map} +1 -1
  103. package/dashboard/dist/assets/{StickyPagination-Pr8kawv8.js → StickyPagination-D8XuMSWD.js} +2 -2
  104. package/dashboard/dist/assets/{StickyPagination-Pr8kawv8.js.map → StickyPagination-D8XuMSWD.js.map} +1 -1
  105. package/dashboard/dist/assets/{StreamMessageDetail-BqmQ-N3y.js → StreamMessageDetail-D5v95pmz.js} +2 -2
  106. package/dashboard/dist/assets/{StreamMessageDetail-BqmQ-N3y.js.map → StreamMessageDetail-D5v95pmz.js.map} +1 -1
  107. package/dashboard/dist/assets/{SwimlaneTimeline-y1YmXrsj.js → SwimlaneTimeline-CUEf2R5R.js} +2 -2
  108. package/dashboard/dist/assets/{SwimlaneTimeline-y1YmXrsj.js.map → SwimlaneTimeline-CUEf2R5R.js.map} +1 -1
  109. package/dashboard/dist/assets/{TagInput-Df4myuYF.js → TagInput-B_WQiuer.js} +2 -2
  110. package/dashboard/dist/assets/{TagInput-Df4myuYF.js.map → TagInput-B_WQiuer.js.map} +1 -1
  111. package/dashboard/dist/assets/{TaskDetailPage-CT5rXHK5.js → TaskDetailPage-BJvKNwEe.js} +2 -2
  112. package/dashboard/dist/assets/{TaskDetailPage-CT5rXHK5.js.map → TaskDetailPage-BJvKNwEe.js.map} +1 -1
  113. package/dashboard/dist/assets/{TaskQueuePill-YalYdCNo.js → TaskQueuePill-Lgn2obNU.js} +2 -2
  114. package/dashboard/dist/assets/{TaskQueuePill-YalYdCNo.js.map → TaskQueuePill-Lgn2obNU.js.map} +1 -1
  115. package/dashboard/dist/assets/{TasksListPage-DOy6JlKS.js → TasksListPage-CM7BWh_h.js} +2 -2
  116. package/dashboard/dist/assets/{TasksListPage-DOy6JlKS.js.map → TasksListPage-CM7BWh_h.js.map} +1 -1
  117. package/dashboard/dist/assets/{TimeAgo-BAQwZSpq.js → TimeAgo-D1K7xKq5.js} +2 -2
  118. package/dashboard/dist/assets/{TimeAgo-BAQwZSpq.js.map → TimeAgo-D1K7xKq5.js.map} +1 -1
  119. package/dashboard/dist/assets/{TimestampCell-BK_jufns.js → TimestampCell-7G0echgP.js} +2 -2
  120. package/dashboard/dist/assets/{TimestampCell-BK_jufns.js.map → TimestampCell-7G0echgP.js.map} +1 -1
  121. package/dashboard/dist/assets/{ToolPill-VGpT6Quy.js → ToolPill-uVqcbHPE.js} +2 -2
  122. package/dashboard/dist/assets/{ToolPill-VGpT6Quy.js.map → ToolPill-uVqcbHPE.js.map} +1 -1
  123. package/dashboard/dist/assets/{ToolTestPanel-5PMQyUCe.js → ToolTestPanel-LxuJvURK.js} +2 -2
  124. package/dashboard/dist/assets/{ToolTestPanel-5PMQyUCe.js.map → ToolTestPanel-LxuJvURK.js.map} +1 -1
  125. package/dashboard/dist/assets/{TopicDetailPage-Cn1ShfR8.js → TopicDetailPage-JvAX6rrR.js} +2 -2
  126. package/dashboard/dist/assets/{TopicDetailPage-Cn1ShfR8.js.map → TopicDetailPage-JvAX6rrR.js.map} +1 -1
  127. package/dashboard/dist/assets/{TopicsPage-D67tjS8L.js → TopicsPage-tFT2AcTz.js} +2 -2
  128. package/dashboard/dist/assets/{TopicsPage-D67tjS8L.js.map → TopicsPage-tFT2AcTz.js.map} +1 -1
  129. package/dashboard/dist/assets/{UserName-Dsu1Jk4N.js → UserName-Djz0nmaZ.js} +2 -2
  130. package/dashboard/dist/assets/{UserName-Dsu1Jk4N.js.map → UserName-Djz0nmaZ.js.map} +1 -1
  131. package/dashboard/dist/assets/{WorkflowExecutionPage-BwQoOmUE.js → WorkflowExecutionPage-f53hilbI.js} +2 -2
  132. package/dashboard/dist/assets/{WorkflowExecutionPage-BwQoOmUE.js.map → WorkflowExecutionPage-f53hilbI.js.map} +1 -1
  133. package/dashboard/dist/assets/{WorkflowPill-pPUqmx6Q.js → WorkflowPill-IDjTRfQ8.js} +2 -2
  134. package/dashboard/dist/assets/{WorkflowPill-pPUqmx6Q.js.map → WorkflowPill-IDjTRfQ8.js.map} +1 -1
  135. package/dashboard/dist/assets/{WorkflowsDashboard-D3z7COgH.js → WorkflowsDashboard-DM9Xe665.js} +2 -2
  136. package/dashboard/dist/assets/{WorkflowsDashboard-D3z7COgH.js.map → WorkflowsDashboard-DM9Xe665.js.map} +1 -1
  137. package/dashboard/dist/assets/{WorkflowsOverview-C15JcxyY.js → WorkflowsOverview-CUGSrx9N.js} +2 -2
  138. package/dashboard/dist/assets/{WorkflowsOverview-C15JcxyY.js.map → WorkflowsOverview-CUGSrx9N.js.map} +1 -1
  139. package/dashboard/dist/assets/{YamlWorkflowDetailPage-BYq3t7E-.js → YamlWorkflowDetailPage-B1_yqxAw.js} +2 -2
  140. package/dashboard/dist/assets/{YamlWorkflowDetailPage-BYq3t7E-.js.map → YamlWorkflowDetailPage-B1_yqxAw.js.map} +1 -1
  141. package/dashboard/dist/assets/{YamlWorkflowsPage-B44WgwE_.js → YamlWorkflowsPage-NtGvqiu7.js} +2 -2
  142. package/dashboard/dist/assets/{YamlWorkflowsPage-B44WgwE_.js.map → YamlWorkflowsPage-NtGvqiu7.js.map} +1 -1
  143. package/dashboard/dist/assets/{agents-BRmmK6Y9.js → agents-tML6AjDj.js} +2 -2
  144. package/dashboard/dist/assets/{agents-BRmmK6Y9.js.map → agents-tML6AjDj.js.map} +1 -1
  145. package/dashboard/dist/assets/{bots-BD75mzGG.js → bots-Cb0WTwhY.js} +2 -2
  146. package/dashboard/dist/assets/{bots-BD75mzGG.js.map → bots-Cb0WTwhY.js.map} +1 -1
  147. package/dashboard/dist/assets/{capabilities-DDgC_Kds.js → capabilities-BGZogsi_.js} +2 -2
  148. package/dashboard/dist/assets/{capabilities-DDgC_Kds.js.map → capabilities-BGZogsi_.js.map} +1 -1
  149. package/dashboard/dist/assets/{controlplane-BJh12lMv.js → controlplane-B7VJTlZD.js} +2 -2
  150. package/dashboard/dist/assets/{controlplane-BJh12lMv.js.map → controlplane-B7VJTlZD.js.map} +1 -1
  151. package/dashboard/dist/assets/{escalation-DIJt2sgn.js → escalation-Bl-SQV_u.js} +2 -2
  152. package/dashboard/dist/assets/{escalation-DIJt2sgn.js.map → escalation-Bl-SQV_u.js.map} +1 -1
  153. package/dashboard/dist/assets/{escalation-columns-zl6E94Up.js → escalation-columns-BlJOswRx.js} +2 -2
  154. package/dashboard/dist/assets/{escalation-columns-zl6E94Up.js.map → escalation-columns-BlJOswRx.js.map} +1 -1
  155. package/dashboard/dist/assets/{index-BhznDv-4.js → index--WeZDfeN.js} +2 -2
  156. package/dashboard/dist/assets/{index-BhznDv-4.js.map → index--WeZDfeN.js.map} +1 -1
  157. package/dashboard/dist/assets/{index-DU7YumVJ.js → index-0o71lRuY.js} +2 -2
  158. package/dashboard/dist/assets/{index-DU7YumVJ.js.map → index-0o71lRuY.js.map} +1 -1
  159. package/dashboard/dist/assets/{index-CV4lEd4I.js → index-3A_5CL81.js} +2 -2
  160. package/dashboard/dist/assets/{index-CV4lEd4I.js.map → index-3A_5CL81.js.map} +1 -1
  161. package/dashboard/dist/assets/{index-DVVIeBBk.js → index-BorpZbZA.js} +2 -2
  162. package/dashboard/dist/assets/{index-DVVIeBBk.js.map → index-BorpZbZA.js.map} +1 -1
  163. package/dashboard/dist/assets/{index-CknKy4RA.js → index-CBv3UxF2.js} +2 -2
  164. package/dashboard/dist/assets/{index-CknKy4RA.js.map → index-CBv3UxF2.js.map} +1 -1
  165. package/dashboard/dist/assets/{index-BUbzfEA9.js → index-D0-8byNu.js} +2 -2
  166. package/dashboard/dist/assets/{index-BUbzfEA9.js.map → index-D0-8byNu.js.map} +1 -1
  167. package/dashboard/dist/assets/{index-j-C7g_Ej.js → index-DcbEIXit.js} +2 -2
  168. package/dashboard/dist/assets/{index-j-C7g_Ej.js.map → index-DcbEIXit.js.map} +1 -1
  169. package/dashboard/dist/assets/{index-DrUkTcn-.js → index-DfDfJivY.js} +2 -2
  170. package/dashboard/dist/assets/{index-DrUkTcn-.js.map → index-DfDfJivY.js.map} +1 -1
  171. package/dashboard/dist/assets/{index-CtDf8vvQ.js → index-Dgw1KKpq.js} +9 -9
  172. package/dashboard/dist/assets/index-Dgw1KKpq.js.map +1 -0
  173. package/dashboard/dist/assets/index-Il_g7kw5.js +6 -0
  174. package/dashboard/dist/assets/{index-DccCwyZl.js.map → index-Il_g7kw5.js.map} +1 -1
  175. package/dashboard/dist/assets/{index-B0kmiSmt.js → index-SCrD3l-K.js} +2 -2
  176. package/dashboard/dist/assets/{index-B0kmiSmt.js.map → index-SCrD3l-K.js.map} +1 -1
  177. package/dashboard/dist/assets/{index-CDPBXB4e.js → index-TiKh2Ctj.js} +2 -2
  178. package/dashboard/dist/assets/{index-CDPBXB4e.js.map → index-TiKh2Ctj.js.map} +1 -1
  179. package/dashboard/dist/assets/index-Wny0bRgq.css +1 -0
  180. package/dashboard/dist/assets/{index-CpgV4Kkp.js → index-e97M6GoI.js} +2 -2
  181. package/dashboard/dist/assets/{index-CpgV4Kkp.js.map → index-e97M6GoI.js.map} +1 -1
  182. package/dashboard/dist/assets/{knowledge-pNDWACXB.js → knowledge-CDzw4E6q.js} +2 -2
  183. package/dashboard/dist/assets/{knowledge-pNDWACXB.js.map → knowledge-CDzw4E6q.js.map} +1 -1
  184. package/dashboard/dist/assets/{mcp-xddLBwUW.js → mcp-D_-NQtnG.js} +2 -2
  185. package/dashboard/dist/assets/{mcp-xddLBwUW.js.map → mcp-D_-NQtnG.js.map} +1 -1
  186. package/dashboard/dist/assets/{mcp-query-CmNjvxTG.js → mcp-query-BqZXdf9A.js} +2 -2
  187. package/dashboard/dist/assets/{mcp-query-CmNjvxTG.js.map → mcp-query-BqZXdf9A.js.map} +1 -1
  188. package/dashboard/dist/assets/{pipelines-B05sd2nB.js → pipelines-CNLhgj8b.js} +2 -2
  189. package/dashboard/dist/assets/{pipelines-B05sd2nB.js.map → pipelines-CNLhgj8b.js.map} +1 -1
  190. package/dashboard/dist/assets/roles-CFD2eYap.js +2 -0
  191. package/dashboard/dist/assets/roles-CFD2eYap.js.map +1 -0
  192. package/dashboard/dist/assets/{tasks-Bm1OKrJA.js → tasks-DcSvF636.js} +2 -2
  193. package/dashboard/dist/assets/{tasks-Bm1OKrJA.js.map → tasks-DcSvF636.js.map} +1 -1
  194. package/dashboard/dist/assets/{topics-BVIqtmd4.js → topics-DI8TFSZv.js} +2 -2
  195. package/dashboard/dist/assets/{topics-BVIqtmd4.js.map → topics-DI8TFSZv.js.map} +1 -1
  196. package/dashboard/dist/assets/{useEventHooks-_qCzU74s.js → useEventHooks-DcIgMDh3.js} +2 -2
  197. package/dashboard/dist/assets/{useEventHooks-_qCzU74s.js.map → useEventHooks-DcIgMDh3.js.map} +1 -1
  198. package/dashboard/dist/assets/{useNamespace-CDnCSFpG.js → useNamespace-C88ZxGoX.js} +2 -2
  199. package/dashboard/dist/assets/{useNamespace-CDnCSFpG.js.map → useNamespace-C88ZxGoX.js.map} +1 -1
  200. package/dashboard/dist/assets/{useYamlActivityEvents-zrHMhaFM.js → useYamlActivityEvents-D2-yVjJ7.js} +2 -2
  201. package/dashboard/dist/assets/{useYamlActivityEvents-zrHMhaFM.js.map → useYamlActivityEvents-D2-yVjJ7.js.map} +1 -1
  202. package/dashboard/dist/assets/{users-BPBMZg27.js → users-C0WV36X8.js} +2 -2
  203. package/dashboard/dist/assets/{users-BPBMZg27.js.map → users-C0WV36X8.js.map} +1 -1
  204. package/dashboard/dist/assets/{vendor-icons-C1zVP_dv.js → vendor-icons-CEWCGPeJ.js} +76 -76
  205. package/dashboard/dist/assets/vendor-icons-CEWCGPeJ.js.map +1 -0
  206. package/dashboard/dist/assets/{workflows-CT6nTL1I.js → workflows-CkPv7xk-.js} +2 -2
  207. package/dashboard/dist/assets/{workflows-CT6nTL1I.js.map → workflows-CkPv7xk-.js.map} +1 -1
  208. package/dashboard/dist/assets/{yaml-workflows-CdfjkOKU.js → yaml-workflows-BjrMQLTK.js} +2 -2
  209. package/dashboard/dist/assets/{yaml-workflows-CdfjkOKU.js.map → yaml-workflows-BjrMQLTK.js.map} +1 -1
  210. package/dashboard/dist/index.html +3 -3
  211. package/docs/api/http/roles.md +62 -0
  212. package/docs/api/mcp/admin.md +32 -1
  213. package/docs/api/mcp/human-queue.md +2 -0
  214. package/docs/api/sdk/roles.md +39 -0
  215. package/docs/cli.md +13 -0
  216. package/docs/dashboard.md +66 -17
  217. package/docs/data.md +34 -0
  218. package/docs/hitl-guide.md +25 -3
  219. package/package.json +2 -2
  220. package/dashboard/dist/assets/OperationsPage-CAmiFHI6.js +0 -2
  221. package/dashboard/dist/assets/OperationsPage-CAmiFHI6.js.map +0 -1
  222. package/dashboard/dist/assets/RoleDetailPage-BB_e330f.js +0 -16
  223. package/dashboard/dist/assets/RoleDetailPage-BB_e330f.js.map +0 -1
  224. package/dashboard/dist/assets/index-BRtKQtQZ.css +0 -1
  225. package/dashboard/dist/assets/index-CtDf8vvQ.js.map +0 -1
  226. package/dashboard/dist/assets/index-DccCwyZl.js +0 -6
  227. package/dashboard/dist/assets/roles-bRgaMcdy.js +0 -2
  228. package/dashboard/dist/assets/roles-bRgaMcdy.js.map +0 -1
  229. package/dashboard/dist/assets/vendor-icons-C1zVP_dv.js.map +0 -1
@@ -40,22 +40,24 @@ exports.createEscalation = createEscalation;
40
40
  const ajv_1 = __importDefault(require("ajv"));
41
41
  const escalationService = __importStar(require("../../services/escalation"));
42
42
  const roleService = __importStar(require("../../services/role"));
43
+ const escalation_1 = require("../../types/escalation");
43
44
  const helpers_1 = require("./helpers");
44
45
  // strict: false — role metadata_schemas use the project's house extension
45
46
  // keywords (x-lt-widget etc., the same style as seeded form_schemas); Ajv 8's
46
47
  // strict mode throws at compile on any unknown keyword, which would turn every
47
48
  // metadata-bearing create for that role into a 500.
48
49
  const ajv = new ajv_1.default({ allErrors: true, strict: false });
49
- // Compiled validators cached per role, keyed by the schema's serialized form
50
- // so an admin's schema edit invalidates the stale validator on the next create.
50
+ // Compiled validators cached per role+version, keyed by the schema's serialized
51
+ // form so an admin's schema edit invalidates the stale validator on the next
52
+ // create. Pinned versions are immutable, but the same key logic covers both.
51
53
  const validatorCache = new Map();
52
- function compileRoleValidator(role, schema) {
54
+ function compileRoleValidator(cacheId, schema) {
53
55
  const key = JSON.stringify(schema);
54
- const cached = validatorCache.get(role);
56
+ const cached = validatorCache.get(cacheId);
55
57
  if (cached && cached.key === key)
56
58
  return cached.validate;
57
59
  const validate = ajv.compile(schema);
58
- validatorCache.set(role, { key, validate });
60
+ validatorCache.set(cacheId, { key, validate });
59
61
  return validate;
60
62
  }
61
63
  // ── Create ────────────────────────────────────────────────────────────────
@@ -92,13 +94,29 @@ async function createEscalation(input, auth) {
92
94
  if (denied) {
93
95
  return { status: 403, error: `You must have write access to the "${role}" role or be a superadmin to create escalations for it` };
94
96
  }
95
- // Validate metadata against the role's declared schema (if any).
97
+ // Validate metadata against the role's declared schema (if any). A
98
+ // metadata.schema_version pin selects that immutable snapshot; a pin that
99
+ // names a missing version is a 400, never a silent fall-through to latest.
96
100
  if (input.metadata) {
97
- const schema = await roleService.getRoleMetadataSchema(role);
101
+ const pinned = input.metadata[escalation_1.ESCALATION_METADATA_KEYS.SCHEMA_VERSION];
102
+ if (pinned !== undefined && (!Number.isInteger(pinned) || pinned < 1)) {
103
+ return { status: 400, error: `metadata.${escalation_1.ESCALATION_METADATA_KEYS.SCHEMA_VERSION} must be a positive integer` };
104
+ }
105
+ let schema;
106
+ if (pinned !== undefined) {
107
+ const snapshot = await roleService.getRoleSchema(role, pinned);
108
+ if (!snapshot) {
109
+ return { status: 400, error: `Schema version ${pinned} does not exist for role "${role}"` };
110
+ }
111
+ schema = snapshot.metadata_schema;
112
+ }
113
+ else {
114
+ schema = await roleService.getRoleMetadataSchema(role);
115
+ }
98
116
  if (schema) {
99
117
  let validate;
100
118
  try {
101
- validate = compileRoleValidator(role, schema);
119
+ validate = compileRoleValidator(`${role}@${pinned ?? 'live'}`, schema);
102
120
  }
103
121
  catch (compileErr) {
104
122
  // A broken stored schema is the role admin's bug, not the creator's —
@@ -108,7 +126,10 @@ async function createEscalation(input, auth) {
108
126
  error: `metadata_schema for role "${role}" is not a valid JSON Schema: ${compileErr.message}`,
109
127
  };
110
128
  }
111
- const valid = validate(input.metadata);
129
+ // The pin is a system key, not caller data — strip it so schemas with
130
+ // additionalProperties: false still validate the caller's bag.
131
+ const { [escalation_1.ESCALATION_METADATA_KEYS.SCHEMA_VERSION]: _pin, ...callerMetadata } = input.metadata;
132
+ const valid = validate(callerMetadata);
112
133
  if (!valid) {
113
134
  const msgs = (validate.errors ?? []).map((e) => `${e.instancePath || '(root)'} ${e.message}`).join('; ');
114
135
  return { status: 400, error: `metadata does not match the role schema: ${msgs}` };
@@ -91,6 +91,38 @@ export declare function updateRole(input: {
91
91
  sla_minutes?: number | null;
92
92
  target_per_hour?: number | null;
93
93
  worker_count?: number | null;
94
+ /** Replace the upstream-input set (omitted = preserve; null or [] = clear). */
95
+ upstream_roles?: string[] | null;
96
+ /** Recorded on the schema snapshot when this update changes a schema field. */
97
+ change_summary?: string;
98
+ }): Promise<LTApiResult>;
99
+ /**
100
+ * Fetch a role's schema pair (form_schema + metadata_schema).
101
+ *
102
+ * With `version`, returns that immutable snapshot from the version history —
103
+ * a missing version is a 404, never a silent fall-through to latest. Without
104
+ * `version`, returns the live (latest) schema along with the current version
105
+ * number so callers can tell what they got.
106
+ *
107
+ * @param input.role — the role whose schema to fetch
108
+ * @param input.version — optional version pin (positive integer)
109
+ * @returns `{ status: 200, data: RoleSchemaVersion }` on success
110
+ */
111
+ export declare function getRoleSchema(input: {
112
+ role: string;
113
+ version?: number;
114
+ }): Promise<LTApiResult>;
115
+ /**
116
+ * List a role's schema version history, newest first. Each entry carries the
117
+ * version number, presence flags for the two schemas, the change summary, and
118
+ * whether it is the role's current version. Full snapshots come from
119
+ * `getRoleSchema({ role, version })`.
120
+ *
121
+ * @param input.role — the role whose history to list
122
+ * @returns `{ status: 200, data: { versions: RoleSchemaVersionSummary[] } }` on success
123
+ */
124
+ export declare function listRoleSchemaVersions(input: {
125
+ role: string;
94
126
  }): Promise<LTApiResult>;
95
127
  /**
96
128
  * Delete a role from the system. Requires admin privileges.
@@ -42,6 +42,8 @@ exports.removeEscalationChain = removeEscalationChain;
42
42
  exports.getEscalationTargets = getEscalationTargets;
43
43
  exports.replaceEscalationTargets = replaceEscalationTargets;
44
44
  exports.updateRole = updateRole;
45
+ exports.getRoleSchema = getRoleSchema;
46
+ exports.listRoleSchemaVersions = listRoleSchemaVersions;
45
47
  exports.deleteRole = deleteRole;
46
48
  const roleService = __importStar(require("../services/role"));
47
49
  /**
@@ -220,6 +222,19 @@ async function updateRole(input) {
220
222
  if (input.parent_role != null && input.parent_role === input.role) {
221
223
  return { status: 400, error: 'parent_role must reference a different role' };
222
224
  }
225
+ if (input.upstream_roles != null) {
226
+ if (!Array.isArray(input.upstream_roles) || input.upstream_roles.some((u) => typeof u !== 'string' || !u.trim())) {
227
+ return { status: 400, error: 'upstream_roles must be an array of role names' };
228
+ }
229
+ if (input.upstream_roles.includes(input.role)) {
230
+ return { status: 400, error: 'upstream_roles must not include the role itself' };
231
+ }
232
+ const known = new Set(await roleService.listDistinctRoles());
233
+ const unknown = input.upstream_roles.filter((u) => !known.has(u));
234
+ if (unknown.length > 0) {
235
+ return { status: 400, error: `upstream_roles reference unknown role(s): ${unknown.join(', ')}` };
236
+ }
237
+ }
223
238
  const updated = await roleService.updateRoleMetadata(input.role, {
224
239
  title: input.title,
225
240
  description: input.description,
@@ -231,6 +246,8 @@ async function updateRole(input) {
231
246
  sla_minutes: input.sla_minutes,
232
247
  target_per_hour: input.target_per_hour,
233
248
  worker_count: input.worker_count,
249
+ upstream_roles: input.upstream_roles,
250
+ change_summary: input.change_summary,
234
251
  });
235
252
  if (!updated) {
236
253
  return { status: 404, error: `Role '${input.role}' not found` };
@@ -241,6 +258,62 @@ async function updateRole(input) {
241
258
  return { status: 500, error: err.message };
242
259
  }
243
260
  }
261
+ /**
262
+ * Fetch a role's schema pair (form_schema + metadata_schema).
263
+ *
264
+ * With `version`, returns that immutable snapshot from the version history —
265
+ * a missing version is a 404, never a silent fall-through to latest. Without
266
+ * `version`, returns the live (latest) schema along with the current version
267
+ * number so callers can tell what they got.
268
+ *
269
+ * @param input.role — the role whose schema to fetch
270
+ * @param input.version — optional version pin (positive integer)
271
+ * @returns `{ status: 200, data: RoleSchemaVersion }` on success
272
+ */
273
+ async function getRoleSchema(input) {
274
+ try {
275
+ if (!input.role) {
276
+ return { status: 400, error: 'role is required' };
277
+ }
278
+ if (input.version !== undefined && (!Number.isInteger(input.version) || input.version < 1)) {
279
+ return { status: 400, error: 'version must be a positive integer' };
280
+ }
281
+ const schema = await roleService.getRoleSchema(input.role, input.version);
282
+ if (!schema) {
283
+ return {
284
+ status: 404,
285
+ error: input.version !== undefined
286
+ ? `Schema version ${input.version} not found for role '${input.role}'`
287
+ : `Role '${input.role}' not found`,
288
+ };
289
+ }
290
+ return { status: 200, data: schema };
291
+ }
292
+ catch (err) {
293
+ return { status: 500, error: err.message };
294
+ }
295
+ }
296
+ /**
297
+ * List a role's schema version history, newest first. Each entry carries the
298
+ * version number, presence flags for the two schemas, the change summary, and
299
+ * whether it is the role's current version. Full snapshots come from
300
+ * `getRoleSchema({ role, version })`.
301
+ *
302
+ * @param input.role — the role whose history to list
303
+ * @returns `{ status: 200, data: { versions: RoleSchemaVersionSummary[] } }` on success
304
+ */
305
+ async function listRoleSchemaVersions(input) {
306
+ try {
307
+ if (!input.role) {
308
+ return { status: 400, error: 'role is required' };
309
+ }
310
+ const versions = await roleService.listRoleSchemaVersions(input.role);
311
+ return { status: 200, data: { versions } };
312
+ }
313
+ catch (err) {
314
+ return { status: 500, error: err.message };
315
+ }
316
+ }
244
317
  /**
245
318
  * Delete a role from the system. Requires admin privileges.
246
319
  *
package/build/bin/ltc.js CHANGED
@@ -54,6 +54,7 @@ const pip = __importStar(require("../lib/cli/commands/pipelines"));
54
54
  const kb = __importStar(require("../lib/cli/commands/knowledge"));
55
55
  const mcp = __importStar(require("../lib/cli/commands/mcp"));
56
56
  const usr = __importStar(require("../lib/cli/commands/users"));
57
+ const roles = __importStar(require("../lib/cli/commands/roles"));
57
58
  const streams = __importStar(require("../lib/cli/commands/streams"));
58
59
  const pkg = require('../package.json');
59
60
  const envPath = path.resolve(process.cwd(), '.env');
@@ -277,6 +278,28 @@ usrCmd.command('list')
277
278
  usrCmd.command('get <id>')
278
279
  .option('--json', 'JSON output')
279
280
  .action(wrap(usr.getUser));
281
+ // ── Roles ────────────────────────────────────────────────────────────────
282
+ const rolesCmd = commander_1.program.command('roles').description('Roles — the queue-backed work surfaces where workflows hand off to people');
283
+ rolesCmd.command('list')
284
+ .option('--json', 'JSON output')
285
+ .option('-q, --quiet', 'Names only')
286
+ .action(wrap(roles.listRoles));
287
+ rolesCmd.command('schema <role>')
288
+ .description('Show a role\'s form/metadata schema (latest, or a pinned version)')
289
+ .option('--version <n>', 'Read an immutable snapshot from the version history')
290
+ .option('--json', 'JSON output')
291
+ .action(wrap(roles.getRoleSchema));
292
+ rolesCmd.command('schema-versions <role>')
293
+ .description('List a role\'s schema version history (newest first)')
294
+ .option('--json', 'JSON output')
295
+ .option('-q, --quiet', 'Versions only')
296
+ .action(wrap(roles.listRoleSchemaVersions));
297
+ rolesCmd.command('save-schema <role>')
298
+ .description('Save the role\'s escalation form schema (a change creates the next version)')
299
+ .option('--file <path>', 'JSON Schema file (reads stdin when omitted)')
300
+ .option('--summary <text>', 'Change summary recorded on the new version')
301
+ .option('--json', 'JSON output')
302
+ .action(wrap(roles.saveRoleSchema));
280
303
  // ── Streams ─────────────────────────────────────────────────────────────
281
304
  const streamsCmd = commander_1.program.command('streams').description('Browse stream messages (admin)');
282
305
  streamsCmd.command('list')
@@ -0,0 +1,17 @@
1
+ export declare function listRoles(opts: {
2
+ json?: boolean;
3
+ quiet?: boolean;
4
+ }): Promise<void>;
5
+ export declare function getRoleSchema(role: string, opts: {
6
+ version?: string;
7
+ json?: boolean;
8
+ }): Promise<void>;
9
+ export declare function listRoleSchemaVersions(role: string, opts: {
10
+ json?: boolean;
11
+ quiet?: boolean;
12
+ }): Promise<void>;
13
+ export declare function saveRoleSchema(role: string, opts: {
14
+ file?: string;
15
+ summary?: string;
16
+ json?: boolean;
17
+ }): Promise<void>;
@@ -0,0 +1,102 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.listRoles = listRoles;
40
+ exports.getRoleSchema = getRoleSchema;
41
+ exports.listRoleSchemaVersions = listRoleSchemaVersions;
42
+ exports.saveRoleSchema = saveRoleSchema;
43
+ const fs = __importStar(require("fs"));
44
+ const picocolors_1 = __importDefault(require("picocolors"));
45
+ const client_1 = require("../client");
46
+ const format_1 = require("../format");
47
+ const ROLE_COLUMNS = [
48
+ { key: 'role', label: 'Role', width: 20 },
49
+ { key: 'title', label: 'Title', width: 24 },
50
+ { key: 'current_schema_version', label: 'Schema v', width: 8, align: 'right' },
51
+ { key: 'user_count', label: 'Users', width: 6, align: 'right' },
52
+ { key: 'workflow_count', label: 'Workflows', width: 9, align: 'right' },
53
+ ];
54
+ const VERSION_COLUMNS = [
55
+ { key: 'version', label: 'Version', width: 8, align: 'right' },
56
+ { key: 'is_current', label: 'Current', width: 8, format: (v) => (v ? '●' : '') },
57
+ { key: 'change_summary', label: 'Summary', width: 40 },
58
+ { key: 'created_at', label: 'Created', width: 12, format: format_1.formatTime },
59
+ ];
60
+ async function listRoles(opts) {
61
+ const data = await (0, client_1.apiFetch)('/roles/details');
62
+ (0, format_1.output)(data, data.roles || [], ROLE_COLUMNS, opts, 'role');
63
+ }
64
+ async function getRoleSchema(role, opts) {
65
+ const params = new URLSearchParams();
66
+ if (opts.version)
67
+ params.set('version', opts.version);
68
+ const qs = params.toString();
69
+ const data = await (0, client_1.apiFetch)(`/roles/${encodeURIComponent(role)}/schema${qs ? `?${qs}` : ''}`);
70
+ if (opts.json) {
71
+ console.log(JSON.stringify(data, null, 2));
72
+ return;
73
+ }
74
+ const versionLabel = data.version != null ? `v${data.version}` : 'unversioned';
75
+ const pinNote = opts.version ? ' (pinned)' : ` (latest: v${data.latest_version ?? '—'})`;
76
+ console.log(`\n ${picocolors_1.default.bold(role)} schema ${versionLabel}${pinNote}`);
77
+ if (data.change_summary)
78
+ console.log(` ${data.change_summary}`);
79
+ console.log(`\n ${picocolors_1.default.bold('form_schema')}\n${JSON.stringify(data.form_schema, null, 2)}`);
80
+ console.log(`\n ${picocolors_1.default.bold('metadata_schema')}\n${JSON.stringify(data.metadata_schema, null, 2)}`);
81
+ console.log();
82
+ }
83
+ async function listRoleSchemaVersions(role, opts) {
84
+ const data = await (0, client_1.apiFetch)(`/roles/${encodeURIComponent(role)}/schema/versions`);
85
+ (0, format_1.output)(data, data.versions || [], VERSION_COLUMNS, opts, 'version');
86
+ }
87
+ async function saveRoleSchema(role, opts) {
88
+ // One PATCH carrying only the schema (and its summary) — a schema change
89
+ // appends the next immutable version; an identical save leaves it alone.
90
+ const raw = opts.file ? fs.readFileSync(opts.file, 'utf8') : fs.readFileSync(0, 'utf8');
91
+ const schema = JSON.parse(raw);
92
+ const data = await (0, client_1.apiFetch)(`/roles/${encodeURIComponent(role)}`, {
93
+ method: 'PATCH',
94
+ body: JSON.stringify({ form_schema: schema, change_summary: opts.summary }),
95
+ });
96
+ if (opts.json) {
97
+ console.log(JSON.stringify(data, null, 2));
98
+ return;
99
+ }
100
+ const version = data.current_schema_version != null ? ` — v${data.current_schema_version} in use` : '';
101
+ console.log(`\n ${picocolors_1.default.green('✓')} Saved ${role} escalation schema${version}\n`);
102
+ }
@@ -0,0 +1,40 @@
1
+ -- Migration 017: Versioned role schemas
2
+ --
3
+ -- lt_role_schemas holds an immutable snapshot of a role's form_schema and
4
+ -- metadata_schema per version. lt_roles.form_schema / metadata_schema remain
5
+ -- the live "latest" copy (single-query reads stay cheap); every schema edit
6
+ -- also appends the next (role, version) snapshot and advances
7
+ -- lt_roles.current_schema_version — see UPDATE_ROLE_METADATA in
8
+ -- services/role/sql.ts, which does both in one atomic statement.
9
+ --
10
+ -- Why: an escalation can pin the exact schema it was created against
11
+ -- (metadata.schema_version, set via conditionLT's schemaVersion config field).
12
+ -- The resolver UI then renders the pinned version even after the role's
13
+ -- schema moves on. Escalations without a pin always use the latest.
14
+
15
+ CREATE TABLE IF NOT EXISTS lt_role_schemas (
16
+ role TEXT NOT NULL REFERENCES lt_roles(role) ON DELETE CASCADE,
17
+ version INTEGER NOT NULL,
18
+ form_schema JSONB,
19
+ metadata_schema JSONB,
20
+ change_summary TEXT,
21
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
22
+ PRIMARY KEY (role, version)
23
+ );
24
+
25
+ ALTER TABLE lt_roles
26
+ ADD COLUMN IF NOT EXISTS current_schema_version INTEGER;
27
+
28
+ -- Seed version 1 from the live columns for roles that already carry a schema.
29
+ -- ON CONFLICT keeps re-runs (and multi-container boot races behind the
30
+ -- advisory lock) no-ops.
31
+ INSERT INTO lt_role_schemas (role, version, form_schema, metadata_schema, change_summary)
32
+ SELECT role, 1, form_schema, metadata_schema, 'Initial version (seeded from lt_roles)'
33
+ FROM lt_roles
34
+ WHERE form_schema IS NOT NULL OR metadata_schema IS NOT NULL
35
+ ON CONFLICT (role, version) DO NOTHING;
36
+
37
+ UPDATE lt_roles
38
+ SET current_schema_version = 1
39
+ WHERE current_schema_version IS NULL
40
+ AND (form_schema IS NOT NULL OR metadata_schema IS NOT NULL);
@@ -0,0 +1,21 @@
1
+ -- Migration 018: Role upstream inputs
2
+ --
3
+ -- Execution is a graph; the Operations page tells its story as sequences.
4
+ -- lt_roles.parent_role stays the sequence membership — the single "prior
5
+ -- step" that places a station in one line. lt_role_upstreams carries the
6
+ -- other edges: the roles a station draws input from that live in OTHER
7
+ -- sequences (mixin-like, many allowed). The Operations chart renders these
8
+ -- as a merge affordance on the station rather than bending the line —
9
+ -- the table stays true to the queue, the SVG stays honest about the graph.
10
+ --
11
+ -- Example: a shoe side-quest (ordering → inserting) feeds shipping. ordering
12
+ -- roots its own sequence; shipping declares inserting as an upstream input
13
+ -- instead of pretending ordering descends from qa.
14
+
15
+ CREATE TABLE IF NOT EXISTS lt_role_upstreams (
16
+ role TEXT NOT NULL REFERENCES lt_roles(role) ON DELETE CASCADE,
17
+ upstream_role TEXT NOT NULL REFERENCES lt_roles(role) ON DELETE CASCADE,
18
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
19
+ PRIMARY KEY (role, upstream_role),
20
+ CHECK (role <> upstream_role)
21
+ );
@@ -103,6 +103,26 @@ router.patch('/:role', auth_1.requireRoleManager, async (req, res) => {
103
103
  const result = await api.updateRole({ ...req.body, role: req.params.role });
104
104
  res.status(result.status).json(result.data ?? { error: result.error });
105
105
  });
106
+ /**
107
+ * GET /api/roles/:role/schema
108
+ * Fetch the role's schema pair. `?version=N` pins an immutable snapshot from
109
+ * the version history; omitted, the live (latest) schema is returned with its
110
+ * current version number.
111
+ */
112
+ router.get('/:role/schema', async (req, res) => {
113
+ const versionParam = req.query.version;
114
+ const version = versionParam !== undefined ? Number(versionParam) : undefined;
115
+ const result = await api.getRoleSchema({ role: req.params.role, version });
116
+ res.status(result.status).json(result.data ?? { error: result.error });
117
+ });
118
+ /**
119
+ * GET /api/roles/:role/schema/versions
120
+ * List the role's schema version history (newest first).
121
+ */
122
+ router.get('/:role/schema/versions', async (req, res) => {
123
+ const result = await api.listRoleSchemaVersions({ role: req.params.role });
124
+ res.status(result.status).json(result.data ?? { error: result.error });
125
+ });
106
126
  /**
107
127
  * GET /api/roles/:role/escalation-targets
108
128
  * Get allowed escalation targets for a specific role.
@@ -2,26 +2,73 @@
2
2
  * ortho-populate — Populate the Operations dashboard with ortho pipeline history.
3
3
  *
4
4
  * Adapted from the boilerplate's 08-day-resolver.ts pattern:
5
- * - concurrent claim loop + resolve loop + watchdog via Promise.all
5
+ * - concurrent enqueue + claim loop + resolve loop + watchdog via Promise.all
6
6
  * - hold time read from esc.claimed_at (API), not tracked in memory
7
7
  * - simple resolve payload { approved: true, station: role }
8
8
  *
9
9
  * Usage:
10
10
  * npx ts-node scripts/ortho-populate.ts
11
11
  * ORDERS=50 HOLD_S=3 npx ts-node scripts/ortho-populate.ts
12
- * ORDERS=5 HOLD_S=0 POLL_MS=500 npx ts-node scripts/ortho-populate.ts
12
+ * ORDERS=10 HOLD_S=25 npx ts-node scripts/ortho-populate.ts
13
+ * ORDERS=5 HOLD_S=0 QUEUE_S=0 SHOES_PCT=0 POLL_MS=500 npx ts-node scripts/ortho-populate.ts
13
14
  *
14
15
  * Env vars:
15
- * ORDERS — number of orders to run (default 20)
16
- * HOLD_S — seconds to hold each escalation before resolving (default 2)
17
- * POLL_MS — poll interval ms (default 1000)
18
- * BASE_URL server URL (default http://localhost:3000)
16
+ * ORDERS — number of orders to run (default 20)
17
+ * HOLD_S average seconds to hold each claimed escalation before
18
+ * resolving (default 2)
19
+ * QUEUE_S average seconds an escalation waits in the queue before being
20
+ * claimed (default: HOLD_S). This is what builds a visible
21
+ * backlog: on the Operations chart, queued-but-unclaimed and
22
+ * claimed-and-worked are two different bands — with QUEUE_S=0
23
+ * everything is claimed the moment it lands and pending always
24
+ * equals active.
25
+ * SHOES_PCT — percent of orders that include shoes (default 50). A shoe
26
+ * order runs the side-quest sequence (ordering → inserting)
27
+ * alongside its manufacturing line — see below.
28
+ * ARRIVAL_S — average seconds between order arrivals (default: HOLD_S).
29
+ * Orders trickle in at this rate instead of landing as one
30
+ * wave, so the chart shows throughput, not a step function.
31
+ * POLL_MS — poll interval ms (default 1000)
32
+ * BASE_URL — server URL (default http://localhost:3000)
33
+ *
34
+ * All durations are per-item averages, not fixed delays: each escalation gets
35
+ * its own wait and hold spread across 0.4×–1.6× of the configured value (a
36
+ * stable hash of its id — no state to track between polls). That staggering is
37
+ * what makes the Operations chart read like a real floor: at any moment a
38
+ * station holds a mix of waiting and active items, claims burn the backlog
39
+ * down gradually, and resolves feed the next station a trickle instead of a
40
+ * wave.
41
+ *
42
+ * ── The shoe side-quest ──────────────────────────────────────────────────────
43
+ * A shoe order exercises the cross-sequence model (ordering → inserting feeds
44
+ * ship via upstream_roles — its own Operations sequence, not a bend in the
45
+ * main line). Timing tells the physical story, scaled to the manufacturing
46
+ * duration (MFG ≈ 8 stages × (QUEUE_S + HOLD_S)):
47
+ *
48
+ * arrival ordering escalation created with the order
49
+ * + ~½·QUEUE claimed quickly — the shoes are on order, work is underway
50
+ * + ~35% MFG shoes arrive → ordering resolves → inserting created
51
+ * + ~35% MFG inserting sits AVAILABLE (you cannot stuff inserts until the
52
+ * insole is made) — a long sky band on its station
53
+ * + HOLD claimed and resolved just ahead of shipping
54
+ *
55
+ * The script provisions the side-quest roles idempotently on start (ordering,
56
+ * inserting under it, ship fed by inserting).
19
57
  */
20
58
  declare const BASE_URL: string;
21
59
  declare const ORDERS: number;
22
60
  declare const HOLD_S: number;
61
+ declare const QUEUE_S: number;
62
+ declare const SHOES_PCT: number;
63
+ declare const ARRIVAL_S: number;
23
64
  declare const POLL_MS: number;
24
65
  declare const ORTHO_ROLES: string[];
66
+ declare const SIDE_ROLES: string[];
67
+ declare const ALL_ROLES: string[];
68
+ declare const MFG_S: number;
69
+ declare const TRANSIT_S: number;
70
+ declare const INSERT_QUEUE_S: number;
71
+ declare const ORDERING_QUEUE_S: number;
25
72
  declare let token: string;
26
73
  declare let userId: string;
27
74
  declare let totalClaimed: number;
@@ -29,9 +76,34 @@ declare let totalResolved: number;
29
76
  declare function ts(): string;
30
77
  declare function sleep(ms: number): Promise<unknown>;
31
78
  declare function ageSeconds(iso: string): number;
79
+ declare function jitter(id: string, salt: string): number;
80
+ /** Spread a base duration across 0.4×–1.6× so a stage's items trickle instead of toggling together. */
81
+ declare function spread(base: number, r: number): number;
82
+ /** Evenly distribute SHOES_PCT across the order sequence (deterministic). */
83
+ declare function hasShoes(i: number): boolean;
84
+ /** Queue wait before this escalation may be claimed. */
85
+ declare function claimWaitFor(esc: {
86
+ id: string;
87
+ role: string;
88
+ }): number;
89
+ /** Hold time after claim before this escalation may be resolved. */
90
+ declare function holdFor(esc: {
91
+ id: string;
92
+ role: string;
93
+ }): number;
32
94
  declare function api(method: string, path: string, body?: unknown): Promise<any>;
33
95
  declare function login(): Promise<void>;
96
+ declare function ensureSideQuestRoles(): Promise<void>;
97
+ declare function createSideEscalation(role: string, workflowId: string, orderId: string, runId: string, description: string): Promise<boolean>;
34
98
  declare function claimBatch(batchTag: string): Promise<number>;
35
- declare function resolveBatch(batchTag: string): Promise<number>;
36
- declare function runOrders(batchTag: string, target: number): Promise<void>;
99
+ declare function resolveBatch(batchTag: string, runId: string): Promise<number>;
100
+ interface PlannedOrder {
101
+ index: number;
102
+ wfId: string;
103
+ orderId: string;
104
+ shoes: boolean;
105
+ }
106
+ declare function runOrders(batchTag: string, runId: string, plan: PlannedOrder[], target: {
107
+ value: number;
108
+ }): Promise<string[]>;
37
109
  declare function main(): Promise<void>;