@vinaes/succ 1.3.31 → 1.5.37

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 (759) hide show
  1. package/README.md +65 -11
  2. package/agents/succ-code-reviewer.md +1 -1
  3. package/agents/succ-diff-reviewer.md +1 -1
  4. package/agents/succ-general.md +1 -1
  5. package/dist/cli.js +84 -2
  6. package/dist/cli.js.map +1 -1
  7. package/dist/commands/agents-md.d.ts.map +1 -1
  8. package/dist/commands/agents-md.js +3 -2
  9. package/dist/commands/agents-md.js.map +1 -1
  10. package/dist/commands/analyze-agents.d.ts.map +1 -1
  11. package/dist/commands/analyze-agents.js +10 -11
  12. package/dist/commands/analyze-agents.js.map +1 -1
  13. package/dist/commands/analyze-profile.d.ts.map +1 -1
  14. package/dist/commands/analyze-profile.js +32 -8
  15. package/dist/commands/analyze-profile.js.map +1 -1
  16. package/dist/commands/analyze-recursive.d.ts.map +1 -1
  17. package/dist/commands/analyze-recursive.js +8 -3
  18. package/dist/commands/analyze-recursive.js.map +1 -1
  19. package/dist/commands/analyze-utils.d.ts.map +1 -1
  20. package/dist/commands/analyze-utils.js +17 -4
  21. package/dist/commands/analyze-utils.js.map +1 -1
  22. package/dist/commands/benchmark-quality.d.ts.map +1 -1
  23. package/dist/commands/benchmark-quality.js +11 -4
  24. package/dist/commands/benchmark-quality.js.map +1 -1
  25. package/dist/commands/benchmark-sqlite-vec.d.ts.map +1 -1
  26. package/dist/commands/benchmark-sqlite-vec.js +4 -0
  27. package/dist/commands/benchmark-sqlite-vec.js.map +1 -1
  28. package/dist/commands/benchmark.d.ts.map +1 -1
  29. package/dist/commands/benchmark.js +5 -1
  30. package/dist/commands/benchmark.js.map +1 -1
  31. package/dist/commands/codex-chat.d.ts +8 -0
  32. package/dist/commands/codex-chat.d.ts.map +1 -0
  33. package/dist/commands/codex-chat.js +161 -0
  34. package/dist/commands/codex-chat.js.map +1 -0
  35. package/dist/commands/config.d.ts.map +1 -1
  36. package/dist/commands/config.js +32 -4
  37. package/dist/commands/config.js.map +1 -1
  38. package/dist/commands/daemon.d.ts.map +1 -1
  39. package/dist/commands/daemon.js +13 -4
  40. package/dist/commands/daemon.js.map +1 -1
  41. package/dist/commands/index-code.d.ts +4 -0
  42. package/dist/commands/index-code.d.ts.map +1 -1
  43. package/dist/commands/index-code.js +1 -1
  44. package/dist/commands/index-code.js.map +1 -1
  45. package/dist/commands/init-templates.js +2 -2
  46. package/dist/commands/init.d.ts.map +1 -1
  47. package/dist/commands/init.js +305 -203
  48. package/dist/commands/init.js.map +1 -1
  49. package/dist/commands/memories.d.ts.map +1 -1
  50. package/dist/commands/memories.js +25 -14
  51. package/dist/commands/memories.js.map +1 -1
  52. package/dist/commands/progress.d.ts.map +1 -1
  53. package/dist/commands/progress.js +3 -2
  54. package/dist/commands/progress.js.map +1 -1
  55. package/dist/commands/reindex.d.ts.map +1 -1
  56. package/dist/commands/reindex.js +54 -36
  57. package/dist/commands/reindex.js.map +1 -1
  58. package/dist/commands/retention.d.ts.map +1 -1
  59. package/dist/commands/retention.js +7 -5
  60. package/dist/commands/retention.js.map +1 -1
  61. package/dist/commands/scan-code.d.ts +76 -0
  62. package/dist/commands/scan-code.d.ts.map +1 -0
  63. package/dist/commands/scan-code.js +385 -0
  64. package/dist/commands/scan-code.js.map +1 -0
  65. package/dist/commands/score.d.ts.map +1 -1
  66. package/dist/commands/score.js +3 -2
  67. package/dist/commands/score.js.map +1 -1
  68. package/dist/commands/session.d.ts +33 -0
  69. package/dist/commands/session.d.ts.map +1 -0
  70. package/dist/commands/session.js +163 -0
  71. package/dist/commands/session.js.map +1 -0
  72. package/dist/commands/setup.d.ts.map +1 -1
  73. package/dist/commands/setup.js +254 -15
  74. package/dist/commands/setup.js.map +1 -1
  75. package/dist/commands/soul.d.ts.map +1 -1
  76. package/dist/commands/soul.js +7 -46
  77. package/dist/commands/soul.js.map +1 -1
  78. package/dist/commands/status.d.ts.map +1 -1
  79. package/dist/commands/status.js +14 -5
  80. package/dist/commands/status.js.map +1 -1
  81. package/dist/commands/watch.d.ts.map +1 -1
  82. package/dist/commands/watch.js +13 -4
  83. package/dist/commands/watch.js.map +1 -1
  84. package/dist/daemon/analyzer.d.ts.map +1 -1
  85. package/dist/daemon/analyzer.js +23 -7
  86. package/dist/daemon/analyzer.js.map +1 -1
  87. package/dist/daemon/client.d.ts.map +1 -1
  88. package/dist/daemon/client.js +32 -8
  89. package/dist/daemon/client.js.map +1 -1
  90. package/dist/daemon/routes/analyzer.d.ts +3 -0
  91. package/dist/daemon/routes/analyzer.d.ts.map +1 -0
  92. package/dist/daemon/routes/analyzer.js +27 -0
  93. package/dist/daemon/routes/analyzer.js.map +1 -0
  94. package/dist/daemon/routes/hooks.d.ts +14 -0
  95. package/dist/daemon/routes/hooks.d.ts.map +1 -0
  96. package/dist/daemon/routes/hooks.js +1212 -0
  97. package/dist/daemon/routes/hooks.js.map +1 -0
  98. package/dist/daemon/routes/memory.d.ts +4 -0
  99. package/dist/daemon/routes/memory.d.ts.map +1 -0
  100. package/dist/daemon/routes/memory.js +71 -0
  101. package/dist/daemon/routes/memory.js.map +1 -0
  102. package/dist/daemon/routes/reflection.d.ts +10 -0
  103. package/dist/daemon/routes/reflection.d.ts.map +1 -0
  104. package/dist/daemon/routes/reflection.js +397 -0
  105. package/dist/daemon/routes/reflection.js.map +1 -0
  106. package/dist/daemon/routes/search.d.ts +5 -0
  107. package/dist/daemon/routes/search.d.ts.map +1 -0
  108. package/dist/daemon/routes/search.js +93 -0
  109. package/dist/daemon/routes/search.js.map +1 -0
  110. package/dist/daemon/routes/sessions.d.ts +3 -0
  111. package/dist/daemon/routes/sessions.d.ts.map +1 -0
  112. package/dist/daemon/routes/sessions.js +160 -0
  113. package/dist/daemon/routes/sessions.js.map +1 -0
  114. package/dist/daemon/routes/skills.d.ts +3 -0
  115. package/dist/daemon/routes/skills.d.ts.map +1 -0
  116. package/dist/daemon/routes/skills.js +36 -0
  117. package/dist/daemon/routes/skills.js.map +1 -0
  118. package/dist/daemon/routes/status.d.ts +3 -0
  119. package/dist/daemon/routes/status.d.ts.map +1 -0
  120. package/dist/daemon/routes/status.js +47 -0
  121. package/dist/daemon/routes/status.js.map +1 -0
  122. package/dist/daemon/routes/types.d.ts +240 -0
  123. package/dist/daemon/routes/types.d.ts.map +1 -0
  124. package/dist/daemon/routes/types.js +97 -0
  125. package/dist/daemon/routes/types.js.map +1 -0
  126. package/dist/daemon/routes/versioning.d.ts +27 -0
  127. package/dist/daemon/routes/versioning.d.ts.map +1 -0
  128. package/dist/daemon/routes/versioning.js +44 -0
  129. package/dist/daemon/routes/versioning.js.map +1 -0
  130. package/dist/daemon/routes/watcher.d.ts +3 -0
  131. package/dist/daemon/routes/watcher.d.ts.map +1 -0
  132. package/dist/daemon/routes/watcher.js +28 -0
  133. package/dist/daemon/routes/watcher.js.map +1 -0
  134. package/dist/daemon/service.d.ts +5 -23
  135. package/dist/daemon/service.d.ts.map +1 -1
  136. package/dist/daemon/service.js +177 -934
  137. package/dist/daemon/service.js.map +1 -1
  138. package/dist/daemon/session-processor.d.ts +4 -8
  139. package/dist/daemon/session-processor.d.ts.map +1 -1
  140. package/dist/daemon/session-processor.js +49 -222
  141. package/dist/daemon/session-processor.js.map +1 -1
  142. package/dist/lib/ai-readiness.d.ts.map +1 -1
  143. package/dist/lib/ai-readiness.js +33 -8
  144. package/dist/lib/ai-readiness.js.map +1 -1
  145. package/dist/lib/analyze-state.d.ts.map +1 -1
  146. package/dist/lib/analyze-state.js +25 -3
  147. package/dist/lib/analyze-state.js.map +1 -1
  148. package/dist/lib/auto-memory/consolidation.d.ts +41 -0
  149. package/dist/lib/auto-memory/consolidation.d.ts.map +1 -0
  150. package/dist/lib/auto-memory/consolidation.js +151 -0
  151. package/dist/lib/auto-memory/consolidation.js.map +1 -0
  152. package/dist/lib/bpe.d.ts.map +1 -1
  153. package/dist/lib/bpe.js +9 -10
  154. package/dist/lib/bpe.js.map +1 -1
  155. package/dist/lib/brain-export.d.ts +65 -0
  156. package/dist/lib/brain-export.d.ts.map +1 -0
  157. package/dist/lib/brain-export.js +413 -0
  158. package/dist/lib/brain-export.js.map +1 -0
  159. package/dist/lib/checkpoint.d.ts.map +1 -1
  160. package/dist/lib/checkpoint.js +22 -6
  161. package/dist/lib/checkpoint.js.map +1 -1
  162. package/dist/lib/chunker.d.ts.map +1 -1
  163. package/dist/lib/chunker.js +6 -1
  164. package/dist/lib/chunker.js.map +1 -1
  165. package/dist/lib/claude-ws-transport.d.ts.map +1 -1
  166. package/dist/lib/claude-ws-transport.js +12 -4
  167. package/dist/lib/claude-ws-transport.js.map +1 -1
  168. package/dist/lib/command-safety.d.ts +64 -0
  169. package/dist/lib/command-safety.d.ts.map +1 -0
  170. package/dist/lib/command-safety.js +625 -0
  171. package/dist/lib/command-safety.js.map +1 -0
  172. package/dist/lib/compact-briefing.d.ts.map +1 -1
  173. package/dist/lib/compact-briefing.js +18 -17
  174. package/dist/lib/compact-briefing.js.map +1 -1
  175. package/dist/lib/config-defaults.d.ts.map +1 -1
  176. package/dist/lib/config-defaults.js +3 -0
  177. package/dist/lib/config-defaults.js.map +1 -1
  178. package/dist/lib/config-display.d.ts +4 -0
  179. package/dist/lib/config-display.d.ts.map +1 -1
  180. package/dist/lib/config-display.js +6 -1
  181. package/dist/lib/config-display.js.map +1 -1
  182. package/dist/lib/config-types.d.ts +157 -0
  183. package/dist/lib/config-types.d.ts.map +1 -1
  184. package/dist/lib/config-validation.d.ts.map +1 -1
  185. package/dist/lib/config-validation.js +6 -0
  186. package/dist/lib/config-validation.js.map +1 -1
  187. package/dist/lib/config.d.ts +8 -0
  188. package/dist/lib/config.d.ts.map +1 -1
  189. package/dist/lib/config.js +108 -9
  190. package/dist/lib/config.js.map +1 -1
  191. package/dist/lib/consolidate.d.ts.map +1 -1
  192. package/dist/lib/consolidate.js +73 -49
  193. package/dist/lib/consolidate.js.map +1 -1
  194. package/dist/lib/content-sanitizer.d.ts +29 -0
  195. package/dist/lib/content-sanitizer.d.ts.map +1 -0
  196. package/dist/lib/content-sanitizer.js +72 -0
  197. package/dist/lib/content-sanitizer.js.map +1 -0
  198. package/dist/lib/cross-repo.d.ts +44 -0
  199. package/dist/lib/cross-repo.d.ts.map +1 -0
  200. package/dist/lib/cross-repo.js +108 -0
  201. package/dist/lib/cross-repo.js.map +1 -0
  202. package/dist/lib/daemon-port.d.ts +12 -0
  203. package/dist/lib/daemon-port.d.ts.map +1 -0
  204. package/dist/lib/daemon-port.js +20 -0
  205. package/dist/lib/daemon-port.js.map +1 -0
  206. package/dist/lib/db/auto-memory.d.ts +40 -0
  207. package/dist/lib/db/auto-memory.d.ts.map +1 -0
  208. package/dist/lib/db/auto-memory.js +74 -0
  209. package/dist/lib/db/auto-memory.js.map +1 -0
  210. package/dist/lib/db/bm25-indexes.d.ts.map +1 -1
  211. package/dist/lib/db/bm25-indexes.js +16 -4
  212. package/dist/lib/db/bm25-indexes.js.map +1 -1
  213. package/dist/lib/db/connection.d.ts.map +1 -1
  214. package/dist/lib/db/connection.js +8 -1
  215. package/dist/lib/db/connection.js.map +1 -1
  216. package/dist/lib/db/documents.d.ts.map +1 -1
  217. package/dist/lib/db/documents.js +4 -1
  218. package/dist/lib/db/documents.js.map +1 -1
  219. package/dist/lib/db/global-memories.d.ts +2 -10
  220. package/dist/lib/db/global-memories.d.ts.map +1 -1
  221. package/dist/lib/db/global-memories.js +13 -6
  222. package/dist/lib/db/global-memories.js.map +1 -1
  223. package/dist/lib/db/graph.d.ts +5 -1
  224. package/dist/lib/db/graph.d.ts.map +1 -1
  225. package/dist/lib/db/graph.js +38 -8
  226. package/dist/lib/db/graph.js.map +1 -1
  227. package/dist/lib/db/hybrid-search.d.ts +4 -2
  228. package/dist/lib/db/hybrid-search.d.ts.map +1 -1
  229. package/dist/lib/db/hybrid-search.js +29 -11
  230. package/dist/lib/db/hybrid-search.js.map +1 -1
  231. package/dist/lib/db/index.d.ts +6 -1
  232. package/dist/lib/db/index.d.ts.map +1 -1
  233. package/dist/lib/db/index.js +5 -1
  234. package/dist/lib/db/index.js.map +1 -1
  235. package/dist/lib/db/memories.d.ts +19 -14
  236. package/dist/lib/db/memories.d.ts.map +1 -1
  237. package/dist/lib/db/memories.js +100 -37
  238. package/dist/lib/db/memories.js.map +1 -1
  239. package/dist/lib/db/parse-helpers.d.ts +14 -0
  240. package/dist/lib/db/parse-helpers.d.ts.map +1 -0
  241. package/dist/lib/db/parse-helpers.js +59 -0
  242. package/dist/lib/db/parse-helpers.js.map +1 -0
  243. package/dist/lib/db/recall-events.d.ts +49 -0
  244. package/dist/lib/db/recall-events.d.ts.map +1 -0
  245. package/dist/lib/db/recall-events.js +196 -0
  246. package/dist/lib/db/recall-events.js.map +1 -0
  247. package/dist/lib/db/retention.d.ts +4 -3
  248. package/dist/lib/db/retention.d.ts.map +1 -1
  249. package/dist/lib/db/retention.js +12 -1
  250. package/dist/lib/db/retention.js.map +1 -1
  251. package/dist/lib/db/schema.d.ts +2 -0
  252. package/dist/lib/db/schema.d.ts.map +1 -1
  253. package/dist/lib/db/schema.js +140 -80
  254. package/dist/lib/db/schema.js.map +1 -1
  255. package/dist/lib/db/skills.d.ts.map +1 -1
  256. package/dist/lib/db/skills.js +10 -6
  257. package/dist/lib/db/skills.js.map +1 -1
  258. package/dist/lib/diff-brain.d.ts +24 -0
  259. package/dist/lib/diff-brain.d.ts.map +1 -0
  260. package/dist/lib/diff-brain.js +114 -0
  261. package/dist/lib/diff-brain.js.map +1 -0
  262. package/dist/lib/diff-parser.d.ts +74 -0
  263. package/dist/lib/diff-parser.d.ts.map +1 -0
  264. package/dist/lib/diff-parser.js +200 -0
  265. package/dist/lib/diff-parser.js.map +1 -0
  266. package/dist/lib/embedding-pool.d.ts.map +1 -1
  267. package/dist/lib/embedding-pool.js +5 -1
  268. package/dist/lib/embedding-pool.js.map +1 -1
  269. package/dist/lib/embeddings.d.ts +12 -0
  270. package/dist/lib/embeddings.d.ts.map +1 -1
  271. package/dist/lib/embeddings.js +77 -19
  272. package/dist/lib/embeddings.js.map +1 -1
  273. package/dist/lib/errors.d.ts +2 -0
  274. package/dist/lib/errors.d.ts.map +1 -1
  275. package/dist/lib/errors.js +4 -0
  276. package/dist/lib/errors.js.map +1 -1
  277. package/dist/lib/fault-logger.d.ts.map +1 -1
  278. package/dist/lib/fault-logger.js +22 -7
  279. package/dist/lib/fault-logger.js.map +1 -1
  280. package/dist/lib/git/co-change.d.ts +39 -0
  281. package/dist/lib/git/co-change.d.ts.map +1 -0
  282. package/dist/lib/git/co-change.js +139 -0
  283. package/dist/lib/git/co-change.js.map +1 -0
  284. package/dist/lib/graph/bridge-edges.d.ts +93 -0
  285. package/dist/lib/graph/bridge-edges.d.ts.map +1 -0
  286. package/dist/lib/graph/bridge-edges.js +276 -0
  287. package/dist/lib/graph/bridge-edges.js.map +1 -0
  288. package/dist/lib/graph/centrality.d.ts +11 -0
  289. package/dist/lib/graph/centrality.d.ts.map +1 -1
  290. package/dist/lib/graph/centrality.js +51 -3
  291. package/dist/lib/graph/centrality.js.map +1 -1
  292. package/dist/lib/graph/cleanup.d.ts.map +1 -1
  293. package/dist/lib/graph/cleanup.js +2 -1
  294. package/dist/lib/graph/cleanup.js.map +1 -1
  295. package/dist/lib/graph/community-detection.d.ts +17 -2
  296. package/dist/lib/graph/community-detection.d.ts.map +1 -1
  297. package/dist/lib/graph/community-detection.js +147 -48
  298. package/dist/lib/graph/community-detection.js.map +1 -1
  299. package/dist/lib/graph/community-summaries.d.ts +26 -0
  300. package/dist/lib/graph/community-summaries.d.ts.map +1 -0
  301. package/dist/lib/graph/community-summaries.js +130 -0
  302. package/dist/lib/graph/community-summaries.js.map +1 -0
  303. package/dist/lib/graph/contextual-proximity.d.ts.map +1 -1
  304. package/dist/lib/graph/contextual-proximity.js +11 -4
  305. package/dist/lib/graph/contextual-proximity.js.map +1 -1
  306. package/dist/lib/graph/graphology-bridge.d.ts +101 -0
  307. package/dist/lib/graph/graphology-bridge.d.ts.map +1 -0
  308. package/dist/lib/graph/graphology-bridge.js +488 -0
  309. package/dist/lib/graph/graphology-bridge.js.map +1 -0
  310. package/dist/lib/graph/llm-relations.d.ts.map +1 -1
  311. package/dist/lib/graph/llm-relations.js +34 -45
  312. package/dist/lib/graph/llm-relations.js.map +1 -1
  313. package/dist/lib/graph-export.d.ts.map +1 -1
  314. package/dist/lib/graph-export.js +2 -2
  315. package/dist/lib/graph-export.js.map +1 -1
  316. package/dist/lib/graph-scheduler.d.ts +0 -5
  317. package/dist/lib/graph-scheduler.d.ts.map +1 -1
  318. package/dist/lib/graph-scheduler.js +5 -1
  319. package/dist/lib/graph-scheduler.js.map +1 -1
  320. package/dist/lib/guardrails.d.ts +50 -0
  321. package/dist/lib/guardrails.d.ts.map +1 -0
  322. package/dist/lib/guardrails.js +502 -0
  323. package/dist/lib/guardrails.js.map +1 -0
  324. package/dist/lib/hook-rules.d.ts +1 -1
  325. package/dist/lib/hook-rules.d.ts.map +1 -1
  326. package/dist/lib/hook-rules.js +8 -2
  327. package/dist/lib/hook-rules.js.map +1 -1
  328. package/dist/lib/ifc/file-labels.d.ts +35 -0
  329. package/dist/lib/ifc/file-labels.d.ts.map +1 -0
  330. package/dist/lib/ifc/file-labels.js +208 -0
  331. package/dist/lib/ifc/file-labels.js.map +1 -0
  332. package/dist/lib/ifc/label.d.ts +38 -0
  333. package/dist/lib/ifc/label.d.ts.map +1 -0
  334. package/dist/lib/ifc/label.js +80 -0
  335. package/dist/lib/ifc/label.js.map +1 -0
  336. package/dist/lib/ifc/session-ifc.d.ts +92 -0
  337. package/dist/lib/ifc/session-ifc.d.ts.map +1 -0
  338. package/dist/lib/ifc/session-ifc.js +222 -0
  339. package/dist/lib/ifc/session-ifc.js.map +1 -0
  340. package/dist/lib/indexer.js +2 -2
  341. package/dist/lib/indexer.js.map +1 -1
  342. package/dist/lib/injection-detector.d.ts +83 -0
  343. package/dist/lib/injection-detector.d.ts.map +1 -0
  344. package/dist/lib/injection-detector.js +586 -0
  345. package/dist/lib/injection-detector.js.map +1 -0
  346. package/dist/lib/injection-semantic.d.ts +31 -0
  347. package/dist/lib/injection-semantic.d.ts.map +1 -0
  348. package/dist/lib/injection-semantic.js +230 -0
  349. package/dist/lib/injection-semantic.js.map +1 -0
  350. package/dist/lib/llm.d.ts +14 -0
  351. package/dist/lib/llm.d.ts.map +1 -1
  352. package/dist/lib/llm.js +44 -8
  353. package/dist/lib/llm.js.map +1 -1
  354. package/dist/lib/lock.d.ts.map +1 -1
  355. package/dist/lib/lock.js +24 -3
  356. package/dist/lib/lock.js.map +1 -1
  357. package/dist/lib/md-fetch.d.ts.map +1 -1
  358. package/dist/lib/md-fetch.js +9 -2
  359. package/dist/lib/md-fetch.js.map +1 -1
  360. package/dist/lib/observability.d.ts +75 -0
  361. package/dist/lib/observability.d.ts.map +1 -0
  362. package/dist/lib/observability.js +201 -0
  363. package/dist/lib/observability.js.map +1 -0
  364. package/dist/lib/ort-session.d.ts +26 -0
  365. package/dist/lib/ort-session.d.ts.map +1 -1
  366. package/dist/lib/ort-session.js +107 -3
  367. package/dist/lib/ort-session.js.map +1 -1
  368. package/dist/lib/prd/codebase-context.d.ts.map +1 -1
  369. package/dist/lib/prd/codebase-context.js +9 -2
  370. package/dist/lib/prd/codebase-context.js.map +1 -1
  371. package/dist/lib/prd/context.d.ts.map +1 -1
  372. package/dist/lib/prd/context.js +11 -3
  373. package/dist/lib/prd/context.js.map +1 -1
  374. package/dist/lib/prd/export.js +1 -1
  375. package/dist/lib/prd/export.js.map +1 -1
  376. package/dist/lib/prd/generate.d.ts.map +1 -1
  377. package/dist/lib/prd/generate.js +19 -5
  378. package/dist/lib/prd/generate.js.map +1 -1
  379. package/dist/lib/prd/parse.d.ts.map +1 -1
  380. package/dist/lib/prd/parse.js +8 -2
  381. package/dist/lib/prd/parse.js.map +1 -1
  382. package/dist/lib/prd/prompt-builder.d.ts +9 -2
  383. package/dist/lib/prd/prompt-builder.d.ts.map +1 -1
  384. package/dist/lib/prd/prompt-builder.js +7 -8
  385. package/dist/lib/prd/prompt-builder.js.map +1 -1
  386. package/dist/lib/prd/runner.d.ts +1 -2
  387. package/dist/lib/prd/runner.d.ts.map +1 -1
  388. package/dist/lib/prd/runner.js +45 -33
  389. package/dist/lib/prd/runner.js.map +1 -1
  390. package/dist/lib/prd/team-runner.js +2 -1
  391. package/dist/lib/prd/team-runner.js.map +1 -1
  392. package/dist/lib/prd/worktree.d.ts +1 -2
  393. package/dist/lib/prd/worktree.d.ts.map +1 -1
  394. package/dist/lib/prd/worktree.js +62 -70
  395. package/dist/lib/prd/worktree.js.map +1 -1
  396. package/dist/lib/precompute-context.d.ts.map +1 -1
  397. package/dist/lib/precompute-context.js +22 -36
  398. package/dist/lib/precompute-context.js.map +1 -1
  399. package/dist/lib/pricing.d.ts.map +1 -1
  400. package/dist/lib/pricing.js +5 -1
  401. package/dist/lib/pricing.js.map +1 -1
  402. package/dist/lib/process-registry.js +3 -3
  403. package/dist/lib/process-registry.js.map +1 -1
  404. package/dist/lib/public-api.d.ts +54 -4
  405. package/dist/lib/public-api.d.ts.map +1 -1
  406. package/dist/lib/public-api.js +52 -2
  407. package/dist/lib/public-api.js.map +1 -1
  408. package/dist/lib/quality.d.ts.map +1 -1
  409. package/dist/lib/quality.js +15 -6
  410. package/dist/lib/quality.js.map +1 -1
  411. package/dist/lib/query-expansion.d.ts +32 -0
  412. package/dist/lib/query-expansion.d.ts.map +1 -1
  413. package/dist/lib/query-expansion.js +64 -13
  414. package/dist/lib/query-expansion.js.map +1 -1
  415. package/dist/lib/reference-embeddings.d.ts.map +1 -1
  416. package/dist/lib/reference-embeddings.js +17 -4
  417. package/dist/lib/reference-embeddings.js.map +1 -1
  418. package/dist/lib/reflection-synthesizer.d.ts.map +1 -1
  419. package/dist/lib/reflection-synthesizer.js +9 -17
  420. package/dist/lib/reflection-synthesizer.js.map +1 -1
  421. package/dist/lib/reranker.d.ts +41 -0
  422. package/dist/lib/reranker.d.ts.map +1 -0
  423. package/dist/lib/reranker.js +294 -0
  424. package/dist/lib/reranker.js.map +1 -0
  425. package/dist/lib/retrieval-feedback.d.ts +100 -0
  426. package/dist/lib/retrieval-feedback.d.ts.map +1 -0
  427. package/dist/lib/retrieval-feedback.js +174 -0
  428. package/dist/lib/retrieval-feedback.js.map +1 -0
  429. package/dist/lib/review/context-pack.d.ts +58 -0
  430. package/dist/lib/review/context-pack.d.ts.map +1 -0
  431. package/dist/lib/review/context-pack.js +300 -0
  432. package/dist/lib/review/context-pack.js.map +1 -0
  433. package/dist/lib/search/hierarchical-summaries.d.ts +65 -0
  434. package/dist/lib/search/hierarchical-summaries.d.ts.map +1 -0
  435. package/dist/lib/search/hierarchical-summaries.js +423 -0
  436. package/dist/lib/search/hierarchical-summaries.js.map +1 -0
  437. package/dist/lib/search/hyde.d.ts +27 -0
  438. package/dist/lib/search/hyde.d.ts.map +1 -0
  439. package/dist/lib/search/hyde.js +141 -0
  440. package/dist/lib/search/hyde.js.map +1 -0
  441. package/dist/lib/search/late-chunking.d.ts +53 -0
  442. package/dist/lib/search/late-chunking.d.ts.map +1 -0
  443. package/dist/lib/search/late-chunking.js +230 -0
  444. package/dist/lib/search/late-chunking.js.map +1 -0
  445. package/dist/lib/search/ppr-retrieval.d.ts +49 -0
  446. package/dist/lib/search/ppr-retrieval.d.ts.map +1 -0
  447. package/dist/lib/search/ppr-retrieval.js +135 -0
  448. package/dist/lib/search/ppr-retrieval.js.map +1 -0
  449. package/dist/lib/search/repo-map.d.ts +43 -0
  450. package/dist/lib/search/repo-map.d.ts.map +1 -0
  451. package/dist/lib/search/repo-map.js +165 -0
  452. package/dist/lib/search/repo-map.js.map +1 -0
  453. package/dist/lib/session-analyzer.d.ts +90 -0
  454. package/dist/lib/session-analyzer.d.ts.map +1 -0
  455. package/dist/lib/session-analyzer.js +467 -0
  456. package/dist/lib/session-analyzer.js.map +1 -0
  457. package/dist/lib/session-observations.d.ts.map +1 -1
  458. package/dist/lib/session-observations.js +13 -3
  459. package/dist/lib/session-observations.js.map +1 -1
  460. package/dist/lib/session-summary.d.ts.map +1 -1
  461. package/dist/lib/session-summary.js +64 -52
  462. package/dist/lib/session-summary.js.map +1 -1
  463. package/dist/lib/session-surgeon.d.ts +53 -0
  464. package/dist/lib/session-surgeon.d.ts.map +1 -0
  465. package/dist/lib/session-surgeon.js +501 -0
  466. package/dist/lib/session-surgeon.js.map +1 -0
  467. package/dist/lib/similarity-utils.d.ts +26 -0
  468. package/dist/lib/similarity-utils.d.ts.map +1 -0
  469. package/dist/lib/similarity-utils.js +66 -0
  470. package/dist/lib/similarity-utils.js.map +1 -0
  471. package/dist/lib/skills.d.ts.map +1 -1
  472. package/dist/lib/skills.js +16 -16
  473. package/dist/lib/skills.js.map +1 -1
  474. package/dist/lib/storage/backends/interface.d.ts +13 -3
  475. package/dist/lib/storage/backends/interface.d.ts.map +1 -1
  476. package/dist/lib/storage/backends/postgresql.d.ts +52 -3
  477. package/dist/lib/storage/backends/postgresql.d.ts.map +1 -1
  478. package/dist/lib/storage/backends/postgresql.js +694 -49
  479. package/dist/lib/storage/backends/postgresql.js.map +1 -1
  480. package/dist/lib/storage/benchmark.js +2 -2
  481. package/dist/lib/storage/benchmark.js.map +1 -1
  482. package/dist/lib/storage/dispatcher/base.d.ts +114 -0
  483. package/dist/lib/storage/dispatcher/base.d.ts.map +1 -0
  484. package/dist/lib/storage/dispatcher/base.js +160 -0
  485. package/dist/lib/storage/dispatcher/base.js.map +1 -0
  486. package/dist/lib/storage/dispatcher/documents.d.ts +25 -0
  487. package/dist/lib/storage/dispatcher/documents.d.ts.map +1 -0
  488. package/dist/lib/storage/dispatcher/documents.js +194 -0
  489. package/dist/lib/storage/dispatcher/documents.js.map +1 -0
  490. package/dist/lib/storage/dispatcher/embeddings.d.ts +34 -0
  491. package/dist/lib/storage/dispatcher/embeddings.d.ts.map +1 -0
  492. package/dist/lib/storage/dispatcher/embeddings.js +144 -0
  493. package/dist/lib/storage/dispatcher/embeddings.js.map +1 -0
  494. package/dist/lib/storage/dispatcher/export-import.d.ts +139 -0
  495. package/dist/lib/storage/dispatcher/export-import.d.ts.map +1 -0
  496. package/dist/lib/storage/dispatcher/export-import.js +191 -0
  497. package/dist/lib/storage/dispatcher/export-import.js.map +1 -0
  498. package/dist/lib/storage/dispatcher/file-hashes.d.ts +13 -0
  499. package/dist/lib/storage/dispatcher/file-hashes.d.ts.map +1 -0
  500. package/dist/lib/storage/dispatcher/file-hashes.js +36 -0
  501. package/dist/lib/storage/dispatcher/file-hashes.js.map +1 -0
  502. package/dist/lib/storage/dispatcher/global-memories.d.ts +28 -0
  503. package/dist/lib/storage/dispatcher/global-memories.d.ts.map +1 -0
  504. package/dist/lib/storage/dispatcher/global-memories.js +151 -0
  505. package/dist/lib/storage/dispatcher/global-memories.js.map +1 -0
  506. package/dist/lib/storage/dispatcher/graph.d.ts +32 -0
  507. package/dist/lib/storage/dispatcher/graph.d.ts.map +1 -0
  508. package/dist/lib/storage/dispatcher/graph.js +146 -0
  509. package/dist/lib/storage/dispatcher/graph.js.map +1 -0
  510. package/dist/lib/storage/dispatcher/index.d.ts +34 -0
  511. package/dist/lib/storage/dispatcher/index.d.ts.map +1 -0
  512. package/dist/lib/storage/dispatcher/index.js +139 -0
  513. package/dist/lib/storage/dispatcher/index.js.map +1 -0
  514. package/dist/lib/storage/dispatcher/memories.d.ts +65 -0
  515. package/dist/lib/storage/dispatcher/memories.d.ts.map +1 -0
  516. package/dist/lib/storage/dispatcher/memories.js +466 -0
  517. package/dist/lib/storage/dispatcher/memories.js.map +1 -0
  518. package/dist/lib/storage/dispatcher/mixin-helper.d.ts +6 -0
  519. package/dist/lib/storage/dispatcher/mixin-helper.d.ts.map +1 -0
  520. package/dist/lib/storage/dispatcher/mixin-helper.js +10 -0
  521. package/dist/lib/storage/dispatcher/mixin-helper.js.map +1 -0
  522. package/dist/lib/storage/dispatcher/retention.d.ts +20 -0
  523. package/dist/lib/storage/dispatcher/retention.d.ts.map +1 -0
  524. package/dist/lib/storage/dispatcher/retention.js +123 -0
  525. package/dist/lib/storage/dispatcher/retention.js.map +1 -0
  526. package/dist/lib/storage/dispatcher/search.d.ts +34 -0
  527. package/dist/lib/storage/dispatcher/search.d.ts.map +1 -0
  528. package/dist/lib/storage/dispatcher/search.js +222 -0
  529. package/dist/lib/storage/dispatcher/search.js.map +1 -0
  530. package/dist/lib/storage/dispatcher/skills.d.ts +53 -0
  531. package/dist/lib/storage/dispatcher/skills.d.ts.map +1 -0
  532. package/dist/lib/storage/dispatcher/skills.js +98 -0
  533. package/dist/lib/storage/dispatcher/skills.js.map +1 -0
  534. package/dist/lib/storage/dispatcher/token-stats.d.ts +23 -0
  535. package/dist/lib/storage/dispatcher/token-stats.d.ts.map +1 -0
  536. package/dist/lib/storage/dispatcher/token-stats.js +92 -0
  537. package/dist/lib/storage/dispatcher/token-stats.js.map +1 -0
  538. package/dist/lib/storage/dispatcher/web-search.d.ts +10 -0
  539. package/dist/lib/storage/dispatcher/web-search.d.ts.map +1 -0
  540. package/dist/lib/storage/dispatcher/web-search.js +39 -0
  541. package/dist/lib/storage/dispatcher/web-search.js.map +1 -0
  542. package/dist/lib/storage/dispatcher-export.d.ts.map +1 -1
  543. package/dist/lib/storage/dispatcher-export.js +48 -39
  544. package/dist/lib/storage/dispatcher-export.js.map +1 -1
  545. package/dist/lib/storage/dispatcher.d.ts +1 -468
  546. package/dist/lib/storage/dispatcher.d.ts.map +1 -1
  547. package/dist/lib/storage/dispatcher.js +1 -1931
  548. package/dist/lib/storage/dispatcher.js.map +1 -1
  549. package/dist/lib/storage/index.d.ts +20 -5
  550. package/dist/lib/storage/index.d.ts.map +1 -1
  551. package/dist/lib/storage/index.js +36 -7
  552. package/dist/lib/storage/index.js.map +1 -1
  553. package/dist/lib/storage/migration/export-import.d.ts.map +1 -1
  554. package/dist/lib/storage/migration/export-import.js +9 -2
  555. package/dist/lib/storage/migration/export-import.js.map +1 -1
  556. package/dist/lib/storage/types.d.ts +152 -10
  557. package/dist/lib/storage/types.d.ts.map +1 -1
  558. package/dist/lib/storage/types.js +13 -0
  559. package/dist/lib/storage/types.js.map +1 -1
  560. package/dist/lib/storage/vector/interface.d.ts +4 -0
  561. package/dist/lib/storage/vector/interface.d.ts.map +1 -1
  562. package/dist/lib/storage/vector/qdrant.d.ts +13 -2
  563. package/dist/lib/storage/vector/qdrant.d.ts.map +1 -1
  564. package/dist/lib/storage/vector/qdrant.js +147 -61
  565. package/dist/lib/storage/vector/qdrant.js.map +1 -1
  566. package/dist/lib/supersession.d.ts.map +1 -1
  567. package/dist/lib/supersession.js +2 -15
  568. package/dist/lib/supersession.js.map +1 -1
  569. package/dist/lib/token-budget.d.ts.map +1 -1
  570. package/dist/lib/token-budget.js +9 -2
  571. package/dist/lib/token-budget.js.map +1 -1
  572. package/dist/lib/transcript-utils.d.ts +60 -0
  573. package/dist/lib/transcript-utils.d.ts.map +1 -0
  574. package/dist/lib/transcript-utils.js +69 -0
  575. package/dist/lib/transcript-utils.js.map +1 -0
  576. package/dist/lib/tree-sitter/extractor.d.ts +1 -1
  577. package/dist/lib/tree-sitter/extractor.d.ts.map +1 -1
  578. package/dist/lib/tree-sitter/extractor.js +34 -9
  579. package/dist/lib/tree-sitter/extractor.js.map +1 -1
  580. package/dist/lib/tree-sitter/parser.d.ts.map +1 -1
  581. package/dist/lib/tree-sitter/parser.js +45 -11
  582. package/dist/lib/tree-sitter/parser.js.map +1 -1
  583. package/dist/lib/tree-sitter/public.d.ts +12 -0
  584. package/dist/lib/tree-sitter/public.d.ts.map +1 -1
  585. package/dist/lib/tree-sitter/public.js +33 -1
  586. package/dist/lib/tree-sitter/public.js.map +1 -1
  587. package/dist/lib/tree-sitter/queries.d.ts.map +1 -1
  588. package/dist/lib/tree-sitter/queries.js +8 -0
  589. package/dist/lib/tree-sitter/queries.js.map +1 -1
  590. package/dist/lib/working-memory-pipeline.d.ts.map +1 -1
  591. package/dist/lib/working-memory-pipeline.js +12 -3
  592. package/dist/lib/working-memory-pipeline.js.map +1 -1
  593. package/dist/lib/worktree-detect.d.ts +43 -0
  594. package/dist/lib/worktree-detect.d.ts.map +1 -0
  595. package/dist/lib/worktree-detect.js +154 -0
  596. package/dist/lib/worktree-detect.js.map +1 -0
  597. package/dist/lsp/client.d.ts +96 -0
  598. package/dist/lsp/client.d.ts.map +1 -0
  599. package/dist/lsp/client.js +435 -0
  600. package/dist/lsp/client.js.map +1 -0
  601. package/dist/lsp/installer.d.ts +39 -0
  602. package/dist/lsp/installer.d.ts.map +1 -0
  603. package/dist/lsp/installer.js +275 -0
  604. package/dist/lsp/installer.js.map +1 -0
  605. package/dist/lsp/manager.d.ts +62 -0
  606. package/dist/lsp/manager.d.ts.map +1 -0
  607. package/dist/lsp/manager.js +234 -0
  608. package/dist/lsp/manager.js.map +1 -0
  609. package/dist/lsp/servers.d.ts +52 -0
  610. package/dist/lsp/servers.d.ts.map +1 -0
  611. package/dist/lsp/servers.js +162 -0
  612. package/dist/lsp/servers.js.map +1 -0
  613. package/dist/mcp/helpers.d.ts +11 -0
  614. package/dist/mcp/helpers.d.ts.map +1 -1
  615. package/dist/mcp/helpers.js +43 -2
  616. package/dist/mcp/helpers.js.map +1 -1
  617. package/dist/mcp/profile.d.ts +16 -0
  618. package/dist/mcp/profile.d.ts.map +1 -0
  619. package/dist/mcp/profile.js +64 -0
  620. package/dist/mcp/profile.js.map +1 -0
  621. package/dist/mcp/server.d.ts +17 -15
  622. package/dist/mcp/server.d.ts.map +1 -1
  623. package/dist/mcp/server.js +135 -22
  624. package/dist/mcp/server.js.map +1 -1
  625. package/dist/mcp/tools/config.d.ts +1 -5
  626. package/dist/mcp/tools/config.d.ts.map +1 -1
  627. package/dist/mcp/tools/config.js +169 -215
  628. package/dist/mcp/tools/config.js.map +1 -1
  629. package/dist/mcp/tools/dead-end.d.ts.map +1 -1
  630. package/dist/mcp/tools/dead-end.js +24 -14
  631. package/dist/mcp/tools/dead-end.js.map +1 -1
  632. package/dist/mcp/tools/debug.d.ts.map +1 -1
  633. package/dist/mcp/tools/debug.js +103 -179
  634. package/dist/mcp/tools/debug.js.map +1 -1
  635. package/dist/mcp/tools/graph.d.ts +0 -1
  636. package/dist/mcp/tools/graph.d.ts.map +1 -1
  637. package/dist/mcp/tools/graph.js +225 -233
  638. package/dist/mcp/tools/graph.js.map +1 -1
  639. package/dist/mcp/tools/indexing.d.ts +1 -7
  640. package/dist/mcp/tools/indexing.d.ts.map +1 -1
  641. package/dist/mcp/tools/indexing.js +180 -268
  642. package/dist/mcp/tools/indexing.js.map +1 -1
  643. package/dist/mcp/tools/memory/forget.d.ts +3 -0
  644. package/dist/mcp/tools/memory/forget.d.ts.map +1 -0
  645. package/dist/mcp/tools/memory/forget.js +175 -0
  646. package/dist/mcp/tools/memory/forget.js.map +1 -0
  647. package/dist/mcp/tools/memory/memory-helpers.d.ts +45 -0
  648. package/dist/mcp/tools/memory/memory-helpers.d.ts.map +1 -0
  649. package/dist/mcp/tools/memory/memory-helpers.js +291 -0
  650. package/dist/mcp/tools/memory/memory-helpers.js.map +1 -0
  651. package/dist/mcp/tools/memory/recall.d.ts +3 -0
  652. package/dist/mcp/tools/memory/recall.d.ts.map +1 -0
  653. package/dist/mcp/tools/memory/recall.js +495 -0
  654. package/dist/mcp/tools/memory/recall.js.map +1 -0
  655. package/dist/mcp/tools/memory/remember.d.ts +3 -0
  656. package/dist/mcp/tools/memory/remember.d.ts.map +1 -0
  657. package/dist/mcp/tools/memory/remember.js +256 -0
  658. package/dist/mcp/tools/memory/remember.js.map +1 -0
  659. package/dist/mcp/tools/memory/temporal-query.d.ts +8 -0
  660. package/dist/mcp/tools/memory/temporal-query.d.ts.map +1 -0
  661. package/dist/mcp/tools/memory/temporal-query.js +68 -0
  662. package/dist/mcp/tools/memory/temporal-query.js.map +1 -0
  663. package/dist/mcp/tools/memory.d.ts +0 -11
  664. package/dist/mcp/tools/memory.d.ts.map +1 -1
  665. package/dist/mcp/tools/memory.js +6 -1184
  666. package/dist/mcp/tools/memory.js.map +1 -1
  667. package/dist/mcp/tools/prd.d.ts +1 -7
  668. package/dist/mcp/tools/prd.d.ts.map +1 -1
  669. package/dist/mcp/tools/prd.js +198 -255
  670. package/dist/mcp/tools/prd.js.map +1 -1
  671. package/dist/mcp/tools/review.d.ts +8 -0
  672. package/dist/mcp/tools/review.d.ts.map +1 -0
  673. package/dist/mcp/tools/review.js +133 -0
  674. package/dist/mcp/tools/review.js.map +1 -0
  675. package/dist/mcp/tools/search.d.ts.map +1 -1
  676. package/dist/mcp/tools/search.js +168 -47
  677. package/dist/mcp/tools/search.js.map +1 -1
  678. package/dist/mcp/tools/status.d.ts +1 -5
  679. package/dist/mcp/tools/status.d.ts.map +1 -1
  680. package/dist/mcp/tools/status.js +270 -273
  681. package/dist/mcp/tools/status.js.map +1 -1
  682. package/dist/mcp/tools/web-fetch.d.ts +2 -1
  683. package/dist/mcp/tools/web-fetch.d.ts.map +1 -1
  684. package/dist/mcp/tools/web-fetch.js +72 -43
  685. package/dist/mcp/tools/web-fetch.js.map +1 -1
  686. package/dist/mcp/tools/web-search.d.ts +1 -6
  687. package/dist/mcp/tools/web-search.d.ts.map +1 -1
  688. package/dist/mcp/tools/web-search.js +199 -276
  689. package/dist/mcp/tools/web-search.js.map +1 -1
  690. package/dist/prompts/briefing.d.ts +10 -4
  691. package/dist/prompts/briefing.d.ts.map +1 -1
  692. package/dist/prompts/briefing.js +33 -35
  693. package/dist/prompts/briefing.js.map +1 -1
  694. package/dist/prompts/daemon.d.ts +5 -10
  695. package/dist/prompts/daemon.d.ts.map +1 -1
  696. package/dist/prompts/daemon.js +9 -18
  697. package/dist/prompts/daemon.js.map +1 -1
  698. package/dist/prompts/extraction.d.ts +19 -3
  699. package/dist/prompts/extraction.d.ts.map +1 -1
  700. package/dist/prompts/extraction.js +33 -46
  701. package/dist/prompts/extraction.js.map +1 -1
  702. package/dist/prompts/graph.d.ts +9 -0
  703. package/dist/prompts/graph.d.ts.map +1 -0
  704. package/dist/prompts/graph.js +27 -0
  705. package/dist/prompts/graph.js.map +1 -0
  706. package/dist/prompts/guardrails.d.ts +14 -0
  707. package/dist/prompts/guardrails.d.ts.map +1 -0
  708. package/dist/prompts/guardrails.js +115 -0
  709. package/dist/prompts/guardrails.js.map +1 -0
  710. package/dist/prompts/index.d.ts +17 -7
  711. package/dist/prompts/index.d.ts.map +1 -1
  712. package/dist/prompts/index.js +23 -7
  713. package/dist/prompts/index.js.map +1 -1
  714. package/dist/prompts/memory.d.ts +6 -3
  715. package/dist/prompts/memory.d.ts.map +1 -1
  716. package/dist/prompts/memory.js +9 -8
  717. package/dist/prompts/memory.js.map +1 -1
  718. package/dist/prompts/onboarding.d.ts +1 -1
  719. package/dist/prompts/onboarding.d.ts.map +1 -1
  720. package/dist/prompts/onboarding.js +2 -3
  721. package/dist/prompts/onboarding.js.map +1 -1
  722. package/dist/prompts/prd.d.ts +13 -6
  723. package/dist/prompts/prd.d.ts.map +1 -1
  724. package/dist/prompts/prd.js +42 -38
  725. package/dist/prompts/prd.js.map +1 -1
  726. package/dist/prompts/query-expansion.d.ts +8 -0
  727. package/dist/prompts/query-expansion.d.ts.map +1 -0
  728. package/dist/prompts/query-expansion.js +17 -0
  729. package/dist/prompts/query-expansion.js.map +1 -0
  730. package/dist/prompts/skills.d.ts +5 -10
  731. package/dist/prompts/skills.d.ts.map +1 -1
  732. package/dist/prompts/skills.js +9 -17
  733. package/dist/prompts/skills.js.map +1 -1
  734. package/dist/prompts/soul.d.ts +9 -0
  735. package/dist/prompts/soul.d.ts.map +1 -0
  736. package/dist/prompts/soul.js +47 -0
  737. package/dist/prompts/soul.js.map +1 -0
  738. package/dist/prompts/supersession.d.ts +9 -0
  739. package/dist/prompts/supersession.d.ts.map +1 -0
  740. package/dist/prompts/supersession.js +21 -0
  741. package/dist/prompts/supersession.js.map +1 -0
  742. package/dist/prompts/synthesis.d.ts +9 -0
  743. package/dist/prompts/synthesis.d.ts.map +1 -0
  744. package/dist/prompts/synthesis.js +22 -0
  745. package/dist/prompts/synthesis.js.map +1 -0
  746. package/hooks/core/__tests__/adapter.test.cjs +340 -0
  747. package/hooks/core/adapter.cjs +463 -0
  748. package/hooks/core/config.cjs +83 -0
  749. package/hooks/core/daemon-boot.cjs +140 -0
  750. package/hooks/core/log.cjs +41 -0
  751. package/hooks/core/worktree.cjs +119 -0
  752. package/hooks/succ-post-tool.cjs +198 -134
  753. package/hooks/succ-pre-compact.cjs +262 -0
  754. package/hooks/succ-pre-tool.cjs +526 -182
  755. package/hooks/succ-session-end.cjs +40 -64
  756. package/hooks/succ-session-start.cjs +504 -450
  757. package/hooks/succ-stop-reflection.cjs +36 -62
  758. package/hooks/succ-user-prompt.cjs +137 -180
  759. package/package.json +18 -7
