hippo-memory 1.61.0 → 1.63.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.
Files changed (377) hide show
  1. package/README.md +37 -53
  2. package/dist/agent-memories/apply.d.ts +3 -1
  3. package/dist/agent-memories/apply.js +23 -1
  4. package/dist/agent-memories/claude-code.d.ts +4 -1
  5. package/dist/agent-memories/claude-code.js +53 -9
  6. package/dist/agent-memories/report.d.ts +1 -0
  7. package/dist/agent-memories/report.js +2 -2
  8. package/dist/agent-memories/sync.d.ts +3 -3
  9. package/dist/agent-memories/sync.js +46 -24
  10. package/dist/agent-memories/types.d.ts +0 -2
  11. package/dist/ambient-store.d.ts +4 -4
  12. package/dist/ambient-store.js +4 -3
  13. package/dist/api/assemble.d.ts +7 -10
  14. package/dist/api/assemble.js +62 -66
  15. package/dist/api/audit.d.ts +2 -2
  16. package/dist/api/audit.js +2 -2
  17. package/dist/api/auth.d.ts +6 -6
  18. package/dist/api/auth.js +5 -7
  19. package/dist/api/context-select.d.ts +50 -0
  20. package/dist/api/context-select.js +342 -0
  21. package/dist/api/context-types.d.ts +12 -17
  22. package/dist/api/context.d.ts +5 -4
  23. package/dist/api/context.js +208 -535
  24. package/dist/api/dormant.js +1 -1
  25. package/dist/api/drill-down.d.ts +4 -4
  26. package/dist/api/drill-down.js +56 -41
  27. package/dist/api/outcome.d.ts +8 -13
  28. package/dist/api/outcome.js +13 -17
  29. package/dist/api/promote.d.ts +5 -8
  30. package/dist/api/promote.js +55 -66
  31. package/dist/api/quarantine.js +3 -2
  32. package/dist/api/recall-types.d.ts +43 -55
  33. package/dist/api/recall.d.ts +3 -3
  34. package/dist/api/recall.js +310 -456
  35. package/dist/api/remember.d.ts +2 -2
  36. package/dist/api/sleep.d.ts +7 -35
  37. package/dist/api/sleep.js +206 -220
  38. package/dist/api/tokens.d.ts +2 -2
  39. package/dist/api/tokens.js +2 -2
  40. package/dist/api/types.d.ts +6 -10
  41. package/dist/api/types.js +2 -4
  42. package/dist/audit-prune.d.ts +4 -6
  43. package/dist/audit-prune.js +3 -5
  44. package/dist/audit.js +11 -33
  45. package/dist/auth.d.ts +8 -9
  46. package/dist/auth.js +5 -7
  47. package/dist/autolearn.d.ts +1 -1
  48. package/dist/autolearn.js +1 -1
  49. package/dist/availability.js +3 -5
  50. package/dist/capture/command.d.ts +5 -4
  51. package/dist/capture/command.js +8 -22
  52. package/dist/capture/compact.d.ts +2 -2
  53. package/dist/capture/compact.js +11 -15
  54. package/dist/capture/extract.js +37 -115
  55. package/dist/capture-error.d.ts +1 -1
  56. package/dist/capture-error.js +1 -1
  57. package/dist/churn-git.d.ts +1 -1
  58. package/dist/churn-git.js +1 -1
  59. package/dist/cli/audit.js +3 -4
  60. package/dist/cli/auth.js +3 -6
  61. package/dist/cli/briefs.js +324 -306
  62. package/dist/cli/context.js +44 -34
  63. package/dist/cli/continuity.js +283 -271
  64. package/dist/cli/curate.d.ts +1 -1
  65. package/dist/cli/curate.js +43 -58
  66. package/dist/cli/dag.js +5 -9
  67. package/dist/cli/decisions.js +334 -345
  68. package/dist/cli/explain.js +68 -61
  69. package/dist/cli/goals.js +1 -1
  70. package/dist/cli/init.js +1 -1
  71. package/dist/cli/maintenance.js +62 -51
  72. package/dist/cli/playbooks.js +391 -379
  73. package/dist/cli/projects.js +11 -6
  74. package/dist/cli/recall.js +30 -44
  75. package/dist/cli/remember.js +118 -87
  76. package/dist/cli/session-hooks.js +106 -115
  77. package/dist/cli/setup.d.ts +1 -1
  78. package/dist/cli/setup.js +267 -250
  79. package/dist/cli/shared.js +3 -3
  80. package/dist/cli/slack.js +1 -1
  81. package/dist/cli/sleep.js +27 -1
  82. package/dist/cli/status.d.ts +4 -4
  83. package/dist/cli/status.js +80 -76
  84. package/dist/cli/transfer.js +88 -107
  85. package/dist/cli/usage.js +9 -6
  86. package/dist/cli.d.ts +1 -1
  87. package/dist/cli.js +4 -9
  88. package/dist/compaction-record.d.ts +2 -2
  89. package/dist/compaction-record.js +89 -68
  90. package/dist/compare.d.ts +11 -16
  91. package/dist/compare.js +11 -16
  92. package/dist/config.d.ts +19 -18
  93. package/dist/config.js +90 -67
  94. package/dist/connectors/github/backfill.d.ts +2 -2
  95. package/dist/connectors/github/backfill.js +8 -15
  96. package/dist/connectors/github/cli-impl.js +3 -8
  97. package/dist/connectors/github/deletion.d.ts +5 -12
  98. package/dist/connectors/github/deletion.js +5 -12
  99. package/dist/connectors/github/dlq.d.ts +6 -9
  100. package/dist/connectors/github/dlq.js +2 -3
  101. package/dist/connectors/github/ingest.d.ts +5 -7
  102. package/dist/connectors/github/ingest.js +8 -12
  103. package/dist/connectors/github/octokit-client.d.ts +3 -5
  104. package/dist/connectors/github/octokit-client.js +5 -6
  105. package/dist/connectors/github/signature.d.ts +9 -39
  106. package/dist/connectors/github/signature.js +9 -39
  107. package/dist/connectors/github/tenant-routing.d.ts +1 -1
  108. package/dist/connectors/github/tenant-routing.js +1 -1
  109. package/dist/connectors/github/transform.js +2 -2
  110. package/dist/connectors/github/types.d.ts +2 -10
  111. package/dist/connectors/github/types.js +1 -3
  112. package/dist/connectors/slack/deletion.d.ts +3 -8
  113. package/dist/connectors/slack/deletion.js +3 -8
  114. package/dist/connectors/slack/dlq.d.ts +1 -1
  115. package/dist/connectors/slack/ingest.d.ts +1 -1
  116. package/dist/connectors/slack/ingest.js +7 -16
  117. package/dist/connectors/slack/signature.d.ts +1 -1
  118. package/dist/connectors/slack/tenant-routing.d.ts +3 -5
  119. package/dist/connectors/slack/tenant-routing.js +3 -5
  120. package/dist/connectors/slack/transform.d.ts +5 -6
  121. package/dist/connectors/slack/transform.js +5 -6
  122. package/dist/connectors/slack/types.d.ts +2 -6
  123. package/dist/connectors/slack/types.js +1 -3
  124. package/dist/connectors/slack/web-client.js +10 -3
  125. package/dist/connectors/slack/workspaces.d.ts +3 -5
  126. package/dist/connectors/slack/workspaces.js +3 -5
  127. package/dist/consolidate/conflicts.js +3 -14
  128. package/dist/consolidate/decay.js +9 -29
  129. package/dist/consolidate/llm-passes.js +4 -5
  130. package/dist/consolidate/merge.js +8 -23
  131. package/dist/consolidate/run.d.ts +1 -8
  132. package/dist/consolidate/run.js +3 -25
  133. package/dist/consolidate/sleep.js +5 -17
  134. package/dist/consolidate/traces.js +9 -21
  135. package/dist/customer-notes.d.ts +5 -7
  136. package/dist/customer-notes.js +82 -76
  137. package/dist/dag.d.ts +10 -21
  138. package/dist/dag.js +189 -203
  139. package/dist/db/continuity.js +2 -2
  140. package/dist/db/migrations/v14.js +1 -1
  141. package/dist/db/migrations/v15.js +1 -2
  142. package/dist/db/migrations/v16.js +3 -4
  143. package/dist/db/migrations/v17.js +2 -3
  144. package/dist/db/migrations/v19.js +1 -1
  145. package/dist/db/migrations/v20.js +1 -1
  146. package/dist/db/migrations/v21.js +2 -6
  147. package/dist/db/migrations/v22.js +2 -4
  148. package/dist/db/migrations/v23.js +1 -1
  149. package/dist/db/migrations/v24.js +4 -6
  150. package/dist/db/migrations/v25.js +2 -3
  151. package/dist/db/migrations/v26.js +3 -3
  152. package/dist/db/migrations/v27.js +2 -10
  153. package/dist/db/migrations/v28.js +5 -8
  154. package/dist/db/migrations/v29.js +3 -4
  155. package/dist/db/migrations/v30.js +2 -2
  156. package/dist/db/migrations/v31.js +1 -1
  157. package/dist/db/migrations/v32.js +1 -1
  158. package/dist/db/migrations/v33.js +3 -3
  159. package/dist/db/migrations/v34.js +1 -1
  160. package/dist/db/migrations/v35.js +3 -4
  161. package/dist/db/migrations/v36.js +3 -4
  162. package/dist/db/migrations/v37.js +5 -5
  163. package/dist/db/migrations/v38.js +7 -8
  164. package/dist/db/migrations/v39.js +1 -1
  165. package/dist/db/migrations/v40.js +4 -16
  166. package/dist/db/migrations/v41.js +3 -4
  167. package/dist/db/migrations/v42.js +3 -4
  168. package/dist/db/migrations/v45.js +1 -1
  169. package/dist/db/migrations/v46.js +1 -1
  170. package/dist/db/migrations/v47.js +1 -1
  171. package/dist/db/migrations/v48.js +1 -1
  172. package/dist/decisions.d.ts +2 -2
  173. package/dist/decisions.js +97 -80
  174. package/dist/dedupe.js +86 -61
  175. package/dist/delivery-recorder.js +154 -135
  176. package/dist/doctor.js +129 -110
  177. package/dist/dormant.js +1 -4
  178. package/dist/embedding-provider.d.ts +4 -8
  179. package/dist/embedding-provider.js +4 -8
  180. package/dist/embeddings.js +55 -47
  181. package/dist/env.d.ts +1 -1
  182. package/dist/env.js +12 -12
  183. package/dist/escape.d.ts +5 -0
  184. package/dist/escape.js +10 -0
  185. package/dist/eval-stats.d.ts +1 -2
  186. package/dist/eval-stats.js +1 -2
  187. package/dist/eval-suite.js +27 -21
  188. package/dist/extract.js +4 -9
  189. package/dist/failure-log.d.ts +3 -3
  190. package/dist/failure-log.js +1 -1
  191. package/dist/forward-claim-detector.d.ts +2 -4
  192. package/dist/forward-claim-detector.js +6 -11
  193. package/dist/goals.d.ts +3 -3
  194. package/dist/goals.js +103 -91
  195. package/dist/graph/read.d.ts +2 -2
  196. package/dist/graph/read.js +5 -6
  197. package/dist/graph/types.d.ts +8 -8
  198. package/dist/graph/write.d.ts +7 -14
  199. package/dist/graph/write.js +16 -23
  200. package/dist/graph-extract.d.ts +7 -8
  201. package/dist/graph-extract.js +62 -72
  202. package/dist/graph-recall.d.ts +2 -2
  203. package/dist/graph-recall.js +55 -49
  204. package/dist/graph-stream.d.ts +5 -6
  205. package/dist/graph-stream.js +66 -57
  206. package/dist/graph-view.d.ts +2 -2
  207. package/dist/graph-view.js +7 -7
  208. package/dist/half-life-migration.d.ts +1 -2
  209. package/dist/half-life-migration.js +2 -3
  210. package/dist/hooks/codex-session.js +1 -1
  211. package/dist/hooks/codex-wrapper.d.ts +1 -1
  212. package/dist/hooks/codex-wrapper.js +3 -2
  213. package/dist/hooks/json-hooks.d.ts +2 -2
  214. package/dist/hooks/json-hooks.js +5 -4
  215. package/dist/hooks/opencode.d.ts +1 -1
  216. package/dist/hooks/opencode.js +5 -4
  217. package/dist/hooks/shared.d.ts +3 -7
  218. package/dist/hooks/shared.js +1 -8
  219. package/dist/http-util.d.ts +2 -3
  220. package/dist/http-util.js +3 -0
  221. package/dist/importers/core.d.ts +2 -9
  222. package/dist/importers/core.js +15 -30
  223. package/dist/importers/sources.js +2 -1
  224. package/dist/importers/vault.js +2 -20
  225. package/dist/incidents.d.ts +1 -1
  226. package/dist/incidents.js +46 -39
  227. package/dist/instruction-detect.d.ts +1 -1
  228. package/dist/instruction-detect.js +1 -1
  229. package/dist/invalidation.d.ts +3 -0
  230. package/dist/invalidation.js +160 -114
  231. package/dist/json.d.ts +5 -0
  232. package/dist/json.js +4 -0
  233. package/dist/judgment.js +1 -2
  234. package/dist/local-embedding.js +1 -1
  235. package/dist/mcp/admin-tools.js +7 -17
  236. package/dist/mcp/format.js +1 -1
  237. package/dist/mcp/framing.js +3 -6
  238. package/dist/mcp/protocol.d.ts +2 -5
  239. package/dist/mcp/protocol.js +1 -3
  240. package/dist/mcp/recall-tools.js +12 -15
  241. package/dist/mcp/request.js +4 -3
  242. package/dist/mcp/session-state.js +2 -3
  243. package/dist/mcp/stdio.js +2 -1
  244. package/dist/mcp/tools.js +9 -6
  245. package/dist/memory-value-weights.d.ts +5 -8
  246. package/dist/memory-value-weights.js +5 -8
  247. package/dist/memory-value.d.ts +13 -13
  248. package/dist/memory-value.js +26 -37
  249. package/dist/memory.d.ts +20 -22
  250. package/dist/memory.js +24 -48
  251. package/dist/multihop.d.ts +1 -1
  252. package/dist/multihop.js +3 -2
  253. package/dist/owner-validation.d.ts +4 -5
  254. package/dist/owner-validation.js +4 -5
  255. package/dist/physics.d.ts +4 -4
  256. package/dist/physics.js +7 -9
  257. package/dist/policies.d.ts +9 -10
  258. package/dist/policies.js +96 -81
  259. package/dist/postinstall.js +3 -6
  260. package/dist/predictions/planning-fallacy.d.ts +9 -14
  261. package/dist/predictions/planning-fallacy.js +10 -16
  262. package/dist/predictions/store.d.ts +15 -23
  263. package/dist/predictions/store.js +36 -33
  264. package/dist/processes.d.ts +2 -7
  265. package/dist/processes.js +88 -72
  266. package/dist/project-briefs.d.ts +2 -3
  267. package/dist/project-briefs.js +141 -118
  268. package/dist/project-identity.d.ts +22 -9
  269. package/dist/project-identity.js +47 -12
  270. package/dist/project-merge.d.ts +28 -5
  271. package/dist/project-merge.js +213 -46
  272. package/dist/project-remote.d.ts +12 -0
  273. package/dist/project-remote.js +138 -0
  274. package/dist/prompt-recall.js +1 -2
  275. package/dist/rate-limit.d.ts +1 -1
  276. package/dist/rate-limit.js +1 -1
  277. package/dist/raw-archive.d.ts +9 -0
  278. package/dist/raw-archive.js +70 -53
  279. package/dist/recall-history.d.ts +19 -20
  280. package/dist/recall-history.js +24 -42
  281. package/dist/recall-pipeline.js +4 -28
  282. package/dist/recall-scope.d.ts +7 -8
  283. package/dist/recall-scope.js +7 -8
  284. package/dist/recall-trace.d.ts +5 -9
  285. package/dist/recall-trace.js +6 -10
  286. package/dist/refine-llm.d.ts +1 -1
  287. package/dist/refine-llm.js +2 -2
  288. package/dist/reject-flow.d.ts +3 -4
  289. package/dist/reject-flow.js +122 -117
  290. package/dist/rejection.d.ts +5 -6
  291. package/dist/rejection.js +7 -15
  292. package/dist/rerankers/clef.d.ts +1 -1
  293. package/dist/rerankers/jev.d.ts +1 -2
  294. package/dist/rerankers/jev.js +4 -5
  295. package/dist/rerankers/llm.d.ts +1 -2
  296. package/dist/rerankers/llm.js +1 -2
  297. package/dist/rerankers/types.d.ts +1 -2
  298. package/dist/rrf.d.ts +2 -2
  299. package/dist/rrf.js +2 -2
  300. package/dist/search/bm25-search.d.ts +1 -1
  301. package/dist/search/bm25-search.js +2 -1
  302. package/dist/search/boosts.js +2 -1
  303. package/dist/search/hybrid.d.ts +1 -1
  304. package/dist/search/hybrid.js +2 -1
  305. package/dist/search/physics-search.d.ts +1 -1
  306. package/dist/search/physics-search.js +2 -1
  307. package/dist/search/types.d.ts +2 -0
  308. package/dist/search/types.js +3 -1
  309. package/dist/secret-detect.d.ts +4 -5
  310. package/dist/secret-detect.js +6 -10
  311. package/dist/server/auth.js +5 -5
  312. package/dist/server/client-ip.js +1 -1
  313. package/dist/server/cursor.js +2 -1
  314. package/dist/server/mcp-http.js +4 -4
  315. package/dist/server/request.d.ts +3 -6
  316. package/dist/server/request.js +6 -7
  317. package/dist/server/routes/admin.js +5 -4
  318. package/dist/server/routes/customer-notes.js +6 -5
  319. package/dist/server/routes/decisions.js +4 -3
  320. package/dist/server/routes/incidents.js +7 -5
  321. package/dist/server/routes/memories.js +7 -7
  322. package/dist/server/routes/policies.js +3 -2
  323. package/dist/server/routes/predictions.js +12 -15
  324. package/dist/server/routes/processes.js +3 -2
  325. package/dist/server/routes/project-briefs.js +8 -7
  326. package/dist/server/routes/recall.js +95 -93
  327. package/dist/server/routes/skills.js +6 -5
  328. package/dist/server/types.d.ts +1 -1
  329. package/dist/server/validation.d.ts +1 -2
  330. package/dist/server/validation.js +7 -14
  331. package/dist/server-detect.js +72 -58
  332. package/dist/server.d.ts +2 -2
  333. package/dist/server.js +131 -117
  334. package/dist/shared.d.ts +26 -17
  335. package/dist/shared.js +102 -104
  336. package/dist/skills.d.ts +3 -3
  337. package/dist/skills.js +88 -72
  338. package/dist/store/audit-event.d.ts +2 -2
  339. package/dist/store/audit-event.js +1 -1
  340. package/dist/store/candidates.d.ts +2 -2
  341. package/dist/store/candidates.js +4 -3
  342. package/dist/store/conflicts.js +30 -22
  343. package/dist/store/delete-and-batch.d.ts +11 -14
  344. package/dist/store/delete-and-batch.js +40 -91
  345. package/dist/store/entry-reads.d.ts +17 -25
  346. package/dist/store/entry-reads.js +59 -40
  347. package/dist/store/entry-row.d.ts +6 -24
  348. package/dist/store/entry-row.js +6 -24
  349. package/dist/store/entry-writes.d.ts +6 -7
  350. package/dist/store/entry-writes.js +13 -11
  351. package/dist/store/handoffs.d.ts +1 -1
  352. package/dist/store/handoffs.js +7 -10
  353. package/dist/store/index-and-stats.d.ts +2 -6
  354. package/dist/store/index-and-stats.js +4 -10
  355. package/dist/store/mirrors.d.ts +6 -19
  356. package/dist/store/mirrors.js +14 -39
  357. package/dist/store/open.js +9 -31
  358. package/dist/store/rows.d.ts +5 -11
  359. package/dist/store/rows.js +6 -11
  360. package/dist/store/search-rows.d.ts +17 -34
  361. package/dist/store/search-rows.js +34 -56
  362. package/dist/store/sessions.d.ts +4 -5
  363. package/dist/store/sessions.js +5 -6
  364. package/dist/store/summaries.d.ts +13 -17
  365. package/dist/store/summaries.js +26 -70
  366. package/dist/support-bundle.js +4 -8
  367. package/dist/tenant.d.ts +1 -5
  368. package/dist/token-ledger.d.ts +1 -1
  369. package/dist/token-ledger.js +3 -5
  370. package/dist/trace.js +1 -3
  371. package/dist/version.d.ts +1 -1
  372. package/dist/version.js +1 -1
  373. package/dist/working-memory.d.ts +1 -1
  374. package/extensions/openclaw-plugin/openclaw.plugin.json +1 -1
  375. package/extensions/openclaw-plugin/package.json +1 -1
  376. package/openclaw.plugin.json +1 -1
  377. package/package.json +1 -1
