paqad-ai 1.55.0 → 1.56.0
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 +8 -0
- package/dist/cli/index.js +213 -79
- package/dist/cli/index.js.map +1 -1
- package/dist/{feature-development-policy-CeD5nYnf.d.ts → feature-development-policy-D3DA8IzN.d.ts} +1 -0
- package/dist/index.d.ts +49 -3
- package/dist/index.js +116 -53
- package/dist/index.js.map +1 -1
- package/dist/kernel/gate.js +17 -0
- package/dist/kernel/gate.js.map +1 -1
- package/dist/rule-scripts/index.js +16 -0
- package/dist/rule-scripts/index.js.map +1 -1
- package/dist/stage-evidence/live-writer.d.ts +2 -2
- package/dist/stage-evidence/live-writer.js +16 -0
- package/dist/stage-evidence/live-writer.js.map +1 -1
- package/dist/stage-evidence/marker-parse.d.ts +2 -2
- package/dist/stage-evidence/marker-parse.js +16 -0
- package/dist/stage-evidence/marker-parse.js.map +1 -1
- package/dist/stage-evidence/narration.d.ts +2 -2
- package/dist/stage-evidence/narration.js +16 -0
- package/dist/stage-evidence/narration.js.map +1 -1
- package/dist/{stages-DwQ5X3Bd.d.ts → stages-B3Mevt44.d.ts} +1 -1
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# paqad-ai
|
|
2
2
|
|
|
3
|
+
## 1.56.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 997bbc0: RAG retrieval was dark: local-model cosine scores on hybrid-fused results rarely reached the 0.75 precision floor, so every query fell back below-floor and the model never received a slice. Retrieval now uses floor-with-relief — the 0.75 floor still marks high-confidence hits, but when nothing clears it the top slices at or above a new `rag_relief_floor` (default 0.35, chosen from live probe data) are delivered tagged low-confidence instead of nothing. When retrieval still finds nothing, the session-context artifact carries one honest line naming the best score instead of silently omitting the section.
|
|
8
|
+
|
|
9
|
+
Also wires the previously-defined-but-never-recorded `used` RAG-evidence event: the background context worker now records what it actually delivered into the artifact (injected sections, slice/pointer counts, top score, bytes) into the per-feature `.paqad/ledger/feature-evidence/<feature>/rag.jsonl`, so you can see whether RAG was used — not just that retrieval ran. Adds a `paqad-ai rag probe "<query>"` verb that prints pre-floor top-10 fused scores for diagnosis.
|
|
10
|
+
|
|
3
11
|
## 1.55.0
|
|
4
12
|
|
|
5
13
|
### Minor Changes
|
package/dist/cli/index.js
CHANGED
|
@@ -344,6 +344,11 @@ function defaultIntelligenceConfig() {
|
|
|
344
344
|
return {
|
|
345
345
|
rag_enabled: false,
|
|
346
346
|
rag_similarity_threshold: 0.75,
|
|
347
|
+
// Issue #354 — chosen from live probe data on this repo: genuine on-target queries
|
|
348
|
+
// top out at 0.37-0.52 (local MiniLM cosine on hybrid-fused results), while a truly
|
|
349
|
+
// irrelevant query ("kubernetes ingress") tops at 0.32. 0.35 sits between: it
|
|
350
|
+
// delivers the weakest real hit and darkens the negative control.
|
|
351
|
+
rag_relief_floor: 0.35,
|
|
347
352
|
rag_top_n: 20,
|
|
348
353
|
rag_max_file_size: 153600,
|
|
349
354
|
benchmark_gates: { ...DEFAULT_BENCHMARK_GATES },
|
|
@@ -368,6 +373,7 @@ function normalizeIntelligenceConfig(input3) {
|
|
|
368
373
|
embedding_provider: provider,
|
|
369
374
|
embedding_model: input3.embedding_model ?? (provider ? getDefaultEmbeddingModel(provider) : void 0),
|
|
370
375
|
rag_similarity_threshold: input3.rag_similarity_threshold ?? defaults.rag_similarity_threshold,
|
|
376
|
+
rag_relief_floor: input3.rag_relief_floor ?? defaults.rag_relief_floor,
|
|
371
377
|
rag_top_n: input3.rag_top_n ?? defaults.rag_top_n,
|
|
372
378
|
rag_max_file_size: input3.rag_max_file_size ?? defaults.rag_max_file_size,
|
|
373
379
|
rag_base_branch: input3.rag_base_branch ?? defaults.rag_base_branch,
|
|
@@ -648,6 +654,7 @@ function resolveFrameworkConfigFromMap(raw) {
|
|
|
648
654
|
embedding_provider: provider,
|
|
649
655
|
embedding_model: embeddingModel,
|
|
650
656
|
rag_similarity_threshold: rn("rag_similarity_threshold"),
|
|
657
|
+
rag_relief_floor: rn("rag_relief_floor"),
|
|
651
658
|
rag_top_n: rn("rag_top_n"),
|
|
652
659
|
rag_max_file_size: rn("rag_max_file_size"),
|
|
653
660
|
rag_base_branch: raw.get("rag_base_branch")?.trim() || void 0
|
|
@@ -1008,6 +1015,7 @@ function frameworkOverridesToFlat(overrides) {
|
|
|
1008
1015
|
i.rag_similarity_threshold,
|
|
1009
1016
|
d.intelligence.rag_similarity_threshold
|
|
1010
1017
|
);
|
|
1018
|
+
put("rag_relief_floor", i.rag_relief_floor, d.intelligence.rag_relief_floor);
|
|
1011
1019
|
put("rag_top_n", i.rag_top_n, d.intelligence.rag_top_n);
|
|
1012
1020
|
put("rag_max_file_size", i.rag_max_file_size, d.intelligence.rag_max_file_size);
|
|
1013
1021
|
put("rag_base_branch", i.rag_base_branch, d.intelligence.rag_base_branch);
|
|
@@ -1309,6 +1317,15 @@ var init_framework_config = __esm({
|
|
|
1309
1317
|
section: "Intelligence / RAG",
|
|
1310
1318
|
comment: "Minimum cosine similarity for a chunk to be retrieved."
|
|
1311
1319
|
},
|
|
1320
|
+
{
|
|
1321
|
+
key: "rag_relief_floor",
|
|
1322
|
+
env: "PAQAD_RAG_RELIEF_FLOOR",
|
|
1323
|
+
type: "number",
|
|
1324
|
+
default: 0.35,
|
|
1325
|
+
group: "rag",
|
|
1326
|
+
section: "Intelligence / RAG",
|
|
1327
|
+
comment: "Relief band for floor-with-relief. When nothing clears rag_similarity_threshold, the top slices at or above this lower score are delivered tagged low-confidence."
|
|
1328
|
+
},
|
|
1312
1329
|
{
|
|
1313
1330
|
key: "rag_top_n",
|
|
1314
1331
|
env: "PAQAD_RAG_TOP_N",
|
|
@@ -1586,6 +1603,7 @@ var init_framework_config = __esm({
|
|
|
1586
1603
|
"rag_embedding_provider",
|
|
1587
1604
|
"rag_embedding_model",
|
|
1588
1605
|
"rag_similarity_threshold",
|
|
1606
|
+
"rag_relief_floor",
|
|
1589
1607
|
"rag_top_n",
|
|
1590
1608
|
"rag_max_file_size",
|
|
1591
1609
|
"rag_base_branch"
|
|
@@ -3578,6 +3596,7 @@ var init_project_profile_schema = __esm({
|
|
|
3578
3596
|
},
|
|
3579
3597
|
embedding_model: { type: "string" },
|
|
3580
3598
|
rag_similarity_threshold: { type: "number" },
|
|
3599
|
+
rag_relief_floor: { type: "number" },
|
|
3581
3600
|
rag_top_n: { type: "integer", minimum: 1 },
|
|
3582
3601
|
rag_max_file_size: { type: "integer", minimum: 1 },
|
|
3583
3602
|
rag_base_branch: { type: "string" },
|
|
@@ -10403,6 +10422,21 @@ import { existsSync as existsSync18, statSync as statSync3 } from "fs";
|
|
|
10403
10422
|
import { mkdir as mkdir9, readdir, rename as rename3, rm as rm3 } from "fs/promises";
|
|
10404
10423
|
import { homedir as homedir7 } from "os";
|
|
10405
10424
|
import { extname, join as join36, resolve as resolve3, sep as sep2 } from "path";
|
|
10425
|
+
function toRetrievalResult(candidates, extra) {
|
|
10426
|
+
return {
|
|
10427
|
+
vector_scores: new Map(candidates.map((candidate) => [candidate.id, candidate.score])),
|
|
10428
|
+
chunks_retrieved: candidates.length,
|
|
10429
|
+
retrieved_chunk_ids: candidates.map((candidate) => candidate.id),
|
|
10430
|
+
retrieved_source_files: candidates.map((candidate) => candidate.source_file),
|
|
10431
|
+
retrieved_chunks: candidates.map((candidate) => ({
|
|
10432
|
+
id: candidate.id,
|
|
10433
|
+
source_file: candidate.source_file,
|
|
10434
|
+
content: candidate.content
|
|
10435
|
+
})),
|
|
10436
|
+
best_score: extra.bestScore,
|
|
10437
|
+
low_confidence: extra.lowConfidence
|
|
10438
|
+
};
|
|
10439
|
+
}
|
|
10406
10440
|
function queryTextFromTask(taskDescription, keywords = [], targetFilePath, symbols = []) {
|
|
10407
10441
|
const parts = [taskDescription, ...keywords, targetFilePath, ...symbols].filter(Boolean);
|
|
10408
10442
|
return parts.join("\n");
|
|
@@ -10450,7 +10484,7 @@ function fuseHybridRanking(dense, queryText) {
|
|
|
10450
10484
|
const fused = reciprocalRankFusion([denseOrder, lexical]);
|
|
10451
10485
|
return fused.map((entry) => byId.get(entry.id)).filter((hit) => hit !== void 0);
|
|
10452
10486
|
}
|
|
10453
|
-
var VISION_MAX_CHUNK_CHARS, CRS_EMBED_BATCH_SIZE, HYBRID_FUSION_POOL_MULTIPLIER, CRS_REVERT_RETENTION_MS, PARTIAL_VECTOR_INDEX, PARTIAL_VECTOR_META, RagService;
|
|
10487
|
+
var VISION_MAX_CHUNK_CHARS, CRS_EMBED_BATCH_SIZE, HYBRID_FUSION_POOL_MULTIPLIER, RELIEF_SLICE_CAP, CRS_REVERT_RETENTION_MS, PARTIAL_VECTOR_INDEX, PARTIAL_VECTOR_META, RagService;
|
|
10454
10488
|
var init_service = __esm({
|
|
10455
10489
|
"src/rag/service.ts"() {
|
|
10456
10490
|
"use strict";
|
|
@@ -10484,6 +10518,7 @@ var init_service = __esm({
|
|
|
10484
10518
|
VISION_MAX_CHUNK_CHARS = 2e3;
|
|
10485
10519
|
CRS_EMBED_BATCH_SIZE = 32;
|
|
10486
10520
|
HYBRID_FUSION_POOL_MULTIPLIER = 4;
|
|
10521
|
+
RELIEF_SLICE_CAP = 2;
|
|
10487
10522
|
CRS_REVERT_RETENTION_MS = 24 * 60 * 60 * 1e3;
|
|
10488
10523
|
PARTIAL_VECTOR_INDEX = PATHS.VECTOR_INDEX.replace(/\.json$/, ".partial.json");
|
|
10489
10524
|
PARTIAL_VECTOR_META = PATHS.VECTOR_META.replace(/\.json$/, ".partial.json");
|
|
@@ -10789,59 +10824,30 @@ var init_service = __esm({
|
|
|
10789
10824
|
if (!skipSync) {
|
|
10790
10825
|
await this.syncVectorIndex(syncResult, intelligence);
|
|
10791
10826
|
}
|
|
10792
|
-
const provider = await this.providerFactory(this.projectRoot, intelligence);
|
|
10793
|
-
const [queryVector] = await provider.embed(
|
|
10794
|
-
queryTextFromTask(
|
|
10795
|
-
input3.taskDescription,
|
|
10796
|
-
input3.keywords,
|
|
10797
|
-
input3.targetFilePath,
|
|
10798
|
-
input3.symbolReferences ?? []
|
|
10799
|
-
)
|
|
10800
|
-
);
|
|
10801
10827
|
const limit = topN ?? intelligence.rag_top_n;
|
|
10802
|
-
const
|
|
10803
|
-
const [
|
|
10804
|
-
|
|
10805
|
-
|
|
10806
|
-
]);
|
|
10807
|
-
const dense = [...fileResults, ...visionResults].sort(
|
|
10808
|
-
(left, right) => right.score - left.score
|
|
10809
|
-
);
|
|
10810
|
-
const queryText = queryTextFromTask(
|
|
10811
|
-
input3.taskDescription,
|
|
10812
|
-
input3.keywords,
|
|
10813
|
-
input3.targetFilePath,
|
|
10814
|
-
input3.symbolReferences ?? []
|
|
10828
|
+
const scored = await this.scoreCandidates(input3, limit, intelligence);
|
|
10829
|
+
const bestScore = scored.length > 0 ? scored[0].score : void 0;
|
|
10830
|
+
const aboveFloor = scored.filter(
|
|
10831
|
+
(candidate) => candidate.score >= intelligence.rag_similarity_threshold
|
|
10815
10832
|
);
|
|
10816
|
-
|
|
10817
|
-
|
|
10818
|
-
|
|
10819
|
-
const
|
|
10820
|
-
|
|
10821
|
-
|
|
10822
|
-
if (filtered.length === 0) {
|
|
10823
|
-
appendRagAudit(this.projectRoot, "WARN", "rag-fallback", {
|
|
10824
|
-
reason: "below-similarity-threshold"
|
|
10825
|
-
});
|
|
10826
|
-
return {
|
|
10827
|
-
vector_scores: /* @__PURE__ */ new Map(),
|
|
10828
|
-
chunks_retrieved: 0,
|
|
10829
|
-
retrieved_chunk_ids: [],
|
|
10830
|
-
retrieved_source_files: [],
|
|
10831
|
-
retrieved_chunks: [],
|
|
10832
|
-
fallback_reason: "below-similarity-threshold"
|
|
10833
|
-
};
|
|
10833
|
+
if (aboveFloor.length > 0) {
|
|
10834
|
+
return toRetrievalResult(aboveFloor, { bestScore, lowConfidence: false });
|
|
10835
|
+
}
|
|
10836
|
+
const relief = scored.filter((candidate) => candidate.score >= intelligence.rag_relief_floor).slice(0, RELIEF_SLICE_CAP);
|
|
10837
|
+
if (relief.length > 0) {
|
|
10838
|
+
return toRetrievalResult(relief, { bestScore, lowConfidence: true });
|
|
10834
10839
|
}
|
|
10840
|
+
appendRagAudit(this.projectRoot, "WARN", "rag-fallback", {
|
|
10841
|
+
reason: "below-similarity-threshold"
|
|
10842
|
+
});
|
|
10835
10843
|
return {
|
|
10836
|
-
vector_scores: new Map(
|
|
10837
|
-
chunks_retrieved:
|
|
10838
|
-
retrieved_chunk_ids:
|
|
10839
|
-
retrieved_source_files:
|
|
10840
|
-
retrieved_chunks:
|
|
10841
|
-
|
|
10842
|
-
|
|
10843
|
-
content: result.item.content
|
|
10844
|
-
}))
|
|
10844
|
+
vector_scores: /* @__PURE__ */ new Map(),
|
|
10845
|
+
chunks_retrieved: 0,
|
|
10846
|
+
retrieved_chunk_ids: [],
|
|
10847
|
+
retrieved_source_files: [],
|
|
10848
|
+
retrieved_chunks: [],
|
|
10849
|
+
fallback_reason: "below-similarity-threshold",
|
|
10850
|
+
best_score: bestScore
|
|
10845
10851
|
};
|
|
10846
10852
|
} catch (error) {
|
|
10847
10853
|
appendRagAudit(this.projectRoot, "WARN", "rag-fallback", {
|
|
@@ -10865,6 +10871,63 @@ var init_service = __esm({
|
|
|
10865
10871
|
const syncResult = await this.refreshContext();
|
|
10866
10872
|
return this.retrieveWithSyncPolicy(syncResult, input3, topN, true);
|
|
10867
10873
|
}
|
|
10874
|
+
/**
|
|
10875
|
+
* Embed the query, query the file + vision indexes over a widened pool, fuse the
|
|
10876
|
+
* dense and lexical (BM25) legs, optionally rerank, and return the top-`limit`
|
|
10877
|
+
* candidates with their scores — BEFORE any similarity/relief floor is applied.
|
|
10878
|
+
*
|
|
10879
|
+
* This is the single canonical scoring path (RULE-04a1): both floor-with-relief
|
|
10880
|
+
* retrieval ({@link retrieveWithSyncPolicy}) and the `probe` diagnostic call it, so
|
|
10881
|
+
* a probe reports exactly the scores retrieval sees. Assumes the index is present
|
|
10882
|
+
* and valid — callers check {@link getStatus} first.
|
|
10883
|
+
*/
|
|
10884
|
+
async scoreCandidates(input3, limit, intelligence) {
|
|
10885
|
+
const provider = await this.providerFactory(this.projectRoot, intelligence);
|
|
10886
|
+
const queryText = queryTextFromTask(
|
|
10887
|
+
input3.taskDescription,
|
|
10888
|
+
input3.keywords,
|
|
10889
|
+
input3.targetFilePath,
|
|
10890
|
+
input3.symbolReferences ?? []
|
|
10891
|
+
);
|
|
10892
|
+
const [queryVector] = await provider.embed(queryText);
|
|
10893
|
+
const pool = limit * HYBRID_FUSION_POOL_MULTIPLIER;
|
|
10894
|
+
const [fileResults, visionResults] = await Promise.all([
|
|
10895
|
+
this.vectorIndex.query(this.projectRoot, queryVector, pool),
|
|
10896
|
+
this.visionVectorIndex.query(this.projectRoot, queryVector, pool)
|
|
10897
|
+
]);
|
|
10898
|
+
const dense = [...fileResults, ...visionResults].sort(
|
|
10899
|
+
(left, right) => right.score - left.score
|
|
10900
|
+
);
|
|
10901
|
+
const fused = fuseHybridRanking(dense, queryText);
|
|
10902
|
+
const reranked = await this.applyReranking(queryText, fused, intelligence.reranking);
|
|
10903
|
+
return reranked.slice(0, limit).map((result) => ({
|
|
10904
|
+
id: result.item.id,
|
|
10905
|
+
source_file: result.item.source_file,
|
|
10906
|
+
content: result.item.content,
|
|
10907
|
+
score: result.score
|
|
10908
|
+
}));
|
|
10909
|
+
}
|
|
10910
|
+
/**
|
|
10911
|
+
* Issue #354 diagnostic — return the top-`limit` scored candidates for a query
|
|
10912
|
+
* BEFORE the similarity/relief floor, so `paqad-ai rag probe` can show how far real
|
|
10913
|
+
* scores sit from the floor on a live repo (the gap that made retrieval dark).
|
|
10914
|
+
* Refreshes the index internally like {@link retrieveForEval}. Returns `[]` when rag
|
|
10915
|
+
* is off or the index is missing/invalid.
|
|
10916
|
+
*/
|
|
10917
|
+
async probe(input3, topN) {
|
|
10918
|
+
const intelligence = normalizeIntelligenceConfig(
|
|
10919
|
+
readProjectProfile(this.projectRoot)?.intelligence
|
|
10920
|
+
);
|
|
10921
|
+
if (!intelligence.rag_enabled || !intelligence.embedding_provider) {
|
|
10922
|
+
return [];
|
|
10923
|
+
}
|
|
10924
|
+
await this.refreshContext();
|
|
10925
|
+
const status = await this.getStatus();
|
|
10926
|
+
if (!status.index_present || !status.valid) {
|
|
10927
|
+
return [];
|
|
10928
|
+
}
|
|
10929
|
+
return this.scoreCandidates(input3, topN ?? intelligence.rag_top_n, intelligence);
|
|
10930
|
+
}
|
|
10868
10931
|
/**
|
|
10869
10932
|
* Accept plain text a consumer extracted from an image (via OCR, captioning,
|
|
10870
10933
|
* etc.) into the retrieval index. The engine never reads the image itself; it
|
|
@@ -29306,7 +29369,7 @@ init_cancelled_error();
|
|
|
29306
29369
|
init_events();
|
|
29307
29370
|
|
|
29308
29371
|
// src/index.ts
|
|
29309
|
-
var VERSION = "1.
|
|
29372
|
+
var VERSION = "1.56.0";
|
|
29310
29373
|
|
|
29311
29374
|
// src/cli/commands/audit.ts
|
|
29312
29375
|
init_esm_shims();
|
|
@@ -40710,9 +40773,6 @@ function truncateSlice(content) {
|
|
|
40710
40773
|
return `${body.slice(0, MAX_SLICE_CHARS)}
|
|
40711
40774
|
\u2026[slice truncated at ${MAX_SLICE_CHARS} chars]`;
|
|
40712
40775
|
}
|
|
40713
|
-
function applyPrecisionFloor(slices, floor) {
|
|
40714
|
-
return slices.filter((slice) => typeof slice.score === "number" && slice.score >= floor);
|
|
40715
|
-
}
|
|
40716
40776
|
function formatScore(score) {
|
|
40717
40777
|
if (typeof score !== "number") {
|
|
40718
40778
|
return "";
|
|
@@ -40730,11 +40790,19 @@ function dedupeSlices(slices) {
|
|
|
40730
40790
|
}
|
|
40731
40791
|
return unique;
|
|
40732
40792
|
}
|
|
40733
|
-
function composeRetrievalSection(slices) {
|
|
40793
|
+
function composeRetrievalSection(slices, opts = {}) {
|
|
40734
40794
|
if (slices.length === 0) {
|
|
40795
|
+
if (typeof opts.bestScore === "number") {
|
|
40796
|
+
return `## Retrieved context \u2014 none above the confidence floor (best match ${Math.round(
|
|
40797
|
+
opts.bestScore * 100
|
|
40798
|
+
)}%)
|
|
40799
|
+
> The index had no slice confident enough to inject for the files in play; read the live files directly.
|
|
40800
|
+
`;
|
|
40801
|
+
}
|
|
40735
40802
|
return "";
|
|
40736
40803
|
}
|
|
40737
40804
|
const capped = dedupeSlices(slices).slice(0, MAX_RETRIEVAL_SLICES);
|
|
40805
|
+
const lowConfidence = capped.some((slice) => slice.lowConfidence);
|
|
40738
40806
|
const blocks = capped.map(
|
|
40739
40807
|
(slice) => `### ${slice.source_file}${formatScore(slice.score)}
|
|
40740
40808
|
\`\`\`
|
|
@@ -40744,8 +40812,10 @@ ${truncateSlice(
|
|
|
40744
40812
|
\`\`\``
|
|
40745
40813
|
).join("\n\n");
|
|
40746
40814
|
const noun = capped.length === 1 ? "slice" : "slices";
|
|
40747
|
-
|
|
40748
|
-
|
|
40815
|
+
const heading = lowConfidence ? `## Retrieved context \u2014 ${capped.length} low-confidence ${noun} (nothing cleared the confidence floor)` : `## Retrieved context \u2014 ${capped.length} ${noun} relevant to the files in play`;
|
|
40816
|
+
const guidance = lowConfidence ? `> Weak matches, below the confidence floor \u2014 treat as loose leads only and verify against the live files before relying on them.` : `> Advisory hints retrieved from the index, not ground truth. Re-read the live files before relying on them; the match % is the index's confidence, not correctness.`;
|
|
40817
|
+
return `${heading}
|
|
40818
|
+
${guidance}
|
|
40749
40819
|
|
|
40750
40820
|
${blocks}
|
|
40751
40821
|
`;
|
|
@@ -40775,7 +40845,7 @@ async function gatherWorkingSetSlices(projectRoot, options = {}) {
|
|
|
40775
40845
|
const changedPaths = options.changedPaths ?? (await loadChangeEvidence(projectRoot)).files;
|
|
40776
40846
|
const promptQuery = options.query?.trim();
|
|
40777
40847
|
if (changedPaths.length === 0 && !promptQuery) {
|
|
40778
|
-
return [];
|
|
40848
|
+
return { slices: [], bestScore: null };
|
|
40779
40849
|
}
|
|
40780
40850
|
const intelligence = normalizeIntelligenceConfig(readProjectProfile(projectRoot)?.intelligence);
|
|
40781
40851
|
let effectiveTopN = options.topN;
|
|
@@ -40783,7 +40853,7 @@ async function gatherWorkingSetSlices(projectRoot, options = {}) {
|
|
|
40783
40853
|
const routing = options.routing ?? { scope: deriveScopeFromWorkingSet(changedPaths) };
|
|
40784
40854
|
const gate = gateRetrieval({ ...routing, baseTopN: intelligence.rag_top_n });
|
|
40785
40855
|
if (gate.skip) {
|
|
40786
|
-
return [];
|
|
40856
|
+
return { slices: [], bestScore: null };
|
|
40787
40857
|
}
|
|
40788
40858
|
effectiveTopN = gate.topN;
|
|
40789
40859
|
}
|
|
@@ -40793,15 +40863,15 @@ async function gatherWorkingSetSlices(projectRoot, options = {}) {
|
|
|
40793
40863
|
try {
|
|
40794
40864
|
result = await service.retrieveForEval(retrievalInput, effectiveTopN);
|
|
40795
40865
|
} catch {
|
|
40796
|
-
return [];
|
|
40866
|
+
return { slices: [], bestScore: null };
|
|
40797
40867
|
}
|
|
40798
40868
|
const slices = result.retrieved_chunks.map((chunk) => ({
|
|
40799
40869
|
source_file: chunk.source_file,
|
|
40800
40870
|
content: chunk.content,
|
|
40801
|
-
score: result.vector_scores.get(chunk.id)
|
|
40871
|
+
score: result.vector_scores.get(chunk.id),
|
|
40872
|
+
lowConfidence: result.low_confidence === true
|
|
40802
40873
|
}));
|
|
40803
|
-
const
|
|
40804
|
-
const aboveFloor = applyPrecisionFloor(slices, floor);
|
|
40874
|
+
const bestScore = typeof result.best_score === "number" ? result.best_score : null;
|
|
40805
40875
|
const scope = options.scope ?? scopeForWorkflow(options.routing?.workflow);
|
|
40806
40876
|
if (options.recordEvidence) {
|
|
40807
40877
|
recordRagEvidence(
|
|
@@ -40819,7 +40889,7 @@ async function gatherWorkingSetSlices(projectRoot, options = {}) {
|
|
|
40819
40889
|
}
|
|
40820
40890
|
);
|
|
40821
40891
|
}
|
|
40822
|
-
return filterToScope(
|
|
40892
|
+
return { slices: filterToScope(slices, scope), bestScore };
|
|
40823
40893
|
}
|
|
40824
40894
|
|
|
40825
40895
|
// src/pipeline/session-route.ts
|
|
@@ -40858,6 +40928,9 @@ function readSessionRoute(projectRoot) {
|
|
|
40858
40928
|
}
|
|
40859
40929
|
}
|
|
40860
40930
|
|
|
40931
|
+
// src/cli/commands/rag.ts
|
|
40932
|
+
init_recorder();
|
|
40933
|
+
|
|
40861
40934
|
// src/rag/background-sync.ts
|
|
40862
40935
|
init_esm_shims();
|
|
40863
40936
|
import { join as join196 } from "path";
|
|
@@ -41465,6 +41538,42 @@ function createRagCommand() {
|
|
|
41465
41538
|
process.stdout.write(`${JSON.stringify(await service.getStatus(), null, 2)}
|
|
41466
41539
|
`);
|
|
41467
41540
|
});
|
|
41541
|
+
command.command("probe <query>").description(
|
|
41542
|
+
"Print the top pre-floor retrieval scores for a query (diagnostic; no floor filter)"
|
|
41543
|
+
).option("--project-root <path>", "Project root", process.cwd()).option("--top-n <n>", "How many candidates to show", "10").action(async (query, options) => {
|
|
41544
|
+
const service = new RagService(options.projectRoot);
|
|
41545
|
+
const { intelligence } = resolveFrameworkConfig(options.projectRoot);
|
|
41546
|
+
const floor = intelligence.rag_similarity_threshold;
|
|
41547
|
+
const reliefFloor = intelligence.rag_relief_floor;
|
|
41548
|
+
const topN = Number.parseInt(options.topN, 10);
|
|
41549
|
+
const candidates = await service.probe(
|
|
41550
|
+
{ taskDescription: query, keywords: [] },
|
|
41551
|
+
Number.isFinite(topN) && topN > 0 ? topN : 10
|
|
41552
|
+
);
|
|
41553
|
+
const rows = candidates.map((candidate, index) => ({
|
|
41554
|
+
rank: index + 1,
|
|
41555
|
+
source_file: candidate.source_file,
|
|
41556
|
+
score: Number(candidate.score.toFixed(4)),
|
|
41557
|
+
above_floor: candidate.score >= floor,
|
|
41558
|
+
above_relief: candidate.score >= reliefFloor,
|
|
41559
|
+
gap_to_floor: Number((floor - candidate.score).toFixed(4))
|
|
41560
|
+
}));
|
|
41561
|
+
process.stdout.write(
|
|
41562
|
+
`${JSON.stringify(
|
|
41563
|
+
{
|
|
41564
|
+
query,
|
|
41565
|
+
similarity_threshold: floor,
|
|
41566
|
+
relief_floor: reliefFloor,
|
|
41567
|
+
candidates: rows.length,
|
|
41568
|
+
best_score: rows[0]?.score ?? null,
|
|
41569
|
+
rows
|
|
41570
|
+
},
|
|
41571
|
+
null,
|
|
41572
|
+
2
|
|
41573
|
+
)}
|
|
41574
|
+
`
|
|
41575
|
+
);
|
|
41576
|
+
});
|
|
41468
41577
|
command.command("refresh-context").description(
|
|
41469
41578
|
"Sync the index, retrieve slices, and recompose session context (background worker)"
|
|
41470
41579
|
).option("--project-root <path>", "Project root", process.cwd()).option("--quiet", "Suppress output (used by the background trigger)").action(async (options) => {
|
|
@@ -41486,24 +41595,24 @@ function createRagCommand() {
|
|
|
41486
41595
|
return;
|
|
41487
41596
|
}
|
|
41488
41597
|
const sync = await backgroundIndexSync(options.projectRoot);
|
|
41489
|
-
const slices = retrieves ? await gatherWorkingSetSlices(options.projectRoot, {
|
|
41598
|
+
const { slices, bestScore } = retrieves ? await gatherWorkingSetSlices(options.projectRoot, {
|
|
41490
41599
|
recordEvidence: { adapter: "engine" },
|
|
41491
41600
|
query: route?.query
|
|
41492
|
-
}) : [];
|
|
41493
|
-
const
|
|
41494
|
-
|
|
41495
|
-
|
|
41496
|
-
|
|
41497
|
-
|
|
41498
|
-
|
|
41499
|
-
|
|
41500
|
-
|
|
41501
|
-
|
|
41502
|
-
|
|
41503
|
-
}
|
|
41601
|
+
}) : { slices: [], bestScore: null };
|
|
41602
|
+
const usesContextPack = slices.length > MAX_RETRIEVAL_SLICES;
|
|
41603
|
+
const packEntries = usesContextPack ? distillSlices(slices, {
|
|
41604
|
+
readFile: (path11) => {
|
|
41605
|
+
try {
|
|
41606
|
+
return readFileSync99(
|
|
41607
|
+
isAbsolute10(path11) ? path11 : join198(options.projectRoot, path11),
|
|
41608
|
+
"utf8"
|
|
41609
|
+
);
|
|
41610
|
+
} catch {
|
|
41611
|
+
return void 0;
|
|
41504
41612
|
}
|
|
41505
|
-
}
|
|
41506
|
-
) :
|
|
41613
|
+
}
|
|
41614
|
+
}) : [];
|
|
41615
|
+
const retrievalSection = usesContextPack ? composeContextPack(packEntries) : composeRetrievalSection(slices, { bestScore });
|
|
41507
41616
|
const memorySection = retrieves ? gatherCodebaseMemory(options.projectRoot) : "";
|
|
41508
41617
|
let driftSection = "";
|
|
41509
41618
|
if (retrieves) {
|
|
@@ -41516,6 +41625,31 @@ function createRagCommand() {
|
|
|
41516
41625
|
driftSection,
|
|
41517
41626
|
loadRules
|
|
41518
41627
|
});
|
|
41628
|
+
if (retrieves) {
|
|
41629
|
+
const sliceCount = usesContextPack ? 0 : slices.length;
|
|
41630
|
+
const pointerCount = usesContextPack ? packEntries.length : 0;
|
|
41631
|
+
const injectedSections = [];
|
|
41632
|
+
if (loadRules) injectedSections.push("rules");
|
|
41633
|
+
if (sliceCount > 0 || pointerCount > 0) injectedSections.push("retrieval");
|
|
41634
|
+
if (memorySection) injectedSections.push("memory");
|
|
41635
|
+
if (driftSection) injectedSections.push("drift");
|
|
41636
|
+
recordRagEvidence(
|
|
41637
|
+
options.projectRoot,
|
|
41638
|
+
"used",
|
|
41639
|
+
{
|
|
41640
|
+
injected: sliceCount > 0 || pointerCount > 0 || Boolean(memorySection) || Boolean(driftSection),
|
|
41641
|
+
injected_sections: injectedSections,
|
|
41642
|
+
slice_count: sliceCount,
|
|
41643
|
+
pointer_count: pointerCount,
|
|
41644
|
+
score_top: bestScore,
|
|
41645
|
+
bytes_injected: Buffer.byteLength(
|
|
41646
|
+
`${retrievalSection}${memorySection}${driftSection}`,
|
|
41647
|
+
"utf8"
|
|
41648
|
+
)
|
|
41649
|
+
},
|
|
41650
|
+
{ ragEnabled: true, adapter: "engine" }
|
|
41651
|
+
);
|
|
41652
|
+
}
|
|
41519
41653
|
if (!options.quiet) {
|
|
41520
41654
|
process.stdout.write(
|
|
41521
41655
|
`${target ? `wrote ${target}` : "nothing to compose"}; index sync: ${sync.synced ? "done" : sync.reason}; slices: ${slices.length}
|