@morphllm/morphsdk 0.2.56 → 0.2.57

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.
Files changed (47) hide show
  1. package/dist/{chunk-SALJ2K6S.js → chunk-6X5UOY7B.js} +32 -37
  2. package/dist/chunk-6X5UOY7B.js.map +1 -0
  3. package/dist/{chunk-UIRJE422.js → chunk-CFF636UC.js} +3 -3
  4. package/dist/{chunk-QVRXBAMM.js → chunk-GJ5TYNRD.js} +2 -2
  5. package/dist/{chunk-4ZHDBKBY.js → chunk-IMYQOKFO.js} +3 -3
  6. package/dist/{chunk-GJURLQ3L.js → chunk-KBQWGT5L.js} +3 -3
  7. package/dist/{chunk-WSSSSBWU.js → chunk-QFIHUCTF.js} +5 -5
  8. package/dist/client.cjs +28 -142
  9. package/dist/client.cjs.map +1 -1
  10. package/dist/client.js +7 -8
  11. package/dist/index.cjs +28 -142
  12. package/dist/index.cjs.map +1 -1
  13. package/dist/index.js +7 -8
  14. package/dist/tools/warp_grep/agent/runner.cjs +28 -142
  15. package/dist/tools/warp_grep/agent/runner.cjs.map +1 -1
  16. package/dist/tools/warp_grep/agent/runner.js +2 -3
  17. package/dist/tools/warp_grep/anthropic.cjs +28 -142
  18. package/dist/tools/warp_grep/anthropic.cjs.map +1 -1
  19. package/dist/tools/warp_grep/anthropic.js +4 -5
  20. package/dist/tools/warp_grep/harness.cjs +859 -0
  21. package/dist/tools/warp_grep/harness.cjs.map +1 -0
  22. package/dist/tools/warp_grep/harness.d.ts +176 -0
  23. package/dist/tools/warp_grep/harness.js +76 -0
  24. package/dist/tools/warp_grep/harness.js.map +1 -0
  25. package/dist/tools/warp_grep/index.cjs +28 -142
  26. package/dist/tools/warp_grep/index.cjs.map +1 -1
  27. package/dist/tools/warp_grep/index.js +6 -7
  28. package/dist/tools/warp_grep/openai.cjs +28 -142
  29. package/dist/tools/warp_grep/openai.cjs.map +1 -1
  30. package/dist/tools/warp_grep/openai.js +4 -5
  31. package/dist/tools/warp_grep/vercel.cjs +28 -142
  32. package/dist/tools/warp_grep/vercel.cjs.map +1 -1
  33. package/dist/tools/warp_grep/vercel.js +4 -5
  34. package/package.json +7 -2
  35. package/dist/chunk-NDZO5IPV.js +0 -121
  36. package/dist/chunk-NDZO5IPV.js.map +0 -1
  37. package/dist/chunk-SALJ2K6S.js.map +0 -1
  38. package/dist/tools/warp_grep/agent/grep_helpers.cjs +0 -148
  39. package/dist/tools/warp_grep/agent/grep_helpers.cjs.map +0 -1
  40. package/dist/tools/warp_grep/agent/grep_helpers.d.ts +0 -16
  41. package/dist/tools/warp_grep/agent/grep_helpers.js +0 -14
  42. package/dist/tools/warp_grep/agent/grep_helpers.js.map +0 -1
  43. /package/dist/{chunk-UIRJE422.js.map → chunk-CFF636UC.js.map} +0 -0
  44. /package/dist/{chunk-QVRXBAMM.js.map → chunk-GJ5TYNRD.js.map} +0 -0
  45. /package/dist/{chunk-4ZHDBKBY.js.map → chunk-IMYQOKFO.js.map} +0 -0
  46. /package/dist/{chunk-GJURLQ3L.js.map → chunk-KBQWGT5L.js.map} +0 -0
  47. /package/dist/{chunk-WSSSSBWU.js.map → chunk-QFIHUCTF.js.map} +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../tools/warp_grep/harness.ts","../../../tools/warp_grep/agent/parser.ts","../../../tools/warp_grep/agent/formatter.ts","../../../tools/warp_grep/agent/prompt.ts","../../../tools/warp_grep/agent/config.ts","../../../tools/warp_grep/tools/finish.ts","../../../tools/warp_grep/providers/local.ts","../../../tools/warp_grep/utils/ripgrep.ts","../../../tools/warp_grep/utils/paths.ts","../../../tools/warp_grep/utils/files.ts"],"sourcesContent":["/**\n * Warp Grep Harness Primitives\n *\n * Building blocks for custom agent harnesses.\n * Use these when you want to control the agent loop yourself.\n *\n * @example\n * ```typescript\n * import {\n * parseToolCalls,\n * SYSTEM_PROMPT,\n * formatToolResult,\n * formatTurnMessage,\n * formatAnalyseTree,\n * resolveFinishFiles,\n * MAX_TURNS,\n * LocalRipgrepProvider,\n * type ToolCall,\n * type WarpGrepProvider,\n * } from '@morphllm/morphsdk/tools/warp-grep/harness';\n *\n * const provider = new LocalRipgrepProvider(repoRoot);\n * const messages = [\n * { role: 'system', content: SYSTEM_PROMPT },\n * { role: 'user', content: `<query>${query}</query>` },\n * ];\n *\n * for (let turn = 1; turn <= MAX_TURNS; turn++) {\n * const response = await myModel(messages);\n * messages.push({ role: 'assistant', content: response });\n *\n * const toolCalls = parseToolCalls(response);\n * const results: string[] = [];\n *\n * for (const call of toolCalls) {\n * if (call.name === 'finish') {\n * return await resolveFinishFiles(provider, call.arguments.files);\n * }\n *\n * let output: string;\n * if (call.name === 'grep') {\n * const r = await provider.grep(call.arguments);\n * output = r.error || r.lines.join('\\n') || 'no matches';\n * } else if (call.name === 'read') {\n * const r = await provider.read(call.arguments);\n * output = r.error || r.lines.join('\\n') || '(empty)';\n * } else if (call.name === 'analyse') {\n * const entries = await provider.analyse(call.arguments);\n * output = formatAnalyseTree(entries);\n * }\n *\n * results.push(formatToolResult(call.name, call.arguments, output));\n * }\n *\n * messages.push({\n * role: 'user',\n * content: results.join('\\n') + formatTurnMessage(turn)\n * });\n * }\n * ```\n */\n\nimport { LLMResponseParser } from './agent/parser.js';\nimport { formatAgentToolOutput } from './agent/formatter.js';\nimport { SYSTEM_PROMPT } from './agent/prompt.js';\nimport { AGENT_CONFIG, DEFAULT_EXCLUDES } from './agent/config.js';\nimport { readFinishFiles } from './tools/finish.js';\nimport { LocalRipgrepProvider } from './providers/local.js';\nimport type { ToolCall, ToolName, FinishFileSpec, ChatMessage } from './agent/types.js';\nimport type { WarpGrepProvider, GrepResult, ReadResult, AnalyseEntry } from './providers/types.js';\n\n// ════════════════════════════════════════════════════════════════════════════\n// PARSING\n// ════════════════════════════════════════════════════════════════════════════\n\nconst parser = new LLMResponseParser();\n\n/**\n * Parse model output into tool calls.\n * Automatically handles <think> block removal and <tool_call> extraction.\n *\n * @param text - Raw model response text\n * @returns Array of parsed tool calls\n *\n * @example\n * ```typescript\n * const response = `\n * <think>Looking for auth code...</think>\n * <tool_call>grep 'authenticate' src/</tool_call>\n * <tool_call>analyse src/auth</tool_call>\n * `;\n * const calls = parseToolCalls(response);\n * // [{ name: 'grep', arguments: { pattern: 'authenticate', path: 'src/' } },\n * // { name: 'analyse', arguments: { path: 'src/auth' } }]\n * ```\n */\nexport function parseToolCalls(text: string): ToolCall[] {\n return parser.parse(text);\n}\n\n// ════════════════════════════════════════════════════════════════════════════\n// SYSTEM PROMPT\n// ════════════════════════════════════════════════════════════════════════════\n\nexport { SYSTEM_PROMPT };\n\n// ════════════════════════════════════════════════════════════════════════════\n// FORMATTING\n// ════════════════════════════════════════════════════════════════════════════\n\n/**\n * Format tool output with XML wrapper for model consumption.\n * Handles grep, read, analyse with appropriate tags.\n *\n * @param name - Tool name ('grep', 'read', 'analyse')\n * @param args - Tool arguments\n * @param output - Tool output string\n * @param options - Optional settings (isError)\n * @returns Formatted XML string\n *\n * @example\n * ```typescript\n * const formatted = formatToolResult('grep', { pattern: 'auth', path: 'src/' }, 'src/auth.ts:10:function auth()');\n * // <grep_output pattern=\"auth\" path=\"src/\">\n * // src/auth.ts:10:function auth()\n * // </grep_output>\n * ```\n */\nexport function formatToolResult(\n name: string,\n args: Record<string, unknown>,\n output: string,\n options?: { isError?: boolean }\n): string {\n return formatAgentToolOutput(name, args, output, options ?? {});\n}\n\n/**\n * Format turn counter message for the model.\n *\n * @param turn - Current turn number (1-based)\n * @param maxTurns - Maximum turns allowed (default: 4)\n * @returns Turn message string\n *\n * @example\n * ```typescript\n * formatTurnMessage(1); // \"\\n\\n[Turn 1/4] You have 3 turns remaining.\"\n * formatTurnMessage(3); // \"\\n\\n[Turn 3/4] You have 1 turn remaining.\"\n * formatTurnMessage(4); // \"\\n\\n[Turn 4/4] This is your LAST turn. You MUST call finish now.\"\n * ```\n */\nexport function formatTurnMessage(turn: number, maxTurns = 4): string {\n const remaining = maxTurns - turn;\n if (remaining === 0) {\n return `\\n\\n[Turn ${turn}/${maxTurns}] This is your LAST turn. You MUST call finish now.`;\n }\n if (remaining === 1) {\n return `\\n\\n[Turn ${turn}/${maxTurns}] You have 1 turn remaining.`;\n }\n return `\\n\\n[Turn ${turn}/${maxTurns}] You have ${remaining} turns remaining.`;\n}\n\n/**\n * Convert AnalyseEntry[] to tree view string.\n * Matches the format expected by the model.\n *\n * @param entries - Array of analyse entries\n * @returns Tree view string\n *\n * @example\n * ```typescript\n * const entries = [\n * { name: 'src', path: 'src', type: 'dir', depth: 0 },\n * { name: 'index.ts', path: 'src/index.ts', type: 'file', depth: 1 },\n * ];\n * formatAnalyseTree(entries);\n * // \"- [D] src\\n - [F] index.ts\"\n * ```\n */\nexport function formatAnalyseTree(entries: AnalyseEntry[]): string {\n if (!entries.length) return 'empty';\n return entries\n .map((e) => `${' '.repeat(e.depth)}- ${e.type === 'dir' ? '[D]' : '[F]'} ${e.name}`)\n .join('\\n');\n}\n\n// ════════════════════════════════════════════════════════════════════════════\n// FINISH RESOLUTION\n// ════════════════════════════════════════════════════════════════════════════\n\n/**\n * Resolved file with path and content\n */\nexport type ResolvedFile = { path: string; content: string };\n\n/**\n * Resolve finish command - read all specified file ranges.\n *\n * @param provider - WarpGrepProvider implementation\n * @param files - Array of file specs from finish command\n * @returns Array of resolved files with content\n *\n * @example\n * ```typescript\n * const files = [\n * { path: 'src/auth.ts', lines: [[1, 50], [100, 120]] },\n * { path: 'src/types.ts', lines: [[1, 30]] },\n * ];\n * const resolved = await resolveFinishFiles(provider, files);\n * // [{ path: 'src/auth.ts', content: '...' }, { path: 'src/types.ts', content: '...' }]\n * ```\n */\nexport async function resolveFinishFiles(\n provider: WarpGrepProvider,\n files: FinishFileSpec[]\n): Promise<ResolvedFile[]> {\n return readFinishFiles('', files, async (p, s, e) => {\n const r = await provider.read({ path: p, start: s, end: e });\n return r.lines.map((l) => {\n const idx = l.indexOf('|');\n return idx >= 0 ? l.slice(idx + 1) : l;\n });\n });\n}\n\n// ════════════════════════════════════════════════════════════════════════════\n// CONSTANTS\n// ════════════════════════════════════════════════════════════════════════════\n\n/** Maximum turns before forced finish (default: 4) */\nexport const MAX_TURNS = 4;\n\n/** Request timeout in milliseconds */\nexport const TIMEOUT_MS = AGENT_CONFIG.TIMEOUT_MS;\n\n/** Default file/directory exclusions */\nexport { DEFAULT_EXCLUDES };\n\n// ════════════════════════════════════════════════════════════════════════════\n// PROVIDER + TYPES\n// ════════════════════════════════════════════════════════════════════════════\n\nexport { LocalRipgrepProvider };\n\nexport type {\n ToolCall,\n ToolName,\n FinishFileSpec,\n ChatMessage,\n WarpGrepProvider,\n GrepResult,\n ReadResult,\n AnalyseEntry,\n};\n\n","// Parses assistant lines into structured tool calls\nimport type { ToolCall } from './types.js';\n\n// Keep for backwards compatibility - no longer thrown, but exported for tests\nexport class LLMResponseParseError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'LLMResponseParseError';\n }\n}\n\n// Valid tool command names\nconst VALID_COMMANDS = ['analyse', 'grep', 'read', 'finish'];\n\n/**\n * Preprocesses text to handle XML tags:\n * 1. Removes <think>...</think> blocks entirely\n * 2. Extracts content from <tool>...</tool> or <tool_call>...</tool_call> tags\n * 3. Passes through raw tool calls (lines starting with valid commands)\n * 4. Discards unclosed <tool...> tags\n */\nfunction preprocessText(text: string): string[] {\n // Step 1: Remove <think>...</think> blocks (including multiline)\n let processed = text.replace(/<think>[\\s\\S]*?<\\/think>/gi, '');\n \n // Step 2: Check for unclosed <tool or <tool_call tags and discard them\n // Find all opening tags and their positions\n const openingTagRegex = /<tool_call>|<tool>/gi;\n const closingTagRegex = /<\\/tool_call>|<\\/tool>/gi;\n \n // Count opening and closing tags\n const openingMatches = processed.match(openingTagRegex) || [];\n const closingMatches = processed.match(closingTagRegex) || [];\n \n // If there are more opening than closing tags, we have unclosed tags\n // In that case, only process complete tag pairs\n if (openingMatches.length > closingMatches.length) {\n // Remove any content after the last complete closing tag\n const lastClosingMatch = /<\\/tool_call>|<\\/tool>/gi;\n let lastClosingIndex = -1;\n let match;\n while ((match = lastClosingMatch.exec(processed)) !== null) {\n lastClosingIndex = match.index + match[0].length;\n }\n if (lastClosingIndex > 0) {\n processed = processed.slice(0, lastClosingIndex);\n }\n }\n \n // Step 3: Extract content from <tool_call>...</tool_call> and <tool>...</tool> tags\n const toolCallLines: string[] = [];\n const toolTagRegex = /<tool_call>([\\s\\S]*?)<\\/tool_call>|<tool>([\\s\\S]*?)<\\/tool>/gi;\n let tagMatch;\n \n while ((tagMatch = toolTagRegex.exec(processed)) !== null) {\n const content = (tagMatch[1] || tagMatch[2] || '').trim();\n if (content) {\n // Split content by newlines in case there are multiple tool calls in one tag\n const lines = content.split(/\\r?\\n/).map(l => l.trim()).filter(l => l);\n toolCallLines.push(...lines);\n }\n }\n \n // Step 4: Also extract raw tool calls (lines starting with valid commands)\n // This provides backwards compatibility\n const allLines = processed.split(/\\r?\\n/).map(l => l.trim());\n for (const line of allLines) {\n if (!line) continue;\n \n // Skip lines that are inside XML tags (already processed above)\n if (line.startsWith('<')) continue;\n \n // Check if line starts with a valid command\n const firstWord = line.split(/\\s/)[0];\n if (VALID_COMMANDS.includes(firstWord)) {\n // Avoid duplicates\n if (!toolCallLines.includes(line)) {\n toolCallLines.push(line);\n }\n }\n }\n \n return toolCallLines;\n}\n\nexport class LLMResponseParser {\n private readonly finishSpecSplitRe = /,(?=[^,\\s]+:)/;\n\n parse(text: string): ToolCall[] {\n if (typeof text !== 'string') {\n // no way we hit this, but sure, we can throw here\n throw new TypeError('Command text must be a string.');\n }\n \n // Preprocess to handle XML tags\n const lines = preprocessText(text);\n \n const commands: ToolCall[] = [];\n let finishAccumulator: Map<string, number[][]> | null = null;\n\n lines.forEach((line) => {\n if (!line || line.startsWith('#')) return;\n const parts = this.splitLine(line);\n if (parts.length === 0) return;\n const cmd = parts[0];\n switch (cmd) {\n case 'analyse':\n this.handleAnalyse(parts, line, commands);\n break;\n case 'grep':\n this.handleGrep(parts, line, commands);\n break;\n case 'read':\n this.handleRead(parts, line, commands);\n break;\n case 'finish':\n finishAccumulator = this.handleFinish(parts, line, commands, finishAccumulator);\n break;\n default:\n // Silently ignore unknown commands after preprocessing\n // (they might be remnants of XML or other content)\n break;\n }\n });\n\n if (finishAccumulator) {\n const map = finishAccumulator as Map<string, number[][]>;\n const entries = [...map.entries()];\n const filesPayload = entries.map(([path, ranges]) => ({\n path,\n lines: [...ranges].sort((a, b) => a[0] - b[0]) as Array<[number, number]>,\n }));\n commands.push({ name: 'finish', arguments: { files: filesPayload } });\n }\n return commands;\n }\n\n private splitLine(line: string): string[] {\n // Split by whitespace but keep quoted blocks as one\n const parts: string[] = [];\n let current = '';\n let inSingle = false;\n for (let i = 0; i < line.length; i++) {\n const ch = line[i];\n if (ch === \"'\" && line[i - 1] !== '\\\\') {\n inSingle = !inSingle;\n current += ch;\n } else if (!inSingle && /\\s/.test(ch)) {\n if (current) {\n parts.push(current);\n current = '';\n }\n } else {\n current += ch;\n }\n }\n if (current) parts.push(current);\n return parts;\n }\n\n /** Helper to create a _skip tool call with an error message */\n private skip(message: string): ToolCall {\n return { name: '_skip', arguments: { message } };\n }\n\n private handleAnalyse(parts: string[], rawLine: string, commands: ToolCall[]) {\n // analyse <path> [pattern]\n if (parts.length < 2) {\n commands.push(this.skip(\n `[SKIPPED] Your command \"${rawLine}\" is missing a path. ` +\n `Correct format: analyse <path> [pattern]. Example: analyse src/`\n ));\n return;\n }\n const path = parts[1];\n const pattern = parts[2]?.replace(/^\"|\"$/g, '') ?? null;\n commands.push({ name: 'analyse', arguments: { path, pattern } });\n }\n\n // no glob tool in MCP\n\n private handleGrep(parts: string[], rawLine: string, commands: ToolCall[]) {\n // grep '<pattern>' <path>\n if (parts.length < 3) {\n commands.push(this.skip(\n `[SKIPPED] Your command \"${rawLine}\" is missing arguments. ` +\n `Correct format: grep '<pattern>' <path>. Example: grep 'TODO' src/`\n ));\n return;\n }\n let pat = parts[1];\n // Be lenient: accept unquoted patterns by treating the first arg as the pattern\n if (pat.startsWith(\"'\") && pat.endsWith(\"'\")) {\n pat = pat.slice(1, -1);\n }\n // If pattern is empty after processing, skip\n if (!pat) {\n commands.push(this.skip(\n `[SKIPPED] Your command \"${rawLine}\" has an empty pattern. ` +\n `Provide a non-empty search pattern. Example: grep 'function' src/`\n ));\n return;\n }\n commands.push({ name: 'grep', arguments: { pattern: pat, path: parts[2] } });\n }\n\n private handleRead(parts: string[], rawLine: string, commands: ToolCall[]) {\n // read <path>[:start-end]\n if (parts.length < 2) {\n commands.push(this.skip(\n `[SKIPPED] Your command \"${rawLine}\" is missing a path. ` +\n `Correct format: read <path> or read <path>:<start>-<end>. Example: read src/index.ts:1-50`\n ));\n return;\n }\n const spec = parts[1];\n const rangeIdx = spec.indexOf(':');\n if (rangeIdx === -1) {\n commands.push({ name: 'read', arguments: { path: spec } });\n return;\n }\n const filePath = spec.slice(0, rangeIdx);\n const range = spec.slice(rangeIdx + 1);\n const [s, e] = range.split('-').map(v => parseInt(v, 10));\n // If range is invalid, fallback to reading the whole file\n if (!Number.isFinite(s) || !Number.isFinite(e)) {\n commands.push({ name: 'read', arguments: { path: filePath } });\n return;\n }\n commands.push({ name: 'read', arguments: { path: filePath, start: s, end: e } });\n }\n\n private handleFinish(parts: string[], rawLine: string, commands: ToolCall[], acc: Map<string, number[][]> | null) {\n // finish file1:1-10,20-30 file2:5-7\n const map = acc ?? new Map<string, number[][]>();\n const args = parts.slice(1);\n for (const token of args) {\n const [filePath, rangesText] = token.split(':', 2);\n if (!filePath || !rangesText) {\n // Skip this malformed token, continue processing others\n commands.push(this.skip(\n `[SKIPPED] Invalid finish token \"${token}\". ` +\n `Correct format: finish <path>:<start>-<end>. Example: finish src/index.ts:1-50`\n ));\n continue;\n }\n const rangeSpecs = rangesText.split(',').filter(Boolean);\n for (const spec of rangeSpecs) {\n const [s, e] = spec.split('-').map(v => parseInt(v, 10));\n if (!Number.isFinite(s) || !Number.isFinite(e) || e < s) {\n // Skip this invalid range, continue with others\n commands.push(this.skip(\n `[SKIPPED] Invalid range \"${spec}\" in \"${token}\". ` +\n `Ranges must be <start>-<end> where start <= end. Example: 1-50`\n ));\n continue;\n }\n const arr = map.get(filePath) ?? [];\n arr.push([s, e]);\n map.set(filePath, arr);\n }\n }\n return map;\n }\n}\n","export class ToolOutputFormatter {\n\tformat(\n\t\ttoolName: string,\n\t\targs: Record<string, unknown> | null | undefined,\n\t\toutput: string,\n\t\toptions: { isError?: boolean } = {}\n\t): string {\n\t\tconst name = (toolName ?? \"\").trim();\n\t\tif (!name) {\n\t\t\treturn \"\";\n\t\t}\n\t\tconst payload = (output as any)?.toString?.()?.trim?.() ?? \"\";\n\t\tconst isError = Boolean(options.isError);\n\t\tconst safeArgs = args ?? {};\n\n\t\tif (!payload && !isError) {\n\t\t\treturn \"\";\n\t\t}\n\n\t\tswitch (name) {\n\t\t\tcase \"read\":\n\t\t\t\treturn this.formatRead(safeArgs, payload, isError);\n\t\t\tcase \"analyse\":\n\t\t\t\treturn this.formatAnalyse(safeArgs, payload, isError);\n\t\t\tcase \"grep\":\n\t\t\t\treturn this.formatGrep(safeArgs, payload, isError);\n\t\t\tdefault:\n\t\t\t\treturn payload ? `<tool_output>\\n${payload}\\n</tool_output>` : \"\";\n\t\t}\n\t}\n\n\tprivate formatRead(args: Record<string, unknown>, payload: string, isError: boolean): string {\n\t\tif (isError) {\n\t\t\treturn payload;\n\t\t}\n\t\tconst path = this.asString(args.path) || \"...\";\n\t\treturn `<file path=\"${path}\">\\n${payload}\\n</file>`;\n\t}\n\n\tprivate formatAnalyse(args: Record<string, unknown>, payload: string, isError: boolean): string {\n\t\tconst path = this.asString(args.path) || \".\";\n\t\tif (isError) {\n\t\t\treturn `<analyse_results path=\"${path}\" status=\"error\">\\n${payload}\\n</analyse_results>`;\n\t\t}\n\t\treturn `<analyse_results path=\"${path}\">\\n${payload}\\n</analyse_results>`;\n\t}\n\n\tprivate formatGrep(args: Record<string, unknown>, payload: string, isError: boolean): string {\n\t\tconst pattern = this.asString(args.pattern);\n\t\tconst path = this.asString(args.path);\n\t\tconst attributes: string[] = [];\n\t\tif (pattern !== undefined) {\n\t\t\tattributes.push(`pattern=\"${pattern}\"`);\n\t\t}\n\t\tif (path !== undefined) {\n\t\t\tattributes.push(`path=\"${path}\"`);\n\t\t}\n\t\tif (isError) {\n\t\t\tattributes.push('status=\"error\"');\n\t\t}\n\t\tconst attrText = attributes.length ? ` ${attributes.join(\" \")}` : \"\";\n\t\treturn `<grep_output${attrText}>\\n${payload}\\n</grep_output>`;\n\t}\n\n\tprivate asString(value: unknown): string | undefined {\n\t\tif (value === null || value === undefined) {\n\t\t\treturn undefined;\n\t\t}\n\t\treturn String(value);\n\t}\n}\n\nconst sharedFormatter = new ToolOutputFormatter();\n\nexport function formatAgentToolOutput(\n\ttoolName: string,\n\targs: Record<string, unknown> | null | undefined,\n\toutput: string,\n\toptions: { isError?: boolean } = {}\n): string {\n\treturn sharedFormatter.format(toolName, args, output, options);\n}\n\n","export const SYSTEM_PROMPT = `You are a code search agent. Your task is to find all relevant code for a given query.\n\n<workflow>\nYou have exactly 4 turns. The 4th turn MUST be a \\`finish\\` call. Each turn allows up to 8 parallel tool calls.\n\n- Turn 1: Map the territory OR dive deep (based on query specificity)\n- Turn 2-3: Refine based on findings\n- Turn 4: MUST call \\`finish\\` with all relevant code locations\n- You MAY call \\`finish\\` early if confident—but never before at least 1 search turn.\n\nRemember, if the task feels easy to you, it is strongly desirable to call \\`finish\\` early using fewer turns, but quality over speed.\n</workflow>\n\n<tools>\n### \\`analyse <path> [pattern]\\`\nDirectory tree or file search. Shows structure of a path, optionally filtered by regex pattern.\n- \\`path\\`: Required. Directory or file path (use \\`.\\` for repo root)\n- \\`pattern\\`: Optional regex to filter results\n\nExamples:\n\\`\\`\\`\nanalyse .\nanalyse src/api\nanalyse . \".*\\\\.ts$\"\nanalyse src \"test.*\"\n\\`\\`\\`\n\n### \\`read <path>[:start-end]\\`\nRead file contents. Line range is 1-based, inclusive.\n- Returns numbered lines for easy reference\n- Omit range to read entire file\n\nExamples:\n\\`\\`\\`\nread src/main.py\nread src/db/conn.py:10-50\nread package.json:1-20\n\\`\\`\\`\n\n### \\`grep '<pattern>' <path>\\`\nRipgrep search. Finds pattern matches across files.\n- \\`'<pattern>'\\`: Required. Regex pattern wrapped in single quotes\n- \\`<path>\\`: Required. Directory or file to search (use \\`.\\` for repo root)\n\nExamples:\n\\`\\`\\`\ngrep 'class.*Service' src/\ngrep 'def authenticate' .\ngrep 'import.*from' src/components/\ngrep 'TODO' .\n\\`\\`\\`\n\n### \\`finish <file1:ranges> [file2:ranges ...]\\`\nSubmit final answer with all relevant code locations.\n- Include generous line ranges—don't be stingy with context\n- Ranges are comma-separated: \\`file.py:10-30,50-60\\`\n- ALWAYS include import statements at the top of files (usually lines 1-20)\n- If code spans multiple files, include ALL of them\n- Small files can be returned in full\n\nExamples:\n\\`\\`\\`\nfinish src/auth.py:1-15,25-50,75-80 src/models/user.py:1-10,20-45\nfinish src/index.ts:1-100\n\\`\\`\\`\n</tools>\n\n<strategy>\n**Before your first tool call, classify the query:**\n\n| Query Type | Turn 1 Strategy | Early Finish? |\n|------------|-----------------|---------------|\n| **Specific** (function name, error string, unique identifier) | 8 parallel greps on likely paths | Often by turn 2 |\n| **Conceptual** (how does X work, where is Y handled) | analyse + 2-3 broad greps | Rarely early |\n| **Exploratory** (find all tests, list API endpoints) | analyse at multiple depths | Usually needs 3 turns |\n\n**Parallel call patterns:**\n- **Shotgun grep**: Same pattern, 8 different directories—fast coverage\n- **Variant grep**: 8 pattern variations (synonyms, naming conventions)—catches inconsistent codebases\n- **Funnel**: 1 analyse + 7 greps—orient and search simultaneously\n- **Deep read**: 8 reads on files you already identified—gather full context fast\n</strategy>\n\n<output_format>\nEVERY response MUST follow this exact format:\n\n1. First, wrap your reasoning in \\`<think>...</think>\\` tags containing:\n - Query classification (specific/conceptual/exploratory)\n - Confidence estimate (can I finish in 1-2 turns?)\n - This turn's parallel strategy\n - What signals would let me finish early?\n\n2. Then, output tool calls wrapped in \\`<tool_call>...</tool_call>\\` tags, one per line.\n\nExample:\n\\`\\`\\`\n<think>\nThis is a specific query about authentication. I'll grep for auth-related patterns.\nHigh confidence I can finish in 2 turns if I find the auth module.\nStrategy: Shotgun grep across likely directories.\n</think>\n<tool_call>grep 'authenticate' src/</tool_call>\n<tool_call>grep 'login' src/</tool_call>\n<tool_call>analyse src/auth</tool_call>\n\\`\\`\\`\n\nNo commentary outside \\`<think>\\`. No explanations after tool calls.\n</output_format>\n\n<finishing_requirements>\nWhen calling \\`finish\\`:\n- Include the import section (typically lines 1-20) of each file\n- Include all function/class definitions that are relevant\n- Include any type definitions, interfaces, or constants used\n- Better to over-include than leave the user missing context\n- If unsure about boundaries, include more rather than less\n</finishing_requirements>\n\nBegin your exploration now to find code relevant to the query.`;\n\nexport function getSystemPrompt(): string {\n\treturn SYSTEM_PROMPT;\n}\n","// Agent configuration defaults for morph-warp-grep\n// Hard-coded: SDK does not expose control over rounds or timeout.\nexport const AGENT_CONFIG = {\n // Give the model freedom; failsafe cap to prevent infinite loops\n MAX_ROUNDS: 10,\n TIMEOUT_MS: 30000,\n};\n\n/**\n * Comprehensive exclusion list for directories and files\n * These patterns are used with ripgrep's -g flag\n */\nconst BUILTIN_EXCLUDES = [\n // Version control\n '.git', '.svn', '.hg', '.bzr',\n \n // Dependencies\n 'node_modules', 'bower_components', '.pnpm', '.yarn',\n 'vendor', 'packages', 'Pods', '.bundle',\n \n // Python\n '__pycache__', '.pytest_cache', '.mypy_cache', '.ruff_cache',\n '.venv', 'venv', '.tox', '.nox', '.eggs', '*.egg-info',\n \n // Build outputs\n 'dist', 'build', 'out', 'output', 'target', '_build',\n '.next', '.nuxt', '.output', '.vercel', '.netlify',\n \n // Cache directories\n '.cache', '.parcel-cache', '.turbo', '.nx', '.gradle',\n \n // IDE/Editor\n '.idea', '.vscode', '.vs',\n \n // Coverage\n 'coverage', '.coverage', 'htmlcov', '.nyc_output',\n \n // Temporary\n 'tmp', 'temp', '.tmp', '.temp',\n \n // Lock files\n 'package-lock.json', 'yarn.lock', 'pnpm-lock.yaml', 'bun.lockb',\n 'Cargo.lock', 'Gemfile.lock', 'poetry.lock',\n \n // Binary/minified\n '*.min.js', '*.min.css', '*.bundle.js',\n '*.wasm', '*.so', '*.dll', '*.pyc',\n '*.map', '*.js.map',\n \n // Hidden directories catch-all\n '.*',\n];\n\nexport const DEFAULT_EXCLUDES = (process.env.MORPH_WARP_GREP_EXCLUDE || '')\n .split(',')\n .map(s => s.trim())\n .filter(Boolean)\n .concat(BUILTIN_EXCLUDES);\n\nexport const DEFAULT_MODEL = 'morph-warp-grep';\n\n\n","import type { FinishFileSpec } from '../agent/types.js';\nimport fs from 'fs/promises';\n\nexport function normalizeFinishFiles(files: FinishFileSpec[]): FinishFileSpec[] {\n return files.map((f) => {\n const merged = mergeRanges(f.lines);\n return { path: f.path, lines: merged };\n });\n}\n\nexport async function readFinishFiles(\n repoRoot: string,\n files: FinishFileSpec[],\n reader: (path: string, start: number, end: number) => Promise<string[]>\n): Promise<{ path: string; ranges: Array<[number, number]>; content: string }[]> {\n const out: { path: string; ranges: Array<[number, number]>; content: string }[] = [];\n for (const f of files) {\n const ranges = mergeRanges(f.lines);\n const chunks: string[] = [];\n for (const [s, e] of ranges) {\n const lines = await reader(f.path, s, e);\n chunks.push(lines.join('\\n'));\n }\n out.push({ path: f.path, ranges, content: chunks.join('\\n') });\n }\n return out;\n}\n\nfunction mergeRanges(ranges: Array<[number, number]>): Array<[number, number]> {\n if (!ranges.length) return [];\n const sorted = [...ranges].sort((a, b) => a[0] - b[0]);\n const merged: Array<[number, number]> = [];\n let [cs, ce] = sorted[0];\n for (let i = 1; i < sorted.length; i++) {\n const [s, e] = sorted[i];\n if (s <= ce + 1) {\n ce = Math.max(ce, e);\n } else {\n merged.push([cs, ce]);\n cs = s;\n ce = e;\n }\n }\n merged.push([cs, ce]);\n return merged;\n}\n\n\n","import fs from 'fs/promises';\nimport fssync from 'fs';\nimport path from 'path';\nimport { runRipgrep } from '../utils/ripgrep.js';\nimport { ensureWithinRepo, resolveUnderRepo, toRepoRelative, isSymlink, isTextualFile } from '../utils/paths.js';\nimport type { WarpGrepProvider, GrepResult, ReadResult, AnalyseEntry } from './types.js';\nimport { readAllLines } from '../utils/files.js';\nimport { DEFAULT_EXCLUDES } from '../agent/config.js';\n\nexport class LocalRipgrepProvider implements WarpGrepProvider {\n constructor(private readonly repoRoot: string, private readonly excludes: string[] = DEFAULT_EXCLUDES) {}\n\n async grep(params: { pattern: string; path: string }): Promise<GrepResult> {\n const abs = resolveUnderRepo(this.repoRoot, params.path);\n const stat = await fs.stat(abs).catch(() => null);\n if (!stat) return { lines: [] };\n const targetArg = abs === path.resolve(this.repoRoot) ? '.' : toRepoRelative(this.repoRoot, abs);\n const args = [\n '--no-config',\n '--no-heading',\n '--with-filename',\n '--line-number',\n '--color=never',\n '--trim',\n '--max-columns=400',\n ...this.excludes.flatMap((e) => ['-g', `!${e}`]),\n params.pattern,\n targetArg || '.',\n ];\n const res = await runRipgrep(args, { cwd: this.repoRoot });\n \n // Gracefully handle ripgrep not being available\n if (res.exitCode === -1) {\n return {\n lines: [],\n error: `[RIPGREP NOT AVAILABLE] ripgrep (rg) is required but failed to execute. Please install it:\\n` +\n ` • macOS: brew install ripgrep\\n` +\n ` • Ubuntu/Debian: apt install ripgrep\\n` +\n ` • Windows: choco install ripgrep\\n` +\n ` • Or visit: https://github.com/BurntSushi/ripgrep#installation\\n` +\n `Exit code: ${res.exitCode}${res.stderr ? `\\nDetails: ${res.stderr}` : ''}`,\n };\n }\n \n // Handle other ripgrep errors gracefully\n if (res.exitCode !== 0 && res.exitCode !== 1) {\n return {\n lines: [],\n error: `[RIPGREP ERROR] grep failed with exit code ${res.exitCode}${res.stderr ? `: ${res.stderr}` : ''}`,\n };\n }\n \n const lines = (res.stdout || '')\n .trim()\n .split(/\\r?\\n/)\n .filter((l) => l.length > 0);\n return { lines };\n }\n\n async glob(params: { pattern: string; path: string }): Promise<{ files: string[] }> {\n const abs = resolveUnderRepo(this.repoRoot, params.path);\n const targetArg = abs === path.resolve(this.repoRoot) ? '.' : toRepoRelative(this.repoRoot, abs);\n const args = [\n '--no-config',\n '--files',\n '-g',\n params.pattern,\n ...this.excludes.flatMap((e) => ['-g', `!${e}`]),\n targetArg || '.',\n ];\n const res = await runRipgrep(args, { cwd: this.repoRoot });\n \n // Gracefully handle ripgrep not being available\n if (res.exitCode === -1) {\n // Return empty files with a console warning - glob is less critical than grep\n console.warn(`[warp_grep] ripgrep not available for glob: ${res.stderr || 'execution failed'}`);\n return { files: [] };\n }\n \n const files = (res.stdout || '')\n .trim()\n .split(/\\r?\\n/)\n .filter((l) => l.length > 0);\n return { files };\n }\n\n async read(params: { path: string; start?: number; end?: number }): Promise<ReadResult> {\n const abs = resolveUnderRepo(this.repoRoot, params.path);\n const stat = await fs.stat(abs).catch(() => null);\n \n // Gracefully handle file not found / not a file\n if (!stat || !stat.isFile()) {\n return {\n lines: [],\n error: `[FILE NOT FOUND] You tried to read \"${params.path}\" but there is no file at this path. ` +\n `Double-check the path exists and is spelled correctly.`,\n };\n }\n \n // Gracefully handle symlinks\n if (isSymlink(abs)) {\n return {\n lines: [],\n error: `[SYMLINK] You tried to read \"${params.path}\" but this is a symlink. ` +\n `Try reading the actual file it points to instead.`,\n };\n }\n \n // Gracefully handle non-text or too-large files\n if (!isTextualFile(abs)) {\n return {\n lines: [],\n error: `[UNREADABLE FILE] You tried to read \"${params.path}\" but this file is either too large ` +\n `or not a text file, so it cannot be read. Try a different file.`,\n };\n }\n \n const lines = await readAllLines(abs);\n const total = lines.length;\n let s = params.start ?? 1;\n let e = Math.min(params.end ?? total, total);\n if (s > total && total > 0) {\n // Model hallucinated range - fallback to full file\n s = 1;\n e = total;\n }\n const out: string[] = [];\n for (let i = s; i <= e; i += 1) {\n const content = lines[i - 1] ?? '';\n out.push(`${i}|${content}`);\n }\n return { lines: out };\n }\n\n async analyse(params: { path: string; pattern?: string | null; maxResults?: number; maxDepth?: number }): Promise<AnalyseEntry[]> {\n const abs = resolveUnderRepo(this.repoRoot, params.path);\n const stat = await fs.stat(abs).catch(() => null);\n if (!stat || !stat.isDirectory()) {\n return [];\n }\n const maxResults = params.maxResults ?? 100;\n const maxDepth = params.maxDepth ?? 2;\n const regex = params.pattern ? new RegExp(params.pattern) : null;\n\n const results: AnalyseEntry[] = [];\n async function walk(dir: string, depth: number) {\n if (depth > maxDepth || results.length >= maxResults) return;\n const entries = await fs.readdir(dir, { withFileTypes: true });\n for (const entry of entries) {\n const full = path.join(dir, entry.name);\n const rel = toRepoRelative(abs, full).replace(/^[.][/\\\\]?/, '');\n if (DEFAULT_EXCLUDES.some((ex) => rel.split(path.sep).includes(ex))) continue;\n if (regex && !regex.test(entry.name)) continue;\n if (results.length >= maxResults) break;\n results.push({\n name: entry.name,\n path: toRepoRelative(path.resolve(''), full), // relative display\n type: entry.isDirectory() ? 'dir' : 'file',\n depth,\n });\n if (entry.isDirectory()) {\n await walk(full, depth + 1);\n }\n }\n }\n await walk(abs, 0);\n return results;\n }\n}\n\n\n","import { spawn } from 'child_process';\nimport { rgPath } from '@vscode/ripgrep';\n\nexport type ExecResult = { stdout: string; stderr: string; exitCode: number };\n\n// Cache the working ripgrep path to avoid repeated fallback checks\nlet resolvedRgPath: string | null = null;\nlet rgPathChecked = false;\n\nfunction spawnRg(rgBinary: string, args: string[], opts?: { cwd?: string; env?: NodeJS.ProcessEnv }): Promise<ExecResult> {\n return new Promise((resolve) => {\n const child = spawn(rgBinary, args, {\n cwd: opts?.cwd,\n env: { ...process.env, ...(opts?.env || {}) },\n stdio: ['ignore', 'pipe', 'pipe'],\n });\n let stdout = '';\n let stderr = '';\n child.stdout.on('data', (d) => (stdout += d.toString()));\n child.stderr.on('data', (d) => (stderr += d.toString()));\n child.on('close', (code) => {\n resolve({ stdout, stderr, exitCode: typeof code === 'number' ? code : -1 });\n });\n child.on('error', () => {\n resolve({ stdout: '', stderr: 'Failed to spawn ripgrep.', exitCode: -1 });\n });\n });\n}\n\n// Check if a result indicates a binary compatibility failure (not a normal rg error)\nfunction isBinaryFailure(result: ExecResult): boolean {\n // Spawn error\n if (result.exitCode === -1) return true;\n // jemalloc page size issue (common on ARM64 with non-standard page sizes)\n if (result.stderr.includes('jemalloc') || result.stderr.includes('Unsupported system page size')) return true;\n // SIGABRT (134 = 128 + 6)\n if (result.exitCode === 134) return true;\n return false;\n}\n\nexport async function runRipgrep(args: string[], opts?: { cwd?: string; env?: NodeJS.ProcessEnv }): Promise<ExecResult> {\n // If we've already determined the working path, use it\n if (rgPathChecked && resolvedRgPath) {\n return spawnRg(resolvedRgPath, args, opts);\n }\n\n // First attempt: bundled ripgrep\n if (!rgPathChecked) {\n const result = await spawnRg(rgPath, args, opts);\n \n if (!isBinaryFailure(result)) {\n // Bundled binary works, cache it\n resolvedRgPath = rgPath;\n rgPathChecked = true;\n return result;\n }\n\n // Bundled binary failed, try system rg\n const fallbackResult = await spawnRg('rg', args, opts);\n \n if (!isBinaryFailure(fallbackResult)) {\n // System rg works, cache it\n resolvedRgPath = 'rg';\n rgPathChecked = true;\n return fallbackResult;\n }\n\n // Neither works, mark as checked and return the original error\n rgPathChecked = true;\n return { \n stdout: '', \n stderr: 'Failed to spawn ripgrep. Neither bundled nor system rg is available.', \n exitCode: -1 \n };\n }\n\n // rgPathChecked is true but resolvedRgPath is null - no working rg found\n return { \n stdout: '', \n stderr: 'Failed to spawn ripgrep. Neither bundled nor system rg is available.', \n exitCode: -1 \n };\n}\n\n\n","import fs from 'fs';\nimport path from 'path';\n\nexport function resolveUnderRepo(repoRoot: string, targetPath: string): string {\n const absRoot = path.resolve(repoRoot);\n const resolved = path.resolve(absRoot, targetPath);\n ensureWithinRepo(absRoot, resolved);\n return resolved;\n}\n\nexport function ensureWithinRepo(repoRoot: string, absTarget: string): void {\n const rel = path.relative(path.resolve(repoRoot), path.resolve(absTarget));\n if (rel.startsWith('..') || path.isAbsolute(rel)) {\n throw new Error(`Path outside repository root: ${absTarget}`);\n }\n}\n\nexport function toRepoRelative(repoRoot: string, absPath: string): string {\n return path.relative(path.resolve(repoRoot), path.resolve(absPath));\n}\n\nexport function isSymlink(p: string): boolean {\n try {\n const st = fs.lstatSync(p);\n return st.isSymbolicLink();\n } catch {\n return false;\n }\n}\n\nexport function isTextualFile(filePath: string, maxBytes = 2_000_000): boolean {\n try {\n const st = fs.statSync(filePath);\n if (!st.isFile()) return false;\n if (st.size > maxBytes) return false;\n const fd = fs.openSync(filePath, 'r');\n const buf = Buffer.alloc(512);\n const read = fs.readSync(fd, buf, 0, buf.length, 0);\n fs.closeSync(fd);\n for (let i = 0; i < read; i++) {\n const c = buf[i];\n if (c === 0) return false; // binary heuristic\n }\n return true;\n } catch {\n return false;\n }\n}\n\n\n","import fs from 'fs/promises';\n\nexport async function readAllLines(filePath: string): Promise<string[]> {\n const content = await fs.readFile(filePath, 'utf8');\n // Preserve newlines; split keeping consistency\n return content.split(/\\r?\\n/);\n}\n\n\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACYA,IAAM,iBAAiB,CAAC,WAAW,QAAQ,QAAQ,QAAQ;AAS3D,SAAS,eAAe,MAAwB;AAE9C,MAAI,YAAY,KAAK,QAAQ,8BAA8B,EAAE;AAI7D,QAAM,kBAAkB;AACxB,QAAM,kBAAkB;AAGxB,QAAM,iBAAiB,UAAU,MAAM,eAAe,KAAK,CAAC;AAC5D,QAAM,iBAAiB,UAAU,MAAM,eAAe,KAAK,CAAC;AAI5D,MAAI,eAAe,SAAS,eAAe,QAAQ;AAEjD,UAAM,mBAAmB;AACzB,QAAI,mBAAmB;AACvB,QAAI;AACJ,YAAQ,QAAQ,iBAAiB,KAAK,SAAS,OAAO,MAAM;AAC1D,yBAAmB,MAAM,QAAQ,MAAM,CAAC,EAAE;AAAA,IAC5C;AACA,QAAI,mBAAmB,GAAG;AACxB,kBAAY,UAAU,MAAM,GAAG,gBAAgB;AAAA,IACjD;AAAA,EACF;AAGA,QAAM,gBAA0B,CAAC;AACjC,QAAM,eAAe;AACrB,MAAI;AAEJ,UAAQ,WAAW,aAAa,KAAK,SAAS,OAAO,MAAM;AACzD,UAAM,WAAW,SAAS,CAAC,KAAK,SAAS,CAAC,KAAK,IAAI,KAAK;AACxD,QAAI,SAAS;AAEX,YAAM,QAAQ,QAAQ,MAAM,OAAO,EAAE,IAAI,OAAK,EAAE,KAAK,CAAC,EAAE,OAAO,OAAK,CAAC;AACrE,oBAAc,KAAK,GAAG,KAAK;AAAA,IAC7B;AAAA,EACF;AAIA,QAAM,WAAW,UAAU,MAAM,OAAO,EAAE,IAAI,OAAK,EAAE,KAAK,CAAC;AAC3D,aAAW,QAAQ,UAAU;AAC3B,QAAI,CAAC,KAAM;AAGX,QAAI,KAAK,WAAW,GAAG,EAAG;AAG1B,UAAM,YAAY,KAAK,MAAM,IAAI,EAAE,CAAC;AACpC,QAAI,eAAe,SAAS,SAAS,GAAG;AAEtC,UAAI,CAAC,cAAc,SAAS,IAAI,GAAG;AACjC,sBAAc,KAAK,IAAI;AAAA,MACzB;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;AAEO,IAAM,oBAAN,MAAwB;AAAA,EACZ,oBAAoB;AAAA,EAErC,MAAM,MAA0B;AAC9B,QAAI,OAAO,SAAS,UAAU;AAE5B,YAAM,IAAI,UAAU,gCAAgC;AAAA,IACtD;AAGA,UAAM,QAAQ,eAAe,IAAI;AAEjC,UAAM,WAAuB,CAAC;AAC9B,QAAI,oBAAoD;AAExD,UAAM,QAAQ,CAAC,SAAS;AACtB,UAAI,CAAC,QAAQ,KAAK,WAAW,GAAG,EAAG;AACnC,YAAM,QAAQ,KAAK,UAAU,IAAI;AACjC,UAAI,MAAM,WAAW,EAAG;AACxB,YAAM,MAAM,MAAM,CAAC;AACnB,cAAQ,KAAK;AAAA,QACX,KAAK;AACH,eAAK,cAAc,OAAO,MAAM,QAAQ;AACxC;AAAA,QACF,KAAK;AACH,eAAK,WAAW,OAAO,MAAM,QAAQ;AACrC;AAAA,QACF,KAAK;AACH,eAAK,WAAW,OAAO,MAAM,QAAQ;AACrC;AAAA,QACF,KAAK;AACH,8BAAoB,KAAK,aAAa,OAAO,MAAM,UAAU,iBAAiB;AAC9E;AAAA,QACF;AAGE;AAAA,MACJ;AAAA,IACF,CAAC;AAED,QAAI,mBAAmB;AACrB,YAAM,MAAM;AACZ,YAAM,UAAU,CAAC,GAAG,IAAI,QAAQ,CAAC;AACjC,YAAM,eAAe,QAAQ,IAAI,CAAC,CAACA,OAAM,MAAM,OAAO;AAAA,QACpD,MAAAA;AAAA,QACA,OAAO,CAAC,GAAG,MAAM,EAAE,KAAK,CAAC,GAAG,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC;AAAA,MAC/C,EAAE;AACF,eAAS,KAAK,EAAE,MAAM,UAAU,WAAW,EAAE,OAAO,aAAa,EAAE,CAAC;AAAA,IACtE;AACA,WAAO;AAAA,EACT;AAAA,EAEQ,UAAU,MAAwB;AAExC,UAAM,QAAkB,CAAC;AACzB,QAAI,UAAU;AACd,QAAI,WAAW;AACf,aAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK;AACpC,YAAM,KAAK,KAAK,CAAC;AACjB,UAAI,OAAO,OAAO,KAAK,IAAI,CAAC,MAAM,MAAM;AACtC,mBAAW,CAAC;AACZ,mBAAW;AAAA,MACb,WAAW,CAAC,YAAY,KAAK,KAAK,EAAE,GAAG;AACrC,YAAI,SAAS;AACX,gBAAM,KAAK,OAAO;AAClB,oBAAU;AAAA,QACZ;AAAA,MACF,OAAO;AACL,mBAAW;AAAA,MACb;AAAA,IACF;AACA,QAAI,QAAS,OAAM,KAAK,OAAO;AAC/B,WAAO;AAAA,EACT;AAAA;AAAA,EAGQ,KAAK,SAA2B;AACtC,WAAO,EAAE,MAAM,SAAS,WAAW,EAAE,QAAQ,EAAE;AAAA,EACjD;AAAA,EAEQ,cAAc,OAAiB,SAAiB,UAAsB;AAE5E,QAAI,MAAM,SAAS,GAAG;AACpB,eAAS,KAAK,KAAK;AAAA,QACjB,2BAA2B,OAAO;AAAA,MAEpC,CAAC;AACD;AAAA,IACF;AACA,UAAMA,QAAO,MAAM,CAAC;AACpB,UAAM,UAAU,MAAM,CAAC,GAAG,QAAQ,UAAU,EAAE,KAAK;AACnD,aAAS,KAAK,EAAE,MAAM,WAAW,WAAW,EAAE,MAAAA,OAAM,QAAQ,EAAE,CAAC;AAAA,EACjE;AAAA;AAAA,EAIQ,WAAW,OAAiB,SAAiB,UAAsB;AAEzE,QAAI,MAAM,SAAS,GAAG;AACpB,eAAS,KAAK,KAAK;AAAA,QACjB,2BAA2B,OAAO;AAAA,MAEpC,CAAC;AACD;AAAA,IACF;AACA,QAAI,MAAM,MAAM,CAAC;AAEjB,QAAI,IAAI,WAAW,GAAG,KAAK,IAAI,SAAS,GAAG,GAAG;AAC5C,YAAM,IAAI,MAAM,GAAG,EAAE;AAAA,IACvB;AAEA,QAAI,CAAC,KAAK;AACR,eAAS,KAAK,KAAK;AAAA,QACjB,2BAA2B,OAAO;AAAA,MAEpC,CAAC;AACD;AAAA,IACF;AACA,aAAS,KAAK,EAAE,MAAM,QAAQ,WAAW,EAAE,SAAS,KAAK,MAAM,MAAM,CAAC,EAAE,EAAE,CAAC;AAAA,EAC7E;AAAA,EAEQ,WAAW,OAAiB,SAAiB,UAAsB;AAEzE,QAAI,MAAM,SAAS,GAAG;AACpB,eAAS,KAAK,KAAK;AAAA,QACjB,2BAA2B,OAAO;AAAA,MAEpC,CAAC;AACD;AAAA,IACF;AACA,UAAM,OAAO,MAAM,CAAC;AACpB,UAAM,WAAW,KAAK,QAAQ,GAAG;AACjC,QAAI,aAAa,IAAI;AACnB,eAAS,KAAK,EAAE,MAAM,QAAQ,WAAW,EAAE,MAAM,KAAK,EAAE,CAAC;AACzD;AAAA,IACF;AACA,UAAM,WAAW,KAAK,MAAM,GAAG,QAAQ;AACvC,UAAM,QAAQ,KAAK,MAAM,WAAW,CAAC;AACrC,UAAM,CAAC,GAAG,CAAC,IAAI,MAAM,MAAM,GAAG,EAAE,IAAI,OAAK,SAAS,GAAG,EAAE,CAAC;AAExD,QAAI,CAAC,OAAO,SAAS,CAAC,KAAK,CAAC,OAAO,SAAS,CAAC,GAAG;AAC9C,eAAS,KAAK,EAAE,MAAM,QAAQ,WAAW,EAAE,MAAM,SAAS,EAAE,CAAC;AAC7D;AAAA,IACF;AACA,aAAS,KAAK,EAAE,MAAM,QAAQ,WAAW,EAAE,MAAM,UAAU,OAAO,GAAG,KAAK,EAAE,EAAE,CAAC;AAAA,EACjF;AAAA,EAEQ,aAAa,OAAiB,SAAiB,UAAsB,KAAqC;AAEhH,UAAM,MAAM,OAAO,oBAAI,IAAwB;AAC/C,UAAM,OAAO,MAAM,MAAM,CAAC;AAC1B,eAAW,SAAS,MAAM;AACxB,YAAM,CAAC,UAAU,UAAU,IAAI,MAAM,MAAM,KAAK,CAAC;AACjD,UAAI,CAAC,YAAY,CAAC,YAAY;AAE5B,iBAAS,KAAK,KAAK;AAAA,UACjB,mCAAmC,KAAK;AAAA,QAE1C,CAAC;AACD;AAAA,MACF;AACA,YAAM,aAAa,WAAW,MAAM,GAAG,EAAE,OAAO,OAAO;AACvD,iBAAW,QAAQ,YAAY;AAC7B,cAAM,CAAC,GAAG,CAAC,IAAI,KAAK,MAAM,GAAG,EAAE,IAAI,OAAK,SAAS,GAAG,EAAE,CAAC;AACvD,YAAI,CAAC,OAAO,SAAS,CAAC,KAAK,CAAC,OAAO,SAAS,CAAC,KAAK,IAAI,GAAG;AAEvD,mBAAS,KAAK,KAAK;AAAA,YACjB,4BAA4B,IAAI,SAAS,KAAK;AAAA,UAEhD,CAAC;AACD;AAAA,QACF;AACA,cAAM,MAAM,IAAI,IAAI,QAAQ,KAAK,CAAC;AAClC,YAAI,KAAK,CAAC,GAAG,CAAC,CAAC;AACf,YAAI,IAAI,UAAU,GAAG;AAAA,MACvB;AAAA,IACF;AACA,WAAO;AAAA,EACT;AACF;;;ACxQO,IAAM,sBAAN,MAA0B;AAAA,EAChC,OACC,UACA,MACA,QACA,UAAiC,CAAC,GACzB;AACT,UAAM,QAAQ,YAAY,IAAI,KAAK;AACnC,QAAI,CAAC,MAAM;AACV,aAAO;AAAA,IACR;AACA,UAAM,UAAW,QAAgB,WAAW,GAAG,OAAO,KAAK;AAC3D,UAAM,UAAU,QAAQ,QAAQ,OAAO;AACvC,UAAM,WAAW,QAAQ,CAAC;AAE1B,QAAI,CAAC,WAAW,CAAC,SAAS;AACzB,aAAO;AAAA,IACR;AAEA,YAAQ,MAAM;AAAA,MACb,KAAK;AACJ,eAAO,KAAK,WAAW,UAAU,SAAS,OAAO;AAAA,MAClD,KAAK;AACJ,eAAO,KAAK,cAAc,UAAU,SAAS,OAAO;AAAA,MACrD,KAAK;AACJ,eAAO,KAAK,WAAW,UAAU,SAAS,OAAO;AAAA,MAClD;AACC,eAAO,UAAU;AAAA,EAAkB,OAAO;AAAA,kBAAqB;AAAA,IACjE;AAAA,EACD;AAAA,EAEQ,WAAW,MAA+B,SAAiB,SAA0B;AAC5F,QAAI,SAAS;AACZ,aAAO;AAAA,IACR;AACA,UAAMC,QAAO,KAAK,SAAS,KAAK,IAAI,KAAK;AACzC,WAAO,eAAeA,KAAI;AAAA,EAAO,OAAO;AAAA;AAAA,EACzC;AAAA,EAEQ,cAAc,MAA+B,SAAiB,SAA0B;AAC/F,UAAMA,QAAO,KAAK,SAAS,KAAK,IAAI,KAAK;AACzC,QAAI,SAAS;AACZ,aAAO,0BAA0BA,KAAI;AAAA,EAAsB,OAAO;AAAA;AAAA,IACnE;AACA,WAAO,0BAA0BA,KAAI;AAAA,EAAO,OAAO;AAAA;AAAA,EACpD;AAAA,EAEQ,WAAW,MAA+B,SAAiB,SAA0B;AAC5F,UAAM,UAAU,KAAK,SAAS,KAAK,OAAO;AAC1C,UAAMA,QAAO,KAAK,SAAS,KAAK,IAAI;AACpC,UAAM,aAAuB,CAAC;AAC9B,QAAI,YAAY,QAAW;AAC1B,iBAAW,KAAK,YAAY,OAAO,GAAG;AAAA,IACvC;AACA,QAAIA,UAAS,QAAW;AACvB,iBAAW,KAAK,SAASA,KAAI,GAAG;AAAA,IACjC;AACA,QAAI,SAAS;AACZ,iBAAW,KAAK,gBAAgB;AAAA,IACjC;AACA,UAAM,WAAW,WAAW,SAAS,IAAI,WAAW,KAAK,GAAG,CAAC,KAAK;AAClE,WAAO,eAAe,QAAQ;AAAA,EAAM,OAAO;AAAA;AAAA,EAC5C;AAAA,EAEQ,SAAS,OAAoC;AACpD,QAAI,UAAU,QAAQ,UAAU,QAAW;AAC1C,aAAO;AAAA,IACR;AACA,WAAO,OAAO,KAAK;AAAA,EACpB;AACD;AAEA,IAAM,kBAAkB,IAAI,oBAAoB;AAEzC,SAAS,sBACf,UACA,MACA,QACA,UAAiC,CAAC,GACzB;AACT,SAAO,gBAAgB,OAAO,UAAU,MAAM,QAAQ,OAAO;AAC9D;;;ACjFO,IAAM,gBAAgB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACEtB,IAAM,eAAe;AAAA;AAAA,EAE1B,YAAY;AAAA,EACZ,YAAY;AACd;AAMA,IAAM,mBAAmB;AAAA;AAAA,EAEvB;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAO;AAAA;AAAA,EAGvB;AAAA,EAAgB;AAAA,EAAoB;AAAA,EAAS;AAAA,EAC7C;AAAA,EAAU;AAAA,EAAY;AAAA,EAAQ;AAAA;AAAA,EAG9B;AAAA,EAAe;AAAA,EAAiB;AAAA,EAAe;AAAA,EAC/C;AAAA,EAAS;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAS;AAAA;AAAA,EAG1C;AAAA,EAAQ;AAAA,EAAS;AAAA,EAAO;AAAA,EAAU;AAAA,EAAU;AAAA,EAC5C;AAAA,EAAS;AAAA,EAAS;AAAA,EAAW;AAAA,EAAW;AAAA;AAAA,EAGxC;AAAA,EAAU;AAAA,EAAiB;AAAA,EAAU;AAAA,EAAO;AAAA;AAAA,EAG5C;AAAA,EAAS;AAAA,EAAW;AAAA;AAAA,EAGpB;AAAA,EAAY;AAAA,EAAa;AAAA,EAAW;AAAA;AAAA,EAGpC;AAAA,EAAO;AAAA,EAAQ;AAAA,EAAQ;AAAA;AAAA,EAGvB;AAAA,EAAqB;AAAA,EAAa;AAAA,EAAkB;AAAA,EACpD;AAAA,EAAc;AAAA,EAAgB;AAAA;AAAA,EAG9B;AAAA,EAAY;AAAA,EAAa;AAAA,EACzB;AAAA,EAAU;AAAA,EAAQ;AAAA,EAAS;AAAA,EAC3B;AAAA,EAAS;AAAA;AAAA,EAGT;AACF;AAEO,IAAM,oBAAoB,QAAQ,IAAI,2BAA2B,IACrE,MAAM,GAAG,EACT,IAAI,OAAK,EAAE,KAAK,CAAC,EACjB,OAAO,OAAO,EACd,OAAO,gBAAgB;;;AC/C1B,eAAsB,gBACpB,UACA,OACA,QAC+E;AAC/E,QAAM,MAA4E,CAAC;AACnF,aAAW,KAAK,OAAO;AACrB,UAAM,SAAS,YAAY,EAAE,KAAK;AAClC,UAAM,SAAmB,CAAC;AAC1B,eAAW,CAAC,GAAG,CAAC,KAAK,QAAQ;AAC3B,YAAM,QAAQ,MAAM,OAAO,EAAE,MAAM,GAAG,CAAC;AACvC,aAAO,KAAK,MAAM,KAAK,IAAI,CAAC;AAAA,IAC9B;AACA,QAAI,KAAK,EAAE,MAAM,EAAE,MAAM,QAAQ,SAAS,OAAO,KAAK,IAAI,EAAE,CAAC;AAAA,EAC/D;AACA,SAAO;AACT;AAEA,SAAS,YAAY,QAA0D;AAC7E,MAAI,CAAC,OAAO,OAAQ,QAAO,CAAC;AAC5B,QAAM,SAAS,CAAC,GAAG,MAAM,EAAE,KAAK,CAAC,GAAG,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC;AACrD,QAAM,SAAkC,CAAC;AACzC,MAAI,CAAC,IAAI,EAAE,IAAI,OAAO,CAAC;AACvB,WAAS,IAAI,GAAG,IAAI,OAAO,QAAQ,KAAK;AACtC,UAAM,CAAC,GAAG,CAAC,IAAI,OAAO,CAAC;AACvB,QAAI,KAAK,KAAK,GAAG;AACf,WAAK,KAAK,IAAI,IAAI,CAAC;AAAA,IACrB,OAAO;AACL,aAAO,KAAK,CAAC,IAAI,EAAE,CAAC;AACpB,WAAK;AACL,WAAK;AAAA,IACP;AAAA,EACF;AACA,SAAO,KAAK,CAAC,IAAI,EAAE,CAAC;AACpB,SAAO;AACT;;;AC7CA,IAAAC,mBAAe;AAEf,IAAAC,eAAiB;;;ACFjB,2BAAsB;AACtB,qBAAuB;AAKvB,IAAI,iBAAgC;AACpC,IAAI,gBAAgB;AAEpB,SAAS,QAAQ,UAAkB,MAAgB,MAAuE;AACxH,SAAO,IAAI,QAAQ,CAAC,YAAY;AAC9B,UAAM,YAAQ,4BAAM,UAAU,MAAM;AAAA,MAClC,KAAK,MAAM;AAAA,MACX,KAAK,EAAE,GAAG,QAAQ,KAAK,GAAI,MAAM,OAAO,CAAC,EAAG;AAAA,MAC5C,OAAO,CAAC,UAAU,QAAQ,MAAM;AAAA,IAClC,CAAC;AACD,QAAI,SAAS;AACb,QAAI,SAAS;AACb,UAAM,OAAO,GAAG,QAAQ,CAAC,MAAO,UAAU,EAAE,SAAS,CAAE;AACvD,UAAM,OAAO,GAAG,QAAQ,CAAC,MAAO,UAAU,EAAE,SAAS,CAAE;AACvD,UAAM,GAAG,SAAS,CAAC,SAAS;AAC1B,cAAQ,EAAE,QAAQ,QAAQ,UAAU,OAAO,SAAS,WAAW,OAAO,GAAG,CAAC;AAAA,IAC5E,CAAC;AACD,UAAM,GAAG,SAAS,MAAM;AACtB,cAAQ,EAAE,QAAQ,IAAI,QAAQ,4BAA4B,UAAU,GAAG,CAAC;AAAA,IAC1E,CAAC;AAAA,EACH,CAAC;AACH;AAGA,SAAS,gBAAgB,QAA6B;AAEpD,MAAI,OAAO,aAAa,GAAI,QAAO;AAEnC,MAAI,OAAO,OAAO,SAAS,UAAU,KAAK,OAAO,OAAO,SAAS,8BAA8B,EAAG,QAAO;AAEzG,MAAI,OAAO,aAAa,IAAK,QAAO;AACpC,SAAO;AACT;AAEA,eAAsB,WAAW,MAAgB,MAAuE;AAEtH,MAAI,iBAAiB,gBAAgB;AACnC,WAAO,QAAQ,gBAAgB,MAAM,IAAI;AAAA,EAC3C;AAGA,MAAI,CAAC,eAAe;AAClB,UAAM,SAAS,MAAM,QAAQ,uBAAQ,MAAM,IAAI;AAE/C,QAAI,CAAC,gBAAgB,MAAM,GAAG;AAE5B,uBAAiB;AACjB,sBAAgB;AAChB,aAAO;AAAA,IACT;AAGA,UAAM,iBAAiB,MAAM,QAAQ,MAAM,MAAM,IAAI;AAErD,QAAI,CAAC,gBAAgB,cAAc,GAAG;AAEpC,uBAAiB;AACjB,sBAAgB;AAChB,aAAO;AAAA,IACT;AAGA,oBAAgB;AAChB,WAAO;AAAA,MACL,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR,UAAU;AAAA,IACZ;AAAA,EACF;AAGA,SAAO;AAAA,IACL,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,UAAU;AAAA,EACZ;AACF;;;AClFA,gBAAe;AACf,kBAAiB;AAEV,SAAS,iBAAiB,UAAkB,YAA4B;AAC7E,QAAM,UAAU,YAAAC,QAAK,QAAQ,QAAQ;AACrC,QAAM,WAAW,YAAAA,QAAK,QAAQ,SAAS,UAAU;AACjD,mBAAiB,SAAS,QAAQ;AAClC,SAAO;AACT;AAEO,SAAS,iBAAiB,UAAkB,WAAyB;AAC1E,QAAM,MAAM,YAAAA,QAAK,SAAS,YAAAA,QAAK,QAAQ,QAAQ,GAAG,YAAAA,QAAK,QAAQ,SAAS,CAAC;AACzE,MAAI,IAAI,WAAW,IAAI,KAAK,YAAAA,QAAK,WAAW,GAAG,GAAG;AAChD,UAAM,IAAI,MAAM,iCAAiC,SAAS,EAAE;AAAA,EAC9D;AACF;AAEO,SAAS,eAAe,UAAkB,SAAyB;AACxE,SAAO,YAAAA,QAAK,SAAS,YAAAA,QAAK,QAAQ,QAAQ,GAAG,YAAAA,QAAK,QAAQ,OAAO,CAAC;AACpE;AAEO,SAAS,UAAU,GAAoB;AAC5C,MAAI;AACF,UAAM,KAAK,UAAAC,QAAG,UAAU,CAAC;AACzB,WAAO,GAAG,eAAe;AAAA,EAC3B,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEO,SAAS,cAAc,UAAkB,WAAW,KAAoB;AAC7E,MAAI;AACF,UAAM,KAAK,UAAAA,QAAG,SAAS,QAAQ;AAC/B,QAAI,CAAC,GAAG,OAAO,EAAG,QAAO;AACzB,QAAI,GAAG,OAAO,SAAU,QAAO;AAC/B,UAAM,KAAK,UAAAA,QAAG,SAAS,UAAU,GAAG;AACpC,UAAM,MAAM,OAAO,MAAM,GAAG;AAC5B,UAAM,OAAO,UAAAA,QAAG,SAAS,IAAI,KAAK,GAAG,IAAI,QAAQ,CAAC;AAClD,cAAAA,QAAG,UAAU,EAAE;AACf,aAAS,IAAI,GAAG,IAAI,MAAM,KAAK;AAC7B,YAAM,IAAI,IAAI,CAAC;AACf,UAAI,MAAM,EAAG,QAAO;AAAA,IACtB;AACA,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;;;AC/CA,sBAAe;AAEf,eAAsB,aAAa,UAAqC;AACtE,QAAM,UAAU,MAAM,gBAAAC,QAAG,SAAS,UAAU,MAAM;AAElD,SAAO,QAAQ,MAAM,OAAO;AAC9B;;;AHGO,IAAM,uBAAN,MAAuD;AAAA,EAC5D,YAA6B,UAAmC,WAAqB,kBAAkB;AAA1E;AAAmC;AAAA,EAAwC;AAAA,EAExG,MAAM,KAAK,QAAgE;AACzE,UAAM,MAAM,iBAAiB,KAAK,UAAU,OAAO,IAAI;AACvD,UAAM,OAAO,MAAM,iBAAAC,QAAG,KAAK,GAAG,EAAE,MAAM,MAAM,IAAI;AAChD,QAAI,CAAC,KAAM,QAAO,EAAE,OAAO,CAAC,EAAE;AAC9B,UAAM,YAAY,QAAQ,aAAAC,QAAK,QAAQ,KAAK,QAAQ,IAAI,MAAM,eAAe,KAAK,UAAU,GAAG;AAC/F,UAAM,OAAO;AAAA,MACX;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,GAAG,KAAK,SAAS,QAAQ,CAAC,MAAM,CAAC,MAAM,IAAI,CAAC,EAAE,CAAC;AAAA,MAC/C,OAAO;AAAA,MACP,aAAa;AAAA,IACf;AACA,UAAM,MAAM,MAAM,WAAW,MAAM,EAAE,KAAK,KAAK,SAAS,CAAC;AAGzD,QAAI,IAAI,aAAa,IAAI;AACvB,aAAO;AAAA,QACL,OAAO,CAAC;AAAA,QACR,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA,aAKc,IAAI,QAAQ,GAAG,IAAI,SAAS;AAAA,WAAc,IAAI,MAAM,KAAK,EAAE;AAAA,MAClF;AAAA,IACF;AAGA,QAAI,IAAI,aAAa,KAAK,IAAI,aAAa,GAAG;AAC5C,aAAO;AAAA,QACL,OAAO,CAAC;AAAA,QACR,OAAO,8CAA8C,IAAI,QAAQ,GAAG,IAAI,SAAS,KAAK,IAAI,MAAM,KAAK,EAAE;AAAA,MACzG;AAAA,IACF;AAEA,UAAM,SAAS,IAAI,UAAU,IAC1B,KAAK,EACL,MAAM,OAAO,EACb,OAAO,CAAC,MAAM,EAAE,SAAS,CAAC;AAC7B,WAAO,EAAE,MAAM;AAAA,EACjB;AAAA,EAEA,MAAM,KAAK,QAAyE;AAClF,UAAM,MAAM,iBAAiB,KAAK,UAAU,OAAO,IAAI;AACvD,UAAM,YAAY,QAAQ,aAAAA,QAAK,QAAQ,KAAK,QAAQ,IAAI,MAAM,eAAe,KAAK,UAAU,GAAG;AAC/F,UAAM,OAAO;AAAA,MACX;AAAA,MACA;AAAA,MACA;AAAA,MACA,OAAO;AAAA,MACP,GAAG,KAAK,SAAS,QAAQ,CAAC,MAAM,CAAC,MAAM,IAAI,CAAC,EAAE,CAAC;AAAA,MAC/C,aAAa;AAAA,IACf;AACA,UAAM,MAAM,MAAM,WAAW,MAAM,EAAE,KAAK,KAAK,SAAS,CAAC;AAGzD,QAAI,IAAI,aAAa,IAAI;AAEvB,cAAQ,KAAK,+CAA+C,IAAI,UAAU,kBAAkB,EAAE;AAC9F,aAAO,EAAE,OAAO,CAAC,EAAE;AAAA,IACrB;AAEA,UAAM,SAAS,IAAI,UAAU,IAC1B,KAAK,EACL,MAAM,OAAO,EACb,OAAO,CAAC,MAAM,EAAE,SAAS,CAAC;AAC7B,WAAO,EAAE,MAAM;AAAA,EACjB;AAAA,EAEA,MAAM,KAAK,QAA6E;AACtF,UAAM,MAAM,iBAAiB,KAAK,UAAU,OAAO,IAAI;AACvD,UAAM,OAAO,MAAM,iBAAAD,QAAG,KAAK,GAAG,EAAE,MAAM,MAAM,IAAI;AAGhD,QAAI,CAAC,QAAQ,CAAC,KAAK,OAAO,GAAG;AAC3B,aAAO;AAAA,QACL,OAAO,CAAC;AAAA,QACR,OAAO,uCAAuC,OAAO,IAAI;AAAA,MAE3D;AAAA,IACF;AAGA,QAAI,UAAU,GAAG,GAAG;AAClB,aAAO;AAAA,QACL,OAAO,CAAC;AAAA,QACR,OAAO,gCAAgC,OAAO,IAAI;AAAA,MAEpD;AAAA,IACF;AAGA,QAAI,CAAC,cAAc,GAAG,GAAG;AACvB,aAAO;AAAA,QACL,OAAO,CAAC;AAAA,QACR,OAAO,wCAAwC,OAAO,IAAI;AAAA,MAE5D;AAAA,IACF;AAEA,UAAM,QAAQ,MAAM,aAAa,GAAG;AACpC,UAAM,QAAQ,MAAM;AACpB,QAAI,IAAI,OAAO,SAAS;AACxB,QAAI,IAAI,KAAK,IAAI,OAAO,OAAO,OAAO,KAAK;AAC3C,QAAI,IAAI,SAAS,QAAQ,GAAG;AAE1B,UAAI;AACJ,UAAI;AAAA,IACN;AACA,UAAM,MAAgB,CAAC;AACvB,aAAS,IAAI,GAAG,KAAK,GAAG,KAAK,GAAG;AAC9B,YAAM,UAAU,MAAM,IAAI,CAAC,KAAK;AAChC,UAAI,KAAK,GAAG,CAAC,IAAI,OAAO,EAAE;AAAA,IAC5B;AACA,WAAO,EAAE,OAAO,IAAI;AAAA,EACtB;AAAA,EAEA,MAAM,QAAQ,QAAoH;AAChI,UAAM,MAAM,iBAAiB,KAAK,UAAU,OAAO,IAAI;AACvD,UAAM,OAAO,MAAM,iBAAAA,QAAG,KAAK,GAAG,EAAE,MAAM,MAAM,IAAI;AAChD,QAAI,CAAC,QAAQ,CAAC,KAAK,YAAY,GAAG;AAChC,aAAO,CAAC;AAAA,IACV;AACA,UAAM,aAAa,OAAO,cAAc;AACxC,UAAM,WAAW,OAAO,YAAY;AACpC,UAAM,QAAQ,OAAO,UAAU,IAAI,OAAO,OAAO,OAAO,IAAI;AAE5D,UAAM,UAA0B,CAAC;AACjC,mBAAe,KAAK,KAAa,OAAe;AAC9C,UAAI,QAAQ,YAAY,QAAQ,UAAU,WAAY;AACtD,YAAM,UAAU,MAAM,iBAAAA,QAAG,QAAQ,KAAK,EAAE,eAAe,KAAK,CAAC;AAC7D,iBAAW,SAAS,SAAS;AAC3B,cAAM,OAAO,aAAAC,QAAK,KAAK,KAAK,MAAM,IAAI;AACtC,cAAM,MAAM,eAAe,KAAK,IAAI,EAAE,QAAQ,cAAc,EAAE;AAC9D,YAAI,iBAAiB,KAAK,CAAC,OAAO,IAAI,MAAM,aAAAA,QAAK,GAAG,EAAE,SAAS,EAAE,CAAC,EAAG;AACrE,YAAI,SAAS,CAAC,MAAM,KAAK,MAAM,IAAI,EAAG;AACtC,YAAI,QAAQ,UAAU,WAAY;AAClC,gBAAQ,KAAK;AAAA,UACX,MAAM,MAAM;AAAA,UACZ,MAAM,eAAe,aAAAA,QAAK,QAAQ,EAAE,GAAG,IAAI;AAAA;AAAA,UAC3C,MAAM,MAAM,YAAY,IAAI,QAAQ;AAAA,UACpC;AAAA,QACF,CAAC;AACD,YAAI,MAAM,YAAY,GAAG;AACvB,gBAAM,KAAK,MAAM,QAAQ,CAAC;AAAA,QAC5B;AAAA,MACF;AAAA,IACF;AACA,UAAM,KAAK,KAAK,CAAC;AACjB,WAAO;AAAA,EACT;AACF;;;AN7FA,IAAM,SAAS,IAAI,kBAAkB;AAqB9B,SAAS,eAAe,MAA0B;AACvD,SAAO,OAAO,MAAM,IAAI;AAC1B;AA8BO,SAAS,iBACd,MACA,MACA,QACA,SACQ;AACR,SAAO,sBAAsB,MAAM,MAAM,QAAQ,WAAW,CAAC,CAAC;AAChE;AAgBO,SAAS,kBAAkB,MAAc,WAAW,GAAW;AACpE,QAAM,YAAY,WAAW;AAC7B,MAAI,cAAc,GAAG;AACnB,WAAO;AAAA;AAAA,QAAa,IAAI,IAAI,QAAQ;AAAA,EACtC;AACA,MAAI,cAAc,GAAG;AACnB,WAAO;AAAA;AAAA,QAAa,IAAI,IAAI,QAAQ;AAAA,EACtC;AACA,SAAO;AAAA;AAAA,QAAa,IAAI,IAAI,QAAQ,cAAc,SAAS;AAC7D;AAmBO,SAAS,kBAAkB,SAAiC;AACjE,MAAI,CAAC,QAAQ,OAAQ,QAAO;AAC5B,SAAO,QACJ,IAAI,CAAC,MAAM,GAAG,KAAK,OAAO,EAAE,KAAK,CAAC,KAAK,EAAE,SAAS,QAAQ,QAAQ,KAAK,IAAI,EAAE,IAAI,EAAE,EACnF,KAAK,IAAI;AACd;AA4BA,eAAsB,mBACpB,UACA,OACyB;AACzB,SAAO,gBAAgB,IAAI,OAAO,OAAO,GAAG,GAAG,MAAM;AACnD,UAAM,IAAI,MAAM,SAAS,KAAK,EAAE,MAAM,GAAG,OAAO,GAAG,KAAK,EAAE,CAAC;AAC3D,WAAO,EAAE,MAAM,IAAI,CAAC,MAAM;AACxB,YAAM,MAAM,EAAE,QAAQ,GAAG;AACzB,aAAO,OAAO,IAAI,EAAE,MAAM,MAAM,CAAC,IAAI;AAAA,IACvC,CAAC;AAAA,EACH,CAAC;AACH;AAOO,IAAM,YAAY;AAGlB,IAAM,aAAa,aAAa;","names":["path","path","import_promises","import_path","path","fs","fs","fs","path"]}
