synabun 2.0.9 → 2.0.10

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 (252) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/README.md +1 -1
  3. package/THIRD-PARTY-LICENSES.md +46 -4
  4. package/launcher.mjs +9 -3
  5. package/lib/start-launcher.js +6 -1
  6. package/mcp-server/package-lock.json +2 -2
  7. package/mcp-server/package.json +1 -1
  8. package/neural-interface/i18n/en.json +218 -7
  9. package/neural-interface/i18n/pt-BR.json +220 -9
  10. package/neural-interface/lib/account-usage.js +5 -3
  11. package/neural-interface/lib/app-controls-api.js +161 -0
  12. package/neural-interface/lib/assistant-api.js +21 -7
  13. package/neural-interface/lib/assistant-brains/opencode.js +13 -1
  14. package/neural-interface/lib/assistant-brains/opencode2-browser-policy/index.js +44 -0
  15. package/neural-interface/lib/assistant-brains/opencode2-route-gate/index.js +57 -0
  16. package/neural-interface/lib/assistant-catalog.js +17 -2
  17. package/neural-interface/lib/assistant-dispatch.js +3 -0
  18. package/neural-interface/lib/assistant-envelope.js +19 -1
  19. package/neural-interface/lib/assistant-runtime.js +127 -8
  20. package/neural-interface/lib/claude-model-catalog.js +64 -14
  21. package/neural-interface/lib/codex-settings-schema.js +109 -47
  22. package/neural-interface/lib/editor-completion-opencode.js +132 -35
  23. package/neural-interface/lib/exec-loop-command.js +8 -1
  24. package/neural-interface/lib/launch-cache.js +293 -0
  25. package/neural-interface/lib/login-start.js +524 -0
  26. package/neural-interface/lib/native-loop-providers.js +18 -3
  27. package/neural-interface/lib/native-loop-runtime.js +19 -0
  28. package/neural-interface/lib/opencode-db-schema.js +75 -0
  29. package/neural-interface/lib/opencode-history.js +16 -1
  30. package/neural-interface/lib/opencode-protocol.js +173 -0
  31. package/neural-interface/lib/opencode-provider-view.js +81 -0
  32. package/neural-interface/lib/opencode-runtime-config.js +10 -3
  33. package/neural-interface/lib/opencode-serve-records.js +606 -0
  34. package/neural-interface/lib/opencode-v2-client.js +33 -6
  35. package/neural-interface/lib/opencode-v2-ws-requests.js +19 -5
  36. package/neural-interface/lib/opencode-worker-activity.js +229 -0
  37. package/neural-interface/lib/opencode2-client.js +760 -0
  38. package/neural-interface/lib/opencode2-proxy.js +116 -0
  39. package/neural-interface/lib/opencode2-translate.js +1008 -0
  40. package/neural-interface/lib/process-identity.js +361 -0
  41. package/neural-interface/lib/session-cache-builder.js +26 -5
  42. package/neural-interface/lib/session-title-generator.js +18 -0
  43. package/neural-interface/lib/style-guide/api.js +4 -3
  44. package/neural-interface/lib/untrusted-file-headers.js +27 -0
  45. package/neural-interface/package-lock.json +1343 -45
  46. package/neural-interface/package.json +5 -1
  47. package/neural-interface/public/index.html +2 -2
  48. package/neural-interface/public/shared/agent-runtime-options.js +55 -11
  49. package/neural-interface/public/shared/assistant/asst-agents-dock.js +172 -17
  50. package/neural-interface/public/shared/assistant/asst-api.js +11 -3
  51. package/neural-interface/public/shared/assistant/asst-brain-picker.js +195 -80
  52. package/neural-interface/public/shared/assistant/asst-composer.js +1 -1
  53. package/neural-interface/public/shared/assistant/asst-hosts.js +49 -2
  54. package/neural-interface/public/shared/assistant/asst-menu.js +77 -6
  55. package/neural-interface/public/shared/assistant/asst-panel.js +197 -33
  56. package/neural-interface/public/shared/assistant/asst-rack-styles.js +28 -10
  57. package/neural-interface/public/shared/assistant/asst-redact.js +186 -0
  58. package/neural-interface/public/shared/assistant/asst-render.js +382 -36
  59. package/neural-interface/public/shared/assistant/asst-run-stage.js +231 -0
  60. package/neural-interface/public/shared/assistant/asst-sanitize.js +193 -3
  61. package/neural-interface/public/shared/assistant/asst-scene-styles.js +273 -0
  62. package/neural-interface/public/shared/assistant/asst-scenes.js +528 -0
  63. package/neural-interface/public/shared/assistant/asst-sidepanel-styles.js +3 -15
  64. package/neural-interface/public/shared/assistant/asst-state.js +14 -0
  65. package/neural-interface/public/shared/assistant/asst-styles.js +43 -0
  66. package/neural-interface/public/shared/assistant/asst-tool-kinds.js +13 -1
  67. package/neural-interface/public/shared/cc-dropdown.js +480 -0
  68. package/neural-interface/public/shared/cdx/cdx-panel.js +13 -1
  69. package/neural-interface/public/shared/cdx/cdx-render.js +15 -3
  70. package/neural-interface/public/shared/cdx/cdx-styles.js +4 -0
  71. package/neural-interface/public/shared/cdx/cdx-tabs.js +149 -35
  72. package/neural-interface/public/shared/cp/cp-markdown.js +4 -57
  73. package/neural-interface/public/shared/data-cache.js +223 -0
  74. package/neural-interface/public/shared/desktop-bridge.js +11 -0
  75. package/neural-interface/public/shared/desktop-titlebar-view.js +78 -1
  76. package/neural-interface/public/shared/desktop-titlebar.js +214 -28
  77. package/neural-interface/public/shared/desktop-update-view.js +15 -5
  78. package/neural-interface/public/shared/file-links.js +73 -8
  79. package/neural-interface/public/shared/html-shell.js +74 -80
  80. package/neural-interface/public/shared/launch-prefetch.js +59 -0
  81. package/neural-interface/public/shared/ocp-v2/ocp-v2-caps.js +3 -2
  82. package/neural-interface/public/shared/ocp-v2/ocp-v2-childpanel.js +7 -2
  83. package/neural-interface/public/shared/ocp-v2/ocp-v2-modelpicker.js +54 -17
  84. package/neural-interface/public/shared/ocp-v2/ocp-v2-panel.js +93 -2
  85. package/neural-interface/public/shared/ocp-v2/ocp-v2-projectbar.js +3 -2
  86. package/neural-interface/public/shared/ocp-v2/ocp-v2-providers.js +11 -9
  87. package/neural-interface/public/shared/ocp-v2/ocp-v2-rehydrate.js +15 -4
  88. package/neural-interface/public/shared/ocp-v2/ocp-v2-render.js +1 -1
  89. package/neural-interface/public/shared/ocp-v2/ocp-v2-send-logic.js +401 -0
  90. package/neural-interface/public/shared/ocp-v2/ocp-v2-send.js +16 -9
  91. package/neural-interface/public/shared/ocp-v2/ocp-v2-sessions-logic.js +60 -0
  92. package/neural-interface/public/shared/ocp-v2/ocp-v2-styles.js +48 -19
  93. package/neural-interface/public/shared/ocp-v2/ocp-v2-ws.js +195 -19
  94. package/neural-interface/public/shared/opencode-providers.js +39 -0
  95. package/neural-interface/public/shared/schedules-view.js +318 -0
  96. package/neural-interface/public/shared/settings/settings-app-pane.js +335 -0
  97. package/neural-interface/public/shared/settings/settings-app.js +243 -0
  98. package/neural-interface/public/shared/settings/settings-data.js +88 -0
  99. package/neural-interface/public/shared/settings/settings-ia.js +17 -0
  100. package/neural-interface/public/shared/sidepanel-session-status.js +49 -0
  101. package/neural-interface/public/shared/styles.css +540 -371
  102. package/neural-interface/public/shared/ui-assistant.js +27 -5
  103. package/neural-interface/public/shared/ui-automation-studio.js +67 -27
  104. package/neural-interface/public/shared/ui-bookmarks.js +2 -1
  105. package/neural-interface/public/shared/ui-claude-panel.js +76 -8
  106. package/neural-interface/public/shared/ui-codex-panel.js +1 -0
  107. package/neural-interface/public/shared/ui-detail.js +6 -6
  108. package/neural-interface/public/shared/ui-file-links.js +1 -0
  109. package/neural-interface/public/shared/ui-image-gallery.js +6 -5
  110. package/neural-interface/public/shared/ui-invite.js +18 -2
  111. package/neural-interface/public/shared/ui-link.js +9 -9
  112. package/neural-interface/public/shared/ui-menubar.js +232 -33
  113. package/neural-interface/public/shared/ui-native-loop-router.js +2 -0
  114. package/neural-interface/public/shared/ui-opencode-panel-v2.js +1 -0
  115. package/neural-interface/public/shared/ui-schedules-studio.js +2099 -869
  116. package/neural-interface/public/shared/ui-sessions.js +15 -4
  117. package/neural-interface/public/shared/ui-settings.js +87 -36
  118. package/neural-interface/public/shared/ui-sidebar.js +4 -4
  119. package/neural-interface/public/shared/ui-sidepanel-session-entry.js +13 -0
  120. package/neural-interface/public/shared/ui-sidepanel-sessions.js +55 -4
  121. package/neural-interface/public/shared/ui-sidepanel-tray.js +84 -8
  122. package/neural-interface/public/shared/ui-skills.js +70 -12
  123. package/neural-interface/public/shared/ui-sync.js +7 -0
  124. package/neural-interface/public/shared/ui-terminal.js +36 -20
  125. package/neural-interface/public/shared/ui-trash.js +8 -1
  126. package/neural-interface/public/shared/ui-tutorial-steps.js +7 -3
  127. package/neural-interface/public/shared/ui-tutorial.js +15 -5
  128. package/neural-interface/public/shared/ui-update.js +83 -14
  129. package/neural-interface/public/shared/ui-whiteboard.js +2 -1
  130. package/neural-interface/public/variant/2d/main.js +4 -1
  131. package/neural-interface/public/variant/3d/main.js +3 -0
  132. package/neural-interface/public/vendor/highlight/LICENSE +29 -0
  133. package/neural-interface/public/vendor/highlight/github-dark-dimmed-11.12.0.min.css +9 -0
  134. package/neural-interface/public/vendor/highlight/highlight-11.12.0.min.js +1262 -0
  135. package/neural-interface/public/vendor/highlight/manifest.json +5 -0
  136. package/neural-interface/public/vendor/html2canvas/LICENSE +22 -0
  137. package/neural-interface/public/vendor/html2canvas/html2canvas-1.4.1.min.js +20 -0
  138. package/neural-interface/public/vendor/html2canvas/manifest.json +4 -0
  139. package/neural-interface/public/vendor/marked/LICENSE.md +44 -0
  140. package/neural-interface/public/vendor/marked/manifest.json +4 -0
  141. package/neural-interface/public/vendor/marked/marked-14.1.4.esm.js +2502 -0
  142. package/neural-interface/public/vendor/mermaid/LICENSE +21 -0
  143. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/abnfDiagram-PAR5IXJI.mjs +1 -0
  144. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/architecture-7GRP2DOG-M7G3SWCO.mjs +1 -0
  145. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/architectureDiagram-2PNWDENW.mjs +36 -0
  146. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/blockDiagram-PUWGUBOA.mjs +129 -0
  147. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/c4Diagram-3NLNHZP5.mjs +38 -0
  148. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-2NMSUPJH.mjs +1 -0
  149. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-32MCUW2P.mjs +206 -0
  150. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-3NPNU5GP.mjs +1 -0
  151. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-3SMGFRGC.mjs +1 -0
  152. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-4CAJECRU.mjs +14 -0
  153. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-4OADUZ5Q.mjs +10 -0
  154. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-5IMINLNL.mjs +1 -0
  155. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-5IQCK5RY.mjs +3 -0
  156. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-5MVFYWFU.mjs +1 -0
  157. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-AQ6EADP3.mjs +1 -0
  158. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-AZZRMDJM.mjs +15 -0
  159. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-CLS4B6BI.mjs +168 -0
  160. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-DZP67EKU.mjs +70 -0
  161. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-EICP7K5X.mjs +1 -0
  162. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-GDY2UAB4.mjs +1 -0
  163. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-H7VHZCWX.mjs +1 -0
  164. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-IJBDOHL6.mjs +1 -0
  165. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-JFFVMJIA.mjs +1 -0
  166. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-JQRUD6KW.mjs +1 -0
  167. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-JZGDI5JK.mjs +1 -0
  168. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-KD6SPIPN.mjs +2 -0
  169. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-NF366YI6.mjs +231 -0
  170. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-NUDPKBEF.mjs +32 -0
  171. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-OJINMHTS.mjs +1 -0
  172. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-Q6NGPIAH.mjs +2 -0
  173. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-QJSWEUOL.mjs +132 -0
  174. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-R7JDMUKV.mjs +1 -0
  175. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-RCXAM2GK.mjs +1 -0
  176. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-RT4MAWR3.mjs +1 -0
  177. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-SFMINX4W.mjs +1 -0
  178. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-SLDQ463B.mjs +88 -0
  179. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-STOV2HOB.mjs +1 -0
  180. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-T5OCTHI4.mjs +1 -0
  181. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-TCEHX7R3.mjs +1 -0
  182. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-VQC2EELJ.mjs +1 -0
  183. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-VRZKL3EH.mjs +1 -0
  184. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-VTHDQ7BV.mjs +1 -0
  185. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-VY2OSTJR.mjs +2 -0
  186. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-WUBWJARI.mjs +1 -0
  187. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-YBMQ5JGJ.mjs +2 -0
  188. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-YDMLL4PJ.mjs +1 -0
  189. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-Z6V44ARX.mjs +62 -0
  190. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-ZJJ7VQJP.mjs +339 -0
  191. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-ZNLK7HRR.mjs +1 -0
  192. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-ZQN7UHIL.mjs +1 -0
  193. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-ZS6VBONO.mjs +174 -0
  194. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-ZUNWM646.mjs +1 -0
  195. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/classDiagram-FGAMPOII.mjs +1 -0
  196. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/classDiagram-v2-AI5RJEK4.mjs +1 -0
  197. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/cose-bilkent-Q72C2OJ3.mjs +1 -0
  198. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/cynefin-OW5HDTMX-6OCTWOGG.mjs +1 -0
  199. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/cynefinDiagram-2OOWUY4M.mjs +62 -0
  200. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/dagre-MPVFI544.mjs +4 -0
  201. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/diagram-AE4NWXFW.mjs +24 -0
  202. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/diagram-BUJID5UG.mjs +41 -0
  203. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/diagram-C44CZ5NI.mjs +24 -0
  204. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/diagram-EYOLEXXS.mjs +30 -0
  205. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/diagram-P7B6PWOJ.mjs +3 -0
  206. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/ebnfDiagram-FTPZ3PV6.mjs +1 -0
  207. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/erDiagram-H3ERR26V.mjs +99 -0
  208. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/eventmodeling-NTZA5JFV-WKSWQHFS.mjs +1 -0
  209. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/flowDiagram-YHGXBVSY.mjs +1 -0
  210. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/ganttDiagram-RWFTB7ED.mjs +292 -0
  211. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/gitGraph-4MIJSDKK-JH2HKYUO.mjs +1 -0
  212. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/gitGraphDiagram-MAR6GKOP.mjs +106 -0
  213. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/info-A6RAGUB7-3TX3OFWW.mjs +1 -0
  214. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/infoDiagram-LV547AFH.mjs +2 -0
  215. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/ishikawaDiagram-PUJQGNDS.mjs +70 -0
  216. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/journeyDiagram-EW5QJT4W.mjs +139 -0
  217. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/kanban-definition-JSQERAZB.mjs +89 -0
  218. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/katex-CZ4GXH2S.mjs +257 -0
  219. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/mindmap-definition-VBJCLQLM.mjs +96 -0
  220. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/packet-AYTQ26CC-MKII6P3Y.mjs +1 -0
  221. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/pegDiagram-7WILOQTJ.mjs +1 -0
  222. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/pie-WAS4IAKB-6B4OPMJ5.mjs +1 -0
  223. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/pieDiagram-EFA7CBSB.mjs +39 -0
  224. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/quadrantDiagram-WARVYA7P.mjs +7 -0
  225. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/radar-RG4KPBEZ-UPTR3XUL.mjs +1 -0
  226. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/railroad-74A4TZTK-MEFUPFMS.mjs +1 -0
  227. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/railroad-abnf-HS5TGJTU-R5YG2S5D.mjs +1 -0
  228. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/railroad-ebnf-LZEXJU2U-G5QEX2L6.mjs +1 -0
  229. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/railroad-peg-WCYAUIDC-H5I2ZRY2.mjs +1 -0
  230. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/railroadDiagram-5NIQVNLQ.mjs +1 -0
  231. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/requirementDiagram-SXREGOKZ.mjs +84 -0
  232. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/sankeyDiagram-VV74M2VP.mjs +40 -0
  233. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/sequenceDiagram-52ZFIFDL.mjs +162 -0
  234. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/sizeCapture-FGA5AGYP.mjs +1 -0
  235. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/stateDiagram-3QAKPIVI.mjs +1 -0
  236. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/stateDiagram-v2-HN2P4RDZ.mjs +1 -0
  237. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/swimlanes-QLVP42QD.mjs +1 -0
  238. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/swimlanesDiagram-JKPDBFCN.mjs +8 -0
  239. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/timeline-definition-GS45XAXW.mjs +120 -0
  240. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/treeView-Q6P3EWNA-ZRJGANNQ.mjs +1 -0
  241. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/treemap-WGGIJYW6-K64VN5AS.mjs +1 -0
  242. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/vennDiagram-QIQU2ZRJ.mjs +34 -0
  243. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/wardley-WFR3VGLG-D67OWCCH.mjs +1 -0
  244. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/wardleyDiagram-U6YNM66N.mjs +78 -0
  245. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/xychartDiagram-XJAZEDNM.mjs +7 -0
  246. package/neural-interface/public/vendor/mermaid/manifest.json +4 -0
  247. package/neural-interface/public/vendor/mermaid/mermaid-11.17.2.esm.min.mjs +14 -0
  248. package/neural-interface/scripts/build-vendor.mjs +84 -3
  249. package/neural-interface/server.js +1099 -286
  250. package/npm-shrinkwrap.json +2 -2
  251. package/package.json +1 -1
  252. package/skills/synabun/modules/schedule.md +1 -1
