@kairos-sdk/core 0.3.2 → 0.4.5

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.
@@ -1,170 +1,26 @@
1
1
  import {
2
- KairosError,
2
+ PromptBuilder,
3
+ inferWorkflowType
4
+ } from "./chunk-CR2NHLOH.js";
5
+ import {
6
+ GenerationError,
7
+ GuardError,
8
+ N8nProvider,
9
+ NullLibrary,
10
+ ResponseParseError,
11
+ ValidationError
12
+ } from "./chunk-6CLI43FI.js";
13
+ import {
3
14
  N8nApiClient,
4
15
  N8nFieldStripper,
5
16
  N8nValidator,
6
- PromptBuilder,
17
+ PatternAnalyzer,
18
+ TelemetryCollector,
7
19
  TelemetryReader,
8
20
  generateUUID,
9
21
  nullLogger,
10
22
  scoreToMode
11
- } from "./chunk-RYGYNOR6.js";
12
-
13
- // src/library/null-library.ts
14
- var NullLibrary = class {
15
- async initialize() {
16
- }
17
- async search(_description, _options) {
18
- return [];
19
- }
20
- async save(_workflow, _metadata) {
21
- return generateUUID();
22
- }
23
- async recordDeployment(_id) {
24
- }
25
- async recordOutcome(_id, _outcome) {
26
- }
27
- async get(_id) {
28
- return null;
29
- }
30
- async list(_filters) {
31
- return [];
32
- }
33
- };
34
-
35
- // src/errors/guard-error.ts
36
- var GuardError = class extends KairosError {
37
- constructor(message) {
38
- super(message);
39
- this.name = "GuardError";
40
- }
41
- };
42
-
43
- // src/providers/n8n/provider.ts
44
- var N8nProvider = class {
45
- constructor(client, stripper) {
46
- this.client = client;
47
- this.stripper = stripper;
48
- }
49
- client;
50
- stripper;
51
- platform = "n8n";
52
- async deploy(workflow) {
53
- const stripped = this.stripper.stripForCreate(workflow);
54
- const response = await this.client.createWorkflow(stripped);
55
- return { workflowId: response.id, name: response.name };
56
- }
57
- async update(id, workflow) {
58
- const stripped = this.stripper.stripForUpdate(workflow);
59
- const response = await this.client.updateWorkflow(id, stripped);
60
- return { workflowId: response.id, name: response.name };
61
- }
62
- async get(id) {
63
- const response = await this.client.getWorkflow(id);
64
- return {
65
- name: response.name,
66
- nodes: response.nodes,
67
- connections: response.connections,
68
- ...response.settings !== void 0 ? { settings: response.settings } : {},
69
- ...response.tags !== void 0 ? { tags: response.tags } : {}
70
- };
71
- }
72
- async list() {
73
- return this.client.listWorkflows();
74
- }
75
- async activate(id) {
76
- await this.client.activateWorkflow(id);
77
- }
78
- async deactivate(id) {
79
- await this.client.deactivateWorkflow(id);
80
- }
81
- async delete(id, options) {
82
- if (options.confirm !== true) {
83
- throw new GuardError("delete() requires { confirm: true } to prevent accidental deletion");
84
- }
85
- await this.client.deleteWorkflow(id);
86
- }
87
- async executions(workflowId, filter) {
88
- return this.client.getExecutions(workflowId, filter);
89
- }
90
- async execution(id) {
91
- return this.client.getExecution(id);
92
- }
93
- async listTags() {
94
- return this.client.listTags();
95
- }
96
- async createTag(name) {
97
- return this.client.createTag(name);
98
- }
99
- async tag(workflowId, tagIds) {
100
- await this.client.tagWorkflow(workflowId, tagIds);
101
- }
102
- async untag(workflowId, tagIds) {
103
- await this.client.untagWorkflow(workflowId, tagIds);
104
- }
105
- };
106
-
107
- // src/errors/generation-error.ts
108
- var GenerationError = class extends KairosError {
109
- constructor(message, cause) {
110
- super(message, cause);
111
- this.name = "GenerationError";
112
- }
113
- };
114
-
115
- // src/errors/response-parse-error.ts
116
- var ResponseParseError = class extends KairosError {
117
- constructor(message, cause) {
118
- super(message, cause);
119
- this.name = "ResponseParseError";
120
- }
121
- };
122
-
123
- // src/errors/validation-error.ts
124
- var ValidationError = class extends KairosError {
125
- constructor(message, issues) {
126
- super(message);
127
- this.issues = issues;
128
- this.name = "ValidationError";
129
- }
130
- issues;
131
- };
132
-
133
- // src/telemetry/collector.ts
134
- import { appendFile, mkdir } from "fs/promises";
135
- import { join } from "path";
136
- import { homedir } from "os";
137
-
138
- // src/telemetry/types.ts
139
- var TELEMETRY_SCHEMA_VERSION = 2;
140
-
141
- // src/telemetry/collector.ts
142
- var TelemetryCollector = class {
143
- dir;
144
- sessionId;
145
- dirReady = null;
146
- constructor(dir) {
147
- this.dir = dir ?? join(homedir(), ".kairos", "telemetry");
148
- this.sessionId = generateUUID();
149
- }
150
- async emit(eventType, data) {
151
- const event = {
152
- schemaVersion: TELEMETRY_SCHEMA_VERSION,
153
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
154
- sessionId: this.sessionId,
155
- eventType,
156
- data
157
- };
158
- if (!this.dirReady) {
159
- this.dirReady = mkdir(this.dir, { recursive: true }).then(() => {
160
- });
161
- }
162
- await this.dirReady;
163
- const filename = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10) + ".jsonl";
164
- const filepath = join(this.dir, filename);
165
- await appendFile(filepath, JSON.stringify(event) + "\n", "utf-8");
166
- }
167
- };
23
+ } from "./chunk-6IXW3WCC.js";
168
24
 
