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,390 @@
1
+ // Shared, bounded git reads. A worktree owns its revision, flight and last good
2
+ // snapshot; callers arriving after an edit wait for a scan of that revision.
3
+ import { spawn, spawnSync } from 'node:child_process';
4
+ import { existsSync, realpathSync } from 'node:fs';
5
+ import { dirname, resolve, win32, posix } from 'node:path';
6
+ import { fileURLToPath } from 'node:url';
7
+ import { devNull, setPriority, constants as osConstants } from 'node:os';
8
+ import { gitReadArgs, gitReadEnv, withGitFallback, createMonitorMemo, readsIndex } from './git-read.js';
9
+ import { createTreeWatcher } from './file-explorer-watch.js';
10
+
11
+ // A watch request can contain 2,000 subfolders plus the root. Leave room
12
+ // for all of them so sequential badge reads cannot churn the bounded cache.
13
+ export const MAX_IGNORE_FOLDERS = 2048;
14
+ export const STATUS_ARGS = Object.freeze(['status', '--porcelain=v1', '-z', '--untracked-files=normal']);
15
+ const REDIRECTING = ['GIT_DIR', 'GIT_WORK_TREE', 'GIT_INDEX_FILE', 'GIT_NAMESPACE', 'GIT_COMMON_DIR', 'GIT_OBJECT_DIRECTORY', 'GIT_ALTERNATE_OBJECT_DIRECTORIES', 'GIT_CEILING_DIRECTORIES'];
16
+ const slash = p => p.replace(/\\/g, '/');
17
+ const failure = (code, extra = {}) => ({ ok: false, code, stdout: Buffer.alloc(0), stderr: Buffer.alloc(0), ...extra });
18
+ const live = child => !!child?.pid && child.exitCode == null && child.signalCode == null;
19
+
20
+ export function canonicalWorktree(cwd) {
21
+ let folder;
22
+ try { folder = realpathSync(cwd); } catch { folder = resolve(cwd); }
23
+ for (let at = folder; ; at = dirname(at)) {
24
+ if (existsSync(resolve(at, '.git'))) return at;
25
+ if (dirname(at) === at) return folder;
26
+ }
27
+ }
28
+ export function worktreeRelative(root, cwd, platform = process.platform) {
29
+ return slash((platform === 'win32' ? win32 : posix).relative(root, cwd));
30
+ }
31
+ export function watchRelative(root, path, platform = process.platform) {
32
+ const paths = platform === 'win32' ? win32 : posix;
33
+ const rel = paths.isAbsolute(path || '') ? worktreeRelative(root, path, platform) : slash(path || '');
34
+ return platform === 'win32' ? rel.toLowerCase() : rel;
35
+ }
36
+ // Membership is O(path depth), never a copied/scanned list on each filesystem event.
37
+ export function createIgnoredPrefixes(platform = process.platform, limit = 4096) {
38
+ const paths = new Set();
39
+ const normalize = p => platform === 'win32' ? slash(p).toLowerCase() : slash(p);
40
+ return {
41
+ add(path) {
42
+ path = normalize(path);
43
+ if (!paths.has(path) && paths.size >= limit) paths.delete(paths.values().next().value);
44
+ paths.add(path);
45
+ },
46
+ has(path) {
47
+ path = normalize(path).replace(/\/$/, '');
48
+ if (paths.has(path) || paths.has(path + '/')) return true;
49
+ for (let i = path.lastIndexOf('/'); i >= 0; i = path.lastIndexOf('/', i - 1)) if (paths.has(path.slice(0, i + 1))) return true;
50
+ return false;
51
+ },
52
+ clear: () => paths.clear(),
53
+ get size() { return paths.size; },
54
+ };
55
+ }
56
+
57
+ export function parseStatus(buffer) {
58
+ const fields = String(buffer).split('\0');
59
+ const changes = [];
60
+ for (let i = 0; i < fields.length; i++) {
61
+ const entry = fields[i];
62
+ if (entry.length < 4) continue;
63
+ const [x, y] = entry;
64
+ if ('RC'.includes(x) || 'RC'.includes(y)) i++;
65
+ const xy = x + y;
66
+ if (xy === '!!') continue;
67
+ const status = xy === '??' ? 'untracked' : x === 'U' || y === 'U' || xy === 'DD' || xy === 'AA' ? 'conflict'
68
+ : x === 'A' ? 'added' : x === 'D' || y === 'D' ? 'deleted' : x === 'R' ? 'renamed'
69
+ : x !== ' ' && y !== ' ' ? 'mixed' : x !== ' ' ? 'staged' : 'modified';
70
+ changes.push({ path: entry.slice(3), status, staged: x !== ' ' && x !== '?' });
71
+ }
72
+ return changes;
73
+ }
74
+
75
+ export function createGitStatusRunner({
76
+ spawnImpl = spawn, spawnSyncImpl = spawnSync, kill = process.kill.bind(process), platform = process.platform,
77
+ timeoutMs = 15000, debounceMs = 150, minIntervalMs = 2000, cacheMs = 30000, maxConcurrent = 2, maxRepos = 64,
78
+ backoffMs = 30000, maxBackoffMs = 300000, uncoveredCacheMs = 2000, killGraceMs = 1500, taskkillMs = 1000,
79
+ watch = true, watchFactory = createTreeWatcher, watchdog = spawnImpl === spawn, now = Date.now,
80
+ } = {}) {
81
+ const binaries = new Map();
82
+ const entries = new Map(), monitors = createMonitorMemo(), queue = [], children = new Set();
83
+ let active = 0, closed = false, guard = null, guardReady = null;
84
+ const nullDevice = platform === 'win32' ? String.raw`\\.\nul` : devNull;
85
+ const keyOf = root => platform === 'win32' ? root.toLowerCase() : root;
86
+ const directKill = child => { if (children.has(child) && live(child)) try { child.kill('SIGKILL'); } catch { /* already gone */ } };
87
+
88
+ function killTree(child, sync = false) {
89
+ if (!children.has(child) || !live(child)) return Promise.resolve();
90
+ if (platform !== 'win32') {
91
+ try { kill(-child.pid, 'SIGKILL'); } catch { directKill(child); }
92
+ return Promise.resolve();
93
+ }
94
+ const args = ['/pid', String(child.pid), '/T', '/F'];
95
+ if (sync) {
96
+ // Only process 'exit' uses this last resort: Node cannot drain async work there.
97
+ try { if (spawnSyncImpl('taskkill', args, { windowsHide: true, stdio: 'ignore', timeout: taskkillMs }).status !== 0) directKill(child); }
98
+ catch { directKill(child); }
99
+ return Promise.resolve();
100
+ }
101
+ return new Promise(done => {
102
+ let task, timer, settled = false;
103
+ const finish = code => {
104
+ if (settled) return;
105
+ settled = true; clearTimeout(timer);
106
+ if (code !== 0) directKill(child);
107
+ done();
108
+ };
109
+ try {
110
+ task = spawnImpl('taskkill', args, { windowsHide: true, stdio: 'ignore', shell: false });
111
+ task.once('error', () => finish(-1));
112
+ task.once('exit', finish); task.once('close', finish);
113
+ timer = setTimeout(() => { try { if (live(task)) task.kill('SIGKILL'); } catch { /* gone */ } finish(-1); }, taskkillMs);
114
+ } catch { finish(-1); }
115
+ });
116
+ }
117
+ function sendGuard(message) {
118
+ if (guard?.connected) try { guard.send(message, () => {}); } catch { /* guard failure aborts owned jobs */ }
119
+ }
120
+ function startGuard() {
121
+ if (!watchdog) return Promise.resolve();
122
+ if (guardReady) return guardReady;
123
+ guardReady = new Promise((done, reject) => {
124
+ const timer = setTimeout(() => reject(new Error('git watchdog did not start')), 3000);
125
+ guard = spawn(process.execPath, [fileURLToPath(new URL('./git-status-watchdog.js', import.meta.url))], { detached: true, stdio: ['ignore', 'ignore', 'ignore', 'ipc'], windowsHide: true });
126
+ const lost = error => { clearTimeout(timer); reject(error || new Error('git watchdog exited')); for (const owned of children) void killTree(owned); };
127
+ guard.on('error', lost);
128
+ guard.once('exit', () => { if (!closed) lost(); });
129
+ // Do not launch a child until the guard has installed its IPC/disconnect handlers.
130
+ guard.once('message', message => {
131
+ if (message !== 'ready') return;
132
+ clearTimeout(timer); guard.unref(); guard.channel?.unref(); done();
133
+ });
134
+ });
135
+ return guardReady;
136
+ }
137
+ function forget(child) {
138
+ if (!children.delete(child)) return;
139
+ sendGuard({ remove: child.pid });
140
+ }
141
+ function pump() {
142
+ while (!closed && active < maxConcurrent && queue.length) {
143
+ const job = queue.shift();
144
+ if (job.signal?.aborted) { job.done(failure('aborted')); continue; }
145
+ active++;
146
+ job.start().catch(() => failure('failed')).then(value => { active--; job.done(value); pump(); });
147
+ }
148
+ }
149
+ function execute(binary, args, { cwd, env, maxBuffer = 64 * 1024 * 1024, timeoutMs: limit = timeoutMs, signal, input, resolvedBinary = false } = {}) {
150
+ if (closed || signal?.aborted) return Promise.resolve(failure('aborted'));
151
+ if (platform === 'win32' && binary === 'git' && !resolvedBinary) {
152
+ const key = env?.PATH || env?.Path || process.env.PATH;
153
+ if (!binaries.has(key)) {
154
+ if (binaries.size >= 4) binaries.delete(binaries.keys().next().value);
155
+ binaries.set(key, execute('where.exe', ['git'], { env, timeoutMs: 1000 }).then(result => {
156
+ const paths = result.ok ? String(result.stdout).split(/\r?\n/).map(p => p.trim()).filter(Boolean) : [];
157
+ return paths.find(p => /\.exe$/i.test(p)) || paths[0] || 'git';
158
+ }));
159
+ }
160
+ return binaries.get(key).then(file => execute(file, args, { cwd, env, maxBuffer, timeoutMs: limit, signal, input, resolvedBinary: true }));
161
+ }
162
+ return new Promise(done => {
163
+ const job = { done, signal, start: async () => {
164
+ await startGuard();
165
+ if (closed || signal?.aborted) return failure('aborted');
166
+ return new Promise(finish => {
167
+ let child, timer, fallback, reason = null, size = 0, settled = false;
168
+ const out = [], err = [];
169
+ const settle = code => {
170
+ if (settled) return;
171
+ settled = true; clearTimeout(timer); clearTimeout(fallback);
172
+ signal?.removeEventListener('abort', abort);
173
+ if (!live(child)) forget(child);
174
+ child?.stdin?.destroy(); child?.stdout?.destroy(); child?.stderr?.destroy();
175
+ child?.unref?.();
176
+ finish({ ok: !reason && code === 0, code: reason || code, status: code, stdout: Buffer.concat(out), stderr: Buffer.concat(err), timedOut: reason === 'timed-out', tooLarge: reason === 'too-large' });
177
+ };
178
+ const stop = code => {
179
+ if (reason || settled) return;
180
+ reason = code;
181
+ // A failed kill or a descendant holding stdio cannot strand a queue slot.
182
+ fallback = setTimeout(() => settle(null), killGraceMs);
183
+ void killTree(child).then(() => { if (reason && !settled) settle(null); });
184
+ };
185
+ const abort = () => stop('aborted');
186
+ try {
187
+ // .cmd/.bat shims require cmd.exe on Windows. All ordinary git reads
188
+ // still use an argument array without a shell; preserve a resolved shim.
189
+ const shell = platform === 'win32' && /\.(cmd|bat)$/i.test(binary);
190
+ child = spawnImpl(binary, args, { cwd, env, detached: platform !== 'win32', windowsHide: true, shell, stdio: ['pipe', 'pipe', 'pipe'] });
191
+ children.add(child);
192
+ try { if (spawnImpl === spawn && child.pid) setPriority(child.pid, osConstants.priority.PRIORITY_BELOW_NORMAL); } catch { /* best effort */ }
193
+ const hardLimit = Math.max(1, Math.min(limit || timeoutMs, timeoutMs));
194
+ sendGuard({ add: child.pid, deadline: now() + hardLimit });
195
+ timer = setTimeout(() => stop('timed-out'), hardLimit);
196
+ const collect = list => chunk => {
197
+ if (settled || reason) return;
198
+ const bytes = Buffer.from(chunk); size += bytes.length;
199
+ if (size > maxBuffer) stop('too-large'); else list.push(bytes);
200
+ };
201
+ child.stdout.on('data', collect(out)); child.stderr.on('data', collect(err));
202
+ child.stdin.on('error', () => {});
203
+ child.once('error', error => { reason ||= error.code || 'failed'; void killTree(child); settle(null); });
204
+ child.once('exit', code => {
205
+ // Drop ownership immediately: this PID can be reused before close.
206
+ forget(child); clearTimeout(timer);
207
+ // close normally drains the remaining output. An inherited open pipe
208
+ // must not keep a completed process occupying a slot indefinitely.
209
+ if (!settled) { clearTimeout(fallback); fallback = setTimeout(() => settle(code), 100); }
210
+ });
211
+ child.once('close', code => { forget(child); settle(code); });
212
+ child.stdin.end(input);
213
+ signal?.addEventListener('abort', abort, { once: true });
214
+ if (signal?.aborted) abort();
215
+ } catch (error) {
216
+ reason ||= error.code || 'failed';
217
+ if (child) void killTree(child);
218
+ settle(null);
219
+ }
220
+ });
221
+ } };
222
+ const cancelQueued = () => { const i = queue.indexOf(job); if (i >= 0) { queue.splice(i, 1); job.done(failure('aborted')); } };
223
+ signal?.addEventListener('abort', cancelQueued, { once: true });
224
+ job.done = value => { signal?.removeEventListener('abort', cancelQueued); done(value); };
225
+ queue.push(job); pump();
226
+ });
227
+ }
228
+ async function run(args, { cwd, binary = 'git', env = process.env, ...options } = {}) {
229
+ const clean = { ...env };
230
+ for (const name of REDIRECTING) delete clean[name];
231
+ const safeEnv = gitReadEnv(clean);
232
+ let monitor = false;
233
+ if (readsIndex(args)) {
234
+ monitor = monitors.get(cwd);
235
+ if (monitor === undefined) {
236
+ const probe = await execute(binary, ['--no-pager', '-c', `core.hooksPath=${nullDevice}`, 'config', '--type=bool', '--get', 'core.fsmonitor'], { ...options, timeoutMs: 3000, cwd, env: safeEnv });
237
+ monitor = monitors.set(cwd, probe.ok && String(probe.stdout).trim() === 'true');
238
+ }
239
+ }
240
+ return execute(binary, gitReadArgs(args, { monitor, nullDevice }), { ...options, cwd, env: safeEnv });
241
+ }
242
+ function drop(key, entry) {
243
+ entry.controller?.abort(); entry.watcher?.close(); entries.delete(key);
244
+ }
245
+ function entryOf(cwd) {
246
+ const root = canonicalWorktree(cwd), key = keyOf(root);
247
+ let entry = entries.get(key);
248
+ if (!entry) {
249
+ for (const [k, e] of entries) if (entries.size >= maxRepos && !e.flight) { drop(k, e); break; }
250
+ entry = { key, root, revision: 0, value: null, good: null, at: 0, nextStartAt: 0, retryAt: 0, failures: 0, flight: null, flightRevision: null, consumers: new Set(), ignored: new Map(), ignoreRevision: 0, ignoreAt: now(), ignoredPaths: createIgnoredPrefixes(platform) };
251
+ entries.set(key, entry);
252
+ }
253
+ if (watch && !entry.watcher && existsSync(resolve(root, '.git'))) {
254
+ entry.watcher = watchFactory({ root, onError: () => invalidate(root), ignoresGit: path => entry.ignoredPaths.has(watchRelative(root, path, platform)), onChange: ({ git, ignores }) => { if (git || ignores) invalidate(root, { ignores }); } });
255
+ entry.watcher.setDirs?.([root]);
256
+ }
257
+ return entry;
258
+ }
259
+ function invalidate(cwd, { ignores = false, refresh = false } = {}) {
260
+ const entry = entries.get(keyOf(canonicalWorktree(cwd)));
261
+ if (!entry) return;
262
+ entry.revision++;
263
+ if (refresh) { entry.failures = 0; entry.retryAt = 0; }
264
+ if (ignores) { entry.ignoreRevision++; entry.ignored.clear(); entry.ignoredPaths.clear(); entry.ignoreAt = now(); }
265
+ }
266
+ function store(entry, result, revision) {
267
+ const value = { ...result, repo: entry.root, revision };
268
+ if (result.code === 'aborted') return value;
269
+ entry.at = now();
270
+ if (result.ok) {
271
+ entry.good = value; entry.failures = 0; entry.retryAt = 0;
272
+ } else {
273
+ entry.retryAt = now() + Math.min(maxBackoffMs, backoffMs * 2 ** Math.min(entry.failures++, 20));
274
+ value.changes = entry.good?.changes || [];
275
+ value.branch ||= entry.good?.branch || null;
276
+ value.isGit ||= !!entry.good?.isGit;
277
+ value.partial = true; value.stale = true; value.retryAt = entry.retryAt;
278
+ }
279
+ entry.value = value;
280
+ return value;
281
+ }
282
+ function read(cwd, { signal, refresh = false } = {}) {
283
+ if (!cwd || closed || signal?.aborted) return Promise.resolve(failure('aborted'));
284
+ const entry = entryOf(cwd);
285
+ if (refresh) invalidate(cwd, { refresh: true });
286
+ if (entry.flight && entry.controller?.signal.aborted) return entry.flight.then(() => read(cwd, { signal }));
287
+ // Watcher churn cannot bypass a failing repository's backoff. Refresh can.
288
+ if (!entry.flight && entry.value && !entry.value.ok && now() < entry.retryAt) return Promise.resolve(entry.value);
289
+ const coverage = entry.watcher?.state?.();
290
+ const ttl = coverage && (coverage.scope !== 'tree' || coverage.live !== 'full') ? Math.min(cacheMs, uncoveredCacheMs) : cacheMs;
291
+ if (!entry.flight && entry.value?.ok && now() - entry.at < ttl && entry.value.revision === entry.revision) return Promise.resolve(entry.value);
292
+ if (!entry.flight) {
293
+ entry.controller = new AbortController();
294
+ const control = entry.controller;
295
+ let started = false;
296
+ entry.flight = (async () => {
297
+ const delay = Math.max(debounceMs, entry.nextStartAt - now());
298
+ await new Promise(done => { const abort = () => { clearTimeout(timer); done(); }; const timer = setTimeout(() => { control.signal.removeEventListener('abort', abort); done(); }, delay); control.signal.addEventListener('abort', abort, { once: true }); });
299
+ if (control.signal.aborted) return failure('aborted');
300
+ started = true;
301
+ const revision = entry.flightRevision = entry.revision;
302
+ const options = { cwd: entry.root, signal: control.signal };
303
+ const named = await run(['symbolic-ref', '--short', '-q', 'HEAD'], options);
304
+ let branch = named.ok ? String(named.stdout).trim() : '';
305
+ const inside = await run(['rev-parse', '--is-inside-work-tree'], options);
306
+ if (!inside.ok) {
307
+ const notGit = inside.status === 128 && /not a git repository/i.test(String(inside.stderr));
308
+ return store(entry, { ...inside, ...(notGit ? { ok: true } : {}), isGit: !notGit && (inside.timedOut || !!entry.good?.isGit), branch, changes: [] }, revision);
309
+ }
310
+ if (!branch) { const head = await run(['rev-parse', '--short', 'HEAD'], options); if (head.ok) branch = String(head.stdout).trim(); }
311
+ const status = await run(STATUS_ARGS, { ...options, timeoutMs: 10000 });
312
+ return store(entry, { ...status, stdout: Buffer.alloc(0), isGit: true, branch, changes: status.ok ? parseStatus(status.stdout) : [] }, revision);
313
+ })().finally(() => { entry.flight = null; entry.flightRevision = null; entry.controller = null; if (started) entry.nextStartAt = now() + minIntervalMs; });
314
+ }
315
+ const revision = entry.revision, token = {};
316
+ entry.consumers.add(token);
317
+ return new Promise(done => {
318
+ let finished = false;
319
+ const finish = value => {
320
+ if (finished) return;
321
+ finished = true; signal?.removeEventListener('abort', abort); entry.consumers.delete(token);
322
+ if (!signal?.aborted && value.ok && value.revision < revision) done(read(cwd, { signal }));
323
+ else done(value);
324
+ };
325
+ const abort = () => { finish(failure('aborted')); if (!entry.consumers.size) entry.controller?.abort(); };
326
+ signal?.addEventListener('abort', abort, { once: true });
327
+ entry.flight.then(finish, () => finish(failure('failed')));
328
+ if (signal?.aborted) abort();
329
+ });
330
+ }
331
+ // null means unknown, [] means checked and not ignored. Listings may warm the
332
+ // cache without waiting; searches wait so they never descend an unknown cache.
333
+ async function ignored(cwd, paths, { signal, cacheOnly = false } = {}) {
334
+ const entry = entryOf(cwd);
335
+ if (!existsSync(resolve(entry.root, '.git'))) return [];
336
+ if (now() - entry.ignoreAt >= cacheMs) invalidate(cwd, { ignores: true });
337
+ const input = paths.filter(p => p && !p.includes('\0'));
338
+ if (!input.length) return [];
339
+ const key = `${keyOf(cwd)}\0${input.join('\0')}`;
340
+ let known = entry.ignored.get(key);
341
+ if (!known || now() - known.at >= cacheMs) {
342
+ if (!known?.flight) {
343
+ const revision = entry.ignoreRevision;
344
+ known = { at: now(), paths: null };
345
+ const record = known;
346
+ known.flight = run(['check-ignore', '-z', '--stdin'], { cwd, signal: cacheOnly ? undefined : signal, input: input.join('\0') + '\0', maxBuffer: 4 * 1024 * 1024 }).then(result => {
347
+ record.paths = result.ok ? String(result.stdout).split('\0').filter(Boolean) : result.status === 1 && result.code === 1 ? [] : null;
348
+ record.at = now(); record.flight = null;
349
+ if (revision !== entry.ignoreRevision) return null;
350
+ const prefix = worktreeRelative(entry.root, cwd, platform);
351
+ for (const path of record.paths || []) entry.ignoredPaths.add((prefix ? prefix + '/' : '') + path);
352
+ return record.paths;
353
+ });
354
+ if (entry.ignored.size >= MAX_IGNORE_FOLDERS) entry.ignored.delete(entry.ignored.keys().next().value);
355
+ entry.ignored.set(key, known);
356
+ }
357
+ }
358
+ if (known.flight) return cacheOnly ? null : known.flight;
359
+ return known.paths;
360
+ }
361
+ function release(cwd) {
362
+ const entry = entries.get(keyOf(canonicalWorktree(cwd)));
363
+ if (!entry) return;
364
+ entry.watcher?.close(); entry.watcher = null;
365
+ if (!entry.consumers.size) entry.controller?.abort();
366
+ }
367
+ function shutdown({ sync = false } = {}) {
368
+ closed = true;
369
+ for (const entry of entries.values()) { entry.controller?.abort(); entry.watcher?.close(); }
370
+ for (const job of queue.splice(0)) job.done(failure('aborted'));
371
+ for (const child of children) void killTree(child, sync);
372
+ if (guard?.connected) guard.disconnect();
373
+ }
374
+ return { read, run, runRaw: (args, options = {}) => execute(options.binary || 'git', args, { ...options, env: withGitFallback(options.env) }), ignored, invalidate, release, shutdown, stats: () => ({ active, queued: queue.length, children: children.size, repos: entries.size }) };
375
+ }
376
+
377
+ export function projectStatus(result, cwd, platform = process.platform) {
378
+ let folder;
379
+ try { folder = realpathSync(cwd); } catch { folder = platform === 'win32' ? win32.resolve(cwd) : resolve(cwd); }
380
+ const below = result.repo ? worktreeRelative(result.repo, folder, platform) : '';
381
+ const prefix = below ? `${below}/` : '';
382
+ const compare = p => platform === 'win32' ? p.toLowerCase() : p;
383
+ return { ...result, changes: (result.changes || []).filter(c => !prefix || compare(c.path).startsWith(compare(prefix))).map(c => ({ ...c, path: prefix ? c.path.slice(prefix.length) : c.path })) };
384
+ }
385
+ export const gitStatusRunner = createGitStatusRunner();
386
+ process.on('exit', () => gitStatusRunner.shutdown({ sync: true }));
387
+ for (const signal of ['SIGINT', 'SIGTERM']) process.on(signal, () => {
388
+ gitStatusRunner.shutdown();
389
+ if (process.listenerCount(signal) === 1) process.exit(signal === 'SIGINT' ? 130 : 143);
390
+ });
@@ -0,0 +1,40 @@
1
+ // Started and acknowledged before the first git child. IPC disconnect survives a
2
+ // hard parent exit; removal on child exit prevents later deadlines using its PID.
3
+ import { spawn } from 'node:child_process';
4
+ const owned = new Map();
5
+ let disconnected = false;
6
+ async function end(pid, record) {
7
+ if (owned.get(pid) !== record || record.killing) return;
8
+ record.killing = true;
9
+ if (process.platform === 'win32') {
10
+ await new Promise(done => {
11
+ let child, timer, finished = false;
12
+ const finish = code => {
13
+ if (finished) return;
14
+ finished = true; clearTimeout(timer);
15
+ if (code !== 0 && owned.get(pid) === record) try { process.kill(pid, 'SIGKILL'); } catch { /* gone */ }
16
+ done();
17
+ };
18
+ try {
19
+ child = spawn('taskkill', ['/pid', String(pid), '/T', '/F'], { windowsHide: true, stdio: 'ignore' });
20
+ child.once('error', () => finish(-1)); child.once('exit', finish); child.once('close', finish);
21
+ timer = setTimeout(() => { try { child.kill('SIGKILL'); } catch { /* gone */ } finish(-1); }, 1000);
22
+ } catch { finish(-1); }
23
+ });
24
+ } else {
25
+ try { process.kill(-pid, 'SIGKILL'); } catch { /* gone */ }
26
+ }
27
+ if (owned.get(pid) === record) owned.delete(pid);
28
+ if (disconnected && !owned.size) process.exit(0);
29
+ }
30
+ process.on('message', message => {
31
+ if (Number.isInteger(message?.add) && message.add > 0) owned.set(message.add, { deadline: message.deadline });
32
+ if (Number.isInteger(message?.remove)) owned.delete(message.remove);
33
+ });
34
+ process.on('disconnect', () => {
35
+ disconnected = true;
36
+ for (const [pid, record] of owned) void end(pid, record);
37
+ if (!owned.size) process.exit(0);
38
+ });
39
+ setInterval(() => { for (const [pid, record] of owned) if (Date.now() >= record.deadline) void end(pid, record); }, 300);
40
+ process.send?.('ready');
@@ -26,6 +26,7 @@ export const SURFACES = Object.freeze({
26
26
  'claude-panel': 'Claude Code (side panel)',
27
27
  'codex-panel': 'Codex (side panel)',
28
28
  'opencode-panel': 'OpenCode (side panel)',
29
+ 'antigravity-panel': 'Antigravity (side panel)',
29
30
  'claude-cli': 'Claude Code (terminal)',
30
31
  });
