synabun 1.3.0 → 2.0.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 (1163) hide show
  1. package/CHANGELOG.md +205 -662
  2. package/LICENSE-COMMERCIAL.md +33 -0
  3. package/NOTICE +17 -0
  4. package/README.md +175 -853
  5. package/THIRD-PARTY-LICENSES.md +136 -0
  6. package/benchmarks/browser-live-results.json +79 -0
  7. package/benchmarks/browser-v2-results.json +258 -0
  8. package/benchmarks/fixtures/browser/README.md +57 -0
  9. package/benchmarks/fixtures/browser/page-state.json +132 -0
  10. package/benchmarks/fixtures/browser/social.json +92 -0
  11. package/benchmarks/fixtures/browser/target.json +319 -0
  12. package/benchmarks/fixtures/desktop/README.md +58 -0
  13. package/benchmarks/fixtures/desktop/abstention.json +59 -0
  14. package/benchmarks/fixtures/desktop/adversarial.json +22 -0
  15. package/benchmarks/fixtures/desktop/forbidden.json +93 -0
  16. package/benchmarks/fixtures/desktop/generate.mjs +1023 -0
  17. package/benchmarks/fixtures/desktop/target.json +136 -0
  18. package/bin/synabun.js +30 -0
  19. package/docs/whatsapp.md +325 -0
  20. package/hooks/claude-code/post-plan.mjs +613 -391
  21. package/hooks/claude-code/post-remember.mjs +246 -122
  22. package/hooks/claude-code/pre-compact.mjs +171 -110
  23. package/hooks/claude-code/pre-task.mjs +133 -0
  24. package/hooks/claude-code/pre-websearch.mjs +15 -4
  25. package/hooks/claude-code/prompt-origin.mjs +94 -0
  26. package/hooks/claude-code/prompt-submit.mjs +1244 -861
  27. package/hooks/claude-code/session-start.mjs +114 -156
  28. package/hooks/claude-code/shared.mjs +1228 -368
  29. package/hooks/claude-code/state.mjs +167 -0
  30. package/hooks/claude-code/stop.mjs +515 -140
  31. package/hooks/claude-code/subagent-start.mjs +154 -0
  32. package/hooks/claude-code/subagent-stop.mjs +102 -0
  33. package/hooks/claude-code/task-gate.mjs +150 -0
  34. package/launcher.mjs +40 -0
  35. package/lib/claude-hooks.js +137 -0
  36. package/lib/claude-usage-audit.js +141 -0
  37. package/lib/client-config-repair.js +145 -0
  38. package/lib/data-home-migration.js +502 -0
  39. package/lib/desktop-pwa.js +206 -0
  40. package/lib/external-tools.js +221 -0
  41. package/lib/first-launch-protection.js +245 -0
  42. package/lib/packaged-runtime.js +110 -0
  43. package/lib/paths.d.ts +21 -0
  44. package/lib/paths.js +103 -0
  45. package/lib/start-launcher.js +1309 -0
  46. package/mcp-server/dist/config.d.ts +22 -0
  47. package/mcp-server/dist/config.d.ts.map +1 -0
  48. package/mcp-server/dist/config.js +109 -0
  49. package/mcp-server/dist/config.js.map +1 -0
  50. package/mcp-server/dist/http.d.ts +30 -0
  51. package/mcp-server/dist/http.d.ts.map +1 -0
  52. package/mcp-server/dist/http.js +276 -0
  53. package/mcp-server/dist/http.js.map +1 -0
  54. package/mcp-server/dist/index.d.ts +48 -0
  55. package/mcp-server/dist/index.d.ts.map +1 -0
  56. package/mcp-server/dist/index.js +538 -0
  57. package/mcp-server/dist/index.js.map +1 -0
  58. package/mcp-server/dist/preload-http.d.ts +3 -0
  59. package/mcp-server/dist/preload-http.d.ts.map +1 -0
  60. package/mcp-server/dist/preload-http.js +28 -0
  61. package/mcp-server/dist/preload-http.js.map +1 -0
  62. package/mcp-server/dist/preload.d.ts +3 -0
  63. package/mcp-server/dist/preload.d.ts.map +1 -0
  64. package/mcp-server/dist/preload.js +32 -0
  65. package/mcp-server/dist/preload.js.map +1 -0
  66. package/mcp-server/dist/scripts/migrate-qdrant-to-sqlite.d.ts +19 -0
  67. package/mcp-server/dist/scripts/migrate-qdrant-to-sqlite.d.ts.map +1 -0
  68. package/mcp-server/dist/scripts/migrate-qdrant-to-sqlite.js +403 -0
  69. package/mcp-server/dist/scripts/migrate-qdrant-to-sqlite.js.map +1 -0
  70. package/mcp-server/dist/scripts/setup-embeddings.d.ts +3 -0
  71. package/mcp-server/dist/scripts/setup-embeddings.d.ts.map +1 -0
  72. package/mcp-server/dist/scripts/setup-embeddings.js +15 -0
  73. package/mcp-server/dist/scripts/setup-embeddings.js.map +1 -0
  74. package/mcp-server/dist/services/assistant-judgments.d.ts +79 -0
  75. package/mcp-server/dist/services/assistant-judgments.d.ts.map +1 -0
  76. package/mcp-server/dist/services/assistant-judgments.js +128 -0
  77. package/mcp-server/dist/services/assistant-judgments.js.map +1 -0
  78. package/mcp-server/dist/services/browser-assist-gate.d.ts +314 -0
  79. package/mcp-server/dist/services/browser-assist-gate.d.ts.map +1 -0
  80. package/mcp-server/dist/services/browser-assist-gate.js +344 -0
  81. package/mcp-server/dist/services/browser-assist-gate.js.map +1 -0
  82. package/mcp-server/dist/services/browser-assist.d.ts +104 -0
  83. package/mcp-server/dist/services/browser-assist.d.ts.map +1 -0
  84. package/mcp-server/dist/services/browser-assist.js +282 -0
  85. package/mcp-server/dist/services/browser-assist.js.map +1 -0
  86. package/mcp-server/dist/services/browser-bench.d.ts +772 -0
  87. package/mcp-server/dist/services/browser-bench.d.ts.map +1 -0
  88. package/mcp-server/dist/services/browser-bench.js +424 -0
  89. package/mcp-server/dist/services/browser-bench.js.map +1 -0
  90. package/mcp-server/dist/services/browser-judgments.d.ts +196 -0
  91. package/mcp-server/dist/services/browser-judgments.d.ts.map +1 -0
  92. package/mcp-server/dist/services/browser-judgments.js +363 -0
  93. package/mcp-server/dist/services/browser-judgments.js.map +1 -0
  94. package/mcp-server/dist/services/browser-risk.d.ts +166 -0
  95. package/mcp-server/dist/services/browser-risk.d.ts.map +1 -0
  96. package/mcp-server/dist/services/browser-risk.js +458 -0
  97. package/mcp-server/dist/services/browser-risk.js.map +1 -0
  98. package/mcp-server/dist/services/categories.d.ts +61 -0
  99. package/mcp-server/dist/services/categories.d.ts.map +1 -0
  100. package/mcp-server/dist/services/categories.js +377 -0
  101. package/mcp-server/dist/services/categories.js.map +1 -0
  102. package/mcp-server/dist/services/codex-config-heal.d.ts +49 -0
  103. package/mcp-server/dist/services/codex-config-heal.d.ts.map +1 -0
  104. package/mcp-server/dist/services/codex-config-heal.js +346 -0
  105. package/mcp-server/dist/services/codex-config-heal.js.map +1 -0
  106. package/mcp-server/dist/services/desktop-assist-gate.d.ts +198 -0
  107. package/mcp-server/dist/services/desktop-assist-gate.d.ts.map +1 -0
  108. package/mcp-server/dist/services/desktop-assist-gate.js +202 -0
  109. package/mcp-server/dist/services/desktop-assist-gate.js.map +1 -0
  110. package/mcp-server/dist/services/desktop-assist.d.ts +104 -0
  111. package/mcp-server/dist/services/desktop-assist.d.ts.map +1 -0
  112. package/mcp-server/dist/services/desktop-assist.js +266 -0
  113. package/mcp-server/dist/services/desktop-assist.js.map +1 -0
  114. package/mcp-server/dist/services/desktop-bench.d.ts +553 -0
  115. package/mcp-server/dist/services/desktop-bench.d.ts.map +1 -0
  116. package/mcp-server/dist/services/desktop-bench.js +311 -0
  117. package/mcp-server/dist/services/desktop-bench.js.map +1 -0
  118. package/mcp-server/dist/services/desktop-client.d.ts +23 -0
  119. package/mcp-server/dist/services/desktop-client.d.ts.map +1 -0
  120. package/mcp-server/dist/services/desktop-client.js +79 -0
  121. package/mcp-server/dist/services/desktop-client.js.map +1 -0
  122. package/mcp-server/dist/services/desktop-judgments.d.ts +125 -0
  123. package/mcp-server/dist/services/desktop-judgments.d.ts.map +1 -0
  124. package/mcp-server/dist/services/desktop-judgments.js +177 -0
  125. package/mcp-server/dist/services/desktop-judgments.js.map +1 -0
  126. package/mcp-server/dist/services/desktop-risk.d.ts +283 -0
  127. package/mcp-server/dist/services/desktop-risk.d.ts.map +1 -0
  128. package/mcp-server/dist/services/desktop-risk.js +605 -0
  129. package/mcp-server/dist/services/desktop-risk.js.map +1 -0
  130. package/mcp-server/dist/services/discord.d.ts +112 -0
  131. package/mcp-server/dist/services/discord.d.ts.map +1 -0
  132. package/mcp-server/dist/services/discord.js +477 -0
  133. package/mcp-server/dist/services/discord.js.map +1 -0
  134. package/mcp-server/dist/services/edit-stale.d.ts +81 -0
  135. package/mcp-server/dist/services/edit-stale.d.ts.map +1 -0
  136. package/mcp-server/dist/services/edit-stale.js +358 -0
  137. package/mcp-server/dist/services/edit-stale.js.map +1 -0
  138. package/mcp-server/dist/services/embedding-service.d.ts +16 -0
  139. package/mcp-server/dist/services/embedding-service.d.ts.map +1 -0
  140. package/mcp-server/dist/services/embedding-service.js +126 -0
  141. package/mcp-server/dist/services/embedding-service.js.map +1 -0
  142. package/mcp-server/dist/services/embedding-worker.d.ts +2 -0
  143. package/mcp-server/dist/services/embedding-worker.d.ts.map +1 -0
  144. package/mcp-server/dist/services/embedding-worker.js +52 -0
  145. package/mcp-server/dist/services/embedding-worker.js.map +1 -0
  146. package/mcp-server/dist/services/fb-regions.d.ts +71 -0
  147. package/mcp-server/dist/services/fb-regions.d.ts.map +1 -0
  148. package/mcp-server/dist/services/fb-regions.js +211 -0
  149. package/mcp-server/dist/services/fb-regions.js.map +1 -0
  150. package/mcp-server/dist/services/file-checksums.d.ts +22 -0
  151. package/mcp-server/dist/services/file-checksums.d.ts.map +1 -0
  152. package/mcp-server/dist/services/file-checksums.js +55 -0
  153. package/mcp-server/dist/services/file-checksums.js.map +1 -0
  154. package/mcp-server/dist/services/file-excerpt.d.ts +24 -0
  155. package/mcp-server/dist/services/file-excerpt.d.ts.map +1 -0
  156. package/mcp-server/dist/services/file-excerpt.js +65 -0
  157. package/mcp-server/dist/services/file-excerpt.js.map +1 -0
  158. package/mcp-server/dist/services/identity.d.ts +124 -0
  159. package/mcp-server/dist/services/identity.d.ts.map +1 -0
  160. package/mcp-server/dist/services/identity.js +241 -0
  161. package/mcp-server/dist/services/identity.js.map +1 -0
  162. package/mcp-server/dist/services/local-embeddings.d.ts +2 -0
  163. package/mcp-server/dist/services/local-embeddings.d.ts.map +1 -0
  164. package/mcp-server/dist/services/local-embeddings.js +3 -0
  165. package/mcp-server/dist/services/local-embeddings.js.map +1 -0
  166. package/mcp-server/dist/services/memory-client.d.ts +4 -0
  167. package/mcp-server/dist/services/memory-client.d.ts.map +1 -0
  168. package/mcp-server/dist/services/memory-client.js +27 -0
  169. package/mcp-server/dist/services/memory-client.js.map +1 -0
  170. package/mcp-server/dist/services/memory-judge-backfill.d.ts +135 -0
  171. package/mcp-server/dist/services/memory-judge-backfill.d.ts.map +1 -0
  172. package/mcp-server/dist/services/memory-judge-backfill.js +510 -0
  173. package/mcp-server/dist/services/memory-judge-backfill.js.map +1 -0
  174. package/mcp-server/dist/services/memory-judgments.d.ts +362 -0
  175. package/mcp-server/dist/services/memory-judgments.d.ts.map +1 -0
  176. package/mcp-server/dist/services/memory-judgments.js +674 -0
  177. package/mcp-server/dist/services/memory-judgments.js.map +1 -0
  178. package/mcp-server/dist/services/memory-maintenance.d.ts +108 -0
  179. package/mcp-server/dist/services/memory-maintenance.d.ts.map +1 -0
  180. package/mcp-server/dist/services/memory-maintenance.js +403 -0
  181. package/mcp-server/dist/services/memory-maintenance.js.map +1 -0
  182. package/mcp-server/dist/services/memory-retrieval.d.ts +66 -0
  183. package/mcp-server/dist/services/memory-retrieval.d.ts.map +1 -0
  184. package/mcp-server/dist/services/memory-retrieval.js +412 -0
  185. package/mcp-server/dist/services/memory-retrieval.js.map +1 -0
  186. package/mcp-server/dist/services/memory-schema.d.ts +16 -0
  187. package/mcp-server/dist/services/memory-schema.d.ts.map +1 -0
  188. package/mcp-server/dist/services/memory-schema.js +282 -0
  189. package/mcp-server/dist/services/memory-schema.js.map +1 -0
  190. package/mcp-server/dist/services/memory-sessions.d.ts +24 -0
  191. package/mcp-server/dist/services/memory-sessions.d.ts.map +1 -0
  192. package/mcp-server/dist/services/memory-sessions.js +110 -0
  193. package/mcp-server/dist/services/memory-sessions.js.map +1 -0
  194. package/mcp-server/dist/services/memory-triage.d.ts +60 -0
  195. package/mcp-server/dist/services/memory-triage.d.ts.map +1 -0
  196. package/mcp-server/dist/services/memory-triage.js +189 -0
  197. package/mcp-server/dist/services/memory-triage.js.map +1 -0
  198. package/mcp-server/dist/services/memory-verification.d.ts +24 -0
  199. package/mcp-server/dist/services/memory-verification.d.ts.map +1 -0
  200. package/mcp-server/dist/services/memory-verification.js +92 -0
  201. package/mcp-server/dist/services/memory-verification.js.map +1 -0
  202. package/mcp-server/dist/services/memory-writer.d.ts +26 -0
  203. package/mcp-server/dist/services/memory-writer.d.ts.map +1 -0
  204. package/mcp-server/dist/services/memory-writer.js +126 -0
  205. package/mcp-server/dist/services/memory-writer.js.map +1 -0
  206. package/mcp-server/dist/services/neural-interface.d.ts +439 -0
  207. package/mcp-server/dist/services/neural-interface.d.ts.map +1 -0
  208. package/mcp-server/dist/services/neural-interface.js +1135 -0
  209. package/mcp-server/dist/services/neural-interface.js.map +1 -0
  210. package/mcp-server/dist/services/profiles.d.ts +120 -0
  211. package/mcp-server/dist/services/profiles.d.ts.map +1 -0
  212. package/mcp-server/dist/services/profiles.js +387 -0
  213. package/mcp-server/dist/services/profiles.js.map +1 -0
  214. package/mcp-server/dist/services/secret-gate.d.ts +29 -0
  215. package/mcp-server/dist/services/secret-gate.d.ts.map +1 -0
  216. package/mcp-server/dist/services/secret-gate.js +43 -0
  217. package/mcp-server/dist/services/secret-gate.js.map +1 -0
  218. package/mcp-server/dist/services/server-instructions.d.ts +10 -0
  219. package/mcp-server/dist/services/server-instructions.d.ts.map +1 -0
  220. package/mcp-server/dist/services/server-instructions.js +101 -0
  221. package/mcp-server/dist/services/server-instructions.js.map +1 -0
  222. package/mcp-server/dist/services/sqlite.d.ts +235 -0
  223. package/mcp-server/dist/services/sqlite.d.ts.map +1 -0
  224. package/mcp-server/dist/services/sqlite.js +1185 -0
  225. package/mcp-server/dist/services/sqlite.js.map +1 -0
  226. package/mcp-server/dist/services/typesafe-bench.d.ts +99 -0
  227. package/mcp-server/dist/services/typesafe-bench.d.ts.map +1 -0
  228. package/mcp-server/dist/services/typesafe-bench.js +218 -0
  229. package/mcp-server/dist/services/typesafe-bench.js.map +1 -0
  230. package/mcp-server/dist/services/typesafe-config.d.ts +342 -0
  231. package/mcp-server/dist/services/typesafe-config.d.ts.map +1 -0
  232. package/mcp-server/dist/services/typesafe-config.js +746 -0
  233. package/mcp-server/dist/services/typesafe-config.js.map +1 -0
  234. package/mcp-server/dist/services/typesafe.d.ts +213 -0
  235. package/mcp-server/dist/services/typesafe.d.ts.map +1 -0
  236. package/mcp-server/dist/services/typesafe.js +330 -0
  237. package/mcp-server/dist/services/typesafe.js.map +1 -0
  238. package/mcp-server/dist/services/vector-cache.d.ts +68 -0
  239. package/mcp-server/dist/services/vector-cache.d.ts.map +1 -0
  240. package/mcp-server/dist/services/vector-cache.js +271 -0
  241. package/mcp-server/dist/services/vector-cache.js.map +1 -0
  242. package/mcp-server/dist/services/whiteboard-geometry.d.ts +305 -0
  243. package/mcp-server/dist/services/whiteboard-geometry.d.ts.map +1 -0
  244. package/mcp-server/dist/services/whiteboard-geometry.js +666 -0
  245. package/mcp-server/dist/services/whiteboard-geometry.js.map +1 -0
  246. package/mcp-server/dist/tools/agents.d.ts +212 -0
  247. package/mcp-server/dist/tools/agents.d.ts.map +1 -0
  248. package/mcp-server/dist/tools/agents.js +407 -0
  249. package/mcp-server/dist/tools/agents.js.map +1 -0
  250. package/mcp-server/dist/tools/bluesky.d.ts +39 -0
  251. package/mcp-server/dist/tools/bluesky.d.ts.map +1 -0
  252. package/mcp-server/dist/tools/bluesky.js +1144 -0
  253. package/mcp-server/dist/tools/bluesky.js.map +1 -0
  254. package/mcp-server/dist/tools/browser-advanced.d.ts +58 -0
  255. package/mcp-server/dist/tools/browser-advanced.d.ts.map +1 -0
  256. package/mcp-server/dist/tools/browser-advanced.js +106 -0
  257. package/mcp-server/dist/tools/browser-advanced.js.map +1 -0
  258. package/mcp-server/dist/tools/browser-batch.d.ts +99 -0
  259. package/mcp-server/dist/tools/browser-batch.d.ts.map +1 -0
  260. package/mcp-server/dist/tools/browser-batch.js +187 -0
  261. package/mcp-server/dist/tools/browser-batch.js.map +1 -0
  262. package/mcp-server/dist/tools/browser-cheatsheet.d.ts +21 -0
  263. package/mcp-server/dist/tools/browser-cheatsheet.d.ts.map +1 -0
  264. package/mcp-server/dist/tools/browser-cheatsheet.js +144 -0
  265. package/mcp-server/dist/tools/browser-cheatsheet.js.map +1 -0
  266. package/mcp-server/dist/tools/browser-interact.d.ts +239 -0
  267. package/mcp-server/dist/tools/browser-interact.d.ts.map +1 -0
  268. package/mcp-server/dist/tools/browser-interact.js +300 -0
  269. package/mcp-server/dist/tools/browser-interact.js.map +1 -0
  270. package/mcp-server/dist/tools/browser-navigate.d.ts +90 -0
  271. package/mcp-server/dist/tools/browser-navigate.d.ts.map +1 -0
  272. package/mcp-server/dist/tools/browser-navigate.js +142 -0
  273. package/mcp-server/dist/tools/browser-navigate.js.map +1 -0
  274. package/mcp-server/dist/tools/browser-observe.d.ts +561 -0
  275. package/mcp-server/dist/tools/browser-observe.d.ts.map +1 -0
  276. package/mcp-server/dist/tools/browser-observe.js +2009 -0
  277. package/mcp-server/dist/tools/browser-observe.js.map +1 -0
  278. package/mcp-server/dist/tools/browser.d.ts +34 -0
  279. package/mcp-server/dist/tools/browser.d.ts.map +1 -0
  280. package/mcp-server/dist/tools/browser.js +112 -0
  281. package/mcp-server/dist/tools/browser.js.map +1 -0
  282. package/mcp-server/dist/tools/card-tools.d.ts +82 -0
  283. package/mcp-server/dist/tools/card-tools.d.ts.map +1 -0
  284. package/mcp-server/dist/tools/card-tools.js +137 -0
  285. package/mcp-server/dist/tools/card-tools.js.map +1 -0
  286. package/mcp-server/dist/tools/card.d.ts +8 -0
  287. package/mcp-server/dist/tools/card.d.ts.map +1 -0
  288. package/mcp-server/dist/tools/card.js +16 -0
  289. package/mcp-server/dist/tools/card.js.map +1 -0
  290. package/mcp-server/dist/tools/category.d.ts +32 -0
  291. package/mcp-server/dist/tools/category.d.ts.map +1 -0
  292. package/mcp-server/dist/tools/category.js +290 -0
  293. package/mcp-server/dist/tools/category.js.map +1 -0
  294. package/mcp-server/dist/tools/choice.d.ts +160 -0
  295. package/mcp-server/dist/tools/choice.d.ts.map +1 -0
  296. package/mcp-server/dist/tools/choice.js +80 -0
  297. package/mcp-server/dist/tools/choice.js.map +1 -0
  298. package/mcp-server/dist/tools/computer.d.ts +71 -0
  299. package/mcp-server/dist/tools/computer.d.ts.map +1 -0
  300. package/mcp-server/dist/tools/computer.js +174 -0
  301. package/mcp-server/dist/tools/computer.js.map +1 -0
  302. package/mcp-server/dist/tools/discord-channel.d.ts +44 -0
  303. package/mcp-server/dist/tools/discord-channel.d.ts.map +1 -0
  304. package/mcp-server/dist/tools/discord-channel.js +236 -0
  305. package/mcp-server/dist/tools/discord-channel.js.map +1 -0
  306. package/mcp-server/dist/tools/discord-guild.d.ts +20 -0
  307. package/mcp-server/dist/tools/discord-guild.d.ts.map +1 -0
  308. package/mcp-server/dist/tools/discord-guild.js +138 -0
  309. package/mcp-server/dist/tools/discord-guild.js.map +1 -0
  310. package/mcp-server/dist/tools/discord-member.d.ts +26 -0
  311. package/mcp-server/dist/tools/discord-member.d.ts.map +1 -0
  312. package/mcp-server/dist/tools/discord-member.js +122 -0
  313. package/mcp-server/dist/tools/discord-member.js.map +1 -0
  314. package/mcp-server/dist/tools/discord-message.d.ts +36 -0
  315. package/mcp-server/dist/tools/discord-message.d.ts.map +1 -0
  316. package/mcp-server/dist/tools/discord-message.js +184 -0
  317. package/mcp-server/dist/tools/discord-message.js.map +1 -0
  318. package/mcp-server/dist/tools/discord-onboarding.d.ts +32 -0
  319. package/mcp-server/dist/tools/discord-onboarding.d.ts.map +1 -0
  320. package/mcp-server/dist/tools/discord-onboarding.js +179 -0
  321. package/mcp-server/dist/tools/discord-onboarding.js.map +1 -0
  322. package/mcp-server/dist/tools/discord-role.d.ts +34 -0
  323. package/mcp-server/dist/tools/discord-role.d.ts.map +1 -0
  324. package/mcp-server/dist/tools/discord-role.js +178 -0
  325. package/mcp-server/dist/tools/discord-role.js.map +1 -0
  326. package/mcp-server/dist/tools/discord-thread.d.ts +28 -0
  327. package/mcp-server/dist/tools/discord-thread.d.ts.map +1 -0
  328. package/mcp-server/dist/tools/discord-thread.js +88 -0
  329. package/mcp-server/dist/tools/discord-thread.js.map +1 -0
  330. package/mcp-server/dist/tools/discord-webhook.d.ts +32 -0
  331. package/mcp-server/dist/tools/discord-webhook.d.ts.map +1 -0
  332. package/mcp-server/dist/tools/discord-webhook.js +154 -0
  333. package/mcp-server/dist/tools/discord-webhook.js.map +1 -0
  334. package/mcp-server/dist/tools/discord.d.ts +7 -0
  335. package/mcp-server/dist/tools/discord.d.ts.map +1 -0
  336. package/mcp-server/dist/tools/discord.js +25 -0
  337. package/mcp-server/dist/tools/discord.js.map +1 -0
  338. package/mcp-server/dist/tools/fb-groups.d.ts +98 -0
  339. package/mcp-server/dist/tools/fb-groups.d.ts.map +1 -0
  340. package/mcp-server/dist/tools/fb-groups.js +369 -0
  341. package/mcp-server/dist/tools/fb-groups.js.map +1 -0
  342. package/mcp-server/dist/tools/forget.d.ts +14 -0
  343. package/mcp-server/dist/tools/forget.d.ts.map +1 -0
  344. package/mcp-server/dist/tools/forget.js +26 -0
  345. package/mcp-server/dist/tools/forget.js.map +1 -0
  346. package/mcp-server/dist/tools/git-tools.d.ts +24 -0
  347. package/mcp-server/dist/tools/git-tools.d.ts.map +1 -0
  348. package/mcp-server/dist/tools/git-tools.js +107 -0
  349. package/mcp-server/dist/tools/git-tools.js.map +1 -0
  350. package/mcp-server/dist/tools/git.d.ts +7 -0
  351. package/mcp-server/dist/tools/git.d.ts.map +1 -0
  352. package/mcp-server/dist/tools/git.js +11 -0
  353. package/mcp-server/dist/tools/git.js.map +1 -0
  354. package/mcp-server/dist/tools/gsc/experience.d.ts +135 -0
  355. package/mcp-server/dist/tools/gsc/experience.d.ts.map +1 -0
  356. package/mcp-server/dist/tools/gsc/experience.js +351 -0
  357. package/mcp-server/dist/tools/gsc/experience.js.map +1 -0
  358. package/mcp-server/dist/tools/gsc/helpers.d.ts +78 -0
  359. package/mcp-server/dist/tools/gsc/helpers.d.ts.map +1 -0
  360. package/mcp-server/dist/tools/gsc/helpers.js +258 -0
  361. package/mcp-server/dist/tools/gsc/helpers.js.map +1 -0
  362. package/mcp-server/dist/tools/gsc/indexing.d.ts +121 -0
  363. package/mcp-server/dist/tools/gsc/indexing.d.ts.map +1 -0
  364. package/mcp-server/dist/tools/gsc/indexing.js +357 -0
  365. package/mcp-server/dist/tools/gsc/indexing.js.map +1 -0
  366. package/mcp-server/dist/tools/gsc/inspect.d.ts +78 -0
  367. package/mcp-server/dist/tools/gsc/inspect.d.ts.map +1 -0
  368. package/mcp-server/dist/tools/gsc/inspect.js +321 -0
  369. package/mcp-server/dist/tools/gsc/inspect.js.map +1 -0
  370. package/mcp-server/dist/tools/gsc/navigate.d.ts +44 -0
  371. package/mcp-server/dist/tools/gsc/navigate.d.ts.map +1 -0
  372. package/mcp-server/dist/tools/gsc/navigate.js +197 -0
  373. package/mcp-server/dist/tools/gsc/navigate.js.map +1 -0
  374. package/mcp-server/dist/tools/gsc/performance.d.ts +84 -0
  375. package/mcp-server/dist/tools/gsc/performance.d.ts.map +1 -0
  376. package/mcp-server/dist/tools/gsc/performance.js +274 -0
  377. package/mcp-server/dist/tools/gsc/performance.js.map +1 -0
  378. package/mcp-server/dist/tools/gsc/settings.d.ts +153 -0
  379. package/mcp-server/dist/tools/gsc/settings.d.ts.map +1 -0
  380. package/mcp-server/dist/tools/gsc/settings.js +425 -0
  381. package/mcp-server/dist/tools/gsc/settings.js.map +1 -0
  382. package/mcp-server/dist/tools/gsc.d.ts +7 -0
  383. package/mcp-server/dist/tools/gsc.d.ts.map +1 -0
  384. package/mcp-server/dist/tools/gsc.js +51 -0
  385. package/mcp-server/dist/tools/gsc.js.map +1 -0
  386. package/mcp-server/dist/tools/image-tools.d.ts +24 -0
  387. package/mcp-server/dist/tools/image-tools.d.ts.map +1 -0
  388. package/mcp-server/dist/tools/image-tools.js +80 -0
  389. package/mcp-server/dist/tools/image-tools.js.map +1 -0
  390. package/mcp-server/dist/tools/image.d.ts +10 -0
  391. package/mcp-server/dist/tools/image.d.ts.map +1 -0
  392. package/mcp-server/dist/tools/image.js +14 -0
  393. package/mcp-server/dist/tools/image.js.map +1 -0
  394. package/mcp-server/dist/tools/leonardo-browser-tools.d.ts +97 -0
  395. package/mcp-server/dist/tools/leonardo-browser-tools.d.ts.map +1 -0
  396. package/mcp-server/dist/tools/leonardo-browser-tools.js +228 -0
  397. package/mcp-server/dist/tools/leonardo-browser-tools.js.map +1 -0
  398. package/mcp-server/dist/tools/leonardo.d.ts +13 -0
  399. package/mcp-server/dist/tools/leonardo.d.ts.map +1 -0
  400. package/mcp-server/dist/tools/leonardo.js +21 -0
  401. package/mcp-server/dist/tools/leonardo.js.map +1 -0
  402. package/mcp-server/dist/tools/loop.d.ts +30 -0
  403. package/mcp-server/dist/tools/loop.d.ts.map +1 -0
  404. package/mcp-server/dist/tools/loop.js +401 -0
  405. package/mcp-server/dist/tools/loop.js.map +1 -0
  406. package/mcp-server/dist/tools/memories.d.ts +56 -0
  407. package/mcp-server/dist/tools/memories.d.ts.map +1 -0
  408. package/mcp-server/dist/tools/memories.js +171 -0
  409. package/mcp-server/dist/tools/memories.js.map +1 -0
  410. package/mcp-server/dist/tools/morelogin.d.ts +24 -0
  411. package/mcp-server/dist/tools/morelogin.d.ts.map +1 -0
  412. package/mcp-server/dist/tools/morelogin.js +79 -0
  413. package/mcp-server/dist/tools/morelogin.js.map +1 -0
  414. package/mcp-server/dist/tools/profile.d.ts +17 -0
  415. package/mcp-server/dist/tools/profile.d.ts.map +1 -0
  416. package/mcp-server/dist/tools/profile.js +114 -0
  417. package/mcp-server/dist/tools/profile.js.map +1 -0
  418. package/mcp-server/dist/tools/recall.d.ts +71 -0
  419. package/mcp-server/dist/tools/recall.d.ts.map +1 -0
  420. package/mcp-server/dist/tools/recall.js +147 -0
  421. package/mcp-server/dist/tools/recall.js.map +1 -0
  422. package/mcp-server/dist/tools/reflect.d.ts +56 -0
  423. package/mcp-server/dist/tools/reflect.d.ts.map +1 -0
  424. package/mcp-server/dist/tools/reflect.js +157 -0
  425. package/mcp-server/dist/tools/reflect.js.map +1 -0
  426. package/mcp-server/dist/tools/remember.d.ts +62 -0
  427. package/mcp-server/dist/tools/remember.d.ts.map +1 -0
  428. package/mcp-server/dist/tools/remember.js +201 -0
  429. package/mcp-server/dist/tools/remember.js.map +1 -0
  430. package/mcp-server/dist/tools/response.d.ts +23 -0
  431. package/mcp-server/dist/tools/response.d.ts.map +1 -0
  432. package/mcp-server/dist/tools/response.js +24 -0
  433. package/mcp-server/dist/tools/response.js.map +1 -0
  434. package/mcp-server/dist/tools/restore.d.ts +14 -0
  435. package/mcp-server/dist/tools/restore.d.ts.map +1 -0
  436. package/mcp-server/dist/tools/restore.js +23 -0
  437. package/mcp-server/dist/tools/restore.js.map +1 -0
  438. package/mcp-server/dist/tools/style-guide-tools.d.ts +51 -0
  439. package/mcp-server/dist/tools/style-guide-tools.d.ts.map +1 -0
  440. package/mcp-server/dist/tools/style-guide-tools.js +231 -0
  441. package/mcp-server/dist/tools/style-guide-tools.js.map +1 -0
  442. package/mcp-server/dist/tools/style-guide.d.ts +8 -0
  443. package/mcp-server/dist/tools/style-guide.d.ts.map +1 -0
  444. package/mcp-server/dist/tools/style-guide.js +12 -0
  445. package/mcp-server/dist/tools/style-guide.js.map +1 -0
  446. package/mcp-server/dist/tools/sync.d.ts +20 -0
  447. package/mcp-server/dist/tools/sync.d.ts.map +1 -0
  448. package/mcp-server/dist/tools/sync.js +156 -0
  449. package/mcp-server/dist/tools/sync.js.map +1 -0
  450. package/mcp-server/dist/tools/tictactoe-tools.d.ts +18 -0
  451. package/mcp-server/dist/tools/tictactoe-tools.d.ts.map +1 -0
  452. package/mcp-server/dist/tools/tictactoe-tools.js +78 -0
  453. package/mcp-server/dist/tools/tictactoe-tools.js.map +1 -0
  454. package/mcp-server/dist/tools/tictactoe.d.ts +7 -0
  455. package/mcp-server/dist/tools/tictactoe.d.ts.map +1 -0
  456. package/mcp-server/dist/tools/tictactoe.js +11 -0
  457. package/mcp-server/dist/tools/tictactoe.js.map +1 -0
  458. package/mcp-server/dist/tools/utils.d.ts +19 -0
  459. package/mcp-server/dist/tools/utils.d.ts.map +1 -0
  460. package/mcp-server/dist/tools/utils.js +54 -0
  461. package/mcp-server/dist/tools/utils.js.map +1 -0
  462. package/mcp-server/dist/tools/whiteboard-format.d.ts +88 -0
  463. package/mcp-server/dist/tools/whiteboard-format.d.ts.map +1 -0
  464. package/mcp-server/dist/tools/whiteboard-format.js +337 -0
  465. package/mcp-server/dist/tools/whiteboard-format.js.map +1 -0
  466. package/mcp-server/dist/tools/whiteboard-tools.d.ts +326 -0
  467. package/mcp-server/dist/tools/whiteboard-tools.d.ts.map +1 -0
  468. package/mcp-server/dist/tools/whiteboard-tools.js +222 -0
  469. package/mcp-server/dist/tools/whiteboard-tools.js.map +1 -0
  470. package/mcp-server/dist/tools/whiteboard.d.ts +8 -0
  471. package/mcp-server/dist/tools/whiteboard.d.ts.map +1 -0
  472. package/mcp-server/dist/tools/whiteboard.js +16 -0
  473. package/mcp-server/dist/tools/whiteboard.js.map +1 -0
  474. package/mcp-server/dist/tools/youtube/analytics.d.ts +46 -0
  475. package/mcp-server/dist/tools/youtube/analytics.d.ts.map +1 -0
  476. package/mcp-server/dist/tools/youtube/analytics.js +83 -0
  477. package/mcp-server/dist/tools/youtube/analytics.js.map +1 -0
  478. package/mcp-server/dist/tools/youtube/dedup.d.ts +35 -0
  479. package/mcp-server/dist/tools/youtube/dedup.d.ts.map +1 -0
  480. package/mcp-server/dist/tools/youtube/dedup.js +56 -0
  481. package/mcp-server/dist/tools/youtube/dedup.js.map +1 -0
  482. package/mcp-server/dist/tools/youtube/download.d.ts +31 -0
  483. package/mcp-server/dist/tools/youtube/download.d.ts.map +1 -0
  484. package/mcp-server/dist/tools/youtube/download.js +30 -0
  485. package/mcp-server/dist/tools/youtube/download.js.map +1 -0
  486. package/mcp-server/dist/tools/youtube/helpers.d.ts +66 -0
  487. package/mcp-server/dist/tools/youtube/helpers.d.ts.map +1 -0
  488. package/mcp-server/dist/tools/youtube/helpers.js +143 -0
  489. package/mcp-server/dist/tools/youtube/helpers.js.map +1 -0
  490. package/mcp-server/dist/tools/youtube/media.d.ts +43 -0
  491. package/mcp-server/dist/tools/youtube/media.d.ts.map +1 -0
  492. package/mcp-server/dist/tools/youtube/media.js +97 -0
  493. package/mcp-server/dist/tools/youtube/media.js.map +1 -0
  494. package/mcp-server/dist/tools/youtube/metadata.d.ts +33 -0
  495. package/mcp-server/dist/tools/youtube/metadata.d.ts.map +1 -0
  496. package/mcp-server/dist/tools/youtube/metadata.js +106 -0
  497. package/mcp-server/dist/tools/youtube/metadata.js.map +1 -0
  498. package/mcp-server/dist/tools/youtube/navigate.d.ts +30 -0
  499. package/mcp-server/dist/tools/youtube/navigate.d.ts.map +1 -0
  500. package/mcp-server/dist/tools/youtube/navigate.js +71 -0
  501. package/mcp-server/dist/tools/youtube/navigate.js.map +1 -0
  502. package/mcp-server/dist/tools/youtube/playlist.d.ts +29 -0
  503. package/mcp-server/dist/tools/youtube/playlist.d.ts.map +1 -0
  504. package/mcp-server/dist/tools/youtube/playlist.js +128 -0
  505. package/mcp-server/dist/tools/youtube/playlist.js.map +1 -0
  506. package/mcp-server/dist/tools/youtube/schedule.d.ts +31 -0
  507. package/mcp-server/dist/tools/youtube/schedule.d.ts.map +1 -0
  508. package/mcp-server/dist/tools/youtube/schedule.js +147 -0
  509. package/mcp-server/dist/tools/youtube/schedule.js.map +1 -0
  510. package/mcp-server/dist/tools/youtube/source.d.ts +36 -0
  511. package/mcp-server/dist/tools/youtube/source.d.ts.map +1 -0
  512. package/mcp-server/dist/tools/youtube/source.js +85 -0
  513. package/mcp-server/dist/tools/youtube/source.js.map +1 -0
  514. package/mcp-server/dist/tools/youtube/upload.d.ts +53 -0
  515. package/mcp-server/dist/tools/youtube/upload.d.ts.map +1 -0
  516. package/mcp-server/dist/tools/youtube/upload.js +255 -0
  517. package/mcp-server/dist/tools/youtube/upload.js.map +1 -0
  518. package/mcp-server/dist/tools/youtube.d.ts +12 -0
  519. package/mcp-server/dist/tools/youtube.d.ts.map +1 -0
  520. package/mcp-server/dist/tools/youtube.js +38 -0
  521. package/mcp-server/dist/tools/youtube.js.map +1 -0
  522. package/mcp-server/dist/tui.d.ts +3 -0
  523. package/mcp-server/dist/tui.d.ts.map +1 -0
  524. package/mcp-server/dist/tui.js +806 -0
  525. package/mcp-server/dist/tui.js.map +1 -0
  526. package/mcp-server/dist/types.d.ts +77 -0
  527. package/mcp-server/dist/types.d.ts.map +1 -0
  528. package/mcp-server/dist/types.js +2 -0
  529. package/mcp-server/dist/types.js.map +1 -0
  530. package/mcp-server/package-lock.json +4798 -0
  531. package/mcp-server/package.json +55 -38
  532. package/mcp-server/run.mjs +12 -2
  533. package/mcp-server/src/config.ts +48 -3
  534. package/mcp-server/src/http.ts +296 -93
  535. package/mcp-server/src/index.ts +546 -203
  536. package/mcp-server/src/preload-http.ts +19 -18
  537. package/mcp-server/src/preload.ts +19 -18
  538. package/mcp-server/src/scripts/setup-embeddings.ts +9 -0
  539. package/mcp-server/src/services/assistant-judgments.ts +157 -0
  540. package/mcp-server/src/services/browser-assist-gate.ts +383 -0
  541. package/mcp-server/src/services/browser-assist.ts +267 -0
  542. package/mcp-server/src/services/browser-bench.ts +477 -0
  543. package/mcp-server/src/services/browser-judgments.ts +414 -0
  544. package/mcp-server/src/services/browser-risk.ts +484 -0
  545. package/mcp-server/src/services/categories.ts +36 -19
  546. package/mcp-server/src/services/codex-config-heal.ts +403 -0
  547. package/mcp-server/src/services/desktop-assist-gate.ts +251 -0
  548. package/mcp-server/src/services/desktop-assist.ts +308 -0
  549. package/mcp-server/src/services/desktop-bench.ts +372 -0
  550. package/mcp-server/src/services/desktop-client.ts +76 -0
  551. package/mcp-server/src/services/desktop-judgments.ts +218 -0
  552. package/mcp-server/src/services/desktop-risk.ts +743 -0
  553. package/mcp-server/src/services/edit-stale.ts +299 -0
  554. package/mcp-server/src/services/embedding-service.ts +108 -0
  555. package/mcp-server/src/services/embedding-worker.ts +46 -0
  556. package/mcp-server/src/services/fb-regions.ts +221 -0
  557. package/mcp-server/src/services/file-checksums.ts +26 -9
  558. package/mcp-server/src/services/file-excerpt.ts +59 -0
  559. package/mcp-server/src/services/identity.ts +291 -0
  560. package/mcp-server/src/services/local-embeddings.ts +2 -77
  561. package/mcp-server/src/services/memory-client.ts +23 -0
  562. package/mcp-server/src/services/memory-judge-backfill.ts +462 -0
  563. package/mcp-server/src/services/memory-judgments.ts +901 -0
  564. package/mcp-server/src/services/memory-maintenance.ts +360 -0
  565. package/mcp-server/src/services/memory-retrieval.ts +349 -0
  566. package/mcp-server/src/services/memory-schema.ts +280 -0
  567. package/mcp-server/src/services/memory-sessions.ts +87 -0
  568. package/mcp-server/src/services/memory-triage.ts +182 -0
  569. package/mcp-server/src/services/memory-verification.ts +68 -0
  570. package/mcp-server/src/services/memory-writer.ts +125 -0
  571. package/mcp-server/src/services/neural-interface.ts +1217 -73
  572. package/mcp-server/src/services/profiles.ts +428 -0
  573. package/mcp-server/src/services/secret-gate.ts +52 -0
  574. package/mcp-server/src/services/server-instructions.ts +122 -0
  575. package/mcp-server/src/services/sqlite.ts +1456 -823
  576. package/mcp-server/src/services/typesafe-bench.ts +247 -0
  577. package/mcp-server/src/services/typesafe-config.ts +790 -0
  578. package/mcp-server/src/services/typesafe.ts +390 -0
  579. package/mcp-server/src/services/vector-cache.ts +255 -0
  580. package/mcp-server/src/services/whiteboard-geometry.ts +711 -0
  581. package/mcp-server/src/tools/agents.ts +539 -0
  582. package/mcp-server/src/tools/bluesky.ts +1186 -0
  583. package/mcp-server/src/tools/browser-advanced.ts +15 -6
  584. package/mcp-server/src/tools/browser-batch.ts +178 -0
  585. package/mcp-server/src/tools/browser-cheatsheet.ts +155 -0
  586. package/mcp-server/src/tools/browser-interact.ts +362 -255
  587. package/mcp-server/src/tools/browser-navigate.ts +164 -77
  588. package/mcp-server/src/tools/browser-observe.ts +2222 -1330
  589. package/mcp-server/src/tools/browser.ts +181 -107
  590. package/mcp-server/src/tools/card.ts +7 -5
  591. package/mcp-server/src/tools/choice.ts +103 -0
  592. package/mcp-server/src/tools/computer.ts +190 -0
  593. package/mcp-server/src/tools/discord.ts +10 -23
  594. package/mcp-server/src/tools/fb-groups.ts +387 -0
  595. package/mcp-server/src/tools/forget.ts +1 -1
  596. package/mcp-server/src/tools/git.ts +3 -1
  597. package/mcp-server/src/tools/gsc/experience.ts +440 -0
  598. package/mcp-server/src/tools/gsc/helpers.ts +288 -0
  599. package/mcp-server/src/tools/gsc/indexing.ts +495 -0
  600. package/mcp-server/src/tools/gsc/inspect.ts +391 -0
  601. package/mcp-server/src/tools/gsc/navigate.ts +236 -0
  602. package/mcp-server/src/tools/gsc/performance.ts +352 -0
  603. package/mcp-server/src/tools/gsc/settings.ts +564 -0
  604. package/mcp-server/src/tools/gsc.ts +95 -0
  605. package/mcp-server/src/tools/image.ts +3 -1
  606. package/mcp-server/src/tools/leonardo-browser-tools.ts +47 -37
  607. package/mcp-server/src/tools/leonardo.ts +7 -5
  608. package/mcp-server/src/tools/loop.ts +118 -25
  609. package/mcp-server/src/tools/memories.ts +65 -5
  610. package/mcp-server/src/tools/morelogin.ts +81 -0
  611. package/mcp-server/src/tools/profile.ts +122 -0
  612. package/mcp-server/src/tools/recall.ts +105 -261
  613. package/mcp-server/src/tools/reflect.ts +171 -145
  614. package/mcp-server/src/tools/remember.ts +223 -115
  615. package/mcp-server/src/tools/style-guide-tools.ts +250 -0
  616. package/mcp-server/src/tools/style-guide.ts +16 -0
  617. package/mcp-server/src/tools/sync.ts +66 -9
  618. package/mcp-server/src/tools/tictactoe.ts +3 -1
  619. package/mcp-server/src/tools/utils.ts +22 -0
  620. package/mcp-server/src/tools/whiteboard-format.ts +394 -0
  621. package/mcp-server/src/tools/whiteboard-tools.ts +191 -183
  622. package/mcp-server/src/tools/whiteboard.ts +7 -5
  623. package/mcp-server/src/tools/youtube/analytics.ts +106 -0
  624. package/mcp-server/src/tools/youtube/dedup.ts +57 -0
  625. package/mcp-server/src/tools/youtube/download.ts +41 -0
  626. package/mcp-server/src/tools/youtube/helpers.ts +183 -0
  627. package/mcp-server/src/tools/youtube/media.ts +109 -0
  628. package/mcp-server/src/tools/youtube/metadata.ts +112 -0
  629. package/mcp-server/src/tools/youtube/navigate.ts +81 -0
  630. package/mcp-server/src/tools/youtube/playlist.ts +134 -0
  631. package/mcp-server/src/tools/youtube/schedule.ts +158 -0
  632. package/mcp-server/src/tools/youtube/source.ts +101 -0
  633. package/mcp-server/src/tools/youtube/upload.ts +296 -0
  634. package/mcp-server/src/tools/youtube.ts +47 -0
  635. package/mcp-server/src/tui.ts +8 -9
  636. package/neural-interface/games/TicTacToe/1.svg +1 -0
  637. package/neural-interface/games/TicTacToe/2.svg +1 -0
  638. package/neural-interface/games/TicTacToe/3.svg +1 -0
  639. package/neural-interface/games/TicTacToe/4.svg +1 -0
  640. package/neural-interface/games/TicTacToe/5.svg +1 -0
  641. package/neural-interface/games/TicTacToe/6.svg +1 -0
  642. package/neural-interface/games/TicTacToe/7.svg +1 -0
  643. package/neural-interface/games/TicTacToe/8.svg +1 -0
  644. package/neural-interface/games/TicTacToe/9.svg +1 -0
  645. package/neural-interface/games/TicTacToe/Board.svg +1 -0
  646. package/neural-interface/games/TicTacToe/Circle.svg +1 -0
  647. package/neural-interface/games/TicTacToe/Cross.svg +1 -0
  648. package/neural-interface/i18n/en.json +7022 -0
  649. package/neural-interface/i18n/pt-BR.json +7026 -0
  650. package/neural-interface/lib/assistant-api.js +1005 -0
  651. package/neural-interface/lib/assistant-attachments.js +121 -0
  652. package/neural-interface/lib/assistant-brains/claude.js +207 -0
  653. package/neural-interface/lib/assistant-brains/codex-browser-policy-hook.mjs +43 -0
  654. package/neural-interface/lib/assistant-brains/codex-route-gate-hook.mjs +56 -0
  655. package/neural-interface/lib/assistant-brains/codex.js +213 -0
  656. package/neural-interface/lib/assistant-brains/opencode-browser-policy.js +47 -0
  657. package/neural-interface/lib/assistant-brains/opencode-route-gate.js +62 -0
  658. package/neural-interface/lib/assistant-brains/opencode.js +320 -0
  659. package/neural-interface/lib/assistant-budget.js +318 -0
  660. package/neural-interface/lib/assistant-catalog.js +731 -0
  661. package/neural-interface/lib/assistant-clarify.js +664 -0
  662. package/neural-interface/lib/assistant-config.js +232 -0
  663. package/neural-interface/lib/assistant-dispatch.js +2744 -0
  664. package/neural-interface/lib/assistant-envelope.js +582 -0
  665. package/neural-interface/lib/assistant-evidence.js +236 -0
  666. package/neural-interface/lib/assistant-hidden-models.js +272 -0
  667. package/neural-interface/lib/assistant-jev.js +117 -0
  668. package/neural-interface/lib/assistant-judgments.js +111 -0
  669. package/neural-interface/lib/assistant-media.js +335 -0
  670. package/neural-interface/lib/assistant-memory.js +398 -0
  671. package/neural-interface/lib/assistant-persona.js +444 -0
  672. package/neural-interface/lib/assistant-plan-permissions.js +448 -0
  673. package/neural-interface/lib/assistant-playbooks/design.md +87 -0
  674. package/neural-interface/lib/assistant-playbooks.js +72 -0
  675. package/neural-interface/lib/assistant-pricing.js +159 -0
  676. package/neural-interface/lib/assistant-route-gate-codex.js +163 -0
  677. package/neural-interface/lib/assistant-route-gate.js +212 -0
  678. package/neural-interface/lib/assistant-router.js +1324 -0
  679. package/neural-interface/lib/assistant-runtime.js +3337 -0
  680. package/neural-interface/lib/assistant-task-prompt.js +472 -0
  681. package/neural-interface/lib/assistant-usage.js +1688 -0
  682. package/neural-interface/lib/augmented-path.js +70 -0
  683. package/neural-interface/lib/automation-storage-migration.js +54 -0
  684. package/neural-interface/lib/backup-service.js +851 -0
  685. package/neural-interface/lib/backup-zip-reader.js +262 -0
  686. package/neural-interface/lib/browser-assist-api.js +41 -0
  687. package/neural-interface/lib/browser-console.js +176 -0
  688. package/neural-interface/lib/browser-execution.js +241 -0
  689. package/neural-interface/lib/browser-extraction.js +191 -0
  690. package/neural-interface/lib/browser-language.js +83 -0
  691. package/neural-interface/lib/browser-screenshot.js +399 -0
  692. package/neural-interface/lib/browser-semantic-context.js +639 -0
  693. package/neural-interface/lib/browser-snapshots.js +192 -0
  694. package/neural-interface/lib/browser-tab-recovery.js +119 -0
  695. package/neural-interface/lib/browser-targets.js +70 -0
  696. package/neural-interface/lib/browser-tool-policy.js +926 -0
  697. package/neural-interface/lib/claude-accounts.js +230 -0
  698. package/neural-interface/lib/claude-agent-bridge.js +3203 -0
  699. package/neural-interface/lib/claude-bypass-policy.js +99 -0
  700. package/neural-interface/lib/claude-executable.js +241 -0
  701. package/neural-interface/lib/claude-history.js +429 -0
  702. package/neural-interface/lib/claude-model-catalog.js +444 -0
  703. package/neural-interface/lib/claude-panel-requests.js +165 -0
  704. package/neural-interface/lib/claude-panel-session.js +272 -0
  705. package/neural-interface/lib/claude-permission-rules.js +153 -0
  706. package/neural-interface/lib/claude-session-cleanup.js +71 -0
  707. package/neural-interface/lib/claude-session-list.js +82 -0
  708. package/neural-interface/lib/claude-session-ops.js +140 -0
  709. package/neural-interface/lib/claude-settings-view.js +51 -0
  710. package/neural-interface/lib/claude-skin-connect.js +79 -0
  711. package/neural-interface/lib/claude-stall-policy.js +7 -0
  712. package/neural-interface/lib/claude-temporary.js +435 -0
  713. package/neural-interface/lib/cli-update-planner.js +312 -0
  714. package/neural-interface/lib/codex-capabilities.d.ts +41 -0
  715. package/neural-interface/lib/codex-capabilities.js +148 -0
  716. package/neural-interface/lib/codex-collaboration-mode.js +17 -0
  717. package/neural-interface/lib/codex-compat-rpc.js +241 -0
  718. package/neural-interface/lib/codex-mcp-approvals.js +56 -0
  719. package/neural-interface/lib/codex-mcp-apps.js +47 -0
  720. package/neural-interface/lib/codex-model-context.js +202 -0
  721. package/neural-interface/lib/codex-plan-store.js +71 -0
  722. package/neural-interface/lib/codex-request-journal.js +242 -0
  723. package/neural-interface/lib/codex-runtime-path.js +213 -0
  724. package/neural-interface/lib/codex-session-catalog.js +231 -0
  725. package/neural-interface/lib/codex-session-settings.js +46 -0
  726. package/neural-interface/lib/codex-sidepanel-permissions.js +110 -0
  727. package/neural-interface/lib/codex-thread-history.js +165 -0
  728. package/neural-interface/lib/codex-user-verification.js +34 -0
  729. package/neural-interface/lib/codex-writer-lifecycle.js +152 -0
  730. package/neural-interface/lib/confined-fs.js +256 -0
  731. package/neural-interface/lib/db.js +922 -0
  732. package/neural-interface/lib/design-md.js +9 -0
  733. package/neural-interface/lib/desktop/api.js +92 -0
  734. package/neural-interface/lib/desktop/audit.js +62 -0
  735. package/neural-interface/lib/desktop/build.js +423 -0
  736. package/neural-interface/lib/desktop/config.js +606 -0
  737. package/neural-interface/lib/desktop/coords.js +76 -0
  738. package/neural-interface/lib/desktop/fake-helper.js +1192 -0
  739. package/neural-interface/lib/desktop/frames.js +77 -0
  740. package/neural-interface/lib/desktop/grants.js +84 -0
  741. package/neural-interface/lib/desktop/guards.js +170 -0
  742. package/neural-interface/lib/desktop/helper/SynabunDesktop.swift +3056 -0
  743. package/neural-interface/lib/desktop/lease.js +51 -0
  744. package/neural-interface/lib/desktop/manager.js +545 -0
  745. package/neural-interface/lib/desktop/press-contexts.js +103 -0
  746. package/neural-interface/lib/desktop/protocol.js +495 -0
  747. package/neural-interface/lib/desktop/service.js +1202 -0
  748. package/neural-interface/lib/effort-levels.js +148 -0
  749. package/neural-interface/lib/event-loop-monitor.js +67 -0
  750. package/neural-interface/lib/exec-loop-command.js +209 -0
  751. package/neural-interface/lib/folder-picker.ps1 +77 -0
  752. package/neural-interface/lib/http-guards.js +287 -0
  753. package/neural-interface/lib/jsonl-lines.js +94 -0
  754. package/neural-interface/lib/loop-logger.js +127 -0
  755. package/neural-interface/lib/mcp-client-config.js +133 -0
  756. package/neural-interface/lib/mcp-installer.js +487 -0
  757. package/neural-interface/lib/mcp-profile-refresh-coordinator.js +78 -0
  758. package/neural-interface/lib/mcp-profile-servers.js +120 -0
  759. package/neural-interface/lib/memo-cache.js +84 -0
  760. package/neural-interface/lib/memory-api.js +758 -0
  761. package/neural-interface/lib/memory-map-api.js +1150 -0
  762. package/neural-interface/lib/memory-map-layout.js +872 -0
  763. package/neural-interface/lib/memory-map-worker.js +165 -0
  764. package/neural-interface/lib/memory-session-capture.js +81 -0
  765. package/neural-interface/lib/memory-timers.js +62 -0
  766. package/neural-interface/lib/morelogin.js +301 -0
  767. package/neural-interface/lib/native-binary-runtime.js +382 -0
  768. package/neural-interface/lib/native-loop-goal.js +33 -0
  769. package/neural-interface/lib/native-loop-providers.js +1002 -0
  770. package/neural-interface/lib/native-loop-runtime.js +747 -0
  771. package/neural-interface/lib/offline-page.js +54 -0
  772. package/neural-interface/lib/opencode-hidden-models.js +113 -0
  773. package/neural-interface/lib/opencode-history.js +253 -0
  774. package/neural-interface/lib/opencode-runtime-config.js +58 -0
  775. package/neural-interface/lib/opencode-v2-client.js +486 -0
  776. package/neural-interface/lib/opencode-v2-ws-requests.js +955 -0
  777. package/neural-interface/lib/path-confine.js +213 -0
  778. package/neural-interface/lib/plugin-installer.js +264 -0
  779. package/neural-interface/lib/project-storage.js +766 -0
  780. package/neural-interface/lib/pty-host/core.js +603 -0
  781. package/neural-interface/lib/pty-host/flow.js +43 -0
  782. package/neural-interface/lib/pty-host/host.js +47 -0
  783. package/neural-interface/lib/pty-host/manager.js +360 -0
  784. package/neural-interface/lib/pty-host/protocol.js +81 -0
  785. package/neural-interface/lib/pty-host/snapshot.js +133 -0
  786. package/neural-interface/lib/remote-policy.js +582 -0
  787. package/neural-interface/lib/rulesets/api.js +175 -0
  788. package/neural-interface/lib/rulesets/fs-safe.js +245 -0
  789. package/neural-interface/lib/rulesets/installer.js +1453 -0
  790. package/neural-interface/lib/rulesets/json-edit.js +186 -0
  791. package/neural-interface/lib/rulesets/lock.js +170 -0
  792. package/neural-interface/lib/rulesets/managed-text.js +314 -0
  793. package/neural-interface/lib/rulesets/normalise.js +24 -0
  794. package/neural-interface/lib/rulesets/render.js +84 -0
  795. package/neural-interface/lib/schedule-launch-intent.js +40 -0
  796. package/neural-interface/lib/session-cache-builder.js +524 -0
  797. package/neural-interface/lib/session-chunker.js +344 -0
  798. package/neural-interface/lib/session-indexer.js +327 -0
  799. package/neural-interface/lib/session-title-generator.js +232 -0
  800. package/neural-interface/lib/skill-prompt-bridge.js +11 -0
  801. package/neural-interface/lib/style-guide/api.js +375 -0
  802. package/neural-interface/lib/style-guide/color.js +352 -0
  803. package/neural-interface/lib/style-guide/export-css.js +99 -0
  804. package/neural-interface/lib/style-guide/export-dtcg.js +209 -0
  805. package/neural-interface/lib/style-guide/export-tailwind.js +102 -0
  806. package/neural-interface/lib/style-guide/fonts.json +227 -0
  807. package/neural-interface/lib/style-guide/import.js +1268 -0
  808. package/neural-interface/lib/style-guide/pointers.js +103 -0
  809. package/neural-interface/lib/style-guide/presets.js +232 -0
  810. package/neural-interface/lib/style-guide/render-design-md.js +319 -0
  811. package/neural-interface/lib/style-guide/schema.js +848 -0
  812. package/neural-interface/lib/style-guide/security.js +43 -0
  813. package/neural-interface/lib/style-guide/store.js +491 -0
  814. package/neural-interface/lib/style-guide/summary.js +106 -0
  815. package/neural-interface/lib/style-guide/tokens.js +170 -0
  816. package/neural-interface/lib/synabun-update-plan.js +324 -0
  817. package/neural-interface/lib/synabun-version.js +179 -0
  818. package/neural-interface/lib/system-restore.js +99 -0
  819. package/neural-interface/lib/terminal-env.js +86 -0
  820. package/neural-interface/lib/ui-blob-store.js +106 -0
  821. package/neural-interface/lib/vector-cache.js +2 -0
  822. package/neural-interface/lib/virtual-socket.js +60 -0
  823. package/neural-interface/lib/whatsapp/api.js +146 -0
  824. package/neural-interface/lib/whatsapp/auth-store.js +389 -0
  825. package/neural-interface/lib/whatsapp/baileys-adapter.js +457 -0
  826. package/neural-interface/lib/whatsapp/brain.js +128 -0
  827. package/neural-interface/lib/whatsapp/bridge.js +1499 -0
  828. package/neural-interface/lib/whatsapp/cards.js +519 -0
  829. package/neural-interface/lib/whatsapp/commands.js +92 -0
  830. package/neural-interface/lib/whatsapp/config.js +291 -0
  831. package/neural-interface/lib/whatsapp/connector/entry.mjs +25 -0
  832. package/neural-interface/lib/whatsapp/connector/manifest.json +6 -0
  833. package/neural-interface/lib/whatsapp/connector/npm-shrinkwrap.json +717 -0
  834. package/neural-interface/lib/whatsapp/connector/package.json +8 -0
  835. package/neural-interface/lib/whatsapp/fake-baileys.js +444 -0
  836. package/neural-interface/lib/whatsapp/format.js +552 -0
  837. package/neural-interface/lib/whatsapp/host-core.js +1225 -0
  838. package/neural-interface/lib/whatsapp/host.js +119 -0
  839. package/neural-interface/lib/whatsapp/identity.js +288 -0
  840. package/neural-interface/lib/whatsapp/inbound.js +149 -0
  841. package/neural-interface/lib/whatsapp/installer.js +562 -0
  842. package/neural-interface/lib/whatsapp/manager.js +638 -0
  843. package/neural-interface/lib/whatsapp/paths.js +81 -0
  844. package/neural-interface/lib/whatsapp/protocol.js +327 -0
  845. package/neural-interface/lib/whatsapp/qr-svg.js +57 -0
  846. package/neural-interface/lib/whatsapp/redact.js +130 -0
  847. package/neural-interface/lib/whatsapp/service.js +1305 -0
  848. package/neural-interface/lib/whatsapp/vendor/qrcodegen.js +848 -0
  849. package/neural-interface/package-lock.json +4431 -0
  850. package/neural-interface/package.json +58 -27
  851. package/neural-interface/public/apple-touch-icon.png +0 -0
  852. package/neural-interface/public/category-logos/synabun.png +0 -0
  853. package/neural-interface/public/claude.svg +1 -0
  854. package/neural-interface/public/favicon-16x16.png +0 -0
  855. package/neural-interface/public/favicon-32x32.png +0 -0
  856. package/neural-interface/public/icon-192.png +0 -0
  857. package/neural-interface/public/icon-512.png +0 -0
  858. package/neural-interface/public/index.html +32 -182
  859. package/neural-interface/public/index2d.html +2 -2
  860. package/neural-interface/public/install-app.html +31 -0
  861. package/neural-interface/public/logo-synabun.png +0 -0
  862. package/neural-interface/public/logoHD.png +0 -0
  863. package/neural-interface/public/manifest.json +2 -0
  864. package/neural-interface/public/mcp.svg +4 -0
  865. package/neural-interface/public/offline.html +670 -299
  866. package/neural-interface/public/onboarding.html +804 -161
  867. package/neural-interface/public/opencode-logo-dark-square.svg +1 -0
  868. package/neural-interface/public/shared/agent-runtime-options.js +596 -0
  869. package/neural-interface/public/shared/api.js +339 -28
  870. package/neural-interface/public/shared/assistant/asst-accounts.js +318 -0
  871. package/neural-interface/public/shared/assistant/asst-agents-dock.js +296 -0
  872. package/neural-interface/public/shared/assistant/asst-api.js +270 -0
  873. package/neural-interface/public/shared/assistant/asst-brain-picker.js +744 -0
  874. package/neural-interface/public/shared/assistant/asst-budget.js +333 -0
  875. package/neural-interface/public/shared/assistant/asst-composer.js +656 -0
  876. package/neural-interface/public/shared/assistant/asst-computer.js +803 -0
  877. package/neural-interface/public/shared/assistant/asst-control.js +1018 -0
  878. package/neural-interface/public/shared/assistant/asst-hosts.js +262 -0
  879. package/neural-interface/public/shared/assistant/asst-mascot.js +313 -0
  880. package/neural-interface/public/shared/assistant/asst-menu.js +375 -0
  881. package/neural-interface/public/shared/assistant/asst-panel.js +1845 -0
  882. package/neural-interface/public/shared/assistant/asst-rack-styles.js +586 -0
  883. package/neural-interface/public/shared/assistant/asst-render.js +3305 -0
  884. package/neural-interface/public/shared/assistant/asst-route.js +1653 -0
  885. package/neural-interface/public/shared/assistant/asst-roving.js +75 -0
  886. package/neural-interface/public/shared/assistant/asst-sanitize.js +117 -0
  887. package/neural-interface/public/shared/assistant/asst-sidepanel-styles.js +488 -0
  888. package/neural-interface/public/shared/assistant/asst-sidepanel.js +675 -0
  889. package/neural-interface/public/shared/assistant/asst-slash.js +258 -0
  890. package/neural-interface/public/shared/assistant/asst-state.js +727 -0
  891. package/neural-interface/public/shared/assistant/asst-styles.js +1686 -0
  892. package/neural-interface/public/shared/assistant/asst-tool-kinds.js +1017 -0
  893. package/neural-interface/public/shared/assistant/asst-usage.js +444 -0
  894. package/neural-interface/public/shared/assistant/asst-ws.js +175 -0
  895. package/neural-interface/public/shared/automation-context.js +75 -0
  896. package/neural-interface/public/shared/blob-store.js +99 -0
  897. package/neural-interface/public/shared/cdx/cdx-capabilities.js +118 -0
  898. package/neural-interface/public/shared/cdx/cdx-cards.js +37 -0
  899. package/neural-interface/public/shared/cdx/cdx-commands.js +171 -0
  900. package/neural-interface/public/shared/cdx/cdx-context-menu.js +402 -0
  901. package/neural-interface/public/shared/cdx/cdx-context-model.js +220 -0
  902. package/neural-interface/public/shared/cdx/cdx-features.js +408 -0
  903. package/neural-interface/public/shared/cdx/cdx-icons.js +89 -0
  904. package/neural-interface/public/shared/cdx/cdx-mcp-app-host.js +414 -0
  905. package/neural-interface/public/shared/cdx/cdx-mcp-app-proxy.html +6 -0
  906. package/neural-interface/public/shared/cdx/cdx-mcp-app-proxy.js +56 -0
  907. package/neural-interface/public/shared/cdx/cdx-mcp-app.js +178 -0
  908. package/neural-interface/public/shared/cdx/cdx-model-catalog.js +120 -0
  909. package/neural-interface/public/shared/cdx/cdx-output-model.js +261 -0
  910. package/neural-interface/public/shared/cdx/cdx-output.js +382 -0
  911. package/neural-interface/public/shared/cdx/cdx-panel.js +1372 -0
  912. package/neural-interface/public/shared/cdx/cdx-plan.js +266 -0
  913. package/neural-interface/public/shared/cdx/cdx-protocol.js +682 -0
  914. package/neural-interface/public/shared/cdx/cdx-render.js +3381 -0
  915. package/neural-interface/public/shared/cdx/cdx-requests.js +1136 -0
  916. package/neural-interface/public/shared/cdx/cdx-styles.js +3251 -0
  917. package/neural-interface/public/shared/cdx/cdx-tabs.js +7992 -0
  918. package/neural-interface/public/shared/cdx/cdx-ws.js +363 -0
  919. package/neural-interface/public/shared/cli-status.js +169 -0
  920. package/neural-interface/public/shared/command-runner-model.js +30 -0
  921. package/neural-interface/public/shared/constants.js +22 -5
  922. package/neural-interface/public/shared/cp/cp-agents.js +186 -0
  923. package/neural-interface/public/shared/cp/cp-bash.js +252 -0
  924. package/neural-interface/public/shared/cp/cp-compose.js +159 -0
  925. package/neural-interface/public/shared/cp/cp-context-menu.js +309 -0
  926. package/neural-interface/public/shared/cp/cp-context-model.js +157 -0
  927. package/neural-interface/public/shared/cp/cp-context-window.js +80 -0
  928. package/neural-interface/public/shared/cp/cp-ctx.js +11 -0
  929. package/neural-interface/public/shared/cp/cp-diff.js +255 -0
  930. package/neural-interface/public/shared/cp/cp-event-rows.js +637 -0
  931. package/neural-interface/public/shared/cp/cp-events.js +846 -0
  932. package/neural-interface/public/shared/cp/cp-markdown.js +104 -0
  933. package/neural-interface/public/shared/cp/cp-permission-model.js +905 -0
  934. package/neural-interface/public/shared/cp/cp-permissions.js +626 -0
  935. package/neural-interface/public/shared/cp/cp-rehydrate.js +113 -0
  936. package/neural-interface/public/shared/cp/cp-restore.js +224 -0
  937. package/neural-interface/public/shared/cp/cp-session-model.js +338 -0
  938. package/neural-interface/public/shared/cp/cp-session-settings.js +254 -0
  939. package/neural-interface/public/shared/cp/cp-sessions.js +227 -0
  940. package/neural-interface/public/shared/cp/cp-statusline.js +153 -0
  941. package/neural-interface/public/shared/cp/cp-styles.js +349 -0
  942. package/neural-interface/public/shared/cp/cp-tasks-model.js +282 -0
  943. package/neural-interface/public/shared/cp/cp-tasks.js +107 -0
  944. package/neural-interface/public/shared/cp/cp-temporary-view.js +56 -0
  945. package/neural-interface/public/shared/cp/cp-temporary.js +231 -0
  946. package/neural-interface/public/shared/cp/cp-tool-cards.js +326 -0
  947. package/neural-interface/public/shared/cp/cp-tool-results.js +618 -0
  948. package/neural-interface/public/shared/cp/cp-usage-model.js +124 -0
  949. package/neural-interface/public/shared/graph-activity.js +68 -0
  950. package/neural-interface/public/shared/html-shell.js +116 -17
  951. package/neural-interface/public/shared/i18n.js +132 -34
  952. package/neural-interface/public/shared/node-positions-store.js +44 -0
  953. package/neural-interface/public/shared/ocp-hidden-models.js +116 -0
  954. package/neural-interface/public/shared/ocp-v2/ocp-v2-approvals.js +355 -0
  955. package/neural-interface/public/shared/ocp-v2/ocp-v2-autoaccept-button.js +35 -0
  956. package/neural-interface/public/shared/ocp-v2/ocp-v2-automation-ownership.js +72 -0
  957. package/neural-interface/public/shared/ocp-v2/ocp-v2-binding.js +280 -0
  958. package/neural-interface/public/shared/ocp-v2/ocp-v2-caps.js +67 -0
  959. package/neural-interface/public/shared/ocp-v2/ocp-v2-changes-logic.js +179 -0
  960. package/neural-interface/public/shared/ocp-v2/ocp-v2-changes.js +164 -0
  961. package/neural-interface/public/shared/ocp-v2/ocp-v2-childpanel.js +725 -0
  962. package/neural-interface/public/shared/ocp-v2/ocp-v2-compact-button.js +75 -0
  963. package/neural-interface/public/shared/ocp-v2/ocp-v2-composer-logic.js +1204 -0
  964. package/neural-interface/public/shared/ocp-v2/ocp-v2-confirm-logic.js +117 -0
  965. package/neural-interface/public/shared/ocp-v2/ocp-v2-confirm.js +59 -0
  966. package/neural-interface/public/shared/ocp-v2/ocp-v2-context-gauge.js +252 -0
  967. package/neural-interface/public/shared/ocp-v2/ocp-v2-context-menu.js +478 -0
  968. package/neural-interface/public/shared/ocp-v2/ocp-v2-context-model.js +179 -0
  969. package/neural-interface/public/shared/ocp-v2/ocp-v2-events.js +233 -0
  970. package/neural-interface/public/shared/ocp-v2/ocp-v2-icons.js +30 -0
  971. package/neural-interface/public/shared/ocp-v2/ocp-v2-manager.js +1065 -0
  972. package/neural-interface/public/shared/ocp-v2/ocp-v2-mention-hints.js +182 -0
  973. package/neural-interface/public/shared/ocp-v2/ocp-v2-modelpicker.js +655 -0
  974. package/neural-interface/public/shared/ocp-v2/ocp-v2-panel.js +2636 -0
  975. package/neural-interface/public/shared/ocp-v2/ocp-v2-plan.js +601 -0
  976. package/neural-interface/public/shared/ocp-v2/ocp-v2-profile-inheritance.js +78 -0
  977. package/neural-interface/public/shared/ocp-v2/ocp-v2-projectbar.js +649 -0
  978. package/neural-interface/public/shared/ocp-v2/ocp-v2-providers.js +35 -0
  979. package/neural-interface/public/shared/ocp-v2/ocp-v2-rehydrate.js +357 -0
  980. package/neural-interface/public/shared/ocp-v2/ocp-v2-render-logic.js +189 -0
  981. package/neural-interface/public/shared/ocp-v2/ocp-v2-render.js +1869 -0
  982. package/neural-interface/public/shared/ocp-v2/ocp-v2-select.js +42 -0
  983. package/neural-interface/public/shared/ocp-v2/ocp-v2-send-logic.js +117 -0
  984. package/neural-interface/public/shared/ocp-v2/ocp-v2-send.js +1714 -0
  985. package/neural-interface/public/shared/ocp-v2/ocp-v2-session-actions.js +468 -0
  986. package/neural-interface/public/shared/ocp-v2/ocp-v2-session-widgets.js +63 -0
  987. package/neural-interface/public/shared/ocp-v2/ocp-v2-sessions-logic.js +366 -0
  988. package/neural-interface/public/shared/ocp-v2/ocp-v2-settings-link.js +19 -0
  989. package/neural-interface/public/shared/ocp-v2/ocp-v2-slash-hints.js +343 -0
  990. package/neural-interface/public/shared/ocp-v2/ocp-v2-state.js +746 -0
  991. package/neural-interface/public/shared/ocp-v2/ocp-v2-status-logic.js +347 -0
  992. package/neural-interface/public/shared/ocp-v2/ocp-v2-status.js +338 -0
  993. package/neural-interface/public/shared/ocp-v2/ocp-v2-styles.js +3543 -0
  994. package/neural-interface/public/shared/ocp-v2/ocp-v2-subagents-logic.js +482 -0
  995. package/neural-interface/public/shared/ocp-v2/ocp-v2-tools-logic.js +351 -0
  996. package/neural-interface/public/shared/ocp-v2/ocp-v2-tools.js +168 -0
  997. package/neural-interface/public/shared/ocp-v2/ocp-v2-variantpicker.js +315 -0
  998. package/neural-interface/public/shared/ocp-v2/ocp-v2-ws.js +545 -0
  999. package/neural-interface/public/shared/provider-icons.js +30 -0
  1000. package/neural-interface/public/shared/pwa-install-page.js +34 -0
  1001. package/neural-interface/public/shared/pwa-install.css +12 -0
  1002. package/neural-interface/public/shared/pwa-install.js +28 -0
  1003. package/neural-interface/public/shared/session-title.js +100 -0
  1004. package/neural-interface/public/shared/settings/settings-ia.js +389 -0
  1005. package/neural-interface/public/shared/settings/settings-kit.js +234 -0
  1006. package/neural-interface/public/shared/settings/settings-rules.js +160 -0
  1007. package/neural-interface/public/shared/settings/settings-search.js +463 -0
  1008. package/neural-interface/public/shared/settings/settings-shell.js +570 -0
  1009. package/neural-interface/public/shared/start-bridge.js +583 -0
  1010. package/neural-interface/public/shared/state.js +5 -0
  1011. package/neural-interface/public/shared/storage.js +50 -7
  1012. package/neural-interface/public/shared/styleguide/sg-agents.js +42 -0
  1013. package/neural-interface/public/shared/styleguide/sg-brand.js +4 -0
  1014. package/neural-interface/public/shared/styleguide/sg-color-client.js +388 -0
  1015. package/neural-interface/public/shared/styleguide/sg-colors.js +151 -0
  1016. package/neural-interface/public/shared/styleguide/sg-components.js +35 -0
  1017. package/neural-interface/public/shared/styleguide/sg-history.js +41 -0
  1018. package/neural-interface/public/shared/styleguide/sg-icons-logo.js +38 -0
  1019. package/neural-interface/public/shared/styleguide/sg-imagery.js +4 -0
  1020. package/neural-interface/public/shared/styleguide/sg-import-export.js +48 -0
  1021. package/neural-interface/public/shared/styleguide/sg-kit.js +284 -0
  1022. package/neural-interface/public/shared/styleguide/sg-labels.js +318 -0
  1023. package/neural-interface/public/shared/styleguide/sg-layout.js +4 -0
  1024. package/neural-interface/public/shared/styleguide/sg-motion.js +5 -0
  1025. package/neural-interface/public/shared/styleguide/sg-panel.js +237 -0
  1026. package/neural-interface/public/shared/styleguide/sg-preview.js +138 -0
  1027. package/neural-interface/public/shared/styleguide/sg-rules.js +9 -0
  1028. package/neural-interface/public/shared/styleguide/sg-shape.js +24 -0
  1029. package/neural-interface/public/shared/styleguide/sg-state.js +132 -0
  1030. package/neural-interface/public/shared/styleguide/sg-typography.js +125 -0
  1031. package/neural-interface/public/shared/styles.css +8614 -1477
  1032. package/neural-interface/public/shared/synabun-mascot.js +1625 -0
  1033. package/neural-interface/public/shared/synabun-ticker.js +376 -0
  1034. package/neural-interface/public/shared/term-ready.js +307 -0
  1035. package/neural-interface/public/shared/term-renderer.js +343 -0
  1036. package/neural-interface/public/shared/ui-assistant-panel.js +10 -0
  1037. package/neural-interface/public/shared/ui-assistant.js +77 -0
  1038. package/neural-interface/public/shared/ui-automation-studio.js +5105 -3573
  1039. package/neural-interface/public/shared/ui-claude-panel.js +11685 -6333
  1040. package/neural-interface/public/shared/ui-codex-panel.js +12 -0
  1041. package/neural-interface/public/shared/ui-command-runner.js +251 -468
  1042. package/neural-interface/public/shared/ui-cost-widget.js +55 -34
  1043. package/neural-interface/public/shared/ui-detail.js +77 -14
  1044. package/neural-interface/public/shared/ui-explorer.js +360 -181
  1045. package/neural-interface/public/shared/ui-file-explorer.js +485 -107
  1046. package/neural-interface/public/shared/ui-help.js +2 -2
  1047. package/neural-interface/public/shared/ui-image-gallery.js +45 -21
  1048. package/neural-interface/public/shared/ui-judgments.js +1346 -0
  1049. package/neural-interface/public/shared/ui-keybinds.js +3 -2
  1050. package/neural-interface/public/shared/ui-link.js +105 -51
  1051. package/neural-interface/public/shared/ui-loading.js +222 -11
  1052. package/neural-interface/public/shared/ui-memory-maintenance.js +111 -0
  1053. package/neural-interface/public/shared/ui-menubar.js +100 -31
  1054. package/neural-interface/public/shared/ui-native-loop-router-state.js +94 -0
  1055. package/neural-interface/public/shared/ui-native-loop-router.js +493 -0
  1056. package/neural-interface/public/shared/ui-native-window-id.js +7 -0
  1057. package/neural-interface/public/shared/ui-navbar.js +357 -222
  1058. package/neural-interface/public/shared/ui-notifications.js +58 -12
  1059. package/neural-interface/public/shared/ui-opencode-panel-v2.js +17 -0
  1060. package/neural-interface/public/shared/ui-panels.js +15 -7
  1061. package/neural-interface/public/shared/ui-resume.js +823 -653
  1062. package/neural-interface/public/shared/ui-schedule-queue.js +172 -0
  1063. package/neural-interface/public/shared/ui-schedules-studio.js +1882 -0
  1064. package/neural-interface/public/shared/ui-scroll-follow.js +194 -0
  1065. package/neural-interface/public/shared/ui-sessions.js +789 -34
  1066. package/neural-interface/public/shared/ui-settings.js +5774 -2194
  1067. package/neural-interface/public/shared/ui-sidepanel-layout.js +109 -0
  1068. package/neural-interface/public/shared/ui-sidepanel-runtime.js +19 -0
  1069. package/neural-interface/public/shared/ui-sidepanel-session-entry.js +40 -0
  1070. package/neural-interface/public/shared/ui-sidepanel-sessions.js +221 -0
  1071. package/neural-interface/public/shared/ui-sidepanel-state.js +131 -0
  1072. package/neural-interface/public/shared/ui-sidepanel-tray.js +547 -0
  1073. package/neural-interface/public/shared/ui-sidepanel-windows.js +387 -0
  1074. package/neural-interface/public/shared/ui-skills.js +18 -32
  1075. package/neural-interface/public/shared/ui-styleguide.js +35 -0
  1076. package/neural-interface/public/shared/ui-sync.js +87 -0
  1077. package/neural-interface/public/shared/ui-terminal.js +7863 -6385
  1078. package/neural-interface/public/shared/ui-tool-updates.js +292 -0
  1079. package/neural-interface/public/shared/ui-tooltip.js +156 -15
  1080. package/neural-interface/public/shared/ui-tutorial-draw.js +119 -683
  1081. package/neural-interface/public/shared/ui-tutorial-layout.js +135 -0
  1082. package/neural-interface/public/shared/ui-tutorial-steps.js +59 -2209
  1083. package/neural-interface/public/shared/ui-tutorial.css +102 -0
  1084. package/neural-interface/public/shared/ui-tutorial.js +557 -1076
  1085. package/neural-interface/public/shared/ui-update.js +420 -68
  1086. package/neural-interface/public/shared/ui-whatsapp.js +941 -0
  1087. package/neural-interface/public/shared/ui-whiteboard.js +1076 -233
  1088. package/neural-interface/public/shared/ui-workspaces.js +2 -1
  1089. package/neural-interface/public/shared/utils.js +58 -0
  1090. package/neural-interface/public/shared/vterm-buffer.js +15 -5
  1091. package/neural-interface/public/shared/whatsapp/wa-phone.js +65 -0
  1092. package/neural-interface/public/shared/whatsapp/wa-view.js +411 -0
  1093. package/neural-interface/public/sidepanel-session.html +18 -0
  1094. package/neural-interface/public/sw.js +10 -2
  1095. package/neural-interface/public/synabun.png +0 -0
  1096. package/neural-interface/public/variant/2d/graph.js +1707 -1635
  1097. package/neural-interface/public/variant/2d/lasso.js +2 -1
  1098. package/neural-interface/public/variant/2d/layout.js +595 -594
  1099. package/neural-interface/public/variant/2d/main.js +557 -521
  1100. package/neural-interface/public/variant/2d/settings-gfx.js +18 -24
  1101. package/neural-interface/public/variant/3d/main.js +517 -500
  1102. package/neural-interface/public/variant/3d/map-controls.js +144 -0
  1103. package/neural-interface/public/variant/3d/map-data.js +251 -0
  1104. package/neural-interface/public/variant/3d/map-labels.js +241 -0
  1105. package/neural-interface/public/variant/3d/map-pick.js +90 -0
  1106. package/neural-interface/public/variant/3d/map-renderer.js +521 -0
  1107. package/neural-interface/public/variant/3d/map-shaders.js +158 -0
  1108. package/neural-interface/public/variant/3d/map-view.js +618 -0
  1109. package/neural-interface/public/vendor/three/manifest.json +3 -0
  1110. package/neural-interface/public/vendor/three/three-0.169.0.min.js +3844 -0
  1111. package/neural-interface/public/vendor/xterm/manifest.json +3 -0
  1112. package/neural-interface/public/vendor/xterm/xterm-6.0.0.css +285 -0
  1113. package/neural-interface/public/vendor/xterm/xterm-6.0.0.min.js +192 -0
  1114. package/neural-interface/scripts/build-vendor.mjs +64 -0
  1115. package/neural-interface/scripts/codex-protocol-fixture.mjs +83 -0
  1116. package/neural-interface/scripts/rebuild-pty.js +39 -22
  1117. package/neural-interface/scripts/vendor-three-entry.js +24 -0
  1118. package/neural-interface/scripts/vendor-xterm-entry.js +8 -0
  1119. package/neural-interface/server.js +33983 -15000
  1120. package/neural-interface/skins/default/skin.css +90 -0
  1121. package/neural-interface/skins/default/skin.json +8 -0
  1122. package/neural-interface/templates/rulesets/README.md +9 -0
  1123. package/neural-interface/templates/rulesets/claude.md +3 -0
  1124. package/neural-interface/templates/rulesets/codex.md +2 -0
  1125. package/neural-interface/templates/rulesets/coexistence.md +6 -0
  1126. package/neural-interface/templates/rulesets/core.md +26 -0
  1127. package/neural-interface/templates/rulesets/cursor.md +2 -0
  1128. package/neural-interface/templates/rulesets/gemini.md +2 -0
  1129. package/neural-interface/templates/rulesets/legacy-hashes.json +227 -0
  1130. package/neural-interface/templates/rulesets/manifest.json +12 -0
  1131. package/neural-interface/templates/rulesets/opencode.md +2 -0
  1132. package/package.json +131 -53
  1133. package/postinstall.js +55 -26
  1134. package/preuninstall.js +187 -0
  1135. package/scripts/restore-data-home-from-backup.mjs +239 -0
  1136. package/setup.js +872 -260
  1137. package/skills/leonardo/SKILL.md +19 -10
  1138. package/skills/leonardo/modules/image-prompter.md +18 -12
  1139. package/skills/leonardo/modules/video-prompter.md +19 -13
  1140. package/skills/synabun/SKILL.md +209 -185
  1141. package/skills/synabun/icon.png +0 -0
  1142. package/skills/synabun/modules/audit.md +380 -374
  1143. package/skills/synabun/modules/changelog.md +15 -6
  1144. package/skills/synabun/modules/idea.md +148 -144
  1145. package/skills/synabun/modules/memorize.md +13 -4
  1146. package/skills/synabun/modules/schedule.md +198 -0
  1147. package/skills/synabun/synabunicon.png +0 -0
  1148. package/updater.cmd +31 -0
  1149. package/updater.mjs +382 -0
  1150. package/updater.sh +29 -0
  1151. package/hooks/claude-code/test-user-learning.mjs +0 -531
  1152. package/neural-interface/public/claude-chat.css +0 -878
  1153. package/neural-interface/public/claude-chat.html +0 -92
  1154. package/neural-interface/public/claude-chat.js +0 -1168
  1155. package/neural-interface/public/index-legacy.html +0 -14677
  1156. package/neural-interface/public/index2d-legacy.html +0 -8193
  1157. package/neural-interface/public/shared/html-term-renderer.js +0 -1022
  1158. package/neural-interface/public/variant/3d/background.js +0 -884
  1159. package/neural-interface/public/variant/3d/camera.js +0 -661
  1160. package/neural-interface/public/variant/3d/gfx.js +0 -116
  1161. package/neural-interface/public/variant/3d/graph.js +0 -2399
  1162. package/neural-interface/public/variant/3d/settings-gfx.js +0 -228
  1163. package/neural-interface/templates/CLAUDE-template.md +0 -211
