@hotmeshio/long-tail 0.4.2 → 0.4.4
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.
- package/build/api/settings.js +4 -0
- package/build/lib/db/index.d.ts +1 -7
- package/build/lib/db/schemas/008_capability_reaction.sql +12 -0
- package/build/lib/db/schemas/009_file_topic_category.sql +10 -0
- package/build/lib/events/index.d.ts +9 -0
- package/build/lib/events/index.js +15 -0
- package/build/lib/events/nats.d.ts +15 -1
- package/build/lib/events/nats.js +44 -2
- package/build/lib/events/publish.d.ts +10 -0
- package/build/lib/events/publish.js +28 -0
- package/build/modules/config.d.ts +1 -7
- package/build/services/agent/subscription-sql.d.ts +3 -3
- package/build/services/agent/subscription-sql.js +7 -5
- package/build/services/agent/subscriptions.d.ts +3 -1
- package/build/services/agent/subscriptions.js +6 -0
- package/build/services/agent/trigger-registry.js +18 -0
- package/build/services/export/index.js +36 -2
- package/build/services/topics/system-topics.js +29 -0
- package/build/services/yaml-workflow/deployer.d.ts +1 -1
- package/build/services/yaml-workflow/deployer.js +2 -2
- package/build/services/yaml-workflow/invoke.d.ts +2 -0
- package/build/services/yaml-workflow/invoke.js +5 -1
- package/build/start/adapters.js +11 -9
- package/build/start/config.js +4 -0
- package/build/start/workers.js +12 -3
- package/build/system/index.js +16 -0
- package/build/system/mcp-servers/file-storage.js +18 -0
- package/build/system/workflows/capability-invoke/activities.d.ts +12 -0
- package/build/system/workflows/capability-invoke/activities.js +14 -0
- package/build/system/workflows/capability-invoke/index.d.ts +14 -0
- package/build/system/workflows/capability-invoke/index.js +74 -0
- package/build/tsconfig.tsbuildinfo +1 -1
- package/build/types/events.d.ts +13 -1
- package/build/types/startup.d.ts +7 -0
- package/dashboard/dist/assets/{AdminDashboard-C0_qN2h3.js → AdminDashboard-DISq0Tz8.js} +2 -2
- package/dashboard/dist/assets/{AdminDashboard-C0_qN2h3.js.map → AdminDashboard-DISq0Tz8.js.map} +1 -1
- package/dashboard/dist/assets/AgentConfigPage-DqaJpB3K.js +16 -0
- package/dashboard/dist/assets/AgentConfigPage-DqaJpB3K.js.map +1 -0
- package/dashboard/dist/assets/AgentDetailPage-BPzPk2GC.js +4 -0
- package/dashboard/dist/assets/AgentDetailPage-BPzPk2GC.js.map +1 -0
- package/dashboard/dist/assets/AgentsPage-CLcxJWjC.js +2 -0
- package/dashboard/dist/assets/AgentsPage-CLcxJWjC.js.map +1 -0
- package/dashboard/dist/assets/AvailableEscalationsPage-hOcjAAIj.js +2 -0
- package/dashboard/dist/assets/{AvailableEscalationsPage-DrarbHov.js.map → AvailableEscalationsPage-hOcjAAIj.js.map} +1 -1
- package/dashboard/dist/assets/{BotPicker-CvXQwE5Z.js → BotPicker-DuAZgHbJ.js} +2 -2
- package/dashboard/dist/assets/{BotPicker-CvXQwE5Z.js.map → BotPicker-DuAZgHbJ.js.map} +1 -1
- package/dashboard/dist/assets/CapabilitiesPage-PRel6TXd.js +2 -0
- package/dashboard/dist/assets/CapabilitiesPage-PRel6TXd.js.map +1 -0
- package/dashboard/dist/assets/{CollapsibleSection-D9F01Tny.js → CollapsibleSection-DnoUFQVf.js} +2 -2
- package/dashboard/dist/assets/{CollapsibleSection-D9F01Tny.js.map → CollapsibleSection-DnoUFQVf.js.map} +1 -1
- package/dashboard/dist/assets/CopyableId-D0SQ39nR.js +2 -0
- package/dashboard/dist/assets/CopyableId-D0SQ39nR.js.map +1 -0
- package/dashboard/dist/assets/{CredentialsPage-C-rjAIK3.js → CredentialsPage-CqedUU7b.js} +2 -2
- package/dashboard/dist/assets/{CredentialsPage-C-rjAIK3.js.map → CredentialsPage-CqedUU7b.js.map} +1 -1
- package/dashboard/dist/assets/{CronLabel-DnZF8_vw.js → CronLabel-CPEjE-mH.js} +2 -2
- package/dashboard/dist/assets/CronLabel-CPEjE-mH.js.map +1 -0
- package/dashboard/dist/assets/CustomDurationPicker-BIQWzbv0.js +2 -0
- package/dashboard/dist/assets/{CustomDurationPicker-BYDrcsYT.js.map → CustomDurationPicker-BIQWzbv0.js.map} +1 -1
- package/dashboard/dist/assets/{DropZone-BEW3jBzf.js → DropZone-e5EOL5gC.js} +2 -2
- package/dashboard/dist/assets/{DropZone-BEW3jBzf.js.map → DropZone-e5EOL5gC.js.map} +1 -1
- package/dashboard/dist/assets/ElapsedCell-CzVjr74Y.js +2 -0
- package/dashboard/dist/assets/{ElapsedCell-BkiVdGaJ.js.map → ElapsedCell-CzVjr74Y.js.map} +1 -1
- package/dashboard/dist/assets/{EscalationsOverview-Cg2SN0WK.js → EscalationsOverview-Bcrb44xJ.js} +2 -2
- package/dashboard/dist/assets/{EscalationsOverview-Cg2SN0WK.js.map → EscalationsOverview-Bcrb44xJ.js.map} +1 -1
- package/dashboard/dist/assets/EventTable-CGfJU7e1.js +2 -0
- package/dashboard/dist/assets/EventTable-CGfJU7e1.js.map +1 -0
- package/dashboard/dist/assets/{EventTopicPill-By-6sXDp.js → EventTopicPill-xJJnxvlP.js} +2 -2
- package/dashboard/dist/assets/{EventTopicPill-By-6sXDp.js.map → EventTopicPill-xJJnxvlP.js.map} +1 -1
- package/dashboard/dist/assets/{HomePage-74mCQ5nB.js → HomePage-BIzUcGW9.js} +2 -2
- package/dashboard/dist/assets/{HomePage-74mCQ5nB.js.map → HomePage-BIzUcGW9.js.map} +1 -1
- package/dashboard/dist/assets/{ListToolbar-DL1wEuvL.js → ListToolbar-DNAGFe14.js} +2 -2
- package/dashboard/dist/assets/{ListToolbar-DL1wEuvL.js.map → ListToolbar-DNAGFe14.js.map} +1 -1
- package/dashboard/dist/assets/{McpOverview-D34bLMuP.js → McpOverview-BpYk21es.js} +2 -2
- package/dashboard/dist/assets/{McpOverview-D34bLMuP.js.map → McpOverview-BpYk21es.js.map} +1 -1
- package/dashboard/dist/assets/{McpQueryDetailPage-gLGTGX6g.js → McpQueryDetailPage-DlrZiSuL.js} +2 -2
- package/dashboard/dist/assets/{McpQueryDetailPage-gLGTGX6g.js.map → McpQueryDetailPage-DlrZiSuL.js.map} +1 -1
- package/dashboard/dist/assets/{McpQueryPage-wPHJkhEp.js → McpQueryPage-COfPssO7.js} +2 -2
- package/dashboard/dist/assets/{McpQueryPage-wPHJkhEp.js.map → McpQueryPage-COfPssO7.js.map} +1 -1
- package/dashboard/dist/assets/McpRunDetailPage-Cra0nQSw.js +2 -0
- package/dashboard/dist/assets/McpRunDetailPage-Cra0nQSw.js.map +1 -0
- package/dashboard/dist/assets/{McpRunsPage-BUSxdydO.js → McpRunsPage-BlgcGN3k.js} +2 -2
- package/dashboard/dist/assets/{McpRunsPage-BUSxdydO.js.map → McpRunsPage-BlgcGN3k.js.map} +1 -1
- package/dashboard/dist/assets/{OperatorDashboard-CYCl2our.js → OperatorDashboard-BbO6cWzb.js} +2 -2
- package/dashboard/dist/assets/{OperatorDashboard-CYCl2our.js.map → OperatorDashboard-BbO6cWzb.js.map} +1 -1
- package/dashboard/dist/assets/{PageHeader-Bo0SpcCK.js → PageHeader-B_gV_jKk.js} +2 -2
- package/dashboard/dist/assets/{PageHeader-Bo0SpcCK.js.map → PageHeader-B_gV_jKk.js.map} +1 -1
- package/dashboard/dist/assets/{PageHeaderWithStats-7K5BdhOj.js → PageHeaderWithStats-D0I0nrnx.js} +2 -2
- package/dashboard/dist/assets/{PageHeaderWithStats-7K5BdhOj.js.map → PageHeaderWithStats-D0I0nrnx.js.map} +1 -1
- package/dashboard/dist/assets/ProcessDetailPage-s_iV8ICg.js +2 -0
- package/dashboard/dist/assets/{ProcessDetailPage-DzGacZpO.js.map → ProcessDetailPage-s_iV8ICg.js.map} +1 -1
- package/dashboard/dist/assets/{ProcessesListPage-Bmn33g_l.js → ProcessesListPage-MnLMnxAa.js} +2 -2
- package/dashboard/dist/assets/{ProcessesListPage-Bmn33g_l.js.map → ProcessesListPage-MnLMnxAa.js.map} +1 -1
- package/dashboard/dist/assets/{RolePill-BDzPFQUv.js → RolePill-BOBytzrP.js} +2 -2
- package/dashboard/dist/assets/{RolePill-BDzPFQUv.js.map → RolePill-BOBytzrP.js.map} +1 -1
- package/dashboard/dist/assets/{RolesPage-BTtaabkS.js → RolesPage-BH7KASM7.js} +2 -2
- package/dashboard/dist/assets/{RolesPage-BTtaabkS.js.map → RolesPage-BH7KASM7.js.map} +1 -1
- package/dashboard/dist/assets/{RunAsSelector-B1R8nJvE.js → RunAsSelector-_QbJKhlo.js} +2 -2
- package/dashboard/dist/assets/{RunAsSelector-B1R8nJvE.js.map → RunAsSelector-_QbJKhlo.js.map} +1 -1
- package/dashboard/dist/assets/{ServerName-CEOFF7UG.js → ServerName-uqa4eBqm.js} +2 -2
- package/dashboard/dist/assets/{ServerName-CEOFF7UG.js.map → ServerName-uqa4eBqm.js.map} +1 -1
- package/dashboard/dist/assets/SwimlaneTimeline-og79Llvs.js +2 -0
- package/dashboard/dist/assets/SwimlaneTimeline-og79Llvs.js.map +1 -0
- package/dashboard/dist/assets/{TagInput-VBY0xIwb.js → TagInput-D3f11sbM.js} +2 -2
- package/dashboard/dist/assets/{TagInput-VBY0xIwb.js.map → TagInput-D3f11sbM.js.map} +1 -1
- package/dashboard/dist/assets/{TaskDetailPage-CZw_F8y4.js → TaskDetailPage-DPV4ySd9.js} +2 -2
- package/dashboard/dist/assets/{TaskDetailPage-CZw_F8y4.js.map → TaskDetailPage-DPV4ySd9.js.map} +1 -1
- package/dashboard/dist/assets/{TaskQueuePill-DZykFijh.js → TaskQueuePill-Bc45J7X1.js} +2 -2
- package/dashboard/dist/assets/{TaskQueuePill-DZykFijh.js.map → TaskQueuePill-Bc45J7X1.js.map} +1 -1
- package/dashboard/dist/assets/{TasksListPage-D-vHndyV.js → TasksListPage-BYj3OqUi.js} +2 -2
- package/dashboard/dist/assets/{TasksListPage-D-vHndyV.js.map → TasksListPage-BYj3OqUi.js.map} +1 -1
- package/dashboard/dist/assets/TimeAgo-W7TdJpV-.js +2 -0
- package/dashboard/dist/assets/{TimeAgo-B6Gz4RAU.js.map → TimeAgo-W7TdJpV-.js.map} +1 -1
- package/dashboard/dist/assets/{TimestampCell-DZu9PtN2.js → TimestampCell-Bi2nc9Q_.js} +2 -2
- package/dashboard/dist/assets/{TimestampCell-DZu9PtN2.js.map → TimestampCell-Bi2nc9Q_.js.map} +1 -1
- package/dashboard/dist/assets/{ToolPill-RP2Tvlrx.js → ToolPill-CuoXcmhv.js} +2 -2
- package/dashboard/dist/assets/{ToolPill-RP2Tvlrx.js.map → ToolPill-CuoXcmhv.js.map} +1 -1
- package/dashboard/dist/assets/ToolTestPanel-Dl3C53zb.js +2 -0
- package/dashboard/dist/assets/ToolTestPanel-Dl3C53zb.js.map +1 -0
- package/dashboard/dist/assets/{TopicDetailPage-C5ZUZpU5.js → TopicDetailPage-CjaZn4WP.js} +2 -2
- package/dashboard/dist/assets/{TopicDetailPage-C5ZUZpU5.js.map → TopicDetailPage-CjaZn4WP.js.map} +1 -1
- package/dashboard/dist/assets/{TopicsPage-Cbc0nVj9.js → TopicsPage-BfsJEC1p.js} +2 -2
- package/dashboard/dist/assets/{TopicsPage-Cbc0nVj9.js.map → TopicsPage-BfsJEC1p.js.map} +1 -1
- package/dashboard/dist/assets/{UserName-YUoNrFAq.js → UserName-b4baWHM_.js} +2 -2
- package/dashboard/dist/assets/{UserName-YUoNrFAq.js.map → UserName-b4baWHM_.js.map} +1 -1
- package/dashboard/dist/assets/WorkflowExecutionPage-BGmJnXnk.js +2 -0
- package/dashboard/dist/assets/WorkflowExecutionPage-BGmJnXnk.js.map +1 -0
- package/dashboard/dist/assets/WorkflowPill-Bg2-0Hkg.js +2 -0
- package/dashboard/dist/assets/WorkflowPill-Bg2-0Hkg.js.map +1 -0
- package/dashboard/dist/assets/{WorkflowsDashboard-BFzCyvgv.js → WorkflowsDashboard-BzH7jMRu.js} +2 -2
- package/dashboard/dist/assets/{WorkflowsDashboard-BFzCyvgv.js.map → WorkflowsDashboard-BzH7jMRu.js.map} +1 -1
- package/dashboard/dist/assets/{WorkflowsOverview-C_y7JCg2.js → WorkflowsOverview-BcUgBvjD.js} +2 -2
- package/dashboard/dist/assets/{WorkflowsOverview-C_y7JCg2.js.map → WorkflowsOverview-BcUgBvjD.js.map} +1 -1
- package/dashboard/dist/assets/{YamlWorkflowsPage-CYQjp3JI.js → YamlWorkflowsPage-OhpCQJ0l.js} +2 -2
- package/dashboard/dist/assets/{YamlWorkflowsPage-CYQjp3JI.js.map → YamlWorkflowsPage-OhpCQJ0l.js.map} +1 -1
- package/dashboard/dist/assets/{agents-2pDPv2Ww.js → agents-Ce3HmPz4.js} +2 -2
- package/dashboard/dist/assets/{agents-2pDPv2Ww.js.map → agents-Ce3HmPz4.js.map} +1 -1
- package/dashboard/dist/assets/{bots-2uGZ2l7A.js → bots-B0BomNnf.js} +2 -2
- package/dashboard/dist/assets/{bots-2uGZ2l7A.js.map → bots-B0BomNnf.js.map} +1 -1
- package/dashboard/dist/assets/capabilities-t-w5N9K9.js +2 -0
- package/dashboard/dist/assets/capabilities-t-w5N9K9.js.map +1 -0
- package/dashboard/dist/assets/{controlplane-CQ29M7lK.js → controlplane-BrtAZnJM.js} +2 -2
- package/dashboard/dist/assets/{controlplane-CQ29M7lK.js.map → controlplane-BrtAZnJM.js.map} +1 -1
- package/dashboard/dist/assets/{escalation-DWOUjrgL.js → escalation-DilnDxw2.js} +2 -2
- package/dashboard/dist/assets/{escalation-DWOUjrgL.js.map → escalation-DilnDxw2.js.map} +1 -1
- package/dashboard/dist/assets/{escalation-columns-WrgLIl-W.js → escalation-columns-C0slywOY.js} +2 -2
- package/dashboard/dist/assets/{escalation-columns-WrgLIl-W.js.map → escalation-columns-C0slywOY.js.map} +1 -1
- package/dashboard/dist/assets/{helpers-LN5b1KBx.js → helpers-BMvPqQr8.js} +2 -2
- package/dashboard/dist/assets/{helpers-LN5b1KBx.js.map → helpers-BMvPqQr8.js.map} +1 -1
- package/dashboard/dist/assets/{index-ugekH3E2.js → index-30-pN97P.js} +2 -2
- package/dashboard/dist/assets/{index-ugekH3E2.js.map → index-30-pN97P.js.map} +1 -1
- package/dashboard/dist/assets/index-BPxglOYm.css +1 -0
- package/dashboard/dist/assets/{index-CFJc47B8.js → index-Bl9wzQ8Q.js} +2 -2
- package/dashboard/dist/assets/{index-CFJc47B8.js.map → index-Bl9wzQ8Q.js.map} +1 -1
- package/dashboard/dist/assets/{index-C_A76Dl1.js → index-BxOuF0hm.js} +2 -2
- package/dashboard/dist/assets/{index-C_A76Dl1.js.map → index-BxOuF0hm.js.map} +1 -1
- package/dashboard/dist/assets/{index-DqFfgd-7.js → index-Ck2QdJXT.js} +2 -2
- package/dashboard/dist/assets/{index-DqFfgd-7.js.map → index-Ck2QdJXT.js.map} +1 -1
- package/dashboard/dist/assets/{index-CvzfRxnj.js → index-DNmlrCAp.js} +2 -2
- package/dashboard/dist/assets/{index-CvzfRxnj.js.map → index-DNmlrCAp.js.map} +1 -1
- package/dashboard/dist/assets/index-DPW_i3fH.js +6 -0
- package/dashboard/dist/assets/index-DPW_i3fH.js.map +1 -0
- package/dashboard/dist/assets/{index-CBS8FBcp.js → index-DZX-E_3q.js} +24 -24
- package/dashboard/dist/assets/index-DZX-E_3q.js.map +1 -0
- package/dashboard/dist/assets/{index-CovZsMow.js → index-D_wqdvG_.js} +2 -2
- package/dashboard/dist/assets/{index-CovZsMow.js.map → index-D_wqdvG_.js.map} +1 -1
- package/dashboard/dist/assets/{index-CdNXBj7w.js → index-DnsVYMMg.js} +2 -2
- package/dashboard/dist/assets/{index-CdNXBj7w.js.map → index-DnsVYMMg.js.map} +1 -1
- package/dashboard/dist/assets/{index-BMo7wCw8.js → index-H5TlloTk.js} +2 -2
- package/dashboard/dist/assets/{index-BMo7wCw8.js.map → index-H5TlloTk.js.map} +1 -1
- package/dashboard/dist/assets/{index-dzxsXeMO.js → index-f-0Duls3.js} +2 -2
- package/dashboard/dist/assets/{index-dzxsXeMO.js.map → index-f-0Duls3.js.map} +1 -1
- package/dashboard/dist/assets/index-pIMl0mYp.js +2 -0
- package/dashboard/dist/assets/{index-CryoNbg0.js.map → index-pIMl0mYp.js.map} +1 -1
- package/dashboard/dist/assets/{index-J0dMfAmE.js → index-qNuxC8kX.js} +2 -2
- package/dashboard/dist/assets/{index-J0dMfAmE.js.map → index-qNuxC8kX.js.map} +1 -1
- package/dashboard/dist/assets/{knowledge-BhZk3Wy9.js → knowledge-D01NdF5h.js} +2 -2
- package/dashboard/dist/assets/{knowledge-BhZk3Wy9.js.map → knowledge-D01NdF5h.js.map} +1 -1
- package/dashboard/dist/assets/{mcp-FOHNY7Zj.js → mcp-CjXjDZI0.js} +2 -2
- package/dashboard/dist/assets/{mcp-FOHNY7Zj.js.map → mcp-CjXjDZI0.js.map} +1 -1
- package/dashboard/dist/assets/{mcp-query-BoNH5uCn.js → mcp-query-LlKQcWLJ.js} +2 -2
- package/dashboard/dist/assets/{mcp-query-BoNH5uCn.js.map → mcp-query-LlKQcWLJ.js.map} +1 -1
- package/dashboard/dist/assets/{mcp-runs-BhkGaw2y.js → mcp-runs-C0C54hU6.js} +2 -2
- package/dashboard/dist/assets/{mcp-runs-BhkGaw2y.js.map → mcp-runs-C0C54hU6.js.map} +1 -1
- package/dashboard/dist/assets/{namespaces-duQCQRHq.js → namespaces-6qWDXXJV.js} +2 -2
- package/dashboard/dist/assets/{namespaces-duQCQRHq.js.map → namespaces-6qWDXXJV.js.map} +1 -1
- package/dashboard/dist/assets/{roles-DW6lI_g5.js → roles-Dtj0uabn.js} +2 -2
- package/dashboard/dist/assets/{roles-DW6lI_g5.js.map → roles-Dtj0uabn.js.map} +1 -1
- package/dashboard/dist/assets/{tasks-C-QX245z.js → tasks-BevFBjZq.js} +2 -2
- package/dashboard/dist/assets/{tasks-C-QX245z.js.map → tasks-BevFBjZq.js.map} +1 -1
- package/dashboard/dist/assets/{topics-CAnsyo3w.js → topics-BMG5tx2g.js} +2 -2
- package/dashboard/dist/assets/{topics-CAnsyo3w.js.map → topics-BMG5tx2g.js.map} +1 -1
- package/dashboard/dist/assets/useCollapsedSections-BU5HULGs.js +2 -0
- package/dashboard/dist/assets/useCollapsedSections-BU5HULGs.js.map +1 -0
- package/dashboard/dist/assets/useEventHooks-CPyvFlVR.js +2 -0
- package/dashboard/dist/assets/useEventHooks-CPyvFlVR.js.map +1 -0
- package/dashboard/dist/assets/{useYamlActivityEvents-BTHFGIsD.js → useYamlActivityEvents-Bll8NPNQ.js} +2 -2
- package/dashboard/dist/assets/{useYamlActivityEvents-BTHFGIsD.js.map → useYamlActivityEvents-Bll8NPNQ.js.map} +1 -1
- package/dashboard/dist/assets/{users--D3LoFOD.js → users-XZ349b0r.js} +2 -2
- package/dashboard/dist/assets/{users--D3LoFOD.js.map → users-XZ349b0r.js.map} +1 -1
- package/dashboard/dist/assets/{vendor-icons-BNtvBbnj.js → vendor-icons-B_Yla7iD.js} +2 -2
- package/dashboard/dist/assets/{vendor-icons-BNtvBbnj.js.map → vendor-icons-B_Yla7iD.js.map} +1 -1
- package/dashboard/dist/assets/{workflows-MpNdzreD.js → workflows-Do-Eiv8f.js} +2 -2
- package/dashboard/dist/assets/{workflows-MpNdzreD.js.map → workflows-Do-Eiv8f.js.map} +1 -1
- package/dashboard/dist/assets/{yaml-workflows-CFhnJzQy.js → yaml-workflows-DolGRQ5f.js} +2 -2
- package/dashboard/dist/assets/{yaml-workflows-CFhnJzQy.js.map → yaml-workflows-DolGRQ5f.js.map} +1 -1
- package/dashboard/dist/index.html +3 -3
- package/docs/agents.md +38 -10
- package/docs/api/http/agents.md +12 -10
- package/docs/api/http/settings.md +4 -0
- package/docs/api/sdk/agents.md +4 -2
- package/docs/api/sdk/settings.md +2 -1
- package/docs/dashboard.md +1 -1
- package/docs/events.md +7 -0
- package/docs/hitl-guide.md +560 -0
- package/package.json +2 -2
- package/dashboard/dist/assets/AgentConfigPage-DG1zOIiz.js +0 -13
- package/dashboard/dist/assets/AgentConfigPage-DG1zOIiz.js.map +0 -1
- package/dashboard/dist/assets/AgentDetailPage-B5kaAJDM.js +0 -4
- package/dashboard/dist/assets/AgentDetailPage-B5kaAJDM.js.map +0 -1
- package/dashboard/dist/assets/AgentsPage-DJWSuoJA.js +0 -2
- package/dashboard/dist/assets/AgentsPage-DJWSuoJA.js.map +0 -1
- package/dashboard/dist/assets/AvailableEscalationsPage-DrarbHov.js +0 -2
- package/dashboard/dist/assets/CapabilitiesPage-BiL9QUxI.js +0 -2
- package/dashboard/dist/assets/CapabilitiesPage-BiL9QUxI.js.map +0 -1
- package/dashboard/dist/assets/CopyableId-DmLF-RqZ.js +0 -2
- package/dashboard/dist/assets/CopyableId-DmLF-RqZ.js.map +0 -1
- package/dashboard/dist/assets/CronLabel-DnZF8_vw.js.map +0 -1
- package/dashboard/dist/assets/CustomDurationPicker-BYDrcsYT.js +0 -2
- package/dashboard/dist/assets/ElapsedCell-BkiVdGaJ.js +0 -2
- package/dashboard/dist/assets/EventTable-B9wYf13g.js +0 -2
- package/dashboard/dist/assets/EventTable-B9wYf13g.js.map +0 -1
- package/dashboard/dist/assets/McpRunDetailPage-SoXudCbq.js +0 -2
- package/dashboard/dist/assets/McpRunDetailPage-SoXudCbq.js.map +0 -1
- package/dashboard/dist/assets/ProcessDetailPage-DzGacZpO.js +0 -2
- package/dashboard/dist/assets/SwimlaneTimeline-CU2ZD9cC.js +0 -2
- package/dashboard/dist/assets/SwimlaneTimeline-CU2ZD9cC.js.map +0 -1
- package/dashboard/dist/assets/TimeAgo-B6Gz4RAU.js +0 -2
- package/dashboard/dist/assets/ToolTestPanel-D4cgYW2p.js +0 -2
- package/dashboard/dist/assets/ToolTestPanel-D4cgYW2p.js.map +0 -1
- package/dashboard/dist/assets/WorkflowExecutionPage-CVGztAdK.js +0 -2
- package/dashboard/dist/assets/WorkflowExecutionPage-CVGztAdK.js.map +0 -1
- package/dashboard/dist/assets/WorkflowPill-pPuGH8v9.js +0 -2
- package/dashboard/dist/assets/WorkflowPill-pPuGH8v9.js.map +0 -1
- package/dashboard/dist/assets/index-CBS8FBcp.js.map +0 -1
- package/dashboard/dist/assets/index-ChGBlYKj.css +0 -1
- package/dashboard/dist/assets/index-CryoNbg0.js +0 -2
- package/dashboard/dist/assets/index-DDxZOINn.js +0 -5
- package/dashboard/dist/assets/index-DDxZOINn.js.map +0 -1
- package/dashboard/dist/assets/settings-wTRbazzw.js +0 -2
- package/dashboard/dist/assets/settings-wTRbazzw.js.map +0 -1
- package/dashboard/dist/assets/useEventHooks-C689a4F7.js +0 -2
- package/dashboard/dist/assets/useEventHooks-C689a4F7.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.
|
|
3
|
+
"version": "0.4.4",
|
|
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.
|
|
73
|
+
"@hotmeshio/hotmesh": "^0.19.3",
|
|
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,13 +0,0 @@
|
|
|
1
|
-
import{j as e,a as g}from"./vendor-query-B2UbickB.js";import{a as te,d as se,b as ae}from"./agents-2pDPv2Ww.js";import{C as ne,u as ie,a as le,b as ce,c as re}from"./CronLabel-DnZF8_vw.js";import{P as oe}from"./PageHeader-Bo0SpcCK.js";import{B as P,d as L,R as z,Y as $,n as B,a4 as U,a as G,aF as K,K as de}from"./vendor-icons-BNtvBbnj.js";import{e as xe}from"./knowledge-BhZk3Wy9.js";import{c as pe}from"./index-CBS8FBcp.js";import{R as H}from"./RunAsSelector-B1R8nJvE.js";import{u as Q}from"./workflows-MpNdzreD.js";import{u as he}from"./topics-CAnsyo3w.js";import{u as me}from"./yaml-workflows-CFhnJzQy.js";import{E as ue}from"./EventTopicPill-By-6sXDp.js";import{W as F}from"./WorkflowPill-pPuGH8v9.js";import{e as ve,c as je,f as ge}from"./vendor-react-CX88sFS5.js";import"./BotPicker-CvXQwE5Z.js";import"./bots-2uGZ2l7A.js";const J={name:"",description:"",goals:"",rules:"",user_id:"",knowledge_domain:"",subscriptions:[],schedules:[]},fe={topic:"",filter:"",reaction_type:"durable",workflow_type:"",pipeline_id:"",mcp_prompt:"",input_mapping:"{}",execute_as:"",enabled:!0},ye={cron:"0 * * * *",reaction_type:"durable",workflow_type:"",pipeline_id:"",envelope:"{}",execute_as:""};function be(t,s){var l,d,h;const o=(d=(l=t.behaviors)==null?void 0:l.schedules)!=null&&d.length?t.behaviors.schedules.map(i=>({cron:i.cron||"",reaction_type:i.reaction_type||"durable",workflow_type:i.workflow_type||"",pipeline_id:i.pipeline_id||"",envelope:i.envelope?JSON.stringify(i.envelope,null,2):"{}",execute_as:i.execute_as||""})):(h=t.behaviors)!=null&&h.cron?[{cron:t.behaviors.cron,reaction_type:"durable",workflow_type:t.workflow_type??"",pipeline_id:"",envelope:"{}",execute_as:""}]:[];return{name:t.id,description:t.description??"",goals:t.goals??"",rules:t.rules??"",user_id:t.user_id??"",knowledge_domain:t.knowledge_domain??"",subscriptions:s.map(i=>({id:i.id,topic:i.topic,filter:i.filter?JSON.stringify(i.filter,null,2):"",reaction_type:i.reaction_type,workflow_type:i.workflow_type??"",pipeline_id:i.pipeline_id??"",mcp_prompt:i.mcp_prompt??"",input_mapping:JSON.stringify(i.input_mapping??{},null,2),execute_as:i.execute_as??"",enabled:i.enabled})),schedules:o}}function we(t){var o;const s={};return t.schedules.length>0&&(s.schedules=t.schedules.map(l=>({cron:l.cron,reaction_type:l.reaction_type||"durable",workflow_type:l.reaction_type==="pipeline"?void 0:l.workflow_type,pipeline_id:l.reaction_type==="pipeline"?l.pipeline_id:void 0,envelope:M(l.envelope)??{},execute_as:l.execute_as||void 0})),s.cron=t.schedules[0].cron),{id:t.name,description:t.description||void 0,goals:t.goals||void 0,rules:t.rules||void 0,user_id:t.user_id||void 0,knowledge_domain:t.knowledge_domain||void 0,behaviors:s,workflow_type:((o=t.schedules[0])==null?void 0:o.workflow_type)||void 0,pipeline_id:void 0}}function Ne(t){return t.subscriptions.map(s=>({id:s.id,topic:s.topic,filter:s.filter?M(s.filter):void 0,reaction_type:s.reaction_type,workflow_type:s.workflow_type||void 0,pipeline_id:s.pipeline_id||void 0,mcp_prompt:s.mcp_prompt||void 0,input_mapping:M(s.input_mapping)??{},execute_as:s.execute_as||void 0,enabled:s.enabled}))}function M(t){if(t.trim())try{return JSON.parse(t)}catch{return}}const k="section-header mt-[3em] first:mt-0",b="label",f="hint",v="input",V="input-json w-full";function ke({form:t,set:s}){return e.jsxs("div",{className:"space-y-8",children:[e.jsxs("div",{className:"border-l-2 border-accent/30 pl-3 py-1 flex items-start justify-between",children:[e.jsx("p",{className:"text-[12px] text-text-secondary italic leading-relaxed",children:"Give your agent a name and describe what it does. The name appears everywhere — in events, logs, and the dashboard."}),e.jsx("button",{onClick:()=>{window.location.hash="#docs:agents.md:identity"},className:"text-text-quaternary hover:text-accent transition-colors shrink-0 ml-3",title:"Docs: Identity",children:e.jsx(P,{className:"w-3 h-3",strokeWidth:1.5})})]}),e.jsxs("div",{children:[e.jsx("label",{className:b,children:"Name *"}),e.jsx("input",{type:"text",value:t.name,onChange:o=>s("name",o.target.value),placeholder:"health-monitor",className:v}),e.jsx("p",{className:f,children:"Lowercase, kebab-case. This appears everywhere."})]}),e.jsxs("div",{children:[e.jsx("label",{className:b,children:"Description"}),e.jsx("input",{type:"text",value:t.description,onChange:o=>s("description",o.target.value),placeholder:"Watches for workflow failures and captures diagnostics",className:v}),e.jsx("p",{className:f,children:"One sentence that explains what this agent does."})]})]})}function _e({form:t,set:s}){return e.jsxs("div",{className:"space-y-8",children:[e.jsxs("div",{className:"border-l-2 border-accent/30 pl-3 py-1 flex items-start justify-between",children:[e.jsx("p",{className:"text-[12px] text-text-secondary italic leading-relaxed",children:"Goals define what drives the agent. Rules define what constrains it. Together they give automation judgment."}),e.jsx("button",{onClick:()=>{window.location.hash="#docs:agents.md:motivation"},className:"text-text-quaternary hover:text-accent transition-colors shrink-0 ml-3",title:"Docs: Motivation",children:e.jsx(P,{className:"w-3 h-3",strokeWidth:1.5})})]}),e.jsxs("div",{className:"grid grid-cols-2 gap-8",children:[e.jsxs("div",{children:[e.jsx("label",{className:b,children:"Goals"}),e.jsx("textarea",{value:t.goals,onChange:o=>s("goals",o.target.value),placeholder:"Detect failures early, capture diagnostics, alert before cascading",rows:4,className:`${v} resize-none`}),e.jsx("p",{className:f,children:"Primary motivation. What the agent is trying to achieve."})]}),e.jsxs("div",{children:[e.jsx("label",{className:b,children:"Rules"}),e.jsx("textarea",{value:t.rules,onChange:o=>s("rules",o.target.value),placeholder:"Never auto-restart failed workflows. Always escalate to humans.",rows:4,className:`${v} resize-none`}),e.jsx("p",{className:f,children:"Guardrails. What it must never do, even when goals suggest it should."})]})]})]})}function Ce({form:t,set:s}){const{data:o}=xe(),l=(o==null?void 0:o.domains)??[];return e.jsxs("div",{className:"space-y-6",children:[e.jsxs("div",{className:"border-l-2 border-accent/30 pl-3 py-1 flex items-start justify-between",children:[e.jsx("p",{className:"text-[12px] text-text-secondary italic leading-relaxed",children:"Assign a knowledge domain — the agent's memory. It stores context here over time."}),e.jsx("button",{onClick:()=>{window.location.hash="#docs:agents.md:knowledge"},className:"text-text-quaternary hover:text-accent transition-colors shrink-0 ml-3",title:"Docs: Knowledge",children:e.jsx(P,{className:"w-3 h-3",strokeWidth:1.5})})]}),e.jsxs("div",{children:[e.jsx("label",{className:b,children:"Knowledge Domain"}),e.jsx("input",{type:"text",value:t.knowledge_domain,onChange:d=>s("knowledge_domain",d.target.value),placeholder:"e.g., system-health, content-review, vendor-data",className:v,list:"domain-suggestions"}),e.jsx("datalist",{id:"domain-suggestions",children:l.map(d=>e.jsx("option",{value:d.domain},d.domain))}),e.jsx("p",{className:f,children:"Choose an existing domain to share memory, or type a new name to create one."})]}),l.length>0&&e.jsxs("div",{children:[e.jsx("label",{className:b,children:"Existing Domains"}),e.jsx("div",{className:"space-y-0.5 mt-2",children:l.map(d=>e.jsxs("button",{type:"button",onClick:()=>s("knowledge_domain",d.domain),className:`w-full flex items-center gap-3 px-3 py-2 rounded-md text-left transition-colors ${t.knowledge_domain===d.domain?"border-l-2 border-l-accent bg-accent/5":"hover:bg-surface-hover border-l-2 border-l-transparent"}`,children:[e.jsx(L,{className:"w-3.5 h-3.5 text-text-quaternary shrink-0",strokeWidth:1.5}),e.jsx("span",{className:"text-sm text-text-primary flex-1",children:d.domain}),e.jsxs("span",{className:"text-[10px] text-text-quaternary",children:[d.count," entries"]}),e.jsx("span",{className:"text-[9px] text-text-quaternary",children:e.jsx(pe,{date:d.latest})})]},d.domain))})]})]})}const Se={task:"bg-blue-400/15 text-blue-400",workflow:"bg-accent/15 text-accent",escalation:"bg-amber-400/15 text-amber-400",activity:"bg-cyan-400/15 text-cyan-400",knowledge:"bg-violet-400/15 text-violet-400",agent:"bg-emerald-400/15 text-emerald-400",app:"bg-rose-400/15 text-rose-400",milestone:"bg-violet-400/15 text-violet-400"};function Ae({form:t,set:s}){const{data:o}=Q(),{data:l}=he({limit:200}),d=(l==null?void 0:l.topics)??[],h=(o??[]).filter(a=>a.invocable).map(a=>a.workflow_type),[i,j]=g.useState(0),x=(a,m,S)=>{const W=[...t.subscriptions];W[a]={...W[a],[m]:S},s("subscriptions",W)},w=()=>{s("subscriptions",[...t.subscriptions,{...fe}]),j(t.subscriptions.length)},_=a=>{s("subscriptions",t.subscriptions.filter((m,S)=>S!==a)),i>=t.subscriptions.length-1&&j(Math.max(0,t.subscriptions.length-2))},r=t.subscriptions,c=r[i],n=a=>!!a.topic&&(a.reaction_type==="durable"?!!a.workflow_type:a.reaction_type==="pipeline"?!!a.pipeline_id:!!a.mcp_prompt),p=c?d.find(a=>a.topic===c.topic):void 0;return r.length===0?e.jsxs("div",{className:"max-w-xl",children:[e.jsxs("div",{className:"border-l-2 border-accent/30 pl-3 py-1 flex items-start justify-between mb-8",children:[e.jsx("p",{className:"text-[12px] text-text-secondary italic leading-relaxed",children:"Optional. When a matching event is published, the agent runs the configured workflow with the event payload."}),e.jsx("button",{onClick:()=>{window.location.hash="#docs:agents.md:subscriptions"},className:"text-text-quaternary hover:text-accent transition-colors shrink-0 ml-3",title:"Docs: Subscriptions",children:e.jsx(P,{className:"w-3 h-3",strokeWidth:1.5})})]}),e.jsxs("div",{className:"flex flex-col items-center justify-center py-12 text-center",children:[e.jsx(z,{className:"w-8 h-8 text-text-quaternary/40 mb-3",strokeWidth:1}),e.jsx("p",{className:"text-sm text-text-tertiary mb-2",children:"No event subscriptions yet"}),e.jsx("p",{className:"text-[11px] text-text-quaternary max-w-sm mb-6",children:"Each subscription listens for a topic pattern and runs a workflow when it matches."}),e.jsxs("button",{onClick:w,className:"flex items-center gap-2 text-xs text-accent hover:text-accent-hover transition-colors",children:[e.jsx($,{className:"w-3.5 h-3.5"})," Add first subscription"]})]})]}):e.jsxs("div",{children:[e.jsxs("div",{className:"border-l-2 border-accent/30 pl-3 py-1 flex items-start justify-between mb-6",children:[e.jsx("p",{className:"text-[12px] text-text-secondary italic leading-relaxed",children:"Optional. When a matching event is published, the agent runs the configured workflow with the event payload."}),e.jsx("button",{onClick:()=>{window.location.hash="#docs:agents.md:subscriptions"},className:"text-text-quaternary hover:text-accent transition-colors shrink-0 ml-3",title:"Docs: Subscriptions",children:e.jsx(P,{className:"w-3 h-3",strokeWidth:1.5})})]}),e.jsxs("div",{className:"flex gap-8",children:[e.jsxs("div",{className:"w-52 shrink-0 space-y-0.5",children:[r.map((a,m)=>e.jsxs("div",{className:`group/sub flex items-center rounded-md transition-colors ${i===m?"bg-accent/10 text-accent":"text-text-tertiary hover:text-text-primary hover:bg-surface-hover"}`,children:[e.jsxs("button",{onClick:()=>j(m),className:"flex-1 text-left px-3 py-2 min-w-0",children:[e.jsxs("div",{className:"flex items-center gap-1.5",children:[e.jsx("span",{className:`w-1.5 h-1.5 rounded-full shrink-0 ${n(a)?"bg-emerald-400":"bg-zinc-500"}`}),e.jsx("span",{className:"text-[11px] font-mono truncate",children:a.topic||"new subscription"})]}),e.jsxs("span",{className:"text-[9px] text-text-quaternary ml-3",children:["→ ",a.workflow_type||a.reaction_type]})]}),e.jsx("button",{onClick:S=>{S.stopPropagation(),confirm(`Remove subscription "${a.topic||"new"}"?
|
|
2
|
-
|
|
3
|
-
This takes effect when you save.`)&&_(m)},className:"opacity-0 group-hover/sub:opacity-100 px-2 text-text-quaternary hover:text-red-400 transition-all",title:"Remove",children:e.jsx(B,{className:"w-3 h-3"})})]},m)),e.jsxs("button",{onClick:w,className:"w-full flex items-center gap-1.5 px-3 py-2 text-[11px] text-accent hover:text-accent-hover transition-colors",children:[e.jsx($,{className:"w-3 h-3"})," Add"]})]}),c&&e.jsxs("div",{className:"flex-1 min-w-0 space-y-12",children:[e.jsxs("div",{children:[e.jsx("label",{className:k,children:"When this event fires"}),e.jsx(Pe,{value:c.topic,onChange:a=>x(i,"topic",a),topics:d}),(p==null?void 0:p.payload_schema)&&e.jsxs("div",{className:"mt-3 p-3 rounded-md bg-surface-sunken border border-surface-border",children:[e.jsx("p",{className:"text-[10px] text-text-quaternary mb-1 uppercase tracking-wider font-medium",children:"Payload Schema"}),e.jsx("pre",{className:"text-[11px] font-mono text-text-secondary whitespace-pre-wrap overflow-x-auto max-h-40",children:JSON.stringify(p.payload_schema.properties??p.payload_schema,null,2)}),p.description&&e.jsx("p",{className:"text-[10px] text-text-tertiary mt-2 italic",children:p.description})]})]}),e.jsxs("div",{className:"grid grid-cols-3 gap-6",children:[e.jsxs("div",{className:"space-y-4",children:[e.jsx("label",{className:k,children:"Run this workflow"}),e.jsx("div",{className:"flex gap-3",children:["durable","pipeline","mcp_query"].map(a=>e.jsxs("label",{className:"flex items-center gap-1 text-[11px] text-text-secondary cursor-pointer",children:[e.jsx("input",{type:"radio",name:"reaction",checked:c.reaction_type===a,onChange:()=>x(i,"reaction_type",a),className:"accent-accent w-3 h-3"}),a==="durable"?"Workflow":a==="pipeline"?"Pipeline":"MCP Query"]},a))}),c.reaction_type==="durable"&&e.jsxs("div",{children:[e.jsx("label",{className:b,children:"Workflow *"}),e.jsxs("select",{value:c.workflow_type,onChange:a=>x(i,"workflow_type",a.target.value),className:v,children:[e.jsx("option",{value:"",children:"Select..."}),h.map(a=>e.jsx("option",{value:a,children:a},a))]})]}),c.reaction_type==="pipeline"&&e.jsxs("div",{children:[e.jsx("label",{className:b,children:"Pipeline ID *"}),e.jsx("input",{type:"text",value:c.pipeline_id,onChange:a=>x(i,"pipeline_id",a.target.value),placeholder:"UUID",className:`${v} font-mono text-xs`})]}),c.reaction_type==="mcp_query"&&e.jsxs("div",{children:[e.jsx("label",{className:b,children:"Prompt *"}),e.jsx("textarea",{value:c.mcp_prompt,onChange:a=>x(i,"mcp_prompt",a.target.value),placeholder:"Analyze the error...",rows:2,className:`${v} resize-none`})]})]}),e.jsxs("div",{children:[e.jsx("label",{className:k,children:"As identity"}),e.jsx(H,{selected:c.execute_as,onChange:a=>x(i,"execute_as",a)}),e.jsx("p",{className:f,children:"Identity used when invoking the workflow."})]}),e.jsxs("div",{children:[e.jsx("label",{className:k,children:"But only if"}),e.jsx("input",{type:"text",value:c.filter,onChange:a=>x(i,"filter",a.target.value),placeholder:"No filter (all matching events)",className:`${v} font-mono text-xs`}),e.jsxs("p",{className:f,children:["JSON filter against event.data, e.g. ",'{"status": 422}']})]})]}),e.jsxs("div",{children:[e.jsx("label",{className:k,children:"With this data"}),e.jsx("textarea",{value:c.input_mapping,onChange:a=>x(i,"input_mapping",a.target.value),rows:10,className:V,placeholder:`{
|
|
4
|
-
"data": {
|
|
5
|
-
"orderId": "{event.data.orderId}",
|
|
6
|
-
"error": "{event.data.error}"
|
|
7
|
-
}
|
|
8
|
-
}`}),e.jsxs("p",{className:f,children:["Maps event fields to workflow input. ","{event.data.fieldName}"," resolves at runtime."]})]})]})]})]})}function Pe({value:t,onChange:s,topics:o}){const[l,d]=g.useState(!1),[h,i]=g.useState(""),j=g.useRef(null),x=g.useRef(null);g.useEffect(()=>{function r(c){x.current&&!x.current.contains(c.target)&&d(!1)}return l&&document.addEventListener("mousedown",r),()=>document.removeEventListener("mousedown",r)},[l]);const w=l?h:t,_=o.filter(r=>r.topic.toLowerCase().includes((l?h:"").toLowerCase())||(r.description??"").toLowerCase().includes((l?h:"").toLowerCase()));return e.jsxs("div",{ref:x,className:"relative",children:[e.jsx("input",{ref:j,type:"text",value:w,onFocus:()=>{d(!0),i(t)},onChange:r=>{const c=r.target.value;i(c),s(c),l||d(!0)},placeholder:"workflow.failed or app.>",className:`${v} font-mono`}),l&&_.length>0&&e.jsx("div",{className:"absolute z-50 left-0 right-0 mt-1 max-h-64 overflow-y-auto rounded-md border border-surface-border bg-surface shadow-lg",children:_.map(r=>{const c=Se[r.category]??"bg-zinc-400/15 text-zinc-400";return e.jsxs("button",{type:"button",onClick:()=>{s(r.topic),i(r.topic),d(!1)},className:`w-full text-left px-3 py-2 hover:bg-surface-hover transition-colors flex items-center gap-3 ${t===r.topic?"bg-accent/5":""}`,children:[e.jsx("span",{className:"text-[11px] font-mono text-text-primary shrink-0",children:r.topic}),e.jsx("span",{className:`inline-flex items-center px-1.5 py-0.5 rounded text-[9px] font-medium shrink-0 ${c}`,children:r.category}),r.description&&e.jsx("span",{className:"text-[10px] text-text-quaternary truncate",children:r.description})]},r.topic)})})]})}const We=["*/5 * * * *","*/15 * * * *","0 * * * *","0 */4 * * *","0 7 * * *","0 9 * * 1-5"];function D(t){return{"* * * * *":"Every minute","*/5 * * * *":"Every 5 min","*/15 * * * *":"Every 15 min","0 * * * *":"Hourly","0 */4 * * *":"Every 4 hours","0 7 * * *":"Daily 7 AM UTC","0 9 * * 1-5":"Weekdays 9 AM UTC","0 0 * * 1":"Weekly Monday"}[t]||t}function qe({form:t,set:s}){const{data:o}=Q(),l=(o??[]).filter(n=>n.invocable).map(n=>n.workflow_type),{data:d}=me({status:"active"}),h=((d==null?void 0:d.workflows)??[]).map(n=>({id:n.id,name:n.graph_topic||n.id})),[i,j]=g.useState(0),x=(n,p,a)=>{const m=[...t.schedules];m[n]={...m[n],[p]:a},s("schedules",m)},w=()=>{s("schedules",[...t.schedules,{...ye}]),j(t.schedules.length)},_=n=>{s("schedules",t.schedules.filter((p,a)=>a!==n)),i>=t.schedules.length-1&&j(Math.max(0,t.schedules.length-2))},r=t.schedules,c=r[i];return r.length===0?e.jsxs("div",{children:[e.jsxs("div",{className:"border-l-2 border-accent/30 pl-3 py-1 mb-8 flex items-start justify-between",children:[e.jsx("p",{className:"text-[12px] text-text-secondary italic leading-relaxed",children:"Optional. Each schedule runs a workflow on a cron timer."}),e.jsx("button",{onClick:()=>{window.location.hash="#docs:agents.md:schedule"},className:"text-text-quaternary hover:text-accent transition-colors shrink-0 ml-3",title:"Docs: Schedule",children:e.jsx(P,{className:"w-3 h-3",strokeWidth:1.5})})]}),e.jsxs("div",{className:"flex flex-col items-center justify-center py-12 text-center",children:[e.jsx(U,{className:"w-8 h-8 text-text-quaternary/40 mb-3",strokeWidth:1}),e.jsx("p",{className:"text-sm text-text-tertiary mb-2",children:"No schedules"}),e.jsx("p",{className:"text-[11px] text-text-quaternary max-w-sm mb-6",children:"Add a schedule to run a workflow on a recurring cron timer."}),e.jsxs("button",{onClick:w,className:"flex items-center gap-2 text-xs text-accent hover:text-accent-hover transition-colors",children:[e.jsx($,{className:"w-3.5 h-3.5"})," Add schedule"]})]})]}):e.jsxs("div",{children:[e.jsxs("div",{className:"border-l-2 border-accent/30 pl-3 py-1 mb-6 flex items-start justify-between",children:[e.jsx("p",{className:"text-[12px] text-text-secondary italic leading-relaxed",children:"Each schedule runs a workflow on a timer. An agent can have multiple schedules targeting different workflows."}),e.jsx("button",{onClick:()=>{window.location.hash="#docs:agents.md:schedule"},className:"text-text-quaternary hover:text-accent transition-colors shrink-0 ml-3",title:"Docs: Schedule",children:e.jsx(P,{className:"w-3 h-3",strokeWidth:1.5})})]}),e.jsxs("div",{className:"flex gap-8",children:[e.jsxs("div",{className:"w-48 shrink-0 space-y-0.5",children:[r.map((n,p)=>e.jsxs("div",{className:`group/sched flex items-center rounded-md transition-colors ${i===p?"bg-accent/10 text-accent":"text-text-tertiary hover:text-text-primary hover:bg-surface-hover"}`,children:[e.jsxs("button",{onClick:()=>j(p),className:"flex-1 text-left px-3 py-2 min-w-0",children:[e.jsx("span",{className:"text-[11px] font-mono block",children:n.cron||"new schedule"}),e.jsxs("span",{className:"text-[9px] text-text-quaternary",children:[(n.reaction_type==="pipeline"?n.pipeline_id:n.workflow_type)||"no target"," · ",D(n.cron)]})]}),e.jsx("button",{onClick:a=>{a.stopPropagation(),confirm(`Remove schedule "${n.cron||"new"}"?
|
|
9
|
-
|
|
10
|
-
This takes effect when you save.`)&&_(p)},className:"opacity-0 group-hover/sched:opacity-100 px-2 text-text-quaternary hover:text-red-400 transition-all",title:"Remove",children:e.jsx(B,{className:"w-3 h-3"})})]},p)),e.jsxs("button",{onClick:w,className:"w-full flex items-center gap-1.5 px-3 py-2 text-[11px] text-accent hover:text-accent-hover transition-colors",children:[e.jsx($,{className:"w-3 h-3"})," Add"]})]}),c&&e.jsxs("div",{className:"flex-1 min-w-0 space-y-12",children:[e.jsxs("div",{children:[e.jsx("label",{className:k,children:"Run every"}),e.jsx("input",{type:"text",value:c.cron,onChange:n=>x(i,"cron",n.target.value),placeholder:"0 * * * *",className:`${v} font-mono`}),c.cron&&e.jsx("p",{className:"text-[11px] text-accent/80 mt-1",children:D(c.cron)}),e.jsx("div",{className:"flex gap-3 mt-2 overflow-x-auto",children:We.map(n=>e.jsx("button",{type:"button",onClick:()=>x(i,"cron",n),className:`text-[10px] font-mono whitespace-nowrap transition-colors ${c.cron===n?"text-accent font-medium":"text-accent/50 hover:text-accent"}`,children:D(n)},n))})]}),e.jsxs("div",{className:"grid grid-cols-2 gap-6",children:[e.jsxs("div",{className:"space-y-4",children:[e.jsx("label",{className:k,children:"Run this workflow"}),e.jsx("div",{className:"flex gap-3",children:["durable","pipeline"].map(n=>e.jsxs("label",{className:"flex items-center gap-1 text-[11px] text-text-secondary cursor-pointer",children:[e.jsx("input",{type:"radio",name:`sched-reaction-${i}`,checked:c.reaction_type===n,onChange:()=>x(i,"reaction_type",n),className:"accent-accent w-3 h-3"}),n==="durable"?"Workflow":"Pipeline"]},n))}),c.reaction_type==="pipeline"?e.jsxs("div",{children:[e.jsx("label",{className:b,children:"Pipeline *"}),e.jsxs("select",{value:c.pipeline_id,onChange:n=>x(i,"pipeline_id",n.target.value),className:v,children:[e.jsx("option",{value:"",children:"Select..."}),h.map(n=>e.jsx("option",{value:n.id,children:n.name},n.id))]}),e.jsx("p",{className:f,children:"YAML pipeline invoked on each cron tick."})]}):e.jsxs("div",{children:[e.jsx("label",{className:b,children:"Workflow *"}),e.jsxs("select",{value:c.workflow_type,onChange:n=>x(i,"workflow_type",n.target.value),className:v,children:[e.jsx("option",{value:"",children:"Select..."}),l.map(n=>e.jsx("option",{value:n,children:n},n))]}),e.jsx("p",{className:f,children:"Invoked on each cron tick."})]})]}),e.jsxs("div",{children:[e.jsx("label",{className:k,children:"As identity"}),e.jsx(H,{selected:c.execute_as,onChange:n=>x(i,"execute_as",n)}),e.jsx("p",{className:f,children:"Identity used when invoking."})]})]}),e.jsxs("div",{children:[e.jsx("label",{className:k,children:"With this data"}),e.jsx("textarea",{value:c.envelope,onChange:n=>x(i,"envelope",n.target.value),rows:10,className:V,placeholder:`{
|
|
11
|
-
"data": { "source": "cron" }
|
|
12
|
-
}`}),e.jsx("p",{className:f,children:"Static payload passed to the workflow on each invocation."})]})]})]})]})}function R({icon:t,color:s,title:o,children:l}){return e.jsxs("div",{className:"mb-10",children:[e.jsxs("div",{className:"flex items-center gap-2 mb-2 pb-1.5 border-b border-surface-border/40",children:[e.jsx(t,{className:`w-3.5 h-3.5 ${s}`,strokeWidth:1.5}),e.jsx("h3",{className:"text-xs font-semibold uppercase tracking-widest text-accent/80",children:o})]}),e.jsx("div",{className:"pl-5.5",children:l})]})}function E({label:t,children:s}){return e.jsxs("div",{className:"flex gap-3 py-0.5",children:[e.jsx("span",{className:"text-[10px] text-text-quaternary w-20 shrink-0",children:t}),e.jsx("span",{className:"text-xs text-text-primary",children:s})]})}function Re({form:t}){return e.jsxs("div",{children:[e.jsxs(R,{icon:G,color:"text-accent",title:"Identity",children:[e.jsx(E,{label:"Name",children:t.name||"—"}),t.description&&e.jsx(E,{label:"Description",children:t.description})]}),(t.goals||t.rules)&&e.jsxs(R,{icon:K,color:"text-rose-400",title:"Motivation",children:[t.goals&&e.jsx(E,{label:"Goals",children:t.goals}),t.rules&&e.jsx(E,{label:"Rules",children:t.rules})]}),e.jsx(R,{icon:L,color:"text-emerald-400",title:"Knowledge",children:e.jsx(E,{label:"Domain",children:t.knowledge_domain||"None"})}),e.jsxs("div",{className:"grid grid-cols-2 gap-x-10",children:[e.jsx(R,{icon:z,color:"text-cyan-400",title:`Subscriptions (${t.subscriptions.length})`,children:t.subscriptions.length===0?e.jsx("span",{className:"text-[11px] text-text-quaternary",children:"None configured"}):e.jsx("div",{className:"divide-y divide-surface-border/30",children:t.subscriptions.map((s,o)=>e.jsxs("div",{className:"flex items-center py-1.5",children:[e.jsx("div",{className:"flex-1 min-w-0",children:e.jsx(ue,{topic:s.topic||"unset"})}),e.jsxs("div",{className:"flex items-center gap-1.5 shrink-0",children:[e.jsx("span",{className:"text-text-quaternary text-[10px]",children:"→"}),s.workflow_type?e.jsx(F,{type:s.workflow_type}):e.jsx("span",{className:"text-[11px] text-text-quaternary",children:s.reaction_type})]})]},o))})}),e.jsx(R,{icon:U,color:"text-amber-400",title:`Schedules (${t.schedules.length})`,children:t.schedules.length===0?e.jsx("span",{className:"text-[11px] text-text-quaternary",children:"None configured"}):e.jsx("div",{className:"divide-y divide-surface-border/30",children:t.schedules.map((s,o)=>e.jsxs("div",{className:"flex items-center py-1.5",children:[e.jsx("div",{className:"flex-1 min-w-0",children:e.jsx(ne,{cron:s.cron})}),e.jsxs("div",{className:"flex items-center gap-1.5 shrink-0",children:[e.jsx("span",{className:"text-text-quaternary text-[10px]",children:"→"}),s.workflow_type?e.jsx(F,{type:s.workflow_type}):e.jsx("span",{className:"text-[11px] text-text-quaternary",children:"no workflow"})]})]},o))})})]}),e.jsx("div",{className:"border-l-2 border-emerald-400/30 pl-3 py-2 mt-6",children:e.jsx("p",{className:"text-[11px] text-text-tertiary leading-relaxed",children:"After saving, subscriptions and schedules activate immediately. You can pause the agent anytime."})})]})}const Ee=[{id:1,label:"Identity",icon:G},{id:2,label:"Motivation",icon:K},{id:3,label:"Knowledge",icon:L},{id:4,label:"Subscriptions",icon:z},{id:5,label:"Schedules",icon:U},{id:6,label:"Review",icon:de}];function Qe(){const{id:t}=ve(),s=!t,o=je(),{data:l,isLoading:d}=te(s?null:t),{data:h}=ie(s?null:t),i=se(),j=ae(),x=le(),w=ce(),_=re(),[r,c]=g.useState(J),[n,p]=g.useState(!1),[a,m]=g.useState(""),[S,W]=ge(),A=parseInt(S.get("step")||"1",10),X=g.useCallback(u=>{W(y=>{const C=new URLSearchParams(y);return C.set("step",String(u)),C},{replace:!0})},[W]);g.useEffect(()=>{if(!n){if(s){c(J),p(!0);return}l&&h&&(c(be(l,h.subscriptions??[])),p(!0))}},[l,h,s,n]);const q=(u,y)=>c(C=>({...C,[u]:y})),Z=async()=>{m("");try{const u=we(r);let y=t;s?y=(await i.mutateAsync(u)).id:await j.mutateAsync({id:t,...u});const C=Ne(r),ee=(h==null?void 0:h.subscriptions)??[],I=new Set(ee.map(N=>N.id));for(const N of C)if(N.id&&I.has(N.id)){const{id:T,...O}=N;await w.mutateAsync({agentId:y,subId:T,...O}),I.delete(T)}else{const{id:T,...O}=N;await x.mutateAsync({agentId:y,...O})}for(const N of I)await _.mutateAsync({agentId:y,subId:N});o(`/agents/${y}`)}catch(u){m(u.message)}};if(!s&&d)return e.jsxs("div",{className:"animate-pulse space-y-4",children:[e.jsx("div",{className:"h-8 bg-surface-sunken rounded w-48"}),e.jsx("div",{className:"h-60 bg-surface-sunken rounded"})]});const Y=i.isPending||j.isPending;return e.jsxs("div",{children:[e.jsx(oe,{title:s?"New Agent":`Agent: ${(l==null?void 0:l.id)??""}`,docsHash:"#docs:agents.md"}),e.jsxs("div",{className:"flex gap-10",children:[e.jsxs("nav",{className:"w-44 shrink-0 sticky top-0 self-start pt-2",children:[e.jsx("div",{className:"space-y-0.5",children:Ee.map(u=>{const y=u.icon,C=A===u.id;return e.jsxs("button",{onClick:()=>X(u.id),className:`w-full flex items-center gap-2.5 px-3 py-2 rounded-md text-left transition-colors ${C?"bg-accent/10 text-accent":"text-text-tertiary hover:text-text-primary hover:bg-surface-hover"}`,children:[e.jsx(y,{className:"w-3.5 h-3.5 shrink-0",strokeWidth:1.5}),e.jsx("span",{className:"text-xs font-medium",children:u.label})]},u.id)})}),e.jsxs("div",{className:"mt-8 pt-4 border-t border-surface-border space-y-2",children:[e.jsx("button",{onClick:Z,disabled:!r.name.trim()||Y,className:"w-full btn-primary text-xs disabled:opacity-50",children:Y?"Saving...":s?"Create Agent":"Save"}),e.jsx("button",{onClick:()=>o(s?"/agents":`/agents/${t}`),className:"w-full btn-ghost text-xs",children:"Cancel"})]}),a&&e.jsx("p",{className:"text-xs text-status-error mt-3",children:a})]}),e.jsxs("div",{className:"flex-1 min-w-0 pt-2",children:[A===1&&e.jsx(ke,{form:r,set:q}),A===2&&e.jsx(_e,{form:r,set:q}),A===3&&e.jsx(Ce,{form:r,set:q}),A===4&&e.jsx(Ae,{form:r,set:q}),A===5&&e.jsx(qe,{form:r,set:q}),A===6&&e.jsx(Re,{form:r})]})]})]})}export{Qe as AgentConfigPage};
|
|
13
|
-
//# sourceMappingURL=AgentConfigPage-DG1zOIiz.js.map
|