31
32
 
@@ -3,7 +3,7 @@
3
3
  // ═══════════════════════════════════════════
4
4
  //
5
5
  // One greeting for every agent. When a person comes back and opens a fresh
6
- // session (the Assistant, the Claude Code / Codex / OpenCode panels, a
6
+ // session (the Assistant, the Claude Code / Codex / OpenCode / Antigravity panels, a
7
7
  // terminal Claude Code session), SynaBun says in one or two sentences what
8
8
  // they were doing last here, what is open, and what finished or failed while
9
9
  // they were away. The harness writes it; the session's own model never greets,
@@ -34,7 +34,7 @@ import { agentNote, awayText, basisOf, buildFacts, clipLabel, decide, fallbackLi
34
34
 
35
35
  export const GREETING_EVENT = 'greeting:changed';
36
36
  const STATE_VERSION = 1;
37
- const SURFACE_AGENT = Object.freeze({ assistant: 'assistant', 'claude-panel': 'claude-code', 'claude-cli': 'claude-code', 'codex-panel': 'codex', 'opencode-panel': 'opencode' });
37
+ const SURFACE_AGENT = Object.freeze({ assistant: 'assistant', 'claude-panel': 'claude-code', 'claude-cli': 'claude-code', 'codex-panel': 'codex', 'opencode-panel': 'opencode', 'antigravity-panel': 'antigravity' });
38
38
 
39
39
  const iso = (at) => new Date(at).toISOString();
40
40
  const ms = (value) => { const at = typeof value === 'number' ? value : Date.parse(value ?? ''); return Number.isFinite(at) ? at : null; };
@@ -11,11 +11,10 @@
11
11
  // `git status`. Everything is injected, so nothing here opens the memory
12
12
  // database itself, and every reader answers an empty list on failure.
13
13
 
14
- import { execFile } from 'node:child_process';
15
14
  import { CATEGORIES } from '../assistant-user-context-budgets.js';
16
15
  import { openItems, parseThread } from '../assistant-user-threads.js';
17
16
  import { readGitBranch } from '../assistant-user-context.js';
18
- import { gitRead } from '../git-read.js';
17
+ import { gitStatusRunner } from '../git-status-runner.js';
19
18
  import { FACTS } from './budgets.js';
20
19
  import { PROBLEMS, runStatus } from './facts.js';
21
20
 
@@ -46,16 +45,15 @@ export function parseSessionSummary(content) {
46
45
  * `git status --porcelain` → the number of changed and untracked paths, or null (not a repository, too slow).
47
46
  * Run as lib/git-read.js runs a read: nothing the project's own git configuration names is started for it.
48
47
  */
49
- export function gitDirtyCount(cwd, { timeoutMs = 1500, run = execFile } = {}) {
50
- return new Promise((done) => {
51
- if (!cwd) return done(null);
52
- gitRead(cwd, ['status', '--porcelain']).then((read) => {
53
- run('git', read.args, { cwd, env: read.env, timeout: timeoutMs, maxBuffer: 4 * 1024 * 1024, windowsHide: true }, (error, stdout) => {
54
- if (error) return done(null);
55
- done(String(stdout || '').split('\n').filter((line) => line.trim()).length);
56
- });
57
- }).catch(() => done(null));
58
- });
48
+ export async function gitDirtyCount(cwd, { timeoutMs = 1500, runner = gitStatusRunner } = {}) {
49
+ if (!cwd) return null;
50
+ const controller = new AbortController();
51
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
52
+ try {
53
+ const result = await runner.read(cwd, { signal: controller.signal });
54
+ return result.ok && result.isGit !== false && !result.partial && !result.stale ? result.changes.length : null;
55
+ } catch { return null; }
56
+ finally { clearTimeout(timer); }
59
57
  }
60
58
 
61
59
  /**
@@ -34,7 +34,7 @@ const day = (at) => {
34
34
  if (at == null) return '';
35
35
  try { return new Date(at).toLocaleDateString('en-US', { weekday: 'long', month: 'short', day: 'numeric', timeZone: 'UTC' }); } catch { return ''; }
36
36
  };
37
- const AGENTS = Object.freeze({ assistant: 'the SynaBun Assistant', 'claude-code': 'Claude Code', codex: 'Codex', opencode: 'OpenCode' });
37
+ const AGENTS = Object.freeze({ assistant: 'the SynaBun Assistant', 'claude-code': 'Claude Code', codex: 'Codex', opencode: 'OpenCode', antigravity: 'Antigravity' });
38
38
 
39
39
  /** The records block: every fact on one line, each a datum (never an instruction). */
40
40
  export function greetingRecords(facts) {
@@ -407,7 +407,10 @@ export function isOwnerOrigin(origin, { port, machineName = thisMachine.names, o
407
407
  * proxy's (`publicOrigins`, left out of the listed names), never a listed name
408
408
  * on another port, whatever Sec-Fetch-Site says or leaves out: BAD_ORIGIN.
409
409
  * `localAddresses` gives this computer's addresses (thisMachineAddresses when
410
- * it is left out). A request without Origin still passes.
410
+ * it is left out). A request without Origin still passes, unless `requireOrigin`
411
+ * is set: then a write must carry one (every browser sends it on a write; a
412
+ * local process that fetched a page for its cookie and sends none is refused,
413
+ * ORIGIN_REQUIRED). First users: the Skills Studio's owner-only writes.
411
414
  *
412
415
  * `allowedOrigins` (an array or a function) are the tunnel's and the invite
413
416
  * proxy's public URLs: their host names count as ours, as for a WebSocket.
@@ -418,7 +421,7 @@ export function isOwnerOrigin(origin, { port, machineName = thisMachine.names, o
418
421
  */
419
422
  export function ownerRouteGuard({
420
423
  isGuestRequest = null, port, allowedOrigins = [], machineName, refuseCrossSite = false,
421
- ownOriginsOnly = false, publicOrigins = [], localAddresses = thisMachineAddresses,
424
+ ownOriginsOnly = false, publicOrigins = [], localAddresses = thisMachineAddresses, requireOrigin = false,
422
425
  } = {}) {
423
426
  // The names the owner listed for this computer: the listed ones, without the tunnel's and the invite proxy's.
424
427
  const ownNames = () => { const theirs = listedNames(publicOrigins); return listedNames(allowedOrigins).filter((name) => !theirs.includes(name)); };
@@ -426,6 +429,9 @@ export function ownerRouteGuard({
426
429
  || isOwnerOrigin(req.headers.origin, { port, machineName, ownNames, addresses: localAddresses });
427
430
  function refusal(req) {
428
431
  if (!isAllowedHost(req?.headers?.host, { allowedHosts: allowedOrigins, machineName })) return { status: 403, code: 'BAD_HOST', error: 'Unexpected Host header.' };
432
+ if (requireOrigin && !SAFE_METHODS.has(req.method) && typeof req?.headers?.origin !== 'string') {
433
+ return { status: 403, code: 'ORIGIN_REQUIRED', error: 'This change is made from SynaBun\'s own page, which sends its Origin.' };
434
+ }
429
435
  if (!SAFE_METHODS.has(req.method) && !(ownOriginsOnly ? ownerOrigin(req) : isAllowedWebSocketOrigin(req, { port, allowedOrigins, machineName }))) {
430
436
  return { status: 403, code: 'BAD_ORIGIN', error: 'Unexpected Origin header.' };
431
437
  }
@@ -14,6 +14,7 @@ import { execFile as nodeExecFile } from 'node:child_process';
14
14
  import { existsSync, readFileSync } from 'node:fs';
15
15
  import path from 'node:path';
16
16
  import { redactDeep, redactSecrets } from '../../lib/log-redact.js';
17
+ import { mcpDistBuilt } from '../../lib/mcp-dist.js';
17
18
 
18
19
  const COMMAND_TIMEOUT_MS = 8000;
19
20
  const OUTPUT_MAX = 2000;
@@ -111,6 +112,7 @@ export async function collectInstallDetails({
111
112
  browserExecutable = null,
112
113
  exists = existsSync,
113
114
  readJson = readJsonFile,
115
+ readText = null,
114
116
  version = runVersion,
115
117
  } = {}) {
116
118
  const windows = platform === 'win32';
@@ -135,7 +137,8 @@ export async function collectInstallDetails({
135
137
  const packages = installed ? packagesInLock(installed) : installedFromLock(shrinkwrap, packageRoot, { exists });
136
138
  return { name, folder, packages, dependencies: directDependencies(dir, { readJson, stopAt: packageRoot }) };
137
139
  });
138
- const mcpBuilt = exists(path.join(packageRoot, 'mcp-server', 'dist', 'index.js'));
140
+ // Built: dist/index.js and every module it imports (lib/mcp-dist.js), not the entry file alone.
141
+ const mcpBuilt = exists(path.join(packageRoot, 'mcp-server', 'dist', 'index.js')) && mcpDistBuilt(packageRoot, { exists, ...(readText ? { readText } : {}) });
139
142
 
140
143
  return redactDeep({
141
144
  kind: packaged ? 'app' : 'source',
@@ -0,0 +1,67 @@
1
+ // Registry conversion is read-only. An explicit switch is authoritative; missing switches are on.
2
+ function asObject(value) {
3
+ return value && typeof value === 'object' && !Array.isArray(value) ? value : {};
4
+ }
5
+
6
+ export function agentAccessFrom(registry) {
7
+ const reg = asObject(registry);
8
+ if (reg.agentAccess && typeof reg.agentAccess === 'object' && !Array.isArray(reg.agentAccess)) return { ...reg.agentAccess };
9
+ const profile = asObject(asObject(reg.profiles)[reg.activeProfile]);
10
+ if (!Array.isArray(profile.servers)) return {};
11
+ const granted = new Set(profile.servers);
12
+ // The old reconciliation marker distinguishes a de-selection from a genuinely new server.
13
+ const known = new Set(Array.isArray(reg.profileDefaults?.knownServers)
14
+ ? reg.profileDefaults.knownServers : Object.keys(asObject(reg.servers)));
15
+ return Object.fromEntries(Object.entries(asObject(reg.servers))
16
+ .filter(([name, entry]) => !entry?.builtin && known.has(name) && !granted.has(name))
17
+ .map(([name]) => [name, false]));
18
+ }
19
+
20
+ export function agentServerNames(registry) {
21
+ const reg = asObject(registry);
22
+ const access = agentAccessFrom(reg);
23
+ return Object.entries(asObject(reg.servers))
24
+ .filter(([name, entry]) => !entry?.builtin && access[name] !== false)
25
+ .map(([name]) => name);
26
+ }
27
+
28
+ /**
29
+ * Tools that mutate ad state or spend budget, keyed by MCP server name.
30
+ *
31
+ * FleetView agents run with `--permission-mode bypassPermissions`, so the read-only
32
+ * allowlist in .claude/settings.json does not apply to them. These are pushed as
33
+ * `--disallowedTools` instead: agents keep full read/reporting access, while anything
34
+ * that creates, edits, activates, or deletes stays with a human.
35
+ */
36
+ export const MCP_SERVER_WRITE_TOOLS = {
37
+ 'meta-ads': [
38
+ 'ads_create_campaign', 'ads_create_ad_set', 'ads_create_ad', 'ads_create_creative',
39
+ 'ads_update_entity', 'ads_activate_entity', 'ads_boost_ig_post',
40
+ 'ads_create_custom_audience', 'ads_update_custom_audience',
41
+ 'ads_update_custom_audience_users', 'ads_delete_custom_audience',
42
+ 'ads_catalog_create', 'ads_catalog_update_catalog',
43
+ 'ads_catalog_product_create', 'ads_catalog_update_product', 'ads_catalog_delete_product',
44
+ 'ads_catalog_create_product_set', 'ads_catalog_update_product_set', 'ads_catalog_product_set_delete',
45
+ 'ads_catalog_create_product_feed', 'ads_catalog_update_product_feed',
46
+ 'ads_catalog_create_product_feed_upload_session', 'ads_catalog_product_feed_delete',
47
+ 'ads_catalog_create_feed_rule', 'ads_catalog_product_feed_delete_rule',
48
+ 'ads_catalog_event_source_connect', 'ads_catalog_event_source_disconnect',
49
+ 'ads_pixel_event_create', 'ads_pixel_event_update', 'ads_pixel_event_delete',
50
+ 'ads_pixel_parameter_create', 'ads_pixel_parameter_update', 'ads_pixel_parameter_delete',
51
+ 'ads_experiment_abtest_create_test', 'ads_experiment_abtest_update_test',
52
+ 'ads_experiment_lift_create_test',
53
+ ],
54
+ };
55
+
56
+ /** Fully-qualified deny list for whichever servers an agent's --mcp-config actually grants. */
57
+ export function buildAgentDisallowedTools(mcpConfigJson) {
58
+ let serverNames = [];
59
+ try {
60
+ serverNames = Object.keys(asObject(JSON.parse(mcpConfigJson || '{}')).mcpServers || {});
61
+ } catch { return []; }
62
+ const deny = [];
63
+ for (const server of serverNames) {
64
+ for (const tool of MCP_SERVER_WRITE_TOOLS[server] || []) deny.push(`mcp__${server}__${tool}`);
65
+ }
66
+ return deny;
67
+ }