hmem-mcp 7.4.0 → 8.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 (315) hide show
  1. package/README.md +266 -54
  2. package/dist/cli-checkpoint-agent.d.ts +27 -0
  3. package/dist/cli-checkpoint-agent.js +320 -0
  4. package/dist/cli-checkpoint-agent.js.map +1 -0
  5. package/dist/cli-checkpoint.js +165 -78
  6. package/dist/cli-checkpoint.js.map +1 -1
  7. package/dist/cli-context-inject.d.ts +7 -7
  8. package/dist/cli-context-inject.js +54 -41
  9. package/dist/cli-context-inject.js.map +1 -1
  10. package/dist/cli-doctor.d.ts +7 -0
  11. package/dist/cli-doctor.js +156 -0
  12. package/dist/cli-doctor.js.map +1 -0
  13. package/dist/cli-hook-startup.d.ts +1 -0
  14. package/dist/cli-hook-startup.js +97 -10
  15. package/dist/cli-hook-startup.js.map +1 -1
  16. package/dist/cli-init.d.ts +0 -4
  17. package/dist/cli-init.js +48 -6
  18. package/dist/cli-init.js.map +1 -1
  19. package/dist/cli-log-exchange.js +10 -2
  20. package/dist/cli-log-exchange.js.map +1 -1
  21. package/dist/cli-mcp-add-curate.d.ts +7 -0
  22. package/dist/cli-mcp-add-curate.js +40 -0
  23. package/dist/cli-mcp-add-curate.js.map +1 -0
  24. package/dist/cli-migrate.d.ts +57 -0
  25. package/dist/cli-migrate.js +394 -0
  26. package/dist/cli-migrate.js.map +1 -0
  27. package/dist/cli-session-summary.d.ts +1 -1
  28. package/dist/cli-session-summary.js +9 -54
  29. package/dist/cli-session-summary.js.map +1 -1
  30. package/dist/cli-statusline.js +15 -11
  31. package/dist/cli-statusline.js.map +1 -1
  32. package/dist/cli-sync-pull.d.ts +3 -0
  33. package/dist/cli-sync-pull.js +79 -0
  34. package/dist/cli-sync-pull.js.map +1 -0
  35. package/dist/cli-sync-push.d.ts +4 -0
  36. package/dist/cli-sync-push.js +85 -0
  37. package/dist/cli-sync-push.js.map +1 -0
  38. package/dist/cli-sync-setup.d.ts +18 -0
  39. package/dist/cli-sync-setup.js +308 -0
  40. package/dist/cli-sync-setup.js.map +1 -0
  41. package/dist/cli-sync-status.d.ts +1 -0
  42. package/dist/cli-sync-status.js +20 -0
  43. package/dist/cli-sync-status.js.map +1 -0
  44. package/dist/cli-v2-checkpoint.d.ts +38 -0
  45. package/dist/cli-v2-checkpoint.js +134 -0
  46. package/dist/cli-v2-checkpoint.js.map +1 -0
  47. package/dist/cli.js +154 -34
  48. package/dist/cli.js.map +1 -1
  49. package/dist/config/config.d.ts +29 -0
  50. package/dist/config/config.js +123 -0
  51. package/dist/config/config.js.map +1 -0
  52. package/dist/diagnostics.js +1 -6
  53. package/dist/diagnostics.js.map +1 -1
  54. package/dist/extensions/pi-hmem.js +141 -35
  55. package/dist/extensions/pi-hmem.js.map +1 -1
  56. package/dist/hmem-config.d.ts +30 -5
  57. package/dist/hmem-config.js +50 -8
  58. package/dist/hmem-config.js.map +1 -1
  59. package/dist/hmem-store.d.ts +60 -2
  60. package/dist/hmem-store.js +205 -26
  61. package/dist/hmem-store.js.map +1 -1
  62. package/dist/hooks/binding-resolver.d.ts +13 -0
  63. package/dist/hooks/binding-resolver.js +42 -0
  64. package/dist/hooks/binding-resolver.js.map +1 -0
  65. package/dist/hooks/brief.d.ts +2 -0
  66. package/dist/hooks/brief.js +26 -0
  67. package/dist/hooks/brief.js.map +1 -0
  68. package/dist/hooks/dispatch.d.ts +5 -0
  69. package/dist/hooks/dispatch.js +119 -0
  70. package/dist/hooks/dispatch.js.map +1 -0
  71. package/dist/hooks/git-probe.d.ts +4 -0
  72. package/dist/hooks/git-probe.js +46 -0
  73. package/dist/hooks/git-probe.js.map +1 -0
  74. package/dist/hooks/harness/claude-code.d.ts +10 -0
  75. package/dist/hooks/harness/claude-code.js +133 -0
  76. package/dist/hooks/harness/claude-code.js.map +1 -0
  77. package/dist/hooks/harness/hermes.d.ts +4 -0
  78. package/dist/hooks/harness/hermes.js +20 -0
  79. package/dist/hooks/harness/hermes.js.map +1 -0
  80. package/dist/hooks/harness/pi.d.ts +4 -0
  81. package/dist/hooks/harness/pi.js +21 -0
  82. package/dist/hooks/harness/pi.js.map +1 -0
  83. package/dist/hooks/prompt-submit.d.ts +16 -0
  84. package/dist/hooks/prompt-submit.js +11 -0
  85. package/dist/hooks/prompt-submit.js.map +1 -0
  86. package/dist/hooks/session-start.d.ts +3 -0
  87. package/dist/hooks/session-start.js +31 -0
  88. package/dist/hooks/session-start.js.map +1 -0
  89. package/dist/hooks/stable-context.d.ts +11 -0
  90. package/dist/hooks/stable-context.js +117 -0
  91. package/dist/hooks/stable-context.js.map +1 -0
  92. package/dist/hooks/stop-log.d.ts +4 -0
  93. package/dist/hooks/stop-log.js +12 -0
  94. package/dist/hooks/stop-log.js.map +1 -0
  95. package/dist/hooks/summarizer-pick.d.ts +8 -0
  96. package/dist/hooks/summarizer-pick.js +18 -0
  97. package/dist/hooks/summarizer-pick.js.map +1 -0
  98. package/dist/hooks/transcript.d.ts +6 -0
  99. package/dist/hooks/transcript.js +72 -0
  100. package/dist/hooks/transcript.js.map +1 -0
  101. package/dist/hooks/types.d.ts +56 -0
  102. package/dist/hooks/types.js +2 -0
  103. package/dist/hooks/types.js.map +1 -0
  104. package/dist/mcp-curate-server.js +50 -4
  105. package/dist/mcp-curate-server.js.map +1 -1
  106. package/dist/mcp-server.js +121 -58
  107. package/dist/mcp-server.js.map +1 -1
  108. package/dist/mcp-shared.js +119 -29
  109. package/dist/mcp-shared.js.map +1 -1
  110. package/dist/memory-search.d.ts +5 -2
  111. package/dist/memory-search.js +5 -2
  112. package/dist/memory-search.js.map +1 -1
  113. package/dist/migrate/o-decompose.d.ts +14 -0
  114. package/dist/migrate/o-decompose.js +112 -0
  115. package/dist/migrate/o-decompose.js.map +1 -0
  116. package/dist/migrate/tree.d.ts +16 -0
  117. package/dist/migrate/tree.js +102 -0
  118. package/dist/migrate/tree.js.map +1 -0
  119. package/dist/migrate/v1-reader.d.ts +38 -0
  120. package/dist/migrate/v1-reader.js +34 -0
  121. package/dist/migrate/v1-reader.js.map +1 -0
  122. package/dist/migrate/v1-to-v2.d.ts +25 -0
  123. package/dist/migrate/v1-to-v2.js +127 -0
  124. package/dist/migrate/v1-to-v2.js.map +1 -0
  125. package/dist/session-state.d.ts +3 -0
  126. package/dist/session-state.js +7 -5
  127. package/dist/session-state.js.map +1 -1
  128. package/dist/store/db.d.ts +25 -0
  129. package/dist/store/db.js +159 -0
  130. package/dist/store/db.js.map +1 -0
  131. package/dist/store/entries.d.ts +48 -0
  132. package/dist/store/entries.js +145 -0
  133. package/dist/store/entries.js.map +1 -0
  134. package/dist/store/events-fts.d.ts +18 -0
  135. package/dist/store/events-fts.js +18 -0
  136. package/dist/store/events-fts.js.map +1 -0
  137. package/dist/store/events.d.ts +30 -0
  138. package/dist/store/events.js +48 -0
  139. package/dist/store/events.js.map +1 -0
  140. package/dist/store/fts.d.ts +24 -0
  141. package/dist/store/fts.js +35 -0
  142. package/dist/store/fts.js.map +1 -0
  143. package/dist/store/links.d.ts +24 -0
  144. package/dist/store/links.js +29 -0
  145. package/dist/store/links.js.map +1 -0
  146. package/dist/store/schema-engine.d.ts +20 -0
  147. package/dist/store/schema-engine.js +41 -0
  148. package/dist/store/schema-engine.js.map +1 -0
  149. package/dist/store/schema-v2.sql +149 -0
  150. package/dist/store/sessions.d.ts +38 -0
  151. package/dist/store/sessions.js +98 -0
  152. package/dist/store/sessions.js.map +1 -0
  153. package/dist/store/types.d.ts +74 -0
  154. package/dist/store/types.js +3 -0
  155. package/dist/store/types.js.map +1 -0
  156. package/dist/store/ulid.d.ts +1 -0
  157. package/dist/store/ulid.js +10 -0
  158. package/dist/store/ulid.js.map +1 -0
  159. package/dist/summarize/anthropic.d.ts +8 -0
  160. package/dist/summarize/anthropic.js +38 -0
  161. package/dist/summarize/anthropic.js.map +1 -0
  162. package/dist/summarize/loop.d.ts +40 -0
  163. package/dist/summarize/loop.js +64 -0
  164. package/dist/summarize/loop.js.map +1 -0
  165. package/dist/sync/api.d.ts +49 -0
  166. package/dist/sync/api.js +99 -0
  167. package/dist/sync/api.js.map +1 -0
  168. package/dist/sync/config.d.ts +16 -0
  169. package/dist/sync/config.js +27 -0
  170. package/dist/sync/config.js.map +1 -0
  171. package/dist/sync/conflict.d.ts +24 -0
  172. package/dist/sync/conflict.js +125 -0
  173. package/dist/sync/conflict.js.map +1 -0
  174. package/dist/sync/crypto.d.ts +53 -0
  175. package/dist/sync/crypto.js +140 -0
  176. package/dist/sync/crypto.js.map +1 -0
  177. package/dist/sync/passphrase.d.ts +1 -0
  178. package/dist/sync/passphrase.js +17 -0
  179. package/dist/sync/passphrase.js.map +1 -0
  180. package/dist/sync/v2/dispatch.d.ts +6 -0
  181. package/dist/sync/v2/dispatch.js +24 -0
  182. package/dist/sync/v2/dispatch.js.map +1 -0
  183. package/dist/sync/v2/envelope.d.ts +13 -0
  184. package/dist/sync/v2/envelope.js +36 -0
  185. package/dist/sync/v2/envelope.js.map +1 -0
  186. package/dist/sync/v2/export.d.ts +10 -0
  187. package/dist/sync/v2/export.js +32 -0
  188. package/dist/sync/v2/export.js.map +1 -0
  189. package/dist/sync/v2/import.d.ts +10 -0
  190. package/dist/sync/v2/import.js +130 -0
  191. package/dist/sync/v2/import.js.map +1 -0
  192. package/dist/sync/v2/queue.d.ts +18 -0
  193. package/dist/sync/v2/queue.js +59 -0
  194. package/dist/sync/v2/queue.js.map +1 -0
  195. package/dist/sync/v2/reconcile.d.ts +3 -0
  196. package/dist/sync/v2/reconcile.js +18 -0
  197. package/dist/sync/v2/reconcile.js.map +1 -0
  198. package/dist/sync/v2/staging.d.ts +27 -0
  199. package/dist/sync/v2/staging.js +46 -0
  200. package/dist/sync/v2/staging.js.map +1 -0
  201. package/dist/sync/v2/sync.d.ts +18 -0
  202. package/dist/sync/v2/sync.js +63 -0
  203. package/dist/sync/v2/sync.js.map +1 -0
  204. package/dist/sync/v2/transport.d.ts +43 -0
  205. package/dist/sync/v2/transport.js +20 -0
  206. package/dist/sync/v2/transport.js.map +1 -0
  207. package/dist/sync-bridge.d.ts +2 -0
  208. package/dist/sync-bridge.js +134 -0
  209. package/dist/sync-bridge.js.map +1 -0
  210. package/dist/tools/analyze-v1.d.ts +1 -0
  211. package/dist/tools/analyze-v1.js +142 -0
  212. package/dist/tools/analyze-v1.js.map +1 -0
  213. package/dist/util/iso.d.ts +8 -0
  214. package/dist/util/iso.js +15 -0
  215. package/dist/util/iso.js.map +1 -0
  216. package/dist/utils.d.ts +7 -0
  217. package/dist/utils.js +14 -0
  218. package/dist/utils.js.map +1 -0
  219. package/dist/v2/addressing.d.ts +9 -0
  220. package/dist/v2/addressing.js +55 -0
  221. package/dist/v2/addressing.js.map +1 -0
  222. package/dist/v2/checkpoint-agent.d.ts +75 -0
  223. package/dist/v2/checkpoint-agent.js +231 -0
  224. package/dist/v2/checkpoint-agent.js.map +1 -0
  225. package/dist/v2/checkpoint-select.d.ts +60 -0
  226. package/dist/v2/checkpoint-select.js +106 -0
  227. package/dist/v2/checkpoint-select.js.map +1 -0
  228. package/dist/v2/checkpoint.d.ts +62 -0
  229. package/dist/v2/checkpoint.js +103 -0
  230. package/dist/v2/checkpoint.js.map +1 -0
  231. package/dist/v2/curation.d.ts +23 -0
  232. package/dist/v2/curation.js +118 -0
  233. package/dist/v2/curation.js.map +1 -0
  234. package/dist/v2/projects.d.ts +29 -0
  235. package/dist/v2/projects.js +60 -0
  236. package/dist/v2/projects.js.map +1 -0
  237. package/dist/v2/read-render.d.ts +31 -0
  238. package/dist/v2/read-render.js +150 -0
  239. package/dist/v2/read-render.js.map +1 -0
  240. package/dist/v2/related.d.ts +8 -0
  241. package/dist/v2/related.js +35 -0
  242. package/dist/v2/related.js.map +1 -0
  243. package/dist/v2/search.d.ts +6 -0
  244. package/dist/v2/search.js +24 -0
  245. package/dist/v2/search.js.map +1 -0
  246. package/dist/v2/session-id.d.ts +7 -0
  247. package/dist/v2/session-id.js +14 -0
  248. package/dist/v2/session-id.js.map +1 -0
  249. package/dist/v2/sessions-tools.d.ts +11 -0
  250. package/dist/v2/sessions-tools.js +30 -0
  251. package/dist/v2/sessions-tools.js.map +1 -0
  252. package/dist/v2/store-facade.d.ts +23 -0
  253. package/dist/v2/store-facade.js +33 -0
  254. package/dist/v2/store-facade.js.map +1 -0
  255. package/dist/v2/summarize-adapter.d.ts +11 -0
  256. package/dist/v2/summarize-adapter.js +19 -0
  257. package/dist/v2/summarize-adapter.js.map +1 -0
  258. package/dist/v2/writes.d.ts +30 -0
  259. package/dist/v2/writes.js +135 -0
  260. package/dist/v2/writes.js.map +1 -0
  261. package/hmem.config.example.json +92 -10
  262. package/opencode-plugin/hmem.js +5 -2
  263. package/package.json +7 -3
  264. package/plugins/hermes-hmem/README.md +56 -0
  265. package/plugins/hermes-hmem/__init__.py +187 -0
  266. package/plugins/hermes-hmem/plugin.yaml +8 -0
  267. package/server.json +2 -2
  268. package/skills/hmem-activate/SKILL.md +1 -1
  269. package/skills/hmem-config/SKILL.md +70 -5
  270. package/skills/hmem-curate/SKILL.md +13 -6
  271. package/skills/hmem-dispatch/SKILL.md +1 -1
  272. package/skills/hmem-migrate-o/SKILL.md +3 -8
  273. package/skills/hmem-new-error/SKILL.md +1 -9
  274. package/skills/hmem-new-project/SKILL.md +1 -9
  275. package/skills/hmem-new-rule/SKILL.md +93 -0
  276. package/skills/hmem-read/SKILL.md +16 -8
  277. package/skills/hmem-release/SKILL.md +108 -9
  278. package/skills/hmem-search/SKILL.md +48 -25
  279. package/skills/hmem-session-start/SKILL.md +147 -46
  280. package/skills/hmem-setup/SKILL.md +3 -3
  281. package/skills/hmem-sync-setup/SKILL.md +1 -5
  282. package/skills/hmem-update/SKILL.md +345 -19
  283. package/skills/hmem-wipe/SKILL.md +41 -7
  284. package/skills/hmem-write/SKILL.md +1 -12
  285. package/skills/hmem-activate-workspace/evals/evals.json +0 -29
  286. package/skills/hmem-activate-workspace/iteration-1/benchmark.json +0 -370
  287. package/skills/hmem-activate-workspace/iteration-1/benchmark.md +0 -13
  288. package/skills/hmem-activate-workspace/iteration-1/eval-1-simple-switch/eval_metadata.json +0 -23
  289. package/skills/hmem-activate-workspace/iteration-1/eval-1-simple-switch/with_skill/grading.json +0 -55
  290. package/skills/hmem-activate-workspace/iteration-1/eval-1-simple-switch/with_skill/outputs/response.md +0 -121
  291. package/skills/hmem-activate-workspace/iteration-1/eval-1-simple-switch/with_skill/run-1/grading.json +0 -55
  292. package/skills/hmem-activate-workspace/iteration-1/eval-1-simple-switch/without_skill/grading.json +0 -44
  293. package/skills/hmem-activate-workspace/iteration-1/eval-1-simple-switch/without_skill/outputs/response.md +0 -78
  294. package/skills/hmem-activate-workspace/iteration-1/eval-1-simple-switch/without_skill/run-1/grading.json +0 -44
  295. package/skills/hmem-activate-workspace/iteration-1/eval-2-misrouting-fix/eval_metadata.json +0 -23
  296. package/skills/hmem-activate-workspace/iteration-1/eval-2-misrouting-fix/with_skill/grading.json +0 -49
  297. package/skills/hmem-activate-workspace/iteration-1/eval-2-misrouting-fix/with_skill/outputs/response.md +0 -98
  298. package/skills/hmem-activate-workspace/iteration-1/eval-2-misrouting-fix/with_skill/run-1/grading.json +0 -49
  299. package/skills/hmem-activate-workspace/iteration-1/eval-2-misrouting-fix/without_skill/grading.json +0 -44
  300. package/skills/hmem-activate-workspace/iteration-1/eval-2-misrouting-fix/without_skill/outputs/response.md +0 -57
  301. package/skills/hmem-activate-workspace/iteration-1/eval-2-misrouting-fix/without_skill/run-1/grading.json +0 -44
  302. package/skills/hmem-activate-workspace/iteration-1/eval-3-temporary-load/eval_metadata.json +0 -23
  303. package/skills/hmem-activate-workspace/iteration-1/eval-3-temporary-load/with_skill/grading.json +0 -48
  304. package/skills/hmem-activate-workspace/iteration-1/eval-3-temporary-load/with_skill/outputs/response.md +0 -69
  305. package/skills/hmem-activate-workspace/iteration-1/eval-3-temporary-load/with_skill/run-1/grading.json +0 -48
  306. package/skills/hmem-activate-workspace/iteration-1/eval-3-temporary-load/without_skill/grading.json +0 -44
  307. package/skills/hmem-activate-workspace/iteration-1/eval-3-temporary-load/without_skill/outputs/response.md +0 -59
  308. package/skills/hmem-activate-workspace/iteration-1/eval-3-temporary-load/without_skill/run-1/grading.json +0 -44
  309. package/skills/hmem-activate-workspace/iteration-1/eval-4-wrong-project/eval_metadata.json +0 -23
  310. package/skills/hmem-activate-workspace/iteration-1/eval-4-wrong-project/with_skill/grading.json +0 -55
  311. package/skills/hmem-activate-workspace/iteration-1/eval-4-wrong-project/with_skill/outputs/response.md +0 -182
  312. package/skills/hmem-activate-workspace/iteration-1/eval-4-wrong-project/with_skill/run-1/grading.json +0 -55
  313. package/skills/hmem-activate-workspace/iteration-1/eval-4-wrong-project/without_skill/grading.json +0 -44
  314. package/skills/hmem-activate-workspace/iteration-1/eval-4-wrong-project/without_skill/outputs/response.md +0 -43
  315. package/skills/hmem-activate-workspace/iteration-1/eval-4-wrong-project/without_skill/run-1/grading.json +0 -44
