@keystrokehq/skills 0.0.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.
- package/AGENTS-blurb.md +123 -0
- package/LICENSE +21 -0
- package/README.md +63 -0
- package/keystroke-agent-authoring/SKILL.md +225 -0
- package/keystroke-agent-authoring/evals/evals.json +29 -0
- package/keystroke-agent-authoring/references/messaging-gateways.md +242 -0
- package/keystroke-agent-authoring/references/patterns.md +417 -0
- package/keystroke-agent-authoring/references/prebuilt-integrations.md +879 -0
- package/keystroke-agent-authoring/references/sandbox-and-mcp.md +214 -0
- package/keystroke-agent-authoring/references/source-map.md +182 -0
- package/keystroke-agent-authoring/references/testing.md +85 -0
- package/keystroke-cli-workspace/SKILL.md +93 -0
- package/keystroke-cli-workspace/evals/evals.json +23 -0
- package/keystroke-cli-workspace/references/command-map.md +50 -0
- package/keystroke-cli-workspace/references/credentials-and-connect.md +79 -0
- package/keystroke-cli-workspace/references/project-lifecycle.md +85 -0
- package/keystroke-credential-binding/SKILL.md +509 -0
- package/keystroke-credential-binding/evals/evals.json +29 -0
- package/keystroke-credential-binding/references/cli.md +85 -0
- package/keystroke-credential-binding/references/patterns.md +878 -0
- package/keystroke-credential-binding/references/source-map.md +69 -0
- package/keystroke-data-toolkit/SKILL.md +59 -0
- package/keystroke-data-toolkit/evals/evals.json +23 -0
- package/keystroke-data-toolkit/references/usage.md +79 -0
- package/keystroke-task-authoring/SKILL.md +124 -0
- package/keystroke-task-authoring/evals/evals.json +23 -0
- package/keystroke-task-authoring/references/patterns.md +132 -0
- package/keystroke-task-authoring/references/source-map.md +61 -0
- package/keystroke-trigger-authoring/SKILL.md +189 -0
- package/keystroke-trigger-authoring/evals/evals.json +29 -0
- package/keystroke-trigger-authoring/references/patterns.md +265 -0
- package/keystroke-trigger-authoring/references/source-map.md +128 -0
- package/keystroke-trigger-authoring/references/testing.md +148 -0
- package/keystroke-workflow-as-tool-debugging/SKILL.md +52 -0
- package/keystroke-workflow-as-tool-debugging/evals/evals.json +23 -0
- package/keystroke-workflow-as-tool-debugging/references/playbook.md +77 -0
- package/keystroke-workflow-authoring/SKILL.md +234 -0
- package/keystroke-workflow-authoring/evals/evals.json +29 -0
- package/keystroke-workflow-authoring/references/patterns.md +265 -0
- package/keystroke-workflow-authoring/references/prebuilt-integrations.md +811 -0
- package/keystroke-workflow-authoring/references/runtime-helpers.md +264 -0
- package/keystroke-workflow-authoring/references/source-map.md +108 -0
- package/keystroke-workflow-authoring/references/testing.md +108 -0
- package/package.json +26 -0
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: keystroke-trigger-authoring
|
|
3
|
+
description: Build Keystroke cron, webhook, polling, and provider triggers with @keystrokehq/core. Use when the user wants to author, test, or explain trigger code, including webhook verification, polling schedules, filter and idempotency callbacks, and mapping trigger input into workflows or task-driven agent runs.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Keystroke Trigger Authoring
|
|
7
|
+
|
|
8
|
+
Use this skill when an agent needs to write or change Keystroke trigger code.
|
|
9
|
+
|
|
10
|
+
Keep this skill focused on authored trigger code:
|
|
11
|
+
- use `../keystroke-workflow-authoring/SKILL.md` for workflow structure
|
|
12
|
+
- use `../keystroke-task-authoring/SKILL.md` for task authoring
|
|
13
|
+
- use `../keystroke-credential-binding/SKILL.md` when the trigger needs credentials
|
|
14
|
+
- use `../keystroke-agent-authoring/SKILL.md` for messaging gateways and conversational agent entry
|
|
15
|
+
|
|
16
|
+
## Quick start
|
|
17
|
+
|
|
18
|
+
### Workflow trigger
|
|
19
|
+
|
|
20
|
+
```ts
|
|
21
|
+
import { webhookTrigger, Workflow } from '@keystrokehq/core';
|
|
22
|
+
import { z } from 'zod';
|
|
23
|
+
|
|
24
|
+
export const paymentWebhook = webhookTrigger({
|
|
25
|
+
name: 'Payment Webhook',
|
|
26
|
+
description: 'Receives payment events from an external provider.',
|
|
27
|
+
source: {
|
|
28
|
+
type: 'custom',
|
|
29
|
+
method: 'POST',
|
|
30
|
+
path: '/payments',
|
|
31
|
+
},
|
|
32
|
+
payload: z.object({
|
|
33
|
+
id: z.string(),
|
|
34
|
+
type: z.string(),
|
|
35
|
+
data: z.object({
|
|
36
|
+
amount: z.number(),
|
|
37
|
+
}),
|
|
38
|
+
}),
|
|
39
|
+
filter: (payload) => payload.type === 'payment.completed',
|
|
40
|
+
idempotencyKey: (payload) => payload.id,
|
|
41
|
+
});
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Attach the trigger to a workflow via the `triggers` array. Call the trigger as a function to bind a `transform`:
|
|
45
|
+
|
|
46
|
+
```ts
|
|
47
|
+
export const paymentWorkflow = new Workflow({
|
|
48
|
+
id: 'payment-workflow',
|
|
49
|
+
name: 'Payment Workflow',
|
|
50
|
+
input: z.object({ eventId: z.string(), amount: z.number() }),
|
|
51
|
+
output: z.object({ ok: z.boolean() }),
|
|
52
|
+
triggers: [
|
|
53
|
+
paymentWebhook({
|
|
54
|
+
transform: (payload) => ({
|
|
55
|
+
eventId: payload.id,
|
|
56
|
+
amount: payload.data.amount,
|
|
57
|
+
}),
|
|
58
|
+
}),
|
|
59
|
+
],
|
|
60
|
+
run: async (input) => ({ ok: true }),
|
|
61
|
+
});
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Task trigger
|
|
65
|
+
|
|
66
|
+
```ts
|
|
67
|
+
import { cronTrigger, Task } from '@keystrokehq/core';
|
|
68
|
+
import { z } from 'zod';
|
|
69
|
+
import { reminderAgent } from './reminder.agent';
|
|
70
|
+
|
|
71
|
+
const dailyReminderTrigger = cronTrigger({
|
|
72
|
+
name: 'Daily Reminder Trigger',
|
|
73
|
+
description: 'Runs every morning.',
|
|
74
|
+
input: z.object({
|
|
75
|
+
mode: z.literal('daily'),
|
|
76
|
+
}),
|
|
77
|
+
payload: {
|
|
78
|
+
mode: 'daily',
|
|
79
|
+
},
|
|
80
|
+
schedule: '0 9 * * *',
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
export const dailyReminderTask = new Task({
|
|
84
|
+
id: 'daily-reminder-task',
|
|
85
|
+
name: 'Daily Reminder Task',
|
|
86
|
+
agent: reminderAgent,
|
|
87
|
+
prompt: 'Send the daily reminder for trigger {{trigger.name}}.',
|
|
88
|
+
triggers: [dailyReminderTrigger],
|
|
89
|
+
});
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## Authoring model
|
|
93
|
+
|
|
94
|
+
Teach this mental model clearly:
|
|
95
|
+
- a trigger defines how external input enters a workflow or task
|
|
96
|
+
- triggers are authored separately from workflows, tasks, and agents
|
|
97
|
+
- workflow triggers are listed in `Workflow({ triggers: [...] })`
|
|
98
|
+
- a bare trigger in the array means the trigger payload passes through directly as workflow input
|
|
99
|
+
- calling the trigger as a function with `{ transform }` creates a bound trigger for payload-to-input mapping
|
|
100
|
+
- task triggers are listed inline in `Task.triggers`
|
|
101
|
+
- a `MessagingGateway` is not a trigger
|
|
102
|
+
|
|
103
|
+
## Trigger types
|
|
104
|
+
|
|
105
|
+
Teach these public trigger factory functions from `@keystrokehq/core`:
|
|
106
|
+
- `cronTrigger`
|
|
107
|
+
- `webhookTrigger`
|
|
108
|
+
- `pollingTrigger`
|
|
109
|
+
- `providerTrigger`
|
|
110
|
+
|
|
111
|
+
Default to cron, webhook, and polling first. Use provider triggers when the user specifically needs provider-event authoring.
|
|
112
|
+
|
|
113
|
+
The factories return a `CallableTrigger` — an object with trigger properties (`.name`, `.toManifest()`, etc.) that is also callable as a function to create a bound trigger with optional `transform` and `filter`. Webhook and polling triggers also expose `.filter` and `.idempotencyKey` callbacks; cron triggers do not.
|
|
114
|
+
|
|
115
|
+
## Manual API Execution
|
|
116
|
+
|
|
117
|
+
Even without a trigger, every workflow and agent can be executed on-demand via the Keystroke API.
|
|
118
|
+
|
|
119
|
+
### Executing a Workflow
|
|
120
|
+
Users can explicitly start a workflow run without needing a cron or webhook trigger.
|
|
121
|
+
- **Endpoint**: `POST /api/v1/workflows/execute`
|
|
122
|
+
- **Body requirements**: Needs `projectId`, `authoredWorkflowId`, and the input payload in an `args` array. May optionally include `workflowGlobals` and `credentialBindings`.
|
|
123
|
+
|
|
124
|
+
### Executing an Agent Conversation
|
|
125
|
+
Users can start a new agent conversation directly.
|
|
126
|
+
- **Endpoint**: `POST /api/v1/agents/[agentId]/conversations`
|
|
127
|
+
- **Body requirements**: Needs a `title`.
|
|
128
|
+
- **Response**: Returns the `conversationId` for subsequent interactions.
|
|
129
|
+
|
|
130
|
+
## Workflow triggers
|
|
131
|
+
|
|
132
|
+
Use workflow triggers when:
|
|
133
|
+
- a schedule should start a workflow
|
|
134
|
+
- a webhook should resolve into workflow input
|
|
135
|
+
- polling should feed workflow input
|
|
136
|
+
- the automation needs durable orchestration after the trigger fires
|
|
137
|
+
|
|
138
|
+
Workflow trigger rules:
|
|
139
|
+
- define the trigger in `*.trigger.ts`
|
|
140
|
+
- list it in `Workflow({ triggers: [...] })`
|
|
141
|
+
- for a bare trigger (no transform), place it directly: `triggers: [myTrigger]`
|
|
142
|
+
- for a bound trigger with transform, call it: `triggers: [myTrigger({ transform: (payload) => ({...}) })]`
|
|
143
|
+
- test the mapping by creating a bound trigger and calling `bound.transform?.(payload)`
|
|
144
|
+
|
|
145
|
+
## Task triggers
|
|
146
|
+
|
|
147
|
+
Use task triggers when:
|
|
148
|
+
- the real job is "trigger -> prompt -> agent run"
|
|
149
|
+
- the user does not need a full workflow orchestration layer
|
|
150
|
+
|
|
151
|
+
Task trigger rules:
|
|
152
|
+
- define the trigger normally
|
|
153
|
+
- list it in `Task.triggers`
|
|
154
|
+
- send users to the task skill for prompt and lifecycle details
|
|
155
|
+
|
|
156
|
+
## Trigger rules
|
|
157
|
+
|
|
158
|
+
- Keep each exported trigger in its own `*.trigger.ts` file.
|
|
159
|
+
- Use `verify` for authenticity or admission checks (webhook triggers only).
|
|
160
|
+
- Use `filter` for event gating (webhook and polling triggers only — cron triggers do not support `filter`).
|
|
161
|
+
- Use `idempotencyKey` for deduplication or stable identity (webhook and polling triggers only — cron triggers do not support `idempotencyKey`).
|
|
162
|
+
- `filter` and `idempotencyKey` receive the typed parsed payload as the first argument. Only `verify` receives credentials.
|
|
163
|
+
- For webhook providers, verify against the raw request data the runtime gives you.
|
|
164
|
+
- Keep the webhook path rooted with `/` and provide only the suffix that follows the organization id in the URL — e.g. `path: '/payments'` is served at `/api/v1/webhooks/{orgId}/payments`. Do **not** include the `/webhooks/` prefix; the router supplies it.
|
|
165
|
+
|
|
166
|
+
## Agent Guidelines for Custom Triggers
|
|
167
|
+
|
|
168
|
+
When an agent needs to write custom triggers, it must follow these rules:
|
|
169
|
+
1. **Always use prebuilt triggers** if they exist before writing custom ones.
|
|
170
|
+
2. **Collect context first**: Do you have all the information you need from the user to build the trigger? If not, ask the user to clarify what they are looking for. **Do Not Guess**.
|
|
171
|
+
3. **Understand API payloads**: If the trigger handles webhooks or fetches from an API endpoint, search the provider's docs to understand the payloads. If possible, hit available endpoints to inspect the actual payloads.
|
|
172
|
+
4. **Always write and run tests**: You must always write tests for custom triggers that handle non-trivial logic in callbacks. Always run the tests to verify that the new triggers run (see `references/testing.md`).
|
|
173
|
+
5. **Handle missing credentials**: If you cannot run tests because of missing credentials, ask the user to configure them following the `../keystroke-credential-binding/SKILL.md` skill. The user will need to upload credentials before deploying anyway.
|
|
174
|
+
|
|
175
|
+
## Testing path
|
|
176
|
+
|
|
177
|
+
Default trigger testing guidance:
|
|
178
|
+
- test trigger-specific callbacks such as `verify`, `filter`, or `poll` directly on the trigger
|
|
179
|
+
- test webhook parsing with `trigger.payload.parse(JSON.parse(request.rawBody))`
|
|
180
|
+
- test polling validation with `trigger.parseResponse(response)`
|
|
181
|
+
- test workflow input mapping by creating a bound trigger and calling `bound.transform?.(payload)`
|
|
182
|
+
- keep broader workflow testing in the workflow skill
|
|
183
|
+
|
|
184
|
+
## References
|
|
185
|
+
|
|
186
|
+
Read these files as needed:
|
|
187
|
+
- `references/source-map.md` for the public trigger surface
|
|
188
|
+
- `references/patterns.md` for field-by-field examples, including provider triggers
|
|
189
|
+
- `references/testing.md` for trigger and bound trigger tests
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"skill_name": "keystroke-trigger-authoring",
|
|
3
|
+
"evals": [
|
|
4
|
+
{
|
|
5
|
+
"id": 1,
|
|
6
|
+
"prompt": "How do I create a Keystroke webhook trigger for a Stripe event and map the incoming payload into my workflow input?",
|
|
7
|
+
"expected_output": "Explains webhookTrigger() factory setup, required and optional fields, and the bound trigger transform pattern via Workflow({ triggers: [trigger({ transform })] }).",
|
|
8
|
+
"files": []
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
"id": 2,
|
|
12
|
+
"prompt": "I need a polling trigger that checks an external API every 15 minutes and only launches the workflow for new items. What should that look like?",
|
|
13
|
+
"expected_output": "Explains pollingTrigger() factory setup, schedule, poll/response, optional filter and idempotency behavior, and listing the trigger in Workflow({ triggers: [...] }) with optional transform binding.",
|
|
14
|
+
"files": []
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
"id": 3,
|
|
18
|
+
"prompt": "What is the right way to test a Keystroke trigger? I want to validate webhook verify/filter logic and the input transform into the workflow.",
|
|
19
|
+
"expected_output": "Points to trigger-specific callback tests (verify, filter, payload.parse) plus bound trigger transform testing via bound.transform?.(payload).",
|
|
20
|
+
"files": []
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
"id": 4,
|
|
24
|
+
"prompt": "I want a cron-driven agent job. Do I attach a trigger to the agent, attach it to a task, or something else?",
|
|
25
|
+
"expected_output": "Explains that workflow triggers are listed in Workflow({ triggers: [...] }), task triggers are listed inline on Task.triggers, and messaging gateways are not triggers.",
|
|
26
|
+
"files": []
|
|
27
|
+
}
|
|
28
|
+
]
|
|
29
|
+
}
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
# Trigger Examples
|
|
2
|
+
|
|
3
|
+
Use these examples when the user wants concrete trigger patterns.
|
|
4
|
+
|
|
5
|
+
Assume `digestWorkflow` and `paymentWorkflow` are `Workflow` instances whose `input` schemas match the trigger examples below.
|
|
6
|
+
|
|
7
|
+
File layout reminder:
|
|
8
|
+
|
|
9
|
+
- keep workflows in `*.workflow.ts` files
|
|
10
|
+
- keep each exported trigger in its own `*.trigger.ts` file
|
|
11
|
+
- keep any credential set used by a trigger in its own `*.credential-set.ts` file
|
|
12
|
+
|
|
13
|
+
## Shared advanced fields
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
import type { ExecutionIdentityPolicy } from '@keystrokehq/core/types';
|
|
17
|
+
|
|
18
|
+
const executionIdentityPolicy: ExecutionIdentityPolicy = {
|
|
19
|
+
subjectMode: 'requiredWhenUserProvidedCredential',
|
|
20
|
+
};
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Use `executionIdentityPolicy` when the trigger should require a subject only in specific credential cases. Use `modeDefault` when the trigger should default to a specific trigger mode such as `'subscribable'`.
|
|
24
|
+
|
|
25
|
+
## `cronTrigger`
|
|
26
|
+
|
|
27
|
+
```ts
|
|
28
|
+
import { cronTrigger } from '@keystrokehq/core';
|
|
29
|
+
import { z } from 'zod';
|
|
30
|
+
|
|
31
|
+
export const nightlyDigestTrigger = cronTrigger({
|
|
32
|
+
name: 'Nightly Digest Trigger',
|
|
33
|
+
description: 'Runs every night.',
|
|
34
|
+
enabled: true,
|
|
35
|
+
modeDefault: 'subscribable',
|
|
36
|
+
executionIdentityPolicy,
|
|
37
|
+
input: z.object({
|
|
38
|
+
mode: z.literal('nightly'),
|
|
39
|
+
}),
|
|
40
|
+
payload: {
|
|
41
|
+
mode: 'nightly',
|
|
42
|
+
},
|
|
43
|
+
schedule: '0 0 * * *',
|
|
44
|
+
timezone: 'UTC',
|
|
45
|
+
});
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Use this when the workflow input can be defined as static payload on a schedule.
|
|
49
|
+
|
|
50
|
+
A cron trigger can be placed bare in the `triggers` array when its payload matches the workflow input:
|
|
51
|
+
|
|
52
|
+
```ts
|
|
53
|
+
export const digestWorkflow = new Workflow({
|
|
54
|
+
id: 'digest',
|
|
55
|
+
name: 'Digest',
|
|
56
|
+
input: z.object({ mode: z.literal('nightly') }),
|
|
57
|
+
output: z.object({ ok: z.boolean() }),
|
|
58
|
+
triggers: [nightlyDigestTrigger],
|
|
59
|
+
run: async () => ({ ok: true }),
|
|
60
|
+
});
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## `webhookTrigger`
|
|
64
|
+
|
|
65
|
+
```ts
|
|
66
|
+
import { webhookTrigger } from '@keystrokehq/core';
|
|
67
|
+
import { z } from 'zod';
|
|
68
|
+
|
|
69
|
+
export const paymentWebhook = webhookTrigger({
|
|
70
|
+
name: 'Payment Webhook',
|
|
71
|
+
description: 'Handles payment events.',
|
|
72
|
+
enabled: true,
|
|
73
|
+
modeDefault: 'subscribable',
|
|
74
|
+
source: {
|
|
75
|
+
type: 'custom',
|
|
76
|
+
method: 'POST',
|
|
77
|
+
path: '/payments',
|
|
78
|
+
verify: async (request) => {
|
|
79
|
+
if (!request.headers['x-signature']) {
|
|
80
|
+
throw new Error('Missing signature header');
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
response: {
|
|
84
|
+
successStatus: 202,
|
|
85
|
+
successBody: {
|
|
86
|
+
accepted: true,
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
},
|
|
90
|
+
payload: z.object({
|
|
91
|
+
id: z.string(),
|
|
92
|
+
type: z.string(),
|
|
93
|
+
amount: z.number(),
|
|
94
|
+
}),
|
|
95
|
+
filter: (payload) => payload.type === 'payment.completed',
|
|
96
|
+
idempotencyKey: (payload) => payload.id,
|
|
97
|
+
});
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Use `request.rawBody` in `verify` when a provider expects raw request verification. To parse a webhook body, use `trigger.payload.parse(JSON.parse(request.rawBody))`.
|
|
101
|
+
|
|
102
|
+
## Binding a webhook trigger to a workflow with `transform`
|
|
103
|
+
|
|
104
|
+
When the webhook payload shape differs from the workflow input, call the trigger with a `transform`:
|
|
105
|
+
|
|
106
|
+
```ts
|
|
107
|
+
export const paymentWorkflow = new Workflow({
|
|
108
|
+
id: 'payment-workflow',
|
|
109
|
+
name: 'Payment Workflow',
|
|
110
|
+
input: z.object({ eventId: z.string(), amount: z.number() }),
|
|
111
|
+
output: z.object({ ok: z.boolean() }),
|
|
112
|
+
triggers: [
|
|
113
|
+
paymentWebhook({
|
|
114
|
+
transform: (payload) => ({
|
|
115
|
+
eventId: payload.id,
|
|
116
|
+
amount: payload.amount,
|
|
117
|
+
}),
|
|
118
|
+
}),
|
|
119
|
+
],
|
|
120
|
+
run: async () => ({ ok: true }),
|
|
121
|
+
});
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## `pollingTrigger`
|
|
125
|
+
|
|
126
|
+
```ts
|
|
127
|
+
import { pollingTrigger } from '@keystrokehq/core';
|
|
128
|
+
import { z } from 'zod';
|
|
129
|
+
|
|
130
|
+
export const orderPolling = pollingTrigger({
|
|
131
|
+
name: 'Order Polling',
|
|
132
|
+
description: 'Polls for the newest order.',
|
|
133
|
+
enabled: true,
|
|
134
|
+
schedule: '*/15 * * * *',
|
|
135
|
+
response: z.object({
|
|
136
|
+
orderId: z.string(),
|
|
137
|
+
status: z.string(),
|
|
138
|
+
}),
|
|
139
|
+
poll: async (ctx) => {
|
|
140
|
+
const previousOrder = ctx.lastResponse?.orderId ?? 'none';
|
|
141
|
+
|
|
142
|
+
return {
|
|
143
|
+
orderId: previousOrder === 'none' ? 'order_123' : 'order_124',
|
|
144
|
+
status: 'created',
|
|
145
|
+
};
|
|
146
|
+
},
|
|
147
|
+
filter: (payload) => payload.status === 'created',
|
|
148
|
+
idempotencyKey: (payload) => payload.orderId,
|
|
149
|
+
});
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
Use this when the workflow should be entered from periodic remote-state checks.
|
|
153
|
+
|
|
154
|
+
## `pollingTrigger.parseResponse(...)`
|
|
155
|
+
|
|
156
|
+
```ts
|
|
157
|
+
const parsedResponse = orderPolling.parseResponse({
|
|
158
|
+
orderId: 'order_123',
|
|
159
|
+
status: 'created',
|
|
160
|
+
});
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
Use `parseResponse(...)` when you want the trigger's response schema validation without running polling logic.
|
|
164
|
+
|
|
165
|
+
## `providerTrigger`
|
|
166
|
+
|
|
167
|
+
```ts
|
|
168
|
+
import { providerTrigger } from '@keystrokehq/core';
|
|
169
|
+
|
|
170
|
+
export const githubIssueTrigger = providerTrigger({
|
|
171
|
+
name: 'GitHub Issue Trigger',
|
|
172
|
+
description: 'Receives GitHub provider events for issue activity.',
|
|
173
|
+
provider: 'github',
|
|
174
|
+
eventTypes: ['issues.opened', 'issues.edited'],
|
|
175
|
+
filter: async (event) => event.type === 'issues.opened',
|
|
176
|
+
idempotencyKey: async (event) => event.id,
|
|
177
|
+
});
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
Use a provider trigger when the user is authoring around normalized provider events instead of a raw webhook or polling loop.
|
|
181
|
+
|
|
182
|
+
## Bare trigger in workflow (no transform)
|
|
183
|
+
|
|
184
|
+
```ts
|
|
185
|
+
triggers: [nightlyDigestTrigger]
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
Use this when the trigger payload already matches the workflow input. The trigger is placed directly in the array.
|
|
189
|
+
|
|
190
|
+
## Bound trigger with `transform`
|
|
191
|
+
|
|
192
|
+
```ts
|
|
193
|
+
triggers: [
|
|
194
|
+
paymentWebhook({
|
|
195
|
+
transform: (payload) => ({
|
|
196
|
+
eventId: payload.id,
|
|
197
|
+
amount: payload.amount,
|
|
198
|
+
}),
|
|
199
|
+
}),
|
|
200
|
+
]
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
Call the trigger as a function with `{ transform }` when the trigger payload and workflow input are different shapes. This returns a `BoundTrigger`.
|
|
204
|
+
|
|
205
|
+
## Bound trigger with additional `filter`
|
|
206
|
+
|
|
207
|
+
```ts
|
|
208
|
+
triggers: [
|
|
209
|
+
paymentWebhook({
|
|
210
|
+
filter: (payload) => payload.amount > 100,
|
|
211
|
+
transform: (payload) => ({
|
|
212
|
+
eventId: payload.id,
|
|
213
|
+
amount: payload.amount,
|
|
214
|
+
}),
|
|
215
|
+
}),
|
|
216
|
+
]
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
An additional filter on the binding composes with the trigger's own filter.
|
|
220
|
+
|
|
221
|
+
## Task trigger note
|
|
222
|
+
|
|
223
|
+
For task-driven agent runs, author the trigger normally and place it in `Task.triggers`.
|
|
224
|
+
|
|
225
|
+
Use the task skill when the user needs prompt templating, task lifecycle, or focused task deploys.
|
|
226
|
+
|
|
227
|
+
## Trigger credentials
|
|
228
|
+
|
|
229
|
+
```ts
|
|
230
|
+
import { CredentialSet, webhookTrigger } from '@keystrokehq/core';
|
|
231
|
+
import { z } from 'zod';
|
|
232
|
+
|
|
233
|
+
const signingCredentials = new CredentialSet({
|
|
234
|
+
id: 'webhookSigning',
|
|
235
|
+
auth: z.object({
|
|
236
|
+
secret: z.string(),
|
|
237
|
+
}),
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
const signedWebhook = webhookTrigger({
|
|
241
|
+
name: 'Signed Webhook',
|
|
242
|
+
description: 'Verifies a signed webhook.',
|
|
243
|
+
source: {
|
|
244
|
+
type: 'custom',
|
|
245
|
+
method: 'POST',
|
|
246
|
+
path: '/signed',
|
|
247
|
+
credentialSets: [signingCredentials],
|
|
248
|
+
verify: async (_request, ctx) => {
|
|
249
|
+
if (!ctx.credentials.webhookSigning.secret) {
|
|
250
|
+
throw new Error('Missing signing secret');
|
|
251
|
+
}
|
|
252
|
+
},
|
|
253
|
+
},
|
|
254
|
+
payload: z.object({
|
|
255
|
+
id: z.string(),
|
|
256
|
+
}),
|
|
257
|
+
});
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
## `describe()` and `toManifest()`
|
|
261
|
+
|
|
262
|
+
```ts
|
|
263
|
+
const triggerSummary = paymentWebhook.describe();
|
|
264
|
+
const triggerManifest = paymentWebhook.toManifest();
|
|
265
|
+
```
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
# Trigger Feature Map
|
|
2
|
+
|
|
3
|
+
Use only the public imports a user repo can rely on:
|
|
4
|
+
|
|
5
|
+
```ts
|
|
6
|
+
import {
|
|
7
|
+
cronTrigger,
|
|
8
|
+
pollingTrigger,
|
|
9
|
+
providerTrigger,
|
|
10
|
+
webhookTrigger,
|
|
11
|
+
} from '@keystrokehq/core';
|
|
12
|
+
import type {
|
|
13
|
+
BoundTrigger,
|
|
14
|
+
CallableTrigger,
|
|
15
|
+
ExecutionIdentityPolicy,
|
|
16
|
+
TriggerBindOptions,
|
|
17
|
+
TriggerEntry,
|
|
18
|
+
TriggerInstance,
|
|
19
|
+
} from '@keystrokehq/core/types';
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
When a trigger explanation also needs to talk about workflow steps or agent tools, use the terminology from the other Keystroke skills:
|
|
23
|
+
|
|
24
|
+
- `Step`, `Tool`, and `Operation` are aliases for the same `Operation` class
|
|
25
|
+
- use the workflow skill for workflow-side operation guidance
|
|
26
|
+
- use the agent skill for agent-side tool guidance
|
|
27
|
+
|
|
28
|
+
## Common trigger fields
|
|
29
|
+
|
|
30
|
+
- `name`
|
|
31
|
+
- `description`
|
|
32
|
+
- `enabled`
|
|
33
|
+
- `credentialSets`
|
|
34
|
+
- `executionIdentityPolicy`
|
|
35
|
+
- `modeDefault`
|
|
36
|
+
|
|
37
|
+
## Common trigger methods
|
|
38
|
+
|
|
39
|
+
- `describe()`
|
|
40
|
+
- `toManifest()`
|
|
41
|
+
|
|
42
|
+
## `cronTrigger` fields
|
|
43
|
+
|
|
44
|
+
- `input`
|
|
45
|
+
- `payload`
|
|
46
|
+
- `schedule`
|
|
47
|
+
- `timezone`
|
|
48
|
+
|
|
49
|
+
## `cronTrigger` instance properties
|
|
50
|
+
|
|
51
|
+
- `.name`
|
|
52
|
+
- `.payload`
|
|
53
|
+
- `.schedule`
|
|
54
|
+
- `.timezone`
|
|
55
|
+
- `.toManifest()`
|
|
56
|
+
- `.describe()`
|
|
57
|
+
|
|
58
|
+
## `webhookTrigger` fields
|
|
59
|
+
|
|
60
|
+
- `path`
|
|
61
|
+
- `method`
|
|
62
|
+
- `payload`
|
|
63
|
+
- `verify`
|
|
64
|
+
- `filter`
|
|
65
|
+
- `idempotencyKey`
|
|
66
|
+
- `response`
|
|
67
|
+
|
|
68
|
+
## `webhookTrigger` instance methods
|
|
69
|
+
|
|
70
|
+
- `.payload.parse(data)` — validate parsed body against the payload schema
|
|
71
|
+
- `.verify?(request, ctx)` — raw request + credentials for authenticity checks
|
|
72
|
+
- `.filter?(payload, request?)` — typed parsed body, optional raw request (no credentials)
|
|
73
|
+
- `.idempotencyKey?(payload, request?)` — typed parsed body, optional raw request (no credentials)
|
|
74
|
+
|
|
75
|
+
## `pollingTrigger` fields
|
|
76
|
+
|
|
77
|
+
- `schedule`
|
|
78
|
+
- `response`
|
|
79
|
+
- `poll`
|
|
80
|
+
- `filter`
|
|
81
|
+
- `idempotencyKey`
|
|
82
|
+
|
|
83
|
+
## `pollingTrigger` instance methods
|
|
84
|
+
|
|
85
|
+
- `.poll(ctx)`
|
|
86
|
+
- `.parseResponse(response)`
|
|
87
|
+
- `.filter?(payload)` — typed parsed payload (no credentials)
|
|
88
|
+
- `.idempotencyKey?(payload)` — typed parsed payload (no credentials)
|
|
89
|
+
|
|
90
|
+
## `providerTrigger` fields
|
|
91
|
+
|
|
92
|
+
- `provider`
|
|
93
|
+
- `eventTypes`
|
|
94
|
+
- `appRef`
|
|
95
|
+
- `filter`
|
|
96
|
+
- `idempotencyKey`
|
|
97
|
+
|
|
98
|
+
## Calling a trigger (creating a bound trigger)
|
|
99
|
+
|
|
100
|
+
All factory-created triggers are callable. Calling one returns a `BoundTrigger`:
|
|
101
|
+
|
|
102
|
+
- `trigger()` — bare binding (no transform)
|
|
103
|
+
- `trigger({ transform })` — bound with payload-to-input mapping
|
|
104
|
+
- `trigger({ filter })` — bound with additional filter
|
|
105
|
+
- `trigger({ transform, filter })` — bound with both
|
|
106
|
+
|
|
107
|
+
## `BoundTrigger`
|
|
108
|
+
|
|
109
|
+
- `.isBoundTrigger` — always `true`
|
|
110
|
+
- `.trigger` — reference to the underlying trigger instance
|
|
111
|
+
- `.filter?` — composed filter (trigger's filter + binding filter)
|
|
112
|
+
- `.transform?` — payload-to-workflow-input mapping function
|
|
113
|
+
|
|
114
|
+
## Workflow `triggers` array
|
|
115
|
+
|
|
116
|
+
`Workflow({ triggers: [...] })` accepts a `TriggerEntry[]`:
|
|
117
|
+
- a bare `CallableTrigger` (trigger payload passes through as workflow input)
|
|
118
|
+
- a `BoundTrigger` (returned by calling the trigger with options)
|
|
119
|
+
|
|
120
|
+
## Task note
|
|
121
|
+
|
|
122
|
+
- task triggers are declared inline in `Task.triggers`
|
|
123
|
+
- messaging gateways belong to agent authoring, not trigger authoring
|
|
124
|
+
|
|
125
|
+
## Where to read next
|
|
126
|
+
|
|
127
|
+
- `patterns.md` for trigger field examples
|
|
128
|
+
- `testing.md` for trigger callback and bound trigger tests
|