169
25
  // src/client.ts
170
26
  import Anthropic from "@anthropic-ai/sdk";
@@ -212,12 +68,12 @@ var GENERATE_WORKFLOW_TOOL = {
212
68
  }
213
69
  };
214
70
  var WorkflowDesigner = class {
215
- constructor(anthropic, model, logger) {
71
+ constructor(anthropic, model, logger, patternsPath) {
216
72
  this.anthropic = anthropic;
217
73
  this.model = model;
218
74
  this.logger = logger;
219
75
  this.validator = new N8nValidator();
220
- this.promptBuilder = new PromptBuilder();
76
+ this.promptBuilder = new PromptBuilder(patternsPath);
221
77
  }
222
78
  anthropic;
223
79
  model;
@@ -262,7 +118,7 @@ var WorkflowDesigner = class {
262
118
  issues: validation.issues
263
119
  });
264
120
  if (validation.valid) {
265
- return { workflow: parsed.workflow, credentialsNeeded: parsed.credentialsNeeded, attempts, attemptMetadata };
121
+ return { workflow: parsed.workflow, credentialsNeeded: parsed.credentialsNeeded, attempts, attemptMetadata, warnedRules: this.promptBuilder.getWarnedRules() };
266
122
  }
267
123
  lastErrors = errors;
268
124
  this.logger.warn(`WorkflowDesigner: validation failed on attempt ${attempt}`, {
@@ -272,7 +128,9 @@ var WorkflowDesigner = class {
272
128
  const finalIssues = attemptMetadata.at(-1)?.issues ?? lastErrors;
273
129
  throw new ValidationError(
274
130
  `Workflow failed validation after ${MAX_ATTEMPTS} attempts`,
275
- finalIssues
131
+ finalIssues,
132
+ attemptMetadata,
133
+ this.promptBuilder.getWarnedRules()
276
134
  );
277
135
  }
278
136
  async callClaude(system, userMessage, temperature) {
@@ -299,6 +157,11 @@ var WorkflowDesigner = class {
299
157
  }
300
158
  }
301
159
  extractToolUse(message) {
160
+ if (message.stop_reason === "max_tokens") {
161
+ throw new GenerationError(
162
+ "Claude response was truncated (max_tokens reached) \u2014 the workflow may be too large. Try a simpler description or break it into smaller workflows."
163
+ );
164
+ }
302
165
  const toolUseBlock = message.content.find(
303
166
  (block) => block.type === "tool_use"
304
167
  );
@@ -325,6 +188,8 @@ var WorkflowDesigner = class {
325
188
  };
326
189
 
327
190
  // src/client.ts
191
+ import { homedir } from "os";
192
+ import { join } from "path";
328
193
  var DEFAULT_MODEL = "claude-sonnet-4-6";
329
194
  var Kairos = class {
330
195
  provider;
@@ -334,6 +199,7 @@ var Kairos = class {
334
199
  logger;
335
200
  telemetry;
336
201
  telemetryReader;
202
+ patternAnalyzer;
337
203
  model;
338
204
  saveQueue = Promise.resolve(null);
339
205
  constructor(options) {
@@ -352,19 +218,23 @@ var Kairos = class {
352
218
  this.provider = null;
353
219
  }
354
220
  const anthropic = new Anthropic({ apiKey: options.anthropicApiKey });
355
- this.designer = new WorkflowDesigner(anthropic, this.model, logger);
221
+ const patternsPath = typeof options.telemetry === "string" ? join(options.telemetry, "..", "patterns.json") : join(homedir(), ".kairos", "patterns.json");
222
+ this.designer = new WorkflowDesigner(anthropic, this.model, logger, patternsPath);
356
223
  this.validator = new N8nValidator();
357
224
  this.library = options.library ?? new NullLibrary();
358
225
  this.logger = logger;
359
226
  if (options.telemetry === true) {
360
227
  this.telemetry = new TelemetryCollector();
361
228
  this.telemetryReader = new TelemetryReader();
229
+ this.patternAnalyzer = new PatternAnalyzer();
362
230
  } else if (typeof options.telemetry === "string") {
363
231
  this.telemetry = new TelemetryCollector(options.telemetry);
364
232
  this.telemetryReader = new TelemetryReader(options.telemetry);
233
+ this.patternAnalyzer = new PatternAnalyzer(options.telemetry);
365
234
  } else {
366
235
  this.telemetry = null;
367
236
  this.telemetryReader = null;
237
+ this.patternAnalyzer = null;
368
238
  }
369
239
  }
370
240
  requireProvider() {
@@ -382,11 +252,13 @@ var Kairos = class {
382
252
  this.validateDescription(description);
383
253
  this.logger.info("Kairos.build", { description, dryRun: options?.dryRun });
384
254
  const buildStart = Date.now();
255
+ const runId = generateUUID();
256
+ const workflowType = inferWorkflowType(description);
385
257
  await this.telemetry?.emit("build_start", {
386
258
  description,
387
259
  model: this.model,
388
260
  dryRun: options?.dryRun ?? false
389
- });
261
+ }, runId);
390
262
  await this.library.initialize();
391
263
  const matches = await this.library.search(description);
392
264
  if (matches.length > 0) {
@@ -402,12 +274,48 @@ var Kairos = class {
402
274
  this.logger.info(`Telemetry: ${highFreq.length} high-frequency failure rule(s) will be warned about`);
403
275
  }
404
276
  }
405
- const designResult = await this.designer.design(
406
- { description, ...options?.name ? { name: options.name } : {} },
407
- matches,
408
- globalFailureRates
409
- );
410
- await this.emitAttemptTelemetry(description, designResult);
277
+ let designResult;
278
+ try {
279
+ designResult = await this.designer.design(
280
+ { description, ...options?.name ? { name: options.name } : {} },
281
+ matches,
282
+ globalFailureRates
283
+ );
284
+ } catch (err) {
285
+ if (err instanceof ValidationError && err.attemptMetadata) {
286
+ for (const meta of err.attemptMetadata) {
287
+ await this.telemetry?.emit("generation_attempt", {
288
+ description,
289
+ attempt: meta.attempt,
290
+ temperature: meta.temperature,
291
+ durationMs: meta.durationMs,
292
+ tokensInput: meta.tokensInput,
293
+ tokensOutput: meta.tokensOutput,
294
+ validationPassed: meta.validationPassed,
295
+ issueCount: meta.issues.length,
296
+ issues: meta.issues.map((i) => ({ rule: i.rule, severity: i.severity, message: i.message, nodeId: i.nodeId ?? null, nodeType: i.nodeType ?? null })),
297
+ workflowType
298
+ }, runId);
299
+ }
300
+ await this.telemetry?.emit("build_complete", {
301
+ description,
302
+ success: false,
303
+ totalAttempts: err.attemptMetadata.length,
304
+ totalDurationMs: Date.now() - buildStart,
305
+ totalTokensInput: err.attemptMetadata.reduce((s, m) => s + m.tokensInput, 0),
306
+ totalTokensOutput: err.attemptMetadata.reduce((s, m) => s + m.tokensOutput, 0),
307
+ workflowName: null,
308
+ workflowId: null,
309
+ dryRun: options?.dryRun ?? false,
310
+ credentialsNeeded: 0,
311
+ warnedRules: err.warnedRules ?? [],
312
+ workflowType
313
+ }, runId);
314
+ this.updatePatterns();
315
+ }
316
+ throw err;
317
+ }
318
+ await this.emitAttemptTelemetry(description, designResult, workflowType, runId);
411
319
  const workflow = options?.name ? { ...designResult.workflow, name: options.name } : designResult.workflow;
412
320
  this.saveToLibrary(workflow, description, designResult, matches);
413
321
  if (options?.dryRun) {
@@ -423,8 +331,11 @@ var Kairos = class {
423
331
  workflowName: workflow.name,
424
332
  workflowId: null,
425
333
  dryRun: true,
426
- credentialsNeeded: designResult.credentialsNeeded.length
427
- });
334
+ credentialsNeeded: designResult.credentialsNeeded.length,
335
+ warnedRules: designResult.warnedRules,
336
+ workflowType
337
+ }, runId);
338
+ this.updatePatterns();
428
339
  return {
429
340
  workflowId: null,
430
341
  name: workflow.name,
@@ -453,8 +364,11 @@ var Kairos = class {
453
364
  workflowName: deployed.name,
454
365
  workflowId: deployed.workflowId,
455
366
  dryRun: false,
456
- credentialsNeeded: designResult.credentialsNeeded.length
457
- });
367
+ credentialsNeeded: designResult.credentialsNeeded.length,
368
+ warnedRules: designResult.warnedRules,
369
+ workflowType
370
+ }, runId);
371
+ this.updatePatterns();
458
372
  return {
459
373
  workflowId: deployed.workflowId,
460
374
  name: deployed.name,
@@ -469,16 +383,54 @@ var Kairos = class {
469
383
  this.validateDescription(description);
470
384
  this.logger.info("Kairos.update", { id, description });
471
385
  const buildStart = Date.now();
386
+ const runId = generateUUID();
387
+ const workflowType = inferWorkflowType(description);
472
388
  await this.telemetry?.emit("build_start", {
473
389
  description,
474
390
  model: this.model,
475
391
  dryRun: false
476
- });
392
+ }, runId);
477
393
  await this.library.initialize();
478
394
  const matches = await this.library.search(description);
479
395
  const globalFailureRates = await this.telemetryReader?.getFailureRates() ?? [];
480
- const designResult = await this.designer.design({ description }, matches, globalFailureRates);
481
- await this.emitAttemptTelemetry(description, designResult);
396
+ let designResult;
397
+ try {
398
+ designResult = await this.designer.design({ description }, matches, globalFailureRates);
399
+ } catch (err) {
400
+ if (err instanceof ValidationError && err.attemptMetadata) {
401
+ for (const meta of err.attemptMetadata) {
402
+ await this.telemetry?.emit("generation_attempt", {
403
+ description,
404
+ attempt: meta.attempt,
405
+ temperature: meta.temperature,
406
+ durationMs: meta.durationMs,
407
+ tokensInput: meta.tokensInput,
408
+ tokensOutput: meta.tokensOutput,
409
+ validationPassed: meta.validationPassed,
410
+ issueCount: meta.issues.length,
411
+ issues: meta.issues.map((i) => ({ rule: i.rule, severity: i.severity, message: i.message, nodeId: i.nodeId ?? null, nodeType: i.nodeType ?? null })),
412
+ workflowType
413
+ }, runId);
414
+ }
415
+ await this.telemetry?.emit("build_complete", {
416
+ description,
417
+ success: false,
418
+ totalAttempts: err.attemptMetadata.length,
419
+ totalDurationMs: Date.now() - buildStart,
420
+ totalTokensInput: err.attemptMetadata.reduce((s, m) => s + m.tokensInput, 0),
421
+ totalTokensOutput: err.attemptMetadata.reduce((s, m) => s + m.tokensOutput, 0),
422
+ workflowName: null,
423
+ workflowId: null,
424
+ dryRun: false,
425
+ credentialsNeeded: 0,
426
+ warnedRules: err.warnedRules ?? [],
427
+ workflowType
428
+ }, runId);
429
+ this.updatePatterns();
430
+ }
431
+ throw err;
432
+ }
433
+ await this.emitAttemptTelemetry(description, designResult, workflowType, runId);
482
434
  const provider = this.requireProvider();
483
435
  const deployed = await provider.update(id, designResult.workflow);
484
436
  this.saveToLibrary(designResult.workflow, description, designResult, matches);
@@ -495,8 +447,11 @@ var Kairos = class {
495
447
  workflowName: deployed.name,
496
448
  workflowId: deployed.workflowId,
497
449
  dryRun: false,
498
- credentialsNeeded: designResult.credentialsNeeded.length
499
- });
450
+ credentialsNeeded: designResult.credentialsNeeded.length,
451
+ warnedRules: designResult.warnedRules,
452
+ workflowType
453
+ }, runId);
454
+ this.updatePatterns();
500
455
  return {
501
456
  workflowId: deployed.workflowId,
502
457
  name: deployed.name,
@@ -511,7 +466,14 @@ var Kairos = class {
511
466
  await this.saveQueue.catch(() => {
512
467
  });
513
468
  }
514
- async emitAttemptTelemetry(description, designResult) {
469
+ updatePatterns() {
470
+ if (!this.patternAnalyzer) return;
471
+ this.saveQueue = this.saveQueue.then(() => this.patternAnalyzer.analyzeAndSave()).then(() => null).catch((err) => {
472
+ this.logger.warn("Pattern analysis failed (non-fatal)", { err: String(err) });
473
+ return null;
474
+ });
475
+ }
476
+ async emitAttemptTelemetry(description, designResult, workflowType, runId) {
515
477
  for (const meta of designResult.attemptMetadata) {
516
478
  await this.telemetry?.emit("generation_attempt", {
517
479
  description,
@@ -522,8 +484,9 @@ var Kairos = class {
522
484
  tokensOutput: meta.tokensOutput,
523
485
  validationPassed: meta.validationPassed,
524
486
  issueCount: meta.issues.length,
525
- issues: meta.issues.map((i) => ({ rule: i.rule, message: i.message }))
526
- });
487
+ issues: meta.issues.map((i) => ({ rule: i.rule, severity: i.severity, message: i.message, nodeId: i.nodeId ?? null, nodeType: i.nodeType ?? null })),
488
+ workflowType
489
+ }, runId);
527
490
  }
528
491
  }
529
492
  recordDeploy() {
@@ -620,190 +583,7 @@ var Kairos = class {
620
583
  }
621
584
  };
622
585
 
623
- // src/templates/safety.ts
624
- var BLOCKED_NODE_TYPES = /* @__PURE__ */ new Set([
625
- "n8n-nodes-base.code",
626
- "n8n-nodes-base.executeCommand",
627
- "n8n-nodes-base.ssh"
628
- ]);
629
- var REVIEW_NODE_TYPES = /* @__PURE__ */ new Set([
630
- "n8n-nodes-base.httpRequest"
631
- ]);
632
- var SECRET_PATTERNS = [
633
- /sk-[a-zA-Z0-9]{20,}/,
634
- /ghp_[a-zA-Z0-9]{36}/,
635
- /xoxb-[0-9]+-[0-9]+-[a-zA-Z0-9]+/,
636
- /AIza[a-zA-Z0-9_-]{35}/,
637
- /AKIA[A-Z0-9]{16}/
638
- ];
639
- function assessTemplateSafety(workflow) {
640
- const reasons = [];
641
- let worst = "safe";
642
- const escalate = (level, reason) => {
643
- reasons.push(reason);
644
- if (level === "blocked") worst = "blocked";
645
- else if (level === "review" && worst === "safe") worst = "review";
646
- };
647
- for (const node of workflow.nodes) {
648
- if (BLOCKED_NODE_TYPES.has(node.type)) {
649
- escalate("blocked", `Contains ${node.type} node "${node.name}"`);
650
- }
651
- if (REVIEW_NODE_TYPES.has(node.type)) {
652
- escalate("review", `Contains ${node.type} node "${node.name}"`);
653
- }
654
- const paramStr = JSON.stringify(node.parameters);
655
- for (const pattern of SECRET_PATTERNS) {
656
- if (pattern.test(paramStr)) {
657
- escalate("blocked", `Node "${node.name}" parameters contain a hardcoded secret`);
658
- break;
659
- }
660
- }
661
- }
662
- return { trustLevel: worst, reasons };
663
- }
664
-
665
- // src/templates/syncer.ts
666
- var N8N_TEMPLATE_API = "https://api.n8n.io/api/templates";
667
- var PAGE_SIZE = 50;
668
- var DELAY_BETWEEN_FETCHES_MS = 200;
669
- var DEFAULT_SETTINGS = {
670
- executionOrder: "v1",
671
- saveManualExecutions: true,
672
- timezone: "UTC"
673
- };
674
- var TemplateSyncer = class {
675
- constructor(library, logger) {
676
- this.library = library;
677
- this.validator = new N8nValidator();
678
- this.logger = logger;
679
- }
680
- library;
681
- validator;
682
- logger;
683
- async sync(options) {
684
- const maxTemplates = options?.maxTemplates ?? 500;
685
- await this.library.initialize();
686
- const existing = await this.library.list();
687
- const existingSourceIds = new Set(
688
- existing.filter((w) => w.sourceKind === "n8n-template" && w.sourceId).map((w) => w.sourceId)
689
- );
690
- const progress = {
691
- total: 0,
692
- processed: 0,
693
- saved: 0,
694
- skippedPaid: 0,
695
- skippedDuplicate: 0,
696
- blocked: 0,
697
- reviewed: 0
698
- };
699
- const templateIds = await this.fetchTemplateIds(maxTemplates, progress);
700
- for (const id of templateIds) {
701
- if (existingSourceIds.has(String(id))) {
702
- progress.skippedDuplicate++;
703
- progress.processed++;
704
- options?.onProgress?.(progress);
705
- continue;
706
- }
707
- try {
708
- await this.processTemplate(id, progress);
709
- } catch (err) {
710
- this.logger.warn(`Failed to process template ${id}`, { err: String(err) });
711
- }
712
- progress.processed++;
713
- options?.onProgress?.(progress);
714
- await new Promise((resolve) => setTimeout(resolve, DELAY_BETWEEN_FETCHES_MS));
715
- }
716
- return progress;
717
- }
718
- async fetchTemplateIds(max, progress) {
719
- const ids = [];
720
- let page = 1;
721
- while (ids.length < max) {
722
- const url = `${N8N_TEMPLATE_API}/search?page=${page}&rows=${PAGE_SIZE}`;
723
- const response = await fetch(url);
724
- if (!response.ok) break;
725
- const data = await response.json();
726
- progress.total = Math.min(data.totalWorkflows, max);
727
- for (const template of data.workflows) {
728
- if (ids.length >= max) break;
729
- if (template.price && template.price > 0) {
730
- progress.skippedPaid++;
731
- continue;
732
- }
733
- ids.push(template.id);
734
- }
735
- if (data.workflows.length < PAGE_SIZE) break;
736
- page++;
737
- await new Promise((resolve) => setTimeout(resolve, DELAY_BETWEEN_FETCHES_MS));
738
- }
739
- return ids;
740
- }
741
- async processTemplate(id, progress) {
742
- const url = `${N8N_TEMPLATE_API}/workflows/${id}`;
743
- const response = await fetch(url);
744
- if (!response.ok) return;
745
- const data = await response.json();
746
- const templateMeta = data.workflow;
747
- const rawWorkflow = templateMeta.workflow;
748
- if (!rawWorkflow?.nodes?.length) return;
749
- const workflow = {
750
- name: templateMeta.name,
751
- nodes: rawWorkflow.nodes.filter((n) => n.type && n.name),
752
- connections: rawWorkflow.connections,
753
- settings: rawWorkflow.settings ? { executionOrder: "v1", ...rawWorkflow.settings } : { ...DEFAULT_SETTINGS }
754
- };
755
- const validation = this.validator.validate(workflow);
756
- const validationErrors = validation.issues.filter((i) => i.severity === "error");
757
- if (validationErrors.length > 0) {
758
- progress.blocked++;
759
- this.logger.debug(`Template ${id} blocked: ${validationErrors.length} validation errors`);
760
- return;
761
- }
762
- const safety = assessTemplateSafety(workflow);
763
- if (safety.trustLevel === "blocked") {
764
- progress.blocked++;
765
- this.logger.debug(`Template ${id} blocked: ${safety.reasons.join(", ")}`);
766
- return;
767
- }
768
- if (safety.trustLevel === "review") {
769
- progress.reviewed++;
770
- }
771
- const description = this.cleanDescription(templateMeta.description);
772
- const autoTags = Array.from(new Set(
773
- workflow.nodes.flatMap((n) => {
774
- const bare = n.type.split(".").pop() ?? "";
775
- const tags = [bare];
776
- if (n.type.includes("Trigger") || n.type.includes("trigger")) tags.push(`trigger:${bare}`);
777
- if (n.type.includes("langchain")) tags.push("ai");
778
- return tags;
779
- })
780
- ));
781
- const metadata = {
782
- description,
783
- tags: autoTags,
784
- sourceKind: "n8n-template",
785
- sourceId: String(id),
786
- sourceUrl: `https://n8n.io/workflows/${id}`,
787
- trustLevel: safety.trustLevel
788
- };
789
- await this.library.save(workflow, metadata);
790
- progress.saved++;
791
- this.logger.debug(`Template ${id} saved: "${templateMeta.name}" (${safety.trustLevel})`);
792
- }
793
- cleanDescription(raw) {
794
- return raw.replace(/#{1,6}\s*/g, "").replace(/\*{1,2}([^*]+)\*{1,2}/g, "$1").replace(/\[([^\]]+)\]\([^)]+\)/g, "$1").replace(/\n{3,}/g, "\n\n").trim().slice(0, 500);
795
- }
796
- };
797
-
798
586
  export {
799
- NullLibrary,
800
- GuardError,
801
- N8nProvider,
802
- GenerationError,
803
- ResponseParseError,
804
- ValidationError,
805
- TelemetryCollector,
806
- Kairos,
807
- TemplateSyncer
587
+ Kairos
808
588
  };
809
- //# sourceMappingURL=chunk-KQSNT3HZ.js.map
589
+ //# sourceMappingURL=chunk-4TS6GW6O.js.map