hippo-memory 1.71.0 → 2.0.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 (625) hide show
  1. package/README.md +7 -5
  2. package/dist/agent-memories/apply.d.ts +0 -11
  3. package/dist/agent-memories/apply.js +11 -26
  4. package/dist/agent-memories/claude-code.js +1 -1
  5. package/dist/agent-memories/codex.js +1 -1
  6. package/dist/agent-memories/copilot.d.ts +1 -3
  7. package/dist/agent-memories/copilot.js +1 -17
  8. package/dist/agent-memories/project-folds.d.ts +10 -0
  9. package/dist/agent-memories/project-folds.js +34 -0
  10. package/dist/agent-memories/sync.js +8 -6
  11. package/dist/api/assemble.js +3 -2
  12. package/dist/api/audit.d.ts +30 -2
  13. package/dist/api/audit.js +48 -7
  14. package/dist/api/auth.d.ts +11 -9
  15. package/dist/api/auth.js +39 -40
  16. package/dist/api/auto-sleep.d.ts +5 -2
  17. package/dist/api/auto-sleep.js +7 -3
  18. package/dist/api/availability.js +2 -3
  19. package/dist/api/cards.d.ts +42 -0
  20. package/dist/api/cards.js +52 -0
  21. package/dist/api/conflicts.d.ts +28 -0
  22. package/dist/api/conflicts.js +61 -0
  23. package/dist/api/context-auto.js +4 -2
  24. package/dist/api/context-render.js +4 -3
  25. package/dist/api/context-select.js +15 -29
  26. package/dist/api/context.js +29 -18
  27. package/dist/api/continuity.d.ts +47 -0
  28. package/dist/api/continuity.js +66 -0
  29. package/dist/api/dormant.js +4 -2
  30. package/dist/api/drill-down.js +3 -2
  31. package/dist/api/embeddings.d.ts +17 -0
  32. package/dist/api/embeddings.js +28 -0
  33. package/dist/api/error-reply.d.ts +8 -0
  34. package/dist/api/error-reply.js +21 -0
  35. package/dist/api/forget.d.ts +7 -3
  36. package/dist/api/forget.js +24 -14
  37. package/dist/api/goals.d.ts +1 -1
  38. package/dist/api/goals.js +5 -9
  39. package/dist/api/graph.js +2 -1
  40. package/dist/api/index.d.ts +4 -2
  41. package/dist/api/index.js +4 -2
  42. package/dist/api/instruction-detect.js +5 -1
  43. package/dist/api/learn.d.ts +5 -1
  44. package/dist/api/learn.js +71 -44
  45. package/dist/api/ledger-db.d.ts +2 -6
  46. package/dist/api/ledger-db.js +4 -36
  47. package/dist/api/memories.d.ts +21 -1
  48. package/dist/api/memories.js +51 -1
  49. package/dist/api/objects.d.ts +1 -1
  50. package/dist/api/objects.js +2 -1
  51. package/dist/api/outcome.d.ts +5 -5
  52. package/dist/api/outcome.js +17 -16
  53. package/dist/api/pilot-arm.d.ts +1 -11
  54. package/dist/api/pilot-arm.js +5 -45
  55. package/dist/api/predictions.d.ts +3 -1
  56. package/dist/api/predictions.js +8 -1
  57. package/dist/api/projects.d.ts +21 -0
  58. package/dist/api/projects.js +24 -0
  59. package/dist/api/promote.d.ts +3 -3
  60. package/dist/api/promote.js +26 -28
  61. package/dist/api/prompt-hook.d.ts +17 -2
  62. package/dist/api/prompt-hook.js +45 -4
  63. package/dist/api/quarantine.js +2 -1
  64. package/dist/api/recall-cli.d.ts +3 -0
  65. package/dist/api/recall-cli.js +13 -4
  66. package/dist/api/recall-core.js +10 -5
  67. package/dist/api/recall-pipeline.js +23 -24
  68. package/dist/api/recall-request.js +1 -1
  69. package/dist/api/recall-types.d.ts +3 -1
  70. package/dist/api/recall.d.ts +0 -3
  71. package/dist/api/recall.js +51 -73
  72. package/dist/api/remember-local.d.ts +5 -2
  73. package/dist/api/remember-local.js +15 -7
  74. package/dist/api/remember.d.ts +7 -3
  75. package/dist/api/remember.js +19 -16
  76. package/dist/api/session-end.d.ts +46 -0
  77. package/dist/api/session-end.js +131 -0
  78. package/dist/api/sleep-run.d.ts +1 -1
  79. package/dist/api/sleep-run.js +16 -11
  80. package/dist/api/sleep.js +4 -3
  81. package/dist/api/status.d.ts +24 -0
  82. package/dist/api/status.js +40 -0
  83. package/dist/api/store-init.d.ts +4 -0
  84. package/dist/api/store-init.js +9 -0
  85. package/dist/api/tokens.js +2 -2
  86. package/dist/api/types.d.ts +3 -7
  87. package/dist/api/types.js +6 -1
  88. package/dist/capture/caller-session.d.ts +0 -3
  89. package/dist/capture/caller-session.js +1 -12
  90. package/dist/capture/capture-error.d.ts +2 -2
  91. package/dist/capture/capture-error.js +23 -18
  92. package/dist/capture/codex-patch.js +3 -1
  93. package/dist/capture/command.js +36 -95
  94. package/dist/capture/compact.js +10 -5
  95. package/dist/capture/compaction-items-caller.js +11 -20
  96. package/dist/capture/compaction-record.d.ts +7 -93
  97. package/dist/capture/compaction-record.js +29 -426
  98. package/dist/capture/compaction-spool.d.ts +7 -7
  99. package/dist/capture/compaction-spool.js +60 -51
  100. package/dist/capture/copilot-transcript.d.ts +2 -1
  101. package/dist/capture/copilot-transcript.js +2 -1
  102. package/dist/capture/extract.d.ts +5 -2
  103. package/dist/capture/extract.js +4 -3
  104. package/dist/capture/failure-caller.d.ts +1 -1
  105. package/dist/capture/failure-caller.js +21 -16
  106. package/dist/capture/failure-reading.d.ts +2 -1
  107. package/dist/capture/failure-reading.js +7 -9
  108. package/dist/capture/pre-compact-caller.js +5 -4
  109. package/dist/capture/session-digest.js +4 -3
  110. package/dist/capture/session-end-caller.js +3 -2
  111. package/dist/capture/session-worker.js +22 -10
  112. package/dist/capture/transcript.d.ts +2 -1
  113. package/dist/capture/transcript.js +6 -3
  114. package/dist/capture/working-state.d.ts +4 -2
  115. package/dist/capture/working-state.js +4 -2
  116. package/dist/cli/audit-prune.d.ts +0 -25
  117. package/dist/cli/audit-prune.js +1 -27
  118. package/dist/cli/audit.js +20 -20
  119. package/dist/cli/auth.d.ts +1 -1
  120. package/dist/cli/auth.js +16 -17
  121. package/dist/cli/briefs.d.ts +1 -4
  122. package/dist/cli/briefs.js +37 -292
  123. package/dist/cli/card.js +41 -33
  124. package/dist/cli/client.d.ts +6 -2
  125. package/dist/cli/client.js +37 -21
  126. package/dist/cli/compact-resume-payload.js +1 -1
  127. package/dist/cli/context.d.ts +1 -1
  128. package/dist/cli/context.js +13 -13
  129. package/dist/cli/continuity.d.ts +1 -1
  130. package/dist/cli/continuity.js +36 -39
  131. package/dist/cli/curate.d.ts +2 -2
  132. package/dist/cli/curate.js +32 -36
  133. package/dist/cli/dag.d.ts +1 -1
  134. package/dist/cli/dag.js +23 -8
  135. package/dist/cli/decisions.d.ts +0 -2
  136. package/dist/cli/decisions.js +7 -285
  137. package/dist/cli/eval.d.ts +1 -1
  138. package/dist/cli/eval.js +12 -13
  139. package/dist/cli/explain.d.ts +1 -1
  140. package/dist/cli/explain.js +21 -25
  141. package/dist/cli/flag-values.js +16 -6
  142. package/dist/cli/flags.d.ts +0 -289
  143. package/dist/cli/flags.js +3 -138
  144. package/dist/cli/graph.d.ts +3 -0
  145. package/dist/cli/graph.js +83 -0
  146. package/dist/cli/hook-runtime.d.ts +6 -6
  147. package/dist/cli/hook-runtime.js +9 -36
  148. package/dist/cli/incidents.d.ts +3 -0
  149. package/dist/cli/incidents.js +112 -0
  150. package/dist/cli/init.d.ts +1 -1
  151. package/dist/cli/init.js +28 -19
  152. package/dist/cli/install-steps.d.ts +2 -1
  153. package/dist/cli/install-steps.js +11 -3
  154. package/dist/cli/last-sleep.js +1 -1
  155. package/dist/cli/maintenance.d.ts +3 -3
  156. package/dist/cli/maintenance.js +29 -30
  157. package/dist/cli/notes.d.ts +2 -0
  158. package/dist/cli/notes.js +41 -0
  159. package/dist/cli/object-verbs.d.ts +2 -2
  160. package/dist/cli/object-verbs.js +4 -4
  161. package/dist/cli/open-browser.d.ts +14 -0
  162. package/dist/cli/open-browser.js +26 -0
  163. package/dist/cli/playbooks.d.ts +3 -4
  164. package/dist/cli/playbooks.js +126 -290
  165. package/dist/cli/predictions.d.ts +3 -0
  166. package/dist/cli/predictions.js +174 -0
  167. package/dist/cli/print.js +2 -1
  168. package/dist/cli/projects.js +30 -22
  169. package/dist/cli/quality-repair-once.d.ts +1 -1
  170. package/dist/cli/quality-repair-once.js +6 -5
  171. package/dist/cli/recall.js +13 -7
  172. package/dist/cli/refine-llm.js +18 -17
  173. package/dist/cli/remember.js +17 -17
  174. package/dist/cli/scheduler.js +2 -1
  175. package/dist/cli/serve.js +11 -6
  176. package/dist/cli/session-hooks.d.ts +1 -5
  177. package/dist/cli/session-hooks.js +89 -168
  178. package/dist/cli/setup.d.ts +1 -1
  179. package/dist/cli/setup.js +17 -10
  180. package/dist/cli/shared.d.ts +2 -6
  181. package/dist/cli/shared.js +9 -29
  182. package/dist/cli/sleep-render.js +6 -3
  183. package/dist/cli/sleep.js +6 -37
  184. package/dist/cli/status.d.ts +3 -3
  185. package/dist/cli/status.js +32 -35
  186. package/dist/cli/transfer.d.ts +3 -3
  187. package/dist/cli/transfer.js +64 -43
  188. package/dist/cli/usage.d.ts +1 -80
  189. package/dist/cli/usage.js +2 -696
  190. package/dist/cli/verb-row.d.ts +34 -0
  191. package/dist/cli/verb-row.js +6 -0
  192. package/dist/cli/verbs/health.d.ts +27 -0
  193. package/dist/cli/verbs/health.js +234 -0
  194. package/dist/cli/verbs/memory.d.ts +16 -0
  195. package/dist/cli/verbs/memory.js +236 -0
  196. package/dist/cli/verbs/objects.d.ts +20 -0
  197. package/dist/cli/verbs/objects.js +257 -0
  198. package/dist/cli/verbs/sessions.d.ts +31 -0
  199. package/dist/cli/verbs/sessions.js +314 -0
  200. package/dist/cli/verbs.d.ts +81 -0
  201. package/dist/cli/verbs.js +7 -0
  202. package/dist/cli/versioned-verbs.d.ts +51 -0
  203. package/dist/cli/versioned-verbs.js +79 -0
  204. package/dist/cli.d.ts +1 -272
  205. package/dist/cli.js +24 -315
  206. package/dist/connectors/dlq.d.ts +39 -3
  207. package/dist/connectors/dlq.js +39 -4
  208. package/dist/connectors/github/backfill.js +12 -3
  209. package/dist/connectors/github/deletion.js +2 -1
  210. package/dist/connectors/github/dlq.js +31 -73
  211. package/dist/connectors/github/ingest.d.ts +2 -6
  212. package/dist/connectors/github/ingest.js +5 -29
  213. package/dist/connectors/github/tenant-routing.js +2 -1
  214. package/dist/connectors/github/webhook.d.ts +2 -1
  215. package/dist/connectors/github/webhook.js +35 -2
  216. package/dist/connectors/ingest.d.ts +10 -0
  217. package/dist/connectors/ingest.js +29 -0
  218. package/dist/connectors/slack/deletion.js +2 -1
  219. package/dist/connectors/slack/dlq.js +49 -71
  220. package/dist/connectors/slack/ingest.d.ts +2 -6
  221. package/dist/connectors/slack/ingest.js +4 -28
  222. package/dist/connectors/slack/tenant-routing.js +2 -1
  223. package/dist/connectors/slack/webhook.d.ts +2 -1
  224. package/dist/connectors/slack/webhook.js +2 -1
  225. package/dist/consolidate/conflicts.js +2 -2
  226. package/dist/consolidate/dag.js +29 -18
  227. package/dist/consolidate/dedupe.d.ts +7 -0
  228. package/dist/consolidate/dedupe.js +6 -4
  229. package/dist/consolidate/llm-passes.js +45 -19
  230. package/dist/consolidate/memory-value.js +3 -2
  231. package/dist/consolidate/merge.js +8 -8
  232. package/dist/consolidate/physics-pass.d.ts +6 -1
  233. package/dist/consolidate/physics-pass.js +26 -6
  234. package/dist/consolidate/replay.js +11 -4
  235. package/dist/consolidate/sleep.js +9 -6
  236. package/dist/consolidate/traces.js +5 -5
  237. package/dist/core/ablation.d.ts +10 -0
  238. package/dist/core/ablation.js +12 -0
  239. package/dist/{sharing/scope.d.ts → core/active-scope.d.ts} +1 -1
  240. package/dist/{sharing/scope.js → core/active-scope.js} +1 -1
  241. package/dist/core/ambient.d.ts +4 -0
  242. package/dist/core/ambient.js +39 -18
  243. package/dist/core/api-errors.d.ts +16 -0
  244. package/dist/core/api-errors.js +20 -0
  245. package/dist/core/card.js +3 -1
  246. package/dist/core/compaction-timing.d.ts +7 -0
  247. package/dist/core/compaction-timing.js +9 -0
  248. package/dist/core/config.d.ts +1 -1
  249. package/dist/core/config.js +22 -18
  250. package/dist/core/cosine.d.ts +3 -0
  251. package/dist/core/cosine.js +19 -0
  252. package/dist/{store → core}/markdown.d.ts +1 -1
  253. package/dist/{store → core}/markdown.js +4 -4
  254. package/dist/core/memory-audit.d.ts +17 -0
  255. package/dist/core/memory-audit.js +54 -0
  256. package/dist/core/memory.d.ts +14 -8
  257. package/dist/core/memory.js +35 -23
  258. package/dist/{consolidate → core}/merged-row.d.ts +1 -1
  259. package/dist/{consolidate → core}/merged-row.js +1 -1
  260. package/dist/core/physics.d.ts +3 -1
  261. package/dist/core/physics.js +23 -18
  262. package/dist/core/prediction.d.ts +3 -0
  263. package/dist/core/prediction.js +6 -0
  264. package/dist/core/project-identity.d.ts +3 -0
  265. package/dist/core/project-identity.js +12 -2
  266. package/dist/{store → core}/recall-scope.d.ts +1 -10
  267. package/dist/{store → core}/recall-scope.js +2 -19
  268. package/dist/core/search-types.d.ts +1 -1
  269. package/dist/dashboard/dashboard-actions.d.ts +2 -2
  270. package/dist/dashboard/dashboard-actions.js +5 -5
  271. package/dist/dashboard/dashboard-queries.js +2 -7
  272. package/dist/dashboard/dashboard-snapshot.js +1 -3
  273. package/dist/dashboard/dashboard.js +16 -5
  274. package/dist/db/busy.d.ts +2 -7
  275. package/dist/db/busy.js +21 -26
  276. package/dist/db/connect.d.ts +2 -2
  277. package/dist/db/connect.js +7 -7
  278. package/dist/db/index.d.ts +3 -3
  279. package/dist/db/index.js +3 -3
  280. package/dist/db/migrate.js +14 -7
  281. package/dist/db/migrations/index.d.ts +1 -1
  282. package/dist/db/migrations/index.js +3 -1
  283. package/dist/db/migrations/v11.js +1 -0
  284. package/dist/db/migrations/v14.js +1 -0
  285. package/dist/db/migrations/v55.d.ts +3 -0
  286. package/dist/db/migrations/v55.js +17 -0
  287. package/dist/db/open.d.ts +0 -2
  288. package/dist/db/open.js +6 -10
  289. package/dist/db/physics-state.d.ts +0 -4
  290. package/dist/db/physics-state.js +7 -33
  291. package/dist/db/request-stores.d.ts +1 -0
  292. package/dist/db/request-stores.js +2 -1
  293. package/dist/db/tables.d.ts +2 -0
  294. package/dist/db/tables.js +6 -2
  295. package/dist/db/vector-store.js +19 -5
  296. package/dist/db/wal-checkpointer.js +2 -1
  297. package/dist/doctor.d.ts +3 -0
  298. package/dist/doctor.js +18 -15
  299. package/dist/embeddings/local.d.ts +62 -0
  300. package/dist/embeddings/local.js +146 -0
  301. package/dist/{store/embeddings → embeddings}/provider.d.ts +5 -2
  302. package/dist/{store/embeddings → embeddings}/provider.js +19 -25
  303. package/dist/embeddings/transformers.d.ts +26 -0
  304. package/dist/embeddings/transformers.js +89 -0
  305. package/dist/entry/session-text.d.ts +1 -1
  306. package/dist/entry/session-text.js +1 -1
  307. package/dist/entry/store-kit.d.ts +37 -0
  308. package/dist/entry/store-kit.js +25 -0
  309. package/dist/entry/testing.d.ts +6 -0
  310. package/dist/entry/testing.js +7 -0
  311. package/dist/eval/eval-suite.js +17 -11
  312. package/dist/eval/eval.js +1 -1
  313. package/dist/eval/judgment.js +12 -5
  314. package/dist/graph/extract.js +3 -3
  315. package/dist/graph/recall.js +1 -1
  316. package/dist/graph/view.js +13 -5
  317. package/dist/graph/write.d.ts +1 -1
  318. package/dist/graph/write.js +9 -9
  319. package/dist/hooks/codex-session.js +3 -2
  320. package/dist/hooks/codex-wrapper.js +2 -1
  321. package/dist/hooks/json-hooks.js +10 -4
  322. package/dist/hooks/opencode.js +8 -5
  323. package/dist/hooks/shared.d.ts +0 -6
  324. package/dist/hooks/shared.js +4 -15
  325. package/dist/importers/core.js +4 -4
  326. package/dist/importers/sources.js +5 -4
  327. package/dist/importers/vault.d.ts +1 -1
  328. package/dist/importers/vault.js +25 -23
  329. package/dist/index.d.ts +5 -4
  330. package/dist/index.js +4 -3
  331. package/dist/learn/autolearn.d.ts +15 -3
  332. package/dist/learn/autolearn.js +38 -16
  333. package/dist/learn/churn-git.js +13 -4
  334. package/dist/learn/extract.d.ts +3 -2
  335. package/dist/learn/extract.js +7 -5
  336. package/dist/learn/forward-claim-detector.js +4 -2
  337. package/dist/learn/invalidation.d.ts +2 -1
  338. package/dist/learn/invalidation.js +18 -9
  339. package/dist/mcp/admin-tools.d.ts +2 -2
  340. package/dist/mcp/admin-tools.js +27 -48
  341. package/dist/mcp/format.js +3 -2
  342. package/dist/mcp/framing.d.ts +1 -0
  343. package/dist/mcp/framing.js +1 -1
  344. package/dist/mcp/memory-tools.d.ts +1 -1
  345. package/dist/mcp/memory-tools.js +8 -9
  346. package/dist/mcp/protocol.d.ts +11 -7
  347. package/dist/mcp/protocol.js +25 -12
  348. package/dist/mcp/recall-tools.js +12 -9
  349. package/dist/mcp/request.js +22 -20
  350. package/dist/mcp/stdio.js +37 -16
  351. package/dist/mcp/tool-args.js +2 -1
  352. package/dist/mcp/tools.js +44 -21
  353. package/dist/objects/customer-notes.d.ts +2 -2
  354. package/dist/objects/decisions.d.ts +2 -2
  355. package/dist/objects/descriptor.d.ts +1 -1
  356. package/dist/objects/incidents.d.ts +2 -2
  357. package/dist/objects/lifecycle.d.ts +1 -1
  358. package/dist/objects/lifecycle.js +2 -1
  359. package/dist/objects/policies.d.ts +2 -2
  360. package/dist/objects/policies.js +2 -1
  361. package/dist/objects/processes.d.ts +2 -2
  362. package/dist/objects/project-briefs.d.ts +2 -2
  363. package/dist/objects/project-briefs.js +2 -3
  364. package/dist/objects/skills.d.ts +2 -2
  365. package/dist/postinstall.js +1 -1
  366. package/dist/rerankers/clef.js +3 -6
  367. package/dist/rerankers/cross-encoder.d.ts +31 -2
  368. package/dist/rerankers/cross-encoder.js +71 -98
  369. package/dist/rerankers/index.js +5 -4
  370. package/dist/rerankers/jev.js +2 -4
  371. package/dist/rerankers/outage-warning.d.ts +3 -1
  372. package/dist/rerankers/outage-warning.js +5 -3
  373. package/dist/search/bm25-search.js +1 -1
  374. package/dist/search/boosts.js +1 -1
  375. package/dist/search/embed-memories.d.ts +12 -0
  376. package/dist/search/embed-memories.js +389 -0
  377. package/dist/search/engine.d.ts +8 -0
  378. package/dist/search/engine.js +10 -0
  379. package/dist/search/goal-boost.d.ts +1 -1
  380. package/dist/search/goal-boost.js +6 -3
  381. package/dist/search/hybrid.d.ts +9 -2
  382. package/dist/search/hybrid.js +9 -4
  383. package/dist/search/physics-search.d.ts +6 -3
  384. package/dist/search/physics-search.js +22 -15
  385. package/dist/search/rerank.js +1 -1
  386. package/dist/search/vector.d.ts +8 -6
  387. package/dist/search/vector.js +14 -17
  388. package/dist/server/auth.d.ts +3 -1
  389. package/dist/server/auth.js +24 -15
  390. package/dist/server/boot.d.ts +2 -1
  391. package/dist/server/boot.js +34 -84
  392. package/dist/server/client-ip.d.ts +9 -6
  393. package/dist/server/client-ip.js +36 -17
  394. package/dist/server/deadline.d.ts +0 -2
  395. package/dist/server/deadline.js +1 -7
  396. package/dist/server/defaults.d.ts +1 -0
  397. package/dist/server/defaults.js +1 -0
  398. package/dist/server/mcp-http.js +16 -11
  399. package/dist/server/request.js +7 -2
  400. package/dist/server/route-checks.d.ts +7 -0
  401. package/dist/server/route-checks.js +42 -0
  402. package/dist/server/route-table.d.ts +9 -8
  403. package/dist/server/route-table.js +127 -133
  404. package/dist/server/routes/admin.d.ts +8 -8
  405. package/dist/server/routes/admin.js +13 -21
  406. package/dist/server/routes/customer-notes.d.ts +6 -6
  407. package/dist/server/routes/customer-notes.js +1 -2
  408. package/dist/server/routes/decisions.d.ts +6 -6
  409. package/dist/server/routes/decisions.js +2 -4
  410. package/dist/server/routes/incidents.d.ts +6 -6
  411. package/dist/server/routes/incidents.js +2 -5
  412. package/dist/server/routes/memories.d.ts +9 -9
  413. package/dist/server/routes/memories.js +19 -40
  414. package/dist/server/routes/object-routes.d.ts +6 -6
  415. package/dist/server/routes/object-routes.js +4 -7
  416. package/dist/server/routes/policies.d.ts +7 -7
  417. package/dist/server/routes/policies.js +2 -4
  418. package/dist/server/routes/predictions.d.ts +6 -6
  419. package/dist/server/routes/predictions.js +9 -13
  420. package/dist/server/routes/processes.d.ts +6 -6
  421. package/dist/server/routes/processes.js +1 -2
  422. package/dist/server/routes/project-briefs.d.ts +7 -7
  423. package/dist/server/routes/project-briefs.js +2 -4
  424. package/dist/server/routes/recall.d.ts +5 -5
  425. package/dist/server/routes/recall.js +6 -10
  426. package/dist/server/routes/skills.d.ts +7 -7
  427. package/dist/server/routes/skills.js +2 -4
  428. package/dist/server/server-detect.d.ts +7 -1
  429. package/dist/server/server-detect.js +36 -11
  430. package/dist/server/sleep-child.js +26 -7
  431. package/dist/server/sleep-offload.d.ts +11 -5
  432. package/dist/server/sleep-offload.js +17 -11
  433. package/dist/server/tls.d.ts +7 -2
  434. package/dist/server/tls.js +17 -5
  435. package/dist/server/types.d.ts +29 -12
  436. package/dist/server/validation.d.ts +1 -2
  437. package/dist/server/validation.js +6 -11
  438. package/dist/server.d.ts +4 -40
  439. package/dist/server.js +3 -29
  440. package/dist/sharing/global-store.d.ts +4 -5
  441. package/dist/sharing/global-store.js +8 -13
  442. package/dist/sharing/global-sync.js +38 -45
  443. package/dist/sharing/project-merge.d.ts +7 -14
  444. package/dist/sharing/project-merge.js +57 -121
  445. package/dist/sharing/search-both.js +10 -3
  446. package/dist/sharing/share.d.ts +2 -0
  447. package/dist/sharing/share.js +39 -38
  448. package/dist/store/ambient.js +7 -7
  449. package/dist/store/audit-event.d.ts +1 -1
  450. package/dist/store/audit.d.ts +0 -16
  451. package/dist/store/audit.js +4 -55
  452. package/dist/store/auth.d.ts +4 -2
  453. package/dist/store/auth.js +7 -3
  454. package/dist/store/candidates.d.ts +3 -2
  455. package/dist/store/candidates.js +59 -30
  456. package/dist/store/capture-write.d.ts +17 -0
  457. package/dist/store/capture-write.js +35 -0
  458. package/dist/store/cards.js +6 -5
  459. package/dist/store/compaction-caller.d.ts +18 -0
  460. package/dist/store/compaction-caller.js +29 -0
  461. package/dist/store/compactions-record.d.ts +115 -0
  462. package/dist/store/compactions-record.js +418 -0
  463. package/dist/store/compactions.js +6 -4
  464. package/dist/store/conflicts.d.ts +7 -1
  465. package/dist/store/conflicts.js +13 -9
  466. package/dist/store/connectors/dlq.d.ts +10 -0
  467. package/dist/store/connectors/dlq.js +33 -0
  468. package/dist/store/connectors/event-log.d.ts +13 -0
  469. package/dist/store/connectors/event-log.js +24 -0
  470. package/dist/store/connectors/github.d.ts +7 -9
  471. package/dist/store/connectors/github.js +35 -60
  472. package/dist/store/connectors/slack.d.ts +2 -11
  473. package/dist/store/connectors/slack.js +24 -57
  474. package/dist/store/dashboard-reads.js +8 -6
  475. package/dist/store/delete-and-batch.d.ts +6 -0
  476. package/dist/store/delete-and-batch.js +40 -6
  477. package/dist/store/delivery-recorder.d.ts +12 -5
  478. package/dist/store/delivery-recorder.js +13 -11
  479. package/dist/store/descendant-page.js +8 -4
  480. package/dist/store/dormant.d.ts +1 -1
  481. package/dist/store/dormant.js +10 -4
  482. package/dist/store/embeddings/index.d.ts +2 -12
  483. package/dist/store/embeddings/index.js +5 -374
  484. package/dist/store/entry-reads.d.ts +35 -4
  485. package/dist/store/entry-reads.js +86 -19
  486. package/dist/store/entry-row.js +9 -2
  487. package/dist/store/entry-writes.d.ts +15 -12
  488. package/dist/store/entry-writes.js +54 -22
  489. package/dist/store/failure-log-at.d.ts +8 -0
  490. package/dist/store/failure-log-at.js +16 -0
  491. package/dist/store/failure-log.d.ts +2 -2
  492. package/dist/store/failure-log.js +5 -3
  493. package/dist/{trust → store}/gated-write.d.ts +3 -1
  494. package/dist/{trust → store}/gated-write.js +7 -5
  495. package/dist/store/goals.js +22 -19
  496. package/dist/store/graph-reads.js +9 -6
  497. package/dist/store/graph-view-rows.js +2 -1
  498. package/dist/store/graph-writes.d.ts +6 -6
  499. package/dist/store/graph-writes.js +48 -71
  500. package/dist/store/half-life.js +2 -2
  501. package/dist/store/handoffs.d.ts +10 -4
  502. package/dist/store/handoffs.js +10 -9
  503. package/dist/store/index-and-stats.js +2 -2
  504. package/dist/store/index.d.ts +2 -2
  505. package/dist/store/index.js +1 -1
  506. package/dist/store/key-writes.js +1 -1
  507. package/dist/store/ledger-turn.d.ts +9 -0
  508. package/dist/store/ledger-turn.js +38 -0
  509. package/dist/store/mirrors.js +19 -10
  510. package/dist/store/open.d.ts +2 -5
  511. package/dist/store/open.js +42 -54
  512. package/dist/store/pilot-arm.d.ts +12 -0
  513. package/dist/store/pilot-arm.js +42 -0
  514. package/dist/store/port.d.ts +68 -6
  515. package/dist/store/port.js +8 -0
  516. package/dist/store/predictions.d.ts +1 -2
  517. package/dist/store/predictions.js +3 -6
  518. package/dist/store/project-tags.d.ts +62 -0
  519. package/dist/store/project-tags.js +67 -0
  520. package/dist/store/quality-repair.js +1 -1
  521. package/dist/store/quarantine.js +2 -1
  522. package/dist/store/recall-trace.d.ts +2 -0
  523. package/dist/store/recall-trace.js +20 -3
  524. package/dist/store/rejected-values.js +16 -10
  525. package/dist/store/rejection-probe.d.ts +4 -0
  526. package/dist/store/rejection-probe.js +19 -0
  527. package/dist/store/rejection.d.ts +0 -17
  528. package/dist/store/rejection.js +1 -21
  529. package/dist/store/rows.js +2 -2
  530. package/dist/store/rule-sql.d.ts +11 -0
  531. package/dist/store/rule-sql.js +31 -6
  532. package/dist/store/search-rows.d.ts +5 -5
  533. package/dist/store/search-rows.js +18 -16
  534. package/dist/store/sessions.js +4 -2
  535. package/dist/store/set-aside.d.ts +14 -0
  536. package/dist/store/set-aside.js +23 -0
  537. package/dist/store/sqlite/busy.d.ts +3 -0
  538. package/dist/store/sqlite/busy.js +8 -0
  539. package/dist/store/sqlite/connector-events-group.js +4 -7
  540. package/dist/store/sqlite/connector-writes-group.js +6 -4
  541. package/dist/store/sqlite/entry-writes-group.d.ts +3 -3
  542. package/dist/store/sqlite/entry-writes-group.js +3 -3
  543. package/dist/store/sqlite/error-codec.js +4 -3
  544. package/dist/store/sqlite/executor.js +10 -4
  545. package/dist/store/sqlite/graph-reads-group.js +1 -1
  546. package/dist/store/sqlite/held-connection.d.ts +11 -0
  547. package/dist/store/sqlite/held-connection.js +41 -0
  548. package/dist/store/sqlite/last-recall-group.d.ts +13 -0
  549. package/dist/store/sqlite/last-recall-group.js +57 -0
  550. package/dist/store/sqlite/local.d.ts +0 -17
  551. package/dist/store/sqlite/local.js +0 -31
  552. package/dist/store/sqlite/maintenance-groups.d.ts +6 -0
  553. package/dist/store/sqlite/maintenance-groups.js +60 -0
  554. package/dist/store/sqlite/object-rows.d.ts +1 -1
  555. package/dist/store/sqlite/objects-group.d.ts +1 -1
  556. package/dist/store/sqlite/objects-group.js +15 -7
  557. package/dist/store/sqlite/store.d.ts +1 -8
  558. package/dist/store/sqlite/store.js +13 -22
  559. package/dist/store/summaries.d.ts +10 -3
  560. package/dist/store/summaries.js +54 -11
  561. package/dist/store/token-ledger-rows.js +3 -2
  562. package/dist/store/token-ledger.d.ts +0 -7
  563. package/dist/store/token-ledger.js +0 -35
  564. package/dist/store/vector-index.js +8 -4
  565. package/dist/support-bundle.js +2 -3
  566. package/dist/trust/reject-flow.js +2 -2
  567. package/dist/util/agent-homes.d.ts +15 -0
  568. package/dist/util/agent-homes.js +34 -0
  569. package/dist/util/anthropic-messages.d.ts +6 -1
  570. package/dist/util/anthropic-messages.js +26 -7
  571. package/dist/util/atomic-write.d.ts +5 -2
  572. package/dist/util/atomic-write.js +12 -8
  573. package/dist/util/chunked.d.ts +3 -0
  574. package/dist/util/chunked.js +10 -0
  575. package/dist/util/ellipsize.d.ts +3 -0
  576. package/dist/util/ellipsize.js +5 -0
  577. package/dist/util/env.d.ts +12 -2
  578. package/dist/util/env.js +12 -2
  579. package/dist/util/format.d.ts +5 -0
  580. package/dist/util/format.js +10 -0
  581. package/dist/util/hook-payload.d.ts +8 -0
  582. package/dist/util/hook-payload.js +37 -0
  583. package/dist/util/http-retry.d.ts +6 -0
  584. package/dist/util/http-retry.js +43 -5
  585. package/dist/util/http-util.d.ts +0 -8
  586. package/dist/util/http-util.js +10 -21
  587. package/dist/util/limits.d.ts +7 -0
  588. package/dist/util/limits.js +14 -0
  589. package/dist/util/log.d.ts +4 -0
  590. package/dist/util/log.js +9 -0
  591. package/dist/util/request-scope.d.ts +9 -3
  592. package/dist/util/request-scope.js +18 -9
  593. package/dist/util/secret-detect.js +6 -1
  594. package/dist/util/spawn-argv.d.ts +9 -0
  595. package/dist/util/spawn-argv.js +57 -0
  596. package/dist/util/sql.d.ts +3 -0
  597. package/dist/util/sql.js +6 -0
  598. package/dist/util/sqlite-blocked.d.ts +2 -0
  599. package/dist/util/sqlite-blocked.js +5 -0
  600. package/dist/util/stream-tee.d.ts +3 -0
  601. package/dist/util/stream-tee.js +35 -0
  602. package/dist/util/time.d.ts +4 -0
  603. package/dist/util/time.js +4 -0
  604. package/dist/util/vector-codec.d.ts +4 -0
  605. package/dist/util/vector-codec.js +15 -0
  606. package/dist/util/version.d.ts +1 -1
  607. package/dist/util/version.js +1 -1
  608. package/extensions/openclaw-plugin/openclaw.plugin.json +1 -1
  609. package/extensions/openclaw-plugin/package.json +1 -1
  610. package/openclaw.plugin.json +1 -1
  611. package/package.json +4 -2
  612. package/dist/api/on-store.d.ts +0 -16
  613. package/dist/api/on-store.js +0 -38
  614. package/dist/store/embeddings/local.d.ts +0 -15
  615. package/dist/store/embeddings/local.js +0 -175
  616. /package/dist/{cli → api}/correction-latency.d.ts +0 -0
  617. /package/dist/{cli → api}/correction-latency.js +0 -0
  618. /package/dist/{cli → api}/provenance-coverage.d.ts +0 -0
  619. /package/dist/{cli → api}/provenance-coverage.js +0 -0
  620. /package/dist/{store → core}/object-types.d.ts +0 -0
  621. /package/dist/{store → core}/object-types.js +0 -0
  622. /package/dist/{capture → util}/compaction-items.d.ts +0 -0
  623. /package/dist/{capture → util}/compaction-items.js +0 -0
  624. /package/dist/{store → util}/yaml.d.ts +0 -0
  625. /package/dist/{store → util}/yaml.js +0 -0
