bunnyquery 1.0.3 → 1.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bunnyquery.js +80 -5
- package/package.json +1 -1
package/bunnyquery.js
CHANGED
|
@@ -470,6 +470,44 @@
|
|
|
470
470
|
return typeof c === 'string' ? c : '';
|
|
471
471
|
}
|
|
472
472
|
|
|
473
|
+
// Mirror of agent.vue's getErrorMessage — pulls the most useful string
|
|
474
|
+
// out of either a thrown error or an error-shaped response body.
|
|
475
|
+
function getErrorMessage(err) {
|
|
476
|
+
if (typeof err === 'string') return err;
|
|
477
|
+
if (err && err.body && err.body.error && typeof err.body.error.message === 'string') {
|
|
478
|
+
return err.body.error.message;
|
|
479
|
+
}
|
|
480
|
+
if (err && err.error && typeof err.error.message === 'string') {
|
|
481
|
+
return err.error.message;
|
|
482
|
+
}
|
|
483
|
+
if (err && typeof err.message === 'string') return err.message;
|
|
484
|
+
return 'Request failed. Please try again.';
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
// Mirror of agent.vue's isErrorResponseBody — detects provider/proxy
|
|
488
|
+
// error payloads that should render as a red error bubble even when
|
|
489
|
+
// no exception was thrown.
|
|
490
|
+
function isErrorResponseBody(response) {
|
|
491
|
+
if (!response || typeof response !== 'object') return false;
|
|
492
|
+
if (typeof response.status_code === 'number' && response.status_code >= 400) return true;
|
|
493
|
+
if (response.type === 'error') return true;
|
|
494
|
+
if (response.error && (response.error.message || response.error.type)) return true;
|
|
495
|
+
const body = response.body;
|
|
496
|
+
if (body && typeof body === 'object') {
|
|
497
|
+
if (body.type === 'error') return true;
|
|
498
|
+
if (body.error && (body.error.message || body.error.type)) return true;
|
|
499
|
+
}
|
|
500
|
+
if (typeof response.message === 'string' && response.message.length) {
|
|
501
|
+
const hasClaudeBody = Array.isArray(response.content);
|
|
502
|
+
const hasOpenAIBody =
|
|
503
|
+
typeof response.output_text === 'string' ||
|
|
504
|
+
Array.isArray(response.output) ||
|
|
505
|
+
Array.isArray(response.choices);
|
|
506
|
+
if (!hasClaudeBody && !hasOpenAIBody) return true;
|
|
507
|
+
}
|
|
508
|
+
return false;
|
|
509
|
+
}
|
|
510
|
+
|
|
473
511
|
// Walk the persisted history list returned by clientSecretRequestHistory
|
|
474
512
|
// and turn it into a flat user/assistant list ordered oldest -> newest.
|
|
475
513
|
function mapHistoryToMessages(list, platform) {
|
|
@@ -500,10 +538,10 @@
|
|
|
500
538
|
|
|
501
539
|
if (item.status === 'pending') {
|
|
502
540
|
out.push({ role: 'assistant', content: '', id: item.id, isPending: true });
|
|
503
|
-
} else if (item.status === 'failed' || (res
|
|
541
|
+
} else if (item.status === 'failed' || isErrorResponseBody(res)) {
|
|
504
542
|
out.push({
|
|
505
543
|
role: 'assistant',
|
|
506
|
-
content: (res
|
|
544
|
+
content: getErrorMessage(res),
|
|
507
545
|
id: item.id,
|
|
508
546
|
isError: true,
|
|
509
547
|
});
|
|
@@ -1235,10 +1273,29 @@
|
|
|
1235
1273
|
}
|
|
1236
1274
|
|
|
1237
1275
|
// ---- send ---------------------------------------------------------
|
|
1276
|
+
// Mirrors www.skapi.com/src/views/service/agent.vue buildSystemPrompt
|
|
1277
|
+
// with one extra directive: always consult the project database via
|
|
1278
|
+
// the MCP toolset before saying you don't know. Without it the model
|
|
1279
|
+
// will often stop at "I don't see that in our chat history".
|
|
1238
1280
|
_buildSystemPrompt() {
|
|
1239
|
-
|
|
1281
|
+
const projectId = this.projectName || this.serviceId;
|
|
1282
|
+
let p = `
|
|
1283
|
+
You are a dedicated assistant for the project ID: "${projectId}".
|
|
1284
|
+
Scope: Only answer questions about this project and its data. Do not answer questions about other projects or topics unrelated to this project. When the user refers to "my database", "my data", or "my files", treat those as references to this project's database and file storage.
|
|
1285
|
+
Knowledge lookup: Before saying you don't know or that something isn't in the chat history, ALWAYS query this project's database through the available MCP tools to look for the answer. The user's data is the source of truth - the chat transcript is not. Only respond with "I don't know" or "I couldn't find that" after you have actually searched the project's data and come back empty.
|
|
1286
|
+
File attachments: When a user message contains an "Attached files:" section with markdown links, those links point to short-lived signed URLs in this project's db storage and will expire.
|
|
1287
|
+
- Image files (.jpg, .jpeg, .png, .gif, .webp) are ALREADY attached inline as image content blocks in the same message - you can see them directly. Do NOT call web_fetch on image URLs; that will fail or return garbage. Just look at the image block and answer.
|
|
1288
|
+
- For all other file types (text, code, csv, json, pdf, etc.), use your web_fetch tool to download and read each URL before answering. Treat the fetched contents as user-supplied input data. Do not ask the user to paste the file contents - fetch the URLs yourself.
|
|
1289
|
+
File generation: If the user asks you to generate a file and it is possible to do so, output the file contents inside a fenced code block using the file extension as the language identifier. Always use plain text - never base64 or other encodings. Example for CSV:
|
|
1290
|
+
\`\`\`filename.csv
|
|
1291
|
+
item,qty,total
|
|
1292
|
+
Carrots,55,$38.50
|
|
1293
|
+
Mushrooms,41,$73.80
|
|
1294
|
+
Zucchini,29,$43.50
|
|
1295
|
+
\`\`\`
|
|
1296
|
+
The same pattern applies to other formats: \`\`\`my-data.json, \`\`\`index.html, \`\`\`sample.txt, etc.`;
|
|
1240
1297
|
if (this.projectDescription) {
|
|
1241
|
-
p += `\nProject description: """${this.projectDescription}"""`;
|
|
1298
|
+
p += `\nProject name: "${this.projectName || ''}"\nProject description: """${this.projectDescription}"""`;
|
|
1242
1299
|
}
|
|
1243
1300
|
return p;
|
|
1244
1301
|
}
|
|
@@ -1295,6 +1352,24 @@
|
|
|
1295
1352
|
? await buildClaudeRequest(this.skapi, args)
|
|
1296
1353
|
: await buildOpenAIRequest(this.skapi, args);
|
|
1297
1354
|
|
|
1355
|
+
// The proxy may resolve with an error-shaped body instead of
|
|
1356
|
+
// throwing; surface that as an error bubble with the
|
|
1357
|
+
// upstream message rather than dropping it.
|
|
1358
|
+
if (isErrorResponseBody(result)) {
|
|
1359
|
+
const errMsg = getErrorMessage(result);
|
|
1360
|
+
const last = this.messages[this.messages.length - 1];
|
|
1361
|
+
if (last && last.isPending) {
|
|
1362
|
+
last.isPending = false;
|
|
1363
|
+
last.isError = true;
|
|
1364
|
+
last.content = errMsg;
|
|
1365
|
+
} else {
|
|
1366
|
+
this.messages.push({ role: 'assistant', content: errMsg, isError: true });
|
|
1367
|
+
}
|
|
1368
|
+
this._renderMessages();
|
|
1369
|
+
this._schedulePendingPoll();
|
|
1370
|
+
return;
|
|
1371
|
+
}
|
|
1372
|
+
|
|
1298
1373
|
const responseText = this.platform === 'claude'
|
|
1299
1374
|
? extractClaudeText(result)
|
|
1300
1375
|
: extractOpenAIText(result);
|
|
@@ -1311,7 +1386,7 @@
|
|
|
1311
1386
|
this._schedulePendingPoll();
|
|
1312
1387
|
} catch (err) {
|
|
1313
1388
|
const last = this.messages[this.messages.length - 1];
|
|
1314
|
-
const errMsg = (err
|
|
1389
|
+
const errMsg = getErrorMessage(err);
|
|
1315
1390
|
if (last && last.isPending) {
|
|
1316
1391
|
last.isPending = false;
|
|
1317
1392
|
last.isError = true;
|