@@ -0,0 +1,320 @@
1
+ /**
2
+ * cli-checkpoint-agent.ts
3
+ *
4
+ * Provider-agnostic agentic loop for the checkpoint agent.
5
+ * Replaces the `claude -p --model haiku` subprocess spawn.
6
+ *
7
+ * Supports: Anthropic (native), OpenAI-compatible (OpenAI, DeepSeek, Groq, etc.)
8
+ */
9
+ import fs from "node:fs";
10
+ import os from "node:os";
11
+ import path from "node:path";
12
+ import { execFileSync, execSync } from "node:child_process";
13
+ import Anthropic from "@anthropic-ai/sdk";
14
+ import OpenAI from "openai";
15
+ // ── Tool schemas (shared between providers) ──────────────────────────────────
16
+ const TOOL_SCHEMAS = {
17
+ read_memory: {
18
+ type: "object",
19
+ properties: {
20
+ id: { type: "string", description: "Node ID, e.g. 'P0048' or 'O0048.3'" },
21
+ search: { type: "string", description: "Full-text search query" },
22
+ prefix: { type: "string", description: "Filter by category prefix (L, P, D, E, ...)" },
23
+ },
24
+ },
25
+ write_memory: {
26
+ type: "object",
27
+ properties: {
28
+ prefix: { type: "string", description: "Entry prefix: L, D, E, R, C" },
29
+ content: { type: "string", description: "Title + optional body (tab-indented children)" },
30
+ tags: { type: "array", items: { type: "string" }, description: "Hashtags, e.g. ['#hmem', '#bug']" },
31
+ links: { type: "array", items: { type: "string" }, description: "Linked entry IDs" },
32
+ },
33
+ required: ["prefix", "content"],
34
+ },
35
+ update_memory: {
36
+ type: "object",
37
+ properties: {
38
+ id: { type: "string", description: "Node ID to update" },
39
+ content: { type: "string", description: "New content (replaces existing)" },
40
+ tags: { type: "array", items: { type: "string" } },
41
+ irrelevant: { type: "boolean" },
42
+ favorite: { type: "boolean" },
43
+ },
44
+ required: ["id"],
45
+ },
46
+ append_memory: {
47
+ type: "object",
48
+ properties: {
49
+ id: { type: "string", description: "Parent node ID" },
50
+ content: { type: "string", description: "Child content (tab-indented for nesting)" },
51
+ },
52
+ required: ["id", "content"],
53
+ },
54
+ move_nodes: {
55
+ type: "object",
56
+ properties: {
57
+ node_ids: { type: "array", items: { type: "string" }, description: "Exchange node IDs to move" },
58
+ target_o_id: { type: "string", description: "Target O-entry ID, e.g. 'O0042'" },
59
+ },
60
+ required: ["node_ids", "target_o_id"],
61
+ },
62
+ list_projects: {
63
+ type: "object",
64
+ properties: {},
65
+ },
66
+ };
67
+ // ── Tool executor ─────────────────────────────────────────────────────────────
68
+ function executeTool(name, input, store) {
69
+ try {
70
+ switch (name) {
71
+ case "read_memory": {
72
+ const { id, search, prefix } = input;
73
+ const entries = store.read({ id, search, prefix, depth: 3 });
74
+ if (!entries.length)
75
+ return "No results found.";
76
+ return entries.slice(0, 20).map(e => `${e.id} ${e.level_1 ?? ""}${e.level_2 ? "\n " + e.level_2 : ""}`).join("\n");
77
+ }
78
+ case "write_memory": {
79
+ const { prefix, content, tags, links } = input;
80
+ const result = store.write(prefix, content, links, undefined, undefined, tags);
81
+ return `Created: ${result.id}`;
82
+ }
83
+ case "update_memory": {
84
+ const { id, content, tags, irrelevant, favorite } = input;
85
+ const ok = store.updateNode(id, content, undefined, undefined, favorite, undefined, irrelevant, tags);
86
+ return ok ? `Updated: ${id}` : `Not found: ${id}`;
87
+ }
88
+ case "append_memory": {
89
+ const { id, content } = input;
90
+ const result = store.appendChildren(id, content);
91
+ return `Appended ${result.count} node(s): ${result.ids.join(", ")}`;
92
+ }
93
+ case "move_nodes": {
94
+ const { node_ids, target_o_id } = input;
95
+ const result = store.moveNodes(node_ids, target_o_id);
96
+ return `Moved ${result.moved}. Errors: ${result.errors.join(", ") || "none"}`;
97
+ }
98
+ case "list_projects": {
99
+ const projects = store.listProjects();
100
+ return projects.map(p => ` ${p.id} ${p.title}`).join("\n");
101
+ }
102
+ default:
103
+ return `Unknown tool: ${name}`;
104
+ }
105
+ }
106
+ catch (e) {
107
+ return `Error: ${e?.message ?? String(e)}`;
108
+ }
109
+ }
110
+ // ── Anthropic loop ────────────────────────────────────────────────────────────
111
+ async function runAnthropicLoop(prompt, store, config) {
112
+ const apiKeyEnv = config.checkpointApiKeyEnv ?? "ANTHROPIC_API_KEY";
113
+ const apiKey = process.env[apiKeyEnv];
114
+ if (!apiKey)
115
+ throw new Error(`Missing env var: ${apiKeyEnv}`);
116
+ const client = new Anthropic({ apiKey });
117
+ const tools = Object.entries(TOOL_SCHEMAS).map(([name, schema]) => ({
118
+ name,
119
+ description: `hmem ${name}`,
120
+ input_schema: schema,
121
+ }));
122
+ const messages = [{ role: "user", content: prompt }];
123
+ for (let i = 0; i < 40; i++) {
124
+ const response = await client.messages.create({
125
+ model: config.checkpointModel,
126
+ max_tokens: 4096,
127
+ tools,
128
+ messages,
129
+ });
130
+ messages.push({ role: "assistant", content: response.content });
131
+ if (response.stop_reason === "end_turn")
132
+ break;
133
+ const toolUses = response.content.filter((b) => b.type === "tool_use");
134
+ if (!toolUses.length)
135
+ break;
136
+ const results = toolUses.map(block => ({
137
+ type: "tool_result",
138
+ tool_use_id: block.id,
139
+ content: executeTool(block.name, block.input, store),
140
+ }));
141
+ messages.push({ role: "user", content: results });
142
+ }
143
+ }
144
+ // ── OpenAI-compatible loop ────────────────────────────────────────────────────
145
+ async function runOpenAILoop(prompt, store, config) {
146
+ const apiKeyEnv = config.checkpointApiKeyEnv ?? "OPENAI_API_KEY";
147
+ const apiKey = process.env[apiKeyEnv];
148
+ if (!apiKey)
149
+ throw new Error(`Missing env var: ${apiKeyEnv}`);
150
+ const client = new OpenAI({
151
+ apiKey,
152
+ ...(config.checkpointBaseUrl ? { baseURL: config.checkpointBaseUrl } : {}),
153
+ });
154
+ const tools = Object.entries(TOOL_SCHEMAS).map(([name, schema]) => ({
155
+ type: "function",
156
+ function: { name, description: `hmem ${name}`, parameters: schema },
157
+ }));
158
+ const messages = [{ role: "user", content: prompt }];
159
+ for (let i = 0; i < 40; i++) {
160
+ const response = await client.chat.completions.create({
161
+ model: config.checkpointModel,
162
+ tools,
163
+ messages,
164
+ });
165
+ const choice = response.choices[0];
166
+ if (!choice)
167
+ break;
168
+ messages.push(choice.message);
169
+ if (choice.finish_reason === "stop" || !choice.message.tool_calls?.length)
170
+ break;
171
+ for (const call of choice.message.tool_calls) {
172
+ if (!("function" in call))
173
+ continue;
174
+ let input = {};
175
+ try {
176
+ input = JSON.parse(call.function.arguments);
177
+ }
178
+ catch { }
179
+ const result = executeTool(call.function.name, input, store);
180
+ messages.push({ role: "tool", tool_call_id: call.id, content: result });
181
+ }
182
+ }
183
+ }
184
+ // ── claude -p subprocess fallback (for Max/OAuth users without API key) ────────
185
+ function buildMcpConfig(projectDir, hmemPath) {
186
+ let hmemServerPath;
187
+ try {
188
+ hmemServerPath = execSync("which hmem", { encoding: "utf8" }).trim();
189
+ const realPath = fs.realpathSync(hmemServerPath);
190
+ hmemServerPath = path.join(path.dirname(realPath), "mcp-server.js");
191
+ if (!fs.existsSync(hmemServerPath)) {
192
+ hmemServerPath = path.join(path.dirname(path.dirname(realPath)), "dist", "mcp-server.js");
193
+ }
194
+ }
195
+ catch {
196
+ hmemServerPath = path.join(process.env.HOME || "/home", ".nvm/versions/node", process.version, "lib/node_modules/hmem-mcp/dist/mcp-server.js");
197
+ }
198
+ const mcpConfig = {
199
+ mcpServers: {
200
+ hmem: {
201
+ command: process.execPath,
202
+ args: [hmemServerPath],
203
+ env: { HMEM_PROJECT_DIR: projectDir, HMEM_PATH: hmemPath, HMEM_NO_SESSION: "1" },
204
+ },
205
+ },
206
+ };
207
+ const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "hmem-checkpoint-"));
208
+ fs.chmodSync(tmpDir, 0o700);
209
+ const tmpPath = path.join(tmpDir, "mcp-config.json");
210
+ fs.writeFileSync(tmpPath, JSON.stringify(mcpConfig), "utf8");
211
+ return tmpPath;
212
+ }
213
+ async function runClaudeSubprocess(prompt, hmemPath, model) {
214
+ const projectDir = path.dirname(hmemPath);
215
+ const mcpConfigPath = buildMcpConfig(projectDir, hmemPath);
216
+ try {
217
+ execFileSync("claude", [
218
+ "-p", "--model", model,
219
+ "--mcp-config", mcpConfigPath,
220
+ "--allowedTools", "mcp__hmem__update_memory mcp__hmem__write_memory mcp__hmem__append_memory mcp__hmem__read_memory mcp__hmem__move_nodes mcp__hmem__list_projects",
221
+ "--dangerously-skip-permissions",
222
+ ], { input: prompt, encoding: "utf8", timeout: 120_000 });
223
+ }
224
+ finally {
225
+ try {
226
+ fs.unlinkSync(mcpConfigPath);
227
+ }
228
+ catch { }
229
+ try {
230
+ fs.rmdirSync(path.dirname(mcpConfigPath));
231
+ }
232
+ catch { }
233
+ }
234
+ }
235
+ function hasClaudeBinary() {
236
+ try {
237
+ execSync("which claude", { stdio: "ignore" });
238
+ return true;
239
+ }
240
+ catch {
241
+ return false;
242
+ }
243
+ }
244
+ /**
245
+ * Detect which AI harness invoked us. Honors explicit HMEM_HARNESS override first
246
+ * (plugins/extensions should set this when they spawn `hmem checkpoint`); falls
247
+ * back to env-var signatures the harnesses set themselves.
248
+ */
249
+ export function detectHarness() {
250
+ const explicit = process.env.HMEM_HARNESS?.toLowerCase();
251
+ if (explicit === "claude-code" || explicit === "codex" || explicit === "opencode" ||
252
+ explicit === "pi" || explicit === "hermes") {
253
+ return explicit;
254
+ }
255
+ if (process.env.CLAUDECODE || process.env.CLAUDE_CODE_SESSION_ID)
256
+ return "claude-code";
257
+ if (process.env.CODEX_SESSION_ID || process.env.CODEX_API_KEY)
258
+ return "codex";
259
+ return "unknown";
260
+ }
261
+ // ── Public entry point ────────────────────────────────────────────────────────
262
+ /**
263
+ * Harness-aware provider routing:
264
+ * - Claude Code → Haiku via `claude -p` subprocess (uses Max OAuth, no API key)
265
+ * - Codex → gpt-5.4-mini via OpenAI API (uses OPENAI_API_KEY)
266
+ * - OpenCode / Pi / Hermes / unknown → user's configured provider (hmem.config.json)
267
+ *
268
+ * The harness routing takes precedence over the configured provider — Claude Code
269
+ * users explicitly want Haiku-via-OAuth (covered by Max), not their DeepSeek key.
270
+ */
271
+ export async function runCheckpointAgent(prompt, store, config, hmemPath) {
272
+ const harness = detectHarness();
273
+ console.error(`[hmem checkpoint] harness=${harness} hmemPath=${hmemPath ? "set" : "unset"} claudeBin=${hasClaudeBinary()}`);
274
+ // 1. Claude Code → Haiku via `claude -p` (Max OAuth, no API key needed)
275
+ if (harness === "claude-code" && hmemPath && hasClaudeBinary()) {
276
+ console.error(`[hmem checkpoint] → routing to claude -p (Haiku via Max OAuth)`);
277
+ store.close();
278
+ await runClaudeSubprocess(prompt, hmemPath, "claude-haiku-4-5-20251001");
279
+ return;
280
+ }
281
+ // 2. Codex → gpt-5.4-mini via OpenAI API
282
+ if (harness === "codex") {
283
+ const codexConfig = {
284
+ ...config,
285
+ checkpointProvider: "openai",
286
+ checkpointModel: "gpt-5.4-mini",
287
+ checkpointApiKeyEnv: "OPENAI_API_KEY",
288
+ checkpointBaseUrl: undefined,
289
+ };
290
+ await runOpenAILoop(prompt, store, codexConfig);
291
+ return;
292
+ }
293
+ // 3. OpenCode / Pi / Hermes / unknown → user's configured provider
294
+ const apiKeyEnv = config.checkpointApiKeyEnv
295
+ ?? (config.checkpointProvider === "openai" ? "OPENAI_API_KEY" : "ANTHROPIC_API_KEY");
296
+ const apiKey = process.env[apiKeyEnv];
297
+ if (apiKey) {
298
+ console.error(`[hmem checkpoint] → routing to ${config.checkpointProvider} API (${config.checkpointModel})`);
299
+ if (config.checkpointProvider === "openai") {
300
+ await runOpenAILoop(prompt, store, config);
301
+ }
302
+ else {
303
+ await runAnthropicLoop(prompt, store, config);
304
+ }
305
+ return;
306
+ }
307
+ // 4. Last-resort: claude -p subprocess if Claude binary is on PATH (zero-config Claude Max)
308
+ if (hmemPath && hasClaudeBinary()) {
309
+ store.close();
310
+ await runClaudeSubprocess(prompt, hmemPath, config.checkpointModel);
311
+ return;
312
+ }
313
+ throw new Error(`[hmem checkpoint] No checkpoint provider available for harness=${harness}.\n` +
314
+ "Configure one in hmem.config.json → memory:\n" +
315
+ ' "checkpointProvider": "anthropic" (or "openai")\n' +
316
+ ' "checkpointModel": "claude-haiku-4-5-20251001"\n' +
317
+ ' "checkpointApiKeyEnv": "ANTHROPIC_API_KEY" (env var name holding your key)\n' +
318
+ "Or install `claude` CLI for the Claude Max zero-config fallback.");
319
+ }
320
+ //# sourceMappingURL=cli-checkpoint-agent.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli-checkpoint-agent.js","sourceRoot":"","sources":["../src/cli-checkpoint-agent.ts"],"names":[],"mappings":"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"}
@@ -14,49 +14,48 @@
14
14
  * HMEM_PROJECT_DIR — directory for config + company.hmem
