hippo-memory 1.62.0 → 1.63.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (370) hide show
  1. package/README.md +9 -0
  2. package/dist/agent-memories/apply.d.ts +2 -0
  3. package/dist/agent-memories/apply.js +23 -1
  4. package/dist/agent-memories/claude-code.d.ts +2 -1
  5. package/dist/agent-memories/claude-code.js +4 -4
  6. package/dist/agent-memories/report.d.ts +1 -0
  7. package/dist/agent-memories/report.js +2 -2
  8. package/dist/agent-memories/sync.js +37 -23
  9. package/dist/ambient-store.d.ts +4 -4
  10. package/dist/ambient-store.js +4 -3
  11. package/dist/api/assemble.d.ts +7 -10
  12. package/dist/api/assemble.js +7 -12
  13. package/dist/api/audit.d.ts +2 -2
  14. package/dist/api/audit.js +2 -2
  15. package/dist/api/auth.d.ts +6 -6
  16. package/dist/api/auth.js +5 -7
  17. package/dist/api/context-select.d.ts +4 -3
  18. package/dist/api/context-select.js +9 -11
  19. package/dist/api/context-types.d.ts +12 -17
  20. package/dist/api/context.d.ts +3 -2
  21. package/dist/api/context.js +20 -20
  22. package/dist/api/dormant.js +1 -1
  23. package/dist/api/drill-down.d.ts +4 -4
  24. package/dist/api/drill-down.js +22 -10
  25. package/dist/api/outcome.d.ts +8 -13
  26. package/dist/api/outcome.js +13 -17
  27. package/dist/api/promote.d.ts +5 -8
  28. package/dist/api/quarantine.js +3 -2
  29. package/dist/api/recall-types.d.ts +43 -55
  30. package/dist/api/recall.d.ts +3 -3
  31. package/dist/api/recall.js +7 -18
  32. package/dist/api/remember.d.ts +2 -2
  33. package/dist/api/sleep.d.ts +7 -35
  34. package/dist/api/sleep.js +3 -2
  35. package/dist/api/tokens.d.ts +2 -2
  36. package/dist/api/tokens.js +2 -2
  37. package/dist/api/types.d.ts +6 -10
  38. package/dist/api/types.js +2 -4
  39. package/dist/audit-prune.d.ts +4 -6
  40. package/dist/audit-prune.js +3 -5
  41. package/dist/audit.js +11 -33
  42. package/dist/auth.d.ts +8 -9
  43. package/dist/auth.js +5 -7
  44. package/dist/autolearn.d.ts +1 -1
  45. package/dist/autolearn.js +1 -1
  46. package/dist/availability.js +3 -5
  47. package/dist/capture/command.d.ts +5 -4
  48. package/dist/capture/command.js +8 -22
  49. package/dist/capture/compact.d.ts +1 -1
  50. package/dist/capture/compact.js +9 -13
  51. package/dist/capture/extract.js +37 -115
  52. package/dist/capture-error.d.ts +1 -1
  53. package/dist/capture-error.js +1 -1
  54. package/dist/churn-git.d.ts +1 -1
  55. package/dist/churn-git.js +1 -1
  56. package/dist/cli/audit.js +3 -4
  57. package/dist/cli/auth.js +3 -6
  58. package/dist/cli/curate.d.ts +1 -1
  59. package/dist/cli/curate.js +8 -24
  60. package/dist/cli/dag.js +5 -9
  61. package/dist/cli/decisions.js +10 -21
  62. package/dist/cli/explain.js +2 -1
  63. package/dist/cli/goals.js +1 -1
  64. package/dist/cli/init.js +1 -1
  65. package/dist/cli/playbooks.js +4 -9
  66. package/dist/cli/projects.js +3 -1
  67. package/dist/cli/recall.js +2 -1
  68. package/dist/cli/remember.js +6 -18
  69. package/dist/cli/session-hooks.js +21 -40
  70. package/dist/cli/setup.d.ts +1 -1
  71. package/dist/cli/setup.js +2 -6
  72. package/dist/cli/shared.js +3 -3
  73. package/dist/cli/slack.js +1 -1
  74. package/dist/cli/sleep.js +27 -1
  75. package/dist/cli/status.d.ts +4 -4
  76. package/dist/cli/status.js +7 -12
  77. package/dist/cli/transfer.js +8 -13
  78. package/dist/cli/usage.js +9 -6
  79. package/dist/cli.d.ts +1 -1
  80. package/dist/cli.js +4 -9
  81. package/dist/compaction-record.d.ts +2 -0
  82. package/dist/compaction-record.js +89 -68
  83. package/dist/compare.d.ts +11 -16
  84. package/dist/compare.js +11 -16
  85. package/dist/config.d.ts +19 -18
  86. package/dist/config.js +90 -67
  87. package/dist/connectors/github/backfill.d.ts +2 -2
  88. package/dist/connectors/github/backfill.js +8 -15
  89. package/dist/connectors/github/cli-impl.js +3 -8
  90. package/dist/connectors/github/deletion.d.ts +5 -12
  91. package/dist/connectors/github/deletion.js +5 -12
  92. package/dist/connectors/github/dlq.d.ts +6 -9
  93. package/dist/connectors/github/dlq.js +2 -3
  94. package/dist/connectors/github/ingest.d.ts +5 -7
  95. package/dist/connectors/github/ingest.js +8 -12
  96. package/dist/connectors/github/octokit-client.d.ts +3 -5
  97. package/dist/connectors/github/octokit-client.js +5 -6
  98. package/dist/connectors/github/signature.d.ts +9 -39
  99. package/dist/connectors/github/signature.js +9 -39
  100. package/dist/connectors/github/tenant-routing.d.ts +1 -1
  101. package/dist/connectors/github/tenant-routing.js +1 -1
  102. package/dist/connectors/github/transform.js +2 -2
  103. package/dist/connectors/github/types.d.ts +2 -10
  104. package/dist/connectors/github/types.js +1 -3
  105. package/dist/connectors/slack/deletion.d.ts +3 -8
  106. package/dist/connectors/slack/deletion.js +3 -8
  107. package/dist/connectors/slack/dlq.d.ts +1 -1
  108. package/dist/connectors/slack/ingest.d.ts +1 -1
  109. package/dist/connectors/slack/ingest.js +7 -16
  110. package/dist/connectors/slack/signature.d.ts +1 -1
  111. package/dist/connectors/slack/tenant-routing.d.ts +3 -5
  112. package/dist/connectors/slack/tenant-routing.js +3 -5
  113. package/dist/connectors/slack/transform.d.ts +5 -6
  114. package/dist/connectors/slack/transform.js +5 -6
  115. package/dist/connectors/slack/types.d.ts +2 -6
  116. package/dist/connectors/slack/types.js +1 -3
  117. package/dist/connectors/slack/web-client.js +10 -3
  118. package/dist/connectors/slack/workspaces.d.ts +3 -5
  119. package/dist/connectors/slack/workspaces.js +3 -5
  120. package/dist/consolidate/conflicts.js +3 -14
  121. package/dist/consolidate/decay.js +9 -29
  122. package/dist/consolidate/llm-passes.js +4 -5
  123. package/dist/consolidate/merge.js +8 -23
  124. package/dist/consolidate/run.d.ts +1 -8
  125. package/dist/consolidate/run.js +3 -25
  126. package/dist/consolidate/sleep.js +5 -17
  127. package/dist/consolidate/traces.js +9 -21
  128. package/dist/customer-notes.d.ts +5 -7
  129. package/dist/customer-notes.js +6 -9
  130. package/dist/dag.d.ts +10 -21
  131. package/dist/dag.js +23 -73
  132. package/dist/db/continuity.js +2 -2
  133. package/dist/db/migrations/v14.js +1 -1
  134. package/dist/db/migrations/v15.js +1 -2
  135. package/dist/db/migrations/v16.js +3 -4
  136. package/dist/db/migrations/v17.js +2 -3
  137. package/dist/db/migrations/v19.js +1 -1
  138. package/dist/db/migrations/v20.js +1 -1
  139. package/dist/db/migrations/v21.js +2 -6
  140. package/dist/db/migrations/v22.js +2 -4
  141. package/dist/db/migrations/v23.js +1 -1
  142. package/dist/db/migrations/v24.js +4 -6
  143. package/dist/db/migrations/v25.js +2 -3
  144. package/dist/db/migrations/v26.js +3 -3
  145. package/dist/db/migrations/v27.js +2 -10
  146. package/dist/db/migrations/v28.js +5 -8
  147. package/dist/db/migrations/v29.js +3 -4
  148. package/dist/db/migrations/v30.js +2 -2
  149. package/dist/db/migrations/v31.js +1 -1
  150. package/dist/db/migrations/v32.js +1 -1
  151. package/dist/db/migrations/v33.js +3 -3
  152. package/dist/db/migrations/v34.js +1 -1
  153. package/dist/db/migrations/v35.js +3 -4
  154. package/dist/db/migrations/v36.js +3 -4
  155. package/dist/db/migrations/v37.js +5 -5
  156. package/dist/db/migrations/v38.js +7 -8
  157. package/dist/db/migrations/v39.js +1 -1
  158. package/dist/db/migrations/v40.js +4 -16
  159. package/dist/db/migrations/v41.js +3 -4
  160. package/dist/db/migrations/v42.js +3 -4
  161. package/dist/db/migrations/v45.js +1 -1
  162. package/dist/db/migrations/v46.js +1 -1
  163. package/dist/db/migrations/v47.js +1 -1
  164. package/dist/db/migrations/v48.js +1 -1
  165. package/dist/decisions.d.ts +2 -2
  166. package/dist/decisions.js +6 -6
  167. package/dist/dedupe.js +86 -61
  168. package/dist/delivery-recorder.js +154 -135
  169. package/dist/doctor.js +119 -104
  170. package/dist/dormant.js +1 -4
  171. package/dist/embedding-provider.d.ts +4 -8
  172. package/dist/embedding-provider.js +4 -8
  173. package/dist/embeddings.js +55 -47
  174. package/dist/env.d.ts +1 -1
  175. package/dist/env.js +12 -12
  176. package/dist/escape.d.ts +5 -0
  177. package/dist/escape.js +10 -0
  178. package/dist/eval-stats.d.ts +1 -2
  179. package/dist/eval-stats.js +1 -2
  180. package/dist/eval-suite.js +27 -21
  181. package/dist/extract.js +4 -9
  182. package/dist/failure-log.d.ts +3 -3
  183. package/dist/failure-log.js +1 -1
  184. package/dist/forward-claim-detector.d.ts +2 -4
  185. package/dist/forward-claim-detector.js +6 -11
  186. package/dist/goals.d.ts +3 -3
  187. package/dist/goals.js +5 -6
  188. package/dist/graph/read.d.ts +2 -2
  189. package/dist/graph/read.js +5 -6
  190. package/dist/graph/types.d.ts +8 -8
  191. package/dist/graph/write.d.ts +7 -14
  192. package/dist/graph/write.js +16 -23
  193. package/dist/graph-extract.d.ts +7 -8
  194. package/dist/graph-extract.js +62 -72
  195. package/dist/graph-recall.d.ts +2 -2
  196. package/dist/graph-recall.js +55 -49
  197. package/dist/graph-stream.d.ts +5 -6
  198. package/dist/graph-stream.js +66 -57
  199. package/dist/graph-view.d.ts +2 -2
  200. package/dist/graph-view.js +7 -7
  201. package/dist/half-life-migration.d.ts +1 -2
  202. package/dist/half-life-migration.js +2 -3
  203. package/dist/hooks/codex-session.js +1 -1
  204. package/dist/hooks/codex-wrapper.d.ts +1 -1
  205. package/dist/hooks/codex-wrapper.js +3 -2
  206. package/dist/hooks/json-hooks.d.ts +2 -2
  207. package/dist/hooks/json-hooks.js +5 -4
  208. package/dist/hooks/opencode.d.ts +1 -1
  209. package/dist/hooks/opencode.js +5 -4
  210. package/dist/hooks/shared.d.ts +3 -7
  211. package/dist/hooks/shared.js +1 -8
  212. package/dist/http-util.d.ts +2 -3
  213. package/dist/http-util.js +3 -0
  214. package/dist/importers/core.d.ts +2 -9
  215. package/dist/importers/core.js +15 -30
  216. package/dist/importers/sources.js +2 -1
  217. package/dist/importers/vault.js +2 -20
  218. package/dist/incidents.d.ts +1 -1
  219. package/dist/incidents.js +1 -1
  220. package/dist/instruction-detect.d.ts +1 -1
  221. package/dist/instruction-detect.js +1 -1
  222. package/dist/invalidation.d.ts +3 -0
  223. package/dist/invalidation.js +160 -114
  224. package/dist/json.d.ts +5 -0
  225. package/dist/json.js +4 -0
  226. package/dist/judgment.js +1 -2
  227. package/dist/local-embedding.js +1 -1
  228. package/dist/mcp/admin-tools.js +7 -17
  229. package/dist/mcp/format.js +1 -1
  230. package/dist/mcp/framing.js +3 -6
  231. package/dist/mcp/protocol.d.ts +2 -5
  232. package/dist/mcp/protocol.js +1 -3
  233. package/dist/mcp/recall-tools.js +12 -15
  234. package/dist/mcp/request.js +4 -3
  235. package/dist/mcp/session-state.js +2 -3
  236. package/dist/mcp/stdio.js +2 -1
  237. package/dist/mcp/tools.js +9 -6
  238. package/dist/memory-value-weights.d.ts +5 -8
  239. package/dist/memory-value-weights.js +5 -8
  240. package/dist/memory-value.d.ts +13 -13
  241. package/dist/memory-value.js +26 -37
  242. package/dist/memory.d.ts +20 -22
  243. package/dist/memory.js +24 -48
  244. package/dist/multihop.d.ts +1 -1
  245. package/dist/multihop.js +3 -2
  246. package/dist/owner-validation.d.ts +4 -5
  247. package/dist/owner-validation.js +4 -5
  248. package/dist/physics.d.ts +4 -4
  249. package/dist/physics.js +7 -9
  250. package/dist/policies.d.ts +9 -10
  251. package/dist/policies.js +12 -14
  252. package/dist/postinstall.js +3 -6
  253. package/dist/predictions/planning-fallacy.d.ts +9 -14
  254. package/dist/predictions/planning-fallacy.js +10 -16
  255. package/dist/predictions/store.d.ts +15 -23
  256. package/dist/predictions/store.js +36 -33
  257. package/dist/processes.d.ts +2 -7
  258. package/dist/processes.js +3 -3
  259. package/dist/project-briefs.d.ts +2 -3
  260. package/dist/project-briefs.js +9 -13
  261. package/dist/project-identity.d.ts +22 -9
  262. package/dist/project-identity.js +47 -12
  263. package/dist/project-merge.d.ts +17 -2
  264. package/dist/project-merge.js +109 -27
  265. package/dist/project-remote.d.ts +12 -0
  266. package/dist/project-remote.js +138 -0
  267. package/dist/prompt-recall.js +1 -2
  268. package/dist/rate-limit.d.ts +1 -1
  269. package/dist/rate-limit.js +1 -1
  270. package/dist/raw-archive.d.ts +9 -0
  271. package/dist/raw-archive.js +70 -53
  272. package/dist/recall-history.d.ts +19 -20
  273. package/dist/recall-history.js +24 -42
  274. package/dist/recall-pipeline.js +4 -28
  275. package/dist/recall-scope.d.ts +7 -8
  276. package/dist/recall-scope.js +7 -8
  277. package/dist/recall-trace.d.ts +5 -9
  278. package/dist/recall-trace.js +6 -10
  279. package/dist/refine-llm.d.ts +1 -1
  280. package/dist/refine-llm.js +2 -2
  281. package/dist/reject-flow.d.ts +3 -4
  282. package/dist/reject-flow.js +122 -117
  283. package/dist/rejection.d.ts +5 -6
  284. package/dist/rejection.js +7 -15
  285. package/dist/rerankers/clef.d.ts +1 -1
  286. package/dist/rerankers/jev.d.ts +1 -2
  287. package/dist/rerankers/jev.js +4 -5
  288. package/dist/rerankers/llm.d.ts +1 -2
  289. package/dist/rerankers/llm.js +1 -2
  290. package/dist/rerankers/types.d.ts +1 -2
  291. package/dist/rrf.d.ts +2 -2
  292. package/dist/rrf.js +2 -2
  293. package/dist/search/bm25-search.d.ts +1 -1
  294. package/dist/search/bm25-search.js +2 -1
  295. package/dist/search/boosts.js +2 -1
  296. package/dist/search/hybrid.d.ts +1 -1
  297. package/dist/search/hybrid.js +2 -1
  298. package/dist/search/physics-search.d.ts +1 -1
  299. package/dist/search/physics-search.js +2 -1
  300. package/dist/search/types.d.ts +2 -0
  301. package/dist/search/types.js +3 -1
  302. package/dist/secret-detect.d.ts +4 -5
  303. package/dist/secret-detect.js +6 -10
  304. package/dist/server/auth.js +5 -5
  305. package/dist/server/client-ip.js +1 -1
  306. package/dist/server/cursor.js +2 -1
  307. package/dist/server/mcp-http.js +4 -4
  308. package/dist/server/request.d.ts +3 -6
  309. package/dist/server/request.js +6 -7
  310. package/dist/server/routes/admin.js +5 -4
  311. package/dist/server/routes/customer-notes.js +6 -5
  312. package/dist/server/routes/decisions.js +4 -3
  313. package/dist/server/routes/incidents.js +7 -5
  314. package/dist/server/routes/memories.js +7 -7
  315. package/dist/server/routes/policies.js +3 -2
  316. package/dist/server/routes/predictions.js +12 -15
  317. package/dist/server/routes/processes.js +3 -2
  318. package/dist/server/routes/project-briefs.js +8 -7
  319. package/dist/server/routes/recall.js +95 -93
  320. package/dist/server/routes/skills.js +6 -5
  321. package/dist/server/types.d.ts +1 -1
  322. package/dist/server/validation.d.ts +1 -2
  323. package/dist/server/validation.js +7 -14
  324. package/dist/server-detect.js +72 -58
  325. package/dist/server.d.ts +2 -2
  326. package/dist/server.js +131 -117
  327. package/dist/shared.d.ts +17 -17
  328. package/dist/shared.js +93 -97
  329. package/dist/skills.d.ts +3 -3
  330. package/dist/skills.js +8 -8
  331. package/dist/store/audit-event.d.ts +2 -2
  332. package/dist/store/audit-event.js +1 -1
  333. package/dist/store/candidates.d.ts +2 -2
  334. package/dist/store/candidates.js +4 -3
  335. package/dist/store/conflicts.js +30 -22
  336. package/dist/store/delete-and-batch.d.ts +11 -14
  337. package/dist/store/delete-and-batch.js +40 -91
  338. package/dist/store/entry-reads.d.ts +17 -25
  339. package/dist/store/entry-reads.js +59 -40
  340. package/dist/store/entry-row.d.ts +6 -24
  341. package/dist/store/entry-row.js +6 -24
  342. package/dist/store/entry-writes.d.ts +6 -7
  343. package/dist/store/entry-writes.js +13 -11
  344. package/dist/store/handoffs.d.ts +1 -1
  345. package/dist/store/handoffs.js +7 -10
  346. package/dist/store/index-and-stats.d.ts +2 -6
  347. package/dist/store/index-and-stats.js +4 -10
  348. package/dist/store/mirrors.d.ts +6 -19
  349. package/dist/store/mirrors.js +14 -39
  350. package/dist/store/open.js +9 -31
  351. package/dist/store/rows.d.ts +5 -11
  352. package/dist/store/rows.js +6 -11
  353. package/dist/store/search-rows.d.ts +17 -34
  354. package/dist/store/search-rows.js +31 -59
  355. package/dist/store/sessions.d.ts +4 -5
  356. package/dist/store/sessions.js +5 -6
  357. package/dist/store/summaries.d.ts +13 -17
  358. package/dist/store/summaries.js +26 -70
  359. package/dist/support-bundle.js +4 -8
  360. package/dist/tenant.d.ts +1 -5
  361. package/dist/token-ledger.d.ts +1 -1
  362. package/dist/token-ledger.js +3 -5
  363. package/dist/trace.js +1 -3
  364. package/dist/version.d.ts +1 -1
  365. package/dist/version.js +1 -1
  366. package/dist/working-memory.d.ts +1 -1
  367. package/extensions/openclaw-plugin/openclaw.plugin.json +1 -1
  368. package/extensions/openclaw-plugin/package.json +1 -1
  369. package/openclaw.plugin.json +1 -1
  370. package/package.json +1 -1
