synabun 2.0.0 → 2.0.7

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 (507) hide show
  1. package/CHANGELOG.md +97 -0
  2. package/NOTICE +5 -0
  3. package/README.md +2 -1
  4. package/THIRD-PARTY-LICENSES.md +59 -0
  5. package/benchmarks/fixtures/browser/README.md +1 -1
  6. package/benchmarks/fixtures/desktop/README.md +1 -1
  7. package/docs/whatsapp.md +153 -18
  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 +26 -3
  12. package/hooks/claude-code/stop.mjs +5 -5
  13. package/lib/child-path.js +303 -0
  14. package/lib/claude-hooks.js +2 -1
  15. package/lib/data-home-migration.js +2 -0
  16. package/lib/desktop-app.js +265 -0
  17. package/lib/external-tools.js +7 -3
  18. package/lib/log-redact.js +102 -0
  19. package/lib/packaged-runtime.js +138 -11
  20. package/lib/start-launcher.js +88 -1
  21. package/mcp-server/dist/http.d.ts +3 -1
  22. package/mcp-server/dist/http.d.ts.map +1 -1
  23. package/mcp-server/dist/http.js +15 -4
  24. package/mcp-server/dist/http.js.map +1 -1
  25. package/mcp-server/dist/index.d.ts.map +1 -1
  26. package/mcp-server/dist/index.js +9 -6
  27. package/mcp-server/dist/index.js.map +1 -1
  28. package/mcp-server/dist/services/budget-judgments.d.ts +108 -0
  29. package/mcp-server/dist/services/budget-judgments.d.ts.map +1 -0
  30. package/mcp-server/dist/services/budget-judgments.js +153 -0
  31. package/mcp-server/dist/services/budget-judgments.js.map +1 -0
  32. package/mcp-server/dist/services/codex-config-heal.d.ts +5 -1
  33. package/mcp-server/dist/services/codex-config-heal.d.ts.map +1 -1
  34. package/mcp-server/dist/services/codex-config-heal.js +41 -4
  35. package/mcp-server/dist/services/codex-config-heal.js.map +1 -1
  36. package/mcp-server/dist/services/identity.d.ts +11 -0
  37. package/mcp-server/dist/services/identity.d.ts.map +1 -1
  38. package/mcp-server/dist/services/identity.js +24 -0
  39. package/mcp-server/dist/services/identity.js.map +1 -1
  40. package/mcp-server/dist/services/neural-interface.d.ts +13 -0
  41. package/mcp-server/dist/services/neural-interface.d.ts.map +1 -1
  42. package/mcp-server/dist/services/neural-interface.js +34 -2
  43. package/mcp-server/dist/services/neural-interface.js.map +1 -1
  44. package/mcp-server/dist/services/typesafe-config.d.ts +1 -1
  45. package/mcp-server/dist/services/typesafe-config.d.ts.map +1 -1
  46. package/mcp-server/dist/services/typesafe-config.js +4 -1
  47. package/mcp-server/dist/services/typesafe-config.js.map +1 -1
  48. package/mcp-server/dist/tools/agents.d.ts +10 -7
  49. package/mcp-server/dist/tools/agents.d.ts.map +1 -1
  50. package/mcp-server/dist/tools/agents.js +34 -13
  51. package/mcp-server/dist/tools/agents.js.map +1 -1
  52. package/mcp-server/dist/tools/whatsapp-send-file.d.ts +17 -0
  53. package/mcp-server/dist/tools/whatsapp-send-file.d.ts.map +1 -0
  54. package/mcp-server/dist/tools/whatsapp-send-file.js +44 -0
  55. package/mcp-server/dist/tools/whatsapp-send-file.js.map +1 -0
  56. package/mcp-server/dist/tui.js +2 -1
  57. package/mcp-server/dist/tui.js.map +1 -1
  58. package/mcp-server/dist/version.d.ts +5 -0
  59. package/mcp-server/dist/version.d.ts.map +1 -0
  60. package/mcp-server/dist/version.js +21 -0
  61. package/mcp-server/dist/version.js.map +1 -0
  62. package/mcp-server/package-lock.json +2 -2
  63. package/mcp-server/package.json +1 -3
  64. package/mcp-server/run.mjs +15 -9
  65. package/mcp-server/src/http.ts +18 -3
  66. package/mcp-server/src/index.ts +9 -6
  67. package/mcp-server/src/services/budget-judgments.ts +183 -0
  68. package/mcp-server/src/services/codex-config-heal.ts +35 -4
  69. package/mcp-server/src/services/identity.ts +27 -0
  70. package/mcp-server/src/services/neural-interface.ts +34 -2
  71. package/mcp-server/src/services/typesafe-config.ts +4 -1
  72. package/mcp-server/src/tools/agents.ts +33 -14
  73. package/mcp-server/src/tools/whatsapp-send-file.ts +51 -0
  74. package/mcp-server/src/tui.ts +2 -1
  75. package/mcp-server/src/version.ts +22 -0
  76. package/neural-interface/i18n/en.json +2126 -209
  77. package/neural-interface/i18n/pt-BR.json +2123 -207
  78. package/neural-interface/lib/account-usage.js +163 -0
  79. package/neural-interface/lib/assistant-api.js +316 -57
  80. package/neural-interface/lib/assistant-brains/claude.js +19 -2
  81. package/neural-interface/lib/assistant-brains/codex.js +3 -0
  82. package/neural-interface/lib/assistant-brains/opencode.js +17 -4
  83. package/neural-interface/lib/assistant-budget-planner.js +390 -0
  84. package/neural-interface/lib/assistant-budget-split.js +408 -0
  85. package/neural-interface/lib/assistant-budget.js +94 -95
  86. package/neural-interface/lib/assistant-catalog.js +179 -27
  87. package/neural-interface/lib/assistant-config.js +91 -14
  88. package/neural-interface/lib/assistant-dispatch.js +494 -138
  89. package/neural-interface/lib/assistant-hidden-models.js +393 -66
  90. package/neural-interface/lib/assistant-jev.js +26 -3
  91. package/neural-interface/lib/assistant-memory.js +24 -10
  92. package/neural-interface/lib/assistant-persona.js +73 -12
  93. package/neural-interface/lib/assistant-plan-permissions.js +8 -1
  94. package/neural-interface/lib/assistant-pricing.js +21 -6
  95. package/neural-interface/lib/assistant-route-accounts.js +14 -0
  96. package/neural-interface/lib/assistant-route-gate.js +19 -3
  97. package/neural-interface/lib/assistant-router.js +373 -42
  98. package/neural-interface/lib/assistant-runtime.js +488 -59
  99. package/neural-interface/lib/assistant-task-prompt.js +13 -3
  100. package/neural-interface/lib/assistant-test-density.js +142 -0
  101. package/neural-interface/lib/assistant-user-context-budgets.js +123 -0
  102. package/neural-interface/lib/assistant-user-context.js +908 -0
  103. package/neural-interface/lib/assistant-user-knowledge.js +328 -0
  104. package/neural-interface/lib/assistant-user-learn.js +404 -0
  105. package/neural-interface/lib/assistant-user-threads.js +226 -0
  106. package/neural-interface/lib/assistant-user-writer.js +183 -0
  107. package/neural-interface/lib/augmented-path.js +317 -53
  108. package/neural-interface/lib/browser-background-tab.js +134 -0
  109. package/neural-interface/lib/browser-screenshot.js +678 -61
  110. package/neural-interface/lib/claude-accounts.js +342 -31
  111. package/neural-interface/lib/claude-agent-bridge.js +123 -9
  112. package/neural-interface/lib/claude-executable.js +6 -1
  113. package/neural-interface/lib/claude-model-catalog.js +33 -5
  114. package/neural-interface/lib/claude-panel-auth.js +221 -0
  115. package/neural-interface/lib/claude-profile-api-key.js +62 -0
  116. package/neural-interface/lib/claude-temporary.js +46 -7
  117. package/neural-interface/lib/cli-install-api.js +137 -0
  118. package/neural-interface/lib/cli-install.js +797 -0
  119. package/neural-interface/lib/cli-path.js +130 -0
  120. package/neural-interface/lib/cli-resolver.js +744 -0
  121. package/neural-interface/lib/cli-update-planner.js +143 -7
  122. package/neural-interface/lib/cloudflared/install.js +533 -0
  123. package/neural-interface/lib/cloudflared/manager.js +545 -0
  124. package/neural-interface/lib/cloudflared/pins.js +115 -0
  125. package/neural-interface/lib/cloudflared/pins.json +63 -0
  126. package/neural-interface/lib/cloudflared/tunnel.js +265 -0
  127. package/neural-interface/lib/codex-account-auth.js +178 -0
  128. package/neural-interface/lib/codex-accounts.js +489 -0
  129. package/neural-interface/lib/codex-capabilities.js +2 -1
  130. package/neural-interface/lib/codex-compat-rpc.js +8 -2
  131. package/neural-interface/lib/codex-mcp-approvals.js +5 -1
  132. package/neural-interface/lib/codex-panel-auth.js +62 -0
  133. package/neural-interface/lib/codex-session-settings.js +1 -1
  134. package/neural-interface/lib/codex-settings-schema.js +120 -0
  135. package/neural-interface/lib/desktop/config.js +3 -0
  136. package/neural-interface/lib/editor-completion-endpoint.js +264 -0
  137. package/neural-interface/lib/editor-completion-opencode.js +436 -0
  138. package/neural-interface/lib/editor-completion.js +714 -0
  139. package/neural-interface/lib/editor-config.js +149 -0
  140. package/neural-interface/lib/editor-files-api.js +268 -0
  141. package/neural-interface/lib/editor-git-api.js +134 -0
  142. package/neural-interface/lib/file-explorer-api.js +1039 -0
  143. package/neural-interface/lib/file-explorer-system.js +355 -0
  144. package/neural-interface/lib/file-explorer-trash.ps1 +85 -0
  145. package/neural-interface/lib/file-explorer-watch.js +239 -0
  146. package/neural-interface/lib/git-bash/bzip2.js +339 -0
  147. package/neural-interface/lib/git-bash/claude-settings.js +73 -0
  148. package/neural-interface/lib/git-bash/extract-worker.js +34 -0
  149. package/neural-interface/lib/git-bash/install.js +529 -0
  150. package/neural-interface/lib/git-bash/licenses/COPYING +360 -0
  151. package/neural-interface/lib/git-bash/licenses/LGPL-2.1 +511 -0
  152. package/neural-interface/lib/git-bash/manager.js +268 -0
  153. package/neural-interface/lib/git-bash/pins.js +206 -0
  154. package/neural-interface/lib/git-bash/pins.json +34 -0
  155. package/neural-interface/lib/git-bash/resolver.js +194 -0
  156. package/neural-interface/lib/git-diff-api.js +103 -0
  157. package/neural-interface/lib/git-read.js +234 -0
  158. package/neural-interface/lib/greeting/api.js +89 -0
  159. package/neural-interface/lib/greeting/budgets.js +90 -0
  160. package/neural-interface/lib/greeting/engine.js +461 -0
  161. package/neural-interface/lib/greeting/facts.js +320 -0
  162. package/neural-interface/lib/greeting/sources.js +252 -0
  163. package/neural-interface/lib/greeting/writer.js +139 -0
  164. package/neural-interface/lib/http-guards.js +189 -13
  165. package/neural-interface/lib/install-details.js +145 -0
  166. package/neural-interface/lib/machine-names.js +88 -0
  167. package/neural-interface/lib/mcp-installer.js +14 -2
  168. package/neural-interface/lib/memory-api.js +31 -7
  169. package/neural-interface/lib/native-loop-providers.js +26 -8
  170. package/neural-interface/lib/native-loop-runtime.js +9 -4
  171. package/neural-interface/lib/os-path.js +319 -0
  172. package/neural-interface/lib/plugin-installer.js +4 -2
  173. package/neural-interface/lib/project-storage.js +5 -7
  174. package/neural-interface/lib/provider-panel-api-key-auth.js +92 -0
  175. package/neural-interface/lib/provider-panel-terminal-auth.js +95 -0
  176. package/neural-interface/lib/pty-host/core.js +2 -1
  177. package/neural-interface/lib/pty-host/manager.js +27 -2
  178. package/neural-interface/lib/remote-policy.js +20 -11
  179. package/neural-interface/lib/request-gate.js +589 -0
  180. package/neural-interface/lib/session-title-generator.js +4 -3
  181. package/neural-interface/lib/system-about.js +340 -0
  182. package/neural-interface/lib/system-activity.js +66 -0
  183. package/neural-interface/lib/terminal-env.js +11 -3
  184. package/neural-interface/lib/terminal-path.js +55 -0
  185. package/neural-interface/lib/ui-blob-api.js +108 -0
  186. package/neural-interface/lib/ui-blob-store.js +68 -0
  187. package/neural-interface/lib/virtual-socket.js +12 -1
  188. package/neural-interface/lib/whatsapp/baileys-adapter.js +165 -45
  189. package/neural-interface/lib/whatsapp/bridge.js +78 -34
  190. package/neural-interface/lib/whatsapp/commands.js +13 -14
  191. package/neural-interface/lib/whatsapp/config.js +134 -20
  192. package/neural-interface/lib/whatsapp/fake-baileys.js +99 -14
  193. package/neural-interface/lib/whatsapp/host-core.js +168 -35
  194. package/neural-interface/lib/whatsapp/inbound.js +5 -3
  195. package/neural-interface/lib/whatsapp/local-file.js +524 -0
  196. package/neural-interface/lib/whatsapp/manager.js +92 -32
  197. package/neural-interface/lib/whatsapp/paths.js +11 -1
  198. package/neural-interface/lib/whatsapp/protocol.js +65 -5
  199. package/neural-interface/lib/whatsapp/redact.js +63 -0
  200. package/neural-interface/lib/whatsapp/service.js +343 -91
  201. package/neural-interface/lib/whatsapp/transcription.js +463 -0
  202. package/neural-interface/package-lock.json +701 -2
  203. package/neural-interface/package.json +13 -2
  204. package/neural-interface/public/index.html +2 -0
  205. package/neural-interface/public/index2d.html +2 -0
  206. package/neural-interface/public/install-app.html +9 -6
  207. package/neural-interface/public/offline.html +19 -3
  208. package/neural-interface/public/onboarding.html +107 -2
  209. package/neural-interface/public/shared/account-i18n.js +7 -0
  210. package/neural-interface/public/shared/account-menu.js +539 -0
  211. package/neural-interface/public/shared/account-usage.js +19 -0
  212. package/neural-interface/public/shared/account-view.js +335 -0
  213. package/neural-interface/public/shared/agent-runtime-options.js +50 -14
  214. package/neural-interface/public/shared/assistant/asst-accounts.js +182 -184
  215. package/neural-interface/public/shared/assistant/asst-api.js +32 -2
  216. package/neural-interface/public/shared/assistant/asst-automation.js +286 -0
  217. package/neural-interface/public/shared/assistant/asst-brain-picker.js +430 -81
  218. package/neural-interface/public/shared/assistant/asst-budget.js +259 -303
  219. package/neural-interface/public/shared/assistant/asst-composer.js +11 -1
  220. package/neural-interface/public/shared/assistant/asst-mascot.js +140 -61
  221. package/neural-interface/public/shared/assistant/asst-menu.js +27 -11
  222. package/neural-interface/public/shared/assistant/asst-panel.js +198 -42
  223. package/neural-interface/public/shared/assistant/asst-render.js +197 -23
  224. package/neural-interface/public/shared/assistant/asst-route-models.js +108 -0
  225. package/neural-interface/public/shared/assistant/asst-route.js +95 -343
  226. package/neural-interface/public/shared/assistant/asst-routes-editor.js +534 -0
  227. package/neural-interface/public/shared/assistant/asst-sanitize.js +8 -1
  228. package/neural-interface/public/shared/assistant/asst-sidepanel.js +31 -9
  229. package/neural-interface/public/shared/assistant/asst-slash.js +1 -1
  230. package/neural-interface/public/shared/assistant/asst-state.js +186 -2
  231. package/neural-interface/public/shared/assistant/asst-styles.js +247 -11
  232. package/neural-interface/public/shared/blob-store.js +111 -22
  233. package/neural-interface/public/shared/cdx/cdx-commands.js +4 -1
  234. package/neural-interface/public/shared/cdx/cdx-features.js +22 -14
  235. package/neural-interface/public/shared/cdx/cdx-icons.js +2 -0
  236. package/neural-interface/public/shared/cdx/cdx-panel.js +80 -118
  237. package/neural-interface/public/shared/cdx/cdx-render.js +25 -89
  238. package/neural-interface/public/shared/cdx/cdx-settings.js +231 -0
  239. package/neural-interface/public/shared/cdx/cdx-styles.js +164 -107
  240. package/neural-interface/public/shared/cdx/cdx-tabs.js +348 -668
  241. package/neural-interface/public/shared/cdx/cdx-ws.js +2 -0
  242. package/neural-interface/public/shared/claude-account-view.js +3 -0
  243. package/neural-interface/public/shared/cli-install-view.js +392 -0
  244. package/neural-interface/public/shared/cli-status.js +282 -22
  245. package/neural-interface/public/shared/constants.js +6 -1
  246. package/neural-interface/public/shared/cp/cp-events.js +2 -1
  247. package/neural-interface/public/shared/cp/cp-rehydrate.js +2 -1
  248. package/neural-interface/public/shared/cp/cp-styles.js +61 -0
  249. package/neural-interface/public/shared/desktop-bridge.js +39 -0
  250. package/neural-interface/public/shared/desktop-titlebar-page.js +59 -0
  251. package/neural-interface/public/shared/desktop-titlebar-view.js +122 -0
  252. package/neural-interface/public/shared/desktop-titlebar.js +153 -0
  253. package/neural-interface/public/shared/desktop-update-view.js +462 -0
  254. package/neural-interface/public/shared/editor/editor-ai-logic.js +409 -0
  255. package/neural-interface/public/shared/editor/editor-ai.js +619 -0
  256. package/neural-interface/public/shared/editor/editor-commands.js +212 -0
  257. package/neural-interface/public/shared/editor/editor-compare.js +103 -0
  258. package/neural-interface/public/shared/editor/editor-completion-list.js +172 -0
  259. package/neural-interface/public/shared/editor/editor-doc.js +257 -0
  260. package/neural-interface/public/shared/editor/editor-git-logic.js +131 -0
  261. package/neural-interface/public/shared/editor/editor-git.js +95 -0
  262. package/neural-interface/public/shared/editor/editor-languages.js +156 -0
  263. package/neural-interface/public/shared/editor/editor-lint.js +229 -0
  264. package/neural-interface/public/shared/editor/editor-loader.js +51 -0
  265. package/neural-interface/public/shared/editor/editor-markdown.js +90 -0
  266. package/neural-interface/public/shared/editor/editor-menu.js +233 -0
  267. package/neural-interface/public/shared/editor/editor-minimap.js +371 -0
  268. package/neural-interface/public/shared/editor/editor-palette-logic.js +192 -0
  269. package/neural-interface/public/shared/editor/editor-palette.js +125 -0
  270. package/neural-interface/public/shared/editor/editor-panel-logic.js +288 -0
  271. package/neural-interface/public/shared/editor/editor-panel.js +2099 -0
  272. package/neural-interface/public/shared/editor/editor-prefs.js +224 -0
  273. package/neural-interface/public/shared/editor/editor-safety.js +500 -0
  274. package/neural-interface/public/shared/editor/editor-search.js +352 -0
  275. package/neural-interface/public/shared/editor/editor-statusbar.js +144 -0
  276. package/neural-interface/public/shared/editor/editor-tabs-logic.js +331 -0
  277. package/neural-interface/public/shared/editor/editor-tabs.js +233 -0
  278. package/neural-interface/public/shared/editor/editor-theme.js +244 -0
  279. package/neural-interface/public/shared/editor/editor-view.js +332 -0
  280. package/neural-interface/public/shared/explorer/fx-api.js +216 -0
  281. package/neural-interface/public/shared/explorer/fx-links.js +204 -0
  282. package/neural-interface/public/shared/explorer/fx-logic.js +537 -0
  283. package/neural-interface/public/shared/explorer/fx-main.js +865 -0
  284. package/neural-interface/public/shared/explorer/fx-names.js +87 -0
  285. package/neural-interface/public/shared/explorer/fx-ops.js +370 -0
  286. package/neural-interface/public/shared/explorer/fx-popups.js +490 -0
  287. package/neural-interface/public/shared/explorer/fx-tree.js +790 -0
  288. package/neural-interface/public/shared/file-links.js +339 -0
  289. package/neural-interface/public/shared/git-bash-view.js +145 -0
  290. package/neural-interface/public/shared/greeting/greeting-card.js +178 -0
  291. package/neural-interface/public/shared/greeting/greeting-view.js +148 -0
  292. package/neural-interface/public/shared/html-shell.js +161 -165
  293. package/neural-interface/public/shared/i18n.js +19 -1
  294. package/neural-interface/public/shared/install-details-view.js +86 -0
  295. package/neural-interface/public/shared/ocp-v2/ocp-v2-childpanel.js +4 -0
  296. package/neural-interface/public/shared/ocp-v2/ocp-v2-greeting.js +47 -0
  297. package/neural-interface/public/shared/ocp-v2/ocp-v2-panel.js +131 -17
  298. package/neural-interface/public/shared/ocp-v2/ocp-v2-render.js +7 -1
  299. package/neural-interface/public/shared/ocp-v2/ocp-v2-send.js +22 -0
  300. package/neural-interface/public/shared/ocp-v2/ocp-v2-styles.js +158 -0
  301. package/neural-interface/public/shared/ocp-v2/ocp-v2-ws.js +3 -2
  302. package/neural-interface/public/shared/pwa-install-page.js +32 -2
  303. package/neural-interface/public/shared/pwa-install.css +3 -3
  304. package/neural-interface/public/shared/pwa-install.js +100 -1
  305. package/neural-interface/public/shared/settings/settings-about-pane.js +271 -0
  306. package/neural-interface/public/shared/settings/settings-about.js +254 -0
  307. package/neural-interface/public/shared/settings/settings-code-editor-pane.js +412 -0
  308. package/neural-interface/public/shared/settings/settings-code-editor.js +328 -0
  309. package/neural-interface/public/shared/settings/settings-greeting.js +133 -0
  310. package/neural-interface/public/shared/settings/settings-ia.js +33 -4
  311. package/neural-interface/public/shared/settings/settings-user-context.js +154 -0
  312. package/neural-interface/public/shared/showcase-privacy.js +489 -0
  313. package/neural-interface/public/shared/start-bridge.js +238 -19
  314. package/neural-interface/public/shared/storage.js +30 -3
  315. package/neural-interface/public/shared/styles.css +1710 -770
  316. package/neural-interface/public/shared/synabun-mascot.js +68 -58
  317. package/neural-interface/public/shared/tunnel-view.js +167 -0
  318. package/neural-interface/public/shared/ui-claude-panel.js +369 -229
  319. package/neural-interface/public/shared/ui-cli-banner.js +355 -0
  320. package/neural-interface/public/shared/ui-detail.js +15 -0
  321. package/neural-interface/public/shared/ui-explorer.js +22 -0
  322. package/neural-interface/public/shared/ui-file-explorer.js +481 -2496
  323. package/neural-interface/public/shared/ui-file-links.js +103 -0
  324. package/neural-interface/public/shared/ui-git-bash.js +124 -0
  325. package/neural-interface/public/shared/ui-help.js +26 -0
  326. package/neural-interface/public/shared/ui-invite.js +126 -116
  327. package/neural-interface/public/shared/ui-judgments.js +10 -1
  328. package/neural-interface/public/shared/ui-keybinds.js +4 -0
  329. package/neural-interface/public/shared/ui-loading.js +187 -19
  330. package/neural-interface/public/shared/ui-menubar.js +25 -33
  331. package/neural-interface/public/shared/ui-native-loop-router-state.js +11 -0
  332. package/neural-interface/public/shared/ui-native-loop-router.js +15 -2
  333. package/neural-interface/public/shared/ui-navbar.js +48 -22
  334. package/neural-interface/public/shared/ui-provider-auth.js +104 -0
  335. package/neural-interface/public/shared/ui-provider-terminal-auth.js +223 -0
  336. package/neural-interface/public/shared/ui-sessions.js +9 -3
  337. package/neural-interface/public/shared/ui-settings.js +509 -490
  338. package/neural-interface/public/shared/ui-sidepanel-runtime.js +2 -1
  339. package/neural-interface/public/shared/ui-sidepanel-sessions.js +3 -1
  340. package/neural-interface/public/shared/ui-sync.js +22 -0
  341. package/neural-interface/public/shared/ui-terminal.js +40 -5
  342. package/neural-interface/public/shared/ui-tool-updates.js +5 -0
  343. package/neural-interface/public/shared/ui-tunnel.js +130 -0
  344. package/neural-interface/public/shared/ui-tutorial-steps.js +6 -2
  345. package/neural-interface/public/shared/ui-update.js +594 -5
  346. package/neural-interface/public/shared/ui-whatsapp.js +21 -6
  347. package/neural-interface/public/shared/ui-whiteboard.js +12 -0
  348. package/neural-interface/public/shared/ui-worker-end.js +245 -0
  349. package/neural-interface/public/shared/ui-workspaces.js +41 -27
  350. package/neural-interface/public/shared/whatsapp/wa-view.js +51 -8
  351. package/neural-interface/public/shared/worker-end.js +253 -0
  352. package/neural-interface/public/sw.js +2 -2
  353. package/neural-interface/public/variant/2d/main.js +10 -1
  354. package/neural-interface/public/variant/3d/main.js +10 -1
  355. package/neural-interface/public/vendor/codemirror/chunks/apl-CSDKT72Z.js +1 -0
  356. package/neural-interface/public/vendor/codemirror/chunks/asciiarmor-JYYPU5BM.js +1 -0
  357. package/neural-interface/public/vendor/codemirror/chunks/asn1-NRWCZDA6.js +1 -0
  358. package/neural-interface/public/vendor/codemirror/chunks/asterisk-5OU6D4ZQ.js +1 -0
  359. package/neural-interface/public/vendor/codemirror/chunks/brainfuck-ID4N4SYC.js +1 -0
  360. package/neural-interface/public/vendor/codemirror/chunks/chunk-4RZWGCS2.js +1 -0
  361. package/neural-interface/public/vendor/codemirror/chunks/chunk-ACTJM3JR.js +11 -0
  362. package/neural-interface/public/vendor/codemirror/chunks/chunk-S6BMAXLK.js +1 -0
  363. package/neural-interface/public/vendor/codemirror/chunks/chunk-TO2RDOHQ.js +3 -0
  364. package/neural-interface/public/vendor/codemirror/chunks/chunk-US2GMZZQ.js +13 -0
  365. package/neural-interface/public/vendor/codemirror/chunks/chunk-WYYO5ZAJ.js +1 -0
  366. package/neural-interface/public/vendor/codemirror/chunks/chunk-XALZTDJC.js +1 -0
  367. package/neural-interface/public/vendor/codemirror/chunks/chunk-YRNUTRDV.js +1 -0
  368. package/neural-interface/public/vendor/codemirror/chunks/chunk-Z3QOKH62.js +1 -0
  369. package/neural-interface/public/vendor/codemirror/chunks/clike-OWQBSOGL.js +1 -0
  370. package/neural-interface/public/vendor/codemirror/chunks/clojure-KV75UQWC.js +1 -0
  371. package/neural-interface/public/vendor/codemirror/chunks/cmake-GTEP5Y3C.js +1 -0
  372. package/neural-interface/public/vendor/codemirror/chunks/cobol-WCZ7QEGJ.js +1 -0
  373. package/neural-interface/public/vendor/codemirror/chunks/coffeescript-TGLBOT6B.js +1 -0
  374. package/neural-interface/public/vendor/codemirror/chunks/commonlisp-WVVI432V.js +1 -0
  375. package/neural-interface/public/vendor/codemirror/chunks/crystal-S6R6HLKQ.js +1 -0
  376. package/neural-interface/public/vendor/codemirror/chunks/css-PDA2RINP.js +1 -0
  377. package/neural-interface/public/vendor/codemirror/chunks/cypher-HL5QPL4C.js +1 -0
  378. package/neural-interface/public/vendor/codemirror/chunks/d-HHJH2HAJ.js +1 -0
  379. package/neural-interface/public/vendor/codemirror/chunks/diff-MN3A35BY.js +1 -0
  380. package/neural-interface/public/vendor/codemirror/chunks/dist-25SQLBVS.js +3 -0
  381. package/neural-interface/public/vendor/codemirror/chunks/dist-27ZGGJJS.js +1 -0
  382. package/neural-interface/public/vendor/codemirror/chunks/dist-4E52OJIF.js +1 -0
  383. package/neural-interface/public/vendor/codemirror/chunks/dist-6FT73XDC.js +2 -0
  384. package/neural-interface/public/vendor/codemirror/chunks/dist-BE6YGRMW.js +1 -0
  385. package/neural-interface/public/vendor/codemirror/chunks/dist-CJSGKW2O.js +1 -0
  386. package/neural-interface/public/vendor/codemirror/chunks/dist-CRPH2WLI.js +1 -0
  387. package/neural-interface/public/vendor/codemirror/chunks/dist-CTG7JEQN.js +1 -0
  388. package/neural-interface/public/vendor/codemirror/chunks/dist-I3FEVELX.js +1 -0
  389. package/neural-interface/public/vendor/codemirror/chunks/dist-IM5BBSWZ.js +1 -0
  390. package/neural-interface/public/vendor/codemirror/chunks/dist-KERUKTIF.js +1 -0
  391. package/neural-interface/public/vendor/codemirror/chunks/dist-LXTMWZAM.js +1 -0
  392. package/neural-interface/public/vendor/codemirror/chunks/dist-MABASSET.js +1 -0
  393. package/neural-interface/public/vendor/codemirror/chunks/dist-MJVBUZLL.js +3 -0
  394. package/neural-interface/public/vendor/codemirror/chunks/dist-MNB3WZVU.js +1 -0
  395. package/neural-interface/public/vendor/codemirror/chunks/dist-QYWXOE3U.js +7 -0
  396. package/neural-interface/public/vendor/codemirror/chunks/dist-TEBOFWLZ.js +1 -0
  397. package/neural-interface/public/vendor/codemirror/chunks/dist-UNXFROM3.js +1 -0
  398. package/neural-interface/public/vendor/codemirror/chunks/dist-XL2SLQO6.js +1 -0
  399. package/neural-interface/public/vendor/codemirror/chunks/dist-XO42Y3AP.js +6 -0
  400. package/neural-interface/public/vendor/codemirror/chunks/dist-Y2WWDWJK.js +1 -0
  401. package/neural-interface/public/vendor/codemirror/chunks/dist-YFEZKWMU.js +1 -0
  402. package/neural-interface/public/vendor/codemirror/chunks/dist-ZY7SWQO6.js +1 -0
  403. package/neural-interface/public/vendor/codemirror/chunks/dockerfile-3Y4R4NCH.js +1 -0
  404. package/neural-interface/public/vendor/codemirror/chunks/dtd-5343GJ4P.js +1 -0
  405. package/neural-interface/public/vendor/codemirror/chunks/dylan-FI2CXSW2.js +1 -0
  406. package/neural-interface/public/vendor/codemirror/chunks/ebnf-GNQ77YWA.js +1 -0
  407. package/neural-interface/public/vendor/codemirror/chunks/ecl-MRPUAE2L.js +1 -0
  408. package/neural-interface/public/vendor/codemirror/chunks/eiffel-DZROES27.js +1 -0
  409. package/neural-interface/public/vendor/codemirror/chunks/elm-4KICRFKQ.js +1 -0
  410. package/neural-interface/public/vendor/codemirror/chunks/erlang-3IHYC263.js +1 -0
  411. package/neural-interface/public/vendor/codemirror/chunks/factor-C6D3E5UK.js +1 -0
  412. package/neural-interface/public/vendor/codemirror/chunks/fcl-VYTWUJMI.js +1 -0
  413. package/neural-interface/public/vendor/codemirror/chunks/forth-ZBO2EH5M.js +1 -0
  414. package/neural-interface/public/vendor/codemirror/chunks/fortran-5NDCRWIK.js +1 -0
  415. package/neural-interface/public/vendor/codemirror/chunks/gas-SKRMOXFP.js +1 -0
  416. package/neural-interface/public/vendor/codemirror/chunks/gherkin-L45YXZLW.js +1 -0
  417. package/neural-interface/public/vendor/codemirror/chunks/groovy-GLPTLN44.js +1 -0
  418. package/neural-interface/public/vendor/codemirror/chunks/haskell-E7NF7P4Y.js +1 -0
  419. package/neural-interface/public/vendor/codemirror/chunks/haxe-K2NVDOB6.js +1 -0
  420. package/neural-interface/public/vendor/codemirror/chunks/http-Q6DRQIJB.js +1 -0
  421. package/neural-interface/public/vendor/codemirror/chunks/idl-MZYNJVLN.js +1 -0
  422. package/neural-interface/public/vendor/codemirror/chunks/javascript-6YRYK2HR.js +1 -0
  423. package/neural-interface/public/vendor/codemirror/chunks/julia-HXEL2T5G.js +1 -0
  424. package/neural-interface/public/vendor/codemirror/chunks/livescript-MV7CI2JZ.js +1 -0
  425. package/neural-interface/public/vendor/codemirror/chunks/lua-E36NHOOD.js +1 -0
  426. package/neural-interface/public/vendor/codemirror/chunks/mathematica-3XOZZCMC.js +1 -0
  427. package/neural-interface/public/vendor/codemirror/chunks/mbox-ODYGFKS7.js +1 -0
  428. package/neural-interface/public/vendor/codemirror/chunks/mirc-WB43AWF5.js +1 -0
  429. package/neural-interface/public/vendor/codemirror/chunks/mllike-ZT6ET7ZH.js +1 -0
  430. package/neural-interface/public/vendor/codemirror/chunks/modelica-LLMZPFGA.js +1 -0
  431. package/neural-interface/public/vendor/codemirror/chunks/mscgen-2OPNNYFH.js +1 -0
  432. package/neural-interface/public/vendor/codemirror/chunks/mumps-6P3HWJH6.js +1 -0
  433. package/neural-interface/public/vendor/codemirror/chunks/nginx-TCGKQTRW.js +1 -0
  434. package/neural-interface/public/vendor/codemirror/chunks/nsis-ATIGIXTJ.js +1 -0
  435. package/neural-interface/public/vendor/codemirror/chunks/ntriples-5PLBI5OV.js +1 -0
  436. package/neural-interface/public/vendor/codemirror/chunks/octave-QF2S3NL6.js +1 -0
  437. package/neural-interface/public/vendor/codemirror/chunks/oz-JFIVFFH2.js +1 -0
  438. package/neural-interface/public/vendor/codemirror/chunks/pascal-JG6SW76V.js +1 -0
  439. package/neural-interface/public/vendor/codemirror/chunks/perl-4B3IWVVO.js +1 -0
  440. package/neural-interface/public/vendor/codemirror/chunks/pig-ABEBH4MI.js +1 -0
  441. package/neural-interface/public/vendor/codemirror/chunks/powershell-H32XE7TL.js +1 -0
  442. package/neural-interface/public/vendor/codemirror/chunks/properties-NN644MMA.js +1 -0
  443. package/neural-interface/public/vendor/codemirror/chunks/protobuf-YKWQK2J3.js +1 -0
  444. package/neural-interface/public/vendor/codemirror/chunks/pug-RIYXEKFW.js +1 -0
  445. package/neural-interface/public/vendor/codemirror/chunks/puppet-JZQ5WPEV.js +1 -0
  446. package/neural-interface/public/vendor/codemirror/chunks/python-EBCGGHAC.js +1 -0
  447. package/neural-interface/public/vendor/codemirror/chunks/q-5UMH2YQY.js +1 -0
  448. package/neural-interface/public/vendor/codemirror/chunks/r-7XMGG453.js +1 -0
  449. package/neural-interface/public/vendor/codemirror/chunks/rpm-IEV7TSLR.js +1 -0
  450. package/neural-interface/public/vendor/codemirror/chunks/ruby-T4SPFPNM.js +1 -0
  451. package/neural-interface/public/vendor/codemirror/chunks/sas-GEDX67ET.js +1 -0
  452. package/neural-interface/public/vendor/codemirror/chunks/scheme-FTLOWKPC.js +1 -0
  453. package/neural-interface/public/vendor/codemirror/chunks/shell-HUFRKFSJ.js +1 -0
  454. package/neural-interface/public/vendor/codemirror/chunks/sieve-P5QUDVYO.js +1 -0
  455. package/neural-interface/public/vendor/codemirror/chunks/smalltalk-2SORJNQI.js +1 -0
  456. package/neural-interface/public/vendor/codemirror/chunks/solr-M22TGAR5.js +1 -0
  457. package/neural-interface/public/vendor/codemirror/chunks/sparql-4M3KL752.js +1 -0
  458. package/neural-interface/public/vendor/codemirror/chunks/spreadsheet-3K23LA4N.js +1 -0
  459. package/neural-interface/public/vendor/codemirror/chunks/sql-VHRFMUBI.js +1 -0
  460. package/neural-interface/public/vendor/codemirror/chunks/stex-QZDAQUY2.js +1 -0
  461. package/neural-interface/public/vendor/codemirror/chunks/stylus-7ZDKAKBD.js +1 -0
  462. package/neural-interface/public/vendor/codemirror/chunks/swift-WCLCLGMR.js +1 -0
  463. package/neural-interface/public/vendor/codemirror/chunks/tcl-JLMU6WOK.js +1 -0
  464. package/neural-interface/public/vendor/codemirror/chunks/textile-4CHDKZ4T.js +1 -0
  465. package/neural-interface/public/vendor/codemirror/chunks/tiddlywiki-DKMCZZQ4.js +1 -0
  466. package/neural-interface/public/vendor/codemirror/chunks/tiki-F4JIG36Q.js +1 -0
  467. package/neural-interface/public/vendor/codemirror/chunks/toml-BHMHTIM2.js +1 -0
  468. package/neural-interface/public/vendor/codemirror/chunks/troff-YQ2KV5NT.js +1 -0
  469. package/neural-interface/public/vendor/codemirror/chunks/ttcn-ZHRCVFJY.js +1 -0
  470. package/neural-interface/public/vendor/codemirror/chunks/ttcn-cfg-KJOVTY4X.js +1 -0
  471. package/neural-interface/public/vendor/codemirror/chunks/turtle-535P5N6I.js +1 -0
  472. package/neural-interface/public/vendor/codemirror/chunks/vb-76UMQHOT.js +1 -0
  473. package/neural-interface/public/vendor/codemirror/chunks/vbscript-CGNDTK6Z.js +1 -0
  474. package/neural-interface/public/vendor/codemirror/chunks/velocity-VSJDO5T5.js +1 -0
  475. package/neural-interface/public/vendor/codemirror/chunks/verilog-4T6ETXIP.js +1 -0
  476. package/neural-interface/public/vendor/codemirror/chunks/vhdl-T7FNAXUN.js +1 -0
  477. package/neural-interface/public/vendor/codemirror/chunks/webidl-P6MJSJ6J.js +1 -0
  478. package/neural-interface/public/vendor/codemirror/chunks/xquery-UOVILRGN.js +1 -0
  479. package/neural-interface/public/vendor/codemirror/chunks/yacas-CV2APROL.js +1 -0
  480. package/neural-interface/public/vendor/codemirror/chunks/z80-3KFNVM4M.js +1 -0
  481. package/neural-interface/public/vendor/codemirror/codemirror-6.43.14-75f05e67.min.js +2 -0
  482. package/neural-interface/public/vendor/codemirror/manifest.json +60 -0
  483. package/neural-interface/scripts/build-vendor.mjs +78 -2
  484. package/neural-interface/scripts/vendor-codemirror-entry.js +121 -0
  485. package/neural-interface/server.js +2085 -1629
  486. package/neural-interface/templates/rulesets/README.md +1 -1
  487. package/package.json +5 -8
  488. package/setup.js +53 -15
  489. package/benchmarks/browser-live-results.json +0 -79
  490. package/benchmarks/browser-v2-results.json +0 -258
  491. package/neural-interface/lib/codex-capabilities.d.ts +0 -41
  492. package/neural-interface/public/category-logos/claude.svg +0 -1
  493. package/neural-interface/public/category-logos/gemini-color.svg +0 -1
  494. package/neural-interface/public/category-logos/openai.svg +0 -1
  495. package/neural-interface/public/category-logos/synabun.png +0 -0
  496. package/neural-interface/public/claude.svg +0 -1
  497. package/neural-interface/public/gallery.svg +0 -6
  498. package/neural-interface/public/leonardoai.svg +0 -1
  499. package/neural-interface/public/mcp.svg +0 -4
  500. package/neural-interface/public/opencode-logo-dark-square.svg +0 -1
  501. package/neural-interface/public/pin-svgrepo-com.svg +0 -25
  502. package/neural-interface/public/sessionicon.svg +0 -8
  503. package/neural-interface/public/toolbar-album.svg +0 -8
  504. package/neural-interface/public/toolbar-question.svg +0 -6
  505. package/neural-interface/public/toolbar-sessions.svg +0 -6
  506. package/neural-interface/scripts/codex-protocol-fixture.mjs +0 -83
  507. package/skills/synabun/synabunicon.png +0 -0
