synabun 2.0.0-beta → 2.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (374) hide show
  1. package/CHANGELOG.md +65 -0
  2. package/NOTICE +5 -0
  3. package/README.md +1 -1
  4. package/THIRD-PARTY-LICENSES.md +51 -0
  5. package/benchmarks/fixtures/browser/README.md +1 -1
  6. package/benchmarks/fixtures/desktop/README.md +1 -1
  7. package/docs/whatsapp.md +2 -2
  8. package/hooks/claude-code/pre-compact.mjs +11 -0
  9. package/hooks/claude-code/prompt-submit.mjs +60 -184
  10. package/hooks/claude-code/session-start.mjs +73 -16
  11. package/hooks/claude-code/shared.mjs +20 -0
  12. package/hooks/claude-code/stop.mjs +5 -5
  13. package/launcher.mjs +40 -0
  14. package/lib/claude-hooks.js +61 -8
  15. package/lib/client-config-repair.js +4 -3
  16. package/lib/data-home-migration.js +4 -0
  17. package/lib/desktop-pwa.js +206 -0
  18. package/lib/external-tools.js +221 -0
  19. package/lib/first-launch-protection.js +5 -0
  20. package/lib/packaged-runtime.js +110 -0
  21. package/lib/start-launcher.js +1396 -0
  22. package/mcp-server/package-lock.json +2 -2
  23. package/mcp-server/package.json +1 -3
  24. package/neural-interface/i18n/en.json +1540 -154
  25. package/neural-interface/i18n/pt-BR.json +1538 -153
  26. package/neural-interface/lib/assistant-api.js +62 -6
  27. package/neural-interface/lib/assistant-brains/claude.js +4 -0
  28. package/neural-interface/lib/assistant-catalog.js +179 -27
  29. package/neural-interface/lib/assistant-config.js +31 -2
  30. package/neural-interface/lib/assistant-dispatch.js +62 -13
  31. package/neural-interface/lib/assistant-envelope.js +9 -6
  32. package/neural-interface/lib/assistant-hidden-models.js +393 -66
  33. package/neural-interface/lib/assistant-memory.js +24 -10
  34. package/neural-interface/lib/assistant-persona.js +6 -2
  35. package/neural-interface/lib/assistant-pricing.js +21 -6
  36. package/neural-interface/lib/assistant-runtime.js +181 -12
  37. package/neural-interface/lib/assistant-task-prompt.js +13 -3
  38. package/neural-interface/lib/assistant-test-density.js +142 -0
  39. package/neural-interface/lib/assistant-user-context-budgets.js +123 -0
  40. package/neural-interface/lib/assistant-user-context.js +908 -0
  41. package/neural-interface/lib/assistant-user-knowledge.js +328 -0
  42. package/neural-interface/lib/assistant-user-learn.js +404 -0
  43. package/neural-interface/lib/assistant-user-threads.js +226 -0
  44. package/neural-interface/lib/assistant-user-writer.js +181 -0
  45. package/neural-interface/lib/augmented-path.js +23 -3
  46. package/neural-interface/lib/browser-background-tab.js +134 -0
  47. package/neural-interface/lib/browser-language.js +83 -0
  48. package/neural-interface/lib/browser-screenshot.js +678 -61
  49. package/neural-interface/lib/claude-agent-bridge.js +87 -153
  50. package/neural-interface/lib/claude-executable.js +241 -0
  51. package/neural-interface/lib/claude-model-catalog.js +82 -93
  52. package/neural-interface/lib/claude-temporary.js +46 -7
  53. package/neural-interface/lib/cloudflared/install.js +533 -0
  54. package/neural-interface/lib/cloudflared/manager.js +545 -0
  55. package/neural-interface/lib/cloudflared/pins.js +115 -0
  56. package/neural-interface/lib/cloudflared/pins.json +63 -0
  57. package/neural-interface/lib/cloudflared/tunnel.js +265 -0
  58. package/neural-interface/lib/editor-completion-endpoint.js +264 -0
  59. package/neural-interface/lib/editor-completion-opencode.js +436 -0
  60. package/neural-interface/lib/editor-completion.js +714 -0
  61. package/neural-interface/lib/editor-config.js +149 -0
  62. package/neural-interface/lib/editor-files-api.js +268 -0
  63. package/neural-interface/lib/editor-git-api.js +134 -0
  64. package/neural-interface/lib/file-explorer-api.js +1039 -0
  65. package/neural-interface/lib/file-explorer-system.js +355 -0
  66. package/neural-interface/lib/file-explorer-trash.ps1 +85 -0
  67. package/neural-interface/lib/file-explorer-watch.js +239 -0
  68. package/neural-interface/lib/git-diff-api.js +103 -0
  69. package/neural-interface/lib/git-read.js +202 -0
  70. package/neural-interface/lib/greeting/api.js +89 -0
  71. package/neural-interface/lib/greeting/budgets.js +90 -0
  72. package/neural-interface/lib/greeting/engine.js +461 -0
  73. package/neural-interface/lib/greeting/facts.js +320 -0
  74. package/neural-interface/lib/greeting/sources.js +252 -0
  75. package/neural-interface/lib/greeting/writer.js +139 -0
  76. package/neural-interface/lib/http-guards.js +189 -13
  77. package/neural-interface/lib/machine-names.js +88 -0
  78. package/neural-interface/lib/memory-api.js +31 -7
  79. package/neural-interface/lib/native-binary-runtime.js +69 -337
  80. package/neural-interface/lib/native-loop-providers.js +40 -49
  81. package/neural-interface/lib/offline-page.js +54 -0
  82. package/neural-interface/lib/plugin-installer.js +4 -2
  83. package/neural-interface/lib/project-storage.js +5 -7
  84. package/neural-interface/lib/request-gate.js +589 -0
  85. package/neural-interface/lib/session-title-generator.js +5 -0
  86. package/neural-interface/lib/synabun-update-plan.js +19 -0
  87. package/neural-interface/lib/ui-blob-api.js +108 -0
  88. package/neural-interface/lib/ui-blob-store.js +68 -0
  89. package/neural-interface/package-lock.json +701 -253
  90. package/neural-interface/package.json +13 -2
  91. package/neural-interface/public/install-app.html +34 -0
  92. package/neural-interface/public/manifest.json +2 -0
  93. package/neural-interface/public/offline.html +402 -66
  94. package/neural-interface/public/onboarding.html +264 -147
  95. package/neural-interface/public/shared/agent-runtime-options.js +22 -6
  96. package/neural-interface/public/shared/assistant/asst-automation.js +286 -0
  97. package/neural-interface/public/shared/assistant/asst-brain-picker.js +312 -62
  98. package/neural-interface/public/shared/assistant/asst-composer.js +11 -1
  99. package/neural-interface/public/shared/assistant/asst-mascot.js +26 -64
  100. package/neural-interface/public/shared/assistant/asst-menu.js +12 -8
  101. package/neural-interface/public/shared/assistant/asst-panel.js +150 -28
  102. package/neural-interface/public/shared/assistant/asst-rack-styles.js +0 -3
  103. package/neural-interface/public/shared/assistant/asst-render.js +219 -67
  104. package/neural-interface/public/shared/assistant/asst-route-models.js +108 -0
  105. package/neural-interface/public/shared/assistant/asst-route.js +23 -329
  106. package/neural-interface/public/shared/assistant/asst-routes-editor.js +495 -0
  107. package/neural-interface/public/shared/assistant/asst-sanitize.js +8 -1
  108. package/neural-interface/public/shared/assistant/asst-sidepanel.js +31 -9
  109. package/neural-interface/public/shared/assistant/asst-state.js +185 -1
  110. package/neural-interface/public/shared/assistant/asst-styles.js +162 -6
  111. package/neural-interface/public/shared/blob-store.js +111 -22
  112. package/neural-interface/public/shared/cdx/cdx-panel.js +32 -11
  113. package/neural-interface/public/shared/cdx/cdx-render.js +25 -89
  114. package/neural-interface/public/shared/cdx/cdx-styles.js +36 -0
  115. package/neural-interface/public/shared/cdx/cdx-tabs.js +87 -4
  116. package/neural-interface/public/shared/command-runner-model.js +30 -0
  117. package/neural-interface/public/shared/constants.js +6 -1
  118. package/neural-interface/public/shared/cp/cp-rehydrate.js +2 -1
  119. package/neural-interface/public/shared/cp/cp-styles.js +15 -0
  120. package/neural-interface/public/shared/editor/editor-ai-logic.js +409 -0
  121. package/neural-interface/public/shared/editor/editor-ai.js +619 -0
  122. package/neural-interface/public/shared/editor/editor-commands.js +212 -0
  123. package/neural-interface/public/shared/editor/editor-compare.js +103 -0
  124. package/neural-interface/public/shared/editor/editor-completion-list.js +172 -0
  125. package/neural-interface/public/shared/editor/editor-doc.js +257 -0
  126. package/neural-interface/public/shared/editor/editor-git-logic.js +131 -0
  127. package/neural-interface/public/shared/editor/editor-git.js +95 -0
  128. package/neural-interface/public/shared/editor/editor-languages.js +156 -0
  129. package/neural-interface/public/shared/editor/editor-lint.js +229 -0
  130. package/neural-interface/public/shared/editor/editor-loader.js +51 -0
  131. package/neural-interface/public/shared/editor/editor-markdown.js +90 -0
  132. package/neural-interface/public/shared/editor/editor-menu.js +233 -0
  133. package/neural-interface/public/shared/editor/editor-minimap.js +371 -0
  134. package/neural-interface/public/shared/editor/editor-palette-logic.js +192 -0
  135. package/neural-interface/public/shared/editor/editor-palette.js +125 -0
  136. package/neural-interface/public/shared/editor/editor-panel-logic.js +288 -0
  137. package/neural-interface/public/shared/editor/editor-panel.js +2099 -0
  138. package/neural-interface/public/shared/editor/editor-prefs.js +224 -0
  139. package/neural-interface/public/shared/editor/editor-safety.js +500 -0
  140. package/neural-interface/public/shared/editor/editor-search.js +352 -0
  141. package/neural-interface/public/shared/editor/editor-statusbar.js +144 -0
  142. package/neural-interface/public/shared/editor/editor-tabs-logic.js +331 -0
  143. package/neural-interface/public/shared/editor/editor-tabs.js +233 -0
  144. package/neural-interface/public/shared/editor/editor-theme.js +244 -0
  145. package/neural-interface/public/shared/editor/editor-view.js +332 -0
  146. package/neural-interface/public/shared/explorer/fx-api.js +216 -0
  147. package/neural-interface/public/shared/explorer/fx-links.js +204 -0
  148. package/neural-interface/public/shared/explorer/fx-logic.js +537 -0
  149. package/neural-interface/public/shared/explorer/fx-main.js +865 -0
  150. package/neural-interface/public/shared/explorer/fx-names.js +87 -0
  151. package/neural-interface/public/shared/explorer/fx-ops.js +370 -0
  152. package/neural-interface/public/shared/explorer/fx-popups.js +490 -0
  153. package/neural-interface/public/shared/explorer/fx-tree.js +790 -0
  154. package/neural-interface/public/shared/file-links.js +339 -0
  155. package/neural-interface/public/shared/greeting/greeting-card.js +178 -0
  156. package/neural-interface/public/shared/greeting/greeting-view.js +148 -0
  157. package/neural-interface/public/shared/html-shell.js +169 -170
  158. package/neural-interface/public/shared/ocp-v2/ocp-v2-childpanel.js +4 -0
  159. package/neural-interface/public/shared/ocp-v2/ocp-v2-greeting.js +47 -0
  160. package/neural-interface/public/shared/ocp-v2/ocp-v2-panel.js +99 -17
  161. package/neural-interface/public/shared/ocp-v2/ocp-v2-render.js +7 -1
  162. package/neural-interface/public/shared/ocp-v2/ocp-v2-send.js +5 -0
  163. package/neural-interface/public/shared/ocp-v2/ocp-v2-styles.js +40 -0
  164. package/neural-interface/public/shared/ocp-v2/ocp-v2-ws.js +3 -2
  165. package/neural-interface/public/shared/pwa-install-page.js +62 -0
  166. package/neural-interface/public/shared/pwa-install.css +12 -0
  167. package/neural-interface/public/shared/pwa-install.js +127 -0
  168. package/neural-interface/public/shared/settings/settings-code-editor-pane.js +412 -0
  169. package/neural-interface/public/shared/settings/settings-code-editor.js +328 -0
  170. package/neural-interface/public/shared/settings/settings-greeting.js +133 -0
  171. package/neural-interface/public/shared/settings/settings-ia.js +18 -4
  172. package/neural-interface/public/shared/settings/settings-rules.js +160 -0
  173. package/neural-interface/public/shared/settings/settings-user-context.js +154 -0
  174. package/neural-interface/public/shared/start-bridge.js +802 -0
  175. package/neural-interface/public/shared/styleguide/sg-colors.js +5 -2
  176. package/neural-interface/public/shared/styleguide/sg-import-export.js +4 -3
  177. package/neural-interface/public/shared/styleguide/sg-kit.js +4 -1
  178. package/neural-interface/public/shared/styleguide/sg-panel.js +9 -1
  179. package/neural-interface/public/shared/styleguide/sg-typography.js +1 -0
  180. package/neural-interface/public/shared/styles.css +2219 -1117
  181. package/neural-interface/public/shared/synabun-mascot.js +20 -56
  182. package/neural-interface/public/shared/term-ready.js +307 -0
  183. package/neural-interface/public/shared/tunnel-view.js +167 -0
  184. package/neural-interface/public/shared/ui-claude-panel.js +160 -19
  185. package/neural-interface/public/shared/ui-command-runner.js +251 -468
  186. package/neural-interface/public/shared/ui-detail.js +15 -0
  187. package/neural-interface/public/shared/ui-explorer.js +22 -0
  188. package/neural-interface/public/shared/ui-file-explorer.js +481 -2496
  189. package/neural-interface/public/shared/ui-file-links.js +103 -0
  190. package/neural-interface/public/shared/ui-help.js +26 -0
  191. package/neural-interface/public/shared/ui-invite.js +126 -116
  192. package/neural-interface/public/shared/ui-judgments.js +1 -1
  193. package/neural-interface/public/shared/ui-keybinds.js +4 -0
  194. package/neural-interface/public/shared/ui-loading.js +367 -12
  195. package/neural-interface/public/shared/ui-menubar.js +21 -55
  196. package/neural-interface/public/shared/ui-native-loop-router-state.js +11 -0
  197. package/neural-interface/public/shared/ui-native-loop-router.js +15 -2
  198. package/neural-interface/public/shared/ui-navbar.js +45 -21
  199. package/neural-interface/public/shared/ui-settings.js +491 -547
  200. package/neural-interface/public/shared/ui-sidepanel-runtime.js +2 -1
  201. package/neural-interface/public/shared/ui-sync.js +16 -0
  202. package/neural-interface/public/shared/ui-terminal.js +153 -149
  203. package/neural-interface/public/shared/ui-tunnel.js +130 -0
  204. package/neural-interface/public/shared/ui-tutorial-steps.js +6 -2
  205. package/neural-interface/public/shared/ui-whiteboard.js +12 -0
  206. package/neural-interface/public/shared/ui-worker-end.js +245 -0
  207. package/neural-interface/public/shared/ui-workspaces.js +41 -27
  208. package/neural-interface/public/shared/worker-end.js +251 -0
  209. package/neural-interface/public/sw.js +3 -1
  210. package/neural-interface/public/variant/2d/main.js +2 -1
  211. package/neural-interface/public/variant/3d/main.js +2 -1
  212. package/neural-interface/public/vendor/codemirror/chunks/apl-CSDKT72Z.js +1 -0
  213. package/neural-interface/public/vendor/codemirror/chunks/asciiarmor-JYYPU5BM.js +1 -0
  214. package/neural-interface/public/vendor/codemirror/chunks/asn1-NRWCZDA6.js +1 -0
  215. package/neural-interface/public/vendor/codemirror/chunks/asterisk-5OU6D4ZQ.js +1 -0
  216. package/neural-interface/public/vendor/codemirror/chunks/brainfuck-ID4N4SYC.js +1 -0
  217. package/neural-interface/public/vendor/codemirror/chunks/chunk-4RZWGCS2.js +1 -0
  218. package/neural-interface/public/vendor/codemirror/chunks/chunk-ACTJM3JR.js +11 -0
  219. package/neural-interface/public/vendor/codemirror/chunks/chunk-S6BMAXLK.js +1 -0
  220. package/neural-interface/public/vendor/codemirror/chunks/chunk-TO2RDOHQ.js +3 -0
  221. package/neural-interface/public/vendor/codemirror/chunks/chunk-US2GMZZQ.js +13 -0
  222. package/neural-interface/public/vendor/codemirror/chunks/chunk-WYYO5ZAJ.js +1 -0
  223. package/neural-interface/public/vendor/codemirror/chunks/chunk-XALZTDJC.js +1 -0
  224. package/neural-interface/public/vendor/codemirror/chunks/chunk-YRNUTRDV.js +1 -0
  225. package/neural-interface/public/vendor/codemirror/chunks/chunk-Z3QOKH62.js +1 -0
  226. package/neural-interface/public/vendor/codemirror/chunks/clike-OWQBSOGL.js +1 -0
  227. package/neural-interface/public/vendor/codemirror/chunks/clojure-KV75UQWC.js +1 -0
  228. package/neural-interface/public/vendor/codemirror/chunks/cmake-GTEP5Y3C.js +1 -0
  229. package/neural-interface/public/vendor/codemirror/chunks/cobol-WCZ7QEGJ.js +1 -0
  230. package/neural-interface/public/vendor/codemirror/chunks/coffeescript-TGLBOT6B.js +1 -0
  231. package/neural-interface/public/vendor/codemirror/chunks/commonlisp-WVVI432V.js +1 -0
  232. package/neural-interface/public/vendor/codemirror/chunks/crystal-S6R6HLKQ.js +1 -0
  233. package/neural-interface/public/vendor/codemirror/chunks/css-PDA2RINP.js +1 -0
  234. package/neural-interface/public/vendor/codemirror/chunks/cypher-HL5QPL4C.js +1 -0
  235. package/neural-interface/public/vendor/codemirror/chunks/d-HHJH2HAJ.js +1 -0
  236. package/neural-interface/public/vendor/codemirror/chunks/diff-MN3A35BY.js +1 -0
  237. package/neural-interface/public/vendor/codemirror/chunks/dist-25SQLBVS.js +3 -0
  238. package/neural-interface/public/vendor/codemirror/chunks/dist-27ZGGJJS.js +1 -0
  239. package/neural-interface/public/vendor/codemirror/chunks/dist-4E52OJIF.js +1 -0
  240. package/neural-interface/public/vendor/codemirror/chunks/dist-6FT73XDC.js +2 -0
  241. package/neural-interface/public/vendor/codemirror/chunks/dist-BE6YGRMW.js +1 -0
  242. package/neural-interface/public/vendor/codemirror/chunks/dist-CJSGKW2O.js +1 -0
  243. package/neural-interface/public/vendor/codemirror/chunks/dist-CRPH2WLI.js +1 -0
  244. package/neural-interface/public/vendor/codemirror/chunks/dist-CTG7JEQN.js +1 -0
  245. package/neural-interface/public/vendor/codemirror/chunks/dist-I3FEVELX.js +1 -0
  246. package/neural-interface/public/vendor/codemirror/chunks/dist-IM5BBSWZ.js +1 -0
  247. package/neural-interface/public/vendor/codemirror/chunks/dist-KERUKTIF.js +1 -0
  248. package/neural-interface/public/vendor/codemirror/chunks/dist-LXTMWZAM.js +1 -0
  249. package/neural-interface/public/vendor/codemirror/chunks/dist-MABASSET.js +1 -0
  250. package/neural-interface/public/vendor/codemirror/chunks/dist-MJVBUZLL.js +3 -0
  251. package/neural-interface/public/vendor/codemirror/chunks/dist-MNB3WZVU.js +1 -0
  252. package/neural-interface/public/vendor/codemirror/chunks/dist-QYWXOE3U.js +7 -0
  253. package/neural-interface/public/vendor/codemirror/chunks/dist-TEBOFWLZ.js +1 -0
  254. package/neural-interface/public/vendor/codemirror/chunks/dist-UNXFROM3.js +1 -0
  255. package/neural-interface/public/vendor/codemirror/chunks/dist-XL2SLQO6.js +1 -0
  256. package/neural-interface/public/vendor/codemirror/chunks/dist-XO42Y3AP.js +6 -0
  257. package/neural-interface/public/vendor/codemirror/chunks/dist-Y2WWDWJK.js +1 -0
  258. package/neural-interface/public/vendor/codemirror/chunks/dist-YFEZKWMU.js +1 -0
  259. package/neural-interface/public/vendor/codemirror/chunks/dist-ZY7SWQO6.js +1 -0
  260. package/neural-interface/public/vendor/codemirror/chunks/dockerfile-3Y4R4NCH.js +1 -0
  261. package/neural-interface/public/vendor/codemirror/chunks/dtd-5343GJ4P.js +1 -0
  262. package/neural-interface/public/vendor/codemirror/chunks/dylan-FI2CXSW2.js +1 -0
  263. package/neural-interface/public/vendor/codemirror/chunks/ebnf-GNQ77YWA.js +1 -0
  264. package/neural-interface/public/vendor/codemirror/chunks/ecl-MRPUAE2L.js +1 -0
  265. package/neural-interface/public/vendor/codemirror/chunks/eiffel-DZROES27.js +1 -0
  266. package/neural-interface/public/vendor/codemirror/chunks/elm-4KICRFKQ.js +1 -0
  267. package/neural-interface/public/vendor/codemirror/chunks/erlang-3IHYC263.js +1 -0
  268. package/neural-interface/public/vendor/codemirror/chunks/factor-C6D3E5UK.js +1 -0
  269. package/neural-interface/public/vendor/codemirror/chunks/fcl-VYTWUJMI.js +1 -0
  270. package/neural-interface/public/vendor/codemirror/chunks/forth-ZBO2EH5M.js +1 -0
  271. package/neural-interface/public/vendor/codemirror/chunks/fortran-5NDCRWIK.js +1 -0
  272. package/neural-interface/public/vendor/codemirror/chunks/gas-SKRMOXFP.js +1 -0
  273. package/neural-interface/public/vendor/codemirror/chunks/gherkin-L45YXZLW.js +1 -0
  274. package/neural-interface/public/vendor/codemirror/chunks/groovy-GLPTLN44.js +1 -0
  275. package/neural-interface/public/vendor/codemirror/chunks/haskell-E7NF7P4Y.js +1 -0
  276. package/neural-interface/public/vendor/codemirror/chunks/haxe-K2NVDOB6.js +1 -0
  277. package/neural-interface/public/vendor/codemirror/chunks/http-Q6DRQIJB.js +1 -0
  278. package/neural-interface/public/vendor/codemirror/chunks/idl-MZYNJVLN.js +1 -0
  279. package/neural-interface/public/vendor/codemirror/chunks/javascript-6YRYK2HR.js +1 -0
  280. package/neural-interface/public/vendor/codemirror/chunks/julia-HXEL2T5G.js +1 -0
  281. package/neural-interface/public/vendor/codemirror/chunks/livescript-MV7CI2JZ.js +1 -0
  282. package/neural-interface/public/vendor/codemirror/chunks/lua-E36NHOOD.js +1 -0
  283. package/neural-interface/public/vendor/codemirror/chunks/mathematica-3XOZZCMC.js +1 -0
  284. package/neural-interface/public/vendor/codemirror/chunks/mbox-ODYGFKS7.js +1 -0
  285. package/neural-interface/public/vendor/codemirror/chunks/mirc-WB43AWF5.js +1 -0
  286. package/neural-interface/public/vendor/codemirror/chunks/mllike-ZT6ET7ZH.js +1 -0
  287. package/neural-interface/public/vendor/codemirror/chunks/modelica-LLMZPFGA.js +1 -0
  288. package/neural-interface/public/vendor/codemirror/chunks/mscgen-2OPNNYFH.js +1 -0
  289. package/neural-interface/public/vendor/codemirror/chunks/mumps-6P3HWJH6.js +1 -0
  290. package/neural-interface/public/vendor/codemirror/chunks/nginx-TCGKQTRW.js +1 -0
  291. package/neural-interface/public/vendor/codemirror/chunks/nsis-ATIGIXTJ.js +1 -0
  292. package/neural-interface/public/vendor/codemirror/chunks/ntriples-5PLBI5OV.js +1 -0
  293. package/neural-interface/public/vendor/codemirror/chunks/octave-QF2S3NL6.js +1 -0
  294. package/neural-interface/public/vendor/codemirror/chunks/oz-JFIVFFH2.js +1 -0
  295. package/neural-interface/public/vendor/codemirror/chunks/pascal-JG6SW76V.js +1 -0
  296. package/neural-interface/public/vendor/codemirror/chunks/perl-4B3IWVVO.js +1 -0
  297. package/neural-interface/public/vendor/codemirror/chunks/pig-ABEBH4MI.js +1 -0
  298. package/neural-interface/public/vendor/codemirror/chunks/powershell-H32XE7TL.js +1 -0
  299. package/neural-interface/public/vendor/codemirror/chunks/properties-NN644MMA.js +1 -0
  300. package/neural-interface/public/vendor/codemirror/chunks/protobuf-YKWQK2J3.js +1 -0
  301. package/neural-interface/public/vendor/codemirror/chunks/pug-RIYXEKFW.js +1 -0
  302. package/neural-interface/public/vendor/codemirror/chunks/puppet-JZQ5WPEV.js +1 -0
  303. package/neural-interface/public/vendor/codemirror/chunks/python-EBCGGHAC.js +1 -0
  304. package/neural-interface/public/vendor/codemirror/chunks/q-5UMH2YQY.js +1 -0
  305. package/neural-interface/public/vendor/codemirror/chunks/r-7XMGG453.js +1 -0
  306. package/neural-interface/public/vendor/codemirror/chunks/rpm-IEV7TSLR.js +1 -0
  307. package/neural-interface/public/vendor/codemirror/chunks/ruby-T4SPFPNM.js +1 -0
  308. package/neural-interface/public/vendor/codemirror/chunks/sas-GEDX67ET.js +1 -0
  309. package/neural-interface/public/vendor/codemirror/chunks/scheme-FTLOWKPC.js +1 -0
  310. package/neural-interface/public/vendor/codemirror/chunks/shell-HUFRKFSJ.js +1 -0
  311. package/neural-interface/public/vendor/codemirror/chunks/sieve-P5QUDVYO.js +1 -0
  312. package/neural-interface/public/vendor/codemirror/chunks/smalltalk-2SORJNQI.js +1 -0
  313. package/neural-interface/public/vendor/codemirror/chunks/solr-M22TGAR5.js +1 -0
  314. package/neural-interface/public/vendor/codemirror/chunks/sparql-4M3KL752.js +1 -0
  315. package/neural-interface/public/vendor/codemirror/chunks/spreadsheet-3K23LA4N.js +1 -0
  316. package/neural-interface/public/vendor/codemirror/chunks/sql-VHRFMUBI.js +1 -0
  317. package/neural-interface/public/vendor/codemirror/chunks/stex-QZDAQUY2.js +1 -0
  318. package/neural-interface/public/vendor/codemirror/chunks/stylus-7ZDKAKBD.js +1 -0
  319. package/neural-interface/public/vendor/codemirror/chunks/swift-WCLCLGMR.js +1 -0
  320. package/neural-interface/public/vendor/codemirror/chunks/tcl-JLMU6WOK.js +1 -0
  321. package/neural-interface/public/vendor/codemirror/chunks/textile-4CHDKZ4T.js +1 -0
  322. package/neural-interface/public/vendor/codemirror/chunks/tiddlywiki-DKMCZZQ4.js +1 -0
  323. package/neural-interface/public/vendor/codemirror/chunks/tiki-F4JIG36Q.js +1 -0
  324. package/neural-interface/public/vendor/codemirror/chunks/toml-BHMHTIM2.js +1 -0
  325. package/neural-interface/public/vendor/codemirror/chunks/troff-YQ2KV5NT.js +1 -0
  326. package/neural-interface/public/vendor/codemirror/chunks/ttcn-ZHRCVFJY.js +1 -0
  327. package/neural-interface/public/vendor/codemirror/chunks/ttcn-cfg-KJOVTY4X.js +1 -0
  328. package/neural-interface/public/vendor/codemirror/chunks/turtle-535P5N6I.js +1 -0
  329. package/neural-interface/public/vendor/codemirror/chunks/vb-76UMQHOT.js +1 -0
  330. package/neural-interface/public/vendor/codemirror/chunks/vbscript-CGNDTK6Z.js +1 -0
  331. package/neural-interface/public/vendor/codemirror/chunks/velocity-VSJDO5T5.js +1 -0
  332. package/neural-interface/public/vendor/codemirror/chunks/verilog-4T6ETXIP.js +1 -0
  333. package/neural-interface/public/vendor/codemirror/chunks/vhdl-T7FNAXUN.js +1 -0
  334. package/neural-interface/public/vendor/codemirror/chunks/webidl-P6MJSJ6J.js +1 -0
  335. package/neural-interface/public/vendor/codemirror/chunks/xquery-UOVILRGN.js +1 -0
  336. package/neural-interface/public/vendor/codemirror/chunks/yacas-CV2APROL.js +1 -0
  337. package/neural-interface/public/vendor/codemirror/chunks/z80-3KFNVM4M.js +1 -0
  338. package/neural-interface/public/vendor/codemirror/codemirror-6.43.14-75f05e67.min.js +2 -0
  339. package/neural-interface/public/vendor/codemirror/manifest.json +60 -0
  340. package/neural-interface/scripts/build-vendor.mjs +78 -2
  341. package/neural-interface/scripts/rebuild-pty.js +15 -12
  342. package/neural-interface/scripts/vendor-codemirror-entry.js +121 -0
  343. package/neural-interface/server.js +1092 -1212
  344. package/neural-interface/templates/rulesets/README.md +1 -1
  345. package/package.json +13 -7
  346. package/postinstall.js +11 -81
  347. package/setup.js +111 -6
  348. package/benchmarks/browser-live-results.json +0 -79
  349. package/benchmarks/browser-v2-results.json +0 -258
  350. package/docs/browser-v2.md +0 -109
  351. package/docs/hook-integrity.md +0 -89
  352. package/docs/judgments.md +0 -182
  353. package/docs/style-guide.md +0 -246
  354. package/neural-interface/lib/codex-capabilities.d.ts +0 -41
  355. package/neural-interface/public/category-logos/claude.svg +0 -1
  356. package/neural-interface/public/category-logos/gemini-color.svg +0 -1
  357. package/neural-interface/public/category-logos/openai.svg +0 -1
  358. package/neural-interface/public/category-logos/synabun.png +0 -0
  359. package/neural-interface/public/claude.svg +0 -1
  360. package/neural-interface/public/gallery.svg +0 -6
  361. package/neural-interface/public/leonardoai.svg +0 -1
  362. package/neural-interface/public/mcp.svg +0 -4
  363. package/neural-interface/public/opencode-logo-dark-square.svg +0 -1
  364. package/neural-interface/public/pin-svgrepo-com.svg +0 -25
  365. package/neural-interface/public/sessionicon.svg +0 -8
  366. package/neural-interface/public/toolbar-album.svg +0 -8
  367. package/neural-interface/public/toolbar-question.svg +0 -6
  368. package/neural-interface/public/toolbar-sessions.svg +0 -6
  369. package/neural-interface/scripts/codex-protocol-fixture.mjs +0 -83
  370. package/scripts/audit-claude-hook-usage.mjs +0 -12
  371. package/scripts/audit-memory-duplicates.mjs +0 -44
  372. package/scripts/measure-claude-hook-overhead.mjs +0 -77
  373. package/scripts/recover-user-config.mjs +0 -254
  374. package/skills/synabun/synabunicon.png +0 -0
