synabun 2.0.10 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (656) hide show
  1. package/CHANGELOG.md +207 -0
  2. package/README.md +25 -2
  3. package/THIRD-PARTY-LICENSES.md +22 -0
  4. package/bin/synabun.js +71 -24
  5. package/docs/whatsapp.md +13 -13
  6. package/hooks/claude-code/pre-websearch.mjs +12 -1
  7. package/hooks/claude-code/shared.mjs +5 -0
  8. package/hooks/claude-code/stop.mjs +1 -1
  9. package/lib/claude-hooks.js +172 -18
  10. package/lib/client-config-repair.js +30 -17
  11. package/lib/desktop-app.js +7 -1
  12. package/lib/external-tools.js +7 -6
  13. package/lib/first-launch-protection.js +57 -12
  14. package/lib/mcp-dist.js +176 -0
  15. package/lib/release-npm-ci.mjs +138 -0
  16. package/lib/release-publish.mjs +91 -0
  17. package/lib/release-scan.mjs +50 -0
  18. package/lib/server-crash-record.js +123 -0
  19. package/lib/server-exit.js +318 -0
  20. package/lib/server-supervisor.js +508 -0
  21. package/lib/start-launcher.js +37 -4
  22. package/lib/tools-cli.js +45 -0
  23. package/lib/update-snapshot.js +302 -0
  24. package/lib/virustotal.mjs +203 -0
  25. package/lib/windows-crash-event.js +269 -0
  26. package/mcp-server/dist/http.d.ts +20 -1
  27. package/mcp-server/dist/http.d.ts.map +1 -1
  28. package/mcp-server/dist/http.js +59 -12
  29. package/mcp-server/dist/http.js.map +1 -1
  30. package/mcp-server/dist/index.d.ts +33 -27
  31. package/mcp-server/dist/index.d.ts.map +1 -1
  32. package/mcp-server/dist/index.js +128 -143
  33. package/mcp-server/dist/index.js.map +1 -1
  34. package/mcp-server/dist/preload.js +1 -1
  35. package/mcp-server/dist/preload.js.map +1 -1
  36. package/mcp-server/dist/services/budget-judgments.d.ts +1 -1
  37. package/mcp-server/dist/services/budget-judgments.d.ts.map +1 -1
  38. package/mcp-server/dist/services/budget-judgments.js +5 -3
  39. package/mcp-server/dist/services/budget-judgments.js.map +1 -1
  40. package/mcp-server/dist/services/codex-config-heal.d.ts +6 -2
  41. package/mcp-server/dist/services/codex-config-heal.d.ts.map +1 -1
  42. package/mcp-server/dist/services/codex-config-heal.js +81 -28
  43. package/mcp-server/dist/services/codex-config-heal.js.map +1 -1
  44. package/mcp-server/dist/services/identity.d.ts +2 -2
  45. package/mcp-server/dist/services/identity.js +2 -2
  46. package/mcp-server/dist/services/memory-judge-backfill.d.ts +6 -0
  47. package/mcp-server/dist/services/memory-judge-backfill.d.ts.map +1 -1
  48. package/mcp-server/dist/services/memory-judge-backfill.js +7 -1
  49. package/mcp-server/dist/services/memory-judge-backfill.js.map +1 -1
  50. package/mcp-server/dist/services/memory-sessions.d.ts +1 -1
  51. package/mcp-server/dist/services/memory-sessions.d.ts.map +1 -1
  52. package/mcp-server/dist/services/memory-sessions.js +28 -2
  53. package/mcp-server/dist/services/memory-sessions.js.map +1 -1
  54. package/mcp-server/dist/services/neural-interface.d.ts +18 -11
  55. package/mcp-server/dist/services/neural-interface.d.ts.map +1 -1
  56. package/mcp-server/dist/services/neural-interface.js +67 -11
  57. package/mcp-server/dist/services/neural-interface.js.map +1 -1
  58. package/mcp-server/dist/services/server-instructions.d.ts +7 -2
  59. package/mcp-server/dist/services/server-instructions.d.ts.map +1 -1
  60. package/mcp-server/dist/services/server-instructions.js +22 -63
  61. package/mcp-server/dist/services/server-instructions.js.map +1 -1
  62. package/mcp-server/dist/services/tool-catalog.d.ts +120 -0
  63. package/mcp-server/dist/services/tool-catalog.d.ts.map +1 -0
  64. package/mcp-server/dist/services/tool-catalog.js +339 -0
  65. package/mcp-server/dist/services/tool-catalog.js.map +1 -0
  66. package/mcp-server/dist/services/tool-list-refresh-barrier.d.ts +12 -0
  67. package/mcp-server/dist/services/tool-list-refresh-barrier.d.ts.map +1 -0
  68. package/mcp-server/dist/services/tool-list-refresh-barrier.js +40 -0
  69. package/mcp-server/dist/services/tool-list-refresh-barrier.js.map +1 -0
  70. package/mcp-server/dist/services/tool-list.d.ts +108 -0
  71. package/mcp-server/dist/services/tool-list.d.ts.map +1 -0
  72. package/mcp-server/dist/services/tool-list.js +334 -0
  73. package/mcp-server/dist/services/tool-list.js.map +1 -0
  74. package/mcp-server/dist/services/tool-runtime.d.ts +161 -0
  75. package/mcp-server/dist/services/tool-runtime.d.ts.map +1 -0
  76. package/mcp-server/dist/services/tool-runtime.js +400 -0
  77. package/mcp-server/dist/services/tool-runtime.js.map +1 -0
  78. package/mcp-server/dist/services/typesafe-config.js +1 -1
  79. package/mcp-server/dist/services/typesafe-config.js.map +1 -1
  80. package/mcp-server/dist/services/whiteboard-geometry.d.ts +26 -2
  81. package/mcp-server/dist/services/whiteboard-geometry.d.ts.map +1 -1
  82. package/mcp-server/dist/services/whiteboard-geometry.js +91 -8
  83. package/mcp-server/dist/services/whiteboard-geometry.js.map +1 -1
  84. package/mcp-server/dist/tools/agents.d.ts +14 -14
  85. package/mcp-server/dist/tools/agents.d.ts.map +1 -1
  86. package/mcp-server/dist/tools/agents.js +29 -19
  87. package/mcp-server/dist/tools/agents.js.map +1 -1
  88. package/mcp-server/dist/tools/browser-batch.d.ts +3 -3
  89. package/mcp-server/dist/tools/choice.d.ts +6 -0
  90. package/mcp-server/dist/tools/choice.d.ts.map +1 -1
  91. package/mcp-server/dist/tools/choice.js +18 -13
  92. package/mcp-server/dist/tools/choice.js.map +1 -1
  93. package/mcp-server/dist/tools/git-tools.js +2 -2
  94. package/mcp-server/dist/tools/git-tools.js.map +1 -1
  95. package/mcp-server/dist/tools/image-tools.d.ts +1 -1
  96. package/mcp-server/dist/tools/image-tools.js +1 -1
  97. package/mcp-server/dist/tools/morelogin.d.ts.map +1 -1
  98. package/mcp-server/dist/tools/morelogin.js +9 -6
  99. package/mcp-server/dist/tools/morelogin.js.map +1 -1
  100. package/mcp-server/dist/tools/skill-tools.d.ts +505 -0
  101. package/mcp-server/dist/tools/skill-tools.d.ts.map +1 -0
  102. package/mcp-server/dist/tools/skill-tools.js +642 -0
  103. package/mcp-server/dist/tools/skill-tools.js.map +1 -0
  104. package/mcp-server/dist/tools/skill.d.ts +7 -0
  105. package/mcp-server/dist/tools/skill.d.ts.map +1 -0
  106. package/mcp-server/dist/tools/skill.js +11 -0
  107. package/mcp-server/dist/tools/skill.js.map +1 -0
  108. package/mcp-server/dist/tools/tool-groups.d.ts +68 -0
  109. package/mcp-server/dist/tools/tool-groups.d.ts.map +1 -0
  110. package/mcp-server/dist/tools/tool-groups.js +147 -0
  111. package/mcp-server/dist/tools/tool-groups.js.map +1 -0
  112. package/mcp-server/dist/tools/whiteboard-format.d.ts.map +1 -1
  113. package/mcp-server/dist/tools/whiteboard-format.js +7 -4
  114. package/mcp-server/dist/tools/whiteboard-format.js.map +1 -1
  115. package/mcp-server/dist/tools/whiteboard-tools.d.ts +64 -13
  116. package/mcp-server/dist/tools/whiteboard-tools.d.ts.map +1 -1
  117. package/mcp-server/dist/tools/whiteboard-tools.js +30 -5
  118. package/mcp-server/dist/tools/whiteboard-tools.js.map +1 -1
  119. package/mcp-server/package-lock.json +2 -2
  120. package/mcp-server/package.json +1 -1
  121. package/mcp-server/src/http.ts +75 -12
  122. package/mcp-server/src/index.ts +147 -154
  123. package/mcp-server/src/preload.ts +12 -12
  124. package/mcp-server/src/services/budget-judgments.ts +5 -3
  125. package/mcp-server/src/services/codex-config-heal.ts +80 -29
  126. package/mcp-server/src/services/identity.ts +2 -2
  127. package/mcp-server/src/services/memory-judge-backfill.ts +7 -1
  128. package/mcp-server/src/services/memory-sessions.ts +24 -3
  129. package/mcp-server/src/services/neural-interface.ts +81 -21
  130. package/mcp-server/src/services/server-instructions.ts +29 -64
  131. package/mcp-server/src/services/tool-catalog.ts +403 -0
  132. package/mcp-server/src/services/tool-list-refresh-barrier.ts +33 -0
  133. package/mcp-server/src/services/tool-list.ts +410 -0
  134. package/mcp-server/src/services/tool-runtime.ts +457 -0
  135. package/mcp-server/src/services/typesafe-config.ts +1 -1
  136. package/mcp-server/src/services/whiteboard-geometry.ts +94 -9
  137. package/mcp-server/src/tools/agents.ts +29 -20
  138. package/mcp-server/src/tools/choice.ts +17 -12
  139. package/mcp-server/src/tools/git-tools.ts +2 -2
  140. package/mcp-server/src/tools/image-tools.ts +1 -1
  141. package/mcp-server/src/tools/morelogin.ts +6 -4
  142. package/mcp-server/src/tools/skill-tools.ts +592 -0
  143. package/mcp-server/src/tools/skill.ts +13 -0
  144. package/mcp-server/src/tools/tool-groups.ts +164 -0
  145. package/mcp-server/src/tools/whiteboard-format.ts +9 -3
  146. package/mcp-server/src/tools/whiteboard-tools.ts +39 -5
  147. package/neural-interface/i18n/en.json +3953 -228
  148. package/neural-interface/i18n/pt-BR.json +3901 -179
  149. package/neural-interface/lib/agent-sandbox-elevation.js +6 -0
  150. package/neural-interface/lib/antigravity/acp-client-host.js +554 -0
  151. package/neural-interface/lib/antigravity/acp-connection.js +299 -0
  152. package/neural-interface/lib/antigravity/bypass.js +100 -0
  153. package/neural-interface/lib/antigravity/catalog.js +273 -0
  154. package/neural-interface/lib/antigravity/home.js +260 -0
  155. package/neural-interface/lib/antigravity/migrate-gemini.js +139 -0
  156. package/neural-interface/lib/antigravity/panel-bridge.js +1058 -0
  157. package/neural-interface/lib/antigravity/policy-hook.mjs +78 -0
  158. package/neural-interface/lib/antigravity/policy.js +74 -0
  159. package/neural-interface/lib/antigravity/tool-call.js +178 -0
  160. package/neural-interface/lib/antigravity/transcripts.js +252 -0
  161. package/neural-interface/lib/antigravity/usage.js +43 -0
  162. package/neural-interface/lib/assistant-api.js +128 -28
  163. package/neural-interface/lib/assistant-brains/antigravity.js +423 -0
  164. package/neural-interface/lib/assistant-brains/claude.js +4 -3
  165. package/neural-interface/lib/assistant-brains/codex-browser-policy-hook.mjs +12 -2
  166. package/neural-interface/lib/assistant-brains/codex-route-gate-hook.mjs +32 -9
  167. package/neural-interface/lib/assistant-brains/codex.js +9 -5
  168. package/neural-interface/lib/assistant-brains/opencode.js +120 -54
  169. package/neural-interface/lib/assistant-brains/opencode2-browser-policy/index.js +28 -13
  170. package/neural-interface/lib/assistant-brains/opencode2-route-gate/index.js +59 -19
  171. package/neural-interface/lib/assistant-budget-planner.js +7 -3
  172. package/neural-interface/lib/assistant-budget-split.js +42 -15
  173. package/neural-interface/lib/assistant-catalog.js +81 -15
  174. package/neural-interface/lib/assistant-config.js +6 -3
  175. package/neural-interface/lib/assistant-dispatch.js +1073 -200
  176. package/neural-interface/lib/assistant-envelope.js +156 -2
  177. package/neural-interface/lib/assistant-evidence.js +80 -0
  178. package/neural-interface/lib/assistant-hidden-models.js +7 -5
  179. package/neural-interface/lib/assistant-persona.js +93 -19
  180. package/neural-interface/lib/assistant-plan-permissions.js +25 -9
  181. package/neural-interface/lib/assistant-route-gate-codex.js +3 -0
  182. package/neural-interface/lib/assistant-route-gate.js +6 -4
  183. package/neural-interface/lib/assistant-router.js +245 -38
  184. package/neural-interface/lib/assistant-runtime.js +214 -30
  185. package/neural-interface/lib/assistant-task-prompt.js +87 -21
  186. package/neural-interface/lib/assistant-tools.js +51 -0
  187. package/neural-interface/lib/assistant-usage.js +29 -0
  188. package/neural-interface/lib/backup-service.js +626 -4
  189. package/neural-interface/lib/backup-zip-reader.js +10 -5
  190. package/neural-interface/lib/backup-zip-writer.js +257 -0
  191. package/neural-interface/lib/browser-execution.js +10 -2
  192. package/neural-interface/lib/browser-guard-cdp.js +84 -0
  193. package/neural-interface/lib/browser-network-guard.js +476 -0
  194. package/neural-interface/lib/browser-policy-roots.js +64 -0
  195. package/neural-interface/lib/browser-tool-policy.js +198 -38
  196. package/neural-interface/lib/claude-agent-bridge.js +11 -2
  197. package/neural-interface/lib/claude-history-cache.js +210 -0
  198. package/neural-interface/lib/claude-hooks-status.js +103 -0
  199. package/neural-interface/lib/claude-model-catalog.js +5 -1
  200. package/neural-interface/lib/claude-temporary.js +5 -11
  201. package/neural-interface/lib/cli-install-api.js +1 -1
  202. package/neural-interface/lib/cli-install.js +163 -154
  203. package/neural-interface/lib/cli-resolver.js +13 -3
  204. package/neural-interface/lib/cli-update-planner.js +52 -41
  205. package/neural-interface/lib/cloudflared/tunnel.js +42 -2
  206. package/neural-interface/lib/codex-mcp-approvals.js +11 -0
  207. package/neural-interface/lib/codex-rollout-reader.js +52 -0
  208. package/neural-interface/lib/codex-worker-app-server.js +191 -0
  209. package/neural-interface/lib/confined-fs.js +30 -3
  210. package/neural-interface/lib/crash-notice-api.js +98 -0
  211. package/neural-interface/lib/editor-completion-opencode.js +68 -68
  212. package/neural-interface/lib/editor-git-api.js +3 -20
  213. package/neural-interface/lib/exec-loop-command.js +13 -10
  214. package/neural-interface/lib/file-explorer-api.js +74 -40
  215. package/neural-interface/lib/file-explorer-watch.js +9 -3
  216. package/neural-interface/lib/git-bash/install.js +52 -6
  217. package/neural-interface/lib/git-bash/manager.js +59 -17
  218. package/neural-interface/lib/git-bash/pins.js +12 -0
  219. package/neural-interface/lib/git-read.js +2 -2
  220. package/neural-interface/lib/git-status-runner.js +390 -0
  221. package/neural-interface/lib/git-status-watchdog.js +40 -0
  222. package/neural-interface/lib/greeting/budgets.js +1 -0
  223. package/neural-interface/lib/greeting/engine.js +2 -2
  224. package/neural-interface/lib/greeting/sources.js +10 -12
  225. package/neural-interface/lib/greeting/writer.js +1 -1
  226. package/neural-interface/lib/http-guards.js +8 -2
  227. package/neural-interface/lib/install-details.js +4 -1
  228. package/neural-interface/lib/mcp-agent-servers.js +67 -0
  229. package/neural-interface/lib/mcp-client-config.js +35 -33
  230. package/neural-interface/lib/mcp-external-servers.js +332 -0
  231. package/neural-interface/lib/mcp-installer.js +2 -1
  232. package/neural-interface/lib/{mcp-profile-refresh-coordinator.js → mcp-tools-refresh-coordinator.js} +4 -4
  233. package/neural-interface/lib/memory-session-capture.js +17 -3
  234. package/neural-interface/lib/morelogin.js +177 -25
  235. package/neural-interface/lib/native-loop-providers.js +965 -66
  236. package/neural-interface/lib/native-loop-runtime.js +290 -42
  237. package/neural-interface/lib/opencode-config.js +557 -0
  238. package/neural-interface/lib/opencode-db-schema.js +17 -34
  239. package/neural-interface/lib/opencode-history.js +48 -218
  240. package/neural-interface/lib/opencode-mcp-ready.js +108 -0
  241. package/neural-interface/lib/opencode-native-boundary.js +53 -0
  242. package/neural-interface/lib/opencode-protocol.js +249 -112
  243. package/neural-interface/lib/opencode-runtime-config.js +32 -22
  244. package/neural-interface/lib/opencode-serve-records.js +39 -21
  245. package/neural-interface/lib/opencode-v2-client.js +48 -153
  246. package/neural-interface/lib/opencode-v2-ws-requests.js +36 -210
  247. package/neural-interface/lib/opencode-worker-data.js +27 -0
  248. package/neural-interface/lib/opencode2-client.js +73 -37
  249. package/neural-interface/lib/opencode2-proxy.js +23 -63
  250. package/neural-interface/lib/opencode2-translate.js +51 -2
  251. package/neural-interface/lib/owner-authority.js +249 -0
  252. package/neural-interface/lib/page-session.js +275 -0
  253. package/neural-interface/lib/path-confine.js +12 -9
  254. package/neural-interface/lib/peer-identity.js +793 -0
  255. package/neural-interface/lib/pty-host/core.js +28 -1
  256. package/neural-interface/lib/pty-host/lifetime.js +12 -0
  257. package/neural-interface/lib/pty-host/manager.js +12 -1
  258. package/neural-interface/lib/remote-policy.js +1 -1
  259. package/neural-interface/lib/request-gate.js +20 -2
  260. package/neural-interface/lib/rulesets/api.js +7 -6
  261. package/neural-interface/lib/rulesets/fs-safe.js +1 -1
  262. package/neural-interface/lib/rulesets/installer.js +180 -36
  263. package/neural-interface/lib/rulesets/json-edit.js +1 -1
  264. package/neural-interface/lib/rulesets/render.js +1 -1
  265. package/neural-interface/lib/session-cache-builder.js +81 -31
  266. package/neural-interface/lib/session-title-generator.js +58 -36
  267. package/neural-interface/lib/skills/api-v2.js +495 -0
  268. package/neural-interface/lib/skills/bundle.js +418 -0
  269. package/neural-interface/lib/skills/catalog.js +298 -0
  270. package/neural-interface/lib/skills/creation-api.js +191 -0
  271. package/neural-interface/lib/skills/creation-brief.js +247 -0
  272. package/neural-interface/lib/skills/creation-sources.js +285 -0
  273. package/neural-interface/lib/skills/creation.js +843 -0
  274. package/neural-interface/lib/skills/deploy.js +569 -0
  275. package/neural-interface/lib/skills/digest.js +119 -0
  276. package/neural-interface/lib/skills/discover.js +111 -0
  277. package/neural-interface/lib/skills/draft-confinement.js +216 -0
  278. package/neural-interface/lib/skills/eval-confinement.js +399 -0
  279. package/neural-interface/lib/skills/eval-sandbox.js +280 -0
  280. package/neural-interface/lib/skills/eval-trace.js +135 -0
  281. package/neural-interface/lib/skills/evals-api.js +212 -0
  282. package/neural-interface/lib/skills/evals.js +1573 -0
  283. package/neural-interface/lib/skills/export.js +203 -0
  284. package/neural-interface/lib/skills/frontmatter.js +64 -0
  285. package/neural-interface/lib/skills/health.js +75 -0
  286. package/neural-interface/lib/skills/json-edit.js +165 -0
  287. package/neural-interface/lib/skills/known-bridge-digests.json +441 -0
  288. package/neural-interface/lib/skills/known-bridges.js +96 -0
  289. package/neural-interface/lib/skills/library.js +823 -0
  290. package/neural-interface/lib/skills/local-store.js +21 -0
  291. package/neural-interface/lib/skills/migration.js +433 -0
  292. package/neural-interface/lib/skills/operations.js +45 -0
  293. package/neural-interface/lib/skills/ownership.js +427 -0
  294. package/neural-interface/lib/skills/packages.js +88 -0
  295. package/neural-interface/lib/skills/reviews.js +212 -0
  296. package/neural-interface/lib/skills/revision.js +80 -0
  297. package/neural-interface/lib/skills/roots.js +269 -0
  298. package/neural-interface/lib/skills/runtime-checks.js +358 -0
  299. package/neural-interface/lib/skills/scan-assets.js +252 -0
  300. package/neural-interface/lib/skills/scan-context.js +26 -0
  301. package/neural-interface/lib/skills/scan-shell.js +324 -0
  302. package/neural-interface/lib/skills/scan.js +364 -0
  303. package/neural-interface/lib/skills/scanners.js +54 -0
  304. package/neural-interface/lib/skills/service.js +530 -0
  305. package/neural-interface/lib/skills/telemetry.js +119 -0
  306. package/neural-interface/lib/sqlite-integrity-worker.js +22 -0
  307. package/neural-interface/lib/synabun-version.js +1 -1
  308. package/neural-interface/lib/system-activity.js +1 -1
  309. package/neural-interface/lib/tool-lists.js +124 -0
  310. package/neural-interface/lib/whatsapp/brain.js +7 -3
  311. package/neural-interface/lib/whatsapp/bridge.js +6 -3
  312. package/neural-interface/lib/whatsapp/cards.js +22 -9
  313. package/neural-interface/lib/whatsapp/config.js +1 -1
  314. package/neural-interface/lib/worker-confinement.js +1268 -0
  315. package/neural-interface/lib/worker-guard-setting.js +56 -0
  316. package/neural-interface/lib/worker-mcp-channel.js +175 -0
  317. package/neural-interface/lib/worker-question.js +74 -0
  318. package/neural-interface/lib/worker-relay-ports.js +227 -0
  319. package/neural-interface/lib/worker-resume.js +238 -0
  320. package/neural-interface/lib/worker-sandbox.js +329 -0
  321. package/neural-interface/package-lock.json +34 -12
  322. package/neural-interface/package.json +3 -2
  323. package/neural-interface/public/index.html +3 -1
  324. package/neural-interface/public/index2d.html +19 -11
  325. package/neural-interface/public/onboarding.html +3 -3
  326. package/neural-interface/public/shared/account-menu.js +8 -5
  327. package/neural-interface/public/shared/agent-runtime-options.js +125 -31
  328. package/neural-interface/public/shared/agy/agy-bypass.js +47 -0
  329. package/neural-interface/public/shared/agy/agy-client.js +266 -0
  330. package/neural-interface/public/shared/agy/agy-commands.js +120 -0
  331. package/neural-interface/public/shared/agy/agy-copy.js +326 -0
  332. package/neural-interface/public/shared/agy/agy-diff.js +161 -0
  333. package/neural-interface/public/shared/agy/agy-events.js +469 -0
  334. package/neural-interface/public/shared/agy/agy-greeting.js +26 -0
  335. package/neural-interface/public/shared/agy/agy-options.js +232 -0
  336. package/neural-interface/public/shared/agy/agy-panel.js +2543 -0
  337. package/neural-interface/public/shared/agy/agy-render.js +392 -0
  338. package/neural-interface/public/shared/agy/agy-setup.js +149 -0
  339. package/neural-interface/public/shared/agy/agy-styles.js +604 -0
  340. package/neural-interface/public/shared/agy/agy-tabs-logic.js +181 -0
  341. package/neural-interface/public/shared/api.js +1527 -1336
  342. package/neural-interface/public/shared/app-frame.js +192 -0
  343. package/neural-interface/public/shared/assistant/asst-agents-dock.js +36 -6
  344. package/neural-interface/public/shared/assistant/asst-api.js +1 -1
  345. package/neural-interface/public/shared/assistant/asst-ask-styles.js +270 -0
  346. package/neural-interface/public/shared/assistant/asst-ask-tail.js +118 -0
  347. package/neural-interface/public/shared/assistant/asst-ask.js +397 -0
  348. package/neural-interface/public/shared/assistant/asst-brain-picker.js +105 -44
  349. package/neural-interface/public/shared/assistant/asst-budget.js +5 -4
  350. package/neural-interface/public/shared/assistant/asst-control.js +205 -26
  351. package/neural-interface/public/shared/assistant/asst-followups-card.js +383 -0
  352. package/neural-interface/public/shared/assistant/asst-followups.js +417 -0
  353. package/neural-interface/public/shared/assistant/asst-hosts.js +16 -7
  354. package/neural-interface/public/shared/assistant/asst-limit-dock.js +45 -0
  355. package/neural-interface/public/shared/assistant/asst-limit-wait.js +327 -0
  356. package/neural-interface/public/shared/assistant/asst-limit.js +168 -0
  357. package/neural-interface/public/shared/assistant/asst-mascot.js +49 -12
  358. package/neural-interface/public/shared/assistant/asst-menu.js +7 -6
  359. package/neural-interface/public/shared/assistant/asst-panel.js +151 -19
  360. package/neural-interface/public/shared/assistant/asst-rack-styles.js +66 -49
  361. package/neural-interface/public/shared/assistant/asst-render.js +445 -82
  362. package/neural-interface/public/shared/assistant/asst-route.js +304 -53
  363. package/neural-interface/public/shared/assistant/asst-routes-editor.js +9 -8
  364. package/neural-interface/public/shared/assistant/asst-scenes.js +56 -1
  365. package/neural-interface/public/shared/assistant/asst-sidepanel.js +117 -10
  366. package/neural-interface/public/shared/assistant/asst-slash.js +17 -9
  367. package/neural-interface/public/shared/assistant/asst-state.js +87 -24
  368. package/neural-interface/public/shared/assistant/asst-stop.js +127 -0
  369. package/neural-interface/public/shared/assistant/asst-styles.js +93 -21
  370. package/neural-interface/public/shared/assistant/asst-tool-kinds.js +190 -23
  371. package/neural-interface/public/shared/assistant/asst-usage.js +2 -1
  372. package/neural-interface/public/shared/automation-context.js +5 -2
  373. package/neural-interface/public/shared/cc-dropdown.js +89 -19
  374. package/neural-interface/public/shared/cdx/cdx-context-menu.js +10 -9
  375. package/neural-interface/public/shared/cdx/cdx-copy.js +16 -0
  376. package/neural-interface/public/shared/cdx/cdx-output-model.js +22 -1
  377. package/neural-interface/public/shared/cdx/cdx-output.js +34 -10
  378. package/neural-interface/public/shared/cdx/cdx-panel.js +59 -151
  379. package/neural-interface/public/shared/cdx/cdx-preview.js +12 -0
  380. package/neural-interface/public/shared/cdx/cdx-protocol.js +39 -11
  381. package/neural-interface/public/shared/cdx/cdx-render.js +57 -38
  382. package/neural-interface/public/shared/cdx/cdx-requests.js +14 -4
  383. package/neural-interface/public/shared/cdx/cdx-skill-hints.js +54 -0
  384. package/neural-interface/public/shared/cdx/cdx-styles.js +15 -3
  385. package/neural-interface/public/shared/cdx/cdx-tabs.js +127 -44
  386. package/neural-interface/public/shared/cdx/cdx-ws.js +0 -1
  387. package/neural-interface/public/shared/cli-install-controller.js +218 -0
  388. package/neural-interface/public/shared/cli-install-view.js +11 -12
  389. package/neural-interface/public/shared/cli-status.js +11 -10
  390. package/neural-interface/public/shared/constants.js +5 -4
  391. package/neural-interface/public/shared/context-menu-areas.js +34 -0
  392. package/neural-interface/public/shared/context-menu-logic.js +243 -0
  393. package/neural-interface/public/shared/context-menu-model.js +367 -0
  394. package/neural-interface/public/shared/context-menu.js +372 -0
  395. package/neural-interface/public/shared/cp/cp-context-menu.js +10 -9
  396. package/neural-interface/public/shared/cp/cp-restore.js +162 -0
  397. package/neural-interface/public/shared/cp/cp-tool-cards.js +10 -3
  398. package/neural-interface/public/shared/crash-notice-view.js +168 -0
  399. package/neural-interface/public/shared/desktop-bridge.js +17 -0
  400. package/neural-interface/public/shared/desktop-titlebar-view.js +12 -0
  401. package/neural-interface/public/shared/desktop-titlebar.js +13 -4
  402. package/neural-interface/public/shared/desktop-update-view.js +21 -0
  403. package/neural-interface/public/shared/editor/editor-panel-logic.js +2 -2
  404. package/neural-interface/public/shared/explorer/fx-api.js +4 -4
  405. package/neural-interface/public/shared/explorer/fx-links.js +3 -3
  406. package/neural-interface/public/shared/explorer/fx-logic.js +1 -1
  407. package/neural-interface/public/shared/explorer/fx-main.js +48 -16
  408. package/neural-interface/public/shared/explorer/fx-tree.js +1 -0
  409. package/neural-interface/public/shared/greeting/greeting-card.js +2 -2
  410. package/neural-interface/public/shared/greeting/greeting-view.js +1 -0
  411. package/neural-interface/public/shared/html-shell.js +30 -15
  412. package/neural-interface/public/shared/i18n.js +2 -0
  413. package/neural-interface/public/shared/ocp-v2/ocp-v2-approvals.js +7 -7
  414. package/neural-interface/public/shared/ocp-v2/ocp-v2-binding.js +4 -4
  415. package/neural-interface/public/shared/ocp-v2/ocp-v2-changes-logic.js +3 -101
  416. package/neural-interface/public/shared/ocp-v2/ocp-v2-childpanel.js +2 -0
  417. package/neural-interface/public/shared/ocp-v2/ocp-v2-compact-button.js +1 -3
  418. package/neural-interface/public/shared/ocp-v2/ocp-v2-composer-logic.js +2 -6
  419. package/neural-interface/public/shared/ocp-v2/ocp-v2-context-menu.js +11 -11
  420. package/neural-interface/public/shared/ocp-v2/ocp-v2-context-model.js +0 -3
  421. package/neural-interface/public/shared/ocp-v2/ocp-v2-events.js +8 -4
  422. package/neural-interface/public/shared/ocp-v2/ocp-v2-manager.js +7 -7
  423. package/neural-interface/public/shared/ocp-v2/ocp-v2-outcome-notifications.js +16 -0
  424. package/neural-interface/public/shared/ocp-v2/ocp-v2-panel.js +74 -259
  425. package/neural-interface/public/shared/ocp-v2/ocp-v2-plan.js +1 -2
  426. package/neural-interface/public/shared/ocp-v2/ocp-v2-projectbar.js +95 -202
  427. package/neural-interface/public/shared/ocp-v2/ocp-v2-rehydrate.js +13 -4
  428. package/neural-interface/public/shared/ocp-v2/ocp-v2-render.js +13 -22
  429. package/neural-interface/public/shared/ocp-v2/ocp-v2-send.js +4 -4
  430. package/neural-interface/public/shared/ocp-v2/ocp-v2-session-actions.js +8 -61
  431. package/neural-interface/public/shared/ocp-v2/ocp-v2-sessions-logic.js +2 -91
  432. package/neural-interface/public/shared/ocp-v2/ocp-v2-slash-hints.js +16 -15
  433. package/neural-interface/public/shared/ocp-v2/ocp-v2-state.js +8 -21
  434. package/neural-interface/public/shared/ocp-v2/ocp-v2-status-logic.js +1 -99
  435. package/neural-interface/public/shared/ocp-v2/ocp-v2-status.js +5 -35
  436. package/neural-interface/public/shared/ocp-v2/ocp-v2-styles.js +17 -21
  437. package/neural-interface/public/shared/ocp-v2/{ocp-v2-profile-inheritance.js → ocp-v2-tools-inheritance.js} +16 -16
  438. package/neural-interface/public/shared/ocp-v2/ocp-v2-ws.js +60 -50
  439. package/neural-interface/public/shared/page-session.js +96 -0
  440. package/neural-interface/public/shared/provider-icons.js +4 -2
  441. package/neural-interface/public/shared/registry.js +5 -3
  442. package/neural-interface/public/shared/retired-2d.js +3 -0
  443. package/neural-interface/public/shared/schedules-view.js +195 -61
  444. package/neural-interface/public/shared/settings/settings-claude-hooks.js +135 -0
  445. package/neural-interface/public/shared/settings/settings-data.js +3 -1
  446. package/neural-interface/public/shared/settings/settings-ia.js +6 -6
  447. package/neural-interface/public/shared/settings/settings-opencode.js +89 -0
  448. package/neural-interface/public/shared/settings/settings-rules.js +2 -2
  449. package/neural-interface/public/shared/settings/settings-skill-folders-pane.js +265 -0
  450. package/neural-interface/public/shared/settings/settings-skill-folders.js +130 -0
  451. package/neural-interface/public/shared/settings/settings-worker-guard.js +72 -0
  452. package/neural-interface/public/shared/showcase-privacy.js +6 -1
  453. package/neural-interface/public/shared/sidepanel-session-status.js +3 -3
  454. package/neural-interface/public/shared/skills/ss-args.js +96 -0
  455. package/neural-interface/public/shared/skills/ss-creation.js +222 -0
  456. package/neural-interface/public/shared/skills/ss-dialog.js +133 -0
  457. package/neural-interface/public/shared/skills/ss-doc.js +173 -0
  458. package/neural-interface/public/shared/skills/ss-eval-adapters.js +269 -0
  459. package/neural-interface/public/shared/skills/ss-eval-grade.js +359 -0
  460. package/neural-interface/public/shared/skills/ss-eval-optimise.js +153 -0
  461. package/neural-interface/public/shared/skills/ss-eval-plan.js +315 -0
  462. package/neural-interface/public/shared/skills/ss-evals.js +417 -0
  463. package/neural-interface/public/shared/skills/ss-form.js +159 -0
  464. package/neural-interface/public/shared/skills/ss-frontmatter.js +376 -0
  465. package/neural-interface/public/shared/skills/ss-ids.js +32 -0
  466. package/neural-interface/public/shared/skills/ss-lint.js +149 -0
  467. package/neural-interface/public/shared/skills/ss-presets.js +117 -0
  468. package/neural-interface/public/shared/skills/ss-profiles.js +513 -0
  469. package/neural-interface/public/shared/skills/ss-resources.js +165 -0
  470. package/neural-interface/public/shared/skills/ss-review-view.js +190 -0
  471. package/neural-interface/public/shared/skills/ss-tabs.js +79 -0
  472. package/neural-interface/public/shared/skills/ss-unicode.js +135 -0
  473. package/neural-interface/public/shared/skills/studio/sx-actions.js +222 -0
  474. package/neural-interface/public/shared/skills/studio/sx-cm.js +101 -0
  475. package/neural-interface/public/shared/skills/studio/sx-create-ai-view.js +749 -0
  476. package/neural-interface/public/shared/skills/studio/sx-create-ai.js +237 -0
  477. package/neural-interface/public/shared/skills/studio/sx-create-view.js +254 -0
  478. package/neural-interface/public/shared/skills/studio/sx-create.js +126 -0
  479. package/neural-interface/public/shared/skills/studio/sx-diff-view.js +43 -0
  480. package/neural-interface/public/shared/skills/studio/sx-diff.js +133 -0
  481. package/neural-interface/public/shared/skills/studio/sx-docs.js +480 -0
  482. package/neural-interface/public/shared/skills/studio/sx-dom.js +230 -0
  483. package/neural-interface/public/shared/skills/studio/sx-edit-view.js +848 -0
  484. package/neural-interface/public/shared/skills/studio/sx-editor-model.js +193 -0
  485. package/neural-interface/public/shared/skills/studio/sx-errors.js +22 -0
  486. package/neural-interface/public/shared/skills/studio/sx-fixes-view.js +359 -0
  487. package/neural-interface/public/shared/skills/studio/sx-fixes.js +327 -0
  488. package/neural-interface/public/shared/skills/studio/sx-geometry.js +82 -0
  489. package/neural-interface/public/shared/skills/studio/sx-import.js +262 -0
  490. package/neural-interface/public/shared/skills/studio/sx-inspector.js +308 -0
  491. package/neural-interface/public/shared/skills/studio/sx-install-flow.js +282 -0
  492. package/neural-interface/public/shared/skills/studio/sx-install.js +313 -0
  493. package/neural-interface/public/shared/skills/studio/sx-library-view.js +748 -0
  494. package/neural-interface/public/shared/skills/studio/sx-library.js +479 -0
  495. package/neural-interface/public/shared/skills/studio/sx-main.js +690 -0
  496. package/neural-interface/public/shared/skills/studio/sx-operations-view.js +98 -0
  497. package/neural-interface/public/shared/skills/studio/sx-operations.js +11 -0
  498. package/neural-interface/public/shared/skills/studio/sx-plan.js +158 -0
  499. package/neural-interface/public/shared/skills/studio/sx-publish-view.js +621 -0
  500. package/neural-interface/public/shared/skills/studio/sx-review-view.js +369 -0
  501. package/neural-interface/public/shared/skills/studio/sx-review.js +148 -0
  502. package/neural-interface/public/shared/skills/studio/sx-scan.js +35 -0
  503. package/neural-interface/public/shared/skills/studio/sx-snippets.js +96 -0
  504. package/neural-interface/public/shared/skills/studio/sx-subject.js +249 -0
  505. package/neural-interface/public/shared/skills/studio/sx-test-view.js +994 -0
  506. package/neural-interface/public/shared/skills/studio/sx-test.js +328 -0
  507. package/neural-interface/public/shared/skills/studio/sx-versions-view.js +241 -0
  508. package/neural-interface/public/shared/skills/studio/sx-workspace-view.js +426 -0
  509. package/neural-interface/public/shared/state.js +1 -1
  510. package/neural-interface/public/shared/streaming-markdown.js +58 -0
  511. package/neural-interface/public/shared/styleguide/sg-panel.js +4 -2
  512. package/neural-interface/public/shared/styles.css +1822 -1756
  513. package/neural-interface/public/shared/synabun-mascot.js +1115 -9
  514. package/neural-interface/public/shared/text-cache.js +29 -0
  515. package/neural-interface/public/shared/tool-list-picker.js +55 -0
  516. package/neural-interface/public/shared/tool-list-storage.js +24 -0
  517. package/neural-interface/public/shared/tool-list-view.js +112 -0
  518. package/neural-interface/public/shared/topright-overflow-logic.js +54 -0
  519. package/neural-interface/public/shared/tour/tour-actor.js +268 -0
  520. package/neural-interface/public/shared/tour/tour-beats.js +336 -0
  521. package/neural-interface/public/shared/tour/tour-engine.js +884 -0
  522. package/neural-interface/public/shared/tour/tour-ink.js +390 -0
  523. package/neural-interface/public/shared/tour/tour-stage.js +440 -0
  524. package/neural-interface/public/shared/tour/tour-state.js +40 -0
  525. package/neural-interface/public/shared/tour/tour-tips.js +99 -0
  526. package/neural-interface/public/shared/tour/tour.css +144 -0
  527. package/neural-interface/public/shared/ui-antigravity-panel.js +19 -0
  528. package/neural-interface/public/shared/ui-assistant.js +2 -0
  529. package/neural-interface/public/shared/ui-automation-studio.js +111 -140
  530. package/neural-interface/public/shared/ui-claude-panel.js +376 -82
  531. package/neural-interface/public/shared/ui-cli-banner.js +23 -167
  532. package/neural-interface/public/shared/ui-command-runner.js +6 -3
  533. package/neural-interface/public/shared/ui-context-menu.js +295 -0
  534. package/neural-interface/public/shared/ui-cost-widget.js +57 -59
  535. package/neural-interface/public/shared/ui-crash-notice.js +187 -0
  536. package/neural-interface/public/shared/ui-detail.js +15 -3
  537. package/neural-interface/public/shared/ui-file-explorer.js +48 -6
  538. package/neural-interface/public/shared/ui-file-links.js +2 -2
  539. package/neural-interface/public/shared/ui-games.js +2 -2
  540. package/neural-interface/public/shared/ui-image-gallery.js +50 -47
  541. package/neural-interface/public/shared/ui-judgments.js +1 -1
  542. package/neural-interface/public/shared/ui-keybinds.js +33 -2
  543. package/neural-interface/public/shared/ui-link.js +3 -3
  544. package/neural-interface/public/shared/ui-menubar.js +10 -20
  545. package/neural-interface/public/shared/ui-native-loop-router-state.js +5 -0
  546. package/neural-interface/public/shared/ui-native-loop-router.js +11 -1
  547. package/neural-interface/public/shared/ui-navbar.js +49 -24
  548. package/neural-interface/public/shared/ui-notifications.js +1 -0
  549. package/neural-interface/public/shared/ui-panels.js +96 -65
  550. package/neural-interface/public/shared/ui-resume.js +9 -7
  551. package/neural-interface/public/shared/ui-schedules-studio.js +546 -431
  552. package/neural-interface/public/shared/ui-scroll-follow.js +16 -5
  553. package/neural-interface/public/shared/ui-sessions.js +27 -15
  554. package/neural-interface/public/shared/ui-settings.js +683 -526
  555. package/neural-interface/public/shared/ui-sidepanel-session-entry.js +9 -2
  556. package/neural-interface/public/shared/ui-sidepanel-sessions.js +4 -4
  557. package/neural-interface/public/shared/ui-sidepanel-state.js +23 -12
  558. package/neural-interface/public/shared/ui-sidepanel-tray.js +48 -7
  559. package/neural-interface/public/shared/ui-sidepanel-windows.js +18 -13
  560. package/neural-interface/public/shared/ui-skills.js +33 -2830
  561. package/neural-interface/public/shared/ui-styleguide.js +5 -2
  562. package/neural-interface/public/shared/ui-sync.js +15 -5
  563. package/neural-interface/public/shared/ui-terminal.js +270 -190
  564. package/neural-interface/public/shared/ui-tool-updates.js +6 -4
  565. package/neural-interface/public/shared/ui-tooltip.js +81 -51
  566. package/neural-interface/public/shared/ui-topright-overflow.js +233 -0
  567. package/neural-interface/public/shared/ui-trash.js +6 -4
  568. package/neural-interface/public/shared/ui-tutorial-layout.js +3 -117
  569. package/neural-interface/public/shared/ui-tutorial-preview.js +103 -0
  570. package/neural-interface/public/shared/ui-tutorial-steps.js +3 -65
  571. package/neural-interface/public/shared/ui-tutorial.css +47 -100
  572. package/neural-interface/public/shared/ui-tutorial.js +285 -509
  573. package/neural-interface/public/shared/ui-update.js +38 -2
  574. package/neural-interface/public/shared/ui-whiteboard.js +312 -59
  575. package/neural-interface/public/shared/ui-worker-end.js +5 -1
  576. package/neural-interface/public/shared/ui-workspaces.js +3 -2
  577. package/neural-interface/public/shared/view-variant.js +66 -0
  578. package/neural-interface/public/shared/whatsapp/wa-view.js +3 -3
  579. package/neural-interface/public/shared/whiteboard-list.js +172 -0
  580. package/neural-interface/public/shared/worker-end.js +10 -3
  581. package/neural-interface/public/shared/worker-questions.js +34 -0
  582. package/neural-interface/public/shared/worker-restart.js +12 -0
  583. package/neural-interface/public/sidepanel-session.html +2 -2
  584. package/neural-interface/public/variant/2d/background.js +2 -1
  585. package/neural-interface/public/variant/2d/context-menu.js +8 -2
  586. package/neural-interface/public/variant/2d/graph.js +42 -20
  587. package/neural-interface/public/variant/2d/hulls.js +14 -9
  588. package/neural-interface/public/variant/2d/lasso.js +11 -7
  589. package/neural-interface/public/variant/2d/main.js +32 -1
  590. package/neural-interface/public/variant/2d/minimap.js +11 -13
  591. package/neural-interface/public/variant/3d/main.js +69 -6
  592. package/neural-interface/public/variant/3d/map-controls.js +24 -9
  593. package/neural-interface/public/vendor/caveat/OFL.txt +93 -0
  594. package/neural-interface/public/vendor/caveat/caveat-latin-400-normal.woff2 +0 -0
  595. package/neural-interface/public/vendor/caveat/caveat-latin-700-normal.woff2 +0 -0
  596. package/neural-interface/public/vendor/caveat/caveat.css +17 -0
  597. package/neural-interface/public/vendor/caveat/manifest.json +12 -0
  598. package/neural-interface/public/vendor/yaml/LICENSE +13 -0
  599. package/neural-interface/public/vendor/yaml/manifest.json +4 -0
  600. package/neural-interface/public/vendor/yaml/yaml-2.9.1.min.js +137 -0
  601. package/neural-interface/scripts/build-vendor.mjs +32 -0
  602. package/neural-interface/server.js +4553 -2855
  603. package/neural-interface/templates/rulesets/README.md +1 -1
  604. package/neural-interface/templates/rulesets/core.md +1 -1
  605. package/neural-interface/templates/rulesets/legacy-hashes.json +23 -5
  606. package/neural-interface/templates/rulesets/manifest.json +8 -8
  607. package/npm-shrinkwrap.json +34 -12
  608. package/package.json +17 -3
  609. package/postinstall.js +9 -4
  610. package/preuninstall.js +1 -1
  611. package/setup.js +97 -322
  612. package/skills/skill-forge/SKILL.md +177 -0
  613. package/skills/skill-forge/evals/evals.json +54 -0
  614. package/skills/skill-forge/references/package.md +105 -0
  615. package/skills/skill-forge/references/review-and-recovery.md +63 -0
  616. package/skills/skill-forge/references/runtime-profiles.md +20 -0
  617. package/skills/skill-forge/references/sources-and-capabilities.md +51 -0
  618. package/skills/synabun/SKILL.md +10 -7
  619. package/skills/synabun/modules/audit.md +1 -5
  620. package/skills/synabun/modules/changelog.md +1 -5
  621. package/skills/synabun/modules/idea.md +1 -1
  622. package/skills/synabun/modules/memorize.md +6 -9
  623. package/skills/synabun/modules/schedule.md +6 -6
  624. package/mcp-server/dist/services/profiles.d.ts +0 -120
  625. package/mcp-server/dist/services/profiles.d.ts.map +0 -1
  626. package/mcp-server/dist/services/profiles.js +0 -387
  627. package/mcp-server/dist/services/profiles.js.map +0 -1
  628. package/mcp-server/dist/tools/leonardo-browser-tools.d.ts +0 -97
  629. package/mcp-server/dist/tools/leonardo-browser-tools.d.ts.map +0 -1
  630. package/mcp-server/dist/tools/leonardo-browser-tools.js +0 -228
  631. package/mcp-server/dist/tools/leonardo-browser-tools.js.map +0 -1
  632. package/mcp-server/dist/tools/leonardo.d.ts +0 -13
  633. package/mcp-server/dist/tools/leonardo.d.ts.map +0 -1
  634. package/mcp-server/dist/tools/leonardo.js +0 -21
  635. package/mcp-server/dist/tools/leonardo.js.map +0 -1
  636. package/mcp-server/dist/tools/profile.d.ts +0 -17
  637. package/mcp-server/dist/tools/profile.d.ts.map +0 -1
  638. package/mcp-server/dist/tools/profile.js +0 -114
  639. package/mcp-server/dist/tools/profile.js.map +0 -1
  640. package/mcp-server/src/services/profiles.ts +0 -428
  641. package/mcp-server/src/tools/leonardo-browser-tools.ts +0 -291
  642. package/mcp-server/src/tools/leonardo.ts +0 -30
  643. package/mcp-server/src/tools/profile.ts +0 -122
  644. package/neural-interface/lib/assistant-brains/opencode-browser-policy.js +0 -47
  645. package/neural-interface/lib/assistant-brains/opencode-route-gate.js +0 -62
  646. package/neural-interface/lib/mcp-profile-servers.js +0 -120
  647. package/neural-interface/public/shared/ui-tutorial-draw.js +0 -311
  648. package/skills/leonardo/SKILL.md +0 -168
  649. package/skills/leonardo/icon.svg +0 -1
  650. package/skills/leonardo/modules/browser-guide.md +0 -378
  651. package/skills/leonardo/modules/image-prompter.md +0 -283
  652. package/skills/leonardo/modules/model-advisor.md +0 -100
  653. package/skills/leonardo/modules/prompt-library.md +0 -110
  654. package/skills/leonardo/modules/style-guide.md +0 -186
  655. package/skills/leonardo/modules/video-prompter.md +0 -317
  656. /package/neural-interface/templates/rulesets/{gemini.md → antigravity.md} +0 -0
