@usewhisper/sdk 3.4.0 → 3.5.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/index.d.mts +242 -1
- package/index.d.ts +242 -1
- package/index.js +755 -11
- package/index.mjs +755 -11
- package/package.json +1 -1
package/index.mjs
CHANGED
|
@@ -1550,7 +1550,582 @@ var AnalyticsModule = class {
|
|
|
1550
1550
|
}
|
|
1551
1551
|
};
|
|
1552
1552
|
|
|
1553
|
+
// ../src/sdk/agent-runtime.ts
|
|
1554
|
+
function detectBrowserStorage() {
|
|
1555
|
+
const maybeStorage = globalThis.localStorage;
|
|
1556
|
+
if (!maybeStorage || typeof maybeStorage !== "object") return null;
|
|
1557
|
+
const candidate = maybeStorage;
|
|
1558
|
+
if (typeof candidate.getItem !== "function" || typeof candidate.setItem !== "function") {
|
|
1559
|
+
return null;
|
|
1560
|
+
}
|
|
1561
|
+
return {
|
|
1562
|
+
getItem: candidate.getItem,
|
|
1563
|
+
setItem: candidate.setItem
|
|
1564
|
+
};
|
|
1565
|
+
}
|
|
1566
|
+
function createBindingStore(filePath) {
|
|
1567
|
+
const storage = detectBrowserStorage();
|
|
1568
|
+
if (storage) {
|
|
1569
|
+
const key = "whisper_agent_runtime_bindings";
|
|
1570
|
+
return {
|
|
1571
|
+
async load() {
|
|
1572
|
+
const raw = storage.getItem(key);
|
|
1573
|
+
if (!raw) return {};
|
|
1574
|
+
try {
|
|
1575
|
+
const parsed = JSON.parse(raw);
|
|
1576
|
+
return parsed && typeof parsed === "object" ? parsed : {};
|
|
1577
|
+
} catch {
|
|
1578
|
+
return {};
|
|
1579
|
+
}
|
|
1580
|
+
},
|
|
1581
|
+
async save(bindings) {
|
|
1582
|
+
storage.setItem(key, JSON.stringify(bindings));
|
|
1583
|
+
}
|
|
1584
|
+
};
|
|
1585
|
+
}
|
|
1586
|
+
return {
|
|
1587
|
+
async load() {
|
|
1588
|
+
if (typeof process === "undefined") return {};
|
|
1589
|
+
const fs = await import("fs/promises");
|
|
1590
|
+
const path = filePath || `${process.env.USERPROFILE || process.env.HOME || "."}/.whisper/sdk/agent-bindings.json`;
|
|
1591
|
+
try {
|
|
1592
|
+
const raw = await fs.readFile(path, "utf8");
|
|
1593
|
+
const parsed = JSON.parse(raw);
|
|
1594
|
+
return parsed && typeof parsed === "object" ? parsed : {};
|
|
1595
|
+
} catch {
|
|
1596
|
+
return {};
|
|
1597
|
+
}
|
|
1598
|
+
},
|
|
1599
|
+
async save(bindings) {
|
|
1600
|
+
if (typeof process === "undefined") return;
|
|
1601
|
+
const fs = await import("fs/promises");
|
|
1602
|
+
const pathMod = await import("path");
|
|
1603
|
+
const path = filePath || `${process.env.USERPROFILE || process.env.HOME || "."}/.whisper/sdk/agent-bindings.json`;
|
|
1604
|
+
await fs.mkdir(pathMod.dirname(path), { recursive: true });
|
|
1605
|
+
await fs.writeFile(path, JSON.stringify(bindings), "utf8");
|
|
1606
|
+
}
|
|
1607
|
+
};
|
|
1608
|
+
}
|
|
1609
|
+
function normalizeWorkspacePath(value) {
|
|
1610
|
+
const trimmed = value?.trim();
|
|
1611
|
+
if (!trimmed) return null;
|
|
1612
|
+
return trimmed.replace(/\\/g, "/").replace(/\/+$/, "").toLowerCase();
|
|
1613
|
+
}
|
|
1614
|
+
function pathBase(value) {
|
|
1615
|
+
const normalized = value.replace(/\\/g, "/");
|
|
1616
|
+
const parts = normalized.split("/").filter(Boolean);
|
|
1617
|
+
return parts[parts.length - 1] || normalized;
|
|
1618
|
+
}
|
|
1619
|
+
function defaultSalience(kind, success) {
|
|
1620
|
+
if (kind === "decision" || kind === "constraint" || kind === "failure") return "high";
|
|
1621
|
+
if (kind === "outcome" || kind === "task_update") return "medium";
|
|
1622
|
+
if (kind === "tool_result" && success === false) return "high";
|
|
1623
|
+
return "low";
|
|
1624
|
+
}
|
|
1625
|
+
function toMemoryType(kind) {
|
|
1626
|
+
if (kind === "decision" || kind === "constraint") return "instruction";
|
|
1627
|
+
return "event";
|
|
1628
|
+
}
|
|
1629
|
+
function summarizeLowSalience(events) {
|
|
1630
|
+
const lines = events.slice(-10).map((event) => {
|
|
1631
|
+
const fileSuffix = event.filePaths?.length ? ` [files: ${event.filePaths.join(", ")}]` : "";
|
|
1632
|
+
const toolSuffix = event.toolName ? ` [tool: ${event.toolName}]` : "";
|
|
1633
|
+
return `- ${event.kind}: ${event.summary}${fileSuffix}${toolSuffix}`;
|
|
1634
|
+
});
|
|
1635
|
+
return `Recent low-salience work:
|
|
1636
|
+
${lines.join("\n")}`;
|
|
1637
|
+
}
|
|
1638
|
+
function compactWhitespace(value) {
|
|
1639
|
+
return value.replace(/\s+/g, " ").trim();
|
|
1640
|
+
}
|
|
1641
|
+
function withTimeout(promise, timeoutMs) {
|
|
1642
|
+
return new Promise((resolve, reject) => {
|
|
1643
|
+
const timeout = setTimeout(() => {
|
|
1644
|
+
reject(new Error("timeout"));
|
|
1645
|
+
}, timeoutMs);
|
|
1646
|
+
promise.then(
|
|
1647
|
+
(value) => {
|
|
1648
|
+
clearTimeout(timeout);
|
|
1649
|
+
resolve(value);
|
|
1650
|
+
},
|
|
1651
|
+
(error) => {
|
|
1652
|
+
clearTimeout(timeout);
|
|
1653
|
+
reject(error);
|
|
1654
|
+
}
|
|
1655
|
+
);
|
|
1656
|
+
});
|
|
1657
|
+
}
|
|
1658
|
+
function extractTimestamp(metadata) {
|
|
1659
|
+
const candidates = [
|
|
1660
|
+
metadata?.updatedAt,
|
|
1661
|
+
metadata?.createdAt,
|
|
1662
|
+
metadata?.timestamp,
|
|
1663
|
+
metadata?.event_date,
|
|
1664
|
+
metadata?.eventDate
|
|
1665
|
+
];
|
|
1666
|
+
for (const value of candidates) {
|
|
1667
|
+
if (typeof value === "string") {
|
|
1668
|
+
const parsed = Date.parse(value);
|
|
1669
|
+
if (!Number.isNaN(parsed)) return parsed;
|
|
1670
|
+
}
|
|
1671
|
+
}
|
|
1672
|
+
return 0;
|
|
1673
|
+
}
|
|
1674
|
+
function salienceBoost(metadata) {
|
|
1675
|
+
const value = metadata?.salience;
|
|
1676
|
+
if (value === "high") return 0.12;
|
|
1677
|
+
if (value === "medium") return 0.06;
|
|
1678
|
+
return 0;
|
|
1679
|
+
}
|
|
1680
|
+
var WhisperAgentRuntime = class {
|
|
1681
|
+
constructor(args) {
|
|
1682
|
+
this.args = args;
|
|
1683
|
+
this.bindingStore = createBindingStore(args.options.bindingStorePath);
|
|
1684
|
+
this.topK = args.options.topK ?? 6;
|
|
1685
|
+
this.maxTokens = args.options.maxTokens ?? 4e3;
|
|
1686
|
+
this.targetRetrievalMs = args.options.targetRetrievalMs ?? 2500;
|
|
1687
|
+
this.hardRetrievalTimeoutMs = args.options.hardRetrievalTimeoutMs ?? 4e3;
|
|
1688
|
+
this.recentWorkLimit = args.options.recentWorkLimit ?? 40;
|
|
1689
|
+
this.baseContext = args.baseContext;
|
|
1690
|
+
this.clientName = args.baseContext.clientName || "whisper-agent-runtime";
|
|
1691
|
+
}
|
|
1692
|
+
bindingStore;
|
|
1693
|
+
topK;
|
|
1694
|
+
maxTokens;
|
|
1695
|
+
targetRetrievalMs;
|
|
1696
|
+
hardRetrievalTimeoutMs;
|
|
1697
|
+
recentWorkLimit;
|
|
1698
|
+
baseContext;
|
|
1699
|
+
clientName;
|
|
1700
|
+
bindings = null;
|
|
1701
|
+
touchedFiles = [];
|
|
1702
|
+
recentWork = [];
|
|
1703
|
+
bufferedLowSalience = [];
|
|
1704
|
+
lastPreparedTurn = null;
|
|
1705
|
+
mergedCount = 0;
|
|
1706
|
+
droppedCount = 0;
|
|
1707
|
+
lastScope = {};
|
|
1708
|
+
async getBindings() {
|
|
1709
|
+
if (!this.bindings) {
|
|
1710
|
+
this.bindings = await this.bindingStore.load();
|
|
1711
|
+
}
|
|
1712
|
+
return this.bindings;
|
|
1713
|
+
}
|
|
1714
|
+
pushTouchedFiles(paths) {
|
|
1715
|
+
if (!paths || paths.length === 0) return;
|
|
1716
|
+
for (const path of paths) {
|
|
1717
|
+
if (!path) continue;
|
|
1718
|
+
this.touchedFiles = [...this.touchedFiles.filter((entry) => entry !== path), path].slice(-20);
|
|
1719
|
+
}
|
|
1720
|
+
}
|
|
1721
|
+
pushWorkEvent(event) {
|
|
1722
|
+
this.recentWork = [...this.recentWork, event].slice(-this.recentWorkLimit);
|
|
1723
|
+
}
|
|
1724
|
+
makeTaskFrameQuery(input) {
|
|
1725
|
+
const task = compactWhitespace(input.taskSummary || "");
|
|
1726
|
+
const salient = this.recentWork.filter((event) => event.salience === "high").slice(-3).map((event) => `${event.kind}: ${event.summary}`);
|
|
1727
|
+
const files = [...input.touchedFiles || [], ...this.touchedFiles].slice(-3).map((file) => pathBase(file));
|
|
1728
|
+
const parts = [
|
|
1729
|
+
task ? `task ${task}` : "",
|
|
1730
|
+
salient.length > 0 ? `recent ${salient.join(" ; ")}` : "",
|
|
1731
|
+
files.length > 0 ? `files ${files.join(" ")}` : "",
|
|
1732
|
+
input.toolContext ? `tool context ${compactWhitespace(input.toolContext)}` : ""
|
|
1733
|
+
].filter(Boolean);
|
|
1734
|
+
if (parts.length === 0) return null;
|
|
1735
|
+
return parts.join(" | ");
|
|
1736
|
+
}
|
|
1737
|
+
async resolveScope(overrides) {
|
|
1738
|
+
const merged = {
|
|
1739
|
+
...this.baseContext,
|
|
1740
|
+
...overrides
|
|
1741
|
+
};
|
|
1742
|
+
const normalizedWorkspace = normalizeWorkspacePath(merged.workspacePath);
|
|
1743
|
+
const bindings = await this.getBindings();
|
|
1744
|
+
const workspaceProject = normalizedWorkspace ? bindings[normalizedWorkspace] : void 0;
|
|
1745
|
+
const configuredProject = merged.project;
|
|
1746
|
+
let projectRef = configuredProject;
|
|
1747
|
+
let projectSource = overrides?.project ? "explicit" : "generated";
|
|
1748
|
+
let warning;
|
|
1749
|
+
if (workspaceProject) {
|
|
1750
|
+
projectRef = workspaceProject;
|
|
1751
|
+
projectSource = "workspace";
|
|
1752
|
+
if (configuredProject && workspaceProject !== configuredProject) {
|
|
1753
|
+
warning = `workspace mapping '${workspaceProject}' overrides configured project '${configuredProject}'`;
|
|
1754
|
+
}
|
|
1755
|
+
} else if (configuredProject) {
|
|
1756
|
+
projectRef = configuredProject;
|
|
1757
|
+
projectSource = overrides?.project ? "explicit" : "config";
|
|
1758
|
+
}
|
|
1759
|
+
const project = (await this.args.adapter.resolveProject(projectRef)).id;
|
|
1760
|
+
if (normalizedWorkspace) {
|
|
1761
|
+
bindings[normalizedWorkspace] = project;
|
|
1762
|
+
await this.bindingStore.save(bindings);
|
|
1763
|
+
}
|
|
1764
|
+
const scope = {
|
|
1765
|
+
...merged,
|
|
1766
|
+
project,
|
|
1767
|
+
userId: merged.userId || `${this.clientName}-user`,
|
|
1768
|
+
sessionId: merged.sessionId || `sess_${stableHash(`${this.clientName}_${normalizedWorkspace || "default"}`)}`
|
|
1769
|
+
};
|
|
1770
|
+
this.lastScope = {
|
|
1771
|
+
project: scope.project,
|
|
1772
|
+
userId: scope.userId,
|
|
1773
|
+
sessionId: scope.sessionId,
|
|
1774
|
+
source: projectSource,
|
|
1775
|
+
warning
|
|
1776
|
+
};
|
|
1777
|
+
return { scope, projectSource, warning };
|
|
1778
|
+
}
|
|
1779
|
+
async runBranch(name, task) {
|
|
1780
|
+
const startedAt = Date.now();
|
|
1781
|
+
try {
|
|
1782
|
+
const value = await withTimeout(task(), this.hardRetrievalTimeoutMs);
|
|
1783
|
+
return {
|
|
1784
|
+
name,
|
|
1785
|
+
status: "ok",
|
|
1786
|
+
durationMs: Date.now() - startedAt,
|
|
1787
|
+
value
|
|
1788
|
+
};
|
|
1789
|
+
} catch (error) {
|
|
1790
|
+
const durationMs = Date.now() - startedAt;
|
|
1791
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
1792
|
+
return {
|
|
1793
|
+
name,
|
|
1794
|
+
status: reason === "timeout" ? "timeout" : "error",
|
|
1795
|
+
durationMs,
|
|
1796
|
+
reason
|
|
1797
|
+
};
|
|
1798
|
+
}
|
|
1799
|
+
}
|
|
1800
|
+
contextItems(result, sourceQuery) {
|
|
1801
|
+
return (result.results || []).map((item) => ({
|
|
1802
|
+
id: item.id,
|
|
1803
|
+
content: item.content,
|
|
1804
|
+
type: "project",
|
|
1805
|
+
score: item.score ?? 0,
|
|
1806
|
+
sourceQuery,
|
|
1807
|
+
metadata: item.metadata || {}
|
|
1808
|
+
}));
|
|
1809
|
+
}
|
|
1810
|
+
memoryItems(result, sourceQuery) {
|
|
1811
|
+
return (result.results || []).map((item, index) => ({
|
|
1812
|
+
id: item.memory?.id || item.chunk?.id || `${sourceQuery}_memory_${index}`,
|
|
1813
|
+
content: item.chunk?.content || item.memory?.content || "",
|
|
1814
|
+
type: "memory",
|
|
1815
|
+
score: item.similarity ?? 0,
|
|
1816
|
+
sourceQuery,
|
|
1817
|
+
metadata: {
|
|
1818
|
+
...item.chunk?.metadata || {},
|
|
1819
|
+
...item.memory?.temporal || {},
|
|
1820
|
+
confidence: item.memory?.confidence
|
|
1821
|
+
}
|
|
1822
|
+
})).filter((item) => item.content);
|
|
1823
|
+
}
|
|
1824
|
+
rerank(items) {
|
|
1825
|
+
const deduped = /* @__PURE__ */ new Map();
|
|
1826
|
+
for (const item of items) {
|
|
1827
|
+
const key = `${item.id}:${item.content.toLowerCase()}`;
|
|
1828
|
+
const recency = extractTimestamp(item.metadata) > 0 ? 0.04 : 0;
|
|
1829
|
+
const queryBonus = item.sourceQuery === "primary" ? 0.08 : item.sourceQuery === "task_frame" ? 0.04 : 0.03;
|
|
1830
|
+
const next = {
|
|
1831
|
+
...item,
|
|
1832
|
+
score: item.score + queryBonus + salienceBoost(item.metadata) + recency
|
|
1833
|
+
};
|
|
1834
|
+
const existing = deduped.get(key);
|
|
1835
|
+
if (!existing || next.score > existing.score) {
|
|
1836
|
+
deduped.set(key, next);
|
|
1837
|
+
}
|
|
1838
|
+
}
|
|
1839
|
+
return [...deduped.values()].sort((left, right) => right.score - left.score);
|
|
1840
|
+
}
|
|
1841
|
+
buildContext(items) {
|
|
1842
|
+
const maxChars = this.maxTokens * 4;
|
|
1843
|
+
const lines = [];
|
|
1844
|
+
let used = 0;
|
|
1845
|
+
for (const item of items) {
|
|
1846
|
+
const label = item.type === "memory" ? "memory" : "context";
|
|
1847
|
+
const content = compactWhitespace(item.content);
|
|
1848
|
+
if (!content) continue;
|
|
1849
|
+
const line = `[${label}] ${content}`;
|
|
1850
|
+
if (used + line.length > maxChars) break;
|
|
1851
|
+
lines.push(line);
|
|
1852
|
+
used += line.length;
|
|
1853
|
+
}
|
|
1854
|
+
if (lines.length === 0) return "";
|
|
1855
|
+
return `Relevant context:
|
|
1856
|
+
${lines.join("\n")}`;
|
|
1857
|
+
}
|
|
1858
|
+
async bootstrap(context = {}) {
|
|
1859
|
+
const { scope, warning } = await this.resolveScope(context);
|
|
1860
|
+
const warnings = warning ? [warning] : [];
|
|
1861
|
+
const startedAt = Date.now();
|
|
1862
|
+
const branches = await Promise.all([
|
|
1863
|
+
this.runBranch("session_recent", () => this.args.adapter.getSessionMemories({
|
|
1864
|
+
project: scope.project,
|
|
1865
|
+
session_id: scope.sessionId,
|
|
1866
|
+
include_pending: true,
|
|
1867
|
+
limit: 12
|
|
1868
|
+
})),
|
|
1869
|
+
this.runBranch("user_profile", () => scope.userId ? this.args.adapter.getUserProfile({
|
|
1870
|
+
project: scope.project,
|
|
1871
|
+
user_id: scope.userId,
|
|
1872
|
+
include_pending: true,
|
|
1873
|
+
memory_types: "preference,instruction,goal"
|
|
1874
|
+
}) : Promise.resolve({ user_id: scope.userId, memories: [], count: 0 })),
|
|
1875
|
+
this.runBranch("project_rules", () => this.args.adapter.query({
|
|
1876
|
+
project: scope.project,
|
|
1877
|
+
query: "project rules instructions constraints conventions open threads",
|
|
1878
|
+
top_k: this.topK,
|
|
1879
|
+
include_memories: false,
|
|
1880
|
+
user_id: scope.userId,
|
|
1881
|
+
session_id: scope.sessionId,
|
|
1882
|
+
max_tokens: this.maxTokens,
|
|
1883
|
+
compress: true,
|
|
1884
|
+
compression_strategy: "adaptive"
|
|
1885
|
+
}))
|
|
1886
|
+
]);
|
|
1887
|
+
const items = [];
|
|
1888
|
+
const branchStatus = {};
|
|
1889
|
+
for (const branch of branches) {
|
|
1890
|
+
branchStatus[branch.name] = branch.status;
|
|
1891
|
+
if (branch.status !== "ok") {
|
|
1892
|
+
if (branch.reason) warnings.push(`${branch.name}:${branch.reason}`);
|
|
1893
|
+
continue;
|
|
1894
|
+
}
|
|
1895
|
+
if (branch.name === "project_rules") {
|
|
1896
|
+
items.push(...this.contextItems(branch.value, "bootstrap"));
|
|
1897
|
+
continue;
|
|
1898
|
+
}
|
|
1899
|
+
const records = branch.value.memories || [];
|
|
1900
|
+
items.push(...records.map((memory, index) => ({
|
|
1901
|
+
id: String(memory.id || `${branch.name}_${index}`),
|
|
1902
|
+
content: String(memory.content || ""),
|
|
1903
|
+
type: "memory",
|
|
1904
|
+
score: 0.4,
|
|
1905
|
+
sourceQuery: "bootstrap",
|
|
1906
|
+
metadata: memory
|
|
1907
|
+
})).filter((item) => item.content));
|
|
1908
|
+
}
|
|
1909
|
+
const ranked = this.rerank(items).slice(0, this.topK * 2);
|
|
1910
|
+
const prepared = {
|
|
1911
|
+
scope,
|
|
1912
|
+
retrieval: {
|
|
1913
|
+
primaryQuery: "bootstrap",
|
|
1914
|
+
taskFrameQuery: null,
|
|
1915
|
+
warnings,
|
|
1916
|
+
degraded: warnings.length > 0,
|
|
1917
|
+
degradedReason: warnings.length > 0 ? "partial_bootstrap" : void 0,
|
|
1918
|
+
durationMs: Date.now() - startedAt,
|
|
1919
|
+
targetBudgetMs: this.targetRetrievalMs,
|
|
1920
|
+
hardTimeoutMs: this.hardRetrievalTimeoutMs,
|
|
1921
|
+
branchStatus
|
|
1922
|
+
},
|
|
1923
|
+
context: this.buildContext(ranked),
|
|
1924
|
+
items: ranked
|
|
1925
|
+
};
|
|
1926
|
+
this.lastPreparedTurn = prepared.retrieval;
|
|
1927
|
+
return prepared;
|
|
1928
|
+
}
|
|
1929
|
+
async beforeTurn(input, context = {}) {
|
|
1930
|
+
this.pushTouchedFiles(input.touchedFiles);
|
|
1931
|
+
const { scope, warning } = await this.resolveScope(context);
|
|
1932
|
+
const primaryQuery = compactWhitespace(input.userMessage);
|
|
1933
|
+
const taskFrameQuery = this.makeTaskFrameQuery(input);
|
|
1934
|
+
const warnings = warning ? [warning] : [];
|
|
1935
|
+
const startedAt = Date.now();
|
|
1936
|
+
const branches = await Promise.all([
|
|
1937
|
+
this.runBranch("context_primary", () => this.args.adapter.query({
|
|
1938
|
+
project: scope.project,
|
|
1939
|
+
query: primaryQuery,
|
|
1940
|
+
top_k: this.topK,
|
|
1941
|
+
include_memories: false,
|
|
1942
|
+
user_id: scope.userId,
|
|
1943
|
+
session_id: scope.sessionId,
|
|
1944
|
+
max_tokens: this.maxTokens,
|
|
1945
|
+
compress: true,
|
|
1946
|
+
compression_strategy: "adaptive"
|
|
1947
|
+
})),
|
|
1948
|
+
this.runBranch("memory_primary", () => this.args.adapter.searchMemories({
|
|
1949
|
+
project: scope.project,
|
|
1950
|
+
query: primaryQuery,
|
|
1951
|
+
user_id: scope.userId,
|
|
1952
|
+
session_id: scope.sessionId,
|
|
1953
|
+
top_k: this.topK,
|
|
1954
|
+
include_pending: true,
|
|
1955
|
+
profile: "balanced"
|
|
1956
|
+
})),
|
|
1957
|
+
taskFrameQuery ? this.runBranch("context_task_frame", () => this.args.adapter.query({
|
|
1958
|
+
project: scope.project,
|
|
1959
|
+
query: taskFrameQuery,
|
|
1960
|
+
top_k: this.topK,
|
|
1961
|
+
include_memories: false,
|
|
1962
|
+
user_id: scope.userId,
|
|
1963
|
+
session_id: scope.sessionId,
|
|
1964
|
+
max_tokens: this.maxTokens,
|
|
1965
|
+
compress: true,
|
|
1966
|
+
compression_strategy: "adaptive"
|
|
1967
|
+
})) : Promise.resolve({
|
|
1968
|
+
name: "context_task_frame",
|
|
1969
|
+
status: "skipped",
|
|
1970
|
+
durationMs: 0
|
|
1971
|
+
}),
|
|
1972
|
+
taskFrameQuery ? this.runBranch("memory_task_frame", () => this.args.adapter.searchMemories({
|
|
1973
|
+
project: scope.project,
|
|
1974
|
+
query: taskFrameQuery,
|
|
1975
|
+
user_id: scope.userId,
|
|
1976
|
+
session_id: scope.sessionId,
|
|
1977
|
+
top_k: this.topK,
|
|
1978
|
+
include_pending: true,
|
|
1979
|
+
profile: "balanced"
|
|
1980
|
+
})) : Promise.resolve({
|
|
1981
|
+
name: "memory_task_frame",
|
|
1982
|
+
status: "skipped",
|
|
1983
|
+
durationMs: 0
|
|
1984
|
+
})
|
|
1985
|
+
]);
|
|
1986
|
+
const branchStatus = {};
|
|
1987
|
+
const collected = [];
|
|
1988
|
+
let okCount = 0;
|
|
1989
|
+
for (const branch of branches) {
|
|
1990
|
+
branchStatus[branch.name] = branch.status;
|
|
1991
|
+
if (branch.status !== "ok") {
|
|
1992
|
+
if (branch.status !== "skipped" && branch.reason) warnings.push(`${branch.name}:${branch.reason}`);
|
|
1993
|
+
continue;
|
|
1994
|
+
}
|
|
1995
|
+
okCount += 1;
|
|
1996
|
+
if (branch.name.startsWith("context")) {
|
|
1997
|
+
collected.push(...this.contextItems(
|
|
1998
|
+
branch.value,
|
|
1999
|
+
branch.name.includes("task_frame") ? "task_frame" : "primary"
|
|
2000
|
+
));
|
|
2001
|
+
} else {
|
|
2002
|
+
collected.push(...this.memoryItems(
|
|
2003
|
+
branch.value,
|
|
2004
|
+
branch.name.includes("task_frame") ? "task_frame" : "primary"
|
|
2005
|
+
));
|
|
2006
|
+
}
|
|
2007
|
+
}
|
|
2008
|
+
const ranked = this.rerank(collected).slice(0, this.topK * 2);
|
|
2009
|
+
const prepared = {
|
|
2010
|
+
scope,
|
|
2011
|
+
retrieval: {
|
|
2012
|
+
primaryQuery,
|
|
2013
|
+
taskFrameQuery,
|
|
2014
|
+
warnings,
|
|
2015
|
+
degraded: okCount < branches.filter((branch) => branch.status !== "skipped").length,
|
|
2016
|
+
degradedReason: okCount === 0 ? "all_retrieval_failed" : warnings.length > 0 ? "partial_retrieval_failed" : void 0,
|
|
2017
|
+
durationMs: Date.now() - startedAt,
|
|
2018
|
+
targetBudgetMs: this.targetRetrievalMs,
|
|
2019
|
+
hardTimeoutMs: this.hardRetrievalTimeoutMs,
|
|
2020
|
+
branchStatus
|
|
2021
|
+
},
|
|
2022
|
+
context: this.buildContext(ranked),
|
|
2023
|
+
items: ranked
|
|
2024
|
+
};
|
|
2025
|
+
this.lastPreparedTurn = prepared.retrieval;
|
|
2026
|
+
return prepared;
|
|
2027
|
+
}
|
|
2028
|
+
async recordWork(event, context = {}) {
|
|
2029
|
+
const normalized = {
|
|
2030
|
+
...event,
|
|
2031
|
+
salience: event.salience || defaultSalience(event.kind, event.success),
|
|
2032
|
+
timestamp: event.timestamp || nowIso()
|
|
2033
|
+
};
|
|
2034
|
+
this.pushTouchedFiles(normalized.filePaths);
|
|
2035
|
+
this.pushWorkEvent(normalized);
|
|
2036
|
+
if (normalized.salience === "low") {
|
|
2037
|
+
this.bufferedLowSalience = [...this.bufferedLowSalience, normalized].slice(-20);
|
|
2038
|
+
return { success: true, buffered: true };
|
|
2039
|
+
}
|
|
2040
|
+
const { scope } = await this.resolveScope(context);
|
|
2041
|
+
return this.args.adapter.addMemory({
|
|
2042
|
+
project: scope.project,
|
|
2043
|
+
user_id: scope.userId,
|
|
2044
|
+
session_id: scope.sessionId,
|
|
2045
|
+
content: `${normalized.kind}: ${normalized.summary}${normalized.details ? ` (${normalized.details})` : ""}`,
|
|
2046
|
+
memory_type: toMemoryType(normalized.kind),
|
|
2047
|
+
event_date: normalized.timestamp,
|
|
2048
|
+
write_mode: "async",
|
|
2049
|
+
metadata: {
|
|
2050
|
+
runtime_auto: true,
|
|
2051
|
+
client_name: this.clientName,
|
|
2052
|
+
work_event_kind: normalized.kind,
|
|
2053
|
+
salience: normalized.salience,
|
|
2054
|
+
file_paths: normalized.filePaths || [],
|
|
2055
|
+
tool_name: normalized.toolName,
|
|
2056
|
+
success: normalized.success
|
|
2057
|
+
}
|
|
2058
|
+
});
|
|
2059
|
+
}
|
|
2060
|
+
async afterTurn(input, context = {}) {
|
|
2061
|
+
this.pushTouchedFiles(input.touchedFiles);
|
|
2062
|
+
const { scope } = await this.resolveScope(context);
|
|
2063
|
+
const result = await this.args.adapter.ingestSession({
|
|
2064
|
+
project: scope.project,
|
|
2065
|
+
session_id: scope.sessionId,
|
|
2066
|
+
user_id: scope.userId,
|
|
2067
|
+
messages: [
|
|
2068
|
+
{ role: "user", content: input.userMessage, timestamp: nowIso() },
|
|
2069
|
+
{ role: "assistant", content: input.assistantMessage, timestamp: nowIso() }
|
|
2070
|
+
],
|
|
2071
|
+
write_mode: "async"
|
|
2072
|
+
});
|
|
2073
|
+
this.mergedCount += result.memories_invalidated || 0;
|
|
2074
|
+
return {
|
|
2075
|
+
success: Boolean(result.success),
|
|
2076
|
+
sessionIngested: true,
|
|
2077
|
+
memoriesCreated: result.memories_created || 0,
|
|
2078
|
+
relationsCreated: result.relations_created || 0,
|
|
2079
|
+
invalidatedCount: result.memories_invalidated || 0,
|
|
2080
|
+
mergedCount: result.memories_invalidated || 0,
|
|
2081
|
+
droppedCount: 0,
|
|
2082
|
+
warnings: result.errors || []
|
|
2083
|
+
};
|
|
2084
|
+
}
|
|
2085
|
+
async flush(reason = "manual", context = {}) {
|
|
2086
|
+
if (this.bufferedLowSalience.length > 0) {
|
|
2087
|
+
const { scope } = await this.resolveScope(context);
|
|
2088
|
+
const summary = summarizeLowSalience(this.bufferedLowSalience);
|
|
2089
|
+
await this.args.adapter.addMemory({
|
|
2090
|
+
project: scope.project,
|
|
2091
|
+
user_id: scope.userId,
|
|
2092
|
+
session_id: scope.sessionId,
|
|
2093
|
+
content: summary,
|
|
2094
|
+
memory_type: "event",
|
|
2095
|
+
write_mode: "async",
|
|
2096
|
+
metadata: {
|
|
2097
|
+
runtime_auto: true,
|
|
2098
|
+
client_name: this.clientName,
|
|
2099
|
+
flush_reason: reason,
|
|
2100
|
+
salience: "low",
|
|
2101
|
+
summarized_count: this.bufferedLowSalience.length
|
|
2102
|
+
}
|
|
2103
|
+
});
|
|
2104
|
+
this.bufferedLowSalience = [];
|
|
2105
|
+
}
|
|
2106
|
+
await this.args.adapter.flushQueue();
|
|
2107
|
+
return this.status();
|
|
2108
|
+
}
|
|
2109
|
+
status() {
|
|
2110
|
+
return {
|
|
2111
|
+
clientName: this.clientName,
|
|
2112
|
+
scope: this.lastScope,
|
|
2113
|
+
queue: this.args.adapter.queueStatus(),
|
|
2114
|
+
retrieval: this.lastPreparedTurn,
|
|
2115
|
+
counters: {
|
|
2116
|
+
mergedCount: this.mergedCount,
|
|
2117
|
+
droppedCount: this.droppedCount,
|
|
2118
|
+
bufferedLowSalience: this.bufferedLowSalience.length
|
|
2119
|
+
}
|
|
2120
|
+
};
|
|
2121
|
+
}
|
|
2122
|
+
};
|
|
2123
|
+
|
|
1553
2124
|
// ../src/sdk/whisper.ts
|
|
2125
|
+
var PROJECT_CACHE_TTL_MS = 3e4;
|
|
2126
|
+
function isLikelyProjectId(projectRef) {
|
|
2127
|
+
return /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(projectRef);
|
|
2128
|
+
}
|
|
1554
2129
|
var WhisperClient = class _WhisperClient {
|
|
1555
2130
|
constructor(config) {
|
|
1556
2131
|
this.config = config;
|
|
@@ -1570,7 +2145,7 @@ var WhisperClient = class _WhisperClient {
|
|
|
1570
2145
|
config.cache?.ttlMs ?? 7e3,
|
|
1571
2146
|
config.cache?.capacity ?? 500
|
|
1572
2147
|
);
|
|
1573
|
-
const queueStore =
|
|
2148
|
+
const queueStore = this.createQueueStore(config);
|
|
1574
2149
|
this.writeQueue = new WriteQueue({
|
|
1575
2150
|
store: queueStore,
|
|
1576
2151
|
maxBatchSize: config.queue?.maxBatchSize ?? 50,
|
|
@@ -1703,6 +2278,9 @@ var WhisperClient = class _WhisperClient {
|
|
|
1703
2278
|
sessionModule;
|
|
1704
2279
|
profileModule;
|
|
1705
2280
|
analyticsModule;
|
|
2281
|
+
projectRefToId = /* @__PURE__ */ new Map();
|
|
2282
|
+
projectCache = [];
|
|
2283
|
+
projectCacheExpiresAt = 0;
|
|
1706
2284
|
static fromEnv(overrides = {}) {
|
|
1707
2285
|
const env = typeof process !== "undefined" ? process.env : {};
|
|
1708
2286
|
const apiKey = overrides.apiKey || env.WHISPER_API_KEY || env.USEWHISPER_API_KEY || env.API_KEY;
|
|
@@ -1716,6 +2294,172 @@ var WhisperClient = class _WhisperClient {
|
|
|
1716
2294
|
...overrides
|
|
1717
2295
|
});
|
|
1718
2296
|
}
|
|
2297
|
+
createQueueStore(config) {
|
|
2298
|
+
const persistence = config.queue?.persistence || this.defaultQueuePersistence();
|
|
2299
|
+
if (persistence === "storage") {
|
|
2300
|
+
return createStorageQueueStore();
|
|
2301
|
+
}
|
|
2302
|
+
if (persistence === "file") {
|
|
2303
|
+
const filePath = config.queue?.filePath || this.defaultQueueFilePath();
|
|
2304
|
+
if (filePath) {
|
|
2305
|
+
return createFileQueueStore(filePath);
|
|
2306
|
+
}
|
|
2307
|
+
}
|
|
2308
|
+
return new InMemoryQueueStore();
|
|
2309
|
+
}
|
|
2310
|
+
defaultQueuePersistence() {
|
|
2311
|
+
const maybeWindow = globalThis.window;
|
|
2312
|
+
if (maybeWindow && typeof maybeWindow === "object") {
|
|
2313
|
+
const maybeStorage = globalThis.localStorage;
|
|
2314
|
+
return maybeStorage && typeof maybeStorage === "object" ? "storage" : "memory";
|
|
2315
|
+
}
|
|
2316
|
+
return "file";
|
|
2317
|
+
}
|
|
2318
|
+
defaultQueueFilePath() {
|
|
2319
|
+
if (typeof process === "undefined") return void 0;
|
|
2320
|
+
const path = process.env.WHISPER_QUEUE_FILE_PATH;
|
|
2321
|
+
if (path) return path;
|
|
2322
|
+
const home = process.env.USERPROFILE || process.env.HOME;
|
|
2323
|
+
if (!home) return void 0;
|
|
2324
|
+
const normalizedHome = home.replace(/[\\\/]+$/, "");
|
|
2325
|
+
return `${normalizedHome}/.whisper/sdk/queue.json`;
|
|
2326
|
+
}
|
|
2327
|
+
getRequiredProject(project) {
|
|
2328
|
+
const resolved = project || this.config.project;
|
|
2329
|
+
if (!resolved) {
|
|
2330
|
+
throw new RuntimeClientError({
|
|
2331
|
+
code: "MISSING_PROJECT",
|
|
2332
|
+
message: "Project is required",
|
|
2333
|
+
retryable: false
|
|
2334
|
+
});
|
|
2335
|
+
}
|
|
2336
|
+
return resolved;
|
|
2337
|
+
}
|
|
2338
|
+
async refreshProjectCache(force = false) {
|
|
2339
|
+
if (!force && Date.now() < this.projectCacheExpiresAt && this.projectCache.length > 0) {
|
|
2340
|
+
return this.projectCache;
|
|
2341
|
+
}
|
|
2342
|
+
const response = await this.runtimeClient.request({
|
|
2343
|
+
endpoint: "/v1/projects",
|
|
2344
|
+
method: "GET",
|
|
2345
|
+
operation: "get",
|
|
2346
|
+
idempotent: true
|
|
2347
|
+
});
|
|
2348
|
+
this.projectRefToId.clear();
|
|
2349
|
+
this.projectCache = response.data?.projects || [];
|
|
2350
|
+
for (const project of this.projectCache) {
|
|
2351
|
+
this.projectRefToId.set(project.id, project.id);
|
|
2352
|
+
this.projectRefToId.set(project.slug, project.id);
|
|
2353
|
+
this.projectRefToId.set(project.name, project.id);
|
|
2354
|
+
}
|
|
2355
|
+
this.projectCacheExpiresAt = Date.now() + PROJECT_CACHE_TTL_MS;
|
|
2356
|
+
return this.projectCache;
|
|
2357
|
+
}
|
|
2358
|
+
async fetchResolvedProject(projectRef) {
|
|
2359
|
+
try {
|
|
2360
|
+
const response = await this.runtimeClient.request({
|
|
2361
|
+
endpoint: `/v1/projects/resolve?project=${encodeURIComponent(projectRef)}`,
|
|
2362
|
+
method: "GET",
|
|
2363
|
+
operation: "get",
|
|
2364
|
+
idempotent: true
|
|
2365
|
+
});
|
|
2366
|
+
return response.data?.resolved || null;
|
|
2367
|
+
} catch (error) {
|
|
2368
|
+
if (error instanceof RuntimeClientError && error.status === 404) {
|
|
2369
|
+
return null;
|
|
2370
|
+
}
|
|
2371
|
+
throw error;
|
|
2372
|
+
}
|
|
2373
|
+
}
|
|
2374
|
+
async resolveProject(projectRef) {
|
|
2375
|
+
const resolvedRef = this.getRequiredProject(projectRef);
|
|
2376
|
+
const cachedProjects = await this.refreshProjectCache(false);
|
|
2377
|
+
const cachedProject = cachedProjects.find(
|
|
2378
|
+
(project) => project.id === resolvedRef || project.slug === resolvedRef || project.name === resolvedRef
|
|
2379
|
+
);
|
|
2380
|
+
if (cachedProject) {
|
|
2381
|
+
return cachedProject;
|
|
2382
|
+
}
|
|
2383
|
+
const resolvedProject = await this.fetchResolvedProject(resolvedRef);
|
|
2384
|
+
if (resolvedProject) {
|
|
2385
|
+
this.projectRefToId.set(resolvedProject.id, resolvedProject.id);
|
|
2386
|
+
this.projectRefToId.set(resolvedProject.slug, resolvedProject.id);
|
|
2387
|
+
this.projectRefToId.set(resolvedProject.name, resolvedProject.id);
|
|
2388
|
+
this.projectCache = [
|
|
2389
|
+
...this.projectCache.filter((project) => project.id !== resolvedProject.id),
|
|
2390
|
+
resolvedProject
|
|
2391
|
+
];
|
|
2392
|
+
this.projectCacheExpiresAt = Date.now() + PROJECT_CACHE_TTL_MS;
|
|
2393
|
+
return resolvedProject;
|
|
2394
|
+
}
|
|
2395
|
+
if (isLikelyProjectId(resolvedRef)) {
|
|
2396
|
+
return {
|
|
2397
|
+
id: resolvedRef,
|
|
2398
|
+
orgId: "",
|
|
2399
|
+
name: resolvedRef,
|
|
2400
|
+
slug: resolvedRef,
|
|
2401
|
+
createdAt: (/* @__PURE__ */ new Date(0)).toISOString(),
|
|
2402
|
+
updatedAt: (/* @__PURE__ */ new Date(0)).toISOString()
|
|
2403
|
+
};
|
|
2404
|
+
}
|
|
2405
|
+
throw new RuntimeClientError({
|
|
2406
|
+
code: "PROJECT_NOT_FOUND",
|
|
2407
|
+
message: `Project '${resolvedRef}' not found`,
|
|
2408
|
+
retryable: false
|
|
2409
|
+
});
|
|
2410
|
+
}
|
|
2411
|
+
async query(params) {
|
|
2412
|
+
const project = (await this.resolveProject(params.project)).id;
|
|
2413
|
+
const response = await this.runtimeClient.request({
|
|
2414
|
+
endpoint: "/v1/context/query",
|
|
2415
|
+
method: "POST",
|
|
2416
|
+
operation: "search",
|
|
2417
|
+
body: {
|
|
2418
|
+
...params,
|
|
2419
|
+
project
|
|
2420
|
+
},
|
|
2421
|
+
idempotent: true
|
|
2422
|
+
});
|
|
2423
|
+
return response.data;
|
|
2424
|
+
}
|
|
2425
|
+
async ingestSession(params) {
|
|
2426
|
+
const project = (await this.resolveProject(params.project)).id;
|
|
2427
|
+
const response = await this.runtimeClient.request({
|
|
2428
|
+
endpoint: "/v1/memory/ingest/session",
|
|
2429
|
+
method: "POST",
|
|
2430
|
+
operation: "session",
|
|
2431
|
+
body: {
|
|
2432
|
+
...params,
|
|
2433
|
+
project
|
|
2434
|
+
}
|
|
2435
|
+
});
|
|
2436
|
+
return response.data;
|
|
2437
|
+
}
|
|
2438
|
+
createAgentRuntime(options = {}) {
|
|
2439
|
+
const baseContext = {
|
|
2440
|
+
workspacePath: options.workspacePath,
|
|
2441
|
+
project: options.project || this.config.project,
|
|
2442
|
+
userId: options.userId,
|
|
2443
|
+
sessionId: options.sessionId,
|
|
2444
|
+
traceId: options.traceId,
|
|
2445
|
+
clientName: options.clientName
|
|
2446
|
+
};
|
|
2447
|
+
return new WhisperAgentRuntime({
|
|
2448
|
+
baseContext,
|
|
2449
|
+
options,
|
|
2450
|
+
adapter: {
|
|
2451
|
+
resolveProject: (project) => this.resolveProject(project),
|
|
2452
|
+
query: (params) => this.query(params),
|
|
2453
|
+
ingestSession: (params) => this.ingestSession(params),
|
|
2454
|
+
getSessionMemories: (params) => this.memory.getSessionMemories(params),
|
|
2455
|
+
getUserProfile: (params) => this.memory.getUserProfile(params),
|
|
2456
|
+
searchMemories: (params) => this.memory.search(params),
|
|
2457
|
+
addMemory: (params) => this.memory.add(params),
|
|
2458
|
+
queueStatus: () => this.queue.status(),
|
|
2459
|
+
flushQueue: () => this.queue.flush()
|
|
2460
|
+
}
|
|
2461
|
+
});
|
|
2462
|
+
}
|
|
1719
2463
|
withRunContext(context) {
|
|
1720
2464
|
const base = this;
|
|
1721
2465
|
return {
|
|
@@ -2257,7 +3001,7 @@ var DEFAULT_MAX_ATTEMPTS = 3;
|
|
|
2257
3001
|
var DEFAULT_BASE_DELAY_MS = 250;
|
|
2258
3002
|
var DEFAULT_MAX_DELAY_MS = 2e3;
|
|
2259
3003
|
var DEFAULT_TIMEOUT_MS = 15e3;
|
|
2260
|
-
var
|
|
3004
|
+
var PROJECT_CACHE_TTL_MS2 = 3e4;
|
|
2261
3005
|
var DEPRECATION_WARNINGS2 = /* @__PURE__ */ new Set();
|
|
2262
3006
|
function warnDeprecatedOnce2(key, message) {
|
|
2263
3007
|
if (DEPRECATION_WARNINGS2.has(key)) return;
|
|
@@ -2266,7 +3010,7 @@ function warnDeprecatedOnce2(key, message) {
|
|
|
2266
3010
|
console.warn(message);
|
|
2267
3011
|
}
|
|
2268
3012
|
}
|
|
2269
|
-
function
|
|
3013
|
+
function isLikelyProjectId2(projectRef) {
|
|
2270
3014
|
return /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(projectRef);
|
|
2271
3015
|
}
|
|
2272
3016
|
function normalizeBaseUrl2(url) {
|
|
@@ -2388,7 +3132,7 @@ var WhisperContext = class _WhisperContext {
|
|
|
2388
3132
|
}
|
|
2389
3133
|
}
|
|
2390
3134
|
}
|
|
2391
|
-
this.projectCacheExpiresAt = Date.now() +
|
|
3135
|
+
this.projectCacheExpiresAt = Date.now() + PROJECT_CACHE_TTL_MS2;
|
|
2392
3136
|
return this.projectCache;
|
|
2393
3137
|
}
|
|
2394
3138
|
async fetchResolvedProject(projectRef) {
|
|
@@ -2421,10 +3165,10 @@ var WhisperContext = class _WhisperContext {
|
|
|
2421
3165
|
...this.projectCache.filter((project) => project.id !== resolvedProject.id),
|
|
2422
3166
|
resolvedProject
|
|
2423
3167
|
];
|
|
2424
|
-
this.projectCacheExpiresAt = Date.now() +
|
|
3168
|
+
this.projectCacheExpiresAt = Date.now() + PROJECT_CACHE_TTL_MS2;
|
|
2425
3169
|
return resolvedProject;
|
|
2426
3170
|
}
|
|
2427
|
-
if (
|
|
3171
|
+
if (isLikelyProjectId2(resolvedRef)) {
|
|
2428
3172
|
return {
|
|
2429
3173
|
id: resolvedRef,
|
|
2430
3174
|
orgId: "",
|
|
@@ -2456,7 +3200,7 @@ var WhisperContext = class _WhisperContext {
|
|
|
2456
3200
|
message: `Project reference '${projectRef}' matched multiple projects. Use project id instead.`
|
|
2457
3201
|
});
|
|
2458
3202
|
}
|
|
2459
|
-
if (
|
|
3203
|
+
if (isLikelyProjectId2(projectRef)) {
|
|
2460
3204
|
return projectRef;
|
|
2461
3205
|
}
|
|
2462
3206
|
const resolvedProject = await this.fetchResolvedProject(projectRef);
|
|
@@ -2468,7 +3212,7 @@ var WhisperContext = class _WhisperContext {
|
|
|
2468
3212
|
...this.projectCache.filter((project) => project.id !== resolvedProject.id),
|
|
2469
3213
|
resolvedProject
|
|
2470
3214
|
];
|
|
2471
|
-
this.projectCacheExpiresAt = Date.now() +
|
|
3215
|
+
this.projectCacheExpiresAt = Date.now() + PROJECT_CACHE_TTL_MS2;
|
|
2472
3216
|
return resolvedProject.id;
|
|
2473
3217
|
}
|
|
2474
3218
|
throw new WhisperError({
|
|
@@ -2485,7 +3229,7 @@ var WhisperContext = class _WhisperContext {
|
|
|
2485
3229
|
candidates.add(match.id);
|
|
2486
3230
|
candidates.add(match.slug);
|
|
2487
3231
|
candidates.add(match.name);
|
|
2488
|
-
} else if (
|
|
3232
|
+
} else if (isLikelyProjectId2(projectRef)) {
|
|
2489
3233
|
const byId = projects.find((p) => p.id === projectRef);
|
|
2490
3234
|
if (byId) {
|
|
2491
3235
|
candidates.add(byId.slug);
|
|
@@ -2646,7 +3390,7 @@ var WhisperContext = class _WhisperContext {
|
|
|
2646
3390
|
...this.projectCache.filter((p) => p.id !== project.id),
|
|
2647
3391
|
project
|
|
2648
3392
|
];
|
|
2649
|
-
this.projectCacheExpiresAt = Date.now() +
|
|
3393
|
+
this.projectCacheExpiresAt = Date.now() + PROJECT_CACHE_TTL_MS2;
|
|
2650
3394
|
return project;
|
|
2651
3395
|
}
|
|
2652
3396
|
async listProjects() {
|
|
@@ -2657,7 +3401,7 @@ var WhisperContext = class _WhisperContext {
|
|
|
2657
3401
|
this.projectRefToId.set(p.slug, p.id);
|
|
2658
3402
|
this.projectRefToId.set(p.name, p.id);
|
|
2659
3403
|
}
|
|
2660
|
-
this.projectCacheExpiresAt = Date.now() +
|
|
3404
|
+
this.projectCacheExpiresAt = Date.now() + PROJECT_CACHE_TTL_MS2;
|
|
2661
3405
|
return projects;
|
|
2662
3406
|
}
|
|
2663
3407
|
async getProject(id) {
|