sweet-search 2.6.14 → 2.6.15

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.
@@ -312,6 +312,77 @@ export class CodeGraphRepository {
312
312
  }
313
313
  }
314
314
 
315
+ /**
316
+ * Find the named entities immediately ADJACENT to a shown line window in
317
+ * the same file: the nearest ones lying fully ABOVE the window
318
+ * (end_line < startLine) and the nearest ones starting BELOW it
319
+ * (start_line > endLine). Powers the agent-format same-file span map —
320
+ * the pointer that tells an agent which sibling symbols sit just outside
321
+ * the chunk window it was shown.
322
+ *
323
+ * Entities enclosing the window (e.g. the surrounding class) never match
324
+ * either predicate, so results are true siblings. Inner entities contained
325
+ * in an already-kept row (closures inside the adjacent function) are
326
+ * filtered so the map names outermost neighbors only.
327
+ *
328
+ * @param {string} filePath
329
+ * @param {number} startLine - first shown line (1-based)
330
+ * @param {number} endLine - last shown line (1-based)
331
+ * @param {{ perSide?: number }} [opts] - max entities per side (default 2)
332
+ * @returns {{ above: Array<{id,name,type,startLine,endLine,parentClass}>,
333
+ * below: Array<{id,name,type,startLine,endLine,parentClass}> }}
334
+ */
335
+ findAdjacentEntities(filePath, startLine, endLine, opts = {}) {
336
+ const perSide = opts.perSide ?? 2;
337
+ const empty = { above: [], below: [] };
338
+ if (!filePath || !Number.isFinite(startLine) || !Number.isFinite(endLine)) return empty;
339
+ const db = this._open();
340
+ if (!db) return empty;
341
+ const mapRow = row => ({
342
+ id: row.id,
343
+ name: row.name,
344
+ type: row.type,
345
+ startLine: row.start_line,
346
+ endLine: row.end_line,
347
+ parentClass: row.parent_class || null,
348
+ });
349
+ // Keep outermost neighbors: drop rows contained in an already-kept row.
350
+ const keepOutermost = rows => {
351
+ const kept = [];
352
+ for (const row of rows) {
353
+ if (kept.some(k => row.start_line >= k.start_line && row.end_line <= k.end_line)) continue;
354
+ kept.push(row);
355
+ if (kept.length >= perSide) break;
356
+ }
357
+ return kept.map(mapRow);
358
+ };
359
+ try {
360
+ const aboveRows = prepareCached(db, `
361
+ SELECT id, name, type, start_line, end_line, parent_class
362
+ FROM entities
363
+ WHERE file_path = ?
364
+ AND end_line < ?
365
+ AND name IS NOT NULL AND name != ''
366
+ AND ${this._entityVisibilitySql(db)}
367
+ ORDER BY end_line DESC, start_line ASC
368
+ LIMIT 8
369
+ `).all(filePath, startLine, ...this._entityVisibilityParams(db));
370
+ const belowRows = prepareCached(db, `
371
+ SELECT id, name, type, start_line, end_line, parent_class
372
+ FROM entities
373
+ WHERE file_path = ?
374
+ AND start_line > ?
375
+ AND name IS NOT NULL AND name != ''
376
+ AND ${this._entityVisibilitySql(db)}
377
+ ORDER BY start_line ASC, end_line DESC
378
+ LIMIT 8
379
+ `).all(filePath, endLine, ...this._entityVisibilityParams(db));
380
+ return { above: keepOutermost(aboveRows), below: keepOutermost(belowRows) };
381
+ } catch {
382
+ return empty;
383
+ }
384
+ }
385
+
315
386
  /**
316
387
  * Get a single entity by id, with file:line metadata.
317
388
  *
@@ -1931,6 +1931,40 @@ export function selectAgentBudget(format, signals, opts = {}) {
1931
1931
  * 'no-syntax-expansion', 'no-header', 'no-diversity', 'no-adaptive-budget'
1932
1932
  * @returns {object} Agent mode response
1933
1933
  */
