fix: AI connection type propagation and get_node improvements (v2.28.1) (#461)

* fix: AI connection type propagation and get_node improvements (v2.28.1)

Bug fixes:
- Issue #458: addConnection now preserves AI connection types (ai_tool, ai_memory, ai_languageModel) instead of defaulting to 'main'
- Fixed false positive "AI Agent has no tools connected" validation warning

Enhancements:
- Added expectedFormat field to resourceLocator properties in get_node output
- Added versionNotice field to make typeVersion more prominent

Conceived by Romuald Członkowski - https://www.aiadvisors.pl/en

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

* test: add missing test coverage for PR #461 improvements

- Added test for AI Agent validation positive case (tools properly connected)
- Added 3 tests for expectedFormat on resourceLocator properties

Conceived by Romuald Członkowski - https://www.aiadvisors.pl/en

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

---------

Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
Romuald Członkowski
2025-12-01 18:54:15 +01:00
committed by GitHub
parent 33690c5650
commit 3188d209b7
9 changed files with 408 additions and 9 deletions

View File

@@ -2203,14 +2203,19 @@ Full documentation is being prepared. For now, use get_node_essentials for confi
// Get operations (already parsed by repository)
const operations = node.operations || [];
// Get the latest version - this is important for AI to use correct typeVersion
const latestVersion = node.version ?? '1';
const result = {
nodeType: node.nodeType,
workflowNodeType: getWorkflowNodeType(node.package ?? 'n8n-nodes-base', node.nodeType),
displayName: node.displayName,
description: node.description,
category: node.category,
version: node.version ?? '1',
version: latestVersion,
isVersioned: node.isVersioned ?? false,
// Prominent warning to use the correct typeVersion
versionNotice: `⚠️ Use typeVersion: ${latestVersion} when creating this node`,
requiredProperties: essentials.required,
commonProperties: essentials.common,
operations: operations.map((op: any) => ({

View File

@@ -16,6 +16,11 @@ export interface SimplifiedProperty {
placeholder?: string;
showWhen?: Record<string, any>;
usageHint?: string;
expectedFormat?: {
structure: Record<string, string>;
modes?: string[];
example: Record<string, any>;
};
}
export interface EssentialConfig {
@@ -322,7 +327,18 @@ export class PropertyFilter {
};
});
}
// Add expectedFormat for resourceLocator types - critical for correct configuration
if (prop.type === 'resourceLocator') {
const modes = prop.modes?.map((m: any) => m.name || m) || ['list', 'id'];
const defaultValue = prop.default?.value || 'your-resource-id';
simplified.expectedFormat = {
structure: { mode: 'string', value: 'string' },
modes,
example: { mode: 'id', value: defaultValue }
};
}
// Include simple display conditions (max 2 conditions)
if (prop.displayOptions?.show) {
const conditions = Object.keys(prop.displayOptions.show);

View File

@@ -757,7 +757,8 @@ export class WorkflowDiffEngine {
const { sourceOutput, sourceIndex } = this.resolveSmartParameters(workflow, operation);
// Use nullish coalescing to properly handle explicit 0 values
const targetInput = operation.targetInput ?? 'main';
// Default targetInput to sourceOutput to preserve connection type for AI connections (ai_tool, ai_memory, etc.)
const targetInput = operation.targetInput ?? sourceOutput;
const targetIndex = operation.targetIndex ?? 0;
// Initialize source node connections object

View File

@@ -1137,16 +1137,23 @@ export class WorkflowValidator {
}
// Check for AI Agent workflows
const aiAgentNodes = workflow.nodes.filter(n =>
n.type.toLowerCase().includes('agent') ||
const aiAgentNodes = workflow.nodes.filter(n =>
n.type.toLowerCase().includes('agent') ||
n.type.includes('langchain.agent')
);
if (aiAgentNodes.length > 0) {
// Check if AI agents have tools connected
// Tools connect TO the agent, so we need to find connections where the target is the agent
for (const agentNode of aiAgentNodes) {
const connections = workflow.connections[agentNode.name];
if (!connections?.ai_tool || connections.ai_tool.flat().filter(c => c).length === 0) {
// Search all connections to find ones targeting this agent via ai_tool
const hasToolConnected = Object.values(workflow.connections).some(sourceOutputs => {
const aiToolConnections = sourceOutputs.ai_tool;
if (!aiToolConnections) return false;
return aiToolConnections.flat().some(conn => conn && conn.node === agentNode.name);
});
if (!hasToolConnected) {
result.warnings.push({
type: 'warning',
nodeId: agentNode.id,