@kinqs/brainrouter-mcp-server 0.3.4

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 (337) hide show
  1. package/.env.example +144 -0
  2. package/README.md +56 -0
  3. package/agents/README.md +120 -0
  4. package/agents/code-reviewer.md +97 -0
  5. package/agents/security-auditor.md +101 -0
  6. package/agents/test-engineer.md +95 -0
  7. package/dist/__tests__/agent_mode.test.d.ts +1 -0
  8. package/dist/__tests__/api-routes.test.d.ts +1 -0
  9. package/dist/__tests__/api-routes.test.js +170 -0
  10. package/dist/__tests__/crypto.test.d.ts +1 -0
  11. package/dist/__tests__/crypto.test.js +28 -0
  12. package/dist/__tests__/host-integrations.test.d.ts +1 -0
  13. package/dist/__tests__/host-integrations.test.js +82 -0
  14. package/dist/__tests__/integration.test.d.ts +1 -0
  15. package/dist/__tests__/integration.test.js +50 -0
  16. package/dist/__tests__/loader.test.d.ts +1 -0
  17. package/dist/__tests__/loader.test.js +89 -0
  18. package/dist/__tests__/neural-spark.test.d.ts +1 -0
  19. package/dist/__tests__/neural-spark.test.js +112 -0
  20. package/dist/__tests__/pagination.test.d.ts +1 -0
  21. package/dist/__tests__/pagination.test.js +23 -0
  22. package/dist/__tests__/redaction.test.d.ts +1 -0
  23. package/dist/__tests__/redaction.test.js +17 -0
  24. package/dist/__tests__/registry.test.d.ts +1 -0
  25. package/dist/__tests__/registry.test.js +56 -0
  26. package/dist/__tests__/retry.test.d.ts +1 -0
  27. package/dist/__tests__/retry.test.js +30 -0
  28. package/dist/__tests__/skill-activation.test.d.ts +1 -0
  29. package/dist/__tests__/skill-activation.test.js +112 -0
  30. package/dist/__tests__/working-memory.test.d.ts +1 -0
  31. package/dist/__tests__/working-memory.test.js +200 -0
  32. package/dist/__tests__/workspace-paths.test.d.ts +1 -0
  33. package/dist/__tests__/workspace-paths.test.js +56 -0
  34. package/dist/__tests__/writer.test.d.ts +1 -0
  35. package/dist/__tests__/writer.test.js +94 -0
  36. package/dist/api/auth/crypto.d.ts +4 -0
  37. package/dist/api/auth/crypto.js +54 -0
  38. package/dist/api/middleware/auth.d.ts +12 -0
  39. package/dist/api/middleware/auth.js +90 -0
  40. package/dist/api/pagination.d.ts +18 -0
  41. package/dist/api/pagination.js +32 -0
  42. package/dist/api/routes/auth.d.ts +1 -0
  43. package/dist/api/routes/auth.js +130 -0
  44. package/dist/api/routes/chat-completions.d.ts +7 -0
  45. package/dist/api/routes/chat-completions.js +474 -0
  46. package/dist/api/routes/contradictions.d.ts +1 -0
  47. package/dist/api/routes/contradictions.js +28 -0
  48. package/dist/api/routes/evidence.d.ts +1 -0
  49. package/dist/api/routes/evidence.js +59 -0
  50. package/dist/api/routes/governance.d.ts +1 -0
  51. package/dist/api/routes/governance.js +95 -0
  52. package/dist/api/routes/graph.d.ts +1 -0
  53. package/dist/api/routes/graph.js +25 -0
  54. package/dist/api/routes/hooks.d.ts +1 -0
  55. package/dist/api/routes/hooks.js +88 -0
  56. package/dist/api/routes/memories.d.ts +1 -0
  57. package/dist/api/routes/memories.js +92 -0
  58. package/dist/api/routes/persona.d.ts +1 -0
  59. package/dist/api/routes/persona.js +9 -0
  60. package/dist/api/routes/scenes.d.ts +1 -0
  61. package/dist/api/routes/scenes.js +35 -0
  62. package/dist/api/routes/skills.d.ts +1 -0
  63. package/dist/api/routes/skills.js +14 -0
  64. package/dist/api/routes/stats.d.ts +1 -0
  65. package/dist/api/routes/stats.js +8 -0
  66. package/dist/api/routes/users.d.ts +1 -0
  67. package/dist/api/routes/users.js +82 -0
  68. package/dist/api/routes/working.d.ts +1 -0
  69. package/dist/api/routes/working.js +88 -0
  70. package/dist/index.d.ts +2 -0
  71. package/dist/index.js +492 -0
  72. package/dist/integrations/claude-code.d.ts +12 -0
  73. package/dist/integrations/claude-code.js +35 -0
  74. package/dist/integrations/codex.d.ts +12 -0
  75. package/dist/integrations/codex.js +34 -0
  76. package/dist/integrations/generic-mcp.d.ts +52 -0
  77. package/dist/integrations/generic-mcp.js +118 -0
  78. package/dist/loader.d.ts +29 -0
  79. package/dist/loader.js +200 -0
  80. package/dist/memory/capture.d.ts +35 -0
  81. package/dist/memory/capture.js +230 -0
  82. package/dist/memory/config.d.ts +2 -0
  83. package/dist/memory/config.js +3 -0
  84. package/dist/memory/engine.d.ts +203 -0
  85. package/dist/memory/engine.js +626 -0
  86. package/dist/memory/llm-semaphore.d.ts +41 -0
  87. package/dist/memory/llm-semaphore.js +81 -0
  88. package/dist/memory/memory-type-config.d.ts +11 -0
  89. package/dist/memory/memory-type-config.js +65 -0
  90. package/dist/memory/pipeline/cognitive-contradiction.d.ts +7 -0
  91. package/dist/memory/pipeline/cognitive-contradiction.js +59 -0
  92. package/dist/memory/pipeline/cognitive-dedup.d.ts +23 -0
  93. package/dist/memory/pipeline/cognitive-dedup.js +38 -0
  94. package/dist/memory/pipeline/cognitive-extractor.d.ts +21 -0
  95. package/dist/memory/pipeline/cognitive-extractor.js +183 -0
  96. package/dist/memory/pipeline/contextual-focus-builder.d.ts +13 -0
  97. package/dist/memory/pipeline/contextual-focus-builder.js +135 -0
  98. package/dist/memory/pipeline/focus-direction-shift.d.ts +10 -0
  99. package/dist/memory/pipeline/focus-direction-shift.js +27 -0
  100. package/dist/memory/pipeline/graph-builder.d.ts +11 -0
  101. package/dist/memory/pipeline/graph-builder.js +88 -0
  102. package/dist/memory/pipeline/graph-recall.d.ts +13 -0
  103. package/dist/memory/pipeline/graph-recall.js +55 -0
  104. package/dist/memory/pipeline/identity-distiller.d.ts +15 -0
  105. package/dist/memory/pipeline/identity-distiller.js +40 -0
  106. package/dist/memory/pipeline/l1-contradiction.d.ts +7 -0
  107. package/dist/memory/pipeline/l1-contradiction.js +66 -0
  108. package/dist/memory/pipeline/l1-dedup.d.ts +23 -0
  109. package/dist/memory/pipeline/l1-dedup.js +39 -0
  110. package/dist/memory/pipeline/l1-extractor.d.ts +21 -0
  111. package/dist/memory/pipeline/l1-extractor.js +180 -0
  112. package/dist/memory/pipeline/l2-direction-shift.d.ts +10 -0
  113. package/dist/memory/pipeline/l2-direction-shift.js +27 -0
  114. package/dist/memory/pipeline/l2-scene.d.ts +15 -0
  115. package/dist/memory/pipeline/l2-scene.js +140 -0
  116. package/dist/memory/pipeline/l3-distiller.d.ts +15 -0
  117. package/dist/memory/pipeline/l3-distiller.js +40 -0
  118. package/dist/memory/pipeline/neural-spark.d.ts +27 -0
  119. package/dist/memory/pipeline/neural-spark.js +78 -0
  120. package/dist/memory/pipeline/skill-prewarm.d.ts +63 -0
  121. package/dist/memory/pipeline/skill-prewarm.js +127 -0
  122. package/dist/memory/pipeline/task-queue.d.ts +54 -0
  123. package/dist/memory/pipeline/task-queue.js +117 -0
  124. package/dist/memory/prompts/cognitive-contradiction.d.ts +1 -0
  125. package/dist/memory/prompts/cognitive-contradiction.js +25 -0
  126. package/dist/memory/prompts/cognitive-extraction.d.ts +10 -0
  127. package/dist/memory/prompts/cognitive-extraction.js +114 -0
  128. package/dist/memory/prompts/core-identity.d.ts +6 -0
  129. package/dist/memory/prompts/core-identity.js +60 -0
  130. package/dist/memory/prompts/focus-direction-shift.d.ts +5 -0
  131. package/dist/memory/prompts/focus-direction-shift.js +32 -0
  132. package/dist/memory/prompts/focus-scene-cluster.d.ts +2 -0
  133. package/dist/memory/prompts/focus-scene-cluster.js +33 -0
  134. package/dist/memory/prompts/focus-scene.d.ts +7 -0
  135. package/dist/memory/prompts/focus-scene.js +40 -0
  136. package/dist/memory/prompts/graph-extraction-batch.d.ts +14 -0
  137. package/dist/memory/prompts/graph-extraction-batch.js +54 -0
  138. package/dist/memory/prompts/graph-extraction.d.ts +2 -0
  139. package/dist/memory/prompts/graph-extraction.js +53 -0
  140. package/dist/memory/prompts/l1-contradiction-batch.d.ts +16 -0
  141. package/dist/memory/prompts/l1-contradiction-batch.js +47 -0
  142. package/dist/memory/prompts/l1-contradiction.d.ts +1 -0
  143. package/dist/memory/prompts/l1-contradiction.js +25 -0
  144. package/dist/memory/prompts/l1-extraction.d.ts +10 -0
  145. package/dist/memory/prompts/l1-extraction.js +114 -0
  146. package/dist/memory/prompts/l2-direction-shift.d.ts +5 -0
  147. package/dist/memory/prompts/l2-direction-shift.js +32 -0
  148. package/dist/memory/prompts/l2-scene-cluster.d.ts +2 -0
  149. package/dist/memory/prompts/l2-scene-cluster.js +33 -0
  150. package/dist/memory/prompts/l2-scene.d.ts +7 -0
  151. package/dist/memory/prompts/l2-scene.js +40 -0
  152. package/dist/memory/prompts/l3-persona.d.ts +6 -0
  153. package/dist/memory/prompts/l3-persona.js +60 -0
  154. package/dist/memory/recall.d.ts +47 -0
  155. package/dist/memory/recall.js +427 -0
  156. package/dist/memory/redaction.d.ts +1 -0
  157. package/dist/memory/redaction.js +24 -0
  158. package/dist/memory/retry.d.ts +13 -0
  159. package/dist/memory/retry.js +53 -0
  160. package/dist/memory/scheduler.d.ts +9 -0
  161. package/dist/memory/scheduler.js +16 -0
  162. package/dist/memory/skill-hints-loader.d.ts +30 -0
  163. package/dist/memory/skill-hints-loader.js +100 -0
  164. package/dist/memory/store/embedding.d.ts +16 -0
  165. package/dist/memory/store/embedding.js +68 -0
  166. package/dist/memory/store/reranker.d.ts +24 -0
  167. package/dist/memory/store/reranker.js +83 -0
  168. package/dist/memory/store/sqlite.d.ts +167 -0
  169. package/dist/memory/store/sqlite.js +1816 -0
  170. package/dist/memory/store/types.d.ts +101 -0
  171. package/dist/memory/store/types.js +1 -0
  172. package/dist/memory/types.d.ts +207 -0
  173. package/dist/memory/types.js +7 -0
  174. package/dist/memory/validation.d.ts +441 -0
  175. package/dist/memory/validation.js +129 -0
  176. package/dist/memory/working/canvas.d.ts +5 -0
  177. package/dist/memory/working/canvas.js +43 -0
  178. package/dist/memory/working/offload.d.ts +71 -0
  179. package/dist/memory/working/offload.js +211 -0
  180. package/dist/memory/working/step-log.d.ts +16 -0
  181. package/dist/memory/working/step-log.js +35 -0
  182. package/dist/registry.d.ts +34 -0
  183. package/dist/registry.js +305 -0
  184. package/dist/resolver.d.ts +17 -0
  185. package/dist/resolver.js +126 -0
  186. package/dist/scripts/validate-foreign-workspace-path.d.ts +1 -0
  187. package/dist/scripts/validate-foreign-workspace-path.js +39 -0
  188. package/dist/tools/agent_memory_tools.d.ts +485 -0
  189. package/dist/tools/agent_memory_tools.js +793 -0
  190. package/dist/tools/create_skill.d.ts +46 -0
  191. package/dist/tools/create_skill.js +46 -0
  192. package/dist/tools/get_doc.d.ts +21 -0
  193. package/dist/tools/get_doc.js +24 -0
  194. package/dist/tools/get_persona.d.ts +15 -0
  195. package/dist/tools/get_persona.js +20 -0
  196. package/dist/tools/get_reference.d.ts +15 -0
  197. package/dist/tools/get_reference.js +20 -0
  198. package/dist/tools/get_skill.d.ts +34 -0
  199. package/dist/tools/get_skill.js +65 -0
  200. package/dist/tools/get_template_doc.d.ts +21 -0
  201. package/dist/tools/get_template_doc.js +24 -0
  202. package/dist/tools/list_docs.d.ts +15 -0
  203. package/dist/tools/list_docs.js +16 -0
  204. package/dist/tools/list_skills.d.ts +18 -0
  205. package/dist/tools/list_skills.js +17 -0
  206. package/dist/tools/list_template_docs.d.ts +15 -0
  207. package/dist/tools/list_template_docs.js +16 -0
  208. package/dist/tools/memory-engineering.d.ts +225 -0
  209. package/dist/tools/memory-engineering.js +284 -0
  210. package/dist/tools/memory-explain.d.ts +34 -0
  211. package/dist/tools/memory-explain.js +109 -0
  212. package/dist/tools/memory-governance.d.ts +171 -0
  213. package/dist/tools/memory-governance.js +224 -0
  214. package/dist/tools/memory-hooks.d.ts +67 -0
  215. package/dist/tools/memory-hooks.js +102 -0
  216. package/dist/tools/memory-working.d.ts +98 -0
  217. package/dist/tools/memory-working.js +101 -0
  218. package/dist/tools/memory_capture_turn.d.ts +66 -0
  219. package/dist/tools/memory_capture_turn.js +85 -0
  220. package/dist/tools/memory_consolidate.d.ts +55 -0
  221. package/dist/tools/memory_consolidate.js +176 -0
  222. package/dist/tools/memory_contradictions.d.ts +53 -0
  223. package/dist/tools/memory_contradictions.js +52 -0
  224. package/dist/tools/memory_graph_query.d.ts +51 -0
  225. package/dist/tools/memory_graph_query.js +35 -0
  226. package/dist/tools/memory_mark_cited.d.ts +43 -0
  227. package/dist/tools/memory_mark_cited.js +63 -0
  228. package/dist/tools/memory_recall.d.ts +77 -0
  229. package/dist/tools/memory_recall.js +81 -0
  230. package/dist/tools/memory_register_skill_hints.d.ts +49 -0
  231. package/dist/tools/memory_register_skill_hints.js +55 -0
  232. package/dist/tools/memory_resolve_session.d.ts +24 -0
  233. package/dist/tools/memory_resolve_session.js +133 -0
  234. package/dist/tools/memory_search.d.ts +146 -0
  235. package/dist/tools/memory_search.js +84 -0
  236. package/dist/tools/search_skills.d.ts +18 -0
  237. package/dist/tools/search_skills.js +17 -0
  238. package/dist/tools/update_doc.d.ts +24 -0
  239. package/dist/tools/update_doc.js +35 -0
  240. package/dist/tools/update_skill.d.ts +30 -0
  241. package/dist/tools/update_skill.js +80 -0
  242. package/dist/types.d.ts +81 -0
  243. package/dist/types.js +4 -0
  244. package/dist/writer.d.ts +30 -0
  245. package/dist/writer.js +220 -0
  246. package/docs/TEMPLATE ONLY +1 -0
  247. package/docs/api/API.md +64 -0
  248. package/docs/api/security/SECURITY.md +58 -0
  249. package/docs/deployment/DockerDeployment.md +30 -0
  250. package/docs/design/Design.md +59 -0
  251. package/docs/design/themes/apple.md +101 -0
  252. package/docs/design/themes/dieter-grid.md +100 -0
  253. package/docs/design/themes/gallery-white.md +100 -0
  254. package/docs/design/themes/pinterest.md +101 -0
  255. package/docs/design/themes/realty-open-house.md +101 -0
  256. package/docs/design/themes/vodafone.md +101 -0
  257. package/docs/hooks/Hooks.md +30 -0
  258. package/docs/schema/Schema.md +35 -0
  259. package/docs/strategy/ScalingStrategy.md +19 -0
  260. package/package.json +88 -0
  261. package/references/accessibility-checklist.md +160 -0
  262. package/references/orchestration-patterns.md +370 -0
  263. package/references/performance-checklist.md +153 -0
  264. package/references/security-checklist.md +134 -0
  265. package/references/testing-patterns.md +236 -0
  266. package/skills/agent/adr-skill/SKILL.md +299 -0
  267. package/skills/agent/agentic-engineering-workflow/SKILL.md +95 -0
  268. package/skills/agent/bootstrap-skill/SKILL.md +103 -0
  269. package/skills/agent/context-engineering/SKILL.md +307 -0
  270. package/skills/agent/debugging-and-error-recovery/SKILL.md +308 -0
  271. package/skills/agent/developer-growth-analysis/SKILL.md +328 -0
  272. package/skills/agent/doubt-driven-skill/SKILL.md +249 -0
  273. package/skills/agent/handover-skill/SKILL.md +112 -0
  274. package/skills/agent/idea-refine-skill/SKILL.md +185 -0
  275. package/skills/agent/idea-refine-skill/examples.md +238 -0
  276. package/skills/agent/idea-refine-skill/frameworks.md +99 -0
  277. package/skills/agent/idea-refine-skill/refinement-criteria.md +113 -0
  278. package/skills/agent/interview-skill/SKILL.md +226 -0
  279. package/skills/agent/planning-skill/SKILL.md +270 -0
  280. package/skills/agent/skill-authoring/SKILL.md +189 -0
  281. package/skills/agent/source-driven-skill/SKILL.md +197 -0
  282. package/skills/agent/spec-driven-skill/SKILL.md +221 -0
  283. package/skills/agent/sync-skill/SKILL.md +92 -0
  284. package/skills/agent/using-agent-skills/SKILL.md +189 -0
  285. package/skills/api/a11y-skill/SKILL.md +88 -0
  286. package/skills/api/api-skill/SKILL.md +123 -0
  287. package/skills/api/auth-skill/SKILL.md +80 -0
  288. package/skills/api/debug-skill/SKILL.md +535 -0
  289. package/skills/api/performance-skill/SKILL.md +100 -0
  290. package/skills/api/testing-skill/SKILL.md +100 -0
  291. package/skills/codebase/code-review-and-quality/SKILL.md +228 -0
  292. package/skills/codebase/code-simplification/SKILL.md +352 -0
  293. package/skills/codebase/code-structure-cleanup/SKILL.md +142 -0
  294. package/skills/codebase/concerns-skill/SKILL.md +89 -0
  295. package/skills/codebase/conventions-skill/SKILL.md +95 -0
  296. package/skills/codebase/doc-management-skill/SKILL.md +47 -0
  297. package/skills/codebase/git-workflow-skill/SKILL.md +312 -0
  298. package/skills/communication/1-3-1-rule/SKILL.md +120 -0
  299. package/skills/design/brutalist-skill/SKILL.md +131 -0
  300. package/skills/design/concept-diagrams/SKILL.md +387 -0
  301. package/skills/design/concept-diagrams/examples/apartment-floor-plan-conversion.md +244 -0
  302. package/skills/design/concept-diagrams/examples/automated-password-reset-flow.md +276 -0
  303. package/skills/design/concept-diagrams/examples/autonomous-llm-research-agent-flow.md +240 -0
  304. package/skills/design/concept-diagrams/examples/banana-journey-tree-to-smoothie.md +161 -0
  305. package/skills/design/concept-diagrams/examples/commercial-aircraft-structure.md +209 -0
  306. package/skills/design/concept-diagrams/examples/cpu-ooo-microarchitecture.md +236 -0
  307. package/skills/design/concept-diagrams/examples/electricity-grid-flow.md +182 -0
  308. package/skills/design/concept-diagrams/examples/feature-film-production-pipeline.md +172 -0
  309. package/skills/design/concept-diagrams/examples/hospital-emergency-department-flow.md +165 -0
  310. package/skills/design/concept-diagrams/examples/ml-benchmark-grouped-bar-chart.md +114 -0
  311. package/skills/design/concept-diagrams/examples/place-order-uml-sequence.md +325 -0
  312. package/skills/design/concept-diagrams/examples/smart-city-infrastructure.md +173 -0
  313. package/skills/design/concept-diagrams/examples/smartphone-layer-anatomy.md +154 -0
  314. package/skills/design/concept-diagrams/examples/sn2-reaction-mechanism.md +247 -0
  315. package/skills/design/concept-diagrams/examples/wind-turbine-structure.md +338 -0
  316. package/skills/design/concept-diagrams/references/dashboard-patterns.md +43 -0
  317. package/skills/design/concept-diagrams/references/infrastructure-patterns.md +144 -0
  318. package/skills/design/concept-diagrams/references/physical-shape-cookbook.md +42 -0
  319. package/skills/design/concept-diagrams/templates/template.html +174 -0
  320. package/skills/design/gpt-tasteskill/SKILL.md +114 -0
  321. package/skills/design/minimalist-skill/SKILL.md +116 -0
  322. package/skills/design/output-skill/SKILL.md +87 -0
  323. package/skills/design/redesign-skill/SKILL.md +213 -0
  324. package/skills/design/soft-skill/SKILL.md +132 -0
  325. package/skills/design/stitch-skill/EXAMPLE.md +121 -0
  326. package/skills/design/stitch-skill/SKILL.md +222 -0
  327. package/skills/design/taste-skill/SKILL.md +269 -0
  328. package/skills/devops/ci-cd-skill/SKILL.md +402 -0
  329. package/skills/devops/docker-skill/SKILL.md +297 -0
  330. package/skills/devops/domain-skill/SKILL.md +234 -0
  331. package/skills/lifecycle/changelog-generator/SKILL.md +135 -0
  332. package/skills/lifecycle/incremental-skill/SKILL.md +257 -0
  333. package/skills/lifecycle/migration-skill/SKILL.md +218 -0
  334. package/skills/lifecycle/shipping-skill/SKILL.md +321 -0
  335. package/skills/memory/agent-memory/SKILL.md +122 -0
  336. package/skills/qa/browser-testing-skill/SKILL.md +314 -0
  337. package/skills/ux/adversarial-ux-skill/SKILL.md +168 -0
