nodedex 0.1.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 (469) hide show
  1. package/adapters/claude-code-watcher.mjs +336 -0
  2. package/adapters/hermes-statedb-watcher.mjs +234 -0
  3. package/adapters/nodedex-capture-core.mjs +129 -0
  4. package/adapters/nodedex-capture.mjs +169 -0
  5. package/dist/agent-protocol.d.ts +7 -0
  6. package/dist/agent-protocol.d.ts.map +1 -0
  7. package/dist/agent-protocol.js +38 -0
  8. package/dist/agent-protocol.js.map +1 -0
  9. package/dist/api-server.d.ts +5 -0
  10. package/dist/api-server.d.ts.map +1 -0
  11. package/dist/api-server.js +351 -0
  12. package/dist/api-server.js.map +1 -0
  13. package/dist/boot-env.d.ts +2 -0
  14. package/dist/boot-env.d.ts.map +1 -0
  15. package/dist/boot-env.js +12 -0
  16. package/dist/boot-env.js.map +1 -0
  17. package/dist/engine/__tests__/search-core.test.d.ts +2 -0
  18. package/dist/engine/__tests__/search-core.test.d.ts.map +1 -0
  19. package/dist/engine/__tests__/search-core.test.js +139 -0
  20. package/dist/engine/__tests__/search-core.test.js.map +1 -0
  21. package/dist/engine/ai-provider.d.ts +45 -0
  22. package/dist/engine/ai-provider.d.ts.map +1 -0
  23. package/dist/engine/ai-provider.js +5 -0
  24. package/dist/engine/ai-provider.js.map +1 -0
  25. package/dist/engine/embeddings.d.ts +51 -0
  26. package/dist/engine/embeddings.d.ts.map +1 -0
  27. package/dist/engine/embeddings.js +89 -0
  28. package/dist/engine/embeddings.js.map +1 -0
  29. package/dist/engine/providers/__tests__/failure-policy.test.d.ts +2 -0
  30. package/dist/engine/providers/__tests__/failure-policy.test.d.ts.map +1 -0
  31. package/dist/engine/providers/__tests__/failure-policy.test.js +134 -0
  32. package/dist/engine/providers/__tests__/failure-policy.test.js.map +1 -0
  33. package/dist/engine/providers/__tests__/model-caps.test.d.ts +2 -0
  34. package/dist/engine/providers/__tests__/model-caps.test.d.ts.map +1 -0
  35. package/dist/engine/providers/__tests__/model-caps.test.js +38 -0
  36. package/dist/engine/providers/__tests__/model-caps.test.js.map +1 -0
  37. package/dist/engine/providers/__tests__/openai-structured.test.d.ts +2 -0
  38. package/dist/engine/providers/__tests__/openai-structured.test.d.ts.map +1 -0
  39. package/dist/engine/providers/__tests__/openai-structured.test.js +73 -0
  40. package/dist/engine/providers/__tests__/openai-structured.test.js.map +1 -0
  41. package/dist/engine/providers/__tests__/usage-ledger.test.d.ts +2 -0
  42. package/dist/engine/providers/__tests__/usage-ledger.test.d.ts.map +1 -0
  43. package/dist/engine/providers/__tests__/usage-ledger.test.js +108 -0
  44. package/dist/engine/providers/__tests__/usage-ledger.test.js.map +1 -0
  45. package/dist/engine/providers/anthropic.d.ts +17 -0
  46. package/dist/engine/providers/anthropic.d.ts.map +1 -0
  47. package/dist/engine/providers/anthropic.js +125 -0
  48. package/dist/engine/providers/anthropic.js.map +1 -0
  49. package/dist/engine/providers/failure-policy.d.ts +56 -0
  50. package/dist/engine/providers/failure-policy.d.ts.map +1 -0
  51. package/dist/engine/providers/failure-policy.js +120 -0
  52. package/dist/engine/providers/failure-policy.js.map +1 -0
  53. package/dist/engine/providers/gemini.d.ts +22 -0
  54. package/dist/engine/providers/gemini.d.ts.map +1 -0
  55. package/dist/engine/providers/gemini.js +180 -0
  56. package/dist/engine/providers/gemini.js.map +1 -0
  57. package/dist/engine/providers/index.d.ts +8 -0
  58. package/dist/engine/providers/index.d.ts.map +1 -0
  59. package/dist/engine/providers/index.js +67 -0
  60. package/dist/engine/providers/index.js.map +1 -0
  61. package/dist/engine/providers/local.d.ts +12 -0
  62. package/dist/engine/providers/local.d.ts.map +1 -0
  63. package/dist/engine/providers/local.js +46 -0
  64. package/dist/engine/providers/local.js.map +1 -0
  65. package/dist/engine/providers/model-caps.d.ts +6 -0
  66. package/dist/engine/providers/model-caps.d.ts.map +1 -0
  67. package/dist/engine/providers/model-caps.js +49 -0
  68. package/dist/engine/providers/model-caps.js.map +1 -0
  69. package/dist/engine/providers/openai.d.ts +30 -0
  70. package/dist/engine/providers/openai.d.ts.map +1 -0
  71. package/dist/engine/providers/openai.js +309 -0
  72. package/dist/engine/providers/openai.js.map +1 -0
  73. package/dist/engine/providers/usage-ledger.d.ts +69 -0
  74. package/dist/engine/providers/usage-ledger.d.ts.map +1 -0
  75. package/dist/engine/providers/usage-ledger.js +209 -0
  76. package/dist/engine/providers/usage-ledger.js.map +1 -0
  77. package/dist/engine/search-core.d.ts +40 -0
  78. package/dist/engine/search-core.d.ts.map +1 -0
  79. package/dist/engine/search-core.js +109 -0
  80. package/dist/engine/search-core.js.map +1 -0
  81. package/dist/engine/vector-math.d.ts +5 -0
  82. package/dist/engine/vector-math.d.ts.map +1 -0
  83. package/dist/engine/vector-math.js +25 -0
  84. package/dist/engine/vector-math.js.map +1 -0
  85. package/dist/home-env.d.ts +26 -0
  86. package/dist/home-env.d.ts.map +1 -0
  87. package/dist/home-env.js +87 -0
  88. package/dist/home-env.js.map +1 -0
  89. package/dist/mcp-server.d.ts +13 -0
  90. package/dist/mcp-server.d.ts.map +1 -0
  91. package/dist/mcp-server.js +79 -0
  92. package/dist/mcp-server.js.map +1 -0
  93. package/dist/middleware/auth.d.ts +23 -0
  94. package/dist/middleware/auth.d.ts.map +1 -0
  95. package/dist/middleware/auth.js +104 -0
  96. package/dist/middleware/auth.js.map +1 -0
  97. package/dist/middleware/auto-recall.d.ts +7 -0
  98. package/dist/middleware/auto-recall.d.ts.map +1 -0
  99. package/dist/middleware/auto-recall.js +257 -0
  100. package/dist/middleware/auto-recall.js.map +1 -0
  101. package/dist/middleware/auto-reflect.d.ts +4 -0
  102. package/dist/middleware/auto-reflect.d.ts.map +1 -0
  103. package/dist/middleware/auto-reflect.js +5 -0
  104. package/dist/middleware/auto-reflect.js.map +1 -0
  105. package/dist/middleware/reflect/apply-flag-verdict.d.ts +27 -0
  106. package/dist/middleware/reflect/apply-flag-verdict.d.ts.map +1 -0
  107. package/dist/middleware/reflect/apply-flag-verdict.js +57 -0
  108. package/dist/middleware/reflect/apply-flag-verdict.js.map +1 -0
  109. package/dist/middleware/reflect/arc-entity-resolve.d.ts +29 -0
  110. package/dist/middleware/reflect/arc-entity-resolve.d.ts.map +1 -0
  111. package/dist/middleware/reflect/arc-entity-resolve.js +356 -0
  112. package/dist/middleware/reflect/arc-entity-resolve.js.map +1 -0
  113. package/dist/middleware/reflect/arc-inactivity-timer.d.ts +47 -0
  114. package/dist/middleware/reflect/arc-inactivity-timer.d.ts.map +1 -0
  115. package/dist/middleware/reflect/arc-inactivity-timer.js +175 -0
  116. package/dist/middleware/reflect/arc-inactivity-timer.js.map +1 -0
  117. package/dist/middleware/reflect/arc-pipeline.d.ts +33 -0
  118. package/dist/middleware/reflect/arc-pipeline.d.ts.map +1 -0
  119. package/dist/middleware/reflect/arc-pipeline.js +498 -0
  120. package/dist/middleware/reflect/arc-pipeline.js.map +1 -0
  121. package/dist/middleware/reflect/comprehend-pergroup.d.ts +100 -0
  122. package/dist/middleware/reflect/comprehend-pergroup.d.ts.map +1 -0
  123. package/dist/middleware/reflect/comprehend-pergroup.js +610 -0
  124. package/dist/middleware/reflect/comprehend-pergroup.js.map +1 -0
  125. package/dist/middleware/reflect/comprehend.d.ts +237 -0
  126. package/dist/middleware/reflect/comprehend.d.ts.map +1 -0
  127. package/dist/middleware/reflect/comprehend.js +706 -0
  128. package/dist/middleware/reflect/comprehend.js.map +1 -0
  129. package/dist/middleware/reflect/config.d.ts +34 -0
  130. package/dist/middleware/reflect/config.d.ts.map +1 -0
  131. package/dist/middleware/reflect/config.js +131 -0
  132. package/dist/middleware/reflect/config.js.map +1 -0
  133. package/dist/middleware/reflect/context.d.ts +138 -0
  134. package/dist/middleware/reflect/context.d.ts.map +1 -0
  135. package/dist/middleware/reflect/context.js +619 -0
  136. package/dist/middleware/reflect/context.js.map +1 -0
  137. package/dist/middleware/reflect/cost-breakdown.d.ts +69 -0
  138. package/dist/middleware/reflect/cost-breakdown.d.ts.map +1 -0
  139. package/dist/middleware/reflect/cost-breakdown.js +63 -0
  140. package/dist/middleware/reflect/cost-breakdown.js.map +1 -0
  141. package/dist/middleware/reflect/cost-guard.d.ts +102 -0
  142. package/dist/middleware/reflect/cost-guard.d.ts.map +1 -0
  143. package/dist/middleware/reflect/cost-guard.js +243 -0
  144. package/dist/middleware/reflect/cost-guard.js.map +1 -0
  145. package/dist/middleware/reflect/cost-pricing.d.ts +54 -0
  146. package/dist/middleware/reflect/cost-pricing.d.ts.map +1 -0
  147. package/dist/middleware/reflect/cost-pricing.js +148 -0
  148. package/dist/middleware/reflect/cost-pricing.js.map +1 -0
  149. package/dist/middleware/reflect/cross-group-link.d.ts +61 -0
  150. package/dist/middleware/reflect/cross-group-link.d.ts.map +1 -0
  151. package/dist/middleware/reflect/cross-group-link.js +212 -0
  152. package/dist/middleware/reflect/cross-group-link.js.map +1 -0
  153. package/dist/middleware/reflect/dedup-by-source-and-value.d.ts +70 -0
  154. package/dist/middleware/reflect/dedup-by-source-and-value.d.ts.map +1 -0
  155. package/dist/middleware/reflect/dedup-by-source-and-value.js +0 -0
  156. package/dist/middleware/reflect/dedup-by-source-and-value.js.map +1 -0
  157. package/dist/middleware/reflect/describe-roots.d.ts +58 -0
  158. package/dist/middleware/reflect/describe-roots.d.ts.map +1 -0
  159. package/dist/middleware/reflect/describe-roots.js +266 -0
  160. package/dist/middleware/reflect/describe-roots.js.map +1 -0
  161. package/dist/middleware/reflect/flag-reviewer-startup.d.ts +16 -0
  162. package/dist/middleware/reflect/flag-reviewer-startup.d.ts.map +1 -0
  163. package/dist/middleware/reflect/flag-reviewer-startup.js +107 -0
  164. package/dist/middleware/reflect/flag-reviewer-startup.js.map +1 -0
  165. package/dist/middleware/reflect/flag-reviewer.d.ts +69 -0
  166. package/dist/middleware/reflect/flag-reviewer.d.ts.map +1 -0
  167. package/dist/middleware/reflect/flag-reviewer.js +520 -0
  168. package/dist/middleware/reflect/flag-reviewer.js.map +1 -0
  169. package/dist/middleware/reflect/inline-dedup.d.ts +26 -0
  170. package/dist/middleware/reflect/inline-dedup.d.ts.map +1 -0
  171. package/dist/middleware/reflect/inline-dedup.js +131 -0
  172. package/dist/middleware/reflect/inline-dedup.js.map +1 -0
  173. package/dist/middleware/reflect/justify-decisions.d.ts +37 -0
  174. package/dist/middleware/reflect/justify-decisions.d.ts.map +1 -0
  175. package/dist/middleware/reflect/justify-decisions.js +159 -0
  176. package/dist/middleware/reflect/justify-decisions.js.map +1 -0
  177. package/dist/middleware/reflect/nl-accept.d.ts +35 -0
  178. package/dist/middleware/reflect/nl-accept.d.ts.map +1 -0
  179. package/dist/middleware/reflect/nl-accept.js +167 -0
  180. package/dist/middleware/reflect/nl-accept.js.map +1 -0
  181. package/dist/middleware/reflect/pass0.d.ts +20 -0
  182. package/dist/middleware/reflect/pass0.d.ts.map +1 -0
  183. package/dist/middleware/reflect/pass0.js +423 -0
  184. package/dist/middleware/reflect/pass0.js.map +1 -0
  185. package/dist/middleware/reflect/pass1.d.ts +17 -0
  186. package/dist/middleware/reflect/pass1.d.ts.map +1 -0
  187. package/dist/middleware/reflect/pass1.js +241 -0
  188. package/dist/middleware/reflect/pass1.js.map +1 -0
  189. package/dist/middleware/reflect/pass2-quarantine.d.ts +129 -0
  190. package/dist/middleware/reflect/pass2-quarantine.d.ts.map +1 -0
  191. package/dist/middleware/reflect/pass2-quarantine.js +272 -0
  192. package/dist/middleware/reflect/pass2-quarantine.js.map +1 -0
  193. package/dist/middleware/reflect/pass2-seams.d.ts +205 -0
  194. package/dist/middleware/reflect/pass2-seams.d.ts.map +1 -0
  195. package/dist/middleware/reflect/pass2-seams.js +279 -0
  196. package/dist/middleware/reflect/pass2-seams.js.map +1 -0
  197. package/dist/middleware/reflect/pass2-split-orchestrator.d.ts +37 -0
  198. package/dist/middleware/reflect/pass2-split-orchestrator.d.ts.map +1 -0
  199. package/dist/middleware/reflect/pass2-split-orchestrator.js +531 -0
  200. package/dist/middleware/reflect/pass2-split-orchestrator.js.map +1 -0
  201. package/dist/middleware/reflect/pass2.d.ts +17 -0
  202. package/dist/middleware/reflect/pass2.d.ts.map +1 -0
  203. package/dist/middleware/reflect/pass2.js +324 -0
  204. package/dist/middleware/reflect/pass2.js.map +1 -0
  205. package/dist/middleware/reflect/pass2a.d.ts +141 -0
  206. package/dist/middleware/reflect/pass2a.d.ts.map +1 -0
  207. package/dist/middleware/reflect/pass2a.js +404 -0
  208. package/dist/middleware/reflect/pass2a.js.map +1 -0
  209. package/dist/middleware/reflect/pass2b.d.ts +108 -0
  210. package/dist/middleware/reflect/pass2b.d.ts.map +1 -0
  211. package/dist/middleware/reflect/pass2b.js +480 -0
  212. package/dist/middleware/reflect/pass2b.js.map +1 -0
  213. package/dist/middleware/reflect/pass2c.d.ts +113 -0
  214. package/dist/middleware/reflect/pass2c.d.ts.map +1 -0
  215. package/dist/middleware/reflect/pass2c.js +360 -0
  216. package/dist/middleware/reflect/pass2c.js.map +1 -0
  217. package/dist/middleware/reflect/pass3-batch.d.ts +62 -0
  218. package/dist/middleware/reflect/pass3-batch.d.ts.map +1 -0
  219. package/dist/middleware/reflect/pass3-batch.js +139 -0
  220. package/dist/middleware/reflect/pass3-batch.js.map +1 -0
  221. package/dist/middleware/reflect/pass3.d.ts +23 -0
  222. package/dist/middleware/reflect/pass3.d.ts.map +1 -0
  223. package/dist/middleware/reflect/pass3.js +371 -0
  224. package/dist/middleware/reflect/pass3.js.map +1 -0
  225. package/dist/middleware/reflect/pass4-slice.d.ts +25 -0
  226. package/dist/middleware/reflect/pass4-slice.d.ts.map +1 -0
  227. package/dist/middleware/reflect/pass4-slice.js +315 -0
  228. package/dist/middleware/reflect/pass4-slice.js.map +1 -0
  229. package/dist/middleware/reflect/pass4.d.ts +30 -0
  230. package/dist/middleware/reflect/pass4.d.ts.map +1 -0
  231. package/dist/middleware/reflect/pass4.js +193 -0
  232. package/dist/middleware/reflect/pass4.js.map +1 -0
  233. package/dist/middleware/reflect/pass5.d.ts +22 -0
  234. package/dist/middleware/reflect/pass5.d.ts.map +1 -0
  235. package/dist/middleware/reflect/pass5.js +178 -0
  236. package/dist/middleware/reflect/pass5.js.map +1 -0
  237. package/dist/middleware/reflect/pass_judge.d.ts +44 -0
  238. package/dist/middleware/reflect/pass_judge.d.ts.map +1 -0
  239. package/dist/middleware/reflect/pass_judge.js +263 -0
  240. package/dist/middleware/reflect/pass_judge.js.map +1 -0
  241. package/dist/middleware/reflect/pipeline-flags.d.ts +140 -0
  242. package/dist/middleware/reflect/pipeline-flags.d.ts.map +1 -0
  243. package/dist/middleware/reflect/pipeline-flags.js +314 -0
  244. package/dist/middleware/reflect/pipeline-flags.js.map +1 -0
  245. package/dist/middleware/reflect/pipeline.d.ts +237 -0
  246. package/dist/middleware/reflect/pipeline.d.ts.map +1 -0
  247. package/dist/middleware/reflect/pipeline.js +3114 -0
  248. package/dist/middleware/reflect/pipeline.js.map +1 -0
  249. package/dist/middleware/reflect/promptOverride.d.ts +14 -0
  250. package/dist/middleware/reflect/promptOverride.d.ts.map +1 -0
  251. package/dist/middleware/reflect/promptOverride.js +28 -0
  252. package/dist/middleware/reflect/promptOverride.js.map +1 -0
  253. package/dist/middleware/reflect/provenance-check.d.ts +48 -0
  254. package/dist/middleware/reflect/provenance-check.d.ts.map +1 -0
  255. package/dist/middleware/reflect/provenance-check.js +180 -0
  256. package/dist/middleware/reflect/provenance-check.js.map +1 -0
  257. package/dist/middleware/reflect/provenance-reviewer.d.ts +52 -0
  258. package/dist/middleware/reflect/provenance-reviewer.d.ts.map +1 -0
  259. package/dist/middleware/reflect/provenance-reviewer.js +253 -0
  260. package/dist/middleware/reflect/provenance-reviewer.js.map +1 -0
  261. package/dist/middleware/reflect/prune-collapsed-types.d.ts +11 -0
  262. package/dist/middleware/reflect/prune-collapsed-types.d.ts.map +1 -0
  263. package/dist/middleware/reflect/prune-collapsed-types.js +32 -0
  264. package/dist/middleware/reflect/prune-collapsed-types.js.map +1 -0
  265. package/dist/middleware/reflect/recognize-root.d.ts +75 -0
  266. package/dist/middleware/reflect/recognize-root.d.ts.map +1 -0
  267. package/dist/middleware/reflect/recognize-root.js +204 -0
  268. package/dist/middleware/reflect/recognize-root.js.map +1 -0
  269. package/dist/middleware/reflect/render-agent-flag.d.ts +25 -0
  270. package/dist/middleware/reflect/render-agent-flag.d.ts.map +1 -0
  271. package/dist/middleware/reflect/render-agent-flag.js +39 -0
  272. package/dist/middleware/reflect/render-agent-flag.js.map +1 -0
  273. package/dist/middleware/reflect/retrieve-graph-slice.d.ts +54 -0
  274. package/dist/middleware/reflect/retrieve-graph-slice.d.ts.map +1 -0
  275. package/dist/middleware/reflect/retrieve-graph-slice.js +173 -0
  276. package/dist/middleware/reflect/retrieve-graph-slice.js.map +1 -0
  277. package/dist/middleware/reflect/root-relatedness.d.ts +31 -0
  278. package/dist/middleware/reflect/root-relatedness.d.ts.map +1 -0
  279. package/dist/middleware/reflect/root-relatedness.js +92 -0
  280. package/dist/middleware/reflect/root-relatedness.js.map +1 -0
  281. package/dist/middleware/reflect/schema-heal.d.ts +22 -0
  282. package/dist/middleware/reflect/schema-heal.d.ts.map +1 -0
  283. package/dist/middleware/reflect/schema-heal.js +119 -0
  284. package/dist/middleware/reflect/schema-heal.js.map +1 -0
  285. package/dist/middleware/reflect/schema-validator.d.ts +85 -0
  286. package/dist/middleware/reflect/schema-validator.d.ts.map +1 -0
  287. package/dist/middleware/reflect/schema-validator.js +196 -0
  288. package/dist/middleware/reflect/schema-validator.js.map +1 -0
  289. package/dist/middleware/reflect/stage-audit-graph.d.ts +115 -0
  290. package/dist/middleware/reflect/stage-audit-graph.d.ts.map +1 -0
  291. package/dist/middleware/reflect/stage-audit-graph.js +563 -0
  292. package/dist/middleware/reflect/stage-audit-graph.js.map +1 -0
  293. package/dist/middleware/reflect/stage-d-resolve-graph.d.ts +87 -0
  294. package/dist/middleware/reflect/stage-d-resolve-graph.d.ts.map +1 -0
  295. package/dist/middleware/reflect/stage-d-resolve-graph.js +256 -0
  296. package/dist/middleware/reflect/stage-d-resolve-graph.js.map +1 -0
  297. package/dist/middleware/reflect/synthesizeFromSceneCard.d.ts +15 -0
  298. package/dist/middleware/reflect/synthesizeFromSceneCard.d.ts.map +1 -0
  299. package/dist/middleware/reflect/synthesizeFromSceneCard.js +91 -0
  300. package/dist/middleware/reflect/synthesizeFromSceneCard.js.map +1 -0
  301. package/dist/middleware/reflect/types.d.ts +261 -0
  302. package/dist/middleware/reflect/types.d.ts.map +1 -0
  303. package/dist/middleware/reflect/types.js +3 -0
  304. package/dist/middleware/reflect/types.js.map +1 -0
  305. package/dist/middleware/reflect/v2-integrate.d.ts +120 -0
  306. package/dist/middleware/reflect/v2-integrate.d.ts.map +1 -0
  307. package/dist/middleware/reflect/v2-integrate.js +388 -0
  308. package/dist/middleware/reflect/v2-integrate.js.map +1 -0
  309. package/dist/middleware/reflect/v2-judge.d.ts +44 -0
  310. package/dist/middleware/reflect/v2-judge.d.ts.map +1 -0
  311. package/dist/middleware/reflect/v2-judge.js +191 -0
  312. package/dist/middleware/reflect/v2-judge.js.map +1 -0
  313. package/dist/relation-sets.d.ts +2 -0
  314. package/dist/relation-sets.d.ts.map +1 -0
  315. package/dist/relation-sets.js +35 -0
  316. package/dist/relation-sets.js.map +1 -0
  317. package/dist/routes/__tests__/flags.test.d.ts +2 -0
  318. package/dist/routes/__tests__/flags.test.d.ts.map +1 -0
  319. package/dist/routes/__tests__/flags.test.js +257 -0
  320. package/dist/routes/__tests__/flags.test.js.map +1 -0
  321. package/dist/routes/__tests__/models-catalog.test.d.ts +2 -0
  322. package/dist/routes/__tests__/models-catalog.test.d.ts.map +1 -0
  323. package/dist/routes/__tests__/models-catalog.test.js +130 -0
  324. package/dist/routes/__tests__/models-catalog.test.js.map +1 -0
  325. package/dist/routes/__tests__/reflect-pause-drain.test.d.ts +2 -0
  326. package/dist/routes/__tests__/reflect-pause-drain.test.d.ts.map +1 -0
  327. package/dist/routes/__tests__/reflect-pause-drain.test.js +38 -0
  328. package/dist/routes/__tests__/reflect-pause-drain.test.js.map +1 -0
  329. package/dist/routes/__tests__/spend-pause-drain.test.d.ts +2 -0
  330. package/dist/routes/__tests__/spend-pause-drain.test.d.ts.map +1 -0
  331. package/dist/routes/__tests__/spend-pause-drain.test.js +38 -0
  332. package/dist/routes/__tests__/spend-pause-drain.test.js.map +1 -0
  333. package/dist/routes/admin.d.ts +49 -0
  334. package/dist/routes/admin.d.ts.map +1 -0
  335. package/dist/routes/admin.js +471 -0
  336. package/dist/routes/admin.js.map +1 -0
  337. package/dist/routes/blocks.d.ts +4 -0
  338. package/dist/routes/blocks.d.ts.map +1 -0
  339. package/dist/routes/blocks.js +893 -0
  340. package/dist/routes/blocks.js.map +1 -0
  341. package/dist/routes/chat-proxy.d.ts +5 -0
  342. package/dist/routes/chat-proxy.d.ts.map +1 -0
  343. package/dist/routes/chat-proxy.js +225 -0
  344. package/dist/routes/chat-proxy.js.map +1 -0
  345. package/dist/routes/conversations.d.ts +4 -0
  346. package/dist/routes/conversations.d.ts.map +1 -0
  347. package/dist/routes/conversations.js +139 -0
  348. package/dist/routes/conversations.js.map +1 -0
  349. package/dist/routes/flags.d.ts +4 -0
  350. package/dist/routes/flags.d.ts.map +1 -0
  351. package/dist/routes/flags.js +151 -0
  352. package/dist/routes/flags.js.map +1 -0
  353. package/dist/routes/inject.d.ts +4 -0
  354. package/dist/routes/inject.d.ts.map +1 -0
  355. package/dist/routes/inject.js +183 -0
  356. package/dist/routes/inject.js.map +1 -0
  357. package/dist/routes/mcp-http.d.ts +5 -0
  358. package/dist/routes/mcp-http.d.ts.map +1 -0
  359. package/dist/routes/mcp-http.js +94 -0
  360. package/dist/routes/mcp-http.js.map +1 -0
  361. package/dist/routes/quarantine.d.ts +4 -0
  362. package/dist/routes/quarantine.d.ts.map +1 -0
  363. package/dist/routes/quarantine.js +66 -0
  364. package/dist/routes/quarantine.js.map +1 -0
  365. package/dist/routes/recall.d.ts +5 -0
  366. package/dist/routes/recall.d.ts.map +1 -0
  367. package/dist/routes/recall.js +573 -0
  368. package/dist/routes/recall.js.map +1 -0
  369. package/dist/routes/reflect.d.ts +5 -0
  370. package/dist/routes/reflect.d.ts.map +1 -0
  371. package/dist/routes/reflect.js +231 -0
  372. package/dist/routes/reflect.js.map +1 -0
  373. package/dist/routes/session.d.ts +4 -0
  374. package/dist/routes/session.d.ts.map +1 -0
  375. package/dist/routes/session.js +418 -0
  376. package/dist/routes/session.js.map +1 -0
  377. package/dist/routes/state.d.ts +116 -0
  378. package/dist/routes/state.d.ts.map +1 -0
  379. package/dist/routes/state.js +621 -0
  380. package/dist/routes/state.js.map +1 -0
  381. package/dist/routes/usage.d.ts +3 -0
  382. package/dist/routes/usage.d.ts.map +1 -0
  383. package/dist/routes/usage.js +141 -0
  384. package/dist/routes/usage.js.map +1 -0
  385. package/dist/routes/workspace.d.ts +5 -0
  386. package/dist/routes/workspace.d.ts.map +1 -0
  387. package/dist/routes/workspace.js +435 -0
  388. package/dist/routes/workspace.js.map +1 -0
  389. package/dist/server.d.ts +13 -0
  390. package/dist/server.d.ts.map +1 -0
  391. package/dist/server.js +298 -0
  392. package/dist/server.js.map +1 -0
  393. package/dist/store/__tests__/backup.test.d.ts +2 -0
  394. package/dist/store/__tests__/backup.test.d.ts.map +1 -0
  395. package/dist/store/__tests__/backup.test.js +53 -0
  396. package/dist/store/__tests__/backup.test.js.map +1 -0
  397. package/dist/store/__tests__/quality.test.d.ts +2 -0
  398. package/dist/store/__tests__/quality.test.d.ts.map +1 -0
  399. package/dist/store/__tests__/quality.test.js +75 -0
  400. package/dist/store/__tests__/quality.test.js.map +1 -0
  401. package/dist/store/backup.d.ts +14 -0
  402. package/dist/store/backup.d.ts.map +1 -0
  403. package/dist/store/backup.js +95 -0
  404. package/dist/store/backup.js.map +1 -0
  405. package/dist/store/database.d.ts +407 -0
  406. package/dist/store/database.d.ts.map +1 -0
  407. package/dist/store/database.js +2004 -0
  408. package/dist/store/database.js.map +1 -0
  409. package/dist/store/quality.d.ts +25 -0
  410. package/dist/store/quality.d.ts.map +1 -0
  411. package/dist/store/quality.js +48 -0
  412. package/dist/store/quality.js.map +1 -0
  413. package/dist/tools/__tests__/assemble-block-chains.test.d.ts +2 -0
  414. package/dist/tools/__tests__/assemble-block-chains.test.d.ts.map +1 -0
  415. package/dist/tools/__tests__/assemble-block-chains.test.js +118 -0
  416. package/dist/tools/__tests__/assemble-block-chains.test.js.map +1 -0
  417. package/dist/tools/__tests__/filter-roots-by-concepts.test.d.ts +2 -0
  418. package/dist/tools/__tests__/filter-roots-by-concepts.test.d.ts.map +1 -0
  419. package/dist/tools/__tests__/filter-roots-by-concepts.test.js +68 -0
  420. package/dist/tools/__tests__/filter-roots-by-concepts.test.js.map +1 -0
  421. package/dist/tools/__tests__/flag-surface.test.d.ts +2 -0
  422. package/dist/tools/__tests__/flag-surface.test.d.ts.map +1 -0
  423. package/dist/tools/__tests__/flag-surface.test.js +130 -0
  424. package/dist/tools/__tests__/flag-surface.test.js.map +1 -0
  425. package/dist/tools/core.d.ts +5 -0
  426. package/dist/tools/core.d.ts.map +1 -0
  427. package/dist/tools/core.js +962 -0
  428. package/dist/tools/core.js.map +1 -0
  429. package/dist/tools/derive.d.ts +5 -0
  430. package/dist/tools/derive.d.ts.map +1 -0
  431. package/dist/tools/derive.js +182 -0
  432. package/dist/tools/derive.js.map +1 -0
  433. package/dist/tools/flag-surface.d.ts +26 -0
  434. package/dist/tools/flag-surface.d.ts.map +1 -0
  435. package/dist/tools/flag-surface.js +59 -0
  436. package/dist/tools/flag-surface.js.map +1 -0
  437. package/dist/tools/helpers.d.ts +99 -0
  438. package/dist/tools/helpers.d.ts.map +1 -0
  439. package/dist/tools/helpers.js +243 -0
  440. package/dist/tools/helpers.js.map +1 -0
  441. package/dist/tools/projects.d.ts +5 -0
  442. package/dist/tools/projects.d.ts.map +1 -0
  443. package/dist/tools/projects.js +175 -0
  444. package/dist/tools/projects.js.map +1 -0
  445. package/dist/tools/system.d.ts +5 -0
  446. package/dist/tools/system.d.ts.map +1 -0
  447. package/dist/tools/system.js +1361 -0
  448. package/dist/tools/system.js.map +1 -0
  449. package/dist/tools/tasks.d.ts +5 -0
  450. package/dist/tools/tasks.d.ts.map +1 -0
  451. package/dist/tools/tasks.js +289 -0
  452. package/dist/tools/tasks.js.map +1 -0
  453. package/package.json +69 -0
  454. package/scripts/nodedex-entry.mjs +396 -0
  455. package/tui-dist/App.js +185 -0
  456. package/tui-dist/api.js +197 -0
  457. package/tui-dist/cli.js +53 -0
  458. package/tui-dist/components.js +63 -0
  459. package/tui-dist/config.js +242 -0
  460. package/tui-dist/connect-snippets.js +98 -0
  461. package/tui-dist/feed.js +51 -0
  462. package/tui-dist/health.js +465 -0
  463. package/tui-dist/hooks.js +23 -0
  464. package/tui-dist/memory.js +220 -0
  465. package/tui-dist/onboarding.js +498 -0
  466. package/tui-dist/review.js +193 -0
  467. package/tui-dist/servers.js +556 -0
  468. package/tui-dist/smoke.js +15 -0
  469. package/tui-dist/theme.js +106 -0
