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
@@ -1,3 +1,5 @@
1
+ import { t } from './i18n.js';
2
+ import { answerWorkerQuestion, syncWorkerQuestionQueue } from './worker-questions.js';
1
3
  import { providerAuthSnapshot } from './ui-provider-auth.js';
2
4
  import { mountProviderTerminalAuth, providerApiKeyRequest, startProviderTerminalLogin } from './ui-provider-terminal-auth.js';
3
5
  import { accountName, accountSwitchPlan, accountText, tabAccountId, serverAccountId } from './account-view.js';
@@ -23,7 +25,7 @@ import { createStickyScrollController } from './ui-scroll-follow.js';
23
25
  import { requestGeneratedSessionTitle } from './session-title.js';
24
26
  import { wireFileLinks } from './ui-file-links.js';
25
27
  import { WORKER_END_CLASS, backToAssistant, buildWorkerEndCard, endWorkerRunOnClose, forgetWorkerEnd, trackWorkerEnd, updateWorkerEndBusy } from './ui-worker-end.js';
26
- import { isDispatchedRun, isEndOfRunAbort } from './worker-end.js';
28
+ import { isDispatchedRun, isEndOfRunAbort, workerEndView } from './worker-end.js';
27
29
  import { runAwaitsAnswer } from './sidepanel-session-status.js';
28
30
  // ── cp/ feature modules (SDK-engine CLI parity: diffs, agents, bash, perms, statusline) ──
29
31
  import { setCpCtx } from './cp/cp-ctx.js';
@@ -52,7 +54,7 @@ import { sanitizeStoredHtml } from './assistant/asst-sanitize.js';
52
54
  import { escapeHtml, classToken } from './cp/cp-markdown.js';
53
55
  import { rehydrateStoredTranscript } from './cp/cp-rehydrate.js';
54
56
  import { compactHolds, compactQueue, isCompactCommand, newConversation, nextQueued, ownsQueued, queuedRefusal, restoredQueue, QUEUED_REFUSAL_LINES } from './cp/cp-compose.js';
55
- import { scrubSavedTab, historyProjectOf, shownUuids, shownTail, snapshotVerdict, withStartConfig, historyOutcome, transcriptOwner, historyNotFoundText } from './cp/cp-restore.js';
57
+ import { scrubSavedTab, historyProjectOf, shownUuids, shownTail, snapshotVerdict, withStartConfig, historyOutcome, transcriptOwner, historyNotFoundText, splitHistoryTail, replayTodosFromMessages, markHistoryRows, earlierHistoryRows, HISTORY_ROW as HISTORY_ROW_KEY, liveRows, queueHistoryLoad, historyGate, renderHistoryRowsInto, holdPendingResult, takePendingResult } from './cp/cp-restore.js';
56
58
  import { hasCapability, slashCommandRoute, bypassesPromptBuffer, idleEndsTurn, describeResult, describeTurnFooter, localCommandOutput, splitAssistantBlocks, normalizeSlashCommands, matchSlashCommands, listSlashCommands, slashMenuMaxHeight, splitCommandsByCapability, readInit, statusRows } from './cp/cp-events.js';