@@ -0,0 +1,1346 @@
1
+ // ═══════════════════════════════════════════
2
+ // UI-JUDGMENTS — Settings → Judgments (TypeSafe / Jev)
3
+ // ═══════════════════════════════════════════
4
+ //
5
+ // The settings home for every Jev judgment SynaBun makes: the API key and
6
+ // where it comes from, the operational kill switch, one row per judgment
7
+ // surface, cost and cache, the corpus backfill, the bench that gates
8
+ // "apply judged importance", the judgment log, misfiled memories and trash
9
+ // triage. Runtime knobs live server-side in kv_config (PUT /api/typesafe/config)
10
+ // so every process obeys them without a restart; only UI preferences (the
11
+ // log filters, telemetry window) go to storage.
12
+ //
13
+ // Contract with ui-settings.js: buildJudgmentsTab() returns a static shell
14
+ // (never blocks the panel opening); wireJudgmentsTab(overlay, deps) loads
15
+ // live data and binds handlers. The delegated collapsible and eye-toggle
16
+ // handlers in ui-settings.js bind to this markup once, so nothing below ever
17
+ // replaces an .iface-section wrapper or a static input — only inner
18
+ // containers are re-rendered.
19
+
20
+ import { escapeHtml, debounce } from './utils.js';
21
+ import { storage } from './storage.js';
22
+ import * as kit from './settings/settings-kit.js';
23
+
24
+ // Mirrors ui-settings.js (module-local there; duplicated to avoid an import cycle).
25
+ const CHEVRON_ICON = '<svg class="cc-section-chevron" viewBox="0 0 24 24"><polyline points="9 18 15 12 9 6"/></svg>';
26
+ const EYE_CLOSED = '<svg viewBox="0 0 24 24"><path d="M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94"/><path d="M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19"/><line x1="1" y1="1" x2="23" y2="23"/></svg>';
27
+
28
+ const LOG_FILTER_KEY = 'synabun-judgments-log-filter';
29
+ const HOOK_BUDGET_MS = 1400; // judgeText() in the hooks aborts at 1500 ms
30
+ const TRIAGE_PAGE = 50; // rows per page; the whole ranking arrives in one answer
31
+ const TRIAGE_LIMIT = '2000'; // the service ceiling: every candidate found is judged and returned
32
+ // ── Coding sessions, log counters, limits (servers that attribute log rows to sessions) ──
33
+ const WINDOW_KEY = 'synabun-judgments-window'; // telemetry window: h24 | d7 | process
34
+ const SESSIONS_DAYS = 7;
35
+ const TIMELINE_LIMIT = 500;
36
+ const LEGACY_HOOK_SURFACES = ['prompt-urgency', 'agent-message', 'turn-worth', 'loop-goal', 'brief-rank']; // budget warning for servers that predate budgetMs
37
+ // The extra knobs some surfaces have. Their caption and help are settings.redesign.judgments.limit.<key>.{label,help}.
38
+ const LIMIT_FIELDS = [
39
+ { key: 'minScore', min: 0, max: 4, step: 0.1, width: 58 },
40
+ { key: 'maxItems', min: 1, max: 50, step: 1, width: 58 },
41
+ { key: 'debounceMs', min: 0, max: 600000, step: 1000, width: 84 },
42
+ { key: 'bashOnlyMinProbability', min: 0, max: 1, step: 0.05, width: 58 },
43
+ ];
44
+ const RELEVANCE_NAMES = ['irrelevant', 'tangential', 'background', 'direct', 'decisive']; // the rerank / brief-rank rubric, 0–4: settings.redesign.judgments.relevance.<name>
45
+ const CHIP = 'display:inline-block;padding:0 6px;border:1px solid var(--s-medium);border-radius:9px;font-size:10.5px;font-family:monospace;color:var(--t-muted);font-weight:400';
46
+ const PRE = 'margin:0;white-space:pre-wrap;font-size:11px;max-height:160px;overflow:auto';
47
+ const LINK = 'color:var(--accent);text-decoration:underline dotted;text-underline-offset:2px;cursor:pointer';
48
+ /** A server new enough to attribute log rows to sessions and count from the log. Older ones get the per-process view. */
49
+ const serverV2 = (cfg) => Boolean(cfg && (Array.isArray(cfg.origins) || (cfg.logStats && typeof cfg.logStats === 'object')));
50
+ const fin = (v) => v != null && v !== '' && Number.isFinite(Number(v));
51
+ const shortId = (id) => String(id ?? '').slice(0, 8);
52
+ const memLink = (id) => (id == null || id === '') ? '' : `<a href="#" class="jv-entity" data-entity="${attr(id)}" title="${attr(id)}${kit.te('settings.redesign.judgments.showEveryJudgmentAboutThisMemory')}" style="${LINK};font-family:monospace">${escapeHtml(shortId(id))}</a>`;
53
+ const when = (iso) => fin(Date.parse(iso)) ? new Date(iso).toLocaleString([], { month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit' }) : '—';
54
+ const day = (iso) => fin(Date.parse(iso)) ? new Date(iso).toLocaleDateString([], { year: 'numeric', month: 'short', day: 'numeric' }) : '—';
55
+ /** A Noul answer as logged ({type:'noul', noul}), or a bare number. */
56
+ const prob = (a) => { const v = a && typeof a === 'object' ? (a.noul ?? a.probability) : a; return fin(v) ? Number(v) : null; };
57
+
58
+ const attr = (s) => String(s ?? '').replace(/&/g, '&amp;').replace(/"/g, '&quot;').replace(/</g, '&lt;');
59
+ const num = (n) => Number(n || 0).toLocaleString('en-US');
60
+ const money = (n) => (n == null || !Number.isFinite(Number(n))) ? '—' : `$${Number(n) < 0.01 && Number(n) > 0 ? Number(n).toFixed(4) : Number(n).toFixed(2)}`;
61
+ const pct = (n) => (n == null || !Number.isFinite(Number(n))) ? '—' : `${Math.round(Number(n) * 100)}%`;
62
+ const clock = (iso) => iso ? new Date(iso).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', second: '2-digit' }) : '—';
63
+ const stamp = (iso) => iso ? new Date(iso).toLocaleString() : kit.tx('settings.redesign.judgments.never');
64
+ const relevanceName = (score) => { const name = RELEVANCE_NAMES[Math.round(Number(score))]; return name ? kit.tx(`settings.redesign.judgments.relevance.${name}`) : ''; };
65
+ const duration = (ms) => { const s = Math.round(Number(ms || 0) / 1000); if (s < 90) return `${s}s`; const m = Math.round(s / 60); if (m < 120) return `${m} min`; return `${(m / 60).toFixed(1)} h`; };
66
+ const btnStyle = 'margin:0;width:auto;flex:0 0 auto;padding:4px 10px';
67
+ const inputStyle = 'flex:1;font-family:monospace;font-size:12px';
68
+ const smallInput = 'font-family:monospace;font-size:12px;width:72px;padding:3px 6px;background:var(--s-darker);color:var(--t-primary);border:1px solid var(--s-medium);border-radius:6px';
69
+ const boxStyle = 'display:none;margin-top:10px;padding:10px 12px;border-radius:8px;font-size:12px;line-height:1.5';
70
+ const okBg = 'rgba(76,175,80,0.12)', errBg = 'rgba(244,67,54,0.12)', warnBg = 'rgba(255,193,7,0.12)';
71
+
72
+ // ── Markup helpers ──
73
+
74
+
75
+ // Section id → its slug under settings.redesign.section.ai_decisions (title and one-line purpose).
76
+ const SECTION_SLUG = {
77
+ 'jv-sec-connection': 'decision_service', 'jv-sec-master': 'master_switch', 'jv-sec-sessions': 'coding_sessions',
78
+ 'jv-sec-surfaces': 'surfaces', 'jv-sec-telemetry': 'cost_telemetry', 'jv-sec-cache': 'answer_cache',
79
+ 'jv-sec-backfill': 'backfill', 'jv-sec-bench': 'bench', 'jv-sec-browser': 'browser_assistance',
80
+ 'jv-sec-desktop': 'desktop_assistance', 'jv-sec-log': 'judgment_log', 'jv-sec-misfiled': 'misfiled',
81
+ 'jv-sec-supersessions': 'superseded_by_jev', 'jv-sec-triage': 'trash_triage',
82
+ };
83
+ // Everyday sections; the rest carry the "Advanced" tag.
84
+ const EVERYDAY = new Set(['jv-sec-connection', 'jv-sec-master']);
85
+
86
+ /** One section card (settings-kit.js): an accordion; the shell opens the ones the user left open. */
87
+ function section({ id, title, badgeId, body }) {
88
+ const slug = SECTION_SLUG[id];
89
+ return kit.card({
90
+ id,
91
+ section: slug ? `ai_decisions.${slug}` : id,
92
+ title: slug ? undefined : kit.esc(title),
93
+ purpose: slug ? undefined : '',
94
+ status: badgeId ? `<span id="${badgeId}"></span>` : '',
95
+ body,
96
+ advanced: !EVERYDAY.has(id),
97
+ });
98
+ }
99
+ const toggle = (id, cls = '') => `<label class="recall-toggle"><input type="checkbox" id="${id}" class="${cls}"><span class="recall-toggle-track"></span></label>`;
100
+ const button = (id, label, extra = '') => `<button class="conn-add-btn" id="${id}" style="${btnStyle}" ${extra}>${label}</button>`;
101
+ const tile = (id, label) => `<div class="db-stat"><span class="db-stat-label">${label}</span><span class="db-stat-value" id="${id}">—</span></div>`;
102
+
103
+ // ── Shell ──
104
+
105
+ export function buildJudgmentsTab() {
106
+ return `
107
+ <div class="stg-pane" data-stg-pane="judgments">
108
+ <div class="settings-status">
109
+ <span class="settings-status-dot disconnected" id="jv-status-dot"></span>
110
+ <span id="jv-status-line">${kit.te('settings.redesign.judgments.checkingTypesafe')}</span>
111
+ </div>
112
+
113
+ ${section({ id: 'jv-sec-connection', title: kit.tx('settings.redesign.judgments.connection'), badgeId: 'jv-key-source-badge', body: `
114
+ <div class="settings-hint" style="margin-bottom:10px">${kit.te('settings.redesign.judgments.jevTypesafeSystemOneReturnsTyped')} <code>~/.synabun/.env</code> ${kit.te('settings.redesign.judgments.andNeverLeavesTheServerOnly')} ${kit.te('settings.redesign.judgments.learnMoreAtTypesafe')} <a href="https://typesafe.ai" target="_blank" rel="noopener noreferrer">typesafe.ai</a>.</div>
115
+ <div class="settings-field">
116
+ <label for="jv-api-key">${kit.te('settings.redesign.judgments.apiKey')}</label>
117
+ <div class="settings-key-row" style="display:flex;gap:6px">
118
+ <input type="password" id="jv-api-key" value="" placeholder="${kit.te('settings.redesign.judgments.pasteATypesafeApiKey')}" autocomplete="off" spellcheck="false" style="${inputStyle}">
119
+ <button class="conn-add-btn settings-toggle-vis" id="jv-key-eye" data-target="jv-api-key" style="margin:0;width:auto;flex:0 0 auto;padding:4px 8px" data-tooltip="${kit.te('settings.redesign.judgments.name.keyEye')}" aria-label="${kit.te('settings.redesign.judgments.name.keyEye')}">${EYE_CLOSED}</button>
120
+ ${button('jv-key-save', kit.tx('settings.redesign.judgments.save'))}${button('jv-key-test', kit.tx('settings.redesign.judgments.test'))}${button('jv-key-clear', kit.tx('settings.redesign.judgments.clear'))}
121
+ </div>
122
+ <div class="settings-hint" id="jv-key-hint">${kit.te('settings.redesign.judgments.noKeyStored')}</div>
123
+ </div>
124
+ <div id="jv-key-warning" style="${boxStyle};background:${warnBg}"></div>
125
+ <div class="settings-field">
126
+ <label for="jv-base-url">${kit.te('settings.redesign.judgments.baseUrl')}</label>
127
+ <div class="settings-key-row" style="display:flex;gap:6px">
128
+ <input type="text" id="jv-base-url" value="" placeholder="https://api.typesafe.ai" autocomplete="off" spellcheck="false" style="${inputStyle}">
129
+ ${button('jv-base-url-save', kit.tx('settings.redesign.judgments.save'))}
130
+ </div>
131
+ <div class="settings-hint" id="jv-base-url-hint">${kit.te('settings.redesign.judgments.blankDefaultAppliesImmediatelyA')} <code>TYPESAFE_BASE_URL</code> ${kit.te('settings.redesign.judgments.exportInTheServerSShell')}</div>
132
+ </div>
133
+ <div class="settings-field">
134
+ <label for="jv-model">${kit.te('settings.redesign.judgments.model')}</label>
135
+ <div class="settings-key-row" style="display:flex;gap:6px;align-items:center">
136
+ <select id="jv-model" style="${smallInput};width:auto;min-width:160px"></select>
137
+ <span class="settings-hint" style="margin:0">${kit.te('settings.redesign.judgments.aliasesMoveWhenTypesafeShipsA')}</span>
138
+ </div>
139
+ </div>
140
+ <div id="jv-test-result" style="${boxStyle}"></div>
141
+ ` })}
142
+
143
+ ${kit.card({ id: 'jv-sec-master', section: 'ai_decisions.master_switch', body: `
144
+ <div class="recall-control-header" style="margin-top:6px">
145
+ <span class="recall-control-label">${kit.te('settings.redesign.judgments.jevJudgments')} <span id="jv-master-badge" style="font-size:11px;color:var(--t-muted);margin-left:8px"></span></span>
146
+ ${toggle('jv-master-toggle')}
147
+ </div>
148
+ <div class="settings-hint" id="jv-master-hint">${kit.te('settings.redesign.judgments.operationalKillSwitchWhenOffEvery')} <code>/history|previous|old|antes|anterior/i</code>${kit.te('settings.redesign.judgments.rerankKeepsFusionOrderOnlyThe')} <code>SYNABUN_TYPESAFE=off</code> ${kit.te('settings.redesign.judgments.inTheServerSEnvironmentForces')}</div>` })}
149
+
150
+ ${section({ id: 'jv-sec-sessions', title: kit.tx('settings.redesign.judgments.codingSessions'), badgeId: 'jv-sessions-badge', body: `
151
+ <div class="settings-hint" style="margin-bottom:8px">${kit.th('settings.redesign.judgments.whatJevDecidedInsideEachClaude', { SESSIONS_DAYS: SESSIONS_DAYS })} <strong style="color:#ffc107">${kit.te('settings.redesign.judgments.amber')}</strong> ${kit.te('settings.redesign.judgments.marksAnAnswerThatCrossedIts')}</div>
152
+ <div class="settings-key-row" style="display:flex;gap:6px;align-items:center;flex-wrap:wrap">
153
+ ${button('jv-sessions-refresh', kit.tx('settings.redesign.judgments.refresh'))}
154
+ <span class="settings-hint" id="jv-sessions-status" style="margin:0"></span>
155
+ </div>
156
+ <div id="jv-sessions-list" style="display:flex;flex-direction:column;gap:6px;margin-top:8px"></div>
157
+ ` })}
158
+
159
+ ${section({ id: 'jv-sec-surfaces', title: kit.tx('settings.redesign.judgments.surfaces'), badgeId: 'jv-surfaces-badge', body: `
160
+ <div class="settings-hint" id="jv-surfaces-hint" style="margin-bottom:8px">${kit.th('settings.redesign.judgments.oneRowPerJudgmentTurningA', { HOOK_BUDGET_MS: HOOK_BUDGET_MS })}</div>
161
+ <div class="stg-mcp-matrix-scroll">
162
+ <table class="cc-cheatsheet-table" id="jv-surfaces-table" style="width:100%">
163
+ <thead><tr><th>${kit.te('settings.redesign.judgments.on')}</th><th>${kit.te('settings.redesign.judgments.surface')}</th><th>${kit.te('settings.redesign.judgments.timeoutMs')}</th><th title="${kit.te('settings.redesign.judgments.minimumChoiceScoreConfidence')}">${kit.te('settings.redesign.judgments.threshold')}</th><th title="${kit.te('settings.redesign.judgments.minimumNoulProbabilityOnSurfacesThat')}">${kit.te('settings.redesign.judgments.probability')}</th><th id="jv-sf-h-calls">${kit.te('settings.redesign.judgments.calls')}</th><th id="jv-sf-h-avg">${kit.te('settings.redesign.judgments.avgMs')}</th><th id="jv-sf-h-fail">${kit.te('settings.redesign.judgments.fail')}</th><th id="jv-sf-h-skipped">${kit.te('settings.redesign.judgments.skipped')}</th></tr></thead>
164
+ <tbody id="jv-surfaces-body"></tbody>
165
+ </table>
166
+ </div>
167
+ <div class="settings-hint" id="jv-surfaces-status" style="margin-top:6px"></div>
168
+ ` })}
169
+
170
+ ${section({ id: 'jv-sec-telemetry', title: 'Cost & telemetry', badgeId: 'jv-telemetry-badge', body: `
171
+ <div class="settings-key-row" id="jv-st-window-row" style="display:none;gap:6px;align-items:center;flex-wrap:wrap;margin-bottom:8px">
172
+ <span class="settings-hint" style="margin:0">${kit.te('settings.redesign.judgments.figuresFor')}</span>
173
+ <select id="jv-st-window" style="${smallInput};width:auto"><option value="h24">${kit.te('settings.redesign.judgments.theLast24HLog')}</option><option value="d7">${kit.te('settings.redesign.judgments.theLast7DLog')}</option><option value="process">${kit.te('settings.redesign.judgments.thisProcess')}</option></select>
174
+ <span class="settings-hint" id="jv-st-window-hint" style="margin:0"></span>
175
+ </div>
176
+ <div class="db-stats-row" style="display:grid;grid-template-columns:repeat(4,1fr);gap:8px">
177
+ ${tile('jv-st-calls', kit.tx('settings.redesign.judgments.calls'))}${tile('jv-st-hits', kit.tx('settings.redesign.judgments.answered'))}${tile('jv-st-failures', kit.tx('settings.redesign.judgments.failed2'))}${tile('jv-st-cached', kit.tx('settings.redesign.judgments.fromCache'))}
178
+ ${tile('jv-st-in', kit.tx('settings.redesign.judgments.tokensIn'))}${tile('jv-st-out', kit.tx('settings.redesign.judgments.tokensOut'))}${tile('jv-st-avg', kit.tx('settings.redesign.judgments.avgMs'))}${tile('jv-st-cost', kit.tx('settings.redesign.judgments.estSpend'))}
179
+ </div>
180
+ <div class="settings-hint" id="jv-st-lasterror" style="margin-top:8px"></div>
181
+ <div class="settings-key-row" style="display:flex;gap:6px;align-items:center;margin-top:10px;flex-wrap:wrap">
182
+ <span class="settings-hint" style="margin:0">${kit.te('settings.redesign.judgments.pricePerMillionIn')}</span>
183
+ <input type="number" id="jv-rate-in" min="0" step="0.001" style="${smallInput}">
184
+ <span class="settings-hint" style="margin:0">${kit.te('settings.redesign.judgments.out')}</span>
185
+ <input type="number" id="jv-rate-out" min="0" step="0.001" style="${smallInput}">
186
+ ${button('jv-rates-save', kit.tx('settings.redesign.judgments.saveRates'))}
187
+ ${button('jv-metrics-reset', kit.tx('settings.redesign.judgments.resetCounters'))}
188
+ </div>
189
+ <div class="settings-hint" style="margin-top:6px">${kit.te('settings.redesign.judgments.typesafeListsJev1130')}</div>
190
+ ` })}
191
+
192
+ ${kit.card({ id: 'jv-sec-cache', section: 'ai_decisions.answer_cache', body: `
193
+ <div class="settings-key-row" style="display:flex;gap:12px;align-items:center;margin-top:6px">
194
+ <span class="settings-hint" style="margin:0">${kit.te('settings.redesign.judgments.entries')} <strong id="jv-cache-size">—</strong> ${kit.te('settings.redesign.judgments.hitRatio')} <strong id="jv-cache-ratio">—</strong></span>
195
+ ${button('jv-cache-clear', kit.tx('settings.redesign.judgments.clearCache'))}
196
+ </div>
197
+ <div class="settings-hint">${kit.te('settings.redesign.judgments.identicalStateQuestionsModelReuseThe')}</div>` })}
198
+
199
+ ${section({ id: 'jv-sec-backfill', title: kit.tx('settings.redesign.judgments.backfill2'), badgeId: 'jv-backfill-badge', body: `
200
+ <div class="settings-hint" id="jv-coverage" style="margin-bottom:6px">${kit.te('settings.redesign.judgments.coverage')}</div>
201
+ <div class="db-reindex-bar-track"><div id="jv-coverage-bar" class="db-reindex-bar" style="width:0%"></div></div>
202
+ <div class="settings-hint" style="margin:10px 0 8px">${kit.te('settings.redesign.judgments.memoriesIndexedBeforeJudgmentsExistedNev')}</div>
203
+ <div class="settings-key-row" style="display:flex;gap:6px;align-items:center;flex-wrap:wrap">
204
+ <input type="text" id="jv-bf-project" placeholder="${kit.te('settings.redesign.judgments.allProjects')}" autocomplete="off" spellcheck="false" style="${smallInput};width:150px">
205
+ <input type="number" id="jv-bf-limit" placeholder="${kit.te('settings.redesign.judgments.allRows')}" min="1" style="${smallInput};width:110px">
206
+ ${button('jv-bf-estimate', kit.tx('settings.redesign.judgments.estimate'))}${button('jv-bf-start', kit.tx('settings.redesign.judgments.start'), 'disabled')}${button('jv-bf-pause', kit.tx('settings.redesign.judgments.pause'), 'disabled')}${button('jv-bf-resume', kit.tx('settings.redesign.judgments.resume'), 'disabled')}${button('jv-bf-cancel', kit.tx('settings.redesign.judgments.cancel'), 'disabled')}
207
+ </div>
208
+ <div id="jv-bf-estimate-box" style="${boxStyle};background:var(--s-darker)"></div>
209
+ <div style="display:flex;align-items:center;gap:8px;margin-top:12px">
210
+ <div class="wiz-status-dot" id="jv-bf-dot"></div>
211
+ <span id="jv-bf-text" style="font-size:12px">${kit.te('settings.redesign.judgments.idle')}</span>
212
+ </div>
213
+ <div class="db-reindex-bar-track" style="margin-top:6px"><div id="jv-bf-bar" class="db-reindex-bar" style="width:0%"></div></div>
214
+ <div class="settings-hint" id="jv-bf-summary" style="margin-top:6px"></div>
215
+ <div class="settings-hint" id="jv-bf-error" style="color:#f66"></div>
216
+ <div id="jv-bf-report" style="${boxStyle};background:var(--s-darker)"></div>
217
+ ` })}
218
+
219
+ ${section({ id: 'jv-sec-bench', title: kit.tx('settings.redesign.judgments.bench2'), badgeId: 'jv-bench-badge', body: `
220
+ <div class="settings-hint" style="margin-bottom:8px">${kit.te('settings.redesign.judgments.replaysStoredMemoriesThroughBothThe')} <code>benchmarks/typesafe-&lt;surface&gt;-&lt;date&gt;.json</code>${kit.te('settings.redesign.judgments.sameCodeAs')} <code>node mcp-server/scripts/bench-typesafe.mjs</code>.</div>
221
+ <div class="settings-key-row" style="display:flex;gap:6px;align-items:center;flex-wrap:wrap">
222
+ <select id="jv-bench-surface" style="${smallInput};width:auto"><option value="importance">${kit.te('settings.redesign.judgments.importance')}</option><option value="kind">${kit.te('settings.redesign.judgments.kind')}</option><option value="relation">${kit.te('settings.redesign.judgments.relation')}</option><option value="historical">${kit.te('settings.redesign.judgments.historical')}</option><option value="browser">${kit.te('settings.redesign.judgments.browserFixtures')}</option><option value="desktop">${kit.te('settings.redesign.judgments.desktopFixtures')}</option></select>
223
+ <input type="number" id="jv-bench-limit" value="50" min="1" max="200" style="${smallInput};width:80px">
224
+ <input type="text" id="jv-bench-project" placeholder="${kit.te('settings.redesign.judgments.allProjects')}" autocomplete="off" spellcheck="false" style="${smallInput};width:150px">
225
+ ${button('jv-bench-run', kit.tx('settings.redesign.judgments.runBench'))}
226
+ <span id="jv-bench-spinner" style="display:none"><div class="wiz-status-dot spin" style="display:inline-block"></div></span>
227
+ </div>
228
+ <div class="settings-hint" id="jv-bench-last" style="margin-top:6px">${kit.te('settings.redesign.judgments.lastBenchNever')}</div>
229
+ <div id="jv-bench-result" style="${boxStyle};background:var(--s-darker)">
230
+ <div id="jv-bench-summary"></div>
231
+ <div class="stg-mcp-matrix-scroll" style="margin-top:8px"><table class="cc-cheatsheet-table" id="jv-bench-table" style="width:100%"></table></div>
232
+ </div>
233
+ <div class="recall-control-header" style="margin-top:14px">
234
+ <span class="recall-control-label">${kit.te('settings.redesign.judgments.applyJudgedImportanceToMemories')}</span>
235
+ ${toggle('jv-apply-importance')}
236
+ </div>
237
+ <div class="settings-hint" id="jv-apply-hint">${kit.te('settings.redesign.judgments.offByDefaultMaintenanceRecordsJev')} <code>memories.importance</code> ${kit.te('settings.redesign.judgments.untilThisIsOnLockedUntil')}</div>
238
+ ` })}
239
+
240
+ ${section({ id: 'jv-sec-browser', title: kit.tx('settings.redesign.judgments.browserAssistance'), badgeId: 'jv-browser-badge', body: `
241
+ <div class="settings-hint" style="margin-bottom:8px">${kit.te('settings.redesign.judgments.jevAdvisesTheBrowserToolsIt')} <code>browser_navigate</code> ${kit.te('settings.redesign.judgments.itSaysWhetherThePageIs')} <code>intent</code> ${kit.te('settings.redesign.judgments.orAFailedSelectorS')} <code>textHint</code> ${kit.te('settings.redesign.judgments.itRanksTheVisibleControlsAnd')}</div>
242
+ <div id="jv-ba-mode" style="${boxStyle}"></div>
243
+ <div class="settings-hint" id="jv-ba-bench" style="margin-top:6px"></div>
244
+ <div class="settings-hint" id="jv-ba-reasons" style="margin-top:4px;color:#ffc107"></div>
245
+ <div id="jv-ba-counters" style="display:flex;flex-wrap:wrap;gap:6px;margin-top:8px"></div>
246
+ <div class="settings-hint" style="margin-top:4px">${kit.te('settings.redesign.judgments.countersBelongToThisServerProcess')}</div>
247
+ <div class="recall-control-header" style="margin-top:14px">
248
+ <span class="recall-control-label">${kit.te('settings.redesign.judgments.enableSafeAutoHeal')}</span>
249
+ ${toggle('jv-ba-autoheal')}
250
+ </div>
251
+ <div class="settings-hint" id="jv-ba-hint">${kit.te('settings.redesign.judgments.offByDefaultWhenOnA')} <code>browser_click</code> ${kit.te('settings.redesign.judgments.whoseSelectorMatchedNothingMayBe')} <strong>${kit.te('settings.redesign.judgments.once')}</strong>${kit.te('settings.redesign.judgments.onAPlainSameOriginNavigation')} <em>${kit.te('settings.redesign.judgments.browser')}</em> ${kit.te('settings.redesign.judgments.benchHasPassedForExactlyThis')} <code>jev-latest</code>${kit.te('settings.redesign.judgments.these')} <code>browser-target</code> ${kit.te('settings.redesign.judgments.thresholdsThisWordingTheseRiskRules')}</div>
252
+ <details style="margin-top:10px">
253
+ <summary style="cursor:pointer;font-size:12px">${kit.te('settings.redesign.judgments.whatMayBeSentToTypesafe')}</summary>
254
+ <div class="settings-hint" style="margin-top:6px"><strong>${kit.te('settings.redesign.judgments.mayBeSent')}</strong>${kit.te('settings.redesign.judgments.fromAnyPageYouBrowseHere')}</div>
255
+ <div class="settings-hint" style="margin-top:6px"><strong>${kit.te('settings.redesign.judgments.neverSent')}</strong>${kit.te('settings.redesign.judgments.anythingTypedIntoAFieldA')} <code>[input]</code>${kit.te('settings.redesign.judgments.hiddenDomQueryStringsAndFragments')}</div>
256
+ </details>
257
+ <div class="settings-hint" style="margin-top:8px;color:#ffc107">${kit.te('settings.redesign.judgments.alwaysAdvisoryWhateverThisSwitchSays')} <code>browser_batch</code>${kit.te('settings.redesign.judgments.andAnyPageWithAControl')}</div>
258
+ ` })}
259
+
260
+ ${section({ id: 'jv-sec-desktop', title: kit.tx('settings.redesign.judgments.desktopAssistance'), badgeId: 'jv-desktop-badge', body: `
261
+ <div class="settings-hint" style="margin-bottom:8px">${kit.te('settings.redesign.judgments.jevAdvisesComputerUseItNever')} <code>computer_ax</code> <code>intent</code>${kit.te('settings.redesign.judgments.itRanksTheControlsOfThe')} <code>desktop-target</code> ${kit.te('settings.redesign.judgments.surfaceAboveCanBeSwitchedOff')}</div>
262
+ <div id="jv-da-mode" style="${boxStyle}"></div>
263
+ <div class="settings-hint" id="jv-da-bench" style="margin-top:6px"></div>
264
+ <div class="settings-hint" id="jv-da-reasons" style="margin-top:4px;color:#ffc107"></div>
265
+ <div id="jv-da-counters" style="display:flex;flex-wrap:wrap;gap:6px;margin-top:8px"></div>
266
+ <div class="settings-hint" style="margin-top:4px">${kit.te('settings.redesign.judgments.countersBelongToThisServerProcess')}</div>
267
+ <div class="recall-control-header" style="margin-top:14px">
268
+ <span class="recall-control-label">${kit.te('settings.redesign.judgments.enablePressByIntent')}</span>
269
+ ${toggle('jv-da-press')}
270
+ </div>
271
+ <div class="settings-hint" id="jv-da-hint">${kit.te('settings.redesign.judgments.offByDefaultWhenOn')} <code>computer_ax</code> ${kit.te('settings.redesign.judgments.action')} <code>press</code> ${kit.te('settings.redesign.judgments.withAn')} <code>intent</code> ${kit.te('settings.redesign.judgments.andNo')} <code>ref</code> ${kit.te('settings.redesign.judgments.mayPress')} <strong>${kit.te('settings.redesign.judgments.once')}</strong>${kit.te('settings.redesign.judgments.aPlainEnabledButtonLinkTab')} <em>${kit.te('settings.redesign.judgments.desktop')}</em> ${kit.te('settings.redesign.judgments.benchHasPassedForExactlyThis')} <code>jev-latest</code>${kit.te('settings.redesign.judgments.these')} <code>desktop-target</code> ${kit.te('settings.redesign.judgments.thresholdsThisWordingTheSharedAnd')}</div>
272
+ <details style="margin-top:10px">
273
+ <summary style="cursor:pointer;font-size:12px">${kit.te('settings.redesign.judgments.whatMayBeSentToTypesafe')}</summary>
274
+ <div class="settings-hint" style="margin-top:6px"><strong>${kit.te('settings.redesign.judgments.mayBeSent')}</strong>${kit.te('settings.redesign.judgments.fromWhicheverAppIsInFront')}</div>
275
+ <div class="settings-hint" style="margin-top:6px"><strong>${kit.te('settings.redesign.judgments.neverSent')}</strong>${kit.te('settings.redesign.judgments.fieldValuesOrAnythingTypedWindow')}</div>
276
+ </details>
277
+ <div class="settings-hint" style="margin-top:8px;color:#ffc107">${kit.te('settings.redesign.judgments.alwaysAdvisoryWhateverThisSwitchSays2')}</div>
278
+ ` })}
279
+
280
+ ${section({ id: 'jv-sec-log', title: kit.tx('settings.redesign.judgments.judgmentLog'), badgeId: 'jv-log-badge', body: `
281
+ <div class="settings-key-row" style="display:flex;gap:6px;align-items:center;flex-wrap:wrap">
282
+ <select id="jv-log-surface" style="${smallInput};width:auto"><option value="">${kit.te('settings.redesign.judgments.allSurfaces')}</option></select>
283
+ <select id="jv-log-origin" style="${smallInput};width:auto"><option value="">${kit.te('settings.redesign.judgments.allOrigins')}</option><option value="live">${kit.te('settings.redesign.judgments.live')}</option><option value="backfill">${kit.te('settings.redesign.judgments.backfill')}</option><option value="bench">${kit.te('settings.redesign.judgments.bench')}</option></select>
284
+ <input type="text" id="jv-log-session" placeholder="${kit.te('settings.redesign.judgments.sessionId')}" title="${kit.te('settings.redesign.judgments.rowsFromOneClaudeCodeSession')}" autocomplete="off" spellcheck="false" style="${smallInput};width:110px;display:none">
285
+ <input type="text" id="jv-log-entity" placeholder="${kit.te('settings.redesign.judgments.memoryId')}" title="${kit.te('settings.redesign.judgments.rowsAboutOneMemory')}" autocomplete="off" spellcheck="false" style="${smallInput};width:110px">
286
+ <input type="text" id="jv-log-project" placeholder="${kit.te('settings.redesign.judgments.project')}" title="${kit.te('settings.redesign.judgments.rowsFromOneProject')}" autocomplete="off" spellcheck="false" style="${smallInput};width:100px;display:none">
287
+ <select id="jv-log-limit" style="${smallInput};width:auto"><option>25</option><option selected>50</option><option>100</option><option>200</option></select>
288
+ ${button('jv-log-refresh', kit.tx('settings.redesign.judgments.refresh'))}
289
+ <span class="settings-hint" id="jv-log-status" style="margin:0"></span>
290
+ </div>
291
+ <div class="settings-hint" style="margin:6px 0">${kit.te('settings.redesign.judgments.whenSomeoneAsksWhyDidIt')}</div>
292
+ <div id="jv-log-list" style="display:flex;flex-direction:column;gap:4px"></div>
293
+ ` })}
294
+
295
+ ${section({ id: 'jv-sec-misfiled', title: kit.tx('settings.redesign.judgments.misfiled'), badgeId: 'jv-misfiled-badge', body: `
296
+ <div class="settings-hint" style="margin-bottom:8px">${kit.te('settings.redesign.judgments.atEvery')} <code>remember</code>${kit.te('settings.redesign.judgments.jevJudgesWhichCategoryFitsThe')}</div>
297
+ <div id="jv-misfiled-list" style="display:flex;flex-direction:column;gap:6px"></div>
298
+ ` })}
299
+
300
+ ${section({ id: 'jv-sec-supersessions', title: kit.tx('settings.redesign.judgments.supersededByJev'), badgeId: 'jv-supersessions-badge', body: `
301
+ <div class="settings-hint" style="margin-bottom:8px">${kit.te('settings.redesign.judgments.whenJevJudgesANewMemory')} <code>supersedes</code> ${kit.te('settings.redesign.judgments.relationTheOlderMemoryLeavesCurrent')}</div>
302
+ <div class="settings-hint" id="jv-supersessions-status" style="margin:0 0 6px"></div>
303
+ <div id="jv-supersessions-list" style="display:flex;flex-direction:column;gap:6px"></div>
304
+ ` })}
305
+
306
+ ${section({ id: 'jv-sec-triage', title: kit.tx('settings.redesign.judgments.trashTriage'), badgeId: 'jv-triage-badge', body: `
307
+ <div class="settings-hint" style="margin-bottom:8px">${kit.te('settings.redesign.judgments.forgetCandidatesDuplicatesOldLowImportan')}</div>
308
+ <div class="settings-key-row" style="display:flex;gap:6px;align-items:center;flex-wrap:wrap">
309
+ <input type="text" id="jv-triage-project" placeholder="${kit.te('settings.redesign.judgments.allProjects')}" autocomplete="off" spellcheck="false" style="${smallInput};width:150px">
310
+ ${button('jv-triage-run', kit.tx('settings.redesign.judgments.findCandidates'))}
311
+ <span id="jv-triage-spinner" style="display:none"><div class="wiz-status-dot spin" style="display:inline-block"></div></span>
312
+ <span class="settings-hint" id="jv-triage-status" style="margin:0"></span>
313
+ </div>
314
+ <div class="settings-key-row" id="jv-triage-bulk" style="display:none;gap:8px;align-items:center;flex-wrap:wrap;margin-top:8px">
315
+ <label style="display:flex;align-items:center;gap:6px;font-size:12px;cursor:pointer"><input type="checkbox" id="jv-triage-all"> ${kit.te('settings.redesign.judgments.selectAllShown')}</label>
316
+ ${button('jv-triage-trash-selected', kit.tx('settings.redesign.judgments.move0ToTrash'), 'disabled')}
317
+ </div>
318
+ <div id="jv-triage-list" style="display:flex;flex-direction:column;gap:6px;margin-top:8px"></div>
319
+ <div id="jv-triage-more-wrap" style="display:none;margin-top:8px">${button('jv-triage-more', kit.tx('settings.redesign.judgments.showMore'))}</div>
320
+ ` })}
321
+ </div>`;
322
+ }
323
+
324
+ // ── Wiring ──
325
+
326
+ export function wireJudgmentsTab(overlay, { toast = () => {}, initial = null } = {}) {
327
+ const $ = (id) => overlay.querySelector('#' + id);
328
+ // Names for the controls whose only caption is a placeholder or a neighbouring word (screen readers, voice control).
329
+ const CONTROL_NAMES = {
330
+ 'jv-st-window': 'window', 'jv-rate-in': 'rateIn', 'jv-rate-out': 'rateOut', 'jv-bf-project': 'bfProject', 'jv-bf-limit': 'bfLimit',
331
+ 'jv-bench-surface': 'benchSurface', 'jv-bench-limit': 'benchLimit', 'jv-bench-project': 'benchProject', 'jv-log-surface': 'logSurface',
332
+ 'jv-log-origin': 'logOrigin', 'jv-log-session': 'logSession', 'jv-log-entity': 'logEntity', 'jv-log-project': 'logProject',
333
+ 'jv-log-limit': 'logLimit', 'jv-triage-project': 'triageProject',
334
+ 'jv-master-toggle': 'masterToggle', 'jv-apply-importance': 'applyImportance', 'jv-ba-autoheal': 'baAutoheal', 'jv-da-press': 'daPress',
335
+ };
336
+ for (const [id, name] of Object.entries(CONTROL_NAMES)) $(id)?.setAttribute('aria-label', kit.tx(`settings.redesign.judgments.name.${name}`));
337
+ if (!$('jv-status-dot')) return;
338
+ const tabBody = kit.paneOf(overlay, 'judgments');
339
+ const alive = () => document.body.contains(overlay);
340
+ const tabActive = () => alive() && kit.paneActive(overlay, 'judgments');
341
+
342
+ const readJson = async (res) => {
343
+ const text = await res.text();
344
+ let json;
345
+ try { json = JSON.parse(text); } catch { json = { ok: false, error: text.slice(0, 120) || `HTTP ${res.status}` }; }
346
+ if (!res.ok && json.ok !== false) json = { ok: false, error: json.error || `HTTP ${res.status}`, status: res.status };
347
+ if (json.status === undefined) json.status = res.status;
348
+ return json;
349
+ };
350
+ const request = async (method, url, body) => {
351
+ try {
352
+ const res = await fetch(url, { method, headers: body === undefined ? {} : { 'Content-Type': 'application/json' }, body: body === undefined ? undefined : JSON.stringify(body) });
353
+ return await readJson(res);
354
+ } catch (error) { return { ok: false, error: error?.message || kit.tx('settings.redesign.judgments.networkError'), status: 0 }; }
355
+ };
356
+ const jget = (url) => request('GET', url);
357
+ const jput = (url, body) => request('PUT', url, body);
358
+ const jpost = (url, body) => request('POST', url, body ?? {});
359
+
360
+ const state = { cfg: null, backfill: null, openLogIds: new Set(), lastLogSignature: '', timers: {}, armed: {}, triageAbort: null, triageTrashed: new Set(), triageSelected: new Set(), triageLast: null, triagePage: 1 };
361
+ Object.assign(state, {
362
+ sessions: new Map(), sessionsSignature: '', sessionsBooted: false, sessionsUnsupported: false,
363
+ openSessions: new Set(), openTimelineIds: new Set(), timelineHtml: new Map(), timelineKeys: new Map(), timelineLoading: new Set(),
364
+ supersessionsBooted: false, supersessionsUnsupported: false,
365
+ });
366
+ // Shown once the config says the server has them (an older server has neither endpoint).
367
+ $('jv-sec-sessions').style.display = 'none';
368
+ $('jv-sec-supersessions').style.display = 'none';
369
+ { const saved = storage.getItem(WINDOW_KEY); if (['h24', 'd7', 'process'].includes(saved)) $('jv-st-window').value = saved; }
370
+
371
+ // Which sections are open is the shell's (settings-shell.js: applyStoredSections / storeSectionState).
372
+ // Sections that load on demand refresh whenever they get expanded — by a click, a deep link's `expand`, or a restore.
373
+ const onExpand = { 'jv-sec-log': () => refreshLog(true), 'jv-sec-misfiled': () => refreshMisfiled(), 'jv-sec-sessions': () => refreshSessions(true), 'jv-sec-supersessions': () => refreshSupersessions() };
374
+ const expandObserver = new MutationObserver((records) => {
375
+ for (const r of records) {
376
+ const el = r.target;
377
+ if (el.classList.contains('collapsed') || !onExpand[el.id]) continue;
378
+ onExpand[el.id]();
379
+ }
380
+ });
381
+ for (const id of Object.keys(onExpand)) { const el = $(id); if (el) expandObserver.observe(el, { attributes: true, attributeFilter: ['class'] }); }
382
+
383
+ // ── Focus-safe input sync ──
384
+ const setValue = (el, value) => { if (!el || document.activeElement === el) return; const v = value == null ? '' : String(value); if (el.value !== v) el.value = v; };
385
+ const setChecked = (el, on) => { if (!el || document.activeElement === el) return; if (el.checked !== Boolean(on)) el.checked = Boolean(on); };
386
+ const setText = (el, text) => { if (el && el.textContent !== String(text)) el.textContent = String(text); };
387
+ const show = (el, html, bg) => { if (!el) return; el.style.display = html ? 'block' : 'none'; el.innerHTML = html || ''; if (bg) el.style.background = bg; };
388
+
389
+ // ── Renderers ──
390
+ function setHeader(cfg) {
391
+ const dot = $('jv-status-dot'), line = $('jv-status-line');
392
+ if (!cfg || cfg.ok === false) {
393
+ dot.className = 'settings-status-dot disconnected';
394
+ line.textContent = cfg?.status === 404 ? kit.tx('settings.redesign.judgments.typesafeSettingsUnavailableTheServerIs') : kit.tx('settings.redesign.judgments.typesafeSettingsUnavailable', { error: cfg?.error || kit.tx('settings.redesign.judgments.noResponse') });
395
+ setNavStatus('disconnected');
396
+ return;
397
+ }
398
+ const on = cfg.enabled;
399
+ dot.className = `settings-status-dot ${on ? 'connected' : 'disconnected'}`;
400
+ const s = cfg.stats || {};
401
+ const w = statsWindow(cfg);
402
+ line.textContent = on
403
+ ? w.key !== 'process' ? kit.tx('settings.redesign.judgments.connectedCallsFailedInAvgMs', { model: cfg.model, num: num(w.s.calls), num2: num(w.s.failures), label: w.label, num3: num(w.s.avgMs) })
404
+ : kit.tx('settings.redesign.judgments.connectedAnsweredFailedThisProcessAvg', { model: cfg.model, num: num(s.hits), num2: num(s.failures), num3: num(s.avgMs) })
405
+ : cfg.killSwitch ? kit.tx('settings.redesign.judgments.offSynabunTypesafeOffIsSet')
406
+ : !cfg.masterEnabled ? kit.tx('settings.redesign.judgments.offMasterSwitchIsOff')
407
+ : !cfg.hasKey ? kit.tx('settings.redesign.judgments.noApiKeyPasteOneBelow') : kit.tx('settings.redesign.judgments.off');
408
+ setNavStatus(on ? 'connected' : 'disconnected');
409
+ }
410
+ function setNavStatus(status) {
411
+ kit.setNavStatus(overlay, 'judgments', status);
412
+ }
413
+ function renderConnection(cfg) {
414
+ const badge = $('jv-key-source-badge');
415
+ const sourceLabel = cfg.keySource === 'env' ? kit.tx('settings.redesign.judgments.shellEnv') : cfg.keySource === 'dotenv' ? '.env' : 'none';
416
+ setText(badge, cfg.hasKey ? `${cfg.maskedKey} · ${sourceLabel}` : kit.tx('settings.redesign.judgments.noKey'));
417
+ badge.style.color = cfg.shadowed ? '#ffc107' : cfg.hasKey ? 'var(--accent)' : 'var(--t-muted)';
418
+ const input = $('jv-api-key');
419
+ input.dataset.mask = cfg.maskedKey || '';
420
+ input.placeholder = cfg.hasKey ? kit.tx('settings.redesign.judgments.storedPasteToReplace', { maskedKey: cfg.maskedKey }) : kit.tx('settings.redesign.judgments.pasteATypesafeApiKey');
421
+ setText($('jv-key-hint'), cfg.hasKey ? kit.tx('settings.redesign.judgments.keyStored', { key: cfg.maskedKey, source: sourceLabel, note: cfg.keySource === 'env' && !cfg.shadowed ? (" " + kit.tx('settings.redesign.judgments.theEnvCopyMatches')) : '' }) : kit.tx('settings.redesign.judgments.noKeyStoredTypesafeKeysAre'));
422
+ let warning = '';
423
+ if (cfg.shadowed) warning = `<strong>${kit.te('settings.redesign.judgments.aShellExportIsOverridingThe')}</strong> ${kit.te('settings.redesign.judgments.theServerWasLaunchedFromA')} <code>TYPESAFE_API_KEY</code>${kit.te('settings.redesign.judgments.soTheKeyIn')} <code>~/.synabun/.env</code> ${kit.th('settings.redesign.judgments.onDiskIsIgnoredSavingHere', { maskedKey: escapeHtml(String(cfg.maskedKey || '')) })}`;
424
+ else if (cfg.launchShellExport && cfg.keySource === 'env') warning = `${kit.te('settings.redesign.judgments.thisServerWasLaunchedWith')} <code>TYPESAFE_API_KEY</code> ${kit.te('settings.redesign.judgments.exportedInItsShellAndThere')} <code>~/.synabun/.env</code>${kit.te('settings.redesign.judgments.saveTheKeyHereSoA')}`;
425
+ else if (cfg.launchShellExport && cfg.hasKey) warning = `${kit.te('settings.redesign.judgments.noteThisServerWasLaunchedWith')} <code>TYPESAFE_API_KEY</code> ${kit.te('settings.redesign.judgments.exportedInItsShellAfterA')}`;
426
+ show($('jv-key-warning'), warning, warnBg);
427
+ const baseInput = $('jv-base-url');
428
+ setValue(baseInput, cfg.configuredBaseUrl || '');
429
+ baseInput.placeholder = cfg.baseUrlDefault || 'https://api.typesafe.ai';
430
+ setText($('jv-base-url-hint'), cfg.baseUrlSource === 'env'
431
+ ? kit.tx('settings.redesign.judgments.usingFromTheServerSTypesafe', { baseUrl: cfg.baseUrl, baseUrlShadowed: cfg.baseUrlShadowed ? (" " + kit.tx('settings.redesign.judgments.itOverridesTheValueSavedHere')) : '' })
432
+ : kit.tx('settings.redesign.judgments.usingBlankDefaultAppliesImmediately', { baseUrl: cfg.baseUrl, baseUrlSource: cfg.baseUrlSource }));
433
+ const model = $('jv-model');
434
+ if (document.activeElement !== model) {
435
+ const models = Array.isArray(cfg.models) ? cfg.models : ['jev-latest'];
436
+ const want = models.map(m => `<option value="${attr(m)}"${m === cfg.model ? ' selected' : ''}>${escapeHtml(m)}</option>`).join('');
437
+ if (model.innerHTML !== want) model.innerHTML = want;
438
+ model.value = cfg.model;
439
+ }
440
+ }
441
+ function renderMaster(cfg) {
442
+ const t = $('jv-master-toggle');
443
+ setChecked(t, cfg.masterEnabled);
444
+ t.disabled = Boolean(cfg.killSwitch);
445
+ setText($('jv-master-badge'), cfg.killSwitch ? kit.tx('settings.redesign.judgments.forcedOffBySynabunTypesafeOff') : cfg.masterEnabled ? (cfg.hasKey ? kit.tx('settings.redesign.judgments.stateOn') : kit.tx('settings.redesign.judgments.onButNoKey')) : kit.tx('settings.redesign.judgments.offAllSurfacesUseTheirFallbacks'));
446
+ }
447
+ function surfaceRow(name, s, all = {}) {
448
+ const threshold = s.minConfidence != null
449
+ ? `<input type="number" class="jv-sf-threshold" aria-label="${attr(kit.tx('settings.redesign.judgments.name.sfThreshold', { name: s.label || name }))}" min="0" max="1" step="0.05" value="${attr(s.minConfidence)}" style="${smallInput};width:64px">`
450
+ : '<span style="color:var(--t-muted)">—</span>';
451
+ const probability = s.minProbability != null
452
+ ? `<input type="number" class="jv-sf-probability" aria-label="${attr(kit.tx('settings.redesign.judgments.name.sfProbability', { name: s.label || name }))}" min="0" max="1" step="0.05" value="${attr(s.minProbability)}" style="${smallInput};width:64px">`
453
+ : '<span style="color:var(--t-muted)">—</span>';
454
+ // A rider is asked inside its lead's request, so the lead's timeout is the one that applies.
455
+ const lead = s.ridesWith ? String(all[s.ridesWith]?.label || s.ridesWith) : null;
456
+ const timeout = lead
457
+ ? `<span class="jv-sf-lead-timeout settings-hint" style="margin:0" title="${kit.th('settings.redesign.judgments.askedInsideSRequestSoThat', { attr: attr(lead) })}">${kit.te('settings.redesign.judgments.leadS')}</span>`
458
+ : `<input type="number" class="jv-sf-timeout" aria-label="${attr(kit.tx('settings.redesign.judgments.name.sfTimeout', { name: s.label || name }))}" min="200" max="30000" step="100" value="${attr(s.timeoutMs)}" style="${smallInput};width:78px"><div class="jv-sf-warn settings-hint" style="margin:2px 0 0;color:#ffc107;display:none">${kit.te('settings.redesign.judgments.overTheHookBudget')}</div>`;
459
+ // Limits sit under the description: the table already fills the panel, so a column of their own would squeeze every description.
460
+ const limits = LIMIT_FIELDS.filter(f => s[f.key] != null).map(f => `<label title="${kit.te(`settings.redesign.judgments.limit.${f.key}.help`)}" style="display:inline-flex;flex-wrap:wrap;align-items:center;gap:2px 4px;font-size:11px;color:var(--t-muted)">${kit.te(`settings.redesign.judgments.limit.${f.key}.label`)}<input type="number" class="jv-sf-limit" data-key="${f.key}" min="${f.min}" max="${f.max}" step="${f.step}" value="${attr(s[f.key])}" style="${smallInput};width:${f.width}px"></label>`).join('');
461
+ return `<tr id="jv-sf-${attr(name)}" data-surface="${attr(name)}">
462
+ <td><label class="recall-toggle"><input type="checkbox" class="jv-sf-enabled" aria-label="${attr(kit.tx('settings.redesign.judgments.name.sfEnabled', { name: s.label || name }))}"${s.enabled ? ' checked' : ''}><span class="recall-toggle-track"></span></label></td>
463
+ <td><div style="font-weight:600">${escapeHtml(s.label || name)} <span style="font-weight:400;color:var(--t-muted);font-size:11px">${escapeHtml(name)}</span></div><div class="settings-hint" style="margin:2px 0 0;max-width:420px">${escapeHtml(s.description || '')}<br><span style="opacity:.75">${kit.te('settings.redesign.judgments.fallback')} ${escapeHtml(s.fallback || '')}</span></div>${lead ? `<div class="settings-hint jv-sf-rides" style="margin:2px 0 0;color:var(--accent)">${kit.th('settings.redesign.judgments.ridesWithAskedInTheSame', { lead: escapeHtml(lead) })}</div>` : ''}${limits ? `<div class="jv-sf-limits" style="display:flex;flex-wrap:wrap;gap:4px 10px;margin-top:5px">${limits}</div>` : ''}</td>
464
+ <td>${timeout}</td>
465
+ <td>${threshold}</td>
466
+ <td>${probability}</td>
467
+ <td class="jv-sf-calls">0</td><td class="jv-sf-avg">0</td><td class="jv-sf-fail">0</td><td class="jv-sf-skipped" title="${kit.te('settings.redesign.judgments.thisProcessOnlyCallsSkippedBecause')}">0</td>
468
+ </tr>`;
469
+ }
470
+ function renderSurfaces(cfg) {
471
+ const body = $('jv-surfaces-body');
472
+ const names = Object.keys(cfg.surfaces || {});
473
+ // Rebuilt when the surfaces or a row's shape change (a restarted server can add fields); values sync in place below.
474
+ const shape = names.map(n => { const s = cfg.surfaces[n] || {}; return [n, s.ridesWith || '', s.minConfidence != null, s.minProbability != null, ...LIMIT_FIELDS.map(f => s[f.key] != null)].join(':'); }).join('|');
475
+ if (body.dataset.shape !== shape) { body.innerHTML = names.map(n => surfaceRow(n, cfg.surfaces[n], cfg.surfaces)).join(''); body.dataset.shape = shape; }
476
+ // Counters come from the judgment log when the server keeps them there; otherwise from this process.
477
+ const logStats = cfg.logStats && typeof cfg.logStats === 'object' ? cfg.logStats : null;
478
+ // A server that sends budgetMs names each hook's budget; an older one gets the list this page always used.
479
+ const budgets = names.some(n => fin(cfg.surfaces[n]?.budgetMs));
480
+ let on = 0;
481
+ for (const name of names) {
482
+ const s = cfg.surfaces[name];
483
+ const row = body.querySelector(`tr[data-surface="${name}"]`);
484
+ if (!row) continue;
485
+ if (s.enabled) on++;
486
+ setChecked(row.querySelector('.jv-sf-enabled'), s.enabled);
487
+ setValue(row.querySelector('.jv-sf-timeout'), s.timeoutMs);
488
+ setValue(row.querySelector('.jv-sf-threshold'), s.minConfidence);
489
+ setValue(row.querySelector('.jv-sf-probability'), s.minProbability);
490
+ for (const input of row.querySelectorAll('.jv-sf-limit')) setValue(input, s[input.dataset.key]);
491
+ const lead = s.ridesWith ? cfg.surfaces[s.ridesWith] : null;
492
+ setText(row.querySelector('.jv-sf-lead-timeout'), lead && fin(lead.timeoutMs) ? `lead's · ${num(lead.timeoutMs)}` : "lead's");
493
+ const m = s.metrics || {};
494
+ const calls = row.querySelector('.jv-sf-calls'), avg = row.querySelector('.jv-sf-avg'), fail = row.querySelector('.jv-sf-fail');
495
+ if (logStats) {
496
+ const h = logStats.surfaces?.[name]?.h24 || {}, d = logStats.surfaces?.[name]?.d7 || {};
497
+ setText(calls, `${num(h.calls)} / ${num(d.calls)}`);
498
+ setText(avg, num(Math.round(Number(Number(h.calls) ? h.avgMs : d.avgMs) || 0)));
499
+ avg.title = Number(h.calls) ? kit.tx('settings.redesign.judgments.averageOverTheLast24H') : kit.tx('settings.redesign.judgments.averageOverTheLast7D');
500
+ setText(fail, num(h.failures));
501
+ } else {
502
+ setText(calls, num(m.calls));
503
+ setText(avg, num(m.avgMs));
504
+ avg.title = '';
505
+ setText(fail, num(m.failures));
506
+ }
507
+ setText(row.querySelector('.jv-sf-skipped'), num(m.skipped));
508
+ const warn = row.querySelector('.jv-sf-warn');
509
+ if (warn) {
510
+ const budget = budgets ? (fin(s.budgetMs) ? Number(s.budgetMs) : null) : LEGACY_HOOK_SURFACES.includes(name) ? HOOK_BUDGET_MS : null;
511
+ const over = budget != null && Number(s.timeoutMs) > budget;
512
+ warn.style.display = over ? 'block' : 'none';
513
+ if (over) setText(warn, kit.tx('settings.redesign.judgments.overTheMsHookBudget', { num: num(budget) }));
514
+ }
515
+ row.style.opacity = s.enabled ? '1' : '0.6';
516
+ }
517
+ // Non-breaking spaces: the header may wrap after its first word, never inside "24 h".
518
+ setText($('jv-sf-h-calls'), logStats ? kit.tx('settings.redesign.judgments.callsWindows') : kit.tx('settings.redesign.judgments.calls'));
519
+ setText($('jv-sf-h-fail'), logStats ? kit.tx('settings.redesign.judgments.failWindow') : kit.tx('settings.redesign.judgments.fail'));
520
+ $('jv-sf-h-skipped').title = logStats ? kit.tx('settings.redesign.judgments.thisProcessOnlyASurfaceThat') : '';
521
+ const hint = $('jv-surfaces-hint');
522
+ const oldest = logStats ? Date.parse(logStats.oldest) : NaN;
523
+ const text = logStats
524
+ ? kit.tx('settings.redesign.judgments.oneRowPerJudgmentTurningA2', { fin: fin(oldest) && Date.now() - oldest < SESSIONS_DAYS * 864e5 ? (" " + kit.tx('settings.redesign.judgments.theLogReachesBackOnlyTo', { stamp: stamp(logStats.oldest) })) : '' })
525
+ : kit.tx('settings.redesign.judgments.oneRowPerJudgmentTurningA', { HOOK_BUDGET_MS: HOOK_BUDGET_MS });
526
+ setText(hint, text);
527
+ setText($('jv-surfaces-badge'), kit.tx('settings.redesign.judgments.surfacesOn', { on, total: names.length }));
528
+ }
529
+ function spend(stats, rateIn, rateOut) {
530
+ const cost = Number(stats.inputTokens || 0) / 1e6 * (rateIn || 0) + Number(stats.outputTokens || 0) / 1e6 * (rateOut || 0);
531
+ return money(cost);
532
+ }
533
+ /** The figures the header and the tiles show: the log's 24 h or 7 d totals when the server keeps them, else this process's counters. */
534
+ function statsWindow(cfg) {
535
+ const logStats = cfg?.logStats && typeof cfg.logStats === 'object' ? cfg.logStats : null;
536
+ const wanted = $('jv-st-window').value;
537
+ if (!logStats || wanted === 'process') return { key: 'process', label: kit.tx('settings.redesign.judgments.thisProcess'), s: cfg?.stats || {} };
538
+ const key = wanted === 'd7' ? 'd7' : 'h24';
539
+ const t = logStats.totals?.[key] || {};
540
+ const calls = Number(t.calls || 0), failures = Number(t.failures || 0);
541
+ // The log's totals carry calls, failures, latency and input tokens; anything else shows as a dash.
542
+ return { key, label: key === 'd7' ? kit.tx('settings.redesign.judgments.theLast7D') : kit.tx('settings.redesign.judgments.theLast24H'), s: { calls, failures, hits: fin(t.hits) ? Number(t.hits) : Math.max(0, calls - failures), cached: fin(t.cached) ? Number(t.cached) : null, inputTokens: Number(t.inputTokens || 0), outputTokens: fin(t.outputTokens) ? Number(t.outputTokens) : null, avgMs: fin(t.avgMs) ? Math.round(Number(t.avgMs)) : 0 } };
543
+ }
544
+ function renderStatsWindow(cfg, w) {
545
+ const logStats = cfg.logStats && typeof cfg.logStats === 'object' ? cfg.logStats : null;
546
+ $('jv-st-window-row').style.display = logStats ? 'flex' : 'none';
547
+ $('jv-metrics-reset').title = logStats ? kit.tx('settings.redesign.judgments.resetsThisProcessSCountersThe') : '';
548
+ if (!logStats) return;
549
+ const hint = $('jv-st-window-hint');
550
+ const oldest = Date.parse(logStats.oldest);
551
+ const partial = fin(oldest) && Date.now() - oldest < SESSIONS_DAYS * 864e5;
552
+ setText(hint, !fin(oldest) ? kit.tx('settings.redesign.judgments.theJudgmentLogIsEmpty')
553
+ : partial ? kit.tx('settings.redesign.judgments.theLogReachesBackOnlyTo2', { stamp: stamp(logStats.oldest), duration: duration(Date.now() - oldest) })
554
+ : kit.tx('settings.redesign.judgments.theLogReachesBackTo', { stamp: stamp(logStats.oldest) }));
555
+ hint.style.color = partial && w.key === 'd7' ? '#ffc107' : '';
556
+ }
557
+ function renderTelemetry(cfg) {
558
+ const s = cfg.stats || {};
559
+ const w = statsWindow(cfg), ws = w.s;
560
+ const dash = (n) => (n == null ? '—' : num(n));
561
+ setText($('jv-st-calls'), num(ws.calls)); setText($('jv-st-hits'), num(ws.hits)); setText($('jv-st-failures'), num(ws.failures)); setText($('jv-st-cached'), dash(ws.cached));
562
+ setText($('jv-st-in'), num(ws.inputTokens)); setText($('jv-st-out'), dash(ws.outputTokens)); setText($('jv-st-avg'), num(ws.avgMs));
563
+ renderStatsWindow(cfg, w);
564
+ const rateIn = Number($('jv-rate-in').value || cfg.costPerMillionInput || 0), rateOut = Number($('jv-rate-out').value || cfg.costPerMillionOutput || 0);
565
+ setText($('jv-st-cost'), (rateIn || rateOut) ? spend(ws, rateIn, rateOut) : kit.tx('settings.redesign.judgments.setRates'));
566
+ setValue($('jv-rate-in'), cfg.costPerMillionInput); setValue($('jv-rate-out'), cfg.costPerMillionOutput);
567
+ const err = $('jv-st-lasterror');
568
+ err.textContent = s.lastError ? kit.tx('settings.redesign.judgments.lastError', { lastError: s.lastError, lastErrorAt: s.lastErrorAt ? (" " + kit.tx('settings.redesign.judgments.at', { clock: clock(s.lastErrorAt) })) : '', retryAfterMs: s.retryAfterMs > 0 ? (" " + kit.tx('settings.redesign.judgments.rateLimitedRetryInS', { retryAfterMs: Math.ceil(s.retryAfterMs / 1000) })) : '' }) : kit.tx('settings.redesign.judgments.noErrorsThisProcess');
569
+ err.style.color = s.lastError ? '#f66' : '';
570
+ setText($('jv-telemetry-badge'), kit.tx('settings.redesign.judgments.telemetryBadge', { calls: num(ws.calls), tokens: num(ws.inputTokens), window: w.key === 'd7' ? ' · 7 d' : w.key === 'h24' ? ' · 24 h' : '' }));
571
+ const size = Number(s.cacheSize || 0), cached = Number(s.cached || 0), calls = Number(s.calls || 0);
572
+ setText($('jv-cache-size'), num(size));
573
+ setText($('jv-cache-ratio'), (cached + calls) ? pct(cached / (cached + calls)) : '—');
574
+ }
575
+ function renderCoverage(cfg) {
576
+ const c = cfg.coverage || { judged: 0, total: 0 };
577
+ const ratio = c.total ? c.judged / c.total : 0;
578
+ setText($('jv-coverage'), kit.tx('settings.redesign.judgments.coverageLiveMemoriesJudged', { num: num(c.judged), num2: num(c.total), pct: pct(ratio) }));
579
+ $('jv-coverage-bar').style.width = `${Math.round(ratio * 100)}%`;
580
+ }
581
+ function renderBench(cfg) {
582
+ const last = cfg.benchRunAt;
583
+ const summaries = cfg.bench || {};
584
+ const parts = Object.entries(summaries).map(([s, b]) => `${s} ${pct(b.agreement)} (n=${b.n})`);
585
+ setText($('jv-bench-last'), last ? kit.tx('settings.redesign.judgments.lastImportanceBench', { stamp: stamp(last), count: parts.length ? ` · ${parts.join(' · ')}` : '' }) : kit.tx('settings.redesign.judgments.lastBenchNeverRunOneTo'));
586
+ setText($('jv-bench-badge'), last ? kit.tx('settings.redesign.judgments.benchBadge', { date: new Date(last).toLocaleDateString() }) : kit.tx('settings.redesign.judgments.notRun'));
587
+ const apply = $('jv-apply-importance');
588
+ apply.disabled = !last;
589
+ setChecked(apply, cfg.applyJudgedImportance);
590
+ }
591
+ // ── Browser assistance ──
592
+ const BA_COUNTERS = [['pageAssessments', kit.tx('settings.redesign.judgments.pageAssessments')], ['recommendations', kit.tx('settings.redesign.judgments.recommendations')], ['shadowAutoHealEligible', kit.tx('settings.redesign.judgments.wouldHaveHealedShadow')], ['autoHealAttempted', kit.tx('settings.redesign.judgments.autoHealAttempted')],
593
+ ['autoHealSucceeded', kit.tx('settings.redesign.judgments.autoHealSucceeded')], ['blockedBySafety', kit.tx('settings.redesign.judgments.blockedBySafety')], ['skippedRateLimit', kit.tx('settings.redesign.judgments.skippedRateLimit')], ['fallbackUnavailable', kit.tx('settings.redesign.judgments.fallbackUnavailable')]];
594
+ function renderBrowserAssist(cfg) {
595
+ const ba = cfg.browserAssist;
596
+ const toggleEl = $('jv-ba-autoheal');
597
+ if (!toggleEl) return;
598
+ // This file is served from disk, so it can be newer than the running server: say so rather than render an empty section.
599
+ if (!ba) {
600
+ setText($('jv-browser-badge'), kit.tx('settings.redesign.judgments.restartTheNeuralInterfaceAfterNpm'));
601
+ show($('jv-ba-mode'), kit.tx('settings.redesign.judgments.thisServerWasStartedBeforeBrowser'), warnBg);
602
+ toggleEl.disabled = true;
603
+ return;
604
+ }
605
+ const on = ba.mode === 'auto-heal';
606
+ setText($('jv-browser-badge'), on ? kit.tx('settings.redesign.judgments.autoHealOn') : ba.autoHealEnabled ? kit.tx('settings.redesign.judgments.shadowAutoHealLocked') : kit.tx('settings.redesign.judgments.stateShadow'));
607
+ show($('jv-ba-mode'), `<strong>${on ? kit.te('settings.redesign.judgments.autoHeal') : kit.te('settings.redesign.judgments.shadow')}</strong> — ${on
608
+ ? kit.te('settings.redesign.judgments.adviceOnEverySurfaceAndOne')
609
+ : kit.te('settings.redesign.judgments.adviceOnlyHintsAreReorderedAnd')}<br><span style="font-family:monospace;font-size:11px;opacity:.85">${kit.te('settings.redesign.judgments.basis')} ${escapeHtml(ba.basis || kit.tx('settings.redesign.judgments.unavailable'))}</span>`, on ? okBg : 'var(--s-darker)');
610
+ const b = ba.targetBench;
611
+ setText($('jv-ba-bench'), b
612
+ ? kit.tx('settings.redesign.judgments.lastBrowserBenchmark', { stamp: stamp(b.at), model: b.model, passed: b.passed ? kit.tx('settings.redesign.judgments.passedEveryGate') : kit.tx('settings.redesign.judgments.didNotPass'), reportFile: b.reportFile ? ` · ${String(b.reportFile).split('/').slice(-2).join('/')}` : '' })
613
+ : kit.tx('settings.redesign.judgments.lastBrowserBenchmarkNeverPickBrowser'));
614
+ setText($('jv-ba-reasons'), ba.eligible ? '' : (ba.reasons || []).join(' '));
615
+ $('jv-ba-counters').innerHTML = BA_COUNTERS.map(([key, label]) => `<div class="db-stat"><span class="db-stat-label">${label}</span><span class="db-stat-value">${num((ba.counters || {})[key])}</span></div>`).join('');
616
+ // Turning it off is always possible; turning it on needs a passing benchmark of this exact configuration.
617
+ toggleEl.disabled = !ba.eligible && !ba.autoHealEnabled;
618
+ setChecked(toggleEl, ba.autoHealEnabled);
619
+ }
620
+ // ── Desktop assistance ──
621
+ const DA_COUNTERS = [['intentSnapshots', kit.tx('settings.redesign.judgments.intentSnapshots')], ['recommendations', kit.tx('settings.redesign.judgments.recommendations')], ['pressEligibleAdvisory', kit.tx('settings.redesign.judgments.wouldHavePressedLocked')], ['pressAttempted', kit.tx('settings.redesign.judgments.pressesAttempted')],
622
+ ['pressSucceeded', kit.tx('settings.redesign.judgments.pressesSucceeded')], ['blockedBySafety', kit.tx('settings.redesign.judgments.blockedBySafety')], ['skippedRateLimit', kit.tx('settings.redesign.judgments.skippedRateLimit')], ['fallbackUnavailable', kit.tx('settings.redesign.judgments.fallbackUnavailable')]];
623
+ function renderDesktopAssist(cfg) {
624
+ const da = cfg.desktopAssist;
625
+ const toggleEl = $('jv-da-press');
626
+ if (!toggleEl) return;
627
+ // Served from disk, so possibly newer than the running server: say so rather than render an empty section.
628
+ if (!da) {
629
+ setText($('jv-desktop-badge'), kit.tx('settings.redesign.judgments.restartAfterNpmRunMcpBuild'));
630
+ show($('jv-da-mode'), '' + kit.te('settings.redesign.judgments.thisServerWasStartedBeforeDesktop') + ' <code>npm run mcp:build</code> ' + kit.te('settings.redesign.judgments.toLoadItUntilThenComputer') + '', warnBg);
631
+ setText($('jv-da-bench'), ''); setText($('jv-da-reasons'), ''); $('jv-da-counters').innerHTML = '';
632
+ toggleEl.disabled = true;
633
+ return;
634
+ }
635
+ const on = da.mode === 'press';
636
+ setText($('jv-desktop-badge'), on ? kit.tx('settings.redesign.judgments.pressByIntentOn') : da.pressEnabled ? kit.tx('settings.redesign.judgments.advisoryPressLocked') : 'advisory');
637
+ show($('jv-da-mode'), `<strong>${on ? kit.te('settings.redesign.judgments.pressByIntent') : kit.te('settings.redesign.judgments.advisory')}</strong> — ${on
638
+ ? kit.te('settings.redesign.judgments.aRankedListAndAdviceOn')
639
+ : kit.te('settings.redesign.judgments.aRankedListAndAdviceOn2')}<br><span style="font-family:monospace;font-size:11px;opacity:.85">${kit.te('settings.redesign.judgments.basis')} ${escapeHtml(da.basis || kit.tx('settings.redesign.judgments.unavailable'))}</span>`, on ? okBg : 'var(--s-darker)');
640
+ const b = da.targetBench;
641
+ setText($('jv-da-bench'), b
642
+ ? kit.tx('settings.redesign.judgments.lastDesktopBenchmark', { stamp: stamp(b.at), model: b.model, passed: b.passed ? kit.tx('settings.redesign.judgments.passedEveryGate') : kit.tx('settings.redesign.judgments.didNotPass'), reportFile: b.reportFile ? ` · ${String(b.reportFile).split('/').slice(-2).join('/')}` : '' })
643
+ : kit.tx('settings.redesign.judgments.lastDesktopBenchmarkNeverPickDesktop'));
644
+ setText($('jv-da-reasons'), da.eligible ? '' : (da.reasons || []).join(' '));
645
+ $('jv-da-counters').innerHTML = DA_COUNTERS.map(([key, label]) => `<div class="db-stat"><span class="db-stat-label">${label}</span><span class="db-stat-value">${num((da.counters || {})[key])}</span></div>`).join('');
646
+ // Off is always reachable; on needs a passing desktop benchmark of this exact configuration.
647
+ toggleEl.disabled = !da.eligible && !da.pressEnabled;
648
+ setChecked(toggleEl, da.pressEnabled);
649
+ }
650
+ function applyConfig(cfg) {
651
+ if (!cfg || cfg.ok === false) { setHeader(cfg); return; }
652
+ state.cfg = cfg;
653
+ setHeader(cfg); renderConnection(cfg); renderMaster(cfg); renderSurfaces(cfg); renderTelemetry(cfg); renderCoverage(cfg); renderBench(cfg); renderBrowserAssist(cfg);
654
+ renderDesktopAssist(cfg);
655
+ renderSessionsMeta(cfg); renderSupersessionsMeta(cfg); syncLogFilters(cfg);
656
+ if (cfg.backfill) applyBackfill(cfg.backfill);
657
+ const logSurface = $('jv-log-surface');
658
+ if (logSurface.options.length <= 1) {
659
+ for (const name of Object.keys(cfg.surfaces || {})) { const o = document.createElement('option'); o.value = name; o.textContent = name; logSurface.appendChild(o); }
660
+ const o = document.createElement('option'); o.value = 'connection-test'; o.textContent = kit.tx('settings.redesign.judgments.connectionTest'); logSurface.appendChild(o);
661
+ // Filters saved before session / memory / project existed parse the same; the new fields just stay blank.
662
+ try { const f = JSON.parse(storage.getItem(LOG_FILTER_KEY) || 'null'); if (f) { logSurface.value = f.surface || ''; $('jv-log-origin').value = f.origin || ''; $('jv-log-limit').value = f.limit || '50'; for (const key of ['session', 'entity', 'project']) if (typeof f[key] === 'string') $(`jv-log-${key}`).value = f[key]; } } catch { /* defaults */ }
663
+ if ($('jv-log-origin').selectedIndex < 0) $('jv-log-origin').value = ''; // an origin this server does not list
664
+ }
665
+ }
666
+ async function loadConfig() { applyConfig(await jget('/api/typesafe/config')); }
667
+ async function putConfig(patch, okMessage) {
668
+ const res = await jput('/api/typesafe/config', patch);
669
+ if (res.ok === false) { toast(kit.tx('settings.redesign.judgments.errJudgments', { error: res.error || kit.tx('settings.redesign.judgments.saveFailed') })); await loadConfig(); return null; }
670
+ applyConfig(res);
671
+ if (okMessage) toast(okMessage);
672
+ return res;
673
+ }
674
+
675
+ // ── Connection handlers ──
676
+ $('jv-key-save').addEventListener('click', async () => {
677
+ const value = $('jv-api-key').value.trim();
678
+ if (!value) { toast(kit.tx('settings.redesign.judgments.pasteAKeyFirst')); return; }
679
+ const res = await putConfig({ apiKey: value }, kit.tx('settings.redesign.judgments.typesafeKeySaved'));
680
+ if (res) { $('jv-api-key').value = ''; }
681
+ });
682
+ $('jv-key-clear').addEventListener('click', async () => {
683
+ const btn = $('jv-key-clear');
684
+ if (!state.armed.clear) { state.armed.clear = true; btn.textContent = kit.tx('settings.redesign.judgments.confirmClear'); setTimeout(() => { state.armed.clear = false; btn.textContent = kit.tx('settings.redesign.judgments.clear'); }, 5000); return; }
685
+ state.armed.clear = false; btn.textContent = kit.tx('settings.redesign.judgments.clear');
686
+ await putConfig({ apiKey: '' }, kit.tx('settings.redesign.judgments.typesafeKeyRemoved'));
687
+ show($('jv-test-result'), '');
688
+ });
689
+ $('jv-key-test').addEventListener('click', async () => {
690
+ const pending = $('jv-api-key').value.trim();
691
+ if (pending) { const saved = await putConfig({ apiKey: pending }); if (!saved) return; $('jv-api-key').value = ''; }
692
+ const box = $('jv-test-result');
693
+ show(box, kit.tx('settings.redesign.judgments.testing'), 'var(--s-darker)');
694
+ const res = await jpost('/api/typesafe/test');
695
+ if (res.ok) {
696
+ show(box, `<strong>${kit.te('settings.redesign.judgments.ok')}</strong> ${kit.th('settings.redesign.judgments.msInOutAt', { num: num(res.latencyMs), num2: num(res.inputTokens), num3: num(res.outputTokens), model: escapeHtml(res.model || '') })} ${escapeHtml(res.baseUrl || '')}<pre style="margin:6px 0 0;white-space:pre-wrap;font-size:11px">${escapeHtml(JSON.stringify(res.answer, null, 2))}</pre>`, okBg);
697
+ } else show(box, `<strong>${kit.te('settings.redesign.judgments.notWorking')}</strong> · ${escapeHtml(res.reason || res.error || kit.tx('settings.redesign.judgments.unknown'))}${res.latencyMs ? (" " + kit.th('settings.redesign.judgments.ms', { num: num(res.latencyMs) })) : ''}`, errBg);
698
+ loadConfig();
699
+ });
700
+ $('jv-base-url-save').addEventListener('click', () => putConfig({ baseUrl: $('jv-base-url').value.trim() }, kit.tx('settings.redesign.judgments.baseUrlSaved')));
701
+ $('jv-model').addEventListener('change', () => putConfig({ model: $('jv-model').value }, kit.tx('settings.redesign.judgments.modelSetTo', { value: $('jv-model').value })));
702
+
703
+ // ── Master switch ──
704
+ $('jv-master-toggle').addEventListener('change', () => putConfig({ enabled: $('jv-master-toggle').checked }, $('jv-master-toggle').checked ? kit.tx('settings.redesign.judgments.judgmentsOn') : kit.tx('settings.redesign.judgments.judgmentsOffEverySurfaceNowUses')));
705
+
706
+ // ── Surfaces ──
707
+ const surfaceDebounce = new Map();
708
+ const readRow = (row) => {
709
+ const patch = { enabled: row.querySelector('.jv-sf-enabled').checked };
710
+ // A rider has no timeout of its own to send: it rides on its lead's.
711
+ const to = row.querySelector('.jv-sf-timeout');
712
+ if (to) patch.timeoutMs = Number(to.value);
713
+ const th = row.querySelector('.jv-sf-threshold');
714
+ if (th) patch.minConfidence = Number(th.value);
715
+ const pr = row.querySelector('.jv-sf-probability');
716
+ if (pr) patch.minProbability = Number(pr.value);
717
+ // Limits: only the fields this surface carries; a half-typed (empty) value is left for the server to keep.
718
+ for (const input of row.querySelectorAll('.jv-sf-limit')) if (fin(input.value)) patch[input.dataset.key] = Number(input.value);
719
+ return patch;
720
+ };
721
+ async function putSurface(row) {
722
+ const name = row.dataset.surface;
723
+ const res = await putConfig({ surfaces: { [name]: readRow(row) } });
724
+ setText($('jv-surfaces-status'), res ? kit.tx('settings.redesign.judgments.savedAt', { name, time: clock(new Date().toISOString()) }) : kit.tx('settings.redesign.judgments.couldNotSave', { name: name }));
725
+ }
726
+ $('jv-surfaces-body').addEventListener('change', (e) => {
727
+ const row = e.target.closest('tr[data-surface]');
728
+ if (!row) return;
729
+ surfaceDebounce.get(row.dataset.surface)?.cancel?.();
730
+ putSurface(row);
731
+ });
732
+ $('jv-surfaces-body').addEventListener('input', (e) => {
733
+ const row = e.target.closest('tr[data-surface]');
734
+ if (!row || !e.target.matches('.jv-sf-timeout, .jv-sf-threshold, .jv-sf-probability, .jv-sf-limit')) return;
735
+ let fn = surfaceDebounce.get(row.dataset.surface);
736
+ if (!fn) { fn = debounce(() => putSurface(row), 600); surfaceDebounce.set(row.dataset.surface, fn); }
737
+ fn();
738
+ });
739
+
740
+ // ── Telemetry / cache ──
741
+ const liveSpend = () => { if (state.cfg) setText($('jv-st-cost'), spend(statsWindow(state.cfg).s, Number($('jv-rate-in').value || 0), Number($('jv-rate-out').value || 0))); };
742
+ $('jv-st-window').addEventListener('change', () => { storage.setItem(WINDOW_KEY, $('jv-st-window').value); if (state.cfg) { setHeader(state.cfg); renderTelemetry(state.cfg); } });
743
+ $('jv-rate-in').addEventListener('input', liveSpend); $('jv-rate-out').addEventListener('input', liveSpend);
744
+ $('jv-rates-save').addEventListener('click', () => putConfig({ costPerMillionInput: Number($('jv-rate-in').value || 0), costPerMillionOutput: Number($('jv-rate-out').value || 0) }, kit.tx('settings.redesign.judgments.ratesSaved')));
745
+ $('jv-metrics-reset').addEventListener('click', async () => { const r = await jpost('/api/typesafe/metrics/reset'); if (r.ok) { toast(kit.tx('settings.redesign.judgments.countersReset')); loadConfig(); } else toast(kit.tx('settings.redesign.judgments.errJudgments', { error: r.error })); });
746
+ $('jv-cache-clear').addEventListener('click', async () => { const r = await jpost('/api/typesafe/cache/clear'); if (r.ok) { toast(kit.tx('settings.redesign.judgments.answerCacheCleared')); loadConfig(); } else toast(kit.tx('settings.redesign.judgments.errJudgments', { error: r.error })); });
747
+
748
+ // ── Backfill ──
749
+ function applyBackfill(b) {
750
+ if (!b) return;
751
+ state.backfill = b;
752
+ const status = b.status || 'idle';
753
+ setText($('jv-backfill-badge'), status);
754
+ const est = b.estimate;
755
+ const inputs = { project: $('jv-bf-project').value.trim() || null, limit: $('jv-bf-limit').value ? Number($('jv-bf-limit').value) : null };
756
+ const estMatches = est && (est.rows != null) && state.estimateFor && state.estimateFor.project === inputs.project && state.estimateFor.limit === inputs.limit;
757
+ if (est) show($('jv-bf-estimate-box'), `<strong>${kit.te('settings.redesign.judgments.estimate')}</strong> ${kit.th('settings.redesign.judgments.memoriesImportanceKindRequestsRelationRe', { basis: escapeHtml(est.basis), stamp: stamp(est.at), num: num(est.rows), num2: num(est.calls.importanceKind), num3: num(est.calls.relations), num4: num(est.tokens.input), num5: num(est.tokens.output) })} <strong>${money(est.costUsd)}</strong> ${kit.th('settings.redesign.judgments.atMInAboutAtConcurrent', { input: est.rates.input, duration: duration(est.wallClockMs), concurrency: est.concurrency })}${est.sample ? (" " + kit.th('settings.redesign.judgments.neighbourSampleHadCandidates', { withCandidates: est.sample.withCandidates, size: est.sample.size })) : ''}`, 'var(--s-darker)');
758
+ const running = status === 'running', paused = status === 'paused';
759
+ $('jv-bf-estimate').disabled = running;
760
+ $('jv-bf-start').disabled = running || paused || !estMatches;
761
+ $('jv-bf-start').textContent = state.armed.start ? kit.tx('settings.redesign.judgments.confirmStart', { money: money(est?.costUsd) }) : kit.tx('settings.redesign.judgments.start');
762
+ $('jv-bf-pause').disabled = !running;
763
+ $('jv-bf-resume').disabled = !paused;
764
+ $('jv-bf-cancel').disabled = !(running || paused);
765
+ const dot = $('jv-bf-dot');
766
+ dot.className = `wiz-status-dot${running ? ' spin' : status === 'done' ? ' green' : (paused && b.pausedReason === 'failures') || status === 'cancelled' ? ' red' : ''}`;
767
+ const total = Number(b.total || 0), judged = Number(b.judged || 0), remaining = Number(b.jobs?.pending || 0) + Number(b.jobs?.running || 0);
768
+ setText($('jv-bf-text'), status === 'idle' ? kit.tx('settings.redesign.judgments.idle')
769
+ : running ? kit.tx('settings.redesign.judgments.bfRunning', { judged: num(judged), remaining: num(remaining), retry: b.retryAfterMs > 0 ? (" " + kit.tx('settings.redesign.judgments.rateLimitedRetryingInS', { retryAfterMs: Math.ceil(b.retryAfterMs / 1000) })) : '' })
770
+ : paused ? kit.tx('settings.redesign.judgments.bfPaused', { reason: b.pausedReason === 'failures' ? (" " + kit.tx('settings.redesign.judgments.afterRepeatedFailures')) : '', judged: num(judged), remaining: num(remaining) })
771
+ : status === 'done' ? kit.tx('settings.redesign.judgments.doneJudgedIn', { num: num(judged), duration: duration(b.report?.wallClockMs) })
772
+ : kit.tx('settings.redesign.judgments.bfCancelled', { judged: num(judged) }));
773
+ $('jv-bf-bar').style.width = total ? `${Math.min(100, Math.round(judged / total * 100))}%` : '0%';
774
+ setText($('jv-bf-summary'), (judged || b.failures) ? kit.tx('settings.redesign.judgments.relationsSimilarPossibleConflictDuplicat', { num: num(b.relationsCreated?.similar), num2: num(b.relationsCreated?.possible_conflict), num3: num(b.relationsCreated?.duplicate_of), num4: num(b.disagreements?.kind), num5: num(b.disagreements?.importance), num6: num(b.tokensIn), num7: num(b.tokensOut), num8: num(b.failures), startedAt: b.startedAt ? (" " + kit.tx('settings.redesign.judgments.started', { stamp: stamp(b.startedAt) })) : '' }) : '');
775
+ setText($('jv-bf-error'), b.lastError ? kit.tx('settings.redesign.judgments.lastError2', { lastError: b.lastError, lastErrorAt: b.lastErrorAt ? (" " + kit.tx('settings.redesign.judgments.at', { clock: clock(b.lastErrorAt) })) : '' }) : '');
776
+ const r = b.report;
777
+ show($('jv-bf-report'), r ? `<strong>${kit.te('settings.redesign.judgments.report')}</strong> ${kit.th('settings.redesign.judgments.ofJudgedSkippedFailuresWallClock', { num: num(r.judged), num2: num(r.total), num3: num(r.skipped), num4: num(r.failures), duration: duration(r.wallClockMs), num5: num(r.avgLatencyMs), num6: num(r.relationsCreated.similar), num7: num(r.relationsCreated.possible_conflict), num8: num(r.relationsCreated.duplicate_of), pct: pct(r.disagreements.kindRate), pct2: pct(r.disagreements.importanceRate), num9: num(r.tokensIn), money: money(r.costUsd), num10: num(r.coverageAfter.judged) })} ${num(r.coverageAfter.total)}` : '', 'var(--s-darker)');
778
+ if (running || paused) startTimer('backfill', 2000, pollBackfill); else stopTimer('backfill');
779
+ }
780
+ async function pollBackfill() {
781
+ if (!tabActive()) return;
782
+ const r = await jget('/api/typesafe/backfill/status');
783
+ if (r.ok) { applyBackfill(r.backfill); if (['done', 'cancelled'].includes(r.backfill.status)) loadConfig(); }
784
+ }
785
+ const bfInputs = () => ({ project: $('jv-bf-project').value.trim() || null, limit: $('jv-bf-limit').value ? Number($('jv-bf-limit').value) : null });
786
+ async function bfAction(action, extra = {}) {
787
+ const r = await jpost('/api/typesafe/backfill', { action, ...bfInputs(), ...extra });
788
+ if (r.ok === false) { toast(kit.tx('settings.redesign.judgments.errBackfill', { error: r.error })); return null; }
789
+ applyBackfill(r.backfill);
790
+ return r.backfill;
791
+ }
792
+ $('jv-bf-estimate').addEventListener('click', async () => { $('jv-bf-estimate').disabled = true; const b = await bfAction('estimate'); state.estimateFor = b ? bfInputs() : null; $('jv-bf-estimate').disabled = false; if (b) applyBackfill(b); });
793
+ for (const id of ['jv-bf-project', 'jv-bf-limit']) $(id).addEventListener('input', () => { state.armed.start = false; if (state.backfill) applyBackfill(state.backfill); });
794
+ $('jv-bf-start').addEventListener('click', async () => {
795
+ if (!state.armed.start) { state.armed.start = true; applyBackfill(state.backfill); setTimeout(() => { state.armed.start = false; if (state.backfill) applyBackfill(state.backfill); }, 8000); return; }
796
+ state.armed.start = false;
797
+ const b = await bfAction('start', { confirm: true });
798
+ if (b) toast(kit.tx('settings.redesign.judgments.backfillStarted'));
799
+ });
800
+ $('jv-bf-pause').addEventListener('click', () => bfAction('pause'));
801
+ $('jv-bf-resume').addEventListener('click', () => bfAction('resume'));
802
+ $('jv-bf-cancel').addEventListener('click', async () => {
803
+ const btn = $('jv-bf-cancel');
804
+ if (!state.armed.cancel) { state.armed.cancel = true; btn.textContent = kit.tx('settings.redesign.judgments.confirmCancel'); setTimeout(() => { state.armed.cancel = false; btn.textContent = kit.tx('settings.redesign.judgments.cancel'); }, 5000); return; }
805
+ state.armed.cancel = false; btn.textContent = kit.tx('settings.redesign.judgments.cancel');
806
+ await bfAction('cancel');
807
+ });
808
+
809
+ // ── Bench ──
810
+ function benchTable(report) {
811
+ const rows = report.disagreements || [];
812
+ const table = $('jv-bench-table');
813
+ if (!rows.length) { table.innerHTML = '<tr><td class="settings-hint">' + kit.te('settings.redesign.judgments.noDisagreementsInThisSample') + '</td></tr>'; return; }
814
+ const cols = ['id', 'stored', 'judged', 'confidence', 'category', 'note'].filter(c => rows.some(r => r[c] != null));
815
+ table.innerHTML = `<thead><tr>${cols.map(c => `<th>${c}</th>`).join('')}</tr></thead><tbody>${rows.map(r => `<tr>${cols.map(c => `<td style="font-family:monospace;font-size:11px">${escapeHtml(c === 'confidence' ? pct(r[c]) : String(r[c] ?? '').slice(0, 80))}</td>`).join('')}</tr>`).join('')}</tbody>`;
816
+ }
817
+ $('jv-bench-run').addEventListener('click', async () => {
818
+ const btn = $('jv-bench-run'); btn.disabled = true; $('jv-bench-spinner').style.display = 'inline';
819
+ const surface = $('jv-bench-surface').value;
820
+ const r = await jpost('/api/typesafe/bench', { surface, limit: Number($('jv-bench-limit').value) || 50, project: $('jv-bench-project').value.trim() || undefined });
821
+ btn.disabled = false; $('jv-bench-spinner').style.display = 'none';
822
+ if (r.ok === false) { toast(kit.tx('settings.redesign.judgments.errBench', { error: r.error })); return; }
823
+ const rep = r.report;
824
+ const a = rep.agreement || {};
825
+ const lines = [];
826
+ if (surface === 'browser') {
827
+ const rate = (x) => (x && x.value != null ? `${(x.value * 100).toFixed(1)}% (${x.numerator}/${x.of})` : 'n/a');
828
+ const t = rep.target, p = rep.pageState, so = rep.social, g = rep.gates || {};
829
+ if (t) lines.push(kit.tx('settings.redesign.judgments.targetTop1PrecisionAbstentionAuto', { rate: rate(t.top1), rate2: rate(t.precision), rate3: rate(t.abstention), rate4: rate(t.eligiblePrecision), rate5: rate(t.coverage), num: num(t.unsafeSelections), num2: num(t.forbiddenAutoExecutable) }));
830
+ if (p) lines.push(kit.tx('settings.redesign.judgments.pageStateMacroF1BlockerRecall', { macroF1: p.macroF1 == null ? 'n/a' : (p.macroF1 * 100).toFixed(1) + '%', rate: rate(p.blockerRecall), num: num(p.authAsUsable) }));
831
+ if (so) lines.push(kit.tx('settings.redesign.judgments.socialStateControlUnsafe', { rate: rate(so.stateAccuracy), rate2: rate(so.candidateAccuracy), num: num(so.unsafeSelections) }));
832
+ lines.push(kit.tx('settings.redesign.judgments.autoHealGatePageStateAdvice', { passed: g.autoHeal?.passed ? kit.tx('settings.redesign.judgments.passed') : kit.tx('settings.redesign.judgments.notPassed', { failed: (g.autoHeal?.failed || []).join(', ') }), passed2: g.pageStateAdvisory?.passed ? kit.tx('settings.redesign.judgments.passed') : kit.tx('settings.redesign.judgments.notPassed', { failed: (g.pageStateAdvisory?.failed || []).join(', ') }) }));
833
+ lines.push(kit.tx('settings.redesign.judgments.benchLatency', { median: num(rep.latency?.medianMs), p95: num(rep.latency?.p95Ms), recorded: r.recorded?.recorded ? kit.tx('settings.redesign.judgments.recordedAsThisConfigurationSBenchmark') : kit.tx('settings.redesign.judgments.notRecorded', { reason: r.recorded?.reason || kit.tx('settings.redesign.judgments.unknown') }) }));
834
+ }
835
+ if (surface === 'desktop') {
836
+ const rate = (x) => (x && x.value != null ? `${(x.value * 100).toFixed(1)}% (${x.numerator}/${x.of})` : 'n/a');
837
+ const t = rep.target, g = rep.gates || {};
838
+ if (t) lines.push(kit.tx('settings.redesign.judgments.desktopTop1AdvisedPrecisionAbstention', { rate: rate(t.top1), rate2: rate(t.precision), rate3: rate(t.abstention), rate4: rate(t.eligiblePrecision), rate5: rate(t.coverage), num: num(t.unsafePresses), num2: num(t.forbiddenPresses) }));
839
+ lines.push(kit.tx('settings.redesign.judgments.pressByIntentGate', { passed: g.press?.passed ? kit.tx('settings.redesign.judgments.passed') : kit.tx('settings.redesign.judgments.notPassed', { failed: (g.press?.failed || []).join(', ') }) }));
840
+ lines.push(kit.tx('settings.redesign.judgments.benchLatency', { median: num(rep.latency?.medianMs), p95: num(rep.latency?.p95Ms), recorded: r.recorded?.recorded ? kit.tx('settings.redesign.judgments.recordedAsThisConfigurationSBenchmark') : kit.tx('settings.redesign.judgments.notRecorded', { reason: r.recorded?.reason || kit.tx('settings.redesign.judgments.unknown') }) }));
841
+ }
842
+ if (a.importance) lines.push(kit.tx('settings.redesign.judgments.importanceMeanWithin1PointWithin', { meanAbsDelta: a.importance.meanAbsDelta, within1: a.importance.within1, of: a.importance.of, within2: a.importance.within2, of2: a.importance.of }));
843
+ if (a.kind) lines.push(kit.tx('settings.redesign.judgments.kindAgree', { agree: a.kind.agree, of: a.kind.of, pct: pct(a.kind.rate) }));
844
+ if (a.relation) lines.push(kit.tx('settings.redesign.judgments.relationsAgree', { agree: a.relation.agree, of: a.relation.of, pct: pct(a.relation.rate) }));
845
+ if (a.historical) lines.push(kit.tx('settings.redesign.judgments.historicalAgree', { agree: a.historical.agree, of: a.historical.of, pct: pct(a.historical.rate) }));
846
+ $('jv-bench-summary').innerHTML = `<strong>${escapeHtml(surface)}</strong> ${kit.th('settings.redesign.judgments.itemsJudged', { num: num(rep.n), num2: num(rep.judged), unavailable: rep.unavailable ? (" " + kit.th('settings.redesign.judgments.unavailable2', { num: num(rep.unavailable) })) : '' })} ${lines.map(escapeHtml).join(' · ')}<br>${num(rep.tokens.input)} ${kit.th('settings.redesign.judgments.tokensInAvgPerItemAvg', { num: num(rep.tokens.avgInputPerItem), num2: num(rep.latency.avgMs) })} ${money(rep.cost)}${r.file ? ` · <code>${escapeHtml(r.file.split('/').slice(-2).join('/'))}</code>` : ''}`;
847
+ benchTable(rep);
848
+ show($('jv-bench-result'), $('jv-bench-result').innerHTML, 'var(--s-darker)');
849
+ toast(kit.tx('settings.redesign.judgments.benchDone', { surface, judged: num(rep.judged) }));
850
+ loadConfig();
851
+ });
852
+ $('jv-ba-autoheal').addEventListener('change', () => putConfig({ browserAssist: { autoHealEnabled: $('jv-ba-autoheal').checked } },
853
+ $('jv-ba-autoheal').checked ? kit.tx('settings.redesign.judgments.safeAutoHealIsOnOne') : kit.tx('settings.redesign.judgments.safeAutoHealIsOffAdvice')));
854
+ $('jv-da-press').addEventListener('change', () => putConfig({ desktopAssist: { pressEnabled: $('jv-da-press').checked } },
855
+ $('jv-da-press').checked ? kit.tx('settings.redesign.judgments.pressByIntentIsOnOne') : kit.tx('settings.redesign.judgments.pressByIntentIsOffAdvice')));
856
+ $('jv-apply-importance').addEventListener('change', () => putConfig({ applyJudgedImportance: $('jv-apply-importance').checked }, $('jv-apply-importance').checked ? kit.tx('settings.redesign.judgments.judgedImportanceWillBeAppliedBy') : kit.tx('settings.redesign.judgments.judgedImportanceStaysRecordedOnly')));
857
+
858
+ // ── Judgment log ──
859
+ function logRow(row) {
860
+ const answers = typeof row.answers === 'string' ? row.answers : JSON.stringify(row.answers, null, 2);
861
+ const riders = Array.isArray(row.surfaces) ? row.surfaces.filter(x => x && x !== row.surface) : [];
862
+ const outcome = row.outcome && typeof row.outcome === 'object' ? row.outcome : null;
863
+ const summary = `${clock(row.created_at)} · <code>${escapeHtml(row.surface)}</code>${riders.map(x => ` <span style="${CHIP}">+${escapeHtml(x)}</span>`).join('')} ${kit.th('settings.redesign.judgments.qMs', { origin: escapeHtml(row.origin), model: escapeHtml(row.model || ''), num: num(row.question_count), num2: num(row.latency_ms), num3: num(row.input_tokens) })}${num(row.output_tokens)}${row.cached ? ' · <span style="color:var(--accent)">' + kit.te('settings.redesign.judgments.cached') + '</span>' : ''}${row.error ? ` · <span style="color:#f66">${escapeHtml(row.error)}</span>` : ''}${row.entity_id ? ` · ${memLink(row.entity_id)}` : ''}${row.session_id ? ` · <a href="#" class="jv-session-link" data-session="${attr(row.session_id)}" title="${attr(row.session_id)}${kit.te('settings.redesign.judgments.thisSessionSJudgments')}" style="${LINK};font-family:monospace">${kit.te('settings.redesign.judgments.session')} ${escapeHtml(shortId(row.session_id))}</a>` : ''}${row.project ? ` · ${escapeHtml(row.project)}` : ''}`;
864
+ return `<details class="jv-log-row" data-id="${attr(row.id)}"${state.openLogIds.has(String(row.id)) ? ' open' : ''} style="font-size:12px;border:1px solid var(--s-medium);border-radius:6px;padding:4px 8px;background:var(--s-darker)">
865
+ <summary style="cursor:pointer;list-style:none" title="${attr(row.created_at)}">${summary}</summary>
866
+ <div class="settings-hint" style="margin:6px 0 2px">${kit.te('settings.redesign.judgments.state')}</div><pre style="margin:0;white-space:pre-wrap;font-size:11px;max-height:160px;overflow:auto">${escapeHtml(row.state_preview || '')}</pre>
867
+ <div class="settings-hint" style="margin:6px 0 2px">${kit.te('settings.redesign.judgments.answers')}</div><pre style="margin:0;white-space:pre-wrap;font-size:11px;max-height:200px;overflow:auto">${escapeHtml(answers || '—')}</pre>
868
+ ${outcome ? `<div class="settings-hint" style="margin:6px 0 2px">${kit.te('settings.redesign.judgments.outcome')}</div><pre style="${PRE}">${escapeHtml(JSON.stringify(outcome, null, 2))}</pre>` : ''}
869
+ </details>`;
870
+ }
871
+ async function refreshLog(force = false) {
872
+ if (!alive()) return;
873
+ const filter = { surface: $('jv-log-surface').value, origin: $('jv-log-origin').value, limit: $('jv-log-limit').value,
874
+ session: $('jv-log-session').value.trim(), entity: $('jv-log-entity').value.trim(), project: $('jv-log-project').value.trim() };
875
+ storage.setItem(LOG_FILTER_KEY, JSON.stringify(filter));
876
+ const qs = new URLSearchParams({ limit: filter.limit }); if (filter.surface) qs.set('surface', filter.surface); if (filter.origin) qs.set('origin', filter.origin);
877
+ if (filter.entity) qs.set('entity', filter.entity);
878
+ // An older server ignores session and project, so they are neither shown nor sent to it.
879
+ if (serverV2(state.cfg)) { if (filter.session) qs.set('session', filter.session); if (filter.project) qs.set('project', filter.project); }
880
+ const r = await jget(`/api/typesafe/log?${qs}`);
881
+ if (r.ok === false) { setText($('jv-log-status'), r.error || 'unavailable'); return; }
882
+ const signature = `${JSON.stringify(filter)}:${r.rows.length}:${r.rows[0]?.id ?? ''}`;
883
+ setText($('jv-log-status'), kit.tx('settings.redesign.judgments.logRowsOf', { shown: num(r.rows.length), total: num(r.total) }));
884
+ setText($('jv-log-badge'), kit.tx('settings.redesign.judgments.logRows', { total: num(r.total) }));
885
+ if (!force && signature === state.lastLogSignature) return;
886
+ state.lastLogSignature = signature;
887
+ const list = $('jv-log-list');
888
+ list.innerHTML = r.rows.length ? r.rows.map(logRow).join('') : '<div class="settings-hint">' + kit.te('settings.redesign.judgments.noJudgmentsLoggedYet') + '</div>';
889
+ }
890
+ $('jv-log-list').addEventListener('toggle', (e) => { const d = e.target; if (d?.classList?.contains('jv-log-row')) { if (d.open) state.openLogIds.add(d.dataset.id); else state.openLogIds.delete(d.dataset.id); } }, true);
891
+ $('jv-log-refresh').addEventListener('click', () => refreshLog(true));
892
+ for (const id of ['jv-log-surface', 'jv-log-origin', 'jv-log-limit']) $(id).addEventListener('change', () => refreshLog(true));
893
+ const typedLogFilter = debounce(() => refreshLog(true), 500);
894
+ for (const id of ['jv-log-session', 'jv-log-entity', 'jv-log-project']) {
895
+ $(id).addEventListener('input', typedLogFilter);
896
+ $(id).addEventListener('change', () => { typedLogFilter.cancel(); refreshLog(true); });
897
+ }
898
+ overlay.addEventListener('settings-close', () => typedLogFilter.cancel());
899
+ /** Session and project filters exist only where the server can apply them; origins come from the server's list. */
900
+ function syncLogFilters(cfg) {
901
+ const v2 = serverV2(cfg);
902
+ for (const id of ['jv-log-session', 'jv-log-project']) $(id).style.display = v2 ? '' : 'none';
903
+ const origin = $('jv-log-origin');
904
+ const want = Array.isArray(cfg.origins) ? cfg.origins.filter(o => typeof o === 'string' && o) : [];
905
+ if (!want.length || document.activeElement === origin) return;
906
+ if (Array.from(origin.options, o => o.value).filter(Boolean).join('|') === want.join('|')) return;
907
+ const value = origin.value;
908
+ // A select is as wide as its longest option, so the note on "live" goes in its tooltip.
909
+ origin.innerHTML = '<option value="">' + kit.te('settings.redesign.judgments.allOrigins') + '</option>' + want.map(o => o === 'live'
910
+ ? '<option value="live" title="' + kit.te('settings.redesign.judgments.everyOriginExceptBackfillAndBench') + '">' + kit.te('settings.redesign.judgments.liveRealTime') + '</option>'
911
+ : `<option value="${attr(o)}">${escapeHtml(o)}</option>`).join('');
912
+ origin.value = want.includes(value) ? value : '';
913
+ }
914
+ /** Point the log at one memory or one session, clearing the other filters, and bring it into view. */
915
+ function filterLogBy({ entity = '', session = '' }) {
916
+ for (const [id, value] of [['jv-log-surface', ''], ['jv-log-origin', ''], ['jv-log-project', ''], ['jv-log-entity', entity], ['jv-log-session', session]]) $(id).value = value;
917
+ const sec = $('jv-sec-log');
918
+ if (sec.classList.contains('collapsed')) sec.classList.remove('collapsed'); // the expand observer refreshes it
919
+ else refreshLog(true);
920
+ sec.scrollIntoView?.({ block: 'start', behavior: 'smooth' });
921
+ }
922
+ // Memory ids and session ids anywhere in the tab (timeline, log, supersessions) filter the log.
923
+ tabBody.addEventListener('click', (e) => {
924
+ const link = e.target.closest?.('a.jv-entity, a.jv-session-link');
925
+ if (!link) return;
926
+ e.preventDefault();
927
+ if (link.classList.contains('jv-entity')) filterLogBy({ entity: link.dataset.entity || '' });
928
+ else filterLogBy({ session: link.dataset.session || '' });
929
+ });
930
+
931
+ // ── Misfiled ──
932
+ async function refreshMisfiled() {
933
+ if (!alive()) return;
934
+ const r = await jget('/api/typesafe/category-checks?limit=50');
935
+ const list = $('jv-misfiled-list');
936
+ if (r.ok === false) { list.innerHTML = `<div class="settings-hint">${escapeHtml(r.error || kit.tx('settings.redesign.judgments.unavailable'))}</div>`; return; }
937
+ setText($('jv-misfiled-badge'), `${num(r.total)}`);
938
+ list.innerHTML = r.rows.length ? r.rows.map(row => `<div style="display:flex;gap:8px;align-items:flex-start;padding:8px 10px;border:1px solid var(--s-medium);border-radius:8px;background:var(--s-darker);font-size:12px">
939
+ <div style="flex:1;min-width:0"><div><code>${escapeHtml(row.category)}</code> → <code>${escapeHtml(row.category_judged)}</code> <span style="color:var(--t-muted)">(${pct(row.category_confidence)} · ${escapeHtml(row.project)} · ${escapeHtml(String(row.id).slice(0, 8))})</span></div><div class="settings-hint" style="margin:4px 0 0;white-space:pre-wrap">${escapeHtml(row.preview || '')}</div></div>
940
+ <button class="conn-add-btn jv-mf-apply" data-id="${attr(row.id)}" style="${btnStyle}">${kit.te('settings.redesign.judgments.apply')}</button><button class="conn-add-btn jv-mf-dismiss" data-id="${attr(row.id)}" style="${btnStyle}">${kit.te('settings.redesign.judgments.dismiss')}</button>
941
+ </div>`).join('') : '<div class="settings-hint">' + kit.te('settings.redesign.judgments.noDisagreementsRecordedNewWritesWill') + '</div>';
942
+ }
943
+ $('jv-misfiled-list').addEventListener('click', async (e) => {
944
+ const btn = e.target.closest('button[data-id]'); if (!btn) return;
945
+ const action = btn.classList.contains('jv-mf-apply') ? 'apply' : 'dismiss';
946
+ btn.disabled = true;
947
+ const r = await jpost(`/api/typesafe/category-checks/${encodeURIComponent(btn.dataset.id)}`, { action });
948
+ if (r.ok === false) { toast(kit.tx('settings.redesign.judgments.categoryCheck', { error: r.error })); btn.disabled = false; return; }
949
+ toast(action === 'apply' ? kit.tx('settings.redesign.judgments.memoryMoved') : kit.tx('settings.redesign.judgments.dismissed'));
950
+ refreshMisfiled();
951
+ });
952
+
953
+ // An endpoint this server does not have: a 404, or a 2xx that is not the JSON asked for (an SPA fallback, an empty object).
954
+ const missingEndpoint = (r) => r.status === 404 || (r.status >= 200 && r.status < 300);
955
+
956
+ // ── Coding sessions ──
957
+ // One card per Claude Code session (GET /api/typesafe/sessions). Opening a card reads that
958
+ // session's rows oldest first (GET /api/typesafe/log?session=…&order=asc) and gives each
959
+ // surface its own line. Hidden on servers that predate session attribution.
960
+ const knob = (surface, key, fallback) => { const v = state.cfg?.surfaces?.[surface]?.[key]; return fin(v) ? Number(v) : fallback; };
961
+ const flagged = (html, on) => (on ? `<strong style="color:#ffc107">${html}</strong>` : html);
962
+ const idList = (list) => (Array.isArray(list) ? list : []).map(x => (x && typeof x === 'object' ? x.id : x)).filter(x => x != null && x !== '').map(String);
963
+ // Rider answers on the prompt turn: [answer key, label, the rider's surface, its default bar].
964
+ const PROMPT_RIDERS = [['new_task', kit.tx('settings.redesign.judgments.newTask'), 'task-boundary', 0.6], ['past_session', kit.tx('settings.redesign.judgments.pastSession'), 'session-lookup', 0.7], ['reveals_preference', 'preference', 'user-learning', 0.7]];
965
+ const STOP_ANSWERS = [
966
+ ['human_blocker', kit.tx('settings.redesign.judgments.blockedOnAHuman'), () => 0.5], ['waiting_for_user', kit.tx('settings.redesign.judgments.waitingForTheUser'), () => 0.5],
967
+ ['worth_remembering', kit.tx('settings.redesign.judgments.worthRemembering'), () => knob('turn-worth', 'minConfidence', 0.35)], ['unsupported_claim', kit.tx('settings.redesign.judgments.unsupportedClaim'), () => knob('claim-check', 'minProbability', 0.9)],
968
+ ];
969
+ const levelName = (c) => (typeof c.level === 'string' && c.level ? c.level.split(':')[0] : relevanceName(c.relevance)) || '—';
970
+ function urgencyLine(row, a, o) {
971
+ const parts = [];
972
+ const u = a.urgency;
973
+ if (u && typeof u === 'object' && typeof u.choice === 'string') parts.push(`${kit.te('settings.redesign.judgments.urgency')} <strong>${escapeHtml(u.choice)}</strong> ${pct(u.confidence)}`);
974
+ for (const [key, label, surface, bar] of PROMPT_RIDERS) { const p = prob(a[key]); if (p != null) parts.push(flagged(`${label} ${pct(p)}`, p >= knob(surface, 'minProbability', bar))); }
975
+ // The Assistant's recall gate records what it did with the answer.
976
+ if (o && typeof o.decision === 'string' && o.decision) parts.push(`→ ${escapeHtml(o.decision)}${o.fallback === true ? (" " + kit.tx('settings.redesign.judgments.regexFallback')) : ''}`);
977
+ return { line: parts.join(' · ') };
978
+ }
979
+ function rankLine(row, a, o) {
980
+ if (!o || !Array.isArray(o.candidates)) return { line: '' };
981
+ const injected = new Set(idList(o.injected)), dropped = new Set(idList(o.dropped));
982
+ const rel = (c) => (fin(c.relevance) ? Number(c.relevance) : -1);
983
+ const candidates = o.candidates.filter(c => c && typeof c === 'object').sort((x, y) => rel(y) - rel(x));
984
+ const fate = (id) => injected.has(id) ? '<span style="color:var(--accent)">' + kit.te('settings.redesign.judgments.injected') + '</span>' : dropped.has(id) ? '<span style="color:#ffc107">' + kit.te('settings.redesign.judgments.dropped') + '</span>' : '<span style="color:var(--t-muted)">' + kit.te('settings.redesign.judgments.notInjected') + '</span>';
985
+ const detail = candidates.length ? `<div class="settings-hint" style="margin:6px 0 2px">${num(candidates.length)} ${kit.te('settings.redesign.judgments.judgedMostRelevantFirst')}</div><div class="jv-tl-candidates" style="display:flex;flex-direction:column;gap:2px;font-size:11px">${candidates.map(c => `<div>${memLink(c.id)} · ${escapeHtml(levelName(c))}${fin(c.relevance) ? ` ${Number(c.relevance).toFixed(1)}` : ''} · ${fate(String(c.id ?? ''))}</div>`).join('')}</div>` : '';
986
+ // Withheld: the Assistant's urgency judgment declined the turn, so nothing was injected or committed.
987
+ const withheld = typeof o.withheld === 'string' && o.withheld ? (" " + kit.th('settings.redesign.judgments.logWithheld', { reason: escapeHtml(o.withheld) })) : '';
988
+ return { line: kit.th('settings.redesign.judgments.logInjected', { injected: num(injected.size), dropped: num(dropped.size), floor: fin(o.floor) ? kit.te('settings.redesign.judgments.logFloor', { floor: Number(o.floor).toFixed(1) }) : kit.te('settings.redesign.judgments.noFloor'), withheld }), detail };
989
+ }
990
+ // Assistant workers (worker-outcome, rider worker-claim): what Jev read, and what the dispatcher did with it.
991
+ function workerLine(row, a, o) {
992
+ const parts = [];
993
+ const s = a.status, c = a.cause;
994
+ // A judgment under minConfidence was logged but not applied: say so, so it doesn't read as missing.
995
+ const bar = knob('worker-outcome', 'minConfidence', 0.7);
996
+ const below = (answer, applied) => (applied === false || (applied == null && Number(answer.confidence) < bar)) ? ` (below ${pct(bar)})` : '';
997
+ if (s && typeof s === 'object' && typeof s.choice === 'string') parts.push(`${kit.te('settings.redesign.judgments.status')} <strong>${escapeHtml(s.choice)}</strong> ${pct(s.confidence)}${s.choice === 'unclear' ? '' : below(s, o?.applied?.status)}`);
998
+ if (c && typeof c === 'object' && typeof c.choice === 'string') parts.push(kit.th('settings.redesign.judgments.logCause', { cause: `${escapeHtml(c.choice)} ${pct(c.confidence)}${below(c, o?.applied?.cause)}` }));
999
+ const p = prob(a.unsupported_claim);
1000
+ if (p != null) parts.push(flagged(kit.tx('settings.redesign.judgments.unsupportedClaim2', { pct: pct(p) }), p >= knob('worker-claim', 'minProbability', 0.9)));
1001
+ if (o?.retried === true) parts.push(kit.tx('settings.redesign.judgments.retryTurn'));
1002
+ if (o && typeof o.escalation === 'string' && o.escalation) parts.push(`→ ${escapeHtml(o.escalation)}`);
1003
+ return { line: parts.join(' · ') };
1004
+ }
1005
+ function stopLine(row, a) {
1006
+ const parts = [];
1007
+ for (const [key, label, bar] of STOP_ANSWERS) { const p = prob(a[key]); if (p != null) parts.push(flagged(`${label} ${pct(p)}`, p >= bar())); }
1008
+ return { line: parts.join(' · ') };
1009
+ }
1010
+ function staleLine(row, a, o) {
1011
+ const out = o || {};
1012
+ const p = fin(out.probability) ? Number(out.probability) : prob(a.accurate);
1013
+ const stale = typeof out.stale === 'boolean' ? out.stale : (row.surface === 'edit-stale' && p != null ? p < knob('edit-stale', 'minProbability', 0.4) : null);
1014
+ const file = out.file ? `<span title="${attr(out.file)}" style="font-family:monospace">${escapeHtml(String(out.file).split(/[\\/]/).slice(-3).join('/'))}</span>` : '';
1015
+ return { line: [file, row.entity_id ? kit.th('settings.redesign.judgments.logMemory', { memory: memLink(row.entity_id) }) : '', p != null ? kit.tx('settings.redesign.judgments.pStillAccurate', { pct: pct(p) }) : '', stale == null ? '' : stale ? flagged('stale', true) : kit.tx('settings.redesign.judgments.notStale')].filter(Boolean).join(' · ') };
1016
+ }
1017
+ function planLine(row, a, o) {
1018
+ if (!o) return { line: '' };
1019
+ const conflicts = idList(o.conflicts);
1020
+ const checked = fin(o.checked) ? (" " + kit.tx('settings.redesign.judgments.logOfChecked', { checked: num(o.checked) })) : '';
1021
+ return { line: conflicts.length ? `${flagged(kit.tx(conflicts.length === 1 ? 'settings.redesign.judgments.logConflicts.one' : 'settings.redesign.judgments.logConflicts.other', { count: num(conflicts.length) }), true)}${checked}: ${conflicts.map(memLink).join(', ')}` : kit.tx('settings.redesign.judgments.noConflicts', { checked: checked }) };
1022
+ }
1023
+ function digestLine(row, a, o) {
1024
+ if (!o) return { line: '' };
1025
+ const picked = Array.isArray(o.picked) ? o.picked.length : o.picked;
1026
+ return { line: kit.tx('settings.redesign.judgments.logPicked', { picked: fin(picked) ? num(picked) : '—', of: fin(o.of) ? num(o.of) : '—' }) };
1027
+ }
1028
+ function loopLine(row, a) {
1029
+ const p = prob(a.goal_met);
1030
+ return { line: p == null ? '' : flagged(kit.tx('settings.redesign.judgments.pGoalMet', { pct: pct(p) }), p >= knob('loop-goal', 'minConfidence', 0.8)) };
1031
+ }
1032
+ const TIMELINE = {
1033
+ 'prompt-urgency': urgencyLine, 'rerank': rankLine, 'brief-rank': rankLine,
1034
+ 'agent-message': stopLine, 'turn-worth': stopLine, 'claim-check': stopLine,
1035
+ 'edit-stale': staleLine, 'stale-check': staleLine, 'plan-conflict': planLine, 'compact-digest': digestLine, 'loop-goal': loopLine,
1036
+ 'worker-outcome': workerLine, 'worker-claim': workerLine,
1037
+ };
1038
+ const SHOWS_ENTITY = new Set(['edit-stale', 'stale-check']);
1039
+ // Rows whose entity is an Assistant run id, not a memory.
1040
+ const RUN_ENTITY = new Set(['worker-outcome', 'worker-claim']);
1041
+ function timelineRow(row) {
1042
+ const a = row.answers && typeof row.answers === 'object' ? row.answers : {};
1043
+ const o = row.outcome && typeof row.outcome === 'object' ? row.outcome : null;
1044
+ let view = { line: '' };
1045
+ const format = Object.prototype.hasOwnProperty.call(TIMELINE, row.surface) ? TIMELINE[row.surface] : null;
1046
+ try { view = format?.(row, a, o) || view; } catch { view = { line: '' }; }
1047
+ // Anything without a formatter, or with nothing to show, gets the compact line.
1048
+ const line = view.line || (row.error ? kit.tx('settings.redesign.judgments.noAnswer') : `q×${num(row.question_count)}`);
1049
+ const riders = Array.isArray(row.surfaces) ? row.surfaces.filter(x => x && x !== row.surface) : [];
1050
+ const meta = [`${num(row.latency_ms)} ms`, row.cached ? '<span style="color:var(--accent)">' + kit.te('settings.redesign.judgments.cached') + '</span>' : '', row.error ? `<span style="color:#f66">${escapeHtml(row.error)}</span>` : '',
1051
+ row.entity_id && !SHOWS_ENTITY.has(row.surface) ? (RUN_ENTITY.has(row.surface) ? `${kit.te('settings.redesign.judgments.run')} <code title="${attr(row.entity_id)}">${escapeHtml(shortId(row.entity_id))}</code>` : kit.th('settings.redesign.judgments.logMemory', { memory: memLink(row.entity_id) })) : '', row.origin ? escapeHtml(row.origin) : ''].filter(Boolean).join(' · ');
1052
+ const answers = typeof row.answers === 'string' ? row.answers : JSON.stringify(row.answers ?? null, null, 2);
1053
+ return `<details class="jv-tl-row" data-id="${attr(row.id)}" data-surface="${attr(row.surface)}"${state.openTimelineIds.has(String(row.id)) ? ' open' : ''} style="font-size:12px;border:1px solid var(--s-medium);border-radius:6px;padding:3px 8px;background:var(--s-darker)">
1054
+ <summary style="cursor:pointer;list-style:none" title="${attr(row.created_at)}">${clock(row.created_at)} · <code>${escapeHtml(row.surface)}</code>${riders.map(x => ` <span style="${CHIP}">+${escapeHtml(x)}</span>`).join('')} · <span class="jv-tl-line">${line}</span> <span style="color:var(--t-muted)">· ${meta}</span></summary>
1055
+ ${view.detail || ''}
1056
+ <div class="settings-hint" style="margin:6px 0 2px">${kit.te('settings.redesign.judgments.state')}</div><pre style="${PRE}">${escapeHtml(row.state_preview || '')}</pre>
1057
+ <div class="settings-hint" style="margin:6px 0 2px">${kit.te('settings.redesign.judgments.answers')}</div><pre style="${PRE}">${escapeHtml(answers || '—')}</pre>
1058
+ ${o ? `<div class="settings-hint" style="margin:6px 0 2px">${kit.te('settings.redesign.judgments.outcome')}</div><pre style="${PRE}">${escapeHtml(JSON.stringify(o, null, 2))}</pre>` : ''}
1059
+ </details>`;
1060
+ }
1061
+ function timelineHtml(sessionId, rows) {
1062
+ if (!rows.length) return '<div class="settings-hint">' + kit.te('settings.redesign.judgments.noJudgmentsLoggedForThisSession') + '</div>';
1063
+ return `<div class="settings-hint" style="margin:0 0 6px">${num(rows.length)} ${kit.th('settings.redesign.judgments.judgmentOldestFirst', { count: rows.length === 1 ? '' : 's', count2: rows.length >= TIMELINE_LIMIT ? (" " + kit.th('settings.redesign.judgments.theFirst', { num: num(TIMELINE_LIMIT) })) : '' })} <a href="#" class="jv-session-link" data-session="${attr(sessionId)}" style="${LINK}">${kit.te('settings.redesign.judgments.openInTheJudgmentLog')}</a></div>
1064
+ <div style="display:flex;flex-direction:column;gap:3px">${rows.map(timelineRow).join('')}</div>`;
1065
+ }
1066
+ const sessionKey = (s) => (s ? `${s.calls}:${s.last_at}` : '');
1067
+ // Assistant sessions log as assistant-<uuid>: show the part that tells them apart.
1068
+ const sessionLabel = (id) => (/^assistant-/.test(String(id ?? '')) ? kit.tx('settings.redesign.judgments.assistantSession', { id: String(id).slice(10, 18) }) : shortId(id));
1069
+ const sessionCardEl = (id) => Array.from($('jv-sessions-list').querySelectorAll('details.jv-sess')).find(d => d.dataset.session === id) || null;
1070
+ function sessionCard(s) {
1071
+ const id = String(s.session_id ?? '');
1072
+ const first = Date.parse(s.first_at), last = Date.parse(s.last_at);
1073
+ const sameDay = fin(first) && fin(last) && new Date(first).toDateString() === new Date(last).toDateString();
1074
+ const lastText = sameDay ? new Date(last).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }) : when(s.last_at);
1075
+ const span = fin(first) && fin(last) && last - first >= 1000 ? ` (${duration(last - first)})` : '';
1076
+ const counts = s.surfaces && typeof s.surfaces === 'object' ? Object.entries(s.surfaces).filter(([, n]) => Number(n) > 0).sort((x, y) => Number(y[1]) - Number(x[1])) : [];
1077
+ return `<details class="jv-sess" data-session="${attr(id)}"${state.openSessions.has(id) ? ' open' : ''} style="font-size:12px;border:1px solid var(--s-medium);border-radius:8px;padding:6px 10px;background:var(--s-darker)">
1078
+ <summary style="cursor:pointer" title="${attr(id)}"><code>${escapeHtml(sessionLabel(id) || '—')}</code> ${kit.th('settings.redesign.judgments.judgment', { project: escapeHtml(s.project || kit.tx('settings.redesign.judgments.noProject')), when: escapeHtml(when(s.first_at)), lastText: escapeHtml(lastText), span: escapeHtml(span), num: num(s.calls) })}${Number(s.calls) === 1 ? '' : 's'}${Number(s.failures) ? ` · <span style="color:#f66">${num(s.failures)} ${kit.te('settings.redesign.judgments.failed')}</span>` : ''}
1079
+ <span class="jv-sess-chips" style="display:flex;flex-wrap:wrap;gap:4px;margin:4px 0 0 14px">${counts.map(([name, n]) => `<span style="${CHIP}">${escapeHtml(name)} ${num(n)}</span>`).join('')}</span>
1080
+ </summary>
1081
+ <div class="jv-sess-timeline" style="margin-top:8px">${state.timelineHtml.get(id) || '<div class="settings-hint">' + kit.te('settings.redesign.judgments.loading') + '</div>'}</div>
1082
+ </details>`;
1083
+ }
1084
+ function renderSessionsMeta(cfg) {
1085
+ const sec = $('jv-sec-sessions');
1086
+ const available = serverV2(cfg) && !state.sessionsUnsupported;
1087
+ sec.style.display = available ? '' : 'none';
1088
+ if (available && !state.sessionsBooted && !sec.classList.contains('collapsed')) { state.sessionsBooted = true; refreshSessions(true); }
1089
+ }
1090
+ async function refreshSessions(force = false) {
1091
+ if (!alive()) return;
1092
+ const sec = $('jv-sec-sessions');
1093
+ if (!serverV2(state.cfg) || state.sessionsUnsupported) { sec.style.display = 'none'; return; }
1094
+ state.sessionsBooted = true;
1095
+ const qs = new URLSearchParams({ since: new Date(Date.now() - SESSIONS_DAYS * 864e5).toISOString(), limit: '30' });
1096
+ const r = await jget(`/api/typesafe/sessions?${qs}`);
1097
+ if (!alive()) return;
1098
+ if (!Array.isArray(r.rows)) {
1099
+ if (missingEndpoint(r)) { state.sessionsUnsupported = true; sec.style.display = 'none'; return; }
1100
+ setText($('jv-sessions-status'), r.error || 'unavailable');
1101
+ return;
1102
+ }
1103
+ const rows = r.rows.filter(s => s && typeof s === 'object' && s.session_id != null && s.session_id !== '');
1104
+ state.sessions = new Map(rows.map(s => [String(s.session_id), s]));
1105
+ setText($('jv-sessions-badge'), kit.tx(rows.length === 1 ? 'settings.redesign.judgments.sessionsBadge.one' : 'settings.redesign.judgments.sessionsBadge.other', { count: num(rows.length), days: SESSIONS_DAYS }));
1106
+ setText($('jv-sessions-status'), rows.length ? kit.tx('settings.redesign.judgments.theMostRecentNewestActivityFirst', { num: num(rows.length) }) : '');
1107
+ const signature = rows.map(s => `${s.session_id}:${sessionKey(s)}`).join('|');
1108
+ if (force || signature !== state.sessionsSignature) {
1109
+ state.sessionsSignature = signature;
1110
+ $('jv-sessions-list').innerHTML = rows.length ? rows.map(sessionCard).join('')
1111
+ : `<div class="settings-hint">${kit.th('settings.redesign.judgments.noJudgmentsCarryASessionId', { SESSIONS_DAYS: SESSIONS_DAYS })}</div>`;
1112
+ }
1113
+ // Open timelines follow their session: reload when it gained judgments (or on an explicit refresh).
1114
+ for (const id of state.openSessions) if (state.sessions.has(id) && (force || state.timelineKeys.get(id) !== sessionKey(state.sessions.get(id)))) loadTimeline(id);
1115
+ }
1116
+ async function loadTimeline(id) {
1117
+ if (state.timelineLoading.has(id)) return;
1118
+ state.timelineLoading.add(id);
1119
+ state.timelineKeys.set(id, sessionKey(state.sessions.get(id)));
1120
+ try {
1121
+ const r = await jget(`/api/typesafe/log?${new URLSearchParams({ session: id, order: 'asc', limit: String(TIMELINE_LIMIT) })}`);
1122
+ if (!alive()) return;
1123
+ let html;
1124
+ if (Array.isArray(r.rows)) {
1125
+ // Only this session's rows, oldest first, even from a server that ignores `session` or `order`.
1126
+ const rows = r.rows.filter(row => row && typeof row === 'object' && String(row.session_id ?? '') === id);
1127
+ if (rows.every(row => fin(row.id))) rows.sort((x, y) => Number(x.id) - Number(y.id));
1128
+ html = timelineHtml(id, rows); state.timelineHtml.set(id, html);
1129
+ } else {
1130
+ html = `<div class="settings-hint" style="color:#f66">${escapeHtml(r.error || kit.tx('settings.redesign.judgments.theTimelineIsUnavailable'))}</div>`;
1131
+ state.timelineKeys.delete(id);
1132
+ }
1133
+ const box = sessionCardEl(id)?.querySelector('.jv-sess-timeline');
1134
+ if (box) box.innerHTML = html;
1135
+ } catch (error) {
1136
+ // Called without await (a toggle, a poll): render the failure rather than reject.
1137
+ state.timelineKeys.delete(id);
1138
+ const box = sessionCardEl(id)?.querySelector('.jv-sess-timeline');
1139
+ if (box) box.innerHTML = `<div class="settings-hint" style="color:#f66">${escapeHtml(error?.message || kit.tx('settings.redesign.judgments.theTimelineCouldNotBeDrawn'))}</div>`;
1140
+ } finally { state.timelineLoading.delete(id); }
1141
+ }
1142
+ // <details> toggles do not bubble; capture them. Re-rendered open cards fire one too, which is harmless.
1143
+ $('jv-sessions-list').addEventListener('toggle', (e) => {
1144
+ const d = e.target;
1145
+ if (d?.classList?.contains('jv-tl-row')) { if (d.open) state.openTimelineIds.add(d.dataset.id); else state.openTimelineIds.delete(d.dataset.id); return; }
1146
+ if (!d?.classList?.contains('jv-sess')) return;
1147
+ const id = d.dataset.session;
1148
+ if (!d.open) { state.openSessions.delete(id); return; }
1149
+ state.openSessions.add(id);
1150
+ if (!state.timelineHtml.has(id)) loadTimeline(id);
1151
+ }, true);
1152
+ $('jv-sessions-refresh').addEventListener('click', () => refreshSessions(true));
1153
+
1154
+ // ── Superseded by Jev ──
1155
+ function renderSupersessionsMeta(cfg) {
1156
+ const sec = $('jv-sec-supersessions');
1157
+ const info = cfg.supersessions && typeof cfg.supersessions === 'object' ? cfg.supersessions : null;
1158
+ if (!info || state.supersessionsUnsupported) { sec.style.display = 'none'; return; }
1159
+ sec.style.display = '';
1160
+ setText($('jv-supersessions-badge'), kit.tx('settings.redesign.judgments.supersessionsActive', { count: num(info.active) }));
1161
+ if (!state.supersessionsBooted && !sec.classList.contains('collapsed')) { state.supersessionsBooted = true; refreshSupersessions(); }
1162
+ }
1163
+ function supersessionRow(row) {
1164
+ const side = (label, id, created, preview, dim) => `<div style="min-width:0"><div class="settings-hint" style="margin:0 0 2px">${label} · ${memLink(id)} · ${escapeHtml(day(created))}</div><div style="white-space:pre-wrap;overflow-wrap:anywhere${dim ? ';opacity:.75' : ''}">${escapeHtml(preview || '')}</div></div>`;
1165
+ return `<div class="jv-ss-row" data-id="${attr(row.id)}" style="padding:8px 10px;border:1px solid var(--s-medium);border-radius:8px;background:var(--s-darker);font-size:12px">
1166
+ <div style="display:flex;gap:8px;align-items:center;flex-wrap:wrap">
1167
+ <span style="flex:1;min-width:0"><code>${escapeHtml(row.category || '—')}</code> ${kit.th('settings.redesign.judgments.recorded', { project: escapeHtml(row.project || kit.tx('settings.redesign.judgments.noProject')) })} ${escapeHtml(when(row.created_at))}${row.automatic ? (" " + kit.te('settings.redesign.judgments.byJev')) : ' · <span style="color:var(--accent)">' + kit.te('settings.redesign.judgments.kept') + '</span>'}</span>
1168
+ ${row.automatic ? `<button class="conn-add-btn jv-ss-keep" data-id="${attr(row.id)}" style="${btnStyle}">${kit.te('settings.redesign.judgments.keep')}</button>` : ''}<button class="conn-add-btn jv-ss-undo" data-id="${attr(row.id)}" style="${btnStyle}">${kit.te('settings.redesign.judgments.undo')}</button>
1169
+ </div>
1170
+ <div style="display:grid;grid-template-columns:1fr 1fr;gap:10px;margin-top:6px">${side(kit.tx('settings.redesign.judgments.newerCurrent'), row.from_id, row.from_created_at, row.from_preview, false)}${side(kit.tx('settings.redesign.judgments.olderOutOfCurrentRecall'), row.to_id, row.to_created_at, row.to_preview, true)}</div>
1171
+ </div>`;
1172
+ }
1173
+ async function refreshSupersessions() {
1174
+ if (!alive()) return;
1175
+ const sec = $('jv-sec-supersessions'), list = $('jv-supersessions-list');
1176
+ if (!state.cfg?.supersessions || state.supersessionsUnsupported) { sec.style.display = 'none'; return; }
1177
+ state.supersessionsBooted = true;
1178
+ const r = await jget('/api/typesafe/supersessions?limit=50');
1179
+ if (!alive()) return;
1180
+ if (!Array.isArray(r.rows)) {
1181
+ if (missingEndpoint(r)) { state.supersessionsUnsupported = true; sec.style.display = 'none'; return; }
1182
+ list.innerHTML = `<div class="settings-hint">${escapeHtml(r.error || kit.tx('settings.redesign.judgments.unavailable'))}</div>`;
1183
+ return;
1184
+ }
1185
+ const rows = r.rows.filter(row => row && typeof row === 'object');
1186
+ setText($('jv-supersessions-status'), rows.length ? kit.tx('settings.redesign.judgments.showingOfNewestFirst', { num: num(rows.length), num2: num(fin(r.total) ? r.total : rows.length) }) : '');
1187
+ list.innerHTML = rows.length ? rows.map(supersessionRow).join('')
1188
+ : '<div class="settings-hint">' + kit.te('settings.redesign.judgments.nothingSupersededWhenMaintenanceRelatesA') + '</div>';
1189
+ }
1190
+ $('jv-supersessions-list').addEventListener('click', async (e) => {
1191
+ const btn = e.target.closest('button.jv-ss-undo, button.jv-ss-keep'); if (!btn) return;
1192
+ const action = btn.classList.contains('jv-ss-undo') ? 'undo' : 'keep';
1193
+ btn.disabled = true;
1194
+ const r = await jpost(`/api/typesafe/supersessions/${encodeURIComponent(btn.dataset.id)}`, { action });
1195
+ if (r.ok === false) { toast(kit.tx('settings.redesign.judgments.errSupersession', { error: r.error || kit.tx('settings.redesign.judgments.failed') })); btn.disabled = false; return; }
1196
+ toast(action === 'undo' ? kit.tx('settings.redesign.judgments.undoneTheOlderMemoryIsBack') : kit.tx('settings.redesign.judgments.keptTheRelationIsConfirmed'));
1197
+ await refreshSupersessions();
1198
+ loadConfig();
1199
+ });
1200
+
1201
+ // ── Trash triage ──
1202
+ // The server streams NDJSON snapshots (?stream=1) so rows show while batches
1203
+ // are still being judged; a plain JSON answer renders the same way.
1204
+ async function streamTriage(url, onSnapshot) {
1205
+ state.triageAbort?.abort();
1206
+ const controller = new AbortController();
1207
+ state.triageAbort = controller;
1208
+ try {
1209
+ const res = await fetch(url, { signal: controller.signal });
1210
+ if (!res.ok || !(res.headers.get('content-type') || '').includes('ndjson') || !res.body?.getReader) return await readJson(res);
1211
+ const reader = res.body.getReader();
1212
+ const decoder = new TextDecoder();
1213
+ let buffer = '';
1214
+ while (true) {
1215
+ const { done, value } = await reader.read();
1216
+ if (done) break;
1217
+ buffer += decoder.decode(value, { stream: true });
1218
+ let idx;
1219
+ while ((idx = buffer.indexOf('\n')) !== -1) {
1220
+ const line = buffer.slice(0, idx).trim();
1221
+ buffer = buffer.slice(idx + 1);
1222
+ if (!line) continue;
1223
+ let payload;
1224
+ try { payload = JSON.parse(line); } catch { continue; }
1225
+ if (payload.done || payload.ok === false) return payload;
1226
+ onSnapshot(payload);
1227
+ }
1228
+ }
1229
+ return { ok: false, error: kit.tx('settings.redesign.judgments.theTriageStreamEndedEarly') };
1230
+ } catch (error) {
1231
+ return { ok: false, error: error?.name === 'AbortError' ? kit.tx('settings.redesign.judgments.cancelled') : (error?.message || kit.tx('settings.redesign.judgments.networkError')), status: 0 };
1232
+ } finally {
1233
+ if (state.triageAbort === controller) state.triageAbort = null;
1234
+ }
1235
+ }
1236
+ function triageRow(row, pending) {
1237
+ const scoreText = row.expendability == null ? (pending ? '…' : '—') : Number(row.expendability).toFixed(1);
1238
+ return `<div data-row="${attr(row.id)}" style="display:flex;gap:8px;align-items:flex-start;padding:8px 10px;border:1px solid var(--s-medium);border-radius:8px;background:var(--s-darker);font-size:12px">
1239
+ <input type="checkbox" class="jv-tr-pick" aria-label="${kit.te('settings.redesign.judgments.name.triagePick')}" data-id="${attr(row.id)}"${state.triageSelected.has(row.id) ? ' checked' : ''} style="margin-top:3px;flex:0 0 auto">
1240
+ <div style="min-width:60px;text-align:center"><div style="font-size:16px;font-weight:700">${escapeHtml(scoreText)}</div><div class="settings-hint" style="margin:0">${escapeHtml(row.level || '')}</div></div>
1241
+ <div style="flex:1;min-width:0"><div><code>${escapeHtml(row.category)}</code> ${kit.th('settings.redesign.judgments.importance2', { project: escapeHtml(row.project), importance: escapeHtml(String(row.importance)) })} ${escapeHtml(row.reasons?.join(', ') || '')}</div><div class="settings-hint" style="margin:4px 0 0;white-space:pre-wrap">${escapeHtml(row.preview || '')}</div></div>
1242
+ <button class="conn-add-btn jv-tr-trash" data-id="${attr(row.id)}" style="${btnStyle}">${kit.te('settings.redesign.judgments.moveToTrash')}</button>
1243
+ </div>`;
1244
+ }
1245
+ /** Renders the last answer: the page of rows, the status line and the selection controls. */
1246
+ function renderTriage(r) {
1247
+ if (r) state.triageLast = r;
1248
+ const answer = state.triageLast;
1249
+ if (!answer) return;
1250
+ const rows = (answer.rows || []).filter(row => !state.triageTrashed.has(row.id));
1251
+ const pending = Number(answer.pending || 0);
1252
+ const shown = pending ? rows.length : Math.min(state.triagePage * TRIAGE_PAGE, rows.length);
1253
+ const unjudged = answer.judged ? rows.filter(row => row.expendability == null).length : 0;
1254
+ setText($('jv-triage-status'), pending
1255
+ ? kit.tx('settings.redesign.judgments.judgingOfCandidatesLeft', { num: num(pending), num2: num(answer.candidates) })
1256
+ : kit.tx('settings.redesign.judgments.triageShowing', { shown: num(shown), total: num(rows.length), detail: " " + (answer.judged ? kit.tx('settings.redesign.judgments.triageJudged', { saved: answer.saved ? (" " + kit.tx('settings.redesign.judgments.fromSavedScores', { num: num(answer.saved) })) : '', unjudged: unjudged ? (" " + kit.tx('settings.redesign.judgments.notJudgedThisTime', { num: num(unjudged) })) : '' }) : kit.tx('settings.redesign.judgments.fallbackOrdering')) }));
1257
+ setText($('jv-triage-badge'), `${num(rows.length)}`);
1258
+ $('jv-triage-list').innerHTML = rows.length ? rows.slice(0, shown).map(row => triageRow(row, pending)).join('') : '<div class="settings-hint">' + kit.te('settings.redesign.judgments.noCandidatesFound') + '</div>';
1259
+ const remaining = rows.length - shown;
1260
+ $('jv-triage-more-wrap').style.display = remaining > 0 ? 'block' : 'none';
1261
+ setText($('jv-triage-more'), kit.tx('settings.redesign.judgments.showMoreLeft', { num: num(remaining) }));
1262
+ $('jv-triage-bulk').style.display = rows.length ? 'flex' : 'none';
1263
+ for (const id of [...state.triageSelected]) if (!rows.some(row => row.id === id)) state.triageSelected.delete(id);
1264
+ const picks = Array.from($('jv-triage-list').querySelectorAll('input.jv-tr-pick'));
1265
+ $('jv-triage-all').checked = picks.length > 0 && picks.every(pick => pick.checked);
1266
+ renderTriageSelection();
1267
+ }
1268
+ function renderTriageSelection() {
1269
+ const btn = $('jv-triage-trash-selected');
1270
+ const count = state.triageSelected.size;
1271
+ btn.disabled = count === 0;
1272
+ setText(btn, state.armed.triage && count ? kit.tx('settings.redesign.judgments.triageConfirm', { count: num(count) }) : kit.tx('settings.redesign.judgments.moveToTrash2', { num: num(count) }));
1273
+ }
1274
+ $('jv-triage-run').addEventListener('click', async () => {
1275
+ const btn = $('jv-triage-run'); btn.disabled = true; $('jv-triage-spinner').style.display = 'inline';
1276
+ state.triageTrashed = new Set(); state.triageSelected = new Set(); state.triageLast = null; state.triagePage = 1; state.armed.triage = false;
1277
+ const qs = new URLSearchParams({ limit: TRIAGE_LIMIT, stream: '1' }); const project = $('jv-triage-project').value.trim(); if (project) qs.set('project', project);
1278
+ setText($('jv-triage-status'), kit.tx('settings.redesign.judgments.findingCandidates'));
1279
+ const r = await streamTriage(`/api/trash/candidates?${qs}`, renderTriage);
1280
+ btn.disabled = false; $('jv-triage-spinner').style.display = 'none';
1281
+ if (r.ok === false) { setText($('jv-triage-status'), r.status === 404 ? kit.tx('settings.redesign.judgments.notAvailableOnThisServerYet') : (r.error || 'unavailable')); return; }
1282
+ renderTriage(r);
1283
+ });
1284
+ $('jv-triage-more').addEventListener('click', () => { state.triagePage++; renderTriage(); });
1285
+ $('jv-triage-all').addEventListener('change', () => {
1286
+ for (const pick of $('jv-triage-list').querySelectorAll('input.jv-tr-pick')) {
1287
+ pick.checked = $('jv-triage-all').checked;
1288
+ if (pick.checked) state.triageSelected.add(pick.dataset.id); else state.triageSelected.delete(pick.dataset.id);
1289
+ }
1290
+ state.armed.triage = false;
1291
+ renderTriageSelection();
1292
+ });
1293
+ $('jv-triage-list').addEventListener('change', (e) => {
1294
+ const pick = e.target.closest('input.jv-tr-pick'); if (!pick) return;
1295
+ if (pick.checked) state.triageSelected.add(pick.dataset.id); else state.triageSelected.delete(pick.dataset.id);
1296
+ state.armed.triage = false;
1297
+ const picks = Array.from($('jv-triage-list').querySelectorAll('input.jv-tr-pick'));
1298
+ $('jv-triage-all').checked = picks.length > 0 && picks.every(box => box.checked);
1299
+ renderTriageSelection();
1300
+ });
1301
+ $('jv-triage-trash-selected').addEventListener('click', async () => {
1302
+ const ids = [...state.triageSelected];
1303
+ if (!ids.length) return;
1304
+ if (!state.armed.triage) {
1305
+ state.armed.triage = true; renderTriageSelection();
1306
+ setTimeout(() => { if (state.armed.triage) { state.armed.triage = false; renderTriageSelection(); } }, 5000);
1307
+ return;
1308
+ }
1309
+ state.armed.triage = false;
1310
+ const btn = $('jv-triage-trash-selected'); btn.disabled = true;
1311
+ const r = await jpost('/api/trash/candidates/trash-selected', { ids });
1312
+ if (r.ok === false) { toast(kit.tx('settings.redesign.judgments.errTrash', { error: r.error })); renderTriageSelection(); return; }
1313
+ for (const id of ids) state.triageTrashed.add(id);
1314
+ state.triageSelected.clear();
1315
+ renderTriage();
1316
+ toast(kit.tx('settings.redesign.judgments.movedToTrashRestorableFromThe2', { num: num(r.trashed), count: r.skipped?.length ? (" " + kit.tx('settings.redesign.judgments.skipped2', { num: num(r.skipped.length) })) : '' }));
1317
+ });
1318
+ $('jv-triage-list').addEventListener('click', async (e) => {
1319
+ const btn = e.target.closest('button.jv-tr-trash'); if (!btn) return;
1320
+ btn.disabled = true;
1321
+ const r = await jpost(`/api/trash/candidates/${encodeURIComponent(btn.dataset.id)}/trash`);
1322
+ if (r.ok === false) { toast(kit.tx('settings.redesign.judgments.errTrash', { error: r.error })); btn.disabled = false; return; }
1323
+ state.triageTrashed.add(btn.dataset.id);
1324
+ state.triageSelected.delete(btn.dataset.id);
1325
+ renderTriage();
1326
+ toast(kit.tx('settings.redesign.judgments.movedToTrashRestorableFromThe'));
1327
+ });
1328
+
1329
+ // ── Timers ──
1330
+ function startTimer(name, ms, fn) {
1331
+ if (state.timers[name]) return;
1332
+ state.timers[name] = setInterval(() => { if (!alive()) return stopAll(); fn().catch?.(() => {}); }, ms);
1333
+ }
1334
+ function stopTimer(name) { if (state.timers[name]) clearInterval(state.timers[name]); delete state.timers[name]; }
1335
+ function stopAll() { for (const name of Object.keys(state.timers)) stopTimer(name); for (const fn of surfaceDebounce.values()) fn.cancel?.(); expandObserver.disconnect(); state.triageAbort?.abort(); }
1336
+ overlay.addEventListener('settings-close', stopAll);
1337
+ startTimer('config', 5000, async () => { if (tabActive()) await loadConfig(); });
1338
+ startTimer('log', 5000, async () => { if (tabActive() && !$('jv-sec-log').classList.contains('collapsed')) await refreshLog(); });
1339
+ startTimer('sessions', 10000, async () => { const sec = $('jv-sec-sessions'); if (tabActive() && sec.style.display !== 'none' && !sec.classList.contains('collapsed')) await refreshSessions(); });
1340
+
1341
+ // ── Boot ──
1342
+ if (initial && initial.ok !== false) applyConfig(initial); else loadConfig();
1343
+ jget('/api/typesafe/backfill/status').then(r => { if (r.ok) applyBackfill(r.backfill); });
1344
+ if (!$('jv-sec-log').classList.contains('collapsed')) refreshLog(true);
1345
+ if (!$('jv-sec-misfiled').classList.contains('collapsed')) refreshMisfiled();
1346
+ }