@theihtisham/agent-shadow-brain 7.1.4 → 7.22.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (275) hide show
  1. package/CHANGELOG.md +478 -462
  2. package/README.md +97 -24
  3. package/dist/brain/auto-update.js +1 -1
  4. package/dist/brain/auto-update.js.map +1 -1
  5. package/dist/brain/brain-portability.js +1 -1
  6. package/dist/brain/brain-portability.js.map +1 -1
  7. package/dist/brain/client-integrations.js +1 -1
  8. package/dist/brain/client-integrations.js.map +1 -1
  9. package/dist/brain/context-assembly.d.ts +10 -0
  10. package/dist/brain/context-assembly.d.ts.map +1 -1
  11. package/dist/brain/context-assembly.js +15 -6
  12. package/dist/brain/context-assembly.js.map +1 -1
  13. package/dist/brain/context-efficiency.d.ts +48 -0
  14. package/dist/brain/context-efficiency.d.ts.map +1 -0
  15. package/dist/brain/context-efficiency.js +34 -0
  16. package/dist/brain/context-efficiency.js.map +1 -0
  17. package/dist/brain/context-request.d.ts +1 -1
  18. package/dist/brain/context-request.d.ts.map +1 -1
  19. package/dist/brain/context-request.js +4 -2
  20. package/dist/brain/context-request.js.map +1 -1
  21. package/dist/brain/context-retrieval.d.ts +1 -1
  22. package/dist/brain/context-retrieval.d.ts.map +1 -1
  23. package/dist/brain/context-retrieval.js +2 -1
  24. package/dist/brain/context-retrieval.js.map +1 -1
  25. package/dist/brain/feature-settings.d.ts +5 -0
  26. package/dist/brain/feature-settings.d.ts.map +1 -1
  27. package/dist/brain/feature-settings.js +8 -2
  28. package/dist/brain/feature-settings.js.map +1 -1
  29. package/dist/brain/global-brain.d.ts +1 -1
  30. package/dist/brain/global-brain.d.ts.map +1 -1
  31. package/dist/brain/global-brain.js +1 -0
  32. package/dist/brain/global-brain.js.map +1 -1
  33. package/dist/brain/managed-project-skills.d.ts +48 -0
  34. package/dist/brain/managed-project-skills.d.ts.map +1 -0
  35. package/dist/brain/managed-project-skills.js +230 -0
  36. package/dist/brain/managed-project-skills.js.map +1 -0
  37. package/dist/brain/mcp-presence.d.ts +50 -0
  38. package/dist/brain/mcp-presence.d.ts.map +1 -0
  39. package/dist/brain/mcp-presence.js +214 -0
  40. package/dist/brain/mcp-presence.js.map +1 -0
  41. package/dist/brain/mcp-server.d.ts +12 -0
  42. package/dist/brain/mcp-server.d.ts.map +1 -1
  43. package/dist/brain/mcp-server.js +177 -31
  44. package/dist/brain/mcp-server.js.map +1 -1
  45. package/dist/brain/mcp-stdio.d.ts +1 -1
  46. package/dist/brain/mcp-stdio.d.ts.map +1 -1
  47. package/dist/brain/mcp-stdio.js +19 -1
  48. package/dist/brain/mcp-stdio.js.map +1 -1
  49. package/dist/brain/memory-annotations.d.ts +2 -0
  50. package/dist/brain/memory-annotations.d.ts.map +1 -1
  51. package/dist/brain/memory-annotations.js +13 -0
  52. package/dist/brain/memory-annotations.js.map +1 -1
  53. package/dist/brain/memory-inspection.d.ts +1 -0
  54. package/dist/brain/memory-inspection.d.ts.map +1 -1
  55. package/dist/brain/memory-inspection.js +1 -1
  56. package/dist/brain/memory-inspection.js.map +1 -1
  57. package/dist/brain/memory-mcp-content.d.ts +4 -2
  58. package/dist/brain/memory-mcp-content.d.ts.map +1 -1
  59. package/dist/brain/memory-mcp-content.js +20 -16
  60. package/dist/brain/memory-mcp-content.js.map +1 -1
  61. package/dist/brain/model-registry.d.ts +4 -5
  62. package/dist/brain/model-registry.d.ts.map +1 -1
  63. package/dist/brain/model-registry.js +22 -207
  64. package/dist/brain/model-registry.js.map +1 -1
  65. package/dist/brain/model-usage.d.ts +66 -0
  66. package/dist/brain/model-usage.d.ts.map +1 -0
  67. package/dist/brain/model-usage.js +246 -0
  68. package/dist/brain/model-usage.js.map +1 -0
  69. package/dist/brain/native-codex.d.ts +105 -0
  70. package/dist/brain/native-codex.d.ts.map +1 -0
  71. package/dist/brain/native-codex.js +292 -0
  72. package/dist/brain/native-codex.js.map +1 -0
  73. package/dist/brain/native-session-import.d.ts +56 -0
  74. package/dist/brain/native-session-import.d.ts.map +1 -0
  75. package/dist/brain/native-session-import.js +307 -0
  76. package/dist/brain/native-session-import.js.map +1 -0
  77. package/dist/brain/orchestrator.js +1 -1
  78. package/dist/brain/orchestrator.js.map +1 -1
  79. package/dist/brain/project-feedback.d.ts +1 -1
  80. package/dist/brain/project-feedback.d.ts.map +1 -1
  81. package/dist/brain/project-feedback.js +19 -8
  82. package/dist/brain/project-feedback.js.map +1 -1
  83. package/dist/brain/project-hub-contract.d.ts +6 -0
  84. package/dist/brain/project-hub-contract.d.ts.map +1 -1
  85. package/dist/brain/project-hub-contract.js +2 -1
  86. package/dist/brain/project-hub-contract.js.map +1 -1
  87. package/dist/brain/project-hub-mcp.d.ts +14 -0
  88. package/dist/brain/project-hub-mcp.d.ts.map +1 -1
  89. package/dist/brain/project-hub-mcp.js +6 -1
  90. package/dist/brain/project-hub-mcp.js.map +1 -1
  91. package/dist/brain/project-hub-store.d.ts +14 -2
  92. package/dist/brain/project-hub-store.d.ts.map +1 -1
  93. package/dist/brain/project-hub-store.js +70 -2
  94. package/dist/brain/project-hub-store.js.map +1 -1
  95. package/dist/brain/project-hub.d.ts +5 -0
  96. package/dist/brain/project-hub.d.ts.map +1 -1
  97. package/dist/brain/project-hub.js +23 -1
  98. package/dist/brain/project-hub.js.map +1 -1
  99. package/dist/brain/project-kit.d.ts +10 -0
  100. package/dist/brain/project-kit.d.ts.map +1 -1
  101. package/dist/brain/project-kit.js +80 -16
  102. package/dist/brain/project-kit.js.map +1 -1
  103. package/dist/brain/project-policy-delivery.d.ts +8 -0
  104. package/dist/brain/project-policy-delivery.d.ts.map +1 -0
  105. package/dist/brain/project-policy-delivery.js +38 -0
  106. package/dist/brain/project-policy-delivery.js.map +1 -0
  107. package/dist/brain/project-policy.d.ts +88 -0
  108. package/dist/brain/project-policy.d.ts.map +1 -0
  109. package/dist/brain/project-policy.js +267 -0
  110. package/dist/brain/project-policy.js.map +1 -0
  111. package/dist/brain/project-session-contract.d.ts +180 -0
  112. package/dist/brain/project-session-contract.d.ts.map +1 -0
  113. package/dist/brain/project-session-contract.js +50 -0
  114. package/dist/brain/project-session-contract.js.map +1 -0
  115. package/dist/brain/project-session-mcp.d.ts +145 -0
  116. package/dist/brain/project-session-mcp.d.ts.map +1 -0
  117. package/dist/brain/project-session-mcp.js +58 -0
  118. package/dist/brain/project-session-mcp.js.map +1 -0
  119. package/dist/brain/project-sessions.d.ts +97 -0
  120. package/dist/brain/project-sessions.d.ts.map +1 -0
  121. package/dist/brain/project-sessions.js +223 -0
  122. package/dist/brain/project-sessions.js.map +1 -0
  123. package/dist/brain/project-source-contract.d.ts +109 -0
  124. package/dist/brain/project-source-contract.d.ts.map +1 -0
  125. package/dist/brain/project-source-contract.js +2 -0
  126. package/dist/brain/project-source-contract.js.map +1 -0
  127. package/dist/brain/project-source-intake.d.ts +26 -0
  128. package/dist/brain/project-source-intake.d.ts.map +1 -0
  129. package/dist/brain/project-source-intake.js +457 -0
  130. package/dist/brain/project-source-intake.js.map +1 -0
  131. package/dist/cli.js +1 -1
  132. package/dist/cli.js.map +1 -1
  133. package/dist/context-cli.d.ts.map +1 -1
  134. package/dist/context-cli.js +9 -3
  135. package/dist/context-cli.js.map +1 -1
  136. package/dist/dashboard/brain-hub-actions.d.ts.map +1 -1
  137. package/dist/dashboard/brain-hub-actions.js +232 -135
  138. package/dist/dashboard/brain-hub-actions.js.map +1 -1
  139. package/dist/dashboard/brain-hub-agent-dock.d.ts +2 -0
  140. package/dist/dashboard/brain-hub-agent-dock.d.ts.map +1 -0
  141. package/dist/dashboard/brain-hub-agent-dock.js +255 -0
  142. package/dist/dashboard/brain-hub-agent-dock.js.map +1 -0
  143. package/dist/dashboard/brain-hub-brand-icons.d.ts +2 -0
  144. package/dist/dashboard/brain-hub-brand-icons.d.ts.map +1 -0
  145. package/dist/dashboard/brain-hub-brand-icons.js +9 -0
  146. package/dist/dashboard/brain-hub-brand-icons.js.map +1 -0
  147. package/dist/dashboard/brain-hub-client.d.ts.map +1 -1
  148. package/dist/dashboard/brain-hub-client.js +433 -333
  149. package/dist/dashboard/brain-hub-client.js.map +1 -1
  150. package/dist/dashboard/brain-hub-context.d.ts +2 -0
  151. package/dist/dashboard/brain-hub-context.d.ts.map +1 -0
  152. package/dist/dashboard/brain-hub-context.js +37 -0
  153. package/dist/dashboard/brain-hub-context.js.map +1 -0
  154. package/dist/dashboard/brain-hub-controls.d.ts +2 -0
  155. package/dist/dashboard/brain-hub-controls.d.ts.map +1 -0
  156. package/dist/dashboard/brain-hub-controls.js +63 -0
  157. package/dist/dashboard/brain-hub-controls.js.map +1 -0
  158. package/dist/dashboard/brain-hub-exchanges.d.ts +2 -0
  159. package/dist/dashboard/brain-hub-exchanges.d.ts.map +1 -0
  160. package/dist/dashboard/brain-hub-exchanges.js +82 -0
  161. package/dist/dashboard/brain-hub-exchanges.js.map +1 -0
  162. package/dist/dashboard/brain-hub-graph.d.ts.map +1 -1
  163. package/dist/dashboard/brain-hub-graph.js +236 -74
  164. package/dist/dashboard/brain-hub-graph.js.map +1 -1
  165. package/dist/dashboard/brain-hub-html.d.ts.map +1 -1
  166. package/dist/dashboard/brain-hub-html.js +56 -1
  167. package/dist/dashboard/brain-hub-html.js.map +1 -1
  168. package/dist/dashboard/brain-hub-knowledge.d.ts +2 -0
  169. package/dist/dashboard/brain-hub-knowledge.d.ts.map +1 -0
  170. package/dist/dashboard/brain-hub-knowledge.js +33 -0
  171. package/dist/dashboard/brain-hub-knowledge.js.map +1 -0
  172. package/dist/dashboard/brain-hub-live.d.ts +2 -0
  173. package/dist/dashboard/brain-hub-live.d.ts.map +1 -0
  174. package/dist/dashboard/brain-hub-live.js +151 -0
  175. package/dist/dashboard/brain-hub-live.js.map +1 -0
  176. package/dist/dashboard/brain-hub-managed-skills.d.ts +2 -0
  177. package/dist/dashboard/brain-hub-managed-skills.d.ts.map +1 -0
  178. package/dist/dashboard/brain-hub-managed-skills.js +20 -0
  179. package/dist/dashboard/brain-hub-managed-skills.js.map +1 -0
  180. package/dist/dashboard/brain-hub-native.d.ts +2 -0
  181. package/dist/dashboard/brain-hub-native.d.ts.map +1 -0
  182. package/dist/dashboard/brain-hub-native.js +44 -0
  183. package/dist/dashboard/brain-hub-native.js.map +1 -0
  184. package/dist/dashboard/brain-hub-project-views.d.ts.map +1 -1
  185. package/dist/dashboard/brain-hub-project-views.js +78 -77
  186. package/dist/dashboard/brain-hub-project-views.js.map +1 -1
  187. package/dist/dashboard/brain-hub-sessions.d.ts +2 -0
  188. package/dist/dashboard/brain-hub-sessions.d.ts.map +1 -0
  189. package/dist/dashboard/brain-hub-sessions.js +40 -0
  190. package/dist/dashboard/brain-hub-sessions.js.map +1 -0
  191. package/dist/dashboard/brain-hub-usage.d.ts +2 -0
  192. package/dist/dashboard/brain-hub-usage.d.ts.map +1 -0
  193. package/dist/dashboard/brain-hub-usage.js +28 -0
  194. package/dist/dashboard/brain-hub-usage.js.map +1 -0
  195. package/dist/dashboard/brain-hub-views.d.ts.map +1 -1
  196. package/dist/dashboard/brain-hub-views.js +151 -146
  197. package/dist/dashboard/brain-hub-views.js.map +1 -1
  198. package/dist/dashboard/live-workspace-html.js +1039 -1039
  199. package/dist/dashboard/managed-project-skills-api.d.ts +29 -0
  200. package/dist/dashboard/managed-project-skills-api.d.ts.map +1 -0
  201. package/dist/dashboard/managed-project-skills-api.js +28 -0
  202. package/dist/dashboard/managed-project-skills-api.js.map +1 -0
  203. package/dist/dashboard/native-codex-api.d.ts +83 -0
  204. package/dist/dashboard/native-codex-api.d.ts.map +1 -0
  205. package/dist/dashboard/native-codex-api.js +91 -0
  206. package/dist/dashboard/native-codex-api.js.map +1 -0
  207. package/dist/dashboard/project-file-watch.d.ts +46 -0
  208. package/dist/dashboard/project-file-watch.d.ts.map +1 -0
  209. package/dist/dashboard/project-file-watch.js +221 -0
  210. package/dist/dashboard/project-file-watch.js.map +1 -0
  211. package/dist/dashboard/project-live-stream.d.ts +33 -0
  212. package/dist/dashboard/project-live-stream.d.ts.map +1 -0
  213. package/dist/dashboard/project-live-stream.js +233 -0
  214. package/dist/dashboard/project-live-stream.js.map +1 -0
  215. package/dist/dashboard/project-policy-api.d.ts +37 -0
  216. package/dist/dashboard/project-policy-api.d.ts.map +1 -0
  217. package/dist/dashboard/project-policy-api.js +31 -0
  218. package/dist/dashboard/project-policy-api.js.map +1 -0
  219. package/dist/dashboard/project-session-api.d.ts +28 -0
  220. package/dist/dashboard/project-session-api.d.ts.map +1 -0
  221. package/dist/dashboard/project-session-api.js +84 -0
  222. package/dist/dashboard/project-session-api.js.map +1 -0
  223. package/dist/dashboard/project-source-api.d.ts +11 -0
  224. package/dist/dashboard/project-source-api.d.ts.map +1 -0
  225. package/dist/dashboard/project-source-api.js +67 -0
  226. package/dist/dashboard/project-source-api.js.map +1 -0
  227. package/dist/dashboard/project-workspace.d.ts.map +1 -1
  228. package/dist/dashboard/project-workspace.js +18 -0
  229. package/dist/dashboard/project-workspace.js.map +1 -1
  230. package/dist/dashboard/server.d.ts.map +1 -1
  231. package/dist/dashboard/server.js +64 -25
  232. package/dist/dashboard/server.js.map +1 -1
  233. package/dist/dashboard/v6-dashboard-html.d.ts +1 -1
  234. package/dist/dashboard/v6-dashboard-html.d.ts.map +1 -1
  235. package/dist/dashboard/v6-dashboard-html.js +4261 -4309
  236. package/dist/dashboard/v6-dashboard-html.js.map +1 -1
  237. package/dist/dashboard/workspace-api.d.ts.map +1 -1
  238. package/dist/dashboard/workspace-api.js +105 -23
  239. package/dist/dashboard/workspace-api.js.map +1 -1
  240. package/dist/dashboard/workspace-status.d.ts +3 -0
  241. package/dist/dashboard/workspace-status.d.ts.map +1 -1
  242. package/dist/dashboard/workspace-status.js +4 -3
  243. package/dist/dashboard/workspace-status.js.map +1 -1
  244. package/dist/project-cli.js +1 -1
  245. package/dist/project-cli.js.map +1 -1
  246. package/docs/BRAND_ASSETS.md +36 -0
  247. package/docs/FEATURES_AND_LIMITS.md +29 -24
  248. package/docs/INTEGRATIONS.md +4 -2
  249. package/docs/LEARNING.md +1 -1
  250. package/docs/PROJECT_KIT.md +6 -1
  251. package/docs/QUICKSTART.md +62 -44
  252. package/docs/README.md +36 -33
  253. package/docs/SESSIONS.md +77 -0
  254. package/docs/SOURCE_INTAKE.md +33 -0
  255. package/docs/STATS.md +21 -21
  256. package/docs/VISUAL_GUIDE_v7.22.0.md +73 -0
  257. package/docs/WORKFLOW_INTEGRATION.md +3 -1
  258. package/docs/WORKSPACE.md +63 -37
  259. package/docs/assets/7.22.0-agent-dock-mobile.png +0 -0
  260. package/docs/assets/7.22.0-agent-dock.gif +0 -0
  261. package/docs/assets/7.22.0-agent-dock.png +0 -0
  262. package/docs/assets/7.22.0-hub-exchange.png +0 -0
  263. package/docs/assets/7.22.0-hub-exchanges.gif +0 -0
  264. package/docs/assets/7.22.0-hub-graph.png +0 -0
  265. package/docs/assets/7.22.0-hub-mobile-empty.png +0 -0
  266. package/docs/assets/7.22.0-managed-skills.png +0 -0
  267. package/docs/assets/7.22.0-plain-evidence.png +0 -0
  268. package/docs/assets/7.22.0-project-controls.png +0 -0
  269. package/docs/assets/7.22.0-sessions.png +0 -0
  270. package/docs/assets/7.22.0-teach-brain.png +0 -0
  271. package/docs/launch/RELEASE_ACCEPTANCE_v7.22.0.md +48 -0
  272. package/docs/launch/RELEASE_NOTES_v7.22.0.md +53 -0
  273. package/docs/launch/RESEARCH_AND_PLAN_v7.22.0.md +672 -0
  274. package/package.json +135 -130
  275. package/skills/shadow-workflow/SKILL.md +2 -0
