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
@@ -2,10 +2,10 @@ import { type AmbientTallies } from './ambient.js';
2
2
  /** The rows an ambient summary describes: a context read's envelope, origin partition and tag secret veto. */
3
3
  export interface AmbientStoreFilter {
4
4
  exactScope?: string;
5
- /** Rows of this project and user-global rows pass; absent admits every origin. */
6
- project?: string;
7
- /** The reader's project: a secret-tagged row counts only inside its own non-empty origin project. */
8
- currentProject: string;
5
+ /** Rows carrying one of these project names, and user-global rows, pass; absent admits every origin. */
6
+ project?: readonly string[];
7
+ /** The reader's project names: a secret-tagged row counts only inside its own non-empty origin project. */
8
+ currentProject: readonly string[];
9
9
  now: Date;
10
10
  }
11
11
  /** Whole-store ambient tallies in one aggregate pass, no row loaded. Content-pattern secrets and session-local
@@ -3,6 +3,7 @@ import { strengthSql } from './memory.js';
3
3
  import { RECALL_DEFAULT_DENY_SCOPES } from './recall-scope.js';
4
4
  import { SECRET_TAGS } from './secret-detect.js';
5
5
  import { openStore } from './store/open.js';
6
+ import { originInSql } from './project-identity.js';
6
7
  import { isErrorTagged } from './ambient.js';
7
8
  // A malformed or non-array JSON list reads as empty, as parseJsonArray does, instead of failing json_each.
8
9
  const jsonList = (column) => `(CASE WHEN json_valid(${column}) AND json_type(${column}) = 'array' THEN ${column} ELSE '[]' END)`;
@@ -19,8 +20,8 @@ function contextRowsWhere(tenantId, filter) {
19
20
  params.push(...RECALL_DEFAULT_DENY_SCOPES);
20
21
  }
21
22
  if (filter.project !== undefined) {
22
- where.push(`(origin_project = '' OR origin_project = ?)`);
23
- params.push(filter.project);
23
+ where.push(`(origin_project = '' OR ${originInSql(filter.project)})`);
24
+ params.push(...filter.project);
24
25
  }
25
26
  return { where, params };
26
27
  }
@@ -57,7 +58,7 @@ export function loadAmbientTallies(hippoRoot, tenantId, filter) {
57
58
  '[' || COALESCE(group_concat(${jsonList('tags_json')}, ','), '') || ']' AS tagLists
58
59
  FROM memories
59
60
  WHERE ${where.join(' AND ')}
60
- AND ((origin_project = ? AND origin_project != '') OR NOT ${secret.sql})`).get(sevenDaysAgo, ...params, filter.currentProject, ...secret.params);
61
+ AND ((${originInSql(filter.currentProject)} AND origin_project != '') OR NOT ${secret.sql})`).get(sevenDaysAgo, ...params, ...filter.currentProject, ...secret.params);
61
62
  const tagCounts = new Map();
62
63
  let errors = 0;
63
64
  const lists = /* SAFETY: jsonList yields an array per row, joined into one array */ JSON.parse(row.tagLists);
@@ -1,6 +1,7 @@
1
1
  import type { Context } from './types.js';