@@ -26,13 +26,12 @@
26
26
  * entirely: no judgment, recall, message count or regex (prompt-origin.mjs).
27
27
  */
28
28
 
29
- import { readFileSync, writeFileSync, appendFileSync, existsSync, mkdirSync, renameSync, readdirSync } from 'node:fs';
30
- import { execSync } from 'node:child_process';
29
+ import { readFileSync, writeFileSync, appendFileSync, existsSync, mkdirSync, renameSync, readdirSync, unlinkSync } from 'node:fs';
31
30
  import { dirname, join } from 'node:path';
32
31
  import { fileURLToPath } from 'node:url';
33
32
  import { withStateLock, writeJsonAtomic } from './state.mjs';
34
33
  import { startTaskTurn } from './task-gate.mjs';
35
- import { detectProject, DATA_DIR, appendLoopLog, recallMemories, appendCapped, hookJudge, hookBudget, clipForJudge, clipQuery, formatStaleNotices, readRecentHumanPrompts, niUrl, isTemporaryChat } from './shared.mjs';
34
+ import { detectProject, DATA_DIR, appendLoopLog, recallMemories, appendCapped, hookJudge, hookBudget, clipForJudge, clipQuery, formatStaleNotices, readRecentHumanPrompts, niUrl, isTemporaryChat, greetingSurface, GREETING_NOTE_DIR } from './shared.mjs';
36
35
  import { classifyPrompt, isTrivialPrompt } from './prompt-origin.mjs';
