synabun 2.0.10 → 2.2.0

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 (656) hide show
  1. package/CHANGELOG.md +207 -0
  2. package/README.md +25 -2
  3. package/THIRD-PARTY-LICENSES.md +22 -0
  4. package/bin/synabun.js +71 -24
  5. package/docs/whatsapp.md +13 -13
  6. package/hooks/claude-code/pre-websearch.mjs +12 -1
  7. package/hooks/claude-code/shared.mjs +5 -0
  8. package/hooks/claude-code/stop.mjs +1 -1
  9. package/lib/claude-hooks.js +172 -18
  10. package/lib/client-config-repair.js +30 -17
  11. package/lib/desktop-app.js +7 -1
  12. package/lib/external-tools.js +7 -6
  13. package/lib/first-launch-protection.js +57 -12
  14. package/lib/mcp-dist.js +176 -0
  15. package/lib/release-npm-ci.mjs +138 -0
  16. package/lib/release-publish.mjs +91 -0
  17. package/lib/release-scan.mjs +50 -0
  18. package/lib/server-crash-record.js +123 -0
  19. package/lib/server-exit.js +318 -0
  20. package/lib/server-supervisor.js +508 -0
  21. package/lib/start-launcher.js +37 -4
  22. package/lib/tools-cli.js +45 -0
  23. package/lib/update-snapshot.js +302 -0
  24. package/lib/virustotal.mjs +203 -0
  25. package/lib/windows-crash-event.js +269 -0
  26. package/mcp-server/dist/http.d.ts +20 -1
  27. package/mcp-server/dist/http.d.ts.map +1 -1
  28. package/mcp-server/dist/http.js +59 -12
  29. package/mcp-server/dist/http.js.map +1 -1
  30. package/mcp-server/dist/index.d.ts +33 -27
  31. package/mcp-server/dist/index.d.ts.map +1 -1
  32. package/mcp-server/dist/index.js +128 -143
  33. package/mcp-server/dist/index.js.map +1 -1
  34. package/mcp-server/dist/preload.js +1 -1
  35. package/mcp-server/dist/preload.js.map +1 -1
  36. package/mcp-server/dist/services/budget-judgments.d.ts +1 -1
  37. package/mcp-server/dist/services/budget-judgments.d.ts.map +1 -1
  38. package/mcp-server/dist/services/budget-judgments.js +5 -3
  39. package/mcp-server/dist/services/budget-judgments.js.map +1 -1
  40. package/mcp-server/dist/services/codex-config-heal.d.ts +6 -2
  41. package/mcp-server/dist/services/codex-config-heal.d.ts.map +1 -1
  42. package/mcp-server/dist/services/codex-config-heal.js +81 -28
  43. package/mcp-server/dist/services/codex-config-heal.js.map +1 -1
  44. package/mcp-server/dist/services/identity.d.ts +2 -2
  45. package/mcp-server/dist/services/identity.js +2 -2
  46. package/mcp-server/dist/services/memory-judge-backfill.d.ts +6 -0
  47. package/mcp-server/dist/services/memory-judge-backfill.d.ts.map +1 -1
  48. package/mcp-server/dist/services/memory-judge-backfill.js +7 -1
  49. package/mcp-server/dist/services/memory-judge-backfill.js.map +1 -1
  50. package/mcp-server/dist/services/memory-sessions.d.ts +1 -1
  51. package/mcp-server/dist/services/memory-sessions.d.ts.map +1 -1
  52. package/mcp-server/dist/services/memory-sessions.js +28 -2
  53. package/mcp-server/dist/services/memory-sessions.js.map +1 -1
  54. package/mcp-server/dist/services/neural-interface.d.ts +18 -11
  55. package/mcp-server/dist/services/neural-interface.d.ts.map +1 -1
  56. package/mcp-server/dist/services/neural-interface.js +67 -11
  57. package/mcp-server/dist/services/neural-interface.js.map +1 -1
  58. package/mcp-server/dist/services/server-instructions.d.ts +7 -2
  59. package/mcp-server/dist/services/server-instructions.d.ts.map +1 -1
  60. package/mcp-server/dist/services/server-instructions.js +22 -63
  61. package/mcp-server/dist/services/server-instructions.js.map +1 -1
  62. package/mcp-server/dist/services/tool-catalog.d.ts +120 -0
  63. package/mcp-server/dist/services/tool-catalog.d.ts.map +1 -0
  64. package/mcp-server/dist/services/tool-catalog.js +339 -0
  65. package/mcp-server/dist/services/tool-catalog.js.map +1 -0
  66. package/mcp-server/dist/services/tool-list-refresh-barrier.d.ts +12 -0
  67. package/mcp-server/dist/services/tool-list-refresh-barrier.d.ts.map +1 -0
  68. package/mcp-server/dist/services/tool-list-refresh-barrier.js +40 -0
  69. package/mcp-server/dist/services/tool-list-refresh-barrier.js.map +1 -0
  70. package/mcp-server/dist/services/tool-list.d.ts +108 -0
  71. package/mcp-server/dist/services/tool-list.d.ts.map +1 -0
  72. package/mcp-server/dist/services/tool-list.js +334 -0
  73. package/mcp-server/dist/services/tool-list.js.map +1 -0
  74. package/mcp-server/dist/services/tool-runtime.d.ts +161 -0
  75. package/mcp-server/dist/services/tool-runtime.d.ts.map +1 -0
  76. package/mcp-server/dist/services/tool-runtime.js +400 -0
  77. package/mcp-server/dist/services/tool-runtime.js.map +1 -0
  78. package/mcp-server/dist/services/typesafe-config.js +1 -1
  79. package/mcp-server/dist/services/typesafe-config.js.map +1 -1
  80. package/mcp-server/dist/services/whiteboard-geometry.d.ts +26 -2
  81. package/mcp-server/dist/services/whiteboard-geometry.d.ts.map +1 -1
  82. package/mcp-server/dist/services/whiteboard-geometry.js +91 -8
  83. package/mcp-server/dist/services/whiteboard-geometry.js.map +1 -1
  84. package/mcp-server/dist/tools/agents.d.ts +14 -14
  85. package/mcp-server/dist/tools/agents.d.ts.map +1 -1
  86. package/mcp-server/dist/tools/agents.js +29 -19
  87. package/mcp-server/dist/tools/agents.js.map +1 -1
  88. package/mcp-server/dist/tools/browser-batch.d.ts +3 -3
  89. package/mcp-server/dist/tools/choice.d.ts +6 -0
  90. package/mcp-server/dist/tools/choice.d.ts.map +1 -1
  91. package/mcp-server/dist/tools/choice.js +18 -13
  92. package/mcp-server/dist/tools/choice.js.map +1 -1
  93. package/mcp-server/dist/tools/git-tools.js +2 -2
  94. package/mcp-server/dist/tools/git-tools.js.map +1 -1
  95. package/mcp-server/dist/tools/image-tools.d.ts +1 -1
  96. package/mcp-server/dist/tools/image-tools.js +1 -1
  97. package/mcp-server/dist/tools/morelogin.d.ts.map +1 -1
  98. package/mcp-server/dist/tools/morelogin.js +9 -6
  99. package/mcp-server/dist/tools/morelogin.js.map +1 -1
  100. package/mcp-server/dist/tools/skill-tools.d.ts +505 -0
  101. package/mcp-server/dist/tools/skill-tools.d.ts.map +1 -0
  102. package/mcp-server/dist/tools/skill-tools.js +642 -0
  103. package/mcp-server/dist/tools/skill-tools.js.map +1 -0
  104. package/mcp-server/dist/tools/skill.d.ts +7 -0
  105. package/mcp-server/dist/tools/skill.d.ts.map +1 -0
  106. package/mcp-server/dist/tools/skill.js +11 -0
  107. package/mcp-server/dist/tools/skill.js.map +1 -0
  108. package/mcp-server/dist/tools/tool-groups.d.ts +68 -0
  109. package/mcp-server/dist/tools/tool-groups.d.ts.map +1 -0
  110. package/mcp-server/dist/tools/tool-groups.js +147 -0
  111. package/mcp-server/dist/tools/tool-groups.js.map +1 -0
  112. package/mcp-server/dist/tools/whiteboard-format.d.ts.map +1 -1
  113. package/mcp-server/dist/tools/whiteboard-format.js +7 -4
  114. package/mcp-server/dist/tools/whiteboard-format.js.map +1 -1
  115. package/mcp-server/dist/tools/whiteboard-tools.d.ts +64 -13
  116. package/mcp-server/dist/tools/whiteboard-tools.d.ts.map +1 -1
  117. package/mcp-server/dist/tools/whiteboard-tools.js +30 -5
  118. package/mcp-server/dist/tools/whiteboard-tools.js.map +1 -1
  119. package/mcp-server/package-lock.json +2 -2
  120. package/mcp-server/package.json +1 -1
  121. package/mcp-server/src/http.ts +75 -12
  122. package/mcp-server/src/index.ts +147 -154
  123. package/mcp-server/src/preload.ts +12 -12
  124. package/mcp-server/src/services/budget-judgments.ts +5 -3
  125. package/mcp-server/src/services/codex-config-heal.ts +80 -29
  126. package/mcp-server/src/services/identity.ts +2 -2
  127. package/mcp-server/src/services/memory-judge-backfill.ts +7 -1
  128. package/mcp-server/src/services/memory-sessions.ts +24 -3
  129. package/mcp-server/src/services/neural-interface.ts +81 -21
  130. package/mcp-server/src/services/server-instructions.ts +29 -64
  131. package/mcp-server/src/services/tool-catalog.ts +403 -0
  132. package/mcp-server/src/services/tool-list-refresh-barrier.ts +33 -0
  133. package/mcp-server/src/services/tool-list.ts +410 -0
  134. package/mcp-server/src/services/tool-runtime.ts +457 -0
  135. package/mcp-server/src/services/typesafe-config.ts +1 -1
  136. package/mcp-server/src/services/whiteboard-geometry.ts +94 -9
  137. package/mcp-server/src/tools/agents.ts +29 -20
  138. package/mcp-server/src/tools/choice.ts +17 -12
  139. package/mcp-server/src/tools/git-tools.ts +2 -2
  140. package/mcp-server/src/tools/image-tools.ts +1 -1
  141. package/mcp-server/src/tools/morelogin.ts +6 -4
  142. package/mcp-server/src/tools/skill-tools.ts +592 -0
  143. package/mcp-server/src/tools/skill.ts +13 -0
  144. package/mcp-server/src/tools/tool-groups.ts +164 -0
  145. package/mcp-server/src/tools/whiteboard-format.ts +9 -3
  146. package/mcp-server/src/tools/whiteboard-tools.ts +39 -5
  147. package/neural-interface/i18n/en.json +3953 -228
  148. package/neural-interface/i18n/pt-BR.json +3901 -179
  149. package/neural-interface/lib/agent-sandbox-elevation.js +6 -0
  150. package/neural-interface/lib/antigravity/acp-client-host.js +554 -0
  151. package/neural-interface/lib/antigravity/acp-connection.js +299 -0
  152. package/neural-interface/lib/antigravity/bypass.js +100 -0
  153. package/neural-interface/lib/antigravity/catalog.js +273 -0
  154. package/neural-interface/lib/antigravity/home.js +260 -0
  155. package/neural-interface/lib/antigravity/migrate-gemini.js +139 -0
  156. package/neural-interface/lib/antigravity/panel-bridge.js +1058 -0
  157. package/neural-interface/lib/antigravity/policy-hook.mjs +78 -0
  158. package/neural-interface/lib/antigravity/policy.js +74 -0
  159. package/neural-interface/lib/antigravity/tool-call.js +178 -0
  160. package/neural-interface/lib/antigravity/transcripts.js +252 -0
  161. package/neural-interface/lib/antigravity/usage.js +43 -0
  162. package/neural-interface/lib/assistant-api.js +128 -28
  163. package/neural-interface/lib/assistant-brains/antigravity.js +423 -0
  164. package/neural-interface/lib/assistant-brains/claude.js +4 -3
  165. package/neural-interface/lib/assistant-brains/codex-browser-policy-hook.mjs +12 -2
  166. package/neural-interface/lib/assistant-brains/codex-route-gate-hook.mjs +32 -9
  167. package/neural-interface/lib/assistant-brains/codex.js +9 -5
  168. package/neural-interface/lib/assistant-brains/opencode.js +120 -54
  169. package/neural-interface/lib/assistant-brains/opencode2-browser-policy/index.js +28 -13
  170. package/neural-interface/lib/assistant-brains/opencode2-route-gate/index.js +59 -19
  171. package/neural-interface/lib/assistant-budget-planner.js +7 -3
  172. package/neural-interface/lib/assistant-budget-split.js +42 -15
  173. package/neural-interface/lib/assistant-catalog.js +81 -15
  174. package/neural-interface/lib/assistant-config.js +6 -3
  175. package/neural-interface/lib/assistant-dispatch.js +1073 -200
  176. package/neural-interface/lib/assistant-envelope.js +156 -2
  177. package/neural-interface/lib/assistant-evidence.js +80 -0
  178. package/neural-interface/lib/assistant-hidden-models.js +7 -5
  179. package/neural-interface/lib/assistant-persona.js +93 -19
  180. package/neural-interface/lib/assistant-plan-permissions.js +25 -9
  181. package/neural-interface/lib/assistant-route-gate-codex.js +3 -0
  182. package/neural-interface/lib/assistant-route-gate.js +6 -4
  183. package/neural-interface/lib/assistant-router.js +245 -38
  184. package/neural-interface/lib/assistant-runtime.js +214 -30
  185. package/neural-interface/lib/assistant-task-prompt.js +87 -21
  186. package/neural-interface/lib/assistant-tools.js +51 -0
  187. package/neural-interface/lib/assistant-usage.js +29 -0
  188. package/neural-interface/lib/backup-service.js +626 -4
  189. package/neural-interface/lib/backup-zip-reader.js +10 -5
  190. package/neural-interface/lib/backup-zip-writer.js +257 -0
  191. package/neural-interface/lib/browser-execution.js +10 -2
  192. package/neural-interface/lib/browser-guard-cdp.js +84 -0
  193. package/neural-interface/lib/browser-network-guard.js +476 -0
  194. package/neural-interface/lib/browser-policy-roots.js +64 -0
  195. package/neural-interface/lib/browser-tool-policy.js +198 -38
  196. package/neural-interface/lib/claude-agent-bridge.js +11 -2
  197. package/neural-interface/lib/claude-history-cache.js +210 -0
  198. package/neural-interface/lib/claude-hooks-status.js +103 -0
  199. package/neural-interface/lib/claude-model-catalog.js +5 -1
  200. package/neural-interface/lib/claude-temporary.js +5 -11
  201. package/neural-interface/lib/cli-install-api.js +1 -1
  202. package/neural-interface/lib/cli-install.js +163 -154
  203. package/neural-interface/lib/cli-resolver.js +13 -3
  204. package/neural-interface/lib/cli-update-planner.js +52 -41
  205. package/neural-interface/lib/cloudflared/tunnel.js +42 -2
  206. package/neural-interface/lib/codex-mcp-approvals.js +11 -0
  207. package/neural-interface/lib/codex-rollout-reader.js +52 -0
  208. package/neural-interface/lib/codex-worker-app-server.js +191 -0
  209. package/neural-interface/lib/confined-fs.js +30 -3
  210. package/neural-interface/lib/crash-notice-api.js +98 -0
  211. package/neural-interface/lib/editor-completion-opencode.js +68 -68
  212. package/neural-interface/lib/editor-git-api.js +3 -20
  213. package/neural-interface/lib/exec-loop-command.js +13 -10
  214. package/neural-interface/lib/file-explorer-api.js +74 -40
  215. package/neural-interface/lib/file-explorer-watch.js +9 -3
  216. package/neural-interface/lib/git-bash/install.js +52 -6
  217. package/neural-interface/lib/git-bash/manager.js +59 -17
  218. package/neural-interface/lib/git-bash/pins.js +12 -0
  219. package/neural-interface/lib/git-read.js +2 -2
  220. package/neural-interface/lib/git-status-runner.js +390 -0
  221. package/neural-interface/lib/git-status-watchdog.js +40 -0
  222. package/neural-interface/lib/greeting/budgets.js +1 -0
  223. package/neural-interface/lib/greeting/engine.js +2 -2
  224. package/neural-interface/lib/greeting/sources.js +10 -12
  225. package/neural-interface/lib/greeting/writer.js +1 -1
  226. package/neural-interface/lib/http-guards.js +8 -2
  227. package/neural-interface/lib/install-details.js +4 -1
  228. package/neural-interface/lib/mcp-agent-servers.js +67 -0
  229. package/neural-interface/lib/mcp-client-config.js +35 -33
  230. package/neural-interface/lib/mcp-external-servers.js +332 -0
  231. package/neural-interface/lib/mcp-installer.js +2 -1
  232. package/neural-interface/lib/{mcp-profile-refresh-coordinator.js → mcp-tools-refresh-coordinator.js} +4 -4
  233. package/neural-interface/lib/memory-session-capture.js +17 -3
  234. package/neural-interface/lib/morelogin.js +177 -25
  235. package/neural-interface/lib/native-loop-providers.js +965 -66
  236. package/neural-interface/lib/native-loop-runtime.js +290 -42
  237. package/neural-interface/lib/opencode-config.js +557 -0
  238. package/neural-interface/lib/opencode-db-schema.js +17 -34
  239. package/neural-interface/lib/opencode-history.js +48 -218
  240. package/neural-interface/lib/opencode-mcp-ready.js +108 -0
  241. package/neural-interface/lib/opencode-native-boundary.js +53 -0
  242. package/neural-interface/lib/opencode-protocol.js +249 -112
  243. package/neural-interface/lib/opencode-runtime-config.js +32 -22
  244. package/neural-interface/lib/opencode-serve-records.js +39 -21
  245. package/neural-interface/lib/opencode-v2-client.js +48 -153
  246. package/neural-interface/lib/opencode-v2-ws-requests.js +36 -210
  247. package/neural-interface/lib/opencode-worker-data.js +27 -0
  248. package/neural-interface/lib/opencode2-client.js +73 -37
  249. package/neural-interface/lib/opencode2-proxy.js +23 -63
  250. package/neural-interface/lib/opencode2-translate.js +51 -2
  251. package/neural-interface/lib/owner-authority.js +249 -0
  252. package/neural-interface/lib/page-session.js +275 -0
  253. package/neural-interface/lib/path-confine.js +12 -9
  254. package/neural-interface/lib/peer-identity.js +793 -0
  255. package/neural-interface/lib/pty-host/core.js +28 -1
  256. package/neural-interface/lib/pty-host/lifetime.js +12 -0
  257. package/neural-interface/lib/pty-host/manager.js +12 -1
  258. package/neural-interface/lib/remote-policy.js +1 -1
  259. package/neural-interface/lib/request-gate.js +20 -2
  260. package/neural-interface/lib/rulesets/api.js +7 -6
  261. package/neural-interface/lib/rulesets/fs-safe.js +1 -1
  262. package/neural-interface/lib/rulesets/installer.js +180 -36
  263. package/neural-interface/lib/rulesets/json-edit.js +1 -1
  264. package/neural-interface/lib/rulesets/render.js +1 -1
  265. package/neural-interface/lib/session-cache-builder.js +81 -31
  266. package/neural-interface/lib/session-title-generator.js +58 -36
  267. package/neural-interface/lib/skills/api-v2.js +495 -0
  268. package/neural-interface/lib/skills/bundle.js +418 -0
  269. package/neural-interface/lib/skills/catalog.js +298 -0
  270. package/neural-interface/lib/skills/creation-api.js +191 -0
  271. package/neural-interface/lib/skills/creation-brief.js +247 -0
  272. package/neural-interface/lib/skills/creation-sources.js +285 -0
  273. package/neural-interface/lib/skills/creation.js +843 -0
  274. package/neural-interface/lib/skills/deploy.js +569 -0
  275. package/neural-interface/lib/skills/digest.js +119 -0
  276. package/neural-interface/lib/skills/discover.js +111 -0
  277. package/neural-interface/lib/skills/draft-confinement.js +216 -0
  278. package/neural-interface/lib/skills/eval-confinement.js +399 -0
  279. package/neural-interface/lib/skills/eval-sandbox.js +280 -0
  280. package/neural-interface/lib/skills/eval-trace.js +135 -0
  281. package/neural-interface/lib/skills/evals-api.js +212 -0
  282. package/neural-interface/lib/skills/evals.js +1573 -0
  283. package/neural-interface/lib/skills/export.js +203 -0
  284. package/neural-interface/lib/skills/frontmatter.js +64 -0
  285. package/neural-interface/lib/skills/health.js +75 -0
  286. package/neural-interface/lib/skills/json-edit.js +165 -0
  287. package/neural-interface/lib/skills/known-bridge-digests.json +441 -0
  288. package/neural-interface/lib/skills/known-bridges.js +96 -0
  289. package/neural-interface/lib/skills/library.js +823 -0
  290. package/neural-interface/lib/skills/local-store.js +21 -0
  291. package/neural-interface/lib/skills/migration.js +433 -0
  292. package/neural-interface/lib/skills/operations.js +45 -0
  293. package/neural-interface/lib/skills/ownership.js +427 -0
  294. package/neural-interface/lib/skills/packages.js +88 -0
  295. package/neural-interface/lib/skills/reviews.js +212 -0
  296. package/neural-interface/lib/skills/revision.js +80 -0
  297. package/neural-interface/lib/skills/roots.js +269 -0
  298. package/neural-interface/lib/skills/runtime-checks.js +358 -0
  299. package/neural-interface/lib/skills/scan-assets.js +252 -0
  300. package/neural-interface/lib/skills/scan-context.js +26 -0
  301. package/neural-interface/lib/skills/scan-shell.js +324 -0
  302. package/neural-interface/lib/skills/scan.js +364 -0
  303. package/neural-interface/lib/skills/scanners.js +54 -0
  304. package/neural-interface/lib/skills/service.js +530 -0
  305. package/neural-interface/lib/skills/telemetry.js +119 -0
  306. package/neural-interface/lib/sqlite-integrity-worker.js +22 -0
  307. package/neural-interface/lib/synabun-version.js +1 -1
  308. package/neural-interface/lib/system-activity.js +1 -1
  309. package/neural-interface/lib/tool-lists.js +124 -0
  310. package/neural-interface/lib/whatsapp/brain.js +7 -3
  311. package/neural-interface/lib/whatsapp/bridge.js +6 -3
  312. package/neural-interface/lib/whatsapp/cards.js +22 -9
  313. package/neural-interface/lib/whatsapp/config.js +1 -1
  314. package/neural-interface/lib/worker-confinement.js +1268 -0
  315. package/neural-interface/lib/worker-guard-setting.js +56 -0
  316. package/neural-interface/lib/worker-mcp-channel.js +175 -0
  317. package/neural-interface/lib/worker-question.js +74 -0
  318. package/neural-interface/lib/worker-relay-ports.js +227 -0
  319. package/neural-interface/lib/worker-resume.js +238 -0
  320. package/neural-interface/lib/worker-sandbox.js +329 -0
  321. package/neural-interface/package-lock.json +34 -12
  322. package/neural-interface/package.json +3 -2
  323. package/neural-interface/public/index.html +3 -1
  324. package/neural-interface/public/index2d.html +19 -11
  325. package/neural-interface/public/onboarding.html +3 -3
  326. package/neural-interface/public/shared/account-menu.js +8 -5
  327. package/neural-interface/public/shared/agent-runtime-options.js +125 -31
  328. package/neural-interface/public/shared/agy/agy-bypass.js +47 -0
  329. package/neural-interface/public/shared/agy/agy-client.js +266 -0
  330. package/neural-interface/public/shared/agy/agy-commands.js +120 -0
  331. package/neural-interface/public/shared/agy/agy-copy.js +326 -0
  332. package/neural-interface/public/shared/agy/agy-diff.js +161 -0
  333. package/neural-interface/public/shared/agy/agy-events.js +469 -0
  334. package/neural-interface/public/shared/agy/agy-greeting.js +26 -0
  335. package/neural-interface/public/shared/agy/agy-options.js +232 -0
  336. package/neural-interface/public/shared/agy/agy-panel.js +2543 -0
  337. package/neural-interface/public/shared/agy/agy-render.js +392 -0
  338. package/neural-interface/public/shared/agy/agy-setup.js +149 -0
  339. package/neural-interface/public/shared/agy/agy-styles.js +604 -0
  340. package/neural-interface/public/shared/agy/agy-tabs-logic.js +181 -0
  341. package/neural-interface/public/shared/api.js +1527 -1336
  342. package/neural-interface/public/shared/app-frame.js +192 -0
  343. package/neural-interface/public/shared/assistant/asst-agents-dock.js +36 -6
  344. package/neural-interface/public/shared/assistant/asst-api.js +1 -1
  345. package/neural-interface/public/shared/assistant/asst-ask-styles.js +270 -0
  346. package/neural-interface/public/shared/assistant/asst-ask-tail.js +118 -0
  347. package/neural-interface/public/shared/assistant/asst-ask.js +397 -0
  348. package/neural-interface/public/shared/assistant/asst-brain-picker.js +105 -44
  349. package/neural-interface/public/shared/assistant/asst-budget.js +5 -4
  350. package/neural-interface/public/shared/assistant/asst-control.js +205 -26
  351. package/neural-interface/public/shared/assistant/asst-followups-card.js +383 -0
  352. package/neural-interface/public/shared/assistant/asst-followups.js +417 -0
  353. package/neural-interface/public/shared/assistant/asst-hosts.js +16 -7
  354. package/neural-interface/public/shared/assistant/asst-limit-dock.js +45 -0
  355. package/neural-interface/public/shared/assistant/asst-limit-wait.js +327 -0
  356. package/neural-interface/public/shared/assistant/asst-limit.js +168 -0
  357. package/neural-interface/public/shared/assistant/asst-mascot.js +49 -12
  358. package/neural-interface/public/shared/assistant/asst-menu.js +7 -6
  359. package/neural-interface/public/shared/assistant/asst-panel.js +151 -19
  360. package/neural-interface/public/shared/assistant/asst-rack-styles.js +66 -49
  361. package/neural-interface/public/shared/assistant/asst-render.js +445 -82
  362. package/neural-interface/public/shared/assistant/asst-route.js +304 -53
  363. package/neural-interface/public/shared/assistant/asst-routes-editor.js +9 -8
  364. package/neural-interface/public/shared/assistant/asst-scenes.js +56 -1
  365. package/neural-interface/public/shared/assistant/asst-sidepanel.js +117 -10
  366. package/neural-interface/public/shared/assistant/asst-slash.js +17 -9
  367. package/neural-interface/public/shared/assistant/asst-state.js +87 -24
  368. package/neural-interface/public/shared/assistant/asst-stop.js +127 -0
  369. package/neural-interface/public/shared/assistant/asst-styles.js +93 -21
  370. package/neural-interface/public/shared/assistant/asst-tool-kinds.js +190 -23
  371. package/neural-interface/public/shared/assistant/asst-usage.js +2 -1
  372. package/neural-interface/public/shared/automation-context.js +5 -2
  373. package/neural-interface/public/shared/cc-dropdown.js +89 -19
  374. package/neural-interface/public/shared/cdx/cdx-context-menu.js +10 -9
  375. package/neural-interface/public/shared/cdx/cdx-copy.js +16 -0
  376. package/neural-interface/public/shared/cdx/cdx-output-model.js +22 -1
  377. package/neural-interface/public/shared/cdx/cdx-output.js +34 -10
  378. package/neural-interface/public/shared/cdx/cdx-panel.js +59 -151
  379. package/neural-interface/public/shared/cdx/cdx-preview.js +12 -0
  380. package/neural-interface/public/shared/cdx/cdx-protocol.js +39 -11
  381. package/neural-interface/public/shared/cdx/cdx-render.js +57 -38
  382. package/neural-interface/public/shared/cdx/cdx-requests.js +14 -4
  383. package/neural-interface/public/shared/cdx/cdx-skill-hints.js +54 -0
  384. package/neural-interface/public/shared/cdx/cdx-styles.js +15 -3
  385. package/neural-interface/public/shared/cdx/cdx-tabs.js +127 -44
  386. package/neural-interface/public/shared/cdx/cdx-ws.js +0 -1
  387. package/neural-interface/public/shared/cli-install-controller.js +218 -0
  388. package/neural-interface/public/shared/cli-install-view.js +11 -12
  389. package/neural-interface/public/shared/cli-status.js +11 -10
  390. package/neural-interface/public/shared/constants.js +5 -4
  391. package/neural-interface/public/shared/context-menu-areas.js +34 -0
  392. package/neural-interface/public/shared/context-menu-logic.js +243 -0
  393. package/neural-interface/public/shared/context-menu-model.js +367 -0
  394. package/neural-interface/public/shared/context-menu.js +372 -0
  395. package/neural-interface/public/shared/cp/cp-context-menu.js +10 -9
  396. package/neural-interface/public/shared/cp/cp-restore.js +162 -0
  397. package/neural-interface/public/shared/cp/cp-tool-cards.js +10 -3
  398. package/neural-interface/public/shared/crash-notice-view.js +168 -0
  399. package/neural-interface/public/shared/desktop-bridge.js +17 -0
  400. package/neural-interface/public/shared/desktop-titlebar-view.js +12 -0
  401. package/neural-interface/public/shared/desktop-titlebar.js +13 -4
  402. package/neural-interface/public/shared/desktop-update-view.js +21 -0
  403. package/neural-interface/public/shared/editor/editor-panel-logic.js +2 -2
  404. package/neural-interface/public/shared/explorer/fx-api.js +4 -4
  405. package/neural-interface/public/shared/explorer/fx-links.js +3 -3
  406. package/neural-interface/public/shared/explorer/fx-logic.js +1 -1
  407. package/neural-interface/public/shared/explorer/fx-main.js +48 -16
  408. package/neural-interface/public/shared/explorer/fx-tree.js +1 -0
  409. package/neural-interface/public/shared/greeting/greeting-card.js +2 -2
  410. package/neural-interface/public/shared/greeting/greeting-view.js +1 -0
  411. package/neural-interface/public/shared/html-shell.js +30 -15
  412. package/neural-interface/public/shared/i18n.js +2 -0
  413. package/neural-interface/public/shared/ocp-v2/ocp-v2-approvals.js +7 -7
  414. package/neural-interface/public/shared/ocp-v2/ocp-v2-binding.js +4 -4
  415. package/neural-interface/public/shared/ocp-v2/ocp-v2-changes-logic.js +3 -101
  416. package/neural-interface/public/shared/ocp-v2/ocp-v2-childpanel.js +2 -0
  417. package/neural-interface/public/shared/ocp-v2/ocp-v2-compact-button.js +1 -3
  418. package/neural-interface/public/shared/ocp-v2/ocp-v2-composer-logic.js +2 -6
  419. package/neural-interface/public/shared/ocp-v2/ocp-v2-context-menu.js +11 -11
  420. package/neural-interface/public/shared/ocp-v2/ocp-v2-context-model.js +0 -3
  421. package/neural-interface/public/shared/ocp-v2/ocp-v2-events.js +8 -4
  422. package/neural-interface/public/shared/ocp-v2/ocp-v2-manager.js +7 -7
  423. package/neural-interface/public/shared/ocp-v2/ocp-v2-outcome-notifications.js +16 -0
  424. package/neural-interface/public/shared/ocp-v2/ocp-v2-panel.js +74 -259
  425. package/neural-interface/public/shared/ocp-v2/ocp-v2-plan.js +1 -2
  426. package/neural-interface/public/shared/ocp-v2/ocp-v2-projectbar.js +95 -202
  427. package/neural-interface/public/shared/ocp-v2/ocp-v2-rehydrate.js +13 -4
  428. package/neural-interface/public/shared/ocp-v2/ocp-v2-render.js +13 -22
  429. package/neural-interface/public/shared/ocp-v2/ocp-v2-send.js +4 -4
  430. package/neural-interface/public/shared/ocp-v2/ocp-v2-session-actions.js +8 -61
  431. package/neural-interface/public/shared/ocp-v2/ocp-v2-sessions-logic.js +2 -91
  432. package/neural-interface/public/shared/ocp-v2/ocp-v2-slash-hints.js +16 -15
  433. package/neural-interface/public/shared/ocp-v2/ocp-v2-state.js +8 -21
  434. package/neural-interface/public/shared/ocp-v2/ocp-v2-status-logic.js +1 -99
  435. package/neural-interface/public/shared/ocp-v2/ocp-v2-status.js +5 -35
  436. package/neural-interface/public/shared/ocp-v2/ocp-v2-styles.js +17 -21
  437. package/neural-interface/public/shared/ocp-v2/{ocp-v2-profile-inheritance.js → ocp-v2-tools-inheritance.js} +16 -16
  438. package/neural-interface/public/shared/ocp-v2/ocp-v2-ws.js +60 -50
  439. package/neural-interface/public/shared/page-session.js +96 -0
  440. package/neural-interface/public/shared/provider-icons.js +4 -2
  441. package/neural-interface/public/shared/registry.js +5 -3
  442. package/neural-interface/public/shared/retired-2d.js +3 -0
  443. package/neural-interface/public/shared/schedules-view.js +195 -61
  444. package/neural-interface/public/shared/settings/settings-claude-hooks.js +135 -0
  445. package/neural-interface/public/shared/settings/settings-data.js +3 -1
  446. package/neural-interface/public/shared/settings/settings-ia.js +6 -6
  447. package/neural-interface/public/shared/settings/settings-opencode.js +89 -0
  448. package/neural-interface/public/shared/settings/settings-rules.js +2 -2
  449. package/neural-interface/public/shared/settings/settings-skill-folders-pane.js +265 -0
  450. package/neural-interface/public/shared/settings/settings-skill-folders.js +130 -0
  451. package/neural-interface/public/shared/settings/settings-worker-guard.js +72 -0
  452. package/neural-interface/public/shared/showcase-privacy.js +6 -1
  453. package/neural-interface/public/shared/sidepanel-session-status.js +3 -3
  454. package/neural-interface/public/shared/skills/ss-args.js +96 -0
  455. package/neural-interface/public/shared/skills/ss-creation.js +222 -0
  456. package/neural-interface/public/shared/skills/ss-dialog.js +133 -0
  457. package/neural-interface/public/shared/skills/ss-doc.js +173 -0
  458. package/neural-interface/public/shared/skills/ss-eval-adapters.js +269 -0
  459. package/neural-interface/public/shared/skills/ss-eval-grade.js +359 -0
  460. package/neural-interface/public/shared/skills/ss-eval-optimise.js +153 -0
  461. package/neural-interface/public/shared/skills/ss-eval-plan.js +315 -0
  462. package/neural-interface/public/shared/skills/ss-evals.js +417 -0
  463. package/neural-interface/public/shared/skills/ss-form.js +159 -0
  464. package/neural-interface/public/shared/skills/ss-frontmatter.js +376 -0
  465. package/neural-interface/public/shared/skills/ss-ids.js +32 -0
  466. package/neural-interface/public/shared/skills/ss-lint.js +149 -0
  467. package/neural-interface/public/shared/skills/ss-presets.js +117 -0
  468. package/neural-interface/public/shared/skills/ss-profiles.js +513 -0
  469. package/neural-interface/public/shared/skills/ss-resources.js +165 -0
  470. package/neural-interface/public/shared/skills/ss-review-view.js +190 -0
  471. package/neural-interface/public/shared/skills/ss-tabs.js +79 -0
  472. package/neural-interface/public/shared/skills/ss-unicode.js +135 -0
  473. package/neural-interface/public/shared/skills/studio/sx-actions.js +222 -0
  474. package/neural-interface/public/shared/skills/studio/sx-cm.js +101 -0
  475. package/neural-interface/public/shared/skills/studio/sx-create-ai-view.js +749 -0
  476. package/neural-interface/public/shared/skills/studio/sx-create-ai.js +237 -0
  477. package/neural-interface/public/shared/skills/studio/sx-create-view.js +254 -0
  478. package/neural-interface/public/shared/skills/studio/sx-create.js +126 -0
  479. package/neural-interface/public/shared/skills/studio/sx-diff-view.js +43 -0
  480. package/neural-interface/public/shared/skills/studio/sx-diff.js +133 -0
  481. package/neural-interface/public/shared/skills/studio/sx-docs.js +480 -0
  482. package/neural-interface/public/shared/skills/studio/sx-dom.js +230 -0
  483. package/neural-interface/public/shared/skills/studio/sx-edit-view.js +848 -0
  484. package/neural-interface/public/shared/skills/studio/sx-editor-model.js +193 -0
  485. package/neural-interface/public/shared/skills/studio/sx-errors.js +22 -0
  486. package/neural-interface/public/shared/skills/studio/sx-fixes-view.js +359 -0
  487. package/neural-interface/public/shared/skills/studio/sx-fixes.js +327 -0
  488. package/neural-interface/public/shared/skills/studio/sx-geometry.js +82 -0
  489. package/neural-interface/public/shared/skills/studio/sx-import.js +262 -0
  490. package/neural-interface/public/shared/skills/studio/sx-inspector.js +308 -0
  491. package/neural-interface/public/shared/skills/studio/sx-install-flow.js +282 -0
  492. package/neural-interface/public/shared/skills/studio/sx-install.js +313 -0
  493. package/neural-interface/public/shared/skills/studio/sx-library-view.js +748 -0
  494. package/neural-interface/public/shared/skills/studio/sx-library.js +479 -0
  495. package/neural-interface/public/shared/skills/studio/sx-main.js +690 -0
  496. package/neural-interface/public/shared/skills/studio/sx-operations-view.js +98 -0
  497. package/neural-interface/public/shared/skills/studio/sx-operations.js +11 -0
  498. package/neural-interface/public/shared/skills/studio/sx-plan.js +158 -0
  499. package/neural-interface/public/shared/skills/studio/sx-publish-view.js +621 -0
  500. package/neural-interface/public/shared/skills/studio/sx-review-view.js +369 -0
  501. package/neural-interface/public/shared/skills/studio/sx-review.js +148 -0
  502. package/neural-interface/public/shared/skills/studio/sx-scan.js +35 -0
  503. package/neural-interface/public/shared/skills/studio/sx-snippets.js +96 -0
  504. package/neural-interface/public/shared/skills/studio/sx-subject.js +249 -0
  505. package/neural-interface/public/shared/skills/studio/sx-test-view.js +994 -0
  506. package/neural-interface/public/shared/skills/studio/sx-test.js +328 -0
  507. package/neural-interface/public/shared/skills/studio/sx-versions-view.js +241 -0
  508. package/neural-interface/public/shared/skills/studio/sx-workspace-view.js +426 -0
  509. package/neural-interface/public/shared/state.js +1 -1
  510. package/neural-interface/public/shared/streaming-markdown.js +58 -0
  511. package/neural-interface/public/shared/styleguide/sg-panel.js +4 -2
  512. package/neural-interface/public/shared/styles.css +1822 -1756
  513. package/neural-interface/public/shared/synabun-mascot.js +1115 -9
  514. package/neural-interface/public/shared/text-cache.js +29 -0
  515. package/neural-interface/public/shared/tool-list-picker.js +55 -0
  516. package/neural-interface/public/shared/tool-list-storage.js +24 -0
  517. package/neural-interface/public/shared/tool-list-view.js +112 -0
  518. package/neural-interface/public/shared/topright-overflow-logic.js +54 -0
  519. package/neural-interface/public/shared/tour/tour-actor.js +268 -0
  520. package/neural-interface/public/shared/tour/tour-beats.js +336 -0
  521. package/neural-interface/public/shared/tour/tour-engine.js +884 -0
  522. package/neural-interface/public/shared/tour/tour-ink.js +390 -0
  523. package/neural-interface/public/shared/tour/tour-stage.js +440 -0
  524. package/neural-interface/public/shared/tour/tour-state.js +40 -0
  525. package/neural-interface/public/shared/tour/tour-tips.js +99 -0
  526. package/neural-interface/public/shared/tour/tour.css +144 -0
  527. package/neural-interface/public/shared/ui-antigravity-panel.js +19 -0
  528. package/neural-interface/public/shared/ui-assistant.js +2 -0
  529. package/neural-interface/public/shared/ui-automation-studio.js +111 -140
  530. package/neural-interface/public/shared/ui-claude-panel.js +376 -82
  531. package/neural-interface/public/shared/ui-cli-banner.js +23 -167
  532. package/neural-interface/public/shared/ui-command-runner.js +6 -3
  533. package/neural-interface/public/shared/ui-context-menu.js +295 -0
  534. package/neural-interface/public/shared/ui-cost-widget.js +57 -59
  535. package/neural-interface/public/shared/ui-crash-notice.js +187 -0
  536. package/neural-interface/public/shared/ui-detail.js +15 -3
  537. package/neural-interface/public/shared/ui-file-explorer.js +48 -6
  538. package/neural-interface/public/shared/ui-file-links.js +2 -2
  539. package/neural-interface/public/shared/ui-games.js +2 -2
  540. package/neural-interface/public/shared/ui-image-gallery.js +50 -47
  541. package/neural-interface/public/shared/ui-judgments.js +1 -1
  542. package/neural-interface/public/shared/ui-keybinds.js +33 -2
  543. package/neural-interface/public/shared/ui-link.js +3 -3
  544. package/neural-interface/public/shared/ui-menubar.js +10 -20
  545. package/neural-interface/public/shared/ui-native-loop-router-state.js +5 -0
  546. package/neural-interface/public/shared/ui-native-loop-router.js +11 -1
  547. package/neural-interface/public/shared/ui-navbar.js +49 -24
  548. package/neural-interface/public/shared/ui-notifications.js +1 -0
  549. package/neural-interface/public/shared/ui-panels.js +96 -65
  550. package/neural-interface/public/shared/ui-resume.js +9 -7
  551. package/neural-interface/public/shared/ui-schedules-studio.js +546 -431
  552. package/neural-interface/public/shared/ui-scroll-follow.js +16 -5
  553. package/neural-interface/public/shared/ui-sessions.js +27 -15
  554. package/neural-interface/public/shared/ui-settings.js +683 -526
  555. package/neural-interface/public/shared/ui-sidepanel-session-entry.js +9 -2
  556. package/neural-interface/public/shared/ui-sidepanel-sessions.js +4 -4
  557. package/neural-interface/public/shared/ui-sidepanel-state.js +23 -12
  558. package/neural-interface/public/shared/ui-sidepanel-tray.js +48 -7
  559. package/neural-interface/public/shared/ui-sidepanel-windows.js +18 -13
  560. package/neural-interface/public/shared/ui-skills.js +33 -2830
  561. package/neural-interface/public/shared/ui-styleguide.js +5 -2
  562. package/neural-interface/public/shared/ui-sync.js +15 -5
  563. package/neural-interface/public/shared/ui-terminal.js +270 -190
  564. package/neural-interface/public/shared/ui-tool-updates.js +6 -4
  565. package/neural-interface/public/shared/ui-tooltip.js +81 -51
  566. package/neural-interface/public/shared/ui-topright-overflow.js +233 -0
  567. package/neural-interface/public/shared/ui-trash.js +6 -4
  568. package/neural-interface/public/shared/ui-tutorial-layout.js +3 -117
  569. package/neural-interface/public/shared/ui-tutorial-preview.js +103 -0
  570. package/neural-interface/public/shared/ui-tutorial-steps.js +3 -65
  571. package/neural-interface/public/shared/ui-tutorial.css +47 -100
  572. package/neural-interface/public/shared/ui-tutorial.js +285 -509
  573. package/neural-interface/public/shared/ui-update.js +38 -2
  574. package/neural-interface/public/shared/ui-whiteboard.js +312 -59
  575. package/neural-interface/public/shared/ui-worker-end.js +5 -1
  576. package/neural-interface/public/shared/ui-workspaces.js +3 -2
  577. package/neural-interface/public/shared/view-variant.js +66 -0
  578. package/neural-interface/public/shared/whatsapp/wa-view.js +3 -3
  579. package/neural-interface/public/shared/whiteboard-list.js +172 -0
  580. package/neural-interface/public/shared/worker-end.js +10 -3
  581. package/neural-interface/public/shared/worker-questions.js +34 -0
  582. package/neural-interface/public/shared/worker-restart.js +12 -0
  583. package/neural-interface/public/sidepanel-session.html +2 -2
  584. package/neural-interface/public/variant/2d/background.js +2 -1
  585. package/neural-interface/public/variant/2d/context-menu.js +8 -2
  586. package/neural-interface/public/variant/2d/graph.js +42 -20
  587. package/neural-interface/public/variant/2d/hulls.js +14 -9
  588. package/neural-interface/public/variant/2d/lasso.js +11 -7
  589. package/neural-interface/public/variant/2d/main.js +32 -1
  590. package/neural-interface/public/variant/2d/minimap.js +11 -13
  591. package/neural-interface/public/variant/3d/main.js +69 -6
  592. package/neural-interface/public/variant/3d/map-controls.js +24 -9
  593. package/neural-interface/public/vendor/caveat/OFL.txt +93 -0
  594. package/neural-interface/public/vendor/caveat/caveat-latin-400-normal.woff2 +0 -0
  595. package/neural-interface/public/vendor/caveat/caveat-latin-700-normal.woff2 +0 -0
  596. package/neural-interface/public/vendor/caveat/caveat.css +17 -0
  597. package/neural-interface/public/vendor/caveat/manifest.json +12 -0
  598. package/neural-interface/public/vendor/yaml/LICENSE +13 -0
  599. package/neural-interface/public/vendor/yaml/manifest.json +4 -0
  600. package/neural-interface/public/vendor/yaml/yaml-2.9.1.min.js +137 -0
  601. package/neural-interface/scripts/build-vendor.mjs +32 -0
  602. package/neural-interface/server.js +4553 -2855
  603. package/neural-interface/templates/rulesets/README.md +1 -1
  604. package/neural-interface/templates/rulesets/core.md +1 -1
  605. package/neural-interface/templates/rulesets/legacy-hashes.json +23 -5
  606. package/neural-interface/templates/rulesets/manifest.json +8 -8
  607. package/npm-shrinkwrap.json +34 -12
  608. package/package.json +17 -3
  609. package/postinstall.js +9 -4
  610. package/preuninstall.js +1 -1
  611. package/setup.js +97 -322
  612. package/skills/skill-forge/SKILL.md +177 -0
  613. package/skills/skill-forge/evals/evals.json +54 -0
  614. package/skills/skill-forge/references/package.md +105 -0
  615. package/skills/skill-forge/references/review-and-recovery.md +63 -0
  616. package/skills/skill-forge/references/runtime-profiles.md +20 -0
  617. package/skills/skill-forge/references/sources-and-capabilities.md +51 -0
  618. package/skills/synabun/SKILL.md +10 -7
  619. package/skills/synabun/modules/audit.md +1 -5
  620. package/skills/synabun/modules/changelog.md +1 -5
  621. package/skills/synabun/modules/idea.md +1 -1
  622. package/skills/synabun/modules/memorize.md +6 -9
  623. package/skills/synabun/modules/schedule.md +6 -6
  624. package/mcp-server/dist/services/profiles.d.ts +0 -120
  625. package/mcp-server/dist/services/profiles.d.ts.map +0 -1
  626. package/mcp-server/dist/services/profiles.js +0 -387
  627. package/mcp-server/dist/services/profiles.js.map +0 -1
  628. package/mcp-server/dist/tools/leonardo-browser-tools.d.ts +0 -97
  629. package/mcp-server/dist/tools/leonardo-browser-tools.d.ts.map +0 -1
  630. package/mcp-server/dist/tools/leonardo-browser-tools.js +0 -228
  631. package/mcp-server/dist/tools/leonardo-browser-tools.js.map +0 -1
  632. package/mcp-server/dist/tools/leonardo.d.ts +0 -13
  633. package/mcp-server/dist/tools/leonardo.d.ts.map +0 -1
  634. package/mcp-server/dist/tools/leonardo.js +0 -21
  635. package/mcp-server/dist/tools/leonardo.js.map +0 -1
  636. package/mcp-server/dist/tools/profile.d.ts +0 -17
  637. package/mcp-server/dist/tools/profile.d.ts.map +0 -1
  638. package/mcp-server/dist/tools/profile.js +0 -114
  639. package/mcp-server/dist/tools/profile.js.map +0 -1
  640. package/mcp-server/src/services/profiles.ts +0 -428
  641. package/mcp-server/src/tools/leonardo-browser-tools.ts +0 -291
  642. package/mcp-server/src/tools/leonardo.ts +0 -30
  643. package/mcp-server/src/tools/profile.ts +0 -122
  644. package/neural-interface/lib/assistant-brains/opencode-browser-policy.js +0 -47
  645. package/neural-interface/lib/assistant-brains/opencode-route-gate.js +0 -62
  646. package/neural-interface/lib/mcp-profile-servers.js +0 -120
  647. package/neural-interface/public/shared/ui-tutorial-draw.js +0 -311
  648. package/skills/leonardo/SKILL.md +0 -168
  649. package/skills/leonardo/icon.svg +0 -1
  650. package/skills/leonardo/modules/browser-guide.md +0 -378
  651. package/skills/leonardo/modules/image-prompter.md +0 -283
  652. package/skills/leonardo/modules/model-advisor.md +0 -100
  653. package/skills/leonardo/modules/prompt-library.md +0 -110
  654. package/skills/leonardo/modules/style-guide.md +0 -186
  655. package/skills/leonardo/modules/video-prompter.md +0 -317
  656. /package/neural-interface/templates/rulesets/{gemini.md → antigravity.md} +0 -0
