diff --git a/src/services/conversationManager.js b/src/services/conversationManager.js index 6358280..ccc77db 100644 --- a/src/services/conversationManager.js +++ b/src/services/conversationManager.js @@ -105,52 +105,51 @@ async function buildSystemPrompt(vfsRoot, googleAuthId) { } const today = new Date().toISOString().split('T')[0]; const basePrompt = [ - 'You are an intelligent assistant specialized in exploring, querying, and managing data from the Dashback REST API.', - `Today's date: ${today}`, + 'Assistant for QuixPOS Article Management System REST API operation', + `Date: ${today}`, '', 'YOUR TOOLS:', - '1. dashback_request: Execute HTTP requests (GET, POST, PUT, PATCH, DELETE) against Dashback endpoints.', - '2. ask_user: Ask clarifying questions or request decisions from the user if their intent is ambiguous or requires choices. When you call ask_user, execution pauses immediately and waits for the user to provide their response.', - '3. javascript_execute: Run JavaScript snippets in a sandbox to compute, aggregate, transform, or analyze data. Assign module.exports to return a value. Snippets can call Dashback API routes using fetch() (e.g. await fetch("/article/2")). A sandboxed virtual filesystem (fs / vfs) is available to store and retrieve artifacts across runs.', - '4. find_files: List what is stored in the virtual filesystem (VFS). Call find_files({ skills: true }) to list SKILL files only.', - '5. read_file: Read a VFS file as text, windowed with seek/limit for large 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.', - '8. analyze_image: Have a vision model look at up to 9 images (VFS paths or "image/") 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 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.', - '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.', - '13. rm: Delete a single file from the VFS. This is how a SKILL file is deleted on purpose, since reset_vfs always spares skills.', - '14. apisuggestionsorissues: Report missing API endpoints, bugs, or issues to be triaged. Automatically writes an entry to the company history table for review.', + '1. dashback_request: HTTP requests (GET, POST, PUT, PATCH, DELETE) against Dashback endpoints.', + '2. ask_user: Prompt user for clarification or decisions. Pauses execution until response.', + '3. javascript_execute: Sandboxed Node environment for computation, aggregation, and binary I/O. Assign module.exports. API: fetch("/path"). VFS: fs/vfs.', + '4. find_files: List VFS files. Set skills=true for SKILL files.', + '5. read_file: Read VFS text file (seek/limit windowed).', + '6. patch: Create, overwrite, or replace text in VFS files (only text write path; creates SKILL files).', + '7. search_images: Search Google Images; downloads AVIF candidates to VFS.', + '8. analyze_image: Inspect up to 9 images via vision model (VFS paths or "image/"). Always evaluate images through this tool.', + '9. web_search: Web search via Google. Filter with timePeriod ("day"/"week" for current events).', + '10. url_get: Fetch and scrape web page content/markdown.', + '11. pdf_read: Convert PDF/text to layout text ("txt") or VFS page images ("png").', + '12. reset_vfs: Delete VFS files/artifacts. Preserves SKILL files.', + '13. rm: Delete single VFS file (used to delete SKILL files or specific artifacts).', + '14. apisuggestionsorissues: Report missing API endpoints, bugs, or issues for triage.', '', 'VIRTUAL FILESYSTEM:', - '- You have a persistent per-company VFS. Paths are relative to the VFS root and always use forward slashes, e.g. "labels/sheet1.png".', - '- To READ text: find_files (list) then read_file (content). To WRITE text: patch. Binary artifacts (images, PDFs, buffers) go through javascript_execute.', - '- Never invent a VFS path. Always confirm with find_files before reading or referencing a file.', + '- Persistent per-company VFS. Paths are root-relative forward-slash (e.g. "labels/sheet1.png").', + '- Text: list with find_files, read with read_file, write with patch. Binary/buffers: javascript_execute.', + '- Never invent paths; verify with find_files before reading or referencing.', '', 'SKILL FILES:', - '- A SKILL file is a reusable instruction document: a file named ".SKILL" at the VFS root whose FIRST LINE is its title and whose remaining lines are markdown.', - '- SKILL file names may NOT contain "/" — skills are flat, at the root only. Use find_files({ skills: true }) to discover them; this listing is not affected by pagination or by how many artifacts exist.', - '- Read a skill with read_file BEFORE starting a task it covers, and follow it. If you learn a better procedure during the task, offer to write it back as a new SKILL file.', - '- SKILL files are exempt from reset_vfs: artifacts are disposable, skills are not. Never call reset_vfs expecting to clear skills, and never put throwaway output in a .SKILL file.', - '- To delete a skill (or any single file) use rm — it is the only way to remove one. Deletion is final.', + '- Reusable instruction docs: ".SKILL" at VFS root. First line is title, followed by markdown.', + '- Root-only (no "/"). Discover via find_files({ skills: true }).', + '- Read with read_file BEFORE starting covered tasks; follow instructions. Propose saving improved procedures as new SKILL files.', + '- Exempt from reset_vfs. Delete permanently via rm.', '', buildSkillIndex(vfsRoot), '', 'IMAGES:', - '- You cannot see images yourself. To evaluate one, call analyze_image and report what the vision model says.', - '- Typical flow: search_images("query") → analyze_image({ paths: [...candidates], prompt: "…" }) → upload the winner with dashback_request.', - '- The `image/` folder holds database images and is READ-ONLY. Generated images go anywhere else (e.g. "images/…").', + '- Evaluate images via analyze_image; report model findings.', + '- Flow: search_images("query") → analyze_image({ paths: [...], prompt: "…" }) → upload via dashback_request.', + '- `image/` is READ-ONLY database storage. Write generated images elsewhere (e.g. "images/…").', '', 'OPERATING GUIDELINES:', - '- Refer to the DASHBACK API ROUTES documentation below to find appropriate endpoints, parameters, and HTTP methods.', - '- SELLING PRICES: In article responses (GET /article/{sku} and returned article), the selling price field is named net (NOT price). The structure of entries in article.prices is { minmultiplier, net, taxRate, customergroupId }.', - '- GROSS vs NET PRICES: Users almost always mean GROSS (brutto, incl. VAT) prices when they specify a price (e.g. "1,60 €"), but the API stores and accepts NET prices only. Convert: net = gross / (1 + taxRate/100). Send net with high precision (at least 4–6 decimal places) so rounding back gives the exact gross. Calculate this in javascript_execute.', - '- DATA TYPES & WIRE FORMAT: Dashback strictly validates types (numeric values like weight, price, quantity, IDs must be numbers; flags must be booleans).', - '- SHOWING GENERATED IMAGES: Markdown images ARE supported and render inline in the chat. After writing an image into the virtual filesystem (e.g. `fs.writeFileSync("artifacts/example.png", buf)`), reference the exact path you wrote with standard markdown `![title](artifacts/example.png)`, which renders inline. Make the alt text descriptive. Other file types (PDF, JSON, CSV) are NOT rendered inline — link to them instead and offer them as a download.', - '- LABEL TEMPLATES (/labeltemplate): Label templates are NOT ZPL. content is Dashback\'s JSON label schema: { "objects": [ ... ] }. Do NOT write or suggest ZPL.', - '- Format responses clearly using markdown tables, bullet lists, or bold key-value pairs where appropriate.', + '- Endpoint reference: see DASHBACK API ROUTES below.', + '- SELLING PRICES: In article responses (GET /article/{sku}), selling price field is `net` (NOT `price`). Schema in `article.prices`: { minmultiplier, net, taxRate, customergroupId }.', + '- GROSS vs NET PRICES: User prices are almost always GROSS (incl. VAT); API requires NET. Formula: `net = gross / (1 + taxRate / 100)`. Compute in javascript_execute with high precision (4–6 decimals) to prevent rounding error.', + '- WIRE TYPES: Strict validation (IDs, weight, price, quantity must be numbers; flags booleans).', + '- DISPLAYING GENERATED IMAGES: Inline markdown images supported. Embed written VFS image paths: `![descriptive alt](artifacts/example.png)`. Other files (PDF, JSON, CSV) do not render inline; provide download links.', + '- LABEL TEMPLATES (/labeltemplate): Must use Dashback JSON schema `{ "objects": [ ... ] }`. Do NOT use ZPL.', + '- Format output with markdown tables, bullet points, or bold key-value pairs.', ...(routesDoc ? ['', 'DASHBACK API ROUTES:', routesDoc] : []), ].join('\n'); diff --git a/src/tools/analyze-image.js b/src/tools/analyze-image.js index 8960d00..29a2bad 100644 --- a/src/tools/analyze-image.js +++ b/src/tools/analyze-image.js @@ -261,12 +261,8 @@ export function createAnalyzeImageTool({ googleAuthId, model }) { return defineTool({ name: 'analyze_image', description: - 'Look at one or up to 9 images with a vision model and get its written judgement back. ' + - 'Paths are VFS-relative (e.g. "images/bild.de-1.avif" from search_images) or ' + - '"image/" for a database image. ' + - 'Passing several paths renders them as a labelled grid in one call — use that to compare ' + - 'search candidates side by side. The returned text is the vision model\'s answer, so judge from it ' + - 'rather than claiming to have seen the image yourself.', + 'Inspect 1–9 images via vision model. Multiple paths render as a labelled comparison grid. ' + + 'Paths: VFS-relative ("images/foo.avif") or DB ("image/"). Returns model evaluation.', strict: false, parameters: { type: 'object', @@ -275,23 +271,19 @@ export function createAnalyzeImageTool({ googleAuthId, model }) { properties: { paths: { type: 'array', - description: - '1–9 image paths. VFS files (e.g. ["images/a-1.avif"]) or database images (e.g. ["image/"]). ' + - 'Several paths are rendered as one labelled grid.', + description: '1–9 VFS paths or DB paths ("image/").', items: { type: 'string' }, minItems: 1, maxItems: MAX_IMAGES, }, prompt: { type: 'string', - description: - 'What to inspect or compare, e.g. "Which of these is a clean product shot on white, ' + - 'without watermark or text overlay?"', + description: 'Inspection criteria or question.', }, grid: { type: 'string', enum: Object.keys(GRID_CONFIGS), - description: 'Force a grid layout. Omit to pick one from the image count.', + description: 'Grid layout. Omit for auto.', }, }, }, diff --git a/src/tools/apisuggestionsorissues.js b/src/tools/apisuggestionsorissues.js index 0853721..aa8f8c1 100644 --- a/src/tools/apisuggestionsorissues.js +++ b/src/tools/apisuggestionsorissues.js @@ -11,10 +11,7 @@ import * as db from '../db/company.js'; export function createApiSuggestionsOrIssuesTool({ googleAuthId, companyId }) { return defineTool({ name: 'apisuggestionsorissues', - description: - 'Report a missing API endpoint, feature request, bug, or general issue. ' + - 'The report is recorded in the company history table for review by the triage agent. ' + - 'Provide a clear description of the missing functionality or issue.', + description: 'Report missing API endpoint, feature request, bug, or issue for triage.', strict: false, parameters: { type: 'object', @@ -23,16 +20,16 @@ export function createApiSuggestionsOrIssuesTool({ googleAuthId, companyId }) { properties: { text: { type: 'string', - description: 'Detailed description of the missing API, bug, or issue.', + description: 'Issue or request details.', }, type: { type: 'string', enum: ['missing_api', 'bug', 'suggestion', 'issue', 'other'], - description: 'Category/type of the report.', + description: 'Report category.', }, title: { type: 'string', - description: 'Optional short summary or title.', + description: 'Short summary.', }, }, }, diff --git a/src/tools/ask-user.js b/src/tools/ask-user.js index 4c35e2a..1c6092e 100644 --- a/src/tools/ask-user.js +++ b/src/tools/ask-user.js @@ -14,8 +14,7 @@ export function createAskUserTool() { return defineTool({ name: 'ask_user', description: - 'Ask the user when you need clarification or a decision (e.g. which SKU or ID they meant, or what parameter to use). ' + - 'Give a concise question + up to 3 short options (A,B,C); the user may select an option or provide free-form text.', + 'Prompt user for clarification or decision with concise question and up to 3 options.', strict: false, parameters: { type: 'object', @@ -24,11 +23,11 @@ export function createAskUserTool() { properties: { question: { type: 'string', - description: 'Question for the user.', + description: 'Question prompt.', }, options: { type: 'array', - description: 'Up to 3 short option labels.', + description: 'Up to 3 option labels.', items: { type: 'string' }, maxItems: 3, }, diff --git a/src/tools/dashback-request.js b/src/tools/dashback-request.js index b4410c3..a9a6db8 100644 --- a/src/tools/dashback-request.js +++ b/src/tools/dashback-request.js @@ -14,19 +14,17 @@ const METHODS = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE']; export function createDashbackRequestTool({ googleAuthId }) { return defineTool({ name: 'dashback_request', - description: - 'Interrogate and call the Dashback REST API. Supports GET requests directly. ' + - 'Paths are relative to the API root (e.g. "/article/2", "/category/tree", "/brand").', + description: 'Call Dashback REST API. Paths are root-relative (e.g. "/article/2").', strict: false, parameters: { type: 'object', additionalProperties: false, required: ['method', 'path'], properties: { - method: { type: 'string', enum: METHODS, description: 'HTTP method' }, - path: { type: 'string', description: 'Path of the endpoint to request (e.g. "/article/2", "/category/tree", "/brand").' }, - query: { type: 'object', description: 'Optional query parameters as key-value pairs (e.g. { limit: 10, offset: 0 }).' }, - body: { type: 'string', description: 'Raw JSON request payload string (e.g. \'{"weight": 2.5}\').' }, + method: { type: 'string', enum: METHODS, description: 'HTTP method.' }, + path: { type: 'string', description: 'Endpoint path (e.g. "/article/2").' }, + query: { type: 'object', description: 'Query parameters object.' }, + body: { type: 'string', description: 'JSON request payload string.' }, }, }, invoke: async ({ method = 'GET', path, query = null, body = null }) => { diff --git a/src/tools/find-files.js b/src/tools/find-files.js index 141f1c6..9267088 100644 --- a/src/tools/find-files.js +++ b/src/tools/find-files.js @@ -81,13 +81,8 @@ export function createFindFilesTool({ googleAuthId }) { return defineTool({ name: 'find_files', description: - 'List files stored in the virtual filesystem (VFS). ' + - 'VFS paths are relative to the VFS root and use forward slashes, e.g. "labels/sheet1.png". ' + - 'Set skills=true to list ONLY the SKILL files: reusable, hand-authored instruction ' + - 'documents that are EXEMPT from reset_vfs. A SKILL file is a flat, root-level file ' + - 'named ".SKILL" whose first line is its title and whose remaining lines are markdown. ' + - 'Read a skill with read_file before acting on a task it covers. ' + - 'Pass no arguments to list the VFS root.', + 'List VFS files. Root-relative forward-slash paths. ' + + 'Set skills=true to list root-level ".SKILL" files (reusable instructions exempt from reset_vfs; read via read_file).', strict: false, parameters: { type: 'object', @@ -95,23 +90,19 @@ export function createFindFilesTool({ googleAuthId }) { properties: { dirname: { type: 'string', - description: - 'Directory to list, relative to the VFS root (e.g. "labels"). Defaults to the root ".". ' + - 'Must be "." or empty when skills=true — SKILL files always live at the root.', + description: 'VFS directory to list. Default "." (root). Must be "." or empty if skills=true.', }, recursive: { type: 'boolean', - description: 'true = walk the whole subtree. Default false (immediate contents only).', + description: 'Walk subdirectories. Default false.', }, skills: { type: 'boolean', - description: - 'true = return ONLY SKILL files at the VFS root with their titles parsed. ' + - 'Overrides dirname/recursive. Default false.', + description: 'List only root SKILL files with parsed titles. Overrides dirname/recursive. Default false.', }, seek: { type: 'number', - description: 'Entries to skip (pagination). Default 0.', + description: 'Pagination offset. Default 0.', }, limit: { type: 'number', @@ -119,7 +110,7 @@ export function createFindFilesTool({ googleAuthId }) { }, maxDepth: { type: 'number', - description: 'Max directory depth to descend when recursive=true. Default 3.', + description: 'Max recursive directory depth. Default 3.', }, }, }, diff --git a/src/tools/javascript.js b/src/tools/javascript.js index 74300c7..8840a4b 100644 --- a/src/tools/javascript.js +++ b/src/tools/javascript.js @@ -122,29 +122,11 @@ export function createJavascriptExecuteTool({ googleAuthId }) { return defineTool({ name: 'javascript_execute', description: - 'Run JavaScript in a sandboxed Node environment and return the result. ' + - 'Runs in an isolated worker thread with a hard wall-clock timeout. ' + - 'Return a value by assigning `module.exports` (sync value or a promise). ' + - 'For async code: `module.exports = (async () => { ... })()`. ' + - 'console.* is captured and returned as stdout. ' + - 'To query the Dashback REST API from JavaScript: call `await fetch("/route")` (e.g. `const res = await fetch("/article/2"); const data = await res.json();`). ' + - 'Relative paths automatically resolve against Dashback and are authenticated with your user credentials. ' + - 'A virtual filesystem (fs / vfs) is available to store and retrieve artifacts (files, images, JSON data, buffers) across runs (e.g. `fs.writeFileSync("artifact.json", data)`). ' + -'IMAGES WRITTEN TO THE VFS ARE DISPLAYED INLINE IN THE CHAT. After writing an image into the virtual filesystem (e.g. `fs.writeFileSync("artifacts/example.png", buf)`), ' + - 'reference the exact path you wrote with standard markdown `![title](artifacts/example.png)` and it renders as an image inline. ' + - 'Note the `image/` folder is reserved for database images and is read-only; write generated images anywhere else. ' + - 'Always embed images you generate this way so the user sees them. Other file types do not render inline. ' + - 'For TEXT use the dedicated find_files / read_file / patch tools instead of hand-rolling fs calls — they validate paths and persist to the database. ' + - 'Use this sandbox for BINARY work and computation: images, PDFs, buffers, charts, encoding. ' + - 'To find or inspect images, use the search_images and analyze_image tools — they are not available inside the sandbox. ' + - 'The `sharp` image library is a pre-injected GLOBAL. ' + - 'Also global: `validateLabelObjects(objects)` and `validateLabelObject(object)` from react-canvas-label-editor. ' + - 'CRYPTO: only the WebCrypto global `crypto` is available. Node\'s `crypto` module is NOT installed, so ' + - '`require("crypto")` will fail. Available: `crypto.randomUUID()`, `crypto.getRandomValues(typedArray)`, ' + - 'and the async `crypto.subtle` API (digest/hash, sign/verify, encrypt/decrypt, importKey/exportKey, deriveBits). ' + - '`crypto.subtle` is async, so use it inside an async IIFE: ' + - '`module.exports = (async () => (await crypto.subtle.digest("SHA-256", data)).byteLength)();`. ' + - 'Typed arrays work correctly across the sandbox boundary (verified against Node\'s real digests).', + 'Execute JS in isolated Node worker sandbox. Assign `module.exports` to return value (sync value or Promise; no bare top-level await — wrap async in IIFE: `module.exports = (async () => { ... })()`). ' + + 'REST API: `await fetch("/path")` (authenticated). ' + + 'VFS: `fs.readFileSync`/`writeFileSync` for binary artifacts/buffers/images (`image/` is read-only). ' + + 'Embed generated images in markdown: `![alt](path)`. Use find_files/read_file/patch for text. ' + + 'Globals: `fs`/`vfs`, `sharp`, WebCrypto `crypto` (`subtle`, `randomUUID`), web standard globals. Node built-ins unavailable (`require` fails).', strict: false, parameters: { type: 'object', @@ -153,25 +135,15 @@ export function createJavascriptExecuteTool({ googleAuthId }) { properties: { code: { type: 'string', - description: - 'JavaScript source to run in a CommonJS wrapper. Set `module.exports` to return a value; ' + - 'for async code assign the PROMISE, e.g. `module.exports = (async () => { ... })()`. ' + - 'To query Dashback REST API: `const res = await fetch("/article/2"); const data = await res.json();`. ' + - 'To persist BINARY artifacts between runs: use `fs.writeFileSync("name.ext", buffer)` and `fs.readFileSync("name.ext")`. ' + - 'For text files prefer the find_files / read_file / patch tools. ' + - 'Bare top-level `await` is a syntax error here — always wrap it in that async IIFE. ' + - 'Globals available: `fetch`, `console`, `fs`/`vfs`, `sharp`, `performance`, `URL`, `URLSearchParams`, `Headers`, ' + - '`Request`, `Response`, `FormData`, `Blob`, `File`, `TextEncoder`, `TextDecoder`, `AbortController`, ' + - '`AbortSignal`, `setTimeout`, `clearTimeout`, and WebCrypto `crypto` (`randomUUID`, `getRandomValues`, `subtle`). ' + - 'No Node built-ins are installable — `require("crypto")`, `require("fs")`, `require("path")` etc. all fail.', + description: 'CommonJS JS code. Assign result to `module.exports`. Async code must use IIFE.', }, timeout: { type: 'number', - description: `Hard wall-clock limit in ms. Default ${DEFAULT_TIMEOUT_MS}, max ${MAX_TIMEOUT_MS}.`, + description: `Execution timeout in ms. Default ${DEFAULT_TIMEOUT_MS}, max ${MAX_TIMEOUT_MS}.`, }, max_output: { type: 'number', - description: `Max chars of captured console output to return. Default ${DEFAULT_MAX_OUTPUT}.`, + description: `Max stdout chars. Default ${DEFAULT_MAX_OUTPUT}.`, }, }, }, diff --git a/src/tools/patch.js b/src/tools/patch.js index a732dd1..73b2039 100644 --- a/src/tools/patch.js +++ b/src/tools/patch.js @@ -67,17 +67,10 @@ export function createPatchTool({ googleAuthId }) { return defineTool({ name: 'patch', description: - 'Create, overwrite, or search-and-replace text in a virtual filesystem (VFS) file. ' + - 'This is the only way to write VFS files — javascript_execute writes stay for bulk binary artifacts.\n' + - 'Each op needs "file" and "replace"; "search" selects the mode:\n' + - ' - search omitted/null → create the file, or overwrite it entirely (directories are auto-created).\n' + - ' - search given → replace the FIRST occurrence of that exact text. "skiplines" skips N lines before\n' + - ' matching, which is how you target a later duplicate.\n' + - 'Create a SKILL file (title on line 1, markdown below; exempt from reset_vfs):\n' + - '[{"file": "image-pipeline.SKILL", "search": null, "replace": "Image pipeline\\n\\n# Steps\\n1. …"}]\n' + - 'or, letting the tool render the title line for you:\n' + - '[{"file": "image-pipeline.SKILL", "search": null, "title": "Image pipeline", "replace": "# Steps\\n1. …"}]\n' + - 'Read a file with read_file first and copy its text into "search" verbatim — the match is exact.', + 'Create, overwrite, or search-and-replace text in VFS files. Operations apply in order. ' + + 'Mode: search null/omitted = create or overwrite (directories auto-created). ' + + 'search provided = replace first verbatim occurrence (read file with read_file first). ' + + 'SKILL files: root-only ".SKILL" (line 1 is title, or provide title field).', strict: false, parameters: { type: 'object', @@ -86,7 +79,7 @@ export function createPatchTool({ googleAuthId }) { properties: { operations: { type: 'array', - description: 'Operations, applied in order. If one fails, earlier ones are kept and the error names the op.', + description: 'Ordered patch operations.', items: { type: 'object', additionalProperties: false, @@ -94,28 +87,23 @@ export function createPatchTool({ googleAuthId }) { properties: { file: { type: 'string', - description: - 'VFS path, relative to the root, forward slashes (e.g. "notes.md", "image-pipeline.SKILL"). ' + - 'SKILL file names may not contain "/" — they live at the root.', + description: 'VFS path (e.g. "notes.md", "pipeline.SKILL"). SKILL files must be root-level (no "/").', }, search: { type: ['string', 'null'], - description: 'Exact existing text to replace. null/omitted = create or overwrite the whole file.', + description: 'Exact text to replace. null/omitted = overwrite or create file.', }, replace: { type: 'string', - description: - 'Text to write. For a SKILL file you may pass "{ title, markdown }" and the title line is rendered for you.', + description: 'Replacement text.', }, title: { type: 'string', - description: - 'Optional title for a SKILL file written with a raw markdown string in "replace". ' + - 'Becomes line 1 of the file.', + description: 'Optional line-1 title for SKILL files.', }, skiplines: { type: 'integer', - description: 'Skip this many lines before searching for "search". Use when the text occurs more than once.', + description: 'Lines to skip before searching.', }, }, }, diff --git a/src/tools/pdf-read.js b/src/tools/pdf-read.js index 2bb2b7c..806f351 100644 --- a/src/tools/pdf-read.js +++ b/src/tools/pdf-read.js @@ -43,9 +43,7 @@ export function createPdfReadTool({ googleAuthId }) { return defineTool({ name: 'pdf_read', description: - 'Read and convert PDF (or text) documents. ' + - 'Supports converting PDF to layout-preserved text ("txt") or rendering pages to PNG images ("png") ' + - 'stored in the VFS images/ folder for inspection with analyze_image or inline markdown embedding.', + 'Read and convert PDF/text documents to text or VFS PNG images (stored in "images/").', strict: false, parameters: { type: 'object', @@ -54,23 +52,20 @@ export function createPdfReadTool({ googleAuthId }) { properties: { path: { type: 'string', - description: 'VFS path of the PDF or text file (e.g. "invoice.pdf", "docs/order.pdf"), or HTTP(S) URL.', + description: 'VFS path or HTTP(S) URL.', }, format: { type: 'string', enum: ['txt', 'png', 'both'], - description: - 'Conversion format: "txt" extracts layout-preserved text, ' + - '"png" renders pages into PNG images stored in the VFS images/ folder, ' + - '"both" extracts text and renders PNG images. Default is "txt".', + description: 'Output format: "txt" (layout text), "png" (page images), "both". Default "txt".', }, page: { type: 'number', - description: 'Specific page number (1-based) to extract text or render as image. If omitted, all pages are processed.', + description: '1-based page number. Omit for all pages.', }, dpi: { type: 'number', - description: 'Resolution in DPI for PNG rendering. Default 150.', + description: 'PNG render DPI. Default 150.', }, }, }, diff --git a/src/tools/read-file.js b/src/tools/read-file.js index 366ab83..994c173 100644 --- a/src/tools/read-file.js +++ b/src/tools/read-file.js @@ -61,11 +61,9 @@ export function createReadFileTool({ googleAuthId }) { return defineTool({ name: 'read_file', description: - 'Read a file from the virtual filesystem (VFS). Paths are relative to the VFS root ' + - 'and use forward slashes, e.g. "labels/sheet1.png" or "image-pipeline.SKILL". ' + - 'Reading a SKILL file returns its `title` (first line) separately from the markdown `content`. ' + - 'Large files are windowed with seek/limit (lines, 0-indexed) and the result reports ' + - 'how many lines remain. Binary files cannot be read — use analyze_image for images.', + 'Read text file from VFS. Root-relative forward-slash paths. ' + + 'SKILL files return parsed title and markdown content. ' + + 'Windowed via seek/limit (0-indexed lines). Binary files not supported (use analyze_image for images).', strict: false, parameters: { type: 'object', @@ -74,15 +72,15 @@ export function createReadFileTool({ googleAuthId }) { properties: { filename: { type: 'string', - description: 'File path relative to the VFS root, using forward slashes (e.g. "image-pipeline.SKILL").', + description: 'VFS file path.', }, seek: { type: 'number', - description: 'First line to return, 0-indexed. Default 0.', + description: 'Line offset (0-indexed). Default 0.', }, limit: { type: 'number', - description: `Maximum number of lines to return. Default ${DEFAULT_LIMIT}, max ${MAX_LIMIT}.`, + description: `Max lines to return. Default ${DEFAULT_LIMIT}, max ${MAX_LIMIT}.`, }, }, }, diff --git a/src/tools/reset-vfs.js b/src/tools/reset-vfs.js index 2130539..c7142dc 100644 --- a/src/tools/reset-vfs.js +++ b/src/tools/reset-vfs.js @@ -27,10 +27,7 @@ import { resolveVfsRoot } from './vfs.js'; export function createResetVfsTool({ googleAuthId }) { return defineTool({ name: 'reset_vfs', - description: - 'Delete files, images, and artifacts stored in the virtual filesystem (VFS) to free up storage. ' + - 'SKILL files are NOT deleted — reusable instruction documents survive a reset. ' + - 'Use find_files({ skills: true }) to see which SKILL files were kept.', + description: 'Clear VFS files, images, and artifacts. Preserves SKILL files by default.', strict: false, parameters: { type: 'object', @@ -38,8 +35,7 @@ export function createResetVfsTool({ googleAuthId }) { properties: { include_skills: { type: 'boolean', - description: - 'Also delete SKILL files. Default false — they are exempt unless you explicitly opt in.', + description: 'Delete SKILL files too. Default false.', }, }, }, diff --git a/src/tools/rm.js b/src/tools/rm.js index 1356309..82ddb6f 100644 --- a/src/tools/rm.js +++ b/src/tools/rm.js @@ -65,11 +65,7 @@ export function deleteVfsFile(vfsRoot, relPath) { export function createRmTool({ googleAuthId }) { return defineTool({ name: 'rm', - description: - 'Delete a single file from the virtual filesystem (VFS). ' + - 'Use this to remove a SKILL file: reset_vfs intentionally preserves skills, so rm is how a skill is ' + - 'deleted on purpose. Also use it to drop one unwanted artifact. ' + - 'Deletion is immediate and cannot be undone. Use reset_vfs to clear many files or a directory at once.', + description: 'Delete single VFS file (e.g. specific artifact or SKILL file). Irreversible.', strict: false, parameters: { type: 'object', @@ -78,9 +74,7 @@ export function createRmTool({ googleAuthId }) { properties: { filename: { type: 'string', - description: - 'File to delete, relative to the VFS root using forward slashes ' + - '(e.g. "image-pipeline.SKILL", "notes.md", "labels/sheet1.png").', + description: 'VFS file path to delete.', }, }, }, diff --git a/src/tools/search-images.js b/src/tools/search-images.js index f246e50..a6f8907 100644 --- a/src/tools/search-images.js +++ b/src/tools/search-images.js @@ -137,10 +137,7 @@ export function createSearchImagesTool({ googleAuthId }) { return defineTool({ name: 'search_images', description: - 'Search Google Images and store the results in the virtual filesystem as AVIF candidates. ' + - 'Returns VFS paths, not inline images — then pass those paths to analyze_image to actually look at them ' + - '(up to 9 at once, rendered as a labelled grid) and pick the best one before uploading it to an article ' + - 'or category. Candidates are cached, so a second search of the same term is cheap.', + 'Search Google Images and download candidates to VFS as AVIF. Returns VFS paths for inspection via analyze_image.', strict: false, parameters: { type: 'object', @@ -149,19 +146,19 @@ export function createSearchImagesTool({ googleAuthId }) { properties: { query: { type: 'string', - description: 'Search term, e.g. " Mineralwasser Flasche Glas ".', + description: 'Search query.', }, count: { type: 'number', - description: `How many candidate images to return. Default ${DEFAULT_COUNT}, max ${MAX_COUNT}.`, + description: `Candidate count. Default ${DEFAULT_COUNT}, max ${MAX_COUNT}.`, }, gl: { type: 'string', - description: 'Geolocation country code. Default "de".', + description: 'Country code. Default "de".', }, hl: { type: 'string', - description: 'Result language code. Default "de".', + description: 'Language code. Default "de".', }, }, }, diff --git a/src/tools/url-get.js b/src/tools/url-get.js index 5ad63e3..c13d431 100644 --- a/src/tools/url-get.js +++ b/src/tools/url-get.js @@ -63,9 +63,7 @@ async function serperScrape(url, apiKey, { signal } = {}) { export function createUrlGetTool({ googleAuthId }) { return defineTool({ name: 'url_get', - description: - 'Fetch and scrape the contents of a web page using Serper Scrape. ' + - 'Returns the raw scrape result as-is from Serper (text, markdown, metadata).', + description: 'Fetch and scrape web page content via Serper Scrape (text, markdown, metadata).', strict: false, parameters: { type: 'object', @@ -74,7 +72,7 @@ export function createUrlGetTool({ googleAuthId }) { properties: { url: { type: 'string', - description: 'The URL or web address to fetch and scrape (e.g. "https://example.com" or "growheads.de").', + description: 'Target URL.', }, }, }, diff --git a/src/tools/web-search.js b/src/tools/web-search.js index 3b4300b..d08bfc6 100644 --- a/src/tools/web-search.js +++ b/src/tools/web-search.js @@ -89,11 +89,7 @@ export function createWebSearchTool({ googleAuthId }) { return defineTool({ name: 'web_search', description: - 'Search the web using Google Search via Serper.dev. ' + - 'Returns raw search results as-is from Serper. ' + - 'Supports filtering fresh content with timePeriod ("day", "week", "month", "year"). ' + - 'Always use "day" or "week" when searching for news, recent events, or current status. ' + - 'Each page returns 10 results.', + 'Search Google via Serper.dev. Use timePeriod "day" or "week" for news/recent events. Returns 10 results per page.', strict: false, parameters: { type: 'object', @@ -102,27 +98,24 @@ export function createWebSearchTool({ googleAuthId }) { properties: { query: { type: 'string', - description: 'Search query, e.g. "apple inc" or "vite vue3 release".', + description: 'Search query.', }, timePeriod: { type: 'string', enum: ['day', 'week', 'month', 'year'], - description: - 'Filter results by time period: "day" (past 24 hours), "week" (past week), ' + - '"month" (past month), or "year" (past year). ' + - 'Always use "day" or "week" when searching for news, recent events, or current status.', + description: 'Time filter: "day", "week", "month", or "year".', }, gl: { type: 'string', - description: 'Geolocation country code. Default "de".', + description: 'Country code. Default "de".', }, hl: { type: 'string', - description: 'Result language code. Default "de".', + description: 'Language code. Default "de".', }, page: { type: 'number', - description: 'Page number of results. Default 1.', + description: 'Result page number (10/page). Default 1.', }, }, },