@@ -16,52 +16,36 @@
16
16
  import http from 'http';
17
17
  import fs from 'fs';
18
18
  import path from 'path';
19
- import { createSessionManager, createIdleWatcher } from './sessions.js';
19
+ import { createIdleWatcher, createSessionManager } from './sessions.js';
20
20
  import { logError, logWarn } from '../lib/fault-logger.js';
21
21
  import { processRegistry } from '../lib/process-registry.js';
22
- import { processSessionEnd } from './session-processor.js';
23
- import { ValidationError, NotFoundError, NetworkError } from '../lib/errors.js';
24
- import { startWatcher, stopWatcher, getWatcherStatus, indexFileOnDemand } from './watcher.js';
25
- import { startAnalyzer, stopAnalyzer, getAnalyzerStatus, triggerAnalysis } from './analyzer.js';
26
- import { getProjectRoot, getSuccDir, getIdleReflectionConfig, getIdleWatcherConfig, getConfig, getObserverConfig, } from '../lib/config.js';
27
- import { hybridSearchDocs, hybridSearchCode, hybridSearchMemories, saveMemory, closeDb, getStats, getMemoryStats, incrementMemoryAccessBatch, getRecentMemories, getPinnedMemories, setMemoryInvariant, getMemoriesByTag,
28
- // Global memory
29
- saveGlobalMemory, closeGlobalDb,
30
- // Dispatcher lifecycle
31
- initStorageDispatcher, closeStorageDispatcher, getStorageDispatcher, } from '../lib/storage/index.js';
32
- import { getEmbedding, cleanupEmbeddings } from '../lib/embeddings.js';
33
- import { scoreMemory, passesQualityThreshold, cleanupQualityScoring } from '../lib/quality.js';
34
- import { scanSensitive } from '../lib/sensitive-filter.js';
35
- import { generateCompactBriefing } from '../lib/compact-briefing.js';
36
- import { callLLM } from '../lib/llm.js';
37
- import { extractSessionSummary } from '../lib/session-summary.js';
38
- import { recordTranscriptTokens, recordExtraction, resetTranscriptCounter, loadBudgets, flushBudgets, removeBudget, } from '../lib/token-budget.js';
39
- import { appendObservations, removeObservations, cleanupStaleObservations, } from '../lib/session-observations.js';
40
- import { REFLECTION_PROMPT } from '../prompts/index.js';
41
- import { matchRules } from '../lib/hook-rules.js';
42
- // ============================================================================
43
- // Constants
44
- // ============================================================================
22
+ import { NotFoundError, NetworkError, ValidationError } from '../lib/errors.js';
23
+ import { startWatcher, stopWatcher } from './watcher.js';
24
+ import { startAnalyzer, stopAnalyzer } from './analyzer.js';
25
+ import { getConfig, getIdleReflectionConfig, getIdleWatcherConfig, getProjectRoot, getSuccDir, } from '../lib/config.js';
26
+ import { getStablePort } from '../lib/daemon-port.js';
27
+ import { closeDb, closeGlobalDb, closeStorageDispatcher, initStorageDispatcher, } from '../lib/storage/index.js';
28
+ import { cleanupEmbeddings } from '../lib/embeddings.js';
29
+ import { cleanupReranker } from '../lib/reranker.js';
30
+ import { cleanupQualityScoring } from '../lib/quality.js';
31
+ import { loadBudgets } from '../lib/token-budget.js';
32
+ import { getErrorMessage, } from './routes/types.js';
33
+ import { sessionRoutes } from './routes/sessions.js';
34
+ import { searchRoutes, resetSearchRoutesState } from './routes/search.js';
35
+ import { memoryRoutes, resetMemoryRoutesState } from './routes/memory.js';
36
+ import { clearBriefingCache, initReflectionMaintenance, performReflection, preGenerateBriefing, reflectionRoutes, resetReflectionRoutesState, } from './routes/reflection.js';
37
+ import { statusRoutes } from './routes/status.js';
38
+ import { watcherRoutes } from './routes/watcher.js';
39
+ import { analyzerRoutes } from './routes/analyzer.js';
40
+ import { skillRoutes } from './routes/skills.js';
41
+ import { hookRoutes } from './routes/hooks.js';
42
+ import { addVersionedRoutes, getApiVersionInfo } from './routes/versioning.js';
45
43
  const DEFAULT_PORT_RANGE_START = 37842;