@@ -0,0 +1,167 @@
1
+ // ═══════════════════════════════════════════
2
+ // SynaBun Neural Interface — the public address: what the cards say
3
+ // ═══════════════════════════════════════════
4
+ //
5
+ // What the two places that start the Cloudflare tunnel show for an answer of
6
+ // GET /api/tunnel/status: the Share menu's Connection block (ui-invite.js) and
7
+ // Settings → Automations → Online access (ui-settings.js). One reading for
8
+ // both, so they never disagree: the state as a word, one sentence about it,
9
+ // the progress of a download, the action that fits, and whether to ask again.
10
+ //
11
+ // No imports and no DOM: `node --test` loads this file as it is
12
+ // (tests/tunnel-view.test.mjs). TUNNEL_COPY is the English of every sentence;
13
+ // `tunnel.*` in i18n/en.json must equal it and i18n/pt-BR.json must have the
14
+ // same keys (the test checks both). The page side is ui-tunnel.js.
15
+ //
16
+ // A server that was started before SynaBun brought its own tunnel program
17
+ // answers without the `cloudflared` part: the cards then work as they did (a
18
+ // tunnel starts when a cloudflared is installed), and where none is, they say
19
+ // that a restart is what is missing. Nothing here ever tells the user to
20
+ // install anything, except on a system Cloudflare publishes no file for.
21
+
22
+ export const TUNNEL_COPY = Object.freeze({
23
+ stateOff: 'Off',
24
+ stateDownloading: 'Getting the tunnel program',
25
+ stateStarting: 'Starting',
26
+ stateRunning: 'On',
27
+ stateFailed: 'Did not start',
28
+ stateUnsupported: 'Not available',
29
+ stateRestart: 'Restart needed',
30
+ idle: 'A public address through Cloudflare, for invite links and remote MCP.',
31
+ idleDownload: 'A public address through Cloudflare, for invite links and remote MCP. The first start downloads the tunnel program ({size} MB).',
32
+ downloading: 'Downloading the tunnel program: {received} of {total} MB',
33
+ starting: 'Waiting for Cloudflare to give an address…',
34
+ restart: 'Restart SynaBun to use the public address: the server that is running was started before it could download the tunnel program.',
35
+ unsupported: 'Cloudflare publishes no tunnel program for this system. If you install cloudflared yourself, SynaBun uses it.',
36
+ errorOffline: 'The tunnel program could not be downloaded: the download server was not reached. Check the connection and try again.',
37
+ errorHttp: 'The tunnel program could not be downloaded: the download server answered with an error. Try again in a moment.',
38
+ errorInterrupted: 'The download stopped before it was complete. Nothing was kept. Try again.',
39
+ errorTimeout: 'The download stalled. Nothing was kept. Try again.',
40
+ errorChecksum: 'The download was not the file SynaBun expects, so it was removed and never run. Try again; if it happens again, something on the network is changing the file.',
41
+ errorUnpack: 'The downloaded file could not be unpacked, so it was removed. Try again.',
42
+ errorDisk: 'The tunnel program could not be saved in SynaBun\'s data folder. Check free space and permissions, then try again.',
43
+ errorLaunch: 'The tunnel program could not be started on this computer. Try again.',
44
+ errorExited: 'Cloudflare gave no address. The tunnel program said: {reason}',
45
+ errorExitedNoReason: 'The tunnel program stopped before Cloudflare gave an address. Try again.',
46
+ errorNoAddress: 'Cloudflare gave no address in time. Try again.',
47
+ errorEnded: 'The tunnel stopped. Start it again for a new address.',
48
+ errorRefused: 'SynaBun refused this request. Start the tunnel from SynaBun on this computer.',
49
+ errorUnknown: 'The tunnel did not start: {reason}',
50
+ errorUnknownNoReason: 'The tunnel did not start. Try again.',
51
+ start: 'Start tunnel',
52
+ stop: 'Stop',
53
+ cancel: 'Cancel',
54
+ retry: 'Try again',
55
+ copyBundled: 'cloudflared {version}, included with SynaBun',
56
+ copyDownloaded: 'cloudflared {version}, downloaded by SynaBun',
57
+ copySystem: 'cloudflared {version}, installed on this computer',
58
+ copySystemWhy: 'SynaBun could not download its own copy, so the one installed on this computer is used.',
59
+ mcpKeyNeeded: 'Remote MCP needs an access key. Create one below; the address to connect is then this one followed by /mcp/ and the key.',
60
+ });
61
+
62
+ const ERROR_COPY = Object.freeze({
63
+ offline: 'errorOffline', http: 'errorHttp', interrupted: 'errorInterrupted', timeout: 'errorTimeout', checksum: 'errorChecksum',
64
+ unpack: 'errorUnpack', disk: 'errorDisk', launch: 'errorLaunch', no_address: 'errorNoAddress', ended: 'errorEnded',
65
+ unsupported: 'unsupported', refused: 'errorRefused',
66
+ });
67
+
68
+ /** A TUNNEL_COPY sentence with its `{params}` filled in: the translator when the page gives none. */
69
+ export function fillTunnelCopy(key, params = {}) {
70
+ return String(TUNNEL_COPY[key] ?? key).replace(/\{(\w+)\}/g, (whole, name) => (params[name] != null ? params[name] : whole));
71
+ }
72
+
73
+ const megabytes = (bytes) => Math.max(0, Number(bytes) || 0) / (1024 * 1024);
74
+
75
+ /**
76
+ * What a card shows for a status.
77
+ *
78
+ * @param {object | null} status the answer of GET /api/tunnel/status (or of the start / stop that came last)
79
+ * @param {object} [o]
80
+ * @param {(key: string, params?: object) => string} [o.t] a key of TUNNEL_COPY → the sentence in the page's language
81
+ * @param {(value: number, digits: number) => string} [o.number] a number as the page's language writes it
82
+ * @returns {{
83
+ * kind: 'restart' | 'unsupported' | 'idle' | 'downloading' | 'starting' | 'running' | 'failed',
84
+ * tone: 'off' | 'busy' | 'ok' | 'warn' | 'err',
85
+ * on: boolean,
86
+ * label: string, detail: string,
87
+ * progress: { percent: number, text: string } | null,
88
+ * url: string | null,
89
+ * action: 'start' | 'stop' | 'cancel' | 'retry' | null, actionLabel: string,
90
+ * copy: string, copyNote: string,
91
+ * poll: boolean,
92
+ * }}
93
+ * `on`: the switch stands on (something runs or is on its way). `action`: what a press does ("cancel" and
94
+ * "stop" are both POST /api/tunnel/stop; "start" and "retry" both POST /api/tunnel/start). `copy`: which
95
+ * cloudflared runs, when one does. `poll`: the state will change by itself, ask again.
96
+ */
97
+ export function tunnelView(status, { t = fillTunnelCopy, number = (value, digits) => value.toFixed(digits) } = {}) {
98
+ const s = status && typeof status === 'object' ? status : {};
99
+ const tool = s.cloudflared && typeof s.cloudflared === 'object' ? s.cloudflared : null;
100
+ const view = {
101
+ kind: 'idle', tone: 'off', on: false, label: t('stateOff'), detail: t('idle'), progress: null,
102
+ url: typeof s.url === 'string' && s.url ? s.url : null,
103
+ action: 'start', actionLabel: t('start'), copy: '', copyNote: '', poll: false,
104
+ };
105
+ const as = (kind, tone, labelKey, detail, action) => Object.assign(view, {
106
+ kind, tone, label: t(labelKey), detail, action, actionLabel: action ? t(action) : '',
107
+ on: kind === 'downloading' || kind === 'starting' || kind === 'running', poll: kind === 'downloading' || kind === 'starting',
108
+ });
109
+ const running = () => {
110
+ as('running', 'ok', 'stateRunning', view.url, 'stop');
111
+ const source = { bundled: 'copyBundled', downloaded: 'copyDownloaded', system: 'copySystem' }[tool?.source];
112
+ if (source && tool.version) {
113
+ view.copy = t(source, { version: tool.version });
114
+ if (tool.source === 'system' && tool.fallback && tool.fallback.code !== 'unsupported') view.copyNote = t('copySystemWhy');
115
+ }
116
+ return view;
117
+ };
118
+
119
+ // Nothing is known yet (the answer has not arrived): the card as it stands before a first start.
120
+ if (!status || typeof status !== 'object') return view;
121
+
122
+ // A server from before the tunnel program came with SynaBun: no `cloudflared` part in its answer.
123
+ if (!tool) {
124
+ if (view.url) return running();
125
+ if (s.starting || s.running) return as('starting', 'busy', 'stateStarting', t('starting'), 'cancel');
126
+ if (s.available) return as('idle', 'off', 'stateOff', t('idle'), 'start');
127
+ return as('restart', 'warn', 'stateRestart', t('restart'), null);
128
+ }
129
+
130
+ const state = typeof s.state === 'string' ? s.state : 'idle';
131
+ if (state === 'running' && view.url) return running();
132
+ if (state === 'downloading') {
133
+ const total = Number(tool.progress?.total) || Number(tool.download?.size) || 0;
134
+ const received = Math.min(Number(tool.progress?.received) || 0, total || Infinity);
135
+ const percent = total ? Math.max(0, Math.min(100, Math.floor((received / total) * 100))) : 0;
136
+ as('downloading', 'busy', 'stateDownloading', t('downloading', { received: number(megabytes(received), 1), total: number(megabytes(total), 1) }), 'cancel');
137
+ view.progress = { percent, text: `${percent}%` };
138
+ return view;
139
+ }
140
+ if (state === 'starting' || state === 'running') return as('starting', 'busy', 'stateStarting', t('starting'), 'cancel');
141
+ if (state === 'failed') {
142
+ const error = s.error && typeof s.error === 'object' ? s.error : {};
143
+ if (error.code === 'unsupported') return as('unsupported', 'off', 'stateUnsupported', t('unsupported'), null);
144
+ const reason = typeof error.reason === 'string' && error.reason.trim() ? error.reason.trim() : '';
145
+ let detail;
146
+ if (error.code === 'exited') detail = reason ? t('errorExited', { reason }) : t('errorExitedNoReason');
147
+ else if (ERROR_COPY[error.code]) detail = t(ERROR_COPY[error.code]);
148
+ else detail = typeof error.message === 'string' && error.message.trim() ? t('errorUnknown', { reason: error.message.trim() }) : t('errorUnknownNoReason');
149
+ return as('failed', 'err', 'stateFailed', detail, 'retry');
150
+ }
151
+ // idle
152
+ if (tool.state === 'unsupported') return as('unsupported', 'off', 'stateUnsupported', t('unsupported'), null);
153
+ if (tool.state === 'ready') return as('idle', 'off', 'stateOff', t('idle'), 'start');
154
+ const size = Number(tool.download?.size) || 0;
155
+ return as('idle', 'off', 'stateOff', size ? t('idleDownload', { size: number(megabytes(size), 0) }) : t('idle'), 'start');
156
+ }
157
+
158
+ /**
159
+ * The address an assistant outside connects to, or null while there is none to give: the public address
160
+ * followed by /mcp/ and the access key. Without a key there is no such address: the server refuses /mcp from
161
+ * outside (lib/request-gate.js mcpWithoutKey), so the cards offer nothing to copy and say what is missing.
162
+ */
163
+ export function remoteMcpAddress(url, key) {
164
+ const base = typeof url === 'string' ? url.trim().replace(/\/+$/, '') : '';
165
+ const secret = typeof key === 'string' ? key.trim() : '';
166
+ return base && secret ? `${base}/mcp/${secret}` : null;
167
+ }
@@ -7,6 +7,7 @@ import { hostedSidepanelId, sidepanelHost } from './ui-sidepanel-runtime.js';
7
7
  import { storage } from './storage.js';
