@elizaos/plugin-workflow 2.0.0-beta.1 → 2.0.3-beta.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.
Files changed (170) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +28 -26
  3. package/dist/actions/eval-code.d.ts +12 -0
  4. package/dist/actions/eval-code.d.ts.map +1 -0
  5. package/dist/actions/eval-code.js +59 -0
  6. package/dist/actions/eval-code.js.map +1 -0
  7. package/dist/actions/index.d.ts +1 -0
  8. package/dist/actions/index.d.ts.map +1 -1
  9. package/dist/actions/index.js +1 -0
  10. package/dist/actions/index.js.map +1 -1
  11. package/dist/actions/workflow.d.ts +7 -0
  12. package/dist/actions/workflow.d.ts.map +1 -1
  13. package/dist/actions/workflow.js +462 -10
  14. package/dist/actions/workflow.js.map +1 -1
  15. package/dist/db/schema.d.ts +196 -0
  16. package/dist/db/schema.d.ts.map +1 -1
  17. package/dist/db/schema.js +23 -0
  18. package/dist/db/schema.js.map +1 -1
  19. package/dist/index.d.ts.map +1 -1
  20. package/dist/index.js +9 -64
  21. package/dist/index.js.map +1 -1
  22. package/dist/lib/automations-builder.d.ts.map +1 -1
  23. package/dist/lib/automations-builder.js +10 -35
  24. package/dist/lib/automations-builder.js.map +1 -1
  25. package/dist/lib/automations-types.d.ts +2 -2
  26. package/dist/lib/automations-types.d.ts.map +1 -1
  27. package/dist/lib/automations-types.js.map +1 -1
  28. package/dist/lib/index.d.ts +0 -2
  29. package/dist/lib/index.d.ts.map +1 -1
  30. package/dist/lib/index.js +1 -2
  31. package/dist/lib/index.js.map +1 -1
  32. package/dist/lib/workflow-clarification.d.ts +2 -2
  33. package/dist/lib/workflow-clarification.d.ts.map +1 -1
  34. package/dist/lib/workflow-clarification.js +15 -11
  35. package/dist/lib/workflow-clarification.js.map +1 -1
  36. package/dist/plugin-routes.d.ts.map +1 -1
  37. package/dist/plugin-routes.js +6 -0
  38. package/dist/plugin-routes.js.map +1 -1
  39. package/dist/providers/activeWorkflows.js +2 -2
  40. package/dist/providers/activeWorkflows.js.map +1 -1
  41. package/dist/providers/workflowStatus.js +1 -1
  42. package/dist/providers/workflowStatus.js.map +1 -1
  43. package/dist/routes/workflow-routes.d.ts.map +1 -1
  44. package/dist/routes/workflow-routes.js +68 -2
  45. package/dist/routes/workflow-routes.js.map +1 -1
  46. package/dist/routes/workflows.d.ts.map +1 -1
  47. package/dist/routes/workflows.js +5 -1
  48. package/dist/routes/workflows.js.map +1 -1
  49. package/dist/services/embedded-workflow-service.d.ts +74 -17
  50. package/dist/services/embedded-workflow-service.d.ts.map +1 -1
  51. package/dist/services/embedded-workflow-service.js +343 -149
  52. package/dist/services/embedded-workflow-service.js.map +1 -1
  53. package/dist/services/smithers-runtime.d.ts +47 -0
  54. package/dist/services/smithers-runtime.d.ts.map +1 -0
  55. package/dist/services/smithers-runtime.js +444 -0
  56. package/dist/services/smithers-runtime.js.map +1 -0
  57. package/dist/services/workflow-credential-store.js +1 -1
  58. package/dist/services/workflow-credential-store.js.map +1 -1
  59. package/dist/services/workflow-dispatch.d.ts +31 -1
  60. package/dist/services/workflow-dispatch.d.ts.map +1 -1
  61. package/dist/services/workflow-dispatch.js +75 -10
  62. package/dist/services/workflow-dispatch.js.map +1 -1
  63. package/dist/services/workflow-service.d.ts +27 -1
  64. package/dist/services/workflow-service.d.ts.map +1 -1
  65. package/dist/services/workflow-service.js +133 -11
  66. package/dist/services/workflow-service.js.map +1 -1
  67. package/dist/trigger-routes.d.ts +2 -18
  68. package/dist/trigger-routes.d.ts.map +1 -1
  69. package/dist/trigger-routes.js +11 -39
  70. package/dist/trigger-routes.js.map +1 -1
  71. package/dist/types/index.d.ts +82 -2
  72. package/dist/types/index.d.ts.map +1 -1
  73. package/dist/types/index.js.map +1 -1
  74. package/dist/types/workflow-contracts.d.ts +118 -0
  75. package/dist/types/workflow-contracts.d.ts.map +1 -0
  76. package/dist/types/workflow-contracts.js +2 -0
  77. package/dist/types/workflow-contracts.js.map +1 -0
  78. package/dist/utils/catalog.js +2 -2
  79. package/dist/utils/catalog.js.map +1 -1
  80. package/dist/utils/clarification.d.ts +1 -1
  81. package/dist/utils/clarification.d.ts.map +1 -1
  82. package/dist/utils/clarification.js +15 -4
  83. package/dist/utils/clarification.js.map +1 -1
  84. package/dist/utils/context.js +1 -1
  85. package/dist/utils/context.js.map +1 -1
  86. package/dist/utils/evaluation-samples.d.ts +6 -0
  87. package/dist/utils/evaluation-samples.d.ts.map +1 -0
  88. package/dist/utils/evaluation-samples.js +216 -0
  89. package/dist/utils/evaluation-samples.js.map +1 -0
  90. package/dist/utils/execution-diagnostics.d.ts +26 -0
  91. package/dist/utils/execution-diagnostics.d.ts.map +1 -0
  92. package/dist/utils/execution-diagnostics.js +159 -0
  93. package/dist/utils/execution-diagnostics.js.map +1 -0
  94. package/dist/utils/generation.d.ts.map +1 -1
  95. package/dist/utils/generation.js +134 -19
  96. package/dist/utils/generation.js.map +1 -1
  97. package/dist/utils/host-capabilities.d.ts.map +1 -1
  98. package/dist/utils/host-capabilities.js +20 -5
  99. package/dist/utils/host-capabilities.js.map +1 -1
  100. package/dist/utils/inferSyntheticOutputSchema.js +3 -3
  101. package/dist/utils/inferSyntheticOutputSchema.js.map +1 -1
  102. package/dist/utils/outputSchema.js +1 -1
  103. package/dist/utils/outputSchema.js.map +1 -1
  104. package/dist/utils/validateAndRepair.js +10 -10
  105. package/dist/utils/validateAndRepair.js.map +1 -1
  106. package/dist/utils/workflow-prompts/draftIntent.d.ts +1 -1
  107. package/dist/utils/workflow-prompts/draftIntent.d.ts.map +1 -1
  108. package/dist/utils/workflow-prompts/draftIntent.js +1 -1
  109. package/dist/utils/workflow-prompts/keywordExtraction.d.ts +1 -1
  110. package/dist/utils/workflow-prompts/keywordExtraction.d.ts.map +1 -1
  111. package/dist/utils/workflow-prompts/keywordExtraction.js +1 -1
  112. package/dist/utils/workflow-prompts/workflowGeneration.d.ts +1 -1
  113. package/dist/utils/workflow-prompts/workflowGeneration.d.ts.map +1 -1
  114. package/dist/utils/workflow-prompts/workflowGeneration.js +4 -4
  115. package/dist/utils/workflow-prompts/workflowMatching.d.ts +1 -1
  116. package/dist/utils/workflow-prompts/workflowMatching.d.ts.map +1 -1
  117. package/dist/utils/workflow-prompts/workflowMatching.js +1 -1
  118. package/dist/utils/workflow.d.ts +1 -0
  119. package/dist/utils/workflow.d.ts.map +1 -1
  120. package/dist/utils/workflow.js +44 -8
  121. package/dist/utils/workflow.js.map +1 -1
  122. package/package.json +27 -8
  123. package/registry-entry.json +25 -0
  124. package/src/actions/eval-code.ts +81 -0
  125. package/src/actions/index.ts +1 -0
  126. package/src/actions/workflow.ts +518 -10
  127. package/src/db/schema.ts +31 -0
  128. package/src/index.ts +9 -82
  129. package/src/lib/automations-builder.ts +11 -35
  130. package/src/lib/automations-types.ts +1 -2
  131. package/src/lib/index.ts +0 -8
  132. package/src/lib/workflow-clarification.ts +18 -13
  133. package/src/plugin-routes.ts +6 -0
  134. package/src/providers/activeWorkflows.ts +2 -2
  135. package/src/providers/workflowStatus.ts +1 -1
  136. package/src/routes/workflow-routes.ts +100 -2
  137. package/src/routes/workflows.ts +5 -1
  138. package/src/services/embedded-workflow-service.ts +447 -172
  139. package/src/services/smithers-runtime.ts +526 -0
  140. package/src/services/workflow-credential-store.ts +1 -1
  141. package/src/services/workflow-dispatch.ts +116 -13
  142. package/src/services/workflow-service.ts +186 -10
  143. package/src/trigger-routes.ts +12 -70
  144. package/src/types/index.ts +94 -2
  145. package/src/types/workflow-contracts.ts +166 -0
  146. package/src/utils/catalog.ts +2 -2
  147. package/src/utils/clarification.ts +19 -5
  148. package/src/utils/context.ts +1 -1
  149. package/src/utils/evaluation-samples.ts +239 -0
  150. package/src/utils/execution-diagnostics.ts +192 -0
  151. package/src/utils/generation.ts +224 -32
  152. package/src/utils/host-capabilities.ts +21 -5
  153. package/src/utils/inferSyntheticOutputSchema.ts +3 -3
  154. package/src/utils/outputSchema.ts +1 -1
  155. package/src/utils/validateAndRepair.ts +10 -10
  156. package/src/utils/workflow-prompts/draftIntent.ts +1 -1
  157. package/src/utils/workflow-prompts/keywordExtraction.ts +1 -1
  158. package/src/utils/workflow-prompts/workflowGeneration.ts +4 -4
  159. package/src/utils/workflow-prompts/workflowMatching.ts +1 -1
  160. package/src/utils/workflow.ts +56 -8
  161. package/dist/lib/legacy-task-migration.d.ts +0 -20
  162. package/dist/lib/legacy-task-migration.d.ts.map +0 -1
  163. package/dist/lib/legacy-task-migration.js +0 -110
  164. package/dist/lib/legacy-task-migration.js.map +0 -1
  165. package/dist/lib/legacy-text-trigger-migration.d.ts +0 -18
  166. package/dist/lib/legacy-text-trigger-migration.d.ts.map +0 -1
  167. package/dist/lib/legacy-text-trigger-migration.js +0 -131
  168. package/dist/lib/legacy-text-trigger-migration.js.map +0 -1
  169. package/src/lib/legacy-task-migration.ts +0 -143
  170. package/src/lib/legacy-text-trigger-migration.ts +0 -178