@@ -7,17 +7,12 @@ import { audit } from './audit-event.js';
7
7
  import { syncFtsRow } from './entry-row.js';
8
8
  import { openStore } from './open.js';
9
9
  // ---------------------------------------------------------------------------
10
- // v0.30 / E1 of DAG live-coupling — dirty-flag helpers for the existing
11
- // DAG layer's level-2 summaries.
12
- //
13
- // Used by E2 (child-write propagation in invalidation.ts / writeEntry /
14
- // forgetMemory / archiveRawMemory) to mark a summary dirty when one of its
15
- // children changes, and by E3's sleep-cycle rebuildDirtySummaries phase to
16
- // enumerate candidates without scanning every memory row.
10
+ // Dirty-flag helpers for DAG summaries: child writes mark a summary dirty, and the
11
+ // sleep-cycle rebuild enumerates candidates without scanning every memory row.
17
12
  // ---------------------------------------------------------------------------
18
13
  /**
19
14
  * Load summaries flagged dirty for the given tenant. Sorted by latest_at
20
- * DESC (NULLS LAST) so E3's rebuild cap (HIPPO_DAG_REBUILD_CAP, default 20)
15
+ * DESC (NULLS LAST) so the rebuild cap (HIPPO_DAG_REBUILD_CAP, default 20)
21
16
  * takes the most-recently-changed summaries first.
22
17
  *
23
18
  * Returns full MemoryEntry shape via MEMORY_SELECT_COLUMNS + rowToEntry
@@ -46,13 +41,12 @@ export function loadDirtySummaries(hippoRoot, tenantId) {
46
41
  /**
47
42
  * Mark a summary as dirty. Idempotent (re-marking dirty is a no-op + no
48
43
  * second audit row). Tenant-scoped to prevent cross-tenant writes via
49
- * parent-lookup. Called by E2 from invalidation.ts / writeEntry /
44
+ * parent-lookup. Called from invalidation.ts / writeEntry /
50
45
  * forgetMemory / archiveRawMemory whenever a child is invalidated,
51
46
  * superseded, forgotten, or archived.
52
47
  *
53
- * Quietly no-ops if the target row doesn't exist or isn't a level-2
54
- * summary (E5 will widen the dag_level guard to IN (2, 3) when level-3
55
- * build path lands). Emits a 'summary_marked_dirty' audit row on actual
48
+ * Quietly no-ops if the target row doesn't exist or isn't a level-2/3
49
+ * summary. Emits a 'summary_marked_dirty' audit row on actual
56
50
  * state transitions (0 -> 1) via the audit() helper, which try/catches
57
51
  * for missing audit_log (the v27 self-heal scenario).
58
52
  */
