synabun 2.0.10 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (517) hide show
  1. package/CHANGELOG.md +86 -0
  2. package/README.md +25 -2
  3. package/THIRD-PARTY-LICENSES.md +22 -0
  4. package/bin/synabun.js +71 -24
  5. package/docs/whatsapp.md +2 -2
  6. package/hooks/claude-code/pre-websearch.mjs +12 -1
  7. package/hooks/claude-code/shared.mjs +5 -0
  8. package/hooks/claude-code/stop.mjs +1 -1
  9. package/lib/claude-hooks.js +172 -18
  10. package/lib/client-config-repair.js +17 -8
  11. package/lib/desktop-app.js +7 -1
  12. package/lib/external-tools.js +1 -1
  13. package/lib/first-launch-protection.js +57 -12
  14. package/lib/mcp-dist.js +176 -0
  15. package/lib/release-npm-ci.mjs +138 -0
  16. package/lib/release-publish.mjs +91 -0
  17. package/lib/release-scan.mjs +50 -0
  18. package/lib/server-crash-record.js +123 -0
  19. package/lib/server-exit.js +318 -0
  20. package/lib/server-supervisor.js +508 -0
  21. package/lib/start-launcher.js +37 -4
  22. package/lib/tools-cli.js +45 -0
  23. package/lib/update-snapshot.js +228 -0
  24. package/lib/virustotal.mjs +192 -0
  25. package/lib/windows-crash-event.js +269 -0
  26. package/mcp-server/dist/http.d.ts +20 -1
  27. package/mcp-server/dist/http.d.ts.map +1 -1
  28. package/mcp-server/dist/http.js +59 -12
  29. package/mcp-server/dist/http.js.map +1 -1
  30. package/mcp-server/dist/index.d.ts +33 -27
  31. package/mcp-server/dist/index.d.ts.map +1 -1
  32. package/mcp-server/dist/index.js +128 -143
  33. package/mcp-server/dist/index.js.map +1 -1
  34. package/mcp-server/dist/preload.js +1 -1
  35. package/mcp-server/dist/preload.js.map +1 -1
  36. package/mcp-server/dist/services/codex-config-heal.d.ts +6 -2
  37. package/mcp-server/dist/services/codex-config-heal.d.ts.map +1 -1
  38. package/mcp-server/dist/services/codex-config-heal.js +81 -28
  39. package/mcp-server/dist/services/codex-config-heal.js.map +1 -1
  40. package/mcp-server/dist/services/memory-judge-backfill.d.ts +6 -0
  41. package/mcp-server/dist/services/memory-judge-backfill.d.ts.map +1 -1
  42. package/mcp-server/dist/services/memory-judge-backfill.js +7 -1
  43. package/mcp-server/dist/services/memory-judge-backfill.js.map +1 -1
  44. package/mcp-server/dist/services/neural-interface.d.ts +16 -10
  45. package/mcp-server/dist/services/neural-interface.d.ts.map +1 -1
  46. package/mcp-server/dist/services/neural-interface.js +67 -11
  47. package/mcp-server/dist/services/neural-interface.js.map +1 -1
  48. package/mcp-server/dist/services/server-instructions.d.ts +7 -2
  49. package/mcp-server/dist/services/server-instructions.d.ts.map +1 -1
  50. package/mcp-server/dist/services/server-instructions.js +20 -62
  51. package/mcp-server/dist/services/server-instructions.js.map +1 -1
  52. package/mcp-server/dist/services/tool-catalog.d.ts +120 -0
  53. package/mcp-server/dist/services/tool-catalog.d.ts.map +1 -0
  54. package/mcp-server/dist/services/tool-catalog.js +339 -0
  55. package/mcp-server/dist/services/tool-catalog.js.map +1 -0
  56. package/mcp-server/dist/services/tool-list-refresh-barrier.d.ts +12 -0
  57. package/mcp-server/dist/services/tool-list-refresh-barrier.d.ts.map +1 -0
  58. package/mcp-server/dist/services/tool-list-refresh-barrier.js +40 -0
  59. package/mcp-server/dist/services/tool-list-refresh-barrier.js.map +1 -0
  60. package/mcp-server/dist/services/tool-list.d.ts +108 -0
  61. package/mcp-server/dist/services/tool-list.d.ts.map +1 -0
  62. package/mcp-server/dist/services/tool-list.js +334 -0
  63. package/mcp-server/dist/services/tool-list.js.map +1 -0
  64. package/mcp-server/dist/services/tool-runtime.d.ts +161 -0
  65. package/mcp-server/dist/services/tool-runtime.d.ts.map +1 -0
  66. package/mcp-server/dist/services/tool-runtime.js +400 -0
  67. package/mcp-server/dist/services/tool-runtime.js.map +1 -0
  68. package/mcp-server/dist/services/whiteboard-geometry.d.ts +26 -2
  69. package/mcp-server/dist/services/whiteboard-geometry.d.ts.map +1 -1
  70. package/mcp-server/dist/services/whiteboard-geometry.js +91 -8
  71. package/mcp-server/dist/services/whiteboard-geometry.js.map +1 -1
  72. package/mcp-server/dist/tools/agents.d.ts +10 -10
  73. package/mcp-server/dist/tools/agents.d.ts.map +1 -1
  74. package/mcp-server/dist/tools/agents.js +22 -13
  75. package/mcp-server/dist/tools/agents.js.map +1 -1
  76. package/mcp-server/dist/tools/browser-batch.d.ts +3 -3
  77. package/mcp-server/dist/tools/image-tools.d.ts +1 -1
  78. package/mcp-server/dist/tools/image-tools.js +1 -1
  79. package/mcp-server/dist/tools/skill-tools.d.ts +505 -0
  80. package/mcp-server/dist/tools/skill-tools.d.ts.map +1 -0
  81. package/mcp-server/dist/tools/skill-tools.js +639 -0
  82. package/mcp-server/dist/tools/skill-tools.js.map +1 -0
  83. package/mcp-server/dist/tools/skill.d.ts +7 -0
  84. package/mcp-server/dist/tools/skill.d.ts.map +1 -0
  85. package/mcp-server/dist/tools/skill.js +11 -0
  86. package/mcp-server/dist/tools/skill.js.map +1 -0
  87. package/mcp-server/dist/tools/tool-groups.d.ts +68 -0
  88. package/mcp-server/dist/tools/tool-groups.d.ts.map +1 -0
  89. package/mcp-server/dist/tools/tool-groups.js +147 -0
  90. package/mcp-server/dist/tools/tool-groups.js.map +1 -0
  91. package/mcp-server/dist/tools/whiteboard-format.d.ts.map +1 -1
  92. package/mcp-server/dist/tools/whiteboard-format.js +7 -4
  93. package/mcp-server/dist/tools/whiteboard-format.js.map +1 -1
  94. package/mcp-server/dist/tools/whiteboard-tools.d.ts +64 -13
  95. package/mcp-server/dist/tools/whiteboard-tools.d.ts.map +1 -1
  96. package/mcp-server/dist/tools/whiteboard-tools.js +30 -5
  97. package/mcp-server/dist/tools/whiteboard-tools.js.map +1 -1
  98. package/mcp-server/package-lock.json +2 -2
  99. package/mcp-server/package.json +1 -1
  100. package/mcp-server/src/http.ts +75 -12
  101. package/mcp-server/src/index.ts +147 -154
  102. package/mcp-server/src/preload.ts +12 -12
  103. package/mcp-server/src/services/codex-config-heal.ts +80 -29
  104. package/mcp-server/src/services/memory-judge-backfill.ts +7 -1
  105. package/mcp-server/src/services/neural-interface.ts +79 -20
  106. package/mcp-server/src/services/server-instructions.ts +27 -63
  107. package/mcp-server/src/services/tool-catalog.ts +403 -0
  108. package/mcp-server/src/services/tool-list-refresh-barrier.ts +33 -0
  109. package/mcp-server/src/services/tool-list.ts +410 -0
  110. package/mcp-server/src/services/tool-runtime.ts +457 -0
  111. package/mcp-server/src/services/whiteboard-geometry.ts +94 -9
  112. package/mcp-server/src/tools/agents.ts +22 -14
  113. package/mcp-server/src/tools/image-tools.ts +1 -1
  114. package/mcp-server/src/tools/skill-tools.ts +589 -0
  115. package/mcp-server/src/tools/skill.ts +13 -0
  116. package/mcp-server/src/tools/tool-groups.ts +164 -0
  117. package/mcp-server/src/tools/whiteboard-format.ts +9 -3
  118. package/mcp-server/src/tools/whiteboard-tools.ts +39 -5
  119. package/neural-interface/i18n/en.json +3463 -200
  120. package/neural-interface/i18n/pt-BR.json +3411 -151
  121. package/neural-interface/lib/assistant-api.js +127 -28
  122. package/neural-interface/lib/assistant-brains/claude.js +4 -3
  123. package/neural-interface/lib/assistant-brains/codex-browser-policy-hook.mjs +12 -2
  124. package/neural-interface/lib/assistant-brains/codex-route-gate-hook.mjs +32 -9
  125. package/neural-interface/lib/assistant-brains/codex.js +9 -5
  126. package/neural-interface/lib/assistant-brains/opencode.js +120 -54
  127. package/neural-interface/lib/assistant-brains/opencode2-browser-policy/index.js +28 -13
  128. package/neural-interface/lib/assistant-brains/opencode2-route-gate/index.js +59 -19
  129. package/neural-interface/lib/assistant-catalog.js +6 -7
  130. package/neural-interface/lib/assistant-dispatch.js +871 -156
  131. package/neural-interface/lib/assistant-envelope.js +3 -1
  132. package/neural-interface/lib/assistant-persona.js +80 -11
  133. package/neural-interface/lib/assistant-plan-permissions.js +10 -9
  134. package/neural-interface/lib/assistant-route-gate-codex.js +3 -0
  135. package/neural-interface/lib/assistant-route-gate.js +6 -4
  136. package/neural-interface/lib/assistant-router.js +13 -8
  137. package/neural-interface/lib/assistant-runtime.js +56 -18
  138. package/neural-interface/lib/assistant-task-prompt.js +78 -14
  139. package/neural-interface/lib/assistant-tools.js +51 -0
  140. package/neural-interface/lib/backup-service.js +620 -3
  141. package/neural-interface/lib/backup-zip-reader.js +10 -5
  142. package/neural-interface/lib/backup-zip-writer.js +257 -0
  143. package/neural-interface/lib/browser-execution.js +10 -2
  144. package/neural-interface/lib/browser-guard-cdp.js +83 -0
  145. package/neural-interface/lib/browser-network-guard.js +284 -0
  146. package/neural-interface/lib/browser-policy-roots.js +64 -0
  147. package/neural-interface/lib/browser-tool-policy.js +186 -31
  148. package/neural-interface/lib/claude-agent-bridge.js +4 -2
  149. package/neural-interface/lib/claude-hooks-status.js +103 -0
  150. package/neural-interface/lib/claude-temporary.js +5 -11
  151. package/neural-interface/lib/cli-install.js +18 -138
  152. package/neural-interface/lib/cli-resolver.js +4 -1
  153. package/neural-interface/lib/cli-update-planner.js +32 -31
  154. package/neural-interface/lib/cloudflared/tunnel.js +42 -2
  155. package/neural-interface/lib/codex-mcp-approvals.js +11 -0
  156. package/neural-interface/lib/codex-worker-app-server.js +172 -0
  157. package/neural-interface/lib/confined-fs.js +30 -3
  158. package/neural-interface/lib/crash-notice-api.js +98 -0
  159. package/neural-interface/lib/editor-completion-opencode.js +68 -68
  160. package/neural-interface/lib/exec-loop-command.js +4 -8
  161. package/neural-interface/lib/http-guards.js +8 -2
  162. package/neural-interface/lib/install-details.js +4 -1
  163. package/neural-interface/lib/mcp-agent-servers.js +67 -0
  164. package/neural-interface/lib/mcp-client-config.js +0 -29
  165. package/neural-interface/lib/mcp-external-servers.js +323 -0
  166. package/neural-interface/lib/{mcp-profile-refresh-coordinator.js → mcp-tools-refresh-coordinator.js} +4 -4
  167. package/neural-interface/lib/morelogin.js +30 -0
  168. package/neural-interface/lib/native-loop-providers.js +547 -47
  169. package/neural-interface/lib/native-loop-runtime.js +268 -41
  170. package/neural-interface/lib/opencode-config.js +557 -0
  171. package/neural-interface/lib/opencode-db-schema.js +17 -34
  172. package/neural-interface/lib/opencode-history.js +48 -218
  173. package/neural-interface/lib/opencode-mcp-ready.js +108 -0
  174. package/neural-interface/lib/opencode-native-boundary.js +44 -0
  175. package/neural-interface/lib/opencode-protocol.js +249 -112
  176. package/neural-interface/lib/opencode-runtime-config.js +32 -22
  177. package/neural-interface/lib/opencode-serve-records.js +39 -21
  178. package/neural-interface/lib/opencode-v2-client.js +48 -153
  179. package/neural-interface/lib/opencode-v2-ws-requests.js +36 -210
  180. package/neural-interface/lib/opencode-worker-data.js +27 -0
  181. package/neural-interface/lib/opencode2-client.js +73 -37
  182. package/neural-interface/lib/opencode2-proxy.js +23 -63
  183. package/neural-interface/lib/opencode2-translate.js +51 -2
  184. package/neural-interface/lib/owner-authority.js +249 -0
  185. package/neural-interface/lib/page-session.js +275 -0
  186. package/neural-interface/lib/peer-identity.js +763 -0
  187. package/neural-interface/lib/request-gate.js +17 -2
  188. package/neural-interface/lib/rulesets/api.js +1 -1
  189. package/neural-interface/lib/rulesets/fs-safe.js +1 -1
  190. package/neural-interface/lib/rulesets/installer.js +92 -27
  191. package/neural-interface/lib/rulesets/json-edit.js +1 -1
  192. package/neural-interface/lib/session-cache-builder.js +17 -30
  193. package/neural-interface/lib/session-title-generator.js +58 -36
  194. package/neural-interface/lib/skills/api-v2.js +495 -0
  195. package/neural-interface/lib/skills/bundle.js +418 -0
  196. package/neural-interface/lib/skills/catalog.js +294 -0
  197. package/neural-interface/lib/skills/creation-api.js +191 -0
  198. package/neural-interface/lib/skills/creation-brief.js +247 -0
  199. package/neural-interface/lib/skills/creation-sources.js +267 -0
  200. package/neural-interface/lib/skills/creation.js +843 -0
  201. package/neural-interface/lib/skills/deploy.js +564 -0
  202. package/neural-interface/lib/skills/digest.js +119 -0
  203. package/neural-interface/lib/skills/discover.js +111 -0
  204. package/neural-interface/lib/skills/draft-confinement.js +214 -0
  205. package/neural-interface/lib/skills/eval-confinement.js +397 -0
  206. package/neural-interface/lib/skills/eval-sandbox.js +280 -0
  207. package/neural-interface/lib/skills/eval-trace.js +119 -0
  208. package/neural-interface/lib/skills/evals-api.js +212 -0
  209. package/neural-interface/lib/skills/evals.js +1573 -0
  210. package/neural-interface/lib/skills/export.js +200 -0
  211. package/neural-interface/lib/skills/frontmatter.js +64 -0
  212. package/neural-interface/lib/skills/health.js +75 -0
  213. package/neural-interface/lib/skills/json-edit.js +165 -0
  214. package/neural-interface/lib/skills/known-bridge-digests.json +387 -0
  215. package/neural-interface/lib/skills/known-bridges.js +96 -0
  216. package/neural-interface/lib/skills/library.js +823 -0
  217. package/neural-interface/lib/skills/local-store.js +21 -0
  218. package/neural-interface/lib/skills/migration.js +433 -0
  219. package/neural-interface/lib/skills/operations.js +45 -0
  220. package/neural-interface/lib/skills/ownership.js +427 -0
  221. package/neural-interface/lib/skills/packages.js +88 -0
  222. package/neural-interface/lib/skills/reviews.js +212 -0
  223. package/neural-interface/lib/skills/revision.js +80 -0
  224. package/neural-interface/lib/skills/roots.js +262 -0
  225. package/neural-interface/lib/skills/runtime-checks.js +355 -0
  226. package/neural-interface/lib/skills/scan-assets.js +252 -0
  227. package/neural-interface/lib/skills/scan-context.js +26 -0
  228. package/neural-interface/lib/skills/scan-shell.js +324 -0
  229. package/neural-interface/lib/skills/scan.js +364 -0
  230. package/neural-interface/lib/skills/scanners.js +54 -0
  231. package/neural-interface/lib/skills/service.js +522 -0
  232. package/neural-interface/lib/skills/telemetry.js +113 -0
  233. package/neural-interface/lib/sqlite-integrity-worker.js +22 -0
  234. package/neural-interface/lib/tool-lists.js +124 -0
  235. package/neural-interface/lib/whatsapp/bridge.js +6 -3
  236. package/neural-interface/lib/worker-confinement.js +1192 -0
  237. package/neural-interface/lib/worker-guard-setting.js +56 -0
  238. package/neural-interface/lib/worker-mcp-channel.js +175 -0
  239. package/neural-interface/lib/worker-relay-ports.js +227 -0
  240. package/neural-interface/lib/worker-resume.js +229 -0
  241. package/neural-interface/lib/worker-sandbox.js +299 -0
  242. package/neural-interface/package-lock.json +18 -12
  243. package/neural-interface/package.json +2 -2
  244. package/neural-interface/public/index.html +3 -1
  245. package/neural-interface/public/index2d.html +19 -11
  246. package/neural-interface/public/onboarding.html +2 -2
  247. package/neural-interface/public/shared/account-menu.js +8 -5
  248. package/neural-interface/public/shared/agent-runtime-options.js +35 -19
  249. package/neural-interface/public/shared/api.js +1527 -1336
  250. package/neural-interface/public/shared/app-frame.js +192 -0
  251. package/neural-interface/public/shared/assistant/asst-agents-dock.js +33 -2
  252. package/neural-interface/public/shared/assistant/asst-ask-styles.js +270 -0
  253. package/neural-interface/public/shared/assistant/asst-ask-tail.js +118 -0
  254. package/neural-interface/public/shared/assistant/asst-ask.js +397 -0
  255. package/neural-interface/public/shared/assistant/asst-brain-picker.js +49 -35
  256. package/neural-interface/public/shared/assistant/asst-control.js +157 -26
  257. package/neural-interface/public/shared/assistant/asst-followups-card.js +383 -0
  258. package/neural-interface/public/shared/assistant/asst-followups.js +417 -0
  259. package/neural-interface/public/shared/assistant/asst-limit-dock.js +40 -0
  260. package/neural-interface/public/shared/assistant/asst-limit-wait.js +294 -0
  261. package/neural-interface/public/shared/assistant/asst-limit.js +168 -0
  262. package/neural-interface/public/shared/assistant/asst-mascot.js +49 -12
  263. package/neural-interface/public/shared/assistant/asst-menu.js +7 -6
  264. package/neural-interface/public/shared/assistant/asst-panel.js +106 -11
  265. package/neural-interface/public/shared/assistant/asst-rack-styles.js +66 -49
  266. package/neural-interface/public/shared/assistant/asst-render.js +401 -77
  267. package/neural-interface/public/shared/assistant/asst-route.js +83 -12
  268. package/neural-interface/public/shared/assistant/asst-scenes.js +2 -0
  269. package/neural-interface/public/shared/assistant/asst-sidepanel.js +67 -4
  270. package/neural-interface/public/shared/assistant/asst-slash.js +9 -7
  271. package/neural-interface/public/shared/assistant/asst-state.js +11 -15
  272. package/neural-interface/public/shared/assistant/asst-stop.js +127 -0
  273. package/neural-interface/public/shared/assistant/asst-styles.js +59 -21
  274. package/neural-interface/public/shared/assistant/asst-tool-kinds.js +156 -16
  275. package/neural-interface/public/shared/automation-context.js +5 -2
  276. package/neural-interface/public/shared/cc-dropdown.js +89 -19
  277. package/neural-interface/public/shared/cdx/cdx-context-menu.js +10 -9
  278. package/neural-interface/public/shared/cdx/cdx-copy.js +16 -0
  279. package/neural-interface/public/shared/cdx/cdx-output-model.js +22 -1
  280. package/neural-interface/public/shared/cdx/cdx-output.js +8 -7
  281. package/neural-interface/public/shared/cdx/cdx-panel.js +45 -149
  282. package/neural-interface/public/shared/cdx/cdx-protocol.js +28 -1
  283. package/neural-interface/public/shared/cdx/cdx-render.js +47 -29
  284. package/neural-interface/public/shared/cdx/cdx-requests.js +3 -3
  285. package/neural-interface/public/shared/cdx/cdx-skill-hints.js +54 -0
  286. package/neural-interface/public/shared/cdx/cdx-styles.js +11 -3
  287. package/neural-interface/public/shared/cdx/cdx-tabs.js +52 -39
  288. package/neural-interface/public/shared/cdx/cdx-ws.js +0 -1
  289. package/neural-interface/public/shared/cli-install-view.js +4 -8
  290. package/neural-interface/public/shared/cli-status.js +2 -2
  291. package/neural-interface/public/shared/constants.js +2 -2
  292. package/neural-interface/public/shared/context-menu-areas.js +34 -0
  293. package/neural-interface/public/shared/context-menu-logic.js +243 -0
  294. package/neural-interface/public/shared/context-menu-model.js +366 -0
  295. package/neural-interface/public/shared/context-menu.js +372 -0
  296. package/neural-interface/public/shared/cp/cp-context-menu.js +10 -9
  297. package/neural-interface/public/shared/crash-notice-view.js +168 -0
  298. package/neural-interface/public/shared/desktop-update-view.js +20 -0
  299. package/neural-interface/public/shared/editor/editor-panel-logic.js +2 -2
  300. package/neural-interface/public/shared/html-shell.js +9 -11
  301. package/neural-interface/public/shared/i18n.js +2 -0
  302. package/neural-interface/public/shared/ocp-v2/ocp-v2-approvals.js +7 -7
  303. package/neural-interface/public/shared/ocp-v2/ocp-v2-binding.js +4 -4
  304. package/neural-interface/public/shared/ocp-v2/ocp-v2-changes-logic.js +3 -101
  305. package/neural-interface/public/shared/ocp-v2/ocp-v2-childpanel.js +2 -0
  306. package/neural-interface/public/shared/ocp-v2/ocp-v2-compact-button.js +1 -3
  307. package/neural-interface/public/shared/ocp-v2/ocp-v2-composer-logic.js +2 -6
  308. package/neural-interface/public/shared/ocp-v2/ocp-v2-context-menu.js +11 -11
  309. package/neural-interface/public/shared/ocp-v2/ocp-v2-context-model.js +0 -3
  310. package/neural-interface/public/shared/ocp-v2/ocp-v2-events.js +8 -4
  311. package/neural-interface/public/shared/ocp-v2/ocp-v2-manager.js +7 -7
  312. package/neural-interface/public/shared/ocp-v2/ocp-v2-outcome-notifications.js +16 -0
  313. package/neural-interface/public/shared/ocp-v2/ocp-v2-panel.js +47 -252
  314. package/neural-interface/public/shared/ocp-v2/ocp-v2-plan.js +1 -2
  315. package/neural-interface/public/shared/ocp-v2/ocp-v2-projectbar.js +95 -202
  316. package/neural-interface/public/shared/ocp-v2/ocp-v2-render.js +6 -21
  317. package/neural-interface/public/shared/ocp-v2/ocp-v2-send.js +4 -4
  318. package/neural-interface/public/shared/ocp-v2/ocp-v2-session-actions.js +8 -61
  319. package/neural-interface/public/shared/ocp-v2/ocp-v2-sessions-logic.js +2 -91
  320. package/neural-interface/public/shared/ocp-v2/ocp-v2-slash-hints.js +16 -15
  321. package/neural-interface/public/shared/ocp-v2/ocp-v2-state.js +8 -21
  322. package/neural-interface/public/shared/ocp-v2/ocp-v2-status-logic.js +1 -99
  323. package/neural-interface/public/shared/ocp-v2/ocp-v2-status.js +5 -35
  324. package/neural-interface/public/shared/ocp-v2/ocp-v2-styles.js +0 -21
  325. package/neural-interface/public/shared/ocp-v2/{ocp-v2-profile-inheritance.js → ocp-v2-tools-inheritance.js} +16 -16
  326. package/neural-interface/public/shared/ocp-v2/ocp-v2-ws.js +14 -47
  327. package/neural-interface/public/shared/page-session.js +96 -0
  328. package/neural-interface/public/shared/registry.js +5 -3
  329. package/neural-interface/public/shared/retired-2d.js +3 -0
  330. package/neural-interface/public/shared/schedules-view.js +195 -61
  331. package/neural-interface/public/shared/settings/settings-claude-hooks.js +135 -0
  332. package/neural-interface/public/shared/settings/settings-data.js +3 -1
  333. package/neural-interface/public/shared/settings/settings-ia.js +6 -6
  334. package/neural-interface/public/shared/settings/settings-opencode.js +89 -0
  335. package/neural-interface/public/shared/settings/settings-skill-folders-pane.js +265 -0
  336. package/neural-interface/public/shared/settings/settings-skill-folders.js +130 -0
  337. package/neural-interface/public/shared/settings/settings-worker-guard.js +72 -0
  338. package/neural-interface/public/shared/skills/ss-args.js +96 -0
  339. package/neural-interface/public/shared/skills/ss-creation.js +219 -0
  340. package/neural-interface/public/shared/skills/ss-dialog.js +130 -0
  341. package/neural-interface/public/shared/skills/ss-doc.js +173 -0
  342. package/neural-interface/public/shared/skills/ss-eval-adapters.js +269 -0
  343. package/neural-interface/public/shared/skills/ss-eval-grade.js +359 -0
  344. package/neural-interface/public/shared/skills/ss-eval-optimise.js +153 -0
  345. package/neural-interface/public/shared/skills/ss-eval-plan.js +315 -0
  346. package/neural-interface/public/shared/skills/ss-evals.js +417 -0
  347. package/neural-interface/public/shared/skills/ss-form.js +159 -0
  348. package/neural-interface/public/shared/skills/ss-frontmatter.js +376 -0
  349. package/neural-interface/public/shared/skills/ss-ids.js +32 -0
  350. package/neural-interface/public/shared/skills/ss-lint.js +141 -0
  351. package/neural-interface/public/shared/skills/ss-presets.js +117 -0
  352. package/neural-interface/public/shared/skills/ss-profiles.js +475 -0
  353. package/neural-interface/public/shared/skills/ss-resources.js +165 -0
  354. package/neural-interface/public/shared/skills/ss-review-view.js +190 -0
  355. package/neural-interface/public/shared/skills/ss-tabs.js +79 -0
  356. package/neural-interface/public/shared/skills/ss-unicode.js +135 -0
  357. package/neural-interface/public/shared/skills/studio/sx-actions.js +222 -0
  358. package/neural-interface/public/shared/skills/studio/sx-cm.js +101 -0
  359. package/neural-interface/public/shared/skills/studio/sx-create-ai-view.js +749 -0
  360. package/neural-interface/public/shared/skills/studio/sx-create-ai.js +236 -0
  361. package/neural-interface/public/shared/skills/studio/sx-create-view.js +254 -0
  362. package/neural-interface/public/shared/skills/studio/sx-create.js +126 -0
  363. package/neural-interface/public/shared/skills/studio/sx-diff-view.js +43 -0
  364. package/neural-interface/public/shared/skills/studio/sx-diff.js +133 -0
  365. package/neural-interface/public/shared/skills/studio/sx-docs.js +480 -0
  366. package/neural-interface/public/shared/skills/studio/sx-dom.js +229 -0
  367. package/neural-interface/public/shared/skills/studio/sx-edit-view.js +848 -0
  368. package/neural-interface/public/shared/skills/studio/sx-editor-model.js +193 -0
  369. package/neural-interface/public/shared/skills/studio/sx-errors.js +22 -0
  370. package/neural-interface/public/shared/skills/studio/sx-geometry.js +82 -0
  371. package/neural-interface/public/shared/skills/studio/sx-import.js +262 -0
  372. package/neural-interface/public/shared/skills/studio/sx-inspector.js +308 -0
  373. package/neural-interface/public/shared/skills/studio/sx-install-flow.js +279 -0
  374. package/neural-interface/public/shared/skills/studio/sx-install.js +296 -0
  375. package/neural-interface/public/shared/skills/studio/sx-library-view.js +639 -0
  376. package/neural-interface/public/shared/skills/studio/sx-library.js +475 -0
  377. package/neural-interface/public/shared/skills/studio/sx-main.js +681 -0
  378. package/neural-interface/public/shared/skills/studio/sx-operations-view.js +98 -0
  379. package/neural-interface/public/shared/skills/studio/sx-operations.js +11 -0
  380. package/neural-interface/public/shared/skills/studio/sx-plan.js +158 -0
  381. package/neural-interface/public/shared/skills/studio/sx-publish-view.js +621 -0
  382. package/neural-interface/public/shared/skills/studio/sx-review-view.js +369 -0
  383. package/neural-interface/public/shared/skills/studio/sx-review.js +148 -0
  384. package/neural-interface/public/shared/skills/studio/sx-scan.js +35 -0
  385. package/neural-interface/public/shared/skills/studio/sx-snippets.js +96 -0
  386. package/neural-interface/public/shared/skills/studio/sx-subject.js +248 -0
  387. package/neural-interface/public/shared/skills/studio/sx-test-view.js +994 -0
  388. package/neural-interface/public/shared/skills/studio/sx-test.js +328 -0
  389. package/neural-interface/public/shared/skills/studio/sx-versions-view.js +241 -0
  390. package/neural-interface/public/shared/skills/studio/sx-workspace-view.js +425 -0
  391. package/neural-interface/public/shared/styleguide/sg-panel.js +4 -2
  392. package/neural-interface/public/shared/styles.css +1525 -1779
  393. package/neural-interface/public/shared/synabun-mascot.js +1115 -9
  394. package/neural-interface/public/shared/tool-list-picker.js +55 -0
  395. package/neural-interface/public/shared/tool-list-storage.js +24 -0
  396. package/neural-interface/public/shared/tool-list-view.js +112 -0
  397. package/neural-interface/public/shared/tour/tour-actor.js +268 -0
  398. package/neural-interface/public/shared/tour/tour-beats.js +336 -0
  399. package/neural-interface/public/shared/tour/tour-engine.js +884 -0
  400. package/neural-interface/public/shared/tour/tour-ink.js +390 -0
  401. package/neural-interface/public/shared/tour/tour-stage.js +440 -0
  402. package/neural-interface/public/shared/tour/tour-state.js +40 -0
  403. package/neural-interface/public/shared/tour/tour-tips.js +99 -0
  404. package/neural-interface/public/shared/tour/tour.css +144 -0
  405. package/neural-interface/public/shared/ui-automation-studio.js +91 -128
  406. package/neural-interface/public/shared/ui-claude-panel.js +77 -5
  407. package/neural-interface/public/shared/ui-command-runner.js +6 -3
  408. package/neural-interface/public/shared/ui-context-menu.js +295 -0
  409. package/neural-interface/public/shared/ui-cost-widget.js +57 -59
  410. package/neural-interface/public/shared/ui-crash-notice.js +187 -0
  411. package/neural-interface/public/shared/ui-detail.js +15 -3
  412. package/neural-interface/public/shared/ui-image-gallery.js +50 -47
  413. package/neural-interface/public/shared/ui-keybinds.js +26 -1
  414. package/neural-interface/public/shared/ui-menubar.js +8 -18
  415. package/neural-interface/public/shared/ui-native-loop-router-state.js +5 -0
  416. package/neural-interface/public/shared/ui-native-loop-router.js +7 -1
  417. package/neural-interface/public/shared/ui-navbar.js +4 -24
  418. package/neural-interface/public/shared/ui-panels.js +96 -65
  419. package/neural-interface/public/shared/ui-schedules-studio.js +507 -423
  420. package/neural-interface/public/shared/ui-sessions.js +5 -3
  421. package/neural-interface/public/shared/ui-settings.js +594 -462
  422. package/neural-interface/public/shared/ui-sidepanel-sessions.js +1 -1
  423. package/neural-interface/public/shared/ui-sidepanel-state.js +23 -12
  424. package/neural-interface/public/shared/ui-sidepanel-windows.js +16 -11
  425. package/neural-interface/public/shared/ui-skills.js +33 -2830
  426. package/neural-interface/public/shared/ui-styleguide.js +5 -2
  427. package/neural-interface/public/shared/ui-sync.js +15 -5
  428. package/neural-interface/public/shared/ui-terminal.js +124 -180
  429. package/neural-interface/public/shared/ui-tool-updates.js +3 -1
  430. package/neural-interface/public/shared/ui-tooltip.js +10 -5
  431. package/neural-interface/public/shared/ui-trash.js +6 -4
  432. package/neural-interface/public/shared/ui-tutorial-layout.js +3 -117
  433. package/neural-interface/public/shared/ui-tutorial-preview.js +103 -0
  434. package/neural-interface/public/shared/ui-tutorial-steps.js +3 -65
  435. package/neural-interface/public/shared/ui-tutorial.css +47 -100
  436. package/neural-interface/public/shared/ui-tutorial.js +285 -509
  437. package/neural-interface/public/shared/ui-update.js +38 -2
  438. package/neural-interface/public/shared/ui-whiteboard.js +311 -58
  439. package/neural-interface/public/shared/ui-worker-end.js +4 -0
  440. package/neural-interface/public/shared/ui-workspaces.js +3 -2
  441. package/neural-interface/public/shared/view-variant.js +66 -0
  442. package/neural-interface/public/shared/whatsapp/wa-view.js +1 -1
  443. package/neural-interface/public/shared/whiteboard-list.js +172 -0
  444. package/neural-interface/public/shared/worker-end.js +10 -3
  445. package/neural-interface/public/shared/worker-restart.js +12 -0
  446. package/neural-interface/public/sidepanel-session.html +1 -1
  447. package/neural-interface/public/variant/2d/background.js +2 -1
  448. package/neural-interface/public/variant/2d/context-menu.js +8 -2
  449. package/neural-interface/public/variant/2d/graph.js +42 -20
  450. package/neural-interface/public/variant/2d/hulls.js +14 -9
  451. package/neural-interface/public/variant/2d/lasso.js +11 -7
  452. package/neural-interface/public/variant/2d/main.js +32 -1
  453. package/neural-interface/public/variant/2d/minimap.js +11 -13
  454. package/neural-interface/public/variant/3d/main.js +69 -6
  455. package/neural-interface/public/variant/3d/map-controls.js +24 -9
  456. package/neural-interface/public/vendor/caveat/OFL.txt +93 -0
  457. package/neural-interface/public/vendor/caveat/caveat-latin-400-normal.woff2 +0 -0
  458. package/neural-interface/public/vendor/caveat/caveat-latin-700-normal.woff2 +0 -0
  459. package/neural-interface/public/vendor/caveat/caveat.css +17 -0
  460. package/neural-interface/public/vendor/caveat/manifest.json +12 -0
  461. package/neural-interface/public/vendor/yaml/LICENSE +13 -0
  462. package/neural-interface/public/vendor/yaml/manifest.json +4 -0
  463. package/neural-interface/public/vendor/yaml/yaml-2.9.1.min.js +137 -0
  464. package/neural-interface/scripts/build-vendor.mjs +32 -0
  465. package/neural-interface/server.js +3934 -2454
  466. package/neural-interface/templates/rulesets/core.md +1 -1
  467. package/neural-interface/templates/rulesets/legacy-hashes.json +18 -0
  468. package/neural-interface/templates/rulesets/manifest.json +8 -8
  469. package/npm-shrinkwrap.json +18 -12
  470. package/package.json +16 -3
  471. package/postinstall.js +9 -4
  472. package/preuninstall.js +1 -1
  473. package/setup.js +97 -322
  474. package/skills/skill-forge/SKILL.md +177 -0
  475. package/skills/skill-forge/evals/evals.json +54 -0
  476. package/skills/skill-forge/references/package.md +105 -0
  477. package/skills/skill-forge/references/review-and-recovery.md +63 -0
  478. package/skills/skill-forge/references/runtime-profiles.md +20 -0
  479. package/skills/skill-forge/references/sources-and-capabilities.md +51 -0
  480. package/skills/synabun/SKILL.md +10 -7
  481. package/skills/synabun/modules/audit.md +1 -5
  482. package/skills/synabun/modules/changelog.md +1 -5
  483. package/skills/synabun/modules/idea.md +1 -1
  484. package/skills/synabun/modules/memorize.md +6 -9
  485. package/skills/synabun/modules/schedule.md +4 -4
  486. package/mcp-server/dist/services/profiles.d.ts +0 -120
  487. package/mcp-server/dist/services/profiles.d.ts.map +0 -1
  488. package/mcp-server/dist/services/profiles.js +0 -387
  489. package/mcp-server/dist/services/profiles.js.map +0 -1
  490. package/mcp-server/dist/tools/leonardo-browser-tools.d.ts +0 -97
  491. package/mcp-server/dist/tools/leonardo-browser-tools.d.ts.map +0 -1
  492. package/mcp-server/dist/tools/leonardo-browser-tools.js +0 -228
  493. package/mcp-server/dist/tools/leonardo-browser-tools.js.map +0 -1
  494. package/mcp-server/dist/tools/leonardo.d.ts +0 -13
  495. package/mcp-server/dist/tools/leonardo.d.ts.map +0 -1
  496. package/mcp-server/dist/tools/leonardo.js +0 -21
  497. package/mcp-server/dist/tools/leonardo.js.map +0 -1
  498. package/mcp-server/dist/tools/profile.d.ts +0 -17
  499. package/mcp-server/dist/tools/profile.d.ts.map +0 -1
  500. package/mcp-server/dist/tools/profile.js +0 -114
  501. package/mcp-server/dist/tools/profile.js.map +0 -1
  502. package/mcp-server/src/services/profiles.ts +0 -428
  503. package/mcp-server/src/tools/leonardo-browser-tools.ts +0 -291
  504. package/mcp-server/src/tools/leonardo.ts +0 -30
  505. package/mcp-server/src/tools/profile.ts +0 -122
  506. package/neural-interface/lib/assistant-brains/opencode-browser-policy.js +0 -47
  507. package/neural-interface/lib/assistant-brains/opencode-route-gate.js +0 -62
  508. package/neural-interface/lib/mcp-profile-servers.js +0 -120
  509. package/neural-interface/public/shared/ui-tutorial-draw.js +0 -311
  510. package/skills/leonardo/SKILL.md +0 -168
  511. package/skills/leonardo/icon.svg +0 -1
  512. package/skills/leonardo/modules/browser-guide.md +0 -378
  513. package/skills/leonardo/modules/image-prompter.md +0 -283
  514. package/skills/leonardo/modules/model-advisor.md +0 -100
  515. package/skills/leonardo/modules/prompt-library.md +0 -110
  516. package/skills/leonardo/modules/style-guide.md +0 -186
  517. package/skills/leonardo/modules/video-prompter.md +0 -317
