synabun 2.0.10 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (517) hide show
  1. package/CHANGELOG.md +86 -0
  2. package/README.md +25 -2
  3. package/THIRD-PARTY-LICENSES.md +22 -0
  4. package/bin/synabun.js +71 -24
  5. package/docs/whatsapp.md +2 -2
  6. package/hooks/claude-code/pre-websearch.mjs +12 -1
  7. package/hooks/claude-code/shared.mjs +5 -0
  8. package/hooks/claude-code/stop.mjs +1 -1
  9. package/lib/claude-hooks.js +172 -18
  10. package/lib/client-config-repair.js +17 -8
  11. package/lib/desktop-app.js +7 -1
  12. package/lib/external-tools.js +1 -1
  13. package/lib/first-launch-protection.js +57 -12
  14. package/lib/mcp-dist.js +176 -0
  15. package/lib/release-npm-ci.mjs +138 -0
  16. package/lib/release-publish.mjs +91 -0
  17. package/lib/release-scan.mjs +50 -0
  18. package/lib/server-crash-record.js +123 -0
  19. package/lib/server-exit.js +318 -0
  20. package/lib/server-supervisor.js +508 -0
  21. package/lib/start-launcher.js +37 -4
  22. package/lib/tools-cli.js +45 -0
  23. package/lib/update-snapshot.js +228 -0
  24. package/lib/virustotal.mjs +192 -0
  25. package/lib/windows-crash-event.js +269 -0
  26. package/mcp-server/dist/http.d.ts +20 -1
  27. package/mcp-server/dist/http.d.ts.map +1 -1
  28. package/mcp-server/dist/http.js +59 -12
  29. package/mcp-server/dist/http.js.map +1 -1
  30. package/mcp-server/dist/index.d.ts +33 -27
  31. package/mcp-server/dist/index.d.ts.map +1 -1
  32. package/mcp-server/dist/index.js +128 -143
  33. package/mcp-server/dist/index.js.map +1 -1
  34. package/mcp-server/dist/preload.js +1 -1
  35. package/mcp-server/dist/preload.js.map +1 -1
  36. package/mcp-server/dist/services/codex-config-heal.d.ts +6 -2
  37. package/mcp-server/dist/services/codex-config-heal.d.ts.map +1 -1
  38. package/mcp-server/dist/services/codex-config-heal.js +81 -28
  39. package/mcp-server/dist/services/codex-config-heal.js.map +1 -1
  40. package/mcp-server/dist/services/memory-judge-backfill.d.ts +6 -0
  41. package/mcp-server/dist/services/memory-judge-backfill.d.ts.map +1 -1
  42. package/mcp-server/dist/services/memory-judge-backfill.js +7 -1
  43. package/mcp-server/dist/services/memory-judge-backfill.js.map +1 -1
  44. package/mcp-server/dist/services/neural-interface.d.ts +16 -10
  45. package/mcp-server/dist/services/neural-interface.d.ts.map +1 -1
  46. package/mcp-server/dist/services/neural-interface.js +67 -11
  47. package/mcp-server/dist/services/neural-interface.js.map +1 -1
  48. package/mcp-server/dist/services/server-instructions.d.ts +7 -2
  49. package/mcp-server/dist/services/server-instructions.d.ts.map +1 -1
  50. package/mcp-server/dist/services/server-instructions.js +20 -62
  51. package/mcp-server/dist/services/server-instructions.js.map +1 -1
  52. package/mcp-server/dist/services/tool-catalog.d.ts +120 -0
  53. package/mcp-server/dist/services/tool-catalog.d.ts.map +1 -0
  54. package/mcp-server/dist/services/tool-catalog.js +339 -0
  55. package/mcp-server/dist/services/tool-catalog.js.map +1 -0
  56. package/mcp-server/dist/services/tool-list-refresh-barrier.d.ts +12 -0
  57. package/mcp-server/dist/services/tool-list-refresh-barrier.d.ts.map +1 -0
  58. package/mcp-server/dist/services/tool-list-refresh-barrier.js +40 -0
  59. package/mcp-server/dist/services/tool-list-refresh-barrier.js.map +1 -0
  60. package/mcp-server/dist/services/tool-list.d.ts +108 -0
  61. package/mcp-server/dist/services/tool-list.d.ts.map +1 -0
  62. package/mcp-server/dist/services/tool-list.js +334 -0
  63. package/mcp-server/dist/services/tool-list.js.map +1 -0
  64. package/mcp-server/dist/services/tool-runtime.d.ts +161 -0
  65. package/mcp-server/dist/services/tool-runtime.d.ts.map +1 -0
  66. package/mcp-server/dist/services/tool-runtime.js +400 -0
  67. package/mcp-server/dist/services/tool-runtime.js.map +1 -0
  68. package/mcp-server/dist/services/whiteboard-geometry.d.ts +26 -2
  69. package/mcp-server/dist/services/whiteboard-geometry.d.ts.map +1 -1
  70. package/mcp-server/dist/services/whiteboard-geometry.js +91 -8
  71. package/mcp-server/dist/services/whiteboard-geometry.js.map +1 -1
  72. package/mcp-server/dist/tools/agents.d.ts +10 -10
  73. package/mcp-server/dist/tools/agents.d.ts.map +1 -1
  74. package/mcp-server/dist/tools/agents.js +22 -13
  75. package/mcp-server/dist/tools/agents.js.map +1 -1
  76. package/mcp-server/dist/tools/browser-batch.d.ts +3 -3
  77. package/mcp-server/dist/tools/image-tools.d.ts +1 -1
  78. package/mcp-server/dist/tools/image-tools.js +1 -1
  79. package/mcp-server/dist/tools/skill-tools.d.ts +505 -0
  80. package/mcp-server/dist/tools/skill-tools.d.ts.map +1 -0
  81. package/mcp-server/dist/tools/skill-tools.js +639 -0
  82. package/mcp-server/dist/tools/skill-tools.js.map +1 -0
  83. package/mcp-server/dist/tools/skill.d.ts +7 -0
  84. package/mcp-server/dist/tools/skill.d.ts.map +1 -0
  85. package/mcp-server/dist/tools/skill.js +11 -0
  86. package/mcp-server/dist/tools/skill.js.map +1 -0
  87. package/mcp-server/dist/tools/tool-groups.d.ts +68 -0
  88. package/mcp-server/dist/tools/tool-groups.d.ts.map +1 -0
  89. package/mcp-server/dist/tools/tool-groups.js +147 -0
  90. package/mcp-server/dist/tools/tool-groups.js.map +1 -0
  91. package/mcp-server/dist/tools/whiteboard-format.d.ts.map +1 -1
  92. package/mcp-server/dist/tools/whiteboard-format.js +7 -4
  93. package/mcp-server/dist/tools/whiteboard-format.js.map +1 -1
  94. package/mcp-server/dist/tools/whiteboard-tools.d.ts +64 -13
  95. package/mcp-server/dist/tools/whiteboard-tools.d.ts.map +1 -1
  96. package/mcp-server/dist/tools/whiteboard-tools.js +30 -5
  97. package/mcp-server/dist/tools/whiteboard-tools.js.map +1 -1
  98. package/mcp-server/package-lock.json +2 -2
  99. package/mcp-server/package.json +1 -1
  100. package/mcp-server/src/http.ts +75 -12
  101. package/mcp-server/src/index.ts +147 -154
  102. package/mcp-server/src/preload.ts +12 -12
  103. package/mcp-server/src/services/codex-config-heal.ts +80 -29
  104. package/mcp-server/src/services/memory-judge-backfill.ts +7 -1
  105. package/mcp-server/src/services/neural-interface.ts +79 -20
  106. package/mcp-server/src/services/server-instructions.ts +27 -63
  107. package/mcp-server/src/services/tool-catalog.ts +403 -0
  108. package/mcp-server/src/services/tool-list-refresh-barrier.ts +33 -0
  109. package/mcp-server/src/services/tool-list.ts +410 -0
  110. package/mcp-server/src/services/tool-runtime.ts +457 -0
  111. package/mcp-server/src/services/whiteboard-geometry.ts +94 -9
  112. package/mcp-server/src/tools/agents.ts +22 -14
  113. package/mcp-server/src/tools/image-tools.ts +1 -1
  114. package/mcp-server/src/tools/skill-tools.ts +589 -0
  115. package/mcp-server/src/tools/skill.ts +13 -0
  116. package/mcp-server/src/tools/tool-groups.ts +164 -0
  117. package/mcp-server/src/tools/whiteboard-format.ts +9 -3
  118. package/mcp-server/src/tools/whiteboard-tools.ts +39 -5
  119. package/neural-interface/i18n/en.json +3463 -200
  120. package/neural-interface/i18n/pt-BR.json +3411 -151
  121. package/neural-interface/lib/assistant-api.js +127 -28
  122. package/neural-interface/lib/assistant-brains/claude.js +4 -3
  123. package/neural-interface/lib/assistant-brains/codex-browser-policy-hook.mjs +12 -2
  124. package/neural-interface/lib/assistant-brains/codex-route-gate-hook.mjs +32 -9
  125. package/neural-interface/lib/assistant-brains/codex.js +9 -5
  126. package/neural-interface/lib/assistant-brains/opencode.js +120 -54
  127. package/neural-interface/lib/assistant-brains/opencode2-browser-policy/index.js +28 -13
  128. package/neural-interface/lib/assistant-brains/opencode2-route-gate/index.js +59 -19
  129. package/neural-interface/lib/assistant-catalog.js +6 -7
  130. package/neural-interface/lib/assistant-dispatch.js +871 -156
  131. package/neural-interface/lib/assistant-envelope.js +3 -1
  132. package/neural-interface/lib/assistant-persona.js +80 -11
  133. package/neural-interface/lib/assistant-plan-permissions.js +10 -9
  134. package/neural-interface/lib/assistant-route-gate-codex.js +3 -0
  135. package/neural-interface/lib/assistant-route-gate.js +6 -4
  136. package/neural-interface/lib/assistant-router.js +13 -8
  137. package/neural-interface/lib/assistant-runtime.js +56 -18
  138. package/neural-interface/lib/assistant-task-prompt.js +78 -14
  139. package/neural-interface/lib/assistant-tools.js +51 -0
  140. package/neural-interface/lib/backup-service.js +620 -3
  141. package/neural-interface/lib/backup-zip-reader.js +10 -5
  142. package/neural-interface/lib/backup-zip-writer.js +257 -0
  143. package/neural-interface/lib/browser-execution.js +10 -2
  144. package/neural-interface/lib/browser-guard-cdp.js +83 -0
  145. package/neural-interface/lib/browser-network-guard.js +284 -0
  146. package/neural-interface/lib/browser-policy-roots.js +64 -0
  147. package/neural-interface/lib/browser-tool-policy.js +186 -31
  148. package/neural-interface/lib/claude-agent-bridge.js +4 -2
  149. package/neural-interface/lib/claude-hooks-status.js +103 -0
  150. package/neural-interface/lib/claude-temporary.js +5 -11
  151. package/neural-interface/lib/cli-install.js +18 -138
  152. package/neural-interface/lib/cli-resolver.js +4 -1
  153. package/neural-interface/lib/cli-update-planner.js +32 -31
  154. package/neural-interface/lib/cloudflared/tunnel.js +42 -2
  155. package/neural-interface/lib/codex-mcp-approvals.js +11 -0
  156. package/neural-interface/lib/codex-worker-app-server.js +172 -0
  157. package/neural-interface/lib/confined-fs.js +30 -3
  158. package/neural-interface/lib/crash-notice-api.js +98 -0
  159. package/neural-interface/lib/editor-completion-opencode.js +68 -68
  160. package/neural-interface/lib/exec-loop-command.js +4 -8
  161. package/neural-interface/lib/http-guards.js +8 -2
  162. package/neural-interface/lib/install-details.js +4 -1
  163. package/neural-interface/lib/mcp-agent-servers.js +67 -0
  164. package/neural-interface/lib/mcp-client-config.js +0 -29
  165. package/neural-interface/lib/mcp-external-servers.js +323 -0
  166. package/neural-interface/lib/{mcp-profile-refresh-coordinator.js → mcp-tools-refresh-coordinator.js} +4 -4
  167. package/neural-interface/lib/morelogin.js +30 -0
  168. package/neural-interface/lib/native-loop-providers.js +547 -47
  169. package/neural-interface/lib/native-loop-runtime.js +268 -41
  170. package/neural-interface/lib/opencode-config.js +557 -0
  171. package/neural-interface/lib/opencode-db-schema.js +17 -34
  172. package/neural-interface/lib/opencode-history.js +48 -218
  173. package/neural-interface/lib/opencode-mcp-ready.js +108 -0
  174. package/neural-interface/lib/opencode-native-boundary.js +44 -0
  175. package/neural-interface/lib/opencode-protocol.js +249 -112
  176. package/neural-interface/lib/opencode-runtime-config.js +32 -22
  177. package/neural-interface/lib/opencode-serve-records.js +39 -21
  178. package/neural-interface/lib/opencode-v2-client.js +48 -153
  179. package/neural-interface/lib/opencode-v2-ws-requests.js +36 -210
  180. package/neural-interface/lib/opencode-worker-data.js +27 -0
  181. package/neural-interface/lib/opencode2-client.js +73 -37
  182. package/neural-interface/lib/opencode2-proxy.js +23 -63
  183. package/neural-interface/lib/opencode2-translate.js +51 -2
  184. package/neural-interface/lib/owner-authority.js +249 -0
  185. package/neural-interface/lib/page-session.js +275 -0
  186. package/neural-interface/lib/peer-identity.js +763 -0
  187. package/neural-interface/lib/request-gate.js +17 -2
  188. package/neural-interface/lib/rulesets/api.js +1 -1
  189. package/neural-interface/lib/rulesets/fs-safe.js +1 -1
  190. package/neural-interface/lib/rulesets/installer.js +92 -27
  191. package/neural-interface/lib/rulesets/json-edit.js +1 -1
  192. package/neural-interface/lib/session-cache-builder.js +17 -30
  193. package/neural-interface/lib/session-title-generator.js +58 -36
  194. package/neural-interface/lib/skills/api-v2.js +495 -0
  195. package/neural-interface/lib/skills/bundle.js +418 -0
  196. package/neural-interface/lib/skills/catalog.js +294 -0
  197. package/neural-interface/lib/skills/creation-api.js +191 -0
  198. package/neural-interface/lib/skills/creation-brief.js +247 -0
  199. package/neural-interface/lib/skills/creation-sources.js +267 -0
  200. package/neural-interface/lib/skills/creation.js +843 -0
  201. package/neural-interface/lib/skills/deploy.js +564 -0
  202. package/neural-interface/lib/skills/digest.js +119 -0
  203. package/neural-interface/lib/skills/discover.js +111 -0
  204. package/neural-interface/lib/skills/draft-confinement.js +214 -0
  205. package/neural-interface/lib/skills/eval-confinement.js +397 -0
  206. package/neural-interface/lib/skills/eval-sandbox.js +280 -0
  207. package/neural-interface/lib/skills/eval-trace.js +119 -0
  208. package/neural-interface/lib/skills/evals-api.js +212 -0
  209. package/neural-interface/lib/skills/evals.js +1573 -0
  210. package/neural-interface/lib/skills/export.js +200 -0
  211. package/neural-interface/lib/skills/frontmatter.js +64 -0
  212. package/neural-interface/lib/skills/health.js +75 -0
  213. package/neural-interface/lib/skills/json-edit.js +165 -0
  214. package/neural-interface/lib/skills/known-bridge-digests.json +387 -0
  215. package/neural-interface/lib/skills/known-bridges.js +96 -0
  216. package/neural-interface/lib/skills/library.js +823 -0
  217. package/neural-interface/lib/skills/local-store.js +21 -0
  218. package/neural-interface/lib/skills/migration.js +433 -0
  219. package/neural-interface/lib/skills/operations.js +45 -0
  220. package/neural-interface/lib/skills/ownership.js +427 -0
  221. package/neural-interface/lib/skills/packages.js +88 -0
  222. package/neural-interface/lib/skills/reviews.js +212 -0
  223. package/neural-interface/lib/skills/revision.js +80 -0
  224. package/neural-interface/lib/skills/roots.js +262 -0
  225. package/neural-interface/lib/skills/runtime-checks.js +355 -0
  226. package/neural-interface/lib/skills/scan-assets.js +252 -0
  227. package/neural-interface/lib/skills/scan-context.js +26 -0
  228. package/neural-interface/lib/skills/scan-shell.js +324 -0
  229. package/neural-interface/lib/skills/scan.js +364 -0
  230. package/neural-interface/lib/skills/scanners.js +54 -0
  231. package/neural-interface/lib/skills/service.js +522 -0
  232. package/neural-interface/lib/skills/telemetry.js +113 -0
  233. package/neural-interface/lib/sqlite-integrity-worker.js +22 -0
  234. package/neural-interface/lib/tool-lists.js +124 -0
  235. package/neural-interface/lib/whatsapp/bridge.js +6 -3
  236. package/neural-interface/lib/worker-confinement.js +1192 -0
  237. package/neural-interface/lib/worker-guard-setting.js +56 -0
  238. package/neural-interface/lib/worker-mcp-channel.js +175 -0
  239. package/neural-interface/lib/worker-relay-ports.js +227 -0
  240. package/neural-interface/lib/worker-resume.js +229 -0
  241. package/neural-interface/lib/worker-sandbox.js +299 -0
  242. package/neural-interface/package-lock.json +18 -12
  243. package/neural-interface/package.json +2 -2
  244. package/neural-interface/public/index.html +3 -1
  245. package/neural-interface/public/index2d.html +19 -11
  246. package/neural-interface/public/onboarding.html +2 -2
  247. package/neural-interface/public/shared/account-menu.js +8 -5
  248. package/neural-interface/public/shared/agent-runtime-options.js +35 -19
  249. package/neural-interface/public/shared/api.js +1527 -1336
  250. package/neural-interface/public/shared/app-frame.js +192 -0
  251. package/neural-interface/public/shared/assistant/asst-agents-dock.js +33 -2
  252. package/neural-interface/public/shared/assistant/asst-ask-styles.js +270 -0
  253. package/neural-interface/public/shared/assistant/asst-ask-tail.js +118 -0
  254. package/neural-interface/public/shared/assistant/asst-ask.js +397 -0
  255. package/neural-interface/public/shared/assistant/asst-brain-picker.js +49 -35
  256. package/neural-interface/public/shared/assistant/asst-control.js +157 -26
  257. package/neural-interface/public/shared/assistant/asst-followups-card.js +383 -0
  258. package/neural-interface/public/shared/assistant/asst-followups.js +417 -0
  259. package/neural-interface/public/shared/assistant/asst-limit-dock.js +40 -0
  260. package/neural-interface/public/shared/assistant/asst-limit-wait.js +294 -0
  261. package/neural-interface/public/shared/assistant/asst-limit.js +168 -0
  262. package/neural-interface/public/shared/assistant/asst-mascot.js +49 -12
  263. package/neural-interface/public/shared/assistant/asst-menu.js +7 -6
  264. package/neural-interface/public/shared/assistant/asst-panel.js +106 -11
  265. package/neural-interface/public/shared/assistant/asst-rack-styles.js +66 -49
  266. package/neural-interface/public/shared/assistant/asst-render.js +401 -77
  267. package/neural-interface/public/shared/assistant/asst-route.js +83 -12
  268. package/neural-interface/public/shared/assistant/asst-scenes.js +2 -0
  269. package/neural-interface/public/shared/assistant/asst-sidepanel.js +67 -4
  270. package/neural-interface/public/shared/assistant/asst-slash.js +9 -7
  271. package/neural-interface/public/shared/assistant/asst-state.js +11 -15
  272. package/neural-interface/public/shared/assistant/asst-stop.js +127 -0
  273. package/neural-interface/public/shared/assistant/asst-styles.js +59 -21
  274. package/neural-interface/public/shared/assistant/asst-tool-kinds.js +156 -16
  275. package/neural-interface/public/shared/automation-context.js +5 -2
  276. package/neural-interface/public/shared/cc-dropdown.js +89 -19
  277. package/neural-interface/public/shared/cdx/cdx-context-menu.js +10 -9
  278. package/neural-interface/public/shared/cdx/cdx-copy.js +16 -0
  279. package/neural-interface/public/shared/cdx/cdx-output-model.js +22 -1
  280. package/neural-interface/public/shared/cdx/cdx-output.js +8 -7
  281. package/neural-interface/public/shared/cdx/cdx-panel.js +45 -149
  282. package/neural-interface/public/shared/cdx/cdx-protocol.js +28 -1
  283. package/neural-interface/public/shared/cdx/cdx-render.js +47 -29
  284. package/neural-interface/public/shared/cdx/cdx-requests.js +3 -3
  285. package/neural-interface/public/shared/cdx/cdx-skill-hints.js +54 -0
  286. package/neural-interface/public/shared/cdx/cdx-styles.js +11 -3
  287. package/neural-interface/public/shared/cdx/cdx-tabs.js +52 -39
  288. package/neural-interface/public/shared/cdx/cdx-ws.js +0 -1
  289. package/neural-interface/public/shared/cli-install-view.js +4 -8
  290. package/neural-interface/public/shared/cli-status.js +2 -2
  291. package/neural-interface/public/shared/constants.js +2 -2
  292. package/neural-interface/public/shared/context-menu-areas.js +34 -0
  293. package/neural-interface/public/shared/context-menu-logic.js +243 -0
  294. package/neural-interface/public/shared/context-menu-model.js +366 -0
  295. package/neural-interface/public/shared/context-menu.js +372 -0
  296. package/neural-interface/public/shared/cp/cp-context-menu.js +10 -9
  297. package/neural-interface/public/shared/crash-notice-view.js +168 -0
  298. package/neural-interface/public/shared/desktop-update-view.js +20 -0
  299. package/neural-interface/public/shared/editor/editor-panel-logic.js +2 -2
  300. package/neural-interface/public/shared/html-shell.js +9 -11
  301. package/neural-interface/public/shared/i18n.js +2 -0
  302. package/neural-interface/public/shared/ocp-v2/ocp-v2-approvals.js +7 -7
  303. package/neural-interface/public/shared/ocp-v2/ocp-v2-binding.js +4 -4
  304. package/neural-interface/public/shared/ocp-v2/ocp-v2-changes-logic.js +3 -101
  305. package/neural-interface/public/shared/ocp-v2/ocp-v2-childpanel.js +2 -0
  306. package/neural-interface/public/shared/ocp-v2/ocp-v2-compact-button.js +1 -3
  307. package/neural-interface/public/shared/ocp-v2/ocp-v2-composer-logic.js +2 -6
  308. package/neural-interface/public/shared/ocp-v2/ocp-v2-context-menu.js +11 -11
  309. package/neural-interface/public/shared/ocp-v2/ocp-v2-context-model.js +0 -3
  310. package/neural-interface/public/shared/ocp-v2/ocp-v2-events.js +8 -4
  311. package/neural-interface/public/shared/ocp-v2/ocp-v2-manager.js +7 -7
  312. package/neural-interface/public/shared/ocp-v2/ocp-v2-outcome-notifications.js +16 -0
  313. package/neural-interface/public/shared/ocp-v2/ocp-v2-panel.js +47 -252
  314. package/neural-interface/public/shared/ocp-v2/ocp-v2-plan.js +1 -2
  315. package/neural-interface/public/shared/ocp-v2/ocp-v2-projectbar.js +95 -202
  316. package/neural-interface/public/shared/ocp-v2/ocp-v2-render.js +6 -21
  317. package/neural-interface/public/shared/ocp-v2/ocp-v2-send.js +4 -4
  318. package/neural-interface/public/shared/ocp-v2/ocp-v2-session-actions.js +8 -61
  319. package/neural-interface/public/shared/ocp-v2/ocp-v2-sessions-logic.js +2 -91
  320. package/neural-interface/public/shared/ocp-v2/ocp-v2-slash-hints.js +16 -15
  321. package/neural-interface/public/shared/ocp-v2/ocp-v2-state.js +8 -21
  322. package/neural-interface/public/shared/ocp-v2/ocp-v2-status-logic.js +1 -99
  323. package/neural-interface/public/shared/ocp-v2/ocp-v2-status.js +5 -35
  324. package/neural-interface/public/shared/ocp-v2/ocp-v2-styles.js +0 -21
  325. package/neural-interface/public/shared/ocp-v2/{ocp-v2-profile-inheritance.js → ocp-v2-tools-inheritance.js} +16 -16
  326. package/neural-interface/public/shared/ocp-v2/ocp-v2-ws.js +14 -47
  327. package/neural-interface/public/shared/page-session.js +96 -0
  328. package/neural-interface/public/shared/registry.js +5 -3
  329. package/neural-interface/public/shared/retired-2d.js +3 -0
  330. package/neural-interface/public/shared/schedules-view.js +195 -61
  331. package/neural-interface/public/shared/settings/settings-claude-hooks.js +135 -0
  332. package/neural-interface/public/shared/settings/settings-data.js +3 -1
  333. package/neural-interface/public/shared/settings/settings-ia.js +6 -6
  334. package/neural-interface/public/shared/settings/settings-opencode.js +89 -0
  335. package/neural-interface/public/shared/settings/settings-skill-folders-pane.js +265 -0
  336. package/neural-interface/public/shared/settings/settings-skill-folders.js +130 -0
  337. package/neural-interface/public/shared/settings/settings-worker-guard.js +72 -0
  338. package/neural-interface/public/shared/skills/ss-args.js +96 -0
  339. package/neural-interface/public/shared/skills/ss-creation.js +219 -0
  340. package/neural-interface/public/shared/skills/ss-dialog.js +130 -0
  341. package/neural-interface/public/shared/skills/ss-doc.js +173 -0
  342. package/neural-interface/public/shared/skills/ss-eval-adapters.js +269 -0
  343. package/neural-interface/public/shared/skills/ss-eval-grade.js +359 -0
  344. package/neural-interface/public/shared/skills/ss-eval-optimise.js +153 -0
  345. package/neural-interface/public/shared/skills/ss-eval-plan.js +315 -0
  346. package/neural-interface/public/shared/skills/ss-evals.js +417 -0
  347. package/neural-interface/public/shared/skills/ss-form.js +159 -0
  348. package/neural-interface/public/shared/skills/ss-frontmatter.js +376 -0
  349. package/neural-interface/public/shared/skills/ss-ids.js +32 -0
  350. package/neural-interface/public/shared/skills/ss-lint.js +141 -0
  351. package/neural-interface/public/shared/skills/ss-presets.js +117 -0
  352. package/neural-interface/public/shared/skills/ss-profiles.js +475 -0
  353. package/neural-interface/public/shared/skills/ss-resources.js +165 -0
  354. package/neural-interface/public/shared/skills/ss-review-view.js +190 -0
  355. package/neural-interface/public/shared/skills/ss-tabs.js +79 -0
  356. package/neural-interface/public/shared/skills/ss-unicode.js +135 -0
  357. package/neural-interface/public/shared/skills/studio/sx-actions.js +222 -0
  358. package/neural-interface/public/shared/skills/studio/sx-cm.js +101 -0
  359. package/neural-interface/public/shared/skills/studio/sx-create-ai-view.js +749 -0
  360. package/neural-interface/public/shared/skills/studio/sx-create-ai.js +236 -0
  361. package/neural-interface/public/shared/skills/studio/sx-create-view.js +254 -0
  362. package/neural-interface/public/shared/skills/studio/sx-create.js +126 -0
  363. package/neural-interface/public/shared/skills/studio/sx-diff-view.js +43 -0
  364. package/neural-interface/public/shared/skills/studio/sx-diff.js +133 -0
  365. package/neural-interface/public/shared/skills/studio/sx-docs.js +480 -0
  366. package/neural-interface/public/shared/skills/studio/sx-dom.js +229 -0
  367. package/neural-interface/public/shared/skills/studio/sx-edit-view.js +848 -0
  368. package/neural-interface/public/shared/skills/studio/sx-editor-model.js +193 -0
  369. package/neural-interface/public/shared/skills/studio/sx-errors.js +22 -0
  370. package/neural-interface/public/shared/skills/studio/sx-geometry.js +82 -0
  371. package/neural-interface/public/shared/skills/studio/sx-import.js +262 -0
  372. package/neural-interface/public/shared/skills/studio/sx-inspector.js +308 -0
  373. package/neural-interface/public/shared/skills/studio/sx-install-flow.js +279 -0
  374. package/neural-interface/public/shared/skills/studio/sx-install.js +296 -0
  375. package/neural-interface/public/shared/skills/studio/sx-library-view.js +639 -0
  376. package/neural-interface/public/shared/skills/studio/sx-library.js +475 -0
  377. package/neural-interface/public/shared/skills/studio/sx-main.js +681 -0
  378. package/neural-interface/public/shared/skills/studio/sx-operations-view.js +98 -0
  379. package/neural-interface/public/shared/skills/studio/sx-operations.js +11 -0
  380. package/neural-interface/public/shared/skills/studio/sx-plan.js +158 -0
  381. package/neural-interface/public/shared/skills/studio/sx-publish-view.js +621 -0
  382. package/neural-interface/public/shared/skills/studio/sx-review-view.js +369 -0
  383. package/neural-interface/public/shared/skills/studio/sx-review.js +148 -0
  384. package/neural-interface/public/shared/skills/studio/sx-scan.js +35 -0
  385. package/neural-interface/public/shared/skills/studio/sx-snippets.js +96 -0
  386. package/neural-interface/public/shared/skills/studio/sx-subject.js +248 -0
  387. package/neural-interface/public/shared/skills/studio/sx-test-view.js +994 -0
  388. package/neural-interface/public/shared/skills/studio/sx-test.js +328 -0
  389. package/neural-interface/public/shared/skills/studio/sx-versions-view.js +241 -0
  390. package/neural-interface/public/shared/skills/studio/sx-workspace-view.js +425 -0
  391. package/neural-interface/public/shared/styleguide/sg-panel.js +4 -2
  392. package/neural-interface/public/shared/styles.css +1525 -1779
  393. package/neural-interface/public/shared/synabun-mascot.js +1115 -9
  394. package/neural-interface/public/shared/tool-list-picker.js +55 -0
  395. package/neural-interface/public/shared/tool-list-storage.js +24 -0
  396. package/neural-interface/public/shared/tool-list-view.js +112 -0
  397. package/neural-interface/public/shared/tour/tour-actor.js +268 -0
  398. package/neural-interface/public/shared/tour/tour-beats.js +336 -0
  399. package/neural-interface/public/shared/tour/tour-engine.js +884 -0
  400. package/neural-interface/public/shared/tour/tour-ink.js +390 -0
  401. package/neural-interface/public/shared/tour/tour-stage.js +440 -0
  402. package/neural-interface/public/shared/tour/tour-state.js +40 -0
  403. package/neural-interface/public/shared/tour/tour-tips.js +99 -0
  404. package/neural-interface/public/shared/tour/tour.css +144 -0
  405. package/neural-interface/public/shared/ui-automation-studio.js +91 -128
  406. package/neural-interface/public/shared/ui-claude-panel.js +77 -5
  407. package/neural-interface/public/shared/ui-command-runner.js +6 -3
  408. package/neural-interface/public/shared/ui-context-menu.js +295 -0
  409. package/neural-interface/public/shared/ui-cost-widget.js +57 -59
  410. package/neural-interface/public/shared/ui-crash-notice.js +187 -0
  411. package/neural-interface/public/shared/ui-detail.js +15 -3
  412. package/neural-interface/public/shared/ui-image-gallery.js +50 -47
  413. package/neural-interface/public/shared/ui-keybinds.js +26 -1
  414. package/neural-interface/public/shared/ui-menubar.js +8 -18
  415. package/neural-interface/public/shared/ui-native-loop-router-state.js +5 -0
  416. package/neural-interface/public/shared/ui-native-loop-router.js +7 -1
  417. package/neural-interface/public/shared/ui-navbar.js +4 -24
  418. package/neural-interface/public/shared/ui-panels.js +96 -65
  419. package/neural-interface/public/shared/ui-schedules-studio.js +507 -423
  420. package/neural-interface/public/shared/ui-sessions.js +5 -3
  421. package/neural-interface/public/shared/ui-settings.js +594 -462
  422. package/neural-interface/public/shared/ui-sidepanel-sessions.js +1 -1
  423. package/neural-interface/public/shared/ui-sidepanel-state.js +23 -12
  424. package/neural-interface/public/shared/ui-sidepanel-windows.js +16 -11
  425. package/neural-interface/public/shared/ui-skills.js +33 -2830
  426. package/neural-interface/public/shared/ui-styleguide.js +5 -2
  427. package/neural-interface/public/shared/ui-sync.js +15 -5
  428. package/neural-interface/public/shared/ui-terminal.js +124 -180
  429. package/neural-interface/public/shared/ui-tool-updates.js +3 -1
  430. package/neural-interface/public/shared/ui-tooltip.js +10 -5
  431. package/neural-interface/public/shared/ui-trash.js +6 -4
  432. package/neural-interface/public/shared/ui-tutorial-layout.js +3 -117
  433. package/neural-interface/public/shared/ui-tutorial-preview.js +103 -0
  434. package/neural-interface/public/shared/ui-tutorial-steps.js +3 -65
  435. package/neural-interface/public/shared/ui-tutorial.css +47 -100
  436. package/neural-interface/public/shared/ui-tutorial.js +285 -509
  437. package/neural-interface/public/shared/ui-update.js +38 -2
  438. package/neural-interface/public/shared/ui-whiteboard.js +311 -58
  439. package/neural-interface/public/shared/ui-worker-end.js +4 -0
  440. package/neural-interface/public/shared/ui-workspaces.js +3 -2
  441. package/neural-interface/public/shared/view-variant.js +66 -0
  442. package/neural-interface/public/shared/whatsapp/wa-view.js +1 -1
  443. package/neural-interface/public/shared/whiteboard-list.js +172 -0
  444. package/neural-interface/public/shared/worker-end.js +10 -3
  445. package/neural-interface/public/shared/worker-restart.js +12 -0
  446. package/neural-interface/public/sidepanel-session.html +1 -1
  447. package/neural-interface/public/variant/2d/background.js +2 -1
  448. package/neural-interface/public/variant/2d/context-menu.js +8 -2
  449. package/neural-interface/public/variant/2d/graph.js +42 -20
  450. package/neural-interface/public/variant/2d/hulls.js +14 -9
  451. package/neural-interface/public/variant/2d/lasso.js +11 -7
  452. package/neural-interface/public/variant/2d/main.js +32 -1
  453. package/neural-interface/public/variant/2d/minimap.js +11 -13
  454. package/neural-interface/public/variant/3d/main.js +69 -6
  455. package/neural-interface/public/variant/3d/map-controls.js +24 -9
  456. package/neural-interface/public/vendor/caveat/OFL.txt +93 -0
  457. package/neural-interface/public/vendor/caveat/caveat-latin-400-normal.woff2 +0 -0
  458. package/neural-interface/public/vendor/caveat/caveat-latin-700-normal.woff2 +0 -0
  459. package/neural-interface/public/vendor/caveat/caveat.css +17 -0
  460. package/neural-interface/public/vendor/caveat/manifest.json +12 -0
  461. package/neural-interface/public/vendor/yaml/LICENSE +13 -0
  462. package/neural-interface/public/vendor/yaml/manifest.json +4 -0
  463. package/neural-interface/public/vendor/yaml/yaml-2.9.1.min.js +137 -0
  464. package/neural-interface/scripts/build-vendor.mjs +32 -0
  465. package/neural-interface/server.js +3934 -2454
  466. package/neural-interface/templates/rulesets/core.md +1 -1
  467. package/neural-interface/templates/rulesets/legacy-hashes.json +18 -0
  468. package/neural-interface/templates/rulesets/manifest.json +8 -8
  469. package/npm-shrinkwrap.json +18 -12
  470. package/package.json +16 -3
  471. package/postinstall.js +9 -4
  472. package/preuninstall.js +1 -1
  473. package/setup.js +97 -322
  474. package/skills/skill-forge/SKILL.md +177 -0
  475. package/skills/skill-forge/evals/evals.json +54 -0
  476. package/skills/skill-forge/references/package.md +105 -0
  477. package/skills/skill-forge/references/review-and-recovery.md +63 -0
  478. package/skills/skill-forge/references/runtime-profiles.md +20 -0
  479. package/skills/skill-forge/references/sources-and-capabilities.md +51 -0
  480. package/skills/synabun/SKILL.md +10 -7
  481. package/skills/synabun/modules/audit.md +1 -5
  482. package/skills/synabun/modules/changelog.md +1 -5
  483. package/skills/synabun/modules/idea.md +1 -1
  484. package/skills/synabun/modules/memorize.md +6 -9
  485. package/skills/synabun/modules/schedule.md +4 -4
  486. package/mcp-server/dist/services/profiles.d.ts +0 -120
  487. package/mcp-server/dist/services/profiles.d.ts.map +0 -1
  488. package/mcp-server/dist/services/profiles.js +0 -387
  489. package/mcp-server/dist/services/profiles.js.map +0 -1
  490. package/mcp-server/dist/tools/leonardo-browser-tools.d.ts +0 -97
  491. package/mcp-server/dist/tools/leonardo-browser-tools.d.ts.map +0 -1
  492. package/mcp-server/dist/tools/leonardo-browser-tools.js +0 -228
  493. package/mcp-server/dist/tools/leonardo-browser-tools.js.map +0 -1
  494. package/mcp-server/dist/tools/leonardo.d.ts +0 -13
  495. package/mcp-server/dist/tools/leonardo.d.ts.map +0 -1
  496. package/mcp-server/dist/tools/leonardo.js +0 -21
  497. package/mcp-server/dist/tools/leonardo.js.map +0 -1
  498. package/mcp-server/dist/tools/profile.d.ts +0 -17
  499. package/mcp-server/dist/tools/profile.d.ts.map +0 -1
  500. package/mcp-server/dist/tools/profile.js +0 -114
  501. package/mcp-server/dist/tools/profile.js.map +0 -1
  502. package/mcp-server/src/services/profiles.ts +0 -428
  503. package/mcp-server/src/tools/leonardo-browser-tools.ts +0 -291
  504. package/mcp-server/src/tools/leonardo.ts +0 -30
  505. package/mcp-server/src/tools/profile.ts +0 -122
  506. package/neural-interface/lib/assistant-brains/opencode-browser-policy.js +0 -47
  507. package/neural-interface/lib/assistant-brains/opencode-route-gate.js +0 -62
  508. package/neural-interface/lib/mcp-profile-servers.js +0 -120
  509. package/neural-interface/public/shared/ui-tutorial-draw.js +0 -311
  510. package/skills/leonardo/SKILL.md +0 -168
  511. package/skills/leonardo/icon.svg +0 -1
  512. package/skills/leonardo/modules/browser-guide.md +0 -378
  513. package/skills/leonardo/modules/image-prompter.md +0 -283
  514. package/skills/leonardo/modules/model-advisor.md +0 -100
  515. package/skills/leonardo/modules/prompt-library.md +0 -110
  516. package/skills/leonardo/modules/style-guide.md +0 -186
  517. package/skills/leonardo/modules/video-prompter.md +0 -317
