@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,714 @@
1
+ import crypto from 'node:crypto';
2
+ import {
3
+ type TriggerRunRecord as CoreTriggerRunRecord,
4
+ type IAgentRuntime,
5
+ type RouteHelpers,
6
+ type RouteRequestContext,
7
+ stringToUuid,
8
+ type Task,
9
+ type TriggerConfig,
10
+ type TriggerKind,
11
+ type TriggerLastStatus,
12
+ type TriggerType,
13
+ type TriggerWakeMode,
14
+ type UUID,
15
+ } from '@elizaos/core';
16
+
17
+ export type TriggerRouteHelpers = RouteHelpers;
18
+
19
+ export interface TriggerTaskMetadata {
20
+ updatedAt?: number;
21
+ updateInterval?: number;
22
+ blocking?: boolean;
23
+ trigger?: TriggerConfig;
24
+ triggerRuns?: CoreTriggerRunRecord[];
25
+ [key: string]:
26
+ | string
27
+ | number
28
+ | boolean
29
+ | string[]
30
+ | number[]
31
+ | Record<string, string | number | boolean>
32
+ | undefined
33
+ | TriggerConfig
34
+ | CoreTriggerRunRecord[];
35
+ }
36
+
37
+ export interface TriggerSummary {
38
+ id: UUID;
39
+ taskId: UUID;
40
+ displayName: string;
41
+ instructions: string;
42
+ triggerType: TriggerType;
43
+ enabled: boolean;
44
+ wakeMode: TriggerWakeMode;
45
+ createdBy: string;
46
+ timezone?: string;
47
+ intervalMs?: number;
48
+ scheduledAtIso?: string;
49
+ cronExpression?: string;
50
+ eventKind?: string;
51
+ maxRuns?: number;
52
+ runCount: number;
53
+ nextRunAtMs?: number;
54
+ lastRunAtIso?: string;
55
+ lastStatus?: TriggerLastStatus;
56
+ lastError?: string;
57
+ updatedAt?: number;
58
+ updateInterval?: number;
59
+ kind?: TriggerKind;
60
+ workflowId?: string;
61
+ workflowName?: string;
62
+ }
63
+
64
+ export interface TriggerHealthSnapshot {
65
+ triggersEnabled: boolean;
66
+ activeTriggers: number;
67
+ disabledTriggers: number;
68
+ totalExecutions: number;
69
+ totalFailures: number;
70
+ totalSkipped: number;
71
+ lastExecutionAt?: number;
72
+ }
73
+
74
+ export interface NormalizedTriggerDraft {
75
+ displayName: string;
76
+ instructions: string;
77
+ triggerType: TriggerType;
78
+ wakeMode: TriggerWakeMode;
79
+ enabled: boolean;
80
+ createdBy: string;
81
+ timezone?: string;
82
+ intervalMs?: number;
83
+ scheduledAtIso?: string;
84
+ cronExpression?: string;
85
+ eventKind?: string;
86
+ maxRuns?: number;
87
+ kind?: TriggerKind;
88
+ workflowId?: string;
89
+ workflowName?: string;
90
+ }
91
+
92
+ export interface TriggerExecutionOptions {
93
+ source: 'scheduler' | 'manual' | 'event';
94
+ force?: boolean;
95
+ event?: {
96
+ kind: string;
97
+ payload?: Record<string, unknown>;
98
+ };
99
+ }
100
+
101
+ export interface TriggerExecutionResult {
102
+ status: 'success' | 'error' | 'skipped';
103
+ error?: string;
104
+ taskDeleted: boolean;
105
+ runRecord?: CoreTriggerRunRecord;
106
+ trigger?: TriggerSummary | null;
107
+ executionId?: string;
108
+ }
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
+ interface TriggerDraftInput {
122
+ displayName?: string;
123
+ instructions?: string;
124
+ triggerType?: TriggerType;
125
+ wakeMode?: TriggerWakeMode;
126
+ enabled?: boolean;
127
+ createdBy?: string;
128
+ timezone?: string;
129
+ intervalMs?: number;
130
+ scheduledAtIso?: string;
131
+ cronExpression?: string;
132
+ eventKind?: string;
133
+ maxRuns?: number;
134
+ kind?: TriggerKind;
135
+ workflowId?: string;
136
+ workflowName?: string;
137
+ }
138
+
139
+ interface NormalizeTriggerDraftFallback {
140
+ displayName: string;
141
+ instructions: string;
142
+ triggerType: TriggerType;
143
+ wakeMode: TriggerWakeMode;
144
+ enabled: boolean;
145
+ createdBy: string;
146
+ }
147
+
148
+ export interface TriggerRouteContext extends RouteRequestContext {
149
+ runtime: IAgentRuntime | null;
150
+ executeTriggerTask: (
151
+ runtime: IAgentRuntime,
152
+ task: Task,
153
+ options: TriggerExecutionOptions
154
+ ) => Promise<TriggerExecutionResult>;
155
+ getTriggerHealthSnapshot: (runtime: IAgentRuntime) => Promise<TriggerHealthSnapshot>;
156
+ getTriggerLimit: (runtime: IAgentRuntime) => number;
157
+ listTriggerTasks: (runtime: IAgentRuntime) => Promise<Task[]>;
158
+ readTriggerConfig: (task: Task) => TriggerConfig | null;
159
+ readTriggerRuns: (task: Task) => CoreTriggerRunRecord[];
160
+ taskToTriggerSummary: (task: Task) => TriggerSummary | null;
161
+ triggersFeatureEnabled: (runtime: IAgentRuntime) => boolean;
162
+ buildTriggerConfig: (params: {
163
+ draft: NormalizedTriggerDraft;
164
+ triggerId: UUID;
165
+ previous?: TriggerConfig;
166
+ }) => TriggerConfig;
167
+ buildTriggerMetadata: (params: {
168
+ existingMetadata?: TriggerTaskMetadata;
169
+ trigger: TriggerConfig;
170
+ nowMs: number;
171
+ }) => TriggerTaskMetadata | null;
172
+ normalizeTriggerDraft: (params: {
173
+ input: TriggerDraftInput;
174
+ fallback: NormalizeTriggerDraftFallback;
175
+ }) => { 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
+ DISABLED_TRIGGER_INTERVAL_MS: number;
188
+ TRIGGER_TASK_NAME: string;
189
+ TRIGGER_TASK_TAGS: string[];
190
+ }
191
+
192
+ function trim(value: string): string {
193
+ return value.trim().replace(/\s+/g, ' ');
194
+ }
195
+
196
+ function parseTriggerKind(value: unknown): TriggerKind | undefined {
197
+ if (value === 'text' || value === 'workflow') return value;
198
+ return undefined;
199
+ }
200
+
201
+ type ParsedTriggerKind = { ok: true; kind: TriggerKind } | { ok: false; error: string };
202
+
203
+ function parseTriggerKindStrict(value: unknown): ParsedTriggerKind | undefined {
204
+ if (value === undefined) return undefined;
205
+ if (value === 'text' || value === 'workflow') return { ok: true, kind: value };
206
+ return { ok: false, error: "kind must be 'text' or 'workflow'" };
207
+ }
208
+
209
+ function parseNonEmptyString(value: unknown): string | undefined {
210
+ if (typeof value !== 'string') return undefined;
211
+ const trimmed = value.trim();
212
+ return trimmed.length > 0 ? trimmed : undefined;
213
+ }
214
+
215
+ function parseEventPayload(value: unknown): Record<string, unknown> {
216
+ return value && typeof value === 'object' && !Array.isArray(value)
217
+ ? (value as Record<string, unknown>)
218
+ : {};
219
+ }
220
+
221
+ function normalizeTriggerPath(pathname: string): {
222
+ normalizedPathname: string;
223
+ usingHeartbeatsAlias: boolean;
224
+ } {
225
+ if (pathname === '/api/heartbeats') {
226
+ return {
227
+ normalizedPathname: '/api/triggers',
228
+ usingHeartbeatsAlias: true,
229
+ };
230
+ }
231
+ if (pathname.startsWith('/api/heartbeats/')) {
232
+ return {
233
+ normalizedPathname: pathname.replace('/api/heartbeats', '/api/triggers'),
234
+ usingHeartbeatsAlias: true,
235
+ };
236
+ }
237
+ return {
238
+ normalizedPathname: pathname,
239
+ usingHeartbeatsAlias: false,
240
+ };
241
+ }
242
+
243
+ async function findTask(
244
+ runtime: IAgentRuntime,
245
+ id: string,
246
+ listTriggerTasks: (runtime: IAgentRuntime) => Promise<Task[]>,
247
+ readTriggerConfig: (task: Task) => TriggerConfig | null
248
+ ): Promise<Task | null> {
249
+ const tasks = await listTriggerTasks(runtime);
250
+ return (
251
+ tasks.find((task) => {
252
+ const trigger = readTriggerConfig(task);
253
+ return trigger?.triggerId === id || task.id === id;
254
+ }) ?? null
255
+ );
256
+ }
257
+
258
+ export async function handleTriggerRoutes(ctx: TriggerRouteContext): Promise<boolean> {
259
+ const {
260
+ method,
261
+ pathname,
262
+ req,
263
+ res,
264
+ runtime,
265
+ readJsonBody,
266
+ json,
267
+ error,
268
+ executeTriggerTask,
269
+ getTriggerHealthSnapshot,
270
+ getTriggerLimit,
271
+ listTriggerTasks,
272
+ readTriggerConfig,
273
+ readTriggerRuns,
274
+ taskToTriggerSummary,
275
+ triggersFeatureEnabled,
276
+ buildTriggerConfig,
277
+ buildTriggerMetadata,
278
+ normalizeTriggerDraft,
279
+ deployTextTriggerWorkflow,
280
+ DISABLED_TRIGGER_INTERVAL_MS,
281
+ TRIGGER_TASK_NAME,
282
+ TRIGGER_TASK_TAGS,
283
+ } = ctx;
284
+
285
+ const { normalizedPathname, usingHeartbeatsAlias } = normalizeTriggerPath(pathname);
286
+ const listResponse = (triggers: TriggerSummary[], status = 200): void => {
287
+ json(res, usingHeartbeatsAlias ? { triggers, heartbeats: triggers } : { triggers }, status);
288
+ };
289
+ const itemResponse = (summary: TriggerSummary, status = 200): void => {
290
+ json(
291
+ res,
292
+ usingHeartbeatsAlias ? { trigger: summary, heartbeat: summary } : { trigger: summary },
293
+ status
294
+ );
295
+ };
296
+
297
+ if (!normalizedPathname.startsWith('/api/triggers') && !pathname.startsWith('/api/heartbeats'))
298
+ return false;
299
+ if (!runtime) {
300
+ error(res, 'Agent is not running', 503);
301
+ return true;
302
+ }
303
+ if (!triggersFeatureEnabled(runtime) && normalizedPathname !== '/api/triggers/health') {
304
+ error(res, 'Triggers are disabled by configuration', 503);
305
+ return true;
306
+ }
307
+
308
+ if (method === 'GET' && normalizedPathname === '/api/triggers/health') {
309
+ json(res, await getTriggerHealthSnapshot(runtime));
310
+ return true;
311
+ }
312
+
313
+ if (method === 'GET' && normalizedPathname === '/api/triggers') {
314
+ const tasks = await listTriggerTasks(runtime);
315
+ const triggers = tasks
316
+ .map(taskToTriggerSummary)
317
+ .filter((summary): summary is TriggerSummary => summary !== null)
318
+ .sort((a, b) => String(a.displayName ?? '').localeCompare(String(b.displayName ?? '')));
319
+ listResponse(triggers);
320
+ return true;
321
+ }
322
+
323
+ if (method === 'POST' && normalizedPathname === '/api/triggers') {
324
+ const body = await readJsonBody<Record<string, unknown>>(req, res);
325
+ if (!body) return true;
326
+
327
+ const creator = typeof body.createdBy === 'string' ? trim(body.createdBy) || 'api' : 'api';
328
+ const kindParsed = parseTriggerKindStrict(body.kind);
329
+ if (kindParsed !== undefined && kindParsed.ok === false) {
330
+ error(res, kindParsed.error, 400);
331
+ return true;
332
+ }
333
+ const requestedKind: TriggerKind | undefined = kindParsed?.ok ? kindParsed.kind : undefined;
334
+ let workflowId = parseNonEmptyString(body.workflowId);
335
+ let workflowName = parseNonEmptyString(body.workflowName);
336
+ if (requestedKind === 'workflow' && !workflowId) {
337
+ error(res, "workflowId is required when kind is 'workflow'", 400);
338
+ return true;
339
+ }
340
+
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
+ const inputDraft: TriggerDraftInput = {
377
+ displayName: typeof body.displayName === 'string' ? body.displayName : undefined,
378
+ instructions: typeof body.instructions === 'string' ? body.instructions : undefined,
379
+ triggerType:
380
+ typeof body.triggerType === 'string' ? (body.triggerType as TriggerType) : undefined,
381
+ wakeMode: typeof body.wakeMode === 'string' ? (body.wakeMode as TriggerWakeMode) : undefined,
382
+ enabled: !!(body.enabled ?? true),
383
+ createdBy: creator,
384
+ timezone: typeof body.timezone === 'string' ? body.timezone : undefined,
385
+ intervalMs: typeof body.intervalMs === 'number' ? body.intervalMs : undefined,
386
+ scheduledAtIso: typeof body.scheduledAtIso === 'string' ? body.scheduledAtIso : undefined,
387
+ cronExpression: typeof body.cronExpression === 'string' ? body.cronExpression : undefined,
388
+ eventKind: typeof body.eventKind === 'string' ? body.eventKind : undefined,
389
+ maxRuns: typeof body.maxRuns === 'number' ? body.maxRuns : undefined,
390
+ kind,
391
+ workflowId,
392
+ workflowName,
393
+ };
394
+ const normalized = normalizeTriggerDraft({
395
+ input: inputDraft,
396
+ fallback: {
397
+ displayName:
398
+ typeof body.displayName === 'string' && trim(body.displayName)
399
+ ? trim(body.displayName)
400
+ : 'New Trigger',
401
+ instructions: typeof body.instructions === 'string' ? trim(body.instructions) : '',
402
+ triggerType:
403
+ typeof body.triggerType === 'string' ? (body.triggerType as TriggerType) : 'interval',
404
+ wakeMode:
405
+ typeof body.wakeMode === 'string' ? (body.wakeMode as TriggerWakeMode) : 'inject_now',
406
+ enabled: body.enabled === undefined ? true : body.enabled === true,
407
+ createdBy: creator,
408
+ },
409
+ });
410
+ if (!normalized.draft) {
411
+ error(res, normalized.error ?? 'Invalid trigger request', 400);
412
+ return true;
413
+ }
414
+
415
+ const existingTasks = await listTriggerTasks(runtime);
416
+ const activeCount = existingTasks.filter((task) => {
417
+ const trigger = readTriggerConfig(task);
418
+ return trigger?.enabled && trigger.createdBy === creator;
419
+ }).length;
420
+ const limit = getTriggerLimit(runtime);
421
+ if (activeCount >= limit) {
422
+ error(res, `Active trigger limit reached (${limit})`, 429);
423
+ return true;
424
+ }
425
+
426
+ const triggerId = stringToUuid(crypto.randomUUID());
427
+ const trigger = buildTriggerConfig({ draft: normalized.draft, triggerId });
428
+
429
+ const duplicate = existingTasks.find((task) => {
430
+ const existingTrigger = readTriggerConfig(task);
431
+ return (
432
+ existingTrigger?.enabled &&
433
+ existingTrigger.dedupeKey &&
434
+ existingTrigger.dedupeKey === trigger.dedupeKey
435
+ );
436
+ });
437
+ if (duplicate?.id) {
438
+ error(res, 'Equivalent trigger already exists', 409);
439
+ return true;
440
+ }
441
+
442
+ const nowMs = Date.now();
443
+ const metadata = trigger.enabled
444
+ ? buildTriggerMetadata({ trigger, nowMs })
445
+ : ({
446
+ updatedAt: nowMs,
447
+ updateInterval: DISABLED_TRIGGER_INTERVAL_MS,
448
+ trigger: {
449
+ ...trigger,
450
+ nextRunAtMs: nowMs + DISABLED_TRIGGER_INTERVAL_MS,
451
+ },
452
+ } as TriggerTaskMetadata);
453
+ if (!metadata) {
454
+ error(res, 'Unable to compute trigger schedule', 400);
455
+ return true;
456
+ }
457
+
458
+ const roomId = (
459
+ runtime.getService('AUTONOMY') as { getAutonomousRoomId?(): UUID } | null
460
+ )?.getAutonomousRoomId?.();
461
+ const taskId = await runtime.createTask({
462
+ name: TRIGGER_TASK_NAME,
463
+ description: trigger.displayName,
464
+ roomId,
465
+ tags: [...TRIGGER_TASK_TAGS],
466
+ metadata: metadata as Task['metadata'],
467
+ });
468
+ const created = await runtime.getTask(taskId);
469
+ const summary = created ? taskToTriggerSummary(created) : null;
470
+ if (!summary) {
471
+ error(res, 'Trigger created but summary could not be generated', 500);
472
+ return true;
473
+ }
474
+ itemResponse(summary, 201);
475
+ return true;
476
+ }
477
+
478
+ const runsMatch = /^\/api\/triggers\/([^/]+)\/runs$/.exec(normalizedPathname);
479
+ if (method === 'GET' && runsMatch) {
480
+ const task = await findTask(
481
+ runtime,
482
+ decodeURIComponent(runsMatch[1]),
483
+ listTriggerTasks,
484
+ readTriggerConfig
485
+ );
486
+ if (!task) {
487
+ error(res, 'Trigger not found', 404);
488
+ return true;
489
+ }
490
+ json(res, { runs: readTriggerRuns(task) });
491
+ return true;
492
+ }
493
+
494
+ const execMatch = /^\/api\/triggers\/([^/]+)\/execute$/.exec(normalizedPathname);
495
+ if (method === 'POST' && execMatch) {
496
+ const task = await findTask(
497
+ runtime,
498
+ decodeURIComponent(execMatch[1]),
499
+ listTriggerTasks,
500
+ readTriggerConfig
501
+ );
502
+ if (!task) {
503
+ error(res, 'Trigger not found', 404);
504
+ return true;
505
+ }
506
+ const result: TriggerExecutionResult = await executeTriggerTask(runtime, task, {
507
+ source: 'manual',
508
+ force: true,
509
+ });
510
+ const refreshed = task.id ? await runtime.getTask(task.id) : null;
511
+ const summary = refreshed ? taskToTriggerSummary(refreshed) : (result.trigger ?? null);
512
+ json(
513
+ res,
514
+ usingHeartbeatsAlias
515
+ ? { ok: true, result, trigger: summary, heartbeat: summary }
516
+ : { ok: true, result, trigger: summary }
517
+ );
518
+ return true;
519
+ }
520
+
521
+ const eventMatch = /^\/api\/triggers\/events\/([^/]+)$/.exec(normalizedPathname);
522
+ if (method === 'POST' && eventMatch) {
523
+ const eventKind = decodeURIComponent(eventMatch[1] ?? '').trim();
524
+ if (!eventKind) {
525
+ error(res, 'event kind is required', 400);
526
+ return true;
527
+ }
528
+
529
+ const body = await readJsonBody<Record<string, unknown>>(req, res);
530
+ if (!body) return true;
531
+ const payload = parseEventPayload(body.payload ?? body);
532
+ const tasks = await listTriggerTasks(runtime);
533
+ const matchingTasks = tasks.filter((task) => {
534
+ const trigger = readTriggerConfig(task);
535
+ return (
536
+ trigger?.enabled === true &&
537
+ trigger.triggerType === 'event' &&
538
+ trigger.eventKind === eventKind
539
+ );
540
+ });
541
+ const results: Array<{
542
+ taskId: string | undefined;
543
+ result: Awaited<ReturnType<typeof executeTriggerTask>>;
544
+ trigger: ReturnType<typeof taskToTriggerSummary> | null;
545
+ }> = [];
546
+ for (const task of matchingTasks) {
547
+ const result = await executeTriggerTask(runtime, task, {
548
+ source: 'event',
549
+ event: { kind: eventKind, payload },
550
+ });
551
+ const refreshed = task.id ? await runtime.getTask(task.id) : null;
552
+ results.push({
553
+ taskId: task.id,
554
+ result,
555
+ trigger: refreshed ? taskToTriggerSummary(refreshed) : (result.trigger ?? null),
556
+ });
557
+ }
558
+ json(res, {
559
+ ok: true,
560
+ eventKind,
561
+ matched: matchingTasks.length,
562
+ results,
563
+ });
564
+ return true;
565
+ }
566
+
567
+ const itemMatch = /^\/api\/triggers\/([^/]+)$/.exec(normalizedPathname);
568
+ if (!itemMatch) return false;
569
+ const triggerId = decodeURIComponent(itemMatch[1]);
570
+
571
+ if (method === 'GET') {
572
+ const task = await findTask(runtime, triggerId, listTriggerTasks, readTriggerConfig);
573
+ if (!task) {
574
+ error(res, 'Trigger not found', 404);
575
+ return true;
576
+ }
577
+ const summary = taskToTriggerSummary(task);
578
+ if (!summary) {
579
+ error(res, 'Trigger metadata is invalid', 500);
580
+ return true;
581
+ }
582
+ itemResponse(summary);
583
+ return true;
584
+ }
585
+
586
+ if (method === 'DELETE') {
587
+ const task = await findTask(runtime, triggerId, listTriggerTasks, readTriggerConfig);
588
+ if (!task?.id) {
589
+ error(res, 'Trigger not found', 404);
590
+ return true;
591
+ }
592
+ await runtime.deleteTask(task.id);
593
+ json(res, { ok: true });
594
+ return true;
595
+ }
596
+
597
+ if (method === 'PUT') {
598
+ const task = await findTask(runtime, triggerId, listTriggerTasks, readTriggerConfig);
599
+ if (!task?.id) {
600
+ error(res, 'Trigger not found', 404);
601
+ return true;
602
+ }
603
+ const current = readTriggerConfig(task);
604
+ if (!current) {
605
+ error(res, 'Trigger metadata is invalid', 500);
606
+ return true;
607
+ }
608
+
609
+ const body = await readJsonBody<Record<string, unknown>>(req, res);
610
+ if (!body) return true;
611
+
612
+ const kindParsed = parseTriggerKindStrict(body.kind);
613
+ if (kindParsed !== undefined && kindParsed.ok === false) {
614
+ error(res, kindParsed.error, 400);
615
+ return true;
616
+ }
617
+ const parsedKind: TriggerKind | undefined = kindParsed?.ok ? kindParsed.kind : undefined;
618
+ const nextKind: TriggerKind | undefined = parsedKind ?? parseTriggerKind(current.kind);
619
+ const nextWorkflowId = parseNonEmptyString(body.workflowId) ?? current.workflowId;
620
+ const nextWorkflowName = parseNonEmptyString(body.workflowName) ?? current.workflowName;
621
+ if (nextKind === 'workflow' && !nextWorkflowId) {
622
+ error(res, "workflowId is required when kind is 'workflow'", 400);
623
+ return true;
624
+ }
625
+
626
+ const mergedInput: TriggerDraftInput = {
627
+ displayName: typeof body.displayName === 'string' ? body.displayName : undefined,
628
+ instructions: typeof body.instructions === 'string' ? body.instructions : undefined,
629
+ triggerType:
630
+ typeof body.triggerType === 'string' ? (body.triggerType as TriggerType) : undefined,
631
+ wakeMode: typeof body.wakeMode === 'string' ? (body.wakeMode as TriggerWakeMode) : undefined,
632
+ enabled: body.enabled === undefined ? current.enabled : body.enabled === true,
633
+ createdBy: current.createdBy,
634
+ timezone: typeof body.timezone === 'string' ? body.timezone : undefined,
635
+ intervalMs: typeof body.intervalMs === 'number' ? body.intervalMs : current.intervalMs,
636
+ scheduledAtIso:
637
+ typeof body.scheduledAtIso === 'string' ? body.scheduledAtIso : current.scheduledAtIso,
638
+ cronExpression:
639
+ typeof body.cronExpression === 'string' ? body.cronExpression : current.cronExpression,
640
+ eventKind: typeof body.eventKind === 'string' ? body.eventKind : current.eventKind,
641
+ maxRuns: typeof body.maxRuns === 'number' ? body.maxRuns : current.maxRuns,
642
+ kind: nextKind,
643
+ workflowId: nextWorkflowId,
644
+ workflowName: nextWorkflowName,
645
+ };
646
+ const normalized = normalizeTriggerDraft({
647
+ input: mergedInput,
648
+ fallback: {
649
+ displayName: current.displayName,
650
+ instructions: current.instructions,
651
+ triggerType: current.triggerType,
652
+ wakeMode: current.wakeMode,
653
+ enabled: body.enabled === undefined ? current.enabled : body.enabled === true,
654
+ createdBy: current.createdBy,
655
+ },
656
+ });
657
+ if (!normalized.draft) {
658
+ error(res, normalized.error ?? 'Invalid update', 400);
659
+ return true;
660
+ }
661
+
662
+ const nextTrigger = buildTriggerConfig({
663
+ draft: normalized.draft,
664
+ triggerId: current.triggerId,
665
+ previous: current,
666
+ });
667
+ const existingMeta = (task.metadata ?? {}) as TriggerTaskMetadata;
668
+ const existingRuns = readTriggerRuns(task);
669
+
670
+ let nextMeta: TriggerTaskMetadata;
671
+ if (!nextTrigger.enabled) {
672
+ nextMeta = {
673
+ ...existingMeta,
674
+ updatedAt: Date.now(),
675
+ updateInterval: DISABLED_TRIGGER_INTERVAL_MS,
676
+ trigger: {
677
+ ...nextTrigger,
678
+ nextRunAtMs: Date.now() + DISABLED_TRIGGER_INTERVAL_MS,
679
+ },
680
+ triggerRuns: existingRuns,
681
+ };
682
+ } else {
683
+ const built = buildTriggerMetadata({
684
+ existingMetadata: existingMeta,
685
+ trigger: nextTrigger,
686
+ nowMs: Date.now(),
687
+ });
688
+ if (!built) {
689
+ error(res, 'Unable to compute trigger schedule', 400);
690
+ return true;
691
+ }
692
+ nextMeta = built;
693
+ }
694
+
695
+ await runtime.updateTask(task.id, {
696
+ description: nextTrigger.displayName,
697
+ metadata: nextMeta as Task['metadata'],
698
+ });
699
+ const refreshed = await runtime.getTask(task.id);
700
+ if (!refreshed) {
701
+ error(res, 'Trigger updated but no longer available', 500);
702
+ return true;
703
+ }
704
+ const summary = taskToTriggerSummary(refreshed);
705
+ if (!summary) {
706
+ error(res, 'Trigger metadata is invalid', 500);
707
+ return true;
708
+ }
709
+ itemResponse(summary);
710
+ return true;
711
+ }
712
+
713
+ return false;
714
+ }