@elizaos/plugin-workflow 2.0.0-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.
Files changed (294) hide show
  1. package/README.md +71 -0
  2. package/auto-enable.ts +18 -0
  3. package/dist/actions/index.d.ts +2 -0
  4. package/dist/actions/index.d.ts.map +1 -0
  5. package/dist/actions/index.js +2 -0
  6. package/dist/actions/index.js.map +1 -0
  7. package/dist/actions/workflow.d.ts +23 -0
  8. package/dist/actions/workflow.d.ts.map +1 -0
  9. package/dist/actions/workflow.js +425 -0
  10. package/dist/actions/workflow.js.map +1 -0
  11. package/dist/data/defaultNodes.json +9887 -0
  12. package/dist/data/schemaIndex.json +1 -0
  13. package/dist/data/triggerSchemaIndex.json +1 -0
  14. package/dist/db/index.d.ts +2 -0
  15. package/dist/db/index.d.ts.map +1 -0
  16. package/dist/db/index.js +2 -0
  17. package/dist/db/index.js.map +1 -0
  18. package/dist/db/schema.d.ts +588 -0
  19. package/dist/db/schema.d.ts.map +1 -0
  20. package/dist/db/schema.js +59 -0
  21. package/dist/db/schema.js.map +1 -0
  22. package/dist/index.d.ts +34 -0
  23. package/dist/index.d.ts.map +1 -0
  24. package/dist/index.js +126 -0
  25. package/dist/index.js.map +1 -0
  26. package/dist/lib/automations-builder.d.ts +21 -0
  27. package/dist/lib/automations-builder.d.ts.map +1 -0
  28. package/dist/lib/automations-builder.js +557 -0
  29. package/dist/lib/automations-builder.js.map +1 -0
  30. package/dist/lib/automations-types.d.ts +153 -0
  31. package/dist/lib/automations-types.d.ts.map +1 -0
  32. package/dist/lib/automations-types.js +191 -0
  33. package/dist/lib/automations-types.js.map +1 -0
  34. package/dist/lib/index.d.ts +3 -0
  35. package/dist/lib/index.d.ts.map +1 -0
  36. package/dist/lib/index.js +3 -0
  37. package/dist/lib/index.js.map +1 -0
  38. package/dist/lib/legacy-task-migration.d.ts +20 -0
  39. package/dist/lib/legacy-task-migration.d.ts.map +1 -0
  40. package/dist/lib/legacy-task-migration.js +110 -0
  41. package/dist/lib/legacy-task-migration.js.map +1 -0
  42. package/dist/lib/legacy-text-trigger-migration.d.ts +18 -0
  43. package/dist/lib/legacy-text-trigger-migration.d.ts.map +1 -0
  44. package/dist/lib/legacy-text-trigger-migration.js +131 -0
  45. package/dist/lib/legacy-text-trigger-migration.js.map +1 -0
  46. package/dist/lib/workflow-clarification.d.ts +113 -0
  47. package/dist/lib/workflow-clarification.d.ts.map +1 -0
  48. package/dist/lib/workflow-clarification.js +425 -0
  49. package/dist/lib/workflow-clarification.js.map +1 -0
  50. package/dist/plugin-routes.d.ts +9 -0
  51. package/dist/plugin-routes.d.ts.map +1 -0
  52. package/dist/plugin-routes.js +147 -0
  53. package/dist/plugin-routes.js.map +1 -0
  54. package/dist/providers/activeWorkflows.d.ts +11 -0
  55. package/dist/providers/activeWorkflows.d.ts.map +1 -0
  56. package/dist/providers/activeWorkflows.js +72 -0
  57. package/dist/providers/activeWorkflows.js.map +1 -0
  58. package/dist/providers/index.d.ts +4 -0
  59. package/dist/providers/index.d.ts.map +1 -0
  60. package/dist/providers/index.js +4 -0
  61. package/dist/providers/index.js.map +1 -0
  62. package/dist/providers/pendingDraft.d.ts +9 -0
  63. package/dist/providers/pendingDraft.d.ts.map +1 -0
  64. package/dist/providers/pendingDraft.js +48 -0
  65. package/dist/providers/pendingDraft.js.map +1 -0
  66. package/dist/providers/workflowStatus.d.ts +3 -0
  67. package/dist/providers/workflowStatus.d.ts.map +1 -0
  68. package/dist/providers/workflowStatus.js +69 -0
  69. package/dist/providers/workflowStatus.js.map +1 -0
  70. package/dist/register-routes.d.ts +2 -0
  71. package/dist/register-routes.d.ts.map +1 -0
  72. package/dist/register-routes.js +6 -0
  73. package/dist/register-routes.js.map +1 -0
  74. package/dist/routes/_helpers.d.ts +11 -0
  75. package/dist/routes/_helpers.d.ts.map +1 -0
  76. package/dist/routes/_helpers.js +22 -0
  77. package/dist/routes/_helpers.js.map +1 -0
  78. package/dist/routes/automations.d.ts +19 -0
  79. package/dist/routes/automations.d.ts.map +1 -0
  80. package/dist/routes/automations.js +32 -0
  81. package/dist/routes/automations.js.map +1 -0
  82. package/dist/routes/embedded-webhooks.d.ts +3 -0
  83. package/dist/routes/embedded-webhooks.d.ts.map +1 -0
  84. package/dist/routes/embedded-webhooks.js +47 -0
  85. package/dist/routes/embedded-webhooks.js.map +1 -0
  86. package/dist/routes/executions.d.ts +3 -0
  87. package/dist/routes/executions.d.ts.map +1 -0
  88. package/dist/routes/executions.js +58 -0
  89. package/dist/routes/executions.js.map +1 -0
  90. package/dist/routes/index.d.ts +4 -0
  91. package/dist/routes/index.d.ts.map +1 -0
  92. package/dist/routes/index.js +14 -0
  93. package/dist/routes/index.js.map +1 -0
  94. package/dist/routes/nodes.d.ts +3 -0
  95. package/dist/routes/nodes.d.ts.map +1 -0
  96. package/dist/routes/nodes.js +168 -0
  97. package/dist/routes/nodes.js.map +1 -0
  98. package/dist/routes/validation.d.ts +3 -0
  99. package/dist/routes/validation.d.ts.map +1 -0
  100. package/dist/routes/validation.js +41 -0
  101. package/dist/routes/validation.js.map +1 -0
  102. package/dist/routes/workflow-routes.d.ts +27 -0
  103. package/dist/routes/workflow-routes.d.ts.map +1 -0
  104. package/dist/routes/workflow-routes.js +326 -0
  105. package/dist/routes/workflow-routes.js.map +1 -0
  106. package/dist/routes/workflows.d.ts +3 -0
  107. package/dist/routes/workflows.d.ts.map +1 -0
  108. package/dist/routes/workflows.js +252 -0
  109. package/dist/routes/workflows.js.map +1 -0
  110. package/dist/schemas/draftIntent.d.ts +22 -0
  111. package/dist/schemas/draftIntent.d.ts.map +1 -0
  112. package/dist/schemas/draftIntent.js +22 -0
  113. package/dist/schemas/draftIntent.js.map +1 -0
  114. package/dist/schemas/feasibility.d.ts +13 -0
  115. package/dist/schemas/feasibility.d.ts.map +1 -0
  116. package/dist/schemas/feasibility.js +9 -0
  117. package/dist/schemas/feasibility.js.map +1 -0
  118. package/dist/schemas/index.d.ts +5 -0
  119. package/dist/schemas/index.d.ts.map +1 -0
  120. package/dist/schemas/index.js +5 -0
  121. package/dist/schemas/index.js.map +1 -0
  122. package/dist/schemas/keywordExtraction.d.ts +14 -0
  123. package/dist/schemas/keywordExtraction.d.ts.map +1 -0
  124. package/dist/schemas/keywordExtraction.js +12 -0
  125. package/dist/schemas/keywordExtraction.js.map +1 -0
  126. package/dist/schemas/workflowMatching.d.ts +36 -0
  127. package/dist/schemas/workflowMatching.d.ts.map +1 -0
  128. package/dist/schemas/workflowMatching.js +30 -0
  129. package/dist/schemas/workflowMatching.js.map +1 -0
  130. package/dist/services/embedded-workflow-service.d.ts +106 -0
  131. package/dist/services/embedded-workflow-service.d.ts.map +1 -0
  132. package/dist/services/embedded-workflow-service.js +1900 -0
  133. package/dist/services/embedded-workflow-service.js.map +1 -0
  134. package/dist/services/index.d.ts +5 -0
  135. package/dist/services/index.d.ts.map +1 -0
  136. package/dist/services/index.js +5 -0
  137. package/dist/services/index.js.map +1 -0
  138. package/dist/services/workflow-credential-store.d.ts +27 -0
  139. package/dist/services/workflow-credential-store.d.ts.map +1 -0
  140. package/dist/services/workflow-credential-store.js +92 -0
  141. package/dist/services/workflow-credential-store.js.map +1 -0
  142. package/dist/services/workflow-dispatch.d.ts +41 -0
  143. package/dist/services/workflow-dispatch.d.ts.map +1 -0
  144. package/dist/services/workflow-dispatch.js +86 -0
  145. package/dist/services/workflow-dispatch.js.map +1 -0
  146. package/dist/services/workflow-service.d.ts +63 -0
  147. package/dist/services/workflow-service.d.ts.map +1 -0
  148. package/dist/services/workflow-service.js +492 -0
  149. package/dist/services/workflow-service.js.map +1 -0
  150. package/dist/trigger-routes.d.ts +153 -0
  151. package/dist/trigger-routes.d.ts.map +1 -0
  152. package/dist/trigger-routes.js +424 -0
  153. package/dist/trigger-routes.js.map +1 -0
  154. package/dist/types/index.d.ts +457 -0
  155. package/dist/types/index.d.ts.map +1 -0
  156. package/dist/types/index.js +59 -0
  157. package/dist/types/index.js.map +1 -0
  158. package/dist/utils/catalog.d.ts +16 -0
  159. package/dist/utils/catalog.d.ts.map +1 -0
  160. package/dist/utils/catalog.js +211 -0
  161. package/dist/utils/catalog.js.map +1 -0
  162. package/dist/utils/clarification.d.ts +17 -0
  163. package/dist/utils/clarification.d.ts.map +1 -0
  164. package/dist/utils/clarification.js +46 -0
  165. package/dist/utils/clarification.js.map +1 -0
  166. package/dist/utils/context.d.ts +4 -0
  167. package/dist/utils/context.d.ts.map +1 -0
  168. package/dist/utils/context.js +18 -0
  169. package/dist/utils/context.js.map +1 -0
  170. package/dist/utils/credentialResolver.d.ts +22 -0
  171. package/dist/utils/credentialResolver.d.ts.map +1 -0
  172. package/dist/utils/credentialResolver.js +146 -0
  173. package/dist/utils/credentialResolver.js.map +1 -0
  174. package/dist/utils/generation.d.ts +36 -0
  175. package/dist/utils/generation.d.ts.map +1 -0
  176. package/dist/utils/generation.js +701 -0
  177. package/dist/utils/generation.js.map +1 -0
  178. package/dist/utils/host-capabilities.d.ts +27 -0
  179. package/dist/utils/host-capabilities.d.ts.map +1 -0
  180. package/dist/utils/host-capabilities.js +59 -0
  181. package/dist/utils/host-capabilities.js.map +1 -0
  182. package/dist/utils/inferSyntheticOutputSchema.d.ts +20 -0
  183. package/dist/utils/inferSyntheticOutputSchema.d.ts.map +1 -0
  184. package/dist/utils/inferSyntheticOutputSchema.js +151 -0
  185. package/dist/utils/inferSyntheticOutputSchema.js.map +1 -0
  186. package/dist/utils/outputSchema.d.ts +26 -0
  187. package/dist/utils/outputSchema.d.ts.map +1 -0
  188. package/dist/utils/outputSchema.js +297 -0
  189. package/dist/utils/outputSchema.js.map +1 -0
  190. package/dist/utils/validateAndRepair.d.ts +41 -0
  191. package/dist/utils/validateAndRepair.d.ts.map +1 -0
  192. package/dist/utils/validateAndRepair.js +483 -0
  193. package/dist/utils/validateAndRepair.js.map +1 -0
  194. package/dist/utils/workflow-prompts/actionResponse.d.ts +2 -0
  195. package/dist/utils/workflow-prompts/actionResponse.d.ts.map +1 -0
  196. package/dist/utils/workflow-prompts/actionResponse.js +17 -0
  197. package/dist/utils/workflow-prompts/actionResponse.js.map +1 -0
  198. package/dist/utils/workflow-prompts/draftIntent.d.ts +2 -0
  199. package/dist/utils/workflow-prompts/draftIntent.d.ts.map +1 -0
  200. package/dist/utils/workflow-prompts/draftIntent.js +23 -0
  201. package/dist/utils/workflow-prompts/draftIntent.js.map +1 -0
  202. package/dist/utils/workflow-prompts/feasibilityCheck.d.ts +2 -0
  203. package/dist/utils/workflow-prompts/feasibilityCheck.d.ts.map +1 -0
  204. package/dist/utils/workflow-prompts/feasibilityCheck.js +21 -0
  205. package/dist/utils/workflow-prompts/feasibilityCheck.js.map +1 -0
  206. package/dist/utils/workflow-prompts/fieldCorrection.d.ts +3 -0
  207. package/dist/utils/workflow-prompts/fieldCorrection.d.ts.map +1 -0
  208. package/dist/utils/workflow-prompts/fieldCorrection.js +20 -0
  209. package/dist/utils/workflow-prompts/fieldCorrection.js.map +1 -0
  210. package/dist/utils/workflow-prompts/index.d.ts +8 -0
  211. package/dist/utils/workflow-prompts/index.d.ts.map +1 -0
  212. package/dist/utils/workflow-prompts/index.js +8 -0
  213. package/dist/utils/workflow-prompts/index.js.map +1 -0
  214. package/dist/utils/workflow-prompts/keywordExtraction.d.ts +2 -0
  215. package/dist/utils/workflow-prompts/keywordExtraction.d.ts.map +1 -0
  216. package/dist/utils/workflow-prompts/keywordExtraction.js +21 -0
  217. package/dist/utils/workflow-prompts/keywordExtraction.js.map +1 -0
  218. package/dist/utils/workflow-prompts/parameterCorrection.d.ts +3 -0
  219. package/dist/utils/workflow-prompts/parameterCorrection.d.ts.map +1 -0
  220. package/dist/utils/workflow-prompts/parameterCorrection.js +29 -0
  221. package/dist/utils/workflow-prompts/parameterCorrection.js.map +1 -0
  222. package/dist/utils/workflow-prompts/workflowGeneration.d.ts +2 -0
  223. package/dist/utils/workflow-prompts/workflowGeneration.d.ts.map +1 -0
  224. package/dist/utils/workflow-prompts/workflowGeneration.js +529 -0
  225. package/dist/utils/workflow-prompts/workflowGeneration.js.map +1 -0
  226. package/dist/utils/workflow-prompts/workflowMatching.d.ts +2 -0
  227. package/dist/utils/workflow-prompts/workflowMatching.d.ts.map +1 -0
  228. package/dist/utils/workflow-prompts/workflowMatching.js +23 -0
  229. package/dist/utils/workflow-prompts/workflowMatching.js.map +1 -0
  230. package/dist/utils/workflow.d.ts +62 -0
  231. package/dist/utils/workflow.d.ts.map +1 -0
  232. package/dist/utils/workflow.js +712 -0
  233. package/dist/utils/workflow.js.map +1 -0
  234. package/package.json +87 -0
  235. package/src/actions/index.ts +1 -0
  236. package/src/actions/workflow.ts +494 -0
  237. package/src/data/defaultNodes.json +9887 -0
  238. package/src/data/schemaIndex.json +1 -0
  239. package/src/data/triggerSchemaIndex.json +1 -0
  240. package/src/db/index.ts +8 -0
  241. package/src/db/schema.ts +94 -0
  242. package/src/index.ts +179 -0
  243. package/src/lib/automations-builder.ts +679 -0
  244. package/src/lib/automations-types.ts +391 -0
  245. package/src/lib/index.ts +8 -0
  246. package/src/lib/legacy-task-migration.ts +143 -0
  247. package/src/lib/legacy-text-trigger-migration.ts +178 -0
  248. package/src/lib/workflow-clarification.ts +497 -0
  249. package/src/plugin-routes.ts +164 -0
  250. package/src/providers/activeWorkflows.ts +81 -0
  251. package/src/providers/index.ts +3 -0
  252. package/src/providers/pendingDraft.ts +55 -0
  253. package/src/providers/workflowStatus.ts +88 -0
  254. package/src/register-routes.ts +6 -0
  255. package/src/routes/_helpers.ts +27 -0
  256. package/src/routes/automations.ts +46 -0
  257. package/src/routes/embedded-webhooks.ts +64 -0
  258. package/src/routes/executions.ts +75 -0
  259. package/src/routes/index.ts +16 -0
  260. package/src/routes/nodes.ts +211 -0
  261. package/src/routes/validation.ts +51 -0
  262. package/src/routes/workflow-routes.ts +469 -0
  263. package/src/routes/workflows.ts +310 -0
  264. package/src/schemas/draftIntent.ts +21 -0
  265. package/src/schemas/feasibility.ts +8 -0
  266. package/src/schemas/index.ts +4 -0
  267. package/src/schemas/keywordExtraction.ts +11 -0
  268. package/src/schemas/workflowMatching.ts +29 -0
  269. package/src/services/embedded-workflow-service.ts +2224 -0
  270. package/src/services/index.ts +17 -0
  271. package/src/services/workflow-credential-store.ts +132 -0
  272. package/src/services/workflow-dispatch.ts +121 -0
  273. package/src/services/workflow-service.ts +839 -0
  274. package/src/trigger-routes.ts +714 -0
  275. package/src/types/index.ts +562 -0
  276. package/src/utils/catalog.ts +260 -0
  277. package/src/utils/clarification.ts +52 -0
  278. package/src/utils/context.ts +22 -0
  279. package/src/utils/credentialResolver.ts +234 -0
  280. package/src/utils/generation.ts +987 -0
  281. package/src/utils/host-capabilities.ts +81 -0
  282. package/src/utils/inferSyntheticOutputSchema.ts +163 -0
  283. package/src/utils/outputSchema.ts +372 -0
  284. package/src/utils/validateAndRepair.ts +610 -0
  285. package/src/utils/workflow-prompts/actionResponse.ts +16 -0
  286. package/src/utils/workflow-prompts/draftIntent.ts +22 -0
  287. package/src/utils/workflow-prompts/feasibilityCheck.ts +20 -0
  288. package/src/utils/workflow-prompts/fieldCorrection.ts +20 -0
  289. package/src/utils/workflow-prompts/index.ts +10 -0
  290. package/src/utils/workflow-prompts/keywordExtraction.ts +20 -0
  291. package/src/utils/workflow-prompts/parameterCorrection.ts +29 -0
  292. package/src/utils/workflow-prompts/workflowGeneration.ts +528 -0
  293. package/src/utils/workflow-prompts/workflowMatching.ts +22 -0
  294. package/src/utils/workflow.ts +895 -0
