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,327 @@
1
+ // ═══════════════════════════════════════════
2
+ // SynaBun Assistant — waiting for a usage limit to reset
3
+ // ═══════════════════════════════════════════
4
+ // Where a provider's usage limit stopped work (the Assistant's own turn: its
5
+ // error row; a worker: its run card), this card waits with the user: the
6
+ // mascot dozes inside a gold ring that fills toward the reset, a live
7
+ // hh:mm:ss countdown and the local reset time beside it. At zero it wakes
8
+ // ("Usage limit restored") and offers Retry now. With no reset time known it
9
+ // waits with "Reset time unknown" and offers the retry at once. While it
10
+ // blocks, a host that passes onSwitchProvider also offers "Continue with
11
+ // another provider" (the host owns the picker and what a pick does); a host
12
+ // that passes onClose always offers Close, which only dismisses the card —
13
+ // the limit itself is untouched — and Esc does the same while focus is
14
+ // inside. The clock is the shared ticker at 1 fps, only while it counts and
15
+ // only while the card is on the page; under prefers-reduced-motion the mascot
16
+ // is a still and nothing eases. Its sheet is its own (injectLimitWaitStyles),
17
+ // like the rack's.
18
+
19
+ import * as mascotLib from '../synabun-mascot.js';
20
+ import { isReducedMotion, subscribe } from '../synabun-ticker.js';
21
+ import { limitCountdown, resetMs } from './asst-limit.js';
22
+
23
+ const RING_R = 24;
24
+ const DAY_MS = 86_400_000;
25
+
26
+ function el(tag, className, text) {
27
+ const node = document.createElement(tag);
28
+ if (className) node.className = className;
29
+ if (text != null) node.textContent = text;
30
+ return node;
31
+ }
32
+
33
+ function reducedMotion() {
34
+ try { return !!isReducedMotion(); } catch { return !!globalThis.matchMedia?.('(prefers-reduced-motion: reduce)').matches; }
35
+ }
36
+
37
+ /** The reset as the user's clock reads it: "06:01", or "Fri 06:01" when it is not within the next day. */
38
+ function localTime(ms, now) {
39
+ const opts = { hour: '2-digit', minute: '2-digit' };
40
+ if (Math.abs(ms - now) >= DAY_MS || new Date(ms).toDateString() !== new Date(now).toDateString()) opts.weekday = 'short';
41
+ try { return new Date(ms).toLocaleString([], opts); } catch { return new Date(ms).toISOString().slice(11, 16); }
42
+ }
43
+
44
+ /**
45
+ * createLimitWait({ t, resetsAt, since, provider, onRetry, retryLabel, onSwitchProvider, onClose, announce(text) })
46
+ * → { el, get state(), update({ resetsAt, since }), retried(), stale(), destroy() }
47
+ * `resetsAt`: epoch ms / s or ISO, or null (unknown). `since`: when the wait began (the ring's start).
48
+ * `provider`: the name in the meta line ("Codex"). `onRetry()`: Retry now; null: no button.
49
+ * `onSwitchProvider(anchorEl)`: Continue with another provider, while blocking; null: no button.
50
+ * `onClose()`: Close (and Esc, while focus is inside); null: no button.
51
+ */
52
+ export function createLimitWait(opts = {}) {
53
+ injectLimitWaitStyles();
54
+ const t = (key, fallback, params) => {
55
+ const v = typeof opts.t === 'function' ? opts.t(key, fallback, params) : undefined;
56
+ if (v && v !== key && typeof v === 'string') return v;
57
+ return params ? String(fallback).replace(/\{(\w+)\}/g, (_, k) => (params[k] != null ? params[k] : `{${k}}`)) : fallback;
58
+ };
59
+ let resetsAt = resetMs(opts.resetsAt);
60
+ let since = Number.isFinite(opts.since) ? opts.since : Date.now();
61
+ let state = '';
62
+ let unsub = null;
63
+ let rig = null;
64
+ let pose = '';
65
+ let seenConnected = false;
66
+ let destroyed = false;
67
+
68
+ const card = el('div', 'asst-limit-wait');
69
+ card.setAttribute('role', 'group');
70
+ card.innerHTML = `
71
+ <div class="asst-limit-wait-stage" aria-hidden="true">
72
+ <svg class="asst-limit-ring" viewBox="0 0 56 56" width="56" height="56" focusable="false">
73
+ <circle class="asst-limit-ring-track" cx="28" cy="28" r="${RING_R}"></circle>
74
+ <circle class="asst-limit-ring-fill" cx="28" cy="28" r="${RING_R}" pathLength="100" stroke-dasharray="100" stroke-dashoffset="100"></circle>
75
+ </svg>
76
+ <span class="asst-limit-wait-face"></span>
77
+ </div>
78
+ <div class="asst-limit-wait-main">
79
+ <div class="asst-limit-wait-title"></div>
80
+ <div class="asst-limit-wait-clock" role="timer" aria-live="off"></div>
81
+ <div class="asst-limit-wait-meta"></div>
82
+ <div class="asst-limit-wait-actions"></div>
83
+ </div>
84
+ <span class="asst-limit-wait-live" role="status" aria-live="polite"></span>`;
85
+ const $ = (sel) => card.querySelector(sel);
86
+ const fill = $('.asst-limit-ring-fill');
87
+ const face = $('.asst-limit-wait-face');
88
+ const title = $('.asst-limit-wait-title');
89
+ const clock = $('.asst-limit-wait-clock');
90
+ const meta = $('.asst-limit-wait-meta');
91
+ const actions = $('.asst-limit-wait-actions');
92
+ const live = $('.asst-limit-wait-live');
93
+
94
+ // Continue with another provider: only while actively blocking (waiting / unknown). Opens the
95
+ // host's own provider picker (never a native select); the host decides what happens on a pick.
96
+ let switchBtn = null;
97
+ if (typeof opts.onSwitchProvider === 'function') {
98
+ switchBtn = el('button', 'asst-btn asst-btn-secondary asst-limit-wait-switch', t('assistant.limitWait.switchProvider', 'Continue with another provider'));
99
+ switchBtn.type = 'button';
100
+ switchBtn.addEventListener('click', () => { if (!destroyed) opts.onSwitchProvider(switchBtn); });
101
+ actions.appendChild(switchBtn);
102
+ }
103
+
104
+ let retryBtn = null;
105
+ if (typeof opts.onRetry === 'function') {
106
+ retryBtn = el('button', 'asst-btn asst-btn-primary asst-limit-wait-retry');
107
+ retryBtn.type = 'button';
108
+ retryBtn.addEventListener('click', () => {
109
+ if (state !== 'restored' && state !== 'unknown') return;
110
+ retried();
111
+ try { opts.onRetry(); } catch { /* the host reports its own failure */ }
112
+ });
113
+ actions.appendChild(retryBtn);
114
+ }
115
+
116
+ // Dismisses the card only; the limit itself is untouched (the host decides how it resurfaces).
117
+ let closeBtn = null;
118
+ if (typeof opts.onClose === 'function') {
119
+ closeBtn = el('button', 'asst-btn asst-btn-secondary asst-limit-wait-close', t('common.close', 'Close'));
120
+ closeBtn.type = 'button';
121
+ closeBtn.addEventListener('click', () => { if (!destroyed) opts.onClose(); });
122
+ actions.appendChild(closeBtn);
123
+ // Not a dialog (role="group"): Esc closes it only while focus is inside.
124
+ card.addEventListener('keydown', (e) => {
125
+ if (e.key !== 'Escape' || destroyed) return;
126
+ e.stopPropagation();
127
+ opts.onClose();
128
+ });
129
+ }
130
+
131
+ function setPose(next) {
132
+ if (pose === next || destroyed) return;
133
+ pose = next;
134
+ if (reducedMotion()) {
135
+ try { rig?.destroy(); } catch { /* gone */ }
136
+ rig = null;
137
+ face.innerHTML = mascotLib.mascotSvg({ width: 34, height: 17, className: 'synabun-mascot asst-still' });
138
+ try { mascotLib.paintMascot(face.firstElementChild, mascotLib.stillFrame(next)); } catch { /* the plain face */ }
139
+ return;
140
+ }
141
+ if (!rig) {
142
+ face.textContent = '';
143
+ try { rig = mascotLib.createMascot(face, { width: 34, height: 17, pose: next }); return; } catch { rig = null; }
144
+ }
145
+ try { rig?.setPose(next, next === 'success' ? { then: 'idle' } : undefined); } catch { /* keeps its pose */ }
146
+ }
147
+
148
+ function setRing(progress, indeterminate = false) {
149
+ fill.setAttribute('stroke-dashoffset', String(Math.round((1 - Math.max(0, Math.min(1, progress))) * 1000) / 10));
150
+ card.classList.toggle('is-indeterminate', indeterminate);
151
+ }
152
+
153
+ function paintMeta(now) {
154
+ const bits = [];
155
+ if (opts.provider) bits.push(t('assistant.limitWait.provider', '{provider} usage limit', { provider: opts.provider }));
156
+ if (resetsAt !== null) bits.push((state === 'restored' || state === 'retried' || (state === 'stale' && resetsAt <= now)) ? t('assistant.limitWait.resetAt', 'reset at {time}', { time: localTime(resetsAt, now) }) : t('assistant.limitWait.resetsAt', 'resets at {time}', { time: localTime(resetsAt, now) }));
157
+ meta.textContent = bits.join(' · ');
158
+ meta.hidden = !bits.length;
159
+ }
160
+
161
+ function setState(next, now = Date.now()) {
162
+ const was = state;
163
+ state = next;
164
+ card.dataset.state = next;
165
+ card.dataset.known = resetsAt === null ? '0' : '1';
166
+ card.setAttribute('aria-label', next === 'restored' || next === 'retried' ? t('assistant.limitWait.restored', 'Usage limit restored') : t('assistant.limitWait.waiting', 'Waiting for the usage limit to reset'));
167
+ title.textContent = next === 'restored' || next === 'retried'
168
+ ? t('assistant.limitWait.restored', 'Usage limit restored')
169
+ : next === 'stale' && resetsAt !== null && resetsAt <= now ? t('assistant.limitWait.restored', 'Usage limit restored')
170
+ : t('assistant.limitWait.waiting', 'Waiting for the usage limit to reset');
171
+ if (next === 'unknown') { clock.textContent = t('assistant.limitWait.unknown', 'Reset time unknown'); clock.classList.add('is-text'); clock.removeAttribute('aria-label'); }
172
+ if (next === 'restored' || next === 'retried') { clock.textContent = '00:00:00'; clock.classList.remove('is-text'); clock.removeAttribute('aria-label'); }
173
+ if (retryBtn) {
174
+ retryBtn.hidden = !(next === 'restored' || next === 'unknown' || next === 'retried');
175
+ retryBtn.disabled = next === 'retried' || next === 'stale';
176
+ const label = next === 'retried' ? t('assistant.limitWait.retried', 'Retried')
177
+ : next === 'unknown' ? (opts.retryLabel || t('assistant.limitWait.tryAgain', 'Try again'))
178
+ : (opts.retryLabel || t('assistant.limitWait.retry', 'Retry now'));
179
+ retryBtn.textContent = label;
180
+ }
181
+ if (switchBtn) switchBtn.hidden = !(next === 'waiting' || next === 'unknown');
182
+ if (next === 'waiting') { setPose('sleep'); setRing(limitCountdown({ resetsAt, since }, now).progress || 0); }
183
+ else if (next === 'unknown') { setPose('wait'); setRing(0.18, true); }
184
+ else if (next === 'restored') { setPose(was === 'waiting' ? 'success' : 'idle'); setRing(1); }
185
+ else { setPose('idle'); setRing(resetsAt !== null && resetsAt <= now ? 1 : 0); }
186
+ if (next === 'restored' && was === 'waiting') live.textContent = t('assistant.limitWait.announceRestored', 'The usage limit has reset.');
187
+ paintMeta(now);
188
+ syncTicker();
189
+ }
190
+
191
+ function tick(now = Date.now()) {
192
+ if (destroyed) return;
193
+ if (card.isConnected) seenConnected = true;
194
+ else if (seenConnected) { destroy(); return; }
195
+ if (state !== 'waiting') { syncTicker(); return; }
196
+ const view = limitCountdown({ resetsAt, since }, now);
197
+ if (view.done) { setState('restored', now); return; }
198
+ if (clock.textContent !== view.clock) {
199
+ clock.textContent = view.clock;
200
+ clock.classList.remove('is-text');
201
+ clock.setAttribute('aria-label', t('assistant.limitWait.left', '{clock} left', { clock: view.clock }));
202
+ }
203
+ setRing(view.progress);
204
+ }
205
+
206
+ function syncTicker() {
207
+ const want = state === 'waiting' && !destroyed;
208
+ if (want && !unsub) { try { unsub = subscribe((now) => tick(typeof now === 'number' && now > 1e12 ? now : Date.now()), { fps: 1 }); } catch { unsub = null; } }
209
+ else if (!want && unsub) { try { unsub(); } catch { /* gone */ } unsub = null; }
210
+ }
211
+
212
+ function start(now = Date.now()) {
213
+ if (resetsAt === null) setState('unknown', now);
214
+ else if (resetsAt <= now) setState('restored', now);
215
+ else { setState('waiting', now); tick(now); }
216
+ }
217
+
218
+ function retried() {
219
+ if (state === 'retried' || state === 'stale') return;
220
+ setState('retried');
221
+ }
222
+ function stale() {
223
+ if (state === 'retried' || state === 'stale') return;
224
+ setState('stale');
225
+ if (state === 'stale') {
226
+ clock.classList.add('is-text');
227
+ clock.removeAttribute('aria-label');
228
+ clock.textContent = resetsAt !== null ? localTime(resetsAt, Date.now()) : t('assistant.limitWait.unknown', 'Reset time unknown');
229
+ }
230
+ }
231
+ function destroy() {
232
+ if (destroyed) return;
233
+ destroyed = true;
234
+ if (unsub) { try { unsub(); } catch { /* gone */ } unsub = null; }
235
+ try { rig?.destroy(); } catch { /* gone */ }
236
+ rig = null;
237
+ }
238
+
239
+ start();
240
+ return {
241
+ el: card,
242
+ get state() { return state; },
243
+ get resetsAt() { return resetsAt; },
244
+ /** A reset time learned later (a structured report): the card counts to it. Ignored once it is settled. */
245
+ update({ resetsAt: next, since: from } = {}) {
246
+ if (destroyed || state === 'retried' || state === 'stale') return;
247
+ const ms = resetMs(next);
248
+ if (Number.isFinite(from)) since = from;
249
+ if (ms === null || ms === resetsAt) return;
250
+ resetsAt = ms;
251
+ start();
252
+ },
253
+ retried,
254
+ stale,
255
+ destroy,
256
+ };
257
+ }
258
+
259
+ const CSS = `
260
+ .asst-limit-wait {
261
+ --lw-tone: var(--asst-brand);
262
+ display: grid;
263
+ grid-template-columns: 56px minmax(0, 1fr);
264
+ align-items: center;
265
+ gap: 12px;
266
+ min-width: 0;
267
+ padding: 12px 14px;
268
+ border-radius: var(--asst-r-card);
269
+ background: linear-gradient(135deg, color-mix(in srgb, var(--asst-brand) 8%, transparent), var(--asst-fill-1) 62%);
270
+ box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--asst-brand) 26%, transparent), var(--asst-card-hi);
271
+ color: var(--asst-text);
272
+ font-size: 13px;
273
+ transition: box-shadow var(--asst-dur-base) var(--asst-ease-standard), background-color var(--asst-dur-base) var(--asst-ease-standard);
274
+ }
275
+ .asst-limit-wait:is([data-state="stale"], [data-state="retried"]) {
276
+ --lw-tone: var(--asst-text-3);
277
+ background: transparent;
278
+ box-shadow: inset 0 0 0 1px var(--asst-line);
279
+ }
280
+ .asst-limit-wait-stage { position: relative; width: 56px; height: 56px; }
281
+ .asst-limit-ring { display: block; width: 56px; height: 56px; transform: rotate(-90deg); overflow: visible; }
282
+ .asst-limit-ring-track { fill: none; stroke: var(--asst-line-2); stroke-width: 3; }
283
+ .asst-limit-ring-fill {
284
+ fill: none;
285
+ stroke: var(--lw-tone);
286
+ stroke-width: 3;
287
+ stroke-linecap: round;
288
+ transform-box: fill-box;
289
+ transform-origin: center;
290
+ transition: stroke-dashoffset 1s linear, stroke var(--asst-dur-base) var(--asst-ease-standard);
291
+ }
292
+ .asst-limit-wait[data-state="restored"] .asst-limit-ring-fill { filter: drop-shadow(0 0 3px color-mix(in srgb, var(--asst-brand) 55%, transparent)); }
293
+ .asst-limit-wait.is-indeterminate .asst-limit-ring-fill { stroke-dasharray: 18 82; animation: asst-limit-orbit 2.6s linear infinite; }
294
+ @keyframes asst-limit-orbit { to { transform: rotate(360deg); } }
295
+ .asst-limit-wait-face { position: absolute; inset: 0; display: grid; place-items: center; color: var(--lw-tone); line-height: 0; }
296
+ .asst-limit-wait-face svg { display: block; width: 34px; height: 17px; }
297
+ .asst-limit-wait-main { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
298
+ .asst-limit-wait-title { color: var(--asst-text-2); font-size: 12px; font-weight: 500; }
299
+ .asst-limit-wait[data-state="restored"] .asst-limit-wait-title { color: var(--asst-brand); }
300
+ .asst-limit-wait-clock { color: var(--asst-text); font-family: var(--asst-mono); font-size: 20px; font-weight: 500; line-height: 1.2; font-variant-numeric: tabular-nums; letter-spacing: .02em; }
301
+ .asst-limit-wait-clock.is-text { font-family: inherit; font-size: 13px; color: var(--asst-text-2); letter-spacing: 0; }
302
+ .asst-limit-wait-meta { color: var(--asst-text-3); font-size: 11.5px; overflow-wrap: anywhere; }
303
+ .asst-limit-wait-actions { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 6px; }
304
+ .asst-limit-wait-actions:not(:has(> :not([hidden]))) { display: none; }
305
+ .asst-limit-wait-live { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
306
+ .asst-limit-row .asst-msg-content { width: 100%; }
307
+ .asst-run .asst-limit-wait { grid-column: 2 / span 2; margin-top: 8px; padding: 10px 12px; }
308
+ @container asst (max-width: 320px) {
309
+ .asst-limit-wait { grid-template-columns: 44px minmax(0, 1fr); gap: 10px; padding: 10px; }
310
+ .asst-limit-wait-stage, .asst-limit-ring { width: 44px; height: 44px; }
311
+ .asst-limit-wait-face svg { width: 28px; height: 14px; }
312
+ .asst-limit-wait-clock { font-size: 17px; }
313
+ }
314
+ body.ui-interacting .asst-limit-ring-fill { animation-play-state: paused !important; }
315
+ @media (prefers-reduced-motion: reduce) {
316
+ .asst-limit-wait, .asst-limit-wait * { animation: none !important; transition: none !important; }
317
+ }
318
+ `;
319
+
320
+ /** The card's sheet, once per page. */
321
+ export function injectLimitWaitStyles() {
322
+ if (typeof document === 'undefined' || document.getElementById('asst-limit-wait-styles')) return;
323
+ const style = document.createElement('style');
324
+ style.id = 'asst-limit-wait-styles';
325
+ style.textContent = CSS;
326
+ document.head.appendChild(style);
327
+ }
@@ -0,0 +1,168 @@
1
+ // ═══════════════════════════════════════════
2
+ // SynaBun Assistant — usage limits: is it one, and when does it reset?
3
+ // ═══════════════════════════════════════════
4
+ // A provider that stops a turn on its usage limit says so in words ("You've
5
+ // hit your session limit · resets 5:10am (America/Sao_Paulo)", "try again in
6
+ // 2 hours 3 minutes", "blocked until about 06:01 UTC"). limitFromText reads
7
+ // that message: whether it is a usage limit, and the reset time it names
8
+ // (epoch ms, or null when it names none). The time is read against the moment
9
+ // the message was written (`now`), so a reload draws the same countdown. A
10
+ // structured reset time (the Claude SDK's rate_limit_info, an account's usage
11
+ // window) always wins over the words; this is what is left when there is
12
+ // none. limitCountdown is the waiting card's clock. No imports and no DOM:
13
+ // the dispatcher (lib/assistant-dispatch.js) reads a worker's failure with it.
14
+
15
+ const LIMIT_RE = /\b(?:usage limit|session limit|weekly limit|daily limit|hourly limit|5-hour limit|rate[- ]limit(?:ed)?|limit reached|limit exceeded|hit (?:your|the) (?:\w+ ){0,2}limit|out of (?:usage|credits|quota)|quota (?:exceeded|reached)|exceeded (?:your |the )?(?:current )?quota|too many requests)\b/i;
16
+ const DAY_MS = 86_400_000;
17
+ const MONTHS = ['jan', 'feb', 'mar', 'apr', 'may', 'jun', 'jul', 'aug', 'sep', 'oct', 'nov', 'dec'];
18
+ const UNIT_MS = { d: DAY_MS, h: 3_600_000, m: 60_000, s: 1000 };
19
+
20
+ /** The offset of `timeZone` from UTC at the instant `ms` (wall time − UTC), in ms; null for a zone this runtime does not know. */
21
+ function zoneOffset(ms, timeZone) {
22
+ try {
23
+ const parts = new Intl.DateTimeFormat('en-US', {
24
+ timeZone, hourCycle: 'h23', year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', second: '2-digit',
25
+ }).formatToParts(new Date(ms));
26
+ const get = (type) => Number(parts.find((p) => p.type === type)?.value);
27
+ const wall = Date.UTC(get('year'), get('month') - 1, get('day'), get('hour') % 24, get('minute'), get('second'));
28
+ return Number.isFinite(wall) ? wall - (Math.floor(ms / 1000) * 1000) : null;
29
+ } catch { return null; }
30
+ }
31
+
32
+ /** A wall-clock time in `zone` ('UTC', an IANA name, or null for this machine's own) → epoch ms. */
33
+ export function wallTimeToEpoch({ year, month, day, hour, minute = 0 }, zone = null) {
34
+ if (zone === 'UTC') return Date.UTC(year, month, day, hour, minute);
35
+ if (zone) {
36
+ const guess = Date.UTC(year, month, day, hour, minute);
37
+ const first = zoneOffset(guess, zone);
38
+ if (first !== null) {
39
+ const at = guess - first;
40
+ const second = zoneOffset(at, zone);
41
+ return second !== null && second !== first ? guess - second : at;
42
+ }
43
+ }
44
+ return new Date(year, month, day, hour, minute).getTime();
45
+ }
46
+
47
+ /** Year / month / day of the instant `ms` as `zone` sees it. */
48
+ function dateIn(ms, zone) {
49
+ if (zone === 'UTC') { const d = new Date(ms); return { year: d.getUTCFullYear(), month: d.getUTCMonth(), day: d.getUTCDate() }; }
50
+ if (zone) {
51
+ const offset = zoneOffset(ms, zone);
52
+ if (offset !== null) { const d = new Date(ms + offset); return { year: d.getUTCFullYear(), month: d.getUTCMonth(), day: d.getUTCDate() }; }
53
+ }
54
+ const d = new Date(ms);
55
+ return { year: d.getFullYear(), month: d.getMonth(), day: d.getDate() };
56
+ }
57
+
58
+ function isZone(name) {
59
+ if (!name) return false;
60
+ if (/^(?:utc|gmt|z)$/i.test(name)) return true;
61
+ try { new Intl.DateTimeFormat('en-US', { timeZone: name }); return true; } catch { return false; }
62
+ }
63
+
64
+ /** "in 2 hours 3 minutes" / "in 4h 12m" → ms, or null. */
65
+ function relativeMs(text) {
66
+ const m = /\b(?:in|after|within)\s+(?:about\s+|around\s+|~\s*)?((?:\d+(?:\.\d+)?\s*(?:days?|d|hours?|hrs?|hr|h|minutes?|mins?|min|m|seconds?|secs?|sec|s)\b[\s,]*(?:and\s+)?)+)/i.exec(text);
67
+ if (!m) return null;
68
+ let total = 0;
69
+ for (const part of m[1].matchAll(/(\d+(?:\.\d+)?)\s*(days?|d|hours?|hrs?|hr|h|minutes?|mins?|min|m|seconds?|secs?|sec|s)\b/gi)) {
70
+ const unit = part[2].toLowerCase();
71
+ const key = unit.startsWith('d') ? 'd' : unit.startsWith('h') ? 'h' : unit.startsWith('m') ? 'm' : 's';
72
+ total += Number(part[1]) * UNIT_MS[key];
73
+ }
74
+ return total > 0 ? total : null;
75
+ }
76
+
77
+ /**
78
+ * A clock time the message names after "resets", "until", "try again at"…: the next such moment
79
+ * from `now` (today, or tomorrow when it has passed), in the zone it names ("UTC", "(America/Sao_Paulo)")
80
+ * or this machine's own. A month and day before it ("Oct 23, 5:52 PM") pin the date.
81
+ */
82
+ function clockTime(text, now) {
83
+ const re = /\b(?:resets?|reset at|until|till|try again at|again at|available (?:again )?at|back at)\s+(?:on\s+)?(?:at\s+)?(?:about\s+|around\s+|approximately\s+|~\s*)?(?:([A-Za-z]{3,9})\.?\s+(\d{1,2})(?:st|nd|rd|th)?,?\s+(?:(\d{4}),?\s+)?(?:at\s+)?)?(\d{1,2})(?::(\d{2}))?\s*([ap]\.?m\.?)?(?:\s*(?:\(\s*)?(UTC|GMT|Z|[A-Za-z]+\/[A-Za-z_]+(?:\/[A-Za-z_]+)?)(?:\s*\))?)?/gi;
84
+ for (const m of text.matchAll(re)) {
85
+ const [, monthName, dayText, yearText, hourText, minuteText, meridiem, zoneText] = m;
86
+ let hour = Number(hourText);
87
+ const minute = minuteText ? Number(minuteText) : 0;
88
+ // "until 3 runs finish" is no time: a clock needs minutes or am / pm.
89
+ if (!minuteText && !meridiem) continue;
90
+ if (minute > 59 || hour > 23 || (meridiem && (hour < 1 || hour > 12))) continue;
91
+ if (meridiem) { const pm = /^p/i.test(meridiem); hour = (hour % 12) + (pm ? 12 : 0); }
92
+ const zone = zoneText && isZone(zoneText) ? (/^(?:utc|gmt|z)$/i.test(zoneText) ? 'UTC' : zoneText) : null;
93
+ const monthIndex = monthName ? MONTHS.indexOf(monthName.slice(0, 3).toLowerCase()) : -1;
94
+ if (monthName && monthIndex >= 0) {
95
+ const today = dateIn(now, zone);
96
+ let year = yearText ? Number(yearText) : today.year;
97
+ let at = wallTimeToEpoch({ year, month: monthIndex, day: Number(dayText), hour, minute }, zone);
98
+ if (!yearText && at < now - DAY_MS) { year += 1; at = wallTimeToEpoch({ year, month: monthIndex, day: Number(dayText), hour, minute }, zone); }
99
+ return at;
100
+ }
101
+ if (monthName) continue; // a word that is no month ("resets daily 5pm"): try the next match
102
+ const today = dateIn(now, zone);
103
+ let at = wallTimeToEpoch({ ...today, hour, minute }, zone);
104
+ if (at < now - 60_000) {
105
+ const next = new Date(Date.UTC(today.year, today.month, today.day + 1));
106
+ at = wallTimeToEpoch({ year: next.getUTCFullYear(), month: next.getUTCMonth(), day: next.getUTCDate(), hour, minute }, zone);
107
+ }
108
+ return at;
109
+ }
110
+ return null;
111
+ }
112
+
113
+ /** An instant written out: epoch seconds or ms after "limit reached|" (the Claude CLI's old form), or an ISO timestamp. */
114
+ function absoluteTime(text) {
115
+ const epoch = /limit reached\|(\d{10,13})\b/i.exec(text);
116
+ if (epoch) { const n = Number(epoch[1]); return n < 1e12 ? n * 1000 : n; }
117
+ const isoMatch = /\b(\d{4}-\d{2}-\d{2}[T ]\d{2}:\d{2}(?::\d{2}(?:\.\d+)?)?(?:Z|[+-]\d{2}:?\d{2})?)\b/.exec(text);
118
+ if (isoMatch) { const ms = Date.parse(isoMatch[1].replace(' ', 'T')); if (Number.isFinite(ms)) return ms; }
119
+ return null;
120
+ }
121
+
122
+ /**
123
+ * Is `text` a provider's usage-limit message? → { resetsAt: epoch ms | null } or null.
124
+ * `now`: when the message was written (its reset time is the next one from then).
125
+ * A reset time more than a day before `now` or 31 days after it is none.
126
+ */
127
+ export function limitFromText(text, { now = Date.now() } = {}) {
128
+ const src = String(text ?? '').replace(/\s+/g, ' ').trim();
129
+ if (!src || src.length > 4000 || !LIMIT_RE.test(src)) return null;
130
+ let resetsAt = absoluteTime(src);
131
+ if (resetsAt === null) { const rel = relativeMs(src); if (rel !== null) resetsAt = now + rel; }
132
+ if (resetsAt === null) resetsAt = clockTime(src, now);
133
+ if (resetsAt !== null && (!Number.isFinite(resetsAt) || resetsAt < now - DAY_MS || resetsAt > now + 31 * DAY_MS)) resetsAt = null;
134
+ return { resetsAt };
135
+ }
136
+
137
+ /** A reset time in any form the panel meets (epoch s or ms, ISO text) → epoch ms, or null. Seconds below 1e11 (until the year 5138). */
138
+ export function resetMs(value) {
139
+ if (value === null || value === undefined || value === '') return null;
140
+ if (typeof value === 'number') return Number.isFinite(value) && value > 0 ? (value < 1e11 ? value * 1000 : value) : null;
141
+ const n = Number(value);
142
+ if (Number.isFinite(n) && String(value).trim() !== '') return resetMs(n);
143
+ const ms = Date.parse(String(value));
144
+ return Number.isFinite(ms) ? ms : null;
145
+ }
146
+
147
+ /** ms → "hh:mm:ss" (hours past 99 keep growing), counted up so the last second shows 00:00:01. */
148
+ export function fmtCountdown(ms) {
149
+ const total = Math.max(0, Math.ceil((Number(ms) || 0) / 1000));
150
+ const h = Math.floor(total / 3600);
151
+ const m = Math.floor((total % 3600) / 60);
152
+ const s = total % 60;
153
+ return `${String(h).padStart(2, '0')}:${String(m).padStart(2, '0')}:${String(s).padStart(2, '0')}`;
154
+ }
155
+
156
+ /**
157
+ * The waiting card's clock at `now`: { known, done, remainingMs, clock, progress }.
158
+ * `since`: when the wait began (the ring fills from there to `resetsAt`). No reset time: known false.
159
+ */
160
+ export function limitCountdown({ resetsAt = null, since = null } = {}, now = Date.now()) {
161
+ const at = resetMs(resetsAt);
162
+ if (at === null) return { known: false, done: false, remainingMs: null, clock: '', progress: null };
163
+ const remainingMs = Math.max(0, at - now);
164
+ const start = Number.isFinite(since) && since < at ? since : Math.min(now, at);
165
+ const span = Math.max(1, at - start);
166
+ const progress = remainingMs <= 0 ? 1 : Math.min(1, Math.max(0, 1 - remainingMs / span));
167
+ return { known: true, done: remainingMs <= 0, remainingMs, clock: fmtCountdown(remainingMs), progress };
168
+ }
@@ -3,12 +3,15 @@
3
3
  // ═══════════════════════════════════════════
