synabun 2.0.10 → 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 (517) hide show
  1. package/CHANGELOG.md +86 -0
  2. package/README.md +25 -2
  3. package/THIRD-PARTY-LICENSES.md +22 -0
  4. package/bin/synabun.js +71 -24
  5. package/docs/whatsapp.md +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/lib/claude-hooks.js +172 -18
  10. package/lib/client-config-repair.js +17 -8
  11. package/lib/desktop-app.js +7 -1
  12. package/lib/external-tools.js +1 -1
  13. package/lib/first-launch-protection.js +57 -12
  14. package/lib/mcp-dist.js +176 -0
  15. package/lib/release-npm-ci.mjs +138 -0
  16. package/lib/release-publish.mjs +91 -0
  17. package/lib/release-scan.mjs +50 -0
  18. package/lib/server-crash-record.js +123 -0
  19. package/lib/server-exit.js +318 -0
  20. package/lib/server-supervisor.js +508 -0
  21. package/lib/start-launcher.js +37 -4
  22. package/lib/tools-cli.js +45 -0
  23. package/lib/update-snapshot.js +228 -0
  24. package/lib/virustotal.mjs +192 -0
  25. package/lib/windows-crash-event.js +269 -0
  26. package/mcp-server/dist/http.d.ts +20 -1
  27. package/mcp-server/dist/http.d.ts.map +1 -1
  28. package/mcp-server/dist/http.js +59 -12
  29. package/mcp-server/dist/http.js.map +1 -1
  30. package/mcp-server/dist/index.d.ts +33 -27
  31. package/mcp-server/dist/index.d.ts.map +1 -1
  32. package/mcp-server/dist/index.js +128 -143
  33. package/mcp-server/dist/index.js.map +1 -1
  34. package/mcp-server/dist/preload.js +1 -1
  35. package/mcp-server/dist/preload.js.map +1 -1
  36. package/mcp-server/dist/services/codex-config-heal.d.ts +6 -2
  37. package/mcp-server/dist/services/codex-config-heal.d.ts.map +1 -1
  38. package/mcp-server/dist/services/codex-config-heal.js +81 -28
  39. package/mcp-server/dist/services/codex-config-heal.js.map +1 -1
  40. package/mcp-server/dist/services/memory-judge-backfill.d.ts +6 -0
  41. package/mcp-server/dist/services/memory-judge-backfill.d.ts.map +1 -1
  42. package/mcp-server/dist/services/memory-judge-backfill.js +7 -1
  43. package/mcp-server/dist/services/memory-judge-backfill.js.map +1 -1
  44. package/mcp-server/dist/services/neural-interface.d.ts +16 -10
  45. package/mcp-server/dist/services/neural-interface.d.ts.map +1 -1
  46. package/mcp-server/dist/services/neural-interface.js +67 -11
  47. package/mcp-server/dist/services/neural-interface.js.map +1 -1
  48. package/mcp-server/dist/services/server-instructions.d.ts +7 -2
  49. package/mcp-server/dist/services/server-instructions.d.ts.map +1 -1
  50. package/mcp-server/dist/services/server-instructions.js +20 -62
  51. package/mcp-server/dist/services/server-instructions.js.map +1 -1
  52. package/mcp-server/dist/services/tool-catalog.d.ts +120 -0
  53. package/mcp-server/dist/services/tool-catalog.d.ts.map +1 -0
  54. package/mcp-server/dist/services/tool-catalog.js +339 -0
  55. package/mcp-server/dist/services/tool-catalog.js.map +1 -0
  56. package/mcp-server/dist/services/tool-list-refresh-barrier.d.ts +12 -0
  57. package/mcp-server/dist/services/tool-list-refresh-barrier.d.ts.map +1 -0
  58. package/mcp-server/dist/services/tool-list-refresh-barrier.js +40 -0
  59. package/mcp-server/dist/services/tool-list-refresh-barrier.js.map +1 -0
  60. package/mcp-server/dist/services/tool-list.d.ts +108 -0
  61. package/mcp-server/dist/services/tool-list.d.ts.map +1 -0
  62. package/mcp-server/dist/services/tool-list.js +334 -0
  63. package/mcp-server/dist/services/tool-list.js.map +1 -0
  64. package/mcp-server/dist/services/tool-runtime.d.ts +161 -0
  65. package/mcp-server/dist/services/tool-runtime.d.ts.map +1 -0
  66. package/mcp-server/dist/services/tool-runtime.js +400 -0
  67. package/mcp-server/dist/services/tool-runtime.js.map +1 -0
  68. package/mcp-server/dist/services/whiteboard-geometry.d.ts +26 -2
  69. package/mcp-server/dist/services/whiteboard-geometry.d.ts.map +1 -1
  70. package/mcp-server/dist/services/whiteboard-geometry.js +91 -8
  71. package/mcp-server/dist/services/whiteboard-geometry.js.map +1 -1
  72. package/mcp-server/dist/tools/agents.d.ts +10 -10
  73. package/mcp-server/dist/tools/agents.d.ts.map +1 -1
  74. package/mcp-server/dist/tools/agents.js +22 -13
  75. package/mcp-server/dist/tools/agents.js.map +1 -1
  76. package/mcp-server/dist/tools/browser-batch.d.ts +3 -3
  77. package/mcp-server/dist/tools/image-tools.d.ts +1 -1
  78. package/mcp-server/dist/tools/image-tools.js +1 -1
  79. package/mcp-server/dist/tools/skill-tools.d.ts +505 -0
  80. package/mcp-server/dist/tools/skill-tools.d.ts.map +1 -0
  81. package/mcp-server/dist/tools/skill-tools.js +639 -0
  82. package/mcp-server/dist/tools/skill-tools.js.map +1 -0
  83. package/mcp-server/dist/tools/skill.d.ts +7 -0
  84. package/mcp-server/dist/tools/skill.d.ts.map +1 -0
  85. package/mcp-server/dist/tools/skill.js +11 -0
  86. package/mcp-server/dist/tools/skill.js.map +1 -0
  87. package/mcp-server/dist/tools/tool-groups.d.ts +68 -0
  88. package/mcp-server/dist/tools/tool-groups.d.ts.map +1 -0
  89. package/mcp-server/dist/tools/tool-groups.js +147 -0
  90. package/mcp-server/dist/tools/tool-groups.js.map +1 -0
  91. package/mcp-server/dist/tools/whiteboard-format.d.ts.map +1 -1
  92. package/mcp-server/dist/tools/whiteboard-format.js +7 -4
  93. package/mcp-server/dist/tools/whiteboard-format.js.map +1 -1
  94. package/mcp-server/dist/tools/whiteboard-tools.d.ts +64 -13
  95. package/mcp-server/dist/tools/whiteboard-tools.d.ts.map +1 -1
  96. package/mcp-server/dist/tools/whiteboard-tools.js +30 -5
  97. package/mcp-server/dist/tools/whiteboard-tools.js.map +1 -1
  98. package/mcp-server/package-lock.json +2 -2
  99. package/mcp-server/package.json +1 -1
  100. package/mcp-server/src/http.ts +75 -12
  101. package/mcp-server/src/index.ts +147 -154
  102. package/mcp-server/src/preload.ts +12 -12
  103. package/mcp-server/src/services/codex-config-heal.ts +80 -29
  104. package/mcp-server/src/services/memory-judge-backfill.ts +7 -1
  105. package/mcp-server/src/services/neural-interface.ts +79 -20
  106. package/mcp-server/src/services/server-instructions.ts +27 -63
  107. package/mcp-server/src/services/tool-catalog.ts +403 -0
  108. package/mcp-server/src/services/tool-list-refresh-barrier.ts +33 -0
  109. package/mcp-server/src/services/tool-list.ts +410 -0
  110. package/mcp-server/src/services/tool-runtime.ts +457 -0
  111. package/mcp-server/src/services/whiteboard-geometry.ts +94 -9
  112. package/mcp-server/src/tools/agents.ts +22 -14
  113. package/mcp-server/src/tools/image-tools.ts +1 -1
  114. package/mcp-server/src/tools/skill-tools.ts +589 -0
  115. package/mcp-server/src/tools/skill.ts +13 -0
  116. package/mcp-server/src/tools/tool-groups.ts +164 -0
  117. package/mcp-server/src/tools/whiteboard-format.ts +9 -3
  118. package/mcp-server/src/tools/whiteboard-tools.ts +39 -5
  119. package/neural-interface/i18n/en.json +3463 -200
  120. package/neural-interface/i18n/pt-BR.json +3411 -151
  121. package/neural-interface/lib/assistant-api.js +127 -28
  122. package/neural-interface/lib/assistant-brains/claude.js +4 -3
  123. package/neural-interface/lib/assistant-brains/codex-browser-policy-hook.mjs +12 -2
  124. package/neural-interface/lib/assistant-brains/codex-route-gate-hook.mjs +32 -9
  125. package/neural-interface/lib/assistant-brains/codex.js +9 -5
  126. package/neural-interface/lib/assistant-brains/opencode.js +120 -54
  127. package/neural-interface/lib/assistant-brains/opencode2-browser-policy/index.js +28 -13
  128. package/neural-interface/lib/assistant-brains/opencode2-route-gate/index.js +59 -19
  129. package/neural-interface/lib/assistant-catalog.js +6 -7
  130. package/neural-interface/lib/assistant-dispatch.js +871 -156
  131. package/neural-interface/lib/assistant-envelope.js +3 -1
  132. package/neural-interface/lib/assistant-persona.js +80 -11
  133. package/neural-interface/lib/assistant-plan-permissions.js +10 -9
  134. package/neural-interface/lib/assistant-route-gate-codex.js +3 -0
  135. package/neural-interface/lib/assistant-route-gate.js +6 -4
  136. package/neural-interface/lib/assistant-router.js +13 -8
  137. package/neural-interface/lib/assistant-runtime.js +56 -18
  138. package/neural-interface/lib/assistant-task-prompt.js +78 -14
  139. package/neural-interface/lib/assistant-tools.js +51 -0
  140. package/neural-interface/lib/backup-service.js +620 -3
  141. package/neural-interface/lib/backup-zip-reader.js +10 -5
  142. package/neural-interface/lib/backup-zip-writer.js +257 -0
  143. package/neural-interface/lib/browser-execution.js +10 -2
  144. package/neural-interface/lib/browser-guard-cdp.js +83 -0
  145. package/neural-interface/lib/browser-network-guard.js +284 -0
  146. package/neural-interface/lib/browser-policy-roots.js +64 -0
  147. package/neural-interface/lib/browser-tool-policy.js +186 -31
  148. package/neural-interface/lib/claude-agent-bridge.js +4 -2
  149. package/neural-interface/lib/claude-hooks-status.js +103 -0
  150. package/neural-interface/lib/claude-temporary.js +5 -11
  151. package/neural-interface/lib/cli-install.js +18 -138
  152. package/neural-interface/lib/cli-resolver.js +4 -1
  153. package/neural-interface/lib/cli-update-planner.js +32 -31
  154. package/neural-interface/lib/cloudflared/tunnel.js +42 -2
  155. package/neural-interface/lib/codex-mcp-approvals.js +11 -0
  156. package/neural-interface/lib/codex-worker-app-server.js +172 -0
  157. package/neural-interface/lib/confined-fs.js +30 -3
  158. package/neural-interface/lib/crash-notice-api.js +98 -0
  159. package/neural-interface/lib/editor-completion-opencode.js +68 -68
  160. package/neural-interface/lib/exec-loop-command.js +4 -8
  161. package/neural-interface/lib/http-guards.js +8 -2
  162. package/neural-interface/lib/install-details.js +4 -1
  163. package/neural-interface/lib/mcp-agent-servers.js +67 -0
  164. package/neural-interface/lib/mcp-client-config.js +0 -29
  165. package/neural-interface/lib/mcp-external-servers.js +323 -0
  166. package/neural-interface/lib/{mcp-profile-refresh-coordinator.js → mcp-tools-refresh-coordinator.js} +4 -4
  167. package/neural-interface/lib/morelogin.js +30 -0
  168. package/neural-interface/lib/native-loop-providers.js +547 -47
  169. package/neural-interface/lib/native-loop-runtime.js +268 -41
  170. package/neural-interface/lib/opencode-config.js +557 -0
  171. package/neural-interface/lib/opencode-db-schema.js +17 -34
  172. package/neural-interface/lib/opencode-history.js +48 -218
  173. package/neural-interface/lib/opencode-mcp-ready.js +108 -0
  174. package/neural-interface/lib/opencode-native-boundary.js +44 -0
  175. package/neural-interface/lib/opencode-protocol.js +249 -112
  176. package/neural-interface/lib/opencode-runtime-config.js +32 -22
  177. package/neural-interface/lib/opencode-serve-records.js +39 -21
  178. package/neural-interface/lib/opencode-v2-client.js +48 -153
  179. package/neural-interface/lib/opencode-v2-ws-requests.js +36 -210
  180. package/neural-interface/lib/opencode-worker-data.js +27 -0
  181. package/neural-interface/lib/opencode2-client.js +73 -37
  182. package/neural-interface/lib/opencode2-proxy.js +23 -63
  183. package/neural-interface/lib/opencode2-translate.js +51 -2
  184. package/neural-interface/lib/owner-authority.js +249 -0
  185. package/neural-interface/lib/page-session.js +275 -0
  186. package/neural-interface/lib/peer-identity.js +763 -0
  187. package/neural-interface/lib/request-gate.js +17 -2
  188. package/neural-interface/lib/rulesets/api.js +1 -1
  189. package/neural-interface/lib/rulesets/fs-safe.js +1 -1
  190. package/neural-interface/lib/rulesets/installer.js +92 -27
  191. package/neural-interface/lib/rulesets/json-edit.js +1 -1
  192. package/neural-interface/lib/session-cache-builder.js +17 -30
  193. package/neural-interface/lib/session-title-generator.js +58 -36
  194. package/neural-interface/lib/skills/api-v2.js +495 -0
  195. package/neural-interface/lib/skills/bundle.js +418 -0
  196. package/neural-interface/lib/skills/catalog.js +294 -0
  197. package/neural-interface/lib/skills/creation-api.js +191 -0
  198. package/neural-interface/lib/skills/creation-brief.js +247 -0
  199. package/neural-interface/lib/skills/creation-sources.js +267 -0
  200. package/neural-interface/lib/skills/creation.js +843 -0
  201. package/neural-interface/lib/skills/deploy.js +564 -0
  202. package/neural-interface/lib/skills/digest.js +119 -0
  203. package/neural-interface/lib/skills/discover.js +111 -0
  204. package/neural-interface/lib/skills/draft-confinement.js +214 -0
  205. package/neural-interface/lib/skills/eval-confinement.js +397 -0
  206. package/neural-interface/lib/skills/eval-sandbox.js +280 -0
  207. package/neural-interface/lib/skills/eval-trace.js +119 -0
  208. package/neural-interface/lib/skills/evals-api.js +212 -0
  209. package/neural-interface/lib/skills/evals.js +1573 -0
  210. package/neural-interface/lib/skills/export.js +200 -0
  211. package/neural-interface/lib/skills/frontmatter.js +64 -0
  212. package/neural-interface/lib/skills/health.js +75 -0
  213. package/neural-interface/lib/skills/json-edit.js +165 -0
  214. package/neural-interface/lib/skills/known-bridge-digests.json +387 -0
  215. package/neural-interface/lib/skills/known-bridges.js +96 -0
  216. package/neural-interface/lib/skills/library.js +823 -0
  217. package/neural-interface/lib/skills/local-store.js +21 -0
  218. package/neural-interface/lib/skills/migration.js +433 -0
  219. package/neural-interface/lib/skills/operations.js +45 -0
  220. package/neural-interface/lib/skills/ownership.js +427 -0
  221. package/neural-interface/lib/skills/packages.js +88 -0
  222. package/neural-interface/lib/skills/reviews.js +212 -0
  223. package/neural-interface/lib/skills/revision.js +80 -0
  224. package/neural-interface/lib/skills/roots.js +262 -0
  225. package/neural-interface/lib/skills/runtime-checks.js +355 -0
  226. package/neural-interface/lib/skills/scan-assets.js +252 -0
  227. package/neural-interface/lib/skills/scan-context.js +26 -0
  228. package/neural-interface/lib/skills/scan-shell.js +324 -0
  229. package/neural-interface/lib/skills/scan.js +364 -0
  230. package/neural-interface/lib/skills/scanners.js +54 -0
  231. package/neural-interface/lib/skills/service.js +522 -0
  232. package/neural-interface/lib/skills/telemetry.js +113 -0
  233. package/neural-interface/lib/sqlite-integrity-worker.js +22 -0
  234. package/neural-interface/lib/tool-lists.js +124 -0
  235. package/neural-interface/lib/whatsapp/bridge.js +6 -3
  236. package/neural-interface/lib/worker-confinement.js +1192 -0
  237. package/neural-interface/lib/worker-guard-setting.js +56 -0
  238. package/neural-interface/lib/worker-mcp-channel.js +175 -0
  239. package/neural-interface/lib/worker-relay-ports.js +227 -0
  240. package/neural-interface/lib/worker-resume.js +229 -0
  241. package/neural-interface/lib/worker-sandbox.js +299 -0
  242. package/neural-interface/package-lock.json +18 -12
  243. package/neural-interface/package.json +2 -2
  244. package/neural-interface/public/index.html +3 -1
  245. package/neural-interface/public/index2d.html +19 -11
  246. package/neural-interface/public/onboarding.html +2 -2
  247. package/neural-interface/public/shared/account-menu.js +8 -5
  248. package/neural-interface/public/shared/agent-runtime-options.js +35 -19
  249. package/neural-interface/public/shared/api.js +1527 -1336
  250. package/neural-interface/public/shared/app-frame.js +192 -0
  251. package/neural-interface/public/shared/assistant/asst-agents-dock.js +33 -2
  252. package/neural-interface/public/shared/assistant/asst-ask-styles.js +270 -0
  253. package/neural-interface/public/shared/assistant/asst-ask-tail.js +118 -0
  254. package/neural-interface/public/shared/assistant/asst-ask.js +397 -0
  255. package/neural-interface/public/shared/assistant/asst-brain-picker.js +49 -35
  256. package/neural-interface/public/shared/assistant/asst-control.js +157 -26
  257. package/neural-interface/public/shared/assistant/asst-followups-card.js +383 -0
  258. package/neural-interface/public/shared/assistant/asst-followups.js +417 -0
  259. package/neural-interface/public/shared/assistant/asst-limit-dock.js +40 -0
  260. package/neural-interface/public/shared/assistant/asst-limit-wait.js +294 -0
  261. package/neural-interface/public/shared/assistant/asst-limit.js +168 -0
  262. package/neural-interface/public/shared/assistant/asst-mascot.js +49 -12
  263. package/neural-interface/public/shared/assistant/asst-menu.js +7 -6
  264. package/neural-interface/public/shared/assistant/asst-panel.js +106 -11
  265. package/neural-interface/public/shared/assistant/asst-rack-styles.js +66 -49
  266. package/neural-interface/public/shared/assistant/asst-render.js +401 -77
  267. package/neural-interface/public/shared/assistant/asst-route.js +83 -12
  268. package/neural-interface/public/shared/assistant/asst-scenes.js +2 -0
  269. package/neural-interface/public/shared/assistant/asst-sidepanel.js +67 -4
  270. package/neural-interface/public/shared/assistant/asst-slash.js +9 -7
  271. package/neural-interface/public/shared/assistant/asst-state.js +11 -15
  272. package/neural-interface/public/shared/assistant/asst-stop.js +127 -0
  273. package/neural-interface/public/shared/assistant/asst-styles.js +59 -21
  274. package/neural-interface/public/shared/assistant/asst-tool-kinds.js +156 -16
  275. package/neural-interface/public/shared/automation-context.js +5 -2
  276. package/neural-interface/public/shared/cc-dropdown.js +89 -19
  277. package/neural-interface/public/shared/cdx/cdx-context-menu.js +10 -9
  278. package/neural-interface/public/shared/cdx/cdx-copy.js +16 -0
  279. package/neural-interface/public/shared/cdx/cdx-output-model.js +22 -1
  280. package/neural-interface/public/shared/cdx/cdx-output.js +8 -7
  281. package/neural-interface/public/shared/cdx/cdx-panel.js +45 -149
  282. package/neural-interface/public/shared/cdx/cdx-protocol.js +28 -1
  283. package/neural-interface/public/shared/cdx/cdx-render.js +47 -29
  284. package/neural-interface/public/shared/cdx/cdx-requests.js +3 -3
  285. package/neural-interface/public/shared/cdx/cdx-skill-hints.js +54 -0
  286. package/neural-interface/public/shared/cdx/cdx-styles.js +11 -3
  287. package/neural-interface/public/shared/cdx/cdx-tabs.js +52 -39
  288. package/neural-interface/public/shared/cdx/cdx-ws.js +0 -1
  289. package/neural-interface/public/shared/cli-install-view.js +4 -8
  290. package/neural-interface/public/shared/cli-status.js +2 -2
  291. package/neural-interface/public/shared/constants.js +2 -2
  292. package/neural-interface/public/shared/context-menu-areas.js +34 -0
  293. package/neural-interface/public/shared/context-menu-logic.js +243 -0
  294. package/neural-interface/public/shared/context-menu-model.js +366 -0
  295. package/neural-interface/public/shared/context-menu.js +372 -0
  296. package/neural-interface/public/shared/cp/cp-context-menu.js +10 -9
  297. package/neural-interface/public/shared/crash-notice-view.js +168 -0
  298. package/neural-interface/public/shared/desktop-update-view.js +20 -0
  299. package/neural-interface/public/shared/editor/editor-panel-logic.js +2 -2
  300. package/neural-interface/public/shared/html-shell.js +9 -11
  301. package/neural-interface/public/shared/i18n.js +2 -0
  302. package/neural-interface/public/shared/ocp-v2/ocp-v2-approvals.js +7 -7
  303. package/neural-interface/public/shared/ocp-v2/ocp-v2-binding.js +4 -4
  304. package/neural-interface/public/shared/ocp-v2/ocp-v2-changes-logic.js +3 -101
  305. package/neural-interface/public/shared/ocp-v2/ocp-v2-childpanel.js +2 -0
  306. package/neural-interface/public/shared/ocp-v2/ocp-v2-compact-button.js +1 -3
  307. package/neural-interface/public/shared/ocp-v2/ocp-v2-composer-logic.js +2 -6
  308. package/neural-interface/public/shared/ocp-v2/ocp-v2-context-menu.js +11 -11
  309. package/neural-interface/public/shared/ocp-v2/ocp-v2-context-model.js +0 -3
  310. package/neural-interface/public/shared/ocp-v2/ocp-v2-events.js +8 -4
  311. package/neural-interface/public/shared/ocp-v2/ocp-v2-manager.js +7 -7
  312. package/neural-interface/public/shared/ocp-v2/ocp-v2-outcome-notifications.js +16 -0
  313. package/neural-interface/public/shared/ocp-v2/ocp-v2-panel.js +47 -252
  314. package/neural-interface/public/shared/ocp-v2/ocp-v2-plan.js +1 -2
  315. package/neural-interface/public/shared/ocp-v2/ocp-v2-projectbar.js +95 -202
  316. package/neural-interface/public/shared/ocp-v2/ocp-v2-render.js +6 -21
  317. package/neural-interface/public/shared/ocp-v2/ocp-v2-send.js +4 -4
  318. package/neural-interface/public/shared/ocp-v2/ocp-v2-session-actions.js +8 -61
  319. package/neural-interface/public/shared/ocp-v2/ocp-v2-sessions-logic.js +2 -91
  320. package/neural-interface/public/shared/ocp-v2/ocp-v2-slash-hints.js +16 -15
  321. package/neural-interface/public/shared/ocp-v2/ocp-v2-state.js +8 -21
  322. package/neural-interface/public/shared/ocp-v2/ocp-v2-status-logic.js +1 -99
  323. package/neural-interface/public/shared/ocp-v2/ocp-v2-status.js +5 -35
  324. package/neural-interface/public/shared/ocp-v2/ocp-v2-styles.js +0 -21
  325. package/neural-interface/public/shared/ocp-v2/{ocp-v2-profile-inheritance.js → ocp-v2-tools-inheritance.js} +16 -16
  326. package/neural-interface/public/shared/ocp-v2/ocp-v2-ws.js +14 -47
  327. package/neural-interface/public/shared/page-session.js +96 -0
  328. package/neural-interface/public/shared/registry.js +5 -3
  329. package/neural-interface/public/shared/retired-2d.js +3 -0
  330. package/neural-interface/public/shared/schedules-view.js +195 -61
  331. package/neural-interface/public/shared/settings/settings-claude-hooks.js +135 -0
  332. package/neural-interface/public/shared/settings/settings-data.js +3 -1
  333. package/neural-interface/public/shared/settings/settings-ia.js +6 -6
  334. package/neural-interface/public/shared/settings/settings-opencode.js +89 -0
  335. package/neural-interface/public/shared/settings/settings-skill-folders-pane.js +265 -0
  336. package/neural-interface/public/shared/settings/settings-skill-folders.js +130 -0
  337. package/neural-interface/public/shared/settings/settings-worker-guard.js +72 -0
  338. package/neural-interface/public/shared/skills/ss-args.js +96 -0
  339. package/neural-interface/public/shared/skills/ss-creation.js +219 -0
  340. package/neural-interface/public/shared/skills/ss-dialog.js +130 -0
  341. package/neural-interface/public/shared/skills/ss-doc.js +173 -0
  342. package/neural-interface/public/shared/skills/ss-eval-adapters.js +269 -0
  343. package/neural-interface/public/shared/skills/ss-eval-grade.js +359 -0
  344. package/neural-interface/public/shared/skills/ss-eval-optimise.js +153 -0
  345. package/neural-interface/public/shared/skills/ss-eval-plan.js +315 -0
  346. package/neural-interface/public/shared/skills/ss-evals.js +417 -0
  347. package/neural-interface/public/shared/skills/ss-form.js +159 -0
  348. package/neural-interface/public/shared/skills/ss-frontmatter.js +376 -0
  349. package/neural-interface/public/shared/skills/ss-ids.js +32 -0
  350. package/neural-interface/public/shared/skills/ss-lint.js +141 -0
  351. package/neural-interface/public/shared/skills/ss-presets.js +117 -0
  352. package/neural-interface/public/shared/skills/ss-profiles.js +475 -0
  353. package/neural-interface/public/shared/skills/ss-resources.js +165 -0
  354. package/neural-interface/public/shared/skills/ss-review-view.js +190 -0
  355. package/neural-interface/public/shared/skills/ss-tabs.js +79 -0
  356. package/neural-interface/public/shared/skills/ss-unicode.js +135 -0
  357. package/neural-interface/public/shared/skills/studio/sx-actions.js +222 -0
  358. package/neural-interface/public/shared/skills/studio/sx-cm.js +101 -0
  359. package/neural-interface/public/shared/skills/studio/sx-create-ai-view.js +749 -0
  360. package/neural-interface/public/shared/skills/studio/sx-create-ai.js +236 -0
  361. package/neural-interface/public/shared/skills/studio/sx-create-view.js +254 -0
  362. package/neural-interface/public/shared/skills/studio/sx-create.js +126 -0
  363. package/neural-interface/public/shared/skills/studio/sx-diff-view.js +43 -0
  364. package/neural-interface/public/shared/skills/studio/sx-diff.js +133 -0
  365. package/neural-interface/public/shared/skills/studio/sx-docs.js +480 -0
  366. package/neural-interface/public/shared/skills/studio/sx-dom.js +229 -0
  367. package/neural-interface/public/shared/skills/studio/sx-edit-view.js +848 -0
  368. package/neural-interface/public/shared/skills/studio/sx-editor-model.js +193 -0
  369. package/neural-interface/public/shared/skills/studio/sx-errors.js +22 -0
  370. package/neural-interface/public/shared/skills/studio/sx-geometry.js +82 -0
  371. package/neural-interface/public/shared/skills/studio/sx-import.js +262 -0
  372. package/neural-interface/public/shared/skills/studio/sx-inspector.js +308 -0
  373. package/neural-interface/public/shared/skills/studio/sx-install-flow.js +279 -0
  374. package/neural-interface/public/shared/skills/studio/sx-install.js +296 -0
  375. package/neural-interface/public/shared/skills/studio/sx-library-view.js +639 -0
  376. package/neural-interface/public/shared/skills/studio/sx-library.js +475 -0
  377. package/neural-interface/public/shared/skills/studio/sx-main.js +681 -0
  378. package/neural-interface/public/shared/skills/studio/sx-operations-view.js +98 -0
  379. package/neural-interface/public/shared/skills/studio/sx-operations.js +11 -0
  380. package/neural-interface/public/shared/skills/studio/sx-plan.js +158 -0
  381. package/neural-interface/public/shared/skills/studio/sx-publish-view.js +621 -0
  382. package/neural-interface/public/shared/skills/studio/sx-review-view.js +369 -0
  383. package/neural-interface/public/shared/skills/studio/sx-review.js +148 -0
  384. package/neural-interface/public/shared/skills/studio/sx-scan.js +35 -0
  385. package/neural-interface/public/shared/skills/studio/sx-snippets.js +96 -0
  386. package/neural-interface/public/shared/skills/studio/sx-subject.js +248 -0
  387. package/neural-interface/public/shared/skills/studio/sx-test-view.js +994 -0
  388. package/neural-interface/public/shared/skills/studio/sx-test.js +328 -0
  389. package/neural-interface/public/shared/skills/studio/sx-versions-view.js +241 -0
  390. package/neural-interface/public/shared/skills/studio/sx-workspace-view.js +425 -0
  391. package/neural-interface/public/shared/styleguide/sg-panel.js +4 -2
  392. package/neural-interface/public/shared/styles.css +1525 -1779
  393. package/neural-interface/public/shared/synabun-mascot.js +1115 -9
  394. package/neural-interface/public/shared/tool-list-picker.js +55 -0
  395. package/neural-interface/public/shared/tool-list-storage.js +24 -0
  396. package/neural-interface/public/shared/tool-list-view.js +112 -0
  397. package/neural-interface/public/shared/tour/tour-actor.js +268 -0
  398. package/neural-interface/public/shared/tour/tour-beats.js +336 -0
  399. package/neural-interface/public/shared/tour/tour-engine.js +884 -0
  400. package/neural-interface/public/shared/tour/tour-ink.js +390 -0
  401. package/neural-interface/public/shared/tour/tour-stage.js +440 -0
  402. package/neural-interface/public/shared/tour/tour-state.js +40 -0
  403. package/neural-interface/public/shared/tour/tour-tips.js +99 -0
  404. package/neural-interface/public/shared/tour/tour.css +144 -0
  405. package/neural-interface/public/shared/ui-automation-studio.js +91 -128
  406. package/neural-interface/public/shared/ui-claude-panel.js +77 -5
  407. package/neural-interface/public/shared/ui-command-runner.js +6 -3
  408. package/neural-interface/public/shared/ui-context-menu.js +295 -0
  409. package/neural-interface/public/shared/ui-cost-widget.js +57 -59
  410. package/neural-interface/public/shared/ui-crash-notice.js +187 -0
  411. package/neural-interface/public/shared/ui-detail.js +15 -3
  412. package/neural-interface/public/shared/ui-image-gallery.js +50 -47
  413. package/neural-interface/public/shared/ui-keybinds.js +26 -1
  414. package/neural-interface/public/shared/ui-menubar.js +8 -18
  415. package/neural-interface/public/shared/ui-native-loop-router-state.js +5 -0
  416. package/neural-interface/public/shared/ui-native-loop-router.js +7 -1
  417. package/neural-interface/public/shared/ui-navbar.js +4 -24
  418. package/neural-interface/public/shared/ui-panels.js +96 -65
  419. package/neural-interface/public/shared/ui-schedules-studio.js +507 -423
  420. package/neural-interface/public/shared/ui-sessions.js +5 -3
  421. package/neural-interface/public/shared/ui-settings.js +594 -462
  422. package/neural-interface/public/shared/ui-sidepanel-sessions.js +1 -1
  423. package/neural-interface/public/shared/ui-sidepanel-state.js +23 -12
  424. package/neural-interface/public/shared/ui-sidepanel-windows.js +16 -11
  425. package/neural-interface/public/shared/ui-skills.js +33 -2830
  426. package/neural-interface/public/shared/ui-styleguide.js +5 -2
  427. package/neural-interface/public/shared/ui-sync.js +15 -5
  428. package/neural-interface/public/shared/ui-terminal.js +124 -180
  429. package/neural-interface/public/shared/ui-tool-updates.js +3 -1
  430. package/neural-interface/public/shared/ui-tooltip.js +10 -5
  431. package/neural-interface/public/shared/ui-trash.js +6 -4
  432. package/neural-interface/public/shared/ui-tutorial-layout.js +3 -117
  433. package/neural-interface/public/shared/ui-tutorial-preview.js +103 -0
  434. package/neural-interface/public/shared/ui-tutorial-steps.js +3 -65
  435. package/neural-interface/public/shared/ui-tutorial.css +47 -100
  436. package/neural-interface/public/shared/ui-tutorial.js +285 -509
  437. package/neural-interface/public/shared/ui-update.js +38 -2
  438. package/neural-interface/public/shared/ui-whiteboard.js +311 -58
  439. package/neural-interface/public/shared/ui-worker-end.js +4 -0
  440. package/neural-interface/public/shared/ui-workspaces.js +3 -2
  441. package/neural-interface/public/shared/view-variant.js +66 -0
  442. package/neural-interface/public/shared/whatsapp/wa-view.js +1 -1
  443. package/neural-interface/public/shared/whiteboard-list.js +172 -0
  444. package/neural-interface/public/shared/worker-end.js +10 -3
  445. package/neural-interface/public/shared/worker-restart.js +12 -0
  446. package/neural-interface/public/sidepanel-session.html +1 -1
  447. package/neural-interface/public/variant/2d/background.js +2 -1
  448. package/neural-interface/public/variant/2d/context-menu.js +8 -2
  449. package/neural-interface/public/variant/2d/graph.js +42 -20
  450. package/neural-interface/public/variant/2d/hulls.js +14 -9
  451. package/neural-interface/public/variant/2d/lasso.js +11 -7
  452. package/neural-interface/public/variant/2d/main.js +32 -1
  453. package/neural-interface/public/variant/2d/minimap.js +11 -13
  454. package/neural-interface/public/variant/3d/main.js +69 -6
  455. package/neural-interface/public/variant/3d/map-controls.js +24 -9
  456. package/neural-interface/public/vendor/caveat/OFL.txt +93 -0
  457. package/neural-interface/public/vendor/caveat/caveat-latin-400-normal.woff2 +0 -0
  458. package/neural-interface/public/vendor/caveat/caveat-latin-700-normal.woff2 +0 -0
  459. package/neural-interface/public/vendor/caveat/caveat.css +17 -0
  460. package/neural-interface/public/vendor/caveat/manifest.json +12 -0
  461. package/neural-interface/public/vendor/yaml/LICENSE +13 -0
  462. package/neural-interface/public/vendor/yaml/manifest.json +4 -0
  463. package/neural-interface/public/vendor/yaml/yaml-2.9.1.min.js +137 -0
  464. package/neural-interface/scripts/build-vendor.mjs +32 -0
  465. package/neural-interface/server.js +3934 -2454
  466. package/neural-interface/templates/rulesets/core.md +1 -1
  467. package/neural-interface/templates/rulesets/legacy-hashes.json +18 -0
  468. package/neural-interface/templates/rulesets/manifest.json +8 -8
  469. package/npm-shrinkwrap.json +18 -12
  470. package/package.json +16 -3
  471. package/postinstall.js +9 -4
  472. package/preuninstall.js +1 -1
  473. package/setup.js +97 -322
  474. package/skills/skill-forge/SKILL.md +177 -0
  475. package/skills/skill-forge/evals/evals.json +54 -0
  476. package/skills/skill-forge/references/package.md +105 -0
  477. package/skills/skill-forge/references/review-and-recovery.md +63 -0
  478. package/skills/skill-forge/references/runtime-profiles.md +20 -0
  479. package/skills/skill-forge/references/sources-and-capabilities.md +51 -0
  480. package/skills/synabun/SKILL.md +10 -7
  481. package/skills/synabun/modules/audit.md +1 -5
  482. package/skills/synabun/modules/changelog.md +1 -5
  483. package/skills/synabun/modules/idea.md +1 -1
  484. package/skills/synabun/modules/memorize.md +6 -9
  485. package/skills/synabun/modules/schedule.md +4 -4
  486. package/mcp-server/dist/services/profiles.d.ts +0 -120
  487. package/mcp-server/dist/services/profiles.d.ts.map +0 -1
  488. package/mcp-server/dist/services/profiles.js +0 -387
  489. package/mcp-server/dist/services/profiles.js.map +0 -1
  490. package/mcp-server/dist/tools/leonardo-browser-tools.d.ts +0 -97
  491. package/mcp-server/dist/tools/leonardo-browser-tools.d.ts.map +0 -1
  492. package/mcp-server/dist/tools/leonardo-browser-tools.js +0 -228
  493. package/mcp-server/dist/tools/leonardo-browser-tools.js.map +0 -1
  494. package/mcp-server/dist/tools/leonardo.d.ts +0 -13
  495. package/mcp-server/dist/tools/leonardo.d.ts.map +0 -1
  496. package/mcp-server/dist/tools/leonardo.js +0 -21
  497. package/mcp-server/dist/tools/leonardo.js.map +0 -1
  498. package/mcp-server/dist/tools/profile.d.ts +0 -17
  499. package/mcp-server/dist/tools/profile.d.ts.map +0 -1
  500. package/mcp-server/dist/tools/profile.js +0 -114
  501. package/mcp-server/dist/tools/profile.js.map +0 -1
  502. package/mcp-server/src/services/profiles.ts +0 -428
  503. package/mcp-server/src/tools/leonardo-browser-tools.ts +0 -291
  504. package/mcp-server/src/tools/leonardo.ts +0 -30
  505. package/mcp-server/src/tools/profile.ts +0 -122
  506. package/neural-interface/lib/assistant-brains/opencode-browser-policy.js +0 -47
  507. package/neural-interface/lib/assistant-brains/opencode-route-gate.js +0 -62
  508. package/neural-interface/lib/mcp-profile-servers.js +0 -120
  509. package/neural-interface/public/shared/ui-tutorial-draw.js +0 -311
  510. package/skills/leonardo/SKILL.md +0 -168
  511. package/skills/leonardo/icon.svg +0 -1
  512. package/skills/leonardo/modules/browser-guide.md +0 -378
  513. package/skills/leonardo/modules/image-prompter.md +0 -283
  514. package/skills/leonardo/modules/model-advisor.md +0 -100
  515. package/skills/leonardo/modules/prompt-library.md +0 -110
  516. package/skills/leonardo/modules/style-guide.md +0 -186
  517. package/skills/leonardo/modules/video-prompter.md +0 -317
