open-agents-ai 0.33.0 → 0.34.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +203 -18
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -10083,6 +10083,55 @@ var init_lexicalSearch = __esm({
|
|
|
10083
10083
|
});
|
|
10084
10084
|
|
|
10085
10085
|
// packages/retrieval/dist/semanticSearch.js
|
|
10086
|
+
function parseTagFilter(query) {
|
|
10087
|
+
const required = [];
|
|
10088
|
+
const anyOf = [];
|
|
10089
|
+
const excluded = [];
|
|
10090
|
+
const cleanParts = [];
|
|
10091
|
+
for (const token of query.split(/\s+/)) {
|
|
10092
|
+
if (token.startsWith("+") && token.length > 1) {
|
|
10093
|
+
required.push(token.slice(1).toLowerCase());
|
|
10094
|
+
} else if (token.startsWith("~") && token.length > 1) {
|
|
10095
|
+
anyOf.push(token.slice(1).toLowerCase());
|
|
10096
|
+
} else if (token.startsWith("-") && token.length > 1 && !/^\d/.test(token.slice(1))) {
|
|
10097
|
+
excluded.push(token.slice(1).toLowerCase());
|
|
10098
|
+
} else {
|
|
10099
|
+
cleanParts.push(token);
|
|
10100
|
+
}
|
|
10101
|
+
}
|
|
10102
|
+
const hasFilter = required.length > 0 || anyOf.length > 0 || excluded.length > 0;
|
|
10103
|
+
return {
|
|
10104
|
+
cleanQuery: cleanParts.join(" "),
|
|
10105
|
+
tagFilter: hasFilter ? { required, anyOf, excluded } : null
|
|
10106
|
+
};
|
|
10107
|
+
}
|
|
10108
|
+
function matchesTagFilter(text, filter) {
|
|
10109
|
+
const lowerText = text.toLowerCase();
|
|
10110
|
+
const clusteringTags = /* @__PURE__ */ new Set();
|
|
10111
|
+
for (const match of lowerText.matchAll(/clustering:(\S+)/g)) {
|
|
10112
|
+
clusteringTags.add(match[1]);
|
|
10113
|
+
}
|
|
10114
|
+
const domainMatch = lowerText.match(/domain:\s*(\S+)/);
|
|
10115
|
+
if (domainMatch)
|
|
10116
|
+
clusteringTags.add(domainMatch[1]);
|
|
10117
|
+
const riskMatch = lowerText.match(/risk:\s*(\S+)/);
|
|
10118
|
+
if (riskMatch)
|
|
10119
|
+
clusteringTags.add(riskMatch[1]);
|
|
10120
|
+
for (const tag of filter.required) {
|
|
10121
|
+
if (!clusteringTags.has(tag) && !lowerText.includes(tag))
|
|
10122
|
+
return false;
|
|
10123
|
+
}
|
|
10124
|
+
if (filter.anyOf.length > 0) {
|
|
10125
|
+
const hasAny = filter.anyOf.some((tag) => clusteringTags.has(tag) || lowerText.includes(tag));
|
|
10126
|
+
if (!hasAny)
|
|
10127
|
+
return false;
|
|
10128
|
+
}
|
|
10129
|
+
for (const tag of filter.excluded) {
|
|
10130
|
+
if (clusteringTags.has(tag))
|
|
10131
|
+
return false;
|
|
10132
|
+
}
|
|
10133
|
+
return true;
|
|
10134
|
+
}
|
|
10086
10135
|
function cosineSimilarity(a, b) {
|
|
10087
10136
|
if (a.length !== b.length || a.length === 0)
|
|
10088
10137
|
return 0;
|
|
@@ -10108,7 +10157,7 @@ function makePlaceholderSummary(filePath) {
|
|
|
10108
10157
|
lastIndexed: (/* @__PURE__ */ new Date()).toISOString()
|
|
10109
10158
|
};
|
|
10110
10159
|
}
|
|
10111
|
-
var StubSemanticSearchEngine, IndexBackedSemanticSearchEngine;
|
|
10160
|
+
var StubSemanticSearchEngine, IndexBackedSemanticSearchEngine, LazySemanticSearchEngine;
|
|
10112
10161
|
var init_semanticSearch = __esm({
|
|
10113
10162
|
"packages/retrieval/dist/semanticSearch.js"() {
|
|
10114
10163
|
"use strict";
|
|
@@ -10129,11 +10178,26 @@ var init_semanticSearch = __esm({
|
|
|
10129
10178
|
if (!this.isAvailable)
|
|
10130
10179
|
return [];
|
|
10131
10180
|
const queryVector = await this.options.embedQuery(query);
|
|
10181
|
+
const { cleanQuery, tagFilter } = parseTagFilter(query);
|
|
10182
|
+
const searchVector = cleanQuery !== query ? await this.options.embedQuery(cleanQuery) : queryVector;
|
|
10132
10183
|
const scored = this.options.index.map((item) => ({
|
|
10133
10184
|
filePath: item.filePath,
|
|
10134
|
-
score: cosineSimilarity(
|
|
10185
|
+
score: cosineSimilarity(searchVector, item.vector),
|
|
10186
|
+
text: item.text
|
|
10135
10187
|
}));
|
|
10136
|
-
|
|
10188
|
+
let filtered = scored;
|
|
10189
|
+
if (tagFilter) {
|
|
10190
|
+
filtered = scored.filter((item) => matchesTagFilter(item.text, tagFilter));
|
|
10191
|
+
}
|
|
10192
|
+
const byFile = /* @__PURE__ */ new Map();
|
|
10193
|
+
for (const item of filtered) {
|
|
10194
|
+
const existing = byFile.get(item.filePath);
|
|
10195
|
+
if (!existing || item.score > existing.score) {
|
|
10196
|
+
byFile.set(item.filePath, item);
|
|
10197
|
+
}
|
|
10198
|
+
}
|
|
10199
|
+
const deduped = Array.from(byFile.values());
|
|
10200
|
+
return deduped.sort((a, b) => b.score - a.score).slice(0, topK).map(({ filePath, score }) => {
|
|
10137
10201
|
const summary = this.options.summaryMap.get(filePath);
|
|
10138
10202
|
return {
|
|
10139
10203
|
filePath,
|
|
@@ -10143,6 +10207,74 @@ var init_semanticSearch = __esm({
|
|
|
10143
10207
|
};
|
|
10144
10208
|
});
|
|
10145
10209
|
}
|
|
10210
|
+
/**
|
|
10211
|
+
* Pattern 7: Find semantically related items in the index.
|
|
10212
|
+
* Returns pairs of items with >threshold similarity.
|
|
10213
|
+
* Useful for auto-linking related memories/files.
|
|
10214
|
+
*/
|
|
10215
|
+
findRelated(filePath, threshold = 0.7, maxResults = 5) {
|
|
10216
|
+
const sourceItem = this.options.index.find((i) => i.filePath === filePath);
|
|
10217
|
+
if (!sourceItem)
|
|
10218
|
+
return [];
|
|
10219
|
+
const scored = this.options.index.filter((i) => i.filePath !== filePath).map((item) => ({
|
|
10220
|
+
filePath: item.filePath,
|
|
10221
|
+
score: cosineSimilarity(sourceItem.vector, item.vector)
|
|
10222
|
+
})).filter((r) => r.score >= threshold).sort((a, b) => b.score - a.score).slice(0, maxResults);
|
|
10223
|
+
return scored;
|
|
10224
|
+
}
|
|
10225
|
+
};
|
|
10226
|
+
LazySemanticSearchEngine = class {
|
|
10227
|
+
inner = null;
|
|
10228
|
+
buildPromise = null;
|
|
10229
|
+
buildFailed = false;
|
|
10230
|
+
opts;
|
|
10231
|
+
constructor(opts) {
|
|
10232
|
+
this.opts = opts;
|
|
10233
|
+
}
|
|
10234
|
+
get isAvailable() {
|
|
10235
|
+
return this.inner?.isAvailable ?? false;
|
|
10236
|
+
}
|
|
10237
|
+
/**
|
|
10238
|
+
* Trigger index building in the background.
|
|
10239
|
+
* Call this during idle time to pre-warm the index.
|
|
10240
|
+
*/
|
|
10241
|
+
warmUp() {
|
|
10242
|
+
if (!this.buildPromise && !this.inner && !this.buildFailed) {
|
|
10243
|
+
this.buildPromise = this.build();
|
|
10244
|
+
}
|
|
10245
|
+
}
|
|
10246
|
+
async search(query, topK = 10) {
|
|
10247
|
+
if (this.inner)
|
|
10248
|
+
return this.inner.search(query, topK);
|
|
10249
|
+
if (this.buildFailed)
|
|
10250
|
+
return [];
|
|
10251
|
+
if (!this.buildPromise) {
|
|
10252
|
+
this.buildPromise = this.build();
|
|
10253
|
+
}
|
|
10254
|
+
const timeoutMs = this.opts.timeoutMs ?? 6e4;
|
|
10255
|
+
try {
|
|
10256
|
+
await Promise.race([
|
|
10257
|
+
this.buildPromise,
|
|
10258
|
+
new Promise((_, reject) => setTimeout(() => reject(new Error("Index build timeout")), timeoutMs))
|
|
10259
|
+
]);
|
|
10260
|
+
} catch {
|
|
10261
|
+
return [];
|
|
10262
|
+
}
|
|
10263
|
+
return this.searchInner(query, topK);
|
|
10264
|
+
}
|
|
10265
|
+
searchInner(query, topK) {
|
|
10266
|
+
if (this.inner)
|
|
10267
|
+
return this.inner.search(query, topK);
|
|
10268
|
+
return Promise.resolve([]);
|
|
10269
|
+
}
|
|
10270
|
+
async build() {
|
|
10271
|
+
try {
|
|
10272
|
+
const options = await this.opts.buildIndex();
|
|
10273
|
+
this.inner = new IndexBackedSemanticSearchEngine(options);
|
|
10274
|
+
} catch {
|
|
10275
|
+
this.buildFailed = true;
|
|
10276
|
+
}
|
|
10277
|
+
}
|
|
10146
10278
|
};
|
|
10147
10279
|
}
|
|
10148
10280
|
});
|
|
@@ -10299,24 +10431,41 @@ async function assembleContext(request, opts) {
|
|
|
10299
10431
|
Promise.all(request.errorHint.slice(0, maxLogs).map((e) => searchByError(e, { rootDir: repoRoot, maxMatches: 5 }))).then((res) => res.flat()),
|
|
10300
10432
|
semanticEngine?.isAvailable ? semanticEngine.search(request.query, maxFiles) : Promise.resolve([])
|
|
10301
10433
|
]);
|
|
10302
|
-
const
|
|
10303
|
-
const
|
|
10304
|
-
|
|
10305
|
-
|
|
10306
|
-
|
|
10307
|
-
seenFiles.add(relativePath);
|
|
10308
|
-
candidateFiles.push({ relativePath, priority });
|
|
10309
|
-
}
|
|
10434
|
+
const queryType = classifyQuery(request.query);
|
|
10435
|
+
const rrfK = adaptiveK(queryType, opts.rrfConfig?.k);
|
|
10436
|
+
const wFts = opts.rrfConfig?.weightFts ?? 1;
|
|
10437
|
+
const wSem = opts.rrfConfig?.weightSemantic ?? 1;
|
|
10438
|
+
const lexicalCandidates = [];
|
|
10310
10439
|
for (const m of pathMatches)
|
|
10311
|
-
|
|
10440
|
+
lexicalCandidates.push({ relativePath: m.relativePath, basePriority: 100 });
|
|
10312
10441
|
for (const m of symbolMatches)
|
|
10313
|
-
|
|
10314
|
-
for (const r of semanticResults) {
|
|
10315
|
-
addCandidate(r.filePath, Math.round(50 + r.score * 30));
|
|
10316
|
-
}
|
|
10442
|
+
lexicalCandidates.push({ relativePath: m.relativePath, basePriority: 80 });
|
|
10317
10443
|
for (const m of errorMatches)
|
|
10318
|
-
|
|
10319
|
-
const
|
|
10444
|
+
lexicalCandidates.push({ relativePath: m.relativePath, basePriority: 40 });
|
|
10445
|
+
const lexDedup = /* @__PURE__ */ new Map();
|
|
10446
|
+
for (const c3 of lexicalCandidates) {
|
|
10447
|
+
const existing = lexDedup.get(c3.relativePath) ?? 0;
|
|
10448
|
+
lexDedup.set(c3.relativePath, Math.max(existing, c3.basePriority));
|
|
10449
|
+
}
|
|
10450
|
+
const lexRanked = Array.from(lexDedup.entries()).sort((a, b) => b[1] - a[1]).map(([path], idx) => ({ path, rank: idx + 1 }));
|
|
10451
|
+
const semRanked = semanticResults.sort((a, b) => b.score - a.score).map((r, idx) => ({ path: r.filePath, rank: idx + 1, score: r.score }));
|
|
10452
|
+
const rrfScores = /* @__PURE__ */ new Map();
|
|
10453
|
+
const lexRankMap = new Map(lexRanked.map((l) => [l.path, l.rank]));
|
|
10454
|
+
const semRankMap = new Map(semRanked.map((s) => [s.path, s.rank]));
|
|
10455
|
+
const allPaths = /* @__PURE__ */ new Set([...lexRankMap.keys(), ...semRankMap.keys()]);
|
|
10456
|
+
for (const p of allPaths) {
|
|
10457
|
+
const lexRank = lexRankMap.get(p);
|
|
10458
|
+
const semRank = semRankMap.get(p);
|
|
10459
|
+
let score = 0;
|
|
10460
|
+
if (lexRank !== void 0)
|
|
10461
|
+
score += wFts / (rrfK + lexRank);
|
|
10462
|
+
if (semRank !== void 0)
|
|
10463
|
+
score += wSem / (rrfK + semRank);
|
|
10464
|
+
rrfScores.set(p, score);
|
|
10465
|
+
}
|
|
10466
|
+
const candidateFiles = Array.from(rrfScores.entries()).sort((a, b) => b[1] - a[1]).map(([relativePath, priority]) => ({ relativePath, priority: Math.round(priority * 1e3) }));
|
|
10467
|
+
const seenFiles = new Set(candidateFiles.map((c3) => c3.relativePath));
|
|
10468
|
+
const topFiles = candidateFiles.slice(0, maxFiles);
|
|
10320
10469
|
const neighborFiles = [];
|
|
10321
10470
|
if (expandNeighbors && graph) {
|
|
10322
10471
|
const seedPaths = topFiles.map((f) => f.relativePath);
|
|
@@ -10361,6 +10510,39 @@ async function assembleContext(request, opts) {
|
|
|
10361
10510
|
assembledAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
10362
10511
|
};
|
|
10363
10512
|
}
|
|
10513
|
+
function classifyQuery(query) {
|
|
10514
|
+
const trimmed = query.trim();
|
|
10515
|
+
if (/^["'].*["']$/.test(trimmed) || /"[^"]+"/.test(trimmed))
|
|
10516
|
+
return "quoted";
|
|
10517
|
+
if (/(?:error|exception|stack|traceback|ENOENT|EPERM|TypeError|SyntaxError)/i.test(trimmed))
|
|
10518
|
+
return "error";
|
|
10519
|
+
if (/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(trimmed))
|
|
10520
|
+
return "symbol";
|
|
10521
|
+
const wordCount = trimmed.split(/\s+/).length;
|
|
10522
|
+
if (wordCount <= 3)
|
|
10523
|
+
return "short";
|
|
10524
|
+
return "long";
|
|
10525
|
+
}
|
|
10526
|
+
function adaptiveK(queryType, overrideK) {
|
|
10527
|
+
if (overrideK !== void 0)
|
|
10528
|
+
return overrideK;
|
|
10529
|
+
switch (queryType) {
|
|
10530
|
+
case "short":
|
|
10531
|
+
return 30;
|
|
10532
|
+
// tight — top results matter more
|
|
10533
|
+
case "quoted":
|
|
10534
|
+
return 20;
|
|
10535
|
+
// very tight — exact match should win
|
|
10536
|
+
case "symbol":
|
|
10537
|
+
return 25;
|
|
10538
|
+
// tight — symbol search is precise
|
|
10539
|
+
case "error":
|
|
10540
|
+
return 40;
|
|
10541
|
+
// moderate — errors need breadth
|
|
10542
|
+
case "long":
|
|
10543
|
+
return 60;
|
|
10544
|
+
}
|
|
10545
|
+
}
|
|
10364
10546
|
function buildSymbolSnippets(symbolMatches, maxSnippets) {
|
|
10365
10547
|
const byFile = /* @__PURE__ */ new Map();
|
|
10366
10548
|
for (const m of symbolMatches) {
|
|
@@ -10403,8 +10585,10 @@ __export(dist_exports, {
|
|
|
10403
10585
|
CodeRetriever: () => CodeRetriever,
|
|
10404
10586
|
GrepSearch: () => GrepSearch,
|
|
10405
10587
|
IndexBackedSemanticSearchEngine: () => IndexBackedSemanticSearchEngine,
|
|
10588
|
+
LazySemanticSearchEngine: () => LazySemanticSearchEngine,
|
|
10406
10589
|
StubSemanticSearchEngine: () => StubSemanticSearchEngine,
|
|
10407
10590
|
assembleContext: () => assembleContext,
|
|
10591
|
+
classifyQuery: () => classifyQuery,
|
|
10408
10592
|
estimatePacketTokens: () => estimatePacketTokens,
|
|
10409
10593
|
estimateTokens: () => estimateTokens,
|
|
10410
10594
|
expandGraph: () => expandGraph,
|
|
@@ -10412,6 +10596,7 @@ __export(dist_exports, {
|
|
|
10412
10596
|
oneHopNeighbors: () => oneHopNeighbors,
|
|
10413
10597
|
packFiles: () => packFiles,
|
|
10414
10598
|
packSnippets: () => packSnippets,
|
|
10599
|
+
parseTagFilter: () => parseTagFilter,
|
|
10415
10600
|
searchByError: () => searchByError,
|
|
10416
10601
|
searchByPath: () => searchByPath,
|
|
10417
10602
|
searchByQuery: () => searchByQuery,
|
package/package.json
CHANGED