@jlongo78/agent-spaces 0.7.4 → 0.7.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (515) hide show
  1. package/.next/standalone/.next/BUILD_ID +1 -1
  2. package/.next/standalone/.next/build-manifest.json +2 -2
  3. package/.next/standalone/.next/prerender-manifest.json +3 -3
  4. package/.next/standalone/.next/required-server-files.json +19 -19
  5. package/.next/standalone/.next/server/app/(desktop)/admin/analytics/page.js.nft.json +1 -1
  6. package/.next/standalone/.next/server/app/(desktop)/admin/analytics/page_client-reference-manifest.js +1 -1
  7. package/.next/standalone/.next/server/app/(desktop)/admin/users/page.js.nft.json +1 -1
  8. package/.next/standalone/.next/server/app/(desktop)/admin/users/page_client-reference-manifest.js +1 -1
  9. package/.next/standalone/.next/server/app/(desktop)/analytics/page.js.nft.json +1 -1
  10. package/.next/standalone/.next/server/app/(desktop)/analytics/page_client-reference-manifest.js +1 -1
  11. package/.next/standalone/.next/server/app/(desktop)/cortex/page.js.nft.json +1 -1
  12. package/.next/standalone/.next/server/app/(desktop)/cortex/page_client-reference-manifest.js +1 -1
  13. package/.next/standalone/.next/server/app/(desktop)/network/page.js.nft.json +1 -1
  14. package/.next/standalone/.next/server/app/(desktop)/network/page_client-reference-manifest.js +1 -1
  15. package/.next/standalone/.next/server/app/(desktop)/page.js.nft.json +1 -1
  16. package/.next/standalone/.next/server/app/(desktop)/page_client-reference-manifest.js +1 -1
  17. package/.next/standalone/.next/server/app/(desktop)/projects/page.js.nft.json +1 -1
  18. package/.next/standalone/.next/server/app/(desktop)/projects/page_client-reference-manifest.js +1 -1
  19. package/.next/standalone/.next/server/app/(desktop)/sessions/[id]/page.js.nft.json +1 -1
  20. package/.next/standalone/.next/server/app/(desktop)/sessions/[id]/page_client-reference-manifest.js +1 -1
  21. package/.next/standalone/.next/server/app/(desktop)/sessions/page.js.nft.json +1 -1
  22. package/.next/standalone/.next/server/app/(desktop)/sessions/page_client-reference-manifest.js +1 -1
  23. package/.next/standalone/.next/server/app/(desktop)/settings/page.js.nft.json +1 -1
  24. package/.next/standalone/.next/server/app/(desktop)/settings/page_client-reference-manifest.js +1 -1
  25. package/.next/standalone/.next/server/app/(desktop)/terminal/page.js.nft.json +1 -1
  26. package/.next/standalone/.next/server/app/(desktop)/terminal/page_client-reference-manifest.js +1 -1
  27. package/.next/standalone/.next/server/app/(desktop)/terminal/pane/[id]/page.js.nft.json +1 -1
  28. package/.next/standalone/.next/server/app/(desktop)/terminal/pane/[id]/page_client-reference-manifest.js +1 -1
  29. package/.next/standalone/.next/server/app/(desktop)/terminal/remote/[nodeId]/[workspaceId]/page.js.nft.json +1 -1
  30. package/.next/standalone/.next/server/app/(desktop)/terminal/remote/[nodeId]/[workspaceId]/page_client-reference-manifest.js +1 -1
  31. package/.next/standalone/.next/server/app/(desktop)/workspaces/page.js.nft.json +1 -1
  32. package/.next/standalone/.next/server/app/(desktop)/workspaces/page_client-reference-manifest.js +1 -1
  33. package/.next/standalone/.next/server/app/_global-error.html +2 -2
  34. package/.next/standalone/.next/server/app/_global-error.rsc +1 -1
  35. package/.next/standalone/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  36. package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  37. package/.next/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  38. package/.next/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  39. package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  40. package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  41. package/.next/standalone/.next/server/app/_not-found.html +1 -1
  42. package/.next/standalone/.next/server/app/_not-found.rsc +2 -2
  43. package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +2 -2
  44. package/.next/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  45. package/.next/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +2 -2
  46. package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  47. package/.next/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  48. package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  49. package/.next/standalone/.next/server/app/admin/analytics.html +1 -1
  50. package/.next/standalone/.next/server/app/admin/analytics.rsc +5 -5
  51. package/.next/standalone/.next/server/app/admin/analytics.segments/!KGRlc2t0b3Ap/admin/analytics/__PAGE__.segment.rsc +2 -2
  52. package/.next/standalone/.next/server/app/admin/analytics.segments/!KGRlc2t0b3Ap/admin/analytics.segment.rsc +1 -1
  53. package/.next/standalone/.next/server/app/admin/analytics.segments/!KGRlc2t0b3Ap/admin.segment.rsc +1 -1
  54. package/.next/standalone/.next/server/app/admin/analytics.segments/!KGRlc2t0b3Ap.segment.rsc +3 -3
  55. package/.next/standalone/.next/server/app/admin/analytics.segments/_full.segment.rsc +5 -5
  56. package/.next/standalone/.next/server/app/admin/analytics.segments/_head.segment.rsc +1 -1
  57. package/.next/standalone/.next/server/app/admin/analytics.segments/_index.segment.rsc +2 -2
  58. package/.next/standalone/.next/server/app/admin/analytics.segments/_tree.segment.rsc +2 -2
  59. package/.next/standalone/.next/server/app/admin/users.html +1 -1
  60. package/.next/standalone/.next/server/app/admin/users.rsc +5 -5
  61. package/.next/standalone/.next/server/app/admin/users.segments/!KGRlc2t0b3Ap/admin/users/__PAGE__.segment.rsc +2 -2
  62. package/.next/standalone/.next/server/app/admin/users.segments/!KGRlc2t0b3Ap/admin/users.segment.rsc +1 -1
  63. package/.next/standalone/.next/server/app/admin/users.segments/!KGRlc2t0b3Ap/admin.segment.rsc +1 -1
  64. package/.next/standalone/.next/server/app/admin/users.segments/!KGRlc2t0b3Ap.segment.rsc +3 -3
  65. package/.next/standalone/.next/server/app/admin/users.segments/_full.segment.rsc +5 -5
  66. package/.next/standalone/.next/server/app/admin/users.segments/_head.segment.rsc +1 -1
  67. package/.next/standalone/.next/server/app/admin/users.segments/_index.segment.rsc +2 -2
  68. package/.next/standalone/.next/server/app/admin/users.segments/_tree.segment.rsc +2 -2
  69. package/.next/standalone/.next/server/app/analytics.html +1 -1
  70. package/.next/standalone/.next/server/app/analytics.rsc +5 -5
  71. package/.next/standalone/.next/server/app/analytics.segments/!KGRlc2t0b3Ap/analytics/__PAGE__.segment.rsc +2 -2
  72. package/.next/standalone/.next/server/app/analytics.segments/!KGRlc2t0b3Ap/analytics.segment.rsc +1 -1
  73. package/.next/standalone/.next/server/app/analytics.segments/!KGRlc2t0b3Ap.segment.rsc +3 -3
  74. package/.next/standalone/.next/server/app/analytics.segments/_full.segment.rsc +5 -5
  75. package/.next/standalone/.next/server/app/analytics.segments/_head.segment.rsc +1 -1
  76. package/.next/standalone/.next/server/app/analytics.segments/_index.segment.rsc +2 -2
  77. package/.next/standalone/.next/server/app/analytics.segments/_tree.segment.rsc +2 -2
  78. package/.next/standalone/.next/server/app/api/analytics/overview/route.js.nft.json +1 -1
  79. package/.next/standalone/.next/server/app/api/bulk/route.js.nft.json +1 -1
  80. package/.next/standalone/.next/server/app/api/chat/route.js +1 -1
  81. package/.next/standalone/.next/server/app/api/chat/route.js.nft.json +1 -1
  82. package/.next/standalone/.next/server/app/api/config/route.js.nft.json +1 -1
  83. package/.next/standalone/.next/server/app/api/cortex/context/route.js.nft.json +1 -1
  84. package/.next/standalone/.next/server/app/api/cortex/curation/assess/route.js.nft.json +1 -1
  85. package/.next/standalone/.next/server/app/api/cortex/curation/publish/route.js.nft.json +1 -1
  86. package/.next/standalone/.next/server/app/api/cortex/curation/refine/route.js.nft.json +1 -1
  87. package/.next/standalone/.next/server/app/api/cortex/curation/review/route.js.nft.json +1 -1
  88. package/.next/standalone/.next/server/app/api/cortex/curation/seed/route.js.nft.json +1 -1
  89. package/.next/standalone/.next/server/app/api/cortex/export/route.js.nft.json +1 -1
  90. package/.next/standalone/.next/server/app/api/cortex/federation/pending/route.js.nft.json +1 -1
  91. package/.next/standalone/.next/server/app/api/cortex/federation/resolve/route.js.nft.json +1 -1
  92. package/.next/standalone/.next/server/app/api/cortex/federation/search/route.js.nft.json +1 -1
  93. package/.next/standalone/.next/server/app/api/cortex/federation/teach/route.js.nft.json +1 -1
  94. package/.next/standalone/.next/server/app/api/cortex/graph/edges/route.js.nft.json +1 -1
  95. package/.next/standalone/.next/server/app/api/cortex/graph/entities/[id]/route.js.nft.json +1 -1
  96. package/.next/standalone/.next/server/app/api/cortex/graph/entities/route.js.nft.json +1 -1
  97. package/.next/standalone/.next/server/app/api/cortex/graph/populate/route.js.nft.json +1 -1
  98. package/.next/standalone/.next/server/app/api/cortex/import/route.js.nft.json +1 -1
  99. package/.next/standalone/.next/server/app/api/cortex/import/status/route.js.nft.json +1 -1
  100. package/.next/standalone/.next/server/app/api/cortex/ingest/bootstrap/route.js.nft.json +1 -1
  101. package/.next/standalone/.next/server/app/api/cortex/ingest/status/route.js.nft.json +1 -1
  102. package/.next/standalone/.next/server/app/api/cortex/knowledge/[id]/route.js.nft.json +1 -1
  103. package/.next/standalone/.next/server/app/api/cortex/knowledge/route.js.nft.json +1 -1
  104. package/.next/standalone/.next/server/app/api/cortex/lobes/[id]/route.js.nft.json +1 -1
  105. package/.next/standalone/.next/server/app/api/cortex/lobes/route.js.nft.json +1 -1
  106. package/.next/standalone/.next/server/app/api/cortex/lobes/share/route.js.nft.json +1 -1
  107. package/.next/standalone/.next/server/app/api/cortex/marketplace/browse/route.js.nft.json +1 -1
  108. package/.next/standalone/.next/server/app/api/cortex/marketplace/preview/route.js.nft.json +1 -1
  109. package/.next/standalone/.next/server/app/api/cortex/mcp/call/route.js.nft.json +1 -1
  110. package/.next/standalone/.next/server/app/api/cortex/mcp/tools/route.js.nft.json +1 -1
  111. package/.next/standalone/.next/server/app/api/cortex/search/route.js.nft.json +1 -1
  112. package/.next/standalone/.next/server/app/api/cortex/settings/route.js.nft.json +1 -1
  113. package/.next/standalone/.next/server/app/api/cortex/status/route.js.nft.json +1 -1
  114. package/.next/standalone/.next/server/app/api/cortex/timeline/route.js.nft.json +1 -1
  115. package/.next/standalone/.next/server/app/api/cortex/usage/route.js.nft.json +1 -1
  116. package/.next/standalone/.next/server/app/api/cortex/workspace/[id]/context/route.js.nft.json +1 -1
  117. package/.next/standalone/.next/server/app/api/events/route.js.nft.json +1 -1
  118. package/.next/standalone/.next/server/app/api/folders/route.js.nft.json +1 -1
  119. package/.next/standalone/.next/server/app/api/network/handshake/route.js.nft.json +1 -1
  120. package/.next/standalone/.next/server/app/api/network/projects/route.js.nft.json +1 -1
  121. package/.next/standalone/.next/server/app/api/network/search/route.js.nft.json +1 -1
  122. package/.next/standalone/.next/server/app/api/network/sessions/[id]/messages/route.js.nft.json +1 -1
  123. package/.next/standalone/.next/server/app/api/network/sessions/[id]/route.js.nft.json +1 -1
  124. package/.next/standalone/.next/server/app/api/network/sessions/route.js.nft.json +1 -1
  125. package/.next/standalone/.next/server/app/api/network/workspaces/[id]/route.js.nft.json +1 -1
  126. package/.next/standalone/.next/server/app/api/network/workspaces/route.js +1 -1
  127. package/.next/standalone/.next/server/app/api/network/workspaces/route.js.nft.json +1 -1
  128. package/.next/standalone/.next/server/app/api/panes/[id]/route.js.nft.json +1 -1
  129. package/.next/standalone/.next/server/app/api/panes/route.js.nft.json +1 -1
  130. package/.next/standalone/.next/server/app/api/projects/route.js.nft.json +1 -1
  131. package/.next/standalone/.next/server/app/api/search/route.js.nft.json +1 -1
  132. package/.next/standalone/.next/server/app/api/sessions/[id]/chat/route.js.nft.json +1 -1
  133. package/.next/standalone/.next/server/app/api/sessions/[id]/messages/route.js.nft.json +1 -1
  134. package/.next/standalone/.next/server/app/api/sessions/[id]/route.js.nft.json +1 -1
  135. package/.next/standalone/.next/server/app/api/sessions/route.js.nft.json +1 -1
  136. package/.next/standalone/.next/server/app/api/sync/route.js.nft.json +1 -1
  137. package/.next/standalone/.next/server/app/api/tags/route.js.nft.json +1 -1
  138. package/.next/standalone/.next/server/app/api/tier/route.js.nft.json +1 -1
  139. package/.next/standalone/.next/server/app/api/workspaces/[id]/context/[key]/route.js.nft.json +1 -1
  140. package/.next/standalone/.next/server/app/api/workspaces/[id]/context/route.js.nft.json +1 -1
  141. package/.next/standalone/.next/server/app/api/workspaces/[id]/messages/[msgId]/route.js.nft.json +1 -1
  142. package/.next/standalone/.next/server/app/api/workspaces/[id]/messages/route.js.nft.json +1 -1
  143. package/.next/standalone/.next/server/app/api/workspaces/[id]/route.js.nft.json +1 -1
  144. package/.next/standalone/.next/server/app/api/workspaces/[id]/sessions/route.js.nft.json +1 -1
  145. package/.next/standalone/.next/server/app/api/workspaces/route.js.nft.json +1 -1
  146. package/.next/standalone/.next/server/app/cortex.html +1 -1
  147. package/.next/standalone/.next/server/app/cortex.rsc +5 -5
  148. package/.next/standalone/.next/server/app/cortex.segments/!KGRlc2t0b3Ap/cortex/__PAGE__.segment.rsc +2 -2
  149. package/.next/standalone/.next/server/app/cortex.segments/!KGRlc2t0b3Ap/cortex.segment.rsc +1 -1
  150. package/.next/standalone/.next/server/app/cortex.segments/!KGRlc2t0b3Ap.segment.rsc +3 -3
  151. package/.next/standalone/.next/server/app/cortex.segments/_full.segment.rsc +5 -5
  152. package/.next/standalone/.next/server/app/cortex.segments/_head.segment.rsc +1 -1
  153. package/.next/standalone/.next/server/app/cortex.segments/_index.segment.rsc +2 -2
  154. package/.next/standalone/.next/server/app/cortex.segments/_tree.segment.rsc +2 -2
  155. package/.next/standalone/.next/server/app/login/page_client-reference-manifest.js +1 -1
  156. package/.next/standalone/.next/server/app/login.html +1 -1
  157. package/.next/standalone/.next/server/app/login.rsc +2 -2
  158. package/.next/standalone/.next/server/app/login.segments/_full.segment.rsc +2 -2
  159. package/.next/standalone/.next/server/app/login.segments/_head.segment.rsc +1 -1
  160. package/.next/standalone/.next/server/app/login.segments/_index.segment.rsc +2 -2
  161. package/.next/standalone/.next/server/app/login.segments/_tree.segment.rsc +2 -2
  162. package/.next/standalone/.next/server/app/login.segments/login/__PAGE__.segment.rsc +1 -1
  163. package/.next/standalone/.next/server/app/login.segments/login.segment.rsc +1 -1
  164. package/.next/standalone/.next/server/app/m/page_client-reference-manifest.js +1 -1
  165. package/.next/standalone/.next/server/app/m/projects/page_client-reference-manifest.js +1 -1
  166. package/.next/standalone/.next/server/app/m/projects.html +1 -1
  167. package/.next/standalone/.next/server/app/m/projects.rsc +2 -2
  168. package/.next/standalone/.next/server/app/m/projects.segments/_full.segment.rsc +2 -2
  169. package/.next/standalone/.next/server/app/m/projects.segments/_head.segment.rsc +1 -1
  170. package/.next/standalone/.next/server/app/m/projects.segments/_index.segment.rsc +2 -2
  171. package/.next/standalone/.next/server/app/m/projects.segments/_tree.segment.rsc +2 -2
  172. package/.next/standalone/.next/server/app/m/projects.segments/m/projects/__PAGE__.segment.rsc +1 -1
  173. package/.next/standalone/.next/server/app/m/projects.segments/m/projects.segment.rsc +1 -1
  174. package/.next/standalone/.next/server/app/m/projects.segments/m.segment.rsc +1 -1
  175. package/.next/standalone/.next/server/app/m/sessions/[id]/page.js.nft.json +1 -1
  176. package/.next/standalone/.next/server/app/m/sessions/[id]/page_client-reference-manifest.js +1 -1
  177. package/.next/standalone/.next/server/app/m/sessions/page_client-reference-manifest.js +1 -1
  178. package/.next/standalone/.next/server/app/m/sessions.html +1 -1
  179. package/.next/standalone/.next/server/app/m/sessions.rsc +2 -2
  180. package/.next/standalone/.next/server/app/m/sessions.segments/_full.segment.rsc +2 -2
  181. package/.next/standalone/.next/server/app/m/sessions.segments/_head.segment.rsc +1 -1
  182. package/.next/standalone/.next/server/app/m/sessions.segments/_index.segment.rsc +2 -2
  183. package/.next/standalone/.next/server/app/m/sessions.segments/_tree.segment.rsc +2 -2
  184. package/.next/standalone/.next/server/app/m/sessions.segments/m/sessions/__PAGE__.segment.rsc +1 -1
  185. package/.next/standalone/.next/server/app/m/sessions.segments/m/sessions.segment.rsc +1 -1
  186. package/.next/standalone/.next/server/app/m/sessions.segments/m.segment.rsc +1 -1
  187. package/.next/standalone/.next/server/app/m/settings/page_client-reference-manifest.js +1 -1
  188. package/.next/standalone/.next/server/app/m/settings.html +1 -1
  189. package/.next/standalone/.next/server/app/m/settings.rsc +2 -2
  190. package/.next/standalone/.next/server/app/m/settings.segments/_full.segment.rsc +2 -2
  191. package/.next/standalone/.next/server/app/m/settings.segments/_head.segment.rsc +1 -1
  192. package/.next/standalone/.next/server/app/m/settings.segments/_index.segment.rsc +2 -2
  193. package/.next/standalone/.next/server/app/m/settings.segments/_tree.segment.rsc +2 -2
  194. package/.next/standalone/.next/server/app/m/settings.segments/m/settings/__PAGE__.segment.rsc +1 -1
  195. package/.next/standalone/.next/server/app/m/settings.segments/m/settings.segment.rsc +1 -1
  196. package/.next/standalone/.next/server/app/m/settings.segments/m.segment.rsc +1 -1
  197. package/.next/standalone/.next/server/app/m/terminal/page.js.nft.json +1 -1
  198. package/.next/standalone/.next/server/app/m/terminal/page_client-reference-manifest.js +1 -1
  199. package/.next/standalone/.next/server/app/m/terminal.html +1 -1
  200. package/.next/standalone/.next/server/app/m/terminal.rsc +2 -2
  201. package/.next/standalone/.next/server/app/m/terminal.segments/_full.segment.rsc +2 -2
  202. package/.next/standalone/.next/server/app/m/terminal.segments/_head.segment.rsc +1 -1
  203. package/.next/standalone/.next/server/app/m/terminal.segments/_index.segment.rsc +2 -2
  204. package/.next/standalone/.next/server/app/m/terminal.segments/_tree.segment.rsc +2 -2
  205. package/.next/standalone/.next/server/app/m/terminal.segments/m/terminal/__PAGE__.segment.rsc +1 -1
  206. package/.next/standalone/.next/server/app/m/terminal.segments/m/terminal.segment.rsc +1 -1
  207. package/.next/standalone/.next/server/app/m/terminal.segments/m.segment.rsc +1 -1
  208. package/.next/standalone/.next/server/app/m.html +1 -1
  209. package/.next/standalone/.next/server/app/m.rsc +2 -2
  210. package/.next/standalone/.next/server/app/m.segments/_full.segment.rsc +2 -2
  211. package/.next/standalone/.next/server/app/m.segments/_head.segment.rsc +1 -1
  212. package/.next/standalone/.next/server/app/m.segments/_index.segment.rsc +2 -2
  213. package/.next/standalone/.next/server/app/m.segments/_tree.segment.rsc +2 -2
  214. package/.next/standalone/.next/server/app/m.segments/m/__PAGE__.segment.rsc +1 -1
  215. package/.next/standalone/.next/server/app/m.segments/m.segment.rsc +1 -1
  216. package/.next/standalone/.next/server/app/network.html +1 -1
  217. package/.next/standalone/.next/server/app/network.rsc +5 -5
  218. package/.next/standalone/.next/server/app/network.segments/!KGRlc2t0b3Ap/network/__PAGE__.segment.rsc +2 -2
  219. package/.next/standalone/.next/server/app/network.segments/!KGRlc2t0b3Ap/network.segment.rsc +1 -1
  220. package/.next/standalone/.next/server/app/network.segments/!KGRlc2t0b3Ap.segment.rsc +3 -3
  221. package/.next/standalone/.next/server/app/network.segments/_full.segment.rsc +5 -5
  222. package/.next/standalone/.next/server/app/network.segments/_head.segment.rsc +1 -1
  223. package/.next/standalone/.next/server/app/network.segments/_index.segment.rsc +2 -2
  224. package/.next/standalone/.next/server/app/network.segments/_tree.segment.rsc +2 -2
  225. package/.next/standalone/.next/server/app/projects.html +1 -1
  226. package/.next/standalone/.next/server/app/projects.rsc +5 -5
  227. package/.next/standalone/.next/server/app/projects.segments/!KGRlc2t0b3Ap/projects/__PAGE__.segment.rsc +2 -2
  228. package/.next/standalone/.next/server/app/projects.segments/!KGRlc2t0b3Ap/projects.segment.rsc +1 -1
  229. package/.next/standalone/.next/server/app/projects.segments/!KGRlc2t0b3Ap.segment.rsc +3 -3
  230. package/.next/standalone/.next/server/app/projects.segments/_full.segment.rsc +5 -5
  231. package/.next/standalone/.next/server/app/projects.segments/_head.segment.rsc +1 -1
  232. package/.next/standalone/.next/server/app/projects.segments/_index.segment.rsc +2 -2
  233. package/.next/standalone/.next/server/app/projects.segments/_tree.segment.rsc +2 -2
  234. package/.next/standalone/.next/server/app/sessions.html +1 -1
  235. package/.next/standalone/.next/server/app/sessions.rsc +5 -5
  236. package/.next/standalone/.next/server/app/sessions.segments/!KGRlc2t0b3Ap/sessions/__PAGE__.segment.rsc +2 -2
  237. package/.next/standalone/.next/server/app/sessions.segments/!KGRlc2t0b3Ap/sessions.segment.rsc +1 -1
  238. package/.next/standalone/.next/server/app/sessions.segments/!KGRlc2t0b3Ap.segment.rsc +3 -3
  239. package/.next/standalone/.next/server/app/sessions.segments/_full.segment.rsc +5 -5
  240. package/.next/standalone/.next/server/app/sessions.segments/_head.segment.rsc +1 -1
  241. package/.next/standalone/.next/server/app/sessions.segments/_index.segment.rsc +2 -2
  242. package/.next/standalone/.next/server/app/sessions.segments/_tree.segment.rsc +2 -2
  243. package/.next/standalone/.next/server/app/settings.html +1 -1
  244. package/.next/standalone/.next/server/app/settings.rsc +5 -5
  245. package/.next/standalone/.next/server/app/settings.segments/!KGRlc2t0b3Ap/settings/__PAGE__.segment.rsc +2 -2
  246. package/.next/standalone/.next/server/app/settings.segments/!KGRlc2t0b3Ap/settings.segment.rsc +1 -1
  247. package/.next/standalone/.next/server/app/settings.segments/!KGRlc2t0b3Ap.segment.rsc +3 -3
  248. package/.next/standalone/.next/server/app/settings.segments/_full.segment.rsc +5 -5
  249. package/.next/standalone/.next/server/app/settings.segments/_head.segment.rsc +1 -1
  250. package/.next/standalone/.next/server/app/settings.segments/_index.segment.rsc +2 -2
  251. package/.next/standalone/.next/server/app/settings.segments/_tree.segment.rsc +2 -2
  252. package/.next/standalone/.next/server/app/terminal.html +1 -1
  253. package/.next/standalone/.next/server/app/terminal.rsc +5 -5
  254. package/.next/standalone/.next/server/app/terminal.segments/!KGRlc2t0b3Ap/terminal/__PAGE__.segment.rsc +2 -2
  255. package/.next/standalone/.next/server/app/terminal.segments/!KGRlc2t0b3Ap/terminal.segment.rsc +1 -1
  256. package/.next/standalone/.next/server/app/terminal.segments/!KGRlc2t0b3Ap.segment.rsc +3 -3
  257. package/.next/standalone/.next/server/app/terminal.segments/_full.segment.rsc +5 -5
  258. package/.next/standalone/.next/server/app/terminal.segments/_head.segment.rsc +1 -1
  259. package/.next/standalone/.next/server/app/terminal.segments/_index.segment.rsc +2 -2
  260. package/.next/standalone/.next/server/app/terminal.segments/_tree.segment.rsc +2 -2
  261. package/.next/standalone/.next/server/app/workspaces.html +1 -1
  262. package/.next/standalone/.next/server/app/workspaces.rsc +5 -5
  263. package/.next/standalone/.next/server/app/workspaces.segments/!KGRlc2t0b3Ap/workspaces/__PAGE__.segment.rsc +2 -2
  264. package/.next/standalone/.next/server/app/workspaces.segments/!KGRlc2t0b3Ap/workspaces.segment.rsc +1 -1
  265. package/.next/standalone/.next/server/app/workspaces.segments/!KGRlc2t0b3Ap.segment.rsc +3 -3
  266. package/.next/standalone/.next/server/app/workspaces.segments/_full.segment.rsc +5 -5
  267. package/.next/standalone/.next/server/app/workspaces.segments/_head.segment.rsc +1 -1
  268. package/.next/standalone/.next/server/app/workspaces.segments/_index.segment.rsc +2 -2
  269. package/.next/standalone/.next/server/app/workspaces.segments/_tree.segment.rsc +2 -2
  270. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0041efe4._.js +2 -2
  271. package/.next/standalone/.next/server/chunks/[root-of-the-server]__00bf0ace._.js +2 -2
  272. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0e71d908._.js +2 -2
  273. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0e9142f3._.js +2 -2
  274. package/.next/standalone/.next/server/chunks/[root-of-the-server]__10e47926._.js +1 -1
  275. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1665dc78._.js +2 -2
  276. package/.next/standalone/.next/server/chunks/[root-of-the-server]__175cbabf._.js +2 -2
  277. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1adae357._.js +2 -2
  278. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1d359752._.js +1 -1
  279. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1e8fabeb._.js +3 -3
  280. package/.next/standalone/.next/server/chunks/[root-of-the-server]__1f8deca0._.js +8 -8
  281. package/.next/standalone/.next/server/chunks/[root-of-the-server]__253fdda1._.js +2 -2
  282. package/.next/standalone/.next/server/chunks/[root-of-the-server]__28e6434f._.js +2 -2
  283. package/.next/standalone/.next/server/chunks/[root-of-the-server]__2a386564._.js +1 -1
  284. package/.next/standalone/.next/server/chunks/[root-of-the-server]__2c20fb38._.js +2 -2
  285. package/.next/standalone/.next/server/chunks/[root-of-the-server]__309132cd._.js +1 -1
  286. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__cf3c60c2._.js → [root-of-the-server]__33fec964._.js} +4 -4
  287. package/.next/standalone/.next/server/chunks/[root-of-the-server]__3786d8ae._.js +1 -1
  288. package/.next/standalone/.next/server/chunks/[root-of-the-server]__3ae92407._.js +2 -2
  289. package/.next/standalone/.next/server/chunks/[root-of-the-server]__3beda9fe._.js +2 -2
  290. package/.next/standalone/.next/server/chunks/[root-of-the-server]__4619e9bd._.js +1 -1
  291. package/.next/standalone/.next/server/chunks/[root-of-the-server]__4a051043._.js +1 -1
  292. package/.next/standalone/.next/server/chunks/[root-of-the-server]__508002e4._.js +2 -2
  293. package/.next/standalone/.next/server/chunks/[root-of-the-server]__5086c373._.js +2 -2
  294. package/.next/standalone/.next/server/chunks/[root-of-the-server]__5913e097._.js +2 -2
  295. package/.next/standalone/.next/server/chunks/[root-of-the-server]__5b5f68d2._.js +2 -2
  296. package/.next/standalone/.next/server/chunks/[root-of-the-server]__5c1f2459._.js +2 -2
  297. package/.next/standalone/.next/server/chunks/[root-of-the-server]__5ec8c977._.js +2 -2
  298. package/.next/standalone/.next/server/chunks/[root-of-the-server]__63cebc6c._.js +2 -2
  299. package/.next/standalone/.next/server/chunks/[root-of-the-server]__64d30d4d._.js +2 -2
  300. package/.next/standalone/.next/server/chunks/[root-of-the-server]__6c54fc2e._.js +2 -2
  301. package/.next/standalone/.next/server/chunks/[root-of-the-server]__6dc1fb7e._.js +1 -1
  302. package/.next/standalone/.next/server/chunks/[root-of-the-server]__6e568102._.js +1 -1
  303. package/.next/standalone/.next/server/chunks/[root-of-the-server]__6faa04c0._.js +2 -2
  304. package/.next/standalone/.next/server/chunks/[root-of-the-server]__74a34dc3._.js +2 -2
  305. package/.next/standalone/.next/server/chunks/[root-of-the-server]__7e7250a4._.js +3 -3
  306. package/.next/standalone/.next/server/chunks/[root-of-the-server]__8309e0a4._.js +2 -2
  307. package/.next/standalone/.next/server/chunks/[root-of-the-server]__86cc0e2b._.js +6 -6
  308. package/.next/standalone/.next/server/chunks/[root-of-the-server]__89c2565a._.js +2 -2
  309. package/.next/standalone/.next/server/chunks/[root-of-the-server]__8d178ad9._.js +2 -2
  310. package/.next/standalone/.next/server/chunks/[root-of-the-server]__93ee06f3._.js +3 -3
  311. package/.next/standalone/.next/server/chunks/[root-of-the-server]__9e4c154a._.js +2 -2
  312. package/.next/standalone/.next/server/chunks/[root-of-the-server]__a9d2e1d3._.js +2 -2
  313. package/.next/standalone/.next/server/chunks/[root-of-the-server]__ae53d343._.js +2 -2
  314. package/.next/standalone/.next/server/chunks/[root-of-the-server]__b3a04cef._.js +2 -2
  315. package/.next/standalone/.next/server/chunks/[root-of-the-server]__b4270b77._.js +1 -1
  316. package/.next/standalone/.next/server/chunks/[root-of-the-server]__b6b6ce60._.js +1 -1
  317. package/.next/standalone/.next/server/chunks/[root-of-the-server]__c88b63f7._.js +2 -2
  318. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__eee4c5e8._.js → [root-of-the-server]__cba5f007._.js} +2 -2
  319. package/.next/standalone/.next/server/chunks/[root-of-the-server]__cbf4ceb0._.js +2 -2
  320. package/.next/standalone/.next/server/chunks/[root-of-the-server]__cefdba2f._.js +2 -2
  321. package/.next/standalone/.next/server/chunks/[root-of-the-server]__cf9e82bb._.js +2 -2
  322. package/.next/standalone/.next/server/chunks/[root-of-the-server]__d2897392._.js +2 -2
  323. package/.next/standalone/.next/server/chunks/[root-of-the-server]__d3b2d856._.js +2 -2
  324. package/.next/standalone/.next/server/chunks/[root-of-the-server]__d73273ca._.js +1 -1
  325. package/.next/standalone/.next/server/chunks/[root-of-the-server]__d8417eb6._.js +2 -2
  326. package/.next/standalone/.next/server/chunks/[root-of-the-server]__dc2a55de._.js +2 -2
  327. package/.next/standalone/.next/server/chunks/[root-of-the-server]__e0d4690b._.js +3 -3
  328. package/.next/standalone/.next/server/chunks/[root-of-the-server]__e678dd53._.js +1 -1
  329. package/.next/standalone/.next/server/chunks/[root-of-the-server]__e9223f55._.js +2 -2
  330. package/.next/standalone/.next/server/chunks/[root-of-the-server]__ea630076._.js +3 -3
  331. package/.next/standalone/.next/server/chunks/[root-of-the-server]__eb8acb65._.js +1 -1
  332. package/.next/standalone/.next/server/chunks/[root-of-the-server]__f26ca49d._.js +1 -1
  333. package/.next/standalone/.next/server/chunks/[root-of-the-server]__f33e1101._.js +1 -1
  334. package/.next/standalone/.next/server/chunks/[root-of-the-server]__f515f865._.js +2 -2
  335. package/.next/standalone/.next/server/chunks/[root-of-the-server]__fceb5d60._.js +2 -2
  336. package/.next/standalone/.next/server/chunks/[root-of-the-server]__fed41403._.js +2 -2
  337. package/.next/standalone/.next/server/chunks/[root-of-the-server]__ff2e98c2._.js +2 -2
  338. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__19afc53d._.js +3 -0
  339. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__66aca5d4._.js +1 -1
  340. package/.next/standalone/.next/server/chunks/ssr/{_036cbaa2._.js → _078dd64d._.js} +1 -1
  341. package/.next/standalone/.next/server/chunks/ssr/{_a4eeff0d._.js → _2230ad2d._.js} +2 -2
  342. package/.next/standalone/.next/server/chunks/ssr/{_43455582._.js → _701606d5._.js} +1 -1
  343. package/.next/standalone/.next/server/chunks/ssr/_72b1de37._.js +3 -0
  344. package/.next/standalone/.next/server/chunks/ssr/{_d39bcfda._.js → _93ef0f79._.js} +2 -2
  345. package/.next/standalone/.next/server/chunks/ssr/_950142a4._.js +1 -1
  346. package/.next/standalone/.next/server/chunks/ssr/_a22b5eb0._.js +3 -0
  347. package/.next/standalone/.next/server/chunks/ssr/_aeeff784._.js +3 -0
  348. package/.next/standalone/.next/server/chunks/ssr/_c1cfdd09._.js +3 -0
  349. package/.next/standalone/.next/server/chunks/ssr/_c2d3f6de._.js +3 -0
  350. package/.next/standalone/.next/server/chunks/ssr/{_8167090e._.js → _db2fec84._.js} +2 -2
  351. package/.next/standalone/.next/server/chunks/ssr/src_components_terminal_terminal-pane_tsx_803c5e2c._.js +2 -2
  352. package/.next/standalone/.next/server/edge/chunks/_d73df637._.js +1 -1
  353. package/.next/standalone/.next/server/middleware-manifest.json +5 -5
  354. package/.next/standalone/.next/server/pages/404.html +1 -1
  355. package/.next/standalone/.next/server/pages/500.html +2 -2
  356. package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
  357. package/.next/standalone/.next/server/server-reference-manifest.json +1 -1
  358. package/.next/standalone/.next/static/chunks/25b7a243a404a1a7.js +1 -0
  359. package/.next/standalone/.next/static/chunks/2b997e211a5d547b.js +1 -0
  360. package/.next/standalone/.next/static/chunks/5e08abb00653754a.js +1 -0
  361. package/.next/standalone/.next/static/chunks/61f2ed39b75b1efc.js +1 -0
  362. package/.next/standalone/.next/static/chunks/6c78a1dfa7ec2959.css +3 -0
  363. package/.next/standalone/.next/static/chunks/7246f1ee445f7024.js +1 -0
  364. package/.next/standalone/.next/static/chunks/7424664c6ffa94bd.js +1 -0
  365. package/.next/standalone/.next/static/chunks/7e0091ab6c5ee8bd.js +1 -0
  366. package/.next/standalone/.next/static/chunks/8b3f4572fec83caa.js +5 -0
  367. package/.next/standalone/.next/static/chunks/{224aab5107987011.js → 9899cf4c2bdbe61d.js} +2 -2
  368. package/.next/standalone/.next/static/chunks/ac339e970df82fa5.js +5 -0
  369. package/.next/standalone/.next/static/chunks/f9f2628207848ac2.js +1 -0
  370. package/.next/standalone/bin/cortex-hook.sh +62 -62
  371. package/.next/standalone/bin/cortex-mcp.js +60 -60
  372. package/.next/standalone/docs/superpowers/plans/2026-03-13-cortex-wiring.md +1387 -1387
  373. package/.next/standalone/docs/superpowers/plans/2026-03-14-cortex-v2-entity-graph.md +1923 -1923
  374. package/.next/standalone/docs/superpowers/plans/2026-03-14-cortex-v2-knowledge-evolution.md +1113 -1113
  375. package/.next/standalone/docs/superpowers/plans/2026-03-15-cortex-v2-boundary-engine.md +853 -853
  376. package/.next/standalone/docs/superpowers/plans/2026-03-15-cortex-v2-context-engine.md +1274 -1274
  377. package/.next/standalone/docs/superpowers/plans/2026-03-15-cortex-v2-signal-ingestion.md +933 -933
  378. package/.next/standalone/docs/superpowers/plans/2026-03-16-cortex-lobes.md +1080 -1080
  379. package/.next/standalone/docs/superpowers/plans/2026-03-16-cortex-v2-gravity-system.md +768 -768
  380. package/.next/standalone/docs/superpowers/plans/2026-03-16-cortex-v2-ui.md +1108 -1108
  381. package/.next/standalone/docs/superpowers/plans/2026-03-18-cortex-ui-integration.md +1846 -1846
  382. package/.next/standalone/docs/superpowers/specs/2026-03-13-cortex-wiring-design.md +268 -268
  383. package/.next/standalone/docs/superpowers/specs/2026-03-14-cortex-v2-design.md +623 -623
  384. package/.next/standalone/docs/superpowers/specs/2026-03-16-cortex-lobes-design.md +263 -263
  385. package/.next/standalone/docs/superpowers/specs/2026-03-16-cortex-v2-ui-design.md +240 -240
  386. package/.next/standalone/docs/superpowers/specs/2026-03-18-cortex-ui-integration-design.md +341 -341
  387. package/.next/standalone/node_modules/@img/sharp-libvips-linux-x64/README.md +46 -0
  388. package/.next/standalone/node_modules/@img/sharp-libvips-linux-x64/lib/glib-2.0/include/glibconfig.h +221 -0
  389. package/.next/standalone/node_modules/@img/sharp-libvips-linux-x64/lib/index.js +1 -0
  390. package/.next/standalone/node_modules/@img/sharp-libvips-linux-x64/lib/libvips-cpp.so.8.17.3 +0 -0
  391. package/.next/standalone/node_modules/@img/sharp-libvips-linux-x64/package.json +42 -0
  392. package/.next/standalone/node_modules/@img/sharp-libvips-linuxmusl-x64/README.md +46 -0
  393. package/.next/standalone/node_modules/@img/sharp-libvips-linuxmusl-x64/lib/glib-2.0/include/glibconfig.h +221 -0
  394. package/.next/standalone/node_modules/@img/sharp-libvips-linuxmusl-x64/lib/index.js +1 -0
  395. package/.next/standalone/node_modules/@img/sharp-libvips-linuxmusl-x64/lib/libvips-cpp.so.8.17.3 +0 -0
  396. package/.next/standalone/node_modules/@img/sharp-libvips-linuxmusl-x64/package.json +42 -0
  397. package/.next/standalone/node_modules/@img/sharp-libvips-linuxmusl-x64/versions.json +30 -0
  398. package/.next/standalone/node_modules/@img/sharp-linux-x64/lib/sharp-linux-x64.node +0 -0
  399. package/.next/standalone/node_modules/@img/{sharp-win32-x64 → sharp-linux-x64}/package.json +46 -39
  400. package/.next/standalone/node_modules/@img/sharp-linuxmusl-x64/lib/sharp-linuxmusl-x64.node +0 -0
  401. package/.next/standalone/node_modules/@img/sharp-linuxmusl-x64/package.json +46 -0
  402. package/.next/standalone/package.json +102 -102
  403. package/.next/standalone/server.js +1 -1
  404. package/.next/standalone/src/app/(desktop)/cortex/page.tsx +78 -78
  405. package/.next/standalone/src/app/api/cortex/context/route.ts +78 -78
  406. package/.next/standalone/src/app/api/cortex/curation/assess/route.ts +27 -27
  407. package/.next/standalone/src/app/api/cortex/curation/publish/route.ts +23 -23
  408. package/.next/standalone/src/app/api/cortex/curation/refine/route.ts +23 -23
  409. package/.next/standalone/src/app/api/cortex/curation/review/route.ts +29 -29
  410. package/.next/standalone/src/app/api/cortex/curation/seed/route.ts +23 -23
  411. package/.next/standalone/src/app/api/cortex/export/route.ts +40 -40
  412. package/.next/standalone/src/app/api/cortex/federation/pending/route.ts +20 -20
  413. package/.next/standalone/src/app/api/cortex/federation/resolve/route.ts +43 -43
  414. package/.next/standalone/src/app/api/cortex/federation/search/route.ts +35 -35
  415. package/.next/standalone/src/app/api/cortex/federation/teach/route.ts +76 -76
  416. package/.next/standalone/src/app/api/cortex/graph/edges/route.ts +112 -112
  417. package/.next/standalone/src/app/api/cortex/graph/entities/[id]/route.ts +73 -73
  418. package/.next/standalone/src/app/api/cortex/graph/entities/route.ts +75 -75
  419. package/.next/standalone/src/app/api/cortex/graph/populate/route.ts +203 -203
  420. package/.next/standalone/src/app/api/cortex/import/route.ts +75 -75
  421. package/.next/standalone/src/app/api/cortex/import/status/route.ts +15 -15
  422. package/.next/standalone/src/app/api/cortex/ingest/bootstrap/route.ts +29 -29
  423. package/.next/standalone/src/app/api/cortex/ingest/status/route.ts +15 -15
  424. package/.next/standalone/src/app/api/cortex/knowledge/[id]/route.ts +91 -91
  425. package/.next/standalone/src/app/api/cortex/knowledge/route.ts +93 -93
  426. package/.next/standalone/src/app/api/cortex/lobes/[id]/route.ts +67 -67
  427. package/.next/standalone/src/app/api/cortex/lobes/route.ts +22 -22
  428. package/.next/standalone/src/app/api/cortex/lobes/share/route.ts +80 -80
  429. package/.next/standalone/src/app/api/cortex/marketplace/browse/route.ts +43 -43
  430. package/.next/standalone/src/app/api/cortex/marketplace/preview/route.ts +46 -46
  431. package/.next/standalone/src/app/api/cortex/mcp/call/route.ts +11 -11
  432. package/.next/standalone/src/app/api/cortex/mcp/tools/route.ts +6 -6
  433. package/.next/standalone/src/app/api/cortex/search/route.ts +43 -43
  434. package/.next/standalone/src/app/api/cortex/settings/route.ts +33 -33
  435. package/.next/standalone/src/app/api/cortex/status/route.ts +169 -169
  436. package/.next/standalone/src/app/api/cortex/timeline/route.ts +42 -42
  437. package/.next/standalone/src/app/api/cortex/usage/route.ts +31 -31
  438. package/.next/standalone/src/app/api/cortex/workspace/[id]/context/route.ts +41 -41
  439. package/.next/standalone/src/components/cortex/constants.ts +29 -29
  440. package/.next/standalone/src/components/cortex/cortex-dashboard.tsx +304 -304
  441. package/.next/standalone/src/components/cortex/cortex-indicator.tsx +44 -44
  442. package/.next/standalone/src/components/cortex/cortex-panel.tsx +140 -140
  443. package/.next/standalone/src/components/cortex/cortex-settings.tsx +221 -221
  444. package/.next/standalone/src/components/cortex/curation-tab.tsx +810 -810
  445. package/.next/standalone/src/components/cortex/entity-detail.tsx +101 -101
  446. package/.next/standalone/src/components/cortex/entity-graph.tsx +382 -382
  447. package/.next/standalone/src/components/cortex/import-dialog.tsx +212 -212
  448. package/.next/standalone/src/components/cortex/injection-badge.tsx +72 -72
  449. package/.next/standalone/src/components/cortex/knowledge-card.tsx +109 -109
  450. package/.next/standalone/src/components/cortex/knowledge-tab.tsx +158 -158
  451. package/.next/standalone/src/components/cortex/lobe-settings.tsx +215 -215
  452. package/.next/standalone/src/components/cortex/marketplace-card.tsx +126 -126
  453. package/.next/standalone/src/components/cortex/marketplace-tab.tsx +113 -113
  454. package/.next/standalone/src/lib/cortex/config.ts +40 -40
  455. package/.next/standalone/src/lib/cortex/debug.ts +10 -10
  456. package/.next/standalone/src/lib/cortex/distillation/usage-store.ts +18 -18
  457. package/.next/standalone/src/lib/cortex/graph/resolver.ts +10 -10
  458. package/.next/standalone/src/lib/cortex/graph/types.ts +22 -22
  459. package/.next/standalone/src/lib/cortex/index.ts +56 -56
  460. package/.next/standalone/src/lib/cortex/ingestion/bootstrap.ts +14 -14
  461. package/.next/standalone/src/lib/cortex/knowledge/compat.ts +14 -14
  462. package/.next/standalone/src/lib/cortex/knowledge/contradiction.ts +10 -10
  463. package/.next/standalone/src/lib/cortex/knowledge/types.ts +67 -67
  464. package/.next/standalone/src/lib/cortex/lobes/config.ts +16 -16
  465. package/.next/standalone/src/lib/cortex/lobes/resolver.ts +8 -8
  466. package/.next/standalone/src/lib/cortex/lobes/shares.ts +14 -14
  467. package/.next/standalone/src/lib/cortex/mcp/server.ts +8 -8
  468. package/.next/standalone/src/lib/cortex/portability/exporter.ts +6 -6
  469. package/.next/standalone/src/lib/cortex/portability/importer.ts +10 -10
  470. package/.next/standalone/src/lib/cortex/retrieval/context-engine.ts +10 -10
  471. package/.next/standalone/src/lib/cortex/types.ts +39 -39
  472. package/.next/standalone/tsconfig.json +34 -34
  473. package/LICENSE +661 -661
  474. package/README.md +131 -131
  475. package/bin/cortex-hook.sh +62 -62
  476. package/bin/cortex-mcp.js +60 -60
  477. package/bin/fix-standalone-externals.js +79 -79
  478. package/bin/lib/auto-setup.js +110 -110
  479. package/bin/mdns-service.js +171 -171
  480. package/bin/postinstall.js +35 -35
  481. package/bin/setup-admin.js +195 -195
  482. package/bin/spaces-dev.js +208 -208
  483. package/bin/spaces-install.js +599 -599
  484. package/bin/spaces-reset-totp.js +50 -50
  485. package/bin/spaces-service.js +1020 -1020
  486. package/bin/spaces-setup.js +253 -253
  487. package/bin/spaces.js +776 -717
  488. package/bin/ssh-auth-keys.sh +68 -68
  489. package/bin/terminal-server.js +1649 -1646
  490. package/package.json +102 -102
  491. package/.next/standalone/.claude/settings.local.json +0 -55
  492. package/.next/standalone/.claude/spaces-env.json +0 -1
  493. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__23f374a2._.js +0 -3
  494. package/.next/standalone/.next/server/chunks/ssr/_01842037._.js +0 -3
  495. package/.next/standalone/.next/server/chunks/ssr/_10c0d382._.js +0 -3
  496. package/.next/standalone/.next/server/chunks/ssr/_67887e33._.js +0 -3
  497. package/.next/standalone/.next/server/chunks/ssr/_a012c43b._.js +0 -3
  498. package/.next/standalone/.next/server/chunks/ssr/_ed6c285b._.js +0 -3
  499. package/.next/standalone/.next/static/chunks/17d164c01fa1aaa9.js +0 -5
  500. package/.next/standalone/.next/static/chunks/19da759dde107f02.js +0 -1
  501. package/.next/standalone/.next/static/chunks/415827af44b8c374.js +0 -1
  502. package/.next/standalone/.next/static/chunks/596bd56e0ad09173.js +0 -5
  503. package/.next/standalone/.next/static/chunks/79aacab676df80c4.js +0 -1
  504. package/.next/standalone/.next/static/chunks/846d6ef408f69390.js +0 -1
  505. package/.next/standalone/.next/static/chunks/8a7f58872c123a53.css +0 -3
  506. package/.next/standalone/.next/static/chunks/8de5e432a2fc563a.js +0 -1
  507. package/.next/standalone/.next/static/chunks/b0484608571d975a.js +0 -1
  508. package/.next/standalone/.next/static/chunks/b42a38df3e418ff0.js +0 -1
  509. package/.next/standalone/.next/static/chunks/d6474f65a17c483e.js +0 -1
  510. package/.next/standalone/.spaces/cortex-context.md +0 -70
  511. package/.next/standalone/node_modules/@img/sharp-win32-x64/lib/sharp-win32-x64.node +0 -0
  512. /package/.next/standalone/.next/static/{Ku7_sP1T1UuqtRhUTiJZw → 77VYbwIoyxFNr5xevTrCu}/_buildManifest.js +0 -0
  513. /package/.next/standalone/.next/static/{Ku7_sP1T1UuqtRhUTiJZw → 77VYbwIoyxFNr5xevTrCu}/_clientMiddlewareManifest.json +0 -0
  514. /package/.next/standalone/.next/static/{Ku7_sP1T1UuqtRhUTiJZw → 77VYbwIoyxFNr5xevTrCu}/_ssgManifest.js +0 -0
  515. /package/.next/standalone/node_modules/@img/{sharp-win32-x64 → sharp-libvips-linux-x64}/versions.json +0 -0