@@ -60,8 +54,7 @@ export function markSummaryDirty(hippoRoot, summaryId, tenantId, actor = 'cli')
60
54
  assertTenantId('markSummaryDirty', tenantId);
61
55
  const db = openStore(hippoRoot);
62
56
  try {
63
- // v0.30 / E5: widened dag_level=2 -> IN (2, 3). RETURNING dag_level reads
64
- // actual level in same round trip.
57
+ // RETURNING dag_level reads the actual level in the same round trip.
65
58
  // SAFETY: result's shape matches the single `dag_level` column returned
66
59
  // above.
67
60
  const result = db.prepare(`
@@ -76,8 +69,7 @@ export function markSummaryDirty(hippoRoot, summaryId, tenantId, actor = 'cli')
76
69
  `).get(summaryId, tenantId);
77
70
  if (result) {
78
71
  // audit() wraps appendAuditEvent in try/catch (v27 heal scenario).
79
- // metadata.source=E1 leaves a breadcrumb so E2-E5 debugging can
80
- // distinguish dirty-marks across the arc's wiring layers.
72
+ // metadata.source tells this dirty-mark apart from the other wiring layers' marks.
81
73
  audit(db, 'summary_marked_dirty', summaryId, { dag_level: result.dag_level, source: 'E1' }, actor, tenantId);
82
74
  }
83
75
  }