@@ -0,0 +1,176 @@
1
+ export { SYSTEM_PROMPT } from './agent/prompt.js';
2
+ export { DEFAULT_EXCLUDES } from './agent/config.js';
3
+ export { LocalRipgrepProvider } from './providers/local.js';
4
+ import { ToolCall, FinishFileSpec } from './agent/types.js';
5
+ export { ChatMessage, ToolName } from './agent/types.js';
6
+ import { AnalyseEntry, WarpGrepProvider } from './providers/types.js';
7
+ export { GrepResult, ReadResult } from './providers/types.js';
8
+
9
+ /**
10
+ * Warp Grep Harness Primitives
11
+ *
12
+ * Building blocks for custom agent harnesses.
13
+ * Use these when you want to control the agent loop yourself.
14
+ *
15
+ * @example
16
+ * ```typescript
17
+ * import {
18
+ * parseToolCalls,
19
+ * SYSTEM_PROMPT,
20
+ * formatToolResult,
21
+ * formatTurnMessage,
22
+ * formatAnalyseTree,
23
+ * resolveFinishFiles,
24
+ * MAX_TURNS,
25
+ * LocalRipgrepProvider,
26
+ * type ToolCall,
27
+ * type WarpGrepProvider,
28
+ * } from '@morphllm/morphsdk/tools/warp-grep/harness';
29
+ *
30
+ * const provider = new LocalRipgrepProvider(repoRoot);
31
+ * const messages = [
32
+ * { role: 'system', content: SYSTEM_PROMPT },
33
+ * { role: 'user', content: `<query>${query}</query>` },
34
+ * ];
35
+ *
36
+ * for (let turn = 1; turn <= MAX_TURNS; turn++) {
37
+ * const response = await myModel(messages);
38
+ * messages.push({ role: 'assistant', content: response });
39
+ *
40
+ * const toolCalls = parseToolCalls(response);
41
+ * const results: string[] = [];
42
+ *
43
+ * for (const call of toolCalls) {
44
+ * if (call.name === 'finish') {
45
+ * return await resolveFinishFiles(provider, call.arguments.files);
46
+ * }
47
+ *
48
+ * let output: string;
49
+ * if (call.name === 'grep') {
50
+ * const r = await provider.grep(call.arguments);
51
+ * output = r.error || r.lines.join('\n') || 'no matches';
52
+ * } else if (call.name === 'read') {
53
+ * const r = await provider.read(call.arguments);
54
+ * output = r.error || r.lines.join('\n') || '(empty)';
55
+ * } else if (call.name === 'analyse') {
56
+ * const entries = await provider.analyse(call.arguments);
57
+ * output = formatAnalyseTree(entries);
58
+ * }
59
+ *
60
+ * results.push(formatToolResult(call.name, call.arguments, output));
61
+ * }
62
+ *
63
+ * messages.push({
64
+ * role: 'user',
65
+ * content: results.join('\n') + formatTurnMessage(turn)
66
+ * });
67
+ * }
68
+ * ```
69
+ */
70
+
71
+ /**
72
+ * Parse model output into tool calls.
73
+ * Automatically handles <think> block removal and <tool_call> extraction.
74
+ *
75
+ * @param text - Raw model response text
76
+ * @returns Array of parsed tool calls
77
+ *
78
+ * @example
79
+ * ```typescript
80
+ * const response = `
81
+ * <think>Looking for auth code...</think>
82
+ * <tool_call>grep 'authenticate' src/</tool_call>
83
+ * <tool_call>analyse src/auth</tool_call>
84
+ * `;
85
+ * const calls = parseToolCalls(response);
86
+ * // [{ name: 'grep', arguments: { pattern: 'authenticate', path: 'src/' } },
87
+ * // { name: 'analyse', arguments: { path: 'src/auth' } }]
88
+ * ```
89
+ */
90
+ declare function parseToolCalls(text: string): ToolCall[];
91
+
92
+ /**
93
+ * Format tool output with XML wrapper for model consumption.
94
+ * Handles grep, read, analyse with appropriate tags.
95
+ *
96
+ * @param name - Tool name ('grep', 'read', 'analyse')
97
+ * @param args - Tool arguments
98
+ * @param output - Tool output string
99
+ * @param options - Optional settings (isError)
100
+ * @returns Formatted XML string
101
+ *
102
+ * @example
103
+ * ```typescript
104
+ * const formatted = formatToolResult('grep', { pattern: 'auth', path: 'src/' }, 'src/auth.ts:10:function auth()');
105
+ * // <grep_output pattern="auth" path="src/">
106
+ * // src/auth.ts:10:function auth()
107
+ * // </grep_output>
108
+ * ```
109
+ */
110
+ declare function formatToolResult(name: string, args: Record<string, unknown>, output: string, options?: {
111
+ isError?: boolean;
112
+ }): string;
113
+ /**
114
+ * Format turn counter message for the model.
115
+ *
116
+ * @param turn - Current turn number (1-based)
117
+ * @param maxTurns - Maximum turns allowed (default: 4)
118
+ * @returns Turn message string
119
+ *
120
+ * @example
121
+ * ```typescript
122
+ * formatTurnMessage(1); // "\n\n[Turn 1/4] You have 3 turns remaining."
123
+ * formatTurnMessage(3); // "\n\n[Turn 3/4] You have 1 turn remaining."
124
+ * formatTurnMessage(4); // "\n\n[Turn 4/4] This is your LAST turn. You MUST call finish now."
125
+ * ```
126
+ */
127
+ declare function formatTurnMessage(turn: number, maxTurns?: number): string;
128
+ /**
129
+ * Convert AnalyseEntry[] to tree view string.
130
+ * Matches the format expected by the model.
131
+ *
132
+ * @param entries - Array of analyse entries
133
+ * @returns Tree view string
134
+ *
135
+ * @example
136
+ * ```typescript
137
+ * const entries = [
138
+ * { name: 'src', path: 'src', type: 'dir', depth: 0 },
139
+ * { name: 'index.ts', path: 'src/index.ts', type: 'file', depth: 1 },
140
+ * ];
141
+ * formatAnalyseTree(entries);
142
+ * // "- [D] src\n - [F] index.ts"
143
+ * ```
144
+ */
145
+ declare function formatAnalyseTree(entries: AnalyseEntry[]): string;
146
+ /**
147
+ * Resolved file with path and content
148
+ */
149
+ type ResolvedFile = {
150
+ path: string;
151
+ content: string;
152
+ };
153
+ /**
154
+ * Resolve finish command - read all specified file ranges.
155
+ *
156
+ * @param provider - WarpGrepProvider implementation
157
+ * @param files - Array of file specs from finish command
158
+ * @returns Array of resolved files with content
159
+ *
160
+ * @example
161
+ * ```typescript
162
+ * const files = [
163
+ * { path: 'src/auth.ts', lines: [[1, 50], [100, 120]] },
164
+ * { path: 'src/types.ts', lines: [[1, 30]] },
165
+ * ];
166
+ * const resolved = await resolveFinishFiles(provider, files);
167
+ * // [{ path: 'src/auth.ts', content: '...' }, { path: 'src/types.ts', content: '...' }]
168
+ * ```
169
+ */
170
+ declare function resolveFinishFiles(provider: WarpGrepProvider, files: FinishFileSpec[]): Promise<ResolvedFile[]>;
171
+ /** Maximum turns before forced finish (default: 4) */
172
+ declare const MAX_TURNS = 4;
173
+ /** Request timeout in milliseconds */
174
+ declare const TIMEOUT_MS: number;
175
+
176
+ export { AnalyseEntry, FinishFileSpec, MAX_TURNS, type ResolvedFile, TIMEOUT_MS, ToolCall, WarpGrepProvider, formatAnalyseTree, formatToolResult, formatTurnMessage, parseToolCalls, resolveFinishFiles };
@@ -0,0 +1,76 @@
1
+ import {
2
+ LLMResponseParser
3
+ } from "../../chunk-LVPVVLTI.js";
4
+ import {
5
+ SYSTEM_PROMPT
6
+ } from "../../chunk-WETRQJGU.js";
7
+ import {
8
+ LocalRipgrepProvider
9
+ } from "../../chunk-ZJIIICRA.js";
10
+ import "../../chunk-G2RSY56Q.js";
11
+ import "../../chunk-SMGZ6A64.js";
12
+ import "../../chunk-TPP2UGQP.js";
13
+ import {
14
+ readFinishFiles
15
+ } from "../../chunk-EK7OQPWD.js";
16
+ import {
17
+ AGENT_CONFIG,
18
+ DEFAULT_EXCLUDES
19
+ } from "../../chunk-TJIUA27P.js";
20
+ import {
21
+ formatAgentToolOutput
22
+ } from "../../chunk-TICMYDII.js";
23
+ import "../../chunk-PZ5AY32C.js";
24
+
25
+ // tools/warp_grep/harness.ts
26
+ var parser = new LLMResponseParser();
27
+ function parseToolCalls(text) {
28
+ return parser.parse(text);
29
+ }
30
+ function formatToolResult(name, args, output, options) {
31
+ return formatAgentToolOutput(name, args, output, options ?? {});
32
+ }
33
+ function formatTurnMessage(turn, maxTurns = 4) {
34
+ const remaining = maxTurns - turn;
35
+ if (remaining === 0) {
36
+ return `
37
+
38
+ [Turn ${turn}/${maxTurns}] This is your LAST turn. You MUST call finish now.`;
39
+ }
40
+ if (remaining === 1) {
41
+ return `
42
+
43
+ [Turn ${turn}/${maxTurns}] You have 1 turn remaining.`;
44
+ }
45
+ return `
46
+
47
+ [Turn ${turn}/${maxTurns}] You have ${remaining} turns remaining.`;
48
+ }
49
+ function formatAnalyseTree(entries) {
50
+ if (!entries.length) return "empty";
51
+ return entries.map((e) => `${" ".repeat(e.depth)}- ${e.type === "dir" ? "[D]" : "[F]"} ${e.name}`).join("\n");
52
+ }
53
+ async function resolveFinishFiles(provider, files) {
54
+ return readFinishFiles("", files, async (p, s, e) => {
55
+ const r = await provider.read({ path: p, start: s, end: e });
56
+ return r.lines.map((l) => {
57
+ const idx = l.indexOf("|");
58
+ return idx >= 0 ? l.slice(idx + 1) : l;
59
+ });
60
+ });
61
+ }
62
+ var MAX_TURNS = 4;
63
+ var TIMEOUT_MS = AGENT_CONFIG.TIMEOUT_MS;
64
+ export {
65
+ DEFAULT_EXCLUDES,
66
+ LocalRipgrepProvider,
67
+ MAX_TURNS,
68
+ SYSTEM_PROMPT,
69
+ TIMEOUT_MS,
70
+ formatAnalyseTree,
71
+ formatToolResult,
72
+ formatTurnMessage,
73
+ parseToolCalls,
74
+ resolveFinishFiles
75
+ };
76
+ //# sourceMappingURL=harness.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../tools/warp_grep/harness.ts"],"sourcesContent":["/**\n * Warp Grep Harness Primitives\n *\n * Building blocks for custom agent harnesses.\n * Use these when you want to control the agent loop yourself.\n *\n * @example\n * ```typescript\n * import {\n * parseToolCalls,\n * SYSTEM_PROMPT,\n * formatToolResult,\n * formatTurnMessage,\n * formatAnalyseTree,\n * resolveFinishFiles,\n * MAX_TURNS,\n * LocalRipgrepProvider,\n * type ToolCall,\n * type WarpGrepProvider,\n * } from '@morphllm/morphsdk/tools/warp-grep/harness';\n *\n * const provider = new LocalRipgrepProvider(repoRoot);\n * const messages = [\n * { role: 'system', content: SYSTEM_PROMPT },\n * { role: 'user', content: `<query>${query}</query>` },\n * ];\n *\n * for (let turn = 1; turn <= MAX_TURNS; turn++) {\n * const response = await myModel(messages);\n * messages.push({ role: 'assistant', content: response });\n *\n * const toolCalls = parseToolCalls(response);\n * const results: string[] = [];\n *\n * for (const call of toolCalls) {\n * if (call.name === 'finish') {\n * return await resolveFinishFiles(provider, call.arguments.files);\n * }\n *\n * let output: string;\n * if (call.name === 'grep') {\n * const r = await provider.grep(call.arguments);\n * output = r.error || r.lines.join('\\n') || 'no matches';\n * } else if (call.name === 'read') {\n * const r = await provider.read(call.arguments);\n * output = r.error || r.lines.join('\\n') || '(empty)';\n * } else if (call.name === 'analyse') {\n * const entries = await provider.analyse(call.arguments);\n * output = formatAnalyseTree(entries);\n * }\n *\n * results.push(formatToolResult(call.name, call.arguments, output));\n * }\n *\n * messages.push({\n * role: 'user',\n * content: results.join('\\n') + formatTurnMessage(turn)\n * });\n * }\n * ```\n */\n\nimport { LLMResponseParser } from './agent/parser.js';\nimport { formatAgentToolOutput } from './agent/formatter.js';\nimport { SYSTEM_PROMPT } from './agent/prompt.js';\nimport { AGENT_CONFIG, DEFAULT_EXCLUDES } from './agent/config.js';\nimport { readFinishFiles } from './tools/finish.js';\nimport { LocalRipgrepProvider } from './providers/local.js';\nimport type { ToolCall, ToolName, FinishFileSpec, ChatMessage } from './agent/types.js';\nimport type { WarpGrepProvider, GrepResult, ReadResult, AnalyseEntry } from './providers/types.js';\n\n// ════════════════════════════════════════════════════════════════════════════\n// PARSING\n// ════════════════════════════════════════════════════════════════════════════\n\nconst parser = new LLMResponseParser();\n\n/**\n * Parse model output into tool calls.\n * Automatically handles <think> block removal and <tool_call> extraction.\n *\n * @param text - Raw model response text\n * @returns Array of parsed tool calls\n *\n * @example\n * ```typescript\n * const response = `\n * <think>Looking for auth code...</think>\n * <tool_call>grep 'authenticate' src/</tool_call>\n * <tool_call>analyse src/auth</tool_call>\n * `;\n * const calls = parseToolCalls(response);\n * // [{ name: 'grep', arguments: { pattern: 'authenticate', path: 'src/' } },\n * // { name: 'analyse', arguments: { path: 'src/auth' } }]\n * ```\n */\nexport function parseToolCalls(text: string): ToolCall[] {\n return parser.parse(text);\n}\n\n// ════════════════════════════════════════════════════════════════════════════\n// SYSTEM PROMPT\n// ════════════════════════════════════════════════════════════════════════════\n\nexport { SYSTEM_PROMPT };\n\n// ════════════════════════════════════════════════════════════════════════════\n// FORMATTING\n// ════════════════════════════════════════════════════════════════════════════\n\n/**\n * Format tool output with XML wrapper for model consumption.\n * Handles grep, read, analyse with appropriate tags.\n *\n * @param name - Tool name ('grep', 'read', 'analyse')\n * @param args - Tool arguments\n * @param output - Tool output string\n * @param options - Optional settings (isError)\n * @returns Formatted XML string\n *\n * @example\n * ```typescript\n * const formatted = formatToolResult('grep', { pattern: 'auth', path: 'src/' }, 'src/auth.ts:10:function auth()');\n * // <grep_output pattern=\"auth\" path=\"src/\">\n * // src/auth.ts:10:function auth()\n * // </grep_output>\n * ```\n */\nexport function formatToolResult(\n name: string,\n args: Record<string, unknown>,\n output: string,\n options?: { isError?: boolean }\n): string {\n return formatAgentToolOutput(name, args, output, options ?? {});\n}\n\n/**\n * Format turn counter message for the model.\n *\n * @param turn - Current turn number (1-based)\n * @param maxTurns - Maximum turns allowed (default: 4)\n * @returns Turn message string\n *\n * @example\n * ```typescript\n * formatTurnMessage(1); // \"\\n\\n[Turn 1/4] You have 3 turns remaining.\"\n * formatTurnMessage(3); // \"\\n\\n[Turn 3/4] You have 1 turn remaining.\"\n * formatTurnMessage(4); // \"\\n\\n[Turn 4/4] This is your LAST turn. You MUST call finish now.\"\n * ```\n */\nexport function formatTurnMessage(turn: number, maxTurns = 4): string {\n const remaining = maxTurns - turn;\n if (remaining === 0) {\n return `\\n\\n[Turn ${turn}/${maxTurns}] This is your LAST turn. You MUST call finish now.`;\n }\n if (remaining === 1) {\n return `\\n\\n[Turn ${turn}/${maxTurns}] You have 1 turn remaining.`;\n }\n return `\\n\\n[Turn ${turn}/${maxTurns}] You have ${remaining} turns remaining.`;\n}\n\n/**\n * Convert AnalyseEntry[] to tree view string.\n * Matches the format expected by the model.\n *\n * @param entries - Array of analyse entries\n * @returns Tree view string\n *\n * @example\n * ```typescript\n * const entries = [\n * { name: 'src', path: 'src', type: 'dir', depth: 0 },\n * { name: 'index.ts', path: 'src/index.ts', type: 'file', depth: 1 },\n * ];\n * formatAnalyseTree(entries);\n * // \"- [D] src\\n - [F] index.ts\"\n * ```\n */\nexport function formatAnalyseTree(entries: AnalyseEntry[]): string {\n if (!entries.length) return 'empty';\n return entries\n .map((e) => `${' '.repeat(e.depth)}- ${e.type === 'dir' ? '[D]' : '[F]'} ${e.name}`)\n .join('\\n');\n}\n\n// ════════════════════════════════════════════════════════════════════════════\n// FINISH RESOLUTION\n// ════════════════════════════════════════════════════════════════════════════\n\n/**\n * Resolved file with path and content\n */\nexport type ResolvedFile = { path: string; content: string };\n\n/**\n * Resolve finish command - read all specified file ranges.\n *\n * @param provider - WarpGrepProvider implementation\n * @param files - Array of file specs from finish command\n * @returns Array of resolved files with content\n *\n * @example\n * ```typescript\n * const files = [\n * { path: 'src/auth.ts', lines: [[1, 50], [100, 120]] },\n * { path: 'src/types.ts', lines: [[1, 30]] },\n * ];\n * const resolved = await resolveFinishFiles(provider, files);\n * // [{ path: 'src/auth.ts', content: '...' }, { path: 'src/types.ts', content: '...' }]\n * ```\n */\nexport async function resolveFinishFiles(\n provider: WarpGrepProvider,\n files: FinishFileSpec[]\n): Promise<ResolvedFile[]> {\n return readFinishFiles('', files, async (p, s, e) => {\n const r = await provider.read({ path: p, start: s, end: e });\n return r.lines.map((l) => {\n const idx = l.indexOf('|');\n return idx >= 0 ? l.slice(idx + 1) : l;\n });\n });\n}\n\n// ════════════════════════════════════════════════════════════════════════════\n// CONSTANTS\n// ════════════════════════════════════════════════════════════════════════════\n\n/** Maximum turns before forced finish (default: 4) */\nexport const MAX_TURNS = 4;\n\n/** Request timeout in milliseconds */\nexport const TIMEOUT_MS = AGENT_CONFIG.TIMEOUT_MS;\n\n/** Default file/directory exclusions */\nexport { DEFAULT_EXCLUDES };\n\n// ════════════════════════════════════════════════════════════════════════════\n// PROVIDER + TYPES\n// ════════════════════════════════════════════════════════════════════════════\n\nexport { LocalRipgrepProvider };\n\nexport type {\n ToolCall,\n ToolName,\n FinishFileSpec,\n ChatMessage,\n WarpGrepProvider,\n GrepResult,\n ReadResult,\n AnalyseEntry,\n};\n\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AA2EA,IAAM,SAAS,IAAI,kBAAkB;AAqB9B,SAAS,eAAe,MAA0B;AACvD,SAAO,OAAO,MAAM,IAAI;AAC1B;AA8BO,SAAS,iBACd,MACA,MACA,QACA,SACQ;AACR,SAAO,sBAAsB,MAAM,MAAM,QAAQ,WAAW,CAAC,CAAC;AAChE;AAgBO,SAAS,kBAAkB,MAAc,WAAW,GAAW;AACpE,QAAM,YAAY,WAAW;AAC7B,MAAI,cAAc,GAAG;AACnB,WAAO;AAAA;AAAA,QAAa,IAAI,IAAI,QAAQ;AAAA,EACtC;AACA,MAAI,cAAc,GAAG;AACnB,WAAO;AAAA;AAAA,QAAa,IAAI,IAAI,QAAQ;AAAA,EACtC;AACA,SAAO;AAAA;AAAA,QAAa,IAAI,IAAI,QAAQ,cAAc,SAAS;AAC7D;AAmBO,SAAS,kBAAkB,SAAiC;AACjE,MAAI,CAAC,QAAQ,OAAQ,QAAO;AAC5B,SAAO,QACJ,IAAI,CAAC,MAAM,GAAG,KAAK,OAAO,EAAE,KAAK,CAAC,KAAK,EAAE,SAAS,QAAQ,QAAQ,KAAK,IAAI,EAAE,IAAI,EAAE,EACnF,KAAK,IAAI;AACd;AA4BA,eAAsB,mBACpB,UACA,OACyB;AACzB,SAAO,gBAAgB,IAAI,OAAO,OAAO,GAAG,GAAG,MAAM;AACnD,UAAM,IAAI,MAAM,SAAS,KAAK,EAAE,MAAM,GAAG,OAAO,GAAG,KAAK,EAAE,CAAC;AAC3D,WAAO,EAAE,MAAM,IAAI,CAAC,MAAM;AACxB,YAAM,MAAM,EAAE,QAAQ,GAAG;AACzB,aAAO,OAAO,IAAI,EAAE,MAAM,MAAM,CAAC,IAAI;AAAA,IACvC,CAAC;AAAA,EACH,CAAC;AACH;AAOO,IAAM,YAAY;AAGlB,IAAM,aAAa,aAAa;","names":[]}
@@ -620,120 +620,6 @@ function formatAgentToolOutput(toolName, args, output, options = {}) {
620
620
  return sharedFormatter.format(toolName, args, output, options);
621
621
  }
