@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,265 @@
|
|
|
1
|
+
# Workflow And Step / Operation Examples
|
|
2
|
+
|
|
3
|
+
Use these examples when the user needs a concrete pattern instead of only rules.
|
|
4
|
+
|
|
5
|
+
`Operation`, `Step`, and `Tool` are aliases for the same class. In this workflow reference, examples default to `Step` because that name reads best from workflow context.
|
|
6
|
+
|
|
7
|
+
File layout reminder:
|
|
8
|
+
|
|
9
|
+
- keep every exported primitive in its own typed file
|
|
10
|
+
- `*.workflow.ts` exports one workflow
|
|
11
|
+
- `*.step.ts`, `*.tool.ts`, or `*.operation.ts` exports one operation
|
|
12
|
+
- if a snippet shows multiple primitives together, treat it as a multi-file excerpt and import the reused symbols from their own files
|
|
13
|
+
|
|
14
|
+
## Minimal workflow
|
|
15
|
+
|
|
16
|
+
```ts
|
|
17
|
+
import { Workflow } from '@keystrokehq/core';
|
|
18
|
+
import { z } from 'zod';
|
|
19
|
+
|
|
20
|
+
export const helloWorkflow = new Workflow({
|
|
21
|
+
id: 'hello-workflow',
|
|
22
|
+
name: 'Hello Workflow',
|
|
23
|
+
description: 'Returns a greeting.',
|
|
24
|
+
input: z.object({
|
|
25
|
+
name: z.string(),
|
|
26
|
+
}),
|
|
27
|
+
output: z.object({
|
|
28
|
+
message: z.string(),
|
|
29
|
+
}),
|
|
30
|
+
run: async (input) => {
|
|
31
|
+
return {
|
|
32
|
+
message: `Hello, ${input.name}!`,
|
|
33
|
+
};
|
|
34
|
+
},
|
|
35
|
+
});
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
In normal Keystroke usage, deploy the workflow and enter it through triggers, endpoints, or the UI. Direct `workflow.run(...)` calls are mainly for tests and local authoring.
|
|
39
|
+
|
|
40
|
+
Author workflows as top-level exports like this. Do not hide workflow definitions inside factory functions.
|
|
41
|
+
|
|
42
|
+
## Workflow with triggers
|
|
43
|
+
|
|
44
|
+
```ts
|
|
45
|
+
import { cronTrigger, webhookTrigger, Workflow } from '@keystrokehq/core';
|
|
46
|
+
import { z } from 'zod';
|
|
47
|
+
|
|
48
|
+
const PayloadSchema = z.object({ accountId: z.string() });
|
|
49
|
+
|
|
50
|
+
const accountCron = cronTrigger({
|
|
51
|
+
name: 'Nightly Account Sync',
|
|
52
|
+
schedule: '0 0 * * *',
|
|
53
|
+
input: PayloadSchema,
|
|
54
|
+
payload: { accountId: 'default' },
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
const accountWebhook = webhookTrigger({
|
|
58
|
+
name: 'Account Webhook',
|
|
59
|
+
source: {
|
|
60
|
+
type: 'custom',
|
|
61
|
+
method: 'POST',
|
|
62
|
+
path: '/accounts',
|
|
63
|
+
},
|
|
64
|
+
payload: z.object({ id: z.string(), action: z.string() }),
|
|
65
|
+
filter: (p) => p.action === 'sync',
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
export const accountSyncWorkflow = new Workflow({
|
|
69
|
+
id: 'account-sync',
|
|
70
|
+
name: 'Account Sync',
|
|
71
|
+
input: PayloadSchema,
|
|
72
|
+
output: z.object({ ok: z.boolean() }),
|
|
73
|
+
triggers: [
|
|
74
|
+
accountCron,
|
|
75
|
+
accountWebhook({
|
|
76
|
+
transform: (payload) => ({ accountId: payload.id }),
|
|
77
|
+
}),
|
|
78
|
+
],
|
|
79
|
+
run: async () => ({ ok: true }),
|
|
80
|
+
});
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Bare triggers (like `accountCron` above) pass their payload directly as workflow input. Call the trigger with `{ transform }` when the payload shape differs from the workflow input.
|
|
84
|
+
|
|
85
|
+
## Workflow metadata fields
|
|
86
|
+
|
|
87
|
+
```ts
|
|
88
|
+
import { Workflow } from '@keystrokehq/core';
|
|
89
|
+
import { z } from 'zod';
|
|
90
|
+
|
|
91
|
+
export const versionedWorkflow = new Workflow({
|
|
92
|
+
id: 'billing-digest',
|
|
93
|
+
name: 'Billing Digest',
|
|
94
|
+
description: 'Builds a digest for the billing team.',
|
|
95
|
+
author: 'Keystroke Team',
|
|
96
|
+
version: '1.0.0',
|
|
97
|
+
tags: ['billing', 'digest'],
|
|
98
|
+
timeout: '15m',
|
|
99
|
+
retries: {
|
|
100
|
+
exponential: { attempts: 2, base: 2, multiplier: 2 },
|
|
101
|
+
},
|
|
102
|
+
input: z.object({
|
|
103
|
+
tenantId: z.string(),
|
|
104
|
+
}),
|
|
105
|
+
output: z.object({
|
|
106
|
+
ok: z.boolean(),
|
|
107
|
+
}),
|
|
108
|
+
run: async () => ({ ok: true }),
|
|
109
|
+
});
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## Step with retries and timeout
|
|
113
|
+
|
|
114
|
+
```ts
|
|
115
|
+
import { Step } from '@keystrokehq/core';
|
|
116
|
+
import { z } from 'zod';
|
|
117
|
+
|
|
118
|
+
export const fetchInvoice = new Step({
|
|
119
|
+
name: 'Fetch Invoice',
|
|
120
|
+
description: 'Loads the current invoice.',
|
|
121
|
+
input: z.object({
|
|
122
|
+
invoiceId: z.string(),
|
|
123
|
+
}),
|
|
124
|
+
output: z.object({
|
|
125
|
+
invoiceId: z.string(),
|
|
126
|
+
amount: z.number(),
|
|
127
|
+
}),
|
|
128
|
+
timeout: '2m',
|
|
129
|
+
retries: {
|
|
130
|
+
constant: { attempts: 3, seconds: 30 },
|
|
131
|
+
},
|
|
132
|
+
tags: ['billing'],
|
|
133
|
+
run: async (input) => {
|
|
134
|
+
return {
|
|
135
|
+
invoiceId: input.invoiceId,
|
|
136
|
+
amount: 100,
|
|
137
|
+
};
|
|
138
|
+
},
|
|
139
|
+
});
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
This is the normal place for side effects such as API calls, database writes, or non-deterministic work.
|
|
143
|
+
|
|
144
|
+
The same example could also be written with `new Operation({...})` if the unit is shared outside workflow code.
|
|
145
|
+
|
|
146
|
+
## Step with workflow globals
|
|
147
|
+
|
|
148
|
+
```ts
|
|
149
|
+
import { Step, Workflow } from '@keystrokehq/core';
|
|
150
|
+
import { z } from 'zod';
|
|
151
|
+
|
|
152
|
+
const sendDigest = new Step({
|
|
153
|
+
name: 'Send Digest',
|
|
154
|
+
description: 'Uses workflow globals to decide where to send the digest.',
|
|
155
|
+
workflowGlobals: z.object({
|
|
156
|
+
defaultChannel: z.string(),
|
|
157
|
+
}),
|
|
158
|
+
input: z.object({
|
|
159
|
+
message: z.string(),
|
|
160
|
+
}),
|
|
161
|
+
output: z.object({
|
|
162
|
+
channel: z.string(),
|
|
163
|
+
}),
|
|
164
|
+
run: async (_input, ctx) => {
|
|
165
|
+
return {
|
|
166
|
+
channel: ctx.workflowGlobals.defaultChannel,
|
|
167
|
+
};
|
|
168
|
+
},
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
export const digestWorkflow = new Workflow({
|
|
172
|
+
id: 'digest',
|
|
173
|
+
name: 'Digest',
|
|
174
|
+
input: z.object({}),
|
|
175
|
+
output: z.object({
|
|
176
|
+
channel: z.string(),
|
|
177
|
+
}),
|
|
178
|
+
workflowGlobals: z.object({
|
|
179
|
+
defaultChannel: z.string(),
|
|
180
|
+
}),
|
|
181
|
+
run: async () => sendDigest.run({ message: 'hello' }),
|
|
182
|
+
});
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
## Step with credentials
|
|
186
|
+
|
|
187
|
+
```ts
|
|
188
|
+
import { CredentialSet, Step } from '@keystrokehq/core';
|
|
189
|
+
import { z } from 'zod';
|
|
190
|
+
|
|
191
|
+
const crmCredentials = new CredentialSet({
|
|
192
|
+
id: 'crmApi',
|
|
193
|
+
name: 'CRM API',
|
|
194
|
+
auth: z.object({
|
|
195
|
+
apiKey: z.string(),
|
|
196
|
+
}),
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
export const loadCustomer = new Step({
|
|
200
|
+
name: 'Load Customer',
|
|
201
|
+
description: 'Uses a credential set from step context.',
|
|
202
|
+
credentialSets: [crmCredentials],
|
|
203
|
+
input: z.object({
|
|
204
|
+
customerId: z.string(),
|
|
205
|
+
}),
|
|
206
|
+
output: z.object({
|
|
207
|
+
customerId: z.string(),
|
|
208
|
+
usedKey: z.string(),
|
|
209
|
+
}),
|
|
210
|
+
run: async (input, ctx) => {
|
|
211
|
+
return {
|
|
212
|
+
customerId: input.customerId,
|
|
213
|
+
usedKey: ctx.credentials.crmApi.apiKey,
|
|
214
|
+
};
|
|
215
|
+
},
|
|
216
|
+
});
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
## Parallel orchestration
|
|
220
|
+
|
|
221
|
+
```ts
|
|
222
|
+
const [a, b] = await Promise.all([
|
|
223
|
+
fetchInvoice.run({ invoiceId: 'inv_1' }),
|
|
224
|
+
fetchInvoice.run({ invoiceId: 'inv_2' }),
|
|
225
|
+
]);
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
Use `Promise.all`, `Promise.race`, loops, and branches in the workflow. Keep network calls, database calls, and other side effects inside operations used as steps.
|
|
229
|
+
|
|
230
|
+
## Child workflow composition
|
|
231
|
+
|
|
232
|
+
```ts
|
|
233
|
+
const prepareInvoice = new Workflow({
|
|
234
|
+
id: 'prepare-invoice',
|
|
235
|
+
name: 'Prepare Invoice',
|
|
236
|
+
input: z.object({ invoiceId: z.string() }),
|
|
237
|
+
output: z.object({ invoiceId: z.string() }),
|
|
238
|
+
run: async (input) => ({ invoiceId: input.invoiceId }),
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
const invoicePipeline = new Workflow({
|
|
242
|
+
id: 'invoice-pipeline',
|
|
243
|
+
name: 'Invoice Pipeline',
|
|
244
|
+
input: z.object({ invoiceId: z.string() }),
|
|
245
|
+
output: z.object({ invoiceId: z.string() }),
|
|
246
|
+
run: async (input) => {
|
|
247
|
+
return prepareInvoice.run(input);
|
|
248
|
+
},
|
|
249
|
+
});
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
## `withTimeout(...)` and `retry(...)`
|
|
253
|
+
|
|
254
|
+
```ts
|
|
255
|
+
const fastHelloWorkflow = helloWorkflow.withTimeout('30s');
|
|
256
|
+
const retriedHelloWorkflow = helloWorkflow.retry({
|
|
257
|
+
constant: { attempts: 2, seconds: 5 },
|
|
258
|
+
});
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
## Replay-safe reminder
|
|
262
|
+
|
|
263
|
+
- keep orchestration in `Workflow.run`
|
|
264
|
+
- keep external effects in `Step.run` or `Operation.run`
|
|
265
|
+
- keep random values and changing state out of the workflow body
|