@kairos-sdk/core 0.4.5 → 0.5.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/README.md CHANGED
@@ -8,7 +8,7 @@
8
8
 
9
9
  ![Kairos SDK Demo](demo.gif)
10
10
 
11
- Kairos turns plain-English workflow descriptions into validated, deployable n8n workflow JSON. Use it as an **MCP server** (connect to Claude Code, Claude Desktop, or any MCP host — your LLM generates, Kairos validates and deploys, no Anthropic API key needed) or as a **TypeScript SDK** for programmatic control (calls Claude internally with a specialized prompt). Either way, workflows pass through a **26-rule structural validator** with automatic correction, and a local workflow library with **hybrid retrieval** (TF-IDF + node fingerprinting + outcome history + cluster reranking) injects past failure patterns into future generations. With a seeded template library, Kairos achieves **100% first-try structural validation pass rate** across 20 benchmark prompts (meaning the generated JSON is structurally valid on the first attempt — runtime behavior depends on your credentials and node configuration).
11
+ Kairos turns plain-English workflow descriptions into validated, deployable n8n workflow JSON. Use it as an **MCP server** (connect to Claude Code, Claude Desktop, or any MCP host — your LLM generates, Kairos validates and deploys, no Anthropic API key needed) or as a **TypeScript SDK** for programmatic control (calls Claude internally with a specialized prompt). Either way, workflows pass through a **34-rule structural validator** with automatic correction, and a local workflow library with **hybrid retrieval** (TF-IDF + node fingerprinting + outcome history + cluster reranking) injects past failure patterns into future generations. With a seeded template library, Kairos achieves **100% first-try structural validation pass rate** across 20 benchmark prompts (meaning the generated JSON is structurally valid on the first attempt — runtime behavior depends on your credentials and node configuration).
12
12
 
