gitmem-mcp 1.0.3 → 1.0.5

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 (293) hide show
  1. package/CHANGELOG.md +14 -14
  2. package/dist/commands/check.d.ts +1 -1
  3. package/dist/commands/check.js +4 -3
  4. package/dist/diagnostics/anonymizer.d.ts +1 -1
  5. package/dist/diagnostics/anonymizer.js +1 -1
  6. package/dist/diagnostics/channels.d.ts +1 -1
  7. package/dist/diagnostics/channels.js +1 -1
  8. package/dist/diagnostics/collector.d.ts +1 -1
  9. package/dist/diagnostics/collector.js +1 -1
  10. package/dist/diagnostics/index.d.ts +1 -1
  11. package/dist/diagnostics/index.js +1 -1
  12. package/dist/hooks/quick-retrieve.js +2 -1
  13. package/dist/index.js +0 -0
  14. package/dist/schemas/active-sessions.d.ts +9 -9
  15. package/dist/schemas/active-sessions.js +1 -1
  16. package/dist/schemas/common.d.ts +2 -5
  17. package/dist/schemas/common.js +13 -7
  18. package/dist/schemas/create-learning.js +1 -1
  19. package/dist/schemas/session-close.d.ts +5 -8
  20. package/dist/schemas/session-close.js +7 -3
  21. package/dist/schemas/session-start.d.ts +4 -4
  22. package/dist/schemas/session-start.js +1 -1
  23. package/dist/schemas/thread.d.ts +1 -1
  24. package/dist/schemas/thread.js +1 -1
  25. package/dist/server.d.ts +2 -2
  26. package/dist/server.js +21 -10
  27. package/dist/services/active-sessions.js +3 -2
  28. package/dist/services/agent-briefing.d.ts +1 -1
  29. package/dist/services/agent-briefing.js +1 -1
  30. package/dist/services/agent-detection.d.ts +1 -0
  31. package/dist/services/agent-detection.js +21 -12
  32. package/dist/services/analytics.d.ts +1 -1
  33. package/dist/services/analytics.js +3 -3
  34. package/dist/services/behavioral-decay.js +2 -2
  35. package/dist/services/cache.d.ts +1 -1
  36. package/dist/services/cache.js +1 -1
  37. package/dist/services/compliance-validator.d.ts +1 -1
  38. package/dist/services/compliance-validator.js +5 -5
  39. package/dist/services/config.d.ts +1 -1
  40. package/dist/services/config.js +1 -1
  41. package/dist/services/file-lock.js +12 -0
  42. package/dist/services/gitmem-dir.d.ts +17 -4
  43. package/dist/services/gitmem-dir.js +43 -9
  44. package/dist/services/local-file-storage.d.ts +1 -1
  45. package/dist/services/local-file-storage.js +4 -3
  46. package/dist/services/local-vector-search.d.ts +1 -1
  47. package/dist/services/local-vector-search.js +2 -2
  48. package/dist/services/metrics.d.ts +6 -6
  49. package/dist/services/metrics.js +8 -8
  50. package/dist/services/session-state.d.ts +7 -7
  51. package/dist/services/session-state.js +19 -7
  52. package/dist/services/startup.d.ts +1 -1
  53. package/dist/services/startup.js +3 -2
  54. package/dist/services/supabase-client.d.ts +17 -6
  55. package/dist/services/supabase-client.js +44 -8
  56. package/dist/services/thread-manager.d.ts +1 -1
  57. package/dist/services/thread-manager.js +5 -5
  58. package/dist/services/thread-supabase.d.ts +1 -1
  59. package/dist/services/thread-supabase.js +2 -2
  60. package/dist/services/transcript-chunker.d.ts +1 -1
  61. package/dist/services/transcript-chunker.js +1 -1
  62. package/dist/services/variant-assignment.d.ts +6 -6
  63. package/dist/services/variant-assignment.js +9 -8
  64. package/dist/tools/analyze.d.ts +2 -2
  65. package/dist/tools/analyze.js +2 -2
  66. package/dist/tools/archive-learning.js +36 -22
  67. package/dist/tools/confirm-scars.js +1 -1
  68. package/dist/tools/create-decision.d.ts +1 -1
  69. package/dist/tools/create-decision.js +2 -2
  70. package/dist/tools/create-learning.d.ts +1 -1
  71. package/dist/tools/create-learning.js +4 -4
  72. package/dist/tools/create-linear-issue.d.ts +18 -0
  73. package/dist/tools/create-linear-issue.js +197 -0
  74. package/dist/tools/create-thread.d.ts +1 -1
  75. package/dist/tools/create-thread.js +2 -2
  76. package/dist/tools/definitions.js +50 -48
  77. package/dist/tools/get-transcript.d.ts +1 -1
  78. package/dist/tools/get-transcript.js +1 -1
  79. package/dist/tools/graph-traverse.d.ts +1 -1
  80. package/dist/tools/graph-traverse.js +18 -16
  81. package/dist/tools/list-threads.d.ts +2 -2
  82. package/dist/tools/list-threads.js +4 -4
  83. package/dist/tools/log.d.ts +1 -1
  84. package/dist/tools/log.js +1 -1
  85. package/dist/tools/prepare-context.d.ts +1 -1
  86. package/dist/tools/prepare-context.js +1 -1
  87. package/dist/tools/recall.d.ts +4 -4
  88. package/dist/tools/recall.js +25 -25
  89. package/dist/tools/record-scar-usage-batch.js +2 -2
  90. package/dist/tools/record-scar-usage.d.ts +1 -1
  91. package/dist/tools/record-scar-usage.js +3 -3
  92. package/dist/tools/resolve-thread.d.ts +2 -2
  93. package/dist/tools/resolve-thread.js +3 -3
  94. package/dist/tools/save-transcript.d.ts +1 -1
  95. package/dist/tools/save-transcript.js +1 -1
  96. package/dist/tools/search.d.ts +1 -1
  97. package/dist/tools/search.js +1 -1
  98. package/dist/tools/session-close.d.ts +1 -1
  99. package/dist/tools/session-close.js +31 -31
  100. package/dist/tools/session-start.d.ts +5 -5
  101. package/dist/tools/session-start.js +63 -61
  102. package/dist/types/index.d.ts +13 -13
  103. package/hooks/.claude-plugin/plugin.json +1 -1
  104. package/hooks/scripts/post-tool-use.sh +1 -1
  105. package/hooks/scripts/recall-check.sh +1 -1
  106. package/hooks/scripts/session-close-check.sh +1 -1
  107. package/hooks/scripts/session-start.sh +1 -1
  108. package/package.json +6 -3
  109. package/dist/commands/check.d.ts.map +0 -1
  110. package/dist/commands/check.js.map +0 -1
  111. package/dist/constants/closing-questions.d.ts.map +0 -1
  112. package/dist/constants/closing-questions.js.map +0 -1
  113. package/dist/diagnostics/anonymizer.d.ts.map +0 -1
  114. package/dist/diagnostics/anonymizer.js.map +0 -1
  115. package/dist/diagnostics/channels.d.ts.map +0 -1
  116. package/dist/diagnostics/channels.js.map +0 -1
  117. package/dist/diagnostics/collector.d.ts.map +0 -1
  118. package/dist/diagnostics/collector.js.map +0 -1
  119. package/dist/diagnostics/index.d.ts.map +0 -1
  120. package/dist/diagnostics/index.js.map +0 -1
  121. package/dist/hooks/format-utils.d.ts.map +0 -1
  122. package/dist/hooks/format-utils.js.map +0 -1
  123. package/dist/hooks/quick-retrieve.d.ts.map +0 -1
  124. package/dist/hooks/quick-retrieve.js.map +0 -1
  125. package/dist/index.d.ts.map +0 -1
  126. package/dist/index.js.map +0 -1
  127. package/dist/schemas/absorb-observations.d.ts.map +0 -1
  128. package/dist/schemas/absorb-observations.js.map +0 -1
  129. package/dist/schemas/active-sessions.d.ts.map +0 -1
  130. package/dist/schemas/active-sessions.js.map +0 -1
  131. package/dist/schemas/analyze.d.ts.map +0 -1
  132. package/dist/schemas/analyze.js.map +0 -1
  133. package/dist/schemas/common.d.ts.map +0 -1
  134. package/dist/schemas/common.js.map +0 -1
  135. package/dist/schemas/create-decision.d.ts.map +0 -1
  136. package/dist/schemas/create-decision.js.map +0 -1
  137. package/dist/schemas/create-learning.d.ts.map +0 -1
  138. package/dist/schemas/create-learning.js.map +0 -1
  139. package/dist/schemas/get-transcript.d.ts.map +0 -1
  140. package/dist/schemas/get-transcript.js.map +0 -1
  141. package/dist/schemas/index.d.ts.map +0 -1
  142. package/dist/schemas/index.js.map +0 -1
  143. package/dist/schemas/log.d.ts.map +0 -1
  144. package/dist/schemas/log.js.map +0 -1
  145. package/dist/schemas/prepare-context.d.ts.map +0 -1
  146. package/dist/schemas/prepare-context.js.map +0 -1
  147. package/dist/schemas/recall.d.ts.map +0 -1
  148. package/dist/schemas/recall.js.map +0 -1
  149. package/dist/schemas/record-scar-usage-batch.d.ts.map +0 -1
  150. package/dist/schemas/record-scar-usage-batch.js.map +0 -1
  151. package/dist/schemas/record-scar-usage.d.ts.map +0 -1
  152. package/dist/schemas/record-scar-usage.js.map +0 -1
  153. package/dist/schemas/registry.d.ts.map +0 -1
  154. package/dist/schemas/registry.js.map +0 -1
  155. package/dist/schemas/save-transcript.d.ts.map +0 -1
  156. package/dist/schemas/save-transcript.js.map +0 -1
  157. package/dist/schemas/search-transcripts.d.ts.map +0 -1
  158. package/dist/schemas/search-transcripts.js.map +0 -1
  159. package/dist/schemas/search.d.ts.map +0 -1
  160. package/dist/schemas/search.js.map +0 -1
  161. package/dist/schemas/session-close.d.ts.map +0 -1
  162. package/dist/schemas/session-close.js.map +0 -1
  163. package/dist/schemas/session-start.d.ts.map +0 -1
  164. package/dist/schemas/session-start.js.map +0 -1
  165. package/dist/schemas/thread.d.ts.map +0 -1
  166. package/dist/schemas/thread.js.map +0 -1
  167. package/dist/server.d.ts.map +0 -1
  168. package/dist/server.js.map +0 -1
  169. package/dist/services/active-sessions.d.ts.map +0 -1
  170. package/dist/services/active-sessions.js.map +0 -1
  171. package/dist/services/agent-briefing.d.ts.map +0 -1
  172. package/dist/services/agent-briefing.js.map +0 -1
  173. package/dist/services/agent-detection.d.ts.map +0 -1
  174. package/dist/services/agent-detection.js.map +0 -1
  175. package/dist/services/analytics.d.ts.map +0 -1
  176. package/dist/services/analytics.js.map +0 -1
  177. package/dist/services/behavioral-decay.d.ts.map +0 -1
  178. package/dist/services/behavioral-decay.js.map +0 -1
  179. package/dist/services/bm25.d.ts.map +0 -1
  180. package/dist/services/bm25.js.map +0 -1
  181. package/dist/services/cache.d.ts.map +0 -1
  182. package/dist/services/cache.js.map +0 -1
  183. package/dist/services/cache.test.d.ts +0 -8
  184. package/dist/services/cache.test.d.ts.map +0 -1
  185. package/dist/services/cache.test.js +0 -267
  186. package/dist/services/cache.test.js.map +0 -1
  187. package/dist/services/compliance-validator.d.ts.map +0 -1
  188. package/dist/services/compliance-validator.js.map +0 -1
  189. package/dist/services/config.d.ts.map +0 -1
  190. package/dist/services/config.js.map +0 -1
  191. package/dist/services/display-protocol.d.ts.map +0 -1
  192. package/dist/services/display-protocol.js.map +0 -1
  193. package/dist/services/effect-tracker.d.ts.map +0 -1
  194. package/dist/services/effect-tracker.js.map +0 -1
  195. package/dist/services/embedding.d.ts.map +0 -1
  196. package/dist/services/embedding.js.map +0 -1
  197. package/dist/services/file-lock.d.ts.map +0 -1
  198. package/dist/services/file-lock.js.map +0 -1
  199. package/dist/services/gitmem-dir.d.ts.map +0 -1
  200. package/dist/services/gitmem-dir.js.map +0 -1
  201. package/dist/services/local-file-storage.d.ts.map +0 -1
  202. package/dist/services/local-file-storage.js.map +0 -1
  203. package/dist/services/local-vector-search.d.ts.map +0 -1
  204. package/dist/services/local-vector-search.js.map +0 -1
  205. package/dist/services/metrics.d.ts.map +0 -1
  206. package/dist/services/metrics.js.map +0 -1
  207. package/dist/services/session-state.d.ts.map +0 -1
  208. package/dist/services/session-state.js.map +0 -1
  209. package/dist/services/startup.d.ts.map +0 -1
  210. package/dist/services/startup.js.map +0 -1
  211. package/dist/services/storage.d.ts.map +0 -1
  212. package/dist/services/storage.js.map +0 -1
  213. package/dist/services/supabase-client.d.ts.map +0 -1
  214. package/dist/services/supabase-client.js.map +0 -1
  215. package/dist/services/thread-dedup.d.ts.map +0 -1
  216. package/dist/services/thread-dedup.js.map +0 -1
  217. package/dist/services/thread-manager.d.ts.map +0 -1
  218. package/dist/services/thread-manager.js.map +0 -1
  219. package/dist/services/thread-suggestions.d.ts.map +0 -1
  220. package/dist/services/thread-suggestions.js.map +0 -1
  221. package/dist/services/thread-supabase.d.ts.map +0 -1
  222. package/dist/services/thread-supabase.js.map +0 -1
  223. package/dist/services/thread-vitality.d.ts.map +0 -1
  224. package/dist/services/thread-vitality.js.map +0 -1
  225. package/dist/services/tier.d.ts.map +0 -1
  226. package/dist/services/tier.js.map +0 -1
  227. package/dist/services/timezone.d.ts.map +0 -1
  228. package/dist/services/timezone.js.map +0 -1
  229. package/dist/services/transcript-chunker.d.ts.map +0 -1
  230. package/dist/services/transcript-chunker.js.map +0 -1
  231. package/dist/services/triple-writer.d.ts.map +0 -1
  232. package/dist/services/triple-writer.js.map +0 -1
  233. package/dist/services/variant-assignment.d.ts.map +0 -1
  234. package/dist/services/variant-assignment.js.map +0 -1
  235. package/dist/services/variant-generation.d.ts.map +0 -1
  236. package/dist/services/variant-generation.js.map +0 -1
  237. package/dist/tools/absorb-observations.d.ts.map +0 -1
  238. package/dist/tools/absorb-observations.js.map +0 -1
  239. package/dist/tools/analyze.d.ts.map +0 -1
  240. package/dist/tools/analyze.js.map +0 -1
  241. package/dist/tools/archive-learning.d.ts.map +0 -1
  242. package/dist/tools/archive-learning.js.map +0 -1
  243. package/dist/tools/cleanup-threads.d.ts.map +0 -1
  244. package/dist/tools/cleanup-threads.js.map +0 -1
  245. package/dist/tools/confirm-scars.d.ts.map +0 -1
  246. package/dist/tools/confirm-scars.js.map +0 -1
  247. package/dist/tools/create-decision.d.ts.map +0 -1
  248. package/dist/tools/create-decision.js.map +0 -1
  249. package/dist/tools/create-learning.d.ts.map +0 -1
  250. package/dist/tools/create-learning.js.map +0 -1
  251. package/dist/tools/create-thread.d.ts.map +0 -1
  252. package/dist/tools/create-thread.js.map +0 -1
  253. package/dist/tools/definitions.d.ts.map +0 -1
  254. package/dist/tools/definitions.js.map +0 -1
  255. package/dist/tools/dismiss-suggestion.d.ts.map +0 -1
  256. package/dist/tools/dismiss-suggestion.js.map +0 -1
  257. package/dist/tools/get-transcript.d.ts.map +0 -1
  258. package/dist/tools/get-transcript.js.map +0 -1
  259. package/dist/tools/graph-traverse.d.ts.map +0 -1
  260. package/dist/tools/graph-traverse.js.map +0 -1
  261. package/dist/tools/list-threads.d.ts.map +0 -1
  262. package/dist/tools/list-threads.js.map +0 -1
  263. package/dist/tools/log.d.ts.map +0 -1
  264. package/dist/tools/log.js.map +0 -1
  265. package/dist/tools/prepare-context.d.ts.map +0 -1
  266. package/dist/tools/prepare-context.js.map +0 -1
  267. package/dist/tools/promote-suggestion.d.ts.map +0 -1
  268. package/dist/tools/promote-suggestion.js.map +0 -1
  269. package/dist/tools/recall.d.ts.map +0 -1
  270. package/dist/tools/recall.js.map +0 -1
  271. package/dist/tools/recall.test.d.ts +0 -5
  272. package/dist/tools/recall.test.d.ts.map +0 -1
  273. package/dist/tools/recall.test.js +0 -155
  274. package/dist/tools/recall.test.js.map +0 -1
  275. package/dist/tools/record-scar-usage-batch.d.ts.map +0 -1
  276. package/dist/tools/record-scar-usage-batch.js.map +0 -1
  277. package/dist/tools/record-scar-usage.d.ts.map +0 -1
  278. package/dist/tools/record-scar-usage.js.map +0 -1
  279. package/dist/tools/resolve-thread.d.ts.map +0 -1
  280. package/dist/tools/resolve-thread.js.map +0 -1
  281. package/dist/tools/save-transcript.d.ts.map +0 -1
  282. package/dist/tools/save-transcript.js.map +0 -1
  283. package/dist/tools/search-transcripts.d.ts.map +0 -1
  284. package/dist/tools/search-transcripts.js.map +0 -1
  285. package/dist/tools/search.d.ts.map +0 -1
  286. package/dist/tools/search.js.map +0 -1
  287. package/dist/tools/session-close.d.ts.map +0 -1
  288. package/dist/tools/session-close.js.map +0 -1
  289. package/dist/tools/session-start.d.ts.map +0 -1
  290. package/dist/tools/session-start.js.map +0 -1
  291. package/dist/types/index.d.ts.map +0 -1
  292. package/dist/types/index.js.map +0 -1
  293. package/hooks/tests/test-hooks.sh +0 -577
