import "server-only"; import type { ToolDef } from "../ai/types"; import { webSearch, fetchPage } from "./web"; import { runPython } from "./python"; import { searchDocuments } from "./documents"; import { generateImageTool } from "./image"; import { saveMemoryTool } from "./memory"; export type Permission = "auto" | "approval"; export interface ToolContext { conversationId: string; projectId: string | null; messageId: string; signal?: AbortSignal; allow: Set } export interface Tool extends ToolDef { permission: Permission; category: "web" | "code" | "documents" | "media" | "memory"; run(input: Record, ctx: ToolContext): Promise; } export const TOOLS: Tool[] = [ { name: "web_search", category: "web", permission: "auto", description: "Search the web (Tavily). Returns titles, URLs and snippets. Use for current events, facts to verify, sources to cite.", inputSchema: { type: "object", properties: { query: { type: "string" }, max_results: { type: "integer", minimum: 1, maximum: 10 }, recency_days: { type: "integer", description: "Only results newer than N days" } }, required: ["query"], additionalProperties: false }, run: (i) => webSearch(String(i.query), { maxResults: Number(i.max_results ?? 6), recencyDays: i.recency_days ? Number(i.recency_days) : undefined }), }, { name: "fetch_page", category: "web", permission: "auto", description: "Fetch a public web page and return its readable text (truncated to ~20k chars). Only use on URLs you obtained from search results or the user.", inputSchema: { type: "object", properties: { url: { type: "string" } }, required: ["url"], additionalProperties: false }, run: (i) => fetchPage(String(i.url)), }, { name: "python", category: "code", permission: "auto", description: "Execute a Python 3 script in an isolated sandbox (no network, 20 s timeout, temp dir). Print results to stdout. numpy/pandas available if installed on the host. Use for calculations, data analysis, simulations.", inputSchema: { type: "object", properties: { code: { type: "string" } }, required: ["code"], additionalProperties: false }, run: (i, ctx) => runPython(String(i.code), ctx.signal), }, { name: "search_documents", category: "documents", permission: "auto", description: "Semantic + keyword search over the documents attached to this conversation or project. Returns the most relevant passages with file names and pages.", inputSchema: { type: "object", properties: { query: { type: "string" }, limit: { type: "integer", minimum: 1, maximum: 12 } }, required: ["query"], additionalProperties: false }, run: (i, ctx) => searchDocuments(String(i.query), ctx, Number(i.limit ?? 6)), }, { name: "generate_image", category: "media", permission: "auto", description: "Generate an image with OpenAI gpt-image (verified). Returns an attachment id to display. Keep prompts descriptive.", inputSchema: { type: "object", properties: { prompt: { type: "string" }, size: { type: "string", enum: ["1024x1024", "1536x1024", "1024x1536"] }, quality: { type: "string", enum: ["low", "medium", "high"] } }, required: ["prompt"], additionalProperties: false }, run: (i, ctx) => generateImageTool(String(i.prompt), ctx, { size: i.size as string | undefined, quality: i.quality as string | undefined }), }, { name: "save_memory", category: "memory", permission: "auto", description: "Propose a durable memory about the owner (preference, project fact, infrastructure detail) for future conversations. Never store secrets, passwords or sensitive personal data.", inputSchema: { type: "object", properties: { content: { type: "string" }, category: { type: "string", enum: ["preference", "writing", "research", "project", "infrastructure", "objective", "concept", "context"] } }, required: ["content"], additionalProperties: false }, run: (i, ctx) => saveMemoryTool(String(i.content), String(i.category ?? "context"), ctx), }, ]; export function toolByName(name: string): Tool | undefined { return TOOLS.find(t => t.name === name); } export function toolDefs(names?: string[]): ToolDef[] { return TOOLS.filter(t => !names || names.includes(t.name)).map(({ name, description, inputSchema }) => ({ name, description, inputSchema })); }