gitmem-mcp 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (316) hide show
  1. package/CHANGELOG.md +47 -0
  2. package/CLAUDE.md.template +65 -0
  3. package/LICENSE +21 -0
  4. package/README.md +221 -0
  5. package/bin/gitmem.js +383 -0
  6. package/dist/commands/check.d.ts +33 -0
  7. package/dist/commands/check.d.ts.map +1 -0
  8. package/dist/commands/check.js +492 -0
  9. package/dist/commands/check.js.map +1 -0
  10. package/dist/constants/closing-questions.d.ts +40 -0
  11. package/dist/constants/closing-questions.d.ts.map +1 -0
  12. package/dist/constants/closing-questions.js +107 -0
  13. package/dist/constants/closing-questions.js.map +1 -0
  14. package/dist/diagnostics/anonymizer.d.ts +55 -0
  15. package/dist/diagnostics/anonymizer.d.ts.map +1 -0
  16. package/dist/diagnostics/anonymizer.js +191 -0
  17. package/dist/diagnostics/anonymizer.js.map +1 -0
  18. package/dist/diagnostics/channels.d.ts +132 -0
  19. package/dist/diagnostics/channels.d.ts.map +1 -0
  20. package/dist/diagnostics/channels.js +150 -0
  21. package/dist/diagnostics/channels.js.map +1 -0
  22. package/dist/diagnostics/collector.d.ts +183 -0
  23. package/dist/diagnostics/collector.d.ts.map +1 -0
  24. package/dist/diagnostics/collector.js +227 -0
  25. package/dist/diagnostics/collector.js.map +1 -0
  26. package/dist/diagnostics/index.d.ts +28 -0
  27. package/dist/diagnostics/index.d.ts.map +1 -0
  28. package/dist/diagnostics/index.js +31 -0
  29. package/dist/diagnostics/index.js.map +1 -0
  30. package/dist/index.d.ts +13 -0
  31. package/dist/index.d.ts.map +1 -0
  32. package/dist/index.js +18 -0
  33. package/dist/index.js.map +1 -0
  34. package/dist/schemas/absorb-observations.d.ts +63 -0
  35. package/dist/schemas/absorb-observations.d.ts.map +1 -0
  36. package/dist/schemas/absorb-observations.js +25 -0
  37. package/dist/schemas/absorb-observations.js.map +1 -0
  38. package/dist/schemas/active-sessions.d.ts +71 -0
  39. package/dist/schemas/active-sessions.d.ts.map +1 -0
  40. package/dist/schemas/active-sessions.js +19 -0
  41. package/dist/schemas/active-sessions.js.map +1 -0
  42. package/dist/schemas/analyze.d.ts +38 -0
  43. package/dist/schemas/analyze.d.ts.map +1 -0
  44. package/dist/schemas/analyze.js +30 -0
  45. package/dist/schemas/analyze.js.map +1 -0
  46. package/dist/schemas/common.d.ts +55 -0
  47. package/dist/schemas/common.d.ts.map +1 -0
  48. package/dist/schemas/common.js +65 -0
  49. package/dist/schemas/common.js.map +1 -0
  50. package/dist/schemas/create-decision.d.ts +48 -0
  51. package/dist/schemas/create-decision.d.ts.map +1 -0
  52. package/dist/schemas/create-decision.js +31 -0
  53. package/dist/schemas/create-decision.js.map +1 -0
  54. package/dist/schemas/create-learning.d.ts +107 -0
  55. package/dist/schemas/create-learning.d.ts.map +1 -0
  56. package/dist/schemas/create-learning.js +64 -0
  57. package/dist/schemas/create-learning.js.map +1 -0
  58. package/dist/schemas/get-transcript.d.ts +24 -0
  59. package/dist/schemas/get-transcript.d.ts.map +1 -0
  60. package/dist/schemas/get-transcript.js +22 -0
  61. package/dist/schemas/get-transcript.js.map +1 -0
  62. package/dist/schemas/index.d.ts +23 -0
  63. package/dist/schemas/index.d.ts.map +1 -0
  64. package/dist/schemas/index.js +23 -0
  65. package/dist/schemas/index.js.map +1 -0
  66. package/dist/schemas/log.d.ts +36 -0
  67. package/dist/schemas/log.d.ts.map +1 -0
  68. package/dist/schemas/log.js +27 -0
  69. package/dist/schemas/log.js.map +1 -0
  70. package/dist/schemas/prepare-context.d.ts +41 -0
  71. package/dist/schemas/prepare-context.d.ts.map +1 -0
  72. package/dist/schemas/prepare-context.js +31 -0
  73. package/dist/schemas/prepare-context.js.map +1 -0
  74. package/dist/schemas/recall.d.ts +41 -0
  75. package/dist/schemas/recall.d.ts.map +1 -0
  76. package/dist/schemas/recall.js +47 -0
  77. package/dist/schemas/recall.js.map +1 -0
  78. package/dist/schemas/record-scar-usage-batch.d.ts +82 -0
  79. package/dist/schemas/record-scar-usage-batch.d.ts.map +1 -0
  80. package/dist/schemas/record-scar-usage-batch.js +25 -0
  81. package/dist/schemas/record-scar-usage-batch.js.map +1 -0
  82. package/dist/schemas/record-scar-usage.d.ts +51 -0
  83. package/dist/schemas/record-scar-usage.d.ts.map +1 -0
  84. package/dist/schemas/record-scar-usage.js +32 -0
  85. package/dist/schemas/record-scar-usage.js.map +1 -0
  86. package/dist/schemas/save-transcript.d.ts +38 -0
  87. package/dist/schemas/save-transcript.d.ts.map +1 -0
  88. package/dist/schemas/save-transcript.js +30 -0
  89. package/dist/schemas/save-transcript.js.map +1 -0
  90. package/dist/schemas/search.d.ts +36 -0
  91. package/dist/schemas/search.d.ts.map +1 -0
  92. package/dist/schemas/search.js +27 -0
  93. package/dist/schemas/search.js.map +1 -0
  94. package/dist/schemas/session-close.d.ts +371 -0
  95. package/dist/schemas/session-close.d.ts.map +1 -0
  96. package/dist/schemas/session-close.js +95 -0
  97. package/dist/schemas/session-close.js.map +1 -0
  98. package/dist/schemas/session-start.d.ts +46 -0
  99. package/dist/schemas/session-start.d.ts.map +1 -0
  100. package/dist/schemas/session-start.js +33 -0
  101. package/dist/schemas/session-start.js.map +1 -0
  102. package/dist/schemas/thread.d.ts +72 -0
  103. package/dist/schemas/thread.d.ts.map +1 -0
  104. package/dist/schemas/thread.js +39 -0
  105. package/dist/schemas/thread.js.map +1 -0
  106. package/dist/server.d.ts +22 -0
  107. package/dist/server.d.ts.map +1 -0
  108. package/dist/server.js +313 -0
  109. package/dist/server.js.map +1 -0
  110. package/dist/services/active-sessions.d.ts +66 -0
  111. package/dist/services/active-sessions.d.ts.map +1 -0
  112. package/dist/services/active-sessions.js +311 -0
  113. package/dist/services/active-sessions.js.map +1 -0
  114. package/dist/services/agent-detection.d.ts +25 -0
  115. package/dist/services/agent-detection.d.ts.map +1 -0
  116. package/dist/services/agent-detection.js +93 -0
  117. package/dist/services/agent-detection.js.map +1 -0
  118. package/dist/services/analytics.d.ts +201 -0
  119. package/dist/services/analytics.d.ts.map +1 -0
  120. package/dist/services/analytics.js +483 -0
  121. package/dist/services/analytics.js.map +1 -0
  122. package/dist/services/cache.d.ts +148 -0
  123. package/dist/services/cache.d.ts.map +1 -0
  124. package/dist/services/cache.js +384 -0
  125. package/dist/services/cache.js.map +1 -0
  126. package/dist/services/cache.test.d.ts +8 -0
  127. package/dist/services/cache.test.d.ts.map +1 -0
  128. package/dist/services/cache.test.js +267 -0
  129. package/dist/services/cache.test.js.map +1 -0
  130. package/dist/services/compliance-validator.d.ts +30 -0
  131. package/dist/services/compliance-validator.d.ts.map +1 -0
  132. package/dist/services/compliance-validator.js +257 -0
  133. package/dist/services/compliance-validator.js.map +1 -0
  134. package/dist/services/config.d.ts +48 -0
  135. package/dist/services/config.d.ts.map +1 -0
  136. package/dist/services/config.js +128 -0
  137. package/dist/services/config.js.map +1 -0
  138. package/dist/services/embedding.d.ts +58 -0
  139. package/dist/services/embedding.d.ts.map +1 -0
  140. package/dist/services/embedding.js +243 -0
  141. package/dist/services/embedding.js.map +1 -0
  142. package/dist/services/gitmem-dir.d.ts +38 -0
  143. package/dist/services/gitmem-dir.d.ts.map +1 -0
  144. package/dist/services/gitmem-dir.js +84 -0
  145. package/dist/services/gitmem-dir.js.map +1 -0
  146. package/dist/services/local-file-storage.d.ts +56 -0
  147. package/dist/services/local-file-storage.d.ts.map +1 -0
  148. package/dist/services/local-file-storage.js +213 -0
  149. package/dist/services/local-file-storage.js.map +1 -0
  150. package/dist/services/local-vector-search.d.ts +137 -0
  151. package/dist/services/local-vector-search.d.ts.map +1 -0
  152. package/dist/services/local-vector-search.js +311 -0
  153. package/dist/services/local-vector-search.js.map +1 -0
  154. package/dist/services/metrics.d.ts +104 -0
  155. package/dist/services/metrics.d.ts.map +1 -0
  156. package/dist/services/metrics.js +264 -0
  157. package/dist/services/metrics.js.map +1 -0
  158. package/dist/services/session-state.d.ts +113 -0
  159. package/dist/services/session-state.d.ts.map +1 -0
  160. package/dist/services/session-state.js +203 -0
  161. package/dist/services/session-state.js.map +1 -0
  162. package/dist/services/startup.d.ts +112 -0
  163. package/dist/services/startup.d.ts.map +1 -0
  164. package/dist/services/startup.js +436 -0
  165. package/dist/services/startup.js.map +1 -0
  166. package/dist/services/storage.d.ts +43 -0
  167. package/dist/services/storage.d.ts.map +1 -0
  168. package/dist/services/storage.js +92 -0
  169. package/dist/services/storage.js.map +1 -0
  170. package/dist/services/supabase-client.d.ts +163 -0
  171. package/dist/services/supabase-client.d.ts.map +1 -0
  172. package/dist/services/supabase-client.js +510 -0
  173. package/dist/services/supabase-client.js.map +1 -0
  174. package/dist/services/thread-dedup.d.ts +44 -0
  175. package/dist/services/thread-dedup.d.ts.map +1 -0
  176. package/dist/services/thread-dedup.js +113 -0
  177. package/dist/services/thread-dedup.js.map +1 -0
  178. package/dist/services/thread-manager.d.ts +77 -0
  179. package/dist/services/thread-manager.d.ts.map +1 -0
  180. package/dist/services/thread-manager.js +250 -0
  181. package/dist/services/thread-manager.js.map +1 -0
  182. package/dist/services/thread-suggestions.d.ts +66 -0
  183. package/dist/services/thread-suggestions.d.ts.map +1 -0
  184. package/dist/services/thread-suggestions.js +243 -0
  185. package/dist/services/thread-suggestions.js.map +1 -0
  186. package/dist/services/thread-supabase.d.ts +111 -0
  187. package/dist/services/thread-supabase.d.ts.map +1 -0
  188. package/dist/services/thread-supabase.js +459 -0
  189. package/dist/services/thread-supabase.js.map +1 -0
  190. package/dist/services/thread-vitality.d.ts +65 -0
  191. package/dist/services/thread-vitality.d.ts.map +1 -0
  192. package/dist/services/thread-vitality.js +143 -0
  193. package/dist/services/thread-vitality.js.map +1 -0
  194. package/dist/services/tier.d.ts +52 -0
  195. package/dist/services/tier.d.ts.map +1 -0
  196. package/dist/services/tier.js +109 -0
  197. package/dist/services/tier.js.map +1 -0
  198. package/dist/services/timezone.d.ts +37 -0
  199. package/dist/services/timezone.d.ts.map +1 -0
  200. package/dist/services/timezone.js +147 -0
  201. package/dist/services/timezone.js.map +1 -0
  202. package/dist/services/transcript-chunker.d.ts +18 -0
  203. package/dist/services/transcript-chunker.d.ts.map +1 -0
  204. package/dist/services/transcript-chunker.js +237 -0
  205. package/dist/services/transcript-chunker.js.map +1 -0
  206. package/dist/services/triple-writer.d.ts +128 -0
  207. package/dist/services/triple-writer.d.ts.map +1 -0
  208. package/dist/services/triple-writer.js +338 -0
  209. package/dist/services/triple-writer.js.map +1 -0
  210. package/dist/services/variant-assignment.d.ts +92 -0
  211. package/dist/services/variant-assignment.d.ts.map +1 -0
  212. package/dist/services/variant-assignment.js +196 -0
  213. package/dist/services/variant-assignment.js.map +1 -0
  214. package/dist/tools/absorb-observations.d.ts +16 -0
  215. package/dist/tools/absorb-observations.d.ts.map +1 -0
  216. package/dist/tools/absorb-observations.js +82 -0
  217. package/dist/tools/absorb-observations.js.map +1 -0
  218. package/dist/tools/analyze.d.ts +55 -0
  219. package/dist/tools/analyze.d.ts.map +1 -0
  220. package/dist/tools/analyze.js +139 -0
  221. package/dist/tools/analyze.js.map +1 -0
  222. package/dist/tools/cleanup-threads.d.ts +47 -0
  223. package/dist/tools/cleanup-threads.d.ts.map +1 -0
  224. package/dist/tools/cleanup-threads.js +127 -0
  225. package/dist/tools/cleanup-threads.js.map +1 -0
  226. package/dist/tools/confirm-scars.d.ts +23 -0
  227. package/dist/tools/confirm-scars.d.ts.map +1 -0
  228. package/dist/tools/confirm-scars.js +209 -0
  229. package/dist/tools/confirm-scars.js.map +1 -0
  230. package/dist/tools/create-decision.d.ts +15 -0
  231. package/dist/tools/create-decision.d.ts.map +1 -0
  232. package/dist/tools/create-decision.js +138 -0
  233. package/dist/tools/create-decision.js.map +1 -0
  234. package/dist/tools/create-learning.d.ts +15 -0
  235. package/dist/tools/create-learning.d.ts.map +1 -0
  236. package/dist/tools/create-learning.js +226 -0
  237. package/dist/tools/create-learning.js.map +1 -0
  238. package/dist/tools/create-thread.d.ts +42 -0
  239. package/dist/tools/create-thread.d.ts.map +1 -0
  240. package/dist/tools/create-thread.js +180 -0
  241. package/dist/tools/create-thread.js.map +1 -0
  242. package/dist/tools/definitions.d.ts +5013 -0
  243. package/dist/tools/definitions.d.ts.map +1 -0
  244. package/dist/tools/definitions.js +2017 -0
  245. package/dist/tools/definitions.js.map +1 -0
  246. package/dist/tools/dismiss-suggestion.d.ts +20 -0
  247. package/dist/tools/dismiss-suggestion.d.ts.map +1 -0
  248. package/dist/tools/dismiss-suggestion.js +40 -0
  249. package/dist/tools/dismiss-suggestion.js.map +1 -0
  250. package/dist/tools/get-transcript.d.ts +24 -0
  251. package/dist/tools/get-transcript.d.ts.map +1 -0
  252. package/dist/tools/get-transcript.js +52 -0
  253. package/dist/tools/get-transcript.js.map +1 -0
  254. package/dist/tools/graph-traverse.d.ts +83 -0
  255. package/dist/tools/graph-traverse.d.ts.map +1 -0
  256. package/dist/tools/graph-traverse.js +394 -0
  257. package/dist/tools/graph-traverse.js.map +1 -0
  258. package/dist/tools/list-threads.d.ts +15 -0
  259. package/dist/tools/list-threads.d.ts.map +1 -0
  260. package/dist/tools/list-threads.js +114 -0
  261. package/dist/tools/list-threads.js.map +1 -0
  262. package/dist/tools/log.d.ts +43 -0
  263. package/dist/tools/log.d.ts.map +1 -0
  264. package/dist/tools/log.js +157 -0
  265. package/dist/tools/log.js.map +1 -0
  266. package/dist/tools/prepare-context.d.ts +36 -0
  267. package/dist/tools/prepare-context.d.ts.map +1 -0
  268. package/dist/tools/prepare-context.js +353 -0
  269. package/dist/tools/prepare-context.js.map +1 -0
  270. package/dist/tools/promote-suggestion.d.ts +25 -0
  271. package/dist/tools/promote-suggestion.d.ts.map +1 -0
  272. package/dist/tools/promote-suggestion.js +60 -0
  273. package/dist/tools/promote-suggestion.js.map +1 -0
  274. package/dist/tools/recall.d.ts +77 -0
  275. package/dist/tools/recall.d.ts.map +1 -0
  276. package/dist/tools/recall.js +423 -0
  277. package/dist/tools/recall.js.map +1 -0
  278. package/dist/tools/recall.test.d.ts +5 -0
  279. package/dist/tools/recall.test.d.ts.map +1 -0
  280. package/dist/tools/recall.test.js +155 -0
  281. package/dist/tools/recall.test.js.map +1 -0
  282. package/dist/tools/record-scar-usage-batch.d.ts +10 -0
  283. package/dist/tools/record-scar-usage-batch.d.ts.map +1 -0
  284. package/dist/tools/record-scar-usage-batch.js +153 -0
  285. package/dist/tools/record-scar-usage-batch.js.map +1 -0
  286. package/dist/tools/record-scar-usage.d.ts +14 -0
  287. package/dist/tools/record-scar-usage.d.ts.map +1 -0
  288. package/dist/tools/record-scar-usage.js +94 -0
  289. package/dist/tools/record-scar-usage.js.map +1 -0
  290. package/dist/tools/resolve-thread.d.ts +16 -0
  291. package/dist/tools/resolve-thread.d.ts.map +1 -0
  292. package/dist/tools/resolve-thread.js +102 -0
  293. package/dist/tools/resolve-thread.js.map +1 -0
  294. package/dist/tools/save-transcript.d.ts +29 -0
  295. package/dist/tools/save-transcript.d.ts.map +1 -0
  296. package/dist/tools/save-transcript.js +97 -0
  297. package/dist/tools/save-transcript.js.map +1 -0
  298. package/dist/tools/search.d.ts +46 -0
  299. package/dist/tools/search.d.ts.map +1 -0
  300. package/dist/tools/search.js +186 -0
  301. package/dist/tools/search.js.map +1 -0
  302. package/dist/tools/session-close.d.ts +14 -0
  303. package/dist/tools/session-close.d.ts.map +1 -0
  304. package/dist/tools/session-close.js +881 -0
  305. package/dist/tools/session-close.js.map +1 -0
  306. package/dist/tools/session-start.d.ts +38 -0
  307. package/dist/tools/session-start.d.ts.map +1 -0
  308. package/dist/tools/session-start.js +1104 -0
  309. package/dist/tools/session-start.js.map +1 -0
  310. package/dist/types/index.d.ts +456 -0
  311. package/dist/types/index.d.ts.map +1 -0
  312. package/dist/types/index.js +5 -0
  313. package/dist/types/index.js.map +1 -0
  314. package/package.json +76 -0
  315. package/schema/setup.sql +193 -0
  316. package/schema/starter-scars.json +206 -0