@@ -48,33 +48,27 @@ export function detectAgent() {
48
48
  let agent;
49
49
  if (entrypoint === "cli") {
50
50
  if (docker) {
51
- // CLI in Docker container
52
- agent = "CLI";
51
+ agent = "cli";
53
52
  }
54
53
  else if (process.env.GITMEM_AGENT_HOSTNAME && (hostname === process.env.GITMEM_AGENT_HOSTNAME)) {
55
- // CLI on configured server = CODA-1
56
- agent = "CODA-1";
54
+ agent = "autonomous";
57
55
  }
58
56
  else {
59
- // CLI elsewhere (fallback)
60
- agent = "CLI";
57
+ agent = "cli";
61
58
  }
62
59
  }
63
60
  else if (entrypoint === "claude-desktop") {
64
- // Desktop app code tab
65
- agent = "DAC";
61
+ agent = "desktop";
66
62
  }
67
63
  else if (!entrypoint) {
68
- // No entrypoint - could be Brain Local or Brain Cloud
69
64
  if (hasFilesystemAccess()) {
70
- agent = "Brain_Local";
65
+ agent = "local";
71
66
  }
72
67
  else {
73
- agent = "Brain_Cloud";
68
+ agent = "cloud";
74
69
  }
75
70
  }
76
71
  else {
77
- // Unknown entrypoint
78
72
  agent = "Unknown";
79
73
  }
80
74
  return {
@@ -84,6 +78,21 @@ export function detectAgent() {
84
78
  agent,
85
79
  };
86
80
  }
81
+ /**
82
+ * Normalize legacy agent names to new generic names.
83
+ * Accepts both old (CLI, DAC, CODA-1, Brain_Local, Brain_Cloud)
84
+ * and new (cli, desktop, autonomous, local, cloud) formats.
85
+ */
86
+ const LEGACY_MAP = {
87
+ "CLI": "cli",
88
+ "DAC": "desktop",
89
+ "CODA-1": "autonomous",
90
+ "Brain_Local": "local",
91
+ "Brain_Cloud": "cloud",
92
+ };
93
+ export function normalizeAgent(input) {
94
+ return LEGACY_MAP[input] || input;
95
+ }
87
96
  /**
88
97
  * Get just the agent identity (convenience function)
89
98
  */
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Analytics Service (OD-567)
2
+ * Analytics Service
3
3
  *
4
4
  * Shared analytics engine for session insights. Powers both the
5
5
  * gitmem-analyze MCP tool (CLI) and the GitMem Console dashboard.
@@ -1,12 +1,12 @@
1
1
  /**
2
- * Analytics Service (OD-567)
2
+ * Analytics Service
3
3
  *
4
4
  * Shared analytics engine for session insights. Powers both the
5
5
  * gitmem-analyze MCP tool (CLI) and the GitMem Console dashboard.
6
6
  *
7
7
  * Uses directQuery for raw Supabase REST access (no ww-mcp dependency).
8
8
  */
9
- import { directQuery, directQueryAll } from "./supabase-client.js";
9
+ import { directQuery, directQueryAll, safeInFilter } from "./supabase-client.js";
10
10
  import { getCache } from "./cache.js";
11
11
  // --- Query Layer ---
12
12
  /**
@@ -97,7 +97,7 @@ export async function enrichScarUsageTitles(usages) {
97
97
  const learnings = await directQuery("orchestra_learnings", {
98
98
  select: "id,title,severity",
99
99
  filters: {
100
- id: `in.(${ids.join(",")})`,
100
+ id: safeInFilter(ids),
101
101
  },
102
102
  });
103
103
  // Build lookup map
@@ -10,7 +10,7 @@
10
10
  * 2. fetchDismissalCounts() — queries scar_usage for inline archival hints
11
11
  * (called from recall to annotate frequently-dismissed scars)
12
12
  */
13
- import { isConfigured } from "./supabase-client.js";
13
+ import { isConfigured, safeInFilter } from "./supabase-client.js";
14
14
  const SUPABASE_URL = process.env.SUPABASE_URL || "";
15
15
  const SUPABASE_KEY = process.env.SUPABASE_SERVICE_ROLE_KEY || process.env.SUPABASE_KEY || "";
16
16
  const SUPABASE_REST_URL = SUPABASE_URL ? `${SUPABASE_URL}/rest/v1` : "";
@@ -76,7 +76,7 @@ export async function fetchDismissalCounts(scarIds) {
76
76
  // Query scar_usage for the given scar IDs (last 90 days)
77
77
  const url = new URL(`${SUPABASE_REST_URL}/scar_usage`);
78
78
  url.searchParams.set("select", "scar_id,reference_type");
79
- url.searchParams.set("scar_id", `in.(${scarIds.join(",")})`);
79
+ url.searchParams.set("scar_id", safeInFilter(scarIds));
80
80
  url.searchParams.set("surfaced_at", `gte.${new Date(Date.now() - 90 * 86400000).toISOString()}`);
81
81
  url.searchParams.set("limit", "1000");
82
82
  const response = await fetch(url.toString(), {
@@ -5,7 +5,7 @@
5
5
  * Caches search results to avoid repeated ww-mcp calls.
6
6
  *
7
7
  * Design: docs/systems/gitmem-caching.md
8
- * Issue: OD-473
8
+ *
9
9
  */
10
10
  declare const TTL: {
11
11
  readonly SCAR_SEARCH: number;
@@ -5,7 +5,7 @@
5
5
  * Caches search results to avoid repeated ww-mcp calls.
6
6
  *
7
7
  * Design: docs/systems/gitmem-caching.md
8
- * Issue: OD-473
8
+ *
9
9
  */
10
10
  import { createHash } from "crypto";
11
11
  import { existsSync, mkdirSync, readFileSync, writeFileSync, readdirSync, unlinkSync, statSync } from "fs";
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * Validates session close compliance based on close type.
5
5
  *
6
- * Standard close requires (OD-491):
6
+ * Standard close requires:
7
7
  * - task_completion object with timestamps proving each step was done
8
8
  * - All 6 closing questions answered
9
9
  * - human_corrections field present (even if empty string)
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * Validates session close compliance based on close type.
5
5
  *
6
- * Standard close requires (OD-491):
6
+ * Standard close requires:
7
7
  * - task_completion object with timestamps proving each step was done
8
8
  * - All 6 closing questions answered
9
9
  * - human_corrections field present (even if empty string)
@@ -17,7 +17,7 @@ import { CLOSING_QUESTIONS } from "../constants/closing-questions.js";
17
17
  /** Minimum time (ms) between asking human and receiving response */
18
18
  const MIN_HUMAN_RESPONSE_GAP_MS = 3000;
19
19
  /**
20
- * Validate task_completion timestamps (OD-491)
20
+ * Validate task_completion timestamps
21
21
  *
22
22
  * Ensures:
23
23
  * 1. All timestamps are valid ISO strings
@@ -83,14 +83,14 @@ function validateTaskCompletion(tc) {
83
83
  /**
84
84
  * Validate standard close parameters
85
85
  *
86
- * OD-491: Now requires task_completion with timestamps proving each step was done.
86
+ * Now requires task_completion with timestamps proving each step was done.
87
87
  */
88
88
  function validateStandardClose(params) {
89
89
  const errors = [];
90
90
  const warnings = [];
91
- // OD-491: task_completion is REQUIRED for standard close
91
+ // task_completion is REQUIRED for standard close
92
92
  if (!params.task_completion) {
93
- errors.push("Standard close requires task_completion object (OD-491). " +
93
+ errors.push("Standard close requires task_completion object. " +
94
94
  "You must complete each step: display questions → answer → ask human → wait for response");
95
95
  }
96
96
  else {
@@ -10,7 +10,7 @@
10
10
  * - GITMEM_STALE_CHECK: "true" | "false" - Check for stale data (default: "true")
11
11
  * - SUPABASE_URL: Supabase project URL
12
12
  *
13
- * Issue: OD-473
13
+ *
14
14
  */
15
15
  import type { GitMemTier } from "./tier.js";
16
16
  export type SearchMode = "local" | "remote" | "auto";
@@ -10,7 +10,7 @@
10
10
  * - GITMEM_STALE_CHECK: "true" | "false" - Check for stale data (default: "true")
11
11
  * - SUPABASE_URL: Supabase project URL
12
12
  *
13
- * Issue: OD-473
13
+ *
14
14
  */
15
15
  import { getTier, hasSupabase, getTablePrefix } from "./tier.js";
16
16
  // Private IP patterns for on-prem detection
@@ -66,6 +66,18 @@ export function acquireLockSync(lockPath, timeoutMs = DEFAULT_TIMEOUT_MS, retryM
66
66
  if (error.code !== "EEXIST") {
67
67
  throw new Error(`[file-lock] Failed to create lock file ${lockPath}: ${error.message}`);
68
68
  }
69
+ // Lock file exists — check if we already hold it (reentrance detection)
70
+ try {
71
+ const raw = fs.readFileSync(lockPath, "utf-8");
72
+ const holder = JSON.parse(raw);
73
+ if (holder.pid === process.pid && holder.hostname === os.hostname()) {
74
+ // Same process already holds the lock — reentrant call, allow through
75
+ return;
76
+ }
77
+ }
78
+ catch {
79
+ // Can't read lock — fall through to stale check
80
+ }
69
81
  // Lock file exists — check if stale
70
82
  if (isLockStale(lockPath)) {
71
83
  try {
@@ -2,20 +2,33 @@
2
2
  * Resolved .gitmem directory path
3
3
  *
4
4
  * Solves: process.cwd() changes when agents cd into other repos (e.g., /workspace/gitmem),
5
- * but .gitmem/ was created in the project root (e.g., /workspace/orchestra/).
5
+ * but .gitmem/ was created in the project root.
6
6
  * The MCP server is long-running, so we resolve the path once and cache it.
7
7
  *
8
8
  * Resolution order:
9
- * 1. Cached path from session_start (most reliable — session_start created the directory)
10
- * 2. Walk up from process.cwd() looking for existing .gitmem/ sentinels
11
- * 3. Fall back to process.cwd()/.gitmem (original behavior)
9
+ * 1. GITMEM_DIR env var (explicit override)
10
+ * 2. Cached path from session_start (most reliable session_start created the directory)
11
+ * 3. Walk up from process.cwd() looking for existing .gitmem/ sentinels (backward compat)
12
+ * 4. Fall back to ~/.gitmem (developer-scoped, survives across projects/containers)
12
13
  */
14
+ /**
15
+ * Validate a string intended for use as a single path component (directory name or filename).
16
+ * Rejects path traversal sequences, directory separators, and null bytes.
17
+ * Throws on invalid input — callers should validate before reaching this layer.
18
+ */
19
+ export declare function sanitizePathComponent(value: string, label: string): string;
13
20
  /**
14
21
  * Set the .gitmem directory path (called by session_start after creating it)
15
22
  */
16
23
  export declare function setGitmemDir(dir: string): void;
17
24
  /**
18
25
  * Get the resolved .gitmem directory path
26
+ *
27
+ * Resolution order:
28
+ * 1. GITMEM_DIR env var (explicit override)
29
+ * 2. Cached path from session_start (most reliable)
30
+ * 3. Walk up from CWD looking for existing .gitmem/ sentinels (backward compat)
31
+ * 4. Fall back to ~/.gitmem (developer-scoped, survives across projects/containers)
19
32
  */
20
33
  export declare function getGitmemDir(): string;
21
34
  /**
@@ -2,16 +2,32 @@
2
2
  * Resolved .gitmem directory path
3
3
  *
4
4
  * Solves: process.cwd() changes when agents cd into other repos (e.g., /workspace/gitmem),
5
- * but .gitmem/ was created in the project root (e.g., /workspace/orchestra/).
5
+ * but .gitmem/ was created in the project root.
6
6
  * The MCP server is long-running, so we resolve the path once and cache it.
7
7
  *
8
8
  * Resolution order:
9
- * 1. Cached path from session_start (most reliable — session_start created the directory)
10
- * 2. Walk up from process.cwd() looking for existing .gitmem/ sentinels
11
- * 3. Fall back to process.cwd()/.gitmem (original behavior)
9
+ * 1. GITMEM_DIR env var (explicit override)
10
+ * 2. Cached path from session_start (most reliable session_start created the directory)
11
+ * 3. Walk up from process.cwd() looking for existing .gitmem/ sentinels (backward compat)
12
+ * 4. Fall back to ~/.gitmem (developer-scoped, survives across projects/containers)
12
13
  */
13
14
  import * as path from "path";
14
15
  import * as fs from "fs";
16
+ import * as os from "os";
17
+ /**
18
+ * Validate a string intended for use as a single path component (directory name or filename).
19
+ * Rejects path traversal sequences, directory separators, and null bytes.
20
+ * Throws on invalid input — callers should validate before reaching this layer.
21
+ */
22
+ export function sanitizePathComponent(value, label) {
23
+ if (!value || typeof value !== "string") {
24
+ throw new Error(`${label} must be a non-empty string`);
25
+ }
26
+ if (value.includes("..") || value.includes("/") || value.includes("\\") || value.includes("\0")) {
27
+ throw new Error(`${label} contains invalid characters (path traversal rejected)`);
28
+ }
29
+ return value;
30
+ }
15
31
  let cachedGitmemDir = null;
16
32
  /**
17
33
  * Set the .gitmem directory path (called by session_start after creating it)
@@ -22,13 +38,29 @@ export function setGitmemDir(dir) {
22
38
  }
23
39
  /**
24
40
  * Get the resolved .gitmem directory path
41
+ *
42
+ * Resolution order:
43
+ * 1. GITMEM_DIR env var (explicit override)
44
+ * 2. Cached path from session_start (most reliable)
45
+ * 3. Walk up from CWD looking for existing .gitmem/ sentinels (backward compat)
46
+ * 4. Fall back to ~/.gitmem (developer-scoped, survives across projects/containers)
25
47
  */
26
48
  export function getGitmemDir() {
27
- // 1. Use cached path from session_start
49
+ // 1. GITMEM_DIR env var (explicit override, highest priority)
50
+ const envDir = process.env.GITMEM_DIR;
51
+ if (envDir) {
52
+ if (!cachedGitmemDir || cachedGitmemDir !== envDir) {
53
+ cachedGitmemDir = envDir;
54
+ console.error(`[gitmem-dir] Using GITMEM_DIR env var: ${envDir}`);
55
+ }
56
+ return envDir;
57
+ }
58
+ // 2. Use cached path from session_start
28
59
  if (cachedGitmemDir && fs.existsSync(cachedGitmemDir)) {
29
60
  return cachedGitmemDir;
30
61
  }
31
- // 2. Walk up from CWD looking for existing .gitmem directory
62
+ // 3. Walk up from CWD looking for existing .gitmem directory
63
+ // Backward compat: finds project-scoped .gitmem/ from older installations.
32
64
  // Sentinel files checked in priority order:
33
65
  // - active-sessions.json (multi-session registry, GIT-19)
34
66
  // - config.json (project-level gitmem config)
@@ -46,9 +78,9 @@ export function getGitmemDir() {
46
78
  }
47
79
  dir = path.dirname(dir);
48
80
  }
49
- // 3. Fall back to CWD (original behavior)
50
- const fallback = path.join(process.cwd(), ".gitmem");
51
- console.error(`[gitmem-dir] Falling back to CWD: ${fallback}`);
81
+ // 4. Fall back to ~/.gitmem (developer-scoped — survives across projects and containers)
82
+ const fallback = path.join(os.homedir(), ".gitmem");
83
+ console.error(`[gitmem-dir] Falling back to home dir: ${fallback}`);
52
84
  return fallback;
53
85
  }
54
86
  /**
@@ -62,6 +94,7 @@ export function getGitmemPath(filename) {
62
94
  * Creates the directory if it doesn't exist.
63
95
  */
64
96
  export function getSessionDir(sessionId) {
97
+ sanitizePathComponent(sessionId, "sessionId");
65
98
  const sessionsDir = path.join(getGitmemDir(), "sessions", sessionId);
66
99
  if (!fs.existsSync(sessionsDir)) {
67
100
  fs.mkdirSync(sessionsDir, { recursive: true });
@@ -73,6 +106,7 @@ export function getSessionDir(sessionId) {
73
106
  * Get a file path within a per-session directory.
74
107
  */
75
108
  export function getSessionPath(sessionId, filename) {
109
+ sanitizePathComponent(filename, "filename");
76
110
  return path.join(getSessionDir(sessionId), filename);
77
111
  }
78
112
  /**
@@ -2,7 +2,7 @@
2
2
  * Local File Storage — Free Tier Backend
3
3
  *
4
4
  * Stores scars, sessions, decisions, and scar usage as JSON files
5
- * in the .gitmem/ directory of the current project.
5
+ * in the .gitmem/ directory (defaults to ~/.gitmem, overridable via GITMEM_DIR).
6
6
  *
7
7
  * Provides keyword-based search (no embeddings needed).
8
8
  */
@@ -2,19 +2,20 @@
2
2
  * Local File Storage — Free Tier Backend
3
3
  *
4
4
  * Stores scars, sessions, decisions, and scar usage as JSON files
5
- * in the .gitmem/ directory of the current project.
5
+ * in the .gitmem/ directory (defaults to ~/.gitmem, overridable via GITMEM_DIR).
6
6
  *
7
7
  * Provides keyword-based search (no embeddings needed).
8
8
  */
9
9
  import * as fs from "fs";
10
10
  import * as path from "path";
11
11
  import { bm25Search } from "./bm25.js";
12
+ import { getGitmemDir } from "./gitmem-dir.js";
12
13
  const MAX_FILE_SIZE = 10 * 1024 * 1024; // 10MB
13
14
  const WARN_FILE_SIZE = 1 * 1024 * 1024; // 1MB
14
15
  export class LocalFileStorage {
15
16
  basePath;
16
17
  constructor(basePath) {
17
- this.basePath = basePath || path.join(process.cwd(), ".gitmem");
18
+ this.basePath = basePath || getGitmemDir();
18
19
  this.ensureDir();
19
20
  }
20
21
  ensureDir() {
@@ -142,7 +143,7 @@ export class LocalFileStorage {
142
143
  const l = byId.get(r.id);
143
144
  if (!l)
144
145
  continue;
145
- // OD-684: Deprioritize starter scars (0.7x multiplier)
146
+ // Deprioritize starter scars (0.7x multiplier)
146
147
  const isStarter = !!l.is_starter;
147
148
  const adjustedSimilarity = isStarter ? r.similarity * 0.7 : r.similarity;
148
149
  mapped.push({
@@ -11,7 +11,7 @@
11
11
  * - Deterministic results (same model + same data = same results)
12
12
  * - Per-container consistency (each loads same data)
13
13
  *
14
- * Issue: OD-473 (cache consistency)
14
+ * Cache consistency
15
15
  */
16
16
  import type { Project, RelevantScar } from "../types/index.js";
17
17
  interface ScarRecord {
@@ -11,7 +11,7 @@
11
11
  * - Deterministic results (same model + same data = same results)
12
12
  * - Per-container consistency (each loads same data)
13
13
  *
14
- * Issue: OD-473 (cache consistency)
14
+ * Cache consistency
15
15
  */
16
16
  import { embed as generateEmbedding, getEmbeddingDim, isEmbeddingAvailable } from "./embedding.js";
17
17
  // Embedding dimension — read from provider config at runtime
@@ -184,7 +184,7 @@ export class LocalVectorSearch {
184
184
  severity: scar.severity || "medium",
185
185
  counter_arguments: scar.counter_arguments || [],
186
186
  similarity: Math.round(similarity * 1000) / 1000, // 3 decimal places
187
- // OD-508: Include enriched fields for LLM-cooperative enforcement
187
+ // Include enriched fields for LLM-cooperative enforcement
188
188
  why_this_matters: scar.why_this_matters,
189
189
  action_protocol: scar.action_protocol,
190
190
  self_check_criteria: scar.self_check_criteria,
@@ -2,7 +2,7 @@
2
2
  * GitMem Performance Metrics Service
3
3
  *
4
4
  * Tracks latency, result counts, and relevance signals for all GitMem tools.
5
- * Implements OD-429 instrumentation layer.
5
+ * Instrumentation layer for performance tracking.
6
6
  */
7
7
  /**
8
8
  * Tool names that can be tracked
@@ -15,7 +15,7 @@ export type PhaseTag = "session_start" | "session_refresh" | "session_close" | "
15
15
  /**
16
16
  * Agent identities
17
17
  */
18
- export type AgentIdentity = "CLI" | "DAC" | "CODA-1" | "Brain_Local" | "Brain_Cloud";
18
+ export type AgentIdentity = "cli" | "desktop" | "autonomous" | "local" | "cloud";
19
19
  /**
20
20
  * Metrics data for a query
21
21
  */
@@ -36,7 +36,7 @@ export interface QueryMetrics {
36
36
  metadata?: Record<string, unknown>;
37
37
  }
38
38
  /**
39
- * Performance targets from OD-429
39
+ * Performance targets
40
40
  */
41
41
  export declare const PERFORMANCE_TARGETS: Record<ToolName, number>;
42
42
  /**
@@ -58,16 +58,16 @@ export declare class Timer {
58
58
  export declare function recordMetrics(metrics: QueryMetrics): Promise<void>;
59
59
  /**
60
60
  * Re-export performance types from types/index.ts
61
- * OD-489: Enhanced instrumentation for test harness validation
61
+ * Enhanced instrumentation for test harness validation
62
62
  */
63
63
  export type { PerformanceData, PerformanceBreakdown, ComponentPerformance, DataSource, CacheStatus, } from "../types/index.js";
64
64
  import type { PerformanceData, PerformanceBreakdown, ComponentPerformance, DataSource, CacheStatus } from "../types/index.js";
65
65
  /**
66
- * Build component performance data (OD-489)
66
+ * Build component performance data
67
67
  */
68
68
  export declare function buildComponentPerformance(latencyMs: number, source: DataSource, networkCall: boolean, cacheStatus?: CacheStatus): ComponentPerformance;
69
69
  /**
70
- * Count network calls from breakdown (OD-489)
70
+ * Count network calls from breakdown
71
71
  */
72
72
  export declare function countNetworkCalls(breakdown?: PerformanceBreakdown): number;
73
73
  export declare function buildPerformanceData(toolName: ToolName, latencyMs: number, resultCount: number, options?: {
@@ -2,22 +2,22 @@
2
2
  * GitMem Performance Metrics Service
3
3
  *
4
4
  * Tracks latency, result counts, and relevance signals for all GitMem tools.
5
- * Implements OD-429 instrumentation layer.
5
+ * Instrumentation layer for performance tracking.
6
6
  */
7
7
  import { v4 as uuidv4 } from "uuid";
8
8
  import * as supabase from "./supabase-client.js";
9
9
  import { getEffectTracker } from "./effect-tracker.js";
10
10
  import { hasSupabase } from "./tier.js";
11
11
  /**
12
- * Performance targets from OD-429
12
+ * Performance targets
13
13
  */
14
14
  export const PERFORMANCE_TARGETS = {
15
15
  recall: 2000,
16
16
  search: 500,
17
17
  log: 500,
18
- session_start: 750, // OD-645: Lean start (was 1500)
19
- session_refresh: 750, // OD-645: Lean refresh (was 1500)
20
- session_close: 1500, // OD-645: Tightened (was 3000)
18
+ session_start: 750, // Lean start (was 1500)
19
+ session_refresh: 750, // Lean refresh (was 1500)
20
+ session_close: 1500, // Tightened (was 3000)
21
21
  create_learning: 3000,
22
22
  create_decision: 3000,
23
23
  record_scar_usage: 1000,
@@ -84,7 +84,7 @@ export async function recordMetrics(metrics) {
84
84
  await tracker.track("metrics", metrics.tool_name, () => supabase.directUpsert("gitmem_query_metrics", record));
85
85
  }
86
86
  /**
87
- * Build component performance data (OD-489)
87
+ * Build component performance data
88
88
  */
89
89
  export function buildComponentPerformance(latencyMs, source, networkCall, cacheStatus = networkCall ? "miss" : "hit") {
90
90
  return {
@@ -95,7 +95,7 @@ export function buildComponentPerformance(latencyMs, source, networkCall, cacheS
95
95
  };
96
96
  }
97
97
  /**
98
- * Count network calls from breakdown (OD-489)
98
+ * Count network calls from breakdown
99
99
  */
100
100
  export function countNetworkCalls(breakdown) {
101
101
  if (!breakdown)
@@ -136,7 +136,7 @@ export function buildPerformanceData(toolName, latencyMs, resultCount, options)
136
136
  cache_hit: options?.cache_hit ?? fullyLocal,
137
137
  cache_age_ms: options?.cache_age_ms,
138
138
  search_mode: options?.search_mode,
139
- // OD-489: Detailed instrumentation for test harness
139
+ // Detailed instrumentation for test harness
140
140
  total_latency_ms: latencyMs,
141
141
  network_calls_made: networkCallsMade,
142
142
  fully_local: fullyLocal,
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Session State Management
3
- * OD-547: Track current session context for auto-injecting into recall calls
4
- * OD-552: Track surfaced scars for auto-bridging Q6 answers to scar_usage records
3
+ * Track current session context for auto-injecting into recall calls
4
+ * Track surfaced scars for auto-bridging Q6 answers to scar_usage records
5
5
  *
6
6
  * Maintains in-memory state of the current active session including:
7
7
  * - session_id from session_start
@@ -54,12 +54,12 @@ export declare function getProject(): string | null;
54
54
  */
55
55
  export declare function hasActiveIssue(): boolean;
56
56
  /**
57
- * OD-552: Add surfaced scars to tracking (deduplicates by scar_id)
57
+ * Add surfaced scars to tracking (deduplicates by scar_id)
58
58
  * Called by session_start and recall when scars are surfaced.
59
59
  */
60
60
  export declare function addSurfacedScars(scars: SurfacedScar[]): void;
61
61
  /**
62
- * OD-552: Get all surfaced scars for the current session
62
+ * Get all surfaced scars for the current session
63
63
  */
64
64
  export declare function getSurfacedScars(): SurfacedScar[];
65
65
  /**
@@ -105,15 +105,15 @@ export interface SessionActivity {
105
105
  }
106
106
  export declare function getSessionActivity(): SessionActivity | null;
107
107
  /**
108
- * OD-thread-lifecycle: Set threads for the current session
108
+ * : Set threads for the current session
109
109
  */
110
110
  export declare function setThreads(threads: ThreadObject[]): void;
111
111
  /**
112
- * OD-thread-lifecycle: Get threads for the current session
112
+ * : Get threads for the current session
113
113
  */
114
114
  export declare function getThreads(): ThreadObject[];
115
115
  /**
116
- * OD-thread-lifecycle: Resolve a thread in session state by ID.
116
+ * : Resolve a thread in session state by ID.
117
117
  * Returns the resolved thread or null if not found.
118
118
  */
119
119
  export declare function resolveThreadInState(threadId: string, resolutionNote?: string): ThreadObject | null;
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Session State Management
3
- * OD-547: Track current session context for auto-injecting into recall calls
4
- * OD-552: Track surfaced scars for auto-bridging Q6 answers to scar_usage records
3
+ * Track current session context for auto-injecting into recall calls
4
+ * Track surfaced scars for auto-bridging Q6 answers to scar_usage records
5
5
  *
6
6
  * Maintains in-memory state of the current active session including:
7
7
  * - session_id from session_start
@@ -59,7 +59,7 @@ export function hasActiveIssue() {
59
59
  return !!(currentSession?.linearIssue);
60
60
  }
61
61
  /**
62
- * OD-552: Add surfaced scars to tracking (deduplicates by scar_id)
62
+ * Add surfaced scars to tracking (deduplicates by scar_id)
63
63
  * Called by session_start and recall when scars are surfaced.
64
64
  */
65
65
  export function addSurfacedScars(scars) {
@@ -76,7 +76,7 @@ export function addSurfacedScars(scars) {
76
76
  console.error(`[session-state] Surfaced scars tracked: ${currentSession.surfacedScars.length} total`);
77
77
  }
78
78
  /**
79
- * OD-552: Get all surfaced scars for the current session
79
+ * Get all surfaced scars for the current session
80
80
  */
81
81
  export function getSurfacedScars() {
82
82
  return currentSession?.surfacedScars || [];
@@ -121,6 +121,9 @@ export function hasUnconfirmedScars() {
121
121
  const confirmedIds = new Set(currentSession.confirmations.map(c => c.scar_id));
122
122
  return recallScars.some(s => !confirmedIds.has(s.scar_id));
123
123
  }
124
+ // Security: cap unbounded arrays to prevent memory exhaustion in long sessions
125
+ const MAX_OBSERVATIONS = 500;
126
+ const MAX_CHILDREN = 100;
124
127
  /**
125
128
  * v2 Phase 2: Add observations from sub-agents/teammates
126
129
  */
@@ -134,6 +137,10 @@ export function addObservations(newObs) {
134
137
  absorbed_at: o.absorbed_at || new Date().toISOString(),
135
138
  }));
136
139
  currentSession.observations.push(...timestamped);
140
+ // Cap to prevent memory exhaustion — keep most recent
141
+ if (currentSession.observations.length > MAX_OBSERVATIONS) {
142
+ currentSession.observations = currentSession.observations.slice(-MAX_OBSERVATIONS);
143
+ }
137
144
  console.error(`[session-state] Observations tracked: ${currentSession.observations.length} total`);
138
145
  return timestamped.length;
139
146
  }
@@ -151,6 +158,11 @@ export function addChild(child) {
151
158
  console.warn("[session-state] Cannot add child: no active session");
152
159
  return;
153
160
  }
161
+ // Cap to prevent memory exhaustion — reject silently beyond limit
162
+ if (currentSession.children.length >= MAX_CHILDREN) {
163
+ console.warn(`[session-state] Children cap reached (${MAX_CHILDREN}), ignoring new child: ${child.role}`);
164
+ return;
165
+ }
154
166
  currentSession.children.push(child);
155
167
  console.error(`[session-state] Child registered: ${child.role} (${child.type}), total: ${currentSession.children.length}`);
156
168
  }
@@ -173,7 +185,7 @@ export function getSessionActivity() {
173
185
  };
174
186
  }
175
187
  /**
176
- * OD-thread-lifecycle: Set threads for the current session
188
+ * : Set threads for the current session
177
189
  */
178
190
  export function setThreads(threads) {
179
191
  if (!currentSession) {
@@ -184,13 +196,13 @@ export function setThreads(threads) {
184
196
  console.error(`[session-state] Threads set: ${threads.length} total`);
185
197
  }
186
198
  /**
187
- * OD-thread-lifecycle: Get threads for the current session
199
+ * : Get threads for the current session
188
200
  */
189
201
  export function getThreads() {
190
202
  return currentSession?.threads || [];
191
203
  }
192
204
  /**
193
- * OD-thread-lifecycle: Resolve a thread in session state by ID.
205
+ * : Resolve a thread in session state by ID.
194
206
  * Returns the resolved thread or null if not found.
195
207
  */
196
208
  export function resolveThreadInState(threadId, resolutionNote) {