flowseeker 0.1.7 → 0.1.8
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/.env.example +7 -0
- package/CHANGELOG.md +131 -108
- package/README.md +288 -221
- package/dist/cli/flowCommand.js +175 -0
- package/dist/cli/main.js +1794 -0
- package/dist/cli/mcpServer.js +7 -1
- package/dist/cli/runEvaluation.js +178 -2
- package/dist/config/defaultConfig.js +11 -1
- package/dist/config/env.js +118 -0
- package/dist/config/loadConfig.js +18 -1
- package/dist/config/loadConfigFromPath.js +3 -1
- package/dist/extension.js +23 -0
- package/dist/gateway/embeddingProviders.js +852 -0
- package/dist/index/cacheStore.js +43 -0
- package/dist/index/configRouteDiscoveryProbe.js +288 -0
- package/dist/index/embeddingIndex.js +193 -0
- package/dist/index/graphIndex.js +460 -0
- package/dist/index/indexWatcher.js +86 -0
- package/dist/index/structuredExtractor.js +303 -12
- package/dist/index/treeSitterExtractor.js +264 -0
- package/dist/index/vectorStore.js +41 -0
- package/dist/index/workspaceIndex.js +591 -26
- package/dist/mcp/mcpTools.js +51 -0
- package/dist/pipeline/contextPack.js +3 -3
- package/dist/pipeline/deterministicReranker.js +358 -0
- package/dist/pipeline/evaluationMetrics.js +14 -2
- package/dist/pipeline/fileGroups.js +7 -1
- package/dist/pipeline/fileScanner.js +93 -11
- package/dist/pipeline/llmReranker.js +151 -0
- package/dist/pipeline/nodeScan.js +91 -12
- package/dist/pipeline/ranker.js +875 -16
- package/dist/pipeline/retrievalFusion.js +41 -0
- package/dist/pipeline/runHeadless.js +35 -0
- package/dist/pipeline/solvePacket.js +549 -42
- package/dist/pipeline/subsystem.js +21 -0
- package/docs/demo-screenshot-checklist.md +86 -0
- package/docs/marketplace-copy.md +92 -0
- package/docs/mcp-onboarding.md +191 -0
- package/package.json +633 -561
|
@@ -90,10 +90,47 @@ function scanTextFile(input) {
|
|
|
90
90
|
const score = pathScore + kindBonus(input.relativePath, firstChunk?.text ?? "") + pathIntentModifier(input.relativePath, firstChunk?.text ?? "", input.profile);
|
|
91
91
|
units.push(toEvidenceUnit(input.file, input.relativePath, kind, detectRole(kind, score, input.profile, input.relativePath, firstChunk?.text ?? ""), firstChunk?.range, score, matchedTerms, retrievalPasses, imports, reasonsFor(input.relativePath, kind, matchedTerms, retrievalPasses, imports, pathScore, firstChunk?.text ?? ""), firstChunk?.text, fileMetadata));
|
|
92
92
|
}
|
|
93
|
+
if (units.length === 0 && input.channelHits?.some((hit) => hit.channel === "semantic_vector")) {
|
|
94
|
+
const semanticHit = input.channelHits.find((hit) => hit.channel === "semantic_vector");
|
|
95
|
+
const minSimilarity = input.config.index.semanticMinSimilarity ?? 0.5;
|
|
96
|
+
const maxRank = input.config.index.semanticEvidenceMaxRank ?? 20;
|
|
97
|
+
if (semanticHit.score >= minSimilarity && semanticHit.rank <= maxRank) {
|
|
98
|
+
const firstChunk = (0, chunker_1.chunkFile)(input.content, input.config.index.chunkSizeLines, input.config.index.chunkOverlapLines)[0];
|
|
99
|
+
const kind = detectKind(input.relativePath, firstChunk?.text ?? "");
|
|
100
|
+
const score = 10 + Math.max(0, semanticHit.score) * 20;
|
|
101
|
+
units.push(toEvidenceUnit(input.file, input.relativePath, kind, detectRole(kind, score, input.profile, input.relativePath, firstChunk?.text ?? ""), firstChunk?.range, score, [], ["semantic_vector"], imports, [`semantic-only file evidence: similarity=${semanticHit.score.toFixed(3)} rank=${semanticHit.rank}`], firstChunk?.text, {
|
|
102
|
+
...fileMetadata,
|
|
103
|
+
semanticTrace: {
|
|
104
|
+
channel: "semantic_vector",
|
|
105
|
+
similarity: semanticHit.score,
|
|
106
|
+
rank: semanticHit.rank,
|
|
107
|
+
reason: semanticHit.reason
|
|
108
|
+
}
|
|
109
|
+
}));
|
|
110
|
+
}
|
|
111
|
+
}
|
|
93
112
|
if (structuredNodes.length > 0 && language) {
|
|
94
113
|
units.push(...buildStructuredEvidence(input, structuredNodes, weightedTerms, imports, pathScore, fileMetadata, language));
|
|
95
114
|
}
|
|
96
|
-
|
|
115
|
+
const result = dedupeUnitsPerFile(units, 6);
|
|
116
|
+
return result.map((unit) => {
|
|
117
|
+
const hasSemanticChannel = input.channelHits?.some((hit) => hit.channel === "semantic_vector");
|
|
118
|
+
const updatedPasses = hasSemanticChannel && !unit.retrievalPasses.includes("semantic_vector")
|
|
119
|
+
? [...unit.retrievalPasses, "semantic_vector"]
|
|
120
|
+
: unit.retrievalPasses;
|
|
121
|
+
return {
|
|
122
|
+
...unit,
|
|
123
|
+
channelHits: input.channelHits,
|
|
124
|
+
retrievalTrace: input.retrievalTrace,
|
|
125
|
+
graphReasons: input.graphReasons,
|
|
126
|
+
retrievalPasses: updatedPasses,
|
|
127
|
+
reasons: [
|
|
128
|
+
...unit.reasons,
|
|
129
|
+
...(input.retrievalTrace ?? []).map((trace) => `retrieval channel: ${trace}`),
|
|
130
|
+
...(input.graphReasons ?? []).map((reason) => `graph expansion: ${reason}`)
|
|
131
|
+
]
|
|
132
|
+
};
|
|
133
|
+
});
|
|
97
134
|
}
|
|
98
135
|
function getWeightedTerms(profile) {
|
|
99
136
|
const byTerm = new Map();
|
|
@@ -401,6 +438,12 @@ function pathIntentModifier(relativePath, snippet, profile) {
|
|
|
401
438
|
if ((isBackendOnlyPath(normalizedPath) || isRuntimePath(normalizedPath)) && !isUiSurfacePath(normalizedPath) && focusedKeywordPathHits > 0) {
|
|
402
439
|
modifier += 40 + Math.min(24, focusedKeywordPathHits * 8);
|
|
403
440
|
}
|
|
441
|
+
if ((isFrameworkEntryPath(normalizedPath) || /(^|\/)(controllers?|handlers?)(\/|$)/.test(normalizedPath)) && focusedPathHits > 0) {
|
|
442
|
+
modifier += 38 + Math.min(22, focusedPathHits * 8);
|
|
443
|
+
}
|
|
444
|
+
if (/(^|\/)(jobs?|events?|listeners?|mail|notifications?|queues?|workers?)(\/|$)/.test(normalizedPath) && focusedPathHits > 0) {
|
|
445
|
+
modifier += 28 + Math.min(18, focusedPathHits * 6);
|
|
446
|
+
}
|
|
404
447
|
if (domainEntityTerms.length > 0 && entityPathHits === 0 && (isRuntimePath(normalizedPath) || isBackendOnlyPath(normalizedPath)) && !isFrameworkEntryPath(normalizedPath)) {
|
|
405
448
|
modifier -= 18;
|
|
406
449
|
}
|
|
@@ -414,41 +457,78 @@ function pathIntentModifier(relativePath, snippet, profile) {
|
|
|
414
457
|
modifier -= taskTargetsCopyOrI18n(profile) ? 0 : 24;
|
|
415
458
|
}
|
|
416
459
|
if (isUiSurfacePath(normalizedPath) && !taskTargetsUi(profile) && !isPrimaryFlowPath(normalizedPath, normalizedSnippet, profile)) {
|
|
417
|
-
modifier -=
|
|
460
|
+
modifier -= 22;
|
|
418
461
|
}
|
|
419
462
|
if (isUiSurfacePath(normalizedPath) && taskTargetsSecurity(profile) && !taskTargetsUi(profile)) {
|
|
420
|
-
modifier -=
|
|
463
|
+
modifier -= 46;
|
|
421
464
|
}
|
|
422
465
|
if (taskTargetsConfig(profile) && !taskTargetsSecurity(profile) && isPermissionPath(normalizedPath) && focusedPathHits === 0) {
|
|
423
466
|
modifier -= 20;
|
|
424
467
|
}
|
|
425
468
|
if (taskTargetsUi(profile) && !taskTargetsBackend(profile) && isBackendOnlyPath(normalizedPath) && focusedPathHits === 0) {
|
|
426
|
-
modifier -=
|
|
469
|
+
modifier -= 46;
|
|
427
470
|
}
|
|
428
471
|
if (!taskTargetsData(profile) && isGenericDataPath(normalizedPath) && focusedPathHits === 0) {
|
|
429
472
|
modifier -= taskTargetsUi(profile) ? 26 : 10;
|
|
430
473
|
}
|
|
431
474
|
if (isGenericUtilityPath(normalizedPath) && !taskTargetsUtility(profile)) {
|
|
432
|
-
modifier -= taskTargetsUi(profile) ?
|
|
475
|
+
modifier -= taskTargetsUi(profile) ? 36 : focusedPathHits === 0 ? 42 : 28;
|
|
433
476
|
}
|
|
434
477
|
if (isMaintenanceCommandPath(normalizedPath) && !taskTargetsMaintenanceCommand(profile)) {
|
|
435
|
-
modifier -=
|
|
478
|
+
modifier -= 64;
|
|
436
479
|
}
|
|
437
480
|
if (isGuidanceOrReadinessPath(normalizedPath)) {
|
|
438
|
-
modifier -=
|
|
481
|
+
modifier -= 28;
|
|
439
482
|
}
|
|
440
483
|
if (isStaticContentPath(normalizedPath)) {
|
|
441
|
-
modifier -=
|
|
484
|
+
modifier -= 38;
|
|
442
485
|
}
|
|
443
486
|
if (isAdminSurfacePath(normalizedPath) && !taskTargetsAdmin(profile) && !isRuntimeFlowPath(normalizedPath, normalizedSnippet)) {
|
|
444
|
-
modifier -=
|
|
487
|
+
modifier -= 22;
|
|
445
488
|
}
|
|
446
489
|
if (/\b(smoke|fixture|mock|seed)\b/.test(normalizedPath)) {
|
|
447
|
-
modifier -=
|
|
490
|
+
modifier -= 18;
|
|
448
491
|
}
|
|
449
492
|
if (/(^|\/)\.flowseeker(\/|$)|(^|\/)_gen[^/]*\.(py|sh)$|(^|\/)_bulk_write\.js$/.test(normalizedPath)) {
|
|
450
493
|
modifier -= 80;
|
|
451
494
|
}
|
|
495
|
+
// Framework-specific path hints — moderate boosts for common patterns in weak workspaces
|
|
496
|
+
if (focusedPathHits > 0 || keywordPathHits > 0) {
|
|
497
|
+
// Go: handler/service in internal/, cmd/server entry, router, domain services
|
|
498
|
+
if (/(^|\/)internal\/handler\b/.test(normalizedPath))
|
|
499
|
+
modifier += 12;
|
|
500
|
+
if (/(^|\/)internal\/service\b/.test(normalizedPath))
|
|
501
|
+
modifier += 10;
|
|
502
|
+
if (/(^|\/)cmd\/server\b/.test(normalizedPath))
|
|
503
|
+
modifier += 10;
|
|
504
|
+
if (/(^|\/)internal\/router\b/.test(normalizedPath))
|
|
505
|
+
modifier += 8;
|
|
506
|
+
if (/(^|\/)internal\/domain\b/.test(normalizedPath))
|
|
507
|
+
modifier += 8;
|
|
508
|
+
// Spring Boot: controller/service/repository with java/kotlin, annotated entry points
|
|
509
|
+
if (/\.java$|\.kt$/.test(relativePath.toLowerCase()) && /(^|\/)(controller|service|repository)(\/|$)/.test(normalizedPath))
|
|
510
|
+
modifier += 12;
|
|
511
|
+
if (/\.java$/.test(relativePath.toLowerCase()) && /\b@(RestController|Controller|Service|Repository)\b/i.test(snippet))
|
|
512
|
+
modifier += 6;
|
|
513
|
+
// Rails: controller, model, service, mailer, job, policy
|
|
514
|
+
if (/\.rb$/.test(relativePath.toLowerCase()) && /(^|\/)app\/(controllers|models|services|mailers|jobs|policies)(\/|$)/.test(normalizedPath))
|
|
515
|
+
modifier += 12;
|
|
516
|
+
if (/\.rb$/.test(relativePath.toLowerCase()) && /(^|\/)config\/routes\.rb$/.test(normalizedPath))
|
|
517
|
+
modifier += 10;
|
|
518
|
+
// ASP.NET Core: Controllers/, Services/, Program.cs, Startup
|
|
519
|
+
if (/\.cs$/.test(relativePath.toLowerCase()) && /(^|\/)(Controllers|Services|Repositories)(\/|$)/.test(normalizedPath))
|
|
520
|
+
modifier += 12;
|
|
521
|
+
if (/\.cs$/.test(relativePath.toLowerCase()) && /\b(Program|Startup)\.cs$/.test(normalizedPath))
|
|
522
|
+
modifier += 8;
|
|
523
|
+
// Flutter/Dart: screen, provider, bloc, service directories
|
|
524
|
+
if (/\.dart$/.test(relativePath.toLowerCase()) && /(^|\/)(screens?|providers?|blocs?|services?)(\/|$)/.test(normalizedPath))
|
|
525
|
+
modifier += 12;
|
|
526
|
+
// SvelteKit: route files, lib/server, lib/components
|
|
527
|
+
if (/\.svelte$|\.ts$/.test(relativePath.toLowerCase()) && /(^|\/)\+(page|layout|server)\./.test(normalizedPath))
|
|
528
|
+
modifier += 12;
|
|
529
|
+
if (/\.ts$/.test(relativePath.toLowerCase()) && /(^|\/)lib\/server\//.test(normalizedPath))
|
|
530
|
+
modifier += 10;
|
|
531
|
+
}
|
|
452
532
|
return modifier;
|
|
453
533
|
}
|
|
454
534
|
function reasonsFor(relativePath, kind, matchedTerms, retrievalPasses, imports, pathScore, snippet) {
|
|
@@ -751,6 +831,7 @@ function buildStructuredEvidence(input, nodes, weightedTerms, imports, pathScore
|
|
|
751
831
|
const role = detectRole(nodeKind, nodeScore, input.profile, input.relativePath, snippet);
|
|
752
832
|
units.push(toEvidenceUnit(input.file, input.relativePath, nodeKind, role, node.range, nodeScore, matchedTerms, retrievalPasses, imports, reasonsFor(input.relativePath, nodeKind, matchedTerms, retrievalPasses, imports, pathScore, snippet), snippet, {
|
|
753
833
|
...fileMetadata,
|
|
834
|
+
extractorKind: node.extractorKind ?? "structured-regex",
|
|
754
835
|
symbolMatches: collectSymbolMatches([node.name ?? "", ...(fileMetadata.symbols ?? [])], weightedTerms)
|
|
755
836
|
}));
|
|
756
837
|
}
|
|
@@ -788,7 +869,8 @@ function buildEvidenceMetadata(relativePath, nodes, weightedTerms) {
|
|
|
788
869
|
symbols,
|
|
789
870
|
symbolMatches: collectSymbolMatches(symbols, weightedTerms),
|
|
790
871
|
subsystem: subsystemTerms[0],
|
|
791
|
-
subsystemTerms
|
|
872
|
+
subsystemTerms,
|
|
873
|
+
extractorKind: "fallback-chunk"
|
|
792
874
|
};
|
|
793
875
|
}
|
|
794
876
|
function collectSymbolMatches(symbols, weightedTerms) {
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Phase 05 Sprint 5G-B — Disabled Optional LLM Reranker Module
|
|
3
|
+
// Execution mode: disabled_prototype_report_only
|
|
4
|
+
// No provider/client/API calls. No network. No production imports.
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.buildLlmRerankRequest = buildLlmRerankRequest;
|
|
7
|
+
exports.validateLlmRerankResponse = validateLlmRerankResponse;
|
|
8
|
+
exports.applyLlmRerankOrFallback = applyLlmRerankOrFallback;
|
|
9
|
+
exports.redactLlmRerankCandidate = redactLlmRerankCandidate;
|
|
10
|
+
exports.createDeterministicFallback = createDeterministicFallback;
|
|
11
|
+
const DEFAULT_SYSTEM_PROMPT = "You are a code search result reranker. Given a task description and a list of candidate files with their retrieval metadata, reorder the candidates so the most relevant files appear first. Output a JSON array of {relativePath, newRank, reason}. Do not add files not in the input list. Do not change relativePath values. Do not include file contents or secrets.";
|
|
12
|
+
const FORBIDDEN_FIELDS = new Set([
|
|
13
|
+
"expectedPath", "expectedPaths", "avoidPath", "avoidPaths",
|
|
14
|
+
"caseId", "caseIds", "suiteName", "suiteLabels",
|
|
15
|
+
"benchmark", "benchmarkLabel", "benchmarkLabels",
|
|
16
|
+
"oracle", "postHoc", "fixture", "isAvoid", "isExpected",
|
|
17
|
+
"contrastContext", "fullFileContents",
|
|
18
|
+
]);
|
|
19
|
+
// --- 1. buildLlmRerankRequest ---
|
|
20
|
+
function buildLlmRerankRequest(candidates, model) {
|
|
21
|
+
const redacted = candidates.map((c) => redactLlmRerankCandidate(c));
|
|
22
|
+
const userContent = JSON.stringify({
|
|
23
|
+
taskDescription: candidates[0]?.taskDescription || "",
|
|
24
|
+
candidates: redacted,
|
|
25
|
+
});
|
|
26
|
+
return {
|
|
27
|
+
model: model || "default",
|
|
28
|
+
messages: [
|
|
29
|
+
{ role: "system", content: DEFAULT_SYSTEM_PROMPT },
|
|
30
|
+
{ role: "user", content: userContent },
|
|
31
|
+
],
|
|
32
|
+
temperature: 0,
|
|
33
|
+
max_tokens: 1024,
|
|
34
|
+
response_format: { type: "json_object" },
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
// --- 2. validateLlmRerankResponse ---
|
|
38
|
+
function validateLlmRerankResponse(input, response) {
|
|
39
|
+
const errors = [];
|
|
40
|
+
if (!response || !Array.isArray(response.reranked)) {
|
|
41
|
+
errors.push("R1: reranked must be a non-empty array");
|
|
42
|
+
return { valid: false, errors };
|
|
43
|
+
}
|
|
44
|
+
if (response.reranked.length === 0) {
|
|
45
|
+
errors.push("R7: empty reranked array");
|
|
46
|
+
return { valid: false, errors };
|
|
47
|
+
}
|
|
48
|
+
// Build normalized input path set
|
|
49
|
+
const inputPaths = new Set();
|
|
50
|
+
for (const c of input) {
|
|
51
|
+
inputPaths.add((c.relativePath || "").replace(/\\/g, "/").toLowerCase());
|
|
52
|
+
}
|
|
53
|
+
const seenRanks = new Set();
|
|
54
|
+
const responsePaths = new Set();
|
|
55
|
+
for (const r of response.reranked) {
|
|
56
|
+
const np = (r.relativePath || "").replace(/\\/g, "/").toLowerCase();
|
|
57
|
+
// R3: no new paths
|
|
58
|
+
if (!inputPaths.has(np)) {
|
|
59
|
+
errors.push("R3: unknown file in response: " + r.relativePath);
|
|
60
|
+
}
|
|
61
|
+
// R4: unique ranks in [1, N]
|
|
62
|
+
if (!Number.isInteger(r.newRank) || r.newRank < 1 || r.newRank > input.length) {
|
|
63
|
+
errors.push("R4: invalid newRank " + r.newRank + " for " + r.relativePath);
|
|
64
|
+
}
|
|
65
|
+
if (seenRanks.has(r.newRank)) {
|
|
66
|
+
errors.push("R4: duplicate newRank " + r.newRank);
|
|
67
|
+
}
|
|
68
|
+
seenRanks.add(r.newRank);
|
|
69
|
+
responsePaths.add(np);
|
|
70
|
+
// R5: reason present, non-empty, <= 200 chars
|
|
71
|
+
if (typeof r.reason !== "string" || r.reason.length === 0) {
|
|
72
|
+
errors.push("R5: reason missing for " + r.relativePath);
|
|
73
|
+
}
|
|
74
|
+
if (r.reason && r.reason.length > 200) {
|
|
75
|
+
errors.push("R5: reason too long for " + r.relativePath);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
// R2: all input paths must be in response
|
|
79
|
+
if (responsePaths.size !== inputPaths.size) {
|
|
80
|
+
errors.push("R2/R8: candidate count mismatch (input=" + inputPaths.size + " response=" + responsePaths.size + ")");
|
|
81
|
+
}
|
|
82
|
+
return { valid: errors.length === 0, errors };
|
|
83
|
+
}
|
|
84
|
+
// --- 3. applyLlmRerankOrFallback ---
|
|
85
|
+
function applyLlmRerankOrFallback(input, response, fallbackReason) {
|
|
86
|
+
if (!response) {
|
|
87
|
+
return createDeterministicFallback(input, fallbackReason || "no_response");
|
|
88
|
+
}
|
|
89
|
+
// Try to parse if response is string
|
|
90
|
+
let parsed = response;
|
|
91
|
+
// Validate
|
|
92
|
+
const validation = validateLlmRerankResponse(input, parsed);
|
|
93
|
+
if (!validation.valid) {
|
|
94
|
+
return createDeterministicFallback(input, (fallbackReason || "validation_failed") + ": " + validation.errors.join("; "));
|
|
95
|
+
}
|
|
96
|
+
// Reorder candidates by newRank
|
|
97
|
+
const rankMap = new Map();
|
|
98
|
+
for (const r of parsed.reranked) {
|
|
99
|
+
rankMap.set(r.newRank, r.relativePath);
|
|
100
|
+
}
|
|
101
|
+
const pathToCandidate = new Map();
|
|
102
|
+
for (const c of input) {
|
|
103
|
+
const np = (c.relativePath || "").replace(/\\/g, "/").toLowerCase();
|
|
104
|
+
pathToCandidate.set(np, c);
|
|
105
|
+
}
|
|
106
|
+
const reordered = [];
|
|
107
|
+
for (let rank = 1; rank <= input.length; rank++) {
|
|
108
|
+
const rp = rankMap.get(rank);
|
|
109
|
+
if (rp) {
|
|
110
|
+
const np = rp.replace(/\\/g, "/").toLowerCase();
|
|
111
|
+
const candidate = pathToCandidate.get(np);
|
|
112
|
+
if (candidate)
|
|
113
|
+
reordered.push(candidate);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
// Append any remaining not assigned a rank
|
|
117
|
+
for (const c of input) {
|
|
118
|
+
const np = (c.relativePath || "").replace(/\\/g, "/").toLowerCase();
|
|
119
|
+
if (!reordered.some((r) => (r.relativePath || "").replace(/\\/g, "/").toLowerCase() === np)) {
|
|
120
|
+
reordered.push(c);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return {
|
|
124
|
+
candidates: reordered,
|
|
125
|
+
fallbackTriggered: false,
|
|
126
|
+
fallbackReason: null,
|
|
127
|
+
validationPassed: true,
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
// --- 4. redactLlmRerankCandidate ---
|
|
131
|
+
function redactLlmRerankCandidate(candidate) {
|
|
132
|
+
const redacted = {};
|
|
133
|
+
for (const key of Object.keys(candidate)) {
|
|
134
|
+
if (!FORBIDDEN_FIELDS.has(key)) {
|
|
135
|
+
redacted[key] = candidate[key];
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return redacted;
|
|
139
|
+
}
|
|
140
|
+
// --- 5. createDeterministicFallback ---
|
|
141
|
+
function createDeterministicFallback(input, reason) {
|
|
142
|
+
// Sort by originalRank ascending (preserves deterministic ordering)
|
|
143
|
+
const sorted = [...input].sort((a, b) => a.originalRank - b.originalRank);
|
|
144
|
+
return {
|
|
145
|
+
candidates: sorted,
|
|
146
|
+
fallbackTriggered: true,
|
|
147
|
+
fallbackReason: reason,
|
|
148
|
+
validationPassed: false,
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
//# sourceMappingURL=llmReranker.js.map
|
|
@@ -119,7 +119,26 @@ async function nodeScanWorkspace(workspacePath, profile, config, options = {}) {
|
|
|
119
119
|
indexedTokens: discovery.indexedTokens,
|
|
120
120
|
candidateFiles: discovery.candidateFiles,
|
|
121
121
|
indexReusedFiles: discovery.indexReusedFiles,
|
|
122
|
-
indexUpdatedFiles: discovery.indexUpdatedFiles
|
|
122
|
+
indexUpdatedFiles: discovery.indexUpdatedFiles,
|
|
123
|
+
indexDeletedFiles: discovery.indexDeletedFiles,
|
|
124
|
+
indexDurationMs: discovery.indexDurationMs,
|
|
125
|
+
indexStatus: discovery.indexStatus,
|
|
126
|
+
partialReason: discovery.partialReason,
|
|
127
|
+
semanticRetrievalStatus: discovery.semanticRetrievalStatus,
|
|
128
|
+
semanticProvider: config.index.semanticProvider || "none",
|
|
129
|
+
semanticModel: config.index.semanticModel || "(empty)",
|
|
130
|
+
semanticErrorType: discovery.semanticErrorType,
|
|
131
|
+
semanticErrorMessage: discovery.semanticErrorMessage,
|
|
132
|
+
semanticEmbeddedFiles: discovery.semanticEmbeddedFiles ?? 0,
|
|
133
|
+
semanticCacheHits: discovery.semanticCacheHits ?? 0,
|
|
134
|
+
semanticCacheMisses: discovery.semanticCacheMisses ?? 0,
|
|
135
|
+
semanticHitCount: discovery.semanticHitCount ?? 0,
|
|
136
|
+
semanticAddedFileCount: discovery.semanticAddedFileCount ?? 0,
|
|
137
|
+
semanticSupportedFileCount: discovery.semanticSupportedFileCount ?? 0,
|
|
138
|
+
semanticFallbackUsed: discovery.semanticRetrievalStatus === "error_fallback",
|
|
139
|
+
deepCandidateDiscoveryCandidates: discovery.deepCandidateDiscoveryCandidates ?? 0,
|
|
140
|
+
deepCandidateDiscoveryAdded: discovery.deepCandidateDiscoveryAdded ?? 0,
|
|
141
|
+
deepCandidateDiscoveryAvoidBlocked: discovery.deepCandidateDiscoveryAvoidBlocked ?? 0
|
|
123
142
|
}
|
|
124
143
|
};
|
|
125
144
|
}
|
|
@@ -136,12 +155,22 @@ async function discoverScanFiles(rootPath, profile, config, startedAt, options)
|
|
|
136
155
|
if (!config.index.enableWorkspaceIndex) {
|
|
137
156
|
return (0, fileDiscovery_1.discoverWorkspaceFiles)(rootPath, config, { startedAt, shouldCancel: options.shouldCancel });
|
|
138
157
|
}
|
|
158
|
+
const idxStart = Date.now();
|
|
139
159
|
const indexed = await (0, workspaceIndex_1.loadOrBuildWorkspaceIndex)(rootPath, config, {
|
|
140
160
|
startedAt,
|
|
141
161
|
shouldCancel: options.shouldCancel,
|
|
142
|
-
onProgress: options.onProgress
|
|
162
|
+
onProgress: options.onProgress,
|
|
163
|
+
readOnly: options.readOnly
|
|
143
164
|
});
|
|
144
|
-
const
|
|
165
|
+
const indexDurationMs = Date.now() - idxStart;
|
|
166
|
+
const selection = await (0, workspaceIndex_1.selectIndexCandidates)(indexed.index, profile, config);
|
|
167
|
+
const candidates = selection.files;
|
|
168
|
+
const semanticRetrievalStatus = selection.semanticStatus;
|
|
169
|
+
const semanticErrorType = selection.semanticErrorType;
|
|
170
|
+
const semanticErrorMessage = selection.semanticErrorMessage;
|
|
171
|
+
const deepCands = selection.deepCandidateDiscoveryCandidates ?? 0;
|
|
172
|
+
const deepAdded = selection.deepCandidateDiscoveryAdded ?? 0;
|
|
173
|
+
const deepBlocked = selection.deepCandidateDiscoveryAvoidBlocked ?? 0;
|
|
145
174
|
const files = candidates.length > 0
|
|
146
175
|
? candidates
|
|
147
176
|
: fallbackIndexCandidates(rootPath, indexed.index.files, config.index.maxIndexedCandidateFiles);
|
|
@@ -154,7 +183,23 @@ async function discoverScanFiles(rootPath, profile, config, startedAt, options)
|
|
|
154
183
|
indexedTokens: indexed.indexedTokens,
|
|
155
184
|
candidateFiles: files.length,
|
|
156
185
|
indexReusedFiles: indexed.reusedFiles,
|
|
157
|
-
indexUpdatedFiles: indexed.updatedFiles
|
|
186
|
+
indexUpdatedFiles: indexed.updatedFiles,
|
|
187
|
+
indexDeletedFiles: indexed.deletedFiles,
|
|
188
|
+
indexStatus: indexed.stopReason ? "partial" : "fresh",
|
|
189
|
+
indexDurationMs,
|
|
190
|
+
partialReason: indexed.stopReason,
|
|
191
|
+
semanticRetrievalStatus,
|
|
192
|
+
semanticErrorType,
|
|
193
|
+
semanticErrorMessage,
|
|
194
|
+
semanticEmbeddedFiles: selection.semanticEmbeddedFiles ?? 0,
|
|
195
|
+
semanticCacheHits: selection.semanticCacheHits ?? 0,
|
|
196
|
+
semanticCacheMisses: selection.semanticCacheMisses ?? 0,
|
|
197
|
+
semanticHitCount: selection.semanticHitCount ?? 0,
|
|
198
|
+
semanticAddedFileCount: selection.semanticAddedFileCount ?? 0,
|
|
199
|
+
semanticSupportedFileCount: selection.semanticSupportedFileCount ?? 0,
|
|
200
|
+
deepCandidateDiscoveryCandidates: deepCands,
|
|
201
|
+
deepCandidateDiscoveryAdded: deepAdded,
|
|
202
|
+
deepCandidateDiscoveryAvoidBlocked: deepBlocked
|
|
158
203
|
};
|
|
159
204
|
}
|
|
160
205
|
function fallbackIndexCandidates(rootPath, files, maxFiles) {
|
|
@@ -202,15 +247,49 @@ async function scanNodeFile(file, profile, config) {
|
|
|
202
247
|
if (content.includes("\u0000")) {
|
|
203
248
|
return { units: [], scanned: false, size: stat.size, tokenCount: 0 };
|
|
204
249
|
}
|
|
250
|
+
var units = (0, fileScanner_1.scanTextFile)({
|
|
251
|
+
file: file.absolutePath,
|
|
252
|
+
relativePath: file.relativePath,
|
|
253
|
+
content,
|
|
254
|
+
size: stat.size,
|
|
255
|
+
profile,
|
|
256
|
+
config,
|
|
257
|
+
channelHits: file.channelHits,
|
|
258
|
+
retrievalTrace: file.retrievalTrace,
|
|
259
|
+
graphReasons: file.graphReasons
|
|
260
|
+
});
|
|
261
|
+
// Fallback: if a selected file has graphReasons but produced no units, create
|
|
262
|
+
// one minimal unit so the semantic evidence (discovered by semantic) is visible.
|
|
263
|
+
if (units.length === 0 && file.graphReasons && file.graphReasons.length > 0) {
|
|
264
|
+
units = [{
|
|
265
|
+
id: file.relativePath,
|
|
266
|
+
file: file.absolutePath,
|
|
267
|
+
relativePath: file.relativePath,
|
|
268
|
+
absolutePath: file.absolutePath,
|
|
269
|
+
size: stat.size,
|
|
270
|
+
kind: "file",
|
|
271
|
+
role: "read_context",
|
|
272
|
+
score: 0,
|
|
273
|
+
confidence: 0,
|
|
274
|
+
tier: "low",
|
|
275
|
+
reasons: [],
|
|
276
|
+
matchedTerms: [],
|
|
277
|
+
retrievalPasses: file.retrievalTrace ?? [],
|
|
278
|
+
imports: [],
|
|
279
|
+
channelHits: file.channelHits,
|
|
280
|
+
graphReasons: file.graphReasons,
|
|
281
|
+
lines: "0-0",
|
|
282
|
+
context: "",
|
|
283
|
+
contrastContext: false,
|
|
284
|
+
symbols: [],
|
|
285
|
+
symbolMatches: [],
|
|
286
|
+
subsystem: undefined,
|
|
287
|
+
subsystemTerms: [],
|
|
288
|
+
extractorKind: undefined
|
|
289
|
+
}];
|
|
290
|
+
}
|
|
205
291
|
return {
|
|
206
|
-
units
|
|
207
|
-
file: file.absolutePath,
|
|
208
|
-
relativePath: file.relativePath,
|
|
209
|
-
content,
|
|
210
|
-
size: stat.size,
|
|
211
|
-
profile,
|
|
212
|
-
config
|
|
213
|
-
}),
|
|
292
|
+
units,
|
|
214
293
|
scanned: true,
|
|
215
294
|
size: stat.size,
|
|
216
295
|
tokenCount: (0, text_1.fastTokenEstimate)(`${file.relativePath}\n${content}`)
|