@illuma-ai/agents 1.1.5 → 1.1.7

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 (136) hide show
  1. package/dist/cjs/agents/AgentContext.cjs +1 -0
  2. package/dist/cjs/agents/AgentContext.cjs.map +1 -1
  3. package/dist/cjs/events.cjs +1 -0
  4. package/dist/cjs/events.cjs.map +1 -1
  5. package/dist/cjs/graphs/Graph.cjs +11 -8
  6. package/dist/cjs/graphs/Graph.cjs.map +1 -1
  7. package/dist/cjs/graphs/MultiAgentGraph.cjs +1 -0
  8. package/dist/cjs/graphs/MultiAgentGraph.cjs.map +1 -1
  9. package/dist/cjs/llm/openai/index.cjs +1 -0
  10. package/dist/cjs/llm/openai/index.cjs.map +1 -1
  11. package/dist/cjs/llm/providers.cjs +1 -0
  12. package/dist/cjs/llm/providers.cjs.map +1 -1
  13. package/dist/cjs/main.cjs +35 -13
  14. package/dist/cjs/main.cjs.map +1 -1
  15. package/dist/cjs/messages/content.cjs +1 -0
  16. package/dist/cjs/messages/content.cjs.map +1 -1
  17. package/dist/cjs/messages/core.cjs +1 -0
  18. package/dist/cjs/messages/core.cjs.map +1 -1
  19. package/dist/cjs/messages/dedup.cjs +1 -0
  20. package/dist/cjs/messages/dedup.cjs.map +1 -1
  21. package/dist/cjs/messages/format.cjs +1 -0
  22. package/dist/cjs/messages/format.cjs.map +1 -1
  23. package/dist/cjs/messages/prune.cjs +1 -0
  24. package/dist/cjs/messages/prune.cjs.map +1 -1
  25. package/dist/cjs/messages/tools.cjs +1 -0
  26. package/dist/cjs/messages/tools.cjs.map +1 -1
  27. package/dist/cjs/run.cjs +1 -0
  28. package/dist/cjs/run.cjs.map +1 -1
  29. package/dist/cjs/schemas/validate.cjs +1 -0
  30. package/dist/cjs/schemas/validate.cjs.map +1 -1
  31. package/dist/cjs/splitStream.cjs +1 -0
  32. package/dist/cjs/splitStream.cjs.map +1 -1
  33. package/dist/cjs/stream.cjs +1 -0
  34. package/dist/cjs/stream.cjs.map +1 -1
  35. package/dist/cjs/tools/AskUser.cjs +1 -0
  36. package/dist/cjs/tools/AskUser.cjs.map +1 -1
  37. package/dist/cjs/tools/CodeExecutor.cjs +1 -0
  38. package/dist/cjs/tools/CodeExecutor.cjs.map +1 -1
  39. package/dist/cjs/tools/ProgrammaticToolCalling.cjs +1 -0
  40. package/dist/cjs/tools/ProgrammaticToolCalling.cjs.map +1 -1
  41. package/dist/cjs/tools/ToolNode.cjs +12 -8
  42. package/dist/cjs/tools/ToolNode.cjs.map +1 -1
  43. package/dist/cjs/tools/ToolSearch.cjs +1 -0
  44. package/dist/cjs/tools/ToolSearch.cjs.map +1 -1
  45. package/dist/cjs/tools/approval/constants.cjs +107 -0
  46. package/dist/cjs/tools/approval/constants.cjs.map +1 -0
  47. package/dist/cjs/tools/handlers.cjs +1 -0
  48. package/dist/cjs/tools/handlers.cjs.map +1 -1
  49. package/dist/cjs/tools/search/tool.cjs +1 -0
  50. package/dist/cjs/tools/search/tool.cjs.map +1 -1
  51. package/dist/cjs/utils/fileManifest.cjs.map +1 -1
  52. package/dist/cjs/utils/handlers.cjs +1 -0
  53. package/dist/cjs/utils/handlers.cjs.map +1 -1
  54. package/dist/cjs/utils/llm.cjs +1 -0
  55. package/dist/cjs/utils/llm.cjs.map +1 -1
  56. package/dist/cjs/utils/title.cjs +1 -0
  57. package/dist/cjs/utils/title.cjs.map +1 -1
  58. package/dist/cjs/utils/toolCallContinuation.cjs +1 -0
  59. package/dist/cjs/utils/toolCallContinuation.cjs.map +1 -1
  60. package/dist/cjs/utils/toolDiscoveryCache.cjs +1 -0
  61. package/dist/cjs/utils/toolDiscoveryCache.cjs.map +1 -1
  62. package/dist/esm/agents/AgentContext.mjs +1 -0
  63. package/dist/esm/agents/AgentContext.mjs.map +1 -1
  64. package/dist/esm/events.mjs +1 -0
  65. package/dist/esm/events.mjs.map +1 -1
  66. package/dist/esm/graphs/Graph.mjs +11 -8
  67. package/dist/esm/graphs/Graph.mjs.map +1 -1
  68. package/dist/esm/graphs/MultiAgentGraph.mjs +1 -0
  69. package/dist/esm/graphs/MultiAgentGraph.mjs.map +1 -1
  70. package/dist/esm/llm/openai/index.mjs +1 -0
  71. package/dist/esm/llm/openai/index.mjs.map +1 -1
  72. package/dist/esm/llm/providers.mjs +1 -0
  73. package/dist/esm/llm/providers.mjs.map +1 -1
  74. package/dist/esm/main.mjs +1 -0
  75. package/dist/esm/main.mjs.map +1 -1
  76. package/dist/esm/messages/content.mjs +1 -0
  77. package/dist/esm/messages/content.mjs.map +1 -1
  78. package/dist/esm/messages/core.mjs +1 -0
  79. package/dist/esm/messages/core.mjs.map +1 -1
  80. package/dist/esm/messages/dedup.mjs +1 -0
  81. package/dist/esm/messages/dedup.mjs.map +1 -1
  82. package/dist/esm/messages/format.mjs +1 -0
  83. package/dist/esm/messages/format.mjs.map +1 -1
  84. package/dist/esm/messages/prune.mjs +1 -0
  85. package/dist/esm/messages/prune.mjs.map +1 -1
  86. package/dist/esm/messages/tools.mjs +1 -0
  87. package/dist/esm/messages/tools.mjs.map +1 -1
  88. package/dist/esm/run.mjs +1 -0
  89. package/dist/esm/run.mjs.map +1 -1
  90. package/dist/esm/schemas/validate.mjs +1 -0
  91. package/dist/esm/schemas/validate.mjs.map +1 -1
  92. package/dist/esm/splitStream.mjs +1 -0
  93. package/dist/esm/splitStream.mjs.map +1 -1
  94. package/dist/esm/stream.mjs +1 -0
  95. package/dist/esm/stream.mjs.map +1 -1
  96. package/dist/esm/tools/AskUser.mjs +1 -0
  97. package/dist/esm/tools/AskUser.mjs.map +1 -1
  98. package/dist/esm/tools/CodeExecutor.mjs +1 -0
  99. package/dist/esm/tools/CodeExecutor.mjs.map +1 -1
  100. package/dist/esm/tools/ProgrammaticToolCalling.mjs +1 -0
  101. package/dist/esm/tools/ProgrammaticToolCalling.mjs.map +1 -1
  102. package/dist/esm/tools/ToolNode.mjs +12 -8
  103. package/dist/esm/tools/ToolNode.mjs.map +1 -1
  104. package/dist/esm/tools/ToolSearch.mjs +1 -0
  105. package/dist/esm/tools/ToolSearch.mjs.map +1 -1
  106. package/dist/esm/tools/approval/constants.mjs +105 -0
  107. package/dist/esm/tools/approval/constants.mjs.map +1 -0
  108. package/dist/esm/tools/handlers.mjs +1 -0
  109. package/dist/esm/tools/handlers.mjs.map +1 -1
  110. package/dist/esm/tools/search/tool.mjs +1 -0
  111. package/dist/esm/tools/search/tool.mjs.map +1 -1
  112. package/dist/esm/utils/fileManifest.mjs.map +1 -1
  113. package/dist/esm/utils/handlers.mjs +1 -0
  114. package/dist/esm/utils/handlers.mjs.map +1 -1
  115. package/dist/esm/utils/llm.mjs +1 -0
  116. package/dist/esm/utils/llm.mjs.map +1 -1
  117. package/dist/esm/utils/title.mjs +1 -0
  118. package/dist/esm/utils/title.mjs.map +1 -1
  119. package/dist/esm/utils/toolCallContinuation.mjs +1 -0
  120. package/dist/esm/utils/toolCallContinuation.mjs.map +1 -1
  121. package/dist/esm/utils/toolDiscoveryCache.mjs +1 -0
  122. package/dist/esm/utils/toolDiscoveryCache.mjs.map +1 -1
  123. package/dist/types/common/index.d.ts +1 -0
  124. package/dist/types/tools/approval/constants.d.ts +79 -0
  125. package/dist/types/types/tools.d.ts +4 -1
  126. package/package.json +1 -1
  127. package/src/common/index.ts +1 -0
  128. package/src/graphs/Graph.ts +42 -27
  129. package/src/graphs/gapFeatures.test.ts +65 -22
  130. package/src/tools/ToolNode.ts +11 -12
  131. package/src/tools/__tests__/ToolApproval.test.ts +100 -46
  132. package/src/tools/__tests__/ToolNode.hitl.test.ts +3 -2
  133. package/src/tools/approval/__tests__/constants.test.ts +74 -0
  134. package/src/tools/approval/constants.ts +109 -0
  135. package/src/types/tools.ts +5 -1
  136. package/src/utils/fileManifest.ts +3 -1