4
4
  // The renderer (asst-render.js) owns the rigs and says which one is on screen:
5
5
  // the hero (the empty state's character; a send hands it over to the
6
- // "Thinking" row's at once), the live rack's stage or the "Thinking" row's.
6
+ // "Thinking" row's at once), the face of the newest open card the mascot
7
+ // presents (a follow-ups or question card: asst-ask.js, kind 'card'), the
8
+ // live rack's stage or the "Thinking" row's.
7
9
  // This module dresses that one in the panel's own state, above the pose the
8
10
  // renderer gives it:
9
11
  //
10
12
  // offline the socket dropped (the Reconnecting banner); one startle on reconnect
11
- // blocked a card waits on you: eyes on it, two pulses, then still
13
+ // blocked a card waits on you: eyes on it, two pulses, then still (never on a
14
+ // question card's own face, `waits`: that card asks in its own pose)
12
15
  // success, error once, when a turn that did real work ends (then idle)
13
16
  // watch while you type: eyes on the caret column, look() at most
14
17
  // every 120 ms; back 1.5 s after the last key, at once on send
@@ -17,6 +20,12 @@
17
20
  // sleep the hero only, after 90 s with no input while nothing runs;
18
21
  // the rig wakes itself on a pointer move or a key
19
22
  //
23
+ // present(true) hands the character to a presenter (the Workspace tour,
24
+ // public/shared/tour/): while it lasts the director dresses nothing, never
25
+ // puts the hero to sleep, does not watch the caret and ignores chip
26
+ // attention; present(false) gives it back and the director dresses it again
27
+ // for whatever is true by then.
28
+ //
20
29
  // Anything else is the renderer's pose: the hero idles, the "Thinking" row's