@@ -0,0 +1,893 @@
1
+ // routes/blocks.ts — block CRUD + navigation + graph + review + conflicts + relations + agents
2
+ // Reference: api-server.v1.ts lines 328–1343
3
+ import { Router } from "express";
4
+ import { computeQualityScore } from "../store/quality.js";
5
+ import { CAUSAL_TRAVERSAL_RELS } from "../relation-sets.js";
6
+ import { deriveRootRelatedness } from "../middleware/reflect/root-relatedness.js";
7
+ export function createBlocksRouter(db) {
8
+ const router = Router();
9
+ // ─── Root-relatedness map (which roots relate + how, derived from edges) ──────
10
+ // Meaning-classified (dependency/containment/evolution/conflict/loose), directional.
11
+ // Read-only derivation, no LLM. The "Venn overlap" of project roots.
12
+ router.get("/api/roots/related", (_req, res) => {
13
+ try {
14
+ res.json(deriveRootRelatedness(db));
15
+ }
16
+ catch (e) {
17
+ res.status(500).json({ error: String(e) });
18
+ }
19
+ });
20
+ // ─── Review queue ────────────────────────────────────────────────
21
+ router.get("/api/blocks/review-queue", (req, res) => {
22
+ try {
23
+ const { project } = req.query;
24
+ let blocks = db.getAllBlocks().filter((b) => b.review_status === "needs_review");
25
+ if (project) {
26
+ // Same id-or-label resolution as /api/blocks — a project ID here used to silently match nothing.
27
+ const root = db.getBlock(project);
28
+ const prefix = (root && root.type === "project") ? root.label + "_" : project + "_";
29
+ blocks = blocks.filter((b) => (b.label || "").startsWith(prefix) ||
30
+ (root && (b.project_id === root.id || b.id === root.id)));
31
+ }
32
+ const result = blocks.map((b) => {
33
+ let unique = {};
34
+ try {
35
+ unique = (typeof b.content === "string" ? JSON.parse(b.content) : b.content)?.unique ?? {};
36
+ }
37
+ catch { /* */ }
38
+ return { id: b.id, label: b.label, type: b.type, essence: b.essence, review_reason: b.review_reason, unique, created_at: b.created_at };
39
+ });
40
+ res.json({ total: result.length, blocks: result });
41
+ }
42
+ catch (e) {
43
+ res.status(500).json({ error: e.message });
44
+ }
45
+ });
46
+ router.patch("/api/blocks/:id/review", (req, res) => {
47
+ try {
48
+ const { action } = req.body;
49
+ if (!["approve", "corrected"].includes(action))
50
+ return res.status(400).json({ error: "action must be 'approve' or 'corrected'" });
51
+ const block = db.getBlock(req.params.id);
52
+ if (!block)
53
+ return res.status(404).json({ error: "Block not found" });
54
+ db.updateBlock(block.id, { review_status: action === "approve" ? "reviewed_ok" : "corrected" });
55
+ if (action === "approve")
56
+ db.stampReflectedAt([block.id]);
57
+ res.json({ ok: true, review_status: action === "approve" ? "reviewed_ok" : "corrected" });
58
+ }
59
+ catch (e) {
60
+ res.status(500).json({ error: e.message });
61
+ }
62
+ });
63
+ // ─── Blocks list ────────────────────────────────────────────────
64
+ // This is the agent's QUERY surface (Rule-1 dead-end/constraint checks are filtered
65
+ // lists on this route). Failure discipline: a filter that didn't apply must be loud,
66
+ // never an empty array the agent reads as "truly none".
67
+ router.get("/api/blocks", (req, res) => {
68
+ try {
69
+ const { q, type, status, since, updated_since, project, label_prefix, label, flow_role, concept, detail, limit } = req.query;
70
+ const LIST_DETAILS = new Set(["surface", "content", "full"]);
71
+ const detailLevel = detail ?? "full";
72
+ if (!LIST_DETAILS.has(detailLevel)) {
73
+ return res.status(400).json({ error: `Unknown detail '${detail}'. Allowed on the list endpoint: surface, content, full. ('relations' is available on GET /api/blocks/:id.)` });
74
+ }
75
+ // getAllBlocks() excludes archived by design — an archived-status query needs the explicit fetch.
76
+ let blocks = status === "archived" ? db.getBlocksByStatus("archived") : db.getAllBlocks();
77
+ if (type)
78
+ blocks = blocks.filter((b) => b.type === type);
79
+ if (status) {
80
+ const TASK_STATUSES = new Set(["open", "in_progress", "done", "blocked"]);
81
+ if (type === "task" && TASK_STATUSES.has(status)) {
82
+ blocks = blocks.filter((b) => { try {
83
+ return JSON.parse(b.content)?.unique?.status === status;
84
+ }
85
+ catch {
86
+ return false;
87
+ } });
88
+ }
89
+ else {
90
+ blocks = blocks.filter((b) => b.status === status);
91
+ }
92
+ }
93
+ if (since)
94
+ blocks = blocks.filter((b) => b.created_at > since);
95
+ if (updated_since)
96
+ blocks = blocks.filter((b) => b.updated_at > updated_since || b.created_at > updated_since);
97
+ if (project) {
98
+ // Resolve id-or-label via getBlock (same dual resolution the single-block route has).
99
+ // Scope = the root + descendant sub-projects (project_id chain), unioned with the
100
+ // label-prefix namespace — strictly wider than the old prefix-only match, so no regression.
101
+ const root = db.getBlock(project);
102
+ if (root && root.type === "project") {
103
+ const scope = new Set([root.id]);
104
+ const projectBlocks = db.getAllBlocks().filter((b) => b.type === "project");
105
+ let grew = true;
106
+ while (grew) {
107
+ grew = false;
108
+ for (const p of projectBlocks) {
109
+ if (p.project_id && scope.has(p.project_id) && !scope.has(p.id)) {
110
+ scope.add(p.id);
111
+ grew = true;
112
+ }
113
+ }
114
+ }
115
+ const prefix = root.label + "_";
116
+ blocks = blocks.filter((b) => scope.has(b.id) ||
117
+ (b.project_id != null && scope.has(b.project_id)) ||
118
+ b.label.startsWith(prefix));
119
+ }
120
+ else {
121
+ // No project root resolves — keep the namespace query for label-prefixed blocks
122
+ // without a root (back-compat), but FAIL LOUD when nothing matches at all:
123
+ // a silent [] here poisons the dead-end check.
124
+ const matched = blocks.filter((b) => b.label.startsWith(project + "_") || b.label === project);
125
+ if (matched.length === 0) {
126
+ const known = db.getAllBlocks().filter((b) => b.type === "project").map((p) => ({ id: p.id, label: p.label }));
127
+ return res.status(404).json({
128
+ error: `No project matches '${project}' (by id or label), and no block labels start with '${project}_'.`,
129
+ known_projects: known,
130
+ });
131
+ }
132
+ blocks = matched;
133
+ }
134
+ }
135
+ if (label)
136
+ blocks = blocks.filter((b) => b.label === label);
137
+ if (label_prefix)
138
+ blocks = blocks.filter((b) => b.label.startsWith(label_prefix));
139
+ if (flow_role)
140
+ blocks = blocks.filter((b) => b.flow_role === flow_role);
141
+ if (concept) {
142
+ const conceptLower = concept.toLowerCase();
143
+ blocks = blocks.filter((b) => {
144
+ const concepts = Array.isArray(b.concepts) ? b.concepts : (typeof b.concepts === "string" ? JSON.parse(b.concepts || "[]") : []);
145
+ return concepts.some((c) => c.toLowerCase().includes(conceptLower));
146
+ });
147
+ }
148
+ if (q) {
149
+ const lower = q.toLowerCase();
150
+ blocks = blocks.filter((b) => b.label.toLowerCase().includes(lower) || b.essence.toLowerCase().includes(lower));
151
+ }
152
+ if (limit !== undefined) {
153
+ const n = Number(limit);
154
+ if (!Number.isInteger(n) || n < 0) {
155
+ return res.status(400).json({ error: `limit must be a non-negative integer, got '${limit}'.` });
156
+ }
157
+ blocks = blocks.slice(0, n);
158
+ }
159
+ if (detailLevel === "surface") {
160
+ return res.json(blocks.map((b) => ({
161
+ id: b.id, label: b.label, type: b.type, status: b.status, essence: b.essence,
162
+ project_id: b.project_id ?? null, chain_id: b.chain_id ?? null,
163
+ })));
164
+ }
165
+ if (detailLevel === "content") {
166
+ return res.json(blocks.map((b) => {
167
+ let c = {};
168
+ try {
169
+ c = typeof b.content === "string" ? JSON.parse(b.content) : b.content;
170
+ }
171
+ catch { /* malformed content → empty */ }
172
+ return {
173
+ id: b.id, label: b.label, type: b.type, status: b.status, essence: b.essence,
174
+ project_id: b.project_id ?? null, chain_id: b.chain_id ?? null,
175
+ is_a: c?.is_a ?? null, unique: c?.unique ?? {},
176
+ };
177
+ }));
178
+ }
179
+ const slim = blocks.map(({ embedding: _e, ...rest }) => rest);
180
+ res.json(slim);
181
+ }
182
+ catch (e) {
183
+ res.status(500).json({ error: String(e) });
184
+ }
185
+ });
186
+ // ─── Single block with backlinks ────────────────────────────────
187
+ // detail levels ported from the MCP workspace_get tool (tools/core.ts) — the agent
188
+ // read surface. REST default stays "full" for existing consumers (TUI, scripts);
189
+ // surface/content/relations skip the whole-graph conflicts scan below.
190
+ router.get("/api/blocks/:id", (req, res) => {
191
+ try {
192
+ const DETAILS = new Set(["surface", "content", "relations", "full"]);
193
+ const detail = req.query.detail ?? "full";
194
+ if (!DETAILS.has(detail)) {
195
+ return res.status(400).json({ error: `Unknown detail '${req.query.detail}'. Allowed: surface, content, relations, full.` });
196
+ }
197
+ const block = db.getBlock(req.params.id);
198
+ if (!block)
199
+ return res.status(404).json({ error: "Not found" });
200
+ if (detail !== "full") {
201
+ let c = {};
202
+ try {
203
+ c = typeof block.content === "string" ? JSON.parse(block.content) : block.content;
204
+ }
205
+ catch { /* malformed content → empty */ }
206
+ const base = {
207
+ id: block.id, label: block.label, type: block.type, status: block.status,
208
+ essence: block.essence,
209
+ project_id: block.project_id ?? null, chain_id: block.chain_id ?? null,
210
+ detail_level: detail,
211
+ };
212
+ if (detail === "content") {
213
+ base.is_a = c?.is_a ?? null;
214
+ base.unique = c?.unique ?? {};
215
+ base.has = c?.has ?? {};
216
+ }
217
+ if (detail === "relations") {
218
+ const outgoing = db.getRelations(block.id).filter((r) => r.direction === "outgoing");
219
+ const incoming = db.getAllIncomingRelations(block.id);
220
+ base.outgoing = outgoing.map((r) => ({ type: r.type, target_label: r.target_label, target_id: r.target_id }));
221
+ base.incoming = incoming.map((r) => ({ type: r.type, source_label: r.source_label, source_id: r.source_id }));
222
+ }
223
+ return res.json(base);
224
+ }
225
+ const { embedding: _e, ...blockData } = block;
226
+ const content = typeof block.content === "string" ? JSON.parse(block.content) : block.content;
227
+ const outgoing = db.getRelations(block.id);
228
+ const incoming = db.getAllIncomingRelations(block.id);
229
+ const days = (Date.now() - new Date(block.last_accessed).getTime()) / 86400000;
230
+ const staleness_score = Math.round((days / Math.log(block.access_count + 2)) * 10) / 10;
231
+ const allRelations = db.getAllRelations();
232
+ const allBlocks = db.getAllBlocks();
233
+ const conflicts = allRelations
234
+ .filter((r) => (r.type === "conflicts_with" || r.type === "challenges") && (r.source_id === block.id || r.target_id === block.id))
235
+ .map((r) => {
236
+ const otherId = r.source_id === block.id ? r.target_id : r.source_id;
237
+ const other = allBlocks.find((b) => b.id === otherId);
238
+ return other ? { relation_id: r.id, type: r.type, other_id: other.id, other_label: other.label, other_essence: other.essence } : null;
239
+ }).filter(Boolean);
240
+ res.json({ ...blockData, content, outgoing, incoming, conflicts, staleness_score, days_inactive: Math.floor(days) });
241
+ }
242
+ catch (e) {
243
+ res.status(500).json({ error: String(e) });
244
+ }
245
+ });
246
+ // ─── Block graph navigation ──────────────────────────────────────
247
+ router.get("/api/blocks/:id/nav", (req, res) => {
248
+ try {
249
+ const block = db.getBlock(req.params.id);
250
+ if (!block)
251
+ return res.status(404).json({ error: "Block not found" });
252
+ const allBlocks = db.getAllBlocks();
253
+ const allRelations = db.getAllRelations();
254
+ // Build breadcrumb path using project_id
255
+ const path = [{ id: block.id, label: block.label, type: block.type }];
256
+ if (block.type !== "project" && block.project_id) {
257
+ const proj = allBlocks.find(b => b.id === block.project_id);
258
+ if (proj)
259
+ path.unshift({ id: proj.id, label: proj.label, type: proj.type });
260
+ }
261
+ const depth = path.length > 1 ? path.length - 1 : 0;
262
+ // Siblings: other blocks with the same project_id
263
+ const siblings = block.project_id
264
+ ? allBlocks.filter(b => b.id !== block.id && b.project_id === block.project_id)
265
+ .map(b => ({ id: b.id, label: b.label })).slice(0, 8)
266
+ : [];
267
+ // Children: project blocks use part_of for nesting; non-project blocks have no children in this model
268
+ const children = block.type === "project"
269
+ ? allBlocks.filter(b => b.project_id === block.id)
270
+ .map(b => ({ id: b.id, label: b.label })).slice(0, 8)
271
+ : [];
272
+ const parent = block.project_id ? allBlocks.find(b => b.id === block.project_id) ?? null : null;
273
+ const CAUSAL_RELS = CAUSAL_TRAVERSAL_RELS; // shared single source — relation-sets.ts
274
+ const outgoing = allRelations.filter(r => r.source_id === block.id && CAUSAL_RELS.has(r.type))
275
+ .map(r => { const target = allBlocks.find(b => b.id === r.target_id); return target ? { type: r.type, label: target.label, block_type: target.type, flow_role: target.flow_role || null } : null; })
276
+ .filter((r) => !!r);
277
+ const incoming = allRelations.filter(r => r.target_id === block.id && CAUSAL_RELS.has(r.type))
278
+ .map(r => { const source = allBlocks.find(b => b.id === r.source_id); return source ? { type: r.type, label: source.label, block_type: source.type, flow_role: source.flow_role || null } : null; })
279
+ .filter((r) => !!r);
280
+ res.json({ label: block.label, type: block.type, essence: block.essence || null, flow_role: block.flow_role || null, chain_id: block.chain_id || null, path: path.map(p => p.label), children, children_count: children.length, relations: { outgoing, incoming } });
281
+ }
282
+ catch (e) {
283
+ res.status(500).json({ error: String(e) });
284
+ }
285
+ });
286
+ // ─── Tree view ────────────────────────────────────────────────────
287
+ router.get("/api/tree", (req, res) => {
288
+ try {
289
+ const rawDepth = req.query.depth !== undefined ? Number(req.query.depth) : 1;
290
+ const maxDepth = Math.min(rawDepth, 4);
291
+ const projectFilter = req.query.project;
292
+ const allBlocks = db.getAllBlocks();
293
+ // Build childrenOf map from project_id column (replaces part_of relation scan)
294
+ const childrenOf = new Map();
295
+ for (const b of allBlocks) {
296
+ if (b.project_id) {
297
+ const list = childrenOf.get(b.project_id) || [];
298
+ list.push(b.id);
299
+ childrenOf.set(b.project_id, list);
300
+ }
301
+ }
302
+ const blockMap = new Map(allBlocks.map((b) => [b.id, b]));
303
+ function buildNode(blockId, depth) {
304
+ const b = blockMap.get(blockId);
305
+ if (!b)
306
+ return null;
307
+ const ess = b.essence || "";
308
+ const node = {
309
+ id: b.id, label: b.label, type: b.type,
310
+ ...(b.type === "project" ? { essence: ess.length > 80 ? ess.slice(0, 80) + "…" : ess } : {}),
311
+ ...(b.priority ? { priority: b.priority } : {}),
312
+ ...(b.flow_role ? { flow_role: b.flow_role } : {}),
313
+ ...(b.status && b.status !== "active" && b.status !== "created" ? { status: b.status } : {}),
314
+ children_count: (childrenOf.get(blockId) || []).length,
315
+ };
316
+ if (depth < maxDepth) {
317
+ const childIds = childrenOf.get(blockId) || [];
318
+ const allKids = childIds.map((id) => blockMap.get(id)).filter((k) => k && k.type !== "project" && k.status !== "archived");
319
+ if (b.type === "project") {
320
+ // STANCE types pinned at the project node — they're the action-
321
+ // changing residue an agent needs at orientation (agent.md Rule 1:
322
+ // dead-end + constraint check before proposing; decisions are the
323
+ // project's spine; blueprints are planned-but-pending). active_tasks
324
+ // pinned alongside (kept from prior convention).
325
+ const STANCE_TYPES = ["constraint", "dead_end", "decision", "blueprint"];
326
+ const isActiveTask = (k) => { if (k.type !== "task")
327
+ return false; try {
328
+ return JSON.parse(k.content)?.unique?.status === "in_progress";
329
+ }
330
+ catch {
331
+ return false;
332
+ } };
333
+ const slim = (k) => ({ id: k.id, label: k.label, essence: (k.essence || "").slice(0, 100) });
334
+ for (const t of STANCE_TYPES) {
335
+ const matches = allKids.filter((k) => k.type === t);
336
+ if (matches.length > 0)
337
+ node[`${t}s`] = matches.map(slim);
338
+ }
339
+ const activeTasks = allKids.filter(isActiveTask);
340
+ if (activeTasks.length > 0)
341
+ node.active_tasks = activeTasks.map(slim);
342
+ const rest = allKids.filter((k) => !STANCE_TYPES.includes(k.type) && !isActiveTask(k));
343
+ const kids = rest.map((k) => buildNode(k.id, depth + 1)).filter(Boolean);
344
+ if (kids.length > 0)
345
+ node.children = kids;
346
+ }
347
+ else {
348
+ const kids = allKids.map((k) => buildNode(k.id, depth + 1)).filter(Boolean);
349
+ if (kids.length > 0)
350
+ node.children = kids;
351
+ }
352
+ }
353
+ return node;
354
+ }
355
+ let projects = allBlocks.filter((b) => b.type === "project" && b.status !== "archived");
356
+ if (projectFilter)
357
+ projects = projects.filter((p) => p.id === projectFilter || p.label === projectFilter);
358
+ const tree = projects.map((p) => buildNode(p.id, 0));
359
+ // Cross-root entanglement at orient-time: which roots relate + how, meaning-
360
+ // classified from real edges (same derivation as GET /api/roots/related).
361
+ let related_roots = [];
362
+ try {
363
+ related_roots = deriveRootRelatedness(db).pairs;
364
+ }
365
+ catch { /* best-effort at orient — tree must not fail on it */ }
366
+ res.json({ tree, depth: maxDepth, total_projects: projects.length, related_roots });
367
+ }
368
+ catch (e) {
369
+ res.status(500).json({ error: String(e) });
370
+ }
371
+ });
372
+ // ─── Causal chain ─────────────────────────────────────────────────
373
+ router.get("/api/blocks/:id/chain", (req, res) => {
374
+ try {
375
+ const block = db.getBlock(req.params.id);
376
+ if (!block)
377
+ return res.status(404).json({ error: "Not found" });
378
+ const allBlocks = db.getAllBlocks();
379
+ const allRels = db.getAllRelations(false).filter((r) => r.status === "active");
380
+ const blockMap = new Map(allBlocks.map((b) => [b.id, b]));
381
+ const CAUSAL = CAUSAL_TRAVERSAL_RELS; // shared single source — relation-sets.ts
382
+ const focalId = block.id;
383
+ const depthMap = new Map();
384
+ const viaMap = new Map();
385
+ depthMap.set(focalId, 0);
386
+ const upQueue = [{ id: focalId, depth: 0 }];
387
+ while (upQueue.length) {
388
+ const { id, depth } = upQueue.shift();
389
+ if (depth < -6)
390
+ continue;
391
+ const parents = allRels.filter((r) => CAUSAL.has(r.type) && r.source_id === id);
392
+ for (const r of parents) {
393
+ if (!depthMap.has(r.target_id)) {
394
+ depthMap.set(r.target_id, depth - 1);
395
+ viaMap.set(r.target_id, r.type);
396
+ upQueue.push({ id: r.target_id, depth: depth - 1 });
397
+ }
398
+ }
399
+ }
400
+ const downQueue = [{ id: focalId, depth: 0 }];
401
+ while (downQueue.length) {
402
+ const { id, depth } = downQueue.shift();
403
+ if (depth > 4)
404
+ continue;
405
+ const children = allRels.filter((r) => CAUSAL.has(r.type) && r.target_id === id);
406
+ for (const r of children) {
407
+ if (!depthMap.has(r.source_id)) {
408
+ depthMap.set(r.source_id, depth + 1);
409
+ viaMap.set(r.source_id, r.type);
410
+ downQueue.push({ id: r.source_id, depth: depth + 1 });
411
+ }
412
+ }
413
+ }
414
+ const chainBlocks = [...depthMap.entries()].sort((a, b) => a[1] - b[1]).map(([id, depth]) => {
415
+ const b = blockMap.get(id);
416
+ if (!b)
417
+ return null;
418
+ const ess = (b.essence || "");
419
+ return { label: b.label, type: b.type, flow_role: b.flow_role || null, essence: ess.length > 120 ? ess.slice(0, 120) + "…" : ess, depth, via: viaMap.get(id) || null, is_focal: id === focalId };
420
+ }).filter(Boolean);
421
+ res.json({ chain: chainBlocks, length: chainBlocks.length });
422
+ }
423
+ catch (e) {
424
+ res.status(500).json({ error: String(e) });
425
+ }
426
+ });
427
+ // ─── Graph data ─────────────────────────────────────────────────
428
+ router.get("/api/graph", (_req, res) => {
429
+ try {
430
+ const blocks = db.getAllBlocks();
431
+ const allRelations = db.getAllRelations();
432
+ const now = Date.now();
433
+ const nodes = blocks.map((b) => {
434
+ const days = (now - new Date(b.last_accessed).getTime()) / 86400000;
435
+ const staleness = Math.round((days / Math.log(b.access_count + 2)) * 10) / 10;
436
+ return { id: b.id, label: b.label, type: b.type, status: b.status, essence: b.essence, access_count: b.access_count, created_at: b.created_at, created_by: b.created_by || null, staleness_score: staleness, days_inactive: Math.floor(days) };
437
+ });
438
+ const nodeIds = new Set(nodes.map((n) => n.id));
439
+ const edges = allRelations.filter((r) => nodeIds.has(r.source_id) && nodeIds.has(r.target_id)).map((r) => ({ id: r.id, source: r.source_id, target: r.target_id, type: r.type, bidirectional: r.bidirectional }));
440
+ res.json({ nodes, edges });
441
+ }
442
+ catch (e) {
443
+ res.status(500).json({ error: String(e) });
444
+ }
445
+ });
446
+ router.get("/api/templates", (_req, res) => {
447
+ try {
448
+ res.json(db.getBlockTypes());
449
+ }
450
+ catch (e) {
451
+ res.status(500).json({ error: String(e) });
452
+ }
453
+ });
454
+ router.get("/api/history", (req, res) => {
455
+ try {
456
+ res.json(db.getHistory(undefined, Number(req.query.limit) || 20));
457
+ }
458
+ catch (e) {
459
+ res.status(500).json({ error: String(e) });
460
+ }
461
+ });
462
+ router.get("/api/projects/:name/logs", (req, res) => {
463
+ try {
464
+ res.json(db.getProjectLogs(req.params.name, 50));
465
+ }
466
+ catch (e) {
467
+ res.status(500).json({ error: String(e) });
468
+ }
469
+ });
470
+ router.get("/api/chains/:chain_id", (req, res) => {
471
+ try {
472
+ const id = req.params.chain_id;
473
+ // Members link to a chain block via member_of edges — the AUTHORITATIVE
474
+ // many-to-many. The chain_id COLUMN is lossy single-attribution (a chain
475
+ // block has chain_id=null and few members carry its id), so a column query
476
+ // returns ~nothing for a chain-block id. Prefer the edges; fall back to the
477
+ // column for callers that pass a chain_id-column value.
478
+ const memberRels = db.getAllIncomingRelations(id).filter((r) => r.type === "member_of");
479
+ let blocks = memberRels
480
+ .map((r) => db.getBlock(r.source_id))
481
+ .filter((b) => !!b && b.status !== "archived");
482
+ if (blocks.length === 0)
483
+ blocks = db.getBlocksByChain(id);
484
+ if (blocks.length === 0)
485
+ return res.status(404).json({ error: "Chain not found" });
486
+ blocks.sort((a, b) => String(a.created_at).localeCompare(String(b.created_at)));
487
+ res.json({ chain_id: id, count: blocks.length, blocks: blocks.map((b) => ({ id: b.id, label: b.label, type: b.type, flow_role: b.flow_role, essence: b.essence, quality_score: b.quality_score })) });
488
+ }
489
+ catch (e) {
490
+ res.status(500).json({ error: String(e) });
491
+ }
492
+ });
493
+ router.get("/api/blocks/stale", (req, res) => {
494
+ try {
495
+ const threshold = parseFloat(req.query.threshold || "3");
496
+ const now = Date.now();
497
+ const stale = db.getAllBlocks().filter((b) => b.status === "active").map((b) => {
498
+ const days = (now - new Date(b.last_accessed).getTime()) / 86400000;
499
+ const score = Math.round((days / Math.log(b.access_count + 2)) * 10) / 10;
500
+ return { id: b.id, label: b.label, type: b.type, essence: b.essence, created_by: b.created_by, staleness_score: score, days_inactive: Math.floor(days) };
501
+ }).filter((b) => b.staleness_score > threshold).sort((a, b) => b.staleness_score - a.staleness_score);
502
+ res.json(stale);
503
+ }
504
+ catch (e) {
505
+ res.status(500).json({ error: String(e) });
506
+ }
507
+ });
508
+ router.get("/api/conflicts", (_req, res) => {
509
+ try {
510
+ const allRelations = db.getAllRelations();
511
+ const allBlocks = db.getAllBlocks();
512
+ const conflicts = allRelations.filter((r) => r.type === "conflicts_with" || r.type === "challenges").map((r) => {
513
+ const src = allBlocks.find((b) => b.id === r.source_id);
514
+ const tgt = allBlocks.find((b) => b.id === r.target_id);
515
+ if (!src || !tgt)
516
+ return null;
517
+ return { relation_id: r.id, type: r.type, source: { id: src.id, label: src.label, essence: src.essence, type: src.type, created_by: src.created_by }, target: { id: tgt.id, label: tgt.label, essence: tgt.essence, type: tgt.type, created_by: tgt.created_by } };
518
+ }).filter(Boolean);
519
+ res.json(conflicts);
520
+ }
521
+ catch (e) {
522
+ res.status(500).json({ error: String(e) });
523
+ }
524
+ });
525
+ // ─── Create block ────────────────────────────────────────────────
526
+ router.post("/api/blocks", (req, res) => {
527
+ try {
528
+ const { label, type, essence, ttl, content, concepts, is_a, unique, has, relations } = req.body;
529
+ if (!label)
530
+ return res.status(400).json({ error: "label is required" });
531
+ const mergedContent = { ...(content || {}) };
532
+ if (is_a)
533
+ mergedContent.is_a = is_a;
534
+ if (unique)
535
+ mergedContent.unique = unique;
536
+ if (has)
537
+ mergedContent.has = has;
538
+ const blockConcepts = concepts || [];
539
+ const resolvedTtl = (type === "dead_end") ? "permanent" : (ttl || "permanent");
540
+ const block = db.createBlock({ label, type: type || "note", essence: essence || "", ttl: resolvedTtl, content: mergedContent, concepts: blockConcepts, source: "ui", created_by: "user" });
541
+ const qScore = computeQualityScore(block, blockConcepts);
542
+ db.updateBlock(block.id, { quality_score: qScore });
543
+ const createdRelations = [];
544
+ if (relations?.length) {
545
+ for (const rel of relations) {
546
+ if (rel.type && rel.target_id) {
547
+ try {
548
+ const r = db.createRelation({ source_id: block.id, target_id: rel.target_id, type: rel.type, created_by: "user" });
549
+ createdRelations.push(r.id);
550
+ }
551
+ catch { /* skip */ }
552
+ }
553
+ }
554
+ }
555
+ db.save();
556
+ res.status(201).json({ ...block, quality_score: qScore, relations_created: createdRelations });
557
+ }
558
+ catch (e) {
559
+ res.status(500).json({ error: String(e) });
560
+ }
561
+ });
562
+ router.post("/api/blocks/batch", (req, res) => {
563
+ try {
564
+ const { blocks, project_id } = req.body;
565
+ if (!Array.isArray(blocks) || blocks.length === 0)
566
+ return res.status(400).json({ error: "blocks array is required" });
567
+ const created = blocks.map((spec) => {
568
+ if (!spec.label)
569
+ throw new Error("each block requires a label");
570
+ const concepts = spec.concepts || [];
571
+ const content = typeof spec.content === "object" && spec.content ? { ...spec.content } : {};
572
+ if (spec.is_a)
573
+ content.is_a = spec.is_a;
574
+ if (spec.unique)
575
+ content.unique = spec.unique;
576
+ if (spec.has)
577
+ content.has = spec.has;
578
+ const resolvedSpecTtl = (spec.type === "dead_end") ? "permanent" : (spec.ttl || "permanent");
579
+ const block = db.createBlock({ label: spec.label, type: spec.type || "fact", essence: spec.essence || "", ttl: resolvedSpecTtl, content, source: "ui", created_by: "user", concepts });
580
+ const qScore = computeQualityScore(block, concepts);
581
+ db.updateBlock(block.id, { quality_score: qScore });
582
+ if (project_id) {
583
+ const project = db.getBlock(project_id);
584
+ if (project)
585
+ db.updateBlock(block.id, { project_id: project.id });
586
+ }
587
+ if (spec.relations?.length) {
588
+ for (const rel of spec.relations) {
589
+ if (rel.type && rel.target_id) {
590
+ try {
591
+ db.createRelation({ source_id: block.id, target_id: rel.target_id, type: rel.type, created_by: "user" });
592
+ }
593
+ catch { /* skip */ }
594
+ }
595
+ }
596
+ }
597
+ return { id: block.id, label: block.label, type: block.type };
598
+ });
599
+ db.save();
600
+ res.status(201).json({ saved: created.length, blocks: created });
601
+ }
602
+ catch (e) {
603
+ res.status(500).json({ error: String(e) });
604
+ }
605
+ });
606
+ router.patch("/api/blocks/:id", (req, res) => {
607
+ try {
608
+ const { essence, ttl, status, label, type, content, priority, flow_role, updated_by } = req.body;
609
+ if (priority !== undefined && !["high", "medium", "low", null].includes(priority))
610
+ return res.status(400).json({ error: "priority must be high|medium|low|null" });
611
+ if (flow_role !== undefined && !["problem", "cause", "mechanism", "outcome", "solution", "trigger", null].includes(flow_role))
612
+ return res.status(400).json({ error: "flow_role must be problem|cause|mechanism|outcome|solution|trigger|null" });
613
+ const changes = {};
614
+ if (essence !== undefined)
615
+ changes.essence = essence;
616
+ if (ttl !== undefined)
617
+ changes.ttl = ttl;
618
+ if (status !== undefined)
619
+ changes.status = status;
620
+ if (label !== undefined)
621
+ changes.label = label;
622
+ if (type !== undefined)
623
+ changes.type = type;
624
+ if (content !== undefined)
625
+ changes.content = JSON.stringify(content);
626
+ if (priority !== undefined)
627
+ changes.priority = priority;
628
+ if (flow_role !== undefined)
629
+ changes.flow_role = flow_role;
630
+ if (Object.keys(changes).length === 0)
631
+ return res.status(400).json({ error: "No valid fields to update" });
632
+ const updated = db.updateBlock(req.params.id, changes, "Updated via API", updated_by || "user");
633
+ if (!updated)
634
+ return res.status(404).json({ error: "Block not found" });
635
+ db.save();
636
+ res.json(updated);
637
+ }
638
+ catch (e) {
639
+ res.status(500).json({ error: String(e) });
640
+ }
641
+ });
642
+ router.delete("/api/blocks/:id", (req, res) => {
643
+ try {
644
+ const block = db.getBlock(req.params.id);
645
+ if (!block)
646
+ return res.status(404).json({ error: "Block not found" });
647
+ if (block.locked)
648
+ return res.status(403).json({ error: "Block is locked" });
649
+ db.archiveBlock(req.params.id, "Archived via UI");
650
+ db.save();
651
+ res.json({ archived: true, id: req.params.id });
652
+ }
653
+ catch (e) {
654
+ res.status(500).json({ error: String(e) });
655
+ }
656
+ });
657
+ router.delete("/api/blocks", (req, res) => {
658
+ try {
659
+ const pattern = (req.query.pattern || "").trim();
660
+ if (!pattern)
661
+ return res.status(400).json({ error: "pattern query param required" });
662
+ const all = db.getAllBlocks();
663
+ const prefix = pattern.endsWith("*") ? pattern.slice(0, -1) : pattern;
664
+ const targets = all.filter(b => b.label.startsWith(prefix) && !b.locked);
665
+ for (const b of targets)
666
+ db.archiveBlock(b.id, `Bulk archive: matches pattern ${pattern}`);
667
+ if (targets.length > 0)
668
+ db.save();
669
+ res.json({ archived: targets.length, labels: targets.map(b => b.label) });
670
+ }
671
+ catch (e) {
672
+ res.status(500).json({ error: String(e) });
673
+ }
674
+ });
675
+ router.post("/api/blocks/:id/feedback", (req, res) => {
676
+ try {
677
+ const { useful } = req.body;
678
+ const block = db.getBlock(req.params.id);
679
+ if (!block)
680
+ return res.status(404).json({ error: "Block not found" });
681
+ db.updateBlock(req.params.id, { access_count: (block.access_count || 0) + (useful ? 1 : 0) }, useful ? "Positive recall feedback" : "Negative recall feedback", "user");
682
+ db.save();
683
+ res.json({ ok: true });
684
+ }
685
+ catch (e) {
686
+ res.status(500).json({ error: String(e) });
687
+ }
688
+ });
689
+ router.post("/api/blocks/:id/resolve", (req, res) => {
690
+ try {
691
+ const { action, other_id, reason } = req.body;
692
+ const block = db.getBlock(req.params.id);
693
+ const other = other_id ? db.getBlock(other_id) : null;
694
+ if (!block)
695
+ return res.status(404).json({ error: "Block not found" });
696
+ if (action === "keep_this" && other) {
697
+ db.archiveBlock(other.id, reason || `Conflict resolved: kept ${block.label}`);
698
+ db.save();
699
+ return res.json({ resolved: true, kept: block.id, archived: other.id });
700
+ }
701
+ if (action === "keep_other" && other) {
702
+ db.archiveBlock(block.id, reason || `Conflict resolved: kept ${other.label}`);
703
+ db.save();
704
+ return res.json({ resolved: true, kept: other.id, archived: block.id });
705
+ }
706
+ if (action === "archive_this") {
707
+ db.archiveBlock(block.id, reason || "Archived via conflict resolution");
708
+ db.save();
709
+ return res.json({ resolved: true, archived: block.id });
710
+ }
711
+ return res.status(400).json({ error: "action must be: keep_this | keep_other | archive_this" });
712
+ }
713
+ catch (e) {
714
+ res.status(500).json({ error: String(e) });
715
+ }
716
+ });
717
+ router.get("/api/blocks/:id/expand", (req, res) => {
718
+ try {
719
+ const anchor = db.getBlock(req.params.id);
720
+ if (!anchor)
721
+ return res.status(404).json({ error: "Block not found" });
722
+ const EXPAND_TYPES = new Set(["related_to", "derived_from", "based_on", "caused_by", "part_of"]);
723
+ const SKIP_TYPES = new Set(["contradicts"]);
724
+ const allRelations = db.getAllRelations(false);
725
+ const neighborMap = new Map();
726
+ for (const r of allRelations) {
727
+ if (SKIP_TYPES.has(r.type))
728
+ continue;
729
+ if (!EXPAND_TYPES.has(r.type))
730
+ continue;
731
+ if (r.source_id === anchor.id)
732
+ neighborMap.set(r.target_id, r.type);
733
+ else if (r.target_id === anchor.id)
734
+ neighborMap.set(r.source_id, r.type);
735
+ }
736
+ const decayRates = { session: 2.0, "1hr": 1.0, "24hr": 0.5, "1week": 0.1, project: 0.01, permanent: 0.001 };
737
+ const computeComposite = (block, similarity) => {
738
+ const ageDays = (Date.now() - new Date(block.created_at).getTime()) / 86400000;
739
+ const decayRate = decayRates[block.ttl ?? "permanent"] ?? 0.01;
740
+ const recency = 1 / (1 + ageDays * decayRate);
741
+ return { composite_score: similarity * recency, recency };
742
+ };
743
+ const neighbors = Array.from(neighborMap.entries())
744
+ .map(([id, relType]) => ({ block: db.getBlock(id), relType }))
745
+ .filter((entry) => entry.block !== null && entry.block !== undefined && entry.block.status !== "archived")
746
+ .map(({ block: b, relType }) => {
747
+ const sim = Math.min(1.0, ((b.quality_score ?? 3) / 6) * 1.2);
748
+ const { composite_score, recency } = computeComposite(b, sim);
749
+ const { embedding: _e, content: _c, ...rest } = b;
750
+ return { ...rest, relation_type: relType, composite_score: Math.round(composite_score * 10000) / 10000, pick_components: { similarity: Math.round(sim * 10000) / 10000, recency: Math.round(recency * 10000) / 10000 } };
751
+ }).sort((a, b) => b.composite_score - a.composite_score);
752
+ const { embedding: _ae, content: _ac, ...anchorData } = anchor;
753
+ res.json({ anchor: anchorData, neighbors, total_context_blocks: 1 + neighbors.length });
754
+ }
755
+ catch (e) {
756
+ res.status(500).json({ error: String(e) });
757
+ }
758
+ });
759
+ router.get("/api/conflicts/near-duplicates", (_req, res) => {
760
+ try {
761
+ res.json(db.getOpenConflicts());
762
+ }
763
+ catch (e) {
764
+ res.status(500).json({ error: String(e) });
765
+ }
766
+ });
767
+ router.post("/api/conflicts/near-duplicates/:id/resolve", (req, res) => {
768
+ try {
769
+ const { resolution, merged_essence, reason } = req.body;
770
+ const conflicts = db.getOpenConflicts();
771
+ const conflict = conflicts.find((c) => c.id === req.params.id);
772
+ if (!conflict)
773
+ return res.status(404).json({ error: "Conflict not found or already resolved" });
774
+ if (resolution === "keep_a") {
775
+ db.archiveBlock(conflict.block_b.id, `resolved duplicate: kept ${conflict.block_a.label}`);
776
+ }
777
+ else if (resolution === "keep_b") {
778
+ db.archiveBlock(conflict.block_a.id, `resolved duplicate: kept ${conflict.block_b.label}`);
779
+ }
780
+ else if (resolution === "merge" && merged_essence) {
781
+ db.updateBlock(conflict.block_a.id, { essence: merged_essence }, `merged with ${conflict.block_b.label}`, undefined, true);
782
+ db.archiveBlock(conflict.block_b.id, `merged into ${conflict.block_a.label}`);
783
+ }
784
+ else {
785
+ return res.status(400).json({ error: "resolution must be keep_a | keep_b | merge (merge requires merged_essence)" });
786
+ }
787
+ db.resolveConflict(req.params.id, `${resolution}${reason ? `: ${reason}` : ""}`);
788
+ res.json({ resolved: true, conflict_id: req.params.id, resolution });
789
+ }
790
+ catch (e) {
791
+ res.status(500).json({ error: String(e) });
792
+ }
793
+ });
794
+ // ─── Agents ─────────────────────────────────────────────────────
795
+ router.get("/api/agents", (_req, res) => {
796
+ try {
797
+ const agents = db.getRegisteredAgents();
798
+ const allBlocks = db.getAllBlocks();
799
+ const blockCounts = {};
800
+ for (const b of allBlocks) {
801
+ const a = b.created_by || "unknown";
802
+ blockCounts[a] = (blockCounts[a] || 0) + 1;
803
+ }
804
+ res.json(agents.map(a => ({ ...a, block_count: blockCounts[a.agent_id] || 0 })));
805
+ }
806
+ catch (e) {
807
+ res.status(500).json({ error: String(e) });
808
+ }
809
+ });
810
+ router.post("/api/agents/register", (req, res) => {
811
+ try {
812
+ const { agent_id, name, role, metadata } = req.body;
813
+ if (!agent_id)
814
+ return res.status(400).json({ error: "agent_id required" });
815
+ db.registerAgent(agent_id, name, role || "general", metadata);
816
+ res.json({ registered: true, agent_id });
817
+ }
818
+ catch (e) {
819
+ res.status(500).json({ error: String(e) });
820
+ }
821
+ });
822
+ // ─── Relations ─────────────────────────────────────────────────
823
+ router.post("/api/relations", (req, res) => {
824
+ try {
825
+ const { source_id, target_id, type, bidirectional } = req.body;
826
+ if (!source_id || !target_id || !type)
827
+ return res.status(400).json({ error: "source_id, target_id, and type are required" });
828
+ if (!db.getBlock(source_id))
829
+ return res.status(400).json({ error: `source block "${source_id}" not found` });
830
+ if (!db.getBlock(target_id))
831
+ return res.status(400).json({ error: `target block "${target_id}" not found` });
832
+ const relation = db.createRelation({ source_id, target_id, type, bidirectional, created_by: "user" });
833
+ db.save();
834
+ res.status(201).json(relation);
835
+ }
836
+ catch (e) {
837
+ res.status(500).json({ error: String(e) });
838
+ }
839
+ });
840
+ router.get("/api/relations", (req, res) => {
841
+ try {
842
+ const { source_id, target_id, type } = req.query;
843
+ let rels = db.getAllRelations(false);
844
+ if (source_id)
845
+ rels = rels.filter(r => r.source_id === source_id);
846
+ if (target_id)
847
+ rels = rels.filter(r => r.target_id === target_id);
848
+ if (type)
849
+ rels = rels.filter(r => r.type === type);
850
+ res.json(rels);
851
+ }
852
+ catch (e) {
853
+ res.status(500).json({ error: String(e) });
854
+ }
855
+ });
856
+ router.get("/api/relations/pending", (_req, res) => {
857
+ try {
858
+ res.json(db.getPendingRelations());
859
+ }
860
+ catch (e) {
861
+ res.status(500).json({ error: String(e) });
862
+ }
863
+ });
864
+ router.post("/api/relations/:id/approve", (req, res) => {
865
+ try {
866
+ db.approveRelation(req.params.id);
867
+ res.json({ approved: true, id: req.params.id });
868
+ }
869
+ catch (e) {
870
+ res.status(500).json({ error: String(e) });
871
+ }
872
+ });
873
+ router.post("/api/relations/:id/reject", (req, res) => {
874
+ try {
875
+ db.rejectRelation(req.params.id);
876
+ res.json({ rejected: true, id: req.params.id });
877
+ }
878
+ catch (e) {
879
+ res.status(500).json({ error: String(e) });
880
+ }
881
+ });
882
+ router.post("/api/relations/:id/invalidate", (req, res) => {
883
+ try {
884
+ const reason = req.body?.reason;
885
+ res.json({ invalidated: db.invalidateRelation(req.params.id, reason), id: req.params.id });
886
+ }
887
+ catch (e) {
888
+ res.status(500).json({ error: String(e) });
889
+ }
890
+ });
891
+ return router;
892
+ }
893
+ //# sourceMappingURL=blocks.js.map