@@ -0,0 +1,40 @@
1
+ import { BRAIN_HUB_NATIVE } from './brain-hub-native.js';
2
+ export const BRAIN_HUB_SESSIONS = String.raw `
3
+ ${BRAIN_HUB_NATIVE}
4
+ async function refreshSessions(){
5
+ state.sessions=await api('sessions');
6
+ if(state.session)state.session=await api('sessions/'+state.session.session.id+(state.sessionBefore?'?before='+encodeURIComponent(state.sessionBefore):''));
7
+ }
8
+ function sessionPresence(session){return session.presence==='completed'?'Completed':session.presence==='stale'?'Stale report':session.reportedStatus==='waiting'?'Recently reported waiting':'Recently reported working';}
9
+ function sessionHandoff(checkpoint){
10
+ return '<div class="stack"><p class="preserve">'+esc(checkpoint.summary)+'</p>'+[['decisions','Decisions'],['completed','Completed work'],['nextSteps','Next steps'],['evidence','Evidence references']].map(([key,label])=>arr(checkpoint[key]).length?'<h3>'+label+'</h3>'+list(checkpoint[key].map(item=>'<li class="item">'+esc(item)+'</li>').join('')):'').join('')+'<details><summary>Immutable checkpoint fingerprint</summary><pre>'+esc(checkpoint.sha256)+'</pre></details></div>';
11
+ }
12
+ function sessionRoster(sessions){
13
+ const groups=new Map();for(const session of sessions){const tool=session.native?.tool??session.actor.tool;groups.set(tool,[...groups.get(tool)??[],session]);}
14
+ return '<div class="table-wrap"><table><thead><tr><th>Reporting tool</th><th>Recently working</th><th>Waiting / stale</th><th>Completed</th><th>Worker sessions</th></tr></thead><tbody>'+[...groups].map(([tool,rows])=>'<tr><td>'+esc(hostName(tool))+'</td><td>'+rows.filter(s=>s.presence==='recent'&&s.reportedStatus==='working').length+'</td><td>'+rows.filter(s=>s.presence!=='completed'&&(s.reportedStatus==='waiting'||s.presence==='stale')).length+'</td><td>'+rows.filter(s=>s.presence==='completed').length+'</td><td>'+rows.filter(s=>s.parentSessionId&&!s.resumedFrom).length+'</td></tr>').join('')+'</tbody></table></div><p class="panel-body small muted">Counts describe registered sessions in this workspace. Recent means a report arrived within '+Math.round((state.sessions?.limits?.recentMs??300000)/60000)+' minutes, not continuous proof of execution. A resumed session is separate from a worker session.</p>';
15
+ }
16
+ function sessionsPage(){
17
+ if(!local)return projectRequired('Sessions and handoffs','Keep project conversations and resumable checkpoints together.');
18
+ const sessions=arr(state.sessions?.sessions),detail=state.session,current=detail?.session;
19
+ const queue=error('sessions','sessions')||(sessions.length?list(sessions.slice().reverse().map(session=>'<li><button class="entry" type="button" data-session="'+esc(session.id)+'" aria-pressed="'+(session.id===current?.id)+'"><strong>'+esc(session.title)+'</strong><small>'+esc(hostName(session.native?.tool??session.actor.tool))+' · '+esc(sessionPresence(session))+'</small><small class="muted">Last report '+esc(isoTime(session.lastReportedAt))+'</small></button></li>').join('')):empty('No registered conversations','Connected hosts can register a session and save a checkpoint. You can also begin a manual project conversation below.'));
20
+ const create='<div class="panel-body"><form class="form" data-form="session-open"><label>Conversation title<input name="title" maxlength="160" required></label><label>What are you trying to achieve?<textarea name="goal" rows="2" maxlength="1200" required></textarea></label>'+submit('Start portal session')+'</form><p class="small muted">This creates a portal-owned record. It does not launch an AI model.</p></div>';
21
+ let body=empty('Choose a conversation','Open a recent or completed session to inspect messages and handoffs.');
22
+ if(current){
23
+ const reportingTool=current.native?.tool??current.actor.tool;
24
+ const matched=arr(state.usage?.receipts).filter(receipt=>receipt.projectId===state.project&&current.hostSessionId&&receipt.sessionId===current.hostSessionId&&receipt.agent===reportingTool);
25
+ const controls=arr(state.policy?.chats).find(chat=>chat.id===current.id);
26
+ const latestModel=matched.at(-1);
27
+ const messages='<p class="small muted">Showing '+arr(detail.messages).length+' of '+fmt(detail.messageCount??arr(detail.messages).length)+' retained messages.</p><div class="actions">'+(detail.nextBefore?action('session-older','Earlier messages','','data-before="'+esc(detail.nextBefore)+'"'):'')+(state.sessionBefore?action('session-latest','Latest messages'):'')+'</div>'+list(arr(detail.messages).map(message=>'<li class="item"><div class="row"><strong>'+esc(message.source?'Imported '+message.source.originalRole:message.role)+'</strong><small class="muted">'+esc(isoTime(message.createdAt))+'</small></div><p class="preserve">'+esc(message.text)+'</p><small class="mono muted">'+esc(message.source?message.source.sourceId+' · '+message.source.locator:hostName(message.actor.tool))+'</small></li>').join(''));
28
+ const note=current.reportedStatus!=='completed'?'<form class="form" data-form="session-note"><label>Message or work note<textarea name="text" maxlength="8000" rows="3" required></textarea></label>'+submit('Record note')+'</form>':'';
29
+ const checkpoint='<details><summary>Save a handoff for another agent</summary><form class="form" data-form="session-checkpoint"><label>Summary of the work<textarea name="summary" maxlength="4000" rows="3" required></textarea></label>'+[['decisions','Decisions'],['completed','Completed work'],['nextSteps','Next steps'],['evidence','Evidence references']].map(([name,label])=>'<label>'+label+' · one per line<textarea name="'+name+'" rows="2" maxlength="4800"></textarea></label>').join('')+submit('Save immutable checkpoint')+'</form></details>';
30
+ const permits=!!controls&&controls.shareContext&&!controls.inheritedBlocked&&!state.errors.policy;
31
+ const checkpoints=list(arr(detail.checkpoints).slice().reverse().map(cp=>'<li class="item"><div class="row"><strong>Checkpoint · revision '+esc(cp.revision)+'</strong><small>'+esc(isoTime(cp.createdAt))+'</small></div>'+(state.sessionCheckpoint?.id===cp.id?sessionHandoff(state.sessionCheckpoint):action('session-checkpoint-open','Read checkpoint','','data-checkpoint="'+esc(cp.id)+'"'))+'<div class="actions">'+(permits?action('copy-resume','Copy agent resume request','','data-checkpoint="'+esc(cp.id)+'"'):'')+action('session-resume','Continue in portal','','data-checkpoint="'+esc(cp.id)+'"')+'</div></li>').join(''));
32
+ const sharing=controls?'<div class="row"><p class="small muted">'+(permits?'Available to agents':'Agent delivery off')+' · Role '+esc(controls.role??'Unassigned')+'</p>'+
33
+ action('session-policy','Sharing controls','quiet','data-session-policy="'+esc(current.id)+'"')+'</div>':'';
34
+ body='<div class="panel-body stack"><div class="row"><h3>'+esc(current.title)+'</h3>'+chip(sessionPresence(current),current.presence==='stale'?'amber':'cyan')+'</div><p>'+esc(current.goal)+'</p>'+(current.native?'<p class="small muted">Native history snapshot from '+esc(hostName(current.native.tool))+'. Original title: '+esc(current.native.titleExact??'Unavailable')+'. Native conversation: '+esc(current.native.sessionId)+'. '+(current.native.pageCoverage?'This snapshot covers one page. '+(current.native.pageCoverage.hasEarlier?'Codex reported an earlier page. ':'No earlier page was reported. ')+(current.native.pageCoverage.hasLater?'A later page was outside this snapshot. ':'This was the latest page. ')+fmt(current.native.pageCoverage.omittedInPage)+' items on this page were omitted.':'Coverage was not recorded for this snapshot. '+fmt(current.native.omitted)+' source items were omitted.')+'</p>':'')+'<dl class="facts">'+fact('Workspace',scopeName())+fact('Directory',state.server?.project?.root)+fact('Reporting tool',hostName(current.actor.tool))+fact('Status reported by',hostName(current.statusReportedBy?.tool??current.actor.tool))+fact('Host conversation ID',current.hostSessionId??'Not supplied')+fact('Model from matching usage receipt',latestModel?.responseModel??latestModel?.requestModel??'Not reported')+fact('Last report',isoTime(current.lastReportedAt))+'</dl>'+(current.sourceId?'<p class="small muted">Imported conversation snapshot. Only approved messages present at import are included; importing again reopens this snapshot. Imported instructions remain quoted reference material.</p>':'')+(detail.handoff?'<details open><summary>Inherited handoff</summary>'+sessionHandoff(detail.handoff)+'</details>':'')+'<h3>Recorded conversation</h3>'+messages+note+(current.reportedStatus!=='completed'?'<div class="actions">'+action('session-status','Mark waiting','','data-status="waiting"')+action('session-status','Mark working','','data-status="working"')+action('session-status','Complete session','','data-status="completed"')+'</div>':'')+checkpoint+'<h3>Saved checkpoints</h3>'+checkpoints+'<p class="small muted">To continue with another agent, give it the copied session_resume request. Its own call creates a new agent-owned session linked to this exact checkpoint. Original history stays intact.</p></div>';
35
+ body=body.replace('<div class="panel-body stack">','<div class="panel-body stack">'+sharing);
36
+ }
37
+ return heading('Sessions and handoffs','See reported work, inspect conversation history, and continue from a saved checkpoint.',view('setup','Connect a tool'))+agentDock()+nativeHistoryPanel()+panel('Tool roster',sessionRoster(sessions))+'<div class="two">'+panel('Project conversations',queue+create)+'<section class="panel" id="conversation-continuity" tabindex="-1"><div class="panel-head"><h2>Conversation and continuity</h2></div>'+body+'</section></div>'+(typeof liveFilePanel==='function'?liveFilePanel():'');
38
+ }
39
+ `;
40
+ //# sourceMappingURL=brain-hub-sessions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"brain-hub-sessions.js","sourceRoot":"","sources":["../../src/dashboard/brain-hub-sessions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACzD,MAAM,CAAC,MAAM,kBAAkB,GAAG,MAAM,CAAC,GAAG,CAAA;EAC1C,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAoCjB,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare const BRAIN_HUB_USAGE: string;
2
+ //# sourceMappingURL=brain-hub-usage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"brain-hub-usage.d.ts","sourceRoot":"","sources":["../../src/dashboard/brain-hub-usage.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,eAAe,QA0B3B,CAAC"}
@@ -0,0 +1,28 @@
1
+ export const BRAIN_HUB_USAGE = String.raw `
2
+ function usageValue(stat){return stat?.total===null||!stat?'Not reported':fmt(stat.total)+' · '+stat.known+'/'+stat.receipts+' receipts';}
3
+ function modelReceipts(){
4
+ const usage=state.usage;
5
+ if(state.errors.usage)return error('usage','model usage');
6
+ if(!local)return empty('Project workspace required','Model receipts belong to an explicit project.');
7
+ if(!usage)return empty('Loading usage receipts','Reading project-local host reports.');
8
+ if(!arr(usage.receipts).length)return empty('No model usage reported yet','A connected host can call usage_report with the model and token counts it already knows. Installing a recipe alone does not supply this data.');
9
+ return '<div class="table-wrap"><table><thead><tr><th>Reporting host</th><th>Provider / requested model</th><th>Response model</th><th>Input</th><th>Output</th><th>Last report</th></tr></thead><tbody>'+arr(usage.models).map(row=>'<tr><td>'+esc(hostName(row.agent))+'</td><td>'+esc(row.provider??'Unknown provider')+'<br>'+esc(row.requestModel??'Not reported')+'</td><td>'+esc(row.responseModel??'Not reported')+'</td><td>'+esc(usageValue(row.totals.inputTokens))+'</td><td>'+esc(usageValue(row.totals.outputTokens))+'</td><td>'+esc(isoTime(row.lastAt))+'</td></tr>').join('')+'</tbody></table></div><p class="panel-body small muted">Host-reported metadata. A reported model name is not independent proof of which upstream model a proxy used.</p>';
10
+ }
11
+ function modelsPage(){
12
+ const hosts=arr(state.integrations?.agents);
13
+ const body=hosts.length?'<div class="table-wrap"><table><thead><tr><th>Client recipe</th><th>Project skill files</th><th>Retained exchanges</th><th>Usage reports</th></tr></thead><tbody>'+hosts.map(host=>{
14
+ const events=state.events.filter(event=>event.agent===host.id),latest=events.at(-1),reports=arr(state.usage?.receipts).filter(receipt=>receipt.agent===host.id);
15
+ return '<tr><td><strong>'+esc(host.label)+'</strong></td><td>'+esc(host.installation)+'</td><td>'+esc(latest?evidence(latest)+' · '+eventLabel(latest)+' · '+isoTime(latest.timestamp):state.live?'Not observed for this label':'Activity display is off')+'</td><td>'+esc(reports.length?reports.length+' retained':'Not reported')+'</td></tr>';
16
+ }).join('')+'</tbody></table></div><p class="panel-body small muted">Recipe, skill files, observed calls and usage receipts are separate checks. Host labels must match for these rows; all reporting labels appear below. These records do not assert a persistent live connection.</p>':error('integrations','client checks')||empty('Reading integration evidence','Client skill-file checks are loading.');
17
+ return heading('Connections and models','See what is installed, what exchanged data, and what each host reported.',view('setup','Configure hosts'))+panel('Client readiness',body)+panel('Reported models',modelReceipts(),chip('Host-reported','amber'));
18
+ }
19
+ function usagePage(){
20
+ const measured=state.events.filter(event=>event.source==='measured'&&event.kind==='tool-end');
21
+ const durations=measured.map(event=>event.durationMs).filter(Number.isFinite);
22
+ const usage=state.usage,totals=usage?.totals;
23
+ const usageBody=totals?(state.errors.usage?'<div class="panel-body"><p class="amber" role="status">Usage refresh failed. Showing the last readable snapshot.</p><p class="small muted">'+esc(state.errors.usage)+'</p></div>':'')+'<div class="panel-body stack"><dl class="facts">'+fact('Input tokens',usageValue(totals.inputTokens))+fact('Output tokens',usageValue(totals.outputTokens))+fact('Cache read · part of input',usageValue(totals.cacheReadInputTokens))+fact('Cache write · part of input',usageValue(totals.cacheWriteInputTokens))+fact('Reasoning · part of output',usageValue(totals.reasoningOutputTokens))+'</dl><p class="small muted">Cache and reasoning counts are subsets. They are never added again to input and output. Unknown counts stay unknown; coverage shows how many receipts supplied each figure.</p><dl class="facts">'+fact('Retained receipts',arr(usage.receipts).length+' / '+usage.retentionLimit)+fact('Older receipts removed',usage.dropped)+fact('Coverage starts',isoTime(usage.oldestAt))+fact('Latest receipt',isoTime(usage.newestAt))+fact('Recording',usage.recording?'On':'Off')+'</dl><p class="small muted">These are totals for retained receipts, not lifetime billing. Dollar cost and provider savings are not calculated.</p></div>':error('usage','usage receipts')||empty('No usage evidence','Usage arrives when a host explicitly sends a report.');
24
+ const context=state.context;
25
+ return heading('Usage and timing','Reported model tokens, measured tool timing, and estimated context size are kept separate.')+'<div class="two">'+panel('Reported model tokens',usageBody,chip('Host-reported','amber'))+panel('Tool timing and context','<div class="panel-body stack"><dl class="facts">'+fact('Measured tool returns',state.live?fmt(measured.length):undefined)+fact('Measured tool duration',durations.length?fmt(durations.reduce((sum,item)=>sum+item,0))+' ms':undefined)+fact('Timing coverage',durations.length+' / '+measured.length)+fact('Last context estimate',context?fmt(context.budget.estimatedTokens)+' tokens':'Build a context pack')+fact('Exact context characters',context?fmt(context.budget.usedChars):undefined)+'</dl><p class="small muted">Tool time is not model inference time. Context estimates describe the returned pack; they do not prove billed token savings.</p>'+view('context','Inspect context selection')+'</div>')+'</div>'+panel('Model breakdown',modelReceipts());
26
+ }
27
+ `;
28
+ //# sourceMappingURL=brain-hub-usage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"brain-hub-usage.js","sourceRoot":"","sources":["../../src/dashboard/brain-hub-usage.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,eAAe,GAAG,MAAM,CAAC,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;CA0BxC,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"brain-hub-views.d.ts","sourceRoot":"","sources":["../../src/dashboard/brain-hub-views.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,eAAe,QAkJmB,CAAC"}
1
+ {"version":3,"file":"brain-hub-views.d.ts","sourceRoot":"","sources":["../../src/dashboard/brain-hub-views.ts"],"names":[],"mappings":"AASA,eAAO,MAAM,eAAe,QAiJmB,CAAC"}
@@ -1,150 +1,155 @@
1
+ import { BRAIN_HUB_SESSIONS } from './brain-hub-sessions.js';
2
+ import { BRAIN_HUB_AGENT_DOCK } from './brain-hub-agent-dock.js';
3
+ import { BRAIN_HUB_CONTROLS } from './brain-hub-controls.js';
4
+ import { BRAIN_HUB_MANAGED_SKILLS } from './brain-hub-managed-skills.js';
5
+ import { BRAIN_HUB_CONTEXT } from './brain-hub-context.js';
6
+ import { BRAIN_HUB_USAGE } from './brain-hub-usage.js';
1
7
  import { BRAIN_HUB_PROJECT_VIEWS } from './brain-hub-project-views.js';