46
44
  const MAX_PORT_ATTEMPTS = 100;
47
- // ============================================================================
48
- // Daemon State
49
- // ============================================================================
45
+ const MAX_BODY_SIZE = 10 * 1024 * 1024; // 10MB
50
46
  let state = null;
51
47
  let sessionManager = null;
52
48
  let idleWatcher = null;
53
- const briefingCache = new Map();
54
- const briefingGenerationInProgress = new Set();
55
- // In-flight dedup: prevents race condition when identical /api/remember requests
56
- // arrive within a short window (e.g. hook fires twice for same tool_use)
57
- const rememberInFlight = new Map();
58
- const REMEMBER_DEDUP_TTL_MS = 5000;
59
- // Hook rules cache — stores all hook-rule memories, invalidated on remember with hook-rule tag
60
- let hookRulesCache = null;
61
- const HOOK_RULES_CACHE_TTL = 60_000; // 60s
62
- // ============================================================================
63
- // File Paths
64
- // ============================================================================
65
49
  function getDaemonPidFile() {
66
50
  const succDir = getSuccDir();
67
51
  const tmpDir = path.join(succDir, '.tmp');
@@ -70,13 +54,6 @@ function getDaemonPidFile() {
70
54
  }
71
55
  return path.join(tmpDir, 'daemon.pid');
72
56
  }
