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
package/dist/mcp/tools.js CHANGED
@@ -1,7 +1,10 @@
1
1
  // Tool definitions and input schemas served by tools/list.
2
+ import { DEFAULT_RECALL_BUDGET } from '../search/types.js';
3
+ import { DEFAULT_ASSEMBLE_BUDGET } from '../api/assemble.js';
4
+ import { MAX_ID_LEN } from '../http-util.js';
2
5
  // ── Tool definitions ──
3
- // HTTP sets no budget cap; 25x the 4000 recall default leaves room for large-context clients while bounding one call's work.
4
- const MAX_BUDGET_TOKENS = 100_000;
6
+ // HTTP sets no budget cap; 25x the recall default leaves room for large-context clients while bounding one call's work.
7
+ const MAX_BUDGET_TOKENS = 25 * DEFAULT_RECALL_BUDGET;
5
8
  // Same ceiling as the HTTP list routes' parseListLimit.
6
9
  const MAX_LIST_LIMIT = 1000;
7
10
  export const TOOLS = [
@@ -16,7 +19,7 @@ export const TOOLS = [
16
19
  type: 'number',
17
20
  minimum: 0,
18
21
  maximum: MAX_BUDGET_TOKENS,
19
- description: `Max tokens to return (default: config.defaultBudget, 4000; max ${MAX_BUDGET_TOKENS})`,
22
+ description: `Max tokens to return (default: config.defaultBudget, ${DEFAULT_RECALL_BUDGET}; max ${MAX_BUDGET_TOKENS})`,
20
23
  },
21
24
  include_continuity: {
22
25
  type: 'boolean',
@@ -44,8 +47,8 @@ export const TOOLS = [
44
47
  },
45
48
  session_id: {
46
49
  type: 'string',
47
- maxLength: 256,
48
- description: 'Optional session id (v1.7.4). When set AND (tenant, session) has active goals, applies the dlPFC goal-stack boost to the ranked memories before formatting. Mirrors fresh_tail_session_id shape (256-char cap).',
50
+ maxLength: MAX_ID_LEN,
51
+ description: `Optional session id (v1.7.4). When set AND (tenant, session) has active goals, applies the dlPFC goal-stack boost to the ranked memories before formatting. Mirrors fresh_tail_session_id shape (${MAX_ID_LEN}-char cap).`,
49
52
  },
50
53
  },
51
54
  required: ['query'],
@@ -65,7 +68,7 @@ export const TOOLS = [
65
68
  type: 'number',
66
69
  minimum: 0,
67
70
  maximum: MAX_BUDGET_TOKENS,
68
- description: `Token budget for the assembled context (default 4000; max ${MAX_BUDGET_TOKENS}). Eviction kicks in over budget.`,
71
+ description: `Token budget for the assembled context (default ${DEFAULT_ASSEMBLE_BUDGET}; max ${MAX_BUDGET_TOKENS}). Eviction kicks in over budget.`,
69
72
  },
70
73
  fresh_tail_count: {
71
74
  type: 'number',
@@ -1,20 +1,17 @@
1
1
  /**
2
- * LC2-E2 frozen learned memory-value weight vector.
2
+ * Frozen learned memory-value weight vector.
3
3
  *
4
- * GENERATED FROM the E2 frozen artifact
4
+ * GENERATED FROM the frozen artifact
5
5
  * (benchmarks/memory-value/weights-learned.json +
6
6
  * benchmarks/memory-value/weights-learned.meta.json). NEVER EDIT BY HAND —
7
7
  * tests/memory-value-wiring.test.ts's weights-sync test asserts this constant
8
8
  * equals the committed JSON artifact (value equality + digest match), so
9
9
  * drift between the artifact and this file fails CI.
10
10
  *
11
- * CAVEAT (verbatim from the E2 result doc, carried by design decision D3 /
12
- * binding constraint 4 in docs/plans/2026-08-10-lc2-e3-mv-wiring.md):
13
- * usage-feature signs reflect E1's anti-oracle simulation, NOT real usage
14
- * value. Never read this as production ranking advice — LC3 tests real
15
- * usage value.
11
+ * CAVEAT: usage-feature signs reflect an anti-oracle simulation, NOT real usage
12
+ * value. Never read this as production ranking advice.
16
13
  */
17
- /** The 8 live feature dims the E2 fitter optimized over (FIT_DIMS). */
14
+ /** The 8 live feature dims the fitter optimized over (FIT_DIMS). */
18
15
  export declare const MEMORY_VALUE_WEIGHTS: Readonly<Record<string, number>>;
19
16
  /** sha256 of benchmarks/memory-value/weights-learned.json at freeze time
20
17
  * (weights-learned.meta.json's `weightsFileSha256`). */
@@ -1,20 +1,17 @@
1
1
  /**
2
- * LC2-E2 frozen learned memory-value weight vector.
2
+ * Frozen learned memory-value weight vector.
3
3
  *
4
- * GENERATED FROM the E2 frozen artifact
4
+ * GENERATED FROM the frozen artifact
5
5
  * (benchmarks/memory-value/weights-learned.json +
6
6
  * benchmarks/memory-value/weights-learned.meta.json). NEVER EDIT BY HAND —
7
7
  * tests/memory-value-wiring.test.ts's weights-sync test asserts this constant
8
8
  * equals the committed JSON artifact (value equality + digest match), so
9
9
  * drift between the artifact and this file fails CI.
10
10
  *
11
- * CAVEAT (verbatim from the E2 result doc, carried by design decision D3 /
12
- * binding constraint 4 in docs/plans/2026-08-10-lc2-e3-mv-wiring.md):
13
- * usage-feature signs reflect E1's anti-oracle simulation, NOT real usage
14
- * value. Never read this as production ranking advice — LC3 tests real
15
- * usage value.
11
+ * CAVEAT: usage-feature signs reflect an anti-oracle simulation, NOT real usage
12
+ * value. Never read this as production ranking advice.
16
13
  */
17
- /** The 8 live feature dims the E2 fitter optimized over (FIT_DIMS). */
14
+ /** The 8 live feature dims the fitter optimized over (FIT_DIMS). */
18
15
  export const MEMORY_VALUE_WEIGHTS = Object.freeze({
19
16
  age_days: -0.3245577821391783,
20
17
  half_life_days: 0.11410695580440973,
@@ -1,9 +1,9 @@
1
1
  /**
2
- * LC2-E3 — learned memory-value scorer, wired into the sleep decay pass as a
3
- * rescue-only veto (design D1/D2, docs/plans/2026-08-10-lc2-e3-mv-wiring.md).
2
+ * Learned memory-value scorer, wired into the sleep decay pass as a
3
+ * rescue-only veto.
4
4
  *
5
5
  * computeMvFeatures mirrors benchmarks/memory-value/extract.mjs's
6
- * computeFeatures for the 8 live dims the E2 fitter optimized over
6
+ * computeFeatures for the 8 live dims the fitter optimized over
7
7
  * (FIT_DIMS) — the only dims MEMORY_VALUE_WEIGHTS carries a weight for.
8
8
  * Any future edit to either side must keep them byte-equivalent; the parity
9
9
  * test in tests/memory-value-wiring.test.ts enforces this.
@@ -12,10 +12,10 @@
12
12
  * min-max normalization + weighted scorer (no additional orientation
13
13
  * multiply — the frozen weights already encode sign/orientation).
14
14
  *
15
- * rescueSet implements D1's rescue-only semantics: a condemned entry is
16
- * rescued iff its learned score ranks in the top 30% (RESCUE_BUDGET, the E2
17
- * keep-budget operating point) of its own tenant's non-pinned candidate set
18
- * (D2). Deletes(flag-on) subset Deletes(flag-off) by construction — this
15
+ * rescueSet implements rescue-only semantics: a condemned entry is
16
+ * rescued iff its learned score ranks in the top 30% (RESCUE_BUDGET, the
17
+ * measured keep-budget operating point) of its own tenant's non-pinned
18
+ * candidate set. Deletes(flag-on) subset Deletes(flag-off) by construction — this
19
19
  * function can only ever shrink the condemned set, never grow it.
20
20
  */
21
21
  import { type MemoryEntry } from './memory.js';
@@ -50,13 +50,13 @@ export declare function validateWeights(weights?: Readonly<Record<string, number
50
50
  * Min-max normalize each of the 8 features over the given entry set
51
51
  * (constant feature -> 0, matching evaluate.mjs), then score = dot(weights,
52
52
  * normalized). The normalization context is exactly the entries passed in —
53
- * callers control the bounded scope (D2: per-tenant, non-pinned).
53
+ * callers control the bounded scope (per-tenant, non-pinned).
54
54
  *
55
55
  * `weights` defaults to the real frozen singleton; parameterized (like
56
56
  * validateWeights) so callers/tests can score against an explicit vector
57
57
  * without touching the module singleton.
58
58
  *
59
- * Review-round F2 (non-finite features): Date.parse on a malformed `created`
59
+ * Non-finite features: Date.parse on a malformed `created`
60
60
  * string yields NaN, and NaN would silently corrupt every OTHER entry's
61
61
  * min-max in the same group. An entry with ANY non-finite computed feature
62
62
  * is excluded from the normalization context entirely (its raw values never
@@ -74,13 +74,13 @@ export interface MvRankInfo {
74
74
  score: number;
75
75
  /** 1-based rank by score DESC within the tenant's non-pinned candidate set. */
76
76
  rank: number;
77
- /** Size of the tenant's non-pinned candidate set (D2). */
77
+ /** Size of the tenant's non-pinned candidate set. */
78
78
  totalNonPinned: number;
79
79
  /** ceil(RESCUE_BUDGET * totalNonPinned) — the rescue cutoff; rank <= keepN rescues. */
80
80
  keepN: number;
81
81
  }
82
82
  /**
83
- * Groups non-pinned entries by tenantId (D2), scores + ranks each tenant's
83
+ * Groups non-pinned entries by tenantId, scores + ranks each tenant's
84
84
  * group independently, and returns per-entry rank context for every
85
85
  * non-pinned entry (not just condemned ones) — the shared basis for both
86
86
  * rescueSet's rescue decision and consolidate.ts's audit-row rank context,
@@ -92,7 +92,7 @@ export interface MvRankInfo {
92
92
  */
93
93
  export declare function rankNonPinnedByTenant(entries: MemoryEntry[], now: Date, weights?: Readonly<Record<string, number>>, digest?: string): Map<string, MvRankInfo>;
94
94
  /**
95
- * D1 rescue decision: a condemned entry is rescued iff it ranks in the top
95
+ * Rescue decision: a condemned entry is rescued iff it ranks in the top
96
96
  * 30% of its tenant's non-pinned candidate set by learned score. Returns the
97
97
  * subset of condemnedIds that are rescued — the caller filters commits
98
98
  * (rescued -> survivors) and threads the same set into detectConflicts.
@@ -102,7 +102,7 @@ export declare function rankNonPinnedByTenant(entries: MemoryEntry[], now: Date,
102
102
  * "flag on + a broken constant throws" is directly testable end-to-end
103
103
  * through this function without mutating the frozen module singleton.
104
104
  *
105
- * `precomputedRanks` (round-2 code-review P2-2): when the caller has already
105
+ * `precomputedRanks`: when the caller has already
106
106
  * computed the per-tenant ranking (e.g. consolidate.ts needs it separately
107
107
  * for detail/audit rank context), pass it here to skip the internal
108
108
  * rankNonPinnedByTenant call — the whole-store ranking pass then runs
@@ -1,9 +1,9 @@
1
1
  /**
2
- * LC2-E3 — learned memory-value scorer, wired into the sleep decay pass as a
3
- * rescue-only veto (design D1/D2, docs/plans/2026-08-10-lc2-e3-mv-wiring.md).
2
+ * Learned memory-value scorer, wired into the sleep decay pass as a
3
+ * rescue-only veto.
4
4
  *
5
5
  * computeMvFeatures mirrors benchmarks/memory-value/extract.mjs's
6
- * computeFeatures for the 8 live dims the E2 fitter optimized over
6
+ * computeFeatures for the 8 live dims the fitter optimized over
7
7
  * (FIT_DIMS) — the only dims MEMORY_VALUE_WEIGHTS carries a weight for.
8
8
  * Any future edit to either side must keep them byte-equivalent; the parity
9
9
  * test in tests/memory-value-wiring.test.ts enforces this.
@@ -12,10 +12,10 @@
12
12
  * min-max normalization + weighted scorer (no additional orientation
13
13
  * multiply — the frozen weights already encode sign/orientation).
14
14
  *
15
- * rescueSet implements D1's rescue-only semantics: a condemned entry is
16
- * rescued iff its learned score ranks in the top 30% (RESCUE_BUDGET, the E2
17
- * keep-budget operating point) of its own tenant's non-pinned candidate set
18
- * (D2). Deletes(flag-on) subset Deletes(flag-off) by construction — this
15
+ * rescueSet implements rescue-only semantics: a condemned entry is
16
+ * rescued iff its learned score ranks in the top 30% (RESCUE_BUDGET, the
17
+ * measured keep-budget operating point) of its own tenant's non-pinned
18
+ * candidate set. Deletes(flag-on) subset Deletes(flag-off) by construction — this
19
19
  * function can only ever shrink the condemned set, never grow it.
20
20
  */
21
21
  import { calculateStrength } from './memory.js';
@@ -32,13 +32,13 @@ export const MV_FEATURE_NAMES = [
32
32
  'outcome_ratio',
33
33
  'content_length',
34
34
  ];
35
- /** The E2 keep-budget operating point (the only point with measured
36
- * evidence) — a code constant tied to that evidence, not user-tunable. */
35
+ /** The keep-budget operating point (the only point with measured
36
+ * evidence): a code constant tied to that evidence, not user-tunable. */
37
37
  const RESCUE_BUDGET = 0.3;
38
38
  /**
39
- * Review-round F1 (small-tenant degeneracy): below this per-tenant
40
- * non-pinned candidate-set size, a rank statistic is noise — E2's evidence
41
- * says nothing about tiny scale — and the floor prevents immortal-entry
39
+ * Small-tenant degeneracy: below this per-tenant non-pinned candidate-set
40
+ * size, a rank statistic is noise (the measured evidence says nothing about
41
+ * tiny scale), and the floor prevents immortal-entry
42
42
  * convergence: keepN=ceil(0.3*N) guarantees >=1 rescue at N=1, so without a
43
43
  * floor a condemned-only 1-entry tenant would be rescued every single sleep
44
44
  * forever. A condemned-only tenant below the floor instead drains normally
@@ -108,13 +108,13 @@ export function validateWeights(weights = MEMORY_VALUE_WEIGHTS, digest = SOURCE_
108
108
  * Min-max normalize each of the 8 features over the given entry set
109
109
  * (constant feature -> 0, matching evaluate.mjs), then score = dot(weights,
110
110
  * normalized). The normalization context is exactly the entries passed in —
111
- * callers control the bounded scope (D2: per-tenant, non-pinned).
111
+ * callers control the bounded scope (per-tenant, non-pinned).
112
112
  *
113
113
  * `weights` defaults to the real frozen singleton; parameterized (like
114
114
  * validateWeights) so callers/tests can score against an explicit vector
115
115
  * without touching the module singleton.
116
116
  *
117
- * Review-round F2 (non-finite features): Date.parse on a malformed `created`
117
+ * Non-finite features: Date.parse on a malformed `created`
118
118
  * string yields NaN, and NaN would silently corrupt every OTHER entry's
119
119
  * min-max in the same group. An entry with ANY non-finite computed feature
120
120
  * is excluded from the normalization context entirely (its raw values never
@@ -166,7 +166,7 @@ export function scoreEntries(entries, now, weights = MEMORY_VALUE_WEIGHTS) {
166
166
  return scores;
167
167
  }
168
168
  /**
169
- * Groups non-pinned entries by tenantId (D2), scores + ranks each tenant's
169
+ * Groups non-pinned entries by tenantId, scores + ranks each tenant's
170
170
  * group independently, and returns per-entry rank context for every
171
171
  * non-pinned entry (not just condemned ones) — the shared basis for both
172
172
  * rescueSet's rescue decision and consolidate.ts's audit-row rank context,
@@ -181,11 +181,9 @@ export function rankNonPinnedByTenant(entries, now, weights = MEMORY_VALUE_WEIGH
181
181
  const byTenant = new Map();
182
182
  for (const e of entries) {
183
183
  if (e.pinned)
184
- continue; // D2: pinned entries never compete for rescue (never condemned)
185
- // F9: guard undefined tenantId the same way dag.ts:341 does — the
186
- // MemoryEntry type says `string`, but a raw/legacy row can still carry
187
- // undefined at runtime, and grouping it under the literal key
188
- // "undefined" would silently split it into its own singleton tenant.
184
+ continue; // pinned entries never compete for rescue (never condemned)
185
+ // Default an undefined tenantId as dag.ts:341 does: a raw/legacy row can carry one at runtime,
186
+ // and keying it "undefined" would split it into its own singleton tenant.
189
187
  const tenantId = e.tenantId ?? 'default';
190
188
  const list = byTenant.get(tenantId);
191
189
  if (list)
@@ -196,19 +194,13 @@ export function rankNonPinnedByTenant(entries, now, weights = MEMORY_VALUE_WEIGH
196
194
  const result = new Map();
197
195
  for (const [tenantId, group] of byTenant) {
198
196
  const scores = scoreEntries(group, now, weights);
199
- // score DESC -> compareEntryIdentity (content asc -> metadata -> id asc), the shared
200
- // deterministic tie-break used by every score-primary sort site in this
201
- // codebase (src/compare.ts). F2: `-Infinity - -Infinity` is NaN, not 0 —
202
- // two non-finite-feature entries tied at -Infinity would otherwise fall
203
- // through to `diff` (NaN), which Array.sort treats as "no preference"
204
- // and leaves insertion-order-dependent. Route NaN through the same
205
- // deterministic tie-break as an exact-zero diff.
197
+ // score DESC, then compareEntryIdentity, the shared tie-break of every score-primary sort (src/compare.ts).
198
+ // `-Infinity - -Infinity` is NaN, which Array.sort leaves insertion-order-dependent, so NaN ties too.
206
199
  const sorted = [...group].sort((a, b) => {
207
200
  const diff = scores.get(b.id) - scores.get(a.id);
208
201
  return diff === 0 || Number.isNaN(diff) ? compareEntryIdentity(a, b) : diff;
209
202
  });
210
- // F1: tenants smaller than MIN_RESCUE_GROUP never rescue (keepN 0) — see
211
- // that constant's doc comment.
203
+ // Tenants smaller than MIN_RESCUE_GROUP never rescue (keepN 0); see that constant's doc comment.
212
204
  const keepN = sorted.length < MIN_RESCUE_GROUP
213
205
  ? 0
214
206
  : Math.min(sorted.length, Math.ceil(RESCUE_BUDGET * sorted.length));
@@ -225,7 +217,7 @@ export function rankNonPinnedByTenant(entries, now, weights = MEMORY_VALUE_WEIGH
225
217
  return result;
226
218
  }
227
219
  /**
228
- * D1 rescue decision: a condemned entry is rescued iff it ranks in the top
220
+ * Rescue decision: a condemned entry is rescued iff it ranks in the top
229
221
  * 30% of its tenant's non-pinned candidate set by learned score. Returns the
230
222
  * subset of condemnedIds that are rescued — the caller filters commits
231
223
  * (rescued -> survivors) and threads the same set into detectConflicts.
@@ -235,7 +227,7 @@ export function rankNonPinnedByTenant(entries, now, weights = MEMORY_VALUE_WEIGH
235
227
  * "flag on + a broken constant throws" is directly testable end-to-end
236
228
  * through this function without mutating the frozen module singleton.
237
229
  *
238
- * `precomputedRanks` (round-2 code-review P2-2): when the caller has already
230
+ * `precomputedRanks`: when the caller has already
239
231
  * computed the per-tenant ranking (e.g. consolidate.ts needs it separately
240
232
  * for detail/audit rank context), pass it here to skip the internal
241
233
  * rankNonPinnedByTenant call — the whole-store ranking pass then runs
@@ -243,16 +235,13 @@ export function rankNonPinnedByTenant(entries, now, weights = MEMORY_VALUE_WEIGH
243
235
  * computes it internally as before — existing callers/tests are unaffected.
244
236
  */
245
237
  export function rescueSet(entries, condemnedIds, now, weights = MEMORY_VALUE_WEIGHTS, digest = SOURCE_ARTIFACT_SHA256, precomputedRanks) {
246
- validateWeights(weights, digest); // fail loud before any rescue computation (constraint 5)
238
+ validateWeights(weights, digest); // fail loud before any rescue computation
247
239
  const ranked = precomputedRanks ?? rankNonPinnedByTenant(entries, now, weights, digest);
248
240
  const rescued = new Set();
249
241
  for (const id of condemnedIds) {
250
242
  const info = ranked.get(id);
251
- // F2: Number.isFinite(info.score) is an explicit, absolute guard — not
252
- // just reliance on -Infinity naturally sorting last. In the degenerate
253
- // case where every entry in a tenant is non-finite-scored (a tie at
254
- // -Infinity), rank position alone could otherwise place one inside
255
- // keepN; this makes "never rescued" hold regardless.
243
+ // An explicit finite guard, not just -Infinity sorting last: when a whole tenant
244
+ // ties at -Infinity, rank alone could place one inside keepN.
256
245
  if (info && info.rank <= info.keepN && Number.isFinite(info.score))
257
246
  rescued.add(id);
258
247
  }
package/dist/memory.d.ts CHANGED
@@ -30,8 +30,8 @@ export type MemoryKind = 'raw' | 'distilled' | 'superseded' | 'archived';
30
30
  *
31
31
  * Byte-comparison sort (`<` / `>`) is chronological for any pair of
32
32
  * canonical UTC ISO strings. ~50× faster than `localeCompare` with no
33
- * semantic gain. F4 (v1.6.5) uses byte compare on `assemble`; if a future
34
- * import path admits non-canonical timestamps, the F4 sort and any
33
+ * semantic gain. `assemble` sorts by byte compare; if a future
34
+ * import path admits non-canonical timestamps, that sort and any
35
35
  * downstream chronological reasoning will need a normalization pass.
36
36
  */
37
37
  export interface MemoryEntry {
@@ -65,18 +65,18 @@ export interface MemoryEntry {
65
65
  descendant_count?: number;
66
66
  earliest_at?: string | null;
67
67
  latest_at?: string | null;
68
- /** v28: 1 when this summary row has at least one child invalidated,
68
+ /** 1 when this summary row has at least one child invalidated,
69
69
  * superseded, forgotten, or archived since it was last rebuilt. Cleared
70
- * by E3's rebuildDirtySummaries during sleep. Always 0 for non-summary
71
- * rows (dag_level !== 2; E5 widens to include 3). */
70
+ * by rebuildDirtySummaries during sleep. Always 0 for non-summary
71
+ * rows (dag_level !== 2). */
72
72
  summary_dirty?: 0 | 1;
73
- /** v28: ISO 8601 timestamp of the last successful rebuild for this
73
+ /** ISO 8601 timestamp of the last successful rebuild for this
74
74
  * summary, or null if never rebuilt. */
75
75
  last_rebuilt_at?: string | null;
76
- /** v28: monotonically-increasing counter of successful rebuilds for this
77
- * summary. 0 for initial buildDag write; bumped by E3. */
76
+ /** Monotonically-increasing counter of successful rebuilds for this
77
+ * summary. 0 for initial buildDag write; bumped by each rebuild. */
78
78
  rebuild_count?: number;
79
- /** v28 (reserved for E5): ISO 8601 timestamp the level-3 entity profile
79
+ /** Reserved: ISO 8601 timestamp the level-3 entity profile
80
80
  * was built. Only ever populated on dag_level=3 rows. */
81
81
  dag_level_3_built_at?: string | null;
82
82
  kind: MemoryKind;
@@ -85,16 +85,16 @@ export interface MemoryEntry {
85
85
  artifact_ref: string | null;
86
86
  tenantId: string;
87
87
  /**
88
- * Memory scope isolation (schema v39): owning project for ambient-context
88
+ * Memory scope isolation: owning project for ambient-context
89
89
  * partitioning. A lowercased project name, '' for user-global (injectable
90
- * everywhere), or null for legacy pre-v39 rows - ambient context treats
91
- * null as other-project (deny). Stamped from the store's location at write
92
- * time (store.ts stampOriginProject); undefined only on entries not yet
93
- * written. See docs/plans/2026-07-01-memory-scope-isolation.md.
90
+ * everywhere), or null for legacy rows written before the column - ambient
91
+ * context treats null as other-project (deny). Stamped from the store's location
92
+ * at write time (store.ts stampOriginProject); undefined only on entries not yet
93
+ * written.
94
94
  */
95
95
  origin_project?: string | null;
96
96
  /**
97
- * F1 (v1.7.0): raw SQLite FTS5 bm25() score from the FTS path of
97
+ * Raw SQLite FTS5 bm25() score from the FTS path of
98
98
  * `loadSearchEntries`.
99
99
  *
100
100
  * Populated ONLY when ALL of the following hold:
@@ -113,7 +113,7 @@ export interface MemoryEntry {
113
113
  */
114
114
  bm25_score?: number;
115
115
  }
116
- /** FE2: tag on a memory whose named file/symbol/script changed after it was stored. */
116
+ /** Tag on a memory whose named file/symbol/script changed after it was stored. */
117
117
  export declare const CHURN_STALE_TAG = "churn-stale";
118
118
  /**
119
119
  * Test-only helper. Tests that mutate `process.env.HIPPO_LOSS_AVERSION_RATIO`
@@ -144,9 +144,9 @@ export declare function calculateRewardFactor(entry: Pick<MemoryEntry, 'outcome_
144
144
  export declare function netWrong(entry: Pick<MemoryEntry, 'outcome_positive' | 'outcome_negative'>): number;
145
145
  /**
146
146
  * Options for decay basis.
147
- * - clock: wall-clock time (default pre-v0.15)
147
+ * - clock: wall-clock time (former default)
148
148
  * - session: decay by sleep cycle count (for intermittent agents)
149
- * - adaptive: auto-scale half-life by session frequency (default v0.15+)
149
+ * - adaptive: auto-scale half-life by session frequency (default)
150
150
  */
151
151
  /** What calculateStrength reads, so a caller can score a row without loading its text. */
152
152
  export type StrengthInputs = Pick<MemoryEntry, 'pinned' | 'created' | 'last_retrieved' | 'half_life_days' | 'retrieval_count' | 'emotional_valence' | 'outcome_positive' | 'outcome_negative'>;
@@ -211,10 +211,8 @@ export declare function confidenceLabel(entry: MemoryEntry, now?: Date): {
211
211
  export declare function resolveConfidence(entry: MemoryEntry, now?: Date): ConfidenceLevel;
212
212
  /**
213
213
  * Base half-life for a new memory, in days, before `deriveHalfLife`'s
214
- * write-time multipliers. 365 since 1.46.0: the pre-registered E1 decision
215
- * (docs/evals/2026-09-24-decay-default-result.md and prereg-2) found 7 days
216
- * lost the current fact far more often (29% vs 75% in the top five), and
217
- * 730 days and decay off tied with 365. `hippo sleep` moves memories still
214
+ * write-time multipliers. 365 because 7 days lost the current fact far more
215
+ * often, and 730 days or no decay did no better. `hippo sleep` moves memories still
218
216
  * on an older base (src/half-life-migration.ts).
219
217
  */
220
218
  export declare const DEFAULT_HALF_LIFE_DAYS = 365;
package/dist/memory.js CHANGED
@@ -14,19 +14,10 @@ export var Layer;
14
14
  Layer["Semantic"] = "semantic";
15
15
  Layer["Trace"] = "trace";
16
16
  })(Layer || (Layer = {}));
17
- /** FE2: tag on a memory whose named file/symbol/script changed after it was stored. */
17
+ /** Tag on a memory whose named file/symbol/script changed after it was stored. */
18
18
  export const CHURN_STALE_TAG = 'churn-stale';
19
- // Emotional multipliers from PLAN.md.
20
- //
21
- // v1.13.5 / J5 loss-aversion calibration (Lovallo-Kahneman TFAS empirics:
22
- // losses ~2x larger than equivalent gains). Defaults rebalanced:
23
- // - positive (success-tagged): 1.3 -> 1.0
24
- // - negative (error-tagged): 1.5 -> 2.0
25
- // - critical stays at 2.0 (literal roadmap reading; J5 silent on critical;
26
- // ranking signal in consolidate.ts/salience.ts/ambient.ts unchanged)
27
- // - neutral stays at 1.0
28
- //
29
- // `negative` is further scaled per-process by HIPPO_LOSS_AVERSION_RATIO
19
+ // Emotional multipliers from PLAN.md. Losses weigh ~2x equivalent gains (Lovallo-Kahneman TFAS empirics),
20
+ // so negative is 2.0. `negative` is further scaled per-process by HIPPO_LOSS_AVERSION_RATIO
30
21
  // (env var, default 1.0; see getLossAversionRatio + applyLossAversionRatio).
31
22
  const EMOTIONAL_MULTIPLIERS = {
32
23
  neutral: 1.0,
@@ -35,7 +26,7 @@ const EMOTIONAL_MULTIPLIERS = {
35
26
  critical: 2.0,
36
27
  };
37
28
  /**
38
- * v1.13.5 / J5 — module-level lazy-cached read of HIPPO_LOSS_AVERSION_RATIO.
29
+ * Module-level lazy-cached read of HIPPO_LOSS_AVERSION_RATIO.
39
30
  *
40
31
  * `calculateStrength` is called per-entry inside hot recall loops
41
32
  * (api.ts/consolidate.ts/search.ts), so a per-call `process.env` lookup
@@ -44,20 +35,19 @@ const EMOTIONAL_MULTIPLIERS = {
44
35
  * Test isolation via `_resetLossAversionRatioCacheForTests()` below.
45
36
  */
46
37
  /**
47
- * v1.13.5 minimum acceptable ratio. Below this, the negative multiplier
38
+ * Minimum acceptable ratio. Below this, the negative multiplier
48
39
  * (2.0 * ratio) becomes small enough that calculateStrength * decay can fall
49
40
  * below `DECAY_THRESHOLD = 0.05` in `src/consolidate.ts:146`, which would
50
41
  * permanently delete non-pinned error-tagged memories on the next sleep
51
- * cycle. 0.5 is chosen as the floor because (a) it recovers the v1.13.4
42
+ * cycle. 0.5 is chosen as the floor because (a) it recovers the pre-calibration
52
43
  * effective multiplier (2.0 * 0.5 = 1.0 + the negative premium, i.e. 1.5x
53
- * the v1.13.4 default), and (b) below this the user is asking for LESS
44
+ * the pre-calibration default), and (b) below this the user is asking for LESS
54
45
  * loss aversion than has ever shipped — that's outside the supported
55
- * tuning range. See codex-review-critic round 1 P1.
46
+ * tuning range.
56
47
  */
57
48
  const LOSS_AVERSION_RATIO_MIN = 0.5;
58
49
  /**
59
- * Validation policy (v1.13.5 + independent-review round-1 HIGH + codex
60
- * round-1 P1 folds):
50
+ * Validation policy:
61
51
  * - Valid: finite numbers >= 0.5.
62
52
  * - Invalid (silent fallback to 1.0): empty string, non-numeric,
63
53
  * numbers below 0.5 (including 0 and negatives), NaN, +/-Infinity.
@@ -65,17 +55,16 @@ const LOSS_AVERSION_RATIO_MIN = 0.5;
65
55
  * on a typo.
66
56
  *
67
57
  * Why the 0.5 floor and not 0:
68
- * - codex-review-critic round 1 P1: rejecting only `0` (the original
69
- * HIGH fold) leaves the same silent data-loss surface for any ratio
58
+ * - Rejecting only `0` leaves the same silent data-loss surface for any ratio
70
59
  * below ~0.025 (and worse for aged memories, where even ratio=0.25
71
60
  * can produce strength < DECAY_THRESHOLD = 0.05 in consolidate.ts).
72
- * Floor at the v1.13.4-equivalent (0.5) so the env var's tuning
61
+ * Floor at the pre-calibration equivalent (0.5) so the env var's tuning
73
62
  * range never crosses into the deletion regime.
74
- * - Users wanting LESS loss aversion than v1.13.4's 1.5 multiplier
75
- * should reconsider the design intent of J5 (the calibration was
63
+ * - Users wanting LESS loss aversion than the pre-calibration 1.5 multiplier
64
+ * should reconsider the design intent (the calibration was
76
65
  * toward MORE loss aversion, not less). If a future use case
77
66
  * genuinely needs ratio < 0.5, the right path is a separate
78
- * `HIPPO_NEGATIVE_MULTIPLIER` env override (deferred to J5-v2).
67
+ * `HIPPO_NEGATIVE_MULTIPLIER` env override.
79
68
  */
80
69
  let _lossAversionRatioCache;
81
70
  function getLossAversionRatio() {
@@ -111,8 +100,7 @@ export function _resetLossAversionRatioCacheForTests() {
111
100
  /**
112
101
  * Apply the loss-aversion ratio scalar to the `negative` multiplier ONLY.
113
102
  * Other valences (positive, critical, neutral) pass through unchanged.
114
- * `critical` is deliberately NOT scaled: J5 roadmap is silent on critical;
115
- * its multiplier is left alone so the calibration only touches the
103
+ * `critical` is deliberately NOT scaled, so the calibration only touches the
116
104
  * specific empirical claim (TFAS 2x losses-vs-gains).
117
105
  */
118
106
  function applyLossAversionRatio(valence, baseMultiplier) {
@@ -172,13 +160,9 @@ now = evalNow(), options = {}) {
172
160
  const wrongPenalty = Math.pow(0.5, Math.min(netWrong(entry), MAX_WRONG_HALVINGS));
173
161
  if (entry.pinned)
174
162
  return wrongPenalty;
175
- // EVAL-ONLY ablation (see ablation.ts): with recall-strengthening ablated,
176
- // anchor decay at CREATION, not last_retrieved. A never-strengthened memory
177
- // decays from when it was made; using last_retrieved would let clock resets
178
- // persisted by PRIOR unflagged runs leak strengthening into an ablated
179
- // arm's rankings (codex P2). Identity on fresh stores (created ==
180
- // last_retrieved at write). Prior-run half_life increments are NOT
181
- // reconstructed - see the ablation.ts caveat (fresh stores per arm).
163
+ // EVAL-ONLY ablation (see ablation.ts): anchor decay at CREATION, so clock resets persisted by PRIOR
164
+ // unflagged runs cannot leak strengthening into an ablated arm. Prior-run half_life increments are
165
+ // NOT reconstructed - see the ablation.ts caveat (fresh stores per arm).
182
166
  const lastRetrieved = new Date(isRecallBoostAblated() ? entry.created : entry.last_retrieved);
183
167
  const daysSince = (now.getTime() - lastRetrieved.getTime()) / (1000 * 60 * 60 * 24);
184
168
  // Reward-proportional half-life modulation
@@ -212,19 +196,13 @@ now = evalNow(), options = {}) {
212
196
  // clamp below then caps retrievalBoost at baseline - see ablation.ts
213
197
  // formula note.
214
198
  const decay = isDecayAblated() ? 1.0 : Math.pow(0.5, decayExponent);
215
- // Retrieval boost: 1 + 0.1 * log2(retrieval_count + 1)
216
- // EVAL-ONLY ablation (see ablation.ts): the recall-boost flag neutralizes
217
- // the READ side too, so a store with PRIOR retrieval history (counts > 0
218
- // written before the flag was set) does not leak strengthening into an
219
- // ablated arm's rankings (codex P2).
199
+ // Retrieval boost: 1 + 0.1 * log2(retrieval_count + 1). EVAL-ONLY ablation (see ablation.ts) neutralizes
200
+ // the READ side too, so counts written before the flag cannot leak strengthening into an ablated arm.
220
201
  const retrievalBoost = isRecallBoostAblated() || netWrong(entry) > 0
221
202
  ? 1.0
222
203
  : 1 + 0.1 * Math.log2(entry.retrieval_count + 1);
223
- // Emotional multiplier
224
- // v1.13.5 / J5: apply HIPPO_LOSS_AVERSION_RATIO to the negative multiplier
225
- // ONLY (positive/critical/neutral pass through unchanged). Lazy module-cache
226
- // means this is a single Map lookup + one numeric multiply, not a per-call
227
- // process.env read.
204
+ // Emotional multiplier. HIPPO_LOSS_AVERSION_RATIO scales the negative one ONLY; the lazy
205
+ // module cache makes this one lookup + one multiply, not a per-call process.env read.
228
206
  const baseMultiplier = EMOTIONAL_MULTIPLIERS[entry.emotional_valence] ?? 1.0;
229
207
  const emotionalMultiplier = applyLossAversionRatio(entry.emotional_valence, baseMultiplier);
230
208
  const raw = decay * retrievalBoost * emotionalMultiplier;
@@ -335,10 +313,8 @@ export function resolveConfidence(entry, now = evalNow()) {
335
313
  }
336
314
  /**
337
315
  * Base half-life for a new memory, in days, before `deriveHalfLife`'s
338
- * write-time multipliers. 365 since 1.46.0: the pre-registered E1 decision
339
- * (docs/evals/2026-09-24-decay-default-result.md and prereg-2) found 7 days
340
- * lost the current fact far more often (29% vs 75% in the top five), and
341
- * 730 days and decay off tied with 365. `hippo sleep` moves memories still
316
+ * write-time multipliers. 365 because 7 days lost the current fact far more
317
+ * often, and 730 days or no decay did no better. `hippo sleep` moves memories still
342
318
  * on an older base (src/half-life-migration.ts).
343
319
  */
344
320
  export const DEFAULT_HALF_LIFE_DAYS = 365;
@@ -1,5 +1,5 @@
1
1
  import type { MemoryEntry } from './memory.js';
2
- import type { ResultCost, SearchResult } from './search/types.js';
2
+ import { type ResultCost, type SearchResult } from './search/types.js';
3
3
  export declare function multihopSearch(query: string, entries: MemoryEntry[], options?: {
4
4
  budget?: number;
5
5
  now?: Date;
package/dist/multihop.js CHANGED
@@ -1,7 +1,8 @@
1
1
  import { fitBudget } from './search/finalize.js';
2
2
  import { search } from './search/bm25-search.js';
3
+ import { DEFAULT_RECALL_BUDGET } from './search/types.js';
3
4
  export function multihopSearch(query, entries, options = {}) {
4
- const budget = options.budget ?? 4000;
5
+ const budget = options.budget ?? DEFAULT_RECALL_BUDGET;
5
6
  // Pass 1 searches wide to find entities, so each return fits the caller's budget, as search() does.
6
7
  const fit = (ordered) => fitBudget(ordered, budget, options.minResults ?? 1, options.cost);
7
8
  const pass1 = search(query, entries, { ...options, budget: budget * 2 });
@@ -31,7 +32,7 @@ export function multihopSearch(query, entries, options = {}) {
31
32
  merged.set(r.entry.id, r);
32
33
  }
33
34
  }
34
- // T2 note: PLAIN stable score sort on purpose -- pass1/pass2 inputs are
35
+ // PLAIN stable score sort on purpose -- pass1/pass2 inputs are
35
36
  // deterministically ordered (search() carries the content tail), stability
36
37
  // inherits that, and ties keep pass-1 results ahead of pass-2 follow-ups.
37
38
  return fit([...merged.values()].sort((a, b) => b.score - a.score));
@@ -1,14 +1,13 @@
1
1
  /**
2
- * --owner format validation (B2 v1.12.6).
2
+ * --owner format validation.
3
3
  *
4
4
  * Documented MEMORY_ENVELOPE.md contract: owner = `user:<id>` | `agent:<id>`
5
- * with id ∈ `[A-Za-z0-9_-]+`. Pre-v1.12.6 any string was accepted, leaving
6
- * the documented contract unenforced.
5
+ * with id ∈ `[A-Za-z0-9_-]+`.
7
6
  *
8
7
  * Default: WARN-ONLY (log + accept) to preserve back-compat with existing
9
8
  * scripted callers passing legacy owner strings. Set `HIPPO_STRICT_OWNER=1`
10
- * to reject + exit. Strict mode will become the default once A5 v2 lands
11
- * (see `TODOS.md` A3 follow-ups for the migration path).
9
+ * to reject + exit. Strict mode will become the default once real auth replaces
10
+ * the stub (see `TODOS.md` for the migration path).
12
11
  */
13
12
  export declare const OWNER_RE: RegExp;
14
13
  export declare const OWNER_CONTRACT_HINT = "Must match ^(user|agent):[A-Za-z0-9_-]+$ (e.g. user:alice, agent:capture-bot).";
@@ -1,14 +1,13 @@
1
1
  /**
2
- * --owner format validation (B2 v1.12.6).
2
+ * --owner format validation.
3
3
  *
4
4
  * Documented MEMORY_ENVELOPE.md contract: owner = `user:<id>` | `agent:<id>`
5
- * with id ∈ `[A-Za-z0-9_-]+`. Pre-v1.12.6 any string was accepted, leaving
6
- * the documented contract unenforced.
5
+ * with id ∈ `[A-Za-z0-9_-]+`.
7
6
  *
8
7
  * Default: WARN-ONLY (log + accept) to preserve back-compat with existing
9
8
  * scripted callers passing legacy owner strings. Set `HIPPO_STRICT_OWNER=1`
10
- * to reject + exit. Strict mode will become the default once A5 v2 lands
11
- * (see `TODOS.md` A3 follow-ups for the migration path).
9
+ * to reject + exit. Strict mode will become the default once real auth replaces
10
+ * the stub (see `TODOS.md` for the migration path).
12
11
  */
13
12
  import { processEnv } from './env.js';
14
13
  export const OWNER_RE = /^(user|agent):[A-Za-z0-9_-]+$/;