n8n
Version:
n8n Workflow Automation Tool
109 lines • 5.49 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.createRichInteractionTool = createRichInteractionTool;
const agents_1 = require("@n8n/agents");
const di_1 = require("@n8n/di");
const zod_1 = require("zod");
const agent_chat_integration_1 = require("./agent-chat-integration");
const DEFAULT_SUPPORTED_COMPONENTS = ['section', 'button', 'divider', 'fields'];
const RICH_INTERACTION_INSTRUCTION_FRAGMENT = 'When you have an image URL, gif, or content that benefits from visual ' +
'structure (key-value summaries, info cards, choice options), call the ' +
'rich_interaction tool to render it as a card instead of pasting URLs or ' +
'text directly. With no buttons or selects, the card simply displays and ' +
'you continue immediately — no need to wait for a user response.';
function buildDescription(supportedComponents) {
const componentList = supportedComponents.join(', ');
return [
'Render a card to the user in chat. Use this whenever you want the user to ',
'SEE rich content rather than read plain text — images and gifs, formatted ',
'info cards, key-value summaries, or sets of choices.',
'\n\n',
`Available components: ${componentList}.`,
'\n\n',
'Behavior depends on which components you include:',
'\n',
' • Display-only (no button, select, or radio_select): the card renders in chat ',
'and you continue immediately. Use this for posting gifs, screenshots, summary cards.',
'\n',
' • Interactive (any button, select, or radio_select): the card renders and the ',
"run pauses; the user's click/selection is returned as your tool result.",
'\n\n',
'Prefer this tool over plain text whenever you have an image URL, a gif, or ',
'content that benefits from visual structure.',
].join('');
}
const selectOptionSchema = zod_1.z.object({
label: zod_1.z.string().describe('Display text'),
value: zod_1.z.string().describe('Value returned on selection'),
description: zod_1.z.string().optional().describe('Help text'),
});
const fieldPairSchema = zod_1.z.object({
label: zod_1.z.string().describe('Field label'),
value: zod_1.z.string().describe('Field value'),
});
const resumeSchema = zod_1.z.discriminatedUnion('type', [
zod_1.z.object({
type: zod_1.z.literal('button'),
value: zod_1.z.string().describe('The clicked button value'),
}),
zod_1.z.object({
type: zod_1.z.literal('select'),
id: zod_1.z.string().describe('The select component ID'),
value: zod_1.z.string().describe('The selected option value'),
}),
]);
function buildComponentSchema(supportedComponents) {
const types = supportedComponents;
const hasSelects = supportedComponents.includes('select') || supportedComponents.includes('radio_select');
const hasImage = supportedComponents.includes('image');
const shape = {
type: zod_1.z.enum(types).describe('Component type'),
text: zod_1.z.string().optional().describe('Text content (supports markdown)'),
label: zod_1.z.string().optional().describe('Display label'),
value: zod_1.z.string().optional().describe('Value returned on interaction'),
style: zod_1.z.enum(['primary', 'danger']).optional().describe('Button style'),
fields: zod_1.z.array(fieldPairSchema).optional().describe('Key-value pairs for fields component'),
};
if (hasSelects) {
shape.id = zod_1.z.string().optional().describe('Unique ID for select/radio_select');
shape.placeholder = zod_1.z.string().optional().describe('Placeholder text');
shape.options = zod_1.z
.array(selectOptionSchema)
.optional()
.describe('Options for select/radio_select');
}
if (hasImage) {
shape.url = zod_1.z.string().optional().describe('Image URL');
shape.alt = zod_1.z.string().optional().describe('Image alt text');
}
return zod_1.z.object(shape);
}
function createRichInteractionTool(platform) {
const integration = platform ? di_1.Container.get(agent_chat_integration_1.ChatIntegrationRegistry).get(platform) : undefined;
const supportedComponents = integration?.supportedComponents ?? DEFAULT_SUPPORTED_COMPONENTS;
const description = buildDescription(supportedComponents);
const componentSchema = buildComponentSchema(supportedComponents);
const inputSchema = zod_1.z.object({
title: zod_1.z.string().optional().describe('Card title / header text'),
message: zod_1.z.string().optional().describe('Subtitle or description text'),
components: zod_1.z.array(componentSchema).describe('Card components to render'),
});
const suspendSchema = inputSchema;
return new agents_1.Tool('rich_interaction')
.description(description)
.systemInstruction(RICH_INTERACTION_INSTRUCTION_FRAGMENT)
.input(inputSchema)
.suspend(suspendSchema)
.resume(resumeSchema)
.handler(async (input, ctx) => {
if (ctx.resumeData) {
return ctx.resumeData;
}
const hasActionable = input.components.some((c) => c.type === 'button' || c.type === 'select' || c.type === 'radio_select');
if (!hasActionable) {
return { displayOnly: true };
}
return await ctx.suspend(input);
});
}
//# sourceMappingURL=rich-interaction-tool.js.map