@@ -1,3 +1,5 @@
1
+ import { retainExternalRefs } from './mcp-external-servers.js';
2
+ import { storedToolList, toolListRecord, readStoredToolListRecord } from './tool-lists.js';
1
3
  import { existsSync, mkdirSync, readFileSync, renameSync, unlinkSync, writeFileSync } from 'node:fs';
2
4
  import { dirname, resolve } from 'node:path';
3
5
 
@@ -30,7 +32,9 @@ function cleanDescriptor(record) {
30
32
  contextMode: record.contextMode || null,
31
33
  modelContextWindow: record.modelContextWindow || null,
32
34
  effort: record.effort || null,
33
- mcpProfile: record.mcpProfile || null,
35
+ tools: storedToolList(record),
36
+ externalMcpVersion: 2,
37
+ ...(record.toolListError ? { toolListError: record.toolListError } : {}),
34
38
  accountId: record.accountId || null,
35
39
  currentIteration: record.currentIteration || 0,
36
40
  totalIterations: record.totalIterations || 0,
@@ -53,9 +57,18 @@ function cleanDescriptor(record) {
53
57
  // lastResult.status): an idle warm worker whose turn ended asking waits on the user.
54
58
  outcome: record.outcome || null,
55
59
  assistantSessionId: record.assistantSessionId || null,
60
+ // A dispatched worker a server restart cut off (status 'suspended') waits for its resume on the next start;
61
+ // `resumes` counts how often it was resumed (assistant-dispatch.js resumeRun).
62
+ suspendedAt: record.suspendedAt || null,
63
+ suspendReason: record.suspendReason || null,
64
+ resumes: Number(record.resumes) || 0,
65
+ resumedAt: record.resumedAt || null,
56
66
  };
57
67
  }