15
15
  */
16
16
  import fs from "node:fs";
17
- import os from "node:os";
18
17
  import path from "node:path";
19
- import { execFileSync, execSync } from "node:child_process";
18
+ import { spawn } from "node:child_process";
20
19
  import { HmemStore } from "./hmem-store.js";
21
20
  import { loadHmemConfig } from "./hmem-config.js";
22
21
  import { currentSessionId } from "./session-state.js";
23
- /** Build a minimal MCP config JSON for the subagent (hmem only). */
24
- function buildMcpConfig(projectDir, hmemPath) {
25
- // Find the hmem-mcp entry point
26
- let hmemServerPath;
22
+ import { runCheckpointAgent } from "./cli-checkpoint-agent.js";
23
+ function syncReady(hmemPath) {
24
+ if (!process.env.HMEM_SYNC_PASSPHRASE)
25
+ return false;
27
26
  try {
28
- hmemServerPath = execSync("which hmem", { encoding: "utf8" }).trim();
29
- // Resolve symlink to find the actual JS file
30
- const realPath = fs.realpathSync(hmemServerPath);
31
- hmemServerPath = path.join(path.dirname(realPath), "mcp-server.js");
32
- if (!fs.existsSync(hmemServerPath)) {
33
- // Fallback: look in the dist directory relative to the resolved path
34
- hmemServerPath = path.join(path.dirname(path.dirname(realPath)), "dist", "mcp-server.js");
35
- }
27
+ const cfgPath = path.join(path.dirname(hmemPath), "config.json");
28
+ if (!fs.existsSync(cfgPath))
29
+ return false;
30
+ const cfg = JSON.parse(fs.readFileSync(cfgPath, "utf8"));
31
+ if (!cfg.api_key || !cfg.active_file)
32
+ return false;
33
+ const fileCfg = cfg.files?.[cfg.active_file];
34
+ return !!(fileCfg?.salt && fileCfg?.hmem_path);
36
35
  }
37
36
  catch {
38
- // Fallback: global npm path
39
- hmemServerPath = path.join(process.env.HOME || "/home", ".nvm/versions/node", process.version, "lib/node_modules/hmem-mcp/dist/mcp-server.js");
37
+ return false;
40
38
  }
41
- const nodePath = process.execPath;
42
- const config = {
43
- mcpServers: {
44
- hmem: {
45
- command: nodePath,
46
- args: [hmemServerPath],
47
- env: {
48
- HMEM_PROJECT_DIR: projectDir,
49
- HMEM_PATH: hmemPath,
50
- HMEM_NO_SESSION: "1",
51
- },
52
- },
53
- },
54
- };
55
- const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "hmem-checkpoint-"));
56
- fs.chmodSync(tmpDir, 0o700);
57
- const tmpPath = path.join(tmpDir, "mcp-config.json");
58
- fs.writeFileSync(tmpPath, JSON.stringify(config), "utf8");
59
- return tmpPath;
39
+ }
40
+ async function runSyncChild(args, label) {
41
+ await new Promise((resolve) => {
42
+ const child = spawn(process.execPath, [process.argv[1], "sync", ...args], {
43
+ env: { ...process.env },
44
+ stdio: ["ignore", "pipe", "pipe"],
45
+ windowsHide: true,
46
+ });
47
+ let stderr = "";
48
+ child.stderr?.on("data", (d) => { stderr += d.toString(); });
49
+ child.on("close", (code) => {
50
+ if (code !== 0)
51
+ console.error(`[hmem checkpoint] sync ${label} exited ${code}: ${stderr.trim()}`);
52
+ resolve();
53
+ });
54
+ child.on("error", (err) => {
55
+ console.error(`[hmem checkpoint] sync ${label} spawn failed: ${err.message}`);
56
+ resolve();
57
+ });
58
+ });
60
59
  }