@@ -51,15 +51,16 @@ import { cachedProjects, createBrainPicker, testDensityLabel } from './asst-brai
51
51
  import { createRovingToolbar } from './asst-roving.js';
52
52
  import { openAccountsManager } from './asst-accounts.js';
53
53
  import { createAgentsDock } from './asst-agents-dock.js';
54
+ import { activityOf } from './asst-scenes.js';
54
55
  import { createUsageGauge } from './asst-usage.js';
55
- import { closeMenu, isMenuOpen, openMenu } from './asst-menu.js';
56
+ import { closeMenu, currentMenuAnchor, isMenuOpen, openMenu } from './asst-menu.js';
56
57
  import { createRouteModeControl, openModelsManager, openRoutesEditor, refreshModelsManager, renderRouteCard, routeModeLabel } from './asst-route.js';
57
58
  import { budgetChipText, budgetSpendText, budgetTone, closeBudgetEditor, createBudgetController, mountBudgetChip, openBudgetEditor } from './asst-budget.js';
58
59
  import { computerRemoteView, createComputerPanel, createComputerToggle, normalizeComputerRemote, openFrameLightbox, closeFrameLightbox, requestDesktopStop } from './asst-computer.js';
59
60
  import {
60
61
  applyLimit, applyLimitPacket, applyUsagePacket, brainLabel, createRenameWriter, createTabState, DEFAULT_ROUTE_MODE, deserializeTabState, fmtCost, fmtRunLine, limitExpiresIn, limitNoticeText, modelShortName,
61
62
  brainModePatch, normalizeBrain, normalizeComputerMode, permissionModeLabel, normalizeRouteMode, normalizeTestDensity, projectLabel, providerShortLabel, readStoredBrain, runFromPayload,
62
- serializeTabState, tabStorageKey, userSessionTitle,
63
+ serializeTabState, sessionStatus, tabStorageKey, userSessionTitle,
63
64
  } from './asst-state.js';
