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
@@ -12,24 +12,13 @@ import { duplicateKey } from '../same-text.js';
12
12
  import { isContentWorthStoring } from '../audit.js';
13
13
  // Sentence-level patterns
14
14
  //
15
- // T1 (DF2): each pattern now carries TWO capture groups — group 1 is the
16
- // discriminating keyword (plus its trailing separator, verbatim), group 2 is
17
- // the content that follows it. Previously only the after-keyword content was
18
- // captured, so a negation like "never" / "must not" was discarded and a
19
- // prohibition inverted into an instruction ("Never use X" stored as "use X").
20
- // `extractFromPatterns` reassembles group1 + a clause-bounded group2 (T2, via
21
- // `boundToClause` below) rather than reading a single fixed-width group —
22
- // group 2's own reach is widened to {1,500} because the true stopping point
23
- // is now found by content, not counted characters. Keeping the keyword in
24
- // its own group (rather than folding it into one bigger capture) matters:
25
- // `boundToClause` must scan for a clause boundary only in group 2, never in
26
- // group 1 — several keywords end in their own colon ("error:", "rule:",
27
- // "decision:") which is not a clause boundary in the prose sense and would
28
- // wrongly truncate the capture down to just the keyword if scanned.
29
- // `PREFERENCE_PATTERNS[0]` is the one exception — its two-capture-group
30
- // shape means something different (two content spans either side of
31
- // "instead of"/"over"/"not") and is a separate, backlogged defect (see
32
- // docs/plans/2026-08-23-df2-capture-anchoring.md); left as-is.
15
+ // Each pattern carries TWO capture groups: group 1 is the discriminating keyword (plus its
16
+ // trailing separator, verbatim), group 2 the content after it, so a negation like "never" survives
17
+ // ("Never use X" must not store as "use X"). `extractFromPatterns` joins group1 + a clause-bounded
18
+ // group2 (`boundToClause`), so group 2 may reach {1,500}. The keyword keeps its own group because
19
+ // keywords like "error:" end in a colon that is not a prose clause boundary.
20
+ // `PREFERENCE_PATTERNS[0]` is the exception: its two groups are the content spans either side of
21
+ // "instead of"/"over"/"not"; left as-is.
33
22
  const DECISION_PATTERNS = [
34
23
  /(?:we(?:'ve| have)?|i(?:'ve| have)?|let's)\s+(decid(?:ed|e)\s+(?:to\s+)?)(.{1,500})/i,
35
24
  /(?:let's|we(?:'ll| will| should)?)\s+((?:go with|do|use|try|build|implement|switch to)\s+)(.{1,500})/i,
@@ -49,9 +38,8 @@ const ERROR_PATTERNS = [
49
38
  /((?:the (?:issue|problem|fix) (?:is|was))\s+)(.{1,500})/i,
50
39
  /((?:don't forget|easy to miss):\s*)(.{1,500})/i,
51
40
  ];
52
- // PREFERENCE_PATTERNS[0] keeps its pre-DF2 two-capture-group shape
53
- // (match[1]-only, unbounded) — out of scope here, backlogged. See
54
- // extractFromPatterns' reference check against this exact array element.
41
+ // PREFERENCE_PATTERNS[0] keeps its match[1]-only, unbounded shape; extractFromPatterns
42
+ // checks for this exact array element.
55
43
  const PREFERENCE_PATTERNS = [
56
44
  /(?:prefer|use)\s+(.{5,100})\s+(?:instead of|over|not)\s+(.{3,100})/i,
57
45
  /((?:don't use|avoid|skip)\s+)(.{1,500})/i,
@@ -72,52 +60,20 @@ export function splitSentences(text) {
72
60
  .map((s) => s.trim())
73
61
  .filter((s) => s.length > 5);
74
62
  }
75
- /**
76
- * T2 (DF2): bound a keyword+content capture to its clause instead of a fixed
77
- * character count. `full` is `keywordPrefix + content` (already concatenated
78
- * so the 200-char ceiling below applies to the whole stored string, not just
79
- * the part after the keyword); `searchFrom` is `keywordPrefix.length`, so the
80
- * clause/terminator scan only ever looks INSIDE `content` — several keywords
81
- * end in their own colon ("error:", "rule:", "decision:") which must never
82
- * be mistaken for a clause boundary in the prose that follows.
83
- *
84
- * Stops at the first `,`/`;`/`:` followed by whitespace, or at a sentence
85
- * terminator `[.!?]` followed by whitespace or end-of-string — whichever
86
- * comes first — and never past `maxLen` chars total.
87
- *
88
- * The whitespace requirement on the terminator is load-bearing: a bare
89
- * `[.!?]` would split inside a token like `.env` / `capture.ts` / `v1.35.0`,
90
- * turning a full clause into a fragment that then fails the write gate and
91
- * is silently dropped (measured in the plan). The `maxLen` ceiling is also
92
- * load-bearing: without it, a clause-free span can run past the 500-char
93
- * gate in `extractFromPatterns` and the whole match is dropped, where today
94
- * it is truncated and stored.
95
- */
96
- /**
97
- * Does a plausible CLOSING single quote appear after `from`? Mirror of the
98
- * opener rule in `boundToClause`: a closer sits tight against the literal it
99
- * ends (non-whitespace before) and is followed by whitespace, punctuation, or
100
- * end-of-string - never by a letter, which is what makes "user's" an
101
- * apostrophe rather than a partner.
102
- */
103
63
  function isLetterOrDigit(ch) {
104
64
  return ch !== undefined && /[\p{L}\p{N}]/u.test(ch);
105
65
  }
66
+ /** The last plausible CLOSING single quote, or -1. Mirror of the opener rule in `boundToClause`: a closer sits tight
67
+ * against its literal and is never followed by a letter, which is what makes "user's" an apostrophe. */
106
68
  function lastCloserIndex(full) {
107
- // ONE pass, not one per apostrophe. The previous shape rescanned the whole
108
- // remaining suffix at every boundary apostrophe, so a transcript full of
109
- // elisions ("keep 'em", "wait 'til", ...) made extraction quadratic and
110
- // could stall a moderately sized capture. Codex P2, r7.
69
+ // ONE pass, not one per apostrophe: a rescan per apostrophe goes quadratic
70
+ // on a transcript full of elisions ("keep 'em", "wait 'til", ...).
111
71
  //
112
- // A quote CLOSES THE SIDE IT IS TIGHT AGAINST. The test needs both
113
- // neighbours, and one round proved that empirically: these two have the
114
- // same following character and opposite roles -
72
+ // A quote CLOSES THE SIDE IT IS TIGHT AGAINST, so the test needs both
73
+ // neighbours: these two have the same following character and opposite roles -
115
74
  // "preserve 'a, b'-style" next '-' -> CLOSER, content on the left
116
75
  // "then run '--force," next '-' -> OPENER, content on the right
117
- // so no forward-only rule can separate them. An earlier revision tried
118
- // exactly that and broke in both directions at once (codex P1+P2, r10):
119
- // it missed closers before token-joining punctuation and accepted
120
- // "'.env" as a closer because a dot happened to be in its class.
76
+ // so no forward-only rule can separate them.
121
77
  //
122
78
  // never a closer next is a letter/digit "user's", "keep 'em"
123
79
  // closer prev is non-whitespace "'a, b' to", "'a, b'-style"
@@ -137,11 +93,10 @@ function lastCloserIndex(full) {
137
93
  // "tight against content" excludes an OPENING delimiter: in
138
94
  // "call parse('--force" the paren is non-whitespace but the quote after
139
95
  // it is an opener, and treating it as a closer let an earlier elision
140
- // pair across the clause boundary. Codex P2, r11.
96
+ // pair across the clause boundary.
141
97
  const tightBefore = prev !== undefined && !/[\s([{]/.test(prev);
142
98
  const endsAfter = next === undefined || /\s/.test(next);
143
- // Punctuation splits in two, and this is the fact the last three
144
- // revisions kept rediscovering piecemeal:
99
+ // Punctuation splits in two:
145
100
  // , ; : ! ? ) ] } never join tokens - they end the literal whatever
146
101
  // follows, so "'echo a, b ';then" closes at the ;
147
102
  // . - _ / @ # DO join tokens - "'.env" and "'--force" continue a
@@ -156,17 +111,12 @@ function lastCloserIndex(full) {
156
111
  }
157
112
  return -1;
158
113
  }
114
+ /** Cuts `full` (keyword + content) at the first prose `,;:` or `[.!?]` followed by whitespace, scanning from `searchFrom` so a keyword's
115
+ * own colon never counts; the whitespace rule keeps `.env` whole and `maxLen` truncates a long span the 500-char gate would drop. */
159
116
  function boundToClause(full, searchFrom, maxLen = 200) {
160
- // Scan for the first PROSE clause boundary after `searchFrom`.
161
- //
162
- // Depth-awareness is not a nicety here: hippo memories are full of code, and
163
- // a naive scan reintroduces the exact fragment defect this whole change
164
- // exists to remove. Measured before this guard existed:
117
+ // Depth-awareness matters because memories are full of code; a naive scan cuts
165
118
  // "Always call build(x, y) before deploy." -> "Always call build(x"
166
- // "Never pass {a: 1, b: 2} to the writer." -> "Never pass {a"
167
- // Both then PASS the write gate, because they contain code punctuation and
168
- // so read as "specific" — a malformed fragment stored with high confidence.
169
- // Codex review finding (P1) on this branch.
119
+ // and the fragment PASSES the write gate, since code punctuation reads as "specific".
170
120
  //
171
121
  // So: a separator only ends the clause at bracket depth zero and outside
172
122
  // quotes. Unbalanced closers are tolerated (depth floors at 0) because
@@ -222,15 +172,8 @@ function boundToClause(full, searchFrom, maxLen = 200) {
222
172
  return bounded;
223
173
  }
224
174
  function closesQuote(full, i, quote) {
225
- // Closing uses the SAME shape test as opening. This was asymmetric:
226
- // twelve rounds went into deciding when a quote OPENS a literal, while
227
- // the close accepted any bare "'" - so the apostrophe in a possessive
228
- // INSIDE a literal closed it early:
229
- // "Always pass 'user's a, b list' to the parser."
230
- // -> "Always pass 'user's a" (master kept the whole literal)
231
- // a mid-literal fragment that then PASSES the write gate, which is
232
- // precisely the defect this branch exists to remove. Found by the
233
- // ship-gate review after every earlier gate missed it.
175
+ // Closing uses the SAME shape test as opening, else a possessive INSIDE a literal closes it early:
176
+ // "Always pass 'user's a, b list' to the parser." -> "Always pass 'user's a"
234
177
  return full[i] === quote && (quote !== "'" || !isLetterOrDigit(full[i + 1]));
235
178
  }
236
179
  function opensSingleQuote(full, i, lastCloser) {
@@ -242,7 +185,6 @@ function opensSingleQuote(full, i, lastCloser) {
242
185
  // restart..." never leaves quote mode, bounding disabled entirely
243
186
  // treat no ' as a quote -> "Always pass 'a, b' to the parser."
244
187
  // cuts at the comma and stores "Always pass 'a"
245
- // Both were codex P1s on this branch, in consecutive rounds.
246
188
  //
247
189
  // Discriminator: a ' OPENS a literal only at a word boundary - preceded
248
190
  // by start/whitespace/open-bracket AND followed by non-whitespace. An
@@ -257,7 +199,7 @@ function opensSingleQuote(full, i, lastCloser) {
257
199
  // have no closer, so the scanner never leaves quote mode and bounding
258
200
  // is disabled for the rest of the capture. Requiring an actual closing
259
201
  // quote later in the string replaces a guess with a checkable fact -
260
- // an elided form simply has no partner. Codex P2, this round.
202
+ // an elided form simply has no partner.
261
203
  // ...and the partner must LOOK like a closer, not merely be another
262
204
  // apostrophe. Distance cannot separate the two cases - both put a `'`
263
205
  // far downstream:
@@ -268,7 +210,7 @@ function opensSingleQuote(full, i, lastCloser) {
268
210
  // elision, disabling bounding for the rest of the capture
269
211
  // So apply the SAME shape rule already used to open a literal, mirrored:
270
212
  // a closer has non-whitespace before it and whitespace/punctuation (not
271
- // a letter) after. "user's" fails it on both counts. Codex P2, r6.
213
+ // a letter) after. "user's" fails it on both counts.
272
214
  return atBoundary && lastCloser > i;
273
215
  }
274
216
  function cleanExtract(raw) {
@@ -276,7 +218,7 @@ function cleanExtract(raw) {
276
218
  .replace(/^[:\s-]+/, '')
277
219
  .replace(/[.!?,;:\s]+$/, '')
278
220
  .trim();
279
- // T2 trailing cleanup: clause-bounding can cut inside a parenthetical and
221
+ // Trailing cleanup: clause-bounding can cut inside a parenthetical and
280
222
  // leave an unmatched trailing ')' (e.g. "...(two had never got entries),"
281
223
  // bounds to "never got entries)"). Strip a trailing ')' ONLY while closes
282
224
  // outnumber opens in the string so far — a balanced parenthetical like
@@ -316,51 +258,31 @@ function extractFromPatterns(sentence, patterns, category, tag) {
316
258
  if (match) {
317
259
  let bounded;
318
260
  if (pat === PREFERENCE_PATTERNS[0]) {
319
- // PREFERENCE_PATTERNS[0] is the one pattern left out of T1/T2 (see
320
- // comment at its definition) — its match[1] keeps its pre-DF2,
321
- // unbounded shape rather than going through clause-bounding.
261
+ // PREFERENCE_PATTERNS[0] skips clause-bounding (see comment at its definition).
322
262
  bounded = match[1] ?? match[0];
323
263
  }
324
264
  else {
325
265
  // group1 = keyword + separator (verbatim, never clause-bounded);
326
266
  // group2 = the content that follows it (clause-bounded below).
327
267
  //
328
- // T1 preserves the keyword only when it carries SEMANTIC SIGN — a
268
+ // The keyword is kept only when it carries SEMANTIC SIGN, a
329
269
  // negation or modality ("never", "must not", "do not ever",
330
- // "always"). Dropping those inverts the meaning, which is the whole
331
- // point of T1. A LABEL keyword ("decision:", "rule:", "error:",
270
+ // "always"), because dropping those inverts the meaning. A LABEL keyword ("decision:", "rule:", "error:",
332
271
  // "important:") carries no sign: it only names the category, which
333
272
  // is already recorded in `category`/`tags`, so prefixing it onto the
334
273
  // content is duplication that also breaks value-keyed matching
335
- // (AT1's rejected-value digest hashes the bare content).
274
+ // (the rejected-value digest hashes the bare content).
336
275
  // Discriminator: a label ends in its own colon, or is a "the X is"
337
276
  // phrase.
338
277
  const rawPrefix = match[1] ?? '';
339
- // ONLY colon-terminated labels are dropped. Dropping "the X is/was"
340
- // too left the residue starting with "to ", which isFragment then
341
- // rejected outright - so "The plan is to ship on Friday" and "The fix
342
- // was to bump the pool timeout" stored NOTHING, where every prior
343
- // version stored them. Silent loss on two of the highest-traffic
344
- // patterns, and invisible: an absent memory leaves no trace. The AT1
345
- // rejected-value evidence only ever involved colon labels
346
- // ("decision: "), so the narrower rule keeps that guarantee.
278
+ // ONLY colon-terminated labels are dropped: dropping "the X is/was" leaves a residue starting
279
+ // with "to ", which isFragment rejects, so "The plan is to ship on Friday" would store nothing.
347
280
  const isLabelPrefix = /:\s*$/.test(rawPrefix);
348
281
  const keywordPrefix = isLabelPrefix ? '' : rawPrefix;
349
- // Scan the UNTRUNCATED remainder, not match[2]. The patterns cap
350
- // their content group at 500 chars, so a quoted literal whose closer
351
- // sits past that point had no visible partner and the pairing check
352
- // read the opener as prose - cutting inside the literal. That was a
353
- // blindness built into the scanner's INPUT, not a bad predicate, so
354
- // no further predicate could have fixed it. boundToClause already
355
- // caps its OUTPUT at maxLen, so widening the input costs nothing and
356
- // makes pairing decidable on the whole sentence. Codex P2, round 5.
357
- // Group 2's REAL offset, read from the regex engine. Deriving it as
358
- // `match.index + rawPrefix.length` assumes group 1 starts the match,
359
- // but DECISION_PATTERNS carry an uncaptured subject ("we ", "let's ")
360
- // ahead of it - so the offset landed inside the keyword and the
361
- // widened slice duplicated text: "We decided to pin..." stored as
362
- // "decided to to pin...". Real corruption of the commonest decision
363
- // capture, shipped in the previous commit. Codex P1, r6.
282
+ // Scan the UNTRUNCATED remainder, not match[2]: a quoted literal whose closer sits past the
283
+ // 500-char group cap would read as prose; boundToClause caps its OUTPUT, so this costs nothing.
284
+ // Group 2's REAL offset from the regex engine: DECISION_PATTERNS carry an uncaptured subject
285
+ // ("we ", "let's ") ahead of group 1, so `match.index + rawPrefix.length` lands inside the keyword.
364
286
  const contentStart = match.indices?.[2]?.[0] ?? -1;
365
287
  const afterKeyword = contentStart >= 0 ? sentence.slice(contentStart) : (match[2] ?? match[0]);
366
288
  const keywordStart = match.indices?.[1]?.[0] ?? -1;
@@ -1,4 +1,4 @@
1
- import type { JsonValue } from './working-memory.js';
1
+ import type { JsonValue } from './json.js';
2
2
  /** Why a failure was not stored, or `stored`. */
3
3
  export type CaptureErrorOutcome = 'stored' | 'duplicate' | 'skipped-interrupt' | 'skipped-routine' | 'skipped-invalid';
4
4
  /** Which routine check skipped a failure; the log keeps it so declines can be told apart from empty searches. */
@@ -1,6 +1,6 @@
1
1
  // `hippo capture-error`, run by the Claude Code PostToolUseFailure hook: routine failures and repeats are not
2
2
  // stored, because error memories decay slowly and would crowd out real lessons; what is stored stays `observed`
3
- // until outcome feedback confirms it. Every failure, stored or not, goes to the failure log (ROADMAP CD13).
3
+ // until outcome feedback confirms it. Every failure, stored or not, goes to the failure log.
4
4
  import { createMemory } from './memory.js';
5
5
  import { writeEntry } from './store/entry-writes.js';
6
6
  import { loadContentsWithTag } from './store/entry-reads.js';
@@ -1,4 +1,4 @@
1
- /** Git subprocess helpers for FE2 churn-staleness (src/invalidation.ts). */
1
+ /** Git subprocess helpers for churn-staleness (src/invalidation.ts). */
2
2
  /** A real git failure (not git grep's expected exit-1-no-match). Callers abort the whole run on this. */
3
3
  export declare class GitReadError extends Error {
4
4
  }
package/dist/churn-git.js CHANGED
@@ -1,4 +1,4 @@
1
- /** Git subprocess helpers for FE2 churn-staleness (src/invalidation.ts). */
1
+ /** Git subprocess helpers for churn-staleness (src/invalidation.ts). */
2
2
  import { execFileSync } from 'child_process';
3
3
  import * as fs from 'fs';
4
4
  import * as os from 'os';
package/dist/cli/audit.js CHANGED
@@ -9,7 +9,7 @@ import { pruneAuditLog, parseOlderThanFlag } from '../audit-prune.js';
9
9
  import { printError } from './output.js';
10
10
  import { requireInit, resolveAuthRoot } from './shared.js';
11
11
  // ---------------------------------------------------------------------------
12
- // Audit log subcommands (A5 stub auth — `hippo audit list`)
12
+ // Audit log subcommands (`hippo audit list`)
13
13
  // ---------------------------------------------------------------------------
14
14
  const VALID_AUDIT_OPS = new Set(AUDIT_OPS);
15
15
  function formatAuditRow(ev) {
@@ -23,8 +23,7 @@ function cmdAuditList(hippoRoot, flags) {
23
23
  const tenantId = resolveTenantId({});
24
24
  const opFlag = typeof flags['op'] === 'string' ? flags['op'] : undefined;
25
25
  if (opFlag && !VALID_AUDIT_OPS.has(opFlag)) {
26
- // Regenerate from Set to prevent future drift (v1.11.5: pre-v1.11.5 message
27
- // was hand-maintained and had drifted — missed 'auth_revoke' and 'outcome').
26
+ // Built from the Set so the message cannot drift from the valid ops.
28
27
  const expected = Array.from(VALID_AUDIT_OPS).join(' | ');
29
28
  printError(`Unknown --op value: ${opFlag}. Expected one of: ${expected}.`);
30
29
  process.exit(1);
@@ -115,7 +114,7 @@ function cmdAuditLog(hippoRoot, args, flags) {
115
114
  process.exit(1);
116
115
  }
117
116
  export function handleAudit({ hippoRoot, args, flags }) {
118
- // `audit list` and `audit prune` -> A5 audit-log subcommands.
117
+ // `audit list` and `audit prune` -> audit-log subcommands.
119
118
  // Other forms (no sub, --fix) keep the existing memory-quality auditor
120
119
  // for backwards compatibility.
121
120
  if (args[0] === 'list' || args[0] === 'prune') {
package/dist/cli/auth.js CHANGED
@@ -6,16 +6,14 @@ import { resolveTenantId } from '../tenant.js';
6
6
  import { printError } from './output.js';
7
7
  import { resolveAuthRoot } from './shared.js';
8
8
  // ---------------------------------------------------------------------------
9
- // Auth subcommands (A5 stub auth)
9
+ // Auth subcommands
10
10
  // ---------------------------------------------------------------------------
11
11
  function cmdAuthCreate(hippoRoot, flags) {
12
12
  const root = resolveAuthRoot(hippoRoot, flags);
13
13
  const tenantFlag = typeof flags['tenant'] === 'string' ? flags['tenant'] : undefined;
14
14
  const labelFlag = typeof flags['label'] === 'string' ? flags['label'] : undefined;
15
15
  const asJson = Boolean(flags['json']);
16
- // v1.12.3: --role flag surfaces the api_keys.role column added v1.12.0
17
- // sub-1. Accepts 'admin' | 'member' only; anything else exits 1 with a
18
- // typed error so a typo doesn't silently default to admin.
16
+ // Accepts 'admin' | 'member' only; anything else exits 1 so a typo doesn't silently default to admin.
19
17
  const roleFlag = typeof flags['role'] === 'string' ? flags['role'] : undefined;
20
18
  let role = 'admin';
21
19
  if (roleFlag !== undefined) {
@@ -57,7 +55,6 @@ function formatKeyRow(item) {
57
55
  const label = item.label ?? '-';
58
56
  const created = item.createdAt;
59
57
  const revoked = item.revokedAt ?? '-';
60
- // v1.12.3: role column surfaced
61
58
  return `${item.keyId} ${item.tenantId} ${item.role} ${label} ${created} ${revoked}`;
62
59
  }
63
60
  function cmdAuthList(hippoRoot, flags) {
@@ -111,7 +108,7 @@ function cmdAuthRevoke(hippoRoot, keyId, flags) {
111
108
  }
112
109
  console.log(`Revoked ${keyId} at ${revokedAt}`);
113
110
  }
114
- /** EI2: `hippo auth grant|ungrant <key_id> <scope>`, routed through api so the tenant, restricted-scope and audit checks live in one place. */
111
+ /** `hippo auth grant|ungrant <key_id> <scope>`, routed through api so the tenant, restricted-scope and audit checks live in one place. */
115
112
  function cmdAuthScopeGrant(hippoRoot, keyId, scope, grant, flags) {
116
113
  const ctx = keyContext(resolveAuthRoot(hippoRoot, flags), keyId);
117
114
  try {