@hotmeshio/long-tail 0.1.3 → 0.1.5
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/README.md +118 -173
- package/build/examples/seed.js +1 -1
- package/build/examples/workflows/basic-echo/activities.d.ts +2 -2
- package/build/examples/workflows/basic-echo/activities.js +7 -7
- package/build/examples/workflows/basic-echo/index.js +1 -1
- package/build/examples/workflows/kitchen-sink/index.js +2 -2
- package/build/index.d.ts +12 -10
- package/build/index.js +15 -12
- package/build/lib/db/index.d.ts +18 -0
- package/build/{services → lib}/db/index.js +9 -0
- package/build/{services → lib}/db/migrate.js +3 -3
- package/build/{services → lib}/telemetry/honeycomb.d.ts +2 -2
- package/build/{services → lib}/telemetry/honeycomb.js +2 -2
- package/build/modules/defaults.d.ts +2 -0
- package/build/modules/defaults.js +3 -1
- package/build/routes/controlplane.js +1 -1
- package/build/routes/dba.js +4 -0
- package/build/routes/docs.d.ts +2 -0
- package/build/routes/docs.js +87 -0
- package/build/routes/escalations/bulk.js +120 -202
- package/build/routes/escalations/resolve.js +1 -1
- package/build/routes/escalations/single.js +1 -1
- package/build/routes/files.js +1 -1
- package/build/routes/index.js +2 -0
- package/build/routes/insight.js +2 -1
- package/build/routes/mcp.js +3 -2
- package/build/routes/oauth.js +1 -1
- package/build/routes/settings.js +4 -4
- package/build/routes/workflows/discovery.js +3 -3
- package/build/routes/workflows/invocation.js +1 -1
- package/build/routes/yaml-workflows/crud.js +1 -1
- package/build/routes/yaml-workflows/deployment.js +25 -0
- package/build/services/auth/bot-api-key.js +1 -1
- package/build/services/auth/index.d.ts +4 -0
- package/build/services/auth/index.js +17 -0
- package/build/services/auth/service-token.js +1 -1
- package/build/services/config/provider.js +1 -1
- package/build/services/config/read.js +1 -1
- package/build/services/config/write.js +1 -1
- package/build/services/controlplane/index.js +3 -4
- package/build/services/controlplane/quorum-bridge.js +2 -2
- package/build/services/cron/index.js +4 -5
- package/build/services/dba.d.ts +4 -0
- package/build/services/dba.js +3 -6
- package/build/services/escalation/bulk.js +1 -1
- package/build/services/escalation/crud.js +1 -1
- package/build/services/escalation/queries.js +1 -1
- package/build/services/export/client.js +2 -5
- package/build/services/export/index.js +14 -2
- package/build/services/hotmesh-utils.js +1 -1
- package/build/services/iam/bots.js +6 -12
- package/build/services/iam/context.d.ts +1 -1
- package/build/services/iam/context.js +5 -5
- package/build/services/iam/credentials.js +1 -1
- package/build/services/iam/ephemeral.js +8 -15
- package/build/services/iam/principal.js +3 -10
- package/build/services/iam/resolve.js +1 -1
- package/build/services/iam/sql.d.ts +10 -0
- package/build/services/iam/sql.js +42 -0
- package/build/services/insight/index.d.ts +1 -0
- package/build/services/insight/index.js +4 -6
- package/build/services/interceptor/activities/escalation.js +1 -1
- package/build/services/interceptor/activities/task.js +9 -6
- package/build/services/interceptor/activities/workflow.js +4 -5
- package/build/services/interceptor/activity-interceptor.d.ts +1 -1
- package/build/services/interceptor/activity-interceptor.js +5 -5
- package/build/services/interceptor/completion.js +1 -1
- package/build/services/interceptor/escalation.js +1 -1
- package/build/services/interceptor/index.js +4 -4
- package/build/services/interceptor/lifecycle.js +1 -1
- package/build/services/maintenance/index.js +4 -5
- package/build/services/mcp/adapter.js +1 -16
- package/build/services/mcp/client/connection.d.ts +1 -1
- package/build/services/mcp/client/connection.js +2 -2
- package/build/services/mcp/client/tools.js +1 -1
- package/build/services/mcp/db-server/index.js +1 -1
- package/build/services/mcp/db-server/tools.js +17 -4
- package/build/services/mcp/db.js +1 -1
- package/build/services/mcp/playwright-server/index.js +1 -1
- package/build/services/mcp/playwright-server/lifecycle.js +1 -1
- package/build/services/mcp/playwright-server/schemas.d.ts +2 -2
- package/build/services/mcp/playwright-server/tools.js +1 -1
- package/build/services/mcp/register-tool.d.ts +11 -0
- package/build/services/mcp/register-tool.js +15 -0
- package/build/services/mcp/server.js +1 -1
- package/build/services/mcp/sql.d.ts +6 -0
- package/build/services/mcp/sql.js +34 -1
- package/build/services/mcp/workflow-compiler-server.js +1 -1
- package/build/services/mcp/workflow-server.js +1 -1
- package/build/services/mcp-runs/execution-builder.js +8 -6
- package/build/services/mcp-runs/queries.js +2 -2
- package/build/services/mcp-runs/sql.d.ts +4 -1
- package/build/services/mcp-runs/sql.js +23 -2
- package/build/services/namespace/index.js +1 -1
- package/build/services/oauth/db.js +9 -37
- package/build/services/oauth/index.js +1 -1
- package/build/services/oauth/providers/registry.js +1 -1
- package/build/services/oauth/sql.d.ts +7 -0
- package/build/services/oauth/sql.js +32 -0
- package/build/services/orchestrator/index.js +3 -3
- package/build/services/role/index.js +1 -1
- package/build/services/task/crud.js +1 -1
- package/build/services/task/process.js +1 -1
- package/build/services/task/resolve.js +1 -1
- package/build/services/user/auth.js +1 -1
- package/build/services/user/crud.js +1 -1
- package/build/services/user/rbac.js +1 -1
- package/build/services/user/roles.js +1 -1
- package/build/services/user/sql.d.ts +2 -0
- package/build/services/user/sql.js +3 -1
- package/build/services/yaml-workflow/db-utils.js +1 -1
- package/build/services/yaml-workflow/db.js +1 -1
- package/build/services/yaml-workflow/deployer-helpers.js +1 -1
- package/build/services/yaml-workflow/deployer.js +4 -4
- package/build/services/yaml-workflow/generator.js +1 -1
- package/build/services/yaml-workflow/input-analyzer-helpers.d.ts +6 -0
- package/build/services/yaml-workflow/input-analyzer-helpers.js +36 -7
- package/build/services/yaml-workflow/input-analyzer.js +16 -3
- package/build/services/yaml-workflow/pipeline/analyze.js +1 -1
- package/build/services/yaml-workflow/pipeline/build/dag.js +10 -5
- package/build/services/yaml-workflow/pipeline/compile/llm-call.js +1 -1
- package/build/services/yaml-workflow/pipeline/extract.js +5 -3
- package/build/services/yaml-workflow/pipeline/index.d.ts +5 -0
- package/build/services/yaml-workflow/pipeline/index.js +22 -0
- package/build/services/yaml-workflow/pipeline/validate.js +2 -2
- package/build/services/yaml-workflow/workers/callbacks.js +1 -1
- package/build/services/yaml-workflow/workers/events.js +2 -2
- package/build/services/yaml-workflow/workers/register.js +8 -8
- package/build/start/adapters.js +7 -7
- package/build/start/index.js +4 -4
- package/build/start/server.js +1 -1
- package/build/start/workers.d.ts +1 -2
- package/build/start/workers.js +8 -7
- package/build/system/activities/claude-code.js +1 -1
- package/build/system/activities/file-storage.js +1 -1
- package/build/system/activities/knowledge.d.ts +58 -0
- package/build/system/activities/knowledge.js +128 -0
- package/build/system/activities/sql.d.ts +9 -0
- package/build/system/activities/sql.js +41 -0
- package/build/system/activities/triage/context.js +1 -1
- package/build/system/activities/triage/discovery.d.ts +2 -14
- package/build/system/activities/triage/discovery.js +5 -161
- package/build/system/activities/triage/llm.d.ts +1 -8
- package/build/system/activities/triage/llm.js +2 -28
- package/build/system/activities/triage/tools.d.ts +1 -17
- package/build/system/activities/triage/tools.js +8 -167
- package/build/system/index.js +5 -4
- package/build/system/mcp-servers/admin/escalations.d.ts +5 -0
- package/build/system/mcp-servers/admin/escalations.js +149 -0
- package/build/system/mcp-servers/admin/index.d.ts +31 -0
- package/build/system/mcp-servers/admin/index.js +80 -0
- package/build/system/mcp-servers/admin/maintenance.d.ts +5 -0
- package/build/system/mcp-servers/admin/maintenance.js +58 -0
- package/build/system/mcp-servers/admin/mcp-servers.d.ts +5 -0
- package/build/system/mcp-servers/admin/mcp-servers.js +146 -0
- package/build/system/mcp-servers/admin/schemas.d.ts +411 -0
- package/build/system/mcp-servers/admin/schemas.js +177 -0
- package/build/system/mcp-servers/admin/tasks.d.ts +5 -0
- package/build/system/mcp-servers/admin/tasks.js +112 -0
- package/build/system/mcp-servers/admin/users.d.ts +5 -0
- package/build/system/mcp-servers/admin/users.js +167 -0
- package/build/system/mcp-servers/admin/workflow-config.d.ts +9 -0
- package/build/system/mcp-servers/admin/workflow-config.js +118 -0
- package/build/system/mcp-servers/admin/workflows.d.ts +6 -0
- package/build/system/mcp-servers/admin/workflows.js +138 -0
- package/build/system/mcp-servers/admin/yaml-workflows.d.ts +8 -0
- package/build/system/mcp-servers/admin/yaml-workflows.js +237 -0
- package/build/system/mcp-servers/claude-code.js +1 -1
- package/build/system/mcp-servers/db-query/index.js +1 -1
- package/build/system/mcp-servers/db-query/tools.js +17 -4
- package/build/system/mcp-servers/docs.d.ts +5 -0
- package/build/system/mcp-servers/docs.js +147 -0
- package/build/system/mcp-servers/file-storage.js +1 -1
- package/build/system/mcp-servers/http-fetch.js +1 -1
- package/build/system/mcp-servers/human-queue.js +1 -1
- package/build/system/mcp-servers/knowledge.d.ts +4 -0
- package/build/system/mcp-servers/knowledge.js +137 -0
- package/build/system/mcp-servers/oauth.js +1 -1
- package/build/system/mcp-servers/playwright/browser-lifecycle.js +1 -1
- package/build/system/mcp-servers/playwright/index.js +1 -1
- package/build/system/mcp-servers/playwright/schemas.d.ts +19 -8
- package/build/system/mcp-servers/playwright/schemas.js +3 -0
- package/build/system/mcp-servers/playwright/tools-navigation.js +22 -9
- package/build/system/mcp-servers/playwright/tools-run-script.js +20 -3
- package/build/system/mcp-servers/playwright/vision-helper.d.ts +12 -0
- package/build/system/mcp-servers/playwright/vision-helper.js +81 -0
- package/build/system/mcp-servers/playwright-cli/helpers.js +2 -2
- package/build/system/mcp-servers/playwright-cli/index.js +1 -1
- package/build/system/mcp-servers/playwright-cli/schemas.d.ts +28 -28
- package/build/system/mcp-servers/playwright-cli/schemas.js +1 -1
- package/build/system/mcp-servers/playwright-cli/tools-auth.js +1 -1
- package/build/system/mcp-servers/playwright-cli/tools-capture.js +4 -2
- package/build/system/mcp-servers/translation.d.ts +14 -0
- package/build/system/mcp-servers/translation.js +130 -0
- package/build/system/mcp-servers/vision-prompts.d.ts +2 -0
- package/build/system/mcp-servers/vision-prompts.js +9 -0
- package/build/system/mcp-servers/{document-vision.d.ts → vision.d.ts} +3 -4
- package/build/system/mcp-servers/vision.js +221 -0
- package/build/system/mcp-servers/workflow-compiler.js +1 -1
- package/build/system/mcp-servers/workflow.js +1 -1
- package/build/system/seed/index.js +13 -2
- package/build/system/seed/server-definitions.d.ts +1954 -322
- package/build/system/seed/server-definitions.js +65 -34
- package/build/system/seed/tool-manifests-admin.d.ts +1645 -0
- package/build/system/seed/tool-manifests-admin.js +45 -0
- package/build/system/seed/tool-manifests-browser.d.ts +1 -1
- package/build/system/seed/tool-manifests-browser.js +3 -3
- package/build/system/seed/tool-manifests-data.d.ts +62 -21
- package/build/system/seed/tool-manifests-data.js +55 -17
- package/build/system/seed/tool-manifests-knowledge.d.ts +171 -0
- package/build/system/seed/tool-manifests-knowledge.js +94 -0
- package/build/system/workflows/mcp-deterministic/index.js +1 -1
- package/build/system/workflows/mcp-query/activities/discovery.d.ts +2 -20
- package/build/system/workflows/mcp-query/activities/discovery.js +5 -163
- package/build/system/workflows/mcp-query/activities/llm.d.ts +1 -7
- package/build/system/workflows/mcp-query/activities/llm.js +2 -27
- package/build/system/workflows/mcp-query/activities/tool-executor.d.ts +0 -4
- package/build/system/workflows/mcp-query/activities/tool-executor.js +2 -106
- package/build/system/workflows/mcp-query/activities/tool-loader.d.ts +0 -9
- package/build/system/workflows/mcp-query/activities/tool-loader.js +2 -87
- package/build/system/workflows/mcp-query/index.js +55 -5
- package/build/system/workflows/mcp-query/prompts.d.ts +1 -2
- package/build/system/workflows/mcp-query/prompts.js +5 -32
- package/build/system/workflows/mcp-query/strategy-advisors.d.ts +3 -14
- package/build/system/workflows/mcp-query/strategy-advisors.js +4 -107
- package/build/system/workflows/mcp-query/types.d.ts +2 -10
- package/build/system/workflows/mcp-query/types.js +0 -1
- package/build/system/workflows/mcp-query-router/index.js +1 -1
- package/build/system/workflows/mcp-triage/index.d.ts +2 -2
- package/build/system/workflows/mcp-triage/index.js +39 -7
- package/build/system/workflows/mcp-triage/prompts.js +7 -14
- package/build/system/workflows/mcp-triage-deterministic/index.js +1 -1
- package/build/system/workflows/mcp-triage-router/index.js +1 -1
- package/build/system/workflows/shared/discovery.d.ts +35 -0
- package/build/system/workflows/shared/discovery.js +175 -0
- package/build/system/workflows/shared/index.d.ts +7 -0
- package/build/system/workflows/shared/index.js +18 -0
- package/build/system/workflows/shared/llm-caller.d.ts +8 -0
- package/build/system/workflows/shared/llm-caller.js +31 -0
- package/build/system/workflows/shared/prompts.d.ts +2 -0
- package/build/system/workflows/shared/prompts.js +32 -0
- package/build/system/workflows/shared/strategy-advisors.d.ts +14 -0
- package/build/system/workflows/shared/strategy-advisors.js +109 -0
- package/build/system/workflows/shared/tool-executor.d.ts +11 -0
- package/build/system/workflows/shared/tool-executor.js +111 -0
- package/build/system/workflows/shared/tool-loader.d.ts +19 -0
- package/build/system/workflows/shared/tool-loader.js +94 -0
- package/build/system/workflows/shared/types.d.ts +9 -0
- package/build/system/workflows/shared/types.js +2 -0
- package/build/system/workflows/tool-result-guard.d.ts +14 -0
- package/build/system/workflows/tool-result-guard.js +78 -0
- package/build/tsconfig.tsbuildinfo +1 -1
- package/build/types/user.d.ts +2 -0
- package/build/vitest.config.d.ts +1 -1
- package/build/vitest.integration.config.d.ts +1 -1
- package/build/workers/index.js +2 -8
- package/dashboard/dist/assets/AdminDashboard-CTyAMUJR.js +2 -0
- package/dashboard/dist/assets/AdminDashboard-CTyAMUJR.js.map +1 -0
- package/dashboard/dist/assets/AvailableEscalationsPage-BMXV3Q4l.js +2 -0
- package/dashboard/dist/assets/AvailableEscalationsPage-BMXV3Q4l.js.map +1 -0
- package/dashboard/dist/assets/BotPicker-C51nKFEu.js +2 -0
- package/dashboard/dist/assets/{BotPicker-D6FYW1Gt.js.map → BotPicker-C51nKFEu.js.map} +1 -1
- package/dashboard/dist/assets/CollapsibleSection-BSyfd8uL.js +2 -0
- package/dashboard/dist/assets/{CollapsibleSection-Cxk4wvjT.js.map → CollapsibleSection-BSyfd8uL.js.map} +1 -1
- package/dashboard/dist/assets/ConfirmDeleteModal-CBdhia5T.js +2 -0
- package/dashboard/dist/assets/{ConfirmDeleteModal-FSXyKjaB.js.map → ConfirmDeleteModal-CBdhia5T.js.map} +1 -1
- package/dashboard/dist/assets/CopyableId-dGlewBCS.js +2 -0
- package/dashboard/dist/assets/{CopyableId-CBdxWfp8.js.map → CopyableId-dGlewBCS.js.map} +1 -1
- package/dashboard/dist/assets/CredentialsPage-CoBNFSAu.js +2 -0
- package/dashboard/dist/assets/{CredentialsPage-Ikzsot0w.js.map → CredentialsPage-CoBNFSAu.js.map} +1 -1
- package/dashboard/dist/assets/CustomDurationPicker-BataWFj8.js +2 -0
- package/dashboard/dist/assets/{CustomDurationPicker-CAninCbl.js.map → CustomDurationPicker-BataWFj8.js.map} +1 -1
- package/dashboard/dist/assets/DataTable-B3uf5CCo.js +2 -0
- package/dashboard/dist/assets/DataTable-B3uf5CCo.js.map +1 -0
- package/dashboard/dist/assets/ElapsedCell-G5oSwTpT.js +2 -0
- package/dashboard/dist/assets/ElapsedCell-G5oSwTpT.js.map +1 -0
- package/dashboard/dist/assets/EmptyState-BChBJNGS.js +2 -0
- package/dashboard/dist/assets/{EmptyState-2CmV-IaS.js.map → EmptyState-BChBJNGS.js.map} +1 -1
- package/dashboard/dist/assets/EscalationsOverview-CxUv8xjG.js +2 -0
- package/dashboard/dist/assets/{EscalationsOverview-GXYFPASS.js.map → EscalationsOverview-CxUv8xjG.js.map} +1 -1
- package/dashboard/dist/assets/EventTable-CVt8B0BZ.js +2 -0
- package/dashboard/dist/assets/{EventTable-B01oJf6Y.js.map → EventTable-CVt8B0BZ.js.map} +1 -1
- package/dashboard/dist/assets/FilterBar-CShf0oe7.js +2 -0
- package/dashboard/dist/assets/{FilterBar-Ck4K4rzu.js.map → FilterBar-CShf0oe7.js.map} +1 -1
- package/dashboard/dist/assets/McpOverview-CbaZRnJl.js +2 -0
- package/dashboard/dist/assets/{McpOverview-JkvRcX2e.js.map → McpOverview-CbaZRnJl.js.map} +1 -1
- package/dashboard/dist/assets/McpQueryDetailPage-CGoR9XK6.js +5 -0
- package/dashboard/dist/assets/McpQueryDetailPage-CGoR9XK6.js.map +1 -0
- package/dashboard/dist/assets/McpQueryPage-BjXoYQuU.js +2 -0
- package/dashboard/dist/assets/McpQueryPage-BjXoYQuU.js.map +1 -0
- package/dashboard/dist/assets/McpRunDetailPage-DLkA5Aar.js +2 -0
- package/dashboard/dist/assets/McpRunDetailPage-DLkA5Aar.js.map +1 -0
- package/dashboard/dist/assets/McpRunsPage-DCh9n11D.js +2 -0
- package/dashboard/dist/assets/McpRunsPage-DCh9n11D.js.map +1 -0
- package/dashboard/dist/assets/Modal-CI5RBPOQ.js +2 -0
- package/dashboard/dist/assets/{Modal-B4rbIVAn.js.map → Modal-CI5RBPOQ.js.map} +1 -1
- package/dashboard/dist/assets/OperatorDashboard-Dc80suXd.js +2 -0
- package/dashboard/dist/assets/OperatorDashboard-Dc80suXd.js.map +1 -0
- package/dashboard/dist/assets/PageHeader-SMD9qtOO.js +2 -0
- package/dashboard/dist/assets/PageHeader-SMD9qtOO.js.map +1 -0
- package/dashboard/dist/assets/PageHeaderWithStats-TikLQsTp.js +2 -0
- package/dashboard/dist/assets/PageHeaderWithStats-TikLQsTp.js.map +1 -0
- package/dashboard/dist/assets/PriorityBadge-CQ0EsLTA.js +2 -0
- package/dashboard/dist/assets/{PriorityBadge-DfQY9St9.js.map → PriorityBadge-CQ0EsLTA.js.map} +1 -1
- package/dashboard/dist/assets/ProcessDetailPage-B2GKuGzk.js +2 -0
- package/dashboard/dist/assets/ProcessDetailPage-B2GKuGzk.js.map +1 -0
- package/dashboard/dist/assets/ProcessesListPage-CTjI3Wn6.js +2 -0
- package/dashboard/dist/assets/ProcessesListPage-CTjI3Wn6.js.map +1 -0
- package/dashboard/dist/assets/RefreshButton-BcQDObrv.js +2 -0
- package/dashboard/dist/assets/RefreshButton-BcQDObrv.js.map +1 -0
- package/dashboard/dist/assets/RolePill-Crj4TH5p.js +2 -0
- package/dashboard/dist/assets/{RolePill-BTPa8L-P.js.map → RolePill-Crj4TH5p.js.map} +1 -1
- package/dashboard/dist/assets/RolesPage-C_RInUwS.js +2 -0
- package/dashboard/dist/assets/RolesPage-C_RInUwS.js.map +1 -0
- package/dashboard/dist/assets/RowActions-Cp5HyK_w.js +2 -0
- package/dashboard/dist/assets/{RowActions-Dg-Fsm5O.js.map → RowActions-Cp5HyK_w.js.map} +1 -1
- package/dashboard/dist/assets/RunAsSelector-BhyWtofX.js +2 -0
- package/dashboard/dist/assets/RunAsSelector-BhyWtofX.js.map +1 -0
- package/dashboard/dist/assets/StatCard-BKZLSgNV.js +2 -0
- package/dashboard/dist/assets/{StatCard-DlgF0CJC.js.map → StatCard-BKZLSgNV.js.map} +1 -1
- package/dashboard/dist/assets/StatusBadge-BYNGGZK5.js +2 -0
- package/dashboard/dist/assets/StatusBadge-BYNGGZK5.js.map +1 -0
- package/dashboard/dist/assets/StickyPagination-CTosgiU2.js +2 -0
- package/dashboard/dist/assets/{StickyPagination-F9FZsRy9.js.map → StickyPagination-CTosgiU2.js.map} +1 -1
- package/dashboard/dist/assets/SwimlaneTimeline-ylG5Ps1s.js +2 -0
- package/dashboard/dist/assets/SwimlaneTimeline-ylG5Ps1s.js.map +1 -0
- package/dashboard/dist/assets/TaskDetailPage-C9pDGdD2.js +2 -0
- package/dashboard/dist/assets/TaskDetailPage-C9pDGdD2.js.map +1 -0
- package/dashboard/dist/assets/TaskQueuePill-BtJbZTT0.js +2 -0
- package/dashboard/dist/assets/{TaskQueuePill-awmtb0qw.js.map → TaskQueuePill-BtJbZTT0.js.map} +1 -1
- package/dashboard/dist/assets/TasksListPage-DtFLUEhg.js +2 -0
- package/dashboard/dist/assets/{TasksListPage-C_QF23c1.js.map → TasksListPage-DtFLUEhg.js.map} +1 -1
- package/dashboard/dist/assets/TimeAgo-WuM6xImZ.js +2 -0
- package/dashboard/dist/assets/TimeAgo-WuM6xImZ.js.map +1 -0
- package/dashboard/dist/assets/TimestampCell-IVL_-Upy.js +2 -0
- package/dashboard/dist/assets/TimestampCell-IVL_-Upy.js.map +1 -0
- package/dashboard/dist/assets/UserName-DU9qeg13.js +2 -0
- package/dashboard/dist/assets/{UserName-DaP4YAKr.js.map → UserName-DU9qeg13.js.map} +1 -1
- package/dashboard/dist/assets/WorkflowExecutionPage-DOocX81f.js +2 -0
- package/dashboard/dist/assets/WorkflowExecutionPage-DOocX81f.js.map +1 -0
- package/dashboard/dist/assets/WorkflowPill-Diw8iWBP.js +2 -0
- package/dashboard/dist/assets/WorkflowPill-Diw8iWBP.js.map +1 -0
- package/dashboard/dist/assets/WorkflowsDashboard-DDtUIrTy.js +2 -0
- package/dashboard/dist/assets/WorkflowsDashboard-DDtUIrTy.js.map +1 -0
- package/dashboard/dist/assets/WorkflowsOverview-CPuvA4t3.js +2 -0
- package/dashboard/dist/assets/{WorkflowsOverview-D9OzzQqw.js.map → WorkflowsOverview-CPuvA4t3.js.map} +1 -1
- package/dashboard/dist/assets/YamlWorkflowsPage-DlwwkluN.js +2 -0
- package/dashboard/dist/assets/YamlWorkflowsPage-DlwwkluN.js.map +1 -0
- package/dashboard/dist/assets/{bots-BkKVMbUW.js → bots-BPiZXf2h.js} +2 -2
- package/dashboard/dist/assets/{bots-BkKVMbUW.js.map → bots-BPiZXf2h.js.map} +1 -1
- package/dashboard/dist/assets/constants-BHkpVaqx.js +2 -0
- package/dashboard/dist/assets/constants-BHkpVaqx.js.map +1 -0
- package/dashboard/dist/assets/escalation-DTY_OKRh.js +2 -0
- package/dashboard/dist/assets/escalation-DTY_OKRh.js.map +1 -0
- package/dashboard/dist/assets/escalation-columns-C91fHSkp.js +2 -0
- package/dashboard/dist/assets/{escalation-columns-D6aqStaY.js.map → escalation-columns-C91fHSkp.js.map} +1 -1
- package/dashboard/dist/assets/helpers-DBUZ9pnG.js +2 -0
- package/dashboard/dist/assets/helpers-DBUZ9pnG.js.map +1 -0
- package/dashboard/dist/assets/index-BOeA-gfK.js +17 -0
- package/dashboard/dist/assets/{index-X85K5bHC.js.map → index-BOeA-gfK.js.map} +1 -1
- package/dashboard/dist/assets/index-BZ6K_kmL.js +3 -0
- package/dashboard/dist/assets/index-BZ6K_kmL.js.map +1 -0
- package/dashboard/dist/assets/index-Bpm0yeoi.js +2 -0
- package/dashboard/dist/assets/index-Bpm0yeoi.js.map +1 -0
- package/dashboard/dist/assets/index-BtOwLI0K.js +2 -0
- package/dashboard/dist/assets/{index-DTPzZr_X.js.map → index-BtOwLI0K.js.map} +1 -1
- package/dashboard/dist/assets/index-CBF3ZvRZ.js +6 -0
- package/dashboard/dist/assets/index-CBF3ZvRZ.js.map +1 -0
- package/dashboard/dist/assets/index-CDWOfCmi.js +2 -0
- package/dashboard/dist/assets/index-CDWOfCmi.js.map +1 -0
- package/dashboard/dist/assets/index-Ce6sL__n.js +2 -0
- package/dashboard/dist/assets/index-Ce6sL__n.js.map +1 -0
- package/dashboard/dist/assets/index-DSzSoku1.js +283 -0
- package/dashboard/dist/assets/index-DSzSoku1.js.map +1 -0
- package/dashboard/dist/assets/index-D_qEAYrg.js +2 -0
- package/dashboard/dist/assets/index-D_qEAYrg.js.map +1 -0
- package/dashboard/dist/assets/index-gCy9XX3W.css +1 -0
- package/dashboard/dist/assets/mcp-BzVpaaKF.js +2 -0
- package/dashboard/dist/assets/{mcp-blCW6IL7.js.map → mcp-BzVpaaKF.js.map} +1 -1
- package/dashboard/dist/assets/mcp-query-wTuxTTCV.js +2 -0
- package/dashboard/dist/assets/mcp-query-wTuxTTCV.js.map +1 -0
- package/dashboard/dist/assets/{mcp-runs-ChPbpvXK.js → mcp-runs-DmXYJD19.js} +2 -2
- package/dashboard/dist/assets/{mcp-runs-ChPbpvXK.js.map → mcp-runs-DmXYJD19.js.map} +1 -1
- package/dashboard/dist/assets/namespaces-DoGa7jc7.js +2 -0
- package/dashboard/dist/assets/{namespaces-BgbaC3ow.js.map → namespaces-DoGa7jc7.js.map} +1 -1
- package/dashboard/dist/assets/{roles-ZNrqqnQl.js → roles-wCdQ2Z7k.js} +2 -2
- package/dashboard/dist/assets/{roles-ZNrqqnQl.js.map → roles-wCdQ2Z7k.js.map} +1 -1
- package/dashboard/dist/assets/settings-DDe_L7JT.js +2 -0
- package/dashboard/dist/assets/{settings-eBRSE0mQ.js.map → settings-DDe_L7JT.js.map} +1 -1
- package/dashboard/dist/assets/tasks-3Hih8Bt7.js +2 -0
- package/dashboard/dist/assets/{tasks-tRqClPns.js.map → tasks-3Hih8Bt7.js.map} +1 -1
- package/dashboard/dist/assets/{useFilterParams-BaXUAkYK.js → useFilterParams-BUyLHcx_.js} +2 -2
- package/dashboard/dist/assets/{useFilterParams-BaXUAkYK.js.map → useFilterParams-BUyLHcx_.js.map} +1 -1
- package/dashboard/dist/assets/{useNatsEvents-Xr43X1fG.js → useNatsEvents-DeGKHFTX.js} +2 -2
- package/dashboard/dist/assets/{useNatsEvents-Xr43X1fG.js.map → useNatsEvents-DeGKHFTX.js.map} +1 -1
- package/dashboard/dist/assets/{useYamlActivityEvents-BO51u8tm.js → useYamlActivityEvents-B5dHec6Y.js} +2 -2
- package/dashboard/dist/assets/{useYamlActivityEvents-BO51u8tm.js.map → useYamlActivityEvents-B5dHec6Y.js.map} +1 -1
- package/dashboard/dist/assets/{users-tMvNyOo8.js → users-BTagPmGW.js} +2 -2
- package/dashboard/dist/assets/{users-tMvNyOo8.js.map → users-BTagPmGW.js.map} +1 -1
- package/dashboard/dist/assets/{vendor-icons-ZTAKVwGc.js → vendor-icons-DCLlGYO9.js} +112 -57
- package/dashboard/dist/assets/vendor-icons-DCLlGYO9.js.map +1 -0
- package/dashboard/dist/assets/vendor-query-DLp59M9_.js +35 -0
- package/dashboard/dist/assets/vendor-query-DLp59M9_.js.map +1 -0
- package/dashboard/dist/assets/vendor-react-Co3Y8ikm.js +26 -0
- package/dashboard/dist/assets/vendor-react-Co3Y8ikm.js.map +1 -0
- package/dashboard/dist/assets/{workflows-Cc4VHcrp.js → workflows-B20dR3NE.js} +2 -2
- package/dashboard/dist/assets/{workflows-Cc4VHcrp.js.map → workflows-B20dR3NE.js.map} +1 -1
- package/dashboard/dist/assets/yaml-workflows-CaLPMQha.js +2 -0
- package/dashboard/dist/assets/yaml-workflows-CaLPMQha.js.map +1 -0
- package/dashboard/dist/index.html +5 -5
- package/docs/api/dba.md +81 -0
- package/docs/api/escalations.md +575 -0
- package/docs/api/exports.md +170 -0
- package/docs/api/maintenance.md +93 -0
- package/docs/api/mcp-runs.md +128 -0
- package/docs/api/mcp-servers.md +195 -0
- package/docs/api/namespaces.md +48 -0
- package/docs/api/roles.md +390 -0
- package/docs/api/service-accounts.md +188 -0
- package/docs/api/settings.md +33 -0
- package/docs/api/tasks.md +167 -0
- package/docs/api/users.md +180 -0
- package/docs/api/workflows.md +616 -0
- package/docs/api/yaml-workflows.md +312 -0
- package/docs/architecture.md +221 -0
- package/docs/auth.md +181 -0
- package/docs/cloud.md +272 -0
- package/docs/compilation.md +136 -0
- package/docs/contributing.md +56 -0
- package/docs/dashboard.md +145 -0
- package/docs/data.md +478 -0
- package/docs/escalation-strategies.md +264 -0
- package/docs/events.md +251 -0
- package/docs/iam.md +222 -0
- package/docs/img/01-login.png +0 -0
- package/docs/img/02-dashboard-home.png +0 -0
- package/docs/img/03-processes-list.png +0 -0
- package/docs/img/04-escalations-list.png +0 -0
- package/docs/img/05-mcp-servers.png +0 -0
- package/docs/img/06-mcp-pipelines.png +0 -0
- package/docs/img/07-workflows-list.png +0 -0
- package/docs/img/compilation/01-query-submit.png +0 -0
- package/docs/img/compilation/02-mcp-servers.png +0 -0
- package/docs/img/compilation/03-query-completed.png +0 -0
- package/docs/img/compilation/04-wizard-original.png +0 -0
- package/docs/img/compilation/05-wizard-timeline.png +0 -0
- package/docs/img/compilation/06-wizard-profile.png +0 -0
- package/docs/img/compilation/07-wizard-deploy.png +0 -0
- package/docs/img/compilation/08-wizard-test-modal.png +0 -0
- package/docs/img/compilation/09-wizard-test-compare.png +0 -0
- package/docs/img/compilation/10-wizard-verify.png +0 -0
- package/docs/logging.md +110 -0
- package/docs/maintenance.md +221 -0
- package/docs/mcp.md +715 -0
- package/docs/oauth-and-delegation.md +469 -0
- package/docs/telemetry.md +144 -0
- package/docs/workflows.md +695 -0
- package/lib/db/schemas/015_knowledge.sql +23 -0
- package/package.json +8 -9
- package/build/services/db/index.d.ts +0 -3
- package/build/services/mcp/vision-server.d.ts +0 -15
- package/build/services/mcp/vision-server.js +0 -214
- package/build/system/mcp-servers/document-vision.js +0 -228
- package/build/system/mcp-servers/prompts.d.ts +0 -4
- package/build/system/mcp-servers/prompts.js +0 -10
- package/dashboard/dist/assets/AdminDashboard-jfacvOC7.js +0 -2
- package/dashboard/dist/assets/AdminDashboard-jfacvOC7.js.map +0 -1
- package/dashboard/dist/assets/AvailableEscalationsPage-BglLDoT8.js +0 -2
- package/dashboard/dist/assets/AvailableEscalationsPage-BglLDoT8.js.map +0 -1
- package/dashboard/dist/assets/BotPicker-D6FYW1Gt.js +0 -2
- package/dashboard/dist/assets/CollapsibleSection-Cxk4wvjT.js +0 -2
- package/dashboard/dist/assets/ConfirmDeleteModal-FSXyKjaB.js +0 -2
- package/dashboard/dist/assets/CopyableId-CBdxWfp8.js +0 -2
- package/dashboard/dist/assets/CredentialsPage-Ikzsot0w.js +0 -2
- package/dashboard/dist/assets/CustomDurationPicker-CAninCbl.js +0 -2
- package/dashboard/dist/assets/DataTable-BDn1WBHS.js +0 -2
- package/dashboard/dist/assets/DataTable-BDn1WBHS.js.map +0 -1
- package/dashboard/dist/assets/EmptyState-2CmV-IaS.js +0 -2
- package/dashboard/dist/assets/EscalationsOverview-GXYFPASS.js +0 -2
- package/dashboard/dist/assets/EventTable-B01oJf6Y.js +0 -2
- package/dashboard/dist/assets/Field-DuFBAYhu.js +0 -2
- package/dashboard/dist/assets/Field-DuFBAYhu.js.map +0 -1
- package/dashboard/dist/assets/FilterBar-Ck4K4rzu.js +0 -2
- package/dashboard/dist/assets/McpOverview-JkvRcX2e.js +0 -2
- package/dashboard/dist/assets/McpQueryDetailPage-CUMqhQdS.js +0 -2
- package/dashboard/dist/assets/McpQueryDetailPage-CUMqhQdS.js.map +0 -1
- package/dashboard/dist/assets/McpQueryPage-DRRhw4nN.js +0 -2
- package/dashboard/dist/assets/McpQueryPage-DRRhw4nN.js.map +0 -1
- package/dashboard/dist/assets/McpRunDetailPage-CmPs5EvE.js +0 -2
- package/dashboard/dist/assets/McpRunDetailPage-CmPs5EvE.js.map +0 -1
- package/dashboard/dist/assets/McpRunsPage-Dl5Y2u6k.js +0 -2
- package/dashboard/dist/assets/McpRunsPage-Dl5Y2u6k.js.map +0 -1
- package/dashboard/dist/assets/Modal-B4rbIVAn.js +0 -2
- package/dashboard/dist/assets/OperatorDashboard-B56il28q.js +0 -2
- package/dashboard/dist/assets/OperatorDashboard-B56il28q.js.map +0 -1
- package/dashboard/dist/assets/PageHeader-CpWFly5S.js +0 -2
- package/dashboard/dist/assets/PageHeader-CpWFly5S.js.map +0 -1
- package/dashboard/dist/assets/PriorityBadge-DfQY9St9.js +0 -2
- package/dashboard/dist/assets/ProcessDetailPage-CMLq4M7D.js +0 -2
- package/dashboard/dist/assets/ProcessDetailPage-CMLq4M7D.js.map +0 -1
- package/dashboard/dist/assets/ProcessesListPage-CZ_HF06v.js +0 -2
- package/dashboard/dist/assets/ProcessesListPage-CZ_HF06v.js.map +0 -1
- package/dashboard/dist/assets/RolePill-BTPa8L-P.js +0 -2
- package/dashboard/dist/assets/RolesPage-9grZW7yR.js +0 -2
- package/dashboard/dist/assets/RolesPage-9grZW7yR.js.map +0 -1
- package/dashboard/dist/assets/RowActions-Dg-Fsm5O.js +0 -2
- package/dashboard/dist/assets/SimpleMarkdown-CBlvaWP4.js +0 -4
- package/dashboard/dist/assets/SimpleMarkdown-CBlvaWP4.js.map +0 -1
- package/dashboard/dist/assets/StatCard-DlgF0CJC.js +0 -2
- package/dashboard/dist/assets/StatusBadge-TlC4jiig.js +0 -2
- package/dashboard/dist/assets/StatusBadge-TlC4jiig.js.map +0 -1
- package/dashboard/dist/assets/StickyPagination-F9FZsRy9.js +0 -2
- package/dashboard/dist/assets/SwimlaneTimeline-7SiwATsZ.js +0 -2
- package/dashboard/dist/assets/SwimlaneTimeline-7SiwATsZ.js.map +0 -1
- package/dashboard/dist/assets/TaskDetailPage-CbPVTakt.js +0 -2
- package/dashboard/dist/assets/TaskDetailPage-CbPVTakt.js.map +0 -1
- package/dashboard/dist/assets/TaskQueuePill-awmtb0qw.js +0 -2
- package/dashboard/dist/assets/TasksListPage-C_QF23c1.js +0 -2
- package/dashboard/dist/assets/TimeAgo-UPG6DoH8.js +0 -2
- package/dashboard/dist/assets/TimeAgo-UPG6DoH8.js.map +0 -1
- package/dashboard/dist/assets/TimestampCell-DoWMKg6w.js +0 -2
- package/dashboard/dist/assets/TimestampCell-DoWMKg6w.js.map +0 -1
- package/dashboard/dist/assets/UserName-DaP4YAKr.js +0 -2
- package/dashboard/dist/assets/VersionHistory-Bt7WBr6m.js +0 -5
- package/dashboard/dist/assets/VersionHistory-Bt7WBr6m.js.map +0 -1
- package/dashboard/dist/assets/WorkflowExecutionPage-DjtAQ3hy.js +0 -2
- package/dashboard/dist/assets/WorkflowExecutionPage-DjtAQ3hy.js.map +0 -1
- package/dashboard/dist/assets/WorkflowPill-CCDSVaQj.js +0 -2
- package/dashboard/dist/assets/WorkflowPill-CCDSVaQj.js.map +0 -1
- package/dashboard/dist/assets/WorkflowsDashboard-D8z9uBNB.js +0 -2
- package/dashboard/dist/assets/WorkflowsDashboard-D8z9uBNB.js.map +0 -1
- package/dashboard/dist/assets/WorkflowsOverview-D9OzzQqw.js +0 -2
- package/dashboard/dist/assets/YamlWorkflowDetailPage-DrDvvP62.js +0 -3
- package/dashboard/dist/assets/YamlWorkflowDetailPage-DrDvvP62.js.map +0 -1
- package/dashboard/dist/assets/YamlWorkflowsPage-COqiNCQK.js +0 -2
- package/dashboard/dist/assets/YamlWorkflowsPage-COqiNCQK.js.map +0 -1
- package/dashboard/dist/assets/constants-CgaZfe5d.js +0 -2
- package/dashboard/dist/assets/constants-CgaZfe5d.js.map +0 -1
- package/dashboard/dist/assets/escalation-columns-D6aqStaY.js +0 -2
- package/dashboard/dist/assets/escalation-qalymbKB.js +0 -2
- package/dashboard/dist/assets/escalation-qalymbKB.js.map +0 -1
- package/dashboard/dist/assets/format-gXZXQ-HJ.js +0 -2
- package/dashboard/dist/assets/format-gXZXQ-HJ.js.map +0 -1
- package/dashboard/dist/assets/helpers-0gSleuzT.js +0 -2
- package/dashboard/dist/assets/helpers-0gSleuzT.js.map +0 -1
- package/dashboard/dist/assets/index-BWvMHed7.js +0 -6
- package/dashboard/dist/assets/index-BWvMHed7.js.map +0 -1
- package/dashboard/dist/assets/index-BaszoPO_.css +0 -1
- package/dashboard/dist/assets/index-Cn2jyj9A.js +0 -2
- package/dashboard/dist/assets/index-Cn2jyj9A.js.map +0 -1
- package/dashboard/dist/assets/index-D8VH6K8B.js +0 -54
- package/dashboard/dist/assets/index-D8VH6K8B.js.map +0 -1
- package/dashboard/dist/assets/index-D9SYwJsi.js +0 -3
- package/dashboard/dist/assets/index-D9SYwJsi.js.map +0 -1
- package/dashboard/dist/assets/index-DTPzZr_X.js +0 -2
- package/dashboard/dist/assets/index-D_6AB5BE.js +0 -2
- package/dashboard/dist/assets/index-D_6AB5BE.js.map +0 -1
- package/dashboard/dist/assets/index-S9Ks2Lj2.js +0 -2
- package/dashboard/dist/assets/index-S9Ks2Lj2.js.map +0 -1
- package/dashboard/dist/assets/index-X85K5bHC.js +0 -17
- package/dashboard/dist/assets/index-rjmgHlSH.js +0 -2
- package/dashboard/dist/assets/index-rjmgHlSH.js.map +0 -1
- package/dashboard/dist/assets/mcp-blCW6IL7.js +0 -2
- package/dashboard/dist/assets/mcp-query-DoAyPbjC.js +0 -2
- package/dashboard/dist/assets/mcp-query-DoAyPbjC.js.map +0 -1
- package/dashboard/dist/assets/namespaces-BgbaC3ow.js +0 -2
- package/dashboard/dist/assets/settings-eBRSE0mQ.js +0 -2
- package/dashboard/dist/assets/tasks-tRqClPns.js +0 -2
- package/dashboard/dist/assets/vendor-icons-ZTAKVwGc.js.map +0 -1
- package/dashboard/dist/assets/vendor-query-B2UbickB.js +0 -18
- package/dashboard/dist/assets/vendor-query-B2UbickB.js.map +0 -1
- package/dashboard/dist/assets/vendor-react-Cw8Gy8NJ.js +0 -22
- package/dashboard/dist/assets/vendor-react-Cw8Gy8NJ.js.map +0 -1
- package/dashboard/dist/assets/yaml-workflows-BL4V5CQy.js +0 -2
- package/dashboard/dist/assets/yaml-workflows-BL4V5CQy.js.map +0 -1
- /package/build/{services → lib}/db/migrate.d.ts +0 -0
- /package/build/{services → lib}/events/index.d.ts +0 -0
- /package/build/{services → lib}/events/index.js +0 -0
- /package/build/{services → lib}/events/memory.d.ts +0 -0
- /package/build/{services → lib}/events/memory.js +0 -0
- /package/build/{services → lib}/events/nats.d.ts +0 -0
- /package/build/{services → lib}/events/nats.js +0 -0
- /package/build/{services → lib}/events/publish.d.ts +0 -0
- /package/build/{services → lib}/events/publish.js +0 -0
- /package/build/{services → lib}/events/socketio.d.ts +0 -0
- /package/build/{services → lib}/events/socketio.js +0 -0
- /package/build/{services → lib}/logger/index.d.ts +0 -0
- /package/build/{services → lib}/logger/index.js +0 -0
- /package/build/{services → lib}/logger/pino.d.ts +0 -0
- /package/build/{services → lib}/logger/pino.js +0 -0
- /package/build/{services → lib}/storage/index.d.ts +0 -0
- /package/build/{services → lib}/storage/index.js +0 -0
- /package/build/{services → lib}/storage/local.d.ts +0 -0
- /package/build/{services → lib}/storage/local.js +0 -0
- /package/build/{services → lib}/storage/s3.d.ts +0 -0
- /package/build/{services → lib}/storage/s3.js +0 -0
- /package/build/{services → lib}/storage/types.d.ts +0 -0
- /package/build/{services → lib}/storage/types.js +0 -0
- /package/build/{services → lib}/telemetry/index.d.ts +0 -0
- /package/build/{services → lib}/telemetry/index.js +0 -0
- /package/{services → lib}/db/README.md +0 -0
- /package/{services → lib}/db/schemas/001_schema.sql +0 -0
- /package/{services → lib}/db/schemas/002_seed.sql +0 -0
- /package/{services → lib}/db/schemas/003_workflow_discovery.sql +0 -0
- /package/{services → lib}/db/schemas/004_query_router.sql +0 -0
- /package/{services → lib}/db/schemas/005_triage_router.sql +0 -0
- /package/{services → lib}/db/schemas/006_oauth.sql +0 -0
- /package/{services → lib}/db/schemas/007_security.sql +0 -0
- /package/{services → lib}/db/schemas/008_bot_accounts.sql +0 -0
- /package/{services → lib}/db/schemas/009_audit_trail.sql +0 -0
- /package/{services → lib}/db/schemas/010_credential_providers.sql +0 -0
- /package/{services → lib}/db/schemas/011_system_workflow_configs.sql +0 -0
- /package/{services → lib}/db/schemas/012_drop_modality.sql +0 -0
- /package/{services → lib}/db/schemas/013_execute_as.sql +0 -0
- /package/{services → lib}/db/schemas/014_ephemeral_credentials.sql +0 -0
|
@@ -0,0 +1,695 @@
|
|
|
1
|
+
# Workflows Guide
|
|
2
|
+
|
|
3
|
+
Write a function. If AI is confident, the task completes. If not, it escalates — durably, with full context — to whoever should handle it next. This guide covers how to build, compose, and test durable workflows with human-in-the-loop escalation.
|
|
4
|
+
|
|
5
|
+
## Three Workflow Types
|
|
6
|
+
|
|
7
|
+
Long Tail distinguishes three workflow types based on how they are created and how much infrastructure wraps the execution.
|
|
8
|
+
|
|
9
|
+
### Durable
|
|
10
|
+
|
|
11
|
+
Any function registered as a HotMesh worker is durable. This is the baseline tier and it provides:
|
|
12
|
+
|
|
13
|
+
- **Checkpointed execution** — activity results are persisted in PostgreSQL; crashes resume from the last checkpoint, not the beginning.
|
|
14
|
+
- **Automatic retries** — failed activities retry according to their retry policy.
|
|
15
|
+
- **IAM context** — the caller's identity propagates through the execution envelope.
|
|
16
|
+
|
|
17
|
+
A durable workflow is a plain async function. No config entry, no interceptor involvement. Register a worker, start the workflow, get a result. In the dashboard, durable workflows display a Workflow icon with a "Durable" label.
|
|
18
|
+
|
|
19
|
+
### Certified
|
|
20
|
+
|
|
21
|
+
A certified workflow is durable plus the full Long Tail control plane. It has an entry in the `lt_config_workflows` table, which activates:
|
|
22
|
+
|
|
23
|
+
- **Interceptor wrapping** — every execution is tracked as a task in `lt_tasks` with full audit trail.
|
|
24
|
+
- **Escalation chains** — returning `{ type: 'escalation' }` creates a reviewable record, routes to the correct role, and triggers re-runs on resolution.
|
|
25
|
+
- **Never-fail guarantee** — unhandled errors are caught and surfaced as error escalations. Nothing disappears silently.
|
|
26
|
+
- **Invocation controls** — `invocable: true` exposes the workflow for external invocation via the API and dashboard.
|
|
27
|
+
- **Execution identity** — roles, default assignees, and `execute_as` overrides are defined in the config.
|
|
28
|
+
|
|
29
|
+
In the dashboard, certified workflows display a ShieldCheck icon with a "Certified" label in accent blue. Durable workflows display a muted "Durable" badge. Both are invocable from the same **Invoke Workflow** page; the distinction is how much operational infrastructure backs them.
|
|
30
|
+
|
|
31
|
+
To certify a workflow, create a config entry:
|
|
32
|
+
|
|
33
|
+
```
|
|
34
|
+
PUT /api/workflows/myWorkflow/config
|
|
35
|
+
{ "default_role": "reviewer", "roles": ["reviewer"], "invocable": true }
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
To de-certify, delete the config. The workflow remains durable — it just loses interceptor tracking.
|
|
39
|
+
|
|
40
|
+
### Pipeline
|
|
41
|
+
|
|
42
|
+
A pipeline workflow is a compiled deterministic workflow. It is generated from a successful dynamic execution (typically an `mcpQuery` or `mcpTriage` run) and stored as a YAML DAG in `lt_yaml_workflows`.
|
|
43
|
+
|
|
44
|
+
- **No LLM per step** — the DAG executes tool calls directly with pre-wired data flow.
|
|
45
|
+
- **Discoverable as a tool** — deployed pipelines become MCP tools that any workflow or agent can invoke.
|
|
46
|
+
- **Automatic routing** — the `mcpQueryRouter` discovers compiled pipelines via full-text search and tag matching. When confidence exceeds the threshold, requests go straight to the compiled workflow.
|
|
47
|
+
- **Status lifecycle** — `draft` → `deployed` → `active` → `archived`.
|
|
48
|
+
|
|
49
|
+
In the dashboard, pipeline workflows display a Wand2 (magic wand) icon in purple. They are managed from the **MCP Pipeline Tools** page and created through the **Pipeline Designer**.
|
|
50
|
+
|
|
51
|
+
See the [Compilation Pipeline](compilation.md) guide for the full lifecycle from dynamic execution to deployed pipeline.
|
|
52
|
+
|
|
53
|
+
## Contents
|
|
54
|
+
|
|
55
|
+
- [Anatomy of a Workflow](#anatomy-of-a-workflow)
|
|
56
|
+
- [Activities and Durable Execution](#activities-and-durable-execution)
|
|
57
|
+
- [The Interceptor](#the-interceptor)
|
|
58
|
+
- [Escalation Lifecycle](#escalation-lifecycle)
|
|
59
|
+
- [Composing Workflows](#composing-workflows)
|
|
60
|
+
- [Verify Document Example](#verify-document-example) — full walkthrough
|
|
61
|
+
- [Milestones](#milestones)
|
|
62
|
+
- [Roles](#roles)
|
|
63
|
+
- [Testing](#testing)
|
|
64
|
+
|
|
65
|
+
## Anatomy of a Workflow
|
|
66
|
+
|
|
67
|
+
A workflow is a function that receives an envelope, does work, and returns a result or an escalation.
|
|
68
|
+
|
|
69
|
+
```typescript
|
|
70
|
+
import { Durable } from '@hotmeshio/hotmesh';
|
|
71
|
+
import type { LTEnvelope, LTReturn, LTEscalation } from '@hotmeshio/long-tail';
|
|
72
|
+
|
|
73
|
+
import * as activities from './activities';
|
|
74
|
+
|
|
75
|
+
const { analyzeContent } = Durable.workflow.proxyActivities<typeof activities>({
|
|
76
|
+
activities,
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
export async function reviewContent(
|
|
80
|
+
envelope: LTEnvelope,
|
|
81
|
+
): Promise<LTReturn | LTEscalation> {
|
|
82
|
+
if (envelope.resolver) {
|
|
83
|
+
return {
|
|
84
|
+
type: 'return',
|
|
85
|
+
data: { ...envelope.data, resolution: envelope.resolver },
|
|
86
|
+
milestones: [{ name: 'human_review', value: 'resolved' }],
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const analysis = await analyzeContent(envelope.data.content);
|
|
91
|
+
|
|
92
|
+
if (analysis.confidence >= 0.85) {
|
|
93
|
+
return {
|
|
94
|
+
type: 'return',
|
|
95
|
+
data: { approved: true, analysis },
|
|
96
|
+
milestones: [{ name: 'ai_review', value: 'approved' }],
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return {
|
|
101
|
+
type: 'escalation',
|
|
102
|
+
data: { content: envelope.data.content, analysis },
|
|
103
|
+
message: `Review needed (confidence: ${analysis.confidence})`,
|
|
104
|
+
role: 'reviewer',
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Three things to notice:
|
|
110
|
+
|
|
111
|
+
1. **`proxyActivities()`** wraps side-effect functions as durable, checkpointed activities (more on this in [Activities and Durable Execution](#activities-and-durable-execution))
|
|
112
|
+
2. **`envelope.resolver`** — when present, this is a re-run after human resolution; return the human's decision as the final result
|
|
113
|
+
3. **Two return types** — `{ type: 'return' }` completes the task; `{ type: 'escalation' }` pauses and creates an escalation record
|
|
114
|
+
|
|
115
|
+
### Registration
|
|
116
|
+
|
|
117
|
+
Before a workflow can run, Long Tail needs to know about it. Register a workflow config so the interceptor knows how to route escalations:
|
|
118
|
+
|
|
119
|
+
```
|
|
120
|
+
PUT /api/workflows/reviewContent/config
|
|
121
|
+
|
|
122
|
+
{
|
|
123
|
+
"default_role": "reviewer",
|
|
124
|
+
"roles": ["reviewer"],
|
|
125
|
+
"invocable": true
|
|
126
|
+
}
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
`default_role` and `roles` control escalation routing. `invocable: true` exposes the workflow for invocation via the public API.
|
|
130
|
+
|
|
131
|
+
### Starting a Workflow
|
|
132
|
+
|
|
133
|
+
```typescript
|
|
134
|
+
const handle = await lt.client.workflow.start({
|
|
135
|
+
args: [{ data: { contentId: '123', content: 'Review this' }, metadata: {} }],
|
|
136
|
+
taskQueue: 'my-queue',
|
|
137
|
+
workflowName: 'reviewContent',
|
|
138
|
+
workflowId: `review-${Date.now()}`,
|
|
139
|
+
expire: 86_400,
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
const result = await handle.result();
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
## Activities and Durable Execution
|
|
146
|
+
|
|
147
|
+
The workflow above delegates its side effect — calling the LLM — to `analyzeContent` through `proxyActivities`. Activities are where all I/O lives: API calls, LLM invocations, database reads, file operations. They run outside the deterministic sandbox so they can interact with the outside world.
|
|
148
|
+
|
|
149
|
+
```typescript
|
|
150
|
+
// activities.ts
|
|
151
|
+
export async function analyzeContent(content: string): Promise<AnalysisResult> {
|
|
152
|
+
const response = await openai.chat.completions.create({
|
|
153
|
+
model: 'gpt-4',
|
|
154
|
+
messages: [{ role: 'user', content: `Analyze this content: ${content}` }],
|
|
155
|
+
});
|
|
156
|
+
return parseResponse(response);
|
|
157
|
+
}
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
### Checkpointing
|
|
161
|
+
|
|
162
|
+
When an activity completes, its result is checkpointed in PostgreSQL. If the process crashes:
|
|
163
|
+
|
|
164
|
+
- Activities that already completed are **not re-executed** — their cached results are replayed
|
|
165
|
+
- The workflow resumes from the **last checkpoint**, not the beginning
|
|
166
|
+
- External services (OpenAI, databases) are not called twice
|
|
167
|
+
|
|
168
|
+
This is what `proxyActivities()` provides. The raw function becomes a durable checkpoint:
|
|
169
|
+
|
|
170
|
+
```typescript
|
|
171
|
+
const { analyzeContent, extractDocument } =
|
|
172
|
+
Durable.workflow.proxyActivities<typeof activities>({
|
|
173
|
+
activities,
|
|
174
|
+
retryPolicy: {
|
|
175
|
+
maximumAttempts: 2,
|
|
176
|
+
backoffCoefficient: 2,
|
|
177
|
+
maximumInterval: '10 seconds',
|
|
178
|
+
},
|
|
179
|
+
});
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
### Retry Policies
|
|
183
|
+
|
|
184
|
+
Activities retry automatically on failure. Configure the policy per proxy:
|
|
185
|
+
|
|
186
|
+
| Field | Default | Description |
|
|
187
|
+
|-------|---------|-------------|
|
|
188
|
+
| `maximumAttempts` | 1 | Total attempts before giving up |
|
|
189
|
+
| `backoffCoefficient` | 2 | Multiplier between retries |
|
|
190
|
+
| `maximumInterval` | `'30 seconds'` | Cap on delay between retries |
|
|
191
|
+
|
|
192
|
+
If all retries are exhausted, the workflow receives the error and can escalate.
|
|
193
|
+
|
|
194
|
+
## The Interceptor
|
|
195
|
+
|
|
196
|
+
With the workflow written and activities checkpointed, the next question is: what happens when the workflow returns `{ type: 'escalation' }` instead of `{ type: 'return' }`? That's where the interceptor comes in.
|
|
197
|
+
|
|
198
|
+
The interceptor is the machinery that connects your workflow code to Long Tail's task tracking, escalation management, and audit trail. When a workflow is registered with a config, the interceptor wraps every execution.
|
|
199
|
+
|
|
200
|
+
### What It Does
|
|
201
|
+
|
|
202
|
+
1. **Creates a task record** in `lt_tasks` when the workflow starts
|
|
203
|
+
2. **Inspects the return value** when the workflow completes
|
|
204
|
+
3. **If `{ type: 'return' }`** — marks the task as completed, persists milestones
|
|
205
|
+
4. **If `{ type: 'escalation' }`** — creates an escalation record in `lt_escalations`, pauses
|
|
206
|
+
5. **On resolution** — starts a new workflow execution with `envelope.resolver` populated
|
|
207
|
+
6. **Signals the parent** (if orchestrated) with the final result
|
|
208
|
+
|
|
209
|
+
### The Re-run Pattern
|
|
210
|
+
|
|
211
|
+
This is the core escalation lifecycle:
|
|
212
|
+
|
|
213
|
+
```
|
|
214
|
+
Workflow runs --> returns { type: 'escalation' }
|
|
215
|
+
|
|
|
216
|
+
Interceptor creates escalation record
|
|
217
|
+
|
|
|
218
|
+
Workflow is done (not paused -- done)
|
|
219
|
+
|
|
|
220
|
+
Human claims and resolves the escalation
|
|
221
|
+
|
|
|
222
|
+
Interceptor starts a NEW workflow execution
|
|
223
|
+
with envelope.resolver = human's payload
|
|
224
|
+
|
|
|
225
|
+
Workflow checks if (envelope.resolver)
|
|
226
|
+
and returns the human's decision
|
|
227
|
+
|
|
|
228
|
+
Task completes
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
The workflow itself is stateless between runs. The interceptor manages the state transition. This means the same workflow function handles both the initial AI pass and the human-resolved re-run — the `if (envelope.resolver)` check at the top is the only branching needed.
|
|
232
|
+
|
|
233
|
+
### Error Handling
|
|
234
|
+
|
|
235
|
+
If a workflow throws an unhandled error (instead of returning an escalation), the interceptor catches it and creates an error escalation automatically. This prevents silent failures — every error surfaces as a reviewable escalation with the error details.
|
|
236
|
+
|
|
237
|
+
## Escalation Lifecycle
|
|
238
|
+
|
|
239
|
+
The interceptor creates escalation records and manages re-runs, but what does the full lifecycle look like from the outside? Here's the sequence from the REST API perspective.
|
|
240
|
+
|
|
241
|
+
### 1. Workflow Escalates
|
|
242
|
+
|
|
243
|
+
The workflow returns:
|
|
244
|
+
|
|
245
|
+
```typescript
|
|
246
|
+
return {
|
|
247
|
+
type: 'escalation',
|
|
248
|
+
data: { content, analysis },
|
|
249
|
+
message: 'Review needed (confidence: 0.72)',
|
|
250
|
+
role: 'reviewer',
|
|
251
|
+
};
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
The interceptor creates a record in `lt_escalations` with the full payload, the target role, and a `pending` status.
|
|
255
|
+
|
|
256
|
+
### 2. Reviewer Checks the Queue
|
|
257
|
+
|
|
258
|
+
```
|
|
259
|
+
GET /api/escalations/available?role=reviewer
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
```json
|
|
263
|
+
{
|
|
264
|
+
"escalations": [
|
|
265
|
+
{
|
|
266
|
+
"id": "esc-abc123",
|
|
267
|
+
"workflow_type": "reviewContent",
|
|
268
|
+
"message": "Review needed (confidence: 0.72)",
|
|
269
|
+
"data": { "content": "...", "analysis": { "confidence": 0.72 } },
|
|
270
|
+
"role": "reviewer",
|
|
271
|
+
"status": "pending"
|
|
272
|
+
}
|
|
273
|
+
]
|
|
274
|
+
}
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
The escalation carries everything the AI tried and why it wasn't confident.
|
|
278
|
+
|
|
279
|
+
### 3. Claim
|
|
280
|
+
|
|
281
|
+
Claiming locks the escalation so no one else picks it up. The lock is time-boxed — if the reviewer doesn't finish, it goes back to the queue automatically:
|
|
282
|
+
|
|
283
|
+
```
|
|
284
|
+
POST /api/escalations/esc-abc123/claim
|
|
285
|
+
|
|
286
|
+
{ "durationMinutes": 30 }
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
### 4. Resolve
|
|
290
|
+
|
|
291
|
+
The reviewer makes their decision. This triggers a new workflow execution with the resolver's payload:
|
|
292
|
+
|
|
293
|
+
```
|
|
294
|
+
POST /api/escalations/esc-abc123/resolve
|
|
295
|
+
|
|
296
|
+
{
|
|
297
|
+
"resolverPayload": {
|
|
298
|
+
"approved": true,
|
|
299
|
+
"notes": "Content is fine, AI was overly cautious"
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
### 5. Workflow Re-runs
|
|
305
|
+
|
|
306
|
+
The interceptor starts a new execution. This time `envelope.resolver` is populated:
|
|
307
|
+
|
|
308
|
+
```typescript
|
|
309
|
+
if (envelope.resolver) {
|
|
310
|
+
return {
|
|
311
|
+
type: 'return',
|
|
312
|
+
data: { ...envelope.data, resolution: envelope.resolver },
|
|
313
|
+
milestones: [{ name: 'human_review', value: 'resolved' }],
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
The task completes. The escalation is marked resolved. The audit trail captures the full chain: initial AI pass, escalation, human resolution, final result.
|
|
319
|
+
|
|
320
|
+
### Expired Claims
|
|
321
|
+
|
|
322
|
+
If a reviewer claims an escalation but doesn't resolve it within the lock duration, the claim expires and the escalation returns to the queue. Run the cleanup endpoint to release expired claims:
|
|
323
|
+
|
|
324
|
+
```
|
|
325
|
+
POST /api/escalations/release-expired
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
## Composing Workflows
|
|
329
|
+
|
|
330
|
+
A single workflow handles one task — extract a document, validate a record, review content. But real processes chain multiple tasks together, and any step might escalate. Orchestrators coordinate child workflows, each of which can independently succeed or escalate without blocking the others.
|
|
331
|
+
|
|
332
|
+
### `executeLT`
|
|
333
|
+
|
|
334
|
+
```typescript
|
|
335
|
+
import { executeLT } from '@hotmeshio/long-tail';
|
|
336
|
+
|
|
337
|
+
export async function processDocument(envelope: LTEnvelope) {
|
|
338
|
+
const extraction = await executeLT({
|
|
339
|
+
workflowName: 'extractDocument',
|
|
340
|
+
args: [envelope],
|
|
341
|
+
taskQueue: 'long-tail',
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
const validation = await executeLT({
|
|
345
|
+
workflowName: 'validateExtraction',
|
|
346
|
+
args: [{ data: extraction, metadata: envelope.metadata }],
|
|
347
|
+
taskQueue: 'long-tail',
|
|
348
|
+
});
|
|
349
|
+
|
|
350
|
+
return { type: 'return', data: { extraction, validation } };
|
|
351
|
+
}
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
`executeLT` starts the child workflow, creates a task record, and waits for the result. If `extractDocument` escalates to a human, the orchestrator waits. When the escalation is resolved, it resumes and runs `validateExtraction`. No polling, no callbacks — just sequential code.
|
|
355
|
+
|
|
356
|
+
### How It Works
|
|
357
|
+
|
|
358
|
+
Under the hood, `executeLT`:
|
|
359
|
+
|
|
360
|
+
1. Creates a task record with routing metadata
|
|
361
|
+
2. Starts the child workflow with a **severed connection** (isolated from the parent)
|
|
362
|
+
3. Waits for a signal from the child's interceptor
|
|
363
|
+
4. Records the result on the task
|
|
364
|
+
|
|
365
|
+
The severed connection means the child can escalate, fail, and be re-run multiple times without affecting the parent. The parent only resumes when the child completes successfully.
|
|
366
|
+
|
|
367
|
+
### Data Sharing Between Siblings
|
|
368
|
+
|
|
369
|
+
Children that share an `originId` can read each other's completed data automatically. Declare what a workflow `consumes` in its config:
|
|
370
|
+
|
|
371
|
+
```
|
|
372
|
+
PUT /api/workflows/validateExtraction/config
|
|
373
|
+
|
|
374
|
+
{
|
|
375
|
+
"default_role": "reviewer",
|
|
376
|
+
"roles": ["reviewer"],
|
|
377
|
+
"consumes": ["extractDocument"]
|
|
378
|
+
}
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
When `validateExtraction` runs, Long Tail injects sibling results into `envelope.lt.providers`:
|
|
382
|
+
|
|
383
|
+
```typescript
|
|
384
|
+
export async function validateExtraction(envelope: LTEnvelope) {
|
|
385
|
+
// Data from the extractDocument sibling, injected automatically
|
|
386
|
+
const extractionData = envelope.lt?.providers?.extractDocument;
|
|
387
|
+
|
|
388
|
+
// Use it alongside the direct input
|
|
389
|
+
const validation = await validate(envelope.data, extractionData);
|
|
390
|
+
// ...
|
|
391
|
+
}
|
|
392
|
+
```
|
|
393
|
+
|
|
394
|
+
No manual threading required. The orchestrator doesn't need to pass data between children explicitly.
|
|
395
|
+
|
|
396
|
+
### Orchestrator Registration
|
|
397
|
+
|
|
398
|
+
Register the orchestrator as a container workflow:
|
|
399
|
+
|
|
400
|
+
```
|
|
401
|
+
PUT /api/workflows/processDocumentOrchestrator/config
|
|
402
|
+
|
|
403
|
+
{
|
|
404
|
+
"default_role": "reviewer",
|
|
405
|
+
"roles": ["reviewer"]
|
|
406
|
+
}
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
The orchestrator gets its own task record and tracks the lifecycle of its children.
|
|
410
|
+
|
|
411
|
+
## Verify Document Example
|
|
412
|
+
|
|
413
|
+
The concepts above — activities, interceptor, escalation, composition — come together in a concrete example. The `verify-document` workflow demonstrates the full pattern: AI does initial work (OpenAI Vision extraction), validates against a database, and escalates to a human when it isn't confident.
|
|
414
|
+
|
|
415
|
+
### The Pipeline
|
|
416
|
+
|
|
417
|
+
```
|
|
418
|
+
Document images --> Vision extraction --> Database validation --> Match or escalate
|
|
419
|
+
```
|
|
420
|
+
|
|
421
|
+
**Step 1 — List pages.** The workflow loads document page images from storage.
|
|
422
|
+
|
|
423
|
+
**Step 2 — Extract.** Each page is sent to OpenAI's Vision API (`gpt-4o-mini`) as a durable activity. The prompt asks for structured JSON: member ID, name, address, phone, email, emergency contact.
|
|
424
|
+
|
|
425
|
+
**Step 3 — Merge.** Multi-page extractions are merged. The primary page (with member ID) provides the base record; partial pages (emergency contact, additional fields) are folded in.
|
|
426
|
+
|
|
427
|
+
**Step 4 — Validate.** The merged record is compared against the member database. Address fields are checked for exact match. Member status must be `active`.
|
|
428
|
+
|
|
429
|
+
**Step 5 — Return or escalate.** If everything matches, the workflow returns:
|
|
430
|
+
|
|
431
|
+
```typescript
|
|
432
|
+
return {
|
|
433
|
+
type: 'return',
|
|
434
|
+
milestones: [
|
|
435
|
+
{ name: 'pages_processed', value: pages.length },
|
|
436
|
+
{ name: 'extraction', value: 'success' },
|
|
437
|
+
{ name: 'validation', value: 'match' },
|
|
438
|
+
],
|
|
439
|
+
data: {
|
|
440
|
+
documentId,
|
|
441
|
+
memberId: merged.memberId,
|
|
442
|
+
extractedInfo: merged,
|
|
443
|
+
validationResult: 'match',
|
|
444
|
+
confidence: 1.0,
|
|
445
|
+
},
|
|
446
|
+
};
|
|
447
|
+
```
|
|
448
|
+
|
|
449
|
+
If there's a mismatch or missing data, it escalates with full context:
|
|
450
|
+
|
|
451
|
+
```typescript
|
|
452
|
+
return {
|
|
453
|
+
type: 'escalation',
|
|
454
|
+
data: {
|
|
455
|
+
documentId,
|
|
456
|
+
extractedInfo: merged, // what Vision saw
|
|
457
|
+
validationResult: 'mismatch', // why it's escalating
|
|
458
|
+
databaseRecord: record, // what the database has
|
|
459
|
+
reason: 'Address mismatch for MBR-2024-001...',
|
|
460
|
+
},
|
|
461
|
+
message: reason,
|
|
462
|
+
role: 'reviewer',
|
|
463
|
+
};
|
|
464
|
+
```
|
|
465
|
+
|
|
466
|
+
### Durable Activities
|
|
467
|
+
|
|
468
|
+
Each activity (list pages, extract, validate) is wrapped with `proxyActivities()`:
|
|
469
|
+
|
|
470
|
+
```typescript
|
|
471
|
+
const { listDocumentPages, extractMemberInfo, validateMember } =
|
|
472
|
+
Durable.workflow.proxyActivities<ActivitiesType>({
|
|
473
|
+
activities,
|
|
474
|
+
retryPolicy: {
|
|
475
|
+
maximumAttempts: 2,
|
|
476
|
+
backoffCoefficient: 2,
|
|
477
|
+
maximumInterval: '10 seconds',
|
|
478
|
+
},
|
|
479
|
+
});
|
|
480
|
+
```
|
|
481
|
+
|
|
482
|
+
If the process crashes after `extractMemberInfo` completes but before `validateMember` starts, the workflow replays from the last checkpoint. The Vision API is not called again.
|
|
483
|
+
|
|
484
|
+
### The Orchestrator
|
|
485
|
+
|
|
486
|
+
The workflow is invoked through a thin orchestrator:
|
|
487
|
+
|
|
488
|
+
```typescript
|
|
489
|
+
import { executeLT } from '@hotmeshio/long-tail';
|
|
490
|
+
|
|
491
|
+
export async function verifyDocumentOrchestrator(envelope: LTEnvelope) {
|
|
492
|
+
return await executeLT({
|
|
493
|
+
workflowName: 'verifyDocument',
|
|
494
|
+
args: [envelope],
|
|
495
|
+
taskQueue: 'long-tail-verify',
|
|
496
|
+
});
|
|
497
|
+
}
|
|
498
|
+
```
|
|
499
|
+
|
|
500
|
+
The orchestrator creates the task record, starts the child, and waits. If the child escalates, the orchestrator waits for the human to resolve it. When resolved, the child re-runs, completes, and the orchestrator gets the result.
|
|
501
|
+
|
|
502
|
+
### Running the Tests
|
|
503
|
+
|
|
504
|
+
```bash
|
|
505
|
+
# Vision workflow tests (requires OpenAI API key)
|
|
506
|
+
OPENAI_API_KEY=sk-... npm run test:vision
|
|
507
|
+
|
|
508
|
+
# With verbose output
|
|
509
|
+
npx vitest run tests/workflows/verify-document.test.ts --reporter=verbose
|
|
510
|
+
```
|
|
511
|
+
|
|
512
|
+
### MCP-Native Variant
|
|
513
|
+
|
|
514
|
+
The same pipeline also exists as an MCP-native workflow (`verify-document-mcp`) where every activity routes through MCP tools instead of calling functions directly. See the [MCP Guide](mcp.md#mcp-native-workflow) for that perspective.
|
|
515
|
+
|
|
516
|
+
## Escalation Strategies
|
|
517
|
+
|
|
518
|
+
The standard escalation path covers most cases: workflow escalates, human resolves, workflow re-runs. But sometimes the resolver can't fix the problem directly — an upside-down page, a corrupted image, a document in the wrong language. These aren't judgment calls; they're process gaps that need remediation before the workflow can retry.
|
|
519
|
+
|
|
520
|
+
Escalation strategies are a pluggable layer that intercepts resolution and decides what happens next:
|
|
521
|
+
|
|
522
|
+
- **Default strategy** — always re-runs the original workflow with the resolver's payload (today's behavior)
|
|
523
|
+
- **MCP strategy** — checks `resolverPayload._lt.needsTriage`; if set, routes to the `mcpTriage` workflow that calls MCP tools to remediate, then re-invokes the original workflow with corrected data
|
|
524
|
+
|
|
525
|
+
```typescript
|
|
526
|
+
await start({
|
|
527
|
+
database: { ... },
|
|
528
|
+
workers: [ ... ],
|
|
529
|
+
escalation: { strategy: 'mcp' },
|
|
530
|
+
});
|
|
531
|
+
```
|
|
532
|
+
|
|
533
|
+
When the resolver flags `needsTriage`, the triage workflow:
|
|
534
|
+
|
|
535
|
+
1. Queries all upstream tasks for the `originId` to understand what happened
|
|
536
|
+
2. Reads the `_lt.hint` to determine which tools to call
|
|
537
|
+
3. Calls MCP tools (e.g., `rotate_page` to fix an upside-down image)
|
|
538
|
+
4. Re-invokes the failed workflow with corrected data
|
|
539
|
+
5. Signals back through standard channels to the original parent orchestrator
|
|
540
|
+
|
|
541
|
+
The deterministic path is always the default. MCP triage is opt-in. See [Escalation Strategies](escalation-strategies.md) for the full guide.
|
|
542
|
+
|
|
543
|
+
## Milestones
|
|
544
|
+
|
|
545
|
+
As workflows run and escalations resolve, you often want external systems to know what happened. Milestones are structured markers that workflows emit at key decision points:
|
|
546
|
+
|
|
547
|
+
```typescript
|
|
548
|
+
return {
|
|
549
|
+
type: 'return',
|
|
550
|
+
data: { approved: true },
|
|
551
|
+
milestones: [{ name: 'ai_review', value: 'approved' }],
|
|
552
|
+
};
|
|
553
|
+
```
|
|
554
|
+
|
|
555
|
+
Milestones are persisted on the task record and published to any registered event adapters (NATS, SNS, Kafka, webhooks). External systems can react to workflow progress in real time — trigger notifications, update dashboards, or feed analytics — without polling.
|
|
556
|
+
|
|
557
|
+
When a human resolves an escalation, the interceptor automatically appends `escalated` and `resolved_by_human` milestones, so you always know which tasks went through human review.
|
|
558
|
+
|
|
559
|
+
## Roles
|
|
560
|
+
|
|
561
|
+
Roles connect workflows to people. When a workflow escalates to the `reviewer` role, every user assigned that role sees it in their queue. Roles are implicit — they exist the moment you reference them.
|
|
562
|
+
|
|
563
|
+
A role appears in two places: the [workflow config](api/workflows.md#create-or-replace-a-workflow-configuration) (`default_role` and `roles`) and the [user record](api/roles.md) (assigned via the roles API).
|
|
564
|
+
|
|
565
|
+
### Role Types
|
|
566
|
+
|
|
567
|
+
| Type | Permissions |
|
|
568
|
+
|------|-------------|
|
|
569
|
+
| `member` | Claim and resolve escalations for this role |
|
|
570
|
+
| `admin` | Everything a member can do, plus manage users within this role |
|
|
571
|
+
| `superadmin` | Full access — manage all roles, all users, system configuration |
|
|
572
|
+
|
|
573
|
+
A user can hold multiple roles with different types. See the [Users](api/users.md) and [Roles](api/roles.md) API docs for assignment examples.
|
|
574
|
+
|
|
575
|
+
## Testing
|
|
576
|
+
|
|
577
|
+
### Worker Setup
|
|
578
|
+
|
|
579
|
+
Tests follow a consistent pattern: connect, migrate, register interceptors, create a worker, run workflows.
|
|
580
|
+
|
|
581
|
+
The simplest approach uses `registerLT`, which handles the activity worker, workflow interceptor, and activity interceptor in one call:
|
|
582
|
+
|
|
583
|
+
```typescript
|
|
584
|
+
import { Durable } from '@hotmeshio/hotmesh';
|
|
585
|
+
import { Client as Postgres } from 'pg';
|
|
586
|
+
|
|
587
|
+
import { migrate } from '../../services/db/migrate';
|
|
588
|
+
import { registerLT } from '../../services/interceptor';
|
|
589
|
+
import * as myWorkflow from '../../workflows/my-workflow';
|
|
590
|
+
|
|
591
|
+
const { Connection, Client, Worker } = Durable;
|
|
592
|
+
|
|
593
|
+
beforeAll(async () => {
|
|
594
|
+
const connection = { class: Postgres, options: postgres_options };
|
|
595
|
+
|
|
596
|
+
await Connection.connect(connection);
|
|
597
|
+
await migrate();
|
|
598
|
+
|
|
599
|
+
// Register interceptors (activity worker + workflow + activity interceptors)
|
|
600
|
+
await registerLT(connection, { taskQueue: 'lt-interceptor' });
|
|
601
|
+
|
|
602
|
+
// Create workflow worker
|
|
603
|
+
const worker = await Worker.create({
|
|
604
|
+
connection,
|
|
605
|
+
taskQueue: 'test-queue',
|
|
606
|
+
workflow: myWorkflow.myWorkflow,
|
|
607
|
+
});
|
|
608
|
+
await worker.run();
|
|
609
|
+
|
|
610
|
+
client = new Client({ connection });
|
|
611
|
+
});
|
|
612
|
+
|
|
613
|
+
afterAll(async () => {
|
|
614
|
+
Durable.clearInterceptors();
|
|
615
|
+
Durable.clearActivityInterceptors();
|
|
616
|
+
await Durable.shutdown();
|
|
617
|
+
});
|
|
618
|
+
```
|
|
619
|
+
|
|
620
|
+
For finer control, you can register each piece individually. This is what the actual test files do:
|
|
621
|
+
|
|
622
|
+
```typescript
|
|
623
|
+
import { createLTInterceptor } from '../../services/interceptor';
|
|
624
|
+
import { createLTActivityInterceptor } from '../../services/interceptor/activity-interceptor';
|
|
625
|
+
import * as interceptorActivities from '../../services/interceptor/activities';
|
|
626
|
+
|
|
627
|
+
// 1. Activity worker for interceptor DB operations
|
|
628
|
+
await Durable.registerActivityWorker(
|
|
629
|
+
{ connection, taskQueue: 'lt-interceptor' },
|
|
630
|
+
interceptorActivities,
|
|
631
|
+
'lt-interceptor',
|
|
632
|
+
);
|
|
633
|
+
|
|
634
|
+
// 2. Workflow interceptor (escalation, routing, re-runs)
|
|
635
|
+
Durable.registerInterceptor(createLTInterceptor({
|
|
636
|
+
activityTaskQueue: 'lt-interceptor',
|
|
637
|
+
}));
|
|
638
|
+
|
|
639
|
+
// 3. Activity interceptor (milestone event publishing)
|
|
640
|
+
Durable.registerActivityInterceptor(createLTActivityInterceptor());
|
|
641
|
+
```
|
|
642
|
+
|
|
643
|
+
### Testing Escalation
|
|
644
|
+
|
|
645
|
+
To test a workflow that escalates, start the workflow, wait for the escalation to appear, then resolve it:
|
|
646
|
+
|
|
647
|
+
```typescript
|
|
648
|
+
import { waitForEscalation } from '../setup';
|
|
649
|
+
import { resolveEscalation } from '../setup/resolve';
|
|
650
|
+
|
|
651
|
+
it('should escalate and resolve', async () => {
|
|
652
|
+
const workflowId = `test-${Durable.guid()}`;
|
|
653
|
+
|
|
654
|
+
await client.workflow.start({
|
|
655
|
+
args: [{ data: { documentId: 'DOC-001' }, metadata: {} }],
|
|
656
|
+
taskQueue: 'test-queue',
|
|
657
|
+
workflowName: 'verifyDocument',
|
|
658
|
+
workflowId,
|
|
659
|
+
expire: 120,
|
|
660
|
+
});
|
|
661
|
+
|
|
662
|
+
// Poll until the escalation appears (async workflow timing varies)
|
|
663
|
+
const escalations = await waitForEscalation(workflowId);
|
|
664
|
+
expect(escalations.length).toBe(1);
|
|
665
|
+
expect(escalations[0].status).toBe('pending');
|
|
666
|
+
|
|
667
|
+
// Resolve — triggers a re-run
|
|
668
|
+
await resolveEscalation(escalations[0].id, {
|
|
669
|
+
documentId: 'DOC-001',
|
|
670
|
+
memberId: 'MBR-2024-001',
|
|
671
|
+
verified: true,
|
|
672
|
+
});
|
|
673
|
+
|
|
674
|
+
// Wait for the re-run to complete, then verify
|
|
675
|
+
const resolved = await waitForEscalationStatus(
|
|
676
|
+
escalations[0].id, 'resolved', 30_000,
|
|
677
|
+
);
|
|
678
|
+
expect(resolved.status).toBe('resolved');
|
|
679
|
+
});
|
|
680
|
+
```
|
|
681
|
+
|
|
682
|
+
The test utilities are in `tests/setup/`: `waitForEscalation()` and `waitForEscalationStatus()` in `index.ts`, `resolveEscalation()` in `resolve.ts`.
|
|
683
|
+
|
|
684
|
+
### Running Tests
|
|
685
|
+
|
|
686
|
+
```bash
|
|
687
|
+
# All workflow tests (~4-5 min)
|
|
688
|
+
npm run test:workflows
|
|
689
|
+
|
|
690
|
+
# Verify-document workflow (requires OpenAI key)
|
|
691
|
+
OPENAI_API_KEY=sk-... npm run test:vision
|
|
692
|
+
|
|
693
|
+
# Full backend suite
|
|
694
|
+
npm test
|
|
695
|
+
```
|