58
68
 
69
+ /** Run statuses that hold a run in the ledger as live: it runs, or waits for its resume after a restart. */
70
+ const LIVE_STATUSES = new Set(['starting', 'running', 'suspended']);
71
+
59
72
  function readJson(path, fallback = null) {
60
73
  try { return JSON.parse(readFileSync(path, 'utf8')); }
61
74
  catch { return fallback; }
@@ -76,6 +89,7 @@ export class NativeLoopRuntime {
76
89
  constructor({
77
90
  stateDir,
78
91
  ledgerPath,
92
+ toolListOptions = () => ({}),
79
93
  providerFactories = {},
80
94
  buildPrompt,
81
95
  onEvent = () => {},
@@ -95,10 +109,14 @@ export class NativeLoopRuntime {
95
109
  // A truthy `met` ends the run early with stoppedReason 'goal_met'; the
96
110
  // iteration and time caps stay the fallback.
97
111
  judgeGoal = null,
112
+ // How long a dispatched worker the restart cut off waits, from this start, for its dispatcher to resume it
113
+ // (or end it) before it ends as interrupted by itself.
114
+ resumeHoldMs = 60_000,
98
115
  } = {}) {
99
116
  if (!stateDir) throw new Error('NativeLoopRuntime requires stateDir');
100
117
  if (typeof buildPrompt !== 'function') throw new Error('NativeLoopRuntime requires buildPrompt');
101
118
  this.stateDir = stateDir;
119
+ this.toolListOptions = toolListOptions;
102
120
  this.judgeGoal = typeof judgeGoal === 'function' ? judgeGoal : null;
103
121
  this.ledgerPath = ledgerPath || resolve(dirname(stateDir), 'native-loop-runs.json');
104
122
  this.providerFactories = { ...providerFactories };
@@ -113,6 +131,8 @@ export class NativeLoopRuntime {
113
131
  this.providerStartupTimeoutMs = providerStartupTimeoutMs;
114
132
  this.claimTtlMs = claimTtlMs;
115
133
  this.ledgerFlushMs = Math.max(50, Number(ledgerFlushMs) || 200);
134
+ this.resumeHoldMs = Math.max(1000, Number(resumeHoldMs) || 60_000);
135
+ this._holdTimers = new Map();
116
136
  this.records = new Map();
117
137
  this._pendingPersist = false;
118
138
  this._persistTimer = null;
@@ -184,8 +204,32 @@ export class NativeLoopRuntime {
184
204
  if (!Array.isArray(rows)) return;
185
205
  for (const row of rows) {
186
206
  if (!row?.runId) continue;
187
- const record = { ...row, adapter: null, timer: null, execution: null };
188
- if (record.status === 'starting' || record.status === 'running') {
207
+ let stored;
208
+ try { stored = readStoredToolListRecord(row, this.toolListOptions()); }
209
+ catch (error) {
210
+ // A historical run may name a custom preset the owner has since deleted. Refuse that
211
+ // run, preserve the original selection for diagnosis, and keep the rest of the server up.
212
+ stored = { ...row, tools: [], status: 'failed', stoppedReason: 'tools_invalid',
213
+ error: error.message, toolListError: { code: error.code, message: error.message,
214
+ original: row.tools ?? row.mcpProfile ?? null } };
215
+ delete stored.mcpProfile;
216
+ }
217
+ const record = { ...stored, adapter: null, timer: null, execution: null };
218
+ const cutOff = record.status === 'starting' || record.status === 'running' || record.status === 'suspended';
219
+ if (cutOff && record.runMode === 'task' && isNativeLoopProfile(record.provider)) {
220
+ // A dispatched Claude Code worker the restart cut off (suspended by the last shutdown, or left running by a
221
+ // crash): held for its dispatcher, which resumes it (assistant-dispatch.js resumeRun) or ends it. Its state file
222
+ // stays as it is. Nobody takes it within resumeHoldMs: it ends as interrupted, as every cut-off run did before.
223
+ record.status = 'suspended';
224
+ record.suspendedAt = record.suspendedAt || iso(this.now());
225
+ record.suspendReason = record.suspendReason || 'server_restart';
226
+ record.turnState = 'suspended';
227
+ record.stopping = false;
228
+ this.records.set(record.runId, record);
229
+ this._hold(record);
230
+ continue;
231
+ }
232
+ if (cutOff) {
189
233
  record.status = 'interrupted';
190
234
  record.stoppedReason = 'server_restart';
191
235
  record.completedAt = record.completedAt || iso(this.now());
@@ -208,9 +252,9 @@ export class NativeLoopRuntime {
208
252
 
209
253
  _writeLedgerNow() {
210
254
  const records = [...this.records.values()];
211
- const active = records.filter((record) => ['starting', 'running'].includes(record.status));
255
+ const active = records.filter((record) => LIVE_STATUSES.has(record.status));
212
256
  const terminal = records
213
- .filter((record) => !['starting', 'running'].includes(record.status))
257
+ .filter((record) => !LIVE_STATUSES.has(record.status))
214
258
  .sort((a, b) => String(b.startedAt || '').localeCompare(String(a.startedAt || '')));
215
259
  const retained = [...active, ...terminal.slice(0, Math.max(0, 500 - active.length))];
216
260
  const retainedIds = new Set(retained.map((record) => record.runId));
@@ -221,7 +265,7 @@ export class NativeLoopRuntime {
221
265
  }
222
266
  }
223
267
  const rows = retained
224
- .map(cleanDescriptor)
268
+ .map(record => toolListRecord(cleanDescriptor(record)))
225
269
  .sort((a, b) => String(b.startedAt || '').localeCompare(String(a.startedAt || '')));
226
270
  try { writeJsonAtomic(this.ledgerPath, rows); }
227
271
  catch (error) { this.log(null, 'ledger:error', error.message); }
@@ -265,11 +309,12 @@ export class NativeLoopRuntime {
265
309
 
266
310
  _readState(runId) {
267
311
  const path = this._statePath(runId);
268
- return existsSync(path) ? readJson(path, null) : null;
312
+ const state = existsSync(path) ? readJson(path, null) : null;
313
+ return state ? readStoredToolListRecord(state, this.toolListOptions()) : null;
269
314
  }
270
315
 
271
316
  _writeState(runId, state) {
272
- writeJsonAtomic(this._statePath(runId), state);
317
+ writeJsonAtomic(this._statePath(runId), toolListRecord(state, this.toolListOptions()));
273
318
  }
274
319
 
275
320
  _writeTerminalState(record, patch) {
@@ -285,8 +330,9 @@ export class NativeLoopRuntime {
285
330
  record.version = (Number(record.version) || 0) + 1;
286
331
  record.updatedAt = iso(this.now());
287
332
  // Force-flush on terminal transitions (a crash between throttled flushes
288
- // would otherwise leave the run listed as running after a restart).
289
- const terminal = ['completed', 'failed', 'stopped', 'interrupted'].includes(record.status);
333
+ // would otherwise leave the run listed as running after a restart), and on
334
+ // a suspension (the next start resumes the run from this ledger).
335
+ const terminal = ['completed', 'failed', 'stopped', 'interrupted', 'suspended'].includes(record.status);
290
336
  this._persistLedger({ force: terminal });
291
337
  const descriptor = cleanDescriptor(record);
292
338
  const message = { type, run: descriptor, ...extra };
@@ -333,7 +379,7 @@ export class NativeLoopRuntime {
333
379
  mkdirSync(this.stateDir, { recursive: true });
334
380
  const startedAt = state.startedAt || iso(this.now());
335
381
  const nativeState = {
336
- ...state,
382
+ ...toolListRecord(state, this.toolListOptions()),
337
383
  runId,
338
384
  terminalSessionId: runId,
339
385
  active: true,
@@ -361,7 +407,7 @@ export class NativeLoopRuntime {
361
407
  contextMode: state.contextMode || null,
362
408
  modelContextWindow: state.modelContextWindow || null,
363
409
  effort: state.effort || null,
364
- mcpProfile: state.mcpProfile || null,
410
+ tools: storedToolList(state, this.toolListOptions()),
365
411
  accountId: state.codexAccountId || null,
366
412
  currentIteration: Number(state.currentIteration) || 0,
367
413
  totalIterations: Number(state.totalIterations) || 1,
@@ -400,6 +446,24 @@ export class NativeLoopRuntime {
400
446
  record.timer.unref?.();
401
447
  }
402
448
 
449
+ this._bootstrap(record, factory, nativeState, { source, profile: state.profile });
450
+
451
+ // For back-compat: callers that awaited launch() to know whether the run was
452
+ // accepted can observe the descriptor. The failure (if any) is delivered via
453
+ // sidepanel:run-failed and via record.bootstrap rejecting with the original
454
+ // startup error. The descriptor itself resolves immediately with the
455
+ // 'starting' state so the HTTP caller is never gated on provider boot.
456
+ return cleanDescriptor(record);
457
+ }
458
+
459
+ /**
460
+ * Start the provider of a run whose record is in place (launch, resume) and drive it. Sets record.bootstrap: it
461
+ * resolves once the provider started and rejects with the startup error. A provider that cannot start ends the
462
+ * run as failed, or (`failAs: 'interrupted'`, a resume) as interrupted with the reason resume_failed.
463
+ */
464
+ _bootstrap(record, factory, nativeState, { source = record.source, profile = record.provider, failAs = 'failed' } = {}) {
465
+ const runId = record.runId;
466
+ const state = { profile };
403
467
  const startupSignal = record.startupAbortController.signal;
404
468
  const factoryPromise = Promise.resolve().then(() => factory({
405
469
  ...nativeState,
@@ -418,7 +482,8 @@ export class NativeLoopRuntime {
418
482
  if (!adapter || candidateCleaned) return;
419
483
  candidateCleaned = true;
420
484
  try { await adapter.abort?.(reason); } catch {}
421
- try { await adapter.dispose?.({ preserveSession: true }); } catch {}
485
+ // Suspended for a server shutdown while it started (suspend): its process is ended now, as a started one's is.
486
+ try { await adapter.dispose?.({ preserveSession: true, ...(record.status === 'suspended' ? { terminate: true } : {}) }); } catch {}
422
487
  };
423
488
  const finishBootstrap = async () => {
424
489
  clearTimeout(startupTimer);
@@ -497,11 +562,15 @@ export class NativeLoopRuntime {
497
562
  if (candidate) await cleanupCandidate(candidate, error?.code || 'startup_failed');
498
563
  else factoryPromise.then((adapter) => cleanupCandidate(adapter, error?.code || 'startup_failed')).catch(() => {});
499
564
  }
500
- await this._fail(
501
- record,
502
- error,
503
- error?.code === 'NATIVE_LOOP_PROVIDER_START_TIMEOUT' ? 'provider_startup_timeout' : 'provider_error',
504
- );
565
+ if (failAs === 'interrupted') {
566
+ await this._fail(record, error, 'resume_failed', { status: 'interrupted' });
567
+ } else {
568
+ await this._fail(
569
+ record,
570
+ error,
571
+ error?.code === 'NATIVE_LOOP_PROVIDER_START_TIMEOUT' ? 'provider_startup_timeout' : 'provider_error',
572
+ );
573
+ }
505
574
  } finally {
506
575
  await finishBootstrap();
507
576
  // Re-throw so record.bootstrap rejects with the original startup error.
@@ -516,15 +585,146 @@ export class NativeLoopRuntime {
516
585
  // timer holds the process until cleanup. Swallow the unhandled rejection
517
586
  // here because callers that care must await record.bootstrap explicitly.
518
587
  bootstrap.catch(() => {});
588
+ return bootstrap;
589
+ }
519
590
 
520
- // For back-compat: callers that awaited launch() to know whether the run was
521
- // accepted can observe the descriptor. The failure (if any) is delivered via
522
- // sidepanel:run-failed and via record.bootstrap rejecting with the original
523
- // startup error. The descriptor itself resolves immediately with the
524
- // 'starting' state so the HTTP caller is never gated on provider boot.
591
+ /**
592
+ * Continue a suspended run (a dispatched worker a server restart cut off; assistant-dispatch.js resumeRun) as the
593
+ * same record: its runId, tab, claim, title and startedAt stay, its version keeps growing. The state file is written
594
+ * again at iteration 0 of 1, so the drive runs the run's one task iteration again (the dispatcher's wrapper decides
595
+ * what that turn sends), and the provider factory gets what `state` carries to continue the provider session
596
+ * (resumeSessionId, costBaseUsd). Resolves with the descriptor once the provider started; a provider that cannot
597
+ * start ends the run as interrupted (resume_failed) and the promise rejects with why.
598
+ */
599
+ async resume({ runId, state, buildPrompt = null, wrapAdapter = null } = {}) {
600
+ const record = this.records.get(runId);
601
+ if (!record) throw new Error(`Native loop ${runId} is not known`);
602
+ if (record.status !== 'suspended') throw new Error(`Native loop ${runId} is ${record.status}, not suspended`);
603
+ if (buildPrompt !== null && typeof buildPrompt !== 'function') throw new Error('Native loop buildPrompt override must be a function');
604
+ if (wrapAdapter !== null && typeof wrapAdapter !== 'function') throw new Error('Native loop wrapAdapter override must be a function');
605
+ if (!state?.profile || !isNativeLoopProfile(state.profile)) throw new Error(`Unsupported native loop profile: ${state?.profile || 'unknown'}`);
606
+ const factory = this.providerFactories[state.profile];
607
+ if (!factory) throw new Error(`Native provider unavailable: ${state.profile}`);
608
+ this._clearHold(record);
609
+ mkdirSync(this.stateDir, { recursive: true });
610
+ const totalIterations = Number(state.totalIterations) || 1;
611
+ const nativeState = {
612
+ ...toolListRecord(state, this.toolListOptions()),
613
+ runId,
614
+ terminalSessionId: runId,
615
+ active: true,
616
+ pending: false,
617
+ driverType: 'native',
618
+ runtimeType: 'native',
619
+ surface: 'sidepanel',
620
+ startedAt: record.startedAt || state.startedAt || iso(this.now()),
621
+ currentIteration: 0,
622
+ totalIterations,
623
+ };
624
+ this._writeState(runId, nativeState);
625
+ Object.assign(record, {
626
+ status: 'starting',
627
+ stoppedReason: null,
628
+ completedAt: null,
629
+ error: null,
630
+ adapter: null,
631
+ timer: null,
632
+ execution: null,
633
+ stopping: false,
634
+ startupAbortController: new AbortController(),
635
+ turnState: 'starting',
636
+ currentIteration: 0,
637
+ totalIterations,
638
+ browserSessionId: state.browserSessionId || null,
639
+ browserTabId: state.browserTabId || null,
640
+ suspendedAt: null,
641
+ suspendReason: null,
642
+ resumes: (Number(record.resumes) || 0) + 1,
643
+ resumedAt: iso(this.now()),
644
+ buildPrompt,
645
+ wrapAdapter,
646
+ });
647
+ record.identityPromise = new Promise((resolveIdentity) => { record._resolveIdentity = resolveIdentity; });
648
+ record.startupCancelPromise = new Promise((resolveCancel) => { record._cancelStartup = resolveCancel; });
649
+ this._emit('sidepanel:run-updated', record, { reason: 'resuming' });
650
+ await this._bootstrap(record, factory, nativeState, { profile: state.profile, failAs: 'interrupted' });
525
651
  return cleanDescriptor(record);
526
652
  }
527
653
 
654
+ /** The hold of a run the restart cut off: it ends as interrupted when nobody resumes it in time. */
655
+ _hold(record) {
656
+ this._clearHold(record);
657
+ const timer = setTimeout(() => {
658
+ this._holdTimers.delete(record.runId);
659
+ this.endSuspended(record.runId, record.suspendReason || 'server_restart').catch(() => {});
660
+ }, this.resumeHoldMs);
661
+ timer.unref?.();
662
+ this._holdTimers.set(record.runId, timer);
663
+ }
664
+
665
+ _clearHold(record) {
666
+ const timer = this._holdTimers.get(record?.runId);
667
+ if (timer) { clearTimeout(timer); this._holdTimers.delete(record.runId); }
668
+ }
669
+
670
+ /** Whether a run waits for its resume after a restart (suspended). */
671
+ isSuspended(runId) {
672
+ return this.records.get(runId)?.status === 'suspended';
673
+ }
674
+
675
+ /**
676
+ * The dispatcher takes a suspended run over (it resumes it or ends it itself): its hold no longer ends it.
677
+ * Returns whether the run is suspended.
678
+ */
679
+ keepSuspended(runId) {
680
+ const record = this.records.get(runId);
681
+ if (!record || record.status !== 'suspended') return false;
682
+ this._clearHold(record);
683
+ return true;
684
+ }
685
+
686
+ /**
687
+ * A suspended run that will not be resumed ends as interrupted (`reason`, by default why it was suspended), with an
688
+ * event, so a tab that waited for its resume shows how it ended. `error`: why it was not resumed.
689
+ */
690
+ async endSuspended(runId, reason = null, { error = null } = {}) {
691
+ const record = this.records.get(runId);
692
+ if (!record || record.status !== 'suspended') return false;
693
+ this._clearHold(record);
694
+ record.status = 'interrupted';
695
+ record.stoppedReason = reason || record.suspendReason || 'server_restart';
696
+ record.completedAt = iso(this.now());
697
+ if (error) record.error = String(error);
698
+ this._writeTerminalState(record, { active: false, pending: false, completedAt: record.completedAt, stoppedReason: record.stoppedReason, ...(error ? { error: record.error } : {}) });
699
+ this._releaseRuntimeRefs(record);
700
+ this._emit('sidepanel:run-stopped', record);
701
+ try { await this.onFinish(cleanDescriptor(record)); } catch {}
702
+ return true;
703
+ }
704
+
705
+ /**
706
+ * Suspend a running dispatched worker for a server shutdown (shutdown's `keep`): its CLI is ended (the adapter's
707
+ * dispose with terminate), its state file stays active, and the record waits as suspended for the next start to
708
+ * resume it. No run-stopped event and no end: a run-updated with the reason 'suspended'.
709
+ */
710
+ async suspend(runId, reason = 'server_shutdown') {
711
+ const record = this.records.get(runId);
712
+ if (!record || !['starting', 'running'].includes(record.status) || record.stopping) return false;
713
+ record.stopping = true;
714
+ clearTimeout(record.timer);
715
+ record.status = 'suspended';
716
+ record.suspendedAt = iso(this.now());
717
+ record.suspendReason = reason;
718
+ if (record._resolveIdentity) { record._resolveIdentity(null); record._resolveIdentity = null; }
719
+ try { record.startupAbortController?.abort(reason); } catch {}
720
+ if (record._cancelStartup) { record._cancelStartup(reason); record._cancelStartup = null; }
721
+ try { await record.adapter?.dispose?.({ preserveSession: true, terminate: true }); } catch {}
722
+ this._releaseRuntimeRefs(record, { turnState: 'suspended' });
723
+ record.stopping = false;
724
+ this._emit('sidepanel:run-updated', record, { reason: 'suspended' });
725
+ return true;
726
+ }
727
+
528
728
  async _drive(record) {
529
729
  let failures = 0;
530
730
  while (this.isAlive(record.runId)) {
@@ -618,7 +818,7 @@ export class NativeLoopRuntime {
618
818
  }
619
819
  }
620
820
 
621
- _releaseRuntimeRefs(record) {
821
+ _releaseRuntimeRefs(record, { turnState = 'terminal' } = {}) {
622
822
  if (!record) return;
623
823
  record.adapter = null;
624
824
  record.timer = null;
@@ -630,7 +830,8 @@ export class NativeLoopRuntime {
630
830
  record.startupAbortController = null;
631
831
  record.buildPrompt = null;
632
832
  record.wrapAdapter = null;
633
- if (record.turnState && record.turnState !== 'terminal') record.turnState = 'terminal';
833
+ if (turnState === 'suspended') record.turnState = 'suspended';
834
+ else if (record.turnState && record.turnState !== 'terminal') record.turnState = 'terminal';
634
835
  }
635
836
 
636
837
  async _complete(record, reason = 'completed') {
@@ -646,10 +847,11 @@ export class NativeLoopRuntime {
646
847
  try { await this.onFinish(cleanDescriptor(record)); } catch {}
647
848
  }
648
849
 
649
- async _fail(record, error, reason = 'provider_error') {
650
- if (!record || ['completed', 'failed', 'stopped', 'interrupted'].includes(record.status)) return;
850
+ /** `status`: 'failed', or 'interrupted' for a resume that could not start (reason resume_failed). */
851
+ async _fail(record, error, reason = 'provider_error', { status = 'failed' } = {}) {
852
+ if (!record || ['completed', 'failed', 'stopped', 'interrupted', 'suspended'].includes(record.status)) return;
651
853
  clearTimeout(record.timer);
652
- record.status = 'failed';
854
+ record.status = status === 'interrupted' ? 'interrupted' : 'failed';
653
855
  record.error = error?.message || String(error);
654
856
  record.stoppedReason = reason;
655
857
  record.completedAt = iso(this.now());
@@ -660,12 +862,27 @@ export class NativeLoopRuntime {
660
862
  try { await record.adapter?.abort?.(reason); } catch {}
661
863
  try { await record.adapter?.dispose?.({ preserveSession: true }); } catch {}
662
864
  this._releaseRuntimeRefs(record);
663
- this._emit('sidepanel:run-failed', record);
865
+ this._emit(record.status === 'interrupted' ? 'sidepanel:run-stopped' : 'sidepanel:run-failed', record);
664
866
  try { await this.onFinish(cleanDescriptor(record)); } catch {}
665
867
  }
666
868
 
667
- async stop(runId, reason = 'user') {
869
+ /**
870
+ * `terminate` (a server shutdown): the provider's process is ended at once (the Claude adapter's dispose), never
871
+ * left to finish its tool call after the server is gone. A suspended run (waiting for its resume) ends as stopped.
872
+ */
873
+ async stop(runId, reason = 'user', { terminate = false } = {}) {
668
874
  const record = this.records.get(runId);
875
+ if (record?.status === 'suspended' && !record.stopping) {
876
+ this._clearHold(record);
877
+ record.status = 'stopped';
878
+ record.stoppedReason = reason;
879
+ record.completedAt = iso(this.now());
880
+ this._writeTerminalState(record, { active: false, pending: false, completedAt: record.completedAt, stoppedReason: reason });
881
+ this._releaseRuntimeRefs(record);
882
+ this._emit('sidepanel:run-stopped', record);
883
+ try { await this.onFinish(cleanDescriptor(record)); } catch {}
884
+ return true;
885
+ }
669
886
  if (!record || !['starting', 'running'].includes(record.status) || record.stopping) return false;
670
887
  record.stopping = true;
671
888
  clearTimeout(record.timer);
@@ -677,7 +894,7 @@ export class NativeLoopRuntime {
677
894
  if (record._cancelStartup) { record._cancelStartup(reason); record._cancelStartup = null; }
678
895
  this._writeTerminalState(record, { active: false, pending: false, completedAt: record.completedAt, stoppedReason: reason });
679
896
  try { await record.adapter?.abort?.(reason); } catch {}
680
- try { await record.adapter?.dispose?.({ preserveSession: true }); } catch {}
897
+ try { await record.adapter?.dispose?.({ preserveSession: true, ...(terminate ? { terminate: true } : {}) }); } catch {}
681
898
  this._releaseRuntimeRefs(record);
682
899
  this._emit('sidepanel:run-stopped', record);
683
900
  try { await this.onFinish(cleanDescriptor(record)); } catch {}
@@ -735,19 +952,21 @@ export class NativeLoopRuntime {
735
952
  return released;
736
953
  }
737
954
 
738
- setMcpProfile(runId, profile) {
955
+ setTools(runId, tools) {
739
956
  const record = this.records.get(runId);
740
- const normalized = String(profile || '').toLowerCase().trim();
741
- if (!record || !normalized) return null;
742
- if (record.mcpProfile === normalized) return cleanDescriptor(record);
743
- record.mcpProfile = normalized;
957
+ if (!record) return null;
958
+ if (!Array.isArray(tools)) throw Object.assign(new Error('tools must be an array'), { code: 'TOOLS_INVALID', status: 400 });
959
+ const normalized = storedToolList({ tools: retainExternalRefs(tools, record.tools) });
960
+ if (JSON.stringify(record.tools) === JSON.stringify(normalized)) return cleanDescriptor(record);
961
+ record.tools = normalized;
962
+ delete record.mcpProfile;
744
963
  try {
745
964
  const state = this._readState(record.runId);
746
- if (state) this._writeState(record.runId, { ...state, mcpProfile: normalized });
965
+ if (state) this._writeState(record.runId, { ...state, tools: normalized });
747
966
  } catch (error) {
748
967
  this.log(record.runId, 'native:state-write-error', error?.message || String(error));
749
968
  }
750
- return this._emit('sidepanel:run-updated', record, { reason: 'mcp-profile' });
969
+ return this._emit('sidepanel:run-updated', record, { reason: 'mcp-tools' });
751
970
  }
752
971
 
753
972
  get(runId) {
@@ -757,15 +976,23 @@ export class NativeLoopRuntime {
757
976
 
758
977
  list({ activeOnly = false } = {}) {
759
978
  return [...this.records.values()]
760
- .filter((record) => !activeOnly || ['starting', 'running'].includes(record.status))
979
+ .filter((record) => !activeOnly || LIVE_STATUSES.has(record.status))
761
980
  .map(cleanDescriptor)
762
981
  .sort((a, b) => String(b.startedAt || '').localeCompare(String(a.startedAt || '')));
763
982
  }
764
983
 
765
- async shutdown(reason = 'server_shutdown') {
984
+ /**
985
+ * The server is going down. `keep`: the runs its dispatcher suspends to resume on the next start
986
+ * (assistant-dispatch.js suspendForShutdown); every other running run is stopped. Either way each provider process
987
+ * is ended now (terminate), so nothing outlives the server. A run still waiting for its resume stays suspended.
988
+ */
989
+ async shutdown(reason = 'server_shutdown', { keep = [] } = {}) {
990
+ const kept = new Set((Array.isArray(keep) ? keep : []).map(String));
766
991
  await Promise.allSettled([...this.records.values()]
767
992
  .filter((record) => ['starting', 'running'].includes(record.status))
768
- .map((record) => this.stop(record.runId, reason)));
993
+ .map((record) => (kept.has(record.runId) ? this.suspend(record.runId, reason) : this.stop(record.runId, reason, { terminate: true }))));
994
+ for (const timer of this._holdTimers.values()) clearTimeout(timer);
995
+ this._holdTimers.clear();
769
996
  this._persistLedger({ force: true });
770
997
  }
771
998
  }