blun-king-cli 9.1.583 → 9.1.585
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/CHANGELOG.md +12 -0
- package/blun.mjs +32 -19
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
## 9.1.585 - 2026-09-08
|
|
2
|
+
|
|
3
|
+
- Managed WebSearch validates result envelopes and source rows even when optional search status metadata is absent.
|
|
4
|
+
- Malformed successful HTTP responses no longer appear as empty searches; invalid source rows fail the whole response before results or partial-source notices are returned.
|
|
5
|
+
- Valid legacy responses, empty result arrays and empty string excerpts remain supported. Cancellation, request authentication, FetchURL and Telegram behavior are unchanged.
|
|
6
|
+
|
|
7
|
+
## 9.1.584 - 2026-09-08
|
|
8
|
+
|
|
9
|
+
- WebSearch forwards caller cancellation through the tool and managed HTTP search request, including response-body reads.
|
|
10
|
+
- Already cancelled searches do not start provider work. Cancellation during credential lookup prevents a later search request; late search results are withheld.
|
|
11
|
+
- Complete, partial and empty-result handling, citation output and normal authentication fallback remain unchanged. No new timeout or Telegram routing change.
|
|
12
|
+
|
|
1
13
|
## 9.1.583 - 2026-09-08
|
|
2
14
|
|
|
3
15
|
- FetchURL now passes caller cancellation through the tool and both fetch providers to the HTTP request.
|
package/blun.mjs
CHANGED
|
@@ -265740,16 +265740,18 @@ var init_web_search = __esmMin((() => {
|
|
|
265740
265740
|
execute: (ctx) => this.execution(args, ctx)
|
|
265741
265741
|
};
|
|
265742
265742
|
}
|
|
265743
|
-
async execution(args, { toolCallId, }) {
|
|
265743
|
+
async execution(args, { toolCallId, signal }) {
|
|
265744
265744
|
try {
|
|
265745
|
+
signal?.throwIfAborted();
|
|
265745
265746
|
let failedSources = 0;
|
|
265746
|
-
const opts = { toolCallId, onNotice: notice => {
|
|
265747
|
+
const opts = { toolCallId, signal, onNotice: notice => {
|
|
265747
265748
|
if (notice.code === 'partial-sources' && Number.isInteger(notice.failedSources)
|
|
265748
265749
|
&& notice.failedSources > 0 && notice.failedSources <= 128) {
|
|
265749
265750
|
failedSources = Math.max(failedSources, notice.failedSources);
|
|
265750
265751
|
}
|
|
265751
265752
|
} };
|
|
265752
265753
|
const results = await this.provider.search(args.query, opts);
|
|
265754
|
+
signal?.throwIfAborted();
|
|
265753
265755
|
const builder = new ToolResultBuilder({ maxLineLength: null });
|
|
265754
265756
|
if (failedSources > 0) {
|
|
265755
265757
|
if (results.length === 0)
|
|
@@ -315281,24 +315283,45 @@ var init_blun_web_search = __esmMin((() => {
|
|
|
315281
315283
|
this.fetchImpl = options.fetchImpl ?? globalThis.fetch.bind(globalThis);
|
|
315282
315284
|
}
|
|
315283
315285
|
async search(query, options) {
|
|
315286
|
+
const signal = options?.signal;
|
|
315287
|
+
signal?.throwIfAborted();
|
|
315284
315288
|
const body = { text_query: query };
|
|
315285
315289
|
const bodyJson = JSON.stringify(body);
|
|
315286
315290
|
const toolCallId = options?.toolCallId;
|
|
315287
315291
|
const supportsNotices = typeof options?.onNotice === 'function';
|
|
315288
|
-
const response = await this.post(bodyJson, toolCallId, supportsNotices);
|
|
315292
|
+
const response = await this.post(bodyJson, toolCallId, supportsNotices, signal);
|
|
315293
|
+
signal?.throwIfAborted();
|
|
315289
315294
|
if (response.status === 401) {
|
|
315290
315295
|
const detail = await safeReadText(response);
|
|
315296
|
+
signal?.throwIfAborted();
|
|
315291
315297
|
throw new Error(`Blun search request failed: HTTP 401 (auth/unauthorized). ${detail}`.trim());
|
|
315292
315298
|
}
|
|
315293
315299
|
if (response.status !== 200) {
|
|
315294
315300
|
const detail = await safeReadText(response);
|
|
315301
|
+
signal?.throwIfAborted();
|
|
315295
315302
|
throw new Error(`Blun search request failed: HTTP ${String(response.status)}. ${detail}`.trim());
|
|
315296
315303
|
}
|
|
315297
315304
|
const json = (await response.json());
|
|
315298
|
-
|
|
315305
|
+
signal?.throwIfAborted();
|
|
315306
|
+
if (!json || typeof json !== 'object' || Array.isArray(json) || !Array.isArray(json.search_results))
|
|
315307
|
+
throw new Error('Invalid search results response.');
|
|
315308
|
+
const raw = json.search_results;
|
|
315309
|
+
if (!raw.every(row => {
|
|
315310
|
+
if (!row || typeof row.title !== 'string' || !row.title.trim()
|
|
315311
|
+
|| typeof row.snippet !== 'string' || typeof row.url !== 'string')
|
|
315312
|
+
return false;
|
|
315313
|
+
try {
|
|
315314
|
+
const url = new URL(row.url);
|
|
315315
|
+
return (url.protocol === 'https:' || url.protocol === 'http:') && !url.username && !url.password;
|
|
315316
|
+
}
|
|
315317
|
+
catch {
|
|
315318
|
+
return false;
|
|
315319
|
+
}
|
|
315320
|
+
}))
|
|
315321
|
+
throw new Error('Invalid search results response.');
|
|
315299
315322
|
const status = json.search_status;
|
|
315300
315323
|
if (status !== undefined) {
|
|
315301
|
-
if (!status || typeof status !== 'object'
|
|
315324
|
+
if (!status || typeof status !== 'object'
|
|
315302
315325
|
|| !Number.isInteger(status.failed_sources) || typeof status.failed_sources !== 'number'
|
|
315303
315326
|
|| status.failed_sources < 0 || status.failed_sources > 128
|
|
315304
315327
|
|| (status.state !== 'complete' && status.state !== 'partial')
|
|
@@ -315306,25 +315329,13 @@ var init_blun_web_search = __esmMin((() => {
|
|
|
315306
315329
|
|| (status.state === 'partial' && (status.failed_sources === 0 || raw.length === 0))) {
|
|
315307
315330
|
throw new Error('Invalid search status response.');
|
|
315308
315331
|
}
|
|
315309
|
-
if (!raw.every(row => {
|
|
315310
|
-
if (!row || typeof row.title !== 'string' || !row.title.trim()
|
|
315311
|
-
|| typeof row.snippet !== 'string' || typeof row.url !== 'string')
|
|
315312
|
-
return false;
|
|
315313
|
-
try {
|
|
315314
|
-
const url = new URL(row.url);
|
|
315315
|
-
return (url.protocol === 'https:' || url.protocol === 'http:') && !url.username && !url.password;
|
|
315316
|
-
}
|
|
315317
|
-
catch {
|
|
315318
|
-
return false;
|
|
315319
|
-
}
|
|
315320
|
-
}))
|
|
315321
|
-
throw new Error('Invalid search results response.');
|
|
315322
315332
|
if (status.state === 'partial') {
|
|
315323
315333
|
if (!supportsNotices)
|
|
315324
315334
|
throw new Error('Partial search results require a notice consumer.');
|
|
315325
315335
|
options?.onNotice?.({ code: 'partial-sources', failedSources: status.failed_sources });
|
|
315326
315336
|
}
|
|
315327
315337
|
}
|
|
315338
|
+
signal?.throwIfAborted();
|
|
315328
315339
|
return raw.map((r) => {
|
|
315329
315340
|
const out = {
|
|
315330
315341
|
title: r.title ?? '',
|
|
@@ -315338,10 +315349,12 @@ var init_blun_web_search = __esmMin((() => {
|
|
|
315338
315349
|
return out;
|
|
315339
315350
|
});
|
|
315340
315351
|
}
|
|
315341
|
-
async post(bodyJson, toolCallId, supportsNotices) {
|
|
315352
|
+
async post(bodyJson, toolCallId, supportsNotices, signal) {
|
|
315342
315353
|
const accessToken = await this.resolveApiKey();
|
|
315354
|
+
signal?.throwIfAborted();
|
|
315343
315355
|
return this.fetchImpl(this.baseUrl, {
|
|
315344
315356
|
method: 'POST',
|
|
315357
|
+
signal,
|
|
315345
315358
|
headers: {
|
|
315346
315359
|
...this.defaultHeaders,
|
|
315347
315360
|
Authorization: `Bearer ${accessToken}`,
|