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
@@ -0,0 +1,120 @@
1
+ import { spawnSync } from 'node:child_process';
2
+ import { mkdtempSync, readFileSync, rmSync, statSync } from 'node:fs';
3
+ import { tmpdir } from 'node:os';
4
+ import { join } from 'node:path';
5
+
6
+ const cache = new Map();
7
+ const featureCache = new Map();
8
+ export const CODEX_SETTINGS_KEYS = Object.freeze([
9
+ 'model', 'review_model', 'model_provider', 'approval_policy', 'sandbox_mode', 'personality',
10
+ 'service_tier', 'model_reasoning_effort', 'model_reasoning_summary', 'model_verbosity',
11
+ 'model_context_window', 'model_auto_compact_token_limit', 'web_search',
12
+ ]);
13
+
14
+ export function schemaEnum(schema, definitions) {
15
+ if (!schema) return [];
16
+ if (schema.$ref) return schemaEnum(definitions[schema.$ref.split('/').pop()], definitions);
17
+ return [...new Set([...(schema.enum || []).filter(v => typeof v === 'string'),
18
+ ...['anyOf', 'oneOf', 'allOf'].flatMap(k => (schema[k] || []).flatMap(s => schemaEnum(s, definitions)))])];
19
+ }
20
+
21
+ export function settingsSchemaFromProtocol(configSchema, threadSchema) {
22
+ const properties = configSchema.definitions?.Config?.properties || {};
23
+ const fields = {};
24
+ for (const key of CODEX_SETTINGS_KEYS) {
25
+ const field = properties[key];
26
+ if (!field || /@deprecated/.test(field.description || '')) continue;
27
+ const values = schemaEnum(field, configSchema.definitions || {});
28
+ fields[key] = { values, enumerated: values.length > 0,
29
+ type: key.includes('token_limit') || key === 'model_context_window' ? 'number' : 'select' };
30
+ }
31
+ // Some older runtimes expose personality only on thread/turn params. Newer
32
+ // ones explicitly retain it as a no-op: never present a no-op preference.
33
+ const personality = threadSchema.properties?.personality;
34
+ if (personality && !/@deprecated/.test(personality.description || '')) {
35
+ fields.personality = { type: 'select', values: schemaEnum(personality, threadSchema.definitions || {}) };
36
+ }
37
+ return { fields };
38
+ }
39
+
40
+ /** Generate from the executable we actually launch; never use the global home. */
41
+ export function readCodexSettingsSchema(codexBin) {
42
+ const stamp = statSync(codexBin);
43
+ const key = `${codexBin}:${stamp.mtimeMs}:${stamp.size}`;
44
+ if (cache.has(key)) return cache.get(key);
45
+ const home = mkdtempSync(join(tmpdir(), 'synabun-codex-schema-'));
46
+ try {
47
+ const out = join(home, 'schema');
48
+ const run = spawnSync(codexBin, ['app-server', 'generate-json-schema', '--experimental', '--out', out], {
49
+ env: { ...process.env, CODEX_HOME: home }, encoding: 'utf8', timeout: 15000, windowsHide: true,
50
+ });
51
+ if (run.error || run.status !== 0) throw new Error('Codex could not report its settings schema');
52
+ const read = name => JSON.parse(readFileSync(join(out, 'v2', `${name}.json`), 'utf8'));
53
+ const schema = settingsSchemaFromProtocol(read('ConfigReadResponse'), read('ThreadStartParams'));
54
+ // Protocol enums can retain deprecated variants that the config loader
55
+ // already rejects (0.162 still exports `untrusted`). Ask the loader too.
56
+ // `features list` validates config locally and never runs a model or tool.
57
+ for (const [fieldKey, field] of Object.entries(schema.fields)) {
58
+ field.values = field.values.filter(value => {
59
+ const check = spawnSync(codexBin, ['-c', `${fieldKey}=${JSON.stringify(value)}`, 'features', 'list'], {
60
+ env: { ...process.env, CODEX_HOME: home }, encoding: 'utf8', timeout: 5000, windowsHide: true,
61
+ });
62
+ return !check.error && check.status === 0;
63
+ });
64
+ }
65
+ cache.set(key, schema);
66
+ return schema;
67
+ } finally { rmSync(home, { recursive: true, force: true }); }
68
+ }
69
+
70
+ export function validateCodexSettings(config, schema, features = []) {
71
+ const result = {};
72
+ for (const [key, value] of Object.entries(config || {})) {
73
+ if (key === 'features') {
74
+ if (!value || typeof value !== 'object' || Array.isArray(value)) throw new Error('Invalid feature settings');
75
+ Object.assign(result, validateCodexSettings(Object.fromEntries(Object.entries(value).map(([k, v]) => [`features.${k}`, v])), schema, features));
76
+ continue;
77
+ }
78
+ if (key.startsWith('features.')) {
79
+ const feature = features.find(f => `features.${f.name}` === key && ['stable', 'beta'].includes(f.stage) && f.configurable !== false);
80
+ if (!feature || typeof value !== 'boolean') throw new Error(`Unsupported Codex feature: ${key}`);
81
+ } else {
82
+ const field = schema.fields?.[key];
83
+ if (!field) throw new Error(`Unsupported Codex setting: ${key}`);
84
+ if (value !== null) {
85
+ if (field.type === 'number') {
86
+ if (!Number.isSafeInteger(value) || value <= 0) throw new Error(`Invalid token count: ${key}`);
87
+ } else if (typeof value !== 'string' || !value || ((field.enumerated || field.values.length) && !field.values.includes(value))) {
88
+ // Preserve granular approval policies; only the UI's string choices are editable.
89
+ throw new Error(`Invalid Codex setting value: ${key}`);
90
+ }
91
+ }
92
+ }
93
+ result[key] = value;
94
+ }
95
+ return result;
96
+ }
97
+
98
+ /** Some stable flags are now mandatory and ignore false (e.g. unified_exec).
99
+ * Check both states with the local loader instead of offering a no-op switch. */
100
+ export function readCodexFeatureSupport(codexBin, features) {
101
+ const names = features.filter(f => ['stable', 'beta'].includes(f.stage) && /^[A-Za-z0-9_-]+$/.test(f.name)).map(f => f.name).sort();
102
+ const stat = statSync(codexBin);
103
+ const key = `${codexBin}:${stat.mtimeMs}:${stat.size}:${names.join(',')}`;
104
+ if (featureCache.has(key)) return featureCache.get(key);
105
+ const home = mkdtempSync(join(tmpdir(), 'synabun-feature-support-'));
106
+ try {
107
+ const states = [false, true].map(value => {
108
+ const check = spawnSync(codexBin, ['-c', `features={${names.map(name => `${name}=${value}`).join(',')}}`, 'features', 'list'], {
109
+ env: { ...process.env, CODEX_HOME: home }, encoding: 'utf8', timeout: 10000, windowsHide: true,
110
+ });
111
+ if (check.error || check.status !== 0) throw new Error('Codex could not validate feature switches');
112
+ return new Map(check.stdout.split('\n').map(line => {
113
+ const parts = line.trim().split(/\s+/); return [parts[0], parts.at(-1)];
114
+ }));
115
+ });
116
+ const supported = Object.fromEntries(names.map(name => [name, states[0].get(name) === 'false' && states[1].get(name) === 'true']));
117
+ featureCache.set(key, supported);
118
+ return supported;
119
+ } finally { rmSync(home, { recursive: true, force: true }); }
120
+ }
@@ -446,6 +446,9 @@ export const DESKTOP_DEFAULTS = Object.freeze({
446
446
  bundleIds: ['com.apple.Terminal', 'com.googlecode.iterm2', 'dev.warp.Warp-Stable', 'com.mitchellh.ghostty', 'net.kovidgoyal.kitty', 'com.github.wez.wezterm', 'org.alacritty', 'co.zeit.hyper'],
447
447
  reason: 'Terminal emulators are off-limits (your other agents run there); use your own shell tool instead',
448
448
  },
449
+ // The SynaBun desktop app (packaging/desktop): every window it has is SynaBun's own interface, as the
450
+ // synabun-ui rule below says of SynaBun in a browser or a web app.
451
+ { id: 'synabun-app', bundleIds: ['ai.synabun.app'], reason: "SynaBun's own interface" },
449
452
  ],
