@theihtisham/agent-shadow-brain 7.1.3 → 7.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (254) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/README.md +125 -90
  3. package/dist/adapters/index.d.ts +1 -1
  4. package/dist/adapters/index.d.ts.map +1 -1
  5. package/dist/adapters/index.js.map +1 -1
  6. package/dist/brain/activity-journal.d.ts.map +1 -1
  7. package/dist/brain/activity-journal.js +2 -1
  8. package/dist/brain/activity-journal.js.map +1 -1
  9. package/dist/brain/auto-update.js +1 -1
  10. package/dist/brain/brain-portability.js +1 -1
  11. package/dist/brain/client-config.d.ts +19 -0
  12. package/dist/brain/client-config.d.ts.map +1 -0
  13. package/dist/brain/client-config.js +40 -0
  14. package/dist/brain/client-config.js.map +1 -0
  15. package/dist/brain/client-integrations.d.ts +2 -0
  16. package/dist/brain/client-integrations.d.ts.map +1 -1
  17. package/dist/brain/client-integrations.js +8 -27
  18. package/dist/brain/client-integrations.js.map +1 -1
  19. package/dist/brain/context-assembly.d.ts +10 -2
  20. package/dist/brain/context-assembly.d.ts.map +1 -1
  21. package/dist/brain/context-assembly.js +238 -14
  22. package/dist/brain/context-assembly.js.map +1 -1
  23. package/dist/brain/context-request.d.ts +4 -0
  24. package/dist/brain/context-request.d.ts.map +1 -0
  25. package/dist/brain/context-request.js +22 -0
  26. package/dist/brain/context-request.js.map +1 -0
  27. package/dist/brain/context-retrieval.d.ts +88 -0
  28. package/dist/brain/context-retrieval.d.ts.map +1 -0
  29. package/dist/brain/context-retrieval.js +300 -0
  30. package/dist/brain/context-retrieval.js.map +1 -0
  31. package/dist/brain/feature-settings.d.ts.map +1 -1
  32. package/dist/brain/feature-settings.js +2 -1
  33. package/dist/brain/feature-settings.js.map +1 -1
  34. package/dist/brain/file-identity.d.ts +4 -0
  35. package/dist/brain/file-identity.d.ts.map +1 -0
  36. package/dist/brain/file-identity.js +27 -0
  37. package/dist/brain/file-identity.js.map +1 -0
  38. package/dist/brain/global-brain.d.ts +11 -1
  39. package/dist/brain/global-brain.d.ts.map +1 -1
  40. package/dist/brain/global-brain.js +58 -6
  41. package/dist/brain/global-brain.js.map +1 -1
  42. package/dist/brain/mcp-server.d.ts +9 -0
  43. package/dist/brain/mcp-server.d.ts.map +1 -1
  44. package/dist/brain/mcp-server.js +237 -53
  45. package/dist/brain/mcp-server.js.map +1 -1
  46. package/dist/brain/mcp-stdio.d.ts.map +1 -1
  47. package/dist/brain/mcp-stdio.js +10 -4
  48. package/dist/brain/mcp-stdio.js.map +1 -1
  49. package/dist/brain/memory-annotations.d.ts.map +1 -1
  50. package/dist/brain/memory-annotations.js +2 -1
  51. package/dist/brain/memory-annotations.js.map +1 -1
  52. package/dist/brain/memory-inspection.d.ts +72 -0
  53. package/dist/brain/memory-inspection.d.ts.map +1 -0
  54. package/dist/brain/memory-inspection.js +131 -0
  55. package/dist/brain/memory-inspection.js.map +1 -0
  56. package/dist/brain/memory-mcp-content.d.ts +44 -0
  57. package/dist/brain/memory-mcp-content.d.ts.map +1 -0
  58. package/dist/brain/memory-mcp-content.js +91 -0
  59. package/dist/brain/memory-mcp-content.js.map +1 -0
  60. package/dist/brain/orchestrator.js +1 -1
  61. package/dist/brain/project-feedback.d.ts +63 -0
  62. package/dist/brain/project-feedback.d.ts.map +1 -0
  63. package/dist/brain/project-feedback.js +289 -0
  64. package/dist/brain/project-feedback.js.map +1 -0
  65. package/dist/brain/project-hub-contract.d.ts +326 -0
  66. package/dist/brain/project-hub-contract.d.ts.map +1 -0
  67. package/dist/brain/project-hub-contract.js +56 -0
  68. package/dist/brain/project-hub-contract.js.map +1 -0
  69. package/dist/brain/project-hub-mcp.d.ts +168 -0
  70. package/dist/brain/project-hub-mcp.d.ts.map +1 -0
  71. package/dist/brain/project-hub-mcp.js +41 -0
  72. package/dist/brain/project-hub-mcp.js.map +1 -0
  73. package/dist/brain/project-hub-pdf-worker.d.ts +13 -0
  74. package/dist/brain/project-hub-pdf-worker.d.ts.map +1 -0
  75. package/dist/brain/project-hub-pdf-worker.js +84 -0
  76. package/dist/brain/project-hub-pdf-worker.js.map +1 -0
  77. package/dist/brain/project-hub-store.d.ts +38 -0
  78. package/dist/brain/project-hub-store.d.ts.map +1 -0
  79. package/dist/brain/project-hub-store.js +189 -0
  80. package/dist/brain/project-hub-store.js.map +1 -0
  81. package/dist/brain/project-hub.d.ts +127 -0
  82. package/dist/brain/project-hub.d.ts.map +1 -0
  83. package/dist/brain/project-hub.js +381 -0
  84. package/dist/brain/project-hub.js.map +1 -0
  85. package/dist/brain/project-kit-inspection.d.ts +28 -0
  86. package/dist/brain/project-kit-inspection.d.ts.map +1 -0
  87. package/dist/brain/project-kit-inspection.js +200 -0
  88. package/dist/brain/project-kit-inspection.js.map +1 -0
  89. package/dist/brain/project-kit-paths.d.ts +13 -0
  90. package/dist/brain/project-kit-paths.d.ts.map +1 -0
  91. package/dist/brain/project-kit-paths.js +141 -0
  92. package/dist/brain/project-kit-paths.js.map +1 -0
  93. package/dist/brain/project-kit.d.ts +41 -0
  94. package/dist/brain/project-kit.d.ts.map +1 -0
  95. package/dist/brain/project-kit.js +255 -0
  96. package/dist/brain/project-kit.js.map +1 -0
  97. package/dist/brain/project-memory.d.ts +38 -0
  98. package/dist/brain/project-memory.d.ts.map +1 -0
  99. package/dist/brain/project-memory.js +76 -0
  100. package/dist/brain/project-memory.js.map +1 -0
  101. package/dist/brain/subconscious.js +2 -2
  102. package/dist/brain/subconscious.js.map +1 -1
  103. package/dist/brain/workflow-skills.d.ts.map +1 -1
  104. package/dist/brain/workflow-skills.js +3 -2
  105. package/dist/brain/workflow-skills.js.map +1 -1
  106. package/dist/cli.js +22 -3
  107. package/dist/cli.js.map +1 -1
  108. package/dist/context-cli.d.ts.map +1 -1
  109. package/dist/context-cli.js +39 -5
  110. package/dist/context-cli.js.map +1 -1
  111. package/dist/dashboard/brain-hub-actions.d.ts +2 -0
  112. package/dist/dashboard/brain-hub-actions.d.ts.map +1 -0
  113. package/dist/dashboard/brain-hub-actions.js +137 -0
  114. package/dist/dashboard/brain-hub-actions.js.map +1 -0
  115. package/dist/dashboard/brain-hub-client.d.ts +2 -0
  116. package/dist/dashboard/brain-hub-client.d.ts.map +1 -0
  117. package/dist/dashboard/brain-hub-client.js +335 -0
  118. package/dist/dashboard/brain-hub-client.js.map +1 -0
  119. package/dist/dashboard/brain-hub-graph.d.ts +2 -0
  120. package/dist/dashboard/brain-hub-graph.d.ts.map +1 -0
  121. package/dist/dashboard/brain-hub-graph.js +176 -0
  122. package/dist/dashboard/brain-hub-graph.js.map +1 -0
  123. package/dist/dashboard/brain-hub-html.d.ts +2 -0
  124. package/dist/dashboard/brain-hub-html.d.ts.map +1 -0
  125. package/dist/dashboard/brain-hub-html.js +30 -0
  126. package/dist/dashboard/brain-hub-html.js.map +1 -0
  127. package/dist/dashboard/brain-hub-project-views.d.ts +2 -0
  128. package/dist/dashboard/brain-hub-project-views.d.ts.map +1 -0
  129. package/dist/dashboard/brain-hub-project-views.js +79 -0
  130. package/dist/dashboard/brain-hub-project-views.js.map +1 -0
  131. package/dist/dashboard/brain-hub-views.d.ts +2 -0
  132. package/dist/dashboard/brain-hub-views.d.ts.map +1 -0
  133. package/dist/dashboard/brain-hub-views.js +150 -0
  134. package/dist/dashboard/brain-hub-views.js.map +1 -0
  135. package/dist/dashboard/demo-workspace.d.ts +11 -0
  136. package/dist/dashboard/demo-workspace.d.ts.map +1 -0
  137. package/dist/dashboard/demo-workspace.js +178 -0
  138. package/dist/dashboard/demo-workspace.js.map +1 -0
  139. package/dist/dashboard/live-workspace-html.d.ts.map +1 -1
  140. package/dist/dashboard/live-workspace-html.js +177 -28
  141. package/dist/dashboard/live-workspace-html.js.map +1 -1
  142. package/dist/dashboard/project-feedback-html.d.ts +5 -0
  143. package/dist/dashboard/project-feedback-html.d.ts.map +1 -0
  144. package/dist/dashboard/project-feedback-html.js +216 -0
  145. package/dist/dashboard/project-feedback-html.js.map +1 -0
  146. package/dist/dashboard/project-hub-api.d.ts +5 -0
  147. package/dist/dashboard/project-hub-api.d.ts.map +1 -0
  148. package/dist/dashboard/project-hub-api.js +121 -0
  149. package/dist/dashboard/project-hub-api.js.map +1 -0
  150. package/dist/dashboard/project-kit-html.d.ts +4 -0
  151. package/dist/dashboard/project-kit-html.d.ts.map +1 -0
  152. package/dist/dashboard/project-kit-html.js +359 -0
  153. package/dist/dashboard/project-kit-html.js.map +1 -0
  154. package/dist/dashboard/project-workspace.d.ts +16 -0
  155. package/dist/dashboard/project-workspace.d.ts.map +1 -0
  156. package/dist/dashboard/project-workspace.js +106 -0
  157. package/dist/dashboard/project-workspace.js.map +1 -0
  158. package/dist/dashboard/server.d.ts +1 -0
  159. package/dist/dashboard/server.d.ts.map +1 -1
  160. package/dist/dashboard/server.js +15 -4
  161. package/dist/dashboard/server.js.map +1 -1
  162. package/dist/dashboard/v6-dashboard-html.d.ts +1 -1
  163. package/dist/dashboard/v6-dashboard-html.d.ts.map +1 -1
  164. package/dist/dashboard/v6-dashboard-html.js +27 -27
  165. package/dist/dashboard/workspace-api.d.ts +6 -0
  166. package/dist/dashboard/workspace-api.d.ts.map +1 -1
  167. package/dist/dashboard/workspace-api.js +154 -34
  168. package/dist/dashboard/workspace-api.js.map +1 -1
  169. package/dist/dashboard/workspace-html.d.ts.map +1 -1
  170. package/dist/dashboard/workspace-html.js +487 -81
  171. package/dist/dashboard/workspace-html.js.map +1 -1
  172. package/dist/dashboard/workspace-status.d.ts +50 -0
  173. package/dist/dashboard/workspace-status.d.ts.map +1 -0
  174. package/dist/dashboard/workspace-status.js +22 -0
  175. package/dist/dashboard/workspace-status.js.map +1 -0
  176. package/dist/index.d.ts +10 -0
  177. package/dist/index.d.ts.map +1 -1
  178. package/dist/index.js +7 -0
  179. package/dist/index.js.map +1 -1
  180. package/dist/integration-cli.d.ts.map +1 -1
  181. package/dist/integration-cli.js +6 -4
  182. package/dist/integration-cli.js.map +1 -1
  183. package/dist/project-cli.d.ts +3 -0
  184. package/dist/project-cli.d.ts.map +1 -0
  185. package/dist/project-cli.js +100 -0
  186. package/dist/project-cli.js.map +1 -0
  187. package/dist/types.d.ts +1 -1
  188. package/dist/types.d.ts.map +1 -1
  189. package/docs/COMPARISON.md +22 -0
  190. package/docs/COMPONENTS.md +66 -0
  191. package/docs/CONTEXT_LAB.md +79 -0
  192. package/docs/FEATURES_AND_LIMITS.md +24 -0
  193. package/docs/INTEGRATIONS.md +68 -0
  194. package/docs/LEARNING.md +46 -0
  195. package/docs/PROJECT_KIT.md +93 -0
  196. package/docs/QUICKSTART.md +44 -0
  197. package/docs/README.md +33 -0
  198. package/docs/ROADMAP-v7.md +147 -0
  199. package/docs/SHARING.md +13 -0
  200. package/docs/SHOWCASE.md +87 -0
  201. package/docs/STATS.md +21 -0
  202. package/docs/TROUBLESHOOTING.md +42 -0
  203. package/docs/UPGRADE_PLAN_v7.0.0.md +203 -0
  204. package/docs/VIRAL-PLAYBOOK.md +259 -0
  205. package/docs/VISUAL_GUIDE_v7.1.4.md +70 -0
  206. package/docs/WORKFLOW_INTEGRATION.md +19 -1
  207. package/docs/WORKSPACE.md +17 -91
  208. package/docs/assets/7.1.4-collaboration-design-preview.png +0 -0
  209. package/docs/assets/7.1.4-comparison.png +0 -0
  210. package/docs/assets/7.1.4-context-lab.png +0 -0
  211. package/docs/assets/7.1.4-control-room-dense-preview.png +0 -0
  212. package/docs/assets/7.1.4-control-room-focus-preview.png +0 -0
  213. package/docs/assets/7.1.4-features.png +0 -0
  214. package/docs/assets/7.1.4-graph.png +0 -0
  215. package/docs/assets/7.1.4-hub-control-room.png +0 -0
  216. package/docs/assets/7.1.4-hub-empty.png +0 -0
  217. package/docs/assets/7.1.4-hub-knowledge.png +0 -0
  218. package/docs/assets/7.1.4-hub-learning.png +0 -0
  219. package/docs/assets/7.1.4-hub-mobile.png +0 -0
  220. package/docs/assets/7.1.4-hub-public-docs-source.png +0 -0
  221. package/docs/assets/7.1.4-hub-public-docs.gif +0 -0
  222. package/docs/assets/7.1.4-hub-public-docs.png +0 -0
  223. package/docs/assets/7.1.4-hub-setup.png +0 -0
  224. package/docs/assets/7.1.4-hub-walkthrough.gif +0 -0
  225. package/docs/assets/7.1.4-integrations.png +0 -0
  226. package/docs/assets/7.1.4-knowledge-design-preview.png +0 -0
  227. package/docs/assets/7.1.4-learning-design-preview.png +0 -0
  228. package/docs/assets/7.1.4-learning.png +0 -0
  229. package/docs/assets/7.1.4-live.png +0 -0
  230. package/docs/assets/7.1.4-memory-review.png +0 -0
  231. package/docs/assets/7.1.4-mobile.png +0 -0
  232. package/docs/assets/7.1.4-models-design-preview.png +0 -0
  233. package/docs/assets/7.1.4-outcome-ranking.png +0 -0
  234. package/docs/assets/7.1.4-overview.png +0 -0
  235. package/docs/assets/7.1.4-project-kit.png +0 -0
  236. package/docs/assets/7.1.4-project-skill-preview.png +0 -0
  237. package/docs/assets/7.1.4-replay.png +0 -0
  238. package/docs/assets/7.1.4-setup-design-preview.png +0 -0
  239. package/docs/assets/7.1.4-usage-design-preview.png +0 -0
  240. package/docs/assets/context-v7.1.3.png +0 -0
  241. package/docs/assets/features-v7.1.3.png +0 -0
  242. package/docs/assets/graph-v7.1.3.png +0 -0
  243. package/docs/assets/integrations-v7.1.3.png +0 -0
  244. package/docs/assets/live-v7.1.4.gif +0 -0
  245. package/docs/evaluation/CONTEXT_EVALUATION_v7.1.4.json +1818 -0
  246. package/docs/evaluation/CONTEXT_EVALUATION_v7.1.4.md +36 -0
  247. package/docs/launch/COMPONENT_MAP_v7.1.4.md +307 -0
  248. package/docs/launch/PROJECT_KIT_PLAN_v7.1.4.md +110 -0
  249. package/docs/launch/PROJECT_LEARNING_PLAN_v7.1.4.md +63 -0
  250. package/docs/launch/RELEASE_NOTES_v7.1.4.md +30 -0
  251. package/docs/launch/RESEARCH_AND_PLAN_v7.1.4.md +255 -0
  252. package/package.json +25 -7
  253. package/skills/shadow-research/SKILL.md +2 -0
  254. package/skills/shadow-workflow/SKILL.md +6 -0