@@ -1,4 +1,5 @@
1
1
  // Summaries post-compact could not record because the store was busy, waiting on disk for the next replay.
2
+ import { MINUTE_MS } from '../util/time.js';
2
3
  import { randomBytes } from 'crypto';
3
4
  import * as fs from 'fs';
4
5
  import * as path from 'path';
@@ -7,11 +8,12 @@ import { isSqliteBusy } from '../db/busy.js';
7
8
  import { errorMessage } from '../util/log.js';
8
9
  const STAMP_DIGITS = 13;
9
10
  export const SPOOL_DIR = 'compactions-spool';
10
- /** Starts the log line of a step that broke with an unexpected error, so the session-start banner counts it; a set-aside file is counted by its `.bad` name instead. */
11
+ /** Starts the log line of a step that broke with an unexpected error, so the session-start banner counts it; a set-aside file is counted
12
+ * by its `.bad` name instead. */
11
13
  export const SPOOL_PROBLEM = 'spool problem: ';
12
14
  const LOCK = 'replay.lock';
13
15
  /** Long enough that a live replayer has finished with its claim. */
14
- const STALE_MS = 10 * 60_000;
16
+ const STALE_MS = 10 * MINUTE_MS;
15
17
  const MAX_ATTEMPTS = 3;
16
18
  const BUSY_RETRIES = 3;