@@ -0,0 +1,113 @@
1
+ /**
2
+ * Thread Deduplication Service (Phase 3)
3
+ *
4
+ * Pure functions for detecting duplicate threads by embedding similarity
5
+ * or normalized text equality. Zero I/O — all Supabase and embedding
6
+ * calls live in the caller (create-thread.ts).
7
+ *
8
+ * Strategy:
9
+ * 1. If embedding available: cosine similarity > 0.85 → duplicate
10
+ * 2. If embedding unavailable: normalized text equality → duplicate
11
+ * 3. If no existing threads: skip check
12
+ */
13
+ // ---------- Constants ----------
14
+ export const DEDUP_SIMILARITY_THRESHOLD = 0.85;
15
+ // ---------- Core ----------
16
+ /**
17
+ * Check if new thread text is a semantic duplicate of any existing open thread.
18
+ *
19
+ * @param newText - Trimmed thread text
20
+ * @param newEmbedding - Normalized embedding vector, or null if unavailable
21
+ * @param existingThreads - Open threads with optional embeddings
22
+ */
23
+ export function checkDuplicate(newText, newEmbedding, existingThreads) {
24
+ if (existingThreads.length === 0) {
25
+ return {
26
+ is_duplicate: false,
27
+ matched_thread_id: null,
28
+ matched_text: null,
29
+ similarity: null,
30
+ method: "skipped",
31
+ };
32
+ }
33
+ // Embedding-based comparison
34
+ if (newEmbedding !== null) {
35
+ let bestSimilarity = -1;
36
+ let bestThread = null;
37
+ for (const thread of existingThreads) {
38
+ if (thread.embedding === null)
39
+ continue;
40
+ const sim = cosineSimilarity(newEmbedding, thread.embedding);
41
+ if (sim > bestSimilarity) {
42
+ bestSimilarity = sim;
43
+ bestThread = thread;
44
+ }
45
+ }
46
+ if (bestThread && bestSimilarity > DEDUP_SIMILARITY_THRESHOLD) {
47
+ return {
48
+ is_duplicate: true,
49
+ matched_thread_id: bestThread.thread_id,
50
+ matched_text: bestThread.text,
51
+ similarity: round(bestSimilarity, 4),
52
+ method: "embedding",
53
+ };
54
+ }
55
+ return {
56
+ is_duplicate: false,
57
+ matched_thread_id: null,
58
+ matched_text: null,
59
+ similarity: bestSimilarity >= 0 ? round(bestSimilarity, 4) : null,
60
+ method: "embedding",
61
+ };
62
+ }
63
+ // Text normalization fallback (conservative: exact match only)
64
+ const normalizedNew = normalizeText(newText);
65
+ for (const thread of existingThreads) {
66
+ if (normalizeText(thread.text) === normalizedNew) {
67
+ return {
68
+ is_duplicate: true,
69
+ matched_thread_id: thread.thread_id,
70
+ matched_text: thread.text,
71
+ similarity: null,
72
+ method: "text_normalization",
73
+ };
74
+ }
75
+ }
76
+ return {
77
+ is_duplicate: false,
78
+ matched_thread_id: null,
79
+ matched_text: null,
80
+ similarity: null,
81
+ method: "text_normalization",
82
+ };
83
+ }
84
+ // ---------- Helpers ----------
85
+ /**
86
+ * Cosine similarity between two normalized vectors.
87
+ * Assumes vectors are already L2-normalized (dot product = cosine similarity).
88
+ */
89
+ export function cosineSimilarity(a, b) {
90
+ if (a.length !== b.length)
91
+ return 0;
92
+ let dotProduct = 0;
93
+ for (let i = 0; i < a.length; i++) {
94
+ dotProduct += a[i] * b[i];
95
+ }
96
+ return dotProduct;
97
+ }
98
+ /**
99
+ * Normalize text for conservative text-only comparison.
100
+ * Lowercase, collapse whitespace, trim, strip trailing punctuation.
101
+ */
102
+ export function normalizeText(text) {
103
+ return text
104
+ .toLowerCase()
105
+ .replace(/\s+/g, " ")
106
+ .trim()
107
+ .replace(/[.!?;:]+$/, "");
108
+ }
109
+ function round(value, decimals) {
110
+ const factor = 10 ** decimals;
111
+ return Math.round(value * factor) / factor;
112
+ }
113
+ //# sourceMappingURL=thread-dedup.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"thread-dedup.js","sourceRoot":"","sources":["../../src/services/thread-dedup.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Thread Lifecycle Manager (OD-thread-lifecycle)
3
+ *
4
+ * Core logic for thread lifecycle management:
5
+ * - ID generation, migration from plain strings to ThreadObject
6
+ * - Aggregation across sessions (replaces aggregateOpenThreads)
7
+ * - Resolution by ID or text match
8
+ * - Local file persistence (.gitmem/threads.json)
9
+ */
10
+ import type { ThreadObject } from "../types/index.js";
11
+ /**
12
+ * Generate a thread ID: "t-" + 8 hex chars
13
+ */
14
+ export declare function generateThreadId(): string;
15
+ /**
16
+ * Migrate a plain string thread to a ThreadObject.
17
+ */
18
+ export declare function migrateStringThread(text: string, sourceSession?: string): ThreadObject;
19
+ /**
20
+ * Normalize a mixed array of strings and ThreadObjects into ThreadObject[].
21
+ * Handles backward compatibility with existing plain string threads.
22
+ */
23
+ export declare function normalizeThreads(raw: (string | ThreadObject)[], sourceSession?: string): ThreadObject[];
24
+ interface SessionRecord {
25
+ id: string;
26
+ session_date: string;
27
+ open_threads?: (string | ThreadObject)[];
28
+ close_compliance?: Record<string, unknown> | null;
29
+ }
30
+ interface AggregateResult {
31
+ open: ThreadObject[];
32
+ recently_resolved: ThreadObject[];
33
+ }
34
+ /**
35
+ * Aggregate threads across recent closed sessions.
36
+ * Replaces the old aggregateOpenThreads() function.
37
+ *
38
+ * Returns:
39
+ * - open: deduplicated open threads
40
+ * - recently_resolved: threads resolved in the most recent session
41
+ */
42
+ export declare function aggregateThreads(sessions: SessionRecord[], maxSessions?: number, maxAgeDays?: number): AggregateResult;
43
+ /**
44
+ * Find a thread by exact ID.
45
+ */
46
+ export declare function findThreadById(threads: ThreadObject[], id: string): ThreadObject | null;
47
+ /**
48
+ * Find a thread by case-insensitive substring match on text.
49
+ * Returns the first match.
50
+ */
51
+ export declare function findThreadByText(threads: ThreadObject[], query: string): ThreadObject | null;
52
+ /**
53
+ * Resolve a thread in a list. Returns the resolved thread or null if not found.
54
+ * Mutates the thread object in place.
55
+ */
56
+ export declare function resolveThread(threads: ThreadObject[], options: {
57
+ threadId?: string;
58
+ textMatch?: string;
59
+ sessionId?: string;
60
+ resolutionNote?: string;
61
+ }): ThreadObject | null;
62
+ /**
63
+ * Load threads from .gitmem/threads.json.
64
+ * Returns empty array if file doesn't exist or is invalid.
65
+ */
66
+ export declare function loadThreadsFile(): ThreadObject[];
67
+ /**
68
+ * Save threads to .gitmem/threads.json.
69
+ */
70
+ export declare function saveThreadsFile(threads: ThreadObject[]): void;
71
+ /**
72
+ * Merge two thread lists, preferring resolved state.
73
+ * Used to merge session-close payload with mid-session resolutions.
74
+ */
75
+ export declare function mergeThreadStates(incoming: ThreadObject[], current: ThreadObject[]): ThreadObject[];
76
+ export {};
77
+ //# sourceMappingURL=thread-manager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"thread-manager.d.ts","sourceRoot":"","sources":["../../src/services/thread-manager.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAKH,OAAO,KAAK,EAAE,YAAY,EAAgB,MAAM,mBAAmB,CAAC;AAIpE;;GAEG;AACH,wBAAgB,gBAAgB,IAAI,MAAM,CAEzC;AAID;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,aAAa,CAAC,EAAE,MAAM,GAAG,YAAY,CAQtF;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE,CAAC,MAAM,GAAG,YAAY,CAAC,EAAE,EAC9B,aAAa,CAAC,EAAE,MAAM,GACrB,YAAY,EAAE,CA+DhB;AAID,UAAU,aAAa;IACrB,EAAE,EAAE,MAAM,CAAC;IACX,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,CAAC,MAAM,GAAG,YAAY,CAAC,EAAE,CAAC;IACzC,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;CACnD;AAED,UAAU,eAAe;IACvB,IAAI,EAAE,YAAY,EAAE,CAAC;IACrB,iBAAiB,EAAE,YAAY,EAAE,CAAC;CACnC;AAED;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,aAAa,EAAE,EACzB,WAAW,SAAI,EACf,UAAU,SAAK,GACd,eAAe,CAwCjB;AAID;;GAEG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,YAAY,EAAE,EAAE,EAAE,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI,CAEvF;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,YAAY,EAAE,EAAE,KAAK,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI,CAG5F;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAC3B,OAAO,EAAE,YAAY,EAAE,EACvB,OAAO,EAAE;IACP,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB,GACA,YAAY,GAAG,IAAI,CAkBrB;AAWD;;;GAGG;AACH,wBAAgB,eAAe,IAAI,YAAY,EAAE,CAWhD;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,YAAY,EAAE,GAAG,IAAI,CAO7D;AAID;;;GAGG;AACH,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,YAAY,EAAE,EACxB,OAAO,EAAE,YAAY,EAAE,GACtB,YAAY,EAAE,CA8BhB"}
@@ -0,0 +1,250 @@
1
+ /**
2
+ * Thread Lifecycle Manager (OD-thread-lifecycle)
3
+ *
4
+ * Core logic for thread lifecycle management:
5
+ * - ID generation, migration from plain strings to ThreadObject
6
+ * - Aggregation across sessions (replaces aggregateOpenThreads)
7
+ * - Resolution by ID or text match
8
+ * - Local file persistence (.gitmem/threads.json)
9
+ */
10
+ import * as fs from "fs";
11
+ import * as path from "path";
12
+ import * as crypto from "crypto";
13
+ // ---------- ID Generation ----------
14
+ /**
15
+ * Generate a thread ID: "t-" + 8 hex chars
16
+ */
17
+ export function generateThreadId() {
18
+ return "t-" + crypto.randomBytes(4).toString("hex");
19
+ }
20
+ // ---------- Migration ----------
21
+ /**
22
+ * Migrate a plain string thread to a ThreadObject.
23
+ */
24
+ export function migrateStringThread(text, sourceSession) {
25
+ return {
26
+ id: generateThreadId(),
27
+ text,
28
+ status: "open",
29
+ created_at: new Date().toISOString(),
30
+ ...(sourceSession && { source_session: sourceSession }),
31
+ };
32
+ }
33
+ /**
34
+ * Normalize a mixed array of strings and ThreadObjects into ThreadObject[].
35
+ * Handles backward compatibility with existing plain string threads.
36
+ */
37
+ export function normalizeThreads(raw, sourceSession) {
38
+ return raw.map((item) => {
39
+ if (typeof item === "string") {
40
+ // Try to parse JSON thread objects (some sessions store {item, context} or full ThreadObject)
41
+ if (item.startsWith("{")) {
42
+ try {
43
+ const parsed = JSON.parse(item);
44
+ // If it looks like a ThreadObject already, use it
45
+ if (parsed.id && parsed.text && parsed.status) {
46
+ return parsed;
47
+ }
48
+ // Handle {id, status, note} format — "note" is an alias for "text"
49
+ // This format appears when threads carry forward between sessions
50
+ if (parsed.id && parsed.note && parsed.status) {
51
+ return {
52
+ id: parsed.id,
53
+ text: parsed.note,
54
+ status: parsed.status,
55
+ created_at: parsed.created_at || new Date().toISOString(),
56
+ ...(sourceSession && { source_session: sourceSession }),
57
+ ...(parsed.resolved_at && { resolved_at: parsed.resolved_at }),
58
+ };
59
+ }
60
+ // Legacy format: {item, context} or {text, status} without id
61
+ return migrateStringThread(parsed.item || parsed.text || parsed.note || item, sourceSession);
62
+ }
63
+ catch {
64
+ // Not valid JSON, treat as plain text
65
+ }
66
+ }
67
+ return migrateStringThread(item, sourceSession);
68
+ }
69
+ // Already a ThreadObject — but validate and repair common field issues
70
+ const obj = item;
71
+ // Fix objects with "note" instead of "text" (agent writes {id, status, note})
72
+ if (!obj.text && obj.note && typeof obj.note === "string") {
73
+ return {
74
+ ...item,
75
+ text: obj.note,
76
+ };
77
+ }
78
+ // Fix objects where "text" is a JSON string containing a thread
79
+ if (typeof obj.text === "string" && obj.text.startsWith("{")) {
80
+ try {
81
+ const inner = JSON.parse(obj.text);
82
+ if (inner.id && (inner.text || inner.note)) {
83
+ return {
84
+ id: inner.id,
85
+ text: inner.text || inner.note,
86
+ status: inner.status || item.status,
87
+ created_at: inner.created_at || item.created_at || new Date().toISOString(),
88
+ ...(sourceSession && { source_session: sourceSession }),
89
+ ...(inner.resolved_at && { resolved_at: inner.resolved_at }),
90
+ };
91
+ }
92
+ }
93
+ catch {
94
+ // Not valid JSON in text field, keep as-is
95
+ }
96
+ }
97
+ return item;
98
+ });
99
+ }
100
+ /**
101
+ * Aggregate threads across recent closed sessions.
102
+ * Replaces the old aggregateOpenThreads() function.
103
+ *
104
+ * Returns:
105
+ * - open: deduplicated open threads
106
+ * - recently_resolved: threads resolved in the most recent session
107
+ */
108
+ export function aggregateThreads(sessions, maxSessions = 5, maxAgeDays = 14) {
109
+ const cutoffDate = new Date();
110
+ cutoffDate.setDate(cutoffDate.getDate() - maxAgeDays);
111
+ const cutoffStr = cutoffDate.toISOString().split("T")[0];
112
+ const closedSessions = sessions
113
+ .filter((s) => s.close_compliance != null && s.session_date >= cutoffStr)
114
+ .slice(0, maxSessions);
115
+ const seenText = new Set();
116
+ const seenIds = new Set();
117
+ const open = [];
118
+ const recentlyResolved = [];
119
+ for (const session of closedSessions) {
120
+ const rawThreads = session.open_threads || [];
121
+ const normalized = normalizeThreads(rawThreads, session.id);
122
+ for (const thread of normalized) {
123
+ // Skip PROJECT STATE threads (handled separately via OD-534)
124
+ if (thread.text.startsWith("PROJECT STATE:"))
125
+ continue;
126
+ // Deduplicate by both thread ID and text content
127
+ const key = thread.text.toLowerCase().trim();
128
+ if (!key || seenText.has(key) || seenIds.has(thread.id))
129
+ continue;
130
+ seenText.add(key);
131
+ seenIds.add(thread.id);
132
+ if (thread.status === "resolved") {
133
+ recentlyResolved.push(thread);
134
+ }
135
+ else {
136
+ open.push(thread);
137
+ }
138
+ }
139
+ }
140
+ return { open, recently_resolved: recentlyResolved };
141
+ }
142
+ // ---------- Thread Resolution ----------
143
+ /**
144
+ * Find a thread by exact ID.
145
+ */
146
+ export function findThreadById(threads, id) {
147
+ return threads.find((t) => t.id === id) || null;
148
+ }
149
+ /**
150
+ * Find a thread by case-insensitive substring match on text.
151
+ * Returns the first match.
152
+ */
153
+ export function findThreadByText(threads, query) {
154
+ const lowerQuery = query.toLowerCase().trim();
155
+ return threads.find((t) => t.text.toLowerCase().includes(lowerQuery)) || null;
156
+ }
157
+ /**
158
+ * Resolve a thread in a list. Returns the resolved thread or null if not found.
159
+ * Mutates the thread object in place.
160
+ */
161
+ export function resolveThread(threads, options) {
162
+ let thread = null;
163
+ if (options.threadId) {
164
+ thread = findThreadById(threads, options.threadId);
165
+ }
166
+ else if (options.textMatch) {
167
+ thread = findThreadByText(threads, options.textMatch);
168
+ }
169
+ if (!thread)
170
+ return null;
171
+ if (thread.status === "resolved")
172
+ return thread; // Already resolved
173
+ thread.status = "resolved";
174
+ thread.resolved_at = new Date().toISOString();
175
+ if (options.sessionId)
176
+ thread.resolved_by_session = options.sessionId;
177
+ if (options.resolutionNote)
178
+ thread.resolution_note = options.resolutionNote;
179
+ return thread;
180
+ }
181
+ // ---------- Local File Persistence ----------
182
+ const THREADS_FILENAME = "threads.json";
183
+ function getThreadsFilePath() {
184
+ const gitmemDir = path.join(process.cwd(), ".gitmem");
185
+ return path.join(gitmemDir, THREADS_FILENAME);
186
+ }
187
+ /**
188
+ * Load threads from .gitmem/threads.json.
189
+ * Returns empty array if file doesn't exist or is invalid.
190
+ */
191
+ export function loadThreadsFile() {
192
+ try {
193
+ const filePath = getThreadsFilePath();
194
+ if (!fs.existsSync(filePath))
195
+ return [];
196
+ const content = fs.readFileSync(filePath, "utf-8");
197
+ const data = JSON.parse(content);
198
+ if (!Array.isArray(data))
199
+ return [];
200
+ return data;
201
+ }
202
+ catch {
203
+ return [];
204
+ }
205
+ }
206
+ /**
207
+ * Save threads to .gitmem/threads.json.
208
+ */
209
+ export function saveThreadsFile(threads) {
210
+ const gitmemDir = path.join(process.cwd(), ".gitmem");
211
+ if (!fs.existsSync(gitmemDir)) {
212
+ fs.mkdirSync(gitmemDir, { recursive: true });
213
+ }
214
+ const filePath = path.join(gitmemDir, THREADS_FILENAME);
215
+ fs.writeFileSync(filePath, JSON.stringify(threads, null, 2));
216
+ }
217
+ // ---------- Merge ----------
218
+ /**
219
+ * Merge two thread lists, preferring resolved state.
220
+ * Used to merge session-close payload with mid-session resolutions.
221
+ */
222
+ export function mergeThreadStates(incoming, current) {
223
+ const byId = new Map();
224
+ // Start with current state (may have mid-session resolutions)
225
+ for (const t of current) {
226
+ byId.set(t.id, t);
227
+ }
228
+ // Merge incoming — new threads get added, existing threads prefer resolved state
229
+ for (const t of incoming) {
230
+ const existing = byId.get(t.id);
231
+ if (!existing) {
232
+ byId.set(t.id, t);
233
+ }
234
+ else if (t.status === "resolved" && existing.status === "open") {
235
+ // Incoming says resolved — update
236
+ byId.set(t.id, t);
237
+ }
238
+ // If existing is resolved and incoming is open, keep resolved
239
+ }
240
+ // Also merge by text for threads that have different IDs (migration scenario)
241
+ const textIndex = new Map(); // text -> id
242
+ for (const [id, t] of byId) {
243
+ const key = t.text.toLowerCase().trim();
244
+ if (!textIndex.has(key)) {
245
+ textIndex.set(key, id);
246
+ }
247
+ }
248
+ return Array.from(byId.values());
249
+ }
250
+ //# sourceMappingURL=thread-manager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"thread-manager.js","sourceRoot":"","sources":["../../src/services/thread-manager.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AACzB,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAC7B,OAAO,KAAK,MAAM,MAAM,QAAQ,CAAC;AAGjC,sCAAsC;AAEtC;;GAEG;AACH,MAAM,UAAU,gBAAgB;IAC9B,OAAO,IAAI,GAAG,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;AACtD,CAAC;AAED,kCAAkC;AAElC;;GAEG;AACH,MAAM,UAAU,mBAAmB,CAAC,IAAY,EAAE,aAAsB;IACtE,OAAO;QACL,EAAE,EAAE,gBAAgB,EAAE;QACtB,IAAI;QACJ,MAAM,EAAE,MAAM;QACd,UAAU,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QACpC,GAAG,CAAC,aAAa,IAAI,EAAE,cAAc,EAAE,aAAa,EAAE,CAAC;KACxD,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAC9B,GAA8B,EAC9B,aAAsB;IAEtB,OAAO,GAAG,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE;QACtB,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE,CAAC;YAC7B,8FAA8F;YAC9F,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;gBACzB,IAAI,CAAC;oBACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;oBAChC,kDAAkD;oBAClD,IAAI,MAAM,CAAC,EAAE,IAAI,MAAM,CAAC,IAAI,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;wBAC9C,OAAO,MAAsB,CAAC;oBAChC,CAAC;oBACD,mEAAmE;oBACnE,kEAAkE;oBAClE,IAAI,MAAM,CAAC,EAAE,IAAI,MAAM,CAAC,IAAI,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;wBAC9C,OAAO;4BACL,EAAE,EAAE,MAAM,CAAC,EAAE;4BACb,IAAI,EAAE,MAAM,CAAC,IAAI;4BACjB,MAAM,EAAE,MAAM,CAAC,MAAM;4BACrB,UAAU,EAAE,MAAM,CAAC,UAAU,IAAI,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;4BACzD,GAAG,CAAC,aAAa,IAAI,EAAE,cAAc,EAAE,aAAa,EAAE,CAAC;4BACvD,GAAG,CAAC,MAAM,CAAC,WAAW,IAAI,EAAE,WAAW,EAAE,MAAM,CAAC,WAAW,EAAE,CAAC;yBAC/C,CAAC;oBACpB,CAAC;oBACD,8DAA8D;oBAC9D,OAAO,mBAAmB,CAAC,MAAM,CAAC,IAAI,IAAI,MAAM,CAAC,IAAI,IAAI,MAAM,CAAC,IAAI,IAAI,IAAI,EAAE,aAAa,CAAC,CAAC;gBAC/F,CAAC;gBAAC,MAAM,CAAC;oBACP,sCAAsC;gBACxC,CAAC;YACH,CAAC;YACD,OAAO,mBAAmB,CAAC,IAAI,EAAE,aAAa,CAAC,CAAC;QAClD,CAAC;QACD,uEAAuE;QACvE,MAAM,GAAG,GAAG,IAA0C,CAAC;QAEvD,8EAA8E;QAC9E,IAAI,CAAC,GAAG,CAAC,IAAI,IAAI,GAAG,CAAC,IAAI,IAAI,OAAO,GAAG,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YAC1D,OAAO;gBACL,GAAG,IAAI;gBACP,IAAI,EAAE,GAAG,CAAC,IAAc;aACT,CAAC;QACpB,CAAC;QAED,gEAAgE;QAChE,IAAI,OAAO,GAAG,CAAC,IAAI,KAAK,QAAQ,IAAK,GAAG,CAAC,IAAe,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;YACzE,IAAI,CAAC;gBACH,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAc,CAAC,CAAC;gBAC7C,IAAI,KAAK,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,IAAI,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;oBAC3C,OAAO;wBACL,EAAE,EAAE,KAAK,CAAC,EAAE;wBACZ,IAAI,EAAE,KAAK,CAAC,IAAI,IAAI,KAAK,CAAC,IAAI;wBAC9B,MAAM,EAAE,KAAK,CAAC,MAAM,IAAI,IAAI,CAAC,MAAM;wBACnC,UAAU,EAAE,KAAK,CAAC,UAAU,IAAI,IAAI,CAAC,UAAU,IAAI,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;wBAC3E,GAAG,CAAC,aAAa,IAAI,EAAE,cAAc,EAAE,aAAa,EAAE,CAAC;wBACvD,GAAG,CAAC,KAAK,CAAC,WAAW,IAAI,EAAE,WAAW,EAAE,KAAK,CAAC,WAAW,EAAE,CAAC;qBAC7C,CAAC;gBACpB,CAAC;YACH,CAAC;YAAC,MAAM,CAAC;gBACP,2CAA2C;YAC7C,CAAC;QACH,CAAC;QAED,OAAO,IAAI,CAAC;IACd,CAAC,CAAC,CAAC;AACL,CAAC;AAgBD;;;;;;;GAOG;AACH,MAAM,UAAU,gBAAgB,CAC9B,QAAyB,EACzB,WAAW,GAAG,CAAC,EACf,UAAU,GAAG,EAAE;IAEf,MAAM,UAAU,GAAG,IAAI,IAAI,EAAE,CAAC;IAC9B,UAAU,CAAC,OAAO,CAAC,UAAU,CAAC,OAAO,EAAE,GAAG,UAAU,CAAC,CAAC;IACtD,MAAM,SAAS,GAAG,UAAU,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IAEzD,MAAM,cAAc,GAAG,QAAQ;SAC5B,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,gBAAgB,IAAI,IAAI,IAAI,CAAC,CAAC,YAAY,IAAI,SAAS,CAAC;SACxE,KAAK,CAAC,CAAC,EAAE,WAAW,CAAC,CAAC;IAEzB,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAU,CAAC;IACnC,MAAM,OAAO,GAAG,IAAI,GAAG,EAAU,CAAC;IAClC,MAAM,IAAI,GAAmB,EAAE,CAAC;IAChC,MAAM,gBAAgB,GAAmB,EAAE,CAAC;IAE5C,KAAK,MAAM,OAAO,IAAI,cAAc,EAAE,CAAC;QACrC,MAAM,UAAU,GAAG,OAAO,CAAC,YAAY,IAAI,EAAE,CAAC;QAC9C,MAAM,UAAU,GAAG,gBAAgB,CACjC,UAAuC,EACvC,OAAO,CAAC,EAAE,CACX,CAAC;QAEF,KAAK,MAAM,MAAM,IAAI,UAAU,EAAE,CAAC;YAChC,6DAA6D;YAC7D,IAAI,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,gBAAgB,CAAC;gBAAE,SAAS;YAEvD,iDAAiD;YACjD,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,IAAI,EAAE,CAAC;YAC7C,IAAI,CAAC,GAAG,IAAI,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;gBAAE,SAAS;YAClE,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YAClB,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;YAEvB,IAAI,MAAM,CAAC,MAAM,KAAK,UAAU,EAAE,CAAC;gBACjC,gBAAgB,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YAChC,CAAC;iBAAM,CAAC;gBACN,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YACpB,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,EAAE,IAAI,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,CAAC;AACvD,CAAC;AAED,0CAA0C;AAE1C;;GAEG;AACH,MAAM,UAAU,cAAc,CAAC,OAAuB,EAAE,EAAU;IAChE,OAAO,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,IAAI,IAAI,CAAC;AAClD,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAAC,OAAuB,EAAE,KAAa;IACrE,MAAM,UAAU,GAAG,KAAK,CAAC,WAAW,EAAE,CAAC,IAAI,EAAE,CAAC;IAC9C,OAAO,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,IAAI,IAAI,CAAC;AAChF,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,aAAa,CAC3B,OAAuB,EACvB,OAKC;IAED,IAAI,MAAM,GAAwB,IAAI,CAAC;IAEvC,IAAI,OAAO,CAAC,QAAQ,EAAE,CAAC;QACrB,MAAM,GAAG,cAAc,CAAC,OAAO,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC;IACrD,CAAC;SAAM,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC;QAC7B,MAAM,GAAG,gBAAgB,CAAC,OAAO,EAAE,OAAO,CAAC,SAAS,CAAC,CAAC;IACxD,CAAC;IAED,IAAI,CAAC,MAAM;QAAE,OAAO,IAAI,CAAC;IACzB,IAAI,MAAM,CAAC,MAAM,KAAK,UAAU;QAAE,OAAO,MAAM,CAAC,CAAC,mBAAmB;IAEpE,MAAM,CAAC,MAAM,GAAG,UAAU,CAAC;IAC3B,MAAM,CAAC,WAAW,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAC9C,IAAI,OAAO,CAAC,SAAS;QAAE,MAAM,CAAC,mBAAmB,GAAG,OAAO,CAAC,SAAS,CAAC;IACtE,IAAI,OAAO,CAAC,cAAc;QAAE,MAAM,CAAC,eAAe,GAAG,OAAO,CAAC,cAAc,CAAC;IAE5E,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,+CAA+C;AAE/C,MAAM,gBAAgB,GAAG,cAAc,CAAC;AAExC,SAAS,kBAAkB;IACzB,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,SAAS,CAAC,CAAC;IACtD,OAAO,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,gBAAgB,CAAC,CAAC;AAChD,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,eAAe;IAC7B,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,kBAAkB,EAAE,CAAC;QACtC,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC;YAAE,OAAO,EAAE,CAAC;QACxC,MAAM,OAAO,GAAG,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QACnD,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QACjC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC;YAAE,OAAO,EAAE,CAAC;QACpC,OAAO,IAAsB,CAAC;IAChC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,eAAe,CAAC,OAAuB;IACrD,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,SAAS,CAAC,CAAC;IACtD,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;QAC9B,EAAE,CAAC,SAAS,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC/C,CAAC;IACD,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,gBAAgB,CAAC,CAAC;IACxD,EAAE,CAAC,aAAa,CAAC,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;AAC/D,CAAC;AAED,8BAA8B;AAE9B;;;GAGG;AACH,MAAM,UAAU,iBAAiB,CAC/B,QAAwB,EACxB,OAAuB;IAEvB,MAAM,IAAI,GAAG,IAAI,GAAG,EAAwB,CAAC;IAE7C,8DAA8D;IAC9D,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;QACxB,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;IACpB,CAAC;IAED,iFAAiF;IACjF,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;QACzB,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QAChC,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;QACpB,CAAC;aAAM,IAAI,CAAC,CAAC,MAAM,KAAK,UAAU,IAAI,QAAQ,CAAC,MAAM,KAAK,MAAM,EAAE,CAAC;YACjE,kCAAkC;YAClC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;QACpB,CAAC;QACD,8DAA8D;IAChE,CAAC;IAED,8EAA8E;IAC9E,MAAM,SAAS,GAAG,IAAI,GAAG,EAAkB,CAAC,CAAC,aAAa;IAC1D,KAAK,MAAM,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC;QAC3B,MAAM,GAAG,GAAG,CAAC,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,IAAI,EAAE,CAAC;QACxC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;YACxB,SAAS,CAAC,GAAG,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;QACzB,CAAC;IACH,CAAC;IAED,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;AACnC,CAAC"}
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Thread Suggestions Service (Phase 5: Implicit Thread Detection)
3
+ *
4
+ * Detects recurring session topics not captured by existing threads.
5
+ * Core detection is pure (no I/O). File persistence and Supabase
6
+ * queries are separate functions called by the session lifecycle.
7
+ *
8
+ * Algorithm:
9
+ * 1. Compare current session embedding against recent sessions
10
+ * 2. If 3+ sessions are similar AND no matching open thread exists
11
+ * → create a thread suggestion
12
+ * 3. Suggestions surfaced at session_start, managed via promote/dismiss
13
+ */
14
+ import type { ThreadWithEmbedding } from "./thread-dedup.js";
15
+ import type { ThreadSuggestion, Project } from "../types/index.js";
16
+ export declare const SESSION_SIMILARITY_THRESHOLD = 0.7;
17
+ export declare const THREAD_MATCH_THRESHOLD = 0.8;
18
+ export declare const SUGGESTION_MATCH_THRESHOLD = 0.8;
19
+ export declare const MIN_EVIDENCE_SESSIONS = 3;
20
+ export interface SessionEmbeddingRecord {
21
+ session_id: string;
22
+ session_title: string;
23
+ embedding: number[];
24
+ }
25
+ /**
26
+ * Detect suggested threads from session embedding clustering.
27
+ * Pure function — no I/O, no side effects.
28
+ *
29
+ * Returns the full updated suggestions list (existing + any new/updated).
30
+ */
31
+ export declare function detectSuggestedThreads(currentSession: {
32
+ session_id: string;
33
+ title: string;
34
+ embedding: number[];
35
+ }, recentSessions: SessionEmbeddingRecord[], openThreadEmbeddings: ThreadWithEmbedding[], existingSuggestions: ThreadSuggestion[]): ThreadSuggestion[];
36
+ /**
37
+ * Promote a suggestion to an open thread.
38
+ * Mutates the suggestion in-place and returns it, or null if not found.
39
+ */
40
+ export declare function promoteSuggestionById(id: string, threadId: string, suggestions: ThreadSuggestion[]): ThreadSuggestion | null;
41
+ /**
42
+ * Dismiss a suggestion. Increments dismissed_count.
43
+ * Mutates the suggestion in-place and returns it, or null if not found.
44
+ */
45
+ export declare function dismissSuggestionById(id: string, suggestions: ThreadSuggestion[]): ThreadSuggestion | null;
46
+ /**
47
+ * Filter to only pending suggestions (not promoted/dismissed).
48
+ * Excludes suggestions dismissed 3+ times (permanently suppressed).
49
+ */
50
+ export declare function getPendingSuggestions(suggestions: ThreadSuggestion[]): ThreadSuggestion[];
51
+ /**
52
+ * Load suggestions from .gitmem/suggested-threads.json.
53
+ * Returns empty array if file doesn't exist or is corrupted.
54
+ */
55
+ export declare function loadSuggestions(): ThreadSuggestion[];
56
+ /**
57
+ * Save suggestions to .gitmem/suggested-threads.json.
58
+ */
59
+ export declare function saveSuggestions(suggestions: ThreadSuggestion[]): void;
60
+ /**
61
+ * Fetch recent session embeddings from Supabase for clustering comparison.
62
+ * Returns null if Supabase is unavailable.
63
+ */
64
+ export declare function loadRecentSessionEmbeddings(project?: Project, days?: number, limit?: number): Promise<SessionEmbeddingRecord[] | null>;
65
+ export declare function generateSuggestionId(): string;
66
+ //# sourceMappingURL=thread-suggestions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"thread-suggestions.d.ts","sourceRoot":"","sources":["../../src/services/thread-suggestions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AASH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAC7D,OAAO,KAAK,EAAE,gBAAgB,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAInE,eAAO,MAAM,4BAA4B,MAAO,CAAC;AACjD,eAAO,MAAM,sBAAsB,MAAO,CAAC;AAC3C,eAAO,MAAM,0BAA0B,MAAO,CAAC;AAC/C,eAAO,MAAM,qBAAqB,IAAI,CAAC;AAMvC,MAAM,WAAW,sBAAsB;IACrC,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,MAAM,EAAE,CAAC;CACrB;AAID;;;;;GAKG;AACH,wBAAgB,sBAAsB,CACpC,cAAc,EAAE;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,EAAE,CAAA;CAAE,EAC1E,cAAc,EAAE,sBAAsB,EAAE,EACxC,oBAAoB,EAAE,mBAAmB,EAAE,EAC3C,mBAAmB,EAAE,gBAAgB,EAAE,GACtC,gBAAgB,EAAE,CA4EpB;AAID;;;GAGG;AACH,wBAAgB,qBAAqB,CACnC,EAAE,EAAE,MAAM,EACV,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,gBAAgB,EAAE,GAC9B,gBAAgB,GAAG,IAAI,CAQzB;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CACnC,EAAE,EAAE,MAAM,EACV,WAAW,EAAE,gBAAgB,EAAE,GAC9B,gBAAgB,GAAG,IAAI,CAQzB;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CACnC,WAAW,EAAE,gBAAgB,EAAE,GAC9B,gBAAgB,EAAE,CAIpB;AAID;;;GAGG;AACH,wBAAgB,eAAe,IAAI,gBAAgB,EAAE,CAapD;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,WAAW,EAAE,gBAAgB,EAAE,GAAG,IAAI,CAcrE;AAsBD;;;GAGG;AACH,wBAAsB,2BAA2B,CAC/C,OAAO,GAAE,OAAyB,EAClC,IAAI,GAAE,MAAW,EACjB,KAAK,GAAE,MAAW,GACjB,OAAO,CAAC,sBAAsB,EAAE,GAAG,IAAI,CAAC,CAgD1C;AAID,wBAAgB,oBAAoB,IAAI,MAAM,CAE7C"}