@botpress/adk-cli 1.19.0-beta.4 → 1.19.0-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.
- package/dist/{README-wjb7z7f0.md → README-824wa4jj.md} +1 -1
- package/dist/SKILL-75ym6tcj.md +257 -0
- package/dist/SKILL-chnwzmt1.md +548 -0
- package/dist/SKILL-d4bs53y3.md +244 -0
- package/dist/SKILL-h4b2f1n9.md +114 -0
- package/dist/SKILL-hbw5nt95.md +111 -0
- package/dist/actions-h7hxxvxr.md +823 -0
- package/dist/adk-build-emhwd0tg.md +39 -0
- package/dist/adk-debug-yrrdtpmb.md +23 -0
- package/dist/adk-dev-console-7z9rtjpy.md +17 -0
- package/dist/adk-document-ras6zzzh.md +68 -0
- package/dist/adk-eval-vx76dwq0.md +17 -0
- package/dist/adk-explain-fcsaw852.md +22 -0
- package/dist/adk-init-9kmxmbbe.md +30 -0
- package/dist/adk-ship-dybzn884.md +29 -0
- package/dist/adk-test-xq67savs.md +27 -0
- package/dist/adk-validate-1p4skv4m.md +31 -0
- package/dist/advanced-patterns-z86m9h1a.md +669 -0
- package/dist/agent-config-1aqvkrgf.md +450 -0
- package/dist/agent-steps-zj4c1n9t.md +115 -0
- package/dist/assets-q7jt8r2q.md +237 -0
- package/dist/autonomous-execution-fymc12e5.md +747 -0
- package/dist/cli-c31e06yr.md +1334 -0
- package/dist/cli.js +970 -566
- package/dist/commands.json +24 -3
- package/dist/common-failures-ht29w2db.md +231 -0
- package/dist/component-registry-bbpw0see.md +43 -0
- package/dist/context-api-760cvh0h.md +461 -0
- package/dist/conversation-analysis-8ka4nrm4.md +420 -0
- package/dist/conversation-lifecycle-4seqqjc1.md +311 -0
- package/dist/conversations-gwfvxaee.md +1111 -0
- package/dist/custom-components-y5wdgpey.md +167 -0
- package/dist/debug-workflow-vz84q80v.md +239 -0
- package/dist/desk-k54ktvsd.md +852 -0
- package/dist/doc-standards-ck6ax2jd.md +124 -0
- package/dist/eval-format-7n9brja4.md +354 -0
- package/dist/evals-y1zcr1mc.md +11 -0
- package/dist/explain-config-1yaq3zzd.md +424 -0
- package/dist/files-dzt0wxn3.md +918 -0
- package/dist/integration-actions-xang7tma.md +659 -0
- package/dist/integrations-k5ss07f4.md +266 -0
- package/dist/interfaces-6t0bpxs4.md +97 -0
- package/dist/knowledge-bases-20rg8p5c.md +456 -0
- package/dist/llm-debugging-76ng8rzm.md +209 -0
- package/dist/mcp-server-n1asyzhv.md +112 -0
- package/dist/messages-gkkg0b54.md +769 -0
- package/dist/model-configuration-f273kgd0.md +201 -0
- package/dist/multi-agent-dashboard-1mdfjdts.md +70 -0
- package/dist/pages-xn18xn73.md +273 -0
- package/dist/patterns-mistakes-5vb99dhf.md +469 -0
- package/dist/plugins-ttnx8f49.md +280 -0
- package/dist/tables-x45t36dd.md +1136 -0
- package/dist/tags-46td3qrg.md +516 -0
- package/dist/test-patterns-hj47w4nc.md +355 -0
- package/dist/testing-workflow-2vkp932f.md +225 -0
- package/dist/tools-wk7bq24z.md +581 -0
- package/dist/trace-summarization-hdyx1c2e.md +313 -0
- package/dist/traces-and-logs-f2kjfh19.md +203 -0
- package/dist/triggers-4j2cc86x.md +719 -0
- package/dist/ui-server-entry.js +952 -137
- package/dist/workflow-steps-mrdrf1c5.md +851 -0
- package/dist/workflows-gadx3s69.md +1408 -0
- package/dist/zai-agent-reference-e6644aad.md +454 -0
- package/dist/zai-complete-guide-mnna5d6f.md +1408 -0
- package/package.json +4 -4
|
@@ -0,0 +1,719 @@
|
|
|
1
|
+
# Triggers
|
|
2
|
+
|
|
3
|
+
Triggers allow your bot to react to events from the bot itself or integrated services. They enable event-driven automation and workflow orchestration.
|
|
4
|
+
|
|
5
|
+
> **⚠️ IMPORTANT:** Always verify integration events exist before using them! Use `adk info <integration> --events` to discover real events. Integration events change between versions.
|
|
6
|
+
>
|
|
7
|
+
> **⚠️ Dev-mode limitation:** `adk dev` is not a perfect mirror of production for every integration event flow. Some private or custom integration events may not register or route locally even when the event name is valid. If an event-driven flow works in docs but not in local dev, verify the event first, then test against a deployed bot before assuming your trigger code is wrong.
|
|
8
|
+
|
|
9
|
+
## Basic Concepts
|
|
10
|
+
|
|
11
|
+
### What are Triggers?
|
|
12
|
+
- **Event handlers**: React to bot or integration events
|
|
13
|
+
- **Automated**: Execute automatically when events occur
|
|
14
|
+
- **Flexible**: Can start workflows, update tables, or perform any action
|
|
15
|
+
|
|
16
|
+
### File Location
|
|
17
|
+
- **Location**: `src/triggers/*.ts`
|
|
18
|
+
- **Auto-registration**: Files automatically subscribe to specified events
|
|
19
|
+
|
|
20
|
+
## Creating Triggers
|
|
21
|
+
|
|
22
|
+
### Basic Structure
|
|
23
|
+
|
|
24
|
+
```typescript
|
|
25
|
+
import { Trigger } from "@botpress/runtime";
|
|
26
|
+
|
|
27
|
+
export default new Trigger({
|
|
28
|
+
name: "myTrigger",
|
|
29
|
+
description: "Handles user creation events",
|
|
30
|
+
events: ["user.created"], // Events to subscribe to
|
|
31
|
+
|
|
32
|
+
handler: async ({ event }) => {
|
|
33
|
+
console.log(event.type); // "user.created"
|
|
34
|
+
console.log(event.payload); // Event data
|
|
35
|
+
|
|
36
|
+
// React to the event
|
|
37
|
+
await handleUserCreated(event.payload);
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### Multiple Events
|
|
43
|
+
|
|
44
|
+
Subscribe to multiple events in one trigger:
|
|
45
|
+
|
|
46
|
+
```typescript
|
|
47
|
+
export default new Trigger({
|
|
48
|
+
name: "userEvents",
|
|
49
|
+
description: "Handles all user-related events",
|
|
50
|
+
events: [
|
|
51
|
+
"user.created",
|
|
52
|
+
"user.updated",
|
|
53
|
+
"user.deleted"
|
|
54
|
+
],
|
|
55
|
+
|
|
56
|
+
handler: async ({ event }) => {
|
|
57
|
+
switch (event.type) {
|
|
58
|
+
case "user.created":
|
|
59
|
+
await handleUserCreated(event.payload);
|
|
60
|
+
break;
|
|
61
|
+
case "user.updated":
|
|
62
|
+
await handleUserUpdated(event.payload);
|
|
63
|
+
break;
|
|
64
|
+
case "user.deleted":
|
|
65
|
+
await handleUserDeleted(event.payload);
|
|
66
|
+
break;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Event Types
|
|
73
|
+
|
|
74
|
+
### Bot Events
|
|
75
|
+
|
|
76
|
+
Standard events emitted by the bot:
|
|
77
|
+
|
|
78
|
+
```typescript
|
|
79
|
+
// User events
|
|
80
|
+
"user.created" // New user created
|
|
81
|
+
"user.updated" // User information updated
|
|
82
|
+
"user.deleted" // User deleted
|
|
83
|
+
|
|
84
|
+
// Conversation events
|
|
85
|
+
"conversation.started" // New conversation started
|
|
86
|
+
"conversation.ended" // Conversation ended
|
|
87
|
+
"message.created" // New message in conversation
|
|
88
|
+
|
|
89
|
+
// Workflow events
|
|
90
|
+
"workflow.started" // Workflow instance started
|
|
91
|
+
"workflow.completed" // Workflow completed successfully
|
|
92
|
+
"workflow.failed" // Workflow failed
|
|
93
|
+
|
|
94
|
+
// Bot lifecycle events
|
|
95
|
+
"bot.started" // Bot started
|
|
96
|
+
"bot.stopped" // Bot stopped
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### Integration Events
|
|
100
|
+
|
|
101
|
+
Events from integrated services use the pattern `integrationName:eventName`.
|
|
102
|
+
|
|
103
|
+
**IMPORTANT:** Always verify events exist using `adk info <integration> --events` before using them.
|
|
104
|
+
|
|
105
|
+
```typescript
|
|
106
|
+
// Intercom events (real events from vertical-one)
|
|
107
|
+
"intercom:conversationEvent" // Fires on create/update/delete
|
|
108
|
+
"intercom:contactEvent" // Contact created/updated
|
|
109
|
+
"intercom:adminEvent" // Admin changes
|
|
110
|
+
|
|
111
|
+
// Slack events (verified with adk info slack --events)
|
|
112
|
+
"slack:reactionAdded"
|
|
113
|
+
"slack:reactionRemoved"
|
|
114
|
+
"slack:memberJoinedChannel"
|
|
115
|
+
"slack:memberJoinedWorkspace"
|
|
116
|
+
"slack:memberLeftChannel"
|
|
117
|
+
"slack:workflowWebhook"
|
|
118
|
+
|
|
119
|
+
// Linear events (verified with adk info linear --events)
|
|
120
|
+
"linear:issueCreated"
|
|
121
|
+
"linear:issueUpdated"
|
|
122
|
+
"linear:issueDeleted"
|
|
123
|
+
|
|
124
|
+
// GitHub events (verified with adk info github --events)
|
|
125
|
+
"github:issueOpened"
|
|
126
|
+
"github:pullRequestOpened"
|
|
127
|
+
"github:pullRequestMerged"
|
|
128
|
+
"github:pullRequestReviewSubmitted"
|
|
129
|
+
|
|
130
|
+
// Webchat events (verified with adk info webchat --events)
|
|
131
|
+
"webchat:conversationStarted"
|
|
132
|
+
"webchat:trigger"
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
**How to discover events:**
|
|
136
|
+
```bash
|
|
137
|
+
# Search for an integration
|
|
138
|
+
adk search email
|
|
139
|
+
|
|
140
|
+
# Get all events for an integration
|
|
141
|
+
adk info slack --events
|
|
142
|
+
adk info linear --events
|
|
143
|
+
adk info github --events
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
Checking `adk info <integration> --events` confirms the event exists, but it does not guarantee that every event source behaves the same in `adk dev` as it does after deployment.
|
|
147
|
+
|
|
148
|
+
## Trigger Handlers
|
|
149
|
+
|
|
150
|
+
### Starting Workflows
|
|
151
|
+
|
|
152
|
+
Common pattern: Start a workflow when an event occurs:
|
|
153
|
+
|
|
154
|
+
```typescript
|
|
155
|
+
import { Trigger } from "@botpress/runtime";
|
|
156
|
+
import { OnboardingWorkflow } from "../workflows/onboarding";
|
|
157
|
+
|
|
158
|
+
export default new Trigger({
|
|
159
|
+
name: "userOnboarding",
|
|
160
|
+
description: "Start onboarding when user is created",
|
|
161
|
+
events: ["user.created"],
|
|
162
|
+
|
|
163
|
+
handler: async ({ event }) => {
|
|
164
|
+
const { userId, email, name } = event.payload;
|
|
165
|
+
|
|
166
|
+
// Start onboarding workflow
|
|
167
|
+
const instance = await OnboardingWorkflow.start({
|
|
168
|
+
userId,
|
|
169
|
+
email,
|
|
170
|
+
name,
|
|
171
|
+
createdAt: new Date()
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
console.log(`Started onboarding for ${userId}: ${instance.id}`);
|
|
175
|
+
}
|
|
176
|
+
});
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
### Updating Tables
|
|
180
|
+
|
|
181
|
+
Store event data in tables:
|
|
182
|
+
|
|
183
|
+
```typescript
|
|
184
|
+
import { Trigger } from "@botpress/runtime";
|
|
185
|
+
import { EventLogTable } from "../tables/EventLog";
|
|
186
|
+
|
|
187
|
+
export default new Trigger({
|
|
188
|
+
name: "eventLogger",
|
|
189
|
+
description: "Log all events to database",
|
|
190
|
+
events: [
|
|
191
|
+
"user.created",
|
|
192
|
+
"user.updated",
|
|
193
|
+
"conversation.started",
|
|
194
|
+
"workflow.completed"
|
|
195
|
+
],
|
|
196
|
+
|
|
197
|
+
handler: async ({ event }) => {
|
|
198
|
+
// Store event in table
|
|
199
|
+
await EventLogTable.createRows({
|
|
200
|
+
rows: [{
|
|
201
|
+
type: event.type,
|
|
202
|
+
payload: event.payload,
|
|
203
|
+
timestamp: new Date(),
|
|
204
|
+
metadata: {
|
|
205
|
+
source: event.source || "bot",
|
|
206
|
+
version: event.version || "1.0"
|
|
207
|
+
}
|
|
208
|
+
}]
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
});
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
### Calling Actions
|
|
215
|
+
|
|
216
|
+
Execute bot actions or integration actions in response to events:
|
|
217
|
+
|
|
218
|
+
```typescript
|
|
219
|
+
import { Trigger } from "@botpress/runtime";
|
|
220
|
+
import { actions } from "@botpress/runtime";
|
|
221
|
+
|
|
222
|
+
export default new Trigger({
|
|
223
|
+
name: "notificationSender",
|
|
224
|
+
description: "Send email notifications for important events",
|
|
225
|
+
events: ["workflow.failed", "user.deleted"],
|
|
226
|
+
|
|
227
|
+
handler: async ({ event }) => {
|
|
228
|
+
// Call integration action (e.g., SendGrid)
|
|
229
|
+
// First add the integration: adk add sendgrid
|
|
230
|
+
await actions.sendgrid.sendEmail({
|
|
231
|
+
to: "admin@example.com",
|
|
232
|
+
from: "bot@example.com",
|
|
233
|
+
subject: `Alert: ${event.type}`,
|
|
234
|
+
content: `Event occurred: ${JSON.stringify(event.payload)}`
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
});
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
**Note:** The `actions` object provides access to:
|
|
241
|
+
- **Bot actions**: `actions.myAction()` - your custom actions from `src/actions/`
|
|
242
|
+
- **Integration actions**: `actions.integrationName.actionName()` - actions from installed integrations
|
|
243
|
+
|
|
244
|
+
## Integration Event Examples
|
|
245
|
+
|
|
246
|
+
### Intercom Integration
|
|
247
|
+
|
|
248
|
+
Real-world example from vertical-one bot:
|
|
249
|
+
|
|
250
|
+
```typescript
|
|
251
|
+
import { Trigger } from "@botpress/runtime";
|
|
252
|
+
import {
|
|
253
|
+
syncSingleConversation,
|
|
254
|
+
deleteConversation,
|
|
255
|
+
} from "../utils/syncConversation";
|
|
256
|
+
|
|
257
|
+
export default new Trigger({
|
|
258
|
+
name: "onIntercomUpdate",
|
|
259
|
+
description:
|
|
260
|
+
"Updates the Intercom tables in realtime when conversations are created or messages are sent",
|
|
261
|
+
events: ["intercom:conversationEvent"],
|
|
262
|
+
|
|
263
|
+
handler: async ({ event }) => {
|
|
264
|
+
const { eventType, conversationId } = event.payload;
|
|
265
|
+
|
|
266
|
+
console.log(
|
|
267
|
+
`Intercom event: ${eventType} for conversation ${conversationId}`
|
|
268
|
+
);
|
|
269
|
+
|
|
270
|
+
try {
|
|
271
|
+
if (eventType === "deleted") {
|
|
272
|
+
// Delete the conversation and its messages
|
|
273
|
+
await deleteConversation(conversationId);
|
|
274
|
+
} else {
|
|
275
|
+
// Sync the conversation with all its data
|
|
276
|
+
await syncSingleConversation(conversationId);
|
|
277
|
+
}
|
|
278
|
+
} catch (error) {
|
|
279
|
+
console.error(
|
|
280
|
+
`Failed to handle ${eventType} event for conversation ${conversationId}:`,
|
|
281
|
+
error
|
|
282
|
+
);
|
|
283
|
+
throw error;
|
|
284
|
+
}
|
|
285
|
+
},
|
|
286
|
+
});
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
### Linear Integration
|
|
290
|
+
|
|
291
|
+
Real example from ADK source code:
|
|
292
|
+
|
|
293
|
+
```typescript
|
|
294
|
+
import { Trigger } from "@botpress/runtime";
|
|
295
|
+
import { LinearIssueTable } from "../tables/LinearIssues";
|
|
296
|
+
|
|
297
|
+
export default new Trigger({
|
|
298
|
+
name: "onLinearIssueUpdate",
|
|
299
|
+
description: "Updates the Linear Issue Table when issues change",
|
|
300
|
+
events: [
|
|
301
|
+
"linear:issueCreated",
|
|
302
|
+
"linear:issueUpdated",
|
|
303
|
+
"linear:issueDeleted"
|
|
304
|
+
],
|
|
305
|
+
|
|
306
|
+
handler: async ({ event }) => {
|
|
307
|
+
if (event.type === "linear:issueDeleted") {
|
|
308
|
+
await LinearIssueTable.deleteRows({
|
|
309
|
+
linearId: event.payload.id
|
|
310
|
+
});
|
|
311
|
+
} else if (event.type === "linear:issueCreated") {
|
|
312
|
+
await LinearIssueTable.createRows({
|
|
313
|
+
rows: [{
|
|
314
|
+
linearId: event.payload.linearIds?.issueId,
|
|
315
|
+
content: `# ${event.payload.title}\n\n${event.payload.description}`,
|
|
316
|
+
issue: event.payload,
|
|
317
|
+
linearUpdatedAt: event.payload.updatedAt
|
|
318
|
+
}]
|
|
319
|
+
});
|
|
320
|
+
} else if (event.type === "linear:issueUpdated") {
|
|
321
|
+
await LinearIssueTable.upsertRows({
|
|
322
|
+
rows: [{
|
|
323
|
+
linearId: event.payload.linearIds?.issueId,
|
|
324
|
+
content: `# ${event.payload.title}\n\n${event.payload.description}`,
|
|
325
|
+
issue: event.payload,
|
|
326
|
+
linearUpdatedAt: event.payload.updatedAt
|
|
327
|
+
}],
|
|
328
|
+
keyColumn: "linearId"
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
});
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
### Slack Integration
|
|
336
|
+
|
|
337
|
+
```typescript
|
|
338
|
+
import { Trigger } from "@botpress/runtime";
|
|
339
|
+
import { SlackActivityTable } from "../tables/SlackActivity";
|
|
340
|
+
|
|
341
|
+
export default new Trigger({
|
|
342
|
+
name: "slackActivityMonitor",
|
|
343
|
+
description: "Monitor Slack reactions and member activity",
|
|
344
|
+
events: [
|
|
345
|
+
"slack:reactionAdded",
|
|
346
|
+
"slack:memberJoinedChannel"
|
|
347
|
+
],
|
|
348
|
+
|
|
349
|
+
handler: async ({ event }) => {
|
|
350
|
+
if (event.type === "slack:reactionAdded") {
|
|
351
|
+
const { reaction, user, item } = event.payload;
|
|
352
|
+
|
|
353
|
+
// Log important reactions to table
|
|
354
|
+
if (reaction === "fire" || reaction === "star" || reaction === "eyes") {
|
|
355
|
+
await SlackActivityTable.createRows({
|
|
356
|
+
rows: [{
|
|
357
|
+
type: "reaction",
|
|
358
|
+
reaction: reaction,
|
|
359
|
+
user: user,
|
|
360
|
+
messageId: item.ts,
|
|
361
|
+
channelId: item.channel,
|
|
362
|
+
timestamp: new Date()
|
|
363
|
+
}]
|
|
364
|
+
});
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
if (event.type === "slack:memberJoinedChannel") {
|
|
369
|
+
const { user, channel } = event.payload;
|
|
370
|
+
|
|
371
|
+
// Log new member joins
|
|
372
|
+
await SlackActivityTable.createRows({
|
|
373
|
+
rows: [{
|
|
374
|
+
type: "member_join",
|
|
375
|
+
user: user,
|
|
376
|
+
channelId: channel,
|
|
377
|
+
timestamp: new Date()
|
|
378
|
+
}]
|
|
379
|
+
});
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
});
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
## Advanced Patterns
|
|
386
|
+
|
|
387
|
+
### Conditional Processing
|
|
388
|
+
|
|
389
|
+
Process events based on conditions:
|
|
390
|
+
|
|
391
|
+
```typescript
|
|
392
|
+
export default new Trigger({
|
|
393
|
+
name: "conditionalProcessor",
|
|
394
|
+
events: ["user.created"],
|
|
395
|
+
|
|
396
|
+
handler: async ({ event }) => {
|
|
397
|
+
const { email, source, metadata } = event.payload;
|
|
398
|
+
|
|
399
|
+
// Only process certain users
|
|
400
|
+
if (!email.endsWith("@company.com")) {
|
|
401
|
+
console.log("Skipping external user");
|
|
402
|
+
return;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
// Different handling based on source
|
|
406
|
+
if (source === "api") {
|
|
407
|
+
await handleApiUser(event.payload);
|
|
408
|
+
} else if (source === "signup") {
|
|
409
|
+
await handleSignupUser(event.payload);
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
// Check metadata flags
|
|
413
|
+
if (metadata?.requiresApproval) {
|
|
414
|
+
await startApprovalWorkflow(event.payload);
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
});
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
### Event Aggregation
|
|
421
|
+
|
|
422
|
+
Collect and batch process events:
|
|
423
|
+
|
|
424
|
+
```typescript
|
|
425
|
+
import { Trigger } from "@botpress/runtime";
|
|
426
|
+
import { EventBatchTable } from "../tables/EventBatch";
|
|
427
|
+
|
|
428
|
+
export default new Trigger({
|
|
429
|
+
name: "eventAggregator",
|
|
430
|
+
events: ["metrics.recorded"],
|
|
431
|
+
|
|
432
|
+
handler: async ({ event }) => {
|
|
433
|
+
// Store event for batching
|
|
434
|
+
await EventBatchTable.createRows({
|
|
435
|
+
rows: [{
|
|
436
|
+
type: event.type,
|
|
437
|
+
data: event.payload,
|
|
438
|
+
processed: false,
|
|
439
|
+
timestamp: new Date()
|
|
440
|
+
}]
|
|
441
|
+
});
|
|
442
|
+
|
|
443
|
+
// Check if we should process batch
|
|
444
|
+
const { rows } = await EventBatchTable.findRows({
|
|
445
|
+
where: { processed: false },
|
|
446
|
+
limit: 100
|
|
447
|
+
});
|
|
448
|
+
|
|
449
|
+
if (rows.length >= 100) {
|
|
450
|
+
// Process batch
|
|
451
|
+
await processBatch(rows);
|
|
452
|
+
|
|
453
|
+
// Mark as processed
|
|
454
|
+
const ids = rows.map(r => r.id);
|
|
455
|
+
await EventBatchTable.updateRows({
|
|
456
|
+
where: { id: { $in: ids } },
|
|
457
|
+
updates: { processed: true }
|
|
458
|
+
});
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
});
|
|
462
|
+
```
|
|
463
|
+
|
|
464
|
+
### Workflow Orchestration
|
|
465
|
+
|
|
466
|
+
Chain workflows based on events:
|
|
467
|
+
|
|
468
|
+
```typescript
|
|
469
|
+
export default new Trigger({
|
|
470
|
+
name: "workflowOrchestrator",
|
|
471
|
+
events: ["workflow.completed"],
|
|
472
|
+
|
|
473
|
+
handler: async ({ event }) => {
|
|
474
|
+
const { workflowName, output } = event.payload;
|
|
475
|
+
|
|
476
|
+
// Chain workflows based on completion
|
|
477
|
+
switch (workflowName) {
|
|
478
|
+
case "dataCollection":
|
|
479
|
+
// Start processing workflow
|
|
480
|
+
await ProcessingWorkflow.start({
|
|
481
|
+
data: output.collectedData
|
|
482
|
+
});
|
|
483
|
+
break;
|
|
484
|
+
|
|
485
|
+
case "processing":
|
|
486
|
+
// Start validation workflow
|
|
487
|
+
await ValidationWorkflow.start({
|
|
488
|
+
processedData: output.result
|
|
489
|
+
});
|
|
490
|
+
break;
|
|
491
|
+
|
|
492
|
+
case "validation":
|
|
493
|
+
// Start final reporting workflow
|
|
494
|
+
if (output.isValid) {
|
|
495
|
+
await ReportingWorkflow.start({
|
|
496
|
+
data: output.validatedData
|
|
497
|
+
});
|
|
498
|
+
}
|
|
499
|
+
break;
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
});
|
|
503
|
+
```
|
|
504
|
+
|
|
505
|
+
### Error Recovery
|
|
506
|
+
|
|
507
|
+
Handle failed events:
|
|
508
|
+
|
|
509
|
+
```typescript
|
|
510
|
+
export default new Trigger({
|
|
511
|
+
name: "errorRecovery",
|
|
512
|
+
events: ["workflow.failed", "integration:error"],
|
|
513
|
+
|
|
514
|
+
handler: async ({ event }) => {
|
|
515
|
+
const { error, context, retryCount = 0 } = event.payload;
|
|
516
|
+
|
|
517
|
+
// Log error
|
|
518
|
+
await ErrorLogTable.createRows({
|
|
519
|
+
rows: [{
|
|
520
|
+
type: event.type,
|
|
521
|
+
error: error.message,
|
|
522
|
+
stack: error.stack,
|
|
523
|
+
context,
|
|
524
|
+
timestamp: new Date()
|
|
525
|
+
}]
|
|
526
|
+
});
|
|
527
|
+
|
|
528
|
+
// Attempt recovery
|
|
529
|
+
if (retryCount < 3) {
|
|
530
|
+
// Retry with backoff
|
|
531
|
+
setTimeout(async () => {
|
|
532
|
+
await RetryWorkflow.start({
|
|
533
|
+
originalEvent: event,
|
|
534
|
+
retryCount: retryCount + 1
|
|
535
|
+
});
|
|
536
|
+
}, Math.pow(2, retryCount) * 1000);
|
|
537
|
+
} else {
|
|
538
|
+
// Send alert after max retries
|
|
539
|
+
await actions.sendAlert({
|
|
540
|
+
severity: "critical",
|
|
541
|
+
title: "Max retries exceeded",
|
|
542
|
+
details: { event, error }
|
|
543
|
+
});
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
});
|
|
547
|
+
```
|
|
548
|
+
|
|
549
|
+
## Best Practices
|
|
550
|
+
|
|
551
|
+
### 1. Use Descriptive Names
|
|
552
|
+
```typescript
|
|
553
|
+
// ✅ Good
|
|
554
|
+
export default new Trigger({
|
|
555
|
+
name: "linearIssueSync",
|
|
556
|
+
description: "Syncs Linear issues to internal tracking"
|
|
557
|
+
});
|
|
558
|
+
|
|
559
|
+
// ❌ Bad
|
|
560
|
+
export default new Trigger({
|
|
561
|
+
name: "trigger1",
|
|
562
|
+
description: "Does stuff"
|
|
563
|
+
});
|
|
564
|
+
```
|
|
565
|
+
|
|
566
|
+
### 2. Handle Errors Gracefully
|
|
567
|
+
```typescript
|
|
568
|
+
handler: async ({ event }) => {
|
|
569
|
+
try {
|
|
570
|
+
await processEvent(event);
|
|
571
|
+
} catch (error) {
|
|
572
|
+
console.error(`Failed to process ${event.type}:`, error);
|
|
573
|
+
|
|
574
|
+
// Store error for debugging
|
|
575
|
+
await ErrorTable.createRows({
|
|
576
|
+
rows: [{
|
|
577
|
+
trigger: "myTrigger",
|
|
578
|
+
event: event.type,
|
|
579
|
+
error: error.message,
|
|
580
|
+
timestamp: new Date()
|
|
581
|
+
}]
|
|
582
|
+
});
|
|
583
|
+
|
|
584
|
+
// Don't re-throw unless you want to retry
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
```
|
|
588
|
+
|
|
589
|
+
### 3. Avoid Heavy Processing
|
|
590
|
+
```typescript
|
|
591
|
+
handler: async ({ event }) => {
|
|
592
|
+
// ✅ Good: Start async workflow
|
|
593
|
+
await HeavyProcessingWorkflow.start({
|
|
594
|
+
eventData: event.payload
|
|
595
|
+
});
|
|
596
|
+
|
|
597
|
+
// ❌ Bad: Long-running synchronous processing
|
|
598
|
+
// const result = await performHeavyComputation(event.payload);
|
|
599
|
+
}
|
|
600
|
+
```
|
|
601
|
+
|
|
602
|
+
### 4. Use Event Patterns
|
|
603
|
+
```typescript
|
|
604
|
+
// Group related events from multiple integrations
|
|
605
|
+
export default new Trigger({
|
|
606
|
+
name: "issueTracker",
|
|
607
|
+
events: [
|
|
608
|
+
"linear:issueCreated",
|
|
609
|
+
"linear:issueUpdated",
|
|
610
|
+
"github:issueOpened",
|
|
611
|
+
"github:pullRequestOpened"
|
|
612
|
+
],
|
|
613
|
+
|
|
614
|
+
handler: async ({ event }) => {
|
|
615
|
+
const [integration, action] = event.type.split(":");
|
|
616
|
+
|
|
617
|
+
// Common processing for all issue-related events
|
|
618
|
+
await IssueTrackingTable.createRows({
|
|
619
|
+
rows: [{
|
|
620
|
+
source: integration,
|
|
621
|
+
eventType: action,
|
|
622
|
+
data: event.payload,
|
|
623
|
+
timestamp: new Date()
|
|
624
|
+
}]
|
|
625
|
+
});
|
|
626
|
+
}
|
|
627
|
+
});
|
|
628
|
+
```
|
|
629
|
+
|
|
630
|
+
### 5. Document Event Payloads
|
|
631
|
+
```typescript
|
|
632
|
+
/**
|
|
633
|
+
* Handles user creation events
|
|
634
|
+
*
|
|
635
|
+
* Expected payload:
|
|
636
|
+
* {
|
|
637
|
+
* userId: string
|
|
638
|
+
* email: string
|
|
639
|
+
* name: string
|
|
640
|
+
* source: "signup" | "api" | "admin"
|
|
641
|
+
* metadata?: Record<string, any>
|
|
642
|
+
* }
|
|
643
|
+
*/
|
|
644
|
+
export default new Trigger({
|
|
645
|
+
name: "userCreatedHandler",
|
|
646
|
+
events: ["user.created"],
|
|
647
|
+
|
|
648
|
+
handler: async ({ event }) => {
|
|
649
|
+
const { userId, email, name, source } = event.payload;
|
|
650
|
+
// Process with confidence in payload structure
|
|
651
|
+
}
|
|
652
|
+
});
|
|
653
|
+
```
|
|
654
|
+
|
|
655
|
+
## Common Patterns
|
|
656
|
+
|
|
657
|
+
### Audit Trail
|
|
658
|
+
```typescript
|
|
659
|
+
export default new Trigger({
|
|
660
|
+
name: "auditTrail",
|
|
661
|
+
events: [
|
|
662
|
+
"user.created",
|
|
663
|
+
"user.updated",
|
|
664
|
+
"user.deleted",
|
|
665
|
+
"conversation.started"
|
|
666
|
+
],
|
|
667
|
+
|
|
668
|
+
handler: async ({ event }) => {
|
|
669
|
+
await AuditTable.createRows({
|
|
670
|
+
rows: [{
|
|
671
|
+
eventType: event.type,
|
|
672
|
+
payload: event.payload,
|
|
673
|
+
userId: event.payload.userId || event.userId,
|
|
674
|
+
timestamp: new Date()
|
|
675
|
+
}]
|
|
676
|
+
});
|
|
677
|
+
}
|
|
678
|
+
});
|
|
679
|
+
```
|
|
680
|
+
|
|
681
|
+
### Notification System
|
|
682
|
+
```typescript
|
|
683
|
+
export default new Trigger({
|
|
684
|
+
name: "notifications",
|
|
685
|
+
events: [
|
|
686
|
+
"workflow.failed",
|
|
687
|
+
"user.deleted",
|
|
688
|
+
"integration:error"
|
|
689
|
+
],
|
|
690
|
+
|
|
691
|
+
handler: async ({ event }) => {
|
|
692
|
+
const notifications = {
|
|
693
|
+
"workflow.failed": {
|
|
694
|
+
channel: "slack",
|
|
695
|
+
priority: "high",
|
|
696
|
+
template: "workflow-failure"
|
|
697
|
+
},
|
|
698
|
+
"user.deleted": {
|
|
699
|
+
channel: "email",
|
|
700
|
+
priority: "normal",
|
|
701
|
+
template: "user-deleted"
|
|
702
|
+
},
|
|
703
|
+
"integration:error": {
|
|
704
|
+
channel: "pagerduty",
|
|
705
|
+
priority: "critical",
|
|
706
|
+
template: "integration-error"
|
|
707
|
+
}
|
|
708
|
+
};
|
|
709
|
+
|
|
710
|
+
const config = notifications[event.type];
|
|
711
|
+
if (config) {
|
|
712
|
+
await actions.sendNotification({
|
|
713
|
+
...config,
|
|
714
|
+
data: event.payload
|
|
715
|
+
});
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
});
|
|
719
|
+
```
|