@@ -1,1080 +1,1080 @@
1
- # Cortex Lobes Implementation Plan
2
-
3
- > **For agentic workers:** REQUIRED: Use superpowers:subagent-driven-development (if subagents available) or superpowers:executing-plans to implement this plan. Steps use checkbox (`- [ ]`) syntax for tracking.
4
-
5
- **Goal:** Add knowledge compartmentalization ("lobes") to Cortex so workspaces control which knowledge sources they can pull from — open by default within a user, closed by default across users, with privacy, exclusions, and cross-user sharing.
6
-
7
- **Architecture:** A new `src/lib/cortex/lobes/` module containing lobe config types, a resolver that computes accessible lobes for a workspace, and sharing logic. The ContextEngine's `computeSourceWeights()` is modified to use the lobe resolver instead of hardcoded layers. Lobe config is stored as a JSON column on the workspaces table. Cross-user shares live in the entity graph's SQLite database.
8
-
9
- **Tech Stack:** TypeScript, better-sqlite3 (entity graph DB), vitest
10
-
11
- **Spec:** `docs/superpowers/specs/2026-03-16-cortex-lobes-design.md`
12
-
13
- ---
14
-
15
- ## File Structure
16
-
17
- ```
18
- New files:
19
- ├── src/lib/cortex/lobes/config.ts — LobeConfig types and defaults
20
- ├── src/lib/cortex/lobes/resolver.ts — Resolve accessible lobes for a workspace
21
- ├── src/lib/cortex/lobes/shares.ts — Cross-user sharing (lobe_shares table)
22
- ├── src/lib/cortex/lobes/index.ts — Barrel export
23
- ├── src/app/api/cortex/lobes/route.ts — List lobes, update config
24
- ├── src/app/api/cortex/lobes/share/route.ts — Share management
25
- ├── src/components/cortex/lobe-settings.tsx — UI component for workspace settings
26
-
27
- Test files:
28
- ├── tests/lib/cortex/lobes/resolver.test.ts
29
- ├── tests/lib/cortex/lobes/shares.test.ts
30
-
31
- Modified files:
32
- ├── src/lib/db/schema.ts — Add lobe_config column to workspaces
33
- ├── src/lib/cortex/retrieval/context-engine.ts — Use lobe resolver for search scopes
34
- ```
35
-
36
- ---
37
-
38
- ## Chunk 1: Types, DB Migration, and Lobe Resolver
39
-
40
- ### Task 1: Lobe config types and defaults
41
-
42
- **Files:**
43
- - Create: `src/lib/cortex/lobes/config.ts`
44
-
45
- - [ ] **Step 1: Create the types file**
46
-
47
- ```typescript
48
- // src/lib/cortex/lobes/config.ts
49
-
50
- export interface LobeSubscription {
51
- type: 'workspace' | 'user' | 'tag' | 'team' | 'department' | 'organization';
52
- id: string; // workspace ID, user entity ID, tag name, etc.
53
- label: string; // display name
54
- }
55
-
56
- export interface LobeConfig {
57
- isPrivate: boolean;
58
- excludedFrom: number[]; // workspace IDs blocked from accessing this lobe
59
- subscriptions: LobeSubscription[];
60
- tags: string[];
61
- }
62
-
63
- export const DEFAULT_LOBE_CONFIG: LobeConfig = {
64
- isPrivate: false,
65
- excludedFrom: [],
66
- subscriptions: [],
67
- tags: [],
68
- };
69
-
70
- export function parseLobeConfig(raw: string | null | undefined): LobeConfig {
71
- if (!raw) return { ...DEFAULT_LOBE_CONFIG };
72
- try {
73
- const parsed = JSON.parse(raw);
74
- return {
75
- isPrivate: parsed.isPrivate ?? false,
76
- excludedFrom: Array.isArray(parsed.excludedFrom) ? parsed.excludedFrom : [],
77
- subscriptions: Array.isArray(parsed.subscriptions) ? parsed.subscriptions : [],
78
- tags: Array.isArray(parsed.tags) ? parsed.tags : [],
79
- };
80
- } catch {
81
- return { ...DEFAULT_LOBE_CONFIG };
82
- }
83
- }
84
-
85
- export function serializeLobeConfig(config: LobeConfig): string {
86
- return JSON.stringify(config);
87
- }
88
- ```
89
-
90
- - [ ] **Step 2: Commit**
91
-
92
- ```bash
93
- git add src/lib/cortex/lobes/config.ts
94
- git commit -m "feat(cortex): add lobe config types and defaults"
95
- ```
96
-
97
- ---
98
-
99
- ### Task 2: Database migration — add lobe_config column
100
-
101
- **Files:**
102
- - Modify: `src/lib/db/schema.ts`
103
-
104
- - [ ] **Step 1: Read schema.ts to find the migration section**
105
-
106
- Read `src/lib/db/schema.ts`. Find the section with `addCol` calls (around line 120+). Add:
107
-
108
- ```typescript
109
- addCol('workspaces', 'lobe_config', "TEXT DEFAULT '{}'");
110
- ```
111
-
112
- This adds a JSON text column to the existing workspaces table with an empty config default.
113
-
114
- - [ ] **Step 2: Commit**
115
-
116
- ```bash
117
- git add src/lib/db/schema.ts
118
- git commit -m "feat(cortex): add lobe_config column to workspaces table"
119
- ```
120
-
121
- ---
122
-
123
- ### Task 3: Lobe resolver — compute accessible lobes for a workspace
124
-
125
- **Files:**
126
- - Create: `src/lib/cortex/lobes/resolver.ts`
127
- - Create: `tests/lib/cortex/lobes/resolver.test.ts`
128
-
129
- - [ ] **Step 1: Write failing tests**
130
-
131
- ```typescript
132
- // tests/lib/cortex/lobes/resolver.test.ts
133
- import { describe, it, expect } from 'vitest';
134
- import { resolveLobes } from '@/lib/cortex/lobes/resolver';
135
- import type { LobeConfig } from '@/lib/cortex/lobes/config';
136
- import { DEFAULT_LOBE_CONFIG } from '@/lib/cortex/lobes/config';
137
-
138
- // Minimal workspace shape for testing
139
- interface TestWorkspace {
140
- id: number;
141
- name: string;
142
- lobeConfig: LobeConfig;
143
- }
144
-
145
- describe('resolveLobes', () => {
146
- const workspaces: TestWorkspace[] = [
147
- { id: 1, name: 'Auth Service', lobeConfig: DEFAULT_LOBE_CONFIG },
148
- { id: 2, name: 'Frontend', lobeConfig: DEFAULT_LOBE_CONFIG },
149
- { id: 3, name: 'Private Project', lobeConfig: { ...DEFAULT_LOBE_CONFIG, isPrivate: true } },
150
- { id: 4, name: 'Excluded', lobeConfig: { ...DEFAULT_LOBE_CONFIG, excludedFrom: [1] } },
151
- ];
152
-
153
- it('includes own workspace lobe', () => {
154
- const lobes = resolveLobes({ workspaceId: 1, allWorkspaces: workspaces });
155
- const keys = lobes.map(l => l.layerKey);
156
- expect(keys).toContain('workspace/1');
157
- });
158
-
159
- it('includes personal lobe', () => {
160
- const lobes = resolveLobes({ workspaceId: 1, allWorkspaces: workspaces });
161
- const keys = lobes.map(l => l.layerKey);
162
- expect(keys).toContain('personal');
163
- });
164
-
165
- it('includes other non-private workspaces by default', () => {
166
- const lobes = resolveLobes({ workspaceId: 1, allWorkspaces: workspaces });
167
- const keys = lobes.map(l => l.layerKey);
168
- expect(keys).toContain('workspace/2');
169
- });
170
-
171
- it('excludes private workspaces', () => {
172
- const lobes = resolveLobes({ workspaceId: 1, allWorkspaces: workspaces });
173
- const keys = lobes.map(l => l.layerKey);
174
- expect(keys).not.toContain('workspace/3');
175
- });
176
-
177
- it('excludes workspaces that exclude the requester', () => {
178
- // Workspace 4 excludes workspace 1
179
- const lobes = resolveLobes({ workspaceId: 1, allWorkspaces: workspaces });
180
- const keys = lobes.map(l => l.layerKey);
181
- expect(keys).not.toContain('workspace/4');
182
- });
183
-
184
- it('includes team lobe by default', () => {
185
- const lobes = resolveLobes({ workspaceId: 1, allWorkspaces: workspaces });
186
- const keys = lobes.map(l => l.layerKey);
187
- expect(keys).toContain('team');
188
- });
189
-
190
- it('includes explicit subscriptions', () => {
191
- const ws: TestWorkspace[] = [
192
- {
193
- id: 1, name: 'Main',
194
- lobeConfig: {
195
- ...DEFAULT_LOBE_CONFIG,
196
- subscriptions: [{ type: 'tag', id: 'infrastructure', label: 'Infrastructure' }],
197
- },
198
- },
199
- ];
200
- const lobes = resolveLobes({ workspaceId: 1, allWorkspaces: ws });
201
- const tags = lobes.filter(l => l.type === 'tag');
202
- expect(tags).toHaveLength(1);
203
- expect(tags[0].id).toBe('infrastructure');
204
- });
205
-
206
- it('assigns lower weight to subscribed lobes vs inherited', () => {
207
- const ws: TestWorkspace[] = [
208
- {
209
- id: 1, name: 'Main',
210
- lobeConfig: {
211
- ...DEFAULT_LOBE_CONFIG,
212
- subscriptions: [{ type: 'workspace', id: '99', label: 'Remote' }],
213
- },
214
- },
215
- ];
216
- const lobes = resolveLobes({ workspaceId: 1, allWorkspaces: ws });
217
- const own = lobes.find(l => l.layerKey === 'workspace/1');
218
- const subscribed = lobes.find(l => l.layerKey === 'workspace/99');
219
- expect(own!.baseWeight).toBeGreaterThan(subscribed!.baseWeight);
220
- });
221
- });
222
- ```
223
-
224
- - [ ] **Step 2: Run tests to verify they fail**
225
-
226
- Run: `npx vitest run tests/lib/cortex/lobes/resolver.test.ts`
227
-
228
- - [ ] **Step 3: Implement lobe resolver**
229
-
230
- ```typescript
231
- // src/lib/cortex/lobes/resolver.ts
232
- import type { LobeConfig, LobeSubscription } from './config';
233
-
234
- export interface ResolvedLobe {
235
- layerKey: string; // LanceDB storage path (e.g., 'workspace/42', 'personal', 'team')
236
- label: string; // display name
237
- type: 'own' | 'personal' | 'workspace' | 'team' | 'department' | 'organization' | 'tag' | 'user';
238
- id: string; // source identifier
239
- baseWeight: number; // base retrieval weight (before graph proximity)
240
- inherited: boolean; // true if auto-inherited, false if explicitly subscribed
241
- }
242
-
243
- interface WorkspaceInfo {
244
- id: number;
245
- name: string;
246
- lobeConfig: LobeConfig;
247
- }
248
-
249
- interface ResolveInput {
250
- workspaceId: number;
251
- allWorkspaces: WorkspaceInfo[];
252
- userId?: string; // for personal lobe entity ID
253
- }
254
-
255
- /**
256
- * Resolve the list of accessible knowledge lobes for a workspace.
257
- *
258
- * Default behavior: own workspace + personal + all non-private sibling workspaces + team + org.
259
- * Respects privacy, exclusions, and explicit subscriptions.
260
- */
261
- export function resolveLobes(input: ResolveInput): ResolvedLobe[] {
262
- const { workspaceId, allWorkspaces, userId } = input;
263
- const lobes: ResolvedLobe[] = [];
264
-
265
- const thisWs = allWorkspaces.find(w => w.id === workspaceId);
266
- const thisConfig = thisWs?.lobeConfig;
267
-
268
- // 1. Own workspace lobe (always included)
269
- lobes.push({
270
- layerKey: `workspace/${workspaceId}`,
271
- label: thisWs?.name ?? 'This workspace',
272
- type: 'own',
273
- id: String(workspaceId),
274
- baseWeight: 1.0,
275
- inherited: true,
276
- });
277
-
278
- // 2. Personal lobe (always included)
279
- lobes.push({
280
- layerKey: 'personal',
281
- label: 'Personal',
282
- type: 'personal',
283
- id: userId ?? 'personal',
284
- baseWeight: 0.9,
285
- inherited: true,
286
- });
287
-
288
- // 3. Other workspaces (same user) — included unless private or excluded
289
- for (const ws of allWorkspaces) {
290
- if (ws.id === workspaceId) continue;
291
-
292
- // Skip if the other workspace is private
293
- if (ws.lobeConfig.isPrivate) continue;
294
-
295
- // Skip if the other workspace excludes this workspace
296
- if (ws.lobeConfig.excludedFrom.includes(workspaceId)) continue;
297
-
298
- lobes.push({
299
- layerKey: `workspace/${ws.id}`,
300
- label: ws.name,
301
- type: 'workspace',
302
- id: String(ws.id),
303
- baseWeight: 0.6,
304
- inherited: true,
305
- });
306
- }
307
-
308
- // 4. Team / org inherited lobes
309
- lobes.push({
310
- layerKey: 'team',
311
- label: 'Team',
312
- type: 'team',
313
- id: 'team',
314
- baseWeight: 0.5,
315
- inherited: true,
316
- });
317
-
318
- // 5. Explicit subscriptions from this workspace's config
319
- if (thisConfig?.subscriptions) {
320
- for (const sub of thisConfig.subscriptions) {
321
- // Avoid duplicates
322
- const layerKey = sub.type === 'workspace' ? `workspace/${sub.id}`
323
- : sub.type === 'tag' ? `tag/${sub.id}`
324
- : sub.type === 'team' ? `team/${sub.id}`
325
- : sub.type === 'user' ? `user/${sub.id}`
326
- : sub.id;
327
-
328
- if (lobes.some(l => l.layerKey === layerKey)) continue;
329
-
330
- lobes.push({
331
- layerKey,
332
- label: sub.label,
333
- type: sub.type as ResolvedLobe['type'],
334
- id: sub.id,
335
- baseWeight: 0.4, // subscribed = lower weight than inherited
336
- inherited: false,
337
- });
338
- }
339
- }
340
-
341
- return lobes;
342
- }
343
- ```
344
-
345
- - [ ] **Step 4: Run tests to verify they pass**
346
-
347
- Run: `npx vitest run tests/lib/cortex/lobes/resolver.test.ts`
348
- Expected: PASS (8 tests)
349
-
350
- - [ ] **Step 5: Commit**
351
-
352
- ```bash
353
- git add src/lib/cortex/lobes/resolver.ts tests/lib/cortex/lobes/resolver.test.ts
354
- git commit -m "feat(cortex): add lobe resolver for workspace knowledge scoping"
355
- ```
356
-
357
- ---
358
-
359
- ## Chunk 2: Cross-User Sharing and Context Engine Integration
360
-
361
- ### Task 4: Cross-user lobe sharing
362
-
363
- **Files:**
364
- - Create: `src/lib/cortex/lobes/shares.ts`
365
- - Create: `tests/lib/cortex/lobes/shares.test.ts`
366
-
367
- - [ ] **Step 1: Write failing tests**
368
-
369
- ```typescript
370
- // tests/lib/cortex/lobes/shares.test.ts
371
- import { describe, it, expect, beforeEach, afterEach } from 'vitest';
372
- import fs from 'fs';
373
- import path from 'path';
374
- import os from 'os';
375
- import Database from 'better-sqlite3';
376
- import { LobeShareStore } from '@/lib/cortex/lobes/shares';
377
-
378
- describe('LobeShareStore', () => {
379
- let tmpDir: string;
380
- let store: LobeShareStore;
381
-
382
- beforeEach(() => {
383
- tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'lobe-shares-'));
384
- const db = new Database(path.join(tmpDir, 'test.db'));
385
- store = new LobeShareStore(db);
386
- });
387
-
388
- afterEach(() => {
389
- fs.rmSync(tmpDir, { recursive: true, force: true });
390
- });
391
-
392
- it('creates a share', () => {
393
- const share = store.share({
394
- ownerUserId: 'person-alice',
395
- ownerWorkspaceId: 1,
396
- ownerLobeName: 'Auth Service',
397
- sharedWithUserId: 'person-bob',
398
- });
399
- expect(share.id).toBeDefined();
400
- expect(share.accepted).toBe(false);
401
- });
402
-
403
- it('accepts a share', () => {
404
- const share = store.share({
405
- ownerUserId: 'person-alice',
406
- ownerWorkspaceId: 1,
407
- ownerLobeName: 'Auth Service',
408
- sharedWithUserId: 'person-bob',
409
- });
410
- store.accept(share.id);
411
- const updated = store.getShare(share.id);
412
- expect(updated!.accepted).toBe(true);
413
- });
414
-
415
- it('lists incoming shares for a user', () => {
416
- store.share({ ownerUserId: 'person-alice', ownerWorkspaceId: 1, ownerLobeName: 'WS1', sharedWithUserId: 'person-bob' });
417
- store.share({ ownerUserId: 'person-charlie', ownerWorkspaceId: 2, ownerLobeName: 'WS2', sharedWithUserId: 'person-bob' });
418
- const incoming = store.listIncoming('person-bob');
419
- expect(incoming).toHaveLength(2);
420
- });
421
-
422
- it('lists outgoing shares for a user', () => {
423
- store.share({ ownerUserId: 'person-alice', ownerWorkspaceId: 1, ownerLobeName: 'WS1', sharedWithUserId: 'person-bob' });
424
- const outgoing = store.listOutgoing('person-alice');
425
- expect(outgoing).toHaveLength(1);
426
- });
427
-
428
- it('revokes a share', () => {
429
- const share = store.share({ ownerUserId: 'person-alice', ownerWorkspaceId: 1, ownerLobeName: 'WS1', sharedWithUserId: 'person-bob' });
430
- store.revoke(share.id);
431
- expect(store.getShare(share.id)).toBeNull();
432
- });
433
-
434
- it('prevents duplicate shares', () => {
435
- store.share({ ownerUserId: 'person-alice', ownerWorkspaceId: 1, ownerLobeName: 'WS1', sharedWithUserId: 'person-bob' });
436
- store.share({ ownerUserId: 'person-alice', ownerWorkspaceId: 1, ownerLobeName: 'WS1', sharedWithUserId: 'person-bob' });
437
- const incoming = store.listIncoming('person-bob');
438
- expect(incoming).toHaveLength(1);
439
- });
440
- });
441
- ```
442
-
443
- - [ ] **Step 2: Implement share store**
444
-
445
- ```typescript
446
- // src/lib/cortex/lobes/shares.ts
447
- import type Database from 'better-sqlite3';
448
-
449
- export interface LobeShare {
450
- id: string;
451
- ownerUserId: string;
452
- ownerWorkspaceId: number;
453
- ownerLobeName: string;
454
- sharedWithUserId: string;
455
- accepted: boolean;
456
- created: string;
457
- }
458
-
459
- interface ShareInput {
460
- ownerUserId: string;
461
- ownerWorkspaceId: number;
462
- ownerLobeName: string;
463
- sharedWithUserId: string;
464
- }
465
-
466
- export class LobeShareStore {
467
- private db: InstanceType<typeof Database>;
468
-
469
- constructor(db: InstanceType<typeof Database>) {
470
- this.db = db;
471
- this.db.exec(`
472
- CREATE TABLE IF NOT EXISTS lobe_shares (
473
- id TEXT PRIMARY KEY,
474
- owner_user_id TEXT NOT NULL,
475
- owner_workspace_id INTEGER NOT NULL,
476
- owner_lobe_name TEXT NOT NULL,
477
- shared_with_user_id TEXT NOT NULL,
478
- accepted INTEGER DEFAULT 0,
479
- created TEXT NOT NULL,
480
- UNIQUE(owner_user_id, owner_workspace_id, shared_with_user_id)
481
- );
482
- CREATE INDEX IF NOT EXISTS idx_lobe_shares_recipient ON lobe_shares(shared_with_user_id);
483
- CREATE INDEX IF NOT EXISTS idx_lobe_shares_owner ON lobe_shares(owner_user_id);
484
- `);
485
- }
486
-
487
- share(input: ShareInput): LobeShare {
488
- const id = crypto.randomUUID();
489
- const now = new Date().toISOString();
490
-
491
- this.db.prepare(`
492
- INSERT INTO lobe_shares (id, owner_user_id, owner_workspace_id, owner_lobe_name, shared_with_user_id, created)
493
- VALUES (?, ?, ?, ?, ?, ?)
494
- ON CONFLICT(owner_user_id, owner_workspace_id, shared_with_user_id) DO NOTHING
495
- `).run(id, input.ownerUserId, input.ownerWorkspaceId, input.ownerLobeName, input.sharedWithUserId, now);
496
-
497
- // Return existing if duplicate
498
- const existing = this.db.prepare(
499
- 'SELECT * FROM lobe_shares WHERE owner_user_id = ? AND owner_workspace_id = ? AND shared_with_user_id = ?'
500
- ).get(input.ownerUserId, input.ownerWorkspaceId, input.sharedWithUserId) as any;
501
-
502
- return this.rowToShare(existing);
503
- }
504
-
505
- accept(id: string): void {
506
- this.db.prepare('UPDATE lobe_shares SET accepted = 1 WHERE id = ?').run(id);
507
- }
508
-
509
- revoke(id: string): void {
510
- this.db.prepare('DELETE FROM lobe_shares WHERE id = ?').run(id);
511
- }
512
-
513
- getShare(id: string): LobeShare | null {
514
- const row = this.db.prepare('SELECT * FROM lobe_shares WHERE id = ?').get(id) as any;
515
- return row ? this.rowToShare(row) : null;
516
- }
517
-
518
- listIncoming(userId: string): LobeShare[] {
519
- const rows = this.db.prepare(
520
- 'SELECT * FROM lobe_shares WHERE shared_with_user_id = ? ORDER BY created DESC'
521
- ).all(userId) as any[];
522
- return rows.map(r => this.rowToShare(r));
523
- }
524
-
525
- listOutgoing(userId: string): LobeShare[] {
526
- const rows = this.db.prepare(
527
- 'SELECT * FROM lobe_shares WHERE owner_user_id = ? ORDER BY created DESC'
528
- ).all(userId) as any[];
529
- return rows.map(r => this.rowToShare(r));
530
- }
531
-
532
- listAcceptedForUser(userId: string): LobeShare[] {
533
- const rows = this.db.prepare(
534
- 'SELECT * FROM lobe_shares WHERE shared_with_user_id = ? AND accepted = 1'
535
- ).all(userId) as any[];
536
- return rows.map(r => this.rowToShare(r));
537
- }
538
-
539
- private rowToShare(row: any): LobeShare {
540
- return {
541
- id: row.id,
542
- ownerUserId: row.owner_user_id,
543
- ownerWorkspaceId: row.owner_workspace_id,
544
- ownerLobeName: row.owner_lobe_name,
545
- sharedWithUserId: row.shared_with_user_id,
546
- accepted: row.accepted === 1,
547
- created: row.created,
548
- };
549
- }
550
- }
551
- ```
552
-
553
- - [ ] **Step 3: Run tests, commit**
554
-
555
- Run: `npx vitest run tests/lib/cortex/lobes/shares.test.ts`
556
-
557
- ```bash
558
- git commit -m "feat(cortex): add cross-user lobe sharing with handshake"
559
- ```
560
-
561
- ---
562
-
563
- ### Task 5: Integrate lobe resolver with Context Assembly Engine
564
-
565
- **Files:**
566
- - Modify: `src/lib/cortex/retrieval/context-engine.ts`
567
-
568
- - [ ] **Step 1: Read context-engine.ts**
569
-
570
- Read the file. Find `computeSourceWeights()` — the private method that returns hardcoded layer definitions.
571
-
572
- - [ ] **Step 2: Add optional lobe-aware scope computation**
573
-
574
- Add to `ContextEngineDeps`:
575
- ```typescript
576
- resolvedLobes?: ResolvedLobe[]; // pre-computed accessible lobes for this workspace
577
- ```
578
-
579
- Import:
580
- ```typescript
581
- import type { ResolvedLobe } from '../lobes/resolver';
582
- ```
583
-
584
- Modify `computeSourceWeights()` to use resolved lobes when available:
585
-
586
- ```typescript
587
- private computeSourceWeights(
588
- intent: IntentResult,
589
- workspaceId: number | null,
590
- ): SourceConfig[] {
591
- // If lobes are provided, use them instead of hardcoded layers
592
- if (this.deps.resolvedLobes && this.deps.resolvedLobes.length > 0) {
593
- return this.deps.resolvedLobes.map(lobe => {
594
- const graphProximity = lobe.baseWeight; // lobes already have base weights
595
-
596
- const weight = computeScopeWeight({
597
- graphProximity,
598
- scopeLevel: lobe.type === 'personal' ? 'personal'
599
- : lobe.type === 'team' || lobe.type === 'department' ? 'team'
600
- : lobe.type === 'organization' ? 'organization'
601
- : 'team',
602
- intentBiases: intent.biases,
603
- authorityFactor: 1.0,
604
- });
605
-
606
- return {
607
- layerKey: lobe.layerKey,
608
- weight,
609
- limit: Math.max(3, Math.round(weight * 10)),
610
- };
611
- }).sort((a, b) => b.weight - a.weight);
612
- }
613
-
614
- // Fallback: hardcoded layers (backward compat when no lobe config)
615
- const layerDefs = [
616
- // ... existing code unchanged ...
617
- ];
618
- // ... rest of existing method unchanged ...
619
- }
620
- ```
621
-
622
- - [ ] **Step 3: Run existing context-engine tests to verify no regressions**
623
-
624
- Run: `npx vitest run tests/lib/cortex/retrieval/context-engine.test.ts`
625
-
626
- - [ ] **Step 4: Commit**
627
-
628
- ```bash
629
- git add src/lib/cortex/retrieval/context-engine.ts
630
- git commit -m "feat(cortex): integrate lobe resolver into context assembly engine"
631
- ```
632
-
633
- ---
634
-
635
- ## Chunk 3: API Routes and UI
636
-
637
- ### Task 6: Lobe API endpoints
638
-
639
- **Files:**
640
- - Create: `src/app/api/cortex/lobes/route.ts`
641
- - Create: `src/app/api/cortex/lobes/[id]/route.ts`
642
- - Create: `src/app/api/cortex/lobes/share/route.ts`
643
-
644
- - [ ] **Step 1: Create main lobes endpoint**
645
-
646
- ```typescript
647
- // src/app/api/cortex/lobes/route.ts
648
- import { NextResponse } from 'next/server';
649
- import type { NextRequest } from 'next/server';
650
- import { getAuthUser, withUser } from '@/lib/auth';
651
- import { getDb } from '@/lib/db';
652
- import { parseLobeConfig } from '@/lib/cortex/lobes/config';
653
-
654
- export async function GET(request: NextRequest) {
655
- const user = getAuthUser(request);
656
- return withUser(user, async () => {
657
- const db = getDb();
658
- const workspaces = db.prepare(
659
- 'SELECT id, name, color, lobe_config FROM workspaces ORDER BY name'
660
- ).all() as any[];
661
-
662
- const lobes = workspaces.map(ws => ({
663
- workspaceId: ws.id,
664
- name: ws.name,
665
- color: ws.color,
666
- config: parseLobeConfig(ws.lobe_config),
667
- }));
668
-
669
- return NextResponse.json({ lobes });
670
- });
671
- }
672
- ```
673
-
674
- - [ ] **Step 2: Create single workspace lobe config endpoint**
675
-
676
- ```typescript
677
- // src/app/api/cortex/lobes/[id]/route.ts
678
- import { NextResponse } from 'next/server';
679
- import type { NextRequest } from 'next/server';
680
- import { getAuthUser, withUser } from '@/lib/auth';
681
- import { getDb } from '@/lib/db';
682
- import { parseLobeConfig, serializeLobeConfig } from '@/lib/cortex/lobes/config';
683
- import type { LobeConfig } from '@/lib/cortex/lobes/config';
684
-
685
- export async function GET(
686
- request: NextRequest,
687
- { params }: { params: Promise<{ id: string }> },
688
- ) {
689
- const { id } = await params;
690
- const user = getAuthUser(request);
691
- return withUser(user, async () => {
692
- const db = getDb();
693
- const ws = db.prepare('SELECT id, name, lobe_config FROM workspaces WHERE id = ?').get(Number(id)) as any;
694
- if (!ws) return NextResponse.json({ error: 'Workspace not found' }, { status: 404 });
695
- return NextResponse.json({ workspaceId: ws.id, name: ws.name, config: parseLobeConfig(ws.lobe_config) });
696
- });
697
- }
698
-
699
- export async function PUT(
700
- request: NextRequest,
701
- { params }: { params: Promise<{ id: string }> },
702
- ) {
703
- const { id } = await params;
704
- const user = getAuthUser(request);
705
- return withUser(user, async () => {
706
- const db = getDb();
707
- const body = await request.json() as Partial<LobeConfig>;
708
-
709
- const ws = db.prepare('SELECT lobe_config FROM workspaces WHERE id = ?').get(Number(id)) as any;
710
- if (!ws) return NextResponse.json({ error: 'Workspace not found' }, { status: 404 });
711
-
712
- const current = parseLobeConfig(ws.lobe_config);
713
- const updated: LobeConfig = {
714
- isPrivate: body.isPrivate ?? current.isPrivate,
715
- excludedFrom: body.excludedFrom ?? current.excludedFrom,
716
- subscriptions: body.subscriptions ?? current.subscriptions,
717
- tags: body.tags ?? current.tags,
718
- };
719
-
720
- db.prepare('UPDATE workspaces SET lobe_config = ? WHERE id = ?').run(serializeLobeConfig(updated), Number(id));
721
- return NextResponse.json({ config: updated });
722
- });
723
- }
724
- ```
725
-
726
- - [ ] **Step 3: Create share endpoint**
727
-
728
- ```typescript
729
- // src/app/api/cortex/lobes/share/route.ts
730
- import { NextResponse } from 'next/server';
731
- import type { NextRequest } from 'next/server';
732
- import { getAuthUser, withUser } from '@/lib/auth';
733
- import { getCortex, isCortexAvailable } from '@/lib/cortex';
734
- import { LobeShareStore } from '@/lib/cortex/lobes/shares';
735
- import { slugify } from '@/lib/cortex/graph/types';
736
-
737
- export async function GET(request: NextRequest) {
738
- const user = getAuthUser(request);
739
- return withUser(user, async () => {
740
- if (!isCortexAvailable()) return NextResponse.json({ incoming: [], outgoing: [] });
741
- const cortex = await getCortex();
742
- if (!cortex) return NextResponse.json({ incoming: [], outgoing: [] });
743
-
744
- const shareStore = new LobeShareStore(cortex.graph['db']);
745
- const userId = `person-${slugify(user)}`;
746
-
747
- return NextResponse.json({
748
- incoming: shareStore.listIncoming(userId),
749
- outgoing: shareStore.listOutgoing(userId),
750
- });
751
- });
752
- }
753
-
754
- export async function POST(request: NextRequest) {
755
- const user = getAuthUser(request);
756
- return withUser(user, async () => {
757
- if (!isCortexAvailable()) return NextResponse.json({ error: 'Cortex unavailable' }, { status: 403 });
758
- const cortex = await getCortex();
759
- if (!cortex) return NextResponse.json({ error: 'Cortex unavailable' }, { status: 500 });
760
-
761
- const body = await request.json();
762
- const { action, shareId, workspaceId, lobeName, sharedWithUserId } = body;
763
- const shareStore = new LobeShareStore(cortex.graph['db']);
764
- const userId = `person-${slugify(user)}`;
765
-
766
- if (action === 'share') {
767
- const share = shareStore.share({
768
- ownerUserId: userId,
769
- ownerWorkspaceId: workspaceId,
770
- ownerLobeName: lobeName,
771
- sharedWithUserId,
772
- });
773
- return NextResponse.json({ share }, { status: 201 });
774
- }
775
-
776
- if (action === 'accept') {
777
- shareStore.accept(shareId);
778
- return NextResponse.json({ accepted: true });
779
- }
780
-
781
- if (action === 'revoke' || action === 'decline') {
782
- shareStore.revoke(shareId);
783
- return NextResponse.json({ revoked: true });
784
- }
785
-
786
- return NextResponse.json({ error: 'Invalid action' }, { status: 400 });
787
- });
788
- }
789
- ```
790
-
791
- - [ ] **Step 4: Commit**
792
-
793
- ```bash
794
- git add src/app/api/cortex/lobes/
795
- git commit -m "feat(cortex): add lobe API endpoints for config and sharing"
796
- ```
797
-
798
- ---
799
-
800
- ### Task 7: Lobe settings UI component
801
-
802
- **Files:**
803
- - Create: `src/components/cortex/lobe-settings.tsx`
804
-
805
- - [ ] **Step 1: Create the component**
806
-
807
- A settings panel showing the workspace's knowledge sources with toggles, tags, and subscription management.
808
-
809
- ```typescript
810
- 'use client';
811
-
812
- import { useState, useEffect, useCallback } from 'react';
813
- import { Shield, ShieldOff, Tag, Plus, X, Users } from 'lucide-react';
814
- import { api } from '@/lib/api';
815
- import type { LobeConfig, LobeSubscription } from '@/lib/cortex/lobes/config';
816
-
817
- interface LobeSettingsProps {
818
- workspaceId: number;
819
- workspaceName: string;
820
- }
821
-
822
- export function LobeSettings({ workspaceId, workspaceName }: LobeSettingsProps) {
823
- const [config, setConfig] = useState<LobeConfig | null>(null);
824
- const [allLobes, setAllLobes] = useState<any[]>([]);
825
- const [newTag, setNewTag] = useState('');
826
- const [saving, setSaving] = useState(false);
827
-
828
- const fetchConfig = useCallback(async () => {
829
- const res = await fetch(api(`/api/cortex/lobes/${workspaceId}`));
830
- if (res.ok) {
831
- const data = await res.json();
832
- setConfig(data.config);
833
- }
834
- }, [workspaceId]);
835
-
836
- const fetchAllLobes = useCallback(async () => {
837
- const res = await fetch(api('/api/cortex/lobes'));
838
- if (res.ok) {
839
- const data = await res.json();
840
- setAllLobes(data.lobes || []);
841
- }
842
- }, []);
843
-
844
- useEffect(() => { fetchConfig(); fetchAllLobes(); }, [fetchConfig, fetchAllLobes]);
845
-
846
- const save = async (updates: Partial<LobeConfig>) => {
847
- setSaving(true);
848
- await fetch(api(`/api/cortex/lobes/${workspaceId}`), {
849
- method: 'PUT',
850
- headers: { 'Content-Type': 'application/json' },
851
- body: JSON.stringify(updates),
852
- });
853
- await fetchConfig();
854
- setSaving(false);
855
- };
856
-
857
- if (!config) return <div className="text-gray-500 text-sm p-4">Loading...</div>;
858
-
859
- const activeSourceCount = allLobes.filter(l =>
860
- l.workspaceId !== workspaceId && !l.config.isPrivate && !l.config.excludedFrom?.includes(workspaceId)
861
- ).length + 2; // +2 for personal + team
862
-
863
- return (
864
- <div className="space-y-6">
865
- <div>
866
- <h3 className="text-sm font-medium text-gray-200 mb-1">Knowledge Lobes</h3>
867
- <p className="text-xs text-gray-500">
868
- This workspace draws from {activeSourceCount} lobes.
869
- {config.isPrivate && ' This lobe is private — other workspaces cannot access its knowledge.'}
870
- </p>
871
- </div>
872
-
873
- {/* Privacy toggle */}
874
- <div className="flex items-center justify-between py-3 border-t border-white/5">
875
- <div className="flex items-center gap-2">
876
- {config.isPrivate ? <ShieldOff className="w-4 h-4 text-red-400" /> : <Shield className="w-4 h-4 text-green-400" />}
877
- <div>
878
- <div className="text-sm text-gray-200">Private lobe</div>
879
- <div className="text-[10px] text-gray-500">Other workspaces cannot access this knowledge</div>
880
- </div>
881
- </div>
882
- <button
883
- onClick={() => save({ isPrivate: !config.isPrivate })}
884
- disabled={saving}
885
- className={`px-3 py-1 text-xs rounded ${config.isPrivate ? 'bg-red-500/20 text-red-400' : 'bg-white/5 text-gray-400'}`}
886
- >
887
- {config.isPrivate ? 'Private' : 'Open'}
888
- </button>
889
- </div>
890
-
891
- {/* Tags */}
892
- <div className="border-t border-white/5 pt-3">
893
- <div className="flex items-center gap-2 mb-2">
894
- <Tag className="w-3.5 h-3.5 text-gray-500" />
895
- <span className="text-xs text-gray-400">Tags</span>
896
- </div>
897
- <div className="flex flex-wrap gap-1.5 mb-2">
898
- {config.tags.map(tag => (
899
- <span key={tag} className="flex items-center gap-1 text-[11px] bg-purple-500/10 text-purple-400 px-2 py-0.5 rounded">
900
- {tag}
901
- <button onClick={() => save({ tags: config.tags.filter(t => t !== tag) })} className="hover:text-white">
902
- <X className="w-2.5 h-2.5" />
903
- </button>
904
- </span>
905
- ))}
906
- </div>
907
- <div className="flex gap-1.5">
908
- <input
909
- value={newTag}
910
- onChange={e => setNewTag(e.target.value)}
911
- onKeyDown={e => {
912
- if (e.key === 'Enter' && newTag.trim()) {
913
- save({ tags: [...config.tags, newTag.trim()] });
914
- setNewTag('');
915
- }
916
- }}
917
- placeholder="Add tag..."
918
- className="flex-1 px-2 py-1 text-xs bg-white/5 border border-white/10 rounded text-gray-300 focus:outline-none focus:border-purple-500/50"
919
- />
920
- </div>
921
- </div>
922
-
923
- {/* Subscriptions */}
924
- <div className="border-t border-white/5 pt-3">
925
- <div className="flex items-center gap-2 mb-2">
926
- <Plus className="w-3.5 h-3.5 text-gray-500" />
927
- <span className="text-xs text-gray-400">Additional sources</span>
928
- </div>
929
- {config.subscriptions.length === 0 ? (
930
- <p className="text-[11px] text-gray-600">No additional subscriptions. Using defaults.</p>
931
- ) : (
932
- <div className="space-y-1">
933
- {config.subscriptions.map((sub, i) => (
934
- <div key={i} className="flex items-center justify-between text-xs bg-white/[0.02] rounded px-2 py-1.5">
935
- <span className="text-gray-300">{sub.label} <span className="text-gray-600">({sub.type})</span></span>
936
- <button
937
- onClick={() => save({ subscriptions: config.subscriptions.filter((_, j) => j !== i) })}
938
- className="text-gray-600 hover:text-red-400"
939
- >
940
- <X className="w-3 h-3" />
941
- </button>
942
- </div>
943
- ))}
944
- </div>
945
- )}
946
- </div>
947
-
948
- {/* Exclusions */}
949
- <div className="border-t border-white/5 pt-3">
950
- <div className="flex items-center gap-2 mb-2">
951
- <Users className="w-3.5 h-3.5 text-gray-500" />
952
- <span className="text-xs text-gray-400">Excluded workspaces</span>
953
- </div>
954
- {config.excludedFrom.length === 0 ? (
955
- <p className="text-[11px] text-gray-600">No exclusions. All workspaces can access this lobe.</p>
956
- ) : (
957
- <div className="space-y-1">
958
- {config.excludedFrom.map(wsId => {
959
- const ws = allLobes.find(l => l.workspaceId === wsId);
960
- return (
961
- <div key={wsId} className="flex items-center justify-between text-xs bg-white/[0.02] rounded px-2 py-1.5">
962
- <span className="text-gray-300">{ws?.name || `Workspace ${wsId}`}</span>
963
- <button
964
- onClick={() => save({ excludedFrom: config.excludedFrom.filter(id => id !== wsId) })}
965
- className="text-gray-600 hover:text-red-400"
966
- >
967
- <X className="w-3 h-3" />
968
- </button>
969
- </div>
970
- );
971
- })}
972
- </div>
973
- )}
974
- </div>
975
- </div>
976
- );
977
- }
978
- ```
979
-
980
- - [ ] **Step 2: Commit**
981
-
982
- ```bash
983
- git add src/components/cortex/lobe-settings.tsx
984
- git commit -m "feat(cortex): add lobe settings UI component"
985
- ```
986
-
987
- ---
988
-
989
- ### Task 8: Barrel export and wiring
990
-
991
- **Files:**
992
- - Create: `src/lib/cortex/lobes/index.ts`
993
- - Modify: `src/app/(desktop)/cortex/page.tsx` — add Lobes section to Settings tab
994
-
995
- - [ ] **Step 1: Create barrel export**
996
-
997
- ```typescript
998
- // src/lib/cortex/lobes/index.ts
999
- export { parseLobeConfig, serializeLobeConfig, DEFAULT_LOBE_CONFIG } from './config';
1000
- export type { LobeConfig, LobeSubscription } from './config';
1001
- export { resolveLobes } from './resolver';
1002
- export type { ResolvedLobe } from './resolver';
1003
- export { LobeShareStore } from './shares';
1004
- export type { LobeShare } from './shares';
1005
- ```
1006
-
1007
- - [ ] **Step 2: Add LobeSettings to the Cortex page Settings tab**
1008
-
1009
- Read `src/app/(desktop)/cortex/page.tsx`. Find the Settings tab rendering. Currently it shows `<CortexSettings />`. Add `<LobeSettings>` below it, but only when a workspace is active. The workspace ID needs to come from somewhere — check how the terminal page gets `activeWorkspace`:
1010
-
1011
- Read `src/app/(desktop)/terminal/page.tsx` to see how `activeWorkspace` is loaded. The pattern is likely a fetch to `/api/workspaces` with `is_active=1`.
1012
-
1013
- For the Cortex page, add a simple workspace selector or use the active workspace. The simplest approach: fetch the active workspace and pass its ID to LobeSettings.
1014
-
1015
- ```typescript
1016
- // In CortexPage, add state:
1017
- const [activeWorkspace, setActiveWorkspace] = useState<any>(null);
1018
-
1019
- // Fetch active workspace:
1020
- useEffect(() => {
1021
- fetch(api('/api/workspaces'))
1022
- .then(r => r.json())
1023
- .then(data => {
1024
- const active = (data.workspaces || []).find((w: any) => w.isActive);
1025
- setActiveWorkspace(active);
1026
- })
1027
- .catch(() => {});
1028
- }, []);
1029
-
1030
- // In Settings tab rendering, add LobeSettings:
1031
- {tab === 'settings' && (
1032
- <div className="p-6 max-w-2xl space-y-8">
1033
- <CortexSettings />
1034
- {activeWorkspace && (
1035
- <LobeSettings
1036
- workspaceId={activeWorkspace.id}
1037
- workspaceName={activeWorkspace.name}
1038
- />
1039
- )}
1040
- </div>
1041
- )}
1042
- ```
1043
-
1044
- Import: `import { LobeSettings } from '@/components/cortex/lobe-settings';`
1045
-
1046
- - [ ] **Step 3: Run full test suite**
1047
-
1048
- Run: `npx vitest run tests/lib/cortex/`
1049
-
1050
- - [ ] **Step 4: Commit**
1051
-
1052
- ```bash
1053
- git add src/lib/cortex/lobes/index.ts src/app/(desktop)/cortex/page.tsx
1054
- git commit -m "feat(cortex): wire lobe settings into Cortex page"
1055
- ```
1056
-
1057
- ---
1058
-
1059
- ## Summary
1060
-
1061
- | Task | Component | Tests | Status |
1062
- |------|-----------|-------|--------|
1063
- | 1 | Lobe config types | — | |
1064
- | 2 | DB migration (lobe_config column) | — | |
1065
- | 3 | Lobe resolver | 8 | |
1066
- | 4 | Cross-user sharing | 6 | |
1067
- | 5 | Context Engine integration | regression | |
1068
- | 6 | API endpoints (3 files) | — | |
1069
- | 7 | Lobe settings UI component | — | |
1070
- | 8 | Barrel export + page wiring | regression | |
1071
-
1072
- **Total: 8 tasks, ~14 new tests, 3 chunks**
1073
-
1074
- **Key design decisions:**
1075
- - Lobe config stored as JSON column on workspaces table (simple, always loaded with workspace)
1076
- - Resolver computes accessible lobes at query time from workspace list + config
1077
- - Cross-user shares use a separate SQLite table in the entity graph DB with two-step handshake
1078
- - Context Engine uses resolved lobes when provided, falls back to hardcoded layers for backward compat
1079
- - Privacy is safe-by-default: private workspaces are excluded, cross-user is closed by default
1080
- - Base weights: own=1.0, personal=0.9, sibling workspaces=0.6, team=0.5, subscribed=0.4
1
+ # Cortex Lobes Implementation Plan
2
+
3
+ > **For agentic workers:** REQUIRED: Use superpowers:subagent-driven-development (if subagents available) or superpowers:executing-plans to implement this plan. Steps use checkbox (`- [ ]`) syntax for tracking.
4
+
5
+ **Goal:** Add knowledge compartmentalization ("lobes") to Cortex so workspaces control which knowledge sources they can pull from — open by default within a user, closed by default across users, with privacy, exclusions, and cross-user sharing.
6
+
7
+ **Architecture:** A new `src/lib/cortex/lobes/` module containing lobe config types, a resolver that computes accessible lobes for a workspace, and sharing logic. The ContextEngine's `computeSourceWeights()` is modified to use the lobe resolver instead of hardcoded layers. Lobe config is stored as a JSON column on the workspaces table. Cross-user shares live in the entity graph's SQLite database.
8
+
9
+ **Tech Stack:** TypeScript, better-sqlite3 (entity graph DB), vitest
10
+
11
+ **Spec:** `docs/superpowers/specs/2026-03-16-cortex-lobes-design.md`
12
+
13
+ ---
14
+
15
+ ## File Structure
16
+
17
+ ```
18
+ New files:
19
+ ├── src/lib/cortex/lobes/config.ts — LobeConfig types and defaults
20
+ ├── src/lib/cortex/lobes/resolver.ts — Resolve accessible lobes for a workspace
21
+ ├── src/lib/cortex/lobes/shares.ts — Cross-user sharing (lobe_shares table)
22
+ ├── src/lib/cortex/lobes/index.ts — Barrel export
23
+ ├── src/app/api/cortex/lobes/route.ts — List lobes, update config
24
+ ├── src/app/api/cortex/lobes/share/route.ts — Share management
25
+ ├── src/components/cortex/lobe-settings.tsx — UI component for workspace settings
26
+
27
+ Test files:
28
+ ├── tests/lib/cortex/lobes/resolver.test.ts
29
+ ├── tests/lib/cortex/lobes/shares.test.ts
30
+
31
+ Modified files:
32
+ ├── src/lib/db/schema.ts — Add lobe_config column to workspaces
33
+ ├── src/lib/cortex/retrieval/context-engine.ts — Use lobe resolver for search scopes
34
+ ```
35
+
36
+ ---
37
+
38
+ ## Chunk 1: Types, DB Migration, and Lobe Resolver
39
+
40
+ ### Task 1: Lobe config types and defaults
41
+
42
+ **Files:**
43
+ - Create: `src/lib/cortex/lobes/config.ts`
44
+
45
+ - [ ] **Step 1: Create the types file**
46
+
47
+ ```typescript
48
+ // src/lib/cortex/lobes/config.ts
49
+
50
+ export interface LobeSubscription {
51
+ type: 'workspace' | 'user' | 'tag' | 'team' | 'department' | 'organization';
52
+ id: string; // workspace ID, user entity ID, tag name, etc.
53
+ label: string; // display name
54
+ }
55
+
56
+ export interface LobeConfig {
57
+ isPrivate: boolean;
58
+ excludedFrom: number[]; // workspace IDs blocked from accessing this lobe
59
+ subscriptions: LobeSubscription[];
60
+ tags: string[];
61
+ }
62
+
63
+ export const DEFAULT_LOBE_CONFIG: LobeConfig = {
64
+ isPrivate: false,
65
+ excludedFrom: [],
66
+ subscriptions: [],
67
+ tags: [],
68
+ };
69
+
70
+ export function parseLobeConfig(raw: string | null | undefined): LobeConfig {
71
+ if (!raw) return { ...DEFAULT_LOBE_CONFIG };
72
+ try {
73
+ const parsed = JSON.parse(raw);
74
+ return {
75
+ isPrivate: parsed.isPrivate ?? false,
76
+ excludedFrom: Array.isArray(parsed.excludedFrom) ? parsed.excludedFrom : [],
77
+ subscriptions: Array.isArray(parsed.subscriptions) ? parsed.subscriptions : [],
78
+ tags: Array.isArray(parsed.tags) ? parsed.tags : [],
79
+ };
80
+ } catch {
81
+ return { ...DEFAULT_LOBE_CONFIG };
82
+ }
83
+ }
84
+
85
+ export function serializeLobeConfig(config: LobeConfig): string {
86
+ return JSON.stringify(config);
87
+ }
88
+ ```
89
+
90
+ - [ ] **Step 2: Commit**
91
+
92
+ ```bash
93
+ git add src/lib/cortex/lobes/config.ts
94
+ git commit -m "feat(cortex): add lobe config types and defaults"
95
+ ```
96
+
97
+ ---
98
+
99
+ ### Task 2: Database migration — add lobe_config column
100
+
101
+ **Files:**
102
+ - Modify: `src/lib/db/schema.ts`
103
+
104
+ - [ ] **Step 1: Read schema.ts to find the migration section**
105
+
106
+ Read `src/lib/db/schema.ts`. Find the section with `addCol` calls (around line 120+). Add:
107
+
108
+ ```typescript
109
+ addCol('workspaces', 'lobe_config', "TEXT DEFAULT '{}'");
110
+ ```
111
+
112
+ This adds a JSON text column to the existing workspaces table with an empty config default.
113
+
114
+ - [ ] **Step 2: Commit**
115
+
116
+ ```bash
117
+ git add src/lib/db/schema.ts
118
+ git commit -m "feat(cortex): add lobe_config column to workspaces table"
119
+ ```
120
+
121
+ ---
122
+
123
+ ### Task 3: Lobe resolver — compute accessible lobes for a workspace
124
+
125
+ **Files:**
126
+ - Create: `src/lib/cortex/lobes/resolver.ts`
127
+ - Create: `tests/lib/cortex/lobes/resolver.test.ts`
128
+
129
+ - [ ] **Step 1: Write failing tests**
130
+
131
+ ```typescript
132
+ // tests/lib/cortex/lobes/resolver.test.ts
133
+ import { describe, it, expect } from 'vitest';
134
+ import { resolveLobes } from '@/lib/cortex/lobes/resolver';
135
+ import type { LobeConfig } from '@/lib/cortex/lobes/config';
136
+ import { DEFAULT_LOBE_CONFIG } from '@/lib/cortex/lobes/config';
137
+
138
+ // Minimal workspace shape for testing
139
+ interface TestWorkspace {
140
+ id: number;
141
+ name: string;
142
+ lobeConfig: LobeConfig;
143
+ }
144
+
145
+ describe('resolveLobes', () => {
146
+ const workspaces: TestWorkspace[] = [
147
+ { id: 1, name: 'Auth Service', lobeConfig: DEFAULT_LOBE_CONFIG },
148
+ { id: 2, name: 'Frontend', lobeConfig: DEFAULT_LOBE_CONFIG },
149
+ { id: 3, name: 'Private Project', lobeConfig: { ...DEFAULT_LOBE_CONFIG, isPrivate: true } },
150
+ { id: 4, name: 'Excluded', lobeConfig: { ...DEFAULT_LOBE_CONFIG, excludedFrom: [1] } },
151
+ ];
152
+
153
+ it('includes own workspace lobe', () => {
154
+ const lobes = resolveLobes({ workspaceId: 1, allWorkspaces: workspaces });
155
+ const keys = lobes.map(l => l.layerKey);
156
+ expect(keys).toContain('workspace/1');
157
+ });
158
+
159
+ it('includes personal lobe', () => {
160
+ const lobes = resolveLobes({ workspaceId: 1, allWorkspaces: workspaces });
161
+ const keys = lobes.map(l => l.layerKey);
162
+ expect(keys).toContain('personal');
163
+ });
164
+
165
+ it('includes other non-private workspaces by default', () => {
166
+ const lobes = resolveLobes({ workspaceId: 1, allWorkspaces: workspaces });
167
+ const keys = lobes.map(l => l.layerKey);
168
+ expect(keys).toContain('workspace/2');
169
+ });
170
+
171
+ it('excludes private workspaces', () => {
172
+ const lobes = resolveLobes({ workspaceId: 1, allWorkspaces: workspaces });
173
+ const keys = lobes.map(l => l.layerKey);
174
+ expect(keys).not.toContain('workspace/3');
175
+ });
176
+
177
+ it('excludes workspaces that exclude the requester', () => {
178
+ // Workspace 4 excludes workspace 1
179
+ const lobes = resolveLobes({ workspaceId: 1, allWorkspaces: workspaces });
180
+ const keys = lobes.map(l => l.layerKey);
181
+ expect(keys).not.toContain('workspace/4');
182
+ });
183
+
184
+ it('includes team lobe by default', () => {
185
+ const lobes = resolveLobes({ workspaceId: 1, allWorkspaces: workspaces });
186
+ const keys = lobes.map(l => l.layerKey);
187
+ expect(keys).toContain('team');
188
+ });
189
+
190
+ it('includes explicit subscriptions', () => {
191
+ const ws: TestWorkspace[] = [
192
+ {
193
+ id: 1, name: 'Main',
194
+ lobeConfig: {
195
+ ...DEFAULT_LOBE_CONFIG,
196
+ subscriptions: [{ type: 'tag', id: 'infrastructure', label: 'Infrastructure' }],
197
+ },
198
+ },
199
+ ];
200
+ const lobes = resolveLobes({ workspaceId: 1, allWorkspaces: ws });
201
+ const tags = lobes.filter(l => l.type === 'tag');
202
+ expect(tags).toHaveLength(1);
203
+ expect(tags[0].id).toBe('infrastructure');
204
+ });
205
+
206
+ it('assigns lower weight to subscribed lobes vs inherited', () => {
207
+ const ws: TestWorkspace[] = [
208
+ {
209
+ id: 1, name: 'Main',
210
+ lobeConfig: {
211
+ ...DEFAULT_LOBE_CONFIG,
212
+ subscriptions: [{ type: 'workspace', id: '99', label: 'Remote' }],
213
+ },
214
+ },
215
+ ];
216
+ const lobes = resolveLobes({ workspaceId: 1, allWorkspaces: ws });
217
+ const own = lobes.find(l => l.layerKey === 'workspace/1');
218
+ const subscribed = lobes.find(l => l.layerKey === 'workspace/99');
219
+ expect(own!.baseWeight).toBeGreaterThan(subscribed!.baseWeight);
220
+ });
221
+ });
222
+ ```
223
+
224
+ - [ ] **Step 2: Run tests to verify they fail**
225
+
226
+ Run: `npx vitest run tests/lib/cortex/lobes/resolver.test.ts`
227
+
228
+ - [ ] **Step 3: Implement lobe resolver**
229
+
230
+ ```typescript
231
+ // src/lib/cortex/lobes/resolver.ts
232
+ import type { LobeConfig, LobeSubscription } from './config';
233
+
234
+ export interface ResolvedLobe {
235
+ layerKey: string; // LanceDB storage path (e.g., 'workspace/42', 'personal', 'team')
236
+ label: string; // display name
237
+ type: 'own' | 'personal' | 'workspace' | 'team' | 'department' | 'organization' | 'tag' | 'user';
238
+ id: string; // source identifier
239
+ baseWeight: number; // base retrieval weight (before graph proximity)
240
+ inherited: boolean; // true if auto-inherited, false if explicitly subscribed
241
+ }
242
+
243
+ interface WorkspaceInfo {
244
+ id: number;
245
+ name: string;
246
+ lobeConfig: LobeConfig;
247
+ }
248
+
249
+ interface ResolveInput {
250
+ workspaceId: number;
251
+ allWorkspaces: WorkspaceInfo[];
252
+ userId?: string; // for personal lobe entity ID
253
+ }
254
+
255
+ /**
256
+ * Resolve the list of accessible knowledge lobes for a workspace.
257
+ *
258
+ * Default behavior: own workspace + personal + all non-private sibling workspaces + team + org.
259
+ * Respects privacy, exclusions, and explicit subscriptions.
260
+ */
261
+ export function resolveLobes(input: ResolveInput): ResolvedLobe[] {
262
+ const { workspaceId, allWorkspaces, userId } = input;
263
+ const lobes: ResolvedLobe[] = [];
264
+
265
+ const thisWs = allWorkspaces.find(w => w.id === workspaceId);
266
+ const thisConfig = thisWs?.lobeConfig;
267
+
268
+ // 1. Own workspace lobe (always included)
269
+ lobes.push({
270
+ layerKey: `workspace/${workspaceId}`,
271
+ label: thisWs?.name ?? 'This workspace',
272
+ type: 'own',
273
+ id: String(workspaceId),
274
+ baseWeight: 1.0,
275
+ inherited: true,
276
+ });
277
+
278
+ // 2. Personal lobe (always included)
279
+ lobes.push({
280
+ layerKey: 'personal',
281
+ label: 'Personal',
282
+ type: 'personal',
283
+ id: userId ?? 'personal',
284
+ baseWeight: 0.9,
285
+ inherited: true,
286
+ });
287
+
288
+ // 3. Other workspaces (same user) — included unless private or excluded
289
+ for (const ws of allWorkspaces) {
290
+ if (ws.id === workspaceId) continue;
291
+
292
+ // Skip if the other workspace is private
293
+ if (ws.lobeConfig.isPrivate) continue;
294
+
295
+ // Skip if the other workspace excludes this workspace
296
+ if (ws.lobeConfig.excludedFrom.includes(workspaceId)) continue;
297
+
298
+ lobes.push({
299
+ layerKey: `workspace/${ws.id}`,
300
+ label: ws.name,
301
+ type: 'workspace',
302
+ id: String(ws.id),
303
+ baseWeight: 0.6,
304
+ inherited: true,
305
+ });
306
+ }
307
+
308
+ // 4. Team / org inherited lobes
309
+ lobes.push({
310
+ layerKey: 'team',
311
+ label: 'Team',
312
+ type: 'team',
313
+ id: 'team',
314
+ baseWeight: 0.5,
315
+ inherited: true,
316
+ });
317
+
318
+ // 5. Explicit subscriptions from this workspace's config
319
+ if (thisConfig?.subscriptions) {
320
+ for (const sub of thisConfig.subscriptions) {
321
+ // Avoid duplicates
322
+ const layerKey = sub.type === 'workspace' ? `workspace/${sub.id}`
323
+ : sub.type === 'tag' ? `tag/${sub.id}`
324
+ : sub.type === 'team' ? `team/${sub.id}`
325
+ : sub.type === 'user' ? `user/${sub.id}`
326
+ : sub.id;
327
+
328
+ if (lobes.some(l => l.layerKey === layerKey)) continue;
329
+
330
+ lobes.push({
331
+ layerKey,
332
+ label: sub.label,
333
+ type: sub.type as ResolvedLobe['type'],
334
+ id: sub.id,
335
+ baseWeight: 0.4, // subscribed = lower weight than inherited
336
+ inherited: false,
337
+ });
338
+ }
339
+ }
340
+
341
+ return lobes;
342
+ }
343
+ ```
344
+
345
+ - [ ] **Step 4: Run tests to verify they pass**
346
+
347
+ Run: `npx vitest run tests/lib/cortex/lobes/resolver.test.ts`
348
+ Expected: PASS (8 tests)
349
+
350
+ - [ ] **Step 5: Commit**
351
+
352
+ ```bash
353
+ git add src/lib/cortex/lobes/resolver.ts tests/lib/cortex/lobes/resolver.test.ts
354
+ git commit -m "feat(cortex): add lobe resolver for workspace knowledge scoping"
355
+ ```
356
+
357
+ ---
358
+
359
+ ## Chunk 2: Cross-User Sharing and Context Engine Integration
360
+
361
+ ### Task 4: Cross-user lobe sharing
362
+
363
+ **Files:**
364
+ - Create: `src/lib/cortex/lobes/shares.ts`
365
+ - Create: `tests/lib/cortex/lobes/shares.test.ts`
366
+
367
+ - [ ] **Step 1: Write failing tests**
368
+
369
+ ```typescript
370
+ // tests/lib/cortex/lobes/shares.test.ts
371
+ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
372
+ import fs from 'fs';
373
+ import path from 'path';
374
+ import os from 'os';
375
+ import Database from 'better-sqlite3';
376
+ import { LobeShareStore } from '@/lib/cortex/lobes/shares';
377
+
378
+ describe('LobeShareStore', () => {
379
+ let tmpDir: string;
380
+ let store: LobeShareStore;
381
+
382
+ beforeEach(() => {
383
+ tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'lobe-shares-'));
384
+ const db = new Database(path.join(tmpDir, 'test.db'));
385
+ store = new LobeShareStore(db);
386
+ });
387
+
388
+ afterEach(() => {
389
+ fs.rmSync(tmpDir, { recursive: true, force: true });
390
+ });
391
+
392
+ it('creates a share', () => {
393
+ const share = store.share({
394
+ ownerUserId: 'person-alice',
395
+ ownerWorkspaceId: 1,
396
+ ownerLobeName: 'Auth Service',
397
+ sharedWithUserId: 'person-bob',
398
+ });
399
+ expect(share.id).toBeDefined();
400
+ expect(share.accepted).toBe(false);
401
+ });
402
+
403
+ it('accepts a share', () => {
404
+ const share = store.share({
405
+ ownerUserId: 'person-alice',
406
+ ownerWorkspaceId: 1,
407
+ ownerLobeName: 'Auth Service',
408
+ sharedWithUserId: 'person-bob',
409
+ });
410
+ store.accept(share.id);
411
+ const updated = store.getShare(share.id);
412
+ expect(updated!.accepted).toBe(true);
413
+ });
414
+
415
+ it('lists incoming shares for a user', () => {
416
+ store.share({ ownerUserId: 'person-alice', ownerWorkspaceId: 1, ownerLobeName: 'WS1', sharedWithUserId: 'person-bob' });
417
+ store.share({ ownerUserId: 'person-charlie', ownerWorkspaceId: 2, ownerLobeName: 'WS2', sharedWithUserId: 'person-bob' });
418
+ const incoming = store.listIncoming('person-bob');
419
+ expect(incoming).toHaveLength(2);
420
+ });
421
+
422
+ it('lists outgoing shares for a user', () => {
423
+ store.share({ ownerUserId: 'person-alice', ownerWorkspaceId: 1, ownerLobeName: 'WS1', sharedWithUserId: 'person-bob' });
424
+ const outgoing = store.listOutgoing('person-alice');
425
+ expect(outgoing).toHaveLength(1);
426
+ });
427
+
428
+ it('revokes a share', () => {
429
+ const share = store.share({ ownerUserId: 'person-alice', ownerWorkspaceId: 1, ownerLobeName: 'WS1', sharedWithUserId: 'person-bob' });
430
+ store.revoke(share.id);
431
+ expect(store.getShare(share.id)).toBeNull();
432
+ });
433
+
434
+ it('prevents duplicate shares', () => {
435
+ store.share({ ownerUserId: 'person-alice', ownerWorkspaceId: 1, ownerLobeName: 'WS1', sharedWithUserId: 'person-bob' });
436
+ store.share({ ownerUserId: 'person-alice', ownerWorkspaceId: 1, ownerLobeName: 'WS1', sharedWithUserId: 'person-bob' });
437
+ const incoming = store.listIncoming('person-bob');
438
+ expect(incoming).toHaveLength(1);
439
+ });
440
+ });
441
+ ```
442
+
443
+ - [ ] **Step 2: Implement share store**
444
+
445
+ ```typescript
446
+ // src/lib/cortex/lobes/shares.ts
447
+ import type Database from 'better-sqlite3';
448
+
449
+ export interface LobeShare {
450
+ id: string;
451
+ ownerUserId: string;
452
+ ownerWorkspaceId: number;
453
+ ownerLobeName: string;
454
+ sharedWithUserId: string;
455
+ accepted: boolean;
456
+ created: string;
457
+ }
458
+
459
+ interface ShareInput {
460
+ ownerUserId: string;
461
+ ownerWorkspaceId: number;
462
+ ownerLobeName: string;
463
+ sharedWithUserId: string;
464
+ }
465
+
466
+ export class LobeShareStore {
467
+ private db: InstanceType<typeof Database>;
468
+
469
+ constructor(db: InstanceType<typeof Database>) {
470
+ this.db = db;
471
+ this.db.exec(`
472
+ CREATE TABLE IF NOT EXISTS lobe_shares (
473
+ id TEXT PRIMARY KEY,
474
+ owner_user_id TEXT NOT NULL,
475
+ owner_workspace_id INTEGER NOT NULL,
476
+ owner_lobe_name TEXT NOT NULL,
477
+ shared_with_user_id TEXT NOT NULL,
478
+ accepted INTEGER DEFAULT 0,
479
+ created TEXT NOT NULL,
480
+ UNIQUE(owner_user_id, owner_workspace_id, shared_with_user_id)
481
+ );
482
+ CREATE INDEX IF NOT EXISTS idx_lobe_shares_recipient ON lobe_shares(shared_with_user_id);
483
+ CREATE INDEX IF NOT EXISTS idx_lobe_shares_owner ON lobe_shares(owner_user_id);
484
+ `);
485
+ }
486
+
487
+ share(input: ShareInput): LobeShare {
488
+ const id = crypto.randomUUID();
489
+ const now = new Date().toISOString();
490
+
491
+ this.db.prepare(`
492
+ INSERT INTO lobe_shares (id, owner_user_id, owner_workspace_id, owner_lobe_name, shared_with_user_id, created)
493
+ VALUES (?, ?, ?, ?, ?, ?)
494
+ ON CONFLICT(owner_user_id, owner_workspace_id, shared_with_user_id) DO NOTHING
495
+ `).run(id, input.ownerUserId, input.ownerWorkspaceId, input.ownerLobeName, input.sharedWithUserId, now);
496
+
497
+ // Return existing if duplicate
498
+ const existing = this.db.prepare(
499
+ 'SELECT * FROM lobe_shares WHERE owner_user_id = ? AND owner_workspace_id = ? AND shared_with_user_id = ?'
500
+ ).get(input.ownerUserId, input.ownerWorkspaceId, input.sharedWithUserId) as any;
501
+
502
+ return this.rowToShare(existing);
503
+ }
504
+
505
+ accept(id: string): void {
506
+ this.db.prepare('UPDATE lobe_shares SET accepted = 1 WHERE id = ?').run(id);
507
+ }
508
+
509
+ revoke(id: string): void {
510
+ this.db.prepare('DELETE FROM lobe_shares WHERE id = ?').run(id);
511
+ }
512
+
513
+ getShare(id: string): LobeShare | null {
514
+ const row = this.db.prepare('SELECT * FROM lobe_shares WHERE id = ?').get(id) as any;
515
+ return row ? this.rowToShare(row) : null;
516
+ }
517
+
518
+ listIncoming(userId: string): LobeShare[] {
519
+ const rows = this.db.prepare(
520
+ 'SELECT * FROM lobe_shares WHERE shared_with_user_id = ? ORDER BY created DESC'
521
+ ).all(userId) as any[];
522
+ return rows.map(r => this.rowToShare(r));
523
+ }
524
+
525
+ listOutgoing(userId: string): LobeShare[] {
526
+ const rows = this.db.prepare(
527
+ 'SELECT * FROM lobe_shares WHERE owner_user_id = ? ORDER BY created DESC'
528
+ ).all(userId) as any[];
529
+ return rows.map(r => this.rowToShare(r));
530
+ }
531
+
532
+ listAcceptedForUser(userId: string): LobeShare[] {
533
+ const rows = this.db.prepare(
534
+ 'SELECT * FROM lobe_shares WHERE shared_with_user_id = ? AND accepted = 1'
535
+ ).all(userId) as any[];
536
+ return rows.map(r => this.rowToShare(r));
537
+ }
538
+
539
+ private rowToShare(row: any): LobeShare {
540
+ return {
541
+ id: row.id,
542
+ ownerUserId: row.owner_user_id,
543
+ ownerWorkspaceId: row.owner_workspace_id,
544
+ ownerLobeName: row.owner_lobe_name,
545
+ sharedWithUserId: row.shared_with_user_id,
546
+ accepted: row.accepted === 1,
547
+ created: row.created,
548
+ };
549
+ }
550
+ }
551
+ ```
552
+
553
+ - [ ] **Step 3: Run tests, commit**
554
+
555
+ Run: `npx vitest run tests/lib/cortex/lobes/shares.test.ts`
556
+
557
+ ```bash
558
+ git commit -m "feat(cortex): add cross-user lobe sharing with handshake"
559
+ ```
560
+
561
+ ---
562
+
563
+ ### Task 5: Integrate lobe resolver with Context Assembly Engine
564
+
565
+ **Files:**
566
+ - Modify: `src/lib/cortex/retrieval/context-engine.ts`
567
+
568
+ - [ ] **Step 1: Read context-engine.ts**
569
+
570
+ Read the file. Find `computeSourceWeights()` — the private method that returns hardcoded layer definitions.
571
+
572
+ - [ ] **Step 2: Add optional lobe-aware scope computation**
573
+
574
+ Add to `ContextEngineDeps`:
575
+ ```typescript
576
+ resolvedLobes?: ResolvedLobe[]; // pre-computed accessible lobes for this workspace
577
+ ```
578
+
579
+ Import:
580
+ ```typescript
581
+ import type { ResolvedLobe } from '../lobes/resolver';
582
+ ```
583
+
584
+ Modify `computeSourceWeights()` to use resolved lobes when available:
585
+
586
+ ```typescript
587
+ private computeSourceWeights(
588
+ intent: IntentResult,
589
+ workspaceId: number | null,
590
+ ): SourceConfig[] {
591
+ // If lobes are provided, use them instead of hardcoded layers
592
+ if (this.deps.resolvedLobes && this.deps.resolvedLobes.length > 0) {
593
+ return this.deps.resolvedLobes.map(lobe => {
594
+ const graphProximity = lobe.baseWeight; // lobes already have base weights
595
+
596
+ const weight = computeScopeWeight({
597
+ graphProximity,
598
+ scopeLevel: lobe.type === 'personal' ? 'personal'
599
+ : lobe.type === 'team' || lobe.type === 'department' ? 'team'
600
+ : lobe.type === 'organization' ? 'organization'
601
+ : 'team',
602
+ intentBiases: intent.biases,
603
+ authorityFactor: 1.0,
604
+ });
605
+
606
+ return {
607
+ layerKey: lobe.layerKey,
608
+ weight,
609
+ limit: Math.max(3, Math.round(weight * 10)),
610
+ };
611
+ }).sort((a, b) => b.weight - a.weight);
612
+ }
613
+
614
+ // Fallback: hardcoded layers (backward compat when no lobe config)
615
+ const layerDefs = [
616
+ // ... existing code unchanged ...
617
+ ];
618
+ // ... rest of existing method unchanged ...
619
+ }
620
+ ```
621
+
622
+ - [ ] **Step 3: Run existing context-engine tests to verify no regressions**
623
+
624
+ Run: `npx vitest run tests/lib/cortex/retrieval/context-engine.test.ts`
625
+
626
+ - [ ] **Step 4: Commit**
627
+
628
+ ```bash
629
+ git add src/lib/cortex/retrieval/context-engine.ts
630
+ git commit -m "feat(cortex): integrate lobe resolver into context assembly engine"
631
+ ```
632
+
633
+ ---
634
+
635
+ ## Chunk 3: API Routes and UI
636
+
637
+ ### Task 6: Lobe API endpoints
638
+
639
+ **Files:**
640
+ - Create: `src/app/api/cortex/lobes/route.ts`
641
+ - Create: `src/app/api/cortex/lobes/[id]/route.ts`
642
+ - Create: `src/app/api/cortex/lobes/share/route.ts`
643
+
644
+ - [ ] **Step 1: Create main lobes endpoint**
645
+
646
+ ```typescript
647
+ // src/app/api/cortex/lobes/route.ts
648
+ import { NextResponse } from 'next/server';
649
+ import type { NextRequest } from 'next/server';
650
+ import { getAuthUser, withUser } from '@/lib/auth';
651
+ import { getDb } from '@/lib/db';
652
+ import { parseLobeConfig } from '@/lib/cortex/lobes/config';
653
+
654
+ export async function GET(request: NextRequest) {
655
+ const user = getAuthUser(request);
656
+ return withUser(user, async () => {
657
+ const db = getDb();
658
+ const workspaces = db.prepare(
659
+ 'SELECT id, name, color, lobe_config FROM workspaces ORDER BY name'
660
+ ).all() as any[];
661
+
662
+ const lobes = workspaces.map(ws => ({
663
+ workspaceId: ws.id,
664
+ name: ws.name,
665
+ color: ws.color,
666
+ config: parseLobeConfig(ws.lobe_config),
667
+ }));
668
+
669
+ return NextResponse.json({ lobes });
670
+ });
671
+ }
672
+ ```
673
+
674
+ - [ ] **Step 2: Create single workspace lobe config endpoint**
675
+
676
+ ```typescript
677
+ // src/app/api/cortex/lobes/[id]/route.ts
678
+ import { NextResponse } from 'next/server';
679
+ import type { NextRequest } from 'next/server';
680
+ import { getAuthUser, withUser } from '@/lib/auth';
681
+ import { getDb } from '@/lib/db';
682
+ import { parseLobeConfig, serializeLobeConfig } from '@/lib/cortex/lobes/config';
683
+ import type { LobeConfig } from '@/lib/cortex/lobes/config';
684
+
685
+ export async function GET(
686
+ request: NextRequest,
687
+ { params }: { params: Promise<{ id: string }> },
688
+ ) {
689
+ const { id } = await params;
690
+ const user = getAuthUser(request);
691
+ return withUser(user, async () => {
692
+ const db = getDb();
693
+ const ws = db.prepare('SELECT id, name, lobe_config FROM workspaces WHERE id = ?').get(Number(id)) as any;
694
+ if (!ws) return NextResponse.json({ error: 'Workspace not found' }, { status: 404 });
695
+ return NextResponse.json({ workspaceId: ws.id, name: ws.name, config: parseLobeConfig(ws.lobe_config) });
696
+ });
697
+ }
698
+
699
+ export async function PUT(
700
+ request: NextRequest,
701
+ { params }: { params: Promise<{ id: string }> },
702
+ ) {
703
+ const { id } = await params;
704
+ const user = getAuthUser(request);
705
+ return withUser(user, async () => {
706
+ const db = getDb();
707
+ const body = await request.json() as Partial<LobeConfig>;
708
+
709
+ const ws = db.prepare('SELECT lobe_config FROM workspaces WHERE id = ?').get(Number(id)) as any;
710
+ if (!ws) return NextResponse.json({ error: 'Workspace not found' }, { status: 404 });
711
+
712
+ const current = parseLobeConfig(ws.lobe_config);
713
+ const updated: LobeConfig = {
714
+ isPrivate: body.isPrivate ?? current.isPrivate,
715
+ excludedFrom: body.excludedFrom ?? current.excludedFrom,
716
+ subscriptions: body.subscriptions ?? current.subscriptions,
717
+ tags: body.tags ?? current.tags,
718
+ };
719
+
720
+ db.prepare('UPDATE workspaces SET lobe_config = ? WHERE id = ?').run(serializeLobeConfig(updated), Number(id));
721
+ return NextResponse.json({ config: updated });
722
+ });
723
+ }
724
+ ```
725
+
726
+ - [ ] **Step 3: Create share endpoint**
727
+
728
+ ```typescript
729
+ // src/app/api/cortex/lobes/share/route.ts
730
+ import { NextResponse } from 'next/server';
731
+ import type { NextRequest } from 'next/server';
732
+ import { getAuthUser, withUser } from '@/lib/auth';
733
+ import { getCortex, isCortexAvailable } from '@/lib/cortex';
734
+ import { LobeShareStore } from '@/lib/cortex/lobes/shares';
735
+ import { slugify } from '@/lib/cortex/graph/types';
736
+
737
+ export async function GET(request: NextRequest) {
738
+ const user = getAuthUser(request);
739
+ return withUser(user, async () => {
740
+ if (!isCortexAvailable()) return NextResponse.json({ incoming: [], outgoing: [] });
741
+ const cortex = await getCortex();
742
+ if (!cortex) return NextResponse.json({ incoming: [], outgoing: [] });
743
+
744
+ const shareStore = new LobeShareStore(cortex.graph['db']);
745
+ const userId = `person-${slugify(user)}`;
746
+
747
+ return NextResponse.json({
748
+ incoming: shareStore.listIncoming(userId),
749
+ outgoing: shareStore.listOutgoing(userId),
750
+ });
751
+ });
752
+ }
753
+
754
+ export async function POST(request: NextRequest) {
755
+ const user = getAuthUser(request);
756
+ return withUser(user, async () => {
757
+ if (!isCortexAvailable()) return NextResponse.json({ error: 'Cortex unavailable' }, { status: 403 });
758
+ const cortex = await getCortex();
759
+ if (!cortex) return NextResponse.json({ error: 'Cortex unavailable' }, { status: 500 });
760
+
761
+ const body = await request.json();
762
+ const { action, shareId, workspaceId, lobeName, sharedWithUserId } = body;
763
+ const shareStore = new LobeShareStore(cortex.graph['db']);
764
+ const userId = `person-${slugify(user)}`;
765
+
766
+ if (action === 'share') {
767
+ const share = shareStore.share({
768
+ ownerUserId: userId,
769
+ ownerWorkspaceId: workspaceId,
770
+ ownerLobeName: lobeName,
771
+ sharedWithUserId,
772
+ });
773
+ return NextResponse.json({ share }, { status: 201 });
774
+ }
775
+
776
+ if (action === 'accept') {
777
+ shareStore.accept(shareId);
778
+ return NextResponse.json({ accepted: true });
779
+ }
780
+
781
+ if (action === 'revoke' || action === 'decline') {
782
+ shareStore.revoke(shareId);
783
+ return NextResponse.json({ revoked: true });
784
+ }
785
+
786
+ return NextResponse.json({ error: 'Invalid action' }, { status: 400 });
787
+ });
788
+ }
789
+ ```
790
+
791
+ - [ ] **Step 4: Commit**
792
+
793
+ ```bash
794
+ git add src/app/api/cortex/lobes/
795
+ git commit -m "feat(cortex): add lobe API endpoints for config and sharing"
796
+ ```
797
+
798
+ ---
799
+
800
+ ### Task 7: Lobe settings UI component
801
+
802
+ **Files:**
803
+ - Create: `src/components/cortex/lobe-settings.tsx`
804
+
805
+ - [ ] **Step 1: Create the component**
806
+
807
+ A settings panel showing the workspace's knowledge sources with toggles, tags, and subscription management.
808
+
809
+ ```typescript
810
+ 'use client';
811
+
812
+ import { useState, useEffect, useCallback } from 'react';
813
+ import { Shield, ShieldOff, Tag, Plus, X, Users } from 'lucide-react';
814
+ import { api } from '@/lib/api';
815
+ import type { LobeConfig, LobeSubscription } from '@/lib/cortex/lobes/config';
816
+
817
+ interface LobeSettingsProps {
818
+ workspaceId: number;
819
+ workspaceName: string;
820
+ }
821
+
822
+ export function LobeSettings({ workspaceId, workspaceName }: LobeSettingsProps) {
823
+ const [config, setConfig] = useState<LobeConfig | null>(null);
824
+ const [allLobes, setAllLobes] = useState<any[]>([]);
825
+ const [newTag, setNewTag] = useState('');
826
+ const [saving, setSaving] = useState(false);
827
+
828
+ const fetchConfig = useCallback(async () => {
829
+ const res = await fetch(api(`/api/cortex/lobes/${workspaceId}`));
830
+ if (res.ok) {
831
+ const data = await res.json();
832
+ setConfig(data.config);
833
+ }
834
+ }, [workspaceId]);
835
+
836
+ const fetchAllLobes = useCallback(async () => {
837
+ const res = await fetch(api('/api/cortex/lobes'));
838
+ if (res.ok) {
839
+ const data = await res.json();
840
+ setAllLobes(data.lobes || []);
841
+ }
842
+ }, []);
843
+
844
+ useEffect(() => { fetchConfig(); fetchAllLobes(); }, [fetchConfig, fetchAllLobes]);
845
+
846
+ const save = async (updates: Partial<LobeConfig>) => {
847
+ setSaving(true);
848
+ await fetch(api(`/api/cortex/lobes/${workspaceId}`), {
849
+ method: 'PUT',
850
+ headers: { 'Content-Type': 'application/json' },
851
+ body: JSON.stringify(updates),
852
+ });
853
+ await fetchConfig();
854
+ setSaving(false);
855
+ };
856
+
857
+ if (!config) return <div className="text-gray-500 text-sm p-4">Loading...</div>;
858
+
859
+ const activeSourceCount = allLobes.filter(l =>
860
+ l.workspaceId !== workspaceId && !l.config.isPrivate && !l.config.excludedFrom?.includes(workspaceId)
861
+ ).length + 2; // +2 for personal + team
862
+
863
+ return (
864
+ <div className="space-y-6">
865
+ <div>
866
+ <h3 className="text-sm font-medium text-gray-200 mb-1">Knowledge Lobes</h3>
867
+ <p className="text-xs text-gray-500">
868
+ This workspace draws from {activeSourceCount} lobes.
869
+ {config.isPrivate && ' This lobe is private — other workspaces cannot access its knowledge.'}
870
+ </p>
871
+ </div>
872
+
873
+ {/* Privacy toggle */}
874
+ <div className="flex items-center justify-between py-3 border-t border-white/5">
875
+ <div className="flex items-center gap-2">
876
+ {config.isPrivate ? <ShieldOff className="w-4 h-4 text-red-400" /> : <Shield className="w-4 h-4 text-green-400" />}
877
+ <div>
878
+ <div className="text-sm text-gray-200">Private lobe</div>
879
+ <div className="text-[10px] text-gray-500">Other workspaces cannot access this knowledge</div>
880
+ </div>
881
+ </div>
882
+ <button
883
+ onClick={() => save({ isPrivate: !config.isPrivate })}
884
+ disabled={saving}
885
+ className={`px-3 py-1 text-xs rounded ${config.isPrivate ? 'bg-red-500/20 text-red-400' : 'bg-white/5 text-gray-400'}`}
886
+ >
887
+ {config.isPrivate ? 'Private' : 'Open'}
888
+ </button>
889
+ </div>
890
+
891
+ {/* Tags */}
892
+ <div className="border-t border-white/5 pt-3">
893
+ <div className="flex items-center gap-2 mb-2">
894
+ <Tag className="w-3.5 h-3.5 text-gray-500" />
895
+ <span className="text-xs text-gray-400">Tags</span>
896
+ </div>
897
+ <div className="flex flex-wrap gap-1.5 mb-2">
898
+ {config.tags.map(tag => (
899
+ <span key={tag} className="flex items-center gap-1 text-[11px] bg-purple-500/10 text-purple-400 px-2 py-0.5 rounded">
900
+ {tag}
901
+ <button onClick={() => save({ tags: config.tags.filter(t => t !== tag) })} className="hover:text-white">
902
+ <X className="w-2.5 h-2.5" />
903
+ </button>
904
+ </span>
905
+ ))}
906
+ </div>
907
+ <div className="flex gap-1.5">
908
+ <input
909
+ value={newTag}
910
+ onChange={e => setNewTag(e.target.value)}
911
+ onKeyDown={e => {
912
+ if (e.key === 'Enter' && newTag.trim()) {
913
+ save({ tags: [...config.tags, newTag.trim()] });
914
+ setNewTag('');
915
+ }
916
+ }}
917
+ placeholder="Add tag..."
918
+ className="flex-1 px-2 py-1 text-xs bg-white/5 border border-white/10 rounded text-gray-300 focus:outline-none focus:border-purple-500/50"
919
+ />
920
+ </div>
921
+ </div>
922
+
923
+ {/* Subscriptions */}
924
+ <div className="border-t border-white/5 pt-3">
925
+ <div className="flex items-center gap-2 mb-2">
926
+ <Plus className="w-3.5 h-3.5 text-gray-500" />
927
+ <span className="text-xs text-gray-400">Additional sources</span>
928
+ </div>
929
+ {config.subscriptions.length === 0 ? (
930
+ <p className="text-[11px] text-gray-600">No additional subscriptions. Using defaults.</p>
931
+ ) : (
932
+ <div className="space-y-1">
933
+ {config.subscriptions.map((sub, i) => (
934
+ <div key={i} className="flex items-center justify-between text-xs bg-white/[0.02] rounded px-2 py-1.5">
935
+ <span className="text-gray-300">{sub.label} <span className="text-gray-600">({sub.type})</span></span>
936
+ <button
937
+ onClick={() => save({ subscriptions: config.subscriptions.filter((_, j) => j !== i) })}
938
+ className="text-gray-600 hover:text-red-400"
939
+ >
940
+ <X className="w-3 h-3" />
941
+ </button>
942
+ </div>
943
+ ))}
944
+ </div>
945
+ )}
946
+ </div>
947
+
948
+ {/* Exclusions */}
949
+ <div className="border-t border-white/5 pt-3">
950
+ <div className="flex items-center gap-2 mb-2">
951
+ <Users className="w-3.5 h-3.5 text-gray-500" />
952
+ <span className="text-xs text-gray-400">Excluded workspaces</span>
953
+ </div>
954
+ {config.excludedFrom.length === 0 ? (
955
+ <p className="text-[11px] text-gray-600">No exclusions. All workspaces can access this lobe.</p>
956
+ ) : (
957
+ <div className="space-y-1">
958
+ {config.excludedFrom.map(wsId => {
959
+ const ws = allLobes.find(l => l.workspaceId === wsId);
960
+ return (
961
+ <div key={wsId} className="flex items-center justify-between text-xs bg-white/[0.02] rounded px-2 py-1.5">
962
+ <span className="text-gray-300">{ws?.name || `Workspace ${wsId}`}</span>
963
+ <button
964
+ onClick={() => save({ excludedFrom: config.excludedFrom.filter(id => id !== wsId) })}
965
+ className="text-gray-600 hover:text-red-400"
966
+ >
967
+ <X className="w-3 h-3" />
968
+ </button>
969
+ </div>
970
+ );
971
+ })}
972
+ </div>
973
+ )}
974
+ </div>
975
+ </div>
976
+ );
977
+ }
978
+ ```
979
+
980
+ - [ ] **Step 2: Commit**
981
+
982
+ ```bash
983
+ git add src/components/cortex/lobe-settings.tsx
984
+ git commit -m "feat(cortex): add lobe settings UI component"
985
+ ```
986
+
987
+ ---
988
+
989
+ ### Task 8: Barrel export and wiring
990
+
991
+ **Files:**
992
+ - Create: `src/lib/cortex/lobes/index.ts`
993
+ - Modify: `src/app/(desktop)/cortex/page.tsx` — add Lobes section to Settings tab
994
+
995
+ - [ ] **Step 1: Create barrel export**
996
+
997
+ ```typescript
998
+ // src/lib/cortex/lobes/index.ts
999
+ export { parseLobeConfig, serializeLobeConfig, DEFAULT_LOBE_CONFIG } from './config';
1000
+ export type { LobeConfig, LobeSubscription } from './config';
1001
+ export { resolveLobes } from './resolver';
1002
+ export type { ResolvedLobe } from './resolver';
1003
+ export { LobeShareStore } from './shares';
1004
+ export type { LobeShare } from './shares';
1005
+ ```
1006
+
1007
+ - [ ] **Step 2: Add LobeSettings to the Cortex page Settings tab**
1008
+
1009
+ Read `src/app/(desktop)/cortex/page.tsx`. Find the Settings tab rendering. Currently it shows `<CortexSettings />`. Add `<LobeSettings>` below it, but only when a workspace is active. The workspace ID needs to come from somewhere — check how the terminal page gets `activeWorkspace`:
1010
+
1011
+ Read `src/app/(desktop)/terminal/page.tsx` to see how `activeWorkspace` is loaded. The pattern is likely a fetch to `/api/workspaces` with `is_active=1`.
1012
+
1013
+ For the Cortex page, add a simple workspace selector or use the active workspace. The simplest approach: fetch the active workspace and pass its ID to LobeSettings.
1014
+
1015
+ ```typescript
1016
+ // In CortexPage, add state:
1017
+ const [activeWorkspace, setActiveWorkspace] = useState<any>(null);
1018
+
1019
+ // Fetch active workspace:
1020
+ useEffect(() => {
1021
+ fetch(api('/api/workspaces'))
1022
+ .then(r => r.json())
1023
+ .then(data => {
1024
+ const active = (data.workspaces || []).find((w: any) => w.isActive);
1025
+ setActiveWorkspace(active);
1026
+ })
1027
+ .catch(() => {});
1028
+ }, []);
1029
+
1030
+ // In Settings tab rendering, add LobeSettings:
1031
+ {tab === 'settings' && (
1032
+ <div className="p-6 max-w-2xl space-y-8">
1033
+ <CortexSettings />
1034
+ {activeWorkspace && (
1035
+ <LobeSettings
1036
+ workspaceId={activeWorkspace.id}
1037
+ workspaceName={activeWorkspace.name}
1038
+ />
1039
+ )}
1040
+ </div>
1041
+ )}
1042
+ ```
1043
+
1044
+ Import: `import { LobeSettings } from '@/components/cortex/lobe-settings';`
1045
+
1046
+ - [ ] **Step 3: Run full test suite**
1047
+
1048
+ Run: `npx vitest run tests/lib/cortex/`
1049
+
1050
+ - [ ] **Step 4: Commit**
1051
+
1052
+ ```bash
1053
+ git add src/lib/cortex/lobes/index.ts src/app/(desktop)/cortex/page.tsx
1054
+ git commit -m "feat(cortex): wire lobe settings into Cortex page"
1055
+ ```
1056
+
1057
+ ---
1058
+
1059
+ ## Summary
1060
+
1061
+ | Task | Component | Tests | Status |
1062
+ |------|-----------|-------|--------|
1063
+ | 1 | Lobe config types | — | |
1064
+ | 2 | DB migration (lobe_config column) | — | |
1065
+ | 3 | Lobe resolver | 8 | |
1066
+ | 4 | Cross-user sharing | 6 | |
1067
+ | 5 | Context Engine integration | regression | |
1068
+ | 6 | API endpoints (3 files) | — | |
1069
+ | 7 | Lobe settings UI component | — | |
1070
+ | 8 | Barrel export + page wiring | regression | |
1071
+
1072
+ **Total: 8 tasks, ~14 new tests, 3 chunks**
1073
+
1074
+ **Key design decisions:**
1075
+ - Lobe config stored as JSON column on workspaces table (simple, always loaded with workspace)
1076
+ - Resolver computes accessible lobes at query time from workspace list + config
1077
+ - Cross-user shares use a separate SQLite table in the entity graph DB with two-step handshake
1078
+ - Context Engine uses resolved lobes when provided, falls back to hardcoded layers for backward compat
1079
+ - Privacy is safe-by-default: private workspaces are excluded, cross-user is closed by default
1080
+ - Base weights: own=1.0, personal=0.9, sibling workspaces=0.6, team=0.5, subscribed=0.4