2
8
  import { BRAIN_HUB_ACTIONS } from './brain-hub-actions.js';
3
- export const BRAIN_HUB_VIEWS = String.raw `
4
- function eventLabel(event){
5
- if(event.tool)return event.tool+' · '+(event.outcome??(event.kind==='tool-start'?'started':'returned'));
6
- if(event.phase)return 'Phase · '+event.phase;
7
- return event.kind.replaceAll('-',' ');
8
- }
9
- function eventTone(event){return event.source==='measured'?'cyan':'amber';}
10
- function evidence(event){return event.source==='measured'?'Measured '+(event.transport==='mcp'?'MCP':'tool'):'Reported';}
11
- function timelineMarkup(trace=false){
12
- const events=state.events;
13
- if(!events.length)return empty(state.errors.telemetry?'Activity unavailable':state.live?'No recorded activity':'Activity display is off',state.errors.telemetry||
14
- (state.live?'A tool exchange or reported phase will appear here when activity recording is enabled.':'Enable live dashboard activity in Setup.'),view('setup','Open setup'));
15
- return list([...events].reverse().map(event=>'<li><button class="entry event" type="button" data-event="'+esc(event.id)+'" aria-pressed="'+(state.replayId===event.id)+'"><time class="mono">'+esc(time(event.timestamp))+'</time><span>'+esc(eventLabel(event))+' <span class="muted">· '+esc(hostName(event.agent))+'</span></span>'+chip(evidence(event),eventTone(event))+'</button></li>').join(''),trace?'aria-label="Recorded tool trace"':'aria-label="Recorded activity"');
16
- }
17
- function trace(values){
18
- if(values.length<2)return '';
19
- const max=Math.max(...values,1);
20
- const points=values.map((value,index)=>(2+index*62/(values.length-1))+','+(22-value*19/max));
21
- return '<svg class="metric-trace cyan" viewBox="0 0 66 26" aria-hidden="true"><path d="M'+points.join(' L')+'" fill="none" stroke="currentColor" stroke-width="1.25"/></svg>';
22
- }
23
- function telemetryStrip(){
24
- const measured=state.events.filter(event=>event.source==='measured'&&event.kind==='tool-end');
25
- const durations=measured.map(event=>event.durationMs).filter(Number.isFinite);
26
- const observed=new Set(state.events.filter(event=>event.agent&&event.agent!=='user'&&event.agent!=='portal').map(event=>event.agent));
27
- const memories=state.graph?.totalMemories??state.overview?.quality?.total;
28
- const pack=state.context?.budget;
29
- return '<dl class="strip"><div class="metric"><dt>Stored memories in scope</dt><dd class="mono cyan">'+fmt(memories)+'</dd><small>'+esc(scopeName())+'</small></div><div class="metric">'+trace(durations.slice(-16))+'<dt>Measured tool returns</dt><dd class="mono">'+(state.live?fmt(measured.length):'Unavailable')+'</dd><small>'+esc(state.errors.telemetry?'Activity unavailable':state.live?fmt(observed.size)+' client labels in retained activity':'Activity display off')+'</small></div><div class="metric"><dt>Model input and output</dt><dd class="mono">Unavailable</dd><small>Model usage receipts are unavailable</small></div><div class="metric"><dt>Latest context pack estimate</dt><dd class="mono pink">'+(pack?fmt(pack.estimatedTokens)+' / '+fmt(pack.tokenBudget):'No pack built')+'</dd><small>'+esc(pack?arr(state.context.entries).length+' selected memories':'Assemble a scoped pack in Context')+'</small></div></dl>';
30
- }
31
- function rail(){
32
- const graph=arr(state.graph?.nodes);
33
- const projects=arr(state.overview?.projects).filter(project=>!state.project||project.id===state.project);
34
- const memories=graph.filter(node=>node.kind==='memory').slice(0,6);
35
- const projectsMarkup=projects.length?list(projects.slice(0,7).map(project=>'<li><button type="button" class="rail-entry" data-project="'+esc(project.id)+'" aria-pressed="'+(state.project===project.id)+'"><strong>'+esc(project.name)+'</strong><small>'+fmt(project.memories)+' memories</small></button></li>').join('')):empty('No projects with memories','Open a project workspace to add project-local knowledge.',view('setup','See setup'));
36
- return '<aside class="panel rail" aria-label="Projects and recent records"><div class="rail-title row"><h2>Projects</h2>'+chip(projects.length)+'</div>'+projectsMarkup+'<div class="rail-title row"><h2>Recent records</h2>'+chip(memories.length)+'</div>'+(memories.length?list(memories.map(node=>'<li><button type="button" class="rail-entry" data-node="'+esc(node.id)+'"><strong>'+esc(node.label)+'</strong><small>'+esc(node.category)+' · '+esc(hostName(node.agent))+'</small></button></li>').join('')):empty('No memory records','Add project knowledge or connect a host.',view('knowledge','Open knowledge')) )+'</aside>';
37
- }
38
- function graphMarkup(){
39
- const nodes=arr(state.graph?.nodes),nodeIds=new Set(nodes.map(node=>node.id));
40
- const edges=arr(state.graph?.edges).filter(edge=>nodeIds.has(edge.source)&&nodeIds.has(edge.target));
41
- const traceMode=state.graphMode==='trace';
42
- const listNodes=list(nodes.map(node=>'<li><button class="entry" type="button" data-node="'+esc(node.id)+'" aria-pressed="'+(state.focus&&state.picked===node.id)+'"><strong>'+esc(node.kind==='agent'?hostName(node.label):node.label)+'</strong><small class="muted">'+esc(node.kind+(node.category?' · '+node.category:''))+'</small></button></li>').join(''),'id="node-list" aria-label="Stored graph nodes" '+(state.listing&&!traceMode?'':'hidden'),'node-list');
43
- return '<section class="panel graph-panel"><div class="panel-head"><div><h2>'+(traceMode?'Recorded trace':'Memory graph')+'</h2><span class="small mono muted">'+fmt(nodes.length)+' records / '+fmt(edges.length)+' relationships'+(state.graph?.truncated?' · limited view':'')+'</span></div><div class="actions">'+action('clear-focus','Clear focus','quiet','id="clear-focus" '+(state.focus?'':'hidden'))+action('graph','Graph','','aria-pressed="'+!traceMode+'"')+action('trace','Trace','','aria-pressed="'+traceMode+'"')+action('node-list','Node list','','aria-expanded="'+state.listing+'" aria-controls="node-list" '+(traceMode||!nodes.length?'hidden':''))+'</div></div>'+
44
- '<div class="graph-stage" id="brain-stage"><canvas id="brain-canvas" aria-hidden="true" '+(traceMode?'hidden':'')+'></canvas><div class="graph-meta" aria-hidden="true"><span>PROJECT MEMORY</span><span>RECORDED AUTHORS</span></div>'+
45
- (!nodes.length&&!traceMode?'<div class="graph-empty"><strong>'+esc(state.errors.graph?'Memory graph unavailable':state.graph?'No stored graph records':'Loading memory graph')+'</strong><p class="small muted">'+esc(state.errors.graph??(state.graph?'Save knowledge or use a connected host to create your first record.':'Reading the selected workspace.'))+'</p><div class="actions">'+(state.errors.graph?action('refresh','Retry'):view('knowledge','Open knowledge')+view('setup','Connect a host'))+'</div></div>':'')+
46
- listNodes+'<div id="trace-list" class="trace-list" '+(traceMode?'':'hidden')+'>'+(traceMode?timelineMarkup(true):'')+'</div><div id="node-caption" class="focus-label" hidden></div></div>'+
47
- '<div class="graph-foot"><div class="legend"><span class="cyan"><i></i> Project relation</span><span class="pink"><i></i> Recorded by</span><span class="amber"><i></i> Typed relation</span></div><span class="mono" id="graph-event"></span></div></section>';
48
- }
49
- function inspectorBody(){
50
- const item=arr(state.graph?.nodes).find(node=>node.id===state.picked);
51
- const memory=state.memory?.memory;
52
- const event=displayEvent();
53
- const tabs=[['record','Record'],['sources','Sources'],['relations','Links'],['event','Event']].map(([id,label])=>btn(label,'data-inspect="'+id+'" aria-pressed="'+(state.inspector===id)+'"')).join('');
54
- let details;
55
- if(state.inspector==='event'){
56
- details=event?'<div class="panel-body stack"><div class="row"><h3>'+esc(eventLabel(event))+'</h3>'+chip(evidence(event),eventTone(event))+'</div><dl class="facts">'+fact('Host',hostName(event.agent))+fact('Time',isoTime(event.timestamp))+fact('Transport',event.transport)+fact('Session',event.sessionId)+fact('Outcome',event.outcome)+fact('Duration',Number.isFinite(event.durationMs)?event.durationMs+' ms':undefined)+'</dl>'+action('copy-event','Copy event receipt')+'</div>':empty('No activity selected','Select a recorded journal event to inspect its source and outcome.');
57
- }else if(!item){
58
- details=empty('Select a graph record','Choose a node, a recent record, or a journal event to inspect its evidence.',state.events.length?action('inspect-latest','Inspect latest event'):'');
59
- }else if(state.inspector==='sources'){
60
- const sources=arr(memory?.sources??item.sources);
61
- details='<div class="panel-body stack"><h3>'+esc(item.label)+'</h3>'+(sources.length?list(sources.map(source=>'<li class="item mono small">'+esc(source)+'</li>').join('')):empty('No source attached','This record has no persisted source reference.'))+'</div>';
62
- }else if(state.inspector==='relations'){
63
- const edges=arr(state.graph?.edges).filter(edge=>edge.source===item.id||edge.target===item.id);
64
- details='<div class="panel-body stack"><h3>'+esc(item.label)+'</h3>'+(edges.length?list(edges.map(edge=>{
65
- const id=edge.source===item.id?edge.target:edge.source;
66
- const node=arr(state.graph?.nodes).find(point=>point.id===id);
67
- return '<li><button type="button" class="entry" data-node="'+esc(id)+'"><strong>'+esc(node?.label??id)+'</strong><small>'+esc(edge.kind)+'</small></button></li>';
68
- }).join('')):empty('No graph relationships','There are no visible relationships for this record.'))+'</div>';
69
- }else{
70
- details='<div class="panel-body stack"><div class="row"><h3>'+esc(item.kind==='agent'?hostName(item.label):item.label)+'</h3>'+chip(item.kind)+'</div><dl class="facts">'+fact('Project',item.project)+fact('Recorded by',item.agent&&hostName(item.agent))+fact('State',memory?.effectiveState??item.effectiveState)+fact('Updated',item.updatedAt&&isoTime(item.updatedAt))+'</dl>'+(item.kind==='memory'?'<pre>'+esc(memory?.content??item.snippet)+'</pre>'+action('copy-memory','Copy record'):'')+'</div>';
71
- }
72
- return '<div class="panel-head"><h2>Record inspector</h2>'+(item?chip(item.category??item.kind):'')+'</div><div class="tabs" aria-label="Inspector tabs">'+tabs+'</div>'+details;
73
- }
74
- function renderInspector(){const target=$('inspector');if(target)target.innerHTML=inspectorBody();syncGraphControls();}
75
- function overviewPage(){
76
- const event=displayEvent();
77
- const pack=state.context?.budget;
78
- const compare=state.comparison;
79
- const contextBody=pack?'<div class="panel-body stack"><div class="row"><span class="muted">Estimated pack tokens</span><strong class="mono">'+fmt(pack.estimatedTokens)+' / '+fmt(pack.tokenBudget)+'</strong></div><div class="meter"><i style="width:'+Math.min(100,Math.round(100*pack.estimatedTokens/Math.max(1,pack.tokenBudget)))+'%"></i></div>'+(compare?'<p class="small muted">Legacy '+fmt(compare.legacy.budget.estimatedTokens)+' · Adaptive '+fmt(compare.adaptive.budget.estimatedTokens)+' estimated tokens</p>':'')+view('context','Open context pack')+'</div>':empty('No context pack for this session','Assemble a pack to inspect estimated tokens and selection reasons.',view('context','Build context pack'));
80
- return heading('Brain control room','Explore stored memories, inspect tool exchanges, and connect your project.',action('pause',state.motionPaused?'Resume motion':'Pause motion','','id="pause-motion" '+(reduced.matches?'disabled':''))+view('setup','Connection setup'))+
81
- telemetryStrip()+'<div class="control-grid">'+rail()+graphMarkup()+'<section class="panel inspect" id="inspector">'+inspectorBody()+'</section></div><div class="lower">'+panel('Context efficiency',contextBody,chip('Estimates','pink'))+
82
- panel('Activity journal','<div class="replay"><label for="replay" class="small muted">Seek record</label><input id="replay" type="range" min="0" max="'+Math.max(0,state.events.length-1)+'" value="'+Math.max(0,state.events.indexOf(event))+'" '+(state.events.length?'':'disabled')+'><output class="mono small" id="event-time">'+esc(event?time(event.timestamp):'—')+'</output>'+action('live','Latest','','id="return-live" '+(state.replayId?'':'hidden'))+'</div><div class="timeline" id="timeline">'+timelineMarkup()+'</div>', '<span id="journal-state">'+chip(state.replayId?'Replay':state.live?'Recorded activity':'Activity unavailable',state.replayId?'amber':'cyan')+'</span>')+'</div>';
83
- }
84
- function updateOverviewLive(){
85
- const timeline=$('timeline'),range=$('replay');
86
- if(!timeline||!range)return;
87
- const event=displayEvent();
88
- const markup=timelineMarkup(),previous=timeline.querySelector('[data-event]:focus')?.dataset.event;
89
- if(timeline.dataset.markup!==markup){
90
- timeline.innerHTML=markup;timeline.dataset.markup=markup;
91
- if(previous)[...timeline.querySelectorAll('[data-event]')].find(node=>node.dataset.event===previous)?.focus({preventScroll:true});
92
- }
93
- const traceList=$('trace-list');
94
- if(traceList&&!traceList.hidden){
95
- const trace=timelineMarkup(true),previous=traceList.querySelector('[data-event]:focus')?.dataset.event;
96
- if(traceList.dataset.markup!==trace){
97
- traceList.innerHTML=trace;traceList.dataset.markup=trace;
98
- if(previous)[...traceList.querySelectorAll('[data-event]')].find(node=>node.dataset.event===previous)?.focus({preventScroll:true});
99
- }
100
- }
101
- range.max=String(Math.max(0,state.events.length-1));range.value=String(Math.max(0,state.events.indexOf(event)));range.disabled=!state.events.length;
102
- if(event)range.setAttribute('aria-valuetext',time(event.timestamp)+' · '+eventLabel(event));
103
- else range.removeAttribute('aria-valuetext');
104
- $('event-time').textContent=event?time(event.timestamp):'—';
105
- $('return-live').hidden=!state.replayId;
106
- $('journal-state').innerHTML=chip(state.replayId?'Replay':state.live?'Recorded activity':'Activity unavailable',state.replayId?'amber':'cyan');
107
- const strip=$('main').querySelector('.strip');if(strip)strip.outerHTML=telemetryStrip();
108
- if(state.inspector==='event'&&!$('inspector').matches(':focus-within'))renderInspector();
109
- const pause=$('pause-motion');pause.textContent=state.motionPaused?'Resume motion':'Pause motion';pause.disabled=reduced.matches;
110
- drawGraph();
111
- }
112
- function contextPage(){
113
- const input=state.contextInput??{};
114
- const enabled=state.features?.features?.contextAssembly!==false;
115
- const form='<div class="panel-body"><form class="form" data-form="context"><label>Task or keywords<textarea name="query" rows="3" maxlength="4096" placeholder="Describe the work this context pack supports">'+esc(input.query)+'</textarea></label><div class="form-grid"><label>Token budget<input name="tokenBudget" type="number" min="0" max="12000" value="'+esc(input.tokenBudget??2000)+'" required></label><label>Retrieval profile<select name="profile"><option value="adaptive">Adaptive</option><option value="legacy" '+(input.profile==='legacy'?'selected':'')+'>Legacy</option></select></label></div><label class="check"><input name="expandRelated" type="checkbox" '+(input.expandRelated?'checked':'')+'> Expand related memories</label><div class="actions">'+(state.project&&enabled?submit('Build pack')+'<button class="btn" type="submit" name="compare" value="yes">Compare profiles</button>':chip(state.project?'Context packs disabled':'Select a project','amber'))+'</div></form></div>';
116
- const pack=state.context;
117
- const output=pack?'<div class="panel-body stack"><div class="row"><strong>'+fmt(arr(pack.entries).length)+' selected memories</strong>'+chip(fmt(pack.budget.estimatedTokens)+' estimated tokens','pink')+'</div>'+arr(pack.warnings).map(warning=>'<p class="small amber">'+esc(warning)+'</p>').join('')+
118
- (state.comparison?'<dl class="facts">'+fact('Legacy estimate',fmt(state.comparison.legacy.budget.estimatedTokens)+' tokens')+fact('Adaptive estimate',fmt(state.comparison.adaptive.budget.estimatedTokens)+' tokens')+'</dl>':'')+
119
- (arr(pack.entries).length?list(pack.entries.map(entry=>'<li class="item"><strong>'+esc(entry.excerpt)+'</strong><small class="muted">'+esc(arr(entry.reasons).join(' · '))+'</small><small class="mono cyan">'+esc(arr(entry.sources).join(' · '))+'</small></li>').join('')):empty('No memories fit this pack','Check the project, keywords and budget.'))+'<div class="actions">'+action('copy-pack','Copy pack','primary')+'</div><details><summary>Rendered pack</summary><pre>'+esc(pack.markdown)+'</pre></details></div>':empty('Build a scoped context pack','The result shows the actual selected records, source references, and estimated token budget.');
120
- return heading('Context packs','Assemble and copy project memory for a task.',view('usage','Usage evidence'))+'<div class="two">'+panel('Pack request',form,chip(scopeName()))+panel('Pack result',output)+'</div>';
121
- }
122
- function modelsPage(){
123
- const hosts=arr(state.integrations?.agents);
124
- const body=hosts.length?'<div class="table-wrap"><table><thead><tr><th>Client recipe</th><th>Project skill files</th><th>Retained activity</th><th>Model</th><th>Usage</th></tr></thead><tbody>'+hosts.map(host=>{
125
- const events=state.events.filter(event=>event.agent===host.id),latest=events.at(-1);
126
- return '<tr><td><strong>'+esc(host.label)+'</strong></td><td>'+esc(host.installation)+'</td><td>'+esc(latest?evidence(latest)+' · '+eventLabel(latest)+' · '+isoTime(latest.timestamp):state.live?'No retained records for this client':'Activity display is off')+'</td><td>Unavailable</td><td>Unavailable</td></tr>';
127
- }).join('')+'</tbody></table></div><p class="panel-body small muted">Recipe availability and file presence do not verify client configuration. The journal records exchanges and reports. Current client connectivity is unavailable.</p>':error('integrations','client checks')||empty('Reading integration evidence','Client skill-file checks are loading.');
128
- return heading('Connections and models','Review available client recipes and retained activity separately.',view('setup','Configure hosts'))+panel('Client evidence',body);
129
- }
130
- function usagePage(){
131
- const measured=state.events.filter(event=>event.source==='measured'&&event.kind==='tool-end');
132
- const reported=state.events.filter(event=>event.source==='reported');
133
- const duration=measured.map(event=>event.durationMs).filter(Number.isFinite).sort((a,b)=>a-b);
134
- const total=duration.length?duration.reduce((sum,item)=>sum+item,0):null;
135
- return heading('Usage and timing','Inspect recorded tool activity separately from model usage and context estimates.')+telemetryStrip()+'<div class="two">'+panel('Tool activity','<div class="panel-body stack"><dl class="facts">'+fact('Measured tool returns',state.live?fmt(measured.length):undefined)+fact('Reported activity records',state.live?fmt(reported.length):undefined)+fact('Measured tool duration',total!==null?fmt(total)+' ms':undefined)+fact('Duration coverage',state.live?duration.length+' of '+measured.length+' measured tool returns':undefined)+'</dl><p class="small muted">Counts describe the retained records loaded for this workspace.</p></div>')+panel('Model usage',empty('Model usage unavailable','This workspace does not receive input, output, cached-token or cost receipts from model providers.',view('context','Inspect context estimates')) )+'</div>';
136
- }
137
- function setupPage(){
138
- const features=state.features;
139
- const flags=features?list(arr(features.definitions).map(item=>{
140
- const reason=featureLimit(item.id),on=!reason&&features.features[item.id];
141
- return '<li class="item"><div class="row"><strong>'+esc(item.label)+'</strong>'+btn(reason?'Unavailable':on?'On':'Off','data-feature="'+esc(item.id)+'" role="switch" aria-checked="'+!!on+'" aria-label="'+esc(item.label)+'" '+(state.pending||reason?'disabled':''))+'</div><p class="small muted">'+esc(reason??item.description)+'</p></li>';
142
- }).join('')):error('features','feature settings')||empty('Loading feature settings','The server supplies the current switches and their descriptions.');
143
- const hosts=arr(state.integrations?.agents);
144
- const recipes=hosts.length?list(hosts.map(host=>'<li class="item"><div class="row"><h3>'+esc(host.label)+'</h3>'+chip(host.installation)+'</div><p class="small muted">'+esc(host.detail)+'</p><details><summary>Connection recipe</summary><div class="stack"><pre>'+esc(host.config?.content)+'</pre>'+arr(host.config?.notes).map(note=>'<p class="small muted">'+esc(note)+'</p>').join('')+'<div class="actions">'+action('copy-host','Copy configuration','','data-host="'+esc(host.id)+'"')+action('copy-command','Copy skill command','','data-host="'+esc(host.id)+'"')+'</div></div></details></li>').join('')):error('integrations','host recipes')||empty('Loading host recipes','Configuration recipes are generated locally.');
145
- const checks=arr(state.integrations?.checks);
146
- return heading('Workspace setup','Configure host access and manage the workspace’s real feature switches.',action('refresh','Refresh checks'))+'<div class="two">'+panel('Features',flags,features?chip('Revision '+features.revision):'')+panel('Client configuration',recipes,chip('Local recipes','cyan'))+'</div>'+panel('Local checks',checks.length?list(checks.map(check=>'<li class="item"><div class="row"><strong>'+esc(check.label)+'</strong>'+chip(check.status,check.status==='pass'?'green':check.status==='error'?'red':'')+'</div><p class="small muted">'+esc(check.detail)+'</p></li>').join('')):empty('No checks loaded','Refresh to retry host and storage checks.'),checks.length?action('copy-diagnostics','Copy diagnostics'):'');
147
- }
148
- const views={overview:overviewPage,context:contextPage,models:modelsPage,usage:usagePage,setup:setupPage,knowledge:knowledgePage,room:roomPage,learning:learningPage};
9
+ export const BRAIN_HUB_VIEWS = String.raw `
10
+ function eventLabel(event){
11
+ if(event.tool)return event.tool+' · '+(event.outcome??(event.kind==='tool-start'?'started':'returned'));
12
+ if(event.phase)return 'Phase · '+event.phase;
13
+ return event.kind.replaceAll('-',' ');
14
+ }
15
+ function eventTone(event){return event.source==='measured'?'cyan':'amber';}
16
+ function evidence(event){return event.source==='measured'?'Measured '+(event.transport==='mcp'?'MCP':'tool'):'Reported';}
17
+ function timelineMarkup(trace=false){
18
+ const events=state.events;
19
+ if(!events.length)return empty(state.errors.telemetry?'Activity unavailable':state.live?'No recorded activity':'Activity display is off',state.errors.telemetry||
20
+ (state.live?'A tool exchange or reported phase will appear here when activity recording is enabled.':'Enable live dashboard activity in Setup.'),view('setup','Open setup'));
21
+ return list([...events].reverse().map(event=>'<li><button class="entry event" type="button" data-event="'+esc(event.id)+'" aria-pressed="'+(state.replayId===event.id)+'"><time class="mono" datetime="'+esc(event.timestamp)+'">'+esc(time(event.timestamp))+'</time><span>'+esc(eventLabel(event))+' <span class="muted">· '+esc(hostName(event.agent))+'</span></span>'+chip(evidence(event),eventTone(event))+'</button></li>').join(''),trace?'aria-label="Recorded tool trace"':'aria-label="Recorded activity"');
22
+ }
23
+ function trace(values){
24
+ if(values.length<2)return '';
25
+ const max=Math.max(...values,1);
26
+ const points=values.map((value,index)=>(2+index*62/(values.length-1))+','+(22-value*19/max));
27
+ return '<svg class="metric-trace cyan" viewBox="0 0 66 26" aria-hidden="true"><path d="M'+points.join(' L')+'" fill="none" stroke="currentColor" stroke-width="1.25"/></svg>';
28
+ }
29
+ function telemetryStrip(){
30
+ const measured=state.events.filter(event=>event.source==='measured'&&event.kind==='tool-end');
31
+ const durations=measured.map(event=>event.durationMs).filter(Number.isFinite);
32
+ const observed=new Set(state.events.filter(event=>event.agent&&event.agent!=='user'&&event.agent!=='portal').map(event=>event.agent));
33
+ const memories=state.graph?.totalMemories??state.overview?.quality?.total;
34
+ const pack=state.context?.budget;
35
+ return '<dl class="strip"><div class="metric"><dt>Stored memories in scope</dt><dd class="mono cyan">'+fmt(memories)+'</dd><small>'+esc(scopeName())+'</small></div><div class="metric">'+trace(durations.slice(-16))+'<dt>Measured tool returns</dt><dd class="mono">'+(state.live?fmt(measured.length):'Unavailable')+'</dd><small>'+esc(state.errors.telemetry?'Activity unavailable':state.live?fmt(observed.size)+' client labels in retained activity':'Activity display off')+'</small></div><div class="metric"><dt>Model usage receipts</dt><dd class="mono">'+(state.usage?fmt(arr(state.usage.receipts).length):'Not loaded')+'</dd><small>'+(state.usage?'Host-reported · retained window':'Open Usage to inspect host reports')+'</small></div><div class="metric"><dt>Latest context pack estimate</dt><dd class="mono pink">'+(pack?fmt(pack.estimatedTokens)+' / '+fmt(pack.tokenBudget):'No pack built')+'</dd><small>'+esc(pack?arr(state.context.entries).length+' selected memories':'Assemble a scoped pack in Context')+'</small></div></dl>';
36
+ }
37
+ function rail(){
38
+ const graph=arr(state.graph?.nodes);
39
+ const projects=arr(state.overview?.projects).filter(project=>!state.project||project.id===state.project);
40
+ const memories=graph.filter(node=>node.kind==='memory').slice(0,6);
41
+ const projectsMarkup=projects.length?list(projects.slice(0,7).map(project=>'<li><button type="button" class="rail-entry" data-project="'+esc(project.id)+'" aria-pressed="'+(state.project===project.id)+'"><strong>'+esc(project.name)+'</strong><small>'+fmt(project.memories)+' memories</small></button></li>').join('')):empty('No projects with memories','Open a project workspace to add project-local knowledge.',view('setup','See setup'));
42
+ return '<aside class="panel rail" aria-label="Projects and recent records"><div class="rail-title row"><h2>Projects</h2>'+chip(projects.length)+'</div>'+projectsMarkup+'<div class="rail-title row"><h2>Recent records</h2>'+chip(memories.length)+'</div>'+(memories.length?list(memories.map(node=>'<li><button type="button" class="rail-entry" data-node="'+esc(node.id)+'"><strong>'+esc(node.label)+'</strong><small>'+esc(node.category)+' · '+esc(hostName(node.agent))+'</small></button></li>').join('')):empty('No memory records','Add project knowledge or connect a host.',view('knowledge','Open knowledge')) )+'</aside>';
43
+ }
44
+ function graphMarkup(){
45
+ const nodes=arr(state.graph?.nodes),nodeIds=new Set(nodes.map(node=>node.id));
46
+ const edges=arr(state.graph?.edges).filter(edge=>nodeIds.has(edge.source)&&nodeIds.has(edge.target));
47
+ const traceMode=state.graphMode==='trace';
48
+ const filters=[['all','All links',''],['project','Project membership','blue'],['author','Recorded author','pink'],['typed','Recorded relation','cyan']];
49
+ const legend=filters.map(([id,label,tone])=>'<button class="legend-filter" type="button" data-graph-filter="'+id+'" aria-pressed="'+(state.graphFilter===id)+'">'+
50
+ (tone?'<i class="'+tone+'" aria-hidden="true"></i>':'')+esc(label)+'</button>').join('');
51
+ const graphTools=traceMode?'':action('node-list','Find node','','aria-expanded="'+state.listing+'" aria-controls="node-list"')+
52
+ action('clear-focus','Clear focus','quiet','id="clear-focus" '+(state.focus?'':'hidden'));
53
+ return '<section class="panel graph-panel'+(state.graph&&!nodes.length&&!traceMode?' graph-panel-empty':'')+'"><div class="panel-head"><div><h2>'+(traceMode?'Recorded trace':'Memory graph')+'</h2><span class="small mono muted">'+fmt(nodes.length)+' records / '+fmt(edges.length)+' relationships'+(state.graph?.truncated?' · limited view':'')+'</span></div><div class="actions">'+
54
+ action('graph','Graph','','aria-pressed="'+!traceMode+'"')+action('trace','Trace','','aria-pressed="'+traceMode+'"')+graphTools+'</div></div>'+
55
+ '<div class="graph-stage" id="brain-stage"><canvas id="brain-canvas" aria-hidden="true" '+(traceMode?'hidden':'')+'></canvas><div class="graph-meta" aria-hidden="true"><span>Project memory</span><span>Recorded authors</span></div>'+
56
+ (!nodes.length&&!traceMode?'<div class="graph-empty"><strong>'+esc(state.errors.graph?'Memory graph unavailable':state.graph?'No stored graph records':'Loading memory graph')+'</strong><p class="small muted">'+esc(state.errors.graph??(state.graph?'Save knowledge or use a connected host to create your first record.':'Reading the selected workspace.'))+'</p><div class="actions">'+(state.errors.graph?action('refresh','Retry'):view('knowledge','Open knowledge')+view('setup','Connect a host'))+'</div></div>':'')+
57
+ graphFinderMarkup(!state.listing||traceMode)+'<div id="trace-list" class="trace-list" '+(traceMode?'':'hidden')+'>'+(traceMode?timelineMarkup(true):'')+'</div>'+
58
+ '<span class="live-path-label" id="graph-event" aria-hidden="true" hidden></span><div id="node-caption" class="focus-label" hidden></div></div>'+
59
+ (!traceMode?'<div class="graph-foot"><div class="legend" aria-label="Graph relationship filters">'+legend+'</div><div class="graph-camera" aria-label="Graph zoom controls">'+
60
+ action('graph-zoom-out','−','','id="graph-zoom-out" aria-label="Zoom out"')+'<output class="mono" id="graph-zoom">100%</output>'+
61
+ action('graph-zoom-in','+','','id="graph-zoom-in" aria-label="Zoom in"')+action('graph-fit','Fit','','aria-label="Reset graph to fit"')+'</div></div>':'')+
62
+ '<div class="graph-exchanges" id="graph-exchanges">'+exchangeMarkup()+'</div>'+
63
+ '<p class="small graph-foot" id="project-file-pulse" hidden></p></section>';
64
+ }
65
+ function inspectorBody(){
66
+ const item=arr(state.graph?.nodes).find(node=>node.id===state.picked);
67
+ const memory=state.memory?.memory;
68
+ const event=displayEvent();
69
+ const tabs=[['record','Record'],['sources','Sources'],['relations','Links'],['event','Event']].map(([id,label])=>btn(label,'data-inspect="'+id+'" aria-pressed="'+(state.inspector===id)+'"')).join('');
70
+ let details;
71
+ if(state.inspector==='event'){
72
+ details=event?'<div class="panel-body stack"><div class="row"><h3>'+esc(eventLabel(event))+'</h3>'+chip(evidence(event),eventTone(event))+'</div><dl class="facts">'+fact('Host',hostName(event.agent))+fact('Time',isoTime(event.timestamp))+fact('Transport',event.transport)+fact('Session',event.sessionId)+fact('Outcome',event.outcome)+fact('Duration',Number.isFinite(event.durationMs)?event.durationMs+' ms':undefined)+'</dl>'+pairedExchangeMarkup(event)+action('copy-event','Copy event receipt')+'</div>':empty(state.replayExpired?'Replay record expired':'No activity selected',state.replayExpired?'Choose another journal record or return to Latest.':'Select a recorded journal event to inspect its source and outcome.');
73
+ }else if(!item){
74
+ details=empty('Select a graph record','Choose a node, a recent record, or a journal event to inspect its evidence.',state.events.length?action('inspect-latest','Inspect latest event'):'');
75
+ }else if(state.inspector==='sources'){
76
+ const sources=arr(memory?.sources??item.sources);
77
+ details='<div class="panel-body stack"><h3>'+esc(item.label)+'</h3>'+(sources.length?list(sources.map(source=>'<li class="item mono small">'+esc(source)+'</li>').join('')):empty('No source attached','This record has no persisted source reference.'))+(local&&item.memoryId?action('source-evidence','Verify original source','','data-memory="'+esc(item.memoryId)+'"'):'')+'</div>';
78
+ }else if(state.inspector==='relations'){
79
+ const edges=arr(state.graph?.edges).filter(edge=>edge.source===item.id||edge.target===item.id);
80
+ details='<div class="panel-body stack"><h3>'+esc(item.label)+'</h3>'+(edges.length?list(edges.map(edge=>{
81
+ const id=edge.source===item.id?edge.target:edge.source;
82
+ const node=arr(state.graph?.nodes).find(point=>point.id===id);
83
+ return '<li><button type="button" class="entry" data-node="'+esc(id)+'"><strong>'+esc(node?.label??id)+'</strong><small>'+esc(edge.kind)+'</small></button></li>';
84
+ }).join('')):empty('No graph relationships','There are no visible relationships for this record.'))+'</div>';
85
+ }else{
86
+ details='<div class="panel-body stack"><div class="row"><h3>'+esc(item.kind==='agent'?hostName(item.label):item.label)+'</h3>'+chip(item.kind)+'</div><dl class="facts">'+fact('Project',item.project)+fact('Recorded by',item.agent&&hostName(item.agent))+fact('State',memory?.effectiveState??item.effectiveState)+fact('Updated',item.updatedAt&&isoTime(item.updatedAt))+'</dl>'+(item.kind==='memory'?'<pre>'+esc(memory?.content??item.snippet)+'</pre>'+action('copy-memory','Copy record'):'')+'</div>';
87
+ }
88
+ return '<div class="panel-head"><h2>Record inspector</h2>'+(item?chip(item.category??item.kind):'')+'</div><div class="tabs" aria-label="Inspector tabs">'+tabs+'</div>'+details;
89
+ }
90
+ function renderInspector(){
91
+ const target=$('inspector');
92
+ if(target){
93
+ const tab=target.querySelector('[data-inspect]:focus')?.dataset.inspect;
94
+ target.innerHTML=inspectorBody();
95
+ if(tab)[...target.querySelectorAll('[data-inspect]')].find(button=>button.dataset.inspect===tab)?.focus({preventScroll:true});
96
+ }
97
+ syncGraphControls();
98
+ }
99
+ function overviewPage(){
100
+ const event=displayEvent();
101
+ const pack=state.context?.budget;
102
+ const compare=state.comparison;
103
+ const contextBody=pack?'<div class="panel-body stack"><div class="row"><span class="muted">Estimated pack tokens</span><strong class="mono">'+fmt(pack.estimatedTokens)+' / '+fmt(pack.tokenBudget)+'</strong></div><div class="meter"><i style="width:'+Math.min(100,Math.round(100*pack.estimatedTokens/Math.max(1,pack.tokenBudget)))+'%"></i></div>'+(compare?'<p class="small muted">Legacy '+fmt(compare.legacy.budget.estimatedTokens)+' · Adaptive '+fmt(compare.adaptive.budget.estimatedTokens)+' estimated tokens</p>':'')+(state.context?.efficiency?'<p><strong>'+(state.context.efficiency.signedTokenDifference<0?fmt(-state.context.efficiency.signedTokenDifference)+' more estimated tokens':fmt(state.context.efficiency.estimatedTokensAvoided)+' estimated tokens avoided')+'</strong></p><p class="small muted">Compared with the same task using the largest pack permitted by project controls. '+state.context.efficiency.assemblyMs+' ms assembly. Not measured billing savings.</p>':'')+view('context','Open context pack')+'</div>':empty('No context pack for this session','Assemble a pack to inspect estimated tokens and selection reasons.',view('context','Build context pack'));
104
+ return heading('Brain control room','Explore stored memories, inspect tool exchanges, and connect your project.',action('pause',state.motionPaused?'Resume motion':'Pause motion','','id="pause-motion" '+(reduced.matches?'disabled':''))+view('setup','Connection setup'))+
105
+ telemetryStrip()+agentDock()+'<div class="control-grid">'+rail()+graphMarkup()+'<section class="panel inspect" id="inspector">'+inspectorBody()+'</section></div><div class="lower">'+panel('Context efficiency',contextBody,chip('Estimates','pink'))+
106
+ panel('Activity journal','<div class="replay"><label for="replay" class="small muted">Seek record</label><input id="replay" type="range" min="0" max="'+Math.max(0,state.events.length-1)+'" value="'+Math.max(0,state.events.indexOf(event))+'" '+(state.events.length?'':'disabled')+'><output class="mono small" id="event-time">'+esc(event?time(event.timestamp):state.replayExpired?'Expired':'—')+'</output>'+action('live','Latest','','id="return-live" '+(state.replayId?'':'hidden'))+'</div><div class="timeline" id="timeline">'+timelineMarkup()+'</div>', '<span id="journal-state">'+chip(state.replayId?(state.replayExpired?'Replay expired':'Replay'):state.live?'Recorded activity':'Activity unavailable',state.replayId?'amber':'cyan')+'</span>')+'</div>';
107
+ }
108
+ function updateOverviewLive(){
109
+ const timeline=$('timeline'),range=$('replay');
110
+ if(!timeline||!range)return;
111
+ const event=displayEvent();
112
+ const markup=timelineMarkup(),previous=timeline.querySelector('[data-event]:focus')?.dataset.event;
113
+ if(timeline.dataset.markup!==markup){
114
+ timeline.innerHTML=markup;timeline.dataset.markup=markup;
115
+ if(previous)[...timeline.querySelectorAll('[data-event]')].find(node=>node.dataset.event===previous)?.focus({preventScroll:true});
116
+ }
117
+ const traceList=$('trace-list');
118
+ if(traceList&&!traceList.hidden){
119
+ const trace=timelineMarkup(true),previous=traceList.querySelector('[data-event]:focus')?.dataset.event;
120
+ if(traceList.dataset.markup!==trace){
121
+ traceList.innerHTML=trace;traceList.dataset.markup=trace;
122
+ if(previous)[...traceList.querySelectorAll('[data-event]')].find(node=>node.dataset.event===previous)?.focus({preventScroll:true});
123
+ }
124
+ }
125
+ range.max=String(Math.max(0,state.events.length-1));range.value=String(Math.max(0,state.events.indexOf(event)));range.disabled=!state.events.length;
126
+ if(event)range.setAttribute('aria-valuetext',time(event.timestamp)+' · '+eventLabel(event));
127
+ else range.removeAttribute('aria-valuetext');
128
+ $('event-time').textContent=event?time(event.timestamp):state.replayExpired?'Expired':'—';
129
+ $('return-live').hidden=!state.replayId;
130
+ $('journal-state').innerHTML=chip(state.replayId?(state.replayExpired?'Replay expired':'Replay'):state.live?'Recorded activity':'Activity unavailable',state.replayId?'amber':'cyan');
131
+ const strip=$('main').querySelector('.strip');if(strip)strip.outerHTML=telemetryStrip();
132
+ if(state.inspector==='event'&&!$('inspector').matches(':focus-within'))renderInspector();
133
+ const pause=$('pause-motion');pause.textContent=state.motionPaused?'Resume motion':'Pause motion';pause.disabled=reduced.matches;
134
+ updateExchanges();drawGraph();updateAgentDock();
135
+ }
136
+ ${BRAIN_HUB_CONTEXT}
137
+ ${BRAIN_HUB_AGENT_DOCK}
138
+ ${BRAIN_HUB_USAGE}
139
+ ${BRAIN_HUB_SESSIONS}
140
+ ${BRAIN_HUB_CONTROLS}
141
+ ${BRAIN_HUB_MANAGED_SKILLS}
142
+ function setupPage(){
143
+ const features=state.features;
144
+ const flags=features?list(arr(features.definitions).map(item=>{
145
+ const reason=featureLimit(item.id),on=!reason&&features.features[item.id];
146
+ return '<li class="item"><div class="row"><strong>'+esc(item.label)+'</strong>'+btn(reason?'Unavailable':on?'On':'Off','data-feature="'+esc(item.id)+'" role="switch" aria-checked="'+!!on+'" aria-label="'+esc(item.label)+'" '+(state.pending||reason?'disabled':''))+'</div><p class="small muted">'+esc(reason??item.description)+'</p></li>';
147
+ }).join('')):error('features','feature settings')||empty('Loading feature settings','The server supplies the current switches and their descriptions.');
148
+ const hosts=arr(state.integrations?.agents);
149
+ const recipes=hosts.length?list(hosts.map(host=>'<li class="item"><div class="row"><h3>'+esc(host.label)+'</h3>'+chip(host.installation)+'</div><p class="small muted">'+esc(host.detail)+'</p><details><summary>Connection recipe</summary><div class="stack"><pre>'+esc(host.config?.content)+'</pre>'+arr(host.config?.notes).map(note=>'<p class="small muted">'+esc(note)+'</p>').join('')+'<div class="actions">'+action('copy-host','Copy configuration','','data-host="'+esc(host.id)+'"')+action('copy-command','Copy skill command','','data-host="'+esc(host.id)+'"')+'</div></div></details></li>').join('')):error('integrations','host recipes')||empty('Loading host recipes','Configuration recipes are generated locally.');
150
+ const checks=arr(state.integrations?.checks);
151
+ return heading('Workspace setup','Configure host access and manage the workspace’s real feature switches.',action('refresh','Refresh checks'))+projectControls()+managedSkillControls()+'<div class="two">'+panel('Features',flags,features?chip('Revision '+features.revision):'')+panel('Client configuration',recipes,chip('Local recipes','cyan'))+'</div>'+panel('Local checks',checks.length?list(checks.map(check=>'<li class="item"><div class="row"><strong>'+esc(check.label)+'</strong>'+chip(check.status,check.status==='pass'?'green':check.status==='error'?'red':'')+'</div><p class="small muted">'+esc(check.detail)+'</p></li>').join('')):empty('No checks loaded','Refresh to retry host and storage checks.'),checks.length?action('copy-diagnostics','Copy diagnostics'):'');
152
+ }
153
+ const views={overview:overviewPage,sessions:sessionsPage,context:contextPage,models:modelsPage,usage:usagePage,setup:setupPage,knowledge:knowledgePage,room:roomPage,learning:learningPage};
149
154
  ` + BRAIN_HUB_PROJECT_VIEWS + BRAIN_HUB_ACTIONS;
150
155
  //# sourceMappingURL=brain-hub-views.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"brain-hub-views.js","sourceRoot":"","sources":["../../src/dashboard/brain-hub-views.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAC;AACvE,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAE3D,MAAM,CAAC,MAAM,eAAe,GAAG,MAAM,CAAC,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAkJxC,GAAG,uBAAuB,GAAG,iBAAiB,CAAC"}
1
+ {"version":3,"file":"brain-hub-views.js","sourceRoot":"","sources":["../../src/dashboard/brain-hub-views.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAC7D,OAAO,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AACjE,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAC7D,OAAO,EAAE,wBAAwB,EAAE,MAAM,+BAA+B,CAAC;AACzE,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAC;AACvE,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAE3D,MAAM,CAAC,MAAM,eAAe,GAAG,MAAM,CAAC,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA+HvC,iBAAiB;EACjB,oBAAoB;EACpB,eAAe;EACf,kBAAkB;EAClB,kBAAkB;EAClB,wBAAwB;;;;;;;;;;;;;CAazB,GAAG,uBAAuB,GAAG,iBAAiB,CAAC"}