@@ -5,7 +5,7 @@ import type {
5
5
  INodeProperties,
6
6
  INodeTypeDescription,
7
7
  IWorkflowSettings,
8
- } from '@elizaos/workflows';
8
+ } from './workflow-contracts';
9
9
 
10
10
  // Core workflow types
11
11
 
@@ -111,6 +111,27 @@ export interface WorkflowDefinitionResponse extends WorkflowDefinition {
111
111
  versionId: string;
112
112
  }
113
113
 
114
+ export type WorkflowRevisionOperation =
115
+ | 'update'
116
+ | 'activate'
117
+ | 'deactivate'
118
+ | 'tags'
119
+ | 'restore'
120
+ | 'delete';
121
+
122
+ export interface WorkflowRevision {
123
+ id: string;
124
+ workflowId: string;
125
+ versionId: string;
126
+ name: string;
127
+ active: boolean;
128
+ workflow: WorkflowDefinition;
129
+ createdAt: string;
130
+ updatedAt: string;
131
+ capturedAt: string;
132
+ operation: WorkflowRevisionOperation;
133
+ }
134
+
114
135
  export interface WorkflowCredential {
115
136
  id: string;
116
137
  name: string;
@@ -121,6 +142,18 @@ export interface WorkflowCredential {
121
142
  updatedAt: string;
122
143
  }
123
144
 
145
+ export interface WorkflowExecutionEngineMetrics {
146
+ provider: 'smithers';
147
+ nodes: number;
148
+ levels: number;
149
+ maxConcurrency: number;
150
+ started: number;
151
+ finished: number;
152
+ failed: number;
153
+ skipped: number;
154
+ retries: number;
155
+ }
156
+
124
157
  export interface WorkflowExecution {
125
158
  id: string;
126
159
  finished: boolean;
@@ -147,6 +180,7 @@ export interface WorkflowExecution {
147
180
  resultData?: {
148
181
  runData?: Record<string, unknown[]>;
149
182
  lastNodeExecuted?: string;
183
+ engine?: WorkflowExecutionEngineMetrics;
150
184
  error?: {
151
185
  message: string;
152
186
  stack?: string;
@@ -160,6 +194,64 @@ export interface WorkflowExecution {
160
194
  };
161
195
  }
162
196
 
197
+ export interface WorkflowEvaluationSampleNode {
198
+ name: string;
199
+ status: 'success' | 'error' | 'unknown';
200
+ itemCount: number;
201
+ executionTimeMs?: number;
202
+ error?: string;
203
+ preview?: string;
204
+ }
205
+
206
+ export interface WorkflowEvaluationSample {
207
+ id: string;
208
+ workflowId: string;
209
+ workflowName: string;
210
+ workflowVersionId?: string;
211
+ executionId: string;
212
+ createdAt: string;
213
+ input: {
214
+ mode: WorkflowExecution['mode'];
215
+ triggerData?: Record<string, unknown>;
216
+ };
217
+ expected: {
218
+ status: WorkflowExecution['status'];
219
+ passed: boolean;
220
+ lastNodeExecuted?: string;
221
+ engine?: WorkflowExecutionEngineMetrics;
222
+ error?: string;
223
+ nodes: WorkflowEvaluationSampleNode[];
224
+ };
225
+ score: {
226
+ pass: boolean;
227
+ value: number;
228
+ reason: string;
229
+ };
230
+ tags: string[];
231
+ }
232
+
233
+ export interface WorkflowEvaluationSuite {
234
+ workflowId: string;
235
+ workflowName: string;
236
+ workflowVersionId?: string;
237
+ generatedAt: string;
238
+ sampleCount: number;
239
+ samples: WorkflowEvaluationSample[];
240
+ jsonl: string;
241
+ optimizer: {
242
+ engine: 'smithers-gepa';
243
+ target: 'workflow-generation';
244
+ suiteName: string;
245
+ caseFile: string;
246
+ recommendedCommand: string;
247
+ recommendedEvalCommand: string;
248
+ recommendedOptimizeCommand: string;
249
+ recommendedObservabilityCommand: string;
250
+ recommendedMetricsCommand: string;
251
+ notes: string[];
252
+ };
253
+ }
254
+
163
255
  export interface WorkflowTag {
164
256
  id: string;
165
257
  name: string;
@@ -426,7 +518,7 @@ export interface ConnectorDisconnectedPayload extends EventPayload {
426
518
  userId: string;
427
519
  /**
428
520
  * Workflow credential type ids tied to the disconnected connector
429
- * (e.g. `['gmailOAuth2', 'gmailOAuth2Api']`). Empty list no-op.
521
+ * (e.g. `['gmailOAuth2', 'gmailOAuth2Api']`). Empty list skips deletion.
430
522
  */
431
523
  credTypes: readonly string[];
432
524
  /** Connector name (e.g. `'gmail'`). Informational. */
@@ -0,0 +1,166 @@
1
+ export type GenericValue = string | object | number | boolean | undefined | null;
2
+
3
+ export type NodeParameterValue = string | number | boolean | undefined | null;
4
+
5
+ export type NodeParameterValueType =
6
+ | NodeParameterValue
7
+ | INodeParameters
8
+ | NodeParameterValue[]
9
+ | INodeParameters[]
10
+ | Record<string, unknown>
11
+ | Array<Record<string, unknown>>;
12
+
13
+ export interface INodeParameters {
14
+ [key: string]: NodeParameterValueType;
15
+ }
16
+
17
+ export type NodePropertyTypes =
18
+ | 'boolean'
19
+ | 'button'
20
+ | 'collection'
21
+ | 'color'
22
+ | 'dateTime'
23
+ | 'fixedCollection'
24
+ | 'hidden'
25
+ | 'json'
26
+ | 'notice'
27
+ | 'multiOptions'
28
+ | 'number'
29
+ | 'options'
30
+ | 'string'
31
+ | 'credentialsSelect'
32
+ | 'resourceLocator'
33
+ | 'curlImport'
34
+ | 'resourceMapper'
35
+ | 'filter'
36
+ | 'assignmentCollection'
37
+ | 'credentials'
38
+ | 'workflowSelector';
39
+
40
+ export interface INodePropertyOptions {
41
+ name: string;
42
+ value?: string | number | boolean;
43
+ action?: string;
44
+ description?: string;
45
+ displayName?: string;
46
+ }
47
+
48
+ export interface INodePropertyCollection {
49
+ displayName?: string;
50
+ name: string;
51
+ values: INodeProperties[];
52
+ }
53
+
54
+ export interface INodeProperties {
55
+ displayName: string;
56
+ name: string;
57
+ type: NodePropertyTypes;
58
+ default: NodeParameterValueType;
59
+ description?: string;
60
+ hint?: string;
61
+ options?: Array<INodePropertyOptions | INodeProperties | INodePropertyCollection>;
62
+ placeholder?: string;
63
+ required?: boolean;
64
+ typeOptions?: Record<string, unknown>;
65
+ displayOptions?: unknown;
66
+ routing?: unknown;
67
+ }
68
+
69
+ export interface INodeCredentialsDetails {
70
+ id: string | null;
71
+ name: string;
72
+ __aiGatewayManaged?: boolean;
73
+ }
74
+
75
+ export interface INodeCredentials {
76
+ [key: string]: INodeCredentialsDetails;
77
+ }
78
+
79
+ export type OnError = 'continueErrorOutput' | 'continueRegularOutput' | 'stopWorkflow';
80
+
81
+ export interface INode {
82
+ id: string;
83
+ name: string;
84
+ typeVersion: number;
85
+ type: string;
86
+ position: [number, number];
87
+ disabled?: boolean;
88
+ notes?: string;
89
+ notesInFlow?: boolean;
90
+ retryOnFail?: boolean;
91
+ maxTries?: number;
92
+ waitBetweenTries?: number;
93
+ alwaysOutputData?: boolean;
94
+ executeOnce?: boolean;
95
+ onError?: OnError;
96
+ continueOnFail?: boolean;
97
+ parameters: INodeParameters;
98
+ credentials?: INodeCredentials;
99
+ webhookId?: string;
100
+ extendsCredential?: string;
101
+ rewireOutputLogTo?: string;
102
+ forceCustomOperation?: {
103
+ resource: string;
104
+ operation: string;
105
+ };
106
+ }
107
+
108
+ export type NodeGroupType =
109
+ | 'input'
110
+ | 'output'
111
+ | 'organization'
112
+ | 'schedule'
113
+ | 'transform'
114
+ | 'trigger';
115
+
116
+ export interface INodeTypeDescription {
117
+ displayName: string;
118
+ name: string;
119
+ group: NodeGroupType[];
120
+ version: number | number[];
121
+ description: string;
122
+ defaults: {
123
+ name: string;
124
+ color?: string;
125
+ };
126
+ inputs: unknown[] | string[] | string;
127
+ outputs: unknown[] | string[] | string;
128
+ credentials?: Array<{
129
+ name: string;
130
+ required?: boolean;
131
+ displayOptions?: unknown;
132
+ }>;
133
+ properties: INodeProperties[];
134
+ icon?: string;
135
+ iconUrl?: string;
136
+ polling?: boolean;
137
+ triggerPanel?: unknown;
138
+ webhooks?: Array<Record<string, unknown>>;
139
+ }
140
+
141
+ export namespace WorkflowSettings {
142
+ export type CallerPolicy = 'workflowsFromSameOwner' | 'workflowsFromAList' | 'any';
143
+ export type SaveDataExecution = 'DEFAULT' | 'all' | 'none';
144
+ export type RedactionPolicy = 'none' | 'mask';
145
+ }
146
+
147
+ export type WorkflowSettingsBinaryMode = 'separate' | 'combined';
148
+
149
+ export interface IWorkflowSettings {
150
+ timezone?: 'DEFAULT' | string;
151
+ errorWorkflow?: 'DEFAULT' | string;
152
+ callerIds?: string;
153
+ callerPolicy?: WorkflowSettings.CallerPolicy;
154
+ saveDataErrorExecution?: WorkflowSettings.SaveDataExecution;
155
+ saveDataSuccessExecution?: WorkflowSettings.SaveDataExecution;
156
+ saveManualExecutions?: 'DEFAULT' | boolean;
157
+ saveExecutionProgress?: 'DEFAULT' | boolean;
158
+ executionTimeout?: number;
159
+ executionOrder?: 'v0' | 'v1';
160
+ binaryMode?: WorkflowSettingsBinaryMode;
161
+ timeSavedPerExecution?: number;
162
+ timeSavedMode?: 'fixed' | 'dynamic';
163
+ availableInMCP?: boolean;
164
+ credentialResolverId?: string;
165
+ redactionPolicy?: WorkflowSettings.RedactionPolicy;
166
+ }
@@ -9,7 +9,7 @@ import type {
9
9
  /**
10
10
  * workflows node catalog with keyword-based search
11
11
  * @note Uses embedded catalog (457 nodes as of April 2025)
12
- * @todo Add dynamic refresh via GET /node-types in v2
12
+ * @note Dynamic refresh via GET /node-types belongs in a catalog-refresh pass.
13
13
  */
14
14
 
15
15
  const NODE_CATALOG = defaultNodesData as NodeDefinition[];
@@ -64,7 +64,7 @@ export function searchNodes(keywords: string[], limit = 15): NodeSearchResult[]
64
64
 
65
65
  const nodeName = node.name.toLowerCase();
66
66
  const nodeDisplayName = node.displayName.toLowerCase();
67
- const nodeDescription = node.description?.toLowerCase() || '';
67
+ const nodeDescription = node.description.toLowerCase() || '';
68
68
  const nameTokens = tokenize(node.name);
69
69
  const displayTokens = tokenize(node.displayName);
70
70
 
@@ -18,6 +18,10 @@ export function isCatalogClarification(item: string | ClarificationRequest): boo
18
18
  : isCatalogClarificationString(item.question);
19
19
  }
20
20
 
21
+ function isRecord(value: unknown): value is Record<string, unknown> {
22
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
23
+ }
24
+
21
25
  /**
22
26
  * Normalize a mixed-shape clarifications array into structured
23
27
  * `ClarificationRequest` objects. Legacy strings become `kind: 'free_text'`
@@ -25,7 +29,7 @@ export function isCatalogClarification(item: string | ClarificationRequest): boo
25
29
  * picker). Structured items pass through unchanged.
26
30
  */
27
31
  export function coerceClarificationRequests(
28
- items: ReadonlyArray<string | ClarificationRequest> | undefined | null
32
+ items: ReadonlyArray<unknown> | undefined | null
29
33
  ): ClarificationRequest[] {
30
34
  if (!items || items.length === 0) {
31
35
  return [];
@@ -38,11 +42,21 @@ export function coerceClarificationRequests(
38
42
  continue;
39
43
  }
40
44
  out.push({ kind: 'free_text', question: trimmed, paramPath: '' });
41
- } else if (item && typeof item === 'object' && typeof item.question === 'string') {
45
+ } else if (isRecord(item) && typeof item.question === 'string') {
42
46
  out.push({
43
- kind: item.kind ?? 'free_text',
44
- platform: item.platform,
45
- scope: item.scope,
47
+ kind:
48
+ item.kind === 'target_channel' ||
49
+ item.kind === 'target_server' ||
50
+ item.kind === 'recipient' ||
51
+ item.kind === 'value' ||
52
+ item.kind === 'free_text'
53
+ ? item.kind
54
+ : 'free_text',
55
+ platform: typeof item.platform === 'string' ? item.platform : undefined,
56
+ scope:
57
+ isRecord(item.scope) && typeof item.scope.guildId === 'string'
58
+ ? { guildId: item.scope.guildId }
59
+ : undefined,
46
60
  question: item.question,
47
61
  paramPath: typeof item.paramPath === 'string' ? item.paramPath : '',
48
62
  });
@@ -3,7 +3,7 @@ import type { IAgentRuntime, Memory, State, UUID } from '@elizaos/core';
3
3
  export function buildConversationContext(message: Memory, state: State | undefined): string {
4
4
  const raw = state?.values?.recentMessages;
5
5
  const recentMessages = typeof raw === 'string' ? raw : '';
6
- const currentText = message.content?.text ?? '';
6
+ const currentText = message.content.text ?? '';
7
7
 
8
8
  if (!recentMessages) {
9
9
  return currentText;
@@ -0,0 +1,239 @@
1
+ import type {
2
+ WorkflowDefinitionResponse,
3
+ WorkflowEvaluationSample,
4
+ WorkflowEvaluationSampleNode,
5
+ WorkflowEvaluationSuite,
6
+ WorkflowExecution,
7
+ } from '../types/index';
8
+
9
+ const DEFAULT_LIMIT = 10;
10
+ const MAX_LIMIT = 50;
11
+ const MAX_PREVIEW_LENGTH = 240;
12
+ const MAX_SAMPLE_VALUE_DEPTH = 4;
13
+ const MAX_SAMPLE_OBJECT_KEYS = 20;
14
+ const MAX_SAMPLE_ARRAY_ITEMS = 10;
15
+
16
+ function isRecord(value: unknown): value is Record<string, unknown> {
17
+ return value !== null && typeof value === 'object' && !Array.isArray(value);
18
+ }
19
+
20
+ function readString(value: unknown): string | undefined {
21
+ return typeof value === 'string' && value.trim() ? value.trim() : undefined;
22
+ }
23
+
24
+ function readNumber(value: unknown): number | undefined {
25
+ return typeof value === 'number' && Number.isFinite(value) ? value : undefined;
26
+ }
27
+
28
+ function clampLimit(limit?: number): number {
29
+ if (typeof limit !== 'number' || !Number.isFinite(limit)) return DEFAULT_LIMIT;
30
+ return Math.min(Math.max(1, Math.floor(limit)), MAX_LIMIT);
31
+ }
32
+
33
+ function truncate(value: string, maxLength = MAX_PREVIEW_LENGTH): string {
34
+ return value.length > maxLength ? `${value.slice(0, maxLength - 3)}...` : value;
35
+ }
36
+
37
+ function compactValue(value: unknown, depth = 0): unknown {
38
+ if (
39
+ value === null ||
40
+ typeof value === 'boolean' ||
41
+ typeof value === 'number' ||
42
+ typeof value === 'undefined'
43
+ ) {
44
+ return value;
45
+ }
46
+ if (typeof value === 'string') {
47
+ return truncate(value, MAX_PREVIEW_LENGTH);
48
+ }
49
+ if (depth >= MAX_SAMPLE_VALUE_DEPTH) {
50
+ return '[truncated]';
51
+ }
52
+ if (Array.isArray(value)) {
53
+ return value.slice(0, MAX_SAMPLE_ARRAY_ITEMS).map((item) => compactValue(item, depth + 1));
54
+ }
55
+ if (!isRecord(value)) {
56
+ return String(value);
57
+ }
58
+ return Object.fromEntries(
59
+ Object.entries(value)
60
+ .slice(0, MAX_SAMPLE_OBJECT_KEYS)
61
+ .map(([key, item]) => [key, compactValue(item, depth + 1)])
62
+ );
63
+ }
64
+
65
+ function compactRecord(value: unknown): Record<string, unknown> | undefined {
66
+ if (!isRecord(value)) return undefined;
67
+ const compacted = compactValue(value);
68
+ return isRecord(compacted) ? compacted : undefined;
69
+ }
70
+
71
+ function countMainItems(data: unknown): number {
72
+ if (!isRecord(data) || !Array.isArray(data.main)) return 0;
73
+ return data.main.reduce((total, output) => {
74
+ if (!Array.isArray(output)) return total;
75
+ return total + output.length;
76
+ }, 0);
77
+ }
78
+
79
+ function previewMainData(data: unknown): string | undefined {
80
+ if (!isRecord(data) || !Array.isArray(data.main)) return undefined;
81
+ for (const output of data.main) {
82
+ if (!Array.isArray(output)) continue;
83
+ const first = output.find(isRecord);
84
+ if (!first) continue;
85
+ const json = isRecord(first.json) ? first.json : first;
86
+ try {
87
+ return truncate(JSON.stringify(compactValue(json)));
88
+ } catch {
89
+ return 'Output could not be previewed';
90
+ }
91
+ }
92
+ return undefined;
93
+ }
94
+
95
+ function readRunError(run: unknown): string | undefined {
96
+ if (!isRecord(run)) return undefined;
97
+ const error = run.error;
98
+ if (isRecord(error)) {
99
+ return readString(error.message) ?? readString(error.description);
100
+ }
101
+ return readString(error);
102
+ }
103
+
104
+ function readExecutionError(execution: WorkflowExecution): string | undefined {
105
+ const error = execution.data?.resultData?.error;
106
+ if (error?.message) return error.message;
107
+ for (const node of collectNodeSamples(execution)) {
108
+ if (node.error) return node.error;
109
+ }
110
+ return undefined;
111
+ }
112
+
113
+ function collectNodeSamples(execution: WorkflowExecution): WorkflowEvaluationSampleNode[] {
114
+ const runData = execution.data?.resultData?.runData;
115
+ if (!runData) return [];
116
+
117
+ return Object.entries(runData).flatMap(([name, runs]) => {
118
+ if (!Array.isArray(runs)) return [];
119
+ return runs.map((run): WorkflowEvaluationSampleNode => {
120
+ const record = isRecord(run) ? run : {};
121
+ const error = readRunError(record);
122
+ const preview = previewMainData(record.data);
123
+ return {
124
+ name,
125
+ status: error ? 'error' : execution.status === 'success' ? 'success' : 'unknown',
126
+ itemCount: countMainItems(record.data),
127
+ ...(readNumber(record.executionTime) !== undefined
128
+ ? { executionTimeMs: readNumber(record.executionTime) }
129
+ : {}),
130
+ ...(error ? { error } : {}),
131
+ ...(preview ? { preview } : {}),
132
+ };
133
+ });
134
+ });
135
+ }
136
+
137
+ function slugify(value: string): string {
138
+ return value
139
+ .toLowerCase()
140
+ .replace(/[^a-z0-9]+/g, '-')
141
+ .replace(/^-+|-+$/g, '')
142
+ .slice(0, 48);
143
+ }
144
+
145
+ function buildSample(
146
+ workflow: WorkflowDefinitionResponse,
147
+ execution: WorkflowExecution
148
+ ): WorkflowEvaluationSample {
149
+ const passed = execution.status === 'success';
150
+ const workflowId = workflow.id;
151
+ const triggerData = compactRecord(execution.customData?.triggerData);
152
+ const error = readExecutionError(execution);
153
+ const nodes = collectNodeSamples(execution);
154
+ const scoreReason = passed
155
+ ? 'Execution completed successfully.'
156
+ : error
157
+ ? `Execution failed: ${error}`
158
+ : `Execution finished with status ${execution.status}.`;
159
+
160
+ return {
161
+ id: `${workflowId}:${execution.id}`,
162
+ workflowId,
163
+ workflowName: workflow.name,
164
+ workflowVersionId: workflow.versionId,
165
+ executionId: execution.id,
166
+ createdAt: execution.stoppedAt ?? execution.startedAt,
167
+ input: {
168
+ mode: execution.mode,
169
+ ...(triggerData ? { triggerData } : {}),
170
+ },
171
+ expected: {
172
+ status: execution.status,
173
+ passed,
174
+ ...(execution.data?.resultData?.lastNodeExecuted
175
+ ? { lastNodeExecuted: execution.data.resultData.lastNodeExecuted }
176
+ : {}),
177
+ ...(execution.data?.resultData?.engine ? { engine: execution.data.resultData.engine } : {}),
178
+ ...(error ? { error } : {}),
179
+ nodes,
180
+ },
181
+ score: {
182
+ pass: passed,
183
+ value: passed ? 1 : 0,
184
+ reason: scoreReason,
185
+ },
186
+ tags: [
187
+ 'smithers',
188
+ 'workflow-eval',
189
+ `workflow:${workflowId}`,
190
+ `status:${execution.status}`,
191
+ `mode:${execution.mode}`,
192
+ ],
193
+ };
194
+ }
195
+
196
+ export function buildWorkflowEvaluationSuite(
197
+ workflow: WorkflowDefinitionResponse,
198
+ executions: WorkflowExecution[],
199
+ options?: { limit?: number; generatedAt?: string }
200
+ ): WorkflowEvaluationSuite {
201
+ const limit = clampLimit(options?.limit);
202
+ const samples = executions.slice(0, limit).map((execution) => buildSample(workflow, execution));
203
+ const safeName = slugify(workflow.name) || slugify(workflow.id) || 'workflow';
204
+ const suiteName = safeName;
205
+ const caseFile = `evals/${suiteName}.jsonl`;
206
+ const recommendedEvalCommand = `bunx smithers-orchestrator eval <workflow.tsx> --cases ${caseFile} --suite ${suiteName}`;
207
+ const recommendedOptimizeCommand = 'bunx smithers-orchestrator optimize';
208
+ const recommendedObservabilityCommand = 'bunx smithers-orchestrator observability --detach';
209
+ const recommendedMetricsCommand =
210
+ 'bunx smithers-orchestrator up <workflow.tsx> --serve --metrics';
211
+ const jsonl = samples.map((sample) => JSON.stringify(sample)).join('\n');
212
+ return {
213
+ workflowId: workflow.id,
214
+ workflowName: workflow.name,
215
+ workflowVersionId: workflow.versionId,
216
+ generatedAt: options?.generatedAt ?? new Date().toISOString(),
217
+ sampleCount: samples.length,
218
+ samples,
219
+ jsonl,
220
+ optimizer: {
221
+ engine: 'smithers-gepa',
222
+ target: 'workflow-generation',
223
+ suiteName,
224
+ caseFile,
225
+ recommendedCommand: recommendedEvalCommand,
226
+ recommendedEvalCommand,
227
+ recommendedOptimizeCommand,
228
+ recommendedObservabilityCommand,
229
+ recommendedMetricsCommand,
230
+ notes: [
231
+ `Copy jsonl to ${caseFile} before running eval.`,
232
+ 'Run GEPA optimization after the eval suite is configured; Smithers writes an optimized prompt artifact only when score improves.',
233
+ 'Use the observability and metrics commands when inspecting long-running or flaky workflows.',
234
+ 'Successful executions are positive examples; failed executions remain useful regression cases.',
235
+ 'Samples are generated from persisted workflow executions and compact large payloads.',
236
+ ],
237
+ },
238
+ };
239
+ }