@@ -0,0 +1,469 @@
1
+ import type http from 'node:http';
2
+ import type { AgentRuntime } from '@elizaos/core';
3
+ import {
4
+ applyResolutions,
5
+ buildCatalogSnapshot,
6
+ type CatalogLike,
7
+ coerceClarifications,
8
+ pruneResolvedClarifications,
9
+ type WorkflowClarificationResolution,
10
+ } from '../lib/workflow-clarification';
11
+ import { WORKFLOW_SERVICE_TYPE, type WorkflowService } from '../services/workflow-service';
12
+ import type {
13
+ TriggerContext,
14
+ WorkflowCreationResult,
15
+ WorkflowDefinition,
16
+ WorkflowDefinitionResponse,
17
+ } from '../types/index';
18
+
19
+ export type WorkflowMode = 'local' | 'disabled';
20
+ export type WorkflowRuntimeStatus = 'ready' | 'error';
21
+
22
+ export interface WorkflowStatusResponse {
23
+ mode: WorkflowMode;
24
+ host: string | null;
25
+ status: WorkflowRuntimeStatus;
26
+ cloudConnected: false;
27
+ localEnabled: boolean;
28
+ platform: 'desktop';
29
+ cloudHealth: 'unknown';
30
+ errorMessage?: string | null;
31
+ }
32
+
33
+ type WorkflowJsonResponder = (res: http.ServerResponse, body: unknown, status?: number) => void;
34
+
35
+ export interface WorkflowRouteContext {
36
+ req: http.IncomingMessage;
37
+ res: http.ServerResponse;
38
+ method: string;
39
+ pathname: string;
40
+ runtime: AgentRuntime | null;
41
+ agentId?: string;
42
+ json: WorkflowJsonResponder;
43
+ }
44
+
45
+ function sendJson(
46
+ ctx: Pick<WorkflowRouteContext, 'res' | 'json'>,
47
+ status: number,
48
+ body: unknown
49
+ ): void {
50
+ ctx.json(ctx.res, body, status);
51
+ }
52
+
53
+ function normalizePath(pathname: string): string {
54
+ const withoutPrefix = pathname.replace(/^\/api\/workflow/, '');
55
+ return withoutPrefix.length > 0 ? withoutPrefix : '/';
56
+ }
57
+
58
+ function readId(path: string): string | null {
59
+ const match = /^\/workflows\/([^/]+)(?:\/.*)?$/.exec(path);
60
+ return match?.[1] ? decodeURIComponent(match[1]) : null;
61
+ }
62
+
63
+ function resolveAgentId(ctx: WorkflowRouteContext): string {
64
+ if (ctx.agentId?.trim()) {
65
+ return ctx.agentId.trim();
66
+ }
67
+ return (
68
+ ctx.runtime?.agentId ?? ctx.runtime?.character?.id ?? '00000000-0000-0000-0000-000000000000'
69
+ );
70
+ }
71
+
72
+ function getWorkflowService(ctx: WorkflowRouteContext): WorkflowService | null {
73
+ return (ctx.runtime?.getService?.(WORKFLOW_SERVICE_TYPE) as WorkflowService | null) ?? null;
74
+ }
75
+
76
+ function getConnectorTargetCatalog(ctx: WorkflowRouteContext): CatalogLike | null {
77
+ const raw = ctx.runtime?.getService?.('connector_target_catalog');
78
+ const candidate = raw as unknown as CatalogLike | undefined;
79
+ return candidate && typeof candidate.listGroups === 'function' ? candidate : null;
80
+ }
81
+
82
+ function isRecord(value: unknown): value is Record<string, unknown> {
83
+ return value !== null && typeof value === 'object' && !Array.isArray(value);
84
+ }
85
+
86
+ function asClarificationResolution(value: unknown): WorkflowClarificationResolution | null {
87
+ if (!isRecord(value) || typeof value.paramPath !== 'string' || typeof value.value !== 'string') {
88
+ return null;
89
+ }
90
+ return { paramPath: value.paramPath, value: value.value };
91
+ }
92
+
93
+ function isWorkflowDefinition(value: unknown): value is WorkflowDefinition {
94
+ return (
95
+ isRecord(value) &&
96
+ typeof value.name === 'string' &&
97
+ Array.isArray(value.nodes) &&
98
+ value.nodes.every(isRecord) &&
99
+ isRecord(value.connections)
100
+ );
101
+ }
102
+
103
+ async function readJsonBody(
104
+ req: http.IncomingMessage,
105
+ res: http.ServerResponse,
106
+ maxBytes = 1_048_576
107
+ ): Promise<Record<string, unknown> | null> {
108
+ const chunks: Buffer[] = [];
109
+ let totalBytes = 0;
110
+ for await (const chunk of req) {
111
+ const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
112
+ totalBytes += buffer.length;
113
+ if (totalBytes > maxBytes) {
114
+ res.statusCode = 413;
115
+ res.setHeader('content-type', 'application/json; charset=utf-8');
116
+ res.end(JSON.stringify({ error: 'request body too large' }));
117
+ return null;
118
+ }
119
+ chunks.push(buffer);
120
+ }
121
+
122
+ if (chunks.length === 0) {
123
+ return null;
124
+ }
125
+
126
+ try {
127
+ const parsed = JSON.parse(Buffer.concat(chunks).toString('utf-8'));
128
+ return isRecord(parsed) ? parsed : null;
129
+ } catch {
130
+ res.statusCode = 400;
131
+ res.setHeader('content-type', 'application/json; charset=utf-8');
132
+ res.end(JSON.stringify({ error: 'invalid JSON body' }));
133
+ return null;
134
+ }
135
+ }
136
+
137
+ function asWorkflow(value: unknown): WorkflowDefinition | null {
138
+ return isWorkflowDefinition(value) ? value : null;
139
+ }
140
+
141
+ async function readWorkflowPayload(
142
+ ctx: WorkflowRouteContext
143
+ ): Promise<{ workflow: WorkflowDefinition; activate?: boolean } | null> {
144
+ const body = await readJsonBody(ctx.req, ctx.res);
145
+ if (!body) {
146
+ return null;
147
+ }
148
+
149
+ const record = body as Record<string, unknown>;
150
+ const workflow = asWorkflow(record.workflow) ?? asWorkflow(record);
151
+ if (!workflow) {
152
+ sendJson(ctx, 400, { error: 'workflow payload required' });
153
+ return null;
154
+ }
155
+ return {
156
+ workflow,
157
+ activate: typeof record.activate === 'boolean' ? record.activate : undefined,
158
+ };
159
+ }
160
+
161
+ function toWorkflowResponse(result: WorkflowCreationResult): Pick<
162
+ WorkflowDefinitionResponse,
163
+ 'id' | 'name' | 'active'
164
+ > & {
165
+ nodeCount: number;
166
+ missingCredentials: WorkflowCreationResult['missingCredentials'];
167
+ } {
168
+ return {
169
+ id: result.id,
170
+ name: result.name,
171
+ active: result.active,
172
+ nodeCount: result.nodeCount,
173
+ missingCredentials: result.missingCredentials,
174
+ };
175
+ }
176
+
177
+ async function deployWorkflow(
178
+ ctx: WorkflowRouteContext,
179
+ service: WorkflowService,
180
+ workflow: WorkflowDefinition,
181
+ activate?: boolean
182
+ ): Promise<void> {
183
+ const deployed = await service.deployWorkflow(workflow, resolveAgentId(ctx));
184
+ if (deployed.missingCredentials.length > 0 && !deployed.id) {
185
+ sendJson(ctx, 200, {
186
+ ...toWorkflowResponse(deployed),
187
+ warning: 'missing credentials',
188
+ });
189
+ return;
190
+ }
191
+
192
+ if (activate === false && deployed.id && deployed.active) {
193
+ await service.deactivateWorkflow(deployed.id);
194
+ deployed.active = false;
195
+ }
196
+
197
+ const full = deployed.id ? await service.getWorkflow(deployed.id) : toWorkflowResponse(deployed);
198
+ sendJson(ctx, 200, full);
199
+ }
200
+
201
+ async function buildTriggerContextFromConversation(
202
+ runtime: AgentRuntime | null,
203
+ conversationId: string
204
+ ): Promise<TriggerContext | undefined> {
205
+ const room = await runtime?.getRoom?.(conversationId);
206
+ const metadata = isRecord(room?.metadata) ? room.metadata : null;
207
+ const inbound = isRecord(metadata?.inbound) ? metadata.inbound : null;
208
+ if (!inbound) {
209
+ return undefined;
210
+ }
211
+ const platform = typeof inbound.platform === 'string' ? inbound.platform : undefined;
212
+ const channelId = typeof inbound.channelId === 'string' ? inbound.channelId : undefined;
213
+ const guildId = typeof inbound.guildId === 'string' ? inbound.guildId : undefined;
214
+ const threadId = typeof inbound.threadId === 'string' ? inbound.threadId : undefined;
215
+ return {
216
+ source: platform,
217
+ ...(platform === 'discord' || channelId || guildId || threadId
218
+ ? { discord: { channelId, guildId, threadId } }
219
+ : {}),
220
+ };
221
+ }
222
+
223
+ async function handleStatus(ctx: WorkflowRouteContext): Promise<void> {
224
+ const service = getWorkflowService(ctx);
225
+ sendJson(ctx, 200, {
226
+ mode: service ? 'local' : 'disabled',
227
+ host: 'in-process',
228
+ status: service ? 'ready' : 'error',
229
+ cloudConnected: false,
230
+ localEnabled: Boolean(service),
231
+ platform: 'desktop',
232
+ cloudHealth: 'unknown',
233
+ errorMessage: service ? null : 'Workflow service is not registered',
234
+ } satisfies WorkflowStatusResponse);
235
+ }
236
+
237
+ async function handleList(ctx: WorkflowRouteContext, service: WorkflowService): Promise<void> {
238
+ const workflows = await service.listWorkflows(resolveAgentId(ctx));
239
+ sendJson(ctx, 200, { workflows });
240
+ }
241
+
242
+ async function handleGet(
243
+ ctx: WorkflowRouteContext,
244
+ service: WorkflowService,
245
+ id: string
246
+ ): Promise<void> {
247
+ sendJson(ctx, 200, await service.getWorkflow(id));
248
+ }
249
+
250
+ async function handleGenerate(ctx: WorkflowRouteContext, service: WorkflowService): Promise<void> {
251
+ const body = await readJsonBody(ctx.req, ctx.res);
252
+ if (!isRecord(body)) {
253
+ sendJson(ctx, 400, { error: 'request body required' });
254
+ return;
255
+ }
256
+
257
+ const prompt = typeof body.prompt === 'string' ? body.prompt.trim() : '';
258
+ if (!prompt) {
259
+ sendJson(ctx, 400, { error: 'prompt required' });
260
+ return;
261
+ }
262
+
263
+ const triggerContext =
264
+ typeof body.bridgeConversationId === 'string'
265
+ ? await buildTriggerContextFromConversation(ctx.runtime, body.bridgeConversationId)
266
+ : undefined;
267
+
268
+ const draft = await service.generateWorkflowDraft(
269
+ prompt,
270
+ triggerContext ? { triggerContext } : undefined
271
+ );
272
+ if (typeof body.name === 'string' && body.name.trim()) {
273
+ draft.name = body.name.trim();
274
+ }
275
+ if (typeof body.workflowId === 'string' && body.workflowId.trim()) {
276
+ draft.id = body.workflowId.trim();
277
+ }
278
+
279
+ const clarifications = coerceClarifications(draft._meta?.requiresClarification);
280
+ if (clarifications.length > 0) {
281
+ const catalog = getConnectorTargetCatalog(ctx);
282
+ sendJson(ctx, 200, {
283
+ status: 'needs_clarification',
284
+ draft,
285
+ clarifications,
286
+ catalog: catalog ? await buildCatalogSnapshot(catalog, clarifications) : [],
287
+ });
288
+ return;
289
+ }
290
+
291
+ await deployWorkflow(ctx, service, draft);
292
+ }
293
+
294
+ async function handleResolveClarification(
295
+ ctx: WorkflowRouteContext,
296
+ service: WorkflowService
297
+ ): Promise<void> {
298
+ const body = await readJsonBody(ctx.req, ctx.res);
299
+ if (!isRecord(body) || !isRecord(body.draft) || !Array.isArray(body.resolutions)) {
300
+ sendJson(ctx, 400, { error: 'draft and resolutions required' });
301
+ return;
302
+ }
303
+
304
+ const draftRecord = body.draft;
305
+ const draft = asWorkflow(draftRecord);
306
+ if (!draft) {
307
+ sendJson(ctx, 400, { error: 'valid draft workflow required' });
308
+ return;
309
+ }
310
+ const resolutions = body.resolutions.map(asClarificationResolution);
311
+ const validResolutions = resolutions.filter(
312
+ (resolution): resolution is WorkflowClarificationResolution => resolution !== null
313
+ );
314
+ if (validResolutions.length !== body.resolutions.length) {
315
+ sendJson(ctx, 400, { error: 'resolution missing paramPath or string value' });
316
+ return;
317
+ }
318
+ const result = applyResolutions(draftRecord, validResolutions);
319
+ if (result.ok === false) {
320
+ sendJson(ctx, 400, { error: result.error, paramPath: result.paramPath });
321
+ return;
322
+ }
323
+
324
+ const resolvedPaths = new Set(
325
+ body.resolutions
326
+ .map((resolution) => (isRecord(resolution) ? resolution.paramPath : undefined))
327
+ .filter((path): path is string => typeof path === 'string' && path.length > 0)
328
+ );
329
+ const freeFormCount = body.resolutions.filter(
330
+ (resolution) => !isRecord(resolution) || typeof resolution.paramPath !== 'string'
331
+ ).length;
332
+ pruneResolvedClarifications(draftRecord, resolvedPaths, freeFormCount);
333
+
334
+ if (typeof body.name === 'string' && body.name.trim()) {
335
+ draft.name = body.name.trim();
336
+ }
337
+ if (typeof body.workflowId === 'string' && body.workflowId.trim()) {
338
+ draft.id = body.workflowId.trim();
339
+ }
340
+
341
+ const remaining = coerceClarifications(draft._meta?.requiresClarification);
342
+ if (remaining.length > 0) {
343
+ const catalog = getConnectorTargetCatalog(ctx);
344
+ sendJson(ctx, 200, {
345
+ status: 'needs_clarification',
346
+ draft,
347
+ clarifications: remaining,
348
+ catalog: catalog ? await buildCatalogSnapshot(catalog, remaining) : [],
349
+ });
350
+ return;
351
+ }
352
+
353
+ await deployWorkflow(ctx, service, draft);
354
+ }
355
+
356
+ async function handleWrite(
357
+ ctx: WorkflowRouteContext,
358
+ service: WorkflowService,
359
+ id?: string
360
+ ): Promise<void> {
361
+ const payload = await readWorkflowPayload(ctx);
362
+ if (!payload) {
363
+ return;
364
+ }
365
+ const workflow = id ? { ...payload.workflow, id } : payload.workflow;
366
+ await deployWorkflow(ctx, service, workflow, payload.activate);
367
+ }
368
+
369
+ async function handleToggle(
370
+ ctx: WorkflowRouteContext,
371
+ service: WorkflowService,
372
+ id: string,
373
+ active: boolean
374
+ ): Promise<void> {
375
+ if (active) {
376
+ await service.activateWorkflow(id);
377
+ } else {
378
+ await service.deactivateWorkflow(id);
379
+ }
380
+ sendJson(ctx, 200, await service.getWorkflow(id));
381
+ }
382
+
383
+ async function handleListExecutions(
384
+ ctx: WorkflowRouteContext,
385
+ service: WorkflowService,
386
+ id: string
387
+ ): Promise<void> {
388
+ const url = new URL(`http://x${ctx.req.url ?? ''}`);
389
+ const rawLimit = url.searchParams.get('limit');
390
+ const limit = Math.min(Math.max(1, Number(rawLimit) || 10), 50);
391
+ const response = await service.listExecutions({ workflowId: id, limit });
392
+ sendJson(ctx, 200, { executions: response.data });
393
+ }
394
+
395
+ export async function handleWorkflowRoutes(ctx: WorkflowRouteContext): Promise<void> {
396
+ const path = normalizePath(ctx.pathname);
397
+ const method = ctx.method.toUpperCase();
398
+
399
+ try {
400
+ if (method === 'GET' && path === '/status') {
401
+ await handleStatus(ctx);
402
+ return;
403
+ }
404
+
405
+ if (method === 'POST' && path === '/runtime/start') {
406
+ sendJson(ctx, 200, { ok: true });
407
+ return;
408
+ }
409
+
410
+ const service = getWorkflowService(ctx);
411
+ if (!service) {
412
+ sendJson(ctx, 503, { error: 'workflow service unavailable' });
413
+ return;
414
+ }
415
+
416
+ if (method === 'GET' && path === '/workflows') {
417
+ await handleList(ctx, service);
418
+ return;
419
+ }
420
+
421
+ if (method === 'POST' && path === '/workflows') {
422
+ await handleWrite(ctx, service);
423
+ return;
424
+ }
425
+
426
+ if (method === 'POST' && path === '/workflows/generate') {
427
+ await handleGenerate(ctx, service);
428
+ return;
429
+ }
430
+
431
+ if (method === 'POST' && path === '/workflows/resolve-clarification') {
432
+ await handleResolveClarification(ctx, service);
433
+ return;
434
+ }
435
+
436
+ const id = readId(path);
437
+ if (id && method === 'GET' && path === `/workflows/${encodeURIComponent(id)}`) {
438
+ await handleGet(ctx, service, id);
439
+ return;
440
+ }
441
+ if (id && method === 'PUT' && path === `/workflows/${encodeURIComponent(id)}`) {
442
+ await handleWrite(ctx, service, id);
443
+ return;
444
+ }
445
+ if (id && method === 'DELETE' && path === `/workflows/${encodeURIComponent(id)}`) {
446
+ await service.deleteWorkflow(id);
447
+ sendJson(ctx, 200, { ok: true });
448
+ return;
449
+ }
450
+ if (id && method === 'POST' && path === `/workflows/${encodeURIComponent(id)}/activate`) {
451
+ await handleToggle(ctx, service, id, true);
452
+ return;
453
+ }
454
+ if (id && method === 'POST' && path === `/workflows/${encodeURIComponent(id)}/deactivate`) {
455
+ await handleToggle(ctx, service, id, false);
456
+ return;
457
+ }
458
+ if (id && method === 'GET' && path === `/workflows/${encodeURIComponent(id)}/executions`) {
459
+ await handleListExecutions(ctx, service, id);
460
+ return;
461
+ }
462
+
463
+ sendJson(ctx, 404, { error: 'workflow route not found' });
464
+ } catch (error) {
465
+ sendJson(ctx, 500, {
466
+ error: error instanceof Error ? error.message : String(error),
467
+ });
468
+ }
469
+ }