prompt.ts
tools/ToolSearchTool/prompt.ts
122
Lines
5227
Bytes
4
Exports
7
Imports
10
Keywords
What this is
This page documents one file from the repository and includes its full source so you can read it without leaving the docs site.
Beginner explanation
This file is part of the tool layer, which means it describes actions the system can perform for the user or model.
How it is used
Start from the exports list and related files. Those are the easiest clues for where this file fits into the system.
Expert explanation
Architecturally, this file intersects with tool-system. It contains 122 lines, 7 detected imports, and 4 detected exports.
Important relationships
- tools/ToolSearchTool/ToolSearchTool.ts
- tools/ToolSearchTool/constants.ts
- buddy/prompt.ts
- services/compact/prompt.ts
- tools/AgentTool/prompt.ts
- tools/AskUserQuestionTool/prompt.ts
- tools/BashTool/prompt.ts
- tools/BriefTool/prompt.ts
- tools/ConfigTool/prompt.ts
- tools/EnterPlanModeTool/prompt.ts
- tools/EnterWorktreeTool/prompt.ts
- tools/ExitPlanModeTool/prompt.ts
Detected exports
isDeferredToolformatDeferredToolLinegetPromptTOOL_SEARCH_TOOL_NAME
Keywords
toolnametoolsdeferredfeatureprompttoolsearchkairosrequireconstants
Detected imports
bun:bundle../../bootstrap/state.js../../services/analytics/growthbook.js../../Tool.js../AgentTool/constants.js./constants.js./constants.js
Source notes
This page embeds the full file contents. Small or leaf files are still indexed honestly instead of being over-explained.
Full source
import { feature } from 'bun:bundle'
import { isReplBridgeActive } from '../../bootstrap/state.js'
import { getFeatureValue_CACHED_MAY_BE_STALE } from '../../services/analytics/growthbook.js'
import type { Tool } from '../../Tool.js'
import { AGENT_TOOL_NAME } from '../AgentTool/constants.js'
// Dead code elimination: Brief tool name only needed when KAIROS or KAIROS_BRIEF is on
/* eslint-disable @typescript-eslint/no-require-imports */
const BRIEF_TOOL_NAME: string | null =
feature('KAIROS') || feature('KAIROS_BRIEF')
? (
require('../BriefTool/prompt.js') as typeof import('../BriefTool/prompt.js')
).BRIEF_TOOL_NAME
: null
const SEND_USER_FILE_TOOL_NAME: string | null = feature('KAIROS')
? (
require('../SendUserFileTool/prompt.js') as typeof import('../SendUserFileTool/prompt.js')
).SEND_USER_FILE_TOOL_NAME
: null
/* eslint-enable @typescript-eslint/no-require-imports */
export { TOOL_SEARCH_TOOL_NAME } from './constants.js'
import { TOOL_SEARCH_TOOL_NAME } from './constants.js'
const PROMPT_HEAD = `Fetches full schema definitions for deferred tools so they can be called.
`
// Matches isDeferredToolsDeltaEnabled in toolSearch.ts (not imported —
// toolSearch.ts imports from this file). When enabled: tools announced
// via system-reminder attachments. When disabled: prepended
// <available-deferred-tools> block (pre-gate behavior).
function getToolLocationHint(): string {
const deltaEnabled =
process.env.USER_TYPE === 'ant' ||
getFeatureValue_CACHED_MAY_BE_STALE('tengu_glacier_2xr', false)
return deltaEnabled
? 'Deferred tools appear by name in <system-reminder> messages.'
: 'Deferred tools appear by name in <available-deferred-tools> messages.'
}
const 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.
Result 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.
Query forms:
- "select:Read,Edit,Grep" — fetch these exact tools by name
- "notebook jupyter" — keyword search, up to max_results best matches
- "+slack send" — require "slack" in the name, rank by remaining terms`
/**
* Check if a tool should be deferred (requires ToolSearch to load).
* A tool is deferred if:
* - It's an MCP tool (always deferred - workflow-specific)
* - It has shouldDefer: true
*
* A tool is NEVER deferred if it has alwaysLoad: true (MCP tools set this via
* _meta['anthropic/alwaysLoad']). This check runs first, before any other rule.
*/
export function isDeferredTool(tool: Tool): boolean {
// Explicit opt-out via _meta['anthropic/alwaysLoad'] — tool appears in the
// initial prompt with full schema. Checked first so MCP tools can opt out.
if (tool.alwaysLoad === true) return false
// MCP tools are always deferred (workflow-specific)
if (tool.isMcp === true) return true
// Never defer ToolSearch itself — the model needs it to load everything else
if (tool.name === TOOL_SEARCH_TOOL_NAME) return false
// Fork-first experiment: Agent must be available turn 1, not behind ToolSearch.
// Lazy require: static import of forkSubagent → coordinatorMode creates a cycle
// through constants/tools.ts at module init.
if (feature('FORK_SUBAGENT') && tool.name === AGENT_TOOL_NAME) {
type ForkMod = typeof import('../AgentTool/forkSubagent.js')
// eslint-disable-next-line @typescript-eslint/no-require-imports
const m = require('../AgentTool/forkSubagent.js') as ForkMod
if (m.isForkSubagentEnabled()) return false
}
// Brief is the primary communication channel whenever the tool is present.
// Its prompt contains the text-visibility contract, which the model must
// see without a ToolSearch round-trip. No runtime gate needed here: this
// tool's isEnabled() IS isBriefEnabled(), so being asked about its deferral
// status implies the gate already passed.
if (
(feature('KAIROS') || feature('KAIROS_BRIEF')) &&
BRIEF_TOOL_NAME &&
tool.name === BRIEF_TOOL_NAME
) {
return false
}
// SendUserFile is a file-delivery communication channel (sibling of Brief).
// Must be immediately available without a ToolSearch round-trip.
if (
feature('KAIROS') &&
SEND_USER_FILE_TOOL_NAME &&
tool.name === SEND_USER_FILE_TOOL_NAME &&
isReplBridgeActive()
) {
return false
}
return tool.shouldDefer === true
}
/**
* Format one deferred-tool line for the <available-deferred-tools> user
* message. Search hints (tool.searchHint) are not rendered — the
* hints A/B (exp_xenhnnmn0smrx4, stopped Mar 21) showed no benefit.
*/
export function formatDeferredToolLine(tool: Tool): string {
return tool.name
}
export function getPrompt(): string {
return PROMPT_HEAD + getToolLocationHint() + PROMPT_TAIL
}