73
- // ============================================================================
74
- // Progress File Management
75
- // ============================================================================
76
- /**
77
- * Get path to session progress file
78
- * Progress files accumulate idle reflection briefings for session-end processing
79
- */
80
57
  function getProgressFilePath(sessionId) {
81
58
  const succDir = getSuccDir();
82
59
  const tmpDir = path.join(succDir, '.tmp');
@@ -85,10 +62,6 @@ function getProgressFilePath(sessionId) {
85
62
  }
86
63
  return path.join(tmpDir, `session-${sessionId}-progress.md`);
87
64
  }
88
- /**
89
- * Append a briefing to the session progress file
90
- * Creates file with header if it doesn't exist
91
- */
92
65
  export function appendToProgressFile(sessionId, briefing) {
93
66
  const progressPath = getProgressFilePath(sessionId);
94
67
  const timestamp = new Date().toISOString();
@@ -106,10 +79,6 @@ export function appendToProgressFile(sessionId, briefing) {
106
79
  content += '\n\n---\n\n';
107
80
  fs.appendFileSync(progressPath, content);
108
81
  }
109
- /**
110
- * Read tail of transcript file (for fallback when no progress file)
111
- * Returns the last maxBytes of the file, starting from a complete line
112
- */
113
82
  export function readTailTranscript(transcriptPath, maxBytes = 2 * 1024 * 1024) {
114
83
  if (!fs.existsSync(transcriptPath)) {
115
84
  return '';
@@ -118,107 +87,14 @@ export function readTailTranscript(transcriptPath, maxBytes = 2 * 1024 * 1024) {
118
87
  if (stats.size <= maxBytes) {
119
88
  return fs.readFileSync(transcriptPath, 'utf8');
120
89
  }
121
- // Read only tail
122
90
  const fd = fs.openSync(transcriptPath, 'r');
123
91
  const buffer = Buffer.alloc(maxBytes);
124
92
  fs.readSync(fd, buffer, 0, maxBytes, stats.size - maxBytes);
125
93
  fs.closeSync(fd);
126
- // Find first complete line (skip partial line at start)
127
94
  const content = buffer.toString('utf8');
128
95
  const firstNewline = content.indexOf('\n');
129
96
  return firstNewline > 0 ? content.slice(firstNewline + 1) : content;
130
97
  }
131
- // ============================================================================
132
- // Briefing Pre-Generation
133
- // ============================================================================
134
- const BRIEFING_CACHE_MAX_AGE_MS = 5 * 60 * 1000; // 5 minutes
135
- const BRIEFING_MIN_TRANSCRIPT_GROWTH = 5000; // Re-generate after 5KB growth
136
- // const BRIEFING_PREGENERATE_IDLE_MS = 120 * 1000; // Pre-generate after 2 min idle
137
- /**
138
- * Pre-generate briefing for a session in background
139
- * Called when session is idle or transcript grows significantly
140
- */
141
- async function preGenerateBriefing(sessionId, transcriptPath) {
142
- // Skip if already generating
143
- if (briefingGenerationInProgress.has(sessionId)) {
144
- return;
145
- }
146
- if (!fs.existsSync(transcriptPath)) {
147
- return;
148
- }
149
- const stats = fs.statSync(transcriptPath);
150
- const currentSize = stats.size;
151
- // Check if we need to regenerate
152
- const cached = briefingCache.get(sessionId);
153
- if (cached) {
154
- const age = Date.now() - cached.generatedAt;
155
- const growth = currentSize - cached.transcriptSize;
156
- // Skip if cache is fresh and transcript hasn't grown much
157
- if (age < BRIEFING_CACHE_MAX_AGE_MS && growth < BRIEFING_MIN_TRANSCRIPT_GROWTH) {
158
- return;
159
- }
160
- }
161
- briefingGenerationInProgress.add(sessionId);
162
- log(`[briefing] Pre-generating for session ${sessionId.slice(0, 8)}...`);
163
- try {
164
- const transcriptContent = fs.readFileSync(transcriptPath, 'utf-8');
165
- const result = await generateCompactBriefing(transcriptContent);
166
- if (result.success && result.briefing) {
167
- briefingCache.set(sessionId, {
168
- briefing: result.briefing,
169
- generatedAt: Date.now(),
170
- transcriptSize: currentSize,
171
- });
172
- log(`[briefing] Pre-generated for session ${sessionId.slice(0, 8)} (${result.briefing.length} chars)`);
173
- }
174
- else {
175
- log(`[briefing] Pre-generation failed: ${result.error}`);
176
- }
177
- }
178
- catch (error) {
179
- log(`[briefing] Pre-generation error: ${error}`);
180
- }
181
- finally {
182
- briefingGenerationInProgress.delete(sessionId);
183
- }
184
- }
185
- /**
186
- * Get cached briefing or generate on-demand
187
- */
188
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
189
- async function getCachedBriefing(sessionId, transcriptPath) {
190
- const cached = briefingCache.get(sessionId);
191
- if (cached) {
192
- // Check if cache is still valid
193
- const age = Date.now() - cached.generatedAt;
194
- if (age < BRIEFING_CACHE_MAX_AGE_MS) {
195
- return { briefing: cached.briefing, cached: true };
196
- }
197
- }
198
- // Cache miss or stale - generate fresh
199
- if (!fs.existsSync(transcriptPath)) {
200
- return { cached: false };
201
- }
202
- const transcriptContent = fs.readFileSync(transcriptPath, 'utf-8');
203
- const result = await generateCompactBriefing(transcriptContent);
204
- if (result.success && result.briefing) {
205
- const stats = fs.statSync(transcriptPath);
206
- briefingCache.set(sessionId, {
207
- briefing: result.briefing,
208
- generatedAt: Date.now(),
209
- transcriptSize: stats.size,
210
- });
211
- return { briefing: result.briefing, cached: false };
212
- }
213
- return { cached: false };
214
- }
215
- /**
216
- * Clear briefing cache for a session (called when session ends)
217
- */
218
- function clearBriefingCache(sessionId) {
219
- briefingCache.delete(sessionId);
220
- briefingGenerationInProgress.delete(sessionId);
221
- }
222
98
  function getDaemonPortFile() {
223
99
  const succDir = getSuccDir();
224
100
  const tmpDir = path.join(succDir, '.tmp');
@@ -231,13 +107,9 @@ function getDaemonLogFile() {
231
107
  const succDir = getSuccDir();
232
108
  return path.join(succDir, 'daemon.log');
233
109
  }
234
- // ============================================================================
235
- // Logging
236
- // ============================================================================
237
110
  function log(message) {
238
111
  const timestamp = new Date().toISOString();
239
112
  const line = `[${timestamp}] ${message}\n`;
240
- // Write to daemon.log
241
113
  try {
242
114
  fs.appendFileSync(getDaemonLogFile(), line);
243
115
  }
@@ -246,290 +118,43 @@ function log(message) {
246
118
  error: err instanceof Error ? err.message : String(err),
247
119
  });
248
120
  }
249
- // Also write to stderr for debugging
250
121
  process.stderr.write(line);
251
122
  }
252
- // ============================================================================
253
- // Write Reflection
254
- // ============================================================================
255
- /**
256
- * Write a human-like reflection to the brain vault
257
- * Uses Claude CLI or local LLM to generate introspective text
258
- */
259
- async function writeReflection(transcript, _idleConfig) {
260
- const projectRoot = getProjectRoot();
261
- const reflectionsDir = path.join(projectRoot, '.succ', 'brain', 'reflections');
262
- // Create reflections directory if needed
263
- if (!fs.existsSync(reflectionsDir)) {
264
- fs.mkdirSync(reflectionsDir, { recursive: true });
265
- }
266
- const now = new Date();
267
- const dateStr = now.toISOString().split('T')[0];
268
- const timeStr = now.toTimeString().split(' ')[0].substring(0, 5);
269
- const timestamp = `${dateStr} ${timeStr}`;
270
- const prompt = REFLECTION_PROMPT.replace('{transcript}', transcript.substring(0, 3000));
271
- let reflectionText = null;
272
- // Use sleep agent for background reflection if enabled
273
- try {
274
- reflectionText = await callLLM(prompt, {
275
- timeout: 60000,
276
- useSleepAgent: true, // Use sleep_agent config if available
277
- });
278
- }
279
- catch (err) {
280
- log(`[reflection] LLM call failed: ${err}`);
281
- reflectionText = null;
282
- }
283
- if (!reflectionText || reflectionText.trim().length < 50) {
284
- log(`[reflection] Reflection text too short or empty, skipping`);
285
- return;
286
- }
287
- // Write reflection file with YAML frontmatter
288
- const reflectionFile = path.join(reflectionsDir, `${timestamp}.md`);
289
- const content = `---
290
- date: ${dateStr}
291
- time: ${timeStr}
292
- trigger: idle
293
- tags:
294
- - reflection
295
- ---
296
-
297
- # Reflection ${dateStr} ${timeStr}
298
-
299
- ${reflectionText.trim()}
300
- `;
301
- fs.writeFileSync(reflectionFile, content);
302
- // Also save to memory (with dedup to prevent duplicate reflections)
303
- const embedding = await getEmbedding(reflectionText.trim());
304
- await saveMemory(reflectionText.trim(), embedding, ['reflection'], 'observation', {
305
- qualityScore: { score: 0.6, factors: { hasContext: 1 } },
306
- deduplicate: true,
307
- });
123
+ function isErrnoException(error) {
124
+ return typeof error === 'object' && error !== null && 'code' in error;
308
125
  }
309
- // LLM functions moved to shared module: src/lib/llm.ts
310
- // ============================================================================
311
- // Reflection Handler
312
- // ============================================================================
313
- async function handleReflection(sessionId, session) {
314
- // Skip reflection for service sessions (reflection subagents, analyzers, etc.)
315
- if (session.isService) {
316
- log(`[reflection] Skipping service session ${sessionId}`);
317
- return;
318
- }
319
- log(`[reflection] Starting reflection for session ${sessionId}`);
320
- const idleConfig = getIdleReflectionConfig();
321
- // Only run if we have a transcript
322
- if (!session.transcriptPath || !fs.existsSync(session.transcriptPath)) {
323
- log(`[reflection] No transcript found for session ${sessionId}`);
324
- return;
325
- }
326
- try {
327
- // ── Change detection: skip redundant work during long AFK ──
328
- let transcriptChanged = true;
329
- let memoriesChanged = true;
330
- try {
331
- const currentSize = fs.statSync(session.transcriptPath).size;
332
- if (session.lastTranscriptSize !== undefined && currentSize === session.lastTranscriptSize) {
333
- transcriptChanged = false;
334
- log(`[reflection] Transcript unchanged (${currentSize}b), skipping briefing`);
335
- }
336
- session.lastTranscriptSize = currentSize;
337
- }
338
- catch {
339
- /* transcript file gone — skip size check */
340
- }
341
- // Check memory count for consolidation skip
342
- const memStats = await getMemoryStats();
343
- const currentMemCount = memStats.total;
344
- if (session.lastMemoryCount !== undefined && currentMemCount === session.lastMemoryCount) {
345
- memoriesChanged = false;
346
- }
347
- session.lastMemoryCount = currentMemCount;
348
- // ── Mid-conversation observer: extract facts when enough new content ──
349
- const observerConfig = getObserverConfig();
350
- if (observerConfig.enabled && transcriptChanged) {
351
- try {
352
- const currentSize = session.lastTranscriptSize ?? 0;
353
- const lastObsSize = session.lastObservationSize ?? 0;
354
- const lastObsTime = session.lastObservation ?? session.registeredAt;
355
- const now = Date.now();
356
- // Read new content and track real token count via budget
357
- const newBytes = currentSize - lastObsSize;
358
- const timeThresholdMs = observerConfig.max_minutes * 60 * 1000;
359
- const enoughTime = now - lastObsTime >= timeThresholdMs;
360
- // Use byte-estimated token check first (cheap), then verify with real count
361
- const estimatedTokens = Math.ceil(newBytes / 3.5);
362
- const enoughNewContent = estimatedTokens >= observerConfig.min_tokens;
363
- if (enoughNewContent || enoughTime) {
364
- const newContent = readTailTranscript(session.transcriptPath, newBytes);
365
- // Track real tokens in budget
366
- const realTokens = recordTranscriptTokens(sessionId, newContent);
367
- log(`[observer] Triggering extraction (tokens: ~${realTokens}, time: ${Math.round((now - lastObsTime) / 60000)}min)`);
368
- if (newContent.length > 200) {
369
- const result = await extractSessionSummary(newContent, { verbose: false });
370
- recordExtraction(sessionId, result.transcriptTokens ?? 0, result.summaryTokens ?? 0, result.factsExtracted, result.factsSaved);
371
- resetTranscriptCounter(sessionId);
372
- // Persist extraction metadata to session observations (append-only)
373
- if (result.factsSaved > 0) {
374
- appendObservations(sessionId, [
375
- {
376
- content: `Extracted ${result.factsExtracted} facts, saved ${result.factsSaved}`,
377
- type: 'observation',
378
- tags: ['mid-session'],
379
- extractedAt: new Date().toISOString(),
380
- source: 'mid-session-observer',
381
- transcriptOffset: currentSize,
382
- memoryId: null,
383
- },
384
- ]);
385
- }
386
- log(`[observer] Extracted ${result.factsExtracted} facts, saved ${result.factsSaved} (skipped ${result.factsSkipped})`);
387
- }
388
- session.lastObservation = now;
389
- session.lastObservationSize = currentSize;
390
- flushBudgets();
391
- }
392
- }
393
- catch (err) {
394
- log(`[observer] Mid-session extraction failed: ${err}`);
395
- }
396
- }
397
- // ── Generate briefing (skip if transcript unchanged) ──
398
- let briefingResult = { success: false };
399
- if (transcriptChanged) {
400
- const transcriptContent = readTailTranscript(session.transcriptPath, 100 * 1024); // 100KB max
401
- briefingResult = await generateCompactBriefing(transcriptContent, {
402
- format: 'structured',
403
- include_memories: true,
404
- max_memories: 3,
405
- });
406
- if (briefingResult.success && briefingResult.briefing) {
407
- appendToProgressFile(sessionId, briefingResult.briefing);
408
- log(`[reflection] Appended briefing to progress file (${briefingResult.briefing.length} chars)`);
409
- }
410
- else {
411
- log(`[reflection] Failed to generate briefing for ${sessionId}`);
412
- }
413
- }
414
- // ── Parallel operations ──
415
- const globalConfig = getConfig();
416
- const parallelOps = [];
417
- // memory_consolidation - skip if no new memories (disabled by default, opt-in only)
418
- if (memoriesChanged && idleConfig.operations?.memory_consolidation === true) {
419
- parallelOps.push((async () => {
420
- const threshold = idleConfig.thresholds?.similarity_for_merge ?? 0.92;
421
- const limit = idleConfig.max_memories_to_process ?? 50;
422
- log(`[reflection] Running memory consolidation (threshold=${threshold}, limit=${limit})`);
423
- const { consolidate } = await import('../commands/consolidate.js');
424
- await consolidate({
425
- threshold: String(threshold),
426
- limit: String(limit),
427
- llm: true,
428
- verbose: false,
429
- });
430
- log(`[reflection] Memory consolidation complete`);
431
- })());
432
- }
433
- // retention_cleanup - independent (always runs if enabled)
434
- if (globalConfig.retention?.enabled && idleConfig.operations?.retention_cleanup !== false) {
435
- parallelOps.push((async () => {
436
- log(`[reflection] Running retention cleanup`);
437
- const { retention } = await import('../commands/retention.js');
438
- await retention({ apply: true, verbose: false });
439
- log(`[reflection] Retention cleanup complete`);
440
- })());
441
- }
442
- await Promise.all(parallelOps);
443
- // ── Graph cleanup: prune → enrich → orphans → communities → centrality ──
444
- if (idleConfig.operations?.graph_refinement !== false ||
445
- idleConfig.operations?.graph_enrichment !== false) {
446
- const shouldRun = memoriesChanged || session.lastLinkCount === undefined;
447
- if (shouldRun) {
448
- log(`[reflection] Running graph cleanup pipeline`);
449
- try {
450
- const { graphCleanup } = await import('../lib/graph/cleanup.js');
451
- const cleanupResult = await graphCleanup({
452
- skipEnrich: idleConfig.operations?.graph_enrichment === false,
453
- onProgress: (step, detail) => log(`[reflection] [${step}] ${detail}`),
454
- });
455
- log(`[reflection] Cleanup: pruned ${cleanupResult.pruned}, enriched ${cleanupResult.enriched}, orphans ${cleanupResult.orphansConnected}, communities ${cleanupResult.communitiesDetected}, centrality ${cleanupResult.centralityUpdated}`);
456
- // Proximity links from co-occurrence (not part of cleanup pipeline)
457
- try {
458
- const { createProximityLinks } = await import('../lib/graph/contextual-proximity.js');
459
- const r = await createProximityLinks({ minCooccurrence: 2 });
460
- log(`[reflection] Created ${r.created} proximity links`);
461
- }
462
- catch (err) {
463
- log(`[reflection] Proximity failed: ${err}`);
464
- }
465
- // Synthesize patterns from community clusters (uses cleanup's community result)
466
- if (cleanupResult.communityResult &&
467
- cleanupResult.communityResult.communities.length > 0) {
468
- try {
469
- const { synthesizeFromCommunities } = await import('../lib/reflection-synthesizer.js');
470
- const synthResult = await synthesizeFromCommunities(cleanupResult.communityResult, {
471
- log,
472
- });
473
- const hasSynthActivity = synthResult.patternsCreated > 0 ||
474
- synthResult.duplicatesSkipped > 0 ||
475
- synthResult.reinforced > 0;
476
- if (hasSynthActivity) {
477
- log(`[reflection] Synthesized ${synthResult.patternsCreated} patterns from ${synthResult.clustersProcessed} clusters` +
478
- (synthResult.reinforced > 0
479
- ? `, reinforced ${synthResult.reinforced} existing`
480
- : '') +
481
- (synthResult.duplicatesSkipped > 0
482
- ? `, skipped ${synthResult.duplicatesSkipped} duplicates`
483
- : '') +
484
- (synthResult.observationsMarked > 0
485
- ? `, marked ${synthResult.observationsMarked} as reflected`
486
- : ''));
487
- }
488
- }
489
- catch (err) {
490
- log(`[reflection] Synthesis failed: ${err}`);
491
- }
492
- }
493
- session.lastLinkCount = (session.lastLinkCount ?? 0) + cleanupResult.orphansConnected;
494
- }
495
- catch (err) {
496
- log(`[reflection] Graph cleanup failed: ${err}`);
497
- }
498
- }
499
- else {
500
- log(`[reflection] Skipping graph cleanup (no changes)`);
501
- }
502
- }
503
- // ── Write reflection (runs last, may use LLM) ──
504
- if (idleConfig.operations?.write_reflection !== false) {
505
- log(`[reflection] Writing reflection for ${sessionId}`);
506
- try {
507
- const progressPath = getProgressFilePath(sessionId);
508
- const briefingContent = fs.existsSync(progressPath)
509
- ? fs.readFileSync(progressPath, 'utf-8')
510
- : briefingResult.briefing || '';
511
- if (briefingContent.length >= 100) {
512
- await writeReflection(briefingContent, idleConfig);
513
- log(`[reflection] Reflection written`);
514
- }
515
- }
516
- catch (err) {
517
- log(`[reflection] Write reflection error: ${err}`);
518
- }
519
- }
520
- log(`[reflection] Completed reflection for session ${sessionId}`);
521
- }
522
- catch (err) {
523
- log(`[reflection] Error for session ${sessionId}: ${err}`);
524
- }
126
+ function createRouteContext() {
127
+ return {
128
+ state,
129
+ sessionManager,
130
+ log,
131
+ checkShutdown,
132
+ clearBriefingCache,
133
+ appendToProgressFile,
134
+ readTailTranscript,
135
+ getProgressFilePath,
136
+ };
137
+ }
138
+ function buildRoutes(ctx) {
139
+ const baseRoutes = {
140
+ ...statusRoutes(ctx),
141
+ ...sessionRoutes(ctx),
142
+ ...searchRoutes(ctx),
143
+ ...memoryRoutes(ctx),
144
+ ...reflectionRoutes(ctx),
145
+ ...watcherRoutes(ctx),
146
+ ...analyzerRoutes(ctx),
147
+ ...skillRoutes(ctx),
148
+ ...hookRoutes(ctx),
149
+ // API version info endpoint
150
+ 'GET /api/version': async () => getApiVersionInfo(),
151
+ };
152
+ // Add /v1/api/* aliases for all /api/* routes
153
+ return addVersionedRoutes(baseRoutes);
525
154
  }
526
- // ============================================================================
527
- // HTTP Request Handler
528
- // ============================================================================
529
155
  async function handleRequest(req, res) {
530
156
  const reqUrl = new URL(req.url || '/', `http://localhost`);
531
157
  const method = req.method || 'GET';
532
- // CORS headers (for potential web clients)
533
158
  res.setHeader('Access-Control-Allow-Origin', '*');
534
159
  res.setHeader('Access-Control-Allow-Methods', 'GET, POST, DELETE, OPTIONS');
535
160
  res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
@@ -538,500 +163,120 @@ async function handleRequest(req, res) {
538
163
  res.end();
539
164
  return;
540
165
  }
541
- // Parse JSON body for POST requests
542
166
  let body = null;
543
167
  if (method === 'POST') {
544
- body = await parseBody(req);
168
+ try {
169
+ body = await parseBody(req);
170
+ }
171
+ catch (error) {
172
+ const message = getErrorMessage(error);
173
+ if (message.includes('too large')) {
174
+ res.writeHead(413, { 'Content-Type': 'application/json' });
175
+ res.end(JSON.stringify({ error: 'Request body too large' }));
176
+ return;
177
+ }
178
+ res.writeHead(400, { 'Content-Type': 'application/json' });
179
+ res.end(JSON.stringify({ error: 'Invalid request body' }));
180
+ return;
181
+ }
545
182
  }
546
183
  try {
547
- // Route request
548
184
  const result = await routeRequest(method, reqUrl.pathname, reqUrl.searchParams, body);
549
185
  res.writeHead(200, { 'Content-Type': 'application/json' });
550
186
  res.end(JSON.stringify(result));
551
187
  }
552
188
  catch (err) {
553
- log(`[http] Error: ${err.message}`);
189
+ const message = getErrorMessage(err);
190
+ log(`[http] Error: ${message}`);
191
+ if (err instanceof ValidationError) {
192
+ res.writeHead(400, { 'Content-Type': 'application/json' });
193
+ res.end(JSON.stringify({ error: message }));
194
+ return;
195
+ }
196
+ if (err instanceof NotFoundError) {
197
+ res.writeHead(404, { 'Content-Type': 'application/json' });
198
+ res.end(JSON.stringify({ error: message }));
199
+ return;
200
+ }
554
201
  res.writeHead(500, { 'Content-Type': 'application/json' });
555
- res.end(JSON.stringify({ error: err.message }));
202
+ res.end(JSON.stringify({ error: 'Internal server error' }));
556
203
  }
557
204
  }
558
205
  export async function parseBody(req) {
559
206
  return new Promise((resolve, reject) => {
560
207
  let data = '';
561
- req.on('data', (chunk) => (data += chunk));
208
+ let size = 0;
209
+ let settled = false;
210
+ const settle = (fn) => {
211
+ if (settled)
212
+ return;
213
+ settled = true;
214
+ fn();
215
+ };
216
+ req.on('data', (chunk) => {
217
+ if (settled)
218
+ return;
219
+ const chunkSize = Buffer.isBuffer(chunk) ? chunk.length : Buffer.byteLength(String(chunk));
220
+ size += chunkSize;
221
+ if (size > MAX_BODY_SIZE) {
222
+ req.destroy();
223
+ settle(() => reject(new Error('Request body too large')));
224
+ return;
225
+ }
226
+ data += chunk;
227
+ });
562
228
  req.on('end', () => {
229
+ if (settled)
230
+ return;
563
231
  try {
564
- resolve(data ? JSON.parse(data) : {});
232
+ const parsed = data ? JSON.parse(data) : {};
233
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
234
+ settle(() => reject(new Error('Invalid request body')));
235
+ return;
236
+ }
237
+ settle(() => resolve(parsed));
565
238
  }
566
- catch (err) {
567
- logWarn('daemon', 'Invalid JSON in HTTP request body', {
568
- error: err instanceof Error ? err.message : String(err),
569
- });
570
- resolve({});
239
+ catch {
240
+ settle(() => reject(new Error('Invalid request body')));
571
241
  }
572
242
  });
573
- req.on('error', reject);
243
+ req.on('error', (error) => {
244
+ settle(() => reject(error));
245
+ });
574
246
  });
575
247
  }
576
248
  /** @internal Exported for testing */
577
249
  export async function routeRequest(method, pathname, searchParams, body) {
578
- // Health check
579
- if (pathname === '/health') {
580
- return {
581
- status: 'ok',
582
- pid: process.pid,
583
- uptime: Date.now() - (state?.startedAt || Date.now()),
584
- activeSessions: sessionManager?.count() || 0,
585
- cwd: state?.cwd || process.cwd(),
586
- };
250
+ const routes = buildRoutes(createRouteContext());
251
+ const key = `${method} ${pathname}`;
252
+ const handler = routes[key];
253
+ if (!handler) {
254
+ throw new NotFoundError(`Unknown endpoint: ${method} ${pathname}`);
587
255
  }
588
- // Session endpoints
589
- if (pathname === '/api/session/register' && method === 'POST') {
590
- const { session_id, transcript_path, is_service = false } = body;
591
- if (!session_id) {
592
- throw new ValidationError('session_id required');
593
- }
594
- const session = sessionManager.register(session_id, transcript_path || '', is_service);
595
- log(`[session] Registered: ${session_id}${is_service ? ' (service)' : ''}`);
596
- return { success: true, session };
597
- }
598
- if (pathname === '/api/session/unregister' && method === 'POST') {
599
- const { session_id, transcript_path, run_reflection } = body;
600
- if (!session_id) {
601
- throw new ValidationError('session_id required');
602
- }
603
- const session = sessionManager.get(session_id);
604
- const transcriptFile = transcript_path || session?.transcriptPath || '';
605
- // Flush session counters to learning_deltas before unregister
606
- try {
607
- const d = await getStorageDispatcher();
608
- await d.flushSessionCounters('daemon-session');
609
- }
610
- catch (err) {
611
- log(`[session] Failed to flush session counters: ${err}`);
612
- }
613
- // Unregister the session immediately (don't block on processing)
614
- const removed = sessionManager.unregister(session_id);
615
- clearBriefingCache(session_id); // Clean up any cached briefing
616
- removeBudget(session_id); // Clean up token budget
617
- removeObservations(session_id); // Clean up observation JSONL
618
- flushBudgets();
619
- log(`[session] Unregistered: ${session_id} (removed=${removed})`);
620
- // Process session asynchronously (summarize transcript, extract learnings, save to memory)
621
- if (run_reflection && transcriptFile) {
622
- sessionManager.incrementPendingWork();
623
- log(`[session] Queuing async processing for ${session_id}`);
624
- // Fire-and-forget async processing
625
- (async () => {
626
- try {
627
- const result = await processSessionEnd(transcriptFile, session_id, log);
628
- log(`[session] Processing complete for ${session_id}: summary=${result.summary.length}chars, learnings=${result.learnings.length}, saved=${result.saved}`);
629
- }
630
- catch (err) {
631
- log(`[session] Processing failed for ${session_id}: ${err}`);
632
- }
633
- finally {
634
- sessionManager.decrementPendingWork();
635
- // Check shutdown after work completes
636
- checkShutdown();
637
- }
638
- })();
639
- }
640
- else {
641
- // No processing needed, check shutdown immediately
642
- checkShutdown();
643
- }
644
- return { success: removed, remaining_sessions: sessionManager.count() };
645
- }
646
- if (pathname === '/api/session/activity' && method === 'POST') {
647
- const { session_id, type, transcript_path, is_service = false } = body;
648
- if (!session_id || !type) {
649
- throw new ValidationError('session_id and type required');
650
- }
651
- let session = sessionManager.activity(session_id, type);
652
- if (!session) {
653
- // Auto-register if session not found (with transcript_path if provided)
654
- sessionManager.register(session_id, transcript_path || '', is_service);
655
- session = sessionManager.activity(session_id, type);
656
- log(`[session] Auto-registered and activity: ${session_id} (${type})${is_service ? ' (service)' : ''}`);
657
- }
658
- else if (transcript_path && !session.transcriptPath) {
659
- // Update transcript path if not set
660
- session.transcriptPath = transcript_path;
661
- log(`[session] Activity: ${session_id} (${type}) + updated transcript`);
662
- }
663
- else {
664
- log(`[session] Activity: ${session_id} (${type})`);
665
- }
666
- return { success: true };
667
- }
668
- if (pathname === '/api/sessions' && method === 'GET') {
669
- const includeService = searchParams.get('includeService') === 'true';
670
- const sessions = {};
671
- for (const [id, session] of sessionManager.getAll(includeService)) {
672
- sessions[id] = session;
673
- }
674
- return { sessions, count: sessionManager.count(includeService) };
675
- }
676
- // Search endpoints
677
- if (pathname === '/api/search' && method === 'POST') {
678
- const { query, limit = 5, threshold = 0.3 } = body;
679
- if (!query) {
680
- throw new ValidationError('query required');
681
- }
682
- const queryEmbedding = await getEmbedding(query);
683
- const results = await hybridSearchDocs(query, queryEmbedding, limit, threshold);
684
- // Track access for returned memories
685
- const accesses = results
686
- .filter((r) => r.memory_id)
687
- .map((r) => ({ memoryId: r.memory_id, weight: 0.5 }));
688
- if (accesses.length > 0) {
689
- await incrementMemoryAccessBatch(accesses);
690
- }
691
- return { results };
692
- }
693
- if (pathname === '/api/search-code' && method === 'POST') {
694
- const { query, limit = 5, threshold = 0.3 } = body;
695
- if (!query) {
696
- throw new ValidationError('query required');
697
- }
698
- const queryEmbedding = await getEmbedding(query);
699
- const results = await hybridSearchCode(query, queryEmbedding, limit, threshold);
700
- return { results };
701
- }
702
- if (pathname === '/api/recall' && method === 'POST') {
703
- const { query, limit = 5 } = body;
704
- // Empty query returns recent memories
705
- if (!query) {
706
- const memories = await getRecentMemories(limit);
707
- return { results: memories };
708
- }
709
- // Generate embedding for semantic search
710
- const queryEmbedding = await getEmbedding(query);
711
- const results = await hybridSearchMemories(query, queryEmbedding, limit, 0.3);
712
- // Track access for returned memories
713
- const accesses = results
714
- .filter((r) => r.id)
715
- .map((r) => ({ memoryId: r.id, weight: 1.0 }));
716
- if (accesses.length > 0) {
717
- await incrementMemoryAccessBatch(accesses);
718
- }
719
- return { results };
720
- }
721
- if (pathname === '/api/pinned' && method === 'GET') {
722
- const pinned = await getPinnedMemories();
723
- return { results: pinned };
724
- }
725
- if (pathname === '/api/pinned/cleanup' && method === 'POST') {
726
- // Remove false invariant flags from observation-type memories
727
- const pinned = await getPinnedMemories();
728
- let cleaned = 0;
729
- for (const mem of pinned) {
730
- if (mem.type === 'observation' && mem.is_invariant) {
731
- await setMemoryInvariant(mem.id, false);
732
- cleaned++;
733
- }
734
- }
735
- return { cleaned, total: pinned.length };
736
- }
737
- if (pathname === '/api/recall-by-tag' && method === 'POST') {
738
- const { tag, limit = 5 } = body;
739
- if (!tag) {
740
- throw new ValidationError('tag required');
741
- }
742
- const results = await getMemoriesByTag(tag, limit);
743
- return { results };
744
- }
745
- if (pathname === '/api/hook-rules' && method === 'POST') {
746
- const { tool_name, tool_input: rawInput } = body;
747
- if (!tool_name) {
748
- throw new ValidationError('tool_name required');
749
- }
750
- const tool_input = rawInput && typeof rawInput === 'object' && !Array.isArray(rawInput)
751
- ? rawInput
752
- : {};
753
- // Check cache
754
- const now = Date.now();
755
- if (!hookRulesCache || now - hookRulesCache.timestamp > HOOK_RULES_CACHE_TTL) {
756
- const memories = await getMemoriesByTag('hook-rule', 50);
757
- hookRulesCache = { memories, timestamp: now };
758
- }
759
- const rules = matchRules(hookRulesCache.memories, tool_name, tool_input);
760
- return { rules };
761
- }
762
- if (pathname === '/api/remember' && method === 'POST') {
763
- const { content, tags = [], type = 'observation', source, global = false, valid_from, valid_until, } = body;
764
- if (!content) {
765
- throw new ValidationError('content required');
766
- }
767
- // In-flight dedup: if an identical request is already being processed, wait for it
768
- // Prevents race condition when hooks fire twice for the same tool_use
769
- const contentHash = content.slice(0, 200) + '|' + (tags || []).join(',');
770
- const existing = rememberInFlight.get(contentHash);
771
- if (existing) {
772
- const result = await existing;
773
- return { success: false, id: result.id, isDuplicate: true, reason: 'in-flight dedup' };
774
- }
775
- const processRemember = async () => {
776
- // Check for sensitive content
777
- const config = getConfig();
778
- let finalContent = content;
779
- if (config.sensitive_filter_enabled !== false) {
780
- const scanResult = scanSensitive(content);
781
- if (scanResult.hasSensitive) {
782
- if (config.sensitive_auto_redact) {
783
- finalContent = scanResult.redactedText;
784
- }
785
- else {
786
- throw new ValidationError('Content contains sensitive information');
787
- }
788
- }
789
- }
790
- // Get embedding
791
- const embedding = await getEmbedding(finalContent);
792
- // Score quality
793
- const qualityResult = await scoreMemory(finalContent);
794
- if (!passesQualityThreshold(qualityResult)) {
795
- return { success: false, reason: 'Below quality threshold', score: qualityResult.score };
796
- }
797
- // Save to appropriate DB
798
- let result;
799
- if (global) {
800
- result = await saveGlobalMemory(finalContent, embedding, tags, type);
801
- }
802
- else {
803
- result = await saveMemory(finalContent, embedding, tags, source ?? type, {
804
- qualityScore: { score: qualityResult.score, factors: qualityResult.factors },
805
- validFrom: valid_from,
806
- validUntil: valid_until,
807
- });
808
- }
809
- // Invalidate hook-rules cache if this memory is a hook rule
810
- if (Array.isArray(tags) && tags.includes('hook-rule')) {
811
- hookRulesCache = null;
812
- }
813
- return { success: !result.isDuplicate, id: result.id, isDuplicate: result.isDuplicate };
814
- };
815
- const promise = processRemember();
816
- rememberInFlight.set(contentHash, promise);
817
- setTimeout(() => rememberInFlight.delete(contentHash), REMEMBER_DEDUP_TTL_MS);
818
- try {
819
- return await promise;
820
- }
821
- finally {
822
- rememberInFlight.delete(contentHash);
823
- }
824
- }
825
- // Reflection endpoint
826
- if (pathname === '/api/reflect' && method === 'POST') {
827
- const { session_id } = body;
828
- const watcherConfig = getIdleWatcherConfig();
829
- if (session_id) {
830
- const session = sessionManager.get(session_id);
831
- if (!session) {
832
- throw new NotFoundError('Session not found');
833
- }
834
- await handleReflection(session_id, session);
835
- sessionManager.markReflection(session_id);
836
- return { success: true, session_id };
837
- }
838
- else {
839
- // Run for all idle sessions
840
- const idleSessions = sessionManager.getIdleSessions(watcherConfig.idle_minutes);
841
- for (const { sessionId, session } of idleSessions) {
842
- await handleReflection(sessionId, session);
843
- sessionManager.markReflection(sessionId);
844
- }
845
- return { success: true, sessions_processed: idleSessions.length };
846
- }
847
- }
848
- // Compact briefing endpoint (for /compact hook)
849
- // Supports pre-generated cache for instant responses
850
- if (pathname === '/api/briefing' && method === 'POST') {
851
- const { transcript, transcript_path, session_id, format, include_learnings, include_memories, max_memories, use_cache, } = body;
852
- // Try cached briefing first if session_id provided and use_cache not explicitly false
853
- if (session_id && use_cache !== false) {
854
- const cached = briefingCache.get(session_id);
855
- if (cached) {
856
- const age = Date.now() - cached.generatedAt;
857
- if (age < BRIEFING_CACHE_MAX_AGE_MS) {
858
- log(`[briefing] Serving cached briefing for ${session_id.slice(0, 8)} (age: ${Math.round(age / 1000)}s)`);
859
- return { success: true, briefing: cached.briefing, cached: true };
860
- }
861
- }
862
- }
863
- // Either transcript content or path to transcript file
864
- let transcriptContent;
865
- if (transcript) {
866
- transcriptContent = transcript;
867
- }
868
- else if (transcript_path && fs.existsSync(transcript_path)) {
869
- transcriptContent = fs.readFileSync(transcript_path, 'utf-8');
870
- }
871
- else {
872
- throw new ValidationError('transcript or transcript_path required');
873
- }
874
- const result = await generateCompactBriefing(transcriptContent, {
875
- format,
876
- include_learnings,
877
- include_memories,
878
- max_memories,
879
- });
880
- // Cache the result if session_id provided
881
- if (session_id && result.success && result.briefing && transcript_path) {
882
- const stats = fs.existsSync(transcript_path) ? fs.statSync(transcript_path) : null;
883
- briefingCache.set(session_id, {
884
- briefing: result.briefing,
885
- generatedAt: Date.now(),
886
- transcriptSize: stats?.size || 0,
887
- });
888
- }
889
- return { ...result, cached: false };
890
- }
891
- // Status endpoints
892
- if (pathname === '/api/status' && method === 'GET') {
893
- const stats = await getStats();
894
- const memStats = await getMemoryStats();
895
- const watchStatus = getWatcherStatus();
896
- const analyzeStatus = getAnalyzerStatus();
897
- return {
898
- daemon: {
899
- pid: process.pid,
900
- uptime: Date.now() - (state?.startedAt || Date.now()),
901
- sessions: sessionManager.count(),
902
- },
903
- index: stats,
904
- memories: memStats,
905
- services: {
906
- watch: watchStatus,
907
- analyze: analyzeStatus,
908
- },
909
- };
910
- }
911
- // Watch service endpoints
912
- if (pathname === '/api/watch/start' && method === 'POST') {
913
- const { patterns, includeCode } = body;
914
- const watchState = await startWatcher({ patterns, includeCode }, log);
915
- return {
916
- success: true,
917
- active: watchState.active,
918
- patterns: watchState.patterns,
919
- includeCode: watchState.includeCode,
920
- };
921
- }
922
- if (pathname === '/api/watch/stop' && method === 'POST') {
923
- await stopWatcher(log);
924
- return { success: true };
925
- }
926
- if (pathname === '/api/watch/status' && method === 'GET') {
927
- return getWatcherStatus();
928
- }
929
- if (pathname === '/api/watch/index' && method === 'POST') {
930
- const { file } = body;
931
- if (!file) {
932
- throw new ValidationError('file required');
933
- }
934
- await indexFileOnDemand(file, log);
935
- return { success: true, file };
936
- }
937
- // Analyze service endpoints
938
- if (pathname === '/api/analyze/start' && method === 'POST') {
939
- const { intervalMinutes, mode } = body;
940
- const analyzeState = startAnalyzer({ intervalMinutes, mode }, log);
941
- return {
942
- success: true,
943
- active: analyzeState.active,
944
- runsCompleted: analyzeState.runsCompleted,
945
- };
946
- }
947
- if (pathname === '/api/analyze/stop' && method === 'POST') {
948
- stopAnalyzer(log);
949
- return { success: true };
950
- }
951
- if (pathname === '/api/analyze/status' && method === 'GET') {
952
- return getAnalyzerStatus();
953
- }
954
- if (pathname === '/api/analyze' && method === 'POST') {
955
- const { mode = 'claude' } = body;
956
- await triggerAnalysis(mode, log);
957
- return { success: true };
958
- }
959
- // Skills endpoints
960
- if (pathname === '/api/skills/suggest' && method === 'POST') {
961
- const { prompt, limit = 2 } = body;
962
- if (!prompt) {
963
- throw new ValidationError('prompt required');
964
- }
965
- const { suggestSkills, getSkillsConfig } = await import('../lib/skills.js');
966
- const config = getSkillsConfig();
967
- if (!config.enabled || !config.auto_suggest?.enabled) {
968
- return { success: true, skills: [], disabled: true };
969
- }
970
- const suggestions = await suggestSkills(prompt, config);
971
- return {
972
- success: true,
973
- skills: suggestions.slice(0, limit),
974
- };
975
- }
976
- if (pathname === '/api/skills/index' && method === 'POST') {
977
- const { indexLocalSkills } = await import('../lib/skills.js');
978
- const cwd = state?.cwd || process.cwd();
979
- const count = indexLocalSkills(cwd);
980
- return { success: true, indexed: count };
981
- }
982
- if (pathname === '/api/skills/track' && method === 'POST') {
983
- const { skill_name } = body;
984
- if (!skill_name) {
985
- throw new ValidationError('skill_name required');
986
- }
987
- const { trackSkillUsage } = await import('../lib/skills.js');
988
- trackSkillUsage(skill_name);
989
- return { success: true };
990
- }
991
- // Skyll status endpoint
992
- if (pathname === '/api/skills/skyll' && method === 'GET') {
993
- const { getSkyllStatus } = await import('../lib/skyll-client.js');
994
- return getSkyllStatus();
995
- }
996
- // Services endpoint (list all services status)
997
- if (pathname === '/api/services' && method === 'GET') {
998
- return {
999
- watch: getWatcherStatus(),
1000
- analyze: getAnalyzerStatus(),
1001
- idle: {
1002
- enabled: true,
1003
- sessions: sessionManager.count(),
1004
- },
1005
- };
1006
- }
1007
- throw new NotFoundError(`Unknown endpoint: ${method} ${pathname}`);
256
+ return handler(body, searchParams);
1008
257
  }
1009
- // ============================================================================
1010
- // Daemon Lifecycle
1011
- // ============================================================================
1012
258
  export async function startDaemon() {
1013
259
  if (state?.server) {
1014
260
  return { port: state.port, pid: process.pid };
1015
261
  }
1016
- // Check if another daemon is already running (prevent duplicate processes)
1017
262
  const existingPidFile = getDaemonPidFile();
1018
263
  if (fs.existsSync(existingPidFile)) {
1019
264
  try {
1020
265
  const existingPid = parseInt(fs.readFileSync(existingPidFile, 'utf8').trim(), 10);
1021
266
  if (existingPid && existingPid !== process.pid) {
1022
- // Check if process is actually running
1023
267
  try {
1024
- process.kill(existingPid, 0); // Signal 0 = check if process exists
1025
- // Process exists, read port and return
268
+ process.kill(existingPid, 0);
1026
269
  const portFile = getDaemonPortFile();
1027
270
  if (fs.existsSync(portFile)) {
1028
271
  const port = parseInt(fs.readFileSync(portFile, 'utf8').trim(), 10);
1029
272
  log(`[daemon] Another daemon already running (pid=${existingPid}, port=${port})`);
1030
- process.exit(0); // Exit silently - another daemon is handling things
273
+ process.exit(0);
1031
274
  }
1032
275
  }
1033
- catch {
1034
- // Process doesn't exist, clean up stale files
276
+ catch (error) {
277
+ logWarn('service', 'Failed to signal existing daemon process for liveness check', {
278
+ error: error instanceof Error ? error.message : String(error),
279
+ });
1035
280
  log(`[daemon] Cleaning up stale PID file (pid=${existingPid} not running)`);
1036
281
  fs.unlinkSync(existingPidFile);
1037
282
  const portFile = getDaemonPortFile();
@@ -1050,15 +295,10 @@ export async function startDaemon() {
1050
295
  const cwd = getProjectRoot();
1051
296
  const watcherConfig = getIdleWatcherConfig();
1052
297
  getIdleReflectionConfig();
1053
- // Initialize storage dispatcher (routes to SQLite or PG based on config)
1054
298
  await initStorageDispatcher();
1055
- // Initialize session manager
1056
299
  sessionManager = createSessionManager();
1057
- // Load token budgets from previous daemon run
1058
300
  loadBudgets();
1059
- // Clean up stale observation files (>48h)
1060
- cleanupStaleObservations();
1061
- // Create HTTP server
301
+ initReflectionMaintenance();
1062
302
  const server = http.createServer((req, res) => {
1063
303
  handleRequest(req, res).catch((err) => {
1064
304
  log(`[http] Unhandled error: ${err.message}`);
@@ -1066,14 +306,25 @@ export async function startDaemon() {
1066
306
  res.end();
1067
307
  });
1068
308
  });
1069
- // Find available port
1070
- const portStart = DEFAULT_PORT_RANGE_START;
1071
- let port = portStart;
1072
- for (let i = 0; i < MAX_PORT_ATTEMPTS; i++) {
309
+ // Try stable port first (config or hash-based), then fall back to scan
310
+ const config = getConfig();
311
+ const stablePort = config.daemon?.port ?? getStablePort(cwd);
312
+ const fallbackStart = config.daemon?.port_range_start ?? DEFAULT_PORT_RANGE_START;
313
+ let port = stablePort;
314
+ let portAttempts = 0;
315
+ const maxAttempts = 1 + MAX_PORT_ATTEMPTS; // 1 stable + 100 fallback
316
+ for (let i = 0; i < maxAttempts; i++) {
1073
317
  await new Promise((resolve, reject) => {
1074
318
  server.once('error', (err) => {
1075
319
  if (err.code === 'EADDRINUSE') {
1076
- port++;
320
+ portAttempts++;
321
+ if (portAttempts === 1) {
322
+ // Stable port busy — switch to fallback range
323
+ port = fallbackStart;
324
+ }
325
+ else {
326
+ port++;
327
+ }
1077
328
  resolve();
1078
329
  }
1079
330
  else {
@@ -1089,33 +340,28 @@ export async function startDaemon() {
1089
340
  }
1090
341
  }
1091
342
  if (!server.listening) {
1092
- throw new NetworkError(`Could not find available port in range ${portStart}-${portStart + MAX_PORT_ATTEMPTS}`);
343
+ throw new NetworkError(`Could not find available port (tried ${stablePort}, then ${fallbackStart}-${fallbackStart + MAX_PORT_ATTEMPTS})`);
1093
344
  }
1094
- // Save state
1095
345
  state = {
1096
346
  cwd,
1097
347
  startedAt: Date.now(),
1098
348
  port,
1099
349
  server,
1100
350
  };
1101
- // Write PID and port files
1102
351
  fs.writeFileSync(getDaemonPidFile(), String(process.pid));
1103
352
  fs.writeFileSync(getDaemonPortFile(), String(port));
1104
- // Start idle watcher with briefing pre-generation
1105
353
  idleWatcher = createIdleWatcher({
1106
354
  sessionManager,
1107
- onIdle: handleReflection,
1108
- onPreGenerateBriefing: preGenerateBriefing,
355
+ onIdle: (sessionId, session) => performReflection(createRouteContext(), sessionId, session),
356
+ onPreGenerateBriefing: (sessionId, transcriptPath) => preGenerateBriefing(createRouteContext(), sessionId, transcriptPath),
1109
357
  checkIntervalSeconds: watcherConfig.check_interval,
1110
358
  idleMinutes: watcherConfig.idle_minutes,
1111
359
  reflectionCooldownMinutes: watcherConfig.reflection_cooldown_minutes,
1112
- preGenerateIdleSeconds: 120, // Pre-generate briefing after 2 min idle
360
+ preGenerateIdleSeconds: 120,
1113
361
  log,
1114
362
  });
1115
363
  idleWatcher.start();
1116
364
  log(`[daemon] Started on port ${port} (pid=${process.pid})`);
1117
- // Auto-start watch service if configured
1118
- const config = getConfig();
1119
365
  if (config.daemon?.watch?.auto_start) {
1120
366
  const watchConfig = config.daemon.watch;
1121
367
  await startWatcher({
@@ -1125,7 +371,6 @@ export async function startDaemon() {
1125
371
  }, log);
1126
372
  log(`[daemon] Auto-started watch service`);
1127
373
  }
1128
- // Auto-start analyze service if configured
1129
374
  if (config.daemon?.analyze?.auto_start) {
1130
375
  const analyzeConfig = config.daemon.analyze;
1131
376
  startAnalyzer({
@@ -1134,7 +379,6 @@ export async function startDaemon() {
1134
379
  }, log);
1135
380
  log(`[daemon] Auto-started analyze service`);
1136
381
  }
1137
- // Setup graceful shutdown
1138
382
  setupShutdownHandlers();
1139
383
  return { port, pid: process.pid };
1140
384
  }
@@ -1142,14 +386,18 @@ function setupShutdownHandlers() {
1142
386
  const shutdown = () => shutdownDaemon();
1143
387
  process.on('SIGTERM', shutdown);
1144
388
  process.on('SIGINT', shutdown);
1145
- // SIGHUP only exists on Unix
1146
389
  if (process.platform !== 'win32') {
1147
390
  process.on('SIGHUP', shutdown);
1148
391
  }
392
+ // Prevent silent crashes — log and keep running
393
+ process.on('uncaughtException', (err) => {
394
+ log(`[daemon] UNCAUGHT EXCEPTION: ${err.message}\n${err.stack}`);
395
+ });
396
+ process.on('unhandledRejection', (reason) => {
397
+ const msg = reason instanceof Error ? `${reason.message}\n${reason.stack}` : String(reason);
398
+ log(`[daemon] UNHANDLED REJECTION: ${msg}`);
399
+ });
1149
400
  }
1150
- /**
1151
- * Check if daemon should shutdown (no sessions and no pending work)
1152
- */
1153
401
  function checkShutdown() {
1154
402
  if (sessionManager?.canShutdown()) {
1155
403
  log(`[daemon] No more sessions and no pending work, scheduling shutdown`);
@@ -1157,54 +405,52 @@ function checkShutdown() {
1157
405
  if (sessionManager?.canShutdown()) {
1158
406
  shutdownDaemon();
1159
407
  }
1160
- }, 5000); // Give 5 seconds for new sessions to connect
408
+ }, 5000);
1161
409
  }
1162
410
  }
1163
- export function shutdownDaemon() {
411
+ export async function shutdownDaemon() {
1164
412
  log('[daemon] Shutting down...');
1165
- // Stop idle watcher
1166
413
  if (idleWatcher) {
1167
414
  idleWatcher.stop();
1168
415
  idleWatcher = null;
1169
416
  }
1170
- // Stop watch service (async, but we're shutting down so fire-and-forget)
1171
- stopWatcher(log).catch((err) => log(`[shutdown] Watcher stop failed: ${err}`));
1172
- // Stop analyze service
417
+ const watcherPromise = stopWatcher(log).catch((err) => log(`[shutdown] Watcher stop failed: ${err}`));
1173
418
  stopAnalyzer(log);
1174
- // Close HTTP server
1175
- if (state?.server) {
1176
- state.server.close();
1177
- state.server = null;
1178
- }
1179
- // Kill all spawned child processes (claude CLI, etc.)
419
+ const serverPromise = state?.server
420
+ ? new Promise((resolve) => {
421
+ state.server.close(() => resolve());
422
+ state.server = null;
423
+ })
424
+ : Promise.resolve();
1180
425
  processRegistry.killAll();
1181
- // Cleanup DB connections
1182
- closeStorageDispatcher().catch((err) => log(`[shutdown] Storage close failed: ${err}`));
426
+ // Wait for watcher and HTTP server to finish before closing storage,
427
+ // so in-flight requests don't hit a closed DB.
428
+ await Promise.allSettled([watcherPromise, serverPromise]);
429
+ await closeStorageDispatcher().catch((err) => log(`[shutdown] Storage close failed: ${err}`));
1183
430
  cleanupEmbeddings();
431
+ await cleanupReranker().catch((err) => log(`[shutdown] Reranker cleanup failed: ${err}`));
1184
432
  cleanupQualityScoring();
1185
433
  closeDb();
1186
434
  closeGlobalDb();
1187
- // Remove PID and port files
1188
435
  try {
1189
436
  fs.unlinkSync(getDaemonPidFile());
1190
437
  }
1191
438
  catch (err) {
1192
- if (err.code !== 'ENOENT')
1193
- log(`[shutdown] PID file removal failed: ${err}`);
439
+ if (!isErrnoException(err) || err.code !== 'ENOENT') {
440
+ log(`[shutdown] PID file removal failed: ${getErrorMessage(err)}`);
441
+ }
1194
442
  }
1195
443
  try {
1196
444
  fs.unlinkSync(getDaemonPortFile());
1197
445
  }
1198
446
  catch (err) {
1199
- if (err.code !== 'ENOENT')
1200
- log(`[shutdown] Port file removal failed: ${err}`);
447
+ if (!isErrnoException(err) || err.code !== 'ENOENT') {
448
+ log(`[shutdown] Port file removal failed: ${getErrorMessage(err)}`);
449
+ }
1201
450
  }
1202
451
  log('[daemon] Shutdown complete');
1203
452
  process.exit(0);
1204
453
  }
1205
- // ============================================================================
1206
- // Test Helpers (exported for unit testing)
1207
- // ============================================================================
1208
454
  /** @internal Initialize module state for testing without starting HTTP server */
1209
455
  export function _initTestState(cwd = process.cwd()) {
1210
456
  sessionManager = createSessionManager();
@@ -1215,13 +461,10 @@ export function _resetTestState() {
1215
461
  sessionManager = null;
1216
462
  idleWatcher = null;
1217
463
  state = null;
1218
- briefingCache.clear();
1219
- briefingGenerationInProgress.clear();
464
+ resetReflectionRoutesState();
465
+ resetMemoryRoutesState();
466
+ resetSearchRoutesState();
1220
467
  }
1221
- // ============================================================================
1222
- // CLI Entry Point
1223
- // ============================================================================
1224
- // If run directly, start daemon
1225
468
  if (process.argv[1]?.endsWith('service.js') || process.argv[1]?.endsWith('service.ts')) {
1226
469
  startDaemon()
1227
470
  .then(({ port, pid }) => {