@hotmeshio/long-tail 0.31.0 → 0.32.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (326) hide show
  1. package/build/api/escalations/create.js +17 -6
  2. package/build/api/escalations/files.d.ts +18 -0
  3. package/build/api/escalations/files.js +73 -0
  4. package/build/api/escalations/index.d.ts +1 -0
  5. package/build/api/escalations/index.js +4 -2
  6. package/build/api/escalations/metadata.js +5 -4
  7. package/build/api/escalations/resolve.js +6 -4
  8. package/build/bin/ltc.js +4 -2
  9. package/build/index.d.ts +2 -0
  10. package/build/index.js +13 -2
  11. package/build/lib/cli/commands/escalations.d.ts +2 -0
  12. package/build/lib/cli/commands/escalations.js +12 -4
  13. package/build/lib/storage/s3.js +1 -0
  14. package/build/modules/config.d.ts +9 -0
  15. package/build/modules/config.js +9 -0
  16. package/build/routes/escalations/single.js +11 -0
  17. package/build/scripts/bambu-boat.d.ts +4 -2
  18. package/build/scripts/bambu-boat.js +4 -2
  19. package/build/scripts/bambu-print.d.ts +2 -1
  20. package/build/scripts/bambu-print.js +7 -3
  21. package/build/scripts/machine-line.js +2 -1
  22. package/build/sdk/index.d.ts +4 -0
  23. package/build/sdk/index.js +2 -0
  24. package/build/services/escalation/aggregate-resolved-sql.d.ts +19 -0
  25. package/build/services/escalation/aggregate-resolved-sql.js +57 -0
  26. package/build/services/escalation/aggregate-sql.d.ts +6 -1
  27. package/build/services/escalation/aggregate-sql.js +33 -12
  28. package/build/services/escalation/aggregate-validate.d.ts +5 -2
  29. package/build/services/escalation/aggregate-validate.js +7 -4
  30. package/build/services/escalation/aggregates.js +5 -3
  31. package/build/services/escalation/crud.js +3 -0
  32. package/build/services/escalation/file-links.d.ts +39 -0
  33. package/build/services/escalation/file-links.js +118 -0
  34. package/build/services/escalation/resolver-validation.d.ts +33 -0
  35. package/build/services/escalation/resolver-validation.js +22 -6
  36. package/build/services/orchestrator/condition.d.ts +20 -4
  37. package/build/services/orchestrator/condition.js +27 -8
  38. package/build/shared/form-validation/display-only-widgets.d.ts +3 -2
  39. package/build/shared/form-validation/display-only-widgets.js +3 -2
  40. package/build/shared/form-validation/index.d.ts +2 -0
  41. package/build/shared/form-validation/index.js +2 -0
  42. package/build/shared/form-validation/x-lt-confidence-facets.d.ts +27 -0
  43. package/build/shared/form-validation/x-lt-confidence-facets.js +60 -0
  44. package/build/shared/form-validation/x-lt-confidence.d.ts +57 -0
  45. package/build/shared/form-validation/x-lt-confidence.js +177 -0
  46. package/build/shared/form-validation/x-lt-help.d.ts +1 -1
  47. package/build/shared/form-validation/x-lt-help.js +1 -1
  48. package/build/system/mcp-servers/admin/escalations.js +4 -2
  49. package/build/system/mcp-servers/admin/labels-schemas.d.ts +4 -4
  50. package/build/system/mcp-servers/admin/schemas.d.ts +111 -70
  51. package/build/system/mcp-servers/admin/schemas.js +8 -3
  52. package/build/system/mcp-servers/human-queue-files.d.ts +8 -0
  53. package/build/system/mcp-servers/human-queue-files.js +32 -0
  54. package/build/system/mcp-servers/human-queue-schemas.d.ts +7 -0
  55. package/build/system/mcp-servers/human-queue-schemas.js +4 -1
  56. package/build/system/mcp-servers/human-queue.d.ts +1 -0
  57. package/build/system/mcp-servers/human-queue.js +23 -16
  58. package/build/system/seed/tool-manifests-admin.js +1 -1
  59. package/build/system/seed/tool-manifests-escalation.js +13 -0
  60. package/build/tsconfig.tsbuildinfo +1 -1
  61. package/build/types/analytics.d.ts +14 -3
  62. package/build/types/analytics.js +3 -1
  63. package/build/types/escalation.d.ts +34 -0
  64. package/build/types/escalation.js +72 -1
  65. package/build/types/index.d.ts +4 -4
  66. package/build/types/index.js +5 -1
  67. package/dashboard/dist/assets/{AdminDashboard-BafKaq50.js → AdminDashboard-C25co77C.js} +2 -2
  68. package/dashboard/dist/assets/{AdminDashboard-BafKaq50.js.map → AdminDashboard-C25co77C.js.map} +1 -1
  69. package/dashboard/dist/assets/{AgentConfigPage-CFHf31DT.js → AgentConfigPage-CsCpI15j.js} +5 -5
  70. package/dashboard/dist/assets/{AgentConfigPage-CFHf31DT.js.map → AgentConfigPage-CsCpI15j.js.map} +1 -1
  71. package/dashboard/dist/assets/{AgentDetailPage-CEvz5rJr.js → AgentDetailPage-ByaepsB5.js} +3 -3
  72. package/dashboard/dist/assets/{AgentDetailPage-CEvz5rJr.js.map → AgentDetailPage-ByaepsB5.js.map} +1 -1
  73. package/dashboard/dist/assets/{AgentsPage-DjQaio0O.js → AgentsPage-ByHU42SO.js} +2 -2
  74. package/dashboard/dist/assets/{AgentsPage-DjQaio0O.js.map → AgentsPage-ByHU42SO.js.map} +1 -1
  75. package/dashboard/dist/assets/{AvailableEscalationsPage-UB03VTJx.js → AvailableEscalationsPage--o6qcz7-.js} +2 -2
  76. package/dashboard/dist/assets/{AvailableEscalationsPage-UB03VTJx.js.map → AvailableEscalationsPage--o6qcz7-.js.map} +1 -1
  77. package/dashboard/dist/assets/{BotPicker-C7MNWOF7.js → BotPicker-B3y3H3fs.js} +2 -2
  78. package/dashboard/dist/assets/{BotPicker-C7MNWOF7.js.map → BotPicker-B3y3H3fs.js.map} +1 -1
  79. package/dashboard/dist/assets/{BulkAssignModal-h5fX-_cV.js → BulkAssignModal-DMNIkIn6.js} +2 -2
  80. package/dashboard/dist/assets/{BulkAssignModal-h5fX-_cV.js.map → BulkAssignModal-DMNIkIn6.js.map} +1 -1
  81. package/dashboard/dist/assets/{CapabilitiesPage-BIzDl-ZT.js → CapabilitiesPage-BekfKHlk.js} +2 -2
  82. package/dashboard/dist/assets/{CapabilitiesPage-BIzDl-ZT.js.map → CapabilitiesPage-BekfKHlk.js.map} +1 -1
  83. package/dashboard/dist/assets/{CollapsibleSection-B5TivtEg.js → CollapsibleSection-BAteHgBG.js} +2 -2
  84. package/dashboard/dist/assets/{CollapsibleSection-B5TivtEg.js.map → CollapsibleSection-BAteHgBG.js.map} +1 -1
  85. package/dashboard/dist/assets/{ConfirmDeleteModal-CB4nKePb.js → ConfirmDeleteModal-BNY287W9.js} +2 -2
  86. package/dashboard/dist/assets/{ConfirmDeleteModal-CB4nKePb.js.map → ConfirmDeleteModal-BNY287W9.js.map} +1 -1
  87. package/dashboard/dist/assets/{ConnectedAppsPage-B3S5gVWw.js → ConnectedAppsPage-LLiiH3da.js} +2 -2
  88. package/dashboard/dist/assets/{ConnectedAppsPage-B3S5gVWw.js.map → ConnectedAppsPage-LLiiH3da.js.map} +1 -1
  89. package/dashboard/dist/assets/{CountdownTimer-BHVIq3kA.js → CountdownTimer-DBLURjxh.js} +2 -2
  90. package/dashboard/dist/assets/{CountdownTimer-BHVIq3kA.js.map → CountdownTimer-DBLURjxh.js.map} +1 -1
  91. package/dashboard/dist/assets/{CredentialConfirmModal-w0AGmCIe.js → CredentialConfirmModal-BqpY3yen.js} +2 -2
  92. package/dashboard/dist/assets/{CredentialConfirmModal-w0AGmCIe.js.map → CredentialConfirmModal-BqpY3yen.js.map} +1 -1
  93. package/dashboard/dist/assets/{CredentialsPage-BtCGBJrT.js → CredentialsPage-CW3_dbm2.js} +2 -2
  94. package/dashboard/dist/assets/{CredentialsPage-BtCGBJrT.js.map → CredentialsPage-CW3_dbm2.js.map} +1 -1
  95. package/dashboard/dist/assets/{CronLabel-BG1rZK-5.js → CronLabel-CR6DiZZr.js} +2 -2
  96. package/dashboard/dist/assets/{CronLabel-BG1rZK-5.js.map → CronLabel-CR6DiZZr.js.map} +1 -1
  97. package/dashboard/dist/assets/{DataTable-FjFhx0SE.js → DataTable-C-IlbLhY.js} +2 -2
  98. package/dashboard/dist/assets/{DataTable-FjFhx0SE.js.map → DataTable-C-IlbLhY.js.map} +1 -1
  99. package/dashboard/dist/assets/{DropZone-D8ZVdEb5.js → DropZone-CHmz59w4.js} +2 -2
  100. package/dashboard/dist/assets/{DropZone-D8ZVdEb5.js.map → DropZone-CHmz59w4.js.map} +1 -1
  101. package/dashboard/dist/assets/{ElapsedCell-l4m8Yvfv.js → ElapsedCell-CEapQvgU.js} +2 -2
  102. package/dashboard/dist/assets/{ElapsedCell-l4m8Yvfv.js.map → ElapsedCell-CEapQvgU.js.map} +1 -1
  103. package/dashboard/dist/assets/{EntityTimelinePanel-BOHlIaxF.js → EntityTimelinePanel-DqD_BpJM.js} +2 -2
  104. package/dashboard/dist/assets/{EntityTimelinePanel-BOHlIaxF.js.map → EntityTimelinePanel-DqD_BpJM.js.map} +1 -1
  105. package/dashboard/dist/assets/{EscalationListSchemaPage-BotlwlX3.js → EscalationListSchemaPage-DDKcrVbb.js} +3 -3
  106. package/dashboard/dist/assets/{EscalationListSchemaPage-BotlwlX3.js.map → EscalationListSchemaPage-DDKcrVbb.js.map} +1 -1
  107. package/dashboard/dist/assets/{EscalationSchemaPage-CfZSVOSD.js → EscalationSchemaPage-BZHZfJiQ.js} +3 -3
  108. package/dashboard/dist/assets/{EscalationSchemaPage-CfZSVOSD.js.map → EscalationSchemaPage-BZHZfJiQ.js.map} +1 -1
  109. package/dashboard/dist/assets/{EscalationTimeline-CCKpuRBU.js → EscalationTimeline-Dv0XUgQX.js} +2 -2
  110. package/dashboard/dist/assets/{EscalationTimeline-CCKpuRBU.js.map → EscalationTimeline-Dv0XUgQX.js.map} +1 -1
  111. package/dashboard/dist/assets/{EscalationTitleSelect-cpTzPHiW.js → EscalationTitleSelect-BN7D52wT.js} +2 -2
  112. package/dashboard/dist/assets/{EscalationTitleSelect-cpTzPHiW.js.map → EscalationTitleSelect-BN7D52wT.js.map} +1 -1
  113. package/dashboard/dist/assets/{EscalationsOverview-DS-nK0qV.js → EscalationsOverview-DE6EPkhT.js} +2 -2
  114. package/dashboard/dist/assets/{EscalationsOverview-DS-nK0qV.js.map → EscalationsOverview-DE6EPkhT.js.map} +1 -1
  115. package/dashboard/dist/assets/{EventTable-UfebhJf0.js → EventTable-CiV62txY.js} +2 -2
  116. package/dashboard/dist/assets/{EventTable-UfebhJf0.js.map → EventTable-CiV62txY.js.map} +1 -1
  117. package/dashboard/dist/assets/{FilterBar-newA5l-X.js → FilterBar-WyNQPyQ1.js} +2 -2
  118. package/dashboard/dist/assets/{FilterBar-newA5l-X.js.map → FilterBar-WyNQPyQ1.js.map} +1 -1
  119. package/dashboard/dist/assets/{GraphInvokePage-6SCDyQcK.js → GraphInvokePage--gn48yUD.js} +2 -2
  120. package/dashboard/dist/assets/{GraphInvokePage-6SCDyQcK.js.map → GraphInvokePage--gn48yUD.js.map} +1 -1
  121. package/dashboard/dist/assets/{HomePage-BEc-XdKj.js → HomePage-IR_jgBE8.js} +2 -2
  122. package/dashboard/dist/assets/{HomePage-BEc-XdKj.js.map → HomePage-IR_jgBE8.js.map} +1 -1
  123. package/dashboard/dist/assets/{LabelJobsPage-BO50zXT5.js → LabelJobsPage-C8LSts4O.js} +2 -2
  124. package/dashboard/dist/assets/{LabelJobsPage-BO50zXT5.js.map → LabelJobsPage-C8LSts4O.js.map} +1 -1
  125. package/dashboard/dist/assets/{LinePage-NYxJQ-ex.js → LinePage-CS7uUv00.js} +2 -2
  126. package/dashboard/dist/assets/{LinePage-NYxJQ-ex.js.map → LinePage-CS7uUv00.js.map} +1 -1
  127. package/dashboard/dist/assets/{LinesPage-RbWTaQRh.js → LinesPage-CJprjG39.js} +3 -3
  128. package/dashboard/dist/assets/{LinesPage-RbWTaQRh.js.map → LinesPage-CJprjG39.js.map} +1 -1
  129. package/dashboard/dist/assets/{ListToolbar-B-E9-toh.js → ListToolbar-YP3uwvXC.js} +2 -2
  130. package/dashboard/dist/assets/{ListToolbar-B-E9-toh.js.map → ListToolbar-YP3uwvXC.js.map} +1 -1
  131. package/dashboard/dist/assets/{McpOverview-Shcf9QhZ.js → McpOverview-Ce-m2JON.js} +2 -2
  132. package/dashboard/dist/assets/{McpOverview-Shcf9QhZ.js.map → McpOverview-Ce-m2JON.js.map} +1 -1
  133. package/dashboard/dist/assets/{McpQueryDetailPage-D8kmjy2v.js → McpQueryDetailPage-CtnUqO1v.js} +2 -2
  134. package/dashboard/dist/assets/{McpQueryDetailPage-D8kmjy2v.js.map → McpQueryDetailPage-CtnUqO1v.js.map} +1 -1
  135. package/dashboard/dist/assets/{McpQueryPage-ck-o5m8A.js → McpQueryPage-C-lqUPlg.js} +2 -2
  136. package/dashboard/dist/assets/{McpQueryPage-ck-o5m8A.js.map → McpQueryPage-C-lqUPlg.js.map} +1 -1
  137. package/dashboard/dist/assets/{McpRunDetailPage-BJKkM9-3.js → McpRunDetailPage-MnIe0hwz.js} +2 -2
  138. package/dashboard/dist/assets/{McpRunDetailPage-BJKkM9-3.js.map → McpRunDetailPage-MnIe0hwz.js.map} +1 -1
  139. package/dashboard/dist/assets/{McpRunsPage-DqhHfD5G.js → McpRunsPage-CpYLXVPF.js} +2 -2
  140. package/dashboard/dist/assets/{McpRunsPage-DqhHfD5G.js.map → McpRunsPage-CpYLXVPF.js.map} +1 -1
  141. package/dashboard/dist/assets/{NamespacePill--uIaGtrN.js → NamespacePill-DilSgLyE.js} +2 -2
  142. package/dashboard/dist/assets/{NamespacePill--uIaGtrN.js.map → NamespacePill-DilSgLyE.js.map} +1 -1
  143. package/dashboard/dist/assets/{OperationsPage-D6B9Vv__.js → OperationsPage-xe6bAwaq.js} +2 -2
  144. package/dashboard/dist/assets/{OperationsPage-D6B9Vv__.js.map → OperationsPage-xe6bAwaq.js.map} +1 -1
  145. package/dashboard/dist/assets/{OperatorDashboard-CIH1cxjQ.js → OperatorDashboard-Cm9_C0EP.js} +2 -2
  146. package/dashboard/dist/assets/{OperatorDashboard-CIH1cxjQ.js.map → OperatorDashboard-Cm9_C0EP.js.map} +1 -1
  147. package/dashboard/dist/assets/{PageHeader-DClJ33fV.js → PageHeader-B7SOvLT8.js} +2 -2
  148. package/dashboard/dist/assets/{PageHeader-DClJ33fV.js.map → PageHeader-B7SOvLT8.js.map} +1 -1
  149. package/dashboard/dist/assets/{PageHeaderWithStats-C-TrbKQL.js → PageHeaderWithStats-DWKap7gs.js} +2 -2
  150. package/dashboard/dist/assets/{PageHeaderWithStats-C-TrbKQL.js.map → PageHeaderWithStats-DWKap7gs.js.map} +1 -1
  151. package/dashboard/dist/assets/{PersonaDetailPage-DjvfyIph.js → PersonaDetailPage-CvstNkh3.js} +2 -2
  152. package/dashboard/dist/assets/{PersonaDetailPage-DjvfyIph.js.map → PersonaDetailPage-CvstNkh3.js.map} +1 -1
  153. package/dashboard/dist/assets/{PersonasPage-D7ryA8Vc.js → PersonasPage-ba1KeInW.js} +2 -2
  154. package/dashboard/dist/assets/{PersonasPage-D7ryA8Vc.js.map → PersonasPage-ba1KeInW.js.map} +1 -1
  155. package/dashboard/dist/assets/{PortalPage-D3ybr0mo.js → PortalPage-GDgG88TM.js} +2 -2
  156. package/dashboard/dist/assets/{PortalPage-D3ybr0mo.js.map → PortalPage-GDgG88TM.js.map} +1 -1
  157. package/dashboard/dist/assets/{PrintLabelsPanel-D0HF5x8b.js → PrintLabelsPanel-BkUN00Qd.js} +2 -2
  158. package/dashboard/dist/assets/{PrintLabelsPanel-D0HF5x8b.js.map → PrintLabelsPanel-BkUN00Qd.js.map} +1 -1
  159. package/dashboard/dist/assets/{ProcessDetailPage-0xS9EdHe.js → ProcessDetailPage-BKu9_wWv.js} +2 -2
  160. package/dashboard/dist/assets/{ProcessDetailPage-0xS9EdHe.js.map → ProcessDetailPage-BKu9_wWv.js.map} +1 -1
  161. package/dashboard/dist/assets/{ProcessesListPage-93SMj1Fd.js → ProcessesListPage-BfJYTU_v.js} +2 -2
  162. package/dashboard/dist/assets/{ProcessesListPage-93SMj1Fd.js.map → ProcessesListPage-BfJYTU_v.js.map} +1 -1
  163. package/dashboard/dist/assets/ResolverForm-DZmY6wJV.js +8 -0
  164. package/dashboard/dist/assets/ResolverForm-DZmY6wJV.js.map +1 -0
  165. package/dashboard/dist/assets/{RoleDetailPage-DlUOJWSB.js → RoleDetailPage-vGeth3ve.js} +3 -3
  166. package/dashboard/dist/assets/{RoleDetailPage-DlUOJWSB.js.map → RoleDetailPage-vGeth3ve.js.map} +1 -1
  167. package/dashboard/dist/assets/{RolePill-BjD1Qw5y.js → RolePill-KhT-Yehm.js} +2 -2
  168. package/dashboard/dist/assets/{RolePill-BjD1Qw5y.js.map → RolePill-KhT-Yehm.js.map} +1 -1
  169. package/dashboard/dist/assets/{RolesPage-DqhHSvaw.js → RolesPage-CwxHOkqF.js} +2 -2
  170. package/dashboard/dist/assets/{RolesPage-DqhHSvaw.js.map → RolesPage-CwxHOkqF.js.map} +1 -1
  171. package/dashboard/dist/assets/{RosterWidget-BrIq61HR.js → RosterWidget-DRVj9xyk.js} +2 -2
  172. package/dashboard/dist/assets/{RosterWidget-BrIq61HR.js.map → RosterWidget-DRVj9xyk.js.map} +1 -1
  173. package/dashboard/dist/assets/{RunAsSelector-D6zQrCNr.js → RunAsSelector-DvPX3XmA.js} +2 -2
  174. package/dashboard/dist/assets/{RunAsSelector-D6zQrCNr.js.map → RunAsSelector-DvPX3XmA.js.map} +1 -1
  175. package/dashboard/dist/assets/{ScanCodesPage-B2S9DR2f.js → ScanCodesPage-Cptbe7h8.js} +2 -2
  176. package/dashboard/dist/assets/{ScanCodesPage-B2S9DR2f.js.map → ScanCodesPage-Cptbe7h8.js.map} +1 -1
  177. package/dashboard/dist/assets/{ScanSchemeDetailPage-gpxKN-pL.js → ScanSchemeDetailPage-DquNJ7Or.js} +4 -4
  178. package/dashboard/dist/assets/{ScanSchemeDetailPage-gpxKN-pL.js.map → ScanSchemeDetailPage-DquNJ7Or.js.map} +1 -1
  179. package/dashboard/dist/assets/{ScanStationPage-B-UTgXsG.js → ScanStationPage-DaRfGGa8.js} +2 -2
  180. package/dashboard/dist/assets/{ScanStationPage-B-UTgXsG.js.map → ScanStationPage-DaRfGGa8.js.map} +1 -1
  181. package/dashboard/dist/assets/{SchemeMatchFields-BK24aa7K.js → SchemeMatchFields-w-pffi98.js} +2 -2
  182. package/dashboard/dist/assets/{SchemeMatchFields-BK24aa7K.js.map → SchemeMatchFields-w-pffi98.js.map} +1 -1
  183. package/dashboard/dist/assets/{ScopeBadge-ofLQ6HW-.js → ScopeBadge-HeuiMJhW.js} +2 -2
  184. package/dashboard/dist/assets/{ScopeBadge-ofLQ6HW-.js.map → ScopeBadge-HeuiMJhW.js.map} +1 -1
  185. package/dashboard/dist/assets/{StickyPagination-CgVv7vc_.js → StickyPagination-D6fDHCcJ.js} +2 -2
  186. package/dashboard/dist/assets/{StickyPagination-CgVv7vc_.js.map → StickyPagination-D6fDHCcJ.js.map} +1 -1
  187. package/dashboard/dist/assets/{StreamMessageDetail-B9sxDVw4.js → StreamMessageDetail-BmYVQrzl.js} +2 -2
  188. package/dashboard/dist/assets/{StreamMessageDetail-B9sxDVw4.js.map → StreamMessageDetail-BmYVQrzl.js.map} +1 -1
  189. package/dashboard/dist/assets/{SwimlaneTimeline-DQc0CYSL.js → SwimlaneTimeline-KCqA-PeL.js} +2 -2
  190. package/dashboard/dist/assets/{SwimlaneTimeline-DQc0CYSL.js.map → SwimlaneTimeline-KCqA-PeL.js.map} +1 -1
  191. package/dashboard/dist/assets/{TagInput-47qVYzke.js → TagInput-CZBAu1Hy.js} +2 -2
  192. package/dashboard/dist/assets/{TagInput-47qVYzke.js.map → TagInput-CZBAu1Hy.js.map} +1 -1
  193. package/dashboard/dist/assets/{TaskDetailPage-D6VOsA1f.js → TaskDetailPage-WtVv2z7T.js} +2 -2
  194. package/dashboard/dist/assets/{TaskDetailPage-D6VOsA1f.js.map → TaskDetailPage-WtVv2z7T.js.map} +1 -1
  195. package/dashboard/dist/assets/{TaskQueuePill-C5KmdXAl.js → TaskQueuePill-DWGn3CWx.js} +2 -2
  196. package/dashboard/dist/assets/{TaskQueuePill-C5KmdXAl.js.map → TaskQueuePill-DWGn3CWx.js.map} +1 -1
  197. package/dashboard/dist/assets/{TasksListPage-DG8hvO8G.js → TasksListPage-DMYQNDIw.js} +2 -2
  198. package/dashboard/dist/assets/{TasksListPage-DG8hvO8G.js.map → TasksListPage-DMYQNDIw.js.map} +1 -1
  199. package/dashboard/dist/assets/{TimeAgo-CL02EhFi.js → TimeAgo-BkY4lahi.js} +2 -2
  200. package/dashboard/dist/assets/{TimeAgo-CL02EhFi.js.map → TimeAgo-BkY4lahi.js.map} +1 -1
  201. package/dashboard/dist/assets/{TimestampCell-BE7oKVJg.js → TimestampCell-BBPb9TI2.js} +2 -2
  202. package/dashboard/dist/assets/{TimestampCell-BE7oKVJg.js.map → TimestampCell-BBPb9TI2.js.map} +1 -1
  203. package/dashboard/dist/assets/{ToolPill-Cp9Drk1s.js → ToolPill-B2aRk0o1.js} +2 -2
  204. package/dashboard/dist/assets/{ToolPill-Cp9Drk1s.js.map → ToolPill-B2aRk0o1.js.map} +1 -1
  205. package/dashboard/dist/assets/{ToolTestPanel-CRecLh6S.js → ToolTestPanel-CNMbmymK.js} +2 -2
  206. package/dashboard/dist/assets/{ToolTestPanel-CRecLh6S.js.map → ToolTestPanel-CNMbmymK.js.map} +1 -1
  207. package/dashboard/dist/assets/{TopicDetailPage-QzkinTfN.js → TopicDetailPage-CodBRylU.js} +2 -2
  208. package/dashboard/dist/assets/{TopicDetailPage-QzkinTfN.js.map → TopicDetailPage-CodBRylU.js.map} +1 -1
  209. package/dashboard/dist/assets/TopicsPage-BtD8jTU7.js +2 -0
  210. package/dashboard/dist/assets/{TopicsPage-D8dW6J65.js.map → TopicsPage-BtD8jTU7.js.map} +1 -1
  211. package/dashboard/dist/assets/{UserName-Dw3R_zrl.js → UserName-D6y9AnuY.js} +2 -2
  212. package/dashboard/dist/assets/{UserName-Dw3R_zrl.js.map → UserName-D6y9AnuY.js.map} +1 -1
  213. package/dashboard/dist/assets/{WorkflowExecutionPage-BTocv8or.js → WorkflowExecutionPage-BeffqV13.js} +2 -2
  214. package/dashboard/dist/assets/{WorkflowExecutionPage-BTocv8or.js.map → WorkflowExecutionPage-BeffqV13.js.map} +1 -1
  215. package/dashboard/dist/assets/{WorkflowPill-BiLYq__w.js → WorkflowPill-BNm3R4pP.js} +2 -2
  216. package/dashboard/dist/assets/{WorkflowPill-BiLYq__w.js.map → WorkflowPill-BNm3R4pP.js.map} +1 -1
  217. package/dashboard/dist/assets/{WorkflowsDashboard-CIPEt3et.js → WorkflowsDashboard-m4Co5diK.js} +2 -2
  218. package/dashboard/dist/assets/{WorkflowsDashboard-CIPEt3et.js.map → WorkflowsDashboard-m4Co5diK.js.map} +1 -1
  219. package/dashboard/dist/assets/{WorkflowsOverview-Bj4MJIE-.js → WorkflowsOverview-DNoI9qY8.js} +2 -2
  220. package/dashboard/dist/assets/{WorkflowsOverview-Bj4MJIE-.js.map → WorkflowsOverview-DNoI9qY8.js.map} +1 -1
  221. package/dashboard/dist/assets/{YamlWorkflowDetailPage-DMEk5k9-.js → YamlWorkflowDetailPage-8Vpc4Oxy.js} +2 -2
  222. package/dashboard/dist/assets/{YamlWorkflowDetailPage-DMEk5k9-.js.map → YamlWorkflowDetailPage-8Vpc4Oxy.js.map} +1 -1
  223. package/dashboard/dist/assets/{YamlWorkflowsPage-F2yyyoiB.js → YamlWorkflowsPage-D1JNwDSg.js} +2 -2
  224. package/dashboard/dist/assets/{YamlWorkflowsPage-F2yyyoiB.js.map → YamlWorkflowsPage-D1JNwDSg.js.map} +1 -1
  225. package/dashboard/dist/assets/{agents-mSgxEPpI.js → agents-LTHUsfv3.js} +2 -2
  226. package/dashboard/dist/assets/{agents-mSgxEPpI.js.map → agents-LTHUsfv3.js.map} +1 -1
  227. package/dashboard/dist/assets/{bots-p4Hp0CV6.js → bots-CQxehfBo.js} +2 -2
  228. package/dashboard/dist/assets/{bots-p4Hp0CV6.js.map → bots-CQxehfBo.js.map} +1 -1
  229. package/dashboard/dist/assets/{capabilities-D_-I_SXR.js → capabilities-DECTnJrK.js} +2 -2
  230. package/dashboard/dist/assets/{capabilities-D_-I_SXR.js.map → capabilities-DECTnJrK.js.map} +1 -1
  231. package/dashboard/dist/assets/{controlplane-DjdoQnRU.js → controlplane-Dx20db5g.js} +2 -2
  232. package/dashboard/dist/assets/{controlplane-DjdoQnRU.js.map → controlplane-Dx20db5g.js.map} +1 -1
  233. package/dashboard/dist/assets/{escalation-columns-R_IEVQjQ.js → escalation-columns-CVFGUB79.js} +2 -2
  234. package/dashboard/dist/assets/{escalation-columns-R_IEVQjQ.js.map → escalation-columns-CVFGUB79.js.map} +1 -1
  235. package/dashboard/dist/assets/escalation-items-Bj7bkwWy.js +2 -0
  236. package/dashboard/dist/assets/escalation-items-Bj7bkwWy.js.map +1 -0
  237. package/dashboard/dist/assets/index-BD4O-ISx.js +13 -0
  238. package/dashboard/dist/assets/{index-Dk2KKo0z.js.map → index-BD4O-ISx.js.map} +1 -1
  239. package/dashboard/dist/assets/{index-D9RAN9sA.js → index-BL8XOMHT.js} +2 -2
  240. package/dashboard/dist/assets/{index-D9RAN9sA.js.map → index-BL8XOMHT.js.map} +1 -1
  241. package/dashboard/dist/assets/index-C2vRDf96.js +2 -0
  242. package/dashboard/dist/assets/{index-C5osluV3.js.map → index-C2vRDf96.js.map} +1 -1
  243. package/dashboard/dist/assets/{index-DUYAGxwc.js → index-CNgyKRQ8.js} +2 -2
  244. package/dashboard/dist/assets/{index-DUYAGxwc.js.map → index-CNgyKRQ8.js.map} +1 -1
  245. package/dashboard/dist/assets/{index-DlJkZ12b.js → index-CdXPbBgs.js} +2 -2
  246. package/dashboard/dist/assets/{index-DlJkZ12b.js.map → index-CdXPbBgs.js.map} +1 -1
  247. package/dashboard/dist/assets/{index-BuCEhhnl.js → index-CnBUFCV7.js} +2 -2
  248. package/dashboard/dist/assets/{index-BuCEhhnl.js.map → index-CnBUFCV7.js.map} +1 -1
  249. package/dashboard/dist/assets/index-CoLciFzq.css +1 -0
  250. package/dashboard/dist/assets/{index-D-FYj9jT.js → index-D_TSx-k_.js} +2 -2
  251. package/dashboard/dist/assets/{index-D-FYj9jT.js.map → index-D_TSx-k_.js.map} +1 -1
  252. package/dashboard/dist/assets/{index-C_dAJUzh.js → index-Do9Y6Fgt.js} +16 -16
  253. package/dashboard/dist/assets/index-Do9Y6Fgt.js.map +1 -0
  254. package/dashboard/dist/assets/{index-D6K_IgCz.js → index-Dq1zjilj.js} +2 -2
  255. package/dashboard/dist/assets/{index-D6K_IgCz.js.map → index-Dq1zjilj.js.map} +1 -1
  256. package/dashboard/dist/assets/{index-BEMdD58x.js → index-Dz4LSzA2.js} +2 -2
  257. package/dashboard/dist/assets/{index-BEMdD58x.js.map → index-Dz4LSzA2.js.map} +1 -1
  258. package/dashboard/dist/assets/{index-D7L_VoBD.js → index-g1MmARVR.js} +2 -2
  259. package/dashboard/dist/assets/{index-D7L_VoBD.js.map → index-g1MmARVR.js.map} +1 -1
  260. package/dashboard/dist/assets/{index-BxbQmeqh.js → index-qEd4FVmN.js} +2 -2
  261. package/dashboard/dist/assets/{index-BxbQmeqh.js.map → index-qEd4FVmN.js.map} +1 -1
  262. package/dashboard/dist/assets/index-v4rQ8ctJ.js +3 -0
  263. package/dashboard/dist/assets/index-v4rQ8ctJ.js.map +1 -0
  264. package/dashboard/dist/assets/{knowledge-DSsPTqkK.js → knowledge-CT1I1t1S.js} +2 -2
  265. package/dashboard/dist/assets/{knowledge-DSsPTqkK.js.map → knowledge-CT1I1t1S.js.map} +1 -1
  266. package/dashboard/dist/assets/{machines-DSgGtqqr.js → machines-Dsuw-l1y.js} +2 -2
  267. package/dashboard/dist/assets/{machines-DSgGtqqr.js.map → machines-Dsuw-l1y.js.map} +1 -1
  268. package/dashboard/dist/assets/{mcp-Cg-_uLw8.js → mcp-C3o5wzO8.js} +2 -2
  269. package/dashboard/dist/assets/{mcp-Cg-_uLw8.js.map → mcp-C3o5wzO8.js.map} +1 -1
  270. package/dashboard/dist/assets/{mcp-query-DQombEK_.js → mcp-query-IvkIG7gH.js} +2 -2
  271. package/dashboard/dist/assets/{mcp-query-DQombEK_.js.map → mcp-query-IvkIG7gH.js.map} +1 -1
  272. package/dashboard/dist/assets/pdf-CcZYcL52.js +13 -0
  273. package/dashboard/dist/assets/pdf-CcZYcL52.js.map +1 -0
  274. package/dashboard/dist/assets/pdf.worker.min-DgRcL-GR.js +2 -0
  275. package/dashboard/dist/assets/pdf.worker.min-DgRcL-GR.js.map +1 -0
  276. package/dashboard/dist/assets/pdf.worker.min-yatZIOMy.mjs +21 -0
  277. package/dashboard/dist/assets/{personas-DYrcoVBu.js → personas-zWlWjj6j.js} +2 -2
  278. package/dashboard/dist/assets/{personas-DYrcoVBu.js.map → personas-zWlWjj6j.js.map} +1 -1
  279. package/dashboard/dist/assets/{pipelines-wdb4xsey.js → pipelines-BKNvBMdf.js} +2 -2
  280. package/dashboard/dist/assets/{pipelines-wdb4xsey.js.map → pipelines-BKNvBMdf.js.map} +1 -1
  281. package/dashboard/dist/assets/{tasks-Bil7gvm9.js → tasks-BQSO3Sjv.js} +2 -2
  282. package/dashboard/dist/assets/{tasks-Bil7gvm9.js.map → tasks-BQSO3Sjv.js.map} +1 -1
  283. package/dashboard/dist/assets/{topics-wBqaVhRR.js → topics-Dr_1TtUM.js} +2 -2
  284. package/dashboard/dist/assets/{topics-wBqaVhRR.js.map → topics-Dr_1TtUM.js.map} +1 -1
  285. package/dashboard/dist/assets/{useContainerWidth-DSLp1gEm.js → useContainerWidth-D38m8eub.js} +2 -2
  286. package/dashboard/dist/assets/{useContainerWidth-DSLp1gEm.js.map → useContainerWidth-D38m8eub.js.map} +1 -1
  287. package/dashboard/dist/assets/{useDebouncedValue-CwrY8TpV.js → useDebouncedValue-C5ESASfo.js} +2 -2
  288. package/dashboard/dist/assets/{useDebouncedValue-CwrY8TpV.js.map → useDebouncedValue-C5ESASfo.js.map} +1 -1
  289. package/dashboard/dist/assets/{useNamespace-Sod_xApz.js → useNamespace-DLLOJRYY.js} +2 -2
  290. package/dashboard/dist/assets/{useNamespace-Sod_xApz.js.map → useNamespace-DLLOJRYY.js.map} +1 -1
  291. package/dashboard/dist/assets/{useYamlActivityEvents-CKLInV3C.js → useYamlActivityEvents-D0XNXli1.js} +2 -2
  292. package/dashboard/dist/assets/{useYamlActivityEvents-CKLInV3C.js.map → useYamlActivityEvents-D0XNXli1.js.map} +1 -1
  293. package/dashboard/dist/assets/{validate-CYhW1xMJ.js → validate-Be4GWnJ1.js} +2 -2
  294. package/dashboard/dist/assets/{validate-CYhW1xMJ.js.map → validate-Be4GWnJ1.js.map} +1 -1
  295. package/dashboard/dist/assets/{vendor-icons-ed7mYQw4.js → vendor-icons-D48ijXZU.js} +209 -189
  296. package/dashboard/dist/assets/vendor-icons-D48ijXZU.js.map +1 -0
  297. package/dashboard/dist/assets/{yaml-workflows-C20qHo3L.js → yaml-workflows-BQpO-G-I.js} +2 -2
  298. package/dashboard/dist/assets/{yaml-workflows-C20qHo3L.js.map → yaml-workflows-BQpO-G-I.js.map} +1 -1
  299. package/dashboard/dist/index.html +3 -3
  300. package/docs/api/http/escalations.md +54 -7
  301. package/docs/api/mcp/admin.md +7 -5
  302. package/docs/api/mcp/human-queue.md +17 -1
  303. package/docs/api/sdk/escalations.md +30 -4
  304. package/docs/api/sdk/files.md +19 -0
  305. package/docs/cli.md +4 -1
  306. package/docs/escalation-analytics.md +17 -0
  307. package/docs/hitl/files.md +104 -0
  308. package/docs/hitl/x-lt-confidence.md +139 -0
  309. package/docs/hitl/x-lt-layout.md +19 -0
  310. package/docs/hitl/x-lt-widget.md +20 -0
  311. package/docs/hitl-guide.md +6 -0
  312. package/docs/machines.md +2 -2
  313. package/docs/mcp.md +2 -1
  314. package/package.json +1 -1
  315. package/dashboard/dist/assets/ResolverForm-DfogHDzl.js +0 -8
  316. package/dashboard/dist/assets/ResolverForm-DfogHDzl.js.map +0 -1
  317. package/dashboard/dist/assets/TopicsPage-D8dW6J65.js +0 -2
  318. package/dashboard/dist/assets/escalation-items-BGZlJMpk.js +0 -2
  319. package/dashboard/dist/assets/escalation-items-BGZlJMpk.js.map +0 -1
  320. package/dashboard/dist/assets/index-BUmz0NMg.css +0 -1
  321. package/dashboard/dist/assets/index-C5osluV3.js +0 -2
  322. package/dashboard/dist/assets/index-C_dAJUzh.js.map +0 -1
  323. package/dashboard/dist/assets/index-Dk2KKo0z.js +0 -13
  324. package/dashboard/dist/assets/index-FiNThtNU.js +0 -3
  325. package/dashboard/dist/assets/index-FiNThtNU.js.map +0 -1
  326. package/dashboard/dist/assets/vendor-icons-ed7mYQw4.js.map +0 -1
