hippo-memory 1.62.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 (370) hide show
  1. package/README.md +9 -0
  2. package/dist/agent-memories/apply.d.ts +2 -0
  3. package/dist/agent-memories/apply.js +23 -1
  4. package/dist/agent-memories/claude-code.d.ts +2 -1
  5. package/dist/agent-memories/claude-code.js +4 -4
  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.js +37 -23
  9. package/dist/ambient-store.d.ts +4 -4
  10. package/dist/ambient-store.js +4 -3
  11. package/dist/api/assemble.d.ts +7 -10
  12. package/dist/api/assemble.js +7 -12
  13. package/dist/api/audit.d.ts +2 -2
  14. package/dist/api/audit.js +2 -2
  15. package/dist/api/auth.d.ts +6 -6
  16. package/dist/api/auth.js +5 -7
  17. package/dist/api/context-select.d.ts +4 -3
  18. package/dist/api/context-select.js +9 -11
  19. package/dist/api/context-types.d.ts +12 -17
  20. package/dist/api/context.d.ts +3 -2
  21. package/dist/api/context.js +20 -20
  22. package/dist/api/dormant.js +1 -1
  23. package/dist/api/drill-down.d.ts +4 -4
  24. package/dist/api/drill-down.js +22 -10
  25. package/dist/api/outcome.d.ts +8 -13
  26. package/dist/api/outcome.js +13 -17
  27. package/dist/api/promote.d.ts +5 -8
  28. package/dist/api/quarantine.js +3 -2
  29. package/dist/api/recall-types.d.ts +43 -55
  30. package/dist/api/recall.d.ts +3 -3
  31. package/dist/api/recall.js +7 -18
  32. package/dist/api/remember.d.ts +2 -2
  33. package/dist/api/sleep.d.ts +7 -35
  34. package/dist/api/sleep.js +3 -2
  35. package/dist/api/tokens.d.ts +2 -2
  36. package/dist/api/tokens.js +2 -2
  37. package/dist/api/types.d.ts +6 -10
  38. package/dist/api/types.js +2 -4
  39. package/dist/audit-prune.d.ts +4 -6
  40. package/dist/audit-prune.js +3 -5
  41. package/dist/audit.js +11 -33
  42. package/dist/auth.d.ts +8 -9
  43. package/dist/auth.js +5 -7
  44. package/dist/autolearn.d.ts +1 -1
  45. package/dist/autolearn.js +1 -1
  46. package/dist/availability.js +3 -5
  47. package/dist/capture/command.d.ts +5 -4
  48. package/dist/capture/command.js +8 -22
  49. package/dist/capture/compact.d.ts +1 -1
  50. package/dist/capture/compact.js +9 -13
  51. package/dist/capture/extract.js +37 -115
  52. package/dist/capture-error.d.ts +1 -1
  53. package/dist/capture-error.js +1 -1
  54. package/dist/churn-git.d.ts +1 -1
  55. package/dist/churn-git.js +1 -1
  56. package/dist/cli/audit.js +3 -4
  57. package/dist/cli/auth.js +3 -6
  58. package/dist/cli/curate.d.ts +1 -1
  59. package/dist/cli/curate.js +8 -24
  60. package/dist/cli/dag.js +5 -9
  61. package/dist/cli/decisions.js +10 -21
  62. package/dist/cli/explain.js +2 -1
  63. package/dist/cli/goals.js +1 -1
  64. package/dist/cli/init.js +1 -1
  65. package/dist/cli/playbooks.js +4 -9
  66. package/dist/cli/projects.js +3 -1
  67. package/dist/cli/recall.js +2 -1
  68. package/dist/cli/remember.js +6 -18
  69. package/dist/cli/session-hooks.js +21 -40
  70. package/dist/cli/setup.d.ts +1 -1
  71. package/dist/cli/setup.js +2 -6
  72. package/dist/cli/shared.js +3 -3
  73. package/dist/cli/slack.js +1 -1
  74. package/dist/cli/sleep.js +27 -1
  75. package/dist/cli/status.d.ts +4 -4
  76. package/dist/cli/status.js +7 -12
  77. package/dist/cli/transfer.js +8 -13
  78. package/dist/cli/usage.js +9 -6
  79. package/dist/cli.d.ts +1 -1
  80. package/dist/cli.js +4 -9
  81. package/dist/compaction-record.d.ts +2 -0
  82. package/dist/compaction-record.js +89 -68
  83. package/dist/compare.d.ts +11 -16
  84. package/dist/compare.js +11 -16
  85. package/dist/config.d.ts +19 -18
  86. package/dist/config.js +90 -67
  87. package/dist/connectors/github/backfill.d.ts +2 -2
  88. package/dist/connectors/github/backfill.js +8 -15
  89. package/dist/connectors/github/cli-impl.js +3 -8
  90. package/dist/connectors/github/deletion.d.ts +5 -12
  91. package/dist/connectors/github/deletion.js +5 -12
  92. package/dist/connectors/github/dlq.d.ts +6 -9
  93. package/dist/connectors/github/dlq.js +2 -3
  94. package/dist/connectors/github/ingest.d.ts +5 -7
  95. package/dist/connectors/github/ingest.js +8 -12
  96. package/dist/connectors/github/octokit-client.d.ts +3 -5
  97. package/dist/connectors/github/octokit-client.js +5 -6
  98. package/dist/connectors/github/signature.d.ts +9 -39
  99. package/dist/connectors/github/signature.js +9 -39
  100. package/dist/connectors/github/tenant-routing.d.ts +1 -1
  101. package/dist/connectors/github/tenant-routing.js +1 -1
  102. package/dist/connectors/github/transform.js +2 -2
  103. package/dist/connectors/github/types.d.ts +2 -10
  104. package/dist/connectors/github/types.js +1 -3
  105. package/dist/connectors/slack/deletion.d.ts +3 -8
  106. package/dist/connectors/slack/deletion.js +3 -8
  107. package/dist/connectors/slack/dlq.d.ts +1 -1
  108. package/dist/connectors/slack/ingest.d.ts +1 -1
  109. package/dist/connectors/slack/ingest.js +7 -16
  110. package/dist/connectors/slack/signature.d.ts +1 -1
  111. package/dist/connectors/slack/tenant-routing.d.ts +3 -5
  112. package/dist/connectors/slack/tenant-routing.js +3 -5
  113. package/dist/connectors/slack/transform.d.ts +5 -6
  114. package/dist/connectors/slack/transform.js +5 -6
  115. package/dist/connectors/slack/types.d.ts +2 -6
  116. package/dist/connectors/slack/types.js +1 -3
  117. package/dist/connectors/slack/web-client.js +10 -3
  118. package/dist/connectors/slack/workspaces.d.ts +3 -5
  119. package/dist/connectors/slack/workspaces.js +3 -5
  120. package/dist/consolidate/conflicts.js +3 -14
  121. package/dist/consolidate/decay.js +9 -29
  122. package/dist/consolidate/llm-passes.js +4 -5
  123. package/dist/consolidate/merge.js +8 -23
  124. package/dist/consolidate/run.d.ts +1 -8
  125. package/dist/consolidate/run.js +3 -25
  126. package/dist/consolidate/sleep.js +5 -17
  127. package/dist/consolidate/traces.js +9 -21
  128. package/dist/customer-notes.d.ts +5 -7
  129. package/dist/customer-notes.js +6 -9
  130. package/dist/dag.d.ts +10 -21
  131. package/dist/dag.js +23 -73
  132. package/dist/db/continuity.js +2 -2
  133. package/dist/db/migrations/v14.js +1 -1
  134. package/dist/db/migrations/v15.js +1 -2
  135. package/dist/db/migrations/v16.js +3 -4
  136. package/dist/db/migrations/v17.js +2 -3
  137. package/dist/db/migrations/v19.js +1 -1
  138. package/dist/db/migrations/v20.js +1 -1
  139. package/dist/db/migrations/v21.js +2 -6
  140. package/dist/db/migrations/v22.js +2 -4
  141. package/dist/db/migrations/v23.js +1 -1
  142. package/dist/db/migrations/v24.js +4 -6
  143. package/dist/db/migrations/v25.js +2 -3
  144. package/dist/db/migrations/v26.js +3 -3
  145. package/dist/db/migrations/v27.js +2 -10
  146. package/dist/db/migrations/v28.js +5 -8
  147. package/dist/db/migrations/v29.js +3 -4
  148. package/dist/db/migrations/v30.js +2 -2
  149. package/dist/db/migrations/v31.js +1 -1
  150. package/dist/db/migrations/v32.js +1 -1
  151. package/dist/db/migrations/v33.js +3 -3
  152. package/dist/db/migrations/v34.js +1 -1
  153. package/dist/db/migrations/v35.js +3 -4
  154. package/dist/db/migrations/v36.js +3 -4
  155. package/dist/db/migrations/v37.js +5 -5
  156. package/dist/db/migrations/v38.js +7 -8
  157. package/dist/db/migrations/v39.js +1 -1
  158. package/dist/db/migrations/v40.js +4 -16
  159. package/dist/db/migrations/v41.js +3 -4
  160. package/dist/db/migrations/v42.js +3 -4
  161. package/dist/db/migrations/v45.js +1 -1
  162. package/dist/db/migrations/v46.js +1 -1
  163. package/dist/db/migrations/v47.js +1 -1
  164. package/dist/db/migrations/v48.js +1 -1
  165. package/dist/decisions.d.ts +2 -2
  166. package/dist/decisions.js +6 -6
  167. package/dist/dedupe.js +86 -61
  168. package/dist/delivery-recorder.js +154 -135
  169. package/dist/doctor.js +119 -104
  170. package/dist/dormant.js +1 -4
  171. package/dist/embedding-provider.d.ts +4 -8
  172. package/dist/embedding-provider.js +4 -8
  173. package/dist/embeddings.js +55 -47
  174. package/dist/env.d.ts +1 -1
  175. package/dist/env.js +12 -12
  176. package/dist/escape.d.ts +5 -0
  177. package/dist/escape.js +10 -0
  178. package/dist/eval-stats.d.ts +1 -2
  179. package/dist/eval-stats.js +1 -2
  180. package/dist/eval-suite.js +27 -21
  181. package/dist/extract.js +4 -9
  182. package/dist/failure-log.d.ts +3 -3
  183. package/dist/failure-log.js +1 -1
  184. package/dist/forward-claim-detector.d.ts +2 -4
  185. package/dist/forward-claim-detector.js +6 -11
  186. package/dist/goals.d.ts +3 -3
  187. package/dist/goals.js +5 -6
  188. package/dist/graph/read.d.ts +2 -2
  189. package/dist/graph/read.js +5 -6
  190. package/dist/graph/types.d.ts +8 -8
  191. package/dist/graph/write.d.ts +7 -14
  192. package/dist/graph/write.js +16 -23
  193. package/dist/graph-extract.d.ts +7 -8
  194. package/dist/graph-extract.js +62 -72
  195. package/dist/graph-recall.d.ts +2 -2
  196. package/dist/graph-recall.js +55 -49
  197. package/dist/graph-stream.d.ts +5 -6
  198. package/dist/graph-stream.js +66 -57
  199. package/dist/graph-view.d.ts +2 -2
  200. package/dist/graph-view.js +7 -7
  201. package/dist/half-life-migration.d.ts +1 -2
  202. package/dist/half-life-migration.js +2 -3
  203. package/dist/hooks/codex-session.js +1 -1
  204. package/dist/hooks/codex-wrapper.d.ts +1 -1
  205. package/dist/hooks/codex-wrapper.js +3 -2
  206. package/dist/hooks/json-hooks.d.ts +2 -2
  207. package/dist/hooks/json-hooks.js +5 -4
  208. package/dist/hooks/opencode.d.ts +1 -1
  209. package/dist/hooks/opencode.js +5 -4
  210. package/dist/hooks/shared.d.ts +3 -7
  211. package/dist/hooks/shared.js +1 -8
  212. package/dist/http-util.d.ts +2 -3
  213. package/dist/http-util.js +3 -0
  214. package/dist/importers/core.d.ts +2 -9
  215. package/dist/importers/core.js +15 -30
  216. package/dist/importers/sources.js +2 -1
  217. package/dist/importers/vault.js +2 -20
  218. package/dist/incidents.d.ts +1 -1
  219. package/dist/incidents.js +1 -1
  220. package/dist/instruction-detect.d.ts +1 -1
  221. package/dist/instruction-detect.js +1 -1
  222. package/dist/invalidation.d.ts +3 -0
  223. package/dist/invalidation.js +160 -114
  224. package/dist/json.d.ts +5 -0
  225. package/dist/json.js +4 -0
  226. package/dist/judgment.js +1 -2
  227. package/dist/local-embedding.js +1 -1
  228. package/dist/mcp/admin-tools.js +7 -17
  229. package/dist/mcp/format.js +1 -1
  230. package/dist/mcp/framing.js +3 -6
  231. package/dist/mcp/protocol.d.ts +2 -5
  232. package/dist/mcp/protocol.js +1 -3
  233. package/dist/mcp/recall-tools.js +12 -15
  234. package/dist/mcp/request.js +4 -3
  235. package/dist/mcp/session-state.js +2 -3
  236. package/dist/mcp/stdio.js +2 -1
  237. package/dist/mcp/tools.js +9 -6
  238. package/dist/memory-value-weights.d.ts +5 -8
  239. package/dist/memory-value-weights.js +5 -8
  240. package/dist/memory-value.d.ts +13 -13
  241. package/dist/memory-value.js +26 -37
  242. package/dist/memory.d.ts +20 -22
  243. package/dist/memory.js +24 -48
  244. package/dist/multihop.d.ts +1 -1
  245. package/dist/multihop.js +3 -2
  246. package/dist/owner-validation.d.ts +4 -5
  247. package/dist/owner-validation.js +4 -5
  248. package/dist/physics.d.ts +4 -4
  249. package/dist/physics.js +7 -9
  250. package/dist/policies.d.ts +9 -10
  251. package/dist/policies.js +12 -14
  252. package/dist/postinstall.js +3 -6
  253. package/dist/predictions/planning-fallacy.d.ts +9 -14
  254. package/dist/predictions/planning-fallacy.js +10 -16
  255. package/dist/predictions/store.d.ts +15 -23
  256. package/dist/predictions/store.js +36 -33
  257. package/dist/processes.d.ts +2 -7
  258. package/dist/processes.js +3 -3
  259. package/dist/project-briefs.d.ts +2 -3
  260. package/dist/project-briefs.js +9 -13
  261. package/dist/project-identity.d.ts +22 -9
  262. package/dist/project-identity.js +47 -12
  263. package/dist/project-merge.d.ts +17 -2
  264. package/dist/project-merge.js +109 -27
  265. package/dist/project-remote.d.ts +12 -0
  266. package/dist/project-remote.js +138 -0
  267. package/dist/prompt-recall.js +1 -2
  268. package/dist/rate-limit.d.ts +1 -1
  269. package/dist/rate-limit.js +1 -1
  270. package/dist/raw-archive.d.ts +9 -0
  271. package/dist/raw-archive.js +70 -53
  272. package/dist/recall-history.d.ts +19 -20
  273. package/dist/recall-history.js +24 -42
  274. package/dist/recall-pipeline.js +4 -28
  275. package/dist/recall-scope.d.ts +7 -8
  276. package/dist/recall-scope.js +7 -8
  277. package/dist/recall-trace.d.ts +5 -9
  278. package/dist/recall-trace.js +6 -10
  279. package/dist/refine-llm.d.ts +1 -1
  280. package/dist/refine-llm.js +2 -2
  281. package/dist/reject-flow.d.ts +3 -4
  282. package/dist/reject-flow.js +122 -117
  283. package/dist/rejection.d.ts +5 -6
  284. package/dist/rejection.js +7 -15
  285. package/dist/rerankers/clef.d.ts +1 -1
  286. package/dist/rerankers/jev.d.ts +1 -2
  287. package/dist/rerankers/jev.js +4 -5
  288. package/dist/rerankers/llm.d.ts +1 -2
  289. package/dist/rerankers/llm.js +1 -2
  290. package/dist/rerankers/types.d.ts +1 -2
  291. package/dist/rrf.d.ts +2 -2
  292. package/dist/rrf.js +2 -2
  293. package/dist/search/bm25-search.d.ts +1 -1
  294. package/dist/search/bm25-search.js +2 -1
  295. package/dist/search/boosts.js +2 -1
  296. package/dist/search/hybrid.d.ts +1 -1
  297. package/dist/search/hybrid.js +2 -1
  298. package/dist/search/physics-search.d.ts +1 -1
  299. package/dist/search/physics-search.js +2 -1
  300. package/dist/search/types.d.ts +2 -0
  301. package/dist/search/types.js +3 -1
  302. package/dist/secret-detect.d.ts +4 -5
  303. package/dist/secret-detect.js +6 -10
  304. package/dist/server/auth.js +5 -5
  305. package/dist/server/client-ip.js +1 -1
  306. package/dist/server/cursor.js +2 -1
  307. package/dist/server/mcp-http.js +4 -4
  308. package/dist/server/request.d.ts +3 -6
  309. package/dist/server/request.js +6 -7
  310. package/dist/server/routes/admin.js +5 -4
  311. package/dist/server/routes/customer-notes.js +6 -5
  312. package/dist/server/routes/decisions.js +4 -3
  313. package/dist/server/routes/incidents.js +7 -5
  314. package/dist/server/routes/memories.js +7 -7
  315. package/dist/server/routes/policies.js +3 -2
  316. package/dist/server/routes/predictions.js +12 -15
  317. package/dist/server/routes/processes.js +3 -2
  318. package/dist/server/routes/project-briefs.js +8 -7
  319. package/dist/server/routes/recall.js +95 -93
  320. package/dist/server/routes/skills.js +6 -5
  321. package/dist/server/types.d.ts +1 -1
  322. package/dist/server/validation.d.ts +1 -2
  323. package/dist/server/validation.js +7 -14
  324. package/dist/server-detect.js +72 -58
  325. package/dist/server.d.ts +2 -2
  326. package/dist/server.js +131 -117
  327. package/dist/shared.d.ts +17 -17
  328. package/dist/shared.js +93 -97
  329. package/dist/skills.d.ts +3 -3
  330. package/dist/skills.js +8 -8
  331. package/dist/store/audit-event.d.ts +2 -2
  332. package/dist/store/audit-event.js +1 -1
  333. package/dist/store/candidates.d.ts +2 -2
  334. package/dist/store/candidates.js +4 -3
  335. package/dist/store/conflicts.js +30 -22
  336. package/dist/store/delete-and-batch.d.ts +11 -14
  337. package/dist/store/delete-and-batch.js +40 -91
  338. package/dist/store/entry-reads.d.ts +17 -25
  339. package/dist/store/entry-reads.js +59 -40
  340. package/dist/store/entry-row.d.ts +6 -24
  341. package/dist/store/entry-row.js +6 -24
  342. package/dist/store/entry-writes.d.ts +6 -7
  343. package/dist/store/entry-writes.js +13 -11
  344. package/dist/store/handoffs.d.ts +1 -1
  345. package/dist/store/handoffs.js +7 -10
  346. package/dist/store/index-and-stats.d.ts +2 -6
  347. package/dist/store/index-and-stats.js +4 -10
  348. package/dist/store/mirrors.d.ts +6 -19
  349. package/dist/store/mirrors.js +14 -39
  350. package/dist/store/open.js +9 -31
  351. package/dist/store/rows.d.ts +5 -11
  352. package/dist/store/rows.js +6 -11
  353. package/dist/store/search-rows.d.ts +17 -34
  354. package/dist/store/search-rows.js +31 -59
  355. package/dist/store/sessions.d.ts +4 -5
  356. package/dist/store/sessions.js +5 -6
  357. package/dist/store/summaries.d.ts +13 -17
  358. package/dist/store/summaries.js +26 -70
  359. package/dist/support-bundle.js +4 -8
  360. package/dist/tenant.d.ts +1 -5
  361. package/dist/token-ledger.d.ts +1 -1
  362. package/dist/token-ledger.js +3 -5
  363. package/dist/trace.js +1 -3
  364. package/dist/version.d.ts +1 -1
  365. package/dist/version.js +1 -1
  366. package/dist/working-memory.d.ts +1 -1
  367. package/extensions/openclaw-plugin/openclaw.plugin.json +1 -1
  368. package/extensions/openclaw-plugin/package.json +1 -1
  369. package/openclaw.plugin.json +1 -1
  370. package/package.json +1 -1
