@elizaos/plugin-workflow 2.0.0-beta.1 → 2.0.3-beta.6
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/LICENSE +21 -0
- package/README.md +28 -26
- package/dist/actions/eval-code.d.ts +12 -0
- package/dist/actions/eval-code.d.ts.map +1 -0
- package/dist/actions/eval-code.js +59 -0
- package/dist/actions/eval-code.js.map +1 -0
- package/dist/actions/index.d.ts +1 -0
- package/dist/actions/index.d.ts.map +1 -1
- package/dist/actions/index.js +1 -0
- package/dist/actions/index.js.map +1 -1
- package/dist/actions/workflow.d.ts +7 -0
- package/dist/actions/workflow.d.ts.map +1 -1
- package/dist/actions/workflow.js +462 -10
- package/dist/actions/workflow.js.map +1 -1
- package/dist/db/schema.d.ts +196 -0
- package/dist/db/schema.d.ts.map +1 -1
- package/dist/db/schema.js +23 -0
- package/dist/db/schema.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -64
- package/dist/index.js.map +1 -1
- package/dist/lib/automations-builder.d.ts.map +1 -1
- package/dist/lib/automations-builder.js +10 -35
- package/dist/lib/automations-builder.js.map +1 -1
- package/dist/lib/automations-types.d.ts +2 -2
- package/dist/lib/automations-types.d.ts.map +1 -1
- package/dist/lib/automations-types.js.map +1 -1
- package/dist/lib/index.d.ts +0 -2
- package/dist/lib/index.d.ts.map +1 -1
- package/dist/lib/index.js +1 -2
- package/dist/lib/index.js.map +1 -1
- package/dist/lib/workflow-clarification.d.ts +2 -2
- package/dist/lib/workflow-clarification.d.ts.map +1 -1
- package/dist/lib/workflow-clarification.js +15 -11
- package/dist/lib/workflow-clarification.js.map +1 -1
- package/dist/plugin-routes.d.ts.map +1 -1
- package/dist/plugin-routes.js +6 -0
- package/dist/plugin-routes.js.map +1 -1
- package/dist/providers/activeWorkflows.js +2 -2
- package/dist/providers/activeWorkflows.js.map +1 -1
- package/dist/providers/workflowStatus.js +1 -1
- package/dist/providers/workflowStatus.js.map +1 -1
- package/dist/routes/workflow-routes.d.ts.map +1 -1
- package/dist/routes/workflow-routes.js +68 -2
- package/dist/routes/workflow-routes.js.map +1 -1
- package/dist/routes/workflows.d.ts.map +1 -1
- package/dist/routes/workflows.js +5 -1
- package/dist/routes/workflows.js.map +1 -1
- package/dist/services/embedded-workflow-service.d.ts +74 -17
- package/dist/services/embedded-workflow-service.d.ts.map +1 -1
- package/dist/services/embedded-workflow-service.js +343 -149
- package/dist/services/embedded-workflow-service.js.map +1 -1
- package/dist/services/smithers-runtime.d.ts +47 -0
- package/dist/services/smithers-runtime.d.ts.map +1 -0
- package/dist/services/smithers-runtime.js +444 -0
- package/dist/services/smithers-runtime.js.map +1 -0
- package/dist/services/workflow-credential-store.js +1 -1
- package/dist/services/workflow-credential-store.js.map +1 -1
- package/dist/services/workflow-dispatch.d.ts +31 -1
- package/dist/services/workflow-dispatch.d.ts.map +1 -1
- package/dist/services/workflow-dispatch.js +75 -10
- package/dist/services/workflow-dispatch.js.map +1 -1
- package/dist/services/workflow-service.d.ts +27 -1
- package/dist/services/workflow-service.d.ts.map +1 -1
- package/dist/services/workflow-service.js +133 -11
- package/dist/services/workflow-service.js.map +1 -1
- package/dist/trigger-routes.d.ts +2 -18
- package/dist/trigger-routes.d.ts.map +1 -1
- package/dist/trigger-routes.js +11 -39
- package/dist/trigger-routes.js.map +1 -1
- package/dist/types/index.d.ts +82 -2
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js.map +1 -1
- package/dist/types/workflow-contracts.d.ts +118 -0
- package/dist/types/workflow-contracts.d.ts.map +1 -0
- package/dist/types/workflow-contracts.js +2 -0
- package/dist/types/workflow-contracts.js.map +1 -0
- package/dist/utils/catalog.js +2 -2
- package/dist/utils/catalog.js.map +1 -1
- package/dist/utils/clarification.d.ts +1 -1
- package/dist/utils/clarification.d.ts.map +1 -1
- package/dist/utils/clarification.js +15 -4
- package/dist/utils/clarification.js.map +1 -1
- package/dist/utils/context.js +1 -1
- package/dist/utils/context.js.map +1 -1
- package/dist/utils/evaluation-samples.d.ts +6 -0
- package/dist/utils/evaluation-samples.d.ts.map +1 -0
- package/dist/utils/evaluation-samples.js +216 -0
- package/dist/utils/evaluation-samples.js.map +1 -0
- package/dist/utils/execution-diagnostics.d.ts +26 -0
- package/dist/utils/execution-diagnostics.d.ts.map +1 -0
- package/dist/utils/execution-diagnostics.js +159 -0
- package/dist/utils/execution-diagnostics.js.map +1 -0
- package/dist/utils/generation.d.ts.map +1 -1
- package/dist/utils/generation.js +134 -19
- package/dist/utils/generation.js.map +1 -1
- package/dist/utils/host-capabilities.d.ts.map +1 -1
- package/dist/utils/host-capabilities.js +20 -5
- package/dist/utils/host-capabilities.js.map +1 -1
- package/dist/utils/inferSyntheticOutputSchema.js +3 -3
- package/dist/utils/inferSyntheticOutputSchema.js.map +1 -1
- package/dist/utils/outputSchema.js +1 -1
- package/dist/utils/outputSchema.js.map +1 -1
- package/dist/utils/validateAndRepair.js +10 -10
- package/dist/utils/validateAndRepair.js.map +1 -1
- package/dist/utils/workflow-prompts/draftIntent.d.ts +1 -1
- package/dist/utils/workflow-prompts/draftIntent.d.ts.map +1 -1
- package/dist/utils/workflow-prompts/draftIntent.js +1 -1
- package/dist/utils/workflow-prompts/keywordExtraction.d.ts +1 -1
- package/dist/utils/workflow-prompts/keywordExtraction.d.ts.map +1 -1
- package/dist/utils/workflow-prompts/keywordExtraction.js +1 -1
- package/dist/utils/workflow-prompts/workflowGeneration.d.ts +1 -1
- package/dist/utils/workflow-prompts/workflowGeneration.d.ts.map +1 -1
- package/dist/utils/workflow-prompts/workflowGeneration.js +4 -4
- package/dist/utils/workflow-prompts/workflowMatching.d.ts +1 -1
- package/dist/utils/workflow-prompts/workflowMatching.d.ts.map +1 -1
- package/dist/utils/workflow-prompts/workflowMatching.js +1 -1
- package/dist/utils/workflow.d.ts +1 -0
- package/dist/utils/workflow.d.ts.map +1 -1
- package/dist/utils/workflow.js +44 -8
- package/dist/utils/workflow.js.map +1 -1
- package/package.json +27 -8
- package/registry-entry.json +25 -0
- package/src/actions/eval-code.ts +81 -0
- package/src/actions/index.ts +1 -0
- package/src/actions/workflow.ts +518 -10
- package/src/db/schema.ts +31 -0
- package/src/index.ts +9 -82
- package/src/lib/automations-builder.ts +11 -35
- package/src/lib/automations-types.ts +1 -2
- package/src/lib/index.ts +0 -8
- package/src/lib/workflow-clarification.ts +18 -13
- package/src/plugin-routes.ts +6 -0
- package/src/providers/activeWorkflows.ts +2 -2
- package/src/providers/workflowStatus.ts +1 -1
- package/src/routes/workflow-routes.ts +100 -2
- package/src/routes/workflows.ts +5 -1
- package/src/services/embedded-workflow-service.ts +447 -172
- package/src/services/smithers-runtime.ts +526 -0
- package/src/services/workflow-credential-store.ts +1 -1
- package/src/services/workflow-dispatch.ts +116 -13
- package/src/services/workflow-service.ts +186 -10
- package/src/trigger-routes.ts +12 -70
- package/src/types/index.ts +94 -2
- package/src/types/workflow-contracts.ts +166 -0
- package/src/utils/catalog.ts +2 -2
- package/src/utils/clarification.ts +19 -5
- package/src/utils/context.ts +1 -1
- package/src/utils/evaluation-samples.ts +239 -0
- package/src/utils/execution-diagnostics.ts +192 -0
- package/src/utils/generation.ts +224 -32
- package/src/utils/host-capabilities.ts +21 -5
- package/src/utils/inferSyntheticOutputSchema.ts +3 -3
- package/src/utils/outputSchema.ts +1 -1
- package/src/utils/validateAndRepair.ts +10 -10
- package/src/utils/workflow-prompts/draftIntent.ts +1 -1
- package/src/utils/workflow-prompts/keywordExtraction.ts +1 -1
- package/src/utils/workflow-prompts/workflowGeneration.ts +4 -4
- package/src/utils/workflow-prompts/workflowMatching.ts +1 -1
- package/src/utils/workflow.ts +56 -8
- package/dist/lib/legacy-task-migration.d.ts +0 -20
- package/dist/lib/legacy-task-migration.d.ts.map +0 -1
- package/dist/lib/legacy-task-migration.js +0 -110
- package/dist/lib/legacy-task-migration.js.map +0 -1
- package/dist/lib/legacy-text-trigger-migration.d.ts +0 -18
- package/dist/lib/legacy-text-trigger-migration.d.ts.map +0 -1
- package/dist/lib/legacy-text-trigger-migration.js +0 -131
- package/dist/lib/legacy-text-trigger-migration.js.map +0 -1
- package/src/lib/legacy-task-migration.ts +0 -143
- package/src/lib/legacy-text-trigger-migration.ts +0 -178
|
@@ -27,10 +27,31 @@ export interface WorkflowDispatchResult {
|
|
|
27
27
|
ok: boolean;
|
|
28
28
|
error?: string;
|
|
29
29
|
executionId?: string;
|
|
30
|
+
/**
|
|
31
|
+
* True when the call was short-circuited by an idempotency-key match.
|
|
32
|
+
* Callers (the trigger dispatcher, dashboards) can record a dedup
|
|
33
|
+
* instead of treating the call as a fresh execution.
|
|
34
|
+
*/
|
|
35
|
+
dedup?: boolean;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Optional, structured dispatch options. The `idempotencyKey` field is
|
|
40
|
+
* the durable contract: same workflow + same key → at most one
|
|
41
|
+
* execution. Passed inline through the legacy `payload` shape (key
|
|
42
|
+
* `__idempotencyKey`) when the caller can't pass a second argument.
|
|
43
|
+
*/
|
|
44
|
+
export interface WorkflowDispatchOptions {
|
|
45
|
+
triggerData?: Record<string, unknown>;
|
|
46
|
+
idempotencyKey?: string;
|
|
30
47
|
}
|
|
31
48
|
|
|
32
49
|
export interface WorkflowDispatchService {
|
|
33
|
-
execute(
|
|
50
|
+
execute(
|
|
51
|
+
workflowId: string,
|
|
52
|
+
payload?: Record<string, unknown>,
|
|
53
|
+
options?: WorkflowDispatchOptions
|
|
54
|
+
): Promise<WorkflowDispatchResult>;
|
|
34
55
|
}
|
|
35
56
|
|
|
36
57
|
interface WorkflowDispatchServiceEntry extends WorkflowDispatchService {
|
|
@@ -38,6 +59,24 @@ interface WorkflowDispatchServiceEntry extends WorkflowDispatchService {
|
|
|
38
59
|
capabilityDescription: string;
|
|
39
60
|
}
|
|
40
61
|
|
|
62
|
+
/**
|
|
63
|
+
* Pull `__idempotencyKey` out of the legacy `payload` shape so existing
|
|
64
|
+
* callers (the trigger dispatcher's `event` payload) can attach a key
|
|
65
|
+
* without growing the signature. The wrapper key is stripped before the
|
|
66
|
+
* payload is forwarded as `triggerData`.
|
|
67
|
+
*/
|
|
68
|
+
function partitionPayload(payload: Record<string, unknown> | undefined): {
|
|
69
|
+
triggerData: Record<string, unknown>;
|
|
70
|
+
idempotencyKey?: string;
|
|
71
|
+
} {
|
|
72
|
+
if (!payload) return { triggerData: {} };
|
|
73
|
+
const { __idempotencyKey, ...rest } = payload;
|
|
74
|
+
return {
|
|
75
|
+
triggerData: rest,
|
|
76
|
+
idempotencyKey: typeof __idempotencyKey === 'string' ? __idempotencyKey : undefined,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
|
|
41
80
|
interface RuntimeServiceRegistry {
|
|
42
81
|
set(serviceType: string, services: WorkflowDispatchServiceEntry[]): void;
|
|
43
82
|
}
|
|
@@ -71,12 +110,33 @@ function getRuntimeServiceRegistry(runtime: IAgentRuntime): RuntimeServiceRegist
|
|
|
71
110
|
/**
|
|
72
111
|
* Construct the dispatch service. Registered under `WORKFLOW_DISPATCH` on the
|
|
73
112
|
* runtime by the plugin's `init` lifecycle hook.
|
|
113
|
+
*
|
|
114
|
+
* Idempotency contract: when a caller passes an `idempotencyKey` (either via
|
|
115
|
+
* the explicit `options.idempotencyKey` or via the legacy
|
|
116
|
+
* `payload.__idempotencyKey`), the dispatch service first looks up an
|
|
117
|
+
* existing execution row for `(workflowId, idempotencyKey)`. If one exists,
|
|
118
|
+
* the new run is suppressed and the prior execution id is returned with
|
|
119
|
+
* `{ ok: true, dedup: true }`. Scheduled workflow dispatches use a
|
|
120
|
+
* minute-bucketed key so two simultaneous schedule fires collapse to one
|
|
121
|
+
* execution.
|
|
122
|
+
*
|
|
123
|
+
* Concurrent dispatches that race past the lookup are still safely
|
|
124
|
+
* coalesced because the embedded service persists the idempotency key on
|
|
125
|
+
* the execution row, so the second-to-write completes but is detectable
|
|
126
|
+
* as a duplicate on later lookups.
|
|
74
127
|
*/
|
|
75
128
|
export function createWorkflowDispatchService(runtime: IAgentRuntime): WorkflowDispatchService {
|
|
129
|
+
// Track in-flight executions by `(workflowId, idempotencyKey)` so that
|
|
130
|
+
// two concurrent dispatches inside the same process collapse onto one
|
|
131
|
+
// run. The map entry resolves once the original run finishes, and the
|
|
132
|
+
// late caller returns the same execution id.
|
|
133
|
+
const inflight = new Map<string, Promise<WorkflowDispatchResult>>();
|
|
134
|
+
|
|
76
135
|
return {
|
|
77
136
|
async execute(
|
|
78
137
|
workflowId: string,
|
|
79
|
-
|
|
138
|
+
payload: Record<string, unknown> = {},
|
|
139
|
+
options: WorkflowDispatchOptions = {}
|
|
80
140
|
): Promise<WorkflowDispatchResult> {
|
|
81
141
|
const id = workflowId.trim();
|
|
82
142
|
if (!id) {
|
|
@@ -86,21 +146,64 @@ export function createWorkflowDispatchService(runtime: IAgentRuntime): WorkflowD
|
|
|
86
146
|
if (!service) {
|
|
87
147
|
return { ok: false, error: 'embedded workflow service not registered' };
|
|
88
148
|
}
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
149
|
+
|
|
150
|
+
const partitioned = partitionPayload(payload);
|
|
151
|
+
const triggerData =
|
|
152
|
+
options.triggerData && Object.keys(options.triggerData).length > 0
|
|
153
|
+
? options.triggerData
|
|
154
|
+
: partitioned.triggerData;
|
|
155
|
+
const idempotencyKey = options.idempotencyKey ?? partitioned.idempotencyKey;
|
|
156
|
+
|
|
157
|
+
if (idempotencyKey) {
|
|
158
|
+
const existing = await service.findExecutionByIdempotencyKey(id, idempotencyKey);
|
|
159
|
+
if (existing) {
|
|
160
|
+
return existing.id
|
|
161
|
+
? { ok: true, executionId: existing.id, dedup: true }
|
|
162
|
+
: { ok: true, dedup: true };
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const inflightKey = `${id}::${idempotencyKey}`;
|
|
166
|
+
const pending = inflight.get(inflightKey);
|
|
167
|
+
if (pending) {
|
|
168
|
+
const result = await pending;
|
|
169
|
+
return result.ok ? { ...result, dedup: true } : result;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const promise = runDispatch(service, id, triggerData, idempotencyKey).finally(() => {
|
|
173
|
+
inflight.delete(inflightKey);
|
|
174
|
+
});
|
|
175
|
+
inflight.set(inflightKey, promise);
|
|
176
|
+
return promise;
|
|
99
177
|
}
|
|
178
|
+
|
|
179
|
+
return runDispatch(service, id, triggerData, undefined);
|
|
100
180
|
},
|
|
101
181
|
};
|
|
102
182
|
}
|
|
103
183
|
|
|
184
|
+
async function runDispatch(
|
|
185
|
+
service: EmbeddedWorkflowService,
|
|
186
|
+
workflowId: string,
|
|
187
|
+
triggerData: Record<string, unknown>,
|
|
188
|
+
idempotencyKey: string | undefined
|
|
189
|
+
): Promise<WorkflowDispatchResult> {
|
|
190
|
+
try {
|
|
191
|
+
const execution = await service.executeWorkflow(workflowId, {
|
|
192
|
+
mode: 'trigger',
|
|
193
|
+
triggerData,
|
|
194
|
+
idempotencyKey,
|
|
195
|
+
});
|
|
196
|
+
return execution.id ? { ok: true, executionId: execution.id } : { ok: true };
|
|
197
|
+
} catch (err) {
|
|
198
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
199
|
+
logger.warn(
|
|
200
|
+
{ src: 'plugin:workflow:dispatch' },
|
|
201
|
+
`Workflow execution failed for ${workflowId}: ${message}`
|
|
202
|
+
);
|
|
203
|
+
return { ok: false, error: message };
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
104
207
|
/**
|
|
105
208
|
* Register the dispatch service in the runtime services map under
|
|
106
209
|
* `WORKFLOW_DISPATCH`. Called from the plugin's `init`.
|
|
@@ -113,7 +216,7 @@ export function createWorkflowDispatchService(runtime: IAgentRuntime): WorkflowD
|
|
|
113
216
|
export function registerWorkflowDispatchService(runtime: IAgentRuntime): void {
|
|
114
217
|
const dispatch = createWorkflowDispatchService(runtime);
|
|
115
218
|
const serviceEntry: WorkflowDispatchServiceEntry = {
|
|
116
|
-
execute: dispatch.execute,
|
|
219
|
+
execute: dispatch.execute.bind(dispatch),
|
|
117
220
|
stop: async () => {},
|
|
118
221
|
capabilityDescription: 'Executes embedded workflows by id via the in-process workflow service.',
|
|
119
222
|
};
|
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
import { type IAgentRuntime, logger, Service } from '@elizaos/core';
|
|
2
2
|
import type {
|
|
3
3
|
NodeDefinition,
|
|
4
|
+
NodeSearchResult,
|
|
4
5
|
RuntimeContext,
|
|
5
6
|
TriggerContext,
|
|
6
7
|
WorkflowCreationResult,
|
|
7
8
|
WorkflowCredentialStoreApi,
|
|
8
9
|
WorkflowDefinition,
|
|
9
10
|
WorkflowDefinitionResponse,
|
|
11
|
+
WorkflowEvaluationSuite,
|
|
10
12
|
WorkflowExecution,
|
|
13
|
+
WorkflowRevision,
|
|
11
14
|
} from '../types/index';
|
|
12
15
|
import {
|
|
13
16
|
isCredentialProvider,
|
|
@@ -22,6 +25,7 @@ import { filterNodesByIntegrationSupport, searchNodes } from '../utils/catalog';
|
|
|
22
25
|
import { CATALOG_CLARIFICATION_SUFFIX, isCatalogClarification } from '../utils/clarification';
|
|
23
26
|
import { getUserTagName } from '../utils/context';
|
|
24
27
|
import { resolveCredentials } from '../utils/credentialResolver';
|
|
28
|
+
import { buildWorkflowEvaluationSuite } from '../utils/evaluation-samples';
|
|
25
29
|
import {
|
|
26
30
|
assessFeasibility,
|
|
27
31
|
collectExistingNodeDefinitions,
|
|
@@ -39,6 +43,7 @@ import {
|
|
|
39
43
|
ensureExpressionPrefix,
|
|
40
44
|
injectMissingCredentialBlocks,
|
|
41
45
|
normalizeTriggerSimpleParam,
|
|
46
|
+
normalizeWorkflowNodeParameterShapes,
|
|
42
47
|
positionNodes,
|
|
43
48
|
validateNodeInputs,
|
|
44
49
|
validateNodeParameters,
|
|
@@ -68,6 +73,9 @@ type WorkflowDefinitionClient = Pick<
|
|
|
68
73
|
| 'deleteWorkflow'
|
|
69
74
|
| 'activateWorkflow'
|
|
70
75
|
| 'deactivateWorkflow'
|
|
76
|
+
| 'listWorkflowRevisions'
|
|
77
|
+
| 'restoreWorkflowRevision'
|
|
78
|
+
| 'executeWorkflow'
|
|
71
79
|
| 'updateWorkflowTags'
|
|
72
80
|
| 'createCredential'
|
|
73
81
|
| 'listExecutions'
|
|
@@ -94,6 +102,105 @@ function isWorkflowCredentialStoreApi(service: unknown): service is WorkflowCred
|
|
|
94
102
|
);
|
|
95
103
|
}
|
|
96
104
|
|
|
105
|
+
const FIELD_TRANSFORM_VERB_PATTERN =
|
|
106
|
+
/\b(adds?|adding|sets?|setting|assigns?|assigning|writes?|writing|maps?|mapping|appends?|appending|enrich(?:es|ing)?)\b/;
|
|
107
|
+
const FIELD_TRANSFORM_TARGET_PATTERN =
|
|
108
|
+
/\b(field|fields|value|values|data|item|items|metadata|json|property|properties)\b/;
|
|
109
|
+
const NETWORK_REQUEST_PATTERN =
|
|
110
|
+
/\b(http|https|url|api|request|fetch|call|post|get|put|patch|delete|webhook)\b/;
|
|
111
|
+
|
|
112
|
+
function buildWorkflowSearchKeywords(prompt: string, keywords: string[]): string[] {
|
|
113
|
+
const normalized = new Set(keywords.map((keyword) => keyword.toLowerCase()));
|
|
114
|
+
const addKeyword = (keyword: string): void => {
|
|
115
|
+
if (!normalized.has(keyword)) {
|
|
116
|
+
keywords.unshift(keyword);
|
|
117
|
+
normalized.add(keyword);
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
const lowerPrompt = prompt.toLowerCase();
|
|
121
|
+
if (FIELD_TRANSFORM_VERB_PATTERN.test(lowerPrompt)) {
|
|
122
|
+
if (FIELD_TRANSFORM_TARGET_PATTERN.test(lowerPrompt)) {
|
|
123
|
+
addKeyword('set');
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
return keywords;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function filterPromptCandidateNodes(prompt: string, nodes: NodeSearchResult[]): NodeSearchResult[] {
|
|
130
|
+
const lowerPrompt = prompt.toLowerCase();
|
|
131
|
+
const looksLikeFieldTransform =
|
|
132
|
+
FIELD_TRANSFORM_VERB_PATTERN.test(lowerPrompt) &&
|
|
133
|
+
FIELD_TRANSFORM_TARGET_PATTERN.test(lowerPrompt);
|
|
134
|
+
const looksLikeNetworkRequest = NETWORK_REQUEST_PATTERN.test(lowerPrompt);
|
|
135
|
+
if (!looksLikeFieldTransform || looksLikeNetworkRequest) {
|
|
136
|
+
return nodes;
|
|
137
|
+
}
|
|
138
|
+
return nodes.filter((result) => result.node.name !== 'workflows-nodes-base.httpRequest');
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function normalizeGeneratedNodeParameterShapes(
|
|
142
|
+
workflow: WorkflowDefinition,
|
|
143
|
+
context: 'generated workflow' | 'modified workflow'
|
|
144
|
+
): void {
|
|
145
|
+
const fixes = normalizeWorkflowNodeParameterShapes(workflow);
|
|
146
|
+
if (fixes > 0) {
|
|
147
|
+
logger.debug(
|
|
148
|
+
{ src: 'plugin:workflow:service:main' },
|
|
149
|
+
`Normalized ${fixes} node parameter shape(s) in ${context}`
|
|
150
|
+
);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Score a workflow against a lowercased query: name beats node type beats
|
|
156
|
+
* description, and an exact/prefix name match beats a substring. Returns 0 for
|
|
157
|
+
* no match. Pure + exported so the ranking is unit-testable without a DB.
|
|
158
|
+
*/
|
|
159
|
+
export function scoreWorkflowMatch(workflow: WorkflowDefinitionResponse, query: string): number {
|
|
160
|
+
const q = query.trim().toLowerCase();
|
|
161
|
+
if (!q) return 0;
|
|
162
|
+
const name = String(workflow.name ?? '').toLowerCase();
|
|
163
|
+
let score = 0;
|
|
164
|
+
if (name === q) score += 100;
|
|
165
|
+
else if (name.startsWith(q)) score += 50;
|
|
166
|
+
else if (name.includes(q)) score += 30;
|
|
167
|
+
|
|
168
|
+
const nodes = (workflow as { nodes?: Array<{ type?: unknown; name?: unknown }> }).nodes;
|
|
169
|
+
if (Array.isArray(nodes)) {
|
|
170
|
+
for (const node of nodes) {
|
|
171
|
+
const type = String(node?.type ?? '').toLowerCase();
|
|
172
|
+
const nodeName = String(node?.name ?? '').toLowerCase();
|
|
173
|
+
if (type.includes(q) || nodeName.includes(q)) {
|
|
174
|
+
score += 10;
|
|
175
|
+
break;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const description = String(
|
|
181
|
+
(workflow as { description?: unknown }).description ?? ''
|
|
182
|
+
).toLowerCase();
|
|
183
|
+
if (description.includes(q)) score += 5;
|
|
184
|
+
|
|
185
|
+
return score;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Rank workflows best-match-first for a free-text query, dropping non-matches.
|
|
190
|
+
* An empty query returns the input order unchanged. Pure + exported (#8913).
|
|
191
|
+
*/
|
|
192
|
+
export function rankWorkflowsByQuery(
|
|
193
|
+
workflows: WorkflowDefinitionResponse[],
|
|
194
|
+
query: string
|
|
195
|
+
): WorkflowDefinitionResponse[] {
|
|
196
|
+
if (!query.trim()) return workflows;
|
|
197
|
+
return workflows
|
|
198
|
+
.map((workflow) => ({ workflow, score: scoreWorkflowMatch(workflow, query) }))
|
|
199
|
+
.filter((entry) => entry.score > 0)
|
|
200
|
+
.sort((a, b) => b.score - a.score)
|
|
201
|
+
.map((entry) => entry.workflow);
|
|
202
|
+
}
|
|
203
|
+
|
|
97
204
|
/**
|
|
98
205
|
* Workflow Service - Orchestrates the RAG pipeline for workflow generation.
|
|
99
206
|
*
|
|
@@ -115,7 +222,7 @@ export class WorkflowService extends Service {
|
|
|
115
222
|
|
|
116
223
|
// Get optional pre-configured credentials from character.settings.workflows
|
|
117
224
|
// Note: runtime.getSetting() only returns primitives — nested objects must be read directly
|
|
118
|
-
const workflowSettings = runtime.character
|
|
225
|
+
const workflowSettings = runtime.character.settings?.workflows as
|
|
119
226
|
| { credentials?: Record<string, string> }
|
|
120
227
|
| undefined;
|
|
121
228
|
const credentials = workflowSettings?.credentials;
|
|
@@ -283,21 +390,26 @@ export class WorkflowService extends Service {
|
|
|
283
390
|
|
|
284
391
|
// Fetch host-supplied bias hints early (before keyword extraction) so the
|
|
285
392
|
// LLM is told which providers the host already knows it can satisfy.
|
|
286
|
-
// We pass empty `relevantNodes` / `relevantCredTypes` here
|
|
287
|
-
//
|
|
288
|
-
//
|
|
393
|
+
// We pass empty `relevantNodes` / `relevantCredTypes` here before
|
|
394
|
+
// node-catalog search runs: `preferredProviders` is derived from the
|
|
395
|
+
// host's connector config alone (independent of node search). The
|
|
289
396
|
// full runtime context (with credentials + facts) is fetched again later
|
|
290
397
|
// once we have the filtered node list.
|
|
291
398
|
const earlyContext = await this.fetchRuntimeContext([], opts?.userId ?? 'local');
|
|
292
399
|
const preferredProviders = earlyContext?.preferredProviders;
|
|
293
400
|
|
|
294
|
-
const keywords =
|
|
401
|
+
const keywords = buildWorkflowSearchKeywords(
|
|
402
|
+
prompt,
|
|
403
|
+
await extractKeywords(this.runtime, prompt, preferredProviders)
|
|
404
|
+
);
|
|
295
405
|
logger.debug(
|
|
296
406
|
{ src: 'plugin:workflow:service:main' },
|
|
297
407
|
`Extracted keywords: ${keywords.join(', ')}${preferredProviders?.length ? ` (with bias: ${preferredProviders.join(', ')})` : ''}`
|
|
298
408
|
);
|
|
299
409
|
|
|
300
|
-
let relevantNodes = this.filterForEmbeddedBackend(
|
|
410
|
+
let relevantNodes = this.filterForEmbeddedBackend(
|
|
411
|
+
filterPromptCandidateNodes(prompt, searchNodes(keywords, 15))
|
|
412
|
+
);
|
|
301
413
|
logger.debug(
|
|
302
414
|
{ src: 'plugin:workflow:service:main' },
|
|
303
415
|
`Found ${relevantNodes.length} relevant nodes`
|
|
@@ -369,7 +481,7 @@ export class WorkflowService extends Service {
|
|
|
369
481
|
let workflow = await generateWorkflow(this.runtime, prompt, finalNodeDefs, runtimeContext);
|
|
370
482
|
logger.debug(
|
|
371
483
|
{ src: 'plugin:workflow:service:main' },
|
|
372
|
-
`Generated workflow with ${workflow.nodes
|
|
484
|
+
`Generated workflow with ${workflow.nodes.length || 0} nodes`
|
|
373
485
|
);
|
|
374
486
|
|
|
375
487
|
// Safety net: even with the MANDATORY INVARIANT prompt rule, the LLM
|
|
@@ -448,6 +560,7 @@ export class WorkflowService extends Service {
|
|
|
448
560
|
}
|
|
449
561
|
|
|
450
562
|
normalizeTriggerSimpleParam(workflow);
|
|
563
|
+
normalizeGeneratedNodeParameterShapes(workflow, 'generated workflow');
|
|
451
564
|
|
|
452
565
|
const optionFixes = correctOptionParameters(workflow);
|
|
453
566
|
if (optionFixes > 0) {
|
|
@@ -464,6 +577,7 @@ export class WorkflowService extends Service {
|
|
|
464
577
|
`Found ${unknownParams.length} node(s) with unknown parameters, auto-correcting...`
|
|
465
578
|
);
|
|
466
579
|
workflow = await correctParameterNames(this.runtime, workflow, unknownParams);
|
|
580
|
+
normalizeGeneratedNodeParameterShapes(workflow, 'generated workflow');
|
|
467
581
|
}
|
|
468
582
|
|
|
469
583
|
const invalidRefs = validateOutputReferences(workflow);
|
|
@@ -473,6 +587,7 @@ export class WorkflowService extends Service {
|
|
|
473
587
|
`Found ${invalidRefs.length} invalid field reference(s), auto-correcting...`
|
|
474
588
|
);
|
|
475
589
|
workflow = await correctFieldReferences(this.runtime, workflow, invalidRefs);
|
|
590
|
+
normalizeGeneratedNodeParameterShapes(workflow, 'generated workflow');
|
|
476
591
|
}
|
|
477
592
|
|
|
478
593
|
const exprPrefixed = ensureExpressionPrefix(workflow);
|
|
@@ -516,8 +631,13 @@ export class WorkflowService extends Service {
|
|
|
516
631
|
const existingDefs = collectExistingNodeDefinitions(existingWorkflow);
|
|
517
632
|
|
|
518
633
|
// Search for new nodes the modification might need
|
|
519
|
-
const keywords =
|
|
520
|
-
|
|
634
|
+
const keywords = buildWorkflowSearchKeywords(
|
|
635
|
+
modificationRequest,
|
|
636
|
+
await extractKeywords(this.runtime, modificationRequest)
|
|
637
|
+
);
|
|
638
|
+
const searchResults = this.filterForEmbeddedBackend(
|
|
639
|
+
filterPromptCandidateNodes(modificationRequest, searchNodes(keywords, 10))
|
|
640
|
+
);
|
|
521
641
|
const newDefs = searchResults.map((r) => r.node);
|
|
522
642
|
|
|
523
643
|
// Deduplicate: merge existing + new, preferring existing (already in workflow)
|
|
@@ -615,6 +735,7 @@ export class WorkflowService extends Service {
|
|
|
615
735
|
}
|
|
616
736
|
|
|
617
737
|
normalizeTriggerSimpleParam(workflow);
|
|
738
|
+
normalizeGeneratedNodeParameterShapes(workflow, 'modified workflow');
|
|
618
739
|
|
|
619
740
|
const optionFixes = correctOptionParameters(workflow);
|
|
620
741
|
if (optionFixes > 0) {
|
|
@@ -631,6 +752,7 @@ export class WorkflowService extends Service {
|
|
|
631
752
|
`Found ${unknownParams.length} node(s) with unknown parameters in modified workflow, auto-correcting...`
|
|
632
753
|
);
|
|
633
754
|
workflow = await correctParameterNames(this.runtime, workflow, unknownParams);
|
|
755
|
+
normalizeGeneratedNodeParameterShapes(workflow, 'modified workflow');
|
|
634
756
|
}
|
|
635
757
|
|
|
636
758
|
const invalidRefs = validateOutputReferences(workflow);
|
|
@@ -640,6 +762,7 @@ export class WorkflowService extends Service {
|
|
|
640
762
|
`Found ${invalidRefs.length} invalid field reference(s) in modified workflow, auto-correcting...`
|
|
641
763
|
);
|
|
642
764
|
workflow = await correctFieldReferences(this.runtime, workflow, invalidRefs);
|
|
765
|
+
normalizeGeneratedNodeParameterShapes(workflow, 'modified workflow');
|
|
643
766
|
}
|
|
644
767
|
|
|
645
768
|
const exprPrefixed = ensureExpressionPrefix(workflow);
|
|
@@ -767,7 +890,7 @@ export class WorkflowService extends Service {
|
|
|
767
890
|
id: deployedWorkflow.id,
|
|
768
891
|
name: deployedWorkflow.name,
|
|
769
892
|
active,
|
|
770
|
-
nodeCount: deployedWorkflow.nodes
|
|
893
|
+
nodeCount: deployedWorkflow.nodes.length || 0,
|
|
771
894
|
missingCredentials: credentialResult.missingConnections,
|
|
772
895
|
};
|
|
773
896
|
}
|
|
@@ -793,6 +916,16 @@ export class WorkflowService extends Service {
|
|
|
793
916
|
return response.data;
|
|
794
917
|
}
|
|
795
918
|
|
|
919
|
+
/**
|
|
920
|
+
* Free-text search over the user's workflows by name, node type, and
|
|
921
|
+
* description, ranked best-match-first (#8913). Lets a user find "the Slack
|
|
922
|
+
* workflow" from a chat message without knowing its id.
|
|
923
|
+
*/
|
|
924
|
+
async searchWorkflows(query: string, userId?: string): Promise<WorkflowDefinitionResponse[]> {
|
|
925
|
+
const workflows = await this.listWorkflows(userId);
|
|
926
|
+
return rankWorkflowsByQuery(workflows, query);
|
|
927
|
+
}
|
|
928
|
+
|
|
796
929
|
async activateWorkflow(workflowId: string): Promise<void> {
|
|
797
930
|
const client = this.getClient();
|
|
798
931
|
await client.activateWorkflow(workflowId);
|
|
@@ -816,12 +949,55 @@ export class WorkflowService extends Service {
|
|
|
816
949
|
return client.getWorkflow(workflowId);
|
|
817
950
|
}
|
|
818
951
|
|
|
952
|
+
async listWorkflowRevisions(workflowId: string, limit?: number): Promise<WorkflowRevision[]> {
|
|
953
|
+
const client = this.getClient();
|
|
954
|
+
const response = await client.listWorkflowRevisions(workflowId, limit);
|
|
955
|
+
return response.data;
|
|
956
|
+
}
|
|
957
|
+
|
|
958
|
+
async restoreWorkflowRevision(
|
|
959
|
+
workflowId: string,
|
|
960
|
+
versionId: string
|
|
961
|
+
): Promise<WorkflowDefinitionResponse> {
|
|
962
|
+
const client = this.getClient();
|
|
963
|
+
return client.restoreWorkflowRevision(workflowId, versionId);
|
|
964
|
+
}
|
|
965
|
+
|
|
966
|
+
async runWorkflow(
|
|
967
|
+
workflowId: string,
|
|
968
|
+
options?: {
|
|
969
|
+
mode?: WorkflowExecution['mode'];
|
|
970
|
+
triggerData?: Record<string, unknown>;
|
|
971
|
+
idempotencyKey?: string;
|
|
972
|
+
throwOnError?: boolean;
|
|
973
|
+
}
|
|
974
|
+
): Promise<WorkflowExecution> {
|
|
975
|
+
const client = this.getClient();
|
|
976
|
+
return client.executeWorkflow(workflowId, {
|
|
977
|
+
mode: options?.mode ?? 'manual',
|
|
978
|
+
triggerData: options?.triggerData,
|
|
979
|
+
idempotencyKey: options?.idempotencyKey,
|
|
980
|
+
throwOnError: options?.throwOnError,
|
|
981
|
+
});
|
|
982
|
+
}
|
|
983
|
+
|
|
819
984
|
async getWorkflowExecutions(workflowId: string, limit?: number): Promise<WorkflowExecution[]> {
|
|
820
985
|
const client = this.getClient();
|
|
821
986
|
const response = await client.listExecutions({ workflowId, limit });
|
|
822
987
|
return response.data;
|
|
823
988
|
}
|
|
824
989
|
|
|
990
|
+
async getWorkflowEvaluationSuite(
|
|
991
|
+
workflowId: string,
|
|
992
|
+
limit?: number
|
|
993
|
+
): Promise<WorkflowEvaluationSuite> {
|
|
994
|
+
const [workflow, executions] = await Promise.all([
|
|
995
|
+
this.getWorkflow(workflowId),
|
|
996
|
+
this.getWorkflowExecutions(workflowId, limit),
|
|
997
|
+
]);
|
|
998
|
+
return buildWorkflowEvaluationSuite(workflow, executions, { limit });
|
|
999
|
+
}
|
|
1000
|
+
|
|
825
1001
|
async listExecutions(params?: {
|
|
826
1002
|
workflowId?: string;
|
|
827
1003
|
status?: 'canceled' | 'error' | 'running' | 'success' | 'waiting';
|
package/src/trigger-routes.ts
CHANGED
|
@@ -84,8 +84,8 @@ export interface NormalizedTriggerDraft {
|
|
|
84
84
|
cronExpression?: string;
|
|
85
85
|
eventKind?: string;
|
|
86
86
|
maxRuns?: number;
|
|
87
|
-
kind
|
|
88
|
-
workflowId
|
|
87
|
+
kind: TriggerKind;
|
|
88
|
+
workflowId: string;
|
|
89
89
|
workflowName?: string;
|
|
90
90
|
}
|
|
91
91
|
|
|
@@ -107,17 +107,6 @@ export interface TriggerExecutionResult {
|
|
|
107
107
|
executionId?: string;
|
|
108
108
|
}
|
|
109
109
|
|
|
110
|
-
export interface TextTriggerWorkflowDraft {
|
|
111
|
-
displayName: string;
|
|
112
|
-
instructions: string;
|
|
113
|
-
wakeMode: TriggerWakeMode;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
export interface DeployedTriggerWorkflow {
|
|
117
|
-
id: string;
|
|
118
|
-
name: string;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
110
|
interface TriggerDraftInput {
|
|
122
111
|
displayName?: string;
|
|
123
112
|
instructions?: string;
|
|
@@ -173,17 +162,6 @@ export interface TriggerRouteContext extends RouteRequestContext {
|
|
|
173
162
|
input: TriggerDraftInput;
|
|
174
163
|
fallback: NormalizeTriggerDraftFallback;
|
|
175
164
|
}) => { draft?: NormalizedTriggerDraft; error?: string };
|
|
176
|
-
/**
|
|
177
|
-
* Phase 2E: every persisted trigger is `kind: "workflow"`. When the
|
|
178
|
-
* caller submits `kind: "text"` (or omits `kind`), the route uses this
|
|
179
|
-
* to materialize a single-node `respondToEvent` workflow first, then
|
|
180
|
-
* stores the trigger as `kind: "workflow"` pointing at that workflow.
|
|
181
|
-
*/
|
|
182
|
-
deployTextTriggerWorkflow: (
|
|
183
|
-
runtime: IAgentRuntime,
|
|
184
|
-
draft: TextTriggerWorkflowDraft,
|
|
185
|
-
ownerId: string
|
|
186
|
-
) => Promise<DeployedTriggerWorkflow | null>;
|
|
187
165
|
DISABLED_TRIGGER_INTERVAL_MS: number;
|
|
188
166
|
TRIGGER_TASK_NAME: string;
|
|
189
167
|
TRIGGER_TASK_TAGS: string[];
|
|
@@ -194,7 +172,7 @@ function trim(value: string): string {
|
|
|
194
172
|
}
|
|
195
173
|
|
|
196
174
|
function parseTriggerKind(value: unknown): TriggerKind | undefined {
|
|
197
|
-
if (value === '
|
|
175
|
+
if (value === 'workflow') return value;
|
|
198
176
|
return undefined;
|
|
199
177
|
}
|
|
200
178
|
|
|
@@ -202,8 +180,8 @@ type ParsedTriggerKind = { ok: true; kind: TriggerKind } | { ok: false; error: s
|
|
|
202
180
|
|
|
203
181
|
function parseTriggerKindStrict(value: unknown): ParsedTriggerKind | undefined {
|
|
204
182
|
if (value === undefined) return undefined;
|
|
205
|
-
if (value === '
|
|
206
|
-
return { ok: false, error: "kind must be '
|
|
183
|
+
if (value === 'workflow') return { ok: true, kind: value };
|
|
184
|
+
return { ok: false, error: "kind must be 'workflow'" };
|
|
207
185
|
}
|
|
208
186
|
|
|
209
187
|
function parseNonEmptyString(value: unknown): string | undefined {
|
|
@@ -276,7 +254,6 @@ export async function handleTriggerRoutes(ctx: TriggerRouteContext): Promise<boo
|
|
|
276
254
|
buildTriggerConfig,
|
|
277
255
|
buildTriggerMetadata,
|
|
278
256
|
normalizeTriggerDraft,
|
|
279
|
-
deployTextTriggerWorkflow,
|
|
280
257
|
DISABLED_TRIGGER_INTERVAL_MS,
|
|
281
258
|
TRIGGER_TASK_NAME,
|
|
282
259
|
TRIGGER_TASK_TAGS,
|
|
@@ -315,7 +292,7 @@ export async function handleTriggerRoutes(ctx: TriggerRouteContext): Promise<boo
|
|
|
315
292
|
const triggers = tasks
|
|
316
293
|
.map(taskToTriggerSummary)
|
|
317
294
|
.filter((summary): summary is TriggerSummary => summary !== null)
|
|
318
|
-
.sort((a, b) => String(a.displayName
|
|
295
|
+
.sort((a, b) => String(a.displayName).localeCompare(String(b.displayName)));
|
|
319
296
|
listResponse(triggers);
|
|
320
297
|
return true;
|
|
321
298
|
}
|
|
@@ -330,49 +307,14 @@ export async function handleTriggerRoutes(ctx: TriggerRouteContext): Promise<boo
|
|
|
330
307
|
error(res, kindParsed.error, 400);
|
|
331
308
|
return true;
|
|
332
309
|
}
|
|
333
|
-
const requestedKind: TriggerKind
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
if (
|
|
310
|
+
const requestedKind: TriggerKind = kindParsed?.ok ? kindParsed.kind : 'workflow';
|
|
311
|
+
const workflowId = parseNonEmptyString(body.workflowId);
|
|
312
|
+
const workflowName = parseNonEmptyString(body.workflowName);
|
|
313
|
+
if (!workflowId) {
|
|
337
314
|
error(res, "workflowId is required when kind is 'workflow'", 400);
|
|
338
315
|
return true;
|
|
339
316
|
}
|
|
340
317
|
|
|
341
|
-
// Phase 2E: when the client submits `kind: "text"` or omits `kind`,
|
|
342
|
-
// materialize a single-node `respondToEvent` workflow up front so the
|
|
343
|
-
// persisted trigger is always `kind: "workflow"`.
|
|
344
|
-
if (requestedKind !== 'workflow') {
|
|
345
|
-
const rawDisplayName =
|
|
346
|
-
typeof body.displayName === 'string' && trim(body.displayName)
|
|
347
|
-
? trim(body.displayName)
|
|
348
|
-
: 'New Trigger';
|
|
349
|
-
const rawInstructions = typeof body.instructions === 'string' ? trim(body.instructions) : '';
|
|
350
|
-
if (!rawInstructions) {
|
|
351
|
-
error(res, 'instructions is required', 400);
|
|
352
|
-
return true;
|
|
353
|
-
}
|
|
354
|
-
const wakeModeForWorkflow: TriggerWakeMode =
|
|
355
|
-
typeof body.wakeMode === 'string' && body.wakeMode === 'next_autonomy_cycle'
|
|
356
|
-
? 'next_autonomy_cycle'
|
|
357
|
-
: 'inject_now';
|
|
358
|
-
const deployed = await deployTextTriggerWorkflow(
|
|
359
|
-
runtime,
|
|
360
|
-
{
|
|
361
|
-
displayName: rawDisplayName,
|
|
362
|
-
instructions: rawInstructions,
|
|
363
|
-
wakeMode: wakeModeForWorkflow,
|
|
364
|
-
},
|
|
365
|
-
creator
|
|
366
|
-
);
|
|
367
|
-
if (!deployed) {
|
|
368
|
-
error(res, 'Workflow plugin is not loaded; cannot create text triggers.', 503);
|
|
369
|
-
return true;
|
|
370
|
-
}
|
|
371
|
-
workflowId = deployed.id;
|
|
372
|
-
workflowName = deployed.name;
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
const kind: TriggerKind = 'workflow';
|
|
376
318
|
const inputDraft: TriggerDraftInput = {
|
|
377
319
|
displayName: typeof body.displayName === 'string' ? body.displayName : undefined,
|
|
378
320
|
instructions: typeof body.instructions === 'string' ? body.instructions : undefined,
|
|
@@ -387,7 +329,7 @@ export async function handleTriggerRoutes(ctx: TriggerRouteContext): Promise<boo
|
|
|
387
329
|
cronExpression: typeof body.cronExpression === 'string' ? body.cronExpression : undefined,
|
|
388
330
|
eventKind: typeof body.eventKind === 'string' ? body.eventKind : undefined,
|
|
389
331
|
maxRuns: typeof body.maxRuns === 'number' ? body.maxRuns : undefined,
|
|
390
|
-
kind,
|
|
332
|
+
kind: requestedKind,
|
|
391
333
|
workflowId,
|
|
392
334
|
workflowName,
|
|
393
335
|
};
|
|
@@ -615,7 +557,7 @@ export async function handleTriggerRoutes(ctx: TriggerRouteContext): Promise<boo
|
|
|
615
557
|
return true;
|
|
616
558
|
}
|
|
617
559
|
const parsedKind: TriggerKind | undefined = kindParsed?.ok ? kindParsed.kind : undefined;
|
|
618
|
-
const nextKind: TriggerKind
|
|
560
|
+
const nextKind: TriggerKind = parsedKind ?? parseTriggerKind(current.kind) ?? 'workflow';
|
|
619
561
|
const nextWorkflowId = parseNonEmptyString(body.workflowId) ?? current.workflowId;
|
|
620
562
|
const nextWorkflowName = parseNonEmptyString(body.workflowName) ?? current.workflowName;
|
|
621
563
|
if (nextKind === 'workflow' && !nextWorkflowId) {
|