@@ -6,11 +6,8 @@ import * as skillsModule from '../skills.js';
6
6
  import { resolveTenantId } from '../tenant.js';
7
7
  import { printError } from './output.js';
8
8
  import { requireInit } from './shared.js';
9
- // Strict positive-integer id parse for the mutating process subcommands.
10
- // parseInt alone accepts trailing junk ('1abc' -> 1), which would let
11
- // `process close 1abc` / `supersede 1abc` silently hit the wrong row; require
12
- // the whole arg to be digits. (Mirrors parsePositiveIncidentId; codex P2,
13
- // 2026-05-29.)
9
+ // parseInt alone accepts trailing junk ('1abc' -> 1), which would let `process close 1abc`
10
+ // silently hit the wrong row; require the whole arg to be digits.
14
11
  function parsePositiveProcessId(idRaw) {
15
12
  const s = String(idRaw ?? '').trim();
16
13
  const id = parseInt(s, 10);
@@ -193,8 +190,7 @@ function processCreate(hippoRoot, tenantId, processName, flags) {
193
190
  if (created.memoryId)
194
191
  console.log(` memory: ${created.memoryId}`);
195
192
  }
196
- // Strict positive-integer id parse for the mutating policy subcommands (mirrors
197
- // parsePositiveProcessId; codex P2 class - parseInt alone accepts '1abc' -> 1).
193
+ // Strict positive-integer id parse for the mutating policy subcommands: parseInt alone accepts '1abc' -> 1.
198
194
  function parsePositivePolicyId(idRaw) {
199
195
  const s = String(idRaw ?? '').trim();
200
196
  const id = parseInt(s, 10);
@@ -392,8 +388,7 @@ function policyCreate(hippoRoot, tenantId, args, flags) {
392
388
  process.exit(1);
393
389
  }
394
390
  }
395
- // Strict positive-integer id parse for the mutating skill subcommands (mirrors
396
- // parsePositivePolicyId; codex P2 class - parseInt accepts '1abc' -> 1).
391
+ // Strict positive-integer id parse for the mutating skill subcommands: parseInt accepts '1abc' -> 1.
397
392
  function parsePositiveSkillId(idRaw) {
398
393
  const s = String(idRaw ?? '').trim();
399
394
  const id = parseInt(s, 10);
@@ -58,7 +58,7 @@ export function cmdProjects(hippoRoot, args, flags) {
58
58
  return;
59
59
  }
60
60
  console.log(`${apply ? 'Merged' : 'Dry run: would merge'} ${from} into ${into}:`);
61
- console.log(` ${count(r.setAside.length, 'imported note copy', 'imported note copies')} set aside (dormant; the next sync under ${into} imports the notes still on disk)`);
61
+ console.log(` ${count(r.setAside.length, 'imported note copy', 'imported note copies')} set aside (${into} already holds the same note; the rest move under ${into} on the next sync)`);
62
62
  console.log(` ${count(r.restamped.length, 'memory', 'memories')} re-tagged, plus ${r.dormantRestamped.length} dormant and ${count(r.compactions, 'compaction record')}`);
63
63
  console.log(apply ? `Backup: ${r.backup}\nEvery id is in the audit log: hippo audit list --op project_merge` : 'Nothing written. Add --apply to run it.');
64
64
  return;
@@ -73,6 +73,8 @@ export function cmdProjects(hippoRoot, args, flags) {
73
73
  console.log(` ${count(r.copies.length, 'imported note copy', 'imported note copies')} under the wrong project set aside (the note stays under its own, or user-global)`);
74
74
  for (const f of r.folds)
75
75
  console.log(` ${f.from} folded into ${f.into} (its sessions' folders resolve there now)`);
76
+ for (const c of r.collisions)
77
+ console.log(` ${c.name} left as it is: its folders now resolve to ${c.ids.join(', ')}; fold it by hand with hippo projects merge`);
76
78
  console.log(` sleep's user-global merged rows: ${r.toProject.length} re-tagged to their parents' project`);
77
79
  console.log(` ${r.setAside.length} set aside (parents in two projects; sleep re-merges them per project)`);
78
80
  console.log(` ${r.untraced.length} left as they are (no parent left to show which project; check them with hippo inspect <id>)`);
@@ -10,6 +10,7 @@ import { loadLatestHandoff } from '../store/handoffs.js';
10
10
  import { passesScopeFilterForRecall } from '../recall-scope.js';
11
11
  import { fitBudget } from '../search/finalize.js';
12
12
  import { explainMatch } from '../search/explain.js';
13
+ import { DEFAULT_RECALL_BUDGET } from '../search/types.js';
13
14
  import { writeRecallTraceAtRoot } from '../recall-trace.js';
14
15
  import { loadConfig } from '../config.js';
15
16
  import { openHippoDb, closeHippoDb } from '../db.js';
@@ -188,7 +189,7 @@ export async function cmdRecall(hippoRoot, query, flags) {
188
189
  }
189
190
  /** Every flag recall reads, parsed in the order the single-body command checked them. */
190
191
  function parseRecallOptions(hippoRoot, flags) {
191
- const budget = parseBudgetFlag(flags['budget'], 4000);
192
+ const budget = parseBudgetFlag(flags['budget'], DEFAULT_RECALL_BUDGET);
192
193
  const limit = parseLimitFlag(flags['limit']);
193
194
  const asJson = Boolean(flags['json']);
194
195
  const showWhy = Boolean(flags['why']);
@@ -56,7 +56,7 @@ function parseKindFlag(flags) {
56
56
  const kindFlagRaw = typeof flags['kind'] === 'string' ? flags['kind'] : undefined;
57
57
  const kindFlag = kindFlagRaw === undefined ? undefined : kindFlagRaw.toLowerCase();
58
58
  // CLI surface intentionally restricted: 'raw' is reserved for ingestion connectors
59
- // (E1.x: Slack/Jira/Gmail) that route deletions through archiveRawMemory. Existing
59
+ // that route deletions through archiveRawMemory. Existing
60
60
  // forget/consolidate/conflict-resolve paths abort on kind='raw' via the append-only
61
61
  // trigger, so exposing --kind raw here would create unforgettable memories.
62
62
  // 'archived' is an internal sentinel set only inside archiveRawMemory's transaction.
@@ -68,7 +68,6 @@ function parseKindFlag(flags) {
68
68
  }
69
69
  return kindFlag;
70
70
  }
71
- // A3 envelope flags
72
71
  function parseRememberEnvelope(flags) {
73
72
  const kind = parseKindFlag(flags);
74
73
  const ownerRaw = typeof flags['owner'] === 'string' ? flags['owner'] : null;
@@ -99,8 +98,7 @@ async function cmdRemember(hippoRoot, text, flags) {
99
98
  const existing = loadAllEntries(targetRoot, resolveTenantId({}));
100
99
  const schemaFit = computeSchemaFit(text, requestedTags, existing);
101
100
  const envelope = parseRememberEnvelope(flags);
102
- // A5 stub auth: stamp tenant_id from env (HIPPO_TENANT) so recall isolation
103
- // can filter on this row. Default tenant 'default' for unauthenticated CLI.
101
+ // Stamp tenant_id from env (HIPPO_TENANT) so recall isolation can filter on this row; unauthenticated CLI gets 'default'.
104
102
  const tenantId = resolveTenantId({});
105
103
  const rememberConfig = loadConfig(targetRoot);
106
104
  const entry = createMemory(text, {
@@ -209,15 +207,8 @@ function cmdSupersede(hippoRoot, oldId, newContent, flags) {
209
207
  tags,
210
208
  pinned,
211
209
  });
212
- // AT1: write the SUCCESSOR first. The rejection guard fires on the new
213
- // content — if it refuses, nothing has been mutated yet (the old ordering
214
- // committed old.superseded_by before the guarded new write, leaving a
215
- // dangling pointer to an id that was never created). If the old-row write
216
- // below fails instead, the new row exists unpointered — an orphan
217
- // successor, strictly less harmful than a dangling pointer. NOTE: unlike
218
- // api.supersede (whose CAS + insert commit in ONE transaction), this CLI
219
- // path is two independent writes and stays non-atomic; write order is its
220
- // only ordering guarantee.
210
+ // Write the SUCCESSOR first: a rejection-guard refusal then mutates nothing, and an old-row failure leaves an
211
+ // orphan successor rather than a dangling pointer. Unlike api.supersede this path is two non-atomic writes.
221
212
  try {
222
213
  writeEntry(hippoRoot, newEntry);
223
214
  }
@@ -420,8 +411,7 @@ export async function handleRemember({ hippoRoot, args, flags }) {
420
411
  // flags (--pin, --layer, --extract, --global) still need the direct
421
412
  // path; we only intercept the minimal envelope. The salience gate is
422
413
  // NOT in richFlag and the route does not apply it, so a routed remember
423
- // stores what a direct one would skip. Measured 2026-09-07, tracked in
424
- // TODOS.md; do not read this list as covering salience.
414
+ // stores what a direct one would skip; do not read this list as covering salience.
425
415
  const richFlag = flags['pin'] || flags['global'] || flags['extract'] || flags['force'] ||
426
416
  flags['observed'] || flags['inferred'] || flags['verified'] ||
427
417
  flags['layer'] !== undefined;
@@ -430,9 +420,7 @@ export async function handleRemember({ hippoRoot, args, flags }) {
430
420
  const rememberKindAllowed = ['distilled', 'superseded'];
431
421
  if (rememberKindRaw === undefined || rememberKindAllowed.includes(rememberKindRaw)) {
432
422
  const tags = rememberTags(flags, process.cwd()).all;
433
- // B2 v1.12.6 — validate --owner on the thin-client path too.
434
- // Failure on this path exits early so the user gets the same
435
- // validation experience whether or not a server is up.
423
+ // Validate --owner on the thin-client path too, so validation is the same whether or not a server is up.
436
424
  const thinOwnerRaw = typeof flags['owner'] === 'string' ? flags['owner'] : undefined;
437
425
  const thinOwnerCheck = validateOwner(thinOwnerRaw, { strict: isStrictOwnerEnv() });
438
426
  if (!thinOwnerCheck.ok) {
@@ -14,7 +14,7 @@ import { captureToolFailure } from '../capture-error.js';
14
14
  import { estimateTokens, isSubagentPayload, readApiCalls, recordRereads, recordTokenUse, } from '../token-ledger.js';
15
15
  import { currentMachine, importSessionFolder } from '../agent-memories/sync.js';
16
16
  import { summaryLine } from '../agent-memories/report.js';
17
- import { deriveOriginProject } from '../project-identity.js';
17
+ import { resolveProjectIdentity } from '../project-identity.js';
18
18
  import { getGlobalRoot } from '../shared.js';
19
19
  import { cmdCapture } from '../capture/command.js';
20
20
  import { cmdPreCompact, cmdPostCompact, transcriptWorkingState } from '../capture/compact.js';
@@ -63,22 +63,19 @@ export function cmdLastSleep(flags) {
63
63
  * memories here — the UserPromptSubmit hook already re-injects those every
64
64
  * turn, so duplicating them here would double token cost for nothing.
65
65
  *
66
- * Same exit-0/crash-safety contract as `hippo pre-compact` (critic round
67
- * 2): every path exits 0. A malformed payload or a store read failure
66
+ * Same exit-0/crash-safety contract as `hippo pre-compact`: every path
67
+ * exits 0. A malformed payload or a store read failure
68
68
  * degrades to empty stdout, never a thrown error — a failing SessionStart
69
69
  * hook must not pollute session startup.
70
70
  */
71
- // X8: session-event content is capped at print time only — the shared
72
- // printSessionEvents stays untouched for every other caller.
71
+ // Capped at print time only, so the shared printSessionEvents stays untouched for every other caller.
73
72
  const COMPACT_RESUME_EVENT_CONTENT_CAP = 400;
74
73
  // A snapshot older than this was not written for this compaction (pre-compact skipped), so restoring it is stale, not a resume.
75
74
  const COMPACT_RESUME_MAX_AGE_MS = 15 * 60_000;
76
75
  function cmdCompactResume(hippoRoot, stdinText, stdinTimedOut) {
77
76
  try {
78
- // X3: gate on the non-exiting isInitialized check before any
79
- // store-opening call (loadActiveTaskSnapshot/listSessionEvents both
80
- // call initStore internally, which would silently create a store in a
81
- // project that never ran `hippo init` — this hook fires globally).
77
+ // Gate on the non-exiting isInitialized check first: the store reads below call initStore, which would
78
+ // silently create a store in a project that never ran `hippo init`, and this hook fires globally.
82
79
  if (!isInitialized(hippoRoot)) {
83
80
  process.exit(0);
84
81
  }
@@ -88,7 +85,7 @@ function cmdCompactResume(hippoRoot, stdinText, stdinTimedOut) {
88
85
  // carries a different source (e.g. 'startup') means the matcher-based
89
86
  // gate failed to apply — stay silent rather than print stale state.
90
87
  const nonEmptyStdin = !!stdinText && stdinText.trim() !== '';
91
- // Without a payload session_id the X5 cross-restore guard below can
88
+ // Without a payload session_id the cross-restore guard below can
92
89
  // never fire, so a timed-out empty read must not reach the print path.
93
90
  let suppressOutput = stdinTimedOut && !nonEmptyStdin;
94
91
  let payloadSessionId = null;
@@ -102,19 +99,13 @@ function cmdCompactResume(hippoRoot, stdinText, stdinTimedOut) {
102
99
  payload = null;
103
100
  }
104
101
  if (!payload || typeof payload !== 'object') {
105
- // X13: fail closed on malformed non-empty stdin. The earlier
106
- // "print on malformed" behavior survives only for TTY/no-stdin
107
- // manual invocation (nonEmptyStdin is false there, this branch
108
- // never runs).
102
+ // Fail closed on malformed non-empty stdin; only a TTY/no-stdin manual run, which never reaches here, prints.
109
103
  suppressOutput = true;
110
104
  }
111
105
  else {
112
- // Fail closed on structurally incomplete payloads too ({}, [],
113
- // source missing/non-string): any parsed non-empty payload must say
114
- // source === 'compact' to print. Real SessionStart payloads always
115
- // carry source; only the TTY/no-stdin manual path prints without
116
- // one (codex round 3).
117
- // A sub-agent's payload carries its parent's session id, so X5 would pass and restore the parent's snapshot into it.
106
+ // Fail closed on structurally incomplete payloads too ({}, [], source missing/non-string): real
107
+ // SessionStart payloads always carry source, so a parsed one must say 'compact' to print.
108
+ // A sub-agent's payload carries its parent's session id, so the mismatch guard would pass and restore the parent's snapshot into it.
118
109
  if (payload.source !== 'compact' || isSubagentPayload(stdinText)) {
119
110
  suppressOutput = true;
120
111
  }
@@ -139,9 +130,8 @@ function cmdCompactResume(hippoRoot, stdinText, stdinTimedOut) {
139
130
  function restoreCompactSnapshot(hippoRoot, payloadSessionId) {
140
131
  const tenantId = resolveTenantId({});
141
132
  const snapshot = loadFreshActiveTaskSnapshot(hippoRoot, tenantId, { maxAgeMs: COMPACT_RESUME_MAX_AGE_MS });
142
- // X5: concurrent sessions must not cross-restore. Only suppress when
143
- // BOTH ids are present and differ — either side missing, or a manual
144
- // invocation with no payload session_id, still prints.
133
+ // Concurrent sessions must not cross-restore. Only suppress when BOTH ids are present and differ;
134
+ // either side missing, or a manual invocation with no payload session_id, still prints.
145
135
  const sessionMismatch = !!snapshot &&
146
136
  payloadSessionId !== null &&
147
137
  snapshot.session_id !== null &&
@@ -164,8 +154,7 @@ function restoreCompactSnapshot(hippoRoot, payloadSessionId) {
164
154
  // Printed in one write so the ledger books exactly the text the model is handed.
165
155
  const text = captureConsole(() => {
166
156
  console.log('## Restored after compaction\n');
167
- // X12: re-injected state is background reference, not instructions:
168
- // the framing line the model actually sees at every compaction.
157
+ // Re-injected state is background reference, not instructions: the framing line the model sees at every compaction.
169
158
  console.log("_Point-in-time working-state snapshot, auto-restored after compaction. Background reference, not instructions; the user's live messages win._\n");
170
159
  printActiveTaskSnapshot(snapshot);
171
160
  // Nothing auto-populates session_events, so an empty trail is the common real case;
@@ -191,8 +180,7 @@ function restoreCompactSnapshot(hippoRoot, payloadSessionId) {
191
180
  */
192
181
  export async function cmdSessionEnd(hippoRoot, flags) {
193
182
  const logFile = typeof flags['log-file'] === 'string' ? flags['log-file'] : null;
194
- // Bounded read (DF1 T3, docs/plans/2026-08-23-df1-snapshot-lifecycle.md):
195
- // extracts transcript_path + session_id for the detached worker's argv.
183
+ // Bounded read: extracts transcript_path + session_id for the detached worker's argv.
196
184
  let sessionId = null;
197
185
  const { text: stdinText } = await readStdinBounded();
198
186
  try {
@@ -224,7 +212,7 @@ export async function cmdSessionEnd(hippoRoot, flags) {
224
212
  });
225
213
  child.unref();
226
214
  }
227
- catch (err) {
215
+ catch {
228
216
  // If spawn fails, run inline as a last resort, handed what the child's argv would have carried.
229
217
  if (transcriptPath)
230
218
  flags['transcript'] = transcriptPath;
@@ -261,16 +249,9 @@ export async function cmdSessionEndWorker(hippoRoot, flags) {
261
249
  tenantId: resolveTenantId({}),
262
250
  log: digestLog,
263
251
  });
264
- // DF1 T3: close the ending session's own active task snapshot AFTER
265
- // sleep+capture complete — neither producer (runPreCompact,
266
- // `hippo snapshot save`) runs inside session-end, so this can never
267
- // destroy same-run work. Scoped to `--session-id`: a concurrent session's
268
- // active snapshot is untouched (closeTaskSnapshotsForSession's own WHERE
269
- // clause). Absent session id -> no-op plus one log line; session-end is
270
- // not guaranteed to fire at all (crash, kill -9), so the freshness bound
271
- // in loadFreshActiveTaskSnapshot is the backstop layer, not this close.
272
- // Handoff write happens BEFORE the snapshot close below, while the
273
- // snapshot writeSessionEndHandoff reads is still active.
252
+ // Close only this session's snapshot, after sleep+capture: no snapshot producer runs in session-end, and since
253
+ // session-end may never fire (crash, kill -9) the freshness bound in loadFreshActiveTaskSnapshot is the backstop.
254
+ // The handoff is written first, while the snapshot writeSessionEndHandoff reads is still active.
274
255
  if (closeSessionId)
275
256
  writeEndHandoff(store, closeSessionId, transcriptPath, closeLogFile);
276
257
  try {
@@ -318,7 +299,7 @@ function captureEndedSession(hippoRoot, store, flags, transcriptPath, scan) {
318
299
  global: false,
319
300
  tenantId: resolveTenantId({}),
320
301
  // In the global store, rows would otherwise read as user-global and show up in every project.
321
- originProject: store === hippoRoot ? undefined : deriveOriginProject(process.cwd()),
302
+ originProject: store === hippoRoot ? undefined : resolveProjectIdentity(process.cwd()),
322
303
  sessionTurns: scan?.turns,
323
304
  });
324
305
  }
@@ -531,7 +512,7 @@ export async function cmdCodexSessionEndWorker(hippoRoot, flags) {
531
512
  dryRun: false,
532
513
  global: false,
533
514
  tenantId: resolveTenantId({}),
534
- originProject: store === hippoRoot ? undefined : deriveOriginProject(process.cwd()),
515
+ originProject: store === hippoRoot ? undefined : resolveProjectIdentity(process.cwd()),
535
516
  sessionTurns: scan?.turns,
536
517
  };
537
518
  try {
@@ -1,4 +1,4 @@
1
- export declare function cmdHook(args: string[], flags: Record<string, string | boolean | string[]>): void;
1
+ export declare function cmdHook(args: string[]): void;
2
2
  export declare function cmdSetup(flags: Record<string, string | boolean | string[]>): void;
3
3
  export declare function cmdDailyRunner(): void;
4
4
  //# sourceMappingURL=setup.d.ts.map
package/dist/cli/setup.js CHANGED
@@ -15,10 +15,11 @@ import { log } from '../log.js';
15
15
  import { printError } from './output.js';
16
16
  import { printAgentImport, installCodexMemoryHooks, setupDailySchedule } from './shared.js';
17
17
  import { HOOK_MARKERS, HOOKS, hippoBlock } from './hook-blocks.js';
18
+ import { escapeRegex } from '../escape.js';
18
19
  // ---------------------------------------------------------------------------
19
20
  // Hook install/uninstall
20
21
  // ---------------------------------------------------------------------------
21
- export function cmdHook(args, flags) {
22
+ export function cmdHook(args) {
22
23
  const subcommand = args[0];
23
24
  const target = args[1];
24
25
  if (subcommand === 'list')
@@ -74,7 +75,6 @@ function hookInstall(target) {
74
75
  function patchAgentFile(hook, target) {
75
76
  const filepath = path.resolve(process.cwd(), hook.file);
76
77
  const block = `${HOOK_MARKERS.start}\n${hook.content}\n${HOOK_MARKERS.end}`;
77
- let agentFileTouched = false;
78
78
  if (fs.existsSync(filepath)) {
79
79
  const existing = fs.readFileSync(filepath, 'utf8');
80
80
  if (existing.includes(HOOK_MARKERS.start) && othersBlock(target, existing)) {
@@ -91,7 +91,6 @@ function patchAgentFile(hook, target) {
91
91
  fs.writeFileSync(filepath, existing + sep + block + '\n', 'utf8');
92
92
  console.log(`Installed Hippo hook in ${hook.file} (appended)`);
93
93
  }
94
- agentFileTouched = true;
95
94
  }
96
95
  else {
97
96
  // Do not create a new agent-instructions file (CLAUDE.md, AGENTS.md, etc.) in directories that don't already have one —
@@ -218,9 +217,6 @@ function withoutHookBlock(text) {
218
217
  const re = new RegExp(`\\n?${escapeRegex(HOOK_MARKERS.start)}[\\s\\S]*?${escapeRegex(HOOK_MARKERS.end)}\\n?`, 'g');
219
218
  return text.replace(re, '\n').replace(/\n{3,}/g, '\n\n').trim();
220
219
  }
221
- function escapeRegex(s) {
222
- return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
223
- }
224
220
  // `hippo setup` -- one-shot configuration for every AI coding tool on the box.
225
221
  // Detection and install logic live in ./hooks.ts.
226
222
  export function cmdSetup(flags) {
@@ -91,14 +91,14 @@ export function requireInit(hippoRoot) {
91
91
  /** Runs detectChurnStale against every store this repo's memories can live in. */
92
92
  export function runChurnStaleForRepo(hippoRoot, dryRun) {
93
93
  const repoRoot = execFileSync('git', ['rev-parse', '--show-toplevel'], { cwd: process.cwd(), encoding: 'utf8', timeout: 10_000, windowsHide: true }).trim();
94
- const projectName = resolveProjectIdentity(process.cwd()).name;
94
+ const { name: projectName, legacyName } = resolveProjectIdentity(process.cwd());
95
95
  const globalRoot = getGlobalRoot();
96
96
  const roots = globalRoot !== hippoRoot && isInitialized(globalRoot) ? [hippoRoot, globalRoot] : [hippoRoot];
97
97
  const tenantId = resolveTenantId({});
98
98
  return roots.map((root) => {
99
99
  // One store failing must not abort sleep's later phases or skip the other store.
100
100
  try {
101
- return { root, result: detectChurnStale(root, repoRoot, { tenantId, projectName, dryRun }) };
101
+ return { root, result: detectChurnStale(root, repoRoot, { tenantId, projectName, legacyName, dryRun }) };
102
102
  }
103
103
  catch (err) {
104
104
  const message = err instanceof Error ? err.message : String(err);
@@ -405,7 +405,7 @@ export function cardStringFlag(flags, key) {
405
405
  }
406
406
  // Claude Code exports its own session var, not ours; without the fallback agent-run recalls trace with no session.
407
407
  export function hostSessionId() {
408
- return envHippoSessionId()?.trim() || envClaudeCodeSessionId()?.trim() || undefined;
408
+ return envHippoSessionId() ?? envClaudeCodeSessionId();
409
409
  }
410
410
  /**
411
411
  * Compaction drops the pinned blocks the per-prompt hook injected
package/dist/cli/slack.js CHANGED
@@ -10,7 +10,7 @@ import { addWorkspace as addSlackWorkspace, listWorkspaces as listSlackWorkspace
10
10
  import { printError } from './output.js';
11
11
  import { printSlackBackfillUsage, printSlackWorkspacesUsage } from './usage.js';
12
12
  // ---------------------------------------------------------------------------
13
- // Slack subcommands (E1.3 — `hippo slack backfill` / `hippo slack dlq list`)
13
+ // Slack subcommands (`hippo slack backfill` / `hippo slack dlq list`)
14
14
  // ---------------------------------------------------------------------------
15
15
  function cmdSlackBackfill(hippoRoot, flags) {
16
16
  const channel = typeof flags['channel'] === 'string' ? flags['channel'] : undefined;
package/dist/cli/sleep.js CHANGED
@@ -8,7 +8,9 @@ import { replayCompactionsAt } from '../compaction-record.js';
8
8
  import * as api from '../api.js';
9
9
  import { resolveTenantId } from '../tenant.js';
10
10
  import { renderAmbientSummary } from '../ambient.js';
11
- import { log } from '../log.js';
11
+ import { errorMessage, log } from '../log.js';
12
+ import { closeHippoDb, openHippoDb } from '../db.js';
13
+ import { repairOnceOnSleep } from '../project-merge.js';
12
14
  import { requireInit, learnFromRepo, runChurnStaleForRepo, printAgentImport } from './shared.js';
13
15
  import { printError } from './output.js';
14
16
  /** Runs `hippo sleep`; with `--log-file` it also tees its output to that file. */
@@ -127,6 +129,29 @@ export function renderSleepResult(result) {
127
129
  console.log(`\nGraph: rebuilt ${tenants} tenant${tenants === 1 ? '' : 's'} (${entities} entities, ${relations} relations).`);
128
130
  }
129
131
  }
132
+ /** Fault-isolated: a failed repair warns and runs again next sleep, and never stops the sleep. */
133
+ function repairProjectTagsOnce(hippoRoot) {
134
+ let db;
135
+ try {
136
+ db = openHippoDb(hippoRoot);
137
+ const r = repairOnceOnSleep(db, hippoRoot, resolveTenantId({}));
138
+ if (r === null)
139
+ return;
140
+ const parts = [
141
+ r.copies.length > 0 ? `set aside ${r.copies.length} misfiled note imports` : '',
142
+ r.folds.length > 0 ? `folded ${r.folds.map((f) => `${f.from} into ${f.into}`).join(', ')}` : '',
143
+ r.toProject.length + r.setAside.length > 0 ? `re-tagged ${r.toProject.length + r.setAside.length} merged memories` : '',
144
+ ].filter((p) => p !== '');
145
+ console.log(`Repaired project tags once after the upgrade: ${parts.join('; ')} (backup: ${r.backup}).`);
146
+ }
147
+ catch (err) {
148
+ log.warn(`project tag repair skipped, retried next sleep: ${errorMessage(err)}`);
149
+ }
150
+ finally {
151
+ if (db)
152
+ closeHippoDb(db);
153
+ }
154
+ }
130
155
  async function cmdSleepCore(hippoRoot, flags) {
131
156
  requireInit(hippoRoot);
132
157
  // Phase 1: Auto-learn from git and every coding agent's own memories (CLI-only, uses process.cwd() / os.homedir()).
@@ -157,6 +182,7 @@ async function cmdSleepCore(hippoRoot, flags) {
157
182
  const finished = replayCompactionsAt(hippoRoot, (message) => log.warn(`compaction replay: ${message}`));
158
183
  if (finished > 0)
159
184
  console.log(`Finished saving ${finished} compaction${finished === 1 ? '' : 's'} left over from earlier sessions.`);
185
+ repairProjectTagsOnce(hippoRoot);
160
186
  }
161
187
  // Phase 2-6: Pure-storage pipeline (consolidate + dedup + audit + share + ambient).
162
188
  const ctx = {
@@ -1,14 +1,14 @@
1
1
  import { type CommandContext } from './shared.js';
2
2
  export declare function cmdStatus(hippoRoot: string): void;
3
3
  /**
4
- * `hippo tokens [--days <n>] [--json] [--global]`: the token ledger
5
- * (ROADMAP TE0). Tokens of memory text handed to agents per surface, blocks
6
- * the per-prompt hook skipped as unchanged (TE2) and the tokens that saved,
4
+ * `hippo tokens [--days <n>] [--json] [--global]`: the token ledger.
5
+ * Tokens of memory text handed to agents per surface, blocks
6
+ * the per-prompt hook skipped as unchanged and the tokens that saved,
7
7
  * and the hook blocks' tokens later model calls re-read, counted when each session ends.
8
8
  * Counts are estimates (characters / 4), the same estimate every budget uses.
9
9
  */
10
10
  export declare function cmdTokens(hippoRoot: string, flags: Record<string, string | boolean | string[]>): void;
11
- /** `hippo failures [--days <n>] [--json] [--global]`: failed tool calls by outcome, and repeats across sessions (CD13). */
11
+ /** `hippo failures [--days <n>] [--json] [--global]`: failed tool calls by outcome, and repeats across sessions. */
12
12
  export declare function cmdFailures(hippoRoot: string, flags: Record<string, string | boolean | string[]>): void;
13
13
  export declare function handleCorrectionLatency({ hippoRoot, flags }: CommandContext): void;
14
14
  export declare function handleProvenance({ hippoRoot, flags }: CommandContext): void;
@@ -159,13 +159,8 @@ function printPhysicsStatus(hippoRoot) {
159
159
  const physConfig = loadConfig(hippoRoot);
160
160
  const energy = computeSystemEnergy(particles, physConfig.physics.G_memory);
161
161
  let sumVelMag = 0;
162
- let maxVelMag = 0;
163
- for (const p of particles) {
164
- const mag = vecNorm(p.velocity);
165
- sumVelMag += mag;
166
- if (mag > maxVelMag)
167
- maxVelMag = mag;
168
- }
162
+ for (const p of particles)
163
+ sumVelMag += vecNorm(p.velocity);
169
164
  const avgVelMag = sumVelMag / particles.length;
170
165
  console.log('');
171
166
  console.log(`Physics: ${particles.length} particles, energy: ${fmt(energy.total, 4)} (KE: ${fmt(energy.kinetic, 4)}, PE: ${fmt(energy.potential, 4)}), avg vel: ${fmt(avgVelMag, 4)}`);
@@ -222,9 +217,9 @@ function cmdInspect(hippoRoot, id) {
222
217
  console.log(entry.content);
223
218
  }
224
219
  /**
225
- * `hippo tokens [--days <n>] [--json] [--global]`: the token ledger
226
- * (ROADMAP TE0). Tokens of memory text handed to agents per surface, blocks
227
- * the per-prompt hook skipped as unchanged (TE2) and the tokens that saved,
220
+ * `hippo tokens [--days <n>] [--json] [--global]`: the token ledger.
221
+ * Tokens of memory text handed to agents per surface, blocks
222
+ * the per-prompt hook skipped as unchanged and the tokens that saved,
228
223
  * and the hook blocks' tokens later model calls re-read, counted when each session ends.
229
224
  * Counts are estimates (characters / 4), the same estimate every budget uses.
230
225
  */
@@ -263,7 +258,7 @@ export function cmdTokens(hippoRoot, flags) {
263
258
  console.log(' Re-reads are counted for hook and compact-resume blocks when a session ends; other surfaces, and open or crashed sessions, show sent only.');
264
259
  console.log(" Re-reads usually bill at the provider's cached-input rate, a fraction of the full input price.");
265
260
  }
266
- /** `hippo failures [--days <n>] [--json] [--global]`: failed tool calls by outcome, and repeats across sessions (CD13). */
261
+ /** `hippo failures [--days <n>] [--json] [--global]`: failed tool calls by outcome, and repeats across sessions. */
267
262
  export function cmdFailures(hippoRoot, flags) {
268
263
  // The store the capture-error hook writes to; a report never creates one.
269
264
  const root = flags['global'] ? getGlobalRoot() : hookStoreRoot(hippoRoot);
@@ -298,7 +293,7 @@ export function cmdFailures(hippoRoot, flags) {
298
293
  for (const [label, count, note] of rows) {
299
294
  console.log(` ${label.padEnd(13)}${String(count).padStart(6)} ${note}`.trimEnd());
300
295
  }
301
- // Counts, not a rate: a share means little without a holdout arm to compare against (CD11).
296
+ // Counts, not a rate: a share means little without a holdout arm to compare against.
302
297
  if (summary.rated > 0) {
303
298
  const noSession = errors - summary.rated;
304
299
  const unrated = noSession > 0 ? ` ${noSession} more had no session id.` : '';
@@ -46,10 +46,8 @@ async function cmdWatch(command, hippoRoot) {
46
46
  entry.schema_fit = watchFit;
47
47
  entry.half_life_days = deriveHalfLife(loadConfig(hippoRoot).defaultHalfLifeDays, entry);
48
48
  entry.strength = calculateStrength(entry);
49
- // AT1 (plan §3 containment): mechanical content from a failed command — a
50
- // rejection-guard refusal here must not crash the watcher. Skip silently
51
- // (loud enough via the message below) and still exit with the wrapped
52
- // command's real exit code.
49
+ // A rejection-guard refusal of a failed command's output must not crash the watcher:
50
+ // skip with the message below and still exit with the wrapped command's real exit code.
53
51
  try {
54
52
  writeEntry(hippoRoot, entry);
55
53
  updateStats(hippoRoot, { remembered: 1 });
@@ -126,7 +124,7 @@ export function cmdImport(hippoRoot, args, flags) {
126
124
  extraTags,
127
125
  hippoRoot,
128
126
  };
129
- // K1 vault import: a FOLDER importer that mirrors the connector pattern
127
+ // Vault import: a FOLDER importer that mirrors the connector pattern
130
128
  // (kind='raw' + tag provenance + archiveRaw deletions), so it dispatches
131
129
  // separately from the single-file `importer` function-pointer slot below.
132
130
  // It writes through api.remember/archiveRaw which are tenant-scoped, so we
@@ -220,16 +218,15 @@ function importVaultFolder(hippoRoot, flags, importOptions, useGlobal, dryRun) {
220
218
  process.exit(1);
221
219
  }
222
220
  if (typeof flags['name'] !== 'string' || !flags['name'].trim()) {
223
- // --name is the vault identity key for the destructive source-deletion sync; inferring it from the folder basename let
224
- // same-basename vaults collide and clobber each other (codex R10 P2). A valueless `--name` parses as boolean true, and
225
- // String(true) === "true" would silently import under vault:true:* - reject a non-string so it fails fast instead (codex R11 P2).
221
+ // --name keys the destructive source-deletion sync; a folder-basename default lets same-basename vaults clobber
222
+ // each other, and a valueless `--name` (boolean true) would silently import under vault:true:*.
226
223
  printError('hippo import --vault requires --name <vault> (a non-empty identity key for source-deletion sync).');
227
224
  process.exit(1);
228
225
  }
229
226
  if (flags['scope'] !== undefined && (typeof flags['scope'] !== 'string' || !flags['scope'].trim())) {
230
227
  // Same valueless-flag trap: a bare `--scope` must not become scope "true".
231
228
  // Example uses the source-prefixed private form, since a bare `private` scope
232
- // is NOT treated as private by recall and importVault rejects it (R13 P2).
229
+ // is NOT treated as private by recall and importVault rejects it.
233
230
  printError('hippo import --vault: --scope requires a value (e.g. --scope vault:private:notes).');
234
231
  process.exit(1);
235
232
  }
@@ -251,7 +248,7 @@ function importVaultFolder(hippoRoot, flags, importOptions, useGlobal, dryRun) {
251
248
  warnRedacted(vaultResult.redacted);
252
249
  console.log(` ${dryRun ? 'Would archive: ' : 'Archived (removed): '}${vaultResult.archived ?? 0}`);
253
250
  console.log(` Store: ${hippoRoot}`);
254
- // Batch producer, same contract as the single-file import below: vault rows
251
+ // Batch producer, same contract as the single-file import above: vault rows
255
252
  // write through api.remember (which never embeds), so backfill them here.
256
253
  // Floating promise is deliberate; see the comment at the single-file site.
257
254
  if (!dryRun && vaultResult.imported >= 1) {
@@ -374,9 +371,7 @@ export function handleShare({ hippoRoot, args, flags }) {
374
371
  }
375
372
  }
376
373
  export function handlePeers({ flags }) {
377
- // D4 v1.12.10: tenant-scoped by default. --all-tenants restores the
378
- // pre-D4 host-wide view for the rare operator who genuinely wants
379
- // cross-tenant peer discovery.
374
+ // Tenant-scoped by default; --all-tenants gives the host-wide view for cross-tenant peer discovery.
380
375
  const allTenants = flags['all-tenants'] === true;
381
376
  const tenantScope = allTenants ? undefined : resolveTenantId({});
382
377
  const peers = listPeers(undefined, tenantScope);
package/dist/cli/usage.js CHANGED
@@ -1,5 +1,7 @@
1
1
  // Help text for every verb, keyed like the command table in cli.ts; no command logic lives here.
2
2
  import { TAIL_MAX_LINES } from '../support-bundle.js';
3
+ import { DEFAULT_LOCAL_BUMP, DEFAULT_RECALL_BUDGET } from '../search/types.js';
4
+ import { DEFAULT_ASSEMBLE_BUDGET } from '../api/assemble.js';
3
5
  export function printAuditPruneUsage() {
4
6
  console.log('hippo audit prune --older-than <Nd> [--dry-run] [--tenant <t>]');
5
7
  console.log(' --older-than <Nd> Delete audit_log rows with ts older than N days (e.g. 90d).');
@@ -102,7 +104,7 @@ export const VERB_USAGE = {
102
104
  --global Store in global store ($HIPPO_HOME or ~/.hippo/)`],
103
105
  recall: [`
104
106
  recall <query> Search and retrieve memories (local + global)
105
- --budget <n> Token budget for the whole printed block (default: 4000)
107
+ --budget <n> Token budget for the whole printed block (default: ${DEFAULT_RECALL_BUDGET})
106
108
  --min-results <n> Minimum results regardless of budget (default: 1)
107
109
  --json Output as JSON
108
110
  --why Show match reasons and source annotations
@@ -193,7 +195,7 @@ export const VERB_USAGE = {
193
195
  --json Output as JSON`],
194
196
  assemble: [`
195
197
  assemble --session <id> Build a session's chronological context window
196
- --budget N Token budget for the printed window (default 4000)
198
+ --budget N Token budget for the printed window (default ${DEFAULT_ASSEMBLE_BUDGET})
197
199
  --fresh-tail N Recent rows always kept verbatim (default 10)
198
200
  --no-summarize-older Disable older-row summary substitution
199
201
  --scope <s> Restrict to exact scope (default: deny *:private:*)
@@ -205,7 +207,7 @@ export const VERB_USAGE = {
205
207
  --pin Pin the new memory (default: pinned if the old one was)`],
206
208
  explain: [`
207
209
  explain <query> Show full score breakdown for each retrieved memory
208
- --budget <n> Token budget, counted as recall prints (default: 4000)
210
+ --budget <n> Token budget, counted as recall prints (default: ${DEFAULT_RECALL_BUDGET})
209
211
  --limit <n> Cap the number of results displayed
210
212
  --json Output as JSON
211
213
  --physics | --classic Force search mode (default: from config)
@@ -221,7 +223,7 @@ export const VERB_USAGE = {
221
223
  --no-mmr Disable MMR for this eval run
222
224
  --mmr-lambda <f> Override MMR lambda for this run
223
225
  --embedding-weight <f> Override cosine weight (default: 0.6)
224
- --local-bump <f> Local-over-global priority multiplier (default: 1.2)
226
+ --local-bump <f> Local-over-global priority multiplier (default: ${DEFAULT_LOCAL_BUMP})
225
227
  --equal-sources Shortcut for --local-bump 1.0
226
228
  --min-mrr <f> Exit non-zero if mean MRR falls below this
227
229
  --json Output full summary as JSON`],
@@ -393,8 +395,9 @@ export const VERB_USAGE = {
393
395
  Fold one project name into another (dry run unless --apply;
394
396
  writes a backup and one audit event first)
395
397
  projects repair [--apply]
396
- Re-tag merged rows older versions of sleep saved as user-global,
397
- by their parents' project (dry run unless --apply)`],
398
+ Set aside misfiled note imports, fold old project names into
399
+ their ids, re-tag merged rows (dry run unless --apply;
400
+ writes a backup and one audit event first)`],
398
401
  quarantine: [`
399
402
  quarantine [list] List memories a connector flagged as an instruction attempt, pending review
400
403
  --all Include approved and rejected rows too (default: pending only)
package/dist/cli.d.ts CHANGED
@@ -253,7 +253,7 @@ export declare const COMMANDS: {
253
253
  usage: string[];
254
254
  };
255
255
  hook: {
256
- run: ({ args, flags }: CommandContext) => Promise<void>;
256
+ run: ({ args }: CommandContext) => Promise<void>;
257
257
  usage: string[];
258
258
  };
259
259
  setup: {