@hexabot-ai/api 3.2.2-alpha.9 → 3.2.2-beta.1
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/dist/actions/actions.service.d.ts +1 -0
- package/dist/actions/actions.service.js +19 -1
- package/dist/actions/actions.service.js.map +1 -1
- package/dist/actions/types.d.ts +3 -1
- package/dist/app.controller.d.ts +1 -0
- package/dist/app.controller.js +1 -0
- package/dist/app.controller.js.map +1 -1
- package/dist/app.module.js +13 -1
- package/dist/app.module.js.map +1 -1
- package/dist/config/index.js +14 -2
- package/dist/config/index.js.map +1 -1
- package/dist/config/types.d.ts +9 -0
- package/dist/extensions/actions/ai/agent.action.d.ts +1 -1
- package/dist/extensions/actions/ai/agent.action.js +6 -3
- package/dist/extensions/actions/ai/agent.action.js.map +1 -1
- package/dist/extensions/actions/ai/ai-base.action.d.ts +1 -1
- package/dist/extensions/actions/ai/ai-base.action.js +18 -5
- package/dist/extensions/actions/ai/ai-base.action.js.map +1 -1
- package/dist/extensions/actions/ai/generate-object.base.action.d.ts +1 -1
- package/dist/extensions/actions/ai/generate-object.base.action.js +3 -2
- package/dist/extensions/actions/ai/generate-object.base.action.js.map +1 -1
- package/dist/extensions/actions/ai/generate-text.base.action.d.ts +1 -1
- package/dist/extensions/actions/ai/generate-text.base.action.js +3 -2
- package/dist/extensions/actions/ai/generate-text.base.action.js.map +1 -1
- package/dist/extensions/actions/ai/mcp.binding.js +2 -0
- package/dist/extensions/actions/ai/mcp.binding.js.map +1 -1
- package/dist/extensions/actions/ai/memory.binding.js +2 -0
- package/dist/extensions/actions/ai/memory.binding.js.map +1 -1
- package/dist/extensions/actions/ai/retrieve-content-rag.action.js +2 -0
- package/dist/extensions/actions/ai/retrieve-content-rag.action.js.map +1 -1
- package/dist/extensions/actions/messaging/list.action.d.ts +2 -2
- package/dist/extensions/actions/messaging/list.action.js +7 -0
- package/dist/extensions/actions/messaging/list.action.js.map +1 -1
- package/dist/extensions/actions/subscriber/update-labels.action.js +3 -0
- package/dist/extensions/actions/subscriber/update-labels.action.js.map +1 -1
- package/dist/extensions/actions/web/http-request.action.js +12 -1
- package/dist/extensions/actions/web/http-request.action.js.map +1 -1
- package/dist/extensions/actions/workflow/call-workflow.action.d.ts +19 -0
- package/dist/extensions/actions/workflow/call-workflow.action.js +83 -0
- package/dist/extensions/actions/workflow/call-workflow.action.js.map +1 -0
- package/dist/extensions/actions/workflow/i18n/en.translations.json +9 -0
- package/dist/extensions/actions/workflow/i18n/fr.translations.json +9 -0
- package/dist/extensions/index.d.ts +1 -0
- package/dist/extensions/index.js +1 -0
- package/dist/extensions/index.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/mcp/controllers/mcp-token.controller.d.ts +47 -0
- package/dist/mcp/controllers/mcp-token.controller.js +70 -0
- package/dist/mcp/controllers/mcp-token.controller.js.map +1 -0
- package/dist/mcp/decorators/mcp-permission.decorator.d.ts +8 -0
- package/dist/mcp/decorators/mcp-permission.decorator.js +11 -0
- package/dist/mcp/decorators/mcp-permission.decorator.js.map +1 -0
- package/dist/mcp/dto/mcp-token.dto.d.ts +17 -0
- package/dist/mcp/dto/mcp-token.dto.js +40 -0
- package/dist/mcp/dto/mcp-token.dto.js.map +1 -0
- package/dist/mcp/entities/mcp-token.entity.d.ts +111 -0
- package/dist/mcp/entities/mcp-token.entity.js +69 -0
- package/dist/mcp/entities/mcp-token.entity.js.map +1 -0
- package/dist/mcp/guards/hexabot-mcp-token.guard.d.ts +9 -0
- package/dist/mcp/guards/hexabot-mcp-token.guard.js +49 -0
- package/dist/mcp/guards/hexabot-mcp-token.guard.js.map +1 -0
- package/dist/mcp/guards/mcp-permission.guard.d.ts +9 -0
- package/dist/mcp/guards/mcp-permission.guard.js +47 -0
- package/dist/mcp/guards/mcp-permission.guard.js.map +1 -0
- package/dist/mcp/mcp-api.module.d.ts +2 -0
- package/dist/mcp/mcp-api.module.js +65 -0
- package/dist/mcp/mcp-api.module.js.map +1 -0
- package/dist/mcp/repositories/mcp-token.repository.d.ts +8 -0
- package/dist/mcp/repositories/mcp-token.repository.js +41 -0
- package/dist/mcp/repositories/mcp-token.repository.js.map +1 -0
- package/dist/mcp/services/mcp-token.service.d.ts +24 -0
- package/dist/mcp/services/mcp-token.service.js +106 -0
- package/dist/mcp/services/mcp-token.service.js.map +1 -0
- package/dist/mcp/tools/catalog-mcp.tools.d.ts +63 -0
- package/dist/mcp/tools/catalog-mcp.tools.js +135 -0
- package/dist/mcp/tools/catalog-mcp.tools.js.map +1 -0
- package/dist/mcp/tools/cms-mcp.tools.d.ts +128 -0
- package/dist/mcp/tools/cms-mcp.tools.js +242 -0
- package/dist/mcp/tools/cms-mcp.tools.js.map +1 -0
- package/dist/mcp/tools/credential-mcp.tools.d.ts +19 -0
- package/dist/mcp/tools/credential-mcp.tools.js +82 -0
- package/dist/mcp/tools/credential-mcp.tools.js.map +1 -0
- package/dist/mcp/tools/hexabot-mcp-tool.base.d.ts +22 -0
- package/dist/mcp/tools/hexabot-mcp-tool.base.js +48 -0
- package/dist/mcp/tools/hexabot-mcp-tool.base.js.map +1 -0
- package/dist/mcp/tools/hexabot-mcp.schemas.d.ts +41 -0
- package/dist/mcp/tools/hexabot-mcp.schemas.js +36 -0
- package/dist/mcp/tools/hexabot-mcp.schemas.js.map +1 -0
- package/dist/mcp/tools/hexabot-mcp.tools.d.ts +1 -0
- package/dist/mcp/tools/hexabot-mcp.tools.js +18 -0
- package/dist/mcp/tools/hexabot-mcp.tools.js.map +1 -0
- package/dist/mcp/tools/hexabot-mcp.utils.d.ts +2 -0
- package/dist/mcp/tools/hexabot-mcp.utils.js +21 -0
- package/dist/mcp/tools/hexabot-mcp.utils.js.map +1 -0
- package/dist/mcp/tools/index.d.ts +22 -0
- package/dist/mcp/tools/index.js +50 -0
- package/dist/mcp/tools/index.js.map +1 -0
- package/dist/mcp/tools/mcp-server-mcp.tools.d.ts +77 -0
- package/dist/mcp/tools/mcp-server-mcp.tools.js +130 -0
- package/dist/mcp/tools/mcp-server-mcp.tools.js.map +1 -0
- package/dist/mcp/tools/memory-definition-mcp.tools.d.ts +57 -0
- package/dist/mcp/tools/memory-definition-mcp.tools.js +128 -0
- package/dist/mcp/tools/memory-definition-mcp.tools.js.map +1 -0
- package/dist/mcp/tools/workflow-mcp.helper.d.ts +156 -0
- package/dist/mcp/tools/workflow-mcp.helper.js +128 -0
- package/dist/mcp/tools/workflow-mcp.helper.js.map +1 -0
- package/dist/mcp/tools/workflow-mcp.tools.d.ts +790 -0
- package/dist/mcp/tools/workflow-mcp.tools.js +227 -0
- package/dist/mcp/tools/workflow-mcp.tools.js.map +1 -0
- package/dist/mcp/tools/workflow-run-mcp.tools.d.ts +349 -0
- package/dist/mcp/tools/workflow-run-mcp.tools.js +242 -0
- package/dist/mcp/tools/workflow-run-mcp.tools.js.map +1 -0
- package/dist/mcp/tools/workflow-version-mcp.tools.d.ts +238 -0
- package/dist/mcp/tools/workflow-version-mcp.tools.js +368 -0
- package/dist/mcp/tools/workflow-version-mcp.tools.js.map +1 -0
- package/dist/mcp/types.d.ts +7 -0
- package/dist/mcp/types.js +3 -0
- package/dist/mcp/types.js.map +1 -0
- package/dist/static/assets/{browser-ponyfill-Qk7qpw-i.js → browser-ponyfill-D1dKqhtP.js} +2 -2
- package/dist/static/assets/{cssMode-CdhvY6oD.js → cssMode-DplazEqd.js} +1 -1
- package/dist/static/assets/{freemarker2-BPoURZH1.js → freemarker2-oSA1bGAm.js} +1 -1
- package/dist/static/assets/{handlebars-C4oubER1.js → handlebars-CcC7EgN2.js} +1 -1
- package/dist/static/assets/{html-CDE5RgVN.js → html-CcFTB7eP.js} +1 -1
- package/dist/static/assets/{htmlMode-C8edIUUq.js → htmlMode-g5g2wCUD.js} +1 -1
- package/dist/static/assets/{index-C1NxBXuS.js → index-CMp45yDR.js} +2379 -2330
- package/dist/static/assets/{index-BI1BtkYv.css → index-D-b9KTZd.css} +1 -1
- package/dist/static/assets/{javascript-Wp5m7adK.js → javascript-Dv0J7ioP.js} +1 -1
- package/dist/static/assets/{jsonMode-0eww5Srr.js → jsonMode-CzBkKOe4.js} +1 -1
- package/dist/static/assets/{liquid-DTktXITv.js → liquid-CptRs_ZU.js} +1 -1
- package/dist/static/assets/{lspLanguageFeatures-BzIRlKhn.js → lspLanguageFeatures-DXNU_upS.js} +1 -1
- package/dist/static/assets/{mdx-D508emqD.js → mdx-af00eMTm.js} +1 -1
- package/dist/static/assets/{python-BfhIq2kX.js → python-CNcx7i3L.js} +1 -1
- package/dist/static/assets/{razor-C7F8fOds.js → razor-BL_MSmP7.js} +1 -1
- package/dist/static/assets/{tsMode-B8zPu6Wl.js → tsMode-BWD4nt2F.js} +1 -1
- package/dist/static/assets/{typescript-Dew3VavZ.js → typescript-BbKUic6G.js} +1 -1
- package/dist/static/assets/{xml-B_2tc6Mh.js → xml-DFx6l4N0.js} +1 -1
- package/dist/static/assets/{yaml-pHcw4KrS.js → yaml-BNuTzPxo.js} +1 -1
- package/dist/static/index.html +2 -2
- package/dist/static/locales/en/translation.json +89 -4
- package/dist/static/locales/fr/translation.json +90 -5
- package/dist/transfer/adapters/content-type-transfer.adapter.d.ts +14 -0
- package/dist/transfer/adapters/content-type-transfer.adapter.js +102 -0
- package/dist/transfer/adapters/content-type-transfer.adapter.js.map +1 -0
- package/dist/transfer/adapters/credential-transfer.adapter.d.ts +13 -0
- package/dist/transfer/adapters/credential-transfer.adapter.js +110 -0
- package/dist/transfer/adapters/credential-transfer.adapter.js.map +1 -0
- package/dist/transfer/adapters/label-transfer.adapter.d.ts +15 -0
- package/dist/transfer/adapters/label-transfer.adapter.js +189 -0
- package/dist/transfer/adapters/label-transfer.adapter.js.map +1 -0
- package/dist/transfer/adapters/mcp-server-transfer.adapter.d.ts +16 -0
- package/dist/transfer/adapters/mcp-server-transfer.adapter.js +169 -0
- package/dist/transfer/adapters/mcp-server-transfer.adapter.js.map +1 -0
- package/dist/transfer/adapters/memory-definition-transfer.adapter.d.ts +14 -0
- package/dist/transfer/adapters/memory-definition-transfer.adapter.js +111 -0
- package/dist/transfer/adapters/memory-definition-transfer.adapter.js.map +1 -0
- package/dist/transfer/index.d.ts +7 -0
- package/dist/transfer/index.js +24 -0
- package/dist/transfer/index.js.map +1 -0
- package/dist/transfer/workflow-transfer-adapter.registry.d.ts +27 -0
- package/dist/transfer/workflow-transfer-adapter.registry.js +164 -0
- package/dist/transfer/workflow-transfer-adapter.registry.js.map +1 -0
- package/dist/transfer/workflow-transfer-definition.service.d.ts +40 -0
- package/dist/transfer/workflow-transfer-definition.service.js +353 -0
- package/dist/transfer/workflow-transfer-definition.service.js.map +1 -0
- package/dist/transfer/workflow-transfer-resource-adapter.d.ts +33 -0
- package/dist/transfer/workflow-transfer-resource-adapter.js +81 -0
- package/dist/transfer/workflow-transfer-resource-adapter.js.map +1 -0
- package/dist/transfer/workflow-transfer.controller.d.ts +9 -0
- package/dist/transfer/workflow-transfer.controller.js +63 -0
- package/dist/transfer/workflow-transfer.controller.js.map +1 -0
- package/dist/transfer/workflow-transfer.module.d.ts +2 -0
- package/dist/transfer/workflow-transfer.module.js +47 -0
- package/dist/transfer/workflow-transfer.module.js.map +1 -0
- package/dist/transfer/workflow-transfer.service.d.ts +51 -0
- package/dist/transfer/workflow-transfer.service.js +509 -0
- package/dist/transfer/workflow-transfer.service.js.map +1 -0
- package/dist/transfer/workflow-transfer.types.d.ts +31 -0
- package/dist/transfer/workflow-transfer.types.js +35 -0
- package/dist/transfer/workflow-transfer.types.js.map +1 -0
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/dist/types/event-emitter.d.ts +12 -5
- package/dist/user/dto/user.dto.d.ts +1 -1
- package/dist/user/guards/ability.guard.js +12 -2
- package/dist/user/guards/ability.guard.js.map +1 -1
- package/dist/utils/test/fixtures/label-group.d.ts +1 -1
- package/dist/utils/test/fixtures/label.d.ts +1 -1
- package/dist/utils/test/fixtures/subscriber.d.ts +1 -1
- package/dist/utils/test/fixtures/workflow-run.js +4 -1
- package/dist/utils/test/fixtures/workflow-run.js.map +1 -1
- package/dist/utils/test/port.d.ts +8 -0
- package/dist/utils/test/port.js +34 -0
- package/dist/utils/test/port.js.map +1 -0
- package/dist/websocket/websocket.gateway.d.ts +2 -1
- package/dist/websocket/websocket.gateway.js.map +1 -1
- package/dist/workflow/contexts/workflow-runtime.context.d.ts +3 -1
- package/dist/workflow/contexts/workflow-runtime.context.js +5 -0
- package/dist/workflow/contexts/workflow-runtime.context.js.map +1 -1
- package/dist/workflow/controllers/workflow-run.controller.js +2 -0
- package/dist/workflow/controllers/workflow-run.controller.js.map +1 -1
- package/dist/workflow/dto/workflow-run.dto.d.ts +1 -0
- package/dist/workflow/dto/workflow-run.dto.js +11 -0
- package/dist/workflow/dto/workflow-run.dto.js.map +1 -1
- package/dist/workflow/entities/memory-record.entity.d.ts +2 -0
- package/dist/workflow/entities/workflow-run.entity.d.ts +62 -0
- package/dist/workflow/entities/workflow-run.entity.js +15 -1
- package/dist/workflow/entities/workflow-run.entity.js.map +1 -1
- package/dist/workflow/index.d.ts +7 -0
- package/dist/workflow/index.js +7 -0
- package/dist/workflow/index.js.map +1 -1
- package/dist/workflow/repositories/workflow-run.repository.js +7 -1
- package/dist/workflow/repositories/workflow-run.repository.js.map +1 -1
- package/dist/workflow/resource-refs.d.ts +17 -0
- package/dist/workflow/resource-refs.js +15 -0
- package/dist/workflow/resource-refs.js.map +1 -0
- package/dist/workflow/services/agentic.service.d.ts +9 -2
- package/dist/workflow/services/agentic.service.js +142 -19
- package/dist/workflow/services/agentic.service.js.map +1 -1
- package/dist/workflow/services/workflow-run.service.d.ts +3 -0
- package/dist/workflow/services/workflow-run.service.js +75 -3
- package/dist/workflow/services/workflow-run.service.js.map +1 -1
- package/dist/workflow/services/workflow-version.service.d.ts +2 -0
- package/dist/workflow/services/workflow-version.service.js +19 -0
- package/dist/workflow/services/workflow-version.service.js.map +1 -1
- package/dist/workflow/services/workflow.service.d.ts +13 -9
- package/dist/workflow/services/workflow.service.js +64 -35
- package/dist/workflow/services/workflow.service.js.map +1 -1
- package/dist/workflow/types.d.ts +31 -0
- package/dist/workflow/types.js +2 -1
- package/dist/workflow/types.js.map +1 -1
- package/dist/workflow/workflow.module.js +3 -0
- package/dist/workflow/workflow.module.js.map +1 -1
- package/package.json +8 -6
- package/src/actions/actions.service.ts +34 -4
- package/src/actions/types.ts +6 -5
- package/src/app.controller.ts +1 -0
- package/src/app.module.ts +17 -1
- package/src/config/index.ts +17 -2
- package/src/config/types.ts +9 -0
- package/src/extensions/actions/ai/agent.action.ts +6 -1
- package/src/extensions/actions/ai/ai-base.action.ts +39 -5
- package/src/extensions/actions/ai/generate-object.base.action.ts +3 -0
- package/src/extensions/actions/ai/generate-text.base.action.ts +3 -0
- package/src/extensions/actions/ai/mcp.binding.ts +2 -0
- package/src/extensions/actions/ai/memory.binding.ts +2 -0
- package/src/extensions/actions/ai/retrieve-content-rag.action.ts +2 -0
- package/src/extensions/actions/messaging/list.action.ts +7 -0
- package/src/extensions/actions/subscriber/update-labels.action.ts +3 -0
- package/src/extensions/actions/web/http-request.action.ts +17 -1
- package/src/extensions/actions/workflow/call-workflow.action.ts +112 -0
- package/src/extensions/actions/workflow/i18n/en.translations.json +9 -0
- package/src/extensions/actions/workflow/i18n/fr.translations.json +9 -0
- package/src/extensions/index.ts +2 -0
- package/src/index.ts +2 -0
- package/src/mcp/README.md +277 -0
- package/src/mcp/controllers/mcp-token.controller.ts +64 -0
- package/src/mcp/decorators/mcp-permission.decorator.ts +23 -0
- package/src/mcp/dto/mcp-token.dto.ts +48 -0
- package/src/mcp/entities/mcp-token.entity.ts +60 -0
- package/src/mcp/guards/hexabot-mcp-token.guard.ts +55 -0
- package/src/mcp/guards/mcp-permission.guard.ts +61 -0
- package/src/mcp/mcp-api.module.ts +61 -0
- package/src/mcp/repositories/mcp-token.repository.ts +34 -0
- package/src/mcp/services/mcp-token.service.ts +138 -0
- package/src/mcp/tools/catalog-mcp.tools.ts +122 -0
- package/src/mcp/tools/cms-mcp.tools.ts +239 -0
- package/src/mcp/tools/credential-mcp.tools.ts +81 -0
- package/src/mcp/tools/hexabot-mcp-tool.base.ts +74 -0
- package/src/mcp/tools/hexabot-mcp.schemas.ts +55 -0
- package/src/mcp/tools/hexabot-mcp.tools.ts +7 -0
- package/src/mcp/tools/hexabot-mcp.utils.ts +29 -0
- package/src/mcp/tools/index.ts +51 -0
- package/src/mcp/tools/mcp-server-mcp.tools.ts +135 -0
- package/src/mcp/tools/memory-definition-mcp.tools.ts +125 -0
- package/src/mcp/tools/workflow-mcp.helper.ts +219 -0
- package/src/mcp/tools/workflow-mcp.tools.ts +271 -0
- package/src/mcp/tools/workflow-run-mcp.tools.ts +292 -0
- package/src/mcp/tools/workflow-version-mcp.tools.ts +459 -0
- package/src/mcp/types.ts +14 -0
- package/src/transfer/adapters/content-type-transfer.adapter.ts +154 -0
- package/src/transfer/adapters/credential-transfer.adapter.ts +158 -0
- package/src/transfer/adapters/label-transfer.adapter.ts +291 -0
- package/src/transfer/adapters/mcp-server-transfer.adapter.ts +255 -0
- package/src/transfer/adapters/memory-definition-transfer.adapter.ts +167 -0
- package/src/transfer/index.ts +19 -0
- package/src/transfer/workflow-transfer-adapter.registry.ts +250 -0
- package/src/transfer/workflow-transfer-definition.service.ts +587 -0
- package/src/transfer/workflow-transfer-resource-adapter.ts +132 -0
- package/src/transfer/workflow-transfer.controller.ts +86 -0
- package/src/transfer/workflow-transfer.module.ts +46 -0
- package/src/transfer/workflow-transfer.service.ts +858 -0
- package/src/transfer/workflow-transfer.types.ts +98 -0
- package/src/user/guards/ability.guard.ts +15 -3
- package/src/utils/test/fixtures/workflow-run.ts +4 -1
- package/src/utils/test/port.ts +48 -0
- package/src/websocket/websocket.gateway.ts +2 -1
- package/src/workflow/contexts/workflow-runtime.context.ts +12 -1
- package/src/workflow/controllers/workflow-run.controller.ts +2 -0
- package/src/workflow/dto/workflow-run.dto.ts +10 -0
- package/src/workflow/entities/workflow-run.entity.ts +22 -1
- package/src/workflow/index.ts +14 -0
- package/src/workflow/repositories/workflow-run.repository.ts +7 -1
- package/src/workflow/resource-refs.ts +44 -0
- package/src/workflow/services/agentic.service.ts +271 -30
- package/src/workflow/services/workflow-run.service.ts +120 -7
- package/src/workflow/services/workflow-version.service.ts +33 -0
- package/src/workflow/services/workflow.service.ts +86 -31
- package/src/workflow/types.ts +52 -0
- package/src/workflow/workflow.module.ts +3 -0
- package/types/event-emitter.d.ts +12 -5
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
StepExecutionRecord,
|
|
12
12
|
WorkflowRunner,
|
|
13
13
|
} from '@hexabot-ai/agentic';
|
|
14
|
-
import {
|
|
14
|
+
import { WorkflowFull, WorkflowRunFull } from '@hexabot-ai/types';
|
|
15
15
|
import { Injectable } from '@nestjs/common';
|
|
16
16
|
|
|
17
17
|
import { ActionService } from '@/actions/actions.service';
|
|
@@ -20,16 +20,30 @@ import { I18nService } from '@/i18n/services/i18n.service';
|
|
|
20
20
|
import { LoggerService } from '@/logger/logger.service';
|
|
21
21
|
|
|
22
22
|
import { WorkflowContextFactory } from '../contexts/workflow-context-factory';
|
|
23
|
-
import { WorkflowRuntimeContext } from '../contexts/workflow-runtime.context';
|
|
23
|
+
import type { WorkflowRuntimeContext } from '../contexts/workflow-runtime.context';
|
|
24
24
|
import { TriggerEventWrapper } from '../lib/trigger-event-wrapper';
|
|
25
25
|
import { parseWorkflowDefinition } from '../lib/workflow-definition';
|
|
26
|
-
import {
|
|
26
|
+
import type {
|
|
27
|
+
CallWorkflowOptions,
|
|
28
|
+
CallWorkflowResult,
|
|
29
|
+
RunStrategy,
|
|
30
|
+
RunWorkflowOptions,
|
|
31
|
+
WorkflowCallService,
|
|
32
|
+
WorkflowResult,
|
|
33
|
+
} from '../types';
|
|
27
34
|
|
|
28
35
|
import { WorkflowRunService } from './workflow-run.service';
|
|
29
36
|
import { WorkflowService } from './workflow.service';
|
|
30
37
|
|
|
38
|
+
const MAX_CALL_STACK_DEPTH = 10;
|
|
39
|
+
|
|
40
|
+
type RunWorkflowExecution = {
|
|
41
|
+
run: WorkflowRunFull;
|
|
42
|
+
result: WorkflowResult;
|
|
43
|
+
};
|
|
44
|
+
|
|
31
45
|
@Injectable()
|
|
32
|
-
export class AgenticService {
|
|
46
|
+
export class AgenticService implements WorkflowCallService {
|
|
33
47
|
constructor(
|
|
34
48
|
private readonly workflowService: WorkflowService,
|
|
35
49
|
private readonly workflowRunService: WorkflowRunService,
|
|
@@ -42,9 +56,11 @@ export class AgenticService {
|
|
|
42
56
|
|
|
43
57
|
/**
|
|
44
58
|
* Process an event by resuming a suspended workflow run if it exists,
|
|
45
|
-
* otherwise start a new run using the latest configured workflow
|
|
59
|
+
* otherwise start a new run using the latest configured workflow.
|
|
46
60
|
*/
|
|
47
|
-
async handleEvent(
|
|
61
|
+
async handleEvent(
|
|
62
|
+
event: TriggerEventWrapper,
|
|
63
|
+
): Promise<WorkflowRunFull | null> {
|
|
48
64
|
const initiator = event.getInitiator();
|
|
49
65
|
const requestedWorkflowId = event.getWorkflowId();
|
|
50
66
|
const threadId = event.getThreadId();
|
|
@@ -54,7 +70,7 @@ export class AgenticService {
|
|
|
54
70
|
'Skipping workflow execution due to missing event initiator',
|
|
55
71
|
);
|
|
56
72
|
|
|
57
|
-
return;
|
|
73
|
+
return null;
|
|
58
74
|
}
|
|
59
75
|
|
|
60
76
|
try {
|
|
@@ -72,9 +88,14 @@ export class AgenticService {
|
|
|
72
88
|
suspendedStep: suspendedRun.suspendedStep,
|
|
73
89
|
suspensionReason: suspendedRun.suspensionReason,
|
|
74
90
|
});
|
|
75
|
-
await this.runWorkflow({ mode: 'resume', run: suspendedRun, event });
|
|
76
91
|
|
|
77
|
-
|
|
92
|
+
const execution = await this.runWorkflow({
|
|
93
|
+
mode: 'resume',
|
|
94
|
+
run: suspendedRun,
|
|
95
|
+
event,
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
return execution.run;
|
|
78
99
|
}
|
|
79
100
|
|
|
80
101
|
const workflowToRun = requestedWorkflowId
|
|
@@ -85,46 +106,102 @@ export class AgenticService {
|
|
|
85
106
|
requestedWorkflowId: requestedWorkflowId ?? null,
|
|
86
107
|
});
|
|
87
108
|
|
|
88
|
-
return;
|
|
109
|
+
return null;
|
|
89
110
|
}
|
|
90
111
|
if (!workflowToRun.definition) {
|
|
91
112
|
this.logger.warn('Workflow definition is missing', {
|
|
92
113
|
workflowId: workflowToRun.id,
|
|
93
114
|
});
|
|
94
115
|
|
|
95
|
-
return;
|
|
116
|
+
return null;
|
|
96
117
|
}
|
|
97
118
|
|
|
98
119
|
this.logger.log('Starting workflow run', {
|
|
99
120
|
workflowId: workflowToRun.id,
|
|
100
121
|
});
|
|
101
122
|
|
|
102
|
-
await this.runWorkflow({
|
|
123
|
+
const execution = await this.runWorkflow({
|
|
103
124
|
mode: 'start',
|
|
104
125
|
workflow: workflowToRun,
|
|
105
126
|
event,
|
|
106
127
|
});
|
|
128
|
+
|
|
129
|
+
return execution.run;
|
|
107
130
|
} catch (err) {
|
|
108
131
|
this.logger.error(
|
|
109
132
|
'Unable to process incoming event through agentic workflow',
|
|
110
133
|
err,
|
|
111
134
|
);
|
|
135
|
+
|
|
136
|
+
return null;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Call another workflow from a running parent workflow.
|
|
142
|
+
*
|
|
143
|
+
* This is intentionally an API-level operation: workflows are database
|
|
144
|
+
* records, so type enforcement, call-stack validation, child run creation,
|
|
145
|
+
* and durable parent/child linkage all live here instead of in the agentic
|
|
146
|
+
* DSL runner.
|
|
147
|
+
*/
|
|
148
|
+
async callWorkflow({
|
|
149
|
+
workflowId,
|
|
150
|
+
input,
|
|
151
|
+
parentContext,
|
|
152
|
+
}: CallWorkflowOptions): Promise<CallWorkflowResult> {
|
|
153
|
+
const parentRun = await this.workflowRunService.findOneAndPopulate(
|
|
154
|
+
parentContext.workflowRunId,
|
|
155
|
+
);
|
|
156
|
+
if (!parentRun) {
|
|
157
|
+
throw new Error(
|
|
158
|
+
`Unable to load parent workflow run ${parentContext.workflowRunId}`,
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const workflow = await this.workflowService.findOneAndPopulate(workflowId);
|
|
163
|
+
if (!workflow) {
|
|
164
|
+
throw new Error(`Workflow with ID ${workflowId} not found`);
|
|
165
|
+
}
|
|
166
|
+
if (!workflow.definition) {
|
|
167
|
+
throw new Error(`Workflow ${workflowId} is missing a definition`);
|
|
168
|
+
}
|
|
169
|
+
if (workflow.type !== parentRun.workflow.type) {
|
|
170
|
+
throw new Error(
|
|
171
|
+
`Workflow ${workflowId} has type "${workflow.type}" and cannot be called from a "${parentRun.workflow.type}" workflow`,
|
|
172
|
+
);
|
|
112
173
|
}
|
|
174
|
+
|
|
175
|
+
await this.assertCanCallWorkflow(parentRun, workflow.id);
|
|
176
|
+
|
|
177
|
+
const execution = await this.runWorkflow({
|
|
178
|
+
mode: 'start',
|
|
179
|
+
workflow,
|
|
180
|
+
event: parentContext.event,
|
|
181
|
+
parentRun,
|
|
182
|
+
input,
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
return this.toCallWorkflowResult(execution.run, execution.result);
|
|
113
186
|
}
|
|
114
187
|
|
|
115
188
|
/**
|
|
116
|
-
* Shared runner lifecycle for
|
|
189
|
+
* Shared runner lifecycle for starts, external resumes, and internal parent
|
|
190
|
+
* resumes. When a resumed child reaches a terminal state, this method also
|
|
191
|
+
* unwinds the suspended parent with the child result payload.
|
|
117
192
|
*/
|
|
118
|
-
private async runWorkflow(
|
|
193
|
+
private async runWorkflow(
|
|
194
|
+
options: RunWorkflowOptions,
|
|
195
|
+
): Promise<RunWorkflowExecution> {
|
|
119
196
|
const { event, mode } = options;
|
|
120
197
|
const run =
|
|
121
198
|
mode === 'start'
|
|
122
|
-
? await this.createRun(options.workflow, event
|
|
199
|
+
? await this.createRun(options.workflow, event, {
|
|
200
|
+
parentRun: options.parentRun,
|
|
201
|
+
input: options.input,
|
|
202
|
+
})
|
|
123
203
|
: options.run;
|
|
124
|
-
const
|
|
125
|
-
run.workflow.id,
|
|
126
|
-
);
|
|
127
|
-
const definition = this.resolveRunDefinition(run, workflow);
|
|
204
|
+
const definition = this.resolveRunDefinition(run);
|
|
128
205
|
if (!definition) {
|
|
129
206
|
throw new Error('Workflow definition is required to run the workflow');
|
|
130
207
|
}
|
|
@@ -142,7 +219,7 @@ export class AgenticService {
|
|
|
142
219
|
this.logger.debug('Preparing workflow runner', {
|
|
143
220
|
mode,
|
|
144
221
|
runId: run.id,
|
|
145
|
-
workflowId: workflow?.id,
|
|
222
|
+
workflowId: run.workflow?.id,
|
|
146
223
|
triggeredById: run.triggeredBy?.id,
|
|
147
224
|
});
|
|
148
225
|
|
|
@@ -151,6 +228,7 @@ export class AgenticService {
|
|
|
151
228
|
run,
|
|
152
229
|
context,
|
|
153
230
|
workflowInstance,
|
|
231
|
+
mode === 'resume' ? options.resumeData : undefined,
|
|
154
232
|
);
|
|
155
233
|
|
|
156
234
|
this.logger.debug('Marking workflow run as running', {
|
|
@@ -180,6 +258,17 @@ export class AgenticService {
|
|
|
180
258
|
} catch (err) {
|
|
181
259
|
this.logger.error('Workflow runner threw during execution', err);
|
|
182
260
|
await this.markRunFailed(run, strategy.runner, context.state, err);
|
|
261
|
+
if (mode === 'resume') {
|
|
262
|
+
await this.resumeParentRunFromChild(
|
|
263
|
+
run,
|
|
264
|
+
{
|
|
265
|
+
status: 'failed',
|
|
266
|
+
error: err,
|
|
267
|
+
snapshot: strategy.runner.getSnapshot(),
|
|
268
|
+
},
|
|
269
|
+
event,
|
|
270
|
+
);
|
|
271
|
+
}
|
|
183
272
|
|
|
184
273
|
throw err;
|
|
185
274
|
}
|
|
@@ -198,6 +287,16 @@ export class AgenticService {
|
|
|
198
287
|
strategy.resumeData,
|
|
199
288
|
context,
|
|
200
289
|
);
|
|
290
|
+
|
|
291
|
+
if (mode === 'resume') {
|
|
292
|
+
await this.resumeParentRunFromChild(run, result, event);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
const persistedRun = await this.workflowRunService.findOneAndPopulate(
|
|
296
|
+
run.id,
|
|
297
|
+
);
|
|
298
|
+
|
|
299
|
+
return { run: persistedRun ?? run, result };
|
|
201
300
|
}
|
|
202
301
|
|
|
203
302
|
/**
|
|
@@ -220,12 +319,16 @@ export class AgenticService {
|
|
|
220
319
|
|
|
221
320
|
/**
|
|
222
321
|
* Build the execution strategy for starting a new workflow or resuming an existing one.
|
|
322
|
+
*
|
|
323
|
+
* `resumeData` is supplied only for internal child-to-parent unwinds; normal
|
|
324
|
+
* event resumes use the current event input.
|
|
223
325
|
*/
|
|
224
326
|
private async createRunStrategy(
|
|
225
327
|
mode: 'start' | 'resume',
|
|
226
328
|
run: WorkflowRunFull,
|
|
227
329
|
context: WorkflowRuntimeContext,
|
|
228
330
|
workflowInstance: AgenticWorkflow,
|
|
331
|
+
resumeData?: unknown,
|
|
229
332
|
): Promise<RunStrategy> {
|
|
230
333
|
if (mode === 'start') {
|
|
231
334
|
const runner = await workflowInstance.buildAsyncRunner({
|
|
@@ -245,7 +348,7 @@ export class AgenticService {
|
|
|
245
348
|
};
|
|
246
349
|
}
|
|
247
350
|
|
|
248
|
-
const latestInput = context.event.buildInput();
|
|
351
|
+
const latestInput = resumeData ?? context.event.buildInput();
|
|
249
352
|
const runner = await workflowInstance.buildRunnerFromState({
|
|
250
353
|
state: this.buildExecutionState(run),
|
|
251
354
|
context,
|
|
@@ -278,10 +381,18 @@ export class AgenticService {
|
|
|
278
381
|
|
|
279
382
|
/**
|
|
280
383
|
* Create a workflow run record and load it with relations.
|
|
384
|
+
*
|
|
385
|
+
* Child calls inherit the same event metadata, initiator, and thread as the
|
|
386
|
+
* parent, but store an explicit `parentRun` relation so future events can
|
|
387
|
+
* resume the deepest suspended child before returning to the parent.
|
|
281
388
|
*/
|
|
282
389
|
private async createRun(
|
|
283
390
|
workflow: WorkflowFull,
|
|
284
391
|
event: TriggerEventWrapper,
|
|
392
|
+
options: {
|
|
393
|
+
parentRun?: WorkflowRunFull;
|
|
394
|
+
input?: Record<string, unknown>;
|
|
395
|
+
} = {},
|
|
285
396
|
): Promise<WorkflowRunFull> {
|
|
286
397
|
const initiator = event.getInitiator();
|
|
287
398
|
if (!workflow.definition) {
|
|
@@ -296,7 +407,8 @@ export class AgenticService {
|
|
|
296
407
|
workflowVersion: workflow.currentVersion?.id ?? null,
|
|
297
408
|
triggeredBy: initiator.id,
|
|
298
409
|
thread: event.getThreadId() ?? null,
|
|
299
|
-
|
|
410
|
+
parentRun: options.parentRun?.id ?? null,
|
|
411
|
+
input: options.input ?? event.buildInput(),
|
|
300
412
|
context: Object.keys(initialContext).length > 0 ? initialContext : null,
|
|
301
413
|
metadata: event.getMetadata(),
|
|
302
414
|
});
|
|
@@ -310,18 +422,147 @@ export class AgenticService {
|
|
|
310
422
|
}
|
|
311
423
|
|
|
312
424
|
/**
|
|
313
|
-
*
|
|
425
|
+
* Validate that a workflow call does not create a recursive or overly deep stack.
|
|
426
|
+
*
|
|
427
|
+
* The walk follows active parent links, so only the current call stack is
|
|
428
|
+
* rejected; a workflow can still be called again after an earlier stack has
|
|
429
|
+
* completed.
|
|
430
|
+
*/
|
|
431
|
+
private async assertCanCallWorkflow(
|
|
432
|
+
parentRun: WorkflowRunFull,
|
|
433
|
+
targetWorkflowId: string,
|
|
434
|
+
): Promise<void> {
|
|
435
|
+
let current: WorkflowRunFull | null = parentRun;
|
|
436
|
+
let depth = 0;
|
|
437
|
+
|
|
438
|
+
while (current) {
|
|
439
|
+
depth += 1;
|
|
440
|
+
if (depth >= MAX_CALL_STACK_DEPTH) {
|
|
441
|
+
throw new Error(
|
|
442
|
+
`Workflow call stack depth cannot exceed ${MAX_CALL_STACK_DEPTH}`,
|
|
443
|
+
);
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
if (current.workflow.id === targetWorkflowId) {
|
|
447
|
+
throw new Error(
|
|
448
|
+
`Workflow call cycle detected for workflow ${targetWorkflowId}`,
|
|
449
|
+
);
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
const parentRunId = this.resolveRunId(current.parentRun);
|
|
453
|
+
current = parentRunId
|
|
454
|
+
? await this.workflowRunService.findOneAndPopulate(parentRunId)
|
|
455
|
+
: null;
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
/**
|
|
460
|
+
* Convert a child run terminal/suspended result into the `call_workflow`
|
|
461
|
+
* contract. The same payload shape is used for immediate returns and for
|
|
462
|
+
* resuming a parent after the child completed in a later event.
|
|
314
463
|
*/
|
|
315
|
-
private
|
|
464
|
+
private toCallWorkflowResult(
|
|
316
465
|
run: WorkflowRunFull,
|
|
317
|
-
|
|
318
|
-
) {
|
|
466
|
+
result: WorkflowResult,
|
|
467
|
+
): CallWorkflowResult {
|
|
468
|
+
if (result.status === 'finished') {
|
|
469
|
+
return {
|
|
470
|
+
status: 'finished',
|
|
471
|
+
workflow_id: run.workflow.id,
|
|
472
|
+
workflow_run_id: run.id,
|
|
473
|
+
output: result.output,
|
|
474
|
+
};
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
if (result.status === 'suspended') {
|
|
478
|
+
return {
|
|
479
|
+
status: 'suspended',
|
|
480
|
+
workflow_id: run.workflow.id,
|
|
481
|
+
workflow_run_id: run.id,
|
|
482
|
+
};
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
return {
|
|
486
|
+
status: 'failed',
|
|
487
|
+
workflow_id: run.workflow.id,
|
|
488
|
+
workflow_run_id: run.id,
|
|
489
|
+
error: this.stringifyError(result.error),
|
|
490
|
+
};
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
/**
|
|
494
|
+
* Resume a suspended parent run after its child run completes or fails.
|
|
495
|
+
*
|
|
496
|
+
* This may cascade through multiple ancestors because resuming the parent can
|
|
497
|
+
* itself finish a child call that belongs to another suspended parent.
|
|
498
|
+
*/
|
|
499
|
+
private async resumeParentRunFromChild(
|
|
500
|
+
childRun: WorkflowRunFull,
|
|
501
|
+
result: WorkflowResult,
|
|
502
|
+
event: TriggerEventWrapper,
|
|
503
|
+
): Promise<void> {
|
|
504
|
+
if (result.status === 'suspended') {
|
|
505
|
+
return;
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
const parentRunId = this.resolveRunId(childRun.parentRun);
|
|
509
|
+
if (!parentRunId) {
|
|
510
|
+
return;
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
const parentRun =
|
|
514
|
+
await this.workflowRunService.findOneAndPopulate(parentRunId);
|
|
515
|
+
if (!parentRun) {
|
|
516
|
+
this.logger.warn('Unable to resume missing parent workflow run', {
|
|
517
|
+
childRunId: childRun.id,
|
|
518
|
+
parentRunId,
|
|
519
|
+
});
|
|
520
|
+
|
|
521
|
+
return;
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
if (parentRun.status !== 'suspended') {
|
|
525
|
+
this.logger.warn(
|
|
526
|
+
'Skipping parent workflow resume because it is not suspended',
|
|
527
|
+
{
|
|
528
|
+
childRunId: childRun.id,
|
|
529
|
+
parentRunId,
|
|
530
|
+
parentStatus: parentRun.status,
|
|
531
|
+
},
|
|
532
|
+
);
|
|
533
|
+
|
|
534
|
+
return;
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
await this.runWorkflow({
|
|
538
|
+
mode: 'resume',
|
|
539
|
+
run: parentRun,
|
|
540
|
+
event,
|
|
541
|
+
resumeData: this.toCallWorkflowResult(childRun, result),
|
|
542
|
+
});
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
/**
|
|
546
|
+
* Resolve a workflow run relation from either a populated object or id.
|
|
547
|
+
*/
|
|
548
|
+
private resolveRunId(
|
|
549
|
+
run: string | { id?: string | null } | null | undefined,
|
|
550
|
+
): string | null {
|
|
551
|
+
if (typeof run === 'string') {
|
|
552
|
+
return run;
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
return run?.id ?? null;
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
/**
|
|
559
|
+
* Resolve the workflow definition snapshot used by the current run.
|
|
560
|
+
*/
|
|
561
|
+
private resolveRunDefinition(run: WorkflowRunFull) {
|
|
319
562
|
const definitionYml = run.workflowVersion?.definitionYml;
|
|
320
563
|
if (typeof definitionYml === 'string' && definitionYml.trim() !== '') {
|
|
321
564
|
return parseWorkflowDefinition(definitionYml);
|
|
322
565
|
}
|
|
323
|
-
|
|
324
|
-
return workflow?.definition;
|
|
325
566
|
}
|
|
326
567
|
|
|
327
568
|
/**
|
|
@@ -460,7 +701,7 @@ export class AgenticService {
|
|
|
460
701
|
private pickOutput(
|
|
461
702
|
result: WorkflowResult,
|
|
462
703
|
state: ExecutionState | undefined,
|
|
463
|
-
|
|
704
|
+
previousOutput?: Record<string, unknown> | null,
|
|
464
705
|
): Record<string, unknown> | null {
|
|
465
706
|
if (result.status === 'finished' && result.output) {
|
|
466
707
|
return result.output;
|
|
@@ -470,7 +711,7 @@ export class AgenticService {
|
|
|
470
711
|
return state.output;
|
|
471
712
|
}
|
|
472
713
|
|
|
473
|
-
return
|
|
714
|
+
return previousOutput ?? null;
|
|
474
715
|
}
|
|
475
716
|
|
|
476
717
|
/**
|
|
@@ -4,9 +4,10 @@
|
|
|
4
4
|
* Full terms: see LICENSE.md.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import { WorkflowSnapshot } from '@hexabot-ai/agentic';
|
|
7
|
+
import { EWorkflowRunStatus, WorkflowSnapshot } from '@hexabot-ai/agentic';
|
|
8
8
|
import { WorkflowRun, WorkflowRunFull } from '@hexabot-ai/types';
|
|
9
9
|
import { Injectable } from '@nestjs/common';
|
|
10
|
+
import { In } from 'typeorm';
|
|
10
11
|
|
|
11
12
|
import { BaseOrmService } from '@/utils/generics/base-orm.service';
|
|
12
13
|
|
|
@@ -18,6 +19,12 @@ type StateUpdate = {
|
|
|
18
19
|
context?: Record<string, unknown> | null;
|
|
19
20
|
};
|
|
20
21
|
|
|
22
|
+
const ACTIVE_WORKFLOW_RUN_STATUSES = [
|
|
23
|
+
EWorkflowRunStatus.IDLE,
|
|
24
|
+
EWorkflowRunStatus.RUNNING,
|
|
25
|
+
EWorkflowRunStatus.SUSPENDED,
|
|
26
|
+
];
|
|
27
|
+
|
|
21
28
|
@Injectable()
|
|
22
29
|
export class WorkflowRunService extends BaseOrmService<WorkflowRunOrmEntity> {
|
|
23
30
|
/**
|
|
@@ -130,25 +137,131 @@ export class WorkflowRunService extends BaseOrmService<WorkflowRunOrmEntity> {
|
|
|
130
137
|
}
|
|
131
138
|
|
|
132
139
|
/**
|
|
133
|
-
* Find the
|
|
140
|
+
* Find the suspended run that should receive the next event.
|
|
141
|
+
*
|
|
142
|
+
* For call-and-return stacks, the parent can be suspended while the child is
|
|
143
|
+
* either running or suspended for the same initiator/thread. The next external
|
|
144
|
+
* event must resume the deepest suspended leaf first; once that child
|
|
145
|
+
* completes, AgenticService resumes its parent internally with the child
|
|
146
|
+
* result.
|
|
134
147
|
*
|
|
135
148
|
* @param triggeredById - Identifier of the subscriber whose suspended run should be fetched.
|
|
136
|
-
* @param
|
|
137
|
-
* @
|
|
149
|
+
* @param threadId - Optional thread id for conversational continuity.
|
|
150
|
+
* @param workflowId - Optional workflow id; a parent match still resolves to its child leaf.
|
|
151
|
+
* @returns The suspended leaf run populated with relations, or `null` when none exists.
|
|
138
152
|
*/
|
|
139
153
|
async findSuspendedRunByInitiator(
|
|
140
154
|
triggeredById: string,
|
|
141
155
|
threadId?: string,
|
|
142
156
|
workflowId?: string,
|
|
143
157
|
): Promise<WorkflowRunFull | null> {
|
|
144
|
-
|
|
158
|
+
const runs = await this.findAndPopulate({
|
|
145
159
|
where: {
|
|
146
160
|
triggeredBy: { id: triggeredById },
|
|
147
161
|
...(threadId ? { thread: { id: threadId } } : {}),
|
|
148
|
-
status:
|
|
149
|
-
...(workflowId ? { workflow: { id: workflowId } } : {}),
|
|
162
|
+
status: In(ACTIVE_WORKFLOW_RUN_STATUSES),
|
|
150
163
|
},
|
|
151
164
|
order: { suspendedAt: 'DESC', createdAt: 'DESC' },
|
|
152
165
|
});
|
|
166
|
+
|
|
167
|
+
if (runs.length === 0) {
|
|
168
|
+
return null;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const runsById = new Map(runs.map((run) => [run.id, run]));
|
|
172
|
+
const parentRunIds = new Set(
|
|
173
|
+
runs
|
|
174
|
+
.map((run) => this.resolveRunId(run.parentRun))
|
|
175
|
+
.filter((value): value is string => typeof value === 'string'),
|
|
176
|
+
);
|
|
177
|
+
// Leaves are active runs that are not the parent of another unfinished
|
|
178
|
+
// candidate. Only suspended leaves can consume external events; running or
|
|
179
|
+
// idle leaves are still in flight and block their suspended ancestors.
|
|
180
|
+
const leaves = runs.filter((run) => !parentRunIds.has(run.id));
|
|
181
|
+
const candidates = (leaves.length > 0 ? leaves : runs)
|
|
182
|
+
.filter((run) => run.status === EWorkflowRunStatus.SUSPENDED)
|
|
183
|
+
.filter((run) =>
|
|
184
|
+
workflowId
|
|
185
|
+
? this.isRunInWorkflowStack(run, workflowId, runsById)
|
|
186
|
+
: true,
|
|
187
|
+
);
|
|
188
|
+
|
|
189
|
+
return this.pickDeepestSuspendedRun(candidates, runsById);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
private resolveRunId(
|
|
193
|
+
run: string | { id?: string | null } | null | undefined,
|
|
194
|
+
): string | null {
|
|
195
|
+
if (typeof run === 'string') {
|
|
196
|
+
return run;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
return run?.id ?? null;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
private isRunInWorkflowStack(
|
|
203
|
+
run: WorkflowRunFull,
|
|
204
|
+
workflowId: string,
|
|
205
|
+
runsById: Map<string, WorkflowRunFull>,
|
|
206
|
+
): boolean {
|
|
207
|
+
let current: WorkflowRunFull | undefined = run;
|
|
208
|
+
const visited = new Set<string>();
|
|
209
|
+
|
|
210
|
+
while (current && !visited.has(current.id)) {
|
|
211
|
+
visited.add(current.id);
|
|
212
|
+
if (current.workflow.id === workflowId) {
|
|
213
|
+
return true;
|
|
214
|
+
}
|
|
215
|
+
const parentRunId = this.resolveRunId(current.parentRun);
|
|
216
|
+
current = parentRunId ? runsById.get(parentRunId) : undefined;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
return false;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
private pickDeepestSuspendedRun(
|
|
223
|
+
runs: WorkflowRunFull[],
|
|
224
|
+
runsById: Map<string, WorkflowRunFull>,
|
|
225
|
+
): WorkflowRunFull | null {
|
|
226
|
+
if (runs.length === 0) {
|
|
227
|
+
return null;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
const getDepth = (run: WorkflowRunFull): number => {
|
|
231
|
+
let depth = 0;
|
|
232
|
+
let current: WorkflowRunFull | undefined = run;
|
|
233
|
+
const visited = new Set<string>();
|
|
234
|
+
|
|
235
|
+
while (current && !visited.has(current.id)) {
|
|
236
|
+
visited.add(current.id);
|
|
237
|
+
depth += 1;
|
|
238
|
+
const parentRunId = this.resolveRunId(current.parentRun);
|
|
239
|
+
current = parentRunId ? runsById.get(parentRunId) : undefined;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
return depth;
|
|
243
|
+
};
|
|
244
|
+
const getTime = (value?: Date | string | null): number => {
|
|
245
|
+
if (!value) {
|
|
246
|
+
return 0;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
const date = value instanceof Date ? value : new Date(value);
|
|
250
|
+
const timestamp = date.getTime();
|
|
251
|
+
|
|
252
|
+
return Number.isNaN(timestamp) ? 0 : timestamp;
|
|
253
|
+
};
|
|
254
|
+
|
|
255
|
+
return [...runs].sort((a, b) => {
|
|
256
|
+
const depthDiff = getDepth(b) - getDepth(a);
|
|
257
|
+
if (depthDiff !== 0) {
|
|
258
|
+
return depthDiff;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
return (
|
|
262
|
+
getTime(b.suspendedAt) - getTime(a.suspendedAt) ||
|
|
263
|
+
getTime(b.createdAt) - getTime(a.createdAt)
|
|
264
|
+
);
|
|
265
|
+
})[0];
|
|
153
266
|
}
|
|
154
267
|
}
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
import { WorkflowVersion } from '@hexabot-ai/types';
|
|
8
8
|
import { Injectable, NotFoundException } from '@nestjs/common';
|
|
9
|
+
import { EntityManager } from 'typeorm';
|
|
9
10
|
|
|
10
11
|
import { BaseOrmService } from '@/utils/generics/base-orm.service';
|
|
11
12
|
|
|
@@ -63,6 +64,38 @@ export class WorkflowVersionService extends BaseOrmService<WorkflowVersionOrmEnt
|
|
|
63
64
|
});
|
|
64
65
|
}
|
|
65
66
|
|
|
67
|
+
/**
|
|
68
|
+
* Persist a workflow version snapshot with the provided transaction manager.
|
|
69
|
+
*
|
|
70
|
+
* This method mirrors createSnapshot's version-number behavior without
|
|
71
|
+
* escaping the caller's transaction or emitting repository-level events.
|
|
72
|
+
*/
|
|
73
|
+
public async createSnapshotWithManager(
|
|
74
|
+
manager: EntityManager,
|
|
75
|
+
params: WorkflowNewVersionDto,
|
|
76
|
+
): Promise<WorkflowVersionOrmEntity> {
|
|
77
|
+
const latestVersion = await manager.findOne(WorkflowVersionOrmEntity, {
|
|
78
|
+
where: { workflow: { id: params.workflow } },
|
|
79
|
+
order: { version: 'DESC' },
|
|
80
|
+
});
|
|
81
|
+
const nextVersion = latestVersion ? latestVersion.version + 1 : 1;
|
|
82
|
+
|
|
83
|
+
return await manager.save(
|
|
84
|
+
WorkflowVersionOrmEntity,
|
|
85
|
+
manager.create(WorkflowVersionOrmEntity, {
|
|
86
|
+
workflow: { id: params.workflow },
|
|
87
|
+
version: nextVersion,
|
|
88
|
+
definitionYml: params.definitionYml,
|
|
89
|
+
action: params.action,
|
|
90
|
+
message: params.message,
|
|
91
|
+
parentVersion: params.parentVersion
|
|
92
|
+
? { id: params.parentVersion }
|
|
93
|
+
: null,
|
|
94
|
+
createdBy: params.createdBy ? { id: params.createdBy } : null,
|
|
95
|
+
}),
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
|
|
66
99
|
/**
|
|
67
100
|
* Restore a workflow to a prior version by creating a new snapshot.
|
|
68
101
|
*
|