17
19
  const BUSY_WAIT_MS = 50;
@@ -27,11 +29,6 @@ const BAD = /^([\w-]+)\.(?:\w+\.)?bad$/;
27
29
  const BAD_TMP = /^[\w-]+\.\w+\.bad\.[0-9a-f]{8}\.tmp$/;
28
30
  const NEW_STEM = /^(\d{13})-[0-9a-f]{8}$/;
29
31
  const LEGACY_STEM_TIME = /-(\d+)$/;
30
- let fsx = fs;
31
- /** Test-only seam, so a test can make one fs call fail; null restores the real fs. */
32
- export function _setSpoolFsForTests(next) {
33
- fsx = next ?? fs;
34
- }
35
32
  const stamp = (ms) => String(Math.trunc(ms)).padStart(STAMP_DIGITS, '0');
36
33
  const isTime = (value) => typeof value === 'number' && Number.isFinite(value);
37
34
  const idle = new Int32Array(new SharedArrayBuffer(4));
@@ -97,10 +94,19 @@ function claimOf(name) {
97
94
  function spoolFile(hippoRoot) {
98
95
  return path.join(hippoRoot, SPOOL_DIR, `${stamp(Date.now())}-${randomBytes(4).toString('hex')}.a0.json`);
99
96
  }
100
- export function spool(hippoRoot, tenantId, payload, text, at) {
97
+ export function spool(hippoRoot, tenantId, payload, text, at, fsx = fs) {
101
98
  const file = spoolFile(hippoRoot);
102
99
  fsx.mkdirSync(path.dirname(file), { recursive: true });
103
- const body = { tenantId, sessionId: payload.sessionId, trigger: payload.trigger, cwd: payload.cwd, transcriptPath: payload.transcriptPath, at: at.toISOString(), summary: text.summary, items: text.items };
100
+ const body = {
101
+ tenantId,
102
+ sessionId: payload.sessionId,
103
+ trigger: payload.trigger,
104
+ cwd: payload.cwd,
105
+ transcriptPath: payload.transcriptPath,
106
+ at: at.toISOString(),
107
+ summary: text.summary,
108
+ items: text.items,
109
+ };
104
110
  // Renamed into place so a replayer listing `.json` files never reads half a file.
105
111
  fsx.writeFileSync(`${file}.tmp`, JSON.stringify(body), { encoding: 'utf8', flag: 'wx' });
106
112
  // A rename that stays busy leaves a whole tmp, which a replay promotes once it is STALE_MS old.
@@ -149,7 +155,7 @@ function parseLock(text) {
149
155
  return { pid, at: raw.at, token };
150
156
  }
151
157
  /** The lock's body; its mtime stands in for the time when the body does not parse. */
152
- function readLock(lock, log) {
158
+ function readLock(fsx, lock, log) {
153
159
  let text = '';
154
160
  const read = settle(() => { text = fsx.readFileSync(lock, 'utf8'); }, log, 'spool lock not read');
155
161
  if (read !== 'done')
@@ -161,7 +167,7 @@ function readLock(lock, log) {
161
167
  const stat = settle(() => { mtime = fsx.statSync(lock).mtimeMs; }, log, 'spool lock not read');
162
168
  return stat === 'done' ? { pid: null, at: mtime, token: null } : stat;
163
169
  }
164
- function createLock(lock, body, log) {
170
+ function createLock(fsx, lock, body, log) {
165
171
  try {
166
172
  fsx.writeFileSync(lock, body, { encoding: 'utf8', flag: 'wx' });
167
173
  return 'taken';
@@ -177,18 +183,18 @@ function createLock(lock, body, log) {
177
183
  }
178
184
  }
179
185
  /** This run's token and take time once it holds `replay.lock`, else null: another replayer holds it, or it could not be made. */
180
- function takeLock(dir, log) {
186
+ function takeLock(fsx, dir, log) {
181
187
  const lock = path.join(dir, LOCK);
182
188
  const token = randomBytes(8).toString('hex');
183
189
  const at = Date.now();
184
190
  const body = JSON.stringify({ pid: process.pid, at, token });
185
191
  for (let tries = 0; tries < 2; tries++) {
186
- const made = createLock(lock, body, log);
192
+ const made = createLock(fsx, lock, body, log);
187
193
  if (made === 'taken')
188
194
  return { token, at };
189
195
  if (made === 'skip')
190
196
  break;
191
- const held = readLock(lock, log);
197
+ const held = readLock(fsx, lock, log);
192
198
  if (held === 'gone')
193
199
  continue;
194
200
  if (held === 'busy')
@@ -205,24 +211,24 @@ function takeLock(dir, log) {
205
211
  return null;
206
212
  }
207
213
  /** False, after saying so, once `replay.lock` no longer holds this run's token or cannot be read. */
208
- function ownsLock(dir, token, log) {
209
- const held = readLock(path.join(dir, LOCK), log);
214
+ function ownsLock(fsx, dir, token, log) {
215
+ const held = readLock(fsx, path.join(dir, LOCK), log);
210
216
  if (held !== 'gone' && held !== 'busy' && held.token === token)
211
217
  return true;
212
218
  log(held === 'busy' ? 'spool lock could not be read, stopping' : 'spool left to another replayer (lock taken over)');
213
219
  return false;
214
220
  }
215
221
  /** Removes `replay.lock` only while it holds this run's token, so a run never deletes a successor's lock. Never throws. */
216
- function releaseLock(dir, mine, log) {
222
+ function releaseLock(fsx, dir, mine, log) {
217
223
  const lock = path.join(dir, LOCK);
218
- const held = readLock(lock, log);
224
+ const held = readLock(fsx, lock, log);
219
225
  // A takeover needs a lock over STALE_MS old, so a busy read of one this run took more recently is still this run's.
220
226
  const ours = held === 'busy' ? Date.now() - mine.at < STALE_MS : held !== 'gone' && held.token === mine.token;
221
227
  if (ours)
222
228
  settle(() => fsx.unlinkSync(lock), log, 'spool lock not released');
223
229
  }
224
230
  /** Claim time sits in the claim's name, so a claim over STALE_MS away from now belongs to a replayer that never finished. True when one was put back. */
225
- function recoverStaleClaims(dir, names, log) {
231
+ function recoverStaleClaims(fsx, dir, names, log) {
226
232
  const now = Date.now();
227
233
  // A claim whose unlink stayed busy after its `.bad` landed is a spare copy, not a second failure.
228
234
  const setAside = new Set(names.flatMap((name) => BAD.exec(name)?.[1] ?? []));
@@ -245,21 +251,21 @@ function recoverStaleClaims(dir, names, log) {
245
251
  putBackOne = true;
246
252
  }
247
253
  }
248
- else if (moveBad(dir, file, { stem: claim.stem, attempt: claim.attempt, cause: 'interrupted', log })) {
254
+ else if (moveBad(fsx, dir, file, { stem: claim.stem, attempt: claim.attempt, cause: 'interrupted', log })) {
249
255
  log(`spool file ${claim.name} was claimed by a replayer that never finished ${MAX_ATTEMPTS} times, set aside as .bad`);
250
256
  }
251
257
  }
252
258
  return putBackOne;
253
259
  }
254
260
  /** Returns a claim to the waiting files as `<stem>.a<attempt>.json`; one that stays busy is left for stale recovery. */
255
- function putBack(dir, claim, stem, attempt, log) {
261
+ function putBack(fsx, dir, claim, stem, attempt, log) {
256
262
  const name = path.basename(claim);
257
263
  if (settle(() => fsx.renameSync(claim, path.join(dir, `${stem}.a${attempt}.json`)), log, `spool file ${name} not put back`) === 'busy') {
258
- log(`spool file ${name} could not be put back; it returns ${STALE_MS / 60_000} minutes after its claim`);
264
+ log(`spool file ${name} could not be put back; it returns ${STALE_MS / MINUTE_MS} minutes after its claim`);
259
265
  }
260
266
  }
261
267
  /** Writes `<stem>.<cause>.bad` through a random temp file, so the rename replaces any torn `.bad` a crash left. */
262
- function writeBad(dir, options) {
268
+ function writeBad(fsx, dir, options) {
263
269
  const { stem, cause, text, log, what } = options;
264
270
  const tmp = path.join(dir, `${stem}.${cause}.bad.${randomBytes(4).toString('hex')}.tmp`);
265
271
  const written = settle(() => fsx.writeFileSync(tmp, text, { encoding: 'utf8', flag: 'wx' }), log, what);
@@ -269,21 +275,21 @@ function writeBad(dir, options) {
269
275
  return moved === 'done';
270
276
  }
271
277
  /** Sets a claim aside as `.bad` before the claim goes, so a crash between leaves a whole copy; a busy write puts the claim back. */
272
- function moveBad(dir, claim, options) {
278
+ function moveBad(fsx, dir, claim, options) {
273
279
  const { stem, attempt, cause, log, raw } = options;
274
280
  const what = `spool file ${path.basename(claim)} not set aside`;
275
281
  let text = raw ?? '';
276
282
  if (raw === undefined && settle(() => { text = fsx.readFileSync(claim, 'utf8'); }, log, what) !== 'done')
277
283
  return false;
278
- if (!writeBad(dir, { stem, cause, text, log, what })) {
279
- putBack(dir, claim, stem, attempt, log);
284
+ if (!writeBad(fsx, dir, { stem, cause, text, log, what })) {
285
+ putBack(fsx, dir, claim, stem, attempt, log);
280
286
  return false;
281
287
  }
282
288
  settle(() => fsx.unlinkSync(claim), log, what);
283
289
  return true;
284
290
  }
285
291
  /** A tmp over STALE_MS old belongs to a spool that never renamed it: a whole one joins the waiting files, a torn one is set aside. True when one joined. */
286
- function promoteTmp(dir, names, tenantId, log) {
292
+ function promoteTmp(fsx, dir, names, tenantId, log) {
287
293
  const now = Date.now();
288
294
  let promoted = false;
289
295
  for (const name of names) {
@@ -309,7 +315,7 @@ function promoteTmp(dir, names, tenantId, log) {
309
315
  promoted = true;
310
316
  }
311
317
  }
312
- else if (writeBad(dir, { stem: m[1], cause: 'unreadable', text, log, what })) {
318
+ else if (writeBad(fsx, dir, { stem: m[1], cause: 'unreadable', text, log, what })) {
313
319
  settle(() => fsx.unlinkSync(file), log, what);
314
320
  log(`spool file ${name} was never finished, set aside as .bad`);
315
321
  }
@@ -317,38 +323,40 @@ function promoteTmp(dir, names, tenantId, log) {
317
323
  return promoted;
318
324
  }
319
325
  /** Removes a claim once the store holds its summary; a claim left behind is imported again, so the log says so. */
320
- function removeClaim(claim, name, log) {
326
+ function removeClaim(fsx, claim, name, log) {
321
327
  const removed = settleCode(() => fsx.unlinkSync(claim), log, `spool file ${name} claim not removed`);
322
328
  if (removed.settled === 'busy')
323
329
  log(`spool file ${name} saved; its claim could not be removed (${removed.code}); it will be imported again`);
324
330
  }
325
331
  /** After an importer throw: a busy store stops the run with the count unchanged, any other error counts one try toward .bad. */
326
- function importFailed(dir, claim, entry, options) {
332
+ function importFailed(fsx, dir, claim, entry, options) {
327
333
  const { text, recorded, cause, log } = options;
328
334
  const { name } = entry;
329
335
  const busy = isSqliteBusy(cause);
330
336
  if (recorded) {
331
337
  // The record stays `summarised`, so the stalled-record step of a later replay writes its items.
332
- log(busy ? `spool file ${name} saved; its memories wait for the next sleep (store busy)` : `spool file ${name} saved; writing its memories failed: ${errorMessage(cause)}`);
338
+ log(busy
339
+ ? `spool file ${name} saved; its memories wait for the next sleep (store busy)`
340
+ : `spool file ${name} saved; writing its memories failed: ${errorMessage(cause)}`);
333
341
  return busy ? 'stop' : 'skipped';
334
342
  }
335
343
  if (busy) {
336
- putBack(dir, claim, entry.stem, entry.attempt, log);
344
+ putBack(fsx, dir, claim, entry.stem, entry.attempt, log);
337
345
  log(`spool file ${name} waits for the next run: the store is busy`);
338
346
  return 'stop';
339
347
  }
340
348
  const next = entry.attempt + 1;
341
349
  if (next < MAX_ATTEMPTS) {
342
- putBack(dir, claim, entry.stem, next, log);
350
+ putBack(fsx, dir, claim, entry.stem, next, log);
343
351
  log(`spool file ${name} failed to import (try ${next} of ${MAX_ATTEMPTS}): ${errorMessage(cause)}`);
344
352
  }
345
- else if (moveBad(dir, claim, { stem: entry.stem, attempt: entry.attempt, cause: 'failed', log, raw: text })) {
353
+ else if (moveBad(fsx, dir, claim, { stem: entry.stem, attempt: entry.attempt, cause: 'failed', log, raw: text })) {
346
354
  log(`spool file ${name} set aside as .bad after ${MAX_ATTEMPTS} tries: ${errorMessage(cause)}`);
347
355
  }
348
356
  return 'skipped';
349
357
  }
350
358
  /** Claims, reads and imports one waiting file; `stop` ends the run because the store is busy. */
351
- function importFile(dir, entry, tenantId, log, importOne) {
359
+ function importFile(fsx, dir, entry, tenantId, log, importOne) {
352
360
  const { name } = entry;
353
361
  const claim = path.join(dir, `${entry.stem}.a${entry.attempt}.claim-${stamp(Date.now())}`);
354
362
  const taken = settleCode(() => fsx.renameSync(path.join(dir, name), claim), log, `spool file ${name} not claimed`);
@@ -361,13 +369,14 @@ function importFile(dir, entry, tenantId, log, importOne) {
361
369
  if (read === 'gone')
362
370
  log(`spool file ${name} vanished`);
363
371
  if (read === 'busy')
364
- putBack(dir, claim, entry.stem, entry.attempt, log);
372
+ putBack(fsx, dir, claim, entry.stem, entry.attempt, log);
365
373
  if (read !== 'done')
366
374
  return 'skipped';
367
375
  const spooled = parseSpooled(text, tenantId);
368
376
  if (spooled === null) {
369
- if (moveBad(dir, claim, { stem: entry.stem, attempt: entry.attempt, cause: 'unreadable', log, raw: text }))
377
+ if (moveBad(fsx, dir, claim, { stem: entry.stem, attempt: entry.attempt, cause: 'unreadable', log, raw: text })) {
370
378
  log(`spool file ${name} is not readable, set aside as .bad`);
379
+ }
371
380
  return 'skipped';
372
381
  }
373
382
  let recorded = false;
@@ -375,7 +384,7 @@ function importFile(dir, entry, tenantId, log, importOne) {
375
384
  if (recorded)
376
385
  return;
377
386
  recorded = true;
378
- removeClaim(claim, name, log);
387
+ removeClaim(fsx, claim, name, log);
379
388
  };
380
389
  try {
381
390
  importOne(spooled, markRecorded);
@@ -383,10 +392,10 @@ function importFile(dir, entry, tenantId, log, importOne) {
383
392
  return 'imported';
384
393
  }
385
394
  catch (err) {
386
- return importFailed(dir, claim, entry, { text, recorded, cause: err, log });
395
+ return importFailed(fsx, dir, claim, entry, { text, recorded, cause: err, log });
387
396
  }
388
397
  }
389
- function mtimeOf(file) {
398
+ function mtimeOf(fsx, file) {
390
399
  try {
391
400
  return fsx.statSync(file).mtimeMs;
392
401
  }
@@ -399,7 +408,7 @@ function mtimeOf(file) {
399
408
  }
400
409
  }
401
410
  /** What doctor reports, read only: files a replay should have taken by now, claims and tmp files a dead process left, and `.bad` files. */
402
- export function spoolCounts(hippoRoot, now, waitingAfterMs) {
411
+ export function spoolCounts(hippoRoot, now, waitingAfterMs, fsx = fs) {
403
412
  const counts = { waiting: 0, stale: 0, bad: 0 };
404
413
  const dir = path.join(hippoRoot, SPOOL_DIR);
405
414
  if (!fsx.existsSync(dir))
@@ -412,12 +421,12 @@ export function spoolCounts(hippoRoot, now, waitingAfterMs) {
412
421
  if (name.endsWith('.bad'))
413
422
  counts.bad++;
414
423
  else if (waiting !== null) {
415
- const spooled = NEW_STEM.test(waiting[1]) ? created(waiting[1]) : mtimeOf(file);
424
+ const spooled = NEW_STEM.test(waiting[1]) ? created(waiting[1]) : mtimeOf(fsx, file);
416
425
  if (spooled !== null && at - spooled > waitingAfterMs)
417
426
  counts.waiting++;
418
427
  }
419
428
  else if (claim !== null || TMP.test(name)) {
420
- const since = claim?.claimedAt ?? mtimeOf(file);
429
+ const since = claim?.claimedAt ?? mtimeOf(fsx, file);
421
430
  if (since !== null && Math.abs(at - since) > STALE_MS)
422
431
  counts.stale++;
423
432
  }
@@ -425,25 +434,25 @@ export function spoolCounts(hippoRoot, now, waitingAfterMs) {
425
434
  return counts;
426
435
  }
427
436
  /** One replayer at a time holds `replay.lock`, so only one process claims a file even where Windows lets two renames of it both land. */
428
- export function importSpool(hippoRoot, tenantId, log, deadline, importOne) {
437
+ export function importSpool(hippoRoot, tenantId, log, deadline, importOne, fsx = fs) {
429
438
  const dir = path.join(hippoRoot, SPOOL_DIR);
430
439
  if (!fsx.existsSync(dir))
431
440
  return 0;
432
- const mine = takeLock(dir, log);
441
+ const mine = takeLock(fsx, dir, log);
433
442
  if (mine === null)
434
443
  return 0;
435
444
  try {
436
- if (!ownsLock(dir, mine.token, log))
445
+ if (!ownsLock(fsx, dir, mine.token, log))
437
446
  return 0;
438
447
  const names = fsx.readdirSync(dir);
439
- const promoted = promoteTmp(dir, names, tenantId, log);
440
- const recovered = recoverStaleClaims(dir, names, log);
448
+ const promoted = promoteTmp(fsx, dir, names, tenantId, log);
449
+ const recovered = recoverStaleClaims(fsx, dir, names, log);
441
450
  let finished = 0;
442
451
  // Only promotion and recovery add waiting names, so the folder is listed again only after one of them did.
443
452
  for (const entry of waitingFiles(promoted || recovered ? fsx.readdirSync(dir) : names)) {
444
- if (Date.now() > deadline || !ownsLock(dir, mine.token, log))
453
+ if (Date.now() > deadline || !ownsLock(fsx, dir, mine.token, log))
445
454
  break;
446
- const outcome = importFile(dir, entry, tenantId, log, importOne);
455
+ const outcome = importFile(fsx, dir, entry, tenantId, log, importOne);
447
456
  if (outcome === 'imported')
448
457
  finished++;
449
458
  if (outcome === 'stop')
@@ -452,7 +461,7 @@ export function importSpool(hippoRoot, tenantId, log, deadline, importOne) {
452
461
  return finished;
453
462
  }
454
463
  finally {
455
- releaseLock(dir, mine, log);
464
+ releaseLock(fsx, dir, mine, log);
456
465
  }
457
466
  }
458
467
  //# sourceMappingURL=compaction-spool.js.map
@@ -13,6 +13,7 @@ export declare const SESSION_ID_RE: RegExp;
13
13
  export declare function copilotTranscriptFor(sessionId: string): string | null;
14
14
  /** True for VS Code's own chat log, `<workspaceStorage>/<id>/github.copilot-chat/transcripts/<session id>.jsonl`, in either slash and any case. */
15
15
  export declare function isVscodeTranscript(file: string | null): boolean;
16
- /** The turn one Copilot event-log line carries: the text of a human user.message or of an assistant.message; null for any other line, a sub-agent's included. */
16
+ /** The turn one Copilot event-log line carries: the text of a human user.message or of an assistant.message; null for any other line, a
17
+ * sub-agent's included. */
17
18
  export declare function copilotTurn(line: CopilotEventLine): CopilotTurn | null;
18
19
  //# sourceMappingURL=copilot-transcript.d.ts.map
@@ -33,7 +33,8 @@ function isInjectedUserMessage(data) {
33
33
  const source = data.source;
34
34
  return isStringValue(source) && INJECTED_SOURCE_PREFIXES.some((prefix) => source.startsWith(prefix));
35
35
  }
36
- /** The turn one Copilot event-log line carries: the text of a human user.message or of an assistant.message; null for any other line, a sub-agent's included. */
36
+ /** The turn one Copilot event-log line carries: the text of a human user.message or of an assistant.message; null for any other line, a
37
+ * sub-agent's included. */
37
38
  export function copilotTurn(line) {
38
39
  const role = line.type === 'user.message' ? 'user' : line.type === 'assistant.message' ? 'assistant' : null;
39
40
  const data = isObjectLike(line.data) ? line.data : null;
@@ -2,8 +2,11 @@ export interface ExtractedItem {
2
2
  content: string;
3
3
  category: string;
4
4
  tags: string[];
5
+ /** A person said it, not the agent: capture pins a rule that carries this. */
6
+ fromUser?: boolean;
5
7
  }
6
- /** Captures complete supported statements, each text parsed alone so a fence or heading in one never reaches the next. */
7
- export declare function extractFromTexts(texts: readonly string[]): ExtractedItem[];
8
+ /** Captures complete supported statements, each text parsed alone so a fence or heading in one never reaches the next.
9
+ * The first `userTexts` texts are a person's own words; on a repeat their copy wins, so it keeps `fromUser`. */
10
+ export declare function extractFromTexts(texts: readonly string[], userTexts?: number): ExtractedItem[];
8
11
  export declare function extractFromText(text: string): ExtractedItem[];
9
12
  //# sourceMappingURL=extract.d.ts.map
@@ -158,10 +158,11 @@ function extractOne(text) {
158
158
  }
159
159
  return items;
160
160
  }
161
- /** Captures complete supported statements, each text parsed alone so a fence or heading in one never reaches the next. */
162
- export function extractFromTexts(texts) {
161
+ /** Captures complete supported statements, each text parsed alone so a fence or heading in one never reaches the next.
162
+ * The first `userTexts` texts are a person's own words; on a repeat their copy wins, so it keeps `fromUser`. */
163
+ export function extractFromTexts(texts, userTexts = 0) {
163
164
  const seen = new Set();
164
- return texts.flatMap(extractOne).filter((item) => {
165
+ return texts.flatMap((text, i) => extractOne(text).map((item) => (i < userTexts ? { ...item, fromUser: true } : item))).filter((item) => {
165
166
  const key = duplicateKey(item.content);
166
167
  if (seen.has(key) || !assessAutomaticMemory(item.content).accepted)
167
168
  return false;
@@ -1,5 +1,5 @@
1
1
  import type { Context } from '../api/types.js';
2
- import { type CaptureErrorOutcome, type RoutineRule } from '../store/failure-log.js';
2
+ import type { CaptureErrorOutcome, RoutineRule } from '../store/failure-log.js';
3
3
  import type { CallerProject } from '../api/prompt-hook.js';
4
4
  type FailureSkip = Exclude<CaptureErrorOutcome, 'stored' | 'duplicate'>;
5
5
  /** failureReport's reading of the payload, read on the caller's machine, plus the id its retries carry. */
@@ -1,14 +1,21 @@
1
- // PostToolUseFailure for a caller on another machine: the lesson and its log row carry the caller's owner and project, and a retried request id gets the first answer.
1
+ // PostToolUseFailure for a caller on another machine: the lesson and its log row carry the caller's owner and project, and a retried
2
+ // request id gets the first answer.
2
3
  import { BadRequestError } from '../core/api-errors.js';
3
4
  import { storeLesson } from './capture-error.js';
4
- import { recordFailure, requestOutcome, settleFailureOutcome } from '../store/failure-log.js';
5
+ import { recordFailureAt, requestOutcomeAt, settleFailureOutcomeAt } from '../store/failure-log-at.js';
5
6
  import { errorMessage, log } from '../util/log.js';
6
7
  import { scrubForSharing } from './share-scrub.js';
7
8
  import { truncateCodePointSafe } from '../util/transcript-tail.js';
8
- import { assertRequestId, bindCaller, withCallerDb } from './caller-session.js';
9
+ import { assertRequestId, bindCaller } from './caller-session.js';
9
10
  import { FAILURE_TEXT_MAX_CHARS, failureHash } from './failure-reading.js';
10
11
  const SKIPS = { 'skipped-interrupt': true, 'skipped-routine': true, 'skipped-invalid': true };
11
- const ROUTINE_RULES = { declined: true, 'os-permission': true, 'no-match': true, 'search-tool': true, 'quiet-exit': true };
12
+ const ROUTINE_RULES = {
13
+ declined: true,
14
+ 'os-permission': true,
15
+ 'no-match': true,
16
+ 'search-tool': true,
17
+ 'quiet-exit': true,
18
+ };
12
19
  /** The shapes lessonFromFailure gives: text for a lesson or a routine skip, none for an interrupt or an unreadable payload, a rule only on a routine skip. */
13
20
  function checkedFailure(req) {
14
21
  assertRequestId(req.requestId);
@@ -35,7 +42,7 @@ function checkedFailure(req) {
35
42
  export function captureFailureForCaller(ctx, req) {
36
43
  const failure = checkedFailure(req);
37
44
  const key = bindCaller(ctx, req.sessionId, req.project);
38
- const earlier = withCallerDb(ctx, (db) => requestOutcome(db, ctx.tenantId, req.requestId, req.sessionId));
45
+ const earlier = requestOutcomeAt(ctx.hippoRoot, ctx.tenantId, req.requestId, req.sessionId);
39
46
  // A retry after a lost reply gets the first answer and writes nothing; one whose store failed tries the store again.
40
47
  if (earlier !== null && earlier !== 'store-failed')
41
48
  return { outcome: earlier };
@@ -54,17 +61,15 @@ export function captureFailureForCaller(ctx, req) {
54
61
  /** Runs after the outcome in its own try, so a log error never hides the store's error or its outcome. */
55
62
  function logOutcome(ctx, req, key, outcome, retried) {
56
63
  try {
57
- withCallerDb(ctx, (db) => {
58
- // The request id allows one row, so a retry rewrites the first try's outcome.
59
- if (retried) {
60
- settleFailureOutcome(db, ctx.tenantId, req.requestId, outcome);
61
- return;
62
- }
63
- recordFailure(db, {
64
- tenantId: ctx.tenantId, sessionId: req.sessionId, tool: req.tool, outcome, rule: req.rule,
65
- sigHash: req.text?.trim() ? failureHash(req.text) : null, detailHash: req.detailHash,
66
- ownerSubject: key.owner, originProject: req.project.name, requestId: req.requestId,
67
- });
64
+ // The request id allows one row, so a retry rewrites the first try's outcome.
65
+ if (retried) {
66
+ settleFailureOutcomeAt(ctx.hippoRoot, ctx.tenantId, req.requestId, outcome);
67
+ return;
68
+ }
69
+ recordFailureAt(ctx.hippoRoot, {
70
+ tenantId: ctx.tenantId, sessionId: req.sessionId, tool: req.tool, outcome, rule: req.rule,
71
+ sigHash: req.text?.trim() ? failureHash(req.text) : null, detailHash: req.detailHash,
72
+ ownerSubject: key.owner, originProject: req.project.name, requestId: req.requestId,
68
73
  });
69
74
  }
70
75
  catch (err) {
@@ -22,7 +22,8 @@ export declare function payloadString(payload: JsonValue, key: 'session_id' | 't
22
22
  export declare function failureSignature(text: string): string;
23
23
  /** The hash the failure log stores for a lesson text or a detail; one function so every writer counts repeats alike. */
24
24
  export declare function failureHash(text: string): string;
25
- /** The memory text for a failure payload, or why it is not stored; a routine skip keeps its text for the log. `scrub` runs before the cap, as a mask can be longer than what it hides. Pure. */
25
+ /** The memory text for a failure payload, or why it is not stored; a routine skip keeps its text for the log. `scrub` runs before the
26
+ * cap, as a mask can be longer than what it hides. Pure. */
26
27
  export declare function lessonFromFailure(payload: JsonValue, scrub?: (text: string) => string): FailureReading;
27
28
  /** A failure as it may leave the machine: the capped lesson text and the detail's hash, never the untruncated detail. */
28
29
  export interface FailureReport {
@@ -3,7 +3,7 @@ import { redactSecretsStrict } from '../util/secret-detect.js';
3
3
  import { scrubForSharing } from './share-scrub.js';
4
4
  import { blockHash } from '../util/token-text.js';
5
5
  import { truncateCodePointSafe } from '../util/transcript-tail.js';
6
- import { isJsonObjectLiteral } from '../util/json.js';
6
+ import { isJsonObjectLiteral, isJsonString } from '../util/json.js';
7
7
  const COMMAND_HEAD_WORDS = 2;
8
8
  /** The longest lesson text a reading gives, so a check of a sent one imports it instead of copying it. */
9
9
  export const FAILURE_TEXT_MAX_CHARS = 200;
@@ -19,15 +19,12 @@ const QUIET_EXIT_1 = /^\s*(?:grep|rg|egrep|fgrep|find|test|\[|diff|cmp|git diff|
19
19
  // Claude Code's tool names, then the Copilot CLI's and VS Code's; tests/copilot-failure-reading.test.ts cites where each comes from.
20
20
  const SEARCH_TOOLS = new Set(['Grep', 'Glob', 'grep', 'glob', 'rg', 'grep_search', 'file_search', 'semantic_search']);
21
21
  const SHELL_TOOLS = new Set(['Bash', 'bash', 'powershell', 'run_in_terminal']);
22
- function isString(v) {
23
- return v !== undefined && v !== null && v.constructor === String;
24
- }
25
22
  /** A non-blank string field of the payload, or null. */
26
23
  export function payloadString(payload, key) {
27
24
  if (!isJsonObjectLiteral(payload))
28
25
  return null;
29
26
  const value = payload[key];
30
- return isString(value) && value.trim() !== '' ? value : null;
27
+ return isJsonString(value) && value.trim() !== '' ? value : null;
31
28
  }
32
29
  /** Normalised form used to spot repeats. The failure log keeps only its hash, so changing it breaks repeat counts. */
33
30
  export function failureSignature(text) {
@@ -37,7 +34,8 @@ export function failureSignature(text) {
37
34
  export function failureHash(text) {
38
35
  return blockHash(failureSignature(text));
39
36
  }
40
- /** The memory text for a failure payload, or why it is not stored; a routine skip keeps its text for the log. `scrub` runs before the cap, as a mask can be longer than what it hides. Pure. */
37
+ /** The memory text for a failure payload, or why it is not stored; a routine skip keeps its text for the log. `scrub` runs before the
38
+ * cap, as a mask can be longer than what it hides. Pure. */
41
39
  export function lessonFromFailure(payload, scrub = (text) => text) {
42
40
  if (!isJsonObjectLiteral(payload))
43
41
  return { skip: 'skipped-invalid', text: null, detail: null };
@@ -45,12 +43,12 @@ export function lessonFromFailure(payload, scrub = (text) => text) {
45
43
  const p = payload;
46
44
  if (p.is_interrupt === true)
47
45
  return { skip: 'skipped-interrupt', text: null, detail: null };
48
- if (!isString(p.error) || p.error.trim().length < 12)
46
+ if (!isJsonString(p.error) || p.error.trim().length < 12)
49
47
  return { skip: 'skipped-invalid', text: null, detail: null };
50
- const tool = isString(p.tool_name) ? p.tool_name : 'tool';
48
+ const tool = isJsonString(p.tool_name) ? p.tool_name : 'tool';
51
49
  const error = redactSecretsStrict(p.error.replace(/\s+/g, ' ').trim());
52
50
  const text = truncateCodePointSafe(scrub(`${tool}: ${error}`), FAILURE_TEXT_MAX_CHARS);
53
- const command = isJsonObjectLiteral(p.tool_input) && isString(p.tool_input['command']) ? p.tool_input['command'].replace(LEADING_CD, '') : '';
51
+ const command = isJsonObjectLiteral(p.tool_input) && isJsonString(p.tool_input['command']) ? p.tool_input['command'].replace(LEADING_CD, '') : '';
54
52
  const head = command.trim().split(/\s+/).slice(0, COMMAND_HEAD_WORDS).join(' ');
55
53
  const detail = `${tool}${head ? ` ${head}` : ''}: ${error}`;
56
54
  const routine = (rule) => ({ skip: 'skipped-routine', rule, text, detail });
@@ -1,7 +1,8 @@
1
- import { markSnapshotSaved, PRE_COMPACT_INSTRUCTION, startCompaction } from './compaction-record.js';
1
+ import { PRE_COMPACT_INSTRUCTION } from './compaction-record.js';
2
+ import { markSnapshotSavedAt, startCompactionAt } from '../store/compaction-caller.js';
2
3
  import { errorMessage, log } from '../util/log.js';
3
4
  import { loadActiveTaskSnapshot, saveActiveTaskSnapshot } from '../store/sessions.js';
4
- import { assertTrigger, bindCaller, callerInHoldout, checkedWorkingState, withCallerDb } from './caller-session.js';
5
+ import { assertTrigger, bindCaller, callerInHoldout, checkedWorkingState } from './caller-session.js';
5
6
  import { mergeWorkingState } from './working-state.js';
6
7
  /** A failed step is logged and the instruction still goes out, as the local hook prints it before any store work. */
7
8
  function logged(what, fn) {
@@ -20,10 +21,10 @@ export function preCompactForCaller(ctx, req) {
20
21
  if (callerInHoldout(ctx, req.sessionId))
21
22
  return { stdout: '' };
22
23
  const start = { sessionId: req.sessionId, originProject: req.project.name, trigger: req.trigger, cwd: null, transcriptPath: null };
23
- const recordId = logged('compaction record not started', () => withCallerDb(ctx, (db) => startCompaction(db, ctx.tenantId, start)));
24
+ const recordId = logged('compaction record not started', () => startCompactionAt(ctx.hippoRoot, ctx.tenantId, start));
24
25
  if (state !== null && saveOwnerSnapshot(ctx, req.sessionId, key, state) && recordId !== null) {
25
26
  // Marked under ctx.tenantId, which the environment's tenant may not be.
26
- logged('compaction record not marked with its snapshot', () => withCallerDb(ctx, (db) => markSnapshotSaved(db, ctx.tenantId, recordId)));
27
+ logged('compaction record not marked with its snapshot', () => markSnapshotSavedAt(ctx.hippoRoot, ctx.tenantId, recordId));
27
28
  }
28
29
  return { stdout: PRE_COMPACT_INSTRUCTION };
29
30
  }
@@ -10,9 +10,9 @@ import { USER_SEGMENT } from '../util/home-path.js';
10
10
  import { errorMessage } from '../util/log.js';
11
11
  import { SESSION_DIGEST_TAG } from '../core/session-digest-row.js';
12
12
  import { createMemory, Layer } from '../core/memory.js';
13
- import { RejectedValueError } from '../store/rejection.js';
13
+ import { RejectedValueError } from '../core/api-errors.js';
14
14
  import { maskEmails, redactSecretsStrict } from '../util/secret-detect.js';
15
- import { isInitialized } from '../store/open.js';
15
+ import { isInitialized } from '../core/project-identity.js';
16
16
  import { writeEntry } from '../store/entry-writes.js';
17
17
  import { loadLiveContentsBySourceAndTag } from '../store/entry-reads.js';
18
18
  import { SNAPSHOT_AMBIENT_MAX_AGE_MS } from '../store/sessions.js';
@@ -336,7 +336,8 @@ const ANCHORS = [
336
336
  /(?:^|\s)#\d+\b/,
337
337
  ];
338
338
  /** Each marker states a cause or a choice, the part of a lesson that transfers. */
339
- const MARKERS = /\b(?:because|caused by|due to|root cause|the (?:bug|issue|problem) was|fail(?:s|ed) when|so that|otherwise|instead of|rather than|switched to|decided|chose|must|never|always|do not)\b/i;
339
+ const MARKERS = new RegExp('\\b(?:because|caused by|due to|root cause|the (?:bug|issue|problem) was|fail(?:s|ed) when|so that|otherwise|' +
340
+ 'instead of|rather than|switched to|decided|chose|must|never|always|do not)\\b', 'i');
340
341
  const FENCE = /^\s*(`{3,}|~{3,})/;
341
342
  const STRUCTURE_LINE = /^\s*(?:#{1,6}\s|\||>|([-*_])(?:\s*\1){2,}\s*$)/;
342
343
  const PROTECTED = /`[^`\n]+`|\b(?:e\.g|i\.e)\./gi;
@@ -11,7 +11,8 @@ function checkedEvidence(evidence) {
11
11
  const { gitRef = null, dirtyTree = null, testStatus = null } = evidence;
12
12
  // `git rev-parse HEAD` gives 40 hex, or 64 in a SHA-256 repository.
13
13
  if (gitRef !== null && !/^(?:[0-9a-f]{40}|[0-9a-f]{64})$/.test(gitRef))
14
- throw new BadRequestError('evidence gitRef: 40 or 64 lowercase hex characters or null');
14
+ throw new BadRequestError('evidence gitRef: 40 or 64 lowercase hex characters or ' +
15
+ 'null');
15
16
  if (dirtyTree !== null && dirtyTree !== true && dirtyTree !== false)
16
17
  throw new BadRequestError('evidence dirtyTree: true, false or null');
17
18
  if (testStatus !== null && !TEST_STATUSES.has(testStatus))
@@ -23,7 +24,7 @@ export function sessionEndHandoffForCaller(ctx, req) {
23
24
  const state = checkedWorkingState(req.workingState);
24
25
  const evidence = checkedEvidence(req.evidence);
25
26
  const key = bindCaller(ctx, req.sessionId, req.project);
26
- const handoff = writeSessionEndHandoff(ctx.hippoRoot, ctx.tenantId, req.sessionId, evidence, state, key);
27
+ const handoff = writeSessionEndHandoff(ctx.hippoRoot, { tenantId: ctx.tenantId, sessionId: req.sessionId, evidence, derived: state, key });
27
28
  const snapshotsClosed = closeTaskSnapshotsForSession(ctx.hippoRoot, ctx.tenantId, req.sessionId, 'session-ended', key);
28
29
  return { handoffWritten: handoff !== null, snapshotsClosed };
29
30
  }