@@ -0,0 +1,119 @@
1
+ // ── What a skill is, byte for byte ──
2
+ // A revision is the SHA-256 of a file's exact bytes: a save names the revision it was written over, and one
3
+ // that does not match what is on disk now is refused (lib/skills/revision.js). A digest is the same for a whole
4
+ // skill folder: a manifest of every entry (relative path, type, size, executable bit, the file's hash, a link's
5
+ // target as written) sorted by path, hashed. It is what an install records as SynaBun's own and compares before
6
+ // it replaces or removes anything (lib/skills/ownership.js), and what an import is approved by
7
+ // (lib/skills/bundle.js).
8
+ //
9
+ // Reading a folder never follows a link (a link is an entry, with its target as text), never opens a FIFO or a
10
+ // device (listed as `other`, never read), and is bounded in entries, depth and bytes.
11
+
12
+ import { createHash } from 'node:crypto';
13
+ import { lstatSync, readdirSync, readlinkSync } from 'node:fs';
14
+ import { dirname, join } from 'node:path';
15
+ import { readConfinedFile } from '../confined-fs.js';
16
+ import { realPathOf } from '../path-confine.js';
17
+
18
+ export const sha256 = (data) => `sha256:${createHash('sha256').update(data).digest('hex')}`;
19
+
20
+ /** The revision of a file's content: the hash of its exact bytes. */
21
+ export function revisionOf(bytes) {
22
+ return sha256(Buffer.isBuffer(bytes) ? bytes : Buffer.from(String(bytes ?? ''), 'utf-8'));
23
+ }
24
+
25
+ /** Files the system writes into folders by itself: never part of what a skill is, never copied. */
26
+ export const SYSTEM_NAMES = new Set(['.DS_Store', 'Thumbs.db', 'desktop.ini']);
27
+
28
+ export const TREE_LIMITS = Object.freeze({ maxEntries: 5000, maxDepth: 24, maxBytes: 64 * 1024 * 1024, maxFileBytes: 32 * 1024 * 1024 });
29
+
30
+ const byPath = (a, b) => (a.path < b.path ? -1 : a.path > b.path ? 1 : 0);
31
+
32
+ /**
33
+ * The digest of a list of entries: `{ path, type: 'file'|'dir'|'link'|'other', size?, executable?, sha256?, target? }`.
34
+ * Order does not matter; the entries are sorted by path first.
35
+ */
36
+ export function manifestDigest(entries) {
37
+ const lines = (entries || []).slice().sort(byPath).map((e) => {
38
+ if (e.type === 'file') return `file\t${e.path}\t${e.size}\t${e.executable ? 'x' : '-'}\t${e.sha256}`;
39
+ if (e.type === 'link') return `link\t${e.path}\t${e.target}`;
40
+ if (e.type === 'dir') return `dir\t${e.path}`;
41
+ return `other\t${e.path}`;
42
+ });
43
+ return sha256(lines.join('\n'));
44
+ }
45
+
46
+ /** The manifest entry of bytes about to be written as one file. */
47
+ export function fileEntry(path, bytes, { executable = false } = {}) {
48
+ const buf = Buffer.isBuffer(bytes) ? bytes : Buffer.from(String(bytes ?? ''), 'utf-8');
49
+ return { path, type: 'file', size: buf.length, executable: !!executable, sha256: sha256(buf) };
50
+ }
51
+
52
+ /**
53
+ * Read what is at `path` as a manifest, without following any link.
54
+ * @returns {{ kind: 'absent'|'file'|'dir'|'link'|'other', entries: object[], digest: string|null, complete: boolean,
55
+ * reason?: string }} `digest` is null when the folder could not be read whole (too large, changed while read):
56
+ * such a destination is never taken for SynaBun's own.
57
+ */
58
+ export function readManifest(path, limits = TREE_LIMITS) {
59
+ let st;
60
+ try { st = lstatSync(path); } catch { return { kind: 'absent', entries: [], digest: null, complete: true }; }
61
+ if (st.isSymbolicLink()) {
62
+ let target = '';
63
+ try { target = readlinkSync(path); } catch { target = ''; }
64
+ const entries = [{ path: '.', type: 'link', target }];
65
+ return { kind: 'link', entries, digest: manifestDigest(entries), complete: true };
66
+ }
67
+ if (st.isFile()) {
68
+ let real;
69
+ try {
70
+ real = realPathOf(path);
71
+ const bytes = readConfinedFile(dirname(real), real, { maxBytes: limits.maxFileBytes });
72
+ const entries = [fileEntry('.', bytes, { executable: (st.mode & 0o111) !== 0 })];
73
+ return { kind: 'file', entries, digest: manifestDigest(entries), complete: true };
74
+ } catch (err) {
75
+ return { kind: 'file', entries: [], digest: null, complete: false, reason: String(err?.message || err) };
76
+ }
77
+ }
78
+ if (!st.isDirectory()) return { kind: 'other', entries: [{ path: '.', type: 'other' }], digest: null, complete: false, reason: 'not a file or a folder' };
79
+ const entries = [];
80
+ const state = { bytes: 0, complete: true, reason: '' };
81
+ let base;
82
+ try { base = realPathOf(path); } catch { return { kind: 'dir', entries: [], digest: null, complete: false, reason: 'unresolvable' }; }
83
+ const walk = (dir, rel, depth) => {
84
+ if (!state.complete) return;
85
+ if (depth > limits.maxDepth) { state.complete = false; state.reason = 'too deep'; return; }
86
+ let names;
87
+ try { names = readdirSync(dir).sort(); } catch (err) { state.complete = false; state.reason = String(err?.message || err); return; }
88
+ for (const name of names) {
89
+ if (SYSTEM_NAMES.has(name)) continue;
90
+ if (entries.length >= limits.maxEntries) { state.complete = false; state.reason = 'too many entries'; return; }
91
+ const full = join(dir, name);
92
+ const relPath = rel ? `${rel}/${name}` : name;
93
+ let est;
94
+ try { est = lstatSync(full); } catch { state.complete = false; state.reason = 'changed while read'; return; }
95
+ if (est.isSymbolicLink()) {
96
+ let target = '';
97
+ try { target = readlinkSync(full); } catch { target = ''; }
98
+ entries.push({ path: relPath, type: 'link', target });
99
+ } else if (est.isDirectory()) {
100
+ entries.push({ path: relPath, type: 'dir' });
101
+ walk(full, relPath, depth + 1);
102
+ if (!state.complete) return;
103
+ } else if (est.isFile()) {
104
+ if (est.size > limits.maxFileBytes || state.bytes + est.size > limits.maxBytes) { state.complete = false; state.reason = 'too large'; return; }
105
+ let bytes;
106
+ try { bytes = readConfinedFile(base, full, { maxBytes: limits.maxFileBytes }); } catch (err) { state.complete = false; state.reason = String(err?.message || err); return; }
107
+ state.bytes += bytes.length;
108
+ entries.push(fileEntry(relPath, bytes, { executable: (est.mode & 0o111) !== 0 }));
109
+ } else {
110
+ entries.push({ path: relPath, type: 'other' });
111
+ }
112
+ }
113
+ };
114
+ walk(base, '', 0);
115
+ return { kind: 'dir', entries, digest: state.complete ? manifestDigest(entries) : null, complete: state.complete, ...(state.complete ? {} : { reason: state.reason }) };
116
+ }
117
+
118
+ /** A short form of a digest for people ("sha256:3f2a…9c01"). */
119
+ export const shortDigest = (digest) => (typeof digest === 'string' && digest.length > 20 ? `${digest.slice(0, 13)}…${digest.slice(-4)}` : String(digest ?? ''));
@@ -0,0 +1,111 @@
1
+ // Discover only reads explicitly enabled public catalogs. Downloads become inert, scanned Review submissions.
2
+ // GitHub's API is the transport for pinned upstream folders; no CLI, clone, hooks, installer or remote assets.
3
+ import { createHash } from 'node:crypto';
4
+ import { join } from 'node:path';
5
+ import { localRecord } from './local-store.js';
6
+ import { buildBundle, readBundle, createImportStaging, bundlePathProblem } from './bundle.js';
7
+ import { readHeadOf } from './export.js';
8
+ import { folderDigest } from './library.js';
9
+
10
+ export const DISCOVER_SOURCES = Object.freeze([
11
+ { id: 'anthropic', label: 'Anthropic skills', repo: 'anthropics/skills', prefix: 'skills/', license: 'Per-skill terms; review LICENSE files', licenseCode: 'perSkill', transport: 'github' },
12
+ { id: 'openai', label: 'OpenAI skills', deprecated: true, repo: 'openai/skills', prefix: 'skills/.curated/', license: 'Per-skill terms; review LICENSE files', licenseCode: 'perSkill', transport: 'github' },
13
+ { id: 'claude-plugins', label: 'Claude official plugins', repo: 'anthropics/claude-plugins-official', prefix: '', license: 'Per-publisher terms', licenseCode: 'perPublisher', transport: 'github' },
14
+ { id: 'skills-sh', label: 'skills.sh', license: 'Upstream repository terms', licenseCode: 'upstream', transport: 'catalog-import', reason: 'No verified public catalog API contract; import a catalog of pinned GitHub sources.' },
15
+ { id: 'skillsmp', label: 'SkillsMP', license: 'Upstream repository terms', licenseCode: 'upstream', transport: 'catalog-import', reason: 'API credentials and terms are not configured; import a catalog of pinned GitHub sources.' },
16
+ { id: 'clawhub', label: 'ClawHub', license: 'MIT-0 for published skill content', licenseCode: 'mitZero', transport: 'catalog-import', reason: 'Registry artifact API contract is not configured; import a catalog of pinned GitHub mirrors with MIT-0 terms.' },
17
+ ]);
18
+ const fail = (status, code, message) => { throw Object.assign(new Error(message), { status, code }); };
19
+ const sha = (v) => typeof v === 'string' && /^[a-f0-9]{40}$/.test(v);
20
+ const repoOK = (v) => typeof v === 'string' && /^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/.test(v) && !v.split('/').some((x) => x === '.' || x === '..');
21
+
22
+ export async function githubJSON(path, { fetcher = globalThis.fetch, maxBytes = 8 * 1024 * 1024 } = {}) {
23
+ const response = await fetcher(`https://api.github.com${path}`, { redirect: 'error', signal: AbortSignal.timeout(15000), headers: { Accept: 'application/vnd.github+json', 'User-Agent': 'SynaBun-Skills-Studio' } });
24
+ if (!response.ok) fail(502, 'source_unavailable', `GitHub returned ${response.status}. Local skills are still available.`);
25
+ if (Number(response.headers.get('content-length')) > maxBytes) fail(413, 'source_limit', 'The catalog response exceeds its limit.');
26
+ const chunks = []; let size = 0;
27
+ for await (const chunk of response.body) { size += chunk.length; if (size > maxBytes) { await response.body.cancel?.().catch(() => {}); fail(413, 'source_limit', 'The catalog response exceeds its limit.'); } chunks.push(Buffer.from(chunk)); }
28
+ return JSON.parse(Buffer.concat(chunks).toString('utf-8'));
29
+ }
30
+
31
+ export function createSkillDiscover({ dataDir, service, settings, request = githubJSON, liveRoots, now = Date.now }) {
32
+ const store = localRecord(dataDir, 'discover', { catalogs: {} });
33
+ const staging = createImportStaging({ dir: join(dataDir, 'discover-staging'), liveRoots });
34
+ const sourceOf = (id) => { const s = DISCOVER_SOURCES.find((s) => s.id === id); if (!s) fail(400, 'invalid_source', 'Unknown Discover source.'); return s; };
35
+ function enabled(id) { sourceOf(id); if (!settings().sources.includes(id)) fail(403, 'source_disabled', 'The owner must enable this source in Discover first.'); }
36
+ function list({ sourceId = null, q = '' } = {}) {
37
+ const state = store.read();
38
+ const sources = DISCOVER_SOURCES.map((s) => ({ ...s, enabled: settings().sources.includes(s.id), checkedAt: state.catalogs[s.id]?.checkedAt || null, error: state.catalogs[s.id]?.error || null }));
39
+ const items = Object.entries(state.catalogs).filter(([id]) => (!sourceId || sourceId === id) && settings().sources.includes(id)).flatMap(([id, c]) => (c.items || []).map((i) => ({ ...i, ...(c.evidence ? {} : { licenseCode: sourceOf(id).licenseCode }) }))).filter((i) => `${i.name} ${i.repo} ${i.subpath}`.toLowerCase().includes(String(q).toLowerCase())).slice(0, 500);
40
+ return { sources, items, disclosure: 'Enabled refreshes send repository names and commit requests to api.github.com. No local skill content or usage is sent. Cached metadata and badges are not security approval.' };
41
+ }
42
+ async function tree(repo, commit) {
43
+ if (!repoOK(repo) || !sha(commit)) fail(400, 'invalid_source_pin', 'A public GitHub repository and a full commit are required.');
44
+ const result = await request(`/repos/${repo}/git/trees/${commit}?recursive=1`);
45
+ if (result.truncated || !Array.isArray(result.tree) || result.tree.length > 20000) fail(413, 'catalog_incomplete', 'The complete upstream tree could not be read.');
46
+ return result.tree;
47
+ }
48
+ async function refresh(sourceId) {
49
+ enabled(sourceId);
50
+ const source = sourceOf(sourceId);
51
+ if (!source.repo) fail(409, 'catalog_import_required', source.reason);
52
+ try {
53
+ const pin = await request(`/repos/${source.repo}/commits/HEAD`);
54
+ const rows = await tree(source.repo, pin.sha);
55
+ const items = rows.filter((e) => e.type === 'blob' && e.mode === '100644' && e.path.startsWith(source.prefix) && e.path.endsWith('/SKILL.md')).slice(0, 500).map((e) => {
56
+ const subpath = e.path.slice(0, -9);
57
+ return { sourceId, name: subpath.split('/').pop(), repo: source.repo, subpath, commit: pin.sha, license: source.license, licenseCode: source.licenseCode, id: createHash('sha256').update(`${sourceId}:${source.repo}:${subpath}:${pin.sha}`).digest('hex') };
58
+ });
59
+ const state = store.read(); state.catalogs[sourceId] = { checkedAt: now(), items, error: null }; store.write(state);
60
+ return list({ sourceId });
61
+ } catch (e) {
62
+ const state = store.read(); state.catalogs[sourceId] = { ...state.catalogs[sourceId], error: e.code || 'source_unavailable' }; store.write(state); throw e;
63
+ }
64
+ }
65
+ function importCatalog(sourceId, entries) {
66
+ enabled(sourceId);
67
+ if (!Array.isArray(entries) || entries.length > 500) fail(400, 'invalid_catalog', 'A catalog has at most 500 pinned entries.');
68
+ const items = entries.map((e) => {
69
+ if (!repoOK(e.repo) || !sha(e.commit) || bundlePathProblem(e.subpath) || typeof e.license !== 'string' || e.license.length > 500) fail(400, 'invalid_catalog', 'Each entry needs repo, full commit, safe subpath and declared license.');
70
+ if (sourceId === 'clawhub' && e.license !== 'MIT-0') fail(422, 'license_conflict', 'ClawHub content must carry MIT-0 terms.');
71
+ return { sourceId, repo: e.repo, commit: e.commit, subpath: e.subpath, name: e.subpath.split('/').pop(), license: e.license, id: createHash('sha256').update(`${sourceId}:${e.repo}:${e.subpath}:${e.commit}`).digest('hex') };
72
+ });
73
+ const state = store.read(); state.catalogs[sourceId] = { items, checkedAt: now(), error: null, evidence: 'owner-imported catalog; unverified publisher claim' }; store.write(state);
74
+ return list({ sourceId });
75
+ }
76
+ async function stage(itemId, author) {
77
+ const entry = Object.values(store.read().catalogs).flatMap((c) => c.items || []).find((i) => i.id === itemId && settings().sources.includes(i.sourceId));
78
+ if (!entry) fail(404, 'item_not_found', 'That enabled catalog has no such item.');
79
+ enabled(entry.sourceId);
80
+ const rows = await tree(entry.repo, entry.commit);
81
+ const selected = rows.filter((r) => r.path.startsWith(`${entry.subpath}/`) && r.type !== 'tree');
82
+ if (!selected.length || selected.length > 200 || selected.some((r) => !['100644', '100755'].includes(r.mode) || r.type !== 'blob' || !sha(r.sha) || bundlePathProblem(r.path.slice(entry.subpath.length + 1)))) fail(422, 'unsafe_source', 'The source is empty, too large, or contains links or unsupported paths.');
83
+ if (selected.reduce((n, r) => n + (Number(r.size) || 0), 0) > 18 * 1024 * 1024) fail(413, 'source_limit', 'The skill is larger than the staging limit.');
84
+ const files = []; let size = 0;
85
+ // Repository license is evidence too; keep it alongside the skill when it has no own LICENSE.
86
+ const license = rows.find((r) => /^(?:LICENSE|LICENSE\.md|LICENSE\.txt)$/.test(r.path) && r.type === 'blob' && r.mode === '100644' && sha(r.sha));
87
+ if (license && !selected.some((r) => /\/LICENSE(?:\.[^/]+)?$/i.test(r.path))) selected.push({ ...license, path: `${entry.subpath}/LICENSE.upstream` });
88
+ for (const row of selected) {
89
+ enabled(entry.sourceId); // revocation during a download stops the next outbound request
90
+ const blob = await request(`/repos/${entry.repo}/git/blobs/${row.sha}`);
91
+ if (blob.encoding !== 'base64' || typeof blob.content !== 'string') fail(502, 'invalid_blob', 'The upstream blob is not base64.');
92
+ const bytes = Buffer.from(blob.content.replace(/\s/g, ''), 'base64'); size += bytes.length;
93
+ if (size > 18 * 1024 * 1024 || bytes.length > 8 * 1024 * 1024) fail(413, 'source_limit', 'The skill is larger than the staging limit.');
94
+ const digest = createHash('sha1').update(`blob ${bytes.length}\0`).update(bytes).digest('hex');
95
+ if (digest !== row.sha) fail(409, 'source_changed', 'The upstream file does not match its immutable blob id.');
96
+ files.push({ path: row.path.slice(entry.subpath.length + 1), bytes, executable: row.mode === '100755' });
97
+ }
98
+ const name = readHeadOf(files).parsed.data?.name;
99
+ const read = readBundle(buildBundle({ type: 'skill', name, files, dirs: [] }).bundle);
100
+ const digest = folderDigest({ files: read.files, dirs: read.dirs });
101
+ const scan = service.scanFolder(read, { name, digest });
102
+ const stagingId = staging.stage(read, { provenance: entry, scan, license: entry.license });
103
+ const staged = staging.load(stagingId);
104
+ const made = service.library.createSkill({ name, files: staged.files, dirs: staged.dirs, source: { kind: 'github', uri: `https://github.com/${entry.repo}`, subpath: entry.subpath, requestedRef: entry.commit, resolvedCommit: entry.commit, stagingId },
105
+ author, origin: { kind: 'discover', sourceId: entry.sourceId, repo: entry.repo, license: entry.license } });
106
+ const review = service.submitReview({ revisionId: made.revision.revisionId, author, assumptions: [`License: ${entry.license}. Publisher claims need review.`], releaseNotes: `Discovered from ${entry.repo}@${entry.commit}:${entry.subpath}. Inert staging ${stagingId}.` });
107
+ service.announce({ type: 'revision.created', skillId: made.skill.skillId, revisionId: made.revision.revisionId });
108
+ return { stagingId, skillId: made.skill.skillId, revisionId: made.revision.revisionId, reviewId: review.review.reviewId, scan, provenance: entry, installed: false };
109
+ }
110
+ return { list, refresh, importCatalog, stage };
111
+ }
@@ -0,0 +1,216 @@
1
+ // ── The skill-drafting worker's confinement (P3 review) ──
2
+ // A creation (lib/skills/creation.js) drafts a skill with ONE Claude Code worker of the Assistant's dispatcher. That
3
+ // worker reads sources that may be hostile (a web page, a transcript, someone else's skill), so what it can do is
4
+ // enforced, not asked for: lib/native-loop-providers.js gives a confined run only the tools below (`tools`), no MCP
5
+ // server at all (`strictMcpConfig`), no user or project settings (no user hooks, no allow rules), and checks every
6
+ // tool call with draftToolDecision() twice: in a PreToolUse hook, which Claude Code runs before any permission mode,
7
+ // and in canUseTool.
8
+ //
9
+ // Read, Glob, Grep inside the creation's workspace (its sources/ and what it wrote)
10
+ // Write, Edit inside the workspace's out/ folder only
11
+ // anything else refused: no shell, no web, no subagents, no MCP tools, no notebooks, no skills
12
+ //
13
+ // A path is checked as Claude Code would open it (relative to the workspace when not absolute), then at its real
14
+ // location (lib/path-confine.js realPathOf: links resolved, a dangling link refused), so neither a link nor ".." leads
15
+ // out; a ".." segment is refused outright. Glob and Grep patterns are relative and never climb. A file that is written
16
+ // must be a plain file with one name (no hard link to somewhere else). Anything that cannot be checked is refused.
17
+ // Every provider holds a drafting worker the way it can (draftConfinementSupport): Claude Code as above, exactly;
18
+ // Codex under its sandbox (writes inside the workspace only, no network, no web search, every MCP server of its
19
+ // config disabled; it keeps its shell, which the sandbox confines: codexDraftOptions); OpenCode with its shell, web,
20
+ // subagent and skill tools switched off, no MCP server, and every edit asked and answered by draftToolDecision
21
+ // inside the workspace (openCodeDraftConfig, draftOpenCodeDecision). The dispatcher refuses a provider that cannot
22
+ // (assistant-dispatch.js normalizeSpec), never starts a run unconfined and never escalates it onto one that cannot.
23
+
24
+ import { existsSync, lstatSync, readFileSync, realpathSync } from 'node:fs';
25
+ import { isAbsolute, join, resolve } from 'node:path';
26
+ import { isInside, realPathOf } from '../path-confine.js';
27
+
28
+ export const DRAFT_CONFINEMENT = 'skill-draft';
29
+ /** The writable folder under the workspace (creation-brief.js OUT_DIR). */
30
+ export const DRAFT_WRITABLE = 'out';
31
+ /** The only built-in tools a drafting worker has. */
32
+ export const DRAFT_TOOLS = Object.freeze(['Read', 'Write', 'Edit', 'Glob', 'Grep']);
33
+ /** Removed as well, by name (belt and braces: `tools` already leaves them out, and a hook refuses whatever is not above). */
34
+ export const DRAFT_DISALLOWED_TOOLS = Object.freeze([
35
+ 'Bash', 'BashOutput', 'KillShell', 'KillBash', 'Monitor', 'WebFetch', 'WebSearch', 'Task', 'Agent', 'TaskStop', 'NotebookEdit',
36
+ 'NotebookRead', 'Skill', 'SlashCommand', 'TodoWrite', 'ExitPlanMode', 'EnterPlanMode', 'AskUserQuestion', 'ListMcpResources',
37
+ 'ReadMcpResource', 'Workflow', 'CronCreate', 'ScheduleWakeup', 'RemoteTrigger', 'EnterWorktree', 'ExitWorktree', 'Artifact',
38
+ ]);
39
+ const READERS = new Set(['Read', 'Glob', 'Grep']);
40
+ const WRITERS = new Set(['Write', 'Edit']);
41
+ const REFUSED = 'A skill-drafting run has only Read, Write, Edit, Glob and Grep, inside its own folder: no shell, no web, no MCP tools and no subagents.';
42
+
43
+ /**
44
+ * A confinement as a run carries it, checked: { kind, root (the workspace's real path), writable (its out/) }.
45
+ * Throws when it cannot be used (the run must not start unconfined).
46
+ * @param {{ kind?: string }|null} confinement
47
+ * @param {string} cwd the run's working directory (the creation's workspace)
48
+ */
49
+ export function resolveDraftConfinement(confinement, cwd) {
50
+ if (!confinement || confinement.kind !== DRAFT_CONFINEMENT) throw new Error('Unknown confinement.');
51
+ const root = realpathSync(String(cwd || ''));
52
+ const writableReal = realpathSync(join(root, DRAFT_WRITABLE));
53
+ if (writableReal !== join(root, DRAFT_WRITABLE) || !lstatSync(writableReal).isDirectory()) throw new Error(`The workspace's ${DRAFT_WRITABLE}/ folder is not a plain folder.`);
54
+ return { kind: DRAFT_CONFINEMENT, root, writable: writableReal };
55
+ }
56
+
57
+ /** A path the worker names, where it really is, or null when it cannot be one of the workspace's. */
58
+ function placeOf(raw, root) {
59
+ if (typeof raw !== 'string' || !raw || raw.length > 4096 || /[\u0000-\u001f\u007f]/.test(raw) || raw.startsWith('~')) return null;
60
+ if (raw.split(/[\\/]+/).includes('..')) return null;
61
+ const abs = isAbsolute(raw) ? resolve(raw) : resolve(root, raw);
62
+ try { return realPathOf(abs); } catch { return null; }
63
+ }
64
+
65
+ /** A Glob / Grep pattern: relative, no "..", no home. */
66
+ function patternProblem(p) {
67
+ if (p === undefined || p === null) return null;
68
+ if (typeof p !== 'string' || p.length > 1000 || /[\u0000-\u001f\u007f]/.test(p)) return 'is not a plain pattern';
69
+ if (p.startsWith('/') || p.startsWith('\\') || p.startsWith('~') || /^[A-Za-z]:/.test(p)) return 'is absolute: patterns are relative to the drafting folder';
70
+ if (p.split(/[\\/]+/).includes('..')) return 'climbs out with "..": patterns stay inside the drafting folder';
71
+ return null;
72
+ }
73
+
74
+ /**
75
+ * Whether a drafting worker may make this tool call: null when it may, else why not (the worker reads the reason).
76
+ * @param {string} toolName
77
+ * @param {object} input the tool's input
78
+ * @param {{ root: string, writable: string }} confinement resolveDraftConfinement()'s answer
79
+ */
80
+ export function draftToolDecision(toolName, input, confinement) {
81
+ try {
82
+ const name = String(toolName || '');
83
+ const args = input && typeof input === 'object' ? input : {};
84
+ const { root, writable } = confinement || {};
85
+ if (!root || !writable) return 'This run\'s drafting folder is not known: nothing can be done.';
86
+ if (!READERS.has(name) && !WRITERS.has(name)) return `${name || 'That tool'} is not available. ${REFUSED}`;
87
+ if (name === 'Read') {
88
+ const at = placeOf(args.file_path, root);
89
+ if (!at || !isInside(root, at)) return `Read is limited to the drafting folder (${root}). ${REFUSED}`;
90
+ return null;
91
+ }
92
+ if (name === 'Glob' || name === 'Grep') {
93
+ if (args.path !== undefined && args.path !== null && args.path !== '') {
94
+ const at = placeOf(args.path, root);
95
+ if (!at || !isInside(root, at, { allowRoot: true })) return `${name} searches the drafting folder only (${root}).`;
96
+ }
97
+ const bad = name === 'Glob' ? patternProblem(args.pattern) : patternProblem(args.glob);
98
+ if (name === 'Glob' && (typeof args.pattern !== 'string' || !args.pattern)) return 'Glob needs a pattern.';
99
+ if (bad) return `The ${name === 'Glob' ? 'pattern' : 'glob'} ${bad}.`;
100
+ return null;
101
+ }
102
+ // Write, Edit: a plain file under out/.
103
+ const at = placeOf(args.file_path, root);
104
+ if (!at || !isInside(writable, at)) return `${name} writes inside ${writable} only: the draft goes in ${DRAFT_WRITABLE}/<name>/. ${REFUSED}`;
105
+ let st = null;
106
+ try { st = lstatSync(at); } catch { st = null; }
107
+ if (st && (!st.isFile() || st.nlink > 1)) return `${at} is not a plain file of the draft: it cannot be written.`;
108
+ return null;
109
+ } catch (err) {
110
+ return `The call could not be checked (${err?.message || err}), so it is refused.`;
111
+ }
112
+ }
113
+
114
+ /** The PreToolUse hook a confined run gets (no matcher: every tool call, subagents' included). */
115
+ export function draftPreToolUseHook(confinement) {
116
+ return async (input) => {
117
+ const reason = draftToolDecision(input?.tool_name, input?.tool_input || {}, confinement);
118
+ return reason ? { hookSpecificOutput: { hookEventName: 'PreToolUse', permissionDecision: 'deny', permissionDecisionReason: reason } } : {};
119
+ };
120
+ }
121
+
122
+ // ── The other providers ──
123
+
124
+ /** The providers a drafting run can be confined on, in the order the Create page lists them. */
125
+ export const DRAFT_CONFINED_PROVIDERS = Object.freeze(['claude-code', 'codex', 'opencode']);
126
+
127
+ /**
128
+ * What a provider can hold a drafting worker to: { ok: true, level, note } or { ok: false, reason }. `level`:
129
+ * 'strict' (file tools only, every call checked), 'sandbox' (the OS sandbox around a shell), 'rules' (the
130
+ * provider's permission rules plus its tool switches).
131
+ */
132
+ export function draftConfinementSupport(provider, { platform = process.platform } = {}) {
133
+ const name = String(provider || '');
134
+ if (name === 'claude-code') return { ok: true, level: 'strict', note: 'Read, Write, Edit, Glob and Grep inside its folder only; no shell, no web, no MCP tools, no subagents.' };
135
+ if (name === 'codex') {
136
+ return {
137
+ ok: true, level: 'sandbox',
138
+ 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.`,
139
+ };
140
+ }
141
+ if (name === 'opencode') return { ok: true, level: 'rules', note: 'OpenCode with its shell, web, subagent and skill tools switched off, no MCP server, and every edit answered inside its folder only.' };
142
+ // Antigravity (a worker of the Assistant) has no drafting confinement in SynaBun: a drafting run never starts unconfined.
143
+ if (name === 'antigravity') return { ok: false, reason: 'Antigravity cannot be held to a drafting folder: skill drafting runs on Claude Code, Codex and OpenCode.' };
144
+ return { ok: false, reason: `${name || 'That provider'} cannot be held to a drafting folder: skill drafting runs on Claude Code, Codex and OpenCode.` };
145
+ }
146
+
147
+ /** The MCP server names a Codex config.toml declares ([mcp_servers.NAME] tables), for disabling them one by one. */
148
+ export function codexMcpServerNames(codexHome) {
149
+ const names = new Set();
150
+ try {
151
+ const file = join(String(codexHome || ''), 'config.toml');
152
+ if (!codexHome || !existsSync(file)) return [];
153
+ for (const line of readFileSync(file, 'utf8').split(/\r?\n/)) {
154
+ const m = /^\s*\[\s*mcp_servers\.(?:"([^"]+)"|'([^']+)'|([A-Za-z0-9_-]+))/.exec(line);
155
+ if (m) names.add(m[1] ?? m[2] ?? m[3]);
156
+ }
157
+ } catch { /* an unreadable config declares nothing we can name; SynaBun's own entry is disabled by name anyway */ }
158
+ return [...names];
159
+ }
160
+
161
+ /**
162
+ * Codex: the thread options and the config overrides of a confined drafting run. The sandbox holds the shell
163
+ * (writable roots: the workspace and Codex's own temp folders; no parent folder, no network); every MCP server the
164
+ * account's config declares, and SynaBun's, is disabled; web search is off.
165
+ */
166
+ export function codexDraftOptions(confined, { codexHome = null, projectDir = null } = {}) {
167
+ if (!confined || confined.kind !== DRAFT_CONFINEMENT) throw new Error('Unknown confinement.');
168
+ // The account's config.toml and a project-level .codex/config.toml in the drafting folder (SynaBun makes that folder,
169
+ // so there is none; checked all the same). Servers a Codex plugin brings cannot be named here: Codex loads plugins
170
+ // from its own home, which the sandbox does not reach, and SynaBun installs none for a worker.
171
+ const servers = new Set(['SynaBun', ...codexMcpServerNames(codexHome), ...(projectDir ? codexMcpServerNames(join(String(projectDir), '.codex')) : [])]);
172
+ const mcp_servers = {};
173
+ for (const name of servers) mcp_servers[name] = { enabled: false };
174
+ return {
175
+ thread: { sandboxMode: 'workspace-write', networkAccessEnabled: false, webSearchMode: 'disabled', additionalDirectories: undefined },
176
+ config: { mcp_servers },
177
+ };
178
+ }
179
+
180
+ /**
181
+ * OpenCode's tools a drafting run does without: refused by its rules (OpenCode 2's permission actions). `execute` is
182
+ * Code Mode, whose `opencode` namespace moves the session to another folder (seen live on 2.1.4: a moved session read
183
+ * a file outside the workspace with external_directory denied), creates and removes worktrees and reads MCP
184
+ * resources; `question` would wait for an answer nobody gives.
185
+ */
186
+ export const OPENCODE_DRAFT_TOOLS_OFF = Object.freeze(['shell', 'webfetch', 'websearch', 'subagent', 'skill', 'todo', 'lsp', 'external_directory', 'browser', 'execute', 'question']);
187
+
188
+ /**
189
+ * OpenCode: the config a confined drafting run's serve and session get: OpenCode 2's permission rules (every edit
190
+ * asked, every other tool refused) and no MCP server.
191
+ */
192
+ export function openCodeDraftConfig() {
193
+ return {
194
+ permissions: [
195
+ { action: 'edit', resource: '*', effect: 'ask' },
196
+ ...OPENCODE_DRAFT_TOOLS_OFF.map((action) => ({ action, resource: '*', effect: 'deny' })),
197
+ ],
198
+ mcp: 'off',
199
+ };
200
+ }
201
+
202
+ /** An OpenCode permission ask under the draft confinement: null when it may go ahead, else why not. */
203
+ export function draftOpenCodeDecision(request, confined) {
204
+ const kind = String(request?.permission || request?.type || '').toLowerCase();
205
+ if (/^(?:edit|write|patch|multiedit|apply_patch)$/.test(kind)) {
206
+ const meta = request?.metadata && typeof request.metadata === 'object' ? request.metadata : {};
207
+ let raw = null;
208
+ for (const key of ['filePath', 'filepath', 'file_path', 'path']) if (typeof meta[key] === 'string' && meta[key]) { raw = meta[key]; break; }
209
+ if (!raw) {
210
+ const pattern = request?.pattern ?? request?.patterns;
211
+ raw = typeof pattern === 'string' ? pattern : Array.isArray(pattern) && typeof pattern[0] === 'string' ? pattern[0] : null;
212
+ }
213
+ return draftToolDecision('Write', { file_path: raw }, confined);
214
+ }
215
+ return `${kind || 'That tool'} is not available. ${REFUSED}`;
216
+ }