@hotmeshio/long-tail 0.4.1 → 0.4.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (164) hide show
  1. package/build/services/export/index.js +36 -2
  2. package/build/services/iam/activity.d.ts +4 -3
  3. package/build/services/iam/activity.js +5 -5
  4. package/dashboard/dist/assets/{AdminDashboard-C0_qN2h3.js → AdminDashboard-Dr5wTIZT.js} +2 -2
  5. package/dashboard/dist/assets/{AdminDashboard-C0_qN2h3.js.map → AdminDashboard-Dr5wTIZT.js.map} +1 -1
  6. package/dashboard/dist/assets/{AgentConfigPage-DG1zOIiz.js → AgentConfigPage-D52KyJY-.js} +2 -2
  7. package/dashboard/dist/assets/{AgentConfigPage-DG1zOIiz.js.map → AgentConfigPage-D52KyJY-.js.map} +1 -1
  8. package/dashboard/dist/assets/{AgentDetailPage-B5kaAJDM.js → AgentDetailPage-Cbo_qrYF.js} +2 -2
  9. package/dashboard/dist/assets/{AgentDetailPage-B5kaAJDM.js.map → AgentDetailPage-Cbo_qrYF.js.map} +1 -1
  10. package/dashboard/dist/assets/{AgentsPage-DJWSuoJA.js → AgentsPage-CvuF8--H.js} +2 -2
  11. package/dashboard/dist/assets/{AgentsPage-DJWSuoJA.js.map → AgentsPage-CvuF8--H.js.map} +1 -1
  12. package/dashboard/dist/assets/{AvailableEscalationsPage-DrarbHov.js → AvailableEscalationsPage-UzjXcO3W.js} +2 -2
  13. package/dashboard/dist/assets/{AvailableEscalationsPage-DrarbHov.js.map → AvailableEscalationsPage-UzjXcO3W.js.map} +1 -1
  14. package/dashboard/dist/assets/{BotPicker-CvXQwE5Z.js → BotPicker-CM-_u73k.js} +2 -2
  15. package/dashboard/dist/assets/{BotPicker-CvXQwE5Z.js.map → BotPicker-CM-_u73k.js.map} +1 -1
  16. package/dashboard/dist/assets/{CapabilitiesPage-BiL9QUxI.js → CapabilitiesPage-D1QEHMKw.js} +2 -2
  17. package/dashboard/dist/assets/{CapabilitiesPage-BiL9QUxI.js.map → CapabilitiesPage-D1QEHMKw.js.map} +1 -1
  18. package/dashboard/dist/assets/{CollapsibleSection-D9F01Tny.js → CollapsibleSection-CnPKa7df.js} +2 -2
  19. package/dashboard/dist/assets/{CollapsibleSection-D9F01Tny.js.map → CollapsibleSection-CnPKa7df.js.map} +1 -1
  20. package/dashboard/dist/assets/{CredentialsPage-C-rjAIK3.js → CredentialsPage-CImIzra4.js} +2 -2
  21. package/dashboard/dist/assets/{CredentialsPage-C-rjAIK3.js.map → CredentialsPage-CImIzra4.js.map} +1 -1
  22. package/dashboard/dist/assets/{CronLabel-DnZF8_vw.js → CronLabel-5HPAmciI.js} +2 -2
  23. package/dashboard/dist/assets/{CronLabel-DnZF8_vw.js.map → CronLabel-5HPAmciI.js.map} +1 -1
  24. package/dashboard/dist/assets/{CustomDurationPicker-BYDrcsYT.js → CustomDurationPicker-5JzEgS7I.js} +2 -2
  25. package/dashboard/dist/assets/{CustomDurationPicker-BYDrcsYT.js.map → CustomDurationPicker-5JzEgS7I.js.map} +1 -1
  26. package/dashboard/dist/assets/{ElapsedCell-BkiVdGaJ.js → ElapsedCell-B79BF5Mj.js} +2 -2
  27. package/dashboard/dist/assets/{ElapsedCell-BkiVdGaJ.js.map → ElapsedCell-B79BF5Mj.js.map} +1 -1
  28. package/dashboard/dist/assets/{EscalationsOverview-Cg2SN0WK.js → EscalationsOverview-QvWvbpx3.js} +2 -2
  29. package/dashboard/dist/assets/{EscalationsOverview-Cg2SN0WK.js.map → EscalationsOverview-QvWvbpx3.js.map} +1 -1
  30. package/dashboard/dist/assets/EventTable-DAclQwfr.js +2 -0
  31. package/dashboard/dist/assets/EventTable-DAclQwfr.js.map +1 -0
  32. package/dashboard/dist/assets/{HomePage-74mCQ5nB.js → HomePage-DVOi7rR1.js} +2 -2
  33. package/dashboard/dist/assets/{HomePage-74mCQ5nB.js.map → HomePage-DVOi7rR1.js.map} +1 -1
  34. package/dashboard/dist/assets/{ListToolbar-DL1wEuvL.js → ListToolbar-Bntl2hex.js} +2 -2
  35. package/dashboard/dist/assets/{ListToolbar-DL1wEuvL.js.map → ListToolbar-Bntl2hex.js.map} +1 -1
  36. package/dashboard/dist/assets/{McpOverview-D34bLMuP.js → McpOverview-B-xSYPJj.js} +2 -2
  37. package/dashboard/dist/assets/{McpOverview-D34bLMuP.js.map → McpOverview-B-xSYPJj.js.map} +1 -1
  38. package/dashboard/dist/assets/{McpQueryDetailPage-gLGTGX6g.js → McpQueryDetailPage-Cq71BzjB.js} +2 -2
  39. package/dashboard/dist/assets/{McpQueryDetailPage-gLGTGX6g.js.map → McpQueryDetailPage-Cq71BzjB.js.map} +1 -1
  40. package/dashboard/dist/assets/{McpQueryPage-wPHJkhEp.js → McpQueryPage-Dfz87aZF.js} +2 -2
  41. package/dashboard/dist/assets/{McpQueryPage-wPHJkhEp.js.map → McpQueryPage-Dfz87aZF.js.map} +1 -1
  42. package/dashboard/dist/assets/McpRunDetailPage-CXXRgMmJ.js +2 -0
  43. package/dashboard/dist/assets/{McpRunDetailPage-SoXudCbq.js.map → McpRunDetailPage-CXXRgMmJ.js.map} +1 -1
  44. package/dashboard/dist/assets/{McpRunsPage-BUSxdydO.js → McpRunsPage-MUYvUYqz.js} +2 -2
  45. package/dashboard/dist/assets/{McpRunsPage-BUSxdydO.js.map → McpRunsPage-MUYvUYqz.js.map} +1 -1
  46. package/dashboard/dist/assets/{OperatorDashboard-CYCl2our.js → OperatorDashboard-Rih1SZrn.js} +2 -2
  47. package/dashboard/dist/assets/{OperatorDashboard-CYCl2our.js.map → OperatorDashboard-Rih1SZrn.js.map} +1 -1
  48. package/dashboard/dist/assets/{ProcessDetailPage-DzGacZpO.js → ProcessDetailPage-BMkWCjqD.js} +2 -2
  49. package/dashboard/dist/assets/{ProcessDetailPage-DzGacZpO.js.map → ProcessDetailPage-BMkWCjqD.js.map} +1 -1
  50. package/dashboard/dist/assets/{ProcessesListPage-Bmn33g_l.js → ProcessesListPage-vZjUSc7S.js} +2 -2
  51. package/dashboard/dist/assets/{ProcessesListPage-Bmn33g_l.js.map → ProcessesListPage-vZjUSc7S.js.map} +1 -1
  52. package/dashboard/dist/assets/{RolesPage-BTtaabkS.js → RolesPage-kjeAsj3_.js} +2 -2
  53. package/dashboard/dist/assets/{RolesPage-BTtaabkS.js.map → RolesPage-kjeAsj3_.js.map} +1 -1
  54. package/dashboard/dist/assets/{RunAsSelector-B1R8nJvE.js → RunAsSelector-DC4SLtTq.js} +2 -2
  55. package/dashboard/dist/assets/{RunAsSelector-B1R8nJvE.js.map → RunAsSelector-DC4SLtTq.js.map} +1 -1
  56. package/dashboard/dist/assets/{SwimlaneTimeline-CU2ZD9cC.js → SwimlaneTimeline-MUUXgT3o.js} +2 -2
  57. package/dashboard/dist/assets/{SwimlaneTimeline-CU2ZD9cC.js.map → SwimlaneTimeline-MUUXgT3o.js.map} +1 -1
  58. package/dashboard/dist/assets/{TaskDetailPage-CZw_F8y4.js → TaskDetailPage-C0AlG_2t.js} +2 -2
  59. package/dashboard/dist/assets/{TaskDetailPage-CZw_F8y4.js.map → TaskDetailPage-C0AlG_2t.js.map} +1 -1
  60. package/dashboard/dist/assets/{TasksListPage-D-vHndyV.js → TasksListPage-69ZWCaCP.js} +2 -2
  61. package/dashboard/dist/assets/{TasksListPage-D-vHndyV.js.map → TasksListPage-69ZWCaCP.js.map} +1 -1
  62. package/dashboard/dist/assets/{TimeAgo-B6Gz4RAU.js → TimeAgo-CttiZG0k.js} +2 -2
  63. package/dashboard/dist/assets/{TimeAgo-B6Gz4RAU.js.map → TimeAgo-CttiZG0k.js.map} +1 -1
  64. package/dashboard/dist/assets/{TimestampCell-DZu9PtN2.js → TimestampCell-CYFbEhqc.js} +2 -2
  65. package/dashboard/dist/assets/{TimestampCell-DZu9PtN2.js.map → TimestampCell-CYFbEhqc.js.map} +1 -1
  66. package/dashboard/dist/assets/{ToolTestPanel-D4cgYW2p.js → ToolTestPanel-CY_PLZSe.js} +2 -2
  67. package/dashboard/dist/assets/{ToolTestPanel-D4cgYW2p.js.map → ToolTestPanel-CY_PLZSe.js.map} +1 -1
  68. package/dashboard/dist/assets/{TopicDetailPage-C5ZUZpU5.js → TopicDetailPage-Aq4-6GLl.js} +2 -2
  69. package/dashboard/dist/assets/{TopicDetailPage-C5ZUZpU5.js.map → TopicDetailPage-Aq4-6GLl.js.map} +1 -1
  70. package/dashboard/dist/assets/{TopicsPage-Cbc0nVj9.js → TopicsPage-BYFKjRY_.js} +2 -2
  71. package/dashboard/dist/assets/{TopicsPage-Cbc0nVj9.js.map → TopicsPage-BYFKjRY_.js.map} +1 -1
  72. package/dashboard/dist/assets/{UserName-YUoNrFAq.js → UserName-C4_T5-rI.js} +2 -2
  73. package/dashboard/dist/assets/{UserName-YUoNrFAq.js.map → UserName-C4_T5-rI.js.map} +1 -1
  74. package/dashboard/dist/assets/WorkflowExecutionPage-DUSTBasv.js +2 -0
  75. package/dashboard/dist/assets/WorkflowExecutionPage-DUSTBasv.js.map +1 -0
  76. package/dashboard/dist/assets/{WorkflowsDashboard-BFzCyvgv.js → WorkflowsDashboard-_LMWc-OC.js} +2 -2
  77. package/dashboard/dist/assets/{WorkflowsDashboard-BFzCyvgv.js.map → WorkflowsDashboard-_LMWc-OC.js.map} +1 -1
  78. package/dashboard/dist/assets/{WorkflowsOverview-C_y7JCg2.js → WorkflowsOverview-DFrfw554.js} +2 -2
  79. package/dashboard/dist/assets/{WorkflowsOverview-C_y7JCg2.js.map → WorkflowsOverview-DFrfw554.js.map} +1 -1
  80. package/dashboard/dist/assets/{YamlWorkflowsPage-CYQjp3JI.js → YamlWorkflowsPage-CZzGwfol.js} +2 -2
  81. package/dashboard/dist/assets/{YamlWorkflowsPage-CYQjp3JI.js.map → YamlWorkflowsPage-CZzGwfol.js.map} +1 -1
  82. package/dashboard/dist/assets/{agents-2pDPv2Ww.js → agents-CsKILVSU.js} +2 -2
  83. package/dashboard/dist/assets/{agents-2pDPv2Ww.js.map → agents-CsKILVSU.js.map} +1 -1
  84. package/dashboard/dist/assets/{bots-2uGZ2l7A.js → bots-BzEs6Q9L.js} +2 -2
  85. package/dashboard/dist/assets/{bots-2uGZ2l7A.js.map → bots-BzEs6Q9L.js.map} +1 -1
  86. package/dashboard/dist/assets/{controlplane-CQ29M7lK.js → controlplane-COYEIIAz.js} +2 -2
  87. package/dashboard/dist/assets/{controlplane-CQ29M7lK.js.map → controlplane-COYEIIAz.js.map} +1 -1
  88. package/dashboard/dist/assets/{escalation-DWOUjrgL.js → escalation-BZjS2202.js} +2 -2
  89. package/dashboard/dist/assets/{escalation-DWOUjrgL.js.map → escalation-BZjS2202.js.map} +1 -1
  90. package/dashboard/dist/assets/{escalation-columns-WrgLIl-W.js → escalation-columns-DtRVmPSB.js} +2 -2
  91. package/dashboard/dist/assets/{escalation-columns-WrgLIl-W.js.map → escalation-columns-DtRVmPSB.js.map} +1 -1
  92. package/dashboard/dist/assets/{helpers-LN5b1KBx.js → helpers-BngVEys5.js} +2 -2
  93. package/dashboard/dist/assets/{helpers-LN5b1KBx.js.map → helpers-BngVEys5.js.map} +1 -1
  94. package/dashboard/dist/assets/{index-C_A76Dl1.js → index-ABsZZf_U.js} +2 -2
  95. package/dashboard/dist/assets/{index-C_A76Dl1.js.map → index-ABsZZf_U.js.map} +1 -1
  96. package/dashboard/dist/assets/{index-DqFfgd-7.js → index-BCsShN2l.js} +2 -2
  97. package/dashboard/dist/assets/{index-DqFfgd-7.js.map → index-BCsShN2l.js.map} +1 -1
  98. package/dashboard/dist/assets/{index-CdNXBj7w.js → index-Bq5MSkCL.js} +2 -2
  99. package/dashboard/dist/assets/{index-CdNXBj7w.js.map → index-Bq5MSkCL.js.map} +1 -1
  100. package/dashboard/dist/assets/{index-BMo7wCw8.js → index-BwivT39P.js} +2 -2
  101. package/dashboard/dist/assets/{index-BMo7wCw8.js.map → index-BwivT39P.js.map} +1 -1
  102. package/dashboard/dist/assets/{index-J0dMfAmE.js → index-Byp8BDPs.js} +2 -2
  103. package/dashboard/dist/assets/{index-J0dMfAmE.js.map → index-Byp8BDPs.js.map} +1 -1
  104. package/dashboard/dist/assets/{index-dzxsXeMO.js → index-ByxH4qQ-.js} +2 -2
  105. package/dashboard/dist/assets/{index-dzxsXeMO.js.map → index-ByxH4qQ-.js.map} +1 -1
  106. package/dashboard/dist/assets/index-COgyD_H2.js +2 -0
  107. package/dashboard/dist/assets/{index-CryoNbg0.js.map → index-COgyD_H2.js.map} +1 -1
  108. package/dashboard/dist/assets/index-DXEYynKO.css +1 -0
  109. package/dashboard/dist/assets/{index-CBS8FBcp.js → index-DcpCR9c_.js} +3 -3
  110. package/dashboard/dist/assets/{index-CBS8FBcp.js.map → index-DcpCR9c_.js.map} +1 -1
  111. package/dashboard/dist/assets/index-DuPY59Yu.js +5 -0
  112. package/dashboard/dist/assets/index-DuPY59Yu.js.map +1 -0
  113. package/dashboard/dist/assets/{index-ugekH3E2.js → index-DxMNiFPS.js} +2 -2
  114. package/dashboard/dist/assets/{index-ugekH3E2.js.map → index-DxMNiFPS.js.map} +1 -1
  115. package/dashboard/dist/assets/{index-CovZsMow.js → index-DykjJxzr.js} +2 -2
  116. package/dashboard/dist/assets/{index-CovZsMow.js.map → index-DykjJxzr.js.map} +1 -1
  117. package/dashboard/dist/assets/{index-CFJc47B8.js → index-R61-yG56.js} +2 -2
  118. package/dashboard/dist/assets/{index-CFJc47B8.js.map → index-R61-yG56.js.map} +1 -1
  119. package/dashboard/dist/assets/{index-CvzfRxnj.js → index-xgl431mG.js} +2 -2
  120. package/dashboard/dist/assets/{index-CvzfRxnj.js.map → index-xgl431mG.js.map} +1 -1
  121. package/dashboard/dist/assets/{knowledge-BhZk3Wy9.js → knowledge-DhCbDgy4.js} +2 -2
  122. package/dashboard/dist/assets/{knowledge-BhZk3Wy9.js.map → knowledge-DhCbDgy4.js.map} +1 -1
  123. package/dashboard/dist/assets/{mcp-query-BoNH5uCn.js → mcp-query-il3CfU3U.js} +2 -2
  124. package/dashboard/dist/assets/{mcp-query-BoNH5uCn.js.map → mcp-query-il3CfU3U.js.map} +1 -1
  125. package/dashboard/dist/assets/{mcp-runs-BhkGaw2y.js → mcp-runs-4fyRpegc.js} +2 -2
  126. package/dashboard/dist/assets/{mcp-runs-BhkGaw2y.js.map → mcp-runs-4fyRpegc.js.map} +1 -1
  127. package/dashboard/dist/assets/{mcp-FOHNY7Zj.js → mcp-xh7T0I-f.js} +2 -2
  128. package/dashboard/dist/assets/{mcp-FOHNY7Zj.js.map → mcp-xh7T0I-f.js.map} +1 -1
  129. package/dashboard/dist/assets/{namespaces-duQCQRHq.js → namespaces-BBTvHnRF.js} +2 -2
  130. package/dashboard/dist/assets/{namespaces-duQCQRHq.js.map → namespaces-BBTvHnRF.js.map} +1 -1
  131. package/dashboard/dist/assets/{roles-DW6lI_g5.js → roles-D7bx5FAM.js} +2 -2
  132. package/dashboard/dist/assets/{roles-DW6lI_g5.js.map → roles-D7bx5FAM.js.map} +1 -1
  133. package/dashboard/dist/assets/{settings-wTRbazzw.js → settings-nt6qyR1S.js} +2 -2
  134. package/dashboard/dist/assets/{settings-wTRbazzw.js.map → settings-nt6qyR1S.js.map} +1 -1
  135. package/dashboard/dist/assets/{tasks-C-QX245z.js → tasks-BgxRbhVM.js} +2 -2
  136. package/dashboard/dist/assets/{tasks-C-QX245z.js.map → tasks-BgxRbhVM.js.map} +1 -1
  137. package/dashboard/dist/assets/{topics-CAnsyo3w.js → topics-CTtCboHe.js} +2 -2
  138. package/dashboard/dist/assets/{topics-CAnsyo3w.js.map → topics-CTtCboHe.js.map} +1 -1
  139. package/dashboard/dist/assets/useCollapsedSections-BU5HULGs.js +2 -0
  140. package/dashboard/dist/assets/useCollapsedSections-BU5HULGs.js.map +1 -0
  141. package/dashboard/dist/assets/{useEventHooks-C689a4F7.js → useEventHooks-CkJOmbF-.js} +2 -2
  142. package/dashboard/dist/assets/{useEventHooks-C689a4F7.js.map → useEventHooks-CkJOmbF-.js.map} +1 -1
  143. package/dashboard/dist/assets/{useYamlActivityEvents-BTHFGIsD.js → useYamlActivityEvents-CSMX9He5.js} +2 -2
  144. package/dashboard/dist/assets/{useYamlActivityEvents-BTHFGIsD.js.map → useYamlActivityEvents-CSMX9He5.js.map} +1 -1
  145. package/dashboard/dist/assets/{users--D3LoFOD.js → users-CyF-5WLI.js} +2 -2
  146. package/dashboard/dist/assets/{users--D3LoFOD.js.map → users-CyF-5WLI.js.map} +1 -1
  147. package/dashboard/dist/assets/{workflows-MpNdzreD.js → workflows-Bf4_w24H.js} +2 -2
  148. package/dashboard/dist/assets/{workflows-MpNdzreD.js.map → workflows-Bf4_w24H.js.map} +1 -1
  149. package/dashboard/dist/assets/{yaml-workflows-CFhnJzQy.js → yaml-workflows-Cp1N0NMK.js} +2 -2
  150. package/dashboard/dist/assets/{yaml-workflows-CFhnJzQy.js.map → yaml-workflows-Cp1N0NMK.js.map} +1 -1
  151. package/dashboard/dist/index.html +2 -2
  152. package/docs/agents.md +29 -1
  153. package/docs/events.md +7 -0
  154. package/docs/hitl-guide.md +560 -0
  155. package/package.json +2 -2
  156. package/dashboard/dist/assets/EventTable-B9wYf13g.js +0 -2
  157. package/dashboard/dist/assets/EventTable-B9wYf13g.js.map +0 -1
  158. package/dashboard/dist/assets/McpRunDetailPage-SoXudCbq.js +0 -2
  159. package/dashboard/dist/assets/WorkflowExecutionPage-CVGztAdK.js +0 -2
  160. package/dashboard/dist/assets/WorkflowExecutionPage-CVGztAdK.js.map +0 -1
  161. package/dashboard/dist/assets/index-ChGBlYKj.css +0 -1
  162. package/dashboard/dist/assets/index-CryoNbg0.js +0 -2
  163. package/dashboard/dist/assets/index-DDxZOINn.js +0 -5
  164. package/dashboard/dist/assets/index-DDxZOINn.js.map +0 -1
