(input, { options: { tools }, getAppState })
| 326 | return outputSchema() |
| 327 | }, |
| 328 | async call(input, { options: { tools }, getAppState }) { |
| 329 | const { query, max_results = 5 } = input |
| 330 | |
| 331 | const deferredTools = tools.filter(isDeferredTool) |
| 332 | maybeInvalidateCache(deferredTools) |
| 333 | |
| 334 | // Check for MCP servers still connecting |
| 335 | function getPendingServerNames(): string[] | undefined { |
| 336 | const appState = getAppState() |
| 337 | const pending = appState.mcp.clients.filter(c => c.type === 'pending') |
| 338 | return pending.length > 0 ? pending.map(s => s.name) : undefined |
| 339 | } |
| 340 | |
| 341 | // Helper to log search outcome |
| 342 | function logSearchOutcome( |
| 343 | matches: string[], |
| 344 | queryType: 'select' | 'keyword', |
| 345 | ): void { |
| 346 | logEvent('tengu_tool_search_outcome', { |
| 347 | query: |
| 348 | query as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, |
| 349 | queryType: |
| 350 | queryType as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, |
| 351 | matchCount: matches.length, |
| 352 | totalDeferredTools: deferredTools.length, |
| 353 | maxResults: max_results, |
| 354 | hasMatches: matches.length > 0, |
| 355 | }) |
| 356 | } |
| 357 | |
| 358 | // Check for select: prefix — direct tool selection. |
| 359 | // Supports comma-separated multi-select: `select:A,B,C`. |
| 360 | // If a name isn't in the deferred set but IS in the full tool set, |
| 361 | // we still return it — the tool is already loaded, so "selecting" it |
| 362 | // is a harmless no-op that lets the model proceed without retry churn. |
| 363 | const selectMatch = query.match(/^select:(.+)$/i) |
| 364 | if (selectMatch) { |
| 365 | const requested = selectMatch[1]! |
| 366 | .split(',') |
| 367 | .map(s => s.trim()) |
| 368 | .filter(Boolean) |
| 369 | |
| 370 | const found: string[] = [] |
| 371 | const missing: string[] = [] |
| 372 | for (const toolName of requested) { |
| 373 | const tool = |
| 374 | findToolByName(deferredTools, toolName) ?? |
| 375 | findToolByName(tools, toolName) |
| 376 | if (tool) { |
| 377 | if (!found.includes(tool.name)) found.push(tool.name) |
| 378 | } else { |
| 379 | missing.push(toolName) |
| 380 | } |
| 381 | } |
| 382 | |
| 383 | if (found.length === 0) { |
| 384 | logForDebugging( |
| 385 | `ToolSearchTool: select failed — none found: ${missing.join(', ')}`, |
nothing calls this directly
no test coverage detected