@@ -0,0 +1,508 @@
1
+ /**
2
+ * SynaBun — the supervisor's run of the server
3
+ *
4
+ * setup.js starts the Neural Interface server (neural-interface/server.js) as a
5
+ * child through this module, and decides every time that child ends what comes
6
+ * next (lib/server-exit.js serverExitDecision):
7
+ *
8
+ * restart it asked to be started again (exit code 75, or the restart marker)
9
+ * crashed it ended on its own after it was up: started again within the
10
+ * crash-loop guard (5 starts in 60 s), and the record of the crash
11
+ * (lib/server-crash-record.js) is left for the server it starts,
12
+ * which tells the owner's pages once that it happened
13
+ * failed it ended before it was listening: the start reports it and ends
14
+ * stopped it was asked to stop (the stop marker, or the stop line on its
15
+ * stdout with this start's token), or it ended cleanly: the
16
+ * supervisor ends too
17
+ *
18
+ * Asked both to restart and to stop, the later request wins: a stop asked while
19
+ * a restart shut down (the desktop app quitting) is a stop. A marker that is
20
+ * there but cannot be read (a scanner holding it) is read again a few times; a
21
+ * stop marker that stays unreadable is still read as the stop it stands for.
22
+ *
23
+ * The end is decided once the child's output has been read to the end
24
+ * (watchChildEnd), so a crash's last lines are kept, and its stdout is read in
25
+ * whole lines: a line split across two reads is still one line.
26
+ *
27
+ * When the supervisor ends because the server could not be kept up (the guard
28
+ * gave up, a start failed), it says so first (the supervisor record, the restart
29
+ * report) and then waits, a bounded time, for what Windows recorded about a
30
+ * native crash, which it writes to its logs as soon as each part is found.
31
+ *
32
+ * Everything outside this process is handed in (the record, the restart report,
33
+ * the browser, the Windows lookup, the exit), so
34
+ * tests/server-supervisor.test.mjs runs this with real child processes over a
35
+ * temporary data home. Never run setup.js to check it.
36
+ */
37
+
38
+ import { spawn as nodeSpawn } from 'node:child_process';
39
+ import { randomBytes } from 'node:crypto';
40
+ import { createWriteStream, existsSync, mkdirSync, readFileSync, readdirSync, renameSync, statSync, unlinkSync } from 'node:fs';
41
+ import { join, resolve } from 'node:path';
42
+ import {
43
+ MARKER_CLOCK_SLACK_MS, RESTART_EXIT_CODE, STOP_MARKER_NAME, STOP_TOKEN_ENV, createRestartGuard, describeExit, exitRecordFields, formatExit,
44
+ readRestartMarker, readStopAnnouncement, readStopMarker, serverExitDecision, watchChildEnd,
45
+ } from './server-exit.js';
46
+ import { clearCrashRecord, writeCrashRecord } from './server-crash-record.js';
47
+ import { describeWindowsCrash, lookupWindowsCrash as nodeLookupWindowsCrash } from './windows-crash-event.js';
48
+
49
+ /** The crash-loop guard: more than CRASH_LIMIT starts within CRASH_WINDOW_MS end the supervisor. */
50
+ export const CRASH_WINDOW_MS = 60_000;
51
+ export const CRASH_LIMIT = 5;
52
+ /** How long the server's output is read after it exited, at most, before its end is decided. */
53
+ export const SERVER_OUTPUT_DRAIN_MS = 1500;
54
+ /** The pause before a server is started again (its port is let go). */
55
+ export const RESPAWN_DELAY_MS = 250;
56
+ /**
57
+ * How long a supervisor that ends waits, at most, for what Windows recorded about
58
+ * a native crash (lib/windows-crash-event.js tries at 1.5, 7.5 and 22.5 s). The
59
+ * lookup is given this less CRASH_LOOKUP_MARGIN_MS as its whole budget, waits and
60
+ * queries together, so it ends, with what it found written, before the wait does.
61
+ */
62
+ export const CRASH_LOOKUP_WAIT_MS = 30_000;
63
+ export const CRASH_LOOKUP_MARGIN_MS = 2000;
64
+ /** Each log file is moved aside past this size, when the supervisor starts. */
65
+ export const LOG_ROTATE_BYTES = 50 * 1024 * 1024;
66
+ /**
67
+ * A marker that is there but cannot be read (Windows: a scanner holds a file it just saw written) is read again,
68
+ * this many times in all, this far apart, before the end is decided without its words.
69
+ */
70
+ export const MARKER_READ_ATTEMPTS = 6;
71
+ export const MARKER_RETRY_MS = 150;
72
+ // What a read of a file someone else holds for a moment answers.
73
+ const HELD = new Set(['EPERM', 'EBUSY', 'EACCES', 'EAGAIN', 'EMFILE', 'ENFILE']);
74
+ const MAX_LINE = 64 * 1024;
75
+
76
+ /** Whole lines out of a stream's chunks: `push(chunk)` hands each complete line to `onLine`, `end()` the rest. */
77
+ export function createLineReader(onLine, { maxLine = MAX_LINE } = {}) {
78
+ let rest = '';
79
+ const emit = (line) => { try { onLine(line.endsWith('\r') ? line.slice(0, -1) : line); } catch {} };
80
+ return {
81
+ push(chunk) {
82
+ rest += String(chunk);
83
+ let at;
84
+ while ((at = rest.indexOf('\n')) >= 0) {
85
+ emit(rest.slice(0, at));
86
+ rest = rest.slice(at + 1);
87
+ }
88
+ // A line that never ends is read in pieces, never kept whole.
89
+ while (rest.length > maxLine) {
90
+ emit(rest.slice(0, maxLine));
91
+ rest = rest.slice(maxLine);
92
+ }
93
+ },
94
+ end() {
95
+ if (rest) emit(rest);
96
+ rest = '';
97
+ },
98
+ };
99
+ }
100
+
101
+ /** The address the server says it listens on (" Server: http://localhost:3344"): { url, port } or null. */
102
+ export function listeningAddress(line) {
103
+ const found = /^\s*Server:\s+(https?:\/\/\S+)/.exec(String(line ?? ''));
104
+ if (!found) return null;
105
+ let port = null;
106
+ try {
107
+ const url = new URL(found[1]);
108
+ port = Number(url.port || (url.protocol === 'https:' ? 443 : 80));
109
+ } catch {}
110
+ return { url: found[1], port: Number.isInteger(port) && port > 0 && port < 65536 ? port : null };
111
+ }
112
+
113
+ function within(promise, ms) {
114
+ return new Promise((done) => {
115
+ const timer = setTimeout(done, ms);
116
+ Promise.resolve(promise).catch(() => {}).then(() => { clearTimeout(timer); done(); });
117
+ });
118
+ }
119
+
120
+ /**
121
+ * Run the server and keep it up. Returns { stop(signal), child }: `stop` ends
122
+ * it on the supervisor's own signal (SIGINT, SIGTERM) and exits.
123
+ *
124
+ * @param {object} o
125
+ * @param {string} o.serverPath neural-interface/server.js
126
+ * @param {string} o.cwd the folder it runs in
127
+ * @param {string} o.dataHome SynaBun's data home (markers, records, logs)
128
+ * @param {() => object} [o.env] the environment the server gets, read at each start
129
+ * @param {{ info: Function, warn: Function, fail: Function }} [o.print] the supervisor's own lines
130
+ * @param {(name: string, detail?: object) => void} [o.onPhase] the supervisor record's phase
131
+ * @param {(code: string, message: string, extra?: object) => void} [o.onFailed] the record's failure
132
+ * @param {(o: { port: number|null, first: boolean }) => void} [o.onListening] the server said it listens
133
+ * @param {{ reopen: (port: number) => void, failed: (error: object) => Promise<void> }} [o.reports] the restart report
134
+ * @param {(code: number) => void} [o.exit]
135
+ */
136
+ export function superviseServer({
137
+ serverPath,
138
+ cwd,
139
+ dataHome,
140
+ execPath = process.execPath,
141
+ nodeArgs = ['--disable-warning=ExperimentalWarning', '--max-old-space-size=6144'],
142
+ env = () => process.env,
143
+ windowsHide = false,
144
+ platform = process.platform,
145
+ spawn = nodeSpawn,
146
+ logDir = join(dataHome, 'data'),
147
+ print = { info() {}, warn() {}, fail() {} },
148
+ passthrough = { stdout: process.stdout, stderr: process.stderr },
149
+ onPhase = () => {},
150
+ onFailed = () => {},
151
+ onListening = () => {},
152
+ reports = { reopen() {}, async failed() {} },
153
+ resolvePort = () => null,
154
+ lookupWindowsCrash = nodeLookupWindowsCrash,
155
+ exit = (code) => process.exit(code),
156
+ windowMs = CRASH_WINDOW_MS,
157
+ limit = CRASH_LIMIT,
158
+ drainMs = SERVER_OUTPUT_DRAIN_MS,
159
+ respawnDelayMs = RESPAWN_DELAY_MS,
160
+ crashLookupWaitMs = CRASH_LOOKUP_WAIT_MS,
161
+ markerReadAttempts = MARKER_READ_ATTEMPTS,
162
+ markerRetryMs = MARKER_RETRY_MS,
163
+ files = { readFileSync, statSync },
164
+ sleep = (ms) => new Promise((done) => setTimeout(done, ms)),
165
+ newToken = () => randomBytes(16).toString('hex'),
166
+ now = () => Date.now(),
167
+ } = {}) {
168
+ if (!serverPath || !dataHome) throw new Error('superviseServer needs the server and the data home');
169
+
170
+ // Marker files the server writes before it ends: a restart it asked for (server.js /api/server/restart; a
171
+ // native teardown crash can clobber its exit code 75), and a stop someone asked for (its exit status cannot say
172
+ // so: server.js ends itself by force after its cleanup, which Windows reports as exit code 1 and macOS / Linux
173
+ // as SIGKILL). A marker from a run before this one says nothing about this one.
174
+ const restartMarker = resolve(dataHome, 'restart-requested');
175
+ const stopMarker = resolve(dataHome, STOP_MARKER_NAME);
176
+ const remove = (path) => { try { if (existsSync(path)) unlinkSync(path); } catch {} };
177
+ remove(restartMarker);
178
+ remove(stopMarker);
179
+ // What a stop marker left half-written when its server died between the write and the rename.
180
+ try {
181
+ for (const name of readdirSync(dataHome)) if (/^stop-requested\.\d+\.tmp$/.test(name)) remove(join(dataHome, name));
182
+ } catch {}
183
+
184
+ // The server's stdout and stderr go to log files too, whoever launched the supervisor (the Apps menu, a
185
+ // terminal, launchd, the desktop app): the files a person sends when they report a crash.
186
+ try { mkdirSync(logDir, { recursive: true }); } catch {}
187
+ const stdoutPath = resolve(logDir, 'server-stdout.log');
188
+ const stderrPath = resolve(logDir, 'server-stderr.log');
189
+ for (const path of [stdoutPath, stderrPath]) {
190
+ try { if (existsSync(path) && statSync(path).size > LOG_ROTATE_BYTES) renameSync(path, `${path}.1`); } catch {}
191
+ }
192
+ let outLog = null;
193
+ let errLog = null;
194
+ // A full disk reports asynchronously from WriteStream.write(): without an error listener that event would end
195
+ // the supervisor itself. Only the stream that failed is given up; supervision goes on.
196
+ const openLog = (kind, path) => {
197
+ let stream = null;
198
+ try { stream = createWriteStream(path, { flags: 'a' }); } catch (error) {
199
+ print.warn(`Could not open ${kind} log: ${error.message}`);
200
+ return null;
201
+ }
202
+ stream.on('error', (error) => {
203
+ if (kind === 'stdout' && outLog === stream) outLog = null;
204
+ if (kind === 'stderr' && errLog === stream) errLog = null;
205
+ print.warn(`Could not persist ${kind} server log: ${error.message} — continuing without file capture.`);
206
+ try { stream.destroy(); } catch {}
207
+ });
208
+ return stream;
209
+ };
210
+ outLog = openLog('stdout', stdoutPath);
211
+ errLog = openLog('stderr', stderrPath);
212
+ const stamp = `\n===== supervisor start ${new Date(now()).toISOString()} (pid ${process.pid}) =====\n`;
213
+ try { outLog?.write(stamp); errLog?.write(stamp); } catch {}
214
+
215
+ const guard = createRestartGuard({ windowMs, limit, now });
216
+ const seconds = Math.round(windowMs / 1000);
217
+ const state = { child: null, firstLaunch: true, shuttingDown: false, ending: false, listenPort: null };
218
+
219
+ // What the supervisor says about the server's end also goes into the server's own error log, beside the
220
+ // server's last lines. Once the logs are closed a late line is dropped, never written to an ended stream.
221
+ const noteInServerLog = (line) => {
222
+ try { errLog?.write(`[supervisor ${new Date(now()).toISOString()}] ${line}\n`); } catch {}
223
+ };
224
+
225
+ // Windows: what Windows Error Reporting recorded about a native crash (the module, the fail-fast code, the
226
+ // report's folder), read from the Application log, each part logged as soon as it is found. The next server is
227
+ // never held up for it; a supervisor that ends waits for it, a bounded time (settle).
228
+ const lookupCrash = (pid, since, status) => {
229
+ if (platform !== 'win32' || status.kind !== 'native-crash') return null;
230
+ const said = new Set();
231
+ const tell = (found) => {
232
+ const line = found?.crash ? describeWindowsCrash(found.crash, found.report) : '';
233
+ if (!line || said.has(line)) return;
234
+ said.add(line);
235
+ print.info(line);
236
+ noteInServerLog(line);
237
+ };
238
+ // Its budget fits in the wait of a supervisor that ends: nothing it finds is cut off by that wait.
239
+ const budgetMs = Math.max(0, crashLookupWaitMs - CRASH_LOOKUP_MARGIN_MS);
240
+ return Promise.resolve()
241
+ .then(() => lookupWindowsCrash({ pid, since, platform, onFound: tell, budgetMs }))
242
+ .then((found) => {
243
+ if (found) tell(found);
244
+ else {
245
+ const line = `No Windows Error Reporting event was found for the crash of pid ${pid}.`;
246
+ print.info(line);
247
+ noteInServerLog(line);
248
+ }
249
+ return found;
250
+ }, () => null);
251
+ };
252
+
253
+ // What is still on its way to the log files is written before the supervisor goes: a crash's last lines are
254
+ // the ones that say why. So is what Windows recorded about it, `crashLookupWaitMs` at the most.
255
+ const closeStream = (stream) => new Promise((done) => {
256
+ if (!stream) { done(); return; }
257
+ try { stream.end(done); } catch { done(); }
258
+ });
259
+ async function settle(pending = null) {
260
+ if (pending) await within(pending, crashLookupWaitMs);
261
+ const streams = [outLog, errLog];
262
+ outLog = null;
263
+ errLog = null;
264
+ await within(Promise.allSettled(streams.map(closeStream)), 1500);
265
+ }
266
+
267
+ // The supervisor ends without a server: it has said so already (onFailed); the restart report says it to a
268
+ // page that waits (lib/start-launcher.js openRestartReport), while the logs get their last lines.
269
+ function endWithoutServer(code, error, pending = null) {
270
+ if (state.ending) return;
271
+ state.ending = true;
272
+ // The crash it recovered from is not news any more: no server came back up to tell it.
273
+ clearCrashRecord(dataHome);
274
+ Promise.allSettled([
275
+ settle(pending),
276
+ Promise.resolve().then(() => reports.failed(error)),
277
+ ]).finally(() => exit(code));
278
+ }
279
+
280
+ // A marker file as it is now: { state: 'none' }, { state: 'read', text, mtimeMs } or, when it is there and could
281
+ // not be read after a few tries (a scanner holding it), { state: 'unreadable', code, mtimeMs }.
282
+ const mtimeOf = (path) => {
283
+ try { const ms = files.statSync(path).mtimeMs; return Number.isFinite(ms) ? ms : null; } catch { return null; }
284
+ };
285
+ async function readMarkerFile(path) {
286
+ let failure = null;
287
+ for (let attempt = 0; attempt < Math.max(1, markerReadAttempts); attempt++) {
288
+ if (attempt) await sleep(markerRetryMs);
289
+ try {
290
+ return { state: 'read', text: String(files.readFileSync(path, 'utf8')), mtimeMs: mtimeOf(path) };
291
+ } catch (error) {
292
+ if (error?.code === 'ENOENT') return { state: 'none' };
293
+ failure = error;
294
+ if (!HELD.has(error?.code)) break;
295
+ }
296
+ }
297
+ return { state: 'unreadable', code: String(failure?.code || failure?.message || 'unknown').slice(0, 40), mtimeMs: mtimeOf(path) };
298
+ }
299
+
300
+ // The restart marker: { marked, at } (at: when the restart was asked for, when that can be told).
301
+ async function takeRestartMarker() {
302
+ const file = await readMarkerFile(restartMarker);
303
+ if (file.state === 'none') return { marked: false, at: null };
304
+ remove(restartMarker);
305
+ return { marked: true, at: (file.state === 'read' ? readRestartMarker(file.text) : null) ?? file.mtimeMs };
306
+ }
307
+
308
+ // The stop marker, taken: { reason, at } of a stop this server asked for, or null. One that is there (its file is
309
+ // seen) but stays unreadable is only ever written for a stop someone asked for, so it is read as a stop (and said
310
+ // so), unless it was plainly written before this server started. One that cannot even be seen (Windows: a file
311
+ // being deleted while something still holds it) proves nothing.
312
+ async function takeStopMarker(pid, since) {
313
+ const file = await readMarkerFile(stopMarker);
314
+ if (file.state === 'none') return null;
315
+ remove(stopMarker);
316
+ if (file.state === 'read') return readStopMarker(file.text, { pid, since });
317
+ if (file.mtimeMs === null) {
318
+ noteInServerLog(`A stop marker could not be read or seen (${file.code}) when the server (pid ${pid}) ended: it says nothing about this end.`);
319
+ return null;
320
+ }
321
+ if (file.mtimeMs < since - MARKER_CLOCK_SLACK_MS) return null;
322
+ const line = `The stop marker of the server (pid ${pid}) is there but could not be read (${file.code}): its end is read as the stop it stands for.`;
323
+ print.warn(line);
324
+ noteInServerLog(line);
325
+ return { reason: '', at: file.mtimeMs, unreadable: true };
326
+ }
327
+
328
+ // The server has ended and its output was read: what it left (its markers) is read, then it is started again, or
329
+ // the supervisor ends too.
330
+ function onServerEnd(child, status, info) {
331
+ if (state.child === child) state.child = null;
332
+ if (state.shuttingDown || state.ending) return;
333
+ // (decided once: a read that failed decides without the markers, a decision that failed is only said)
334
+ Promise.all([takeRestartMarker(), takeStopMarker(child.pid, info.spawnedAt)]).then(
335
+ ([restart, marker]) => decideEnd(child, status, { ...info, restart, marker }),
336
+ (error) => {
337
+ print.fail(`The markers the server left could not be read: ${error?.message || error}`);
338
+ decideEnd(child, status, { ...info, restart: { marked: false, at: null }, marker: null });
339
+ },
340
+ ).catch((error) => print.fail(`The end of the server could not be decided: ${error?.message || error}`));
341
+ }
342
+
343
+ function decideEnd(child, { code, signal }, { listened, spawnedAt, announced, restart, marker }) {
344
+ if (state.shuttingDown || state.ending) return;
345
+ const restartMarked = restart.marked;
346
+ const stop = marker || announced;
347
+ // Both asked for: the later request wins (a stop asked while a restart shut down: the desktop app quitting).
348
+ const stopAfterRestart = !!stop && restartMarked && Number.isFinite(stop.at) && Number.isFinite(restart.at) && stop.at >= restart.at;
349
+ const status = describeExit({ code, signal });
350
+ const decision = serverExitDecision({ code, signal, restartRequested: restartMarked, stopRequested: !!stop, stopAfterRestart, listened });
351
+
352
+ if (decision === 'restart' || decision === 'crashed') {
353
+ const crashed = decision === 'crashed';
354
+ if (crashed) {
355
+ // Nobody asked for this end: the server crashed after it was up. It is started again, as a restart is,
356
+ // within the same crash-loop guard.
357
+ print.fail(`The server crashed: ${formatExit(status)}.`);
358
+ noteInServerLog(`The server (pid ${child.pid}) crashed: ${formatExit(status)}. Starting it again.`);
359
+ } else if (code === RESTART_EXIT_CODE) {
360
+ print.info('Server requested restart — respawning...');
361
+ } else {
362
+ print.info(`Server exited with code ${code} but restart marker present — respawning.`);
363
+ }
364
+ const lookup = crashed ? lookupCrash(child.pid, spawnedAt, status) : null;
365
+ const { count, exceeded } = guard.note();
366
+ // The page that asked for this has no server to ask now: from here until the new one is listening, this
367
+ // supervisor says what happens.
368
+ try { reports.reopen(state.listenPort || resolvePort()); } catch {}
369
+ if (exceeded) {
370
+ // A loop of crashes says how the last one ended (the app's stopped page names it); restarts asked for do not.
371
+ const message = crashed
372
+ ? `the server stopped ${count} times in ${seconds} s (the last time: ${status.text})`
373
+ : `the server restarted ${count} times in ${seconds} s`;
374
+ const fields = crashed ? exitRecordFields(status) : {};
375
+ print.fail(`Server restarted ${count} times in ${seconds}s — aborting supervisor.`);
376
+ onFailed('RESTART_LOOP', message, fields);
377
+ endWithoutServer(1, { code: 'RESTART_LOOP', message, ...fields }, lookup);
378
+ return;
379
+ }
380
+ if (crashed) {
381
+ // The server it starts tells the owner's pages, once, what happened and what was lost.
382
+ if (!writeCrashRecord(dataHome, { exit: status, at: now(), uptimeMs: Math.max(0, now() - spawnedAt) })) {
383
+ print.warn('Could not leave the crash notice for the next server.');
384
+ }
385
+ print.info(`Starting it again (${count} of ${limit} restarts within ${seconds} s).`);
386
+ }
387
+ state.firstLaunch = false;
388
+ setTimeout(spawnOnce, respawnDelayMs);
389
+ return;
390
+ }
391
+
392
+ if (decision === 'failed') {
393
+ // It ended before it was listening: a start that failed. Starting it again would fail the same way, so the
394
+ // start reports it and ends.
395
+ const message = `the server exited before it was listening (${status.text})`;
396
+ print.fail(`Server exited with code ${code ?? signal}: ${formatExit(status)}`);
397
+ noteInServerLog(`The server (pid ${child.pid}) ended before it was listening: ${formatExit(status)}.`);
398
+ onFailed('SERVER_EXITED', message, exitRecordFields(status));
399
+ endWithoutServer(Number.isInteger(code) && code !== 0 ? code : 1, { code: 'SERVER_EXITED', message, ...exitRecordFields(status) }, lookupCrash(child.pid, spawnedAt, status));
400
+ return;
401
+ }
402
+
403
+ // Stopped: asked for (Stop Server, the app quitting, an update), or a clean end.
404
+ state.ending = true;
405
+ print.info(`Server stopped${stop?.reason ? ` (${stop.reason})` : ''}.`);
406
+ if (stopAfterRestart) print.info('The restart it had asked for was called off by the stop asked after it.');
407
+ // A native crash on the way out (a native teardown abort) is still worth a line.
408
+ if (status.kind === 'native-crash') {
409
+ print.info(`It ended with ${formatExit(status)} while it stopped.`);
410
+ noteInServerLog(`The server (pid ${child.pid}) ended with ${formatExit(status)} while it stopped.`);
411
+ }
412
+ settle().finally(() => exit(0));
413
+ }
414
+
415
+ function spawnFailed(error) {
416
+ const message = `the server could not be started: ${error?.message || error}`;
417
+ print.fail(`Could not start the server: ${error?.message || error}`);
418
+ onFailed('SERVER_SPAWN_FAILED', message);
419
+ endWithoutServer(1, { code: 'SERVER_SPAWN_FAILED', message });
420
+ }
421
+
422
+ function spawnOnce() {
423
+ if (state.shuttingDown || state.ending) return;
424
+ remove(stopMarker);
425
+ const spawnedAt = now();
426
+ // This start's token: the server prints it with a stop someone asked of it (lib/server-exit.js
427
+ // stopAnnouncement), which tells the stop without the data folder.
428
+ const token = newToken();
429
+ let child;
430
+ try {
431
+ child = spawn(execPath, [...nodeArgs, serverPath], {
432
+ cwd,
433
+ stdio: ['ignore', 'pipe', 'pipe'],
434
+ windowsHide,
435
+ env: {
436
+ ...(typeof env === 'function' ? env() : env),
437
+ SYNABUN_DATA_HOME: dataHome,
438
+ MEMORY_DATA_DIR: resolve(dataHome, 'mcp-data'),
439
+ SYNABUN_SUPERVISED: '1',
440
+ [STOP_TOKEN_ENV]: token,
441
+ },
442
+ });
443
+ } catch (error) {
444
+ spawnFailed(error);
445
+ return;
446
+ }
447
+ state.child = child;
448
+ onPhase('server', { pid: child.pid });
449
+
450
+ const first = state.firstLaunch;
451
+ let listened = false;
452
+ let announced = null;
453
+ const lines = createLineReader((line) => {
454
+ const said = readStopAnnouncement(line, token);
455
+ // When it was said, by this supervisor's clock (the latest, when it is said again): a restart asked for
456
+ // before it gives way to it.
457
+ if (said) { announced = { ...said, at: now() }; return; }
458
+ if (listened) return;
459
+ // The server prints its address from its listen callback: it is up.
460
+ const address = listeningAddress(line);
461
+ if (!address) return;
462
+ listened = true;
463
+ if (address.port) state.listenPort = address.port;
464
+ onPhase('listening', { port: address.port || undefined });
465
+ try { onListening({ port: address.port, first }); } catch {}
466
+ });
467
+
468
+ // Its end is decided once its output has been read to the end, or a moment after it exited when something it
469
+ // started still holds that output open: a crash's last words are its last lines.
470
+ const end = watchChildEnd(child, {
471
+ drainMs,
472
+ onEnd: (status) => {
473
+ lines.end();
474
+ onServerEnd(child, status, { listened, spawnedAt, announced });
475
+ },
476
+ });
477
+ child.on('error', (error) => {
478
+ // A server that could not be started at all is reported here, and not a second time as an end.
479
+ if (end.decided) return;
480
+ end.cancel();
481
+ if (state.child === child) state.child = null;
482
+ spawnFailed(error);
483
+ });
484
+ child.stdout?.setEncoding?.('utf8');
485
+ child.stderr?.setEncoding?.('utf8');
486
+ child.stdout?.on('data', (text) => {
487
+ try { passthrough?.stdout?.write(text); } catch {}
488
+ try { outLog?.write(text); } catch {}
489
+ lines.push(text);
490
+ });
491
+ child.stderr?.on('data', (text) => {
492
+ try { passthrough?.stderr?.write(text); } catch {}
493
+ try { errLog?.write(text); } catch {}
494
+ });
495
+ }
496
+
497
+ spawnOnce();
498
+
499
+ return {
500
+ get child() { return state.child; },
501
+ /** The supervisor's own signal: the server gets it too (its handler stops it gracefully), and this ends. */
502
+ stop(signal) {
503
+ state.shuttingDown = true;
504
+ try { state.child?.kill(signal); } catch {}
505
+ exit(0);
506
+ },
507
+ };
508
+ }
@@ -549,10 +549,17 @@ export const SUPERVISOR_PHASES = Object.freeze([
549
549
 
550
550
  function supervisorError(error) {
551
551
  const exitCode = Number(error?.exitCode);
552
+ // How the server's exit reads (lib/server-exit.js exitRecordFields): the desktop app's stopped page names it.
553
+ const exitHex = typeof error?.exitHex === 'string' && /^0x[0-9A-F]{8}$/i.test(error.exitHex) ? error.exitHex : null;
554
+ const exitSignal = typeof error?.exitSignal === 'string' && /^SIG[A-Z0-9]{1,10}$/.test(error.exitSignal) ? error.exitSignal : null;
555
+ const exitKind = typeof error?.exitKind === 'string' && /^[a-z][a-z-]{0,23}$/.test(error.exitKind) ? error.exitKind : null;
552
556
  return {
553
557
  code: String(error?.code || 'SETUP_FAILED').slice(0, 40),
554
558
  message: String(error?.message || error || 'the start failed').slice(0, 300),
555
559
  ...(Number.isInteger(exitCode) ? { exitCode } : {}),
560
+ ...(exitHex ? { exitHex } : {}),
561
+ ...(exitSignal ? { exitSignal } : {}),
562
+ ...(exitKind ? { exitKind } : {}),
556
563
  };
557
564
  }
558
565
 
@@ -852,6 +859,8 @@ export function createStartBeacon({
852
859
  let retryTimer = null;
853
860
  let finalReads = 0;
854
861
  let onFinalRead = null;
862
+ // Reads by a page (a request with an Origin), whatever the state: whether anybody follows this launch at all.
863
+ let pageReads = 0;
855
864
  // Each phase the supervisor was seen in, with the time it gave for it. One
856
865
  // that came and went between two looks is not here: this is what was seen.
857
866
  const phases = [];
@@ -899,6 +908,7 @@ export function createStartBeacon({
899
908
  'X-Content-Type-Options': 'nosniff',
900
909
  });
901
910
  res.end(body);
911
+ if (origin) pageReads++;
902
912
  if (status.state !== 'starting' && origin) {
903
913
  finalReads++;
904
914
  onFinalRead?.();
@@ -976,17 +986,31 @@ export function createStartBeacon({
976
986
  /**
977
987
  * Keep answering until a page has read how it ended (and a moment more,
978
988
  * for a second tab), `ms` at the most. A beacon nobody can reach is over at once.
989
+ * With `idleMs`, a beacon no page has read at all is over after `idleMs`: a
990
+ * page that follows a launch reads it every few hundred milliseconds, and
991
+ * one that has not looked by then is not waiting for it.
979
992
  */
980
- linger(ms, { afterReadMs = 1500 } = {}) {
993
+ linger(ms, { afterReadMs = 1500, idleMs = 0 } = {}) {
981
994
  if (!listening || closed || !(ms > 0)) return Promise.resolve();
982
995
  return new Promise((done) => {
983
996
  let timer = setTimeout(done, ms);
997
+ let idle = null;
984
998
  const soon = () => {
985
999
  onFinalRead = null;
986
1000
  clearTimeout(timer);
1001
+ if (idle) clearTimeout(idle);
987
1002
  timer = setTimeout(done, afterReadMs);
988
1003
  };
989
- if (finalReads > 0) soon(); else onFinalRead = soon;
1004
+ if (finalReads > 0) { soon(); return; }
1005
+ onFinalRead = soon;
1006
+ if (idleMs > 0 && pageReads === 0) {
1007
+ idle = setTimeout(() => {
1008
+ if (pageReads > 0) return;
1009
+ onFinalRead = null;
1010
+ clearTimeout(timer);
1011
+ done();
1012
+ }, Math.min(idleMs, ms));
1013
+ }
990
1014
  });
991
1015
  },
992
1016
  close() {
@@ -1010,6 +1034,13 @@ export function createStartBeacon({
1010
1034
  export const RESTART_REPORT_ENV = 'SYNABUN_RESTART_REPORT';
1011
1035
  /** How long a restart's report stays once the server said it is listening: a page polls every 300 ms. */
1012
1036
  export const RESTART_REPORT_SETTLE_MS = 3000;
1037
+ /**
1038
+ * How long a restart that ended without a server keeps saying so when no page
1039
+ * has read its report at all: a page that follows it reads it every 300 ms, an
1040
+ * idle one every 3 s just after it loads. After a crash nobody asked for, nobody
1041
+ * usually is (the app's stopped page reads the supervisor record instead).
1042
+ */
1043
+ export const RESTART_REPORT_IDLE_MS = 4000;
1013
1044
 
1014
1045
  /**
1015
1046
  * A restart reports itself the way a launch does, on the start beacon, for as
@@ -1020,7 +1051,8 @@ export const RESTART_REPORT_SETTLE_MS = 3000;
1020
1051
  *
1021
1052
  * phase() — the supervisor's record changed: pass on what it says now
1022
1053
  * failed(error) — it ended without a server; resolves once a page has read
1023
- * why (`lingerMs` at the most), so the caller can exit then
1054
+ * why (`lingerMs` at the most, `idleMs` when no page has
1055
+ * read the report at all), so the caller can exit then
1024
1056
  * close() — over
1025
1057
  *
1026
1058
  * Only what the supervisor writes after the report began goes out: a record
@@ -1035,6 +1067,7 @@ export function openRestartReport({
1035
1067
  now = () => Date.now(),
1036
1068
  settleMs = RESTART_REPORT_SETTLE_MS,
1037
1069
  lingerMs = BEACON_LINGER_MS,
1070
+ idleMs = RESTART_REPORT_IDLE_MS,
1038
1071
  beacon = createStartBeacon,
1039
1072
  } = {}) {
1040
1073
  const startedAt = now();
@@ -1074,7 +1107,7 @@ export function openRestartReport({
1074
1107
  settle = null;
1075
1108
  report.finish('failed', error);
1076
1109
  await listening; // a failure right after it opened: let it be reachable first
1077
- try { await report.linger(lingerMs); } catch {}
1110
+ try { await report.linger(lingerMs, { idleMs }); } catch {}
1078
1111
  close();
1079
1112
  },
1080
1113
  close,
@@ -0,0 +1,45 @@
1
+ import { externalCatalog, selectExternalServers } from '../neural-interface/lib/mcp-external-servers.js';
2
+ // Loaded only by `synabun tools`; setup can still run before MCP dist exists.
3
+ import { readFileSync } from 'node:fs';
4
+ import { resolve } from 'node:path';
5
+ import { toolCatalogView } from '../mcp-server/dist/services/tool-catalog.js';
6
+ import { parseToolList, resolveToolList } from '../mcp-server/dist/services/tool-list.js';
7
+ import { readDefaultTools, requireToolList, writeDefaultTools } from '../neural-interface/lib/tool-lists.js';
8
+
9
+ export function runToolsCommand(args = [], { dataHome, externalContext = {}, log = console.log, error = console.error } = {}) {
10
+ const [action = 'list', ...values] = args;
11
+ try {
12
+ if (action === 'list' && !values.length) {
13
+ const catalog = toolCatalogView();
14
+ log(`Always on: ${catalog.alwaysOnTools.join(', ')} (${catalog.alwaysOn})`);
15
+ for (const group of catalog.groups) log(`${group.id.padEnd(20)} ${String(group.count).padStart(2)} ${group.purpose}`);
16
+ log(`Host effect: ${Object.entries(catalog.effect).map(([host, effect]) => `${host} ${effect}`).join('; ')}.`);
17
+ log('Usage: synabun tools [list | get | set <ids>]. Use comma-separated ids, all, or [] for always-on only.');
18
+ return 0;
19
+ }
20
+ if (action === 'get' && !values.length) {
21
+ let customPresets = null;
22
+ try { customPresets = JSON.parse(readFileSync(resolve(dataHome, 'data/mcp-registry.json'), 'utf8')).profiles; } catch {}
23
+ const tools = readDefaultTools(dataHome, { customPresets });
24
+ const selected = requireToolList(resolveToolList(tools, { source: 'runtime' }));
25
+ log(JSON.stringify({ tools, groups: selected.groups, count: selected.count, effect: toolCatalogView().effect }));
26
+ return 0;
27
+ }
28
+ if (action === 'set' && values.length) {
29
+ const raw = values.join(',');
30
+ const items = requireToolList(parseToolList(raw === '[]' ? [] : raw)).items;
31
+ requireToolList(resolveToolList(items, { source: 'runtime' }));
32
+ if (items.some(item => item.startsWith('mcp:') && item !== 'mcp:*')) {
33
+ const servers = externalCatalog({ dataHome, ...externalContext }).map(row => ({ name: row.ref.slice(4), blocked: row.blocked, deliverable: Object.values(row.providers).includes('deliverable') }));
34
+ selectExternalServers(items, { servers });
35
+ }
36
+ const tools = writeDefaultTools(dataHome, items);
37
+ log(`Default tools: ${JSON.stringify(tools)}. Applies to future runtimes; running sessions keep their own list.`);
38
+ return 0;
39
+ }
40
+ throw new Error('Usage: synabun tools [list | get | set <ids>]');
41
+ } catch (err) {
42
+ error(`${err.code ? `${err.code}: ` : ''}${err.message}`);
43
+ return 1;
44
+ }
45
+ }