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.
Files changed (3) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/blun.mjs +32 -19
  3. 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
- const raw = Array.isArray(json.search_results) ? json.search_results : [];
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' || !Array.isArray(json.search_results)
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}`,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "blun-king-cli",
3
- "version": "9.1.583",
3
+ "version": "9.1.585",
4
4
  "description": "BLUN CLI - your own AI agent with a Telegram channel. Get it done. With BLUN.",
5
5
  "license": "MIT",
6
6
  "bin": {