@@ -86,7 +78,7 @@ export function markSummaryDirty(hippoRoot, summaryId, tenantId, actor = 'cli')
86
78
  }
87
79
  }
88
80
  // ---------------------------------------------------------------------------
89
- // v0.30 / E3 of DAG live-coupling — sleep-cycle rebuild surface.
81
+ // Sleep-cycle rebuild surface.
90
82
  //
91
83
  // loadAllDirtySummaries / loadChildrenOfSummary / applyRebuildResult /
92
84
  // clearSummaryDirtyAfterBuild live HERE (not in dag.ts) because they need
@@ -95,9 +87,9 @@ export function markSummaryDirty(hippoRoot, summaryId, tenantId, actor = 'cli')
95
87
  // rebuildDirtySummaries() that calls into these.
96
88
  // ---------------------------------------------------------------------------
97
89
  /**
98
- * v0.30 / E5 — host-wide loader for L2 topic summaries without an L3 parent.
90
+ * Host-wide loader for L2 topic summaries without an L3 parent.
99
91
  * Used by consolidate phase 1.9 (buildEntityProfiles) to cluster L2s into
100
- * L3 entity profiles. Mirrors loadAllDirtySummaries pattern (E3): SQL-level
92
+ * L3 entity profiles. Mirrors loadAllDirtySummaries: SQL-level
101
93
  * filter is cheaper than reusing in-memory `survivors` (which doesn't
102
94
  * contain L2s freshly created by phase 1.7 buildDag).
103
95
  *
@@ -125,7 +117,7 @@ export function loadAllL2Summaries(hippoRoot) {
125
117
  }
126
118
  }
127
119
  /**
128
- * v0.30 / E3 — host-wide variant of loadDirtySummaries. Iterates all tenants
120
+ * Host-wide variant of loadDirtySummaries. Iterates all tenants
129
121
  * in one query so consolidate.ts (host-wide per L106-109) does not need a
130
122
  * per-tenant loop. Each returned MemoryEntry carries its own tenantId (via
131
123
  * rowToEntry), so per-summary children + rebuild UPDATE stay tenant-scoped.
@@ -152,7 +144,7 @@ export function loadAllDirtySummaries(hippoRoot) {
152
144
  }
153
145
  }
154
146
  /**
155
- * v0.30 / E3 — load live children of a DAG summary. Used by
147
+ * Load live children of a DAG summary. Used by
156
148
  * rebuildDirtySummaries to regenerate content from the CURRENT child set
157
149
  * (not the children at create-time). Skips archived. Tenant-scoped
158
150
  * (defence in depth — dag_parent_id is unique-ish but tenant guard is
@@ -180,7 +172,7 @@ export function loadChildrenOfSummary(hippoRoot, summaryId, tenantId) {
180
172
  }
181
173
  }
182
174
  /**
183
- * v0.30 / E3 — apply a rebuild result to a dirty summary. Atomic: one
175
+ * Apply a rebuild result to a dirty summary. Atomic: one
184
176
  * prepared UPDATE statement plus syncFtsRow inside one SAVEPOINT.
185
177
  * WHERE includes `AND summary_dirty = 1` so concurrent sleep's race-loser
186
178
  * becomes a no-op (no rebuild_count bump, no audit row).
@@ -217,7 +209,6 @@ export function applyRebuildResult(hippoRoot, summary, patch) {
217
209
  }
218
210
  }
219
211
  // ONE prepared UPDATE per branch. Test #8 inspects the SQL string.
220
- // v0.30 / E5: widened dag_level=2 -> IN (2, 3) on both branches.
221
212
  const REBUILD_CONTENT_SQL = `UPDATE memories
222
213
  SET content = ?,
223
214
  descendant_count = ?,
@@ -243,17 +234,8 @@ const REBUILD_METADATA_SQL = `UPDATE memories
243
234
  AND kind != 'archived'`;
244
235
  function applyRebuildInSavepoint(db, summary, patch) {
245
236
  const nowIso = new Date().toISOString();
246
- // AT1 P1a fix (docs/plans/2026-08-15-at1-rejected-value-tombstone.md):
247
- // applyRebuildResult's bumpRebuildCount branch wrote patch.content via
248
- // a direct UPDATE, bypassing the rejection guard entirely (the guard
249
- // lives in upsertEntryRow's INSERT path, which this function never
250
- // calls). A rebuild that regenerates byte-identical content to an
251
- // already-rejected value (e.g. deterministic summarization of an
252
- // unchanged child set) would silently re-assert it every sleep cycle.
253
- // Check BEFORE choosing which UPDATE to run — only the
254
- // bumpRebuildCount branch ever writes content, so a miss or a
255
- // zero-child call is a no-op here (one indexed point query, guarded
256
- // path only).
237
+ // Check tombstones before choosing the UPDATE: this direct UPDATE bypasses upsertEntryRow's guard, and
238
+ // a deterministic rebuild would otherwise re-assert a rejected value every sleep cycle.
257
239
  const tombstone = patch.bumpRebuildCount
258
240
  ? findRejectedValue(db, summary.tenantId, rejectionDigest(patch.content))
259
241
  : null;
@@ -268,23 +250,8 @@ function applyRebuildInSavepoint(db, summary, patch) {
268
250
  const result = applyContentWrite
269
251
  ? db.prepare(REBUILD_CONTENT_SQL).run(patch.content, patch.descendant_count, patch.earliest_at, patch.latest_at, nowIso, summary.id, summary.tenantId)
270
252
  : db.prepare(REBUILD_METADATA_SQL).run(patch.descendant_count, patch.earliest_at, patch.latest_at, summary.id, summary.tenantId);
271
- // Return-value semantics (v0.30/T4 split): `changed` reflects whether
272
- // THIS call's UPDATE (content or metadata-only) affected a row — NOT
273
- // whether patch.content specifically landed. On a refusal, metadata
274
- // still applies, so changed=true even though content did not change.
275
- // This preserves the pre-T4 no-infinite-retry choice: the caller
276
- // (dag.ts rebuildDirtySummaries) treats changed=false as "race lost,
277
- // silently retry next cycle" — returning false on a refusal would
278
- // retry the same doomed LLM rebuild forever, so changed=true settles
279
- // this cycle (dirty cleared) regardless of refusal.
280
- // `refused` is the T4 addition: true only when a tombstone hit AND
281
- // the metadata UPDATE landed (changed=true) — a refusal that loses
282
- // the race to a concurrent writer reports refused=false too, since
283
- // nothing from this call took effect. Before T4, a refusal also
284
- // counted toward the caller's `rebuilt` stat because `changed` alone
285
- // could not distinguish it; the caller now increments `refused`
286
- // instead of `rebuilt` when this is true, so the stat reflects what
287
- // happened without changing dirty-clearing or retry behavior.
253
+ // `changed` means THIS call's UPDATE hit a row, so a refusal still settles the cycle (false would retry
254
+ // the doomed rebuild forever); `refused` lets the caller count it as refused rather than rebuilt.
288
255
  const changed = (result.changes ?? 0) > 0;
289
256
  const refused = Boolean(tombstone) && changed;
290
257
  if (tombstone && changed)
@@ -307,13 +274,8 @@ function auditRefusedRebuild(db, summary, patch, tombstone) {
307
274
  `(digest ${tombstone.digest.slice(0, 12)}...); metadata updated, content unchanged`);
308
275
  }
309
276
  function syncRebuiltSummary(db, summary, patch, applyContentWrite, nowIso) {
310
- // FTS sync — bare UPDATE on memories does NOT update memories_fts.
311
- // R1 HIGH must-fix from plan-eng-r1. Construct the patched entry in
312
- // memory and reuse the existing syncFtsRow helper (delete-then-insert).
313
- // earliest_at/latest_at preserve null semantics (R2 must-fix).
314
- // AT1: content stays summary.content (unchanged) when the write was
315
- // refused — applyContentWrite is false, so patch.content was never
316
- // written to the row FTS must mirror.
277
+ // A bare UPDATE on memories does NOT update memories_fts, so resync from the patched entry;
278
+ // content stays summary.content when the write was refused, since FTS must mirror the row.
317
279
  const patchedEntry = {
318
280
  ...summary,
319
281
  content: applyContentWrite ? patch.content : summary.content,
@@ -328,8 +290,7 @@ function syncRebuiltSummary(db, summary, patch, applyContentWrite, nowIso) {
328
290
  };
329
291
  syncFtsRow(db, patchedEntry);
330
292
  audit(db, 'summary_rebuilt', summary.id, {
331
- // v0.30 / E5: read actual level from the summary in scope
332
- // (NOT hardcoded 2). L2 -> 2, L3 -> 3.
293
+ // Actual level from the summary in scope, never hardcoded: L2 -> 2, L3 -> 3.
333
294
  dag_level: summary.dag_level,
334
295
  source: 'E3-rebuild',
335
296
  zero_children: patch.zeroChildren,
@@ -337,21 +298,17 @@ function syncRebuiltSummary(db, summary, patch, applyContentWrite, nowIso) {
337
298
  }, patch.actor, summary.tenantId);
338
299
  }
339
300
  /**
340
- * v0.30 / E3 — clear summary_dirty on a freshly-built summary. Called by
341
- * buildDag immediately after the child-link loop finishes. Without this,
342
- * each member's writeEntry call fires markSummaryDirtyInTx on the just-
343
- * created parent (E2 hook at store.ts:1214), and the same sleep cycle's
344
- * E3 rebuild phase would re-rebuild every new summary (2x LLM cost).
301
+ * Clear summary_dirty on a freshly-built summary. Called by buildDag right after the child-link loop:
302
+ * each member's writeEntry marks the new parent dirty, so the same cycle's rebuild would redo every new summary.
345
303
  *
346
304
  * Idempotent: no-op + no audit if summary isn't dirty. Audit
347
- * source='buildDag-clean' distinguishes from E3-rebuild source.
305
+ * source='buildDag-clean' distinguishes it from the rebuild's source.
348
306
  */