622
622
 
623
- // tools/warp_grep/agent/grep_helpers.ts
624
- var GrepState = class {
625
- seenLines = /* @__PURE__ */ new Set();
626
- isNew(path4, lineNumber) {
627
- const key = this.makeKey(path4, lineNumber);
628
- return !this.seenLines.has(key);
629
- }
630
- add(path4, lineNumber) {
631
- this.seenLines.add(this.makeKey(path4, lineNumber));
632
- }
633
- makeKey(path4, lineNumber) {
634
- return `${path4}:${lineNumber}`;
635
- }
636
- };
637
- var MAX_GREP_OUTPUT_CHARS_PER_TURN = 6e4;
638
- function extractMatchFields(payload) {
639
- const text = payload.replace(/\r?\n$/, "");
640
- if (!text || text.startsWith("[error]")) {
641
- return null;
642
- }
643
- const firstSep = text.indexOf(":");
644
- if (firstSep === -1) {
645
- return null;
646
- }
647
- let filePath = text.slice(0, firstSep).trim();
648
- if (!filePath) {
649
- return null;
650
- }
651
- if (filePath.startsWith("./") || filePath.startsWith(".\\")) {
652
- filePath = filePath.slice(2);
653
- }
654
- const remainder = text.slice(firstSep + 1);
655
- const secondSep = remainder.indexOf(":");
656
- if (secondSep === -1) {
657
- return null;
658
- }
659
- const linePart = remainder.slice(0, secondSep);
660
- const lineNumber = Number.parseInt(linePart, 10);
661
- if (!Number.isInteger(lineNumber) || lineNumber <= 0) {
662
- return null;
663
- }
664
- let contentSegment = remainder.slice(secondSep + 1);
665
- const columnSep = contentSegment.indexOf(":");
666
- if (columnSep !== -1 && /^\d+$/.test(contentSegment.slice(0, columnSep))) {
667
- contentSegment = contentSegment.slice(columnSep + 1);
668
- }
669
- const content = contentSegment.trim();
670
- if (!content) {
671
- return null;
672
- }
673
- return { path: filePath, lineNumber, content };
674
- }
675
- function parseAndFilterGrepOutput(rawOutput, state) {
676
- const matches = [];
677
- if (typeof rawOutput !== "string" || !rawOutput.trim()) {
678
- return matches;
679
- }
680
- for (const line of rawOutput.split(/\r?\n/)) {
681
- const fields = extractMatchFields(line);
682
- if (!fields) {
683
- continue;
684
- }
685
- if (state.isNew(fields.path, fields.lineNumber)) {
686
- matches.push(fields);
687
- state.add(fields.path, fields.lineNumber);
688
- }
689
- }
690
- return matches;
691
- }
692
- function truncateOutput(payload, maxChars) {
693
- if (payload.length <= maxChars) {
694
- return payload;
695
- }
696
- const note = "... (output truncated)";
697
- const available = maxChars - note.length - 1;
698
- if (available <= 0) {
699
- return note;
700
- }
701
- if (payload.length <= available) {
702
- return `${payload.slice(0, available).replace(/\n$/, "")}
703
- ${note}`;
704
- }
705
- const core = payload.slice(0, Math.max(0, available - 1));
706
- const trimmed = core.replace(/\n$/, "").replace(/\s+$/, "");
707
- const snippet = trimmed ? `${trimmed}\u2026` : "\u2026";
708
- return `${snippet}
709
- ${note}`;
710
- }
711
- function formatTurnGrepOutput(matches, maxChars = MAX_GREP_OUTPUT_CHARS_PER_TURN) {
712
- if (!matches || matches.length === 0) {
713
- return "No new matches found.";
714
- }
715
- const matchesByFile = /* @__PURE__ */ new Map();
716
- for (const match of matches) {
717
- if (!matchesByFile.has(match.path)) {
718
- matchesByFile.set(match.path, []);
719
- }
720
- matchesByFile.get(match.path).push(match);
721
- }
722
- const lines = [];
723
- const sortedPaths = Array.from(matchesByFile.keys()).sort();
724
- sortedPaths.forEach((filePath, index) => {
725
- if (index > 0) {
726
- lines.push("");
727
- }
728
- lines.push(filePath);
729
- const sortedMatches = matchesByFile.get(filePath).slice().sort((a, b) => a.lineNumber - b.lineNumber);
730
- for (const match of sortedMatches) {
731
- lines.push(`${match.lineNumber}:${match.content}`);
732
- }
733
- });
734
- return truncateOutput(lines.join("\n"), maxChars);
735
- }
736
-
737
623
  // tools/warp_grep/tools/finish.ts
