@codebuff/sdk
Version:
Official SDK for Codebuff — AI coding agent & framework
10 lines • 231 kB
Source Map (JSON)
{
"version": 3,
"sources": ["../../common/src/old-constants.ts", "../../packages/code-map/src/parse.ts", "../../packages/code-map/src/languages.ts", "../../packages/code-map/src/init-node.ts", "../src/client.ts", "../src/run-state.ts", "../../common/src/types/session-state.ts", "../../common/src/constants/agents.ts", "../../common/src/util/file.ts", "../../common/src/json-config/constants.ts", "../../common/src/types/messages/codebuff-message.ts", "../../common/src/types/messages/content-part.ts", "../../common/src/types/messages/provider-metadata.ts", "../../common/src/types/json.ts", "../../common/src/types/messages/data-content.ts", "../src/tools/change-file.ts", "../src/tools/code-search.ts", "../src/native/ripgrep.ts", "../src/tools/read-files.ts", "../src/tools/run-terminal-command.ts", "../src/constants.ts", "../../common/src/websockets/websocket-client.ts", "../src/websocket-client.ts", "../../common/src/actions.ts", "../../common/src/types/grant.ts", "../../common/src/types/print-mode.ts", "../../common/src/tools/constants.ts", "../../common/src/tools/list.ts", "../../common/src/tools/params/tool/add-message.ts", "../../common/src/tools/params/tool/add-subgoal.ts", "../../common/src/tools/params/tool/browser-logs.ts", "../../common/src/browser-actions.ts", "../../common/src/tools/params/tool/code-search.ts", "../../common/src/tools/params/tool/create-plan.ts", "../../common/src/tools/params/tool/str-replace.ts", "../../common/src/tools/params/tool/end-turn.ts", "../../common/src/tools/params/tool/find-files.ts", "../../common/src/tools/params/tool/read-files.ts", "../../common/src/tools/params/tool/read-docs.ts", "../../common/src/tools/params/tool/run-file-change-hooks.ts", "../../common/src/tools/params/tool/run-terminal-command.ts", "../../common/src/tools/params/tool/set-messages.ts", "../../common/src/tools/params/tool/set-output.ts", "../../common/src/tools/params/tool/spawn-agent-inline.ts", "../../common/src/tools/params/tool/spawn-agents.ts", "../../common/src/tools/params/tool/spawn-agents-async.ts", "../../common/src/tools/params/tool/think-deeply.ts", "../../common/src/tools/params/tool/update-subgoal.ts", "../../common/src/tools/params/tool/web-search.ts", "../../common/src/tools/params/tool/write-file.ts", "../src/custom-tool.ts", "../src/tools/run-file-change-hooks.ts", "../src/tools/index.ts"],
"sourcesContent": [
"import { isExplicitlyDefinedModel } from './util/model-utils'\n\nexport const STOP_MARKER = '[' + 'END]'\nexport const FIND_FILES_MARKER = '[' + 'FIND_FILES_PLEASE]'\nexport const EXISTING_CODE_MARKER = '[[**REPLACE_WITH_EXISTING_CODE**]]'\n\n// Directory where agent template override files are stored\nexport const AGENT_TEMPLATES_DIR = '.agents/'\nexport const AGENT_DEFINITION_FILE = 'agent-definition.d.ts'\n\nexport const API_KEY_ENV_VAR = 'CODEBUFF_API_KEY'\n\nexport const INVALID_AUTH_TOKEN_MESSAGE =\n 'Invalid auth token. You may have been logged out from the web portal. Please log in again.'\n\n// Enable async agents to run tool calls even when main user input is cancelled\nexport const ASYNC_AGENTS_ENABLED = true\n\n// Allowed model prefixes for validation\nexport const ALLOWED_MODEL_PREFIXES = [\n 'anthropic',\n 'openai',\n 'google',\n 'x-ai',\n] as const\n\nexport const DEFAULT_IGNORED_PATHS = [\n '.git',\n '.env',\n '.env.*',\n '*.min.*',\n 'node_modules',\n 'venv',\n 'virtualenv',\n '.venv',\n '.virtualenv',\n '__pycache__',\n '*.egg-info/',\n '*.pyc',\n '.DS_Store',\n '.pytest_cache',\n '.mypy_cache',\n '.ruff_cache',\n '.next',\n 'package-lock.json',\n 'bun.lockb',\n]\n\n// Special message content tags indicating specific server states\nexport const ASKED_CONFIG = 'asked_config'\nexport const SHOULD_ASK_CONFIG = 'should_ask_config'\nexport const ONE_TIME_TAGS = [] as const\nexport const ONE_TIME_LABELS = [\n ...ONE_TIME_TAGS,\n ASKED_CONFIG,\n SHOULD_ASK_CONFIG,\n] as const\n\nexport const FILE_READ_STATUS = {\n DOES_NOT_EXIST: '[FILE_DOES_NOT_EXIST]',\n IGNORED: '[FILE_IGNORED_BY_GITIGNORE_OR_CODEBUFF_IGNORE]',\n OUTSIDE_PROJECT: '[FILE_OUTSIDE_PROJECT]',\n TOO_LARGE: '[FILE_TOO_LARGE]',\n ERROR: '[FILE_READ_ERROR]',\n} as const\n\nexport const HIDDEN_FILE_READ_STATUS = [\n FILE_READ_STATUS.DOES_NOT_EXIST,\n FILE_READ_STATUS.IGNORED,\n FILE_READ_STATUS.OUTSIDE_PROJECT,\n FILE_READ_STATUS.TOO_LARGE,\n FILE_READ_STATUS.ERROR,\n]\n\nexport function toOptionalFile(file: string | null) {\n if (file === null) return null\n return HIDDEN_FILE_READ_STATUS.some((status) => file.startsWith(status))\n ? null\n : file\n}\n\nexport const REQUEST_CREDIT_SHOW_THRESHOLD = 1\nexport const MAX_DATE = new Date(86399999999999)\nexport const BILLING_PERIOD_DAYS = 30\nexport const CREDITS_REFERRAL_BONUS = 250\nexport const AFFILIATE_USER_REFFERAL_LIMIT = 500\n\n// Default number of free credits granted per cycle\nexport const DEFAULT_FREE_CREDITS_GRANT = 500\n\n// Credit pricing configuration\nexport const CREDIT_PRICING = {\n CENTS_PER_CREDIT: 1, // 1 credit = 1 cent = $0.01\n MIN_PURCHASE_CREDITS: 100, // $1.00 minimum\n DISPLAY_RATE: '$0.01 per credit',\n} as const\n\nexport const AuthState = {\n LOGGED_OUT: 'LOGGED_OUT',\n LOGGED_IN: 'LOGGED_IN',\n} as const\n\nexport type AuthState = (typeof AuthState)[keyof typeof AuthState]\n\nexport const UserState = {\n LOGGED_OUT: 'LOGGED_OUT',\n GOOD_STANDING: 'GOOD_STANDING', // >= 100 credits\n ATTENTION_NEEDED: 'ATTENTION_NEEDED', // 20-99 credits\n CRITICAL: 'CRITICAL', // 1-19 credits\n DEPLETED: 'DEPLETED', // <= 0 credits\n} as const\n\nexport type UserState = (typeof UserState)[keyof typeof UserState]\n\nexport function getUserState(isLoggedIn: boolean, credits: number): UserState {\n if (!isLoggedIn) return UserState.LOGGED_OUT\n\n if (credits >= 100) return UserState.GOOD_STANDING\n if (credits >= 20) return UserState.ATTENTION_NEEDED\n if (credits >= 1) return UserState.CRITICAL\n return UserState.DEPLETED\n}\n\nexport const costModes = [\n 'lite',\n 'normal',\n 'max',\n 'experimental',\n 'ask',\n] as const\nexport type CostMode = (typeof costModes)[number]\n\nexport const getModelForMode = (\n costMode: CostMode,\n operation: 'agent' | 'file-requests' | 'check-new-files',\n) => {\n if (operation === 'agent') {\n return {\n lite: models.openrouter_gemini2_5_flash,\n normal: models.openrouter_claude_sonnet_4,\n max: models.openrouter_claude_sonnet_4,\n experimental: models.openrouter_gemini2_5_pro_preview,\n ask: models.openrouter_gemini2_5_pro_preview,\n }[costMode]\n }\n if (operation === 'file-requests') {\n return {\n lite: models.openrouter_claude_3_5_haiku,\n normal: models.openrouter_claude_3_5_haiku,\n max: models.openrouter_claude_sonnet_4,\n experimental: models.openrouter_claude_sonnet_4,\n ask: models.openrouter_claude_3_5_haiku,\n }[costMode]\n }\n if (operation === 'check-new-files') {\n return {\n lite: models.openrouter_claude_3_5_haiku,\n normal: models.openrouter_claude_sonnet_4,\n max: models.openrouter_claude_sonnet_4,\n experimental: models.openrouter_claude_sonnet_4,\n ask: models.openrouter_claude_sonnet_4,\n }[costMode]\n }\n throw new Error(`Unknown operation: ${operation}`)\n}\n\n// export const claudeModels = {\n// sonnet: 'claude-sonnet-4-20250514',\n// sonnet3_7: 'claude-3-7-sonnet-20250219',\n// sonnet3_5: 'claude-3-5-sonnet-20241022',\n// opus4: 'claude-opus-4-20250514',\n// haiku: 'claude-3-5-haiku-20241022',\n// } as const\n\nexport const openaiModels = {\n gpt4_1: 'gpt-4.1-2025-04-14',\n gpt4o: 'gpt-4o-2024-11-20',\n gpt4omini: 'gpt-4o-mini-2024-07-18',\n o3mini: 'o3-mini-2025-01-31',\n o3: 'o3-2025-04-16',\n o3pro: 'o3-pro-2025-06-10',\n o4mini: 'o4-mini-2025-04-16',\n generatePatch:\n 'ft:gpt-4o-2024-08-06:manifold-markets:generate-patch-batch2:AKYtDIhk',\n} as const\nexport type OpenAIModel = (typeof openaiModels)[keyof typeof openaiModels]\n\nexport const geminiModels = {\n gemini2_5_flash: 'gemini-2.5-flash-preview-05-20',\n gemini2_5_flash_thinking: 'gemini-2.5-flash-preview-05-20:thinking',\n gemini2flash: 'gemini-2.0-flash-001',\n gemini2_5_pro_preview: 'gemini-2.5-pro-preview-06-05',\n} as const\nexport type GeminiModel = (typeof geminiModels)[keyof typeof geminiModels]\n\nexport const openrouterModels = {\n openrouter_claude_sonnet_4: 'anthropic/claude-4-sonnet-20250522',\n openrouter_claude_opus_4: 'anthropic/claude-opus-4.1',\n openrouter_claude_3_5_haiku: 'anthropic/claude-3.5-haiku-20241022',\n openrouter_claude_3_5_sonnet: 'anthropic/claude-3.5-sonnet-20240620',\n openrouter_gpt4o: 'openai/gpt-4o-2024-11-20',\n openrouter_gpt5: 'openai/gpt-5',\n openrouter_gpt5_chat: 'openai/gpt-5-chat',\n openrouter_gpt4o_mini: 'openai/gpt-4o-mini-2024-07-18',\n openrouter_gpt4_1_nano: 'openai/gpt-4.1-nano',\n openrouter_o3_mini: 'openai/o3-mini-2025-01-31',\n openrouter_gemini2_5_pro_preview: 'google/gemini-2.5-pro',\n openrouter_gemini2_5_flash: 'google/gemini-2.5-flash',\n openrouter_gemini2_5_flash_thinking:\n 'google/gemini-2.5-flash-preview:thinking',\n openrouter_grok_4: 'x-ai/grok-4-07-09',\n} as const\nexport type openrouterModel =\n (typeof openrouterModels)[keyof typeof openrouterModels]\n\nexport const deepseekModels = {\n deepseekChat: 'deepseek-chat',\n deepseekReasoner: 'deepseek-reasoner',\n} as const\nexport type DeepseekModel = (typeof deepseekModels)[keyof typeof deepseekModels]\n\n// Vertex uses \"endpoint IDs\" for finetuned models, which are just integers\nexport const finetunedVertexModels = {\n ft_filepicker_003: '196166068534771712',\n ft_filepicker_005: '8493203957034778624',\n ft_filepicker_007: '2589952415784501248',\n ft_filepicker_topk_001: '3676445825887633408',\n ft_filepicker_008: '2672143108984012800',\n ft_filepicker_topk_002: '1694861989844615168',\n ft_filepicker_010: '3808739064941641728',\n ft_filepicker_010_epoch_2: '6231675664466968576',\n ft_filepicker_topk_003: '1502192368286171136',\n} as const\nexport const finetunedVertexModelNames: Record<string, string> = {\n [finetunedVertexModels.ft_filepicker_003]: 'ft_filepicker_003',\n [finetunedVertexModels.ft_filepicker_005]: 'ft_filepicker_005',\n [finetunedVertexModels.ft_filepicker_007]: 'ft_filepicker_007',\n [finetunedVertexModels.ft_filepicker_topk_001]: 'ft_filepicker_topk_001',\n [finetunedVertexModels.ft_filepicker_008]: 'ft_filepicker_008',\n [finetunedVertexModels.ft_filepicker_topk_002]: 'ft_filepicker_topk_002',\n [finetunedVertexModels.ft_filepicker_010]: 'ft_filepicker_010',\n [finetunedVertexModels.ft_filepicker_010_epoch_2]:\n 'ft_filepicker_010_epoch_2',\n [finetunedVertexModels.ft_filepicker_topk_003]: 'ft_filepicker_topk_003',\n}\nexport type FinetunedVertexModel =\n (typeof finetunedVertexModels)[keyof typeof finetunedVertexModels]\n\nexport const models = {\n // ...claudeModels,\n ...openaiModels,\n ...geminiModels,\n ...deepseekModels,\n ...openrouterModels,\n ...finetunedVertexModels,\n} as const\n\nexport const shortModelNames = {\n 'gemini-2.5-pro': models.openrouter_gemini2_5_pro_preview,\n 'flash-2.5': models.openrouter_gemini2_5_flash,\n 'opus-4': models.openrouter_claude_opus_4,\n 'sonnet-4': models.openrouter_claude_sonnet_4,\n 'sonnet-3.7': models.openrouter_claude_sonnet_4,\n 'sonnet-3.6': models.openrouter_claude_3_5_sonnet,\n 'sonnet-3.5': models.openrouter_claude_3_5_sonnet,\n 'gpt-4.1': models.gpt4_1,\n 'o3-mini': models.o3mini,\n o3: models.o3,\n 'o4-mini': models.o4mini,\n 'o3-pro': models.o3pro,\n}\n\nexport const providerModelNames = {\n ...Object.fromEntries(\n Object.entries(geminiModels).map(([name, model]) => [\n model,\n 'gemini' as const,\n ]),\n ),\n // ...Object.fromEntries(\n // Object.entries(openrouterModels).map(([name, model]) => [\n // model,\n // 'claude' as const,\n // ])\n // ),\n ...Object.fromEntries(\n Object.entries(openaiModels).map(([name, model]) => [\n model,\n 'openai' as const,\n ]),\n ),\n ...Object.fromEntries(\n Object.entries(openrouterModels).map(([name, model]) => [\n model,\n 'openrouter' as const,\n ]),\n ),\n}\n\nexport type Model = (typeof models)[keyof typeof models] | (string & {})\n\nexport const shouldCacheModels = [\n 'anthropic/claude-opus-4.1',\n 'anthropic/claude-sonnet-4',\n 'anthropic/claude-opus-4',\n 'anthropic/claude-3.7-sonnet',\n 'anthropic/claude-3.5-haiku',\n 'z-ai/glm-4.5',\n 'qwen/qwen3-coder',\n]\nconst nonCacheableModels = [\n models.openrouter_grok_4,\n] satisfies string[] as string[]\nexport function supportsCacheControl(model: Model): boolean {\n if (!isExplicitlyDefinedModel(model)) {\n // Default to no cache control for unknown models\n return false\n }\n return !nonCacheableModels.includes(model)\n}\n\nexport const TEST_USER_ID = 'test-user-id'\n\nexport function getModelFromShortName(\n modelName: string | undefined,\n): Model | undefined {\n if (!modelName) return undefined\n if (modelName && !(modelName in shortModelNames)) {\n throw new Error(\n `Unknown model: ${modelName}. Please use a valid model. Valid models are: ${Object.keys(\n shortModelNames,\n ).join(', ')}`,\n )\n }\n\n return shortModelNames[modelName as keyof typeof shortModelNames]\n}\n\nexport const providerDomains = {\n google: 'google.com',\n anthropic: 'anthropic.com',\n openai: 'chatgpt.com',\n deepseek: 'deepseek.com',\n xai: 'x.ai',\n} as const\n\nexport function getLogoForModel(modelName: string): string | undefined {\n let domain: string | undefined\n\n if (Object.values(geminiModels).includes(modelName as GeminiModel))\n domain = providerDomains.google\n else if (Object.values(openaiModels).includes(modelName as OpenAIModel))\n domain = providerDomains.openai\n else if (Object.values(deepseekModels).includes(modelName as DeepseekModel))\n domain = providerDomains.deepseek\n else if (modelName.includes('claude')) domain = providerDomains.anthropic\n else if (modelName.includes('grok')) domain = providerDomains.xai\n\n return domain\n ? `https://www.google.com/s2/favicons?domain=${domain}&sz=256`\n : undefined\n}\n",
"import * as fs from 'fs'\nimport * as path from 'path'\n\nimport { getLanguageConfig, LanguageConfig } from './languages'\nimport type { Parser, Query } from 'web-tree-sitter'\n\nexport const DEBUG_PARSING = false\nconst IGNORE_TOKENS = ['__init__', '__post_init__', '__call__', 'constructor']\nconst MAX_CALLERS = 25\n\nexport interface TokenCallerMap {\n [filePath: string]: {\n [token: string]: string[] // Array of files that call this token\n }\n}\n\nexport interface FileTokenData {\n tokenScores: { [filePath: string]: { [token: string]: number } }\n tokenCallers: TokenCallerMap\n}\n\nexport async function getFileTokenScores(\n projectRoot: string,\n filePaths: string[],\n readFile?: (filePath: string) => string | null,\n): Promise<FileTokenData> {\n const startTime = Date.now()\n const tokenScores: { [filePath: string]: { [token: string]: number } } = {}\n const externalCalls: { [token: string]: number } = {}\n const fileCallsMap = new Map<string, string[]>()\n\n // First pass: collect all identifiers and calls\n for (const filePath of filePaths) {\n const fullPath = path.join(projectRoot, filePath)\n const languageConfig = await getLanguageConfig(fullPath)\n if (languageConfig) {\n let parseResults\n if (readFile) {\n // When readFile is provided, use relative filePath\n parseResults = parseTokens(filePath, languageConfig, readFile)\n } else {\n // When readFile is not provided, use full path to read from file system\n parseResults = parseTokens(fullPath, languageConfig)\n }\n const { identifiers, calls, numLines } = parseResults\n\n const tokenScoresForFile: { [token: string]: number } = {}\n tokenScores[filePath] = tokenScoresForFile\n\n const dirs = path.dirname(fullPath).split(path.sep)\n const depth = dirs.length\n const tokenBaseScore =\n 0.8 ** depth * Math.sqrt(numLines / (identifiers.length + 1))\n\n // Store defined tokens\n for (const identifier of identifiers) {\n if (!IGNORE_TOKENS.includes(identifier)) {\n tokenScoresForFile[identifier] = tokenBaseScore\n }\n }\n\n // Store calls for this file\n fileCallsMap.set(filePath, calls)\n\n // Track external calls\n for (const call of calls) {\n if (!tokenScoresForFile[call]) {\n externalCalls[call] = (externalCalls[call] ?? 0) + 1\n }\n }\n }\n }\n // Build a map of tokens to their defining files for O(1) lookup\n const tokenDefinitionMap = new Map<string, string>()\n const highestScores = new Map<string, number>()\n for (const [filePath, scores] of Object.entries(tokenScores)) {\n for (const [token, score] of Object.entries(scores)) {\n const currentHighestScore = highestScores.get(token) ?? -Infinity\n // Keep the file with the higher score for this token\n if (score > currentHighestScore) {\n highestScores.set(token, score)\n tokenDefinitionMap.set(token, filePath)\n }\n }\n }\n\n const tokenCallers: TokenCallerMap = {}\n\n // For each file's calls, add it as a caller to the defining file's tokens\n for (const [callingFile, calls] of fileCallsMap.entries()) {\n for (const call of calls) {\n const definingFile = tokenDefinitionMap.get(call)\n if (!definingFile || callingFile === definingFile) {\n continue\n }\n\n // Skip token names in default objects, e.g. toString, hasOwnProperty\n if (call in {}) {\n continue\n }\n\n if (!tokenCallers[definingFile]) {\n tokenCallers[definingFile] = {}\n }\n\n if (!tokenCallers[definingFile][call]) {\n tokenCallers[definingFile][call] = []\n }\n const callerFiles = tokenCallers[definingFile][call]\n if (\n callerFiles.length < MAX_CALLERS &&\n !callerFiles.includes(callingFile)\n ) {\n callerFiles.push(callingFile)\n }\n }\n }\n\n // Apply call frequency boost to token scores\n for (const scores of Object.values(tokenScores)) {\n for (const token of Object.keys(scores)) {\n const numCalls = externalCalls[token] ?? 0\n if (typeof numCalls !== 'number') continue\n scores[token] *= 1 + Math.log(1 + numCalls)\n // Round to 3 decimal places\n scores[token] = Math.round(scores[token] * 1000) / 1000\n }\n }\n\n if (DEBUG_PARSING) {\n const endTime = Date.now()\n console.log(`Parsed ${filePaths.length} files in ${endTime - startTime}ms`)\n\n try {\n fs.writeFileSync(\n '../debug/debug-parse.json',\n JSON.stringify({\n tokenCallers,\n tokenScores,\n fileCallsMap,\n externalCalls,\n }),\n )\n } catch {\n // Silently ignore debug file write errors in test environments\n }\n }\n\n return { tokenScores, tokenCallers }\n}\n\nexport function parseTokens(\n filePath: string,\n languageConfig: LanguageConfig,\n readFile?: (filePath: string) => string | null,\n) {\n const { parser, query } = languageConfig\n\n try {\n const sourceCode = readFile\n ? readFile(filePath)\n : fs.readFileSync(filePath, 'utf8')\n if (sourceCode === null) {\n return {\n numLines: 0,\n identifiers: [] as string[],\n calls: [] as string[],\n }\n }\n const numLines = sourceCode.match(/\\n/g)?.length ?? 0 + 1\n if (!parser || !query) {\n throw new Error('Parser or query not found')\n }\n const parseResults = parseFile(parser, query, sourceCode)\n const identifiers = Array.from(new Set(parseResults.identifier))\n const calls = Array.from(new Set(parseResults['call.identifier']))\n\n if (DEBUG_PARSING) {\n console.log(`\\nParsing ${filePath}:`)\n console.log('Identifiers:', identifiers)\n console.log('Calls:', calls)\n }\n\n return {\n numLines,\n identifiers: identifiers ?? [],\n calls: calls ?? [],\n }\n } catch (e) {\n if (DEBUG_PARSING) {\n console.error(`Error parsing query: ${e}`)\n console.log(filePath)\n }\n return {\n numLines: 0,\n identifiers: [] as string[],\n calls: [] as string[],\n }\n }\n}\n\nfunction parseFile(\n parser: Parser,\n query: Query,\n sourceCode: string,\n): { [key: string]: string[] } {\n const tree = parser.parse(sourceCode)\n if (!tree) {\n return {}\n }\n const captures = query.captures(tree.rootNode)\n const result: { [key: string]: string[] } = {}\n\n for (const capture of captures) {\n const { name, node } = capture\n if (!result[name]) {\n result[name] = []\n }\n result[name].push(node.text)\n }\n\n return result\n}\n",
"import * as path from 'path'\n\n// Import some types for wasm & .scm files\nimport './types'\n\nimport { Language, Parser, Query } from 'web-tree-sitter'\nimport { DEBUG_PARSING } from './parse'\n\n/* ------------------------------------------------------------------ */\n/* 1. Query imports (these work in all bundled environments) */\n/* ------------------------------------------------------------------ */\nimport csharpQuery from './tree-sitter-queries/tree-sitter-c_sharp-tags.scm'\nimport cppQuery from './tree-sitter-queries/tree-sitter-cpp-tags.scm'\nimport goQuery from './tree-sitter-queries/tree-sitter-go-tags.scm'\nimport javaQuery from './tree-sitter-queries/tree-sitter-java-tags.scm'\nimport javascriptQuery from './tree-sitter-queries/tree-sitter-javascript-tags.scm'\nimport pythonQuery from './tree-sitter-queries/tree-sitter-python-tags.scm'\nimport rubyQuery from './tree-sitter-queries/tree-sitter-ruby-tags.scm'\nimport rustQuery from './tree-sitter-queries/tree-sitter-rust-tags.scm'\nimport typescriptQuery from './tree-sitter-queries/tree-sitter-typescript-tags.scm'\n\n/* ------------------------------------------------------------------ */\n/* 2. Types and interfaces */\n/* ------------------------------------------------------------------ */\nexport interface LanguageConfig {\n extensions: string[]\n wasmFile: string\n queryText: string\n\n /* Loaded lazily ↓ */\n parser?: Parser\n query?: Query\n language?: Language\n}\n\nexport interface RuntimeLanguageLoader {\n loadLanguage(wasmFile: string): Promise<Language>\n initParser(): Promise<void>\n}\n\n/* ------------------------------------------------------------------ */\n/* 3. WASM file manifest */\n/* ------------------------------------------------------------------ */\nexport const WASM_FILES = {\n 'tree-sitter-c-sharp.wasm': 'tree-sitter-c-sharp.wasm',\n 'tree-sitter-cpp.wasm': 'tree-sitter-cpp.wasm',\n 'tree-sitter-go.wasm': 'tree-sitter-go.wasm',\n 'tree-sitter-java.wasm': 'tree-sitter-java.wasm',\n 'tree-sitter-javascript.wasm': 'tree-sitter-javascript.wasm',\n 'tree-sitter-python.wasm': 'tree-sitter-python.wasm',\n 'tree-sitter-ruby.wasm': 'tree-sitter-ruby.wasm',\n 'tree-sitter-rust.wasm': 'tree-sitter-rust.wasm',\n 'tree-sitter-tsx.wasm': 'tree-sitter-tsx.wasm',\n 'tree-sitter-typescript.wasm': 'tree-sitter-typescript.wasm',\n} as const\n\n/* ------------------------------------------------------------------ */\n/* 4. Language table */\n/* ------------------------------------------------------------------ */\nexport const languageTable: LanguageConfig[] = [\n {\n extensions: ['.ts'],\n wasmFile: WASM_FILES['tree-sitter-typescript.wasm'],\n queryText: typescriptQuery,\n },\n {\n extensions: ['.tsx'],\n wasmFile: WASM_FILES['tree-sitter-tsx.wasm'],\n queryText: typescriptQuery,\n },\n {\n extensions: ['.js', '.jsx'],\n wasmFile: WASM_FILES['tree-sitter-javascript.wasm'],\n queryText: javascriptQuery,\n },\n {\n extensions: ['.py'],\n wasmFile: WASM_FILES['tree-sitter-python.wasm'],\n queryText: pythonQuery,\n },\n {\n extensions: ['.java'],\n wasmFile: WASM_FILES['tree-sitter-java.wasm'],\n queryText: javaQuery,\n },\n {\n extensions: ['.cs'],\n wasmFile: WASM_FILES['tree-sitter-c-sharp.wasm'],\n queryText: csharpQuery,\n },\n {\n extensions: ['.cpp', '.hpp'],\n wasmFile: WASM_FILES['tree-sitter-cpp.wasm'],\n queryText: cppQuery,\n },\n {\n extensions: ['.rs'],\n wasmFile: WASM_FILES['tree-sitter-rust.wasm'],\n queryText: rustQuery,\n },\n {\n extensions: ['.rb'],\n wasmFile: WASM_FILES['tree-sitter-ruby.wasm'],\n queryText: rubyQuery,\n },\n {\n extensions: ['.go'],\n wasmFile: WASM_FILES['tree-sitter-go.wasm'],\n queryText: goQuery,\n },\n]\n\n/* ------------------------------------------------------------------ */\n/* 5. WASM directory management */\n/* ------------------------------------------------------------------ */\nlet customWasmDir: string | undefined\n\n/**\n * Set a custom WASM directory for loading tree-sitter WASM files.\n * This can be useful for custom packaging or deployment scenarios.\n */\nexport function setWasmDir(dir: string): void {\n customWasmDir = dir\n}\n\nexport function getWasmDir(): string | undefined {\n return customWasmDir\n}\n\n/* ------------------------------------------------------------------ */\n/* 6. WASM path resolver */\n/* ------------------------------------------------------------------ */\n\n/**\n * Resolve the path to a WASM file in a Node.js or Bun environment.\n * Works for both ESM and CJS builds of the SDK.\n */\nfunction resolveWasmPath(wasmFileName: string): string {\n const customWasmDirPath = getWasmDir()\n if (customWasmDirPath) {\n return path.join(customWasmDirPath, wasmFileName)\n }\n\n // Try environment variable override\n const envWasmDir = process.env.CODEBUFF_WASM_DIR\n if (envWasmDir) {\n return path.join(envWasmDir, wasmFileName)\n }\n\n // Get the directory of this module\n const moduleDir = (() => {\n if (typeof __dirname !== 'undefined') {\n return __dirname\n }\n // For ESM builds, we can't reliably get the module directory in all environments\n // So we fall back to process.cwd() which works for our use case\n return process.cwd()\n })()\n\n // For bundled SDK: WASM files are in a shared wasm directory\n const possiblePaths = [\n // Shared WASM directory (new approach to avoid duplication)\n path.join(moduleDir, '..', 'wasm', wasmFileName),\n // WASM files in the same directory as this module (for bundled builds)\n path.join(moduleDir, 'wasm', wasmFileName),\n // Development scenario - shared wasm directory in SDK dist\n path.join(process.cwd(), 'dist', 'wasm', wasmFileName),\n ]\n\n // Try each path and return the first one that exists (we'll fallback to package resolution if none work)\n for (const wasmPath of possiblePaths) {\n try {\n // Don't actually check file existence here, let the Language.load() call handle it\n // and fall back to package resolution if it fails\n return wasmPath\n } catch {\n continue\n }\n }\n\n // Default fallback\n return possiblePaths[0]\n}\n\n/**\n * Fallback: try to resolve from the original package for development\n */\nfunction tryResolveFromPackage(wasmFileName: string): string | null {\n try {\n // This works in development/monorepo scenarios\n return require.resolve(`@vscode/tree-sitter-wasm/wasm/${wasmFileName}`)\n } catch {\n return null\n }\n}\n\n/* ------------------------------------------------------------------ */\n/* 7. One-time library init */\n/* ------------------------------------------------------------------ */\n// Initialize tree-sitter with Node.js-specific configuration\nimport { initTreeSitterForNode } from './init-node'\n\n/* ------------------------------------------------------------------ */\n/* 8. Unified runtime loader */\n/* ------------------------------------------------------------------ */\nclass UnifiedLanguageLoader implements RuntimeLanguageLoader {\n private parserReady: Promise<void>\n\n constructor() {\n this.parserReady = initTreeSitterForNode()\n }\n\n async initParser(): Promise<void> {\n await this.parserReady\n }\n\n async loadLanguage(wasmFile: string): Promise<Language> {\n // Resolve WASM file path\n let wasmPath = resolveWasmPath(wasmFile)\n\n // Try to load the language using Node.js-specific method if available\n let lang: Language\n try {\n lang = await Language.load(wasmPath)\n } catch (err) {\n // Fallback: try resolving from the original package (development)\n const fallbackPath = tryResolveFromPackage(wasmFile)\n if (fallbackPath) {\n lang = await Language.load(fallbackPath)\n } else {\n throw err\n }\n }\n\n return lang\n }\n}\n\n/* ------------------------------------------------------------------ */\n/* 9. Helper functions */\n/* ------------------------------------------------------------------ */\nexport function findLanguageConfigByExtension(\n filePath: string,\n): LanguageConfig | undefined {\n const ext = path.extname(filePath)\n return languageTable.find((c) => c.extensions.includes(ext))\n}\n\n/* ------------------------------------------------------------------ */\n/* 10. Language configuration loader */\n/* ------------------------------------------------------------------ */\nexport async function createLanguageConfig(\n filePath: string,\n runtimeLoader: RuntimeLanguageLoader,\n): Promise<LanguageConfig | undefined> {\n const cfg = findLanguageConfigByExtension(filePath)\n if (!cfg) {\n return undefined\n }\n\n if (!cfg.parser) {\n try {\n await runtimeLoader.initParser()\n\n // Load the language using the runtime-specific loader\n const lang = await runtimeLoader.loadLanguage(cfg.wasmFile)\n\n // Create parser and query\n const parser = new Parser()\n parser.setLanguage(lang)\n\n cfg.language = lang\n cfg.parser = parser\n cfg.query = new Query(lang, cfg.queryText)\n } catch (err) {\n // Let the runtime-specific implementation handle error logging\n throw err\n }\n }\n\n return cfg\n}\n\n/* ------------------------------------------------------------------ */\n/* 11. Public API */\n/* ------------------------------------------------------------------ */\nconst unifiedLoader = new UnifiedLanguageLoader()\n\nexport async function getLanguageConfig(\n filePath: string,\n): Promise<LanguageConfig | undefined> {\n try {\n return await createLanguageConfig(filePath, unifiedLoader)\n } catch (err) {\n if (DEBUG_PARSING) {\n console.error('[tree-sitter] Load error for', filePath, err)\n }\n return undefined\n }\n}\n",
"import * as path from 'path'\nimport * as fs from 'fs'\nimport { Parser } from 'web-tree-sitter'\nimport { fileURLToPath } from 'url'\n\n/**\n * Helper function to get the current directory path that works in both ESM and CJS\n */\nfunction hereDir() {\n // In CJS, __dirname is available\n if (typeof __dirname !== 'undefined') {\n return __dirname\n }\n\n // For ESM builds, use import.meta.url\n if (typeof import.meta !== 'undefined' && import.meta.url) {\n const dir = path.dirname(fileURLToPath(import.meta.url))\n return dir\n }\n\n // Fallback to process.cwd() as last resort\n return process.cwd()\n}\n\n/**\n * Initialize web-tree-sitter for Node.js environments with proper WASM file location\n */\nexport async function initTreeSitterForNode(): Promise<void> {\n // Get the directory where our WASM files should be located\n const dir = hereDir()\n\n // Try shared WASM directory first (new approach to avoid duplication)\n const sharedWasm = path.join(dir, '..', 'wasm', 'tree-sitter.wasm')\n\n // Use locateFile to override where the runtime looks for tree-sitter.wasm\n await Parser.init({\n locateFile: (name: string, scriptDir: string) => {\n if (name === 'tree-sitter.wasm') {\n // First try shared WASM directory (new approach)\n if (fs.existsSync(sharedWasm)) {\n return sharedWasm\n }\n // Fallback to script directory\n const fallback = path.join(scriptDir, name)\n if (fs.existsSync(fallback)) {\n return fallback\n }\n // Return our preferred path and let web-tree-sitter handle the error\n return sharedWasm\n }\n // For other files, use default behavior\n return path.join(scriptDir, name)\n },\n })\n}\n",
"import path from 'path'\n\nimport {\n initialSessionState,\n applyOverridesToSessionState,\n type RunState,\n} from './run-state'\nimport { changeFile } from './tools/change-file'\nimport { codeSearch } from './tools/code-search'\nimport { getFiles } from './tools/read-files'\nimport { runTerminalCommand } from './tools/run-terminal-command'\nimport { WebSocketHandler } from './websocket-client'\nimport {\n PromptResponseSchema,\n type ServerAction,\n} from '../../common/src/actions'\nimport { MAX_AGENT_STEPS_DEFAULT } from '../../common/src/constants/agents'\nimport { API_KEY_ENV_VAR } from '../../common/src/old-constants'\nimport { toolNames } from '../../common/src/tools/constants'\n\nimport {\n clientToolCallSchema,\n type ClientToolCall,\n type ClientToolName,\n type CodebuffToolOutput,\n} from '../../common/src/tools/list'\n\nimport type { CustomToolDefinition } from './custom-tool'\nimport type { AgentDefinition } from '../../common/src/templates/initial-agents-dir/types/agent-definition'\nimport type {\n PublishedToolName,\n ToolName,\n} from '../../common/src/tools/constants'\nimport type { PublishedClientToolName } from '../../common/src/tools/list'\nimport type {\n ToolResultOutput,\n ToolResultPart,\n} from '../../common/src/types/messages/content-part'\nimport type { PrintModeEvent } from '../../common/src/types/print-mode'\nimport type { SessionState } from '../../common/src/types/session-state'\n\nexport type CodebuffClientOptions = {\n // Provide an API key or set the CODEBUFF_API_KEY environment variable.\n apiKey?: string\n cwd?: string\n onError: (error: { message: string }) => void\n overrideTools?: Partial<\n {\n [K in ClientToolName & PublishedToolName]: (\n input: ClientToolCall<K>['input'],\n ) => Promise<CodebuffToolOutput<K>>\n } & {\n // Include read_files separately, since it has a different signature.\n read_files: (input: {\n filePaths: string[]\n }) => Promise<Record<string, string | null>>\n }\n >\n}\n\nexport class CodebuffClient {\n public cwd: string\n\n private readonly websocketHandler: WebSocketHandler\n private readonly overrideTools: NonNullable<\n CodebuffClientOptions['overrideTools']\n >\n private readonly fingerprintId = `codebuff-sdk-${Math.random().toString(36).substring(2, 15)}`\n\n private readonly promptIdToHandlers: Record<\n string,\n {\n handleEvent?: (event: PrintModeEvent) => void\n handleStreamChunk?: (chunk: string) => void\n resolveResponse?: {\n resolve: (response: any) => void\n reject: (error: any) => void\n }\n customToolHandler?: WebSocketHandler['handleToolCall']\n }\n > = {}\n\n constructor({ apiKey, cwd, onError, overrideTools }: CodebuffClientOptions) {\n const foundApiKey = apiKey ?? process.env[API_KEY_ENV_VAR]\n if (!foundApiKey) {\n throw new Error(\n `Codebuff API key not found. Please provide an apiKey in the constructor of CodebuffClient or set the ${API_KEY_ENV_VAR} environment variable.`,\n )\n }\n\n this.cwd = cwd ?? process.cwd()\n this.overrideTools = overrideTools ?? {}\n this.websocketHandler = new WebSocketHandler({\n apiKey: foundApiKey,\n onWebsocketError: (error) => {\n onError({ message: error.message })\n },\n onWebsocketReconnect: () => {},\n onRequestReconnect: async () => {},\n onResponseError: async (error) => {\n onError({ message: error.message })\n },\n readFiles: this.readFiles.bind(this),\n handleToolCall: this.handleToolCall.bind(this),\n onCostResponse: async () => {},\n\n onResponseChunk: async (action) => {\n const { userInputId, chunk } = action\n if (typeof chunk === 'string') {\n const handleStreamChunk =\n this.promptIdToHandlers[userInputId]?.handleStreamChunk\n if (handleStreamChunk) {\n handleStreamChunk(chunk)\n }\n } else {\n const handleEvent = this.promptIdToHandlers[userInputId]?.handleEvent\n if (handleEvent) {\n handleEvent(chunk)\n }\n }\n },\n onSubagentResponseChunk: async () => {},\n\n onPromptResponse: this.handlePromptResponse.bind(this),\n onPromptError: this.handlePromptResponse.bind(this),\n })\n }\n\n public closeConnection() {\n this.websocketHandler.close()\n }\n\n /**\n * Run a Codebuff agent with the specified options.\n *\n * @param agent - The agent to run. Use 'base' for the default agent, or specify a custom agent ID if you made your own agent config.\n * @param prompt - The user prompt describing what you want the agent to do.\n * @param params - (Optional) Additional parameters for the agent. Most agents don't use this, but some custom agents can take a JSON object as input in addition to the user prompt string.\n * @param handleEvent - (Optional) Callback function that receives every event during execution (assistant messages, tool calls, etc.). This allows you to stream the agent's progress in real-time. We will likely add a token-by-token streaming callback in the future.\n * @param previousRun - (Optional) JSON state returned from a previous run() call. Use this to continue a conversation or session with the agent, maintaining context from previous interactions.\n * @param projectFiles - (Optional) All the files in your project as a plain JavaScript object. Keys should be the full path from your current directory to each file, and values should be the string contents of the file. Example: { \"src/index.ts\": \"console.log('hi')\" }. This helps Codebuff pick good source files for context.\n * @param knowledgeFiles - (Optional) Knowledge files to inject into every run() call. Uses the same schema as projectFiles - keys are file paths and values are file contents. These files are added directly to the agent's context.\n * @param agentDefinitions - (Optional) Array of custom agent definitions. Each object should satisfy the AgentDefinition type. You can input the agent's id field into the agent parameter to run that agent.\n * @param customToolDefinitions - (Optional) Array of custom tool definitions that extend the agent's capabilities. Each tool definition includes a name, Zod schema for input validation, and a handler function. These tools can be called by the agent during execution.\n * @param maxAgentSteps - (Optional) Maximum number of steps the agent can take before stopping. Use this as a safety measure in case your agent starts going off the rails. A reasonable number is around 20.\n *\n * @returns A Promise that resolves to a RunState JSON object which you can pass to a subsequent run() call to continue the run. Use result.output to get the agent's output.\n */\n public async run({\n agent,\n prompt,\n params,\n handleEvent,\n handleStreamChunk,\n previousRun,\n projectFiles,\n knowledgeFiles,\n agentDefinitions,\n customToolDefinitions,\n maxAgentSteps = MAX_AGENT_STEPS_DEFAULT,\n extraToolResults,\n }: {\n agent: string\n prompt: string\n params?: Record<string, any>\n handleEvent?: (event: PrintModeEvent) => void\n handleStreamChunk?: (chunk: string) => void\n previousRun?: RunState\n projectFiles?: Record<string, string>\n knowledgeFiles?: Record<string, string>\n agentDefinitions?: AgentDefinition[]\n customToolDefinitions?: CustomToolDefinition[]\n maxAgentSteps?: number\n extraToolResults?: ToolResultPart[]\n }): Promise<RunState> {\n await this.websocketHandler.connect()\n\n const promptId = Math.random().toString(36).substring(2, 15)\n\n let sessionState: SessionState\n if (previousRun?.sessionState) {\n // applyOverridesToSessionState handles deep cloning and applying any provided overrides\n sessionState = await applyOverridesToSessionState(\n this.cwd,\n previousRun.sessionState,\n {\n knowledgeFiles,\n agentDefinitions,\n customToolDefinitions,\n projectFiles,\n maxAgentSteps,\n },\n )\n } else {\n // No previous run, so create a fresh session state\n sessionState = await initialSessionState(this.cwd, {\n knowledgeFiles,\n agentDefinitions,\n customToolDefinitions,\n projectFiles,\n maxAgentSteps,\n })\n }\n this.promptIdToHandlers[promptId] = {\n handleEvent,\n handleStreamChunk,\n }\n if (customToolDefinitions) {\n this.promptIdToHandlers[promptId].customToolHandler = async ({\n toolName,\n input,\n }) => {\n const toolDefs = customToolDefinitions.filter(\n (def) => def.toolName === toolName,\n )\n if (toolDefs.length === 0) {\n throw new Error(\n `Implementation for custom tool ${toolName} not found.`,\n )\n }\n const toolDef = toolDefs[toolDefs.length - 1]\n const handler = toolDef.execute\n try {\n return {\n output: toolDef.outputSchema.parse(\n await handler(toolDef.zodSchema.parse(input)),\n ),\n }\n } catch (error) {\n return {\n output: [\n {\n type: 'json',\n value: {\n errorMessage:\n error &&\n typeof error === 'object' &&\n 'message' in error &&\n typeof error.message === 'string'\n ? error.message\n : typeof error === 'string'\n ? error\n : 'Unknown error',\n },\n },\n ],\n }\n }\n }\n }\n this.websocketHandler.sendInput({\n promptId,\n prompt,\n promptParams: params,\n fingerprintId: this.fingerprintId,\n costMode: 'normal',\n sessionState,\n toolResults: extraToolResults ?? [],\n agentId: agent,\n })\n\n return new Promise<RunState>((resolve, reject) => {\n this.promptIdToHandlers[promptId].resolveResponse = { resolve, reject }\n })\n }\n\n private async handlePromptResponse(\n action: ServerAction<'prompt-response'> | ServerAction<'prompt-error'>,\n ) {\n const promptId =\n action.type === 'prompt-response' ? action.promptId : action.userInputId\n const promiseActions = this.promptIdToHandlers[promptId]?.resolveResponse\n if (!promiseActions) {\n return\n }\n\n delete this.promptIdToHandlers[promptId]\n\n if (action.type === 'prompt-error') {\n promiseActions.reject(new Error(action.message))\n } else if (action.type === 'prompt-response') {\n const parsedAction = PromptResponseSchema.safeParse(action)\n if (!parsedAction.success) {\n const message = [\n 'Received invalid prompt response from server:',\n JSON.stringify(parsedAction.error.issues),\n 'If this issues persists, please contact support@codebuff.com',\n ].join('\\n')\n promiseActions.reject(new Error(message))\n } else {\n const { sessionState, output } = parsedAction.data\n const state: RunState = {\n sessionState,\n output: output ?? {\n type: 'error',\n message: 'No output from agent',\n },\n }\n promiseActions.resolve(state)\n }\n }\n }\n\n private async readFiles({ filePaths }: { filePaths: string[] }) {\n const override = this.overrideTools.read_files\n if (override) {\n return await override({ filePaths })\n }\n return getFiles(filePaths, this.cwd)\n }\n\n private async handleToolCall(\n action: ServerAction<'tool-call-request'>,\n ): ReturnType<WebSocketHandler['handleToolCall']> {\n const toolName = action.toolName\n const input = action.input\n\n let result: ToolResultOutput[]\n if (toolNames.includes(toolName as ToolName)) {\n clientToolCallSchema.parse(action)\n } else {\n const customToolHandler =\n this.promptIdToHandlers[action.userInputId].customToolHandler\n if (!customToolHandler) {\n throw new Error(\n `Custom tool handler not found for user input ID ${action.userInputId}`,\n )\n }\n return await customToolHandler(action)\n }\n\n try {\n let override = this.overrideTools[toolName as PublishedClientToolName]\n if (!override && toolName === 'str_replace') {\n // Note: write_file and str_replace have the same implementation, so reuse their write_file override.\n override = this.overrideTools['write_file']\n }\n if (override) {\n result = await override(input as any)\n } else if (toolName === 'end_turn') {\n result = []\n } else if (toolName === 'write_file' || toolName === 'str_replace') {\n result = changeFile(input, this.cwd)\n } else if (toolName === 'run_terminal_command') {\n result = await runTerminalCommand({\n ...input,\n cwd: path.resolve(this.cwd, input.cwd ?? '.'),\n } as Parameters<typeof runTerminalCommand>[0])\n } else if (toolName === 'code_search') {\n result = await codeSearch({\n projectPath: this.cwd,\n ...input,\n } as Parameters<typeof codeSearch>[0])\n } else if (toolName === 'run_file_change_hooks') {\n // No-op: SDK doesn't run file change hooks\n result = [\n {\n type: 'json',\n value: {\n message: 'File change hooks are not supported in SDK mode',\n },\n },\n ]\n } else {\n throw new Error(\n `Tool not implemented in SDK. Please provide an override or modify your agent to not use this tool: ${toolName}`,\n )\n }\n } catch (error) {\n return {\n output: [\n {\n type: 'json',\n value: {\n errorMessage:\n error &&\n typeof error === 'object' &&\n 'message' in error &&\n typeof error.message === 'string'\n ? error.message\n : typeof error === 'string'\n ? error\n : 'Unknown error',\n },\n },\n ],\n }\n }\n return {\n output: result,\n }\n }\n}\n",
"import * as os from 'os'\n\nimport { getFileTokenScores } from '@codebuff/code-map/parse'\n\nimport { type CustomToolDefinition } from './custom-tool'\nimport { getInitialSessionState } from '../../common/src/types/session-state'\n\nimport type { AgentDefinition } from '../../common/src/templates/initial-agents-dir/types/agent-definition'\nimport type { Message } from '../../common/src/types/messages/codebuff-message'\nimport type {\n AgentOutput,\n SessionState,\n} from '../../common/src/types/session-state'\nimport type {\n CustomToolDefinitions,\n FileTreeNode,\n} from '../../common/src/util/file'\n\nexport type RunState = {\n sessionState: SessionState\n output: AgentOutput\n}\n\n/**\n * Processes agent definitions array and converts handleSteps functions to strings\n */\nfunction processAgentDefinitions(\n agentDefinitions: AgentDefinition[],\n): Record<string, any> {\n const processedAgentTemplates: Record<string, any> = {}\n agentDefinitions.forEach((definition) => {\n const processedConfig = { ...definition } as Record<string, any>\n if (\n processedConfig.handleSteps &&\n typeof processedConfig.handleSteps === 'function'\n ) {\n processedConfig.handleSteps = processedConfig.handleSteps.toString()\n }\n if (processedConfig.id) {\n processedAgentTemplates[processedConfig.id] = processedConfig\n }\n })\n return processedAgentTemplates\n}\n\n/**\n * Processes custom tool definitions into the format expected by SessionState\n */\nfunction processCustomToolDefinitions(\n customToolDefinitions: CustomToolDefinition[],\n): Record<\n string,\n Pick<CustomToolDefinition, keyof NonNullable<CustomToolDefinitions>[string]>\n> {\n return Object.fromEntries(\n customToolDefinitions.map((toolDefinition) => [\n toolDefinition.toolName,\n {\n inputJsonSchema: toolDefinition.inputJsonSchema,\n description: toolDefinition.description,\n endsAgentStep: toolDefinition.endsAgentStep,\n exampleInputs: toolDefinition.exampleInputs,\n },\n ]),\n )\n}\n\n/**\n * Computes project file indexes (file tree and token scores)\n */\nasync function computeProjectIndex(\n cwd: string,\n projectFiles: Record<string, string>,\n): Promise<{\n fileTree: FileTreeNode[]\n fileTokenScores: Record<string, any>\n tokenCallers: Record<string, any>\n}> {\n const filePaths = Object.keys(projectFil