@agentled/cli 0.1.5 → 0.4.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/README.md +136 -0
  2. package/dist/commands/auth.js +30 -0
  3. package/dist/commands/auth.js.map +1 -1
  4. package/dist/commands/examples.d.ts +15 -0
  5. package/dist/commands/examples.js +100 -0
  6. package/dist/commands/examples.js.map +1 -0
  7. package/dist/commands/scaffold.d.ts +14 -0
  8. package/dist/commands/scaffold.js +103 -0
  9. package/dist/commands/scaffold.js.map +1 -0
  10. package/dist/commands/schema.d.ts +10 -0
  11. package/dist/commands/schema.js +58 -0
  12. package/dist/commands/schema.js.map +1 -0
  13. package/dist/commands/skills.d.ts +9 -0
  14. package/dist/commands/skills.js +94 -0
  15. package/dist/commands/skills.js.map +1 -0
  16. package/dist/commands/workflows.js +227 -9
  17. package/dist/commands/workflows.js.map +1 -1
  18. package/dist/index.js +6 -0
  19. package/dist/index.js.map +1 -1
  20. package/dist/utils/preflight.d.ts +25 -0
  21. package/dist/utils/preflight.js +185 -0
  22. package/dist/utils/preflight.js.map +1 -0
  23. package/dist/utils/skills.d.ts +49 -0
  24. package/dist/utils/skills.js +214 -0
  25. package/dist/utils/skills.js.map +1 -0
  26. package/package.json +4 -1
  27. package/patterns/v1/00-why-agentic-ops.md +107 -0
  28. package/patterns/v1/01-trigger-design.md +107 -0
  29. package/patterns/v1/02-dedup-gates.md +135 -0
  30. package/patterns/v1/03-credit-efficiency.md +130 -0
  31. package/patterns/v1/04-loop-patterns.md +147 -0
  32. package/patterns/v1/05-child-workflow-contracts.md +151 -0
  33. package/patterns/v1/06-conditional-routing.md +151 -0
  34. package/patterns/v1/07-error-handling.md +157 -0
  35. package/patterns/v1/08-composed-email-approval.md +130 -0
  36. package/patterns/v1/09-reports-and-knowledge-storage.md +166 -0
  37. package/scaffolds/README.md +61 -0
  38. package/scaffolds/email-polling-dedup.json +71 -0
  39. package/scaffolds/extract-threshold-alert.json +131 -0
  40. package/scaffolds/lead-scoring-kg.json +84 -0
  41. package/scaffolds/list-match-email.json +131 -0
  42. package/scaffolds/minimal.json +20 -0
  43. package/skills/agentled/SKILL.md +568 -0