@@ -1 +1 @@
1
- {"version":3,"file":"tool.mjs","sources":["../../../../src/tools/search/tool.ts"],"sourcesContent":["import { tool, DynamicStructuredTool } from '@langchain/core/tools';\nimport type { RunnableConfig } from '@langchain/core/runnables';\nimport type * as t from './types';\nimport {\n countrySchema,\n imagesSchema,\n videosSchema,\n querySchema,\n dateSchema,\n newsSchema,\n DATE_RANGE,\n} from './schema';\nimport { createSearchAPI, createSourceProcessor } from './search';\nimport { createSerperScraper } from './serper-scraper';\nimport { createFirecrawlScraper } from './firecrawl';\nimport { expandHighlights } from './highlights';\nimport { formatResultsForLLM } from './format';\nimport { createDefaultLogger } from './utils';\nimport { createReranker } from './rerankers';\nimport { Constants } from '@/common';\n\n/**\n * URL regex pattern to detect direct URLs in query\n */\nconst URL_PATTERN = /https?:\\/\\/[^\\s<>\"{}|\\\\^`[\\]]+/gi;\n\n/**\n * Extracts URLs from a query string\n * @param query - The search query\n * @returns Array of URLs found in the query\n */\nfunction extractUrlsFromQuery(query: string): string[] {\n const matches = query.match(URL_PATTERN);\n return matches ?? [];\n}\n\n/**\n * Checks if the query is primarily a URL request (contains URL and minimal other text)\n * @param query - The search query\n * @returns True if the query appears to be a direct URL request\n */\nfunction isDirectUrlRequest(query: string): boolean {\n const urls = extractUrlsFromQuery(query);\n if (urls.length === 0) {\n return false;\n }\n\n // Remove URLs from query and check remaining text\n let remainingText = query;\n for (const url of urls) {\n remainingText = remainingText.replace(url, '');\n }\n\n // Clean up and check if remaining text is minimal (just filler words or questions about the URL)\n remainingText = remainingText.trim().toLowerCase();\n\n // If very little text remains, it's likely a direct URL request\n if (remainingText.length < 50) {\n return true;\n }\n\n return false;\n}\n\n/**\n * Directly extracts content from URLs using the scraper\n * @param urls - URLs to extract content from\n * @param scraper - The scraper instance to use\n * @param logger - Logger instance\n * @returns Search result with extracted content\n */\nasync function extractDirectUrlContent({\n urls,\n scraper,\n logger,\n}: {\n urls: string[];\n scraper: t.BaseScraper;\n logger: t.Logger;\n}): Promise<t.SearchResult> {\n try {\n const results: t.ProcessedOrganic[] = [];\n\n for (const url of urls) {\n try {\n logger.debug(`Direct URL extraction: ${url}`);\n const [, response] = await scraper.scrapeUrl(url);\n\n if (response.success && response.data) {\n const [content, references] = scraper.extractContent(response);\n const metadata = scraper.extractMetadata(response);\n\n // Helper to safely extract string from metadata\n const getString = (value: unknown): string | undefined => {\n return typeof value === 'string' ? value : undefined;\n };\n\n results.push({\n position: results.length + 1,\n title:\n getString(metadata.title) ?? getString(metadata.ogTitle) ?? url,\n link: url,\n snippet:\n getString(metadata.description) ??\n getString(metadata.ogDescription) ??\n '',\n content: content,\n references: references,\n processed: true,\n });\n } else {\n logger.warn(\n `Failed to extract content from ${url}: ${response.error}`\n );\n // Still add the URL as a result, but without content\n results.push({\n position: results.length + 1,\n title: url,\n link: url,\n snippet: response.error ?? 'Failed to extract content',\n processed: false,\n });\n }\n } catch (error) {\n logger.error(`Error extracting URL ${url}:`, error);\n results.push({\n position: results.length + 1,\n title: url,\n link: url,\n snippet: error instanceof Error ? error.message : String(error),\n processed: false,\n });\n }\n }\n\n return {\n success: true,\n data: {\n organic: results,\n topStories: [],\n images: [],\n videos: [],\n relatedSearches: [],\n },\n };\n } catch (error) {\n logger.error('Error in direct URL extraction:', error);\n return {\n success: false,\n error: error instanceof Error ? error.message : String(error),\n };\n }\n}\n\n/**\n * Executes parallel searches and merges the results\n */\nasync function executeParallelSearches({\n searchAPI,\n query,\n date,\n country,\n safeSearch,\n images,\n videos,\n news,\n logger,\n}: {\n searchAPI: ReturnType<typeof createSearchAPI>;\n query: string;\n date?: DATE_RANGE;\n country?: string;\n safeSearch: t.SearchToolConfig['safeSearch'];\n images: boolean;\n videos: boolean;\n news: boolean;\n logger: t.Logger;\n}): Promise<t.SearchResult> {\n // Prepare all search tasks to run in parallel\n const searchTasks: Promise<t.SearchResult>[] = [\n // Main search\n searchAPI.getSources({\n query,\n date,\n country,\n safeSearch,\n }),\n ];\n\n if (images) {\n searchTasks.push(\n searchAPI\n .getSources({\n query,\n date,\n country,\n safeSearch,\n type: 'images',\n })\n .catch((error) => {\n logger.error('Error fetching images:', error);\n return {\n success: false,\n error: `Images search failed: ${error instanceof Error ? error.message : String(error)}`,\n };\n })\n );\n }\n if (videos) {\n searchTasks.push(\n searchAPI\n .getSources({\n query,\n date,\n country,\n safeSearch,\n type: 'videos',\n })\n .catch((error) => {\n logger.error('Error fetching videos:', error);\n return {\n success: false,\n error: `Videos search failed: ${error instanceof Error ? error.message : String(error)}`,\n };\n })\n );\n }\n if (news) {\n searchTasks.push(\n searchAPI\n .getSources({\n query,\n date,\n country,\n safeSearch,\n type: 'news',\n })\n .catch((error) => {\n logger.error('Error fetching news:', error);\n return {\n success: false,\n error: `News search failed: ${error instanceof Error ? error.message : String(error)}`,\n };\n })\n );\n }\n\n // Run all searches in parallel\n const results = await Promise.all(searchTasks);\n\n // Get the main search result (first result)\n const mainResult = results[0];\n if (!mainResult.success) {\n throw new Error(mainResult.error ?? 'Search failed');\n }\n\n // Merge additional results with the main results\n const mergedResults = { ...mainResult.data };\n\n // Convert existing news to topStories if present\n if (mergedResults.news !== undefined && mergedResults.news.length > 0) {\n const existingNewsAsTopStories = mergedResults.news\n .filter((newsItem) => newsItem.link !== undefined && newsItem.link !== '')\n .map((newsItem) => ({\n title: newsItem.title ?? '',\n link: newsItem.link ?? '',\n source: newsItem.source ?? '',\n date: newsItem.date ?? '',\n imageUrl: newsItem.imageUrl ?? '',\n processed: false,\n }));\n mergedResults.topStories = [\n ...(mergedResults.topStories ?? []),\n ...existingNewsAsTopStories,\n ];\n delete mergedResults.news;\n }\n\n results.slice(1).forEach((result) => {\n if (result.success && result.data !== undefined) {\n if (result.data.images !== undefined && result.data.images.length > 0) {\n mergedResults.images = [\n ...(mergedResults.images ?? []),\n ...result.data.images,\n ];\n }\n if (result.data.videos !== undefined && result.data.videos.length > 0) {\n mergedResults.videos = [\n ...(mergedResults.videos ?? []),\n ...result.data.videos,\n ];\n }\n if (result.data.news !== undefined && result.data.news.length > 0) {\n const newsAsTopStories = result.data.news.map((newsItem) => ({\n ...newsItem,\n link: newsItem.link ?? '',\n }));\n mergedResults.topStories = [\n ...(mergedResults.topStories ?? []),\n ...newsAsTopStories,\n ];\n }\n }\n });\n\n return { success: true, data: mergedResults };\n}\n\nfunction createSearchProcessor({\n searchAPI,\n safeSearch,\n sourceProcessor,\n scraper,\n onGetHighlights,\n logger,\n}: {\n safeSearch: t.SearchToolConfig['safeSearch'];\n searchAPI: ReturnType<typeof createSearchAPI>;\n sourceProcessor: ReturnType<typeof createSourceProcessor>;\n scraper: t.BaseScraper;\n onGetHighlights: t.SearchToolConfig['onGetHighlights'];\n logger: t.Logger;\n}) {\n return async function ({\n query,\n date,\n country,\n proMode = true,\n maxSources = 5,\n onSearchResults,\n images = false,\n videos = false,\n news = false,\n }: {\n query: string;\n country?: string;\n date?: DATE_RANGE;\n proMode?: boolean;\n maxSources?: number;\n onSearchResults: t.SearchToolConfig['onSearchResults'];\n images?: boolean;\n videos?: boolean;\n news?: boolean;\n }): Promise<t.SearchResultData> {\n try {\n // Check if query contains direct URLs for extraction\n const urls = extractUrlsFromQuery(query);\n const isDirectUrl = isDirectUrlRequest(query);\n\n let searchResult: t.SearchResult;\n\n if (isDirectUrl && urls.length > 0) {\n // Direct URL extraction mode - skip search API and extract directly\n logger.debug(`Direct URL extraction mode for: ${urls.join(', ')}`);\n searchResult = await extractDirectUrlContent({\n urls,\n scraper,\n logger,\n });\n } else {\n // Normal search mode - execute parallel searches and merge results\n searchResult = await executeParallelSearches({\n searchAPI,\n query,\n date,\n country,\n safeSearch,\n images,\n videos,\n news,\n logger,\n });\n }\n\n onSearchResults?.(searchResult);\n\n const processedSources = await sourceProcessor.processSources({\n query,\n news,\n result: searchResult,\n proMode,\n onGetHighlights,\n numElements: maxSources,\n // Skip additional scraping if we already extracted content directly\n skipScraping: isDirectUrl,\n });\n\n return expandHighlights(processedSources);\n } catch (error) {\n logger.error('Error in search:', error);\n return {\n organic: [],\n topStories: [],\n images: [],\n videos: [],\n news: [],\n relatedSearches: [],\n error: error instanceof Error ? error.message : String(error),\n };\n }\n };\n}\n\nfunction createOnSearchResults({\n runnableConfig,\n onSearchResults,\n}: {\n runnableConfig: RunnableConfig;\n onSearchResults: t.SearchToolConfig['onSearchResults'];\n}) {\n return function (results: t.SearchResult): void {\n if (!onSearchResults) {\n return;\n }\n onSearchResults(results, runnableConfig);\n };\n}\n\nfunction createTool({\n schema,\n search,\n onSearchResults: _onSearchResults,\n}: {\n schema: Record<string, unknown>;\n search: ReturnType<typeof createSearchProcessor>;\n onSearchResults: t.SearchToolConfig['onSearchResults'];\n}): DynamicStructuredTool {\n return tool(\n async (rawParams, runnableConfig) => {\n const params = rawParams as SearchToolParams;\n const { query, date, country: _c, images, videos, news } = params;\n const country = typeof _c === 'string' && _c ? _c : undefined;\n\n // Log the incoming query for debugging URL detection\n const toolLogger = createDefaultLogger();\n toolLogger.debug(`[web_search] Received query: \"${query}\"`);\n const detectedUrls = extractUrlsFromQuery(query);\n if (detectedUrls.length > 0) {\n toolLogger.debug(\n `[web_search] Detected URLs in query: ${detectedUrls.join(', ')}`\n );\n }\n\n const searchResult = await search({\n query,\n date,\n country,\n images,\n videos,\n news,\n onSearchResults: createOnSearchResults({\n runnableConfig,\n onSearchResults: _onSearchResults,\n }),\n });\n const turn = runnableConfig.toolCall?.turn ?? 0;\n const { output, references } = formatResultsForLLM(turn, searchResult);\n const data: t.SearchResultData = { turn, ...searchResult, references };\n return [output, { [Constants.WEB_SEARCH]: data }];\n },\n {\n name: Constants.WEB_SEARCH,\n description:\n `Real-time web search and direct URL content extraction. Results have required citation anchors.\n\n**CAPABILITIES:**\n- Search: Query the web for information on any topic\n- Direct URL: Fetch and extract content from a specific URL for summarization or analysis\n\n**CRITICAL - URL HANDLING:**\nWhen user provides a URL (e.g., \"summarize https://example.com/article\"), you MUST include the FULL URL in the query parameter.\n- CORRECT: query = \"https://example.com/article\" or \"summarize https://example.com/article\"\n- WRONG: query = \"example article summary\" (do NOT convert URLs to search terms)\n\n**USAGE:**\n- For search: Use concise search terms as query\n- For URL extraction: Pass the complete URL in the query field\n\nNote: Use ONCE per reply unless instructed otherwise.\n\nAnchors:\n- \\\\ue202turnXtypeY\n- X = turn idx, type = 'search' | 'news' | 'image' | 'ref', Y = item idx\n\nSpecial Markers:\n- \\\\ue203...\\\\ue204 — highlight start/end of cited text (for Standalone or Group citations)\n- \\\\ue200...\\\\ue201 — group block (e.g. \\\\ue200\\\\ue202turn0search1\\\\ue202turn0news2\\\\ue201)\n\n**CITE EVERY NON-OBVIOUS FACT/QUOTE:**\nUse anchor marker(s) immediately after the statement:\n- Standalone: \"Pure functions produce same output. \\\\ue202turn0search0\"\n- Standalone (multiple): \"Today's News \\\\ue202turn0search0\\\\ue202turn0news0\"\n- Highlight: \"\\\\ue203Highlight text.\\\\ue204\\\\ue202turn0news1\"\n- Group: \"Sources. \\\\ue200\\\\ue202turn0search0\\\\ue202turn0news1\\\\ue201\"\n- Group Highlight: \"\\\\ue203Highlight for group.\\\\ue204 \\\\ue200\\\\ue202turn0search0\\\\ue202turn0news1\\\\ue201\"\n- Image: \"See photo \\\\ue202turn0image0.\"\n\n**NEVER use markdown links, [1], or footnotes. CITE ONLY with anchors provided.**\n`.trim(),\n schema: schema,\n responseFormat: Constants.CONTENT_AND_ARTIFACT,\n }\n );\n}\n\n/**\n * Creates a search tool with a schema that dynamically includes the country field\n * only when the searchProvider is 'serper'.\n *\n * Supports multiple scraper providers:\n * - Firecrawl (default): Full-featured web scraping with multiple formats\n * - Serper: Lightweight scraping using Serper's scrape API\n *\n * @example\n * ```typescript\n * // Using Firecrawl scraper (default)\n * const searchTool = createSearchTool({\n * searchProvider: 'serper',\n * scraperProvider: 'firecrawl',\n * firecrawlApiKey: 'your-firecrawl-key'\n * });\n *\n * // Using Serper scraper\n * const searchTool = createSearchTool({\n * searchProvider: 'serper',\n * scraperProvider: 'serper',\n * serperApiKey: 'your-serper-key'\n * });\n * ```\n *\n * @param config - The search tool configuration\n * @returns A DynamicStructuredTool with a schema that depends on the searchProvider\n */\n/** Input params type for search tool */\ninterface SearchToolParams {\n query: string;\n date?: DATE_RANGE;\n country?: string;\n images?: boolean;\n videos?: boolean;\n news?: boolean;\n}\n\nexport const createSearchTool = (\n config: t.SearchToolConfig = {}\n): DynamicStructuredTool => {\n const {\n searchProvider = 'serper',\n serperApiKey,\n searxngInstanceUrl,\n searxngApiKey,\n domainBlocklist,\n rerankerType = 'cohere',\n topResults = 5,\n strategies = ['no_extraction'],\n filterContent = true,\n safeSearch = 1,\n scraperProvider = 'firecrawl',\n firecrawlApiKey,\n firecrawlApiUrl,\n firecrawlVersion,\n firecrawlOptions,\n serperScraperOptions,\n scraperTimeout,\n jinaApiKey,\n jinaApiUrl,\n cohereApiKey,\n onSearchResults: _onSearchResults,\n onGetHighlights,\n } = config;\n\n const logger = config.logger || createDefaultLogger();\n\n const schemaProperties: Record<string, unknown> = {\n query: querySchema,\n date: dateSchema,\n images: imagesSchema,\n videos: videosSchema,\n news: newsSchema,\n };\n\n if (searchProvider === 'serper') {\n schemaProperties.country = countrySchema;\n }\n\n const toolSchema = {\n type: 'object',\n properties: schemaProperties,\n required: ['query'],\n };\n\n const searchAPI = createSearchAPI({\n searchProvider,\n serperApiKey,\n searxngInstanceUrl,\n searxngApiKey,\n domainBlocklist,\n });\n\n /** Create scraper based on scraperProvider */\n let scraperInstance: t.BaseScraper;\n\n if (scraperProvider === 'serper') {\n scraperInstance = createSerperScraper({\n ...serperScraperOptions,\n apiKey: serperApiKey,\n timeout: scraperTimeout ?? serperScraperOptions?.timeout,\n logger,\n });\n } else {\n scraperInstance = createFirecrawlScraper({\n ...firecrawlOptions,\n apiKey: firecrawlApiKey ?? process.env.FIRECRAWL_API_KEY,\n apiUrl: firecrawlApiUrl,\n version: firecrawlVersion,\n timeout: scraperTimeout ?? firecrawlOptions?.timeout,\n formats: firecrawlOptions?.formats ?? ['markdown', 'rawHtml'],\n logger,\n });\n }\n\n const selectedReranker = createReranker({\n rerankerType,\n jinaApiKey,\n jinaApiUrl,\n cohereApiKey,\n logger,\n });\n\n if (!selectedReranker) {\n logger.warn('No reranker selected. Using default ranking.');\n }\n\n const sourceProcessor = createSourceProcessor(\n {\n reranker: selectedReranker,\n topResults,\n strategies,\n filterContent,\n logger,\n },\n scraperInstance\n );\n\n const search = createSearchProcessor({\n searchAPI,\n safeSearch,\n sourceProcessor,\n scraper: scraperInstance,\n onGetHighlights,\n logger,\n });\n\n return createTool({\n search,\n schema: toolSchema,\n onSearchResults: _onSearchResults,\n });\n};\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"tool.mjs","sources":["../../../../src/tools/search/tool.ts"],"sourcesContent":["import { tool, DynamicStructuredTool } from '@langchain/core/tools';\nimport type { RunnableConfig } from '@langchain/core/runnables';\nimport type * as t from './types';\nimport {\n countrySchema,\n imagesSchema,\n videosSchema,\n querySchema,\n dateSchema,\n newsSchema,\n DATE_RANGE,\n} from './schema';\nimport { createSearchAPI, createSourceProcessor } from './search';\nimport { createSerperScraper } from './serper-scraper';\nimport { createFirecrawlScraper } from './firecrawl';\nimport { expandHighlights } from './highlights';\nimport { formatResultsForLLM } from './format';\nimport { createDefaultLogger } from './utils';\nimport { createReranker } from './rerankers';\nimport { Constants } from '@/common';\n\n/**\n * URL regex pattern to detect direct URLs in query\n */\nconst URL_PATTERN = /https?:\\/\\/[^\\s<>\"{}|\\\\^`[\\]]+/gi;\n\n/**\n * Extracts URLs from a query string\n * @param query - The search query\n * @returns Array of URLs found in the query\n */\nfunction extractUrlsFromQuery(query: string): string[] {\n const matches = query.match(URL_PATTERN);\n return matches ?? [];\n}\n\n/**\n * Checks if the query is primarily a URL request (contains URL and minimal other text)\n * @param query - The search query\n * @returns True if the query appears to be a direct URL request\n */\nfunction isDirectUrlRequest(query: string): boolean {\n const urls = extractUrlsFromQuery(query);\n if (urls.length === 0) {\n return false;\n }\n\n // Remove URLs from query and check remaining text\n let remainingText = query;\n for (const url of urls) {\n remainingText = remainingText.replace(url, '');\n }\n\n // Clean up and check if remaining text is minimal (just filler words or questions about the URL)\n remainingText = remainingText.trim().toLowerCase();\n\n // If very little text remains, it's likely a direct URL request\n if (remainingText.length < 50) {\n return true;\n }\n\n return false;\n}\n\n/**\n * Directly extracts content from URLs using the scraper\n * @param urls - URLs to extract content from\n * @param scraper - The scraper instance to use\n * @param logger - Logger instance\n * @returns Search result with extracted content\n */\nasync function extractDirectUrlContent({\n urls,\n scraper,\n logger,\n}: {\n urls: string[];\n scraper: t.BaseScraper;\n logger: t.Logger;\n}): Promise<t.SearchResult> {\n try {\n const results: t.ProcessedOrganic[] = [];\n\n for (const url of urls) {\n try {\n logger.debug(`Direct URL extraction: ${url}`);\n const [, response] = await scraper.scrapeUrl(url);\n\n if (response.success && response.data) {\n const [content, references] = scraper.extractContent(response);\n const metadata = scraper.extractMetadata(response);\n\n // Helper to safely extract string from metadata\n const getString = (value: unknown): string | undefined => {\n return typeof value === 'string' ? value : undefined;\n };\n\n results.push({\n position: results.length + 1,\n title:\n getString(metadata.title) ?? getString(metadata.ogTitle) ?? url,\n link: url,\n snippet:\n getString(metadata.description) ??\n getString(metadata.ogDescription) ??\n '',\n content: content,\n references: references,\n processed: true,\n });\n } else {\n logger.warn(\n `Failed to extract content from ${url}: ${response.error}`\n );\n // Still add the URL as a result, but without content\n results.push({\n position: results.length + 1,\n title: url,\n link: url,\n snippet: response.error ?? 'Failed to extract content',\n processed: false,\n });\n }\n } catch (error) {\n logger.error(`Error extracting URL ${url}:`, error);\n results.push({\n position: results.length + 1,\n title: url,\n link: url,\n snippet: error instanceof Error ? error.message : String(error),\n processed: false,\n });\n }\n }\n\n return {\n success: true,\n data: {\n organic: results,\n topStories: [],\n images: [],\n videos: [],\n relatedSearches: [],\n },\n };\n } catch (error) {\n logger.error('Error in direct URL extraction:', error);\n return {\n success: false,\n error: error instanceof Error ? error.message : String(error),\n };\n }\n}\n\n/**\n * Executes parallel searches and merges the results\n */\nasync function executeParallelSearches({\n searchAPI,\n query,\n date,\n country,\n safeSearch,\n images,\n videos,\n news,\n logger,\n}: {\n searchAPI: ReturnType<typeof createSearchAPI>;\n query: string;\n date?: DATE_RANGE;\n country?: string;\n safeSearch: t.SearchToolConfig['safeSearch'];\n images: boolean;\n videos: boolean;\n news: boolean;\n logger: t.Logger;\n}): Promise<t.SearchResult> {\n // Prepare all search tasks to run in parallel\n const searchTasks: Promise<t.SearchResult>[] = [\n // Main search\n searchAPI.getSources({\n query,\n date,\n country,\n safeSearch,\n }),\n ];\n\n if (images) {\n searchTasks.push(\n searchAPI\n .getSources({\n query,\n date,\n country,\n safeSearch,\n type: 'images',\n })\n .catch((error) => {\n logger.error('Error fetching images:', error);\n return {\n success: false,\n error: `Images search failed: ${error instanceof Error ? error.message : String(error)}`,\n };\n })\n );\n }\n if (videos) {\n searchTasks.push(\n searchAPI\n .getSources({\n query,\n date,\n country,\n safeSearch,\n type: 'videos',\n })\n .catch((error) => {\n logger.error('Error fetching videos:', error);\n return {\n success: false,\n error: `Videos search failed: ${error instanceof Error ? error.message : String(error)}`,\n };\n })\n );\n }\n if (news) {\n searchTasks.push(\n searchAPI\n .getSources({\n query,\n date,\n country,\n safeSearch,\n type: 'news',\n })\n .catch((error) => {\n logger.error('Error fetching news:', error);\n return {\n success: false,\n error: `News search failed: ${error instanceof Error ? error.message : String(error)}`,\n };\n })\n );\n }\n\n // Run all searches in parallel\n const results = await Promise.all(searchTasks);\n\n // Get the main search result (first result)\n const mainResult = results[0];\n if (!mainResult.success) {\n throw new Error(mainResult.error ?? 'Search failed');\n }\n\n // Merge additional results with the main results\n const mergedResults = { ...mainResult.data };\n\n // Convert existing news to topStories if present\n if (mergedResults.news !== undefined && mergedResults.news.length > 0) {\n const existingNewsAsTopStories = mergedResults.news\n .filter((newsItem) => newsItem.link !== undefined && newsItem.link !== '')\n .map((newsItem) => ({\n title: newsItem.title ?? '',\n link: newsItem.link ?? '',\n source: newsItem.source ?? '',\n date: newsItem.date ?? '',\n imageUrl: newsItem.imageUrl ?? '',\n processed: false,\n }));\n mergedResults.topStories = [\n ...(mergedResults.topStories ?? []),\n ...existingNewsAsTopStories,\n ];\n delete mergedResults.news;\n }\n\n results.slice(1).forEach((result) => {\n if (result.success && result.data !== undefined) {\n if (result.data.images !== undefined && result.data.images.length > 0) {\n mergedResults.images = [\n ...(mergedResults.images ?? []),\n ...result.data.images,\n ];\n }\n if (result.data.videos !== undefined && result.data.videos.length > 0) {\n mergedResults.videos = [\n ...(mergedResults.videos ?? []),\n ...result.data.videos,\n ];\n }\n if (result.data.news !== undefined && result.data.news.length > 0) {\n const newsAsTopStories = result.data.news.map((newsItem) => ({\n ...newsItem,\n link: newsItem.link ?? '',\n }));\n mergedResults.topStories = [\n ...(mergedResults.topStories ?? []),\n ...newsAsTopStories,\n ];\n }\n }\n });\n\n return { success: true, data: mergedResults };\n}\n\nfunction createSearchProcessor({\n searchAPI,\n safeSearch,\n sourceProcessor,\n scraper,\n onGetHighlights,\n logger,\n}: {\n safeSearch: t.SearchToolConfig['safeSearch'];\n searchAPI: ReturnType<typeof createSearchAPI>;\n sourceProcessor: ReturnType<typeof createSourceProcessor>;\n scraper: t.BaseScraper;\n onGetHighlights: t.SearchToolConfig['onGetHighlights'];\n logger: t.Logger;\n}) {\n return async function ({\n query,\n date,\n country,\n proMode = true,\n maxSources = 5,\n onSearchResults,\n images = false,\n videos = false,\n news = false,\n }: {\n query: string;\n country?: string;\n date?: DATE_RANGE;\n proMode?: boolean;\n maxSources?: number;\n onSearchResults: t.SearchToolConfig['onSearchResults'];\n images?: boolean;\n videos?: boolean;\n news?: boolean;\n }): Promise<t.SearchResultData> {\n try {\n // Check if query contains direct URLs for extraction\n const urls = extractUrlsFromQuery(query);\n const isDirectUrl = isDirectUrlRequest(query);\n\n let searchResult: t.SearchResult;\n\n if (isDirectUrl && urls.length > 0) {\n // Direct URL extraction mode - skip search API and extract directly\n logger.debug(`Direct URL extraction mode for: ${urls.join(', ')}`);\n searchResult = await extractDirectUrlContent({\n urls,\n scraper,\n logger,\n });\n } else {\n // Normal search mode - execute parallel searches and merge results\n searchResult = await executeParallelSearches({\n searchAPI,\n query,\n date,\n country,\n safeSearch,\n images,\n videos,\n news,\n logger,\n });\n }\n\n onSearchResults?.(searchResult);\n\n const processedSources = await sourceProcessor.processSources({\n query,\n news,\n result: searchResult,\n proMode,\n onGetHighlights,\n numElements: maxSources,\n // Skip additional scraping if we already extracted content directly\n skipScraping: isDirectUrl,\n });\n\n return expandHighlights(processedSources);\n } catch (error) {\n logger.error('Error in search:', error);\n return {\n organic: [],\n topStories: [],\n images: [],\n videos: [],\n news: [],\n relatedSearches: [],\n error: error instanceof Error ? error.message : String(error),\n };\n }\n };\n}\n\nfunction createOnSearchResults({\n runnableConfig,\n onSearchResults,\n}: {\n runnableConfig: RunnableConfig;\n onSearchResults: t.SearchToolConfig['onSearchResults'];\n}) {\n return function (results: t.SearchResult): void {\n if (!onSearchResults) {\n return;\n }\n onSearchResults(results, runnableConfig);\n };\n}\n\nfunction createTool({\n schema,\n search,\n onSearchResults: _onSearchResults,\n}: {\n schema: Record<string, unknown>;\n search: ReturnType<typeof createSearchProcessor>;\n onSearchResults: t.SearchToolConfig['onSearchResults'];\n}): DynamicStructuredTool {\n return tool(\n async (rawParams, runnableConfig) => {\n const params = rawParams as SearchToolParams;\n const { query, date, country: _c, images, videos, news } = params;\n const country = typeof _c === 'string' && _c ? _c : undefined;\n\n // Log the incoming query for debugging URL detection\n const toolLogger = createDefaultLogger();\n toolLogger.debug(`[web_search] Received query: \"${query}\"`);\n const detectedUrls = extractUrlsFromQuery(query);\n if (detectedUrls.length > 0) {\n toolLogger.debug(\n `[web_search] Detected URLs in query: ${detectedUrls.join(', ')}`\n );\n }\n\n const searchResult = await search({\n query,\n date,\n country,\n images,\n videos,\n news,\n onSearchResults: createOnSearchResults({\n runnableConfig,\n onSearchResults: _onSearchResults,\n }),\n });\n const turn = runnableConfig.toolCall?.turn ?? 0;\n const { output, references } = formatResultsForLLM(turn, searchResult);\n const data: t.SearchResultData = { turn, ...searchResult, references };\n return [output, { [Constants.WEB_SEARCH]: data }];\n },\n {\n name: Constants.WEB_SEARCH,\n description:\n `Real-time web search and direct URL content extraction. Results have required citation anchors.\n\n**CAPABILITIES:**\n- Search: Query the web for information on any topic\n- Direct URL: Fetch and extract content from a specific URL for summarization or analysis\n\n**CRITICAL - URL HANDLING:**\nWhen user provides a URL (e.g., \"summarize https://example.com/article\"), you MUST include the FULL URL in the query parameter.\n- CORRECT: query = \"https://example.com/article\" or \"summarize https://example.com/article\"\n- WRONG: query = \"example article summary\" (do NOT convert URLs to search terms)\n\n**USAGE:**\n- For search: Use concise search terms as query\n- For URL extraction: Pass the complete URL in the query field\n\nNote: Use ONCE per reply unless instructed otherwise.\n\nAnchors:\n- \\\\ue202turnXtypeY\n- X = turn idx, type = 'search' | 'news' | 'image' | 'ref', Y = item idx\n\nSpecial Markers:\n- \\\\ue203...\\\\ue204 — highlight start/end of cited text (for Standalone or Group citations)\n- \\\\ue200...\\\\ue201 — group block (e.g. \\\\ue200\\\\ue202turn0search1\\\\ue202turn0news2\\\\ue201)\n\n**CITE EVERY NON-OBVIOUS FACT/QUOTE:**\nUse anchor marker(s) immediately after the statement:\n- Standalone: \"Pure functions produce same output. \\\\ue202turn0search0\"\n- Standalone (multiple): \"Today's News \\\\ue202turn0search0\\\\ue202turn0news0\"\n- Highlight: \"\\\\ue203Highlight text.\\\\ue204\\\\ue202turn0news1\"\n- Group: \"Sources. \\\\ue200\\\\ue202turn0search0\\\\ue202turn0news1\\\\ue201\"\n- Group Highlight: \"\\\\ue203Highlight for group.\\\\ue204 \\\\ue200\\\\ue202turn0search0\\\\ue202turn0news1\\\\ue201\"\n- Image: \"See photo \\\\ue202turn0image0.\"\n\n**NEVER use markdown links, [1], or footnotes. CITE ONLY with anchors provided.**\n`.trim(),\n schema: schema,\n responseFormat: Constants.CONTENT_AND_ARTIFACT,\n }\n );\n}\n\n/**\n * Creates a search tool with a schema that dynamically includes the country field\n * only when the searchProvider is 'serper'.\n *\n * Supports multiple scraper providers:\n * - Firecrawl (default): Full-featured web scraping with multiple formats\n * - Serper: Lightweight scraping using Serper's scrape API\n *\n * @example\n * ```typescript\n * // Using Firecrawl scraper (default)\n * const searchTool = createSearchTool({\n * searchProvider: 'serper',\n * scraperProvider: 'firecrawl',\n * firecrawlApiKey: 'your-firecrawl-key'\n * });\n *\n * // Using Serper scraper\n * const searchTool = createSearchTool({\n * searchProvider: 'serper',\n * scraperProvider: 'serper',\n * serperApiKey: 'your-serper-key'\n * });\n * ```\n *\n * @param config - The search tool configuration\n * @returns A DynamicStructuredTool with a schema that depends on the searchProvider\n */\n/** Input params type for search tool */\ninterface SearchToolParams {\n query: string;\n date?: DATE_RANGE;\n country?: string;\n images?: boolean;\n videos?: boolean;\n news?: boolean;\n}\n\nexport const createSearchTool = (\n config: t.SearchToolConfig = {}\n): DynamicStructuredTool => {\n const {\n searchProvider = 'serper',\n serperApiKey,\n searxngInstanceUrl,\n searxngApiKey,\n domainBlocklist,\n rerankerType = 'cohere',\n topResults = 5,\n strategies = ['no_extraction'],\n filterContent = true,\n safeSearch = 1,\n scraperProvider = 'firecrawl',\n firecrawlApiKey,\n firecrawlApiUrl,\n firecrawlVersion,\n firecrawlOptions,\n serperScraperOptions,\n scraperTimeout,\n jinaApiKey,\n jinaApiUrl,\n cohereApiKey,\n onSearchResults: _onSearchResults,\n onGetHighlights,\n } = config;\n\n const logger = config.logger || createDefaultLogger();\n\n const schemaProperties: Record<string, unknown> = {\n query: querySchema,\n date: dateSchema,\n images: imagesSchema,\n videos: videosSchema,\n news: newsSchema,\n };\n\n if (searchProvider === 'serper') {\n schemaProperties.country = countrySchema;\n }\n\n const toolSchema = {\n type: 'object',\n properties: schemaProperties,\n required: ['query'],\n };\n\n const searchAPI = createSearchAPI({\n searchProvider,\n serperApiKey,\n searxngInstanceUrl,\n searxngApiKey,\n domainBlocklist,\n });\n\n /** Create scraper based on scraperProvider */\n let scraperInstance: t.BaseScraper;\n\n if (scraperProvider === 'serper') {\n scraperInstance = createSerperScraper({\n ...serperScraperOptions,\n apiKey: serperApiKey,\n timeout: scraperTimeout ?? serperScraperOptions?.timeout,\n logger,\n });\n } else {\n scraperInstance = createFirecrawlScraper({\n ...firecrawlOptions,\n apiKey: firecrawlApiKey ?? process.env.FIRECRAWL_API_KEY,\n apiUrl: firecrawlApiUrl,\n version: firecrawlVersion,\n timeout: scraperTimeout ?? firecrawlOptions?.timeout,\n formats: firecrawlOptions?.formats ?? ['markdown', 'rawHtml'],\n logger,\n });\n }\n\n const selectedReranker = createReranker({\n rerankerType,\n jinaApiKey,\n jinaApiUrl,\n cohereApiKey,\n logger,\n });\n\n if (!selectedReranker) {\n logger.warn('No reranker selected. Using default ranking.');\n }\n\n const sourceProcessor = createSourceProcessor(\n {\n reranker: selectedReranker,\n topResults,\n strategies,\n filterContent,\n logger,\n },\n scraperInstance\n );\n\n const search = createSearchProcessor({\n searchAPI,\n safeSearch,\n sourceProcessor,\n scraper: scraperInstance,\n onGetHighlights,\n logger,\n });\n\n return createTool({\n search,\n schema: toolSchema,\n onSearchResults: _onSearchResults,\n });\n};\n"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"fileManifest.mjs","sources":["../../../src/utils/fileManifest.ts"],"sourcesContent":["// src/utils/fileManifest.ts\n//\n// Utility for building a lightweight [Conversation Files] context block\n// from a file manifest. Injected into the compaction windowed view so the\n// LLM retains awareness of ALL conversation files, even when old messages\n// (with full file content) are behind the summary.\n\nimport type { FileManifestEntry } from '@/types/graph';\n\n/**\n * Prefix marker for the file manifest block.\n * Used to detect and deduplicate manifest messages across turns.\n */\nexport const FILE_MANIFEST_PREFIX = '[Conversation Files]';\n\n/**\n * Builds a compact text block listing all files in the conversation.\n * Each entry costs ~10-15 tokens, so 10 files ≈ 100-150 tokens total.\n *\n * The block includes retrieval hints so the LLM knows how to fetch\n * full content on demand (via file_search or content_tool).\n *\n * @param manifest - Array of file metadata entries\n * @returns Formatted text block, or empty string if manifest is empty/undefined\n */\nexport function buildFileManifestBlock(manifest: FileManifestEntry[] | undefined): string {\n if (!manifest || manifest.length === 0) {\n return '';\n }\n\n const lines = manifest.map((entry) => {\n const parts: string[] = [`- ${entry.filename}`];\n if (entry.contentId) {\n parts.push(`(content_id: ${entry.contentId})`);\n }\n if (entry.source) {\n parts.push(`[${entry.source}]`);\n }\n return parts.join(' ');\n });\n\n return [\n FILE_MANIFEST_PREFIX,\n 'The following files have been shared in this conversation.',\n 'Use file_search or content_tool read (with content_id) to retrieve full content when needed.',\n '',\n ...lines,\n ].join('\\n');\n}\n"],"names":[],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AAIA;;;AAGG;AACI,MAAM,oBAAoB,GAAG;AAEpC;;;;;;;;;AASG;AACG,SAAU,sBAAsB,CAAC,QAAyC,EAAA;IAC9E,IAAI,CAAC,QAAQ,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE;AACtC,QAAA,OAAO,EAAE;IACX;IAEA,MAAM,KAAK,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,KAAK,KAAI;QACnC,MAAM,KAAK,GAAa,CAAC,CAAA,EAAA,EAAK,KAAK,CAAC,QAAQ,CAAA,CAAE,CAAC;AAC/C,QAAA,IAAI,KAAK,CAAC,SAAS,EAAE;YACnB,KAAK,CAAC,IAAI,CAAC,CAAA,aAAA,EAAgB,KAAK,CAAC,SAAS,CAAA,CAAA,CAAG,CAAC;QAChD;AACA,QAAA,IAAI,KAAK,CAAC,MAAM,EAAE;YAChB,KAAK,CAAC,IAAI,CAAC,CAAA,CAAA,EAAI,KAAK,CAAC,MAAM,CAAA,CAAA,CAAG,CAAC;QACjC;AACA,QAAA,OAAO,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC;AACxB,IAAA,CAAC,CAAC;IAEF,OAAO;QACL,oBAAoB;QACpB,4DAA4D;QAC5D,8FAA8F;QAC9F,EAAE;AACF,QAAA,GAAG,KAAK;AACT,KAAA,CAAC,IAAI,CAAC,IAAI,CAAC;AACd;;;;"}
1
+ {"version":3,"file":"fileManifest.mjs","sources":["../../../src/utils/fileManifest.ts"],"sourcesContent":["// src/utils/fileManifest.ts\n//\n// Utility for building a lightweight [Conversation Files] context block\n// from a file manifest. Injected into the compaction windowed view so the\n// LLM retains awareness of ALL conversation files, even when old messages\n// (with full file content) are behind the summary.\n\nimport type { FileManifestEntry } from '@/types/graph';\n\n/**\n * Prefix marker for the file manifest block.\n * Used to detect and deduplicate manifest messages across turns.\n */\nexport const FILE_MANIFEST_PREFIX = '[Conversation Files]';\n\n/**\n * Builds a compact text block listing all files in the conversation.\n * Each entry costs ~10-15 tokens, so 10 files ≈ 100-150 tokens total.\n *\n * The block includes retrieval hints so the LLM knows how to fetch\n * full content on demand (via file_search or content_tool).\n *\n * @param manifest - Array of file metadata entries\n * @returns Formatted text block, or empty string if manifest is empty/undefined\n */\nexport function buildFileManifestBlock(\n manifest: FileManifestEntry[] | undefined\n): string {\n if (!manifest || manifest.length === 0) {\n return '';\n }\n\n const lines = manifest.map((entry) => {\n const parts: string[] = [`- ${entry.filename}`];\n if (entry.contentId) {\n parts.push(`(content_id: ${entry.contentId})`);\n }\n if (entry.source) {\n parts.push(`[${entry.source}]`);\n }\n return parts.join(' ');\n });\n\n return [\n FILE_MANIFEST_PREFIX,\n 'The following files have been shared in this conversation.',\n 'Use file_search or content_tool read (with content_id) to retrieve full content when needed.',\n '',\n ...lines,\n ].join('\\n');\n}\n"],"names":[],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AAIA;;;AAGG;AACI,MAAM,oBAAoB,GAAG;AAEpC;;;;;;;;;AASG;AACG,SAAU,sBAAsB,CACpC,QAAyC,EAAA;IAEzC,IAAI,CAAC,QAAQ,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE;AACtC,QAAA,OAAO,EAAE;IACX;IAEA,MAAM,KAAK,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,KAAK,KAAI;QACnC,MAAM,KAAK,GAAa,CAAC,CAAA,EAAA,EAAK,KAAK,CAAC,QAAQ,CAAA,CAAE,CAAC;AAC/C,QAAA,IAAI,KAAK,CAAC,SAAS,EAAE;YACnB,KAAK,CAAC,IAAI,CAAC,CAAA,aAAA,EAAgB,KAAK,CAAC,SAAS,CAAA,CAAA,CAAG,CAAC;QAChD;AACA,QAAA,IAAI,KAAK,CAAC,MAAM,EAAE;YAChB,KAAK,CAAC,IAAI,CAAC,CAAA,CAAA,EAAI,KAAK,CAAC,MAAM,CAAA,CAAA,CAAG,CAAC;QACjC;AACA,QAAA,OAAO,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC;AACxB,IAAA,CAAC,CAAC;IAEF,OAAO;QACL,oBAAoB;QACpB,4DAA4D;QAC5D,8FAA8F;QAC9F,EAAE;AACF,QAAA,GAAG,KAAK;AACT,KAAA,CAAC,IAAI,CAAC,IAAI,CAAC;AACd;;;;"}
@@ -1,4 +1,5 @@
1
1
  import { GraphEvents } from '../common/enum.mjs';
2
+ import '../tools/approval/constants.mjs';
2
3
  import { createContentAggregator, ChatModelStreamHandler } from '../stream.mjs';
3
4
  import { ModelEndHandler, ToolEndHandler } from '../events.mjs';
4
5
 
@@ -1 +1 @@
1
- {"version":3,"file":"handlers.mjs","sources":["../../../src/utils/handlers.ts"],"sourcesContent":["/**\n * Multi-Agent Handler Utilities\n *\n * Provides a simple helper to create handlers with content aggregation for multi-agent scripts.\n *\n * Usage:\n * ```typescript\n * const { contentParts, aggregateContent, handlers } = createHandlers();\n *\n * // With callbacks\n * const { contentParts, aggregateContent, handlers } = createHandlers({\n * onRunStep: (event, data) => console.log('Step:', data),\n * onRunStepCompleted: (event, data) => console.log('Completed:', data)\n * });\n * ```\n */\n\nimport { GraphEvents } from '@/common';\nimport { ChatModelStreamHandler, createContentAggregator } from '@/stream';\nimport { ToolEndHandler, ModelEndHandler } from '@/events';\nimport type * as t from '@/types';\n\ninterface HandlerCallbacks {\n onRunStep?: (event: GraphEvents.ON_RUN_STEP, data: t.StreamEventData) => void;\n onRunStepCompleted?: (\n event: GraphEvents.ON_RUN_STEP_COMPLETED,\n data: t.StreamEventData\n ) => void;\n onRunStepDelta?: (\n event: GraphEvents.ON_RUN_STEP_DELTA,\n data: t.StreamEventData\n ) => void;\n onMessageDelta?: (\n event: GraphEvents.ON_MESSAGE_DELTA,\n data: t.StreamEventData\n ) => void;\n}\n\n/**\n * Creates handlers with content aggregation for multi-agent scripts\n */\nexport function createHandlers(callbacks?: HandlerCallbacks): {\n contentParts: Array<t.MessageContentComplex | undefined>;\n aggregateContent: ReturnType<\n typeof createContentAggregator\n >['aggregateContent'];\n handlers: Record<string, t.EventHandler>;\n} {\n // Set up content aggregator\n const { contentParts, aggregateContent } = createContentAggregator();\n\n // Create the handlers object\n const handlers = {\n [GraphEvents.TOOL_END]: new ToolEndHandler(),\n [GraphEvents.CHAT_MODEL_END]: new ModelEndHandler(),\n [GraphEvents.CHAT_MODEL_STREAM]: new ChatModelStreamHandler(),\n\n [GraphEvents.ON_RUN_STEP]: {\n handle: (\n event: GraphEvents.ON_RUN_STEP,\n data: t.StreamEventData\n ): void => {\n aggregateContent({ event, data: data as t.RunStep });\n callbacks?.onRunStep?.(event, data);\n },\n },\n\n [GraphEvents.ON_RUN_STEP_COMPLETED]: {\n handle: (\n event: GraphEvents.ON_RUN_STEP_COMPLETED,\n data: t.StreamEventData\n ): void => {\n aggregateContent({\n event,\n data: data as unknown as { result: t.ToolEndEvent },\n });\n callbacks?.onRunStepCompleted?.(event, data);\n },\n },\n\n [GraphEvents.ON_RUN_STEP_DELTA]: {\n handle: (\n event: GraphEvents.ON_RUN_STEP_DELTA,\n data: t.StreamEventData\n ): void => {\n aggregateContent({ event, data: data as t.RunStepDeltaEvent });\n callbacks?.onRunStepDelta?.(event, data);\n },\n },\n\n [GraphEvents.ON_MESSAGE_DELTA]: {\n handle: (\n event: GraphEvents.ON_MESSAGE_DELTA,\n data: t.StreamEventData\n ): void => {\n aggregateContent({ event, data: data as t.MessageDeltaEvent });\n callbacks?.onMessageDelta?.(event, data);\n },\n },\n };\n\n return {\n contentParts,\n aggregateContent,\n handlers,\n };\n}\n"],"names":[],"mappings":";;;;AAAA;;;;;;;;;;;;;;;AAeG;AAuBH;;AAEG;AACG,SAAU,cAAc,CAAC,SAA4B,EAAA;;IAQzD,MAAM,EAAE,YAAY,EAAE,gBAAgB,EAAE,GAAG,uBAAuB,EAAE;;AAGpE,IAAA,MAAM,QAAQ,GAAG;AACf,QAAA,CAAC,WAAW,CAAC,QAAQ,GAAG,IAAI,cAAc,EAAE;AAC5C,QAAA,CAAC,WAAW,CAAC,cAAc,GAAG,IAAI,eAAe,EAAE;AACnD,QAAA,CAAC,WAAW,CAAC,iBAAiB,GAAG,IAAI,sBAAsB,EAAE;AAE7D,QAAA,CAAC,WAAW,CAAC,WAAW,GAAG;AACzB,YAAA,MAAM,EAAE,CACN,KAA8B,EAC9B,IAAuB,KACf;gBACR,gBAAgB,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,IAAiB,EAAE,CAAC;gBACpD,SAAS,EAAE,SAAS,GAAG,KAAK,EAAE,IAAI,CAAC;YACrC,CAAC;AACF,SAAA;AAED,QAAA,CAAC,WAAW,CAAC,qBAAqB,GAAG;AACnC,YAAA,MAAM,EAAE,CACN,KAAwC,EACxC,IAAuB,KACf;AACR,gBAAA,gBAAgB,CAAC;oBACf,KAAK;AACL,oBAAA,IAAI,EAAE,IAA6C;AACpD,iBAAA,CAAC;gBACF,SAAS,EAAE,kBAAkB,GAAG,KAAK,EAAE,IAAI,CAAC;YAC9C,CAAC;AACF,SAAA;AAED,QAAA,CAAC,WAAW,CAAC,iBAAiB,GAAG;AAC/B,YAAA,MAAM,EAAE,CACN,KAAoC,EACpC,IAAuB,KACf;gBACR,gBAAgB,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,IAA2B,EAAE,CAAC;gBAC9D,SAAS,EAAE,cAAc,GAAG,KAAK,EAAE,IAAI,CAAC;YAC1C,CAAC;AACF,SAAA;AAED,QAAA,CAAC,WAAW,CAAC,gBAAgB,GAAG;AAC9B,YAAA,MAAM,EAAE,CACN,KAAmC,EACnC,IAAuB,KACf;gBACR,gBAAgB,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,IAA2B,EAAE,CAAC;gBAC9D,SAAS,EAAE,cAAc,GAAG,KAAK,EAAE,IAAI,CAAC;YAC1C,CAAC;AACF,SAAA;KACF;IAED,OAAO;QACL,YAAY;QACZ,gBAAgB;QAChB,QAAQ;KACT;AACH;;;;"}
1
+ {"version":3,"file":"handlers.mjs","sources":["../../../src/utils/handlers.ts"],"sourcesContent":["/**\n * Multi-Agent Handler Utilities\n *\n * Provides a simple helper to create handlers with content aggregation for multi-agent scripts.\n *\n * Usage:\n * ```typescript\n * const { contentParts, aggregateContent, handlers } = createHandlers();\n *\n * // With callbacks\n * const { contentParts, aggregateContent, handlers } = createHandlers({\n * onRunStep: (event, data) => console.log('Step:', data),\n * onRunStepCompleted: (event, data) => console.log('Completed:', data)\n * });\n * ```\n */\n\nimport { GraphEvents } from '@/common';\nimport { ChatModelStreamHandler, createContentAggregator } from '@/stream';\nimport { ToolEndHandler, ModelEndHandler } from '@/events';\nimport type * as t from '@/types';\n\ninterface HandlerCallbacks {\n onRunStep?: (event: GraphEvents.ON_RUN_STEP, data: t.StreamEventData) => void;\n onRunStepCompleted?: (\n event: GraphEvents.ON_RUN_STEP_COMPLETED,\n data: t.StreamEventData\n ) => void;\n onRunStepDelta?: (\n event: GraphEvents.ON_RUN_STEP_DELTA,\n data: t.StreamEventData\n ) => void;\n onMessageDelta?: (\n event: GraphEvents.ON_MESSAGE_DELTA,\n data: t.StreamEventData\n ) => void;\n}\n\n/**\n * Creates handlers with content aggregation for multi-agent scripts\n */\nexport function createHandlers(callbacks?: HandlerCallbacks): {\n contentParts: Array<t.MessageContentComplex | undefined>;\n aggregateContent: ReturnType<\n typeof createContentAggregator\n >['aggregateContent'];\n handlers: Record<string, t.EventHandler>;\n} {\n // Set up content aggregator\n const { contentParts, aggregateContent } = createContentAggregator();\n\n // Create the handlers object\n const handlers = {\n [GraphEvents.TOOL_END]: new ToolEndHandler(),\n [GraphEvents.CHAT_MODEL_END]: new ModelEndHandler(),\n [GraphEvents.CHAT_MODEL_STREAM]: new ChatModelStreamHandler(),\n\n [GraphEvents.ON_RUN_STEP]: {\n handle: (\n event: GraphEvents.ON_RUN_STEP,\n data: t.StreamEventData\n ): void => {\n aggregateContent({ event, data: data as t.RunStep });\n callbacks?.onRunStep?.(event, data);\n },\n },\n\n [GraphEvents.ON_RUN_STEP_COMPLETED]: {\n handle: (\n event: GraphEvents.ON_RUN_STEP_COMPLETED,\n data: t.StreamEventData\n ): void => {\n aggregateContent({\n event,\n data: data as unknown as { result: t.ToolEndEvent },\n });\n callbacks?.onRunStepCompleted?.(event, data);\n },\n },\n\n [GraphEvents.ON_RUN_STEP_DELTA]: {\n handle: (\n event: GraphEvents.ON_RUN_STEP_DELTA,\n data: t.StreamEventData\n ): void => {\n aggregateContent({ event, data: data as t.RunStepDeltaEvent });\n callbacks?.onRunStepDelta?.(event, data);\n },\n },\n\n [GraphEvents.ON_MESSAGE_DELTA]: {\n handle: (\n event: GraphEvents.ON_MESSAGE_DELTA,\n data: t.StreamEventData\n ): void => {\n aggregateContent({ event, data: data as t.MessageDeltaEvent });\n callbacks?.onMessageDelta?.(event, data);\n },\n },\n };\n\n return {\n contentParts,\n aggregateContent,\n handlers,\n };\n}\n"],"names":[],"mappings":";;;;;AAAA;;;;;;;;;;;;;;;AAeG;AAuBH;;AAEG;AACG,SAAU,cAAc,CAAC,SAA4B,EAAA;;IAQzD,MAAM,EAAE,YAAY,EAAE,gBAAgB,EAAE,GAAG,uBAAuB,EAAE;;AAGpE,IAAA,MAAM,QAAQ,GAAG;AACf,QAAA,CAAC,WAAW,CAAC,QAAQ,GAAG,IAAI,cAAc,EAAE;AAC5C,QAAA,CAAC,WAAW,CAAC,cAAc,GAAG,IAAI,eAAe,EAAE;AACnD,QAAA,CAAC,WAAW,CAAC,iBAAiB,GAAG,IAAI,sBAAsB,EAAE;AAE7D,QAAA,CAAC,WAAW,CAAC,WAAW,GAAG;AACzB,YAAA,MAAM,EAAE,CACN,KAA8B,EAC9B,IAAuB,KACf;gBACR,gBAAgB,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,IAAiB,EAAE,CAAC;gBACpD,SAAS,EAAE,SAAS,GAAG,KAAK,EAAE,IAAI,CAAC;YACrC,CAAC;AACF,SAAA;AAED,QAAA,CAAC,WAAW,CAAC,qBAAqB,GAAG;AACnC,YAAA,MAAM,EAAE,CACN,KAAwC,EACxC,IAAuB,KACf;AACR,gBAAA,gBAAgB,CAAC;oBACf,KAAK;AACL,oBAAA,IAAI,EAAE,IAA6C;AACpD,iBAAA,CAAC;gBACF,SAAS,EAAE,kBAAkB,GAAG,KAAK,EAAE,IAAI,CAAC;YAC9C,CAAC;AACF,SAAA;AAED,QAAA,CAAC,WAAW,CAAC,iBAAiB,GAAG;AAC/B,YAAA,MAAM,EAAE,CACN,KAAoC,EACpC,IAAuB,KACf;gBACR,gBAAgB,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,IAA2B,EAAE,CAAC;gBAC9D,SAAS,EAAE,cAAc,GAAG,KAAK,EAAE,IAAI,CAAC;YAC1C,CAAC;AACF,SAAA;AAED,QAAA,CAAC,WAAW,CAAC,gBAAgB,GAAG;AAC9B,YAAA,MAAM,EAAE,CACN,KAAmC,EACnC,IAAuB,KACf;gBACR,gBAAgB,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,IAA2B,EAAE,CAAC;gBAC9D,SAAS,EAAE,cAAc,GAAG,KAAK,EAAE,IAAI,CAAC;YAC1C,CAAC;AACF,SAAA;KACF;IAED,OAAO;QACL,YAAY;QACZ,gBAAgB;QAChB,QAAQ;KACT;AACH;;;;"}
@@ -1,4 +1,5 @@
1
1
  import { Providers } from '../common/enum.mjs';
2
+ import '../tools/approval/constants.mjs';
2
3
 
3
4
  // src/utils/llm.ts
4
5
  function isOpenAILike(provider) {
@@ -1 +1 @@
1
- {"version":3,"file":"llm.mjs","sources":["../../../src/utils/llm.ts"],"sourcesContent":["// src/utils/llm.ts\nimport { Providers } from '@/common';\n\nexport function isOpenAILike(provider?: string | Providers): boolean {\n if (provider == null) {\n return false;\n }\n return (\n [\n Providers.OPENAI,\n Providers.AZURE,\n Providers.OPENROUTER,\n Providers.XAI,\n Providers.DEEPSEEK,\n ] as string[]\n ).includes(provider);\n}\n\nexport function isGoogleLike(provider?: string | Providers): boolean {\n if (provider == null) {\n return false;\n }\n return ([Providers.GOOGLE, Providers.VERTEXAI] as string[]).includes(\n provider\n );\n}\n"],"names":[],"mappings":";;AAAA;AAGM,SAAU,YAAY,CAAC,QAA6B,EAAA;AACxD,IAAA,IAAI,QAAQ,IAAI,IAAI,EAAE;AACpB,QAAA,OAAO,KAAK;IACd;IACA,OACE;AACE,QAAA,SAAS,CAAC,MAAM;AAChB,QAAA,SAAS,CAAC,KAAK;AACf,QAAA,SAAS,CAAC,UAAU;AACpB,QAAA,SAAS,CAAC,GAAG;AACb,QAAA,SAAS,CAAC,QAAQ;AAErB,KAAA,CAAC,QAAQ,CAAC,QAAQ,CAAC;AACtB;AAEM,SAAU,YAAY,CAAC,QAA6B,EAAA;AACxD,IAAA,IAAI,QAAQ,IAAI,IAAI,EAAE;AACpB,QAAA,OAAO,KAAK;IACd;AACA,IAAA,OAAQ,CAAC,SAAS,CAAC,MAAM,EAAE,SAAS,CAAC,QAAQ,CAAc,CAAC,QAAQ,CAClE,QAAQ,CACT;AACH;;;;"}
1
+ {"version":3,"file":"llm.mjs","sources":["../../../src/utils/llm.ts"],"sourcesContent":["// src/utils/llm.ts\nimport { Providers } from '@/common';\n\nexport function isOpenAILike(provider?: string | Providers): boolean {\n if (provider == null) {\n return false;\n }\n return (\n [\n Providers.OPENAI,\n Providers.AZURE,\n Providers.OPENROUTER,\n Providers.XAI,\n Providers.DEEPSEEK,\n ] as string[]\n ).includes(provider);\n}\n\nexport function isGoogleLike(provider?: string | Providers): boolean {\n if (provider == null) {\n return false;\n }\n return ([Providers.GOOGLE, Providers.VERTEXAI] as string[]).includes(\n provider\n );\n}\n"],"names":[],"mappings":";;;AAAA;AAGM,SAAU,YAAY,CAAC,QAA6B,EAAA;AACxD,IAAA,IAAI,QAAQ,IAAI,IAAI,EAAE;AACpB,QAAA,OAAO,KAAK;IACd;IACA,OACE;AACE,QAAA,SAAS,CAAC,MAAM;AAChB,QAAA,SAAS,CAAC,KAAK;AACf,QAAA,SAAS,CAAC,UAAU;AACpB,QAAA,SAAS,CAAC,GAAG;AACb,QAAA,SAAS,CAAC,QAAQ;AAErB,KAAA,CAAC,QAAQ,CAAC,QAAQ,CAAC;AACtB;AAEM,SAAU,YAAY,CAAC,QAA6B,EAAA;AACxD,IAAA,IAAI,QAAQ,IAAI,IAAI,EAAE;AACpB,QAAA,OAAO,KAAK;IACd;AACA,IAAA,OAAQ,CAAC,SAAS,CAAC,MAAM,EAAE,SAAS,CAAC,QAAQ,CAAc,CAAC,QAAQ,CAClE,QAAQ,CACT;AACH;;;;"}
@@ -1,6 +1,7 @@
1
1
  import { ChatPromptTemplate } from '@langchain/core/prompts';
2
2
  import { RunnableLambda, RunnableSequence } from '@langchain/core/runnables';
3
3
  import { ContentTypes } from '../common/enum.mjs';
4
+ import '../tools/approval/constants.mjs';
4
5
 
5
6
  const defaultTitlePrompt = `Analyze this conversation and provide:
6
7
  1. The detected language of the conversation
@@ -1 +1 @@
1
- {"version":3,"file":"title.mjs","sources":["../../../src/utils/title.ts"],"sourcesContent":["import { ChatPromptTemplate } from '@langchain/core/prompts';\nimport { RunnableLambda, RunnableSequence } from '@langchain/core/runnables';\nimport type { Runnable, RunnableConfig } from '@langchain/core/runnables';\nimport type { AIMessage } from '@langchain/core/messages';\nimport type * as t from '@/types';\nimport { ContentTypes } from '@/common';\n\nconst defaultTitlePrompt = `Analyze this conversation and provide:\n1. The detected language of the conversation\n2. A concise title in the detected language (5 words or less, no punctuation or quotation)\n\n{convo}`;\n\nconst titleSchema = {\n type: 'object',\n properties: {\n title: {\n type: 'string',\n description:\n 'A concise title for the conversation in 5 words or less, without punctuation or quotation',\n },\n },\n required: ['title'],\n} as const;\n\nconst combinedSchema = {\n type: 'object',\n properties: {\n language: {\n type: 'string',\n description: 'The detected language of the conversation',\n },\n title: {\n type: 'string',\n description:\n 'A concise title for the conversation in 5 words or less, without punctuation or quotation',\n },\n },\n required: ['language', 'title'],\n} as const;\n\nexport const createTitleRunnable = async (\n model: t.ChatModelInstance,\n _titlePrompt?: string\n): Promise<Runnable> => {\n // Disabled since this works fine\n // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n /* @ts-ignore */\n const titleLLM = model.withStructuredOutput(titleSchema);\n // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n /* @ts-ignore */\n const combinedLLM = model.withStructuredOutput(combinedSchema);\n\n const titlePrompt = ChatPromptTemplate.fromTemplate(\n _titlePrompt ?? defaultTitlePrompt\n ).withConfig({ runName: 'TitlePrompt' });\n\n const titleOnlyInnerChain = RunnableSequence.from([titlePrompt, titleLLM]);\n const combinedInnerChain = RunnableSequence.from([titlePrompt, combinedLLM]);\n\n /** Wrap titleOnlyChain in RunnableLambda to create parent span */\n const titleOnlyChain = new RunnableLambda({\n func: async (\n input: { convo: string },\n config?: Partial<RunnableConfig>\n ): Promise<{ title: string }> => {\n const result = await titleOnlyInnerChain.invoke(input, config);\n return result as { title: string };\n },\n }).withConfig({ runName: 'TitleOnlyChain' });\n\n /** Wrap combinedChain in RunnableLambda to create parent span */\n const combinedChain = new RunnableLambda({\n func: async (\n input: { convo: string },\n config?: Partial<RunnableConfig>\n ): Promise<{ language: string; title: string }> => {\n const result = await combinedInnerChain.invoke(input, config);\n return result as { language: string; title: string };\n },\n }).withConfig({ runName: 'TitleLanguageChain' });\n\n /** Runnable to add default values if needed */\n const addDefaults = new RunnableLambda({\n func: (\n result: { language: string; title: string } | undefined\n ): { language: string; title: string } => ({\n language: result?.language ?? 'English',\n title: result?.title ?? '',\n }),\n }).withConfig({ runName: 'AddDefaults' });\n\n const combinedChainInner = RunnableSequence.from([\n combinedChain,\n addDefaults,\n ]);\n\n /** Wrap combinedChainWithDefaults in RunnableLambda to create parent span */\n const combinedChainWithDefaults = new RunnableLambda({\n func: async (\n input: { convo: string },\n config?: Partial<RunnableConfig>\n ): Promise<{ language: string; title: string }> => {\n return await combinedChainInner.invoke(input, config);\n },\n }).withConfig({ runName: 'CombinedChainWithDefaults' });\n\n return new RunnableLambda({\n func: async (\n input: {\n convo: string;\n inputText: string;\n skipLanguage: boolean;\n },\n config?: Partial<RunnableConfig>\n ): Promise<{ language: string; title: string } | { title: string }> => {\n const invokeInput = { convo: input.convo };\n\n if (input.skipLanguage) {\n return (await titleOnlyChain.invoke(invokeInput, config)) as {\n title: string;\n };\n }\n\n return await combinedChainWithDefaults.invoke(invokeInput, config);\n },\n }).withConfig({ runName: 'TitleGenerator' });\n};\n\nconst defaultCompletionPrompt = `Provide a concise, 5-word-or-less title for the conversation, using title case conventions. Only return the title itself.\n\nConversation:\n{convo}`;\n\nexport const createCompletionTitleRunnable = async (\n model: t.ChatModelInstance,\n titlePrompt?: string\n): Promise<Runnable> => {\n const completionPrompt = ChatPromptTemplate.fromTemplate(\n titlePrompt ?? defaultCompletionPrompt\n ).withConfig({ runName: 'CompletionTitlePrompt' });\n\n /** Runnable to extract content from model response */\n const extractContent = new RunnableLambda({\n func: (response: AIMessage): { title: string } => {\n let content = '';\n if (typeof response.content === 'string') {\n content = response.content;\n } else if (Array.isArray(response.content)) {\n content = response.content\n .filter(\n (part): part is { type: ContentTypes.TEXT; text: string } =>\n part.type === ContentTypes.TEXT\n )\n .map((part) => part.text)\n .join('');\n }\n return { title: content.trim() };\n },\n }).withConfig({ runName: 'ExtractTitle' });\n\n const innerChain = RunnableSequence.from([\n completionPrompt,\n model,\n extractContent,\n ]);\n\n /** Wrap in RunnableLambda to create a parent span for LangFuse */\n return new RunnableLambda({\n func: async (\n input: { convo: string },\n config?: Partial<RunnableConfig>\n ): Promise<{ title: string }> => {\n return await innerChain.invoke(input, config);\n },\n }).withConfig({ runName: 'CompletionTitleChain' });\n};\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"title.mjs","sources":["../../../src/utils/title.ts"],"sourcesContent":["import { ChatPromptTemplate } from '@langchain/core/prompts';\nimport { RunnableLambda, RunnableSequence } from '@langchain/core/runnables';\nimport type { Runnable, RunnableConfig } from '@langchain/core/runnables';\nimport type { AIMessage } from '@langchain/core/messages';\nimport type * as t from '@/types';\nimport { ContentTypes } from '@/common';\n\nconst defaultTitlePrompt = `Analyze this conversation and provide:\n1. The detected language of the conversation\n2. A concise title in the detected language (5 words or less, no punctuation or quotation)\n\n{convo}`;\n\nconst titleSchema = {\n type: 'object',\n properties: {\n title: {\n type: 'string',\n description:\n 'A concise title for the conversation in 5 words or less, without punctuation or quotation',\n },\n },\n required: ['title'],\n} as const;\n\nconst combinedSchema = {\n type: 'object',\n properties: {\n language: {\n type: 'string',\n description: 'The detected language of the conversation',\n },\n title: {\n type: 'string',\n description:\n 'A concise title for the conversation in 5 words or less, without punctuation or quotation',\n },\n },\n required: ['language', 'title'],\n} as const;\n\nexport const createTitleRunnable = async (\n model: t.ChatModelInstance,\n _titlePrompt?: string\n): Promise<Runnable> => {\n // Disabled since this works fine\n // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n /* @ts-ignore */\n const titleLLM = model.withStructuredOutput(titleSchema);\n // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n /* @ts-ignore */\n const combinedLLM = model.withStructuredOutput(combinedSchema);\n\n const titlePrompt = ChatPromptTemplate.fromTemplate(\n _titlePrompt ?? defaultTitlePrompt\n ).withConfig({ runName: 'TitlePrompt' });\n\n const titleOnlyInnerChain = RunnableSequence.from([titlePrompt, titleLLM]);\n const combinedInnerChain = RunnableSequence.from([titlePrompt, combinedLLM]);\n\n /** Wrap titleOnlyChain in RunnableLambda to create parent span */\n const titleOnlyChain = new RunnableLambda({\n func: async (\n input: { convo: string },\n config?: Partial<RunnableConfig>\n ): Promise<{ title: string }> => {\n const result = await titleOnlyInnerChain.invoke(input, config);\n return result as { title: string };\n },\n }).withConfig({ runName: 'TitleOnlyChain' });\n\n /** Wrap combinedChain in RunnableLambda to create parent span */\n const combinedChain = new RunnableLambda({\n func: async (\n input: { convo: string },\n config?: Partial<RunnableConfig>\n ): Promise<{ language: string; title: string }> => {\n const result = await combinedInnerChain.invoke(input, config);\n return result as { language: string; title: string };\n },\n }).withConfig({ runName: 'TitleLanguageChain' });\n\n /** Runnable to add default values if needed */\n const addDefaults = new RunnableLambda({\n func: (\n result: { language: string; title: string } | undefined\n ): { language: string; title: string } => ({\n language: result?.language ?? 'English',\n title: result?.title ?? '',\n }),\n }).withConfig({ runName: 'AddDefaults' });\n\n const combinedChainInner = RunnableSequence.from([\n combinedChain,\n addDefaults,\n ]);\n\n /** Wrap combinedChainWithDefaults in RunnableLambda to create parent span */\n const combinedChainWithDefaults = new RunnableLambda({\n func: async (\n input: { convo: string },\n config?: Partial<RunnableConfig>\n ): Promise<{ language: string; title: string }> => {\n return await combinedChainInner.invoke(input, config);\n },\n }).withConfig({ runName: 'CombinedChainWithDefaults' });\n\n return new RunnableLambda({\n func: async (\n input: {\n convo: string;\n inputText: string;\n skipLanguage: boolean;\n },\n config?: Partial<RunnableConfig>\n ): Promise<{ language: string; title: string } | { title: string }> => {\n const invokeInput = { convo: input.convo };\n\n if (input.skipLanguage) {\n return (await titleOnlyChain.invoke(invokeInput, config)) as {\n title: string;\n };\n }\n\n return await combinedChainWithDefaults.invoke(invokeInput, config);\n },\n }).withConfig({ runName: 'TitleGenerator' });\n};\n\nconst defaultCompletionPrompt = `Provide a concise, 5-word-or-less title for the conversation, using title case conventions. Only return the title itself.\n\nConversation:\n{convo}`;\n\nexport const createCompletionTitleRunnable = async (\n model: t.ChatModelInstance,\n titlePrompt?: string\n): Promise<Runnable> => {\n const completionPrompt = ChatPromptTemplate.fromTemplate(\n titlePrompt ?? defaultCompletionPrompt\n ).withConfig({ runName: 'CompletionTitlePrompt' });\n\n /** Runnable to extract content from model response */\n const extractContent = new RunnableLambda({\n func: (response: AIMessage): { title: string } => {\n let content = '';\n if (typeof response.content === 'string') {\n content = response.content;\n } else if (Array.isArray(response.content)) {\n content = response.content\n .filter(\n (part): part is { type: ContentTypes.TEXT; text: string } =>\n part.type === ContentTypes.TEXT\n )\n .map((part) => part.text)\n .join('');\n }\n return { title: content.trim() };\n },\n }).withConfig({ runName: 'ExtractTitle' });\n\n const innerChain = RunnableSequence.from([\n completionPrompt,\n model,\n extractContent,\n ]);\n\n /** Wrap in RunnableLambda to create a parent span for LangFuse */\n return new RunnableLambda({\n func: async (\n input: { convo: string },\n config?: Partial<RunnableConfig>\n ): Promise<{ title: string }> => {\n return await innerChain.invoke(input, config);\n },\n }).withConfig({ runName: 'CompletionTitleChain' });\n};\n"],"names":[],"mappings":"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"}
@@ -1,4 +1,5 @@
1
1
  import { FinishReasons } from '../common/enum.mjs';
2
+ import '../tools/approval/constants.mjs';
2
3
 
3
4
  /**
4
5
  * toolCallContinuation — Utilities for detecting and classifying LLM finish
@@ -1 +1 @@
1
- {"version":3,"file":"toolCallContinuation.mjs","sources":["../../../src/utils/toolCallContinuation.ts"],"sourcesContent":["/**\n * toolCallContinuation — Utilities for detecting and classifying LLM finish\n * reasons across providers. Used to detect max_tokens truncation and normalize\n * stop reason extraction from provider-specific metadata formats.\n *\n * @module\n */\n\nimport type { AIMessageChunk } from '@langchain/core/messages';\nimport { FinishReasons } from '@/common';\n\n/**\n * Extract the finish/stop reason from an AIMessageChunk's response_metadata.\n * Centralizes provider-specific metadata access patterns.\n *\n * Provider formats:\n * - OpenAI/Azure: `response_metadata.finish_reason`\n * - Anthropic direct: `response_metadata.stop_reason`\n * - Bedrock invoke: `response_metadata.stopReason`\n * - Bedrock streaming: `response_metadata.messageStop.stopReason`\n * - VertexAI/Google: `response_metadata.finishReason`\n *\n * @param chunk - The final accumulated AIMessageChunk\n * @returns The finish reason string, or undefined if not found\n */\nexport function extractFinishReason(\n chunk: AIMessageChunk | undefined\n): string | undefined {\n if (!chunk || !('response_metadata' in chunk)) return undefined;\n\n const meta = chunk.response_metadata as Record<string, unknown>;\n if (!meta) return undefined;\n\n // Bedrock streaming nests stopReason inside messageStop: { stopReason: '...' }\n const messageStop = meta.messageStop as Record<string, unknown> | undefined;\n\n return (\n (meta.finish_reason as string | undefined) ?? // OpenAI/Azure\n (meta.stop_reason as string | undefined) ?? // Anthropic direct API\n (meta.stopReason as string | undefined) ?? // Bedrock invoke (non-streaming)\n (messageStop?.stopReason as string | undefined) ?? // Bedrock streaming\n (meta.finishReason as string | undefined) // VertexAI/Google\n );\n}\n\n/**\n * Check if a finish reason indicates the response was truncated by token limits.\n *\n * @param reason - The finish reason from extractFinishReason()\n * @returns true if the response was cut short by max_tokens\n */\nexport function isMaxTokensFinish(reason: string | undefined): boolean {\n if (!reason) return false;\n return reason === FinishReasons.MAX_TOKENS || reason === FinishReasons.LENGTH;\n}\n"],"names":[],"mappings":";;AAAA;;;;;;AAMG;AAKH;;;;;;;;;;;;;AAaG;AACG,SAAU,mBAAmB,CACjC,KAAiC,EAAA;IAEjC,IAAI,CAAC,KAAK,IAAI,EAAE,mBAAmB,IAAI,KAAK,CAAC;AAAE,QAAA,OAAO,SAAS;AAE/D,IAAA,MAAM,IAAI,GAAG,KAAK,CAAC,iBAA4C;AAC/D,IAAA,IAAI,CAAC,IAAI;AAAE,QAAA,OAAO,SAAS;;AAG3B,IAAA,MAAM,WAAW,GAAG,IAAI,CAAC,WAAkD;AAE3E,IAAA,QACG,IAAI,CAAC,aAAoC;QACzC,IAAI,CAAC,WAAkC;QACvC,IAAI,CAAC,UAAiC;QACtC,WAAW,EAAE,UAAiC;QAC9C,IAAI,CAAC,YAAmC;;AAE7C;AAEA;;;;;AAKG;AACG,SAAU,iBAAiB,CAAC,MAA0B,EAAA;AAC1D,IAAA,IAAI,CAAC,MAAM;AAAE,QAAA,OAAO,KAAK;IACzB,OAAO,MAAM,KAAK,aAAa,CAAC,UAAU,IAAI,MAAM,KAAK,aAAa,CAAC,MAAM;AAC/E;;;;"}
1
+ {"version":3,"file":"toolCallContinuation.mjs","sources":["../../../src/utils/toolCallContinuation.ts"],"sourcesContent":["/**\n * toolCallContinuation — Utilities for detecting and classifying LLM finish\n * reasons across providers. Used to detect max_tokens truncation and normalize\n * stop reason extraction from provider-specific metadata formats.\n *\n * @module\n */\n\nimport type { AIMessageChunk } from '@langchain/core/messages';\nimport { FinishReasons } from '@/common';\n\n/**\n * Extract the finish/stop reason from an AIMessageChunk's response_metadata.\n * Centralizes provider-specific metadata access patterns.\n *\n * Provider formats:\n * - OpenAI/Azure: `response_metadata.finish_reason`\n * - Anthropic direct: `response_metadata.stop_reason`\n * - Bedrock invoke: `response_metadata.stopReason`\n * - Bedrock streaming: `response_metadata.messageStop.stopReason`\n * - VertexAI/Google: `response_metadata.finishReason`\n *\n * @param chunk - The final accumulated AIMessageChunk\n * @returns The finish reason string, or undefined if not found\n */\nexport function extractFinishReason(\n chunk: AIMessageChunk | undefined\n): string | undefined {\n if (!chunk || !('response_metadata' in chunk)) return undefined;\n\n const meta = chunk.response_metadata as Record<string, unknown>;\n if (!meta) return undefined;\n\n // Bedrock streaming nests stopReason inside messageStop: { stopReason: '...' }\n const messageStop = meta.messageStop as Record<string, unknown> | undefined;\n\n return (\n (meta.finish_reason as string | undefined) ?? // OpenAI/Azure\n (meta.stop_reason as string | undefined) ?? // Anthropic direct API\n (meta.stopReason as string | undefined) ?? // Bedrock invoke (non-streaming)\n (messageStop?.stopReason as string | undefined) ?? // Bedrock streaming\n (meta.finishReason as string | undefined) // VertexAI/Google\n );\n}\n\n/**\n * Check if a finish reason indicates the response was truncated by token limits.\n *\n * @param reason - The finish reason from extractFinishReason()\n * @returns true if the response was cut short by max_tokens\n */\nexport function isMaxTokensFinish(reason: string | undefined): boolean {\n if (!reason) return false;\n return reason === FinishReasons.MAX_TOKENS || reason === FinishReasons.LENGTH;\n}\n"],"names":[],"mappings":";;;AAAA;;;;;;AAMG;AAKH;;;;;;;;;;;;;AAaG;AACG,SAAU,mBAAmB,CACjC,KAAiC,EAAA;IAEjC,IAAI,CAAC,KAAK,IAAI,EAAE,mBAAmB,IAAI,KAAK,CAAC;AAAE,QAAA,OAAO,SAAS;AAE/D,IAAA,MAAM,IAAI,GAAG,KAAK,CAAC,iBAA4C;AAC/D,IAAA,IAAI,CAAC,IAAI;AAAE,QAAA,OAAO,SAAS;;AAG3B,IAAA,MAAM,WAAW,GAAG,IAAI,CAAC,WAAkD;AAE3E,IAAA,QACG,IAAI,CAAC,aAAoC;QACzC,IAAI,CAAC,WAAkC;QACvC,IAAI,CAAC,UAAiC;QACtC,WAAW,EAAE,UAAiC;QAC9C,IAAI,CAAC,YAAmC;;AAE7C;AAEA;;;;;AAKG;AACG,SAAU,iBAAiB,CAAC,MAA0B,EAAA;AAC1D,IAAA,IAAI,CAAC,MAAM;AAAE,QAAA,OAAO,KAAK;IACzB,OAAO,MAAM,KAAK,aAAa,CAAC,UAAU,IAAI,MAAM,KAAK,aAAa,CAAC,MAAM;AAC/E;;;;"}
@@ -1,5 +1,6 @@
1
1
  import { MessageTypes, Constants } from '../common/enum.mjs';
2
2
  import { TOOL_DISCOVERY_CACHE_MAX_SIZE } from '../common/constants.mjs';
3
+ import '../tools/approval/constants.mjs';
3
4
 
4
5
  /**
5
6
  * ToolDiscoveryCache provides a run-scoped cache of tool search results.
@@ -1 +1 @@
1
- {"version":3,"file":"toolDiscoveryCache.mjs","sources":["../../../src/utils/toolDiscoveryCache.ts"],"sourcesContent":["// src/utils/toolDiscoveryCache.ts\nimport type { BaseMessage } from '@langchain/core/messages';\nimport { Constants, MessageTypes } from '@/common';\nimport { TOOL_DISCOVERY_CACHE_MAX_SIZE } from '@/common/constants';\n\n/**\n * Cached tool discovery entry.\n * Stores the tool name and the message index where it was discovered,\n * enabling efficient lookups without re-parsing conversation history.\n */\nexport interface ToolDiscoveryEntry {\n /** The tool name that was discovered */\n toolName: string;\n /** Message index in conversation history where discovery occurred */\n discoveredAtIndex: number;\n}\n\n/**\n * ToolDiscoveryCache provides a run-scoped cache of tool search results.\n *\n * Problem: Without caching, every LLM iteration re-parses the full message\n * history via extractToolDiscoveries() to find tool_search results. In long\n * conversations with many tool iterations, this is redundant work.\n *\n * Solution: Cache discovered tool names by message index. On each iteration,\n * only scan messages AFTER the last scanned index. Already-seen discoveries\n * are returned from cache instantly.\n *\n * This mirrors the pattern used by VS Code Copilot Chat where tool search\n * results from prior turns are cached to avoid re-discovery.\n *\n * @example\n * ```ts\n * const cache = new ToolDiscoveryCache();\n *\n * // First call: scans all messages\n * const newTools = cache.getNewDiscoveries(messages);\n * // Returns: ['web_search', 'file_read']\n *\n * // Second call (3 new messages added): only scans new messages\n * const moreTools = cache.getNewDiscoveries(messages);\n * // Returns: ['code_exec'] (only newly discovered)\n * ```\n */\nexport class ToolDiscoveryCache {\n /** Set of all discovered tool names (deduped) */\n private _discoveredTools: Set<string> = new Set();\n /** Last message index that was scanned */\n private _lastScannedIndex: number = -1;\n\n /**\n * Scan messages for new tool_search results since the last scan.\n * Only processes messages after `_lastScannedIndex` to avoid redundant work.\n *\n * @param messages - Full conversation message array\n * @returns Array of newly discovered tool names (not previously cached)\n */\n getNewDiscoveries(messages: BaseMessage[]): string[] {\n if (messages.length === 0) {\n return [];\n }\n\n const startIndex = this._lastScannedIndex + 1;\n if (startIndex >= messages.length) {\n return [];\n }\n\n const newDiscoveries: string[] = [];\n\n for (let i = startIndex; i < messages.length; i++) {\n const msg = messages[i];\n if (msg.getType() !== MessageTypes.TOOL) {\n continue;\n }\n\n // Check if this is a tool_search result\n if ((msg as { name?: string }).name !== Constants.TOOL_SEARCH) {\n continue;\n }\n\n // Extract tool references from artifact\n const artifact = (msg as { artifact?: unknown }).artifact;\n if (typeof artifact === 'object' && artifact != null) {\n const refs = (\n artifact as { tool_references?: Array<{ tool_name: string }> }\n ).tool_references;\n if (refs && refs.length > 0) {\n for (const ref of refs) {\n if (!this._discoveredTools.has(ref.tool_name)) {\n // Enforce cache size limit\n if (this._discoveredTools.size >= TOOL_DISCOVERY_CACHE_MAX_SIZE) {\n break;\n }\n this._discoveredTools.add(ref.tool_name);\n newDiscoveries.push(ref.tool_name);\n }\n }\n }\n }\n }\n\n this._lastScannedIndex = messages.length - 1;\n return newDiscoveries;\n }\n\n /**\n * Returns all tool names discovered so far (across all scans).\n */\n getAllDiscoveredTools(): string[] {\n return [...this._discoveredTools];\n }\n\n /**\n * Check if a specific tool has been discovered.\n */\n has(toolName: string): boolean {\n return this._discoveredTools.has(toolName);\n }\n\n /**\n * Number of unique tools discovered.\n */\n get size(): number {\n return this._discoveredTools.size;\n }\n\n /**\n * Reset the cache (e.g., on graph reset).\n */\n reset(): void {\n this._discoveredTools.clear();\n this._lastScannedIndex = -1;\n }\n\n /**\n * Seed the cache with previously known tool names (e.g., from prior conversation turns).\n * Does not affect _lastScannedIndex — the next getNewDiscoveries call will still\n * scan all messages from the beginning.\n *\n * @param toolNames - Tool names to pre-seed into the cache\n */\n seed(toolNames: string[]): void {\n for (const name of toolNames) {\n if (this._discoveredTools.size >= TOOL_DISCOVERY_CACHE_MAX_SIZE) {\n break;\n }\n this._discoveredTools.add(name);\n }\n }\n}\n"],"names":[],"mappings":";;;AAiBA;;;;;;;;;;;;;;;;;;;;;;;;;;AA0BG;MACU,kBAAkB,CAAA;;AAErB,IAAA,gBAAgB,GAAgB,IAAI,GAAG,EAAE;;IAEzC,iBAAiB,GAAW,EAAE;AAEtC;;;;;;AAMG;AACH,IAAA,iBAAiB,CAAC,QAAuB,EAAA;AACvC,QAAA,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE;AACzB,YAAA,OAAO,EAAE;QACX;AAEA,QAAA,MAAM,UAAU,GAAG,IAAI,CAAC,iBAAiB,GAAG,CAAC;AAC7C,QAAA,IAAI,UAAU,IAAI,QAAQ,CAAC,MAAM,EAAE;AACjC,YAAA,OAAO,EAAE;QACX;QAEA,MAAM,cAAc,GAAa,EAAE;AAEnC,QAAA,KAAK,IAAI,CAAC,GAAG,UAAU,EAAE,CAAC,GAAG,QAAQ,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE;AACjD,YAAA,MAAM,GAAG,GAAG,QAAQ,CAAC,CAAC,CAAC;YACvB,IAAI,GAAG,CAAC,OAAO,EAAE,KAAK,YAAY,CAAC,IAAI,EAAE;gBACvC;YACF;;YAGA,IAAK,GAAyB,CAAC,IAAI,KAAK,SAAS,CAAC,WAAW,EAAE;gBAC7D;YACF;;AAGA,YAAA,MAAM,QAAQ,GAAI,GAA8B,CAAC,QAAQ;YACzD,IAAI,OAAO,QAAQ,KAAK,QAAQ,IAAI,QAAQ,IAAI,IAAI,EAAE;AACpD,gBAAA,MAAM,IAAI,GACR,QACD,CAAC,eAAe;gBACjB,IAAI,IAAI,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE;AAC3B,oBAAA,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE;AACtB,wBAAA,IAAI,CAAC,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE;;4BAE7C,IAAI,IAAI,CAAC,gBAAgB,CAAC,IAAI,IAAI,6BAA6B,EAAE;gCAC/D;4BACF;4BACA,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,GAAG,CAAC,SAAS,CAAC;AACxC,4BAAA,cAAc,CAAC,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC;wBACpC;oBACF;gBACF;YACF;QACF;QAEA,IAAI,CAAC,iBAAiB,GAAG,QAAQ,CAAC,MAAM,GAAG,CAAC;AAC5C,QAAA,OAAO,cAAc;IACvB;AAEA;;AAEG;IACH,qBAAqB,GAAA;AACnB,QAAA,OAAO,CAAC,GAAG,IAAI,CAAC,gBAAgB,CAAC;IACnC;AAEA;;AAEG;AACH,IAAA,GAAG,CAAC,QAAgB,EAAA;QAClB,OAAO,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,QAAQ,CAAC;IAC5C;AAEA;;AAEG;AACH,IAAA,IAAI,IAAI,GAAA;AACN,QAAA,OAAO,IAAI,CAAC,gBAAgB,CAAC,IAAI;IACnC;AAEA;;AAEG;IACH,KAAK,GAAA;AACH,QAAA,IAAI,CAAC,gBAAgB,CAAC,KAAK,EAAE;AAC7B,QAAA,IAAI,CAAC,iBAAiB,GAAG,EAAE;IAC7B;AAEA;;;;;;AAMG;AACH,IAAA,IAAI,CAAC,SAAmB,EAAA;AACtB,QAAA,KAAK,MAAM,IAAI,IAAI,SAAS,EAAE;YAC5B,IAAI,IAAI,CAAC,gBAAgB,CAAC,IAAI,IAAI,6BAA6B,EAAE;gBAC/D;YACF;AACA,YAAA,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,IAAI,CAAC;QACjC;IACF;AACD;;;;"}
1
+ {"version":3,"file":"toolDiscoveryCache.mjs","sources":["../../../src/utils/toolDiscoveryCache.ts"],"sourcesContent":["// src/utils/toolDiscoveryCache.ts\nimport type { BaseMessage } from '@langchain/core/messages';\nimport { Constants, MessageTypes } from '@/common';\nimport { TOOL_DISCOVERY_CACHE_MAX_SIZE } from '@/common/constants';\n\n/**\n * Cached tool discovery entry.\n * Stores the tool name and the message index where it was discovered,\n * enabling efficient lookups without re-parsing conversation history.\n */\nexport interface ToolDiscoveryEntry {\n /** The tool name that was discovered */\n toolName: string;\n /** Message index in conversation history where discovery occurred */\n discoveredAtIndex: number;\n}\n\n/**\n * ToolDiscoveryCache provides a run-scoped cache of tool search results.\n *\n * Problem: Without caching, every LLM iteration re-parses the full message\n * history via extractToolDiscoveries() to find tool_search results. In long\n * conversations with many tool iterations, this is redundant work.\n *\n * Solution: Cache discovered tool names by message index. On each iteration,\n * only scan messages AFTER the last scanned index. Already-seen discoveries\n * are returned from cache instantly.\n *\n * This mirrors the pattern used by VS Code Copilot Chat where tool search\n * results from prior turns are cached to avoid re-discovery.\n *\n * @example\n * ```ts\n * const cache = new ToolDiscoveryCache();\n *\n * // First call: scans all messages\n * const newTools = cache.getNewDiscoveries(messages);\n * // Returns: ['web_search', 'file_read']\n *\n * // Second call (3 new messages added): only scans new messages\n * const moreTools = cache.getNewDiscoveries(messages);\n * // Returns: ['code_exec'] (only newly discovered)\n * ```\n */\nexport class ToolDiscoveryCache {\n /** Set of all discovered tool names (deduped) */\n private _discoveredTools: Set<string> = new Set();\n /** Last message index that was scanned */\n private _lastScannedIndex: number = -1;\n\n /**\n * Scan messages for new tool_search results since the last scan.\n * Only processes messages after `_lastScannedIndex` to avoid redundant work.\n *\n * @param messages - Full conversation message array\n * @returns Array of newly discovered tool names (not previously cached)\n */\n getNewDiscoveries(messages: BaseMessage[]): string[] {\n if (messages.length === 0) {\n return [];\n }\n\n const startIndex = this._lastScannedIndex + 1;\n if (startIndex >= messages.length) {\n return [];\n }\n\n const newDiscoveries: string[] = [];\n\n for (let i = startIndex; i < messages.length; i++) {\n const msg = messages[i];\n if (msg.getType() !== MessageTypes.TOOL) {\n continue;\n }\n\n // Check if this is a tool_search result\n if ((msg as { name?: string }).name !== Constants.TOOL_SEARCH) {\n continue;\n }\n\n // Extract tool references from artifact\n const artifact = (msg as { artifact?: unknown }).artifact;\n if (typeof artifact === 'object' && artifact != null) {\n const refs = (\n artifact as { tool_references?: Array<{ tool_name: string }> }\n ).tool_references;\n if (refs && refs.length > 0) {\n for (const ref of refs) {\n if (!this._discoveredTools.has(ref.tool_name)) {\n // Enforce cache size limit\n if (this._discoveredTools.size >= TOOL_DISCOVERY_CACHE_MAX_SIZE) {\n break;\n }\n this._discoveredTools.add(ref.tool_name);\n newDiscoveries.push(ref.tool_name);\n }\n }\n }\n }\n }\n\n this._lastScannedIndex = messages.length - 1;\n return newDiscoveries;\n }\n\n /**\n * Returns all tool names discovered so far (across all scans).\n */\n getAllDiscoveredTools(): string[] {\n return [...this._discoveredTools];\n }\n\n /**\n * Check if a specific tool has been discovered.\n */\n has(toolName: string): boolean {\n return this._discoveredTools.has(toolName);\n }\n\n /**\n * Number of unique tools discovered.\n */\n get size(): number {\n return this._discoveredTools.size;\n }\n\n /**\n * Reset the cache (e.g., on graph reset).\n */\n reset(): void {\n this._discoveredTools.clear();\n this._lastScannedIndex = -1;\n }\n\n /**\n * Seed the cache with previously known tool names (e.g., from prior conversation turns).\n * Does not affect _lastScannedIndex — the next getNewDiscoveries call will still\n * scan all messages from the beginning.\n *\n * @param toolNames - Tool names to pre-seed into the cache\n */\n seed(toolNames: string[]): void {\n for (const name of toolNames) {\n if (this._discoveredTools.size >= TOOL_DISCOVERY_CACHE_MAX_SIZE) {\n break;\n }\n this._discoveredTools.add(name);\n }\n }\n}\n"],"names":[],"mappings":";;;;AAiBA;;;;;;;;;;;;;;;;;;;;;;;;;;AA0BG;MACU,kBAAkB,CAAA;;AAErB,IAAA,gBAAgB,GAAgB,IAAI,GAAG,EAAE;;IAEzC,iBAAiB,GAAW,EAAE;AAEtC;;;;;;AAMG;AACH,IAAA,iBAAiB,CAAC,QAAuB,EAAA;AACvC,QAAA,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE;AACzB,YAAA,OAAO,EAAE;QACX;AAEA,QAAA,MAAM,UAAU,GAAG,IAAI,CAAC,iBAAiB,GAAG,CAAC;AAC7C,QAAA,IAAI,UAAU,IAAI,QAAQ,CAAC,MAAM,EAAE;AACjC,YAAA,OAAO,EAAE;QACX;QAEA,MAAM,cAAc,GAAa,EAAE;AAEnC,QAAA,KAAK,IAAI,CAAC,GAAG,UAAU,EAAE,CAAC,GAAG,QAAQ,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE;AACjD,YAAA,MAAM,GAAG,GAAG,QAAQ,CAAC,CAAC,CAAC;YACvB,IAAI,GAAG,CAAC,OAAO,EAAE,KAAK,YAAY,CAAC,IAAI,EAAE;gBACvC;YACF;;YAGA,IAAK,GAAyB,CAAC,IAAI,KAAK,SAAS,CAAC,WAAW,EAAE;gBAC7D;YACF;;AAGA,YAAA,MAAM,QAAQ,GAAI,GAA8B,CAAC,QAAQ;YACzD,IAAI,OAAO,QAAQ,KAAK,QAAQ,IAAI,QAAQ,IAAI,IAAI,EAAE;AACpD,gBAAA,MAAM,IAAI,GACR,QACD,CAAC,eAAe;gBACjB,IAAI,IAAI,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE;AAC3B,oBAAA,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE;AACtB,wBAAA,IAAI,CAAC,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE;;4BAE7C,IAAI,IAAI,CAAC,gBAAgB,CAAC,IAAI,IAAI,6BAA6B,EAAE;gCAC/D;4BACF;4BACA,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,GAAG,CAAC,SAAS,CAAC;AACxC,4BAAA,cAAc,CAAC,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC;wBACpC;oBACF;gBACF;YACF;QACF;QAEA,IAAI,CAAC,iBAAiB,GAAG,QAAQ,CAAC,MAAM,GAAG,CAAC;AAC5C,QAAA,OAAO,cAAc;IACvB;AAEA;;AAEG;IACH,qBAAqB,GAAA;AACnB,QAAA,OAAO,CAAC,GAAG,IAAI,CAAC,gBAAgB,CAAC;IACnC;AAEA;;AAEG;AACH,IAAA,GAAG,CAAC,QAAgB,EAAA;QAClB,OAAO,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,QAAQ,CAAC;IAC5C;AAEA;;AAEG;AACH,IAAA,IAAI,IAAI,GAAA;AACN,QAAA,OAAO,IAAI,CAAC,gBAAgB,CAAC,IAAI;IACnC;AAEA;;AAEG;IACH,KAAK,GAAA;AACH,QAAA,IAAI,CAAC,gBAAgB,CAAC,KAAK,EAAE;AAC7B,QAAA,IAAI,CAAC,iBAAiB,GAAG,EAAE;IAC7B;AAEA;;;;;;AAMG;AACH,IAAA,IAAI,CAAC,SAAmB,EAAA;AACtB,QAAA,KAAK,MAAM,IAAI,IAAI,SAAS,EAAE;YAC5B,IAAI,IAAI,CAAC,gBAAgB,CAAC,IAAI,IAAI,6BAA6B,EAAE;gBAC/D;YACF;AACA,YAAA,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,IAAI,CAAC;QACjC;IACF;AACD;;;;"}
@@ -1,2 +1,3 @@
1
1
  export * from './enum';
2
2
  export * from './constants';
3
+ export * from '../tools/approval/constants';
@@ -0,0 +1,79 @@
1
+ /**
2
+ * Shared HITL (Human-in-the-Loop) constants and enums.
3
+ *
4
+ * Single source of truth for approval-related values consumed by
5
+ * the agents library, ranger backend, and browser extension.
6
+ *
7
+ * @module approval/constants
8
+ */
9
+ /**
10
+ * Execution context determines whether HITL approval is required.
11
+ *
12
+ * - `INTERACTIVE`: User is present in the chat session — approval prompts are shown.
13
+ * - `SCHEDULED`: Automated/scheduled execution — all approvals are auto-granted.
14
+ */
15
+ export declare enum ExecutionContext {
16
+ INTERACTIVE = "interactive",
17
+ SCHEDULED = "scheduled"
18
+ }
19
+ /**
20
+ * Static approval policy values.
21
+ *
22
+ * - `ALWAYS`: Every tool call requires approval (strictest).
23
+ * - `NEVER`: No tool call requires approval (used for auto-approved tools).
24
+ */
25
+ export declare enum ApprovalPolicy {
26
+ ALWAYS = "always",
27
+ NEVER = "never"
28
+ }
29
+ /**
30
+ * Three-tier policy evaluation pipeline.
31
+ * Higher tiers are checked first; once a tier matches, lower tiers are skipped.
32
+ *
33
+ * - `AUTO_APPROVE`: Built-in tools that always skip approval (Tier 1).
34
+ * - `RULE_BASED`: Per-tool argument-aware rules (Tier 2).
35
+ * - `KEYWORD_FALLBACK`: Token-based keyword matching on tool name (Tier 3).
36
+ */
37
+ export declare enum ApprovalTier {
38
+ AUTO_APPROVE = "auto_approve",
39
+ RULE_BASED = "rule_based",
40
+ KEYWORD_FALLBACK = "keyword_fallback"
41
+ }
42
+ /**
43
+ * Risk classification for tool calls.
44
+ * Used for audit logging and future UI customization (e.g., showing risk badges).
45
+ *
46
+ * - `NONE`: Read-only, no side effects (search, list, get).
47
+ * - `LOW`: Minor side effects (bookmark, star, mark as read).
48
+ * - `MEDIUM`: User-specific mutations (send email to known contact).
49
+ * - `HIGH`: Destructive or irreversible (delete, modify critical data).
50
+ * - `CRITICAL`: Multi-system impact (transfer funds, delete account).
51
+ */
52
+ export declare enum RiskLevel {
53
+ NONE = "none",
54
+ LOW = "low",
55
+ MEDIUM = "medium",
56
+ HIGH = "high",
57
+ CRITICAL = "critical"
58
+ }
59
+ /**
60
+ * Semantic category for tool actions.
61
+ * Maps action keywords to high-level intent for risk classification.
62
+ */
63
+ export declare enum ActionCategory {
64
+ READ = "read",
65
+ WRITE = "write",
66
+ DELETE = "delete",
67
+ SEND = "send",
68
+ EXECUTE = "execute",
69
+ FINANCIAL = "financial",
70
+ ACCOUNT = "account"
71
+ }
72
+ /**
73
+ * Delimiter used in MCP tool names to separate the tool name from the server key.
74
+ *
75
+ * Example: `send_email_mcp_outlook` → tool = `send_email`, server = `outlook`
76
+ *
77
+ * Must match `Constants.mcp_delimiter` in `@ranger/data-provider`.
78
+ */
79
+ export declare const MCP_DELIMITER = "_mcp_";
@@ -181,8 +181,11 @@ export type ProgrammaticCache = {
181
181
  * Execution context determines whether HITL approval is required.
182
182
  * - 'interactive': User is present in the chat, approval prompts are shown.
183
183
  * - 'scheduled': Automated/scheduled execution, approval is auto-granted.
184
+ *
185
+ * Re-exported from approval/constants for convenience.
186
+ * @see ExecutionContext enum in approval/constants.ts
184
187
  */
185
- export type ExecutionContext = 'interactive' | 'scheduled' | 'browser';
188
+ export type ExecutionContext = `${import('../tools/approval/constants').ExecutionContext}`;
186
189
  /**
187
190
  * Policy for when a tool requires human approval.
188
191
  * - 'always': Always require approval (default for interactive context).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@illuma-ai/agents",
3
- "version": "1.1.5",
3
+ "version": "1.1.7",
4
4
  "main": "./dist/cjs/main.cjs",
5
5
  "module": "./dist/esm/main.mjs",
6
6
  "types": "./dist/types/index.d.ts",
@@ -1,3 +1,4 @@
1
1
  // src/common/index.ts
2
2
  export * from './enum';
3
3
  export * from './constants';
4
+ export * from '../tools/approval/constants';
@@ -69,8 +69,8 @@ import {
69
69
  updatePruneCalibration,
70
70
  applyCalibration,
71
71
  } from '@/utils';
72
- import type { PruneCalibrationState, FileManifestEntry } from '@/types/graph';
73
- import { buildFileManifestBlock, FILE_MANIFEST_PREFIX } from '@/utils/fileManifest';
72
+ import type { PruneCalibrationState } from '@/types/graph';
73
+ import { buildFileManifestBlock } from '@/utils/fileManifest';
74
74
  import {
75
75
  buildContextAnalytics,
76
76
  type ContextAnalytics,
@@ -1566,13 +1566,18 @@ export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
1566
1566
  agentContext.instructionTokens,
1567
1567
  agentContext.maxContextTokens
1568
1568
  );
1569
- const threshold = (agentContext.summarizationConfig?.triggerThreshold ?? PROACTIVE_SUMMARY_THRESHOLD * 100);
1569
+ const threshold =
1570
+ agentContext.summarizationConfig?.triggerThreshold ??
1571
+ PROACTIVE_SUMMARY_THRESHOLD * 100;
1570
1572
 
1571
1573
  if (utilization >= threshold) {
1572
1574
  // Identify older messages to summarize proactively.
1573
1575
  // Keep the last N messages (recent turns) intact — only summarize older history.
1574
1576
  // This is incremental: the callback checks for existing summary and updates it.
1575
- const recentTurnCount = Math.max(4, Math.floor(messages.length * 0.3));
1577
+ const recentTurnCount = Math.max(
1578
+ 4,
1579
+ Math.floor(messages.length * 0.3)
1580
+ );
1576
1581
  const oldMessages = messages.slice(
1577
1582
  messages[0]?.getType() === 'system' ? 1 : 0,
1578
1583
  Math.max(1, messages.length - recentTurnCount)
@@ -1657,17 +1662,22 @@ export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
1657
1662
 
1658
1663
  // Step 2: Calculate token budget
1659
1664
  // Apply EMA calibration for accuracy across iterations
1660
- const calibratedMax = applyCalibration(maxTokens, this._pruneCalibration);
1661
- const systemMsg = messages[0]?.getType() === 'system' ? messages[0] : null;
1662
- const systemTokens = systemMsg != null
1663
- ? (agentContext.indexTokenCountMap[0] ?? 0)
1664
- : 0;
1665
- const summaryMsg = summary != null && summary !== ''
1666
- ? new SystemMessage(`[Conversation Summary]\n${summary}`)
1667
- : null;
1668
- const summaryTokens = summaryMsg != null && tokenCounter != null
1669
- ? tokenCounter(summaryMsg)
1670
- : 0;
1665
+ const calibratedMax = applyCalibration(
1666
+ maxTokens,
1667
+ this._pruneCalibration
1668
+ );
1669
+ const systemMsg =
1670
+ messages[0]?.getType() === 'system' ? messages[0] : null;
1671
+ const systemTokens =
1672
+ systemMsg != null ? (agentContext.indexTokenCountMap[0] ?? 0) : 0;
1673
+ const summaryMsg =
1674
+ summary != null && summary !== ''
1675
+ ? new SystemMessage(`[Conversation Summary]\n${summary}`)
1676
+ : null;
1677
+ const summaryTokens =
1678
+ summaryMsg != null && tokenCounter != null
1679
+ ? tokenCounter(summaryMsg)
1680
+ : 0;
1671
1681
 
1672
1682
  // Budget for recent messages = total - system - summary - 3 (assistant priming)
1673
1683
  const recentBudget = calibratedMax - systemTokens - summaryTokens - 3;
@@ -1752,13 +1762,18 @@ export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
1752
1762
 
1753
1763
  // Inject file manifest when files exist and messages are being compacted
1754
1764
  const fileManifest = agentContext.fileManifest;
1755
- if (fileManifest && fileManifest.length > 0 && compactedMessages.length > 0) {
1765
+ if (
1766
+ fileManifest &&
1767
+ fileManifest.length > 0 &&
1768
+ compactedMessages.length > 0
1769
+ ) {
1756
1770
  const manifestBlock = buildFileManifestBlock(fileManifest);
1757
1771
  if (manifestBlock) {
1758
1772
  const manifestMsg = new SystemMessage(manifestBlock);
1759
1773
  viewParts.push(manifestMsg);
1760
1774
  // Account for manifest tokens in the view token map
1761
- const manifestTokens = tokenCounter != null ? tokenCounter(manifestMsg) : 0;
1775
+ const manifestTokens =
1776
+ tokenCounter != null ? tokenCounter(manifestMsg) : 0;
1762
1777
  // Will be inserted at the correct index when rebuilding viewTokenMap below
1763
1778
  fileManifestTokens = manifestTokens;
1764
1779
  }
@@ -1791,18 +1806,17 @@ export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
1791
1806
 
1792
1807
  console.debug(
1793
1808
  `[Graph:Compaction] ${messages.length}→${viewParts.length} msgs | ` +
1794
- `compacted=${compactedMessages.length} window=${recentMessages.length} | ` +
1795
- `summary=${summarySource} | budget=${usedTokens}/${recentBudget}` +
1796
- (fileManifestTokens > 0 ? ` | manifest=${fileManifest?.length ?? 0} files (${fileManifestTokens}tok)` : '')
1809
+ `compacted=${compactedMessages.length} window=${recentMessages.length} | ` +
1810
+ `summary=${summarySource} | budget=${usedTokens}/${recentBudget}` +
1811
+ (fileManifestTokens > 0
1812
+ ? ` | manifest=${fileManifest?.length ?? 0} files (${fileManifestTokens}tok)`
1813
+ : '')
1797
1814
  );
1798
1815
 
1799
1816
  // Step 5: Fire background summary update (non-blocking)
1800
1817
  // Summarize messages outside the window so next iteration has a fresh summary.
1801
1818
  // Only trigger if there are compacted messages worth summarizing.
1802
- if (
1803
- compactedMessages.length > 0 &&
1804
- agentContext.summarizeCallback
1805
- ) {
1819
+ if (compactedMessages.length > 0 && agentContext.summarizeCallback) {
1806
1820
  const shouldSummarize = this.shouldTriggerSummarization(
1807
1821
  compactedMessages.length,
1808
1822
  maxTokens,
@@ -1819,9 +1833,10 @@ export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
1819
1833
  );
1820
1834
  } else {
1821
1835
  this._summaryInFlight = true;
1822
- const allMessages = this._pendingMessagesToRefine.length > 0
1823
- ? [...this._pendingMessagesToRefine, ...compactedMessages]
1824
- : compactedMessages;
1836
+ const allMessages =
1837
+ this._pendingMessagesToRefine.length > 0
1838
+ ? [...this._pendingMessagesToRefine, ...compactedMessages]
1839
+ : compactedMessages;
1825
1840
  this._pendingMessagesToRefine = [];
1826
1841
 
1827
1842
  agentContext