@@ -0,0 +1,57 @@
1
+ "use strict";
2
+ // ---------------------------------------------------------------------------
3
+ // The resolved measure: rows resolved within a half-open window, counted per
4
+ // group and optionally per UTC calendar bucket. `status = 'resolved'` inlines
5
+ // as a literal and the role scope is required, so the planner reads the
6
+ // role-led idx_hmsh_esc_resolved_cover range (role, resolved_at) instead of
7
+ // sweeping every queue's history. Cost grows with the rows resolved in the
8
+ // window for the scoped roles.
9
+ // ---------------------------------------------------------------------------
10
+ Object.defineProperty(exports, "__esModule", { value: true });
11
+ exports.resolveBucket = resolveBucket;
12
+ exports.buildResolvedAggregate = buildResolvedAggregate;
13
+ const config_1 = require("../../modules/config");
14
+ const aggregate_validate_1 = require("./aggregate-validate");
15
+ const types_1 = require("../../types");
16
+ /** The bucket, validated against the closed set; it inlines into date_trunc. */
17
+ function resolveBucket(bucket) {
18
+ if (bucket === undefined)
19
+ return null;
20
+ if (!types_1.RESOLVED_BUCKETS.includes(bucket)) {
21
+ throw new aggregate_validate_1.AnalyticsInputError(`measure.bucket "${String(bucket)}" is not one of ${types_1.RESOLVED_BUCKETS.join(', ')}`);
22
+ }
23
+ return bucket;
24
+ }
25
+ function buildResolvedAggregate(input) {
26
+ const win = (0, aggregate_validate_1.resolveWindow)(input.window, 'measure.window', {
27
+ days: config_1.config.LT_ANALYTICS_MAX_RESOLVED_WINDOW_DAYS,
28
+ envVar: 'LT_ANALYTICS_MAX_RESOLVED_WINDOW_DAYS',
29
+ });
30
+ if (!win)
31
+ throw new aggregate_validate_1.AnalyticsInputError('measure.window is required for resolved');
32
+ const { params } = input;
33
+ params.push(win.from.toISOString());
34
+ const fromP = `$${params.length}::timestamptz`;
35
+ params.push(win.to.toISOString());
36
+ const toP = `$${params.length}::timestamptz`;
37
+ const select = [...input.groupSelect];
38
+ const keys = [...input.groupKeys];
39
+ if (input.bucket) {
40
+ select.unshift(`(date_trunc('${input.bucket}', resolved_at AT TIME ZONE 'UTC') AT TIME ZONE 'UTC') AS bucket`);
41
+ keys.unshift('bucket');
42
+ }
43
+ const countExpr = input.distinctBy
44
+ ? `count(DISTINCT (metadata->>'${input.distinctBy}'))::int`
45
+ : 'count(*)::int';
46
+ const groupBySql = keys.length ? `GROUP BY ${keys.join(', ')}\n` : '';
47
+ params.push(input.pageLimit + 1, input.offset);
48
+ const sql = `SELECT ${[...select, `${countExpr} AS "count"`, 'count(*)::int AS sample_count'].join(',\n ')}
49
+ FROM public.lt_escalations
50
+ WHERE ${input.where}
51
+ AND status = 'resolved'
52
+ AND resolved_at >= ${fromP}
53
+ AND resolved_at < ${toP}
54
+ ${groupBySql}ORDER BY ${input.order}
55
+ LIMIT $${params.length - 1} OFFSET $${params.length}`;
56
+ return { sql, params, nowAnchored: win.nowAnchored };
57
+ }
@@ -9,10 +9,13 @@ export interface BuiltAggregateQuery {
9
9
  facets: string[];
10
10
  /** true when grouped by the derived state label (groupBy.state). */
11
11
  stateGrouped: boolean;
12
- measureKind: 'membership' | 'dwell';
12
+ measureKind: AggregateMeasureKind;
13
+ /** true when the resolved measure groups by calendar bucket. */
14
+ bucketed: boolean;
13
15
  /** true = the anchor touches now (short cache); false = fully past (immutable). */
14
16
  nowAnchored: boolean;
15
17
  }