@@ -25,6 +25,8 @@
25
25
  // closes and moves on, the choice as it was. An open menu is placed `fixed` against its trigger,
26
26
  // so a scrolling pane never cuts it off, and kept inside the box that clips it (a window).
27
27
 
28
+ import { placementArea } from './app-frame.js';
29
+
28
30
  const ARROW = '<svg class="cc-dropdown-arrow" viewBox="0 0 24 24" aria-hidden="true"><polyline points="6 9 12 15 18 9"/></svg>';
29
31
  const GAP = 4; // between the trigger and its menu
30
32
  const EDGE = 6; // the least room left between a menu and the edge of what clips it
@@ -94,13 +96,39 @@ export function matchIndex(options, typed, from = -1) {
94
96
  return -1;
95
97
  }
96
98
 
99
+ /** Multi-choice values in menu order. Disabled selected rows are mandatory. */
100
+ export function multipleValues(options) {
101
+ const exclusive = options.find(o => o.selected && o.exclusive && !o.scope);
102
+ if (exclusive) return [String(exclusive.value ?? '')];
103
+ const scoped = new Set(options.filter(o => o.selected && o.exclusive && o.scope).map(o => o.scope));
104
+ return options.filter(o => o.selected && (!scoped.has(o.scope) || o.exclusive)).map(o => String(o.value ?? ''));
105
+ }
106
+
107
+ /** Exclusive rows may apply globally (inheritance) or within a named section. */
108
+ export function toggleMultiple(options, values, index) {
109
+ const option = options[index];
110
+ if (!option || option.disabled) return [...values];
111
+ if (option.exclusive && !option.scope) return [String(option.value ?? '')];
112
+ const selected = new Set(values);
113
+ for (const row of options) {
114
+ if ((row.exclusive && !row.scope) || (option.exclusive && row.scope === option.scope) || (!option.exclusive && row.exclusive && row.scope === option.scope)) selected.delete(String(row.value ?? ''));
115
+ }
116
+ const value = String(option.value ?? '');
117
+ if (option.exclusive) selected.add(value);
118
+ else if (selected.has(value)) selected.delete(value); else selected.add(value);
119
+ return multipleValues(options.map(row => ({ ...row, selected: selected.has(String(row.value ?? '')) || (!option.exclusive && row.disabled && row.selected) })));
120
+ }
121
+
122
+ const multipleText = (options, values) => options.filter(o => values.includes(String(o.value ?? '')))
123
+ .map(o => o.label).join(', ');
124
+
97
125
  // ── Markup ──