13
13
  ```ts
14
14
  import { Kairos } from '@kairos-sdk/core'
@@ -32,7 +32,7 @@ console.log(result.credentialsNeeded) // what the user still needs to configure
32
32
  | Kairos does | Kairos does not guarantee (yet) |
33
33
  |---|---|
34
34
  | Generates valid n8n workflow JSON | Perfect business logic |
35
- | Validates structure before deploy (23 rules) | Correct credentials |
35
+ | Validates structure before deploy (34 rules) | Correct credentials |
36
36
  | Syncs node types from your live instance | Runtime success for every API |
37
37
  | Learns from prior successful builds | That every workflow matches intent perfectly |
38
38
  | Works through MCP, SDK, or CLI | Full replacement for human review |
@@ -95,7 +95,7 @@ The MCP server does **not** call an LLM internally. Instead, it gives your host
95
95
 
96
96
  1. **Host LLM calls `kairos_prompt`** — gets the n8n system prompt, node catalog, library matches, and failure patterns
97
97
  2. **Host LLM generates the workflow JSON** using that context (no separate API call)
98
- 3. **Host LLM calls `kairos_validate`** — checks the JSON against 26 structural rules
98
+ 3. **Host LLM calls `kairos_validate`** — checks the JSON against 34 structural rules
99
99
  4. If invalid, the host LLM fixes the issues and validates again
100
100
  5. **Host LLM calls `kairos_deploy`** — sends the validated workflow to n8n
101
101
 
@@ -108,7 +108,7 @@ This means Kairos works with **any LLM** — Claude, GPT, Gemini, Llama, or anyt
108
108
  | Tool | Description |
109
109
  |------|-------------|
110
110
  | `kairos_prompt` | Returns the specialized system prompt, node catalog, library matches, and failure patterns for a given description |
111
- | `kairos_validate` | Validates workflow JSON against 26 structural rules — returns errors and warnings |
111
+ | `kairos_validate` | Validates workflow JSON against 34 structural rules — returns errors and warnings |
112
112
  | `kairos_search` | Searches the local workflow library for similar past builds |
113
113
  | `kairos_sync` | Manually refresh the node catalog from your n8n instance (auto-runs on first `kairos_prompt` call) |
114
114
 
@@ -180,7 +180,7 @@ console.log(deployed.workflowId) // now live in n8n
180
180
 
181
181
  ## Benchmark Results
182
182
 
183
- Tested against 20 workflow prompts of varying complexity (simple triggers, multi-step conditional logic, AI agents with memory). Results measure **structural validation pass rate** — whether the generated workflow passes all 26 validator rules, not end-to-end execution correctness.
183
+ Tested against 20 workflow prompts of varying complexity (simple triggers, multi-step conditional logic, AI agents with memory). Results measure **structural validation pass rate** — whether the generated workflow passes all 34 validator rules, not end-to-end execution correctness.
184
184
 
185
185
  ### Before vs After: Template-Seeded Library
186
186
 
@@ -192,7 +192,7 @@ Tested against 20 workflow prompts of varying complexity (simple triggers, multi
192
192
  | Avg generation time | 30.6s | **20.7s** | -32% |
193
193
  | Failures | 0 | 0 | — |
194
194
 
195
- The baseline run used Claude with the 26-rule validator and correction loop but no library. The seeded run used the same validator plus a library of 105 workflows (16 organic + 89 ingested from the n8n community). The broader local development library now contains 286+ generated/ingested workflows. Template seeding eliminated the correction loop entirely and cut generation time by a third.
195
+ The baseline run used Claude with the 34-rule validator and correction loop but no library. The seeded run used the same validator plus a library of 105 workflows (16 organic + 89 ingested from the n8n community). The broader local development library now contains 286+ generated/ingested workflows. Template seeding eliminated the correction loop entirely and cut generation time by a third.
196
196
 
197
197
  > **Note:** These results confirm that generated workflows are structurally valid and deployable to n8n. They do not verify runtime execution correctness, credential configuration, or whether the workflow output matches user intent.
198
198
 
@@ -205,7 +205,7 @@ The baseline run used Claude with the 26-rule validator and correction loop but
205
205
  1. **Search** — Kairos searches its local workflow library for similar past builds. Matching workflows and their failure patterns are pulled into context.
206
206
  2. **Warn** — Known failure patterns (from library matches and global telemetry rates) are injected into the system prompt so Claude avoids repeating known mistakes.
207
207
  3. **Generate** — Your description is sent to Claude with a detailed system prompt, forcing a `generate_workflow` tool call that produces structured n8n workflow JSON.
208
- 4. **Validate** — The workflow is checked against **26 structural rules** covering node IDs, types, versions, names, positions, connections, forbidden fields, trigger presence, AI connection direction, cycle detection, webhook pairing, and required parameters.
208
+ 4. **Validate** — The workflow is checked against **34 structural rules** covering node IDs, types, versions, names, positions, connections, forbidden fields, trigger presence, AI connection direction, cycle detection, webhook pairing, required parameters, and content quality (placeholder URLs, empty code nodes, missing required fields for Slack/Gmail/IF/Set/Schedule/Webhook nodes).
209
209
  5. **Correct** — If validation fails, the specific rule violations are sent back to Claude for correction (up to 3 attempts, with tighter temperature on the final try).
210
210
  6. **Strip** — Forbidden server-assigned fields (`id`, `createdAt`, `updatedAt`, etc.) are stripped before deployment.
211
211
  7. **Deploy** — The validated workflow is posted to your n8n instance via REST API.
@@ -215,7 +215,7 @@ The baseline run used Claude with the 26-rule validator and correction loop but
215
215
 
216
216
  1. **Prompt** — Your LLM calls `kairos_prompt`, which searches the library and returns the specialized system prompt, node catalog, library matches, and failure patterns.
217
217
  2. **Generate** — Your LLM generates the workflow JSON itself using that context. No separate API call.
218
- 3. **Validate** — Your LLM calls `kairos_validate`, which checks the JSON against the same 26 structural rules.
218
+ 3. **Validate** — Your LLM calls `kairos_validate`, which checks the JSON against the same 34 structural rules.
219
219
  4. **Correct** — If validation fails, your LLM fixes the issues and calls `kairos_validate` again.
220
220
  5. **Deploy** — Your LLM calls `kairos_deploy`, which strips forbidden fields and posts the workflow to n8n.
221
221
  6. **Record** — The deployed workflow is saved to the local library for future retrieval.
@@ -224,7 +224,7 @@ The baseline run used Claude with the 26-rule validator and correction loop but
224
224
 
225
225
  ## Validator Rules
226
226
 
227
- The 26-rule validator is the core of what makes Kairos reliable. In baseline testing (no library), Claude needed the correction loop 45% of the time. Each rule targets a specific class of error:
227
+ The 34-rule validator is the core of what makes Kairos reliable. In baseline testing (no library), Claude needed the correction loop 45% of the time. Each rule targets a specific class of error:
228
228
 
229
229
  | Rule | Severity | What it checks |
230
230
  |------|----------|----------------|
@@ -254,6 +254,14 @@ The 26-rule validator is the core of what makes Kairos reliable. In baseline tes
254
254
  | 24 | warn | No deprecated `$node["..."]` accessor syntax in expressions |
255
255
  | 25 | warn | No `$json.items[n]` array access (n8n flattens items automatically) |
256
256
  | 26 | warn | Node references use `.first()` or `.all()` (bare `$('Node').json` throws at runtime) |
257
+ | 27 | warn | HTTP Request URLs are real endpoints (not `example.com` or `YOUR_URL` placeholders) |
258
+ | 28 | warn | Code nodes contain actual executable logic (not empty or comment-only) |
259
+ | 29 | warn | Slack message operations specify a channel (`channelId` with `__rl` object, or `channel`) |
260
+ | 30 | warn | Gmail send operations specify at least one recipient (`to` field non-empty) |
261
+ | 31 | warn | IF nodes have at least one condition in `conditions.conditions` |
262
+ | 32 | warn | Set nodes have at least one field assignment in `assignments.assignments` (typeVersion 3.x) |
263
+ | 33 | warn | Schedule triggers have at least one rule in `rule.interval` |
264
+ | 34 | warn | Webhook paths are relative — no spaces, no leading slash, no protocol prefix |
257
265
 
258
266
  Errors block deployment. Warnings are recorded and fed back into the prompt for future builds.
259
267
 
@@ -392,7 +400,7 @@ try {
392
400
  |---|---|
393
401
  | `GenerationError` | Anthropic API call failed |
394
402
  | `ResponseParseError` | Claude responded but produced no usable tool call |
395
- | `ValidationError` | Workflow failed 26-rule validation after max retries (carries `.attemptMetadata` and `.warnedRules`) |
403
+ | `ValidationError` | Workflow failed 34-rule validation after max retries (carries `.attemptMetadata` and `.warnedRules`) |
396
404
  | `ProviderError` | Network/auth failure talking to n8n |
397
405
  | `ApiError` | n8n returned a 4xx or 5xx (carries `.statusCode`) |
398
406
  | `GuardError` | Input validation failed (empty description) or `delete()` called without `{ confirm: true }` |
@@ -1,8 +1,10 @@
1
1
  import {
2
+ GuardError,
2
3
  KairosError,
3
4
  N8nValidator,
5
+ ProviderError,
4
6
  generateUUID
5
- } from "./chunk-6IXW3WCC.js";
7
+ } from "./chunk-KIFT5LA7.js";
6
8
 
7
9
  // src/library/null-library.ts
8
10
  var NullLibrary = class {
@@ -26,15 +28,9 @@ var NullLibrary = class {
26
28
  }
27
29
  };
28
30
 
29
- // src/errors/guard-error.ts
30
- var GuardError = class extends KairosError {
31
- constructor(message) {
32
- super(message);
33
- this.name = "GuardError";
34
- }
35
- };
36
-
37
31
  // src/providers/n8n/provider.ts
32
+ var SMOKE_TEST_TIMEOUT_MS = 3e4;
33
+ var SMOKE_TEST_POLL_INTERVAL_MS = 1e3;
38
34
  var N8nProvider = class {
39
35
  constructor(client, stripper) {
40
36
  this.client = client;
@@ -96,6 +92,71 @@ var N8nProvider = class {
96
92
  async untag(workflowId, tagIds) {
97
93
  await this.client.untagWorkflow(workflowId, tagIds);
98
94
  }
95
+ async smokeTest(workflowId, workflow) {
96
+ const start = Date.now();
97
+ const trigger = this.detectTrigger(workflow);
98
+ if (trigger.type === "unsupported") {
99
+ return { status: "not-applicable", triggerType: "not-applicable" };
100
+ }
101
+ if (trigger.type === "manual") {
102
+ let executionId;
103
+ try {
104
+ executionId = await this.client.triggerManual(workflowId);
105
+ } catch (err) {
106
+ return { status: "error", triggerType: "manual", durationMs: Date.now() - start, error: String(err) };
107
+ }
108
+ try {
109
+ const execution = await this.pollExecution(executionId);
110
+ const durationMs = Date.now() - start;
111
+ if (execution.status === "success") {
112
+ return { status: "passed", triggerType: "manual", executionId, durationMs };
113
+ }
114
+ return {
115
+ status: "failed",
116
+ triggerType: "manual",
117
+ executionId,
118
+ durationMs,
119
+ error: `Execution ended with status: ${execution.status}`
120
+ };
121
+ } catch (err) {
122
+ return { status: "error", triggerType: "manual", executionId, durationMs: Date.now() - start, error: String(err) };
123
+ }
124
+ }
125
+ try {
126
+ const statusCode = await this.client.triggerWebhookTest(trigger.path);
127
+ const durationMs = Date.now() - start;
128
+ if (statusCode >= 200 && statusCode < 300) {
129
+ return { status: "passed", triggerType: "webhook", durationMs };
130
+ }
131
+ return { status: "failed", triggerType: "webhook", durationMs, error: `Webhook returned HTTP ${statusCode}` };
132
+ } catch (err) {
133
+ return { status: "error", triggerType: "webhook", durationMs: Date.now() - start, error: String(err) };
134
+ }
135
+ }
136
+ detectTrigger(workflow) {
137
+ for (const node of workflow.nodes) {
138
+ if (node.type === "n8n-nodes-base.manualTrigger") return { type: "manual" };
139
+ if (node.type === "n8n-nodes-base.webhook") {
140
+ const params = node.parameters;
141
+ const path = typeof params?.["path"] === "string" ? params["path"] : "webhook";
142
+ return { type: "webhook", path };
143
+ }
144
+ }
145
+ return { type: "unsupported" };
146
+ }
147
+ async pollExecution(executionId) {
148
+ const deadline = Date.now() + SMOKE_TEST_TIMEOUT_MS;
149
+ for (; ; ) {
150
+ const execution = await this.client.getExecution(executionId);
151
+ if (execution.status !== "running" && execution.status !== "waiting") {
152
+ return execution;
153
+ }
154
+ const remaining = deadline - Date.now();
155
+ if (remaining <= 0) break;
156
+ await new Promise((resolve) => setTimeout(resolve, Math.min(SMOKE_TEST_POLL_INTERVAL_MS, remaining)));
157
+ }
158
+ throw new ProviderError(`Smoke test: execution ${executionId} did not complete within ${SMOKE_TEST_TIMEOUT_MS}ms`);
159
+ }
99
160
  };
100
161
 
101
162
  // src/errors/generation-error.ts
@@ -144,6 +205,19 @@ var SECRET_PATTERNS = [
144
205
  /AIza[a-zA-Z0-9_-]{35}/,
145
206
  /AKIA[A-Z0-9]{16}/
146
207
  ];
208
+ var SECRET_PREFIXES = ["sk-", "ghp_", "xoxb-", "AIza", "AKIA"];
209
+ function collectExpressionStrings(obj, out = []) {
210
+ if (typeof obj === "string") {
211
+ if (obj.includes("={{")) out.push(obj);
212
+ } else if (Array.isArray(obj)) {
213
+ for (const item of obj) collectExpressionStrings(item, out);
214
+ } else if (obj !== null && typeof obj === "object") {
215
+ for (const val of Object.values(obj)) {
216
+ collectExpressionStrings(val, out);
217
+ }
218
+ }
219
+ return out;
220
+ }
147
221
  function assessTemplateSafety(workflow) {
148
222
  const reasons = [];
149
223
  let worst = "safe";
@@ -166,6 +240,15 @@ function assessTemplateSafety(workflow) {
166
240
  break;
167
241
  }
168
242
  }
243
+ const expressions = collectExpressionStrings(node.parameters);
244
+ for (const expr of expressions) {
245
+ for (const prefix of SECRET_PREFIXES) {
246
+ if (expr.includes(prefix)) {
247
+ escalate("review", `Node "${node.name}" has an expression containing credential-like prefix "${prefix}"`);
248
+ break;
249
+ }
250
+ }
251
+ }
169
252
  }
170
253
  return { trustLevel: worst, reasons };
171
254
  }
@@ -223,12 +306,26 @@ var TemplateSyncer = class {
223
306
  }
224
307
  return progress;
225
308
  }
309
+ async fetchWithBackoff(url, maxRetries = 3) {
310
+ let delayMs = DELAY_BETWEEN_FETCHES_MS;
311
+ let lastResponse;
312
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
313
+ lastResponse = await fetch(url);
314
+ if (lastResponse.status !== 429 && lastResponse.status !== 503) return lastResponse;
315
+ if (attempt === maxRetries) break;
316
+ const retryAfterHeader = lastResponse.headers.get("Retry-After");
317
+ const waitMs = retryAfterHeader ? parseInt(retryAfterHeader, 10) * 1e3 : delayMs * Math.pow(2, attempt);
318
+ this.logger.warn(`HTTP ${lastResponse.status} from template API, retrying in ${waitMs}ms`, { url, attempt });
319
+ await new Promise((resolve) => setTimeout(resolve, waitMs));
320
+ }
321
+ return lastResponse;
322
+ }
226
323
  async fetchTemplateIds(max, progress) {
227
324
  const ids = [];
228
325
  let page = 1;
229
326
  while (ids.length < max) {
230
327
  const url = `${N8N_TEMPLATE_API}/search?page=${page}&rows=${PAGE_SIZE}`;
231
- const response = await fetch(url);
328
+ const response = await this.fetchWithBackoff(url);
232
329
  if (!response.ok) break;
233
330
  const data = await response.json();
234
331
  progress.total = Math.min(data.totalWorkflows, max);
@@ -248,7 +345,7 @@ var TemplateSyncer = class {
248
345
  }
249
346
  async processTemplate(id, progress) {
250
347
  const url = `${N8N_TEMPLATE_API}/workflows/${id}`;
251
- const response = await fetch(url);
348
+ const response = await this.fetchWithBackoff(url);
252
349
  if (!response.ok) return;
253
350
  const data = await response.json();
254
351
  const templateMeta = data.workflow;
@@ -305,11 +402,10 @@ var TemplateSyncer = class {
305
402
 
306
403
  export {
307
404
  NullLibrary,
308
- GuardError,
309
405
  N8nProvider,
310
406
  GenerationError,
311
407
  ResponseParseError,
312
408
  ValidationError,
313
409
  TemplateSyncer
314
410
  };
315
- //# sourceMappingURL=chunk-6CLI43FI.js.map
411
+ //# sourceMappingURL=chunk-5GAY7CSJ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/library/null-library.ts","../src/providers/n8n/provider.ts","../src/errors/generation-error.ts","../src/errors/response-parse-error.ts","../src/errors/validation-error.ts","../src/templates/safety.ts","../src/templates/syncer.ts"],"sourcesContent":["import type { IWorkflowLibrary, WorkflowMatch, StoredWorkflow, WorkflowMetadataInput, LibraryFilters, SearchOptions, OutcomeData } from './types.js'\nimport type { N8nWorkflow } from '../types/workflow.js'\nimport { generateUUID } from '../utils/uuid.js'\n\nexport class NullLibrary implements IWorkflowLibrary {\n async initialize(): Promise<void> {}\n\n async search(_description: string, _options?: SearchOptions): Promise<WorkflowMatch[]> {\n return []\n }\n\n async save(_workflow: N8nWorkflow, _metadata: WorkflowMetadataInput): Promise<string> {\n return generateUUID()\n }\n\n async recordDeployment(_id: string): Promise<void> {}\n\n async recordOutcome(_id: string, _outcome: OutcomeData): Promise<void> {}\n\n async get(_id: string): Promise<StoredWorkflow | null> {\n return null\n }\n\n async list(_filters?: LibraryFilters): Promise<StoredWorkflow[]> {\n return []\n }\n}\n","import type { N8nWorkflow, Tag } from '../../types/workflow.js'\nimport type { DeployResult, WorkflowListItem, ExecutionSummary, ExecutionDetail, SmokeTestResult } from '../../types/result.js'\nimport type { DeleteOptions, ExecutionFilter } from '../../types/options.js'\nimport type { IProvider } from '../types.js'\nimport { GuardError } from '../../errors/guard-error.js'\nimport { ProviderError } from '../../errors/provider-error.js'\nimport { N8nApiClient } from './api-client.js'\nimport { N8nFieldStripper } from './stripper.js'\n\nconst SMOKE_TEST_TIMEOUT_MS = 30_000\nconst SMOKE_TEST_POLL_INTERVAL_MS = 1_000\n\ntype TriggerInfo =\n | { type: 'manual' }\n | { type: 'webhook'; path: string }\n | { type: 'unsupported' }\n\nexport class N8nProvider implements IProvider {\n readonly platform = 'n8n'\n\n constructor(\n private readonly client: N8nApiClient,\n private readonly stripper: N8nFieldStripper,\n ) {}\n\n async deploy(workflow: N8nWorkflow): Promise<DeployResult> {\n const stripped = this.stripper.stripForCreate(workflow)\n const response = await this.client.createWorkflow(stripped)\n return { workflowId: response.id, name: response.name }\n }\n\n async update(id: string, workflow: N8nWorkflow): Promise<DeployResult> {\n const stripped = this.stripper.stripForUpdate(workflow)\n const response = await this.client.updateWorkflow(id, stripped)\n return { workflowId: response.id, name: response.name }\n }\n\n async get(id: string): Promise<N8nWorkflow> {\n const response = await this.client.getWorkflow(id)\n return {\n name: response.name,\n nodes: response.nodes,\n connections: response.connections,\n ...(response.settings !== undefined ? { settings: response.settings } : {}),\n ...(response.tags !== undefined ? { tags: response.tags } : {}),\n }\n }\n\n async list(): Promise<WorkflowListItem[]> {\n return this.client.listWorkflows()\n }\n\n async activate(id: string): Promise<void> {\n await this.client.activateWorkflow(id)\n }\n\n async deactivate(id: string): Promise<void> {\n await this.client.deactivateWorkflow(id)\n }\n\n async delete(id: string, options: DeleteOptions): Promise<void> {\n if (options.confirm !== true) {\n throw new GuardError('delete() requires { confirm: true } to prevent accidental deletion')\n }\n await this.client.deleteWorkflow(id)\n }\n\n async executions(workflowId?: string, filter?: ExecutionFilter): Promise<ExecutionSummary[]> {\n return this.client.getExecutions(workflowId, filter)\n }\n\n async execution(id: string): Promise<ExecutionDetail> {\n return this.client.getExecution(id)\n }\n\n async listTags(): Promise<Tag[]> {\n return this.client.listTags()\n }\n\n async createTag(name: string): Promise<Tag> {\n return this.client.createTag(name)\n }\n\n async tag(workflowId: string, tagIds: string[]): Promise<void> {\n await this.client.tagWorkflow(workflowId, tagIds)\n }\n\n async untag(workflowId: string, tagIds: string[]): Promise<void> {\n await this.client.untagWorkflow(workflowId, tagIds)\n }\n\n async smokeTest(workflowId: string, workflow: N8nWorkflow): Promise<SmokeTestResult> {\n const start = Date.now()\n const trigger = this.detectTrigger(workflow)\n\n if (trigger.type === 'unsupported') {\n return { status: 'not-applicable', triggerType: 'not-applicable' }\n }\n\n if (trigger.type === 'manual') {\n let executionId: string\n try {\n executionId = await this.client.triggerManual(workflowId)\n } catch (err) {\n return { status: 'error', triggerType: 'manual', durationMs: Date.now() - start, error: String(err) }\n }\n try {\n const execution = await this.pollExecution(executionId)\n const durationMs = Date.now() - start\n if (execution.status === 'success') {\n return { status: 'passed', triggerType: 'manual', executionId, durationMs }\n }\n return {\n status: 'failed',\n triggerType: 'manual',\n executionId,\n durationMs,\n error: `Execution ended with status: ${execution.status}`,\n }\n } catch (err) {\n return { status: 'error', triggerType: 'manual', executionId, durationMs: Date.now() - start, error: String(err) }\n }\n }\n\n // webhook\n try {\n const statusCode = await this.client.triggerWebhookTest(trigger.path)\n const durationMs = Date.now() - start\n if (statusCode >= 200 && statusCode < 300) {\n return { status: 'passed', triggerType: 'webhook', durationMs }\n }\n return { status: 'failed', triggerType: 'webhook', durationMs, error: `Webhook returned HTTP ${statusCode}` }\n } catch (err) {\n return { status: 'error', triggerType: 'webhook', durationMs: Date.now() - start, error: String(err) }\n }\n }\n\n private detectTrigger(workflow: N8nWorkflow): TriggerInfo {\n for (const node of workflow.nodes) {\n if (node.type === 'n8n-nodes-base.manualTrigger') return { type: 'manual' }\n if (node.type === 'n8n-nodes-base.webhook') {\n const params = node.parameters as Record<string, unknown> | undefined\n const path = typeof params?.['path'] === 'string' ? params['path'] : 'webhook'\n return { type: 'webhook', path }\n }\n }\n return { type: 'unsupported' }\n }\n\n private async pollExecution(executionId: string): Promise<ExecutionDetail> {\n const deadline = Date.now() + SMOKE_TEST_TIMEOUT_MS\n for (;;) {\n const execution = await this.client.getExecution(executionId)\n if (execution.status !== 'running' && execution.status !== 'waiting') {\n return execution\n }\n const remaining = deadline - Date.now()\n if (remaining <= 0) break\n await new Promise<void>((resolve) => setTimeout(resolve, Math.min(SMOKE_TEST_POLL_INTERVAL_MS, remaining)))\n }\n throw new ProviderError(`Smoke test: execution ${executionId} did not complete within ${SMOKE_TEST_TIMEOUT_MS}ms`)\n }\n}\n","import { KairosError } from './base.js'\n\nexport class GenerationError extends KairosError {\n constructor(message: string, cause?: unknown) {\n super(message, cause)\n this.name = 'GenerationError'\n }\n}\n","import { KairosError } from './base.js'\n\nexport class ResponseParseError extends KairosError {\n constructor(message: string, cause?: unknown) {\n super(message, cause)\n this.name = 'ResponseParseError'\n }\n}\n","import { KairosError } from './base.js'\nimport type { ValidationIssue } from '../validation/types.js'\nimport type { AttemptMetadata } from '../telemetry/types.js'\n\nexport type { ValidationIssue }\n\nexport class ValidationError extends KairosError {\n constructor(\n message: string,\n public readonly issues: ValidationIssue[],\n public readonly attemptMetadata?: AttemptMetadata[],\n public readonly warnedRules?: number[],\n ) {\n super(message)\n this.name = 'ValidationError'\n }\n}\n","import type { TrustLevel } from '../library/types.js'\nimport type { N8nWorkflow } from '../types/workflow.js'\n\ninterface SafetyResult {\n trustLevel: TrustLevel\n reasons: string[]\n}\n\nconst BLOCKED_NODE_TYPES = new Set([\n 'n8n-nodes-base.code',\n 'n8n-nodes-base.executeCommand',\n 'n8n-nodes-base.ssh',\n])\n\nconst REVIEW_NODE_TYPES = new Set([\n 'n8n-nodes-base.httpRequest',\n])\n\nconst SECRET_PATTERNS = [\n /sk-[a-zA-Z0-9]{20,}/,\n /ghp_[a-zA-Z0-9]{36}/,\n /xoxb-[0-9]+-[0-9]+-[a-zA-Z0-9]+/,\n /AIza[a-zA-Z0-9_-]{35}/,\n /AKIA[A-Z0-9]{16}/,\n]\n\n// Looser prefixes for scanning inside expressions where the full token may be split\n// across string concatenation (e.g. \"ghp_\" + \"sometoken...\"). We flag any expression\n// containing one of these high-signal prefixes for human review.\nconst SECRET_PREFIXES = ['sk-', 'ghp_', 'xoxb-', 'AIza', 'AKIA']\n\nfunction collectExpressionStrings(obj: unknown, out: string[] = []): string[] {\n if (typeof obj === 'string') {\n if (obj.includes('={{')) out.push(obj)\n } else if (Array.isArray(obj)) {\n for (const item of obj) collectExpressionStrings(item, out)\n } else if (obj !== null && typeof obj === 'object') {\n for (const val of Object.values(obj as Record<string, unknown>)) {\n collectExpressionStrings(val, out)\n }\n }\n return out\n}\n\nexport function assessTemplateSafety(workflow: N8nWorkflow): SafetyResult {\n const reasons: string[] = []\n let worst: TrustLevel = 'safe'\n\n const escalate = (level: TrustLevel, reason: string) => {\n reasons.push(reason)\n if (level === 'blocked') worst = 'blocked'\n else if (level === 'review' && worst === 'safe') worst = 'review'\n }\n\n for (const node of workflow.nodes) {\n if (BLOCKED_NODE_TYPES.has(node.type)) {\n escalate('blocked', `Contains ${node.type} node \"${node.name}\"`)\n }\n\n if (REVIEW_NODE_TYPES.has(node.type)) {\n escalate('review', `Contains ${node.type} node \"${node.name}\"`)\n }\n\n // Scan serialized parameters for literal secrets\n const paramStr = JSON.stringify(node.parameters)\n for (const pattern of SECRET_PATTERNS) {\n if (pattern.test(paramStr)) {\n escalate('blocked', `Node \"${node.name}\" parameters contain a hardcoded secret`)\n break\n }\n }\n\n // Scan expression strings for split/concatenated secret prefixes\n // e.g. ={{ \"ghp_\" + variable }} won't match the full regex but the prefix is a red flag\n const expressions = collectExpressionStrings(node.parameters)\n for (const expr of expressions) {\n for (const prefix of SECRET_PREFIXES) {\n if (expr.includes(prefix)) {\n escalate('review', `Node \"${node.name}\" has an expression containing credential-like prefix \"${prefix}\"`)\n break\n }\n }\n }\n }\n\n return { trustLevel: worst, reasons }\n}\n","import type { IWorkflowLibrary, WorkflowMetadataInput } from '../library/types.js'\nimport type { N8nWorkflow, N8nNode } from '../types/workflow.js'\nimport type { ILogger } from '../utils/logger.js'\nimport type { TemplateSearchResponse, TemplateDetailResponse, SyncProgress } from './types.js'\nimport { N8nValidator } from '../validation/validator.js'\nimport { assessTemplateSafety } from './safety.js'\n\nconst N8N_TEMPLATE_API = 'https://api.n8n.io/api/templates'\nconst PAGE_SIZE = 50\nconst DELAY_BETWEEN_FETCHES_MS = 200\n\nconst DEFAULT_SETTINGS: N8nWorkflow['settings'] = {\n executionOrder: 'v1',\n saveManualExecutions: true,\n timezone: 'UTC',\n}\n\nexport interface SyncOptions {\n maxTemplates?: number\n onProgress?: (progress: SyncProgress) => void\n}\n\nexport class TemplateSyncer {\n private readonly validator: N8nValidator\n private readonly logger: ILogger\n\n constructor(\n private readonly library: IWorkflowLibrary,\n logger: ILogger,\n ) {\n this.validator = new N8nValidator()\n this.logger = logger\n }\n\n async sync(options?: SyncOptions): Promise<SyncProgress> {\n const maxTemplates = options?.maxTemplates ?? 500\n\n await this.library.initialize()\n\n const existing = await this.library.list()\n const existingSourceIds = new Set(\n existing\n .filter((w) => w.sourceKind === 'n8n-template' && w.sourceId)\n .map((w) => w.sourceId!),\n )\n\n const progress: SyncProgress = {\n total: 0,\n processed: 0,\n saved: 0,\n skippedPaid: 0,\n skippedDuplicate: 0,\n blocked: 0,\n reviewed: 0,\n }\n\n const templateIds = await this.fetchTemplateIds(maxTemplates, progress)\n\n for (const id of templateIds) {\n if (existingSourceIds.has(String(id))) {\n progress.skippedDuplicate++\n progress.processed++\n options?.onProgress?.(progress)\n continue\n }\n\n try {\n await this.processTemplate(id, progress)\n } catch (err) {\n this.logger.warn(`Failed to process template ${id}`, { err: String(err) })\n }\n\n progress.processed++\n options?.onProgress?.(progress)\n\n await new Promise((resolve) => setTimeout(resolve, DELAY_BETWEEN_FETCHES_MS))\n }\n\n return progress\n }\n\n private async fetchWithBackoff(url: string, maxRetries = 3): Promise<Response> {\n let delayMs = DELAY_BETWEEN_FETCHES_MS\n let lastResponse!: Response\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n lastResponse = await fetch(url)\n if (lastResponse.status !== 429 && lastResponse.status !== 503) return lastResponse\n if (attempt === maxRetries) break\n const retryAfterHeader = lastResponse.headers.get('Retry-After')\n const waitMs = retryAfterHeader ? parseInt(retryAfterHeader, 10) * 1000 : delayMs * Math.pow(2, attempt)\n this.logger.warn(`HTTP ${lastResponse.status} from template API, retrying in ${waitMs}ms`, { url, attempt })\n await new Promise((resolve) => setTimeout(resolve, waitMs))\n }\n return lastResponse\n }\n\n private async fetchTemplateIds(max: number, progress: SyncProgress): Promise<number[]> {\n const ids: number[] = []\n let page = 1\n\n while (ids.length < max) {\n const url = `${N8N_TEMPLATE_API}/search?page=${page}&rows=${PAGE_SIZE}`\n const response = await this.fetchWithBackoff(url)\n if (!response.ok) break\n\n const data = (await response.json()) as TemplateSearchResponse\n progress.total = Math.min(data.totalWorkflows, max)\n\n for (const template of data.workflows) {\n if (ids.length >= max) break\n if (template.price && template.price > 0) {\n progress.skippedPaid++\n continue\n }\n ids.push(template.id)\n }\n\n if (data.workflows.length < PAGE_SIZE) break\n page++\n\n await new Promise((resolve) => setTimeout(resolve, DELAY_BETWEEN_FETCHES_MS))\n }\n\n return ids\n }\n\n private async processTemplate(id: number, progress: SyncProgress): Promise<void> {\n const url = `${N8N_TEMPLATE_API}/workflows/${id}`\n const response = await this.fetchWithBackoff(url)\n if (!response.ok) return\n\n const data = (await response.json()) as TemplateDetailResponse\n const templateMeta = data.workflow\n const rawWorkflow = templateMeta.workflow\n\n if (!rawWorkflow?.nodes?.length) return\n\n const workflow: N8nWorkflow = {\n name: templateMeta.name,\n nodes: rawWorkflow.nodes.filter((n) => n.type && n.name) as N8nNode[],\n connections: rawWorkflow.connections as N8nWorkflow['connections'],\n settings: rawWorkflow.settings\n ? { executionOrder: 'v1' as const, ...rawWorkflow.settings }\n : { ...DEFAULT_SETTINGS },\n }\n\n const validation = this.validator.validate(workflow)\n const validationErrors = validation.issues.filter((i) => i.severity === 'error')\n\n if (validationErrors.length > 0) {\n progress.blocked++\n this.logger.debug(`Template ${id} blocked: ${validationErrors.length} validation errors`)\n return\n }\n\n const safety = assessTemplateSafety(workflow)\n\n if (safety.trustLevel === 'blocked') {\n progress.blocked++\n this.logger.debug(`Template ${id} blocked: ${safety.reasons.join(', ')}`)\n return\n }\n\n if (safety.trustLevel === 'review') {\n progress.reviewed++\n }\n\n const description = this.cleanDescription(templateMeta.description)\n\n const autoTags = Array.from(new Set(\n workflow.nodes.flatMap((n) => {\n const bare = n.type.split('.').pop() ?? ''\n const tags = [bare]\n if (n.type.includes('Trigger') || n.type.includes('trigger')) tags.push(`trigger:${bare}`)\n if (n.type.includes('langchain')) tags.push('ai')\n return tags\n }),\n ))\n\n const metadata: WorkflowMetadataInput = {\n description,\n tags: autoTags,\n sourceKind: 'n8n-template',\n sourceId: String(id),\n sourceUrl: `https://n8n.io/workflows/${id}`,\n trustLevel: safety.trustLevel,\n }\n\n await this.library.save(workflow, metadata)\n progress.saved++\n this.logger.debug(`Template ${id} saved: \"${templateMeta.name}\" (${safety.trustLevel})`)\n }\n\n private cleanDescription(raw: string): string {\n return raw\n .replace(/#{1,6}\\s*/g, '')\n .replace(/\\*{1,2}([^*]+)\\*{1,2}/g, '$1')\n .replace(/\\[([^\\]]+)\\]\\([^)]+\\)/g, '$1')\n .replace(/\\n{3,}/g, '\\n\\n')\n .trim()\n .slice(0, 500)\n }\n}\n"],"mappings":"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","names":[]}
@@ -2,7 +2,7 @@ import {
2
2
  RULE_EXAMPLES,
3
3
  RULE_MITIGATIONS,
4
4
  scoreToMode
5
- } from "./chunk-6IXW3WCC.js";
5
+ } from "./chunk-KIFT5LA7.js";
6
6
 