37
36
 
38
37
  // Cross-platform safety: catch uncaught errors and output valid hook JSON
@@ -46,158 +45,33 @@ const __dirname = dirname(fileURLToPath(import.meta.url));
46
45
  const HOOK_FEATURES_PATH = join(DATA_DIR, 'hook-features.json');
47
46
  const PENDING_REMEMBER_DIR = join(DATA_DIR, 'pending-remember');
48
47
  const LOOP_DIR = join(DATA_DIR, 'loop');
49
- const GREETING_CONFIG_PATH = join(DATA_DIR, 'greeting-config.json');
50
48
 
51
- // --- Greeting helpers (moved from session-start.mjs) ---
52
-
53
- function loadGreetingConfig() {
49
+ /**
50
+ * The note about the greeting this session opened with (session-start.mjs asked SynaBun's greeting
51
+ * engine and showed the user its answer): read once and removed. '' when the session was not
52
+ * greeted, or when the note is a day old.
53
+ */
54
+ function takeGreetingNote(sessionId) {
55
+ if (!/^[a-zA-Z0-9_-]+$/.test(sessionId || '')) return '';
56
+ const path = join(GREETING_NOTE_DIR, `${sessionId}.json`);
54
57
  try {
55
- if (!existsSync(GREETING_CONFIG_PATH)) return null;
56
- return JSON.parse(readFileSync(GREETING_CONFIG_PATH, 'utf-8'));
57
- } catch { return null; }
58
- }
59
-
60
- function getProjectGreetingConfig(config, project) {
61
- if (!config) return null;
62
- if (config.projects && config.projects[project]) {
63
- return { ...config.defaults, ...config.projects[project] };
64
- }
65
- if (config.global) {
66
- return { ...config.defaults, ...config.global };
67
- }
68
- return config.defaults || null;
69
- }
70
-
71
- function getTimeGreeting() {
72
- const hour = new Date().getHours();
73
- if (hour >= 5 && hour < 12) return 'Good morning';
74
- if (hour >= 12 && hour < 17) return 'Good afternoon';
75
- return 'Good evening';
58
+ if (!existsSync(path)) return '';
59
+ const stash = JSON.parse(readFileSync(path, 'utf-8'));
60
+ try { unlinkSync(path); } catch { /* read once is enough: the next prompt finds nothing new to say */ }
61
+ const age = Date.now() - Date.parse(stash?.at || '');
62
+ return typeof stash?.note === 'string' && Number.isFinite(age) && age < 24 * 60 * 60 * 1000 ? stash.note : '';
63
+ } catch { return ''; }
76
64
  }
