source dump of claude code
at main 121 lines 5.2 kB view raw
1import { feature } from 'bun:bundle' 2import { isReplBridgeActive } from '../../bootstrap/state.js' 3import { getFeatureValue_CACHED_MAY_BE_STALE } from '../../services/analytics/growthbook.js' 4import type { Tool } from '../../Tool.js' 5import { AGENT_TOOL_NAME } from '../AgentTool/constants.js' 6 7// Dead code elimination: Brief tool name only needed when KAIROS or KAIROS_BRIEF is on 8/* eslint-disable @typescript-eslint/no-require-imports */ 9const BRIEF_TOOL_NAME: string | null = 10 feature('KAIROS') || feature('KAIROS_BRIEF') 11 ? ( 12 require('../BriefTool/prompt.js') as typeof import('../BriefTool/prompt.js') 13 ).BRIEF_TOOL_NAME 14 : null 15const SEND_USER_FILE_TOOL_NAME: string | null = feature('KAIROS') 16 ? ( 17 require('../SendUserFileTool/prompt.js') as typeof import('../SendUserFileTool/prompt.js') 18 ).SEND_USER_FILE_TOOL_NAME 19 : null 20 21/* eslint-enable @typescript-eslint/no-require-imports */ 22 23export { TOOL_SEARCH_TOOL_NAME } from './constants.js' 24 25import { TOOL_SEARCH_TOOL_NAME } from './constants.js' 26 27const PROMPT_HEAD = `Fetches full schema definitions for deferred tools so they can be called. 28 29` 30 31// Matches isDeferredToolsDeltaEnabled in toolSearch.ts (not imported — 32// toolSearch.ts imports from this file). When enabled: tools announced 33// via system-reminder attachments. When disabled: prepended 34// <available-deferred-tools> block (pre-gate behavior). 35function getToolLocationHint(): string { 36 const deltaEnabled = 37 process.env.USER_TYPE === 'ant' || 38 getFeatureValue_CACHED_MAY_BE_STALE('tengu_glacier_2xr', false) 39 return deltaEnabled 40 ? 'Deferred tools appear by name in <system-reminder> messages.' 41 : 'Deferred tools appear by name in <available-deferred-tools> messages.' 42} 43 44const PROMPT_TAIL = ` Until fetched, only the name is known — there is no parameter schema, so the tool cannot be invoked. This tool takes a query, matches it against the deferred tool list, and returns the matched tools' complete JSONSchema definitions inside a <functions> block. Once a tool's schema appears in that result, it is callable exactly like any tool defined at the top of the prompt. 45 46Result format: each matched tool appears as one <function>{"description": "...", "name": "...", "parameters": {...}}</function> line inside the <functions> block — the same encoding as the tool list at the top of this prompt. 47 48Query forms: 49- "select:Read,Edit,Grep" — fetch these exact tools by name 50- "notebook jupyter" — keyword search, up to max_results best matches 51- "+slack send" — require "slack" in the name, rank by remaining terms` 52 53/** 54 * Check if a tool should be deferred (requires ToolSearch to load). 55 * A tool is deferred if: 56 * - It's an MCP tool (always deferred - workflow-specific) 57 * - It has shouldDefer: true 58 * 59 * A tool is NEVER deferred if it has alwaysLoad: true (MCP tools set this via 60 * _meta['anthropic/alwaysLoad']). This check runs first, before any other rule. 61 */ 62export function isDeferredTool(tool: Tool): boolean { 63 // Explicit opt-out via _meta['anthropic/alwaysLoad'] — tool appears in the 64 // initial prompt with full schema. Checked first so MCP tools can opt out. 65 if (tool.alwaysLoad === true) return false 66 67 // MCP tools are always deferred (workflow-specific) 68 if (tool.isMcp === true) return true 69 70 // Never defer ToolSearch itself — the model needs it to load everything else 71 if (tool.name === TOOL_SEARCH_TOOL_NAME) return false 72 73 // Fork-first experiment: Agent must be available turn 1, not behind ToolSearch. 74 // Lazy require: static import of forkSubagent → coordinatorMode creates a cycle 75 // through constants/tools.ts at module init. 76 if (feature('FORK_SUBAGENT') && tool.name === AGENT_TOOL_NAME) { 77 type ForkMod = typeof import('../AgentTool/forkSubagent.js') 78 // eslint-disable-next-line @typescript-eslint/no-require-imports 79 const m = require('../AgentTool/forkSubagent.js') as ForkMod 80 if (m.isForkSubagentEnabled()) return false 81 } 82 83 // Brief is the primary communication channel whenever the tool is present. 84 // Its prompt contains the text-visibility contract, which the model must 85 // see without a ToolSearch round-trip. No runtime gate needed here: this 86 // tool's isEnabled() IS isBriefEnabled(), so being asked about its deferral 87 // status implies the gate already passed. 88 if ( 89 (feature('KAIROS') || feature('KAIROS_BRIEF')) && 90 BRIEF_TOOL_NAME && 91 tool.name === BRIEF_TOOL_NAME 92 ) { 93 return false 94 } 95 96 // SendUserFile is a file-delivery communication channel (sibling of Brief). 97 // Must be immediately available without a ToolSearch round-trip. 98 if ( 99 feature('KAIROS') && 100 SEND_USER_FILE_TOOL_NAME && 101 tool.name === SEND_USER_FILE_TOOL_NAME && 102 isReplBridgeActive() 103 ) { 104 return false 105 } 106 107 return tool.shouldDefer === true 108} 109 110/** 111 * Format one deferred-tool line for the <available-deferred-tools> user 112 * message. Search hints (tool.searchHint) are not rendered — the 113 * hints A/B (exp_xenhnnmn0smrx4, stopped Mar 21) showed no benefit. 114 */ 115export function formatDeferredToolLine(tool: Tool): string { 116 return tool.name 117} 118 119export function getPrompt(): string { 120 return PROMPT_HEAD + getToolLocationHint() + PROMPT_TAIL 121}