61
60
  export async function checkpoint() {
62
61
  const projectDir = process.env.HMEM_PROJECT_DIR;
@@ -65,24 +64,119 @@ export async function checkpoint() {
65
64
  const hmemPath = process.env.HMEM_PATH;
66
65
  if (!fs.existsSync(hmemPath))
67
66
  process.exit(0);
67
+ const doSync = syncReady(hmemPath);
68
+ if (doSync)
69
+ await runSyncChild(["pull"], "pull");
68
70
  const config = loadHmemConfig(path.dirname(hmemPath));
69
71
  const store = new HmemStore(hmemPath, config);
70
- let mcpConfigPath = "";
71
72
  try {
72
- // 1. Get active project and its O-entry (prefer env from log-exchange, fallback to DB)
73
+ // 1. Get active project and its O-entry. Tries in order:
74
+ // (a) HMEM_ACTIVE_PROJECT env (set by log-exchange when it knows the project)
75
+ // (b) getActiveProject(currentSessionId) — session marker → DB active=1 flag
76
+ // (c) getMostRecentlyActiveProject() — newest O-entry exchange (heuristic)
77
+ // The previous "silent return on null" hid genuine misrouted exchanges; loud
78
+ // logging + a heuristic fallback prevents the multi-device "session-marker stripped
79
+ // but exchanges still arrive" failure mode.
73
80
  const envProjectId = process.env.HMEM_ACTIVE_PROJECT;
74
- const activeProject = envProjectId
81
+ let activeProject = envProjectId
75
82
  ? store.getProjectById(envProjectId)
76
83
  : store.getActiveProject(currentSessionId());
77
- if (!activeProject)
84
+ if (!activeProject) {
85
+ const recent = store.getMostRecentlyActiveProject();
86
+ if (recent) {
87
+ activeProject = recent;
88
+ console.error(`[hmem checkpoint] No active-project marker — falling back to most recent O-entry activity: ${recent.id}`);
89
+ }
90
+ }
91
+ if (!activeProject) {
92
+ console.error("[hmem checkpoint] No active project found. Tried: " +
93
+ `env(HMEM_ACTIVE_PROJECT)=${envProjectId ?? "(unset)"}, ` +
94
+ `session-marker(sessionId=${currentSessionId() ?? "(none)"})=miss, ` +
95
+ "DB active=1 flag=miss, recent O-entry activity=none. " +
96
+ "Skipping checkpoint — exchanges will accumulate without summary until a project is activated.");
78
97
  return;
98
+ }
79
99
  const projectSeq = parseInt(activeProject.id.replace(/\D/g, ""), 10);
80
100
  const oId = store.resolveProjectO(projectSeq);
81
101
  // 2. Find the latest full batch (L3 with >= batchSize L4 children)
82
102
  const batchSize = config.checkpointInterval || 5;
83
103
  const latestFullBatch = store.getLatestFullBatch(oId, batchSize);
84
- if (!latestFullBatch)
104
+ // Determine current session node to exclude from orphan search
105
+ const latestSession = store.getChildNodes(oId)
106
+ .filter(n => n.depth === 2)
107
+ .sort((a, b) => b.seq - a.seq)[0];
108
+ const currentSessionNodeId = latestFullBatch?.sessionId ?? latestSession?.id ?? null;
109
+ // Find orphaned batches from previous sessions that never got summarized.
110
+ // cap=5: catch up backlog without exceeding context budget (~5KB per batch after truncation).
111
+ const orphanedBatches = store.getOrphanedBatches(oId, currentSessionNodeId);
112
+ if (!latestFullBatch && orphanedBatches.length === 0)
85
113
  return;
114
+ // Build orphan section text once — used by both the orphan-only path
115
+ // (when no full batch) and the normal path (as Section 0b alongside the latest batch).
116
+ // Side effect: tags #skill-dialog on matching exchanges.
117
+ const orphanSections = orphanedBatches.map((ob, i) => {
118
+ const exs = store.getOEntryExchangesV2(oId, 20, { sessionScope: [ob.sessionId] });
119
+ const batchExs = exs.filter(ex => ex.nodeId.startsWith(ob.batchId + "."));
120
+ if (batchExs.length === 0)
121
+ return null;
122
+ const formatted = batchExs.map((ex, j) => {
123
+ let user = ex.userText.replace(/<channel[^>]*>\s*/g, "").replace(/<\/channel>\s*/g, "").trim();
124
+ let agent = (ex.agentText ?? "").replace(/<[^>]+>/g, "").trim();
125
+ user = user.length > 800 ? user.substring(0, 800) + "..." : user;
126
+ agent = agent.length > 1200 ? agent.substring(0, 1200) + "..." : agent;
127
+ return `--- Exchange ${j + 1} (${ex.nodeId}) ---\nUSER: ${user}\nAGENT: ${agent}`;
128
+ }).join("\n\n");
129
+ const listing = batchExs.map(ex => ` ${ex.nodeId}: "${ex.title}"`).join("\n");
130
+ for (const ex of batchExs) {
131
+ if (ex.userText.includes("Base directory for this skill:")) {
132
+ store.addTag(ex.nodeId, "#skill-dialog");
133
+ }
134
+ }
135
+ return `=== Orphan Batch ${i + 1}: ${ob.batchId} | Session ${ob.sessionId} (${ob.sessionDate}) — "${ob.sessionTitle}" ===
136
+ Current exchange titles:
137
+ ${listing}
138
+
139
+ ${formatted}`;
140
+ }).filter((s) => s !== null);
141
+ // Orphan-only path: no full batch but orphaned batches exist
142
+ if (!latestFullBatch) {
143
+ const pName = activeProject.title.split("|")[0].trim();
144
+ const pId = activeProject.id;
145
+ const pList = store.listProjects().map(p => ` ${p.id} ${p.title}`).join("\n");
146
+ if (orphanSections.length === 0)
147
+ return;
148
+ const orphanPrompt = `You are a checkpoint agent for "${pName}" (${pId}).
149
+ Process ${orphanSections.length} orphaned batch(es) — prior sessions too short for a regular checkpoint.
150
+
151
+ == All Projects ==
152
+ ${pList}
153
+
154
+ ${orphanSections.join("\n\n")}
155
+
156
+ ## Tasks for EACH batch above (Tasks 1, 2, 5, 6 only):
157
+
158
+ ### 1. Title each exchange
159
+ For each: update_memory(id="<nodeId>", content="Descriptive title, max 50 chars, match conversation language")
160
+ GOOD: "Fix: cleanTitle strips separators" | BAD: "Projekt laden" or "ok"
161
+
162
+ ### 2. Write rolling summary for each batch
163
+ update_memory(id="<batchId>", content="Summary: 2-5 sentences covering this batch. Match conversation language.")
164
+
165
+ ### 5. Tag exchanges
166
+ For each exchange, add ONE tag if applicable: #skill-dialog, #irrelevant, #planning, #debugging, #admin, #meta, #repetition
167
+
168
+ ### 6. Update each session title and summary
169
+ update_memory(id="<sessionId>", content="Short session title, max 60 chars\\n> 2-5 sentences. Key decisions, outcomes. Written for someone picking up cold.")
170
+
171
+ ## Rules:
172
+ - Match language of existing entries
173
+ - Tags: lowercase with #
174
+ - Do NOT extract L/E/D entries (skip Task 3)
175
+ - Do NOT update P-entry (skip Task 4)`;
176
+ await runCheckpointAgent(orphanPrompt, store, config);
177
+ console.log(`[hmem checkpoint] Orphan batches processed: ${orphanSections.length} (${config.checkpointProvider}/${config.checkpointModel})`);
178
+ return;
179
+ }
86
180
  const batchId = latestFullBatch.id;
87
181
  const sessionId = latestFullBatch.sessionId;
88
182
  // 3. Get exchanges from this batch
@@ -126,10 +220,7 @@ export async function checkpoint() {
126
220
  const allProjects = store.listProjects();
127
221
  const projectName = activeProject.title.split("|")[0].trim();
128
222
  const projectId = activeProject.id;
129
- // Close store before spawning subagent
130
- store.close();
131
- // 7. Build MCP config and prompt
132
- mcpConfigPath = buildMcpConfig(projectDir, hmemPath);
223
+ // 7. Build prompt
133
224
  const formattedExchanges = batchExchanges.map((ex, i) => {
134
225
  // Strip XML channel tags from Telegram messages before passing to Haiku
135
226
  let user = ex.userText.replace(/<channel[^>]*>\s*/g, "").replace(/<\/channel>\s*/g, "").trim();
@@ -167,6 +258,16 @@ Call: update_memory(id="<id>", content="Short title\\n> 2-5 sentences. Skip if n
167
258
 
168
259
  ${catchupSessions.map(d => `Session ${d.id} (${d.date}) — "${d.title}":\n${d.body}`).join("\n\n")}
169
260
 
261
+ `
262
+ : "";
263
+ // Section 0b: orphan batches from previous sessions that never got summarized.
264
+ // Applies Tasks 1, 2, 5 to each (title, batch summary, tag) — NOT P-entry updates or knowledge extraction.
265
+ const orphanCatchupSection = orphanSections.length > 0
266
+ ? `### 0b. Catch up unsummarized batches from past sessions
267
+ ${orphanSections.length} prior batch(es) were never summarized — likely because a newer full batch arrived first. Apply Tasks 1, 2, and 5 to EACH batch below (titles, rolling summary on the batch node, exchange tags). Do NOT extract L/E/D entries from orphans (Task 3) and do NOT update the P-entry (Task 4) from them.
268
+
269
+ ${orphanSections.join("\n\n")}
270
+
170
271
  `
171
272
  : "";
172
273
  const prompt = `You are a checkpoint agent for "${projectName}" (${projectId}).
@@ -181,7 +282,7 @@ ${prevSummaryText}
181
282
  == Batch Exchanges ==
182
283
  ${formattedExchanges}
183
284
 
184
- ${catchupSection}## Tasks (execute ALL in order):
285
+ ${catchupSection}${orphanCatchupSection}## Tasks (execute ALL in order):
185
286
 
186
287
  ### 1. Title each exchange (REQUIRED)
187
288
  Current titles (auto-extracted):
@@ -256,50 +357,36 @@ Do ALL exchanges belong to ${projectName}?
256
357
  Check against the project list above. If an exchange belongs elsewhere, call:
257
358
  move_nodes(node_ids=["<exchange_id>"], target_o_id="O00XX")
258
359
 
360
+ ### 8. Update O-entry project state (${oId})
361
+ 1. read_memory(id="${oId}") — read the CURRENT body first
362
+ 2. Synthesize: keep what's still true, drop what's outdated, add what changed this session
363
+ 3. update_memory(id="${oId}", content="<fresh replacement, max 4 sentences>")
364
+
365
+ The result must be a REPLACEMENT, not an addition. Cap at 4 sentences total regardless of how much is in the old body.
366
+ Content: current project status, what just shipped/changed, top open item or next step.
367
+ Written for someone picking up cold — concrete specifics, no jargon.
368
+ Skip if this session was purely admin/infra with no meaningful project-level change.
369
+ This body is injected verbatim into every load_project briefing.
370
+
259
371
  ## Rules:
260
372
  - read_memory() FIRST to see current state
261
373
  - Match language of existing entries
262
374
  - Tags: 3-5 per entry, lowercase with #
263
375
  - Only save what's valuable in 6 months`;
264
- // 8. Spawn Haiku with MCP access
265
- const allowedTools = [
266
- "mcp__hmem__read_memory",
267
- "mcp__hmem__write_memory",
268
- "mcp__hmem__append_memory",
269
- "mcp__hmem__update_memory",
270
- "mcp__hmem__list_projects",
271
- "mcp__hmem__move_nodes",
272
- ].join(" ");
273
- const disallowedTools = "mcp__hmem__flush_context";
274
- try {
275
- const output = execFileSync("claude", [
276
- "-p", "--model", "haiku",
277
- "--mcp-config", mcpConfigPath,
278
- "--allowedTools", allowedTools,
279
- "--disallowedTools", disallowedTools,
280
- "--dangerously-skip-permissions",
281
- ], { input: prompt, encoding: "utf8", timeout: 120_000 }).trim();
282
- console.log(`[hmem checkpoint] Haiku: ${output.substring(0, 300)}`);
283
- }
284
- catch (e) {
285
- const stdout = e.stdout?.toString()?.substring(0, 200) || "";
286
- console.error(`[hmem checkpoint] Failed (exit ${e.status}): ${stdout}`);
287
- }
376
+ // 8. Run checkpoint agent (actual provider chosen by runCheckpointAgent based on harness — see its diagnostic logs)
377
+ await runCheckpointAgent(prompt, store, config, hmemPath);
378
+ console.log(`[hmem checkpoint] Done (batch ${batchId}, ${batchExchanges.length} exchanges)`);
288
379
  }
289
380
  catch (e) {
290
381
  console.error(`[hmem checkpoint] ${e}`);
291
382
  }
292
383
  finally {
293
- if (mcpConfigPath) {
294
- try {
295
- fs.unlinkSync(mcpConfigPath);
296
- }
297
- catch { }
298
- try {
299
- fs.rmdirSync(path.dirname(mcpConfigPath));
300
- }
301
- catch { }
384
+ try {
385
+ store.close();
302
386
  }
387
+ catch { }
388
+ if (doSync)
389
+ await runSyncChild(["push", "--skip-pull"], "push");
303
390
  }
304
391
  }
305
392
  //# sourceMappingURL=cli-checkpoint.js.map