@@ -1 +1 @@
1
- {"version":3,"file":"v6-dashboard-html.d.ts","sourceRoot":"","sources":["../../src/dashboard/v6-dashboard-html.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,iBAAiB,sirTAqtItB,CAAC"}
1
+ {"version":3,"file":"v6-dashboard-html.d.ts","sourceRoot":"","sources":["../../src/dashboard/v6-dashboard-html.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,iBAAiB,6trTAqtItB,CAAC"}
@@ -1129,9 +1129,9 @@ export const V6_DASHBOARD_HTML = `<!DOCTYPE html>
1129
1129
  <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="22 12 18 12 15 21 9 3 6 12 2 12"/></svg>
1130
1130
  Activity
1131
1131
  </button>
1132
- <button class="nav-item" data-tab="agents" data-title="Connected Tools" title="See which AI coding tools (Claude, Cursor, Codex…) are connected to your brain.">
1132
+ <button class="nav-item" data-tab="agents" data-title="Client setup" title="Inspect coding-client configuration and recorded tool identities.">
1133
1133
  <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M23 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/></svg>
1134
- Connected Tools<span class="badge-count" id="badge-agents">0</span>
1134
+ Client setup<span class="badge-count" id="badge-agents">0</span>
1135
1135
  </button>
1136
1136
  <button class="nav-item" data-tab="livebrain" data-title="Brain Health" title="A health check of your brain's memory quality, with tips to improve it.">
1137
1137
  <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 12h-4l-3 9L9 3l-3 9H2"/></svg>
@@ -1149,9 +1149,9 @@ export const V6_DASHBOARD_HTML = `<!DOCTYPE html>
1149
1149
  <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 19V5"/><path d="M4 6h9l1 2h6v8h-9l-1-2H4"/></svg>
1150
1150
  Project Memory<span class="badge-count" id="badge-ww2br">0</span>
1151
1151
  </button>
1152
- <button class="nav-item" data-tab="graph" data-title="Live Network" title="A live animation of your tools talking to the brain.">
1152
+ <button class="nav-item" data-tab="graph" data-title="Recorded network" title="Configured clients and newly observed recorded events.">
1153
1153
  <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="3"/><circle cx="12" cy="12" r="8"/></svg>
1154
- Live Network
1154
+ Recorded network
1155
1155
  </button>
1156
1156
  <button class="nav-item" data-tab="seeder" data-title="Add Knowledge" title="Research a topic and save it as verified, source-backed knowledge.">
1157
1157
  <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 2v20"/><path d="M5 9c0-3 2-5 7-7 5 2 7 4 7 7 0 5-4 8-7 8s-7-3-7-8z"/><path d="M8 12h8"/></svg>
@@ -1279,7 +1279,7 @@ export const V6_DASHBOARD_HTML = `<!DOCTYPE html>
1279
1279
 
1280
1280
  <div class="grid cols-4">
1281
1281
  <div class="card stat-card accent">
1282
- <div class="label">Tools Connected</div>
1282
+ <div class="label">Recorded tools</div>
1283
1283
  <div class="value accent" id="ov-agents">—</div>
1284
1284
  <div class="trend">AI tools linked to your brain</div>
1285
1285
  </div>
@@ -1941,7 +1941,7 @@ export const V6_DASHBOARD_HTML = `<!DOCTYPE html>
1941
1941
  </div>
1942
1942
  <div class="grid cols-2 mt">
1943
1943
  <div class="card stat-card"><div class="label">Monthly Projection</div><div class="value" id="tok-proj">$—</div><div class="trend">from last 7 days</div></div>
1944
- <div class="card stat-card"><div class="label">Savings Available</div><div class="value" style="color:var(--success)" id="tok-save">$—</div><div class="trend up">via smart routing</div></div>
1944
+ <div class="card stat-card"><div class="label">Estimated opportunity</div><div class="value" style="color:var(--success)" id="tok-save">$—</div><div class="trend up">manual ledger · pricing estimates</div></div>
1945
1945
  </div>
1946
1946
  <div class="warning-banner mt" id="tok-runtime-note"><strong>Accuracy</strong><span>Token records are historical. Current agent and model spend are shown separately.</span></div>
1947
1947
  <div class="grid cols-2 mt-lg">
@@ -2213,10 +2213,10 @@ const FEATURES = [
2213
2213
  ['memory','Memories','Browse and search saved knowledge'],
2214
2214
  ['chat','Ask the Brain','Ask questions in plain language'],
2215
2215
  ['activity','Activity','Live feed of what the brain is doing'],
2216
- ['agents','Connected Tools','Which AI tools are linked to the brain'],
2216
+ ['agents','Client setup','Client configuration and recorded tools'],
2217
2217
  ['livebrain','Brain Health','Memory quality with tips to improve'],
2218
2218
  ['ww2br','Project Memory','Memory and status for one project'],
2219
- ['graph','Live Network','Live animation of tools talking to the brain'],
2219
+ ['graph','Recorded network','Configured clients and newly observed recorded events'],
2220
2220
  ['seeder','Add Knowledge','Research a topic and save it as verified knowledge'],
2221
2221
  ['garden','Memory Garden','Watch your memories grow over time'],
2222
2222
  ['sabb','Sub-Agents','Share the right context with helper agents'],
@@ -2412,10 +2412,10 @@ async function refresh() {
2412
2412
  state.hive = hive;
2413
2413
  state.lastRefreshAt = Date.now();
2414
2414
 
2415
- animateNum('stat-agents', hive.totalAgentsConnected ?? 0);
2415
+ animateNum('stat-agents', hive.totalRecordedAgents ?? hive.totalAgentsConnected ?? 0);
2416
2416
  animateNum('stat-memories', hive.totalMemoriesStored ?? 0);
2417
2417
  animateNum('stat-dreams', hive.modules.dream?.totalDreams ?? 0);
2418
- animateNum('stat-tools', hive.totalAgentsConnected ?? 0);
2418
+ animateNum('stat-tools', hive.totalRecordedAgents ?? hive.totalAgentsConnected ?? 0);
2419
2419
  document.getElementById('badge-sabb').textContent = hive.modules.sabb?.totalSpawns ?? 0;
2420
2420
  document.getElementById('badge-causal').textContent = hive.modules.causal?.totalLinks ?? 0;
2421
2421
  document.getElementById('badge-collision').textContent = hive.modules.collision?.collisionsDetected ?? 0;
@@ -2428,7 +2428,7 @@ async function refresh() {
2428
2428
  async function refreshOverview() {
2429
2429
  const hive = state.hive || await api('/api/v6/hive-status');
2430
2430
  if (!hive) return;
2431
- animateNum('ov-agents', hive.totalAgentsConnected ?? 0);
2431
+ animateNum('ov-agents', hive.totalRecordedAgents ?? hive.totalAgentsConnected ?? 0);
2432
2432
  animateNum('ov-memories', hive.totalMemoriesStored ?? 0);
2433
2433
  animateNum('ov-subagents', hive.modules.sabb?.totalSpawns ?? 0);
2434
2434
  document.getElementById('ov-subagents-sub').textContent = \`grad: \${hive.modules.sabb?.graduated ?? 0} · quar: \${hive.modules.sabb?.quarantined ?? 0}\`;
@@ -2455,7 +2455,7 @@ async function refreshOverview() {
2455
2455
  ['Air-gap', hive.modules.airGap?.enabled ? 'ENABLED' : 'disabled', hive.modules.airGap?.enabled ? 'good' : ''],
2456
2456
  ['Version', hive.version, ''],
2457
2457
  ['Monthly cost', '$' + (hive.modules.tokenEconomy?.monthlyProjectionUsd?.toFixed?.(2) ?? '0.00'), 'warn'],
2458
- ['Cost saved/mo', '$' + (hive.modules.tokenEconomy?.savingsOpportunitiesUsd?.toFixed?.(2) ?? '0.00'), 'good'],
2458
+ ['Estimated savings opportunity', '$' + (hive.modules.tokenEconomy?.savingsOpportunitiesUsd?.toFixed?.(2) ?? '0.00'), 'good'],
2459
2459
  ];
2460
2460
  document.getElementById('ov-health').innerHTML = health.map(([k, v, cls]) => \`<div class="row"><span class="k">\${k}</span><span class="v \${cls}">\${v}</span></div>\`).join('');
2461
2461
 
@@ -3298,7 +3298,7 @@ async function refreshConfig() {
3298
3298
  document.getElementById('config-state').innerHTML = \`
3299
3299
  <div class="row"><span class="k">Version</span><span class="v">\${hive.version}</span></div>
3300
3300
  <div class="row"><span class="k">Local-first</span><span class="v \${hive.localFirst ? 'good' : 'warn'}">\${hive.localFirst ? 'YES' : 'no'}</span></div>
3301
- <div class="row"><span class="k">Agents connected</span><span class="v">\${hive.totalAgentsConnected}</span></div>
3301
+ <div class="row"><span class="k">Recorded tools</span><span class="v">\${hive.totalAgentsConnected}</span></div>
3302
3302
  <div class="row"><span class="k">Memories stored</span><span class="v">\${hive.totalMemoriesStored}</span></div>
3303
3303
  <div class="row"><span class="k">Modules active</span><span class="v good">\${Object.keys(hive.modules || {}).length}</span></div>\`;
3304
3304
  }
@@ -3614,14 +3614,7 @@ function ingestActivity(raw, fresh) {
3614
3614
  if (!activityPaused) {
3615
3615
  appendLog(e.source, e.type, e.detail);
3616
3616
  if (fresh) {
3617
- try {
3618
- if (typeof graphTimer !== 'undefined' && graphTimer) {
3619
- const col = signalColors[e.type] || '#18ffff';
3620
- const srcExists = graphNodes.find(n => n.id === e.source);
3621
- graphSignals.push({ fromId: srcExists ? e.source : 'brain', toId: 'brain', type: e.type, color: col, progress: 0, spawnedAt: Date.now() });
3622
- if (graphSignals.length > 100) graphSignals = graphSignals.slice(-100);
3623
- }
3624
- } catch (err) {}
3617
+ // Graph animation consumes the deduplicated stored-event stream in pullTopology.
3625
3618
  lastIngestFresh = true;
3626
3619
  }
3627
3620
  }
@@ -3707,6 +3700,7 @@ async function refreshActivity() {
3707
3700
  }
3708
3701
 
3709
3702
  // ──── Live Graph ───────────────────────────────────────────────────────
3703
+ let graphObservedEvents = new Set(), graphObserved = false;
3710
3704
  let graphFrame = 0, graphNodes = [], graphEdges = [], graphSignals = [], graphTimer = null, graphPullTimer = null;
3711
3705
  const signalColors = { 'memory-write':'#10e68c','subagent-spawn':'#18ffff','dream':'#a855f7','revisit':'#a855f7','counterfactual':'#a855f7','consolidation':'#a855f7','contradiction':'#ff5864','pattern-discovery':'#10e68c','collision':'#ff5864','quarantine':'#ec4899','firewall-block':'#ffb53f' };
3712
3706
  async function startGraph() { await pullTopology(); if (graphTimer) cancelAnimationFrame(graphTimer); graphLoop(); if (graphPullTimer) clearInterval(graphPullTimer); graphPullTimer = setInterval(pullTopology, 3000); }
@@ -3718,10 +3712,10 @@ async function pullTopology() {
3718
3712
  const W = canvas.width, H = canvas.height, cx = W/2, cy = H/2;
3719
3713
  const prev = new Map(graphNodes.map(n => [n.id, { x: n.x, y: n.y }]));
3720
3714
  graphNodes = [{ id:'brain', label:'HIVE MIND', x: cx, y: cy, r: 50, type:'brain', color:'#a855f7' }];
3721
- (topo.agents || []).forEach((a, i, arr) => {
3715
+ (topo.agents || []).filter(a => a.configurationDetected).forEach((a, i, arr) => {
3722
3716
  const ang = (i / arr.length) * Math.PI * 2 - Math.PI / 2, r = 240;
3723
3717
  const p = prev.get(a.id);
3724
- graphNodes.push({ id: a.id, label: a.displayName, x: p?.x ?? cx + Math.cos(ang) * r, y: p?.y ?? cy + Math.sin(ang) * r, targetX: cx + Math.cos(ang) * r, targetY: cy + Math.sin(ang) * r, r: a.connected ? 24 : 16, type:'agent', connected: a.connected, color: a.connected ? '#18ffff' : '#556080' });
3718
+ graphNodes.push({ id: a.id, label: a.displayName, x: p?.x ?? cx + Math.cos(ang) * r, y: p?.y ?? cy + Math.sin(ang) * r, targetX: cx + Math.cos(ang) * r, targetY: cy + Math.sin(ang) * r, r: 20, type:'agent', connected: false, color: '#8695aa' });
3725
3719
  });
3726
3720
  (topo.subAgents || []).forEach((s, i, arr) => {
3727
3721
  const parent = graphNodes.find(n => n.id === s.parent), ang = (i / Math.max(arr.length,1)) * Math.PI * 2, r = 130;
@@ -3730,19 +3724,25 @@ async function pullTopology() {
3730
3724
  });
3731
3725
  graphEdges = [];
3732
3726
  for (const n of graphNodes) {
3733
- if (n.type === 'agent') graphEdges.push({ from:'brain', to: n.id, active: n.connected });
3734
- if (n.type === 'subagent') graphEdges.push({ from: n.parent ?? 'brain', to: n.id, active: true });
3727
+ if (n.type === 'agent') graphEdges.push({ from:'brain', to: n.id, active: false });
3728
+ if (n.type === 'subagent') graphEdges.push({ from: n.parent ?? 'brain', to: n.id, active: false });
3735
3729
  }
3736
3730
  const now = Date.now();
3737
3731
  for (const e of (topo.events || [])) {
3732
+ const eventKey = e.id || JSON.stringify([e.type,e.source,e.target,e.timestamp]);
3733
+ if (graphObservedEvents.has(eventKey)) continue;
3734
+ graphObservedEvents.add(eventKey);
3735
+ if (!graphObserved) continue;
3738
3736
  const src = e.source ?? 'brain';
3739
- const srcN = graphNodes.find(n => n.id === src) ?? graphNodes[0];
3737
+ const srcN = graphNodes.find(n => n.id === src);
3740
3738
  const dstN = e.type === 'subagent-spawn' ? graphNodes.find(n => n.id === e.target) : graphNodes.find(n => n.id === 'brain');
3741
3739
  if (!srcN || !dstN) continue;
3742
3740
  graphSignals.push({ fromId: srcN.id, toId: dstN.id, type: e.type, color: signalColors[e.type] || '#18ffff', progress: 0, spawnedAt: now });
3743
3741
  }
3742
+ graphObserved = true;
3743
+ if (graphObservedEvents.size > 500) graphObservedEvents = new Set([...graphObservedEvents].slice(-500));
3744
3744
  if (graphSignals.length > 100) graphSignals = graphSignals.slice(-100);
3745
- document.getElementById('graph-meta').textContent = \`\${(topo.agents || []).length} agents · \${(topo.subAgents || []).length} sub-agents · \${graphSignals.length} signals\`;
3745
+ document.getElementById('graph-meta').textContent = \`\${(topo.agents || []).filter(a => a.configurationDetected).length} configured/detected clients · \${(topo.subAgents || []).length} recorded sub-agents · \${graphSignals.length} new event signals\`;
3746
3746
  }
3747
3747
  function graphLoop() {
3748
3748
  const c = document.getElementById('graph-canvas');
@@ -3,6 +3,8 @@ import { type GlobalBrain } from '../brain/global-brain.js';
3
3
  import { type FeatureSettingsStore } from '../brain/feature-settings.js';
4
4
  import { type MemoryAnnotationsStore } from '../brain/memory-annotations.js';
5
5
  import { type ActivityJournal } from '../brain/activity-journal.js';
6
+ import { WorkspaceRuntime } from './workspace-status.js';
7
+ import type { ProjectHubService } from '../brain/project-hub.js';
6
8
  export interface WorkspaceDependencies {
7
9
  brain?: GlobalBrain;
8
10
  settings?: FeatureSettingsStore;
@@ -10,6 +12,10 @@ export interface WorkspaceDependencies {
10
12
  projectDir?: string;
11
13
  journal?: ActivityJournal;
12
14
  demo?: boolean;
15
+ localProject?: boolean;
16
+ runtime?: WorkspaceRuntime;
17
+ hub?: ProjectHubService;
18
+ prepareStorage?: () => Promise<string | null>;
13
19
  }
14
20
  /** The workspace API reads only the memory store and dedicated feature settings. */
15
21
  export declare function handleWorkspaceRequest(req: IncomingMessage, res: ServerResponse, dependencies?: WorkspaceDependencies): Promise<void>;
@@ -1 +1 @@
1
- {"version":3,"file":"workspace-api.d.ts","sourceRoot":"","sources":["../../src/dashboard/workspace-api.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AACjE,OAAO,EAAkB,KAAK,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAG5E,OAAO,EAA2C,KAAK,oBAAoB,EAAE,MAAM,8BAA8B,CAAC;AAClH,OAAO,EAAqC,KAAK,sBAAsB,EAAiC,MAAM,gCAAgC,CAAC;AAG/I,OAAO,EAAsB,KAAK,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAOxF,MAAM,WAAW,qBAAqB;IACpC,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,QAAQ,CAAC,EAAE,oBAAoB,CAAC;IAChC,WAAW,CAAC,EAAE,sBAAsB,CAAC;IACrC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,eAAe,CAAC;IAC1B,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAsED,oFAAoF;AACpF,wBAAsB,sBAAsB,CAAC,GAAG,EAAE,eAAe,EAAE,GAAG,EAAE,cAAc,EAAE,YAAY,GAAE,qBAA0B,GAAG,OAAO,CAAC,IAAI,CAAC,CAqM/I"}
1
+ {"version":3,"file":"workspace-api.d.ts","sourceRoot":"","sources":["../../src/dashboard/workspace-api.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAEjE,OAAO,EAAkB,KAAK,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAK5E,OAAO,EAA2C,KAAK,oBAAoB,EAAE,MAAM,8BAA8B,CAAC;AAClH,OAAO,EAAqC,KAAK,sBAAsB,EAAiC,MAAM,gCAAgC,CAAC;AAG/I,OAAO,EAAsB,KAAK,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAMxF,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAEzD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAKjE,MAAM,WAAW,qBAAqB;IACpC,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,QAAQ,CAAC,EAAE,oBAAoB,CAAC;IAChC,WAAW,CAAC,EAAE,sBAAsB,CAAC;IACrC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,eAAe,CAAC;IAC1B,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,OAAO,CAAC,EAAE,gBAAgB,CAAC;IAC3B,GAAG,CAAC,EAAE,iBAAiB,CAAC;IACxB,cAAc,CAAC,EAAE,MAAM,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;CAC/C;AAwED,oFAAoF;AACpF,wBAAsB,sBAAsB,CAAC,GAAG,EAAE,eAAe,EAAE,GAAG,EAAE,cAAc,EAAE,YAAY,GAAE,qBAA0B,GAAG,OAAO,CAAC,IAAI,CAAC,CA4R/I"}
@@ -1,13 +1,22 @@
1
+ import { randomUUID } from 'node:crypto';
1
2
  import { getGlobalBrain } from '../brain/global-brain.js';
2
3
  import { getBrainBus } from '../brain/brain-bus.js';
3
4
  import { assembleContext } from '../brain/context-assembly.js';
5
+ import { parseContextRequest } from '../brain/context-request.js';
6
+ import { inspectMemoryHealth } from '../brain/memory-inspection.js';
4
7
  import { FEATURE_DEFINITIONS, getFeatureSettings } from '../brain/feature-settings.js';
5
8
  import { memoryAnnotationsFor, memoryState } from '../brain/memory-annotations.js';
6
9
  import { normalizeMemorySources } from '../brain/memory-sources.js';
7
10
  import { inspectClientIntegrations } from '../brain/client-integrations.js';
8
11
  import { activityJournalFor } from '../brain/activity-journal.js';
9
12
  import { reportAgentActivity } from '../brain/activity-reporting.js';
13
+ import { inspectProject, planProjectSkill, applyProjectSkill } from '../brain/project-kit.js';
14
+ import { requireProjectStore, addProjectKnowledge } from '../brain/project-memory.js';
15
+ import { ProjectFeedbackStore } from '../brain/project-feedback.js';
16
+ import { WorkspaceRuntime } from './workspace-status.js';
17
+ import { isProjectHubRoute, handleProjectHubRequest } from './project-hub-api.js';
10
18
  const INVENTORY_LIMIT = 2500;
19
+ const sharedRuntime = new WorkspaceRuntime('shared');
11
20
  function sources(entry) {
12
21
  return normalizeMemorySources(entry.metadata, { limit: 8, maxChars: 500 });
13
22
  }
@@ -89,10 +98,105 @@ export async function handleWorkspaceRequest(req, res, dependencies = {}) {
89
98
  const url = new URL(req.url ?? '/', 'http://localhost');
90
99
  const route = url.pathname.slice('/api/workspace/'.length);
91
100
  const method = req.method ?? 'GET';
101
+ const expectedProject = req.headers['x-shadow-brain-project'];
102
+ if (method === 'POST' && expectedProject !== undefined && expectedProject !== dependencies.runtime?.project?.id) {
103
+ return send({ error: 'This port now serves a different project. Reload the portal before making changes.', code: 'WORKSPACE_PROJECT_CHANGED' }, 409);
104
+ }
105
+ const storageError = await dependencies.prepareStorage?.();
106
+ if (route === 'status' && method === 'GET')
107
+ return send((dependencies.runtime ?? sharedRuntime).status(dependencies.brain, !!dependencies.hub && !storageError, storageError));
108
+ if (storageError)
109
+ return send({ error: storageError, code: 'WORKSPACE_STORAGE_UNAVAILABLE' }, 503);
110
+ if (isProjectHubRoute(url.pathname)) {
111
+ const settings = dependencies.settings ?? getFeatureSettings();
112
+ const journal = dependencies.journal;
113
+ const correlationId = randomUUID();
114
+ const projectId = dependencies.runtime?.project?.id;
115
+ const tool = 'hub-' + (url.pathname.split('/')[4] || 'overview');
116
+ const record = async (kind) => {
117
+ if (method !== 'POST' || !journal || !settings.get().features.activityRecording)
118
+ return;
119
+ await journal.append({ kind, source: 'reported', transport: 'host', projectId, agent: 'portal', tool, correlationId,
120
+ ...(kind === 'tool-end' ? { outcome: res.statusCode < 400 ? 'success' : 'error' } : {}) }).catch(() => { });
121
+ };
122
+ await record('tool-start');
123
+ try {
124
+ await handleProjectHubRequest(req, res, dependencies.hub, url);
125
+ }
126
+ finally {
127
+ await record('tool-end');
128
+ }
129
+ return;
130
+ }
131
+ const local = dependencies.localProject && dependencies.projectDir && dependencies.brain
132
+ ? requireProjectStore(dependencies.projectDir, dependencies.brain) : undefined;
133
+ const projectRoutes = ['project-profile', 'project-knowledge', 'project-skill-preview', 'project-skill-apply', 'project-feedback'];
92
134
  const writes = ['memory-review', 'memory-link', 'memory-unlink'];
93
- if (!['GET', 'POST'].includes(method) || (method === 'POST' && !['context', 'features', 'activity-report', ...writes].includes(route)))
135
+ if (!['GET', 'POST'].includes(method) || (method === 'POST' && !['context', 'context-compare', 'features', 'activity-report', ...writes, ...projectRoutes.slice(1)].includes(route)))
94
136
  return send({ error: 'Method not allowed.' }, 405);
95
137
  const settings = dependencies.settings ?? getFeatureSettings();
138
+ const localActivity = () => {
139
+ const enabled = settings.get().features.liveActivity;
140
+ if (!local)
141
+ return activity(enabled);
142
+ const journal = dependencies.journal ?? activityJournalFor(dependencies.brain);
143
+ return { enabled, activity: enabled ? journal.read({ projectId: local.projectId, limit: 200 }).events.slice(-30).reverse().map(event => ({
144
+ id: event.id, type: event.kind, label: [event.tool, event.phase, event.outcome].filter(Boolean).join(' · ') || event.kind,
145
+ agent: event.agent ?? '', project: local.projectName, timestamp: event.timestamp,
146
+ })) : [] };
147
+ };
148
+ if (projectRoutes.includes(route)) {
149
+ if (!local)
150
+ return send({ error: 'Open shadow-brain project workspace for project-local knowledge and skills.' }, 409);
151
+ if (route === 'project-profile')
152
+ return send(inspectProject(local.root));
153
+ if (route === 'project-feedback') {
154
+ const feedback = new ProjectFeedbackStore(local.root, dependencies.brain);
155
+ if (method === 'GET')
156
+ return send(feedback.get());
157
+ const input = await body(req);
158
+ const { expectedRevision, kind, ...params } = input;
159
+ await dependencies.brain.init();
160
+ dependencies.brain.refresh();
161
+ if (dependencies.brain.getStorageHealth().lastError)
162
+ throw new Error('Project memory could not be read; feedback was not changed.');
163
+ const command = { kind: kind === 'record' ? 'upsert' : kind === 'enable' ? 'toggle' : kind, ...params };
164
+ return send(await feedback.update(command, expectedRevision, 'user'));
165
+ }
166
+ if (method !== 'POST')
167
+ return send({ error: 'Use POST for project knowledge and skill requests.' }, 405);
168
+ const input = await body(req);
169
+ if (route === 'project-skill-preview')
170
+ return send(planProjectSkill(local.root, input));
171
+ const started = Date.now();
172
+ const correlationId = randomUUID();
173
+ const recordOperation = async (outcome) => {
174
+ if (!settings.get().features.activityRecording)
175
+ return;
176
+ try {
177
+ await (dependencies.journal ?? activityJournalFor(dependencies.brain)).append({ projectId: local.projectId,
178
+ kind: outcome ? 'tool-end' : 'tool-start', source: 'reported', transport: 'host', agent: 'user', sessionId: 'project-portal',
179
+ tool: route, correlationId, ...(outcome ? { outcome, durationMs: Math.max(0, Date.now() - started) } : {}) });
180
+ }
181
+ catch { /* The committed result remains valid when optional activity recording is unavailable. */ }
182
+ };
183
+ await recordOperation();
184
+ let result;
185
+ try {
186
+ if (route === 'project-knowledge')
187
+ result = await addProjectKnowledge(local.root, dependencies.brain, input);
188
+ else {
189
+ const { expectedPlanHash, ...params } = input;
190
+ result = await applyProjectSkill(local.root, params, expectedPlanHash);
191
+ }
192
+ }
193
+ catch (error) {
194
+ await recordOperation('error');
195
+ throw error;
196
+ }
197
+ await recordOperation('success');
198
+ return send(result);
199
+ }
96
200
  if (route === 'features') {
97
201
  if (method === 'POST') {
98
202
  const input = await body(req);
@@ -105,7 +209,7 @@ export async function handleWorkspaceRequest(req, res, dependencies = {}) {
105
209
  return send(settingsResponse(settings));
106
210
  }
107
211
  if (route === 'activity')
108
- return send(activity(settings.get().features.liveActivity));
212
+ return send(localActivity());
109
213
  if (route === 'telemetry') {
110
214
  const flags = settings.get().features;
111
215
  const common = { checkedAt: new Date().toISOString(), liveEnabled: flags.liveActivity, recordingEnabled: flags.activityRecording, demo: dependencies.demo === true };
@@ -116,23 +220,29 @@ export async function handleWorkspaceRequest(req, res, dependencies = {}) {
116
220
  const cursor = qs.get('cursor');
117
221
  const journal = dependencies.journal ?? activityJournalFor(dependencies.brain ?? getGlobalBrain());
118
222
  const page = journal.read({ afterSequence: cursor === null ? undefined : bounded(cursor, 0, Number.MAX_SAFE_INTEGER, 0),
119
- journalId: query(qs, 'journalId'), projectId: query(qs, 'project'), limit: bounded(qs.get('limit'), 200, 200) });
223
+ journalId: query(qs, 'journalId'), projectId: local?.projectId ?? query(qs, 'project'), limit: bounded(qs.get('limit'), 200, 200) });
120
224
  return send({ ...common, ...page, cursor: page.nextAfterSequence });
121
225
  }
122
226
  if (route === 'activity-report') {
123
227
  if (method !== 'POST')
124
228
  return send({ error: 'Use POST to report activity.' }, 405);
125
229
  const input = await body(req);
230
+ if (local && input.projectId !== undefined && input.projectId !== local.projectId)
231
+ throw new TypeError('This portal is pinned to its local project.');
232
+ if (local)
233
+ input.projectId = local.projectId;
126
234
  return send(await reportAgentActivity(dependencies.brain ?? getGlobalBrain(), settings, input, dependencies.journal));
127
235
  }
128
- if (!['overview', 'memories', 'graph', 'context', 'memory', 'integrations', ...writes].includes(route))
236
+ if (!['overview', 'memories', 'graph', 'context', 'context-compare', 'memory', 'memory-health', 'integrations', ...writes].includes(route))
129
237
  return send({ error: 'Not found.' }, 404);
130
- if (route === 'context' && method !== 'POST')
238
+ if (['context', 'context-compare'].includes(route) && method !== 'POST')
131
239
  return send({ error: 'Use POST for context assembly.' }, 405);
132
240
  if (writes.includes(route) && method !== 'POST')
133
241
  return send({ error: 'Use POST for memory changes.' }, 405);
134
242
  const mutationInput = writes.includes(route) ? await body(req) : undefined;
135
243
  if (mutationInput) {
244
+ if (local && mutationInput.projectId !== local.projectId)
245
+ throw new TypeError('This portal is pinned to its local project.');
136
246
  if (!Number.isSafeInteger(mutationInput.expectedRevision) || Number(mutationInput.expectedRevision) < 0)
137
247
  throw new TypeError('A current expectedRevision is required.');
138
248
  const allowed = route === 'memory-review' ? ['projectId', 'memoryId', 'state', 'note', 'expectedRevision'] :
@@ -142,41 +252,42 @@ export async function handleWorkspaceRequest(req, res, dependencies = {}) {
142
252
  }
143
253
  const brain = dependencies.brain ?? getGlobalBrain();
144
254
  if (route === 'integrations')
145
- return send(inspectClientIntegrations({ projectDir: dependencies.projectDir, brain }));
255
+ return send(inspectClientIntegrations({ projectDir: dependencies.projectDir, brain, localProjectId: local?.projectId }));
146
256
  const annotationStore = dependencies.annotations ?? memoryAnnotationsFor(brain);
147
257
  const refresh = async () => {
148
258
  await brain.init();
149
- if (writes.includes(route))
150
- brain.refresh();
151
- else
152
- await brain.sync();
259
+ brain.refresh();
153
260
  if (brain.getStorageHealth().lastError)
154
261
  throw new Error('The memory store could not be read. Check storage health.');
155
262
  };
156
- if (route === 'context') {
263
+ if (route === 'context' || route === 'context-compare') {
157
264
  const input = await body(req);
158
- if (Object.keys(input).some(key => !['projectId', 'query', 'tokenBudget', 'includeCrossProject'].includes(key)))
159
- throw new TypeError('Unknown context request field.');
160
- if (typeof input.projectId !== 'string' || !input.projectId.trim())
161
- throw new TypeError('Choose a project.');
162
- if (input.query !== undefined && typeof input.query !== 'string')
163
- throw new TypeError('Task must be text.');
164
- if (input.includeCrossProject !== undefined && typeof input.includeCrossProject !== 'boolean')
165
- throw new TypeError('Cross-project choice must be boolean.');
166
- if (input.tokenBudget !== undefined && (!Number.isSafeInteger(input.tokenBudget) || Number(input.tokenBudget) < 0 || Number(input.tokenBudget) > 12000))
167
- throw new TypeError('Token budget must be an integer from 0 to 12000.');
265
+ const options = parseContextRequest(input);
266
+ if (local && (options.projectId !== local.projectId || options.includeCrossProject === true))
267
+ throw new TypeError('This portal is pinned to its local project.');
168
268
  const flags = settings.get().features;
169
269
  if (flags.contextAssembly)
170
270
  await refresh();
171
- const pack = assembleContext(brain, { projectId: input.projectId, query: input.query,
172
- tokenBudget: input.tokenBudget, includeCrossProject: input.includeCrossProject,
173
- features: flags, annotations: flags.contextAssembly ? annotationStore.get() : undefined });
174
- return send(pack);
271
+ const common = { ...options, features: flags, annotations: flags.contextAssembly ? annotationStore.get() : undefined,
272
+ feedback: flags.contextAssembly && local ? new ProjectFeedbackStore(local.root, brain).get() : undefined };
273
+ if (route === 'context')
274
+ return send(assembleContext(brain, common));
275
+ const legacy = assembleContext(brain, { ...common, profile: 'legacy' });
276
+ const adaptive = assembleContext(brain, { ...common, profile: 'adaptive' });
277
+ const oldIds = new Set(legacy.entries.map(entry => entry.id));
278
+ const newIds = new Set(adaptive.entries.map(entry => entry.id));
279
+ return send({ legacy, adaptive, delta: { addedIds: [...newIds].filter(id => !oldIds.has(id)),
280
+ removedIds: [...oldIds].filter(id => !newIds.has(id)), sharedIds: [...newIds].filter(id => oldIds.has(id)) },
281
+ warnings: ['Both profiles use the same memory view. Their token estimators differ; selection changes are not proof of higher accuracy.'] });
175
282
  }
176
283
  await refresh();
177
284
  const annotations = annotationStore.get();
178
285
  const qs = url.searchParams;
179
- const requestedProject = query(qs, 'project');
286
+ const requestedProject = local?.projectId ?? query(qs, 'project');
287
+ if (local && query(qs, 'project') && query(qs, 'project') !== local.projectId)
288
+ throw new TypeError('This portal is pinned to its local project.');
289
+ if (route === 'memory-health')
290
+ return send(inspectMemoryHealth(brain, annotations, { projectId: requestedProject, limit: bounded(qs.get('limit'), 30, 100) }));
180
291
  if (route === 'memory') {
181
292
  const id = query(qs, 'id');
182
293
  if (!id || !requestedProject)
@@ -218,16 +329,23 @@ export async function handleWorkspaceRequest(req, res, dependencies = {}) {
218
329
  const committed = await annotationStore.update(command, input.expectedRevision);
219
330
  return send({ revision: committed.revision, updatedAt: committed.updatedAt });
220
331
  }
221
- const inventory = brain.timeline({ limit: INVENTORY_LIMIT });
332
+ const recentInventory = brain.timeline({ limit: INVENTORY_LIMIT, ...(local ? { projectId: local.projectId } : {}) });
333
+ const inventory = requestedProject ? brain.timeline({ projectId: requestedProject, limit: INVENTORY_LIMIT }) : recentInventory;
222
334
  const projectMap = new Map();
223
- for (const entry of inventory) {
335
+ if (local)
336
+ projectMap.set(local.projectId, { id: local.projectId, name: local.projectName, memories: 0, updatedAt: null });
337
+ for (const entry of recentInventory) {
224
338
  const project = projectMap.get(entry.projectId) ?? { id: entry.projectId, name: entry.projectName || entry.projectId, memories: 0, updatedAt: date(entry.createdAt) };
225
339
  project.memories++;
226
340
  projectMap.set(entry.projectId, project);
227
341
  }
342
+ if (requestedProject && inventory.length)
343
+ projectMap.set(requestedProject, {
344
+ id: requestedProject, name: inventory[0].projectName || requestedProject, memories: inventory.length, updatedAt: date(inventory[0].createdAt),
345
+ });
228
346
  const projects = [...projectMap.values()].sort((a, b) => a.name.localeCompare(b.name));
229
347
  const facets = { projects, categories: [...new Set(inventory.map(entry => entry.category))].sort(), agents: [...new Set(inventory.map(entry => entry.agentTool))].sort() };
230
- const inventoryTruncated = brain.getStats().totalEntries > inventory.length;
348
+ const inventoryTruncated = requestedProject ? brain.timeline({ projectId: requestedProject, limit: INVENTORY_LIMIT + 1 }).length > INVENTORY_LIMIT : brain.getStats().totalEntries > inventory.length;
231
349
  if (route === 'overview') {
232
350
  const scoped = inventory.filter(entry => !requestedProject || entry.projectId === requestedProject);
233
351
  const quality = { total: scoped.length, sourceBacked: 0, active: 0, contested: 0, archived: 0, superseded: 0, reviewed: 0 };
@@ -238,13 +356,15 @@ export async function handleWorkspaceRequest(req, res, dependencies = {}) {
238
356
  if (annotations.reviews[entry.id]?.projectId === entry.projectId)
239
357
  quality.reviewed++;
240
358
  }
241
- return send({ version: '7.1.3', totals: { projects: projects.length, memories: brain.getStats().totalEntries, agents: facets.agents.length },
242
- projects, ...activity(settings.get().features.liveActivity), storageHealth: brain.getStorageHealth(),
359
+ return send({ version: '7.1.4', demo: dependencies.demo === true,
360
+ projectKit: local ? { available: true, projectId: local.projectId, projectName: local.projectName, root: local.root, storagePath: local.storagePath } : { available: false },
361
+ totals: { projects: projects.length, memories: local ? inventory.length : brain.getStats().totalEntries, agents: facets.agents.length },
362
+ projects, ...localActivity(), storageHealth: brain.getStorageHealth(),
243
363
  features: settings.get().features, recentMemories: scoped.slice(0, 5).map(entry => summary(entry, annotations)), truncated: inventoryTruncated,
244
364
  quality, scope: { projectId: requestedProject ?? null, basis: 'recent-inventory' },
245
365
  inventoryLimit: INVENTORY_LIMIT });
246
366
  }
247
- const projectId = query(qs, 'project');
367
+ const projectId = requestedProject;
248
368
  const category = query(qs, 'category');
249
369
  const agentTool = query(qs, 'agent');
250
370
  const search = query(qs, 'q');
@@ -288,7 +408,7 @@ export async function handleWorkspaceRequest(req, res, dependencies = {}) {
288
408
  }
289
409
  const detail = summary(entry, annotations);
290
410
  nodes.push({ id: memoryNode, kind: 'memory', label: detail.title, project: entry.projectId, category: entry.category,
291
- memoryId: entry.id, effectiveState: detail.effectiveState, snippet: detail.snippet, source: detail.source, agent: entry.agentTool, updatedAt: detail.updatedAt });
411
+ memoryId: entry.id, effectiveState: detail.effectiveState, snippet: detail.snippet, source: detail.source, sources: detail.sources, agent: entry.agentTool, updatedAt: detail.updatedAt });
292
412
  edges.push({ source: projectNode, target: memoryNode, kind: 'belongs-to' }, { source: agentNode, target: memoryNode, kind: 'recorded-by' });
293
413
  }
294
414
  const shownById = new Map(shown.map(entry => [entry.id, entry.projectId]));
@@ -301,7 +421,7 @@ export async function handleWorkspaceRequest(req, res, dependencies = {}) {
301
421
  }
302
422
  catch (error) {
303
423
  const code = error.code;
304
- send({ error: error instanceof Error ? error.message : String(error), ...(code ? { code } : {}) }, code === 'FEATURE_SETTINGS_CONFLICT' || code === 'ANNOTATIONS_CONFLICT' ? 409 : code === 'ANNOTATIONS_NOT_FOUND' ? 404 :
424
+ send({ error: error instanceof Error ? error.message : String(error), ...(code ? { code } : {}) }, code === 'FEATURE_SETTINGS_CONFLICT' || code === 'ANNOTATIONS_CONFLICT' || code?.includes('CONFLICT') || code?.includes('STALE') ? 409 : code === 'ANNOTATIONS_NOT_FOUND' ? 404 :
305
425
  code === 'FEATURE_SETTINGS_INVALID_PATCH' || code === 'ANNOTATIONS_INVALID_COMMAND' || code === 'ACTIVITY_JOURNAL_INVALID_EVENT' ||
306
426
  code === 'ACTIVITY_JOURNAL_INVALID_CURSOR' || error instanceof TypeError || error instanceof RangeError ? 400 : 500);
307
427
  }