u
This commit is contained in:
@@ -3,88 +3,71 @@ import { listSkillFiles } from './skill.js';
|
||||
|
||||
// #region Tool Prompts & Descriptions
|
||||
|
||||
const NOTE_DESC = 'Status message shown to the user during execution.';
|
||||
|
||||
/**
|
||||
* Descriptions for all tools registered in the agent's toolbelt.
|
||||
* Used in defineTool({ description }) across src/tools/*.
|
||||
*/
|
||||
export const TOOL_DESCRIPTIONS = {
|
||||
dashback_request:
|
||||
'Call the Dashback (QuixPOS) REST API with GET, POST, PUT, PATCH or DELETE. Paths are root-relative (e.g. "/article/2").',
|
||||
'Call Dashback (QuixPOS) REST API with GET/POST/PUT/PATCH/DELETE. Root-relative paths (e.g. "/article/2").',
|
||||
|
||||
ask_user:
|
||||
'Prompt user for missing decisions or parameters with a concise question and up to 3 options. Pauses the run until the user answers.',
|
||||
'Prompt user for missing parameters with a concise question and up to 3 options. Pauses run until answered.',
|
||||
|
||||
javascript_execute:
|
||||
'Execute JS in an isolated worker sandbox using either inline `code` or a VFS applet. Use for computation, binary I/O and aggregation. Provide exactly one of `code` and `appletvfsfile`. ' +
|
||||
'Applets are the preferred method when the first execution may throw: save a `.applet.js` file in the VFS so it can be inspected and further edited with `patch`, then call it by VFS path. ' +
|
||||
'Applet files must start with `// @applet-input-schema {JSON Schema}` on line 1 and `// @applet-output-schema <columns>` on line 2. ' +
|
||||
'The input schema describes the GUI form passed as `applet_input`. The output line declares the RESULT COLUMNS as typed CSV: `name:type` pairs separated by `;` (e.g. `id:integer;name:string;active:boolean`); a bare `name` defaults to string, and the allowed types are string, integer, number, boolean and date. ' +
|
||||
'Applets return typed CSV, NOT JSON: export a function that receives `applet_input` and returns a row object, or an array of row objects, whose keys are the declared columns (e.g. `return [{ id: 1, name: "Kaffee" }]`). Only declared columns are emitted, in header order; missing keys become empty cells. The tool result then carries `result` as a `;`-separated CSV string with a header row, plus `columns` (name/type) and `rows` (data row count). Returning a single object yields one data row. ' +
|
||||
'Inline code: assign `module.exports` to return value (sync value or Promise; wrap async in IIFE: `module.exports = (async () => { ... })()`). ' +
|
||||
'REST API: `await fetch("/path")` (authenticated). ' +
|
||||
'VFS: directly use global `fs.readFileSync`/`writeFileSync` for binary artifacts/buffers/images (`image/` is read-only; paths must be relative paths, root is not allowed). VFS can host a static website (HTML, CSS, JS, images, etc.). ' +
|
||||
'Embed generated images/links in markdown: ``. Internet links use `https://...` or `hostname.com`/`hostname.com/filename`; a pure relative file with no dotted hostname is always a VFS relative path. Use find_files/read_file/patch for text. ' +
|
||||
'All APIs are pre-injected globals: `fs`/`vfs` (virtual filesystem), `sharp` (image processing), `ExcelJS`/`exceljs` (spreadsheet processing with VFS support for readFile/writeFile/writeBuffer/load), `PDFDocument`/`PDFKit`/`pdfkit` (PDF generation with VFS support for toFile/toBuffer/pipe), WebCrypto `crypto` (`subtle`, `randomUUID`), and standard web globals. Do NOT use `require()` or `import` (disabled; all necessary APIs are already in global scope).',
|
||||
'Execute JS in an isolated sandbox via inline `code` (assign to `module.exports`) or `appletvfsfile` ' +
|
||||
'(.applet.js returning typed CSV per @applet-input-schema and @applet-output-schema; details in applet.SKILL). ' +
|
||||
'Pre-injected globals: fs/vfs, sharp, ExcelJS, PDFDocument, crypto, and fetch("/path"). No require/import.',
|
||||
|
||||
find_files:
|
||||
'List VFS files. Root-relative forward-slash paths. ' +
|
||||
'Set skills=true to list root-level "<slug>.SKILL" files (reusable instructions exempt from reset_vfs; read via read_file).',
|
||||
'List VFS files (root-relative paths). Set skills=true to list root-level "<slug>.SKILL" files.',
|
||||
|
||||
read_file:
|
||||
'Read text file (text, CSV, JSON, markdown, …) 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).',
|
||||
'Read text file (text, CSV, JSON, markdown, SKILL) from VFS with seek/limit pagination. Binary not supported.',
|
||||
|
||||
patch:
|
||||
'Create, overwrite, or search-and-replace text in VFS files (the only write path for text 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 "<slug>.SKILL" (line 1 is title, or provide title field).',
|
||||
'Create, overwrite, or search-and-replace text in VFS files. Operations apply in order. Omit search to create/overwrite.',
|
||||
|
||||
search_images:
|
||||
'Search Google Images and download candidates to VFS as AVIF. Returns VFS paths for inspection via analyze_image.',
|
||||
'Search Google Images and download candidates to VFS as AVIF. Inspect candidates via analyze_image.',
|
||||
|
||||
analyze_image:
|
||||
'Inspect 1-9 images via vision model. Always evaluate images with this tool before using them. Multiple paths render as a labelled comparison grid. ' +
|
||||
'Paths: VFS-relative ("images/foo.avif") or DB ("image/<hash>"). Returns model evaluation.',
|
||||
'Inspect 1-9 images via vision model. Paths: VFS-relative ("images/foo.avif") or DB ("image/<hash>").',
|
||||
|
||||
web_search:
|
||||
'Search Google via Serper.dev. Use timePeriod "day" or "week" for news/recent events. Returns 10 results per page.',
|
||||
'Search Google via Serper.dev. Use timePeriod "day" or "week" for news/recent events. 10 results/page.',
|
||||
|
||||
url_get:
|
||||
'Fetch and scrape web page content via Serper Scrape (text, markdown, metadata).',
|
||||
'Fetch and scrape web page content (text, markdown, metadata) via Serper Scrape.',
|
||||
|
||||
pdf_read:
|
||||
'Read PDF/text documents as layout text ("txt") or render pages to VFS PNG images ("png", stored in "images/").',
|
||||
'Read PDF/text documents as layout text ("txt") or render pages to VFS PNG images ("png").',
|
||||
|
||||
reset_vfs:
|
||||
'Clear VFS files, images, and artifacts. Preserves SKILL files by default.',
|
||||
'Clear VFS files, images, and artifacts. Preserves SKILL files unless include_skills=true.',
|
||||
|
||||
rm:
|
||||
'Delete single VFS file (e.g. specific artifact or SKILL file). Irreversible.',
|
||||
'Permanently delete a single VFS file or SKILL file.',
|
||||
|
||||
apisuggestionsorissues:
|
||||
'Report missing API endpoint, feature request, bug, or issue for triage.',
|
||||
|
||||
search_articles:
|
||||
'Text search articles by name, brand, EAN, SKU, or supplier SKU. ' +
|
||||
'Returns window of full articles (default 3, max 20), offset, and total count. Ordered alphabetically by name.',
|
||||
'Search articles by name, brand, EAN, SKU, or supplier SKU (AND logic, ordered by name).',
|
||||
|
||||
edit_image:
|
||||
'Generate or edit images using the configured company image model (modelslugimage). ' +
|
||||
'Supports reference image attachments fed in low or high quality, output size tier, ' +
|
||||
'aspect ratio, and saving directly to a specified VFS output path (always saved as AVIF).',
|
||||
'Generate or edit images via modelslugimage. Supports reference image attachments, size tier, ratio, and VFS AVIF output.',
|
||||
|
||||
ean_checksum:
|
||||
'Validate EAN/GTIN/UPC barcode check digits or calculate the missing check digit for barcodes ' +
|
||||
'(EAN-13, EAN-8, UPC-A, GTIN-14). Strips spaces and hyphens automatically.',
|
||||
'Validate or calculate check digits for EAN-13, EAN-8, UPC-A, and GTIN-14 barcodes.',
|
||||
|
||||
net_gross_calc:
|
||||
'Convert between net and gross prices (net to gross, gross to net) with VAT/tax rate (default 19%). ' +
|
||||
'Computes 2-decimal rounded display values and 6-decimal high-precision net values for API article creation.',
|
||||
'Convert net/gross prices with VAT rate (default 19%). Returns rounded display values and 6-decimal net API values.',
|
||||
|
||||
set_user_route:
|
||||
'Navigate the user to a frontend route. Use this when the user says "show me the article" or asks to see, open, or view a specific page or entity (e.g. an article detail page, orders, quotes, deliveries, or customer). Use a path from the ROUTE MATRIX in the system prompt.',
|
||||
'Navigate user to a frontend route when asked to view/open an entity or page (see ROUTE MATRIX in prompt).',
|
||||
};
|
||||
|
||||
// #region Tool Parameter Descriptions
|
||||
@@ -94,11 +77,11 @@ export const TOOL_DESCRIPTIONS = {
|
||||
*/
|
||||
export const TOOL_PARAMETER_DESCRIPTIONS = {
|
||||
dashback_request: {
|
||||
note: 'A note explaining what this request is doing (or null). This note will be displayed to the user while the request is executing.',
|
||||
note: NOTE_DESC,
|
||||
method: 'HTTP method.',
|
||||
path: 'Endpoint path (e.g. "/article/2").',
|
||||
query: 'Query parameters object or null.',
|
||||
body: 'JSON request payload string or null.',
|
||||
body: 'JSON payload string or null.',
|
||||
},
|
||||
|
||||
ask_user: {
|
||||
@@ -107,62 +90,60 @@ export const TOOL_PARAMETER_DESCRIPTIONS = {
|
||||
},
|
||||
|
||||
javascript_execute: {
|
||||
note: 'A note explaining what this code is doing (or null). This note will be displayed to the user while the code is executing.',
|
||||
code: 'Optional inline JavaScript code. Provide exactly one of this and appletvfsfile. Assign result to `module.exports`. Async code must use IIFE. Do not use require() or import.',
|
||||
appletvfsfile:
|
||||
'Optional VFS-relative path to a .applet.js file. Provide exactly one of this and code. The file must begin with the two applet metadata lines (`@applet-input-schema` JSON Schema, then `@applet-output-schema` typed CSV columns like `id:integer;name:string`).',
|
||||
applet_input:
|
||||
'Input values passed to the exported function when running appletvfsfile. Omit to pass an empty object. The applet returns typed CSV rows mapped onto its `@applet-output-schema` columns.',
|
||||
timeout: 'Execution timeout in ms or null. Default 20000, max 60000.',
|
||||
max_output: 'Max stdout chars or null. Default 20000.',
|
||||
note: NOTE_DESC,
|
||||
code: 'Inline JS code. Assign result to `module.exports`. Wrap async in IIFE. Mutually exclusive with appletvfsfile.',
|
||||
appletvfsfile: 'VFS path to .applet.js file. Mutually exclusive with code.',
|
||||
applet_input: 'Input values passed to applet export function. Returns typed CSV rows.',
|
||||
timeout: 'Timeout in ms (default 20000, max 60000).',
|
||||
max_output: 'Max stdout chars (default 20000).',
|
||||
},
|
||||
|
||||
find_files: {
|
||||
dirname: 'VFS directory to list or null. Default "." (root). Must be "." or empty if skills=true.',
|
||||
recursive: 'Walk subdirectories or null. Default false.',
|
||||
skills: 'List only root SKILL files with parsed titles or null. Overrides dirname/recursive. Default false.',
|
||||
seek: 'Pagination offset or null. Default 0.',
|
||||
limit: 'Max entries to return or null. Default 50, max 500.',
|
||||
maxDepth: 'Max recursive directory depth or null. Default 3.',
|
||||
dirname: 'Directory to list (default "."). Must be "." if skills=true.',
|
||||
recursive: 'Walk subdirectories (default false).',
|
||||
skills: 'List only root SKILL files with parsed titles (default false).',
|
||||
seek: 'Pagination offset (default 0).',
|
||||
limit: 'Max entries (default 50, max 500).',
|
||||
maxDepth: 'Max recursive depth (default 3).',
|
||||
},
|
||||
|
||||
read_file: {
|
||||
filename: 'VFS file path.',
|
||||
seek: 'Line offset (0-indexed) or null. Default 0.',
|
||||
limit: 'Max lines to return or null. Default 400, max 5000.',
|
||||
seek: '0-indexed line offset (default 0).',
|
||||
limit: 'Max lines to return (default 400, max 5000).',
|
||||
},
|
||||
|
||||
patch: {
|
||||
operations: 'Ordered patch operations.',
|
||||
file: 'VFS path (e.g. "notes.md", "pipeline.SKILL"). SKILL files must be root-level (no "/").',
|
||||
search: 'Exact text to replace. null/omitted = overwrite or create file.',
|
||||
file: 'VFS path. SKILL files must be root-level.',
|
||||
search: 'Exact text to replace. Omit to overwrite or create file.',
|
||||
replace: 'Replacement text.',
|
||||
title: 'Optional line-1 title for SKILL files or null.',
|
||||
skiplines: 'Lines to skip before searching or null.',
|
||||
title: 'Line-1 title for SKILL files.',
|
||||
skiplines: 'Lines to skip before search.',
|
||||
},
|
||||
|
||||
search_images: {
|
||||
note: 'A note explaining what this image search is doing (or null). This note will be displayed to the user while executing.',
|
||||
note: NOTE_DESC,
|
||||
query: 'Search query.',
|
||||
count: 'Candidate count or null. Default 4, max 9.',
|
||||
gl: 'Country code or null. Default "de".',
|
||||
hl: 'Language code or null. Default "de".',
|
||||
count: 'Candidate count (default 4, max 9).',
|
||||
gl: 'Country code (default "de").',
|
||||
hl: 'Language code (default "de").',
|
||||
},
|
||||
|
||||
analyze_image: {
|
||||
note: 'A note explaining what this image inspection is doing (or null). This note will be displayed to the user while the images are being analyzed.',
|
||||
note: NOTE_DESC,
|
||||
paths: '1–9 VFS paths or DB paths ("image/<hash>").',
|
||||
prompt: 'Inspection criteria or question, or null.',
|
||||
prompt: 'Inspection criteria or question.',
|
||||
grid: 'Grid layout or null for auto.',
|
||||
},
|
||||
|
||||
web_search: {
|
||||
note: 'A note explaining what this search is doing (or null). This note will be displayed to the user while the search is executing.',
|
||||
note: NOTE_DESC,
|
||||
query: 'Search query.',
|
||||
timePeriod: 'Time filter: "day", "week", "month", or "year", or null.',
|
||||
gl: 'Country code or null. Default "de".',
|
||||
hl: 'Language code or null. Default "de".',
|
||||
page: 'Result page number (10/page) or null. Default 1.',
|
||||
timePeriod: 'Time filter: "day" | "week" | "month" | "year".',
|
||||
gl: 'Country code (default "de").',
|
||||
hl: 'Language code (default "de").',
|
||||
page: 'Result page number (10/page, default 1).',
|
||||
},
|
||||
|
||||
url_get: {
|
||||
@@ -171,13 +152,13 @@ export const TOOL_PARAMETER_DESCRIPTIONS = {
|
||||
|
||||
pdf_read: {
|
||||
path: 'VFS path or HTTP(S) URL.',
|
||||
format: 'Output format: "txt" (layout text), "png" (page images), "both", or null. Default "txt".',
|
||||
page: '1-based page number or null for all pages.',
|
||||
dpi: 'PNG render DPI or null. Default 150.',
|
||||
format: '"txt" (layout text), "png" (page images), or "both" (default "txt").',
|
||||
page: '1-based page number, or null for all pages.',
|
||||
dpi: 'Render DPI for PNG (default 150).',
|
||||
},
|
||||
|
||||
reset_vfs: {
|
||||
include_skills: 'Delete SKILL files too (or null). Default false.',
|
||||
include_skills: 'Delete SKILL files too (default false).',
|
||||
},
|
||||
|
||||
rm: {
|
||||
@@ -186,53 +167,47 @@ export const TOOL_PARAMETER_DESCRIPTIONS = {
|
||||
|
||||
apisuggestionsorissues: {
|
||||
text: 'Issue or request details.',
|
||||
type: 'Report category or null.',
|
||||
title: 'Short summary or null.',
|
||||
type: 'Report category.',
|
||||
title: 'Short summary.',
|
||||
},
|
||||
|
||||
search_articles: {
|
||||
searchquery:
|
||||
'Words that must all match across name, brand, EAN, SKU, or supplier SKU (AND logic, order-independent) or null.',
|
||||
excludewords:
|
||||
'Words that if all match exclude the article (AND logic, order-independent) or null.',
|
||||
offset: 'Pagination offset or null. Default 0.',
|
||||
limit: 'Result window size (1–20) or null. Default 3.',
|
||||
searchquery: 'Keywords matching name, brand, EAN, SKU, or supplier SKU (AND logic).',
|
||||
excludewords: 'Keywords to exclude (AND logic).',
|
||||
offset: 'Pagination offset (default 0).',
|
||||
limit: 'Window size 1–20 (default 3).',
|
||||
},
|
||||
|
||||
edit_image: {
|
||||
note: 'A note explaining what this image generation or edit is doing (or null). This note will be displayed to the user while the image is being generated or edited.',
|
||||
prompt: 'Description of the image to generate, or edit/transformation instructions when attachments are provided.',
|
||||
attachments:
|
||||
'Optional reference image(s) for editing/image-to-image or null. Omit for text-to-image generation.',
|
||||
attachment_vfsfilename:
|
||||
'VFS path (e.g. "images/sample.png") or database path ("image/<hash>").',
|
||||
attachment_high:
|
||||
'Feed image in high quality/resolution if true; low quality/resolution if false or null (default).',
|
||||
size: 'Resolution tier (3 choices): "1K" (standard, default), "2K" (high quality), "4K" (ultra high), or null.',
|
||||
ratio: 'Aspect ratio (3 choices): "1:1" (square, default), "4:3" (landscape), "3:4" (portrait), or null.',
|
||||
vfsoutputfilename:
|
||||
'VFS path where the output image should be saved (e.g. "images/edited.avif") or null. Defaults to "images/generated-<timestamp>.avif". Output is always converted to AVIF.',
|
||||
note: NOTE_DESC,
|
||||
prompt: 'Prompt for text-to-image or edit/transformation instructions.',
|
||||
attachments: 'Reference image attachments for img2img.',
|
||||
attachment_vfsfilename: 'VFS path or DB path ("image/<hash>").',
|
||||
attachment_high: 'High resolution input if true (default false).',
|
||||
size: 'Resolution tier: "1K" (default), "2K", "4K".',
|
||||
ratio: 'Aspect ratio: "1:1" (default), "4:3", "3:4".',
|
||||
vfsoutputfilename: 'Target VFS path (always saved as AVIF). Defaults to "images/generated-<timestamp>.avif".',
|
||||
},
|
||||
|
||||
ean_checksum: {
|
||||
note: 'A note explaining what this EAN checksum check is doing (or null). This note will be displayed to the user while executing.',
|
||||
code: 'Barcode number to validate or complete (e.g. 12 digits for EAN-13 check digit calculation, or 13 digits for EAN-13 validation).',
|
||||
action: 'Optional action: "auto" (default: infer from length), "validate" (check last digit), "calculate" (compute check digit for the given payload), or null.',
|
||||
note: NOTE_DESC,
|
||||
code: 'Barcode number to validate or complete.',
|
||||
action: '"auto" (default), "validate", or "calculate".',
|
||||
},
|
||||
|
||||
net_gross_calc: {
|
||||
note: 'A note explaining what this price calculation is doing. This note will be displayed to the user while executing.',
|
||||
gross: 'Gross price (incl. VAT) or null. When provided, computes net price and tax amount.',
|
||||
net: 'Net price (excl. VAT) or null. When provided, computes gross price and tax amount.',
|
||||
taxRate: 'Tax/VAT rate percentage (e.g. 19 for 19%, 7 for 7%) or null. Default 19.',
|
||||
amount: 'Generic price amount or null when used with "from" ("gross" or "net").',
|
||||
from: 'Direction when using "amount": "gross" (default) or "net".',
|
||||
precision: 'Decimal precision for net price API value or null (default 6, between 2 and 8).',
|
||||
items: 'Optional array of prices or objects for batch calculation: [19.99, 49.95] or [{ gross: 19.99, taxRate: 7 }].',
|
||||
note: NOTE_DESC,
|
||||
gross: 'Gross price (incl. VAT).',
|
||||
net: 'Net price (excl. VAT).',
|
||||
taxRate: 'VAT percentage (default 19).',
|
||||
amount: 'Price amount when paired with "from".',
|
||||
from: '"gross" (default) or "net".',
|
||||
precision: 'Net price API decimal precision 2–8 (default 6).',
|
||||
items: 'Array of prices or calculation objects for batch processing.',
|
||||
},
|
||||
|
||||
set_user_route: {
|
||||
route: 'Target frontend route path according to the Route Construction Matrix (e.g. "/artikel/ART-10042", "/kategorien/elektronik/kabel", "/auftrag/1001").',
|
||||
route: 'Frontend route from Route Matrix (e.g. "/artikel/ART-10042", "/auftrag/1001").',
|
||||
},
|
||||
};
|
||||
|
||||
@@ -242,70 +217,42 @@ export const PATCH_PARAM_DESCRIPTIONS = TOOL_PARAMETER_DESCRIPTIONS.patch;
|
||||
|
||||
// #region Tool-specific Subprompts
|
||||
export const ANALYZE_IMAGE_VISION_COMPARE_PROMPT =
|
||||
'Compare these images, judge visual quality, flag watermarks, text overlays or defects, and say which one is best and why.';
|
||||
'Compare these images, evaluate visual quality, flag watermarks/overlays/defects, and state the best candidate with rationale.';
|
||||
|
||||
export const ANALYZE_IMAGE_VISION_DESCRIBE_PROMPT =
|
||||
'Describe this image, judge its visual quality, flag any watermark, text overlay or defect, and say whether it is usable as a product or category image.';
|
||||
'Describe this image, evaluate visual quality, flag watermarks/overlays/defects, and verify suitability as a product/category image.';
|
||||
// #endregion
|
||||
|
||||
// #endregion
|
||||
|
||||
// #region System Prompt Components
|
||||
|
||||
export const SYSTEM_PROMPT_VFS_RULES = [
|
||||
'VFS & SKILLS:',
|
||||
'- Persistent per-company VFS; root-relative forward-slash paths (e.g. "labels/sheet1.png"). Can host a static website.',
|
||||
'- User uploads go to "attachments/…", tagged in messages as `[ATTACHMENT name= path= type=]`. Inspect with read_file (text/csv/json), pdf_read (PDF), analyze_image (images), javascript_execute + ExcelJS (spreadsheets).',
|
||||
'- Markdown links: internet links are "https://…" or dotted hostnames ("hostname.com/file"); anything else is a VFS path (`[download](report.csv)`).',
|
||||
'- SKILL files: root-only "<slug>.SKILL" markdown (first line = title). Discover via find_files({ skills: true }). ALWAYS read_file the matching skill BEFORE executing a covered task. Propose saving improved procedures as new SKILL files. Exempt from reset_vfs; delete via rm.',
|
||||
].join('\n');
|
||||
|
||||
export const SYSTEM_PROMPT_RULES = [
|
||||
'RULES:',
|
||||
'- LABELS (/labeltemplate): Dashback JSON `{ "objects": [ ... ] }`. Do NOT use ZPL.',
|
||||
'- IMAGES: flow search_images → analyze_image({ paths, prompt }) → upload via fetch in javascript_execute. `image/` is READ-ONLY; write generated images elsewhere (e.g. "images/…").',
|
||||
'- OUTPUT: Markdown + emoji. Render VFS images inline ``; other files (PDF, JSON, CSV) need download links.',
|
||||
'- NAVIGATION: when the user asks to view/open a page or entity, call set-user-route with a path from the matrix.',
|
||||
'- API: routes are not inlined. BEFORE dashback_request, read_file the api-<section>.SKILL for exact params and response shapes. `api-binary-streaming-routes.SKILL` covers fetch()-only routes.',
|
||||
'- T* TYPES: `T<n>` (e.g. T2) is shorthand for a reusable object shape. Ids are GLOBAL: the same T<n> always means the same shape in every skill. Each api-<section>.SKILL only lists, in its "Reusable types" block, the T<n> it uses (plus the ones those reference), so a skill shows a subset of the registry, and a T<n> may appear in several skills with an identical definition.',
|
||||
].join('\n');
|
||||
|
||||
export const SYSTEM_PROMPT_BINARY_UPLOAD = [
|
||||
'BINARY IMAGE UPLOAD (fetch() in javascript_execute, image/avif buffer body):',
|
||||
'- POST /article/{sku}/image -> 201 { image: { picId, sort, full: { w, h, size }, thumb: { w, h }, hash, thumbHash } }',
|
||||
'- POST /category/{id}/image -> 201 { category: { id, thumbW, thumbH, thumbHash } }',
|
||||
'- POST /brand/{id}/image -> 201 { brand: { id, fullHash, thumbW, thumbH, thumbHash, pics: [...] } }',
|
||||
'RULES & VFS:',
|
||||
'- VFS: Persistent root-relative paths ("labels/sheet1.png"). Uploads land in "attachments/..." ([ATTACHMENT name= path= type=]); inspect via read_file, pdf_read, analyze_image, or javascript_execute + ExcelJS. Can host static sites.',
|
||||
'- Links: Web links use "https://..." or domains; relative paths format as [label](relative/path). Inline images: . Other files require download links.',
|
||||
'- SKILLS: Flat root "<slug>.SKILL" markdown files. ALWAYS read_file matching skill BEFORE executing covered tasks. Exempt from reset_vfs.',
|
||||
'- API: Routes are not inlined. BEFORE dashback_request, read_file the api-<section>.SKILL for parameters and response structures. api-binary-streaming-routes.SKILL covers fetch()-only upload/stream endpoints.',
|
||||
'- Types: T<n> refers to globally shared object schemas across skills.',
|
||||
'- Images: Pipeline: search_images -> analyze_image -> upload via fetch() in javascript_execute. "image/" is read-only; write generated assets to "images/...".',
|
||||
'- Labels: Use Dashback JSON `{ "objects": [ ... ] }` for /labeltemplate. Never use ZPL.',
|
||||
'- Navigation: Call set_user_route when the user asks to open or view any page/entity.',
|
||||
].join('\n');
|
||||
|
||||
export const SYSTEM_PROMPT_ROUTE_MATRIX = [
|
||||
'ROUTE MATRIX (client routes German, API sections English; skills = api-<section>):',
|
||||
'| Destination | Path | Example | API sections |',
|
||||
'|:---|:---|:---|:---|',
|
||||
'| Article Search | `/artikel?target={query}&filter={filter}&offset={n}` | `/artikel?target=kabel` | article, category, brand |',
|
||||
'| Article Detail | `/artikel/${encodeURIComponent(sku)}` | `/artikel/ART-10042` | article, category, brand, attributename, propertyname, unit, supplier, printer, labeltemplate, ledger |',
|
||||
'| Warehouse / Stock | `/artikel/lager` | `/artikel/lager` | warehouse, stock, ledger |',
|
||||
'| Brands | `/artikel/marken` | `/artikel/marken` | brand |',
|
||||
'| Label Templates | `/artikel/vorlagen` | `/artikel/vorlagen` | labeltemplate, printer |',
|
||||
'| Attributes | `/artikel/merkmale` | `/artikel/merkmale` | attributename, attributevalue, propertyname |',
|
||||
'| Category Root | `/kategorien` | `/kategorien` | category |',
|
||||
'| Category Subtree | `/kategorien/${slug1}/${slug2}` (encoded slugs) | `/kategorien/elektronik/kabel` | category, article |',
|
||||
'| Delivery List | `/lieferung` | `/lieferung` | delivery |',
|
||||
'| New Delivery | `/lieferung/neu` | `/lieferung/neu` | delivery, supplier, article, warehouse, stock |',
|
||||
'| Delivery Detail | `/lieferung/${deliveryId}` | `/lieferung/42` | delivery, supplier, ledger |',
|
||||
'| Suppliers | `/lieferung/lieferanten` | `/lieferung/lieferanten` | supplier |',
|
||||
'| Quote List | `/auftrag` | `/auftrag` | quote |',
|
||||
'| New Quote | `/auftrag/neu` | `/auftrag/neu` | quote, entity, article |',
|
||||
'| Quote Detail | `/auftrag/${quoteId}` | `/auftrag/1001` | quote, entity |',
|
||||
'| Order List | `/auftrag/bestellung` | `/auftrag/bestellung` | order |',
|
||||
'| New Order | `/auftrag/bestellung/neu` | `/auftrag/bestellung/neu` | order, entity, article |',
|
||||
'| Order Detail | `/auftrag/bestellung/${orderId}` | `/auftrag/bestellung/2005` | order, entity, account, payment, shipping, package |',
|
||||
'| Customer List | `/kunde` | `/kunde` | entity |',
|
||||
'| New Customer | `/kunde/neu` | `/kunde/neu` | entity, customergroup, number |',
|
||||
'| Customer Detail | `/kunde/${entityId}` | `/kunde/312` | entity, customergroup |',
|
||||
'| Customer Groups | `/kunde/gruppen` | `/kunde/gruppen` | customergroup |',
|
||||
'| Article History | `/infos/articlehistory` | `/infos/articlehistory` | article (`GET /article/{sku}/history`) |',
|
||||
'| Global History | `/infos/globalhistory` | `/infos/globalhistory` | company (`GET /company/history`) |',
|
||||
'| Statistics | `/infos/statistics` | `/infos/statistics` | none; aggregate from order, delivery, ledger |',
|
||||
'ROUTE MATRIX (client German, API skills = api-<section>):',
|
||||
'- Search: /artikel?target={query}&filter={filter}&offset={n} -> article, category, brand',
|
||||
'- Article Detail: /artikel/${sku} -> article, category, brand, attributename, propertyname, unit, supplier, printer, labeltemplate, ledger',
|
||||
'- Warehouse / Stock: /artikel/lager -> warehouse, stock, ledger',
|
||||
'- Brands: /artikel/marken -> brand',
|
||||
'- Label Templates: /artikel/vorlagen -> labeltemplate, printer',
|
||||
'- Attributes: /artikel/merkmale -> attributename, attributevalue, propertyname',
|
||||
'- Categories: /kategorien | /kategorien/${slug1}/${slug2} -> category, article',
|
||||
'- Deliveries: /lieferung | /lieferung/neu | /lieferung/${deliveryId} | /lieferung/lieferanten -> delivery, supplier, article, warehouse, stock, ledger',
|
||||
'- Quotes: /auftrag | /auftrag/neu | /auftrag/${quoteId} -> quote, entity, article',
|
||||
'- Orders: /auftrag/bestellung | /auftrag/bestellung/neu | /auftrag/bestellung/${orderId} -> order, entity, account, payment, shipping, package',
|
||||
'- Customers: /kunde | /kunde/neu | /kunde/${entityId} | /kunde/gruppen -> entity, customergroup, number',
|
||||
'- History & Stats: /infos/articlehistory | /infos/globalhistory | /infos/statistics -> article, company, order, delivery, ledger',
|
||||
].join('\n');
|
||||
|
||||
// #endregion
|
||||
@@ -315,17 +262,12 @@ export const SYSTEM_PROMPT_ROUTE_MATRIX = [
|
||||
/**
|
||||
* Builds the SKILL index block for the system prompt.
|
||||
*
|
||||
* Listing the skills inline means the model knows what guidance exists BEFORE it decides what
|
||||
* to do, so it can reach for `read_file` on the right skill instead of guessing a procedure
|
||||
* from scratch. Only the filename, title and line count are included — the body is read on
|
||||
* demand, which keeps the prompt small no matter how many skills exist.
|
||||
*
|
||||
* @param {string} vfsRoot
|
||||
* @returns {string}
|
||||
*/
|
||||
export function buildSkillIndex(vfsRoot) {
|
||||
if (!vfsRoot) {
|
||||
return 'AVAILABLE SKILLS: unknown (the virtual filesystem could not be read this turn). Call find_files({ skills: true }) to check.';
|
||||
return 'AVAILABLE SKILLS: unknown. Call find_files({ skills: true }) to verify.';
|
||||
}
|
||||
|
||||
let skills;
|
||||
@@ -333,21 +275,16 @@ export function buildSkillIndex(vfsRoot) {
|
||||
skills = listSkillFiles(vfsRoot);
|
||||
} catch (err) {
|
||||
console.warn('conversation: failed to list skill files:', err.message);
|
||||
return 'AVAILABLE SKILLS: could not be read this turn. Call find_files({ skills: true }) to check.';
|
||||
return 'AVAILABLE SKILLS: unreadable this turn. Call find_files({ skills: true }) to verify.';
|
||||
}
|
||||
|
||||
if (skills.length === 0) {
|
||||
return [
|
||||
'AVAILABLE SKILLS: none. This company has no SKILL files yet.',
|
||||
].join('\n');
|
||||
return 'AVAILABLE SKILLS: none.';
|
||||
}
|
||||
|
||||
const width = Math.max(...skills.map((s) => s.path.length));
|
||||
return [
|
||||
`AVAILABLE SKILLS (${skills.length}): stored in the VFS root as flat <slug>.SKILL files.`,
|
||||
'Before starting a task that any of these cover, read the matching skill with read_file and follow it.',
|
||||
'',
|
||||
...skills.map((s) => ` ${s.path.padEnd(width)} ${String(s.lines).padStart(4)} lines | ${s.title}`),
|
||||
`AVAILABLE SKILLS (${skills.length}): stored in VFS root. Read with read_file before executing covered tasks.`,
|
||||
...skills.map((s) => `- ${s.path} (${s.lines} lines): ${s.title}`),
|
||||
].join('\n');
|
||||
}
|
||||
|
||||
@@ -356,8 +293,7 @@ export function buildSkillIndex(vfsRoot) {
|
||||
// #region System Prompt
|
||||
|
||||
/**
|
||||
* System prompt: base instructions plus the generated API routes reference
|
||||
* (docs/api/routes.md), read on each run so it stays current.
|
||||
* System prompt: base instructions and contextual index.
|
||||
*
|
||||
* @param {string} vfsRoot Resolved VFS root for this user's company, used to list SKILL files.
|
||||
* @param {number} [googleAuthId]
|
||||
@@ -385,15 +321,11 @@ export async function buildSystemPrompt(vfsRoot, googleAuthId) {
|
||||
const basePrompt = [
|
||||
'QuixPOS Article Management System API Assistant',
|
||||
`Date: ${today}`,
|
||||
'[TESTSYSTEM, destructive commands are allowed, main purpose of this system is to find bugs]',
|
||||
'',
|
||||
SYSTEM_PROMPT_VFS_RULES,
|
||||
'',
|
||||
buildSkillIndex(vfsRoot),
|
||||
'[TESTSYSTEM: destructive commands allowed; primary goal is finding bugs]',
|
||||
'',
|
||||
SYSTEM_PROMPT_RULES,
|
||||
'',
|
||||
SYSTEM_PROMPT_BINARY_UPLOAD,
|
||||
buildSkillIndex(vfsRoot),
|
||||
'',
|
||||
SYSTEM_PROMPT_ROUTE_MATRIX,
|
||||
].join('\n');
|
||||
|
||||
Reference in New Issue
Block a user