21
30
  // character thinks, the stage acts out its call. A pose the director sets is
22
31
  // held: the renderer leaves it alone until the director lets go and asks it
@@ -67,8 +76,8 @@ export function lookToward(box, point, { horizontal = false } = {}) {
67
76
 
68
77
  /**
69
78
  * createMascotDirector({ getTarget, caret, env })
70
- * getTarget() → the renderer's visible mascot { kind: 'hero'|'stage'|'working',
71
- * rig, el, parked?(), hold(on), restore() } or null; caret() → the caret's
79
+ * getTarget() → the renderer's visible mascot { kind: 'hero'|'card'|'stage'|'working',
80
+ * rig, el, parked?(), waits?, hold(on), restore() } or null; caret() → the caret's
72
81
  * client point or null.
73
82
  * Control API: attend(owner, { pose, rectOf, options, open }), leave(owner),
74
83
  * controlCommitted({ pose, options, reaction }); type(present, pointOf?) also
@@ -103,13 +112,15 @@ export function createMascotDirector({ getTarget = () => null, caret = () => nul
103
112
  let sleepTimer = 0;
104
113
  let lastInput = now();
105
114
  let stopInput = null;
115
+ let presenting = false; // a presenter (the tour) owns the character's pose
106
116
 
107
117
  const safe = (fn) => { try { return fn(); } catch { return undefined; } };
108
118
  const boxOf = (t) => safe(() => t.el?.getBoundingClientRect?.()) || null;
109
119
 
110
120
  function desired(t) {
111
121
  if (offline) return { pose: 'offline' };
112
- if (blocked) {
122
+ // A question card's own face is the card that waits: it asks in its own pose (asst-ask.js), never "blocked" at itself.
123
+ if (blocked && !t.waits) {
113
124
  // The card's head (its title and command sit top left), not its centre.
114
125
  const card = safe(blocked);
115
126
  const point = card && card.width ? { x: card.left + Math.min(card.width * 0.3, 160), y: card.top + Math.min(card.height / 2, 28) } : null;
@@ -133,7 +144,7 @@ export function createMascotDirector({ getTarget = () => null, caret = () => nul
133
144
  if (destroyed) return;
134
145
  const t = safe(getTarget) || null;
135
146
  if (t !== target) {
136
- if (target && holding) safe(() => target.hold(false));
147
+ if (target && holding && !presenting) safe(() => target.hold(false));
137
148
  target = t;
138
149
  holding = false;
139
150
  shown = '';
@@ -141,7 +152,7 @@ export function createMascotDirector({ getTarget = () => null, caret = () => nul
141
152
  listenInput();
142
153
  armSleep();
143
154
  }
144
- if (!t) return;
155
+ if (!t || presenting) return; // a presenter holds the pose: nothing is dressed until it lets go
145
156
  const want = desired(t);
146
157
  if (!want) {
147
158
  if (!holding) return;
@@ -171,7 +182,7 @@ export function createMascotDirector({ getTarget = () => null, caret = () => nul
171
182
 
172
183
  function canSleep() {
173
184
  const t = target;
174
- return !destroyed && !!t && t.kind === 'hero' && !safe(() => t.parked?.()) && !turnActive && !typing && !attention?.open && !blocked && !offline && !oneShot;
185
+ return !destroyed && !presenting && !!t && t.kind === 'hero' && !safe(() => t.parked?.()) && !turnActive && !typing && !attention?.open && !blocked && !offline && !oneShot;
175
186
  }
176
187
 
177
188
  function armSleep() {
@@ -202,7 +213,7 @@ export function createMascotDirector({ getTarget = () => null, caret = () => nul
202
213
  function type(present = true, pointOf = null) {
203
214
  if (destroyed) return;
204
215
  noteInput();
205
- if (!present) { stopTyping(); return; }
216
+ if (!present || presenting) { stopTyping(); return; }
206
217
  if (oneShot?.control) dropOneShot(); // a new edit resumes watching after an acknowledgement
207
218
  const first = !typing;
208
219
  typing = true;
@@ -252,7 +263,7 @@ export function createMascotDirector({ getTarget = () => null, caret = () => nul
252
263
  stopTyping,
253
264
  /** Chip previews relax after a short dwell; only open menus hold attention and block sleep. */
254
265
  attend(owner, spec) {
255
- if (destroyed || (attention?.open && attention.owner !== owner && !spec.open)) return;
266
+ if (destroyed || presenting || (attention?.open && attention.owner !== owner && !spec.open)) return;
256
267
  noteInput();
257
268
  clearAttention();
258
269
  attention = { owner, ...spec };
@@ -272,7 +283,7 @@ export function createMascotDirector({ getTarget = () => null, caret = () => nul
272
283
  },
273
284
  /** Only acknowledged user commits call this; loads, live updates and keystrokes do not. */
274
285
  controlCommitted({ pose, options = {}, reaction = 'nod' }) {
275
- if (destroyed) return;
286
+ if (destroyed || presenting) return;
276
287
  const t = safe(getTarget);
277
288
  if (!t || t.kind !== 'hero' || safe(() => t.parked?.()) || turnActive || offline || blocked) return;
278
289
  stopTyping();
@@ -319,8 +330,34 @@ export function createMascotDirector({ getTarget = () => null, caret = () => nul
319
330
  retarget() { apply(); },
320
331
  /** The user is here (focus, a click): a sleeping hero wakes, the quiet starts over. */
321
332
  touch() { if (!destroyed) noteInput(); },
333
+ /**
334
+ * A presenter takes the character (true) or gives it back (false). While on:
335
+ * no pose, sleep, caret watch, chip attention or acknowledgement from here;
336
+ * the hold this director had is handed over as it is (the presenter holds it).
337
+ */
338
+ present(on) {
339
+ const next = !!on;
340
+ if (destroyed || next === presenting) return;
341
+ presenting = next;
342
+ if (presenting) {
343
+ clearAttention();
344
+ dropOneShot();
345
+ if (typingTimer) { cancel(typingTimer); typingTimer = 0; }
346
+ if (lookTimer) { cancel(lookTimer); lookTimer = 0; }
347
+ typing = false;
348
+ typingPoint = null;
349
+ sleeping = false;
350
+ if (sleepTimer) { cancel(sleepTimer); sleepTimer = 0; }
351
+ holding = false;
352
+ shown = '';
353
+ return;
354
+ }
355
+ lastInput = now();
356
+ apply();
357
+ armSleep();
358
+ },
322
359
  /** For tests and devtools: what the director holds right now. */
323
- state: () => ({ target: target?.kind || null, pose: shown.split('|')[0] || null, typing, attention: attention?.pose || null, menuOpen: !!attention?.open, blocked: !!blocked, offline, sleeping, oneShot: oneShot?.pose || null }),
360
+ state: () => ({ target: target?.kind || null, presenting, pose: shown.split('|')[0] || null, typing, attention: attention?.pose || null, menuOpen: !!attention?.open, blocked: !!blocked, offline, sleeping, oneShot: oneShot?.pose || null }),
324
361
  destroy() {
325
362
  if (destroyed) return;
326
363
  if (target && holding) safe(() => target.hold(false));