u
This commit is contained in:
@@ -217,6 +217,20 @@
|
||||
* format: date-time
|
||||
* text:
|
||||
* type: string
|
||||
* CompanyHistoryEntry:
|
||||
* type: object
|
||||
* required: [id, date, text]
|
||||
* properties:
|
||||
* id:
|
||||
* type: integer
|
||||
* companyuserId:
|
||||
* type: integer
|
||||
* nullable: true
|
||||
* date:
|
||||
* type: string
|
||||
* format: date-time
|
||||
* text:
|
||||
* type: string
|
||||
* ArticleSupplierPrice:
|
||||
* type: object
|
||||
* required: [net, taxId]
|
||||
|
||||
@@ -84,26 +84,11 @@ export function register(app, basePath = '') {
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* required: [attributevalues]
|
||||
* properties:
|
||||
* attributevalues:
|
||||
* type: array
|
||||
* description: Array of attribute value IDs (numbers) or objects with id or attributeValueId
|
||||
* items:
|
||||
* oneOf:
|
||||
* - type: integer
|
||||
* description: Attribute value ID
|
||||
* - type: object
|
||||
* properties:
|
||||
* id:
|
||||
* type: integer
|
||||
* description: Attribute value ID
|
||||
* attributeValueId:
|
||||
* type: integer
|
||||
* description: Attribute value ID
|
||||
* sort:
|
||||
* type: integer
|
||||
* type: array
|
||||
* description: Array of attribute value IDs. Array order defines sort.
|
||||
* items:
|
||||
* type: integer
|
||||
* example: [166]
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Updated article attribute values
|
||||
@@ -147,13 +132,10 @@ export function register(app, basePath = '') {
|
||||
return res.status(400).json({ error: skuParsed.error });
|
||||
}
|
||||
|
||||
const valuesInput = req.body?.attributevalues ?? req.body?.attributeValues ?? req.body?.attribute_values ?? req.body?.values ?? req.body;
|
||||
if (!Array.isArray(valuesInput)) {
|
||||
return res.status(400).json({ error: 'attributevalues array required' });
|
||||
}
|
||||
if (!Array.isArray(req.body)) return res.status(400).json({ error: 'attributevalues array required' });
|
||||
|
||||
try {
|
||||
const attributevalues = await db.setByArticleSku(req.user.id, skuParsed.sku, valuesInput);
|
||||
const attributevalues = await db.setByArticleSku(req.user.id, skuParsed.sku, req.body);
|
||||
emitArticleSignal(req, skuParsed.sku);
|
||||
res.json({ attributevalues });
|
||||
} catch (error) {
|
||||
|
||||
@@ -17,6 +17,7 @@ function attributeErrorStatus(error) {
|
||||
if (error.message === 'Name required') return 400;
|
||||
if (error.message === 'Invalid attribute name order') return 400;
|
||||
if (error.message === 'Invalid id') return 400;
|
||||
if (error.message === 'Attribute name in use' || error.message?.includes('FOREIGN KEY constraint')) return 409;
|
||||
return 500;
|
||||
}
|
||||
|
||||
@@ -200,6 +201,42 @@ export function register(app, basePath = '') {
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Deleted successfully
|
||||
* 400:
|
||||
* description: Bad request or no company selected
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: '#/components/schemas/Error'
|
||||
* 401:
|
||||
* description: Unauthorized
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: '#/components/schemas/Error'
|
||||
* 403:
|
||||
* description: Forbidden
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: '#/components/schemas/Error'
|
||||
* 404:
|
||||
* description: Attribute name not found
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: '#/components/schemas/Error'
|
||||
* 409:
|
||||
* description: Attribute name in use
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: '#/components/schemas/Error'
|
||||
* 500:
|
||||
* description: Server error
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: '#/components/schemas/Error'
|
||||
*/
|
||||
app.patch(`${basePath}/attributename/:id`, async (req, res) => {
|
||||
const selected = requireSelectedCompany(req);
|
||||
@@ -213,7 +250,9 @@ export function register(app, basePath = '') {
|
||||
emit('attributename', { companyId: selected.companyId });
|
||||
res.json({ attributename });
|
||||
} catch (error) {
|
||||
res.status(attributeErrorStatus(error)).json({ error: error.message });
|
||||
const status = attributeErrorStatus(error);
|
||||
if (status === 500) console.error('Update attribute name failed:', error);
|
||||
res.status(status).json({ error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
@@ -229,7 +268,12 @@ export function register(app, basePath = '') {
|
||||
emit('attributename', { companyId: selected.companyId });
|
||||
res.json({ ok: true });
|
||||
} catch (error) {
|
||||
res.status(attributeErrorStatus(error)).json({ error: error.message });
|
||||
const status = attributeErrorStatus(error);
|
||||
const message = (error.message === 'Attribute name in use' || error.message?.includes('FOREIGN KEY constraint'))
|
||||
? 'Attribute name in use'
|
||||
: error.message;
|
||||
if (status === 500) console.error('Delete attribute name failed:', error);
|
||||
res.status(status).json({ error: message });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ function attributeValueErrorStatus(error) {
|
||||
if (error.message === 'Value required') return 400;
|
||||
if (error.message === 'Invalid attribute value order') return 400;
|
||||
if (error.message === 'Invalid id' || error.message === 'Invalid attributeId') return 400;
|
||||
if (error.message === 'Attribute value in use' || error.message?.includes('FOREIGN KEY constraint')) return 409;
|
||||
return 500;
|
||||
}
|
||||
|
||||
@@ -216,6 +217,42 @@ export function register(app, basePath = '') {
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Deleted successfully
|
||||
* 400:
|
||||
* description: Bad request or no company selected
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: '#/components/schemas/Error'
|
||||
* 401:
|
||||
* description: Unauthorized
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: '#/components/schemas/Error'
|
||||
* 403:
|
||||
* description: Forbidden
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: '#/components/schemas/Error'
|
||||
* 404:
|
||||
* description: Attribute value not found
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: '#/components/schemas/Error'
|
||||
* 409:
|
||||
* description: Attribute value in use
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: '#/components/schemas/Error'
|
||||
* 500:
|
||||
* description: Server error
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: '#/components/schemas/Error'
|
||||
*/
|
||||
app.patch(`${basePath}/attributevalue/:id`, async (req, res) => {
|
||||
const selected = requireSelectedCompany(req);
|
||||
@@ -229,7 +266,9 @@ export function register(app, basePath = '') {
|
||||
emit('attributename', { companyId: selected.companyId });
|
||||
res.json({ attributevalue });
|
||||
} catch (error) {
|
||||
res.status(attributeValueErrorStatus(error)).json({ error: error.message });
|
||||
const status = attributeValueErrorStatus(error);
|
||||
if (status === 500) console.error('Update attribute value failed:', error);
|
||||
res.status(status).json({ error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
@@ -245,7 +284,12 @@ export function register(app, basePath = '') {
|
||||
emit('attributename', { companyId: selected.companyId });
|
||||
res.json({ ok: true });
|
||||
} catch (error) {
|
||||
res.status(attributeValueErrorStatus(error)).json({ error: error.message });
|
||||
const status = attributeValueErrorStatus(error);
|
||||
const message = (error.message === 'Attribute value in use' || error.message?.includes('FOREIGN KEY constraint'))
|
||||
? 'Attribute value in use'
|
||||
: error.message;
|
||||
if (status === 500) console.error('Delete attribute value failed:', error);
|
||||
res.status(status).json({ error: message });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -400,6 +400,7 @@ export function register(app, basePath = '') {
|
||||
.map((a) => ({
|
||||
...a,
|
||||
childCount: Number(a.childCount ?? 0),
|
||||
isFather: Boolean(a.isFather ?? a.is_father),
|
||||
}));
|
||||
res.status(200).json({ articles });
|
||||
} catch (error) {
|
||||
|
||||
@@ -85,26 +85,11 @@ export function register(app, basePath = '') {
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* required: [attributevalues]
|
||||
* properties:
|
||||
* attributevalues:
|
||||
* type: array
|
||||
* description: Array of attribute value IDs (numbers) or objects with id or attributeValueId
|
||||
* items:
|
||||
* oneOf:
|
||||
* - type: integer
|
||||
* description: Attribute value ID
|
||||
* - type: object
|
||||
* properties:
|
||||
* id:
|
||||
* type: integer
|
||||
* description: Attribute value ID
|
||||
* attributeValueId:
|
||||
* type: integer
|
||||
* description: Attribute value ID
|
||||
* sort:
|
||||
* type: integer
|
||||
* type: array
|
||||
* description: Array of attribute value IDs. Array order defines sort.
|
||||
* items:
|
||||
* type: integer
|
||||
* example: [166]
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Updated category attribute values
|
||||
@@ -148,13 +133,10 @@ export function register(app, basePath = '') {
|
||||
return res.status(400).json({ error: idParsed.error });
|
||||
}
|
||||
|
||||
const valuesInput = req.body?.attributevalues ?? req.body?.attributeValues ?? req.body?.attribute_values ?? req.body?.values ?? req.body;
|
||||
if (!Array.isArray(valuesInput)) {
|
||||
return res.status(400).json({ error: 'attributevalues array required' });
|
||||
}
|
||||
if (!Array.isArray(req.body)) return res.status(400).json({ error: 'attributevalues array required' });
|
||||
|
||||
try {
|
||||
const attributevalues = await db.setByCategoryId(req.user.id, idParsed.id, valuesInput);
|
||||
const attributevalues = await db.setByCategoryId(req.user.id, idParsed.id, req.body);
|
||||
emitCategorySignal(req);
|
||||
res.json({ attributevalues });
|
||||
} catch (error) {
|
||||
|
||||
@@ -32,10 +32,21 @@ function parseCompanyId(id) {
|
||||
return { companyId };
|
||||
}
|
||||
|
||||
function requireSelectedCompany(req) {
|
||||
const id = req.user?.selectedCompanyId;
|
||||
if (id == null) {
|
||||
const err = new Error('No company selected');
|
||||
err.statusCode = 400;
|
||||
throw err;
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
function companyErrorStatus(error) {
|
||||
if (error.message === 'Company not found') return 404;
|
||||
if (error.message === 'Company name not available') return 409;
|
||||
if (error.message === 'Invalid tax rate') return 400;
|
||||
if (error.message === 'Invalid tax rate' || error.message === 'Text required' || error.message === 'No company selected') return 400;
|
||||
if (error.message === 'Not authorized') return 403;
|
||||
return 500;
|
||||
}
|
||||
|
||||
@@ -167,6 +178,170 @@ export function register(app, basePath = '') {
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* @openapi
|
||||
* /company/history:
|
||||
* get:
|
||||
* tags: [Company]
|
||||
* summary: Get company history
|
||||
* description: |
|
||||
* Returns `companyhistory` rows for the selected company, newest first (`date` DESC, `id` DESC).
|
||||
* security:
|
||||
* - cookieAuth: []
|
||||
* x-markdown: company/history.md
|
||||
* parameters:
|
||||
* - in: query
|
||||
* name: limit
|
||||
* required: false
|
||||
* schema:
|
||||
* type: integer
|
||||
* minimum: 1
|
||||
* maximum: 1000
|
||||
* description: Maximum number of entries to return (newest first). Omit for all entries.
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Company history (newest first)
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* required: [history, truncated]
|
||||
* properties:
|
||||
* history:
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: '#/components/schemas/CompanyHistoryEntry'
|
||||
* truncated:
|
||||
* type: boolean
|
||||
* description: True when a limit was applied and the result set was exactly that size (more entries may exist).
|
||||
* 400:
|
||||
* description: Invalid limit or no company selected
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: '#/components/schemas/Error'
|
||||
* 401:
|
||||
* description: Unauthorized
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: '#/components/schemas/Error'
|
||||
* 403:
|
||||
* description: Forbidden
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: '#/components/schemas/Error'
|
||||
* 500:
|
||||
* description: Server error
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: '#/components/schemas/Error'
|
||||
* post:
|
||||
* tags: [Company]
|
||||
* summary: Add company history entry
|
||||
* description: |
|
||||
* Appends an entry to `companyhistory` for the selected company.
|
||||
* security:
|
||||
* - cookieAuth: []
|
||||
* x-markdown: company/addHistory.md
|
||||
* requestBody:
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* required: [text]
|
||||
* properties:
|
||||
* text:
|
||||
* type: string
|
||||
* description: History entry text or note
|
||||
* responses:
|
||||
* 201:
|
||||
* description: History entry created
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* required: [history]
|
||||
* properties:
|
||||
* history:
|
||||
* $ref: '#/components/schemas/CompanyHistoryEntry'
|
||||
* 400:
|
||||
* description: Text required or no company selected
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: '#/components/schemas/Error'
|
||||
* 401:
|
||||
* description: Unauthorized
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: '#/components/schemas/Error'
|
||||
* 403:
|
||||
* description: Forbidden
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: '#/components/schemas/Error'
|
||||
* 500:
|
||||
* description: Server error
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: '#/components/schemas/Error'
|
||||
*/
|
||||
app.get(`${basePath}/company/history`, async (req, res) => {
|
||||
try {
|
||||
requireSelectedCompany(req);
|
||||
} catch (error) {
|
||||
return res.status(error.statusCode || 400).json({ error: error.message });
|
||||
}
|
||||
|
||||
const rawLimit = req.query.limit;
|
||||
let limit = null;
|
||||
if (rawLimit !== undefined) {
|
||||
limit = parseInt(rawLimit, 10);
|
||||
if (!Number.isInteger(limit) || limit < 1 || limit > 1000) {
|
||||
return res.status(400).json({ error: 'limit must be an integer between 1 and 1000' });
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const history = await db.getCompanyHistory(req.user.id, limit != null ? { limit } : undefined);
|
||||
const truncated = limit != null && history.length === limit;
|
||||
res.status(200).json({ history, truncated });
|
||||
} catch (error) {
|
||||
const status = companyErrorStatus(error);
|
||||
if (status === 500) console.error('Get company history failed:', error);
|
||||
res.status(status).json({ error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
app.post(`${basePath}/company/history`, async (req, res) => {
|
||||
try {
|
||||
requireSelectedCompany(req);
|
||||
} catch (error) {
|
||||
return res.status(error.statusCode || 400).json({ error: error.message });
|
||||
}
|
||||
|
||||
const text = typeof req.body?.text === 'string' ? req.body.text.trim() : '';
|
||||
if (!text) {
|
||||
return res.status(400).json({ error: 'text is required' });
|
||||
}
|
||||
|
||||
try {
|
||||
const entry = await db.addCompanyHistory(req.user.id, { text });
|
||||
res.status(201).json({ history: entry });
|
||||
} catch (error) {
|
||||
const status = companyErrorStatus(error);
|
||||
if (status === 500) console.error('Add company history failed:', error);
|
||||
res.status(status).json({ error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* @openapi
|
||||
* /company/{id}:
|
||||
|
||||
@@ -17,6 +17,7 @@ function propertyErrorStatus(error) {
|
||||
if (error.message === 'Name required') return 400;
|
||||
if (error.message === 'Invalid property name order') return 400;
|
||||
if (error.message === 'Invalid id') return 400;
|
||||
if (error.message === 'Property name in use' || error.message?.includes('FOREIGN KEY constraint')) return 409;
|
||||
return 500;
|
||||
}
|
||||
|
||||
@@ -214,6 +215,42 @@ export function register(app, basePath = '') {
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Deleted successfully
|
||||
* 400:
|
||||
* description: Bad request or no company selected
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: '#/components/schemas/Error'
|
||||
* 401:
|
||||
* description: Unauthorized
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: '#/components/schemas/Error'
|
||||
* 403:
|
||||
* description: Forbidden
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: '#/components/schemas/Error'
|
||||
* 404:
|
||||
* description: Property name not found
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: '#/components/schemas/Error'
|
||||
* 409:
|
||||
* description: Property name in use
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: '#/components/schemas/Error'
|
||||
* 500:
|
||||
* description: Server error
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: '#/components/schemas/Error'
|
||||
*/
|
||||
app.patch(`${basePath}/propertyname/:id`, async (req, res) => {
|
||||
const selected = requireSelectedCompany(req);
|
||||
@@ -227,7 +264,9 @@ export function register(app, basePath = '') {
|
||||
emit('propertyname', { companyId: selected.companyId });
|
||||
res.json({ propertyname });
|
||||
} catch (error) {
|
||||
res.status(propertyErrorStatus(error)).json({ error: error.message });
|
||||
const status = propertyErrorStatus(error);
|
||||
if (status === 500) console.error('Update property name failed:', error);
|
||||
res.status(status).json({ error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
@@ -243,7 +282,12 @@ export function register(app, basePath = '') {
|
||||
emit('propertyname', { companyId: selected.companyId });
|
||||
res.json({ ok: true });
|
||||
} catch (error) {
|
||||
res.status(propertyErrorStatus(error)).json({ error: error.message });
|
||||
const status = propertyErrorStatus(error);
|
||||
const message = (error.message === 'Property name in use' || error.message?.includes('FOREIGN KEY constraint'))
|
||||
? 'Property name in use'
|
||||
: error.message;
|
||||
if (status === 500) console.error('Delete property name failed:', error);
|
||||
res.status(status).json({ error: message });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -24,3 +24,13 @@ export async function deleteCompany(companyId) {
|
||||
const store = await getStore();
|
||||
await store.company.delete(companyId);
|
||||
}
|
||||
|
||||
export async function getCompanyHistory(googleAuthId, opts) {
|
||||
const store = await getStore();
|
||||
return store.company.getHistory(googleAuthId, opts);
|
||||
}
|
||||
|
||||
export async function addCompanyHistory(googleAuthId, data) {
|
||||
const store = await getStore();
|
||||
return store.company.addHistory(googleAuthId, data);
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import { createDashbackRequestTool } from '../tools/dashback-request.js';
|
||||
import { createAskUserTool } from '../tools/ask-user.js';
|
||||
import { createJavascriptExecuteTool } from '../tools/javascript.js';
|
||||
import { createResetVfsTool } from '../tools/reset-vfs.js';
|
||||
import { createApiSuggestionsOrIssuesTool } from '../tools/apisuggestionsorissues.js';
|
||||
|
||||
const KEEP_ALIVE_MS = 20_000;
|
||||
|
||||
@@ -38,6 +39,7 @@ function buildSystemPrompt() {
|
||||
'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. With allow_network: true, snippets can directly call Dashback API routes using fetch() (e.g. await fetch("/article/2")) and search images using search_for_images(query). A sandboxed virtual filesystem (fs / vfs) is available to store and retrieve artifacts across runs.',
|
||||
'4. reset_vfs: Delete all files, images, and artifacts stored in the virtual filesystem (VFS) to free up storage or clean up.',
|
||||
'5. apisuggestionsorissues: Report missing API endpoints, bugs, or issues to be triaged. Automatically writes an entry to the company history table for review.',
|
||||
'',
|
||||
'OPERATING GUIDELINES:',
|
||||
'- Refer to the DASHBACK API ROUTES documentation below to find appropriate endpoints, parameters, and HTTP methods.',
|
||||
@@ -404,6 +406,7 @@ export class ConversationManager {
|
||||
createAskUserTool(),
|
||||
createJavascriptExecuteTool({ googleAuthId, companyId: this.companyId }),
|
||||
createResetVfsTool({ googleAuthId, companyId: this.companyId }),
|
||||
createApiSuggestionsOrIssuesTool({ googleAuthId, companyId: this.companyId }),
|
||||
],
|
||||
});
|
||||
|
||||
|
||||
61
src/tools/apisuggestionsorissues.js
Normal file
61
src/tools/apisuggestionsorissues.js
Normal file
@@ -0,0 +1,61 @@
|
||||
import { defineTool } from 'libllmorbiter';
|
||||
import * as db from '../db/company.js';
|
||||
|
||||
/**
|
||||
* Creates the apisuggestionsorissues tool allowing the agent to report
|
||||
* missing API endpoints, bugs, or general issues into the company history table.
|
||||
* These reports are then visible to triage agents or administrators.
|
||||
*
|
||||
* @param {{ googleAuthId: number, companyId?: number }} ctx
|
||||
*/
|
||||
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.',
|
||||
strict: false,
|
||||
parameters: {
|
||||
type: 'object',
|
||||
additionalProperties: false,
|
||||
required: ['text'],
|
||||
properties: {
|
||||
text: {
|
||||
type: 'string',
|
||||
description: 'Detailed description of the missing API, bug, or issue.',
|
||||
},
|
||||
type: {
|
||||
type: 'string',
|
||||
enum: ['missing_api', 'bug', 'suggestion', 'issue', 'other'],
|
||||
description: 'Category/type of the report.',
|
||||
},
|
||||
title: {
|
||||
type: 'string',
|
||||
description: 'Optional short summary or title.',
|
||||
},
|
||||
},
|
||||
},
|
||||
invoke: async ({ text, type, title }) => {
|
||||
const trimmedText = typeof text === 'string' ? text.trim() : '';
|
||||
if (!trimmedText) {
|
||||
throw new Error('text is required');
|
||||
}
|
||||
|
||||
const prefixParts = [];
|
||||
if (type) prefixParts.push(`[${type.toUpperCase()}]`);
|
||||
if (title) prefixParts.push(title.trim());
|
||||
|
||||
const formatted = prefixParts.length > 0
|
||||
? `${prefixParts.join(' ')}\n${trimmedText}`
|
||||
: trimmedText;
|
||||
|
||||
const entry = await db.addCompanyHistory(googleAuthId, { text: formatted });
|
||||
return {
|
||||
success: true,
|
||||
message: 'Recorded in company history for triage agent review',
|
||||
entry,
|
||||
};
|
||||
},
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user