64
65
  import {
65
66
  closeAssistantSession, dispatchAssistantRun, escalateAssistantRun, focusAssistantRun, getAssistantRun,
@@ -74,6 +75,7 @@ const ICON_COG = '<svg viewBox="0 0 24 24"><path d="M12.22 2h-.44a2 2 0 0 0-2 2v
74
75
  const ICON_ROUTE = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 7h13l-3-3M20 17H7l3 3"/></svg>';
75
76
  const ICON_COMPUTER = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="4" width="18" height="12" rx="2"/><path d="M8 20h8M12 16v4"/></svg>';
76
77
  const ICON_CLOSE = '<svg viewBox="0 0 24 24"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>';
78
+ const ICON_TRASH = '<svg viewBox="0 0 24 24"><path d="M3 6h18M8 6V4.5A1.5 1.5 0 0 1 9.5 3h5A1.5 1.5 0 0 1 16 4.5V6M18.5 6l-.8 13a2 2 0 0 1-2 1.9H8.3a2 2 0 0 1-2-1.9L5.5 6M10 11v6M14 11v6"/></svg>';
77
79
  const REPLAY_WAIT_MS = 1500; // a replay that stops short of its count is drawn after this long
78
80
 
79
81
  function el(tag, className, text) {
@@ -148,7 +150,9 @@ export function mountAssistant(viewport, { sessionId, brain, host = {}, session
148
150
  usageView: saved?.usageView || null,
149
151
  });
150
152
  Object.assign(st, {
151
- status: 'idle',
153
+ status: 'idle', // the brain's own: its turn, its cards (setStatus)
154
+ workers: { working: 0, waiting: 0 }, // its dispatched agents, from the agents tray (sessionStatus)
155
+ shownStatus: 'idle', // what the host shows: both together (pushStatus)
152
156
  dispatchUsd: 0,
153
157
  serverCost: false,
154
158
  engine: '',
@@ -165,6 +169,9 @@ export function mountAssistant(viewport, { sessionId, brain, host = {}, session
165
169
  desktopProbe: false,
166
170
  routingProbe: false,
167
171
  sessionTitle: String(session?.title || ''), // the server's name (the user's rename, else the first prompt): history + notifications
172
+ // Whether this session holds a chat (a prompt was sent): until then the server keeps it as a draft,
173
+ // New reuses it, and leaving it for another session closes it (nothing of it is stored).
174
+ chatStarted: !!session?.chatStartedAt || Number(session?.turns) > 0,
168
175
  });
169
176
  let recordName = userSessionTitle(session); // the name the session record holds as the user's ('' = never renamed)
170
177
  const controls = new Map(); // requestId → { card, origin, kind }
@@ -315,18 +322,33 @@ export function mountAssistant(viewport, { sessionId, brain, host = {}, session
315
322
  notify(host.notifySource || 'cli', type, notifyLabel(), { sessionId, provider: 'assistant', panel: 'assistant' });
316
323
  }
317
324
 
318
- /** root[data-status] and data-computer drive status styling (the live dot, the Computer switch). */
325
+ /** root[data-status] (the session's status, sessionStatus) and data-computer drive status styling (the live dot, the Computer switch). */
319
326
  function renderStatus() {
320
- root.dataset.status = st.status || 'idle';
327
+ root.dataset.status = st.shownStatus || 'idle';
321
328
  if (st.desktopActive) root.dataset.computer = 'active';
322
329
  else delete root.dataset.computer;
323
330
  }
324
331
 
332
+ /** The brain's own status: its turn ('working', 'done', 'idle') and its cards ('action'). */
325
333
  function setStatus(status) {
326
334
  st.status = status || 'idle';
327
- renderStatus();
335
+ pushStatus(true);
328
336
  syncBlocked();
329
- try { host.setStatus?.(st.status); } catch { /* ignore */ }
337
+ }
338
+
339
+ /**
340
+ * What the host shows (tray pill, header, terminal badge) is the session's status: the brain's own and
341
+ * its dispatched agents' together (asst-state.js sessionStatus), so the gold stays while a worker works
342
+ * after the turn that dispatched it ended, and a worker that waits on you makes the session need you.
343
+ * The brain's own changes always reach the host; a change of the agents' counts only when the status moved.
344
+ */
345
+ function pushStatus(always = false) {
346
+ if (destroyed) return;
347
+ const next = sessionStatus(st.status, st.workers);
348
+ if (!always && next === st.shownStatus) return;
349
+ st.shownStatus = next;
350
+ renderStatus();
351
+ try { host.setStatus?.(next); } catch { /* ignore */ }
330
352
  }
331
353
 
332
354
  /**
@@ -632,13 +654,24 @@ export function mountAssistant(viewport, { sessionId, brain, host = {}, session
632
654
  onStopAll: () => stopAll(),
633
655
  onRemove: (runId) => removeRun(runId),
634
656
  onClearFinished: () => clearFinished(),
657
+ // A mini scene or a lane: that run's card in the transcript (drawn first when this window never got it).
658
+ onReveal: (runId) => {
659
+ if (renderer.revealRun(runId)) return;
660
+ const run = dock.getRun(runId);
661
+ if (run) { renderer.restoreRuns([run]); renderer.revealRun(runId); }
662
+ },
663
+ // Whatever told the tray (the sync bus, its fetch), the transcript's card and route line follow.
664
+ onRunChange: (run) => renderer.syncRun(run),
635
665
  onToggle: (open) => { st.dockOpen = open; agentsBtn.setAttribute('aria-pressed', open ? 'true' : 'false'); persist(); },
636
666
  onShowAllChange: (showAll) => { st.dockShowAll = showAll; persist(); },
637
- onCountChange: ({ total, active, attention, visible: shown }) => {
667
+ onCountChange: ({ total, active, attention, working = 0, waiting = 0, visible: shown }) => {
638
668
  agentsCount.textContent = String(active || total);
639
669
  agentsCount.classList.toggle('attention', attention > 0);
640
670
  agentsBtn.hidden = !shown;
641
671
  agentsBtn.classList.toggle('attention', attention > 0);
672
+ // Whatever told the tray (this socket, the sync bus, its fetch after a reload): the session's status follows.
673
+ st.workers = { working, waiting };
674
+ pushStatus();
642
675
  },
643
676
  });
644
677
  agentsBtn.setAttribute('aria-pressed', st.dockOpen ? 'true' : 'false');
@@ -777,6 +810,8 @@ export function mountAssistant(viewport, { sessionId, brain, host = {}, session
777
810
  lockControl(packet.request_id, resolvedLabel(packet.origin, tt('assistant.control.state.answered', 'Answered')));
778
811
  return;
779
812
  case 'reattach_result':
813
+ // Whether a turn runs now: a route the journal left open is that turn's, or nobody's.
814
+ renderer.reattached(!!(packet.ok && packet.running));
780
815
  if (packet.ok) {
781
816
  if (packet.sessionId) st.providerSessionId = packet.sessionId;
782
817
  if (packet.resynced) rehydrate({ force: true });
@@ -805,6 +840,8 @@ export function mountAssistant(viewport, { sessionId, brain, host = {}, session
805
840
  finishTurn({ aborted: true });
806
841
  return;
807
842
  case 'error': {
843
+ // The server no longer has this session: no error row and no retry loop; the host closes the tab.
844
+ if (packet.code === 'SESSION_NOT_FOUND') { sessionGone(); return; }
808
845
  // A rejected route answer keeps its card open with the message.
809
846
  const routeCard = packet.request_id ? routeCardByRequest(packet.request_id) : null;
810
847
  if (routeCard) { routeCard.fail(packet.message); return; }
@@ -930,6 +967,7 @@ export function mountAssistant(viewport, { sessionId, brain, host = {}, session
930
967
  if (meta.id && meta.id !== sessionId) return;
931
968
  // The server's title names the conversation (history, notifications); the tab's name is the host's.
932
969
  applyRecordName(meta);
970
+ if (meta.chatStartedAt || Number(meta.turns) > 0) st.chatStarted = true;
933
971
  if (meta.brain && typeof meta.brain === 'object') {
934
972
  const next = normalizeBrain({ ...st.brain, ...meta.brain, provider: meta.brain.provider || meta.brain.brain || st.brain.provider });
935
973
  if (JSON.stringify(next) !== JSON.stringify(st.brain)) { st.brain = next; picker.render(); host.setBrain?.(next); persist(); }
@@ -1343,6 +1381,7 @@ export function mountAssistant(viewport, { sessionId, brain, host = {}, session
1343
1381
  st.turnStartedAt = Date.now();
1344
1382
  setRunning(true);
1345
1383
  sock.send(msg);
1384
+ st.chatStarted = true; // the server stores the session with this prompt
1346
1385
  // Name the conversation like the server will (its first prompt) — not the tab.
1347
1386
  if (!st.userRenamed && (!st.sessionTitle || /^(?:SynaBun|Assistant(?: \d+)?)$/.test(st.sessionTitle))) {
1348
1387
  const guess = String(payload.text || '').split('\n')[0].trim().slice(0, 60);
@@ -1504,15 +1543,34 @@ export function mountAssistant(viewport, { sessionId, brain, host = {}, session
1504
1543
  if (!st.running) setRunning(true, { label: tt('assistant.compacting', 'compacting…') });
1505
1544
  }
1506
1545
 
1546
+ /** Nobody chatted in this session yet (and nothing runs in it): New reuses it, leaving it closes it. */
1547
+ function unused() {
1548
+ return !st.chatStarted && !st.running;
1549
+ }
1550
+
1507
1551
  function newSession(provider) {
1508
1552
  const b = provider ? normalizeBrain({ ...st.brain, provider, model: '', effort: null, agent: null, accountId: null }) : st.brain;
1553
+ // This one is still empty: it is the new session (the server keeps one draft, not a pile of them).
1554
+ // /new <provider> switches its brain as the picker does; what only this page showed (/help) is cleared.
1555
+ if (unused() && !destroyed) {
1556
+ if (provider && provider !== st.brain.provider) applyBrain(b, { requiresSwitch: true, field: 'provider' });
1557
+ if (!renderer.isEmpty()) clearTranscript();
1558
+ composer.focus();
1559
+ return;
1560
+ }
1509
1561
  emit('assistant:new', { brain: b, host: hostId });
1510
1562
  }
1511
1563
 
1564
+ /** Open another assistant session; this one goes with it when nobody chatted in it (`replaces`). */
1565
+ function openOther(id) {
1566
+ emit('assistant:resume', { sessionId: id, host: hostId, replaces: unused() ? sessionId : null });
1567
+ }
1568
+
1512
1569
  function resume(id) {
1513
1570
  if (!id) return;
1514
- if (String(id).startsWith('assistant-')) { emit('assistant:resume', { sessionId: id, host: hostId }); return; }
1571
+ if (String(id).startsWith('assistant-')) { if (id !== sessionId) openOther(id); return; }
1515
1572
  sock.send({ type: 'resume', providerSessionId: id });
1573
+ st.chatStarted = true; // a provider conversation: the server stores the session with it
1516
1574
  renderer.appendDivider(tt('assistant.status.resuming', 'resuming {id}', { id: String(id).slice(0, 8) }));
1517
1575
  }
1518
1576
 
@@ -1634,35 +1692,109 @@ export function mountAssistant(viewport, { sessionId, brain, host = {}, session
1634
1692
  }
1635
1693
 
1636
1694
  // ── Menus ──
1695
+ /**
1696
+ * The Sessions menu: New, then every session that holds a chat (the server lists no other). Each row
1697
+ * opens its session; its Remove (hover / focus, → from the row, Delete) asks in the row itself, then
1698
+ * removes the session for good: its running turn and agents stop (the question says so), every host
1699
+ * drops its tab, and the session showing here is replaced by a fresh one, as New does.
1700
+ */
1637
1701
  function showSessionsMenu(list) {
1638
- const sessions = (Array.isArray(list) ? list : []).filter(s => s?.id);
1639
- const items = [
1640
- { id: 'new', label: tt('assistant.topbar.new', 'New assistant session'), desc: brainLabel(st.brain), icon: ICON_NEW, onSelect: () => newSession(null) },
1641
- { kind: 'separator' },
1642
- ];
1643
- if (!sessions.length) items.push({ kind: 'info', label: tt('assistant.sessions.empty', 'No other sessions') });
1644
- else items.push({ kind: 'header', label: tt('assistant.sessions.recent', 'Recent') });
1645
- for (const s of sessions.slice(0, 30)) {
1646
- const isCurrent = s.id === sessionId;
1647
- const b = s.brain && typeof s.brain === 'object' ? s.brain : { provider: s.brain, model: s.model, effort: s.effort };
1648
- items.push({
1649
- kind: 'radio',
1650
- id: s.id,
1651
- label: s.title || s.label || s.id,
1652
- desc: [brainLabel(b), relativeTime(s.updatedAt || s.createdAt), s.status].filter(Boolean).join(' · '),
1653
- selected: isCurrent,
1654
- onSelect: () => { if (!isCurrent) emit('assistant:resume', { sessionId: s.id, host: hostId }); },
1655
- });
1656
- }
1657
- openMenu(sessionsBtn, {
1702
+ let sessions = (Array.isArray(list) ? list : []).filter(s => s?.id);
1703
+ let asking = null; // the id of the row that asks "Remove …?"
1704
+ const nameOf = (s) => s.title || s.label || s.id;
1705
+ const build = () => {
1706
+ const items = [
1707
+ { id: 'new', label: tt('assistant.topbar.new', 'New assistant session'), desc: brainLabel(st.brain), icon: ICON_NEW, onSelect: () => newSession(null) },
1708
+ { kind: 'separator' },
1709
+ ];
1710
+ if (!sessions.length) items.push({ kind: 'info', label: tt('assistant.sessions.empty', 'No other sessions') });
1711
+ else items.push({ kind: 'header', label: tt('assistant.sessions.recent', 'Recent') });
1712
+ for (const s of sessions.slice(0, 30)) {
1713
+ const isCurrent = s.id === sessionId;
1714
+ const name = nameOf(s);
1715
+ if (s.id === asking) { items.push(removeQuestion(s, name, isCurrent)); continue; }
1716
+ const b = s.brain && typeof s.brain === 'object' ? s.brain : { provider: s.brain, model: s.model, effort: s.effort };
1717
+ items.push({
1718
+ kind: 'radio',
1719
+ id: s.id,
1720
+ label: name,
1721
+ desc: [brainLabel(b), relativeTime(s.updatedAt || s.createdAt), s.status].filter(Boolean).join(' · '),
1722
+ selected: isCurrent,
1723
+ onSelect: () => { if (!isCurrent) openOther(s.id); },
1724
+ action: {
1725
+ id: `remove:${s.id}`,
1726
+ icon: ICON_TRASH,
1727
+ label: tt('assistant.sessions.removeNamed', 'Remove “{title}”', { title: name }),
1728
+ tooltip: tt('assistant.sessions.remove', 'Remove session'),
1729
+ onSelect: () => ask(s.id),
1730
+ },
1731
+ });
1732
+ }
1733
+ return items;
1734
+ };
1735
+ /** "Remove “name”? …" with Remove and Cancel, in place of the row (it still matches the filter by name). */
1736
+ const removeQuestion = (s, name, isCurrent) => {
1737
+ const runs = Math.max(0, Number(s.activeRuns) || 0);
1738
+ const running = isCurrent ? st.running : s.running === true;
1739
+ const question = [
1740
+ tt('assistant.sessions.removeAsk', 'Remove “{title}” for good?', { title: name }),
1741
+ running ? tt('assistant.sessions.removeStopsTurn', 'Its running turn stops.') : '',
1742
+ runs ? tt('assistant.sessions.removeStopsAgents', 'Its running agents stop ({count}).', { count: runs }) : '',
1743
+ ].filter(Boolean).join(' ');
1744
+ return {
1745
+ kind: 'buttons',
1746
+ id: `ask:${s.id}`,
1747
+ className: 'asst-dd-confirm',
1748
+ label: question,
1749
+ buttons: [
1750
+ { id: `remove-yes:${s.id}`, label: tt('assistant.sessions.removeConfirm', 'Remove'), danger: true, keywords: name, keepOpen: true, onSelect: () => confirmRemove(s, isCurrent) },
1751
+ { id: `remove-no:${s.id}`, label: tt('common.cancel', 'Cancel'), keywords: name, keepOpen: true, onSelect: () => cancel() },
1752
+ ],
1753
+ };
1754
+ };
1755
+ const handle = openMenu(sessionsBtn, {
1658
1756
  title: tt('assistant.topbar.sessions', 'Sessions'),
1659
- items,
1757
+ items: build(),
1660
1758
  role: 'menu',
1661
1759
  placement: 'below',
1662
1760
  width: 300,
1663
1761
  filter: sessions.length > 8,
1664
1762
  filterPlaceholder: tt('assistant.sessions.filter', 'Filter sessions…'),
1763
+ // Esc while a row asks: back to the row, the menu stays open.
1764
+ onEscape: () => { if (!asking) return false; cancel(); return true; },
1665
1765
  });
1766
+ function ask(id) {
1767
+ asking = id;
1768
+ handle?.update({ items: build(), focus: `remove-no:${id}` });
1769
+ }
1770
+ function cancel() {
1771
+ const id = asking;
1772
+ asking = null;
1773
+ handle?.update({ items: build(), focus: id });
1774
+ }
1775
+ function confirmRemove(s, isCurrent) {
1776
+ asking = null;
1777
+ const name = nameOf(s);
1778
+ const done = (result) => {
1779
+ if (result?.ok) return;
1780
+ const text = tt('assistant.sessions.removeFailed', 'Could not remove “{title}”: {error}', { title: name, error: result?.error || tt('assistant.errorGeneric', 'Something went wrong') });
1781
+ if (destroyed) return;
1782
+ renderer.appendError(text);
1783
+ announce(text);
1784
+ };
1785
+ if (isCurrent) {
1786
+ // This panel's own session: a fresh one takes its place (as New), then this one goes.
1787
+ closeMenu();
1788
+ emit('assistant:remove', { sessionId: s.id, fresh: true, brain: st.brain, done });
1789
+ return;
1790
+ }
1791
+ // Another session (open elsewhere or not): its row goes now, and focus stays in the list.
1792
+ const at = sessions.findIndex((row) => row.id === s.id);
1793
+ sessions = sessions.filter((row) => row.id !== s.id);
1794
+ const next = sessions[Math.min(at, sessions.length - 1)];
1795
+ handle?.update({ items: build(), focus: next ? next.id : 'new' });
1796
+ emit('assistant:remove', { sessionId: s.id, fresh: false, done });
1797
+ }
1666
1798
  }
1667
1799
 
1668
1800
  function engineText() {
@@ -1898,6 +2030,18 @@ export function mountAssistant(viewport, { sessionId, brain, host = {}, session
1898
2030
  const requestId = msg?.request_id || msg?.requestId;
1899
2031
  if (requestId) lockControl(requestId, resolvedLabel(msg.resolvedBy));
1900
2032
  }));
2033
+ // What this session's workers do right now (Claude and Codex workers stream their events to the page,
2034
+ // OpenCode workers a compact copy of their tool calls: lib/opencode-worker-activity.js):
2035
+ // their stages and the tray's scenes follow the current call.
2036
+ unsubs.push(on('sync:sidepanel:provider-event', (msg) => {
2037
+ const runId = msg?.run?.runId;
2038
+ if (!runId || !dock.getRun(runId)) return;
2039
+ const activity = activityOf(msg.event);
2040
+ if (!activity) return;
2041
+ const stamped = { ...activity, at: Date.now() };
2042
+ renderer.setRunActivity(runId, stamped);
2043
+ dock.setRunActivity(runId, stamped);
2044
+ }));
1901
2045
  unsubs.push(on('sync:assistant:desktop', (msg) => computerPanel.applyDesktop(msg)));
1902
2046
  unsubs.push(on('sync:assistant:desktop-setup', (msg) => computerPanel.applySetup(msg)));
1903
2047
  // Models switched on/off elsewhere: the open Models manager reloads (the menus drop their cache on their own).
@@ -1924,6 +2068,19 @@ export function mountAssistant(viewport, { sessionId, brain, host = {}, session
1924
2068
  }
1925
2069
 
1926
2070
  // ── Rehydration ──
2071
+ /**
2072
+ * The server no longer has this session: a draft it dropped (a restart before its first prompt, a
2073
+ * page away too long) or one removed while this page missed the broadcast. The tab closes; one that
2074
+ * was showing with no chat in it is replaced by a fresh session (asst-hosts.js gone), never left dead.
2075
+ */
2076
+ let gone = false;
2077
+ function sessionGone() {
2078
+ if (gone || destroyed) return;
2079
+ gone = true;
2080
+ sock.close();
2081
+ emit('assistant:gone', { sessionId, fresh: visible() && unused(), brain: st.brain });
2082
+ }
2083
+
1927
2084
  async function rehydrate({ force = false } = {}) {
1928
2085
  try {
1929
2086
  const data = await getAssistantSession(sessionId);
@@ -1936,7 +2093,11 @@ export function mountAssistant(viewport, { sessionId, brain, host = {}, session
1936
2093
  // A conversation with nothing in it yet may be greeted; a restored one never is.
1937
2094
  if (Array.isArray(transcript) && !transcript.length && !(Number(meta?.turns) > 0)) renderer.offerGreeting();
1938
2095
  for (const run of (Array.isArray(data?.runs) ? data.runs : [])) dock.upsertRun(run);
2096
+ // Run cards are not journaled: the runs the tray fetched come back into the transcript, next to the
2097
+ // calls that dispatched them, and a worker that still works is live at once (its stage, its route line).
2098
+ dock.ready.then(() => { if (!destroyed) renderer.restoreRuns(dock.getRuns()); });
1939
2099
  } catch (err) {
2100
+ if (err?.status === 404) { sessionGone(); return; }
1940
2101
  console.warn('[assistant] could not restore the conversation', err);
1941
2102
  renderer.showEmpty();
1942
2103
  }
@@ -1951,7 +2112,7 @@ export function mountAssistant(viewport, { sessionId, brain, host = {}, session
1951
2112
  if (session && typeof session === 'object') applySessionMeta(session);
1952
2113
  else applyFeatures();
1953
2114
  rehydrate().finally(() => {
1954
- if (destroyed) return;
2115
+ if (destroyed || gone) return;
1955
2116
  probeFeatures();
1956
2117
  applyLastUsedRouteMode();
1957
2118
  applyLastUsedTestDensity();
@@ -1991,7 +2152,8 @@ export function mountAssistant(viewport, { sessionId, brain, host = {}, session
1991
2152
  attachPath: (path) => { if (path) { composer.insertText(`\`${path}\` `); composer.focus(); } },
1992
2153
  /** The server's name for the conversation (the user's rename, else the first prompt): tooltips. */
1993
2154
  getConversationTitle: () => st.sessionTitle || '',
1994
- getStatus: () => st.status || 'idle',
2155
+ /** The session's status as its host shows it: the brain's own with its dispatched agents' (sessionStatus). */
2156
+ getStatus: () => st.shownStatus || 'idle',
1995
2157
  focus: () => composer.focus(),
1996
2158
  onShown: () => { syncTabLabel(); renderer.scrollEnd(true); if (visible()) composer.focus(); director.touch(); },
1997
2159
  onHidden: () => { closeMenu(); picker.close(); director.stopTyping(); },
@@ -2012,7 +2174,9 @@ export function mountAssistant(viewport, { sessionId, brain, host = {}, session
2012
2174
  unsubs.forEach(off => { try { off(); } catch { /* ignore */ } });
2013
2175
  document.removeEventListener('keydown', onEscCapture, true);
2014
2176
  surfaces.forEach((node) => { node.removeEventListener('keydown', onRootKey); node.removeEventListener('focusin', onFocusIn); });
2015
- closeMenu();
2177
+ // Its own menu closes; another panel's stays open (its Sessions menu may be what removed this session).
2178
+ const menuAnchor = currentMenuAnchor();
2179
+ if (!menuAnchor || !menuAnchor.isConnected || root.contains(menuAnchor) || barEl.contains(menuAnchor)) closeMenu();
2016
2180
  closeFrameLightbox();
2017
2181
  if (helpModal) { helpModal.remove(); helpModal = null; }
2018
2182
  for (const card of routeCards.values()) card.destroy();
@@ -4,7 +4,9 @@
4
4
  // Injected once by asst-render.js as <style id="asst-rack-styles">, right
5
5
  // after the main assistant styles. One rack per burst of agent work: a stage
6
6
  // where the mascot acts out the call it is on (live only), a ledger with one
7
- // row per tool kind, and a receipt once it settles.
7
+ // row per tool kind, and a receipt once it settles. On a direct route the
8
+ // mascot stands over its task class's scene (asst-scenes.js; the motion is
9
+ // asst-scene-styles.js): one column, as wide as the scene.
8
10
  //
9
11
  // Rules: gold only means live (the awake row's ring and tick, the caret, the
10
12
  // sweep); a rack a card holds (data-state="waiting") drops its gold: "Waiting
@@ -81,10 +83,12 @@ export const RACK_CSS = `
81
83
  position: relative;
82
84
  display: grid;
83
85
  grid-template-columns: auto minmax(0, 1fr);
84
- grid-template-areas: "face caption" "face bubble";
86
+ /* The mascot sits level with its bubble (the caption over the bubble), so the notch, near the bubble's top,
87
+ points at the mascot however tall the bubble grows. */
88
+ grid-template-areas: ". caption" "face bubble";
85
89
  align-items: center;
86
90
  column-gap: 8px;
87
- row-gap: 6px;
91
+ row-gap: 0;
88
92
  min-height: 0;
89
93
  overflow: hidden;
90
94
  padding: 0 14px;
@@ -96,12 +100,24 @@ export const RACK_CSS = `
96
100
  visibility: visible;
97
101
  transition: padding var(--rk-moderate) var(--rk-ease), visibility 0s;
98
102
  }
99
- .asst-rack-face { grid-area: face; align-self: center; color: var(--rk-text); line-height: 0; }
103
+ /* The set: the mascot, and under it the direct route's scene (none: the set is the mascot alone). Never between the
104
+ mascot and the bubble's notch: under the mascot here (the bubble is beside it), after it ≤400 (the bubble is under it). */
105
+ .asst-rack-set { grid-area: face; align-self: start; display: flex; flex-direction: column; align-items: center; gap: 4px 6px; min-width: 0; line-height: 0; }
106
+ .asst-rack-face { color: var(--rk-text); line-height: 0; }
100
107
  .asst-rack-face svg { display: block; width: 72px; height: 36px; }
108
+ .asst-rack-scene { color: var(--rk-text-2); line-height: 0; }
109
+ .asst-rack-scene:empty { display: none; }
110
+ /* 96×32 (the 120×40 scene at .8, strokes held near the run stage's weight). */
111
+ .asst-rack-scene .asst-scene { width: 96px; height: 32px; stroke-width: 1.9; }
112
+ /* Gold only means live: none while a card holds the rack or once it settled (the scene holds its frame, dimmed). */
113
+ .asst-rack:not(.is-live) .asst-rack-scene,
114
+ .asst-rack[data-state="waiting"] .asst-rack-scene { --sc-gold: currentColor; }
115
+ .asst-rack[data-state="waiting"] .asst-rack-scene { opacity: .55; }
101
116
  .asst-rack-caption {
102
117
  grid-area: caption;
103
118
  align-self: end;
104
119
  min-width: 0;
120
+ margin-bottom: 6px; /* the gap over the bubble: an empty caption takes no room */
105
121
  overflow: hidden;
106
122
  display: -webkit-box;
107
123
  -webkit-box-orient: vertical;
@@ -111,7 +127,6 @@ export const RACK_CSS = `
111
127
  line-height: 1.45;
112
128
  }
113
129
  .asst-rack-caption:empty { display: none; }
114
- .asst-rack-caption:empty ~ .asst-bubble { grid-row: 1 / span 2; }
115
130
 
116
131
  .asst-bubble {
117
132
  grid-area: bubble;
@@ -528,15 +543,17 @@ export const RACK_CSS = `
528
543
 
529
544
  /* ── Narrow containers ─────────────────────────────────────────────
530
545
  ≤400: the face (still 72×36) sits over the bubble, whose notch points up
531
- at it; the rows keep the last 12 ticks and drop the time (the receipt and
546
+ at it (a direct route's scene goes after the face, never between the two);
547
+ the rows keep the last 12 ticks and drop the time (the receipt and
532
548
  the Timeline carry it). ≤320: only the bubble's meta goes; its subject
533
549
  wraps to two lines, cut from the head so the file name survives, and the
534
- error keeps two lines. Under a 300px container the face is 56×28. */
550
+ error keeps two lines. Under a 300px container the face is 56×28 and a
551
+ direct route's scene beside it 72×24 (96×32 above). */
535
552
  @container asst (max-width: 400px) {
536
- .asst-rack-stage { grid-template-columns: auto minmax(0, 1fr); grid-template-areas: "face caption" "bubble bubble"; column-gap: 8px; }
553
+ .asst-rack-stage { grid-template-columns: auto minmax(0, 1fr); grid-template-areas: "face caption" "bubble bubble"; column-gap: 8px; row-gap: 6px; }
554
+ .asst-rack-set { align-self: center; flex-direction: row; }
537
555
  .asst-rack.is-staged > .asst-rack-stage { padding: 10px 10px 10px; }
538
- .asst-rack-caption { align-self: center; }
539
- .asst-rack-caption:empty ~ .asst-bubble { grid-row: auto; }
556
+ .asst-rack-caption { align-self: center; margin-bottom: 0; }
540
557
  .asst-bubble { justify-self: stretch; max-width: 100%; margin: 4px 0 0; border-radius: 3px 12px 12px 12px; }
541
558
  .asst-bubble::before { left: 30px; top: -5.5px; box-shadow: inset 1px 1px 0 var(--rk-line); clip-path: polygon(0 100%, 0 0, 100% 0); }
542
559
  .asst-station-hdr { gap: 8px; padding: 0 6px; }
@@ -554,6 +571,7 @@ export const RACK_CSS = `
554
571
  }
555
572
  @container asst (max-width: 299px) {
556
573
  .asst-rack-face svg { width: 56px; height: 28px; }
574
+ .asst-rack-scene .asst-scene { width: 72px; height: 24px; stroke-width: 2.2; }
557
575
  .asst-bubble::before { left: 22px; }
558
576
  }
559
577