@usewhisper/sdk 3.6.0 → 3.8.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/README.md +51 -49
- package/index.d.mts +85 -2
- package/index.d.ts +85 -2
- package/index.js +389 -64
- package/index.mjs +389 -64
- package/package.json +2 -2
package/index.js
CHANGED
|
@@ -1402,6 +1402,29 @@ ${lines.join("\n")}`;
|
|
|
1402
1402
|
function compactWhitespace(value) {
|
|
1403
1403
|
return value.replace(/\s+/g, " ").trim();
|
|
1404
1404
|
}
|
|
1405
|
+
function normalizeSummary(value) {
|
|
1406
|
+
return compactWhitespace(String(value || "").toLowerCase());
|
|
1407
|
+
}
|
|
1408
|
+
function tokenize(value) {
|
|
1409
|
+
return normalizeSummary(value).split(/[^a-z0-9_./-]+/i).map((token) => token.trim()).filter(Boolean);
|
|
1410
|
+
}
|
|
1411
|
+
function jaccardOverlap(left, right) {
|
|
1412
|
+
const leftTokens = new Set(tokenize(left));
|
|
1413
|
+
const rightTokens = new Set(tokenize(right));
|
|
1414
|
+
if (leftTokens.size === 0 || rightTokens.size === 0) return 0;
|
|
1415
|
+
let intersection = 0;
|
|
1416
|
+
for (const token of leftTokens) {
|
|
1417
|
+
if (rightTokens.has(token)) intersection += 1;
|
|
1418
|
+
}
|
|
1419
|
+
const union = (/* @__PURE__ */ new Set([...leftTokens, ...rightTokens])).size;
|
|
1420
|
+
return union > 0 ? intersection / union : 0;
|
|
1421
|
+
}
|
|
1422
|
+
function clamp01(value) {
|
|
1423
|
+
if (!Number.isFinite(value)) return 0;
|
|
1424
|
+
if (value < 0) return 0;
|
|
1425
|
+
if (value > 1) return 1;
|
|
1426
|
+
return value;
|
|
1427
|
+
}
|
|
1405
1428
|
function withTimeout(promise, timeoutMs) {
|
|
1406
1429
|
return new Promise((resolve, reject) => {
|
|
1407
1430
|
const timeout = setTimeout(() => {
|
|
@@ -1435,39 +1458,76 @@ function extractTimestamp(metadata) {
|
|
|
1435
1458
|
}
|
|
1436
1459
|
return 0;
|
|
1437
1460
|
}
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1461
|
+
var DEFAULT_RANK_WEIGHTS = {
|
|
1462
|
+
focusedPassBonus: 0.2,
|
|
1463
|
+
sourceMatchBonus: 0.18,
|
|
1464
|
+
touchedFileBonus: 0.12,
|
|
1465
|
+
clientMatchBonus: 0.1,
|
|
1466
|
+
highSalienceBonus: 0.12,
|
|
1467
|
+
mediumSalienceBonus: 0.06,
|
|
1468
|
+
staleBroadPenalty: -0.1,
|
|
1469
|
+
unrelatedClientPenalty: -0.18,
|
|
1470
|
+
lowSaliencePenalty: -0.12
|
|
1471
|
+
};
|
|
1472
|
+
var DEFAULT_SOURCE_ACTIVITY = {
|
|
1473
|
+
maxTurns: 10,
|
|
1474
|
+
maxIdleMs: 30 * 60 * 1e3,
|
|
1475
|
+
decayAfterTurns: 5,
|
|
1476
|
+
decayAfterIdleMs: 15 * 60 * 1e3,
|
|
1477
|
+
evictOnTaskSwitch: true
|
|
1478
|
+
};
|
|
1444
1479
|
var WhisperAgentRuntime = class {
|
|
1445
1480
|
constructor(args) {
|
|
1446
1481
|
this.args = args;
|
|
1447
1482
|
this.bindingStore = createBindingStore(args.options.bindingStorePath);
|
|
1448
|
-
|
|
1483
|
+
const retrieval = args.options.retrieval || {};
|
|
1484
|
+
this.focusedTopK = retrieval.focusedTopK ?? args.options.topK ?? 6;
|
|
1485
|
+
this.broadTopK = retrieval.broadTopK ?? Math.max(args.options.topK ?? 6, 10);
|
|
1449
1486
|
this.maxTokens = args.options.maxTokens ?? 4e3;
|
|
1450
1487
|
this.targetRetrievalMs = args.options.targetRetrievalMs ?? 2500;
|
|
1451
1488
|
this.hardRetrievalTimeoutMs = args.options.hardRetrievalTimeoutMs ?? 4e3;
|
|
1452
1489
|
this.recentWorkLimit = args.options.recentWorkLimit ?? 40;
|
|
1453
1490
|
this.baseContext = args.baseContext;
|
|
1454
1491
|
this.clientName = args.baseContext.clientName || "whisper-agent-runtime";
|
|
1492
|
+
this.minFocusedResults = retrieval.minFocusedResults ?? 3;
|
|
1493
|
+
this.minFocusedTopScore = retrieval.minFocusedTopScore ?? 0.55;
|
|
1494
|
+
this.minProjectScore = retrieval.minProjectScore ?? 0.5;
|
|
1495
|
+
this.minMemoryScore = retrieval.minMemoryScore ?? 0.6;
|
|
1496
|
+
this.rankWeights = { ...DEFAULT_RANK_WEIGHTS, ...retrieval.rankWeights || {} };
|
|
1497
|
+
this.sourceActivityOptions = { ...DEFAULT_SOURCE_ACTIVITY, ...retrieval.sourceActivity || {} };
|
|
1455
1498
|
}
|
|
1456
1499
|
bindingStore;
|
|
1457
|
-
|
|
1500
|
+
focusedTopK;
|
|
1501
|
+
broadTopK;
|
|
1458
1502
|
maxTokens;
|
|
1459
1503
|
targetRetrievalMs;
|
|
1460
1504
|
hardRetrievalTimeoutMs;
|
|
1461
1505
|
recentWorkLimit;
|
|
1462
1506
|
baseContext;
|
|
1463
1507
|
clientName;
|
|
1508
|
+
minFocusedResults;
|
|
1509
|
+
minFocusedTopScore;
|
|
1510
|
+
minProjectScore;
|
|
1511
|
+
minMemoryScore;
|
|
1512
|
+
rankWeights;
|
|
1513
|
+
sourceActivityOptions;
|
|
1464
1514
|
bindings = null;
|
|
1465
1515
|
touchedFiles = [];
|
|
1466
1516
|
recentWork = [];
|
|
1517
|
+
recentSourceActivity = [];
|
|
1467
1518
|
bufferedLowSalience = [];
|
|
1468
1519
|
lastPreparedTurn = null;
|
|
1469
1520
|
mergedCount = 0;
|
|
1470
1521
|
droppedCount = 0;
|
|
1522
|
+
focusedPassHits = 0;
|
|
1523
|
+
fallbackTriggers = 0;
|
|
1524
|
+
floorDroppedCount = 0;
|
|
1525
|
+
injectedItemCount = 0;
|
|
1526
|
+
sourceScopedTurns = 0;
|
|
1527
|
+
broadScopedTurns = 0;
|
|
1528
|
+
totalTurns = 0;
|
|
1529
|
+
currentTurn = 0;
|
|
1530
|
+
lastTaskSummary = "";
|
|
1471
1531
|
lastScope = {};
|
|
1472
1532
|
async getBindings() {
|
|
1473
1533
|
if (!this.bindings) {
|
|
@@ -1485,6 +1545,64 @@ var WhisperAgentRuntime = class {
|
|
|
1485
1545
|
pushWorkEvent(event) {
|
|
1486
1546
|
this.recentWork = [...this.recentWork, event].slice(-this.recentWorkLimit);
|
|
1487
1547
|
}
|
|
1548
|
+
noteSourceActivity(sourceIds) {
|
|
1549
|
+
const now = Date.now();
|
|
1550
|
+
for (const sourceId of [...new Set((sourceIds || []).map((value) => String(value || "").trim()).filter(Boolean))]) {
|
|
1551
|
+
this.recentSourceActivity = [
|
|
1552
|
+
...this.recentSourceActivity.filter((entry) => entry.sourceId !== sourceId),
|
|
1553
|
+
{ sourceId, turn: this.currentTurn, at: now }
|
|
1554
|
+
].slice(-24);
|
|
1555
|
+
}
|
|
1556
|
+
}
|
|
1557
|
+
refreshTaskSummary(taskSummary) {
|
|
1558
|
+
const next = normalizeSummary(taskSummary);
|
|
1559
|
+
if (!next) return;
|
|
1560
|
+
if (this.sourceActivityOptions.evictOnTaskSwitch && this.lastTaskSummary && this.lastTaskSummary !== next && jaccardOverlap(this.lastTaskSummary, next) < 0.6) {
|
|
1561
|
+
this.recentSourceActivity = [];
|
|
1562
|
+
}
|
|
1563
|
+
this.lastTaskSummary = next;
|
|
1564
|
+
}
|
|
1565
|
+
activeSourceIds() {
|
|
1566
|
+
const now = Date.now();
|
|
1567
|
+
const active = /* @__PURE__ */ new Map();
|
|
1568
|
+
const maxTurns = this.sourceActivityOptions.maxTurns;
|
|
1569
|
+
const maxIdleMs = this.sourceActivityOptions.maxIdleMs;
|
|
1570
|
+
const decayAfterTurns = this.sourceActivityOptions.decayAfterTurns;
|
|
1571
|
+
const decayAfterIdleMs = this.sourceActivityOptions.decayAfterIdleMs;
|
|
1572
|
+
const fresh = [];
|
|
1573
|
+
for (const entry of this.recentSourceActivity) {
|
|
1574
|
+
const turnDelta = this.currentTurn - entry.turn;
|
|
1575
|
+
const idleDelta = now - entry.at;
|
|
1576
|
+
if (turnDelta > maxTurns || idleDelta > maxIdleMs) continue;
|
|
1577
|
+
fresh.push(entry);
|
|
1578
|
+
let weight = 1;
|
|
1579
|
+
if (turnDelta > decayAfterTurns || idleDelta > decayAfterIdleMs) {
|
|
1580
|
+
weight = 0.5;
|
|
1581
|
+
}
|
|
1582
|
+
const current = active.get(entry.sourceId) || 0;
|
|
1583
|
+
active.set(entry.sourceId, Math.max(current, weight));
|
|
1584
|
+
}
|
|
1585
|
+
this.recentSourceActivity = fresh.slice(-24);
|
|
1586
|
+
return [...active.entries()].sort((left, right) => right[1] - left[1]).map(([sourceId]) => sourceId).slice(0, 4);
|
|
1587
|
+
}
|
|
1588
|
+
focusedScope(input) {
|
|
1589
|
+
const sourceIds = this.activeSourceIds();
|
|
1590
|
+
const fileHints = [...new Set([
|
|
1591
|
+
...input.touchedFiles || [],
|
|
1592
|
+
...this.touchedFiles,
|
|
1593
|
+
...this.recentWork.flatMap((event) => event.filePaths || [])
|
|
1594
|
+
].map((value) => String(value || "").trim()).filter(Boolean))].slice(-4);
|
|
1595
|
+
return {
|
|
1596
|
+
sourceIds,
|
|
1597
|
+
fileHints,
|
|
1598
|
+
clientName: this.clientName || void 0
|
|
1599
|
+
};
|
|
1600
|
+
}
|
|
1601
|
+
exactFileMetadataFilter(fileHints) {
|
|
1602
|
+
const exact = fileHints.find((value) => /[\\/]/.test(value));
|
|
1603
|
+
if (!exact) return void 0;
|
|
1604
|
+
return { filePath: exact };
|
|
1605
|
+
}
|
|
1488
1606
|
makeTaskFrameQuery(input) {
|
|
1489
1607
|
const task = compactWhitespace(input.taskSummary || "");
|
|
1490
1608
|
const salient = this.recentWork.filter((event) => event.salience === "high").slice(-3).map((event) => `${event.kind}: ${event.summary}`);
|
|
@@ -1561,23 +1679,29 @@ var WhisperAgentRuntime = class {
|
|
|
1561
1679
|
};
|
|
1562
1680
|
}
|
|
1563
1681
|
}
|
|
1564
|
-
contextItems(result, sourceQuery) {
|
|
1682
|
+
contextItems(result, sourceQuery, pass) {
|
|
1683
|
+
const sourceScope = result.meta?.source_scope;
|
|
1684
|
+
if (sourceScope?.mode === "auto" || sourceScope?.mode === "explicit") {
|
|
1685
|
+
this.noteSourceActivity(sourceScope.source_ids || []);
|
|
1686
|
+
}
|
|
1565
1687
|
return (result.results || []).map((item) => ({
|
|
1566
1688
|
id: item.id,
|
|
1567
1689
|
content: item.content,
|
|
1568
1690
|
type: "project",
|
|
1569
1691
|
score: item.score ?? 0,
|
|
1570
1692
|
sourceQuery,
|
|
1693
|
+
pass,
|
|
1571
1694
|
metadata: item.metadata || {}
|
|
1572
1695
|
}));
|
|
1573
1696
|
}
|
|
1574
|
-
memoryItems(result, sourceQuery) {
|
|
1697
|
+
memoryItems(result, sourceQuery, pass) {
|
|
1575
1698
|
return (result.results || []).map((item, index) => ({
|
|
1576
1699
|
id: item.memory?.id || item.chunk?.id || `${sourceQuery}_memory_${index}`,
|
|
1577
1700
|
content: item.chunk?.content || item.memory?.content || "",
|
|
1578
1701
|
type: "memory",
|
|
1579
1702
|
score: item.similarity ?? 0,
|
|
1580
1703
|
sourceQuery,
|
|
1704
|
+
pass,
|
|
1581
1705
|
metadata: {
|
|
1582
1706
|
...item.chunk?.metadata || {},
|
|
1583
1707
|
...item.memory?.temporal || {},
|
|
@@ -1585,22 +1709,99 @@ var WhisperAgentRuntime = class {
|
|
|
1585
1709
|
}
|
|
1586
1710
|
})).filter((item) => item.content);
|
|
1587
1711
|
}
|
|
1588
|
-
|
|
1712
|
+
stableItemKey(item) {
|
|
1713
|
+
const metadata = item.metadata || {};
|
|
1714
|
+
const sourceId = String(metadata.source_id || "");
|
|
1715
|
+
const documentId = String(metadata.document_id || metadata.documentId || "");
|
|
1716
|
+
const chunkId = String(metadata.chunk_id || metadata.chunkId || item.id || "");
|
|
1717
|
+
return stableHash(`${sourceId}|${documentId}|${chunkId}|${item.content.slice(0, 256)}`);
|
|
1718
|
+
}
|
|
1719
|
+
metadataStrings(item) {
|
|
1720
|
+
const metadata = item.metadata || {};
|
|
1721
|
+
return [
|
|
1722
|
+
metadata.filePath,
|
|
1723
|
+
metadata.file_path,
|
|
1724
|
+
metadata.path,
|
|
1725
|
+
metadata.section_path,
|
|
1726
|
+
metadata.parent_section_path,
|
|
1727
|
+
metadata.web_url,
|
|
1728
|
+
metadata.url
|
|
1729
|
+
].map((value) => String(value || "").toLowerCase()).filter(Boolean);
|
|
1730
|
+
}
|
|
1731
|
+
hasSourceMatch(item, scope) {
|
|
1732
|
+
const sourceId = String(item.metadata?.source_id || "");
|
|
1733
|
+
return Boolean(sourceId && scope.sourceIds.includes(sourceId));
|
|
1734
|
+
}
|
|
1735
|
+
hasFileMatch(item, scope) {
|
|
1736
|
+
if (scope.fileHints.length === 0) return false;
|
|
1737
|
+
const metadata = this.metadataStrings(item);
|
|
1738
|
+
const lowerHints = scope.fileHints.map((hint) => hint.toLowerCase());
|
|
1739
|
+
return lowerHints.some((hint) => {
|
|
1740
|
+
const base = pathBase(hint).toLowerCase();
|
|
1741
|
+
return metadata.some((value) => value.includes(hint) || value.endsWith(base));
|
|
1742
|
+
});
|
|
1743
|
+
}
|
|
1744
|
+
hasClientMatch(item, scope) {
|
|
1745
|
+
const itemClient = String(item.metadata?.client_name || "");
|
|
1746
|
+
return Boolean(scope.clientName && itemClient && itemClient === scope.clientName);
|
|
1747
|
+
}
|
|
1748
|
+
salienceAdjustment(item) {
|
|
1749
|
+
const salience = item.metadata?.salience;
|
|
1750
|
+
if (salience === "high") return this.rankWeights.highSalienceBonus;
|
|
1751
|
+
if (salience === "medium") return this.rankWeights.mediumSalienceBonus;
|
|
1752
|
+
if (salience === "low") return this.rankWeights.lowSaliencePenalty;
|
|
1753
|
+
return 0;
|
|
1754
|
+
}
|
|
1755
|
+
narrowFocusedMemories(items, scope) {
|
|
1756
|
+
const hasSignals = scope.sourceIds.length > 0 || scope.fileHints.length > 0 || Boolean(scope.clientName);
|
|
1757
|
+
if (!hasSignals) return items;
|
|
1758
|
+
const narrowed = items.filter((item) => {
|
|
1759
|
+
const matchesClient = this.hasClientMatch(item, scope);
|
|
1760
|
+
const matchesFile = this.hasFileMatch(item, scope);
|
|
1761
|
+
const matchesSource = this.hasSourceMatch(item, scope);
|
|
1762
|
+
const salience = item.metadata?.salience;
|
|
1763
|
+
if (scope.clientName && item.metadata?.client_name && !matchesClient) {
|
|
1764
|
+
return false;
|
|
1765
|
+
}
|
|
1766
|
+
if (salience === "low" && !matchesFile && !matchesSource) {
|
|
1767
|
+
return false;
|
|
1768
|
+
}
|
|
1769
|
+
return matchesClient || matchesFile || matchesSource || !scope.clientName;
|
|
1770
|
+
});
|
|
1771
|
+
return narrowed.length > 0 ? narrowed : items;
|
|
1772
|
+
}
|
|
1773
|
+
applyRelevanceFloor(items) {
|
|
1774
|
+
const filtered = items.filter(
|
|
1775
|
+
(item) => item.type === "project" ? item.score >= this.minProjectScore : item.score >= this.minMemoryScore
|
|
1776
|
+
);
|
|
1777
|
+
return { items: filtered, dropped: Math.max(0, items.length - filtered.length) };
|
|
1778
|
+
}
|
|
1779
|
+
rerank(items, scope) {
|
|
1589
1780
|
const deduped = /* @__PURE__ */ new Map();
|
|
1590
1781
|
for (const item of items) {
|
|
1591
|
-
const key =
|
|
1782
|
+
const key = this.stableItemKey(item);
|
|
1592
1783
|
const recency = extractTimestamp(item.metadata) > 0 ? 0.04 : 0;
|
|
1593
1784
|
const queryBonus = item.sourceQuery === "primary" ? 0.08 : item.sourceQuery === "task_frame" ? 0.04 : 0.03;
|
|
1785
|
+
const sourceMatch = this.hasSourceMatch(item, scope);
|
|
1786
|
+
const fileMatch = this.hasFileMatch(item, scope);
|
|
1787
|
+
const clientMatch = this.hasClientMatch(item, scope);
|
|
1788
|
+
const broadPenalty = item.pass === "broad" && !sourceMatch && !fileMatch && !clientMatch ? this.rankWeights.staleBroadPenalty : 0;
|
|
1789
|
+
const clientPenalty = scope.clientName && item.metadata?.client_name && !clientMatch ? this.rankWeights.unrelatedClientPenalty : 0;
|
|
1594
1790
|
const next = {
|
|
1595
1791
|
...item,
|
|
1596
|
-
score:
|
|
1792
|
+
score: clamp01(
|
|
1793
|
+
item.score + queryBonus + recency + (item.pass === "focused" ? this.rankWeights.focusedPassBonus : 0) + (sourceMatch ? this.rankWeights.sourceMatchBonus : 0) + (fileMatch ? this.rankWeights.touchedFileBonus : 0) + (clientMatch ? this.rankWeights.clientMatchBonus : 0) + this.salienceAdjustment(item) + broadPenalty + clientPenalty
|
|
1794
|
+
)
|
|
1597
1795
|
};
|
|
1598
1796
|
const existing = deduped.get(key);
|
|
1599
1797
|
if (!existing || next.score > existing.score) {
|
|
1600
1798
|
deduped.set(key, next);
|
|
1601
1799
|
}
|
|
1602
1800
|
}
|
|
1603
|
-
return
|
|
1801
|
+
return {
|
|
1802
|
+
items: [...deduped.values()].sort((left, right) => right.score - left.score),
|
|
1803
|
+
dedupedCount: Math.max(0, items.length - deduped.size)
|
|
1804
|
+
};
|
|
1604
1805
|
}
|
|
1605
1806
|
buildContext(items) {
|
|
1606
1807
|
const maxChars = this.maxTokens * 4;
|
|
@@ -1639,7 +1840,7 @@ ${lines.join("\n")}`;
|
|
|
1639
1840
|
this.runBranch("project_rules", () => this.args.adapter.query({
|
|
1640
1841
|
project: scope.project,
|
|
1641
1842
|
query: "project rules instructions constraints conventions open threads",
|
|
1642
|
-
top_k: this.
|
|
1843
|
+
top_k: this.focusedTopK,
|
|
1643
1844
|
include_memories: false,
|
|
1644
1845
|
user_id: scope.userId,
|
|
1645
1846
|
session_id: scope.sessionId,
|
|
@@ -1657,7 +1858,7 @@ ${lines.join("\n")}`;
|
|
|
1657
1858
|
continue;
|
|
1658
1859
|
}
|
|
1659
1860
|
if (branch.name === "project_rules") {
|
|
1660
|
-
items.push(...this.contextItems(branch.value, "bootstrap"));
|
|
1861
|
+
items.push(...this.contextItems(branch.value, "bootstrap", "bootstrap"));
|
|
1661
1862
|
continue;
|
|
1662
1863
|
}
|
|
1663
1864
|
const records = branch.value.memories || [];
|
|
@@ -1667,10 +1868,12 @@ ${lines.join("\n")}`;
|
|
|
1667
1868
|
type: "memory",
|
|
1668
1869
|
score: 0.4,
|
|
1669
1870
|
sourceQuery: "bootstrap",
|
|
1871
|
+
pass: "bootstrap",
|
|
1670
1872
|
metadata: memory
|
|
1671
1873
|
})).filter((item) => item.content));
|
|
1672
1874
|
}
|
|
1673
|
-
const
|
|
1875
|
+
const reranked = this.rerank(items, { sourceIds: [], fileHints: [], clientName: this.clientName });
|
|
1876
|
+
const ranked = reranked.items.slice(0, this.broadTopK * 2);
|
|
1674
1877
|
const prepared = {
|
|
1675
1878
|
scope,
|
|
1676
1879
|
retrieval: {
|
|
@@ -1682,7 +1885,14 @@ ${lines.join("\n")}`;
|
|
|
1682
1885
|
durationMs: Date.now() - startedAt,
|
|
1683
1886
|
targetBudgetMs: this.targetRetrievalMs,
|
|
1684
1887
|
hardTimeoutMs: this.hardRetrievalTimeoutMs,
|
|
1685
|
-
branchStatus
|
|
1888
|
+
branchStatus,
|
|
1889
|
+
focusedScopeApplied: false,
|
|
1890
|
+
focusedSourceIds: [],
|
|
1891
|
+
focusedFileHints: [],
|
|
1892
|
+
clientScoped: false,
|
|
1893
|
+
fallbackUsed: false,
|
|
1894
|
+
droppedBelowFloor: 0,
|
|
1895
|
+
dedupedCount: reranked.dedupedCount
|
|
1686
1896
|
},
|
|
1687
1897
|
context: this.buildContext(ranked),
|
|
1688
1898
|
items: ranked
|
|
@@ -1691,100 +1901,195 @@ ${lines.join("\n")}`;
|
|
|
1691
1901
|
return prepared;
|
|
1692
1902
|
}
|
|
1693
1903
|
async beforeTurn(input, context = {}) {
|
|
1904
|
+
this.currentTurn += 1;
|
|
1694
1905
|
this.pushTouchedFiles(input.touchedFiles);
|
|
1906
|
+
this.refreshTaskSummary(input.taskSummary);
|
|
1695
1907
|
const { scope, warning } = await this.resolveScope(context);
|
|
1696
1908
|
const primaryQuery = compactWhitespace(input.userMessage);
|
|
1697
1909
|
const taskFrameQuery = this.makeTaskFrameQuery(input);
|
|
1910
|
+
const focusedScope = this.focusedScope(input);
|
|
1911
|
+
const focusedMetadataFilter = this.exactFileMetadataFilter(focusedScope.fileHints);
|
|
1912
|
+
const focusedScopeApplied = focusedScope.sourceIds.length > 0 || focusedScope.fileHints.length > 0 || Boolean(focusedScope.clientName);
|
|
1698
1913
|
const warnings = warning ? [warning] : [];
|
|
1699
1914
|
const startedAt = Date.now();
|
|
1700
|
-
const
|
|
1701
|
-
|
|
1915
|
+
const branchStatus = {};
|
|
1916
|
+
const collectFromBranches = (branches, pass) => {
|
|
1917
|
+
const collected = [];
|
|
1918
|
+
let okCount = 0;
|
|
1919
|
+
for (const branch of branches) {
|
|
1920
|
+
branchStatus[branch.name] = branch.status;
|
|
1921
|
+
if (branch.status !== "ok") {
|
|
1922
|
+
if (branch.status !== "skipped" && branch.reason) warnings.push(`${branch.name}:${branch.reason}`);
|
|
1923
|
+
continue;
|
|
1924
|
+
}
|
|
1925
|
+
okCount += 1;
|
|
1926
|
+
if (branch.name.startsWith("context")) {
|
|
1927
|
+
collected.push(...this.contextItems(
|
|
1928
|
+
branch.value,
|
|
1929
|
+
branch.name.includes("task_frame") ? "task_frame" : "primary",
|
|
1930
|
+
pass
|
|
1931
|
+
));
|
|
1932
|
+
} else {
|
|
1933
|
+
const memoryItems = this.memoryItems(
|
|
1934
|
+
branch.value,
|
|
1935
|
+
branch.name.includes("task_frame") ? "task_frame" : "primary",
|
|
1936
|
+
pass
|
|
1937
|
+
);
|
|
1938
|
+
collected.push(...pass === "focused" ? this.narrowFocusedMemories(memoryItems, focusedScope) : memoryItems);
|
|
1939
|
+
}
|
|
1940
|
+
}
|
|
1941
|
+
return { collected, okCount };
|
|
1942
|
+
};
|
|
1943
|
+
const focusedBranches = await Promise.all([
|
|
1944
|
+
this.runBranch("context_primary_focused", () => this.args.adapter.query({
|
|
1702
1945
|
project: scope.project,
|
|
1703
1946
|
query: primaryQuery,
|
|
1704
|
-
top_k: this.
|
|
1947
|
+
top_k: this.focusedTopK,
|
|
1705
1948
|
include_memories: false,
|
|
1706
1949
|
user_id: scope.userId,
|
|
1707
1950
|
session_id: scope.sessionId,
|
|
1951
|
+
source_ids: focusedScope.sourceIds.length > 0 ? focusedScope.sourceIds : void 0,
|
|
1952
|
+
metadata_filter: focusedMetadataFilter,
|
|
1708
1953
|
max_tokens: this.maxTokens,
|
|
1709
1954
|
compress: true,
|
|
1710
1955
|
compression_strategy: "adaptive"
|
|
1711
1956
|
})),
|
|
1712
|
-
this.runBranch("
|
|
1957
|
+
this.runBranch("memory_primary_focused", () => this.args.adapter.searchMemories({
|
|
1713
1958
|
project: scope.project,
|
|
1714
1959
|
query: primaryQuery,
|
|
1715
1960
|
user_id: scope.userId,
|
|
1716
1961
|
session_id: scope.sessionId,
|
|
1717
|
-
top_k: this.
|
|
1962
|
+
top_k: this.focusedTopK,
|
|
1718
1963
|
include_pending: true,
|
|
1719
1964
|
profile: "balanced"
|
|
1720
1965
|
})),
|
|
1721
|
-
taskFrameQuery ? this.runBranch("
|
|
1966
|
+
taskFrameQuery ? this.runBranch("context_task_frame_focused", () => this.args.adapter.query({
|
|
1722
1967
|
project: scope.project,
|
|
1723
1968
|
query: taskFrameQuery,
|
|
1724
|
-
top_k: this.
|
|
1969
|
+
top_k: this.focusedTopK,
|
|
1725
1970
|
include_memories: false,
|
|
1726
1971
|
user_id: scope.userId,
|
|
1727
1972
|
session_id: scope.sessionId,
|
|
1973
|
+
source_ids: focusedScope.sourceIds.length > 0 ? focusedScope.sourceIds : void 0,
|
|
1974
|
+
metadata_filter: focusedMetadataFilter,
|
|
1728
1975
|
max_tokens: this.maxTokens,
|
|
1729
1976
|
compress: true,
|
|
1730
1977
|
compression_strategy: "adaptive"
|
|
1731
|
-
})) : Promise.resolve({
|
|
1732
|
-
|
|
1733
|
-
status: "skipped",
|
|
1734
|
-
durationMs: 0
|
|
1735
|
-
}),
|
|
1736
|
-
taskFrameQuery ? this.runBranch("memory_task_frame", () => this.args.adapter.searchMemories({
|
|
1978
|
+
})) : Promise.resolve({ name: "context_task_frame_focused", status: "skipped", durationMs: 0 }),
|
|
1979
|
+
taskFrameQuery ? this.runBranch("memory_task_frame_focused", () => this.args.adapter.searchMemories({
|
|
1737
1980
|
project: scope.project,
|
|
1738
1981
|
query: taskFrameQuery,
|
|
1739
1982
|
user_id: scope.userId,
|
|
1740
1983
|
session_id: scope.sessionId,
|
|
1741
|
-
top_k: this.
|
|
1984
|
+
top_k: this.focusedTopK,
|
|
1742
1985
|
include_pending: true,
|
|
1743
1986
|
profile: "balanced"
|
|
1744
|
-
})) : Promise.resolve({
|
|
1745
|
-
name: "memory_task_frame",
|
|
1746
|
-
status: "skipped",
|
|
1747
|
-
durationMs: 0
|
|
1748
|
-
})
|
|
1987
|
+
})) : Promise.resolve({ name: "memory_task_frame_focused", status: "skipped", durationMs: 0 })
|
|
1749
1988
|
]);
|
|
1750
|
-
const
|
|
1751
|
-
const
|
|
1752
|
-
|
|
1753
|
-
|
|
1989
|
+
const focusedCollected = collectFromBranches(focusedBranches, "focused");
|
|
1990
|
+
const focusedRanked = this.rerank(focusedCollected.collected, focusedScope);
|
|
1991
|
+
const focusedFloored = this.applyRelevanceFloor(focusedRanked.items);
|
|
1992
|
+
let allCollected = [...focusedFloored.items];
|
|
1993
|
+
let totalOkCount = focusedCollected.okCount;
|
|
1994
|
+
let dedupedCount = focusedRanked.dedupedCount;
|
|
1995
|
+
let droppedBelowFloor = focusedFloored.dropped;
|
|
1996
|
+
const focusedTopScore = focusedFloored.items[0]?.score ?? 0;
|
|
1997
|
+
const fallbackUsed = focusedFloored.items.length < this.minFocusedResults || focusedTopScore < this.minFocusedTopScore;
|
|
1998
|
+
if (focusedScopeApplied) {
|
|
1999
|
+
this.sourceScopedTurns += 1;
|
|
2000
|
+
}
|
|
2001
|
+
if (!fallbackUsed) {
|
|
2002
|
+
this.focusedPassHits += 1;
|
|
2003
|
+
}
|
|
2004
|
+
const broadBranches = fallbackUsed ? await Promise.all([
|
|
2005
|
+
this.runBranch("context_primary_broad", () => this.args.adapter.query({
|
|
2006
|
+
project: scope.project,
|
|
2007
|
+
query: primaryQuery,
|
|
2008
|
+
top_k: this.broadTopK,
|
|
2009
|
+
include_memories: false,
|
|
2010
|
+
user_id: scope.userId,
|
|
2011
|
+
session_id: scope.sessionId,
|
|
2012
|
+
max_tokens: this.maxTokens,
|
|
2013
|
+
compress: true,
|
|
2014
|
+
compression_strategy: "adaptive"
|
|
2015
|
+
})),
|
|
2016
|
+
this.runBranch("memory_primary_broad", () => this.args.adapter.searchMemories({
|
|
2017
|
+
project: scope.project,
|
|
2018
|
+
query: primaryQuery,
|
|
2019
|
+
user_id: scope.userId,
|
|
2020
|
+
session_id: scope.sessionId,
|
|
2021
|
+
top_k: this.broadTopK,
|
|
2022
|
+
include_pending: true,
|
|
2023
|
+
profile: "balanced"
|
|
2024
|
+
})),
|
|
2025
|
+
taskFrameQuery ? this.runBranch("context_task_frame_broad", () => this.args.adapter.query({
|
|
2026
|
+
project: scope.project,
|
|
2027
|
+
query: taskFrameQuery,
|
|
2028
|
+
top_k: this.broadTopK,
|
|
2029
|
+
include_memories: false,
|
|
2030
|
+
user_id: scope.userId,
|
|
2031
|
+
session_id: scope.sessionId,
|
|
2032
|
+
max_tokens: this.maxTokens,
|
|
2033
|
+
compress: true,
|
|
2034
|
+
compression_strategy: "adaptive"
|
|
2035
|
+
})) : Promise.resolve({ name: "context_task_frame_broad", status: "skipped", durationMs: 0 }),
|
|
2036
|
+
taskFrameQuery ? this.runBranch("memory_task_frame_broad", () => this.args.adapter.searchMemories({
|
|
2037
|
+
project: scope.project,
|
|
2038
|
+
query: taskFrameQuery,
|
|
2039
|
+
user_id: scope.userId,
|
|
2040
|
+
session_id: scope.sessionId,
|
|
2041
|
+
top_k: this.broadTopK,
|
|
2042
|
+
include_pending: true,
|
|
2043
|
+
profile: "balanced"
|
|
2044
|
+
})) : Promise.resolve({ name: "memory_task_frame_broad", status: "skipped", durationMs: 0 })
|
|
2045
|
+
]) : [
|
|
2046
|
+
{ name: "context_primary_broad", status: "skipped", durationMs: 0 },
|
|
2047
|
+
{ name: "memory_primary_broad", status: "skipped", durationMs: 0 },
|
|
2048
|
+
{ name: "context_task_frame_broad", status: "skipped", durationMs: 0 },
|
|
2049
|
+
{ name: "memory_task_frame_broad", status: "skipped", durationMs: 0 }
|
|
2050
|
+
];
|
|
2051
|
+
const broadCollected = collectFromBranches(broadBranches, "broad");
|
|
2052
|
+
totalOkCount += broadCollected.okCount;
|
|
2053
|
+
if (fallbackUsed) {
|
|
2054
|
+
this.fallbackTriggers += 1;
|
|
2055
|
+
this.broadScopedTurns += 1;
|
|
2056
|
+
allCollected = [...allCollected, ...broadCollected.collected];
|
|
2057
|
+
}
|
|
2058
|
+
const ranked = this.rerank(allCollected, focusedScope);
|
|
2059
|
+
dedupedCount += ranked.dedupedCount;
|
|
2060
|
+
const floored = this.applyRelevanceFloor(ranked.items);
|
|
2061
|
+
droppedBelowFloor += floored.dropped;
|
|
2062
|
+
this.floorDroppedCount += droppedBelowFloor;
|
|
2063
|
+
this.droppedCount += droppedBelowFloor;
|
|
2064
|
+
const finalItems = floored.items.slice(0, this.broadTopK);
|
|
2065
|
+
this.injectedItemCount += finalItems.length;
|
|
2066
|
+
this.totalTurns += 1;
|
|
2067
|
+
const executedBranches = [...focusedBranches, ...broadBranches].filter((branch) => branch.status !== "skipped");
|
|
2068
|
+
for (const branch of [...focusedBranches, ...broadBranches]) {
|
|
1754
2069
|
branchStatus[branch.name] = branch.status;
|
|
1755
|
-
if (branch.status !== "ok") {
|
|
1756
|
-
if (branch.status !== "skipped" && branch.reason) warnings.push(`${branch.name}:${branch.reason}`);
|
|
1757
|
-
continue;
|
|
1758
|
-
}
|
|
1759
|
-
okCount += 1;
|
|
1760
|
-
if (branch.name.startsWith("context")) {
|
|
1761
|
-
collected.push(...this.contextItems(
|
|
1762
|
-
branch.value,
|
|
1763
|
-
branch.name.includes("task_frame") ? "task_frame" : "primary"
|
|
1764
|
-
));
|
|
1765
|
-
} else {
|
|
1766
|
-
collected.push(...this.memoryItems(
|
|
1767
|
-
branch.value,
|
|
1768
|
-
branch.name.includes("task_frame") ? "task_frame" : "primary"
|
|
1769
|
-
));
|
|
1770
|
-
}
|
|
1771
2070
|
}
|
|
1772
|
-
const ranked = this.rerank(collected).slice(0, this.topK * 2);
|
|
1773
2071
|
const prepared = {
|
|
1774
2072
|
scope,
|
|
1775
2073
|
retrieval: {
|
|
1776
2074
|
primaryQuery,
|
|
1777
2075
|
taskFrameQuery,
|
|
1778
2076
|
warnings,
|
|
1779
|
-
degraded:
|
|
1780
|
-
degradedReason:
|
|
2077
|
+
degraded: totalOkCount < executedBranches.length,
|
|
2078
|
+
degradedReason: totalOkCount === 0 ? "all_retrieval_failed" : warnings.length > 0 ? "partial_retrieval_failed" : void 0,
|
|
1781
2079
|
durationMs: Date.now() - startedAt,
|
|
1782
2080
|
targetBudgetMs: this.targetRetrievalMs,
|
|
1783
2081
|
hardTimeoutMs: this.hardRetrievalTimeoutMs,
|
|
1784
|
-
branchStatus
|
|
2082
|
+
branchStatus,
|
|
2083
|
+
focusedScopeApplied,
|
|
2084
|
+
focusedSourceIds: focusedScope.sourceIds,
|
|
2085
|
+
focusedFileHints: focusedScope.fileHints.map((value) => pathBase(value)),
|
|
2086
|
+
clientScoped: Boolean(focusedScope.clientName),
|
|
2087
|
+
fallbackUsed,
|
|
2088
|
+
droppedBelowFloor,
|
|
2089
|
+
dedupedCount
|
|
1785
2090
|
},
|
|
1786
|
-
context: this.buildContext(
|
|
1787
|
-
items:
|
|
2091
|
+
context: this.buildContext(finalItems),
|
|
2092
|
+
items: finalItems
|
|
1788
2093
|
};
|
|
1789
2094
|
this.lastPreparedTurn = prepared.retrieval;
|
|
1790
2095
|
return prepared;
|
|
@@ -1879,7 +2184,14 @@ ${lines.join("\n")}`;
|
|
|
1879
2184
|
counters: {
|
|
1880
2185
|
mergedCount: this.mergedCount,
|
|
1881
2186
|
droppedCount: this.droppedCount,
|
|
1882
|
-
bufferedLowSalience: this.bufferedLowSalience.length
|
|
2187
|
+
bufferedLowSalience: this.bufferedLowSalience.length,
|
|
2188
|
+
focusedPassHits: this.focusedPassHits,
|
|
2189
|
+
fallbackTriggers: this.fallbackTriggers,
|
|
2190
|
+
floorDroppedCount: this.floorDroppedCount,
|
|
2191
|
+
injectedItemCount: this.injectedItemCount,
|
|
2192
|
+
sourceScopedTurns: this.sourceScopedTurns,
|
|
2193
|
+
broadScopedTurns: this.broadScopedTurns,
|
|
2194
|
+
totalTurns: this.totalTurns
|
|
1883
2195
|
}
|
|
1884
2196
|
};
|
|
1885
2197
|
}
|
|
@@ -3588,6 +3900,19 @@ var WhisperContext = class _WhisperContext {
|
|
|
3588
3900
|
}
|
|
3589
3901
|
]);
|
|
3590
3902
|
}
|
|
3903
|
+
async listMemories(params) {
|
|
3904
|
+
const projectRef = this.getRequiredProject(params.project);
|
|
3905
|
+
return this.withProjectRefFallback(projectRef, async (project) => {
|
|
3906
|
+
const query = new URLSearchParams({
|
|
3907
|
+
project,
|
|
3908
|
+
...params.user_id ? { user_id: params.user_id } : {},
|
|
3909
|
+
...params.session_id ? { session_id: params.session_id } : {},
|
|
3910
|
+
...params.agent_id ? { agent_id: params.agent_id } : {},
|
|
3911
|
+
limit: String(Math.min(Math.max(params.limit ?? 200, 1), 200))
|
|
3912
|
+
});
|
|
3913
|
+
return this.request(`/v1/memories?${query.toString()}`, { method: "GET" });
|
|
3914
|
+
});
|
|
3915
|
+
}
|
|
3591
3916
|
async addMemory(params) {
|
|
3592
3917
|
const projectRef = this.getRequiredProject(params.project);
|
|
3593
3918
|
return this.withProjectRefFallback(projectRef, async (project) => {
|