UNPKG

n8n

Version:

n8n Workflow Automation Tool

174 lines • 8.9 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.readKnowledgeInputSchema = exports.globKnowledgeFilesInputSchema = exports.searchKnowledgeInputSchema = exports.MAX_READ_RANGES = exports.MAX_SEARCH_CONTEXT_LINES = exports.MAX_OPERATION_OUTPUT_CHARS = exports.MAX_READ_LINE_CHARS = exports.MAX_SEARCH_LINE_CHARS = exports.DEFAULT_GLOB_FILES_LIMIT = exports.DEFAULT_SEARCH_TEXT_LIMIT = void 0; exports.parseSearchKnowledgeRequest = parseSearchKnowledgeRequest; exports.parseGlobKnowledgeFilesRequest = parseGlobKnowledgeFilesRequest; exports.parseReadKnowledgeRequest = parseReadKnowledgeRequest; exports.assertValidKnowledgeFilePath = assertValidKnowledgeFilePath; exports.truncateKnowledgeText = truncateKnowledgeText; const bad_request_error_1 = require("../../errors/response-errors/bad-request.error"); const zod_1 = require("zod"); const agent_knowledge_storage_1 = require("./agent-knowledge-storage"); const MAX_SEARCH_PATTERN_LENGTH = 500; const MAX_GLOB_PATTERN_LENGTH = 255; const MAX_FILE_PATH_LENGTH = 512; const MAX_SEARCH_TEXT_LIMIT = 100; const MAX_GLOB_FILES_LIMIT = 100; const MAX_FILE_ID_LENGTH = 64; const MAX_SEARCH_PATHS = 20; exports.DEFAULT_SEARCH_TEXT_LIMIT = 20; exports.DEFAULT_GLOB_FILES_LIMIT = 20; exports.MAX_SEARCH_LINE_CHARS = 500; exports.MAX_READ_LINE_CHARS = 2_000; exports.MAX_OPERATION_OUTPUT_CHARS = 20_000; exports.MAX_SEARCH_CONTEXT_LINES = 10; exports.MAX_READ_RANGES = 10; const filePathSchema = zod_1.z.string().trim().min(1).max(MAX_FILE_PATH_LENGTH); const ALL_FILES_PATH_PLACEHOLDERS = new Set(['', '.', '/', '*']); const searchPathSchema = zod_1.z.preprocess((value) => { const values = typeof value === 'string' ? [value] : value; if (!Array.isArray(values)) return values; const scoped = values.filter((entry) => !(typeof entry === 'string' && ALL_FILES_PATH_PLACEHOLDERS.has(entry.trim()))); return scoped.length === 0 ? undefined : scoped; }, zod_1.z.array(filePathSchema).min(1).max(MAX_SEARCH_PATHS).optional()); const fileIdSchema = zod_1.z.string().trim().min(1).max(MAX_FILE_ID_LENGTH); const searchPatternSchema = zod_1.z.string().trim().min(1).max(MAX_SEARCH_PATTERN_LENGTH); const globPatternSchema = zod_1.z.string().trim().min(1).max(MAX_GLOB_PATTERN_LENGTH); const searchOutputModeSchema = zod_1.z.enum(['content', 'files_with_matches', 'count']); const searchContextFlagSchema = zod_1.z.number().int().min(0).max(exports.MAX_SEARCH_CONTEXT_LINES); exports.searchKnowledgeInputSchema = zod_1.z .object({ pattern: searchPatternSchema.describe('Ripgrep regex pattern to search for in uploaded knowledge file contents. This is line-based regex search, not semantic search. Simple words and phrases usually work as-is; escape punctuation-heavy literals when needed.'), path: searchPathSchema.describe('Optional uploaded knowledge file path or paths to search within. Pass one exact file value or an array of exact file values copied from previous knowledge tool results to scope the search. Omit to search across every uploaded knowledge file.'), output_mode: searchOutputModeSchema .optional() .describe('Optional output mode. Defaults to content. Use files_with_matches to identify matching uploaded files without snippets, or count to compare match frequency by file.'), head_limit: zod_1.z .number() .int() .min(1) .max(MAX_SEARCH_TEXT_LIMIT) .optional() .describe('Optional maximum number of results to return. Use a small value such as 5-20, then narrow the pattern if results have hasMore or truncated.'), '-C': searchContextFlagSchema .optional() .describe('Optional symmetric context lines around each content match, equivalent to ripgrep -C. Use 0 or omit for no surrounding context.'), '-i': zod_1.z .boolean() .optional() .describe('Optional case-insensitive search flag. Defaults to true for uploaded knowledge search; set false only when capitalization matters.'), }) .strict(); exports.globKnowledgeFilesInputSchema = zod_1.z .object({ pattern: globPatternSchema.describe('Filename pattern matched against uploaded knowledge file names, not file contents. Supports * and ? wildcards and is case-insensitive by default. Use `*` to list every uploaded file, `*.pdf` to filter by extension, or name fragments like `*bert*` when the user gives title or filename clues.'), limit: zod_1.z .number() .int() .min(1) .max(MAX_GLOB_FILES_LIMIT) .optional() .describe('Optional maximum number of candidate files to return. Use a small value such as 5-20 and make the pattern more specific if hasMore is true.'), offset: zod_1.z .number() .int() .min(0) .optional() .describe('Optional number of matching files to skip, for paging. When hasMore is true, call again with offset = previous offset + number of returned files.'), caseSensitive: zod_1.z .boolean() .optional() .describe('Optional. Defaults to false for case-insensitive filename matching. Set true only when filename capitalization is part of the exact evidence you need.'), }) .strict() .superRefine((input, ctx) => { const segments = input.pattern.split('/'); if (input.pattern.startsWith('/') || input.pattern.includes('\\') || (0, agent_knowledge_storage_1.hasControlCharacter)(input.pattern) || segments.some((segment) => segment === '.' || segment === '..' || segment.length === 0)) { ctx.addIssue({ code: zod_1.z.ZodIssueCode.custom, path: ['pattern'], message: 'Invalid knowledge file pattern', }); } }); exports.readKnowledgeInputSchema = zod_1.z .object({ file: filePathSchema .optional() .describe('Uploaded knowledge file path to read. Use only an exact `file` value copied from a previous knowledge tool result. Required unless `fileId` is provided.'), fileId: fileIdSchema .optional() .describe('Uploaded knowledge file ID to read. Use only an exact `fileId` copied from a previous knowledge tool result. Required unless `file` is provided.'), ranges: zod_1.z .array(zod_1.z .object({ startLine: zod_1.z .number() .int() .min(1) .describe('First 1-based line number to read, usually near a search_text match. Keep ranges narrow enough for citation-ready evidence.'), endLine: zod_1.z .number() .int() .min(1) .describe('Last 1-based line number to read. Must be greater than or equal to startLine. Prefer short ranges around the evidence.'), }) .strict()) .min(1) .max(exports.MAX_READ_RANGES) .optional() .describe('Optional line ranges to read from the selected file. Prefer bounded ranges from search_text matches; omit only when full-file context is genuinely needed and output truncation is acceptable.'), }) .strict() .superRefine((input, ctx) => { if (!input.file && !input.fileId) { ctx.addIssue({ code: zod_1.z.ZodIssueCode.custom, message: 'Provide file or fileId', }); } for (const [index, range] of (input.ranges ?? []).entries()) { if (range.endLine < range.startLine) { ctx.addIssue({ code: zod_1.z.ZodIssueCode.custom, path: ['ranges', index, 'endLine'], message: 'endLine must be greater than or equal to startLine', }); } } }); function parseSearchKnowledgeRequest(input) { return exports.searchKnowledgeInputSchema.parse(input); } function parseGlobKnowledgeFilesRequest(input) { return exports.globKnowledgeFilesInputSchema.parse(input); } function parseReadKnowledgeRequest(input) { return exports.readKnowledgeInputSchema.parse(input); } function assertValidKnowledgeFilePath(filePath) { const trimmed = filePath.trim(); if (!trimmed || trimmed.length > MAX_FILE_PATH_LENGTH || trimmed.startsWith('/') || trimmed.includes('\\') || (0, agent_knowledge_storage_1.hasControlCharacter)(trimmed)) { throw new bad_request_error_1.BadRequestError('Invalid knowledge file path'); } const segments = trimmed.split('/'); if (segments.some((segment) => segment === '.' || segment === '..' || segment.length === 0)) { throw new bad_request_error_1.BadRequestError('Invalid knowledge file path'); } return trimmed; } function truncateKnowledgeText(text, maxLength) { if (text.length <= maxLength) { return { text, truncated: false }; } return { text: text.slice(0, maxLength), truncated: true }; } //# sourceMappingURL=agent-knowledge-retrieval.js.map