@devflow-tools/context-engine 0.16.27 → 0.16.29
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/dist/context-engine.d.ts +3 -2
- package/dist/context-engine.d.ts.map +1 -1
- package/dist/context-engine.js +137 -13
- package/dist/context-engine.js.map +1 -1
- package/dist/context-file-roles.d.ts.map +1 -1
- package/dist/context-file-roles.js +16 -4
- package/dist/context-file-roles.js.map +1 -1
- package/dist/create-engines.d.ts.map +1 -1
- package/dist/create-engines.js +10 -8
- package/dist/create-engines.js.map +1 -1
- package/dist/graph-adapter.d.ts.map +1 -1
- package/dist/graph-adapter.js +21 -10
- package/dist/graph-adapter.js.map +1 -1
- package/dist/graph-edge-quality.d.ts +7 -0
- package/dist/graph-edge-quality.d.ts.map +1 -1
- package/dist/graph-edge-quality.js +52 -1
- package/dist/graph-edge-quality.js.map +1 -1
- package/dist/indexer-adapter.d.ts +23 -0
- package/dist/indexer-adapter.d.ts.map +1 -1
- package/dist/indexer-adapter.js +94 -0
- package/dist/indexer-adapter.js.map +1 -1
- package/dist/query-cache.d.ts +2 -0
- package/dist/query-cache.d.ts.map +1 -1
- package/dist/query-cache.js +27 -1
- package/dist/query-cache.js.map +1 -1
- package/dist/retrieval-handlers.d.ts +5 -11
- package/dist/retrieval-handlers.d.ts.map +1 -1
- package/dist/retrieval-handlers.js +11 -1
- package/dist/retrieval-handlers.js.map +1 -1
- package/dist/retrieval-ledger.d.ts +3 -0
- package/dist/retrieval-ledger.d.ts.map +1 -0
- package/dist/retrieval-ledger.js +91 -0
- package/dist/retrieval-ledger.js.map +1 -0
- package/dist/retrieval-session-controller.js +23 -2
- package/dist/retrieval-session-controller.js.map +1 -1
- package/dist/retrieval-task-plan.d.ts +4 -7
- package/dist/retrieval-task-plan.d.ts.map +1 -1
- package/dist/retrieval-task-plan.js +98 -6
- package/dist/retrieval-task-plan.js.map +1 -1
- package/dist/task-retrieval-orchestrator.d.ts +9 -0
- package/dist/task-retrieval-orchestrator.d.ts.map +1 -1
- package/dist/task-retrieval-orchestrator.js +180 -44
- package/dist/task-retrieval-orchestrator.js.map +1 -1
- package/package.json +8 -8
package/dist/context-engine.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ContextRequest, ContextResult, IndexStatus, IDataProvider } from "@devflow-tools/sdk";
|
|
1
|
+
import type { ContextRequest, ContextResult, IndexStatus, IDataProvider, SelectedMemoryEvidence } from "@devflow-tools/sdk";
|
|
2
2
|
import type { EmbeddingProvider } from "@devflow-tools/memory-engine";
|
|
3
3
|
import type { IMemoryEngine } from "@devflow-tools/sdk";
|
|
4
4
|
import type { KnowledgeEngine } from "@devflow-tools/knowledge-engine";
|
|
@@ -33,11 +33,12 @@ export declare class ContextEngine {
|
|
|
33
33
|
close(): void;
|
|
34
34
|
private queryCache;
|
|
35
35
|
recordPrefetchedContextSelection(input: {
|
|
36
|
-
|
|
36
|
+
entries: SelectedMemoryEvidence[];
|
|
37
37
|
sessionId?: string;
|
|
38
38
|
executionId?: string;
|
|
39
39
|
requestId?: string;
|
|
40
40
|
query: string;
|
|
41
|
+
contextReceipt: string;
|
|
41
42
|
}): Promise<void>;
|
|
42
43
|
index(_projectRoot: string): Promise<void>;
|
|
43
44
|
private getKnowledgeSearchOptions;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context-engine.d.ts","sourceRoot":"","sources":["../src/context-engine.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,cAAc,EACd,aAAa,EAEb,WAAW,EACX,aAAa,
|
|
1
|
+
{"version":3,"file":"context-engine.d.ts","sourceRoot":"","sources":["../src/context-engine.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,cAAc,EACd,aAAa,EAEb,WAAW,EACX,aAAa,EAOb,sBAAsB,EACvB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AACtE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,KAAK,EAAE,eAAe,EAA0B,MAAM,iCAAiC,CAAC;AAC/F,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,KAAK,EAAwB,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACjF,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AAQzD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAClE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAU3D,OAAO,KAAK,EAKV,kBAAkB,EAClB,+BAA+B,EAChC,MAAM,YAAY,CAAC;AAuSpB,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,SAAS,CAAC;IACd,IAAI,EAAE,aAAa,CAAC;IACpB,KAAK,EAAE,YAAY,CAAC;IACpB,OAAO,EAAE,cAAc,CAAC;IACxB,MAAM,EAAE,aAAa,CAAC;IACtB,SAAS,EAAE,eAAe,CAAC;IAC3B,QAAQ,CAAC,EAAE,iBAAiB,CAAC;IAC7B,WAAW,CAAC,EAAE,iBAAiB,CAAC;IAChC,cAAc,CAAC,EAAE,cAAc,CAAC;CACjC;AAoCD,qBAAa,aAAa;IACxB,OAAO,CAAC,EAAE,CAAY;IACtB,OAAO,CAAC,IAAI,CAAgB;IAC5B,OAAO,CAAC,KAAK,CAAe;IAC5B,OAAO,CAAC,OAAO,CAAiB;IAChC,OAAO,CAAC,MAAM,CAAgB;IAC9B,OAAO,CAAC,SAAS,CAAkB;IACnC,OAAO,CAAC,QAAQ,CAAC,CAAoB;IACrC,OAAO,CAAC,WAAW,CAAC,CAAoB;IACxC,OAAO,CAAC,cAAc,CAAC,CAAiB;gBAE5B,IAAI,EAAE,iBAAiB;IAYnC,KAAK,IAAI,IAAI;IAKb,OAAO,CAAC,UAAU,CAAkD;IAE9D,gCAAgC,CAAC,KAAK,EAAE;QAC5C,OAAO,EAAE,sBAAsB,EAAE,CAAC;QAClC,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,KAAK,EAAE,MAAM,CAAC;QACd,cAAc,EAAE,MAAM,CAAC;KACxB,GAAG,OAAO,CAAC,IAAI,CAAC;IAIX,KAAK,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIhD,OAAO,CAAC,yBAAyB;YA4BnB,yBAAyB;IAiCvC,OAAO,CAAC,eAAe;IAKjB,KAAK,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC;IAmRtD,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,kBAAkB,GAAG,OAAO,CAAC,+BAA+B,CAAC;YA4iB/F,sBAAsB;IAmWpC,OAAO,CAAC,yBAAyB;YAgBnB,2BAA2B;YA4B3B,6BAA6B;IAkB3C,OAAO,CAAC,mBAAmB;IAY3B,OAAO,CAAC,sBAAsB;IAM9B,OAAO,CAAC,2BAA2B;IAOnC,SAAS,IAAI,WAAW;CASzB"}
|
package/dist/context-engine.js
CHANGED
|
@@ -4,12 +4,13 @@ import { buildRetrievalTaskPlan } from './retrieval-task-plan.js';
|
|
|
4
4
|
import { fuzzySearch, shouldFallback } from "./fuzzy-fallback.js";
|
|
5
5
|
import { compressToTokenLimit } from "./compressor.js";
|
|
6
6
|
import { claudeRerank } from "./claude-rerank.js";
|
|
7
|
-
import { QueryCache } from "./query-cache.js";
|
|
7
|
+
import { invalidateContextPrefetchCache, QueryCache } from "./query-cache.js";
|
|
8
8
|
import { rankOptionalContextFiles, } from "./ranker.js";
|
|
9
9
|
import { describeLayerEvidence, detectLanguageFromPath, } from "./layer-rules.js";
|
|
10
10
|
import { reachableCodeGraphNodeIds, sanitizeCodeGraphEdges, } from './graph-edge-quality.js';
|
|
11
11
|
import { assembleContextFileRoles } from './context-file-roles.js';
|
|
12
12
|
import { buildTaskContextEnvelope } from './task-retrieval-orchestrator.js';
|
|
13
|
+
import { recordTaskEnvelopeLedger } from './retrieval-ledger.js';
|
|
13
14
|
const CG = CGModule.default ?? CGModule;
|
|
14
15
|
const SYMBOL_VECTOR_WARNING = "Symbol vector search is not ready; using CodeGraph/BM25 results. Run `devflow doctor` for details.";
|
|
15
16
|
// ── RRF (Reciprocal Rank Fusion) ─────────────────────────────────
|
|
@@ -242,8 +243,21 @@ function extractTargetAnchors(query) {
|
|
|
242
243
|
if (match[1])
|
|
243
244
|
pathAnchors.add(match[1].replace(/^\.\//, '').replace(/\/+$/, '').toLowerCase());
|
|
244
245
|
}
|
|
245
|
-
if (pathAnchors.size > 0)
|
|
246
|
+
if (pathAnchors.size > 0) {
|
|
247
|
+
const directories = [...pathAnchors]
|
|
248
|
+
.filter(anchor => /\.[a-z0-9]+$/iu.test(anchor))
|
|
249
|
+
.map(anchor => anchor.split('/').slice(0, -1).join('/'))
|
|
250
|
+
.filter(Boolean);
|
|
251
|
+
const existingNames = new Set([...pathAnchors].map(anchor => anchor.split('/').at(-1)));
|
|
252
|
+
for (const match of query.matchAll(/\b([A-Za-z0-9_.-]+\.(?:[cm]?[jt]sx?|vue|svelte|json|ya?ml|md))\b/gu)) {
|
|
253
|
+
const fileName = match[1]?.toLowerCase();
|
|
254
|
+
if (!fileName || existingNames.has(fileName))
|
|
255
|
+
continue;
|
|
256
|
+
if (directories.length === 1)
|
|
257
|
+
pathAnchors.add(`${directories[0]}/${fileName}`);
|
|
258
|
+
}
|
|
246
259
|
return [...pathAnchors];
|
|
260
|
+
}
|
|
247
261
|
const anchors = new Set();
|
|
248
262
|
for (const match of query.matchAll(/([a-zA-Z0-9]+(?:-[a-zA-Z0-9]+)+)\s+(?:component|module|file|组件|模块|文件)/gi)) {
|
|
249
263
|
if (match[1])
|
|
@@ -290,7 +304,7 @@ export class ContextEngine {
|
|
|
290
304
|
async index(_projectRoot) {
|
|
291
305
|
await this.indexer.reconcile();
|
|
292
306
|
}
|
|
293
|
-
getKnowledgeSearchOptions(query, limit, options = {}) {
|
|
307
|
+
getKnowledgeSearchOptions(query, limit, options = {}, taskPlan, totalBudget = 8_000) {
|
|
294
308
|
const stats = this.cg.getStats();
|
|
295
309
|
const frameworks = typeof this.cg.getDetectedFrameworks === "function"
|
|
296
310
|
? this.cg.getDetectedFrameworks()
|
|
@@ -298,8 +312,11 @@ export class ContextEngine {
|
|
|
298
312
|
const languages = Object.keys(stats.filesByLanguage ?? {});
|
|
299
313
|
return {
|
|
300
314
|
limit,
|
|
315
|
+
budgetTokens: Math.max(0, Math.floor(totalBudget * 0.25)),
|
|
301
316
|
activeSkill: options.activeSkill,
|
|
302
317
|
relevantFiles: options.relevantFiles,
|
|
318
|
+
action: taskPlan?.action,
|
|
319
|
+
knowledgeNeeds: taskPlan?.knowledgeNeeds,
|
|
303
320
|
projectSignals: {
|
|
304
321
|
frameworks,
|
|
305
322
|
languages,
|
|
@@ -425,7 +442,7 @@ export class ContextEngine {
|
|
|
425
442
|
}
|
|
426
443
|
// 2. Knowledge + Memory in parallel
|
|
427
444
|
const [kResultsRaw, memResultsRaw] = await Promise.all([
|
|
428
|
-
this.searchKnowledgeWithStatus(combinedQuery, this.getKnowledgeSearchOptions(combinedQuery, 10, { activeSkill: request.activeSkill, relevantFiles: request.relevantFiles })).catch(() => ({ entries: [], warnings: [] })),
|
|
445
|
+
this.searchKnowledgeWithStatus(combinedQuery, this.getKnowledgeSearchOptions(combinedQuery, 10, { activeSkill: request.activeSkill, relevantFiles: request.relevantFiles }, retrievalTaskPlan, maxTokens)).catch(() => ({ entries: [], warnings: [] })),
|
|
429
446
|
this.memory.search(request.query, {
|
|
430
447
|
limit: 5,
|
|
431
448
|
purpose: intent.intent,
|
|
@@ -576,9 +593,27 @@ export class ContextEngine {
|
|
|
576
593
|
const mode = options?.mode ?? "fast";
|
|
577
594
|
const memoryRevision = this.memory.getRetrievalRevision?.() ?? 0;
|
|
578
595
|
const cacheKey = `ctx:${query}:${mode}:${options?.activeSkill ?? ""}:${(options?.relevantFiles ?? []).join(",")}:memory:${memoryRevision}`;
|
|
579
|
-
|
|
596
|
+
let cached = warnings.length === 0 && !options?.requestId
|
|
580
597
|
? this.queryCache.get(cacheKey)
|
|
581
598
|
: undefined;
|
|
599
|
+
if (cached) {
|
|
600
|
+
const changed = typeof this.cg.getChangedFiles === 'function'
|
|
601
|
+
? this.cg.getChangedFiles()
|
|
602
|
+
: {};
|
|
603
|
+
const changedPaths = [...(changed.added ?? []), ...(changed.modified ?? []), ...(changed.removed ?? [])];
|
|
604
|
+
const cachedPaths = [
|
|
605
|
+
...getGroupFilePaths(cached.requiredFiles),
|
|
606
|
+
...getGroupFilePaths(cached.optionalFiles),
|
|
607
|
+
];
|
|
608
|
+
const freshness = this.indexer?.ensureFilesCurrent
|
|
609
|
+
? await this.indexer.ensureFilesCurrent(cachedPaths, { refresh: false })
|
|
610
|
+
: null;
|
|
611
|
+
if (changedPaths.length > 0 || freshness?.files.some(file => !file.current)) {
|
|
612
|
+
this.queryCache.invalidatePaths(new Set([...changedPaths, ...(freshness?.stale ?? []), ...(freshness?.missing ?? [])]));
|
|
613
|
+
invalidateContextPrefetchCache(this.cg.getProjectRoot?.() ?? process.cwd());
|
|
614
|
+
cached = undefined;
|
|
615
|
+
}
|
|
616
|
+
}
|
|
582
617
|
if (cached) {
|
|
583
618
|
return {
|
|
584
619
|
...cached,
|
|
@@ -615,7 +650,7 @@ export class ContextEngine {
|
|
|
615
650
|
})
|
|
616
651
|
: Promise.resolve(null),
|
|
617
652
|
Promise.race([
|
|
618
|
-
this.searchKnowledgeWithStatus(combinedQuery, this.getKnowledgeSearchOptions(combinedQuery, 5, options)),
|
|
653
|
+
this.searchKnowledgeWithStatus(combinedQuery, this.getKnowledgeSearchOptions(combinedQuery, 5, options, retrievalTaskPlan, maxTokens)),
|
|
619
654
|
new Promise((_, reject) => setTimeout(() => reject(new Error("knowledge timeout")), 5000)),
|
|
620
655
|
]).catch((err) => {
|
|
621
656
|
console.error("[devflow] knowledge.search timeout:", err.message);
|
|
@@ -699,6 +734,31 @@ export class ContextEngine {
|
|
|
699
734
|
}
|
|
700
735
|
console.error("[devflow:timing] Fallback searchNodes loop:", Date.now() - tFallback, "ms");
|
|
701
736
|
timing["2_fallback_loop"] = Date.now() - tFallback;
|
|
737
|
+
const fileFreshnessByPath = new Map();
|
|
738
|
+
if (this.indexer?.ensureFilesCurrent) {
|
|
739
|
+
const candidatePaths = [...new Set([
|
|
740
|
+
...ctx.nodes.map((node) => String(node.filePath ?? '')).filter(Boolean),
|
|
741
|
+
...ctx.entryPoints.map((entry) => String(entry.filePath ?? '')).filter(Boolean),
|
|
742
|
+
])];
|
|
743
|
+
const freshness = await this.indexer.ensureFilesCurrent(candidatePaths, { refresh: true, timeoutMs: 5_000 });
|
|
744
|
+
for (const file of freshness.files)
|
|
745
|
+
fileFreshnessByPath.set(file.path, file);
|
|
746
|
+
const rejectedPaths = new Set([...freshness.missing, ...freshness.stale]);
|
|
747
|
+
if (rejectedPaths.size > 0 || freshness.refreshed.length > 0) {
|
|
748
|
+
this.queryCache.invalidatePaths(new Set([...rejectedPaths, ...freshness.refreshed]));
|
|
749
|
+
invalidateContextPrefetchCache(this.cg.getProjectRoot?.() ?? process.cwd());
|
|
750
|
+
}
|
|
751
|
+
if (freshness.missing.length > 0)
|
|
752
|
+
warnings.push(`codegraph-missing-files:${freshness.missing.length}`);
|
|
753
|
+
if (freshness.stale.length > 0)
|
|
754
|
+
warnings.push(`codegraph-stale-degraded:${freshness.stale.length}`);
|
|
755
|
+
ctx.nodes = ctx.nodes.filter((node) => !rejectedPaths.has(node.filePath));
|
|
756
|
+
ctx.entryPoints = ctx.entryPoints.filter((entry) => !rejectedPaths.has(entry.filePath));
|
|
757
|
+
if (Array.isArray(ctx.edges) && rejectedPaths.size > 0) {
|
|
758
|
+
const retainedIds = new Set(ctx.nodes.map((node) => node.id));
|
|
759
|
+
ctx.edges = ctx.edges.filter((edge) => retainedIds.has(edge.source) && retainedIds.has(edge.target));
|
|
760
|
+
}
|
|
761
|
+
}
|
|
702
762
|
const edgeQuality = sanitizeCodeGraphEdges(this.cg, Array.isArray(ctx.edges) ? ctx.edges : []);
|
|
703
763
|
ctx.edges = edgeQuality.accepted;
|
|
704
764
|
if (edgeQuality.rejected.length > 0) {
|
|
@@ -795,6 +855,9 @@ export class ContextEngine {
|
|
|
795
855
|
})
|
|
796
856
|
: [];
|
|
797
857
|
for (const candidate of roleCandidates) {
|
|
858
|
+
if (this.indexer?.getFileFreshness) {
|
|
859
|
+
fileFreshnessByPath.set(candidate.path, this.indexer.getFileFreshness(candidate.path));
|
|
860
|
+
}
|
|
798
861
|
fileRoleByPath.set(candidate.path, candidate);
|
|
799
862
|
if (candidate.role === 'target') {
|
|
800
863
|
requiredFileSet.add(candidate.path);
|
|
@@ -812,10 +875,21 @@ export class ContextEngine {
|
|
|
812
875
|
});
|
|
813
876
|
}
|
|
814
877
|
}
|
|
815
|
-
|
|
816
|
-
const rankedOptionalPaths = new Set(rankedOptionalFiles.map(candidate => candidate.path));
|
|
878
|
+
let rankedOptionalFiles = rankOptionalContextFiles([...optionalCandidates.values()], taskType);
|
|
817
879
|
for (const candidate of rankedOptionalFiles)
|
|
818
880
|
optionalFileSet.add(candidate.path);
|
|
881
|
+
if (this.indexer?.getFileFreshness) {
|
|
882
|
+
for (const path of [...requiredFileSet, ...optionalFileSet]) {
|
|
883
|
+
const freshness = fileFreshnessByPath.get(path) ?? this.indexer.getFileFreshness(path);
|
|
884
|
+
fileFreshnessByPath.set(path, freshness);
|
|
885
|
+
if (freshness.current)
|
|
886
|
+
continue;
|
|
887
|
+
requiredFileSet.delete(path);
|
|
888
|
+
optionalFileSet.delete(path);
|
|
889
|
+
fileRoleByPath.delete(path);
|
|
890
|
+
}
|
|
891
|
+
rankedOptionalFiles = rankedOptionalFiles.filter(candidate => optionalFileSet.has(candidate.path));
|
|
892
|
+
}
|
|
819
893
|
const optionalCandidateByPath = new Map(rankedOptionalFiles.map(candidate => [candidate.path, candidate]));
|
|
820
894
|
const retainedFiles = new Set([...requiredFileSet, ...optionalFileSet]);
|
|
821
895
|
for (let index = keySymbols.length - 1; index >= 0; index--) {
|
|
@@ -866,7 +940,7 @@ export class ContextEngine {
|
|
|
866
940
|
score: 0.5,
|
|
867
941
|
});
|
|
868
942
|
}
|
|
869
|
-
const compressed = compressToTokenLimit(allFileSnippets, maxTokens);
|
|
943
|
+
const compressed = compressToTokenLimit(allFileSnippets, Math.floor(maxTokens * 0.45));
|
|
870
944
|
if (compressed.overBudget)
|
|
871
945
|
warnings.push("context-compressor-budget-exceeded");
|
|
872
946
|
console.error("[devflow:timing] Snippet building + compress:", Date.now() - tSnippet, "ms");
|
|
@@ -1002,6 +1076,7 @@ export class ContextEngine {
|
|
|
1002
1076
|
targetAnchors,
|
|
1003
1077
|
taskPlan: retrievalTaskPlan,
|
|
1004
1078
|
fileRoles: fileRoleByPath,
|
|
1079
|
+
fileFreshness: fileFreshnessByPath,
|
|
1005
1080
|
memoryHits,
|
|
1006
1081
|
knowledgeHits,
|
|
1007
1082
|
rejectedCodeRelations: edgeQuality.rejected.length,
|
|
@@ -1010,14 +1085,21 @@ export class ContextEngine {
|
|
|
1010
1085
|
+ Object.values((knowledgeResult
|
|
1011
1086
|
.retrievalDiagnostics?.qualityRejected) ?? {}).reduce((total, count) => total + count, 0),
|
|
1012
1087
|
});
|
|
1088
|
+
try {
|
|
1089
|
+
recordTaskEnvelopeLedger(result.taskEnvelope);
|
|
1090
|
+
}
|
|
1091
|
+
catch {
|
|
1092
|
+
result.warnings = [...(result.warnings ?? []), 'retrieval-ledger-degraded'];
|
|
1093
|
+
}
|
|
1013
1094
|
if (options?.requestId && result.taskEnvelope.memory.entries.length > 0) {
|
|
1014
1095
|
try {
|
|
1015
1096
|
await this.memory.recordContextEnvelopeSelection?.({
|
|
1016
|
-
|
|
1097
|
+
entries: result.taskEnvelope.memory.entries,
|
|
1017
1098
|
sessionId: options.sessionId,
|
|
1018
1099
|
executionId: options.executionId,
|
|
1019
1100
|
requestId: options.requestId,
|
|
1020
1101
|
query,
|
|
1102
|
+
contextReceipt: result.taskEnvelope.receipt.contextHash,
|
|
1021
1103
|
});
|
|
1022
1104
|
}
|
|
1023
1105
|
catch {
|
|
@@ -1051,6 +1133,27 @@ export class ContextEngine {
|
|
|
1051
1133
|
});
|
|
1052
1134
|
await this.applyLegacySymbolVectorRecall(cgResult, query, warnings);
|
|
1053
1135
|
this.clearRecoveredSymbolWarning(warnings);
|
|
1136
|
+
const fileFreshnessByPath = new Map();
|
|
1137
|
+
if (this.indexer?.ensureFilesCurrent) {
|
|
1138
|
+
const candidatePaths = [...new Set(Array.from(cgResult.nodes.values()).map(node => node.filePath))];
|
|
1139
|
+
const freshness = await this.indexer.ensureFilesCurrent(candidatePaths, { refresh: true, timeoutMs: 5_000 });
|
|
1140
|
+
for (const file of freshness.files)
|
|
1141
|
+
fileFreshnessByPath.set(file.path, file);
|
|
1142
|
+
const rejectedPaths = new Set([...freshness.missing, ...freshness.stale]);
|
|
1143
|
+
for (const [id, node] of cgResult.nodes) {
|
|
1144
|
+
if (rejectedPaths.has(node.filePath))
|
|
1145
|
+
cgResult.nodes.delete(id);
|
|
1146
|
+
}
|
|
1147
|
+
cgResult.roots = cgResult.roots.filter(id => cgResult.nodes.has(id));
|
|
1148
|
+
if (freshness.missing.length > 0)
|
|
1149
|
+
warnings.push(`codegraph-missing-files:${freshness.missing.length}`);
|
|
1150
|
+
if (freshness.stale.length > 0)
|
|
1151
|
+
warnings.push(`codegraph-stale-degraded:${freshness.stale.length}`);
|
|
1152
|
+
if (rejectedPaths.size > 0 || freshness.refreshed.length > 0) {
|
|
1153
|
+
this.queryCache.invalidatePaths(new Set([...rejectedPaths, ...freshness.refreshed]));
|
|
1154
|
+
invalidateContextPrefetchCache(this.cg.getProjectRoot?.() ?? process.cwd());
|
|
1155
|
+
}
|
|
1156
|
+
}
|
|
1054
1157
|
const edgeQuality = sanitizeCodeGraphEdges(this.cg, cgResult.edges ?? []);
|
|
1055
1158
|
if (edgeQuality.rejected.length > 0) {
|
|
1056
1159
|
warnings.push(`codegraph-relations-rejected:${edgeQuality.rejected.length}`);
|
|
@@ -1124,6 +1227,9 @@ export class ContextEngine {
|
|
|
1124
1227
|
})
|
|
1125
1228
|
: [];
|
|
1126
1229
|
for (const candidate of roleCandidates) {
|
|
1230
|
+
if (this.indexer?.getFileFreshness) {
|
|
1231
|
+
fileFreshnessByPath.set(candidate.path, this.indexer.getFileFreshness(candidate.path));
|
|
1232
|
+
}
|
|
1127
1233
|
fileRoleByPath.set(candidate.path, candidate);
|
|
1128
1234
|
if (candidate.role === 'target') {
|
|
1129
1235
|
requiredFileSet.add(candidate.path);
|
|
@@ -1141,9 +1247,21 @@ export class ContextEngine {
|
|
|
1141
1247
|
});
|
|
1142
1248
|
}
|
|
1143
1249
|
}
|
|
1144
|
-
|
|
1250
|
+
let rankedOptionalFiles = rankOptionalContextFiles([...optionalCandidates.values()], taskType);
|
|
1145
1251
|
for (const candidate of rankedOptionalFiles)
|
|
1146
1252
|
optionalFileSet.add(candidate.path);
|
|
1253
|
+
if (this.indexer?.getFileFreshness) {
|
|
1254
|
+
for (const path of [...requiredFileSet, ...optionalFileSet]) {
|
|
1255
|
+
const freshness = fileFreshnessByPath.get(path) ?? this.indexer.getFileFreshness(path);
|
|
1256
|
+
fileFreshnessByPath.set(path, freshness);
|
|
1257
|
+
if (freshness.current)
|
|
1258
|
+
continue;
|
|
1259
|
+
requiredFileSet.delete(path);
|
|
1260
|
+
optionalFileSet.delete(path);
|
|
1261
|
+
fileRoleByPath.delete(path);
|
|
1262
|
+
}
|
|
1263
|
+
rankedOptionalFiles = rankedOptionalFiles.filter(candidate => optionalFileSet.has(candidate.path));
|
|
1264
|
+
}
|
|
1147
1265
|
for (const node of allNodes) {
|
|
1148
1266
|
if (optionalFileSet.has(node.filePath))
|
|
1149
1267
|
optionalSymbols.push(node.name);
|
|
@@ -1203,7 +1321,7 @@ export class ContextEngine {
|
|
|
1203
1321
|
let knowledgeReasoning = [];
|
|
1204
1322
|
let knowledgeHits = [];
|
|
1205
1323
|
try {
|
|
1206
|
-
const kResults = await this.searchKnowledgeWithStatus(combinedQuery, this.getKnowledgeSearchOptions(combinedQuery, 5, options));
|
|
1324
|
+
const kResults = await this.searchKnowledgeWithStatus(combinedQuery, this.getKnowledgeSearchOptions(combinedQuery, 5, options, retrievalTaskPlan, maxTokens));
|
|
1207
1325
|
if (kResults.warnings.length > 0)
|
|
1208
1326
|
warnings.push(...kResults.warnings);
|
|
1209
1327
|
knowledgeHits = kResults.entries;
|
|
@@ -1257,7 +1375,7 @@ export class ContextEngine {
|
|
|
1257
1375
|
score: 0.5,
|
|
1258
1376
|
});
|
|
1259
1377
|
}
|
|
1260
|
-
const compressed = compressToTokenLimit(allFileSnippets, maxTokens);
|
|
1378
|
+
const compressed = compressToTokenLimit(allFileSnippets, Math.floor(maxTokens * 0.45));
|
|
1261
1379
|
if (compressed.overBudget)
|
|
1262
1380
|
warnings.push("context-compressor-budget-exceeded");
|
|
1263
1381
|
const requiredPaths = new Set(compressed.files.filter(f => requiredFileSet.has(f.path)).map(f => f.path));
|
|
@@ -1330,6 +1448,7 @@ export class ContextEngine {
|
|
|
1330
1448
|
tokenBudget: maxTokens,
|
|
1331
1449
|
targetAnchors,
|
|
1332
1450
|
fileRoles: fileRoleByPath,
|
|
1451
|
+
fileFreshness: fileFreshnessByPath,
|
|
1333
1452
|
memoryHits,
|
|
1334
1453
|
knowledgeHits,
|
|
1335
1454
|
rejectedCodeRelations: edgeQuality.rejected.length,
|
|
@@ -1420,4 +1539,9 @@ export class ContextEngine {
|
|
|
1420
1539
|
};
|
|
1421
1540
|
}
|
|
1422
1541
|
}
|
|
1542
|
+
function getGroupFilePaths(group) {
|
|
1543
|
+
if (Array.isArray(group))
|
|
1544
|
+
return group;
|
|
1545
|
+
return group?.files ?? [];
|
|
1546
|
+
}
|
|
1423
1547
|
//# sourceMappingURL=context-engine.js.map
|