57
59
  import {
58
60
  renderSdkEvent, noteUnhandledEvent, applyEngineHello, renderCompactBoundary, markDeniedFromResult, endTurnRows,
@@ -282,7 +284,7 @@ const _snapshotsReady = fetchBlobNamespace('claude-snapshots')
282
284
  const EFFORT_LEVELS = ['off', 'low', 'medium', 'high', 'xhigh', 'max'];
283
285
  const EFFORT_LABELS = { off: 'Think', low: 'lo', medium: 'med', high: 'hi', xhigh: 'xhi', max: 'max' };
284
286
  const EFFORT_TITLES = {
285
- off: 'Effort: model default (thinking not shown; /session can turn thinking off)',
287
+ off: 'Thinking off',
286
288
  low: 'Thinking: low',
287
289
  medium: 'Thinking: medium',
288
290
  high: 'Thinking: high',
@@ -483,6 +485,10 @@ function injectStyles() {
483
485
  opacity: 1;
484
486
  overflow: visible;
485
487
  }
488
+ /* Windows: the shell is 92% opaque, so the blur behind it barely shows, yet Chromium recomputes it on every repaint
489
+ inside the panel (a transcript rendering, a stream). There it goes; the colour stays. */
490
+ html.platform-win32 .claude-panel,
491
+ html.desktop-titlebar--win32 .claude-panel { backdrop-filter: none; -webkit-backdrop-filter: none; }
486
492
  /* Raise panel above floating terminal windows while a dropdown is open
487
493
  so project/branch/recall menus aren't clipped behind them. */
488
494
  .claude-panel:has(.cp-dropdown.open) { z-index: 99999; }
@@ -1768,8 +1774,8 @@ function injectStyles() {
1768
1774
  pointer-events: auto;
1769
1775
  width: 0; opacity: 0; overflow: hidden;
1770
1776
  background: rgba(18, 18, 20, 0.92);
1771
- backdrop-filter: blur(60px) saturate(1.5);
1772
- -webkit-backdrop-filter: blur(60px) saturate(1.5);
1777
+ backdrop-filter: none;
1778
+ -webkit-backdrop-filter: none;
1773
1779
  border: 0.5px solid rgba(255,255,255,0.06);
1774
1780
  border-radius: 14px;
1775
1781
  box-shadow:
@@ -1782,7 +1788,19 @@ function injectStyles() {
1782
1788
  transition: width 0.22s cubic-bezier(0.2,0,0.2,1), opacity 0.18s;
1783
1789
  max-height: 100%;
1784
1790
  }
1785
- .cp-todo-dock.is-open .cp-todo-drawer { width: 260px; opacity: 1; }
1791
+ .cp-todo-dock.is-open .cp-todo-drawer {
1792
+ width: 260px; opacity: 1;
1793
+ backdrop-filter: blur(60px) saturate(1.5);
1794
+ -webkit-backdrop-filter: blur(60px) saturate(1.5);
1795
+ }
1796
+ html.platform-win32 .cp-todo-tag,
1797
+ html.desktop-titlebar--win32 .cp-todo-tag,
1798
+ html.platform-win32 .cp-todo-dock.is-open .cp-todo-drawer,
1799
+ html.desktop-titlebar--win32 .cp-todo-dock.is-open .cp-todo-drawer {
1800
+ background: rgba(18, 18, 20, 0.96);
1801
+ backdrop-filter: none;
1802
+ -webkit-backdrop-filter: none;
1803
+ }
1786
1804
  .cp-todo-dock.is-open .cp-todo-tag { display: none; }
1787
1805
 
1788
1806
  .cp-todo-drawer .cp-todo-head {
@@ -2336,6 +2354,19 @@ function injectStyles() {
2336
2354
  rgba(212,162,127,0.0) 100%
2337
2355
  );
2338
2356
  }
2357
+ html.platform-win32 .cp-session-pill.cp-pill-running,
2358
+ html.desktop-titlebar--win32 .cp-session-pill.cp-pill-running {
2359
+ border-color: rgba(212, 162, 127, 0.42);
2360
+ }
2361
+ html.platform-win32 .cp-session-pill.cp-pill-running:hover,
2362
+ html.desktop-titlebar--win32 .cp-session-pill.cp-pill-running:hover {
2363
+ border-color: rgba(212, 162, 127, 0.62);
2364
+ }
2365
+ html.platform-win32 .cp-session-pill.cp-pill-running::before,
2366
+ html.desktop-titlebar--win32 .cp-session-pill.cp-pill-running::before {
2367
+ animation: none;
2368
+ opacity: 0;
2369
+ }
2339
2370
  .cp-session-pill .term-minimized-pill-icon { color: rgba(212, 162, 127, 0.45); transition: color 0.2s; }
2340
2371
  .cp-session-pill.cp-pill-running .term-minimized-pill-icon { color: rgba(212, 162, 127, 1); }
2341
2372
  .cp-session-pill:hover .term-minimized-pill-icon { color: rgba(212, 162, 127, 0.85); }
@@ -2600,7 +2631,7 @@ function injectStyles() {
2600
2631
  border: 0.5px solid rgba(255,255,255,0.05);
2601
2632
  border-radius: 6px;
2602
2633
  font-size: 11px; color: rgba(255,255,255,0.7);
2603
- cursor: grab;
2634
+ cursor: move;
2604
2635
  transition: background 0.15s, border-color 0.15s;
2605
2636
  }
2606
2637
  .cp-queue-item:hover {
@@ -2614,7 +2645,7 @@ function injectStyles() {
2614
2645
  background: rgba(120,180,255,0.08);
2615
2646
  }
2616
2647
  .cp-queue-drag {
2617
- color: rgba(255,255,255,0.2); cursor: grab;
2648
+ color: rgba(255,255,255,0.2); cursor: move;
2618
2649
  font-size: 10px; user-select: none; flex-shrink: 0;
2619
2650
  }
2620
2651
  .cp-queue-text {
@@ -2802,6 +2833,13 @@ function _effortLevelsForModel() {
2802
2833
  return ['off', ...advertised.filter(id => id !== 'off')];
2803
2834
  }
2804
2835
 
2836
+ // Whether the tab's model (the selector's, for the active tab) has effort levels.
2837
+ function _modelSupportsEffort(tab) {
2838
+ if (tab === activeTab()) return _effortLevelsForModel().length > 1;
2839
+ const model = tab?.model ? _models.find(m => m.id === tab.model) : null;
2840
+ return !Array.isArray(model?.effortLevels) || model.effortLevels.some(id => id !== 'off');
2841
+ }
2842
+
2805
2843
  // Some models have no thinking at all (Haiku). Disable the toggle and force 'off'
2806
2844
  // rather than passing a --effort the CLI will ignore.
2807
2845
  function _syncThinkAvailability() {
@@ -2922,7 +2960,7 @@ function newTabState(sessionId = null, label = 'New chat', id = null) {
2922
2960
  };
2923
2961
  }
2924
2962
 
2925
- function createTab(sessionId = null, label = 'New chat', { autoSwitch = true, id = null } = {}) {
2963
+ function createTab(sessionId = null, label = 'New chat', { autoSwitch = true, id = null, deferHistory = false } = {}) {
2926
2964
  if (_tabs.length >= MAX_TABS) return null;
2927
2965
  const tab = newTabState(sessionId, label, id);
2928
2966
  // Create messages div — kept DETACHED until the tab activates. Inactive
@@ -2937,14 +2975,17 @@ function createTab(sessionId = null, label = 'New chat', { autoSwitch = true, id
2937
2975
  msgDiv.innerHTML = `<div class="cp-empty"><div class="cp-empty-logo">${CLAUDE_ICON}</div><span class="cp-empty-name">Claude</span><span class="cp-empty-hint">Send a message to start</span></div>`;
2938
2976
  tab.messagesEl = msgDiv;
2939
2977
  tab.scrollController = createStickyScrollController(msgDiv, { active: false });
2940
- // Connect WebSocket
2978
+ // Connect WebSocket (every tab, active or not: a restored tab reattaches to the background agents,
2979
+ // shells and wakeups its session kept on the server).
2941
2980
  connectTab(tab);
2942
2981
  // Create pill (rendered in shared tray, not inside panel)
2943
2982
  tab.pillEl = _createTrayPill(tab);
2944
2983
  _paintTemporary(tab);
2945
2984
  _tabs.push(tab);
2946
2985
  if (autoSwitch) switchTab(_tabs.length - 1);
2947
- if (sessionId) loadSessionHistory(sessionId, tab.messagesEl);
2986
+ // A restored background tab loads its transcript when it is first shown (switchTab), not all at once at panel open.
2987
+ if (sessionId && deferHistory) tab._historyDeferred = sessionId;
2988
+ if (sessionId && !deferHistory) loadSessionHistory(sessionId, tab.messagesEl);
2948
2989
  renderPills();
2949
2990
  if (autoSwitch) saveTabs();
2950
2991
  return tab;
@@ -3095,7 +3136,9 @@ function switchTab(idx) {
3095
3136
  const items = _models.map(m => ({ value: m.id, label: m.label }));
3096
3137
  const modelVal = _resolveModelValue(items, tab.model || _getDefaultModel());
3097
3138
  ddPopulateModels($model, items, modelVal);
3098
- if (modelVal && modelVal !== tab.model) { tab.model = modelVal; saveTabs(); }
3139
+ // The unverified fallback list (the CLI not asked yet: /api/claude/config answers at once) never
3140
+ // rewrites a tab's model; refreshModels() puts the real list in and settles it.
3141
+ if (modelVal && modelVal !== tab.model && _modelsSource !== 'fallback') { tab.model = modelVal; saveTabs(); }
3099
3142
  }
3100
3143
  if ($think && tab.effort) _setEffort($think, tab.effort);
3101
3144
  _syncThinkAvailability();
@@ -3117,6 +3160,10 @@ function switchTab(idx) {
3117
3160
  populateModeDropdown(_panel?.querySelector('#cp-mode'), tab);
3118
3161
  _renderAccountMenu();
3119
3162
  _paintTemporary(tab);
3163
+ if (tab._historyDeferred && tab._historyDeferred === tab.sessionId && !tab.automationRunId) {
3164
+ tab._historyDeferred = false;
3165
+ loadSessionHistory(tab.sessionId, tab.messagesEl);
3166
+ }
3120
3167
  saveTabs();
3121
3168
  }
3122
3169
 
@@ -3414,8 +3461,23 @@ function _scrubStaleTabState(key) {
3414
3461
  }
3415
3462
  }
3416
3463
 
3464
+ // A restored tab that was working (a turn, a question, background agents or shells) can get live rows
3465
+ // as soon as its socket reattaches: its transcript loads at once, ahead of them. An idle one waits until
3466
+ // it is shown (switchTab), so opening the panel does not fetch and render every tab's transcript.
3467
+ function _loadHistoryIfWorking(tab, saved) {
3468
+ if (!tab._historyDeferred || tab._historyDeferred !== tab.sessionId) return;
3469
+ if (!(saved?.running || saved?.needsYou || Number(saved?.backgroundWork) > 0)) return;
3470
+ tab._historyDeferred = false;
3471
+ loadSessionHistory(tab.sessionId, tab.messagesEl);
3472
+ }
3473
+
3417
3474
  function restoreTabs() {
3418
3475
  _cleanStaleWindows();
3476
+ const pickRestoredTabIdx = (activeIdx) => {
3477
+ const pendingId = globalThis.__synabunPendingTab?.provider === 'claude' ? globalThis.__synabunPendingTab.tabId : '';
3478
+ const pendingIdx = pendingId ? _tabs.findIndex(t => t.id === pendingId || t.sessionId === pendingId) : -1;
3479
+ return pendingIdx >= 0 ? pendingIdx : Math.min(activeIdx || 0, _tabs.length - 1);
3480
+ };
3419
3481
  // Try window-scoped tabs first
3420
3482
  try {
3421
3483
  const raw = storage.getItem(STOR.tabs);
@@ -3424,7 +3486,7 @@ function restoreTabs() {
3424
3486
  if (data.tabs?.length) {
3425
3487
  for (const saved of data.tabs) {
3426
3488
  if (saved.automationActive && saved.automationOwnerId !== nativeLoopWindowId) continue;
3427
- const t = createTab(saved.automationRunId ? null : saved.sessionId, saved.label, { autoSwitch: false, id: saved.id });
3489
+ const t = createTab(saved.automationRunId ? null : saved.sessionId, saved.label, { autoSwitch: false, id: saved.id, deferHistory: true });
3428
3490
  if (t && saved.automationRunId) t.sessionId = saved.sessionId || null;
3429
3491
  if (t && saved.sessionCost) t.sessionCost = saved.sessionCost;
3430
3492
  if (t && saved.running && !saved.automationRunId) t._wasRunning = true; // trigger reattach on WS open
@@ -3448,9 +3510,10 @@ function restoreTabs() {
3448
3510
  if (t && saved.automationRunId) t.automationActive = !!saved.automationActive;
3449
3511
  if (t && saved.automationRunId) t.automationOwnerId = saved.automationOwnerId || null;
3450
3512
  if (t && saved.releasedRunId && !saved.automationRunId) t.releasedRunId = saved.releasedRunId;
3513
+ if (t) _loadHistoryIfWorking(t, saved);
3451
3514
  }
3452
3515
  if (_tabs.length) {
3453
- switchTab(Math.min(data.activeIdx || 0, _tabs.length - 1));
3516
+ switchTab(pickRestoredTabIdx(data.activeIdx));
3454
3517
  return;
3455
3518
  }
3456
3519
  }
@@ -3464,7 +3527,7 @@ function restoreTabs() {
3464
3527
  if (data.tabs?.length) {
3465
3528
  for (const saved of data.tabs) {
3466
3529
  if (saved.automationActive && saved.automationOwnerId !== nativeLoopWindowId) continue;
3467
- const t = createTab(saved.automationRunId ? null : saved.sessionId, saved.label, { autoSwitch: false, id: saved.id });
3530
+ const t = createTab(saved.automationRunId ? null : saved.sessionId, saved.label, { autoSwitch: false, id: saved.id, deferHistory: true });
3468
3531
  if (t && saved.automationRunId) t.sessionId = saved.sessionId || null;
3469
3532
  if (t && saved.sessionCost) t.sessionCost = saved.sessionCost;
3470
3533
  if (t && saved.running && !saved.automationRunId) t._wasRunning = true;
@@ -3475,9 +3538,10 @@ function restoreTabs() {
3475
3538
  if (t && saved.automationRunId) t.automationRunId = saved.automationRunId;
3476
3539
  if (t && saved.automationRunId) t.automationActive = !!saved.automationActive;
3477
3540
  if (t && saved.automationRunId) t.automationOwnerId = saved.automationOwnerId || null;
3541
+ if (t) _loadHistoryIfWorking(t, saved);
3478
3542
  }
3479
3543
  if (_tabs.length) {
3480
- switchTab(Math.min(data.activeIdx || 0, _tabs.length - 1));
3544
+ switchTab(pickRestoredTabIdx(data.activeIdx));
3481
3545
  // Don't remove legacy key — other windows may still need it during rollout
3482
3546
  return;
3483
3547
  }
@@ -4512,6 +4576,8 @@ function writeSessionSnapshot(tab) {
4512
4576
  const sid = tab?.sessionId || null;
4513
4577
  const $msgs = tab?.messagesEl || null;
4514
4578
  if (!sid || !$msgs) return;
4579
+ // Not loaded yet (a restored tab not shown so far), or half rebuilt: what is on screen is not the transcript.
4580
+ if (tab._historyDeferred || tab._historyRendering || tab._historyHold) return;
4515
4581
  const html = providerAuthSnapshot($msgs);
4516
4582
  if (!html.trim()) return;
4517
4583
  // Skip snapshot if the transcript only contains the empty-state placeholder
@@ -4686,9 +4752,14 @@ function _renderHistoryRow(tab, $msgs, m) {
4686
4752
  if (typeof t === 'object' && t.name) {
4687
4753
  // A question or a plan that was already answered: a record of it, not
4688
4754
  // the interactive card (and not a JSON dump).
4689
- if (t.name === 'AskUserQuestion') wrap.appendChild(buildHistoryAskCard(t));
4690
- else if (t.name === 'ExitPlanMode') wrap.appendChild(buildHistoryPlanCard(t));
4691
- else wrap.appendChild(buildTool(t, tab));
4755
+ let card;
4756
+ if (t.name === 'AskUserQuestion') card = buildHistoryAskCard(t);
4757
+ else if (t.name === 'ExitPlanMode') card = buildHistoryPlanCard(t);
4758
+ else card = buildTool(t, tab);
4759
+ if (card) {
4760
+ if (t.id && $msgs._replayCards instanceof Map) $msgs._replayCards.set(String(t.id), card);
4761
+ wrap.appendChild(card);
4762
+ }
4692
4763
  } else {
4693
4764
  // Legacy: tool name string only
4694
4765
  const tName = String(t);
@@ -4704,20 +4775,33 @@ function _renderHistoryRow(tab, $msgs, m) {
4704
4775
  }
4705
4776
  }
4706
4777
  el.appendChild(wrap); $msgs.appendChild(el);
4778
+ // Live results that arrived for these cards while the replay had not built them.
4779
+ if (live && tab._pendingResults instanceof Map && tab._pendingResults.size) {
4780
+ for (const t of m.tools || []) {
4781
+ const ev = t?.id ? takePendingResult(tab, t.id) : null;
4782
+ if (ev) { try { updateToolResult(tab, ev); } catch {} }
4783
+ }
4784
+ }
4707
4785
  return;
4708
4786
  }
4709
4787
  if (m.role === 'tool_result' && m.toolUseId) {
4710
4788
  if (fillHistoryCard($msgs, m)) return;
4789
+ const indexedCard = $msgs._replayCards instanceof Map ? $msgs._replayCards.get(String(m.toolUseId)) : null;
4711
4790
  if (live) {
4712
- updateToolResult(tab, { type: 'tool_result', tool_use_id: m.toolUseId, content: m.text || '', is_error: !!m.isError, tool_use_result: m.structured });
4791
+ // Marked as a replayed result: it is not kept as pending, and its todos are already in the precomputed list.
4792
+ tab._replayRow = true;
4793
+ try { updateToolResult(tab, { type: 'tool_result', tool_use_id: m.toolUseId, content: m.text || '', is_error: !!m.isError, tool_use_result: m.structured }); }
4794
+ finally { tab._replayRow = false; }
4713
4795
  // A subagent of a past session: its own transcript loads when the card is opened.
4714
4796
  const agentId = m.structured?.agentId;
4715
- const agentCard = agentId && sessionActionsAvailable(tab) ? $msgs.querySelector(`.cp-agent-card[data-tool-id="${CSS.escape(m.toolUseId)}"]`) : null;
4797
+ const agentCard = agentId && sessionActionsAvailable(tab)
4798
+ ? ((indexedCard?.classList?.contains('cp-agent-card') ? indexedCard : null) || $msgs.querySelector(`.cp-agent-card[data-tool-id="${CSS.escape(m.toolUseId)}"]`))
4799
+ : null;
4716
4800
  if (agentCard) _wireSubagentTranscript(tab, agentCard, String(agentId));
4717
4801
  return;
4718
4802
  }
4719
4803
  // No tab behind this container: plain text into the card.
4720
- const card = $msgs.querySelector(`.tool-card[data-tool-id="${CSS.escape(m.toolUseId)}"]`);
4804
+ const card = (indexedCard?.matches?.('.tool-card') ? indexedCard : null) || $msgs.querySelector(`.tool-card[data-tool-id="${CSS.escape(m.toolUseId)}"]`);
4721
4805
  const rLbl = card?.querySelector('.tool-result-label');
4722
4806
  const rSec = card?.querySelector('.tool-result-content');
4723
4807
  if (rSec && m.text) {
@@ -4822,12 +4906,19 @@ function _addHistoryPager(tab, $msgs, sid, project, data) {
4822
4906
  // Render at the end (the live renderers append), then move the block up.
4823
4907
  const tail = $msgs.lastElementChild;
4824
4908
  tab._replaying = (tab._replaying || 0) + 1;
4909
+ const replayCards = new Map();
4910
+ $msgs._replayCards = replayCards;
4825
4911
  try { for (const m of page.messages) _renderHistoryRow(tab, $msgs, m); }
4826
- finally { tab._replaying = Math.max(0, (tab._replaying || 1) - 1); }
4912
+ finally {
4913
+ if ($msgs._replayCards === replayCards) delete $msgs._replayCards;
4914
+ tab._replaying = Math.max(0, (tab._replaying || 1) - 1);
4915
+ }
4827
4916
  const added = [];
4828
4917
  for (let n = tail?.nextElementSibling; n; n = n.nextElementSibling) added.push(n);
4918
+ markHistoryRows(added, 'earlier'); // history: a later rebuild replaces it
4829
4919
  const anchor = note.nextElementSibling;
4830
4920
  for (const n of added) $msgs.insertBefore(n, anchor);
4921
+ try { collapseAdjacentMcpTools($msgs); } catch {}
4831
4922
  try { settleReplayedCards($msgs, tab); } catch {}
4832
4923
  // What the user asked to see must survive the next prune.
4833
4924
  $msgs._pruneCap = $msgs.childElementCount + MAX_MSG_CHILDREN;
@@ -4860,20 +4951,42 @@ function _dropSessionSnapshot(sid) {
4860
4951
  try { delete _sessionSnapshots[sid]; deleteBlob('claude-snapshots', sid); } catch {}
4861
4952
  }
4862
4953
 
4863
- async function loadSessionHistory(sid, $msgs) {
4954
+ // Loads of one container run one at a time, the newest asked for wins (queueHistoryLoad, cp/cp-restore.js).
4955
+ // The tab that owns the container is taken once, here: per-tab state (todos, usage, the snapshot) never goes
4956
+ // to another tab, and a tab closed meanwhile stops the load. While any load of a tab runs, its live
4957
+ // transcript messages wait in its buffer (historyGate in handleTabMsg) and play after the history.
4958
+ function loadSessionHistory(sid, $msgs) {
4864
4959
  if (!$msgs) $msgs = activeTab()?.messagesEl;
4865
- if (!$msgs) return;
4960
+ if (!$msgs) return Promise.resolve();
4961
+ const ownerTab = _tabs.find(t => t.messagesEl === $msgs) || null;
4962
+ if (ownerTab && ownerTab._historyDeferred === sid) ownerTab._historyDeferred = false;
4963
+ if (ownerTab) ownerTab._historyHold = (ownerTab._historyHold || 0) + 1;
4964
+ const job = queueHistoryLoad($msgs, (isStale, ticket) => {
4965
+ const stale = () => isStale() || !!(ownerTab && (ownerTab.closed || ownerTab.messagesEl !== $msgs));
4966
+ if (stale()) return;
4967
+ return _loadSessionHistoryNow(sid, $msgs, ownerTab, stale, ticket);
4968
+ });
4969
+ return job.finally(() => {
4970
+ if (!ownerTab) return;
4971
+ ownerTab._historyHold = Math.max(0, (ownerTab._historyHold || 1) - 1);
4972
+ if (!ownerTab._historyHold && ownerTab._msgBuffer?.length && !ownerTab.closed) _flushMsgBuffer(ownerTab);
4973
+ });
4974
+ }
4975
+
4976
+ async function _loadSessionHistoryNow(sid, $msgs, ownerTab, stale, ticket) {
4866
4977
  // Try local snapshot first — exact replay of the live render, lossless.
4867
4978
  // Snapshot hydration is async (blob store); wait for it so a fresh page
4868
4979
  // load doesn't miss its own snapshots and rebuild from JSONL needlessly.
4869
4980
  try { await _snapshotsReady; } catch {}
4981
+ if (stale()) return;
4870
4982
  const snap = getSessionSnapshot(sid);
4871
4983
  let restored = null; // the rows the snapshot put back, when a rebuild follows it
4872
4984
  if (snap && snap.html) {
4873
- const tab = _tabs.find(t => t.messagesEl === $msgs) || activeTab();
4985
+ const tab = ownerTab;
4874
4986
  $msgs._historySid = sid; // the session this transcript shows (a restored pager loads its earlier pages)
4875
4987
  if (renderStoredSession(snap, $msgs, tab)) {
4876
4988
  restored = [...$msgs.children];
4989
+ markHistoryRows(restored, ticket); // the next rebuild replaces them
4877
4990
  scrollEnd();
4878
4991
  // Background refresh: pull latest JSONL in case the session continued in
4879
4992
  // another window, or was rewound there. The probe reads what the owning
@@ -4889,6 +5002,7 @@ async function loadSessionHistory(sid, $msgs) {
4889
5002
  if (tab?.accountId) params.set('account', tab.accountId);
4890
5003
  const probe = await fetch(`/api/claude-code/sessions/${encodeURIComponent(sid)}/messages?${params}`);
4891
5004
  const probeData = await probe.json();
5005
+ if (stale()) return;
4892
5006
  // `visible` counts prompts and replies only (a server that predates it sends `total`).
4893
5007
  if ((probeData?.visible ?? probeData?.total ?? 0) > (snap.itemCount || 0) + 2) {
4894
5008
  // Upstream has materially more content — fall through to full rebuild
@@ -4904,8 +5018,8 @@ async function loadSessionHistory(sid, $msgs) {
4904
5018
  } catch { return; }
4905
5019
  }
4906
5020
  }
4907
- const accountTab = _tabs.find(t => t.messagesEl === $msgs);
4908
- let project = _historyProject(accountTab || activeTab()) || undefined;
5021
+ const accountTab = ownerTab;
5022
+ let project = _historyProject(ownerTab || activeTab()) || undefined;
4909
5023
  const readHistory = async (from) => {
4910
5024
  const params = new URLSearchParams({ limit: '500' });
4911
5025
  if (from) params.set('project', from);
@@ -4916,6 +5030,7 @@ async function loadSessionHistory(sid, $msgs) {
4916
5030
  };
4917
5031
  try {
4918
5032
  let data = await readHistory(project);
5033
+ if (stale()) return;
4919
5034
  // The tab's Claude account was removed in Settings: say so. Its transcripts
4920
5035
  // are not looked for under the default account (another identity's folder).
4921
5036
  if (data.code === 'account_unavailable') {
@@ -4932,6 +5047,7 @@ async function loadSessionHistory(sid, $msgs) {
4932
5047
  if (historyOutcome(data) === 'not_in_project') {
4933
5048
  const owner = await _transcriptOwner(sid, project, accountTab?.accountId || '');
4934
5049
  const moved = owner ? await readHistory(owner) : null;
5050
+ if (stale()) return;
4935
5051
  if (moved && historyOutcome(moved) !== 'not_in_project' && historyOutcome(moved) !== 'not_found' && historyOutcome(moved) !== 'refused') {
4936
5052
  data = moved;
4937
5053
  project = owner;
@@ -4963,29 +5079,66 @@ async function loadSessionHistory(sid, $msgs) {
4963
5079
  return;
4964
5080
  }
4965
5081
  // Chunked rebuild: rendering 500 messages in one task froze the UI during
4966
- // cold restore / orphan resync. The generation counter aborts a stale
4967
- // rebuild when a newer one starts (reattach resync, in-tab session switch,
4968
- // tab close) — the newer run owns the transcript.
4969
- const ownerTab = _tabs.find(t => t.messagesEl === $msgs) || activeTab();
4970
- const gen = ownerTab ? (ownerTab._historyGen = (ownerTab._historyGen || 0) + 1) : 0;
4971
- // The rebuild replaces the snapshot that was put back above (only those
4972
- // rows: what a live turn appended meanwhile stays).
5082
+ // cold restore / orphan resync. A newer load of this container (reattach resync,
5083
+ // in-tab session switch) or the tab closing stops this one at its next yield.
5084
+ // What an earlier rebuild of this container left (finished, stopped half way, or
5085
+ // the snapshot put back above) is taken out: never a second copy after it.
5086
+ for (const node of earlierHistoryRows($msgs.children, ticket)) node.remove();
4973
5087
  if (restored) for (const node of restored) node.remove();
5088
+ // Rows that came live before the load (the history goes before them: it is older).
5089
+ const live = liveRows($msgs.children);
5090
+ const liveAnchor = live[0] || null;
5091
+ const liveSet = new Set(live);
5092
+ const { head, tail } = !restored ? splitHistoryTail(data.messages, 40) : { head: [], tail: data.messages };
5093
+ // Renders rows at the end, marks them as this load's, and moves them before `before`.
5094
+ const renderRows = (rows, before) => renderHistoryRowsInto($msgs, rows, (m) => _renderHistoryRow(ownerTab, $msgs, m), { before, ticket, skip: liveSet });
4974
5095
  // Replay goes through the live renderers (tool cards, typed results), marked
4975
5096
  // so it leaves no activity behind: no hook-strip entries, no statusline verb.
4976
5097
  if (ownerTab) ownerTab._replaying = (ownerTab._replaying || 0) + 1;
5098
+ if (ownerTab) ownerTab._historyRendering = (ownerTab._historyRendering || 0) + 1;
5099
+ if (ownerTab && head.length) {
5100
+ const precomputedTodos = replayTodosFromMessages(data.messages, ownerTab.todos);
5101
+ if (precomputedTodos) ownerTab.todos = precomputedTodos;
5102
+ ownerTab._skipReplayTodos = true;
5103
+ }
5104
+ const replayCards = new Map();
5105
+ $msgs._replayCards = replayCards;
4977
5106
  try {
4978
- let processed = 0;
4979
- for (const m of data.messages) {
4980
- if (++processed % 40 === 0) {
5107
+ if (head.length) {
5108
+ const tailNodes = renderRows(tail, liveAnchor);
5109
+ const firstTailNode = tailNodes[0] || liveAnchor;
5110
+ scrollEnd();
5111
+ const CHUNK = 50;
5112
+ for (let offset = 0; offset < head.length; offset += CHUNK) {
4981
5113
  await new Promise(r => setTimeout(r, 0));
4982
- if (ownerTab && (ownerTab._historyGen !== gen || ownerTab.closed)) return;
5114
+ if (stale()) return;
5115
+ renderRows(head.slice(offset, offset + CHUNK), firstTailNode);
5116
+ // Following the bottom: stay there. Scrolled up: the browser's scroll anchoring keeps the rows in view.
5117
+ if (ownerTab === activeTab()) scrollEnd();
5118
+ }
5119
+ } else {
5120
+ const CHUNK = 40;
5121
+ for (let offset = 0; offset < data.messages.length; offset += CHUNK) {
5122
+ if (offset) {
5123
+ await new Promise(r => setTimeout(r, 0));
5124
+ if (stale()) return;
5125
+ }
5126
+ renderRows(data.messages.slice(offset, offset + CHUNK), liveAnchor);
4983
5127
  }
4984
- _renderHistoryRow(ownerTab, $msgs, m);
4985
5128
  }
4986
5129
  } finally {
4987
- if (ownerTab) ownerTab._replaying = Math.max(0, (ownerTab._replaying || 1) - 1);
5130
+ if ($msgs._replayCards === replayCards) delete $msgs._replayCards;
5131
+ if (ownerTab) {
5132
+ delete ownerTab._skipReplayTodos;
5133
+ ownerTab._historyRendering = Math.max(0, (ownerTab._historyRendering || 1) - 1);
5134
+ ownerTab._replaying = Math.max(0, (ownerTab._replaying || 1) - 1);
5135
+ // Live results kept for cards this page never built (cut by the page) are dropped.
5136
+ if (!ownerTab._historyRendering) ownerTab._pendingResults = null;
5137
+ }
4988
5138
  }
5139
+ try { collapseAdjacentMcpTools($msgs); } catch {}
5140
+ // Always for the tab on screen: a list the page emptied must clear the dock too.
5141
+ if (ownerTab && ownerTab === activeTab()) { try { renderTodoWidget(ownerTab); } catch {} }
4989
5142
  // Calls whose result is not in this page (interrupted, or cut by the page) stop looking busy.
4990
5143
  try { settleReplayedCards($msgs, ownerTab); } catch {}
4991
5144
  pruneMessages($msgs);
@@ -5001,6 +5154,8 @@ async function loadSessionHistory(sid, $msgs) {
5001
5154
  $msgs.appendChild(note);
5002
5155
  }
5003
5156
  if (ownerTab) ownerTab._historyResults = historyHasResultSupport(data);
5157
+ // The pager and the notes are this load's too (a later rebuild replaces them).
5158
+ markHistoryRows([...$msgs.children].filter(n => !liveSet.has(n) && n[HISTORY_ROW_KEY] == null), ticket);
5004
5159
  // Populate context gauge from session history usage
5005
5160
  const tab = ownerTab;
5006
5161
  if (tab && data.usage) {
@@ -5013,10 +5168,10 @@ async function loadSessionHistory(sid, $msgs) {
5013
5168
  renderGauge(tab);
5014
5169
  }
5015
5170
  scrollEnd();
5016
- // Cache the rebuilt transcript so the next resume on a fresh browser hits
5017
- // the snapshot path and avoids paying the JSONL → DOM cost again.
5018
- if (tab && tab.sessionId === sid) writeSessionSnapshot(tab);
5171
+ // Cache the rebuilt transcript (debounced so DOM serialization never blocks session open).
5172
+ if (tab && tab.sessionId === sid) scheduleSessionSnapshotSave(tab);
5019
5173
  } catch {
5174
+ if (stale()) return;
5020
5175
  $msgs.innerHTML = '<div class="msg-status">Failed to load history</div>';
5021
5176
  }
5022
5177
  }
@@ -5270,6 +5425,14 @@ function handleTabMsg(tab, msg) {
5270
5425
  // An MCP server's elicitation_complete resolves the URL card that is waiting
5271
5426
  // (bypassesPromptBuffer in cp/cp-events.js lists what passes).
5272
5427
  const _bufferBypass = bypassesPromptBuffer(msg);
5428
+ // A restored tab whose history is not loaded yet gets a live row (a wakeup, a background turn): its
5429
+ // history loads first and the message waits for it; any running history load holds them the same way.
5430
+ const gate = historyGate(tab, msg, _bufferBypass);
5431
+ if (gate === 'load') { tab._historyDeferred = false; loadSessionHistory(tab.sessionId, tab.messagesEl); }
5432
+ if (gate !== 'pass') {
5433
+ if (tab._msgBuffer.length < 500) tab._msgBuffer.push(msg);
5434
+ return;
5435
+ }
5273
5436
  // Also buffer while a chunked drain is in progress (or older messages still
5274
5437
  // queue) — without this, live events overtake buffered ones between slices.
5275
5438
  if ((tab._activePerm || tab.pendingAskRequestId || tab._draining || tab._msgBuffer.length) && !_bufferBypass) {
@@ -5295,10 +5458,11 @@ function _flushMsgBuffer(tab) {
5295
5458
  while (tab._msgBuffer.length && n++ < budget) {
5296
5459
  // A drained message can re-arm a permission/ask prompt — stop and let
5297
5460
  // that flow's resolution resume the drain (its handlers call us again).
5298
- if (tab._activePerm || tab.pendingAskRequestId) break;
5461
+ if (tab._activePerm || tab.pendingAskRequestId || tab._historyHold) break;
5299
5462
  _processTabMsg(tab, tab._msgBuffer.shift());
5300
5463
  }
5301
- if (tab._msgBuffer.length && !tab._activePerm && !tab.pendingAskRequestId) {
5464
+ // A history load holds the rest: it drains them when it is done.
5465
+ if (tab._msgBuffer.length && !tab._activePerm && !tab.pendingAskRequestId && !tab._historyHold) {
5302
5466
  setTimeout(step, 0);
5303
5467
  } else {
5304
5468
  tab._draining = false;
@@ -5413,6 +5577,11 @@ function _processTabMsg(tab, msg) {
5413
5577
  _sessionSaysMode(tab, msg.mode, 'reattach', msg);
5414
5578
  // Orphan buffer overflowed while detached — rebuild transcript from JSONL
5415
5579
  if (msg.resynced && tab.sessionId) {
5580
+ tab._historyDeferred = false;
5581
+ try { loadSessionHistory(tab.sessionId, tab.messagesEl); } catch {}
5582
+ } else if (tab._historyDeferred && tab._historyDeferred === tab.sessionId) {
5583
+ // A restored tab not shown yet whose session is still live on the server: its history first.
5584
+ tab._historyDeferred = false;
5416
5585
  try { loadSessionHistory(tab.sessionId, tab.messagesEl); } catch {}
5417
5586
  }
5418
5587
  if (msg.running) {
@@ -5442,7 +5611,8 @@ function _processTabMsg(tab, msg) {
5442
5611
  if (expectRunning && !(tab.sendStartedAt > tab._reattachSentAt)) {
5443
5612
  console.log('[claude-panel] No orphan to reattach — the turn ended while disconnected');
5444
5613
  finishTab(tab, true);
5445
- appendStatus(tab, 'Connection lost — this turn did not survive the disconnect.');
5614
+ // A dispatched worker's session says what became of its run (resumed, or how it ended), not only this.
5615
+ _lostTurnLine(tab).then((line) => { if (!tab.closed) appendStatus(tab, line); });
5446
5616
  }
5447
5617
  // Prompts queued behind a Compact when the socket closed: nothing says it finished.
5448
5618
  if (tab._queueWaits === 'session') _compactQueue.after(tab, 'lost');
@@ -6687,6 +6857,8 @@ function renderAssistant(tab, msg) {
6687
6857
 
6688
6858
  /** Build an interactive AskUserQuestion card from a tool_use block */
6689
6859
  function buildAskFromToolUse(tab, block) {
6860
+ // A task worker’s broker owns the interactive card, even if this tool event arrives first.
6861
+ if (tab.automationRunId) { const placeholder = document.createElement('div'); placeholder.hidden = true; return placeholder; }
6690
6862
  const input = block.input || {};
6691
6863
  const toolUseId = block.id || '';
6692
6864
  tab.pendingAskToolUseId = toolUseId;
@@ -6878,7 +7050,7 @@ function buildAskFromToolUse(tab, block) {
6878
7050
  // Submit button — sends all answers as a batch
6879
7051
  submitBtn.addEventListener('click', () => {
6880
7052
  // An answer is shown as given only once it can be sent: until then the card stays as it is.
6881
- if (tab.ws?.readyState !== WebSocket.OPEN) { appendStatus(tab, ASK_ANSWER_UNSENT); return; }
7053
+ if (!tab.automationRunId && tab.ws?.readyState !== WebSocket.OPEN) { appendStatus(tab, ASK_ANSWER_UNSENT); return; }
6882
7054
  container.querySelectorAll('.ask-option').forEach(b => { b.disabled = true; });
6883
7055
  container.querySelectorAll('.ask-text-input, .ask-notes').forEach(i => { i.disabled = true; });
6884
7056
  submitBtn.disabled = true;
@@ -6896,6 +7068,21 @@ function buildAskFromToolUse(tab, block) {
6896
7068
 
6897
7069
  // Returns whether the answer left: false while the tab's socket is closed.
6898
7070
  function sendAskAnswer(tab, questions, answers, annotations = null) {
7071
+ if (tab.automationRunId) {
7072
+ const requestId = tab.pendingAskRequestId;
7073
+ if (!tab._workerQuestions?.has(requestId)) return false;
7074
+ answerWorkerQuestion(tab.automationRunId, requestId, { decision: 'answer', answers }).catch((error) => {
7075
+ appendStatus(tab, error.message);
7076
+ // Keep the native card retryable after a delivery failure.
7077
+ if (tab._workerQuestions?.has(requestId)) {
7078
+ const card = tab.messagesEl?.querySelector(`[data-worker-question-id="${CSS.escape(requestId)}"]`);
7079
+ card?.querySelectorAll('button, input, textarea').forEach((el) => { el.disabled = false; });
7080
+ const submit = card?.querySelector('.ask-submit');
7081
+ if (submit) submit.textContent = t('common.retry');
7082
+ }
7083
+ });
7084
+ return true;
7085
+ }
6899
7086
  if (!tab.ws || tab.ws.readyState !== WebSocket.OPEN) return false;
6900
7087
 
6901
7088
  const requestId = tab.pendingAskRequestId;
@@ -7347,9 +7534,15 @@ function updateToolResult(tab, ev) {
7347
7534
  if (!$msgs) return;
7348
7535
  // Background task output (BashOutput results) routes back to the origin card
7349
7536
  try { handleBgToolResult(ev, tab); } catch {}
7350
- const escapedId = CSS.escape(ev.tool_use_id);
7351
- const card = $msgs.querySelector(`.tool-card[data-tool-id="${escapedId}"]`) || $msgs.querySelector(`.plan-card[data-tool-id="${escapedId}"]`);
7352
- if (!card) return;
7537
+ // During a replay the cards it built are indexed by id (no selector scan of the transcript per result).
7538
+ const indexed = $msgs._replayCards instanceof Map ? $msgs._replayCards.get(String(ev.tool_use_id)) : null;
7539
+ let card = indexed?.matches?.('.tool-card, .plan-card') ? indexed : null;
7540
+ if (!card) {
7541
+ const escapedId = CSS.escape(ev.tool_use_id);
7542
+ card = $msgs.querySelector(`.tool-card[data-tool-id="${escapedId}"]`) || $msgs.querySelector(`.plan-card[data-tool-id="${escapedId}"]`);
7543
+ }
7544
+ // A live result for a card the tail-first replay has not built yet: applied when it is built.
7545
+ if (!card) { holdPendingResult(tab, ev); return; }
7353
7546
  // Synthetic PostToolUse hook event + SubagentStop
7354
7547
  const toolNm = card.dataset.toolName || '';
7355
7548
  recordHookEvent(tab, ev.is_error ? 'PostToolUseFailure' : 'PostToolUse', toolNm);
@@ -7374,8 +7567,10 @@ function updateToolResult(tab, ev) {
7374
7567
  finalizeDiffCard(card, ev, typed?.view);
7375
7568
  if (!ev.is_error) return; // success: stats in the header say it all — skip noisy RESULT text
7376
7569
  }
7377
- // Collapse adjacent repeated MCP tool calls (post-insert) — cheap, DOM-local
7378
- try { collapseAdjacentMcpTools($msgs); } catch {}
7570
+ // Collapse adjacent repeated MCP tool calls inside this turn (replays batch once at the end)
7571
+ if (!tab._replaying && toolNm.startsWith('mcp__')) {
7572
+ try { collapseAdjacentMcpTools(card.parentElement || $msgs); } catch {}
7573
+ }
7379
7574
  // Plan cards don't have result sections — just mark success/error via border
7380
7575
  if (card.classList.contains('plan-card')) {
7381
7576
  card.style.borderColor = ev.is_error ? 'rgba(255,82,82,0.3)' : 'rgba(100,200,120,0.3)';
@@ -7557,6 +7752,9 @@ function _applySessionOptions(tab, msg) {
7557
7752
  if (hasCapability(tab, 'tool_policy')) msg.toolPolicy = tab.toolPolicy || 'full';
7558
7753
  // The tab's session settings (/session): the bridge validates them again.
7559
7754
  if (hasCapability(tab, 'session_settings')) msg.session = normalizeSession(tab.session);
7755
+ // Think off on a model with effort levels is a pick of its own: sent as 'off'
7756
+ // so the bridge disables thinking, or the user's settings' effortLevel applies.
7757
+ if (!msg.effort && hasCapability(tab, 'effort_off') && _modelSupportsEffort(tab)) msg.effort = 'off';
7560
7758
  // The Claude account this tab runs under (the default account sends nothing).
7561
7759
  if (hasCapability(tab, 'accounts')) msg.accountId = tab.accountId || 'default';
7562
7760
  // A name typed before the first message becomes the new session's title.
@@ -8056,6 +8254,7 @@ function renderAskUserQuestion(tab, requestId, input) {
8056
8254
  const allQuestions = input.questions || questions;
8057
8255
  const el = document.createElement('div');
8058
8256
  el.className = 'msg msg-assistant';
8257
+ el.dataset.workerQuestionId = requestId;
8059
8258
 
8060
8259
  const avatar = document.createElement('div');
8061
8260
  avatar.className = 'msg-avatar';
@@ -8237,7 +8436,7 @@ function renderAskUserQuestion(tab, requestId, input) {
8237
8436
  // Submit button — sends all answers as a batch
8238
8437
  submitBtn.addEventListener('click', () => {
8239
8438
  // An answer is shown as given only once it can be sent: until then the card stays as it is.
8240
- if (tab.ws?.readyState !== WebSocket.OPEN) { appendStatus(tab, ASK_ANSWER_UNSENT); return; }
8439
+ if (!tab.automationRunId && tab.ws?.readyState !== WebSocket.OPEN) { appendStatus(tab, ASK_ANSWER_UNSENT); return; }
8241
8440
  wrap.querySelectorAll('.ask-option').forEach(b => { b.disabled = true; });
8242
8441
  wrap.querySelectorAll('.ask-text-input, .ask-notes').forEach(i => { i.disabled = true; });
8243
8442
  submitBtn.disabled = true;
@@ -8307,8 +8506,12 @@ function appendUser(tab, text, images, files) {
8307
8506
  function _stampUserMessageUuid(tab, uuid) {
8308
8507
  const $msgs = tab.messagesEl;
8309
8508
  if (!$msgs || !uuid) return;
8310
- const rows = [...$msgs.querySelectorAll('.msg-user')].reverse();
8311
- const row = rows.find(r => !r.dataset.uuid);
8509
+ const last = $msgs.lastElementChild;
8510
+ let row = (last?.classList?.contains('msg-user') && !last.dataset.uuid) ? last : null;
8511
+ if (!row) {
8512
+ const rows = [...$msgs.querySelectorAll('.msg-user')].reverse();
8513
+ row = rows.find(r => !r.dataset.uuid);
8514
+ }
8312
8515
  if (!row) return;
8313
8516
  row.dataset.uuid = uuid;
8314
8517
  if (!tab.sdkMode) return;
@@ -10348,29 +10551,36 @@ const _fileLinkOptions = {
10348
10551
  };
10349
10552
 
10350
10553
  // ── Public API ──
10554
+ let _readyPromise = null;
10351
10555
  async function _ensureClaudePanelReady() {
10556
+ if (_readyPromise) return _readyPromise;
10352
10557
  if (!_panel) {
10353
- injectStyles();
10354
- _panel = buildPanel();
10355
- document.body.appendChild(_panel);
10356
- registerSidepanel({
10357
- owner: PANEL_OWNER, provider: 'claude', element: _panel,
10358
- header: _panel.querySelector('.cp-header'), actions: _panel.querySelector('.cp-header-actions'),
10359
- buttonClass: 'cp-header-btn', dockHandle: _panel.querySelector('.cp-resize-handle'),
10360
- applyVisibility: applyClaudeVisibility,
10361
- });
10362
- wireEvents();
10363
- _panel.querySelector('#cp-messages-container')?.addEventListener('click', _onMessagesClick);
10364
- wireFileLinks(_panel.querySelector('#cp-messages-container'), _fileLinkOptions);
10365
- await loadConfig(); // must complete before restoreTabs — checks server boot ID
10366
- loadSkills();
10367
- restoreTabs();
10368
- loadMonthlyCost();
10369
- syncSessionCosts();
10370
- _wireBrowserSync();
10371
- _startHeartbeat();
10372
- window.dispatchEvent(new CustomEvent('sidepanel-tray:provider-loaded', { detail: { provider: 'claude' } }));
10373
- _ensureCliSubscription();
10558
+ _readyPromise = (async () => {
10559
+ injectStyles();
10560
+ _panel = buildPanel();
10561
+ document.body.appendChild(_panel);
10562
+ registerSidepanel({
10563
+ owner: PANEL_OWNER, provider: 'claude', element: _panel,
10564
+ header: _panel.querySelector('.cp-header'), actions: _panel.querySelector('.cp-header-actions'),
10565
+ buttonClass: 'cp-header-btn', dockHandle: _panel.querySelector('.cp-resize-handle'),
10566
+ applyVisibility: applyClaudeVisibility,
10567
+ });
10568
+ wireEvents();
10569
+ _panel.querySelector('#cp-messages-container')?.addEventListener('click', _onMessagesClick);
10570
+ wireFileLinks(_panel.querySelector('#cp-messages-container'), _fileLinkOptions);
10571
+ await loadConfig(); // must complete before restoreTabs — checks server boot ID
10572
+ loadSkills();
10573
+ restoreTabs();
10574
+ setTimeout(() => {
10575
+ loadMonthlyCost();
10576
+ syncSessionCosts();
10577
+ }, 400);
10578
+ _wireBrowserSync();
10579
+ _startHeartbeat();
10580
+ window.dispatchEvent(new CustomEvent('sidepanel-tray:provider-loaded', { detail: { provider: 'claude' } }));
10581
+ _ensureCliSubscription();
10582
+ })();
10583
+ return _readyPromise;
10374
10584
  }
10375
10585
  }
10376
10586
 
@@ -10452,8 +10662,11 @@ export async function attachClaudeAutomation(run, { focus = !!run?.focus, isCanc
10452
10662
  }
10453
10663
  if (!tab) return { ok: false, reason: 'tab_limit' };
10454
10664
  const running = run.status === 'starting' || run.status === 'running';
10665
+ // A dispatched worker a server restart cut off: suspended until the next start resumes it as the same run
10666
+ // (lib/assistant-dispatch.js). It still owns this tab; nothing of its cut-off turn runs any more.
10667
+ const suspended = run.status === 'suspended';
10455
10668
  tab.automationRunId = run.runId;
10456
- tab.automationActive = running;
10669
+ tab.automationActive = running || suspended;
10457
10670
  tab.automationOwnerId = nativeLoopWindowId;
10458
10671
  tab.releasedRunId = null;
10459
10672
  tab.sdkMode = true;
@@ -10467,6 +10680,12 @@ export async function attachClaudeAutomation(run, { focus = !!run?.focus, isCanc
10467
10680
  setRunning(tab, running);
10468
10681
  updatePillLabel(tab);
10469
10682
  tab._automationIteration = run.currentIteration;
10683
+ // Resumed after a server restart: what the run did while this window was away (the continuation prompt, its first
10684
+ // steps: events sent before the page reconnected) is in its transcript, not in this tab, which reads it again.
10685
+ // That holds for a run that resumed and ended while this window was away too.
10686
+ const resumes = Number(run.resumes) || 0;
10687
+ const resumedSinceSeen = resumes > (tab._automationResumes || 0);
10688
+ if (tab._automationHydrated && resumedSinceSeen) tab._automationHydrated = false;
10470
10689
  if (!tab._automationHydrated) {
10471
10690
  tab._automationHydrated = true;
10472
10691
  if (tab.messagesEl) tab.messagesEl.innerHTML = '';
@@ -10476,11 +10695,28 @@ export async function attachClaudeAutomation(run, { focus = !!run?.focus, isCanc
10476
10695
  return { ok: false, reason: 'dismissed' };
10477
10696
  }
10478
10697
  }
10698
+ if (running && resumedSinceSeen && tab._automationResumes !== undefined) appendStatus(tab, 'Resumed after the server restart — the run continues here.');
10699
+ tab._automationResumes = Math.max(tab._automationResumes || 0, resumes);
10700
+ if (suspended) _showRunSuspended(tab, run);
10701
+ else tab._automationSuspendedAt = null;
10479
10702
  // (A dispatched worker's failure, with its reason, is on its end card.)
10480
10703
  if (run.status === 'failed' && run.error && !isDispatchedRun(run) && tab._automationLifecycleError !== run.error) {
10481
10704
  tab._automationLifecycleError = run.error;
10482
10705
  appendError(tab, run.error);
10483
10706
  }
10707
+ syncWorkerQuestionQueue(tab, run, {
10708
+ open: (id, pending) => handleControlRequest(tab, { request_id: id, request: { tool_name: 'AskUserQuestion', input: pending.input || { questions: pending.questions } } }),
10709
+ close: (id) => {
10710
+ const card = tab.messagesEl?.querySelector(`[data-worker-question-id="${CSS.escape(id)}"]`);
10711
+ card?.querySelectorAll('button, input, textarea').forEach((el) => { el.disabled = true; });
10712
+ const submit = card?.querySelector('.ask-submit');
10713
+ if (submit) submit.textContent = t('workerQuestion.resolved');
10714
+ tab.pendingAskRequestId = null; tab.pendingAskToolUseId = null;
10715
+ tab.pendingAskBufferedAnswer = null; tab.askRenderedViaControl = false;
10716
+ // The same provider turn resumes; release messages buffered while its question was open.
10717
+ _flushMsgBuffer(tab);
10718
+ },
10719
+ });
10484
10720
  trackWorkerEnd(run, { apply: (view) => showWorkerEnd(tab, view), busy: hasLiveBackgroundWork(tab) });
10485
10721
  // The mode control is hidden while the run is active and back when it ended.
10486
10722
  if (tab === activeTab()) populateModeDropdown(_panel?.querySelector('#cp-mode'), tab);
@@ -10527,6 +10763,64 @@ function hasLiveBackgroundWork(tab) {
10527
10763
  return false;
10528
10764
  }
10529
10765
 
10766
+ const LOST_TURN_LINE = 'Connection lost — this turn did not survive the disconnect.';
10767
+ // What a tab says when the turn it showed did not survive a lost connection. When its session is a dispatched worker's
10768
+ // (a tab opened on that session), the line says what became of the run: resumed after a server restart (in its own
10769
+ // tab), or how it ended, with its reason. A tab the user continued by hand after its run ended keeps the generic line:
10770
+ // the turn that was cut off was the user's own.
10771
+ async function _lostTurnLine(tab) {
10772
+ if (!tab?.sessionId || tab.releasedRunId) return LOST_TURN_LINE;
10773
+ try {
10774
+ const res = await fetch('/api/sidepanel-runs');
10775
+ const runs = res.ok ? ((await res.json())?.runs || []) : [];
10776
+ const run = runs.find((item) => item?.providerSessionId === tab.sessionId && isDispatchedRun(item));
10777
+ if (!run) return LOST_TURN_LINE;
10778
+ if (run.status === 'suspended') return 'SynaBun restarted — this worker run is being resumed in its own tab.';
10779
+ if (run.status === 'starting' || run.status === 'running') {
10780
+ return Number(run.resumes) > 0 ? 'SynaBun restarted — this worker run was resumed and continues in its own tab.' : LOST_TURN_LINE;
10781
+ }
10782
+ let dispatch = null;
10783
+ try {
10784
+ const reply = await fetch(`/api/assistant/runs/${encodeURIComponent(run.runId)}`);
10785
+ if (reply.ok) dispatch = (await reply.json())?.run || null;
10786
+ } catch {}
10787
+ const view = workerEndView(run, dispatch);
10788
+ return view ? `This session's worker run ended: ${view.title}${view.detail ? ` — ${view.detail}` : ''}` : LOST_TURN_LINE;
10789
+ } catch {
10790
+ return LOST_TURN_LINE;
10791
+ }
10792
+ }
10793
+
10794
+ // A run tab whose run the server suspended as it restarted (status 'suspended'): its turn was cut off, so the tab
10795
+ // stops looking busy (spinner, a tool row and its timer, background shells: all went with the CLI) and says the run
10796
+ // resumes. Once per suspension.
10797
+ function _showRunSuspended(tab, run) {
10798
+ const key = run?.suspendedAt || 'suspended';
10799
+ if (tab._automationSuspendedAt === key) return;
10800
+ tab._automationSuspendedAt = key;
10801
+ finishTab(tab, true);
10802
+ _closeCutOffTools(tab);
10803
+ appendStatus(tab, 'SynaBun restarted — resuming this run…');
10804
+ }
10805
+
10806
+ // Every tool call of the tab still waiting for its result is closed as cut off, through the path a result takes (the
10807
+ // card's state, its timer, an agent card), and the background work the tab showed is over.
10808
+ function _closeCutOffTools(tab) {
10809
+ const $msgs = tab?.messagesEl;
10810
+ if (!$msgs) return;
10811
+ for (const card of $msgs.querySelectorAll('.tool-card[data-tool-id]')) {
10812
+ if (card.classList.contains('tool-ok') || card.classList.contains('tool-error') || card.dataset.resolved === '1') continue;
10813
+ if (!card.dataset.toolId) continue;
10814
+ try { updateToolResult(tab, { type: 'tool_result', tool_use_id: card.dataset.toolId, is_error: true, content: 'Cut off by the server restart.' }); } catch {}
10815
+ }
10816
+ let shells = false;
10817
+ for (const task of tab.bgTasks?.values?.() || []) {
10818
+ if (task?.status === 'running') { task.status = 'stopped'; shells = true; }
10819
+ }
10820
+ if (shells) { try { renderBgTray(tab); } catch {} }
10821
+ _setBackgroundWork(tab, []);
10822
+ }
10823
+
10530
10824
  // The end of a worker the Assistant dispatched (ui-worker-end.js): one card,
10531
10825
  // the last row of its transcript. Its button closes this tab the way the X
10532
10826
  // does (a warm worker that finished its task is completed, one still at work
@@ -11925,11 +12219,11 @@ function wireEvents() {
11925
12219
  // Side panel browser launch removed — all automations use floating browsers only.
11926
12220
 
11927
12221
  // Show panel without toggling it closed (used by notification click-through)
11928
- on('claude-panel:show', (data) => {
11929
- if (!_visible) toggleClaudePanel();
12222
+ on('claude-panel:show', async (data) => {
12223
+ if (!_visible) await toggleClaudePanel();
11930
12224
  if (data?.tabId) {
11931
- const idx = _tabs.findIndex(t => t.id === data.tabId);
11932
- if (idx >= 0) switchTab(idx);
12225
+ const idx = _tabs.findIndex(t => t.id === data.tabId || t.sessionId === data.tabId);
12226
+ if (idx >= 0 && idx !== _activeTabIdx) switchTab(idx);
11933
12227
  }
11934
12228
  });
11935
12229