@@ -0,0 +1,397 @@
1
+ // ── The confinement of a skill-evaluation run (P4) ──
2
+ // An evaluation (lib/skills/evals.js) runs untrusted skill text with real models: a skill under test can tell the
3
+ // model anything. So every run of an evaluation is held to its own job folder, the way a drafting run is
4
+ // (lib/skills/draft-confinement.js), by what each provider can enforce, and it never sees SynaBun's MCP server, the
5
+ // owner's skills or the owner's runtime folders: the skill under test is copied into the run's own folder, in the
6
+ // project skill folder its runtime reads (EVAL_SKILL_ROOTS), and nothing is ever installed for a test.
7
+ //
8
+ // role what it does its tools (Claude Code names)
9
+ // trial the case's request, with or without the skill Read, Glob, Grep in its folder; Write, Edit in its folder
10
+ // but never in a skill folder (.claude, .agents, .opencode);
11
+ // Skill; Bash only when `shell` (the case asks for one AND
12
+ // the run is under the OS sandbox: server.js wraps it)
13
+ // grader grades one answer against its assertions Read, Glob, Grep in its folder; its JSON answer
14
+ // judge compares two answers blind the same
15
+ // proposer proposes a description (the optimiser) the same
16
+ //
17
+ // Claude Code: `tools` is exactly the list above, no MCP server (strictMcpConfig), no settings file at all
18
+ // (settingSources []: no project, user or local settings, no CLAUDE.md), hooks and a skill's inline shell switched off
19
+ // in the flag settings (disableAllHooks, disableSkillShellExecution: a skill's own frontmatter hooks and `!` commands
20
+ // never run; SynaBun's SDK hooks still do), the skill under test loaded as the one local plugin `workspace` from the
21
+ // run's .claude folder (`skills` names it, so nothing else is in its catalog; a baseline loads none), and every call
22
+ // checked twice (a PreToolUse hook Claude Code runs before any permission mode, and canUseTool). Codex: its sandbox
23
+ // (workspace-write in the folder for a trial, read-only otherwise), no network, no web search, every MCP server
24
+ // disabled; it keeps its shell, which its sandbox holds to writes in the folder but not to reads: Codex is the owner's
25
+ // opt-in per skill version (lib/skills/evals.js). OpenCode: no project configuration (OPENCODE_DISABLE_PROJECT_CONFIG:
26
+ // no opencode.json, no .opencode plugin, agent, command or tool of the folder, no AGENTS.md at its top), no formatter
27
+ // and no snapshot (OPENCODE_EVAL_SETTINGS), the skill under test copied into the run's own configuration folder
28
+ // (placeOpenCodeEvalSkill), Code Mode, web, subagents, todo, LSP and question tools off, the shell off unless `shell`,
29
+ // the skill tool on for a trial only, no MCP server, reads outside the folder denied, and every edit asked and answered
30
+ // inside the folder (evalOpenCodeDecision). Every run gets git's safety settings (evalGitSafetyEnv). A shell, where a
31
+ // case asks, runs under the evaluation's own macOS sandbox (lib/skills/eval-sandbox.js). A run whose confinement
32
+ // cannot be set up never starts (CONFINEMENT_UNSUPPORTED), and it is never escalated to another provider.
33
+
34
+ import { constants as fsConstants, copyFileSync, lstatSync, mkdirSync, readdirSync, realpathSync, rmSync, writeFileSync } from 'node:fs';
35
+ import { dirname, isAbsolute, join, relative, resolve, sep } from 'node:path';
36
+ import { isInside, realPathOf } from '../path-confine.js';
37
+ import { DRAFT_DISALLOWED_TOOLS, codexMcpServerNames } from './draft-confinement.js';
38
+ import { evalConfigPathProblem } from '../../public/shared/skills/ss-evals.js';
39
+
40
+ export const EVAL_CONFINEMENT = 'skill-eval';
41
+ /** Part of every run's experiment identity: a change to what an evaluation run may do is a new version. */
42
+ export const EVAL_CONFINEMENT_VERSION = 'skill-eval/5';
43
+ export const EVAL_ROLES = Object.freeze(['trial', 'grader', 'judge', 'proposer']);
44
+ /** Where a trial's copy of the skill goes in its folder: the project skill folder each runtime reads. */
45
+ export const EVAL_SKILL_ROOTS = Object.freeze({ 'claude-code': '.claude/skills', codex: '.agents/skills', opencode: '.opencode/skills' });
46
+ /** The skill folders of a trial (SynaBun writes them; no tool does: a skill folder written mid-run would change what the
47
+ * runtime sees). Every other folder or file a runtime reads its settings or code from is refused the same way
48
+ * (ss-evals.js evalConfigPathProblem); an instruction file (CLAUDE.md, AGENTS.md) is a file a trial may write. */
49
+ export const EVAL_PROTECTED_DIRS = Object.freeze(['.claude', '.agents', '.opencode', '.codex']);
50
+ /** The local plugin a Claude Code trial loads the skill under test from (its catalog names it workspace:<skill>). */
51
+ export const EVAL_CLAUDE_PLUGIN = 'workspace';
52
+ /** Claude Code's flag settings for every evaluation run: no hook of any settings file, plugin or skill, no inline shell. */
53
+ export const EVAL_CLAUDE_SETTINGS = Object.freeze({ disableAllHooks: true, disableSkillShellExecution: true });
54
+
55
+ /**
56
+ * git's settings for every evaluation run's process tree (the runtime and what it starts outside the evaluation's
57
+ * shell sandbox: Claude Code's and OpenCode's own git calls, Codex and its shell). The job folder never holds a
58
+ * repository: no `.git` is copied there (ss-evals.js), written there by a tool (evalToolDecision) or by a shell step
59
+ * (lib/skills/eval-sandbox.js, whose git keeps its repository outside the folder). On top of that, git here never looks
60
+ * above the job folder for one (GIT_CEILING_DIRECTORIES), never takes a folder as a bare repository it was not pointed
61
+ * at (a HEAD, objects/ and refs/ left in the folder: safe.bareRepository), reads no system and no global configuration
62
+ * (GIT_CONFIG_NOSYSTEM, GIT_CONFIG_GLOBAL=/dev/null: no ~/.gitconfig or $XDG_CONFIG_HOME/git/config, wherever HOME and
63
+ * XDG_CONFIG_HOME point), and runs no fsmonitor program and no hook (command-line scope: what a repository's own
64
+ * configuration cannot override). So no configuration a run could have written is read by the runtime's git.
65
+ */
66
+ export function evalGitSafetyEnv(root) {
67
+ return {
68
+ // withServeEnv clears the prefix first, then installs only these deliberate settings. A list of known names
69
+ // misses alternate object/index paths and command-line config (including future GIT_* variables).
70
+ 'GIT_*': null,
71
+ GIT_CEILING_DIRECTORIES: dirname(String(root)),
72
+ GIT_CONFIG_NOSYSTEM: '1',
73
+ GIT_CONFIG_GLOBAL: '/dev/null',
74
+ GIT_CONFIG_COUNT: '3',
75
+ GIT_CONFIG_KEY_0: 'safe.bareRepository', GIT_CONFIG_VALUE_0: 'explicit',
76
+ GIT_CONFIG_KEY_1: 'core.fsmonitor', GIT_CONFIG_VALUE_1: 'false',
77
+ GIT_CONFIG_KEY_2: 'core.hooksPath', GIT_CONFIG_VALUE_2: '/dev/null',
78
+ };
79
+ }
80
+ export const EVAL_TRIAL_TOOLS = Object.freeze(['Read', 'Write', 'Edit', 'Glob', 'Grep', 'Skill']);
81
+ export const EVAL_READER_TOOLS = Object.freeze(['Read', 'Glob', 'Grep', 'StructuredOutput']);
82
+ const READERS = new Set(['Read', 'Glob', 'Grep']);
83
+ const WRITERS = new Set(['Write', 'Edit']);
84
+ const REFUSED = 'An evaluation run works only inside its own folder: no web, no MCP tools, no subagents.';
85
+
86
+ /** The providers an evaluation can run on, in the order the Test tab lists them. */
87
+ export const EVAL_CONFINED_PROVIDERS = Object.freeze(['claude-code', 'codex', 'opencode']);
88
+
89
+ /**
90
+ * What a provider can hold an evaluation run to: { ok, level, note, shell } or { ok: false, reason }. `shell`:
91
+ * 'sandbox' (a shell only under the OS sandbox: macOS, Protect workers = Enforce), 'own-sandbox' (Codex's own, always
92
+ * on), 'never'.
93
+ */
94
+ export function evalConfinementSupport(provider, { platform = process.platform } = {}) {
95
+ const name = String(provider || '');
96
+ if (name === 'claude-code') return { ok: true, level: 'strict', shell: platform === 'darwin' ? 'sandbox' : 'never', optIn: false, note: 'Read, Write, Edit, Glob, Grep and Skill inside its folder; no settings file, hook or plugin but the skill under test; a shell only under the evaluation\'s macOS sandbox (no network, no home folder); no web, no MCP tools, no subagents.' };
97
+ if (name === 'codex') return { ok: true, level: 'sandbox', shell: 'own-sandbox', optIn: true, note: `Codex keeps its shell under its ${platform === 'win32' ? 'Windows' : platform === 'darwin' ? 'Seatbelt' : 'Linux'} sandbox: writes inside its folder only, no network, no web search, no MCP server; its shell can read files anywhere on this computer, and what it reads can reach its model provider.` };
98
+ if (name === 'opencode') return { ok: true, level: 'rules', shell: platform === 'darwin' ? 'sandbox' : 'never', optIn: false, note: 'OpenCode with no project configuration or plugin of the folder, its web, subagent and todo tools off, no MCP server, reads and edits inside its folder only; a shell only under the evaluation\'s macOS sandbox (no network, no home folder).' };
99
+ return { ok: false, reason: `${name || 'That provider'} cannot be held to an evaluation folder.` };
100
+ }
101
+
102
+ /**
103
+ * A confinement as a run carries it, checked: { kind, role, shell, root (the folder's real path), protected (the
104
+ * skill folders' places), skill (the name under test, or null) }. Throws when it cannot be used: the run must not
105
+ * start unconfined.
106
+ */
107
+ export function resolveEvalConfinement(confinement, cwd) {
108
+ if (!confinement || confinement.kind !== EVAL_CONFINEMENT) throw new Error('Unknown confinement.');
109
+ const role = EVAL_ROLES.includes(confinement.role) ? confinement.role : null;
110
+ if (!role) throw new Error('Unknown evaluation role.');
111
+ const root = realpathSync(String(cwd || ''));
112
+ if (!lstatSync(root).isDirectory()) throw new Error('The evaluation folder is not a folder.');
113
+ const skill = typeof confinement.skill === 'string' && /^[a-z0-9][a-z0-9._-]{0,63}$/.test(confinement.skill) ? confinement.skill : null;
114
+ return { kind: EVAL_CONFINEMENT, role, shell: role === 'trial' && confinement.shell === true, root, trialDir: dirname(root), protected: EVAL_PROTECTED_DIRS.map((d) => join(root, d)), skill };
115
+ }
116
+
117
+ function placeOf(raw, root) {
118
+ if (typeof raw !== 'string' || !raw || raw.length > 4096 || /[\u0000-\u001f\u007f]/.test(raw) || raw.startsWith('~')) return null;
119
+ if (raw.split(/[\\/]+/).includes('..')) return null;
120
+ const abs = isAbsolute(raw) ? resolve(raw) : resolve(root, raw);
121
+ try { return realPathOf(abs); } catch { return null; }
122
+ }
123
+ function patternProblem(p) {
124
+ if (p === undefined || p === null) return null;
125
+ if (typeof p !== 'string' || p.length > 1000 || /[\u0000-\u001f\u007f]/.test(p)) return 'is not a plain pattern';
126
+ if (p.startsWith('/') || p.startsWith('\\') || p.startsWith('~') || /^[A-Za-z]:/.test(p)) return 'is absolute: patterns are relative to the evaluation folder';
127
+ if (p.split(/[\\/]+/).includes('..')) return 'climbs out with "..": patterns stay inside the evaluation folder';
128
+ return null;
129
+ }
130
+ const inProtected = (confined, at) => confined.protected.some((dir) => at === dir || isInside(dir, at));
131
+ /** Why a place of the folder holds a runtime's settings or code (a skill folder, .git, opencode.json, …), or null. */
132
+ function configPlace(confined, at) {
133
+ if (inProtected(confined, at)) return 'a skill folder (.claude, .agents, .opencode, .codex): the skill under test stays as it is';
134
+ const rel = relative(confined.root, at).split(sep).join('/');
135
+ const why = rel ? evalConfigPathProblem(rel) : null;
136
+ return why ? `${why.name}, which ${why.who || 'a runtime'} reads as its own settings or code` : null;
137
+ }
138
+
139
+ /**
140
+ * Whether an evaluation run may make this tool call: null when it may, else why not (the model reads the reason).
141
+ * @param {string} toolName @param {object} input @param {object} confined resolveEvalConfinement()'s answer
142
+ */
143
+ export function evalToolDecision(toolName, input, confined) {
144
+ try {
145
+ const name = String(toolName || '');
146
+ const args = input && typeof input === 'object' ? input : {};
147
+ const { root, role } = confined || {};
148
+ if (!root || !role) return 'This run\'s evaluation folder is not known: nothing can be done.';
149
+ if (name === 'StructuredOutput' && role !== 'trial') return null; // the answer itself (an output schema), no file access
150
+ const trial = role === 'trial';
151
+ if (trial && name === 'Skill') return null; // the catalog is the run's own folder: only the skill under test is in it
152
+ if (trial && name === 'Bash') return confined.shell ? null : `Bash is not available in this evaluation (the case does not ask for a shell, or no OS sandbox holds one). ${REFUSED}`;
153
+ if (!READERS.has(name) && !(trial && WRITERS.has(name))) return `${name || 'That tool'} is not available. ${REFUSED}`;
154
+ if (name === 'Read') {
155
+ const at = placeOf(args.file_path, root);
156
+ if (!at || !isInside(root, at)) return `Read is limited to this run's folder (${root}). ${REFUSED}`;
157
+ return null;
158
+ }
159
+ if (name === 'Glob' || name === 'Grep') {
160
+ if (args.path !== undefined && args.path !== null && args.path !== '') {
161
+ const at = placeOf(args.path, root);
162
+ if (!at || !isInside(root, at, { allowRoot: true })) return `${name} searches this run's folder only (${root}).`;
163
+ }
164
+ if (name === 'Glob' && (typeof args.pattern !== 'string' || !args.pattern)) return 'Glob needs a pattern.';
165
+ const bad = name === 'Glob' ? patternProblem(args.pattern) : patternProblem(args.glob);
166
+ if (bad) return `The ${name === 'Glob' ? 'pattern' : 'glob'} ${bad}.`;
167
+ return null;
168
+ }
169
+ // Write, Edit (trials only): a plain file inside the folder, never in a skill folder.
170
+ const at = placeOf(args.file_path, root);
171
+ if (!at || !isInside(root, at)) return `${name} writes inside this run's folder only (${root}). ${REFUSED}`;
172
+ const place = configPlace(confined, at);
173
+ if (place) return `${name} cannot write into ${place}.`;
174
+ let st = null;
175
+ try { st = lstatSync(at); } catch { st = null; }
176
+ if (st && (!st.isFile() || st.nlink > 1)) return `${at} is not a plain file of this run: it cannot be written.`;
177
+ return null;
178
+ } catch (err) {
179
+ return `The call could not be checked (${err?.message || err}), so it is refused.`;
180
+ }
181
+ }
182
+
183
+ /** The PreToolUse hook an evaluation run gets (no matcher: every call). */
184
+ export function evalPreToolUseHook(confined) {
185
+ return async (input) => {
186
+ const reason = evalToolDecision(input?.tool_name, input?.tool_input || {}, confined);
187
+ return reason ? { hookSpecificOutput: { hookEventName: 'PreToolUse', permissionDecision: 'deny', permissionDecisionReason: reason } } : {};
188
+ };
189
+ }
190
+
191
+ /**
192
+ * Claude Code's options for an evaluation run: the tools, what is removed by name, no settings file of any source,
193
+ * the flag settings that switch hooks and inline shell off, the one plugin (a trial with the skill: the run's .claude
194
+ * folder, where SynaBun copied the skill and wrote the plugin's manifest; anything else: none), the skills it may use
195
+ * (that one, as workspace:<skill>; a baseline and the helpers: none) and git's safety settings for its environment.
196
+ */
197
+ export function evalClaudeOptions(confined) {
198
+ const trial = confined.role === 'trial';
199
+ const tools = trial ? [...EVAL_TRIAL_TOOLS, ...(confined.shell ? ['Bash'] : [])] : [...EVAL_READER_TOOLS];
200
+ const disallowed = DRAFT_DISALLOWED_TOOLS.filter((t) => !tools.includes(t));
201
+ const withSkill = trial && !!confined.skill;
202
+ return {
203
+ tools, disallowedTools: disallowed, settingSources: [], settings: { ...EVAL_CLAUDE_SETTINGS },
204
+ plugins: withSkill ? [{ type: 'local', path: join(confined.root, '.claude') }] : [],
205
+ skills: withSkill ? [`${EVAL_CLAUDE_PLUGIN}:${confined.skill}`] : [],
206
+ env: evalGitSafetyEnv(confined.root),
207
+ };
208
+ }
209
+
210
+ /** The manifest of a trial's Claude Code plugin (<root>/.claude/.claude-plugin/plugin.json): its name, nothing else. */
211
+ export function evalClaudePluginManifest() {
212
+ return `${JSON.stringify({ name: EVAL_CLAUDE_PLUGIN, version: '1.0.0', description: 'The skill under test (a SynaBun evaluation).' }, null, 2)}\n`;
213
+ }
214
+
215
+ /**
216
+ * Codex: the thread options and the config overrides of an evaluation run. A trial writes in its folder (the
217
+ * sandbox's writable root, no parent folder); the others read only. No network, no web search, every MCP server the
218
+ * account's config declares (and SynaBun's) disabled.
219
+ */
220
+ export function codexEvalOptions(confined, { codexHome = null, projectDir = null } = {}) {
221
+ if (!confined || confined.kind !== EVAL_CONFINEMENT) throw new Error('Unknown confinement.');
222
+ const servers = new Set(['SynaBun', ...codexMcpServerNames(codexHome), ...(projectDir ? codexMcpServerNames(join(String(projectDir), '.codex')) : [])]);
223
+ const mcp_servers = {};
224
+ for (const name of servers) mcp_servers[name] = { enabled: false };
225
+ return {
226
+ thread: { sandboxMode: confined.role === 'trial' ? 'workspace-write' : 'read-only', networkAccessEnabled: false, webSearchMode: 'disabled', additionalDirectories: undefined },
227
+ config: { mcp_servers },
228
+ };
229
+ }
230
+
231
+ /**
232
+ * OpenCode's tools an evaluation run does without: refused by its rules (OpenCode 2's permission actions; a tool
233
+ * denied everywhere leaves the model's tool list). `execute` is Code Mode, which reaches the `opencode` namespace
234
+ * (session_move to another folder, worktree_create / worktree_remove, MCP resources) and the browser; `question`
235
+ * would wait for an answer nobody gives. What is left is Claude Code's list: read, write, edit, glob, grep, skill
236
+ * (and the shell when the case asks for it).
237
+ */
238
+ export const OPENCODE_EVAL_TOOLS_OFF = Object.freeze(['webfetch', 'websearch', 'subagent', 'todo', 'lsp', 'external_directory', 'browser', 'execute', 'question']);
239
+
240
+ /**
241
+ * OpenCode settings every evaluation serve reads last of its configuration files (OPENCODE_CONFIG, after the run's own
242
+ * configuration, which starts from the owner's): no formatter (OpenCode 2 runs one after an edit, outside any sandbox,
243
+ * when a configuration turns them on, and prettier or biome would then load the folder's prettier.config.js or
244
+ * biome.json: a test file would run as code) and no snapshot (OpenCode 2 snapshots a git project's files with git).
245
+ */
246
+ export const OPENCODE_EVAL_SETTINGS = Object.freeze({ formatter: false, snapshots: false });
247
+ /** Where an evaluation serve's last configuration file goes: the trial's own folder, beside the job folder (no tool or
248
+ * shell step of the run writes there). */
249
+ export const OPENCODE_EVAL_CONFIG_FILE = 'opencode-eval.json';
250
+
251
+ /**
252
+ * OpenCode: the environment of an evaluation run's serve. No project configuration of the folder at all (no
253
+ * opencode.json, no .opencode plugin, agent, command, tool or skill, no .agents skill, no AGENTS.md at the top:
254
+ * OPENCODE_DISABLE_PROJECT_CONFIG), nothing of Claude Code's (OPENCODE_DISABLE_CLAUDE_CODE), no file watcher (nothing is
255
+ * loaded again mid-run), OPENCODE_EVAL_SETTINGS (written here, as OPENCODE_CONFIG), git's safety settings. An inherited
256
+ * OPENCODE_CONFIG_DIR is removed (null), not merely left unset: OpenCode 2 reads that folder in place of its global one
257
+ * (config.directory, before XDG), so a SynaBun launched from a shell that exports it would lose the run's own
258
+ * configuration (its providers, rules and the skill under test placed in it; seen live on 2.1.4: "Model unavailable"
259
+ * for a provider the configuration defines). Every inherited GIT_* variable is removed before evalGitSafetyEnv's
260
+ * deliberate settings are applied, so none points the runtime's git around them. The skill under test goes into the
261
+ * run's configuration folder (placeOpenCodeEvalSkill). OpenCode's OPENCODE_CONFIG_CONTENT stays server.js's (the
262
+ * runtime overlay of every isolated serve).
263
+ */
264
+ export function openCodeEvalEnv(confined) {
265
+ const file = join(confined.trialDir || dirname(confined.root), OPENCODE_EVAL_CONFIG_FILE);
266
+ rmSync(file, { force: true });
267
+ writeFileSync(file, `${JSON.stringify(OPENCODE_EVAL_SETTINGS)}\n`, { flag: 'wx', mode: 0o600 });
268
+ return {
269
+ OPENCODE_DISABLE_PROJECT_CONFIG: '1', OPENCODE_CONFIG_PROJECT_DISABLE: '1', OPENCODE_DISABLE_CLAUDE_CODE: '1', OPENCODE_DISABLE_FILEWATCHER: '1',
270
+ OPENCODE_CONFIG: file,
271
+ // An inherited OPENCODE_CONFIG_DIR takes the place of the run's own configuration folder (OpenCode 2 reads
272
+ // config.directory = process.env.OPENCODE_CONFIG_DIR, before XDG): removed, or SynaBun launched from a shell that
273
+ // set it would lose this run's isolated config (and the skill under test placed in it). A value of null tells
274
+ // server.js ensureIsolatedServe (withServeEnv) to delete the variable.
275
+ OPENCODE_CONFIG_DIR: null,
276
+ ...evalGitSafetyEnv(confined.root),
277
+ };
278
+ }
279
+
280
+ /** The most a skill copy may hold (lib/skills/evals.js wrote it from a library revision, which is bounded already). */
281
+ const OPENCODE_SKILL_COPY_LIMITS = Object.freeze({ entries: 2000, bytes: 64 * 1024 * 1024 });
282
+
283
+ /**
284
+ * OpenCode: a trial's copy of the skill under test placed in the run's own configuration folder (`configDir`, the
285
+ * folder server.js writes the run's configuration into and removes with its serve; OpenCode reads skills/<name>
286
+ * there as a global skill). Copied from the trial's .opencode/skills/<name>, which lib/skills/evals.js wrote and checked
287
+ * before the run, without following a link: regular files and folders only, bounded. → the folder, or null for a run
288
+ * without the skill (a baseline, a grader). Throws when the copy cannot be placed: a trial without its skill tests
289
+ * nothing, so it must not start.
290
+ */
291
+ export function placeOpenCodeEvalSkill(confined, configDir) {
292
+ if (!confined || confined.kind !== EVAL_CONFINEMENT) throw new Error('Unknown confinement.');
293
+ if (confined.role !== 'trial' || !confined.skill) return null;
294
+ if (!/^[a-z0-9][a-z0-9._-]{0,63}$/.test(String(confined.skill))) throw new Error('The skill under test has no usable name.');
295
+ if (typeof configDir !== 'string' || !isAbsolute(configDir)) throw new Error('The run has no configuration folder.');
296
+ const from = join(confined.root, EVAL_SKILL_ROOTS.opencode, confined.skill);
297
+ const to = join(configDir, 'skills', confined.skill);
298
+ const top = lstatSync(from, { throwIfNoEntry: false });
299
+ if (!top || !top.isDirectory()) throw new Error(`The trial has no copy of the skill (${EVAL_SKILL_ROOTS.opencode}/${confined.skill}).`);
300
+ rmSync(to, { recursive: true, force: true });
301
+ let entries = 0;
302
+ let bytes = 0;
303
+ const copy = (src, dest) => {
304
+ mkdirSync(dest, { recursive: true, mode: 0o700 });
305
+ for (const name of readdirSync(src)) {
306
+ if (++entries > OPENCODE_SKILL_COPY_LIMITS.entries) throw new Error('The skill copy holds too many files.');
307
+ const s = join(src, name);
308
+ const st = lstatSync(s);
309
+ if (st.isDirectory()) copy(s, join(dest, name));
310
+ else if (st.isFile()) {
311
+ bytes += st.size;
312
+ if (bytes > OPENCODE_SKILL_COPY_LIMITS.bytes) throw new Error('The skill copy is too large.');
313
+ copyFileSync(s, join(dest, name), fsConstants.COPYFILE_EXCL);
314
+ } else throw new Error(`The skill copy holds something that is not a file or a folder (${relative(from, s)}).`);
315
+ }
316
+ };
317
+ copy(from, to);
318
+ return to;
319
+ }
320
+
321
+ /**
322
+ * OpenCode: the config an evaluation run's serve and session get: OpenCode 2's permission rules (a trial's edits
323
+ * and shell asked, its skill allowed; a grader's refused) and no MCP server.
324
+ */
325
+ export function openCodeEvalConfig(confined) {
326
+ const trial = confined?.role === 'trial';
327
+ const shell = !!confined?.shell;
328
+ const rule = (action, effect) => ({ action, resource: '*', effect });
329
+ return {
330
+ permissions: [
331
+ // read / grep / glob are asked, not allowed: OpenCode 2's FileAccess.resolve checks only that a path is
332
+ // syntactically inside the folder. The adapter answers each ask by evalOpenCodeDecision, which resolves the real
333
+ // path and refuses one that leaves the folder. On its own that is check-then-open (TOCTOU): a shell step could
334
+ // swap a job-folder symlink between the ask and the read tool's open, which runs in the runtime's own process,
335
+ // not under the shell's sandbox — so the ask cannot bind the path finally opened. What closes it is that no
336
+ // symlink can exist in the job folder to swap: the eval shell's Seatbelt profile denies symlink creation in every
337
+ // writable place (lib/skills/eval-sandbox.js), SynaBun's skill copy follows none, Write/Edit make plain files
338
+ // only, and the folder starts empty — the shell is the sole possible symlink writer. With the race surface gone
339
+ // this realpath check is sound (and stays as defense in depth). (A denied tool would leave the model's list;
340
+ // 'ask' keeps it.)
341
+ rule('read', 'ask'),
342
+ rule('grep', 'ask'),
343
+ rule('glob', 'ask'),
344
+ rule('edit', trial ? 'ask' : 'deny'),
345
+ rule('shell', shell ? 'ask' : 'deny'),
346
+ rule('skill', trial ? 'allow' : 'deny'),
347
+ ...OPENCODE_EVAL_TOOLS_OFF.map((action) => rule(action, 'deny')),
348
+ ],
349
+ mcp: 'off',
350
+ };
351
+ }
352
+
353
+ /** The path a read / grep / glob ask names: its resource (read: the file; the search root otherwise) or metadata. */
354
+ function evalReadPathOf(request, kind) {
355
+ const meta = request?.metadata && typeof request.metadata === 'object' ? request.metadata : {};
356
+ // grep / glob carry the search root in metadata.path (its resource is the pattern); read carries the file in its resource.
357
+ if (kind === 'grep' || kind === 'glob') {
358
+ for (const key of ['path', 'directory', 'root']) if (typeof meta[key] === 'string' && meta[key]) return meta[key];
359
+ return '.';
360
+ }
361
+ for (const key of ['filePath', 'filepath', 'file_path', 'path']) if (typeof meta[key] === 'string' && meta[key]) return meta[key];
362
+ const pattern = request?.pattern ?? request?.patterns;
363
+ return typeof pattern === 'string' ? pattern : Array.isArray(pattern) && typeof pattern[0] === 'string' ? pattern[0] : null;
364
+ }
365
+
366
+ /** A read / grep / glob ask under an evaluation confinement: null when the real path is inside the folder, else why not. */
367
+ function evalReadDecision(request, confined, kind) {
368
+ const raw = evalReadPathOf(request, kind);
369
+ const root = confined?.root;
370
+ if (!root) return 'This run\'s evaluation folder is not known: nothing can be read.';
371
+ const at = placeOf(raw, root);
372
+ // placeOf follows every symlink (realPathOf): a link that leaves the folder resolves outside it and is refused.
373
+ // This check is race-free because the eval shell's sandbox denies symlink creation (lib/skills/eval-sandbox.js), so
374
+ // no link can be planted or repointed between this resolve and the read tool's open.
375
+ if (!at || !isInside(root, at, { allowRoot: true })) return `${kind === 'read' ? 'Read' : kind === 'grep' ? 'Grep' : 'Glob'} stays inside this run's folder (${root}); a path that resolves outside it (a symlink) is refused. ${REFUSED}`;
376
+ return null;
377
+ }
378
+
379
+ /** An OpenCode permission ask under an evaluation confinement: null when it may go ahead, else why not. */
380
+ export function evalOpenCodeDecision(request, confined) {
381
+ const kind = String(request?.permission || request?.type || '').toLowerCase();
382
+ const trial = confined?.role === 'trial';
383
+ if (kind === 'read' || kind === 'grep' || kind === 'glob') return evalReadDecision(request, confined, kind);
384
+ if (trial && kind === 'skill') return null;
385
+ if (trial && kind === 'bash') return confined.shell ? null : `The shell is not available in this evaluation. ${REFUSED}`;
386
+ if (trial && /^(?:edit|write|patch|multiedit|apply_patch)$/.test(kind)) {
387
+ const meta = request?.metadata && typeof request.metadata === 'object' ? request.metadata : {};
388
+ let raw = null;
389
+ for (const key of ['filePath', 'filepath', 'file_path', 'path']) if (typeof meta[key] === 'string' && meta[key]) { raw = meta[key]; break; }
390
+ if (!raw) {
391
+ const pattern = request?.pattern ?? request?.patterns;
392
+ raw = typeof pattern === 'string' ? pattern : Array.isArray(pattern) && typeof pattern[0] === 'string' ? pattern[0] : null;
393
+ }
394
+ return evalToolDecision('Write', { file_path: raw }, confined);
395
+ }
396
+ return `${kind || 'That tool'} is not available. ${REFUSED}`;
397
+ }