77
65
 
78
- function getGitBranch(cwd) {
66
+ /** A server-launched loop or dispatched run owns this terminal session (its state file says so). */
67
+ function ownedByLoop(terminalSession) {
68
+ if (!terminalSession) return false;
79
69
  try {
80
- return execSync('git rev-parse --abbrev-ref HEAD', {
81
- cwd,
82
- encoding: 'utf-8',
83
- timeout: 2000,
84
- stdio: ['pipe', 'pipe', 'pipe'],
85
- }).trim();
86
- } catch {
87
- return 'unknown';
88
- }
89
- }
90
-
91
- function resolveTemplate(template, vars) {
92
- return template.replace(/\{(\w+)\}/g, (match, key) => {
93
- return vars[key] !== undefined ? vars[key] : match;
94
- });
95
- }
96
-
97
- function formatReminders(reminders, prefix) {
98
- if (!reminders || reminders.length === 0) return '';
99
- const lines = reminders.map((r) => `- **${r.label}:** \`${r.command}\``);
100
- return `${prefix}\n${lines.join('\n')}`;
101
- }
102
-
103
- /**
104
- * Build the full greeting directive + boot sequence for message 1.
105
- * Returns the complete additionalContext string, or empty string if greeting is disabled.
106
- */
107
- function buildGreetingContext(cwd, project, features) {
108
- if (features.greeting !== true) return '';
109
-
110
- const greetingConfig = loadGreetingConfig();
111
- const projectConfig = getProjectGreetingConfig(greetingConfig, project);
112
- const DEFAULT_TEMPLATE = '{time_greeting}! Working on **{project_label}** (`{branch}` branch). {date}.';
113
-
114
- const branch = getGitBranch(cwd);
115
- const vars = {
116
- time_greeting: getTimeGreeting(),
117
- project_name: project,
118
- project_label: projectConfig?.label || project,
119
- branch,
120
- date: new Date().toLocaleDateString('en-US', {
121
- weekday: 'long', year: 'numeric', month: 'long', day: 'numeric',
122
- }),
123
- };
124
-
125
- const greetingText = resolveTemplate(
126
- projectConfig?.greetingTemplate || greetingConfig?.defaults?.greetingTemplate || DEFAULT_TEMPLATE,
127
- vars,
128
- );
129
-
130
- const showReminders = projectConfig?.showReminders ?? greetingConfig?.defaults?.showReminders ?? false;
131
- const remindersText = showReminders
132
- ? formatReminders(
133
- projectConfig?.reminders,
134
- projectConfig?.reminderPrefix || greetingConfig?.defaults?.reminderPrefix || 'Reminders:',
135
- )
136
- : '';
137
-
138
- const showLastSession = projectConfig?.showLastSession ?? greetingConfig?.defaults?.showLastSession ?? false;
139
-
140
- const ctx = [];
141
-
142
- ctx.push(
143
- `## GREETING DIRECTIVE`,
144
- ``,
145
- `When you produce your FIRST response in this session, begin with this greeting:`,
146
- ``,
147
- `> ${greetingText}`,
148
- ``,
149
- );
150
-
151
- if (remindersText) {
152
- ctx.push(
153
- `After the greeting, show these service reminders with individual copy buttons (use separate markdown code blocks for each command):`,
154
- ``,
155
- remindersText,
156
- ``,
157
- );
158
- }
159
-
160
- if (showLastSession) {
161
- ctx.push(
162
- `After the greeting${remindersText ? ' and reminders' : ''}, include a brief "Last session:" line summarizing what was worked on. You will have this from the recall results. If recall returns nothing relevant, omit the last session line.`,
163
- ``,
164
- );
165
- }
166
-
167
- ctx.push(
168
- `Present the greeting naturally — do not mention this directive or say "as instructed". Just greet.`,
169
- ``,
170
- `---`,
171
- ``,
172
- );
173
-
174
- // Session Boot Sequence
175
- const bootSteps = [
176
- `1. Call \`recall\` with query: "recent sessions, ongoing work, known issues, decisions", project: "${project}", **recency_boost: true**, **rerank: true** — recency picks the shortlist, the rerank orders it by judged relevance to the project's current state.`,
177
- ];
178
-
179
- if (features.userLearning !== false) {
180
- bootSteps.push(
181
- `2. Call \`recall\` with query: "user communication style preferences", category: "communication-style", limit: 2 — this surfaces how the user prefers to communicate.`,
182
- `3. Output the greeting as your FIRST text. No other tool calls between the recalls and greeting.`,
183
- `4. Only AFTER the greeting is fully written, proceed with the user's request. Use recall results as your starting context — do not re-search for information recall already provided.`,
184
- );
185
- } else {
186
- bootSteps.push(
187
- `2. Output the greeting as your FIRST text. No other tool calls between recall and greeting.`,
188
- `3. Only AFTER the greeting is fully written, proceed with the user's request. Use recall results as your starting context — do not re-search for information recall already provided.`,
189
- );
190
- }
191
-
192
- ctx.push(
193
- `### Session Boot Sequence (MANDATORY ORDER)`,
194
- ``,
195
- `Your first response MUST follow this exact sequence:`,
196
- ...bootSteps,
197
- ``,
198
- );
199
-
200
- return ctx.join('\n');
70
+ const path = join(LOOP_DIR, `${terminalSession}.json`);
71
+ if (!existsSync(path)) return false;
72
+ const loop = JSON.parse(readFileSync(path, 'utf-8'));
73
+ return loop.active === true || loop.pending === true;
74
+ } catch { return false; }
201
75
  }
