@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.mjs
CHANGED
|
@@ -1353,6 +1353,29 @@ ${lines.join("\n")}`;
|
|
|
1353
1353
|
function compactWhitespace(value) {
|
|
1354
1354
|
return value.replace(/\s+/g, " ").trim();
|
|
1355
1355
|
}
|
|
1356
|
+
function normalizeSummary(value) {
|
|
1357
|
+
return compactWhitespace(String(value || "").toLowerCase());
|
|
1358
|
+
}
|
|
1359
|
+
function tokenize(value) {
|
|
1360
|
+
return normalizeSummary(value).split(/[^a-z0-9_./-]+/i).map((token) => token.trim()).filter(Boolean);
|
|
1361
|
+
}
|
|
1362
|
+
function jaccardOverlap(left, right) {
|
|
1363
|
+
const leftTokens = new Set(tokenize(left));
|
|
1364
|
+
const rightTokens = new Set(tokenize(right));
|
|
1365
|
+
if (leftTokens.size === 0 || rightTokens.size === 0) return 0;
|
|
1366
|
+
let intersection = 0;
|
|
1367
|
+
for (const token of leftTokens) {
|
|
1368
|
+
if (rightTokens.has(token)) intersection += 1;
|
|
1369
|
+
}
|
|
1370
|
+
const union = (/* @__PURE__ */ new Set([...leftTokens, ...rightTokens])).size;
|
|
1371
|
+
return union > 0 ? intersection / union : 0;
|
|
1372
|
+
}
|
|
1373
|
+
function clamp01(value) {
|
|
1374
|
+
if (!Number.isFinite(value)) return 0;
|
|
1375
|
+
if (value < 0) return 0;
|
|
1376
|
+
if (value > 1) return 1;
|
|
1377
|
+
return value;
|
|
1378
|
+
}
|
|
1356
1379
|
function withTimeout(promise, timeoutMs) {
|
|
1357
1380
|
return new Promise((resolve, reject) => {
|
|
1358
1381
|
const timeout = setTimeout(() => {
|
|
@@ -1386,39 +1409,76 @@ function extractTimestamp(metadata) {
|
|
|
1386
1409
|
}
|
|
1387
1410
|
return 0;
|
|
1388
1411
|
}
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1412
|
+
var DEFAULT_RANK_WEIGHTS = {
|
|
1413
|
+
focusedPassBonus: 0.2,
|
|
1414
|
+
sourceMatchBonus: 0.18,
|
|
1415
|
+
touchedFileBonus: 0.12,
|
|
1416
|
+
clientMatchBonus: 0.1,
|
|
1417
|
+
highSalienceBonus: 0.12,
|
|
1418
|
+
mediumSalienceBonus: 0.06,
|
|
1419
|
+
staleBroadPenalty: -0.1,
|
|
1420
|
+
unrelatedClientPenalty: -0.18,
|
|
1421
|
+
lowSaliencePenalty: -0.12
|
|
1422
|
+
};
|
|
1423
|
+
var DEFAULT_SOURCE_ACTIVITY = {
|
|
1424
|
+
maxTurns: 10,
|
|
1425
|
+
maxIdleMs: 30 * 60 * 1e3,
|
|
1426
|
+
decayAfterTurns: 5,
|
|
1427
|
+
decayAfterIdleMs: 15 * 60 * 1e3,
|
|
1428
|
+
evictOnTaskSwitch: true
|
|
1429
|
+
};
|
|
1395
1430
|
var WhisperAgentRuntime = class {
|
|
1396
1431
|
constructor(args) {
|
|
1397
1432
|
this.args = args;
|
|
1398
1433
|
this.bindingStore = createBindingStore(args.options.bindingStorePath);
|
|
1399
|
-
|
|
1434
|
+
const retrieval = args.options.retrieval || {};
|
|
1435
|
+
this.focusedTopK = retrieval.focusedTopK ?? args.options.topK ?? 6;
|
|
1436
|
+
this.broadTopK = retrieval.broadTopK ?? Math.max(args.options.topK ?? 6, 10);
|
|
1400
1437
|
this.maxTokens = args.options.maxTokens ?? 4e3;
|
|
1401
1438
|
this.targetRetrievalMs = args.options.targetRetrievalMs ?? 2500;
|
|
1402
1439
|
this.hardRetrievalTimeoutMs = args.options.hardRetrievalTimeoutMs ?? 4e3;
|
|
1403
1440
|
this.recentWorkLimit = args.options.recentWorkLimit ?? 40;
|
|
1404
1441
|
this.baseContext = args.baseContext;
|
|
1405
1442
|
this.clientName = args.baseContext.clientName || "whisper-agent-runtime";
|
|
1443
|
+
this.minFocusedResults = retrieval.minFocusedResults ?? 3;
|
|
1444
|
+
this.minFocusedTopScore = retrieval.minFocusedTopScore ?? 0.55;
|
|
1445
|
+
this.minProjectScore = retrieval.minProjectScore ?? 0.5;
|
|
1446
|
+
this.minMemoryScore = retrieval.minMemoryScore ?? 0.6;
|
|
1447
|
+
this.rankWeights = { ...DEFAULT_RANK_WEIGHTS, ...retrieval.rankWeights || {} };
|
|
1448
|
+
this.sourceActivityOptions = { ...DEFAULT_SOURCE_ACTIVITY, ...retrieval.sourceActivity || {} };
|
|
1406
1449
|
}
|
|
1407
1450
|
bindingStore;
|
|
1408
|
-
|
|
1451
|
+
focusedTopK;
|
|
1452
|
+
broadTopK;
|
|
1409
1453
|
maxTokens;
|
|
1410
1454
|
targetRetrievalMs;
|
|
1411
1455
|
hardRetrievalTimeoutMs;
|
|
1412
1456
|
recentWorkLimit;
|
|
1413
1457
|
baseContext;
|
|
1414
1458
|
clientName;
|
|
1459
|
+
minFocusedResults;
|
|
1460
|
+
minFocusedTopScore;
|
|
1461
|
+
minProjectScore;
|
|
1462
|
+
minMemoryScore;
|
|
1463
|
+
rankWeights;
|
|
1464
|
+
sourceActivityOptions;
|
|
1415
1465
|
bindings = null;
|
|
1416
1466
|
touchedFiles = [];
|
|
1417
1467
|
recentWork = [];
|
|
1468
|
+
recentSourceActivity = [];
|
|
1418
1469
|
bufferedLowSalience = [];
|
|
1419
1470
|
lastPreparedTurn = null;
|
|
1420
1471
|
mergedCount = 0;
|
|
1421
1472
|
droppedCount = 0;
|
|
1473
|
+
focusedPassHits = 0;
|
|
1474
|
+
fallbackTriggers = 0;
|
|
1475
|
+
floorDroppedCount = 0;
|
|
1476
|
+
injectedItemCount = 0;
|
|
1477
|
+
sourceScopedTurns = 0;
|
|
1478
|
+
broadScopedTurns = 0;
|
|
1479
|
+
totalTurns = 0;
|
|
1480
|
+
currentTurn = 0;
|
|
1481
|
+
lastTaskSummary = "";
|
|
1422
1482
|
lastScope = {};
|
|
1423
1483
|
async getBindings() {
|
|
1424
1484
|
if (!this.bindings) {
|
|
@@ -1436,6 +1496,64 @@ var WhisperAgentRuntime = class {
|
|
|
1436
1496
|
pushWorkEvent(event) {
|
|
1437
1497
|
this.recentWork = [...this.recentWork, event].slice(-this.recentWorkLimit);
|
|
1438
1498
|
}
|
|
1499
|
+
noteSourceActivity(sourceIds) {
|
|
1500
|
+
const now = Date.now();
|
|
1501
|
+
for (const sourceId of [...new Set((sourceIds || []).map((value) => String(value || "").trim()).filter(Boolean))]) {
|
|
1502
|
+
this.recentSourceActivity = [
|
|
1503
|
+
...this.recentSourceActivity.filter((entry) => entry.sourceId !== sourceId),
|
|
1504
|
+
{ sourceId, turn: this.currentTurn, at: now }
|
|
1505
|
+
].slice(-24);
|
|
1506
|
+
}
|
|
1507
|
+
}
|
|
1508
|
+
refreshTaskSummary(taskSummary) {
|
|
1509
|
+
const next = normalizeSummary(taskSummary);
|
|
1510
|
+
if (!next) return;
|
|
1511
|
+
if (this.sourceActivityOptions.evictOnTaskSwitch && this.lastTaskSummary && this.lastTaskSummary !== next && jaccardOverlap(this.lastTaskSummary, next) < 0.6) {
|
|
1512
|
+
this.recentSourceActivity = [];
|
|
1513
|
+
}
|
|
1514
|
+
this.lastTaskSummary = next;
|
|
1515
|
+
}
|
|
1516
|
+
activeSourceIds() {
|
|
1517
|
+
const now = Date.now();
|
|
1518
|
+
const active = /* @__PURE__ */ new Map();
|
|
1519
|
+
const maxTurns = this.sourceActivityOptions.maxTurns;
|
|
1520
|
+
const maxIdleMs = this.sourceActivityOptions.maxIdleMs;
|
|
1521
|
+
const decayAfterTurns = this.sourceActivityOptions.decayAfterTurns;
|
|
1522
|
+
const decayAfterIdleMs = this.sourceActivityOptions.decayAfterIdleMs;
|
|
1523
|
+
const fresh = [];
|
|
1524
|
+
for (const entry of this.recentSourceActivity) {
|
|
1525
|
+
const turnDelta = this.currentTurn - entry.turn;
|
|
1526
|
+
const idleDelta = now - entry.at;
|
|
1527
|
+
if (turnDelta > maxTurns || idleDelta > maxIdleMs) continue;
|
|
1528
|
+
fresh.push(entry);
|
|
1529
|
+
let weight = 1;
|
|
1530
|
+
if (turnDelta > decayAfterTurns || idleDelta > decayAfterIdleMs) {
|
|
1531
|
+
weight = 0.5;
|
|
1532
|
+
}
|
|
1533
|
+
const current = active.get(entry.sourceId) || 0;
|
|
1534
|
+
active.set(entry.sourceId, Math.max(current, weight));
|
|
1535
|
+
}
|
|
1536
|
+
this.recentSourceActivity = fresh.slice(-24);
|
|
1537
|
+
return [...active.entries()].sort((left, right) => right[1] - left[1]).map(([sourceId]) => sourceId).slice(0, 4);
|
|
1538
|
+
}
|
|
1539
|
+
focusedScope(input) {
|
|
1540
|
+
const sourceIds = this.activeSourceIds();
|
|
1541
|
+
const fileHints = [...new Set([
|
|
1542
|
+
...input.touchedFiles || [],
|
|
1543
|
+
...this.touchedFiles,
|
|
1544
|
+
...this.recentWork.flatMap((event) => event.filePaths || [])
|
|
1545
|
+
].map((value) => String(value || "").trim()).filter(Boolean))].slice(-4);
|
|
1546
|
+
return {
|
|
1547
|
+
sourceIds,
|
|
1548
|
+
fileHints,
|
|
1549
|
+
clientName: this.clientName || void 0
|
|
1550
|
+
};
|
|
1551
|
+
}
|
|
1552
|
+
exactFileMetadataFilter(fileHints) {
|
|
1553
|
+
const exact = fileHints.find((value) => /[\\/]/.test(value));
|
|
1554
|
+
if (!exact) return void 0;
|
|
1555
|
+
return { filePath: exact };
|
|
1556
|
+
}
|
|
1439
1557
|
makeTaskFrameQuery(input) {
|
|
1440
1558
|
const task = compactWhitespace(input.taskSummary || "");
|
|
1441
1559
|
const salient = this.recentWork.filter((event) => event.salience === "high").slice(-3).map((event) => `${event.kind}: ${event.summary}`);
|
|
@@ -1512,23 +1630,29 @@ var WhisperAgentRuntime = class {
|
|
|
1512
1630
|
};
|
|
1513
1631
|
}
|
|
1514
1632
|
}
|
|
1515
|
-
contextItems(result, sourceQuery) {
|
|
1633
|
+
contextItems(result, sourceQuery, pass) {
|
|
1634
|
+
const sourceScope = result.meta?.source_scope;
|
|
1635
|
+
if (sourceScope?.mode === "auto" || sourceScope?.mode === "explicit") {
|
|
1636
|
+
this.noteSourceActivity(sourceScope.source_ids || []);
|
|
1637
|
+
}
|
|
1516
1638
|
return (result.results || []).map((item) => ({
|
|
1517
1639
|
id: item.id,
|
|
1518
1640
|
content: item.content,
|
|
1519
1641
|
type: "project",
|
|
1520
1642
|
score: item.score ?? 0,
|
|
1521
1643
|
sourceQuery,
|
|
1644
|
+
pass,
|
|
1522
1645
|
metadata: item.metadata || {}
|
|
1523
1646
|
}));
|
|
1524
1647
|
}
|
|
1525
|
-
memoryItems(result, sourceQuery) {
|
|
1648
|
+
memoryItems(result, sourceQuery, pass) {
|
|
1526
1649
|
return (result.results || []).map((item, index) => ({
|
|
1527
1650
|
id: item.memory?.id || item.chunk?.id || `${sourceQuery}_memory_${index}`,
|
|
1528
1651
|
content: item.chunk?.content || item.memory?.content || "",
|
|
1529
1652
|
type: "memory",
|
|
1530
1653
|
score: item.similarity ?? 0,
|
|
1531
1654
|
sourceQuery,
|
|
1655
|
+
pass,
|
|
1532
1656
|
metadata: {
|
|
1533
1657
|
...item.chunk?.metadata || {},
|
|
1534
1658
|
...item.memory?.temporal || {},
|
|
@@ -1536,22 +1660,99 @@ var WhisperAgentRuntime = class {
|
|
|
1536
1660
|
}
|
|
1537
1661
|
})).filter((item) => item.content);
|
|
1538
1662
|
}
|
|
1539
|
-
|
|
1663
|
+
stableItemKey(item) {
|
|
1664
|
+
const metadata = item.metadata || {};
|
|
1665
|
+
const sourceId = String(metadata.source_id || "");
|
|
1666
|
+
const documentId = String(metadata.document_id || metadata.documentId || "");
|
|
1667
|
+
const chunkId = String(metadata.chunk_id || metadata.chunkId || item.id || "");
|
|
1668
|
+
return stableHash(`${sourceId}|${documentId}|${chunkId}|${item.content.slice(0, 256)}`);
|
|
1669
|
+
}
|
|
1670
|
+
metadataStrings(item) {
|
|
1671
|
+
const metadata = item.metadata || {};
|
|
1672
|
+
return [
|
|
1673
|
+
metadata.filePath,
|
|
1674
|
+
metadata.file_path,
|
|
1675
|
+
metadata.path,
|
|
1676
|
+
metadata.section_path,
|
|
1677
|
+
metadata.parent_section_path,
|
|
1678
|
+
metadata.web_url,
|
|
1679
|
+
metadata.url
|
|
1680
|
+
].map((value) => String(value || "").toLowerCase()).filter(Boolean);
|
|
1681
|
+
}
|
|
1682
|
+
hasSourceMatch(item, scope) {
|
|
1683
|
+
const sourceId = String(item.metadata?.source_id || "");
|
|
1684
|
+
return Boolean(sourceId && scope.sourceIds.includes(sourceId));
|
|
1685
|
+
}
|
|
1686
|
+
hasFileMatch(item, scope) {
|
|
1687
|
+
if (scope.fileHints.length === 0) return false;
|
|
1688
|
+
const metadata = this.metadataStrings(item);
|
|
1689
|
+
const lowerHints = scope.fileHints.map((hint) => hint.toLowerCase());
|
|
1690
|
+
return lowerHints.some((hint) => {
|
|
1691
|
+
const base = pathBase(hint).toLowerCase();
|
|
1692
|
+
return metadata.some((value) => value.includes(hint) || value.endsWith(base));
|
|
1693
|
+
});
|
|
1694
|
+
}
|
|
1695
|
+
hasClientMatch(item, scope) {
|
|
1696
|
+
const itemClient = String(item.metadata?.client_name || "");
|
|
1697
|
+
return Boolean(scope.clientName && itemClient && itemClient === scope.clientName);
|
|
1698
|
+
}
|
|
1699
|
+
salienceAdjustment(item) {
|
|
1700
|
+
const salience = item.metadata?.salience;
|
|
1701
|
+
if (salience === "high") return this.rankWeights.highSalienceBonus;
|
|
1702
|
+
if (salience === "medium") return this.rankWeights.mediumSalienceBonus;
|
|
1703
|
+
if (salience === "low") return this.rankWeights.lowSaliencePenalty;
|
|
1704
|
+
return 0;
|
|
1705
|
+
}
|
|
1706
|
+
narrowFocusedMemories(items, scope) {
|
|
1707
|
+
const hasSignals = scope.sourceIds.length > 0 || scope.fileHints.length > 0 || Boolean(scope.clientName);
|
|
1708
|
+
if (!hasSignals) return items;
|
|
1709
|
+
const narrowed = items.filter((item) => {
|
|
1710
|
+
const matchesClient = this.hasClientMatch(item, scope);
|
|
1711
|
+
const matchesFile = this.hasFileMatch(item, scope);
|
|
1712
|
+
const matchesSource = this.hasSourceMatch(item, scope);
|
|
1713
|
+
const salience = item.metadata?.salience;
|
|
1714
|
+
if (scope.clientName && item.metadata?.client_name && !matchesClient) {
|
|
1715
|
+
return false;
|
|
1716
|
+
}
|
|
1717
|
+
if (salience === "low" && !matchesFile && !matchesSource) {
|
|
1718
|
+
return false;
|
|
1719
|
+
}
|
|
1720
|
+
return matchesClient || matchesFile || matchesSource || !scope.clientName;
|
|
1721
|
+
});
|
|
1722
|
+
return narrowed.length > 0 ? narrowed : items;
|
|
1723
|
+
}
|
|
1724
|
+
applyRelevanceFloor(items) {
|
|
1725
|
+
const filtered = items.filter(
|
|
1726
|
+
(item) => item.type === "project" ? item.score >= this.minProjectScore : item.score >= this.minMemoryScore
|
|
1727
|
+
);
|
|
1728
|
+
return { items: filtered, dropped: Math.max(0, items.length - filtered.length) };
|
|
1729
|
+
}
|
|
1730
|
+
rerank(items, scope) {
|
|
1540
1731
|
const deduped = /* @__PURE__ */ new Map();
|
|
1541
1732
|
for (const item of items) {
|
|
1542
|
-
const key =
|
|
1733
|
+
const key = this.stableItemKey(item);
|
|
1543
1734
|
const recency = extractTimestamp(item.metadata) > 0 ? 0.04 : 0;
|
|
1544
1735
|
const queryBonus = item.sourceQuery === "primary" ? 0.08 : item.sourceQuery === "task_frame" ? 0.04 : 0.03;
|
|
1736
|
+
const sourceMatch = this.hasSourceMatch(item, scope);
|
|
1737
|
+
const fileMatch = this.hasFileMatch(item, scope);
|
|
1738
|
+
const clientMatch = this.hasClientMatch(item, scope);
|
|
1739
|
+
const broadPenalty = item.pass === "broad" && !sourceMatch && !fileMatch && !clientMatch ? this.rankWeights.staleBroadPenalty : 0;
|
|
1740
|
+
const clientPenalty = scope.clientName && item.metadata?.client_name && !clientMatch ? this.rankWeights.unrelatedClientPenalty : 0;
|
|
1545
1741
|
const next = {
|
|
1546
1742
|
...item,
|
|
1547
|
-
score:
|
|
1743
|
+
score: clamp01(
|
|
1744
|
+
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
|
|
1745
|
+
)
|
|
1548
1746
|
};
|
|
1549
1747
|
const existing = deduped.get(key);
|
|
1550
1748
|
if (!existing || next.score > existing.score) {
|
|
1551
1749
|
deduped.set(key, next);
|
|
1552
1750
|
}
|
|
1553
1751
|
}
|
|
1554
|
-
return
|
|
1752
|
+
return {
|
|
1753
|
+
items: [...deduped.values()].sort((left, right) => right.score - left.score),
|
|
1754
|
+
dedupedCount: Math.max(0, items.length - deduped.size)
|
|
1755
|
+
};
|
|
1555
1756
|
}
|
|
1556
1757
|
buildContext(items) {
|
|
1557
1758
|
const maxChars = this.maxTokens * 4;
|
|
@@ -1590,7 +1791,7 @@ ${lines.join("\n")}`;
|
|
|
1590
1791
|
this.runBranch("project_rules", () => this.args.adapter.query({
|
|
1591
1792
|
project: scope.project,
|
|
1592
1793
|
query: "project rules instructions constraints conventions open threads",
|
|
1593
|
-
top_k: this.
|
|
1794
|
+
top_k: this.focusedTopK,
|
|
1594
1795
|
include_memories: false,
|
|
1595
1796
|
user_id: scope.userId,
|
|
1596
1797
|
session_id: scope.sessionId,
|
|
@@ -1608,7 +1809,7 @@ ${lines.join("\n")}`;
|
|
|
1608
1809
|
continue;
|
|
1609
1810
|
}
|
|
1610
1811
|
if (branch.name === "project_rules") {
|
|
1611
|
-
items.push(...this.contextItems(branch.value, "bootstrap"));
|
|
1812
|
+
items.push(...this.contextItems(branch.value, "bootstrap", "bootstrap"));
|
|
1612
1813
|
continue;
|
|
1613
1814
|
}
|
|
1614
1815
|
const records = branch.value.memories || [];
|
|
@@ -1618,10 +1819,12 @@ ${lines.join("\n")}`;
|
|
|
1618
1819
|
type: "memory",
|
|
1619
1820
|
score: 0.4,
|
|
1620
1821
|
sourceQuery: "bootstrap",
|
|
1822
|
+
pass: "bootstrap",
|
|
1621
1823
|
metadata: memory
|
|
1622
1824
|
})).filter((item) => item.content));
|
|
1623
1825
|
}
|
|
1624
|
-
const
|
|
1826
|
+
const reranked = this.rerank(items, { sourceIds: [], fileHints: [], clientName: this.clientName });
|
|
1827
|
+
const ranked = reranked.items.slice(0, this.broadTopK * 2);
|
|
1625
1828
|
const prepared = {
|
|
1626
1829
|
scope,
|
|
1627
1830
|
retrieval: {
|
|
@@ -1633,7 +1836,14 @@ ${lines.join("\n")}`;
|
|
|
1633
1836
|
durationMs: Date.now() - startedAt,
|
|
1634
1837
|
targetBudgetMs: this.targetRetrievalMs,
|
|
1635
1838
|
hardTimeoutMs: this.hardRetrievalTimeoutMs,
|
|
1636
|
-
branchStatus
|
|
1839
|
+
branchStatus,
|
|
1840
|
+
focusedScopeApplied: false,
|
|
1841
|
+
focusedSourceIds: [],
|
|
1842
|
+
focusedFileHints: [],
|
|
1843
|
+
clientScoped: false,
|
|
1844
|
+
fallbackUsed: false,
|
|
1845
|
+
droppedBelowFloor: 0,
|
|
1846
|
+
dedupedCount: reranked.dedupedCount
|
|
1637
1847
|
},
|
|
1638
1848
|
context: this.buildContext(ranked),
|
|
1639
1849
|
items: ranked
|
|
@@ -1642,100 +1852,195 @@ ${lines.join("\n")}`;
|
|
|
1642
1852
|
return prepared;
|
|
1643
1853
|
}
|
|
1644
1854
|
async beforeTurn(input, context = {}) {
|
|
1855
|
+
this.currentTurn += 1;
|
|
1645
1856
|
this.pushTouchedFiles(input.touchedFiles);
|
|
1857
|
+
this.refreshTaskSummary(input.taskSummary);
|
|
1646
1858
|
const { scope, warning } = await this.resolveScope(context);
|
|
1647
1859
|
const primaryQuery = compactWhitespace(input.userMessage);
|
|
1648
1860
|
const taskFrameQuery = this.makeTaskFrameQuery(input);
|
|
1861
|
+
const focusedScope = this.focusedScope(input);
|
|
1862
|
+
const focusedMetadataFilter = this.exactFileMetadataFilter(focusedScope.fileHints);
|
|
1863
|
+
const focusedScopeApplied = focusedScope.sourceIds.length > 0 || focusedScope.fileHints.length > 0 || Boolean(focusedScope.clientName);
|
|
1649
1864
|
const warnings = warning ? [warning] : [];
|
|
1650
1865
|
const startedAt = Date.now();
|
|
1651
|
-
const
|
|
1652
|
-
|
|
1866
|
+
const branchStatus = {};
|
|
1867
|
+
const collectFromBranches = (branches, pass) => {
|
|
1868
|
+
const collected = [];
|
|
1869
|
+
let okCount = 0;
|
|
1870
|
+
for (const branch of branches) {
|
|
1871
|
+
branchStatus[branch.name] = branch.status;
|
|
1872
|
+
if (branch.status !== "ok") {
|
|
1873
|
+
if (branch.status !== "skipped" && branch.reason) warnings.push(`${branch.name}:${branch.reason}`);
|
|
1874
|
+
continue;
|
|
1875
|
+
}
|
|
1876
|
+
okCount += 1;
|
|
1877
|
+
if (branch.name.startsWith("context")) {
|
|
1878
|
+
collected.push(...this.contextItems(
|
|
1879
|
+
branch.value,
|
|
1880
|
+
branch.name.includes("task_frame") ? "task_frame" : "primary",
|
|
1881
|
+
pass
|
|
1882
|
+
));
|
|
1883
|
+
} else {
|
|
1884
|
+
const memoryItems = this.memoryItems(
|
|
1885
|
+
branch.value,
|
|
1886
|
+
branch.name.includes("task_frame") ? "task_frame" : "primary",
|
|
1887
|
+
pass
|
|
1888
|
+
);
|
|
1889
|
+
collected.push(...pass === "focused" ? this.narrowFocusedMemories(memoryItems, focusedScope) : memoryItems);
|
|
1890
|
+
}
|
|
1891
|
+
}
|
|
1892
|
+
return { collected, okCount };
|
|
1893
|
+
};
|
|
1894
|
+
const focusedBranches = await Promise.all([
|
|
1895
|
+
this.runBranch("context_primary_focused", () => this.args.adapter.query({
|
|
1653
1896
|
project: scope.project,
|
|
1654
1897
|
query: primaryQuery,
|
|
1655
|
-
top_k: this.
|
|
1898
|
+
top_k: this.focusedTopK,
|
|
1656
1899
|
include_memories: false,
|
|
1657
1900
|
user_id: scope.userId,
|
|
1658
1901
|
session_id: scope.sessionId,
|
|
1902
|
+
source_ids: focusedScope.sourceIds.length > 0 ? focusedScope.sourceIds : void 0,
|
|
1903
|
+
metadata_filter: focusedMetadataFilter,
|
|
1659
1904
|
max_tokens: this.maxTokens,
|
|
1660
1905
|
compress: true,
|
|
1661
1906
|
compression_strategy: "adaptive"
|
|
1662
1907
|
})),
|
|
1663
|
-
this.runBranch("
|
|
1908
|
+
this.runBranch("memory_primary_focused", () => this.args.adapter.searchMemories({
|
|
1664
1909
|
project: scope.project,
|
|
1665
1910
|
query: primaryQuery,
|
|
1666
1911
|
user_id: scope.userId,
|
|
1667
1912
|
session_id: scope.sessionId,
|
|
1668
|
-
top_k: this.
|
|
1913
|
+
top_k: this.focusedTopK,
|
|
1669
1914
|
include_pending: true,
|
|
1670
1915
|
profile: "balanced"
|
|
1671
1916
|
})),
|
|
1672
|
-
taskFrameQuery ? this.runBranch("
|
|
1917
|
+
taskFrameQuery ? this.runBranch("context_task_frame_focused", () => this.args.adapter.query({
|
|
1673
1918
|
project: scope.project,
|
|
1674
1919
|
query: taskFrameQuery,
|
|
1675
|
-
top_k: this.
|
|
1920
|
+
top_k: this.focusedTopK,
|
|
1676
1921
|
include_memories: false,
|
|
1677
1922
|
user_id: scope.userId,
|
|
1678
1923
|
session_id: scope.sessionId,
|
|
1924
|
+
source_ids: focusedScope.sourceIds.length > 0 ? focusedScope.sourceIds : void 0,
|
|
1925
|
+
metadata_filter: focusedMetadataFilter,
|
|
1679
1926
|
max_tokens: this.maxTokens,
|
|
1680
1927
|
compress: true,
|
|
1681
1928
|
compression_strategy: "adaptive"
|
|
1682
|
-
})) : Promise.resolve({
|
|
1683
|
-
|
|
1684
|
-
status: "skipped",
|
|
1685
|
-
durationMs: 0
|
|
1686
|
-
}),
|
|
1687
|
-
taskFrameQuery ? this.runBranch("memory_task_frame", () => this.args.adapter.searchMemories({
|
|
1929
|
+
})) : Promise.resolve({ name: "context_task_frame_focused", status: "skipped", durationMs: 0 }),
|
|
1930
|
+
taskFrameQuery ? this.runBranch("memory_task_frame_focused", () => this.args.adapter.searchMemories({
|
|
1688
1931
|
project: scope.project,
|
|
1689
1932
|
query: taskFrameQuery,
|
|
1690
1933
|
user_id: scope.userId,
|
|
1691
1934
|
session_id: scope.sessionId,
|
|
1692
|
-
top_k: this.
|
|
1935
|
+
top_k: this.focusedTopK,
|
|
1693
1936
|
include_pending: true,
|
|
1694
1937
|
profile: "balanced"
|
|
1695
|
-
})) : Promise.resolve({
|
|
1696
|
-
name: "memory_task_frame",
|
|
1697
|
-
status: "skipped",
|
|
1698
|
-
durationMs: 0
|
|
1699
|
-
})
|
|
1938
|
+
})) : Promise.resolve({ name: "memory_task_frame_focused", status: "skipped", durationMs: 0 })
|
|
1700
1939
|
]);
|
|
1701
|
-
const
|
|
1702
|
-
const
|
|
1703
|
-
|
|
1704
|
-
|
|
1940
|
+
const focusedCollected = collectFromBranches(focusedBranches, "focused");
|
|
1941
|
+
const focusedRanked = this.rerank(focusedCollected.collected, focusedScope);
|
|
1942
|
+
const focusedFloored = this.applyRelevanceFloor(focusedRanked.items);
|
|
1943
|
+
let allCollected = [...focusedFloored.items];
|
|
1944
|
+
let totalOkCount = focusedCollected.okCount;
|
|
1945
|
+
let dedupedCount = focusedRanked.dedupedCount;
|
|
1946
|
+
let droppedBelowFloor = focusedFloored.dropped;
|
|
1947
|
+
const focusedTopScore = focusedFloored.items[0]?.score ?? 0;
|
|
1948
|
+
const fallbackUsed = focusedFloored.items.length < this.minFocusedResults || focusedTopScore < this.minFocusedTopScore;
|
|
1949
|
+
if (focusedScopeApplied) {
|
|
1950
|
+
this.sourceScopedTurns += 1;
|
|
1951
|
+
}
|
|
1952
|
+
if (!fallbackUsed) {
|
|
1953
|
+
this.focusedPassHits += 1;
|
|
1954
|
+
}
|
|
1955
|
+
const broadBranches = fallbackUsed ? await Promise.all([
|
|
1956
|
+
this.runBranch("context_primary_broad", () => this.args.adapter.query({
|
|
1957
|
+
project: scope.project,
|
|
1958
|
+
query: primaryQuery,
|
|
1959
|
+
top_k: this.broadTopK,
|
|
1960
|
+
include_memories: false,
|
|
1961
|
+
user_id: scope.userId,
|
|
1962
|
+
session_id: scope.sessionId,
|
|
1963
|
+
max_tokens: this.maxTokens,
|
|
1964
|
+
compress: true,
|
|
1965
|
+
compression_strategy: "adaptive"
|
|
1966
|
+
})),
|
|
1967
|
+
this.runBranch("memory_primary_broad", () => this.args.adapter.searchMemories({
|
|
1968
|
+
project: scope.project,
|
|
1969
|
+
query: primaryQuery,
|
|
1970
|
+
user_id: scope.userId,
|
|
1971
|
+
session_id: scope.sessionId,
|
|
1972
|
+
top_k: this.broadTopK,
|
|
1973
|
+
include_pending: true,
|
|
1974
|
+
profile: "balanced"
|
|
1975
|
+
})),
|
|
1976
|
+
taskFrameQuery ? this.runBranch("context_task_frame_broad", () => this.args.adapter.query({
|
|
1977
|
+
project: scope.project,
|
|
1978
|
+
query: taskFrameQuery,
|
|
1979
|
+
top_k: this.broadTopK,
|
|
1980
|
+
include_memories: false,
|
|
1981
|
+
user_id: scope.userId,
|
|
1982
|
+
session_id: scope.sessionId,
|
|
1983
|
+
max_tokens: this.maxTokens,
|
|
1984
|
+
compress: true,
|
|
1985
|
+
compression_strategy: "adaptive"
|
|
1986
|
+
})) : Promise.resolve({ name: "context_task_frame_broad", status: "skipped", durationMs: 0 }),
|
|
1987
|
+
taskFrameQuery ? this.runBranch("memory_task_frame_broad", () => this.args.adapter.searchMemories({
|
|
1988
|
+
project: scope.project,
|
|
1989
|
+
query: taskFrameQuery,
|
|
1990
|
+
user_id: scope.userId,
|
|
1991
|
+
session_id: scope.sessionId,
|
|
1992
|
+
top_k: this.broadTopK,
|
|
1993
|
+
include_pending: true,
|
|
1994
|
+
profile: "balanced"
|
|
1995
|
+
})) : Promise.resolve({ name: "memory_task_frame_broad", status: "skipped", durationMs: 0 })
|
|
1996
|
+
]) : [
|
|
1997
|
+
{ name: "context_primary_broad", status: "skipped", durationMs: 0 },
|
|
1998
|
+
{ name: "memory_primary_broad", status: "skipped", durationMs: 0 },
|
|
1999
|
+
{ name: "context_task_frame_broad", status: "skipped", durationMs: 0 },
|
|
2000
|
+
{ name: "memory_task_frame_broad", status: "skipped", durationMs: 0 }
|
|
2001
|
+
];
|
|
2002
|
+
const broadCollected = collectFromBranches(broadBranches, "broad");
|
|
2003
|
+
totalOkCount += broadCollected.okCount;
|
|
2004
|
+
if (fallbackUsed) {
|
|
2005
|
+
this.fallbackTriggers += 1;
|
|
2006
|
+
this.broadScopedTurns += 1;
|
|
2007
|
+
allCollected = [...allCollected, ...broadCollected.collected];
|
|
2008
|
+
}
|
|
2009
|
+
const ranked = this.rerank(allCollected, focusedScope);
|
|
2010
|
+
dedupedCount += ranked.dedupedCount;
|
|
2011
|
+
const floored = this.applyRelevanceFloor(ranked.items);
|
|
2012
|
+
droppedBelowFloor += floored.dropped;
|
|
2013
|
+
this.floorDroppedCount += droppedBelowFloor;
|
|
2014
|
+
this.droppedCount += droppedBelowFloor;
|
|
2015
|
+
const finalItems = floored.items.slice(0, this.broadTopK);
|
|
2016
|
+
this.injectedItemCount += finalItems.length;
|
|
2017
|
+
this.totalTurns += 1;
|
|
2018
|
+
const executedBranches = [...focusedBranches, ...broadBranches].filter((branch) => branch.status !== "skipped");
|
|
2019
|
+
for (const branch of [...focusedBranches, ...broadBranches]) {
|
|
1705
2020
|
branchStatus[branch.name] = branch.status;
|
|
1706
|
-
if (branch.status !== "ok") {
|
|
1707
|
-
if (branch.status !== "skipped" && branch.reason) warnings.push(`${branch.name}:${branch.reason}`);
|
|
1708
|
-
continue;
|
|
1709
|
-
}
|
|
1710
|
-
okCount += 1;
|
|
1711
|
-
if (branch.name.startsWith("context")) {
|
|
1712
|
-
collected.push(...this.contextItems(
|
|
1713
|
-
branch.value,
|
|
1714
|
-
branch.name.includes("task_frame") ? "task_frame" : "primary"
|
|
1715
|
-
));
|
|
1716
|
-
} else {
|
|
1717
|
-
collected.push(...this.memoryItems(
|
|
1718
|
-
branch.value,
|
|
1719
|
-
branch.name.includes("task_frame") ? "task_frame" : "primary"
|
|
1720
|
-
));
|
|
1721
|
-
}
|
|
1722
2021
|
}
|
|
1723
|
-
const ranked = this.rerank(collected).slice(0, this.topK * 2);
|
|
1724
2022
|
const prepared = {
|
|
1725
2023
|
scope,
|
|
1726
2024
|
retrieval: {
|
|
1727
2025
|
primaryQuery,
|
|
1728
2026
|
taskFrameQuery,
|
|
1729
2027
|
warnings,
|
|
1730
|
-
degraded:
|
|
1731
|
-
degradedReason:
|
|
2028
|
+
degraded: totalOkCount < executedBranches.length,
|
|
2029
|
+
degradedReason: totalOkCount === 0 ? "all_retrieval_failed" : warnings.length > 0 ? "partial_retrieval_failed" : void 0,
|
|
1732
2030
|
durationMs: Date.now() - startedAt,
|
|
1733
2031
|
targetBudgetMs: this.targetRetrievalMs,
|
|
1734
2032
|
hardTimeoutMs: this.hardRetrievalTimeoutMs,
|
|
1735
|
-
branchStatus
|
|
2033
|
+
branchStatus,
|
|
2034
|
+
focusedScopeApplied,
|
|
2035
|
+
focusedSourceIds: focusedScope.sourceIds,
|
|
2036
|
+
focusedFileHints: focusedScope.fileHints.map((value) => pathBase(value)),
|
|
2037
|
+
clientScoped: Boolean(focusedScope.clientName),
|
|
2038
|
+
fallbackUsed,
|
|
2039
|
+
droppedBelowFloor,
|
|
2040
|
+
dedupedCount
|
|
1736
2041
|
},
|
|
1737
|
-
context: this.buildContext(
|
|
1738
|
-
items:
|
|
2042
|
+
context: this.buildContext(finalItems),
|
|
2043
|
+
items: finalItems
|
|
1739
2044
|
};
|
|
1740
2045
|
this.lastPreparedTurn = prepared.retrieval;
|
|
1741
2046
|
return prepared;
|
|
@@ -1830,7 +2135,14 @@ ${lines.join("\n")}`;
|
|
|
1830
2135
|
counters: {
|
|
1831
2136
|
mergedCount: this.mergedCount,
|
|
1832
2137
|
droppedCount: this.droppedCount,
|
|
1833
|
-
bufferedLowSalience: this.bufferedLowSalience.length
|
|
2138
|
+
bufferedLowSalience: this.bufferedLowSalience.length,
|
|
2139
|
+
focusedPassHits: this.focusedPassHits,
|
|
2140
|
+
fallbackTriggers: this.fallbackTriggers,
|
|
2141
|
+
floorDroppedCount: this.floorDroppedCount,
|
|
2142
|
+
injectedItemCount: this.injectedItemCount,
|
|
2143
|
+
sourceScopedTurns: this.sourceScopedTurns,
|
|
2144
|
+
broadScopedTurns: this.broadScopedTurns,
|
|
2145
|
+
totalTurns: this.totalTurns
|
|
1834
2146
|
}
|
|
1835
2147
|
};
|
|
1836
2148
|
}
|
|
@@ -3539,6 +3851,19 @@ var WhisperContext = class _WhisperContext {
|
|
|
3539
3851
|
}
|
|
3540
3852
|
]);
|
|
3541
3853
|
}
|
|
3854
|
+
async listMemories(params) {
|
|
3855
|
+
const projectRef = this.getRequiredProject(params.project);
|
|
3856
|
+
return this.withProjectRefFallback(projectRef, async (project) => {
|
|
3857
|
+
const query = new URLSearchParams({
|
|
3858
|
+
project,
|
|
3859
|
+
...params.user_id ? { user_id: params.user_id } : {},
|
|
3860
|
+
...params.session_id ? { session_id: params.session_id } : {},
|
|
3861
|
+
...params.agent_id ? { agent_id: params.agent_id } : {},
|
|
3862
|
+
limit: String(Math.min(Math.max(params.limit ?? 200, 1), 200))
|
|
3863
|
+
});
|
|
3864
|
+
return this.request(`/v1/memories?${query.toString()}`, { method: "GET" });
|
|
3865
|
+
});
|
|
3866
|
+
}
|
|
3542
3867
|
async addMemory(params) {
|
|
3543
3868
|
const projectRef = this.getRequiredProject(params.project);
|
|
3544
3869
|
return this.withProjectRefFallback(projectRef, async (project) => {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@usewhisper/sdk",
|
|
3
|
-
"version": "3.
|
|
4
|
-
"whisperContractVersion": "2026.03.
|
|
3
|
+
"version": "3.8.0",
|
|
4
|
+
"whisperContractVersion": "2026.03.10",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"build": "tsup ../src/sdk/index.ts --format esm,cjs --dts --out-dir .",
|
|
7
7
|
"prepublishOnly": "npm run build"
|