738
624
  async function readFinishFiles(repoRoot, files, reader) {
739
625
  const out = [];
@@ -829,7 +715,6 @@ async function runWarpGrep(config) {
829
715
  const model = config.model || DEFAULT_MODEL;
830
716
  const provider = config.provider;
831
717
  const errors = [];
832
- const grepState = new GrepState();
833
718
  let finishMeta;
834
719
  let terminationReason = "terminated";
835
720
  for (let round = 1; round <= maxRounds; round += 1) {
@@ -855,10 +740,25 @@ async function runWarpGrep(config) {
855
740
  const msg = c.arguments?.message || "Command skipped due to parsing error";
856
741
  formatted.push(msg);
857
742
  }
858
- const otherPromises = [];
743
+ const allPromises = [];
744
+ for (const c of grepCalls) {
745
+ const args = c.arguments ?? {};
746
+ allPromises.push(
747
+ provider.grep({ pattern: args.pattern, path: args.path }).then(
748
+ (grepRes) => {
749
+ if (grepRes.error) {
750
+ return { terminate: true, error: grepRes.error };
751
+ }
752
+ const output = grepRes.lines.join("\n") || "no matches";
753
+ return formatAgentToolOutput("grep", args, output, { isError: false });
754
+ },
755
+ (err) => formatAgentToolOutput("grep", args, String(err), { isError: true })
756
+ )
757
+ );
758
+ }
859
759
  for (const c of analyseCalls) {
860
760
  const args = c.arguments ?? {};
861
- otherPromises.push(
761
+ allPromises.push(
862
762
  toolAnalyse(provider, args).then(
863
763
  (p) => formatAgentToolOutput("analyse", args, p, { isError: false }),
864
764
  (err) => formatAgentToolOutput("analyse", args, String(err), { isError: true })
@@ -867,38 +767,24 @@ async function runWarpGrep(config) {
867
767
  }
868
768
  for (const c of readCalls) {
869
769
  const args = c.arguments ?? {};
870
- otherPromises.push(
770
+ allPromises.push(
871
771
  toolRead(provider, args).then(
872
772
  (p) => formatAgentToolOutput("read", args, p, { isError: false }),
873
773
  (err) => formatAgentToolOutput("read", args, String(err), { isError: true })
874
774
  )
875
775
  );
876
776
  }
877
- const otherResults = await Promise.all(otherPromises);
878
- formatted.push(...otherResults);
879
- for (const c of grepCalls) {
880
- const args = c.arguments ?? {};
881
- try {
882
- const grepRes = await provider.grep({ pattern: args.pattern, path: args.path });
883
- if (grepRes.error) {
884
- errors.push({ message: grepRes.error });
885
- terminationReason = "terminated";
886
- return {
887
- terminationReason: "terminated",
888
- messages,
889
- errors
890
- };
891
- }
892
- const rawOutput = Array.isArray(grepRes.lines) ? grepRes.lines.join("\n") : "";
893
- const newMatches = parseAndFilterGrepOutput(rawOutput, grepState);
894
- let formattedPayload = formatTurnGrepOutput(newMatches);
895
- if (formattedPayload === "No new matches found.") {
896
- formattedPayload = "no new matches";
897
- }
898
- formatted.push(formatAgentToolOutput("grep", args, formattedPayload, { isError: false }));
899
- } catch (err) {
900
- formatted.push(formatAgentToolOutput("grep", args, String(err), { isError: true }));
777
+ const allResults = await Promise.all(allPromises);
778
+ for (const result of allResults) {
779
+ if (typeof result === "object" && "terminate" in result) {
780
+ errors.push({ message: result.error });
781
+ return {
782
+ terminationReason: "terminated",
783
+ messages,
784
+ errors
785
+ };
901
786
  }
787
+ formatted.push(result);
902
788
  }
903
789
  if (formatted.length > 0) {
904
790
  const turnsUsed = round;