18
+ export type AggregateMeasureKind = 'membership' | 'dwell' | 'resolved';
16
19
  /** Per-role state naming, from lt_roles.entity_state_source. */
17
20
  export type StateSources = Record<string, 'role' | 'subtype'>;
18
21
  export interface BuiltTimelineQuery {
@@ -23,6 +26,8 @@ export interface BuiltTimelineQuery {
23
26
  facets: string[];
24
27
  nowAnchored: boolean;
25
28
  }
29
+ /** The projected-facet output alias; the key is FACET_KEY-validated. */
30
+ export declare function facetAlias(key: string): string;
26
31
  /**
27
32
  * Build the grouped aggregate (membership or dwell) as one parameterized
28
33
  * query. `query.entity` must be RESOLVED by the caller before building
@@ -18,9 +18,11 @@
18
18
  // ---------------------------------------------------------------------------
19
19
  Object.defineProperty(exports, "__esModule", { value: true });
20
20
  exports.AnalyticsInputError = void 0;
21
+ exports.facetAlias = facetAlias;
21
22
  exports.buildAggregateQuery = buildAggregateQuery;
22
23
  exports.buildTimelineQuery = buildTimelineQuery;
23
24
  const facet_sql_1 = require("./facet-sql");
25
+ const aggregate_resolved_sql_1 = require("./aggregate-resolved-sql");
24
26
  const aggregate_validate_1 = require("./aggregate-validate");
25
27
  const types_1 = require("../../types");
26
28
  var aggregate_validate_2 = require("./aggregate-validate");
@@ -60,24 +62,27 @@ function overlapsAfter(liveList, fromParam) {
60
62
  return `(status IN (${liveList}) OR (status NOT IN (${liveList}) AND COALESCE(resolved_at, updated_at) > ${fromParam}))`;
61
63
  }
62
64
  /** ORDER BY over the RESULT groups: measure aliases and grouped keys only. */
63
- function buildAggregateOrder(orderBy, measureKind, columns, facets, stateGrouped = false) {
65
+ function buildAggregateOrder(orderBy, measureKind, columns, facets, stateGrouped = false, bucketed = false) {
64
66
  if (!orderBy || !orderBy.length)
65
- return 'sample_count DESC';
67
+ return bucketed ? 'bucket ASC, sample_count DESC' : 'sample_count DESC';
66
68
  const parts = [];
67
69
  for (const o of orderBy) {
68
70
  const dir = o.direction === 'desc' ? 'DESC' : 'ASC';
69
71
  if (o.field === 'state' && stateGrouped) {
70
72
  parts.push(`state_label ${dir}`);
71
73
  }
74
+ else if (o.field === 'bucket' && bucketed) {
75
+ parts.push(`bucket ${dir}`);
76
+ }
72
77
  else if (o.field === 'count') {
73
- if (measureKind !== 'membership') {
74
- throw new aggregate_validate_1.AnalyticsInputError('orderBy "count" applies to membership; use "dwellSeconds" for dwell');
78
+ if (measureKind === 'dwell') {
79
+ throw new aggregate_validate_1.AnalyticsInputError('orderBy "count" applies to membership and resolved; use "dwellSeconds" for dwell');
75
80
  }
76
81
  parts.push(`"count" ${dir}`);
77
82
  }
78
83
  else if (o.field === 'dwellSeconds') {
79
84
  if (measureKind !== 'dwell') {
80
- throw new aggregate_validate_1.AnalyticsInputError('orderBy "dwellSeconds" applies to dwell; use "count" for membership');
85
+ throw new aggregate_validate_1.AnalyticsInputError('orderBy "dwellSeconds" applies to dwell; use "count" for membership and resolved');
81
86
  }
82
87
  parts.push(`dwell_seconds ${dir}`);
83
88
  }
@@ -91,7 +96,7 @@ function buildAggregateOrder(orderBy, measureKind, columns, facets, stateGrouped
91
96
  parts.push(`${facetAlias(o.field)} ${dir} NULLS LAST`);
92
97
  }
93
98
  else {
94
- throw new aggregate_validate_1.AnalyticsInputError(`orderBy field "${o.field}" is not a measure (count, dwellSeconds, sampleCount) or a grouped key`);
99
+ throw new aggregate_validate_1.AnalyticsInputError(`orderBy field "${o.field}" is not a measure (count, dwellSeconds, sampleCount), a bucket, or a grouped key`);
95
100
  }
96
101
  }
97
102
  return parts.join(', ');
@@ -119,12 +124,13 @@ function buildAggregateQuery(input, opts = {}) {
119
124
  const liveList = statusLiterals(live);
120
125
  const { pageLimit, offset } = (0, aggregate_validate_1.resolvePage)(input.limit, input.offset);
121
126
  const measure = input.measure;
122
- if (measure?.kind !== 'membership' && measure?.kind !== 'dwell') {
123
- throw new aggregate_validate_1.AnalyticsInputError('measure.kind must be "membership" or "dwell"');
127
+ if (measure?.kind !== 'membership' && measure?.kind !== 'dwell' && measure?.kind !== 'resolved') {
128
+ throw new aggregate_validate_1.AnalyticsInputError('measure.kind must be "membership", "dwell" or "resolved"');
124
129
  }
130
+ const bucket = measure.kind === 'resolved' ? (0, aggregate_resolved_sql_1.resolveBucket)(measure.bucket) : null;
125
131
  const params = [];
126
132
  const where = (0, facet_sql_1.buildFacetWhere)(query, params);
127
- const order = buildAggregateOrder(input.orderBy, measure.kind, columns, facets, state);
133
+ const order = buildAggregateOrder(input.orderBy, measure.kind, columns, facets, state, bucket !== null);
128
134
  const groupSelect = [];
129
135
  const groupKeys = [];
130
136
  if (state) {
@@ -139,6 +145,21 @@ function buildAggregateQuery(input, opts = {}) {
139
145
  groupSelect.push(`(metadata->>'${key}') AS ${facetAlias(key)}`);
140
146
  groupKeys.push(facetAlias(key));
141
147
  }
148
+ if (measure.kind === 'resolved') {
149
+ if (input.liveStatuses !== undefined) {
150
+ throw new aggregate_validate_1.AnalyticsInputError('liveStatuses does not apply to the resolved measure, which counts resolved rows');
151
+ }
152
+ if (!query.role && !query.roles?.length) {
153
+ throw new aggregate_validate_1.AnalyticsInputError('the resolved measure requires a role scope: query.role, query.roles or query.entity');
154
+ }
155
+ if (input.distinctBy !== undefined)
156
+ (0, aggregate_validate_1.requireFacetKey)(input.distinctBy, 'distinctBy');
157
+ const built = (0, aggregate_resolved_sql_1.buildResolvedAggregate)({
158
+ window: measure.window, bucket, where, params, groupSelect, groupKeys, order,
159
+ distinctBy: input.distinctBy, pageLimit, offset,
160
+ });
161
+ return { ...built, pageLimit, columns, facets, stateGrouped: state, measureKind: 'resolved', bucketed: bucket !== null };
162
+ }
142
163
  const groupBySql = groupKeys.length ? `GROUP BY ${groupKeys.join(', ')}\n` : '';
143
164
  if (measure.kind === 'membership') {
144
165
  const asOf = (0, aggregate_validate_1.resolveAsOf)(measure.asOf);
@@ -165,11 +186,11 @@ WHERE ${where}
165
186
  AND ${intervalPred}
166
187
  ${groupBySql}ORDER BY ${order}
167
188
  LIMIT $${params.length - 1} OFFSET $${params.length}`;
168
- return { sql, params, pageLimit, columns, facets, stateGrouped: state, measureKind: 'membership', nowAnchored: asOf === null };
189
+ return { sql, params, pageLimit, columns, facets, stateGrouped: state, measureKind: 'membership', bucketed: false, nowAnchored: asOf === null };
169
190
  }
170
191
  // Dwell: clip each overlapping interval to the window, sum open-seconds.
171
192
  if (input.distinctBy !== undefined) {
172
- throw new aggregate_validate_1.AnalyticsInputError('distinctBy is a membership concept — for per-entity dwell, put the entity facet in groupBy.facets');
193
+ throw new aggregate_validate_1.AnalyticsInputError('distinctBy applies to membership and resolved; for per-entity dwell, put the entity facet in groupBy.facets');
173
194
  }
174
195
  const win = (0, aggregate_validate_1.resolveWindow)(measure.window, 'measure.window');
175
196
  if (!win)
@@ -198,7 +219,7 @@ SELECT ${[...groupKeys, 'SUM(EXTRACT(EPOCH FROM (e - s)))::float8 AS dwell_secon
198
219
  FROM span
199
220
  ${groupBySql}ORDER BY ${order}
200
221
  LIMIT $${params.length - 1} OFFSET $${params.length}`;
201
- return { sql, params, pageLimit, columns, facets, stateGrouped: state, measureKind: 'dwell', nowAnchored: win.nowAnchored };
222
+ return { sql, params, pageLimit, columns, facets, stateGrouped: state, measureKind: 'dwell', bucketed: false, nowAnchored: win.nowAnchored };
202
223
  }
203
224
  /** Build the single-entity interval sequence, created_at-ordered, gaps preserved. */
204
225
  function buildTimelineQuery(input) {
@@ -29,8 +29,11 @@ export declare function resolveTimelinePage(order: 'asc' | 'desc' | undefined, b
29
29
  before: Date | null;
30
30
  };
31
31
  export declare function toInstant(value: Date | string, label: string): Date;
32
- /** Half-open [from, to): ordered, and bounded by LT_ANALYTICS_MAX_WINDOW_DAYS. */
33
- export declare function resolveWindow(window: AnalyticsWindow | undefined, label?: string): {
32
+ /** Half-open [from, to): ordered, and bounded by LT_ANALYTICS_MAX_WINDOW_DAYS (or the env var naming `maxDays`). */
33
+ export declare function resolveWindow(window: AnalyticsWindow | undefined, label?: string, maxDays?: {
34
+ days: number;
35
+ envVar: string;
36
+ }): {
34
37
  from: Date;
35
38
  to: Date;
36
39
  nowAnchored: boolean;
@@ -142,8 +142,11 @@ function toInstant(value, label) {
142
142
  }
143
143
  return d;
144
144
  }
145
- /** Half-open [from, to): ordered, and bounded by LT_ANALYTICS_MAX_WINDOW_DAYS. */
146
- function resolveWindow(window, label = 'window') {
145
+ /** Half-open [from, to): ordered, and bounded by LT_ANALYTICS_MAX_WINDOW_DAYS (or the env var naming `maxDays`). */
146
+ function resolveWindow(window, label = 'window', maxDays = {
147
+ days: config_1.config.LT_ANALYTICS_MAX_WINDOW_DAYS,
148
+ envVar: 'LT_ANALYTICS_MAX_WINDOW_DAYS',
149
+ }) {
147
150
  if (!window)
148
151
  return null;
149
152
  const from = toInstant(window.from, `${label}.from`);
@@ -152,8 +155,8 @@ function resolveWindow(window, label = 'window') {
152
155
  throw new AnalyticsInputError(`${label} is empty: from must be before to`);
153
156
  }
154
157
  const spanDays = (to.getTime() - from.getTime()) / 86_400_000;
155
- if (spanDays > config_1.config.LT_ANALYTICS_MAX_WINDOW_DAYS) {
156
- throw new AnalyticsInputError(`${label} spans ${spanDays.toFixed(1)} days; the maximum is ${config_1.config.LT_ANALYTICS_MAX_WINDOW_DAYS} (LT_ANALYTICS_MAX_WINDOW_DAYS)`);
158
+ if (spanDays > maxDays.days) {
159
+ throw new AnalyticsInputError(`${label} spans ${spanDays.toFixed(1)} days; the maximum is ${maxDays.days} (${maxDays.envVar})`);
157
160
  }
158
161
  return { from, to, nowAnchored: to.getTime() >= Date.now() };
159
162
  }
@@ -67,10 +67,12 @@ function toAggregateRow(raw, built, states) {
67
67
  const row = { facets, sampleCount: Number(raw.sample_count) };
68
68
  for (const column of built.columns)
69
69
  row[column] = raw[column] ?? undefined;
70
- if (built.measureKind === 'membership')
71
- row.count = Number(raw.count);
72
- else
70
+ if (built.measureKind === 'dwell')
73
71
  row.dwellSeconds = Number(raw.dwell_seconds);
72
+ else
73
+ row.count = Number(raw.count);
74
+ if (built.bucketed)
75
+ row.bucket = toIso(raw.bucket);
74
76
  if (built.stateGrouped) {
75
77
  row.state = raw.state_label;
76
78
  }
@@ -58,6 +58,9 @@ async function createEscalation(input) {
58
58
  if (envelopeObj[escalation_1.ESCALATION_ENVELOPE_KEYS.LOOKUPS] !== undefined) {
59
59
  (0, escalation_1.assertLookupRefs)(envelopeObj[escalation_1.ESCALATION_ENVELOPE_KEYS.LOOKUPS]);
60
60
  }
61
+ if (envelopeObj[escalation_1.ESCALATION_ENVELOPE_KEYS.FILES] !== undefined) {
62
+ (0, escalation_1.assertFileRefs)(envelopeObj[escalation_1.ESCALATION_ENVELOPE_KEYS.FILES]);
63
+ }
61
64
  const client = await (0, client_1.escalations)();
62
65
  const entry = await client.create({
63
66
  type: input.type,
@@ -0,0 +1,39 @@
1
+ import { type EscalationFileRef, type LTEscalationRecord } from '../../types/escalation';
2
+ /** One file ref answered: a signed link, or the reason there is none. */
3
+ export interface EscalationFileLink {
4
+ as: string;
5
+ path: string;
6
+ name: string;
7
+ contentType: string;
8
+ url?: string;
9
+ expiresAt?: string;
10
+ error?: string;
11
+ }
12
+ /**
13
+ * The refs riding the row's envelope under the reserved key, each checked
14
+ * again at read time: an engine-written envelope can carry refs that never
15
+ * passed the creation check, and nothing outside the store is ever signed.
16
+ */
17
+ export declare function readFileRefs(envelope: string | Record<string, unknown> | null | undefined): Array<EscalationFileRef | {
18
+ error: string;
19
+ ref: unknown;
20
+ }>;
21
+ /**
22
+ * The `file` form domain without links, keyed by each ref's `as`: what the
23
+ * server's validation pass sees, so a condition on a file's presence
24
+ * evaluates the same on both sides of the wire.
25
+ */
26
+ export declare function fileContext(envelope: string | Record<string, unknown> | null | undefined): Record<string, Pick<EscalationFileLink, 'as' | 'path' | 'name' | 'contentType'>> | null;
27
+ /** Seconds left on the reader's own live claim, or 0. */
28
+ export declare function claimSecondsLeft(escalation: Pick<LTEscalationRecord, 'assigned_to' | 'assigned_until'>, userId: string, now?: number): number;
29
+ /**
30
+ * How long a link lives: the ref's ask (else the default), stretched to
31
+ * cover the reader's live claim, held within the configured bounds.
32
+ */
33
+ export declare function fileLinkSeconds(ref: Pick<EscalationFileRef, 'expiresIn'>, claimSeconds: number): number;
34
+ /**
35
+ * Sign every ref. Signing is local to the process (no storage round trip),
36
+ * so a page open costs one signature per file. A ref that cannot be signed
37
+ * answers with `error`; the batch never fails.
38
+ */
39
+ export declare function signFileRefs(refs: ReturnType<typeof readFileRefs>, claimSeconds: number, now?: number): Promise<EscalationFileLink[]>;
@@ -0,0 +1,118 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.readFileRefs = readFileRefs;
7
+ exports.fileContext = fileContext;
8
+ exports.claimSecondsLeft = claimSecondsLeft;
9
+ exports.fileLinkSeconds = fileLinkSeconds;
10
+ exports.signFileRefs = signFileRefs;
11
+ const path_1 = __importDefault(require("path"));
12
+ const config_1 = require("../../modules/config");
13
+ const storage_1 = require("../../lib/storage");
14
+ const mime_1 = require("../../lib/storage/mime");
15
+ const escalation_1 = require("../../types/escalation");
16
+ /**
17
+ * The refs riding the row's envelope under the reserved key, each checked
18
+ * again at read time: an engine-written envelope can carry refs that never
19
+ * passed the creation check, and nothing outside the store is ever signed.
20
+ */
21
+ function readFileRefs(envelope) {
22
+ if (!envelope)
23
+ return [];
24
+ let refs;
25
+ try {
26
+ const parsed = typeof envelope === 'string' ? JSON.parse(envelope) : envelope;
27
+ refs = parsed && typeof parsed === 'object' ? parsed[escalation_1.ESCALATION_ENVELOPE_KEYS.FILES] : undefined;
28
+ }
29
+ catch {
30
+ return [];
31
+ }
32
+ if (!Array.isArray(refs))
33
+ return [];
34
+ const seen = new Set();
35
+ return refs.slice(0, escalation_1.ESCALATION_FILE_REFS_MAX).map((ref) => {
36
+ const problem = (0, escalation_1.fileRefProblem)(ref);
37
+ if (problem)
38
+ return { error: problem, ref };
39
+ const alias = ref.as;
40
+ if (seen.has(alias))
41
+ return { error: `File ref ${alias}: "as" names more than one file`, ref };
42
+ seen.add(alias);
43
+ return ref;
44
+ });
45
+ }
46
+ /**
47
+ * The `file` form domain without links, keyed by each ref's `as`: what the
48
+ * server's validation pass sees, so a condition on a file's presence
49
+ * evaluates the same on both sides of the wire.
50
+ */
51
+ function fileContext(envelope) {
52
+ const entries = readFileRefs(envelope).filter((r) => !('error' in r));
53
+ if (entries.length === 0)
54
+ return null;
55
+ return Object.fromEntries(entries.map((ref) => {
56
+ const key = (0, escalation_1.normalizeFileRefPath)(ref.path);
57
+ return [ref.as, {
58
+ as: ref.as,
59
+ path: key,
60
+ name: ref.name ?? path_1.default.posix.basename(key),
61
+ contentType: ref.contentType ?? (0, mime_1.mimeFromPath)(key),
62
+ }];
63
+ }));
64
+ }
65
+ /** Seconds left on the reader's own live claim, or 0. */
66
+ function claimSecondsLeft(escalation, userId, now = Date.now()) {
67
+ if (!escalation.assigned_to || escalation.assigned_to !== userId || !escalation.assigned_until)
68
+ return 0;
69
+ const until = new Date(escalation.assigned_until).getTime();
70
+ return Number.isFinite(until) && until > now ? Math.ceil((until - now) / 1000) : 0;
71
+ }
72
+ /**
73
+ * How long a link lives: the ref's ask (else the default), stretched to
74
+ * cover the reader's live claim, held within the configured bounds.
75
+ */
76
+ function fileLinkSeconds(ref, claimSeconds) {
77
+ const asked = ref.expiresIn ?? config_1.config.LT_ESCALATION_FILE_URL_TTL_SECONDS;
78
+ const wanted = Math.max(asked, claimSeconds);
79
+ const min = config_1.config.LT_ESCALATION_FILE_URL_MIN_SECONDS;
80
+ const max = Math.max(min, config_1.config.LT_ESCALATION_FILE_URL_MAX_SECONDS);
81
+ return Math.min(Math.max(wanted, min), max);
82
+ }
83
+ /**
84
+ * Sign every ref. Signing is local to the process (no storage round trip),
85
+ * so a page open costs one signature per file. A ref that cannot be signed
86
+ * answers with `error`; the batch never fails.
87
+ */
88
+ async function signFileRefs(refs, claimSeconds, now = Date.now()) {
89
+ const storage = (0, storage_1.getStorageBackend)();
90
+ return Promise.all(refs.map(async (entry) => {
91
+ if ('error' in entry) {
92
+ const raw = (entry.ref ?? {});
93
+ return {
94
+ as: typeof raw.as === 'string' ? raw.as : '',
95
+ path: typeof raw.path === 'string' ? raw.path : '',
96
+ name: '',
97
+ contentType: '',
98
+ error: entry.error,
99
+ };
100
+ }
101
+ const key = (0, escalation_1.normalizeFileRefPath)(entry.path);
102
+ const link = {
103
+ as: entry.as,
104
+ path: key,
105
+ name: entry.name ?? path_1.default.posix.basename(key),
106
+ contentType: entry.contentType ?? (0, mime_1.mimeFromPath)(key),
107
+ };
108
+ const seconds = fileLinkSeconds(entry, claimSeconds);
109
+ try {
110
+ link.url = await storage.getSignedUrl(key, seconds);
111
+ link.expiresAt = new Date(now + seconds * 1000).toISOString();
112
+ }
113
+ catch (err) {
114
+ link.error = err?.message ?? 'The file link could not be signed';
115
+ }
116
+ return link;
117
+ }));
118
+ }
@@ -1,3 +1,23 @@
1
+ /**
2
+ * Server-side resolver schema enforcement — the API-first counterpart of the
3
+ * dashboard's pre-submission pass. Every resolve surface (HTTP, SDK, MCP)
4
+ * calls checkResolverPayload before committing a resolution; roles that opt in
5
+ * via enforce_schema get their form_schema enforced as an API contract, and
6
+ * violations surface as the canonical 422 body (types/validation.ts).
7
+ *
8
+ * Production shape: the gate consults the cached enforcing-role set first, so
9
+ * when no involved role enforces, a resolve costs zero additional SQL. Schema
10
+ * reads for enforcing roles come from the enforcement cache (immutable pinned
11
+ * snapshots cached indefinitely; latest schemas under a short TTL).
12
+ *
13
+ * Schema resolution matches the resolve UI, most specific first:
14
+ * 1. metadata.form_schema — a full form embedded on the row
15
+ * 2. the lt_role_schemas snapshot pinned by metadata.schema_version
16
+ * 3. the role's live (latest) form_schema
17
+ * A role that enforces but declares no schema has no contract to enforce —
18
+ * the gate passes.
19
+ */
20
+ import { type ReviewFacets } from '../../shared/form-validation';
1
21
  import { type LTFieldViolation, type LTValidationErrorBody } from '../../types/validation';
2
22
  /** The row fields the gate reads — every resolve surface already holds these. */
3
23
  export interface ResolvableEscalationRow {
@@ -14,6 +34,12 @@ export interface ResolverSchemaViolationReport {
14
34
  schemaVersion: number | null;
15
35
  violations: LTFieldViolation[];
16
36
  }
37
+ /** The gate's verdict, plus the review facets a passing answer stamps. */
38
+ export interface ResolverPayloadAssessment {
39
+ violation: ResolverSchemaViolationReport | null;
40
+ /** Review summary for the row's metadata, when the form opts in (x-lt-confidence facets). */
41
+ facets: ReviewFacets | null;
42
+ }
17
43
  /**
18
44
  * Validate a submitted resolverPayload against the escalation's enforced form
19
45
  * schema. Returns null when the resolution may proceed (role does not enforce,
@@ -24,5 +50,12 @@ export interface ResolverSchemaViolationReport {
24
50
  * It is only invoked when the role actually enforces.
25
51
  */
26
52
  export declare function checkResolverPayload(escalation: ResolvableEscalationRow, resolverPayload: Record<string, any>, loadEnvelope?: () => Promise<Record<string, any>>, resolvingUserId?: string): Promise<ResolverSchemaViolationReport | null>;
53
+ /**
54
+ * The same gate, also returning the review facets a passing answer stamps
55
+ * onto the row. Single-row resolve paths merge `facets` into the outcome
56
+ * patch, so they commit inside the guarded resolve UPDATE. Computed from the
57
+ * schema and context the gate already loaded: no extra reads.
58
+ */
59
+ export declare function assessResolverPayload(escalation: ResolvableEscalationRow, resolverPayload: Record<string, any>, loadEnvelope?: () => Promise<Record<string, any>>, resolvingUserId?: string): Promise<ResolverPayloadAssessment>;
27
60
  /** The canonical 422 body for a violation report (types/validation.ts). */
28
61
  export declare function toValidationErrorBody(report: ResolverSchemaViolationReport): LTValidationErrorBody;
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.checkResolverPayload = checkResolverPayload;
4
+ exports.assessResolverPayload = assessResolverPayload;
4
5
  exports.toValidationErrorBody = toValidationErrorBody;
5
6
  /**
6
7
  * Server-side resolver schema enforcement — the API-first counterpart of the
@@ -24,6 +25,7 @@ exports.toValidationErrorBody = toValidationErrorBody;
24
25
  const form_validation_1 = require("../../shared/form-validation");
25
26
  const submit_guard_1 = require("./submit-guard");
26
27
  const roster_1 = require("./roster");
28
+ const file_links_1 = require("./file-links");
27
29
  const enforcement_cache_1 = require("../role/enforcement-cache");
28
30
  const knowledge_1 = require("../knowledge");
29
31
  const escalation_1 = require("../../types/escalation");
@@ -48,6 +50,7 @@ function pinnedVersionOf(metadata) {
48
50
  return parseInt(raw, 10);
49
51
  return null;
50
52
  }
53
+ const PASS = { violation: null, facets: null };
51
54
  /**
52
55
  * Validate a submitted resolverPayload against the escalation's enforced form
53
56
  * schema. Returns null when the resolution may proceed (role does not enforce,
@@ -58,12 +61,22 @@ function pinnedVersionOf(metadata) {
58
61
  * It is only invoked when the role actually enforces.
59
62
  */
60
63
  async function checkResolverPayload(escalation, resolverPayload, loadEnvelope, resolvingUserId) {
64
+ const { violation } = await assessResolverPayload(escalation, resolverPayload, loadEnvelope, resolvingUserId);
65
+ return violation;
66
+ }
67
+ /**
68
+ * The same gate, also returning the review facets a passing answer stamps
69
+ * onto the row. Single-row resolve paths merge `facets` into the outcome
70
+ * patch, so they commit inside the guarded resolve UPDATE. Computed from the
71
+ * schema and context the gate already loaded: no extra reads.
72
+ */
73
+ async function assessResolverPayload(escalation, resolverPayload, loadEnvelope, resolvingUserId) {
61
74
  const role = escalation.role;
62
75
  if (!role)
63
- return null;
76
+ return PASS;
64
77
  const enforcing = await (0, enforcement_cache_1.getEnforcingRoles)();
65
78
  if (!enforcing.has(role))
66
- return null;
79
+ return PASS;
67
80
  const metadata = (escalation.metadata ?? {});
68
81
  const pin = pinnedVersionOf(metadata);
69
82
  const embedded = metadata[escalation_1.ESCALATION_METADATA_KEYS.FORM_SCHEMA];
@@ -71,7 +84,7 @@ async function checkResolverPayload(escalation, resolverPayload, loadEnvelope, r
71
84
  ? embedded
72
85
  : await (0, enforcement_cache_1.getEnforcedFormSchema)(role, pin);
73
86
  if (!schema)
74
- return null;
87
+ return PASS;
75
88
  const envelope = loadEnvelope
76
89
  ? await loadEnvelope()
77
90
  : parseJsonOrNull(escalation.envelope) ?? {};
@@ -79,12 +92,14 @@ async function checkResolverPayload(escalation, resolverPayload, loadEnvelope, r
79
92
  // immutable snapshot cache, so enforcement reads the exact editions the
80
93
  // form rendered from. Rows without refs cost zero SQL here.
81
94
  const lookup = await (0, knowledge_1.resolveLookupContext)(envelope[escalation_1.ESCALATION_ENVELOPE_KEYS.LOOKUPS]);
95
+ const file = (0, file_links_1.fileContext)(envelope);
82
96
  const ctx = {
83
97
  escalation: escalation,
84
98
  metadata,
85
99
  envelope,
86
100
  payload: parseJsonOrNull(escalation.escalation_payload),
87
101
  ...(lookup ? { lookup } : {}),
102
+ ...(file ? { file } : {}),
88
103
  };
89
104
  // Roster counts read the submitted form values (an `in` source may name a
90
105
  // form field), then join the context so showIf and the guard see them.
@@ -98,9 +113,10 @@ async function checkResolverPayload(escalation, resolverPayload, loadEnvelope, r
98
113
  // contract, so a raw-API resolve honors the gate the dashboard shows.
99
114
  const guardViolation = await (0, submit_guard_1.checkSubmitGuard)(schema, ctx, resolvingUserId);
100
115
  const allViolations = guardViolation ? [...violations, guardViolation] : violations;
101
- if (allViolations.length === 0)
102
- return null;
103
- return { role, schemaVersion: pin, violations: allViolations };
116
+ if (allViolations.length > 0) {
117
+ return { violation: { role, schemaVersion: pin, violations: allViolations }, facets: null };
118
+ }
119
+ return { violation: null, facets: (0, form_validation_1.buildReviewFacets)(schema, ctx, formValues) };
104
120
  }
105
121
  /** The canonical 422 body for a violation report (types/validation.ts). */
106
122
  function toValidationErrorBody(report) {
@@ -1,14 +1,15 @@
1
1
  import type { Types } from '@hotmeshio/hotmesh';
2
- import { type AccumulateConfig, type AccumulatorResult, type EscalationLookupRef } from '../../types/escalation';
2
+ import { type AccumulateConfig, type AccumulatorResult, type EscalationLookupRef, type EscalationFileRef } from '../../types/escalation';
3
3
  /**
4
4
  * HotMesh's escalation config plus long-tail sugar. `schemaVersion` pins the
5
5
  * role form version (lt_role_schemas) the resolve UI renders; `lookups` names
6
6
  * the knowledge entries the form may read (the `lookup.*` context domain, see
7
7
  * docs/hitl/lookups.md), each pinned to an edition or following the current
8
- * one. Both are LITERALS the workflow author sets, folded before the config
9
- * reaches the engine:
8
+ * one; `files` names the stored files the form shows (the `file.*` context
9
+ * domain, see docs/hitl/files.md). All are set by the workflow author and
10
+ * folded before the config reaches the engine:
10
11
  * schemaVersion into the GIN-indexed metadata (a queryable facet), lookups
11
- * into the unindexed envelope (form plumbing, never a facet). Folding is a
12
+ * and files into the unindexed envelope (form plumbing, never a facet). Folding is a
12
13
  * pure transform — no query, no activity — so a pinned `conditional` costs
13
14
  * exactly what an unpinned one does. Omit `schemaVersion` and the resolve UI
14
15
  * renders the role's latest form when the escalation is fetched.
@@ -16,6 +17,7 @@ import { type AccumulateConfig, type AccumulatorResult, type EscalationLookupRef
16
17
  export type ConditionEscalationConfig = Types.ConditionQueueConfig & {
17
18
  schemaVersion?: number;
18
19
  lookups?: EscalationLookupRef[];
20
+ files?: EscalationFileRef[];
19
21
  };
20
22
  /**
21
23
  * Wait for a signal and resolve the associated escalation automatically.
@@ -74,6 +76,20 @@ export type ConditionEscalationConfig = Types.ConditionQueueConfig & {
74
76
  * });
75
77
  * ```
76
78
  *
79
+ * **Stored files: documents the form shows.** Each ref names an object in
80
+ * file storage and the address the form reads it at (`file.<as>`). The
81
+ * resolve UI fetches time-limited signed links when the page opens and again
82
+ * on claim; a `document` widget renders PDFs and images inline.
83
+ *
84
+ * ```typescript
85
+ * const decision = await conditional<IntakeReviewV1>(signalId, {
86
+ * role: 'intake-verifier',
87
+ * description: instructions,
88
+ * envelope: { formDefaults: extracted, confidence: scores },
89
+ * files: [{ path: `referrals/${referralId}.pdf`, as: 'referral' }],
90
+ * });
91
+ * ```
92
+ *
77
93
  * **With an SLA (hotmesh 0.25.1+) — add `timeout` to the config.** The same
78
94
  * single wait arms a resume timer alongside the escalation row: when the timer
79
95
  * fires first, this helper returns `false`, the row transitions to