synabun 2.0.9 → 2.1.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 (679) hide show
  1. package/CHANGELOG.md +115 -0
  2. package/README.md +25 -2
  3. package/THIRD-PARTY-LICENSES.md +68 -4
  4. package/bin/synabun.js +71 -24
  5. package/docs/whatsapp.md +2 -2
  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/launcher.mjs +9 -3
  10. package/lib/claude-hooks.js +172 -18
  11. package/lib/client-config-repair.js +17 -8
  12. package/lib/desktop-app.js +7 -1
  13. package/lib/external-tools.js +1 -1
  14. package/lib/first-launch-protection.js +57 -12
  15. package/lib/mcp-dist.js +176 -0
  16. package/lib/release-npm-ci.mjs +138 -0
  17. package/lib/release-publish.mjs +91 -0
  18. package/lib/release-scan.mjs +50 -0
  19. package/lib/server-crash-record.js +123 -0
  20. package/lib/server-exit.js +318 -0
  21. package/lib/server-supervisor.js +508 -0
  22. package/lib/start-launcher.js +43 -5
  23. package/lib/tools-cli.js +45 -0
  24. package/lib/update-snapshot.js +228 -0
  25. package/lib/virustotal.mjs +192 -0
  26. package/lib/windows-crash-event.js +269 -0
  27. package/mcp-server/dist/http.d.ts +20 -1
  28. package/mcp-server/dist/http.d.ts.map +1 -1
  29. package/mcp-server/dist/http.js +59 -12
  30. package/mcp-server/dist/http.js.map +1 -1
  31. package/mcp-server/dist/index.d.ts +33 -27
  32. package/mcp-server/dist/index.d.ts.map +1 -1
  33. package/mcp-server/dist/index.js +128 -143
  34. package/mcp-server/dist/index.js.map +1 -1
  35. package/mcp-server/dist/preload.js +1 -1
  36. package/mcp-server/dist/preload.js.map +1 -1
  37. package/mcp-server/dist/services/codex-config-heal.d.ts +6 -2
  38. package/mcp-server/dist/services/codex-config-heal.d.ts.map +1 -1
  39. package/mcp-server/dist/services/codex-config-heal.js +81 -28
  40. package/mcp-server/dist/services/codex-config-heal.js.map +1 -1
  41. package/mcp-server/dist/services/memory-judge-backfill.d.ts +6 -0
  42. package/mcp-server/dist/services/memory-judge-backfill.d.ts.map +1 -1
  43. package/mcp-server/dist/services/memory-judge-backfill.js +7 -1
  44. package/mcp-server/dist/services/memory-judge-backfill.js.map +1 -1
  45. package/mcp-server/dist/services/neural-interface.d.ts +16 -10
  46. package/mcp-server/dist/services/neural-interface.d.ts.map +1 -1
  47. package/mcp-server/dist/services/neural-interface.js +67 -11
  48. package/mcp-server/dist/services/neural-interface.js.map +1 -1
  49. package/mcp-server/dist/services/server-instructions.d.ts +7 -2
  50. package/mcp-server/dist/services/server-instructions.d.ts.map +1 -1
  51. package/mcp-server/dist/services/server-instructions.js +20 -62
  52. package/mcp-server/dist/services/server-instructions.js.map +1 -1
  53. package/mcp-server/dist/services/tool-catalog.d.ts +120 -0
  54. package/mcp-server/dist/services/tool-catalog.d.ts.map +1 -0
  55. package/mcp-server/dist/services/tool-catalog.js +339 -0
  56. package/mcp-server/dist/services/tool-catalog.js.map +1 -0
  57. package/mcp-server/dist/services/tool-list-refresh-barrier.d.ts +12 -0
  58. package/mcp-server/dist/services/tool-list-refresh-barrier.d.ts.map +1 -0
  59. package/mcp-server/dist/services/tool-list-refresh-barrier.js +40 -0
  60. package/mcp-server/dist/services/tool-list-refresh-barrier.js.map +1 -0
  61. package/mcp-server/dist/services/tool-list.d.ts +108 -0
  62. package/mcp-server/dist/services/tool-list.d.ts.map +1 -0
  63. package/mcp-server/dist/services/tool-list.js +334 -0
  64. package/mcp-server/dist/services/tool-list.js.map +1 -0
  65. package/mcp-server/dist/services/tool-runtime.d.ts +161 -0
  66. package/mcp-server/dist/services/tool-runtime.d.ts.map +1 -0
  67. package/mcp-server/dist/services/tool-runtime.js +400 -0
  68. package/mcp-server/dist/services/tool-runtime.js.map +1 -0
  69. package/mcp-server/dist/services/whiteboard-geometry.d.ts +26 -2
  70. package/mcp-server/dist/services/whiteboard-geometry.d.ts.map +1 -1
  71. package/mcp-server/dist/services/whiteboard-geometry.js +91 -8
  72. package/mcp-server/dist/services/whiteboard-geometry.js.map +1 -1
  73. package/mcp-server/dist/tools/agents.d.ts +10 -10
  74. package/mcp-server/dist/tools/agents.d.ts.map +1 -1
  75. package/mcp-server/dist/tools/agents.js +22 -13
  76. package/mcp-server/dist/tools/agents.js.map +1 -1
  77. package/mcp-server/dist/tools/browser-batch.d.ts +3 -3
  78. package/mcp-server/dist/tools/image-tools.d.ts +1 -1
  79. package/mcp-server/dist/tools/image-tools.js +1 -1
  80. package/mcp-server/dist/tools/skill-tools.d.ts +505 -0
  81. package/mcp-server/dist/tools/skill-tools.d.ts.map +1 -0
  82. package/mcp-server/dist/tools/skill-tools.js +639 -0
  83. package/mcp-server/dist/tools/skill-tools.js.map +1 -0
  84. package/mcp-server/dist/tools/skill.d.ts +7 -0
  85. package/mcp-server/dist/tools/skill.d.ts.map +1 -0
  86. package/mcp-server/dist/tools/skill.js +11 -0
  87. package/mcp-server/dist/tools/skill.js.map +1 -0
  88. package/mcp-server/dist/tools/tool-groups.d.ts +68 -0
  89. package/mcp-server/dist/tools/tool-groups.d.ts.map +1 -0
  90. package/mcp-server/dist/tools/tool-groups.js +147 -0
  91. package/mcp-server/dist/tools/tool-groups.js.map +1 -0
  92. package/mcp-server/dist/tools/whiteboard-format.d.ts.map +1 -1
  93. package/mcp-server/dist/tools/whiteboard-format.js +7 -4
  94. package/mcp-server/dist/tools/whiteboard-format.js.map +1 -1
  95. package/mcp-server/dist/tools/whiteboard-tools.d.ts +64 -13
  96. package/mcp-server/dist/tools/whiteboard-tools.d.ts.map +1 -1
  97. package/mcp-server/dist/tools/whiteboard-tools.js +30 -5
  98. package/mcp-server/dist/tools/whiteboard-tools.js.map +1 -1
  99. package/mcp-server/package-lock.json +2 -2
  100. package/mcp-server/package.json +1 -1
  101. package/mcp-server/src/http.ts +75 -12
  102. package/mcp-server/src/index.ts +147 -154
  103. package/mcp-server/src/preload.ts +12 -12
  104. package/mcp-server/src/services/codex-config-heal.ts +80 -29
  105. package/mcp-server/src/services/memory-judge-backfill.ts +7 -1
  106. package/mcp-server/src/services/neural-interface.ts +79 -20
  107. package/mcp-server/src/services/server-instructions.ts +27 -63
  108. package/mcp-server/src/services/tool-catalog.ts +403 -0
  109. package/mcp-server/src/services/tool-list-refresh-barrier.ts +33 -0
  110. package/mcp-server/src/services/tool-list.ts +410 -0
  111. package/mcp-server/src/services/tool-runtime.ts +457 -0
  112. package/mcp-server/src/services/whiteboard-geometry.ts +94 -9
  113. package/mcp-server/src/tools/agents.ts +22 -14
  114. package/mcp-server/src/tools/image-tools.ts +1 -1
  115. package/mcp-server/src/tools/skill-tools.ts +589 -0
  116. package/mcp-server/src/tools/skill.ts +13 -0
  117. package/mcp-server/src/tools/tool-groups.ts +164 -0
  118. package/mcp-server/src/tools/whiteboard-format.ts +9 -3
  119. package/mcp-server/src/tools/whiteboard-tools.ts +39 -5
  120. package/neural-interface/i18n/en.json +3680 -206
  121. package/neural-interface/i18n/pt-BR.json +3628 -157
  122. package/neural-interface/lib/account-usage.js +5 -3
  123. package/neural-interface/lib/app-controls-api.js +161 -0
  124. package/neural-interface/lib/assistant-api.js +148 -35
  125. package/neural-interface/lib/assistant-brains/claude.js +4 -3
  126. package/neural-interface/lib/assistant-brains/codex-browser-policy-hook.mjs +12 -2
  127. package/neural-interface/lib/assistant-brains/codex-route-gate-hook.mjs +32 -9
  128. package/neural-interface/lib/assistant-brains/codex.js +9 -5
  129. package/neural-interface/lib/assistant-brains/opencode.js +122 -44
  130. package/neural-interface/lib/assistant-brains/opencode2-browser-policy/index.js +59 -0
  131. package/neural-interface/lib/assistant-brains/opencode2-route-gate/index.js +97 -0
  132. package/neural-interface/lib/assistant-catalog.js +23 -9
  133. package/neural-interface/lib/assistant-dispatch.js +874 -156
  134. package/neural-interface/lib/assistant-envelope.js +22 -2
  135. package/neural-interface/lib/assistant-persona.js +80 -11
  136. package/neural-interface/lib/assistant-plan-permissions.js +10 -9
  137. package/neural-interface/lib/assistant-route-gate-codex.js +3 -0
  138. package/neural-interface/lib/assistant-route-gate.js +6 -4
  139. package/neural-interface/lib/assistant-router.js +13 -8
  140. package/neural-interface/lib/assistant-runtime.js +183 -26
  141. package/neural-interface/lib/assistant-task-prompt.js +78 -14
  142. package/neural-interface/lib/assistant-tools.js +51 -0
  143. package/neural-interface/lib/backup-service.js +620 -3
  144. package/neural-interface/lib/backup-zip-reader.js +10 -5
  145. package/neural-interface/lib/backup-zip-writer.js +257 -0
  146. package/neural-interface/lib/browser-execution.js +10 -2
  147. package/neural-interface/lib/browser-guard-cdp.js +83 -0
  148. package/neural-interface/lib/browser-network-guard.js +284 -0
  149. package/neural-interface/lib/browser-policy-roots.js +64 -0
  150. package/neural-interface/lib/browser-tool-policy.js +186 -31
  151. package/neural-interface/lib/claude-agent-bridge.js +4 -2
  152. package/neural-interface/lib/claude-hooks-status.js +103 -0
  153. package/neural-interface/lib/claude-model-catalog.js +64 -14
  154. package/neural-interface/lib/claude-temporary.js +5 -11
  155. package/neural-interface/lib/cli-install.js +18 -138
  156. package/neural-interface/lib/cli-resolver.js +4 -1
  157. package/neural-interface/lib/cli-update-planner.js +32 -31
  158. package/neural-interface/lib/cloudflared/tunnel.js +42 -2
  159. package/neural-interface/lib/codex-mcp-approvals.js +11 -0
  160. package/neural-interface/lib/codex-settings-schema.js +109 -47
  161. package/neural-interface/lib/codex-worker-app-server.js +172 -0
  162. package/neural-interface/lib/confined-fs.js +30 -3
  163. package/neural-interface/lib/crash-notice-api.js +98 -0
  164. package/neural-interface/lib/editor-completion-opencode.js +164 -67
  165. package/neural-interface/lib/exec-loop-command.js +4 -1
  166. package/neural-interface/lib/http-guards.js +8 -2
  167. package/neural-interface/lib/install-details.js +4 -1
  168. package/neural-interface/lib/launch-cache.js +293 -0
  169. package/neural-interface/lib/login-start.js +524 -0
  170. package/neural-interface/lib/mcp-agent-servers.js +67 -0
  171. package/neural-interface/lib/mcp-client-config.js +0 -29
  172. package/neural-interface/lib/mcp-external-servers.js +323 -0
  173. package/neural-interface/lib/{mcp-profile-refresh-coordinator.js → mcp-tools-refresh-coordinator.js} +4 -4
  174. package/neural-interface/lib/morelogin.js +30 -0
  175. package/neural-interface/lib/native-loop-providers.js +565 -50
  176. package/neural-interface/lib/native-loop-runtime.js +287 -41
  177. package/neural-interface/lib/opencode-config.js +557 -0
  178. package/neural-interface/lib/opencode-db-schema.js +58 -0
  179. package/neural-interface/lib/opencode-history.js +49 -204
  180. package/neural-interface/lib/opencode-mcp-ready.js +108 -0
  181. package/neural-interface/lib/opencode-native-boundary.js +44 -0
  182. package/neural-interface/lib/opencode-protocol.js +310 -0
  183. package/neural-interface/lib/opencode-provider-view.js +81 -0
  184. package/neural-interface/lib/opencode-runtime-config.js +32 -15
  185. package/neural-interface/lib/opencode-serve-records.js +624 -0
  186. package/neural-interface/lib/opencode-v2-client.js +53 -131
  187. package/neural-interface/lib/opencode-v2-ws-requests.js +35 -195
  188. package/neural-interface/lib/opencode-worker-activity.js +229 -0
  189. package/neural-interface/lib/opencode-worker-data.js +27 -0
  190. package/neural-interface/lib/opencode2-client.js +796 -0
  191. package/neural-interface/lib/opencode2-proxy.js +76 -0
  192. package/neural-interface/lib/opencode2-translate.js +1057 -0
  193. package/neural-interface/lib/owner-authority.js +249 -0
  194. package/neural-interface/lib/page-session.js +275 -0
  195. package/neural-interface/lib/peer-identity.js +763 -0
  196. package/neural-interface/lib/process-identity.js +361 -0
  197. package/neural-interface/lib/request-gate.js +17 -2
  198. package/neural-interface/lib/rulesets/api.js +1 -1
  199. package/neural-interface/lib/rulesets/fs-safe.js +1 -1
  200. package/neural-interface/lib/rulesets/installer.js +92 -27
  201. package/neural-interface/lib/rulesets/json-edit.js +1 -1
  202. package/neural-interface/lib/session-cache-builder.js +18 -10
  203. package/neural-interface/lib/session-title-generator.js +58 -18
  204. package/neural-interface/lib/skills/api-v2.js +495 -0
  205. package/neural-interface/lib/skills/bundle.js +418 -0
  206. package/neural-interface/lib/skills/catalog.js +294 -0
  207. package/neural-interface/lib/skills/creation-api.js +191 -0
  208. package/neural-interface/lib/skills/creation-brief.js +247 -0
  209. package/neural-interface/lib/skills/creation-sources.js +267 -0
  210. package/neural-interface/lib/skills/creation.js +843 -0
  211. package/neural-interface/lib/skills/deploy.js +564 -0
  212. package/neural-interface/lib/skills/digest.js +119 -0
  213. package/neural-interface/lib/skills/discover.js +111 -0
  214. package/neural-interface/lib/skills/draft-confinement.js +214 -0
  215. package/neural-interface/lib/skills/eval-confinement.js +397 -0
  216. package/neural-interface/lib/skills/eval-sandbox.js +280 -0
  217. package/neural-interface/lib/skills/eval-trace.js +119 -0
  218. package/neural-interface/lib/skills/evals-api.js +212 -0
  219. package/neural-interface/lib/skills/evals.js +1573 -0
  220. package/neural-interface/lib/skills/export.js +200 -0
  221. package/neural-interface/lib/skills/frontmatter.js +64 -0
  222. package/neural-interface/lib/skills/health.js +75 -0
  223. package/neural-interface/lib/skills/json-edit.js +165 -0
  224. package/neural-interface/lib/skills/known-bridge-digests.json +387 -0
  225. package/neural-interface/lib/skills/known-bridges.js +96 -0
  226. package/neural-interface/lib/skills/library.js +823 -0
  227. package/neural-interface/lib/skills/local-store.js +21 -0
  228. package/neural-interface/lib/skills/migration.js +433 -0
  229. package/neural-interface/lib/skills/operations.js +45 -0
  230. package/neural-interface/lib/skills/ownership.js +427 -0
  231. package/neural-interface/lib/skills/packages.js +88 -0
  232. package/neural-interface/lib/skills/reviews.js +212 -0
  233. package/neural-interface/lib/skills/revision.js +80 -0
  234. package/neural-interface/lib/skills/roots.js +262 -0
  235. package/neural-interface/lib/skills/runtime-checks.js +355 -0
  236. package/neural-interface/lib/skills/scan-assets.js +252 -0
  237. package/neural-interface/lib/skills/scan-context.js +26 -0
  238. package/neural-interface/lib/skills/scan-shell.js +324 -0
  239. package/neural-interface/lib/skills/scan.js +364 -0
  240. package/neural-interface/lib/skills/scanners.js +54 -0
  241. package/neural-interface/lib/skills/service.js +522 -0
  242. package/neural-interface/lib/skills/telemetry.js +113 -0
  243. package/neural-interface/lib/sqlite-integrity-worker.js +22 -0
  244. package/neural-interface/lib/style-guide/api.js +4 -3
  245. package/neural-interface/lib/tool-lists.js +124 -0
  246. package/neural-interface/lib/untrusted-file-headers.js +27 -0
  247. package/neural-interface/lib/whatsapp/bridge.js +6 -3
  248. package/neural-interface/lib/worker-confinement.js +1192 -0
  249. package/neural-interface/lib/worker-guard-setting.js +56 -0
  250. package/neural-interface/lib/worker-mcp-channel.js +175 -0
  251. package/neural-interface/lib/worker-relay-ports.js +227 -0
  252. package/neural-interface/lib/worker-resume.js +229 -0
  253. package/neural-interface/lib/worker-sandbox.js +299 -0
  254. package/neural-interface/package-lock.json +1354 -50
  255. package/neural-interface/package.json +6 -2
  256. package/neural-interface/public/index.html +5 -3
  257. package/neural-interface/public/index2d.html +19 -11
  258. package/neural-interface/public/onboarding.html +2 -2
  259. package/neural-interface/public/shared/account-menu.js +8 -5
  260. package/neural-interface/public/shared/agent-runtime-options.js +76 -16
  261. package/neural-interface/public/shared/api.js +1527 -1336
  262. package/neural-interface/public/shared/app-frame.js +192 -0
  263. package/neural-interface/public/shared/assistant/asst-agents-dock.js +205 -19
  264. package/neural-interface/public/shared/assistant/asst-api.js +11 -3
  265. package/neural-interface/public/shared/assistant/asst-ask-styles.js +270 -0
  266. package/neural-interface/public/shared/assistant/asst-ask-tail.js +118 -0
  267. package/neural-interface/public/shared/assistant/asst-ask.js +397 -0
  268. package/neural-interface/public/shared/assistant/asst-brain-picker.js +241 -112
  269. package/neural-interface/public/shared/assistant/asst-composer.js +1 -1
  270. package/neural-interface/public/shared/assistant/asst-control.js +157 -26
  271. package/neural-interface/public/shared/assistant/asst-followups-card.js +383 -0
  272. package/neural-interface/public/shared/assistant/asst-followups.js +417 -0
  273. package/neural-interface/public/shared/assistant/asst-hosts.js +49 -2
  274. package/neural-interface/public/shared/assistant/asst-limit-dock.js +40 -0
  275. package/neural-interface/public/shared/assistant/asst-limit-wait.js +294 -0
  276. package/neural-interface/public/shared/assistant/asst-limit.js +168 -0
  277. package/neural-interface/public/shared/assistant/asst-mascot.js +49 -12
  278. package/neural-interface/public/shared/assistant/asst-menu.js +84 -12
  279. package/neural-interface/public/shared/assistant/asst-panel.js +303 -44
  280. package/neural-interface/public/shared/assistant/asst-rack-styles.js +93 -58
  281. package/neural-interface/public/shared/assistant/asst-redact.js +186 -0
  282. package/neural-interface/public/shared/assistant/asst-render.js +770 -100
  283. package/neural-interface/public/shared/assistant/asst-route.js +83 -12
  284. package/neural-interface/public/shared/assistant/asst-run-stage.js +231 -0
  285. package/neural-interface/public/shared/assistant/asst-sanitize.js +193 -3
  286. package/neural-interface/public/shared/assistant/asst-scene-styles.js +273 -0
  287. package/neural-interface/public/shared/assistant/asst-scenes.js +530 -0
  288. package/neural-interface/public/shared/assistant/asst-sidepanel-styles.js +3 -15
  289. package/neural-interface/public/shared/assistant/asst-sidepanel.js +67 -4
  290. package/neural-interface/public/shared/assistant/asst-slash.js +9 -7
  291. package/neural-interface/public/shared/assistant/asst-state.js +25 -15
  292. package/neural-interface/public/shared/assistant/asst-stop.js +127 -0
  293. package/neural-interface/public/shared/assistant/asst-styles.js +102 -21
  294. package/neural-interface/public/shared/assistant/asst-tool-kinds.js +165 -13
  295. package/neural-interface/public/shared/automation-context.js +5 -2
  296. package/neural-interface/public/shared/cc-dropdown.js +550 -0
  297. package/neural-interface/public/shared/cdx/cdx-context-menu.js +10 -9
  298. package/neural-interface/public/shared/cdx/cdx-copy.js +16 -0
  299. package/neural-interface/public/shared/cdx/cdx-output-model.js +22 -1
  300. package/neural-interface/public/shared/cdx/cdx-output.js +8 -7
  301. package/neural-interface/public/shared/cdx/cdx-panel.js +58 -150
  302. package/neural-interface/public/shared/cdx/cdx-protocol.js +28 -1
  303. package/neural-interface/public/shared/cdx/cdx-render.js +62 -32
  304. package/neural-interface/public/shared/cdx/cdx-requests.js +3 -3
  305. package/neural-interface/public/shared/cdx/cdx-skill-hints.js +54 -0
  306. package/neural-interface/public/shared/cdx/cdx-styles.js +15 -3
  307. package/neural-interface/public/shared/cdx/cdx-tabs.js +201 -74
  308. package/neural-interface/public/shared/cdx/cdx-ws.js +0 -1
  309. package/neural-interface/public/shared/cli-install-view.js +4 -8
  310. package/neural-interface/public/shared/cli-status.js +2 -2
  311. package/neural-interface/public/shared/constants.js +2 -2
  312. package/neural-interface/public/shared/context-menu-areas.js +34 -0
  313. package/neural-interface/public/shared/context-menu-logic.js +243 -0
  314. package/neural-interface/public/shared/context-menu-model.js +366 -0
  315. package/neural-interface/public/shared/context-menu.js +372 -0
  316. package/neural-interface/public/shared/cp/cp-context-menu.js +10 -9
  317. package/neural-interface/public/shared/cp/cp-markdown.js +4 -57
  318. package/neural-interface/public/shared/crash-notice-view.js +168 -0
  319. package/neural-interface/public/shared/data-cache.js +223 -0
  320. package/neural-interface/public/shared/desktop-bridge.js +11 -0
  321. package/neural-interface/public/shared/desktop-titlebar-view.js +78 -1
  322. package/neural-interface/public/shared/desktop-titlebar.js +214 -28
  323. package/neural-interface/public/shared/desktop-update-view.js +35 -5
  324. package/neural-interface/public/shared/editor/editor-panel-logic.js +2 -2
  325. package/neural-interface/public/shared/file-links.js +73 -8
  326. package/neural-interface/public/shared/html-shell.js +78 -86
  327. package/neural-interface/public/shared/i18n.js +2 -0
  328. package/neural-interface/public/shared/launch-prefetch.js +59 -0
  329. package/neural-interface/public/shared/ocp-v2/ocp-v2-approvals.js +7 -7
  330. package/neural-interface/public/shared/ocp-v2/ocp-v2-binding.js +4 -4
  331. package/neural-interface/public/shared/ocp-v2/ocp-v2-caps.js +3 -2
  332. package/neural-interface/public/shared/ocp-v2/ocp-v2-changes-logic.js +3 -101
  333. package/neural-interface/public/shared/ocp-v2/ocp-v2-childpanel.js +9 -2
  334. package/neural-interface/public/shared/ocp-v2/ocp-v2-compact-button.js +1 -3
  335. package/neural-interface/public/shared/ocp-v2/ocp-v2-composer-logic.js +2 -6
  336. package/neural-interface/public/shared/ocp-v2/ocp-v2-context-menu.js +11 -11
  337. package/neural-interface/public/shared/ocp-v2/ocp-v2-context-model.js +0 -3
  338. package/neural-interface/public/shared/ocp-v2/ocp-v2-events.js +8 -4
  339. package/neural-interface/public/shared/ocp-v2/ocp-v2-manager.js +7 -7
  340. package/neural-interface/public/shared/ocp-v2/ocp-v2-modelpicker.js +54 -17
  341. package/neural-interface/public/shared/ocp-v2/ocp-v2-outcome-notifications.js +16 -0
  342. package/neural-interface/public/shared/ocp-v2/ocp-v2-panel.js +140 -254
  343. package/neural-interface/public/shared/ocp-v2/ocp-v2-plan.js +1 -2
  344. package/neural-interface/public/shared/ocp-v2/ocp-v2-projectbar.js +97 -203
  345. package/neural-interface/public/shared/ocp-v2/ocp-v2-providers.js +11 -9
  346. package/neural-interface/public/shared/ocp-v2/ocp-v2-rehydrate.js +15 -4
  347. package/neural-interface/public/shared/ocp-v2/ocp-v2-render.js +7 -22
  348. package/neural-interface/public/shared/ocp-v2/ocp-v2-send-logic.js +401 -0
  349. package/neural-interface/public/shared/ocp-v2/ocp-v2-send.js +20 -13
  350. package/neural-interface/public/shared/ocp-v2/ocp-v2-session-actions.js +8 -61
  351. package/neural-interface/public/shared/ocp-v2/ocp-v2-sessions-logic.js +53 -82
  352. package/neural-interface/public/shared/ocp-v2/ocp-v2-slash-hints.js +16 -15
  353. package/neural-interface/public/shared/ocp-v2/ocp-v2-state.js +8 -21
  354. package/neural-interface/public/shared/ocp-v2/ocp-v2-status-logic.js +1 -99
  355. package/neural-interface/public/shared/ocp-v2/ocp-v2-status.js +5 -35
  356. package/neural-interface/public/shared/ocp-v2/ocp-v2-styles.js +48 -40
  357. package/neural-interface/public/shared/ocp-v2/{ocp-v2-profile-inheritance.js → ocp-v2-tools-inheritance.js} +16 -16
  358. package/neural-interface/public/shared/ocp-v2/ocp-v2-ws.js +205 -62
  359. package/neural-interface/public/shared/opencode-providers.js +39 -0
  360. package/neural-interface/public/shared/page-session.js +96 -0
  361. package/neural-interface/public/shared/registry.js +5 -3
  362. package/neural-interface/public/shared/retired-2d.js +3 -0
  363. package/neural-interface/public/shared/schedules-view.js +452 -0
  364. package/neural-interface/public/shared/settings/settings-app-pane.js +335 -0
  365. package/neural-interface/public/shared/settings/settings-app.js +243 -0
  366. package/neural-interface/public/shared/settings/settings-claude-hooks.js +135 -0
  367. package/neural-interface/public/shared/settings/settings-data.js +90 -0
  368. package/neural-interface/public/shared/settings/settings-ia.js +23 -6
  369. package/neural-interface/public/shared/settings/settings-opencode.js +89 -0
  370. package/neural-interface/public/shared/settings/settings-skill-folders-pane.js +265 -0
  371. package/neural-interface/public/shared/settings/settings-skill-folders.js +130 -0
  372. package/neural-interface/public/shared/settings/settings-worker-guard.js +72 -0
  373. package/neural-interface/public/shared/sidepanel-session-status.js +49 -0
  374. package/neural-interface/public/shared/skills/ss-args.js +96 -0
  375. package/neural-interface/public/shared/skills/ss-creation.js +219 -0
  376. package/neural-interface/public/shared/skills/ss-dialog.js +130 -0
  377. package/neural-interface/public/shared/skills/ss-doc.js +173 -0
  378. package/neural-interface/public/shared/skills/ss-eval-adapters.js +269 -0
  379. package/neural-interface/public/shared/skills/ss-eval-grade.js +359 -0
  380. package/neural-interface/public/shared/skills/ss-eval-optimise.js +153 -0
  381. package/neural-interface/public/shared/skills/ss-eval-plan.js +315 -0
  382. package/neural-interface/public/shared/skills/ss-evals.js +417 -0
  383. package/neural-interface/public/shared/skills/ss-form.js +159 -0
  384. package/neural-interface/public/shared/skills/ss-frontmatter.js +376 -0
  385. package/neural-interface/public/shared/skills/ss-ids.js +32 -0
  386. package/neural-interface/public/shared/skills/ss-lint.js +141 -0
  387. package/neural-interface/public/shared/skills/ss-presets.js +117 -0
  388. package/neural-interface/public/shared/skills/ss-profiles.js +475 -0
  389. package/neural-interface/public/shared/skills/ss-resources.js +165 -0
  390. package/neural-interface/public/shared/skills/ss-review-view.js +190 -0
  391. package/neural-interface/public/shared/skills/ss-tabs.js +79 -0
  392. package/neural-interface/public/shared/skills/ss-unicode.js +135 -0
  393. package/neural-interface/public/shared/skills/studio/sx-actions.js +222 -0
  394. package/neural-interface/public/shared/skills/studio/sx-cm.js +101 -0
  395. package/neural-interface/public/shared/skills/studio/sx-create-ai-view.js +749 -0
  396. package/neural-interface/public/shared/skills/studio/sx-create-ai.js +236 -0
  397. package/neural-interface/public/shared/skills/studio/sx-create-view.js +254 -0
  398. package/neural-interface/public/shared/skills/studio/sx-create.js +126 -0
  399. package/neural-interface/public/shared/skills/studio/sx-diff-view.js +43 -0
  400. package/neural-interface/public/shared/skills/studio/sx-diff.js +133 -0
  401. package/neural-interface/public/shared/skills/studio/sx-docs.js +480 -0
  402. package/neural-interface/public/shared/skills/studio/sx-dom.js +229 -0
  403. package/neural-interface/public/shared/skills/studio/sx-edit-view.js +848 -0
  404. package/neural-interface/public/shared/skills/studio/sx-editor-model.js +193 -0
  405. package/neural-interface/public/shared/skills/studio/sx-errors.js +22 -0
  406. package/neural-interface/public/shared/skills/studio/sx-geometry.js +82 -0
  407. package/neural-interface/public/shared/skills/studio/sx-import.js +262 -0
  408. package/neural-interface/public/shared/skills/studio/sx-inspector.js +308 -0
  409. package/neural-interface/public/shared/skills/studio/sx-install-flow.js +279 -0
  410. package/neural-interface/public/shared/skills/studio/sx-install.js +296 -0
  411. package/neural-interface/public/shared/skills/studio/sx-library-view.js +639 -0
  412. package/neural-interface/public/shared/skills/studio/sx-library.js +475 -0
  413. package/neural-interface/public/shared/skills/studio/sx-main.js +681 -0
  414. package/neural-interface/public/shared/skills/studio/sx-operations-view.js +98 -0
  415. package/neural-interface/public/shared/skills/studio/sx-operations.js +11 -0
  416. package/neural-interface/public/shared/skills/studio/sx-plan.js +158 -0
  417. package/neural-interface/public/shared/skills/studio/sx-publish-view.js +621 -0
  418. package/neural-interface/public/shared/skills/studio/sx-review-view.js +369 -0
  419. package/neural-interface/public/shared/skills/studio/sx-review.js +148 -0
  420. package/neural-interface/public/shared/skills/studio/sx-scan.js +35 -0
  421. package/neural-interface/public/shared/skills/studio/sx-snippets.js +96 -0
  422. package/neural-interface/public/shared/skills/studio/sx-subject.js +248 -0
  423. package/neural-interface/public/shared/skills/studio/sx-test-view.js +994 -0
  424. package/neural-interface/public/shared/skills/studio/sx-test.js +328 -0
  425. package/neural-interface/public/shared/skills/studio/sx-versions-view.js +241 -0
  426. package/neural-interface/public/shared/skills/studio/sx-workspace-view.js +425 -0
  427. package/neural-interface/public/shared/styleguide/sg-panel.js +4 -2
  428. package/neural-interface/public/shared/styles.css +2065 -2150
  429. package/neural-interface/public/shared/synabun-mascot.js +1115 -9
  430. package/neural-interface/public/shared/tool-list-picker.js +55 -0
  431. package/neural-interface/public/shared/tool-list-storage.js +24 -0
  432. package/neural-interface/public/shared/tool-list-view.js +112 -0
  433. package/neural-interface/public/shared/tour/tour-actor.js +268 -0
  434. package/neural-interface/public/shared/tour/tour-beats.js +336 -0
  435. package/neural-interface/public/shared/tour/tour-engine.js +884 -0
  436. package/neural-interface/public/shared/tour/tour-ink.js +390 -0
  437. package/neural-interface/public/shared/tour/tour-stage.js +440 -0
  438. package/neural-interface/public/shared/tour/tour-state.js +40 -0
  439. package/neural-interface/public/shared/tour/tour-tips.js +99 -0
  440. package/neural-interface/public/shared/tour/tour.css +144 -0
  441. package/neural-interface/public/shared/ui-assistant.js +27 -5
  442. package/neural-interface/public/shared/ui-automation-studio.js +155 -152
  443. package/neural-interface/public/shared/ui-bookmarks.js +2 -1
  444. package/neural-interface/public/shared/ui-claude-panel.js +153 -13
  445. package/neural-interface/public/shared/ui-codex-panel.js +1 -0
  446. package/neural-interface/public/shared/ui-command-runner.js +6 -3
  447. package/neural-interface/public/shared/ui-context-menu.js +295 -0
  448. package/neural-interface/public/shared/ui-cost-widget.js +57 -59
  449. package/neural-interface/public/shared/ui-crash-notice.js +187 -0
  450. package/neural-interface/public/shared/ui-detail.js +21 -9
  451. package/neural-interface/public/shared/ui-file-links.js +1 -0
  452. package/neural-interface/public/shared/ui-image-gallery.js +56 -52
  453. package/neural-interface/public/shared/ui-invite.js +18 -2
  454. package/neural-interface/public/shared/ui-keybinds.js +26 -1
  455. package/neural-interface/public/shared/ui-link.js +9 -9
  456. package/neural-interface/public/shared/ui-menubar.js +229 -40
  457. package/neural-interface/public/shared/ui-native-loop-router-state.js +5 -0
  458. package/neural-interface/public/shared/ui-native-loop-router.js +9 -1
  459. package/neural-interface/public/shared/ui-navbar.js +4 -24
  460. package/neural-interface/public/shared/ui-opencode-panel-v2.js +1 -0
  461. package/neural-interface/public/shared/ui-panels.js +96 -65
  462. package/neural-interface/public/shared/ui-schedules-studio.js +2267 -953
  463. package/neural-interface/public/shared/ui-sessions.js +20 -7
  464. package/neural-interface/public/shared/ui-settings.js +679 -496
  465. package/neural-interface/public/shared/ui-sidebar.js +4 -4
  466. package/neural-interface/public/shared/ui-sidepanel-session-entry.js +13 -0
  467. package/neural-interface/public/shared/ui-sidepanel-sessions.js +56 -5
  468. package/neural-interface/public/shared/ui-sidepanel-state.js +23 -12
  469. package/neural-interface/public/shared/ui-sidepanel-tray.js +84 -8
  470. package/neural-interface/public/shared/ui-sidepanel-windows.js +16 -11
  471. package/neural-interface/public/shared/ui-skills.js +42 -2781
  472. package/neural-interface/public/shared/ui-styleguide.js +5 -2
  473. package/neural-interface/public/shared/ui-sync.js +22 -5
  474. package/neural-interface/public/shared/ui-terminal.js +159 -199
  475. package/neural-interface/public/shared/ui-tool-updates.js +3 -1
  476. package/neural-interface/public/shared/ui-tooltip.js +10 -5
  477. package/neural-interface/public/shared/ui-trash.js +14 -5
  478. package/neural-interface/public/shared/ui-tutorial-layout.js +3 -117
  479. package/neural-interface/public/shared/ui-tutorial-preview.js +103 -0
  480. package/neural-interface/public/shared/ui-tutorial-steps.js +3 -61
  481. package/neural-interface/public/shared/ui-tutorial.css +47 -100
  482. package/neural-interface/public/shared/ui-tutorial.js +285 -499
  483. package/neural-interface/public/shared/ui-update.js +121 -16
  484. package/neural-interface/public/shared/ui-whiteboard.js +313 -59
  485. package/neural-interface/public/shared/ui-worker-end.js +4 -0
  486. package/neural-interface/public/shared/ui-workspaces.js +3 -2
  487. package/neural-interface/public/shared/view-variant.js +66 -0
  488. package/neural-interface/public/shared/whatsapp/wa-view.js +1 -1
  489. package/neural-interface/public/shared/whiteboard-list.js +172 -0
  490. package/neural-interface/public/shared/worker-end.js +10 -3
  491. package/neural-interface/public/shared/worker-restart.js +12 -0
  492. package/neural-interface/public/sidepanel-session.html +1 -1
  493. package/neural-interface/public/variant/2d/background.js +2 -1
  494. package/neural-interface/public/variant/2d/context-menu.js +8 -2
  495. package/neural-interface/public/variant/2d/graph.js +42 -20
  496. package/neural-interface/public/variant/2d/hulls.js +14 -9
  497. package/neural-interface/public/variant/2d/lasso.js +11 -7
  498. package/neural-interface/public/variant/2d/main.js +36 -2
  499. package/neural-interface/public/variant/2d/minimap.js +11 -13
  500. package/neural-interface/public/variant/3d/main.js +72 -6
  501. package/neural-interface/public/variant/3d/map-controls.js +24 -9
  502. package/neural-interface/public/vendor/caveat/OFL.txt +93 -0
  503. package/neural-interface/public/vendor/caveat/caveat-latin-400-normal.woff2 +0 -0
  504. package/neural-interface/public/vendor/caveat/caveat-latin-700-normal.woff2 +0 -0
  505. package/neural-interface/public/vendor/caveat/caveat.css +17 -0
  506. package/neural-interface/public/vendor/caveat/manifest.json +12 -0
  507. package/neural-interface/public/vendor/highlight/LICENSE +29 -0
  508. package/neural-interface/public/vendor/highlight/github-dark-dimmed-11.12.0.min.css +9 -0
  509. package/neural-interface/public/vendor/highlight/highlight-11.12.0.min.js +1262 -0
  510. package/neural-interface/public/vendor/highlight/manifest.json +5 -0
  511. package/neural-interface/public/vendor/html2canvas/LICENSE +22 -0
  512. package/neural-interface/public/vendor/html2canvas/html2canvas-1.4.1.min.js +20 -0
  513. package/neural-interface/public/vendor/html2canvas/manifest.json +4 -0
  514. package/neural-interface/public/vendor/marked/LICENSE.md +44 -0
  515. package/neural-interface/public/vendor/marked/manifest.json +4 -0
  516. package/neural-interface/public/vendor/marked/marked-14.1.4.esm.js +2502 -0
  517. package/neural-interface/public/vendor/mermaid/LICENSE +21 -0
  518. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/abnfDiagram-PAR5IXJI.mjs +1 -0
  519. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/architecture-7GRP2DOG-M7G3SWCO.mjs +1 -0
  520. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/architectureDiagram-2PNWDENW.mjs +36 -0
  521. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/blockDiagram-PUWGUBOA.mjs +129 -0
  522. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/c4Diagram-3NLNHZP5.mjs +38 -0
  523. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-2NMSUPJH.mjs +1 -0
  524. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-32MCUW2P.mjs +206 -0
  525. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-3NPNU5GP.mjs +1 -0
  526. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-3SMGFRGC.mjs +1 -0
  527. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-4CAJECRU.mjs +14 -0
  528. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-4OADUZ5Q.mjs +10 -0
  529. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-5IMINLNL.mjs +1 -0
  530. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-5IQCK5RY.mjs +3 -0
  531. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-5MVFYWFU.mjs +1 -0
  532. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-AQ6EADP3.mjs +1 -0
  533. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-AZZRMDJM.mjs +15 -0
  534. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-CLS4B6BI.mjs +168 -0
  535. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-DZP67EKU.mjs +70 -0
  536. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-EICP7K5X.mjs +1 -0
  537. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-GDY2UAB4.mjs +1 -0
  538. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-H7VHZCWX.mjs +1 -0
  539. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-IJBDOHL6.mjs +1 -0
  540. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-JFFVMJIA.mjs +1 -0
  541. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-JQRUD6KW.mjs +1 -0
  542. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-JZGDI5JK.mjs +1 -0
  543. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-KD6SPIPN.mjs +2 -0
  544. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-NF366YI6.mjs +231 -0
  545. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-NUDPKBEF.mjs +32 -0
  546. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-OJINMHTS.mjs +1 -0
  547. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-Q6NGPIAH.mjs +2 -0
  548. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-QJSWEUOL.mjs +132 -0
  549. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-R7JDMUKV.mjs +1 -0
  550. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-RCXAM2GK.mjs +1 -0
  551. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-RT4MAWR3.mjs +1 -0
  552. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-SFMINX4W.mjs +1 -0
  553. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-SLDQ463B.mjs +88 -0
  554. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-STOV2HOB.mjs +1 -0
  555. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-T5OCTHI4.mjs +1 -0
  556. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-TCEHX7R3.mjs +1 -0
  557. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-VQC2EELJ.mjs +1 -0
  558. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-VRZKL3EH.mjs +1 -0
  559. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-VTHDQ7BV.mjs +1 -0
  560. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-VY2OSTJR.mjs +2 -0
  561. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-WUBWJARI.mjs +1 -0
  562. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-YBMQ5JGJ.mjs +2 -0
  563. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-YDMLL4PJ.mjs +1 -0
  564. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-Z6V44ARX.mjs +62 -0
  565. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-ZJJ7VQJP.mjs +339 -0
  566. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-ZNLK7HRR.mjs +1 -0
  567. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-ZQN7UHIL.mjs +1 -0
  568. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-ZS6VBONO.mjs +174 -0
  569. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-ZUNWM646.mjs +1 -0
  570. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/classDiagram-FGAMPOII.mjs +1 -0
  571. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/classDiagram-v2-AI5RJEK4.mjs +1 -0
  572. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/cose-bilkent-Q72C2OJ3.mjs +1 -0
  573. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/cynefin-OW5HDTMX-6OCTWOGG.mjs +1 -0
  574. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/cynefinDiagram-2OOWUY4M.mjs +62 -0
  575. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/dagre-MPVFI544.mjs +4 -0
  576. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/diagram-AE4NWXFW.mjs +24 -0
  577. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/diagram-BUJID5UG.mjs +41 -0
  578. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/diagram-C44CZ5NI.mjs +24 -0
  579. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/diagram-EYOLEXXS.mjs +30 -0
  580. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/diagram-P7B6PWOJ.mjs +3 -0
  581. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/ebnfDiagram-FTPZ3PV6.mjs +1 -0
  582. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/erDiagram-H3ERR26V.mjs +99 -0
  583. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/eventmodeling-NTZA5JFV-WKSWQHFS.mjs +1 -0
  584. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/flowDiagram-YHGXBVSY.mjs +1 -0
  585. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/ganttDiagram-RWFTB7ED.mjs +292 -0
  586. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/gitGraph-4MIJSDKK-JH2HKYUO.mjs +1 -0
  587. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/gitGraphDiagram-MAR6GKOP.mjs +106 -0
  588. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/info-A6RAGUB7-3TX3OFWW.mjs +1 -0
  589. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/infoDiagram-LV547AFH.mjs +2 -0
  590. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/ishikawaDiagram-PUJQGNDS.mjs +70 -0
  591. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/journeyDiagram-EW5QJT4W.mjs +139 -0
  592. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/kanban-definition-JSQERAZB.mjs +89 -0
  593. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/katex-CZ4GXH2S.mjs +257 -0
  594. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/mindmap-definition-VBJCLQLM.mjs +96 -0
  595. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/packet-AYTQ26CC-MKII6P3Y.mjs +1 -0
  596. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/pegDiagram-7WILOQTJ.mjs +1 -0
  597. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/pie-WAS4IAKB-6B4OPMJ5.mjs +1 -0
  598. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/pieDiagram-EFA7CBSB.mjs +39 -0
  599. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/quadrantDiagram-WARVYA7P.mjs +7 -0
  600. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/radar-RG4KPBEZ-UPTR3XUL.mjs +1 -0
  601. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/railroad-74A4TZTK-MEFUPFMS.mjs +1 -0
  602. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/railroad-abnf-HS5TGJTU-R5YG2S5D.mjs +1 -0
  603. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/railroad-ebnf-LZEXJU2U-G5QEX2L6.mjs +1 -0
  604. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/railroad-peg-WCYAUIDC-H5I2ZRY2.mjs +1 -0
  605. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/railroadDiagram-5NIQVNLQ.mjs +1 -0
  606. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/requirementDiagram-SXREGOKZ.mjs +84 -0
  607. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/sankeyDiagram-VV74M2VP.mjs +40 -0
  608. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/sequenceDiagram-52ZFIFDL.mjs +162 -0
  609. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/sizeCapture-FGA5AGYP.mjs +1 -0
  610. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/stateDiagram-3QAKPIVI.mjs +1 -0
  611. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/stateDiagram-v2-HN2P4RDZ.mjs +1 -0
  612. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/swimlanes-QLVP42QD.mjs +1 -0
  613. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/swimlanesDiagram-JKPDBFCN.mjs +8 -0
  614. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/timeline-definition-GS45XAXW.mjs +120 -0
  615. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/treeView-Q6P3EWNA-ZRJGANNQ.mjs +1 -0
  616. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/treemap-WGGIJYW6-K64VN5AS.mjs +1 -0
  617. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/vennDiagram-QIQU2ZRJ.mjs +34 -0
  618. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/wardley-WFR3VGLG-D67OWCCH.mjs +1 -0
  619. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/wardleyDiagram-U6YNM66N.mjs +78 -0
  620. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/xychartDiagram-XJAZEDNM.mjs +7 -0
  621. package/neural-interface/public/vendor/mermaid/manifest.json +4 -0
  622. package/neural-interface/public/vendor/mermaid/mermaid-11.17.2.esm.min.mjs +14 -0
  623. package/neural-interface/public/vendor/yaml/LICENSE +13 -0
  624. package/neural-interface/public/vendor/yaml/manifest.json +4 -0
  625. package/neural-interface/public/vendor/yaml/yaml-2.9.1.min.js +137 -0
  626. package/neural-interface/scripts/build-vendor.mjs +116 -3
  627. package/neural-interface/server.js +4834 -2541
  628. package/neural-interface/templates/rulesets/core.md +1 -1
  629. package/neural-interface/templates/rulesets/legacy-hashes.json +18 -0
  630. package/neural-interface/templates/rulesets/manifest.json +8 -8
  631. package/npm-shrinkwrap.json +18 -12
  632. package/package.json +16 -3
  633. package/postinstall.js +9 -4
  634. package/preuninstall.js +1 -1
  635. package/setup.js +97 -322
  636. package/skills/skill-forge/SKILL.md +177 -0
  637. package/skills/skill-forge/evals/evals.json +54 -0
  638. package/skills/skill-forge/references/package.md +105 -0
  639. package/skills/skill-forge/references/review-and-recovery.md +63 -0
  640. package/skills/skill-forge/references/runtime-profiles.md +20 -0
  641. package/skills/skill-forge/references/sources-and-capabilities.md +51 -0
  642. package/skills/synabun/SKILL.md +10 -7
  643. package/skills/synabun/modules/audit.md +1 -5
  644. package/skills/synabun/modules/changelog.md +1 -5
  645. package/skills/synabun/modules/idea.md +1 -1
  646. package/skills/synabun/modules/memorize.md +6 -9
  647. package/skills/synabun/modules/schedule.md +5 -5
  648. package/mcp-server/dist/services/profiles.d.ts +0 -120
  649. package/mcp-server/dist/services/profiles.d.ts.map +0 -1
  650. package/mcp-server/dist/services/profiles.js +0 -387
  651. package/mcp-server/dist/services/profiles.js.map +0 -1
  652. package/mcp-server/dist/tools/leonardo-browser-tools.d.ts +0 -97
  653. package/mcp-server/dist/tools/leonardo-browser-tools.d.ts.map +0 -1
  654. package/mcp-server/dist/tools/leonardo-browser-tools.js +0 -228
  655. package/mcp-server/dist/tools/leonardo-browser-tools.js.map +0 -1
  656. package/mcp-server/dist/tools/leonardo.d.ts +0 -13
  657. package/mcp-server/dist/tools/leonardo.d.ts.map +0 -1
  658. package/mcp-server/dist/tools/leonardo.js +0 -21
  659. package/mcp-server/dist/tools/leonardo.js.map +0 -1
  660. package/mcp-server/dist/tools/profile.d.ts +0 -17
  661. package/mcp-server/dist/tools/profile.d.ts.map +0 -1
  662. package/mcp-server/dist/tools/profile.js +0 -114
  663. package/mcp-server/dist/tools/profile.js.map +0 -1
  664. package/mcp-server/src/services/profiles.ts +0 -428
  665. package/mcp-server/src/tools/leonardo-browser-tools.ts +0 -291
  666. package/mcp-server/src/tools/leonardo.ts +0 -30
  667. package/mcp-server/src/tools/profile.ts +0 -122
  668. package/neural-interface/lib/assistant-brains/opencode-browser-policy.js +0 -47
  669. package/neural-interface/lib/assistant-brains/opencode-route-gate.js +0 -62
  670. package/neural-interface/lib/mcp-profile-servers.js +0 -120
  671. package/neural-interface/public/shared/ui-tutorial-draw.js +0 -311
  672. package/skills/leonardo/SKILL.md +0 -168
  673. package/skills/leonardo/icon.svg +0 -1
  674. package/skills/leonardo/modules/browser-guide.md +0 -378
  675. package/skills/leonardo/modules/image-prompter.md +0 -283
  676. package/skills/leonardo/modules/model-advisor.md +0 -100
  677. package/skills/leonardo/modules/prompt-library.md +0 -110
  678. package/skills/leonardo/modules/style-guide.md +0 -186
  679. package/skills/leonardo/modules/video-prompter.md +0 -317