450
453
  // Arrays replace wholesale (mergeConfig): a user's own protectedWindows (or
451
454
  // blockedApps) list drops every default rule below, including the localized
@@ -0,0 +1,264 @@
1
+ // ── AI autocomplete through an endpoint of the user's own ──
2
+ // A local Ollama, or anything that speaks the OpenAI completions or chat
3
+ // protocol. What is sent (docs/design/code-editor/SPEC.md, section 9.6):
4
+ //
5
+ // ollama + fim POST {url}/api/generate { model, prompt: prefix, suffix, stream, options, keep_alive }
6
+ // ollama + chat POST {url}/api/chat { model, messages, stream, options, keep_alive }
7
+ // openai + fim POST {url} { model, prompt: prefix, suffix, max_tokens, temperature, stream }
8
+ // openai + chat POST {url} { model, messages, max_tokens, temperature, stream }
9
+ //
10
+ // `Authorization: Bearer` is added when SYNABUN_EDITOR_AI_KEY is set; the key is
11
+ // never part of an error, in any form: nothing an endpoint says about a failure
12
+ // (an error body, an error inside a stream) is passed on. An endpoint may repeat
13
+ // the request's Authorization header as sent, URL-encoded, JSON-escaped or in a
14
+ // form nobody listed, so its words are only read to pick one of a few sentences
15
+ // of this file's own (endpointFailure). Every failure of a call leaves here as
16
+ // an EndpointFailure with such a sentence. The answer is read as it streams
17
+ // (server-sent events, one JSON object per line, or one JSON body) until it
18
+ // ends, until the caller says it holds enough, or until the request is aborted.
19
+
20
+ const MAX_TOKENS = 256;
21
+ const TEMPERATURE = 0.1;
22
+ const MAX_REPLY_CHARS = 16_000;
23
+
24
+ const trimSlashes = (url) => String(url || '').replace(/\/+$/, '');
25
+
26
+ const REDACTED = '[redacted]';
27
+ // What follows "Bearer" in a header an endpoint echoes: the characters a token is made of (lib/editor-config.js).
28
+ const BEARER_TOKEN = /\b(Bearer)\s+[A-Za-z0-9._~+/=:@-]+/gi;
29
+
30
+ /**
31
+ * A text without the endpoint's key as it was sent: the key itself wherever it stands, and whatever follows
32
+ * "Bearer" (a credential whoever it belongs to). It knows the key in one form only, so it is not what keeps the
33
+ * key out of an endpoint's errors (nothing an endpoint says is passed on: endpointFailure); it is a second rule
34
+ * for text of this server's own.
35
+ * @param {string} text
36
+ * @param {string} [key] the endpoint's key (SYNABUN_EDITOR_AI_KEY), when one is set
37
+ */
38
+ export function redactKey(text, key = '') {
39
+ let out = String(text ?? '');
40
+ if (key) out = out.split(String(key)).join(REDACTED);
41
+ return out.replace(BEARER_TOKEN, `$1 ${REDACTED}`);
42
+ }
43
+
44
+ /** A failure of an endpoint call, in words of this file only: safe for the page, a test's row and the log. */
45
+ export class EndpointFailure extends Error {
46
+ /** @param {string} message @param {{ status?: number | null, kind?: string | null }} [o] */
47
+ constructor(message, { status = null, kind = null } = {}) {
48
+ super(message);
49
+ this.name = 'EndpointFailure';
50
+ this.status = status;
51
+ this.kind = kind;
52
+ }
53
+ }
54
+
55
+ // What a failure is called, by what the answer mentions and by its status. The first sentence whose words are
56
+ // found wins; with no words found, the first whose statuses hold this one.
57
+ const FAILURE_KINDS = [
58
+ { kind: 'model', says: 'it does not have that model', statuses: [], words: /model.{0,60}(?:not found|does not exist|doesn't exist|unknown|not supported|not available)|(?:no such|unknown|invalid) model|try pulling/i },
59
+ { kind: 'key', says: 'it refused the key', statuses: [401, 403], words: /unauthori[sz]ed|incorrect api key|invalid.{0,12}(?:api.?key|token|credential)|authentication|not authenticated|forbidden|permission denied/i },
60
+ { kind: 'rate', says: 'it takes no more requests for now', statuses: [429], words: /rate.?limit|too many requests|quota|insufficient.{0,12}(?:credit|balance|funds|quota)/i },
61
+ { kind: 'long', says: 'the request is too long for the model', statuses: [413], words: /context.{0,20}(?:length|window)|too long|maximum.{0,20}tokens|too many tokens/i },
62
+ { kind: 'busy', says: 'it is busy or still loading the model', statuses: [502, 503, 504], words: /overloaded|unavailable|loading model|still loading|timed? ?out/i },
63
+ { kind: 'address', says: 'nothing takes completions at that address', statuses: [404, 405], words: null },
64
+ ];
65
+ // Why a request never got an answer, by the code of what ended it (fetch's `cause`).
66
+ const NETWORK_KINDS = {
67
+ ECONNREFUSED: 'nothing is listening at its address',
68
+ ENOTFOUND: 'its address is not a name that is known',
69
+ EAI_AGAIN: 'its address could not be looked up',
70
+ ECONNRESET: 'it closed the connection',
71
+ ETIMEDOUT: 'it did not answer in time',
72
+ UND_ERR_CONNECT_TIMEOUT: 'it did not answer in time',
73
+ UND_ERR_SOCKET: 'it closed the connection',
74
+ CERT_HAS_EXPIRED: 'its certificate is not accepted',
75
+ DEPTH_ZERO_SELF_SIGNED_CERT: 'its certificate is not accepted',
76
+ SELF_SIGNED_CERT_IN_CHAIN: 'its certificate is not accepted',
77
+ UNABLE_TO_VERIFY_LEAF_SIGNATURE: 'its certificate is not accepted',
78
+ ERR_TLS_CERT_ALTNAME_INVALID: 'its certificate is not accepted',
79
+ ERR_INVALID_URL: 'its address is not a URL',
80
+ };
81
+ const READ_FOR_KIND = 4000;
82
+
83
+ /**
84
+ * What an endpoint's failure is called. `said` (its error body, or the error inside its stream) is read for what
85
+ * it mentions and for nothing else: not a character of it is in the result.
86
+ * @param {number | null} status the HTTP status, or null for an error an answer carried in its stream
87
+ * @param {string} [said]
88
+ * @returns {EndpointFailure}
89
+ */
90
+ export function endpointFailure(status, said = '') {
91
+ const words = String(said ?? '').slice(0, READ_FOR_KIND);
92
+ const code = Number.isInteger(status) ? status : null;
93
+ const found = FAILURE_KINDS.find((each) => each.words && each.words.test(words))
94
+ || (code === null ? null : FAILURE_KINDS.find((each) => each.statuses.includes(code)));
95
+ const lead = code === null ? 'The endpoint reported an error' : `The endpoint answered ${code}`;
96
+ return new EndpointFailure(found ? `${lead}: ${found.says}` : lead, { status: code, kind: found?.kind ?? null });
97
+ }
98
+
99
+ /**
100
+ * Whatever ended an endpoint call, as an EndpointFailure: its own when it is one; a request that got no answer by
101
+ * the code of what ended it; anything else without its words (they are not this file's).
102
+ */
103
+ export function asEndpointFailure(error) {
104
+ if (error instanceof EndpointFailure) return error;
105
+ // fetch says "fetch failed" and keeps the reason in `cause` (several of them when a name has several addresses).
106
+ const causes = [error?.cause, error, error?.cause?.cause, ...(Array.isArray(error?.cause?.errors) ? error.cause.errors : [])];
107
+ const code = causes.map((each) => each?.code).find((each) => typeof each === 'string' && Object.hasOwn(NETWORK_KINDS, each));
108
+ return new EndpointFailure(code ? `The endpoint could not be reached: ${NETWORK_KINDS[code]}` : 'The endpoint could not be asked', { kind: code ? 'network' : null });
109
+ }
110
+
111
+ /**
112
+ * The HTTP request of one completion.
113
+ * @param {object} o
114
+ * @param {{ kind: 'ollama' | 'openai', url: string, model: string, shape: 'fim' | 'chat' }} o.endpoint `ai.endpoint`
115
+ * @param {string} [o.key] the bearer token, when one is set
116
+ * @param {{ prefix: string, suffix: string }} o.request
117
+ * @param {{ system: string, user: string }} o.prompt the chat messages (lib/editor-completion.js buildChatPrompt)
118
+ * @returns {{ url: string, headers: object, body: object }}
119
+ */
120
+ export function endpointRequest({ endpoint, key = '', request, prompt }) {
121
+ const headers = { 'Content-Type': 'application/json', Accept: 'text/event-stream, application/x-ndjson, application/json' };
122
+ if (key) headers.Authorization = `Bearer ${key}`;
123
+ const messages = [{ role: 'system', content: prompt.system }, { role: 'user', content: prompt.user }];
124
+ if (endpoint.kind === 'ollama') {
125
+ // The address is the server's; a pasted /api/generate or /api/chat is not doubled.
126
+ const base = trimSlashes(endpoint.url).replace(/\/api\/(?:generate|chat)$/, '');
127
+ const common = { model: endpoint.model, stream: true, options: { temperature: TEMPERATURE, num_predict: MAX_TOKENS }, keep_alive: '30m' };
128
+ return endpoint.shape === 'chat'
129
+ ? { url: `${base}/api/chat`, headers, body: { ...common, messages } }
130
+ : { url: `${base}/api/generate`, headers, body: { ...common, prompt: request.prefix, suffix: request.suffix } };
131
+ }
132
+ const common = { model: endpoint.model, max_tokens: MAX_TOKENS, temperature: TEMPERATURE, stream: true };
133
+ return endpoint.shape === 'chat'
134
+ ? { url: endpoint.url, headers, body: { ...common, messages } }
135
+ : { url: endpoint.url, headers, body: { ...common, prompt: request.prefix, suffix: request.suffix } };
136
+ }
137
+
138
+ /**
139
+ * What one object of an answer holds, whichever of the four protocols it is.
140
+ * @param {object} chunk
141
+ * @returns {{ text: string, done: boolean, tokens: { input: number, output: number } | null, error: string | null }}
142
+ * `error`: what the failure is called (endpointFailure), never the endpoint's own words
143
+ */
144
+ export function readChunk(chunk) {
145
+ const out = { text: '', done: false, tokens: null, error: null };
146
+ if (!chunk || typeof chunk !== 'object') return out;
147
+ if (chunk.error) {
148
+ let said = '';
149
+ try { said = typeof chunk.error === 'string' ? chunk.error : JSON.stringify(chunk.error); } catch { said = ''; }
150
+ out.error = endpointFailure(null, said).message;
151
+ return out;
152
+ }
153
+ const choice = Array.isArray(chunk.choices) ? chunk.choices[0] : null;
154
+ const text = chunk.response ?? chunk.message?.content ?? choice?.text ?? choice?.delta?.content ?? choice?.message?.content;
155
+ if (typeof text === 'string') out.text = text;
156
+ out.done = chunk.done === true || (choice != null && choice.finish_reason != null);
157
+ const input = chunk.prompt_eval_count ?? chunk.usage?.prompt_tokens;
158
+ const output = chunk.eval_count ?? chunk.usage?.completion_tokens;
159
+ if (Number.isFinite(input) || Number.isFinite(output)) out.tokens = { input: Math.round(input || 0), output: Math.round(output || 0) };
160
+ return out;
161
+ }
162
+
163
+ /**
164
+ * Read an answer to its end, or until `isDone(text)` says it holds enough.
165
+ * @param {Response} res
166
+ * @param {{ isDone?: (text: string) => boolean }} [o]
167
+ * @returns {Promise<{ text: string, tokens: { input: number, output: number } | null, stoppedEarly: boolean }>}
168
+ * rejects with an EndpointFailure for an error the answer carries, and for an answer that cannot be read
169
+ */
170
+ export async function readEndpointAnswer(res, { isDone = () => false } = {}) {
171
+ let text = '';
172
+ let tokens = null;
173
+ let pending = ''; // the line that is still arriving
174
+ let unparsed = ''; // lines that were not objects on their own: one JSON body spread over several lines
175
+ let finished = false;
176
+
177
+ const take = (chunk) => {
178
+ const part = readChunk(chunk);
179
+ if (part.error) throw new EndpointFailure(part.error, { kind: 'reported' });
180
+ text += part.text;
181
+ if (part.tokens) tokens = part.tokens;
182
+ if (part.done) finished = true;
183
+ };
184
+ const line = (raw) => {
185
+ let value = raw.trim();
186
+ if (!value || value.startsWith(':') || value.startsWith('event:') || value.startsWith('id:') || value.startsWith('retry:')) return;
187
+ if (value.startsWith('data:')) value = value.slice(5).trim();
188
+ if (value === '[DONE]') { finished = true; return; }
189
+ let chunk;
190
+ try { chunk = JSON.parse(value); } catch { unparsed += `${raw}\n`; return; }
191
+ take(chunk);
192
+ };
193
+
194
+ const reader = res.body?.getReader?.();
195
+ if (!reader) {
196
+ for (const each of (await res.text()).split('\n')) line(each);
197
+ } else {
198
+ const decoder = new TextDecoder();
199
+ let stoppedEarly = false;
200
+ try {
201
+ for (;;) {
202
+ const { value, done } = await reader.read();
203
+ if (done) break;
204
+ pending += decoder.decode(value, { stream: true });
205
+ const lines = pending.split('\n');
206
+ pending = lines.pop();
207
+ for (const each of lines) line(each);
208
+ if (finished) break;
209
+ if (text.length > MAX_REPLY_CHARS || isDone(text)) { stoppedEarly = true; break; }
210
+ }
211
+ } finally {
212
+ // Whatever is still on its way is not wanted: the connection is given back.
213
+ try { await reader.cancel(); } catch { /* already closed */ }
214
+ }
215
+ if (stoppedEarly) return { text, tokens, stoppedEarly: true };
216
+ if (pending) line(pending);
217
+ }
218
+ if (unparsed.trim() && !text) {
219
+ let whole;
220
+ try { whole = JSON.parse(unparsed); } catch { throw new EndpointFailure('The endpoint did not answer in a form this editor reads', { kind: 'form' }); }
221
+ take(whole);
222
+ }
223
+ return { text, tokens, stoppedEarly: false };
224
+ }
225
+
226
+ /** @param {{ fetchImpl?: typeof fetch }} [deps] */
227
+ export function createEndpointCompletionEngine({ fetchImpl = (...args) => fetch(...args) } = {}) {
228
+ return {
229
+ /**
230
+ * One completion.
231
+ * @param {object} o
232
+ * @param {{ kind: string, url: string, model: string, shape: string }} o.endpoint
233
+ * @param {string} [o.key]
234
+ * @param {{ prefix: string, suffix: string }} o.request
235
+ * @param {{ system: string, user: string }} o.prompt
236
+ * @param {AbortSignal} [o.signal]
237
+ * @param {(text: string) => boolean} [o.isDone] the first stop condition: the answer so far holds all that would be shown
238
+ * @returns {Promise<{ text: string, tokens: { input: number, output: number } | null }>} rejects with an
239
+ * EndpointFailure (its message holds nothing the endpoint said), or with the abort when `signal` fired
240
+ */
241
+ async complete({ endpoint, key = '', request, prompt, signal, isDone } = {}) {
242
+ const { url, headers, body } = endpointRequest({ endpoint, key, request, prompt });
243
+ // Our own controller, so an answer that holds enough can be cut off without the caller aborting.
244
+ const own = new AbortController();
245
+ const onAbort = () => own.abort();
246
+ if (signal?.aborted) own.abort();
247
+ signal?.addEventListener('abort', onAbort, { once: true });
248
+ try {
249
+ const res = await fetchImpl(url, { method: 'POST', headers, body: JSON.stringify(body), signal: own.signal });
250
+ // What it says is read for what the failure is called, and stays here.
251
+ if (!res.ok) throw endpointFailure(res.status, await res.text().catch(() => ''));
252
+ const answer = await readEndpointAnswer(res, { isDone });
253
+ return { text: answer.text, tokens: answer.tokens };
254
+ } catch (error) {
255
+ // The caller gave up: that is not the endpoint's failure.
256
+ if (signal?.aborted) throw error;
257
+ throw asEndpointFailure(error);
258
+ } finally {
259
+ signal?.removeEventListener('abort', onAbort);
260
+ own.abort();
261
+ }
262
+ },
263
+ };
264
+ }