@lhi/n8m 0.1.1 → 0.1.2

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.
@@ -12,6 +12,7 @@ export declare const graph: import("@langchain/langgraph").CompiledStateGraph<{
12
12
  strategies: any[];
13
13
  candidates: any[];
14
14
  customTools: Record<string, string>;
15
+ collaborationLog: string[];
15
16
  }, {
16
17
  userGoal?: string | undefined;
17
18
  spec?: any;
@@ -25,6 +26,7 @@ export declare const graph: import("@langchain/langgraph").CompiledStateGraph<{
25
26
  strategies?: any[] | undefined;
26
27
  candidates?: any[] | undefined;
27
28
  customTools?: Record<string, string> | undefined;
29
+ collaborationLog?: string[] | undefined;
28
30
  }, "__start__" | "architect" | "engineer" | "reviewer" | "supervisor" | "qa", {
29
31
  userGoal: {
30
32
  (): import("@langchain/langgraph").LastValue<string>;
@@ -74,6 +76,7 @@ export declare const graph: import("@langchain/langgraph").CompiledStateGraph<{
74
76
  };
75
77
  candidates: import("@langchain/langgraph").BinaryOperatorAggregate<any[], any[]>;
76
78
  customTools: import("@langchain/langgraph").BinaryOperatorAggregate<Record<string, string>, Record<string, string>>;
79
+ collaborationLog: import("@langchain/langgraph").BinaryOperatorAggregate<string[], string[]>;
77
80
  }, {
78
81
  userGoal: {
79
82
  (): import("@langchain/langgraph").LastValue<string>;
@@ -123,11 +126,13 @@ export declare const graph: import("@langchain/langgraph").CompiledStateGraph<{
123
126
  };
124
127
  candidates: import("@langchain/langgraph").BinaryOperatorAggregate<any[], any[]>;
125
128
  customTools: import("@langchain/langgraph").BinaryOperatorAggregate<Record<string, string>, Record<string, string>>;
129
+ collaborationLog: import("@langchain/langgraph").BinaryOperatorAggregate<string[], string[]>;
126
130
  }, import("@langchain/langgraph").StateDefinition, {
127
131
  architect: {
128
132
  spec: any;
129
133
  strategies: any[];
130
134
  needsClarification: any;
135
+ collaborationLog: string[];
131
136
  };
132
137
  engineer: {
133
138
  workflowJson: any;
@@ -188,11 +193,14 @@ export declare const graph: import("@langchain/langgraph").CompiledStateGraph<{
188
193
  };
189
194
  candidates: import("@langchain/langgraph").BinaryOperatorAggregate<any[], any[]>;
190
195
  customTools: import("@langchain/langgraph").BinaryOperatorAggregate<Record<string, string>, Record<string, string>>;
196
+ collaborationLog: import("@langchain/langgraph").BinaryOperatorAggregate<string[], string[]>;
191
197
  }>;
192
198
  supervisor: {
193
199
  workflowJson?: undefined;
200
+ collaborationLog?: undefined;
194
201
  } | {
195
202
  workflowJson: any;
203
+ collaborationLog: string[];
196
204
  };
197
205
  qa: import("@langchain/langgraph").UpdateType<{
198
206
  userGoal: {
@@ -243,6 +251,7 @@ export declare const graph: import("@langchain/langgraph").CompiledStateGraph<{
243
251
  };
244
252
  candidates: import("@langchain/langgraph").BinaryOperatorAggregate<any[], any[]>;
245
253
  customTools: import("@langchain/langgraph").BinaryOperatorAggregate<Record<string, string>, Record<string, string>>;
254
+ collaborationLog: import("@langchain/langgraph").BinaryOperatorAggregate<string[], string[]>;
246
255
  }>;
247
256
  }, unknown, unknown>;
248
257
  /**
@@ -300,6 +309,7 @@ export declare const runAgenticWorkflow: (goal: string, initialState?: Partial<t
300
309
  };
301
310
  candidates: import("@langchain/langgraph").BinaryOperatorAggregate<any[], any[]>;
302
311
  customTools: import("@langchain/langgraph").BinaryOperatorAggregate<Record<string, string>, Record<string, string>>;
312
+ collaborationLog: import("@langchain/langgraph").BinaryOperatorAggregate<string[], string[]>;
303
313
  }>>;
304
314
  /**
305
315
  * Run the Agentic Workflow with Streaming
@@ -311,6 +321,7 @@ export declare const runAgenticWorkflowStream: (goal: string, threadId?: string)
311
321
  spec: any;
312
322
  strategies: any[];
313
323
  needsClarification: any;
324
+ collaborationLog: string[];
314
325
  } | undefined;
315
326
  engineer?: {
316
327
  workflowJson: any;
@@ -371,11 +382,14 @@ export declare const runAgenticWorkflowStream: (goal: string, threadId?: string)
371
382
  };
372
383
  candidates: import("@langchain/langgraph").BinaryOperatorAggregate<any[], any[]>;
373
384
  customTools: import("@langchain/langgraph").BinaryOperatorAggregate<Record<string, string>, Record<string, string>>;
385
+ collaborationLog: import("@langchain/langgraph").BinaryOperatorAggregate<string[], string[]>;
374
386
  }> | undefined;
375
387
  supervisor?: {
376
388
  workflowJson?: undefined;
389
+ collaborationLog?: undefined;
377
390
  } | {
378
391
  workflowJson: any;
392
+ collaborationLog: string[];
379
393
  } | undefined;
380
394
  qa?: import("@langchain/langgraph").UpdateType<{
381
395
  userGoal: {
@@ -426,6 +440,7 @@ export declare const runAgenticWorkflowStream: (goal: string, threadId?: string)
426
440
  };
427
441
  candidates: import("@langchain/langgraph").BinaryOperatorAggregate<any[], any[]>;
428
442
  customTools: import("@langchain/langgraph").BinaryOperatorAggregate<Record<string, string>, Record<string, string>>;
443
+ collaborationLog: import("@langchain/langgraph").BinaryOperatorAggregate<string[], string[]>;
429
444
  }> | undefined;
430
445
  }>>;
431
446
  /**
@@ -480,4 +495,5 @@ export declare const resumeAgenticWorkflow: (threadId: string, input?: any) => P
480
495
  };
481
496
  candidates: import("@langchain/langgraph").BinaryOperatorAggregate<any[], any[]>;
482
497
  customTools: import("@langchain/langgraph").BinaryOperatorAggregate<Record<string, string>, Record<string, string>>;
498
+ collaborationLog: import("@langchain/langgraph").BinaryOperatorAggregate<string[], string[]>;
483
499
  }>>;
@@ -3,4 +3,5 @@ export declare const architectNode: (state: typeof TeamState.State) => Promise<{
3
3
  spec: any;
4
4
  strategies: any[];
5
5
  needsClarification: any;
6
+ collaborationLog: string[];
6
7
  }>;
@@ -30,18 +30,20 @@ export const architectNode = async (state) => {
30
30
  // Check if the spec requires clarification
31
31
  const questions = spec.questions;
32
32
  const needsClarification = questions && questions.length > 0;
33
- // For parallelism, we can create a secondary "Alternative" strategy
34
- // In a real scenario, the LLM would generate these explicitly.
35
- // Here we simulate it by wrapping the single spec into a strategy list.
33
+ // Multi-agent collaboration: generate an alternative strategy in parallel with the primary.
34
+ // Both are handed off to separate Engineer agents that run concurrently.
35
+ const alternativeSpec = await aiService.generateAlternativeSpec(state.userGoal, spec);
36
36
  const strategies = [
37
- { ...spec, name: "Primary Strategy" },
38
- // We could ask AI for an alternative here, but for now let's keep it simple to save tokens
39
- // { ...spec, name: "Alternative Strategy (Robust)" }
37
+ { ...spec, strategyName: "Primary Strategy" },
38
+ { ...alternativeSpec, strategyName: "Alternative Strategy" },
40
39
  ];
40
+ const logEntry = `Architect: Generated 2 strategies — "${strategies[0].suggestedName}" (primary) and "${strategies[1].suggestedName}" (alternative)`;
41
+ console.log(`[Architect] ${logEntry}`);
41
42
  return {
42
- spec, // Keep backward compatibility for single-path
43
+ spec,
43
44
  strategies,
44
45
  needsClarification,
46
+ collaborationLog: [logEntry],
45
47
  };
46
48
  }
47
49
  catch (error) {
@@ -1,6 +1,8 @@
1
1
  import { TeamState } from "../state.js";
2
2
  export declare const supervisorNode: (state: typeof TeamState.State) => Promise<{
3
3
  workflowJson?: undefined;
4
+ collaborationLog?: undefined;
4
5
  } | {
5
6
  workflowJson: any;
7
+ collaborationLog: string[];
6
8
  }>;
@@ -1,3 +1,4 @@
1
+ import { AIService } from "../../services/ai.service.js";
1
2
  import { theme } from "../../utils/theme.js";
2
3
  export const supervisorNode = async (state) => {
3
4
  const candidates = state.candidates;
@@ -5,14 +6,15 @@ export const supervisorNode = async (state) => {
5
6
  // Fallback: use existing workflowJson if available
6
7
  return {};
7
8
  }
8
- console.log(theme.agent(`Supervisor found ${candidates.length} candidates.`));
9
- // In a real agentic system, we would have an LLM evaluate them.
10
- // For now, we'll pick the first one (or the one with the most nodes? or custom logic?).
11
- // Let's simulate "Selection":
12
- const bestCandidate = candidates[0];
13
- console.log(theme.success(`Supervisor selected: ${bestCandidate.name || "Unnamed Workflow"}`));
14
- // We set the chosen one as the canonical 'workflowJson' for the rest of the flow (QA, etc)
9
+ console.log(theme.agent(`Supervisor evaluating ${candidates.length} candidate(s)...`));
10
+ const aiService = AIService.getInstance();
11
+ const evaluation = await aiService.evaluateCandidates(state.userGoal, candidates);
12
+ const bestCandidate = candidates[evaluation.selectedIndex] ?? candidates[0];
13
+ const logEntry = `Supervisor: Selected candidate ${evaluation.selectedIndex + 1}/${candidates.length} ("${bestCandidate.name || 'Unnamed'}"). Reason: ${evaluation.reason}`;
14
+ console.log(theme.success(`Supervisor selected: ${bestCandidate.name || "Unnamed Workflow"} (candidate ${evaluation.selectedIndex + 1})`));
15
+ console.log(theme.agent(` → ${evaluation.reason}`));
15
16
  return {
16
- workflowJson: bestCandidate
17
+ workflowJson: bestCandidate,
18
+ collaborationLog: [logEntry],
17
19
  };
18
20
  };
@@ -48,4 +48,5 @@ export declare const TeamState: import("@langchain/langgraph").AnnotationRoot<{
48
48
  };
49
49
  candidates: import("@langchain/langgraph").BinaryOperatorAggregate<any[], any[]>;
50
50
  customTools: import("@langchain/langgraph").BinaryOperatorAggregate<Record<string, string>, Record<string, string>>;
51
+ collaborationLog: import("@langchain/langgraph").BinaryOperatorAggregate<string[], string[]>;
51
52
  }>;
@@ -23,4 +23,9 @@ export const TeamState = Annotation.Root({
23
23
  reducer: (x, y) => ({ ...x, ...y }),
24
24
  default: () => ({}),
25
25
  }),
26
+ // Collaboration Log: agents record their reasoning for visibility
27
+ collaborationLog: Annotation({
28
+ reducer: (x, y) => x.concat(y),
29
+ default: () => [],
30
+ }),
26
31
  });
@@ -44,6 +44,19 @@ export declare class AIService {
44
44
  * Force-fix connection structure to prevent "object is not iterable" errors
45
45
  */
46
46
  private fixN8nConnections;
47
+ /**
48
+ * Generate an alternative workflow specification with a different approach to the same goal.
49
+ * Used by the Architect node to produce a second strategy for parallel Engineer execution.
50
+ */
51
+ generateAlternativeSpec(goal: string, primarySpec: any): Promise<any>;
52
+ /**
53
+ * Evaluate multiple workflow candidates and select the best one for the given goal.
54
+ * Used by the Supervisor node to choose between parallel Engineer outputs.
55
+ */
56
+ evaluateCandidates(goal: string, candidates: any[]): Promise<{
57
+ selectedIndex: number;
58
+ reason: string;
59
+ }>;
47
60
  /**
48
61
  * Validate against real node types and shim unknown ones
49
62
  */
@@ -370,6 +370,89 @@ export class AIService {
370
370
  workflow.connections = fixedConnections;
371
371
  return workflow;
372
372
  }
373
+ /**
374
+ * Generate an alternative workflow specification with a different approach to the same goal.
375
+ * Used by the Architect node to produce a second strategy for parallel Engineer execution.
376
+ */
377
+ async generateAlternativeSpec(goal, primarySpec) {
378
+ const prompt = `You are an n8n Solutions Architect exploring alternative workflow designs.
379
+
380
+ A primary solution already exists for this goal. Your task is to design a DIFFERENT approach that achieves the same result.
381
+
382
+ Goal: ${goal}
383
+
384
+ Primary Specification (for reference — design something DIFFERENT):
385
+ ${JSON.stringify(primarySpec, null, 2)}
386
+
387
+ Create an alternative approach that:
388
+ - Uses DIFFERENT n8n nodes or integrations where possible
389
+ - May take a simpler, more minimal path OR a more robust, comprehensive one
390
+ - Achieves the SAME end goal
391
+
392
+ Output ONLY valid JSON with this structure:
393
+ {
394
+ "goal": "...",
395
+ "suggestedName": "...",
396
+ "tasks": [...],
397
+ "nodes": [...],
398
+ "assumptions": [...],
399
+ "questions": [],
400
+ "strategyType": "alternative"
401
+ }`;
402
+ const response = await this.generateContent(prompt, { temperature: 0.9 });
403
+ let cleanJson = response || '{}';
404
+ cleanJson = cleanJson.replace(/```json\n?|\n?```/g, '').trim();
405
+ try {
406
+ return JSON.parse(cleanJson);
407
+ }
408
+ catch {
409
+ console.warn('[AIService] Failed to parse alternative spec, falling back to primary variant');
410
+ return { ...primarySpec, suggestedName: `${primarySpec.suggestedName} (Alt)`, strategyType: 'alternative' };
411
+ }
412
+ }
413
+ /**
414
+ * Evaluate multiple workflow candidates and select the best one for the given goal.
415
+ * Used by the Supervisor node to choose between parallel Engineer outputs.
416
+ */
417
+ async evaluateCandidates(goal, candidates) {
418
+ if (candidates.length <= 1) {
419
+ return { selectedIndex: 0, reason: 'Single candidate — no evaluation needed.' };
420
+ }
421
+ const candidateSummaries = candidates.map((c, i) => {
422
+ const nodeNames = c?.nodes?.map((n) => n.name || n.type).join(', ') || 'unknown nodes';
423
+ return `Candidate ${i + 1}: "${c?.name || 'Unnamed'}" — Nodes: [${nodeNames}]`;
424
+ }).join('\n');
425
+ const prompt = `You are an n8n Solutions Architect evaluating workflow implementations.
426
+
427
+ Goal: ${goal}
428
+
429
+ Evaluate these ${candidates.length} candidate workflows and select the BEST one:
430
+ ${candidateSummaries}
431
+
432
+ Select the best candidate based on:
433
+ 1. Completeness — does it fully achieve the goal?
434
+ 2. Node quality — are the nodes standard and appropriate?
435
+ 3. Simplicity — is it appropriately complex (not over-engineered)?
436
+ 4. Data flow — is the logic sound?
437
+
438
+ Output ONLY valid JSON:
439
+ { "selectedIndex": <0-based integer>, "reason": "<1-2 sentence explanation>" }`;
440
+ const response = await this.generateContent(prompt, { temperature: 0.3 });
441
+ let cleanJson = response || '{}';
442
+ cleanJson = cleanJson.replace(/```json\n?|\n?```/g, '').trim();
443
+ try {
444
+ const result = JSON.parse(cleanJson);
445
+ const idx = typeof result.selectedIndex === 'number' ? result.selectedIndex : 0;
446
+ return {
447
+ selectedIndex: Math.max(0, Math.min(idx, candidates.length - 1)),
448
+ reason: result.reason || 'Best overall candidate.',
449
+ };
450
+ }
451
+ catch {
452
+ console.warn('[AIService] Failed to parse candidate evaluation, defaulting to index 0');
453
+ return { selectedIndex: 0, reason: 'Evaluation failed, defaulting to first candidate.' };
454
+ }
455
+ }
373
456
  /**
374
457
  * Validate against real node types and shim unknown ones
375
458
  */
@@ -327,5 +327,5 @@
327
327
  ]
328
328
  }
329
329
  },
330
- "version": "0.1.1"
330
+ "version": "0.1.2"
331
331
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lhi/n8m",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "description": "Agentic n8n CLI wrapper - A Skill Bridge for n8n workflow automation",
5
5
  "author": "Lem Canady",
6
6
  "license": "MIT",