@@ -1,8 +1,7 @@
1
1
  /**
2
- * v1.25.0 — recall-side scope predicates, extracted from api.ts into a leaf
3
- * module so shared.ts (which api.ts imports) can apply the same default-deny
4
- * rule to searchBothHybrid's internal candidate loads without an import
5
- * cycle. Mirrors the v39 `project-identity.ts` precedent. api.ts imports
2
+ * Recall-side scope predicates, in a leaf module so shared.ts (which api.ts
3
+ * imports) can apply the same default-deny rule to searchBothHybrid's internal
4
+ * candidate loads without an import cycle. api.ts imports
6
5
  * these for its own call sites AND re-exports them for back-compat
7
6
  * (`api.isPrivateScope`, test imports of `passesScopeFilterForRecall`).
8
7
  */
@@ -35,7 +34,7 @@ export function assertNonEmpty(arr, name) {
35
34
  }
36
35
  assertNonEmpty(RECALL_DEFAULT_DENY_SCOPES, 'RECALL_DEFAULT_DENY_SCOPES');
37
36
  /**
38
- * v1.2.1: source-agnostic private-scope detector. A scope string is treated
37
+ * Source-agnostic private-scope detector. A scope string is treated
39
38
  * as private when it has the shape `<lowercase-source>:private:<rest>`.
40
39
  *
41
40
  * Examples that match:
@@ -68,7 +67,7 @@ export function isPrivateScope(scope) {
68
67
  * `<source>:private:*` scope and on the `RECALL_DEFAULT_DENY_SCOPES`
69
68
  * quarantine buckets. `null` and public scopes pass.
70
69
  *
71
- * @internal v1.7.2 — exported for test parity with
70
+ * @internal Exported for test parity with
72
71
  * `RECALL_DEFAULT_DENY_SCOPES` (single-source-of-truth verification). NOT part
73
72
  * of the public API surface; not re-exported from `src/index.ts`. Subject to
74
73
  * change without semver bump.
@@ -80,7 +79,7 @@ export function passesScopeFilterForRecall(scope, requested) {
80
79
  return !isRestrictedScope(scope);
81
80
  }
82
81
  /**
83
- * v1.25.0 — the CLI `--scope` variant of the recall filter (JS half of the
82
+ * The CLI `--scope` variant of the recall filter (JS half of the
84
83
  * SQL 'default-deny-or-exact' mode in loadSearchRows).
85
84
  *
86
85
  * The CLI flag predates the envelope column as a TAG-boost ranking hint
@@ -135,7 +134,7 @@ export function canReadScope(actor, scope) {
135
134
  return true;
136
135
  return (actor.scopes ?? []).includes(scope);
137
136
  }
138
- /** Authorize an explicitly requested scope before any read honours it (ROADMAP Part VIII EI2: member scope grants). */
137
+ /** Authorize an explicitly requested scope before any read honours it (member scope grants). */
139
138
  export function assertScopeRequestAllowed(actor, requested) {
140
139
  if (requested === undefined || requested === '')
141
140
  return;
@@ -1,6 +1,5 @@
1
1
  /**
2
- * LC1 — retrieval-trace persistence
3
- * (docs/plans/2026-08-02-lc1-recall-trace-persistence.md).
2
+ * Retrieval-trace persistence.
4
3
  *
5
4
  * Single producer for the `recall_traces` / `recall_trace_results` /
6
5
  * `recall_trace_outcomes` tables (schema v40). Every recall on the three
@@ -59,14 +58,11 @@ export declare function writeRecallTrace(db: DatabaseSyncLike, input: RecallTrac
59
58
  * Used at api.getContext and CLI cmdRecall — sites where the block's own
60
59
  * convention is per-call handles (writeEntry, saveIndex) and the earlier
61
60
  * audit handles are already closed. NOT used by api.recall, which must
62
- * reuse the caller's open handle (v1.11.5 no-side-effects contract,
61
+ * reuse the caller's open handle (no-side-effects contract,
63
62
  * tests/api-recall-no-side-effects.test.ts).
64
63
  *
65
- * F1 structural fix (replaces the earlier stamp-then-clear design): this
66
- * function does NOT touch the `last_trace_id` meta key. Stamping lived here
67
- * originally, on its own connection, separate from the `last_retrieval_ids`
68
- * write in `saveIndex` — two connections meant two commits, so a crash or
69
- * a failed second write could advance one without the other. LOCKSTEP
64
+ * This function does NOT touch the `last_trace_id` meta key: its own connection
65
+ * would commit apart from `saveIndex`, so a crash could advance one key alone. LOCKSTEP
70
66
  * INVARIANT: `last_trace_id` must only ever advance in the SAME write as
71
67
  * `last_retrieval_ids`. The caller now does: call this function FIRST, set
72
68
  * `localIndex.last_trace_id` from the returned id, THEN call `saveIndex`
@@ -98,7 +94,7 @@ export interface RecordTraceOutcomeInput {
98
94
  * Lives in its own append-only table, not audit_log metadata: audit_log is
99
95
  * pruned by `pruneAuditLog`, and pruning must never erase training data.
100
96
  *
101
- * F4 validation (codex cross-model finding): `traceId`/`memoryIds` reach
97
+ * Validation: `traceId`/`memoryIds` reach
102
98
  * this function from caller-side state (`last_trace_id` / applied outcome
103
99
  * ids) that can go stale relative to the trace it names — a forgotten
104
100
  * memory, a tenant switch mid-session, or a race between two callers. Two
@@ -1,6 +1,5 @@
1
1
  /**
2
- * LC1 — retrieval-trace persistence
3
- * (docs/plans/2026-08-02-lc1-recall-trace-persistence.md).
2
+ * Retrieval-trace persistence.
4
3
  *
5
4
  * Single producer for the `recall_traces` / `recall_trace_results` /
6
5
  * `recall_trace_outcomes` tables (schema v40). Every recall on the three
@@ -21,7 +20,7 @@ import { DELIVERY_LEDGER_VERSION } from './delivery-recorder.js';
21
20
  import { log } from './log.js';
22
21
  /**
23
22
  * Strip a RerankStep down to {stage, multiplier, scoreBefore, scoreAfter}
24
- * before persisting (F3 privacy fix, codex cross-model finding). `note` is
23
+ * before persisting. `note` is
25
24
  * free-form human text — the CLI's goal-boost step embeds matched goal tag
26
25
  * text there, so persisting it verbatim would leak raw user content into
27
26
  * training data via `rerank_json`. Only the four structured fields survive;
@@ -89,14 +88,11 @@ export function writeRecallTrace(db, input) {
89
88
  * Used at api.getContext and CLI cmdRecall — sites where the block's own
90
89
  * convention is per-call handles (writeEntry, saveIndex) and the earlier
91
90
  * audit handles are already closed. NOT used by api.recall, which must
92
- * reuse the caller's open handle (v1.11.5 no-side-effects contract,
91
+ * reuse the caller's open handle (no-side-effects contract,
93
92
  * tests/api-recall-no-side-effects.test.ts).
94
93
  *
95
- * F1 structural fix (replaces the earlier stamp-then-clear design): this
96
- * function does NOT touch the `last_trace_id` meta key. Stamping lived here
97
- * originally, on its own connection, separate from the `last_retrieval_ids`
98
- * write in `saveIndex` — two connections meant two commits, so a crash or
99
- * a failed second write could advance one without the other. LOCKSTEP
94
+ * This function does NOT touch the `last_trace_id` meta key: its own connection
95
+ * would commit apart from `saveIndex`, so a crash could advance one key alone. LOCKSTEP
100
96
  * INVARIANT: `last_trace_id` must only ever advance in the SAME write as
101
97
  * `last_retrieval_ids`. The caller now does: call this function FIRST, set
102
98
  * `localIndex.last_trace_id` from the returned id, THEN call `saveIndex`
@@ -135,7 +131,7 @@ export function writeRecallTraceAtRoot(root, input) {
135
131
  * Lives in its own append-only table, not audit_log metadata: audit_log is
136
132
  * pruned by `pruneAuditLog`, and pruning must never erase training data.
137
133
  *
138
- * F4 validation (codex cross-model finding): `traceId`/`memoryIds` reach
134
+ * Validation: `traceId`/`memoryIds` reach
139
135
  * this function from caller-side state (`last_trace_id` / applied outcome
140
136
  * ids) that can go stale relative to the trace it names — a forgotten
141
137
  * memory, a tenant switch mid-session, or a race between two callers. Two
@@ -23,7 +23,7 @@ export interface RefineOptions {
23
23
  /** Injected for testing — defaults to the real fetch. */
24
24
  fetcher?: typeof fetch;
25
25
  /**
26
- * L9: tenant scope. When provided, refineStore only scans consolidated
26
+ * Tenant scope. When provided, refineStore only scans consolidated
27
27
  * entries belonging to this tenant, and parent lookups are scoped to the
28
28
  * same tenant. Cross-tenant parents return null from readEntry and are
29
29
  * silently skipped (refine still produces output from merged content).
@@ -109,7 +109,7 @@ export async function refineStore(hippoRoot, opts) {
109
109
  failed: 0,
110
110
  details: [],
111
111
  };
112
- // L9: when opts.tenantId is provided, scope the top-level scan to this
112
+ // When opts.tenantId is provided, scope the top-level scan to this
113
113
  // tenant's consolidated entries.
114
114
  const entries = loadAllEntries(hippoRoot, opts.tenantId);
115
115
  let processed = 0;
@@ -130,7 +130,7 @@ export async function refineStore(hippoRoot, opts) {
130
130
  const sources = [];
131
131
  const parentIds = Array.isArray(entry.parents) ? entry.parents : [];
132
132
  for (const pid of parentIds) {
133
- // L9: parent lookup scoped by opts.tenantId when provided.
133
+ // Parent lookup scoped by opts.tenantId when provided.
134
134
  // Cross-tenant parents return null and are silently skipped — refine
135
135
  // still produces output from the merged content alone (graceful
136
136
  // degradation rather than refuse-to-refine).
@@ -1,6 +1,5 @@
1
1
  /**
2
- * AT1 rejected-value tombstone — shared reject/unreject/list flow.
3
- * docs/plans/2026-08-15-at1-rejected-value-tombstone.md (T2, plan §4).
2
+ * Rejected-value tombstone: shared reject/unreject/list flow.
4
3
  *
5
4
  * The CLI (`hippo reject`/`rejections`/`unreject`) and the Context-based
6
5
  * `api.reject`/`api.unreject`/`api.listRejections` surfaces both need the
@@ -24,12 +23,12 @@ export interface RejectFlowOpts {
24
23
  }
25
24
  export interface RejectFlowResult {
26
25
  digest: string;
27
- /** The rejected content, for the CLI's at-reject-time echo (plan §2: the
26
+ /** The rejected content, for the CLI's at-reject-time echo (the
28
27
  * tombstone itself stores no content — this is the only place it's seen
29
28
  * again after this call returns). */
30
29
  content: string;
31
30
  /** Every row removed this call, live or dormant: all whose normalized digest matched (not just the id
32
- * passed, per the K1/R7 duplicate lesson), and each sleep-merged row holding the value, whose other
31
+ * passed, since duplicates share a digest), and each sleep-merged row holding the value, whose other
33
32
  * texts move to a new row: listed in successorIds when it was live, dormantSuccessorIds when dormant. */
34
33
  removedIds: string[];
35
34
  /** Subset of removedIds that were kind='raw' (archived, not deleted). */
@@ -1,6 +1,5 @@
1
1
  /**
2
- * AT1 rejected-value tombstone — shared reject/unreject/list flow.
3
- * docs/plans/2026-08-15-at1-rejected-value-tombstone.md (T2, plan §4).
2
+ * Rejected-value tombstone: shared reject/unreject/list flow.
4
3
  *
5
4
  * The CLI (`hippo reject`/`rejections`/`unreject`) and the Context-based
6
5
  * `api.reject`/`api.unreject`/`api.listRejections` surfaces both need the
@@ -24,16 +23,7 @@ import { deleteEntryCore } from './store/delete-and-batch.js';
24
23
  import { heldTexts } from './same-text.js';
25
24
  import { mergedSuccessor } from './merged-row.js';
26
25
  import { rejectionDigest, normalizeValueForRejection, insertRejectedValue, deleteRejectedValue, listRejectedValues, } from './rejection.js';
27
- /**
28
- * `hippo reject` / `api.reject` core flow. ONE connection, one transaction:
29
- * insert the tombstone, enumerate + remove every live tenant row whose
30
- * normalized digest matches (kind-aware), one aggregate `reject_value`
31
- * audit, COMMIT. Then post-commit (mirrors the existing purge+reaper
32
- * pattern verbatim from api.archiveRaw, api.ts:1913-1938): best-effort
33
- * mirror purge per removed id, `mirror_cleaned_at` stamps for raw ids.
34
- * index.json itself is only refreshed by `rebuildIndex()`.
35
- */
36
- export function rejectValue(opts) {
26
+ function assertRejectOpts(opts) {
37
27
  if (!opts.reason.trim()) {
38
28
  throw new Error('reject requires a non-empty --reason (the tombstone stores no content; reason is its only identity).');
39
29
  }
@@ -41,10 +31,8 @@ export function rejectValue(opts) {
41
31
  throw new Error('reject requires either a memory id or --value.');
42
32
  }
43
33
  if (opts.memoryId !== undefined && opts.value !== undefined) {
44
- // P2 fix: the CLI's flag parser already refuses both forms together;
45
- // the shared flow itself didn't enforce it, so a direct api caller
46
- // passing both silently got the memoryId path with `value` ignored —
47
- // surprising for a caller who thought they were rejecting `value`.
34
+ // Enforced here, not only in the CLI parser, so a direct api caller passing both
35
+ // is refused instead of silently getting the memoryId path with `value` ignored.
48
36
  throw new Error('reject accepts either a memory id or --value, not both.');
49
37
  }
50
38
  if (opts.value !== undefined && normalizeValueForRejection(opts.value).length === 0) {
@@ -53,28 +41,122 @@ export function rejectValue(opts) {
53
41
  // and pollute the listing.
54
42
  throw new Error('reject --value requires non-empty content.');
55
43
  }
56
- const db = openStore(opts.hippoRoot);
57
- try {
58
- let content;
59
- if (opts.memoryId !== undefined) {
60
- // SAFETY: row's shape matches the two columns named in the SELECT above.
61
- const row = db
62
- .prepare(`SELECT content, tenant_id FROM memories WHERE id = ?`)
63
- .get(opts.memoryId);
64
- if (!row || row.tenant_id !== opts.tenantId) {
65
- throw new Error(`memory not found: ${opts.memoryId}`);
66
- }
67
- content = row.content;
44
+ }
45
+ function contentToReject(db, opts) {
46
+ if (opts.memoryId === undefined)
47
+ return opts.value;
48
+ // SAFETY: row's shape matches the two columns named in the SELECT above.
49
+ const row = db
50
+ .prepare(`SELECT content, tenant_id FROM memories WHERE id = ?`)
51
+ .get(opts.memoryId);
52
+ if (!row || row.tenant_id !== opts.tenantId) {
53
+ throw new Error(`memory not found: ${opts.memoryId}`);
54
+ }
55
+ return row.content;
56
+ }
57
+ function removeLiveRows(db, opts, holdsValue, removal) {
58
+ const { removedIds, removedRawIds, successors } = removal;
59
+ const merged = [];
60
+ for (const row of selectAllEntries(db, opts.tenantId)) {
61
+ if (!holdsValue(row.content)) {
62
+ if (heldTexts(row).some(holdsValue))
63
+ merged.push(row);
64
+ continue;
65
+ }
66
+ if (row.kind === 'raw') {
67
+ // Append-only trigger respected — archiveRawMemory is the only
68
+ // legitimate removal path for kind='raw', and its inner SAVEPOINT
69
+ // composes safely inside this BEGIN/COMMIT.
70
+ archiveRawMemory(db, row.id, { reason: opts.reason, who: opts.actor });
71
+ removedRawIds.push(row.id);
68
72
  }
69
73
  else {
70
- content = opts.value;
74
+ // suppressForgetAudit: the aggregate reject_value row below is the
75
+ // trail for these removals, not N individual forget rows.
76
+ deleteEntryCore(db, row.id, { actor: opts.actor, suppressForgetAudit: true });
77
+ }
78
+ removedIds.push(row.id);
79
+ }
80
+ for (const row of merged) {
81
+ const successor = mergedSuccessor(row, holdsValue, new Set(removedIds));
82
+ deleteEntryCore(db, row.id, { actor: opts.actor, suppressForgetAudit: true });
83
+ removedIds.push(row.id);
84
+ if (!successor)
85
+ continue;
86
+ const kept = stampOriginProject(opts.hippoRoot, successor);
87
+ writeEntryDbOnly(db, kept, { actor: opts.actor });
88
+ successors.push(kept);
89
+ }
90
+ }
91
+ // Dormant copies (src/dormant.ts), whole or inside a merged row, go too, in the same transaction: a
92
+ // rejected value may not linger where `hippo dormant restore` could
93
+ // bring it back. They have no markdown mirror, so the post-commit
94
+ // mirror purge below is a no-op for them; they join removedIds for the
95
+ // audit trail and the caller's report.
96
+ function removeDormantCopies(db, tenantId, digest, holdsValue, removal) {
97
+ const { removedIds, dormantSuccessorIds } = removal;
98
+ removedIds.push(...purgeDormantByDigest(db, tenantId, digest));
99
+ for (const dormant of listDormantSnapshots(db, tenantId)) {
100
+ const successor = mergedSuccessor(dormant.entry, holdsValue, new Set(removedIds));
101
+ if (successor === undefined)
102
+ continue;
103
+ removedIds.push(dormant.entry.id);
104
+ if (!successor) {
105
+ deleteDormantRow(db, tenantId, dormant.entry.id);
106
+ continue;
107
+ }
108
+ replaceDormantEntry(db, tenantId, dormant.entry.id, successor);
109
+ dormantSuccessorIds.push(successor.id);
110
+ }
111
+ }
112
+ function auditRejectValue(db, opts, digest, removedIds) {
113
+ try {
114
+ appendAuditEvent(db, {
115
+ tenantId: opts.tenantId,
116
+ actor: opts.actor,
117
+ op: 'reject_value',
118
+ targetId: opts.memoryId,
119
+ metadata: { digest, removedIds, count: removedIds.length },
120
+ });
121
+ }
122
+ catch (error) {
123
+ // Inside the open transaction: the reject commits without its trail row rather than rolling back over bookkeeping.
124
+ reportAuditWriteFailure('reject_value', String(error), opts.memoryId);
125
+ }
126
+ }
127
+ // Post-commit, db handle still open (same pattern as api.archiveRaw):
128
+ // best-effort mirror purge per removed id, reaper-backstop stamp for
129
+ // raw ids.
130
+ function purgeRemovedMirrors(db, hippoRoot, removal) {
131
+ for (const id of removal.removedIds) {
132
+ // purgeMirrorBestEffort retries once, then for non-raw ids (which the
133
+ // reaper never scans) reports the EXPLICIT leftover path(s). See its own doc comment (store.ts, near
134
+ // removeEntryMirrors) for the full rationale.
135
+ const mirrorOk = purgeMirrorBestEffort(hippoRoot, id, removal.removedRawIds.includes(id), 'hippo reject');
136
+ if (mirrorOk && removal.removedRawIds.includes(id)) {
137
+ db.prepare(`UPDATE raw_archive SET mirror_cleaned_at = ? WHERE memory_id = ?`).run(new Date().toISOString(), id);
71
138
  }
139
+ }
140
+ for (const successor of removal.successors)
141
+ writeEntryMirrors(hippoRoot, successor);
142
+ }
143
+ /**
144
+ * `hippo reject` / `api.reject` core flow. ONE connection, one transaction:
145
+ * insert the tombstone, enumerate + remove every live tenant row whose
146
+ * normalized digest matches (kind-aware), one aggregate `reject_value`
147
+ * audit, COMMIT. Then post-commit (mirrors the existing purge+reaper
148
+ * pattern verbatim from api.archiveRaw, api.ts:1913-1938): best-effort
149
+ * mirror purge per removed id, `mirror_cleaned_at` stamps for raw ids.
150
+ * index.json itself is only refreshed by `rebuildIndex()`.
151
+ */
152
+ export function rejectValue(opts) {
153
+ assertRejectOpts(opts);
154
+ const db = openStore(opts.hippoRoot);
155
+ try {
156
+ const content = contentToReject(db, opts);
72
157
  const digest = rejectionDigest(content);
73
158
  const now = new Date().toISOString();
74
- const removedIds = [];
75
- const removedRawIds = [];
76
- const successors = [];
77
- const dormantSuccessorIds = [];
159
+ const removal = { removedIds: [], removedRawIds: [], successors: [], dormantSuccessorIds: [] };
78
160
  db.exec('BEGIN');
79
161
  try {
80
162
  insertRejectedValue(db, {
@@ -90,69 +172,9 @@ export function rejectValue(opts) {
90
172
  // on ~1-5k-row stores — acceptable, documented. A digest column on
91
173
  // memories is the escape if stores grow 100x; not needed now.
92
174
  const holdsValue = (text) => rejectionDigest(text) === digest;
93
- const merged = [];
94
- for (const row of selectAllEntries(db, opts.tenantId)) {
95
- if (!holdsValue(row.content)) {
96
- if (heldTexts(row).some(holdsValue))
97
- merged.push(row);
98
- continue;
99
- }
100
- if (row.kind === 'raw') {
101
- // Append-only trigger respected — archiveRawMemory is the only
102
- // legitimate removal path for kind='raw', and its inner SAVEPOINT
103
- // composes safely inside this BEGIN/COMMIT.
104
- archiveRawMemory(db, row.id, { reason: opts.reason, who: opts.actor });
105
- removedRawIds.push(row.id);
106
- }
107
- else {
108
- // suppressForgetAudit: the aggregate reject_value row below is the
109
- // trail for these removals, not N individual forget rows (plan
110
- // §4, round-3 advisory 2 — mirrors api.ts:1873-1877).
111
- deleteEntryCore(db, row.id, { actor: opts.actor, suppressForgetAudit: true });
112
- }
113
- removedIds.push(row.id);
114
- }
115
- for (const row of merged) {
116
- const successor = mergedSuccessor(row, holdsValue, new Set(removedIds));
117
- deleteEntryCore(db, row.id, { actor: opts.actor, suppressForgetAudit: true });
118
- removedIds.push(row.id);
119
- if (!successor)
120
- continue;
121
- const kept = stampOriginProject(opts.hippoRoot, successor);
122
- writeEntryDbOnly(db, kept, { actor: opts.actor });
123
- successors.push(kept);
124
- }
125
- // Dormant copies (src/dormant.ts), whole or inside a merged row, go too, in the same transaction: a
126
- // rejected value may not linger where `hippo dormant restore` could
127
- // bring it back. They have no markdown mirror, so the post-commit
128
- // mirror purge below is a no-op for them; they join removedIds for the
129
- // audit trail and the caller's report.
130
- removedIds.push(...purgeDormantByDigest(db, opts.tenantId, digest));
131
- for (const dormant of listDormantSnapshots(db, opts.tenantId)) {
132
- const successor = mergedSuccessor(dormant.entry, holdsValue, new Set(removedIds));
133
- if (successor === undefined)
134
- continue;
135
- removedIds.push(dormant.entry.id);
136
- if (!successor) {
137
- deleteDormantRow(db, opts.tenantId, dormant.entry.id);
138
- continue;
139
- }
140
- replaceDormantEntry(db, opts.tenantId, dormant.entry.id, successor);
141
- dormantSuccessorIds.push(successor.id);
142
- }
143
- try {
144
- appendAuditEvent(db, {
145
- tenantId: opts.tenantId,
146
- actor: opts.actor,
147
- op: 'reject_value',
148
- targetId: opts.memoryId,
149
- metadata: { digest, removedIds, count: removedIds.length },
150
- });
151
- }
152
- catch (error) {
153
- // Inside the open transaction: the reject commits without its trail row rather than rolling back over bookkeeping.
154
- reportAuditWriteFailure('reject_value', String(error), opts.memoryId);
155
- }
175
+ removeLiveRows(db, opts, holdsValue, removal);
176
+ removeDormantCopies(db, opts.tenantId, digest, holdsValue, removal);
177
+ auditRejectValue(db, opts, digest, removal.removedIds);
156
178
  db.exec('COMMIT');
157
179
  }
158
180
  catch (err) {
@@ -164,22 +186,8 @@ export function rejectValue(opts) {
164
186
  }
165
187
  throw err;
166
188
  }
167
- // Post-commit, db handle still open (same pattern as api.archiveRaw):
168
- // best-effort mirror purge per removed id, reaper-backstop stamp for
169
- // raw ids.
170
- for (const id of removedIds) {
171
- // AT1 fix: purgeMirrorBestEffort retries once, then — for non-raw ids,
172
- // which cleanupArchivedMirrors' reaper never scans — reports the
173
- // EXPLICIT leftover path(s) instead of the false "will retry via
174
- // reaper" claim. See its own doc comment (store.ts, near
175
- // removeEntryMirrors) for the full rationale.
176
- const mirrorOk = purgeMirrorBestEffort(opts.hippoRoot, id, removedRawIds.includes(id), 'hippo reject');
177
- if (mirrorOk && removedRawIds.includes(id)) {
178
- db.prepare(`UPDATE raw_archive SET mirror_cleaned_at = ? WHERE memory_id = ?`).run(new Date().toISOString(), id);
179
- }
180
- }
181
- for (const successor of successors)
182
- writeEntryMirrors(opts.hippoRoot, successor);
189
+ purgeRemovedMirrors(db, opts.hippoRoot, removal);
190
+ const { removedIds, removedRawIds, successors, dormantSuccessorIds } = removal;
183
191
  return { digest, content, removedIds, removedRawIds, successorIds: successors.map((s) => s.id), dormantSuccessorIds };
184
192
  }
185
193
  finally {
@@ -192,11 +200,8 @@ export function rejectValue(opts) {
192
200
  * (plan §4): no per-write force flag.
193
201
  */
194
202
  export function unrejectValue(hippoRoot, tenantId, digestOrPrefix, actor) {
195
- // P2 fix: an empty/blank prefix startsWith-matches EVERY digest (every
196
- // string starts with ''), which would previously fall through to the
197
- // ambiguous-candidates branch and list the whole tombstone set instead of
198
- // failing loud on the actually-invalid input. Reject before the DB round
199
- // trip.
203
+ // An empty/blank prefix startsWith-matches EVERY digest and would list the whole
204
+ // tombstone set as ambiguous, so reject it before the DB round trip.
200
205
  if (digestOrPrefix.trim().length === 0) {
201
206
  return { status: 'not_found' };
202
207
  }
@@ -1,6 +1,5 @@
1
1
  /**
2
- * AT1 rejected-value tombstone — core invariant.
3
- * docs/plans/2026-08-15-at1-rejected-value-tombstone.md
2
+ * Rejected-value tombstone: core invariant.
4
3
  *
5
4
  * Exact-normalized-value semantics: a human who rejects a fact can refuse
6
5
  * byte-stable re-ingestion of the same value across remember/capture/import/
@@ -64,7 +63,7 @@ export interface RejectedValueRow {
64
63
  export declare function findRejectedValue(db: DatabaseSyncLike, tenantId: string, digest: string): RejectedValueRow | null;
65
64
  /**
66
65
  * Insert (or refresh) a tombstone row. Caller owns the transaction — used by
67
- * the T2 `reject` verb and `resolveConflict`'s `rejectLoserValue` path.
66
+ * the `reject` verb and `resolveConflict`'s `rejectLoserValue` path.
68
67
  */
69
68
  export declare function insertRejectedValue(db: DatabaseSyncLike, opts: {
70
69
  tenantId: string;
@@ -76,11 +75,11 @@ export declare function insertRejectedValue(db: DatabaseSyncLike, opts: {
76
75
  normalizedChars: number;
77
76
  }): void;
78
77
  /**
79
- * Delete a tombstone by tenant + exact digest — the T2 `unreject` verb, the
80
- * only v1 escape hatch (plan §4).
78
+ * Delete a tombstone by tenant + exact digest: the `unreject` verb, the
79
+ * only escape hatch.
81
80
  */
82
81
  export declare function deleteRejectedValue(db: DatabaseSyncLike, tenantId: string, digest: string): boolean;
83
- /** List tombstones for a tenant, newest first — the T2 `rejections` verb. */
82
+ /** List tombstones for a tenant, newest first: the `rejections` verb. */
84
83
  export declare function listRejectedValues(db: DatabaseSyncLike, tenantId: string): RejectedValueRow[];
85
84
  /**
86
85
  * The write-path guard's check helper, called from `upsertEntryRow`
package/dist/rejection.js CHANGED
@@ -1,6 +1,5 @@
1
1
  /**
2
- * AT1 rejected-value tombstone — core invariant.
3
- * docs/plans/2026-08-15-at1-rejected-value-tombstone.md
2
+ * Rejected-value tombstone: core invariant.
4
3
  *
5
4
  * Exact-normalized-value semantics: a human who rejects a fact can refuse
6
5
  * byte-stable re-ingestion of the same value across remember/capture/import/
@@ -80,7 +79,7 @@ export function findRejectedValue(db, tenantId, digest) {
80
79
  }
81
80
  /**
82
81
  * Insert (or refresh) a tombstone row. Caller owns the transaction — used by
83
- * the T2 `reject` verb and `resolveConflict`'s `rejectLoserValue` path.
82
+ * the `reject` verb and `resolveConflict`'s `rejectLoserValue` path.
84
83
  */
85
84
  export function insertRejectedValue(db, opts) {
86
85
  db.prepare(`INSERT INTO rejected_values(tenant_id, digest, reason, rejected_by, rejected_at, source_memory_id, normalized_chars)
@@ -93,14 +92,14 @@ export function insertRejectedValue(db, opts) {
93
92
  normalized_chars = excluded.normalized_chars`).run(opts.tenantId, opts.digest, opts.reason, opts.rejectedBy, opts.rejectedAt, opts.sourceMemoryId ?? null, opts.normalizedChars);
94
93
  }
95
94
  /**
96
- * Delete a tombstone by tenant + exact digest — the T2 `unreject` verb, the
97
- * only v1 escape hatch (plan §4).
95
+ * Delete a tombstone by tenant + exact digest: the `unreject` verb, the
96
+ * only escape hatch.
98
97
  */
99
98
  export function deleteRejectedValue(db, tenantId, digest) {
100
99
  const result = db.prepare(`DELETE FROM rejected_values WHERE tenant_id = ? AND digest = ?`).run(tenantId, digest);
101
100
  return (result.changes ?? 0) > 0;
102
101
  }
103
- /** List tombstones for a tenant, newest first — the T2 `rejections` verb. */
102
+ /** List tombstones for a tenant, newest first: the `rejections` verb. */
104
103
  export function listRejectedValues(db, tenantId) {
105
104
  // SAFETY: rows' shape matches the columns named in the SELECT above.
106
105
  const rows = db
@@ -134,15 +133,8 @@ export function checkRejectionGuard(db, tenantId, entryId, content) {
134
133
  // id the caller is already writing, and the tombstone lookup above is the
135
134
  // tenant-scoped decision. Matches deleteEntry's own by-id SELECT.
136
135
  //
137
- // P2 fix: also read tenant_id. Content-digest-only comparison let a
138
- // same-id upsert that ONLY changes tenantId slip through as an "unchanged
139
- // re-persist" — content C sitting quietly (never rejected) in tenant A
140
- // could be re-tagged into tenant B, and since C's digest already matched
141
- // this row's stored digest, the guard exempted it even though B is the
142
- // tenant that rejected C (that is WHY `tombstone` above is non-null: the
143
- // lookup already ran under the INCOMING/destination tenantId). A tenant
144
- // change on the SAME id is therefore always a content introduction into
145
- // the destination tenant, exactly as if the row were new there.
136
+ // Also read tenant_id: a same-id upsert that only changes tenantId introduces the
137
+ // content into the destination tenant, exactly as if the row were new there.
146
138
  // SAFETY: storedRow's shape matches the two columns named in the SELECT above.
147
139
  const storedRow = db.prepare(`SELECT content, tenant_id FROM memories WHERE id = ?`).get(entryId);
148
140
  const isNewRow = storedRow === undefined;
@@ -1,5 +1,5 @@
1
1
  import type { RerankerFn, RerankProvenance } from './types.js';
2
- import { type JsonValue } from '../http-util.js';
2
+ import type { JsonValue } from '../json.js';
3
3
  /** The two pretrained CLEF decision models served by Cloudflare Workers AI. */
4
4
  export type ClefModel = 'clef-flash' | 'clef';
5
5
  /** True when `name` is one of the CLEF reranker names. */
@@ -16,7 +16,6 @@ export declare function createJevReranker(localFallback: RerankerFn): RerankerFn
16
16
  /** Orders `head` by `scores[i]`, keeping any upstream pre-rerank rank. Shared with CLEF. */
17
17
  export declare function rankByScores(head: readonly SearchResult[], scores: readonly number[]): RerankResult[];
18
18
  /** Track 4 reranker: hosted TypeSafe Jev, opt-in and paid (TYPESAFE_API_KEY), one batched call per recall.
19
- * Any failure warns once and delegates to the local cross-encoder. Scores are not bit-stable run to run.
20
- * Cost, env vars, evidence and limits: docs/evals/2026-09-19-jev-reranker.md. */
19
+ * Any failure warns once and delegates to the local cross-encoder. Scores are not bit-stable run to run. */
21
20
  export declare const jevReranker: RerankerFn;
22
21
  //# sourceMappingURL=jev.d.ts.map
@@ -5,10 +5,10 @@ import { log } from '../log.js';
5
5
  const ENDPOINT = 'https://api.typesafe.ai/v1/systemone';
6
6
  const DEFAULT_TIMEOUT_MS = 5_000;
7
7
  const TRUNCATE_CHARS = 1200;
8
- // Pinned, not `jev-latest`: every number in docs/evals/2026-09-19-jev-reranker.md was
9
- // measured on this version, and the alias moves whenever the vendor ships a release.
8
+ // Pinned, not `jev-latest`: the eval numbers were measured on this version,
9
+ // and the alias moves whenever the vendor ships a release.
10
10
  const DEFAULT_MODEL = 'jev-1.13.0';
11
- // The pool size every number in docs/evals/2026-09-19-jev-reranker.md was measured at.
11
+ // The pool size the eval numbers were measured at.
12
12
  export const JEV_DEFAULT_TOP_K = 40;
13
13
  function truncate(s, n) {
14
14
  return s.length <= n ? s : `${s.slice(0, n)}...`;
@@ -124,7 +124,6 @@ export function rankByScores(head, scores) {
124
124
  return scored;
125
125
  }
126
126
  /** Track 4 reranker: hosted TypeSafe Jev, opt-in and paid (TYPESAFE_API_KEY), one batched call per recall.
127
- * Any failure warns once and delegates to the local cross-encoder. Scores are not bit-stable run to run.
128
- * Cost, env vars, evidence and limits: docs/evals/2026-09-19-jev-reranker.md. */
127
+ * Any failure warns once and delegates to the local cross-encoder. Scores are not bit-stable run to run. */
129
128
  export const jevReranker = createJevReranker(crossEncoderReranker);
130
129
  //# sourceMappingURL=jev.js.map
@@ -4,8 +4,7 @@ import type { RerankerFn } from './types.js';
4
4
  * OpenAI-compatible endpoint. Gated on HIPPO_LLM_RERANKER_URL to prevent
5
5
  * accidental cost.
6
6
  *
7
- * Skeleton only — see docs/plans/2026-05-10-f6-reranker-hardening.md Task 8.
8
- * Full characterisation deferred to a follow-on plan.
7
+ * Skeleton only; full characterisation is deferred.
9
8
  *
10
9
  * Timeout: defaults to 30s; overridable via HIPPO_LLM_RERANKER_TIMEOUT_MS.
11
10
  * On timeout or any fetch failure, the reranker warns once per process and
@@ -7,8 +7,7 @@ const DEFAULT_TIMEOUT_MS = 30_000;
7
7
  * OpenAI-compatible endpoint. Gated on HIPPO_LLM_RERANKER_URL to prevent
8
8
  * accidental cost.
9
9
  *
10
- * Skeleton only — see docs/plans/2026-05-10-f6-reranker-hardening.md Task 8.
11
- * Full characterisation deferred to a follow-on plan.
10
+ * Skeleton only; full characterisation is deferred.
12
11
  *
13
12
  * Timeout: defaults to 30s; overridable via HIPPO_LLM_RERANKER_TIMEOUT_MS.
14
13
  * On timeout or any fetch failure, the reranker warns once per process and
@@ -9,8 +9,7 @@ import type { SearchResult } from '../search/types.js';
9
9
  * Rerankers MUST be deterministic for a given (query, results) input
10
10
  * unless explicitly documented as stochastic (the LLM track, and the
11
11
  * hosted jev reranker, whose scores move slightly run to run).
12
- * Determinism is required for paired A/B and for the workload-validity
13
- * gate in docs/evals/2026-05-10-f6-reranker-prereg.md.
12
+ * Determinism is required for paired A/B and for the workload-validity gate.
14
13
  *
15
14
  * @returns Reordered (and optionally rescaled) results.
16
15
  */