src / firstTurnInjection.ts
src / firstTurnInjection.ts
/**
* First-turn / tools-documentation injection helpers.
* Soft token budget + category filtering.
*/
import { TOOLS_DOCUMENTATION, TOOLS_DOCUMENTATION_LITE } from "./toolsDocumentation";
const TOOL_KEYWORD_MAP: Record<string, string[]> = {
file_system: [
"file", "folder", "directory", "read", "write", "save", "delete",
"move", "copy", "edit", "create", "open",
],
code_execution: [
"run", "execute", "code", "script", "python", "javascript",
"terminal", "command", "shell", "npm", "node", "deno",
],
git_github: [
"git", "commit", "push", "pull", "branch", "repo", "github",
"issue", "pr", "merge", "diff", "log", "checkout", "add",
],
web_rag: [
"search", "web", "url", "fetch", "browse", "rag", "embed",
"vector", "wikipedia", "duckduckgo", "google", "bing",
],
browser: [
"browser", "page", "click", "type", "screenshot", "navigate",
"automation", "puppeteer",
],
neo4j: ["neo4j", "cypher", "graph", "node", "relationship", "query", "database"],
postgres: ["postgres", "postgresql", "sql", "database", "schema", "backup", "restore", "csv"],
email: ["email", "smtp", "mail", "send", "attach", "template", "verify"],
rss_feeds: ["rss", "feed", "atom", "subscribe", "monitor"],
image_pdf: ["image", "pdf", "resize", "convert", "thumbnail", "metadata", "webp", "sharp"],
media_video: ["video", "audio", "transcode", "gif", "ffmpeg", "media", "extract"],
archives: ["zip", "tar", "archive", "extract", "compress", "unzip"],
data_formats: ["csv", "json", "yaml", "xml", "convert", "schema", "merge", "diff"],
timekeeping: ["timer", "cron", "calendar", "time", "log", "reminder", "event"],
utilities: [
"hash", "uuid", "regex", "url", "merge", "config", "clipboard",
"system", "info", "metadata", "preview", "document",
],
agent_mgmt: ["agent", "delegate", "workflow", "parallel", "skill", "eval", "context", "memory"],
};
const SECTION_TO_CATEGORY: Record<string, string[]> = {
"File System": ["file_system"],
"Git & Version Control": ["git_github"],
"Advanced File Tools": ["file_system", "utilities"],
"Execution & Terminal": ["code_execution"],
"Web & Research": ["web_rag", "browser"],
"System & Utility": ["utilities"],
"Agents & Memory": ["agent_mgmt"],
};
const ALWAYS_INCLUDE = new Set(["file_system", "utilities", "agent_mgmt"]);
export function getRelevantToolCategories(query: string): string[] {
const lower = query.toLowerCase();
const relevant = new Set<string>();
for (const [category, keywords] of Object.entries(TOOL_KEYWORD_MAP)) {
if (keywords.some((kw) => lower.includes(kw))) {
relevant.add(category);
}
}
for (const c of ALWAYS_INCLUDE) relevant.add(c);
return Array.from(relevant);
}
export function filterToolsDocumentation(
docLines: string[],
categories: string[],
): string[] {
const result: string[] = [];
let inRelevantSection = false;
let pastPreamble = false;
for (const line of docLines) {
const sectionMatch = line.match(/^###\s+(.+)$/);
if (sectionMatch) {
pastPreamble = true;
inRelevantSection = false;
const sectionName = sectionMatch[1].trim();
const mapped = SECTION_TO_CATEGORY[sectionName];
if (mapped && categories.some((c) => mapped.includes(c))) {
inRelevantSection = true;
result.push(line);
}
continue;
}
if (!pastPreamble) {
result.push(line);
continue;
}
if (inRelevantSection) {
if (/^###\s+/.test(line)) {
inRelevantSection = false;
} else {
result.push(line);
}
}
}
return result;
}
/**
* Build the documentation block for the first turn.
* TOOLS_DOCUMENTATION / LITE are already joined strings.
*/
export function buildFirstTurnDocs(
userPrompt: string,
simpleMode: boolean,
maxApproxTokens = 6_000,
): { content: string; usedLite: boolean; categories: string[] } {
if (simpleMode) {
return {
content: TOOLS_DOCUMENTATION_LITE,
usedLite: true,
categories: [],
};
}
const categories = getRelevantToolCategories(userPrompt);
const fullLines = TOOLS_DOCUMENTATION.split("\n");
let filtered = filterToolsDocumentation(fullLines, categories);
if (filtered.length < fullLines.length * 0.35) {
filtered = fullLines;
}
let content = filtered.join("\n");
let usedLite = false;
const approxTokens = Math.ceil(content.length / 4);
if (approxTokens > maxApproxTokens) {
content = TOOLS_DOCUMENTATION_LITE;
usedLite = true;
}
return { content, usedLite, categories };
}
/**
* First-turn / tools-documentation injection helpers.
* Soft token budget + category filtering.
*/
import { TOOLS_DOCUMENTATION, TOOLS_DOCUMENTATION_LITE } from "./toolsDocumentation";
const TOOL_KEYWORD_MAP: Record<string, string[]> = {
file_system: [
"file", "folder", "directory", "read", "write", "save", "delete",
"move", "copy", "edit", "create", "open",
],
code_execution: [
"run", "execute", "code", "script", "python", "javascript",
"terminal", "command", "shell", "npm", "node", "deno",
],
git_github: [
"git", "commit", "push", "pull", "branch", "repo", "github",
"issue", "pr", "merge", "diff", "log", "checkout", "add",
],
web_rag: [
"search", "web", "url", "fetch", "browse", "rag", "embed",
"vector", "wikipedia", "duckduckgo", "google", "bing",
],
browser: [
"browser", "page", "click", "type", "screenshot", "navigate",
"automation", "puppeteer",
],
neo4j: ["neo4j", "cypher", "graph", "node", "relationship", "query", "database"],
postgres: ["postgres", "postgresql", "sql", "database", "schema", "backup", "restore", "csv"],
email: ["email", "smtp", "mail", "send", "attach", "template", "verify"],
rss_feeds: ["rss", "feed", "atom", "subscribe", "monitor"],
image_pdf: ["image", "pdf", "resize", "convert", "thumbnail", "metadata", "webp", "sharp"],
media_video: ["video", "audio", "transcode", "gif", "ffmpeg", "media", "extract"],
archives: ["zip", "tar", "archive", "extract", "compress", "unzip"],
data_formats: ["csv", "json", "yaml", "xml", "convert", "schema", "merge", "diff"],
timekeeping: ["timer", "cron", "calendar", "time", "log", "reminder", "event"],
utilities: [
"hash", "uuid", "regex", "url", "merge", "config", "clipboard",
"system", "info", "metadata", "preview", "document",
],
agent_mgmt: ["agent", "delegate", "workflow", "parallel", "skill", "eval", "context", "memory"],
};
const SECTION_TO_CATEGORY: Record<string, string[]> = {
"File System": ["file_system"],
"Git & Version Control": ["git_github"],
"Advanced File Tools": ["file_system", "utilities"],
"Execution & Terminal": ["code_execution"],
"Web & Research": ["web_rag", "browser"],
"System & Utility": ["utilities"],
"Agents & Memory": ["agent_mgmt"],
};
const ALWAYS_INCLUDE = new Set(["file_system", "utilities", "agent_mgmt"]);
export function getRelevantToolCategories(query: string): string[] {
const lower = query.toLowerCase();
const relevant = new Set<string>();
for (const [category, keywords] of Object.entries(TOOL_KEYWORD_MAP)) {
if (keywords.some((kw) => lower.includes(kw))) {
relevant.add(category);
}
}
for (const c of ALWAYS_INCLUDE) relevant.add(c);
return Array.from(relevant);
}
export function filterToolsDocumentation(
docLines: string[],
categories: string[],
): string[] {
const result: string[] = [];
let inRelevantSection = false;
let pastPreamble = false;
for (const line of docLines) {
const sectionMatch = line.match(/^###\s+(.+)$/);
if (sectionMatch) {
pastPreamble = true;
inRelevantSection = false;
const sectionName = sectionMatch[1].trim();
const mapped = SECTION_TO_CATEGORY[sectionName];
if (mapped && categories.some((c) => mapped.includes(c))) {
inRelevantSection = true;
result.push(line);
}
continue;
}
if (!pastPreamble) {
result.push(line);
continue;
}
if (inRelevantSection) {
if (/^###\s+/.test(line)) {
inRelevantSection = false;
} else {
result.push(line);
}
}
}
return result;
}
/**
* Build the documentation block for the first turn.
* TOOLS_DOCUMENTATION / LITE are already joined strings.
*/
export function buildFirstTurnDocs(
userPrompt: string,
simpleMode: boolean,
maxApproxTokens = 6_000,
): { content: string; usedLite: boolean; categories: string[] } {
if (simpleMode) {
return {
content: TOOLS_DOCUMENTATION_LITE,
usedLite: true,
categories: [],
};
}
const categories = getRelevantToolCategories(userPrompt);
const fullLines = TOOLS_DOCUMENTATION.split("\n");
let filtered = filterToolsDocumentation(fullLines, categories);
if (filtered.length < fullLines.length * 0.35) {
filtered = fullLines;
}
let content = filtered.join("\n");
let usedLite = false;
const approxTokens = Math.ceil(content.length / 4);
if (approxTokens > maxApproxTokens) {
content = TOOLS_DOCUMENTATION_LITE;
usedLite = true;
}
return { content, usedLite, categories };
}