@hotmeshio/long-tail 0.4.13 → 0.4.14

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 (184) hide show
  1. package/build/api/escalations/helpers.d.ts +14 -0
  2. package/build/api/escalations/helpers.js +14 -0
  3. package/build/api/escalations/index.d.ts +1 -0
  4. package/build/api/escalations/index.js +5 -1
  5. package/build/api/escalations/metadata.d.ts +56 -0
  6. package/build/api/escalations/metadata.js +178 -0
  7. package/build/bin/ltc.js +17 -0
  8. package/build/lib/cli/commands/escalations.d.ts +9 -0
  9. package/build/lib/cli/commands/escalations.js +36 -0
  10. package/build/lib/db/schemas/010_metadata_gin.sql +5 -0
  11. package/build/routes/escalations/index.js +3 -0
  12. package/build/routes/escalations/metadata.d.ts +6 -0
  13. package/build/routes/escalations/metadata.js +86 -0
  14. package/build/sdk/index.d.ts +19 -0
  15. package/build/sdk/index.js +3 -0
  16. package/build/services/escalation/crud.d.ts +5 -0
  17. package/build/services/escalation/crud.js +27 -0
  18. package/build/services/escalation/sql.d.ts +5 -0
  19. package/build/services/escalation/sql.js +39 -1
  20. package/build/services/interceptor/activities/escalation.js +11 -0
  21. package/build/start/server.js +5 -4
  22. package/build/tsconfig.tsbuildinfo +1 -1
  23. package/dashboard/dist/assets/{AdminDashboard-Jwr3Fsaz.js → AdminDashboard-BuqyRY2r.js} +2 -2
  24. package/dashboard/dist/assets/{AdminDashboard-Jwr3Fsaz.js.map → AdminDashboard-BuqyRY2r.js.map} +1 -1
  25. package/dashboard/dist/assets/AgentConfigPage-Bum1dUIi.js +16 -0
  26. package/dashboard/dist/assets/AgentConfigPage-Bum1dUIi.js.map +1 -0
  27. package/dashboard/dist/assets/{AgentDetailPage-Cw7foCHd.js → AgentDetailPage-0Kq-tBF2.js} +2 -2
  28. package/dashboard/dist/assets/{AgentDetailPage-Cw7foCHd.js.map → AgentDetailPage-0Kq-tBF2.js.map} +1 -1
  29. package/dashboard/dist/assets/{AgentsPage-DzpWsTFO.js → AgentsPage-B5gYDSOX.js} +2 -2
  30. package/dashboard/dist/assets/{AgentsPage-DzpWsTFO.js.map → AgentsPage-B5gYDSOX.js.map} +1 -1
  31. package/dashboard/dist/assets/AvailableEscalationsPage-BWHThQDC.js +2 -0
  32. package/dashboard/dist/assets/AvailableEscalationsPage-BWHThQDC.js.map +1 -0
  33. package/dashboard/dist/assets/{BotPicker-Ddu4V0uf.js → BotPicker-BQ336piW.js} +2 -2
  34. package/dashboard/dist/assets/{BotPicker-Ddu4V0uf.js.map → BotPicker-BQ336piW.js.map} +1 -1
  35. package/dashboard/dist/assets/{CapabilitiesPage-BTd-uYTM.js → CapabilitiesPage-CkiJROX-.js} +2 -2
  36. package/dashboard/dist/assets/{CapabilitiesPage-BTd-uYTM.js.map → CapabilitiesPage-CkiJROX-.js.map} +1 -1
  37. package/dashboard/dist/assets/{CollapsibleSection-DM-75khr.js → CollapsibleSection-SM8_UjNe.js} +2 -2
  38. package/dashboard/dist/assets/{CollapsibleSection-DM-75khr.js.map → CollapsibleSection-SM8_UjNe.js.map} +1 -1
  39. package/dashboard/dist/assets/{CredentialsPage-BQNraRZu.js → CredentialsPage-f6niro9_.js} +2 -2
  40. package/dashboard/dist/assets/{CredentialsPage-BQNraRZu.js.map → CredentialsPage-f6niro9_.js.map} +1 -1
  41. package/dashboard/dist/assets/{CronLabel-CPuMNBua.js → CronLabel-DINmdqoe.js} +2 -2
  42. package/dashboard/dist/assets/{CronLabel-CPuMNBua.js.map → CronLabel-DINmdqoe.js.map} +1 -1
  43. package/dashboard/dist/assets/{CustomDurationPicker-CLq8B89Y.js → CustomDurationPicker-BCUcYxfB.js} +2 -2
  44. package/dashboard/dist/assets/{CustomDurationPicker-CLq8B89Y.js.map → CustomDurationPicker-BCUcYxfB.js.map} +1 -1
  45. package/dashboard/dist/assets/{ElapsedCell-8lk94nZt.js → ElapsedCell-DPYZnXsX.js} +2 -2
  46. package/dashboard/dist/assets/{ElapsedCell-8lk94nZt.js.map → ElapsedCell-DPYZnXsX.js.map} +1 -1
  47. package/dashboard/dist/assets/{EscalationsOverview-BfSrQ7A5.js → EscalationsOverview-CTB8AEBd.js} +2 -2
  48. package/dashboard/dist/assets/{EscalationsOverview-BfSrQ7A5.js.map → EscalationsOverview-CTB8AEBd.js.map} +1 -1
  49. package/dashboard/dist/assets/{EventTable-D3IOLoxv.js → EventTable-8_r3Tg09.js} +2 -2
  50. package/dashboard/dist/assets/{EventTable-D3IOLoxv.js.map → EventTable-8_r3Tg09.js.map} +1 -1
  51. package/dashboard/dist/assets/{HomePage-RO3qbF38.js → HomePage-Bjxnjv6p.js} +2 -2
  52. package/dashboard/dist/assets/{HomePage-RO3qbF38.js.map → HomePage-Bjxnjv6p.js.map} +1 -1
  53. package/dashboard/dist/assets/{ListToolbar-4lObXT3_.js → ListToolbar-B60JrvJ9.js} +2 -2
  54. package/dashboard/dist/assets/{ListToolbar-4lObXT3_.js.map → ListToolbar-B60JrvJ9.js.map} +1 -1
  55. package/dashboard/dist/assets/{McpOverview-BdLivZv8.js → McpOverview-whVRP_Nj.js} +2 -2
  56. package/dashboard/dist/assets/{McpOverview-BdLivZv8.js.map → McpOverview-whVRP_Nj.js.map} +1 -1
  57. package/dashboard/dist/assets/{McpQueryDetailPage-DKFkH1qa.js → McpQueryDetailPage-DPuujJkH.js} +2 -2
  58. package/dashboard/dist/assets/{McpQueryDetailPage-DKFkH1qa.js.map → McpQueryDetailPage-DPuujJkH.js.map} +1 -1
  59. package/dashboard/dist/assets/{McpQueryPage-AFV_QPwm.js → McpQueryPage-DciK6r7r.js} +2 -2
  60. package/dashboard/dist/assets/{McpQueryPage-AFV_QPwm.js.map → McpQueryPage-DciK6r7r.js.map} +1 -1
  61. package/dashboard/dist/assets/{McpRunDetailPage-DQJ41oKW.js → McpRunDetailPage-QEz8BCTu.js} +2 -2
  62. package/dashboard/dist/assets/{McpRunDetailPage-DQJ41oKW.js.map → McpRunDetailPage-QEz8BCTu.js.map} +1 -1
  63. package/dashboard/dist/assets/{McpRunsPage-CzVS7zcc.js → McpRunsPage-BA6AVpi_.js} +2 -2
  64. package/dashboard/dist/assets/{McpRunsPage-CzVS7zcc.js.map → McpRunsPage-BA6AVpi_.js.map} +1 -1
  65. package/dashboard/dist/assets/OperatorDashboard-DrUMzwnl.js +2 -0
  66. package/dashboard/dist/assets/OperatorDashboard-DrUMzwnl.js.map +1 -0
  67. package/dashboard/dist/assets/{ProcessDetailPage-qibro3Dm.js → ProcessDetailPage-Dc5ASJpQ.js} +2 -2
  68. package/dashboard/dist/assets/{ProcessDetailPage-qibro3Dm.js.map → ProcessDetailPage-Dc5ASJpQ.js.map} +1 -1
  69. package/dashboard/dist/assets/{ProcessesListPage-CPgiDbdS.js → ProcessesListPage-Sa-bjC-g.js} +2 -2
  70. package/dashboard/dist/assets/{ProcessesListPage-CPgiDbdS.js.map → ProcessesListPage-Sa-bjC-g.js.map} +1 -1
  71. package/dashboard/dist/assets/{RolesPage-BAj88I_Y.js → RolesPage-DmO8Jlbw.js} +2 -2
  72. package/dashboard/dist/assets/{RolesPage-BAj88I_Y.js.map → RolesPage-DmO8Jlbw.js.map} +1 -1
  73. package/dashboard/dist/assets/{RunAsSelector-IdZ-qOfl.js → RunAsSelector-yWEwIZRe.js} +2 -2
  74. package/dashboard/dist/assets/{RunAsSelector-IdZ-qOfl.js.map → RunAsSelector-yWEwIZRe.js.map} +1 -1
  75. package/dashboard/dist/assets/{SwimlaneTimeline-WQ6VMuqg.js → SwimlaneTimeline-CmzfFQ09.js} +2 -2
  76. package/dashboard/dist/assets/{SwimlaneTimeline-WQ6VMuqg.js.map → SwimlaneTimeline-CmzfFQ09.js.map} +1 -1
  77. package/dashboard/dist/assets/{TaskDetailPage-buNgjwiz.js → TaskDetailPage-CI4JTC62.js} +2 -2
  78. package/dashboard/dist/assets/{TaskDetailPage-buNgjwiz.js.map → TaskDetailPage-CI4JTC62.js.map} +1 -1
  79. package/dashboard/dist/assets/{TasksListPage-BQjjNjRC.js → TasksListPage-xdNmQsNE.js} +2 -2
  80. package/dashboard/dist/assets/{TasksListPage-BQjjNjRC.js.map → TasksListPage-xdNmQsNE.js.map} +1 -1
  81. package/dashboard/dist/assets/{TimeAgo-Dvkw4shy.js → TimeAgo-B_um9BWR.js} +2 -2
  82. package/dashboard/dist/assets/{TimeAgo-Dvkw4shy.js.map → TimeAgo-B_um9BWR.js.map} +1 -1
  83. package/dashboard/dist/assets/{TimestampCell-DGEGdbOW.js → TimestampCell-BJ6trAqW.js} +2 -2
  84. package/dashboard/dist/assets/{TimestampCell-DGEGdbOW.js.map → TimestampCell-BJ6trAqW.js.map} +1 -1
  85. package/dashboard/dist/assets/{ToolTestPanel-GY3n1V12.js → ToolTestPanel-DMQhLDES.js} +2 -2
  86. package/dashboard/dist/assets/{ToolTestPanel-GY3n1V12.js.map → ToolTestPanel-DMQhLDES.js.map} +1 -1
  87. package/dashboard/dist/assets/{TopicDetailPage-CGim5yi0.js → TopicDetailPage-YeGQA0vD.js} +2 -2
  88. package/dashboard/dist/assets/{TopicDetailPage-CGim5yi0.js.map → TopicDetailPage-YeGQA0vD.js.map} +1 -1
  89. package/dashboard/dist/assets/{TopicsPage-DLyRlo0A.js → TopicsPage-B3QZNlWz.js} +2 -2
  90. package/dashboard/dist/assets/{TopicsPage-DLyRlo0A.js.map → TopicsPage-B3QZNlWz.js.map} +1 -1
  91. package/dashboard/dist/assets/{UserName-B8dGlxj9.js → UserName-MpSZ2_EH.js} +2 -2
  92. package/dashboard/dist/assets/{UserName-B8dGlxj9.js.map → UserName-MpSZ2_EH.js.map} +1 -1
  93. package/dashboard/dist/assets/{WorkflowExecutionPage-BQbIIdIA.js → WorkflowExecutionPage-DqMqDb1h.js} +2 -2
  94. package/dashboard/dist/assets/{WorkflowExecutionPage-BQbIIdIA.js.map → WorkflowExecutionPage-DqMqDb1h.js.map} +1 -1
  95. package/dashboard/dist/assets/{WorkflowsDashboard-DL6oRbka.js → WorkflowsDashboard-BF7FpMmk.js} +2 -2
  96. package/dashboard/dist/assets/{WorkflowsDashboard-DL6oRbka.js.map → WorkflowsDashboard-BF7FpMmk.js.map} +1 -1
  97. package/dashboard/dist/assets/{WorkflowsOverview-Reab_xHT.js → WorkflowsOverview-YFc_KBMS.js} +2 -2
  98. package/dashboard/dist/assets/{WorkflowsOverview-Reab_xHT.js.map → WorkflowsOverview-YFc_KBMS.js.map} +1 -1
  99. package/dashboard/dist/assets/{YamlWorkflowsPage-BoFstOcp.js → YamlWorkflowsPage-BOLs5KTB.js} +2 -2
  100. package/dashboard/dist/assets/{YamlWorkflowsPage-BoFstOcp.js.map → YamlWorkflowsPage-BOLs5KTB.js.map} +1 -1
  101. package/dashboard/dist/assets/{agents-CQsJU21y.js → agents-CPYVSCQ3.js} +2 -2
  102. package/dashboard/dist/assets/{agents-CQsJU21y.js.map → agents-CPYVSCQ3.js.map} +1 -1
  103. package/dashboard/dist/assets/{bots-t1FPESbm.js → bots-DCXjHjID.js} +2 -2
  104. package/dashboard/dist/assets/{bots-t1FPESbm.js.map → bots-DCXjHjID.js.map} +1 -1
  105. package/dashboard/dist/assets/{capabilities-D1Y3hVvf.js → capabilities-CreogBYU.js} +2 -2
  106. package/dashboard/dist/assets/{capabilities-D1Y3hVvf.js.map → capabilities-CreogBYU.js.map} +1 -1
  107. package/dashboard/dist/assets/{controlplane-CV-y8cfH.js → controlplane-Cm_-Gb1x.js} +2 -2
  108. package/dashboard/dist/assets/{controlplane-CV-y8cfH.js.map → controlplane-Cm_-Gb1x.js.map} +1 -1
  109. package/dashboard/dist/assets/escalation-columns-CLqe28Ba.js +2 -0
  110. package/dashboard/dist/assets/escalation-columns-CLqe28Ba.js.map +1 -0
  111. package/dashboard/dist/assets/{escalation-Bf_SO_75.js → escalation-ulsBFHVb.js} +2 -2
  112. package/dashboard/dist/assets/{escalation-Bf_SO_75.js.map → escalation-ulsBFHVb.js.map} +1 -1
  113. package/dashboard/dist/assets/{helpers-nWSTagzD.js → helpers-etjHeZEI.js} +2 -2
  114. package/dashboard/dist/assets/{helpers-nWSTagzD.js.map → helpers-etjHeZEI.js.map} +1 -1
  115. package/dashboard/dist/assets/{index-BRYCB_g0.js → index-7Fbktqcl.js} +2 -2
  116. package/dashboard/dist/assets/{index-BRYCB_g0.js.map → index-7Fbktqcl.js.map} +1 -1
  117. package/dashboard/dist/assets/{index-CHBiEYmf.js → index-BkCkBW_D.js} +2 -2
  118. package/dashboard/dist/assets/{index-CHBiEYmf.js.map → index-BkCkBW_D.js.map} +1 -1
  119. package/dashboard/dist/assets/{index-B77P0ssX.js → index-BkOv2dQA.js} +2 -2
  120. package/dashboard/dist/assets/{index-B77P0ssX.js.map → index-BkOv2dQA.js.map} +1 -1
  121. package/dashboard/dist/assets/index-C37LMzJa.css +1 -0
  122. package/dashboard/dist/assets/{index-DUaF8VYe.js → index-CKDOaej4.js} +6 -6
  123. package/dashboard/dist/assets/index-CKDOaej4.js.map +1 -0
  124. package/dashboard/dist/assets/{index-Dp8iH4i2.js → index-CcvHiZW-.js} +2 -2
  125. package/dashboard/dist/assets/{index-Dp8iH4i2.js.map → index-CcvHiZW-.js.map} +1 -1
  126. package/dashboard/dist/assets/{index-BVXXvXlF.js → index-CihScSLF.js} +2 -2
  127. package/dashboard/dist/assets/{index-BVXXvXlF.js.map → index-CihScSLF.js.map} +1 -1
  128. package/dashboard/dist/assets/{index-wlL3EZ14.js → index-Cnpo94XG.js} +2 -2
  129. package/dashboard/dist/assets/{index-wlL3EZ14.js.map → index-Cnpo94XG.js.map} +1 -1
  130. package/dashboard/dist/assets/{index-CdUj8mKq.js → index-DFuHrLll.js} +2 -2
  131. package/dashboard/dist/assets/{index-CdUj8mKq.js.map → index-DFuHrLll.js.map} +1 -1
  132. package/dashboard/dist/assets/{index-0i5oHs_4.js → index-DGpIF_Td.js} +2 -2
  133. package/dashboard/dist/assets/{index-0i5oHs_4.js.map → index-DGpIF_Td.js.map} +1 -1
  134. package/dashboard/dist/assets/{index-C42ACUTi.js → index-DT0JeuiL.js} +2 -2
  135. package/dashboard/dist/assets/{index-C42ACUTi.js.map → index-DT0JeuiL.js.map} +1 -1
  136. package/dashboard/dist/assets/{index-MO0YnTPi.js → index-DT68ewTC.js} +2 -2
  137. package/dashboard/dist/assets/{index-MO0YnTPi.js.map → index-DT68ewTC.js.map} +1 -1
  138. package/dashboard/dist/assets/{index-DAQvhgrL.js → index-DVqtJBno.js} +2 -2
  139. package/dashboard/dist/assets/{index-DAQvhgrL.js.map → index-DVqtJBno.js.map} +1 -1
  140. package/dashboard/dist/assets/{index-BZu5zewH.js → index-DYmrNJ_H.js} +4 -4
  141. package/dashboard/dist/assets/index-DYmrNJ_H.js.map +1 -0
  142. package/dashboard/dist/assets/{knowledge-DJhm5z0p.js → knowledge-CXA2DJwY.js} +2 -2
  143. package/dashboard/dist/assets/{knowledge-DJhm5z0p.js.map → knowledge-CXA2DJwY.js.map} +1 -1
  144. package/dashboard/dist/assets/{mcp-DMYXb9fv.js → mcp-DeC-PpeL.js} +2 -2
  145. package/dashboard/dist/assets/{mcp-DMYXb9fv.js.map → mcp-DeC-PpeL.js.map} +1 -1
  146. package/dashboard/dist/assets/{mcp-query-DE-oPOvi.js → mcp-query-DldD_RPZ.js} +2 -2
  147. package/dashboard/dist/assets/{mcp-query-DE-oPOvi.js.map → mcp-query-DldD_RPZ.js.map} +1 -1
  148. package/dashboard/dist/assets/{namespaces-Bjjm4EG1.js → namespaces-BIGZ6exX.js} +2 -2
  149. package/dashboard/dist/assets/{namespaces-Bjjm4EG1.js.map → namespaces-BIGZ6exX.js.map} +1 -1
  150. package/dashboard/dist/assets/{pipelines-C8aRprVr.js → pipelines-BtihifKT.js} +2 -2
  151. package/dashboard/dist/assets/{pipelines-C8aRprVr.js.map → pipelines-BtihifKT.js.map} +1 -1
  152. package/dashboard/dist/assets/{roles-CQsPYJXe.js → roles-4DocbpKy.js} +2 -2
  153. package/dashboard/dist/assets/{roles-CQsPYJXe.js.map → roles-4DocbpKy.js.map} +1 -1
  154. package/dashboard/dist/assets/{tasks-BQ1c7trT.js → tasks-B9P_7SR_.js} +2 -2
  155. package/dashboard/dist/assets/{tasks-BQ1c7trT.js.map → tasks-B9P_7SR_.js.map} +1 -1
  156. package/dashboard/dist/assets/{topics-DIziCjqg.js → topics-CcLT-IrY.js} +2 -2
  157. package/dashboard/dist/assets/{topics-DIziCjqg.js.map → topics-CcLT-IrY.js.map} +1 -1
  158. package/dashboard/dist/assets/{useEventHooks-CPxcH6zx.js → useEventHooks-B9UOxef_.js} +2 -2
  159. package/dashboard/dist/assets/{useEventHooks-CPxcH6zx.js.map → useEventHooks-B9UOxef_.js.map} +1 -1
  160. package/dashboard/dist/assets/{useYamlActivityEvents-DnPywDgy.js → useYamlActivityEvents-V_MENSI5.js} +2 -2
  161. package/dashboard/dist/assets/{useYamlActivityEvents-DnPywDgy.js.map → useYamlActivityEvents-V_MENSI5.js.map} +1 -1
  162. package/dashboard/dist/assets/{users-CMGaVe_B.js → users-BHF3YOU1.js} +2 -2
  163. package/dashboard/dist/assets/{users-CMGaVe_B.js.map → users-BHF3YOU1.js.map} +1 -1
  164. package/dashboard/dist/assets/{workflows-CD7-d5w8.js → workflows-DorgmYSk.js} +2 -2
  165. package/dashboard/dist/assets/{workflows-CD7-d5w8.js.map → workflows-DorgmYSk.js.map} +1 -1
  166. package/dashboard/dist/assets/{yaml-workflows-CIeymjZr.js → yaml-workflows-DTGpqnEG.js} +2 -2
  167. package/dashboard/dist/assets/{yaml-workflows-CIeymjZr.js.map → yaml-workflows-DTGpqnEG.js.map} +1 -1
  168. package/dashboard/dist/index.html +2 -2
  169. package/docs/api/http/escalations.md +96 -0
  170. package/docs/api/sdk/escalations.md +84 -0
  171. package/docs/cli.md +8 -0
  172. package/docs/sdk.md +1 -1
  173. package/package.json +1 -1
  174. package/dashboard/dist/assets/AgentConfigPage-DbqbFXEq.js +0 -16
  175. package/dashboard/dist/assets/AgentConfigPage-DbqbFXEq.js.map +0 -1
  176. package/dashboard/dist/assets/AvailableEscalationsPage-D2cxvpAK.js +0 -2
  177. package/dashboard/dist/assets/AvailableEscalationsPage-D2cxvpAK.js.map +0 -1
  178. package/dashboard/dist/assets/OperatorDashboard-B_QmNzLw.js +0 -2
  179. package/dashboard/dist/assets/OperatorDashboard-B_QmNzLw.js.map +0 -1
  180. package/dashboard/dist/assets/escalation-columns-DgY8c1hM.js +0 -2
  181. package/dashboard/dist/assets/escalation-columns-DgY8c1hM.js.map +0 -1
  182. package/dashboard/dist/assets/index-BZu5zewH.js.map +0 -1
  183. package/dashboard/dist/assets/index-DUaF8VYe.js.map +0 -1
  184. package/dashboard/dist/assets/index-ib-nDwd6.css +0 -1