@@ -0,0 +1,560 @@
1
+ # Human-in-the-Loop (HITL) Guide
2
+
3
+ Build durable workflows that pause for human input and resume automatically when the human responds. Long-tail provides the full escalation lifecycle — claiming, routing, forms, resolution — so you focus on business logic and form design.
4
+
5
+ ---
6
+
7
+ ## Architecture Overview
8
+
9
+ ```
10
+ Durable Workflow Long-tail Platform Dashboard
11
+ ┌─────────────┐ ┌──────────────────┐ ┌──────────────┐
12
+ │ Run logic │ │ Create record │ │ List view │
13
+ │ Hit decision│──escalate───────>│ Route to role │───event──> │ Detail page │
14
+ │ point │ │ Persist schema │ │ Render form │
15
+ │ ...pause... │ │ │ │ Human edits │
16
+ │ │<──signal─────────│ Signal workflow │<──submit── │ Submit │
17
+ │ Resume with │ │ Mark resolved │ │ │
18
+ │ payload │ └──────────────────┘ └──────────────┘
19
+ └─────────────┘
20
+ ```
21
+
22
+ 1. **Workflow escalates** — creates an escalation record with a role, description, and optional form schema
23
+ 2. **Platform routes** — the escalation appears in the dashboard for users with the matching role
24
+ 3. **Human claims** — a user claims the work item (soft-lock with TTL)
25
+ 4. **Human submits** — the form response is sent back as a signal to the paused workflow
26
+ 5. **Workflow resumes** — continues execution with the human's input as the resolver payload
27
+
28
+ ---
29
+
30
+ ## Creating Escalations
31
+
32
+ ### Pattern 1: `conditionLT` Signal (Recommended)
33
+
34
+ The workflow stays running and waits for a signal. Lightweight, no re-run needed.
35
+
36
+ ```typescript
37
+ import { conditionLT } from 'long-tail/orchestrator';
38
+ import { ltCreateEscalation } from 'long-tail/activities';
39
+
40
+ export async function approvalWorkflow(envelope: LTEnvelope) {
41
+ // ... do initial work ...
42
+
43
+ const signalId = `approval-${ctx.workflowId}`;
44
+
45
+ // Create the escalation with a form schema
46
+ await ltCreateEscalation({
47
+ type: 'approval',
48
+ subtype: 'budget-request',
49
+ description: `Budget approval needed: $${envelope.amount}`,
50
+ role: 'finance-reviewer',
51
+ priority: 2,
52
+ envelope: JSON.stringify(envelope),
53
+ workflowId: ctx.workflowId,
54
+ taskQueue: ctx.taskQueue,
55
+ workflowType: 'approvalWorkflow',
56
+ metadata: {
57
+ signal_id: signalId,
58
+ form_schema: {
59
+ title: 'Budget Approval',
60
+ description: 'Review the budget request and approve or reject.',
61
+ properties: {
62
+ approved: { type: 'boolean', description: 'Approve this request?' },
63
+ notes: { type: 'string', format: 'textarea', description: 'Optional reviewer notes' },
64
+ },
65
+ required: ['approved'],
66
+ },
67
+ },
68
+ });
69
+
70
+ // Workflow pauses here until the human responds
71
+ const decision = await conditionLT<{ approved: boolean; notes?: string }>(signalId);
72
+
73
+ if (decision.approved) {
74
+ // ... proceed with approved flow ...
75
+ } else {
76
+ // ... handle rejection ...
77
+ }
78
+ }
79
+ ```
80
+
81
+ ### Pattern 2: Interceptor Return
82
+
83
+ The workflow returns an escalation result. The interceptor handles creation. On resolution, the workflow is re-run with the resolver payload injected into the envelope.
84
+
85
+ ```typescript
86
+ export async function reviewWorkflow(envelope: LTEnvelope) {
87
+ if (needsHumanReview(envelope)) {
88
+ return {
89
+ type: 'escalation',
90
+ data: { document: envelope.documentUrl },
91
+ message: 'Document requires human review before publishing',
92
+ priority: 2,
93
+ role: 'content-reviewer',
94
+ };
95
+ }
96
+ // ... normal flow ...
97
+ }
98
+ ```
99
+
100
+ ### Per-Escalation vs Workflow-Level Schema
101
+
102
+ - **Workflow config `resolver_schema`**: Default form for all escalations of this workflow type. Set in the workflow registry.
103
+ - **`metadata.form_schema`**: Per-escalation override. Takes precedence over workflow config. Use when different escalation points in the same workflow need different forms.
104
+
105
+ ---
106
+
107
+ ## JSON Schema Form Authoring
108
+
109
+ The dashboard renders forms automatically from JSON Schema. No frontend code needed.
110
+
111
+ ### Supported Field Types
112
+
113
+ | JSON Type | Renders As |
114
+ |-----------|-----------|
115
+ | `boolean` | Checkbox toggle |
116
+ | `number` | Number input |
117
+ | `string` | Text input (default) |
118
+ | `string` + `enum` | Dropdown select |
119
+ | `null` | Read-only "null" display |
120
+ | `array` | Tag display (read-only) |
121
+ | `object` | Nested section with recursive fields |
122
+
123
+ ### String Format Extensions
124
+
125
+ Use the `format` keyword to get specialized inputs:
126
+
127
+ | Format | Input Type |
128
+ |--------|-----------|
129
+ | `"password"` | Password field (masked, with ephemeral token redaction) |
130
+ | `"date"` | Date picker |
131
+ | `"date-time"` | Date + time picker |
132
+ | `"email"` | Email input with validation |
133
+ | `"uri"` | URL input |
134
+ | `"textarea"` | Multi-line textarea (always, regardless of content length) |
135
+
136
+ ```json
137
+ {
138
+ "properties": {
139
+ "due_date": { "type": "string", "format": "date" },
140
+ "contact_email": { "type": "string", "format": "email" },
141
+ "detailed_notes": { "type": "string", "format": "textarea" }
142
+ }
143
+ }
144
+ ```
145
+
146
+ ### Custom Widgets (`x-lt-widget`)
147
+
148
+ For rich inputs beyond standard HTML types:
149
+
150
+ | Widget | Description |
151
+ |--------|------------|
152
+ | `"file-upload"` | File picker with drag-and-drop. Stores base64 data URL. Use `accept` to filter file types. |
153
+ | `"code-editor"` | Monospace textarea with tab-key support. Use `x-lt-language` for syntax hint. |
154
+ | `"signature"` | HTML5 Canvas drawing pad. Outputs PNG data URL. |
155
+ | `"rich-text"` | Tall textarea for formatted text input. |
156
+
157
+ ```json
158
+ {
159
+ "properties": {
160
+ "screenshot": {
161
+ "type": "string",
162
+ "x-lt-widget": "file-upload",
163
+ "accept": "image/*",
164
+ "description": "Upload a screenshot of the issue"
165
+ },
166
+ "fix_script": {
167
+ "type": "string",
168
+ "x-lt-widget": "code-editor",
169
+ "x-lt-language": "sql",
170
+ "description": "SQL migration to apply"
171
+ },
172
+ "signature": {
173
+ "type": "string",
174
+ "x-lt-widget": "signature",
175
+ "description": "Sign to confirm"
176
+ }
177
+ }
178
+ }
179
+ ```
180
+
181
+ ### Layout Options (`x-lt-layout`)
182
+
183
+ Control how fields are arranged:
184
+
185
+ | Layout | Behavior |
186
+ |--------|----------|
187
+ | `"two-column"` | Fields in a 2-column grid. Use `x-lt-span: 2` on a field for full-width. |
188
+
189
+ ```json
190
+ {
191
+ "x-lt-layout": "two-column",
192
+ "properties": {
193
+ "first_name": { "type": "string" },
194
+ "last_name": { "type": "string" },
195
+ "notes": { "type": "string", "format": "textarea", "x-lt-span": 2 }
196
+ }
197
+ }
198
+ ```
199
+
200
+ ### Field Ordering (`x-lt-order`)
201
+
202
+ By default, fields render in JSON key order. Use `x-lt-order` to control sequence:
203
+
204
+ ```json
205
+ {
206
+ "x-lt-order": ["priority", "decision", "notes"],
207
+ "properties": {
208
+ "notes": { "type": "string" },
209
+ "decision": { "type": "string", "enum": ["approve", "reject", "defer"] },
210
+ "priority": { "type": "number" }
211
+ }
212
+ }
213
+ ```
214
+
215
+ ### Validation (`required`)
216
+
217
+ Fields listed in `required` show a red asterisk and block submission when empty:
218
+
219
+ ```json
220
+ {
221
+ "required": ["decision"],
222
+ "properties": {
223
+ "decision": { "type": "string", "enum": ["approve", "reject"] },
224
+ "notes": { "type": "string", "description": "Optional comments" }
225
+ }
226
+ }
227
+ ```
228
+
229
+ ### Read-Only Fields (`readOnly`)
230
+
231
+ Fields with `readOnly: true` display as static text. Useful for showing context alongside editable fields:
232
+
233
+ ```json
234
+ {
235
+ "properties": {
236
+ "request_amount": { "type": "number", "readOnly": true },
237
+ "approved_amount": { "type": "number", "description": "Enter the approved amount" }
238
+ }
239
+ }
240
+ ```
241
+
242
+ ### Schema Title and Description
243
+
244
+ The `title` and `description` at the schema root are used in the UI:
245
+ - **`title`**: Shown as the section header (replaces "Submit Your Resolution" in user mode)
246
+ - **`description`** or **`x-lt-context`**: In user mode, displayed as a context panel alongside the form in a two-panel layout
247
+
248
+ ```json
249
+ {
250
+ "title": "Expense Approval",
251
+ "description": "Review the expense report below. Verify receipts match the claimed amounts. Approve or reject with notes.",
252
+ "properties": { ... }
253
+ }
254
+ ```
255
+
256
+ ---
257
+
258
+ ## Iframe Viewport Protocol
259
+
260
+ For fully custom UIs (PDF viewers, complex multi-step forms, specialized domain UIs), use an iframe viewport.
261
+
262
+ ### Schema Declaration
263
+
264
+ ```json
265
+ {
266
+ "x-lt-viewport": {
267
+ "type": "iframe",
268
+ "src": "https://your-app.example.com/hitl-form"
269
+ },
270
+ "properties": { ... }
271
+ }
272
+ ```
273
+
274
+ When `x-lt-viewport` is present, the dashboard renders an iframe instead of the standard form.
275
+
276
+ ### Message Protocol
277
+
278
+ Communication happens via `window.postMessage`.
279
+
280
+ #### Parent to Iframe
281
+
282
+ ```typescript
283
+ // Sent when the iframe signals ready or on load
284
+ {
285
+ type: 'lt:init',
286
+ escalation: {
287
+ id: string,
288
+ type: string,
289
+ subtype: string,
290
+ description: string | null,
291
+ status: string,
292
+ priority: number,
293
+ role: string,
294
+ workflow_type: string | null,
295
+ },
296
+ schema: Record<string, unknown>, // The full form schema
297
+ }
298
+
299
+ // Optional: parent requests the iframe to submit
300
+ {
301
+ type: 'lt:requestSubmit'
302
+ }
303
+ ```
304
+
305
+ #### Iframe to Parent
306
+
307
+ ```typescript
308
+ // Signal that the iframe is ready to receive init data
309
+ { type: 'lt:ready' }
310
+
311
+ // Submit the human's response — triggers escalation resolution
312
+ { type: 'lt:submit', payload: { approved: true, notes: '...' } }
313
+
314
+ // Escalate to a different role
315
+ { type: 'lt:escalate', target: 'senior-reviewer' }
316
+
317
+ // Auto-resize the iframe
318
+ { type: 'lt:resize', height: 600 }
319
+ ```
320
+
321
+ ### Minimal Example
322
+
323
+ ```html
324
+ <!DOCTYPE html>
325
+ <html>
326
+ <head><title>Custom HITL Form</title></head>
327
+ <body>
328
+ <div id="form"></div>
329
+ <button id="submit">Approve</button>
330
+
331
+ <script>
332
+ // Signal ready
333
+ window.parent.postMessage({ type: 'lt:ready' }, '*');
334
+
335
+ // Receive init data
336
+ window.addEventListener('message', (event) => {
337
+ if (event.data.type === 'lt:init') {
338
+ const { escalation, schema } = event.data;
339
+ document.getElementById('form').textContent =
340
+ `Reviewing: ${escalation.description}`;
341
+ }
342
+ });
343
+
344
+ // Submit response
345
+ document.getElementById('submit').addEventListener('click', () => {
346
+ window.parent.postMessage({
347
+ type: 'lt:submit',
348
+ payload: { approved: true, reviewed_at: new Date().toISOString() },
349
+ }, '*');
350
+ });
351
+ </script>
352
+ </body>
353
+ </html>
354
+ ```
355
+
356
+ ### Security
357
+
358
+ - The iframe runs with `sandbox="allow-scripts allow-same-origin allow-forms"`
359
+ - The parent validates message origins — only messages from the iframe's origin are accepted
360
+ - The `envelope` field (which may contain secrets) is NOT sent to the iframe
361
+ - Only safe escalation metadata (id, type, description, status, priority, role) is exposed
362
+
363
+ ---
364
+
365
+ ## Dev Mode vs User Mode
366
+
367
+ The escalation detail page has two viewing modes:
368
+
369
+ | Aspect | Dev Mode | User Mode |
370
+ |--------|----------|-----------|
371
+ | **Default for** | Admins, superadmins, engineers | All other roles |
372
+ | **Shows** | Everything | Title, description, form, action bar |
373
+ | **Hides** | Nothing | Technical IDs, raw JSON, envelope data, AI triage, raw JSON editor |
374
+ | **Persistence** | sessionStorage (per browser session) | sessionStorage |
375
+
376
+ **Key principle**: User mode only hides technical debugging information. All HITL workflow actions (claim, submit, escalate, release) are always visible in both modes.
377
+
378
+ Privileged users can toggle between modes via the switch in the page header.
379
+
380
+ ### Designing for User Mode
381
+
382
+ To create a polished user mode experience:
383
+
384
+ 1. Set `title` on your schema — it replaces the section header
385
+ 2. Set `description` or `x-lt-context` — it appears as a context panel in a two-panel layout
386
+ 3. Use `readOnly` fields for context the human needs to see but shouldn't edit
387
+ 4. Use `x-lt-order` to put the most important fields first
388
+ 5. Use `required` to guide users on what must be filled
389
+ 6. Use descriptive `description` on individual fields for inline help text
390
+
391
+ ---
392
+
393
+ ## Role-Based Routing
394
+
395
+ Escalations are routed by role. Users only see escalations for roles they hold.
396
+
397
+ ```typescript
398
+ // Workflow escalates to a specific role
399
+ await ltCreateEscalation({
400
+ role: 'finance-reviewer', // Only users with this role see it
401
+ // ...
402
+ });
403
+ ```
404
+
405
+ ### Escalation Chains
406
+
407
+ Users can escalate to other roles via the "Escalate" tab:
408
+
409
+ ```
410
+ Analyst → Senior Analyst → Manager → VP
411
+ ```
412
+
413
+ Configure escalation targets in the role configuration (Admin > Roles). Each role defines which other roles it can escalate to.
414
+
415
+ ### Multi-Tier Example
416
+
417
+ ```typescript
418
+ // Level 1: Auto-review
419
+ const result = await autoReview(document);
420
+
421
+ if (result.confidence < 0.8) {
422
+ // Level 2: Human analyst
423
+ await ltCreateEscalation({
424
+ role: 'analyst',
425
+ description: `Low confidence review (${result.confidence})`,
426
+ metadata: {
427
+ form_schema: {
428
+ title: 'Document Review',
429
+ properties: {
430
+ approved: { type: 'boolean' },
431
+ corrections: { type: 'string', format: 'textarea' },
432
+ },
433
+ required: ['approved'],
434
+ },
435
+ },
436
+ });
437
+ // User can further escalate to 'senior-analyst' or 'manager' from the dashboard
438
+ }
439
+ ```
440
+
441
+ ---
442
+
443
+ ## Worked Examples
444
+
445
+ ### Simple Approval
446
+
447
+ A workflow needs a yes/no decision with optional notes.
448
+
449
+ ```typescript
450
+ metadata: {
451
+ signal_id: signalId,
452
+ form_schema: {
453
+ title: 'Approve Request',
454
+ description: 'Review the details and approve or reject this request.',
455
+ required: ['approved'],
456
+ properties: {
457
+ approved: { type: 'boolean', description: 'Check to approve' },
458
+ notes: { type: 'string', format: 'textarea', description: 'Optional comments' },
459
+ },
460
+ },
461
+ }
462
+ ```
463
+
464
+ ### Document Review with PDF Viewer
465
+
466
+ Use an iframe viewport to embed a PDF viewer alongside approval controls.
467
+
468
+ ```typescript
469
+ metadata: {
470
+ signal_id: signalId,
471
+ form_schema: {
472
+ title: 'Document Review',
473
+ 'x-lt-viewport': {
474
+ type: 'iframe',
475
+ src: 'https://internal.example.com/pdf-reviewer',
476
+ },
477
+ },
478
+ }
479
+ ```
480
+
481
+ The iframe at `pdf-reviewer` loads the document, renders it with a viewer, and posts `lt:submit` with the review decision.
482
+
483
+ ### Multi-Field Data Entry
484
+
485
+ A complex form with layout and validation.
486
+
487
+ ```typescript
488
+ metadata: {
489
+ signal_id: signalId,
490
+ form_schema: {
491
+ title: 'Customer Intake',
492
+ description: 'Complete the customer information form. All required fields must be filled before submission.',
493
+ 'x-lt-layout': 'two-column',
494
+ 'x-lt-order': ['first_name', 'last_name', 'email', 'phone', 'tier', 'notes'],
495
+ required: ['first_name', 'last_name', 'email', 'tier'],
496
+ properties: {
497
+ first_name: { type: 'string' },
498
+ last_name: { type: 'string' },
499
+ email: { type: 'string', format: 'email' },
500
+ phone: { type: 'string' },
501
+ tier: {
502
+ type: 'string',
503
+ enum: ['free', 'pro', 'enterprise'],
504
+ description: 'Select the customer tier',
505
+ },
506
+ notes: {
507
+ type: 'string',
508
+ format: 'textarea',
509
+ 'x-lt-span': 2,
510
+ description: 'Additional notes about this customer',
511
+ },
512
+ },
513
+ },
514
+ }
515
+ ```
516
+
517
+ ### Credential Provisioning
518
+
519
+ Password fields are automatically redacted and replaced with ephemeral tokens (15-min TTL) before being sent back to the workflow.
520
+
521
+ ```typescript
522
+ metadata: {
523
+ signal_id: signalId,
524
+ form_schema: {
525
+ title: 'Provide Credentials',
526
+ description: 'Enter the API credentials for this integration. Passwords are encrypted and stored as ephemeral tokens.',
527
+ required: ['api_key', 'api_secret'],
528
+ properties: {
529
+ api_key: { type: 'string', description: 'API Key' },
530
+ api_secret: { type: 'string', format: 'password', description: 'API Secret (will be redacted)' },
531
+ environment: {
532
+ type: 'string',
533
+ enum: ['sandbox', 'production'],
534
+ description: 'Target environment',
535
+ },
536
+ },
537
+ },
538
+ }
539
+ ```
540
+
541
+ ---
542
+
543
+ ## What Long-tail Provides (For Free)
544
+
545
+ When you author a HITL-backed workflow, the platform handles:
546
+
547
+ - **Escalation routing** — role-based, priority-ordered work queues
548
+ - **Claim/release** — soft-lock with TTL, prevents duplicate work
549
+ - **Real-time updates** — NATS/Socket.IO events push changes to the dashboard instantly
550
+ - **Form rendering** — JSON Schema to rich form controls, no frontend code needed
551
+ - **Dev/user mode** — technical vs clean views, per-session preference
552
+ - **Section state persistence** — collapsed sections remembered across navigation
553
+ - **Escalation chains** — users can re-route work to other roles
554
+ - **AI triage** — optional auto-resolution for common patterns (dev mode)
555
+ - **Signal routing** — 5 resolution paths (conditionLT, waitFor, triage, re-run, notification-only)
556
+ - **Credential security** — password fields use ephemeral tokens, never stored in plain text
557
+ - **Telemetry** — trace IDs link escalations to OpenTelemetry traces
558
+ - **Bulk operations** — bulk claim, assign, escalate, triage for queue management
559
+
560
+ You write the workflow and the schema. Everything else is provided.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hotmeshio/long-tail",
3
- "version": "0.4.1",
3
+ "version": "0.4.3",
4
4
  "description": "Long Tail Workflows — Durable AI workflows with human-in-the-loop escalation. Powered by PostgreSQL.",