2
+ export declare const DEFAULT_ASSEMBLE_BUDGET = 4000;
2
3
  export interface AssembleOpts {
3
- /** Token budget. Default 4000. */
4
+ /** Token budget. Default DEFAULT_ASSEMBLE_BUDGET. */
4
5
  budget?: number;
5
6
  /** Recent raw rows always kept verbatim. Default 10. */
6
7
  freshTailCount?: number;
@@ -8,7 +9,7 @@ export interface AssembleOpts {
8
9
  * ancestor. Default true. */
9
10
  summarizeOlder?: boolean;
10
11
  /**
11
- * Restrict to a specific scope. v1.6.1 senior-review P1 #3 parity with
12
+ * Restrict to a specific scope, same rule as
12
13
  * `recall`: when set, exact match required (so an authorised caller can
13
14
  * assemble a `slack:private:CSEC` session by passing scope explicitly).
14
15
  * When undefined, default-deny applies to ANY `<source>:private:*` and
@@ -49,19 +50,15 @@ export interface AssembleResult {
49
50
  items: AssembledContextItem[];
50
51
  tokens: number;
51
52
  /**
52
- * Tenant + scope-filtered raw row count for the session — what the caller
53
- * could have seen given their grant. Pre-v1.6.1 was pre-filter (confusing
54
- * for all-private sessions); pre-v1.6.3 was capped (under-reported on
55
- * sessions > rowCap). v1.6.3 reports the FULL post-filter count via a
56
- * separate COUNT(*) query so consumers can render "session has N msgs"
57
- * accurately even when items[] is the windowed view.
53
+ * Tenant + scope-filtered raw row count for the session, uncapped by `rowCap`, so
54
+ * "session has N msgs" stays accurate when items[] is the windowed view.
58
55
  */
59
56
  totalRaw: number;
60
57
  summarized: number;
61
58
  evicted: number;
62
59
  /**
63
- * True when `rowCap` truncated the loaded window. With v1.6.2's NEWEST-cap
64
- * semantics, the items[] array represents the freshest tail of the session;
60
+ * True when `rowCap` truncated the loaded window. The cap keeps the NEWEST
61
+ * rows, so the items[] array represents the freshest tail of the session;
65
62
  * older rows beyond the cap are silently absent. Use `totalRaw - items.length
66
63
  * - summarized + ...` to estimate how much you didn't see, or widen `rowCap`.
67
64
  */
@@ -2,6 +2,7 @@
2
2
  import { loadEntriesByIds, loadSessionRawMemories, countSessionRawMemories } from '../store/entry-reads.js';
3
3
  import { estimateTokens } from '../token-ledger.js';
4
4
  import { passesScopeFilterForRecall, assertScopeRequestAllowed } from '../recall-scope.js';
5
+ export const DEFAULT_ASSEMBLE_BUDGET = 4000;
5
6
  /**
6
7
  * Build a chronologically-ordered context window for a session. Adapts the
7
8
  * lossless-claw context-engine pattern to Hippo's score-ranked memory store.
@@ -26,7 +27,7 @@ import { passesScopeFilterForRecall, assertScopeRequestAllowed } from '../recall
26
27
  */
27
28
  export function assemble(ctx, sessionId, opts = {}) {
28
29
  assertScopeRequestAllowed(ctx.actor, opts.scope);
29
- const budget = opts.budget ?? 4000;
30
+ const budget = opts.budget ?? DEFAULT_ASSEMBLE_BUDGET;
30
31
  const freshTailCount = opts.freshTailCount ?? 10;
31
32
  const summarizeOlder = opts.summarizeOlder ?? true;
32
33
  const rowCap = opts.rowCap ?? 5000;
@@ -35,17 +36,12 @@ export function assemble(ctx, sessionId, opts = {}) {
35
36
  }
36
37
  const rows = loadSessionRawMemories(ctx.hippoRoot, sessionId, ctx.tenantId, rowCap);
37
38
  const truncated = rows.length === rowCap;
38
- // v1.6.3 senior-review P0-1: report the FULL post-filter row count even
39
- // when the cap windows the loaded set. Pre-v1.6.3 used `scoped.length`
40
- // which under-reported on long sessions and made consumers render
41
- // wrong "session has N msgs" UX.
39
+ // `scoped.length` under-counts a capped session, so totalRaw falls back to a COUNT below.
42
40
  const scoped = rows.filter((r) => passesScopeFilterForRecall(r.scope ?? null, opts.scope));
43
41
  let totalRaw;
44
42
  if (truncated) {
45
- // v1.6.3 codex P1 / senior P0: scope-aware unbounded COUNT. The helper
46
- // SQL-encodes the same default-deny rule passesScopeFilterForRecall
47
- // applies in TS, so a no-scope caller cannot infer private rows by
48
- // comparing totalRaw to items.length on a truncated session.
43
+ // The COUNT applies the same default-deny scope rule in SQL, so a no-scope
44
+ // caller cannot infer private rows by comparing totalRaw to items.length.
49
45
  totalRaw = countSessionRawMemories(ctx.hippoRoot, sessionId, ctx.tenantId, opts.scope);
50
46
  }
51
47
  else {
@@ -59,58 +55,9 @@ export function assemble(ctx, sessionId, opts = {}) {
59
55
  const olderRows = scoped.slice(0, tailStartIdx);
60
56
  const tailRows = scoped.slice(tailStartIdx);
61
57
  // Substitute parent summaries for older rows that share one.
62
- const olderItems = [];
63
- let summarized = 0;
64
- if (summarizeOlder && olderRows.length > 0) {
65
- const olderByParent = new Map();
66
- for (const r of olderRows) {
67
- if (!r.dag_parent_id)
68
- continue;
69
- const list = olderByParent.get(r.dag_parent_id) ?? [];
70
- list.push(r);
71
- olderByParent.set(r.dag_parent_id, list);
72
- }
73
- const eligibleParentIds = Array.from(olderByParent.keys()).filter((pid) => (olderByParent.get(pid)?.length ?? 0) >= 2);
74
- const parents = eligibleParentIds.length > 0
75
- ? loadEntriesByIds(ctx.hippoRoot, eligibleParentIds, ctx.tenantId)
76
- .filter((p) => (p.dag_level ?? 0) === 2 && !p.superseded_by)
77
- .filter((p) => passesScopeFilterForRecall(p.scope ?? null, opts.scope))
78
- : [];
79
- const claimedRawIds = new Set();
80
- for (const parent of parents) {
81
- const claimed = (olderByParent.get(parent.id) ?? []).map((r) => r.id);
82
- claimed.forEach((id) => claimedRawIds.add(id));
83
- olderItems.push({
84
- id: parent.id,
85
- content: parent.content,
86
- createdAt: parent.earliest_at ?? parent.created,
87
- isSummary: true,
88
- substitutedFor: claimed,
89
- strength: parent.strength,
90
- });
91
- summarized += claimed.length;
92
- }
93
- for (const r of olderRows) {
94
- if (claimedRawIds.has(r.id))
95
- continue;
96
- olderItems.push({
97
- id: r.id,
98
- content: r.content,
99
- createdAt: r.created,
100
- strength: r.strength,
101
- });
102
- }
103
- }
104
- else {
105
- for (const r of olderRows) {
106
- olderItems.push({
107
- id: r.id,
108
- content: r.content,
109
- createdAt: r.created,
110
- strength: r.strength,
111
- });
112
- }
113
- }
58
+ const { olderItems, summarized } = summarizeOlder && olderRows.length > 0
59
+ ? substituteSummaries(ctx, olderRows, opts.scope)
60
+ : { olderItems: olderRows.map(rawItem), summarized: 0 };
114
61
  const tailItems = tailRows.map((r) => ({
115
62
  id: r.id,
116
63
  content: r.content,
@@ -118,15 +65,64 @@ export function assemble(ctx, sessionId, opts = {}) {
118
65
  isFreshTail: true,
119
66
  strength: r.strength,
120
67
  }));
121
- // F4 (v1.6.5): byte compare canonical UTC ISO timestamps. ~50× faster than
122
- // localeCompare and chronological by virtue of the timestamp invariant
123
- // documented in src/memory.ts above MemoryEntry.
68
+ // Byte compare is chronological for the fixed-form UTC ISO timestamps
69
+ // (invariant documented in src/memory.ts above MemoryEntry).
124
70
  const cmpIso = (a, b) => (a < b ? -1 : a > b ? 1 : 0);
125
71
  olderItems.sort((a, b) => cmpIso(a.createdAt, b.createdAt));
126
72
  tailItems.sort((a, b) => cmpIso(a.createdAt, b.createdAt));
127
- let items = [...olderItems, ...tailItems];
128
73
  const itemCost = opts.cost?.item ?? ((it) => estimateTokens(it.content));
129
74
  const room = budget - (opts.cost?.fixed(Math.max(budget, totalRaw)) ?? 0);
75
+ const { items, tokens, evicted } = evictToBudget([...olderItems, ...tailItems], itemCost, room);
76
+ return { sessionId, items, tokens, totalRaw, summarized, evicted, truncated };
77
+ }
78
+ function rawItem(r) {
79
+ return {
80
+ id: r.id,
81
+ content: r.content,
82
+ createdAt: r.created,
83
+ strength: r.strength,
84
+ };
85
+ }
86
+ function substituteSummaries(ctx, olderRows, scope) {
87
+ const olderItems = [];
88
+ let summarized = 0;
89
+ const olderByParent = new Map();
90
+ for (const r of olderRows) {
91
+ if (!r.dag_parent_id)
92
+ continue;
93
+ const list = olderByParent.get(r.dag_parent_id) ?? [];
94
+ list.push(r);
95
+ olderByParent.set(r.dag_parent_id, list);
96
+ }
97
+ const eligibleParentIds = Array.from(olderByParent.keys()).filter((pid) => (olderByParent.get(pid)?.length ?? 0) >= 2);
98
+ const parents = eligibleParentIds.length > 0
99
+ ? loadEntriesByIds(ctx.hippoRoot, eligibleParentIds, ctx.tenantId)
100
+ .filter((p) => (p.dag_level ?? 0) === 2 && !p.superseded_by)
101
+ .filter((p) => passesScopeFilterForRecall(p.scope ?? null, scope))
102
+ : [];
103
+ const claimedRawIds = new Set();
104
+ for (const parent of parents) {
105
+ const claimed = (olderByParent.get(parent.id) ?? []).map((r) => r.id);
106
+ claimed.forEach((id) => claimedRawIds.add(id));
107
+ olderItems.push({
108
+ id: parent.id,
109
+ content: parent.content,
110
+ createdAt: parent.earliest_at ?? parent.created,
111
+ isSummary: true,
112
+ substitutedFor: claimed,
113
+ strength: parent.strength,
114
+ });
115
+ summarized += claimed.length;
116
+ }
117
+ for (const r of olderRows) {
118
+ if (claimedRawIds.has(r.id))
119
+ continue;
120
+ olderItems.push(rawItem(r));
121
+ }
122
+ return { olderItems, summarized };
123
+ }
124
+ function evictToBudget(start, itemCost, room) {
125
+ let items = start;
130
126
  let tokens = items.reduce((acc, it) => acc + itemCost(it), 0);
131
127
  let evicted = 0;
132
128
  while (tokens > room && items.length > 0) {
@@ -147,6 +143,6 @@ export function assemble(ctx, sessionId, opts = {}) {
147
143
  tokens -= cost;
148
144
  evicted++;
149
145
  }
150
- return { sessionId, items, tokens, totalRaw, summarized, evicted, truncated };
146
+ return { items, tokens, evicted };
151
147
  }
152
148
  //# sourceMappingURL=assemble.js.map
@@ -10,8 +10,8 @@ export interface AuditListOpts {
10
10
  after?: KeysetPosition;
11
11
  }
12
12
  /**
13
- * Read audit events scoped to `ctx.tenantId`. Read-only — no audit emit (matches
14
- * A5: cmdAuditList does not record a 'recall'-style read event).
13
+ * Read audit events scoped to `ctx.tenantId`. Read-only, no audit emit (matches
14
+ * cmdAuditList, which does not record a 'recall'-style read event).
15
15
  */
16
16
  export declare function auditList(ctx: Context, opts: AuditListOpts): AuditEvent[];
17
17
  //# sourceMappingURL=audit.d.ts.map
package/dist/api/audit.js CHANGED
@@ -2,8 +2,8 @@
2
2
  import { openHippoDb, closeHippoDb } from '../db.js';
3
3
  import { queryAuditEvents } from '../audit.js';
4
4
  /**
5
- * Read audit events scoped to `ctx.tenantId`. Read-only — no audit emit (matches
6
- * A5: cmdAuditList does not record a 'recall'-style read event).
5
+ * Read audit events scoped to `ctx.tenantId`. Read-only, no audit emit (matches
6
+ * cmdAuditList, which does not record a 'recall'-style read event).
7
7
  */
8
8
  export function auditList(ctx, opts) {
9
9
  const db = openHippoDb(ctx.hippoRoot);
@@ -4,8 +4,8 @@ import type { Context } from './types.js';
4
4
  export interface AuthCreateOpts {
5
5
  label?: string;
6
6
  /**
7
- * v1.12.3: authorization role for the new key. Defaults to `'admin'` for
8
- * back-compat with v1.12.0-v1.12.2 (the api_keys.role column DEFAULT also
7
+ * Authorization role for the new key. Defaults to `'admin'` for
8
+ * back-compat with keys minted before roles existed (the api_keys.role column DEFAULT also
9
9
  * resolves to 'admin' if omitted from the INSERT). Member keys are
10
10
  * 403-blocked from admin-gated routes (e.g. `POST /v1/sleep`).
11
11
  */
@@ -15,7 +15,7 @@ export interface AuthCreateResult {
15
15
  keyId: string;
16
16
  plaintext: string;
17
17
  tenantId: string;
18
- /** v1.12.3: the role bound to the new key (admin | member). */
18
+ /** The role bound to the new key (admin | member). */
19
19
  role: 'admin' | 'member';
20
20
  }
21
21
  /**
@@ -36,7 +36,7 @@ export declare function authCreate(ctx: Context, opts: AuthCreateOpts): AuthCrea
36
36
  * Divergence from `cmdAuthList` in src/cli.ts: the CLI today returns ALL keys
37
37
  * regardless of tenant (single-tenant deployments). The API surface is tenant-
38
38
  * scoped because future multi-tenant deployments will share a hippoRoot, and
39
- * tenant A must not see tenant B's keys. Read-only — no audit emit (matches A5).
39
+ * tenant A must not see tenant B's keys. Read-only, so no audit emit.
40
40
  */
41
41
  export declare function authList(ctx: Context, opts: {
42
42
  active: boolean;
@@ -55,7 +55,7 @@ export declare function authListRows(ctx: Context, opts: {
55
55
  * revoke only its own key (checked first), so no caller can probe other key_ids.
56
56
  *
57
57
  * Audit: emits 'auth_revoke' with `tenantId` set to the KEY ROW's tenant_id
58
- * (M1 fix from A5 review, mirrors src/cli.ts:cmdAuthRevoke). Skipped on no-op
58
+ * (mirrors src/cli.ts:cmdAuthRevoke). Skipped on no-op
59
59
  * revoke (already revoked) so re-running doesn't pad the audit log.
60
60
  */
61
61
  export interface AuthRevokeResult {
@@ -72,7 +72,7 @@ export declare function authKeyTenant(ctx: Context, keyId: string): string | und
72
72
  export interface AuthGrantResult {
73
73
  ok: true;
74
74
  }
75
- /** Grant `keyId` read access to one restricted `scope` (ROADMAP Part VIII EI2). Admin only. */
75
+ /** Grant `keyId` read access to one restricted `scope`. Admin only. */
76
76
  export declare function authGrant(ctx: Context, keyId: string, scope: string): AuthGrantResult;
77
77
  /** Revoke `keyId`'s grant on `scope`. Same authorization and lookup rules as authGrant. */
78
78
  export declare function authUngrant(ctx: Context, keyId: string, scope: string): AuthGrantResult;
package/dist/api/auth.js CHANGED
@@ -27,10 +27,8 @@ export function authCreate(ctx, opts) {
27
27
  try {
28
28
  const role = opts.role ?? (ctx.actor.viaAuthResolver ? 'member' : 'admin');
29
29
  const result = createApiKey(db, { tenantId: ctx.tenantId, label: opts.label, role });
30
- // v1.12.4: audit emit (closes the gap v1.12.3 CHANGELOG flagged as deferred).
31
- // Mirrors the auth_revoke pattern at authRevoke — same try/catch so audit
32
- // failure can't crash a successful mint. The plaintext is NEVER logged;
33
- // metadata carries label + role + the keyId (which is non-secret).
30
+ // Same try/catch as authRevoke so audit failure can't crash a successful mint.
31
+ // The plaintext is NEVER logged; metadata carries label + role, keyId is non-secret.
34
32
  try {
35
33
  appendAuditEvent(db, {
36
34
  tenantId: ctx.tenantId,
@@ -59,7 +57,7 @@ export function authCreate(ctx, opts) {
59
57
  * Divergence from `cmdAuthList` in src/cli.ts: the CLI today returns ALL keys
60
58
  * regardless of tenant (single-tenant deployments). The API surface is tenant-
61
59
  * scoped because future multi-tenant deployments will share a hippoRoot, and
62
- * tenant A must not see tenant B's keys. Read-only — no audit emit (matches A5).
60
+ * tenant A must not see tenant B's keys. Read-only, so no audit emit.
63
61
  */
64
62
  export function authList(ctx, opts) {
65
63
  return authListRows(ctx, opts).map((r) => r.key);
@@ -101,7 +99,7 @@ export function authRevoke(ctx, keyId) {
101
99
  if (!alreadyRevoked) {
102
100
  try {
103
101
  appendAuditEvent(db, {
104
- tenantId: row.tenantId, // M1: KEY's tenant, not ctx.tenantId.
102
+ tenantId: row.tenantId, // KEY's tenant, not ctx.tenantId.
105
103
  actor: ctx.actor.subject,
106
104
  op: 'auth_revoke',
107
105
  targetId: keyId,
@@ -134,7 +132,7 @@ export function authKeyTenant(ctx, keyId) {
134
132
  closeHippoDb(db);
135
133
  }
136
134
  }
137
- /** Grant `keyId` read access to one restricted `scope` (ROADMAP Part VIII EI2). Admin only. */
135
+ /** Grant `keyId` read access to one restricted `scope`. Admin only. */
138
136
  export function authGrant(ctx, keyId, scope) {
139
137
  return changeScopeGrant(ctx, keyId, scope, 'auth_grant');
140
138
  }
@@ -0,0 +1,50 @@
1
+ import type { AmbientLoadResult } from '../store/candidates.js';
2
+ import { type MemoryEntry } from '../memory.js';
3
+ import { type HippoConfig } from '../config.js';
4
+ import type { ProjectRef } from '../project-identity.js';
5
+ import type { DeliveryObserver } from '../delivery-recorder.js';
6
+ import type { ContextCost, ContextOpts, ContextResultEntry } from './context-types.js';
7
+ import type { Context } from './types.js';
8
+ /** What getContext resolved from opts and config before it read a row. */
9
+ export interface ContextPlan {
10
+ pinnedOnly: boolean;
11
+ limit: number;
12
+ includeRecent: number;
13
+ activeScope: string;
14
+ exactScope: string | undefined;
15
+ query: string;
16
+ hasLocal: boolean;
17
+ hasGlobal: boolean;
18
+ globalRoot: string;
19
+ primaryIsGlobal: boolean;
20
+ hasLocalTaskState: boolean;
21
+ config: HippoConfig;
22
+ currentProject: ProjectRef;
23
+ includeCrossProject: boolean;
24
+ originProject: readonly string[] | undefined;
25
+ promptRecallPending: boolean;
26
+ cost: ContextCost | undefined;
27
+ price: (entry: MemoryEntry, isGlobal: boolean, promptRecall?: boolean) => number;
28
+ obs: DeliveryObserver | undefined;
29
+ }
30
+ /** The ambient admit rules; `digestHidden` is read late because a prompt-recall eligibility check can still set it. */
31
+ export interface ContextAdmission {
32
+ ambientAdmit: (e: MemoryEntry) => boolean;
33
+ admit: (e: MemoryEntry) => boolean;
34
+ /** What the two-store search admits: `admit` without the own-session compaction rule. */
35
+ bothStoresAdmit: (e: MemoryEntry) => boolean;
36
+ digestHidden: () => boolean;
37
+ }
38
+ export interface ContextPools {
39
+ local: AmbientLoadResult;
40
+ global: AmbientLoadResult;
41
+ }
42
+ export declare function oneCopyPerMemory(local: readonly MemoryEntry[], global: readonly MemoryEntry[], now: Date): [MemoryEntry[], MemoryEntry[]];
43
+ export declare const finiteOr: (v: number, dflt: number, min: number) => number;
44
+ /** Pins plus the prompt-recall or recent-N backfill; null means the block is empty. */
45
+ export declare function selectPinned(opts: ContextOpts, plan: ContextPlan, left: number, pools: ContextPools, admission: ContextAdmission): ContextResultEntry[] | null;
46
+ /** No query: the strongest memories by strength, up to budget. */
47
+ export declare function selectStrongest(plan: ContextPlan, left: number, pools: ContextPools): ContextResultEntry[];
48
+ /** Real query: hybrid search over both stores, or physics/hybrid over the local rows; emits the 'recall' audit row. */
49
+ export declare function selectBySearch(ctx: Context, plan: ContextPlan, left: number, pools: ContextPools, admission: ContextAdmission): Promise<ContextResultEntry[]>;
50
+ //# sourceMappingURL=context-select.d.ts.map