@@ -91,11 +91,13 @@ export function createAccountUsage({ accounts = {}, readCodex = null, fetchImpl
91
91
  combined.fetchedAt = new Date(Math.max(...fresh.map(w => w.at), value.windows.length ? Date.parse(value.fetchedAt) : 0)).toISOString();
92
92
  return combined;
93
93
  }
94
- function peek(provider, id) {
94
+ // `stale`: a reading older than ttlMs still counts (a display that refreshes in the background); the
95
+ // account's own state must still match it, and a window past its reset is still unknown.
96
+ function peek(provider, id, { stale = false } = {}) {
95
97
  const row = rowOf(provider, id);
96
98
  if (row?.mode === 'apiKey' || row?.mode === 'api') return unknown('not_applicable', now());
97
99
  const item = cache.get(keyOf(provider, id));
98
- const value = item?.signature === signature(row) && now() - item.at < ttlMs ? summarize(item.value.windows, item.value.source, Date.parse(item.value.fetchedAt), item.value.limited && !item.value.windows.some(w => w.usedPercent >= 100)) : unknown('unknown', now());
100
+ const value = item?.signature === signature(row) && (stale || now() - item.at < ttlMs) ? summarize(item.value.windows, item.value.source, Date.parse(item.value.fetchedAt), item.value.limited && !item.value.windows.some(w => w.usedPercent >= 100)) : unknown('unknown', now());
99
101
  // Do not keep a limited window alive after its reset.
100
102
  if (value.windows.some(w => w.resetsAt && Date.parse(w.resetsAt) <= now())) return overlay(provider, id, unknown('unknown', now()));
101
103
  return overlay(provider, id, value);
@@ -137,7 +139,7 @@ export function createAccountUsage({ accounts = {}, readCodex = null, fetchImpl
137
139
  await Promise.all(Object.entries(accounts).filter(([p]) => !provider || p === provider).flatMap(([p, service]) => service.list().map(row => read(p, row.id))));
138
140
  return list();
139
141
  }
140
- function list() { return Object.fromEntries(Object.entries(accounts).map(([p, service]) => [p, service.listForClient().map(row => ({ ...row, usage: peek(p, row.id) }))])); }
142
+ function list({ stale = false } = {}) { return Object.fromEntries(Object.entries(accounts).map(([p, service]) => [p, service.listForClient().map(row => ({ ...row, usage: peek(p, row.id, { stale }) }))])); }
141
143
  function invalidate(p, id) { const key = keyOf(p, id); generations.set(key, (generations.get(key) || 0) + 1); cache.delete(key); overlays.delete(key); }
142
144
  return { read, peek, note, refresh, list, invalidate };
143
145
  }
@@ -0,0 +1,161 @@
1
+ // ═══════════════════════════════════════════
2
+ // SynaBun — /api/system/app: Settings > App for an npm or Git install
3
+ // ═══════════════════════════════════════════
4
+ //
5
+ // GET /api/system/app { ok, install, desktop, updates: { auto }, startup: { … } }
6
+ // PUT /api/system/app/updates { auto: boolean } the automatic update check and its badge
7
+ // PUT /api/system/app/startup { enabled: boolean } start the server at login (lib/login-start.js)
8
+ // POST /api/system/app/open-folder { which: 'data' | 'logs' }
9
+ //
10
+ // Automatic updates here are the check: on (the default), the server looks for
11
+ // a newer version when it starts and the page shows the update badge and asks
12
+ // again every half hour; off, it looks only when the user asks (Check for
13
+ // updates). Installing always goes through the update dialog and its backup
14
+ // step; nothing installs unattended. The setting is DATA_HOME/data/app-settings.json.
15
+ //
16
+ // In the desktop app (`desktop: true`) the app's own settings are the
17
+ // window's (packaging/desktop/src/app-prefs.mjs, through the preload bridge):
18
+ // this answers what it is and changes nothing.
19
+ //
20
+ // These are the owner's acts on this computer (a login entry, a folder opened
21
+ // on its screen), so the routes take the guards of /api/cli/install
22
+ // (lib/http-guards.js): a loopback socket and no proxy header, a localhost
23
+ // Host, no invite guest; a write also its own Origin, a JSON body,
24
+ // X-SynaBun-UI: 1 and no agent header. (`/api/system` is admin-only in
25
+ // lib/request-gate.js too.) A request names a switch, never a path or a command.
26
+
27
+ import { Router } from 'express';
28
+ import { mkdirSync, readFileSync, renameSync, writeFileSync } from 'node:fs';
29
+ import { dirname } from 'node:path';
30
+ import { guestForbidden, noStore, requireLocal, requireLocalHost, requireSameOriginJson, sendGuardError, uiOnly } from './http-guards.js';
31
+ import { runProgram } from './file-explorer-system.js';
32
+
33
+ export const APP_SETTINGS_DEFAULTS = Object.freeze({ autoUpdateCheck: true });
34
+
35
+ export function normalizeAppSettings(raw) {
36
+ const source = raw && typeof raw === 'object' && !Array.isArray(raw) ? raw : {};
37
+ const out = {};
38
+ for (const [key, value] of Object.entries(APP_SETTINGS_DEFAULTS)) out[key] = typeof source[key] === typeof value ? source[key] : value;
39
+ return out;
40
+ }
41
+
42
+ /** DATA_HOME/data/app-settings.json, read once and written through (this server is its only writer). */
43
+ export function createAppSettingsStore({ path, fs = { mkdirSync, readFileSync, renameSync, writeFileSync } } = {}) {
44
+ let current = null;
45
+ const load = () => {
46
+ if (current) return current;
47
+ try { current = normalizeAppSettings(JSON.parse(fs.readFileSync(path, 'utf8'))); } catch { current = normalizeAppSettings(null); }
48
+ return current;
49
+ };
50
+ return {
51
+ get: () => ({ ...load() }),
52
+ set(patch) {
53
+ const next = normalizeAppSettings({ ...load(), ...patch });
54
+ fs.mkdirSync(dirname(path), { recursive: true });
55
+ const temp = `${path}.${process.pid}.tmp`;
56
+ fs.writeFileSync(temp, JSON.stringify(next, null, 2) + '\n');
57
+ fs.renameSync(temp, path);
58
+ current = next;
59
+ return { ...next };
60
+ },
61
+ };
62
+ }
63
+
64
+ /** The program that shows a folder in the system's file manager (no shell; the path is an argument of its own). */
65
+ export function openFolderCommand(platform, path, { env = process.env } = {}) {
66
+ if (platform === 'darwin') return { file: 'open', args: [path] };
67
+ // explorer.exe answers 1 even when it opened the folder.
68
+ if (platform === 'win32') return { file: 'explorer.exe', args: [path], anyExit: true };
69
+ if (!env.DISPLAY && !env.WAYLAND_DISPLAY) return null;
70
+ return { file: 'xdg-open', args: [path] };
71
+ }
72
+
73
+ /** Open a folder on this computer's screen; resolves true when the file manager took it. */
74
+ export async function openFolderOnScreen(path, { platform = process.platform, env = process.env, run = runProgram, mkdir = mkdirSync } = {}) {
75
+ const command = openFolderCommand(platform, path, { env });
76
+ if (!command) return false;
77
+ try { mkdir(path, { recursive: true }); } catch {}
78
+ try {
79
+ await run(command.file, command.args);
80
+ return true;
81
+ } catch (error) {
82
+ return !!command.anyExit && error?.code !== 'ENOENT';
83
+ }
84
+ }
85
+
86
+ function localRouter({ isGuestRequest, port }) {
87
+ const router = Router();
88
+ const portOf = () => Number(typeof port === 'function' ? port() : port);
89
+ const built = new Map();
90
+ const perPort = (name, factory) => (req, res, next) => {
91
+ const key = `${name}:${portOf()}`;
92
+ let guard = built.get(key);
93
+ if (!guard) {
94
+ try { guard = factory(portOf()); } catch { return sendGuardError(res, 500, 'BAD_PORT', 'The app settings have no valid port.'); }
95
+ built.set(key, guard);
96
+ }
97
+ return guard(req, res, next);
98
+ };
99
+ router.use(noStore, guestForbidden(isGuestRequest), requireLocal, perPort('host', requireLocalHost));
100
+ return { router, mutating: [perPort('origin', requireSameOriginJson), uiOnly] };
101
+ }
102
+
103
+ /**
104
+ * @param {object} deps
105
+ * @param {ReturnType<typeof createAppSettingsStore>} deps.store
106
+ * @param {ReturnType<import('./login-start.js').createLoginStart>} deps.loginStart
107
+ * @param {{ data: string, logs: string }} deps.folders
108
+ * @param {string} deps.install 'npm' | 'git' | 'app' | 'desktop'
109
+ * @param {(auto: boolean) => void} [deps.onAutoUpdateChange]
110
+ * @param {(path: string) => Promise<boolean>} [deps.openFolder]
111
+ */
112
+ export function createAppControlsApi({
113
+ store, loginStart, folders, install = 'npm', onAutoUpdateChange = () => {}, openFolder = (path) => openFolderOnScreen(path),
114
+ isGuestRequest = () => false, port = 3344, log = () => {},
115
+ } = {}) {
116
+ if (!store || !loginStart || !folders) throw new Error('createAppControlsApi needs the settings store, the login start and the folders');
117
+ const { router, mutating } = localRouter({ isGuestRequest, port });
118
+ const desktop = install === 'desktop';
119
+
120
+ const body = () => {
121
+ let startup;
122
+ try { startup = loginStart.status(); } catch (error) { startup = { supported: false, reason: 'unsupported', enabled: false, state: 'off', owner: null, error: String(error?.message || error) }; }
123
+ return { ok: true, install, desktop, updates: { auto: store.get().autoUpdateCheck !== false }, startup };
124
+ };
125
+
126
+ router.get('/', (req, res) => { res.json(body()); });
127
+
128
+ router.put('/updates', ...mutating, (req, res) => {
129
+ const auto = req.body?.auto;
130
+ if (typeof auto !== 'boolean') return res.status(400).json({ ok: false, code: 'BAD_REQUEST', error: 'Send { auto: true | false }.' });
131
+ if (desktop) return res.status(409).json({ ok: false, code: 'DESKTOP_MANAGED', error: 'The SynaBun app keeps this setting itself.' });
132
+ try { store.set({ autoUpdateCheck: auto }); } catch (error) {
133
+ return res.status(500).json({ ok: false, code: 'SAVE_FAILED', error: String(error?.message || error) });
134
+ }
135
+ try { onAutoUpdateChange(auto); } catch {}
136
+ res.json(body());
137
+ });
138
+
139
+ router.put('/startup', ...mutating, async (req, res) => {
140
+ const enabled = req.body?.enabled;
141
+ if (typeof enabled !== 'boolean') return res.status(400).json({ ok: false, code: 'BAD_REQUEST', error: 'Send { enabled: true | false }.' });
142
+ try {
143
+ await loginStart.set(enabled);
144
+ } catch (error) {
145
+ const code = String(error?.code || 'refused');
146
+ log(`[app] start at login could not be turned ${enabled ? 'on' : 'off'}: ${error?.message || error}`);
147
+ return res.status(409).json({ ...body(), ok: false, code, error: String(error?.message || error).slice(0, 300) });
148
+ }
149
+ res.json(body());
150
+ });
151
+
152
+ router.post('/open-folder', ...mutating, async (req, res) => {
153
+ const which = req.body?.which;
154
+ if (which !== 'data' && which !== 'logs') return res.status(400).json({ ok: false, code: 'BAD_REQUEST', error: "Send { which: 'data' | 'logs' }." });
155
+ const opened = await openFolder(folders[which]).catch(() => false);
156
+ if (!opened) return res.status(422).json({ ok: false, code: 'NO_FILE_MANAGER', error: 'No file manager opened the folder.' });
157
+ res.json({ ok: true });
158
+ });
159
+
160
+ return router;
161
+ }
@@ -7,6 +7,7 @@
7
7
  // composer attachments, Claude/Codex account management, and the sidepanel tab registry.
8
8
  // Mounted from server.js with `app.use('/api/assistant', createAssistantApi(deps))`.
9
9
 
10
+ import { dispatchInput } from './assistant-tools.js';
10
11
  import { Router } from 'express';
11
12
  import { DispatchError } from './assistant-dispatch.js';
12
13
  import { openServable } from './assistant-media.js';
@@ -17,6 +18,7 @@ import { tooLargeMessage } from './assistant-attachments.js';
17
18
  import { getSessionPolicy, requiresHumanApproval } from './remote-policy.js';
18
19
  import { clampPlanDispatch, PLAN_DISPATCH_NOTE, runCanChangeCode } from './assistant-plan-permissions.js';
19
20
  import { noStore, requireJson, requireLocal, requireLocalHost, sendGuardError } from './http-guards.js';
21
+ import { setUntrustedFileHeaders } from './untrusted-file-headers.js';
20
22
 
21
23
  /** The header whatsapp_send_file's caller proves it is the WhatsApp conversation's own brain with (assistant-runtime.js BRAIN_CREDENTIAL_RE). */
22
24
  export const BRAIN_CREDENTIAL_HEADER = 'x-synabun-brain-token';
@@ -41,7 +43,7 @@ const NO_CATALOG = Object.freeze({});
41
43
  function sendError(res, error, fallbackStatus = 500) {
42
44
  if (error instanceof DispatchError || (error && error.code && error.status)) {
43
45
  const status = Number(error.status) || fallbackStatus;
44
- const body = typeof error.toJSON === 'function' ? error.toJSON() : { code: error.code, message: error.message };
46
+ const body = typeof error.toJSON === 'function' ? error.toJSON() : { code: error.code, message: error.message, ...(error.problems ? { problems: error.problems } : {}) };
45
47
  return res.status(status).json({ ok: false, error: body.message, ...body });
46
48
  }
47
49
  return res.status(fallbackStatus).json({ ok: false, error: error?.message || String(error), code: error?.code || 'INTERNAL' });
@@ -95,7 +97,7 @@ export function slimRunView(run) {
95
97
  * @param {object} [deps.runtime] createAssistantRuntime() (sessions; optional until wired)
96
98
  * @param {object} [deps.claudeAccounts] createClaudeAccounts()
97
99
  * @param {object} [deps.codexAccounts] createCodexAccounts() (lib/codex-accounts.js)
98
- * @param {Function} [deps.buildCatalog] async () => catalog object (models/accounts/projects/profiles)
100
+ * @param {Function} [deps.buildCatalog] async () => catalog object (models/accounts/projects/tools)
99
101
  * @param {Function} [deps.createLoginTerminal] async ({ env, cwd, profile, accountId }) => { terminalSessionId, profile }
100
102
  * @param {Function} [deps.claudeSignIn] async (accountId) => { sessionId, status, error } — Claude Code's own
101
103
  * `auth login` in a SynaBun terminal under the account's config directory (lib/provider-panel-terminal-auth.js)
@@ -152,6 +154,11 @@ export function createAssistantApi({
152
154
  port = 3344,
153
155
  // The Admin sandbox setting (lib/agent-sandbox-setting.js): one global value for every agent SynaBun starts.
154
156
  adminSandbox = null,
157
+ // Protect workers (lib/worker-guard-setting.js): what capability read-only / workspace means for every worker.
158
+ workerGuard = null,
159
+ // The page secret (lib/page-session.js): a request without an agent pin is the owner's page only when it carries
160
+ // this boot's cookie; a local process that merely sends no headers is nobody (never 'ui').
161
+ pageSession = null,
155
162
  } = {}) {
156
163
  if (!dispatcher) throw new Error('createAssistantApi requires a dispatcher');
157
164
  const router = Router();
@@ -163,20 +170,31 @@ export function createAssistantApi({
163
170
  next();
164
171
  });
165
172
 
166
- // Resolve who is calling: the UI (no pin), an assistant brain (assistant-<uuid>
167
- // pin), or a dispatched worker (run id pin → forbidden for orchestration).
173
+ // Resolve who is calling: the UI (the page secret cookie, no pin), a dispatched worker (run id pin →
174
+ // forbidden for orchestration), or nobody known. A brain is NOT decided here: a pin only records which
175
+ // session a request *claims* to be (for attribution), never that the request is that session's brain.
176
+ // Brain authority is the per-launch credential alone (brainCaller) — the final pre-restart review
177
+ // (run f283e53b) showed a forged `X-Synabun-Terminal: assistant-*`, and equally a real but non-secret
178
+ // session id (a worker's environment and its task text both carry one), would otherwise pass as the brain.
168
179
  function callerContext(req) {
169
180
  const pin = String(req.get('x-synabun-terminal') || req.get('x-synabun-assistant') || '').trim();
170
181
  const bodySession = req.body?.assistantSessionId || req.body?.assistant_session_id || req.query?.assistantSessionId || null;
171
- if (!pin) return { kind: 'ui', assistantSessionId: bodySession ? String(bodySession) : null };
182
+ if (!pin) {
183
+ // The page: this boot's page secret cookie (the browser sends it by itself). Without a page session wired
184
+ // (a test, an older host) every pinless request is the page, as before.
185
+ if (pageSession && typeof pageSession.verify === 'function' && !pageSession.verify(req)) return { kind: 'unknown', assistantSessionId: bodySession ? String(bodySession) : null, pin: '', pageless: true };
186
+ return { kind: 'ui', assistantSessionId: bodySession ? String(bodySession) : null };
187
+ }
172
188
  if (dispatcher.get(pin)) return { kind: 'worker', forbidden: true, pin };
173
- // A brain's pin resolves to its session (Claude pins the session id; the
174
- // OpenCode brain pins assistant-oc-<24>, which must never be taken as an id).
189
+ // The session a request claims, for attribution once a credential has proven it a brain: a Claude brain's
190
+ // pin resolves to its session; the OpenCode brain pins assistant-oc-<24> (never taken as an id) and names
191
+ // the session in the body. The claim grants nothing on its own.
175
192
  const resolved = runtime?.resolveTerminal?.(pin) || null;
176
- if (resolved) return { kind: 'assistant', assistantSessionId: resolved, pin };
177
- if (/^assistant-oc-/.test(pin)) return { kind: 'assistant', assistantSessionId: bodySession ? String(bodySession) : null, pin };
178
- if (/^assistant-/.test(pin)) return { kind: 'assistant', assistantSessionId: pin, pin };
179
- return { kind: 'unknown', assistantSessionId: bodySession ? String(bodySession) : null, pin };
193
+ const claimed = resolved
194
+ || (/^assistant-oc-/.test(pin) ? (bodySession ? String(bodySession) : null)
195
+ : /^assistant-/.test(pin) ? pin
196
+ : (bodySession ? String(bodySession) : null));
197
+ return { kind: 'unknown', assistantSessionId: claimed, pin };
180
198
  }
181
199
  /**
182
200
  * The plan mode of the brain making this call: 'read-only' (a remote session
@@ -211,20 +229,56 @@ export function createAssistantApi({
211
229
  function orchestrationGuard(req, res, next) {
212
230
  const caller = callerContext(req);
213
231
  if (caller.forbidden) return res.status(403).json({ ok: false, code: 'AGENTS_NOT_PERMITTED', error: 'Dispatched workers cannot orchestrate other agents.' });
214
- req.assistantCaller = caller;
215
- next();
232
+ // The user's own page (this boot's page secret cookie): the only caller that orchestrates without being a brain.
233
+ if (caller.kind === 'ui') { req.assistantCaller = caller; return next(); }
234
+ // The only other caller allowed is an Assistant brain, and only its per-launch credential proves it (brainCaller):
235
+ // never a pin, never a session id, which a worker's environment and its task text both carry. This is the forged-
236
+ // and known-pin bypass the final pre-restart review found (run f283e53b, assistant-api.js:189): an
237
+ // `X-Synabun-Terminal: assistant-*` header, or a real live session id, used to pass here as the brain.
238
+ const brain = brainCaller(req);
239
+ if (brain) { req.assistantCaller = brain; return next(); }
240
+ return res.status(403).json({ ok: false, code: 'UI_ONLY', error: 'Orchestration is the user\'s (from SynaBun\'s own page) and the Assistant\'s brain\'s: this request is neither (no page secret, no brain launch credential a live session knows).' });
216
241
  }
217
242
  /**
218
- * The caller when it is an assistant brain, else null. Brains pin their
219
- * session (Claude) or assistant-oc-<24> (OpenCode); the Codex brain's MCP
220
- * child pins codex-sp-<uuid>, which no session resolves, but it sends
221
- * X-Synabun-Role: assistant. The UI, workers and other sessions never count.
243
+ * The caller when it is an assistant brain, else null. The one proof is the per-launch credential
244
+ * (X-Synabun-Brain-Token, SYNABUN_BRAIN_TOKEN in the brain's own environment only, minted per brain launch and
245
+ * resolved by runtime.sessionForBrainCredential over the live brains): a pin or a session id names nobody here,
246
+ * because any local process can send a header and a session id is not secret (a worker carries its own, and ids
247
+ * appear in task text). The credential's session is authoritative, whatever pin or body the request carries. The
248
+ * UI, workers and other sessions never count.
222
249
  */
223
250
  function brainCaller(req) {
251
+ if (req.assistantCaller?.credentialed) return req.assistantCaller;
252
+ if (String(req.get('x-synabun-role') || '').trim().toLowerCase() !== 'assistant') return null;
253
+ let session = null;
254
+ try { session = runtime?.sessionForBrainCredential?.(req.get('x-synabun-brain-token')) || null; } catch { session = null; }
255
+ if (!session) return null;
224
256
  const caller = req.assistantCaller || callerContext(req);
225
- if (caller.kind === 'assistant') return caller;
226
- if (caller.kind === 'unknown' && String(req.get('x-synabun-role') || '').trim().toLowerCase() === 'assistant') return caller;
227
- return null;
257
+ return { ...caller, kind: 'assistant', assistantSessionId: String(session), credentialed: true };
258
+ }
259
+ /**
260
+ * Who may READ a run (GET /runs/:id, /result, /transcript): the owner's page (ui), a credential-verified
261
+ * Assistant brain (its session's runs, as today), or the worker of that very run — a worker's pin is its own
262
+ * run id, so it reads only itself, never another run. Everyone else (a pinless local process with no page
263
+ * secret, a worker naming another run) is refused. Listing and deletion are owner-or-brain only (never a worker):
264
+ * the list is not "its own run", and a finished run is cleared by the owner or the brain that owns its session.
265
+ */
266
+ function runReadAllowed(req, runId) {
267
+ const caller = callerContext(req);
268
+ if (caller.kind === 'ui') return true;
269
+ const brain = brainCaller(req);
270
+ if (brain) {
271
+ // A brain reads only its OWN session's runs (like DELETE): another session's run is not readable through the
272
+ // credential. A run with no session, or a sessionless brain, falls through as before (nothing to scope to).
273
+ const runSession = dispatcher.get(runId)?.assistantSessionId || null;
274
+ if (runSession && brain.assistantSessionId && String(runSession) !== String(brain.assistantSessionId)) return false;
275
+ return true;
276
+ }
277
+ if (caller.kind === 'worker' && caller.pin && String(caller.pin) === String(runId)) return true;
278
+ return false;
279
+ }
280
+ function refuseRunRead(res) {
281
+ return res.status(403).json({ ok: false, code: 'RUN_FORBIDDEN', error: 'This run is readable by the owner (from SynaBun\'s own page), its own worker and the Assistant brain of its session only.' });
228
282
  }
229
283
  /** A remote session whose workers only a person may answer (strict worker approvals below autonomous). */
230
284
  function humanApprovalRequired(assistantSessionId) {
@@ -301,7 +355,10 @@ export function createAssistantApi({
301
355
  // ── catalog ────────────────────────────────────────────────────────────────
302
356
  router.get('/catalog', async (req, res) => {
303
357
  try {
304
- await accountUsage?.refresh();
358
+ // Quotas are shown as last read and read again in the background (the probe can take seconds; the
359
+ // dispatcher and the router read them fresh before they choose an account).
360
+ accountUsage?.refresh()?.catch?.(() => {});
361
+ const usage = () => accountUsage?.list({ stale: true });
305
362
  // refresh=1 rebuilds from the providers' endpoints; deep=1 also makes them ask the CLI / app-server again.
306
363
  const deep = req.query.deep === '1';
307
364
  const force = req.query.refresh === '1' || deep;
@@ -320,8 +377,8 @@ export function createAssistantApi({
320
377
  // names none.
321
378
  const accounts = {
322
379
  ...(brief.accounts || {}),
323
- ...(claudeAccounts ? { 'claude-code': accountUsage?.list()?.['claude-code'] || claudeAccounts.listForClient() } : {}),
324
- ...(codexAccounts ? { codex: accountUsage?.list()?.codex || codexAccounts.listForClient() } : {}),
380
+ ...(claudeAccounts ? { 'claude-code': usage()?.['claude-code'] || claudeAccounts.listForClient() } : {}),
381
+ ...(codexAccounts ? { codex: usage()?.codex || codexAccounts.listForClient() } : {}),
325
382
  };
326
383
  return res.json({ ok: true, generatedAt: new Date(now()).toISOString(), ...brief, accounts, claudeWorkerAccount: claudeWorkerAccountId(), codexWorkerAccount: codexWorkerAccountId(), limits: dispatcher.limits, runs: dispatcher.list({ activeOnly: true }), totals: dispatcher.totals() });
327
384
  }
@@ -329,7 +386,7 @@ export function createAssistantApi({
329
386
  // within seconds), so it is asked every time: this answer is reused only while it hands back the
330
387
  // build the answer was made from (a rebuild or invalidate() gives another one).
331
388
  const base = typeof buildCatalog === 'function' ? await buildCatalog({ force, deep }) : NO_CATALOG;
332
- if (!force && catalogCache && catalogCache.base === base && now() - catalogCache.at < 30_000) return res.json(accountUsage ? { ...catalogCache.value, accounts: { ...catalogCache.value.accounts, ...accountUsage.list() } } : catalogCache.value);
389
+ if (!force && catalogCache && catalogCache.base === base && now() - catalogCache.at < 30_000) return res.json(accountUsage ? { ...catalogCache.value, accounts: { ...catalogCache.value.accounts, ...usage() } } : catalogCache.value);
333
390
  // Hidden rows stay server-side (view=manage lists them); the lists themselves ship.
334
391
  const { hiddenRows: _hiddenRows, ...visible } = base || {};
335
392
  const value = {
@@ -338,8 +395,8 @@ export function createAssistantApi({
338
395
  ...visible,
339
396
  accounts: {
340
397
  ...(base.accounts || {}),
341
- 'claude-code': accountUsage?.list()?.['claude-code'] || (claudeAccounts ? claudeAccounts.listForClient() : (base.accounts?.['claude-code'] || [])),
342
- codex: accountUsage?.list()?.codex || (codexAccounts ? codexAccounts.listForClient() : (base.accounts?.codex || [])),
398
+ 'claude-code': usage()?.['claude-code'] || (claudeAccounts ? claudeAccounts.listForClient() : (base.accounts?.['claude-code'] || [])),
399
+ codex: usage()?.codex || (codexAccounts ? codexAccounts.listForClient() : (base.accounts?.codex || [])),
343
400
  },
344
401
  claudeWorkerAccount: claudeWorkerAccountId(),
345
402
  codexWorkerAccount: codexWorkerAccountId(),
@@ -359,9 +416,8 @@ export function createAssistantApi({
359
416
  try {
360
417
  const body = req.body || {};
361
418
  const spec = {
362
- ...body,
419
+ ...dispatchInput(body),
363
420
  accountId: body.accountId || body.account_id || body.codexAccountId || body.claudeAccountId || null,
364
- mcpProfile: body.mcpProfile || body.mcp_profile,
365
421
  permissionPolicy: body.permissionPolicy || body.permission_policy,
366
422
  maxMinutes: body.maxMinutes ?? body.max_minutes,
367
423
  budgetUsd: body.budgetUsd ?? body.budget_usd,
@@ -720,18 +776,25 @@ export function createAssistantApi({
720
776
  });
721
777
 
722
778
  router.get('/runs', (req, res) => {
779
+ // Listing is owner-or-brain only: a worker reads its own run by id, never the roster of every run.
780
+ const caller = callerContext(req);
781
+ const brain = brainCaller(req);
782
+ if (caller.kind !== 'ui' && !brain) return refuseRunRead(res);
723
783
  try {
724
784
  const active = req.query.active === '1' || req.query.activeOnly === '1';
785
+ // A brain sees only its OWN session's runs (its session id forced over whatever the query asks); the owner's
786
+ // page may list any session.
787
+ const scopedSession = (brain && caller.kind !== 'ui') ? (brain.assistantSessionId || null) : (req.query.assistantSessionId || null);
725
788
  res.json({
726
789
  ok: true,
727
790
  runs: dispatcher.list({
728
791
  activeOnly: active,
729
- assistantSessionId: req.query.assistantSessionId || null,
792
+ assistantSessionId: scopedSession,
730
793
  workflowId: req.query.workflowId || null,
731
794
  status: req.query.status || null,
732
795
  limit: clampInt(req.query.limit, 1, 500, 100),
733
796
  }),
734
- totals: dispatcher.totals({ assistantSessionId: req.query.assistantSessionId || null, workflowId: req.query.workflowId || null }),
797
+ totals: dispatcher.totals({ assistantSessionId: scopedSession, workflowId: req.query.workflowId || null }),
735
798
  });
736
799
  } catch (error) { sendError(res, error); }
737
800
  });
@@ -764,12 +827,19 @@ export function createAssistantApi({
764
827
  if (!assistantSessionId && !workflowId && req.assistantCaller.kind !== 'ui') {
765
828
  return res.status(400).json({ ok: false, code: 'SCOPE_REQUIRED', error: 'Pass assistant_session_id (your ASSISTANT_SESSION_ID) or workflow_id to stop runs.' });
766
829
  }
767
- const result = await dispatcher.killAll({ assistantSessionId, workflowId, reason: body.reason || 'kill_all' });
830
+ const reason = body.reason || 'kill_all';
831
+ const result = await dispatcher.killAll({ assistantSessionId, workflowId, reason });
832
+ // Who stopped every run, when: the records only say `kill_all` (2026-10-24 had to be traced by elimination).
833
+ const caller = req.assistantCaller.kind === 'ui' ? 'the page' : `the brain of ${req.assistantCaller.assistantSessionId || 'an unknown session'}`;
834
+ const scope = [assistantSessionId ? `session ${assistantSessionId}` : null, workflowId ? `workflow ${workflowId}` : null].filter(Boolean).join(', ') || 'every session';
835
+ const stopped = Array.isArray(result?.stopped) ? result.stopped : [];
836
+ log('kill-all', `${new Date(now()).toISOString()} ${caller} stopped ${stopped.length} run(s) of ${scope} (${reason})${stopped.length ? `: ${stopped.join(', ')}` : ''}`);
768
837
  res.json(result);
769
838
  } catch (error) { sendError(res, error); }
770
839
  });
771
840
 
772
841
  router.get('/runs/:runId', (req, res) => {
842
+ if (!runReadAllowed(req, req.params.runId)) return refuseRunRead(res);
773
843
  const run = dispatcher.get(req.params.runId);
774
844
  if (!run) return res.status(404).json({ ok: false, code: 'RUN_NOT_FOUND', error: `Unknown run ${req.params.runId}` });
775
845
  const brain = brainCaller(req);
@@ -777,6 +847,7 @@ export function createAssistantApi({
777
847
  res.json({ ok: true, run: brain ? slimRunView(run) : run });
778
848
  });
779
849
  router.get('/runs/:runId/result', (req, res) => {
850
+ if (!runReadAllowed(req, req.params.runId)) return refuseRunRead(res);
780
851
  try {
781
852
  const out = dispatcher.transcript(req.params.runId, { format: 'result' });
782
853
  if (out?.result) {
@@ -787,6 +858,7 @@ export function createAssistantApi({
787
858
  } catch (error) { sendError(res, error); }
788
859
  });
789
860
  router.get('/runs/:runId/transcript', (req, res) => {
861
+ if (!runReadAllowed(req, req.params.runId)) return refuseRunRead(res);
790
862
  try {
791
863
  const format = req.query.format || 'tail';
792
864
  const out = dispatcher.transcript(req.params.runId, {
@@ -807,6 +879,7 @@ export function createAssistantApi({
807
879
  // path from the request; owner-only like everything here. sendFile answers
808
880
  // Range requests, so a video seeks.
809
881
  router.get('/runs/:runId/media/:n', async (req, res) => {
882
+ if (!runReadAllowed(req, req.params.runId)) return refuseRunRead(res);
810
883
  const notFound = () => res.status(404).json({ ok: false, code: 'MEDIA_NOT_FOUND', error: `No media ${req.params.n} for run ${req.params.runId}` });
811
884
  if (!/^\d{1,4}$/.test(String(req.params.n))) return notFound();
812
885
  let file = null;
@@ -836,6 +909,8 @@ export function createAssistantApi({
836
909
  'Accept-Ranges': 'bytes', 'X-Content-Type-Options': 'nosniff', 'Cache-Control': 'private, max-age=3600',
837
910
  });
838
911
  if (range) res.set('Content-Range', `bytes ${start}-${end}/${size}`);
912
+ // A run's media is what a worker made: opened by its address it runs nothing, and an SVG is a download.
913
+ setUntrustedFileHeaders(res, file.path, file.mime);
839
914
  if (req.method === 'HEAD' || !size) { await handle.close().catch(() => {}); return res.end(); }
840
915
  const stream = handle.createReadStream({ start, end });
841
916
  stream.on('error', () => res.destroy());
@@ -894,10 +969,23 @@ export function createAssistantApi({
894
969
  router.post('/runs/:runId/stop', orchestrationGuard, async (req, res) => {
895
970
  try { res.json(await dispatcher.stop(req.params.runId, req.body?.reason || (req.assistantCaller.kind === 'ui' ? 'user' : 'assistant'))); } catch (error) { sendError(res, error); }
896
971
  });
897
- // Finished runs are cleared from the agents tray by the user, never by an agent.
972
+ // Finished runs are cleared from the agents tray by the owner (the page) or the Assistant brain of the run's
973
+ // own session; never by a worker (even its own run). A brain's delete is scoped to its session's runs.
898
974
  router.delete('/runs/:runId', async (req, res) => {
899
- if (callerContext(req).kind !== 'ui') return res.status(403).json({ ok: false, code: 'UI_ONLY', error: 'Finished runs are removed from the assistant panel only.' });
900
- try { res.json(await dispatcher.remove(req.params.runId, { assistantSessionId: req.query.assistantSessionId || req.body?.assistantSessionId || null })); } catch (error) { sendError(res, error); }
975
+ const caller = callerContext(req);
976
+ const brain = brainCaller(req);
977
+ if (caller.kind !== 'ui' && !brain) return res.status(403).json({ ok: false, code: 'UI_ONLY', error: 'Finished runs are removed by the owner (from SynaBun\'s own page) or the Assistant brain of the run\'s session.' });
978
+ if (brain && caller.kind !== 'ui') {
979
+ // A brain removes only the runs of its own session: another session's run is left untouched.
980
+ const runSession = dispatcher.get(req.params.runId)?.assistantSessionId || null;
981
+ if (runSession && brain.assistantSessionId && String(runSession) !== String(brain.assistantSessionId)) {
982
+ return res.status(403).json({ ok: false, code: 'RUN_NOT_IN_SESSION', error: `Run ${req.params.runId} belongs to another assistant session` });
983
+ }
984
+ }
985
+ const assistantSessionId = (brain && caller.kind !== 'ui')
986
+ ? (brain.assistantSessionId || null)
987
+ : (req.query.assistantSessionId || req.body?.assistantSessionId || null);
988
+ try { res.json(await dispatcher.remove(req.params.runId, { assistantSessionId })); } catch (error) { sendError(res, error); }
901
989
  });
902
990
  router.post('/runs/clear-finished', async (req, res) => {
903
991
  if (callerContext(req).kind !== 'ui') return res.status(403).json({ ok: false, code: 'UI_ONLY', error: 'Finished runs are removed from the assistant panel only.' });
@@ -996,9 +1084,17 @@ export function createAssistantApi({
996
1084
  if (!requireRuntime(res)) return;
997
1085
  try { res.json({ ok: true, session: await runtime.closeSession(req.params.id) }); } catch (error) { sendError(res, error); }
998
1086
  });
1087
+ // Remove (the Sessions menu): the record, its transcript and its usage journal go for good, after its
1088
+ // running turn and the workers it dispatched are stopped. Memories made from it stay.
999
1089
  router.delete('/sessions/:id', async (req, res) => {
1000
1090
  if (!requireRuntime(res)) return;
1001
- try { res.json({ ok: true, removed: await runtime.destroySession(req.params.id) }); } catch (error) { sendError(res, error); }
1091
+ try {
1092
+ if (runtime.removeSession) {
1093
+ const { removed, stoppedRuns } = await runtime.removeSession(req.params.id);
1094
+ return res.json({ ok: true, removed, stoppedRuns });
1095
+ }
1096
+ res.json({ ok: true, removed: await runtime.destroySession(req.params.id) });
1097
+ } catch (error) { sendError(res, error); }
1002
1098
  });
1003
1099
 
1004
1100
  // ── attachments ────────────────────────────────────────────────────────────
@@ -1249,6 +1345,23 @@ export function createAssistantApi({
1249
1345
  try { res.json({ ok: true, ...adminSandbox.set(value) }); } catch (error) { sendError(res, error); }
1250
1346
  });
1251
1347
 
1348
+ // ── Protect workers (lib/worker-guard-setting.js) ──────────────────────────
1349
+ // What capability read-only / workspace means for every worker the dispatcher starts
1350
+ // (lib/worker-confinement.js): enforce (default), warn, off. The user's setting: an agent cannot change it.
1351
+ router.get('/worker-guard', (req, res) => {
1352
+ if (!workerGuard) return res.status(503).json({ ok: false, code: 'WORKER_GUARD_UNAVAILABLE', error: 'The Protect workers setting is not available on this server' });
1353
+ res.json({ ok: true, ...workerGuard.view() });
1354
+ });
1355
+ router.put('/worker-guard', requireJson, (req, res) => {
1356
+ if (!workerGuard) return res.status(503).json({ ok: false, code: 'WORKER_GUARD_UNAVAILABLE', error: 'The Protect workers setting is not available on this server' });
1357
+ if (callerContext(req).kind !== 'ui' || req.get('x-synabun-desktop-grant') || req.get('x-synabun-role')) {
1358
+ return res.status(403).json({ ok: false, code: 'UI_ONLY', error: 'Only the user can change Protect workers.' });
1359
+ }
1360
+ const value = req.body?.mode;
1361
+ if (typeof value !== 'string' || !workerGuard.view().modes.includes(value)) return res.status(400).json({ ok: false, code: 'INVALID_VALUE', error: `mode must be one of ${workerGuard.view().modes.join(', ')}.` });
1362
+ try { res.json({ ok: true, ...workerGuard.set(value) }); } catch (error) { sendError(res, error); }
1363
+ });
1364
+
1252
1365
  // ── sidepanel tab registry (UI heartbeat) ──────────────────────────────────
1253
1366
  function listTabs() {
1254
1367
  const cutoff = now() - tabTtlMs;
@@ -123,10 +123,11 @@ export function createClaudeBrain({ session, sink, deps = {}, persona = '', hook
123
123
  'X-Synabun-Role': 'assistant', 'X-Synabun-Terminal': session.id, 'X-Synabun-Memory-Session': session.id,
124
124
  ...(deps.desktopGrant ? { 'X-Synabun-Desktop-Grant': deps.desktopGrant } : {}),
125
125
  ...mcpHeaders,
126
- // A WhatsApp conversation's brain credential (whatsapp_send_file). The header names the variable and
126
+ // This brain launch's credential (assistant-runtime.js mintBrainCredential). The header names the variable and
127
127
  // the CLI expands it from its own environment (below): the MCP config is on its command line, which every
128
128
  // account of the computer can read, an environment only this user's processes. The in-process MCP server
129
- // keeps the value for the request it arrived with, and sends it on the file route alone. Last, so nothing replaces it.
129
+ // keeps the value for the request it arrived with, and sends it only to the routes that ask who the brain is
130
+ // (whatsapp_send_file, the Skills Service). Last, so nothing replaces it.
130
131
  ...(deps.brainCredential ? { 'X-Synabun-Brain-Token': '${SYNABUN_BRAIN_TOKEN}' } : {}),
131
132
  },
132
133
  // A remote (WhatsApp) session never pre-approves computer use: its PreToolUse hook refuses, asks or allows each call.
@@ -143,7 +144,7 @@ export function createClaudeBrain({ session, sink, deps = {}, persona = '', hook
143
144
  ...(typeof deps.claudeAccountEnv === 'function' ? deps.claudeAccountEnv(brain.accountId) : {}),
144
145
  SYNABUN_ASSISTANT_SESSION: String(session.id),
145
146
  ...(deps.userContextThreads === true ? { SYNABUN_ASSISTANT_THREADS: '1' } : {}),
146
- // The value of the X-Synabun-Brain-Token header above (a WhatsApp conversation's brain only).
147
+ // The value of the X-Synabun-Brain-Token header above.
147
148
  ...(deps.brainCredential ? { SYNABUN_BRAIN_TOKEN: deps.brainCredential } : {}),
148
149
  },
149
150
  maxBudgetUsd: Number(deps.maxBudgetUsd) > 0 ? Number(deps.maxBudgetUsd) : undefined,
@@ -6,7 +6,9 @@
6
6
  // PreToolUse payload from stdin and asks lib/browser-tool-policy.js here, with
7
7
  // no server round trip; a refusal becomes permissionDecision "deny" with the
8
8
  // reason. Script paths resolve against the payload's cwd, else
9
- // SYNABUN_BROWSER_POLICY_CWD (the run's project). An unreadable payload allows
9
+ // SYNABUN_BROWSER_POLICY_CWD (the run's project); SYNABUN_BROWSER_POLICY_ROOTS
10
+ // (a JSON list, written as the run starts) names the registered code projects
11
+ // its test runners may also run in. An unreadable payload allows
10
12
  // the call: the policy is a speed bump, not a sandbox. A policy module that
11
13
  // cannot load fails closed (logged on stderr): shell commands, web search and
12
14
  // every MCP tool but SynaBun's are denied with that reason.
@@ -25,6 +27,14 @@ function unloadedDenial(tool) {
25
27
  return `SynaBun browser policy: ${tool} was refused — SynaBun's browser policy could not load (${loadError?.message || 'browserToolDenial is missing'}), so no command, web search or other server's tool can be checked for a browser launch. SynaBun's own tools still work. Stop and report this`;
26
28
  }
27
29
 
30
+ /** The registered code projects from the env (a JSON list of paths); anything unreadable is none. */
31
+ function policyRoots() {
32
+ try {
33
+ const list = JSON.parse(process.env.SYNABUN_BROWSER_POLICY_ROOTS || '[]');
34
+ return Array.isArray(list) ? list.filter((path) => typeof path === 'string') : [];
35
+ } catch { return []; }
36
+ }
37
+
28
38
  let raw = '';
29
39
  try { for await (const chunk of process.stdin) raw += chunk; } catch {}
30
40
  let payload = {};
@@ -35,7 +45,7 @@ try {
35
45
  const input = payload.tool_input && typeof payload.tool_input === 'object' ? payload.tool_input : {};
36
46
  const cwd = (typeof payload.cwd === 'string' && payload.cwd) || process.env.SYNABUN_BROWSER_POLICY_CWD || process.cwd();
37
47
  if (typeof browserToolDenial !== 'function') process.stderr.write(`[SynaBun] browser policy could not load (${loadError?.message || 'browserToolDenial is missing'}); denying shell, web search and other servers' tools\n`);
38
- const reason = !tool ? null : typeof browserToolDenial === 'function' ? browserToolDenial(tool, input, { host: 'codex', cwd }) : unloadedDenial(tool);
48
+ const reason = !tool ? null : typeof browserToolDenial === 'function' ? browserToolDenial(tool, input, { host: 'codex', cwd, roots: policyRoots() }) : unloadedDenial(tool);
39
49
  // Codex prints "Command blocked by PreToolUse hook: <reason>. Command: <cmd>", so
40
50
  // the reason goes without its own final period.
41
51
  if (reason) process.stdout.write(JSON.stringify({ hookSpecificOutput: { hookEventName: 'PreToolUse', permissionDecision: 'deny', permissionDecisionReason: reason.replace(/[.\s]+$/, '') } }));