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,90 @@
1
+ // ═══════════════════════════════════════════
2
+ // SynaBun — Greeting: every number and name in one place
3
+ // ═══════════════════════════════════════════
4
+ //
5
+ // The greeting engine (engine.js: what SynaBun says to a person who comes back
6
+ // and opens a fresh session, on every surface) reads all of its limits from
7
+ // here. Pure: no I/O, no SynaBun imports.
8
+
9
+ /** `DATA_HOME/data/greeting.json`: one switch, on by default. */
10
+ export const GREETING_DEFAULTS = Object.freeze({ enabled: true });
11
+
12
+ /** The settings as booleans; anything that is not exactly `false` reads as on. */
13
+ export function normalizeGreetingSettings(raw) {
14
+ const value = raw && typeof raw === 'object' ? raw : {};
15
+ return { enabled: value.enabled !== false };
16
+ }
17
+
18
+ /** The environment's kill switch: SYNABUN_GREETING=off (also "0" / "false"). */
19
+ export function greetingKilled(env = process.env) {
20
+ return /^(off|0|false|no)$/i.test(String(env?.SYNABUN_GREETING ?? '').trim());
21
+ }
22
+
23
+ /** The surfaces a greeting can open on, and how the writer is told about each. */
24
+ export const SURFACES = Object.freeze({
25
+ assistant: 'the SynaBun Assistant',
26
+ 'claude-panel': 'Claude Code (side panel)',
27
+ 'codex-panel': 'Codex (side panel)',
28
+ 'opencode-panel': 'OpenCode (side panel)',
29
+ 'claude-cli': 'Claude Code (terminal)',
30
+ });
31
+
32
+ /** A greeting is for a return: the last human message in the project is at least this old. */
33
+ export const RETURN = Object.freeze({ gapMs: 60 * 60_000 });
34
+
35
+ /** The sentence itself. */
36
+ export const TEXT = Object.freeze({
37
+ maxChars: 280,
38
+ maxSentences: 2,
39
+ // A name (a thread title, a run title) may be shortened with an ellipsis; a fact never is.
40
+ titleChars: 90,
41
+ quoteChars: 120,
42
+ itemChars: 160,
43
+ });
44
+
45
+ /** What is read to build a greeting. */
46
+ export const FACTS = Object.freeze({
47
+ threads: 3,
48
+ sessions: 4,
49
+ memories: 3,
50
+ // Memories of the last working period count when someone thought them significant.
51
+ memoryImportance: 7,
52
+ memoryWindowMs: 36 * 3_600_000,
53
+ excerptChars: 260,
54
+ openItems: 2,
55
+ runs: 4,
56
+ schedules: 3,
57
+ elsewhere: 3,
58
+ // News older than this is not news.
59
+ newsWindowMs: 14 * 24 * 3_600_000,
60
+ ownerNotes: 4,
61
+ ownerWords: 2,
62
+ });
63
+
64
+ /** Preparing ahead of time. */
65
+ export const PREPARE = Object.freeze({
66
+ // A project is prepared once its last human message is this old (the session went quiet).
67
+ settleMs: 10 * 60_000,
68
+ sweepMs: 5 * 60_000,
69
+ bootDelayMs: 45_000,
70
+ // While the person stays away, a project is written again at most this often (then the last value).
71
+ intervalsMs: Object.freeze([10 * 60_000, 30 * 60_000, 60 * 60_000, 120 * 60_000]),
72
+ // Writer calls per local day, every project together; past it the fallback line is used.
73
+ dailyCalls: 40,
74
+ projects: 8,
75
+ activeDays: 14,
76
+ });
77
+
78
+ /** The writer (a Claude one-shot on the cheapest Claude model). */
79
+ export const WRITER = Object.freeze({ timeoutMs: 15_000, fallbackModel: 'haiku' });
80
+
81
+ /** Kept in greeting-state.json. */
82
+ export const STATE = Object.freeze({ greetingsKept: 60, sessionsKept: 300, toldKept: 400, projectsKept: 60 });
83
+
84
+ /** A session key or a greeting id as it may arrive from a client. */
85
+ export const ID_RE = /^[A-Za-z0-9_.:-]{1,160}$/;
86
+
87
+ /** The repo's estimator (mcp-server memory-retrieval `estimateTokens`): UTF-8 bytes / 3, which over-counts. */
88
+ export function estimateTokens(value) {
89
+ return Math.ceil(Buffer.byteLength(String(value ?? ''), 'utf8') / 3);
90
+ }
@@ -0,0 +1,461 @@
1
+ // ═══════════════════════════════════════════
2
+ // SynaBun — Greeting (the engine)
3
+ // ═══════════════════════════════════════════
4
+ //
5
+ // One greeting for every agent. When a person comes back and opens a fresh
6
+ // session (the Assistant, the Claude Code / Codex / OpenCode panels, a
7
+ // terminal Claude Code session), SynaBun says in one or two sentences what
8
+ // they were doing last here, what is open, and what finished or failed while
9
+ // they were away. The harness writes it; the session's own model never greets,
10
+ // it is only told once what was said.
11
+ //
12
+ // prepared in the background. Every human prompt is noted per project
13
+ // (noteActivity); a sweep looks at the projects that went quiet and
14
+ // calls the writer when their facts changed (a growing interval
15
+ // while the person stays away, a daily cap).
16
+ // open a lookup: local facts, a fingerprint compare, the prepared
17
+ // sentence or the deterministic line. Never a model call, and
18
+ // nothing at all unless this opening is a return: an hour away,
19
+ // something real to say, something new since the last greeting.
20
+ // known the surface echoes the greeting's id with the first prompt and
21
+ // the session's model gets one short note (consumeNote), once.
22
+ // visible every greeting carries the records it was built from and what
23
+ // the writer cost.
24
+ //
25
+ // Everything heavy is injected (the records, the writer), so the module is
26
+ // unit-testable and never opens the memory database itself. Nothing here
27
+ // throws into a session: a failure is logged and the session opens quietly.
28
+
29
+ import { randomUUID } from 'node:crypto';
30
+ import { mkdirSync, readFileSync, renameSync, statSync, writeFileSync } from 'node:fs';
31
+ import { dirname } from 'node:path';
32
+ import { GREETING_DEFAULTS, ID_RE, PREPARE, RETURN, STATE, SURFACES, TEXT, estimateTokens, greetingKilled, normalizeGreetingSettings } from './budgets.js';
33
+ import { agentNote, awayText, basisOf, buildFacts, clipLabel, decide, fallbackLine, fingerprint, gitKey } from './facts.js';
34
+
35
+ export const GREETING_EVENT = 'greeting:changed';
36
+ const STATE_VERSION = 1;
37
+ const SURFACE_AGENT = Object.freeze({ assistant: 'assistant', 'claude-panel': 'claude-code', 'claude-cli': 'claude-code', 'codex-panel': 'codex', 'opencode-panel': 'opencode' });
38
+
39
+ const iso = (at) => new Date(at).toISOString();
40
+ const ms = (value) => { const at = typeof value === 'number' ? value : Date.parse(value ?? ''); return Number.isFinite(at) ? at : null; };
41
+ const count = (value) => (Number.isFinite(Number(value)) && Number(value) > 0 ? Math.round(Number(value)) : 0);
42
+ const localDay = (at) => { const d = new Date(at); return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`; };
43
+ const object = (value) => (value && typeof value === 'object' && !Array.isArray(value) ? value : {});
44
+
45
+ /**
46
+ * @param {object} deps
47
+ * @param {string} deps.settingsPath DATA_HOME/data/greeting.json ({ enabled }), read live
48
+ * @param {string} [deps.statePath] DATA_HOME/data/greeting-state.json (operational state)
49
+ * @param {object} deps.sources sources.js createGreetingSources()
50
+ * @param {{ available: Function, write: Function } | null} [deps.writer] writer.js createGreetingWriter()
51
+ * @param {(text:string) => string} [deps.redact] credentials out of anything read
52
+ * @param {(event:object) => void} [deps.broadcast] tells open windows a setting changed
53
+ */
54
+ export function createGreetingEngine({
55
+ settingsPath = null, statePath = null, sources = null, writer = null, redact = (text) => text, broadcast = () => {},
56
+ env = process.env, now = Date.now, log = () => {}, timers = { setTimeout, clearTimeout, setInterval, clearInterval }, autoSweep = true,
57
+ } = {}) {
58
+ let settingsCache = null; // { mtimeMs, value }
59
+ let state = loadState();
60
+ let stateTimer = null;
61
+ let sweepTimer = null;
62
+ let bootTimer = null;
63
+ let sweeping = null;
64
+ let stopped = false;
65
+ const preparing = new Map(); // project → the preparation under way
66
+
67
+ // ── settings ──
68
+ function settings() {
69
+ if (!settingsPath) return { ...GREETING_DEFAULTS };
70
+ try {
71
+ const stat = statSync(settingsPath);
72
+ if (settingsCache && settingsCache.mtimeMs === stat.mtimeMs) return settingsCache.value;
73
+ const value = normalizeGreetingSettings(JSON.parse(readFileSync(settingsPath, 'utf8')));
74
+ settingsCache = { mtimeMs: stat.mtimeMs, value };
75
+ return value;
76
+ } catch {
77
+ // No file yet (the default), or one that cannot be read: the default.
78
+ return { ...GREETING_DEFAULTS };
79
+ }
80
+ }
81
+ function killed() { return greetingKilled(env); }
82
+ function enabled() { return settings().enabled && !killed(); }
83
+ function setEnabled(value) {
84
+ if (!settingsPath) throw new Error('The greeting setting has nowhere to be saved');
85
+ const next = normalizeGreetingSettings({ enabled: value !== false });
86
+ mkdirSync(dirname(settingsPath), { recursive: true });
87
+ const tmp = `${settingsPath}.${process.pid}.tmp`;
88
+ writeFileSync(tmp, JSON.stringify(next, null, 2) + '\n', 'utf8');
89
+ renameSync(tmp, settingsPath);
90
+ settingsCache = null;
91
+ try { broadcast({ type: GREETING_EVENT, enabled: next.enabled }); } catch { /* nobody listening */ }
92
+ return next;
93
+ }
94
+
95
+ // ── operational state ──
96
+ function emptyState() {
97
+ return { version: STATE_VERSION, activity: {}, lastActivityAt: null, prepared: {}, shown: {}, greetings: {}, sessions: {}, told: {}, rearm: {}, spend: { day: null, calls: 0, costUsd: 0, tokens: 0 } };
98
+ }
99
+ function loadState() {
100
+ const empty = emptyState();
101
+ if (!statePath) return empty;
102
+ try {
103
+ const raw = JSON.parse(readFileSync(statePath, 'utf8'));
104
+ if (!raw || typeof raw !== 'object') return empty;
105
+ return {
106
+ ...empty,
107
+ activity: object(raw.activity), lastActivityAt: typeof raw.lastActivityAt === 'string' ? raw.lastActivityAt : null,
108
+ prepared: object(raw.prepared), shown: object(raw.shown), greetings: object(raw.greetings), sessions: object(raw.sessions),
109
+ told: object(raw.told), rearm: object(raw.rearm), spend: { ...empty.spend, ...object(raw.spend) },
110
+ };
111
+ } catch { return empty; }
112
+ }
113
+ const newest = (entries, at, keep) => Object.fromEntries(entries.sort((a, b) => (ms(at(b[1])) ?? 0) - (ms(at(a[1])) ?? 0)).slice(0, keep));
114
+ function saveStateNow() {
115
+ if (stateTimer) { timers.clearTimeout(stateTimer); stateTimer = null; }
116
+ if (!statePath) return;
117
+ try {
118
+ state.greetings = newest(Object.entries(state.greetings), (row) => row.shownAt, STATE.greetingsKept);
119
+ state.sessions = newest(Object.entries(state.sessions), (row) => row.at, STATE.sessionsKept);
120
+ state.told = newest(Object.entries(state.told), (row) => row, STATE.toldKept);
121
+ state.activity = newest(Object.entries(state.activity), (row) => row.at, STATE.projectsKept);
122
+ for (const key of ['prepared', 'shown']) for (const project of Object.keys(state[key])) if (!state.activity[project]) delete state[key][project];
123
+ mkdirSync(dirname(statePath), { recursive: true });
124
+ const tmp = `${statePath}.${process.pid}.tmp`;
125
+ writeFileSync(tmp, JSON.stringify(state, null, 2) + '\n', 'utf8');
126
+ renameSync(tmp, statePath);
127
+ } catch (error) { log('greeting:state-error', error?.message || String(error)); }
128
+ }
129
+ function saveState() {
130
+ if (stateTimer || stopped) return;
131
+ stateTimer = timers.setTimeout(() => { stateTimer = null; saveStateNow(); }, 500);
132
+ stateTimer?.unref?.();
133
+ }
134
+
135
+ // ── who, where, when ──
136
+ function projectKey({ project = null, cwd = null } = {}) {
137
+ if (project) return String(project).toLowerCase();
138
+ try { return String(sources?.projectOf?.(cwd) || 'global').toLowerCase(); } catch { return 'global'; }
139
+ }
140
+ function labelOf(project) {
141
+ try { return sources?.projectInfo?.(project)?.label || project; } catch { return project; }
142
+ }
143
+ /** The person's last message in a project (ms), seeded from the records the first time it is asked. */
144
+ function lastActivity(project) {
145
+ const known = ms(state.activity[project]?.at);
146
+ if (known != null) return known;
147
+ let seeded = null;
148
+ try { seeded = sources?.lastWorkAt?.(project) ?? null; } catch { seeded = null; }
149
+ if (seeded != null) { state.activity[project] = { at: iso(seeded), seeded: true, surfaces: {} }; saveState(); }
150
+ return seeded;
151
+ }
152
+ function activityList(project) {
153
+ const surfaces = object(state.activity[project]?.surfaces);
154
+ return Object.entries(surfaces).map(([surface, entry]) => ({ surface, agent: SURFACE_AGENT[surface] || null, at: entry?.at || null, words: entry?.words || '' }));
155
+ }
156
+ const toldSet = () => new Set(Object.keys(state.told));
157
+
158
+ /**
159
+ * A person sent a message (every surface calls this from its prompt path; never a loop, a
160
+ * dispatched run, a subagent or a system turn). `words`: what they typed, kept as one short quote.
161
+ */
162
+ function noteActivity({ project = null, cwd = null, surface = 'assistant', words = '' } = {}) {
163
+ try {
164
+ const key = projectKey({ project, cwd });
165
+ const at = iso(now());
166
+ const entry = state.activity[key] && typeof state.activity[key] === 'object' ? state.activity[key] : { surfaces: {} };
167
+ entry.at = at;
168
+ delete entry.seeded;
169
+ entry.surfaces = object(entry.surfaces);
170
+ // With the greeting off only the time is kept: nothing they typed is stored for a feature that is not in use.
171
+ const quote = enabled() ? clipLabel(safeRedact(words), TEXT.quoteChars) : '';
172
+ entry.surfaces[String(surface)] = { at, ...(quote ? { words: quote } : {}) };
173
+ state.activity[key] = entry;
174
+ state.lastActivityAt = at;
175
+ // They are here: the next absence starts its own preparation rhythm.
176
+ if (state.prepared[key]) state.prepared[key].attempts = 0;
177
+ saveState();
178
+ return key;
179
+ } catch (error) { log('greeting:activity-error', error?.message || String(error)); return null; }
180
+ }
181
+ function safeRedact(text) {
182
+ try { return String(redact(String(text ?? '')) ?? ''); } catch { return ''; }
183
+ }
184
+
185
+ // ── facts ──
186
+ async function factsFor(project, { fast = false } = {}) {
187
+ const since = lastActivity(project);
188
+ const at = now();
189
+ const raw = await sources.collect(project, {
190
+ since, globalSince: ms(state.lastActivityAt) ?? since, now: at, activity: activityList(project), told: toldSet(),
191
+ gitTimeoutMs: fast ? 400 : 1500,
192
+ });
193
+ return { raw, facts: buildFacts(raw, { redact: safeRedact }), since, at };
194
+ }
195
+ /** The fingerprint the same facts will have once their unscoped news has been said. */
196
+ function fingerprintAfterTelling(raw) {
197
+ return fingerprint(buildFacts({ ...raw, schedules: [], elsewhere: [] }, { redact: safeRedact }));
198
+ }
199
+
200
+ // ── preparing ──
201
+ function spendToday() {
202
+ const day = localDay(now());
203
+ if (state.spend.day !== day) state.spend = { day, calls: 0, costUsd: 0, tokens: 0 };
204
+ return state.spend;
205
+ }
206
+ function writerAvailable() {
207
+ try { return !!writer && (typeof writer.available !== 'function' || !!writer.available()); } catch { return false; }
208
+ }
209
+ function publicCost(result) {
210
+ const tokens = result?.tokens && typeof result.tokens === 'object' ? result.tokens : null;
211
+ return {
212
+ model: result?.model || null,
213
+ tokens: tokens ? { input: count(tokens.input), cacheWrite: count(tokens.cacheWrite), cacheRead: count(tokens.cacheRead), output: count(tokens.output) } : null,
214
+ costUsd: Number.isFinite(Number(result?.costUsd)) && result?.costUsd != null ? Number(result.costUsd) : null,
215
+ ms: count(result?.ms),
216
+ };
217
+ }
218
+
219
+ /**
220
+ * Write a project's greeting ahead of time. Nothing happens while the person is still there,
221
+ * while the facts are the ones already written or already said, or before the interval is up.
222
+ * `force` (Settings' preview) writes now. → the prepared entry, or null.
223
+ */
224
+ async function prepare(project, { force = false } = {}) {
225
+ const key = String(project || 'global').toLowerCase();
226
+ if (!sources || (!force && !enabled())) return null;
227
+ if (preparing.has(key)) return preparing.get(key);
228
+ const job = (async () => {
229
+ const since = lastActivity(key);
230
+ if (since == null) return null;
231
+ if (!force && now() - since < PREPARE.settleMs) return state.prepared[key] || null;
232
+ const current = state.prepared[key] || null;
233
+ const attempts = count(current?.attempts);
234
+ const wait = PREPARE.intervalsMs[Math.min(attempts, PREPARE.intervalsMs.length - 1)];
235
+ if (!force && current?.lastTryAt && now() - (ms(current.lastTryAt) ?? 0) < wait) return current;
236
+ const { facts } = await factsFor(key);
237
+ if (!facts.substantive) { if (current) { delete state.prepared[key]; saveState(); } return null; }
238
+ const print = fingerprint(facts);
239
+ const tree = gitKey(facts);
240
+ if (!force && current && current.fingerprint === print && (tree == null || current.gitKey === tree)) return current;
241
+ // Already said and nothing new: a sentence nobody would be shown is not worth a call.
242
+ if (!force && state.shown[key]?.fingerprint === print) return current;
243
+ const fallback = fallbackLine(facts);
244
+ const spend = spendToday();
245
+ let result = { text: '', error: writerAvailable() ? null : 'no-writer' };
246
+ if (writerAvailable()) {
247
+ if (spend.calls >= PREPARE.dailyCalls) result = { text: '', error: 'daily-cap' };
248
+ else {
249
+ spend.calls += 1;
250
+ try { result = await writer.write(facts, {}); } catch (error) { result = { text: '', error: String(error?.message || error).slice(0, 160) }; }
251
+ const cost = publicCost(result);
252
+ spend.costUsd = Number((Number(spend.costUsd || 0) + (cost.costUsd || 0)).toFixed(6));
253
+ spend.tokens = count(spend.tokens) + (cost.tokens ? cost.tokens.input + cost.tokens.cacheWrite + cost.tokens.cacheRead + cost.tokens.output : 0);
254
+ }
255
+ }
256
+ const text = result.text || fallback;
257
+ if (!text) return null;
258
+ const entry = {
259
+ fingerprint: print, gitKey: tree ?? current?.gitKey ?? null, text, source: result.text ? 'writer' : 'fallback', error: result.text ? null : (result.error || null),
260
+ ...publicCost(result), preparedAt: iso(now()), basis: basisOf(facts), attempts: attempts + 1, lastTryAt: iso(now()),
261
+ };
262
+ state.prepared[key] = entry;
263
+ saveState();
264
+ return entry;
265
+ })().catch((error) => { log('greeting:prepare-error', error?.message || String(error)); return null; })
266
+ .finally(() => { preparing.delete(key); });
267
+ preparing.set(key, job);
268
+ return job;
269
+ }
270
+
271
+ /** Every project the person worked in lately, one after the other. */
272
+ function sweep() {
273
+ if (sweeping) return sweeping;
274
+ sweeping = (async () => {
275
+ if (stopped || !enabled() || !sources) return 0;
276
+ // A fresh install of the engine: the projects with the newest records get their first entry.
277
+ if (!Object.keys(state.activity).length) {
278
+ let recent = [];
279
+ try { recent = sources.recentProjects?.({ limit: PREPARE.projects }) || []; } catch { recent = []; }
280
+ for (const project of recent) lastActivity(String(project).toLowerCase());
281
+ }
282
+ const floor = now() - PREPARE.activeDays * 24 * 3_600_000;
283
+ const projects = Object.entries(state.activity).map(([project, entry]) => ({ project, at: ms(entry?.at) }))
284
+ .filter((row) => row.at != null && row.at >= floor).sort((a, b) => b.at - a.at).slice(0, PREPARE.projects);
285
+ let written = 0;
286
+ for (const { project } of projects) {
287
+ if (stopped) break;
288
+ const before = state.prepared[project]?.preparedAt || null;
289
+ const entry = await prepare(project);
290
+ if (entry && entry.preparedAt !== before) written += 1;
291
+ }
292
+ return written;
293
+ })().catch((error) => { log('greeting:sweep-error', error?.message || String(error)); return 0; })
294
+ .finally(() => { sweeping = null; });
295
+ return sweeping;
296
+ }
297
+ function start() {
298
+ if (!autoSweep || stopped || sweepTimer) return;
299
+ bootTimer = timers.setTimeout(() => { bootTimer = null; sweep(); }, PREPARE.bootDelayMs);
300
+ bootTimer?.unref?.();
301
+ sweepTimer = timers.setInterval(() => { sweep(); }, PREPARE.sweepMs);
302
+ sweepTimer?.unref?.();
303
+ }
304
+
305
+ // ── opening a session ──
306
+ function view(greeting) {
307
+ if (!greeting) return null;
308
+ const away = greeting.awaySince && greeting.shownAt ? (ms(greeting.shownAt) ?? 0) - (ms(greeting.awaySince) ?? 0) : null;
309
+ return {
310
+ id: greeting.id, text: greeting.text, project: greeting.project, label: greeting.label, surface: greeting.surface || null,
311
+ source: greeting.source, error: greeting.error || null, model: greeting.model || null, tokens: greeting.tokens || null,
312
+ costUsd: greeting.costUsd ?? null, ms: greeting.ms || 0, preparedAt: greeting.preparedAt || null,
313
+ shownAt: greeting.shownAt, awaySince: greeting.awaySince || null, away: away != null && away > 0 ? awayText(away) : '',
314
+ basis: Array.isArray(greeting.basis) ? greeting.basis : [], reason: greeting.reason || null,
315
+ };
316
+ }
317
+ function rememberSession(sessionKey, id, reason) {
318
+ if (!sessionKey) return;
319
+ state.sessions[sessionKey] = { id: id || null, reason: id ? null : reason, at: iso(now()) };
320
+ }
321
+
322
+ /**
323
+ * A person opened a fresh interactive session. → { greeting | null, reason }.
324
+ * `session`: the surface's name for this conversation; the same one asking again (a reload, a
325
+ * redraw) gets the same answer, so nothing greets twice. `note: true` (a surface with no later
326
+ * call of its own: the terminal hook) also returns the model's note, given once.
327
+ */
328
+ async function open({ surface = 'assistant', project = null, cwd = null, session = null, note = false } = {}) {
329
+ try {
330
+ if (!enabled()) return { greeting: null, reason: killed() ? 'killed' : 'off' };
331
+ if (!sources) return { greeting: null, reason: 'unavailable' };
332
+ if (!Object.prototype.hasOwnProperty.call(SURFACES, surface)) return { greeting: null, reason: 'unknown-surface' };
333
+ const key = projectKey({ project, cwd });
334
+ const sessionKey = session && ID_RE.test(String(session)) ? `${surface}:${session}` : null;
335
+ const prior = sessionKey ? state.sessions[sessionKey] : null;
336
+ if (prior) {
337
+ const again = prior.id ? state.greetings[prior.id] : null;
338
+ return { greeting: again && !again.notedAt ? view(again) : null, reason: again ? 'repeat' : (prior.reason || 'nothing-to-say') };
339
+ }
340
+ const forced = state.rearm[key] === true;
341
+ const since = lastActivity(key);
342
+ // Being greeted is being here: an hour starts over from the last greeting too.
343
+ const presentAt = Math.max(since ?? 0, ms(state.shown[key]?.at) ?? 0);
344
+ if (!forced && since != null && now() - presentAt < RETURN.gapMs) { rememberSession(sessionKey, null, 'recent'); saveState(); return { greeting: null, reason: 'recent' }; }
345
+ const { raw, facts, at } = await factsFor(key, { fast: true });
346
+ const verdict = decide({ enabled: true, facts, now: at, since, shownFingerprint: state.shown[key]?.fingerprint || null, forced });
347
+ if (!verdict.greet) { rememberSession(sessionKey, null, verdict.reason); saveState(); return { greeting: null, reason: verdict.reason }; }
348
+ const print = fingerprint(facts);
349
+ const tree = gitKey(facts);
350
+ const prepared = state.prepared[key];
351
+ const current = !!prepared?.text && prepared.fingerprint === print && (tree == null || prepared.gitKey == null || prepared.gitKey === tree);
352
+ const text = current ? prepared.text : fallbackLine(facts);
353
+ if (!text) { rememberSession(sessionKey, null, 'nothing-to-say'); saveState(); return { greeting: null, reason: 'nothing-to-say' }; }
354
+ const greeting = {
355
+ id: `g-${randomUUID().replace(/-/g, '').slice(0, 16)}`, project: key, label: facts.label, text, surface, session: sessionKey,
356
+ source: current ? prepared.source : 'fallback', error: current ? (prepared.error || null) : (prepared ? 'out-of-date' : 'not-prepared'),
357
+ model: current ? prepared.model : null, tokens: current ? prepared.tokens : null, costUsd: current ? prepared.costUsd : null, ms: current ? prepared.ms : 0,
358
+ preparedAt: current ? prepared.preparedAt : null, basis: basisOf(facts), reason: verdict.reason,
359
+ shownAt: iso(at), awaySince: since != null ? iso(since) : null, notedAt: null,
360
+ };
361
+ state.greetings[greeting.id] = greeting;
362
+ state.shown[key] = { fingerprint: fingerprintAfterTelling(raw), at: greeting.shownAt, greetingId: greeting.id };
363
+ for (const told of raw.toldKeys || []) state.told[told] = at;
364
+ delete state.rearm[key];
365
+ rememberSession(sessionKey, greeting.id, null);
366
+ const out = { greeting: view(greeting), reason: verdict.reason };
367
+ if (note) { out.note = agentNote(greeting.text); greeting.notedAt = iso(at); }
368
+ saveState();
369
+ return out;
370
+ } catch (error) {
371
+ log('greeting:open-error', error?.message || String(error));
372
+ return { greeting: null, reason: 'error' };
373
+ }
374
+ }
375
+
376
+ /**
377
+ * The note for the session's model, once: the surface echoes the greeting's id with the first
378
+ * prompt. '' for an id that is unknown, already told, or when the prompt is a slash command
379
+ * (`skip`: the command's own flow comes first; the greeting is not owed to it later either).
380
+ */
381
+ function consumeNote(id, { skip = false } = {}) {
382
+ try {
383
+ const greeting = typeof id === 'string' && ID_RE.test(id) ? state.greetings[id] : null;
384
+ if (!greeting || greeting.notedAt) return '';
385
+ greeting.notedAt = iso(now());
386
+ saveState();
387
+ return skip ? '' : agentNote(greeting.text);
388
+ } catch { return ''; }
389
+ }
390
+ /** The greeting a note would be about, without telling it (the Assistant's trace names its records). */
391
+ function peek(id) {
392
+ const greeting = typeof id === 'string' && ID_RE.test(id) ? state.greetings[id] : null;
393
+ return greeting ? view(greeting) : null;
394
+ }
395
+
396
+ /**
397
+ * What a new session in a project would be greeted with right now (Settings → Greeting). Marks
398
+ * nothing as shown or told. `write: true` asks the writer now and keeps the answer as the
399
+ * project's prepared greeting.
400
+ */
401
+ async function preview({ project = null, cwd = null, write = false } = {}) {
402
+ const key = projectKey({ project, cwd });
403
+ const base = { enabled: settings().enabled, killed: killed(), project: key, label: labelOf(key), writer: writerAvailable(), gapMs: RETURN.gapMs, maxChars: TEXT.maxChars };
404
+ if (!sources) return { ...base, would: false, reason: 'unavailable', text: '', basis: [], lastHereAt: null, lastGreetingAt: null };
405
+ try {
406
+ if (write) await prepare(key, { force: true });
407
+ const { facts, since, at } = await factsFor(key);
408
+ const presentAt = Math.max(since ?? 0, ms(state.shown[key]?.at) ?? 0);
409
+ const forced = state.rearm[key] === true;
410
+ let verdict = decide({ enabled: enabled(), facts, now: at, since, shownFingerprint: state.shown[key]?.fingerprint || null, forced });
411
+ // The same order a real opening decides in: here a moment ago comes before nothing new.
412
+ if (!forced && enabled() && facts.substantive && since != null && at - presentAt < RETURN.gapMs) verdict = { greet: false, reason: 'recent' };
413
+ const print = fingerprint(facts);
414
+ const tree = gitKey(facts);
415
+ const prepared = state.prepared[key];
416
+ const current = !!prepared?.text && prepared.fingerprint === print && (tree == null || prepared.gitKey == null || prepared.gitKey === tree);
417
+ const text = facts.substantive ? (current ? prepared.text : fallbackLine(facts)) : '';
418
+ return {
419
+ ...base, would: verdict.greet, reason: verdict.reason, text,
420
+ source: current ? prepared.source : 'fallback', error: current ? (prepared.error || null) : (prepared ? 'out-of-date' : 'not-prepared'),
421
+ model: current ? prepared.model : null, tokens: current ? prepared.tokens : null, costUsd: current ? prepared.costUsd ?? null : null, ms: current ? prepared.ms : 0,
422
+ preparedAt: current ? prepared.preparedAt : null, basis: basisOf(facts),
423
+ lastHereAt: since != null ? iso(since) : null, away: since != null ? awayText(at - since) : '',
424
+ lastGreetingAt: state.shown[key]?.at || null, noteTokens: text ? estimateTokens(agentNote(text)) : 0,
425
+ spend: { ...spendToday(), cap: PREPARE.dailyCalls },
426
+ };
427
+ } catch (error) {
428
+ log('greeting:preview-error', error?.message || String(error));
429
+ return { ...base, would: false, reason: 'error', text: '', basis: [], lastHereAt: null, lastGreetingAt: null };
430
+ }
431
+ }
432
+
433
+ /** The next session opened in a project is greeted once, whatever the gap (the first live look). */
434
+ function rearm({ project = null, cwd = null } = {}) {
435
+ const key = projectKey({ project, cwd });
436
+ state.rearm[key] = true;
437
+ saveState();
438
+ return key;
439
+ }
440
+
441
+ function status() {
442
+ const shown = Object.values(state.greetings).sort((a, b) => (ms(b.shownAt) ?? 0) - (ms(a.shownAt) ?? 0));
443
+ return {
444
+ settings: settings(), killed: killed(), enabled: enabled(), writer: writerAvailable(),
445
+ spend: { ...spendToday(), cap: PREPARE.dailyCalls }, gapMs: RETURN.gapMs,
446
+ last: view(shown[0] || null), shownCount: shown.length,
447
+ };
448
+ }
449
+
450
+ function shutdown() {
451
+ stopped = true;
452
+ if (bootTimer) { timers.clearTimeout(bootTimer); bootTimer = null; }
453
+ if (sweepTimer) { timers.clearInterval(sweepTimer); sweepTimer = null; }
454
+ saveStateNow();
455
+ }
456
+
457
+ return {
458
+ settings, setEnabled, enabled, killed, noteActivity, open, consumeNote, peek, preview, prepare, sweep, start, rearm, status, shutdown,
459
+ _internals: { state: () => state, saveStateNow, factsFor },
460
+ };
461
+ }