UNPKG

n8n

Version:

n8n Workflow Automation Tool

103 lines 4.88 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.buildAskQuestionsTool = buildAskQuestionsTool; const tool_1 = require("@n8n/agents/tool"); const api_types_1 = require("@n8n/api-types"); const telemetry_1 = require("@n8n/telemetry"); const nanoid_1 = require("nanoid"); const n8n_workflow_1 = require("n8n-workflow"); const zod_1 = require("zod"); const askQuestionsQuestionSchema = zod_1.z.object({ id: zod_1.z.string().optional().describe('Unique question identifier; defaults to q1, q2, ...'), question: zod_1.z.string().describe('The question text to display to the user'), type: zod_1.z .enum(['single', 'multi', 'text']) .describe('single = pick one option, multi = pick many, text = free-form input'), options: zod_1.z .array(zod_1.z.string()) .optional() .describe('Suggested answers (required for single/multi, ignored for text)'), }); const askQuestionsInputSchema = zod_1.z.object({ questions: zod_1.z .array(askQuestionsQuestionSchema) .min(1) .describe('All questions to ask the user, batched into a single card'), introMessage: zod_1.z.string().optional().describe('Brief intro text shown above the questions'), }); function withDefaultIds(questions) { const explicitIds = questions.map((q) => q.id).filter((id) => id !== undefined); if (new Set(explicitIds).size !== explicitIds.length) { throw new n8n_workflow_1.UserError('ask_questions: question ids must be unique'); } const usedIds = new Set(explicitIds); return questions.map((question, index) => { if (question.id) return { ...question, id: question.id }; let candidate = index + 1; let id = `q${candidate}`; while (usedIds.has(id)) { candidate++; id = `q${candidate}`; } usedIds.add(id); return { ...question, id }; }); } function enrichAnswers(answers, questions) { return answers.map((answer) => { const question = questions.find((q) => q.id === answer.questionId); return { ...answer, question: question?.question ?? answer.questionId }; }); } function usableAnswers(resumeData) { if (resumeData.approved === false) return undefined; if (!resumeData.answers || resumeData.answers.length === 0) return undefined; if (resumeData.answers.every((answer) => answer.skipped === true)) return undefined; return resumeData.answers; } function buildAskQuestionsTool(deps) { return new tool_1.Tool(api_types_1.ASK_QUESTIONS_TOOL_NAME) .description('Ask the user one or more questions in a single batched card; the run suspends until ' + 'they respond. ALWAYS use this instead of calling it multiple times when you have more ' + 'than one question — batch them into one call. Questions are single-select, ' + 'multi-select, or free-text. A question is asked at most once — a dismissal or empty ' + 'answer means "proceed without this": assume a sensible default and never re-present ' + 'it. Returns { answered: false } on dismissal, or { answered: true, answers } otherwise.') .input(askQuestionsInputSchema) .suspend(api_types_1.questionsSuspendPayloadSchema) .resume(api_types_1.questionsResumeSchema) .handler(async (input, ctx) => { const questions = withDefaultIds(input.questions); if (ctx.resumeData === undefined || ctx.resumeData === null) { deps.track(telemetry_1.TELEMETRY_EVENT.AGENTS.BUILDER_ASKED_QUESTIONS, { question_count: questions.length, question_types: [...new Set(questions.map((q) => q.type))].sort(), }); return await ctx.suspend({ requestId: (0, nanoid_1.nanoid)(), message: input.introMessage ?? 'The agent builder has questions', severity: 'info', inputType: 'questions', questions, ...(input.introMessage ? { introMessage: input.introMessage } : {}), }); } const answers = usableAnswers(ctx.resumeData); const rawAnswers = ctx.resumeData.answers ?? []; const skippedCount = rawAnswers.filter((answer) => answer.skipped === true).length; deps.track(telemetry_1.TELEMETRY_EVENT.AGENTS.USER_ANSWERED_BUILDER_QUESTIONS, { outcome: ctx.resumeData.approved === false ? 'dismissed' : answers ? 'answered' : 'skipped', answered_count: rawAnswers.length - skippedCount, skipped_count: skippedCount, }); if (!answers) return { answered: false }; return { answered: true, answers: enrichAnswers(answers, questions) }; }) .build(); } //# sourceMappingURL=ask-questions.tool.js.map