This commit is contained in:
seb
2026-10-06 03:18:30 +02:00
parent 159f02992b
commit 0369b4e819
2 changed files with 27 additions and 24 deletions

View File

@@ -117,7 +117,7 @@ async function buildSystemPrompt(vfsRoot, googleAuthId) {
'6. patch: Create, overwrite, or search-and-replace text in a VFS file. This is the only text write path — it also creates SKILL files.', '6. patch: Create, overwrite, or search-and-replace text in a VFS file. This is the only text write path — it also creates SKILL files.',
'7. search_images: Search Google Images and store the candidates in the VFS as AVIF files.', '7. search_images: Search Google Images and store the candidates in the VFS as AVIF files.',
'8. analyze_image: Have a vision model look at up to 9 images (VFS paths or "image/<fullHash>") and return its judgement. This is how you "see" — never claim to have inspected an image yourself.', '8. analyze_image: Have a vision model look at up to 9 images (VFS paths or "image/<fullHash>") and return its judgement. This is how you "see" — never claim to have inspected an image yourself.',
'9. web_search: Search the web using Google Search. Returns organic results, snippets, and knowledge graph. Supports time filters via `tbs` ("qdr:d" past day, "qdr:w" past week, "qdr:m" past month, "qdr:y" past year).', '9. web_search: Search the web using Google Search. Returns organic results, snippets, and knowledge graph. Supports filtering by timePeriod ("day", "week", "month", "year") — always use "day" or "week" for news and recent events.',
'10. url_get: Fetch and scrape the content and markdown of a web page/URL.', '10. url_get: Fetch and scrape the content and markdown of a web page/URL.',
'11. pdf_read: Read and convert PDF or text files. Converts PDF to layout-preserved text ("txt") or renders pages into PNG images ("png") saved in the VFS images/ folder.', '11. pdf_read: Read and convert PDF or text files. Converts PDF to layout-preserved text ("txt") or renders pages into PNG images ("png") saved in the VFS images/ folder.',
'12. reset_vfs: Delete files, images, and artifacts stored in the virtual filesystem (VFS) to free up storage or clean up. SKILL files are exempt and survive.', '12. reset_vfs: Delete files, images, and artifacts stored in the virtual filesystem (VFS) to free up storage or clean up. SKILL files are exempt and survive.',

View File

@@ -5,30 +5,30 @@ const SERPER_SEARCH_ENDPOINT = 'https://google.serper.dev/search';
const SEARCH_TIMEOUT_MS = 15_000; const SEARCH_TIMEOUT_MS = 15_000;
/** /**
* Normalizes user/model-supplied time filter (`tbs`) into Google's qdr syntax. * Normalizes user/model-supplied time filter into Google's qdr syntax.
* Supported: * Supported:
* - "qdr:d" or "d" / "day" / "24h" -> "qdr:d" * - "day" / "24h" / "d" -> "qdr:d"
* - "qdr:w" or "w" / "week" -> "qdr:w" * - "week" / "w" -> "qdr:w"
* - "qdr:m" or "m" / "month" -> "qdr:m" * - "month" / "m" -> "qdr:m"
* - "qdr:y" or "y" / "year" -> "qdr:y" * - "year" / "y" -> "qdr:y"
* - "qdr:h" or "h" / "hour" -> "qdr:h" * - "hour" / "h" -> "qdr:h"
* *
* @param {string|null} tbs * @param {string|null} time
* @returns {string|undefined} * @returns {string|undefined}
*/ */
function normalizeTbs(tbs) { function normalizeTbs(time) {
if (!tbs || typeof tbs !== 'string') return undefined; if (!time || typeof time !== 'string') return undefined;
const trimmed = tbs.trim().toLowerCase(); const trimmed = time.trim().toLowerCase();
if (!trimmed) return undefined; if (!trimmed) return undefined;
if (trimmed === 'day' || trimmed === 'd' || trimmed === '24h' || trimmed === 'today') return 'qdr:d';
if (trimmed === 'week' || trimmed === 'w') return 'qdr:w';
if (trimmed === 'month' || trimmed === 'm') return 'qdr:m';
if (trimmed === 'year' || trimmed === 'y') return 'qdr:y';
if (trimmed === 'hour' || trimmed === 'h') return 'qdr:h';
if (trimmed.startsWith('qdr:')) return trimmed; if (trimmed.startsWith('qdr:')) return trimmed;
if (trimmed === 'd' || trimmed === 'day' || trimmed === '24h') return 'qdr:d';
if (trimmed === 'w' || trimmed === 'week') return 'qdr:w';
if (trimmed === 'm' || trimmed === 'month') return 'qdr:m';
if (trimmed === 'y' || trimmed === 'year') return 'qdr:y';
if (trimmed === 'h' || trimmed === 'hour') return 'qdr:h';
return trimmed; return undefined;
} }
/** /**
@@ -91,8 +91,9 @@ export function createWebSearchTool({ googleAuthId }) {
description: description:
'Search the web using Google Search via Serper.dev. ' + 'Search the web using Google Search via Serper.dev. ' +
'Returns raw search results as-is from Serper. ' + 'Returns raw search results as-is from Serper. ' +
'Supports time filters via `tbs` ("qdr:d" for past day, "qdr:w" for past week, ' + 'Supports filtering fresh content with timePeriod ("day", "week", "month", "year"). ' +
'"qdr:m" for past month, "qdr:y" for past year). Each page returns 10 results.', 'Always use "day" or "week" when searching for news, recent events, or current status. ' +
'Each page returns 10 results.',
strict: false, strict: false,
parameters: { parameters: {
type: 'object', type: 'object',
@@ -103,11 +104,13 @@ export function createWebSearchTool({ googleAuthId }) {
type: 'string', type: 'string',
description: 'Search query, e.g. "apple inc" or "vite vue3 release".', description: 'Search query, e.g. "apple inc" or "vite vue3 release".',
}, },
tbs: { timePeriod: {
type: 'string', type: 'string',
enum: ['day', 'week', 'month', 'year'],
description: description:
'Time filter for results: "qdr:d" (past 24h / day), "qdr:w" (past week), ' + 'Filter results by time period: "day" (past 24 hours), "week" (past week), ' +
'"qdr:m" (past month), "qdr:y" (past year). Short forms like "d", "w", "m", "y" are also accepted.', '"month" (past month), or "year" (past year). ' +
'Always use "day" or "week" when searching for news, recent events, or current status.',
}, },
gl: { gl: {
type: 'string', type: 'string',
@@ -123,7 +126,7 @@ export function createWebSearchTool({ googleAuthId }) {
}, },
}, },
}, },
invoke: async ({ query, tbs, gl = 'de', hl = 'de', page = 1 } = {}, { signal } = {}) => { invoke: async ({ query, timePeriod, tbs, gl = 'de', hl = 'de', page = 1 } = {}, { signal } = {}) => {
const term = typeof query === 'string' ? query.trim() : ''; const term = typeof query === 'string' ? query.trim() : '';
if (!term) { if (!term) {
throw new ToolError('web_search requires a non-empty "query".'); throw new ToolError('web_search requires a non-empty "query".');
@@ -134,7 +137,7 @@ export function createWebSearchTool({ googleAuthId }) {
throw new ToolError(`"page" must be a positive integer (received ${page}).`); throw new ToolError(`"page" must be a positive integer (received ${page}).`);
} }
const normalizedTbs = normalizeTbs(tbs); const normalizedTbs = normalizeTbs(timePeriod || tbs);
let apiKey = null; let apiKey = null;
try { try {