349
307
  export function clearSummaryDirtyAfterBuild(hippoRoot, summaryId, tenantId, actor = 'cli', source = 'buildDag-clean') {
350
308
  assertTenantId('clearSummaryDirtyAfterBuild', tenantId);
351
309
  const db = openStore(hippoRoot);
352
310
  try {
353
- // v0.30 / E5: widened dag_level=2 -> IN (2, 3). RETURNING dag_level reads
354
- // actual level so audit metadata stays accurate without an extra SELECT.
311
+ // RETURNING dag_level reads the actual level so audit metadata stays accurate without an extra SELECT.
355
312
  // SAFETY: result's shape matches the single `dag_level` column returned
356
313
  // below.
357
314
  const result = db.prepare(`
@@ -365,8 +322,7 @@ export function clearSummaryDirtyAfterBuild(hippoRoot, summaryId, tenantId, acto
365
322
  RETURNING dag_level
366
323
  `).get(summaryId, tenantId);
367
324
  if (result) {
368
- // v0.30 / E5: source param distinguishes buildDag-clean (L2) from
369
- // buildEntityProfiles-clean (L3) and any future build path.
325
+ // source tells buildDag-clean (L2) from buildEntityProfiles-clean (L3) and any future build path.
370
326
  audit(db, 'summary_marked_clean', summaryId, { dag_level: result.dag_level, source }, actor, tenantId);
371
327
  }
372
328
  }
@@ -11,6 +11,8 @@ import { openHippoDbReadOnly, closeHippoDb, getSchemaVersion, getMeta, countTabl
11
11
  import { runDoctor } from './doctor.js';
12
12
  import { loadConfig } from './config.js';
13
13
  import { redactSecretsStrict } from './secret-detect.js';
14
+ import { isJsonString } from './json.js';
15
+ import { escapeRegex } from './escape.js';
14
16
  const TAIL_MAX_BYTES = 256 * 1024;
15
17
  export const TAIL_MAX_LINES = 200;
16
18
  const TAIL_MAX_LINE_CHARS = 2000;
@@ -28,9 +30,6 @@ const CONFIG_SECRET_KEY_RE = /key|token|secret|passw|credential|auth|cookie|bear
28
30
  function isJsonObject(v) {
29
31
  return typeof v === 'object' && v !== null && !Array.isArray(v);
30
32
  }
31
- function isJsonString(v) {
32
- return typeof v === 'string';
33
- }
34
33
  function buildRuntime() {
35
34
  return {
36
35
  node: process.versions.node,
@@ -177,9 +176,6 @@ function buildLogsSection(opts) {
177
176
  }
178
177
  return logs;
179
178
  }
180
- function escapeRegExp(s) {
181
- return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
182
- }
183
179
  // The native realpath is the only form that exposes a short-name (8.3) or symlinked alias for what it is.
184
180
  function nativeRealPathKey(p) {
185
181
  try {
@@ -232,12 +228,12 @@ function mountedDrive(letter) {
232
228
  /** Regex source for one spelling of the home, or null when too little of it is left to swap safely. */
233
229
  function spellingPattern(spelling, deep) {
234
230
  if (process.platform !== 'win32')
235
- return spelling.length >= 3 ? escapeRegExp(spelling) : null;
231
+ return spelling.length >= 3 ? escapeRegex(spelling) : null;
236
232
  // Each tool that mounts a drive writes it its own way, so a home two or more folders below its drive (\Users\<name>)
237
233
  // matches after any prefix, with \, / or JSON's \\ between folders. A drive written a known way goes into the swap with it.
238
234
  const drive = /^([A-Za-z])[:-]/.exec(spelling);
239
235
  const below = drive === null ? spelling : spelling.slice(2);
240
- const body = below.split(/[\\/]+/).map(escapeRegExp).join('[\\\\/]+');
236
+ const body = below.split(/[\\/]+/).map(escapeRegex).join('[\\\\/]+');
241
237
  if (drive === null) {
242
238
  if (spelling.length < 3)
243
239
  return null;
package/dist/tenant.d.ts CHANGED
@@ -1,13 +1,10 @@
1
1
  import type { DatabaseSyncLike } from './db.js';
2
+ import type { JsonValue } from './json.js';
2
3
  export interface ResolveOpts {
3
4
  db?: DatabaseSyncLike;
4
5
  apiKey?: string;
5
6
  }
6
7
  export declare function resolveTenantId(opts: ResolveOpts): string;
7
- /** A value that round-trips through JSON.stringify/JSON.parse unchanged. */
8
- type JsonValue = string | number | boolean | null | JsonValue[] | {
9
- [key: string]: JsonValue;
10
- };
11
8
  /**
12
9
  * Defensive runtime guard for tenant id arguments.
13
10
  *
@@ -25,5 +22,4 @@ type JsonValue = string | number | boolean | null | JsonValue[] | {
25
22
  * Acceptable tradeoff for catching the silent-leak class.
26
23
  */
27
24
  export declare function assertTenantId(fnName: string, value: JsonValue): asserts value is string;
28
- export {};
29
25
  //# sourceMappingURL=tenant.d.ts.map
@@ -2,7 +2,7 @@ import type { DatabaseSyncLike } from './db.js';
2
2
  /**
3
3
  * Where a block of memory text was sent.
4
4
  * - `hook`: the per-prompt `UserPromptSubmit` hook (`hippo context --pinned-only`).
5
- * - `hook_recall`: the same hook's Z1 prompt-recall section (docs/plans/2026-09-26-z1-prompt-recall.md).
5
+ * - `hook_recall`: the same hook's prompt-recall section.
6
6
  * - `compact_resume`: the snapshot the SessionStart(compact) hook prints (`hippo compact-resume`).
7
7
  * - `context`, `recall`: the CLI commands.
8
8
  * - `mcp_recall`, `mcp_context`: the MCP tools.
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Token ledger (ROADMAP Part IX, TE0): what memory text hippo hands agents,
2
+ * Token ledger: what memory text hippo hands agents,
3
3
  * and how many tokens it costs.
4
4
  *
5
5
  * One row per block of memory text sent to an agent, on every surface: the
@@ -11,7 +11,7 @@
11
11
  * Every later model call re-reads a sent block until the host compacts; at session end the worker counts
12
12
  * those calls from the transcript as `reread` rows for the {@link REREAD_SURFACES} blocks, dated by call day.
13
13
  *
14
- * It also backs TE2, inject only on change: the per-prompt hook compares the
14
+ * It also backs inject-only-on-change: the per-prompt hook compares the
15
15
  * hash of the block it is about to send with the last block it sent in the
16
16
  * same session and records a `skip` instead of sending it again.
17
17
  *
@@ -25,6 +25,7 @@
25
25
  */
26
26
  import { createHash } from 'node:crypto';
27
27
  import { open } from 'node:fs/promises';
28
+ import { isJsonString } from './json.js';
28
29
  /** All surfaces, in report order. */
29
30
  export const TOKEN_SURFACES = [
30
31
  'hook', 'hook_recall', 'compact_resume', 'context', 'recall', 'mcp_recall', 'mcp_context',
@@ -144,9 +145,6 @@ export function summarizeTokenUse(db, tenantId, sinceIso) {
144
145
  export function isSyntheticMessage(message) {
145
146
  return message.model === '<synthetic>';
146
147
  }
147
- function isJsonString(value) {
148
- return value !== undefined && value !== null && value.constructor === String;
149
- }
150
148
  /** JSON-value plain-object check (excludes arrays and null). */
151
149
  function isJsonObject(value) {
152
150
  return value !== undefined && value !== null && !Array.isArray(value) && value.constructor === Object;
package/dist/trace.js CHANGED
@@ -5,6 +5,7 @@
5
5
  * sequence of agent actions and their final outcome. v1 stores steps as
6
6
  * markdown in `content`; `renderTraceContent` is the canonical formatter.
7
7
  */
8
+ import { isJsonString } from './json.js';
8
9
  /**
9
10
  * Render a trace record as agent-readable markdown.
10
11
  *
@@ -32,9 +33,6 @@ export function renderTraceContent(rec) {
32
33
  function isJsonRecord(value) {
33
34
  return value !== null && !Array.isArray(value) && typeof value === 'object';
34
35
  }
35
- function isJsonString(value) {
36
- return typeof value === 'string';
37
- }
38
36
  /**
39
37
  * Parse a JSON string into an array of TraceStep. Throws on invalid shape.
40
38
  */
package/dist/version.d.ts CHANGED
@@ -16,7 +16,7 @@
16
16
  * an ESM `import` can resolve cleanly, and a hardcoded constant survives
17
17
  * any packager that drops .json files.
18
18
  */
19
- export declare const PACKAGE_VERSION = "1.61.0";
19
+ export declare const PACKAGE_VERSION = "1.63.0";
20
20
  /** Compares plain x.y.z versions, positive if a > b; tags throw so the rollback guard never misfires silently. */
21
21
  export declare function compareSemver(a: string, b: string): number;
22
22
  //# sourceMappingURL=version.d.ts.map
package/dist/version.js CHANGED
@@ -16,7 +16,7 @@
16
16
  * an ESM `import` can resolve cleanly, and a hardcoded constant survives
17
17
  * any packager that drops .json files.
18
18
  */
19
- export const PACKAGE_VERSION = '1.61.0';
19
+ export const PACKAGE_VERSION = '1.63.0';
20
20
  /** Compares plain x.y.z versions, positive if a > b; tags throw so the rollback guard never misfires silently. */
21
21
  export function compareSemver(a, b) {
22
22
  const parse = (v) => {
@@ -5,8 +5,8 @@
5
5
  * importance-ranked, and automatically evicted when the buffer
6
6
  * exceeds WM_MAX_ENTRIES per scope.
7
7
  */
8
+ import type { JsonValue } from './json.js';
8
9
  export declare const WM_MAX_ENTRIES = 20;
9
- export type JsonValue = string | number | boolean | null | JsonValue[] | JsonObject;
10
10
  export type JsonObject = {
11
11
  [key: string]: JsonValue;
12
12
  };
@@ -2,7 +2,7 @@
2
2
  "id": "hippo-memory",
3
3
  "name": "Hippo Memory",
4
4
  "description": "Memory for AI agents that learns what is wrong and ranks it down. Injects context at session start and captures errors.",
5
- "version": "1.61.0",
5
+ "version": "1.63.0",
6
6
 
7
7
  "configSchema": {
8
8
  "type": "object",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hippo-memory",
3
- "version": "1.61.0",
3
+ "version": "1.63.0",
4
4
  "type": "module",
5
5
  "description": "Hippo Memory plugin for OpenClaw - biologically-inspired agent memory",
6
6
  "main": "index.ts",
@@ -2,7 +2,7 @@
2
2
  "id": "hippo-memory",
3
3
  "name": "Hippo Memory",
4
4
  "description": "Memory for AI agents that learns what is wrong and ranks it down. Injects context at session start and captures errors.",
5
- "version": "1.61.0",
5
+ "version": "1.63.0",
6
6
  "configSchema": {
7
7
  "type": "object",
8
8
  "additionalProperties": false,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hippo-memory",
3
- "version": "1.61.0",
3
+ "version": "1.63.0",
4
4
  "description": "Memory for AI agents that learns what is wrong and ranks it down. MCP server, hooks for Claude Code, OpenCode and Codex, AGENTS.md instructions for Codex, Cursor, OpenClaw and Pi. SQLite, zero runtime deps.",
5
5
  "mcpName": "io.github.kitfunso/hippo-memory",
6
6
  "type": "module",