sift-light 1.0.3-5 → 1.0.5-1
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 +11 -0
- package/README.md +1 -1
- package/README.zh-CN.md +1 -1
- package/package.json +7 -4
- package/plugins/sift-light/.claude-plugin/plugin.json +1 -1
- package/plugins/sift-light/.codex-plugin/plugin.json +1 -1
- package/plugins/sift-light/.mcp.json +1 -1
- package/plugins/sift-light/concept-worker.mjs +83 -33
- package/plugins/sift-light/kimi.plugin.json +2 -2
- package/plugins/sift-light/omp-extension.mjs +3164 -2168
- package/plugins/sift-light/package.json +1 -1
- package/src/analysis-store.ts +23 -2
- package/src/analysis-types.ts +3 -0
- package/src/byspace-data-source.ts +104 -0
- package/src/concept-model.ts +33 -0
- package/src/concept-search.ts +116 -227
- package/src/concept-worker-client.ts +306 -0
- package/src/concept-worker-protocol.ts +86 -0
- package/src/concept-worker.mjs +83 -33
- package/src/concept-worker.ts +86 -32
- package/src/config-reader.ts +11 -2
- package/src/data-source-config.ts +98 -0
- package/src/data-source-http.ts +120 -0
- package/src/data-source-protocol.ts +113 -0
- package/src/data-sources.ts +113 -0
- package/src/errors.ts +6 -0
- package/src/evidence-service.ts +27 -4
- package/src/index.ts +1 -0
- package/src/language-capabilities.ts +16 -0
- package/src/mcp-semantic-judge.ts +8 -1
- package/src/mcp-server.mjs +2994 -1996
- package/src/mcp-server.ts +10 -5
- package/src/mcp.ts +3 -0
- package/src/omp-index.ts +1 -0
- package/src/package-smoke.mjs +81 -0
- package/src/relevance-ranking.ts +184 -0
- package/src/request-contract-catalog.ts +14 -1
- package/src/request-contract-error.ts +2 -0
- package/src/request-contract.ts +134 -9
- package/src/rg.ts +30 -7
- package/src/ripgrep-diagnostics.ts +21 -3
- package/src/ripgrep-executable.ts +28 -3
- package/src/scan-revisions.ts +1 -1
- package/src/service.ts +41 -3
- package/src/tool-schema.ts +19 -1
- package/src/types.ts +6 -0
- package/src/workspace-files.ts +3 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.0.5-1 — 2026-10-09
|
|
4
|
+
|
|
5
|
+
- Fix OMP plugin-cache searches when the generated bundle cannot resolve its platform ripgrep package. Bundled ripgrep remains preferred; an executable `rg` on `PATH` is now a bounded compatibility fallback, while explicit `SIFT_LIGHT_RG_PATH` values retain strict absolute-path and executable validation.
|
|
6
|
+
- Document the fallback and update the packaged Pi, OMP, MCP and Codex plugin artifacts.
|
|
7
|
+
|
|
8
|
+
## 1.0.3-6 — 2026-10-09
|
|
9
|
+
|
|
10
|
+
- Add bounded, read-only configured data-source adapters with explicit HTTPS/loopback validation, credential references and revision-aware source reads.
|
|
11
|
+
- Improve hybrid relevance ranking with request-scoped BM25 and semantic rank fusion, identifier-declaration candidates and distinct passage handling.
|
|
12
|
+
- Harden MCP/request-contract integration and add a packaged MCP smoke test so the published artifact is exercised through the real stdio transport.
|
|
13
|
+
|
|
3
14
|
## 1.0.3-5 — 2026-10-07
|
|
4
15
|
|
|
5
16
|
- Update the MCP SDK to 1.31.0 and Transformers.js to 4.3.1 to remove the dependency vulnerabilities reported in [#111](https://github.com/lightsifter/sift-light/issues/111), including the old ONNX proxy/logging dependency chain. Refresh compatible SDK transitive dependencies in the lockfile.
|
package/README.md
CHANGED
|
@@ -98,7 +98,7 @@ MCP needs Node.js 22.19+. Pi needs Pi 0.84.3+ with Node.js 22.19+ or Bun 1.4+. T
|
|
|
98
98
|
|
|
99
99
|
When upgrading from a release published under an earlier package identity, remove that earlier plugin and MCP registration before installing sift-light. This avoids loading two search tools or enforcement hooks in the same host.
|
|
100
100
|
|
|
101
|
-
To use your own ripgrep, set `SIFT_LIGHT_RG_PATH` to the absolute path of the executable in the MCP server or Pi process environment, then restart the host. It applies to content, filename and Git-source searches. Paths containing spaces work; aliases, shell functions, relative paths and `~` are not expanded. An unusable value fails explicitly instead of silently choosing another executable. If the bundled engine is missing, reinstall with optional dependencies enabled or point this setting at your own binary.
|
|
101
|
+
To use your own ripgrep, set `SIFT_LIGHT_RG_PATH` to the absolute path of the executable in the MCP server or Pi process environment, then restart the host. It applies to content, filename and Git-source searches. Paths containing spaces work; aliases, shell functions, relative paths and `~` are not expanded. An unusable value fails explicitly instead of silently choosing another executable. If the bundled engine is missing, sift-light uses an executable `rg` found on `PATH` as a compatibility fallback; otherwise reinstall with optional dependencies enabled or point this setting at your own binary.
|
|
102
102
|
|
|
103
103
|
### Pi
|
|
104
104
|
|
package/README.zh-CN.md
CHANGED
|
@@ -98,7 +98,7 @@ MCP 需要 Node.js 22.19+。Pi 需要 Pi 0.84.3+,以及 Node.js 22.19+ 或 Bun
|
|
|
98
98
|
|
|
99
99
|
如果你从更早的包身份升级,请先移除此前安装的插件和 MCP 注册,再安装 sift-light,避免同一宿主同时加载两套搜索工具或拦截钩子。
|
|
100
100
|
|
|
101
|
-
如需使用自己的 ripgrep,在 MCP 服务或 Pi 进程的环境变量中设置 `SIFT_LIGHT_RG_PATH` 为可执行文件的绝对路径,然后重启宿主。该设置统一作用于内容、文件名和 Git 源文件搜索。路径中的空格可用;不支持别名、Shell 函数、相对路径或 `~`
|
|
101
|
+
如需使用自己的 ripgrep,在 MCP 服务或 Pi 进程的环境变量中设置 `SIFT_LIGHT_RG_PATH` 为可执行文件的绝对路径,然后重启宿主。该设置统一作用于内容、文件名和 Git 源文件搜索。路径中的空格可用;不支持别名、Shell 函数、相对路径或 `~` 展开。配置无效时明确报错,不会另选程序。随包引擎缺失时,sift-light 会把 `PATH` 中找到的可执行 `rg` 作为兼容性 fallback;如果 PATH 中也没有,再保留可选依赖重新安装,或把该项指向自己的二进制。
|
|
102
102
|
|
|
103
103
|
### Pi
|
|
104
104
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sift-light",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.5-1",
|
|
4
4
|
"description": "Context-efficient local search for files, documents, notes and logs across Pi, OMP and MCP clients",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai-agent",
|
|
@@ -38,6 +38,7 @@
|
|
|
38
38
|
"src/syntax-worker.mjs",
|
|
39
39
|
"src/mcp-server.mjs",
|
|
40
40
|
"src/syntax-worker.toml",
|
|
41
|
+
"src/package-smoke.mjs",
|
|
41
42
|
"README.md",
|
|
42
43
|
"README.zh-CN.md",
|
|
43
44
|
"CONTRIBUTING.md",
|
|
@@ -62,8 +63,9 @@
|
|
|
62
63
|
"format:check": "oxfmt --check .",
|
|
63
64
|
"lint": "oxlint --type-aware --deny-warnings --report-unused-disable-directives src scripts",
|
|
64
65
|
"typecheck": "tsc --noEmit",
|
|
65
|
-
"test": "bun test ./doc/testing/test",
|
|
66
|
-
"test
|
|
66
|
+
"test:repo": "bun test ./doc/testing/test",
|
|
67
|
+
"test": "node src/package-smoke.mjs",
|
|
68
|
+
"test:node:repo": "bun run doc/testing/scripts/node-smoke.ts",
|
|
67
69
|
"benchmark": "bun run doc/testing/scripts/benchmark.ts",
|
|
68
70
|
"check": "bun run format:check && bun run lint && bun run typecheck && bun run build",
|
|
69
71
|
"pack:check": "bun pm pack --dry-run && bun run scripts/check-npm-publish.ts",
|
|
@@ -71,6 +73,7 @@
|
|
|
71
73
|
"build:concept-worker": "bun run scripts/build-concept-worker.ts",
|
|
72
74
|
"check:concept-worker": "bun run doc/testing/scripts/check-concept-worker.ts",
|
|
73
75
|
"test:concept": "bun run doc/testing/scripts/test-concept.ts",
|
|
76
|
+
"evaluate:relevance": "bun run doc/testing/scripts/relevance-eval.ts",
|
|
74
77
|
"evaluate:investigations": "bun run doc/testing/scripts/investigation-eval.ts",
|
|
75
78
|
"build:search-plugin": "bun run scripts/build-search-plugin.ts",
|
|
76
79
|
"check:search-plugin": "bun run doc/testing/scripts/check-search-plugin.ts",
|
|
@@ -79,7 +82,7 @@
|
|
|
79
82
|
"format:local:check": "bun run doc/testing/scripts/format-local.ts --check",
|
|
80
83
|
"lint:local": "oxlint --no-ignore --type-aware --tsconfig doc/testing/tsconfig.json --deny-warnings --report-unused-disable-directives src scripts doc/testing/test doc/testing/scripts",
|
|
81
84
|
"typecheck:local": "tsc --noEmit --project doc/testing/tsconfig.json",
|
|
82
|
-
"check:local": "bun run format:check && bun run format:local:check && bun run check:search-plugin && bun run check:concept-worker && bun run check:worker && bun run check:mcp && bun run lint:local && bun run typecheck:local && bun run test && bun run test:node && bun run benchmark"
|
|
85
|
+
"check:local": "bun run format:check && bun run format:local:check && bun run check:search-plugin && bun run check:concept-worker && bun run check:worker && bun run check:mcp && bun run lint:local && bun run typecheck:local && bun run test:repo && bun run test:node:repo && bun run benchmark"
|
|
83
86
|
},
|
|
84
87
|
"dependencies": {
|
|
85
88
|
"@ast-grep/lang-go": "0.0.6",
|
|
@@ -462,8 +462,12 @@ async function installConceptModel() {
|
|
|
462
462
|
|
|
463
463
|
// src/concept-worker.ts
|
|
464
464
|
var CACHE_IO_CONCURRENCY = 64;
|
|
465
|
+
var activeRequestId;
|
|
466
|
+
var extractor;
|
|
467
|
+
var modelVerified = false;
|
|
468
|
+
var modelLoads = 0;
|
|
465
469
|
function emitProgress(progress) {
|
|
466
|
-
process.stdout.write(`${JSON.stringify({ type: "progress", ...progress })}
|
|
470
|
+
process.stdout.write(`${JSON.stringify({ type: "progress", id: activeRequestId, ...progress })}
|
|
467
471
|
`);
|
|
468
472
|
}
|
|
469
473
|
async function readCachedEmbeddings(root, requested, onProgress) {
|
|
@@ -475,7 +479,7 @@ async function readCachedEmbeddings(root, requested, onProgress) {
|
|
|
475
479
|
}
|
|
476
480
|
return cached;
|
|
477
481
|
}
|
|
478
|
-
async function
|
|
482
|
+
async function readSingleRequest() {
|
|
479
483
|
const chunks = [];
|
|
480
484
|
let bytes = 0;
|
|
481
485
|
for await (const chunk of process.stdin) {
|
|
@@ -485,7 +489,9 @@ async function requestFromStdin() {
|
|
|
485
489
|
if (bytes > MAX_CONCEPT_WORKER_INPUT_BYTES)
|
|
486
490
|
throw new Error("Concept worker input exceeds its 64 MiB source protocol budget");
|
|
487
491
|
}
|
|
488
|
-
|
|
492
|
+
return JSON.parse(Buffer.concat(chunks).toString("utf8"));
|
|
493
|
+
}
|
|
494
|
+
function decodeRequest(request) {
|
|
489
495
|
if (!isRecordValue(request) || typeof request.query !== "string" || request.query.length === 0 || request.query.length > 256 || !request.query.isWellFormed() || /[\r\n\0]/u.test(request.query) || !Array.isArray(request.encodedPassages) || request.encodedPassages.some((item) => typeof item !== "string" || item.length === 0 || item.length % 4 !== 0 || item.length > Math.ceil((MAX_CONCEPT_CHARS + 256) * 4 / 3 * 4) + 4 || !/^[A-Za-z0-9+/]+={0,2}$/u.test(item)))
|
|
490
496
|
throw new Error("Invalid concept worker input");
|
|
491
497
|
const encodedPassages = request.encodedPassages.filter((item) => typeof item === "string");
|
|
@@ -497,12 +503,16 @@ async function requestFromStdin() {
|
|
|
497
503
|
throw new Error("Invalid decoded concept worker passage");
|
|
498
504
|
return { query: request.query, passages };
|
|
499
505
|
}
|
|
500
|
-
async function search() {
|
|
501
|
-
const request =
|
|
506
|
+
async function search(raw) {
|
|
507
|
+
const request = decodeRequest(raw);
|
|
502
508
|
const directory = conceptModelDirectory();
|
|
503
509
|
const cacheRoot = conceptCacheDirectory();
|
|
504
510
|
const stagingRoot = process.env.SIFT_LIGHT_CONCEPT_CACHE_STAGING_DIR ?? cacheRoot;
|
|
505
|
-
|
|
511
|
+
if (!modelVerified) {
|
|
512
|
+
await verifyConceptModel(directory);
|
|
513
|
+
modelVerified = true;
|
|
514
|
+
}
|
|
515
|
+
const modelReused = extractor !== undefined;
|
|
506
516
|
const requested = [
|
|
507
517
|
{ key: conceptEmbeddingKey("query", request.query), role: "query", text: request.query },
|
|
508
518
|
...request.passages.map((text) => ({
|
|
@@ -526,28 +536,27 @@ async function search() {
|
|
|
526
536
|
env.useFSCache = false;
|
|
527
537
|
env.useBrowserCache = false;
|
|
528
538
|
env.localModelPath = "/";
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
try {
|
|
537
|
-
created = await embedConceptInputs(extractor, missing, {
|
|
538
|
-
onCompletedEmbedding: async (embedding, completed, total) => {
|
|
539
|
-
try {
|
|
540
|
-
await writeConceptEmbedding(cacheRoot, embedding, stagingRoot);
|
|
541
|
-
} catch {
|
|
542
|
-
cacheWriteFailed = true;
|
|
543
|
-
}
|
|
544
|
-
emitProgress({ phase: "cache-write", completed, total, ...progressContext });
|
|
545
|
-
},
|
|
546
|
-
onBatch: (completed, total) => emitProgress({ phase: "embedding", completed, total, ...progressContext })
|
|
539
|
+
if (!extractor) {
|
|
540
|
+
emitProgress({ phase: "model-loading", ...progressContext });
|
|
541
|
+
extractor = await pipeline("feature-extraction", directory, {
|
|
542
|
+
local_files_only: true,
|
|
543
|
+
dtype: "q8",
|
|
544
|
+
device: "cpu",
|
|
545
|
+
session_options: { intraOpNumThreads: 4, interOpNumThreads: 1 }
|
|
547
546
|
});
|
|
548
|
-
|
|
549
|
-
await extractor.dispose();
|
|
547
|
+
modelLoads += 1;
|
|
550
548
|
}
|
|
549
|
+
created = await embedConceptInputs(extractor, missing, {
|
|
550
|
+
onCompletedEmbedding: async (embedding, completed, total) => {
|
|
551
|
+
try {
|
|
552
|
+
await writeConceptEmbedding(cacheRoot, embedding, stagingRoot);
|
|
553
|
+
} catch {
|
|
554
|
+
cacheWriteFailed = true;
|
|
555
|
+
}
|
|
556
|
+
emitProgress({ phase: "cache-write", completed, total, ...progressContext });
|
|
557
|
+
},
|
|
558
|
+
onBatch: (completed, total) => emitProgress({ phase: "embedding", completed, total, ...progressContext })
|
|
559
|
+
});
|
|
551
560
|
if (cacheWriteFailed)
|
|
552
561
|
warnings.push("Concept embedding cache write failed; ranking completed but some work may repeat");
|
|
553
562
|
}
|
|
@@ -575,6 +584,9 @@ async function search() {
|
|
|
575
584
|
}
|
|
576
585
|
process.stdout.write(JSON.stringify({
|
|
577
586
|
type: "result",
|
|
587
|
+
id: activeRequestId,
|
|
588
|
+
modelLoads,
|
|
589
|
+
modelReused,
|
|
578
590
|
scores,
|
|
579
591
|
cacheHits: cached.filter(Boolean).length,
|
|
580
592
|
cacheMisses: missing.length,
|
|
@@ -584,11 +596,49 @@ async function search() {
|
|
|
584
596
|
warnings: [...new Set(warnings)],
|
|
585
597
|
peakRssBytes: process.resourceUsage().maxRSS * 1024,
|
|
586
598
|
modelBytes: CONCEPT_ASSETS.reduce((sum, asset) => sum + asset.bytes, 0)
|
|
587
|
-
})
|
|
599
|
+
}) + `
|
|
600
|
+
`);
|
|
601
|
+
}
|
|
602
|
+
async function serve() {
|
|
603
|
+
let buffer = Buffer.alloc(0);
|
|
604
|
+
for await (const chunk of process.stdin) {
|
|
605
|
+
buffer = Buffer.concat([buffer, Buffer.from(chunk)]);
|
|
606
|
+
if (buffer.length > MAX_CONCEPT_WORKER_INPUT_BYTES)
|
|
607
|
+
throw new Error("Concept request frame exceeds 64 MiB");
|
|
608
|
+
let newline = buffer.indexOf(10);
|
|
609
|
+
while (newline >= 0) {
|
|
610
|
+
const line = buffer.subarray(0, newline);
|
|
611
|
+
buffer = buffer.subarray(newline + 1);
|
|
612
|
+
const raw = JSON.parse(line.toString("utf8"));
|
|
613
|
+
if (!isRecordValue(raw) || typeof raw.id !== "string" || !/^[a-f0-9-]{36}$/u.test(raw.id))
|
|
614
|
+
throw new Error("Invalid concept request identity");
|
|
615
|
+
activeRequestId = raw.id;
|
|
616
|
+
try {
|
|
617
|
+
await search(raw);
|
|
618
|
+
} catch (error) {
|
|
619
|
+
process.stdout.write(JSON.stringify({
|
|
620
|
+
type: "error",
|
|
621
|
+
id: activeRequestId,
|
|
622
|
+
message: error instanceof Error ? error.message.slice(0, 512) : "Concept inference failed"
|
|
623
|
+
}) + `
|
|
624
|
+
`);
|
|
625
|
+
return;
|
|
626
|
+
}
|
|
627
|
+
newline = buffer.indexOf(10);
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
if (buffer.length)
|
|
631
|
+
throw new Error("Incomplete concept request frame");
|
|
632
|
+
}
|
|
633
|
+
try {
|
|
634
|
+
if (process.argv.includes("--install-model"))
|
|
635
|
+
await installConceptModel();
|
|
636
|
+
else if (process.argv.includes("--infer"))
|
|
637
|
+
await search(await readSingleRequest());
|
|
638
|
+
else if (process.argv.includes("--serve"))
|
|
639
|
+
await serve();
|
|
640
|
+
else
|
|
641
|
+
throw new Error("Usage: sift-light-model --install-model");
|
|
642
|
+
} finally {
|
|
643
|
+
await extractor?.dispose();
|
|
588
644
|
}
|
|
589
|
-
if (process.argv.includes("--install-model"))
|
|
590
|
-
await installConceptModel();
|
|
591
|
-
else if (process.argv.includes("--infer"))
|
|
592
|
-
await search();
|
|
593
|
-
else
|
|
594
|
-
throw new Error("Usage: sift-light-model --install-model");
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sift-light",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.5-1",
|
|
4
4
|
"description": "Require sift-light for conventional local searches while keeping development tools available.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "baoer"
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"args": [
|
|
14
14
|
"--yes",
|
|
15
15
|
"--package",
|
|
16
|
-
"sift-light-runtime@npm:sift-light@1.0.
|
|
16
|
+
"sift-light-runtime@npm:sift-light@1.0.5-1",
|
|
17
17
|
"sift-light-mcp",
|
|
18
18
|
"--stdio"
|
|
19
19
|
],
|