98
126
 
99
127
  function optionHTML(id, option, index, chosen) {
100
128
  const label = String(option.label ?? '');
101
- const active = index === chosen;
129
+ const active = Array.isArray(chosen) ? (chosen.includes(String(option.value ?? '')) || (option.automatic && option.selected)) : index === chosen;
102
130
  return `<div class="cc-dropdown-item${active ? ' active' : ''}" id="${esc(id)}-opt-${index}" role="option" aria-selected="${active}"`
103
- + `${option.disabled ? ' aria-disabled="true"' : ''} data-value="${esc(option.value ?? '')}" data-index="${index}" title="${esc(label)}">${esc(label)}</div>`;
131
+ + `${option.disabled ? ' aria-disabled="true"' : ''} data-value="${esc(option.value ?? '')}" data-index="${index}"${option.exclusive ? ' data-exclusive="true"' : ''}${option.scope ? ` data-scope="${esc(option.scope)}"` : ''}${option.automatic ? ' data-automatic="true"' : ''} data-label="${esc(label)}" title="${esc(option.caption || label)}">${Array.isArray(chosen) ? '<span class="cc-dropdown-check" aria-hidden="true"></span>' : ''}<span>${esc(label)}${option.caption ? `<small class="cc-dropdown-caption">${esc(option.caption)}</small>` : ''}</span></div>`;
104
132
  }