7
7
  // src/generation/prompt-builder.ts
8
8
  import { readFileSync } from "fs";
@@ -208,6 +208,14 @@ Cron: { "rule": { "interval": [{ "field": "cronExpression", "expression": "0 9 *
208
208
  8. No deprecated $node["NodeName"].json \u2014 use $('NodeName').item.json.field
209
209
  9. No $json.items[0] array indexing \u2014 access fields directly as $json.field
210
210
  10. No bare $('NodeName').json \u2014 always use .first().json.field or .all()
211
+ 11. httpRequest URL is a real endpoint (not "example.com" or "YOUR_URL")
212
+ 12. code nodes contain actual logic \u2014 not empty or comment-only
213
+ 13. Slack message nodes have a channel specified (channelId or channel)
214
+ 14. Gmail send nodes have a recipient (to field non-empty)
215
+ 15. if nodes have at least one condition in conditions.conditions[]
216
+ 16. set nodes have at least one entry in assignments.assignments[]
217
+ 17. scheduleTrigger has at least one rule in rule.interval[]
218
+ 18. webhook path is relative (no spaces, no leading slash, no http://)
211
219
 
212
220
  ---
213
221
 
@@ -243,18 +251,37 @@ var PromptBuilder = class {
243
251
  }
244
252
  build(request, matches, globalFailureRates = [], dynamicCatalog) {
245
253
  const mode = this.resolveMode(matches);
246
- const system = this.buildSystem(matches, mode, globalFailureRates, dynamicCatalog);
254
+ const system = this.buildSystem(matches, mode, globalFailureRates, dynamicCatalog, request.description);
247
255
  const userMessage = this.buildUserMessage(request, matches, mode);
248
256
  return { system, userMessage, mode, matches };
249
257
  }
250
- buildCorrectionMessage(request, matches, allIssues, attempt) {
258
+ buildCorrectionMessage(request, matches, allIssues, attempt, failingRuleIds) {
251
259
  const base = this.buildUserMessage(request, matches, this.resolveMode(matches));
260
+ let examplesSection = "";
261
+ if (failingRuleIds && failingRuleIds.length > 0) {
262
+ const uniqueRules = [...new Set(failingRuleIds)];
263
+ const exampleLines = [];
264
+ for (const rule of uniqueRules) {
265
+ const ex = RULE_EXAMPLES[rule];
266
+ if (ex) {
267
+ exampleLines.push(`Rule ${rule}:
268
+ Bad: ${ex.bad}
269
+ Good: ${ex.good}`);
270
+ }
271
+ }
272
+ if (exampleLines.length > 0) {
273
+ examplesSection = `
274
+
275
+ ## Concrete Fix Examples
276
+ ${exampleLines.join("\n\n")}`;
277
+ }
278
+ }
252
279
  return `${base}
253
280
 
254
281
  IMPORTANT: A previous generation attempt (attempt ${attempt}) failed validation with these issues:
255
282
  ${allIssues.join("\n")}
256
283
 
257
- Fix ALL of the above issues in your new response. Do not repeat any of these mistakes.`;
284
+ Fix ALL of the above issues in your new response. Do not repeat any of these mistakes.${examplesSection}`;
258
285
  }
259
286
  resolveMode(matches) {
260
287
  if (matches.length === 0) return "scratch";
@@ -262,7 +289,7 @@ Fix ALL of the above issues in your new response. Do not repeat any of these mis
262
289
  if (!top) return "scratch";
263
290
  return scoreToMode(top.score);
264
291
  }
265
- buildSystem(matches, mode, globalFailureRates = [], dynamicCatalog) {
292
+ buildSystem(matches, mode, globalFailureRates = [], dynamicCatalog, description) {
266
293
  let basePrompt = SYSTEM_PROMPT_V1;
267
294
  if (dynamicCatalog) {
268
295
  basePrompt = basePrompt.replace(
@@ -322,7 +349,7 @@ A loosely similar workflow (score: ${hint.score.toFixed(2)}) used these node typ
322
349
  });
323
350
  }
324
351
  }
325
- const warnings = this.buildFailureWarnings(matches, globalFailureRates);
352
+ const warnings = this.buildFailureWarnings(matches, globalFailureRates, description);
326
353
  if (warnings) {
327
354
  blocks.push({ type: "text", text: warnings });
328
355
  }
@@ -349,15 +376,34 @@ A loosely similar workflow (score: ${hint.score.toFixed(2)}) used these node typ
349
376
  const patterns = this._lastActivePatterns ?? this.getActivePatterns(this.resolveMaxPatterns());
350
377
  return patterns.map((p) => p.rule);
351
378
  }
352
- getActivePatterns(maxCount = 10) {
379
+ getActivePatterns(maxCount = 10, description) {
353
380
  const all = this.loadPatterns().filter((p) => p.state !== "resolved" && p.confidence > 0);
354
381
  const regressed = all.filter((p) => p.regressed).sort((a, b) => b.compositeScore - a.compositeScore);
355
382
  const confirmed = all.filter((p) => !p.regressed && p.state === "confirmed").sort((a, b) => b.compositeScore - a.compositeScore);
356
383
  const drafts = all.filter((p) => !p.regressed && p.state !== "confirmed").sort((a, b) => b.compositeScore - a.compositeScore);
357
- return [...regressed, ...confirmed, ...drafts].slice(0, maxCount);
384
+ const ordered = [...regressed, ...confirmed, ...drafts];
385
+ if (this.profile === "minimal" && description) {
386
+ return this.rankByRelevance(ordered, description).slice(0, maxCount);
387
+ }
388
+ return ordered.slice(0, maxCount);
389
+ }
390
+ rankByRelevance(patterns, description) {
391
+ const lower = description.toLowerCase();
392
+ const STAGE_KEYWORDS = {
393
+ credential_injection: ["credential", "auth", "api key", "token", "oauth", "smtp", "imap", "password", "secret"],
394
+ connection_wiring: ["connect", "link", "wire", "chain", "merge", "branch", "join"],
395
+ expression_syntax: ["expression", "variable", "json", "field", "data", "$json", "item"],
396
+ workflow_structure: ["trigger", "webhook", "schedule", "structure", "workflow"],
397
+ node_generation: ["node", "generate", "create", "build", "send", "fetch", "email", "slack", "http"]
398
+ };
399
+ return patterns.map((p) => {
400
+ const keywords = STAGE_KEYWORDS[p.pipelineStage] ?? [];
401
+ const relevanceBoost = keywords.some((kw) => lower.includes(kw)) ? 1 : 0;
402
+ return { pattern: p, sort: relevanceBoost * 10 + p.compositeScore };
403
+ }).sort((a, b) => b.sort - a.sort).map((x) => x.pattern);
358
404
  }
359
- buildFailureWarnings(matches, globalFailureRates) {
360
- const richPatterns = this.getActivePatterns(this.resolveMaxPatterns());
405
+ buildFailureWarnings(matches, globalFailureRates, description) {
406
+ const richPatterns = this.getActivePatterns(this.resolveMaxPatterns(), description);
361
407
  this._lastActivePatterns = richPatterns;
362
408
  if (richPatterns.length > 0) {
363
409
  return this.buildStageGroupedWarnings(richPatterns, matches);
@@ -520,4 +566,4 @@ export {
520
566
  PromptBuilder,
521
567
  inferWorkflowType
522
568
  };
523
- //# sourceMappingURL=chunk-CR2NHLOH.js.map
569
+ //# sourceMappingURL=chunk-EVOAYH2K.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/generation/prompt-builder.ts","../src/generation/prompts/v1.ts","../src/utils/workflow-type.ts"],"sourcesContent":["import { readFileSync } from 'node:fs'\nimport { join } from 'node:path'\nimport { homedir } from 'node:os'\nimport type { WorkflowMatch } from '../library/types.js'\nimport type { RuleFailureRate } from '../telemetry/reader.js'\nimport type { PatternAnalysis, Pattern } from '../telemetry/pattern-analyzer.js'\nimport type { DesignRequest, BuiltPrompt, SystemPromptBlock } from './types.js'\nimport { SYSTEM_PROMPT_V1 } from './prompts/v1.js'\nimport { scoreToMode } from '../utils/thresholds.js'\nimport { RULE_MITIGATIONS, RULE_EXAMPLES } from '../validation/rule-metadata.js'\n\nconst CRITICAL_SCORE_THRESHOLD = 0.15\n\ntype PromptProfile = 'minimal' | 'standard' | 'rich'\n\nfunction resolveProfile(): PromptProfile {\n const env = process.env['KAIROS_PROMPT_PROFILE']\n if (env === 'minimal' || env === 'standard' || env === 'rich') return env\n return 'standard'\n}\n\nconst PROACTIVE_EXPRESSION_GUIDANCE = `## Expression Syntax Quick Reference\\n\\nAlways use these patterns in expressions:\\n- Access node data: $('NodeName').item.json.field (not $node[\"NodeName\"].json)\\n- Access JSON field: $json.field (not $json.items[0].field)\\n- Single item: $('NodeName').first().json.field\\n- All items: $('NodeName').all()`\n\nexport class PromptBuilder {\n private readonly patternsPath: string\n private readonly profile: PromptProfile\n private _lastActivePatterns: Pattern[] | null = null\n\n constructor(patternsPath?: string, profile?: PromptProfile) {\n this.patternsPath = patternsPath ?? join(homedir(), '.kairos', 'patterns.json')\n this.profile = profile ?? resolveProfile()\n }\n\n private resolveMaxPatterns(): number {\n if (this.profile === 'minimal') return 3\n if (this.profile === 'rich') return 15\n return 10\n }\n\n build(request: DesignRequest, matches: WorkflowMatch[], globalFailureRates: RuleFailureRate[] = [], dynamicCatalog?: string): BuiltPrompt {\n const mode = this.resolveMode(matches)\n const system = this.buildSystem(matches, mode, globalFailureRates, dynamicCatalog, request.description)\n const userMessage = this.buildUserMessage(request, matches, mode)\n return { system, userMessage, mode, matches }\n }\n\n buildCorrectionMessage(\n request: DesignRequest,\n matches: WorkflowMatch[],\n allIssues: string[],\n attempt: number,\n failingRuleIds?: number[],\n ): string {\n const base = this.buildUserMessage(request, matches, this.resolveMode(matches))\n\n let examplesSection = ''\n if (failingRuleIds && failingRuleIds.length > 0) {\n const uniqueRules = [...new Set(failingRuleIds)]\n const exampleLines: string[] = []\n for (const rule of uniqueRules) {\n const ex = RULE_EXAMPLES[rule]\n if (ex) {\n exampleLines.push(`Rule ${rule}:\\n Bad: ${ex.bad}\\n Good: ${ex.good}`)\n }\n }\n if (exampleLines.length > 0) {\n examplesSection = `\\n\\n## Concrete Fix Examples\\n${exampleLines.join('\\n\\n')}`\n }\n }\n\n return `${base}\n\nIMPORTANT: A previous generation attempt (attempt ${attempt}) failed validation with these issues:\n${allIssues.join('\\n')}\n\nFix ALL of the above issues in your new response. Do not repeat any of these mistakes.${examplesSection}`\n }\n\n private resolveMode(matches: WorkflowMatch[]): 'direct' | 'reference' | 'scratch' {\n if (matches.length === 0) return 'scratch'\n const top = matches[0]\n if (!top) return 'scratch'\n return scoreToMode(top.score)\n }\n\n private buildSystem(matches: WorkflowMatch[], mode: 'direct' | 'reference' | 'scratch', globalFailureRates: RuleFailureRate[] = [], dynamicCatalog?: string, description?: string): SystemPromptBlock[] {\n let basePrompt = SYSTEM_PROMPT_V1\n if (dynamicCatalog) {\n basePrompt = basePrompt.replace(\n /## NODE CATALOG — exact type strings and safe typeVersions[\\s\\S]*?(?=## PRE-DELIVERY SELF-CHECK)/,\n dynamicCatalog + '\\n\\n',\n )\n }\n\n const blocks: SystemPromptBlock[] = [\n {\n type: 'text',\n text: basePrompt,\n cache_control: { type: 'ephemeral' },\n },\n ]\n\n if (this.profile !== 'minimal') {\n if (mode === 'reference' && matches.length > 0) {\n const refText = matches\n .slice(0, 3)\n .map((m) => {\n const nodes = m.workflow.workflow.nodes\n .map((n) => ` - ${n.name} (${n.type} v${n.typeVersion})`)\n .join('\\n')\n return `Reference workflow: \"${m.workflow.description}\" (similarity: ${m.score.toFixed(2)})\\nNodes:\\n${nodes}`\n })\n .join('\\n\\n')\n\n blocks.push({\n type: 'text',\n text: `## Similar Workflows From Library (for reference only — adapt, do not copy verbatim)\\n\\n${refText}`,\n })\n }\n\n if (mode === 'direct' && matches[0]) {\n const match = matches[0]\n const json = JSON.stringify(match.workflow.workflow, null, 2)\n if (json.length > 30_000) {\n const nodes = match.workflow.workflow.nodes\n .map((n) => ` - ${n.name} (${n.type} v${n.typeVersion})`)\n .join('\\n')\n blocks.push({\n type: 'text',\n text: `## Closely Matched Workflow (score: ${match.score.toFixed(2)}) — too large for full JSON, using reference:\\nNodes:\\n${nodes}`,\n })\n } else {\n blocks.push({\n type: 'text',\n text: `## Closely Matched Workflow (score: ${match.score.toFixed(2)}) — adapt this structure:\\n\\n${json}`,\n })\n }\n }\n\n if (mode === 'scratch' && matches.length > 0 && matches[0]!.score >= 0.40) {\n const hint = matches[0]!\n const nodeTypes = hint.workflow.workflow.nodes.map((n) => n.type.split('.').pop()).join(', ')\n blocks.push({\n type: 'text',\n text: `## Weak Structural Hint\\nA loosely similar workflow (score: ${hint.score.toFixed(2)}) used these node types: ${nodeTypes}`,\n })\n }\n }\n\n const warnings = this.buildFailureWarnings(matches, globalFailureRates, description)\n if (warnings) {\n blocks.push({ type: 'text', text: warnings })\n }\n\n if (this.profile === 'rich') {\n const expressionRules = new Set([24, 25, 26])\n const expressionAlreadyCovered = (this._lastActivePatterns ?? []).some(p => expressionRules.has(p.rule))\n if (!expressionAlreadyCovered) {\n blocks.push({ type: 'text', text: PROACTIVE_EXPRESSION_GUIDANCE })\n }\n }\n\n return blocks\n }\n\n private loadPatterns(): Pattern[] {\n try {\n const raw = readFileSync(this.patternsPath, 'utf-8')\n const analysis = JSON.parse(raw) as PatternAnalysis\n const patterns = analysis.topFailureRules ?? []\n return patterns.filter(p => typeof p.pipelineStage === 'string' && typeof p.state === 'string')\n } catch {\n return []\n }\n }\n\n getWarnedRules(): number[] {\n const patterns = this._lastActivePatterns ?? this.getActivePatterns(this.resolveMaxPatterns())\n return patterns.map(p => p.rule)\n }\n\n private getActivePatterns(maxCount = 10, description?: string): Pattern[] {\n const all = this.loadPatterns()\n .filter(p => p.state !== 'resolved' && p.confidence > 0)\n\n const regressed = all.filter(p => p.regressed).sort((a, b) => b.compositeScore - a.compositeScore)\n const confirmed = all.filter(p => !p.regressed && p.state === 'confirmed').sort((a, b) => b.compositeScore - a.compositeScore)\n const drafts = all.filter(p => !p.regressed && p.state !== 'confirmed').sort((a, b) => b.compositeScore - a.compositeScore)\n\n const ordered = [...regressed, ...confirmed, ...drafts]\n\n if (this.profile === 'minimal' && description) {\n return this.rankByRelevance(ordered, description).slice(0, maxCount)\n }\n\n return ordered.slice(0, maxCount)\n }\n\n private rankByRelevance(patterns: Pattern[], description: string): Pattern[] {\n const lower = description.toLowerCase()\n const STAGE_KEYWORDS: Record<string, string[]> = {\n credential_injection: ['credential', 'auth', 'api key', 'token', 'oauth', 'smtp', 'imap', 'password', 'secret'],\n connection_wiring: ['connect', 'link', 'wire', 'chain', 'merge', 'branch', 'join'],\n expression_syntax: ['expression', 'variable', 'json', 'field', 'data', '$json', 'item'],\n workflow_structure: ['trigger', 'webhook', 'schedule', 'structure', 'workflow'],\n node_generation: ['node', 'generate', 'create', 'build', 'send', 'fetch', 'email', 'slack', 'http'],\n }\n\n return patterns\n .map(p => {\n const keywords = STAGE_KEYWORDS[p.pipelineStage] ?? []\n const relevanceBoost = keywords.some(kw => lower.includes(kw)) ? 1 : 0\n return { pattern: p, sort: relevanceBoost * 10 + p.compositeScore }\n })\n .sort((a, b) => b.sort - a.sort)\n .map(x => x.pattern)\n }\n\n private buildFailureWarnings(matches: WorkflowMatch[], globalFailureRates: RuleFailureRate[], description?: string): string | null {\n const richPatterns = this.getActivePatterns(this.resolveMaxPatterns(), description)\n this._lastActivePatterns = richPatterns\n\n if (richPatterns.length > 0) {\n return this.buildStageGroupedWarnings(richPatterns, matches)\n }\n\n return this.buildLegacyWarnings(matches, globalFailureRates)\n }\n\n private buildStageGroupedWarnings(patterns: Pattern[], matches: WorkflowMatch[]): string | null {\n const stageLabels: Record<string, string> = {\n credential_injection: 'CREDENTIAL FORMATTING',\n connection_wiring: 'CONNECTION WIRING',\n node_generation: 'NODE GENERATION',\n workflow_structure: 'WORKFLOW STRUCTURE',\n expression_syntax: 'EXPRESSION SYNTAX',\n }\n\n const byStage = new Map<string, Pattern[]>()\n for (const p of patterns) {\n const list = byStage.get(p.pipelineStage) ?? []\n list.push(p)\n byStage.set(p.pipelineStage, list)\n }\n\n const sections: string[] = []\n for (const [stage, stagePatterns] of byStage) {\n const label = stageLabels[stage] ?? stage\n\n const byMitigation = new Map<string, Pattern[]>()\n for (const p of stagePatterns) {\n const key = p.mitigation ?? `rule_${p.rule}`\n const list = byMitigation.get(key) ?? []\n list.push(p)\n byMitigation.set(key, list)\n }\n\n const lines: string[] = []\n for (const group of byMitigation.values()) {\n if (group.length === 1) {\n const p = group[0]!\n const urgency = p.regressed ? 'CRITICAL REGRESSION: ' : (p.compositeScore ?? 0) >= CRITICAL_SCORE_THRESHOLD ? 'CRITICAL: ' : ''\n const statePrefix = p.state === 'confirmed' ? '[CONFIRMED] ' : ''\n const trendSuffix = p.trend === 'worsening' ? ' (GETTING WORSE)' : p.trend === 'improving' ? ' (improving)' : ''\n const remedy = p.mitigation ?? RULE_MITIGATIONS[p.rule]\n const remedyStr = remedy ? `\\n Fix: ${remedy}` : ''\n const ex = RULE_EXAMPLES[p.rule]\n const exampleStr = ex ? `\\n Bad: ${ex.bad}\\n Good: ${ex.good}` : ''\n lines.push(`- ${urgency}${statePrefix}Rule ${p.rule}${trendSuffix}: ${p.exampleMessages[0] ?? 'No example'}${remedyStr}${exampleStr}`)\n } else {\n const ruleNums = group.map(p => p.rule).join(', ')\n const totalFailures = group.reduce((s, p) => s + p.failureCount, 0)\n const hasConfirmed = group.some(p => p.state === 'confirmed')\n const statePrefix = hasConfirmed ? '[CONFIRMED] ' : ''\n const remedy = group[0]!.mitigation\n const remedyStr = remedy ? `\\n Fix: ${remedy}` : ''\n lines.push(`- ${statePrefix}Rules ${ruleNums} (${totalFailures} failures combined): same root cause${remedyStr}`)\n }\n }\n sections.push(`### ${label}\\n${lines.join('\\n')}`)\n }\n\n for (const match of matches) {\n const fps = match.workflow.failurePatterns\n if (!fps?.length) continue\n const coveredRules = new Set(patterns.map(p => p.rule))\n const extra = fps.filter(fp => !coveredRules.has(fp.rule))\n for (const fp of extra) {\n const remedy = RULE_MITIGATIONS[fp.rule]\n const remedyStr = remedy ? ` — Fix: ${remedy}` : ''\n sections.push(`- Rule ${fp.rule}: \"${fp.message}\"${remedyStr} (seen in similar workflows)`)\n }\n }\n\n if (sections.length === 0) return null\n\n return `## Known Failure Patterns — AVOID THESE\\n\\nGrouped by generation stage. Fix these BEFORE outputting your response:\\n\\n${sections.join('\\n\\n')}`\n }\n\n private buildLegacyWarnings(matches: WorkflowMatch[], globalFailureRates: RuleFailureRate[]): string | null {\n const lines: string[] = []\n\n for (const match of matches) {\n const patterns = match.workflow.failurePatterns\n if (!patterns?.length) continue\n for (const fp of patterns) {\n const remedy = RULE_MITIGATIONS[fp.rule]\n const remedyStr = remedy ? ` — Fix: ${remedy}` : ''\n lines.push(`- Rule ${fp.rule}: \"${fp.message}\"${remedyStr} (seen ${fp.occurrences}x in similar workflows)`)\n }\n }\n\n const highFreqRules = globalFailureRates.filter((r) => r.rate >= 0.15)\n for (const rule of highFreqRules) {\n const remedy = RULE_MITIGATIONS[rule.rule]\n const remedyStr = remedy ? ` — Fix: ${remedy}` : ''\n lines.push(`- Rule ${rule.rule}: \"${rule.commonMessage}\"${remedyStr} (fails in ${Math.round(rule.rate * 100)}% of all builds)`)\n }\n\n if (lines.length === 0) return null\n\n const unique = [...new Set(lines)]\n return `## Known Failure Patterns — AVOID THESE\\n\\nPrevious builds frequently failed the following validation rules. Ensure your output does NOT repeat these mistakes:\\n${unique.join('\\n')}`\n }\n\n private buildUserMessage(request: DesignRequest, _matches: WorkflowMatch[], _mode: string): string {\n const namePart = request.name ? `\\nWorkflow name: \"${request.name}\"` : ''\n return `Build a workflow that: ${request.description}${namePart}`\n }\n}\n","export const SYSTEM_PROMPT_V1 = `You are a workflow generation engine for n8n. Your only output is a generate_workflow tool call containing valid n8n workflow JSON. You never respond with prose, explanations, or markdown. If you cannot fulfill the request, set the error field in the tool call.\n\n## HARD RULES — violating any of these causes immediate deployment failure\n\n### Forbidden fields — NEVER include these in the workflow object:\nid, active, createdAt, updatedAt, versionId, meta, isArchived, activeVersionId, activeVersion, pinData, triggerCount, shared, staticData\n\n### Required top-level structure:\n{\n \"name\": \"<descriptive name>\",\n \"nodes\": [...],\n \"connections\": {...},\n \"settings\": {\n \"saveExecutionProgress\": true,\n \"saveManualExecutions\": true,\n \"saveDataErrorExecution\": \"all\",\n \"saveDataSuccessExecution\": \"all\",\n \"executionTimeout\": 3600,\n \"timezone\": \"UTC\",\n \"executionOrder\": \"v1\"\n }\n}\n\n### Node IDs:\n- Every node.id must be a valid UUID v4 (random hex, format: xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx)\n- Never reuse IDs, never use sequential fake IDs like \"node-1\"\n\n### Credentials:\n- Each credential is keyed by its type string, with an object value containing id and name:\n \"credentials\": { \"slackOAuth2Api\": { \"id\": \"placeholder-id\", \"name\": \"My Slack Credential\" } }\n- Use \"placeholder-id\" as the id — users replace this with their real credential ID from n8n after deployment\n- The credentialsNeeded field in your response declares what credentials the user must configure\n- Never put API keys or tokens directly in node parameters when a credential type exists\n\n### Node names:\n- All node names must be unique within the workflow\n- Use descriptive names: \"Fetch Open Invoices\" not \"HTTP Request 2\"\n\n### Positioning:\n- Trigger node: [250, 300]\n- Each subsequent step: x + 220 minimum\n- Parallel branches: offset y by ±150\n- AI sub-nodes: place below their root node (y + 200)\n\n---\n\n## CONNECTION RULES — the most common source of errors\n\n### Standard connections (main data flow):\n\"NodeA\": { \"main\": [ [ { \"node\": \"NodeB\", \"type\": \"main\", \"index\": 0 } ] ] }\n\n### AI connections — CRITICAL: the SUB-NODE is the SOURCE, NOT the agent/chain:\n\"OpenAI Chat Model\": { \"ai_languageModel\": [ [ { \"node\": \"AI Agent\", \"type\": \"ai_languageModel\", \"index\": 0 } ] ] }\n\"Simple Memory\": { \"ai_memory\": [ [ { \"node\": \"AI Agent\", \"type\": \"ai_memory\", \"index\": 0 } ] ] }\n\"Calculator Tool\": { \"ai_tool\": [ [ { \"node\": \"AI Agent\", \"type\": \"ai_tool\", \"index\": 0 } ] ] }\n\nThe AI Agent node does NOT appear in connections as a source for ai_* types.\nEvery AI Agent must have at least one ai_languageModel sub-node connected.\n\n### IF node — two output ports (0 = true, 1 = false):\n\"IF Check\": { \"main\": [ [{ \"node\": \"True Path\", \"type\": \"main\", \"index\": 0 }], [{ \"node\": \"False Path\", \"type\": \"main\", \"index\": 0 }] ] }\n\n### SplitInBatches — two output ports (0 = done/finished, 1 = loop body per batch):\nConnect output 0 to the node that runs AFTER all batches complete.\nConnect output 1 to the processing chain for each batch. The last node in the chain loops back to SplitInBatches via main input.\n\n### Webhook + RespondToWebhook pattern:\nWhen webhook responseMode is \"responseNode\", you MUST include a respondToWebhook node in the flow.\n\"Webhook\": { \"main\": [[{ \"node\": \"Process Data\", \"type\": \"main\", \"index\": 0 }]] }\n\"Process Data\": { \"main\": [[{ \"node\": \"Respond to Webhook\", \"type\": \"main\", \"index\": 0 }]] }\n\n### Triggers have no incoming connections.\n### Connection keys are NODE NAMES, never node IDs.\n\n### Nested parameters:\nNode parameters like conditions, assignments, and rule intervals MUST include all required nested fields. Do not leave nested objects empty or partially filled.\n\n---\n\n## EXPRESSION SYNTAX — how to reference upstream node data\n\n### Accessing a field from an upstream node:\n- CORRECT: $('NodeName').item.json.field\n- WRONG: $node[\"NodeName\"].json.field ← deprecated accessor, fails at runtime (Rule 24)\n\n### Accessing array items from $json:\n- CORRECT: $json.field ← n8n auto-flattens items; each item is already a flat object\n- WRONG: $json.items[0].field ← do not index into items[] (Rule 25)\n\n### Calling node data — always qualify with .first() or .all():\n- CORRECT: $('NodeName').first().json.field ← single item\n- CORRECT: $('NodeName').all() ← array of all items\n- WRONG: $('NodeName').json ← throws at runtime without .first() or .all() (Rule 26)\n\n---\n\n## NODE CATALOG — exact type strings and safe typeVersions\n\n### Triggers (always at least one required):\nn8n-nodes-base.manualTrigger typeVersion: 1 — testing only\nn8n-nodes-base.scheduleTrigger typeVersion: 1.2 — params: rule.interval[{field, ...}]\nn8n-nodes-base.webhook typeVersion: 2 — params: httpMethod, path, responseMode\nn8n-nodes-base.formTrigger typeVersion: 2.2\nn8n-nodes-base.emailReadImap typeVersion: 2 — cred: imap\nn8n-nodes-base.errorTrigger typeVersion: 1\nn8n-nodes-base.executeWorkflowTrigger typeVersion: 1.1\nn8n-nodes-base.gmailTrigger typeVersion: 1.2 — cred: gmailOAuth2\nn8n-nodes-base.slackTrigger typeVersion: 1 — cred: slackApi\nn8n-nodes-base.telegramTrigger typeVersion: 1.2 — cred: telegramApi\nn8n-nodes-base.githubTrigger typeVersion: 1 — cred: githubApi\nn8n-nodes-base.airtableTrigger typeVersion: 1 — cred: airtableTokenApi\nn8n-nodes-base.notionTrigger typeVersion: 1 — cred: notionApi\n@n8n/n8n-nodes-langchain.chatTrigger typeVersion: 1.1 — pairs with AI Agent\n\n### Core logic:\nn8n-nodes-base.code typeVersion: 2 — params: mode, jsCode\nn8n-nodes-base.httpRequest typeVersion: 4.2 — params: method, url, [sendBody, jsonBody, sendHeaders, headerParameters]\nn8n-nodes-base.set typeVersion: 3.4 — params: assignments.assignments[{id, name, value, type}]\nn8n-nodes-base.if typeVersion: 2.2 — params: conditions.conditions[{id, leftValue, rightValue, operator}], combinator\nn8n-nodes-base.switch typeVersion: 3.2 — multi-branch routing\nn8n-nodes-base.filter typeVersion: 2.2 — params: conditions (same as IF), 1 output\nn8n-nodes-base.merge typeVersion: 3 — modes: append/combine/chooseBranch\nn8n-nodes-base.splitInBatches typeVersion: 3 — output 0=done, output 1=loop body\nn8n-nodes-base.wait typeVersion: 1.1\nn8n-nodes-base.executeWorkflow typeVersion: 1.2\nn8n-nodes-base.respondToWebhook typeVersion: 1.1 — required when webhook responseMode is \"responseNode\"\nn8n-nodes-base.noOp typeVersion: 1\nn8n-nodes-base.splitOut typeVersion: 1\nn8n-nodes-base.aggregate typeVersion: 1\nn8n-nodes-base.stickyNote typeVersion: 1 — never connected, canvas annotation only\n\n### Email / messaging:\nn8n-nodes-base.emailSend typeVersion: 2.1 — cred: smtp\nn8n-nodes-base.slack typeVersion: 2.2 — cred: slackOAuth2Api — params: resource, operation, select, channelId{__rl}, text\nn8n-nodes-base.telegram typeVersion: 1.2 — cred: telegramApi\nn8n-nodes-base.discord typeVersion: 2 — cred: discordWebhookApi\n\n### Google:\nn8n-nodes-base.gmail typeVersion: 2.1 — cred: gmailOAuth2 — params: resource, operation\nn8n-nodes-base.googleSheets typeVersion: 4.5 — cred: googleSheetsOAuth2Api — params: resource, operation, documentId{__rl}, sheetName{__rl}\nn8n-nodes-base.googleDrive typeVersion: 3 — cred: googleDriveOAuth2Api\nn8n-nodes-base.googleCalendar typeVersion: 1.3 — cred: googleCalendarOAuth2Api\n\n### Productivity:\nn8n-nodes-base.notion typeVersion: 2.2 — cred: notionApi\nn8n-nodes-base.airtable typeVersion: 2.1 — cred: airtableTokenApi\nn8n-nodes-base.github typeVersion: 1.1 — cred: githubApi\nn8n-nodes-base.jira typeVersion: 1 — cred: jiraSoftwareCloudApi\nn8n-nodes-base.hubspot typeVersion: 2.1 — cred: hubspotOAuth2Api\n\n### Databases:\nn8n-nodes-base.postgres typeVersion: 2.5 — cred: postgres\nn8n-nodes-base.mySql typeVersion: 2.4 — cred: mySql\nn8n-nodes-base.redis typeVersion: 1 — cred: redis\nn8n-nodes-base.supabase typeVersion: 1 — cred: supabaseApi\nn8n-nodes-base.awsS3 typeVersion: 2 — cred: aws\n\n### AI — Root nodes (sit on main data flow, receive ai_* connections as TARGETS):\n@n8n/n8n-nodes-langchain.agent typeVersion: 1.9 — params: promptType, text (if define), options.systemMessage\n@n8n/n8n-nodes-langchain.chainLlm typeVersion: 1.5\n@n8n/n8n-nodes-langchain.chainRetrievalQa typeVersion: 1.4\n@n8n/n8n-nodes-langchain.openAi typeVersion: 1.8 — cred: openAiApi — standalone node, calls OpenAI directly without sub-nodes\n@n8n/n8n-nodes-langchain.anthropic typeVersion: 1 — cred: anthropicApi — standalone node, calls Anthropic directly without sub-nodes\n\n### AI — Sub-nodes (sources of ai_* connections, wire INTO root nodes above):\n@n8n/n8n-nodes-langchain.lmChatOpenAi typeVersion: 1.7 — cred: openAiApi — ai_languageModel — use with agent/chain, NOT standalone\n@n8n/n8n-nodes-langchain.lmChatAnthropic typeVersion: 1.3 — cred: anthropicApi — ai_languageModel — use with agent/chain, NOT standalone\n@n8n/n8n-nodes-langchain.lmChatGoogleGemini typeVersion: 1 — cred: googlePalmApi — ai_languageModel\n@n8n/n8n-nodes-langchain.memoryBufferWindow typeVersion: 1.3 — — ai_memory\n@n8n/n8n-nodes-langchain.toolWorkflow typeVersion: 2 — — ai_tool\n@n8n/n8n-nodes-langchain.toolCode typeVersion: 1.1 — — ai_tool\n@n8n/n8n-nodes-langchain.toolHttpRequest typeVersion: 1.1 — — ai_tool\n@n8n/n8n-nodes-langchain.toolCalculator typeVersion: 1 — — ai_tool\n\n### Resource locator (__rl) format (Google / Slack / Notion modern nodes):\n{ \"__rl\": true, \"mode\": \"id\", \"value\": \"ACTUAL_ID\" }\n{ \"__rl\": true, \"mode\": \"name\", \"value\": \"#channel-name\" }\n\n### App node parameter pattern:\n{ \"resource\": \"message\", \"operation\": \"send\", ...operation-specific fields }\n\n### Schedule Trigger — daily at 9am example:\n{ \"rule\": { \"interval\": [{ \"field\": \"days\", \"daysInterval\": 1, \"triggerAtHour\": 9, \"triggerAtMinute\": 0 }] } }\nCron: { \"rule\": { \"interval\": [{ \"field\": \"cronExpression\", \"expression\": \"0 9 * * 1-5\" }] } }\n\n---\n\n## PRE-DELIVERY SELF-CHECK (do this before calling the tool):\n1. Every connection source/target name exists in nodes array\n2. No duplicate node names\n3. No duplicate node IDs\n4. No forbidden fields at the workflow root\n5. At least one trigger node present\n6. Every AI Agent has an ai_languageModel sub-node\n7. settings block is complete with executionOrder: \"v1\"\n8. No deprecated $node[\"NodeName\"].json — use $('NodeName').item.json.field\n9. No $json.items[0] array indexing — access fields directly as $json.field\n10. No bare $('NodeName').json — always use .first().json.field or .all()\n11. httpRequest URL is a real endpoint (not \"example.com\" or \"YOUR_URL\")\n12. code nodes contain actual logic — not empty or comment-only\n13. Slack message nodes have a channel specified (channelId or channel)\n14. Gmail send nodes have a recipient (to field non-empty)\n15. if nodes have at least one condition in conditions.conditions[]\n16. set nodes have at least one entry in assignments.assignments[]\n17. scheduleTrigger has at least one rule in rule.interval[]\n18. webhook path is relative (no spaces, no leading slash, no http://)\n\n---\n\nRespond ONLY with a generate_workflow tool call. No prose. No markdown outside the tool call.\nIf the request is impossible or unclear, set the error field instead of generating a workflow.`\n","const TYPE_KEYWORDS: Array<[string, string]> = [\n ['gmail', 'email'],\n ['imap', 'email'],\n ['smtp', 'email'],\n [' email', 'email'],\n ['slack', 'slack'],\n ['telegram', 'messaging'],\n ['discord', 'messaging'],\n [' sms', 'messaging'],\n ['twilio', 'messaging'],\n ['webhook', 'webhook'],\n ['google sheets', 'data'],\n ['spreadsheet', 'data'],\n ['airtable', 'data'],\n ['notion', 'data'],\n ['github', 'devops'],\n ['gitlab', 'devops'],\n ['schedule', 'schedule'],\n [' cron', 'schedule'],\n ['daily', 'schedule'],\n ['weekly', 'schedule'],\n ['hourly', 'schedule'],\n ['every day', 'schedule'],\n ['every hour', 'schedule'],\n ['every morning', 'schedule'],\n ['postgres', 'database'],\n ['mysql', 'database'],\n ['supabase', 'database'],\n ['redis', 'database'],\n [' database', 'database'],\n [' llm', 'ai'],\n [' gpt', 'ai'],\n ['claude', 'ai'],\n [' agent', 'ai'],\n ['langchain', 'ai'],\n [' ai ', 'ai'],\n [' ai', 'ai'],\n ['http request', 'api'],\n ['rest api', 'api'],\n [' api', 'api'],\n]\n\nexport function inferWorkflowType(description: string): string | null {\n const lower = ' ' + description.toLowerCase()\n for (const [keyword, type] of TYPE_KEYWORDS) {\n if (lower.includes(keyword)) return type\n }\n return null\n}\n"],"mappings":"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","names":[]}