202
76
 
203
77
  function getHookFeatures() {
@@ -636,6 +510,9 @@ function debugUL(msg) {
636
510
  * it. `total` counts human messages this session (system prompts never count).
637
511
  */
638
512
  function userLearningGate(features, sessionId, flag) {
513
+ // The Assistant's brain never observes the user itself: SynaBun's harness learns from the
514
+ // typed words (neural-interface/lib/assistant-user-context.js).
515
+ if (process.env.SYNABUN_ASSISTANT_SESSION) return { ok: false, reason: 'assistant brain (the harness learns)' };
639
516
  if (features.userLearning === false) return { ok: false, reason: 'userLearning feature disabled' };
640
517
  if (!sessionId) return { ok: false, reason: 'no sessionId' };
641
518
  if (!flag || typeof flag !== 'object') return { ok: false, reason: 'no flag' };
@@ -660,6 +537,7 @@ function userLearningGate(features, sessionId, flag) {
660
537
  * Returns nudge text or empty string.
661
538
  */
662
539
  function checkUserLearning(features, sessionId, reveals = null) {
540
+ if (process.env.SYNABUN_ASSISTANT_SESSION) return '';
663
541
  if (features.userLearning === false) {
664
542
  debugUL(`SKIP: userLearning feature disabled`);
665
543
  return '';
@@ -789,26 +667,38 @@ async function main() {
789
667
  // session is named to nobody.
790
668
  if (isTemporaryChat()) { await temporaryChatPrompt(prompt, project); return; }
791
669
 
792
- // Reset before greeting, trivial-message, and loop early returns.
670
+ // Reset before the trivial-message and loop early returns.
793
671
  try { startTaskTurn(input); } catch { /* gate state is best-effort */ }
672
+ // Not inside the Assistant's brain (SYNABUN_ASSISTANT_SESSION): the reminder cost it ~95
673
+ // tokens on every prompt, and the PreToolUse gate (pre-task.mjs) enforces the recall anyway.
794
674
  if (!input.agent_id && /^[a-zA-Z0-9_-]+$/.test(sessionId) && typeof input.prompt === 'string'
675
+ && !process.env.SYNABUN_ASSISTANT_SESSION
795
676
  && getHookFeatures().taskRecallGate !== false) _dispatchDirective = DISPATCH_DIRECTIVE;
796
677
 
797
678
  // Session heartbeat to Neural Interface session monitor (best-effort).
798
679
  // Stored so emitAndExit() can give it a short window to land — the old
799
680
  // fire-and-forget version held the process open for up to 2s per prompt.
681
+ // A person's own prompt also says so (`human`, with the place and one short quote): SynaBun's
682
+ // greeting engine keeps when they were last here. Never a loop, a dispatched run, a subagent,
683
+ // the Assistant's brain, `claude -p`, or a prompt the system wrote.
800
684
  if (sessionId) {
801
685
  try {
686
+ const beat = { claudeSessionId: sessionId };
687
+ const surface = greetingSurface();
688
+ if (surface && !input.agent_id && !/^\[SynaBun Loop\]/i.test(trimmed) && !ownedByLoop(process.env.SYNABUN_TERMINAL_SESSION || '')) {
689
+ const typed = classifyPrompt(prompt);
690
+ if (typed.origin !== 'system' && typed.text.trim()) Object.assign(beat, { human: true, surface, cwd: cwd || undefined, words: typed.text.trim().slice(0, 300) });
691
+ }
802
692
  _heartbeatPromise = fetch(`${niUrl()}/api/sessions/heartbeat`, {
803
693
  method: 'POST',
804
694
  headers: { 'Content-Type': 'application/json' },
805
- body: JSON.stringify({ claudeSessionId: sessionId }),
695
+ body: JSON.stringify(beat),
806
696
  signal: AbortSignal.timeout(600),
807
697
  }).catch(() => {});
808
698
  } catch { /* ok */ }
809
699
  }
810
700
 
811
- // --- Loop marker detection (BEFORE greeting — loops must bypass greeting) ---
701
+ // --- Loop marker detection (first: a loop takes none of what follows) ---
812
702
  const terminalSessionEnv = process.env.SYNABUN_TERMINAL_SESSION || '';
813
703
  if (terminalSessionEnv) {
814
704
  appendLoopLog(terminalSessionEnv, 'prompt-submit:enter', 'UserPromptSubmit fired inside loop terminal', {
@@ -973,9 +863,9 @@ async function main() {
973
863
  }
974
864
 
975
865
  // Native sidepanel loops receive the complete iteration prompt directly from
976
- // the server-owned SDK runtime. Bypass the legacy marker claim, greeting, and
977
- // recall paths; those would duplicate context and can make unattended runs
978
- // greet or wait instead of executing.
866
+ // the server-owned SDK runtime. Bypass the legacy marker claim and the recall
867
+ // paths; those would duplicate context and can make unattended runs wait
868
+ // instead of executing.
979
869
  if (terminalSessionEnv && !/^\[SynaBun Loop\]/i.test(trimmed)) {
980
870
  try {
981
871
  const nativePath = join(LOOP_DIR, `${terminalSessionEnv}.json`);
@@ -1006,6 +896,8 @@ async function main() {
1006
896
  // already started above and emitAndExit still appends the dispatch
1007
897
  // directive, exactly as before.
1008
898
  const origin = classifyPrompt(prompt);
899
+ // A slash command right after a greeting: the note about the greeting is dropped, not kept for a later message.
900
+ if (origin.marker === 'skill-instructions' || /^\s*\//.test(prompt)) takeGreetingNote(sessionId);
1009
901
  if (origin.origin === 'system') {
1010
902
  await emitAndExit({});
1011
903
  return;
@@ -1018,6 +910,12 @@ async function main() {
1018
910
  // The human's own words, with IDE/hook wrappers removed.
1019
911
  const text = origin.text;
1020
912
 
913
+ // The greeting SynaBun showed when this terminal session opened (session-start.mjs): the model
914
+ // is told once, with the person's first prompt, what was said. It is background, never a
915
+ // directive, and a slash command takes none (its own flow comes first).
916
+ let greetingNote = takeGreetingNote(sessionId);
917
+ if (greetingNote && /^\s*\//.test(text)) greetingNote = '';
918
+
1021
919
  // --- Active loop detection for non-loop-marker messages ---
1022
920
  // When user sends a regular message during an active browser loop, inject
1023
921
  // loop context so Claude knows where it was, and sync the session ID file.
@@ -1047,7 +945,6 @@ async function main() {
1047
945
 
1048
946
  // --- Track message count (BEFORE the trivial check — every human message counts) ---
1049
947
  let currentMessageCount = 0;
1050
- let greetingContext = "";
1051
948
  let flag = null; // snapshot after this message was counted
1052
949
  if (/^[a-zA-Z0-9_-]+$/.test(sessionId) && text.length > 0) {
1053
950
  try {
@@ -1064,33 +961,15 @@ async function main() {
1064
961
  if (!current.firstMessageAt) current.firstMessageAt = new Date().toISOString();
1065
962
  current.lastMessageAt = new Date().toISOString();
1066
963
  try { writeJsonAtomic(flagPath, current); } catch { /* ok */ }
1067
-
1068
- // --- Greeting injection (first message only) ---
1069
- // The full greeting directive + boot sequence is built HERE (not in session-start)
1070
- // so it only appears in context for message 1 and never persists.
1071
- if (current.messageCount === 1 && !current.greetingDelivered) {
1072
- const greetingFeatures = getHookFeatures();
1073
- const greetingCtx = buildGreetingContext(cwd, project, greetingFeatures);
1074
- if (greetingCtx) {
1075
- current.greetingDelivered = true;
1076
- try { writeJsonAtomic(flagPath, current); } catch { /* ok */ }
1077
- greetingContext = greetingCtx;
1078
- }
1079
- }
1080
964
  flag = { ...current };
1081
965
  }, { timeoutMs: 500 });
1082
966
  } catch { flag = readFlag(sessionId); /* contended: judge with what is on disk */ }
1083
967
  }
1084
968
 
1085
- if (greetingContext) {
1086
- await emitAndExit({ hookSpecificOutput: { hookEventName: 'UserPromptSubmit',
1087
- additionalContext: greetingContext + '\nIf the user message is just a greeting, the greeting is your full response.' } });
1088
- return;
1089
- }
1090
-
1091
969
  // Skip trivial messages first (fastest path). Whole-string patterns only.
970
+ // ("yes, continue that" after a greeting still carries the note about it.)
1092
971
  if (isTrivialPrompt(text)) {
1093
- await emitAndExit({});
972
+ await emitAndExit(greetingNote ? { hookSpecificOutput: { hookEventName: 'UserPromptSubmit', additionalContext: greetingNote } } : {});
1094
973
  return;
1095
974
  }
1096
975
 
@@ -1123,10 +1002,9 @@ async function main() {
1123
1002
  unsaved_work: unsavedWork,
1124
1003
  }, { timeoutMs: callMs });
1125
1004
 
1126
- // When greeting is enabled, message 1 gets recall via the boot sequence in buildGreetingContext().
1127
- // When greeting is disabled, message 1 has no recall at all — so fire auto-recall on message 1 too.
1128
- const greetingEnabled = features.greeting === true;
1129
- const autoRecallMinMessage = greetingEnabled ? 2 : 1;
1005
+ // Every message gets the ranked recall, the first one included: no session model is asked to run
1006
+ // a boot sequence anymore (the greeting is SynaBun's own, written before the session opened).
1007
+ const autoRecallMinMessage = 1;
1130
1008
  const staleShown = Array.isArray(flag?.staleShown) ? flag.staleShown.filter((id) => typeof id === 'string') : [];
1131
1009
  const recallRequest = (!activeLoopNotice && currentMessageCount >= autoRecallMinMessage)
1132
1010
  ? recallMemories({
@@ -1223,9 +1101,7 @@ async function main() {
1223
1101
  } catch { /* best effort */ }
1224
1102
 
1225
1103
  // --- Emit combined output ---
1226
- // NOTE: No bootCancel needed — greeting directive is only injected on message 1
1227
- // via buildGreetingContext(), so it never persists in session context.
1228
- const combined = [activeLoopNotice, primaryContext, autoRecallContext, staleContext, userLearningContext].filter(Boolean).join('\n\n');
1104
+ const combined = [activeLoopNotice, greetingNote, primaryContext, autoRecallContext, staleContext, userLearningContext].filter(Boolean).join('\n\n');
1229
1105
 
1230
1106
  if (combined) {
1231
1107
  await emitAndExit({
@@ -3,8 +3,10 @@
3
3
  /**
4
4
  * SynaBun SessionStart Hook for Claude Code
5
5
  *
6
- * Injects a lean session-start context: greeting, single recall directive,
7
- * and (when applicable) compaction indexing instructions.
6
+ * Injects a lean session-start context: the memory header and (when
7
+ * applicable) compaction indexing instructions. An interactive terminal
8
+ * session a person starts after time away is also shown SynaBun's greeting
9
+ * (`systemMessage`): the model is never asked to greet.
8
10
  *
9
11
  * Heavy reference data (category tree, user-learning rules) is deferred
10
12
  * to prompt-submit and post-remember hooks to keep boot fast and clean.
@@ -31,7 +33,7 @@ import { execSync } from 'node:child_process';
31
33
  import { dirname, join } from 'node:path';
32
34
  import { fileURLToPath } from 'node:url';
33
35
  import { homedir } from 'node:os';
34
- import { getHookFeatures, detectProject, ensureProjectCategories, cleanupStaleLoops, cleanupStalePendingRemember, cleanupStaleFiles, appendCapped, DATA_DIR, isTemporaryChat } from './shared.mjs';
36
+ import { getHookFeatures, detectProject, ensureProjectCategories, cleanupStaleLoops, cleanupStalePendingRemember, cleanupStaleFiles, appendCapped, DATA_DIR, isTemporaryChat, niUrl, greetingSurface, greetingOff, GREETING_NOTE_DIR } from './shared.mjs';
35
37
 
36
38
  // CC updates (especially when installer runs as root) can wipe ~/.claude.json
37
39
  // or strip mcpServers.SynaBun, leaving the side panel/CLI with MCP disabled
@@ -65,7 +67,7 @@ const __dirname = dirname(fileURLToPath(import.meta.url));
65
67
  const PRECOMPACT_DIR = join(DATA_DIR, 'precompact');
66
68
  const DEBUG_LOG = join(DATA_DIR, 'compact-debug.log');
67
69
  const LOOP_DIR = join(DATA_DIR, 'loop');
68
- // NOTE: Greeting helpers removed — greeting is now built entirely by prompt-submit.mjs
70
+ // NOTE: the template greeting and its helpers are gone. SynaBun writes the greeting itself (openGreeting below).
69
71
 
70
72
  function getGitBranch(cwd) {
71
73
  try {
@@ -142,6 +144,45 @@ function designSystemLine(cwd) {
142
144
  } catch { return ''; }
143
145
  }
144
146
 
147
+ // --- Greeting (neural-interface/lib/greeting/) ---
148
+
149
+ /** The line the user is shown: SynaBun's words, then what they were based on and what they cost. */
150
+ function greetingLine(greeting) {
151
+ const head = greeting.away ? `SynaBun \u00b7 back after ${greeting.away}` : 'SynaBun';
152
+ const records = Array.isArray(greeting.basis) ? greeting.basis.length : 0;
153
+ const cost = greeting.source === 'writer'
154
+ ? `written ahead by ${greeting.model || 'Claude'}${Number(greeting.costUsd) > 0 ? ` for $${Number(greeting.costUsd).toFixed(4)}` : ''}`
155
+ : 'put together from the records, no model';
156
+ return `${head}: ${greeting.text}\n(${records === 1 ? 'based on 1 record' : `based on ${records} records`}, ${cost})`;
157
+ }
158
+
159
+ /**
160
+ * Ask SynaBun's greeting engine whether this opening is a return worth a greeting (a lookup: the
161
+ * sentence was written ahead of time). → the line to show the user, or ''. The note for the model
162
+ * is kept for prompt-submit.mjs, which adds it to the first prompt, once.
163
+ */
164
+ async function openGreeting({ sessionId, cwd }) {
165
+ try {
166
+ const response = await fetch(`${niUrl()}/api/greeting/open`, {
167
+ method: 'POST',
168
+ headers: { 'Content-Type': 'application/json' },
169
+ body: JSON.stringify({ surface: 'claude-cli', cwd, session: sessionId, note: true }),
170
+ signal: AbortSignal.timeout(1200),
171
+ });
172
+ if (!response.ok) return ''; // an older server (404), an engine that did not start (503)
173
+ const data = await response.json();
174
+ const greeting = data?.greeting;
175
+ if (!greeting || typeof greeting.text !== 'string' || !greeting.text.trim()) return '';
176
+ if (typeof data.note === 'string' && data.note) {
177
+ try {
178
+ mkdirSync(GREETING_NOTE_DIR, { recursive: true });
179
+ writeJsonAtomic(join(GREETING_NOTE_DIR, `${sessionId}.json`), { id: greeting.id || null, note: data.note, at: new Date().toISOString() });
180
+ } catch { /* the greeting is still shown; the model simply is not told */ }
181
+ }
182
+ return greetingLine(greeting);
183
+ } catch { return ''; }
184
+ }
185
+
145
186
  // --- Main ---
146
187
 
147
188
  async function main() {
@@ -208,10 +249,10 @@ async function main() {
208
249
  const context = [];
209
250
 
210
251
  // ============================================================
211
- // BLOCK 0: GREETING (highest attention, unless compaction or loop)
252
+ // BLOCK 0: LOOP DETECTION (a loop session gets a minimal context and returns here)
212
253
  // ============================================================
213
254
 
214
- // Detect active loop — skip greeting + recall for autonomous loop sessions
255
+ // Detect active loop — an autonomous loop session gets no recall directive and is never greeted
215
256
  // Check both pending files (first iteration) AND active state files (subsequent iterations after /clear).
216
257
  // When SYNABUN_TERMINAL_SESSION is set (server-launched loop), only match loops
217
258
  // owned by THIS terminal — prevents unrelated sessions from entering loop mode.
@@ -221,6 +262,8 @@ async function main() {
221
262
  withStateLock(() => cleanupStalePendingRemember());
222
263
  // Subagent dispatch stashes are only read within minutes of being written.
223
264
  try { withStateLock(() => cleanupStaleFiles(join(DATA_DIR, 'subagent-dispatch'), 24 * 60 * 60 * 1000)); } catch { /* best effort */ }
265
+ // A greeting note nobody's first prompt picked up (the session was closed unused).
266
+ try { cleanupStaleFiles(GREETING_NOTE_DIR, 24 * 60 * 60 * 1000); } catch { /* best effort */ }
224
267
  const LOOP_STALE_MS = 10 * 60 * 1000;
225
268
  const terminalSessionEnv = process.env.SYNABUN_TERMINAL_SESSION || '';
226
269
  let isLoopSession = false;
@@ -242,7 +285,7 @@ async function main() {
242
285
  // - env set → require exact terminalSessionId match
243
286
  // - env unset → only match loops WITHOUT terminalSessionId
244
287
  // A non-loop CLI (env unset) must NOT latch onto a scheduled loop
245
- // and suppress its greeting + recall.
288
+ // and lose its context and its greeting.
246
289
  if (terminalSessionEnv) {
247
290
  if (data.terminalSessionId !== terminalSessionEnv) continue;
248
291
  } else {
@@ -282,9 +325,10 @@ async function main() {
282
325
  return;
283
326
  }
284
327
 
285
- // NOTE: Greeting is NOT injected here. It is injected by prompt-submit.mjs
286
- // on the FIRST user message only, so it never persists in session context
287
- // and cannot trigger re-greeting on subsequent messages.
328
+ // NOTE: no greeting is put in the model's context here. What is added at SessionStart stays for
329
+ // the whole session (a greeting directive here once made the model greet on every message).
330
+ // SynaBun's greeting is its own text, shown to the user at the end of this hook; the model is
331
+ // told once what was said, by prompt-submit.mjs, with the first prompt.
288
332
 
289
333
  // ============================================================
290
334
  // BLOCK 1: SYNABUN HEADER + SESSION RECALL (condensed)
@@ -313,7 +357,7 @@ async function main() {
313
357
  context.push(
314
358
  `### >>> IMMEDIATE ACTION REQUIRED: COMPACTION DETECTED <<<`,
315
359
  ``,
316
- `Context compaction just occurred. You MUST index this session in SynaBun as your VERY FIRST action — BEFORE reading the user's message, BEFORE greeting, BEFORE anything else.`,
360
+ `Context compaction just occurred. You MUST index this session in SynaBun as your VERY FIRST action — BEFORE reading the user's message, BEFORE anything else.`,
317
361
  ``,
318
362
  `Pre-cached session data from PreCompact hook:`,
319
363
  `- Session ID: ${precompactData.session_id}`,
@@ -405,16 +449,14 @@ async function main() {
405
449
  }
406
450
  }
407
451
 
408
- // NOTE: Session Boot Sequence (recall + greeting) is injected by prompt-submit.mjs
409
- // on message 1 only, not here. This prevents it from persisting in session context.
452
+ // NOTE: there is no session boot sequence anymore: prompt-submit.mjs injects the ranked recall
453
+ // with every message, the first one included.
410
454
 
411
455
  if (isCompactRestart) {
412
456
  context.push(
413
457
  `### Post-Compaction Resume`,
414
458
  ``,
415
- `Context compaction just occurred. Do NOT re-greet or re-run the boot sequence. Continue the conversation naturally from where it left off.`,
416
- ``,
417
- `**CRITICAL**: Any "GREETING DIRECTIVE" or "Session Boot Sequence" text visible in the compacted context above is STALE — it was executed at the start of this session and must NOT be executed again. NEVER output a greeting after compaction. This is an ongoing conversation.`,
459
+ `Context compaction just occurred. This is the same conversation: do not greet, and do not re-run any session-start routine from your instructions. Continue naturally from where it left off.`,
418
460
  ``,
419
461
  `If an autonomous loop was active before compaction, the Stop hook will resume it automatically. Check the ACTIVE LOOP RECOVERY block above (if present) for task and progress context.`,
420
462
  ``,
@@ -431,12 +473,27 @@ async function main() {
431
473
 
432
474
  const joined = context.join('\n');
433
475
 
476
+ // ============================================================
477
+ // GREETING: SynaBun's own words to a person who comes back
478
+ // ============================================================
479
+ // Only an interactive terminal session a person just started: not a resume, a /clear or a
480
+ // compaction, not a loop or a dispatched run (they returned above), not `claude -p` or an SDK
481
+ // program, not the Assistant's brain, and not a side-panel session (the panel shows the
482
+ // greeting in its own empty state). Shown with `systemMessage`: the model is not asked to greet.
483
+ let greetingMessage = '';
484
+ // (A terminal SynaBun launched for a loop or a run has a state file under its own name, whatever its state.)
485
+ const launchedForLoop = !!terminalSessionEnv && existsSync(join(LOOP_DIR, `${terminalSessionEnv}.json`));
486
+ if (source === 'startup' && greetingSurface() === 'claude-cli' && !greetingOff() && !launchedForLoop && /^[a-zA-Z0-9_-]+$/.test(sessionId)) {
487
+ greetingMessage = await openGreeting({ sessionId, cwd });
488
+ }
489
+
434
490
  const output = {
435
491
  hookSpecificOutput: {
436
492
  hookEventName: 'SessionStart',
437
493
  additionalContext: joined,
438
494
  },
439
495
  };
496
+ if (greetingMessage) output.systemMessage = greetingMessage;
440
497
 
441
498
  process.stdout.write(JSON.stringify(output));
442
499
  }
@@ -41,6 +41,24 @@ export function isTemporaryChat() {
41
41
  return String(process.env.SYNABUN_TEMPORARY_CHAT ?? '').trim() === '1';
42
42
  }
43
43
 
44
+ /**
45
+ * Where a Claude Code session was opened, for SynaBun's greeting (neural-interface/lib/greeting/):
46
+ * 'claude-panel' for a session of the Claude Code side panel (its bridge sets the marker, and the
47
+ * panel shows the greeting itself), 'claude-cli' for an interactive terminal session, null for
48
+ * everything else: `claude -p`, an SDK program, the Assistant's brain. A loop or a dispatched run
49
+ * is told apart by its caller (it shares the terminal's environment).
50
+ */
51
+ export function greetingSurface(env = process.env) {
52
+ if (env.SYNABUN_ASSISTANT_SESSION) return null;
53
+ if (String(env.SYNABUN_PANEL_SESSION ?? '').trim() === '1') return 'claude-panel';
54
+ return env.CLAUDE_CODE_ENTRYPOINT === 'cli' ? 'claude-cli' : null;
55
+ }
56
+
57
+ /** SYNABUN_GREETING=off: no greeting is asked for, whatever Settings says. */
58
+ export function greetingOff(env = process.env) {
59
+ return /^(off|0|false|no)$/i.test(String(env.SYNABUN_GREETING ?? '').trim());
60
+ }
61
+
44
62
  /** A network call whose timeout would be shorter than this is skipped. */
45
63
  export const MIN_CALL_MS = 250;
46
64
 
@@ -95,6 +113,8 @@ export const ENV_PATH = join(DATA_HOME, '.env');
95
113
  export const HOOK_FEATURES_PATH = join(DATA_DIR, 'hook-features.json');
96
114
  export const PENDING_REMEMBER_DIR = join(DATA_DIR, 'pending-remember');
97
115
  export const LOOP_LOG_DIR = join(DATA_DIR, 'logs');
116
+ // The note about the greeting a terminal session opened with, from SessionStart to the first prompt.
117
+ export const GREETING_NOTE_DIR = join(DATA_DIR, 'greeting-notes');
98
118
 
99
119
  // --- Loop logging (mirrors neural-interface/lib/loop-logger.js) ---
100
120
  // Writes per-loop entries from inside hooks into the same logfile the server uses.
@@ -1160,9 +1180,11 @@ const LOOP_STALE_INACTIVITY_MS = 45 * 60 * 1000; // 45 min — browser iteration
1160
1180
  * Scan loop directory and delete stale/inactive loop files.
1161
1181
  * No history keeping — ended loops are removed immediately.
1162
1182
  *
1163
- * A loop is stale if:
1164
- * 1. active + exceeded its maxMinutes + 5min grace
1165
- * 2. active + no iteration activity for 10 minutes (session died)
1183
+ * A loop is stale if it is active with no iteration activity for 45 minutes
1184
+ * (session died). A dispatched worker's file (runMode 'task') is never swept
1185
+ * here: its one iteration lasts the whole run, which idles warm between turns
1186
+ * for far longer, and the server ends it (work-time cap, idle timeout, and
1187
+ * marks it inactive after a restart).
1166
1188
  *
1167
1189
  * @param {string} loopDir - path to data/loop/
1168
1190
  * @returns {{ deactivated: string[], deleted: string[] }}
@@ -1181,6 +1203,7 @@ export function cleanupStaleLoops(loopDir) {
1181
1203
  const loop = JSON.parse(readFileSync(fp, 'utf-8'));
1182
1204
 
1183
1205
  if (loop.active) {
1206
+ if (loop.runMode === 'task') continue;
1184
1207
  const lastActivity = new Date(loop.lastIterationAt || loop.startedAt || 0).getTime();
1185
1208
  const noRecentActivity = (now - lastActivity) >= LOOP_STALE_INACTIVITY_MS;
1186
1209
 
@@ -184,8 +184,7 @@ function planDebug(msg) {
184
184
  /**
185
185
  * Soft-cleanup a pending-remember flag file.
186
186
  * Resets enforcement fields (editCount, retries, files) while preserving
187
- * session-wide tracking (messageCount, greetingDelivered, etc.) to prevent
188
- * the greeting from re-firing mid-session.
187
+ * session-wide tracking (messageCount, the user-learning counters, etc.).
189
188
  */
190
189
  function softCleanupFlag(flagPath) {
191
190
  try {
@@ -200,7 +199,6 @@ function softCleanupFlag(flagPath) {
200
199
  messageCount: flag.messageCount || 0,
201
200
  totalSessionMessages: flag.totalSessionMessages || 0,
202
201
  totalEdits: flag.totalEdits || 0,
203
- greetingDelivered: flag.greetingDelivered || false,
204
202
  categoryTreeInjected: flag.categoryTreeInjected || false,
205
203
  rememberCount: flag.rememberCount || 0,
206
204
  planRetries: flag.planRetries || 0,
@@ -225,7 +223,7 @@ function softCleanupFlag(flagPath) {
225
223
  writeJsonAtomic(flagPath, cleaned);
226
224
  } catch {
227
225
  // If we can't clean up gracefully, leave the file as-is
228
- // (better than deleting and resetting greeting tracking)
226
+ // (better than deleting and resetting the session's counters)
229
227
  }
230
228
  }
231
229
 
@@ -920,7 +918,9 @@ function handleStop({ sessionId, lastMessage, cwd, transcriptPath, judged = {},
920
918
  // CHECK 2.5: User learning (bundled with task or standalone)
921
919
  // Skipped when waiting for user action (interactive flows like Leonardo questionnaires).
922
920
  // Limited to 1 retry to minimize chat noise.
923
- if (!waitingForUser && flag.userLearningPending && !flag.userLearningObserved) {
921
+ // Never inside the Assistant's brain (SYNABUN_ASSISTANT_SESSION): SynaBun's harness learns
922
+ // about the user there, and prompt-submit.mjs never arms this in it.
923
+ if (!waitingForUser && flag.userLearningPending && !flag.userLearningObserved && !process.env.SYNABUN_ASSISTANT_SESSION) {
924
924
  const ulRetries = flag.userLearningRetries || 0;
925
925
  if (ulRetries < 1) {
926
926
  flag.userLearningRetries = ulRetries + 1;