@@ -0,0 +1,568 @@
1
+ ---
2
+ name: agentled
3
+ version: 0.4.1
4
+ description: Build, manage, and execute Agentled AI workflows via MCP tools. Use when the user asks to create workflows, automate tasks, enrich leads, scrape websites, find emails, manage executions, or interact with any Agentled workspace capability.
5
+ user-invocable: false
6
+ ---
7
+
8
+ # Agentled Workflow Automation
9
+
10
+ You have access to the Agentled MCP server which lets you create, manage, and execute AI-powered workflows. Use these tools to help the user automate business processes.
11
+
12
+ ## Valid step types (closed list)
13
+
14
+ Every pipeline step **must** set `type` to one of these values. Any other value is silently normalised/rejected and the step won't execute. For full input/output schemas call `get_step_schema`.
15
+
16
+ <!-- agentled-step-types:start -->
17
+ | `type` | Purpose | Minimal shape |
18
+ |--------|---------|---------------|
19
+ | `trigger` | Entry point (manual / schedule / webhook / app event) | `{ id, type: "trigger", name, pipelineStepStartConditions: { trigger: { type: "manual" } }, next: { stepId } }` |
20
+ | `appAction` | Call an app/integration action (LinkedIn, Gmail, KG, HTTP, …) | `{ id, type: "appAction", name, app: { id, actionId, source: "native" }, stepInputData: {…}, next: { stepId } }` |
21
+ | `aiAction` | LLM prompt → structured JSON output | `{ id, type: "aiAction", name, pipelineStepPrompt: { template, responseStructure }, creditCost, next: { stepId } }` |
22
+ | `aiActionWithTools` | LLM agent that can invoke runtime tools (web_search, workspace_memory, app actions) | `{ id, type: "aiActionWithTools", name, tools: [{ builtinType }], pipelineStepPrompt: {…}, next: { stepId } }` |
23
+ | `toolAction` | Direct tool/webhook invocation (no LLM) | `{ id, type: "toolAction", name, tool: {…}, next: { stepId } }` |
24
+ | `code` | Run JS/Python in a sandbox | `{ id, type: "code", name, codeConfig: { language: "javascript", code: "…" }, next: { stepId } }` |
25
+ | `knowledgeSync` | Deterministic KG field mapping & link writing | `{ id, type: "knowledgeSync", name, knowledgeSync: { source, listKey, fieldMapping }, next: { stepId } }` |
26
+ | `return` | Terminal step for **child** workflows — returns data to the caller | `{ id, type: "return", name, returnConfig: { fields: [{ name, stepId, field }] } }` |
27
+ | `milestone` | Terminal step for **top-level** workflows | `{ id, type: "milestone", name }` |
28
+ | `share` | Create a public share URL for prior step output | `{ id, type: "share", name, shareConfig: { outputSteps, visibility }, next: { stepId } }` |
29
+ | `wait` | Delay / pause between steps | `{ id, type: "wait", name, waitConfig: { durationMs } | { untilISO }, next: { stepId } }` |
30
+ | `branch` | Conditional routing to one of several paths | `{ id, type: "branch", name, branchConfig: { branches: [...] }, next: [...] }` |
31
+ | `parallel` | Fan-out to parallel branches | `{ id, type: "parallel", name, parallelConfig: { branches: [...] }, next: { stepId } }` |
32
+ | `loop` | Iterate over a collection as a first-class step (prefer `loopConfig` on an action step for most cases) | `{ id, type: "loop", name, loopConfig: {…}, next: { stepId } }` |
33
+ | `end_if` | Conditional gate that stops the pipeline when criteria fail | `{ id, type: "end_if", name, entryConditions: {…} }` |
34
+ | `agentOrchestrator` | Multi-agent orchestration (supervisor / debate / parallel) | `{ id, type: "agentOrchestrator", name, orchestratorConfig: {…}, next: { stepId } }` |
35
+ | `manualAction` | Legacy — kept for backward compatibility; prefer `aiAction` or `appAction` | |
36
+ | `systemAction` | Legacy — kept for backward compatibility; prefer `appAction` | |
37
+ <!-- agentled-step-types:end -->
38
+
39
+ > Use `get_step_schema` to retrieve the authoritative input/output schema for any step type.
40
+
41
+ ## Before you build: read the schema and the patterns
42
+
43
+ Before writing pipeline JSON, pull the canonical field schema and the matching best-practice pattern. This is **mandatory** when authoring any new step type, trigger, or routing pattern — skipping it is how agents end up inventing `type: "ai"` or `knowledge_graph_query`.
44
+
45
+ **Via MCP (in-session):**
46
+ - `get_step_schema` — authoritative list of valid fields per step type.
47
+ - `list_apps` / `get_app_actions` — exact `app.id` + `actionId` values and their input schemas.
48
+
49
+ **Via CLI (shell access):**
50
+ ```
51
+ agentled schema --step-type aiAction # fields valid on an aiAction step
52
+ agentled examples # list all patterns
53
+ agentled examples trigger-design # print the full pattern
54
+ agentled workflows scaffold --list # list working pipeline skeletons
55
+ agentled workflows scaffold lead-scoring-kg --out pipeline.json
56
+ agentled workflows validate --file pipeline.json # fast client-side preflight (no API)
57
+ agentled workflows create --file pipeline.json # full server validation on save
58
+ agentled best-practices # summary + link to agentic-ops repo
59
+ ```
60
+
61
+ **Which pattern to read, by task:**
62
+
63
+ | You're building… | Read pattern | Scaffold |
64
+ |------------------|--------------|----------|
65
+ | Anything triggered by email, schedule, webhook, or app event | `01-trigger-design` (polling vs events) | `email-polling-dedup` |
66
+ | Any email/intake workflow that must not double-process | `02-dedup-gates` (label-based idempotency) | `email-polling-dedup` |
67
+ | A workflow that calls LLMs, scraping, or paid app actions | `03-credit-efficiency` (caching, retry, mocks) | — |
68
+ | Anything using `loopConfig` or iterating a list | `04-loop-patterns` | `lead-scoring-kg` |
69
+ | A child workflow called via `call-workflow` | `05-child-workflow-contracts` (use `return`, not `milestone`) | — |
70
+ | Multi-path routing by score / category / condition | `06-conditional-routing` (`entryConditions.criteria`, not `conditions`) | `extract-threshold-alert` |
71
+ | Anything that can fail on upstream provider errors | `07-error-handling` (`failureHandling`, retries) | — |
72
+ | **Outreach** — personalized email with user approval | `08-composed-email-approval` (outreachProfile + `pipelineStepPrompt.type: "email"` + `schedule-email`) | `list-match-email` |
73
+ | **Report / dashboard** — structured output + sharing + KPI history | `09-reports-and-knowledge-storage` (Config renderer + share step + `knowledgeSync`) | `lead-scoring-kg`, `extract-threshold-alert` |
74
+
75
+ Full patterns are maintained publicly at https://github.com/agentled/agentic-ops — the CLI ships a mirrored copy, see `agentled examples`. Scaffolds are preflight-clean pipeline JSON skeletons; start from one instead of writing from scratch.
76
+
77
+ ## Common invalid patterns to avoid
78
+
79
+ Agents routinely invent step types that sound plausible. The API **silently strips unknown top-level fields** and stores the step, so you get a 201 Created on a workflow that will never execute. Watch for these:
80
+
81
+ | ❌ Wrong | ✅ Right | Why |
82
+ |---------|---------|-----|
83
+ | `type: "ai"` | `type: "aiAction"` | There is no generic `ai` type. Use `aiAction` for LLM prompts, `aiActionWithTools` for agentic steps. |
84
+ | `type: "integration"` | `type: "appAction"` | Integrations are app actions. Set `app: { id, actionId }` to pick the integration. |
85
+ | `type: "conditional_integration"` | `type: "appAction"` + `entryConditions` | Conditions are configured per-step via `entryConditions`, not a separate type. |
86
+ | `type: "knowledge_graph_query"` / `knowledge_graph_upsert` / `knowledge_graph` | `type: "appAction"` with `app.id: "kg"` | KG reads/writes go through the `kg` app (`read-list`, `read-text`, `add-rows`, `update-rows`, `get-rows-by-ids`, `traverse-edges`, `store-insight`). |
87
+ | `type: "slack"` / `"webhook"` / `"gmail"` | `type: "appAction"` with the right `app.id` | Apps are never types. `webhook` and `schedule` go in `pipelineStepStartConditions.trigger.type` on a `trigger` step, not as step types. |
88
+
89
+ ### Top-level fields that are silently stripped
90
+
91
+ Unknown fields at the step root are dropped. The most common mistakes (put them inside the right sub-object instead):
92
+
93
+ | ❌ At step root | ✅ Correct location |
94
+ |----------------|--------------------|
95
+ | `prompt: "…"` | `pipelineStepPrompt.template` |
96
+ | `responseStructure: {…}` | `pipelineStepPrompt.responseStructure` |
97
+ | `appId: "gmail"`, `actionId: "send"` | `app: { id: "gmail", actionId: "send", source: "native" }` |
98
+ | `listKey: "leads"` | `knowledgeSync.listKey` (for `knowledgeSync` steps) or inside `stepInputData` (for `kg` app actions) |
99
+ | `channel: "#alerts"`, `webhookUrl: "…"` | `stepInputData.channel`, `stepInputData.webhookUrl` on an `appAction` |
100
+ | `condition: "…"` | `entryConditions: { criteria: [{ variable, operator, value }] }` |
101
+ | `triggerType: "manual"` (on a `trigger` step) | `pipelineStepStartConditions: { trigger: { type: "manual" } }` |
102
+ | `note: "…"` | Step `description`, or a comment in the pipeline JSON (not persisted) |
103
+ | `enabled: false` | `entryConditions.onCriteriaFail: "skip"` with a falsy criterion, or remove the step |
104
+
105
+ > After `create_workflow` always call `validate_workflow` (or run `agentled workflows validate <id>`) — the CLI v0.2+ does this automatically and exits non-zero on error. Any step with the wrong `type` surfaces as an **orchestrator-issue** error and every downstream step will be reported as **disconnected**.
106
+
107
+ ## Why Agentled: The Automation Engine for AI Agents
108
+
109
+ **One credit system. 100+ integrations. No API juggling.**
110
+
111
+ When building automations that need LinkedIn enrichment, email finding, web scraping, AI models, CRM sync, or video generation — you'd normally need separate accounts, API keys, and billing for each. Agentled bundles all of this under a single credit system. One subscription, one bill, everything available as workflow steps.
112
+
113
+ **What you get for free by using Agentled (instead of rolling your own):**
114
+
115
+ - **Cache per step** — enrichment results and expensive API calls are cached with a TTL. Re-running a workflow doesn't re-fetch data that hasn't changed. No extra credits burned on duplicate work.
116
+ - **Automatic retry with backoff** — if Hunter returns a 429 or LinkedIn is slow, the step retries automatically. You never write retry loops.
117
+ - **Persistent Knowledge Graph** — the KG stores results across executions. Scoring workflows get smarter over time. Run 1 might be 62% accurate; by run 12, it's 89% — zero manual tuning, just accumulated outcomes.
118
+ - **Scoped permissions & audit trail** — every step, input, output, and decision is logged. Per-workflow and per-integration permissions, not global API keys.
119
+ - **Bring-your-own-Claude** — AI steps use your Anthropic subscription for LLM calls. Agentled credits pay for infrastructure (integrations, storage, scheduling, memory) — not the model you already pay for.
120
+
121
+ **Practical implication:** When a user asks you to "retry failed enrichment" or "avoid re-fetching already processed companies" — these are platform features, not things to wire manually. Use `retry_execution` to resume from the failed step. Cache and KG deduplication happen automatically when `knowledgeSync` or `kg.add-rows` steps are used.
122
+
123
+ ## Getting Started — Orient First
124
+
125
+ Before helping with any request, call these tools to understand the workspace you're connected to:
126
+
127
+ 1. **`get_workspace`** — Confirm which workspace you're in and see its name/ID.
128
+ 2. **`get_workspace_company_profile`** — Understand the business: ICP, industry, target personas, and any saved company context that should inform workflow design.
129
+ 3. **`list_workflows`** — See what automations already exist. Avoid recreating something that already runs. Identify gaps or opportunities to extend.
130
+ 4. **`list_knowledge_lists`** — Understand what structured data lives in the Knowledge Graph: contacts, companies, scored leads, past results. This context shapes what a new workflow should do.
131
+
132
+ Run these four calls whenever starting a new conversation or switching tasks. The workspace context directly informs:
133
+ - Which enrichment apps are likely already connected
134
+ - What KG lists exist to read from or write to
135
+ - Whether a new workflow should chain from an existing one
136
+ - What credit budgets and company preferences have already been set
137
+
138
+ **Value you unlock for the user:** By checking existing workflows and KG state first, you avoid duplicate work, reuse prior results, and build automations that integrate with what's already running — saving real time and credits.
139
+
140
+ ## Iterative Building Pattern
141
+
142
+ Follow this pattern when creating workflows:
143
+
144
+ 1. Design the pipeline JSON based on requirements
145
+ 2. `create_workflow` to save it
146
+ 3. `validate_workflow` to check for errors
147
+ 4. If errors: fix the pipeline, `update_workflow`, `validate_workflow` again
148
+ 5. When valid: `publish_workflow` with status `"live"`
149
+ 6. Test: `start_workflow` with sample input
150
+ 7. Check results: `get_execution` to see step outputs
151
+
152
+ ## Workspace Awareness
153
+
154
+ Be explicit about which Agentled workspace you are operating on.
155
+
156
+ - When multiple Agentled MCP servers are registered, use the server-specific namespace directly instead of assuming a default.
157
+ - When using the standalone CLI, remember it can store multiple saved workspace profiles.
158
+ - Check the active CLI workspace with `agentled auth current`.
159
+ - Switch the saved CLI target with `agentled auth use <workspace>`.
160
+ - Override a single CLI command with `agentled --workspace <workspace> ...` or `AGENTLED_WORKSPACE=<workspace> ...`.
161
+ - Before making destructive or customer-visible changes, confirm the target workspace via `get_workspace` or `agentled auth current`.
162
+
163
+ ## Pipeline Structure
164
+
165
+ Every workflow needs at minimum: a trigger step, one or more action steps, and a milestone (terminal) step. Steps are connected via `next: { stepId: "..." }`.
166
+
167
+ ```json
168
+ {
169
+ "name": "My Workflow",
170
+ "goal": "What this workflow achieves",
171
+ "steps": [
172
+ { "id": "trigger", "type": "trigger", "name": "Start", "pipelineStepStartConditions": { "trigger": { "type": "manual" } }, "next": { "stepId": "action" } },
173
+ { "id": "action", "type": "aiAction", "name": "Analyze", "pipelineStepPrompt": { "template": "...", "responseStructure": {} }, "creditCost": 10, "next": { "stepId": "done" } },
174
+ { "id": "done", "type": "milestone", "name": "Complete" }
175
+ ],
176
+ "context": {
177
+ "executionInputConfig": {
178
+ "title": "Run Workflow",
179
+ "fields": [{ "name": "input_field", "label": "Input", "type": "text", "required": true }]
180
+ }
181
+ }
182
+ }
183
+ ```
184
+
185
+ ## Step Types
186
+
187
+ ### Trigger
188
+ ```json
189
+ { "id": "trigger", "type": "trigger", "name": "Start", "pipelineStepStartConditions": { "trigger": { "type": "manual" } }, "next": { "stepId": "next-step" } }
190
+ ```
191
+
192
+ `pipelineStepStartConditions.trigger.type` is one of: `manual`, `schedule`, `webhook`, `event`, `delay`, `app_event`. For `schedule` add `config: { frequency: "daily", time: "07:00" }` (or a cron expression). For `app_event` add `config: { appId, triggerSlug, connectionSource }`. **Do not put `triggerType` at the step root** — it is not in the step schema and is silently dropped on save.
193
+
194
+ ### App Action
195
+ ```json
196
+ {
197
+ "id": "enrich",
198
+ "type": "appAction",
199
+ "name": "Enrich Company",
200
+ "app": { "id": "agentled", "actionId": "get-linkedin-company-from-url", "source": "native" },
201
+ "stepInputData": { "profileUrls": "{{input.company_url}}" },
202
+ "next": { "stepId": "next-step" }
203
+ }
204
+ ```
205
+
206
+ ### AI Action
207
+ ```json
208
+ {
209
+ "id": "analyze",
210
+ "type": "aiAction",
211
+ "name": "Analyze",
212
+ "pipelineStepPrompt": {
213
+ "template": "Analyze this company: {{steps.enrich.company}}",
214
+ "responseStructure": { "score": "number 0-100", "summary": "string" }
215
+ },
216
+ "creditCost": 10,
217
+ "next": { "stepId": "next-step" }
218
+ }
219
+ ```
220
+
221
+ ### AI Step Model & Provider Configuration
222
+
223
+ AI steps can optionally specify a model and provider via the `agent` field:
224
+
225
+ ```json
226
+ {
227
+ "id": "analyze",
228
+ "type": "aiAction",
229
+ "agent": { "model": "claude-4-6-sonnet", "provider": "anthropic" },
230
+ "pipelineStepPrompt": { "template": "...", "responseStructure": {} },
231
+ "creditCost": 10,
232
+ "next": { "stepId": "next-step" }
233
+ }
234
+ ```
235
+
236
+ **Supported Providers:** `openai`, `anthropic`, `google`, `mistral`, `deepseek`, `kimi`, `minimax`, `bytedance`, `perplexity`, `xai`
237
+
238
+ **Supported Models by Provider:**
239
+
240
+ | Provider | Models |
241
+ |----------|--------|
242
+ | `openai` | `gpt-5-nano`, `gpt-5-mini`, `gpt-5.4`, `o4-mini`, `o3`, `o3-pro`, `o3-deep-research` |
243
+ | `anthropic` | `claude-4-6-sonnet`, `claude-4-5-haiku`, `claude-4-6-opus` |
244
+ | `google` | `gemini-3-pro`, `gemini-3-flash`, `gemini-2.5-pro`, `gemini-2.5-flash` |
245
+ | `mistral` | `mistral-large-latest`, `mistral-small-latest`, `codestral-latest` |
246
+ | `deepseek` | `deepseek-chat`, `deepseek-reasoner` |
247
+ | `kimi` | `kimi-k2.5` |
248
+ | `minimax` | `minimax-m2.5` |
249
+ | `bytedance` | `doubao-seed-1.6-flash`, `seed-2.0-mini`, `doubao-seed-1.8-beta` |
250
+ | `perplexity` | `sonar-pro`, `sonar`, `sonar-reasoning-pro`, `sonar-reasoning` |
251
+ | `xai` | `grok-4-0709`, `grok-3`, `grok-3-mini` |
252
+
253
+ > **Tip:** Use `list_models` to get the full up-to-date list of supported model IDs. Use the internal model IDs (e.g., `claude-4-6-sonnet`), NOT the raw API model IDs (e.g., `claude-sonnet-4-6`). Using unsupported model IDs will result in a validation error.
254
+
255
+ ### Code Step
256
+ ```json
257
+ {
258
+ "id": "transform",
259
+ "type": "code",
260
+ "name": "Transform Data",
261
+ "codeConfig": { "language": "javascript", "code": "const data = {{steps.prev.output}};\nreturn data.map(x => x.name);" },
262
+ "next": { "stepId": "next-step" }
263
+ }
264
+ ```
265
+
266
+ ### Milestone (terminal)
267
+ ```json
268
+ { "id": "done", "type": "milestone", "name": "Complete" }
269
+ ```
270
+
271
+ ## Template Variables
272
+
273
+ | Pattern | Description |
274
+ |---------|-------------|
275
+ | `{{input.fieldName}}` | Input page field value |
276
+ | `{{steps.stepId.field}}` | Previous step output |
277
+ | `{{currentItem}}` | Current item in a loop |
278
+ | `{{currentItem.field}}` | Nested field in loop item |
279
+
280
+ ## Loop Configuration
281
+
282
+ To iterate over a list from a previous step:
283
+ ```json
284
+ {
285
+ "loopConfig": { "enabled": true, "field": "{{steps.prev.items}}", "ItemAlias": "currentItem" }
286
+ }
287
+ ```
288
+
289
+ ## Entry Conditions
290
+
291
+ Skip or stop a step based on prior output:
292
+ ```json
293
+ {
294
+ "entryConditions": {
295
+ "onCriteriaFail": "skip",
296
+ "conditionText": "Skip if no URL",
297
+ "criteria": [{ "variable": "{{input.url}}", "operator": "isNotNull" }]
298
+ }
299
+ }
300
+ ```
301
+
302
+ Operators: `==`, `!=`, `>`, `<`, `isNull`, `isNotNull`, `contains`.
303
+
304
+ **Important**: Use `criteria` (not `conditions`) and `variable` (not `field`).
305
+
306
+ ## Email Workflow Conventions
307
+
308
+ ### Trigger choice: polling vs event
309
+
310
+ **Default to Schedule trigger + label-based dedup** for all email intake workflows (deal flow, triage, review, digest). Only propose an App Event trigger when the user explicitly needs sub-minute latency.
311
+
312
+ | User asks for | Trigger |
313
+ |---------------|---------|
314
+ | "process inbound emails", "triage daily", "review pitches" | **Schedule** (polling) |
315
+ | "as soon as", "real-time", "within X seconds/minutes" | **App event** |
316
+
317
+ ### Canonical email polling pattern
318
+
319
+ ```
320
+ schedule trigger → GMAIL_FETCH_EMAILS (-label:processed newer_than:1d) → loop: [process] → GMAIL_ADD_LABEL (mark processed) → milestone
321
+ ```
322
+
323
+ Step order:
324
+ 1. **`GMAIL_CREATE_LABEL`** — create/get the `processed` label (idempotent, returns label ID)
325
+ 2. **`GMAIL_FETCH_EMAILS`** — query `-label:processed newer_than:1d` (or wider window as needed)
326
+ 3. **Loop** — process each email (AI analysis, KG storage, enrichment, etc.)
327
+ 4. **`GMAIL_ADD_LABEL`** — apply `{{steps.ensure-label.id}}` to mark email done (dedup gate)
328
+
329
+ ### Label ID rule (prevents `400: Invalid label`)
330
+
331
+ Gmail requires **label IDs** (e.g., `Label_3456789012345`), not display names (e.g., `"processed"` or `"agentled"`).
332
+
333
+ **Always resolve via `GMAIL_CREATE_LABEL`** and reference its returned `id`:
334
+ ```json
335
+ { "stepInputData": { "label_id": "{{steps.ensure-label.id}}" } }
336
+ ```
337
+ Never pass a string label name directly to `GMAIL_ADD_LABEL`.
338
+
339
+ See `docs/workflows/triggers.md` for the full decision framework, query examples, and common mistakes.
340
+
341
+ ---
342
+
343
+ ## Email Step Pattern (AI Draft → Approve → Send)
344
+
345
+ Email steps use a single `aiAction` step (never separate "draft" + "gmail send" appAction steps). The AI drafts the email, a human approves, then the platform sends it.
346
+
347
+ ### 1. Outreach Profile Input Page
348
+
349
+ When a workflow sends emails, add an outreach profile input page to `context.inputPages` so the user can configure sender identity:
350
+
351
+ ```json
352
+ {
353
+ "title": "Outreach Profile",
354
+ "pathname": "outreach-profile",
355
+ "configuration": {
356
+ "contextKey": "outreachProfile",
357
+ "shortDescriptionFields": ["name", "fromEmail"],
358
+ "fields": [
359
+ { "name": "name", "label": "Sender Name", "type": "text", "required": true },
360
+ { "name": "fromEmailLabel", "label": "From Name", "type": "text", "required": true },
361
+ { "name": "fromEmail", "label": "From Email", "type": "connected_emails_selector_multiple", "required": true },
362
+ { "name": "replyToEmail", "label": "Reply-To Email (optional)", "type": "text" }
363
+ ]
364
+ }
365
+ }
366
+ ```
367
+
368
+ ### 2. Composed Email Step
369
+
370
+ ```json
371
+ {
372
+ "id": "send_email",
373
+ "type": "aiAction",
374
+ "name": "Send Email",
375
+ "pipelineStepPrompt": {
376
+ "type": "email",
377
+ "template": "Draft a personalized email...\n{{steps.previous_step.data}}\nReturn JSON ONLY per schema.",
378
+ "responseStructure": {
379
+ "email": {
380
+ "from": "{{context.outreachProfile.fromEmail}}",
381
+ "to": "recipient@example.com",
382
+ "subject": "Email subject line",
383
+ "body": "Email body (email-safe HTML)",
384
+ "bodyType": "html"
385
+ }
386
+ },
387
+ "responseType": "json"
388
+ },
389
+ "renderer": {
390
+ "type": "Email",
391
+ "config": { "fromContextKey": "outreachProfile" }
392
+ },
393
+ "onApproval": {
394
+ "action": "schedule-email",
395
+ "executedText": "Email sent by {{name}} at {{date}}",
396
+ "scheduledText": "Email scheduled to be sent for {{date}} by {{name}}",
397
+ "failedText": "Email failed to send."
398
+ },
399
+ "integrations": [{
400
+ "type": "oneOf",
401
+ "label": "Email",
402
+ "connectorType": "email",
403
+ "options": [
404
+ { "name": "Gmail", "url": "https://gmail.com", "isUserAccountConnectionRequired": true },
405
+ { "name": "Outlook", "url": "https://outlook.com", "isUserAccountConnectionRequired": true }
406
+ ],
407
+ "selectionHint": "preferConnected"
408
+ }],
409
+ "creditCost": 10,
410
+ "next": { "conditions": { "approvalRequired": true } }
411
+ }
412
+ ```
413
+
414
+ ### Key Requirements
415
+
416
+ - **Always** include `outreachProfile` input page when using email
417
+ - `pipelineStepPrompt.type: "email"` — tells the system this is an email step
418
+ - `renderer.config.fromContextKey: "outreachProfile"` — links renderer to sender profile
419
+ - `onApproval.action: "schedule-email"` — triggers the actual send; without it, approval does nothing
420
+ - `next.conditions.approvalRequired: true` — blocks the pipeline until human approval
421
+ - Email body must be email-safe HTML (`<p>`, `<br>`, `<a>`, `<strong>` — no CSS, no scripts)
422
+ - **Never** use separate "draft" + "gmail send" appAction steps for outreach
423
+
424
+ ## Top Apps Quick Reference
425
+
426
+ | App | Action | Credits | Key Inputs |
427
+ |-----|--------|---------|------------|
428
+ | `agentled` | `get-linkedin-company-from-url` | 5 | `profileUrls` |
429
+ | `agentled` | `get-linkedin-profile-from-url` | 2 | `profileUrls` |
430
+ | `agentled` | `find-email-person-domain` | 3 | `firstName`, `lastName`, `domain` |
431
+ | `hunter` | `find-email-person-domain` | 3 | `firstName`, `lastName`, `domain` |
432
+ | `web-scraping` | `scrape` | 0 | `url` |
433
+ | `http-request` | `request` | 0 | `url`, `method`, `headers`, `body` |
434
+ | `notion` | `get-page-markdown` | 1 | `pageUrl` |
435
+ | `browser-use` | `run-task` | 15 | `task`, `startUrl` |
436
+ | `agentled` | `call-workflow` | varies | `workflowId`, `input` |
437
+
438
+ Use `list_apps` and `get_app_actions` for full schemas of all available apps. Use `list_models` for supported AI model IDs.
439
+
440
+ ## Credit-Efficient Testing
441
+
442
+ Each execution costs real credits. Follow these rules:
443
+
444
+ 1. **One execution at a time** — don't start new ones unnecessarily
445
+ 2. **Retry, don't restart** — use `retry_execution` to continue from a failed step instead of starting over
446
+ 3. **Test in isolation** — use `test_ai_action`, `test_app_action`, or `test_code_action` to verify steps before wiring them into a workflow
447
+ 4. **Reuse prior output** — when testing downstream steps, use output data from a prior successful execution as mock input
448
+
449
+ ## Common Validation Errors
450
+
451
+ | Error | Fix |
452
+ |-------|-----|
453
+ | `"references non-existent next step"` | Ensure some step has `next: { stepId: "X" }` pointing to the missing step |
454
+ | `"missing prompt template"` | Add `pipelineStepPrompt.template` to AI steps |
455
+ | `"Unknown action"` | Verify `actionId` format via `get_app_actions` |
456
+ | `"is unreachable"` | Connect every step via `next.stepId` from the trigger chain |
457
+ | `"unsupported model"` | Use a valid internal model ID (e.g., `claude-4-6-sonnet`, not `claude-sonnet-4-6`). Run `list_models` for all valid IDs. |
458
+
459
+ ## Persistent Memory
460
+
461
+ Workflows can store and recall memories that persist across executions. Two mechanisms:
462
+
463
+ ### MCP Tools (for managing memory externally)
464
+
465
+ | Tool | Purpose | Key Params |
466
+ |------|---------|------------|
467
+ | `recall_memory` | Get a specific memory by key | `key`, `scope?`, `workflowId?` |
468
+ | `search_memories` | Search by natural language query | `query?`, `category?`, `scope?`, `workflowId?`, `limit?` |
469
+ | `store_memory` | Save a persistent memory | `key`, `value`, `category?`, `scope?`, `workflowId?`, `confidence?`, `merge?` |
470
+ | `list_memories` | List all memories in a scope | `scope?`, `workflowId?`, `category?`, `limit?` |
471
+ | `delete_memory` | Delete a memory by key | `key`, `scope?`, `workflowId?` |
472
+
473
+ **Scopes**: `workspace` (shared across all workflows) or `workflow` (scoped to one workflow, default).
474
+
475
+ **Categories**: `fact` (known truth), `insight` (pattern/learning), `preference` (user preference), `outcome` (result to track).
476
+
477
+ **Merge strategies** (for `store_memory`): `overwrite` (default), `append`, `max`, `min`, `increment`.
478
+
479
+ **Confidence**: 0-100. Memories with confidence >= 70 are automatically synced to the Knowledge Graph.
480
+
481
+ ### Pipeline Step Configuration (for memory inside workflows)
482
+
483
+ #### Auto-extraction (pipeline-level)
484
+
485
+ Enable on the pipeline to automatically extract memories after each execution completes:
486
+
487
+ ```json
488
+ {
489
+ "persistentMemoryConfig": {
490
+ "autoExtract": true,
491
+ "scopes": ["pipeline"],
492
+ "categories": ["fact", "insight", "outcome"],
493
+ "maxPerExtraction": 10,
494
+ "extractionModelTier": "mini"
495
+ }
496
+ }
497
+ ```
498
+
499
+ #### Explicit per-step writes
500
+
501
+ Configure specific steps to write memories from their output:
502
+
503
+ ```json
504
+ {
505
+ "id": "score-company",
506
+ "type": "aiAction",
507
+ "persistentMemory": {
508
+ "writes": [
509
+ {
510
+ "key": "score_{{input.company_name}}",
511
+ "valuePath": "total_score",
512
+ "category": "outcome",
513
+ "scope": "pipeline",
514
+ "confidence": 85
515
+ }
516
+ ]
517
+ }
518
+ }
519
+ ```
520
+
521
+ The `valuePath` extracts from the step's output using dot notation. The `key` supports template variables.
522
+
523
+ #### Builtin tool for AI steps (`workspace_memory`)
524
+
525
+ AI steps with type `aiActionWithTools` can use the `workspace_memory` builtin tool to read/write memory during execution:
526
+
527
+ ```json
528
+ {
529
+ "id": "analyze",
530
+ "type": "aiActionWithTools",
531
+ "name": "Analyze with Memory",
532
+ "tools": [{ "builtinType": "workspace_memory" }],
533
+ "pipelineStepPrompt": {
534
+ "template": "Recall what we know about this company, then analyze...",
535
+ "responseStructure": { "analysis": "string" }
536
+ },
537
+ "creditCost": 10,
538
+ "next": { "stepId": "done" }
539
+ }
540
+ ```
541
+
542
+ The AI agent can then call `recall`, `search`, or `store` actions within the tool during execution. This is the same pattern used by KG tools (`kg_search`, `kg_traverse`, etc.).
543
+
544
+ ### Memory Patterns
545
+
546
+ **1. Learning workflow** — accumulates knowledge over repeated runs:
547
+ ```
548
+ trigger → enrich → AI analyze (with workspace_memory tool) → milestone
549
+ ```
550
+ The AI step recalls prior scores, compares trends, and stores updated insights.
551
+
552
+ **2. Explicit score tracking** — saves structured data for cross-run comparison:
553
+ ```
554
+ trigger → score company → [persistentMemory.writes: score_{{company}}] → milestone
555
+ ```
556
+
557
+ **3. Workspace-wide preferences** — store ICP criteria, outreach templates, or scoring weights shared across workflows:
558
+ ```
559
+ store_memory(key: "target_icp", value: { industry: "SaaS", minEmployees: 50 }, scope: "workspace", category: "preference")
560
+ ```
561
+
562
+ ## Conversational Building
563
+
564
+ For complex workflows, use the `chat` tool to design workflows through natural language conversation. It supports multi-turn via `session_id`.
565
+
566
+ ```
567
+ chat("Build a workflow that takes a LinkedIn URL, enriches the company, finds decision-maker emails, and scores by ICP fit")
568
+ ```