@@ -0,0 +1,27 @@
1
+ import { FOCUS_DIRECTION_SHIFT_SYSTEM_PROMPT, formatFocusDirectionShiftPrompt } from "../prompts/focus-direction-shift.js";
2
+ export async function detectFocusShift(params) {
3
+ const { activeScene, newCognitiveRecords, llmRunner } = params;
4
+ try {
5
+ const prompt = formatFocusDirectionShiftPrompt(activeScene.sceneName, activeScene.summaryMd, newCognitiveRecords.map(r => ({ content: r.content, type: r.type })));
6
+ const response = await llmRunner.run({
7
+ prompt,
8
+ systemPrompt: FOCUS_DIRECTION_SHIFT_SYSTEM_PROMPT,
9
+ taskId: "focus-direction-shift",
10
+ timeoutMs: 30_000,
11
+ });
12
+ const jsonMatch = response.match(/\{[\s\S]*\}/);
13
+ if (!jsonMatch) {
14
+ throw new Error("No JSON object found in LLM response");
15
+ }
16
+ const parsed = JSON.parse(jsonMatch[0]);
17
+ return {
18
+ shift: Boolean(parsed.shift),
19
+ confidence: Number(parsed.confidence) || 0,
20
+ reason: String(parsed.reason) || "",
21
+ };
22
+ }
23
+ catch (err) {
24
+ console.error(`[BrainRouter] Focus direction shift detection failed:`, err.message);
25
+ return { shift: false, confidence: 0, reason: "Error" };
26
+ }
27
+ }
@@ -0,0 +1,11 @@
1
+ import type { IMemoryStore } from "@kinqs/brainrouter-types";
2
+ import type { LLMRunner, CognitiveRecord } from "@kinqs/brainrouter-types";
3
+ /**
4
+ * Graph Construction Pipeline
5
+ * Extracts entities and relationships from Cognitive records to update the Knowledge Graph.
6
+ */
7
+ export declare function buildGraphFromCognitive(params: {
8
+ record: CognitiveRecord;
9
+ store: IMemoryStore;
10
+ llmRunner: LLMRunner;
11
+ }): Promise<void>;
@@ -0,0 +1,88 @@
1
+ import { GRAPH_EXTRACTION_SYSTEM_PROMPT, formatGraphExtractionPrompt } from "../prompts/graph-extraction.js";
2
+ import crypto from "node:crypto";
3
+ /**
4
+ * Graph Construction Pipeline
5
+ * Extracts entities and relationships from Cognitive records to update the Knowledge Graph.
6
+ */
7
+ export async function buildGraphFromCognitive(params) {
8
+ const { record, store, llmRunner } = params;
9
+ // Gate execution with env flag (default is true)
10
+ if (process.env.BRAINROUTER_GRAPH_ENABLED === "false") {
11
+ return;
12
+ }
13
+ // Skip graph extraction if memory is invalidated
14
+ if (record.invalidAt)
15
+ return;
16
+ const _parsedTimeout = parseInt(process.env.BRAINROUTER_GRAPH_TIMEOUT_MS || "", 10);
17
+ const timeoutMs = isNaN(_parsedTimeout) ? 120000 : _parsedTimeout;
18
+ try {
19
+ const rawExtraction = await llmRunner.run({
20
+ prompt: formatGraphExtractionPrompt(record.content),
21
+ systemPrompt: GRAPH_EXTRACTION_SYSTEM_PROMPT,
22
+ taskId: `graph-extraction-${record.id}`,
23
+ timeoutMs
24
+ });
25
+ const jsonMatch = rawExtraction.match(/\{[\s\S]*\}/);
26
+ if (!jsonMatch)
27
+ return;
28
+ const data = JSON.parse(jsonMatch[0]);
29
+ if (!data || !Array.isArray(data.entities))
30
+ return;
31
+ const entityMap = new Map(); // name -> node.id
32
+ // 1. Process and upsert nodes
33
+ for (const ent of data.entities) {
34
+ const entityName = String(ent.entity || "").trim();
35
+ const entityType = String(ent.type || "concept").trim();
36
+ const confidence = typeof ent.confidence === "number" ? ent.confidence : 1.0;
37
+ if (!entityName)
38
+ continue;
39
+ // Check if node already exists to get its ID, otherwise create a new one
40
+ const existingNode = store.getGraphNodeByEntity(record.userId, entityName);
41
+ const nodeId = existingNode?.id ?? `gn_${crypto.randomBytes(6).toString("hex")}`;
42
+ entityMap.set(entityName.toLowerCase(), nodeId);
43
+ const node = {
44
+ id: nodeId,
45
+ userId: record.userId,
46
+ entity: entityName,
47
+ entityType,
48
+ skillTag: record.skillTag || "",
49
+ confidence,
50
+ sourceRecordId: record.id,
51
+ createdTime: record.createdTime || new Date().toISOString()
52
+ };
53
+ store.upsertGraphNode(node);
54
+ }
55
+ // 2. Process and upsert edges
56
+ if (Array.isArray(data.relations)) {
57
+ for (const rel of data.relations) {
58
+ const fromName = String(rel.from || "").trim();
59
+ const toName = String(rel.to || "").trim();
60
+ const relation = String(rel.relation || "relates_to").trim();
61
+ const confidence = typeof rel.confidence === "number" ? rel.confidence : 1.0;
62
+ if (!fromName || !toName)
63
+ continue;
64
+ const fromNodeId = entityMap.get(fromName.toLowerCase());
65
+ const toNodeId = entityMap.get(toName.toLowerCase());
66
+ if (!fromNodeId || !toNodeId) {
67
+ // Skip edges where one endpoint wasn't extracted (LLM mentioned it in relations but not in entities — common with the 5-entity cap)
68
+ continue;
69
+ }
70
+ const edge = {
71
+ id: `ge_${crypto.randomBytes(6).toString("hex")}`,
72
+ userId: record.userId,
73
+ fromNodeId,
74
+ toNodeId,
75
+ relation,
76
+ skillTag: record.skillTag || "",
77
+ confidence,
78
+ sourceRecordId: record.id,
79
+ createdTime: record.createdTime || new Date().toISOString()
80
+ };
81
+ store.upsertGraphEdge(edge);
82
+ }
83
+ }
84
+ }
85
+ catch (err) {
86
+ console.error(`[BrainRouter] Graph extraction failed for record ${record.id}:`, err.message);
87
+ }
88
+ }
@@ -0,0 +1,13 @@
1
+ import type { IMemoryStore } from "@kinqs/brainrouter-types";
2
+ /**
3
+ * Hybrid GraphRAG Recall Expansion
4
+ * Finds matching entities in the query and top Cognitive results, performs a 2-hop
5
+ * BFS traversal, and returns a formatted markdown block of the context.
6
+ */
7
+ export declare function expandRecallWithGraph(params: {
8
+ topCognitiveResults: any[];
9
+ query: string;
10
+ userId: string;
11
+ activeSkill?: string;
12
+ store: IMemoryStore;
13
+ }): string;
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Hybrid GraphRAG Recall Expansion
3
+ * Finds matching entities in the query and top Cognitive results, performs a 2-hop
4
+ * BFS traversal, and returns a formatted markdown block of the context.
5
+ */
6
+ export function expandRecallWithGraph(params) {
7
+ const { topCognitiveResults, query, userId, activeSkill, store } = params;
8
+ try {
9
+ // 1. Fetch all graph nodes for this user to match entities
10
+ const allNodes = store.getAllGraphNodes(userId);
11
+ if (allNodes.length === 0)
12
+ return "";
13
+ const combinedText = `${query} ${topCognitiveResults.map(r => r.content || "").join(" ")}`.toLowerCase();
14
+ // 2. Find which nodes are mentioned in the query or top results
15
+ const matchingNodeIds = new Set();
16
+ for (const node of allNodes) {
17
+ if (combinedText.includes(node.entity.toLowerCase())) {
18
+ matchingNodeIds.add(node.id);
19
+ }
20
+ }
21
+ if (matchingNodeIds.size === 0)
22
+ return "";
23
+ // 3. For each matching node, fetch neighbors up to 2 hops
24
+ const unionNodes = new Map();
25
+ const unionEdges = new Map();
26
+ for (const nodeId of matchingNodeIds) {
27
+ const { nodes, edges } = store.getGraphNeighbors(userId, nodeId, activeSkill, 2);
28
+ for (const n of nodes)
29
+ unionNodes.set(n.id, n);
30
+ for (const e of edges)
31
+ unionEdges.set(e.id, e);
32
+ }
33
+ if (unionNodes.size === 0)
34
+ return "";
35
+ // 4. Format into a beautiful Knowledge Graph Context
36
+ const nodeLines = Array.from(unionNodes.values()).map(n => `- **${n.entity}** (${n.entityType})`);
37
+ const edgeLines = Array.from(unionEdges.values()).map(e => {
38
+ const fromNode = unionNodes.get(e.fromNodeId);
39
+ const toNode = unionNodes.get(e.toNodeId);
40
+ if (!fromNode || !toNode)
41
+ return null;
42
+ return `- **${fromNode.entity}** --[${e.relation}]--> **${toNode.entity}** (confidence: ${e.confidence.toFixed(2)}${e.skillTag ? `, skill: ${e.skillTag}` : ""})`;
43
+ }).filter(Boolean);
44
+ let output = "\n==================================================\n";
45
+ output += "🕸️ KNOWLEDGE GRAPH CONTEXT (GraphRAG)\n";
46
+ output += "==================================================\n\n";
47
+ output += "### Graph Entities:\n" + nodeLines.join("\n") + "\n\n";
48
+ output += "### Graph Relationships:\n" + edgeLines.join("\n") + "\n";
49
+ return output;
50
+ }
51
+ catch (err) {
52
+ console.error("[BrainRouter] Graph recall expansion failed:", err.message);
53
+ return "";
54
+ }
55
+ }
@@ -0,0 +1,15 @@
1
+ import type { IMemoryStore } from "@kinqs/brainrouter-types";
2
+ import type { LLMRunner } from "@kinqs/brainrouter-types";
3
+ /**
4
+ * Core Identity Distillation Pipeline
5
+ * Scans ALL persona + instruction CognitiveRecords across all sessions
6
+ * for a user and synthesizes a durable Narrative Profile.
7
+ */
8
+ export declare function distillCoreIdentity(params: {
9
+ userId: string;
10
+ store: IMemoryStore;
11
+ llmRunner: LLMRunner;
12
+ }): Promise<{
13
+ success: boolean;
14
+ personaMd?: string;
15
+ }>;
@@ -0,0 +1,40 @@
1
+ import { CORE_IDENTITY_SYSTEM_PROMPT, formatCoreIdentityPrompt } from "../prompts/core-identity.js";
2
+ /**
3
+ * Core Identity Distillation Pipeline
4
+ * Scans ALL persona + instruction CognitiveRecords across all sessions
5
+ * for a user and synthesizes a durable Narrative Profile.
6
+ */
7
+ export async function distillCoreIdentity(params) {
8
+ const { userId, store, llmRunner } = params;
9
+ // Cross-session: fetch all persona + instruction cognitives for this user
10
+ const memories = store.getIdentityAndInstructionCognitives(userId, 100);
11
+ if (memories.length === 0) {
12
+ console.error(`[BrainRouter] Core Identity distillation skipped for "${userId}" — no persona/instruction memories yet.`);
13
+ return { success: false };
14
+ }
15
+ let personaMd;
16
+ try {
17
+ personaMd = await llmRunner.run({
18
+ prompt: formatCoreIdentityPrompt(memories),
19
+ systemPrompt: CORE_IDENTITY_SYSTEM_PROMPT,
20
+ taskId: "identity-distillation",
21
+ timeoutMs: 90_000,
22
+ });
23
+ }
24
+ catch (err) {
25
+ console.error(`[BrainRouter] Core Identity distillation failed for "${userId}":`, err.message);
26
+ return { success: false };
27
+ }
28
+ const now = new Date().toISOString();
29
+ const existing = store.getCoreIdentity(userId);
30
+ const record = {
31
+ userId,
32
+ personaMd: personaMd.trim(),
33
+ cognitiveCountAtGeneration: memories.length,
34
+ createdTime: existing?.createdTime ?? now,
35
+ updatedTime: now,
36
+ };
37
+ store.upsertCoreIdentity(record);
38
+ console.error(`[BrainRouter] Core Identity updated for "${userId}" (${memories.length} cognitive records).`);
39
+ return { success: true, personaMd: personaMd.trim() };
40
+ }
@@ -0,0 +1,7 @@
1
+ import type { IMemoryStore } from "@brainrouter/types";
2
+ import type { LLMRunner, L1Record } from "@brainrouter/types";
3
+ export declare function detectContradictions(params: {
4
+ newRecord: L1Record;
5
+ store: IMemoryStore;
6
+ llmRunner: LLMRunner;
7
+ }): Promise<void>;
@@ -0,0 +1,66 @@
1
+ import { L1_CONTRADICTION_PROMPT } from "../prompts/l1-contradiction.js";
2
+ import crypto from "node:crypto";
3
+ export async function detectContradictions(params) {
4
+ const { newRecord, store, llmRunner } = params;
5
+ // 1. Search for potentially related memories
6
+ // We use keyword search on the content of the new record to find similar existing ones
7
+ const candidates = store.searchL1Fts(newRecord.userId, newRecord.content, 5);
8
+ const evaluations = [];
9
+ const _parsedContradictionTimeout = parseInt(process.env.BRAINROUTER_CONTRADICTION_TIMEOUT_MS || "", 10);
10
+ const contradictionTimeoutMs = isNaN(_parsedContradictionTimeout) ? 60000 : _parsedContradictionTimeout;
11
+ for (const candidate of candidates) {
12
+ // Don't compare with self
13
+ if (candidate.record_id === newRecord.id)
14
+ continue;
15
+ // Only compare if they are of the same type or both are episodic/persona
16
+ // (instructions don't usually contradict episodic facts)
17
+ const prompt = L1_CONTRADICTION_PROMPT
18
+ .replace("{{newContent}}", newRecord.content)
19
+ .replace("{{existingContent}}", candidate.content);
20
+ try {
21
+ const response = await llmRunner.run({
22
+ prompt,
23
+ taskId: `contradiction-check-${newRecord.id}-${candidate.record_id}`,
24
+ timeoutMs: contradictionTimeoutMs
25
+ });
26
+ // Simple JSON extraction (flexible for local models)
27
+ const jsonMatch = response.match(/\{[\s\S]*\}/);
28
+ if (!jsonMatch)
29
+ continue;
30
+ const data = JSON.parse(jsonMatch[0]);
31
+ if (data.isContradiction && data.confidence > 0.7) {
32
+ evaluations.push({
33
+ candidate,
34
+ isContradiction: true,
35
+ confidence: data.confidence,
36
+ kind: data.kind || "genuine_conflict",
37
+ reason: data.reason
38
+ });
39
+ }
40
+ }
41
+ catch (e) {
42
+ console.error(`[BrainRouter] Contradiction check failed for ${newRecord.id} vs ${candidate.record_id}:`, e.message);
43
+ }
44
+ }
45
+ // If ANY evaluation is a temporal_update, then the entire batch of contradictions represents a temporal transition!
46
+ const hasTemporalUpdate = evaluations.some(ev => ev.kind === "temporal_update");
47
+ for (const ev of evaluations) {
48
+ if (hasTemporalUpdate) {
49
+ // Treat all conflicting old records as superseded by the new record
50
+ console.error(`[BrainRouter] TEMPORAL UPDATE DETECTED (transition): Superseding memory ${ev.candidate.record_id} with new memory ${newRecord.id}`);
51
+ store.invalidateL1Record(newRecord.userId, ev.candidate.record_id, newRecord.id);
52
+ }
53
+ else {
54
+ // Genuine conflict
55
+ console.error(`[BrainRouter] CONTRADICTION DETECTED: ${newRecord.id} vs ${ev.candidate.record_id}`);
56
+ store.upsertContradiction({
57
+ id: `conflict_${crypto.randomBytes(4).toString("hex")}`,
58
+ userId: newRecord.userId,
59
+ recordIdA: ev.candidate.record_id,
60
+ recordIdB: newRecord.id,
61
+ reason: ev.reason,
62
+ confidence: ev.confidence
63
+ });
64
+ }
65
+ }
66
+ }
@@ -0,0 +1,23 @@
1
+ import type { L1Record } from "@brainrouter/types";
2
+ import type { IMemoryStore } from "@brainrouter/types";
3
+ /**
4
+ * Result of the deduplication process
5
+ */
6
+ export interface DedupResult {
7
+ /** Memories that are unique and should be stored */
8
+ uniqueRecords: L1Record[];
9
+ /** Memories that were identified as exact duplicates and dropped */
10
+ droppedCount: number;
11
+ }
12
+ /**
13
+ * Proactively deduplicate extracted memories against the existing memory store
14
+ * before they are stored.
15
+ *
16
+ * Uses exact/near-exact string matching to prevent identical noisy facts
17
+ * from accumulating in the L1 store.
18
+ */
19
+ export declare function deduplicateMemories(params: {
20
+ records: L1Record[];
21
+ store: IMemoryStore;
22
+ userId: string;
23
+ }): Promise<DedupResult>;
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Proactively deduplicate extracted memories against the existing memory store
3
+ * before they are stored.
4
+ *
5
+ * Uses exact/near-exact string matching to prevent identical noisy facts
6
+ * from accumulating in the L1 store.
7
+ */
8
+ export async function deduplicateMemories(params) {
9
+ const { records, store, userId } = params;
10
+ if (records.length === 0) {
11
+ return { uniqueRecords: [], droppedCount: 0 };
12
+ }
13
+ const uniqueRecords = [];
14
+ let droppedCount = 0;
15
+ for (const newRecord of records) {
16
+ // 1. Keyword search to find potentially identical memories
17
+ // We only need top 3 to see if there is an exact match
18
+ const candidates = store.searchL1Fts(userId, newRecord.content, 3);
19
+ let isDuplicate = false;
20
+ for (const candidate of candidates) {
21
+ // Direct string comparison (case-insensitive, trimmed)
22
+ if (candidate.content.trim().toLowerCase() === newRecord.content.trim().toLowerCase()) {
23
+ isDuplicate = true;
24
+ break;
25
+ }
26
+ }
27
+ if (isDuplicate) {
28
+ console.log(`[BrainRouter] Dropped exact duplicate memory: "${newRecord.content}"`);
29
+ droppedCount++;
30
+ }
31
+ else {
32
+ uniqueRecords.push(newRecord);
33
+ }
34
+ }
35
+ return {
36
+ uniqueRecords,
37
+ droppedCount
38
+ };
39
+ }
@@ -0,0 +1,21 @@
1
+ import type { L0Record, L1Record, LLMRunner } from "@brainrouter/types";
2
+ export interface L1ExtractionResult {
3
+ success: boolean;
4
+ extractedCount: number;
5
+ records: L1Record[];
6
+ sceneNames: string[];
7
+ errorMessage?: string;
8
+ }
9
+ export declare function extractL1Memories(params: {
10
+ messages: L0Record[];
11
+ userId: string;
12
+ sessionKey: string;
13
+ sessionId: string;
14
+ llmRunner: LLMRunner;
15
+ maxMessagesPerExtraction?: number;
16
+ maxBackgroundMessages?: number;
17
+ previousSceneName?: string;
18
+ existingSceneNames?: string[];
19
+ activeSkill?: string;
20
+ skillHints?: string;
21
+ }): Promise<L1ExtractionResult>;
@@ -0,0 +1,180 @@
1
+ import { EXTRACT_MEMORIES_SYSTEM_PROMPT, formatExtractionPrompt } from "../prompts/l1-extraction.js";
2
+ import { getMemoryTypeConfig } from "../memory-type-config.js";
3
+ import crypto from "node:crypto";
4
+ const ALLOWED_MEMORY_TYPES = new Set([
5
+ "persona", "episodic", "instruction", "skill_context", "tool_preference",
6
+ "codebase_fact", "api_contract", "data_model", "dependency_constraint",
7
+ "environment_constraint", "architecture_decision", "implementation_decision",
8
+ "design_constraint", "security_policy", "performance_baseline", "bug_finding",
9
+ "debug_trace", "fix_summary", "verification_result", "failed_attempt",
10
+ "regression_risk", "task_state", "handover_note", "blocked_reason",
11
+ "review_comment", "release_note", "source_evidence", "artifact_reference",
12
+ "file_history", "command_knowledge",
13
+ ]);
14
+ const ALLOWED_SOURCE_KINDS = new Set([
15
+ "", "user_instruction", "source_file", "command_output", "test_result",
16
+ "model_inference", "prior_memory",
17
+ ]);
18
+ const ALLOWED_VERIFICATION_STATUSES = new Set([
19
+ "", "verified", "unverified", "stale",
20
+ ]);
21
+ // Ensure the message has actual words to extract from, not just symbols or single letters.
22
+ function shouldExtractL1(text) {
23
+ if (!text)
24
+ return false;
25
+ const clean = text.trim();
26
+ if (clean.length < 3)
27
+ return false;
28
+ // If it's pure symbols/numbers, ignore
29
+ if (/^[^a-zA-Z\u4e00-\u9fa5]+$/.test(clean))
30
+ return false;
31
+ return true;
32
+ }
33
+ export async function extractL1Memories(params) {
34
+ const { messages, userId, sessionKey, sessionId, llmRunner, maxMessagesPerExtraction = 10, maxBackgroundMessages = 5, previousSceneName, existingSceneNames, activeSkill, skillHints } = params;
35
+ if (messages.length === 0) {
36
+ return { success: true, extractedCount: 0, records: [], sceneNames: [] };
37
+ }
38
+ const qualifiedMessages = messages.filter((m) => shouldExtractL1(m.messageText));
39
+ if (qualifiedMessages.length === 0) {
40
+ return { success: true, extractedCount: 0, records: [], sceneNames: [] };
41
+ }
42
+ const newMessages = qualifiedMessages.slice(-maxMessagesPerExtraction);
43
+ const bgEndIdx = qualifiedMessages.length - newMessages.length;
44
+ const backgroundMessages = bgEndIdx > 0
45
+ ? qualifiedMessages.slice(Math.max(0, bgEndIdx - maxBackgroundMessages), bgEndIdx)
46
+ : [];
47
+ const userPrompt = formatExtractionPrompt({
48
+ newMessages,
49
+ backgroundMessages,
50
+ previousSceneName,
51
+ existingSceneNames,
52
+ activeSkill,
53
+ skillHints
54
+ });
55
+ let rawResult;
56
+ try {
57
+ rawResult = await llmRunner.run({
58
+ prompt: userPrompt,
59
+ systemPrompt: EXTRACT_MEMORIES_SYSTEM_PROMPT,
60
+ taskId: "l1-extraction",
61
+ timeoutMs: 120_000
62
+ });
63
+ }
64
+ catch (err) {
65
+ const errorMessage = err instanceof Error ? err.message : String(err);
66
+ console.error("[BrainRouter] LLM extraction failed:", err);
67
+ return { success: false, extractedCount: 0, records: [], sceneNames: [], errorMessage };
68
+ }
69
+ const parsedScenes = parseExtractionResult(rawResult);
70
+ const records = [];
71
+ const sceneNames = [];
72
+ const nowStr = new Date().toISOString();
73
+ for (const scene of parsedScenes) {
74
+ sceneNames.push(scene.scene_name);
75
+ for (const mem of scene.memories) {
76
+ const config = getMemoryTypeConfig(mem.type);
77
+ records.push({
78
+ id: `l1_${sessionKey}_${Date.now()}_${crypto.randomBytes(4).toString("hex")}`,
79
+ userId,
80
+ sessionKey,
81
+ sessionId,
82
+ content: mem.content,
83
+ type: mem.type,
84
+ priority: mem.priority,
85
+ sceneName: scene.scene_name,
86
+ skillTag: mem.skill_tag || activeSkill || "",
87
+ halfLifeDays: config.halfLifeDays,
88
+ supersededBy: null,
89
+ timestampStr: "", // Phase 1: Not strictly tracking time from LLM, just raw extraction
90
+ timestampStart: "",
91
+ timestampEnd: "",
92
+ createdTime: nowStr,
93
+ updatedTime: nowStr,
94
+ metadata: mem.metadata,
95
+ confidence: mem.confidence ?? config.defaultConfidence,
96
+ status: "active",
97
+ sourceKind: mem.sourceKind,
98
+ verificationStatus: mem.verificationStatus,
99
+ repoPaths: mem.repoPaths,
100
+ filePaths: mem.filePaths,
101
+ commands: mem.commands,
102
+ // ACE fields — zero on creation, updated by citation tracking
103
+ citationCount: 0,
104
+ lastCitedAt: null,
105
+ neverCitedCount: 0,
106
+ archived: false,
107
+ });
108
+ }
109
+ }
110
+ return {
111
+ success: true,
112
+ extractedCount: records.length,
113
+ records,
114
+ sceneNames
115
+ };
116
+ }
117
+ function parseExtractionResult(raw) {
118
+ try {
119
+ let cleaned = raw.trim();
120
+ if (cleaned.startsWith("\`\`\`")) {
121
+ cleaned = cleaned.replace(/^\`\`\`(?:json)?\s*\n?/, "").replace(/\n?\`\`\`\s*$/, "");
122
+ }
123
+ const match = cleaned.match(/\[[\s\S]*\]/);
124
+ if (!match)
125
+ return [];
126
+ const parsed = JSON.parse(match[0]);
127
+ if (!Array.isArray(parsed))
128
+ return [];
129
+ const scenes = [];
130
+ for (const item of parsed) {
131
+ if (!item || typeof item !== "object")
132
+ continue;
133
+ const s = item;
134
+ const memories = Array.isArray(s.memories) ? s.memories.map((m) => ({
135
+ content: String(m.content || ""),
136
+ type: parseMemoryType(m.type),
137
+ priority: clampNumber(m.priority, 0, 100, 50),
138
+ skill_tag: m.skill_tag ? String(m.skill_tag) : undefined,
139
+ confidence: typeof m.confidence === "number" ? clampNumber(m.confidence, 0, 1, 0.65) : undefined,
140
+ sourceKind: parseSourceKind(m.sourceKind ?? m.source_kind),
141
+ verificationStatus: parseVerificationStatus(m.verificationStatus ?? m.verification_status),
142
+ repoPaths: parseStringArray(m.repoPaths ?? m.repo_paths),
143
+ filePaths: parseStringArray(m.filePaths ?? m.file_paths),
144
+ commands: parseStringArray(m.commands),
145
+ metadata: m.metadata && typeof m.metadata === "object" ? m.metadata : {}
146
+ })).filter((m) => m.content.length > 0) : [];
147
+ scenes.push({
148
+ scene_name: String(s.scene_name || "Unknown Scene"),
149
+ memories
150
+ });
151
+ }
152
+ return scenes;
153
+ }
154
+ catch (err) {
155
+ console.error("[BrainRouter] Failed to parse extraction result", err);
156
+ return [];
157
+ }
158
+ }
159
+ function parseMemoryType(value) {
160
+ const candidate = String(value || "");
161
+ return ALLOWED_MEMORY_TYPES.has(candidate) ? candidate : "episodic";
162
+ }
163
+ function parseSourceKind(value) {
164
+ const candidate = String(value || "");
165
+ return ALLOWED_SOURCE_KINDS.has(candidate) ? candidate : "model_inference";
166
+ }
167
+ function parseVerificationStatus(value) {
168
+ const candidate = String(value || "");
169
+ return ALLOWED_VERIFICATION_STATUSES.has(candidate) ? candidate : "unverified";
170
+ }
171
+ function parseStringArray(value) {
172
+ if (!Array.isArray(value))
173
+ return [];
174
+ return [...new Set(value.map((item) => String(item).trim()).filter(Boolean))];
175
+ }
176
+ function clampNumber(value, min, max, fallback) {
177
+ return typeof value === "number" && Number.isFinite(value)
178
+ ? Math.min(max, Math.max(min, value))
179
+ : fallback;
180
+ }
@@ -0,0 +1,10 @@
1
+ import type { L1Record, L2SceneRecord, LLMRunner } from "@brainrouter/types";
2
+ export declare function detectDirectionShift(params: {
3
+ activeScene: L2SceneRecord;
4
+ newL1Records: L1Record[];
5
+ llmRunner: LLMRunner;
6
+ }): Promise<{
7
+ shift: boolean;
8
+ confidence: number;
9
+ reason: string;
10
+ }>;
@@ -0,0 +1,27 @@
1
+ import { L2_DIRECTION_SHIFT_SYSTEM_PROMPT, formatL2DirectionShiftPrompt } from "../prompts/l2-direction-shift.js";
2
+ export async function detectDirectionShift(params) {
3
+ const { activeScene, newL1Records, llmRunner } = params;
4
+ try {
5
+ const prompt = formatL2DirectionShiftPrompt(activeScene.sceneName, activeScene.summaryMd, newL1Records.map(r => ({ content: r.content, type: r.type })));
6
+ const response = await llmRunner.run({
7
+ prompt,
8
+ systemPrompt: L2_DIRECTION_SHIFT_SYSTEM_PROMPT,
9
+ taskId: "l2-direction-shift",
10
+ timeoutMs: 30_000,
11
+ });
12
+ const jsonMatch = response.match(/\{[\s\S]*\}/);
13
+ if (!jsonMatch) {
14
+ throw new Error("No JSON object found in LLM response");
15
+ }
16
+ const parsed = JSON.parse(jsonMatch[0]);
17
+ return {
18
+ shift: Boolean(parsed.shift),
19
+ confidence: Number(parsed.confidence) || 0,
20
+ reason: String(parsed.reason) || "",
21
+ };
22
+ }
23
+ catch (err) {
24
+ console.error(`[BrainRouter] L2 direction shift detection failed:`, err.message);
25
+ return { shift: false, confidence: 0, reason: "Error" };
26
+ }
27
+ }
@@ -0,0 +1,15 @@
1
+ import type { IMemoryStore } from "@brainrouter/types";
2
+ import type { LLMRunner } from "@brainrouter/types";
3
+ /**
4
+ * L2 Scene Pipeline
5
+ * Groups L1 memories by scene_name and asks the LLM to produce
6
+ * a Markdown summary for each scene. Updates heat scores.
7
+ */
8
+ export declare function distillScenes(params: {
9
+ userId: string;
10
+ store: IMemoryStore;
11
+ llmRunner: LLMRunner;
12
+ }): Promise<{
13
+ scenesDistilled: number;
14
+ sceneNames: string[];
15
+ }>;