@usewhisper/sdk 3.3.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.js
CHANGED
|
@@ -1599,7 +1599,582 @@ var AnalyticsModule = class {
|
|
|
1599
1599
|
}
|
|
1600
1600
|
};
|
|
1601
1601
|
|
|
1602
|
+
// ../src/sdk/agent-runtime.ts
|
|
1603
|
+
function detectBrowserStorage() {
|
|
1604
|
+
const maybeStorage = globalThis.localStorage;
|
|
1605
|
+
if (!maybeStorage || typeof maybeStorage !== "object") return null;
|
|
1606
|
+
const candidate = maybeStorage;
|
|
1607
|
+
if (typeof candidate.getItem !== "function" || typeof candidate.setItem !== "function") {
|
|
1608
|
+
return null;
|
|
1609
|
+
}
|
|
1610
|
+
return {
|
|
1611
|
+
getItem: candidate.getItem,
|
|
1612
|
+
setItem: candidate.setItem
|
|
1613
|
+
};
|
|
1614
|
+
}
|
|
1615
|
+
function createBindingStore(filePath) {
|
|
1616
|
+
const storage = detectBrowserStorage();
|
|
1617
|
+
if (storage) {
|
|
1618
|
+
const key = "whisper_agent_runtime_bindings";
|
|
1619
|
+
return {
|
|
1620
|
+
async load() {
|
|
1621
|
+
const raw = storage.getItem(key);
|
|
1622
|
+
if (!raw) return {};
|
|
1623
|
+
try {
|
|
1624
|
+
const parsed = JSON.parse(raw);
|
|
1625
|
+
return parsed && typeof parsed === "object" ? parsed : {};
|
|
1626
|
+
} catch {
|
|
1627
|
+
return {};
|
|
1628
|
+
}
|
|
1629
|
+
},
|
|
1630
|
+
async save(bindings) {
|
|
1631
|
+
storage.setItem(key, JSON.stringify(bindings));
|
|
1632
|
+
}
|
|
1633
|
+
};
|
|
1634
|
+
}
|
|
1635
|
+
return {
|
|
1636
|
+
async load() {
|
|
1637
|
+
if (typeof process === "undefined") return {};
|
|
1638
|
+
const fs = await import("fs/promises");
|
|
1639
|
+
const path = filePath || `${process.env.USERPROFILE || process.env.HOME || "."}/.whisper/sdk/agent-bindings.json`;
|
|
1640
|
+
try {
|
|
1641
|
+
const raw = await fs.readFile(path, "utf8");
|
|
1642
|
+
const parsed = JSON.parse(raw);
|
|
1643
|
+
return parsed && typeof parsed === "object" ? parsed : {};
|
|
1644
|
+
} catch {
|
|
1645
|
+
return {};
|
|
1646
|
+
}
|
|
1647
|
+
},
|
|
1648
|
+
async save(bindings) {
|
|
1649
|
+
if (typeof process === "undefined") return;
|
|
1650
|
+
const fs = await import("fs/promises");
|
|
1651
|
+
const pathMod = await import("path");
|
|
1652
|
+
const path = filePath || `${process.env.USERPROFILE || process.env.HOME || "."}/.whisper/sdk/agent-bindings.json`;
|
|
1653
|
+
await fs.mkdir(pathMod.dirname(path), { recursive: true });
|
|
1654
|
+
await fs.writeFile(path, JSON.stringify(bindings), "utf8");
|
|
1655
|
+
}
|
|
1656
|
+
};
|
|
1657
|
+
}
|
|
1658
|
+
function normalizeWorkspacePath(value) {
|
|
1659
|
+
const trimmed = value?.trim();
|
|
1660
|
+
if (!trimmed) return null;
|
|
1661
|
+
return trimmed.replace(/\\/g, "/").replace(/\/+$/, "").toLowerCase();
|
|
1662
|
+
}
|
|
1663
|
+
function pathBase(value) {
|
|
1664
|
+
const normalized = value.replace(/\\/g, "/");
|
|
1665
|
+
const parts = normalized.split("/").filter(Boolean);
|
|
1666
|
+
return parts[parts.length - 1] || normalized;
|
|
1667
|
+
}
|
|
1668
|
+
function defaultSalience(kind, success) {
|
|
1669
|
+
if (kind === "decision" || kind === "constraint" || kind === "failure") return "high";
|
|
1670
|
+
if (kind === "outcome" || kind === "task_update") return "medium";
|
|
1671
|
+
if (kind === "tool_result" && success === false) return "high";
|
|
1672
|
+
return "low";
|
|
1673
|
+
}
|
|
1674
|
+
function toMemoryType(kind) {
|
|
1675
|
+
if (kind === "decision" || kind === "constraint") return "instruction";
|
|
1676
|
+
return "event";
|
|
1677
|
+
}
|
|
1678
|
+
function summarizeLowSalience(events) {
|
|
1679
|
+
const lines = events.slice(-10).map((event) => {
|
|
1680
|
+
const fileSuffix = event.filePaths?.length ? ` [files: ${event.filePaths.join(", ")}]` : "";
|
|
1681
|
+
const toolSuffix = event.toolName ? ` [tool: ${event.toolName}]` : "";
|
|
1682
|
+
return `- ${event.kind}: ${event.summary}${fileSuffix}${toolSuffix}`;
|
|
1683
|
+
});
|
|
1684
|
+
return `Recent low-salience work:
|
|
1685
|
+
${lines.join("\n")}`;
|
|
1686
|
+
}
|
|
1687
|
+
function compactWhitespace(value) {
|
|
1688
|
+
return value.replace(/\s+/g, " ").trim();
|
|
1689
|
+
}
|
|
1690
|
+
function withTimeout(promise, timeoutMs) {
|
|
1691
|
+
return new Promise((resolve, reject) => {
|
|
1692
|
+
const timeout = setTimeout(() => {
|
|
1693
|
+
reject(new Error("timeout"));
|
|
1694
|
+
}, timeoutMs);
|
|
1695
|
+
promise.then(
|
|
1696
|
+
(value) => {
|
|
1697
|
+
clearTimeout(timeout);
|
|
1698
|
+
resolve(value);
|
|
1699
|
+
},
|
|
1700
|
+
(error) => {
|
|
1701
|
+
clearTimeout(timeout);
|
|
1702
|
+
reject(error);
|
|
1703
|
+
}
|
|
1704
|
+
);
|
|
1705
|
+
});
|
|
1706
|
+
}
|
|
1707
|
+
function extractTimestamp(metadata) {
|
|
1708
|
+
const candidates = [
|
|
1709
|
+
metadata?.updatedAt,
|
|
1710
|
+
metadata?.createdAt,
|
|
1711
|
+
metadata?.timestamp,
|
|
1712
|
+
metadata?.event_date,
|
|
1713
|
+
metadata?.eventDate
|
|
1714
|
+
];
|
|
1715
|
+
for (const value of candidates) {
|
|
1716
|
+
if (typeof value === "string") {
|
|
1717
|
+
const parsed = Date.parse(value);
|
|
1718
|
+
if (!Number.isNaN(parsed)) return parsed;
|
|
1719
|
+
}
|
|
1720
|
+
}
|
|
1721
|
+
return 0;
|
|
1722
|
+
}
|
|
1723
|
+
function salienceBoost(metadata) {
|
|
1724
|
+
const value = metadata?.salience;
|
|
1725
|
+
if (value === "high") return 0.12;
|
|
1726
|
+
if (value === "medium") return 0.06;
|
|
1727
|
+
return 0;
|
|
1728
|
+
}
|
|
1729
|
+
var WhisperAgentRuntime = class {
|
|
1730
|
+
constructor(args) {
|
|
1731
|
+
this.args = args;
|
|
1732
|
+
this.bindingStore = createBindingStore(args.options.bindingStorePath);
|
|
1733
|
+
this.topK = args.options.topK ?? 6;
|
|
1734
|
+
this.maxTokens = args.options.maxTokens ?? 4e3;
|
|
1735
|
+
this.targetRetrievalMs = args.options.targetRetrievalMs ?? 2500;
|
|
1736
|
+
this.hardRetrievalTimeoutMs = args.options.hardRetrievalTimeoutMs ?? 4e3;
|
|
1737
|
+
this.recentWorkLimit = args.options.recentWorkLimit ?? 40;
|
|
1738
|
+
this.baseContext = args.baseContext;
|
|
1739
|
+
this.clientName = args.baseContext.clientName || "whisper-agent-runtime";
|
|
1740
|
+
}
|
|
1741
|
+
bindingStore;
|
|
1742
|
+
topK;
|
|
1743
|
+
maxTokens;
|
|
1744
|
+
targetRetrievalMs;
|
|
1745
|
+
hardRetrievalTimeoutMs;
|
|
1746
|
+
recentWorkLimit;
|
|
1747
|
+
baseContext;
|
|
1748
|
+
clientName;
|
|
1749
|
+
bindings = null;
|
|
1750
|
+
touchedFiles = [];
|
|
1751
|
+
recentWork = [];
|
|
1752
|
+
bufferedLowSalience = [];
|
|
1753
|
+
lastPreparedTurn = null;
|
|
1754
|
+
mergedCount = 0;
|
|
1755
|
+
droppedCount = 0;
|
|
1756
|
+
lastScope = {};
|
|
1757
|
+
async getBindings() {
|
|
1758
|
+
if (!this.bindings) {
|
|
1759
|
+
this.bindings = await this.bindingStore.load();
|
|
1760
|
+
}
|
|
1761
|
+
return this.bindings;
|
|
1762
|
+
}
|
|
1763
|
+
pushTouchedFiles(paths) {
|
|
1764
|
+
if (!paths || paths.length === 0) return;
|
|
1765
|
+
for (const path of paths) {
|
|
1766
|
+
if (!path) continue;
|
|
1767
|
+
this.touchedFiles = [...this.touchedFiles.filter((entry) => entry !== path), path].slice(-20);
|
|
1768
|
+
}
|
|
1769
|
+
}
|
|
1770
|
+
pushWorkEvent(event) {
|
|
1771
|
+
this.recentWork = [...this.recentWork, event].slice(-this.recentWorkLimit);
|
|
1772
|
+
}
|
|
1773
|
+
makeTaskFrameQuery(input) {
|
|
1774
|
+
const task = compactWhitespace(input.taskSummary || "");
|
|
1775
|
+
const salient = this.recentWork.filter((event) => event.salience === "high").slice(-3).map((event) => `${event.kind}: ${event.summary}`);
|
|
1776
|
+
const files = [...input.touchedFiles || [], ...this.touchedFiles].slice(-3).map((file) => pathBase(file));
|
|
1777
|
+
const parts = [
|
|
1778
|
+
task ? `task ${task}` : "",
|
|
1779
|
+
salient.length > 0 ? `recent ${salient.join(" ; ")}` : "",
|
|
1780
|
+
files.length > 0 ? `files ${files.join(" ")}` : "",
|
|
1781
|
+
input.toolContext ? `tool context ${compactWhitespace(input.toolContext)}` : ""
|
|
1782
|
+
].filter(Boolean);
|
|
1783
|
+
if (parts.length === 0) return null;
|
|
1784
|
+
return parts.join(" | ");
|
|
1785
|
+
}
|
|
1786
|
+
async resolveScope(overrides) {
|
|
1787
|
+
const merged = {
|
|
1788
|
+
...this.baseContext,
|
|
1789
|
+
...overrides
|
|
1790
|
+
};
|
|
1791
|
+
const normalizedWorkspace = normalizeWorkspacePath(merged.workspacePath);
|
|
1792
|
+
const bindings = await this.getBindings();
|
|
1793
|
+
const workspaceProject = normalizedWorkspace ? bindings[normalizedWorkspace] : void 0;
|
|
1794
|
+
const configuredProject = merged.project;
|
|
1795
|
+
let projectRef = configuredProject;
|
|
1796
|
+
let projectSource = overrides?.project ? "explicit" : "generated";
|
|
1797
|
+
let warning;
|
|
1798
|
+
if (workspaceProject) {
|
|
1799
|
+
projectRef = workspaceProject;
|
|
1800
|
+
projectSource = "workspace";
|
|
1801
|
+
if (configuredProject && workspaceProject !== configuredProject) {
|
|
1802
|
+
warning = `workspace mapping '${workspaceProject}' overrides configured project '${configuredProject}'`;
|
|
1803
|
+
}
|
|
1804
|
+
} else if (configuredProject) {
|
|
1805
|
+
projectRef = configuredProject;
|
|
1806
|
+
projectSource = overrides?.project ? "explicit" : "config";
|
|
1807
|
+
}
|
|
1808
|
+
const project = (await this.args.adapter.resolveProject(projectRef)).id;
|
|
1809
|
+
if (normalizedWorkspace) {
|
|
1810
|
+
bindings[normalizedWorkspace] = project;
|
|
1811
|
+
await this.bindingStore.save(bindings);
|
|
1812
|
+
}
|
|
1813
|
+
const scope = {
|
|
1814
|
+
...merged,
|
|
1815
|
+
project,
|
|
1816
|
+
userId: merged.userId || `${this.clientName}-user`,
|
|
1817
|
+
sessionId: merged.sessionId || `sess_${stableHash(`${this.clientName}_${normalizedWorkspace || "default"}`)}`
|
|
1818
|
+
};
|
|
1819
|
+
this.lastScope = {
|
|
1820
|
+
project: scope.project,
|
|
1821
|
+
userId: scope.userId,
|
|
1822
|
+
sessionId: scope.sessionId,
|
|
1823
|
+
source: projectSource,
|
|
1824
|
+
warning
|
|
1825
|
+
};
|
|
1826
|
+
return { scope, projectSource, warning };
|
|
1827
|
+
}
|
|
1828
|
+
async runBranch(name, task) {
|
|
1829
|
+
const startedAt = Date.now();
|
|
1830
|
+
try {
|
|
1831
|
+
const value = await withTimeout(task(), this.hardRetrievalTimeoutMs);
|
|
1832
|
+
return {
|
|
1833
|
+
name,
|
|
1834
|
+
status: "ok",
|
|
1835
|
+
durationMs: Date.now() - startedAt,
|
|
1836
|
+
value
|
|
1837
|
+
};
|
|
1838
|
+
} catch (error) {
|
|
1839
|
+
const durationMs = Date.now() - startedAt;
|
|
1840
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
1841
|
+
return {
|
|
1842
|
+
name,
|
|
1843
|
+
status: reason === "timeout" ? "timeout" : "error",
|
|
1844
|
+
durationMs,
|
|
1845
|
+
reason
|
|
1846
|
+
};
|
|
1847
|
+
}
|
|
1848
|
+
}
|
|
1849
|
+
contextItems(result, sourceQuery) {
|
|
1850
|
+
return (result.results || []).map((item) => ({
|
|
1851
|
+
id: item.id,
|
|
1852
|
+
content: item.content,
|
|
1853
|
+
type: "project",
|
|
1854
|
+
score: item.score ?? 0,
|
|
1855
|
+
sourceQuery,
|
|
1856
|
+
metadata: item.metadata || {}
|
|
1857
|
+
}));
|
|
1858
|
+
}
|
|
1859
|
+
memoryItems(result, sourceQuery) {
|
|
1860
|
+
return (result.results || []).map((item, index) => ({
|
|
1861
|
+
id: item.memory?.id || item.chunk?.id || `${sourceQuery}_memory_${index}`,
|
|
1862
|
+
content: item.chunk?.content || item.memory?.content || "",
|
|
1863
|
+
type: "memory",
|
|
1864
|
+
score: item.similarity ?? 0,
|
|
1865
|
+
sourceQuery,
|
|
1866
|
+
metadata: {
|
|
1867
|
+
...item.chunk?.metadata || {},
|
|
1868
|
+
...item.memory?.temporal || {},
|
|
1869
|
+
confidence: item.memory?.confidence
|
|
1870
|
+
}
|
|
1871
|
+
})).filter((item) => item.content);
|
|
1872
|
+
}
|
|
1873
|
+
rerank(items) {
|
|
1874
|
+
const deduped = /* @__PURE__ */ new Map();
|
|
1875
|
+
for (const item of items) {
|
|
1876
|
+
const key = `${item.id}:${item.content.toLowerCase()}`;
|
|
1877
|
+
const recency = extractTimestamp(item.metadata) > 0 ? 0.04 : 0;
|
|
1878
|
+
const queryBonus = item.sourceQuery === "primary" ? 0.08 : item.sourceQuery === "task_frame" ? 0.04 : 0.03;
|
|
1879
|
+
const next = {
|
|
1880
|
+
...item,
|
|
1881
|
+
score: item.score + queryBonus + salienceBoost(item.metadata) + recency
|
|
1882
|
+
};
|
|
1883
|
+
const existing = deduped.get(key);
|
|
1884
|
+
if (!existing || next.score > existing.score) {
|
|
1885
|
+
deduped.set(key, next);
|
|
1886
|
+
}
|
|
1887
|
+
}
|
|
1888
|
+
return [...deduped.values()].sort((left, right) => right.score - left.score);
|
|
1889
|
+
}
|
|
1890
|
+
buildContext(items) {
|
|
1891
|
+
const maxChars = this.maxTokens * 4;
|
|
1892
|
+
const lines = [];
|
|
1893
|
+
let used = 0;
|
|
1894
|
+
for (const item of items) {
|
|
1895
|
+
const label = item.type === "memory" ? "memory" : "context";
|
|
1896
|
+
const content = compactWhitespace(item.content);
|
|
1897
|
+
if (!content) continue;
|
|
1898
|
+
const line = `[${label}] ${content}`;
|
|
1899
|
+
if (used + line.length > maxChars) break;
|
|
1900
|
+
lines.push(line);
|
|
1901
|
+
used += line.length;
|
|
1902
|
+
}
|
|
1903
|
+
if (lines.length === 0) return "";
|
|
1904
|
+
return `Relevant context:
|
|
1905
|
+
${lines.join("\n")}`;
|
|
1906
|
+
}
|
|
1907
|
+
async bootstrap(context = {}) {
|
|
1908
|
+
const { scope, warning } = await this.resolveScope(context);
|
|
1909
|
+
const warnings = warning ? [warning] : [];
|
|
1910
|
+
const startedAt = Date.now();
|
|
1911
|
+
const branches = await Promise.all([
|
|
1912
|
+
this.runBranch("session_recent", () => this.args.adapter.getSessionMemories({
|
|
1913
|
+
project: scope.project,
|
|
1914
|
+
session_id: scope.sessionId,
|
|
1915
|
+
include_pending: true,
|
|
1916
|
+
limit: 12
|
|
1917
|
+
})),
|
|
1918
|
+
this.runBranch("user_profile", () => scope.userId ? this.args.adapter.getUserProfile({
|
|
1919
|
+
project: scope.project,
|
|
1920
|
+
user_id: scope.userId,
|
|
1921
|
+
include_pending: true,
|
|
1922
|
+
memory_types: "preference,instruction,goal"
|
|
1923
|
+
}) : Promise.resolve({ user_id: scope.userId, memories: [], count: 0 })),
|
|
1924
|
+
this.runBranch("project_rules", () => this.args.adapter.query({
|
|
1925
|
+
project: scope.project,
|
|
1926
|
+
query: "project rules instructions constraints conventions open threads",
|
|
1927
|
+
top_k: this.topK,
|
|
1928
|
+
include_memories: false,
|
|
1929
|
+
user_id: scope.userId,
|
|
1930
|
+
session_id: scope.sessionId,
|
|
1931
|
+
max_tokens: this.maxTokens,
|
|
1932
|
+
compress: true,
|
|
1933
|
+
compression_strategy: "adaptive"
|
|
1934
|
+
}))
|
|
1935
|
+
]);
|
|
1936
|
+
const items = [];
|
|
1937
|
+
const branchStatus = {};
|
|
1938
|
+
for (const branch of branches) {
|
|
1939
|
+
branchStatus[branch.name] = branch.status;
|
|
1940
|
+
if (branch.status !== "ok") {
|
|
1941
|
+
if (branch.reason) warnings.push(`${branch.name}:${branch.reason}`);
|
|
1942
|
+
continue;
|
|
1943
|
+
}
|
|
1944
|
+
if (branch.name === "project_rules") {
|
|
1945
|
+
items.push(...this.contextItems(branch.value, "bootstrap"));
|
|
1946
|
+
continue;
|
|
1947
|
+
}
|
|
1948
|
+
const records = branch.value.memories || [];
|
|
1949
|
+
items.push(...records.map((memory, index) => ({
|
|
1950
|
+
id: String(memory.id || `${branch.name}_${index}`),
|
|
1951
|
+
content: String(memory.content || ""),
|
|
1952
|
+
type: "memory",
|
|
1953
|
+
score: 0.4,
|
|
1954
|
+
sourceQuery: "bootstrap",
|
|
1955
|
+
metadata: memory
|
|
1956
|
+
})).filter((item) => item.content));
|
|
1957
|
+
}
|
|
1958
|
+
const ranked = this.rerank(items).slice(0, this.topK * 2);
|
|
1959
|
+
const prepared = {
|
|
1960
|
+
scope,
|
|
1961
|
+
retrieval: {
|
|
1962
|
+
primaryQuery: "bootstrap",
|
|
1963
|
+
taskFrameQuery: null,
|
|
1964
|
+
warnings,
|
|
1965
|
+
degraded: warnings.length > 0,
|
|
1966
|
+
degradedReason: warnings.length > 0 ? "partial_bootstrap" : void 0,
|
|
1967
|
+
durationMs: Date.now() - startedAt,
|
|
1968
|
+
targetBudgetMs: this.targetRetrievalMs,
|
|
1969
|
+
hardTimeoutMs: this.hardRetrievalTimeoutMs,
|
|
1970
|
+
branchStatus
|
|
1971
|
+
},
|
|
1972
|
+
context: this.buildContext(ranked),
|
|
1973
|
+
items: ranked
|
|
1974
|
+
};
|
|
1975
|
+
this.lastPreparedTurn = prepared.retrieval;
|
|
1976
|
+
return prepared;
|
|
1977
|
+
}
|
|
1978
|
+
async beforeTurn(input, context = {}) {
|
|
1979
|
+
this.pushTouchedFiles(input.touchedFiles);
|
|
1980
|
+
const { scope, warning } = await this.resolveScope(context);
|
|
1981
|
+
const primaryQuery = compactWhitespace(input.userMessage);
|
|
1982
|
+
const taskFrameQuery = this.makeTaskFrameQuery(input);
|
|
1983
|
+
const warnings = warning ? [warning] : [];
|
|
1984
|
+
const startedAt = Date.now();
|
|
1985
|
+
const branches = await Promise.all([
|
|
1986
|
+
this.runBranch("context_primary", () => this.args.adapter.query({
|
|
1987
|
+
project: scope.project,
|
|
1988
|
+
query: primaryQuery,
|
|
1989
|
+
top_k: this.topK,
|
|
1990
|
+
include_memories: false,
|
|
1991
|
+
user_id: scope.userId,
|
|
1992
|
+
session_id: scope.sessionId,
|
|
1993
|
+
max_tokens: this.maxTokens,
|
|
1994
|
+
compress: true,
|
|
1995
|
+
compression_strategy: "adaptive"
|
|
1996
|
+
})),
|
|
1997
|
+
this.runBranch("memory_primary", () => this.args.adapter.searchMemories({
|
|
1998
|
+
project: scope.project,
|
|
1999
|
+
query: primaryQuery,
|
|
2000
|
+
user_id: scope.userId,
|
|
2001
|
+
session_id: scope.sessionId,
|
|
2002
|
+
top_k: this.topK,
|
|
2003
|
+
include_pending: true,
|
|
2004
|
+
profile: "balanced"
|
|
2005
|
+
})),
|
|
2006
|
+
taskFrameQuery ? this.runBranch("context_task_frame", () => this.args.adapter.query({
|
|
2007
|
+
project: scope.project,
|
|
2008
|
+
query: taskFrameQuery,
|
|
2009
|
+
top_k: this.topK,
|
|
2010
|
+
include_memories: false,
|
|
2011
|
+
user_id: scope.userId,
|
|
2012
|
+
session_id: scope.sessionId,
|
|
2013
|
+
max_tokens: this.maxTokens,
|
|
2014
|
+
compress: true,
|
|
2015
|
+
compression_strategy: "adaptive"
|
|
2016
|
+
})) : Promise.resolve({
|
|
2017
|
+
name: "context_task_frame",
|
|
2018
|
+
status: "skipped",
|
|
2019
|
+
durationMs: 0
|
|
2020
|
+
}),
|
|
2021
|
+
taskFrameQuery ? this.runBranch("memory_task_frame", () => this.args.adapter.searchMemories({
|
|
2022
|
+
project: scope.project,
|
|
2023
|
+
query: taskFrameQuery,
|
|
2024
|
+
user_id: scope.userId,
|
|
2025
|
+
session_id: scope.sessionId,
|
|
2026
|
+
top_k: this.topK,
|
|
2027
|
+
include_pending: true,
|
|
2028
|
+
profile: "balanced"
|
|
2029
|
+
})) : Promise.resolve({
|
|
2030
|
+
name: "memory_task_frame",
|
|
2031
|
+
status: "skipped",
|
|
2032
|
+
durationMs: 0
|
|
2033
|
+
})
|
|
2034
|
+
]);
|
|
2035
|
+
const branchStatus = {};
|
|
2036
|
+
const collected = [];
|
|
2037
|
+
let okCount = 0;
|
|
2038
|
+
for (const branch of branches) {
|
|
2039
|
+
branchStatus[branch.name] = branch.status;
|
|
2040
|
+
if (branch.status !== "ok") {
|
|
2041
|
+
if (branch.status !== "skipped" && branch.reason) warnings.push(`${branch.name}:${branch.reason}`);
|
|
2042
|
+
continue;
|
|
2043
|
+
}
|
|
2044
|
+
okCount += 1;
|
|
2045
|
+
if (branch.name.startsWith("context")) {
|
|
2046
|
+
collected.push(...this.contextItems(
|
|
2047
|
+
branch.value,
|
|
2048
|
+
branch.name.includes("task_frame") ? "task_frame" : "primary"
|
|
2049
|
+
));
|
|
2050
|
+
} else {
|
|
2051
|
+
collected.push(...this.memoryItems(
|
|
2052
|
+
branch.value,
|
|
2053
|
+
branch.name.includes("task_frame") ? "task_frame" : "primary"
|
|
2054
|
+
));
|
|
2055
|
+
}
|
|
2056
|
+
}
|
|
2057
|
+
const ranked = this.rerank(collected).slice(0, this.topK * 2);
|
|
2058
|
+
const prepared = {
|
|
2059
|
+
scope,
|
|
2060
|
+
retrieval: {
|
|
2061
|
+
primaryQuery,
|
|
2062
|
+
taskFrameQuery,
|
|
2063
|
+
warnings,
|
|
2064
|
+
degraded: okCount < branches.filter((branch) => branch.status !== "skipped").length,
|
|
2065
|
+
degradedReason: okCount === 0 ? "all_retrieval_failed" : warnings.length > 0 ? "partial_retrieval_failed" : void 0,
|
|
2066
|
+
durationMs: Date.now() - startedAt,
|
|
2067
|
+
targetBudgetMs: this.targetRetrievalMs,
|
|
2068
|
+
hardTimeoutMs: this.hardRetrievalTimeoutMs,
|
|
2069
|
+
branchStatus
|
|
2070
|
+
},
|
|
2071
|
+
context: this.buildContext(ranked),
|
|
2072
|
+
items: ranked
|
|
2073
|
+
};
|
|
2074
|
+
this.lastPreparedTurn = prepared.retrieval;
|
|
2075
|
+
return prepared;
|
|
2076
|
+
}
|
|
2077
|
+
async recordWork(event, context = {}) {
|
|
2078
|
+
const normalized = {
|
|
2079
|
+
...event,
|
|
2080
|
+
salience: event.salience || defaultSalience(event.kind, event.success),
|
|
2081
|
+
timestamp: event.timestamp || nowIso()
|
|
2082
|
+
};
|
|
2083
|
+
this.pushTouchedFiles(normalized.filePaths);
|
|
2084
|
+
this.pushWorkEvent(normalized);
|
|
2085
|
+
if (normalized.salience === "low") {
|
|
2086
|
+
this.bufferedLowSalience = [...this.bufferedLowSalience, normalized].slice(-20);
|
|
2087
|
+
return { success: true, buffered: true };
|
|
2088
|
+
}
|
|
2089
|
+
const { scope } = await this.resolveScope(context);
|
|
2090
|
+
return this.args.adapter.addMemory({
|
|
2091
|
+
project: scope.project,
|
|
2092
|
+
user_id: scope.userId,
|
|
2093
|
+
session_id: scope.sessionId,
|
|
2094
|
+
content: `${normalized.kind}: ${normalized.summary}${normalized.details ? ` (${normalized.details})` : ""}`,
|
|
2095
|
+
memory_type: toMemoryType(normalized.kind),
|
|
2096
|
+
event_date: normalized.timestamp,
|
|
2097
|
+
write_mode: "async",
|
|
2098
|
+
metadata: {
|
|
2099
|
+
runtime_auto: true,
|
|
2100
|
+
client_name: this.clientName,
|
|
2101
|
+
work_event_kind: normalized.kind,
|
|
2102
|
+
salience: normalized.salience,
|
|
2103
|
+
file_paths: normalized.filePaths || [],
|
|
2104
|
+
tool_name: normalized.toolName,
|
|
2105
|
+
success: normalized.success
|
|
2106
|
+
}
|
|
2107
|
+
});
|
|
2108
|
+
}
|
|
2109
|
+
async afterTurn(input, context = {}) {
|
|
2110
|
+
this.pushTouchedFiles(input.touchedFiles);
|
|
2111
|
+
const { scope } = await this.resolveScope(context);
|
|
2112
|
+
const result = await this.args.adapter.ingestSession({
|
|
2113
|
+
project: scope.project,
|
|
2114
|
+
session_id: scope.sessionId,
|
|
2115
|
+
user_id: scope.userId,
|
|
2116
|
+
messages: [
|
|
2117
|
+
{ role: "user", content: input.userMessage, timestamp: nowIso() },
|
|
2118
|
+
{ role: "assistant", content: input.assistantMessage, timestamp: nowIso() }
|
|
2119
|
+
],
|
|
2120
|
+
write_mode: "async"
|
|
2121
|
+
});
|
|
2122
|
+
this.mergedCount += result.memories_invalidated || 0;
|
|
2123
|
+
return {
|
|
2124
|
+
success: Boolean(result.success),
|
|
2125
|
+
sessionIngested: true,
|
|
2126
|
+
memoriesCreated: result.memories_created || 0,
|
|
2127
|
+
relationsCreated: result.relations_created || 0,
|
|
2128
|
+
invalidatedCount: result.memories_invalidated || 0,
|
|
2129
|
+
mergedCount: result.memories_invalidated || 0,
|
|
2130
|
+
droppedCount: 0,
|
|
2131
|
+
warnings: result.errors || []
|
|
2132
|
+
};
|
|
2133
|
+
}
|
|
2134
|
+
async flush(reason = "manual", context = {}) {
|
|
2135
|
+
if (this.bufferedLowSalience.length > 0) {
|
|
2136
|
+
const { scope } = await this.resolveScope(context);
|
|
2137
|
+
const summary = summarizeLowSalience(this.bufferedLowSalience);
|
|
2138
|
+
await this.args.adapter.addMemory({
|
|
2139
|
+
project: scope.project,
|
|
2140
|
+
user_id: scope.userId,
|
|
2141
|
+
session_id: scope.sessionId,
|
|
2142
|
+
content: summary,
|
|
2143
|
+
memory_type: "event",
|
|
2144
|
+
write_mode: "async",
|
|
2145
|
+
metadata: {
|
|
2146
|
+
runtime_auto: true,
|
|
2147
|
+
client_name: this.clientName,
|
|
2148
|
+
flush_reason: reason,
|
|
2149
|
+
salience: "low",
|
|
2150
|
+
summarized_count: this.bufferedLowSalience.length
|
|
2151
|
+
}
|
|
2152
|
+
});
|
|
2153
|
+
this.bufferedLowSalience = [];
|
|
2154
|
+
}
|
|
2155
|
+
await this.args.adapter.flushQueue();
|
|
2156
|
+
return this.status();
|
|
2157
|
+
}
|
|
2158
|
+
status() {
|
|
2159
|
+
return {
|
|
2160
|
+
clientName: this.clientName,
|
|
2161
|
+
scope: this.lastScope,
|
|
2162
|
+
queue: this.args.adapter.queueStatus(),
|
|
2163
|
+
retrieval: this.lastPreparedTurn,
|
|
2164
|
+
counters: {
|
|
2165
|
+
mergedCount: this.mergedCount,
|
|
2166
|
+
droppedCount: this.droppedCount,
|
|
2167
|
+
bufferedLowSalience: this.bufferedLowSalience.length
|
|
2168
|
+
}
|
|
2169
|
+
};
|
|
2170
|
+
}
|
|
2171
|
+
};
|
|
2172
|
+
|
|
1602
2173
|
// ../src/sdk/whisper.ts
|
|
2174
|
+
var PROJECT_CACHE_TTL_MS = 3e4;
|
|
2175
|
+
function isLikelyProjectId(projectRef) {
|
|
2176
|
+
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);
|
|
2177
|
+
}
|
|
1603
2178
|
var WhisperClient = class _WhisperClient {
|
|
1604
2179
|
constructor(config) {
|
|
1605
2180
|
this.config = config;
|
|
@@ -1619,7 +2194,7 @@ var WhisperClient = class _WhisperClient {
|
|
|
1619
2194
|
config.cache?.ttlMs ?? 7e3,
|
|
1620
2195
|
config.cache?.capacity ?? 500
|
|
1621
2196
|
);
|
|
1622
|
-
const queueStore =
|
|
2197
|
+
const queueStore = this.createQueueStore(config);
|
|
1623
2198
|
this.writeQueue = new WriteQueue({
|
|
1624
2199
|
store: queueStore,
|
|
1625
2200
|
maxBatchSize: config.queue?.maxBatchSize ?? 50,
|
|
@@ -1752,6 +2327,9 @@ var WhisperClient = class _WhisperClient {
|
|
|
1752
2327
|
sessionModule;
|
|
1753
2328
|
profileModule;
|
|
1754
2329
|
analyticsModule;
|
|
2330
|
+
projectRefToId = /* @__PURE__ */ new Map();
|
|
2331
|
+
projectCache = [];
|
|
2332
|
+
projectCacheExpiresAt = 0;
|
|
1755
2333
|
static fromEnv(overrides = {}) {
|
|
1756
2334
|
const env = typeof process !== "undefined" ? process.env : {};
|
|
1757
2335
|
const apiKey = overrides.apiKey || env.WHISPER_API_KEY || env.USEWHISPER_API_KEY || env.API_KEY;
|
|
@@ -1765,6 +2343,172 @@ var WhisperClient = class _WhisperClient {
|
|
|
1765
2343
|
...overrides
|
|
1766
2344
|
});
|
|
1767
2345
|
}
|
|
2346
|
+
createQueueStore(config) {
|
|
2347
|
+
const persistence = config.queue?.persistence || this.defaultQueuePersistence();
|
|
2348
|
+
if (persistence === "storage") {
|
|
2349
|
+
return createStorageQueueStore();
|
|
2350
|
+
}
|
|
2351
|
+
if (persistence === "file") {
|
|
2352
|
+
const filePath = config.queue?.filePath || this.defaultQueueFilePath();
|
|
2353
|
+
if (filePath) {
|
|
2354
|
+
return createFileQueueStore(filePath);
|
|
2355
|
+
}
|
|
2356
|
+
}
|
|
2357
|
+
return new InMemoryQueueStore();
|
|
2358
|
+
}
|
|
2359
|
+
defaultQueuePersistence() {
|
|
2360
|
+
const maybeWindow = globalThis.window;
|
|
2361
|
+
if (maybeWindow && typeof maybeWindow === "object") {
|
|
2362
|
+
const maybeStorage = globalThis.localStorage;
|
|
2363
|
+
return maybeStorage && typeof maybeStorage === "object" ? "storage" : "memory";
|
|
2364
|
+
}
|
|
2365
|
+
return "file";
|
|
2366
|
+
}
|
|
2367
|
+
defaultQueueFilePath() {
|
|
2368
|
+
if (typeof process === "undefined") return void 0;
|
|
2369
|
+
const path = process.env.WHISPER_QUEUE_FILE_PATH;
|
|
2370
|
+
if (path) return path;
|
|
2371
|
+
const home = process.env.USERPROFILE || process.env.HOME;
|
|
2372
|
+
if (!home) return void 0;
|
|
2373
|
+
const normalizedHome = home.replace(/[\\\/]+$/, "");
|
|
2374
|
+
return `${normalizedHome}/.whisper/sdk/queue.json`;
|
|
2375
|
+
}
|
|
2376
|
+
getRequiredProject(project) {
|
|
2377
|
+
const resolved = project || this.config.project;
|
|
2378
|
+
if (!resolved) {
|
|
2379
|
+
throw new RuntimeClientError({
|
|
2380
|
+
code: "MISSING_PROJECT",
|
|
2381
|
+
message: "Project is required",
|
|
2382
|
+
retryable: false
|
|
2383
|
+
});
|
|
2384
|
+
}
|
|
2385
|
+
return resolved;
|
|
2386
|
+
}
|
|
2387
|
+
async refreshProjectCache(force = false) {
|
|
2388
|
+
if (!force && Date.now() < this.projectCacheExpiresAt && this.projectCache.length > 0) {
|
|
2389
|
+
return this.projectCache;
|
|
2390
|
+
}
|
|
2391
|
+
const response = await this.runtimeClient.request({
|
|
2392
|
+
endpoint: "/v1/projects",
|
|
2393
|
+
method: "GET",
|
|
2394
|
+
operation: "get",
|
|
2395
|
+
idempotent: true
|
|
2396
|
+
});
|
|
2397
|
+
this.projectRefToId.clear();
|
|
2398
|
+
this.projectCache = response.data?.projects || [];
|
|
2399
|
+
for (const project of this.projectCache) {
|
|
2400
|
+
this.projectRefToId.set(project.id, project.id);
|
|
2401
|
+
this.projectRefToId.set(project.slug, project.id);
|
|
2402
|
+
this.projectRefToId.set(project.name, project.id);
|
|
2403
|
+
}
|
|
2404
|
+
this.projectCacheExpiresAt = Date.now() + PROJECT_CACHE_TTL_MS;
|
|
2405
|
+
return this.projectCache;
|
|
2406
|
+
}
|
|
2407
|
+
async fetchResolvedProject(projectRef) {
|
|
2408
|
+
try {
|
|
2409
|
+
const response = await this.runtimeClient.request({
|
|
2410
|
+
endpoint: `/v1/projects/resolve?project=${encodeURIComponent(projectRef)}`,
|
|
2411
|
+
method: "GET",
|
|
2412
|
+
operation: "get",
|
|
2413
|
+
idempotent: true
|
|
2414
|
+
});
|
|
2415
|
+
return response.data?.resolved || null;
|
|
2416
|
+
} catch (error) {
|
|
2417
|
+
if (error instanceof RuntimeClientError && error.status === 404) {
|
|
2418
|
+
return null;
|
|
2419
|
+
}
|
|
2420
|
+
throw error;
|
|
2421
|
+
}
|
|
2422
|
+
}
|
|
2423
|
+
async resolveProject(projectRef) {
|
|
2424
|
+
const resolvedRef = this.getRequiredProject(projectRef);
|
|
2425
|
+
const cachedProjects = await this.refreshProjectCache(false);
|
|
2426
|
+
const cachedProject = cachedProjects.find(
|
|
2427
|
+
(project) => project.id === resolvedRef || project.slug === resolvedRef || project.name === resolvedRef
|
|
2428
|
+
);
|
|
2429
|
+
if (cachedProject) {
|
|
2430
|
+
return cachedProject;
|
|
2431
|
+
}
|
|
2432
|
+
const resolvedProject = await this.fetchResolvedProject(resolvedRef);
|
|
2433
|
+
if (resolvedProject) {
|
|
2434
|
+
this.projectRefToId.set(resolvedProject.id, resolvedProject.id);
|
|
2435
|
+
this.projectRefToId.set(resolvedProject.slug, resolvedProject.id);
|
|
2436
|
+
this.projectRefToId.set(resolvedProject.name, resolvedProject.id);
|
|
2437
|
+
this.projectCache = [
|
|
2438
|
+
...this.projectCache.filter((project) => project.id !== resolvedProject.id),
|
|
2439
|
+
resolvedProject
|
|
2440
|
+
];
|
|
2441
|
+
this.projectCacheExpiresAt = Date.now() + PROJECT_CACHE_TTL_MS;
|
|
2442
|
+
return resolvedProject;
|
|
2443
|
+
}
|
|
2444
|
+
if (isLikelyProjectId(resolvedRef)) {
|
|
2445
|
+
return {
|
|
2446
|
+
id: resolvedRef,
|
|
2447
|
+
orgId: "",
|
|
2448
|
+
name: resolvedRef,
|
|
2449
|
+
slug: resolvedRef,
|
|
2450
|
+
createdAt: (/* @__PURE__ */ new Date(0)).toISOString(),
|
|
2451
|
+
updatedAt: (/* @__PURE__ */ new Date(0)).toISOString()
|
|
2452
|
+
};
|
|
2453
|
+
}
|
|
2454
|
+
throw new RuntimeClientError({
|
|
2455
|
+
code: "PROJECT_NOT_FOUND",
|
|
2456
|
+
message: `Project '${resolvedRef}' not found`,
|
|
2457
|
+
retryable: false
|
|
2458
|
+
});
|
|
2459
|
+
}
|
|
2460
|
+
async query(params) {
|
|
2461
|
+
const project = (await this.resolveProject(params.project)).id;
|
|
2462
|
+
const response = await this.runtimeClient.request({
|
|
2463
|
+
endpoint: "/v1/context/query",
|
|
2464
|
+
method: "POST",
|
|
2465
|
+
operation: "search",
|
|
2466
|
+
body: {
|
|
2467
|
+
...params,
|
|
2468
|
+
project
|
|
2469
|
+
},
|
|
2470
|
+
idempotent: true
|
|
2471
|
+
});
|
|
2472
|
+
return response.data;
|
|
2473
|
+
}
|
|
2474
|
+
async ingestSession(params) {
|
|
2475
|
+
const project = (await this.resolveProject(params.project)).id;
|
|
2476
|
+
const response = await this.runtimeClient.request({
|
|
2477
|
+
endpoint: "/v1/memory/ingest/session",
|
|
2478
|
+
method: "POST",
|
|
2479
|
+
operation: "session",
|
|
2480
|
+
body: {
|
|
2481
|
+
...params,
|
|
2482
|
+
project
|
|
2483
|
+
}
|
|
2484
|
+
});
|
|
2485
|
+
return response.data;
|
|
2486
|
+
}
|
|
2487
|
+
createAgentRuntime(options = {}) {
|
|
2488
|
+
const baseContext = {
|
|
2489
|
+
workspacePath: options.workspacePath,
|
|
2490
|
+
project: options.project || this.config.project,
|
|
2491
|
+
userId: options.userId,
|
|
2492
|
+
sessionId: options.sessionId,
|
|
2493
|
+
traceId: options.traceId,
|
|
2494
|
+
clientName: options.clientName
|
|
2495
|
+
};
|
|
2496
|
+
return new WhisperAgentRuntime({
|
|
2497
|
+
baseContext,
|
|
2498
|
+
options,
|
|
2499
|
+
adapter: {
|
|
2500
|
+
resolveProject: (project) => this.resolveProject(project),
|
|
2501
|
+
query: (params) => this.query(params),
|
|
2502
|
+
ingestSession: (params) => this.ingestSession(params),
|
|
2503
|
+
getSessionMemories: (params) => this.memory.getSessionMemories(params),
|
|
2504
|
+
getUserProfile: (params) => this.memory.getUserProfile(params),
|
|
2505
|
+
searchMemories: (params) => this.memory.search(params),
|
|
2506
|
+
addMemory: (params) => this.memory.add(params),
|
|
2507
|
+
queueStatus: () => this.queue.status(),
|
|
2508
|
+
flushQueue: () => this.queue.flush()
|
|
2509
|
+
}
|
|
2510
|
+
});
|
|
2511
|
+
}
|
|
1768
2512
|
withRunContext(context) {
|
|
1769
2513
|
const base = this;
|
|
1770
2514
|
return {
|
|
@@ -2306,7 +3050,7 @@ var DEFAULT_MAX_ATTEMPTS = 3;
|
|
|
2306
3050
|
var DEFAULT_BASE_DELAY_MS = 250;
|
|
2307
3051
|
var DEFAULT_MAX_DELAY_MS = 2e3;
|
|
2308
3052
|
var DEFAULT_TIMEOUT_MS = 15e3;
|
|
2309
|
-
var
|
|
3053
|
+
var PROJECT_CACHE_TTL_MS2 = 3e4;
|
|
2310
3054
|
var DEPRECATION_WARNINGS2 = /* @__PURE__ */ new Set();
|
|
2311
3055
|
function warnDeprecatedOnce2(key, message) {
|
|
2312
3056
|
if (DEPRECATION_WARNINGS2.has(key)) return;
|
|
@@ -2315,7 +3059,7 @@ function warnDeprecatedOnce2(key, message) {
|
|
|
2315
3059
|
console.warn(message);
|
|
2316
3060
|
}
|
|
2317
3061
|
}
|
|
2318
|
-
function
|
|
3062
|
+
function isLikelyProjectId2(projectRef) {
|
|
2319
3063
|
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);
|
|
2320
3064
|
}
|
|
2321
3065
|
function normalizeBaseUrl2(url) {
|
|
@@ -2437,7 +3181,7 @@ var WhisperContext = class _WhisperContext {
|
|
|
2437
3181
|
}
|
|
2438
3182
|
}
|
|
2439
3183
|
}
|
|
2440
|
-
this.projectCacheExpiresAt = Date.now() +
|
|
3184
|
+
this.projectCacheExpiresAt = Date.now() + PROJECT_CACHE_TTL_MS2;
|
|
2441
3185
|
return this.projectCache;
|
|
2442
3186
|
}
|
|
2443
3187
|
async fetchResolvedProject(projectRef) {
|
|
@@ -2470,10 +3214,10 @@ var WhisperContext = class _WhisperContext {
|
|
|
2470
3214
|
...this.projectCache.filter((project) => project.id !== resolvedProject.id),
|
|
2471
3215
|
resolvedProject
|
|
2472
3216
|
];
|
|
2473
|
-
this.projectCacheExpiresAt = Date.now() +
|
|
3217
|
+
this.projectCacheExpiresAt = Date.now() + PROJECT_CACHE_TTL_MS2;
|
|
2474
3218
|
return resolvedProject;
|
|
2475
3219
|
}
|
|
2476
|
-
if (
|
|
3220
|
+
if (isLikelyProjectId2(resolvedRef)) {
|
|
2477
3221
|
return {
|
|
2478
3222
|
id: resolvedRef,
|
|
2479
3223
|
orgId: "",
|
|
@@ -2505,7 +3249,7 @@ var WhisperContext = class _WhisperContext {
|
|
|
2505
3249
|
message: `Project reference '${projectRef}' matched multiple projects. Use project id instead.`
|
|
2506
3250
|
});
|
|
2507
3251
|
}
|
|
2508
|
-
if (
|
|
3252
|
+
if (isLikelyProjectId2(projectRef)) {
|
|
2509
3253
|
return projectRef;
|
|
2510
3254
|
}
|
|
2511
3255
|
const resolvedProject = await this.fetchResolvedProject(projectRef);
|
|
@@ -2517,7 +3261,7 @@ var WhisperContext = class _WhisperContext {
|
|
|
2517
3261
|
...this.projectCache.filter((project) => project.id !== resolvedProject.id),
|
|
2518
3262
|
resolvedProject
|
|
2519
3263
|
];
|
|
2520
|
-
this.projectCacheExpiresAt = Date.now() +
|
|
3264
|
+
this.projectCacheExpiresAt = Date.now() + PROJECT_CACHE_TTL_MS2;
|
|
2521
3265
|
return resolvedProject.id;
|
|
2522
3266
|
}
|
|
2523
3267
|
throw new WhisperError({
|
|
@@ -2534,7 +3278,7 @@ var WhisperContext = class _WhisperContext {
|
|
|
2534
3278
|
candidates.add(match.id);
|
|
2535
3279
|
candidates.add(match.slug);
|
|
2536
3280
|
candidates.add(match.name);
|
|
2537
|
-
} else if (
|
|
3281
|
+
} else if (isLikelyProjectId2(projectRef)) {
|
|
2538
3282
|
const byId = projects.find((p) => p.id === projectRef);
|
|
2539
3283
|
if (byId) {
|
|
2540
3284
|
candidates.add(byId.slug);
|
|
@@ -2695,7 +3439,7 @@ var WhisperContext = class _WhisperContext {
|
|
|
2695
3439
|
...this.projectCache.filter((p) => p.id !== project.id),
|
|
2696
3440
|
project
|
|
2697
3441
|
];
|
|
2698
|
-
this.projectCacheExpiresAt = Date.now() +
|
|
3442
|
+
this.projectCacheExpiresAt = Date.now() + PROJECT_CACHE_TTL_MS2;
|
|
2699
3443
|
return project;
|
|
2700
3444
|
}
|
|
2701
3445
|
async listProjects() {
|
|
@@ -2706,7 +3450,7 @@ var WhisperContext = class _WhisperContext {
|
|
|
2706
3450
|
this.projectRefToId.set(p.slug, p.id);
|
|
2707
3451
|
this.projectRefToId.set(p.name, p.id);
|
|
2708
3452
|
}
|
|
2709
|
-
this.projectCacheExpiresAt = Date.now() +
|
|
3453
|
+
this.projectCacheExpiresAt = Date.now() + PROJECT_CACHE_TTL_MS2;
|
|
2710
3454
|
return projects;
|
|
2711
3455
|
}
|
|
2712
3456
|
async getProject(id) {
|