1934
+ /**
1935
+ * Same-file span map (2026-07, within-file blind-spot fix). When the top-1
1936
+ * pack entry is a WINDOWED view of a file (kind chunk/sandwich/syntax — not
1937
+ * a fully-shown symbol) and the sufficiency verdict is not a clear YES, one
1938
+ * compact line names the sibling symbols immediately above/below the shown
1939
+ * window so the agent can sweep the fix surface instead of leaving the file
1940
+ * (the fhir/sushi-1175 miss shape: right file, bug 30 lines above the
1941
+ * window). Names+kinds+lines only — never bodies; ~25-45 tokens.
1942
+ *
1943
+ * @param {{ file:string, startLine:number, endLine:number }} top - top-1 agent result
1944
+ * @param {{ above:Array, below:Array }} adjacent - findAdjacentEntities() result
1945
+ * @returns {{ rendered:string, tokens:number, neighbors:Array }|null}
1946
+ */
1947
+ export function buildSameFileMap(top, adjacent) {
1948
+ const above = adjacent?.above || [];
1949
+ const below = adjacent?.below || [];
1950
+ if (above.length === 0 && below.length === 0) return null;
1951
+ const shortType = t => (t === 'function' ? 'fn' : (t || 'sym'));
1952
+ const fmt = (e, pos) => `${e.name} (${shortType(e.type)} ${e.startLine}-${e.endLine} ${pos})`;
1953
+ const neighbors = [
1954
+ ...above.map(e => ({ ...e, position: 'above' })),
1955
+ ...below.map(e => ({ ...e, position: 'below' })),
1956
+ ];
1957
+ const parts = [
1958
+ ...above.map(e => fmt(e, 'above')),
1959
+ ...below.map(e => fmt(e, 'below')),
1960
+ ];
1961
+ // Placeholder-style drill-in hint (the v2.6.13 `ss-grep "<regex>" --in
1962
+ // <file>` convention, which agents demonstrably follow) — embedding the
1963
+ // live query costs ~25-30 tokens per pack for no extra signal.
1964
+ const rendered = `# same file: ${parts.join(' · ')} — sweep: ss-semantic ${top.file} "<query>"`;
1965
+ return { rendered, tokens: estimateTokens(rendered), neighbors };
1966
+ }
1967
+
1934
1968
  export function packageForAgent(rankedResults, searchStats, opts) {
1935
1969
  const {
1936
1970
  query,
@@ -2347,6 +2381,53 @@ export function packageForAgent(rankedResults, searchStats, opts) {
2347
2381
  unresolvedExternalCount = sufficiency.unresolvedExternalCount || 0;
2348
2382
  }
2349
2383
 
2384
+ // Phase 7: same-file span map (top-1 only). Emitted ONLY when the verdict
2385
+ // is not a clear YES (composes with the query-conditioned verdict: the map
2386
+ // supplies the "where to look next" exactly when the engine says "keep
2387
+ // looking") AND top-1 is a windowed view (kind != 'full' — fully-shown
2388
+ // symbols stay byte-identical) AND the line fits the remaining tier
2389
+ // budget (dropped on overflow, never a truncated pack). Its tokens are
2390
+ // counted inside tokensUsed. Disabled by 'no-same-file-map' ablation.
2391
+ if (!ablations.has('no-same-file-map')
2392
+ && agentResults.length > 0
2393
+ && sufficiencyVerdict !== 'yes'
2394
+ && codeGraphRepo
2395
+ && typeof codeGraphRepo.findAdjacentEntities === 'function') {
2396
+ const top = agentResults[0];
2397
+ const windowed = top.code
2398
+ && top.presentation !== 'summary'
2399
+ && top.expansionKind
2400
+ && top.expansionKind !== 'full'
2401
+ && top.file
2402
+ && Number.isFinite(top.startLine)
2403
+ && Number.isFinite(top.endLine);
2404
+ if (windowed) {
2405
+ let adjacent = null;
2406
+ try {
2407
+ adjacent = codeGraphRepo.findAdjacentEntities(top.file, top.startLine, top.endLine, { perSide: 2 });
2408
+ } catch { adjacent = null; }
2409
+ // Don't name neighbors whose code is ALREADY visible in the pack —
2410
+ // locality clustering pulls same-file companions to ranks 2-3; a map
2411
+ // entry for a shown span is pure noise.
2412
+ if (adjacent) {
2413
+ const shownSameFile = agentResults.filter(r =>
2414
+ r !== top && r.code && r.file === top.file
2415
+ && Number.isFinite(r.startLine) && Number.isFinite(r.endLine));
2416
+ const overlapsShown = e => shownSameFile.some(r =>
2417
+ e.startLine <= r.endLine && e.endLine >= r.startLine);
2418
+ adjacent = {
2419
+ above: adjacent.above.filter(e => !overlapsShown(e)),
2420
+ below: adjacent.below.filter(e => !overlapsShown(e)),
2421
+ };
2422
+ }
2423
+ const map = adjacent ? buildSameFileMap(top, adjacent) : null;
2424
+ if (map && map.tokens <= Math.max(0, tokenBudget - tokensUsed)) {
2425
+ top.sameFile = map;
2426
+ tokensUsed += map.tokens;
2427
+ }
2428
+ }
2429
+ }
2430
+
2350
2431
  return {
2351
2432
  query,
2352
2433
  regex,
@@ -239,6 +239,48 @@ function _attachIndexMetadata(filePathRel, projectRoot) {
239
239
  return { indexed: true, chunks, language };
240
240
  }
241
241
 
242
+ // ---------------------------------------------------------------------------
243
+ // Remainder definition sniffing — fallback symbol names for the "what
244
+ // remains" trailer when the index has no named chunks in the unread span
245
+ // (e.g. C++ files where the chunker recorded `name: null`). Scans ONLY the
246
+ // remainder lines of the buffer already in memory: zero I/O, capped.
247
+ // ---------------------------------------------------------------------------
248
+
249
+ const SNIFF_MAX_LINES = 4000;
250
+ const UNREAD_SYMBOLS_MAX = 5; // hard cap on named symbols in the trailer
251
+ const UNREAD_SYMBOLS_MIN_LINES = 20; // smaller remainders get the short form
252
+ const C_FAMILY_EXTS = new Set(['.c', '.h', '.cc', '.cpp', '.cxx', '.hpp', '.hh', '.hxx', '.java', '.cs', '.m', '.mm']);
253
+
254
+ // Keyword-introduced definitions (Python/Ruby/JS/TS/Go/Rust/Kotlin/PHP/...).
255
+ const KEYWORD_DEF_RE = /^\s*(?:export\s+|default\s+|pub(?:\([^)]*\))?\s+|static\s+|async\s+|abstract\s+|final\s+|public\s+|private\s+|protected\s+|inline\s+|constexpr\s+|unsafe\s+|override\s+|open\s+|sealed\s+)*(?:def|fn|func|function\*?|class|struct|enum|trait|interface|impl|object|module|proc)\s+(?:\([^)]*\)\s*)?([A-Za-z_][\w]*(?:(?:::|\.)[A-Za-z_][\w]*)*)/;
256
+ // C-family definitions at low indent: `[return-type] Qualified::name(args...`
257
+ // with no trailing `;` (declarations) — captures the identifier before the
258
+ // first `(`. The return-type prefix is lazy so qualification stays intact.
259
+ const C_DEF_RE = /^(?:[A-Za-z_][\w:<>,*&~\s]*?[\s*&]+)?((?:[A-Za-z_~][\w]*::)*(?:~?[A-Za-z_][\w]*|operator\s*[^\s(]{1,3}))\s*\(/;
260
+ const C_CONTROL_RE = /^\s*(?:if|for|while|switch|return|else|do|catch|case|sizeof|new|delete|throw|goto|using|typedef)\b/;
261
+
262
+ function _sniffRemainderDefinitions(text, isCFamily) {
263
+ const names = [];
264
+ const seen = new Set();
265
+ const lines = text.split('\n', SNIFF_MAX_LINES);
266
+ for (let i = 0; i < lines.length; i++) {
267
+ const line = lines[i];
268
+ if (!/\S/.test(line)) continue;
269
+ let name = null;
270
+ const kw = line.match(KEYWORD_DEF_RE);
271
+ if (kw) name = kw[1];
272
+ else if (isCFamily && /^[A-Za-z_]/.test(line) && !line.trimEnd().endsWith(';') && !C_CONTROL_RE.test(line)) {
273
+ const m = line.match(C_DEF_RE);
274
+ if (m) name = m[1].replace(/\s+/g, '');
275
+ }
276
+ if (name && !seen.has(name)) {
277
+ seen.add(name);
278
+ names.push({ symbol: name, type: null, startLine: i + 1 }); // startLine relative; caller offsets
279
+ }
280
+ }
281
+ return names;
282
+ }
283
+
242
284
  // ---------------------------------------------------------------------------
243
285
  // Public API — single read
244
286
  // ---------------------------------------------------------------------------
@@ -293,6 +335,47 @@ async function _readFileUnpinned(req) {
293
335
  language = meta.language;
294
336
  }
295
337
 
338
+ // "What remains" trailer data (2026-07, within-file blind-spot fix): when
339
+ // a range read stops before EOF, record what the UNREAD remainder below
340
+ // the window contains — computed from the full chunk table BEFORE the
341
+ // overlap-narrowing just below. A bare "(lines a-b of N)" marker is
342
+ // provably ignored by agents (the botan-2738 shape: three reads, never
343
+ // past line 205 of 272, fix surface below); naming the symbols is what
344
+ // makes the remainder actionable. Whole-file reads and read-to-EOF stay
345
+ // byte-identical (unreadBelow stays null).
346
+ let unreadBelow = null;
347
+ if (wantsRange && sliced.totalLines > 0 && sliced.endLine < sliced.totalLines) {
348
+ // Token diet: a tiny remainder needs no symbol list — the range plus the
349
+ // continue command IS the affordance; names only earn their tokens when
350
+ // the unread span is big enough to hide a sibling branch.
351
+ const remainderLines = sliced.totalLines - sliced.endLine;
352
+ const seen = new Set();
353
+ let symbols = [];
354
+ if (remainderLines >= UNREAD_SYMBOLS_MIN_LINES) {
355
+ for (const c of chunks) {
356
+ if (c.startLine == null || c.startLine <= sliced.endLine) continue;
357
+ if (!c.symbol || seen.has(c.symbol)) continue;
358
+ seen.add(c.symbol);
359
+ symbols.push({ symbol: c.symbol, type: c.type ?? null, startLine: c.startLine });
360
+ }
361
+ // Index had no named chunks in the remainder (common for C/C++ where the
362
+ // chunker stores name:null) — sniff definition lines from the in-memory
363
+ // buffer instead. Zero I/O; capped at SNIFF_MAX_LINES.
364
+ if (symbols.length === 0 && disk.text != null) {
365
+ const remainder = _sliceLines(disk.text, disk.lineOffsets, sliced.endLine + 1, sliced.totalLines);
366
+ const isCFamily = C_FAMILY_EXTS.has(path.extname(absPath).toLowerCase());
367
+ symbols = _sniffRemainderDefinitions(remainder.text, isCFamily)
368
+ .map(s => ({ ...s, startLine: sliced.endLine + s.startLine }));
369
+ }
370
+ }
371
+ unreadBelow = {
372
+ startLine: sliced.endLine + 1,
373
+ endLine: sliced.totalLines,
374
+ symbols: symbols.slice(0, UNREAD_SYMBOLS_MAX),
375
+ moreCount: Math.max(0, symbols.length - UNREAD_SYMBOLS_MAX),
376
+ };
377
+ }
378
+
296
379
  // If a line range was requested, narrow attached chunks to the overlap.
297
380
  if (wantsRange && chunks.length) {
298
381
  chunks = chunks.filter(c =>
@@ -315,6 +398,7 @@ async function _readFileUnpinned(req) {
315
398
  range: wantsRange ? { startLine: sliced.startLine, endLine: sliced.endLine } : null,
316
399
  text: sliced.text,
317
400
  chunks,
401
+ unreadBelow,
318
402
  timings: { totalMs: +(performance.now() - t0).toFixed(2) },
319
403
  };
320
404
  }
@@ -362,6 +446,28 @@ export async function readFiles(files, opts = {}) {
362
446
  // Formatting
363
447
  // ---------------------------------------------------------------------------
364
448
 
449
+ /**
450
+ * Render the "what remains" trailer for a range read that stopped before
451
+ * EOF. Names the symbols in the unread remainder plus the exact continue
452
+ * command — the actionable form (a bare truncation marker is ignored;
453
+ * see the 2026-07 within-file design note). Returns '' when the read
454
+ * covered the whole file / reached EOF.
455
+ *
456
+ * @param {Object} result - readFile() result
457
+ * @param {{ command?: 'read'|'ss-read' }} [opts] - continue-command surface
458
+ * @returns {string} one line without trailing newline, or ''
459
+ */
460
+ export function renderUnreadBelow(result, { command = 'read' } = {}) {
461
+ const u = result?.unreadBelow;
462
+ if (!u) return '';
463
+ const names = (u.symbols || []).map(s => s.symbol).join(', ');
464
+ const more = u.moreCount > 0 ? ` +${u.moreCount} more` : '';
465
+ const cont = command === 'ss-read'
466
+ ? `ss-read ${result.file} ${u.startLine} ${u.endLine}`
467
+ : `read ${result.file} ${u.startLine}-${u.endLine}`;
468
+ return `# unread below (${u.startLine}-${u.endLine})${names ? ': ' + names + more : ''} — continue: ${cont}`;
469
+ }
470
+
365
471
  function _formatAgent(result) {
366
472
  if (!result.ok) {
367
473
  return `### ${result.file}\n[error] ${result.error}\n`;
@@ -377,7 +483,8 @@ function _formatAgent(result) {
377
483
  .filter(Boolean);
378
484
  if (names.length) symbolHint = `\nsymbols: ${names.join(', ')}`;
379
485
  }
380
- return `### ${result.file}${range}${symbolHint}\n${fence}\n${result.text}\n\`\`\`\n`;
486
+ const remainder = renderUnreadBelow(result);
487
+ return `### ${result.file}${range}${symbolHint}\n${fence}\n${result.text}\n\`\`\`${remainder ? '\n' + remainder : ''}\n`;
381
488
  }
382
489
 
383
490
  export function formatReadResults(results, format = 'agent') {
@@ -242,6 +242,11 @@ async function cmdFind(rawArgs) {
242
242
  if (r.neighbors && r.neighbors.rendered) {
243
243
  process.stdout.write(`### related (1-hop graph, ~${r.neighbors.tokens} tok)\n${r.neighbors.rendered}\n`);
244
244
  }
245
+ // Same-file span map (top-1, windowed chunk, verdict != YES): sibling
246
+ // symbols just outside the shown window + a copy-paste drill-in command.
247
+ if (r.sameFile && r.sameFile.rendered) {
248
+ process.stdout.write(`${r.sameFile.rendered}\n`);
249
+ }
245
250
  }
246
251
  if (!response.results || response.results.length === 0) process.stdout.write('(no matches)\n');
247
252
  process.exit(0);
@@ -297,7 +302,7 @@ async function cmdRead(args) {
297
302
  }
298
303
  }
299
304
  }
300
- const { readFile } = await import(path.join(REPO_ROOT, 'core/search/search-read.js'));
305
+ const { readFile, renderUnreadBelow } = await import(path.join(REPO_ROOT, 'core/search/search-read.js'));
301
306
  const r = await readFile({ path: file, projectRoot: PROJECT_ROOT, startLine: start ?? undefined, endLine: end ?? undefined });
302
307
  if (!r.ok) {
303
308
  process.stderr.write(`[ss-read] error: ${r.error}\n`);
@@ -305,7 +310,11 @@ async function cmdRead(args) {
305
310
  }
306
311
  const range = r.range ? ` (lines ${r.range.startLine}-${r.range.endLine} of ${r.totalLines})` : ` (${r.totalLines} lines)`;
307
312
  const fence = r.language ? '```' + r.language : '```';
308
- process.stdout.write(`# ss-read ${r.file}${range}\n${fence}\n${r.text}\n\`\`\`\n`);
313
+ // "What remains" trailer: on a range read that stops before EOF, one final
314
+ // line names the symbols in the unread remainder + the exact continue
315
+ // command (last line for recency — the actionable form of truncation).
316
+ const remainder = renderUnreadBelow(r, { command: 'ss-read' });
317
+ process.stdout.write(`# ss-read ${r.file}${range}\n${fence}\n${r.text}\n\`\`\`${remainder ? '\n' + remainder : ''}\n`);
309
318
  process.exit(0);
310
319
  }
311
320
 
@@ -441,6 +450,11 @@ async function cmdAgentSearch(rawArgs) {
441
450
  if (r.neighbors && r.neighbors.rendered) {
442
451
  process.stdout.write(`### related (1-hop graph, ~${r.neighbors.tokens} tok)\n${r.neighbors.rendered}\n`);
443
452
  }
453
+ // Same-file span map (top-1, windowed chunk, verdict != YES): sibling
454
+ // symbols just outside the shown window + a copy-paste drill-in command.
455
+ if (r.sameFile && r.sameFile.rendered) {
456
+ process.stdout.write(`${r.sameFile.rendered}\n`);
457
+ }
444
458
  }
445
459
 
446
460
  if (!response.results || response.results.length === 0) {
@@ -479,6 +493,8 @@ async function cmdAgentSearch(rawArgs) {
479
493
  sufficiencyReasons: Array.isArray(response.sufficiencyReasons) ? response.sufficiencyReasons : null,
480
494
  unresolvedExternalCount: typeof response.unresolvedExternalCount === 'number'
481
495
  ? response.unresolvedExternalCount : null,
496
+ sameFileMapTokens: response.results?.[0]?.sameFile?.tokens ?? null,
497
+ sameFileNeighborCount: response.results?.[0]?.sameFile?.neighbors?.length ?? null,
482
498
  };
483
499
  process.stdout.write(`\n<<SS_ROUTE_META>>${JSON.stringify(meta)}\n`);
484
500
  process.exit(0);
package/mcp/read-tool.js CHANGED
@@ -23,6 +23,16 @@ const ReadFileResultSchema = z.object({
23
23
  endLine: z.number().int().nullable().optional(),
24
24
  signature: z.string().nullable().optional(),
25
25
  })).optional(),
26
+ unreadBelow: z.object({
27
+ startLine: z.number().int(),
28
+ endLine: z.number().int(),
29
+ symbols: z.array(z.object({
30
+ symbol: z.string(),
31
+ type: z.string().nullable().optional(),
32
+ startLine: z.number().int().nullable().optional(),
33
+ })),
34
+ moreCount: z.number().int(),
35
+ }).nullable().optional(),
26
36
  error: z.string().optional(),
27
37
  timings: z.object({ totalMs: z.number() }).optional(),
28
38
  });
@@ -127,7 +127,14 @@ export async function handleSearch({ query, k, mode, structural, regex, format,
127
127
  const header = `${r.rank}. ${r.file}:${r.startLine}-${r.endLine} (score: ${r.score.toFixed(3)}, ${r.presentation})`;
128
128
  const symbolInfo = r.symbol ? `\n ${r.symbolType || 'symbol'}: ${r.symbol}` : '';
129
129
  const code = r.code ? `\n\`\`\`\n${r.code}\n\`\`\`` : '';
130
- return header + symbolInfo + code;
130
+ // Same-file span map (top-1, windowed chunk, verdict != YES) —
131
+ // MCP phrasing references the MCP drill-in tool name.
132
+ const sameFile = (r.sameFile && r.sameFile.neighbors?.length)
133
+ ? `\n Same file: ${r.sameFile.neighbors
134
+ .map(n => `${n.name} (${n.type === 'function' ? 'fn' : (n.type || 'sym')} ${n.startLine}-${n.endLine} ${n.position})`)
135
+ .join(' · ')} — sweep: read-semantic ${r.file}`
136
+ : '';
137
+ return header + symbolInfo + code + sameFile;
131
138
  });
132
139
 
133
140
  const summaries = agentResults
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sweet-search",
3
- "version": "2.6.14",
3
+ "version": "2.6.15",
4
4
  "description": "Sweet Search - SOTA Hybrid Code Search Engine with WASM CatBoost Query Router, Semantic/Lexical/Structural Search, and Multilingual Support",
5
5
  "type": "module",
6
6
  "main": "core/search/sweet-search.js",
@@ -167,13 +167,13 @@
167
167
  "vitest": "^4.0.16"
168
168
  },
169
169
  "optionalDependencies": {
170
- "@sweet-search/native-darwin-arm64": "2.6.14",
171
- "@sweet-search/native-darwin-x64": "2.6.14",
172
- "@sweet-search/native-linux-arm64-gnu": "2.6.14",
173
- "@sweet-search/native-linux-arm64-gnu-cuda": "2.6.14",
174
- "@sweet-search/native-linux-x64-gnu": "2.6.14",
175
- "@sweet-search/native-linux-x64-gnu-cuda": "2.6.14",
176
- "@sweet-search/bg-priority": "2.6.14"
170
+ "@sweet-search/native-darwin-arm64": "2.6.15",
171
+ "@sweet-search/native-darwin-x64": "2.6.15",
172
+ "@sweet-search/native-linux-arm64-gnu": "2.6.15",
173
+ "@sweet-search/native-linux-arm64-gnu-cuda": "2.6.15",
174
+ "@sweet-search/native-linux-x64-gnu": "2.6.15",
175
+ "@sweet-search/native-linux-x64-gnu-cuda": "2.6.15",
176
+ "@sweet-search/bg-priority": "2.6.15"
177
177
  },
178
178
  "engines": {
179
179
  "node": ">=18.0.0"