@lhi/n8m 0.1.1 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -0
- package/bin/dev.js +8 -0
- package/bin/run.js +7 -1
- package/dist/agentic/graph.d.ts +48 -8
- package/dist/agentic/nodes/architect.d.ts +16 -3
- package/dist/agentic/nodes/architect.js +18 -7
- package/dist/agentic/nodes/engineer.d.ts +1 -1
- package/dist/agentic/nodes/engineer.js +16 -9
- package/dist/agentic/nodes/qa.js +16 -4
- package/dist/agentic/nodes/reviewer.js +5 -3
- package/dist/agentic/nodes/supervisor.d.ts +2 -0
- package/dist/agentic/nodes/supervisor.js +10 -8
- package/dist/agentic/state.d.ts +1 -0
- package/dist/agentic/state.js +5 -0
- package/dist/resources/node-definitions-fallback.json +213 -0
- package/dist/services/ai.service.d.ts +37 -41
- package/dist/services/ai.service.js +188 -347
- package/dist/services/node-definitions.service.d.ts +5 -0
- package/dist/services/node-definitions.service.js +64 -8
- package/dist/utils/n8nClient.d.ts +10 -10
- package/dist/utils/n8nClient.js +80 -145
- package/oclif.manifest.json +1 -1
- package/package.json +6 -4
package/README.md
CHANGED
package/bin/dev.js
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env node --loader ts-node/esm --no-warnings=ExperimentalWarning
|
|
2
2
|
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
4
|
+
import { dirname, join } from 'path';
|
|
5
|
+
import { config } from 'dotenv';
|
|
6
|
+
|
|
7
|
+
// Load .env from the package root regardless of the user's cwd
|
|
8
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
9
|
+
config({ path: join(__dirname, '..', '.env') });
|
|
10
|
+
|
|
3
11
|
import {execute} from '@oclif/core'
|
|
4
12
|
|
|
5
13
|
await execute({development: true, dir: import.meta.url})
|
package/bin/run.js
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import '
|
|
2
|
+
import { fileURLToPath } from 'url';
|
|
3
|
+
import { dirname, join } from 'path';
|
|
4
|
+
import { config } from 'dotenv';
|
|
5
|
+
|
|
6
|
+
// Load .env from the package root regardless of the user's cwd
|
|
7
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
8
|
+
config({ path: join(__dirname, '..', '.env') });
|
|
3
9
|
|
|
4
10
|
import {execute} from '@oclif/core'
|
|
5
11
|
|
package/dist/agentic/graph.d.ts
CHANGED
|
@@ -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,25 @@ 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
|
-
spec:
|
|
129
|
-
strategies:
|
|
130
|
-
|
|
132
|
+
spec: import("../services/ai.service.js").WorkflowSpec;
|
|
133
|
+
strategies: {
|
|
134
|
+
strategyName: string;
|
|
135
|
+
aiModel: string;
|
|
136
|
+
suggestedName: string;
|
|
137
|
+
description: string;
|
|
138
|
+
nodes: {
|
|
139
|
+
type: string;
|
|
140
|
+
purpose: string;
|
|
141
|
+
config?: any;
|
|
142
|
+
}[];
|
|
143
|
+
questions?: string[];
|
|
144
|
+
aiProvider?: string;
|
|
145
|
+
}[];
|
|
146
|
+
needsClarification: boolean | undefined;
|
|
147
|
+
collaborationLog: string[];
|
|
131
148
|
};
|
|
132
149
|
engineer: {
|
|
133
150
|
workflowJson: any;
|
|
@@ -136,8 +153,8 @@ export declare const graph: import("@langchain/langgraph").CompiledStateGraph<{
|
|
|
136
153
|
workflowJson?: undefined;
|
|
137
154
|
candidates?: undefined;
|
|
138
155
|
} | {
|
|
139
|
-
workflowJson: any;
|
|
140
156
|
candidates: any[];
|
|
157
|
+
workflowJson?: undefined;
|
|
141
158
|
};
|
|
142
159
|
reviewer: import("@langchain/langgraph").UpdateType<{
|
|
143
160
|
userGoal: {
|
|
@@ -188,11 +205,14 @@ export declare const graph: import("@langchain/langgraph").CompiledStateGraph<{
|
|
|
188
205
|
};
|
|
189
206
|
candidates: import("@langchain/langgraph").BinaryOperatorAggregate<any[], any[]>;
|
|
190
207
|
customTools: import("@langchain/langgraph").BinaryOperatorAggregate<Record<string, string>, Record<string, string>>;
|
|
208
|
+
collaborationLog: import("@langchain/langgraph").BinaryOperatorAggregate<string[], string[]>;
|
|
191
209
|
}>;
|
|
192
210
|
supervisor: {
|
|
193
211
|
workflowJson?: undefined;
|
|
212
|
+
collaborationLog?: undefined;
|
|
194
213
|
} | {
|
|
195
214
|
workflowJson: any;
|
|
215
|
+
collaborationLog: string[];
|
|
196
216
|
};
|
|
197
217
|
qa: import("@langchain/langgraph").UpdateType<{
|
|
198
218
|
userGoal: {
|
|
@@ -243,6 +263,7 @@ export declare const graph: import("@langchain/langgraph").CompiledStateGraph<{
|
|
|
243
263
|
};
|
|
244
264
|
candidates: import("@langchain/langgraph").BinaryOperatorAggregate<any[], any[]>;
|
|
245
265
|
customTools: import("@langchain/langgraph").BinaryOperatorAggregate<Record<string, string>, Record<string, string>>;
|
|
266
|
+
collaborationLog: import("@langchain/langgraph").BinaryOperatorAggregate<string[], string[]>;
|
|
246
267
|
}>;
|
|
247
268
|
}, unknown, unknown>;
|
|
248
269
|
/**
|
|
@@ -300,6 +321,7 @@ export declare const runAgenticWorkflow: (goal: string, initialState?: Partial<t
|
|
|
300
321
|
};
|
|
301
322
|
candidates: import("@langchain/langgraph").BinaryOperatorAggregate<any[], any[]>;
|
|
302
323
|
customTools: import("@langchain/langgraph").BinaryOperatorAggregate<Record<string, string>, Record<string, string>>;
|
|
324
|
+
collaborationLog: import("@langchain/langgraph").BinaryOperatorAggregate<string[], string[]>;
|
|
303
325
|
}>>;
|
|
304
326
|
/**
|
|
305
327
|
* Run the Agentic Workflow with Streaming
|
|
@@ -308,9 +330,22 @@ export declare const runAgenticWorkflow: (goal: string, initialState?: Partial<t
|
|
|
308
330
|
*/
|
|
309
331
|
export declare const runAgenticWorkflowStream: (goal: string, threadId?: string) => Promise<import("@langchain/core/utils/stream").IterableReadableStream<{
|
|
310
332
|
architect?: {
|
|
311
|
-
spec:
|
|
312
|
-
strategies:
|
|
313
|
-
|
|
333
|
+
spec: import("../services/ai.service.js").WorkflowSpec;
|
|
334
|
+
strategies: {
|
|
335
|
+
strategyName: string;
|
|
336
|
+
aiModel: string;
|
|
337
|
+
suggestedName: string;
|
|
338
|
+
description: string;
|
|
339
|
+
nodes: {
|
|
340
|
+
type: string;
|
|
341
|
+
purpose: string;
|
|
342
|
+
config?: any;
|
|
343
|
+
}[];
|
|
344
|
+
questions?: string[];
|
|
345
|
+
aiProvider?: string;
|
|
346
|
+
}[];
|
|
347
|
+
needsClarification: boolean | undefined;
|
|
348
|
+
collaborationLog: string[];
|
|
314
349
|
} | undefined;
|
|
315
350
|
engineer?: {
|
|
316
351
|
workflowJson: any;
|
|
@@ -319,8 +354,8 @@ export declare const runAgenticWorkflowStream: (goal: string, threadId?: string)
|
|
|
319
354
|
workflowJson?: undefined;
|
|
320
355
|
candidates?: undefined;
|
|
321
356
|
} | {
|
|
322
|
-
workflowJson: any;
|
|
323
357
|
candidates: any[];
|
|
358
|
+
workflowJson?: undefined;
|
|
324
359
|
} | undefined;
|
|
325
360
|
reviewer?: import("@langchain/langgraph").UpdateType<{
|
|
326
361
|
userGoal: {
|
|
@@ -371,11 +406,14 @@ export declare const runAgenticWorkflowStream: (goal: string, threadId?: string)
|
|
|
371
406
|
};
|
|
372
407
|
candidates: import("@langchain/langgraph").BinaryOperatorAggregate<any[], any[]>;
|
|
373
408
|
customTools: import("@langchain/langgraph").BinaryOperatorAggregate<Record<string, string>, Record<string, string>>;
|
|
409
|
+
collaborationLog: import("@langchain/langgraph").BinaryOperatorAggregate<string[], string[]>;
|
|
374
410
|
}> | undefined;
|
|
375
411
|
supervisor?: {
|
|
376
412
|
workflowJson?: undefined;
|
|
413
|
+
collaborationLog?: undefined;
|
|
377
414
|
} | {
|
|
378
415
|
workflowJson: any;
|
|
416
|
+
collaborationLog: string[];
|
|
379
417
|
} | undefined;
|
|
380
418
|
qa?: import("@langchain/langgraph").UpdateType<{
|
|
381
419
|
userGoal: {
|
|
@@ -426,6 +464,7 @@ export declare const runAgenticWorkflowStream: (goal: string, threadId?: string)
|
|
|
426
464
|
};
|
|
427
465
|
candidates: import("@langchain/langgraph").BinaryOperatorAggregate<any[], any[]>;
|
|
428
466
|
customTools: import("@langchain/langgraph").BinaryOperatorAggregate<Record<string, string>, Record<string, string>>;
|
|
467
|
+
collaborationLog: import("@langchain/langgraph").BinaryOperatorAggregate<string[], string[]>;
|
|
429
468
|
}> | undefined;
|
|
430
469
|
}>>;
|
|
431
470
|
/**
|
|
@@ -480,4 +519,5 @@ export declare const resumeAgenticWorkflow: (threadId: string, input?: any) => P
|
|
|
480
519
|
};
|
|
481
520
|
candidates: import("@langchain/langgraph").BinaryOperatorAggregate<any[], any[]>;
|
|
482
521
|
customTools: import("@langchain/langgraph").BinaryOperatorAggregate<Record<string, string>, Record<string, string>>;
|
|
522
|
+
collaborationLog: import("@langchain/langgraph").BinaryOperatorAggregate<string[], string[]>;
|
|
483
523
|
}>>;
|
|
@@ -1,6 +1,19 @@
|
|
|
1
1
|
import { TeamState } from "../state.js";
|
|
2
2
|
export declare const architectNode: (state: typeof TeamState.State) => Promise<{
|
|
3
|
-
spec:
|
|
4
|
-
strategies:
|
|
5
|
-
|
|
3
|
+
spec: import("../../services/ai.service.js").WorkflowSpec;
|
|
4
|
+
strategies: {
|
|
5
|
+
strategyName: string;
|
|
6
|
+
aiModel: string;
|
|
7
|
+
suggestedName: string;
|
|
8
|
+
description: string;
|
|
9
|
+
nodes: {
|
|
10
|
+
type: string;
|
|
11
|
+
purpose: string;
|
|
12
|
+
config?: any;
|
|
13
|
+
}[];
|
|
14
|
+
questions?: string[];
|
|
15
|
+
aiProvider?: string;
|
|
16
|
+
}[];
|
|
17
|
+
needsClarification: boolean | undefined;
|
|
18
|
+
collaborationLog: string[];
|
|
6
19
|
}>;
|
|
@@ -30,18 +30,29 @@ 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
|
-
//
|
|
34
|
-
//
|
|
35
|
-
|
|
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
|
+
const alternativeModel = aiService.getAlternativeModel();
|
|
36
37
|
const strategies = [
|
|
37
|
-
{
|
|
38
|
-
|
|
39
|
-
|
|
38
|
+
{
|
|
39
|
+
...spec,
|
|
40
|
+
strategyName: "Primary Strategy",
|
|
41
|
+
aiModel: aiService.getDefaultModel()
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
...alternativeSpec,
|
|
45
|
+
strategyName: "Alternative Strategy",
|
|
46
|
+
aiModel: alternativeModel
|
|
47
|
+
},
|
|
40
48
|
];
|
|
49
|
+
const logEntry = `Architect: Generated 2 strategies — "${strategies[0].suggestedName}" (primary) and "${strategies[1].suggestedName}" (alternative)`;
|
|
50
|
+
console.log(`[Architect] ${logEntry}`);
|
|
41
51
|
return {
|
|
42
|
-
spec,
|
|
52
|
+
spec,
|
|
43
53
|
strategies,
|
|
44
54
|
needsClarification,
|
|
55
|
+
collaborationLog: [logEntry],
|
|
45
56
|
};
|
|
46
57
|
}
|
|
47
58
|
catch (error) {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { AIService } from "../../services/ai.service.js";
|
|
2
2
|
import { NodeDefinitionsService } from "../../services/node-definitions.service.js";
|
|
3
|
+
import { jsonrepair } from "jsonrepair";
|
|
3
4
|
export const engineerNode = async (state) => {
|
|
4
5
|
const aiService = AIService.getInstance();
|
|
5
6
|
// RAG: Load and Search Node Definitions
|
|
@@ -9,8 +10,9 @@ export const engineerNode = async (state) => {
|
|
|
9
10
|
const queryText = (state.userGoal + (state.spec ? ` ${state.spec.suggestedName} ${state.spec.description}` : "")).replace(/\n/g, " ");
|
|
10
11
|
// Search for relevant nodes (limit 8 to save context)
|
|
11
12
|
const relevantDefs = nodeService.search(queryText, 8);
|
|
12
|
-
const
|
|
13
|
-
|
|
13
|
+
const staticRef = nodeService.getStaticReference();
|
|
14
|
+
const ragContext = (relevantDefs.length > 0 || staticRef)
|
|
15
|
+
? `\n\n[N8N NODE REFERENCE GUIDE]\n${staticRef}\n\n[AVAILABLE NODE SCHEMAS - USE THESE EXACT PARAMETERS]\n${nodeService.formatForLLM(relevantDefs)}`
|
|
14
16
|
: "";
|
|
15
17
|
if (relevantDefs.length > 0) {
|
|
16
18
|
console.log(`[Engineer] RAG: Found ${relevantDefs.length} relevant node schemas.`);
|
|
@@ -22,7 +24,8 @@ export const engineerNode = async (state) => {
|
|
|
22
24
|
// We pass the entire list of errors as context
|
|
23
25
|
const errorContext = state.validationErrors.join('\n');
|
|
24
26
|
// Use the robust fix logic from AIService
|
|
25
|
-
const fixedWorkflow = await aiService.generateWorkflowFix(state.workflowJson, errorContext,
|
|
27
|
+
const fixedWorkflow = await aiService.generateWorkflowFix(state.workflowJson, errorContext, state.spec?.aiModel, // Pass model if available
|
|
28
|
+
false, state.availableNodeTypes || []);
|
|
26
29
|
return {
|
|
27
30
|
workflowJson: fixedWorkflow,
|
|
28
31
|
// validationErrors will be overwritten by next QA run
|
|
@@ -74,23 +77,27 @@ export const engineerNode = async (state) => {
|
|
|
74
77
|
Output ONLY valid JSON. No commentary. No markdown.
|
|
75
78
|
`;
|
|
76
79
|
// Using AIService just for the LLM call to keep auth logic dry
|
|
77
|
-
const response = await aiService.generateContent(prompt
|
|
80
|
+
const response = await aiService.generateContent(prompt, {
|
|
81
|
+
provider: state.spec.aiProvider,
|
|
82
|
+
model: state.spec.aiModel
|
|
83
|
+
});
|
|
78
84
|
let cleanJson = response || "{}";
|
|
79
85
|
cleanJson = cleanJson.replace(/```json\n?|\n?```/g, "").trim();
|
|
80
86
|
let result;
|
|
81
87
|
try {
|
|
82
|
-
result = JSON.parse(cleanJson);
|
|
88
|
+
result = JSON.parse(jsonrepair(cleanJson));
|
|
83
89
|
}
|
|
84
|
-
catch (
|
|
85
|
-
console.error("Failed to parse workflow JSON from spec",
|
|
90
|
+
catch (e2) {
|
|
91
|
+
console.error("Failed to parse workflow JSON from spec", e2);
|
|
86
92
|
throw new Error("AI generated invalid JSON for workflow from spec");
|
|
87
93
|
}
|
|
88
94
|
if (result.workflows && Array.isArray(result.workflows)) {
|
|
89
95
|
result.workflows = result.workflows.map((wf) => fixHallucinatedNodes(wf));
|
|
90
96
|
}
|
|
91
97
|
return {
|
|
92
|
-
workflowJson
|
|
93
|
-
//
|
|
98
|
+
// Only push to candidates — the Supervisor sets workflowJson after fan-in.
|
|
99
|
+
// Writing workflowJson here would cause a LastValue conflict when two
|
|
100
|
+
// Engineers run in parallel via Send().
|
|
94
101
|
candidates: [result],
|
|
95
102
|
};
|
|
96
103
|
}
|
package/dist/agentic/nodes/qa.js
CHANGED
|
@@ -10,8 +10,9 @@ export const qaNode = async (state) => {
|
|
|
10
10
|
}
|
|
11
11
|
// 1. Load Credentials
|
|
12
12
|
const config = await ConfigManager.load();
|
|
13
|
-
|
|
14
|
-
const
|
|
13
|
+
// Env vars take priority over stored config so a fresh key in .env is always used
|
|
14
|
+
const n8nUrl = process.env.N8N_API_URL || config.n8nUrl;
|
|
15
|
+
const n8nKey = process.env.N8N_API_KEY || config.n8nKey;
|
|
15
16
|
if (!n8nUrl || !n8nKey) {
|
|
16
17
|
throw new Error('Credentials missing. Configure environment via \'n8m config\'.');
|
|
17
18
|
}
|
|
@@ -28,10 +29,21 @@ export const qaNode = async (state) => {
|
|
|
28
29
|
targetWorkflow = workflowJson.workflows[0];
|
|
29
30
|
}
|
|
30
31
|
const workflowName = targetWorkflow.name || 'Agentic_Test_Workflow';
|
|
32
|
+
// Drop timezone — sanitizeSettings in N8nClient strips it unconditionally
|
|
33
|
+
const rawSettings = { ...(targetWorkflow.settings || {}) };
|
|
34
|
+
delete rawSettings.timezone;
|
|
35
|
+
// Strip credentials from all nodes — n8n 2.x refuses to activate ("publish")
|
|
36
|
+
// a workflow that references credentials that don't exist on the instance.
|
|
37
|
+
// Structural validation can still run; only live execution of credentialed
|
|
38
|
+
// nodes will be skipped/fail, which is expected for an ephemeral test.
|
|
39
|
+
const strippedNodes = targetWorkflow.nodes.map((node) => {
|
|
40
|
+
const { credentials: _creds, ...rest } = node;
|
|
41
|
+
return rest;
|
|
42
|
+
});
|
|
31
43
|
const rootPayload = {
|
|
32
|
-
nodes:
|
|
44
|
+
nodes: strippedNodes,
|
|
33
45
|
connections: targetWorkflow.connections,
|
|
34
|
-
settings:
|
|
46
|
+
settings: rawSettings,
|
|
35
47
|
staticData: targetWorkflow.staticData || {},
|
|
36
48
|
name: `[n8m:test] ${workflowName}`,
|
|
37
49
|
};
|
|
@@ -66,6 +66,8 @@ export const reviewerNode = async (state) => {
|
|
|
66
66
|
// Triggers usually have no input.
|
|
67
67
|
// We use a broader check: any node with "trigger" or "webhook" in the name, plus generic start types.
|
|
68
68
|
const isTrigger = (type) => {
|
|
69
|
+
if (!type)
|
|
70
|
+
return false;
|
|
69
71
|
const lower = type.toLowerCase();
|
|
70
72
|
return lower.includes('trigger') ||
|
|
71
73
|
lower.includes('webhook') ||
|
|
@@ -79,9 +81,9 @@ export const reviewerNode = async (state) => {
|
|
|
79
81
|
// Sticky notes and Merge nodes can be tricky, but generally Merge needs input.
|
|
80
82
|
if (!node.type.includes('StickyNote')) {
|
|
81
83
|
// Double check for "On Execution" (custom trigger name sometimes used)
|
|
82
|
-
if (!node.name.toLowerCase().includes('trigger') && !node.name.toLowerCase().includes('webhook')) {
|
|
83
|
-
console.log(theme.warn(`[Reviewer] Validated disconnection: Node "${node.name}" has no incoming connections.`));
|
|
84
|
-
validationErrors.push(`Node "${node.name}" (${node.type}) is disconnected (orphaned). Connect it or remove it.`);
|
|
84
|
+
if (!node.name || (!node.name.toLowerCase().includes('trigger') && !node.name.toLowerCase().includes('webhook'))) {
|
|
85
|
+
console.log(theme.warn(`[Reviewer] Validated disconnection: Node "${node.name || 'Unnamed'}" has no incoming connections.`));
|
|
86
|
+
validationErrors.push(`Node "${node.name || 'Unnamed'}" (${node.type || 'unknown type'}) is disconnected (orphaned). Connect it or remove it.`);
|
|
85
87
|
}
|
|
86
88
|
}
|
|
87
89
|
}
|
|
@@ -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
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
const
|
|
13
|
-
console.log(theme.success(`Supervisor selected: ${bestCandidate.name || "Unnamed Workflow"}`));
|
|
14
|
-
|
|
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
|
};
|
package/dist/agentic/state.d.ts
CHANGED
|
@@ -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
|
}>;
|
package/dist/agentic/state.js
CHANGED
|
@@ -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
|
});
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"name": "n8n-nodes-base.start",
|
|
4
|
+
"displayName": "Manual Trigger",
|
|
5
|
+
"description": "The starting point for manual execution.",
|
|
6
|
+
"properties": []
|
|
7
|
+
},
|
|
8
|
+
{
|
|
9
|
+
"name": "n8n-nodes-base.httpRequest",
|
|
10
|
+
"displayName": "HTTP Request",
|
|
11
|
+
"description": "Send HTTP requests to any API",
|
|
12
|
+
"properties": [
|
|
13
|
+
{
|
|
14
|
+
"name": "method",
|
|
15
|
+
"displayName": "Method",
|
|
16
|
+
"type": "options",
|
|
17
|
+
"default": "GET",
|
|
18
|
+
"options": [
|
|
19
|
+
{ "name": "GET", "value": "GET" },
|
|
20
|
+
{ "name": "POST", "value": "POST" },
|
|
21
|
+
{ "name": "PUT", "value": "PUT" },
|
|
22
|
+
{ "name": "DELETE", "value": "DELETE" }
|
|
23
|
+
]
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"name": "url",
|
|
27
|
+
"displayName": "URL",
|
|
28
|
+
"type": "string",
|
|
29
|
+
"default": "",
|
|
30
|
+
"description": "The URL to send the request to"
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"name": "authentication",
|
|
34
|
+
"displayName": "Authentication",
|
|
35
|
+
"type": "options",
|
|
36
|
+
"default": "none",
|
|
37
|
+
"options": [
|
|
38
|
+
{ "name": "None", "value": "none" },
|
|
39
|
+
{ "name": "Predefined Credential Type", "value": "predefinedCredentialType" }
|
|
40
|
+
]
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
"name": "sendBody",
|
|
44
|
+
"displayName": "Send Body",
|
|
45
|
+
"type": "boolean",
|
|
46
|
+
"default": false
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
"name": "body",
|
|
50
|
+
"displayName": "Body",
|
|
51
|
+
"type": "json",
|
|
52
|
+
"default": ""
|
|
53
|
+
}
|
|
54
|
+
]
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
"name": "n8n-nodes-base.slack",
|
|
58
|
+
"displayName": "Slack",
|
|
59
|
+
"description": "Interact with Slack",
|
|
60
|
+
"properties": [
|
|
61
|
+
{
|
|
62
|
+
"name": "resource",
|
|
63
|
+
"displayName": "Resource",
|
|
64
|
+
"type": "options",
|
|
65
|
+
"default": "message",
|
|
66
|
+
"options": [
|
|
67
|
+
{ "name": "Message", "value": "message" },
|
|
68
|
+
{ "name": "Channel", "value": "channel" }
|
|
69
|
+
]
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
"name": "operation",
|
|
73
|
+
"displayName": "Operation",
|
|
74
|
+
"type": "options",
|
|
75
|
+
"default": "post",
|
|
76
|
+
"options": [
|
|
77
|
+
{ "name": "Post", "value": "post" },
|
|
78
|
+
{ "name": "Update", "value": "update" }
|
|
79
|
+
]
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
"name": "channel",
|
|
83
|
+
"displayName": "Channel",
|
|
84
|
+
"type": "string",
|
|
85
|
+
"default": ""
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
"name": "text",
|
|
89
|
+
"displayName": "Text",
|
|
90
|
+
"type": "string",
|
|
91
|
+
"default": ""
|
|
92
|
+
}
|
|
93
|
+
]
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
"name": "n8n-nodes-base.googleSheets",
|
|
97
|
+
"displayName": "Google Sheets",
|
|
98
|
+
"description": "Interact with Google Sheets",
|
|
99
|
+
"properties": [
|
|
100
|
+
{
|
|
101
|
+
"name": "resource",
|
|
102
|
+
"displayName": "Resource",
|
|
103
|
+
"type": "options",
|
|
104
|
+
"default": "sheet",
|
|
105
|
+
"options": [
|
|
106
|
+
{ "name": "Sheet", "value": "sheet" }
|
|
107
|
+
]
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
"name": "operation",
|
|
111
|
+
"displayName": "Operation",
|
|
112
|
+
"type": "options",
|
|
113
|
+
"default": "append",
|
|
114
|
+
"options": [
|
|
115
|
+
{ "name": "Append", "value": "append" },
|
|
116
|
+
{ "name": "Read", "value": "read" },
|
|
117
|
+
{ "name": "Update", "value": "update" }
|
|
118
|
+
]
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
"name": "sheetId",
|
|
122
|
+
"displayName": "Sheet ID",
|
|
123
|
+
"type": "string",
|
|
124
|
+
"default": ""
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
"name": "range",
|
|
128
|
+
"displayName": "Range",
|
|
129
|
+
"type": "string",
|
|
130
|
+
"default": "Sheet1!A:Z"
|
|
131
|
+
}
|
|
132
|
+
]
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
"name": "n8n-nodes-base.if",
|
|
136
|
+
"displayName": "IF",
|
|
137
|
+
"description": "Conditional logic: True/False",
|
|
138
|
+
"properties": [
|
|
139
|
+
{
|
|
140
|
+
"name": "conditions",
|
|
141
|
+
"displayName": "Conditions",
|
|
142
|
+
"type": "json",
|
|
143
|
+
"default": {}
|
|
144
|
+
}
|
|
145
|
+
]
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
"name": "n8n-nodes-base.set",
|
|
149
|
+
"displayName": "Set",
|
|
150
|
+
"description": "Set variables or values",
|
|
151
|
+
"properties": [
|
|
152
|
+
{
|
|
153
|
+
"name": "values",
|
|
154
|
+
"displayName": "Values",
|
|
155
|
+
"type": "fixedCollection",
|
|
156
|
+
"default": {}
|
|
157
|
+
}
|
|
158
|
+
]
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
"name": "n8n-nodes-base.webhook",
|
|
162
|
+
"displayName": "Webhook",
|
|
163
|
+
"description": "Receive HTTP requests",
|
|
164
|
+
"properties": [
|
|
165
|
+
{
|
|
166
|
+
"name": "httpMethod",
|
|
167
|
+
"displayName": "HTTP Method",
|
|
168
|
+
"type": "options",
|
|
169
|
+
"default": "GET",
|
|
170
|
+
"options": [
|
|
171
|
+
{ "name": "GET", "value": "GET" },
|
|
172
|
+
{ "name": "POST", "value": "POST" }
|
|
173
|
+
]
|
|
174
|
+
},
|
|
175
|
+
{
|
|
176
|
+
"name": "path",
|
|
177
|
+
"displayName": "Path",
|
|
178
|
+
"type": "string",
|
|
179
|
+
"default": ""
|
|
180
|
+
}
|
|
181
|
+
]
|
|
182
|
+
},
|
|
183
|
+
{
|
|
184
|
+
"name": "n8n-nodes-base.code",
|
|
185
|
+
"displayName": "Code",
|
|
186
|
+
"description": "Run JavaScript/TypeScript",
|
|
187
|
+
"properties": [
|
|
188
|
+
{
|
|
189
|
+
"name": "jsCode",
|
|
190
|
+
"displayName": "JS Code",
|
|
191
|
+
"type": "string",
|
|
192
|
+
"default": "// Your code here\nreturn items;"
|
|
193
|
+
}
|
|
194
|
+
]
|
|
195
|
+
},
|
|
196
|
+
{
|
|
197
|
+
"name": "n8n-nodes-base.merge",
|
|
198
|
+
"displayName": "Merge",
|
|
199
|
+
"description": "Merge data from multiple inputs",
|
|
200
|
+
"properties": [
|
|
201
|
+
{
|
|
202
|
+
"name": "mode",
|
|
203
|
+
"displayName": "Mode",
|
|
204
|
+
"type": "options",
|
|
205
|
+
"default": "append",
|
|
206
|
+
"options": [
|
|
207
|
+
{ "name": "Append", "value": "append" },
|
|
208
|
+
{ "name": "Merge by Index", "value": "mergeByIndex" }
|
|
209
|
+
]
|
|
210
|
+
}
|
|
211
|
+
]
|
|
212
|
+
}
|
|
213
|
+
]
|