@keystrokehq/cli 0.1.38 → 0.2.0
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/{dist-DkLbeW8l.mjs → dist-BOhrc_Nv.mjs} +198 -561
- package/dist/dist-BOhrc_Nv.mjs.map +1 -0
- package/dist/{dist-B6z1wti6.mjs → dist-D-cLLjHv.mjs} +87 -2017
- package/dist/dist-D-cLLjHv.mjs.map +1 -0
- package/dist/{dist-GSI9JDuz.mjs → dist-DGKF3FGu.mjs} +31 -265
- package/dist/dist-DGKF3FGu.mjs.map +1 -0
- package/dist/dist-DMuIdus5.mjs +3 -0
- package/dist/{dist-gAvgHBlr.mjs → dist-Re6HHSqz.mjs} +2 -2
- package/dist/{dist-gAvgHBlr.mjs.map → dist-Re6HHSqz.mjs.map} +1 -1
- package/dist/index.mjs +177 -463
- package/dist/index.mjs.map +1 -1
- package/dist/{maybe-auto-update-Dv4MJvWb.mjs → maybe-auto-update-q5MthdI8.mjs} +2 -2
- package/dist/{maybe-auto-update-Dv4MJvWb.mjs.map → maybe-auto-update-q5MthdI8.mjs.map} +1 -1
- package/dist/skills-bundle/_AGENTS.mcp.md +5 -9
- package/dist/skills-bundle/_AGENTS.md +112 -243
- package/dist/skills-bundle/skills/keystroke-actions/SKILL.md +160 -0
- package/dist/skills-bundle/skills/keystroke-actions/references/catalog-and-imports.md +71 -0
- package/dist/skills-bundle/skills/keystroke-agents/SKILL.md +115 -0
- package/dist/skills-bundle/skills/keystroke-agents/references/models.md +23 -0
- package/dist/skills-bundle/skills/keystroke-agents/references/tools-mcp-codemode.md +73 -0
- package/dist/skills-bundle/skills/keystroke-agents/references/workflows-and-testing.md +26 -0
- package/dist/skills-bundle/skills/keystroke-apps/SKILL.md +151 -0
- package/dist/skills-bundle/skills/keystroke-apps/references/cli-and-catalog.md +104 -0
- package/dist/skills-bundle/skills/keystroke-channels/SKILL.md +66 -0
- package/dist/skills-bundle/skills/keystroke-channels/references/slack-setup.md +41 -0
- package/dist/skills-bundle/skills/keystroke-cli/SKILL.md +93 -0
- package/dist/skills-bundle/skills/keystroke-deploy/SKILL.md +93 -0
- package/dist/skills-bundle/skills/keystroke-deploy/references/build-and-full-deploy.md +30 -0
- package/dist/skills-bundle/skills/keystroke-deploy/references/filtered-deploy.md +50 -0
- package/dist/skills-bundle/skills/keystroke-deploy/references/wip-ignore.md +35 -0
- package/dist/skills-bundle/skills/keystroke-files/SKILL.md +43 -0
- package/dist/skills-bundle/skills/keystroke-skills/SKILL.md +42 -0
- package/dist/skills-bundle/skills/keystroke-triggers/SKILL.md +143 -0
- package/dist/skills-bundle/skills/keystroke-workflows/SKILL.md +78 -0
- package/dist/skills-bundle/skills/keystroke-workflows/references/authoring.md +168 -0
- package/dist/skills-bundle/skills/keystroke-workflows/references/testing.md +138 -0
- package/dist/templates/hello-world/.env.example +4 -0
- package/dist/templates/hello-world/README.md +3 -4
- package/dist/templates/hello-world/package.json +0 -1
- package/dist/templates/hello-world/src/actions/greet.ts +0 -1
- package/dist/templates/hello-world/src/agents/hello.ts +0 -2
- package/dist/templates/hello-world/src/workflows/greeting.ts +0 -1
- package/dist/{version-CiFlKPyE.mjs → version-DcR3O1UD.mjs} +3 -2
- package/dist/version-DcR3O1UD.mjs.map +1 -0
- package/package.json +5 -5
- package/dist/dist-B6z1wti6.mjs.map +0 -1
- package/dist/dist-CjWXZCN7.mjs +0 -3
- package/dist/dist-DkLbeW8l.mjs.map +0 -1
- package/dist/dist-GSI9JDuz.mjs.map +0 -1
- package/dist/version-CiFlKPyE.mjs.map +0 -1
|
@@ -1,292 +1,144 @@
|
|
|
1
1
|
# keystroke
|
|
2
2
|
|
|
3
|
-
This codebase is a Keystroke project.
|
|
3
|
+
This codebase is a Keystroke project.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
> Keystroke is a code-first AI automation platform. With it, anyone can build specialized AI agents and workflow automations in TypeScript, then deploy them to a managed cloud platform where they run.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
- **Build agents and workflows:** Build agents and workflows with an open TypeScript framework. Run tests, connect integrations, and maintain what you build over time.
|
|
8
|
+
- **Connect to thousands of integrations:** Keystroke has over 1,000 built-in integrations (Slack, Google, Linear, and hundreds more). It is also extremely easy to quickly build a connection for any HTTP API (including private internal APIs) or MCP server.
|
|
9
|
+
- **Deploy what you build:** The Keystroke platform gives teams a managed workspace to run what they build. Deploy agents and workflows from your project. Then, view workflows on an interactive canvas, chat with agents in a chat interface, inspect runs, manage credentials, and collaborate from a shared web platform.
|
|
8
10
|
|
|
9
|
-
|
|
11
|
+
## Keystroke projects
|
|
10
12
|
|
|
11
|
-
|
|
12
|
-
- **Narrate in outcomes, not plumbing.** Default to "I'll set this to run every morning and message you a summary", not "deploying the cron trigger to the runtime." Don't surface internal concepts (build artifacts, event logs, correlation IDs, filtered deploys) unless the user has to make a decision about one.
|
|
13
|
-
- **Don't hand them the work.** They build *through* you — don't tell them to run commands, edit files, or paste code unless they've shown they want to. Do it yourself, do it quickly, and report the result.
|
|
14
|
-
- **Simplify the talk, never the work.** Speaking plainly doesn't lower the bar — follow every invariant in this guide regardless of how the user communicates.
|
|
15
|
-
- **If the end-state isn't clear, just ask.** It's okay to clarify how the thing you're building should be built, which integrations to use, etc.
|
|
13
|
+
In Keystroke, everything you build belongs to a project. A project is a shared space for building and running your agents, workflows, triggers, and actions. Think of a project as the unit you build, deploy, give credentials to, invite teammates to, and inspect run history for.
|
|
16
14
|
|
|
17
|
-
|
|
15
|
+
Keystroke projects use two related ideas:
|
|
18
16
|
|
|
19
|
-
|
|
17
|
+
- **Local codebase**: a local directory (this codebase) with `keystroke.config.ts` and `src/` (`keystroke init` scaffolds this). Run it locally with `keystroke start` / `keystroke dev`; no platform project required.
|
|
18
|
+
- **Platform project**: an org-owned cloud record: a deploy target plus a hosted runtime (its own keystroke server, URL, managed credentials). Projects are inactive until their first deploy.
|
|
20
19
|
|
|
21
|
-
|
|
22
|
-
| --- | --- | --- |
|
|
23
|
-
| **Local codebase** | This directory — `keystroke.config.ts` + `src/`. Source of truth. | Your machine / Git |
|
|
24
|
-
| **Platform project** | Org-owned cloud runtime (its own server, URL, credentials, run history). Inactive until first deploy. | Keystroke cloud |
|
|
20
|
+
`keystroke deploy --project <id>` links your local codebase to your platform project: it uploads the built local codebase and activates that platform project's server. After the first deploy the project is saved as your active target (in `~/.keystroke`), so later commands — including subsequent deploys — can drop `--project`. Deployed agents and workflows instantly run there with no additional configuration; the web workspace is where organization members can manage their projects, deployments, logs, credentials, etc.
|
|
25
21
|
|
|
26
|
-
|
|
22
|
+
## How this codebase fits in
|
|
27
23
|
|
|
28
|
-
|
|
24
|
+
This is the local codebase that defines your agents, workflows, triggers, actions, skills, context files, etc.
|
|
29
25
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
### Project layout
|
|
33
|
-
|
|
34
|
-
Keystroke discovers everything under `src/` by convention.
|
|
26
|
+
Projects are scaffolded with an opinionated folder structure (like Next.js for building AI systems):
|
|
35
27
|
|
|
36
28
|
```
|
|
37
29
|
my-app/
|
|
38
|
-
keystroke.config.ts
|
|
30
|
+
keystroke.config.ts # project configuration
|
|
39
31
|
src/
|
|
40
|
-
agents/
|
|
41
|
-
actions/
|
|
42
|
-
workflows/
|
|
43
|
-
triggers/
|
|
44
|
-
skills/
|
|
45
|
-
files/
|
|
46
|
-
AGENTS.md
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
Each primitive imports from `@keystrokehq/keystroke/<piece>` (`/agent`, `/action`, `/workflow`, `/trigger`, `/sandbox`). Integrations are `@keystrokehq/<slug>` packages.
|
|
50
|
-
|
|
51
|
-
## The deploy-first loop
|
|
52
|
-
|
|
53
|
-
Keystroke is deploy-first: build, ship, then run and inspect what's live. Deploy often.
|
|
54
|
-
|
|
55
|
-
1. **Auth once** — `keystroke auth login` (token is stored and reused).
|
|
56
|
-
2. **Edit** primitives under `src/`.
|
|
57
|
-
3. **Deploy** — `keystroke deploy` (full) or `keystroke deploy --filter agents/support` (one module).
|
|
58
|
-
4. **Run** — `keystroke workflow run <slug> --input '{...}'` / `keystroke agent prompt <slug> --message "..."` / `keystroke app execute <app> <tool> --input '{...}'` (connected catalog actions only).
|
|
59
|
-
5. **Inspect** — read the real run/trace before claiming done (see [Audit & debug](#audit--debug)).
|
|
60
|
-
6. Repeat.
|
|
61
|
-
|
|
62
|
-
```bash
|
|
63
|
-
keystroke auth status # current user + org
|
|
64
|
-
keystroke project list # your platform projects
|
|
65
|
-
keystroke deploy # build + ship src/ (first deploy must be full)
|
|
66
|
-
keystroke workflow run greeting --input '{"name":"Ada"}' # run a workflow
|
|
67
|
-
keystroke agent prompt hello --message "Hi" # prompt an agent
|
|
68
|
-
keystroke app execute github github_get_the_authenticated_user # run a connected catalog action
|
|
32
|
+
agents/ # LLM agents
|
|
33
|
+
actions/ # reusable units of work
|
|
34
|
+
workflows/ # multi-step automations
|
|
35
|
+
triggers/ # schedules, webhooks, app events
|
|
36
|
+
skills/ # project skills for agents
|
|
37
|
+
files/ # context for attaching to agents
|
|
38
|
+
AGENTS.md # this guide
|
|
69
39
|
```
|
|
70
40
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
## Choosing a primitive
|
|
74
|
-
|
|
75
|
-
| Build a… | When |
|
|
76
|
-
| --- | --- |
|
|
77
|
-
| **Agent** | The path isn't fixed — needs judgment, tool use, language, or multi-turn context. Or when a user is explicitly asking you to build an agent (e.g. AI data analyst for use in Slack, support agent, etc) |
|
|
78
|
-
| **Workflow** | You know the order of steps and want durability and predictability. |
|
|
79
|
-
| **Action** | A single reusable capability (an API call, a computation) used by workflows or agents. |
|
|
80
|
-
| **Trigger** | Something should start a workflow/agent automatically — schedule, webhook, or poll. |
|
|
81
|
-
|
|
82
|
-
Generally, prefer a workflow (or a plain action / LLM step) unless the path genuinely varies at runtime. Reach for an agent only when the user is clearly asking you to build one or when the work needs judgment, tool selection, or multi-turn context — not just because it's easier to wire up. Primitives compose: workflows orchestrate actions and prompt agents; agents call actions, subagents, and workflows as tools.
|
|
83
|
-
|
|
84
|
-
## Building blocks
|
|
85
|
-
|
|
86
|
-
### Agent — `defineAgent`
|
|
87
|
-
|
|
88
|
-
Required: `slug`, `systemPrompt`, `model`. Everything else is optional.
|
|
89
|
-
|
|
90
|
-
```ts
|
|
91
|
-
import { defineAgent } from "@keystrokehq/keystroke/agent";
|
|
92
|
-
|
|
93
|
-
export default defineAgent({
|
|
94
|
-
slug: "support",
|
|
95
|
-
systemPrompt: "You are a helpful support assistant. Answer concisely.",
|
|
96
|
-
model: "anthropic/claude-sonnet-4.6",
|
|
97
|
-
});
|
|
98
|
-
```
|
|
99
|
-
|
|
100
|
-
- **Model** — exact catalog id in `vendor/model-id` format from https://keystroke.ai/models.md. Do **not** kebab-case the version (`anthropic/claude-sonnet-4.6` is valid; `...-4-6` fails at deploy).
|
|
101
|
-
- **Tools** — attach actions, subagents, workflows, and MCP tools directly in `tools: [...]`. Do **not** hand-roll a `defineTool` + `executeWorkflow()` wrapper. A subagent's tool name is its `slug` and takes a `message`.
|
|
102
|
-
- **Built in**: isolated `/workspace` with file + `bash` tools, session + persistent memory (`memory: false` to disable), `web_search`/`web_fetch` when configured, self-scheduling tools.
|
|
103
|
-
- **Credentials** are declared on actions, not agents — the agent gets them by calling those actions as tools.
|
|
104
|
-
- **Sandbox** — default in-process bash covers most needs. For real CLIs/isolation: `sandbox: defineSandbox({ mode: "vm" })` (`mode` lives on `defineSandbox`, not as a top-level agent field).
|
|
105
|
-
|
|
106
|
-
### Workflow — `defineWorkflow`
|
|
107
|
-
|
|
108
|
-
Global `slug`, typed Zod `input`/`output`, a normal `async` `run`.
|
|
109
|
-
|
|
110
|
-
```ts
|
|
111
|
-
import { defineWorkflow } from "@keystrokehq/keystroke/workflow";
|
|
112
|
-
import { z } from "zod";
|
|
113
|
-
|
|
114
|
-
export default defineWorkflow({
|
|
115
|
-
slug: "signup-pipeline",
|
|
116
|
-
input: z.object({ name: z.string(), email: z.string().email() }),
|
|
117
|
-
output: z.object({ brief: z.string() }),
|
|
118
|
-
async run(input) {
|
|
119
|
-
const { brief } = await researchSignup.run(input);
|
|
120
|
-
await postBrief.run({ text: brief });
|
|
121
|
-
return { brief };
|
|
122
|
-
},
|
|
123
|
-
});
|
|
124
|
-
```
|
|
125
|
-
|
|
126
|
-
| Step | Syntax |
|
|
127
|
-
| --- | --- |
|
|
128
|
-
| Action | `await action.run(input)` |
|
|
129
|
-
| Agent | `await agent.prompt({ message })` |
|
|
130
|
-
| One-shot LLM | `await promptLlm(prompt, { model, outputSchema? })` |
|
|
131
|
-
| Sub-workflow | `await otherWorkflow.run(input)` — never pass `ctx` |
|
|
132
|
-
| Durable sleep | `await ctx.sleep("1h")` |
|
|
133
|
-
| Durable wait | `await ctx.hook<T>()` |
|
|
134
|
-
|
|
135
|
-
`ctx` is the second `run` argument (`async run(input, ctx)`) — add it only when a step needs `ctx.sleep`/`ctx.hook`.
|
|
136
|
-
|
|
137
|
-
Use `promptLlm(...)` (import from `@keystrokehq/keystroke/workflow`) for one-shot generation/classification. Use `agent.prompt(...)` when the step needs tools, memory, or multi-turn reasoning.
|
|
138
|
-
|
|
139
|
-
**Durability** — completed steps replay on retry: keep side effects inside steps; make steps idempotent; keep control flow deterministic (no `Date.now()` / randomness). Step ids are assigned automatically from each call's position in `run`, so adding or removing an unrelated step never shifts the others. `ctx.sleep`/`ctx.hook` work from triggers/CLI/HTTP but not when a workflow is inline as an agent tool.
|
|
140
|
-
|
|
141
|
-
#### Canvas-legible, replayable workflows
|
|
142
|
-
|
|
143
|
-
The platform renders every workflow as an interactive **canvas** (a deploy-time parser reads your source) and lights up each step in run history. These habits keep steps rendering as real nodes and keep runs replayable. Full detail: `/learn/workflows/authoring-best-practices`.
|
|
144
|
-
|
|
145
|
-
**Principle** — put durable work in steps (`action.run`, `agent.prompt`, `promptLlm`, `ctx.sleep`, `ctx.hook`) and orchestrate them directly in `run` (or a same-file helper). Code between steps isn't checkpointed — it re-runs on every replay.
|
|
41
|
+
Keystroke discovers everything under `src/` by convention. Add a file that exports an agent, workflow, trigger, etc, and it becomes part of the project. Because the project is code, agents (Cursor, Claude Code, Codex, etc) can read it, change it, run tests, and maintain it over time.
|
|
146
42
|
|
|
147
|
-
|
|
43
|
+
Each primitive imports from `@keystrokehq/keystroke/<piece>` (`/action`, `/agent`, `/workflow`, `/trigger`, `/app`); integrations are their own `@keystrokehq/<slug>` packages. The example files in `src/` show the exact imports.
|
|
148
44
|
|
|
149
|
-
|
|
150
|
-
2. **Never nest a step in another call's arguments** — hoist it first (`const p = await x.run({}); await y.run({ v: p.field })`).
|
|
45
|
+
## How you work
|
|
151
46
|
|
|
152
|
-
|
|
47
|
+
### Build, deploy, test
|
|
153
48
|
|
|
154
|
-
|
|
155
|
-
- Iterate step work with **`for-of`**, not `.map` / `.filter` / `.forEach` / `.reduce`.
|
|
156
|
-
- Parallelize with a **literal** `Promise.all([a.run(), b.run()])`, not `Promise.all(arr.map(...))` / `race` / `allSettled` / `any`.
|
|
157
|
-
- List step **input fields explicitly** — no `...spread` or computed `[key]:` keys.
|
|
158
|
-
- Branch with **`if` / `else` / `switch`**, not step-bearing ternaries.
|
|
159
|
-
- Keep all side effects and non-determinism **inside steps**.
|
|
160
|
-
- Do HTTP/IO through an **action** (prebuilt integration *or* your own `defineAction`/`defineApp`), never a bare `fetch` in `run` — an inline `fetch` is neither a durable step nor a canvas node.
|
|
49
|
+
You build in `src/`, deploy to your project on the platform, then run and inspect what's deployed with the CLI. The loop is: edit → `keystroke deploy` → run/inspect/test → repeat. You should deploy often.
|
|
161
50
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
export const triage = defineAction({
|
|
171
|
-
slug: "triage",
|
|
172
|
-
input: z.object({ message: z.string() }),
|
|
173
|
-
output: z.object({ priority: z.enum(["low", "normal", "high", "urgent"]) }),
|
|
174
|
-
run: async (input) => ({ priority: /urgent/i.test(input.message) ? "urgent" : "normal" }),
|
|
175
|
-
});
|
|
176
|
-
```
|
|
177
|
-
|
|
178
|
-
Compose in workflows. Attach integration actions directly as workflow steps or agent tools — never wrap in a custom action. An action *may* call an agent (`await agent.prompt(...)`).
|
|
179
|
-
|
|
180
|
-
### Trigger — source + `.attach()`
|
|
181
|
-
|
|
182
|
-
Exactly three sources: `defineCronSource`, `defineWebhookSource`, `definePollSource`. Default-export the attached result.
|
|
183
|
-
|
|
184
|
-
```ts
|
|
185
|
-
import { defineWebhookSource } from "@keystrokehq/keystroke/trigger";
|
|
186
|
-
import { z } from "zod";
|
|
187
|
-
import workflow from "../workflows/signup-pipeline";
|
|
188
|
-
|
|
189
|
-
export default defineWebhookSource({
|
|
190
|
-
slug: "signup",
|
|
191
|
-
endpoint: "signup",
|
|
192
|
-
request: z.object({ name: z.string(), email: z.string().email() }),
|
|
193
|
-
}).attach({ workflow });
|
|
51
|
+
```bash
|
|
52
|
+
keystroke auth status # show the current user and active organization
|
|
53
|
+
keystroke auth login # once; token is automatically stored and reused
|
|
54
|
+
keystroke project list # your projects on the platform
|
|
55
|
+
keystroke deploy --project <id> # build + ship src/ to the platform
|
|
56
|
+
keystroke workflow run greeting --input '{"name":"Ada"}' # test and run a workflow
|
|
57
|
+
keystroke agent prompt hello --message "Hi" # test and chat with an agent
|
|
194
58
|
```
|
|
195
59
|
|
|
196
|
-
|
|
197
|
-
| --- | --- |
|
|
198
|
-
| **Schedule** (`defineCronSource`) | Time alone starts the run (cron is UTC in prod). Passes `{}` — workflow `input` must accept an empty object. |
|
|
199
|
-
| **Webhook** (`defineWebhookSource`) | Another system pushes events. Best option when available. |
|
|
200
|
-
| **Poll** (`definePollSource`) | Keystroke must pull/check an external system on a schedule. |
|
|
60
|
+
After a deploy, runtime commands (`workflow`, `agent`, `trigger`, `app`, `connect`) operate on that project automatically — no target to manage.
|
|
201
61
|
|
|
202
|
-
|
|
62
|
+
A project is a single live runtime. A deploy replaces what's running on that project — there's no separate dev and prod within one project. You can use `--filter` deploys and `@keystroke ignore` to iterate quickly.
|
|
203
63
|
|
|
204
|
-
|
|
64
|
+
To create a new project: `keystroke init my-app --yes`, then deploy.
|
|
205
65
|
|
|
206
|
-
|
|
66
|
+
Look things up cheapest-first: `keystroke <command> --help` for the authoritative flags and usage of any command (local, always current); `keystroke docs search`/`query` for documentation and examples; and `/cli` for the full reference when you need broad discovery.
|
|
207
67
|
|
|
208
|
-
|
|
68
|
+
### Building agents
|
|
209
69
|
|
|
210
|
-
|
|
70
|
+
Agents are useful when a task needs judgment, language understanding, tool use, or multi-turn context.
|
|
211
71
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
`keystroke deploy` **never uploads `.env`** — connect cloud credentials with `keystroke connect` / `keystroke credentials set`. Missing a built-in? Use `defineCredential` + an action, `defineApp`, or `defineMcp`. See `/learn/credentials/custom-integrations`.
|
|
72
|
+
| Capability | How it works |
|
|
73
|
+
| -------------------- | --------------------------------------------------- |
|
|
74
|
+
| **Instructions** | `systemPrompt` tells the model how to behave |
|
|
75
|
+
| **Models** | `model` uses `vendor/model-id`, with optional `thinkingLevel` |
|
|
76
|
+
| **File system** | Each session gets a `/workspace` with a built-in file system and bash (code execution) tools |
|
|
77
|
+
| **Web search** | Built-in `web_search` and `web_fetch` tools when a web provider is configured |
|
|
78
|
+
| **Tools** | Attach custom tools, MCP tools, subagents, and workflows as tools |
|
|
79
|
+
| **Skills and files** | Attach skills from `src/skills/` and context files from `src/files/` |
|
|
80
|
+
| **Memory** | Sessions and persistent memory are enabled by default unless disabled |
|
|
81
|
+
| **Sandbox** | A built-in sandbox environment for using CLIs and more advanced code execution |
|
|
82
|
+
| **Self-scheduling** | Built-in tools let an agent schedule its own future or recurring runs (ephemeral triggers) |
|
|
224
83
|
|
|
225
|
-
|
|
84
|
+
### Building workflows
|
|
226
85
|
|
|
227
|
-
|
|
228
|
-
- **Files** (`src/files/<set>/`) — static context an agent can access. Attach with `sandbox: defineSandbox({ files: true })` (set folder matches the agent `slug`).
|
|
86
|
+
Workflows are useful when you know the order of work: call an action, prompt an agent, branch on the result, wait for approval, then continue.
|
|
229
87
|
|
|
230
|
-
|
|
88
|
+
| Capability | How it works |
|
|
89
|
+
| -------------------------- | --------------------------------------------------------------------------------------------- |
|
|
90
|
+
| **Typed input and output** | Zod schemas validate the payload going in and the result coming out |
|
|
91
|
+
| **Action steps** | Call any [action](/learn/actions/overview) with `.run()`, your own or one from an integration |
|
|
92
|
+
| **Agent steps** | Prompt an [agent](/learn/agents/overview) with `.prompt()` when a step needs judgment |
|
|
93
|
+
| **LLM steps** | Use `promptLlm()` for a one-shot model call without defining a full agent |
|
|
94
|
+
| **Durability** | Completed steps are recorded and replayed, so retries resume instead of restarting |
|
|
95
|
+
| **Durable waits** | `ctx.sleep()` and `ctx.hook()` suspend a run for a delay or until it is resumed externally |
|
|
231
96
|
|
|
232
|
-
|
|
97
|
+
### Common failures
|
|
233
98
|
|
|
234
|
-
- **
|
|
235
|
-
- **
|
|
236
|
-
- **
|
|
99
|
+
- **Research real APIs before mirroring them** — building actions for custom integrations is very common; web search and fetch the actual reference & explore relevant docs, don't guess endpoints or payload shapes.
|
|
100
|
+
- **Run before you claim you are finished** — deploy what you build, run it in the cloud, and read the real output before activating live triggers or claiming you are finished building something. Chat with agents to ensure they behave correctly. Run workflows to ensure they behave correctly.
|
|
101
|
+
- **Cover obvious edge cases** and write simple tests as needed (null field, empty array, a step that throws).
|
|
102
|
+
- **Connected apps aren't in `.env`** — `keystroke deploy` never uploads `.env`; connect apps (aka, integrations) for your project with `keystroke connect <slug>`.
|
|
103
|
+
- **Filtered deploys** — determine if you should do a full vs filtered deploy, or use WIP ignore directives.
|
|
104
|
+
- **Actions are leaf units** - An action never calls another action (including integration actions like `slackSendMessage`). You can compose actions in a workflow, or attach an integration action directly as a workflow step or agent tool. Action can call agents, allowing you to create "subagent tools" or "agent steps".
|
|
237
105
|
|
|
238
|
-
|
|
106
|
+
### Audit & debug (CLI)
|
|
239
107
|
|
|
240
|
-
|
|
108
|
+
Use the CLI to inspect your deployed project.
|
|
241
109
|
|
|
242
110
|
```bash
|
|
243
|
-
|
|
244
|
-
keystroke workflow
|
|
245
|
-
|
|
246
|
-
keystroke
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
keystroke
|
|
250
|
-
keystroke
|
|
251
|
-
keystroke
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
keystroke
|
|
111
|
+
# Workflows
|
|
112
|
+
keystroke workflow list # list deployed workflows
|
|
113
|
+
keystroke workflow runs list <workflow-slug> # recent runs for a workflow
|
|
114
|
+
keystroke workflow runs get <workflow-slug> <run-id> --include steps,trace # one run in full: input, each step, execution trace
|
|
115
|
+
|
|
116
|
+
# Agents
|
|
117
|
+
keystroke agent list # list deployed agents
|
|
118
|
+
keystroke agent sessions list <agent-slug> # recent sessions for an agent
|
|
119
|
+
keystroke agent sessions get <agent-slug> <session-id> --include messages,trace # one session in full: messages, execution trace
|
|
120
|
+
|
|
121
|
+
# Triggers (attachment id is <trigger-slug>:<workflow-slug>)
|
|
122
|
+
keystroke trigger list # list deployed triggers
|
|
123
|
+
keystroke trigger url <trigger-slug> # print a trigger's webhook URL
|
|
124
|
+
keystroke trigger runs list <trigger-slug>:<workflow-slug> # recent runs for a trigger→workflow attachment
|
|
125
|
+
keystroke trigger runs get <trigger-slug>:<workflow-slug> <run-id> --include workflows,trace # one run in full: workflows fired, execution trace
|
|
255
126
|
```
|
|
256
127
|
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
## Before you claim you're done
|
|
260
|
-
|
|
261
|
-
- [ ] Deployed the change and read the real run output/trace — not just "it should work".
|
|
262
|
-
- [ ] Prompted agents / ran workflows with realistic input and verified behavior.
|
|
263
|
-
- [ ] Researched real APIs before mirroring them in custom integration actions — no guessed endpoints or payloads.
|
|
264
|
-
- [ ] Covered obvious edge cases (null field, empty array, a step that throws) with a simple test where warranted.
|
|
265
|
-
- [ ] Connected any new apps with `keystroke connect` (not `.env`).
|
|
266
|
-
- [ ] Tested manually before deploying attached cron/polling triggers, or used `trigger attachment disable` to pause until ready.
|
|
267
|
-
|
|
268
|
-
## Common gotchas
|
|
269
|
-
|
|
270
|
-
1. **Action calling an action** — throws at runtime and fails lint. Compose in a workflow, or attach the integration action directly.
|
|
271
|
-
2. **Bad model id** — must be exact `vendor/model-id` from the catalog; don't kebab the version. Fails at deploy, not typecheck.
|
|
272
|
-
3. **LLM structured output: use `.nullish()`, not `.optional()`** — models emit `"field": null` for "not applicable" values, and Zod `.optional()` rejects `null` (the step fails). Use `.nullish()` (or `.nullable()`) for any optional field in an `outputSchema`.
|
|
273
|
-
4. **Silent fallbacks hide real failures** — for live config (Sheets, DBs, credentials), don't `catch` and quietly substitute fallback/empty data in production paths. A run that "completes" with empty inputs looks like success in the trace but is a silent failure. Throw when required config is missing or empty, and validate response shape, not just status.
|
|
274
|
-
5. **Side effects outside steps** — they re-run on every replay. Keep them inside `.run()` / `.prompt()`.
|
|
275
|
-
6. **Schedule input** — a cron trigger passes `{}`; give the workflow `input: z.object({})`.
|
|
128
|
+
Read `src/` first; existing agents, workflows, actions, and triggers are your best map of how this project works.
|
|
276
129
|
|
|
277
130
|
## Documentation
|
|
278
131
|
|
|
279
|
-
|
|
132
|
+
**You should regularly and thoroughly read the documentation** when working with the CLI, building agents/workflows/triggers/actions/etc, and debugging. Bias heavily toward quickly reading documentation before making decisions or changes, and prefer what the docs say over your prior knowledge.
|
|
280
133
|
|
|
281
|
-
|
|
134
|
+
Documentation is directly available via the CLI (no auth required). Use these CLI commands to search, query, and read the Keystroke documentation:
|
|
282
135
|
|
|
283
|
-
-
|
|
284
|
-
-
|
|
285
|
-
- `keystroke docs query "<cmd>"` — read/search pages with `cat`/`rg`/`ls`/`tree`/`head` (e.g. `keystroke docs query "cat /learn/agents/build-agents.mdx"`).
|
|
136
|
+
- **`keystroke docs search "<query>"`** — best for broad or conceptual lookups when you don't know the path (e.g. `keystroke docs search "webhook trigger"`). Returns matching pages with titles and paths; read one by passing its path to `query`.
|
|
137
|
+
- **`keystroke docs query "<command>"`** — exact keyword/regex search, structure exploration, and reading pages by path with a shell-style command (`cat`, `rg`, `ls`, `tree`, `head`). Append `.mdx` to a doc path to read it: `keystroke docs query "cat /quickstart.mdx"`, `keystroke docs query "rg -il trigger /"`, `keystroke docs query "tree / -L 2"`.
|
|
286
138
|
|
|
287
|
-
`query` is stateless
|
|
139
|
+
Each `query` is stateless — the working dir resets to `/`, so use absolute paths or chain with `&&` — and output is capped (~30KB). Prefer a targeted `rg -C 3 "term" /path.mdx` or `head -n 120 /path.mdx` over `cat` on large pages, and batch several files into one read (`head -n 80 /a.mdx /b.mdx`).
|
|
288
140
|
|
|
289
|
-
Below is a sample of
|
|
141
|
+
Below is a sample of common documentation pages. You can read a page with `keystroke docs query "cat <path>.mdx"`.
|
|
290
142
|
|
|
291
143
|
| Doc path | Read when |
|
|
292
144
|
| -------- | --------- |
|
|
@@ -300,13 +152,13 @@ Below is a sample of some of the most common documentation pages.
|
|
|
300
152
|
| `/learn/agents/test-agents` | Testing agents locally |
|
|
301
153
|
| `/learn/agents/external-channels` | Slack / external channel bindings |
|
|
302
154
|
| `/learn/workflows/build-workflows` | Steps, orchestration, sandboxes |
|
|
303
|
-
| `/learn/workflows/authoring-best-practices` | Canvas-legible, replayable workflow patterns (hard + soft rules) |
|
|
304
155
|
| `/learn/workflows/test-workflows` | Workflow tests |
|
|
305
156
|
| `/learn/workflows/run-workflows` | Running workflows, inspecting output |
|
|
306
157
|
| `/learn/actions/overview` | Action rules (leaf units, no action-in-action) |
|
|
307
158
|
| `/learn/actions/workflow-steps` | Wiring actions into workflows |
|
|
308
159
|
| `/learn/actions/agent-tools` | Exposing actions as agent tools |
|
|
309
160
|
| `/learn/triggers/overview` | Choosing a trigger type |
|
|
161
|
+
| `/learn/triggers/app-events` | App-event triggers |
|
|
310
162
|
| `/learn/triggers/webhooks` | Webhook triggers |
|
|
311
163
|
| `/learn/triggers/schedules` | Cron / scheduled triggers |
|
|
312
164
|
| `/learn/triggers/polling` | Polling triggers |
|
|
@@ -314,7 +166,24 @@ Below is a sample of some of the most common documentation pages.
|
|
|
314
166
|
| `/learn/credentials/connect-credentials` | `keystroke connect`, OAuth, project credentials |
|
|
315
167
|
| `/learn/credentials/use-credentials` | Using connected apps in code |
|
|
316
168
|
| `/learn/credentials/custom-integrations` | Building a custom HTTP / MCP / GraphQL integration |
|
|
317
|
-
| `/learn/skills/overview` | `src/skills/`
|
|
169
|
+
| `/learn/skills/overview` | `src/skills/` playbooks for agents (runtime, not this guide) |
|
|
318
170
|
| `/learn/skills/create-skills` | Authoring an agent skill |
|
|
319
171
|
| `/learn/files/overview` | `src/files/` workspace context |
|
|
320
|
-
| `/learn/logs/overview` | Reading run history, debugging deployed runs |
|
|
172
|
+
| `/learn/logs/overview` | Reading run history, debugging deployed runs |
|
|
173
|
+
|
|
174
|
+
## Integrations and secrets
|
|
175
|
+
|
|
176
|
+
For searching native integrations use the CLI, not docs. The docs contain an index of integrations, but the documentation integration index is not reliable. To discover apps/integrations and their actions, query the live catalog with the CLI:
|
|
177
|
+
|
|
178
|
+
```bash
|
|
179
|
+
keystroke app list # list all registered/supported apps
|
|
180
|
+
keystroke app search <query> # find an app and its slug in the full catalog
|
|
181
|
+
keystroke app actions <slug> --search <q> # actions an app exposes
|
|
182
|
+
keystroke app action <action-slug> # input/output schema for one action
|
|
183
|
+
keystroke connect <slug> # connect an app credential (web flow)
|
|
184
|
+
keystroke credentials list # List stored/available credential instances
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
You can read `/learn/credentials/use-credentials` for using credentials in code and binding credentials to workflow steps and agent tools.
|
|
188
|
+
|
|
189
|
+
If you ever run into issues with an integration (or a required action isn't supported), it is *extremely* easy to build custom integrations and actions. You can build connections to any HTTP API or MCP server, including internal or private ones. See `/learn/credentials/custom-integrations`.
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: keystroke-actions
|
|
3
|
+
description: Define keystroke actions — workflow steps and agent tools. Custom API actions use a credential (defineCredential) or an app wrapper (app.action); catalog integrations import from npm packages. Use when authoring src/actions/ or wiring integrations.
|
|
4
|
+
metadata:
|
|
5
|
+
keystroke-domain: actions
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Actions
|
|
9
|
+
|
|
10
|
+
One **executable unit** used everywhere: workflow steps, agent tools, and codemode host calls.
|
|
11
|
+
|
|
12
|
+
Anything that calls an external API needs a **credential**. Two paths: declare a standalone `defineCredential` on a `defineAction` (simplest), or use an **app** wrapper (`app.action()`) when several actions share one connection or you've synced a catalog/custom app into `src/apps/`. See [apps skill](.agents/skills/keystroke-apps/SKILL.md).
|
|
13
|
+
|
|
14
|
+
## Actions are leaf units — never call an action from an action
|
|
15
|
+
|
|
16
|
+
An action's `run` must **not** call another action (yours or an integration action like `slackSendMessage`). Composition belongs in a **workflow**; an integration action is used directly as a workflow step or agent tool — not wrapped in a custom action.
|
|
17
|
+
|
|
18
|
+
```ts
|
|
19
|
+
// ❌ Don't: an action that calls another action
|
|
20
|
+
import { slackSendMessage } from "@keystrokehq/slack/actions";
|
|
21
|
+
export const notify = defineAction({ slug: "notify",
|
|
22
|
+
run: async (input) => slackSendMessage.run({ channel: input.channel, markdown_text: input.text }),
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
// ✅ Do: use the integration action directly as a workflow step
|
|
26
|
+
async run(input) {
|
|
27
|
+
return slackSendMessage.run({ channel: input.channel, markdown_text: buildText(input) });
|
|
28
|
+
}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
This is enforced at runtime (calling an action inside an action throws) and by lint (`no-restricted-imports` blocks importing `@keystrokehq/*/actions` inside `src/actions/`). An action **may** call an agent — see below.
|
|
32
|
+
|
|
33
|
+
## Custom API actions (standalone credential)
|
|
34
|
+
|
|
35
|
+
The simplest custom integration: declare a `defineCredential` and consume it from a plain `defineAction`. No app needed.
|
|
36
|
+
|
|
37
|
+
```ts
|
|
38
|
+
import { defineAction } from "@keystrokehq/keystroke/action";
|
|
39
|
+
import { defineCredential } from "@keystrokehq/keystroke/credentials";
|
|
40
|
+
import { z } from "zod";
|
|
41
|
+
|
|
42
|
+
const acme = defineCredential({ key: "acme", fields: { apiKey: z.string() } });
|
|
43
|
+
|
|
44
|
+
export const createAcmeTicket = defineAction({
|
|
45
|
+
slug: "create-acme-ticket",
|
|
46
|
+
input: z.object({ title: z.string() }),
|
|
47
|
+
output: z.object({ id: z.string() }),
|
|
48
|
+
credentials: [acme] as const,
|
|
49
|
+
async run(input, credentials) {
|
|
50
|
+
return createTicket({ apiKey: credentials.acme.apiKey, title: input.title });
|
|
51
|
+
},
|
|
52
|
+
});
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Connect it with `keystroke credentials set acme --set apiKey=@env:ACME_API_KEY --scope org`.
|
|
56
|
+
|
|
57
|
+
## App-backed actions (shared connection / synced apps)
|
|
58
|
+
|
|
59
|
+
Reach for an app when several actions share one connection, or when you've synced a catalog/custom app:
|
|
60
|
+
|
|
61
|
+
1. **App first** — create in the dashboard or `keystroke app create`, then `keystroke app sync <slug>` → `src/apps/<name>/app.ts`
|
|
62
|
+
2. **Connect** — `keystroke connect <slug>` (see [apps skill](.agents/skills/keystroke-apps/SKILL.md))
|
|
63
|
+
3. **Action** — `app.action()` in `src/actions/`, reading `credentials[app.slug]` in `run`
|
|
64
|
+
|
|
65
|
+
```ts
|
|
66
|
+
import { z } from "zod";
|
|
67
|
+
import { kwatch } from "../apps/kwatch/app";
|
|
68
|
+
|
|
69
|
+
export const kwatchListAlerts = kwatch.action({
|
|
70
|
+
slug: "kwatch-list-alerts",
|
|
71
|
+
input: z.object({}),
|
|
72
|
+
output: z.object({ ok: z.boolean(), alertCount: z.number() }),
|
|
73
|
+
async run(_input, credentials) {
|
|
74
|
+
const { apiKey } = credentials["keystroke/kwatch"];
|
|
75
|
+
return { ok: true, alertCount: 0 };
|
|
76
|
+
},
|
|
77
|
+
});
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Use an app wrapper (over a standalone credential) when a family of actions should share one connection definition.
|
|
81
|
+
|
|
82
|
+
## Pure actions (no credential)
|
|
83
|
+
|
|
84
|
+
Use `defineAction` with no `credentials` when the step needs no connection at all (pure logic, local transforms):
|
|
85
|
+
|
|
86
|
+
```ts
|
|
87
|
+
import { defineAction } from "@keystrokehq/keystroke/action";
|
|
88
|
+
import { z } from "zod";
|
|
89
|
+
|
|
90
|
+
export const triage = defineAction({
|
|
91
|
+
slug: "triage",
|
|
92
|
+
name: "Triage",
|
|
93
|
+
description: "Classify an inbound message",
|
|
94
|
+
input: z.object({ message: z.string(), sender: z.string() }),
|
|
95
|
+
output: z.object({ priority: z.enum(["low", "normal", "high", "urgent"]) }),
|
|
96
|
+
run: async (input) => ({ priority: "normal" }),
|
|
97
|
+
});
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## Catalog integration actions
|
|
101
|
+
|
|
102
|
+
Official integrations ship as npm packages with pre-built actions. Discover with `keystroke app search`, connect with `keystroke connect <slug>`, then import in workflows or agent tools — not in `src/actions/`:
|
|
103
|
+
|
|
104
|
+
```ts
|
|
105
|
+
import { exaSearch } from "@keystrokehq/exa/actions";
|
|
106
|
+
import { slackSendMessage } from "@keystrokehq/slack/actions";
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Detail: [catalog-and-imports.md](references/catalog-and-imports.md).
|
|
110
|
+
|
|
111
|
+
## Call an agent from an action (allowed)
|
|
112
|
+
|
|
113
|
+
Agents — not other actions — are the one thing an action may invoke:
|
|
114
|
+
|
|
115
|
+
```ts
|
|
116
|
+
import signupResearcher from "../agents/signup-researcher";
|
|
117
|
+
|
|
118
|
+
export const researchSignup = defineAction({
|
|
119
|
+
slug: "research-signup",
|
|
120
|
+
run: async (input) => {
|
|
121
|
+
const result = await signupResearcher.prompt({ message: "…" });
|
|
122
|
+
return { brief: "…" };
|
|
123
|
+
},
|
|
124
|
+
});
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## Credential scope & resolution
|
|
128
|
+
|
|
129
|
+
When a credential's scope isn't pinned, the runtime resolves it **project default first, then org default**. Pin a scope per-use with `.scope()` (the authoring-side counterpart to the CLI `--scope` flag) — it returns a fresh action that still binds like any other step/tool:
|
|
130
|
+
|
|
131
|
+
```ts
|
|
132
|
+
await myAction.scope("user").run(input); // workflow step
|
|
133
|
+
tools: [myAction.scope("user")]; // agent tool
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
When several instances exist for one app + scope and none is the default, pin an exact instance to the consuming step/tool from the CLI: `keystroke credentials assignments assign --workflow <slug> --credential <app>/<slug> --consumer step:<slug>#0` (see [apps skill](.agents/skills/keystroke-apps/SKILL.md)).
|
|
137
|
+
|
|
138
|
+
## Long-running actions: honor the abort signal
|
|
139
|
+
|
|
140
|
+
`run` receives a third `ctx` argument with an `AbortSignal`. Thread it into fetches/SDK calls so the framework can cancel work cleanly:
|
|
141
|
+
|
|
142
|
+
```ts
|
|
143
|
+
async run(input, credentials, ctx) {
|
|
144
|
+
const res = await fetch(url, { signal: ctx?.signal });
|
|
145
|
+
return res.json();
|
|
146
|
+
}
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
## Where actions run
|
|
150
|
+
|
|
151
|
+
| Consumer | Usage |
|
|
152
|
+
| -------- | ----------------------------------------------- |
|
|
153
|
+
| Workflow | `await myAction.run(input)` in `defineWorkflow` |
|
|
154
|
+
| Agent | `tools: [myAction, exaSearch]` on `defineAgent` |
|
|
155
|
+
|
|
156
|
+
## Next references
|
|
157
|
+
|
|
158
|
+
- [catalog-and-imports.md](references/catalog-and-imports.md) — catalog discovery, npm imports
|
|
159
|
+
|
|
160
|
+
Related: [apps](.agents/skills/keystroke-apps/SKILL.md), [workflows](.agents/skills/keystroke-workflows/SKILL.md), [agents](.agents/skills/keystroke-agents/SKILL.md).
|