@@ -7,4 +7,18 @@ export declare function checkBulkPermission(userId: string, ids: string[]): Prom
7
7
  status: 403;
8
8
  error: string;
9
9
  }>;
10
+ /**
11
+ * Resolve an optional assignee external_id to an internal userId.
12
+ * When omitted, returns the caller's userId from auth.
13
+ */
14
+ export declare function resolveAssignee(assignee: string | undefined, auth: {
15
+ userId: string;
16
+ }): Promise<{
17
+ userId: string;
18
+ } | {
19
+ error: {
20
+ status: number;
21
+ error: string;
22
+ };
23
+ }>;
10
24
  export declare function publishBulkClaimEvents(ids: string[], assignedTo: string): void;
@@ -36,6 +36,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.getVisibleRoles = getVisibleRoles;
37
37
  exports.validateIds = validateIds;
38
38
  exports.checkBulkPermission = checkBulkPermission;
39
+ exports.resolveAssignee = resolveAssignee;
39
40
  exports.publishBulkClaimEvents = publishBulkClaimEvents;
40
41
  const escalationService = __importStar(require("../../services/escalation"));
41
42
  const userService = __importStar(require("../../services/user"));
@@ -64,6 +65,19 @@ async function checkBulkPermission(userId, ids) {
64
65
  }
