u
This commit is contained in:
@@ -109,9 +109,9 @@ async function buildSystemPrompt(vfsRoot, googleAuthId) {
|
||||
`Date: ${today}`,
|
||||
'',
|
||||
'YOUR TOOLS:',
|
||||
'1. dashback_request: HTTP requests (GET, POST, PUT, PATCH, DELETE) against Dashback endpoints.',
|
||||
'1. dashback_request: HTTP requests (GET, POST, PUT, PATCH, DELETE) against QuixPOS Article Management System.',
|
||||
'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.',
|
||||
'3. javascript_execute: Sandboxed Node environment for computation, aggregation, and binary I/O. Assign module.exports. API: fetch("/path"). 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).',
|
||||
@@ -126,13 +126,12 @@ async function buildSystemPrompt(vfsRoot, googleAuthId) {
|
||||
'',
|
||||
'VIRTUAL FILESYSTEM:',
|
||||
'- 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.',
|
||||
'- Text: list with find_files, read with read_file, write with patch.',
|
||||
'',
|
||||
'SKILL FILES:',
|
||||
'- Reusable instruction docs: "<slug>.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.',
|
||||
'- Read with read_file BEFORE starting covered tasks. Propose saving improved procedures as new SKILL files.',
|
||||
'- Exempt from reset_vfs. Delete permanently via rm.',
|
||||
'',
|
||||
buildSkillIndex(vfsRoot),
|
||||
@@ -143,10 +142,7 @@ async function buildSystemPrompt(vfsRoot, googleAuthId) {
|
||||
'- `image/` is READ-ONLY database storage. Write generated images elsewhere (e.g. "images/…").',
|
||||
'',
|
||||
'OPERATING GUIDELINES:',
|
||||
'- 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: ``. 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.',
|
||||
@@ -168,6 +164,38 @@ async function buildSystemPrompt(vfsRoot, googleAuthId) {
|
||||
* `idle`/`busy` state. The finer SSE states (`thinking`, `working`, `done`)
|
||||
* and the streamed `delta` chunks are SSE-only.
|
||||
*/
|
||||
function parseUtcDate(val) {
|
||||
if (!val) return new Date();
|
||||
if (val instanceof Date) return isNaN(val.getTime()) ? new Date() : val;
|
||||
if (typeof val === 'string') {
|
||||
const s = val.trim();
|
||||
if (/^\d{4}-\d{2}-\d{2}[ T]\d{2}:\d{2}:\d{2}/.test(s) && !s.endsWith('Z') && !/[+-]\d{2}(:?\d{2})?$/.test(s)) {
|
||||
const parsed = new Date(s.replace(' ', 'T') + 'Z');
|
||||
if (!isNaN(parsed.getTime())) return parsed;
|
||||
}
|
||||
const parsed = new Date(s);
|
||||
if (!isNaN(parsed.getTime())) return parsed;
|
||||
}
|
||||
return new Date();
|
||||
}
|
||||
|
||||
export function formatTimePrefix(createdDate) {
|
||||
const d = parseUtcDate(createdDate);
|
||||
const parts = new Intl.DateTimeFormat('en-GB', {
|
||||
timeZone: 'Europe/Berlin',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
hour12: false,
|
||||
timeZoneName: 'short',
|
||||
}).formatToParts(d);
|
||||
const hour = parts.find((p) => p.type === 'hour')?.value.padStart(2, '0') || '00';
|
||||
const minute = parts.find((p) => p.type === 'minute')?.value.padStart(2, '0') || '00';
|
||||
let tz = parts.find((p) => p.type === 'timeZoneName')?.value || 'MEST';
|
||||
if (tz === 'CEST') tz = 'MEST';
|
||||
else if (tz === 'CET') tz = 'MET';
|
||||
return `[${hour}:${minute}${tz}] `;
|
||||
}
|
||||
|
||||
export function historyToLlmMessages(history) {
|
||||
const messages = [];
|
||||
for (const m of history) {
|
||||
@@ -179,7 +207,12 @@ export function historyToLlmMessages(history) {
|
||||
payload = { content: m.message };
|
||||
}
|
||||
if (m.role === 'user') {
|
||||
messages.push({ role: 'user', content: payload.content ?? (typeof m.message === 'string' ? m.message : '') });
|
||||
const rawContent = payload.content ?? (typeof m.message === 'string' ? m.message : '');
|
||||
const timePrefix = formatTimePrefix(m.created_at);
|
||||
const content = /^\s*\[\d{2}:\d{2}[A-Za-z]+\]\s*/.test(rawContent)
|
||||
? rawContent
|
||||
: `${timePrefix}${rawContent}`;
|
||||
messages.push({ role: 'user', content });
|
||||
} else if (m.role === 'assistant') {
|
||||
messages.push({
|
||||
role: 'assistant',
|
||||
|
||||
Reference in New Issue
Block a user