hippo-memory 1.52.0 → 1.52.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api.d.ts +4 -0
- package/dist/api.js +71 -13
- package/dist/audit.d.ts +1 -0
- package/dist/audit.js +1 -1
- package/dist/auth.d.ts +1 -0
- package/dist/auth.js +6 -1
- package/dist/capture-error.js +2 -2
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +71 -27
- package/dist/config.d.ts +15 -0
- package/dist/config.js +6 -0
- package/dist/db.js +4 -3
- package/dist/half-life-migration.js +2 -3
- package/dist/prompt-recall.d.ts +33 -0
- package/dist/prompt-recall.js +68 -0
- package/dist/reject-flow.js +5 -8
- package/dist/store.d.ts +16 -1
- package/dist/store.js +131 -133
- package/dist/token-ledger.d.ts +2 -1
- package/dist/token-ledger.js +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/working-memory.js +5 -8
- package/extensions/openclaw-plugin/openclaw.plugin.json +1 -1
- package/extensions/openclaw-plugin/package.json +1 -1
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
package/dist/store.js
CHANGED
|
@@ -13,6 +13,7 @@ import { rowToSessionHandoff, isHandoffOutcome } from './handoff.js';
|
|
|
13
13
|
import { CARD_TRANSITIONS, CARD_LEASE_MS } from './card.js';
|
|
14
14
|
import { tokenize, markRetrieved } from './search.js';
|
|
15
15
|
import { isRecallBoostAblated } from './ablation.js';
|
|
16
|
+
import { rarestPromptTerms, RAREST_TERM_COUNT } from './prompt-recall.js';
|
|
16
17
|
import { appendAuditEvent } from './audit.js';
|
|
17
18
|
import { resolveTenantId } from './tenant.js';
|
|
18
19
|
import { deriveOriginProject, originFromSource, findHippoStoreDir, realpathOrResolve } from './project-identity.js';
|
|
@@ -113,6 +114,10 @@ export function isInitialized(hippoRoot) {
|
|
|
113
114
|
return fs.existsSync(path.join(hippoRoot, 'hippo.db'));
|
|
114
115
|
}
|
|
115
116
|
export function initStore(hippoRoot) {
|
|
117
|
+
closeHippoDb(openStore(hippoRoot));
|
|
118
|
+
}
|
|
119
|
+
/** One open connection with init done on it, for callers who used to pay for `initStore` + a second `openHippoDb`. */
|
|
120
|
+
export function openStore(hippoRoot) {
|
|
116
121
|
ensureMirrorDirectories(hippoRoot);
|
|
117
122
|
const db = openHippoDb(hippoRoot);
|
|
118
123
|
try {
|
|
@@ -121,9 +126,16 @@ export function initStore(hippoRoot) {
|
|
|
121
126
|
syncMirrorFiles(hippoRoot, db);
|
|
122
127
|
}
|
|
123
128
|
recordHalfLifeBaseForNewStore(db);
|
|
129
|
+
return db;
|
|
124
130
|
}
|
|
125
|
-
|
|
126
|
-
|
|
131
|
+
catch (error) {
|
|
132
|
+
try {
|
|
133
|
+
closeHippoDb(db);
|
|
134
|
+
}
|
|
135
|
+
catch {
|
|
136
|
+
// Best effort only; surface the original init error.
|
|
137
|
+
}
|
|
138
|
+
throw error;
|
|
127
139
|
}
|
|
128
140
|
}
|
|
129
141
|
/** `meta` key holding the default half-life base a store's memories are on (src/half-life-migration.ts). */
|
|
@@ -1111,8 +1123,7 @@ function syncMirrorFiles(hippoRoot, db) {
|
|
|
1111
1123
|
}
|
|
1112
1124
|
/** Load the derived index from SQLite. Read-only: index.json is only ever written by `rebuildIndex`. */
|
|
1113
1125
|
export function loadIndex(hippoRoot) {
|
|
1114
|
-
|
|
1115
|
-
const db = openHippoDb(hippoRoot);
|
|
1126
|
+
const db = openStore(hippoRoot);
|
|
1116
1127
|
try {
|
|
1117
1128
|
return buildIndexFromDb(db);
|
|
1118
1129
|
}
|
|
@@ -1131,8 +1142,7 @@ export function loadIndex(hippoRoot) {
|
|
|
1131
1142
|
* is left untouched; only `rebuildIndex` writes it.
|
|
1132
1143
|
*/
|
|
1133
1144
|
export function saveIndex(hippoRoot, index) {
|
|
1134
|
-
|
|
1135
|
-
const db = openHippoDb(hippoRoot);
|
|
1145
|
+
const db = openStore(hippoRoot);
|
|
1136
1146
|
try {
|
|
1137
1147
|
db.exec('BEGIN');
|
|
1138
1148
|
try {
|
|
@@ -1204,10 +1214,9 @@ function stampOriginProjectForImport(hippoRoot, entry) {
|
|
|
1204
1214
|
};
|
|
1205
1215
|
}
|
|
1206
1216
|
export function writeEntry(hippoRoot, entry, opts) {
|
|
1207
|
-
|
|
1208
|
-
const stamped = stampOriginProject(hippoRoot, entry);
|
|
1209
|
-
const db = openHippoDb(hippoRoot);
|
|
1217
|
+
const db = openStore(hippoRoot);
|
|
1210
1218
|
try {
|
|
1219
|
+
const stamped = stampOriginProject(hippoRoot, entry);
|
|
1211
1220
|
writeEntryDbOnly(db, stamped, opts);
|
|
1212
1221
|
opts?.afterCommit?.();
|
|
1213
1222
|
writeEntryMirrors(hippoRoot, stamped);
|
|
@@ -1286,8 +1295,7 @@ export function writeEntryMirrors(hippoRoot, entry) {
|
|
|
1286
1295
|
* legacy single-tenant callers and the writeEntry/readEntry round-trip.
|
|
1287
1296
|
*/
|
|
1288
1297
|
export function readEntry(hippoRoot, id, tenantId) {
|
|
1289
|
-
|
|
1290
|
-
const db = openHippoDb(hippoRoot);
|
|
1298
|
+
const db = openStore(hippoRoot);
|
|
1291
1299
|
try {
|
|
1292
1300
|
// SAFETY: both branches select exactly MEMORY_SELECT_COLUMNS, matching
|
|
1293
1301
|
// MemoryRow's field set.
|
|
@@ -1310,8 +1318,7 @@ export function loadEntriesByIds(hippoRoot, ids, tenantId) {
|
|
|
1310
1318
|
if (ids.length === 0)
|
|
1311
1319
|
return [];
|
|
1312
1320
|
const capped = ids.slice(0, 500);
|
|
1313
|
-
|
|
1314
|
-
const db = openHippoDb(hippoRoot);
|
|
1321
|
+
const db = openStore(hippoRoot);
|
|
1315
1322
|
try {
|
|
1316
1323
|
const placeholders = capped.map(() => '?').join(',');
|
|
1317
1324
|
// T2: no ORDER BY meant row order followed SQLite's IN(...) scan order
|
|
@@ -1383,8 +1390,7 @@ export function strengthenRetrieved(hippoRoot, ids, tenantId) {
|
|
|
1383
1390
|
export function loadSessionRawMemories(hippoRoot, sessionId, tenantId, cap) {
|
|
1384
1391
|
if (!sessionId)
|
|
1385
1392
|
return [];
|
|
1386
|
-
|
|
1387
|
-
const db = openHippoDb(hippoRoot);
|
|
1393
|
+
const db = openStore(hippoRoot);
|
|
1388
1394
|
try {
|
|
1389
1395
|
const params = [];
|
|
1390
1396
|
let sql = `SELECT ${MEMORY_SELECT_COLUMNS} FROM memories WHERE kind = 'raw' AND source_session_id = ? AND superseded_by IS NULL`;
|
|
@@ -1429,8 +1435,7 @@ export function loadSessionRawMemories(hippoRoot, sessionId, tenantId, cap) {
|
|
|
1429
1435
|
export function countSessionRawMemories(hippoRoot, sessionId, tenantId, scope) {
|
|
1430
1436
|
if (!sessionId)
|
|
1431
1437
|
return 0;
|
|
1432
|
-
|
|
1433
|
-
const db = openHippoDb(hippoRoot);
|
|
1438
|
+
const db = openStore(hippoRoot);
|
|
1434
1439
|
try {
|
|
1435
1440
|
const params = [];
|
|
1436
1441
|
let sql = `SELECT COUNT(*) AS c FROM memories WHERE kind = 'raw' AND source_session_id = ? AND superseded_by IS NULL`;
|
|
@@ -1483,8 +1488,7 @@ export function loadFreshRawMemories(hippoRoot, count, tenantId, sessionId) {
|
|
|
1483
1488
|
if (count <= 0)
|
|
1484
1489
|
return [];
|
|
1485
1490
|
const capped = Math.min(count, 200);
|
|
1486
|
-
|
|
1487
|
-
const db = openHippoDb(hippoRoot);
|
|
1491
|
+
const db = openStore(hippoRoot);
|
|
1488
1492
|
try {
|
|
1489
1493
|
const params = [];
|
|
1490
1494
|
let sql = `SELECT ${MEMORY_SELECT_COLUMNS} FROM memories WHERE kind = 'raw' AND superseded_by IS NULL`;
|
|
@@ -1517,8 +1521,7 @@ export function loadFreshRawMemories(hippoRoot, count, tenantId, sessionId) {
|
|
|
1517
1521
|
* Used by `drillDown` (Task 3).
|
|
1518
1522
|
*/
|
|
1519
1523
|
export function loadChildrenOf(hippoRoot, parentId, tenantId) {
|
|
1520
|
-
|
|
1521
|
-
const db = openHippoDb(hippoRoot);
|
|
1524
|
+
const db = openStore(hippoRoot);
|
|
1522
1525
|
try {
|
|
1523
1526
|
// SAFETY: both branches select exactly MEMORY_SELECT_COLUMNS, matching
|
|
1524
1527
|
// MemoryRow's field set.
|
|
@@ -1586,8 +1589,7 @@ export function deleteEntryCore(db, id, opts) {
|
|
|
1586
1589
|
* existing caller.
|
|
1587
1590
|
*/
|
|
1588
1591
|
export function deleteEntry(hippoRoot, id, opts) {
|
|
1589
|
-
|
|
1590
|
-
const db = openHippoDb(hippoRoot);
|
|
1592
|
+
const db = openStore(hippoRoot);
|
|
1591
1593
|
try {
|
|
1592
1594
|
const result = deleteEntryCore(db, id, opts);
|
|
1593
1595
|
if (!result)
|
|
@@ -1621,8 +1623,7 @@ export function batchWriteAndDelete(hippoRoot, toWrite, toDeleteIds, opts) {
|
|
|
1621
1623
|
const dormantMoves = opts?.dormant ?? [];
|
|
1622
1624
|
if (toWrite.length === 0 && toDeleteIds.length === 0 && dormantMoves.length === 0)
|
|
1623
1625
|
return [];
|
|
1624
|
-
|
|
1625
|
-
const db = openHippoDb(hippoRoot);
|
|
1626
|
+
const db = openStore(hippoRoot);
|
|
1626
1627
|
try {
|
|
1627
1628
|
// BEGIN IMMEDIATE (codex delta-review P2): the AT1 tombstone probes below
|
|
1628
1629
|
// READ before the first write. Under a deferred BEGIN, that read pins a
|
|
@@ -1788,8 +1789,7 @@ export function batchWriteAndDelete(hippoRoot, toWrite, toDeleteIds, opts) {
|
|
|
1788
1789
|
* paths that surface results to a user MUST pass a resolved tenant.
|
|
1789
1790
|
*/
|
|
1790
1791
|
export function loadAllEntries(hippoRoot, tenantId) {
|
|
1791
|
-
|
|
1792
|
-
const db = openHippoDb(hippoRoot);
|
|
1792
|
+
const db = openStore(hippoRoot);
|
|
1793
1793
|
try {
|
|
1794
1794
|
return selectAllEntries(db, tenantId);
|
|
1795
1795
|
}
|
|
@@ -1806,15 +1806,26 @@ export function selectAllEntries(db, tenantId) {
|
|
|
1806
1806
|
: db.prepare(`SELECT ${MEMORY_SELECT_COLUMNS} FROM memories ORDER BY created ASC, id ASC`).all();
|
|
1807
1807
|
return rows.map(rowToEntry);
|
|
1808
1808
|
}
|
|
1809
|
+
// Content of every tenant row tagged `tag`, without reading the rest of the store.
|
|
1810
|
+
// `instr` is a substring prefilter over the raw JSON; `includes` below re-checks exactly.
|
|
1811
|
+
export function loadContentsWithTag(hippoRoot, tenantId, tag) {
|
|
1812
|
+
const db = openStore(hippoRoot);
|
|
1813
|
+
try {
|
|
1814
|
+
/** SAFETY: rows' shape matches the two columns named in the SELECT below. */
|
|
1815
|
+
const rows = db.prepare(`SELECT content, tags_json FROM memories WHERE tenant_id = ? AND instr(tags_json, ?) > 0`).all(tenantId, JSON.stringify(tag));
|
|
1816
|
+
return rows.filter((r) => parseJsonArray(r.tags_json).includes(tag)).map((r) => r.content);
|
|
1817
|
+
}
|
|
1818
|
+
finally {
|
|
1819
|
+
closeHippoDb(db);
|
|
1820
|
+
}
|
|
1821
|
+
}
|
|
1809
1822
|
// The pins plus the `recentNeeded` newest rows that pass `admit`, for ambient
|
|
1810
|
-
// injection. One connection
|
|
1811
|
-
|
|
1812
|
-
export function loadAmbientCandidates(hippoRoot, tenantId, recentNeeded, admit) {
|
|
1813
|
-
initStore(hippoRoot);
|
|
1823
|
+
// injection. One connection; `recall` piggybacks the Z1 FTS query on it too.
|
|
1824
|
+
export function loadAmbientCandidates(hippoRoot, tenantId, recentNeeded, admit, recall) {
|
|
1814
1825
|
// A SQL LIMIT takes an integer; the Array.slice this replaced truncated one,
|
|
1815
1826
|
// and include_recent is any non-negative finite number at the HTTP edge.
|
|
1816
1827
|
const needed = Math.trunc(recentNeeded);
|
|
1817
|
-
const db =
|
|
1828
|
+
const db = openStore(hippoRoot);
|
|
1818
1829
|
try {
|
|
1819
1830
|
// SAFETY: every `where` below starts from MEMORY_SELECT_COLUMNS' table.
|
|
1820
1831
|
const run = (where, params) => db.prepare(`SELECT ${MEMORY_SELECT_COLUMNS} FROM memories WHERE ${where}`).all(...params).map(rowToEntry);
|
|
@@ -1842,10 +1853,17 @@ export function loadAmbientCandidates(hippoRoot, tenantId, recentNeeded, admit)
|
|
|
1842
1853
|
}
|
|
1843
1854
|
// loadAllEntries' order: rankedPinned's comparator can tie and Array.sort
|
|
1844
1855
|
// is stable, so input order is load-bearing downstream.
|
|
1845
|
-
|
|
1856
|
+
const entries = [...byId.values()].sort((a, b) => {
|
|
1846
1857
|
const byCreated = a.created.localeCompare(b.created);
|
|
1847
1858
|
return byCreated !== 0 ? byCreated : a.id.localeCompare(b.id);
|
|
1848
1859
|
});
|
|
1860
|
+
if (!recall)
|
|
1861
|
+
return { entries };
|
|
1862
|
+
const ftsQuery = pickRarestFtsQuery(db, recall.terms);
|
|
1863
|
+
const recallEntries = ftsQuery
|
|
1864
|
+
? loadRecallSearchEntriesFromDb(db, ftsQuery, recall.limit, tenantId, undefined, 'exact', false)
|
|
1865
|
+
: [];
|
|
1866
|
+
return { entries, recall: recallEntries };
|
|
1849
1867
|
}
|
|
1850
1868
|
finally {
|
|
1851
1869
|
closeHippoDb(db);
|
|
@@ -1859,8 +1877,7 @@ export function loadAmbientCandidates(hippoRoot, tenantId, recentNeeded, admit)
|
|
|
1859
1877
|
* by tenant_id. Cross-tenant memories never surface. Omitted = no filter.
|
|
1860
1878
|
*/
|
|
1861
1879
|
export function loadSearchEntries(hippoRoot, query, limit = DEFAULT_SEARCH_CANDIDATE_LIMIT, tenantId) {
|
|
1862
|
-
|
|
1863
|
-
const db = openHippoDb(hippoRoot);
|
|
1880
|
+
const db = openStore(hippoRoot);
|
|
1864
1881
|
try {
|
|
1865
1882
|
return loadSearchRows(db, query, limit, tenantId).map(rowToEntry);
|
|
1866
1883
|
}
|
|
@@ -1893,30 +1910,53 @@ export function loadSearchEntries(hippoRoot, query, limit = DEFAULT_SEARCH_CANDI
|
|
|
1893
1910
|
* undefined as "no tenant filter" for legacy callers.
|
|
1894
1911
|
*/
|
|
1895
1912
|
export function loadRecallSearchEntries(hippoRoot, query, limit = DEFAULT_SEARCH_CANDIDATE_LIMIT, tenantId, requestedScope, explicitScopeMode = 'exact', includeSuperseded = true) {
|
|
1896
|
-
|
|
1897
|
-
const db = openHippoDb(hippoRoot);
|
|
1913
|
+
const db = openStore(hippoRoot);
|
|
1898
1914
|
try {
|
|
1899
|
-
|
|
1900
|
-
// 'exact' — api.recall semantics: narrow to m.scope = requested.
|
|
1901
|
-
// 'additive' — CLI --scope semantics (v1.25.0): default-admitted set
|
|
1902
|
-
// PLUS the requested scope; see RecallScopeFilter docs.
|
|
1903
|
-
const scopeFilter = requestedScope && requestedScope !== ''
|
|
1904
|
-
? explicitScopeMode === 'additive'
|
|
1905
|
-
? { mode: 'default-deny-or-exact', value: requestedScope }
|
|
1906
|
-
: { mode: 'exact', value: requestedScope }
|
|
1907
|
-
: { mode: 'default-deny' };
|
|
1908
|
-
return loadSearchRows(db, query, limit, tenantId, scopeFilter, includeSuperseded).map(rowToEntry);
|
|
1915
|
+
return loadRecallSearchEntriesFromDb(db, query, limit, tenantId, requestedScope, explicitScopeMode, includeSuperseded);
|
|
1909
1916
|
}
|
|
1910
1917
|
finally {
|
|
1911
1918
|
closeHippoDb(db);
|
|
1912
1919
|
}
|
|
1913
1920
|
}
|
|
1921
|
+
// Split out so callers with an already-open db (Z1 prompt-recall path) skip
|
|
1922
|
+
// the initStore+open/close cycle per store per call.
|
|
1923
|
+
export function loadRecallSearchEntriesFromDb(db, query, limit = DEFAULT_SEARCH_CANDIDATE_LIMIT, tenantId, requestedScope, explicitScopeMode = 'exact', includeSuperseded = true) {
|
|
1924
|
+
// 'exact' narrows to requestedScope; 'additive' adds it to the default-admitted set.
|
|
1925
|
+
const scopeFilter = requestedScope && requestedScope !== ''
|
|
1926
|
+
? explicitScopeMode === 'additive'
|
|
1927
|
+
? { mode: 'default-deny-or-exact', value: requestedScope }
|
|
1928
|
+
: { mode: 'exact', value: requestedScope }
|
|
1929
|
+
: { mode: 'default-deny' };
|
|
1930
|
+
return loadSearchRows(db, query, limit, tenantId, scopeFilter, includeSuperseded).map(rowToEntry);
|
|
1931
|
+
}
|
|
1932
|
+
/** Rarest-K prompt terms for this connection's FTS index, as a space-joined query string.
|
|
1933
|
+
* Without FTS, returns the first 32 terms, as before rarest-term selection. */
|
|
1934
|
+
export function pickRarestFtsQuery(db, terms, maxTerms = RAREST_TERM_COUNT) {
|
|
1935
|
+
// The LIKE path has no bm25 ranking to bound, so it keeps the pre-rarest 32-term query.
|
|
1936
|
+
if (!isFtsAvailable(db))
|
|
1937
|
+
return terms.slice(0, 32).join(' ');
|
|
1938
|
+
db.exec(`CREATE VIRTUAL TABLE IF NOT EXISTS temp.z1_rarest_vocab USING fts5vocab(main, 'memories_fts', 'row')`);
|
|
1939
|
+
// unicode61 splits `journal_mode` into two vocab terms; a term's count is its rarest part's (an upper bound).
|
|
1940
|
+
const partsOf = (t) => t.split(/[^\p{L}\p{N}]+/u).filter(Boolean);
|
|
1941
|
+
const vocab = Array.from(new Set(terms.flatMap(partsOf)));
|
|
1942
|
+
if (vocab.length === 0)
|
|
1943
|
+
return '';
|
|
1944
|
+
// SAFETY: rows' shape matches the two columns named in the SELECT.
|
|
1945
|
+
const rows = db
|
|
1946
|
+
.prepare(`SELECT term, doc FROM temp.z1_rarest_vocab WHERE term IN (${vocab.map(() => '?').join(', ')})`)
|
|
1947
|
+
.all(...vocab);
|
|
1948
|
+
const counts = new Map(rows.map((r) => [r.term, r.doc]));
|
|
1949
|
+
const docCount = (t) => {
|
|
1950
|
+
const parts = partsOf(t);
|
|
1951
|
+
return parts.length === 0 ? 0 : Math.min(...parts.map((x) => counts.get(x) ?? 0));
|
|
1952
|
+
};
|
|
1953
|
+
return rarestPromptTerms(terms, docCount, maxTerms).join(' ');
|
|
1954
|
+
}
|
|
1914
1955
|
/**
|
|
1915
1956
|
* Rebuild mirrors from SQLite, importing any legacy markdown files not already present.
|
|
1916
1957
|
*/
|
|
1917
1958
|
export function rebuildIndex(hippoRoot) {
|
|
1918
|
-
|
|
1919
|
-
const db = openHippoDb(hippoRoot);
|
|
1959
|
+
const db = openStore(hippoRoot);
|
|
1920
1960
|
try {
|
|
1921
1961
|
// SAFETY: rows' shape matches the single `id` column selected above.
|
|
1922
1962
|
const existingIds = new Set(db.prepare(`SELECT id FROM memories`).all().map((row) => row.id));
|
|
@@ -1967,8 +2007,7 @@ export function rebuildIndex(hippoRoot) {
|
|
|
1967
2007
|
}
|
|
1968
2008
|
}
|
|
1969
2009
|
export function updateStats(hippoRoot, delta) {
|
|
1970
|
-
|
|
1971
|
-
const db = openHippoDb(hippoRoot);
|
|
2010
|
+
const db = openStore(hippoRoot);
|
|
1972
2011
|
try {
|
|
1973
2012
|
// One atomic statement per counter, and only for counters the caller
|
|
1974
2013
|
// named: the read-modify-write this replaces both lost increments to a
|
|
@@ -1995,8 +2034,7 @@ export function updateStats(hippoRoot, delta) {
|
|
|
1995
2034
|
}
|
|
1996
2035
|
}
|
|
1997
2036
|
export function loadStats(hippoRoot) {
|
|
1998
|
-
|
|
1999
|
-
const db = openHippoDb(hippoRoot);
|
|
2037
|
+
const db = openStore(hippoRoot);
|
|
2000
2038
|
try {
|
|
2001
2039
|
return buildStatsFromDb(db);
|
|
2002
2040
|
}
|
|
@@ -2005,8 +2043,7 @@ export function loadStats(hippoRoot) {
|
|
|
2005
2043
|
}
|
|
2006
2044
|
}
|
|
2007
2045
|
export function appendConsolidationRun(hippoRoot, run) {
|
|
2008
|
-
|
|
2009
|
-
const db = openHippoDb(hippoRoot);
|
|
2046
|
+
const db = openStore(hippoRoot);
|
|
2010
2047
|
try {
|
|
2011
2048
|
db.prepare(`INSERT INTO consolidation_runs(timestamp, decayed, merged, removed) VALUES (?, ?, ?, ?)`).run(run.timestamp, run.decayed, run.merged, run.removed);
|
|
2012
2049
|
pruneConsolidationRuns(db, 50);
|
|
@@ -2018,8 +2055,7 @@ export function appendConsolidationRun(hippoRoot, run) {
|
|
|
2018
2055
|
}
|
|
2019
2056
|
/** Rows a tenant created since the last sleep (runs are host-wide), looking back at most 24 hours. */
|
|
2020
2057
|
export function countCreatedSinceLastSleep(hippoRoot, tenantId, now = new Date()) {
|
|
2021
|
-
|
|
2022
|
-
const db = openHippoDb(hippoRoot);
|
|
2058
|
+
const db = openStore(hippoRoot);
|
|
2023
2059
|
try {
|
|
2024
2060
|
const dayAgo = new Date(now.getTime() - 86_400_000).toISOString();
|
|
2025
2061
|
const row = db.prepare(`SELECT COUNT(*) AS n FROM memories WHERE tenant_id = ?
|
|
@@ -2035,8 +2071,7 @@ export function countCreatedSinceLastSleep(hippoRoot, tenantId, now = new Date()
|
|
|
2035
2071
|
* Uses consolidation_runs timestamps to compute session intervals.
|
|
2036
2072
|
*/
|
|
2037
2073
|
export function loadSessionDecayContext(hippoRoot) {
|
|
2038
|
-
|
|
2039
|
-
const db = openHippoDb(hippoRoot);
|
|
2074
|
+
const db = openStore(hippoRoot);
|
|
2040
2075
|
try {
|
|
2041
2076
|
// Get recent consolidation timestamps (last 20)
|
|
2042
2077
|
// SAFETY: rows' shape matches the single `timestamp` column above.
|
|
@@ -2063,8 +2098,7 @@ export function loadSessionDecayContext(hippoRoot) {
|
|
|
2063
2098
|
* Increment the sleep counter. Called after each consolidation run.
|
|
2064
2099
|
*/
|
|
2065
2100
|
export function incrementSleepCount(hippoRoot) {
|
|
2066
|
-
|
|
2067
|
-
const db = openHippoDb(hippoRoot);
|
|
2101
|
+
const db = openStore(hippoRoot);
|
|
2068
2102
|
try {
|
|
2069
2103
|
const current = Number(getMeta(db, 'sleep_count', '0')) || 0;
|
|
2070
2104
|
setMeta(db, 'sleep_count', String(current + 1));
|
|
@@ -2101,8 +2135,7 @@ export function assertTenantId(fnName, value) {
|
|
|
2101
2135
|
}
|
|
2102
2136
|
export function saveActiveTaskSnapshot(hippoRoot, tenantId, snapshot) {
|
|
2103
2137
|
assertTenantId('saveActiveTaskSnapshot', tenantId);
|
|
2104
|
-
|
|
2105
|
-
const db = openHippoDb(hippoRoot);
|
|
2138
|
+
const db = openStore(hippoRoot);
|
|
2106
2139
|
const now = new Date().toISOString();
|
|
2107
2140
|
try {
|
|
2108
2141
|
db.exec('BEGIN');
|
|
@@ -2141,8 +2174,7 @@ export function saveActiveTaskSnapshot(hippoRoot, tenantId, snapshot) {
|
|
|
2141
2174
|
}
|
|
2142
2175
|
export function loadActiveTaskSnapshot(hippoRoot, tenantId) {
|
|
2143
2176
|
assertTenantId('loadActiveTaskSnapshot', tenantId);
|
|
2144
|
-
|
|
2145
|
-
const db = openHippoDb(hippoRoot);
|
|
2177
|
+
const db = openStore(hippoRoot);
|
|
2146
2178
|
try {
|
|
2147
2179
|
// SAFETY: row's shape matches the ten columns named in the SELECT above.
|
|
2148
2180
|
const row = db.prepare(`
|
|
@@ -2217,8 +2249,7 @@ export function loadFreshActiveTaskSnapshot(hippoRoot, tenantId, opts = {}) {
|
|
|
2217
2249
|
}
|
|
2218
2250
|
export function clearActiveTaskSnapshot(hippoRoot, tenantId, clearedStatus = 'cleared') {
|
|
2219
2251
|
assertTenantId('clearActiveTaskSnapshot', tenantId);
|
|
2220
|
-
|
|
2221
|
-
const db = openHippoDb(hippoRoot);
|
|
2252
|
+
const db = openStore(hippoRoot);
|
|
2222
2253
|
const now = new Date().toISOString();
|
|
2223
2254
|
try {
|
|
2224
2255
|
// SAFETY: active's shape matches the single `id` column selected above.
|
|
@@ -2246,8 +2277,7 @@ export function clearActiveTaskSnapshot(hippoRoot, tenantId, clearedStatus = 'cl
|
|
|
2246
2277
|
*/
|
|
2247
2278
|
export function closeTaskSnapshotsForSession(hippoRoot, tenantId, sessionId, status = 'session-ended') {
|
|
2248
2279
|
assertTenantId('closeTaskSnapshotsForSession', tenantId);
|
|
2249
|
-
|
|
2250
|
-
const db = openHippoDb(hippoRoot);
|
|
2280
|
+
const db = openStore(hippoRoot);
|
|
2251
2281
|
const now = new Date().toISOString();
|
|
2252
2282
|
try {
|
|
2253
2283
|
const result = db.prepare(`UPDATE task_snapshots SET status = ?, updated_at = ? WHERE status = 'active' AND tenant_id = ? AND session_id = ?`).run(status, now, tenantId, sessionId);
|
|
@@ -2259,8 +2289,7 @@ export function closeTaskSnapshotsForSession(hippoRoot, tenantId, sessionId, sta
|
|
|
2259
2289
|
}
|
|
2260
2290
|
export function appendSessionEvent(hippoRoot, tenantId, event) {
|
|
2261
2291
|
assertTenantId('appendSessionEvent', tenantId);
|
|
2262
|
-
|
|
2263
|
-
const db = openHippoDb(hippoRoot);
|
|
2292
|
+
const db = openStore(hippoRoot);
|
|
2264
2293
|
const now = new Date().toISOString();
|
|
2265
2294
|
// v1.2: scope is wired through. Default-deny in api.recall + cmdRecall
|
|
2266
2295
|
// continuity reads applies to slack:private:* and 'unknown:legacy' rows.
|
|
@@ -2300,8 +2329,7 @@ export function appendSessionEvent(hippoRoot, tenantId, event) {
|
|
|
2300
2329
|
}
|
|
2301
2330
|
export function listSessionEvents(hippoRoot, tenantId, options = {}) {
|
|
2302
2331
|
assertTenantId('listSessionEvents', tenantId);
|
|
2303
|
-
|
|
2304
|
-
const db = openHippoDb(hippoRoot);
|
|
2332
|
+
const db = openStore(hippoRoot);
|
|
2305
2333
|
try {
|
|
2306
2334
|
const clauses = ['tenant_id = ?'];
|
|
2307
2335
|
const params = [tenantId];
|
|
@@ -2337,8 +2365,7 @@ export function listSessionEvents(hippoRoot, tenantId, options = {}) {
|
|
|
2337
2365
|
*/
|
|
2338
2366
|
export function findPromotableSessions(hippoRoot, tenantId, sinceMs) {
|
|
2339
2367
|
assertTenantId('findPromotableSessions', tenantId);
|
|
2340
|
-
|
|
2341
|
-
const db = openHippoDb(hippoRoot);
|
|
2368
|
+
const db = openStore(hippoRoot);
|
|
2342
2369
|
try {
|
|
2343
2370
|
// SAFETY: rows' shape matches the single `session_id` column selected
|
|
2344
2371
|
// above.
|
|
@@ -2358,8 +2385,7 @@ export function findPromotableSessions(hippoRoot, tenantId, sinceMs) {
|
|
|
2358
2385
|
*/
|
|
2359
2386
|
export function traceExistsForSession(hippoRoot, tenantId, session_id) {
|
|
2360
2387
|
assertTenantId('traceExistsForSession', tenantId);
|
|
2361
|
-
|
|
2362
|
-
const db = openHippoDb(hippoRoot);
|
|
2388
|
+
const db = openStore(hippoRoot);
|
|
2363
2389
|
try {
|
|
2364
2390
|
const row = db.prepare(`
|
|
2365
2391
|
SELECT 1 FROM memories
|
|
@@ -2373,8 +2399,7 @@ export function traceExistsForSession(hippoRoot, tenantId, session_id) {
|
|
|
2373
2399
|
}
|
|
2374
2400
|
}
|
|
2375
2401
|
export function listMemoryConflicts(hippoRoot, status = 'open', tenantId) {
|
|
2376
|
-
|
|
2377
|
-
const db = openHippoDb(hippoRoot);
|
|
2402
|
+
const db = openStore(hippoRoot);
|
|
2378
2403
|
try {
|
|
2379
2404
|
// v0.28 — '*' is a sentinel meaning "no status filter, return all rows".
|
|
2380
2405
|
// Pre-v0.28 callers (cli/mcp/dashboard) always passed 'open' or default,
|
|
@@ -2432,8 +2457,7 @@ export function listMemoryConflicts(hippoRoot, status = 'open', tenantId) {
|
|
|
2432
2457
|
}
|
|
2433
2458
|
}
|
|
2434
2459
|
export function replaceDetectedConflicts(hippoRoot, detected, detectedAt = new Date().toISOString()) {
|
|
2435
|
-
|
|
2436
|
-
const db = openHippoDb(hippoRoot);
|
|
2460
|
+
const db = openStore(hippoRoot);
|
|
2437
2461
|
try {
|
|
2438
2462
|
db.exec('BEGIN');
|
|
2439
2463
|
// Tenant guard (E2): a conflict is meaningful only within one tenant.
|
|
@@ -2548,8 +2572,7 @@ export function replaceDetectedConflicts(hippoRoot, detected, detectedAt = new D
|
|
|
2548
2572
|
* Returns the resolved conflict, or null if not found.
|
|
2549
2573
|
*/
|
|
2550
2574
|
export function resolveConflict(hippoRoot, conflictId, keepId, forgetLoser = false, tenantId, opts) {
|
|
2551
|
-
|
|
2552
|
-
const db = openHippoDb(hippoRoot);
|
|
2575
|
+
const db = openStore(hippoRoot);
|
|
2553
2576
|
// When tenantId is set, the conflict lookup requires BOTH members in-tenant
|
|
2554
2577
|
// and every memories mutation carries AND tenant_id = ?. A cross-tenant probe
|
|
2555
2578
|
// then returns null, indistinguishable from a bad id. Omitted tenantId =
|
|
@@ -2772,8 +2795,7 @@ const HANDOFF_COLUMNS = 'id, session_id, repo_root, task_id, summary, next_actio
|
|
|
2772
2795
|
*/
|
|
2773
2796
|
export function saveSessionHandoff(hippoRoot, tenantId, handoff) {
|
|
2774
2797
|
assertTenantId('saveSessionHandoff', tenantId);
|
|
2775
|
-
|
|
2776
|
-
const db = openHippoDb(hippoRoot);
|
|
2798
|
+
const db = openStore(hippoRoot);
|
|
2777
2799
|
const now = new Date().toISOString();
|
|
2778
2800
|
// v1.2: scope is wired through. Read-side default-deny in api.recall +
|
|
2779
2801
|
// cmdRecall continuity excludes slack:private:* and 'unknown:legacy'.
|
|
@@ -2801,8 +2823,7 @@ export function saveSessionHandoff(hippoRoot, tenantId, handoff) {
|
|
|
2801
2823
|
/** Load the most recent handoff, optionally filtered by session ID. */
|
|
2802
2824
|
export function loadLatestHandoff(hippoRoot, tenantId, sessionId, opts = {}) {
|
|
2803
2825
|
assertTenantId('loadLatestHandoff', tenantId);
|
|
2804
|
-
|
|
2805
|
-
const db = openHippoDb(hippoRoot);
|
|
2826
|
+
const db = openStore(hippoRoot);
|
|
2806
2827
|
try {
|
|
2807
2828
|
const conditions = ['tenant_id = ?'];
|
|
2808
2829
|
const params = [tenantId];
|
|
@@ -2846,8 +2867,7 @@ export function loadLatestHandoff(hippoRoot, tenantId, sessionId, opts = {}) {
|
|
|
2846
2867
|
*/
|
|
2847
2868
|
export function loadHandoffById(hippoRoot, tenantId, id) {
|
|
2848
2869
|
assertTenantId('loadHandoffById', tenantId);
|
|
2849
|
-
|
|
2850
|
-
const db = openHippoDb(hippoRoot);
|
|
2870
|
+
const db = openStore(hippoRoot);
|
|
2851
2871
|
try {
|
|
2852
2872
|
// SAFETY: row's shape matches HANDOFF_COLUMNS.
|
|
2853
2873
|
const row = db.prepare(`
|
|
@@ -2864,8 +2884,7 @@ export function loadHandoffById(hippoRoot, tenantId, id) {
|
|
|
2864
2884
|
/** Stamp the outcome on a session's newest handoff, only if it has none yet. Returns rows changed. */
|
|
2865
2885
|
export function stampHandoffOutcome(hippoRoot, tenantId, sessionId, outcome) {
|
|
2866
2886
|
assertTenantId('stampHandoffOutcome', tenantId);
|
|
2867
|
-
|
|
2868
|
-
const db = openHippoDb(hippoRoot);
|
|
2887
|
+
const db = openStore(hippoRoot);
|
|
2869
2888
|
try {
|
|
2870
2889
|
const result = db.prepare(`
|
|
2871
2890
|
UPDATE session_handoffs SET outcome = ?
|
|
@@ -3033,8 +3052,7 @@ export function createCard(hippoRoot, tenantId, input) {
|
|
|
3033
3052
|
if (input.budget !== undefined && !(Number.isSafeInteger(input.budget) && input.budget > 0)) {
|
|
3034
3053
|
throw new Error(`Invalid budget: ${input.budget} (expected a positive integer)`);
|
|
3035
3054
|
}
|
|
3036
|
-
|
|
3037
|
-
const db = openHippoDb(hippoRoot);
|
|
3055
|
+
const db = openStore(hippoRoot);
|
|
3038
3056
|
try {
|
|
3039
3057
|
const dependsOn = [...new Set(input.dependsOn ?? [])];
|
|
3040
3058
|
let id = '';
|
|
@@ -3086,8 +3104,7 @@ export function createCard(hippoRoot, tenantId, input) {
|
|
|
3086
3104
|
/** Returns the card row for id, or null if it does not exist under this tenant. */
|
|
3087
3105
|
export function loadCard(hippoRoot, tenantId, id) {
|
|
3088
3106
|
assertTenantId('loadCard', tenantId);
|
|
3089
|
-
|
|
3090
|
-
const db = openHippoDb(hippoRoot);
|
|
3107
|
+
const db = openStore(hippoRoot);
|
|
3091
3108
|
try {
|
|
3092
3109
|
return loadCardRow(db, tenantId, id);
|
|
3093
3110
|
}
|
|
@@ -3098,8 +3115,7 @@ export function loadCard(hippoRoot, tenantId, id) {
|
|
|
3098
3115
|
/** Lists cards for this tenant, optionally filtered to one status, newest-updated first. */
|
|
3099
3116
|
export function listCards(hippoRoot, tenantId, opts = {}) {
|
|
3100
3117
|
assertTenantId('listCards', tenantId);
|
|
3101
|
-
|
|
3102
|
-
const db = openHippoDb(hippoRoot);
|
|
3118
|
+
const db = openStore(hippoRoot);
|
|
3103
3119
|
try {
|
|
3104
3120
|
const conditions = ['tenant_id = ?'];
|
|
3105
3121
|
const params = [tenantId];
|
|
@@ -3120,8 +3136,7 @@ export function listCards(hippoRoot, tenantId, opts = {}) {
|
|
|
3120
3136
|
/** Returns this card's parent and child ids from card_deps. */
|
|
3121
3137
|
export function loadCardDeps(hippoRoot, tenantId, id) {
|
|
3122
3138
|
assertTenantId('loadCardDeps', tenantId);
|
|
3123
|
-
|
|
3124
|
-
const db = openHippoDb(hippoRoot);
|
|
3139
|
+
const db = openStore(hippoRoot);
|
|
3125
3140
|
try {
|
|
3126
3141
|
// SAFETY: rows' shape matches the single `parent` column named in the SELECT below.
|
|
3127
3142
|
const parents = db.prepare(`SELECT parent FROM card_deps WHERE tenant_id = ? AND child = ?`).all(tenantId, id).map((r) => r.parent);
|
|
@@ -3136,8 +3151,7 @@ export function loadCardDeps(hippoRoot, tenantId, id) {
|
|
|
3136
3151
|
/** Returns this card's run history, most recent first. */
|
|
3137
3152
|
export function loadCardRuns(hippoRoot, tenantId, id) {
|
|
3138
3153
|
assertTenantId('loadCardRuns', tenantId);
|
|
3139
|
-
|
|
3140
|
-
const db = openHippoDb(hippoRoot);
|
|
3154
|
+
const db = openStore(hippoRoot);
|
|
3141
3155
|
try {
|
|
3142
3156
|
// SAFETY: rows' shape matches CardRunRow.
|
|
3143
3157
|
const rows = db.prepare(`
|
|
@@ -3153,8 +3167,7 @@ export function loadCardRuns(hippoRoot, tenantId, id) {
|
|
|
3153
3167
|
/** Returns this card's comments, most recent first. */
|
|
3154
3168
|
export function loadCardComments(hippoRoot, tenantId, id) {
|
|
3155
3169
|
assertTenantId('loadCardComments', tenantId);
|
|
3156
|
-
|
|
3157
|
-
const db = openHippoDb(hippoRoot);
|
|
3170
|
+
const db = openStore(hippoRoot);
|
|
3158
3171
|
try {
|
|
3159
3172
|
// SAFETY: rows' shape matches CardCommentRow.
|
|
3160
3173
|
const rows = db.prepare(`
|
|
@@ -3170,8 +3183,7 @@ export function loadCardComments(hippoRoot, tenantId, id) {
|
|
|
3170
3183
|
/** Read side of the card <-> handoff round trip: the newest handoff filed against this card. */
|
|
3171
3184
|
export function loadLatestHandoffForCard(hippoRoot, tenantId, cardId) {
|
|
3172
3185
|
assertTenantId('loadLatestHandoffForCard', tenantId);
|
|
3173
|
-
|
|
3174
|
-
const db = openHippoDb(hippoRoot);
|
|
3186
|
+
const db = openStore(hippoRoot);
|
|
3175
3187
|
try {
|
|
3176
3188
|
// SAFETY: row's shape matches HANDOFF_COLUMNS.
|
|
3177
3189
|
const row = db.prepare(`
|
|
@@ -3190,8 +3202,7 @@ export function claimCard(hippoRoot, tenantId, id, runtime, sessionId) {
|
|
|
3190
3202
|
if (runtime.trim() === '') {
|
|
3191
3203
|
throw new Error('runtime must not be empty');
|
|
3192
3204
|
}
|
|
3193
|
-
|
|
3194
|
-
const db = openHippoDb(hippoRoot);
|
|
3205
|
+
const db = openStore(hippoRoot);
|
|
3195
3206
|
try {
|
|
3196
3207
|
db.exec('BEGIN IMMEDIATE');
|
|
3197
3208
|
let runId = 0;
|
|
@@ -3233,8 +3244,7 @@ export function claimCard(hippoRoot, tenantId, id, runtime, sessionId) {
|
|
|
3233
3244
|
export function heartbeatCard(hippoRoot, tenantId, id, runId) {
|
|
3234
3245
|
assertTenantId('heartbeatCard', tenantId);
|
|
3235
3246
|
assertRunId(runId);
|
|
3236
|
-
|
|
3237
|
-
const db = openHippoDb(hippoRoot);
|
|
3247
|
+
const db = openStore(hippoRoot);
|
|
3238
3248
|
try {
|
|
3239
3249
|
db.exec('BEGIN IMMEDIATE');
|
|
3240
3250
|
try {
|
|
@@ -3272,8 +3282,7 @@ export function blockCard(hippoRoot, tenantId, id, reason, runId) {
|
|
|
3272
3282
|
}
|
|
3273
3283
|
if (runId !== undefined)
|
|
3274
3284
|
assertRunId(runId);
|
|
3275
|
-
|
|
3276
|
-
const db = openHippoDb(hippoRoot);
|
|
3285
|
+
const db = openStore(hippoRoot);
|
|
3277
3286
|
try {
|
|
3278
3287
|
db.exec('BEGIN IMMEDIATE');
|
|
3279
3288
|
try {
|
|
@@ -3310,8 +3319,7 @@ export function reviewCard(hippoRoot, tenantId, id, runId) {
|
|
|
3310
3319
|
assertTenantId('reviewCard', tenantId);
|
|
3311
3320
|
if (runId !== undefined)
|
|
3312
3321
|
assertRunId(runId);
|
|
3313
|
-
|
|
3314
|
-
const db = openHippoDb(hippoRoot);
|
|
3322
|
+
const db = openStore(hippoRoot);
|
|
3315
3323
|
try {
|
|
3316
3324
|
db.exec('BEGIN IMMEDIATE');
|
|
3317
3325
|
try {
|
|
@@ -3347,8 +3355,7 @@ export function completeCard(hippoRoot, tenantId, id, outcome, runId) {
|
|
|
3347
3355
|
}
|
|
3348
3356
|
if (runId !== undefined)
|
|
3349
3357
|
assertRunId(runId);
|
|
3350
|
-
|
|
3351
|
-
const db = openHippoDb(hippoRoot);
|
|
3358
|
+
const db = openStore(hippoRoot);
|
|
3352
3359
|
try {
|
|
3353
3360
|
db.exec('BEGIN IMMEDIATE');
|
|
3354
3361
|
let promotedChildren = [];
|
|
@@ -3404,8 +3411,7 @@ export function completeCard(hippoRoot, tenantId, id, outcome, runId) {
|
|
|
3404
3411
|
/** Returns to ready every running card of the tenant whose lease has expired or is missing: clears its assignee, closes its live run as 'reclaimed' and leaves its handoffs alone, all in one write transaction. Returns the reclaimed card ids in id order. */
|
|
3405
3412
|
export function reclaimExpiredCards(hippoRoot, tenantId) {
|
|
3406
3413
|
assertTenantId('reclaimExpiredCards', tenantId);
|
|
3407
|
-
|
|
3408
|
-
const db = openHippoDb(hippoRoot);
|
|
3414
|
+
const db = openStore(hippoRoot);
|
|
3409
3415
|
try {
|
|
3410
3416
|
db.exec('BEGIN IMMEDIATE');
|
|
3411
3417
|
try {
|
|
@@ -3442,8 +3448,7 @@ export function addCardComment(hippoRoot, tenantId, cardId, author, body) {
|
|
|
3442
3448
|
if (body.trim() === '') {
|
|
3443
3449
|
throw new Error('body must not be empty');
|
|
3444
3450
|
}
|
|
3445
|
-
|
|
3446
|
-
const db = openHippoDb(hippoRoot);
|
|
3451
|
+
const db = openStore(hippoRoot);
|
|
3447
3452
|
try {
|
|
3448
3453
|
return insertCardComment(db, tenantId, cardId, author, body);
|
|
3449
3454
|
}
|
|
@@ -3470,8 +3475,7 @@ export function addCardComment(hippoRoot, tenantId, cardId, author, body) {
|
|
|
3470
3475
|
*/
|
|
3471
3476
|
export function loadDirtySummaries(hippoRoot, tenantId) {
|
|
3472
3477
|
assertTenantId('loadDirtySummaries', tenantId);
|
|
3473
|
-
|
|
3474
|
-
const db = openHippoDb(hippoRoot);
|
|
3478
|
+
const db = openStore(hippoRoot);
|
|
3475
3479
|
try {
|
|
3476
3480
|
// SAFETY: this query selects exactly MEMORY_SELECT_COLUMNS, matching
|
|
3477
3481
|
// MemoryRow's field set.
|
|
@@ -3540,8 +3544,7 @@ export function markSummaryDirtyInTx(db, summaryId, tenantId, actor) {
|
|
|
3540
3544
|
*/
|
|
3541
3545
|
export function markSummaryDirty(hippoRoot, summaryId, tenantId, actor = 'cli') {
|
|
3542
3546
|
assertTenantId('markSummaryDirty', tenantId);
|
|
3543
|
-
|
|
3544
|
-
const db = openHippoDb(hippoRoot);
|
|
3547
|
+
const db = openStore(hippoRoot);
|
|
3545
3548
|
try {
|
|
3546
3549
|
// v0.30 / E5: widened dag_level=2 -> IN (2, 3). RETURNING dag_level reads
|
|
3547
3550
|
// actual level in same round trip.
|
|
@@ -3588,8 +3591,7 @@ export function markSummaryDirty(hippoRoot, summaryId, tenantId, actor = 'cli')
|
|
|
3588
3591
|
* tenant-scoped via summary.tenantId.
|
|
3589
3592
|
*/
|
|
3590
3593
|
export function loadAllL2Summaries(hippoRoot) {
|
|
3591
|
-
|
|
3592
|
-
const db = openHippoDb(hippoRoot);
|
|
3594
|
+
const db = openStore(hippoRoot);
|
|
3593
3595
|
try {
|
|
3594
3596
|
// SAFETY: this query selects exactly MEMORY_SELECT_COLUMNS, matching
|
|
3595
3597
|
// MemoryRow's field set.
|
|
@@ -3618,8 +3620,7 @@ export function loadAllL2Summaries(hippoRoot) {
|
|
|
3618
3620
|
* HIPPO_DAG_REBUILD_CAP takes most-recently-changed summaries first.
|
|
3619
3621
|
*/
|
|
3620
3622
|
export function loadAllDirtySummaries(hippoRoot) {
|
|
3621
|
-
|
|
3622
|
-
const db = openHippoDb(hippoRoot);
|
|
3623
|
+
const db = openStore(hippoRoot);
|
|
3623
3624
|
try {
|
|
3624
3625
|
// SAFETY: this query selects exactly MEMORY_SELECT_COLUMNS, matching
|
|
3625
3626
|
// MemoryRow's field set.
|
|
@@ -3645,8 +3646,7 @@ export function loadAllDirtySummaries(hippoRoot) {
|
|
|
3645
3646
|
*/
|
|
3646
3647
|
export function loadChildrenOfSummary(hippoRoot, summaryId, tenantId) {
|
|
3647
3648
|
assertTenantId('loadChildrenOfSummary', tenantId);
|
|
3648
|
-
|
|
3649
|
-
const db = openHippoDb(hippoRoot);
|
|
3649
|
+
const db = openStore(hippoRoot);
|
|
3650
3650
|
try {
|
|
3651
3651
|
// SAFETY: this query selects exactly MEMORY_SELECT_COLUMNS, matching
|
|
3652
3652
|
// MemoryRow's field set.
|
|
@@ -3679,8 +3679,7 @@ export function loadChildrenOfSummary(hippoRoot, summaryId, tenantId) {
|
|
|
3679
3679
|
*/
|
|
3680
3680
|
export function applyRebuildResult(hippoRoot, summary, patch) {
|
|
3681
3681
|
assertTenantId('applyRebuildResult', summary.tenantId);
|
|
3682
|
-
|
|
3683
|
-
const db = openHippoDb(hippoRoot);
|
|
3682
|
+
const db = openStore(hippoRoot);
|
|
3684
3683
|
try {
|
|
3685
3684
|
db.exec('SAVEPOINT rebuild_summary');
|
|
3686
3685
|
try {
|
|
@@ -3827,8 +3826,7 @@ export function applyRebuildResult(hippoRoot, summary, patch) {
|
|
|
3827
3826
|
*/
|
|
3828
3827
|
export function clearSummaryDirtyAfterBuild(hippoRoot, summaryId, tenantId, actor = 'cli', source = 'buildDag-clean') {
|
|
3829
3828
|
assertTenantId('clearSummaryDirtyAfterBuild', tenantId);
|
|
3830
|
-
|
|
3831
|
-
const db = openHippoDb(hippoRoot);
|
|
3829
|
+
const db = openStore(hippoRoot);
|
|
3832
3830
|
try {
|
|
3833
3831
|
// v0.30 / E5: widened dag_level=2 -> IN (2, 3). RETURNING dag_level reads
|
|
3834
3832
|
// actual level so audit metadata stays accurate without an extra SELECT.
|