65
66
  return { allowed: true };
66
67
  }
68
+ /**
69
+ * Resolve an optional assignee external_id to an internal userId.
70
+ * When omitted, returns the caller's userId from auth.
71
+ */
72
+ async function resolveAssignee(assignee, auth) {
73
+ if (!assignee)
74
+ return { userId: auth.userId };
75
+ const user = await userService.getUserByExternalId(assignee);
76
+ if (!user) {
77
+ return { error: { status: 404, error: `User not found for external_id: ${assignee}` } };
78
+ }
79
+ return { userId: user.id };
80
+ }
67
81
  function publishBulkClaimEvents(ids, assignedTo) {
68
82
  for (const id of ids) {
69
83
  (0, publish_1.publishEscalationEvent)({
@@ -4,3 +4,4 @@ export { getEscalation, getEscalationsByWorkflowId, escalateToRole } from './sin
4
4
  export { claimEscalation, releaseEscalation } from './claim';
5
5
  export { releaseExpiredClaims, updatePriority, bulkClaim, bulkAssign, bulkEscalate, bulkTriage } from './bulk';
6
6
  export { resolveEscalation } from './resolve';
7
+ export { findByMetadata, claimByMetadata, resolveByMetadata } from './metadata';
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.resolveEscalation = exports.bulkTriage = exports.bulkEscalate = exports.bulkAssign = exports.bulkClaim = exports.updatePriority = exports.releaseExpiredClaims = exports.releaseEscalation = exports.claimEscalation = exports.escalateToRole = exports.getEscalationsByWorkflowId = exports.getEscalation = exports.getEscalationStats = exports.listDistinctTypes = exports.listAvailableEscalations = exports.listEscalations = exports.createEscalation = void 0;
3
+ exports.resolveByMetadata = exports.claimByMetadata = exports.findByMetadata = exports.resolveEscalation = exports.bulkTriage = exports.bulkEscalate = exports.bulkAssign = exports.bulkClaim = exports.updatePriority = exports.releaseExpiredClaims = exports.releaseEscalation = exports.claimEscalation = exports.escalateToRole = exports.getEscalationsByWorkflowId = exports.getEscalation = exports.getEscalationStats = exports.listDistinctTypes = exports.listAvailableEscalations = exports.listEscalations = exports.createEscalation = void 0;
4
4
  var create_1 = require("./create");
5
5
  Object.defineProperty(exports, "createEscalation", { enumerable: true, get: function () { return create_1.createEscalation; } });
6
6
  var list_1 = require("./list");
@@ -24,3 +24,7 @@ Object.defineProperty(exports, "bulkEscalate", { enumerable: true, get: function
24
24
  Object.defineProperty(exports, "bulkTriage", { enumerable: true, get: function () { return bulk_1.bulkTriage; } });
25
25
  var resolve_1 = require("./resolve");
26
26
  Object.defineProperty(exports, "resolveEscalation", { enumerable: true, get: function () { return resolve_1.resolveEscalation; } });
27
+ var metadata_1 = require("./metadata");
28
+ Object.defineProperty(exports, "findByMetadata", { enumerable: true, get: function () { return metadata_1.findByMetadata; } });
29
+ Object.defineProperty(exports, "claimByMetadata", { enumerable: true, get: function () { return metadata_1.claimByMetadata; } });
30
+ Object.defineProperty(exports, "resolveByMetadata", { enumerable: true, get: function () { return metadata_1.resolveByMetadata; } });
@@ -0,0 +1,56 @@
1
+ import type { LTApiAuth, LTApiResult } from '../../types/sdk';
2
+ /**
3
+ * Find escalations by a metadata key-value pair.
4
+ *
5
+ * Uses JSONB containment (`@>`) backed by a GIN index.
6
+ * Results are RBAC-scoped to the caller's visible roles.
7
+ *
8
+ * @param input.key — metadata field name (e.g. `"orderId"`)
9
+ * @param input.value — metadata field value (e.g. `"order-123"`)
10
+ * @param input.status — optional status filter (e.g. `"pending"`)
11
+ * @returns `{ status: 200, data: { escalations, total } }`
12
+ */
13
+ export declare function findByMetadata(input: {
14
+ key: string;
15
+ value: string;
16
+ status?: string;
17
+ limit?: number;
18
+ offset?: number;
19
+ }, auth: LTApiAuth): Promise<LTApiResult>;
20
+ /**
21
+ * Claim an escalation by metadata key-value pair.
22
+ *
23
+ * Finds one available (pending + unassigned/expired) escalation matching
24
+ * the metadata and claims it atomically. Optionally resolves an assignee
25
+ * from an external_id.
26
+ *
27
+ * @param input.key — metadata field name
28
+ * @param input.value — metadata field value
29
+ * @param input.durationMinutes — claim duration (default 30)
30
+ * @param input.assignee — optional external_id of the user to claim as
31
+ * @returns `{ status: 200, data: { escalation, isExtension } }`
32
+ */
33
+ export declare function claimByMetadata(input: {
34
+ key: string;
35
+ value: string;
36
+ durationMinutes?: number;
37
+ assignee?: string;
38
+ }, auth: LTApiAuth): Promise<LTApiResult>;
39
+ /**
40
+ * Resolve an escalation by metadata key-value pair.
41
+ *
42
+ * Finds the pending escalation, auto-claims if unclaimed, then delegates
43
+ * to the standard resolve logic (supports all 5 resolution paths).
44
+ *
45
+ * @param input.key — metadata field name
46
+ * @param input.value — metadata field value
47
+ * @param input.resolverPayload — resolution data for the workflow
48
+ * @param input.assignee — optional external_id of the resolving user
49
+ * @returns result from the standard resolve endpoint
50
+ */
51
+ export declare function resolveByMetadata(input: {
52
+ key: string;
53
+ value: string;
54
+ resolverPayload: Record<string, any>;
55
+ assignee?: string;
56
+ }, auth: LTApiAuth): Promise<LTApiResult>;
@@ -0,0 +1,178 @@
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
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.findByMetadata = findByMetadata;
37
+ exports.claimByMetadata = claimByMetadata;
38
+ exports.resolveByMetadata = resolveByMetadata;
39
+ const escalationService = __importStar(require("../../services/escalation"));
40
+ const userService = __importStar(require("../../services/user"));
41
+ const publish_1 = require("../../lib/events/publish");
42
+ const helpers_1 = require("./helpers");
43
+ /**
44
+ * Find escalations by a metadata key-value pair.
45
+ *
46
+ * Uses JSONB containment (`@>`) backed by a GIN index.
47
+ * Results are RBAC-scoped to the caller's visible roles.
48
+ *
49
+ * @param input.key — metadata field name (e.g. `"orderId"`)
50
+ * @param input.value — metadata field value (e.g. `"order-123"`)
51
+ * @param input.status — optional status filter (e.g. `"pending"`)
52
+ * @returns `{ status: 200, data: { escalations, total } }`
53
+ */
54
+ async function findByMetadata(input, auth) {
55
+ try {
56
+ if (!input.key || !input.value) {
57
+ return { status: 400, error: 'key and value are required' };
58
+ }
59
+ const result = await escalationService.findByMetadata(input.key, input.value, input.status, input.limit, input.offset);
60
+ // RBAC: scope to visible roles
61
+ const visibleRoles = await (0, helpers_1.getVisibleRoles)(auth.userId);
62
+ if (visibleRoles) {
63
+ const roleSet = new Set(visibleRoles);
64
+ result.escalations = result.escalations.filter(e => roleSet.has(e.role));
65
+ result.total = result.escalations.length;
66
+ }
67
+ return { status: 200, data: result };
68
+ }
69
+ catch (err) {
70
+ return { status: 500, error: err.message };
71
+ }
72
+ }
73
+ /**
74
+ * Claim an escalation by metadata key-value pair.
75
+ *
76
+ * Finds one available (pending + unassigned/expired) escalation matching
77
+ * the metadata and claims it atomically. Optionally resolves an assignee
78
+ * from an external_id.
79
+ *
80
+ * @param input.key — metadata field name
81
+ * @param input.value — metadata field value
82
+ * @param input.durationMinutes — claim duration (default 30)
83
+ * @param input.assignee — optional external_id of the user to claim as
84
+ * @returns `{ status: 200, data: { escalation, isExtension } }`
85
+ */
86
+ async function claimByMetadata(input, auth) {
87
+ try {
88
+ if (!input.key || !input.value) {
89
+ return { status: 400, error: 'key and value are required' };
90
+ }
91
+ const resolved = await (0, helpers_1.resolveAssignee)(input.assignee, auth);
92
+ if ('error' in resolved)
93
+ return resolved.error;
94
+ const claimUserId = resolved.userId;
95
+ // RBAC: find the candidate to check role membership before atomic claim
96
+ const candidates = await escalationService.findByMetadata(input.key, input.value, 'pending', 1, 0);
97
+ if (candidates.escalations.length === 0) {
98
+ return { status: 404, error: 'No pending escalation found for this metadata' };
99
+ }
100
+ const candidate = candidates.escalations[0];
101
+ const isSuperAdmin = await userService.isSuperAdmin(auth.userId);
102
+ if (!isSuperAdmin) {
103
+ const userHasRole = await userService.hasRole(claimUserId, candidate.role);
104
+ if (!userHasRole) {
105
+ return { status: 403, error: `User must have the "${candidate.role}" role to claim this escalation` };
106
+ }
107
+ }
108
+ const result = await escalationService.claimByMetadata(input.key, input.value, claimUserId, input.durationMinutes);
109
+ if (!result) {
110
+ return { status: 409, error: 'Escalation not available for claim' };
111
+ }
112
+ (0, publish_1.publishEscalationEvent)({
113
+ type: 'escalation.claimed',
114
+ source: 'api',
115
+ workflowId: result.escalation.workflow_id || '',
116
+ workflowName: result.escalation.workflow_type || '',
117
+ taskQueue: result.escalation.task_queue || '',
118
+ escalationId: result.escalation.id,
119
+ status: 'claimed',
120
+ data: { assigned_to: claimUserId },
121
+ });
122
+ return { status: 200, data: result };
123
+ }
124
+ catch (err) {
125
+ return { status: 500, error: err.message };
126
+ }
127
+ }
128
+ /**
129
+ * Resolve an escalation by metadata key-value pair.
130
+ *
131
+ * Finds the pending escalation, auto-claims if unclaimed, then delegates
132
+ * to the standard resolve logic (supports all 5 resolution paths).
133
+ *
134
+ * @param input.key — metadata field name
135
+ * @param input.value — metadata field value
136
+ * @param input.resolverPayload — resolution data for the workflow
137
+ * @param input.assignee — optional external_id of the resolving user
138
+ * @returns result from the standard resolve endpoint
139
+ */
140
+ async function resolveByMetadata(input, auth) {
141
+ try {
142
+ if (!input.key || !input.value) {
143
+ return { status: 400, error: 'key and value are required' };
144
+ }
145
+ if (!input.resolverPayload) {
146
+ return { status: 400, error: 'resolverPayload is required' };
147
+ }
148
+ const candidates = await escalationService.findByMetadata(input.key, input.value, 'pending', 1, 0);
149
+ if (candidates.escalations.length === 0) {
150
+ return { status: 404, error: 'No pending escalation found for this metadata' };
151
+ }
152
+ const escalation = candidates.escalations[0];
153
+ const resolved = await (0, helpers_1.resolveAssignee)(input.assignee, auth);
154
+ if ('error' in resolved)
155
+ return resolved.error;
156
+ const resolveUserId = resolved.userId;
157
+ const isSuperAdmin = await userService.isSuperAdmin(auth.userId);
158
+ if (!isSuperAdmin) {
159
+ const userHasRole = await userService.hasRole(resolveUserId, escalation.role);
160
+ if (!userHasRole) {
161
+ return { status: 403, error: `User must have the "${escalation.role}" role` };
162
+ }
163
+ }
164
+ // Auto-claim if unclaimed or expired
165
+ const isClaimed = escalation.assigned_to &&
166
+ escalation.assigned_until &&
167
+ new Date(escalation.assigned_until) > new Date();
168
+ if (!isClaimed) {
169
+ await escalationService.claimEscalation(escalation.id, resolveUserId, 5);
170
+ }
171
+ // Delegate to the full resolve logic (handles all 5 resolution paths)
172
+ const { resolveEscalation } = await Promise.resolve().then(() => __importStar(require('./resolve')));
173
+ return resolveEscalation({ id: escalation.id, resolverPayload: input.resolverPayload }, auth);
174
+ }
175
+ catch (err) {
176
+ return { status: 500, error: err.message };
177
+ }
178
+ }
package/build/bin/ltc.js CHANGED
@@ -134,6 +134,23 @@ escCmd.command('release <id>')
134
134
  escCmd.command('resolve <id>')
135
135
  .requiredOption('--data <json>', 'Resolver payload (JSON string)')
136
136
  .action(wrap(esc.resolveEscalation));
137
+ escCmd.command('find-by-meta <key> <value>')
138
+ .description('Find escalations by metadata key-value pair')
139
+ .option('--status <status>', 'Filter by status')
140
+ .option('--limit <n>', 'Max results')
141
+ .option('--json', 'JSON output')
142
+ .option('-q, --quiet', 'IDs only')
143
+ .action(wrap(esc.findByMetadata));
144
+ escCmd.command('claim-by-meta <key> <value>')
145
+ .description('Claim an escalation by metadata key-value pair')
146
+ .option('--duration <minutes>', 'Claim duration in minutes')
147
+ .option('--assignee <external_id>', 'Claim on behalf of user (external_id)')
148
+ .action(wrap(esc.claimByMetadata));
149
+ escCmd.command('resolve-by-meta <key> <value>')
150
+ .description('Resolve an escalation by metadata key-value pair')
151
+ .option('--data <json>', 'Resolver payload (JSON string)')
152
+ .option('--assignee <external_id>', 'Resolve on behalf of user (external_id)')
153
+ .action(wrap(esc.resolveByMetadata));
137
154
  // ── Workflows ────────────────────────────────────────────────────────────
138
155
  const wfCmd = commander_1.program.command('workflows').alias('wf').description('Manage durable workflows');
139
156
  wfCmd.command('list')
@@ -16,4 +16,13 @@ export declare function releaseEscalation(id: string): Promise<void>;
16
16
  export declare function resolveEscalation(id: string, opts: {
17
17
  data?: string;
18
18
  }): Promise<void>;
19
+ export declare function findByMetadata(key: string, value: string, opts: ListOptions): Promise<void>;
20
+ export declare function claimByMetadata(key: string, value: string, opts: {
21
+ duration?: string;
22
+ assignee?: string;
23
+ }): Promise<void>;
24
+ export declare function resolveByMetadata(key: string, value: string, opts: {
25
+ data?: string;
26
+ assignee?: string;
27
+ }): Promise<void>;
19
28
  export {};
@@ -8,6 +8,9 @@ exports.getEscalation = getEscalation;
8
8
  exports.claimEscalation = claimEscalation;
9
9
  exports.releaseEscalation = releaseEscalation;
10
10
  exports.resolveEscalation = resolveEscalation;
11
+ exports.findByMetadata = findByMetadata;
12
+ exports.claimByMetadata = claimByMetadata;
13
+ exports.resolveByMetadata = resolveByMetadata;
11
14
  const picocolors_1 = __importDefault(require("picocolors"));
12
15
  const client_1 = require("../client");
13
16
  const format_1 = require("../format");
@@ -67,3 +70,36 @@ async function resolveEscalation(id, opts) {
67
70
  });
68
71
  console.log(`\n ${picocolors_1.default.green('✓')} Resolved ${picocolors_1.default.dim(id)}\n`);
69
72
  }
73
+ // --- Metadata candidate key commands ----------------------------------------
74
+ async function findByMetadata(key, value, opts) {
75
+ const params = new URLSearchParams({ key, value });
76
+ if (opts.status)
77
+ params.set('status', opts.status);
78
+ if (opts.limit)
79
+ params.set('limit', opts.limit);
80
+ const data = await (0, client_1.apiFetch)(`/escalations/by-metadata?${params}`);
81
+ (0, format_1.output)(data, data.escalations || [], COLUMNS, opts);
82
+ }
83
+ async function claimByMetadata(key, value, opts) {
84
+ const body = { key, value };
85
+ if (opts.duration)
86
+ body.durationMinutes = parseInt(opts.duration, 10);
87
+ if (opts.assignee)
88
+ body.assignee = opts.assignee;
89
+ const data = await (0, client_1.apiFetch)('/escalations/claim-by-metadata', {
90
+ method: 'POST',
91
+ body: JSON.stringify(body),
92
+ });
93
+ console.log(`\n ${picocolors_1.default.green('✓')} Claimed ${picocolors_1.default.dim(data.escalation?.id || '')} by ${key}=${value}\n`);
94
+ }
95
+ async function resolveByMetadata(key, value, opts) {
96
+ const resolverPayload = opts.data ? JSON.parse(opts.data) : {};
97
+ const body = { key, value, resolverPayload };
98
+ if (opts.assignee)
99
+ body.assignee = opts.assignee;
100
+ await (0, client_1.apiFetch)('/escalations/resolve-by-metadata', {
101
+ method: 'POST',
102
+ body: JSON.stringify(body),
103
+ });
104
+ console.log(`\n ${picocolors_1.default.green('✓')} Resolved by ${key}=${value}\n`);
105
+ }
@@ -0,0 +1,5 @@
1
+ -- GIN index for JSONB containment queries on lt_escalations.metadata.
2
+ -- Enables efficient lookups like: WHERE metadata @> '{"orderId":"order-123"}'::jsonb
3
+ -- Uses jsonb_path_ops (smaller, faster for @> queries).
4
+ CREATE INDEX IF NOT EXISTS idx_lt_escalations_metadata
5
+ ON lt_escalations USING GIN (metadata jsonb_path_ops);
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const express_1 = require("express");
4
4
  const list_1 = require("./list");
5
5
  const bulk_1 = require("./bulk");
6
+ const metadata_1 = require("./metadata");
6
7
  const single_1 = require("./single");
7
8
  const resolve_1 = require("./resolve");
8
9
  const router = (0, express_1.Router)();
@@ -12,6 +13,8 @@ const router = (0, express_1.Router)();
12
13
  // POST /release-expired, PATCH /priority, POST /bulk-claim,
13
14
  // POST /bulk-assign, PATCH /bulk-escalate, POST /bulk-triage
14
15
  (0, bulk_1.registerBulkRoutes)(router);
16
+ // GET /by-metadata, POST /claim-by-metadata, POST /resolve-by-metadata
17
+ (0, metadata_1.registerMetadataRoutes)(router);
15
18
  // PATCH /:id/escalate, GET /by-workflow/:workflowId,
16
19
  // GET /:id, POST /:id/claim, POST /:id/release
17
20
  (0, single_1.registerSingleRoutes)(router);
@@ -0,0 +1,6 @@
1
+ import type { Router } from 'express';
2
+ /**
3
+ * Register metadata-based escalation lookup routes.
4
+ * Must be registered BEFORE parameterized /:id routes.
5
+ */
6
+ export declare function registerMetadataRoutes(router: Router): void;
@@ -0,0 +1,86 @@
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
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.registerMetadataRoutes = registerMetadataRoutes;
37
+ const api = __importStar(require("../../api/escalations"));
38
+ /**
39
+ * Register metadata-based escalation lookup routes.
40
+ * Must be registered BEFORE parameterized /:id routes.
41
+ */
42
+ function registerMetadataRoutes(router) {
43
+ /**
44
+ * GET /api/escalations/by-metadata
45
+ * Find escalations by a metadata key-value pair.
46
+ * Query: key, value, status?, limit?, offset?
47
+ */
48
+ router.get('/by-metadata', async (req, res) => {
49
+ const result = await api.findByMetadata({
50
+ key: req.query.key,
51
+ value: req.query.value,
52
+ status: req.query.status,
53
+ limit: req.query.limit ? parseInt(req.query.limit, 10) : undefined,
54
+ offset: req.query.offset ? parseInt(req.query.offset, 10) : undefined,
55
+ }, req.auth);
56
+ res.status(result.status).json(result.data ?? { error: result.error });
57
+ });
58
+ /**
59
+ * POST /api/escalations/claim-by-metadata
60
+ * Find and claim an escalation by metadata key-value pair.
61
+ * Body: { key, value, durationMinutes?, assignee? }
62
+ */
63
+ router.post('/claim-by-metadata', async (req, res) => {
64
+ const result = await api.claimByMetadata({
65
+ key: req.body?.key,
66
+ value: req.body?.value,
67
+ durationMinutes: req.body?.durationMinutes,
68
+ assignee: req.body?.assignee,
69
+ }, req.auth);
70
+ res.status(result.status).json(result.data ?? { error: result.error });
71
+ });
72
+ /**
73
+ * POST /api/escalations/resolve-by-metadata
74
+ * Find and resolve an escalation by metadata key-value pair.
75
+ * Body: { key, value, resolverPayload, assignee? }
76
+ */
77
+ router.post('/resolve-by-metadata', async (req, res) => {
78
+ const result = await api.resolveByMetadata({
79
+ key: req.body?.key,
80
+ value: req.body?.value,
81
+ resolverPayload: req.body?.resolverPayload,
82
+ assignee: req.body?.assignee,
83
+ }, req.auth);
84
+ res.status(result.status).json(result.data ?? { error: result.error });
85
+ });
86
+ }
@@ -150,6 +150,25 @@ export declare function createClient(options?: LTClientOptions): {
150
150
  ids: string[];
151
151
  hint?: string;
152
152
  }, auth?: LTApiAuth) => Promise<LTApiResult<any>>;
153
+ findByMetadata: (input: {
154
+ key: string;
155
+ value: string;
156
+ status?: string;
157
+ limit?: number;
158
+ offset?: number;
159
+ }, auth?: LTApiAuth) => Promise<LTApiResult<any>>;
160
+ claimByMetadata: (input: {
161
+ key: string;
162
+ value: string;
163
+ durationMinutes?: number;
164
+ assignee?: string;
165
+ }, auth?: LTApiAuth) => Promise<LTApiResult<any>>;
166
+ resolveByMetadata: (input: {
167
+ key: string;
168
+ value: string;
169
+ resolverPayload: Record<string, any>;
170
+ assignee?: string;
171
+ }, auth?: LTApiAuth) => Promise<LTApiResult<any>>;
153
172
  };
154
173
  workflows: {
155
174
  invoke: (input: {
@@ -130,6 +130,9 @@ function createClient(options = {}) {
130
130
  bulkAssign: bindAuth(escalationsApi.bulkAssign, auth),
131
131
  bulkEscalate: bindAuth(escalationsApi.bulkEscalate, auth),
132
132
  bulkTriage: bindAuth(escalationsApi.bulkTriage, auth),
133
+ findByMetadata: bindAuth(escalationsApi.findByMetadata, auth),
134
+ claimByMetadata: bindAuth(escalationsApi.claimByMetadata, auth),
135
+ resolveByMetadata: bindAuth(escalationsApi.resolveByMetadata, auth),
133
136
  },
134
137
  // ── Workflows ──────────────────────────────────────────────────────────
135
138
  workflows: {
@@ -45,3 +45,8 @@ export declare function enrichEscalationRouting(id: string, metadataPatch: Recor
45
45
  taskId?: string;
46
46
  }): Promise<LTEscalationRecord | null>;
47
47
  export declare function getEscalationsByOriginId(originId: string): Promise<LTEscalationRecord[]>;
48
+ export declare function findByMetadata(key: string, value: string, status?: string, limit?: number, offset?: number): Promise<{
49
+ escalations: LTEscalationRecord[];
50
+ total: number;
51
+ }>;
52
+ export declare function claimByMetadata(key: string, value: string, userId: string, durationMinutes?: number): Promise<ClaimResult | null>;
@@ -14,6 +14,8 @@ exports.getEscalationsByWorkflowId = getEscalationsByWorkflowId;
14
14
  exports.updateEscalationMetadata = updateEscalationMetadata;
15
15
  exports.enrichEscalationRouting = enrichEscalationRouting;
16
16
  exports.getEscalationsByOriginId = getEscalationsByOriginId;
17
+ exports.findByMetadata = findByMetadata;
18
+ exports.claimByMetadata = claimByMetadata;
17
19
  const db_1 = require("../../lib/db");
18
20
  const sql_1 = require("./sql");
19
21
  async function createEscalation(input) {
@@ -148,3 +150,28 @@ async function getEscalationsByOriginId(originId) {
148
150
  const { rows } = await pool.query(sql_1.GET_ESCALATIONS_BY_ORIGIN_ID, [originId]);
149
151
  return rows;
150
152
  }
153
+ // --- Metadata candidate key lookups -----------------------------------------
154
+ async function findByMetadata(key, value, status, limit = 50, offset = 0) {
155
+ const pool = (0, db_1.getPool)();
156
+ const filter = JSON.stringify({ [key]: value });
157
+ const [countResult, dataResult] = await Promise.all([
158
+ pool.query(sql_1.COUNT_BY_METADATA, [filter, status || null]),
159
+ pool.query(sql_1.FIND_BY_METADATA, [filter, status || null, limit, offset]),
160
+ ]);
161
+ return {
162
+ escalations: dataResult.rows,
163
+ total: parseInt(countResult.rows[0].count, 10),
164
+ };
165
+ }
166
+ async function claimByMetadata(key, value, userId, durationMinutes = 30) {
167
+ const pool = (0, db_1.getPool)();
168
+ const filter = JSON.stringify({ [key]: value });
169
+ const { rows } = await pool.query(sql_1.CLAIM_BY_METADATA, [filter, userId, durationMinutes]);
170
+ if (rows.length === 0)
171
+ return null;
172
+ const row = rows[0];
173
+ return {
174
+ escalation: row,
175
+ isExtension: row.prev_assigned_to === userId,
176
+ };
177
+ }
@@ -19,3 +19,8 @@ export declare const BULK_RESOLVE_FOR_TRIAGE = "UPDATE lt_escalations\nSET statu
19
19
  export declare const UPDATE_ESCALATION_METADATA = "UPDATE lt_escalations\nSET metadata = COALESCE(metadata, '{}'::jsonb) || $2::jsonb,\n updated_at = NOW()\nWHERE id = $1\nRETURNING *";
20
20
  export declare const ENRICH_ESCALATION_ROUTING = "UPDATE lt_escalations\nSET metadata = COALESCE(metadata, '{}'::jsonb) || $2::jsonb,\n workflow_type = COALESCE(workflow_type, $3),\n workflow_id = COALESCE(workflow_id, $4),\n task_queue = COALESCE(task_queue, $5),\n task_id = COALESCE(task_id, $6),\n updated_at = NOW()\nWHERE id = $1\nRETURNING *";
21
21
  export declare const LIST_DISTINCT_TYPES = "SELECT DISTINCT type FROM lt_escalations ORDER BY type";
22
+ /** Find escalations by a single metadata key-value pair. */
23
+ export declare const FIND_BY_METADATA = "SELECT * FROM lt_escalations\nWHERE metadata @> $1::jsonb\n AND ($2::text IS NULL OR status = $2)\nORDER BY priority ASC, created_at ASC\nLIMIT $3 OFFSET $4";
24
+ export declare const COUNT_BY_METADATA = "SELECT COUNT(*) FROM lt_escalations\nWHERE metadata @> $1::jsonb\n AND ($2::text IS NULL OR status = $2)";
25
+ /** Atomic claim by metadata: find one available escalation and claim it. */
26
+ export declare const CLAIM_BY_METADATA = "WITH target AS (\n SELECT id, assigned_to\n FROM lt_escalations\n WHERE metadata @> $1::jsonb\n AND status = 'pending'\n AND (\n assigned_to IS NULL\n OR assigned_until <= NOW()\n OR assigned_to = $2\n )\n ORDER BY priority ASC, created_at ASC\n LIMIT 1\n FOR UPDATE SKIP LOCKED\n),\nupdated AS (\n UPDATE lt_escalations e\n SET assigned_to = $2,\n claimed_at = NOW(),\n assigned_until = NOW() + INTERVAL '1 minute' * $3\n FROM target t\n WHERE e.id = t.id\n RETURNING e.*, t.assigned_to AS prev_assigned_to\n)\nSELECT * FROM updated";
@@ -3,7 +3,7 @@
3
3
  // Escalation SQL – externalized from crud.ts, bulk.ts, queries.ts
4
4
  // ---------------------------------------------------------------------------
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.LIST_DISTINCT_TYPES = exports.ENRICH_ESCALATION_ROUTING = exports.UPDATE_ESCALATION_METADATA = exports.BULK_RESOLVE_FOR_TRIAGE = exports.BULK_ESCALATE_TO_ROLE = exports.BULK_ASSIGN = exports.BULK_CLAIM = exports.GET_ESCALATIONS_BY_ORIGIN_ID = exports.GET_ESCALATIONS_BY_WORKFLOW_ID = exports.GET_ESCALATIONS_BY_TASK_ID = exports.GET_ESCALATION = exports.ESCALATE_TO_ROLE = exports.RELEASE_EXPIRED_CLAIMS = exports.RELEASE_ESCALATION = exports.GET_ESCALATION_ROLES = exports.UPDATE_ESCALATIONS_PRIORITY = exports.RESOLVE_ESCALATION = exports.CLAIM_ESCALATION = exports.CREATE_ESCALATION = exports.ENSURE_ROLE_EXISTS = void 0;
6
+ exports.CLAIM_BY_METADATA = exports.COUNT_BY_METADATA = exports.FIND_BY_METADATA = exports.LIST_DISTINCT_TYPES = exports.ENRICH_ESCALATION_ROUTING = exports.UPDATE_ESCALATION_METADATA = exports.BULK_RESOLVE_FOR_TRIAGE = exports.BULK_ESCALATE_TO_ROLE = exports.BULK_ASSIGN = exports.BULK_CLAIM = exports.GET_ESCALATIONS_BY_ORIGIN_ID = exports.GET_ESCALATIONS_BY_WORKFLOW_ID = exports.GET_ESCALATIONS_BY_TASK_ID = exports.GET_ESCALATION = exports.ESCALATE_TO_ROLE = exports.RELEASE_EXPIRED_CLAIMS = exports.RELEASE_ESCALATION = exports.GET_ESCALATION_ROLES = exports.UPDATE_ESCALATIONS_PRIORITY = exports.RESOLVE_ESCALATION = exports.CLAIM_ESCALATION = exports.CREATE_ESCALATION = exports.ENSURE_ROLE_EXISTS = void 0;
7
7
  // --- Role management -------------------------------------------------------
8
8
  exports.ENSURE_ROLE_EXISTS = 'INSERT INTO lt_roles (role) VALUES ($1) ON CONFLICT DO NOTHING';
9
9
  // --- Single-record CRUD ---------------------------------------------------
@@ -133,3 +133,41 @@ SET metadata = COALESCE(metadata, '{}'::jsonb) || $2::jsonb,
133
133
  WHERE id = $1
134
134
  RETURNING *`;
135
135
  exports.LIST_DISTINCT_TYPES = 'SELECT DISTINCT type FROM lt_escalations ORDER BY type';
136
+ // --- Metadata candidate key lookups -----------------------------------------
137
+ /** Find escalations by a single metadata key-value pair. */
138
+ exports.FIND_BY_METADATA = `\
139
+ SELECT * FROM lt_escalations
140
+ WHERE metadata @> $1::jsonb
141
+ AND ($2::text IS NULL OR status = $2)
142
+ ORDER BY priority ASC, created_at ASC
143
+ LIMIT $3 OFFSET $4`;
144
+ exports.COUNT_BY_METADATA = `\
145
+ SELECT COUNT(*) FROM lt_escalations
146
+ WHERE metadata @> $1::jsonb
147
+ AND ($2::text IS NULL OR status = $2)`;
148
+ /** Atomic claim by metadata: find one available escalation and claim it. */
149
+ exports.CLAIM_BY_METADATA = `\
150
+ WITH target AS (
151
+ SELECT id, assigned_to
152
+ FROM lt_escalations
153
+ WHERE metadata @> $1::jsonb
154
+ AND status = 'pending'
155
+ AND (
156
+ assigned_to IS NULL
157
+ OR assigned_until <= NOW()
158
+ OR assigned_to = $2
159
+ )
160
+ ORDER BY priority ASC, created_at ASC
161
+ LIMIT 1
162
+ FOR UPDATE SKIP LOCKED
163
+ ),
164
+ updated AS (
165
+ UPDATE lt_escalations e
166
+ SET assigned_to = $2,
167
+ claimed_at = NOW(),
168
+ assigned_until = NOW() + INTERVAL '1 minute' * $3
169
+ FROM target t
170
+ WHERE e.id = t.id
171
+ RETURNING e.*, t.assigned_to AS prev_assigned_to
172
+ )
173
+ SELECT * FROM updated`;