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,476 @@
1
+ // Playwright context.route(), even with a narrow matcher, enables Fetch("*")
2
+ // AND disables HTTP cache on every target. Keep our safety rules on a separate
3
+ // CDP client with native, selective Fetch patterns. Never change cache, Runtime,
4
+ // identity, emulation, or page scripts here. Unmatched requests stay in Chromium.
5
+ //
6
+ // Auto-attach pauses new pages, OOPIFs and workers so
7
+ // their FIRST request is guarded too. Closing this client releases its sessions;
8
+ // it never closes a user's target or browser.
9
+ //
10
+ // Faults are per target first. A target is protected once its Fetch rules are in
11
+ // place; releasing it (Runtime.runIfWaitingForDebugger) is not part of that, and its
12
+ // reply comes from the target itself once it actually runs: Chromium holds a new
13
+ // service worker until EVERY auto-attached CDP client has released it, and does not
14
+ // answer a stopped worker's Runtime commands until it runs again. A slow or silent
15
+ // reply is therefore never a fault. A paused service/shared worker whose rules cannot
16
+ // be installed stays paused and is retried; pages, frames, dedicated workers and
17
+ // targets already running still fail closed for the whole context, and a faulted
18
+ // context re-arms with a fresh attachment (recoverBrowserNetworkGuard).
19
+ import { connectGuardCdp } from './browser-guard-cdp.js';
20
+ const guards = new WeakMap();
21
+ const endpoints = new WeakMap();
22
+ const legacyRules = new WeakMap();
23
+ const FILTER = ['page', 'iframe', 'worker', 'shared_worker', 'service_worker'].map(type => ({ type }));
24
+ const AUTO = { autoAttach: true, waitForDebuggerOnStart: true, flatten: true, filter: FILTER };
25
+ const gone = error => /closed|detached|No session|No target|Target.*not found|Session with given id not found/i.test(error?.message || '');
26
+ const expiredRequest = error => /^Fetch\.(?:continueRequest|failRequest)$/.test(error?.method || '')
27
+ && /Invalid InterceptionId|Invalid interception id/i.test(error.message);
28
+ // Workers whose own requests only their own Fetch rules intercept. Dedicated workers
29
+ // are intercepted by their owning page or frame instead.
30
+ const OWN_FETCH_WORKERS = new Set(['service_worker', 'shared_worker']);
31
+ const TIMING = {
32
+ quarantineRetryMs: [1000, 5000, 15000, 60000], // the last delay repeats while the worker exists
33
+ rearmBackoffMs: [2000, 5000, 15000, 30000, 60000], // after a failed re-arm; the last repeats
34
+ recoverTimeoutMs: 10000, // the longest a caller waits for one re-arm
35
+ };
36
+ export const NETWORK_GUARD_FAULT_EVENT = 'synabun-network-guard-fault';
37
+ export const NETWORK_GUARD_RECOVERED_EVENT = 'synabun-network-guard-recovered';
38
+
39
+ /** Register only externally owned Chromium contexts with an existing endpoint. */
40
+ export function registerBrowserNetworkEndpoint(context, endpoint, { connect = connectGuardCdp, timing } = {}) {
41
+ endpoints.set(context, { endpoint, connect, timing: { ...TIMING, ...timing } });
42
+ }
43
+
44
+ export function usesNativeBrowserNetworkGuard(context) { return endpoints.has(context); }
45
+
46
+ /** One flattened CDP connection and every session it holds. Re-arming replaces it whole. */
47
+ class GuardLink {
48
+ constructor(guard) {
49
+ this.guard = guard;
50
+ this.children = new Map();
51
+ this.installing = new Set();
52
+ this.timers = new Set();
53
+ this.fault = null;
54
+ this.closed = false;
55
+ }
56
+ /** A fault of this link. It is the context's fault only while this link is the current one. */
57
+ failed(error) {
58
+ if (this.closed) return;
59
+ this.fault ||= error;
60
+ if (this.guard.link === this) this.guard.fault = error;
61
+ }
62
+ later(delay, work) {
63
+ if (this.closed) return;
64
+ const timer = setTimeout(() => { this.timers.delete(timer); if (!this.closed) work(); }, delay);
65
+ timer.unref?.();
66
+ this.timers.add(timer);
67
+ }
68
+ async open() {
69
+ const { endpoint, connect } = endpoints.get(this.guard.context);
70
+ this.connecting = connect(endpoint);
71
+ this.root = await this.connecting;
72
+ if (this.closed) { await this.root.detach(); throw new Error('Network guard disposed during setup'); }
73
+ this.root.on('event', event => this.dispatch(event.sessionId ? this.children.get(event.sessionId) : this.root, event.method, event.params));
74
+ this.root.on('close', () => {
75
+ if (this.closed) return;
76
+ this.failed(new Error('Network guard CDP disconnected; reconnect the browser session'));
77
+ // Cancel pending automation and refuse subsequent actions until a fresh
78
+ // protected attachment exists. Never reinstate context.route on MoreLogin:
79
+ // it would mutate unrelated native traffic and cache even during teardown.
80
+ console.error('[browser-network-guard] CDP disconnected; browser automation refused until the guard re-arms');
81
+ });
82
+ // A page-target attachment can arrive after a popup's initial navigation
83
+ // has started (another CDP client may resume it). The browser Fetch domain
84
+ // closes that gap for selective Document requests only. Attribute each to
85
+ // this profile before deciding; never apply rules to a foreign context.
86
+ await this.configure(this.root);
87
+ await this.root.send('Target.setAutoAttach', AUTO);
88
+ while (this.installing.size) await Promise.all([...this.installing]);
89
+ if (this.fault) throw this.fault;
90
+ }
91
+ async configure(child) {
92
+ // Dedicated workers have no Fetch domain in Chromium. Their requests are
93
+ // intercepted by the owning page/frame (also for nested workers), as in
94
+ // Playwright's CRNetworkManager workerFrame path. Never resume a worker
95
+ // until that parent's current selective guard is installed.
96
+ if (child.target?.type === 'worker') {
97
+ if (!child.parent?.guarded || child.parent.quarantined || !child.parent.configuration) {
98
+ throw new Error(`Network guard worker ${child.target.targetId} has no protected network parent`);
99
+ }
100
+ await child.parent.configuration;
101
+ return;
102
+ }
103
+ const patterns = this.guard.patterns().map(pattern => child === this.root ? { ...pattern, resourceType: 'Document' } : pattern);
104
+ await child.send(patterns.length ? 'Fetch.enable' : 'Fetch.disable', patterns.length ? { patterns } : {});
105
+ }
106
+ /** Rules in place for the target and for what it starts next. */
107
+ async protect(child) {
108
+ await (child.configuration = this.configure(child));
109
+ if (this.closed) return;
110
+ await child.send('Target.setAutoAttach', AUTO);
111
+ child.protected = true;
112
+ }
113
+ /** Release a target this client holds paused. Never awaited and never a fault: the
114
+ * reply comes from the target once it actually runs, which another CDP client of the
115
+ * profile, or a stopped service worker, can delay indefinitely. The target's rules are
116
+ * already in place, so its requests are guarded whenever it runs. */
117
+ resume(child) {
118
+ child.send('Runtime.runIfWaitingForDebugger').catch(error => {
119
+ if (this.closed || gone(error)) return;
120
+ console.warn(`[browser-network-guard] ${error.message}; the target keeps its rules and runs when Chromium releases it`);
121
+ });
122
+ }
123
+ /** Whether a failed command's target is gone, which is no fault. A reply can be lost
124
+ * while its target is destroyed, so a timeout asks the browser before deciding. */
125
+ async vanished(child, error) {
126
+ if (this.closed || gone(error) || this.children.get(child.id) !== child) return true;
127
+ try {
128
+ const { targetInfo } = await this.root.send('Target.getTargetInfo', { targetId: child.target.targetId });
129
+ return !targetInfo;
130
+ } catch (probe) {
131
+ return /No target with given id/i.test(probe.protocolMessage || probe.message);
132
+ }
133
+ }
134
+ /** A paused service/shared worker whose rules could not be installed. Nothing else
135
+ * intercepts its requests, so it must not run unprotected: keep holding it paused
136
+ * (no other target is affected), retry, and release it once its rules are in place. */
137
+ quarantine(child, error, attempt = 0) {
138
+ child.quarantined = error;
139
+ const delays = this.guard.timing.quarantineRetryMs;
140
+ const delay = delays[Math.min(attempt, delays.length - 1)];
141
+ if (!attempt) console.error(`[browser-network-guard] ${error.message}; this worker stays paused, retrying its protection`);
142
+ this.later(delay, async () => {
143
+ if (this.children.get(child.id) !== child) return;
144
+ try {
145
+ await this.protect(child);
146
+ } catch (retryError) {
147
+ if (await this.vanished(child, retryError)) return;
148
+ this.quarantine(child, retryError, attempt + 1);
149
+ return;
150
+ }
151
+ if (this.closed) return;
152
+ child.quarantined = null;
153
+ console.warn(`[browser-network-guard] worker ${child.target.targetId} protected after ${attempt + 1} retr${attempt ? 'ies' : 'y'}; releasing it`);
154
+ this.resume(child);
155
+ });
156
+ }
157
+ async ownsDocument(frameId) {
158
+ try {
159
+ const { targetInfo } = await this.root.send('Target.getTargetInfo', { targetId: frameId });
160
+ return targetInfo.browserContextId === this.guard.contextId;
161
+ } catch (error) {
162
+ // Same-process subframes have frame IDs, but no separate Target. Find
163
+ // their owning protected page; unrelated incognito frames stay native.
164
+ if (!/No target with given id found/i.test(error.message)) throw error;
165
+ const contains = tree => tree.frame.id === frameId || tree.childFrames?.some(contains);
166
+ let uncertain = false;
167
+ for (const child of this.children.values()) {
168
+ if (!child.guarded || !['page', 'iframe'].includes(child.target.type)) continue;
169
+ try {
170
+ if (contains((await child.send('Page.getFrameTree')).frameTree)) return true;
171
+ } catch (treeError) { if (!gone(treeError)) uncertain = true; }
172
+ }
173
+ // A protected page that cannot list its frames may hold this one. Decide it as
174
+ // the profile's (the stricter answer) instead of faulting every tab.
175
+ return uncertain;
176
+ }
177
+ }
178
+ dispatch(parent, method, event) {
179
+ if (this.closed) return;
180
+ if (method === 'Target.attachedToTarget') {
181
+ const child = {
182
+ id: event.sessionId, parent, target: event.targetInfo,
183
+ send: async (method, params) => {
184
+ try { return await this.root.send(method, params, event.sessionId); }
185
+ catch (error) {
186
+ Object.assign(error, { method, targetType: child.target.type, targetId: child.target.targetId,
187
+ parentTargetType: parent?.target?.type, parentTargetId: parent?.target?.targetId });
188
+ error.message = `${method} [target=${child.target.type}:${child.target.targetId}, session=${child.id}, parent=${parent?.target?.type || 'browser'}:${parent?.target?.targetId || 'root'}]: ${error.protocolMessage || error.message}`;
189
+ throw error;
190
+ }
191
+ },
192
+ close: () => this.root.rejectSession(event.sessionId),
193
+ };
194
+ this.children.set(event.sessionId, child);
195
+ const installing = this.attach(child, event).catch(error => {
196
+ if (!gone(error)) {
197
+ this.failed(error);
198
+ // Do not resume an unguarded new target. Existing user tabs are never
199
+ // closed. Tool operations fail until the guard re-arms.
200
+ console.error('[browser-network-guard] target protection failed:', error.message);
201
+ }
202
+ }).finally(() => this.installing.delete(installing));
203
+ this.installing.add(installing);
204
+ } else if (method === 'Target.detachedFromTarget') {
205
+ this.children.get(event.sessionId)?.close();
206
+ this.children.delete(event.sessionId);
207
+ } else if (method === 'Fetch.requestPaused') {
208
+ if (!parent) return; // event queued before this target's detach arrived
209
+ void this.request(parent, event).catch(error => {
210
+ if (!gone(error)) { this.failed(error); console.error('[browser-network-guard] request refused after guard failure:', error.message); }
211
+ });
212
+ } else if (method === 'Inspector.targetReloadedAfterCrash') {
213
+ // A stopped service worker that runs again keeps this session and its rules.
214
+ // Release it as Playwright does, so this client never holds it.
215
+ if (parent?.protected && !parent.quarantined) this.resume(parent);
216
+ }
217
+ }
218
+ async attach(child, event) {
219
+ if (this.closed) return;
220
+ const waiting = !!event.waitingForDebugger;
221
+ const inherited = !event.targetInfo.browserContextId && child.parent?.guarded;
222
+ if (event.targetInfo.browserContextId !== this.guard.contextId && !inherited) {
223
+ // A browser-level CDP connection also sees other incognito contexts. Our
224
+ // session there intercepts nothing: releasing it is never this profile's fault.
225
+ if (waiting) this.resume(child);
226
+ await child.parent.send('Target.detachFromTarget', { sessionId: child.id }).catch(error => {
227
+ if (!gone(error)) console.warn('[browser-network-guard] could not release a foreign target:', error.message);
228
+ });
229
+ return;
230
+ }
231
+ child.guarded = true;
232
+ try {
233
+ await this.protect(child);
234
+ } catch (error) {
235
+ if (await this.vanished(child, error)) return;
236
+ if (waiting && OWN_FETCH_WORKERS.has(child.target.type)) { this.quarantine(child, error); return; }
237
+ // Pages, frames, dedicated workers and anything already running fail closed.
238
+ throw error;
239
+ }
240
+ if (this.closed) return;
241
+ // Only a target that waits for us is released; a running or stopped one is not.
242
+ if (waiting) this.resume(child);
243
+ }
244
+ async request(child, event) {
245
+ const { requestId, request } = event;
246
+ try {
247
+ const fault = this.fault || this.guard.fault;
248
+ if (fault) throw fault;
249
+ if (child === this.root && !await this.ownsDocument(event.frameId)) {
250
+ await child.send('Fetch.continueRequest', { requestId });
251
+ return;
252
+ }
253
+ let headers;
254
+ for (const rule of this.guard.rules.values()) {
255
+ const decision = rule.decide(request);
256
+ if (decision?.abort) {
257
+ await child.send('Fetch.failRequest', { requestId, errorReason: 'BlockedByClient' });
258
+ return;
259
+ }
260
+ if (decision?.headers) headers = { ...headers, ...decision.headers };
261
+ }
262
+ const params = { requestId };
263
+ if (headers) {
264
+ const replacements = new Set(Object.keys(headers).map(name => name.toLowerCase()));
265
+ params.headers = Object.entries(request.headers).filter(([name]) => !replacements.has(name.toLowerCase()))
266
+ .concat(Object.entries(headers)).map(([name, value]) => ({ name, value: String(value) }));
267
+ }
268
+ await child.send('Fetch.continueRequest', params);
269
+ } catch (error) {
270
+ // A navigation/closed tab can retire the request before continue/fail is
271
+ // processed. These protocol replies do not invalidate other targets.
272
+ if (gone(error) || expiredRequest(error)) return;
273
+ // Never continue without a required financial/local marker decision.
274
+ await child.send('Fetch.failRequest', { requestId, errorReason: 'BlockedByClient' }).catch(() => {});
275
+ throw error;
276
+ }
277
+ }
278
+ /** Current rules onto every protected target of this link. */
279
+ async reconfigure() {
280
+ await this.configure(this.root);
281
+ // A quarantined worker takes the current rules on its next retry.
282
+ await Promise.all([...this.children.values()].filter(child => child.guarded && !child.quarantined).map(async child => {
283
+ try { await (child.configuration = this.configure(child)); }
284
+ catch (error) { if (!await this.vanished(child, error)) throw error; }
285
+ }));
286
+ }
287
+ close() {
288
+ if (this.closing) return this.closing;
289
+ this.closed = true;
290
+ for (const timer of this.timers) clearTimeout(timer);
291
+ this.timers.clear();
292
+ // Detaching our browser CDP session also detaches its child sessions and
293
+ // releases paused targets. No Target.closeTarget or Browser.close.
294
+ this.closing = (async () => {
295
+ // Closing may race a handshake. Await and detach that late connection
296
+ // as well; returning early would briefly leave a new client attached.
297
+ if (!this.root && this.connecting) this.root = await this.connecting.catch(() => null);
298
+ await this.root?.detach().catch(() => {});
299
+ for (const child of this.children.values()) child.close();
300
+ this.children.clear();
301
+ })();
302
+ return this.closing;
303
+ }
304
+ }
305
+
306
+ class ContextGuard {
307
+ get fault() { return this._fault; }
308
+ set fault(error) {
309
+ if (this.disposed) return;
310
+ this._fault = error;
311
+ if (error) {
312
+ this.context.__browserNetworkGuardError = error.message;
313
+ this.context.emit?.(NETWORK_GUARD_FAULT_EVENT, error.message);
314
+ }
315
+ }
316
+ constructor(context) {
317
+ this.context = context;
318
+ this.timing = endpoints.get(context)?.timing || TIMING;
319
+ this.rules = new Map();
320
+ this.fault = null;
321
+ this.queue = Promise.resolve();
322
+ this.rearmFailures = 0;
323
+ this.rearmAt = 0;
324
+ }
325
+ patterns() {
326
+ return [...new Set([...this.rules.values()].flatMap(rule => rule.patterns))].map(urlPattern => ({ urlPattern, requestStage: 'Request' }));
327
+ }
328
+ async start() {
329
+ this.onContextClose = () => { void this.dispose(); };
330
+ this.onBrowserDisconnect = () => { void this.dispose(); };
331
+ this.context.once('close', this.onContextClose);
332
+ this.context.browser().once('disconnected', this.onBrowserDisconnect);
333
+ const page = this.context.pages().find(page => !page.isClosed());
334
+ if (!page) throw new Error('Network guard requires the attachment tab');
335
+ const probe = await this.context.newCDPSession(page);
336
+ try { this.contextId = (await probe.send('Target.getTargetInfo')).targetInfo.browserContextId; }
337
+ finally { await probe.detach(); }
338
+ if (this.disposed) throw new Error('Network guard disposed during setup');
339
+ this.link = new GuardLink(this);
340
+ await this.link.open();
341
+ }
342
+ async update(key, rule) {
343
+ if (this.disposed) throw new Error('Network guard disposed');
344
+ if (this.fault) throw this.fault;
345
+ if (rule) this.rules.set(key, rule); else this.rules.delete(key);
346
+ if (!this.ready) this.ready = this.start().catch(async error => { await this.dispose(); throw error; });
347
+ await this.ready;
348
+ await this.link.reconfigure();
349
+ }
350
+ /** Re-arm a faulted guard; resolves true once the context is protected again. One
351
+ * attempt at a time, in order with rule updates, and none during the backoff that
352
+ * follows a failed one. */
353
+ rearm() {
354
+ if (this.disposed || !this.link) return Promise.resolve(false);
355
+ if (!this.fault) return Promise.resolve(true);
356
+ if (this.rearming) return this.rearming;
357
+ if (Date.now() < this.rearmAt) return Promise.resolve(false);
358
+ const attempt = this.queue.then(() => this.replaceLink());
359
+ this.queue = attempt.catch(() => {});
360
+ this.rearming = attempt.catch(() => false).finally(() => { this.rearming = null; });
361
+ return this.rearming;
362
+ }
363
+ async replaceLink() {
364
+ if (this.disposed) return false;
365
+ if (!this.fault) return true;
366
+ // The faulted link stays attached (failing its own paused requests closed) until
367
+ // the fresh one has every current target protected, so no target is ever left
368
+ // without an attachment holding its rules.
369
+ const fresh = this.pendingLink = new GuardLink(this);
370
+ try {
371
+ await fresh.open();
372
+ } catch (error) {
373
+ await fresh.close();
374
+ const delays = this.timing.rearmBackoffMs;
375
+ this.rearmAt = Date.now() + delays[Math.min(this.rearmFailures++, delays.length - 1)];
376
+ console.error('[browser-network-guard] re-arm failed; actions stay refused:', error.message);
377
+ return false;
378
+ } finally { this.pendingLink = null; }
379
+ if (this.disposed) { await fresh.close(); return false; }
380
+ const old = this.link;
381
+ this.link = fresh;
382
+ this.rearmFailures = 0;
383
+ this.rearmAt = 0;
384
+ this._fault = null;
385
+ delete this.context.__browserNetworkGuardError;
386
+ this.context.emit?.(NETWORK_GUARD_RECOVERED_EVENT);
387
+ console.warn('[browser-network-guard] re-armed with a fresh protected attachment');
388
+ await old.close();
389
+ return true;
390
+ }
391
+ dispose() {
392
+ if (this.disposal) return this.disposal;
393
+ this.disposed = true;
394
+ if (this.onContextClose) this.context.removeListener('close', this.onContextClose);
395
+ if (this.onBrowserDisconnect) this.context.browser().removeListener('disconnected', this.onBrowserDisconnect);
396
+ this.disposal = (async () => {
397
+ await Promise.all([this.link?.close(), this.pendingLink?.close()]);
398
+ if (guards.get(this.context) === this) guards.delete(this.context);
399
+ })();
400
+ return this.disposal;
401
+ }
402
+ }
403
+
404
+ /** Re-arm a faulted native guard: a fresh protected attachment replaces the broken one,
405
+ * so the context needs no server restart. Resolves true when the context is protected
406
+ * (again), false while its actions stay refused. Never rejects. */
407
+ export async function recoverBrowserNetworkGuard(context, { timeoutMs } = {}) {
408
+ const guard = guards.get(context);
409
+ if (!guard) return !context?.__browserNetworkGuardError;
410
+ let timer;
411
+ try {
412
+ return await Promise.race([
413
+ guard.rearm(),
414
+ new Promise(resolve => { timer = setTimeout(() => resolve(false), timeoutMs ?? guard.timing.recoverTimeoutMs); timer.unref?.(); }),
415
+ ]);
416
+ } catch { return false; }
417
+ finally { clearTimeout(timer); }
418
+ }
419
+
420
+ /** Explicitly release only this context's safety transport, including failed setup. */
421
+ export async function disposeBrowserNetworkGuard(context) {
422
+ await guards.get(context)?.dispose();
423
+ }
424
+
425
+ /** Add/remove a named rule. Decisions compose: an abort always wins over headers. */
426
+ export function setBrowserNetworkRule(context, key, rule) {
427
+ if (!context) return Promise.reject(new Error('Network guard needs a browser context'));
428
+ if (rule && (!rule.patterns?.length || rule.patterns.some(pattern => pattern === '*' || pattern === '*://*/*'))) {
429
+ return Promise.reject(new Error('Network guard requires selective URL patterns'));
430
+ }
431
+ // Managed browsers retain their established Playwright guards. MoreLogin is
432
+ // registered at attachment and MUST use the native transport; a failure there
433
+ // rejects setup rather than silently falling back to wildcard interception.
434
+ if (!endpoints.has(context)) return setManagedBrowserRule(context, key, rule);
435
+ let guard = guards.get(context);
436
+ if (!guard) { guard = new ContextGuard(context); guards.set(context, guard); }
437
+ const updated = guard.queue.then(() => guard.update(key, rule));
438
+ guard.queue = updated.catch(error => { guard.fault = error; });
439
+ return updated;
440
+ }
441
+
442
+ async function setManagedBrowserRule(context, key, rule) {
443
+ let entries = legacyRules.get(context);
444
+ if (!entries) { entries = new Map(); legacyRules.set(context, entries); }
445
+ const old = entries.get(key);
446
+ if (old) { await context.unroute(old); entries.delete(key); }
447
+ if (!rule) return;
448
+ const matches = url => !!rule.decide({ url: url.href });
449
+ await context.route(matches, async route => {
450
+ const decision = rule.decide({ url: route.request().url() });
451
+ if (decision?.abort) return route.abort('blockedbyclient');
452
+ if (decision?.headers) return route.fallback({ headers: { ...await route.request().allHeaders(), ...decision.headers } });
453
+ return route.fallback();
454
+ });
455
+ entries.set(key, matches);
456
+ }
457
+
458
+ // A serve may start at any time. Inspect explicit ports only, then decide using
459
+ // the CURRENT live-port set. Default-port public HTTPS never enters Fetch.
460
+ export const SERVE_PORT_PATTERNS = ['*://*:*/*'];
461
+ // Include authority spellings (trailing dot / explicit port) the exact existing
462
+ // regex already matches. The decide predicate still determines every abort.
463
+ export const MONEY_PATTERNS = ['*://*.facebook.com*/*', '*://facebook.com*/*'];
464
+
465
+ /** Native URL patterns covering isLocalMarkerHost, with the exact check in decide. */
466
+ export function localMarkerPatterns(hostNames = []) {
467
+ const hosts = ['localhost*', '*.localhost*', '127.*', '10.*', '0.*', '192.168.*', '169.254.*', '[*]'];
468
+ for (let n = 16; n <= 31; n++) hosts.push(`172.${n}.*`);
469
+ for (let n = 64; n <= 127; n++) hosts.push(`100.${n}.*`);
470
+ for (const raw of hostNames) {
471
+ const name = String(raw).toLowerCase().replace(/\.+$/, '').replace(/\.local$/, '');
472
+ // DNS names only; never turn an untrusted machine name into a wildcard.
473
+ if (/^[a-z0-9_-]+(?:\.[a-z0-9_-]+)*$/.test(name)) hosts.push(`${name}*`, `${name}.local*`);
474
+ }
475
+ return [...new Set(hosts)].flatMap(host => [`*://${host}/*`, `*://${host}:*/*`]);
476
+ }
@@ -0,0 +1,64 @@
1
+ // ═══════════════════════════════════════════
2
+ // SynaBun — Browser policy: the projects where automated tests may run
3
+ // ═══════════════════════════════════════════
4
+ //
5
+ // The browser policy (lib/browser-tool-policy.js) lets a test runner (npm
6
+ // test, node --test, vitest, jest, npx playwright test) run browser tests from
7
+ // inside the session's own project, and from inside the `roots` its caller
8
+ // passes: the registered SynaBun projects (DATA_HOME/data/claude-code-projects.json,
9
+ // the "Registered projects" of the Assistant's routing sheet) that are code
10
+ // projects. A registered project is a code project when its folder
11
+ // - is an absolute path,
12
+ // - holds `.git` (a git work tree's root; a worktree's or a submodule's `.git`
13
+ // file counts), `package.json`, `deno.json` or `deno.jsonc` at its top,
14
+ // - is not the home folder or a folder above it, and
15
+ // - is not the Downloads folder or anything inside it (downloaded files are
16
+ // untrusted, whatever they hold).
17
+ // Read where a run starts (server.js for workers, assistant-runtime.js for the
18
+ // brain); the policy module itself reads nothing and gets the list.
19
+
20
+ import { statSync } from 'node:fs';
21
+ import { homedir } from 'node:os';
22
+ import { isAbsolute, relative, resolve } from 'node:path';
23
+
24
+ /** What at a folder's top makes it a code project. */
25
+ export const CODE_PROJECT_MARKERS = Object.freeze(['.git', 'package.json', 'deno.json', 'deno.jsonc']);
26
+
27
+ // macOS and Windows name a folder in any letter case: the exclusions compare that way.
28
+ const caseless = (path) => (process.platform === 'darwin' || process.platform === 'win32' ? path.toLowerCase() : path);
29
+ function inside(child, parent) {
30
+ const rel = relative(caseless(resolve(parent)), caseless(resolve(child)));
31
+ return rel === '' || (!rel.startsWith('..') && !isAbsolute(rel));
32
+ }
33
+
34
+ /** Why `path` is not a code project the browser policy lets tests run in, else null. `stat`: tests. */
35
+ export function codeProjectRefusal(path, { home = homedir(), stat = statSync } = {}) {
36
+ const text = typeof path === 'string' ? path.trim() : '';
37
+ if (!text || !isAbsolute(text)) return 'not an absolute path';
38
+ const root = resolve(text);
39
+ if (inside(home, root)) return 'the home folder or a folder above it';
40
+ if (inside(root, resolve(home, 'Downloads'))) return 'the Downloads folder (downloaded files)';
41
+ const marked = CODE_PROJECT_MARKERS.some((name) => {
42
+ try {
43
+ const entry = stat(resolve(root, name));
44
+ return name === '.git' ? entry.isDirectory() || entry.isFile() : entry.isFile();
45
+ } catch { return false; }
46
+ });
47
+ return marked ? null : 'no .git, package.json, deno.json or deno.jsonc at its top';
48
+ }
49
+
50
+ /**
51
+ * The registered projects (claude-code-projects.json rows `{ path }` or plain
52
+ * paths) that are code projects, resolved, each once: the browser policy's
53
+ * `roots`.
54
+ */
55
+ export function browserPolicyRoots(projects, options = {}) {
56
+ const out = [];
57
+ for (const row of Array.isArray(projects) ? projects : []) {
58
+ const path = typeof row === 'string' ? row : row?.path;
59
+ if (typeof path !== 'string' || codeProjectRefusal(path, options) !== null) continue;
60
+ const root = resolve(path.trim());
61
+ if (!out.includes(root)) out.push(root);
62
+ }
63
+ return out;
64
+ }