5
5
  "main": "./build/index.js",
6
6
  "types": "./build/index.d.ts",
@@ -70,7 +70,7 @@
70
70
  "@anthropic-ai/sdk": "^0.92.0",
71
71
  "@aws-sdk/client-s3": "^3.1017.0",
72
72
  "@aws-sdk/s3-request-presigner": "^3.1045.0",
73
- "@hotmeshio/hotmesh": "^0.19.0",
73
+ "@hotmeshio/hotmesh": "^0.19.2",
74
74
  "@modelcontextprotocol/sdk": "^1.27.1",
75
75
  "@opentelemetry/exporter-trace-otlp-proto": "^0.215.0",
76
76
  "@opentelemetry/resources": "^2.5.1",
@@ -1,2 +0,0 @@
1
- import{a as d,j as t}from"./vendor-query-B2UbickB.js";import{F as $,b as g}from"./FilterBar-Ck4K4rzu.js";import{f as T,C as F}from"./index-CBS8FBcp.js";import{E as O}from"./SwimlaneTimeline-CU2ZD9cC.js";const y="lt-collapsed-sections";function D(r){try{const n=localStorage.getItem(`${y}:${r}`);return n?new Set(JSON.parse(n)):new Set}catch{return new Set}}function P(r,n){localStorage.setItem(`${y}:${r}`,JSON.stringify([...n]))}function G(r){const[n,l]=d.useState(()=>D(r)),p=d.useCallback(o=>n.has(o),[n]),u=d.useCallback(o=>{l(x=>{const c=new Set(x);return c.has(o)?c.delete(o):c.add(o),P(r,c),c})},[r]);return{isCollapsed:p,toggle:u}}const A=new Set(["activity_task_scheduled","timer_started","child_workflow_execution_started","signal_wait_started"]),L=new Set(["activity_task_completed","activity_task_failed","timer_fired","child_workflow_execution_completed","child_workflow_execution_failed","workflow_execution_signaled"]);function R(r){const n=new Set;for(const l of r)L.has(l.event_type)&&l.attributes.timeline_key&&n.add(l.attributes.timeline_key);return n}function K({events:r,childTasks:n}){const[l,p]=d.useState(""),[u,o]=d.useState(""),[x,c]=d.useState("asc"),[m,f]=d.useState(new Set),b=[...new Set(r.map(e=>e.category))].sort(),w=[...new Set(r.map(e=>e.event_type))].sort(),v=R(r),S=e=>{if(e.duration_ms!==null||!A.has(e.event_type))return!1;const s=e.attributes.timeline_key;return!(s&&v.has(s))};let a=r;l&&(a=a.filter(e=>e.category===l)),u&&(a=a.filter(e=>e.event_type===u)),a=[...a].sort((e,s)=>x==="asc"?e.event_id-s.event_id:s.event_id-e.event_id);const j=e=>{switch(e){case"activity":return"bg-blue-500";case"signal":return"bg-emerald-500";case"timer":return"bg-status-warning";case"child_workflow":return"bg-violet-500";default:return"bg-text-tertiary"}},k=e=>{const s=e.event_type;if(e.attributes.activity_type)return`${s} — ${e.attributes.activity_type}`;if(e.attributes.signal_name)return`${s} — ${e.attributes.signal_name}`;if(e.attributes.child_workflow_id){const i=e.attributes.child_workflow_id,C=i.length>24?`${i.slice(0,24)}...`:i;return`${s} — ${C}`}return s},N=e=>{if(!(n!=null&&n.length))return;const s=e.attributes.activity_type;if(s)return n.find(i=>i.workflow_type===s)},_=e=>{f(s=>{const i=new Set(s);return i.has(e)?i.delete(e):i.add(e),i})},h=a.length>0&&a.every(e=>m.has(e.event_id)),E=()=>{f(h?new Set:new Set(a.map(e=>e.event_id)))};return t.jsxs("div",{children:[t.jsxs("div",{className:"px-6 py-4 border-b border-surface-border flex items-center justify-between flex-wrap gap-3",children:[t.jsxs("div",{className:"flex items-center gap-4",children:[t.jsxs("p",{className:"text-[10px] font-semibold uppercase tracking-widest text-text-tertiary",children:["Events (",a.length,")"]}),a.length>0&&t.jsx("button",{onClick:E,className:"text-[10px] text-accent hover:underline",children:h?"Collapse all":"Expand all"})]}),t.jsxs($,{children:[t.jsx(g,{label:"Category",value:l,onChange:p,options:b.map(e=>({value:e,label:e}))}),t.jsx(g,{label:"Type",value:u,onChange:o,options:w.map(e=>({value:e,label:e}))}),t.jsx("button",{onClick:()=>c(e=>e==="asc"?"desc":"asc"),className:"btn-ghost text-xs",children:x==="asc"?"Oldest first":"Newest first"})]})]}),t.jsx("div",{children:a.length===0?t.jsx("div",{className:"px-6 py-8 text-center",children:t.jsx("p",{className:"text-sm text-text-tertiary",children:"No events match the current filters"})}):a.map(e=>{const s=m.has(e.event_id),i=S(e);return t.jsxs("div",{className:"border-b border-surface-border last:border-b-0",children:[t.jsxs("div",{className:"px-6 py-3 flex items-center gap-4 cursor-pointer hover:bg-surface-hover transition-colors duration-200",onClick:()=>_(e.event_id),children:[t.jsx("span",{className:`text-[10px] text-text-tertiary transition-transform duration-300 ${s?"rotate-90":""}`,children:"▶"}),t.jsx("span",{className:"text-xs font-mono text-text-tertiary w-8 shrink-0",children:e.event_id}),t.jsx("span",{className:`w-2 h-2 rounded-full shrink-0 ${j(e.category)}`}),t.jsx("span",{className:"text-sm text-text-primary flex-1 truncate",children:k(e)}),t.jsx("span",{className:"text-xs font-mono text-text-tertiary shrink-0",children:i?t.jsxs("span",{className:"inline-flex items-center gap-1 text-status-warning",children:[t.jsx("span",{className:"w-1.5 h-1.5 rounded-full bg-status-warning animate-pulse"}),"Pending"]}):e.duration_ms!==null?T(e.duration_ms):"--"}),t.jsx("time",{className:"text-[10px] font-mono text-text-tertiary shrink-0",children:new Date(e.event_time).toLocaleTimeString()})]}),t.jsx(F,{open:s&&!!e.attributes,children:t.jsx("div",{className:"px-6 pb-4 pl-16",children:t.jsx(O,{event:e,childTask:N(e),pending:i,onClose:()=>_(e.event_id)})})})]},e.event_id)})})]})}export{K as E,G as u};
2
- //# sourceMappingURL=EventTable-B9wYf13g.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"EventTable-B9wYf13g.js","sources":["../../src/hooks/useCollapsedSections.ts","../../src/pages/workflows/workflow-execution/EventTable.tsx"],"sourcesContent":["import { useState, useCallback } from 'react';\n\nconst STORAGE_KEY = 'lt-collapsed-sections';\n\nfunction load(pageKey: string): Set<string> {\n try {\n const raw = localStorage.getItem(`${STORAGE_KEY}:${pageKey}`);\n if (!raw) return new Set();\n return new Set(JSON.parse(raw));\n } catch {\n return new Set();\n }\n}\n\nfunction save(pageKey: string, set: Set<string>) {\n localStorage.setItem(`${STORAGE_KEY}:${pageKey}`, JSON.stringify([...set]));\n}\n\nexport function useCollapsedSections(pageKey: string) {\n const [collapsed, setCollapsed] = useState<Set<string>>(() => load(pageKey));\n\n const isCollapsed = useCallback(\n (section: string) => collapsed.has(section),\n [collapsed],\n );\n\n const toggle = useCallback(\n (section: string) => {\n setCollapsed((prev) => {\n const next = new Set(prev);\n if (next.has(section)) {\n next.delete(section);\n } else {\n next.add(section);\n }\n save(pageKey, next);\n return next;\n });\n },\n [pageKey],\n );\n\n return { isCollapsed, toggle };\n}\n","import { useState } from 'react';\nimport { FilterBar, FilterSelect } from '../../../components/common/data/FilterBar';\nimport { Collapsible } from '../../../components/common/layout/Collapsible';\nimport type { WorkflowExecutionEvent, LTTaskRecord } from '../../../api/types';\nimport { EventDetailPanel } from './EventDetailPanel';\nimport { formatDuration } from './utils';\n\ninterface EventTableProps {\n events: WorkflowExecutionEvent[];\n childTasks?: LTTaskRecord[];\n}\n\n/** Event types that represent the \"start\" phase of a paired operation */\nconst STARTED_TYPES = new Set([\n 'activity_task_scheduled',\n 'timer_started',\n 'child_workflow_execution_started',\n 'signal_wait_started',\n]);\n\n/** Completion event types that close a paired operation */\nconst COMPLETED_TYPES = new Set([\n 'activity_task_completed',\n 'activity_task_failed',\n 'timer_fired',\n 'child_workflow_execution_completed',\n 'child_workflow_execution_failed',\n 'workflow_execution_signaled',\n]);\n\n/**\n * Build a set of timeline_keys that have a matching completion event.\n * A \"scheduled\" event is only truly pending if no completion exists.\n */\nfunction buildCompletedKeys(events: WorkflowExecutionEvent[]): Set<string> {\n const keys = new Set<string>();\n for (const e of events) {\n if (COMPLETED_TYPES.has(e.event_type) && e.attributes.timeline_key) {\n keys.add(e.attributes.timeline_key);\n }\n }\n return keys;\n}\n\nexport function EventTable({ events, childTasks }: EventTableProps) {\n const [categoryFilter, setCategoryFilter] = useState('');\n const [typeFilter, setTypeFilter] = useState('');\n const [sortOrder, setSortOrder] = useState<'asc' | 'desc'>('asc');\n const [expandedEvents, setExpandedEvents] = useState<Set<number>>(new Set());\n\n const categories = [...new Set(events.map((e) => e.category))].sort();\n const eventTypes = [...new Set(events.map((e) => e.event_type))].sort();\n const completedKeys = buildCompletedKeys(events);\n\n /** True only when a \"started/scheduled\" event has no matching completion */\n const isPending = (evt: WorkflowExecutionEvent): boolean => {\n if (evt.duration_ms !== null) return false;\n if (!STARTED_TYPES.has(evt.event_type)) return false;\n const tlk = evt.attributes.timeline_key;\n if (tlk && completedKeys.has(tlk)) return false;\n return true;\n };\n\n let filtered = events;\n if (categoryFilter) filtered = filtered.filter((e) => e.category === categoryFilter);\n if (typeFilter) filtered = filtered.filter((e) => e.event_type === typeFilter);\n\n filtered = [...filtered].sort((a, b) =>\n sortOrder === 'asc' ? a.event_id - b.event_id : b.event_id - a.event_id,\n );\n\n const categoryDot = (category: string) => {\n switch (category) {\n case 'activity':\n return 'bg-blue-500';\n case 'signal':\n return 'bg-emerald-500';\n case 'timer':\n return 'bg-status-warning';\n case 'child_workflow':\n return 'bg-violet-500';\n default:\n return 'bg-text-tertiary';\n }\n };\n\n /** Build a descriptive label for an event row */\n const eventLabel = (evt: WorkflowExecutionEvent): string => {\n const base = evt.event_type;\n if (evt.attributes.activity_type) {\n return `${base} — ${evt.attributes.activity_type}`;\n }\n if (evt.attributes.signal_name) {\n return `${base} — ${evt.attributes.signal_name}`;\n }\n if (evt.attributes.child_workflow_id) {\n const id = evt.attributes.child_workflow_id;\n const truncated = id.length > 24 ? `${id.slice(0, 24)}...` : id;\n return `${base} — ${truncated}`;\n }\n return base;\n };\n\n /** Find a matching child task for an event's activity_type */\n const findChildTask = (evt: WorkflowExecutionEvent): LTTaskRecord | undefined => {\n if (!childTasks?.length) return undefined;\n const activityType = evt.attributes.activity_type;\n if (!activityType) return undefined;\n return childTasks.find((t) => t.workflow_type === activityType);\n };\n\n const toggleEvent = (id: number) => {\n setExpandedEvents((prev) => {\n const next = new Set(prev);\n if (next.has(id)) next.delete(id);\n else next.add(id);\n return next;\n });\n };\n\n const allExpanded = filtered.length > 0 && filtered.every((e) => expandedEvents.has(e.event_id));\n\n const toggleAll = () => {\n if (allExpanded) {\n setExpandedEvents(new Set());\n } else {\n setExpandedEvents(new Set(filtered.map((e) => e.event_id)));\n }\n };\n\n return (\n <div>\n <div className=\"px-6 py-4 border-b border-surface-border flex items-center justify-between flex-wrap gap-3\">\n <div className=\"flex items-center gap-4\">\n <p className=\"text-[10px] font-semibold uppercase tracking-widest text-text-tertiary\">\n Events ({filtered.length})\n </p>\n {filtered.length > 0 && (\n <button onClick={toggleAll} className=\"text-[10px] text-accent hover:underline\">\n {allExpanded ? 'Collapse all' : 'Expand all'}\n </button>\n )}\n </div>\n <FilterBar>\n <FilterSelect\n label=\"Category\"\n value={categoryFilter}\n onChange={setCategoryFilter}\n options={categories.map((c) => ({ value: c, label: c }))}\n />\n <FilterSelect\n label=\"Type\"\n value={typeFilter}\n onChange={setTypeFilter}\n options={eventTypes.map((t) => ({ value: t, label: t }))}\n />\n <button\n onClick={() => setSortOrder((s) => (s === 'asc' ? 'desc' : 'asc'))}\n className=\"btn-ghost text-xs\"\n >\n {sortOrder === 'asc' ? 'Oldest first' : 'Newest first'}\n </button>\n </FilterBar>\n </div>\n\n <div>\n {filtered.length === 0 ? (\n <div className=\"px-6 py-8 text-center\">\n <p className=\"text-sm text-text-tertiary\">No events match the current filters</p>\n </div>\n ) : (\n filtered.map((evt) => {\n const isExpanded = expandedEvents.has(evt.event_id);\n const pending = isPending(evt);\n\n return (\n <div\n key={evt.event_id}\n className=\"border-b border-surface-border last:border-b-0\"\n >\n {/* Row header */}\n <div\n className=\"px-6 py-3 flex items-center gap-4 cursor-pointer hover:bg-surface-hover transition-colors duration-200\"\n onClick={() => toggleEvent(evt.event_id)}\n >\n {/* Expand chevron */}\n <span\n className={`text-[10px] text-text-tertiary transition-transform duration-300 ${isExpanded ? 'rotate-90' : ''}`}\n >\n &#9654;\n </span>\n\n <span className=\"text-xs font-mono text-text-tertiary w-8 shrink-0\">\n {evt.event_id}\n </span>\n <span\n className={`w-2 h-2 rounded-full shrink-0 ${categoryDot(evt.category)}`}\n />\n <span className=\"text-sm text-text-primary flex-1 truncate\">\n {eventLabel(evt)}\n </span>\n <span className=\"text-xs font-mono text-text-tertiary shrink-0\">\n {pending ? (\n <span className=\"inline-flex items-center gap-1 text-status-warning\">\n <span className=\"w-1.5 h-1.5 rounded-full bg-status-warning animate-pulse\" />\n Pending\n </span>\n ) : evt.duration_ms !== null ? (\n formatDuration(evt.duration_ms)\n ) : (\n '--'\n )}\n </span>\n <time className=\"text-[10px] font-mono text-text-tertiary shrink-0\">\n {new Date(evt.event_time).toLocaleTimeString()}\n </time>\n </div>\n\n {/* Inline detail panel — directly below the clicked row */}\n <Collapsible open={isExpanded && !!evt.attributes}>\n <div className=\"px-6 pb-4 pl-16\">\n <EventDetailPanel\n event={evt}\n childTask={findChildTask(evt)}\n pending={pending}\n onClose={() => toggleEvent(evt.event_id)}\n />\n </div>\n </Collapsible>\n </div>\n );\n })\n )}\n </div>\n </div>\n );\n}\n"],"names":["STORAGE_KEY","load","pageKey","raw","save","set","useCollapsedSections","collapsed","setCollapsed","useState","isCollapsed","useCallback","section","toggle","prev","next","STARTED_TYPES","COMPLETED_TYPES","buildCompletedKeys","events","keys","e","EventTable","childTasks","categoryFilter","setCategoryFilter","typeFilter","setTypeFilter","sortOrder","setSortOrder","expandedEvents","setExpandedEvents","categories","eventTypes","completedKeys","isPending","evt","tlk","filtered","a","b","categoryDot","category","eventLabel","base","id","truncated","findChildTask","activityType","t","toggleEvent","allExpanded","toggleAll","jsxs","jsx","FilterBar","FilterSelect","c","s","isExpanded","pending","formatDuration","Collapsible","EventDetailPanel"],"mappings":"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"}
@@ -1,2 +0,0 @@
1
- import{b as P,a as I,j as e}from"./vendor-query-B2UbickB.js";import{b as D}from"./mcp-runs-BhkGaw2y.js";import{u as R}from"./settings-wTRbazzw.js";import{S as K}from"./StatusBadge-XQlNFwmH.js";import{u as U,D as $,c as N,J as w,N as J}from"./index-CBS8FBcp.js";import{P as k}from"./PageHeader-Bo0SpcCK.js";import{C as L}from"./CopyableId-DmLF-RqZ.js";import{C as j}from"./CollapsibleSection-D9F01Tny.js";import{u as M,E as V}from"./EventTable-B9wYf13g.js";import{L as q}from"./ListToolbar-DL1wEuvL.js";import{S as B}from"./SwimlaneTimeline-CU2ZD9cC.js";import{e as F,f as H}from"./vendor-react-CX88sFS5.js";import"./vendor-icons-BNtvBbnj.js";import"./FilterBar-Ck4K4rzu.js";const Q={running:"in_progress",completed:"completed",failed:"failed"};function le(){var g,v;const{jobId:s}=F(),[o]=H(),r=o.get("namespace")||"longtail",h=P(),{data:t,isLoading:S,error:i,refetch:_,isFetching:C}=D(s,r),{data:x}=R(),{isCollapsed:d,toggle:m}=M("mcp-run-detail"),f=((g=x==null?void 0:x.telemetry)==null?void 0:g.traceUrl)??null,E=I.useCallback(c=>{!s||c.workflowId!==s||h.invalidateQueries({queryKey:["mcpRunExecution",s]})},[s,h]);if(U(`${J}.activity.>`,E),S)return e.jsxs("div",{className:"animate-pulse space-y-4",children:[e.jsx("div",{className:"h-8 bg-surface-sunken rounded w-64"}),e.jsx("div",{className:"h-60 bg-surface-sunken rounded"})]});if(i||!t)return e.jsxs("div",{children:[e.jsx(k,{title:"Pipeline Execution"}),e.jsxs("div",{className:"mt-4 text-center py-8",children:[e.jsx("p",{className:"text-sm text-text-primary mb-1",children:(v=i==null?void 0:i.message)!=null&&v.includes("expired")?"Execution data is no longer available":"Unable to load execution"}),e.jsx("p",{className:"text-xs text-text-tertiary",children:(i==null?void 0:i.message)??"The run could not be resolved."})]})]});const{events:u,summary:a}=t,p=u.find(c=>{var y;return c.category==="activity"&&((y=c.attributes.kind)==null?void 0:y.includes("completed"))}),T=(p==null?void 0:p.attributes.result)??null,n=t.result,b=(n==null?void 0:n.data)??n??null;return e.jsxs("div",{children:[e.jsx(k,{title:"Pipeline Execution",actions:e.jsx(q,{onRefresh:()=>_(),isFetching:C,apiPath:`/mcp-runs/${s}/execution?app_id=${r}`})}),e.jsxs("div",{className:"bg-surface-raised border border-surface-border rounded-md p-5 mb-8",children:[e.jsxs("div",{className:"flex items-center gap-4 mb-4",children:[e.jsx("h2",{className:"text-sm font-mono text-text-primary truncate flex-1",children:t.workflow_id}),e.jsxs("div",{className:"flex items-center gap-4 shrink-0",children:[e.jsx(l,{label:"Tools",value:a.activities.user}),e.jsx(l,{label:"System",value:a.activities.system,muted:!0}),a.child_workflows.total>0&&e.jsx(l,{label:"Children",value:a.child_workflows.total}),a.timers>0&&e.jsx(l,{label:"Timers",value:a.timers}),a.signals>0&&e.jsx(l,{label:"Signals",value:a.signals}),e.jsx(K,{status:Q[t.status]??t.status})]})]}),e.jsxs("div",{className:"grid grid-cols-6 gap-x-6",children:[e.jsxs("div",{children:[e.jsx("p",{className:"text-[10px] font-semibold uppercase tracking-widest text-text-tertiary mb-0.5",children:"Server"}),e.jsx("p",{className:"text-xs font-mono text-text-primary truncate",children:r})]}),e.jsxs("div",{children:[e.jsx("p",{className:"text-[10px] font-semibold uppercase tracking-widest text-text-tertiary mb-0.5",children:"Tool"}),e.jsx("p",{className:"text-xs font-mono text-text-primary truncate",children:t.workflow_type||"—"})]}),e.jsxs("div",{children:[e.jsx("p",{className:"text-[10px] font-semibold uppercase tracking-widest text-text-tertiary mb-0.5",children:"Duration"}),e.jsx($,{ms:t.duration_ms,className:"font-mono text-text-primary"})]}),e.jsxs("div",{children:[e.jsx("p",{className:"text-[10px] font-semibold uppercase tracking-widest text-text-tertiary mb-0.5",children:"Started"}),t.start_time?e.jsx(N,{date:t.start_time,format:"datetime",className:"font-mono text-text-primary"}):e.jsx("span",{className:"text-xs text-text-tertiary",children:"--"})]}),e.jsxs("div",{children:[e.jsx("p",{className:"text-[10px] font-semibold uppercase tracking-widest text-text-tertiary mb-0.5",children:"Completed"}),t.close_time?e.jsx(N,{date:t.close_time,format:"datetime",className:"font-mono text-text-primary"}):e.jsx("span",{className:"text-xs text-text-tertiary",children:"--"})]}),e.jsxs("div",{children:[e.jsx("p",{className:"text-[10px] font-semibold uppercase tracking-widest text-text-tertiary mb-0.5",children:"Trace"}),t.trace_id?e.jsx(L,{label:"",value:t.trace_id,href:f?f.replace("{traceId}",t.trace_id):void 0,external:!0}):e.jsx("span",{className:"text-xs text-text-tertiary",children:"—"})]})]})]}),e.jsxs("div",{className:"space-y-6",children:[e.jsx(j,{title:"Details",sectionKey:"details",isCollapsed:d("details"),onToggle:m,children:e.jsxs("div",{className:"grid grid-cols-1 md:grid-cols-2 gap-4 mb-6",children:[e.jsx("div",{children:e.jsx(w,{data:T??{},label:"Input"})}),b!==null&&e.jsx("div",{children:e.jsx(w,{data:b,label:"Result"})})]})}),e.jsx(j,{title:"Execution Timeline",sectionKey:"timeline",isCollapsed:d("timeline"),onToggle:m,children:e.jsx(B,{events:u,outline:!0})}),e.jsx(j,{title:"Events",sectionKey:"events",isCollapsed:d("events"),onToggle:m,children:e.jsx(V,{events:u})})]})]})}function l({label:s,value:o,muted:r}){return e.jsxs("div",{className:"flex items-center gap-1.5 text-xs",children:[e.jsx("span",{className:"text-text-tertiary",children:s}),e.jsx("span",{className:`font-medium tabular-nums ${r?"text-text-tertiary":"text-text-primary"}`,children:o})]})}export{le as McpRunDetailPage};
2
- //# sourceMappingURL=McpRunDetailPage-SoXudCbq.js.map
@@ -1,2 +0,0 @@
1
- import{j as e,a as b}from"./vendor-query-B2UbickB.js";import{a as $,g as L}from"./workflows-MpNdzreD.js";import{h as R}from"./useEventHooks-C689a4F7.js";import{u as P,E as A}from"./EventTable-B9wYf13g.js";import{b as D,e as K}from"./tasks-C-QX245z.js";import{c as k,D as Q,J as N,d as V}from"./index-CBS8FBcp.js";import{P as z}from"./PageHeader-Bo0SpcCK.js";import{C as g}from"./CollapsibleSection-D9F01Tny.js";import{L as B}from"./ListToolbar-DL1wEuvL.js";import{S as _}from"./StatusBadge-XQlNFwmH.js";import{L as u,e as H,u as J,c as M}from"./vendor-react-CX88sFS5.js";import{S as O}from"./SwimlaneTimeline-CU2ZD9cC.js";import"./FilterBar-Ck4K4rzu.js";import"./vendor-icons-BNtvBbnj.js";function x({label:t,value:a,mono:r,truncate:s,children:n}){return e.jsxs("div",{children:[e.jsx("p",{className:"text-[10px] font-semibold uppercase tracking-widest text-text-tertiary mb-1",children:t}),n||e.jsx("p",{className:`text-xs text-text-primary ${r?"font-mono":""} ${s?"truncate":""}`,title:s?a:void 0,children:a})]})}function F(t){const a=t.lastIndexOf("-");return a<=0?{taskQueue:t,workflowType:t}:{taskQueue:t.substring(0,a),workflowType:t.substring(a+1)}}function U({execution:t,task:a,escalations:r}){const n=a&&a.workflow_id===t.workflow_id?a.parent_workflow_id:null,{taskQueue:o,workflowType:m}=F(t.workflow_type);return e.jsxs("div",{className:"px-6 py-6 mb-6",children:[e.jsxs("div",{className:"grid grid-cols-2 sm:grid-cols-4 gap-y-4 gap-x-8",children:[e.jsx(x,{label:"Workflow Type",value:m,mono:!0}),e.jsx(x,{label:"Task Queue",value:o,mono:!0}),e.jsx(x,{label:"Start Time",children:t.start_time?e.jsx(k,{date:t.start_time,format:"datetime",className:"text-text-primary"}):e.jsx("span",{className:"text-xs text-text-tertiary",children:"--"})}),e.jsx(x,{label:"End Time",children:t.close_time?e.jsx(k,{date:t.close_time,format:"datetime",className:"text-text-primary"}):e.jsx("span",{className:"text-xs text-text-tertiary",children:"--"})}),e.jsx(x,{label:"Duration",children:e.jsx(Q,{ms:t.duration_ms,className:"font-mono text-text-primary"})}),e.jsx(x,{label:"History Size",value:`${t.summary.total_events} events`}),e.jsx(x,{label:"Activities",value:`${t.summary.activities.completed} / ${t.summary.activities.total}`}),e.jsx(x,{label:"Run ID",value:t.workflow_id,mono:!0,truncate:!0})]}),(n||a||r&&r.length>0)&&e.jsxs("div",{className:"mt-5 pt-4 border-t border-surface-border space-y-3",children:[n&&e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx("span",{className:"text-[10px] font-semibold uppercase tracking-widest text-text-tertiary shrink-0",children:"Parent"}),e.jsx(u,{to:`/workflows/executions/${n}`,className:"text-xs font-mono text-accent hover:underline truncate",title:n,children:n})]}),!1,r&&r.length>0&&e.jsxs("div",{className:"flex items-start gap-2",children:[e.jsx("span",{className:"text-[10px] font-semibold uppercase tracking-widest text-text-tertiary shrink-0 mt-0.5",children:r.length===1?"Escalation":"Escalations"}),e.jsx("div",{className:"flex flex-wrap gap-x-4 gap-y-1",children:r.map(l=>e.jsxs(u,{to:`/escalations/detail/${l.id}`,className:"inline-flex items-center gap-1.5 text-xs font-mono text-accent hover:underline",children:[e.jsx("span",{children:l.type}),e.jsx(_,{status:l.status})]},l.id))})]})]})]})}function q(t){var s;const a=t.events.find(n=>n.event_type==="workflow_execution_started"),r=(s=a==null?void 0:a.attributes)==null?void 0:s.input;return r&&typeof r=="object"?r:null}function Z({execution:t}){const a=q(t),r=t.result,s=(r==null?void 0:r.data)??r??null;return!a&&!s?null:e.jsxs("div",{className:"grid grid-cols-1 md:grid-cols-2 gap-4 mb-6",children:[a!==null&&e.jsx("div",{children:e.jsx(N,{data:a,label:"Input Envelope"})}),s!==null&&e.jsx("div",{children:e.jsx(N,{data:s,label:"Result"})})]})}function G({isRunning:t,hasToolCalls:a,workflowId:r,onAction:s}){const[n,o]=b.useState(!1),m=b.useRef(null);return b.useEffect(()=>{if(!n)return;const l=p=>{m.current&&!m.current.contains(p.target)&&o(!1)};return document.addEventListener("mousedown",l),()=>document.removeEventListener("mousedown",l)},[n]),e.jsxs("div",{className:"relative",ref:m,children:[e.jsx("button",{onClick:()=>o(!n),className:"btn-primary text-xs",children:"Actions"}),n&&e.jsxs("div",{className:"absolute right-0 mt-1 w-44 bg-surface-raised border border-surface-border rounded-md shadow-lg z-10",children:[e.jsx("button",{onClick:()=>{s("restart"),o(!1)},className:"block w-full text-left px-4 py-2 text-xs text-text-secondary hover:bg-surface-hover",children:"Restart Workflow"}),t&&e.jsx("button",{onClick:()=>{s("terminate"),o(!1)},className:"block w-full text-left px-4 py-2 text-xs text-status-error hover:bg-surface-hover",children:"Terminate"}),a&&e.jsx(u,{to:`/mcp/queries/${r}?step=3`,className:"block w-full text-left px-4 py-2 text-xs text-accent hover:bg-surface-hover",onClick:()=>o(!1),children:"Compile into Pipeline"})]})]})}function me(){const{workflowId:t}=H(),{pathname:a}=J();R(t);const r=(a.startsWith("/workflows/durable/"),"Durable Execution"),{data:s,isLoading:n,error:o,refetch:m,isFetching:l}=$(t),{data:p}=D(t),{data:c}=K(t),{data:f}=V(t),T=M(),h=L(),{isCollapsed:w,toggle:j}=P("workflow-execution"),E=d=>{var i;if(d==="terminate")confirm("Are you sure you want to terminate this workflow?")&&h.mutate(t);else if(d==="restart"&&s){const I=s.workflow_id.replace(/-[A-Za-z0-9_-]{20,}$/,""),v=s.events.find(S=>S.event_type==="workflow_execution_started"),y=(i=v==null?void 0:v.attributes)==null?void 0:i.input;y&&sessionStorage.setItem("lt:invoke:prefill",JSON.stringify(y)),T(`/workflows/start?type=${encodeURIComponent(I)}&mode=now`)}};if(n)return e.jsxs("div",{className:"animate-pulse space-y-4",children:[e.jsx("div",{className:"h-8 bg-surface-sunken rounded w-64"}),e.jsx("div",{className:"h-60 bg-surface-sunken rounded"})]});if(o||!s){const d=(o==null?void 0:o.message)??"",i=d.includes("expired")||d.includes("no longer available");return e.jsxs("div",{children:[e.jsx(u,{to:"/workflows/executions",className:"text-xs text-text-tertiary hover:text-text-primary",children:"← Workflows"}),e.jsxs("div",{className:"mt-4 text-center py-8",children:[e.jsx("p",{className:"text-sm text-text-primary mb-1",children:i?"Execution data is no longer available":o?"Unable to load execution":"Execution not found"}),e.jsx("p",{className:"text-xs text-text-tertiary",children:i?"This workflow's underlying job has expired. The task record is preserved, but the execution timeline has been cleaned up.":d||"The workflow could not be resolved."})]})]})}const C=s.status!=="completed"&&s.status!=="failed",W=s.status==="completed"&&s.events.some(d=>{if(d.event_type!=="activity_task_completed")return!1;const i=d.attributes.activity_type;return i==="callDbTool"||i==="callVisionTool"||i==="callMcpTool"||(i==null?void 0:i.startsWith("mcp_"))});return e.jsxs("div",{children:[e.jsx(z,{title:r,actions:e.jsxs("div",{className:"flex items-center gap-3",children:[e.jsx(B,{onRefresh:()=>m(),isFetching:l,apiPath:`/workflow-states/${t}/execution`}),e.jsx(_,{status:s.status}),e.jsx(G,{isRunning:C,hasToolCalls:W,workflowId:t,onAction:E})]})}),e.jsx(U,{execution:s,task:p,childTasks:c==null?void 0:c.tasks,escalations:f==null?void 0:f.escalations}),h.error&&e.jsx("div",{className:"py-3 mb-6",children:e.jsxs("p",{className:"text-xs text-status-error",children:["Terminate failed: ",h.error.message]})}),e.jsxs("div",{className:"space-y-6",children:[e.jsx(g,{title:"Details",sectionKey:"details",isCollapsed:w("details"),onToggle:j,contentClassName:"mt-4 ml-9",children:e.jsx(Z,{execution:s})}),e.jsx(g,{title:"Execution Timeline",sectionKey:"timeline",isCollapsed:w("timeline"),onToggle:j,contentClassName:"mt-4 ml-9",children:e.jsx(O,{events:s.events,childTasks:c==null?void 0:c.tasks})}),e.jsx(g,{title:"Events",sectionKey:"events",isCollapsed:w("events"),onToggle:j,contentClassName:"mt-4 ml-9",children:e.jsx(A,{events:s.events,childTasks:c==null?void 0:c.tasks})})]})]})}export{me as WorkflowExecutionPage};
2
- //# sourceMappingURL=WorkflowExecutionPage-CVGztAdK.js.map