8
8
  import { fetchBlobNamespace, putBlob, deleteBlob, flushBlobs } from './blob-store.js';
9
9
  import { state, emit, on } from './state.js';
10
+ import { forgetGreeting, mountGreeting } from './greeting/greeting-card.js';
10
11
  import { fetchClaudeSessions, fetchBrowserSessions, searchSessions } from './api.js';
11
12
  import { createFrameRenderer } from './utils.js';
12
13
  import { notify, NOTIF_TYPE } from './ui-notifications.js';
@@ -15,6 +16,9 @@ import { subscribeCliStatus, recheckCliStatus, getCliDocUrl, getCliInstallComman
15
16
  import { nativeLoopWindowId } from './ui-native-window-id.js';
16
17
  import { createStickyScrollController } from './ui-scroll-follow.js';
17
18
  import { requestGeneratedSessionTitle } from './session-title.js';
19
+ import { wireFileLinks } from './ui-file-links.js';
20
+ import { WORKER_END_CLASS, backToAssistant, buildWorkerEndCard, endWorkerRunOnClose, forgetWorkerEnd, trackWorkerEnd, updateWorkerEndBusy } from './ui-worker-end.js';
21
+ import { isDispatchedRun, isEndOfRunAbort } from './worker-end.js';
18
22
  // ── cp/ feature modules (SDK-engine CLI parity: diffs, agents, bash, perms, statusline) ──
19
23
  import { setCpCtx } from './cp/cp-ctx.js';
20
24
  import { buildDiffCard, buildDiffPreviewEl, finalizeDiffCard } from './cp/cp-diff.js';
@@ -24,7 +28,7 @@ import { buildAgentCard, ensureAgentScope, noteAgentActivity, updateAgentTodoBad
24
28
  import { renderPermissionCard, renderPlanApprovalCard, renderElicitationCard, decorateAskCard, askAnnotations, markAskAnswered, PERMISSION_MODES, MODE_LABELS } from './cp/cp-permissions.js';
25
29
  import { normalizeSession, changedStartSettings, sessionFlags, sessionRows, fastModeSupported, fastModeReason, mergeSessionModels, parseMcpServerLines, formatMcpServerLines } from './cp/cp-session-model.js';
26
30
  import { renderSessionSettingsCard } from './cp/cp-session-settings.js';
27
- import { describeOrigin, reconcileTasks, adoptLiveTasks, describeHistorySystemRow, workEndedByRestart } from './cp/cp-tasks-model.js';
31
+ import { describeOrigin, reconcileTasks, adoptLiveTasks, describeHistorySystemRow, workEndedByRestart, isTaskRunning } from './cp/cp-tasks-model.js';
28
32
  import { renderTasksCard } from './cp/cp-tasks.js';
29
33
  import { sessionListLabel, collectTags, tagSession as tagClaudeSession, sessionActionsAvailable, sessionOpsSupported, labelledSessionIds, mergeSessionSearch, writeSessionTitle, forkSession as forkClaudeSession, deleteSession as deleteClaudeSession, fetchSubagentMessages, forkTitle, entryBefore, promptText, removeFrom, sessionRowParts, fillSelectOptions } from './cp/cp-sessions.js';
30
34
  import { contextCard, usageCard, mcpRows, rewindPreviewText, rewindResultText } from './cp/cp-usage-model.js';
@@ -2928,6 +2932,7 @@ function newTabState(sessionId = null, label = 'New chat', id = null) {
2928
2932
  automationRunId: null,
2929
2933
  automationActive: false,
2930
2934
  automationOwnerId: null,
2935
+ releasedRunId: null, // the ended run this tab showed before the user continued it by hand
2931
2936
  };
2932
2937
  }
2933
2938
 
@@ -3130,15 +3135,18 @@ function closeTab(idx, { detachAutomation = false } = {}) {
3130
3135
  if (idx < 0 || idx >= _tabs.length) return;
3131
3136
  const tab = _tabs[idx];
3132
3137
  if (tab.automationRunId && !detachAutomation) {
3138
+ const runId = tab.automationRunId;
3133
3139
  window.dispatchEvent(new CustomEvent('native-loop:dismissed', {
3134
- detail: { runId: tab.automationRunId },
3140
+ detail: { runId },
3135
3141
  }));
3136
- fetch('/api/loop/stop', {
3142
+ // A worker that finished its task is completed; any other run is stopped.
3143
+ endWorkerRunOnClose(runId, () => fetch('/api/loop/stop', {
3137
3144
  method: 'POST',
3138
3145
  headers: { 'Content-Type': 'application/json' },
3139
- body: JSON.stringify({ runId: tab.automationRunId }),
3140
- }).catch(() => {});
3146
+ body: JSON.stringify({ runId }),
3147
+ }).catch(() => {}), { busy: hasLiveBackgroundWork(tab) });
3141
3148
  }
3149
+ if (tab.automationRunId) forgetWorkerEnd(tab.automationRunId);
3142
3150
  flushSessionSnapshotSave(tab);
3143
3151
  recordHookEvent(tab, 'SessionEnd', tab.sessionId || '(new)');
3144
3152
  tab.closed = true;
@@ -3264,7 +3272,7 @@ function saveTabs() {
3264
3272
  try {
3265
3273
  // A temporary tab is never written: not the tab, its label, its queue or its session id.
3266
3274
  const kept = persistableTabs(_tabs, _activeTabIdx);
3267
- const localTabs = kept.tabs.map(t => ({ id: t.id, sessionId: t.sessionId, label: t.label, titleState: t.titleState === 'generating' ? 'default' : (t.titleState || 'default'), sessionCost: t.sessionCost || 0, running: t.running || false, project: t.project || '', model: t.model || '', effort: t.effort || 'off', planMode: t.planMode || false, ...savedMode(t), toolPolicy: t.toolPolicy || 'full', accountId: t.accountId || '', session: normalizeSession(t.session), transcriptAt: t.transcriptAt || null, planFilePath: t.planFilePath || '', conversation: t.conversation || '', queue: t.queue || [], queuePaused: t.queuePaused || false, automationRunId: t.automationRunId || null, automationActive: !!t.automationActive, automationOwnerId: t.automationOwnerId || null }));
3275
+ const localTabs = kept.tabs.map(t => ({ id: t.id, sessionId: t.sessionId, label: t.label, titleState: t.titleState === 'generating' ? 'default' : (t.titleState || 'default'), sessionCost: t.sessionCost || 0, running: t.running || false, project: t.project || '', model: t.model || '', effort: t.effort || 'off', planMode: t.planMode || false, ...savedMode(t), toolPolicy: t.toolPolicy || 'full', accountId: t.accountId || '', session: normalizeSession(t.session), transcriptAt: t.transcriptAt || null, planFilePath: t.planFilePath || '', conversation: t.conversation || '', queue: t.queue || [], queuePaused: t.queuePaused || false, automationRunId: t.automationRunId || null, automationActive: !!t.automationActive, automationOwnerId: t.automationOwnerId || null, releasedRunId: t.releasedRunId || null }));
3268
3276
  const localRunIds = new Set(localTabs.map((tab) => tab.automationRunId).filter(Boolean));
3269
3277
  let foreignAutomationTabs = [];
3270
3278
  try {
@@ -3406,6 +3414,7 @@ function restoreTabs() {
3406
3414
  if (t && saved.automationRunId) t.automationRunId = saved.automationRunId;
3407
3415
  if (t && saved.automationRunId) t.automationActive = !!saved.automationActive;
3408
3416
  if (t && saved.automationRunId) t.automationOwnerId = saved.automationOwnerId || null;
3417
+ if (t && saved.releasedRunId && !saved.automationRunId) t.releasedRunId = saved.releasedRunId;
3409
3418
  }
3410
3419
  if (_tabs.length) {
3411
3420
  switchTab(Math.min(data.activeIdx || 0, _tabs.length - 1));
@@ -5653,9 +5662,9 @@ function handleTabEvent(tab, ev) {
5653
5662
  appendStatus(tab, ev.message || 'Retrying…');
5654
5663
  return;
5655
5664
  }
5656
- // Non-fatal runtime problems (e.g. the bundled Claude binary lost its execute
5657
- // bit and was repaired, or we fell back to the globally installed CLI). The
5658
- // session continues — this must not render as a fatal error.
5665
+ // Non-fatal runtime notices (e.g. a session setting that could not be
5666
+ // applied, or where this session's debug log is written). The session
5667
+ // continues — this must not render as a fatal error.
5659
5668
  if (ev.type === 'system' && ev.subtype === 'runtime_notice') {
5660
5669
  if (ev.level === 'error') appendError(tab, ev.message || 'Runtime error');
5661
5670
  else if (ev.level === 'info') appendStatus(tab, ev.message || 'Runtime notice');
@@ -7445,7 +7454,57 @@ function handleControlRequest(tab, msg) {
7445
7454
  // What a query tells the bridge about this tab's session: the permission mode
7446
7455
  // (or that the user's own settings should pick it), the tool policy, and what
7447
7456
  // this panel can render. Keys a server that predates them ignores.
7457
+ // ── Greeting (shared/greeting/, lib/greeting/) ──
7458
+ // What SynaBun says when a person comes back and opens a new chat: one or two sentences in the
7459
+ // empty state, written by SynaBun ahead of time (this session's model never greets). Only for a
7460
+ // tab that holds no conversation yet: never a resumed session, a temporary chat or a tab showing
7461
+ // an automation run (hence the short wait: those are marked right after the tab is made). The
7462
+ // server decides whether this opening is a return worth greeting; an older server answers nothing.
7463
+ // Asked from _paintTemporary(), which runs wherever the conversation area is rebuilt. Nothing of it
7464
+ // is kept on the tab object (what a tab carries over, and what is saved of it, is listed elsewhere).
7465
+ const _greetingOf = new WeakMap(); // tab → { seq, host, greeting: { id, session } | null }
7466
+ function _greetingState(tab) {
7467
+ let held = _greetingOf.get(tab);
7468
+ if (!held) { held = { seq: 0, host: null, greeting: null }; _greetingOf.set(tab, held); }
7469
+ return held;
7470
+ }
7471
+ function _offerGreetingSoon(tab) {
7472
+ if (!tab) return;
7473
+ setTimeout(() => { try { _offerGreeting(tab); } catch { /* a tab always opens */ } }, 300);
7474
+ }
7475
+ function _offerGreeting(tab) {
7476
+ if (!_tabs.includes(tab) || tab.sessionId || tab.temporary || tab.automationRunId || tab.automationActive || tab.running) return;
7477
+ const host = tab.messagesEl?.querySelector(':scope > .cp-empty');
7478
+ if (!host || host.dataset.greetingAsked === '1') return;
7479
+ host.dataset.greetingAsked = '1';
7480
+ const held = _greetingState(tab);
7481
+ // The conversation area was rebuilt: a new conversation in the same tab is a new opening.
7482
+ if (held.host && held.host !== host) { held.seq += 1; held.greeting = null; }
7483
+ held.host = host;
7484
+ const session = `${tab.id}.${held.seq}`;
7485
+ const cwd = tab.project || ddGetValue(_panel?.querySelector('#cp-project')) || '';
7486
+ mountGreeting(host, {
7487
+ surface: 'claude-panel', session, cwd,
7488
+ onOpenMemory: (id) => { if (id) emit('memory:open', { id }); },
7489
+ isCurrent: () => !tab.sessionId && !tab.temporary && !tab.automationRunId && held.host === host && tab.messagesEl?.contains(host),
7490
+ }).then((greeting) => {
7491
+ if (held.host === host) held.greeting = greeting ? { id: greeting.id, session } : null;
7492
+ }).catch(() => {});
7493
+ }
7494
+
7495
+ // The greeting a chat opened with goes back once, with its first prompt: the bridge tells the model
7496
+ // what was said (capability 'greeting'). A temporary chat names nothing, so it takes none.
7497
+ function _echoGreeting(tab, msg) {
7498
+ const held = msg.type === 'query' ? _greetingOf.get(tab) : null;
7499
+ if (!held?.greeting?.id) return;
7500
+ if (hasCapability(tab, 'greeting') && !tab.temporary) msg.greetingId = held.greeting.id;
7501
+ forgetGreeting('claude-panel', held.greeting.session);
7502
+ held.greeting = null;
7503
+ }
7504
+
7448
7505
  function _applySessionOptions(tab, msg) {
7506
+ // The greeting this chat opened with goes back once, with its first prompt.
7507
+ _echoGreeting(tab, msg);
7449
7508
  // The tab's own mode (statedMode() in cp/cp-permission-model.js: Bypass only
7450
7509
  // as the user's recorded pick). A tab that never picked one leaves it to the
7451
7510
  // user's Claude Code settings (permissions.defaultMode).
@@ -7471,6 +7530,8 @@ function _applySessionOptions(tab, msg) {
7471
7530
  function _paintTemporary(tab) {
7472
7531
  if (!tab) return;
7473
7532
  paintTemporary(tab, { capable: hasCapability(tab, TEMPORARY_CAPABILITY), onToggle: (on) => _chooseTemporary(tab, on) });
7533
+ // The conversation area was (re)built: a new chat may open with SynaBun's greeting.
7534
+ _offerGreetingSoon(tab);
7474
7535
  }
7475
7536
 
7476
7537
  // The user's choice on a new tab. A process kept ready for the other kind of
@@ -9228,6 +9289,9 @@ function send({ shift = false } = {}) {
9228
9289
  // A temporary chat that is over, or one the running server would save: nothing is sent.
9229
9290
  { const refusal = temporaryRefusal(tab, { capable: hasCapability(tab, TEMPORARY_CAPABILITY) }); if (refusal) { appendStatus(tab, refusal); return; } }
9230
9291
 
9292
+ // A message of the user's own in the tab of a run that ended: the tab is theirs from here on.
9293
+ releaseAutomationForManualUse(tab);
9294
+
9231
9295
  // ── !bash shortcut — prefix a Bash-tool hint so Claude executes the command ──
9232
9296
  if (text.startsWith('!') && text.length > 1) {
9233
9297
  const cmd = text.slice(1).trim();
@@ -10162,15 +10226,7 @@ function _onMessagesClick(e) {
10162
10226
  });
10163
10227
  return;
10164
10228
  }
10165
- const link = e.target.closest('a.file-link');
10166
- if (link) {
10167
- e.preventDefault();
10168
- const path = (link.textContent || '').replace(/:\d+$/, '');
10169
- navigator.clipboard.writeText(path);
10170
- link.style.opacity = '0.5';
10171
- setTimeout(() => { link.style.opacity = ''; }, 300);
10172
- return;
10173
- }
10229
+ // File links (a.file-link, and the links of a message's Markdown) are taken by _fileLinkOptions below.
10174
10230
  const hdr = e.target.closest('.tool-hdr');
10175
10231
  if (hdr && !e.target.closest('button, a, input, select, textarea')) {
10176
10232
  const card = hdr.closest('.tool-card');
@@ -10183,6 +10239,25 @@ function _onMessagesClick(e) {
10183
10239
  }
10184
10240
  }
10185
10241
 
10242
+ // ── File links ──
10243
+ // A link to a file the code editor can open opens it there (ui-file-links.js, shared with the other panels).
10244
+ // The same delegation as above: it covers streaming, finished and snapshot-restored messages of every tab.
10245
+ const _fileLinkOptions = {
10246
+ // A relative path is a path in the folder of the tab the message belongs to.
10247
+ cwd: (link) => {
10248
+ const tab = _tabs.find((t) => t.messagesEl?.contains(link)) || activeTab();
10249
+ return tab?.init?.cwd || tab?.project || '';
10250
+ },
10251
+ textLinks: 'a.file-link',
10252
+ // A path the editor does not open keeps what a click on it did: it is copied.
10253
+ copyPath: (link) => {
10254
+ const path = (link.textContent || '').replace(/:\d+$/, '');
10255
+ link.style.opacity = '0.5';
10256
+ setTimeout(() => { link.style.opacity = ''; }, 300);
10257
+ return navigator.clipboard.writeText(path);
10258
+ },
10259
+ };
10260
+
10186
10261
  // ── Public API ──
10187
10262
  async function _ensureClaudePanelReady() {
10188
10263
  if (!_panel) {
@@ -10197,6 +10272,7 @@ async function _ensureClaudePanelReady() {
10197
10272
  });
10198
10273
  wireEvents();
10199
10274
  _panel.querySelector('#cp-messages-container')?.addEventListener('click', _onMessagesClick);
10275
+ wireFileLinks(_panel.querySelector('#cp-messages-container'), _fileLinkOptions);
10200
10276
  await loadConfig(); // must complete before restoreTabs — checks server boot ID
10201
10277
  loadSkills();
10202
10278
  restoreTabs();
@@ -10248,6 +10324,18 @@ export async function attachClaudeAutomation(run, { focus = !!run?.focus, isCanc
10248
10324
  if (!run?.runId || !run?.providerSessionId) return { ok: false, reason: 'identity_pending' };
10249
10325
  await _ensureClaudePanelReady();
10250
10326
  if (isCancelled()) return { ok: false, reason: 'dismissed' };
10327
+ // The user continued this run's tab by hand after the run ended: it stays theirs. The dock's
10328
+ // Focus raises it; the run is let go again, and nothing in the tab changes.
10329
+ const own = _tabs.find((entry) => !entry.automationRunId && entry.releasedRunId === run.runId && entry.sessionId === run.providerSessionId);
10330
+ if (own && !(run.status === 'starting' || run.status === 'running')) {
10331
+ window.dispatchEvent(new CustomEvent('native-loop:dismissed', { detail: { runId: run.runId } }));
10332
+ if (focus) {
10333
+ if (!_visible) await toggleClaudePanel();
10334
+ const ownIdx = _tabs.indexOf(own);
10335
+ if (ownIdx >= 0) switchTab(ownIdx);
10336
+ }
10337
+ return { ok: true, tabId: own.id, released: true };
10338
+ }
10251
10339
  let tab = _tabs.find((entry) => entry.automationRunId === run.runId)
10252
10340
  || _tabs.find((entry) => entry.sessionId === run.providerSessionId);
10253
10341
  if (!tab) {
@@ -10270,6 +10358,7 @@ export async function attachClaudeAutomation(run, { focus = !!run?.focus, isCanc
10270
10358
  tab.automationRunId = run.runId;
10271
10359
  tab.automationActive = running;
10272
10360
  tab.automationOwnerId = nativeLoopWindowId;
10361
+ tab.releasedRunId = null;
10273
10362
  tab.sdkMode = true;
10274
10363
  tab.sessionId = run.providerSessionId;
10275
10364
  tab.label = run.title || tab.label || 'Automation';
@@ -10288,10 +10377,12 @@ export async function attachClaudeAutomation(run, { focus = !!run?.focus, isCanc
10288
10377
  return { ok: false, reason: 'dismissed' };
10289
10378
  }
10290
10379
  }
10291
- if (run.status === 'failed' && run.error && tab._automationLifecycleError !== run.error) {
10380
+ // (A dispatched worker's failure, with its reason, is on its end card.)
10381
+ if (run.status === 'failed' && run.error && !isDispatchedRun(run) && tab._automationLifecycleError !== run.error) {
10292
10382
  tab._automationLifecycleError = run.error;
10293
10383
  appendError(tab, run.error);
10294
10384
  }
10385
+ trackWorkerEnd(run, { apply: (view) => showWorkerEnd(tab, view), busy: hasLiveBackgroundWork(tab) });
10295
10386
  // The mode control is hidden while the run is active and back when it ended.
10296
10387
  if (tab === activeTab()) populateModeDropdown(_panel?.querySelector('#cp-mode'), tab);
10297
10388
  const idx = _tabs.indexOf(tab);
@@ -10319,10 +10410,60 @@ export function handleClaudeAutomationEvent(runId, payload) {
10319
10410
  return true;
10320
10411
  }
10321
10412
  if (event.type === 'synabun.error') {
10413
+ // Every run's end disposes its SDK query, and the SDK says so in these words: not a failure.
10414
+ if (isEndOfRunAbort(event.message)) return true;
10322
10415
  appendError(tab, event.message || 'Claude Code automation failed');
10323
10416
  return true;
10324
10417
  }
10325
10418
  handleTabMsg(tab, { type: 'event', event, fromRun: true });
10419
+ // Background agents still running keep a warm worker's "Task complete" away.
10420
+ if (event.type === 'system' || event.type === 'result') updateWorkerEndBusy(runId, hasLiveBackgroundWork(tab));
10421
+ return true;
10422
+ }
10423
+
10424
+ function hasLiveBackgroundWork(tab) {
10425
+ for (const task of tab?.tasks?.values?.() || []) {
10426
+ if (task && !task.ambient && isTaskRunning(task)) return true;
10427
+ }
10428
+ return false;
10429
+ }
10430
+
10431
+ // The end of a worker the Assistant dispatched (ui-worker-end.js): one card,
10432
+ // the last row of its transcript. Its button closes this tab the way the X
10433
+ // does (a warm worker that finished its task is completed, one still at work
10434
+ // is stopped; its record stays for the Assistant) and shows the Assistant
10435
+ // session that dispatched it.
10436
+ function showWorkerEnd(tab, view) {
10437
+ tab._workerEnd = view || null;
10438
+ const $msgs = tab.messagesEl;
10439
+ if (!$msgs || tab.closed) return;
10440
+ $msgs.querySelectorAll(`.${WORKER_END_CLASS}`).forEach((el) => el.remove());
10441
+ if (!view) return;
10442
+ $msgs.appendChild(buildWorkerEndCard(view, {
10443
+ classes: { card: 'cp-worker-end', title: 'post-plan-header', actions: 'post-plan-actions', button: 'post-plan-action pp-primary' },
10444
+ onBack: () => {
10445
+ const idx = _tabs.indexOf(tab);
10446
+ if (idx >= 0) closeTab(idx);
10447
+ backToAssistant(view.assistantSessionId);
10448
+ },
10449
+ }));
10450
+ if (tab === activeTab()) scrollEnd(true);
10451
+ }
10452
+
10453
+ // The user sends a message of their own in the tab of a run that ended (Codex's
10454
+ // releaseAutomationForManualUse): the run's end card goes, the tab stops following
10455
+ // the run, and the router leaves the run alone, so nothing of it comes back here.
10456
+ // A run that is still going keeps its tab (the composer refuses a manual turn then).
10457
+ function releaseAutomationForManualUse(tab) {
10458
+ if (!tab?.automationRunId || tab.automationActive) return false;
10459
+ const runId = tab.automationRunId;
10460
+ forgetWorkerEnd(runId);
10461
+ showWorkerEnd(tab, null);
10462
+ tab.automationRunId = null;
10463
+ tab.automationOwnerId = null;
10464
+ tab.releasedRunId = runId;
10465
+ window.dispatchEvent(new CustomEvent('native-loop:dismissed', { detail: { runId } }));
10466
+ saveTabs();
10326
10467
  return true;
10327
10468
  }
10328
10469