@roarpeng/graphflow 1.15.2 → 1.15.4
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 +16 -0
- package/README.md +62 -61
- package/README.zh.md +58 -45
- package/cordis.patch.yml +4 -1
- package/dist/graph/context-package-core.d.ts +59 -0
- package/dist/graph/context-package-core.d.ts.map +1 -0
- package/dist/graph/context-package-core.js +512 -0
- package/dist/graph/context-package-core.js.map +1 -0
- package/dist/graph/context-slicer.d.ts +7 -11
- package/dist/graph/context-slicer.d.ts.map +1 -1
- package/dist/graph/context-slicer.js +54 -636
- package/dist/graph/context-slicer.js.map +1 -1
- package/dist/integrations/dsh-harness-installer.d.ts +41 -5
- package/dist/integrations/dsh-harness-installer.d.ts.map +1 -1
- package/dist/integrations/dsh-harness-installer.js +204 -15
- package/dist/integrations/dsh-harness-installer.js.map +1 -1
- package/dist/integrations/host-adapter-install.d.ts.map +1 -1
- package/dist/integrations/host-adapter-install.js +1 -0
- package/dist/integrations/host-adapter-install.js.map +1 -1
- package/package.json +1 -1
- package/plugin.json +1 -1
|
@@ -1,435 +1,63 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.DEFAULT_MAX_SIBLING_FILES = exports.DEFAULT_MAX_SYMBOLS_PER_FILE = exports.prepareHitsForPackaging = exports.hasModuleFamilyIntent = exports.expandSiblingDirectoryHits = exports.diversifyHitsBySourceFile = exports.extractFileFromSymbolId = exports.deriveModuleId = exports.modulePathKey = exports.markLayerUsed = exports.canUseLayer = exports.classifyLayer = exports.summarizeNodes = exports.estimateTokens = exports.getEncoder = exports.ARCHITECTURE_QUERY = exports.DEFAULT_EXPANSION_RELATIONS = void 0;
|
|
4
|
-
exports.isPinnedL3Node = isPinnedL3Node;
|
|
5
|
-
exports.expandSubgraph = expandSubgraph;
|
|
3
|
+
exports.DEFAULT_MAX_SIBLING_FILES = exports.DEFAULT_MAX_SYMBOLS_PER_FILE = exports.prepareHitsForPackaging = exports.hasModuleFamilyIntent = exports.expandSiblingDirectoryHits = exports.diversifyHitsBySourceFile = exports.extractFileFromSymbolId = exports.deriveModuleId = exports.modulePathKey = exports.markLayerUsed = exports.canUseLayer = exports.classifyLayer = exports.summarizeNodes = exports.estimateTokens = exports.getEncoder = exports.vectorRecall = exports.expandSubgraph = exports.isPinnedL3Node = exports.ARCHITECTURE_QUERY = exports.DEFAULT_EXPANSION_RELATIONS = void 0;
|
|
6
4
|
exports.buildContextSlice = buildContextSlice;
|
|
7
|
-
exports.vectorRecall = vectorRecall;
|
|
8
5
|
exports.buildLayeredContextPackage = buildLayeredContextPackage;
|
|
9
6
|
exports.createContextRefillManager = createContextRefillManager;
|
|
10
7
|
exports.buildEnhancedContextPackage = buildEnhancedContextPackage;
|
|
11
8
|
const logger_js_1 = require("../utils/logger.js");
|
|
12
|
-
const embeddings_js_1 = require("../learning/embeddings.js");
|
|
13
|
-
const graph_utils_js_1 = require("./graph-utils.js");
|
|
14
|
-
const query_expand_js_1 = require("./query-expand.js");
|
|
15
|
-
const hit_diversify_js_1 = require("./hit-diversify.js");
|
|
16
9
|
const graph_compression_js_1 = require("./graph-compression.js");
|
|
17
10
|
const repo_map_js_1 = require("./repo-map.js");
|
|
18
11
|
const adaptive_budget_js_1 = require("./adaptive-budget.js");
|
|
19
12
|
const symbol_extract_js_1 = require("./symbol-extract.js");
|
|
13
|
+
const context_slicer_utils_js_1 = require("./context-slicer-utils.js");
|
|
14
|
+
const embeddings_js_1 = require("../learning/embeddings.js");
|
|
15
|
+
const context_package_core_js_1 = require("./context-package-core.js");
|
|
16
|
+
Object.defineProperty(exports, "expandSubgraph", { enumerable: true, get: function () { return context_package_core_js_1.expandSubgraph; } });
|
|
17
|
+
Object.defineProperty(exports, "isPinnedL3Node", { enumerable: true, get: function () { return context_package_core_js_1.isPinnedL3Node; } });
|
|
18
|
+
Object.defineProperty(exports, "vectorRecall", { enumerable: true, get: function () { return context_package_core_js_1.vectorRecall; } });
|
|
20
19
|
var context_slicer_types_js_1 = require("./context-slicer-types.js");
|
|
21
20
|
Object.defineProperty(exports, "DEFAULT_EXPANSION_RELATIONS", { enumerable: true, get: function () { return context_slicer_types_js_1.DEFAULT_EXPANSION_RELATIONS; } });
|
|
22
21
|
Object.defineProperty(exports, "ARCHITECTURE_QUERY", { enumerable: true, get: function () { return context_slicer_types_js_1.ARCHITECTURE_QUERY; } });
|
|
23
|
-
const L3_PIN_HINT = /alignment|deviation|goal/i;
|
|
24
|
-
/**
|
|
25
|
-
* Governance pins for L3: Decision/goal nodes that packing must prefer so
|
|
26
|
-
* budget truncation cannot drop alignment / deviation / goal constraints.
|
|
27
|
-
*/
|
|
28
|
-
function isPinnedL3Node(node) {
|
|
29
|
-
if (node.id.startsWith("goal:")) {
|
|
30
|
-
return true;
|
|
31
|
-
}
|
|
32
|
-
if (node.type !== "Decision") {
|
|
33
|
-
return false;
|
|
34
|
-
}
|
|
35
|
-
if (L3_PIN_HINT.test(node.content)) {
|
|
36
|
-
return true;
|
|
37
|
-
}
|
|
38
|
-
if (node.metadata && L3_PIN_HINT.test(JSON.stringify(node.metadata))) {
|
|
39
|
-
return true;
|
|
40
|
-
}
|
|
41
|
-
return false;
|
|
42
|
-
}
|
|
43
|
-
function collectPinnedL3Nodes(ranked, snapshotNodes, added) {
|
|
44
|
-
const pins = [];
|
|
45
|
-
const seen = new Set();
|
|
46
|
-
const consider = (node) => {
|
|
47
|
-
if (added.has(node.id) || seen.has(node.id) || !isPinnedL3Node(node)) {
|
|
48
|
-
return;
|
|
49
|
-
}
|
|
50
|
-
seen.add(node.id);
|
|
51
|
-
pins.push(node);
|
|
52
|
-
};
|
|
53
|
-
for (const node of ranked)
|
|
54
|
-
consider(node);
|
|
55
|
-
if (snapshotNodes) {
|
|
56
|
-
for (const node of snapshotNodes)
|
|
57
|
-
consider(node);
|
|
58
|
-
}
|
|
59
|
-
return pins;
|
|
60
|
-
}
|
|
61
|
-
function tryAppendL3Node(node, ctx, acc) {
|
|
62
|
-
if (ctx.maxAnchors !== undefined && ctx.anchorChannel.length >= ctx.maxAnchors) {
|
|
63
|
-
return "skip";
|
|
64
|
-
}
|
|
65
|
-
if (ctx.added.has(node.id)) {
|
|
66
|
-
return "skip";
|
|
67
|
-
}
|
|
68
|
-
if (!(0, context_slicer_utils_js_2.canUseLayer)("L3", ctx.quota, ctx.used)) {
|
|
69
|
-
return "skip";
|
|
70
|
-
}
|
|
71
|
-
const summary = `${node.type}: ${node.content}`;
|
|
72
|
-
const estimate = (0, context_slicer_utils_js_2.estimateTokens)(summary);
|
|
73
|
-
if (acc.tokens + estimate > ctx.maxTokens) {
|
|
74
|
-
acc.truncated = true;
|
|
75
|
-
return "budget";
|
|
76
|
-
}
|
|
77
|
-
ctx.summaryChannel.push(summary);
|
|
78
|
-
ctx.anchorChannel.push({ id: node.id, type: node.type, layer: "L3" });
|
|
79
|
-
ctx.added.add(node.id);
|
|
80
|
-
acc.tokens += estimate;
|
|
81
|
-
(0, context_slicer_utils_js_2.markLayerUsed)("L3", ctx.used);
|
|
82
|
-
return "packed";
|
|
83
|
-
}
|
|
84
|
-
// ───────────────── Conversation Graph W2a: dialogue nodes in L3 packing ─────────────────
|
|
85
|
-
/** Max matched dialogue turns packed per preview (after effective-turns filter). */
|
|
86
|
-
const DIALOGUE_PACK_MAX_TURNS = 3;
|
|
87
|
-
/**
|
|
88
|
-
* Collect dialogue-turn context for a query (Conversation Graph W2a).
|
|
89
|
-
*
|
|
90
|
-
* - Matches effective (non-superseded) turns whose query/title/summary tokens
|
|
91
|
-
* overlap the search tokens; never within the same recording session id
|
|
92
|
-
* chain — matched turns are historical Q&A, including other sessions.
|
|
93
|
-
* - Each packed turn carries an optional correction annotation when the
|
|
94
|
-
* turn itself supersedes an earlier one ("结论 X 已被修正为 Y").
|
|
95
|
-
* - Falls back to [] on any failure — dialogue packing is additive.
|
|
96
|
-
*/
|
|
97
|
-
async function collectDialogueContextLines(client, query, options) {
|
|
98
|
-
try {
|
|
99
|
-
const { listDialogueTurns, effectiveTurns, formatSupersessionLine } = await import("../learning/dialogue-thread.js");
|
|
100
|
-
const allTurns = await listDialogueTurns(client, { limit: 60 });
|
|
101
|
-
const turns = effectiveTurns(allTurns);
|
|
102
|
-
if (turns.length === 0)
|
|
103
|
-
return [];
|
|
104
|
-
const scoreTokens = (0, graph_utils_js_1.buildSearchScoreTokens)(query, options?.englishQuery);
|
|
105
|
-
if (scoreTokens.length === 0)
|
|
106
|
-
return [];
|
|
107
|
-
const tokenSet = new Set(scoreTokens.map((t) => t.toLowerCase()));
|
|
108
|
-
const scored = turns
|
|
109
|
-
.map((turn) => {
|
|
110
|
-
const haystack = `${turn.userQuery} ${turn.title ?? ""} ${turn.summary ?? ""}`.toLowerCase();
|
|
111
|
-
let hits = 0;
|
|
112
|
-
for (const token of tokenSet) {
|
|
113
|
-
if (haystack.includes(token))
|
|
114
|
-
hits += 1;
|
|
115
|
-
}
|
|
116
|
-
return { turn, hits };
|
|
117
|
-
})
|
|
118
|
-
.filter((s) => s.hits > 0)
|
|
119
|
-
.sort((a, b) => b.hits - a.hits || b.turn.updatedAt - a.turn.updatedAt)
|
|
120
|
-
.slice(0, DIALOGUE_PACK_MAX_TURNS);
|
|
121
|
-
if (scored.length === 0)
|
|
122
|
-
return [];
|
|
123
|
-
// Correction chain lookup uses ALL turns (the superseded one is off the
|
|
124
|
-
// effective list by design — we need it to render "was corrected").
|
|
125
|
-
const byId = new Map(allTurns.map((t) => [t.id, t]));
|
|
126
|
-
const lines = [];
|
|
127
|
-
for (const { turn } of scored) {
|
|
128
|
-
const correctionTargets = (turn.supersedesTurnIds ?? [])
|
|
129
|
-
.map((id) => byId.get(id))
|
|
130
|
-
.filter((t) => Boolean(t));
|
|
131
|
-
const correctionLine = correctionTargets.length > 0
|
|
132
|
-
? formatSupersessionLine(correctionTargets[correctionTargets.length - 1], turn)
|
|
133
|
-
: undefined;
|
|
134
|
-
lines.push({
|
|
135
|
-
kind: "turn",
|
|
136
|
-
text: turn.title ?? turn.userQuery,
|
|
137
|
-
node: {
|
|
138
|
-
id: turn.id,
|
|
139
|
-
type: "Decision",
|
|
140
|
-
content: `dialogue-turn #${turn.seq} Q: ${turn.userQuery}`,
|
|
141
|
-
metadata: { kind: "dialogue-turn", record: JSON.stringify(turn) },
|
|
142
|
-
},
|
|
143
|
-
...(correctionLine ? { correctionLine } : {}),
|
|
144
|
-
});
|
|
145
|
-
}
|
|
146
|
-
return lines;
|
|
147
|
-
}
|
|
148
|
-
catch {
|
|
149
|
-
return [];
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
22
|
// Re-export utility functions that were previously exported
|
|
153
|
-
var
|
|
154
|
-
Object.defineProperty(exports, "getEncoder", { enumerable: true, get: function () { return
|
|
155
|
-
Object.defineProperty(exports, "estimateTokens", { enumerable: true, get: function () { return
|
|
156
|
-
Object.defineProperty(exports, "summarizeNodes", { enumerable: true, get: function () { return
|
|
157
|
-
Object.defineProperty(exports, "classifyLayer", { enumerable: true, get: function () { return
|
|
158
|
-
Object.defineProperty(exports, "canUseLayer", { enumerable: true, get: function () { return
|
|
159
|
-
Object.defineProperty(exports, "markLayerUsed", { enumerable: true, get: function () { return
|
|
160
|
-
Object.defineProperty(exports, "modulePathKey", { enumerable: true, get: function () { return
|
|
161
|
-
Object.defineProperty(exports, "deriveModuleId", { enumerable: true, get: function () { return
|
|
162
|
-
Object.defineProperty(exports, "extractFileFromSymbolId", { enumerable: true, get: function () { return
|
|
163
|
-
var
|
|
164
|
-
Object.defineProperty(exports, "diversifyHitsBySourceFile", { enumerable: true, get: function () { return
|
|
165
|
-
Object.defineProperty(exports, "expandSiblingDirectoryHits", { enumerable: true, get: function () { return
|
|
166
|
-
Object.defineProperty(exports, "hasModuleFamilyIntent", { enumerable: true, get: function () { return
|
|
167
|
-
Object.defineProperty(exports, "prepareHitsForPackaging", { enumerable: true, get: function () { return
|
|
168
|
-
Object.defineProperty(exports, "DEFAULT_MAX_SYMBOLS_PER_FILE", { enumerable: true, get: function () { return
|
|
169
|
-
Object.defineProperty(exports, "DEFAULT_MAX_SIBLING_FILES", { enumerable: true, get: function () { return
|
|
170
|
-
// Import internal helpers (not re-exported)
|
|
171
|
-
const context_slicer_utils_js_2 = require("./context-slicer-utils.js");
|
|
172
|
-
const context_slicer_types_js_2 = require("./context-slicer-types.js");
|
|
173
|
-
async function expandSubgraph(client, seedIds, options) {
|
|
174
|
-
if (typeof client.getNeighbors !== "function")
|
|
175
|
-
return [];
|
|
176
|
-
const hops = options?.hops ?? 1;
|
|
177
|
-
const maxNodes = options?.maxNodes ?? 20;
|
|
178
|
-
const relations = options?.relations ?? context_slicer_types_js_2.DEFAULT_EXPANSION_RELATIONS;
|
|
179
|
-
const excluded = new Set(seedIds);
|
|
180
|
-
const collected = new Map();
|
|
181
|
-
let frontier = [...seedIds];
|
|
182
|
-
for (let depth = 0; depth < hops; depth += 1) {
|
|
183
|
-
if (frontier.length === 0)
|
|
184
|
-
break;
|
|
185
|
-
const neighbors = await client.getNeighbors(frontier, relations, "both");
|
|
186
|
-
const nextFrontier = [];
|
|
187
|
-
for (const { node } of neighbors) {
|
|
188
|
-
if (excluded.has(node.id) || collected.has(node.id))
|
|
189
|
-
continue;
|
|
190
|
-
collected.set(node.id, node);
|
|
191
|
-
nextFrontier.push(node.id);
|
|
192
|
-
if (collected.size >= maxNodes)
|
|
193
|
-
break;
|
|
194
|
-
}
|
|
195
|
-
if (collected.size >= maxNodes)
|
|
196
|
-
break;
|
|
197
|
-
frontier = nextFrontier;
|
|
198
|
-
}
|
|
199
|
-
return Array.from(collected.values());
|
|
200
|
-
}
|
|
23
|
+
var context_slicer_utils_js_2 = require("./context-slicer-utils.js");
|
|
24
|
+
Object.defineProperty(exports, "getEncoder", { enumerable: true, get: function () { return context_slicer_utils_js_2.getEncoder; } });
|
|
25
|
+
Object.defineProperty(exports, "estimateTokens", { enumerable: true, get: function () { return context_slicer_utils_js_2.estimateTokens; } });
|
|
26
|
+
Object.defineProperty(exports, "summarizeNodes", { enumerable: true, get: function () { return context_slicer_utils_js_2.summarizeNodes; } });
|
|
27
|
+
Object.defineProperty(exports, "classifyLayer", { enumerable: true, get: function () { return context_slicer_utils_js_2.classifyLayer; } });
|
|
28
|
+
Object.defineProperty(exports, "canUseLayer", { enumerable: true, get: function () { return context_slicer_utils_js_2.canUseLayer; } });
|
|
29
|
+
Object.defineProperty(exports, "markLayerUsed", { enumerable: true, get: function () { return context_slicer_utils_js_2.markLayerUsed; } });
|
|
30
|
+
Object.defineProperty(exports, "modulePathKey", { enumerable: true, get: function () { return context_slicer_utils_js_2.modulePathKey; } });
|
|
31
|
+
Object.defineProperty(exports, "deriveModuleId", { enumerable: true, get: function () { return context_slicer_utils_js_2.deriveModuleId; } });
|
|
32
|
+
Object.defineProperty(exports, "extractFileFromSymbolId", { enumerable: true, get: function () { return context_slicer_utils_js_2.extractFileFromSymbolId; } });
|
|
33
|
+
var hit_diversify_js_1 = require("./hit-diversify.js");
|
|
34
|
+
Object.defineProperty(exports, "diversifyHitsBySourceFile", { enumerable: true, get: function () { return hit_diversify_js_1.diversifyHitsBySourceFile; } });
|
|
35
|
+
Object.defineProperty(exports, "expandSiblingDirectoryHits", { enumerable: true, get: function () { return hit_diversify_js_1.expandSiblingDirectoryHits; } });
|
|
36
|
+
Object.defineProperty(exports, "hasModuleFamilyIntent", { enumerable: true, get: function () { return hit_diversify_js_1.hasModuleFamilyIntent; } });
|
|
37
|
+
Object.defineProperty(exports, "prepareHitsForPackaging", { enumerable: true, get: function () { return hit_diversify_js_1.prepareHitsForPackaging; } });
|
|
38
|
+
Object.defineProperty(exports, "DEFAULT_MAX_SYMBOLS_PER_FILE", { enumerable: true, get: function () { return hit_diversify_js_1.DEFAULT_MAX_SYMBOLS_PER_FILE; } });
|
|
39
|
+
Object.defineProperty(exports, "DEFAULT_MAX_SIBLING_FILES", { enumerable: true, get: function () { return hit_diversify_js_1.DEFAULT_MAX_SIBLING_FILES; } });
|
|
201
40
|
async function buildContextSlice(client, query, maxTokens) {
|
|
202
41
|
const pkg = await buildLayeredContextPackage(client, query, maxTokens);
|
|
203
42
|
return { items: pkg.summaryChannel, tokenEstimate: pkg.tokenEstimate };
|
|
204
43
|
}
|
|
205
|
-
function vectorRecall(nodes, queryEmbedding, topK, minSimilarity) {
|
|
206
|
-
if (!queryEmbedding || queryEmbedding.length === 0)
|
|
207
|
-
return [];
|
|
208
|
-
const scored = [];
|
|
209
|
-
for (const node of nodes) {
|
|
210
|
-
const emb = (0, embeddings_js_1.extractEmbedding)(node);
|
|
211
|
-
if (!emb)
|
|
212
|
-
continue;
|
|
213
|
-
const sim = (0, embeddings_js_1.cosineSimilarity)(queryEmbedding, emb);
|
|
214
|
-
if (sim >= minSimilarity)
|
|
215
|
-
scored.push({ node, sim });
|
|
216
|
-
}
|
|
217
|
-
scored.sort((a, b) => b.sim - a.sim);
|
|
218
|
-
return scored.slice(0, topK).map((s) => s.node);
|
|
219
|
-
}
|
|
220
|
-
function collectVectorRecallCandidates(client, keywordHits, enableFullGraphVectorRecall) {
|
|
221
|
-
if (!enableFullGraphVectorRecall) {
|
|
222
|
-
return keywordHits;
|
|
223
|
-
}
|
|
224
|
-
const byId = new Map();
|
|
225
|
-
for (const hit of keywordHits) {
|
|
226
|
-
if ((0, embeddings_js_1.extractEmbedding)(hit)) {
|
|
227
|
-
byId.set(hit.id, hit);
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
const snapshot = client.readSnapshot?.();
|
|
231
|
-
if (!snapshot) {
|
|
232
|
-
return keywordHits;
|
|
233
|
-
}
|
|
234
|
-
for (const node of snapshot.nodes) {
|
|
235
|
-
if ((0, embeddings_js_1.extractEmbedding)(node)) {
|
|
236
|
-
byId.set(node.id, node);
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
return byId.size > 0 ? Array.from(byId.values()) : keywordHits;
|
|
240
|
-
}
|
|
241
|
-
async function hnswVectorRecall(nodes, queryEmbedding, topK, minSimilarity, hnswIndexPath) {
|
|
242
|
-
// Memoized per-process + optional disk-persisted index: avoids re-extracting
|
|
243
|
-
// embeddings for every query and speeds up MCP server restarts.
|
|
244
|
-
const { getSharedVectorIndex } = await import("../learning/hnsw-index.js");
|
|
245
|
-
const { index } = getSharedVectorIndex(nodes, hnswIndexPath);
|
|
246
|
-
const results = await index.search(queryEmbedding, topK, minSimilarity);
|
|
247
|
-
return results.map((result) => result.node);
|
|
248
|
-
}
|
|
249
44
|
async function buildLayeredContextPackage(client, query, maxTokens, options) {
|
|
250
|
-
const keywordHits = await (0,
|
|
45
|
+
const keywordHits = await (0, context_package_core_js_1.collectKeywordHits)(client, query, options);
|
|
251
46
|
let hits = keywordHits;
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
const topK = options.vectorTopK ?? 8;
|
|
256
|
-
const minSim = options.vectorMinSimilarity ?? 0.05;
|
|
257
|
-
const vectorCandidates = collectVectorRecallCandidates(client, keywordHits, options.enableFullGraphVectorRecall === true);
|
|
258
|
-
const vectorHits = await hnswVectorRecall(vectorCandidates, queryEmbedding, topK, minSim, options?.hnswIndexPath);
|
|
259
|
-
hits = (0, embeddings_js_1.reciprocalRankFusion)([keywordHits, vectorHits]);
|
|
260
|
-
}
|
|
261
|
-
catch (error) {
|
|
262
|
-
logger_js_1.logger.warn({ error }, "Vector recall failed in buildLayeredContextPackage");
|
|
263
|
-
hits = keywordHits;
|
|
264
|
-
}
|
|
47
|
+
const fused = await (0, context_package_core_js_1.fuseVectorRecallIfEnabled)(client, query, keywordHits, options, "buildLayeredContextPackage");
|
|
48
|
+
if (fused) {
|
|
49
|
+
hits = fused;
|
|
265
50
|
}
|
|
266
51
|
const snapshotNodes = client.readSnapshot?.()?.nodes;
|
|
267
|
-
hits = (0,
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
const quota = {
|
|
277
|
-
l1: options?.layerQuota?.l1 ?? Number.POSITIVE_INFINITY,
|
|
278
|
-
l2: options?.layerQuota?.l2 ?? Number.POSITIVE_INFINITY,
|
|
279
|
-
l3: options?.layerQuota?.l3 ?? Number.POSITIVE_INFINITY,
|
|
280
|
-
};
|
|
281
|
-
const used = { l1: 0, l2: 0, l3: 0 };
|
|
282
|
-
const added = new Set();
|
|
283
|
-
const hitById = new Map();
|
|
284
|
-
for (const hit of hits) {
|
|
285
|
-
hitById.set(hit.id, hit);
|
|
286
|
-
const layer = (0, context_slicer_utils_js_2.classifyLayer)(hit);
|
|
287
|
-
if (!(0, context_slicer_utils_js_2.canUseLayer)(layer, quota, used)) {
|
|
288
|
-
continue;
|
|
289
|
-
}
|
|
290
|
-
const summary = `${hit.type}: ${hit.content}`;
|
|
291
|
-
const estimate = (0, context_slicer_utils_js_2.estimateTokens)(summary);
|
|
292
|
-
if (tokens + estimate > maxTokens) {
|
|
293
|
-
truncated = true;
|
|
294
|
-
break;
|
|
295
|
-
}
|
|
296
|
-
summaryChannel.push(summary);
|
|
297
|
-
anchorChannel.push({ id: hit.id, type: hit.type, layer });
|
|
298
|
-
added.add(hit.id);
|
|
299
|
-
tokens += estimate;
|
|
300
|
-
(0, context_slicer_utils_js_2.markLayerUsed)(layer, used);
|
|
301
|
-
}
|
|
302
|
-
if (!truncated) {
|
|
303
|
-
const moduleIds = new Set();
|
|
304
|
-
for (const anchor of anchorChannel) {
|
|
305
|
-
if (anchor.layer !== "L1" || (anchor.type !== "File" && anchor.type !== "Symbol")) {
|
|
306
|
-
continue;
|
|
307
|
-
}
|
|
308
|
-
const moduleId = (0, context_slicer_utils_js_2.deriveModuleId)(anchor, hitById.get(anchor.id));
|
|
309
|
-
if (moduleId) {
|
|
310
|
-
moduleIds.add(moduleId);
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
let moduleNodes = [];
|
|
314
|
-
if (moduleIds.size > 0 && typeof client.getNodesByIds === "function") {
|
|
315
|
-
moduleNodes = await client.getNodesByIds(Array.from(moduleIds));
|
|
316
|
-
}
|
|
317
|
-
for (const moduleId of moduleIds) {
|
|
318
|
-
if (added.has(moduleId))
|
|
319
|
-
continue;
|
|
320
|
-
if (!(0, context_slicer_utils_js_2.canUseLayer)("L2", quota, used))
|
|
321
|
-
break;
|
|
322
|
-
const node = moduleNodes.find((n) => n.id === moduleId) ??
|
|
323
|
-
({ id: moduleId, type: "Module", content: moduleId.slice("module:".length) });
|
|
324
|
-
const summary = `${node.type}: ${node.content}`;
|
|
325
|
-
const estimate = (0, context_slicer_utils_js_2.estimateTokens)(summary);
|
|
326
|
-
if (tokens + estimate > maxTokens) {
|
|
327
|
-
truncated = true;
|
|
328
|
-
break;
|
|
329
|
-
}
|
|
330
|
-
summaryChannel.push(summary);
|
|
331
|
-
anchorChannel.push({ id: node.id, type: node.type, layer: "L2" });
|
|
332
|
-
added.add(node.id);
|
|
333
|
-
tokens += estimate;
|
|
334
|
-
(0, context_slicer_utils_js_2.markLayerUsed)("L2", used);
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
const archIntent = context_slicer_types_js_2.ARCHITECTURE_QUERY.test((0, graph_utils_js_1.composeContextQuery)(query, options?.englishQuery));
|
|
338
|
-
if (options?.enableAlwaysOnLayers || archIntent) {
|
|
339
|
-
const l3Candidates = (0, graph_utils_js_1.rankNodesForContextQuery)((await client.queryByKeyword(query)).filter((n) => n.type === "Skill" || n.type === "Decision"), query, {
|
|
340
|
-
scoreTokens: (0, graph_utils_js_1.buildSearchScoreTokens)(query, options?.englishQuery),
|
|
341
|
-
matchQueries: options?.englishQuery?.trim()
|
|
342
|
-
? [query, options.englishQuery.trim()]
|
|
343
|
-
: [query],
|
|
344
|
-
...(options?.englishQuery !== undefined ? { englishQuery: options.englishQuery } : {}),
|
|
345
|
-
});
|
|
346
|
-
const snapshotForPins = client.readSnapshot?.()?.nodes;
|
|
347
|
-
const acc = { tokens, truncated };
|
|
348
|
-
const packCtx = { summaryChannel, anchorChannel, added, quota, used, maxTokens };
|
|
349
|
-
const pins = collectPinnedL3Nodes(l3Candidates, snapshotForPins, added);
|
|
350
|
-
for (const node of pins) {
|
|
351
|
-
if (tryAppendL3Node(node, packCtx, acc) === "budget")
|
|
352
|
-
break;
|
|
353
|
-
}
|
|
354
|
-
tokens = acc.tokens;
|
|
355
|
-
truncated = acc.truncated;
|
|
356
|
-
if (!truncated) {
|
|
357
|
-
let l3Added = pins.filter((n) => added.has(n.id)).length;
|
|
358
|
-
for (const node of l3Candidates) {
|
|
359
|
-
if (l3Added >= 2)
|
|
360
|
-
break;
|
|
361
|
-
if (isPinnedL3Node(node))
|
|
362
|
-
continue;
|
|
363
|
-
const result = tryAppendL3Node(node, packCtx, acc);
|
|
364
|
-
if (result === "budget")
|
|
365
|
-
break;
|
|
366
|
-
if (result === "packed")
|
|
367
|
-
l3Added += 1;
|
|
368
|
-
}
|
|
369
|
-
tokens = acc.tokens;
|
|
370
|
-
truncated = acc.truncated;
|
|
371
|
-
}
|
|
372
|
-
}
|
|
373
|
-
// Conversation Graph W2a: pack matched dialogue turns (effective ones plus
|
|
374
|
-
// a correction-chain annotation) under the same L3 quota + token budget as
|
|
375
|
-
// every other node — dialogue anchors are never exempt from budgeting.
|
|
376
|
-
if (!truncated) {
|
|
377
|
-
const dialogueAcc = { tokens, truncated };
|
|
378
|
-
const dialoguePackCtx = { summaryChannel, anchorChannel, added, quota, used, maxTokens };
|
|
379
|
-
const dialogueLines = await collectDialogueContextLines(client, query, options);
|
|
380
|
-
for (const line of dialogueLines) {
|
|
381
|
-
if (line.kind === "summary") {
|
|
382
|
-
const estimate = (0, context_slicer_utils_js_2.estimateTokens)(line.text);
|
|
383
|
-
if (dialogueAcc.tokens + estimate > maxTokens) {
|
|
384
|
-
dialogueAcc.truncated = true;
|
|
385
|
-
break;
|
|
386
|
-
}
|
|
387
|
-
dialogueAcc.tokens += estimate;
|
|
388
|
-
}
|
|
389
|
-
else if (line.kind === "turn" && line.node) {
|
|
390
|
-
if (tryAppendL3Node(line.node, dialoguePackCtx, dialogueAcc) === "budget") {
|
|
391
|
-
break;
|
|
392
|
-
}
|
|
393
|
-
if (line.correctionLine) {
|
|
394
|
-
const estimate = (0, context_slicer_utils_js_2.estimateTokens)(line.correctionLine);
|
|
395
|
-
if (dialogueAcc.tokens + estimate > maxTokens) {
|
|
396
|
-
dialogueAcc.truncated = true;
|
|
397
|
-
break;
|
|
398
|
-
}
|
|
399
|
-
dialoguePackCtx.summaryChannel.push(line.correctionLine);
|
|
400
|
-
dialogueAcc.tokens += estimate;
|
|
401
|
-
}
|
|
402
|
-
}
|
|
403
|
-
}
|
|
404
|
-
tokens = dialogueAcc.tokens;
|
|
405
|
-
truncated = dialogueAcc.truncated;
|
|
406
|
-
}
|
|
407
|
-
const enableExpansion = options?.enableEdgeExpansion !== false;
|
|
408
|
-
if (enableExpansion && !truncated && typeof client.getNeighbors === "function") {
|
|
409
|
-
const seedIds = anchorChannel.slice(0, 5).map((a) => a.id);
|
|
410
|
-
if (seedIds.length > 0) {
|
|
411
|
-
const expanded = await expandSubgraph(client, seedIds, { hops: 1 });
|
|
412
|
-
for (const node of expanded) {
|
|
413
|
-
if (added.has(node.id))
|
|
414
|
-
continue;
|
|
415
|
-
const layer = (0, context_slicer_utils_js_2.classifyLayer)(node);
|
|
416
|
-
if (!(0, context_slicer_utils_js_2.canUseLayer)(layer, quota, used))
|
|
417
|
-
continue;
|
|
418
|
-
const summary = `${node.type}: ${node.content}`;
|
|
419
|
-
const estimate = (0, context_slicer_utils_js_2.estimateTokens)(summary);
|
|
420
|
-
if (tokens + estimate > maxTokens) {
|
|
421
|
-
truncated = true;
|
|
422
|
-
break;
|
|
423
|
-
}
|
|
424
|
-
summaryChannel.push(summary);
|
|
425
|
-
anchorChannel.push({ id: node.id, type: node.type, layer });
|
|
426
|
-
added.add(node.id);
|
|
427
|
-
tokens += estimate;
|
|
428
|
-
(0, context_slicer_utils_js_2.markLayerUsed)(layer, used);
|
|
429
|
-
}
|
|
430
|
-
}
|
|
431
|
-
}
|
|
432
|
-
return { summaryChannel, anchorChannel, tokenEstimate: tokens, truncated };
|
|
52
|
+
hits = (0, context_package_core_js_1.preparePackageHits)(hits, query, options, snapshotNodes);
|
|
53
|
+
const state = (0, context_package_core_js_1.createPackState)(maxTokens, options);
|
|
54
|
+
const budget = { tokens: 0, truncated: false };
|
|
55
|
+
(0, context_package_core_js_1.packPrimaryHits)(hits, state, budget);
|
|
56
|
+
await (0, context_package_core_js_1.injectL2Modules)(client, hits, state, budget, "continue");
|
|
57
|
+
await (0, context_package_core_js_1.injectL3SkillsAndPins)(client, query, options, snapshotNodes, state, budget);
|
|
58
|
+
await (0, context_package_core_js_1.injectDialogueTurns)(client, query, options, state, budget);
|
|
59
|
+
await (0, context_package_core_js_1.injectNeighborExpansion)(client, options, state, budget);
|
|
60
|
+
return (0, context_package_core_js_1.toLayeredPackage)(state, budget);
|
|
433
61
|
}
|
|
434
62
|
function createContextRefillManager(client, maxTokens, options) {
|
|
435
63
|
const seenAnchors = new Set();
|
|
@@ -451,7 +79,7 @@ function createContextRefillManager(client, maxTokens, options) {
|
|
|
451
79
|
continue;
|
|
452
80
|
}
|
|
453
81
|
const line = `${hit.id} | ${hit.type}: ${hit.content}`;
|
|
454
|
-
const estimate = (0,
|
|
82
|
+
const estimate = (0, context_slicer_utils_js_1.estimateTokens)(line);
|
|
455
83
|
if (tokens + estimate > maxTokens) {
|
|
456
84
|
return items;
|
|
457
85
|
}
|
|
@@ -474,6 +102,8 @@ function createContextRefillManager(client, maxTokens, options) {
|
|
|
474
102
|
* 3. Graph compression: connected subgraph + PageRank re-ranking
|
|
475
103
|
* 4. Layer quotas + token budgeting
|
|
476
104
|
* 5. Edge expansion
|
|
105
|
+
*
|
|
106
|
+
* Shared recall/pack steps live in `context-package-core` (same as layered).
|
|
477
107
|
*/
|
|
478
108
|
async function buildEnhancedContextPackage(client, query, task, maxTokens, options) {
|
|
479
109
|
// Step 0: Adaptive budget estimation.
|
|
@@ -487,7 +117,7 @@ async function buildEnhancedContextPackage(client, query, task, maxTokens, optio
|
|
|
487
117
|
try {
|
|
488
118
|
const repoMap = await (0, repo_map_js_1.buildRepoMap)(client);
|
|
489
119
|
const mapStr = (0, repo_map_js_1.formatRepoMapString)(repoMap);
|
|
490
|
-
const tokens = (0,
|
|
120
|
+
const tokens = (0, context_slicer_utils_js_1.estimateTokens)(mapStr);
|
|
491
121
|
if (tokens < maxTokens) {
|
|
492
122
|
return {
|
|
493
123
|
summaryChannel: [mapStr],
|
|
@@ -502,7 +132,7 @@ async function buildEnhancedContextPackage(client, query, task, maxTokens, optio
|
|
|
502
132
|
}
|
|
503
133
|
}
|
|
504
134
|
// Step 2: Keyword retrieval (CJK-aware multi-query RRF when workspaceRoot is set).
|
|
505
|
-
const keywordHits = await (0,
|
|
135
|
+
const keywordHits = await (0, context_package_core_js_1.collectKeywordHits)(client, query, options);
|
|
506
136
|
let hits = keywordHits;
|
|
507
137
|
// Step 2b: Symbol-aware boosting — extract camelCase/PascalCase tokens from
|
|
508
138
|
// the task description and fetch matching symbol nodes to improve recall.
|
|
@@ -513,19 +143,9 @@ async function buildEnhancedContextPackage(client, query, task, maxTokens, optio
|
|
|
513
143
|
hits = (0, embeddings_js_1.reciprocalRankFusion)([hits, symbolHits]);
|
|
514
144
|
}
|
|
515
145
|
}
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
const topK = options.vectorTopK ?? 8;
|
|
520
|
-
const minSim = options.vectorMinSimilarity ?? 0.05;
|
|
521
|
-
const vectorCandidates = collectVectorRecallCandidates(client, keywordHits, options.enableFullGraphVectorRecall === true);
|
|
522
|
-
const vectorHits = await hnswVectorRecall(vectorCandidates, queryEmbedding, topK, minSim, options?.hnswIndexPath);
|
|
523
|
-
hits = (0, embeddings_js_1.reciprocalRankFusion)([keywordHits, vectorHits]);
|
|
524
|
-
}
|
|
525
|
-
catch (error) {
|
|
526
|
-
logger_js_1.logger.warn({ error }, "Vector recall failed in buildEnhancedContextPackage");
|
|
527
|
-
hits = keywordHits;
|
|
528
|
-
}
|
|
146
|
+
const fused = await (0, context_package_core_js_1.fuseVectorRecallIfEnabled)(client, query, keywordHits, options, "buildEnhancedContextPackage");
|
|
147
|
+
if (fused) {
|
|
148
|
+
hits = fused;
|
|
529
149
|
}
|
|
530
150
|
if (options?.enableGraphCompression && hits.length > 0) {
|
|
531
151
|
try {
|
|
@@ -544,217 +164,15 @@ async function buildEnhancedContextPackage(client, query, task, maxTokens, optio
|
|
|
544
164
|
}
|
|
545
165
|
}
|
|
546
166
|
const snapshotNodes = client.readSnapshot?.()?.nodes;
|
|
547
|
-
hits = (0,
|
|
548
|
-
query,
|
|
549
|
-
...(options?.englishQuery !== undefined ? { englishQuery: options.englishQuery } : {}),
|
|
550
|
-
...(snapshotNodes !== undefined ? { allNodes: snapshotNodes } : {}),
|
|
551
|
-
});
|
|
552
|
-
// Step 4: Build layered package with anchor cap.
|
|
167
|
+
hits = (0, context_package_core_js_1.preparePackageHits)(hits, query, options, snapshotNodes);
|
|
553
168
|
const maxAnchors = options?.maxAnchors ?? 15;
|
|
554
|
-
const
|
|
555
|
-
const
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
};
|
|
563
|
-
const used = { l1: 0, l2: 0, l3: 0 };
|
|
564
|
-
const added = new Set();
|
|
565
|
-
const hitById = new Map();
|
|
566
|
-
for (const hit of hits)
|
|
567
|
-
hitById.set(hit.id, hit);
|
|
568
|
-
for (const hit of hits) {
|
|
569
|
-
if (anchorChannel.length >= maxAnchors) {
|
|
570
|
-
truncated = true;
|
|
571
|
-
break;
|
|
572
|
-
}
|
|
573
|
-
const layer = (0, context_slicer_utils_js_2.classifyLayer)(hit);
|
|
574
|
-
if (!(0, context_slicer_utils_js_2.canUseLayer)(layer, quota, used))
|
|
575
|
-
continue;
|
|
576
|
-
const summary = `${hit.type}: ${hit.content}`;
|
|
577
|
-
const estimate = (0, context_slicer_utils_js_2.estimateTokens)(summary);
|
|
578
|
-
if (tokens + estimate > maxTokens) {
|
|
579
|
-
truncated = true;
|
|
580
|
-
break;
|
|
581
|
-
}
|
|
582
|
-
summaryChannel.push(summary);
|
|
583
|
-
anchorChannel.push({ id: hit.id, type: hit.type, layer });
|
|
584
|
-
added.add(hit.id);
|
|
585
|
-
tokens += estimate;
|
|
586
|
-
(0, context_slicer_utils_js_2.markLayerUsed)(layer, used);
|
|
587
|
-
}
|
|
588
|
-
// Step 4b: L2 module injection — aggregate file/symbol hits into module overviews.
|
|
589
|
-
if (!truncated && anchorChannel.length < maxAnchors) {
|
|
590
|
-
const moduleIds = new Set();
|
|
591
|
-
for (const anchor of anchorChannel) {
|
|
592
|
-
if (anchor.layer !== "L1" || (anchor.type !== "File" && anchor.type !== "Symbol"))
|
|
593
|
-
continue;
|
|
594
|
-
const moduleId = (0, context_slicer_utils_js_2.deriveModuleId)(anchor, hitById.get(anchor.id));
|
|
595
|
-
if (moduleId)
|
|
596
|
-
moduleIds.add(moduleId);
|
|
597
|
-
}
|
|
598
|
-
let moduleNodes = [];
|
|
599
|
-
if (moduleIds.size > 0 && typeof client.getNodesByIds === "function") {
|
|
600
|
-
moduleNodes = await client.getNodesByIds(Array.from(moduleIds));
|
|
601
|
-
}
|
|
602
|
-
for (const moduleId of moduleIds) {
|
|
603
|
-
if (added.has(moduleId) || anchorChannel.length >= maxAnchors)
|
|
604
|
-
break;
|
|
605
|
-
if (!(0, context_slicer_utils_js_2.canUseLayer)("L2", quota, used))
|
|
606
|
-
break;
|
|
607
|
-
const node = moduleNodes.find((n) => n.id === moduleId) ??
|
|
608
|
-
({ id: moduleId, type: "Module", content: moduleId.slice("module:".length) });
|
|
609
|
-
const summary = `${node.type}: ${node.content}`;
|
|
610
|
-
const estimate = (0, context_slicer_utils_js_2.estimateTokens)(summary);
|
|
611
|
-
if (tokens + estimate > maxTokens) {
|
|
612
|
-
truncated = true;
|
|
613
|
-
break;
|
|
614
|
-
}
|
|
615
|
-
summaryChannel.push(summary);
|
|
616
|
-
anchorChannel.push({ id: node.id, type: node.type, layer: "L2" });
|
|
617
|
-
added.add(node.id);
|
|
618
|
-
tokens += estimate;
|
|
619
|
-
(0, context_slicer_utils_js_2.markLayerUsed)("L2", used);
|
|
620
|
-
}
|
|
621
|
-
}
|
|
622
|
-
// Step 4c: L3 skill/decision injection — always-on when enableAlwaysOnLayers,
|
|
623
|
-
// otherwise only for architecture queries. Pin goal/alignment/deviation
|
|
624
|
-
// Decision nodes first so budget truncation cannot drop them.
|
|
625
|
-
const archIntent = context_slicer_types_js_2.ARCHITECTURE_QUERY.test((0, graph_utils_js_1.composeContextQuery)(query, options?.englishQuery));
|
|
626
|
-
if (anchorChannel.length < maxAnchors && (options?.enableAlwaysOnLayers || archIntent)) {
|
|
627
|
-
const l3Candidates = (0, graph_utils_js_1.rankNodesForContextQuery)((await client.queryByKeyword(query)).filter((n) => n.type === "Skill" || n.type === "Decision"), query, {
|
|
628
|
-
scoreTokens: (0, graph_utils_js_1.buildSearchScoreTokens)(query, options?.englishQuery),
|
|
629
|
-
matchQueries: options?.englishQuery?.trim()
|
|
630
|
-
? [query, options.englishQuery.trim()]
|
|
631
|
-
: [query],
|
|
632
|
-
...(options?.englishQuery !== undefined ? { englishQuery: options.englishQuery } : {}),
|
|
633
|
-
});
|
|
634
|
-
const acc = { tokens, truncated };
|
|
635
|
-
const packCtx = {
|
|
636
|
-
summaryChannel,
|
|
637
|
-
anchorChannel,
|
|
638
|
-
added,
|
|
639
|
-
quota,
|
|
640
|
-
used,
|
|
641
|
-
maxTokens,
|
|
642
|
-
maxAnchors,
|
|
643
|
-
};
|
|
644
|
-
const pins = collectPinnedL3Nodes(l3Candidates, snapshotNodes, added);
|
|
645
|
-
for (const node of pins) {
|
|
646
|
-
if (tryAppendL3Node(node, packCtx, acc) === "budget")
|
|
647
|
-
break;
|
|
648
|
-
}
|
|
649
|
-
tokens = acc.tokens;
|
|
650
|
-
truncated = acc.truncated;
|
|
651
|
-
if (!truncated) {
|
|
652
|
-
let l3Added = pins.filter((n) => added.has(n.id)).length;
|
|
653
|
-
for (const node of l3Candidates) {
|
|
654
|
-
if (l3Added >= 2 || anchorChannel.length >= maxAnchors)
|
|
655
|
-
break;
|
|
656
|
-
if (isPinnedL3Node(node))
|
|
657
|
-
continue;
|
|
658
|
-
const result = tryAppendL3Node(node, packCtx, acc);
|
|
659
|
-
if (result === "budget")
|
|
660
|
-
break;
|
|
661
|
-
if (result === "packed")
|
|
662
|
-
l3Added += 1;
|
|
663
|
-
}
|
|
664
|
-
tokens = acc.tokens;
|
|
665
|
-
truncated = acc.truncated;
|
|
666
|
-
}
|
|
667
|
-
}
|
|
668
|
-
// Step 5: Import-based multi-hop expansion + same-file symbol boosting.
|
|
669
|
-
// Find files that import the anchored files, AND add symbols from anchored
|
|
670
|
-
// files to improve symbol-level recall.
|
|
671
|
-
if (!truncated && anchorChannel.length < maxAnchors) {
|
|
672
|
-
const seedFilePaths = [];
|
|
673
|
-
for (const anchor of anchorChannel) {
|
|
674
|
-
if (anchor.type === "File" && anchor.id.startsWith("file:")) {
|
|
675
|
-
seedFilePaths.push(anchor.id.slice("file:".length));
|
|
676
|
-
}
|
|
677
|
-
else if (anchor.type === "Symbol") {
|
|
678
|
-
const fp = (0, context_slicer_utils_js_2.extractFileFromSymbolId)(anchor.id);
|
|
679
|
-
if (fp)
|
|
680
|
-
seedFilePaths.push(fp);
|
|
681
|
-
}
|
|
682
|
-
}
|
|
683
|
-
// 5a: Same-file symbol boosting — add symbols from anchored files
|
|
684
|
-
if (seedFilePaths.length > 0 && snapshotNodes) {
|
|
685
|
-
for (const node of snapshotNodes) {
|
|
686
|
-
if (added.has(node.id) || anchorChannel.length >= maxAnchors)
|
|
687
|
-
continue;
|
|
688
|
-
if (node.type !== "Symbol")
|
|
689
|
-
continue;
|
|
690
|
-
const nodeFile = (0, context_slicer_utils_js_2.extractFileFromSymbolId)(node.id);
|
|
691
|
-
if (!nodeFile)
|
|
692
|
-
continue;
|
|
693
|
-
const inSeedFile = seedFilePaths.some(sp => nodeFile.includes(sp.replace(/\.(ts|tsx|js|jsx|py|go|rs)$/, "")) ||
|
|
694
|
-
sp.replace(/\.(ts|tsx|js|jsx|py|go|rs)$/, "").includes(nodeFile.replace(/\.(ts|tsx|js|jsx|py|go|rs)$/, "")));
|
|
695
|
-
if (!inSeedFile)
|
|
696
|
-
continue;
|
|
697
|
-
const layer = (0, context_slicer_utils_js_2.classifyLayer)(node);
|
|
698
|
-
const summary = `${node.type}: ${node.content}`;
|
|
699
|
-
const estimate = (0, context_slicer_utils_js_2.estimateTokens)(summary);
|
|
700
|
-
if (tokens + estimate > maxTokens) {
|
|
701
|
-
truncated = true;
|
|
702
|
-
break;
|
|
703
|
-
}
|
|
704
|
-
summaryChannel.push(summary);
|
|
705
|
-
anchorChannel.push({ id: node.id, type: node.type, layer });
|
|
706
|
-
added.add(node.id);
|
|
707
|
-
tokens += estimate;
|
|
708
|
-
(0, context_slicer_utils_js_2.markLayerUsed)(layer, used);
|
|
709
|
-
}
|
|
710
|
-
}
|
|
711
|
-
// 5b: Import-based expansion
|
|
712
|
-
if (seedFilePaths.length > 0 && anchorChannel.length < maxAnchors) {
|
|
713
|
-
const importHits = await (0, symbol_extract_js_1.collectImportRelatedFiles)(client, seedFilePaths, 5);
|
|
714
|
-
for (const node of importHits) {
|
|
715
|
-
if (added.has(node.id) || anchorChannel.length >= maxAnchors)
|
|
716
|
-
continue;
|
|
717
|
-
const layer = (0, context_slicer_utils_js_2.classifyLayer)(node);
|
|
718
|
-
const summary = `${node.type}: ${node.content}`;
|
|
719
|
-
const estimate = (0, context_slicer_utils_js_2.estimateTokens)(summary);
|
|
720
|
-
if (tokens + estimate > maxTokens) {
|
|
721
|
-
truncated = true;
|
|
722
|
-
break;
|
|
723
|
-
}
|
|
724
|
-
summaryChannel.push(summary);
|
|
725
|
-
anchorChannel.push({ id: node.id, type: node.type, layer });
|
|
726
|
-
added.add(node.id);
|
|
727
|
-
tokens += estimate;
|
|
728
|
-
(0, context_slicer_utils_js_2.markLayerUsed)(layer, used);
|
|
729
|
-
}
|
|
730
|
-
}
|
|
731
|
-
}
|
|
732
|
-
// Step 6: Edge expansion (respects anchor cap).
|
|
733
|
-
const enableExpansion = options?.enableEdgeExpansion !== false;
|
|
734
|
-
if (enableExpansion && !truncated && anchorChannel.length < maxAnchors && typeof client.getNeighbors === "function") {
|
|
735
|
-
const seedIds = anchorChannel.slice(0, 5).map((a) => a.id);
|
|
736
|
-
if (seedIds.length > 0) {
|
|
737
|
-
const expanded = await expandSubgraph(client, seedIds, { hops: 1 });
|
|
738
|
-
for (const node of expanded) {
|
|
739
|
-
if (added.has(node.id) || anchorChannel.length >= maxAnchors)
|
|
740
|
-
continue;
|
|
741
|
-
const layer = (0, context_slicer_utils_js_2.classifyLayer)(node);
|
|
742
|
-
if (!(0, context_slicer_utils_js_2.canUseLayer)(layer, quota, used))
|
|
743
|
-
continue;
|
|
744
|
-
const summary = `${node.type}: ${node.content}`;
|
|
745
|
-
const estimate = (0, context_slicer_utils_js_2.estimateTokens)(summary);
|
|
746
|
-
if (tokens + estimate > maxTokens) {
|
|
747
|
-
truncated = true;
|
|
748
|
-
break;
|
|
749
|
-
}
|
|
750
|
-
summaryChannel.push(summary);
|
|
751
|
-
anchorChannel.push({ id: node.id, type: node.type, layer });
|
|
752
|
-
added.add(node.id);
|
|
753
|
-
tokens += estimate;
|
|
754
|
-
(0, context_slicer_utils_js_2.markLayerUsed)(layer, used);
|
|
755
|
-
}
|
|
756
|
-
}
|
|
757
|
-
}
|
|
758
|
-
return { summaryChannel, anchorChannel, tokenEstimate: tokens, truncated };
|
|
169
|
+
const state = (0, context_package_core_js_1.createPackState)(maxTokens, options, maxAnchors);
|
|
170
|
+
const budget = { tokens: 0, truncated: false };
|
|
171
|
+
(0, context_package_core_js_1.packPrimaryHits)(hits, state, budget);
|
|
172
|
+
await (0, context_package_core_js_1.injectL2Modules)(client, hits, state, budget, "break");
|
|
173
|
+
await (0, context_package_core_js_1.injectL3SkillsAndPins)(client, query, options, snapshotNodes, state, budget);
|
|
174
|
+
await (0, context_package_core_js_1.injectSameFileAndImportExpansion)(client, snapshotNodes, state, budget);
|
|
175
|
+
await (0, context_package_core_js_1.injectNeighborExpansion)(client, options, state, budget);
|
|
176
|
+
return (0, context_package_core_js_1.toLayeredPackage)(state, budget);
|
|
759
177
|
}
|
|
760
178
|
//# sourceMappingURL=context-slicer.js.map
|