105
133
 
106
134
  /** The menu's options (and headings) for `entries`, the `chosen` index marked. */
@@ -120,17 +148,17 @@ export function menuHTML(id, entries, chosen = chosenIndex(flatOptions(entries))
120
148
  * Name it with `labelledBy` (the id of its visible label) or `label`. `classes` and `attrs` go on
121
149
  * the `.cc-dropdown`.
122
150
  */
123
- export function dropdownHTML(id, entries, { classes = '', labelledBy = '', label = '', attrs = '', inputAttrs = '', disabled = false } = {}) {
151
+ export function dropdownHTML(id, entries, { classes = '', labelledBy = '', label = '', attrs = '', inputAttrs = '', disabled = false, multiple = false, summary = '' } = {}) {
124
152
  const options = flatOptions(entries);
125
- const chosen = chosenIndex(options);
153
+ const chosen = multiple ? multipleValues(options) : chosenIndex(options);
126
154
  const current = chosen >= 0 ? options[chosen] : null;
127
155
  const name = labelledBy ? ` aria-labelledby="${esc(labelledBy)}"` : label ? ` aria-label="${esc(label)}"` : '';
128
- const text = String(current?.label ?? '');
129
- return `<input type="hidden" id="${esc(id)}" value="${esc(current?.value ?? '')}"${disabled ? ' disabled' : ''}${inputAttrs ? ` ${inputAttrs}` : ''}>`
130
- + `<div class="cc-dropdown${classes ? ` ${classes}` : ''}${disabled ? ' disabled' : ''}" data-for="${esc(id)}"${attrs ? ` ${attrs}` : ''}>`
156
+ const text = multiple ? (summary || multipleText(options, chosen)) : String(current?.label ?? '');
157
+ return `<input type="hidden" id="${esc(id)}" value="${esc(multiple ? chosen.join(',') : current?.value ?? '')}"${disabled ? ' disabled' : ''}${inputAttrs ? ` ${inputAttrs}` : ''}>`
158
+ + `<div class="cc-dropdown${classes ? ` ${classes}` : ''}${disabled ? ' disabled' : ''}" data-for="${esc(id)}"${multiple ? ' data-multiple="true"' : ''}${attrs ? ` ${attrs}` : ''}>`
131
159
  + `<button type="button" class="cc-dropdown-trigger" id="${esc(id)}-trigger" role="combobox" aria-haspopup="listbox" aria-expanded="false" aria-controls="${esc(id)}-menu"${name}${disabled ? ' disabled' : ''}>`
132
160
  + `<span class="cc-dropdown-value" title="${esc(text)}">${esc(text)}</span>${ARROW}</button>`
133
- + `<div class="cc-dropdown-menu" id="${esc(id)}-menu" role="listbox"${name} tabindex="-1">${menuHTML(id, entries, chosen)}</div>`
161
+ + `<div class="cc-dropdown-menu" id="${esc(id)}-menu" role="listbox"${multiple ? ' aria-multiselectable="true"' : ''}${name} tabindex="-1">${menuHTML(id, entries, chosen)}</div>`
134
162
  + '</div>';
135
163
  }
136
164
 
@@ -143,7 +171,7 @@ const partsOf = (dd) => ({
143
171
  input: dd.dataset.for ? document.getElementById(dd.dataset.for) : null,
144
172
  });
145
173
  const itemsOf = (dd) => [...(partsOf(dd).menu?.querySelectorAll('.cc-dropdown-item') || [])];
146
- const optionsOf = (items) => items.map(el => ({ value: el.dataset.value ?? '', label: el.textContent, disabled: el.getAttribute('aria-disabled') === 'true' }));
174
+ const optionsOf = (items) => items.map(el => ({ value: el.dataset.value ?? '', label: el.dataset.label ?? el.textContent, disabled: el.getAttribute('aria-disabled') === 'true', exclusive: el.dataset.exclusive === 'true', scope: el.dataset.scope || undefined, selected: el.classList.contains('active') }));
147
175
  const highlightedOf = (items) => items.findIndex(el => el.classList.contains('highlight'));
148
176
  const chosenOf = (items) => items.findIndex(el => el.classList.contains('active'));
149
177
  const isOpen = (dd) => !!dd?.classList.contains('open');
@@ -194,6 +222,21 @@ function showChosen(dd, index) {
194
222
  }
195
223
  }
196
224
 
225
+ function showMultiple(dd, values) {
226
+ const { label, input } = partsOf(dd);
227
+ const items = itemsOf(dd);
228
+ items.forEach(el => {
229
+ const active = values.includes(el.dataset.value ?? '') || el.dataset.automatic === 'true';
230
+ el.classList.toggle('active', active);
231
+ el.setAttribute('aria-selected', String(active));
232
+ });
233
+ if (input) input.value = values.join(',');
234
+ if (label) {
235
+ label.textContent = multipleText(optionsOf(items), values);
236
+ label.title = label.textContent;
237
+ }
238
+ }
239
+
197
240
  function highlight(dd, index) {
198
241
  const { trigger, menu } = partsOf(dd);
199
242
  const items = itemsOf(dd);
@@ -227,7 +270,9 @@ function place(dd) {
227
270
  Object.assign(style, { position: 'fixed', left: '0px', top: '0px', right: 'auto', bottom: 'auto', width: 'auto', maxHeight: 'none', minWidth: '0px', maxWidth: 'none' });
228
271
  const origin = menu.getBoundingClientRect(); // where the containing block's (0, 0) is on screen
229
272
  const t = trigger.getBoundingClientRect();
230
- let room = { left: 0, top: 0, right: window.innerWidth, bottom: window.innerHeight };
273
+ // The app frame for a trigger inside it (app-frame.js), else the window; then whatever clips the menu
274
+ const area = placementArea(t.left + t.width / 2, t.top + t.height / 2);
275
+ let room = { left: area.left, top: area.top, right: area.right, bottom: area.bottom };
231
276
  const box = fixedContainer(dd);
232
277
  if (box) {
233
278
  const cs = getComputedStyle(box);
@@ -268,8 +313,12 @@ function open(dd, index = null) {
268
313
  if (!trigger || trigger.disabled || dd.classList.contains('disabled')) return;
269
314
  for (const other of document.querySelectorAll('.cc-dropdown.open')) if (other !== dd) close(other);
270
315
  ensureAria(dd);
316
+ dd._ccBefore = partsOf(dd).input?.value;
317
+ dd._ccBeforeLabel = partsOf(dd).label?.textContent;
318
+ dd._ccBeforeMenu = partsOf(dd).menu?.innerHTML;
271
319
  dd.classList.add('open');
272
320
  trigger.setAttribute('aria-expanded', 'true');
321
+ partsOf(dd).input?.dispatchEvent(new Event('cc-dropdown-open', { bubbles: true }));
273
322
  if (document.activeElement !== trigger) trigger.focus({ preventScroll: true });
274
323
  place(dd);
275
324
  const items = itemsOf(dd);
@@ -278,8 +327,14 @@ function open(dd, index = null) {
278
327
  highlight(dd, index ?? (chosen >= 0 && !options[chosen].disabled ? chosen : edgeIndex(options, 'first')));
279
328
  }
280
329
 
281
- function close(dd, { focus = false } = {}) {
282
- const { trigger, menu } = partsOf(dd);
330
+ function close(dd, { focus = false, cancel = false } = {}) {
331
+ const { trigger, menu, input, label } = partsOf(dd);
332
+ const changed = isOpen(dd) && dd.dataset.multiple === 'true' && input && input.value !== dd._ccBefore;
333
+ if (cancel && changed) {
334
+ if (menu && dd._ccBeforeMenu != null) menu.innerHTML = dd._ccBeforeMenu;
335
+ showMultiple(dd, dd._ccBefore === '' ? [''] : dd._ccBefore.split(','));
336
+ if (label) { label.textContent = dd._ccBeforeLabel; label.title = label.textContent; }
337
+ }
283
338
  dd.classList.remove('open', 'drop-up');
284
339
  trigger?.setAttribute('aria-expanded', 'false');
285
340
  trigger?.removeAttribute('aria-activedescendant');
@@ -287,6 +342,10 @@ function close(dd, { focus = false } = {}) {
287
342
  menu?.removeAttribute('style');
288
343
  dd._ccPlacedAt = null;
289
344
  if (focus && trigger?.isConnected) trigger.focus({ preventScroll: true });
345
+ if (changed && !cancel) {
346
+ input.dispatchEvent(new Event('input', { bubbles: true }));
347
+ input.dispatchEvent(new Event('change', { bubbles: true }));
348
+ }
290
349
  }
291
350
 
292
351
  /** The user's choice: shown, the menu closed, the focus on the trigger, then `input` and `change` when it changed. */
@@ -295,6 +354,12 @@ function choose(dd, index) {
295
354
  const item = items[index];
296
355
  const { input } = partsOf(dd);
297
356
  if (!item || item.getAttribute('aria-disabled') === 'true') { close(dd, { focus: true }); return; }
357
+ if (dd.dataset.multiple === 'true') {
358
+ showMultiple(dd, toggleMultiple(optionsOf(items), items.filter(el => el.classList.contains('active')).map(el => el.dataset.value), index));
359
+ input?.dispatchEvent(new Event('cc-dropdown-draft', { bubbles: true }));
360
+ highlight(dd, index);
361
+ return;
362
+ }
298
363
  const changed = !!input && input.value !== (item.dataset.value ?? '');
299
364
  showChosen(dd, index);
300
365
  close(dd, { focus: true });
@@ -315,20 +380,25 @@ export function setDropdownItems(input, entries) {
315
380
  const wasOpen = isOpen(dd);
316
381
  const before = wasOpen ? itemsOf(dd)[highlightedOf(itemsOf(dd))]?.dataset.value : undefined;
317
382
  const options = flatOptions(entries);
318
- const chosen = chosenIndex(options);
383
+ const multiple = dd.dataset.multiple === 'true';
384
+ const chosen = multiple ? multipleValues(options) : chosenIndex(options);
319
385
  menu.innerHTML = menuHTML(input.id, entries, chosen);
320
- showChosen(dd, chosen);
386
+ if (multiple) showMultiple(dd, chosen); else showChosen(dd, chosen);
321
387
  if (!wasOpen) return;
322
388
  place(dd);
323
389
  const items = itemsOf(dd);
324
390
  const again = before === undefined ? -1 : items.findIndex(el => el.dataset.value === before);
325
- highlight(dd, again >= 0 ? again : chosen);
391
+ highlight(dd, again >= 0 ? again : chosenOf(items));
326
392
  }
327
393
 
328
394
  /** `select.value = …`: the first choice with that value, or none (the value is then ''). No event. */
329
395
  export function setDropdownValue(input, value) {
330
396
  const dd = dropdownOf(input);
331
397
  if (!dd) { if (input) input.value = value ?? ''; return; }
398
+ if (dd.dataset.multiple === 'true') {
399
+ showMultiple(dd, Array.isArray(value) ? value.map(String) : String(value ?? '').split(','));
400
+ return;
401
+ }
332
402
  showChosen(dd, itemsOf(dd).findIndex(el => el.dataset.value === String(value ?? '')));
333
403
  }
334
404
 
@@ -337,16 +407,16 @@ export function setDropdownDisabled(input, disabled) {
337
407
  const dd = dropdownOf(input);
338
408
  if (input) input.disabled = !!disabled;
339
409
  if (!dd) return;
340
- if (disabled && isOpen(dd)) close(dd);
410
+ if (disabled && isOpen(dd)) close(dd, { cancel: true });
341
411
  dd.classList.toggle('disabled', !!disabled);
342
412
  const { trigger } = partsOf(dd);
343
413
  if (trigger) trigger.disabled = !!disabled;
344
414
  }
345
415
 
346
416
  /** Closes every open dropdown under `root`; true when one was open. `focus` puts the focus back on its trigger. */
347
- export function closeDropdowns(root = document, { focus = false } = {}) {
417
+ export function closeDropdowns(root = document, { focus = false, cancel = false } = {}) {
348
418
  const open = [...root.querySelectorAll('.cc-dropdown.open')];
349
- for (const dd of open) close(dd, { focus });
419
+ for (const dd of open) close(dd, { focus, cancel });
350
420
  return open.length > 0;
351
421
  }
352
422
 
@@ -446,7 +516,7 @@ export function wireDropdowns(root, { signal } = {}) {
446
516
  case 'Escape':
447
517
  e.preventDefault();
448
518
  e.stopPropagation();
449
- close(dd, { focus: true });
519
+ close(dd, { focus: true, cancel: true });
450
520
  break;
451
521
  case 'Tab':
452
522
  close(dd); // the focus moves on as usual; the choice stays as it was
@@ -164,6 +164,7 @@ function build(m, { copied = false } = {}) {
164
164
  * opts.onManage() opens the MCP servers; without it there is no "Manage"
165
165
  * opts.onSettings() opens Codex settings; without it there is no "Settings"
166
166
  * opts.onAccount() opens the account menu; without it there is no Account row
167
+ * opts.bounds() the area the popover stays in ({ left, top, right, bottom }: the app frame); the window without it
167
168
  * Returns { element, open(), close({ focus }), isOpen(), sync(), destroy() }.
168
169
  */
169
170
  export function mountContextMenu(actionsEl, panelEl, opts = {}) {
@@ -215,23 +216,23 @@ export function mountContextMenu(actionsEl, panelEl, opts = {}) {
215
216
  }
216
217
 
217
218
  // Below the cog, its right edge on the cog's; above it when there is more room
218
- // there; always inside the viewport, scrolling inside when it is taller.
219
+ // there; always inside the app frame, scrolling inside when it is taller.
219
220
  function place() {
220
221
  if (!open || !pop) return;
221
222
  const r = cog.getBoundingClientRect();
222
- const vw = window.innerWidth;
223
- const vh = window.innerHeight;
224
- const width = Math.max(0, Math.min(WIDTH, vw - MARGIN * 2));
223
+ // Inside the area the panel names (the app frame), else the window
224
+ const f = opts.bounds?.() || { left: 0, top: 0, right: window.innerWidth, bottom: window.innerHeight, width: window.innerWidth };
225
+ const width = Math.max(0, Math.min(WIDTH, f.width - MARGIN * 2));
225
226
  pop.style.width = `${width}px`;
226
- const below = vh - r.bottom - GAP - MARGIN;
227
- const above = r.top - GAP - MARGIN;
227
+ const below = f.bottom - r.bottom - GAP - MARGIN;
228
+ const above = r.top - f.top - GAP - MARGIN;
228
229
  const up = below < 220 && above > below;
229
230
  pop.style.maxHeight = `${Math.max(120, up ? above : below)}px`;
230
231
  const height = pop.offsetHeight;
231
- const left = Math.max(MARGIN, Math.min(r.right - width, vw - width - MARGIN));
232
+ const left = Math.max(f.left + MARGIN, Math.min(r.right - width, f.right - width - MARGIN));
232
233
  const top = up
233
- ? Math.max(MARGIN, r.top - GAP - height)
234
- : Math.max(MARGIN, Math.min(r.bottom + GAP, vh - MARGIN - height));
234
+ ? Math.max(f.top + MARGIN, r.top - GAP - height)
235
+ : Math.max(f.top + MARGIN, Math.min(r.bottom + GAP, f.bottom - MARGIN - height));
235
236
  pop.style.left = `${Math.round(left)}px`;
236
237
  pop.style.top = `${Math.round(top)}px`;
237
238
  // Once it has its width: only now is a cut value known.
@@ -0,0 +1,16 @@
1
+ let translate = null;
2
+ export function setCodexTranslator(fn) { translate = typeof fn === 'function' ? fn : null; }
3
+
4
+ // Detached panels/tests can render before the locale bundle is ready.
5
+ export function cxpText(key, english, params) {
6
+ const path = `codexPanel.${key}`;
7
+ const value = translate?.(path, params) || path;
8
+ if (value !== path) return value;
9
+ return english.replace(/\{(\w+)\}/g, (_, name) => params?.[name] ?? `{${name}}`);
10
+ }
11
+
12
+ export function cxpSessionLabel(label) {
13
+ if (label === 'New session') return cxpText('newSession', 'New session');
14
+ if (label === 'Saved session') return cxpText('savedSession', 'Saved session');
15
+ return label;
16
+ }
@@ -236,7 +236,10 @@ export function fileChangeDiffs(item = {}) {
236
236
  if (raw && (/^(?:diff --git |--- )/m.test(raw) || changes.length <= 1 && /^@@ /m.test(raw))) return parseUnifiedDiff(raw, changes.length === 1 ? changeInfo(changes[0]) : {});
237
237
  const files = changes.slice(0, OUTPUT_LIMITS.files).flatMap(change => {
238
238
  const info = changeInfo(change);
239
- const patch = change.diff || change.patch || change.content || change.output || '';
239
+ const patch = change.diff ?? change.patch ?? change.content ?? change.output;
240
+ if ((info.status === 'added' || info.status === 'deleted') && typeof patch === 'string') {
241
+ return [wholeFileDiff(patch, info)];
242
+ }
240
243
  if (!patch) return [{ ...info, lines: [], hunks: [], added: 0, removed: 0, raw: '', unavailable: true }];
241
244
  return parseUnifiedDiff(typeof patch === 'string' ? patch : readableStructuredValue(patch), info);
242
245
  });
@@ -244,6 +247,24 @@ export function fileChangeDiffs(item = {}) {
244
247
  return files;
245
248
  }
246
249
 
250
+ function wholeFileDiff(content, info) {
251
+ const added = info.status === 'added';
252
+ const text = content.replace(/\r\n/g, '\n');
253
+ const lines = text === '' ? [] : text.split('\n');
254
+ if (text.endsWith('\n')) lines.pop();
255
+ const sign = added ? '+' : '-';
256
+ const raw = `@@ -${added ? '0,0' : `1,${lines.length}`} +${added ? `1,${lines.length}` : '0,0'} @@\n`
257
+ + lines.map(line => `${sign}${line}\n`).join('')
258
+ + (text && !text.endsWith('\n') ? '\\n' : '');
259
+ const oversized = content.length > OUTPUT_LIMITS.input || lines.length > OUTPUT_LIMITS.diffLines;
260
+ return { ...info, lines: oversized ? [] : lines.map((text, i) => ({
261
+ kind: added ? 'add' : 'delete', sign, text,
262
+ oldLine: added ? null : i + 1, newLine: added ? i + 1 : null,
263
+ })), hunks: [{ oldStart: added ? 0 : 1, newStart: added ? 1 : 0 }],
264
+ added: added ? lines.length : 0, removed: added ? 0 : lines.length,
265
+ raw, oversized };
266
+ }
267
+
247
268
  function changeInfo(change) {
248
269
  const kind = typeof change.kind === 'object' ? change.kind : { type: change.kind || change.type };
249
270
  const moved = kind.move_path || kind.movePath || change.newPath;
@@ -1,3 +1,4 @@
1
+ import { cxpText } from './cdx-copy.js';
1
2
  import { OUTPUT_LIMITS, boundedText, cleanTerminal, jsonAnswer, looksLikeStreamingJson, isPreformattedText, structuredValueModel, readableStructuredValue, parseUnifiedDiff, hasOverlongRun, outputOverflows, displayDiffPath } from './cdx-output-model.js';
2
3
 
3
4
  const sources = new WeakMap();
@@ -193,7 +194,7 @@ export function hydrateOutputControls(container) {
193
194
  watchCaps(root);
194
195
  } else if (button.dataset.outputAction === 'copy') {
195
196
  const text = sources.get(root)?.text ?? root.dataset.original ?? diffStates.get(root)?.raw ?? root.querySelector('.cxp-diff-source')?.textContent ?? '';
196
- navigator.clipboard.writeText(text).then(() => { button.textContent = 'Copied'; setTimeout(() => { button.textContent = root.classList.contains('cxp-diff-view') ? 'Copy patch' : 'Copy'; }, 1500); }).catch(() => {});
197
+ navigator.clipboard.writeText(text).then(() => { button.textContent = 'Copied'; setTimeout(() => { button.textContent = root.classList.contains('cxp-diff-view') ? cxpText('copyPatch', 'Copy patch') : 'Copy'; }, 1500); }).catch(() => {});
197
198
  }
198
199
  event.stopPropagation();
199
200
  });
@@ -310,19 +311,19 @@ export function renderDiffView(container, files, { lazy = false, cwd = container
310
311
  if (!state) {
311
312
  state = { entries: new Map(), raw: '' }; diffStates.set(container, state);
312
313
  const controls = node('div', 'cxp-output-controls');
313
- const copy = node('button', 'cxp-output-control', 'Copy patch'); copy.type = 'button'; copy.dataset.outputAction = 'copy'; controls.append(copy);
314
+ const copy = node('button', 'cxp-output-control', cxpText('copyPatch', 'Copy patch')); copy.type = 'button'; copy.dataset.outputAction = 'copy'; controls.append(copy);
314
315
  const source = node('pre', 'cxp-diff-source'); source.hidden = true;
315
316
  if (summaryEl) controls.insertBefore(summaryEl, copy);
316
317
  else controls.insertBefore(node('div', 'cxp-card-meta'), copy);
317
318
  container.replaceChildren(controls, source); hydrateOutputControls(container);
318
319
  }
319
320
  const summary = container.querySelector('.cxp-output-controls .cxp-card-meta');
320
- if (summary && !summaryEl) summary.textContent = `${files.length} file change${files.length === 1 ? '' : 's'}`;
321
+ if (summary && !summaryEl) summary.textContent = cxpText(files.length === 1 ? 'oneFileChange' : 'fileChanges', files.length === 1 ? '1 file change' : '{count} file changes', { count: files.length });
321
322
  state.raw = files.map(file => file.raw || '').join('\n');
322
323
  let filesNote = container.querySelector('.cxp-diff-files-note');
323
324
  if (!filesNote) { filesNote = node('div', 'cxp-output-note cxp-diff-files-note'); container.append(filesNote); }
324
325
  filesNote.hidden = !files.some(file => file.filesTruncated);
325
- filesNote.textContent = 'Remaining files omitted: preview limited to 100 files.';
326
+ filesNote.textContent = cxpText('filesLimit', 'Remaining files omitted: preview limited to 100 files.');
326
327
  const serialized = files.map(file => file.raw?.startsWith('diff --git ') ? file.raw : `diff --git a/${file.oldPath || file.path} b/${file.path}\n${file.status === 'renamed' ? `rename from ${file.oldPath}\nrename to ${file.path}\n` : ''}--- ${file.status === 'added' ? '/dev/null' : `a/${file.oldPath || file.path}`}\n+++ ${file.status === 'deleted' ? '/dev/null' : `b/${file.path}`}\n${file.raw || ''}`).join('\n');
327
328
  container.querySelector('.cxp-diff-source').textContent = boundedText(serialized, OUTPUT_LIMITS.input);
328
329
  const retained = new Set();
@@ -341,11 +342,11 @@ export function renderDiffView(container, files, { lazy = false, cwd = container
341
342
  const f = entry.file;
342
343
  if (!f) return;
343
344
  entry.built = true;
344
- const message = f.binary ? 'Binary file; text diff unavailable.' : f.oversized ? 'Oversized patch; rendering omitted (200,000 characters / 2,000 lines per file limit). Raw patch is copyable.' : f.unavailable || !f.lines.length && !f.hunks.length && f.status !== 'renamed' ? 'Patch not supplied or not a unified diff.' : '';
345
+ const message = f.binary ? cxpText('binary', 'Binary file; text diff unavailable.') : f.oversized ? cxpText('oversized', 'Oversized patch; rendering omitted (200,000 characters / 2,000 lines per file limit). Raw patch is copyable.') : f.unavailable || !f.lines.length && !f.hunks.length && f.status !== 'renamed' ? cxpText('patchMissing', 'Patch not supplied or not a unified diff.') : '';
345
346
  entry.note.textContent = message; entry.note.hidden = !message;
346
347
  diffRows(f, entry.rows, message ? 0 : entry.full ? OUTPUT_LIMITS.diffLines : OUTPUT_LIMITS.diffPreview);
347
348
  entry.more.hidden = !!message || f.lines.length <= OUTPUT_LIMITS.diffPreview;
348
- entry.more.textContent = entry.full ? 'Show fewer lines' : `Show full diff (${f.lines.length} lines)`;
349
+ entry.more.textContent = entry.full ? cxpText('fewerLines', 'Show fewer lines') : cxpText('fullDiff', 'Show full diff ({count} lines)', { count: f.lines.length });
349
350
  watchCap(entry.rows);
350
351
  };
351
352
  entry.build = build;
@@ -361,7 +362,7 @@ export function renderDiffView(container, files, { lazy = false, cwd = container
361
362
  entry.path.title = file.status === 'renamed' ? `${file.oldPath} → ${file.path}` : file.path;
362
363
  measureChanges(entry.path, () => fitDiffPath(entry.path, entry.file, container.dataset.cwd));
363
364
  fitDiffPath(entry.path, file, cwd);
364
- entry.status.textContent = file.status;
365
+ entry.status.textContent = cxpText(`diff_${file.status}`, file.status);
365
366
  entry.stats.textContent = `+${file.added} −${file.removed}`;
366
367
  if (entry.built || !lazy || !observer) { if (entry.details.open || entry.built) entry.build(); }
367
368
  else { pending.set(entry.details, entry.build); observer.observe(entry.details); }