agentix-cli 0.25.0 → 0.27.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 (269) hide show
  1. package/README.md +2 -0
  2. package/dist/{App-6Y7B3YQH.js → App-U52IPUK2.js} +2 -2
  3. package/dist/{ChatApp-KIEUJ2DS.js → ChatApp-XP4OMHPL.js} +2 -2
  4. package/dist/agent-V2CA4TXD.js +2 -0
  5. package/dist/article-fields-L4D2BOGY.js +2 -0
  6. package/dist/article-quality-44ONVGLN.js +3 -0
  7. package/dist/article-quality-44ONVGLN.js.map +1 -0
  8. package/dist/{audit-JU3HWISB.js → audit-4245CX6P.js} +2 -2
  9. package/dist/backfill-MTD2B62I.js +6 -0
  10. package/dist/backfill-MTD2B62I.js.map +1 -0
  11. package/dist/{board-dashboard-CQ5F4GPD.js → board-dashboard-TTMAYRLG.js} +2445 -519
  12. package/dist/board-dashboard-TTMAYRLG.js.map +1 -0
  13. package/dist/{builtin-FRKAQMFS.js → builtin-B67DMJ3M.js} +2 -2
  14. package/dist/calibration-ILS5OI44.js +2 -0
  15. package/dist/chunk-2T5W3ETS.js +5 -0
  16. package/dist/{chunk-FDMWOPEL.js.map → chunk-2T5W3ETS.js.map} +1 -1
  17. package/dist/chunk-3SCPRYJB.js +5 -0
  18. package/dist/chunk-3SCPRYJB.js.map +1 -0
  19. package/dist/chunk-4BR7D2OU.js +36 -0
  20. package/dist/chunk-4BR7D2OU.js.map +1 -0
  21. package/dist/{chunk-P4VWIVKN.js → chunk-52ZPELD2.js} +403 -29
  22. package/dist/chunk-52ZPELD2.js.map +1 -0
  23. package/dist/{chunk-5U35WAZF.js → chunk-77OV7346.js} +6 -6
  24. package/dist/chunk-77OV7346.js.map +1 -0
  25. package/dist/chunk-AJ5RMU7X.js +2 -0
  26. package/dist/chunk-AJ5RMU7X.js.map +1 -0
  27. package/dist/chunk-AJONZWLT.js +2 -0
  28. package/dist/chunk-AJONZWLT.js.map +1 -0
  29. package/dist/chunk-AX2PWSRT.js +114 -0
  30. package/dist/chunk-AX2PWSRT.js.map +1 -0
  31. package/dist/chunk-BH3GTCZ6.js +2 -0
  32. package/dist/chunk-BH3GTCZ6.js.map +1 -0
  33. package/dist/chunk-BPIRO4YD.js +126 -0
  34. package/dist/chunk-BPIRO4YD.js.map +1 -0
  35. package/dist/chunk-CJQQIRPD.js +13 -0
  36. package/dist/chunk-CJQQIRPD.js.map +1 -0
  37. package/dist/chunk-DCDQZR7I.js +24 -0
  38. package/dist/chunk-DCDQZR7I.js.map +1 -0
  39. package/dist/chunk-EVWEG3VD.js +3 -0
  40. package/dist/chunk-EVWEG3VD.js.map +1 -0
  41. package/dist/chunk-F4HZTZHW.js +2 -0
  42. package/dist/chunk-F4HZTZHW.js.map +1 -0
  43. package/dist/chunk-F6CNF7V3.js +25 -0
  44. package/dist/chunk-F6CNF7V3.js.map +1 -0
  45. package/dist/chunk-FIEJDZVC.js +121 -0
  46. package/dist/chunk-FIEJDZVC.js.map +1 -0
  47. package/dist/chunk-FIOB3MEX.js +2 -0
  48. package/dist/{chunk-H5D7ATXS.js → chunk-FKBGJYWQ.js} +5 -5
  49. package/dist/chunk-FKBGJYWQ.js.map +1 -0
  50. package/dist/chunk-GDWJUGB5.js +9 -0
  51. package/dist/chunk-GDWJUGB5.js.map +1 -0
  52. package/dist/{chunk-TRJTTBE2.js → chunk-IB3F7VBE.js} +7 -7
  53. package/dist/{chunk-TRJTTBE2.js.map → chunk-IB3F7VBE.js.map} +1 -1
  54. package/dist/chunk-IBUSGCLQ.js +109 -0
  55. package/dist/chunk-IBUSGCLQ.js.map +1 -0
  56. package/dist/{chunk-LDHPMJ7M.js → chunk-IWMW5432.js} +3 -3
  57. package/dist/chunk-IWMW5432.js.map +1 -0
  58. package/dist/chunk-K33GIBSE.js +2 -0
  59. package/dist/chunk-K33GIBSE.js.map +1 -0
  60. package/dist/chunk-L2IZPR6P.js +45 -0
  61. package/dist/chunk-L2IZPR6P.js.map +1 -0
  62. package/dist/chunk-L2WSPY6S.js +2 -0
  63. package/dist/chunk-L2WSPY6S.js.map +1 -0
  64. package/dist/chunk-LCIREYTD.js +54 -0
  65. package/dist/chunk-LCIREYTD.js.map +1 -0
  66. package/dist/chunk-LNRB5MCU.js +530 -0
  67. package/dist/chunk-LNRB5MCU.js.map +1 -0
  68. package/dist/chunk-OPHIXXGI.js +157 -0
  69. package/dist/chunk-OPHIXXGI.js.map +1 -0
  70. package/dist/{chunk-JTJIPNXY.js → chunk-PNWNVOEZ.js} +1 -1
  71. package/dist/chunk-PNWNVOEZ.js.map +1 -0
  72. package/dist/{chunk-LNXCJWOU.js → chunk-UFAGYX73.js} +45 -47
  73. package/dist/chunk-UFAGYX73.js.map +1 -0
  74. package/dist/{chunk-DJEZ2HVU.js → chunk-VDMYBPYB.js} +1 -1
  75. package/dist/chunk-VDMYBPYB.js.map +1 -0
  76. package/dist/chunk-VTE7L6LO.js +5 -0
  77. package/dist/{chunk-G57RZCXM.js.map → chunk-VTE7L6LO.js.map} +1 -1
  78. package/dist/chunk-W5C2GDNC.js +32 -0
  79. package/dist/{chunk-JP5IEXR7.js.map → chunk-W5C2GDNC.js.map} +1 -1
  80. package/dist/chunk-WPMX3VGI.js +3 -0
  81. package/dist/chunk-WPMX3VGI.js.map +1 -0
  82. package/dist/chunk-WZ72HX5C.js +1182 -0
  83. package/dist/chunk-WZ72HX5C.js.map +1 -0
  84. package/dist/chunk-ZTEU5JVQ.js +2 -0
  85. package/dist/chunk-ZTEU5JVQ.js.map +1 -0
  86. package/dist/{chunk-2WVX3LEC.js → chunk-ZVP6GB4G.js} +1 -1
  87. package/dist/chunk-ZVP6GB4G.js.map +1 -0
  88. package/dist/cli.js +1 -1
  89. package/dist/cli.js.map +1 -1
  90. package/dist/client-PX25MD47.js +2 -0
  91. package/dist/{compaction-NWEMOSHA.js → compaction-FD4R23HZ.js} +4 -4
  92. package/dist/config-HL5F36V4.js +2 -0
  93. package/dist/config-mutator-HBX53OMJ.js +2 -0
  94. package/dist/{conflict-detector-D7VIYPDI.js → conflict-detector-ZPOT3M2C.js} +2 -2
  95. package/dist/conflict-detector-ZPOT3M2C.js.map +1 -0
  96. package/dist/context-planner-AQHJMVY2.js +17 -0
  97. package/dist/context-planner-AQHJMVY2.js.map +1 -0
  98. package/dist/debug-3MEUO2P2.js +2 -0
  99. package/dist/{discover-XJXQXXIO.js → discover-D4VVRNAI.js} +2 -2
  100. package/dist/discover-D4VVRNAI.js.map +1 -0
  101. package/dist/doctor-PBAH6XL3.js +2 -0
  102. package/dist/editor-chat-OB7IQOB5.js +137 -0
  103. package/dist/editor-chat-OB7IQOB5.js.map +1 -0
  104. package/dist/entry-triage-LK34TSQD.js +2 -0
  105. package/dist/entry-triage-LK34TSQD.js.map +1 -0
  106. package/dist/facts-YVEJOM7O.js +22 -0
  107. package/dist/facts-YVEJOM7O.js.map +1 -0
  108. package/dist/heal-FVLBHYP7.js +2 -0
  109. package/dist/index.d.ts +1073 -65
  110. package/dist/index.js +13 -13
  111. package/dist/index.js.map +1 -1
  112. package/dist/{ingest-whatsapp-LZE62INA.js → ingest-whatsapp-NS4OWDOU.js} +2 -2
  113. package/dist/loader-4ZGTK7LD.js +2 -0
  114. package/dist/loader-BOKE5M5S.js +2 -0
  115. package/dist/memory-extract-F5ZHWY5E.js +2 -0
  116. package/dist/{migrate-v2-QO7SLKIM.js → migrate-v2-V4WN2VVM.js} +2 -2
  117. package/dist/ntfy-X7EPB3TG.js +4 -0
  118. package/dist/ntfy-X7EPB3TG.js.map +1 -0
  119. package/dist/processes-WU23U2OJ.js +2 -0
  120. package/dist/program-YGXVCV2Y.js +2 -0
  121. package/dist/{projects-api-KRME2YKZ.js → projects-api-QE6L65SK.js} +2 -2
  122. package/dist/{projects-mutate-3NHVG437.js → projects-mutate-MY7JTLSZ.js} +2 -2
  123. package/dist/providers-KO2DODIO.js +2 -0
  124. package/dist/query-ZKTFJMNE.js +2 -0
  125. package/dist/questions-4B4KSPXG.js +3 -0
  126. package/dist/questions-4B4KSPXG.js.map +1 -0
  127. package/dist/recipes-7S4MC5OJ.js +2 -0
  128. package/dist/registry-PSZDPFZK.js +2 -0
  129. package/dist/review-candidates-AEUXRQQC.js +4 -0
  130. package/dist/review-candidates-AEUXRQQC.js.map +1 -0
  131. package/dist/review-store-B4YE3EFK.js +4 -0
  132. package/dist/review-store-B4YE3EFK.js.map +1 -0
  133. package/dist/{rotation-memo-B3EXXSEH.js → rotation-memo-7T75BLEX.js} +2 -2
  134. package/dist/routing-4PV66BK6.js +2 -0
  135. package/dist/routing-4PV66BK6.js.map +1 -0
  136. package/dist/runner-YRBVNYTY.js +2 -0
  137. package/dist/scheduler-GZZAN6ZG.js +2 -0
  138. package/dist/seat-VZ7AVO27.js +2 -0
  139. package/dist/select-F5I5PO2O.js +2 -0
  140. package/dist/select-F5I5PO2O.js.map +1 -0
  141. package/dist/store-MCOA6N4F.js +2 -0
  142. package/dist/{subagent-WV7QKQ3L.js → subagent-2SJHSSJS.js} +2 -2
  143. package/dist/{surface-usage-PRL6Z4U3.js → surface-usage-NS74AXSE.js} +2 -2
  144. package/dist/timers-PCB4ZIBD.js +2 -0
  145. package/dist/triage-backtest-JNO7RKX7.js +2 -0
  146. package/dist/triage-backtest-JNO7RKX7.js.map +1 -0
  147. package/dist/types-H5ENIUIT.js +2 -0
  148. package/dist/types-OIFU3KSQ.js +2 -0
  149. package/dist/web/workflow-editor.global.js +1542 -0
  150. package/dist/web/workflow-editor.global.js.map +1 -0
  151. package/dist/{whatsapp-state-XMTCSRXU.js → whatsapp-state-6D6FHHD2.js} +2 -2
  152. package/dist/wiki-N5SMP3DT.js +2 -0
  153. package/dist/wiki-N5SMP3DT.js.map +1 -0
  154. package/dist/workflows/templates/branching.yaml +1 -1
  155. package/dist/{workspace-setup-TBQAHYHJ.js → workspace-setup-B3F3TUDE.js} +2 -2
  156. package/dist/workspace-setup-B3F3TUDE.js.map +1 -0
  157. package/package.json +1 -1
  158. package/dist/agent-QGZIAZMY.js +0 -2
  159. package/dist/architect-SEPWMWS4.js +0 -2
  160. package/dist/board-dashboard-CQ5F4GPD.js.map +0 -1
  161. package/dist/chunk-2WVX3LEC.js.map +0 -1
  162. package/dist/chunk-3WQFCIB6.js +0 -24
  163. package/dist/chunk-3WQFCIB6.js.map +0 -1
  164. package/dist/chunk-4YCH6IZV.js +0 -2
  165. package/dist/chunk-53GCFZPR.js +0 -116
  166. package/dist/chunk-53GCFZPR.js.map +0 -1
  167. package/dist/chunk-53NRX2NA.js +0 -2
  168. package/dist/chunk-53NRX2NA.js.map +0 -1
  169. package/dist/chunk-5U35WAZF.js.map +0 -1
  170. package/dist/chunk-BFXONUUN.js +0 -108
  171. package/dist/chunk-BFXONUUN.js.map +0 -1
  172. package/dist/chunk-BFZUG66X.js +0 -45
  173. package/dist/chunk-BFZUG66X.js.map +0 -1
  174. package/dist/chunk-DJEZ2HVU.js.map +0 -1
  175. package/dist/chunk-FDMWOPEL.js +0 -5
  176. package/dist/chunk-FDWOECJY.js +0 -54
  177. package/dist/chunk-FDWOECJY.js.map +0 -1
  178. package/dist/chunk-G57RZCXM.js +0 -5
  179. package/dist/chunk-H5D7ATXS.js.map +0 -1
  180. package/dist/chunk-HA3XI7Z2.js +0 -155
  181. package/dist/chunk-HA3XI7Z2.js.map +0 -1
  182. package/dist/chunk-HR4JSRZS.js +0 -3
  183. package/dist/chunk-HR4JSRZS.js.map +0 -1
  184. package/dist/chunk-ISEBLHWX.js +0 -118
  185. package/dist/chunk-ISEBLHWX.js.map +0 -1
  186. package/dist/chunk-JP5IEXR7.js +0 -32
  187. package/dist/chunk-JTJIPNXY.js.map +0 -1
  188. package/dist/chunk-JXWAGMI2.js +0 -13
  189. package/dist/chunk-JXWAGMI2.js.map +0 -1
  190. package/dist/chunk-KUCCW2VG.js +0 -431
  191. package/dist/chunk-KUCCW2VG.js.map +0 -1
  192. package/dist/chunk-LDHPMJ7M.js.map +0 -1
  193. package/dist/chunk-LNXCJWOU.js.map +0 -1
  194. package/dist/chunk-NGSIRE4X.js +0 -126
  195. package/dist/chunk-NGSIRE4X.js.map +0 -1
  196. package/dist/chunk-P4VWIVKN.js.map +0 -1
  197. package/dist/chunk-Q6YO7LYE.js +0 -47
  198. package/dist/chunk-Q6YO7LYE.js.map +0 -1
  199. package/dist/chunk-SR3X25TN.js +0 -3
  200. package/dist/chunk-SR3X25TN.js.map +0 -1
  201. package/dist/chunk-TMKLHTCG.js +0 -971
  202. package/dist/chunk-TMKLHTCG.js.map +0 -1
  203. package/dist/chunk-VJUTQAPJ.js +0 -2
  204. package/dist/chunk-VJUTQAPJ.js.map +0 -1
  205. package/dist/client-GGVVLOOF.js +0 -2
  206. package/dist/config-7FSZFRTQ.js +0 -2
  207. package/dist/config-mutator-POCOYYJ4.js +0 -2
  208. package/dist/conflict-detector-D7VIYPDI.js.map +0 -1
  209. package/dist/context-planner-45GEWKGL.js +0 -17
  210. package/dist/context-planner-45GEWKGL.js.map +0 -1
  211. package/dist/debug-N3B5NVJU.js +0 -2
  212. package/dist/discover-XJXQXXIO.js.map +0 -1
  213. package/dist/doctor-DHLBEKA4.js +0 -2
  214. package/dist/heal-HZ3EQD52.js +0 -2
  215. package/dist/loader-5CA3O5GM.js +0 -2
  216. package/dist/loader-6IHUCPVO.js +0 -2
  217. package/dist/memory-extract-AENFVVO5.js +0 -2
  218. package/dist/processes-UIC4XXA6.js +0 -2
  219. package/dist/program-WVAVJFND.js +0 -2
  220. package/dist/providers-L27BJ5A6.js +0 -2
  221. package/dist/query-HZYYRCLU.js +0 -2
  222. package/dist/recipes-EPLE2SNT.js +0 -2
  223. package/dist/registry-T36ZIAWV.js +0 -2
  224. package/dist/runner-BFYDWPUO.js +0 -2
  225. package/dist/scheduler-GH7TIKYS.js +0 -2
  226. package/dist/store-I3HEWVLQ.js +0 -2
  227. package/dist/timers-5LPZN55J.js +0 -2
  228. package/dist/types-VECTMYTO.js +0 -2
  229. package/dist/types-Y6ZCZ52Z.js +0 -2
  230. package/dist/wiki-M6YNRFC4.js +0 -2
  231. /package/dist/{App-6Y7B3YQH.js.map → App-U52IPUK2.js.map} +0 -0
  232. /package/dist/{ChatApp-KIEUJ2DS.js.map → ChatApp-XP4OMHPL.js.map} +0 -0
  233. /package/dist/{agent-QGZIAZMY.js.map → agent-V2CA4TXD.js.map} +0 -0
  234. /package/dist/{architect-SEPWMWS4.js.map → article-fields-L4D2BOGY.js.map} +0 -0
  235. /package/dist/{audit-JU3HWISB.js.map → audit-4245CX6P.js.map} +0 -0
  236. /package/dist/{builtin-FRKAQMFS.js.map → builtin-B67DMJ3M.js.map} +0 -0
  237. /package/dist/{chunk-4YCH6IZV.js.map → calibration-ILS5OI44.js.map} +0 -0
  238. /package/dist/{client-GGVVLOOF.js.map → chunk-FIOB3MEX.js.map} +0 -0
  239. /package/dist/{config-7FSZFRTQ.js.map → client-PX25MD47.js.map} +0 -0
  240. /package/dist/{compaction-NWEMOSHA.js.map → compaction-FD4R23HZ.js.map} +0 -0
  241. /package/dist/{config-mutator-POCOYYJ4.js.map → config-HL5F36V4.js.map} +0 -0
  242. /package/dist/{debug-N3B5NVJU.js.map → config-mutator-HBX53OMJ.js.map} +0 -0
  243. /package/dist/{doctor-DHLBEKA4.js.map → debug-3MEUO2P2.js.map} +0 -0
  244. /package/dist/{heal-HZ3EQD52.js.map → doctor-PBAH6XL3.js.map} +0 -0
  245. /package/dist/{loader-5CA3O5GM.js.map → heal-FVLBHYP7.js.map} +0 -0
  246. /package/dist/{ingest-whatsapp-LZE62INA.js.map → ingest-whatsapp-NS4OWDOU.js.map} +0 -0
  247. /package/dist/{loader-6IHUCPVO.js.map → loader-4ZGTK7LD.js.map} +0 -0
  248. /package/dist/{memory-extract-AENFVVO5.js.map → loader-BOKE5M5S.js.map} +0 -0
  249. /package/dist/{processes-UIC4XXA6.js.map → memory-extract-F5ZHWY5E.js.map} +0 -0
  250. /package/dist/{migrate-v2-QO7SLKIM.js.map → migrate-v2-V4WN2VVM.js.map} +0 -0
  251. /package/dist/{program-WVAVJFND.js.map → processes-WU23U2OJ.js.map} +0 -0
  252. /package/dist/{providers-L27BJ5A6.js.map → program-YGXVCV2Y.js.map} +0 -0
  253. /package/dist/{projects-api-KRME2YKZ.js.map → projects-api-QE6L65SK.js.map} +0 -0
  254. /package/dist/{projects-mutate-3NHVG437.js.map → projects-mutate-MY7JTLSZ.js.map} +0 -0
  255. /package/dist/{query-HZYYRCLU.js.map → providers-KO2DODIO.js.map} +0 -0
  256. /package/dist/{recipes-EPLE2SNT.js.map → query-ZKTFJMNE.js.map} +0 -0
  257. /package/dist/{registry-T36ZIAWV.js.map → recipes-7S4MC5OJ.js.map} +0 -0
  258. /package/dist/{runner-BFYDWPUO.js.map → registry-PSZDPFZK.js.map} +0 -0
  259. /package/dist/{rotation-memo-B3EXXSEH.js.map → rotation-memo-7T75BLEX.js.map} +0 -0
  260. /package/dist/{scheduler-GH7TIKYS.js.map → runner-YRBVNYTY.js.map} +0 -0
  261. /package/dist/{store-I3HEWVLQ.js.map → scheduler-GZZAN6ZG.js.map} +0 -0
  262. /package/dist/{surface-usage-PRL6Z4U3.js.map → seat-VZ7AVO27.js.map} +0 -0
  263. /package/dist/{timers-5LPZN55J.js.map → store-MCOA6N4F.js.map} +0 -0
  264. /package/dist/{subagent-WV7QKQ3L.js.map → subagent-2SJHSSJS.js.map} +0 -0
  265. /package/dist/{types-VECTMYTO.js.map → surface-usage-NS74AXSE.js.map} +0 -0
  266. /package/dist/{types-Y6ZCZ52Z.js.map → timers-PCB4ZIBD.js.map} +0 -0
  267. /package/dist/{wiki-M6YNRFC4.js.map → types-H5ENIUIT.js.map} +0 -0
  268. /package/dist/{workspace-setup-TBQAHYHJ.js.map → types-OIFU3KSQ.js.map} +0 -0
  269. /package/dist/{whatsapp-state-XMTCSRXU.js.map → whatsapp-state-6D6FHHD2.js.map} +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/daemon/ui/tokens.ts","../src/daemon/ui/assistant.ts","../src/daemon/ui/inject.ts","../src/utils/markdown-html.ts","../src/daemon/ui/components.css.ts","../src/daemon/ui/shell.ts","../src/daemon/ui/util.ts","../src/daemon/ui/components.ts"],"sourcesContent":["// --- Design tokens for every dashboard surface ---\n//\n// SINGLE source of truth. Every page imports AX_TOKENS_CSS and drops it in\n// its <style> block once, so no page carries its own :root copy.\n//\n// The palette and shape language are ported from the agentina console\n// (packages/console/src/index.ts) — a deliberately plain, friendly system\n// built on three principles worth restating here, because they are as much\n// about what we remove as how it looks:\n//\n// 1. CONTACTS, NOT CONCEPTS — surface the people and agents you work with;\n// grants, sessions and adapters stay machinery.\n// 2. ONE SCREEN, ONE JOB — a flow is a short wizard with one obvious next\n// action, not a dense panel of every option.\n// 3. HONEST UI — nothing simulated. Offline is offline, denied is denied.\n//\n// What was ported and what was not:\n// - Ported: the four-colour brand palette, 2px borders, generous radii,\n// pill chips, the hard offset shadow (`0 4px 0 <darker>`) that makes\n// controls read as physical, and Outfit / Roboto Mono.\n// - NOT ported: light-only. agentina is a consumer console; this is a\n// dense ops dashboard people read at 2am, so the dark variant stays and\n// carries the same shape language with the palette lifted for contrast.\n// - Removed: the `crt` theme. Two well-maintained themes beat three, and\n// it had no users.\n//\n// Guideline for adding tokens: every token must be page-neutral. Page-\n// specific padding/gaps belong in the page's own CSS file, not here.\n\nexport const AX_TOKENS_CSS = `:root {\n /* --- Brand (agentina) --------------------------------------------- */\n --ax-blue: #2979FF;\n --ax-blue-d: #1B5FD9;\n --ax-blue-t: #E7F0FF;\n --ax-blue-e: #A9CBFF;\n --ax-green: #22B573;\n --ax-green-d: #178F58;\n --ax-green-e: #A9E8C9;\n --ax-amber: #FFB300;\n --ax-amber-e: #FFE08A;\n --ax-amber-t: #FFFCF5;\n --ax-red: #F23A3A;\n --ax-red-t: #FEECEC;\n --ax-red-e: #F9C1C1;\n\n /* Ink steps. Amber and red are legible as FILLS, not as small text:\n #FFB300 on white is ~1.8:1 and #F23A3A ~3.6:1, both under 4.5:1. Use\n these wherever the colour carries words rather than area. */\n --ax-amber-ink: #8a5a00;\n --ax-red-ink: #a52020;\n\n /* --- Surfaces + text ---------------------------------------------- */\n --ax-bg: #f8f9fa;\n --ax-bg-elev: #f1f3f4;\n --ax-surface: #ffffff;\n --ax-surface-2: #f8f9fa;\n --ax-surface-3: #f1f3f4;\n --ax-border: #e8eaed;\n --ax-border-2: #dadce0;\n --ax-text: #202124;\n --ax-text-2: #5f6368;\n --ax-muted: #9aa0a6;\n\n /* --- Semantic ------------------------------------------------------ */\n --ax-accent: var(--ax-blue);\n --ax-accent-2: var(--ax-blue-d);\n --ax-accent-t: var(--ax-blue-t);\n --ax-ok: var(--ax-green);\n --ax-warn: var(--ax-amber);\n --ax-err: var(--ax-red);\n --ax-info: var(--ax-blue);\n\n /* --- Shape ---------------------------------------------------------\n The offset shadow is the signature move: a solid, un-blurred drop in a\n darker shade of the element's own colour. It reads as a physical edge\n rather than a glow, and it is what stops flat 2px-bordered boxes from\n looking like a wireframe. */\n --ax-border-w: 2px;\n --ax-radius: 16px;\n --ax-radius-lg: 20px;\n --ax-radius-sm: 12px;\n --ax-radius-pill: 999px;\n --ax-shadow: 0 3px 0 var(--ax-border);\n --ax-shadow-lg: 0 4px 0 var(--ax-border-2);\n --ax-shadow-accent: 0 4px 0 var(--ax-blue-d);\n --ax-shadow-ok: 0 4px 0 var(--ax-green-d);\n\n /* --- Spacing ------------------------------------------------------- */\n /* Width of the ask-an-agent drawer. Lives here because pages reflow\n around it; the drawer overwrites it on :root when resized. */\n --ax-as-w: 420px;\n --ax-pad: 16px;\n --ax-pad-sm: 10px;\n --ax-gap: 12px;\n\n /* --- Type ---------------------------------------------------------- */\n --ax-font: \"Outfit\", -apple-system, \"Segoe UI\", system-ui, sans-serif;\n --ax-mono: \"Roboto Mono\", ui-monospace, \"SF Mono\", Consolas, monospace;\n --ax-fs: 14px;\n --ax-fs-sm: 13px;\n --ax-fs-xs: 12px;\n\n /* Legacy aliases so older CSS blocks keep rendering until they're ported. */\n --bg: var(--ax-bg);\n --card: var(--ax-surface);\n --node: var(--ax-bg-elev);\n --border: var(--ax-border);\n --text: var(--ax-text);\n --muted: var(--ax-muted);\n --accent: var(--ax-accent);\n --yellow: var(--ax-warn);\n --red: var(--ax-err);\n --blue: var(--ax-info);\n --gray: var(--ax-muted);\n color-scheme: light;\n}\n\n/* Dark is not a tint of light — the brand colours are lifted toward their\n tints so they keep contrast against a dark ground, and the offset shadow\n goes DARKER than the surface instead of lighter. Same shape language,\n inverted physics. */\n[data-theme=\"dark\"] {\n --ax-blue: #6BA5FF;\n --ax-blue-d: #1B5FD9;\n --ax-blue-t: #16233d;\n --ax-blue-e: #2f4d7a;\n --ax-green: #4ED89B;\n --ax-green-d: #178F58;\n --ax-green-e: #1e4a36;\n --ax-amber: #FFC948;\n --ax-amber-e: #4a3a12;\n --ax-amber-t: #2a2313;\n --ax-red: #FF6B6B;\n --ax-red-t: #3a1c1c;\n --ax-red-e: #6b2f2f;\n --ax-amber-ink: #FFC948;\n --ax-red-ink: #FF8A8A;\n\n --ax-bg: #14161a;\n --ax-bg-elev: #191c21;\n --ax-surface: #1e2228;\n --ax-surface-2: #23272e;\n --ax-surface-3: #2a2f37;\n --ax-border: #2f353e;\n --ax-border-2: #3d444f;\n --ax-text: #f1f3f4;\n --ax-text-2: #b6bcc4;\n --ax-muted: #858c96;\n\n --ax-shadow: 0 3px 0 #0e1013;\n --ax-shadow-lg: 0 4px 0 #0e1013;\n --ax-shadow-accent: 0 4px 0 #14396e;\n --ax-shadow-ok: 0 4px 0 #11402c;\n color-scheme: dark;\n}\n\n/* Base element resets shared across every page. */\n* { box-sizing: border-box; }\nhtml, body {\n margin: 0; min-height: 100vh;\n background: var(--ax-bg); color: var(--ax-text);\n font-family: var(--ax-font); font-size: var(--ax-fs);\n -webkit-font-smoothing: antialiased;\n}\npre, code, .ax-mono {\n font-family: var(--ax-mono); font-variant-numeric: tabular-nums;\n letter-spacing: -0.01em;\n}\na { color: var(--ax-accent); text-decoration: none; font-weight: 600; }\na:hover { color: var(--ax-accent-2); text-decoration: underline; }\n.ax-muted { color: var(--ax-muted); }\n.ax-accent { color: var(--ax-accent); }`\n","// --- Ask an agent, from wherever you are ----------------------------------\n//\n// A handle on the right edge of every dashboard page opens a drawer that\n// dispatches to an agent on any node in the mesh. What makes it worth having\n// over a terminal is context: each page publishes what it is currently\n// showing via `window.axPageContext`, and that travels with the message. So\n// \"why is this stuck?\" on the Monitor means the client filter, the bucket\n// counts and the visible actions — not a question the agent has to\n// interrogate you for.\n//\n// Pages opt in by assigning window.axPageContext = () => ({ ... }). Pages\n// that do not still get the path and tab.\n\nexport const ASSISTANT_CSS = `\n.ax-as-dock{position:fixed;bottom:0;left:50%;transform:translateX(-50%);z-index:59;\n display:flex;flex-direction:column;align-items:center;\n width:min(620px,calc(100vw - 36px));transition:transform 160ms ease}\n/* Centred in the space that is left, not in the space the drawer covers. */\nbody.ax-as-open .ax-as-dock{left:calc((100vw - var(--ax-as-w)) / 2)}\n/* Tucked: slide out of the way but leave the grip, or there is nothing to\n click to bring it back. */\n.ax-as-dock.is-tucked{transform:translateX(-50%) translateY(calc(100% - 26px))}\nbody.ax-as-open .ax-as-dock.is-tucked{transform:translateX(-50%) translateY(calc(100% - 26px))}\n@media (prefers-reduced-motion:reduce){.ax-as-dock{transition:none}}\n.ax-as-dock__grip{width:56px;height:20px;margin-bottom:-2px;cursor:pointer;\n background:var(--ax-surface);border:var(--ax-border-w) solid var(--ax-border);\n border-bottom:none;border-radius:var(--ax-radius) var(--ax-radius) 0 0;padding:0;\n display:flex;align-items:center;justify-content:center}\n.ax-as-dock__grip::before{content:\"\";width:22px;height:3px;border-radius:2px;\n background:var(--ax-border-2)}\n.ax-as-dock__grip:hover::before{background:var(--ax-accent)}\n.ax-as-bar{display:flex;gap:8px;align-items:center;width:100%;padding:8px;\n background:var(--ax-surface);border:var(--ax-border-w) solid var(--ax-border);\n border-radius:var(--ax-radius-lg) var(--ax-radius-lg) 0 0;border-bottom:none;\n box-shadow:var(--ax-shadow-lg)}\n.ax-as-bar input{flex:1;min-width:0;font:inherit;font-size:13px;padding:7px 10px;\n border-radius:var(--ax-radius-sm);border:var(--ax-border-w) solid var(--ax-border-2);\n background:var(--ax-bg);color:var(--ax-text)}\n.ax-as-bar button{flex:none}\n/* Keep the last row of a page clear of the dock. */\nbody{padding-bottom:86px}\n@media (max-width:640px){.ax-as-dock{width:calc(100vw - 24px)}\n body.ax-as-open .ax-as-dock{left:50%}}\n.ax-as-handle{position:fixed;right:0;top:50%;transform:translateY(-50%);z-index:60;\n display:flex;align-items:center;gap:6px;writing-mode:vertical-rl;\n background:var(--ax-surface);color:var(--ax-text);cursor:pointer;\n border:var(--ax-border-w) solid var(--ax-border);border-right:none;\n border-radius:var(--ax-radius) 0 0 var(--ax-radius);padding:14px 7px;\n font:inherit;font-size:12px;font-weight:600;box-shadow:var(--ax-shadow)}\n.ax-as-handle:hover{background:var(--ax-surface-2)}\n/* Open pushes the page over rather than covering it: a drawer that hides the\n rows you are asking about is worse than no drawer. Below 900px there is no\n room to give, so it overlays. */\nbody.ax-as-open{padding-right:var(--ax-as-w)}\n@media (max-width:900px){body.ax-as-open{padding-right:0}}\n.ax-as{position:fixed;top:0;right:0;height:100vh;width:var(--ax-as-w);max-width:92vw;z-index:61;\n display:flex;flex-direction:column;background:var(--ax-surface);\n border-left:var(--ax-border-w) solid var(--ax-border);\n transform:translateX(100%);transition:transform 160ms ease}\n.ax-as.is-open{transform:translateX(0)}\n@media (prefers-reduced-motion:reduce){.ax-as{transition:none}}\n.ax-as__grip{position:absolute;left:0;top:0;bottom:0;width:6px;cursor:col-resize;\n background:transparent}\n.ax-as__grip:hover,.ax-as__grip:focus-visible{background:var(--ax-accent)}\n.ax-as__head{display:flex;align-items:center;gap:8px;padding:12px 14px;\n border-bottom:var(--ax-border-w) solid var(--ax-border)}\n.ax-as__title{font-size:13px;font-weight:600;flex:1}\n.ax-as__x{background:none;border:none;color:var(--ax-text-2);cursor:pointer;font:inherit;\n font-size:18px;line-height:1;padding:0 4px}\n.ax-as__icon{background:var(--ax-surface-2);border:1px solid var(--ax-border-2);color:var(--ax-text-2);\n cursor:pointer;font:inherit;font-size:11.5px;font-weight:600;padding:3px 9px;\n border-radius:var(--ax-radius-pill)}\n.ax-as__icon:hover{color:var(--ax-text);background:var(--ax-surface-3)}\n.ax-as__icon.is-on{background:var(--ax-blue-t);border-color:var(--ax-blue-e);color:var(--ax-blue-d)}\n.ax-as__hist{border-bottom:var(--ax-border-w) solid var(--ax-border);max-height:44vh;overflow:auto}\n.ax-as__hist-empty{padding:12px 14px;font-size:12px;color:var(--ax-text-2)}\n.ax-as__hist-list{list-style:none;margin:0;padding:0}\n.ax-as__hist-list li{display:flex;align-items:center;gap:8px;padding:9px 14px;\n border-bottom:1px solid var(--ax-border);cursor:pointer}\n.ax-as__hist-list li:hover{background:var(--ax-surface-2)}\n.ax-as__hist-list li.is-on{background:var(--ax-blue-t)}\n.ax-as__hist-t{flex:1;min-width:0;font-size:12.5px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n.ax-as__hist-m{font-family:var(--ax-mono);font-size:10.5px;color:var(--ax-text-2);flex:none}\n.ax-as__hist-x{background:none;border:none;color:var(--ax-text-2);cursor:pointer;font-size:15px;\n line-height:1;padding:0 2px;flex:none}\n.ax-as__pending{opacity:0.75}\n/* Rendered markdown inside a bubble: tight enough not to look like a page. */\n.ax-as__md{white-space:normal}\n.ax-as__md p{margin:0 0 8px}\n.ax-as__md p:last-child{margin-bottom:0}\n.ax-as__md ul,.ax-as__md ol{margin:0 0 8px;padding-left:20px}\n.ax-as__md li{margin:2px 0}\n.ax-as__md code{font-family:var(--ax-mono);font-size:11.5px;background:var(--ax-surface-3);\n border:1px solid var(--ax-border);border-radius:4px;padding:0 4px}\n.ax-as__md pre{margin:0 0 8px;padding:9px 11px;overflow-x:auto;background:var(--ax-surface-3);\n border:1px solid var(--ax-border);border-radius:var(--ax-radius-sm)}\n.ax-as__md pre code{background:none;border:none;padding:0;font-size:11.5px}\n.ax-as__md h1,.ax-as__md h2,.ax-as__md h3,.ax-as__md h4{font-size:13px;margin:10px 0 6px}\n.ax-as__md blockquote{margin:0 0 8px;padding-left:10px;border-left:3px solid var(--ax-border-2);\n color:var(--ax-text-2)}\n.ax-as__md hr{border:none;border-top:1px solid var(--ax-border);margin:10px 0}\n/* Tables get their own scroller so a wide one cannot stretch the drawer. */\n.ax-as__md table{display:block;overflow-x:auto;border-collapse:collapse;margin:0 0 8px;font-size:12px}\n.ax-as__md th,.ax-as__md td{border:1px solid var(--ax-border);padding:4px 8px;text-align:left;\n white-space:nowrap}\n.ax-as__md th{background:var(--ax-surface-3);font-weight:600}\n.ax-as__pick{display:flex;gap:8px;padding:10px 14px;border-bottom:1px solid var(--ax-border)}\n.ax-as__pick select{flex:1;min-width:0;font:inherit;font-size:12px;padding:5px 8px;\n border-radius:var(--ax-radius-sm);border:var(--ax-border-w) solid var(--ax-border-2);\n background:var(--ax-bg);color:var(--ax-text)}\n.ax-as__ctx{padding:8px 14px;font-size:11.5px;color:var(--ax-text-2);\n border-bottom:1px solid var(--ax-border);display:flex;align-items:center;gap:7px;flex-wrap:wrap}\n.ax-as__ctx code{font-family:var(--ax-mono);font-size:11px}\n.ax-as__log{flex:1;overflow:auto;padding:12px 14px;display:flex;flex-direction:column;gap:10px}\n.ax-as__msg{font-size:13px;line-height:1.5;white-space:pre-wrap;overflow-wrap:anywhere;\n padding:9px 12px;border-radius:var(--ax-radius);border:var(--ax-border-w) solid var(--ax-border)}\n.ax-as__msg--me{background:var(--ax-blue-t);border-color:var(--ax-blue-e);align-self:flex-end;max-width:88%}\n.ax-as__msg--them{background:var(--ax-surface-2)}\n.ax-as__msg--err{background:var(--ax-red-t);border-color:var(--ax-red-e);color:var(--ax-red-ink)}\n.ax-as__empty{color:var(--ax-text-2);font-size:12.5px;line-height:1.5}\n.ax-as__foot{padding:10px 14px;border-top:var(--ax-border-w) solid var(--ax-border);\n display:flex;gap:8px;align-items:flex-end}\n.ax-as__foot textarea{flex:1;resize:none;font:inherit;font-size:13px;padding:8px 10px;\n min-height:38px;max-height:140px;border-radius:var(--ax-radius-sm);\n border:var(--ax-border-w) solid var(--ax-border-2);background:var(--ax-bg);color:var(--ax-text)}\n`\n\nexport const ASSISTANT_HTML = `\n<div class=\"ax-as-dock\" id=\"ax-as-dock\">\n <button class=\"ax-as-dock__grip\" id=\"ax-as-bar-toggle\" type=\"button\"\n aria-expanded=\"true\" aria-controls=\"ax-as-bar\" title=\"Hide the composer\"></button>\n <form class=\"ax-as-bar\" id=\"ax-as-bar\">\n <input id=\"ax-as-bar-input\" autocomplete=\"off\" placeholder=\"Ask an agent about this page&hellip;\" aria-label=\"Ask an agent about this page\">\n <button class=\"ax-btn ax-btn--primary ax-btn--sm\" type=\"submit\" id=\"ax-as-bar-send\">Ask</button>\n </form>\n</div>\n<button class=\"ax-as-handle\" id=\"ax-as-handle\" aria-expanded=\"false\" aria-controls=\"ax-as\">Ask an agent</button>\n<aside class=\"ax-as\" id=\"ax-as\" aria-label=\"Ask an agent\" aria-hidden=\"true\">\n <div class=\"ax-as__grip\" id=\"ax-as-grip\" role=\"separator\" aria-orientation=\"vertical\"\n tabindex=\"0\" aria-label=\"Resize panel — arrow keys adjust\"></div>\n <div class=\"ax-as__head\">\n <span class=\"ax-as__title\" id=\"ax-as-title\">Ask an agent</span>\n <button class=\"ax-as__icon\" id=\"ax-as-hist\" title=\"Past conversations\">History</button>\n <button class=\"ax-as__icon\" id=\"ax-as-new\" title=\"Start a new conversation\">New</button>\n <button class=\"ax-as__x\" id=\"ax-as-close\" title=\"Close\">&times;</button>\n </div>\n <div class=\"ax-as__hist\" id=\"ax-as-hist-panel\" hidden>\n <div class=\"ax-as__hist-empty\" id=\"ax-as-hist-empty\">No past conversations yet.</div>\n <ul class=\"ax-as__hist-list\" id=\"ax-as-hist-list\"></ul>\n </div>\n <div class=\"ax-as__pick\">\n <select id=\"ax-as-agent\" aria-label=\"Agent\"><option value=\"\">Loading agents&hellip;</option></select>\n <select id=\"ax-as-node\" aria-label=\"Node\"><option value=\"\">This node</option></select>\n </div>\n <div class=\"ax-as__ctx\" id=\"ax-as-ctx\"></div>\n <div class=\"ax-as__log\" id=\"ax-as-log\">\n <p class=\"ax-as__empty\">Ask about what is on this page. What you are looking at &mdash; the page, its filters and the rows in view &mdash; goes with the question.</p>\n </div>\n</aside>`\n\nexport const ASSISTANT_SCRIPT = String.raw`\n(function(){\nconst $=id=>document.getElementById(id);\nconst panel=$('ax-as'), handle=$('ax-as-handle');\nif(!panel||!handle)return;\nconst esc=s=>String(s==null?'':s).replace(/[&<>\"']/g,c=>({'&':'&amp;','<':'&lt;','>':'&gt;','\"':'&quot;',\"'\":'&#39;'}[c]));\nlet busy=false;\n\nconst WMIN=320,WMAX=760;\nfunction setWidth(px){\nconst w=Math.max(WMIN,Math.min(WMAX,Math.round(px)));\ndocument.documentElement.style.setProperty('--ax-as-w',w+'px');\ntry{localStorage.setItem('ax-assistant-w',String(w))}catch{}\n}\ntry{const w=Number(localStorage.getItem('ax-assistant-w'));if(w)setWidth(w)}catch{}\n\n/* '0' means the operator closed it on purpose; null means they simply have\n not opened it yet, and auto-open is still welcome. */\nfunction userHid(){try{return localStorage.getItem('ax-assistant-open')==='0'}catch{return false}}\nfunction open(v){\ndocument.body.classList.toggle('ax-as-open',v);\npanel.classList.toggle('is-open',v);\npanel.setAttribute('aria-hidden',String(!v));\nhandle.setAttribute('aria-expanded',String(v));\ntry{localStorage.setItem('ax-assistant-open',v?'1':'0')}catch{}\nif(v)paintCtx();\n}\nhandle.onclick=()=>open(!panel.classList.contains('is-open'));\n$('ax-as-close').onclick=()=>open(false);\ndocument.addEventListener('keydown',e=>{if(e.key==='Escape'&&panel.classList.contains('is-open'))open(false);});\ntry{if(localStorage.getItem('ax-assistant-open')==='1')open(true)}catch{}\n\n/* What the agent is told about where you are. Pages publish their own slice;\n everything else still gets the path, so the question is never contextless. */\nfunction pageContext(){\nconst base={path:location.pathname,tab:document.querySelector('.ax-topbar__tab.is-active')?.textContent?.trim()||null};\ntry{ if(typeof window.axPageContext==='function') return {...base,...(window.axPageContext()||{})}; }catch(e){}\nreturn base;\n}\nfunction paintCtx(){\nconst c=pageContext();\n// A page that names itself makes the raw path noise, and tab just\n// repeats the nav — so drop both when the page supplies its own name.\nconst shownCtx={...c};\nif(shownCtx.page){delete shownCtx.path;delete shownCtx.tab;}\nconst bits=Object.entries(shownCtx).filter(([k,v])=>v!=null&&v!=='').slice(0,4)\n .map(([k,v])=>'<code>'+esc(k)+'='+esc(typeof v==='object'?(Array.isArray(v)?v.length+' items':JSON.stringify(v).slice(0,28)):v)+'</code>');\n$('ax-as-ctx').innerHTML='Sending with your question: '+(bits.join(' ')||'<code>this page</code>');\n}\n\n/* A one-off line in the log, for errors raised before a thread exists. */\nfunction add(kind,text){\nconst log=$('ax-as-log');\nconst empty=log.querySelector('.ax-as__empty'); if(empty)empty.remove();\nconst el=document.createElement('div');\nel.className='ax-as__msg ax-as__msg--'+kind;\nel.textContent=text;\nlog.appendChild(el); log.scrollTop=log.scrollHeight;\nreturn el;\n}\n\n/* Who can answer, and where. Both pickers degrade to a plain statement\n rather than an empty dropdown that looks like it is still loading. */\nfetch('/api/agents').then(r=>r.json()).then(d=>{\nconst list=(Array.isArray(d)?d:(d.agents||[])).map(a=>a.id||a.name).filter(Boolean);\n$('ax-as-agent').innerHTML=list.length\n ?list.map(a=>'<option value=\"'+esc(a)+'\">'+esc(a)+'</option>').join('')\n :'<option value=\"\">no agents on this node</option>';\n}).catch(()=>{$('ax-as-agent').innerHTML='<option value=\"\">agents unavailable</option>';});\n\nfetch('/api/monitor').then(r=>r.json()).then(d=>{\nconst ns=(d.nodes||[]).filter(n=>n.ok);\nif(ns.length>1)$('ax-as-node').innerHTML=ns.map(n=>'<option value=\"'+esc(n.url)+'\">'+esc(n.name)+'</option>').join('');\n}).catch(()=>{});\n\n/* --- Conversation state ---------------------------------------------------\n The thread lives on the daemon; the page only remembers which one it was\n looking at. That is what survives a navigation: a turn started here keeps\n running even after this page is gone, and the answer is waiting in the\n thread when you come back. */\nlet threadId=null, poll=null;\ntry{threadId=localStorage.getItem('ax-assistant-thread')||null}catch{}\nfunction setThread(id){\nthreadId=id;\ntry{id?localStorage.setItem('ax-assistant-thread',id):localStorage.removeItem('ax-assistant-thread')}catch{}\n}\n\nfunction bubble(m){\nconst el=document.createElement('div');\nconst kind=m.role==='user'?'me':(m.status==='error'?'err':'them');\nel.className='ax-as__msg ax-as__msg--'+kind+(m.status==='pending'?' ax-as__pending':'');\nif(m.status==='pending'){el.textContent='Thinking…';return el;}\nconst body=m.content||'(no reply)';\n// The reply is markdown. markdownToHtml escapes before it marks up, which\n// matters here: this text came from a model, not from us.\nif(m.role==='assistant'&&m.status==='done'&&typeof markdownToHtml==='function'){\n el.classList.add('ax-as__md');\n try{el.innerHTML=markdownToHtml(body);}catch(e){el.textContent=body;}\n}else el.textContent=body;\nreturn el;\n}\nfunction renderMessages(msgs){\nconst log=$('ax-as-log');\nlog.innerHTML='';\nif(!msgs.length){\n log.innerHTML='<p class=\"ax-as__empty\">Ask about what is on this page. What you are looking at &mdash; the page, its filters and the rows in view &mdash; goes with the question.</p>';\n return;\n}\nfor(const m of msgs)log.appendChild(bubble(m));\nlog.scrollTop=log.scrollHeight;\n}\n\nasync function loadThread(id,{quiet}={}){\nif(!id){renderMessages([]);$('ax-as-title').textContent='Ask an agent';return;}\ntry{\n const r=await fetch('/api/assistant/thread?id='+encodeURIComponent(id));\n if(!r.ok){setThread(null);renderMessages([]);return;}\n const d=await r.json();\n $('ax-as-title').textContent=d.thread.title||'Ask an agent';\n if(d.thread.agentId)$('ax-as-agent').value=d.thread.agentId;\n renderMessages(d.messages||[]);\n const pending=(d.messages||[]).some(m=>m.status==='pending');\n clearTimeout(poll);\n if(pending)poll=setTimeout(()=>loadThread(id,{quiet:true}),1500);\n else if(!quiet)loadHistory();\n}catch(e){}\n}\n\nasync function loadHistory(){\ntry{\n const r=await fetch('/api/assistant/threads');\n const d=await r.json();\n const list=d.threads||[];\n $('ax-as-hist-empty').hidden=list.length>0;\n $('ax-as-hist-list').innerHTML=list.map(t=>\n '<li data-thread=\"'+esc(t.id)+'\"'+(t.id===threadId?' class=\"is-on\"':'')+'>'\n +'<span class=\"ax-as__hist-t\">'+esc(t.title)+'</span>'\n +'<span class=\"ax-as__hist-m\">'+esc(t.agentId)+'</span>'\n +'<button class=\"ax-as__hist-x\" data-del=\"'+esc(t.id)+'\" title=\"Delete\">&times;</button></li>').join('');\n}catch(e){}\n}\n\n$('ax-as-hist').onclick=()=>{\nconst panel2=$('ax-as-hist-panel');\nconst show=panel2.hidden;\npanel2.hidden=!show;\n$('ax-as-hist').classList.toggle('is-on',show);\nif(show)loadHistory();\n};\n$('ax-as-new').onclick=()=>{\nsetThread(null);renderMessages([]);$('ax-as-title').textContent='Ask an agent';\n$('ax-as-hist-panel').hidden=true;$('ax-as-hist').classList.remove('is-on');\n$('ax-as-bar-input').focus();\n};\n$('ax-as-hist-list').addEventListener('click',async e=>{\nconst del=e.target.closest('[data-del]');\nif(del){e.stopPropagation();\n await fetch('/api/assistant/delete',{method:'POST',headers:{'Content-Type':'application/json'},\n body:JSON.stringify({threadId:del.dataset.del})});\n if(del.dataset.del===threadId){setThread(null);renderMessages([]);}\n loadHistory();return;}\nconst li=e.target.closest('[data-thread]');\nif(!li)return;\nsetThread(li.dataset.thread);\n$('ax-as-hist-panel').hidden=true;$('ax-as-hist').classList.remove('is-on');\nloadThread(threadId);loadHistory();\n});\n\nasync function send(){\nconst input=$('ax-as-bar-input'); const text=input.value.trim();\nif(!text||busy)return;\nconst agent=$('ax-as-agent').value;\nif(!agent&&!threadId){add('err','Pick an agent first.');return;}\nbusy=true; $('ax-as-bar-send').disabled=true;\ninput.value='';\ntry{\n const r=await fetch('/api/assistant',{method:'POST',headers:{'Content-Type':'application/json'},\n body:JSON.stringify({threadId:threadId||undefined,agentId:agent,node:$('ax-as-node').value||undefined,\n message:text,context:pageContext()})});\n const d=await r.json();\n if(!r.ok||d.error)throw new Error(d.error||('HTTP '+r.status));\n setThread(d.threadId);\n await loadThread(d.threadId);\n}catch(e){add('err',e.message);}\nfinally{busy=false;$('ax-as-bar-send').disabled=false;input.focus();}\n}\n\n/* The floating bar is the fast path: type from anywhere on the page, and the\n drawer opens to show the answer arriving. It shares one send(), so a\n question asked here continues the same conversation as one asked there. */\n$('ax-as-bar').addEventListener('submit',e=>{e.preventDefault();open(true);send();});\n\n/* Focusing the composer is a statement of intent: show the conversation it\n belongs to — unless the operator has deliberately closed the drawer, in\n which case reopening it under them is the opposite of helpful. */\n$('ax-as-bar-input').addEventListener('focus',()=>{if(!userHid())open(true);});\n\n/* Tuck the whole dock away, and remember it. */\nconst dock=$('ax-as-dock');\nfunction tuck(v){\ndock.classList.toggle('is-tucked',v);\n$('ax-as-bar-toggle').setAttribute('aria-expanded',String(!v));\n$('ax-as-bar-toggle').title=v?'Show the composer':'Hide the composer';\ntry{localStorage.setItem('ax-assistant-bar',v?'0':'1')}catch{}\n}\n$('ax-as-bar-toggle').onclick=()=>tuck(!dock.classList.contains('is-tucked'));\ntry{if(localStorage.getItem('ax-assistant-bar')==='0')tuck(true)}catch{}\n\n/* Restore whatever was in flight when the last page unloaded, and surface it:\n an answer landing while you are on another page is the whole point. */\nloadThread(threadId).then(()=>{\nif(threadId&&!userHid()&&$('ax-as-log').querySelector('.ax-as__pending'))open(true);\n});\nloadHistory();\n})();\n`\n","// --- Shipping a server function to the browser ----------------------------\n//\n// Pages send small pure helpers to the client by stringifying them, which\n// keeps one implementation instead of two that drift. The trap: the bundler\n// minifies, so `export function decayOf` ships as `function Fe(...)` and any\n// sibling that CALLS it emits `Fe(...)`. Assigning it to `const decayOf`\n// leaves `Fe` unbound — a named function expression binds its name only\n// inside its own body — and the page throws ReferenceError at runtime with a\n// perfectly clean build and green type-check.\n//\n// So bind each function under BOTH names: the readable one the page code\n// uses, and its own `.name`, which is whatever the minifier chose.\n\nexport function injectFns(fns: Record<string, Function>): string {\n // A page can carry more than one of these — its own helpers plus whatever\n // the shell injects — and two top-level `const __bind` declarations in one\n // document is a SyntaxError that kills both scripts. Hang it off globalThis\n // instead, and only once.\n const bind = \"globalThis.__axBind=globalThis.__axBind||((n,f)=>{globalThis[f.name]=f;globalThis[n]=f;return f});\"\n return bind + Object.entries(fns)\n .map(([name, fn]) => `const ${name}=globalThis.__axBind(${JSON.stringify(name)},${fn.toString()});`)\n .join(\"\")\n}\n","// --- Markdown to HTML, shared -------------------------------------------\n//\n// One renderer for the wiki, the dashboard's ask-an-agent drawer and the task\n// conversation view. It escapes first and only then adds markup, because\n// everything it renders — wiki articles, agent replies — is written by\n// someone or something other than the person reading it.\n//\n// Deliberately small: headings, bold/italic, code, links, lists, tables,\n// blockquotes and rules. Anything it does not know stays as text.\n\nexport interface MarkdownOptions {\n /** Resolve [[wikilinks]]. Omitted, they render as their display text. */\n wikilink?: (target: string, display: string) => string\n}\n\nexport function markdownToHtml(text: string, opts: MarkdownOptions = {}): string {\n // Strip section-tag HTML comments before escaping so they don't render\n // as literal \"<!-- tags: ... -->\" in the output.\n // Inlined rather than a module-scope helper: this function is stringified\n // and shipped to the browser, where the minifier renames such a helper and\n // module scope does not travel with the source.\n let html = String(text ?? \"\")\n .replace(/&/g, \"&amp;\").replace(/</g, \"&lt;\").replace(/>/g, \"&gt;\")\n .replace(/\"/g, \"&quot;\").replace(/'/g, \"&#39;\")\n\n // Code blocks (fenced)\n html = html.replace(/```(\\w*)\\n([\\s\\S]*?)```/g, (_m, lang, code) =>\n `<pre><code class=\"lang-${lang}\">${code.trim()}</code></pre>`)\n\n // Inline code\n html = html.replace(/`([^`]+)`/g, '<code>$1</code>')\n\n // Tables\n html = html.replace(/^(\\|.+\\|)\\n(\\|[-| :]+\\|)\\n((?:\\|.+\\|\\n?)*)/gm, (_m, header, _sep, body) => {\n const ths = header.split(\"|\").filter((c: string) => c.trim()).map((c: string) => `<th>${c.trim()}</th>`).join(\"\")\n const rows = body.trim().split(\"\\n\").map((row: string) => {\n const tds = row.split(\"|\").filter((c: string) => c.trim()).map((c: string) => `<td>${c.trim()}</td>`).join(\"\")\n return `<tr>${tds}</tr>`\n }).join(\"\")\n return `<table><thead><tr>${ths}</tr></thead><tbody>${rows}</tbody></table>`\n })\n\n // Headings\n html = html.replace(/^#### (.+)$/gm, '<h4>$1</h4>')\n html = html.replace(/^### (.+)$/gm, '<h3>$1</h3>')\n html = html.replace(/^## (.+)$/gm, '<h2>$1</h2>')\n html = html.replace(/^# (.+)$/gm, '<h1>$1</h1>')\n\n // Bold and italic\n html = html.replace(/\\*\\*(.+?)\\*\\*/g, '<strong>$1</strong>')\n html = html.replace(/\\*(.+?)\\*/g, '<em>$1</em>')\n\n // Wikilinks — support three forms for the [[...]] body:\n // [[Title]] exact article title\n // [[path/to/article]] path without .md (e.g. \"people/alex\")\n // [[path/to/article.md]] full relative path\n // [[Title|custom display text]] optional display override (Obsidian-style)\n html = html.replace(/\\[\\[([^\\]]+)\\]\\]/g, (_m, inner: string) => {\n const [rawTarget, rawDisplay] = inner.split(\"|\", 2)\n const target = rawTarget.trim().replace(/^\\//, \"\")\n const display = (rawDisplay || rawTarget).trim()\n // Without a resolver a wikilink is just words — better than a dead link.\n return opts.wikilink ? opts.wikilink(target, display) : display\n })\n\n // External links\n html = html.replace(/\\[([^\\]]+)\\]\\(([^)]+)\\)/g, '<a href=\"$2\" target=\"_blank\" rel=\"noopener noreferrer\">$1</a>')\n\n // Blockquotes (consecutive `> ` lines → one <blockquote>)\n html = html.replace(/^(>\\s?.*(?:\\n>\\s?.*)*)/gm, (block: string) => {\n const inner = block.split(\"\\n\").map(l => l.replace(/^>\\s?/, \"\")).join(\"<br>\")\n return `<blockquote>${inner}</blockquote>`\n })\n\n // Task-list items (render as plain text with unicode checkbox)\n html = html.replace(/^[-*] \\[( |x|X)\\] (.+)$/gm, (_m, chk, rest) =>\n `<li class=\"task\">${chk.toLowerCase() === \"x\" ? \"☑\" : \"☐\"} ${rest}</li>`)\n\n // Unordered lists\n html = html.replace(/^[-*] (.+)$/gm, '<li>$1</li>')\n\n // Ordered lists — match lines like \"1. foo\" (number, dot, space)\n html = html.replace(/^\\d+\\. (.+)$/gm, '<li data-ordered=\"1\">$1</li>')\n\n // Wrap consecutive ordered <li>s into <ol>. Keep the data-ordered marker\n // on each <li> inside the <ol> so the subsequent <ul> regex below can\n // correctly skip them via negative lookahead. Marker is stripped once all\n // list-wrapping is done.\n html = html.replace(/(<li data-ordered=\"1\">.*?<\\/li>(?:\\n?<li data-ordered=\"1\">.*?<\\/li>)*)/g, '<ol>$1</ol>')\n\n // Wrap consecutive unordered <li>s — the negative lookahead `(?! data-ordered)`\n // ensures ordered <li>s inside the <ol> above are NOT re-wrapped in <ul>.\n html = html.replace(/((?:<li(?! data-ordered)[^>]*>.*?<\\/li>\\n?)+)/g, '<ul>$1</ul>')\n\n // Clean up the ordered marker now that wrapping is settled.\n html = html.replace(/ data-ordered=\"1\"/g, \"\")\n\n // Horizontal rules\n html = html.replace(/^---$/gm, '<hr>')\n\n // Paragraphs — only wrap non-block lines. A paragraph is a run of\n // text that isn't already wrapped in a block-level tag.\n html = html.replace(/\\n\\n+/g, '</p><p>')\n html = `<p>${html}</p>`\n // Unwrap <p>...</p> that now contain block-level elements.\n html = html.replace(/<p>\\s*(<(?:h[1-6]|ul|ol|blockquote|table|pre|hr)\\b[^>]*>[\\s\\S]*?<\\/(?:h[1-6]|ul|ol|blockquote|table|pre)>)\\s*<\\/p>/g, \"$1\")\n html = html.replace(/<p>\\s*<hr>\\s*<\\/p>/g, \"<hr>\")\n // Drop empty paragraphs that remain.\n html = html.replace(/<p>\\s*<\\/p>/g, \"\")\n\n html = html.replace(/<p>(<(?:h[1-4]|pre|table|ul|hr|div))/g, '$1')\n html = html.replace(/(<\\/(?:h[1-4]|pre|table|ul|hr|div)>)<\\/p>/g, '$1')\n html = html.replace(/<p>\\s*<\\/p>/g, '')\n\n return html\n}\n","// --- Shared component CSS ---\n//\n// Styles for the primitives in components.ts. Kept separate from tokens so a\n// page can opt-in to only what it uses (setup-wizard doesn't need stat-strip\n// styles, for example). Most pages just emit both blocks together.\n//\n// All class names are prefixed `ax-` — they won't collide with host-app or\n// per-page styles.\n\nexport const AX_COMPONENTS_CSS = `\n/* --- Dots --- */\n.ax-dot { display: inline-block; width: 7px; height: 7px; border-radius: 50%; background: var(--ax-muted); }\n.ax-dot--ok, .ax-dot--live { background: var(--ax-accent); }\n.ax-dot--warn { background: var(--ax-warn); }\n.ax-dot--err { background: var(--ax-err); }\n.ax-dot--off { background: var(--ax-border-2); }\n.ax-dot--pulse {\n box-shadow: 0 0 0 0 currentColor;\n animation: ax-pulse 1.8s infinite;\n color: var(--ax-accent);\n}\n@keyframes ax-pulse {\n 0% { box-shadow: 0 0 0 0 color-mix(in oklch, var(--ax-accent) 70%, transparent); }\n 70% { box-shadow: 0 0 0 6px color-mix(in oklch, var(--ax-accent) 0%, transparent); }\n 100% { box-shadow: 0 0 0 0 color-mix(in oklch, var(--ax-accent) 0%, transparent); }\n}\n\n/* --- Badges --- */\n.ax-badge {\n display: inline-flex; align-items: center; gap: 4px;\n padding: 2px 10px; border-radius: var(--ax-radius-pill); font-size: var(--ax-fs-xs); line-height: 18px;\n border: var(--ax-border-w) solid var(--ax-border-2); color: var(--ax-text-2);\n background: var(--ax-surface); font-weight: 600;\n}\n.ax-badge--mono {\n font-family: var(--ax-mono); letter-spacing: 0.02em;\n text-transform: uppercase; font-size: 10px;\n}\n.ax-badge--accent {\n color: var(--ax-accent);\n border-color: color-mix(in oklch, var(--ax-accent) 50%, transparent);\n}\n.ax-badge--live {\n color: var(--ax-accent);\n background: color-mix(in oklch, var(--ax-accent) 14%, transparent);\n border-color: color-mix(in oklch, var(--ax-accent) 60%, transparent);\n font-weight: 600; letter-spacing: 0.06em;\n}\n.ax-badge--warn {\n color: var(--ax-warn);\n border-color: color-mix(in oklch, var(--ax-warn) 50%, transparent);\n}\n.ax-badge--err {\n color: var(--ax-err);\n border-color: color-mix(in oklch, var(--ax-err) 50%, transparent);\n}\n.ax-badge--ghost {\n color: var(--ax-muted); border-color: var(--ax-border); background: var(--ax-surface-2);\n}\n\n/* --- Buttons --- */\n.ax-btn {\n background: var(--ax-surface); color: var(--ax-text-2);\n border: var(--ax-border-w) solid var(--ax-border-2); border-radius: var(--ax-radius);\n padding: 8px 16px; font: inherit; font-size: var(--ax-fs); font-weight: 600;\n cursor: pointer; line-height: 1.4;\n box-shadow: var(--ax-shadow);\n transition: border-color 0.1s, color 0.1s, background 0.1s, transform 0.06s, box-shadow 0.06s;\n}\n.ax-btn:hover:not(:disabled) { color: var(--ax-text); border-color: var(--ax-accent); }\n/* The offset shadow collapses on press so the control physically sinks —\n the whole point of an un-blurred drop rather than a glow. */\n.ax-btn:active:not(:disabled) { transform: translateY(2px); box-shadow: 0 1px 0 var(--ax-border); }\n.ax-btn:disabled { opacity: 0.5; cursor: not-allowed; box-shadow: none; }\n\n.ax-btn--primary {\n background: color-mix(in oklch, var(--ax-accent) 15%, var(--ax-surface));\n color: var(--ax-accent);\n border-color: color-mix(in oklch, var(--ax-accent) 50%, var(--ax-border-2));\n font-weight: 700;\n box-shadow: var(--ax-shadow-accent);\n}\n.ax-btn--primary:hover:not(:disabled) {\n background: color-mix(in oklch, var(--ax-accent) 25%, var(--ax-surface));\n border-color: var(--ax-accent);\n}\n\n.ax-btn--ghost {\n background: transparent; color: var(--ax-muted);\n border-color: var(--ax-border); box-shadow: none;\n}\n.ax-btn--ghost:active:not(:disabled) { transform: none; }\n\n.ax-btn--danger {\n color: var(--ax-err);\n border-color: var(--ax-red-e);\n box-shadow: 0 4px 0 var(--ax-red-e);\n}\n.ax-btn--danger:hover:not(:disabled) {\n background: color-mix(in oklch, var(--ax-err) 12%, var(--ax-surface));\n border-color: var(--ax-err);\n}\n\n/* --- Chips (read-only labels used in kanban filters, etc.) --- */\n.ax-chip {\n display: inline-block; padding: 0 6px; font-family: var(--ax-mono);\n font-size: 11px; line-height: 18px; border-radius: var(--ax-radius-pill); color: var(--ax-text-2);\n border: var(--ax-border-w) solid var(--ax-border); background: var(--ax-surface); font-weight: 600;\n}\n\n/* --- Cards --- */\n.ax-card {\n background: var(--ax-surface); border: var(--ax-border-w) solid var(--ax-border);\n border-radius: var(--ax-radius-lg); padding: var(--ax-pad);\n box-shadow: var(--ax-shadow);\n}\n\n/* Step cards used by the setup wizard / onboarding-style flows. */\n.ax-step {\n background: var(--ax-surface); border: var(--ax-border-w) solid var(--ax-border);\n border-radius: var(--ax-radius); padding: 20px 22px; margin: 14px 0;\n}\n.ax-step__head {\n font-size: var(--ax-fs-xs); font-weight: 600; text-transform: uppercase;\n letter-spacing: 0.08em; color: var(--ax-muted);\n margin: 0 0 14px; display: flex; align-items: center; gap: 10px;\n}\n.ax-step__num {\n width: 22px; height: 22px; border-radius: var(--ax-radius-sm);\n background: color-mix(in oklch, var(--ax-accent) 15%, var(--ax-surface));\n border: 1px solid color-mix(in oklch, var(--ax-accent) 45%, var(--ax-border-2));\n color: var(--ax-accent);\n display: inline-flex; align-items: center; justify-content: center;\n font-family: var(--ax-mono); font-size: 11px; font-weight: 700;\n letter-spacing: 0; text-transform: none;\n}\n\n/* --- Stat strip (the \"COUNT / label\" cards across the top of Live) --- */\n.ax-statstrip {\n display: grid; grid-template-columns: repeat(auto-fit, minmax(170px, 1fr));\n gap: var(--ax-gap); padding: var(--ax-pad) var(--ax-pad) 0;\n}\n.ax-stat {\n background: var(--ax-surface); border: var(--ax-border-w) solid var(--ax-border);\n border-radius: var(--ax-radius); padding: 14px 16px;\n}\n.ax-stat__label {\n font-size: var(--ax-fs-xs); text-transform: uppercase; letter-spacing: 0.06em;\n color: var(--ax-muted); display: flex; align-items: center; gap: 6px;\n}\n.ax-stat__value {\n font-size: 24px; font-weight: 600; margin-top: 4px; letter-spacing: -0.02em;\n font-family: var(--ax-mono); font-variant-numeric: tabular-nums;\n}\n.ax-stat__sub {\n font-size: var(--ax-fs-xs); color: var(--ax-muted); margin-top: 2px;\n overflow: hidden; text-overflow: ellipsis; white-space: nowrap;\n}\n.ax-stat--live .ax-stat__value { color: var(--ax-accent); }\n.ax-stat--warn .ax-stat__value { color: var(--ax-warn); }\n.ax-stat--err .ax-stat__value { color: var(--ax-err); }\n\n/* --- Forms --- */\n.ax-field { margin: 10px 0 0; }\n.ax-field:first-child { margin-top: 0; }\n.ax-field label {\n display: block; margin-bottom: 5px;\n font-size: var(--ax-fs-xs); color: var(--ax-muted);\n text-transform: uppercase; letter-spacing: 0.06em; font-weight: 500;\n}\n.ax-field label .ax-hint {\n text-transform: none; letter-spacing: 0; color: var(--ax-muted);\n font-weight: 400; font-size: var(--ax-fs-xs); margin-left: 6px;\n}\n.ax-field input, .ax-field textarea, .ax-field select {\n width: 100%;\n background: var(--ax-bg); color: var(--ax-text);\n border: var(--ax-border-w) solid var(--ax-border-2); border-radius: var(--ax-radius);\n padding: 11px 14px; font: inherit; font-size: var(--ax-fs);\n transition: border-color 0.1s ease;\n}\n.ax-field input:focus, .ax-field textarea:focus, .ax-field select:focus {\n outline: none; border-color: var(--ax-accent);\n box-shadow: 0 0 0 2px color-mix(in oklch, var(--ax-accent) 18%, transparent);\n}\n.ax-field textarea { resize: vertical; min-height: 72px; font-family: var(--ax-font); }\n.ax-field input::placeholder, .ax-field textarea::placeholder {\n color: var(--ax-muted); opacity: 0.7;\n}\n\n.ax-row { display: flex; gap: 12px; }\n.ax-row > * { flex: 1; }\n\n/* --- Section titles shared across pages --- */\n.ax-section-title {\n display: flex; justify-content: space-between; align-items: center;\n margin-bottom: 12px;\n}\n.ax-section-title__label {\n font-size: var(--ax-fs-xs); text-transform: uppercase;\n letter-spacing: 0.08em; color: var(--ax-muted);\n}\n.ax-section-title__right {\n display: flex; align-items: center; gap: 8px;\n font-size: var(--ax-fs-xs); color: var(--ax-muted);\n}\n\n/* ============================================================\n * Settings-page design system (also reusable on other pages).\n * Ported from the Stitch redesign mockup so the primitives can\n * be consumed by any page — not just /admin. Sections:\n * page-head — kicker + h1 + lead paragraph\n * health-strip — 4-up status cards with coloured dot\n * wit — dismissable \"what is this?\" info banner\n * section-head — icon chip + title + lead + right-aligned CTA\n * row-card — list row with avatar + info + actions + expand\n * avatar — coloured-initial square (teal/coral/amber/blue)\n * pill — small status pill with coloured dot (chip-flavor)\n * connectors — 2-up grid of connector cards\n * tab-count — mono count bubble on a tab\n * toast — bottom-right ephemeral notification\n * modal — simple centred dialog + backdrop\n * sec-label — uppercase mini-heading with optional CTA\n * ============================================================ */\n\n/* --- Page head --- */\n.ax-page-head {\n max-width: 1040px; margin: 0 auto; padding: 28px 24px 12px;\n}\n.ax-page-head .ax-kicker {\n display: inline-block; font-size: 10px; letter-spacing: 0.1em;\n color: var(--ax-muted); text-transform: uppercase; margin-bottom: 6px;\n font-family: var(--ax-mono);\n}\n.ax-page-head h1 {\n font-size: 22px; font-weight: 600; letter-spacing: -0.015em;\n margin: 0 0 4px; display: flex; align-items: center; gap: 10px;\n}\n.ax-page-head .ax-lead {\n color: var(--ax-text-2); margin: 0; font-size: 13px;\n max-width: 640px; line-height: 1.6;\n}\n.ax-page-head .ax-lead code {\n font-family: var(--ax-mono); font-size: 11.5px;\n background: var(--ax-surface-2); padding: 1px 6px; border-radius: var(--ax-radius-sm);\n}\n\n/* --- Health strip --- */\n.ax-health-strip {\n max-width: 1040px; margin: 18px auto 22px; padding: 0 24px;\n display: grid; grid-template-columns: repeat(4, 1fr); gap: 10px;\n}\n.ax-health-card {\n background: var(--ax-surface); border: var(--ax-border-w) solid var(--ax-border);\n border-radius: var(--ax-radius-sm); padding: 12px 14px;\n display: flex; align-items: center; gap: 11px;\n transition: border-color 120ms;\n}\n.ax-health-card:hover { border-color: var(--ax-border-2); }\n.ax-health-card .ax-hc-dot {\n width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0;\n}\n.ax-health-card .ax-hc-dot--ok {\n background: var(--ax-accent);\n box-shadow: 0 0 8px color-mix(in oklch, var(--ax-accent) 50%, transparent);\n}\n.ax-health-card .ax-hc-dot--warn { background: var(--ax-warn); }\n.ax-health-card .ax-hc-dot--off { background: var(--ax-border-2); }\n.ax-health-card .ax-hc-txt { flex: 1; min-width: 0; }\n.ax-health-card .ax-hc-num {\n font-size: 17px; font-weight: 600; letter-spacing: -0.015em;\n}\n.ax-health-card .ax-hc-lbl {\n font-size: 11px; color: var(--ax-muted); margin-top: 1px;\n}\n\n/* --- \"What is this?\" info banner (dismissable) --- */\n.ax-wit {\n background: color-mix(in oklch, var(--ax-info) 6%, var(--ax-bg-elev));\n border: 1px solid color-mix(in oklch, var(--ax-info) 22%, var(--ax-border));\n border-radius: var(--ax-radius-sm); padding: 12px 14px; margin-bottom: 18px;\n font-size: 12.5px; color: var(--ax-text-2); line-height: 1.6;\n display: flex; gap: 10px; align-items: flex-start;\n}\n.ax-wit__icon {\n width: 18px; height: 18px; border-radius: 50%;\n background: color-mix(in oklch, var(--ax-info) 18%, transparent);\n color: var(--ax-info);\n display: grid; place-items: center;\n font-size: 11px; font-weight: 600; flex-shrink: 0; margin-top: 1px;\n}\n.ax-wit b { color: var(--ax-text); font-weight: 600; }\n.ax-wit code {\n font-family: var(--ax-mono); font-size: 11.5px;\n background: var(--ax-bg); padding: 1px 5px; border-radius: var(--ax-radius-sm);\n border: var(--ax-border-w) solid var(--ax-border);\n}\n.ax-wit .ax-wit__dismiss {\n background: transparent; border: 0; color: var(--ax-muted);\n font-size: 16px; cursor: pointer; margin-left: auto;\n padding: 0 4px; line-height: 1;\n}\n.ax-wit .ax-wit__dismiss:hover { color: var(--ax-text); }\n\n/* --- Section head (icon + title + lead + right CTA) --- */\n.ax-section-head {\n display: flex; align-items: flex-start; gap: 16px; margin-bottom: 18px;\n}\n.ax-section-head__icon {\n width: 44px; height: 44px; border-radius: var(--ax-radius);\n background: color-mix(in oklch, var(--ax-accent) 10%, var(--ax-surface));\n border: 1px solid color-mix(in oklch, var(--ax-accent) 25%, var(--ax-border));\n display: grid; place-items: center; flex-shrink: 0;\n color: var(--ax-accent);\n}\n.ax-section-head__icon svg { width: 22px; height: 22px; }\n.ax-section-head__text { flex: 1; }\n.ax-section-head h2 {\n font-size: 18px; margin: 0 0 3px;\n font-weight: 600; letter-spacing: -0.01em;\n}\n.ax-section-head .ax-lead {\n color: var(--ax-text-2); margin: 0; font-size: 13px; line-height: 1.55;\n max-width: 640px;\n}\n\n/* --- Row cards (collapsible list items) --- */\n.ax-stack { display: flex; flex-direction: column; gap: 10px; }\n.ax-row-card {\n background: var(--ax-surface); border: var(--ax-border-w) solid var(--ax-border);\n border-radius: var(--ax-radius-lg); padding: 14px 16px;\n transition: border-color 160ms, transform 160ms;\n}\n.ax-row-card:hover { border-color: var(--ax-border-2); }\n.ax-row-card__top { display: flex; align-items: center; gap: 12px; }\n.ax-row-card__info { flex: 1; min-width: 0; }\n.ax-row-card__info .ax-name {\n font-size: 14px; font-weight: 600; letter-spacing: -0.005em;\n display: flex; align-items: center; gap: 8px;\n}\n.ax-row-card__info .ax-slug {\n font-family: var(--ax-mono); font-size: 11px;\n color: var(--ax-muted); margin-left: 4px;\n}\n.ax-row-card__info .ax-sub {\n font-size: 12px; color: var(--ax-text-2); margin-top: 3px;\n line-height: 1.5; display: flex; align-items: center; gap: 8px; flex-wrap: wrap;\n}\n.ax-row-card__actions { display: flex; gap: 6px; align-items: center; }\n.ax-row-card__details {\n display: none; margin-top: 14px; padding-top: 14px;\n border-top: 1px dashed var(--ax-border);\n font-size: 12.5px; color: var(--ax-text-2); line-height: 1.6;\n}\n.ax-row-card.is-open .ax-row-card__details { display: block; }\n.ax-row-card.is-open .ax-chev { transform: rotate(180deg); }\n.ax-chev { transition: transform 160ms; }\n\n.ax-detail-grid {\n display: grid; grid-template-columns: repeat(2, 1fr);\n gap: 12px 20px; margin-bottom: 12px;\n}\n.ax-detail-grid dt {\n font-size: 10px; letter-spacing: 0.08em; text-transform: uppercase;\n color: var(--ax-muted); margin-bottom: 3px; font-family: var(--ax-mono);\n}\n.ax-detail-grid dd {\n margin: 0; font-size: 12.5px; color: var(--ax-text);\n}\n.ax-triggers { display: flex; gap: 4px; flex-wrap: wrap; }\n.ax-trigger-pill {\n font-family: var(--ax-mono); font-size: 11px;\n padding: 2px 8px; background: var(--ax-bg);\n border: var(--ax-border-w) solid var(--ax-border); border-radius: var(--ax-radius-sm);\n color: var(--ax-text);\n}\n\n/* --- Avatars (coloured initial square) --- */\n.ax-avatar {\n width: 36px; height: 36px; border-radius: var(--ax-radius); flex-shrink: 0;\n background: var(--ax-surface-2); border: var(--ax-border-w) solid var(--ax-border-2);\n display: grid; place-items: center;\n font-weight: 600; font-size: 14px; letter-spacing: -0.01em;\n color: var(--ax-text);\n}\n.ax-avatar--teal {\n background: color-mix(in oklch, var(--ax-accent) 18%, var(--ax-surface));\n border-color: color-mix(in oklch, var(--ax-accent) 35%, var(--ax-border));\n color: var(--ax-accent);\n}\n.ax-avatar--coral {\n background: color-mix(in oklch, var(--ax-err) 14%, var(--ax-surface));\n border-color: color-mix(in oklch, var(--ax-err) 30%, var(--ax-border));\n color: var(--ax-err);\n}\n.ax-avatar--amber {\n background: color-mix(in oklch, var(--ax-warn) 14%, var(--ax-surface));\n border-color: color-mix(in oklch, var(--ax-warn) 30%, var(--ax-border));\n color: var(--ax-warn);\n}\n.ax-avatar--blue {\n background: color-mix(in oklch, var(--ax-info) 14%, var(--ax-surface));\n border-color: color-mix(in oklch, var(--ax-info) 30%, var(--ax-border));\n color: var(--ax-info);\n}\n\n/* --- Pills (status indicator with coloured dot) ---\n *\n * Different from .ax-badge: pills have a rounded (10px) shape, a small\n * leading coloured dot, and stronger semantics (ok/warn/off/info). Use\n * for \"live\", \"idle\", \"receiving\", \"stale\" states next to a name.\n */\n.ax-pill {\n display: inline-flex; align-items: center; gap: 5px;\n font-size: 11px; padding: 2px 8px; line-height: 16px;\n border-radius: var(--ax-radius); background: var(--ax-surface-2);\n color: var(--ax-text-2); border: var(--ax-border-w) solid var(--ax-border);\n white-space: nowrap;\n}\n.ax-pill .ax-pill__dot {\n width: 6px; height: 6px; border-radius: 50%; background: currentColor;\n}\n.ax-pill--ok {\n background: color-mix(in oklch, var(--ax-accent) 14%, transparent);\n color: var(--ax-accent);\n border-color: color-mix(in oklch, var(--ax-accent) 30%, transparent);\n}\n.ax-pill--warn {\n background: color-mix(in oklch, var(--ax-warn) 14%, transparent);\n color: var(--ax-warn);\n border-color: color-mix(in oklch, var(--ax-warn) 30%, transparent);\n}\n.ax-pill--off { color: var(--ax-muted); }\n.ax-pill--info {\n background: color-mix(in oklch, var(--ax-info) 14%, transparent);\n color: var(--ax-info);\n border-color: color-mix(in oklch, var(--ax-info) 30%, transparent);\n}\n\n/* --- Connectors grid (Channels tab) --- */\n.ax-connectors {\n display: grid; grid-template-columns: repeat(2, 1fr); gap: 12px;\n}\n.ax-connector {\n background: var(--ax-surface); border: var(--ax-border-w) solid var(--ax-border);\n border-radius: var(--ax-radius-lg); padding: 18px 18px 16px;\n cursor: pointer; position: relative;\n transition: border-color 160ms, transform 160ms, background 160ms;\n}\n.ax-connector:hover { border-color: var(--ax-border-2); background: var(--ax-surface-2); }\n.ax-connector.is-on {\n border-color: color-mix(in oklch, var(--ax-accent) 40%, var(--ax-border));\n}\n.ax-connector.is-active {\n border-color: var(--ax-accent);\n background: color-mix(in oklch, var(--ax-accent) 8%, var(--ax-surface));\n}\n.ax-connector__top {\n display: flex; align-items: center; gap: 12px; margin-bottom: 10px;\n}\n.ax-connector__logo {\n width: 40px; height: 40px; border-radius: var(--ax-radius);\n display: grid; place-items: center;\n font-weight: 700; font-size: 15px; letter-spacing: -0.02em; flex-shrink: 0;\n background: var(--ax-surface-2); border: var(--ax-border-w) solid var(--ax-border-2);\n color: var(--ax-text); font-family: var(--ax-mono);\n}\n.ax-connector__meta { flex: 1; }\n.ax-connector__name {\n font-weight: 600; font-size: 14px; letter-spacing: -0.005em;\n}\n.ax-connector__sub {\n font-size: 11.5px; color: var(--ax-muted); margin-top: 2px;\n}\n.ax-connector__status { margin-left: auto; }\n.ax-connector__desc {\n font-size: 12.5px; color: var(--ax-text-2); line-height: 1.5; min-height: 36px;\n}\n.ax-connector__foot {\n margin-top: 12px; display: flex; align-items: center; justify-content: space-between;\n font-size: 11.5px; color: var(--ax-muted);\n}\n.ax-connector__cta {\n color: var(--ax-accent); font-weight: 500;\n display: inline-flex; align-items: center; gap: 4px;\n}\n.ax-connector__cta svg { width: 12px; height: 12px; }\n\n/* --- Tab counts + warn indicator --- */\n.ax-tab-count {\n font-family: var(--ax-mono); font-size: 10px;\n padding: 1px 6px; background: var(--ax-surface-2);\n color: var(--ax-muted); border-radius: var(--ax-radius); line-height: 14px;\n margin-left: 6px;\n}\n.is-active .ax-tab-count {\n background: color-mix(in oklch, var(--ax-accent) 18%, transparent);\n color: var(--ax-accent);\n}\n.ax-tab-warn {\n width: 6px; height: 6px; border-radius: 50%; background: var(--ax-warn);\n display: inline-block; margin-left: 6px;\n}\n\n/* --- Section mini-labels --- */\n.ax-sec-label {\n display: flex; align-items: center; justify-content: space-between;\n margin: 22px 0 10px;\n}\n.ax-sec-label h3 {\n font-size: 12px; font-weight: 600; letter-spacing: 0.06em;\n text-transform: uppercase; color: var(--ax-muted);\n margin: 0; font-family: var(--ax-mono);\n}\n\n/* --- Toast --- */\n.ax-toast {\n position: fixed; bottom: 24px; right: 24px;\n background: var(--ax-surface-2);\n border: var(--ax-border-w) solid var(--ax-accent); color: var(--ax-text);\n padding: 10px 16px; border-radius: var(--ax-radius); font-size: 12.5px;\n display: flex; align-items: center; gap: 10px;\n box-shadow: 0 10px 30px rgba(0,0,0,0.3);\n opacity: 0; transform: translateY(10px); pointer-events: none;\n transition: opacity 200ms, transform 200ms; z-index: 100;\n}\n.ax-toast.is-show { opacity: 1; transform: none; }\n.ax-toast__icon { color: var(--ax-accent); }\n\n/* --- Modal (centred dialog + backdrop) --- */\n.ax-modal-bd {\n position: fixed; inset: 0;\n background: color-mix(in oklch, var(--ax-bg) 60%, black);\n display: none; align-items: center; justify-content: center;\n z-index: 200; padding: 24px;\n}\n.ax-modal-bd.is-show { display: flex; }\n.ax-modal {\n width: min(560px, 94vw); max-height: 86vh; overflow: auto;\n background: var(--ax-surface); border: var(--ax-border-w) solid var(--ax-border-2);\n border-radius: var(--ax-radius-lg); padding: 0;\n box-shadow: 0 20px 60px rgba(0,0,0,0.5);\n}\n.ax-modal > header {\n padding: 18px 22px 10px; border-bottom: var(--ax-border-w) solid var(--ax-border);\n display: flex; align-items: center; gap: 10px;\n}\n.ax-modal > header h3 {\n margin: 0; font-size: 16px; font-weight: 600; letter-spacing: -0.01em; flex: 1;\n}\n.ax-modal > header .ax-modal__close {\n background: transparent; border: 0; color: var(--ax-muted);\n font-size: 22px; cursor: pointer; line-height: 1; padding: 0 6px;\n}\n.ax-modal__body { padding: 18px 22px; }\n.ax-modal__foot {\n padding: 12px 22px 18px;\n display: flex; justify-content: flex-end; gap: 8px;\n border-top: var(--ax-border-w) solid var(--ax-border);\n}\n\n/* --- Mesh hero (enable toggle + SVG network viz) --- */\n.ax-mesh-hero {\n background: var(--ax-surface); border: var(--ax-border-w) solid var(--ax-border);\n border-radius: var(--ax-radius-lg); padding: 20px;\n display: grid; grid-template-columns: 1fr 240px; gap: 24px;\n align-items: center; margin-bottom: 18px;\n}\n.ax-mesh-hero h3 { margin: 0 0 4px; font-size: 15px; font-weight: 600; }\n.ax-mesh-hero p { margin: 0 0 12px; font-size: 12.5px; color: var(--ax-text-2); line-height: 1.5; }\n.ax-mesh-viz { position: relative; width: 240px; height: 140px; }\n.ax-mesh-dot {\n position: absolute; width: 44px; height: 44px; border-radius: 50%;\n background: var(--ax-surface-2); border: 2px solid var(--ax-border-2);\n display: grid; place-items: center;\n font-family: var(--ax-mono); font-size: 10px; font-weight: 600;\n color: var(--ax-text-2);\n}\n.ax-mesh-dot.self {\n background: color-mix(in oklch, var(--ax-accent) 18%, var(--ax-surface));\n border-color: var(--ax-accent); color: var(--ax-accent);\n}\n.ax-mesh-dot.empty {\n border-style: dashed; color: var(--ax-muted); font-size: 14px;\n}\n.ax-mesh-wires { position: absolute; inset: 0; }\n.ax-mesh-wires line { stroke: var(--ax-border-2); stroke-width: 1.5; stroke-dasharray: 3 3; }\n.ax-mesh-wires line.live { stroke: var(--ax-accent); stroke-dasharray: 0; opacity: 0.6; }\n\n/* Pill-shaped toggle (used for the mesh enable switch + similar binary actions). */\n.ax-mesh-toggle {\n display: inline-flex; align-items: center; gap: 10px;\n background: var(--ax-bg-elev); border: var(--ax-border-w) solid var(--ax-border);\n border-radius: 20px; padding: 4px 14px 4px 5px;\n cursor: pointer; user-select: none; font-size: 12px;\n}\n.ax-mesh-switch {\n width: 32px; height: 18px; background: var(--ax-surface-3);\n border-radius: var(--ax-radius); position: relative; transition: background 160ms;\n}\n.ax-mesh-switch::after {\n content: \"\"; position: absolute; top: 2px; left: 2px;\n width: 14px; height: 14px; border-radius: 50%;\n background: var(--ax-text-2); transition: all 160ms;\n}\n.ax-mesh-toggle.is-on .ax-mesh-switch {\n background: color-mix(in oklch, var(--ax-accent) 80%, transparent);\n}\n.ax-mesh-toggle.is-on .ax-mesh-switch::after {\n left: 16px; background: var(--ax-bg);\n}\n\n/* --- Schedule builder: sentence-builder + pill picks + day picker --- */\n.ax-builder {\n background: var(--ax-surface); border: var(--ax-border-w) solid var(--ax-border);\n border-radius: var(--ax-radius-lg); padding: 20px;\n}\n.ax-builder h3 {\n margin: 0 0 4px; font-size: 15px; font-weight: 600; letter-spacing: -0.005em;\n}\n.ax-builder__hint { margin: 0 0 16px; font-size: 12.5px; color: var(--ax-muted); }\n.ax-builder__lbl {\n display: block; font-size: 11px; color: var(--ax-muted);\n letter-spacing: 0.03em; text-transform: uppercase; margin: 12px 0 5px;\n font-weight: 500;\n}\n.ax-builder__lbl .opt {\n text-transform: none; letter-spacing: 0; font-weight: 400;\n opacity: 0.75; margin-left: 6px;\n}\n.ax-builder__inp {\n width: 100%; background: var(--ax-bg); color: var(--ax-text);\n border: var(--ax-border-w) solid var(--ax-border); border-radius: var(--ax-radius-sm);\n padding: 8px 11px; font: inherit; font-size: 13px;\n transition: border-color 120ms, box-shadow 120ms;\n}\n.ax-builder__inp:focus {\n outline: none; border-color: var(--ax-accent);\n box-shadow: 0 0 0 3px color-mix(in oklch, var(--ax-accent) 18%, transparent);\n}\ntextarea.ax-builder__inp { min-height: 80px; line-height: 1.5; resize: vertical; }\n\n.ax-mode-switch {\n display: inline-flex; background: var(--ax-surface-2);\n border: var(--ax-border-w) solid var(--ax-border); border-radius: var(--ax-radius); padding: 3px;\n margin-bottom: 14px;\n}\n.ax-mode-switch button {\n background: transparent; border: 0; color: var(--ax-muted);\n padding: 6px 14px; font: inherit; font-size: 12px; cursor: pointer;\n border-radius: var(--ax-radius-sm); font-weight: 500;\n}\n.ax-mode-switch button.is-active {\n background: var(--ax-surface); color: var(--ax-text);\n}\n\n.ax-sentence {\n display: flex; align-items: center; gap: 8px;\n font-size: 16px; line-height: 1.9; flex-wrap: wrap;\n margin-bottom: 14px; padding: 14px 16px;\n background: var(--ax-bg-elev); border: var(--ax-border-w) solid var(--ax-border);\n border-radius: var(--ax-radius-sm);\n}\n.ax-sentence > span { color: var(--ax-text-2); }\n.ax-pill-pick {\n background: var(--ax-surface); border: var(--ax-border-w) solid var(--ax-border-2);\n color: var(--ax-accent); padding: 3px 10px; border-radius: var(--ax-radius);\n font-size: 14px; font-family: inherit; cursor: pointer; font-weight: 500;\n}\n.ax-pill-pick:hover { background: var(--ax-surface-2); border-color: var(--ax-accent); }\n.ax-pill-pick--sm { font-size: 13px; padding: 3px 8px; }\n\n.ax-day-picker { display: flex; gap: 4px; }\n.ax-day-picker button {\n width: 30px; height: 30px; border-radius: var(--ax-radius);\n background: var(--ax-surface); border: var(--ax-border-w) solid var(--ax-border);\n color: var(--ax-muted); font: inherit; font-size: 11px; font-weight: 500;\n cursor: pointer;\n}\n.ax-day-picker button:hover { border-color: var(--ax-border-2); color: var(--ax-text); }\n.ax-day-picker button.is-active {\n background: color-mix(in oklch, var(--ax-accent) 20%, var(--ax-surface));\n border-color: var(--ax-accent); color: var(--ax-accent);\n}\n\n.ax-preview-box {\n background: color-mix(in oklch, var(--ax-accent) 5%, var(--ax-bg-elev));\n border: 1px solid color-mix(in oklch, var(--ax-accent) 20%, var(--ax-border));\n border-radius: var(--ax-radius-sm); padding: 12px 14px; margin-top: 12px;\n font-size: 12.5px; color: var(--ax-text-2);\n display: flex; align-items: center; gap: 10px;\n}\n.ax-preview-box svg { width: 15px; height: 15px; color: var(--ax-accent); flex-shrink: 0; }\n.ax-preview-box b { color: var(--ax-text); font-weight: 600; }\n\n.ax-cron-hints {\n display: grid; grid-template-columns: repeat(2, 1fr); gap: 6px 18px;\n margin-top: 10px; font-size: 11.5px; color: var(--ax-muted);\n}\n.ax-cron-hints code {\n font-family: var(--ax-mono); color: var(--ax-text);\n background: var(--ax-bg); border: var(--ax-border-w) solid var(--ax-border);\n padding: 1px 6px; border-radius: var(--ax-radius-sm); margin-right: 6px;\n}\n\n/* --- JSON tree viewer (Advanced tab) --- */\n.ax-jv-toolbar {\n display: flex; align-items: center; gap: 8px;\n padding: 8px 12px; background: var(--ax-bg-elev);\n border: var(--ax-border-w) solid var(--ax-border); border-bottom: 0;\n border-radius: var(--ax-radius) var(--ax-radius) 0 0;\n}\n.ax-jv-toolbar .ax-jv-spacer { flex: 1; }\n.ax-jv-toolbar .ax-jv-seg {\n display: inline-flex; background: var(--ax-surface);\n border: var(--ax-border-w) solid var(--ax-border); border-radius: var(--ax-radius-sm); padding: 2px;\n}\n.ax-jv-toolbar .ax-jv-seg button {\n background: transparent; border: 0; color: var(--ax-muted);\n padding: 4px 10px; font: inherit; font-size: 11px;\n cursor: pointer; border-radius: var(--ax-radius-sm);\n}\n.ax-jv-toolbar .ax-jv-seg button.is-active { background: var(--ax-surface-2); color: var(--ax-text); }\n.ax-jv-search {\n background: var(--ax-surface); border: var(--ax-border-w) solid var(--ax-border);\n border-radius: var(--ax-radius-sm); padding: 4px 9px; font: inherit; font-size: 12px;\n color: var(--ax-text); width: 180px;\n}\n.ax-jv-search:focus { outline: none; border-color: var(--ax-accent); }\n\n.ax-jv-viewer {\n background: var(--ax-bg-elev); border: var(--ax-border-w) solid var(--ax-border);\n border-top: 0; border-radius: 0 0 var(--ax-radius) var(--ax-radius);\n font-family: var(--ax-mono); font-size: 12.5px; line-height: 1.75;\n padding: 14px 18px; max-height: 620px; overflow: auto;\n}\n.ax-jv-line {\n display: flex; align-items: flex-start; padding: 1px 0; gap: 0;\n position: relative; border-radius: var(--ax-radius-sm); white-space: nowrap;\n}\n.ax-jv-line:hover { background: color-mix(in oklch, var(--ax-accent) 5%, transparent); }\n.ax-jv-line.is-match { background: color-mix(in oklch, var(--ax-warn) 16%, transparent); }\n.ax-jv-toggle {\n width: 12px; display: inline-block; color: var(--ax-muted);\n cursor: pointer; user-select: none; text-align: center;\n font-size: 9px; line-height: 1.75;\n}\n.ax-jv-toggle.empty { cursor: default; }\n.ax-jv-key { color: var(--ax-info); margin-right: 0; }\n.ax-jv-punc { color: var(--ax-muted); }\n.ax-jv-string { color: var(--ax-accent); }\n.ax-jv-number { color: var(--ax-warn); }\n.ax-jv-bool { color: var(--ax-info); font-weight: 500; }\n.ax-jv-null { color: var(--ax-muted); font-style: italic; }\n.ax-jv-comment {\n color: var(--ax-muted); font-family: var(--ax-font);\n font-style: italic; font-size: 11.5px; margin-left: 8px; opacity: 0.8;\n}\n.ax-jv-children {\n padding-left: 18px;\n border-left: 1px dashed color-mix(in oklch, var(--ax-border) 80%, transparent);\n margin-left: 4px;\n}\n.ax-jv-children.is-collapsed { display: none; }\n.ax-jv-collapsed-note {\n color: var(--ax-muted); font-size: 11px; font-style: italic; margin-left: 4px;\n}\n.ax-jv-empty-brace { color: var(--ax-muted); }\n\n/* ============================================================\n * Detail-page primitives (Agent manage, and any future\n * detail-view). Built around a 3-col layout:\n *\n * [rail 200px] [main] [test-drive 360px]\n *\n * Scales down: test-drive hides at 1180px, rail hides at 780px.\n * ============================================================ */\n\n/* Crumb */\n.ax-crumb {\n max-width: 1400px; margin: 0 auto; padding: 16px 24px 0;\n font-size: 12px; color: var(--ax-muted);\n display: flex; align-items: center; gap: 6px;\n}\n.ax-crumb a { color: var(--ax-muted); }\n.ax-crumb a:hover { color: var(--ax-accent); text-decoration: none; }\n.ax-crumb .ax-crumb__sep { color: var(--ax-border-2); }\n.ax-crumb .ax-crumb__cur { color: var(--ax-text); }\n\n/* Hero — large avatar + editable name + chip row + actions */\n.ax-hero {\n max-width: 1400px; margin: 0 auto; padding: 14px 24px 18px;\n display: flex; align-items: center; gap: 18px; flex-wrap: wrap;\n}\n.ax-hero__ava {\n width: 64px; height: 64px; border-radius: 14px;\n background: color-mix(in oklch, var(--ax-accent) 20%, var(--ax-surface));\n border: 1px solid color-mix(in oklch, var(--ax-accent) 40%, var(--ax-border));\n display: grid; place-items: center;\n font-weight: 700; font-size: 22px; letter-spacing: -0.02em;\n color: var(--ax-accent); flex-shrink: 0; position: relative;\n}\n.ax-hero__on {\n position: absolute; bottom: -2px; right: -2px;\n width: 16px; height: 16px; border-radius: 50%;\n background: var(--ax-accent); border: 3px solid var(--ax-bg);\n}\n.ax-hero__title { flex: 1; min-width: 260px; }\n.ax-hero h1 {\n margin: 0 0 3px; font-size: 22px; font-weight: 600;\n letter-spacing: -0.02em; display: flex; align-items: center; gap: 10px;\n}\n.ax-hero h1 input {\n background: transparent; border: 0; color: var(--ax-text);\n font: inherit; padding: 0; border-bottom: 1px dashed transparent;\n transition: border-color 120ms; max-width: 260px;\n}\n.ax-hero h1 input:hover { border-color: var(--ax-border-2); }\n.ax-hero h1 input:focus { outline: none; border-color: var(--ax-accent); }\n.ax-hero__sub {\n color: var(--ax-text-2); font-size: 13px; margin-top: 2px;\n display: flex; align-items: center; gap: 12px; flex-wrap: wrap;\n}\n.ax-hero__sub .ax-hero__dot {\n width: 3px; height: 3px; background: var(--ax-border-2); border-radius: 50%;\n}\n.ax-hero__actions { display: flex; gap: 8px; align-items: center; }\n\n/* Inline stats strip (smaller than the settings health strip — fits in\n * the detail page hero below the hero title). */\n.ax-stats-inline {\n max-width: 1400px; margin: 0 auto; padding: 0 24px;\n display: grid; grid-template-columns: repeat(4, 1fr); gap: 10px;\n}\n.ax-stat-inline {\n background: var(--ax-surface); border: var(--ax-border-w) solid var(--ax-border);\n border-radius: var(--ax-radius-sm); padding: 10px 12px;\n display: flex; align-items: center; gap: 11px;\n}\n.ax-stat-inline__icon {\n width: 26px; height: 26px; border-radius: var(--ax-radius);\n background: color-mix(in oklch, var(--ax-accent) 12%, var(--ax-surface-2));\n display: grid; place-items: center;\n color: var(--ax-accent); flex-shrink: 0;\n}\n.ax-stat-inline__icon svg { width: 14px; height: 14px; }\n.ax-stat-inline__v {\n font-size: 15px; font-weight: 600; letter-spacing: -0.01em; line-height: 1.1;\n}\n.ax-stat-inline__l {\n font-size: 11px; color: var(--ax-muted); margin-top: 1px;\n}\n\n/* Detail layout — 2-col rail + main. Overlays (test-drive, history, etc.)\n * dock separately via fixed positioning. */\n.ax-layout {\n max-width: 1400px; margin: 22px auto 0; padding: 0 24px 60px;\n display: grid; grid-template-columns: 200px 1fr;\n gap: 22px; align-items: flex-start;\n}\n\n/* Left rail — sticky nav list with grouped items */\n.ax-rail {\n position: sticky; top: 72px;\n display: flex; flex-direction: column; gap: 1px;\n}\n.ax-rail a {\n display: flex; align-items: center; gap: 10px;\n padding: 9px 11px; border-radius: var(--ax-radius);\n color: var(--ax-text-2); font-size: 13px; font-weight: 500;\n cursor: pointer; position: relative;\n transition: background 120ms, color 120ms;\n}\n.ax-rail a svg {\n width: 15px; height: 15px; flex-shrink: 0; color: var(--ax-muted);\n}\n.ax-rail a:hover {\n background: var(--ax-surface); color: var(--ax-text); text-decoration: none;\n}\n.ax-rail a:hover svg { color: var(--ax-text); }\n.ax-rail a.is-active {\n background: color-mix(in oklch, var(--ax-accent) 12%, var(--ax-surface));\n color: var(--ax-accent);\n}\n.ax-rail a.is-active svg { color: var(--ax-accent); }\n.ax-rail a.is-active::before {\n content: \"\"; position: absolute; left: -3px; top: 7px; bottom: 7px;\n width: 2px; border-radius: var(--ax-radius-sm); background: var(--ax-accent);\n}\n.ax-rail__count {\n margin-left: auto; font-family: var(--ax-mono);\n font-size: 10px; padding: 1px 6px; background: var(--ax-surface-2);\n color: var(--ax-muted); border-radius: var(--ax-radius); line-height: 14px;\n}\n.ax-rail a.is-active .ax-rail__count {\n background: color-mix(in oklch, var(--ax-accent) 22%, transparent);\n color: var(--ax-accent);\n}\n.ax-rail__sep { height: 1px; background: var(--ax-border); margin: 8px 4px; }\n.ax-rail__label {\n font-size: 10px; letter-spacing: 0.1em; text-transform: uppercase;\n color: var(--ax-muted); padding: 6px 11px 4px; font-family: var(--ax-mono);\n}\n\n/* Main panel containers (one per section) */\n.ax-panel {\n background: var(--ax-surface); border: var(--ax-border-w) solid var(--ax-border);\n border-radius: var(--ax-radius-lg); padding: 22px;\n}\n.ax-panel + .ax-panel { margin-top: 12px; }\n.ax-panel h2 {\n margin: 0 0 4px; font-size: 16px; font-weight: 600; letter-spacing: -0.01em;\n}\n.ax-panel .ax-lead {\n margin: 0 0 16px; font-size: 12.5px; color: var(--ax-text-2); line-height: 1.55;\n}\n.ax-panel__head {\n display: flex; align-items: flex-start; justify-content: space-between;\n gap: 12px; margin-bottom: 14px;\n}\n.ax-panel__head h2 { margin: 0 0 3px; }\n.ax-panel__tab { display: none; animation: ax-fade 180ms ease; }\n.ax-panel__tab.is-active { display: block; }\n@keyframes ax-fade {\n from { opacity: 0; transform: translateY(4px); }\n to { opacity: 1; transform: none; }\n}\n\n/* Grids for form rows */\n.ax-grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }\n.ax-grid-3 { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 12px; }\n\n/* Slider-style setting row — label + hint + segmented options */\n.ax-slider-row {\n display: grid; grid-template-columns: 140px 1fr auto;\n gap: 12px; align-items: center;\n padding: 10px 0; border-bottom: 1px dashed var(--ax-border);\n}\n.ax-slider-row:last-child { border-bottom: 0; }\n.ax-slider-row label { font-size: 12.5px; color: var(--ax-text); margin: 0; }\n.ax-slider-row .ax-hint {\n margin: 0; color: var(--ax-muted); font-size: 11.5px;\n}\n.ax-slider-row .ax-opts {\n display: flex; gap: 4px; background: var(--ax-bg);\n border: var(--ax-border-w) solid var(--ax-border); border-radius: var(--ax-radius-sm); padding: 2px;\n}\n.ax-slider-row .ax-opts button {\n background: transparent; border: 0; color: var(--ax-muted);\n padding: 5px 11px; font: inherit; font-size: 11.5px;\n cursor: pointer; border-radius: var(--ax-radius-sm); white-space: nowrap;\n}\n.ax-slider-row .ax-opts button.is-active {\n background: var(--ax-surface-2); color: var(--ax-text);\n}\n\n/* Sub-tabs (Guided/Freeform, CLAUDE.md/SOUL.md/IDENTITY.md) */\n.ax-subtabs {\n display: inline-flex; background: var(--ax-bg);\n border: var(--ax-border-w) solid var(--ax-border); border-radius: var(--ax-radius);\n padding: 3px; margin-bottom: 16px;\n}\n.ax-subtabs button {\n background: transparent; border: 0; color: var(--ax-muted);\n padding: 6px 14px; font: inherit; font-size: 12px;\n cursor: pointer; border-radius: var(--ax-radius-sm); font-weight: 500;\n}\n.ax-subtabs button.is-active {\n background: var(--ax-surface-2); color: var(--ax-text);\n}\n\n/* Trigger-word chips with inline input (for Wake words field) */\n.ax-triggers-edit {\n display: flex; flex-wrap: wrap; gap: 6px;\n background: var(--ax-bg); border: var(--ax-border-w) solid var(--ax-border);\n padding: 7px; border-radius: var(--ax-radius-sm); min-height: 38px;\n align-items: center; cursor: text;\n}\n.ax-triggers-edit:focus-within {\n border-color: var(--ax-accent);\n box-shadow: 0 0 0 3px color-mix(in oklch, var(--ax-accent) 18%, transparent);\n}\n.ax-trig-pill {\n display: inline-flex; align-items: center; gap: 6px;\n font-family: var(--ax-mono); font-size: 11px;\n padding: 3px 4px 3px 9px;\n background: color-mix(in oklch, var(--ax-accent) 16%, var(--ax-surface));\n border: 1px solid color-mix(in oklch, var(--ax-accent) 30%, var(--ax-border));\n border-radius: var(--ax-radius-sm); color: var(--ax-accent);\n}\n.ax-trig-pill button {\n background: transparent; border: 0; color: currentColor;\n opacity: 0.6; cursor: pointer; font-size: 13px; line-height: 1;\n padding: 0 2px; border-radius: var(--ax-radius-sm);\n}\n.ax-trig-pill button:hover {\n opacity: 1;\n background: color-mix(in oklch, var(--ax-accent) 20%, transparent);\n}\n.ax-triggers-edit input {\n flex: 1; min-width: 120px;\n background: transparent; border: 0; color: var(--ax-text);\n font: inherit; font-size: 12.5px;\n padding: 4px 6px; font-family: var(--ax-mono);\n}\n.ax-triggers-edit input:focus { outline: none; }\n\n/* Prompt editor (freeform CLAUDE.md) */\n.ax-prompt-edit { position: relative; }\n.ax-prompt-edit textarea { min-height: 220px; line-height: 1.6; }\n.ax-prompt-badges {\n position: absolute; top: 8px; right: 8px; display: flex; gap: 6px;\n}\n.ax-prompt-badges .ax-pbadge {\n font-size: 10px; padding: 3px 7px; border-radius: var(--ax-radius-sm);\n background: var(--ax-surface-2); color: var(--ax-muted);\n font-family: var(--ax-mono); letter-spacing: 0.03em;\n}\n\n/* Skill gallery */\n.ax-skill-toolbar {\n display: flex; gap: 10px; align-items: center;\n margin-bottom: 14px; flex-wrap: wrap;\n}\n.ax-search {\n position: relative; flex: 1; min-width: 220px;\n}\n.ax-search svg {\n position: absolute; left: 10px; top: 50%; transform: translateY(-50%);\n width: 13px; height: 13px; color: var(--ax-muted); pointer-events: none;\n}\n.ax-search input { width: 100%; padding-left: 30px; }\n.ax-skill-filter {\n display: flex; gap: 4px; background: var(--ax-bg);\n border: var(--ax-border-w) solid var(--ax-border); border-radius: var(--ax-radius-sm); padding: 2px;\n}\n.ax-skill-filter button {\n background: transparent; border: 0; color: var(--ax-muted);\n padding: 5px 10px; font: inherit; font-size: 11.5px;\n cursor: pointer; border-radius: var(--ax-radius-sm);\n}\n.ax-skill-filter button.is-active {\n background: var(--ax-surface-2); color: var(--ax-text);\n}\n.ax-skill-grid {\n display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));\n gap: 10px;\n}\n.ax-skill-card {\n background: var(--ax-bg-elev); border: var(--ax-border-w) solid var(--ax-border);\n border-radius: var(--ax-radius-lg); padding: 14px;\n display: flex; flex-direction: column; gap: 8px;\n cursor: pointer; position: relative; overflow: hidden;\n transition: border-color 160ms, transform 160ms, background 160ms;\n}\n.ax-skill-card:hover { border-color: var(--ax-border-2); transform: translateY(-1px); }\n.ax-skill-card.is-on {\n border-color: color-mix(in oklch, var(--ax-accent) 40%, var(--ax-border));\n background: color-mix(in oklch, var(--ax-accent) 5%, var(--ax-bg-elev));\n}\n.ax-skill-card.is-on::after {\n content: \"\"; position: absolute; top: 0; right: 0;\n width: 0; height: 0;\n border-left: 26px solid transparent;\n border-top: 26px solid var(--ax-accent);\n}\n.ax-skill-card.is-on::before {\n content: \"\"; position: absolute; top: 5px; right: 3px;\n width: 7px; height: 4px;\n border-left: 2px solid var(--ax-bg);\n border-bottom: 2px solid var(--ax-bg);\n transform: rotate(-45deg); z-index: 2;\n}\n.ax-skill-card__top { display: flex; align-items: center; gap: 10px; }\n.ax-skill-card__icon {\n width: 32px; height: 32px; border-radius: var(--ax-radius);\n background: var(--ax-surface-2);\n display: grid; place-items: center; flex-shrink: 0;\n color: var(--ax-accent);\n}\n.ax-skill-card__icon svg { width: 16px; height: 16px; }\n.ax-skill-card__name {\n font-weight: 600; font-size: 13px; letter-spacing: -0.005em;\n}\n.ax-skill-card__slug {\n font-family: var(--ax-mono); font-size: 10.5px;\n color: var(--ax-muted); margin-top: 1px;\n}\n.ax-skill-card__desc {\n font-size: 12px; color: var(--ax-text-2); line-height: 1.5;\n flex: 1; min-height: 32px;\n}\n.ax-skill-card__foot {\n display: flex; align-items: center; justify-content: space-between;\n font-size: 11px; color: var(--ax-muted);\n}\n\n/* Info callout (similar to wit but inline inside panels) */\n.ax-callout {\n background: color-mix(in oklch, var(--ax-info) 5%, var(--ax-bg-elev));\n border: 1px solid color-mix(in oklch, var(--ax-info) 20%, var(--ax-border));\n border-radius: var(--ax-radius-sm); padding: 12px 14px;\n font-size: 12.5px; color: var(--ax-text-2); line-height: 1.6;\n display: flex; gap: 10px; align-items: flex-start; margin-bottom: 16px;\n}\n.ax-callout__icon {\n width: 18px; height: 18px; border-radius: 50%;\n background: color-mix(in oklch, var(--ax-info) 18%, transparent);\n color: var(--ax-info); display: grid; place-items: center;\n font-size: 11px; font-weight: 600; flex-shrink: 0; margin-top: 1px;\n}\n.ax-callout b { color: var(--ax-text); }\n.ax-callout code {\n font-family: var(--ax-mono); font-size: 11.5px;\n background: var(--ax-bg); padding: 1px 5px; border-radius: var(--ax-radius-sm);\n border: var(--ax-border-w) solid var(--ax-border);\n}\n\n/* Channel-binding rows (agent-specific view) */\n.ax-chan-row {\n background: var(--ax-bg-elev); border: var(--ax-border-w) solid var(--ax-border);\n border-radius: var(--ax-radius-lg); padding: 12px 14px;\n display: flex; align-items: center; gap: 12px; margin-bottom: 8px;\n}\n.ax-chan-row__logo {\n width: 32px; height: 32px; border-radius: var(--ax-radius);\n display: grid; place-items: center;\n font-weight: 700; font-size: 13px; color: white; flex-shrink: 0;\n font-family: var(--ax-mono);\n}\n.ax-chan-row__info { flex: 1; }\n.ax-chan-row__name { font-size: 13px; font-weight: 600; }\n.ax-chan-row__desc {\n font-size: 12px; color: var(--ax-muted);\n margin-top: 2px; font-family: var(--ax-mono);\n}\n\n/* Handover viz (from-agent → to-agent arrow) */\n.ax-handover-viz {\n display: flex; align-items: center; justify-content: space-between;\n gap: 14px; padding: 16px 18px;\n background: var(--ax-bg-elev); border: var(--ax-border-w) solid var(--ax-border);\n border-radius: var(--ax-radius-lg); margin-bottom: 14px;\n}\n.ax-ho-side {\n display: flex; flex-direction: column; align-items: center;\n gap: 6px; flex: 0 0 130px;\n}\n.ax-ho-side__av {\n width: 42px; height: 42px; border-radius: var(--ax-radius);\n display: grid; place-items: center;\n font-weight: 600; font-size: 15px;\n}\n.ax-ho-side--from .ax-ho-side__av {\n background: color-mix(in oklch, var(--ax-accent) 18%, var(--ax-surface));\n border: 1px solid color-mix(in oklch, var(--ax-accent) 35%, var(--ax-border));\n color: var(--ax-accent);\n}\n.ax-ho-side--to .ax-ho-side__av {\n background: var(--ax-surface-2); border: var(--ax-border-w) solid var(--ax-border-2);\n color: var(--ax-text);\n}\n.ax-ho-side__nm { font-size: 12px; font-weight: 600; }\n.ax-ho-side__rl {\n font-size: 10px; color: var(--ax-muted);\n text-transform: uppercase; letter-spacing: 0.06em;\n font-family: var(--ax-mono);\n}\n.ax-ho-wire {\n flex: 1; height: 2px; position: relative;\n background: repeating-linear-gradient(90deg,\n var(--ax-border-2) 0, var(--ax-border-2) 4px,\n transparent 4px, transparent 8px);\n}\n.ax-ho-wire svg {\n position: absolute; right: -6px; top: 50%; transform: translateY(-50%);\n width: 16px; height: 16px; color: var(--ax-accent);\n}\n.ax-ho-list-row {\n display: flex; align-items: center; gap: 10px;\n padding: 10px 12px;\n background: var(--ax-bg-elev); border: var(--ax-border-w) solid var(--ax-border);\n border-radius: var(--ax-radius-sm); margin-bottom: 6px;\n font-size: 12.5px;\n}\n.ax-ho-list-row .ax-who { flex: 1; color: var(--ax-text-2); }\n.ax-ho-list-row .ax-who b {\n color: var(--ax-text); font-family: var(--ax-mono); font-weight: 500;\n}\n.ax-ho-list-row .ax-when { color: var(--ax-muted); font-size: 11px; }\n\n/* Activity cells + spark + session rows */\n.ax-activity-grid {\n display: grid; grid-template-columns: repeat(4, 1fr);\n gap: 10px; margin-bottom: 18px;\n}\n.ax-activity-cell {\n background: var(--ax-bg-elev); border: var(--ax-border-w) solid var(--ax-border);\n border-radius: var(--ax-radius-sm); padding: 14px;\n}\n.ax-activity-cell .ax-v {\n font-size: 20px; font-weight: 600; letter-spacing: -0.02em; line-height: 1.2;\n}\n.ax-activity-cell .ax-l {\n font-size: 11px; color: var(--ax-muted); margin-top: 2px;\n}\n.ax-activity-cell .ax-delta {\n font-size: 11px; margin-top: 6px;\n display: inline-flex; align-items: center; gap: 3px;\n}\n.ax-activity-cell .ax-delta--up { color: var(--ax-accent); }\n.ax-activity-cell .ax-delta--down { color: var(--ax-muted); }\n\n.ax-spark { width: 100%; height: 100px; margin: 10px 0 14px; }\n\n.ax-session {\n display: flex; align-items: center; gap: 10px;\n padding: 10px 12px;\n background: var(--ax-bg-elev); border: var(--ax-border-w) solid var(--ax-border);\n border-radius: var(--ax-radius-sm); margin-bottom: 6px;\n font-size: 12.5px;\n}\n.ax-session__ch {\n width: 24px; height: 24px; border-radius: var(--ax-radius-sm);\n display: grid; place-items: center;\n font-weight: 700; font-size: 10px; color: white;\n flex-shrink: 0; font-family: var(--ax-mono);\n}\n.ax-session__body { flex: 1; min-width: 0; }\n.ax-session__txt {\n color: var(--ax-text); overflow: hidden; text-overflow: ellipsis;\n white-space: nowrap;\n}\n.ax-session__meta {\n font-size: 11px; color: var(--ax-muted);\n margin-top: 1px; font-family: var(--ax-mono);\n}\n\n/* Log row (Conversations tab) */\n.ax-log-head {\n display: grid; grid-template-columns: 24px 90px 160px 1fr auto;\n gap: 12px; padding: 8px 10px;\n border-bottom: var(--ax-border-w) solid var(--ax-border-2);\n font-size: 10px; color: var(--ax-muted);\n text-transform: uppercase; letter-spacing: 0.06em; font-family: var(--ax-mono);\n}\n.ax-log-row {\n display: grid; grid-template-columns: 24px 90px 160px 1fr auto;\n gap: 12px; align-items: center; padding: 9px 10px;\n border-bottom: var(--ax-border-w) solid var(--ax-border); font-size: 12px;\n}\n.ax-log-row:hover { background: color-mix(in oklch, var(--ax-accent) 4%, transparent); }\n.ax-log-row .ax-sev {\n width: 8px; height: 8px; border-radius: 50%; margin-left: 8px;\n}\n.ax-log-row .ax-sev--ok { background: var(--ax-accent); }\n.ax-log-row .ax-sev--warn { background: var(--ax-warn); }\n.ax-log-row .ax-sev--err { background: var(--ax-err); }\n.ax-log-row .ax-t {\n font-family: var(--ax-mono); color: var(--ax-muted); font-size: 11px;\n}\n.ax-log-row .ax-who {\n font-family: var(--ax-mono); font-size: 11px; color: var(--ax-text-2);\n}\n.ax-log-row .ax-msg {\n color: var(--ax-text); overflow: hidden;\n text-overflow: ellipsis; white-space: nowrap;\n}\n.ax-log-row .ax-ms {\n color: var(--ax-muted); font-family: var(--ax-mono); font-size: 11px;\n}\n\n/* Danger zone */\n.ax-panel--danger {\n border-color: color-mix(in oklch, var(--ax-err) 30%, var(--ax-border));\n}\n.ax-danger-item {\n display: flex; align-items: center; gap: 14px;\n padding: 14px 0; border-bottom: 1px dashed var(--ax-border);\n}\n.ax-danger-item:last-child { border-bottom: 0; padding-bottom: 0; }\n.ax-danger-item:first-child { padding-top: 4px; }\n.ax-danger-item__info { flex: 1; }\n.ax-danger-item__name { font-size: 13px; font-weight: 600; margin-bottom: 3px; }\n.ax-danger-item__desc { font-size: 12px; color: var(--ax-muted); line-height: 1.5; }\n\n/* Test-drive panel — docked to the right edge, slides in from outside\n * the viewport when toggled. Similar to the Live page history-panel. */\n.ax-td-panel {\n position: fixed; top: 0; right: 0; bottom: 0; width: 420px;\n background: var(--ax-bg-elev); border-left: var(--ax-border-w) solid var(--ax-border);\n z-index: 900;\n display: flex; flex-direction: column;\n box-shadow: -8px 0 24px rgba(0,0,0,0.4);\n transform: translateX(100%); transition: transform 0.22s ease;\n}\n.ax-td-panel.is-open { transform: translateX(0); }\n@media (max-width: 520px) {\n .ax-td-panel { width: 100vw; }\n}\n.ax-td-head {\n padding: 12px 14px; border-bottom: var(--ax-border-w) solid var(--ax-border);\n background: var(--ax-bg-elev);\n display: flex; align-items: center; gap: 10px;\n}\n.ax-td-head__dot {\n width: 8px; height: 8px; border-radius: 50%;\n background: var(--ax-accent);\n box-shadow: 0 0 8px color-mix(in oklch, var(--ax-accent) 60%, transparent);\n animation: ax-pulse 2.2s infinite;\n}\n.ax-td-head__t { flex: 1; font-size: 12px; font-weight: 600; }\n.ax-td-head__sub {\n font-size: 11px; color: var(--ax-muted); font-family: var(--ax-mono);\n}\n.ax-td-head__seg {\n display: inline-flex; background: var(--ax-surface);\n border: var(--ax-border-w) solid var(--ax-border); border-radius: var(--ax-radius-sm); padding: 2px;\n}\n.ax-td-head__seg button {\n background: transparent; border: 0; color: var(--ax-muted);\n padding: 3px 8px; font: inherit; font-size: 10.5px;\n cursor: pointer; border-radius: var(--ax-radius-sm); font-family: var(--ax-mono);\n}\n.ax-td-head__seg button.is-active {\n background: var(--ax-surface-2); color: var(--ax-text);\n}\n\n.ax-td-scenarios {\n padding: 10px 14px; background: var(--ax-bg-elev);\n border-bottom: var(--ax-border-w) solid var(--ax-border);\n display: flex; gap: 5px; flex-wrap: wrap;\n}\n.ax-td-scenarios__lbl {\n font-size: 10px; color: var(--ax-muted);\n text-transform: uppercase; letter-spacing: 0.06em;\n font-family: var(--ax-mono); width: 100%; margin-bottom: 3px;\n}\n.ax-td-scenario {\n font-size: 11px; padding: 3px 8px;\n background: var(--ax-surface); border: var(--ax-border-w) solid var(--ax-border);\n border-radius: var(--ax-radius); color: var(--ax-text-2);\n cursor: pointer; transition: all 120ms;\n}\n.ax-td-scenario:hover { border-color: var(--ax-accent); color: var(--ax-accent); }\n\n.ax-td-chat {\n flex: 1; overflow-y: auto; padding: 14px;\n display: flex; flex-direction: column; gap: 8px;\n background: var(--ax-bg);\n}\n.ax-td-chat__msg-wrap {\n display: flex; flex-direction: column; gap: 2px; max-width: 86%;\n}\n.ax-td-chat__msg-wrap.is-user { align-self: flex-end; align-items: flex-end; }\n.ax-td-chat__msg-wrap.is-bot { align-self: flex-start; }\n.ax-td-chat__bubble {\n padding: 8px 12px; border-radius: var(--ax-radius-lg);\n font-size: 12.5px; line-height: 1.55; word-wrap: break-word;\n}\n.ax-td-chat__bubble.is-user {\n background: var(--ax-accent); color: var(--ax-bg);\n border-bottom-right-radius: 3px; font-weight: 500;\n}\n.ax-td-chat__bubble.is-bot {\n background: var(--ax-surface-2); color: var(--ax-text);\n border-bottom-left-radius: 3px; border: var(--ax-border-w) solid var(--ax-border);\n}\n.ax-td-chat__meta {\n font-size: 9.5px; color: var(--ax-muted);\n font-family: var(--ax-mono); padding: 0 4px;\n}\n.ax-td-chat__typing {\n align-self: flex-start; padding: 10px 14px;\n background: var(--ax-surface-2); border: var(--ax-border-w) solid var(--ax-border);\n border-radius: var(--ax-radius-lg); border-bottom-left-radius: 3px;\n display: inline-flex; gap: 4px;\n}\n.ax-td-chat__typing span {\n width: 6px; height: 6px; background: var(--ax-muted);\n border-radius: 50%; animation: axTypingBounce 1.2s infinite;\n}\n.ax-td-chat__typing span:nth-child(2) { animation-delay: 0.15s; }\n.ax-td-chat__typing span:nth-child(3) { animation-delay: 0.3s; }\n@keyframes axTypingBounce {\n 0%, 60%, 100% { transform: translateY(0); opacity: 0.4; }\n 30% { transform: translateY(-5px); opacity: 1; }\n}\n.ax-td-input {\n padding: 10px; border-top: var(--ax-border-w) solid var(--ax-border);\n background: var(--ax-bg-elev); display: flex; gap: 6px;\n}\n.ax-td-input input {\n flex: 1; background: var(--ax-bg); border: var(--ax-border-w) solid var(--ax-border);\n border-radius: var(--ax-radius); padding: 8px 11px;\n color: var(--ax-text); font: inherit; font-size: 12.5px;\n}\n.ax-td-input input:focus { outline: none; border-color: var(--ax-accent); }\n.ax-td-input button {\n background: var(--ax-accent); color: var(--ax-bg); border: 0;\n border-radius: var(--ax-radius); padding: 0 12px;\n cursor: pointer; font-weight: 600;\n}\n.ax-td-input button:hover {\n background: color-mix(in oklch, var(--ax-accent) 88%, white);\n}\n\n/* Inline task-queue chip on bot messages */\n.ax-td-task-chip {\n display: inline-flex; align-items: center; gap: 5px;\n margin-top: 5px; padding: 3px 8px;\n background: color-mix(in oklch, var(--ax-accent) 10%, var(--ax-surface));\n border: 1px solid color-mix(in oklch, var(--ax-accent) 35%, var(--ax-border));\n border-radius: var(--ax-radius); font-size: 10px; color: var(--ax-accent);\n font-family: var(--ax-mono); letter-spacing: 0.04em;\n cursor: default; user-select: none;\n}\n.ax-td-task-chip__dot {\n width: 6px; height: 6px; border-radius: 50%;\n background: var(--ax-accent); flex-shrink: 0;\n}\n.ax-td-task-chip--queued .ax-td-task-chip__dot {\n background: var(--ax-muted);\n}\n.ax-td-task-chip__id {\n font-family: var(--ax-mono); opacity: 0.7; font-size: 9px;\n}\n\n/* Section mini-label used throughout detail pages */\n.ax-sec-label-d {\n display: flex; align-items: center; justify-content: space-between;\n margin: 20px 0 10px;\n}\n.ax-sec-label-d h3 {\n font-size: 11px; font-weight: 600; letter-spacing: 0.06em;\n text-transform: uppercase; color: var(--ax-muted);\n margin: 0; font-family: var(--ax-mono);\n}\n\n/* --- Responsive collapses for the settings primitives --- */\n@media (max-width: 1180px) {\n .ax-layout { grid-template-columns: 180px 1fr; }\n}\n@media (max-width: 760px) {\n .ax-health-strip { grid-template-columns: repeat(2, 1fr); }\n .ax-connectors { grid-template-columns: 1fr; }\n .ax-detail-grid { grid-template-columns: 1fr; }\n .ax-mesh-hero { grid-template-columns: 1fr; }\n .ax-mesh-viz { margin: 0 auto; }\n .ax-cron-hints { grid-template-columns: 1fr; }\n .ax-layout { grid-template-columns: 1fr; }\n .ax-rail { display: none; }\n .ax-stats-inline { grid-template-columns: repeat(2, 1fr); }\n .ax-activity-grid { grid-template-columns: repeat(2, 1fr); }\n .ax-log-head, .ax-log-row { grid-template-columns: 8px 80px 1fr auto; }\n .ax-log-head span:nth-child(3), .ax-log-row .ax-who { display: none; }\n .ax-grid-2, .ax-grid-3 { grid-template-columns: 1fr; }\n}`\n","// --- Page shell renderer ---\n//\n// Every dashboard page used to hand-roll its own <html><head><body> wrapper:\n// meta tags, font links, theme bootstrap script, style tags, topbar markup,\n// closing tags. That's ~60 lines of boilerplate per page, identical across\n// ~8 pages. This module centralises it.\n//\n// A page calls `renderShell({ title, activeTab, subtitle, body, css, ... })`\n// and gets back a complete HTML document with:\n// - Correct <meta>, <title>, Plex font link, theme-bootstrap script\n// - AX_TOKENS_CSS (design tokens) + AX_COMPONENTS_CSS (badge/stat/card/...)\n// - TOPBAR_CSS (chrome) always included\n// - Page-specific CSS appended after\n// - renderTopbar() at the top\n// - <main> with the page body\n// - TOPBAR_SCRIPT (theme toggle + mesh selector) at the end\n// - Page-specific <script> appended after\n//\n// Pages that want a slimmer header (e.g. /setup, which has its own simplified\n// topbar) can pass `renderHeader: \"custom\"` and provide their own <header>.\n\nimport { AX_TOKENS_CSS } from \"./tokens\"\nimport { ASSISTANT_CSS, ASSISTANT_HTML, ASSISTANT_SCRIPT } from \"./assistant\"\nimport { injectFns } from \"./inject\"\nimport { markdownToHtml } from \"@/utils/markdown-html\"\nimport { AX_COMPONENTS_CSS } from \"./components.css\"\nimport {\n TOPBAR_HEAD,\n TOPBAR_CSS,\n TOPBAR_SCRIPT,\n renderTopbar,\n type TopbarOpts,\n type TopbarPeer,\n} from \"../topbar\"\n\nexport interface ShellOpts {\n /** Browser tab title — \"AgentX · Live\", \"AgentX · Settings\", etc. */\n title: string\n /**\n * The topbar tab to highlight, OR \"custom\" to skip rendering the default\n * topbar so the page can provide its own header.\n */\n activeTab: TopbarOpts[\"activeTab\"] | \"custom\"\n /** Subtitle after the brand — \"Live\", \"Settings\", \"Setup\", \"Intent Graph\". */\n subtitle: string\n /** Page body HTML — everything inside <main>. */\n body: string\n /** Opt out of the ask-an-agent drawer (used by full-canvas surfaces). */\n noAssistant?: boolean\n /** Optional HTML inserted as a second row under the topbar. */\n subheader?: string\n /**\n * Page-specific CSS appended after the shared blocks. Use this for styles\n * that aren't worth promoting to components.css (e.g. kanban column\n * layout, graph tree indentation).\n */\n css?: string\n /** Page-specific <script> blocks (full tags, not raw JS). */\n scripts?: string\n /** Mesh peers to populate the selector. */\n peers?: TopbarPeer[]\n currentPeerId?: string\n /** If provided, replaces the default topbar entirely. */\n customHeader?: string\n /**\n * When true, omit the <main> wrapper — caller renders their own. Useful\n * for pages (e.g. boards kanban) that need a full-viewport layout without\n * the default padding.\n */\n noMain?: boolean\n /** Extra content to inject into <head> (rare — e.g. preload hints). */\n headExtras?: string\n}\n\nexport function renderShell(opts: ShellOpts): string {\n const css = `${AX_TOKENS_CSS}\\n${AX_COMPONENTS_CSS}\\n${TOPBAR_CSS}\\n${ASSISTANT_CSS}${opts.css ? \"\\n\" + opts.css : \"\"}`\n\n const header = opts.customHeader\n ?? (opts.activeTab === \"custom\"\n ? \"\"\n : renderTopbar({\n activeTab: opts.activeTab,\n subtitle: opts.subtitle,\n subheader: opts.subheader,\n peers: opts.peers,\n currentPeerId: opts.currentPeerId,\n }))\n\n const main = opts.noMain ? opts.body : `<main>${opts.body}</main>`\n\n return `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n<title>${escapeTitle(opts.title)}</title>\n${TOPBAR_HEAD}\n${opts.headExtras || \"\"}\n<style>${css}</style>\n</head>\n<body>\n${header}\n${main}\n${opts.noAssistant ? \"\" : ASSISTANT_HTML}\n${TOPBAR_SCRIPT}\n${opts.scripts || \"\"}\n${opts.noAssistant ? \"\" : `<script>${injectFns({ markdownToHtml })}${ASSISTANT_SCRIPT}</script>`}\n</body>\n</html>`\n}\n\nfunction escapeTitle(s: string): string {\n return String(s || \"\").replace(/[<>&]/g, (c) => ({ \"<\": \"&lt;\", \">\": \"&gt;\", \"&\": \"&amp;\" }[c] as string))\n}\n","// Shared small utilities for UI renderers.\n\n/**\n * HTML-escape a string so it's safe to interpolate inside attributes or text\n * content. Every page used to ship its own `esc` / `escapeHtml` / `safe` —\n * this is the canonical one.\n */\nexport function esc(s: unknown): string {\n return String(s ?? \"\").replace(/[&<>\"']/g, (c) => ({\n \"&\": \"&amp;\", \"<\": \"&lt;\", \">\": \"&gt;\", '\"': \"&quot;\", \"'\": \"&#39;\",\n }[c] as string))\n}\n\n/** Join class names, dropping falsy values. */\nexport function cx(...parts: Array<string | false | null | undefined>): string {\n return parts.filter(Boolean).join(\" \")\n}\n","// --- Shared UI components ---\n//\n// Small helpers that return HTML strings. Used by every page to produce\n// consistent badges, cards, form rows, buttons without copy-pasting.\n//\n// ### Philosophy\n//\n// - Pure functions: `string` in, `string` out. No classes, no DSL, no JSX.\n// - Caller composes: `cardHtml(children)` not `Card({children})`.\n// - Null/undefined opts render an empty string, so conditional composition\n// just inlines: `${badge(...) ?? \"\"}` without wrapper ifs.\n// - Variants are flat classnames, not object props: `badge(\"LIVE\", \"live\")`\n// not `badge({label: \"LIVE\", variant: \"live\"})`. Cheaper to read, easier\n// to grep for.\n\nimport { esc } from \"./util\"\n\n// --- Dots (status indicators) ---\n\nexport type DotKind = \"ok\" | \"live\" | \"warn\" | \"err\" | \"off\" | undefined\n\nexport function dot(kind?: DotKind, pulse = false): string {\n const classes = [\"ax-dot\", kind ? `ax-dot--${kind}` : \"\", pulse ? \"ax-dot--pulse\" : \"\"]\n .filter(Boolean).join(\" \")\n return `<span class=\"${classes}\"></span>`\n}\n\n// --- Badges (small pill labels) ---\n\nexport type BadgeKind = \"accent\" | \"live\" | \"warn\" | \"err\" | \"ghost\" | \"mono\" | undefined\n\nexport function badge(label: string, kind?: BadgeKind, extraClass = \"\"): string {\n const classes = [\"ax-badge\", kind ? `ax-badge--${kind}` : \"\", extraClass]\n .filter(Boolean).join(\" \")\n return `<span class=\"${classes}\">${esc(label)}</span>`\n}\n\n// --- Buttons ---\n\nexport interface BtnOpts {\n type?: \"button\" | \"submit\"\n primary?: boolean\n ghost?: boolean\n danger?: boolean\n id?: string\n disabled?: boolean\n extraClass?: string\n onclick?: string\n}\n\nexport function btn(label: string, opts: BtnOpts = {}): string {\n const classes = [\"ax-btn\",\n opts.primary ? \"ax-btn--primary\" : \"\",\n opts.ghost ? \"ax-btn--ghost\" : \"\",\n opts.danger ? \"ax-btn--danger\" : \"\",\n opts.extraClass || \"\",\n ].filter(Boolean).join(\" \")\n const attrs = [\n `type=\"${opts.type || \"button\"}\"`,\n `class=\"${classes}\"`,\n opts.id ? `id=\"${esc(opts.id)}\"` : \"\",\n opts.disabled ? \"disabled\" : \"\",\n opts.onclick ? `onclick=\"${esc(opts.onclick)}\"` : \"\",\n ].filter(Boolean).join(\" \")\n return `<button ${attrs}>${esc(label)}</button>`\n}\n\n// --- Stat cards (the \"NUMBER + label\" strip) ---\n\nexport interface StatOpts {\n label: string\n value: string\n sub?: string\n kind?: \"live\" | \"warn\" | \"err\"\n}\n\nexport function stat(opts: StatOpts): string {\n const cls = opts.kind ? `ax-stat ax-stat--${opts.kind}` : \"ax-stat\"\n return `<div class=\"${cls}\">\n <div class=\"ax-stat__label\">${esc(opts.label)}</div>\n <div class=\"ax-stat__value\">${esc(opts.value)}</div>\n ${opts.sub ? `<div class=\"ax-stat__sub\">${esc(opts.sub)}</div>` : \"\"}\n </div>`\n}\n\nexport function statStrip(stats: StatOpts[]): string {\n return `<div class=\"ax-statstrip\">${stats.map(stat).join(\"\")}</div>`\n}\n\n// --- Form field (label + input/textarea/select) ---\n\nexport interface FieldOpts {\n name: string\n label: string\n hint?: string\n type?: \"text\" | \"password\" | \"email\" | \"url\" | \"number\" | \"textarea\" | \"select\"\n value?: string\n placeholder?: string\n pattern?: string\n required?: boolean\n readonly?: boolean\n rows?: number\n options?: Array<{ value: string; label: string; selected?: boolean }>\n extraAttrs?: string\n}\n\nexport function field(opts: FieldOpts): string {\n const labelHtml = `<label>${esc(opts.label)}${\n opts.hint ? ` <span class=\"ax-hint\">${opts.hint}</span>` : \"\"\n }</label>`\n let controlHtml = \"\"\n const common = [\n `name=\"${esc(opts.name)}\"`,\n opts.required ? \"required\" : \"\",\n opts.readonly ? \"readonly\" : \"\",\n opts.placeholder ? `placeholder=\"${esc(opts.placeholder)}\"` : \"\",\n opts.extraAttrs || \"\",\n ].filter(Boolean).join(\" \")\n\n if (opts.type === \"textarea\") {\n controlHtml = `<textarea ${common} rows=\"${opts.rows || 3}\">${esc(opts.value || \"\")}</textarea>`\n } else if (opts.type === \"select\" && opts.options) {\n const options = opts.options.map(o =>\n `<option value=\"${esc(o.value)}\"${o.selected ? \" selected\" : \"\"}>${esc(o.label)}</option>`\n ).join(\"\")\n controlHtml = `<select ${common}>${options}</select>`\n } else {\n const type = opts.type || \"text\"\n controlHtml = `<input type=\"${type}\" ${common}`\n + (opts.value !== undefined ? ` value=\"${esc(opts.value)}\"` : \"\")\n + (opts.pattern ? ` pattern=\"${esc(opts.pattern)}\"` : \"\")\n + \" />\"\n }\n return `<div class=\"ax-field\">${labelHtml}${controlHtml}</div>`\n}\n\nexport function row(children: string[]): string {\n return `<div class=\"ax-row\">${children.join(\"\")}</div>`\n}\n\n// --- Cards ---\n\nexport function card(content: string, extraClass = \"\"): string {\n return `<div class=\"ax-card${extraClass ? \" \" + extraClass : \"\"}\">${content}</div>`\n}\n\nexport function stepCard(num: number | string, title: string, body: string): string {\n return `<section class=\"ax-step\">\n <div class=\"ax-step__head\"><span class=\"ax-step__num\">${esc(String(num))}</span>${esc(title)}</div>\n ${body}\n </section>`\n}\n\n// --- Small layout helpers ---\n\nexport function spacer(height = 12): string {\n return `<div style=\"height:${height}px\"></div>`\n}\n\n// ============================================================================\n// Settings-page primitives (from the Stitch redesign)\n// ============================================================================\n\n/** Page head — kicker + h1 + lead. Lives outside <main> so it can span wider. */\nexport interface PageHeadOpts {\n kicker?: string\n title: string\n lead?: string\n}\nexport function pageHead(opts: PageHeadOpts): string {\n return `<div class=\"ax-page-head\">\n ${opts.kicker ? `<div class=\"ax-kicker\">${esc(opts.kicker)}</div>` : \"\"}\n <h1>${esc(opts.title)}</h1>\n ${opts.lead ? `<p class=\"ax-lead\">${opts.lead}</p>` : \"\"}\n </div>`\n}\n\n/** 4-up health strip: colour-coded status cards across the top of a page. */\nexport interface HealthCardOpts {\n kind?: \"ok\" | \"warn\" | \"off\"\n num: string\n label: string\n}\nexport function healthCard(opts: HealthCardOpts): string {\n const kind = opts.kind || \"off\"\n return `<div class=\"ax-health-card\">\n <div class=\"ax-hc-dot ax-hc-dot--${kind}\"></div>\n <div class=\"ax-hc-txt\">\n <div class=\"ax-hc-num\">${esc(opts.num)}</div>\n <div class=\"ax-hc-lbl\">${esc(opts.label)}</div>\n </div>\n </div>`\n}\nexport function healthStrip(cards: HealthCardOpts[]): string {\n return `<div class=\"ax-health-strip\">${cards.map(healthCard).join(\"\")}</div>`\n}\n\n/**\n * \"What is this?\" banner. Renders a dismissable explanatory box for users\n * meeting a tab for the first time. Pass raw HTML for body — short runs\n * with <b> and <code> are the intended style.\n */\nexport interface WitOpts {\n bodyHtml: string\n /** Persist the dismissal under this key in localStorage. Optional. */\n persistKey?: string\n icon?: string\n}\nexport function witBanner(opts: WitOpts): string {\n const attr = opts.persistKey ? ` data-wit=\"${esc(opts.persistKey)}\"` : \"\"\n return `<div class=\"ax-wit\"${attr}>\n <span class=\"ax-wit__icon\">${esc(opts.icon || \"?\")}</span>\n <div>${opts.bodyHtml}</div>\n <button class=\"ax-wit__dismiss\" onclick=\"this.parentElement.style.display='none'\">×</button>\n </div>`\n}\n\n/**\n * Section header with icon chip + title + lead + optional right-aligned\n * action. Icon is raw SVG so the caller picks the glyph; feed a 24x24\n * stroked Feather-style icon for best fit.\n */\nexport interface SectionHeadOpts {\n icon: string\n title: string\n lead?: string\n actionHtml?: string\n}\nexport function sectionHead(opts: SectionHeadOpts): string {\n return `<div class=\"ax-section-head\">\n <div class=\"ax-section-head__icon\">${opts.icon}</div>\n <div class=\"ax-section-head__text\">\n <h2>${esc(opts.title)}</h2>\n ${opts.lead ? `<p class=\"ax-lead\">${esc(opts.lead)}</p>` : \"\"}\n </div>\n ${opts.actionHtml || \"\"}\n </div>`\n}\n\n/** Coloured-initial avatar — pass 1-2 letters; variant sets the tint. */\nexport type AvatarVariant = \"plain\" | \"teal\" | \"coral\" | \"amber\" | \"blue\"\nexport function avatar(initials: string, variant: AvatarVariant = \"plain\"): string {\n const cls = variant === \"plain\" ? \"ax-avatar\" : `ax-avatar ax-avatar--${variant}`\n return `<div class=\"${cls}\">${esc(initials)}</div>`\n}\n\n/**\n * Status pill with coloured dot — \"live\", \"idle\", \"receiving\", \"stale\".\n * Different from {@link badge}: pill has a rounded shape (10px radius) and\n * a leading dot. Prefer for attached-to-name status labels.\n */\nexport type PillKind = \"ok\" | \"warn\" | \"off\" | \"info\" | \"plain\"\nexport function pill(label: string, kind: PillKind = \"plain\", withDot = true): string {\n const cls = kind === \"plain\" ? \"ax-pill\" : `ax-pill ax-pill--${kind}`\n const dot = withDot && kind !== \"plain\" && kind !== \"off\" ? `<span class=\"ax-pill__dot\"></span>` : \"\"\n return `<span class=\"${cls}\">${dot}${esc(label)}</span>`\n}\n\n/**\n * Row card with colored avatar + info + actions + optional expandable\n * details section. Details are hidden behind `.is-open` on the card;\n * toggleCard() on the client flips it.\n */\nexport interface RowCardOpts {\n avatarHtml?: string\n /** Left block: title + pills + sub-line. Raw HTML. */\n infoHtml: string\n /** Right-aligned action cluster. Raw HTML (usually btns). */\n actionsHtml?: string\n /** Collapsible details. When provided, the card is collapsible. */\n detailsHtml?: string\n /** Initial open state. Default false. */\n open?: boolean\n /** Extra classes on the card wrapper. */\n extraClass?: string\n}\nexport function rowCard(opts: RowCardOpts): string {\n const cls = [\"ax-row-card\", opts.open ? \"is-open\" : \"\", opts.extraClass || \"\"]\n .filter(Boolean).join(\" \")\n return `<div class=\"${cls}\">\n <div class=\"ax-row-card__top\">\n ${opts.avatarHtml || \"\"}\n <div class=\"ax-row-card__info\">${opts.infoHtml}</div>\n ${opts.actionsHtml ? `<div class=\"ax-row-card__actions\">${opts.actionsHtml}</div>` : \"\"}\n </div>\n ${opts.detailsHtml ? `<div class=\"ax-row-card__details\">${opts.detailsHtml}</div>` : \"\"}\n </div>`\n}\n\n/**\n * Channel/webhook-style connector card — big logo, name, status pill,\n * description, foot row. Clickable (caller attaches onclick / wraps\n * in <a>).\n */\nexport interface ConnectorOpts {\n logoHtml: string\n name: string\n sub: string\n statusHtml?: string\n desc: string\n footLeft?: string\n footCta?: string\n isOn?: boolean\n onclick?: string\n extraStyle?: string\n}\nexport function connector(opts: ConnectorOpts): string {\n const cls = opts.isOn ? \"ax-connector is-on\" : \"ax-connector\"\n const onclick = opts.onclick ? ` onclick=\"${esc(opts.onclick)}\"` : \"\"\n const style = opts.extraStyle ? ` style=\"${esc(opts.extraStyle)}\"` : \"\"\n return `<div class=\"${cls}\"${onclick}${style}>\n <div class=\"ax-connector__top\">\n ${opts.logoHtml}\n <div class=\"ax-connector__meta\">\n <div class=\"ax-connector__name\">${esc(opts.name)}</div>\n <div class=\"ax-connector__sub\">${esc(opts.sub)}</div>\n </div>\n ${opts.statusHtml ? `<div class=\"ax-connector__status\">${opts.statusHtml}</div>` : \"\"}\n </div>\n <div class=\"ax-connector__desc\">${opts.desc}</div>\n <div class=\"ax-connector__foot\">\n <span>${esc(opts.footLeft || \"\")}</span>\n ${opts.footCta\n ? `<span class=\"ax-connector__cta\">${esc(opts.footCta)} <svg viewBox=\"0 0 12 12\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\"><path d=\"M4 2l4 4-4 4\"/></svg></span>`\n : \"\"}\n </div>\n </div>`\n}\n\n/** Empty state used when a tab has nothing yet. */\nexport interface EmptyOpts {\n title: string\n desc?: string\n actionHtml?: string\n}\nexport function emptyState(opts: EmptyOpts): string {\n return `<div class=\"ax-empty-card\" style=\"text-align:center;padding:42px 22px;background:var(--ax-surface);border:1px dashed var(--ax-border-2);border-radius:var(--ax-radius-lg);color:var(--ax-muted)\">\n <h3 style=\"margin:0 0 4px;color:var(--ax-text);font-size:15px;font-weight:600\">${esc(opts.title)}</h3>\n ${opts.desc ? `<p style=\"margin:0 0 16px;font-size:13px;line-height:1.55;max-width:380px;margin-left:auto;margin-right:auto\">${esc(opts.desc)}</p>` : \"\"}\n ${opts.actionHtml || \"\"}\n </div>`\n}\n\n/** Mini-label used to divide sections inside a tab. */\nexport interface SecLabelOpts {\n label: string\n rightHtml?: string\n}\nexport function secLabel(opts: SecLabelOpts): string {\n return `<div class=\"ax-sec-label\"><h3>${esc(opts.label)}</h3>${opts.rightHtml || \"\"}</div>`\n}\n\n/** Toast element + helper script. Include once per page that uses it. */\nexport const TOAST_HTML = `<div class=\"ax-toast\" id=\"ax-toast\">\n <svg class=\"ax-toast__icon\" width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><path d=\"M20 6L9 17l-5-5\"/></svg>\n <span id=\"ax-toast-msg\">Copied</span>\n</div>`\nexport const TOAST_SCRIPT = `\n let __axToastTimer;\n window.showToast = function(msg) {\n const el = document.getElementById('ax-toast');\n const m = document.getElementById('ax-toast-msg');\n if (!el || !m) return;\n m.textContent = msg;\n el.classList.add('is-show');\n clearTimeout(__axToastTimer);\n __axToastTimer = setTimeout(function(){ el.classList.remove('is-show'); }, 1800);\n };\n window.copyText = function(_btn, text) {\n try { navigator.clipboard.writeText(text); } catch (e) {}\n window.showToast('Copied to clipboard');\n };`\n\n/** Client script that wires `.ax-row-card` expand/collapse on any button\n * that calls `toggleCard(this)`. Include once per page. */\nexport const ROW_CARD_SCRIPT = `\n window.toggleCard = function(btn) {\n const card = btn.closest('.ax-row-card');\n if (card) card.classList.toggle('is-open');\n };`\n"],"mappings":"6DA6BO,IAAMA,EAAgB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;yCChBtB,IAAMC,EAAgB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAkHhBC,EAAiB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,UAiCjBC,EAAmB,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;ECnJhC,SAASC,EAAUC,EAAuC,CAM/D,MADa,qGACC,OAAO,QAAQA,CAAG,EAC7B,IAAI,CAAC,CAACC,EAAMC,CAAE,IAAM,SAASD,yBAA4B,KAAK,UAAUA,CAAI,KAAKC,EAAG,SAAS,KAAK,EAClG,KAAK,EAAE,CACZ,CCPO,SAASC,EAAeC,EAAcC,EAAwB,CAAC,EAAW,CAM/E,IAAIC,EAAO,OAAOF,GAAQ,EAAE,EACzB,QAAQ,KAAM,OAAO,EAAE,QAAQ,KAAM,MAAM,EAAE,QAAQ,KAAM,MAAM,EACjE,QAAQ,KAAM,QAAQ,EAAE,QAAQ,KAAM,OAAO,EAGhD,OAAAE,EAAOA,EAAK,QAAQ,2BAA4B,CAACC,EAAIC,EAAMC,IACzD,0BAA0BD,MAASC,EAAK,KAAK,gBAAgB,EAG/DH,EAAOA,EAAK,QAAQ,aAAc,iBAAiB,EAGnDA,EAAOA,EAAK,QAAQ,+CAAgD,CAACC,EAAIG,EAAQC,EAAMC,IAAS,CAC9F,IAAMC,EAAMH,EAAO,MAAM,GAAG,EAAE,OAAQI,GAAcA,EAAE,KAAK,CAAC,EAAE,IAAKA,GAAc,OAAOA,EAAE,KAAK,QAAQ,EAAE,KAAK,EAAE,EAC1GC,EAAOH,EAAK,KAAK,EAAE,MAAM;AAAA,CAAI,EAAE,IAAKI,GAEjC,OADKA,EAAI,MAAM,GAAG,EAAE,OAAQ,GAAc,EAAE,KAAK,CAAC,EAAE,IAAK,GAAc,OAAO,EAAE,KAAK,QAAQ,EAAE,KAAK,EAAE,QAE9G,EAAE,KAAK,EAAE,EACV,MAAO,qBAAqBH,wBAA0BE,mBACxD,CAAC,EAGDT,EAAOA,EAAK,QAAQ,gBAAiB,aAAa,EAClDA,EAAOA,EAAK,QAAQ,eAAgB,aAAa,EACjDA,EAAOA,EAAK,QAAQ,cAAe,aAAa,EAChDA,EAAOA,EAAK,QAAQ,aAAc,aAAa,EAG/CA,EAAOA,EAAK,QAAQ,iBAAkB,qBAAqB,EAC3DA,EAAOA,EAAK,QAAQ,aAAc,aAAa,EAO/CA,EAAOA,EAAK,QAAQ,oBAAqB,CAACC,EAAIU,IAAkB,CAC9D,GAAM,CAACC,EAAWC,CAAU,EAAIF,EAAM,MAAM,IAAK,CAAC,EAC5CG,EAASF,EAAU,KAAK,EAAE,QAAQ,MAAO,EAAE,EAC3CG,GAAWF,GAAcD,GAAW,KAAK,EAE/C,OAAOb,EAAK,SAAWA,EAAK,SAASe,EAAQC,CAAO,EAAIA,CAC1D,CAAC,EAGDf,EAAOA,EAAK,QAAQ,2BAA4B,+DAA+D,EAG/GA,EAAOA,EAAK,QAAQ,2BAA6BgB,GAExC,eADOA,EAAM,MAAM;AAAA,CAAI,EAAE,IAAIC,GAAKA,EAAE,QAAQ,QAAS,EAAE,CAAC,EAAE,KAAK,MAAM,gBAE7E,EAGDjB,EAAOA,EAAK,QAAQ,4BAA6B,CAACC,EAAIiB,EAAKC,IACzD,oBAAoBD,EAAI,YAAY,IAAM,IAAM,SAAM,YAAOC,QAAW,EAG1EnB,EAAOA,EAAK,QAAQ,gBAAiB,aAAa,EAGlDA,EAAOA,EAAK,QAAQ,iBAAkB,8BAA8B,EAMpEA,EAAOA,EAAK,QAAQ,0EAA2E,aAAa,EAI5GA,EAAOA,EAAK,QAAQ,iDAAkD,aAAa,EAGnFA,EAAOA,EAAK,QAAQ,qBAAsB,EAAE,EAG5CA,EAAOA,EAAK,QAAQ,UAAW,MAAM,EAIrCA,EAAOA,EAAK,QAAQ,SAAU,SAAS,EACvCA,EAAO,MAAMA,QAEbA,EAAOA,EAAK,QAAQ,sHAAuH,IAAI,EAC/IA,EAAOA,EAAK,QAAQ,sBAAuB,MAAM,EAEjDA,EAAOA,EAAK,QAAQ,eAAgB,EAAE,EAEtCA,EAAOA,EAAK,QAAQ,wCAAyC,IAAI,EACjEA,EAAOA,EAAK,QAAQ,6CAA8C,IAAI,EACtEA,EAAOA,EAAK,QAAQ,eAAgB,EAAE,EAE/BA,CACT,CC1GO,IAAMoB,EAAoB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;GCiE1B,SAASC,EAAYC,EAAyB,CACnD,IAAMC,EAAM,GAAGC;AAAA,EAAkBC;AAAA,EAAsBC;AAAA,EAAeC,IAAgBL,EAAK,IAAM;AAAA,EAAOA,EAAK,IAAM,KAE7GM,EAASN,EAAK,eACdA,EAAK,YAAc,SACnB,GACAO,EAAa,CACX,UAAWP,EAAK,UAChB,SAAUA,EAAK,SACf,UAAWA,EAAK,UAChB,MAAOA,EAAK,MACZ,cAAeA,EAAK,aACtB,CAAC,GAEDQ,EAAOR,EAAK,OAASA,EAAK,KAAO,SAASA,EAAK,cAErD,MAAO;AAAA;AAAA;AAAA;AAAA;AAAA,SAKAS,EAAYT,EAAK,KAAK;AAAA,EAC7BU;AAAA,EACAV,EAAK,YAAc;AAAA,SACZC;AAAA;AAAA;AAAA,EAGPK;AAAA,EACAE;AAAA,EACAR,EAAK,YAAc,GAAKW;AAAA,EACxBC;AAAA,EACAZ,EAAK,SAAW;AAAA,EAChBA,EAAK,YAAc,GAAK,WAAWa,EAAU,CAAE,eAAAC,CAAe,CAAC,IAAIC;AAAA;AAAA,QAGrE,CAEA,SAASN,EAAYO,EAAmB,CACtC,OAAO,OAAOA,GAAK,EAAE,EAAE,QAAQ,SAAWC,IAAO,CAAE,IAAK,OAAQ,IAAK,OAAQ,IAAK,OAAQ,GAAEA,CAAC,CAAY,CAC3G,CC1GO,SAASC,EAAIC,EAAoB,CACtC,OAAO,OAAOA,GAAK,EAAE,EAAE,QAAQ,WAAaC,IAAO,CACjD,IAAK,QAAS,IAAK,OAAQ,IAAK,OAAQ,IAAK,SAAU,IAAK,OAC9D,GAAEA,CAAC,CAAY,CACjB,CCuCO,SAASC,EAAIC,EAAeC,EAAgB,CAAC,EAAW,CAC7D,IAAMC,EAAU,CAAC,SACfD,EAAK,QAAU,kBAAoB,GACnCA,EAAK,MAAQ,gBAAkB,GAC/BA,EAAK,OAAS,iBAAmB,GACjCA,EAAK,YAAc,EACrB,EAAE,OAAO,OAAO,EAAE,KAAK,GAAG,EAQ1B,MAAO,WAPO,CACZ,SAASA,EAAK,MAAQ,YACtB,UAAUC,KACVD,EAAK,GAAK,OAAOE,EAAIF,EAAK,EAAE,KAAO,GACnCA,EAAK,SAAW,WAAa,GAC7BA,EAAK,QAAU,YAAYE,EAAIF,EAAK,OAAO,KAAO,EACpD,EAAE,OAAO,OAAO,EAAE,KAAK,GAAG,KACCE,EAAIH,CAAK,YACtC,CAyCO,SAASI,EAAMC,EAAyB,CAC7C,IAAMC,EAAY,UAAUC,EAAIF,EAAK,KAAK,IACxCA,EAAK,KAAO,0BAA0BA,EAAK,cAAgB,aAEzDG,EAAc,GACZC,EAAS,CACb,SAASF,EAAIF,EAAK,IAAI,KACtBA,EAAK,SAAW,WAAa,GAC7BA,EAAK,SAAW,WAAa,GAC7BA,EAAK,YAAc,gBAAgBE,EAAIF,EAAK,WAAW,KAAO,GAC9DA,EAAK,YAAc,EACrB,EAAE,OAAO,OAAO,EAAE,KAAK,GAAG,EAE1B,GAAIA,EAAK,OAAS,WAChBG,EAAc,aAAaC,WAAgBJ,EAAK,MAAQ,MAAME,EAAIF,EAAK,OAAS,EAAE,uBACzEA,EAAK,OAAS,UAAYA,EAAK,QAAS,CACjD,IAAMK,EAAUL,EAAK,QAAQ,IAAIM,GAC/B,kBAAkBJ,EAAII,EAAE,KAAK,KAAKA,EAAE,SAAW,YAAc,MAAMJ,EAAII,EAAE,KAAK,YAChF,EAAE,KAAK,EAAE,EACTH,EAAc,WAAWC,KAAUC,kBAGnCF,EAAc,gBADDH,EAAK,MAAQ,WACaI,KAClCJ,EAAK,QAAU,OAAY,WAAWE,EAAIF,EAAK,KAAK,KAAO,KAC3DA,EAAK,QAAU,aAAaE,EAAIF,EAAK,OAAO,KAAO,IACpD,MAEN,MAAO,yBAAyBC,IAAYE,SAC9C,CAEO,SAASI,EAAIC,EAA4B,CAC9C,MAAO,uBAAuBA,EAAS,KAAK,EAAE,SAChD,CAQO,SAASC,EAASC,EAAsBC,EAAeC,EAAsB,CAClF,MAAO;AAAA,4DACmDC,EAAI,OAAOH,CAAG,CAAC,WAAWG,EAAIF,CAAK;AAAA,MACzFC;AAAA,aAEN,CAkBO,SAASE,EAASC,EAA4B,CACnD,MAAO;AAAA,MACHA,EAAK,OAAS,0BAA0BC,EAAID,EAAK,MAAM,UAAY;AAAA,UAC/DC,EAAID,EAAK,KAAK;AAAA,MAClBA,EAAK,KAAO,sBAAsBA,EAAK,WAAa;AAAA,SAE1D,CAQO,SAASE,EAAWF,EAA8B,CAEvD,MAAO;AAAA,uCADMA,EAAK,MAAQ;AAAA;AAAA,+BAIGC,EAAID,EAAK,GAAG;AAAA,+BACZC,EAAID,EAAK,KAAK;AAAA;AAAA,SAG7C,CACO,SAASG,EAAYC,EAAiC,CAC3D,MAAO,gCAAgCA,EAAM,IAAIF,CAAU,EAAE,KAAK,EAAE,SACtE,CAaO,SAASG,EAAUL,EAAuB,CAE/C,MAAO,sBADMA,EAAK,WAAa,cAAcC,EAAID,EAAK,UAAU,KAAO;AAAA,iCAExCC,EAAID,EAAK,MAAQ,GAAG;AAAA,WAC1CA,EAAK;AAAA;AAAA,SAGhB,CAaO,SAASM,EAAYN,EAA+B,CACzD,MAAO;AAAA,yCACgCA,EAAK;AAAA;AAAA,YAElCC,EAAID,EAAK,KAAK;AAAA,QAClBA,EAAK,KAAO,sBAAsBC,EAAID,EAAK,IAAI,QAAU;AAAA;AAAA,MAE3DA,EAAK,YAAc;AAAA,SAEzB,CA+GO,SAASO,EAASC,EAA4B,CACnD,MAAO,iCAAiCC,EAAID,EAAK,KAAK,SAASA,EAAK,WAAa,UACnF,CAGO,IAAME,EAAa;AAAA;AAAA;AAAA,QAIbC,EAAe;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAkBfC,EAAkB;AAAA;AAAA;AAAA;","names":["AX_TOKENS_CSS","ASSISTANT_CSS","ASSISTANT_HTML","ASSISTANT_SCRIPT","injectFns","fns","name","fn","markdownToHtml","text","opts","html","_m","lang","code","header","_sep","body","ths","c","rows","row","inner","rawTarget","rawDisplay","target","display","block","l","chk","rest","AX_COMPONENTS_CSS","renderShell","opts","css","AX_TOKENS_CSS","AX_COMPONENTS_CSS","TOPBAR_CSS","ASSISTANT_CSS","header","renderTopbar","main","escapeTitle","TOPBAR_HEAD","ASSISTANT_HTML","TOPBAR_SCRIPT","injectFns","markdownToHtml","ASSISTANT_SCRIPT","s","c","esc","s","c","btn","label","opts","classes","esc","field","opts","labelHtml","esc","controlHtml","common","options","o","row","children","stepCard","num","title","body","esc","pageHead","opts","esc","healthCard","healthStrip","cards","witBanner","sectionHead","secLabel","opts","esc","TOAST_HTML","TOAST_SCRIPT","ROW_CARD_SCRIPT"]}
@@ -1,11 +1,11 @@
1
- var m="claude-haiku-4-20250514",f=`You are a memory extraction system for an AI agent. Given a conversation exchange, extract facts worth remembering for future conversations across different chat sessions.
1
+ var d="claude-haiku-4-20250514",f=`You are a memory extraction system for an AI agent. Given a conversation exchange, extract facts worth remembering for future conversations across different chat sessions.
2
2
 
3
3
  Extract ONLY facts that would help the agent in FUTURE, DIFFERENT conversations:
4
4
  - Credentials, tokens, API keys shared by the user
5
5
  - User preferences and instructions ("never do X", "always use Y")
6
6
  - Commitments the agent made ("I will deploy by Friday")
7
- - Relationships ("Anis is the admin", "Nadia handles content")
8
- - Task state ("GitLab tokens need deploying to clawd-server")
7
+ - Relationships ("Alex is the admin", "Marketing handles content")
8
+ - Task state ("GitLab tokens need deploying to peer-server")
9
9
  - Important facts about infrastructure, config, or processes
10
10
 
11
11
  SKIP:
@@ -17,7 +17,7 @@ SKIP:
17
17
  For each fact, output a JSON array:
18
18
  [{"category":"fact|secret|preference|commitment|task-state","content":"concise fact, 1-2 sentences","keywords":["keyword1","keyword2"]}]
19
19
 
20
- If nothing worth remembering, output: []`;async function h(t,s,a,n,r){if(s.length<20&&a.length<50)return;let{createProvider:i}=await import("./providers-L27BJ5A6.js"),c=(await i("claude").generate([{role:"system",content:f},{role:"user",content:`${n.sender} (via ${n.channel}): ${s}
20
+ If nothing worth remembering, output: []`;async function h(t,s,a,n,r){if(s.length<20&&a.length<50)return;let{createProvider:i}=await import("./providers-KO2DODIO.js"),c=(await i("claude").generate([{role:"system",content:f},{role:"user",content:`${n.sender} (via ${n.channel}): ${s}
21
21
 
22
- Agent: ${a}`}],{model:m,maxTokens:512})).content.trim().match(/\[[\s\S]*\]/);if(!c)return;let o=JSON.parse(c[0]);if(!Array.isArray(o)||o.length===0)return;let d=new Date().toISOString().slice(0,10);for(let e of o)r.hasSimilar(t,e.content)||r.addMemory(t,{agentId:t,category:e.category,content:e.content,keywords:e.keywords||[],source:{...n,date:d},expiresAt:e.category==="task-state"?new Date(Date.now()+7*24*60*60*1e3).toISOString():void 0});Math.random()<.02&&r.prune(t)}export{h as a};
23
- //# sourceMappingURL=chunk-5U35WAZF.js.map
22
+ Agent: ${a}`}],{model:d,maxTokens:512})).content.trim().match(/\[[\s\S]*\]/);if(!c)return;let o=JSON.parse(c[0]);if(!Array.isArray(o)||o.length===0)return;let m=new Date().toISOString().slice(0,10);for(let e of o)r.hasSimilar(t,e.content)||r.addMemory(t,{agentId:t,category:e.category,content:e.content,keywords:e.keywords||[],source:{...n,date:m},expiresAt:e.category==="task-state"?new Date(Date.now()+7*24*60*60*1e3).toISOString():void 0});Math.random()<.02&&r.prune(t)}export{h as a};
23
+ //# sourceMappingURL=chunk-77OV7346.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/agents/memory-extract.ts"],"sourcesContent":["import type { MemoryStore } from \"./memory-store\"\n\n// --- Haiku-powered memory extraction ---\n// Fire-and-forget after each agent response.\n// Extracts memorable facts and writes them to the persistent store.\n\nconst EXTRACTION_MODEL = \"claude-haiku-4-20250514\"\n\nconst EXTRACTION_PROMPT = `You are a memory extraction system for an AI agent. Given a conversation exchange, extract facts worth remembering for future conversations across different chat sessions.\n\nExtract ONLY facts that would help the agent in FUTURE, DIFFERENT conversations:\n- Credentials, tokens, API keys shared by the user\n- User preferences and instructions (\"never do X\", \"always use Y\")\n- Commitments the agent made (\"I will deploy by Friday\")\n- Relationships (\"Alex is the admin\", \"Marketing handles content\")\n- Task state (\"GitLab tokens need deploying to peer-server\")\n- Important facts about infrastructure, config, or processes\n\nSKIP:\n- Routine greetings, acknowledgments, status updates\n- Information derivable from code or config files\n- Transient conversation flow (\"let me check\", \"here's what I found\")\n- Facts already obvious from the agent's system prompt\n\nFor each fact, output a JSON array:\n[{\"category\":\"fact|secret|preference|commitment|task-state\",\"content\":\"concise fact, 1-2 sentences\",\"keywords\":[\"keyword1\",\"keyword2\"]}]\n\nIf nothing worth remembering, output: []`\n\nexport async function extractMemories(\n agentId: string,\n userMessage: string,\n agentResponse: string,\n source: { channel: string; chatId: string; sender: string },\n store: MemoryStore,\n): Promise<void> {\n // Skip very short exchanges (unlikely to contain memorable facts)\n if (userMessage.length < 20 && agentResponse.length < 50) return\n\n const { createProvider } = await import(\"@/agent/providers\")\n const provider = createProvider(\"claude\")\n\n const result = await provider.generate(\n [\n { role: \"system\", content: EXTRACTION_PROMPT },\n {\n role: \"user\",\n content: `${source.sender} (via ${source.channel}): ${userMessage}\\n\\nAgent: ${agentResponse}`,\n },\n ],\n { model: EXTRACTION_MODEL, maxTokens: 512 },\n )\n\n // Parse JSON array from response\n const content = result.content.trim()\n const jsonMatch = content.match(/\\[[\\s\\S]*\\]/)\n if (!jsonMatch) return\n\n const facts = JSON.parse(jsonMatch[0]) as Array<{\n category: string\n content: string\n keywords: string[]\n }>\n\n if (!Array.isArray(facts) || facts.length === 0) return\n\n const date = new Date().toISOString().slice(0, 10)\n\n for (const fact of facts) {\n // Skip duplicates\n if (store.hasSimilar(agentId, fact.content)) continue\n\n store.addMemory(agentId, {\n agentId,\n category: fact.category as any,\n content: fact.content,\n keywords: fact.keywords || [],\n source: { ...source, date },\n expiresAt: fact.category === \"task-state\"\n ? new Date(Date.now() + 7 * 24 * 60 * 60 * 1000).toISOString()\n : undefined,\n })\n }\n\n // Prune occasionally (every ~50 calls, probabilistic)\n if (Math.random() < 0.02) {\n store.prune(agentId)\n }\n}\n"],"mappings":"AAMA,IAAMA,EAAmB,0BAEnBC,EAAoB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,0CAqB1B,eAAsBC,EACpBC,EACAC,EACAC,EACAC,EACAC,EACe,CAEf,GAAIH,EAAY,OAAS,IAAMC,EAAc,OAAS,GAAI,OAE1D,GAAM,CAAE,eAAAG,CAAe,EAAI,KAAM,QAAO,yBAAmB,EAgBrDC,GAbS,MAFED,EAAe,QAAQ,EAEV,SAC5B,CACE,CAAE,KAAM,SAAU,QAASP,CAAkB,EAC7C,CACE,KAAM,OACN,QAAS,GAAGK,EAAO,eAAeA,EAAO,aAAaF;AAAA;AAAA,SAAyBC,GACjF,CACF,EACA,CAAE,MAAOL,EAAkB,UAAW,GAAI,CAC5C,GAGuB,QAAQ,KAAK,EACV,MAAM,aAAa,EAC7C,GAAI,CAACS,EAAW,OAEhB,IAAMC,EAAQ,KAAK,MAAMD,EAAU,CAAC,CAAC,EAMrC,GAAI,CAAC,MAAM,QAAQC,CAAK,GAAKA,EAAM,SAAW,EAAG,OAEjD,IAAMC,EAAO,IAAI,KAAK,EAAE,YAAY,EAAE,MAAM,EAAG,EAAE,EAEjD,QAAWC,KAAQF,EAEbH,EAAM,WAAWJ,EAASS,EAAK,OAAO,GAE1CL,EAAM,UAAUJ,EAAS,CACvB,QAAAA,EACA,SAAUS,EAAK,SACf,QAASA,EAAK,QACd,SAAUA,EAAK,UAAY,CAAC,EAC5B,OAAQ,CAAE,GAAGN,EAAQ,KAAAK,CAAK,EAC1B,UAAWC,EAAK,WAAa,aACzB,IAAI,KAAK,KAAK,IAAI,EAAI,EAAI,GAAK,GAAK,GAAK,GAAI,EAAE,YAAY,EAC3D,MACN,CAAC,EAIC,KAAK,OAAO,EAAI,KAClBL,EAAM,MAAMJ,CAAO,CAEvB","names":["EXTRACTION_MODEL","EXTRACTION_PROMPT","extractMemories","agentId","userMessage","agentResponse","source","store","createProvider","jsonMatch","facts","date","fact"]}
@@ -0,0 +1,2 @@
1
+ import{a as c,b as d,f as u}from"./chunk-F4HZTZHW.js";u();var f="wiki-rerank",k=30,p=900;function b(t){let r=t.excerptChars??p;return{query:t.query,candidates:t.candidates.map(e=>({id:e.id,title:e.title,tags:e.tags?.length?e.tags.join(", "):null,excerpt:l(e.excerpt,r)}))}}function w(t){let r={};for(let e of t)r[e.id]=e.title;return{best:d(r,"Which candidate answers the query?"),answerable:c("Do any of the candidates answer the query?")}}function x(t,r){let e=t.best,s={};for(let n of r)s[n.id]=e.probabilities[n.id]??0;let o=new Map(r.map((n,a)=>[n.id,a]));return{ranked:[...r.map(n=>n.id)].sort((n,a)=>{let i=s[a]-s[n];return i!==0?i:(o.get(n)??0)-(o.get(a)??0)}),scores:s,answerable:t.answerable.noul,confidence:e.confidence}}function l(t,r){let e=String(t??"").replace(/\s+/g," ").trim();return e.length>r?e.slice(0,r):e}export{f as a,k as b,b as c,w as d,x as e};
2
+ //# sourceMappingURL=chunk-AJ5RMU7X.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/decisions/seats/wiki-rerank.ts"],"sourcesContent":["import { choice, noul } from \"../questions\"\nimport type { AnswersFor, ChoiceAnswer, NoulAnswer, StateValue } from \"../types\"\n\n// Re-ranking a BM25 shortlist.\n//\n// Measured on the operator's own wiki (11,656 articles, 12 queries):\n// BM25 alone gets R@1 33.3%, R@3 41.7%. Two thirds of queries do not put\n// the right article first. BM25 is a bag-of-words match, so it does well\n// on \"gitlab token expired\" and badly on \"the bot keeps repeating itself\n// what is wrong\" — the synonym cases score 33% / 33%.\n//\n// Shape follows TypeSafe's semantic_find cookbook rather than their\n// rerank one. Rerank scores each query-candidate pair separately, which\n// is N calls; semantic_find scores 218 line ids against one query with a\n// single Choice, and the per-option probabilities ARE the ranking. One\n// call for the whole shortlist, and the candidates are compared against\n// each other rather than each judged alone — which is what ranking means.\n//\n// The companion Noul is the part that keeps this honest. A Choice must\n// return something: asked which of 30 articles answers a question the\n// wiki has no answer to, it will still name one, with a confident-looking\n// probability. `answerable` is the question that can say no, and code\n// reads it first.\n\nexport const WIKI_RERANK_SEAT = \"wiki-rerank\"\n\n// Measured on the 12-query eval, sweeping candidate count against\n// excerpt length under a fixed state budget:\n//\n// k=120 x 150 chars R@1 25.0% (worse than BM25's 33.3%)\n// k=60 x 300 chars R@1 41.7%\n// k=30 x 600 chars R@1 41.7%\n// k=30 x 900 chars R@1 50.0% <- default\n// k=20 x 1100 chars R@1 50.0%\n//\n// Candidates and excerpt trade against one another, and excerpt wins.\n// More candidates raises the ceiling — what the shortlist could contain —\n// while starving the re-ranker of the text it needs to rank with. At 150\n// characters a candidate is barely more than its title, and the result is\n// worse than no re-ranking at all. Raise k only alongside the state\n// budget, never instead of the excerpt.\nexport const DEFAULT_SHORTLIST = 30\nexport const DEFAULT_EXCERPT_CHARS = 900\n\nexport interface RerankCandidate {\n id: string\n title: string\n tags?: string[]\n excerpt: string\n}\n\nexport interface RerankInput {\n query: string\n candidates: RerankCandidate[]\n /** Per-candidate excerpt budget. See DEFAULT_EXCERPT_CHARS — this is\n * the lever that actually moves accuracy. */\n excerptChars?: number\n}\n\nexport function rerankState(input: RerankInput): StateValue {\n const budget = input.excerptChars ?? DEFAULT_EXCERPT_CHARS\n return {\n query: input.query,\n candidates: input.candidates.map((c) => ({\n id: c.id,\n title: c.title,\n tags: c.tags?.length ? c.tags.join(\", \") : null,\n excerpt: clip(c.excerpt, budget),\n })),\n }\n}\n\n/** Built per call, because the option set IS the candidate set. */\nexport function rerankQuestions(candidates: RerankCandidate[]) {\n const criteria: Record<string, string> = {}\n for (const c of candidates) criteria[c.id] = c.title\n return {\n best: choice(criteria, \"Which candidate answers the query?\"),\n answerable: noul(\"Do any of the candidates answer the query?\"),\n }\n}\n\nexport type RerankAnswers = AnswersFor<ReturnType<typeof rerankQuestions>>\n\nexport interface RerankResult {\n /** Candidate ids, best first, by the Choice's per-option probability. */\n ranked: string[]\n scores: Record<string, number>\n /** P(the shortlist contains an answer at all). */\n answerable: number\n /** The Choice's own confidence — flat means it could not separate them. */\n confidence: number\n}\n\n/**\n * Turn one answer into a ranking.\n *\n * Ties keep the incoming order, so a Choice that genuinely cannot\n * separate the candidates degrades to BM25's ranking rather than to an\n * arbitrary one.\n */\nexport function toRanking(\n answers: RerankAnswers,\n candidates: RerankCandidate[],\n): RerankResult {\n const best = answers.best as ChoiceAnswer\n const scores: Record<string, number> = {}\n for (const c of candidates) scores[c.id] = best.probabilities[c.id] ?? 0\n\n const order = new Map(candidates.map((c, i) => [c.id, i]))\n const ranked = [...candidates.map((c) => c.id)].sort((a, b) => {\n const diff = scores[b] - scores[a]\n return diff !== 0 ? diff : (order.get(a) ?? 0) - (order.get(b) ?? 0)\n })\n\n return {\n ranked,\n scores,\n answerable: (answers.answerable as NoulAnswer).noul,\n confidence: best.confidence,\n }\n}\n\nfunction clip(text: string, max: number): string {\n const flat = String(text ?? \"\").replace(/\\s+/g, \" \").trim()\n return flat.length > max ? flat.slice(0, max) : flat\n}\n"],"mappings":"sDAAAA,IAwBO,IAAMC,EAAmB,cAiBnBC,EAAoB,GACpBC,EAAwB,IAiB9B,SAASC,EAAYC,EAAgC,CAC1D,IAAMC,EAASD,EAAM,cAAgBF,EACrC,MAAO,CACL,MAAOE,EAAM,MACb,WAAYA,EAAM,WAAW,IAAKE,IAAO,CACvC,GAAIA,EAAE,GACN,MAAOA,EAAE,MACT,KAAMA,EAAE,MAAM,OAASA,EAAE,KAAK,KAAK,IAAI,EAAI,KAC3C,QAASC,EAAKD,EAAE,QAASD,CAAM,CACjC,EAAE,CACJ,CACF,CAGO,SAASG,EAAgBC,EAA+B,CAC7D,IAAMC,EAAmC,CAAC,EAC1C,QAAWJ,KAAKG,EAAYC,EAASJ,EAAE,EAAE,EAAIA,EAAE,MAC/C,MAAO,CACL,KAAMK,EAAOD,EAAU,oCAAoC,EAC3D,WAAYE,EAAK,4CAA4C,CAC/D,CACF,CAqBO,SAASC,EACdC,EACAL,EACc,CACd,IAAMM,EAAOD,EAAQ,KACfE,EAAiC,CAAC,EACxC,QAAWV,KAAKG,EAAYO,EAAOV,EAAE,EAAE,EAAIS,EAAK,cAAcT,EAAE,EAAE,GAAK,EAEvE,IAAMW,EAAQ,IAAI,IAAIR,EAAW,IAAI,CAACH,EAAGY,IAAM,CAACZ,EAAE,GAAIY,CAAC,CAAC,CAAC,EAMzD,MAAO,CACL,OANa,CAAC,GAAGT,EAAW,IAAKH,GAAMA,EAAE,EAAE,CAAC,EAAE,KAAK,CAACa,EAAGC,IAAM,CAC7D,IAAMC,EAAOL,EAAOI,CAAC,EAAIJ,EAAOG,CAAC,EACjC,OAAOE,IAAS,EAAIA,GAAQJ,EAAM,IAAIE,CAAC,GAAK,IAAMF,EAAM,IAAIG,CAAC,GAAK,EACpE,CAAC,EAIC,OAAAJ,EACA,WAAaF,EAAQ,WAA0B,KAC/C,WAAYC,EAAK,UACnB,CACF,CAEA,SAASR,EAAKe,EAAcC,EAAqB,CAC/C,IAAMC,EAAO,OAAOF,GAAQ,EAAE,EAAE,QAAQ,OAAQ,GAAG,EAAE,KAAK,EAC1D,OAAOE,EAAK,OAASD,EAAMC,EAAK,MAAM,EAAGD,CAAG,EAAIC,CAClD","names":["init_questions","WIKI_RERANK_SEAT","DEFAULT_SHORTLIST","DEFAULT_EXCERPT_CHARS","rerankState","input","budget","c","clip","rerankQuestions","candidates","criteria","choice","noul","toRanking","answers","best","scores","order","i","a","b","diff","text","max","flat"]}
@@ -0,0 +1,2 @@
1
+ import{b as o}from"./chunk-FIOB3MEX.js";function g(e){if(!e)return null;let s=l(e);if(s!==void 0)return s;let c=e.match(/```(?:json)?\s*([\s\S]*?)```/);if(c){let n=l(c[1]);if(n!==void 0)return n}let f=[["{","}"],["[","]"]].map(([n,i])=>({open:n,close:i,at:e.indexOf(n)})).filter(n=>n.at>=0).sort((n,i)=>n.at-i.at);for(let{open:n,close:i}of f){let t=a(e,n,i);if(t!==null){let r=l(t);if(r!==void 0)return r}}return null}function l(e){try{return JSON.parse(e.trim())}catch{return}}function a(e,s,c){let f=e.indexOf(s);if(f<0)return null;let n=0,i=!1,t=!1;for(let r=f;r<e.length;r++){let u=e[r];if(t){t=!1;continue}if(u==="\\"){i&&(t=!0);continue}if(u==='"'){i=!i;continue}if(!i){if(u===s)n++;else if(u===c&&(n--,n===0))return e.slice(f,r+1)}}return null}var d=o(()=>{"use strict"});export{g as a,d as b};
2
+ //# sourceMappingURL=chunk-AJONZWLT.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/utils/extract-json.ts"],"sourcesContent":["// Pull a JSON value out of text a model wrote.\n//\n// Consolidated from four byte-identical copies (src/graph/classifier.ts,\n// src/agents/context-planner.ts, src/commands/graph.ts, and\n// src/daemon/board-dashboard.ts). Callers that return prose keep their own\n// bespoke variants — this is for call sites whose whole reply is supposed\n// to be a structured value.\n//\n// One behavioural difference from the copies it replaces: the balanced-brace\n// scan is string-aware. The old version counted every `{` and `}` including\n// ones inside string literals, so a perfectly valid `{\"glob\": \"src/**/{a,b}\"}`\n// truncated at the wrong byte and returned null.\n\n/** Parse `text` as JSON, tolerating code fences and surrounding prose.\n * Returns `null` when nothing parseable is found — never throws. */\nexport function extractJson<T = any>(text: string): T | null {\n if (!text) return null\n\n const direct = tryParse<T>(text)\n if (direct !== undefined) return direct\n\n const fenced = text.match(/```(?:json)?\\s*([\\s\\S]*?)```/)\n if (fenced) {\n const parsed = tryParse<T>(fenced[1])\n if (parsed !== undefined) return parsed\n }\n\n // Last-ditch: the first balanced object or array in the text. Whichever\n // bracket appears first wins, so `here: [{\"a\":1}]` yields the array and\n // not the object nested inside it.\n const pairs = ([[\"{\", \"}\"], [\"[\", \"]\"]] as const)\n .map(([open, close]) => ({ open, close, at: text.indexOf(open) }))\n .filter((p) => p.at >= 0)\n .sort((a, b) => a.at - b.at)\n\n for (const { open, close } of pairs) {\n const slice = balancedSlice(text, open, close)\n if (slice !== null) {\n const parsed = tryParse<T>(slice)\n if (parsed !== undefined) return parsed\n }\n }\n\n return null\n}\n\nfunction tryParse<T>(text: string): T | undefined {\n try {\n return JSON.parse(text.trim()) as T\n } catch {\n return undefined\n }\n}\n\nfunction balancedSlice(text: string, open: string, close: string): string | null {\n const start = text.indexOf(open)\n if (start < 0) return null\n\n let depth = 0\n let inString = false\n let escaped = false\n\n for (let i = start; i < text.length; i++) {\n const ch = text[i]\n\n if (escaped) {\n escaped = false\n continue\n }\n if (ch === \"\\\\\") {\n if (inString) escaped = true\n continue\n }\n if (ch === '\"') {\n inString = !inString\n continue\n }\n if (inString) continue\n\n if (ch === open) depth++\n else if (ch === close) {\n depth--\n if (depth === 0) return text.slice(start, i + 1)\n }\n }\n\n return null\n}\n"],"mappings":"wCAeO,SAASA,EAAqBC,EAAwB,CAC3D,GAAI,CAACA,EAAM,OAAO,KAElB,IAAMC,EAASC,EAAYF,CAAI,EAC/B,GAAIC,IAAW,OAAW,OAAOA,EAEjC,IAAME,EAASH,EAAK,MAAM,8BAA8B,EACxD,GAAIG,EAAQ,CACV,IAAMC,EAASF,EAAYC,EAAO,CAAC,CAAC,EACpC,GAAIC,IAAW,OAAW,OAAOA,EAMnC,IAAMC,EAAS,CAAC,CAAC,IAAK,GAAG,EAAG,CAAC,IAAK,GAAG,CAAC,EACnC,IAAI,CAAC,CAACC,EAAMC,CAAK,KAAO,CAAE,KAAAD,EAAM,MAAAC,EAAO,GAAIP,EAAK,QAAQM,CAAI,CAAE,EAAE,EAChE,OAAQE,GAAMA,EAAE,IAAM,CAAC,EACvB,KAAK,CAACC,EAAGC,IAAMD,EAAE,GAAKC,EAAE,EAAE,EAE7B,OAAW,CAAE,KAAAJ,EAAM,MAAAC,CAAM,IAAKF,EAAO,CACnC,IAAMM,EAAQC,EAAcZ,EAAMM,EAAMC,CAAK,EAC7C,GAAII,IAAU,KAAM,CAClB,IAAMP,EAASF,EAAYS,CAAK,EAChC,GAAIP,IAAW,OAAW,OAAOA,GAIrC,OAAO,IACT,CAEA,SAASF,EAAYF,EAA6B,CAChD,GAAI,CACF,OAAO,KAAK,MAAMA,EAAK,KAAK,CAAC,CAC/B,MAAE,CACA,MACF,CACF,CAEA,SAASY,EAAcZ,EAAcM,EAAcC,EAA8B,CAC/E,IAAMM,EAAQb,EAAK,QAAQM,CAAI,EAC/B,GAAIO,EAAQ,EAAG,OAAO,KAEtB,IAAIC,EAAQ,EACRC,EAAW,GACXC,EAAU,GAEd,QAASC,EAAIJ,EAAOI,EAAIjB,EAAK,OAAQiB,IAAK,CACxC,IAAMC,EAAKlB,EAAKiB,CAAC,EAEjB,GAAID,EAAS,CACXA,EAAU,GACV,SAEF,GAAIE,IAAO,KAAM,CACXH,IAAUC,EAAU,IACxB,SAEF,GAAIE,IAAO,IAAK,CACdH,EAAW,CAACA,EACZ,SAEF,GAAI,CAAAA,GAEJ,GAAIG,IAAOZ,EAAMQ,YACRI,IAAOX,IACdO,IACIA,IAAU,GAAG,OAAOd,EAAK,MAAMa,EAAOI,EAAI,CAAC,GAInD,OAAO,IACT,CAvFA,IAAAE,EAAAC,EAAA","names":["extractJson","text","direct","tryParse","fenced","parsed","pairs","open","close","p","a","b","slice","balancedSlice","start","depth","inString","escaped","i","ch","init_extract_json","__esmMin"]}
@@ -0,0 +1,114 @@
1
+ import{a as Te,b as ft}from"./chunk-AJONZWLT.js";import{a as et,b as tt,c as nt,d as S,e as A,f as N}from"./chunk-F4HZTZHW.js";import{a as ne,b as He}from"./chunk-ZTEU5JVQ.js";import{o as we,p as Ke}from"./chunk-LCIREYTD.js";import{i as We,j as Ze}from"./chunk-CJQQIRPD.js";import{a as rt,b as ot,c as st,d as it,e as at,f as ct,g as ut,h as lt,i as dt,j as pt,k as mt,l as bt}from"./chunk-BH3GTCZ6.js";import{b as E,c as Ye,d as he}from"./chunk-FIOB3MEX.js";function R(e,t){$.set(e,t),X.delete(e)}function D(e){let t=X.get(e);if(t)return t;let r=$.get(e);if(!r){let o=Ee();throw new Error(`unknown decision backend "${e}"`+(o.length>0?` \u2014 registered: ${o.join(", ")}`:" \u2014 none registered"))}let n=r();return X.set(e,n),n}function Ee(){return[...$.keys()].sort()}function gt(){$.clear(),X.clear()}var $,X,C=E(()=>{"use strict";$=new Map,X=new Map});import yt from"better-sqlite3";import{createHash as ht}from"crypto";import{mkdirSync as wt}from"fs";import{dirname as Tt,resolve as Et}from"path";function Q(e){if(e.type==="noul"){let r=e.noul,n={yes:r,no:1-r},o=r>=.5?"yes":"no",s=Math.abs(r-.5)*2;return{predicted:o,confidence:s,probabilities:n,topLabel:o,topProb:Math.max(r,1-r),expectedScore:null,pMax:Math.max(r,1-r),negEntropy:s}}if(e.type==="choice")return{predicted:e.choice,confidence:e.confidence,probabilities:{...e.probabilities},topLabel:e.choice,topProb:e.probabilities[e.choice]??0,expectedScore:null,pMax:e.pMax,negEntropy:e.negEntropy};let t=String(Math.round(e.score));return{predicted:t,confidence:e.confidence,probabilities:{...e.probabilities},topLabel:t,topProb:e.probabilities[t]??0,expectedScore:e.score,pMax:e.pMax,negEntropy:e.negEntropy}}function kt(e){if(typeof e!="string"||e.length===0)return{};try{let t=JSON.parse(e);return t&&typeof t=="object"&&!Array.isArray(t)?t:{}}catch{return{}}}function xt(e){e.exec("CREATE TABLE IF NOT EXISTS schema_version (v INTEGER PRIMARY KEY);");let t=e.prepare("SELECT MAX(v) AS v FROM schema_version").get().v??0;t<1&&Rt(e),t<2&&At(e),t<3&&St(e)}function St(e){e.exec(`
2
+ ALTER TABLE decision_calls ADD COLUMN features_json TEXT;
3
+ INSERT INTO schema_version (v) VALUES (3);
4
+ `)}function At(e){e.exec(`
5
+ ALTER TABLE decision_calls ADD COLUMN action TEXT;
6
+ ALTER TABLE decision_calls ADD COLUMN explored INTEGER NOT NULL DEFAULT 0;
7
+ CREATE INDEX IF NOT EXISTS idx_decision_calls_action ON decision_calls (seat, action, explored);
8
+ INSERT INTO schema_version (v) VALUES (2);
9
+ `)}function Rt(e){e.exec(`
10
+ CREATE TABLE IF NOT EXISTS decision_calls (
11
+ id TEXT NOT NULL PRIMARY KEY,
12
+ ts INTEGER NOT NULL,
13
+ seat TEXT NOT NULL,
14
+ mode TEXT NOT NULL,
15
+ backend TEXT NOT NULL,
16
+ model TEXT NOT NULL,
17
+ structure_mode TEXT NOT NULL,
18
+ answer_mode TEXT NOT NULL,
19
+ state_hash TEXT NOT NULL,
20
+ state_json TEXT,
21
+ questions_json TEXT NOT NULL,
22
+ latency_ms INTEGER,
23
+ input_tokens INTEGER NOT NULL DEFAULT 0,
24
+ output_tokens INTEGER NOT NULL DEFAULT 0,
25
+ retries INTEGER NOT NULL DEFAULT 0,
26
+ truncated INTEGER NOT NULL DEFAULT 0,
27
+ error TEXT
28
+ );
29
+ CREATE INDEX IF NOT EXISTS idx_decision_calls_seat ON decision_calls (seat, ts);
30
+ CREATE INDEX IF NOT EXISTS idx_decision_calls_backend ON decision_calls (backend, model, ts);
31
+ CREATE INDEX IF NOT EXISTS idx_decision_calls_state ON decision_calls (state_hash);
32
+
33
+ CREATE TABLE IF NOT EXISTS decision_answers (
34
+ call_id TEXT NOT NULL REFERENCES decision_calls(id),
35
+ question TEXT NOT NULL,
36
+ type TEXT NOT NULL,
37
+ answer_json TEXT NOT NULL,
38
+ top_label TEXT,
39
+ top_prob REAL,
40
+ expected_score REAL,
41
+ p_max REAL,
42
+ neg_entropy REAL,
43
+ PRIMARY KEY (call_id, question)
44
+ );
45
+
46
+ CREATE TABLE IF NOT EXISTS decision_incumbent (
47
+ call_id TEXT NOT NULL REFERENCES decision_calls(id),
48
+ question TEXT NOT NULL,
49
+ value TEXT,
50
+ score REAL,
51
+ source TEXT,
52
+ PRIMARY KEY (call_id, question)
53
+ );
54
+
55
+ CREATE TABLE IF NOT EXISTS decision_labels (
56
+ id TEXT NOT NULL PRIMARY KEY,
57
+ call_id TEXT NOT NULL REFERENCES decision_calls(id),
58
+ question TEXT NOT NULL,
59
+ value TEXT NOT NULL,
60
+ kind TEXT NOT NULL,
61
+ labeled_at INTEGER NOT NULL,
62
+ labeled_by TEXT,
63
+ note TEXT
64
+ );
65
+ CREATE INDEX IF NOT EXISTS idx_decision_labels_call ON decision_labels (call_id, question);
66
+
67
+ CREATE TABLE IF NOT EXISTS decision_links (
68
+ call_id TEXT NOT NULL REFERENCES decision_calls(id),
69
+ ref_kind TEXT NOT NULL,
70
+ ref_id TEXT NOT NULL,
71
+ PRIMARY KEY (call_id, ref_kind, ref_id)
72
+ );
73
+ CREATE INDEX IF NOT EXISTS idx_decision_links_ref ON decision_links (ref_kind, ref_id);
74
+
75
+ INSERT INTO schema_version (v) VALUES (1);
76
+ `)}var I,V=E(()=>{"use strict";He();I=class{db;path;constructor(t={}){this.path=Et(process.cwd(),t.path??".agentx/decisions/decisions.sqlite"),t.readonly||wt(Tt(this.path),{recursive:!0}),this.db=new yt(this.path,{readonly:t.readonly??!1}),t.readonly||(this.db.pragma("journal_mode = WAL"),this.db.pragma("synchronous = NORMAL"),this.db.pragma("foreign_keys = ON"),xt(this.db))}close(){this.db.close()}schemaVersion(){return this.db.prepare("SELECT MAX(v) AS v FROM schema_version").get().v??0}recordCall(t){let r=t.ts??Date.now(),n=ne(r),o=JSON.stringify(t.state??null),s=ht("sha256").update(o).digest("hex");return this.db.transaction(()=>{this.db.prepare(`INSERT INTO decision_calls
77
+ (id, ts, seat, mode, backend, model, structure_mode, answer_mode,
78
+ state_hash, state_json, questions_json, latency_ms,
79
+ input_tokens, output_tokens, retries, truncated, error, features_json)
80
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(n,r,t.seat,t.mode,t.backend,t.model,t.meta.structureMode,t.meta.answerMode,s,t.keepState===!1?null:o,JSON.stringify(t.questions),t.meta.latencyMs,t.usage?.inputTokens??0,t.usage?.outputTokens??0,t.meta.retries,t.meta.stateTruncated?1:0,t.error??null,t.features?JSON.stringify(t.features):null);let a=this.db.prepare(`INSERT INTO decision_answers
81
+ (call_id, question, type, answer_json, top_label, top_prob,
82
+ expected_score, p_max, neg_entropy)
83
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`);for(let[u,c]of Object.entries(t.answers)){let l=Q(c);a.run(n,u,c.type,JSON.stringify(c),l.topLabel,l.topProb,l.expectedScore,l.pMax,l.negEntropy)}if(t.incumbent){let u=this.db.prepare(`INSERT OR REPLACE INTO decision_incumbent (call_id, question, value, score, source)
84
+ VALUES (?, ?, ?, ?, ?)`);for(let[c,l]of Object.entries(t.incumbent))u.run(n,c,l.value===void 0?null:String(l.value),l.score??null,l.source??null)}if(t.links){let u=this.db.prepare("INSERT OR IGNORE INTO decision_links (call_id, ref_kind, ref_id) VALUES (?, ?, ?)");for(let c of t.links)u.run(n,c.kind,c.id)}})(),n}label(t,r,n,o={}){let s=o.ts??Date.now(),i=ne(s);return this.db.prepare(`INSERT INTO decision_labels (id, call_id, question, value, kind, labeled_at, labeled_by, note)
85
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)`).run(i,t,r,String(n),o.kind??"human",s,o.labeledBy??null,o.note??null),i}recordOutcome(t,r,n=!1){this.db.prepare("UPDATE decision_calls SET action = ?, explored = ? WHERE id = ?").run(r,n?1:0,t)}findCallsByLink(t,r){return this.db.prepare("SELECT call_id FROM decision_links WHERE ref_kind = ? AND ref_id = ?").all(t,r).map(o=>o.call_id)}gradedRows(t={}){let r=["c.error IS NULL"],n=[];t.seat&&(r.push("c.seat = ?"),n.push(t.seat)),t.question&&(r.push("a.question = ?"),n.push(t.question)),t.backend&&(r.push("c.backend = ?"),n.push(t.backend)),t.model&&(r.push("c.model = ?"),n.push(t.model)),t.structureMode&&(r.push("c.structure_mode = ?"),n.push(t.structureMode)),t.since!==void 0&&(r.push("c.ts >= ?"),n.push(t.since)),t.action&&(r.push("c.action = ?"),n.push(t.action)),t.exploredOnly&&r.push("c.explored = 1");let o=`
86
+ SELECT c.id AS call_id, c.seat, c.ts, c.backend, c.model, c.structure_mode,
87
+ c.mode, c.action, c.explored, c.features_json,
88
+ a.question, a.type, a.answer_json, a.top_label, a.expected_score, a.neg_entropy,
89
+ i.value AS incumbent,
90
+ (SELECT l.value FROM decision_labels l
91
+ WHERE l.call_id = c.id AND l.question = a.question
92
+ ORDER BY l.id DESC LIMIT 1) AS truth
93
+ FROM decision_calls c
94
+ JOIN decision_answers a ON a.call_id = c.id
95
+ LEFT JOIN decision_incumbent i ON i.call_id = c.id AND i.question = a.question
96
+ WHERE ${r.join(" AND ")}
97
+ ORDER BY c.ts DESC
98
+ ${t.limit?"LIMIT ?":""}`;return t.limit&&n.push(t.limit),this.db.prepare(o).all(...n).map(i=>{let a=JSON.parse(i.answer_json),u=Q(a);return{callId:i.call_id,seat:i.seat,ts:i.ts,backend:i.backend,model:i.model,structureMode:i.structure_mode,question:i.question,type:a.type,predicted:u.predicted,confidence:u.confidence,probabilities:u.probabilities,incumbent:i.incumbent??void 0,truth:i.truth??void 0,features:kt(i.features_json),mode:i.mode,action:i.action??void 0,explored:i.explored===1}}).filter(i=>!t.labeledOnly||i.truth!==void 0)}pruneState(t,r){return this.db.prepare(`UPDATE decision_calls SET state_json = NULL
99
+ WHERE seat = ? AND state_json IS NOT NULL
100
+ AND id NOT IN (
101
+ SELECT id FROM decision_calls WHERE seat = ? ORDER BY ts DESC LIMIT ?
102
+ )`).run(t,t,r).changes}}});var ke=E(()=>{"use strict"});function xe(e,t,r={}){let n=r.normalize??!0,o=r.epsilon??1e-6,s=e!==null&&typeof e=="object"&&!Array.isArray(e)?e:{},i=e===null||typeof e!="object"||Array.isArray(e);for(let c of Object.keys(s))t.includes(c)||(i=!0);let a={},u=0;for(let c of t){if(!(c in s)){a[c]=o,u+=o,i=!0;continue}let l=Number(s[c]);if(!Number.isFinite(l)||l<0){a[c]=0,i=!0;continue}a[c]=l,u+=l}if(t.length===0)return{probs:a,repaired:i};if(u<=0){let c=1/t.length;for(let l of t)a[l]=c;return{probs:a,repaired:!0}}if(n){for(let c of t)a[c]=a[c]/u;Math.abs(u-1)>1e-6&&(i=!0)}return{probs:a,repaired:i}}function Se(e){let t=Object.values(e);return t.length===0?0:Math.max(...t)}function oe(e){let t=Object.values(e).filter(s=>Number.isFinite(s)&&s>0),r=Object.keys(e).length;if(r<=1)return 1;let n=t.reduce((s,i)=>s+i,0);if(n<=0)return 0;let o=0;for(let s of t){let i=s/n;o-=i*Math.log(i)}return _t(1-o/Math.log(r))}function Ae(e){let t=0,r=0;for(let[n,o]of Object.entries(e)){let s=Number(n);!Number.isFinite(s)||!Number.isFinite(o)||o<=0||(t+=o,r+=s*o)}return t>0?r/t:0}function re(e){return oe(e)}function _t(e){return Number.isFinite(e)?Math.max(0,Math.min(1,e)):0}function _(e,t,r={}){if(e.type==="noul"){let c=Number(t.noul),l=Number.isFinite(c)&&c>=0&&c<=1;return{answer:{type:"noul",noul:l?c:.5},repaired:!l}}let n=S(e),{probs:o,repaired:s}=xe(t.probabilities,n,r),i=Se(o),a=oe(o);if(e.type==="choice"){let c=n[0];for(let l of n)o[l]>o[c]&&(c=l);return{answer:{type:"choice",choice:c,confidence:re(o),probabilities:o,pMax:i,negEntropy:a},repaired:s}}let u={};return e.criteria.forEach((c,l)=>{u[String(l)]=c}),{answer:{type:"score",score:Ae(o),confidence:re(o),legend:u,probabilities:o,pMax:i,negEntropy:a},repaired:s}}var j=E(()=>{"use strict";N()});import{z as O}from"zod";function Re(e){return e.type==="noul"?Ot:Nt}function F(e){let t={};for(let[r,n]of Object.entries(e))t[r]=Re(n);return O.object(t)}function U(e){return e.issues.map(t=>`- ${t.path.length>0?t.path.join("."):"(root)"}: ${t.message}`).join(`
103
+ `)}var Ot,Nt,z=E(()=>{"use strict";Ot=O.object({noul:O.number()}),Nt=O.object({probabilities:O.record(O.string(),O.unknown())})});function se(e){let t={};for(let[r,n]of Object.entries(e))t[r]=vt(n);return{type:"object",additionalProperties:!1,required:["answers"],properties:{answers:{type:"object",additionalProperties:!1,required:Object.keys(e),properties:t}}}}function vt(e){if(e.type==="noul")return{type:"object",additionalProperties:!1,required:["noul"],properties:{noul:{type:"number",minimum:0,maximum:1,description:"Probability the answer is yes."}}};let t=S(e),r={};for(let n of t)r[n]={type:"number",minimum:0,maximum:1};return{type:"object",additionalProperties:!1,required:["probabilities"],properties:{probabilities:{type:"object",additionalProperties:!1,required:t,properties:r,description:"One probability per option. Must sum to 1."}}}}function G(e,t){let r=typeof e=="string"?e:JSON.stringify(e,null,2)??"null";return r.length<=t?{text:r,truncated:!1}:{text:`${r.slice(0,t)}
104
+ \u2026[truncated: ${r.length-t} more characters]`,truncated:!0}}function _e(e){let t=[];for(let[r,n]of Object.entries(e)){let o=[`### ${r}`];if(n.instructions&&o.push(n.instructions),n.type==="noul")o.push("Type: yes/no. Report `noul` \u2014 the probability the answer is yes."),n.criteria?.true&&o.push(`- yes means: ${n.criteria.true}`),n.criteria?.false&&o.push(`- no means: ${n.criteria.false}`);else if(n.type==="choice"){o.push("Type: choice. Report `probabilities` over exactly these options:");for(let[s,i]of Object.entries(n.criteria))o.push(i?`- ${s}: ${i}`:`- ${s}`)}else o.push("Type: score. Report `probabilities` over exactly these levels:"),n.criteria.forEach((s,i)=>{o.push(`- "${i}": ${s}`)});t.push(o.join(`
105
+ `))}return t.join(`
106
+
107
+ `)}function ie(e,t,r){let n=G(e,r);return{text:["## State",n.text,"","## Questions",_e(t)].join(`
108
+ `),truncated:n.truncated}}function ae(e){let t={};for(let[r,n]of Object.entries(e)){if(n.type==="noul"){t[r]={noul:.5};continue}let o={},s=S(n);for(let i of s)o[i]=Number((1/s.length).toFixed(4));t[r]={probabilities:o}}return["","## Output","Reply with this JSON object and nothing else:",JSON.stringify({answers:t},null,2)].join(`
109
+ `)}var B,J,Y=E(()=>{"use strict";N();B="answer_questions",J=["You answer questions about a piece of state by reporting a probability distribution for each one.","","Rules:","- Answer every question. Never add a question that was not asked.","- For a yes/no question, report `noul`: the probability the answer is yes, between 0 and 1.","- For every other question, report `probabilities`: one number per listed option, summing to 1.","- Use only the exact option names given. Never invent one.","- Spread the distribution when the state genuinely does not settle the question. A flat"," distribution is a useful answer; a confident wrong one is not.","- Output the structure only. No prose, no explanation, no code fences."].join(`
110
+ `)});function Oe(e){return e.predicted===e.truth?1:0}function le(e){let t=Math.min(1-K,Math.max(K,e));return Math.log(t/(1-t))}function de(e){if(e>=0)return 1/(1+Math.exp(-e));let t=Math.exp(e);return t/(1+t)}function ce(e,t){let r=e.features?.[t];if(r!=null)return String(r);let n=e[t];return n==null?"(none)":String(n)}function Ne(e,t){let r=t.minLevelCount??5,n={};for(let a of t.covariates){let u=new Map;for(let l of e){let p=ce(l,a);u.set(p,(u.get(p)??0)+1)}let c=[...u.entries()].filter(([,l])=>l>=r).map(([l])=>l).sort();n[a]=c.slice(1)}let o=["intercept","logitConfidence"];for(let a of t.covariates)for(let u of n[a])o.push(`${a}=${u}`);let s=[],i=[];for(let a of e){let u=[1,le(a.confidence)];for(let c of t.covariates){let l=ce(a,c);for(let p of n[c])u.push(l===p?1:0)}s.push(u),i.push(Oe(a))}return{X:s,y:i,names:o,levels:n}}function ve(e,t,r={}){let n=r.l2??1,o=r.iterations??25,s=e.length,i=s>0?e[0].length:0,a=new Array(i).fill(0);if(s===0||i===0)return a;for(let u=0;u<o;u++){let c=Array.from({length:i},()=>new Array(i).fill(0)),l=new Array(i).fill(0);for(let d=0;d<s;d++){let b=0;for(let m=0;m<i;m++)b+=a[m]*e[d][m];let g=de(b),w=Math.max(g*(1-g),1e-8),f=t[d]-g;for(let m=0;m<i;m++){l[m]+=f*e[d][m];for(let h=m;h<i;h++)c[m][h]+=w*e[d][m]*e[d][h]}}for(let d=0;d<i;d++){d>0&&(c[d][d]+=n,l[d]-=n*a[d]);for(let b=0;b<d;b++)c[d][b]=c[b][d]}let p=Mt(c,l);if(!p)break;let y=0;for(let d=0;d<i;d++)a[d]+=p[d],y+=Math.abs(p[d]);if(y<1e-8)break}return a}function Mt(e,t){let r=t.length,n=e.map((s,i)=>[...s,t[i]]);for(let s=0;s<r;s++){let i=s;for(let a=s+1;a<r;a++)Math.abs(n[a][s])>Math.abs(n[i][s])&&(i=a);if(Math.abs(n[i][s])<1e-12)return null;[n[s],n[i]]=[n[i],n[s]];for(let a=0;a<r;a++){if(a===s)continue;let u=n[a][s]/n[s][s];if(u!==0)for(let c=s;c<=r;c++)n[a][c]-=u*n[s][c]}}let o=new Array(r);for(let s=0;s<r;s++)o[s]=n[s][r]/n[s][s];return o}function P(e,t){let r=e.filter(o=>o.truth!==void 0),n=Ne(r,t);return{weights:ve(n.X,n.y),names:n.names,levels:n.levels,covariates:t.covariates,n:r.length}}function ue(e,t){let r=e.weights[0]+e.weights[1]*le(t.confidence),n=2;for(let o of e.covariates){let s=ce(t,o);for(let i of e.levels[o]??[])s===i&&(r+=e.weights[n]),n++}return de(r)}function Lt(e,t,r={}){let n=r.folds??5,o=r.minN??100,s=e.filter(d=>d.truth!==void 0),i={insufficient:s.length<o,n:s.length,minN:o,folds:n,scores:[]};if(i.insufficient)return i;let a=r.rng??Math.random,u=[...s];for(let d=u.length-1;d>0;d--){let b=Math.floor(a()*(d+1));[u[d],u[b]]=[u[b],u[d]]}let c={raw:[],temperature:[],covariate:[]},l=[];for(let d=0;d<n;d++){let b=u.filter((m,h)=>h%n===d),g=u.filter((m,h)=>h%n!==d);if(g.length===0||b.length===0)continue;let w=P(g,{...t,covariates:[]}),f=P(g,t);for(let m of b)l.push(Oe(m)),c.raw.push(m.confidence),c.temperature.push(ue(w,m)),c.covariate.push(ue(f,m))}let p=Object.entries(c).map(([d,b])=>({name:d,logLoss:Dt(b,l),brier:Ct(b,l)})),y=[...p].sort((d,b)=>d.logLoss-b.logLoss)[0]?.name;return{...i,scores:p,best:y,model:y==="covariate"?P(s,t):P(s,{...t,covariates:[]})}}function Dt(e,t){if(e.length===0)return 0;let r=0;for(let n=0;n<e.length;n++){let o=Math.min(1-K,Math.max(K,e[n]));r+=t[n]===1?-Math.log(o):-Math.log(1-o)}return r/e.length}function Ct(e,t){if(e.length===0)return 0;let r=0;for(let n=0;n<e.length;n++)r+=(e[n]-t[n])**2;return r/e.length}var K,Me=E(()=>{"use strict";K=1e-6});import{randomBytes as It}from"crypto";function Ie(e,t=De,r=Ce){return e<=t?"no":e>r?"yes":"uncertain"}async function Qt(e,t,r,n={}){let o=Math.max(2,n.samples??15),s=D(e),i=Date.now(),a=await Promise.all(Array.from({length:o},(p,y)=>s.decide({state:Qe(t,`${y}:${It(4).toString("hex")}`),questions:r,model:n.model}))),u=Object.keys(r),c=a.reduce((p,y)=>({inputTokens:p.inputTokens+y.usage.inputTokens,outputTokens:p.outputTokens+y.usage.outputTokens}),{inputTokens:0,outputTokens:0}),l=u.map(p=>{let y=a.map(f=>Q(f.answers[p])),d=y.map((f,m)=>{let h=a[m].answers[p];return h.type==="noul"?h.noul:f.topProb}),b=[...new Set(y.map(f=>f.predicted))],g=[...new Set(d.map(f=>Ie(f,n.low,n.high)))],w=Le(d);return{question:p,type:a[0].answers[p].type,n:o,mean:w,stdev:jt(d,w),min:Math.min(...d),max:Math.max(...d),distinctAnswers:b,bands:g,crossesThreshold:b.length>1||g.length>1,samples:d}});return{backend:e,model:a[0].model,samples:o,meanStdev:Le(l.map(p=>p.stdev)),unstable:l.filter(p=>p.crossesThreshold).map(p=>p.question),questions:l,usage:c,totalMs:Date.now()-i}}function Qe(e,t){return e!==null&&typeof e=="object"&&!Array.isArray(e)?{...e,uid:t}:{uid:t,state:e}}function Le(e){return e.length===0?0:e.reduce((t,r)=>t+r,0)/e.length}function jt(e,t){return e.length<2?0:Math.sqrt(e.reduce((r,n)=>r+(n-t)**2,0)/e.length)}var De,Ce,je=E(()=>{"use strict";C();V();De=.3,Ce=.7});function H(e={}){return{name:"mock",capabilities:Ft,async decide(t){if(e.fail)throw e.fail;A(t.questions);let r=JSON.stringify(t.state??null),n={},o=!1;for(let[i,a]of Object.entries(t.questions)){let u=e.answers?.[i]??Ut(r,i,a),c=_(a,u);n[i]=c.answer,c.repaired&&(o=!0)}let s="probabilities";return{model:t.model??"mock",answers:n,usage:{inputTokens:r.length,outputTokens:0},meta:{backend:"mock",structureMode:"mock",answerMode:s,repaired:o,retries:0,stateTruncated:!1,latencyMs:e.latencyMs??0}}}}}function Ut(e,t,r){if(r.type==="noul")return{noul:Fe(`${e}|${t}|noul`)};let n=S(r),o=n.map(a=>Fe(`${e}|${t}|${a}`)+.001),s=o.reduce((a,u)=>a+u,0),i={};return n.forEach((a,u)=>{i[a]=o[u]/s}),{probabilities:i}}function Fe(e){let t=2166136261;for(let r=0;r<e.length;r++)t^=e.charCodeAt(r),t=Math.imul(t,16777619)>>>0;return t/4294967296}var Ft,pe=E(()=>{"use strict";j();N();Ft={probabilitySource:"synthetic",calibratedProbabilities:!1,maxChoiceOptions:255,maxStateChars:24e3,parallelQuestions:!0,images:!1}});function Un(e,t){let r=me[e];if(!r)return[`Unknown provider "${e}" \u2014 capabilities unknown`];if(!t?.length)return[];let n=[],o=[];for(let s of t)s in r&&!r[s]&&o.push(s);return o.length&&n.push(`Provider "${e}" lacks: ${o.join(", ")}. Some features will be degraded.`),n}var me,Ue=E(()=>{"use strict";me={"claude-code":{streaming:!0,tools:!0,vision:!0,thinking:!0,maxContext:1e6,structuredOutput:!1,logprobs:!1},claude:{streaming:!0,tools:!0,vision:!0,thinking:!0,maxContext:1e6,structuredOutput:!0,logprobs:!1},openai:{streaming:!0,tools:!0,vision:!0,thinking:!1,maxContext:128e3,structuredOutput:!0,logprobs:!0},deepseek:{streaming:!0,tools:!0,vision:!1,thinking:!0,maxContext:128e3,structuredOutput:!0,logprobs:!0},ollama:{streaming:!0,tools:!1,vision:!1,thinking:!1,maxContext:32e3,structuredOutput:!1,logprobs:!1},demo:{streaming:!0,tools:!1,vision:!1,thinking:!1,maxContext:8e3,structuredOutput:!1,logprobs:!1},custom:{streaming:!0,tools:!1,vision:!1,thinking:!1,maxContext:32e3,structuredOutput:!1,logprobs:!1}}});function W(e={}){if(e.answerMode&&e.answerMode!=="probabilities")throw new Error(`local decision backend supports answerMode "probabilities" only (got "${e.answerMode}")`);let t=e.maxStateChars??qt,r={probabilitySource:"verbalized",calibratedProbabilities:!1,maxChoiceOptions:255,maxStateChars:t,parallelQuestions:!0,images:!1},n=null,o=()=>(n||(n=e.providerFactory?e.providerFactory():we(e.provider??Bt)),n);return{name:"local",capabilities:r,async decide(s){A(s.questions);let i=Date.now(),a=s.model??e.model??Pt,u=o(),c=zt(e.structureMode??"auto",u),l=ie(s.state,s.questions,t),p=F(s.questions),y=e.nRetryMalformedStructure??1,{signal:d,dispose:b}=Jt(s.abortSignal,s.timeoutMs??e.timeoutMs??Xt),g={inputTokens:0,outputTokens:0},w=null,f=0,m=null,h="no attempt was made";try{for(let k=0;k<=y;k++){k>0&&f++;let x=await(c==="tool"?$t(u,s.questions,l.text,w,{model:a,maxTokens:e.maxTokens??Be,temperature:e.temperature??0,abortSignal:d}):Vt(u,s.questions,l.text,w,{model:a,maxTokens:e.maxTokens??Be,temperature:e.temperature??0,abortSignal:d}));g={inputTokens:g.inputTokens+x.usage.inputTokens,outputTokens:g.outputTokens+x.usage.outputTokens};let te=p.safeParse(x.answers);if(te.success){m=te.data;break}h=U(te.error),w=["Your previous reply did not match the required structure:",h,"","Reply again with the corrected structure only."].join(`
111
+ `)}if(!m)throw new Error(`local decision backend could not get a valid answer after ${f+1} attempt(s): ${h}`);let M={},L=!1;for(let[k,q]of Object.entries(s.questions)){let x=_(q,m[k],{normalize:e.normalizeProbabilities??!0});M[k]=x.answer,x.repaired&&(L=!0)}return{model:a,answers:M,usage:g,meta:{backend:"local",structureMode:c,answerMode:"probabilities",repaired:L,retries:f,stateTruncated:l.truncated,latencyMs:Date.now()-i}}}finally{b()}}}}async function $t(e,t,r,n,o){if(!e.generateRaw)throw new Error(`provider "${e.name}" has no generateRaw(); use structureMode "text"`);let s=[{role:"user",content:r},...n?[{role:"user",content:n}]:[]],i=await e.generateRaw(s,J,[{name:B,description:"Report a probability distribution for every question.",input_schema:se(t)}],{...o,toolChoice:{type:"tool",name:B}}),a=i.content.find(c=>c.type==="tool_use"&&c.name===B),u=a&&a.type==="tool_use"?a.input:null;return{answers:Pe(u),usage:{inputTokens:i.usage.input_tokens,outputTokens:i.usage.output_tokens}}}async function Vt(e,t,r,n,o){let s=[r,ae(t)].join(`
112
+ `),i=[{role:"system",content:J},{role:"user",content:s},...n?[{role:"user",content:n}]:[]],a=await e.generate(i,o);return{answers:Pe(Te(a.content??"")),usage:{inputTokens:0,outputTokens:a.tokensUsed??0}}}function Pe(e){if(e&&typeof e=="object"&&!Array.isArray(e)){let t=e;if(t.answers&&typeof t.answers=="object")return t.answers}return e}function zt(e,t){return e!=="auto"?e:t.generateRaw&&me[t.name]?.structuredOutput?"tool":"text"}function Jt(e,t){let r=new AbortController,n=setTimeout(()=>r.abort(new Error(`decision timed out after ${t}ms`)),t),o=()=>r.abort(e?.reason);return e&&(e.aborted?o():e.addEventListener("abort",o,{once:!0})),{signal:r.signal,dispose:()=>{clearTimeout(n),e?.removeEventListener("abort",o)}}}var Bt,Pt,qt,Be,Xt,be=E(()=>{"use strict";Ke();Ue();ft();j();Y();N();z();Bt="claude-code",Pt="claude-haiku-4-5-20251001",qt=24e3,Be=2e3,Xt=3e4});import{readFileSync as Gt}from"fs";import{homedir as Yt}from"os";import{join as Kt}from"path";function v(e={}){let t=(e.baseUrl??Ht).replace(/\/+$/,""),r=e.path??"/classifier",n=e.name??"simple-jev",o=e.maxStateChars??Zt,s=e.maxChoiceOptions??en,i={probabilitySource:e.probabilitySource??"logits",calibratedProbabilities:!1,maxChoiceOptions:s,maxStateChars:o,parallelQuestions:!0,images:!1};return{name:n,capabilities:i,async decide(a){A(a.questions),on(a.questions,s);let u=Date.now(),c=a.model??e.model;if(!c)throw new Error("simple-jev backend needs a model id");let l=G(a.state,o),p=e.fetchImpl??fetch,{signal:y,dispose:d}=sn(a.abortSignal,a.timeoutMs??e.timeoutMs??Wt);try{let b={"Content-Type":"application/json"},g=rn(e.apiKeyEnv,e.apiKeyFile);g&&(b.Authorization=`Bearer ${g}`);let w=await p(`${t}${r}`,{method:"POST",headers:b,body:JSON.stringify({model:c,state:l.text,questions:nn(a.questions)}),signal:y});if(!w.ok){let k=await w.text().catch(()=>"");throw new Error(`${n} ${w.status}: ${k.slice(0,300)}`)}let f=await w.json(),m=F(a.questions).safeParse(f.answers);if(!m.success)throw new Error(`${n} returned a response that does not match the requested questions (contract drift, not a model slip \u2014 retrying will not help):
113
+ ${U(m.error)}`);let h=m.data,M={},L=!1;for(let[k,q]of Object.entries(a.questions)){let x=_(q,h[k]);M[k]=x.answer,x.repaired&&(L=!0)}return{model:f.model??c,answers:M,usage:{inputTokens:f.usage?.input_tokens??0,outputTokens:f.usage?.output_tokens??0},meta:{backend:n,structureMode:i.probabilitySource==="native"?"native":"logprobs",answerMode:"probabilities",repaired:L,retries:0,stateTruncated:l.truncated,latencyMs:Date.now()-u}}}finally{d()}}}}function nn(e){let t={};for(let[r,n]of Object.entries(e))t[r]=n.instructions?n:{...n,instructions:tn[n.type]};return t}function rn(e,t){let r=e?process.env[e]:void 0;if(r)return r;if(!t)return;let n=t.startsWith("~/")?Kt(Yt(),t.slice(2)):t;if(!Z.has(n))try{let o=Gt(n,"utf-8").trim();Z.set(n,o.length>0?o:null)}catch{Z.set(n,null)}return Z.get(n)??void 0}function on(e,t){for(let[r,n]of Object.entries(e))if(n.type==="choice"){let o=Object.keys(n.criteria).length;if(o>t)throw new Error(`question "${r}": accepts at most ${t} choice options, got ${o}`)}else if(n.type==="score"&&n.criteria.length>t)throw new Error(`question "${r}": accepts at most ${t} score levels, got ${n.criteria.length}`)}function sn(e,t){let r=new AbortController,n=setTimeout(()=>r.abort(new Error(`decision timed out after ${t}ms`)),t),o=()=>r.abort(e?.reason);return e&&(e.aborted?o():e.addEventListener("abort",o,{once:!0})),{signal:r.signal,dispose:()=>{clearTimeout(n),e?.removeEventListener("abort",o)}}}var Ht,Wt,Zt,en,tn,Z,fe=E(()=>{"use strict";j();N();z();Y();Ht="http://127.0.0.1:8000/v1",Wt=3e4,Zt=6e3,en=50;tn={choice:"Which option best fits?",score:"Which level best fits?",noul:"Is this true of the state?"};Z=new Map});var qe={};Ye(qe,{DecisionStore:()=>I,MAX_CHOICE_OPTIONS:()=>MAX_CHOICE_OPTIONS,SYSTEM_PROMPT:()=>J,TOOL_NAME:()=>B,UNCERTAINTY_HIGH:()=>Ce,UNCERTAINTY_LOW:()=>De,_resetDecisionBackendsForTesting:()=>gt,agreement:()=>rt,answerView:()=>Q,applyRecalibrator:()=>ue,applyTemperature:()=>lt,askSeat:()=>pn,band:()=>Ie,bootstrapInterval:()=>pt,brier:()=>at,buildDesign:()=>Ne,calibrationReport:()=>mt,choice:()=>tt,compareCalibrators:()=>Lt,confidenceFor:()=>re,configureDecisions:()=>cn,coverageCurve:()=>ut,createLocalDecisionBackend:()=>W,createMockDecisionBackend:()=>H,createSimpleJevBackend:()=>v,decisionsRuntime:()=>ln,describeIssues:()=>U,ece:()=>st,expectedScore:()=>Ae,finalizeAnswer:()=>_,fitLogistic:()=>ve,fitRecalibrator:()=>P,fitTemperature:()=>dt,getDecisionBackend:()=>D,getSeatMode:()=>Ve,labelsOf:()=>S,listDecisionBackends:()=>Ee,logLoss:()=>ct,logit:()=>le,mce:()=>it,measureConsistency:()=>Qt,normalizeDistribution:()=>xe,normalizedNegEntropy:()=>oe,noul:()=>et,pMax:()=>Se,parseSeatMode:()=>ge,rawAnswerSchema:()=>Re,rawAnswersSchema:()=>F,recordSeatOutcome:()=>dn,registerBuiltinDecisionBackends:()=>an,registerDecisionBackend:()=>R,reliabilityBins:()=>ot,renderQuestions:()=>_e,renderShapeInstructions:()=>ae,renderState:()=>G,renderUserPrompt:()=>ie,resetDecisionsRuntime:()=>un,score:()=>nt,seatEnvVar:()=>$e,sigmoid:()=>de,toolInputSchema:()=>se,validateQuestions:()=>A,withUid:()=>Qe});function an(e={},t={},r={},n={}){R("mock",()=>H()),R("local",()=>W(e)),R("simple-jev",()=>v(t)),R("jev",()=>v({name:"jev",baseUrl:"https://openrouter.ai/api/alpha",path:"/decisions",model:"jev-latest",apiKeyEnv:"OPENROUTER_API_KEY",apiKeyFile:"~/.agentx/openrouter-key.txt",probabilitySource:"native",maxChoiceOptions:255,maxStateChars:9e4,...r})),R("typesafe",()=>v({name:"typesafe",baseUrl:"https://api.typesafe.ai/v1",path:"/systemone",model:"jev-latest",apiKeyEnv:"TYPESAFE_API_KEY",apiKeyFile:"~/.agentx/typesafe-key.txt",probabilitySource:"native",maxChoiceOptions:255,maxStateChars:9e4,...n}))}var Xe=E(()=>{"use strict";ke();N();j();z();C();Y();V();bt();Me();je();ze();pe();be();fe();C();pe();be();fe()});function cn(e){T={...ye,...T,...e},ee=!0}function un(){T={...ye,seats:{}},ee=!1}function mn(){if(!ee){ee=!0;try{let{loadDaemonConfig:e}=(Ze(),he(We)),t=e()?.decisions;if(!t?.enabled)return;let{registerBuiltinDecisionBackends:r}=(Xe(),he(qe)),n=t.backends??{};r(n.local,n.simpleJev,n.jev,n.typesafe);let o=null;try{o=new I({path:t.dbPath})}catch{}T={enabled:!0,defaultBackend:t.defaultBackend,store:o,seats:Object.fromEntries(Object.entries(t.seats??{}).map(([s,i])=>[s,{...i,redactState:t.redactState,keepStateRows:t.keepStateRows}]))}}catch{}}}function ln(){return T}function ge(e){if(typeof e!="string")return null;let t=e.trim().toLowerCase();return bn.includes(t)?t:null}function $e(e){return`AGENTX_DECISION_SEAT_${e.toUpperCase().replace(/[^A-Z0-9]+/g,"_")}`}function Ve(e,t=process.env){mn();let r=ge(t[$e(e)]);return r||(T.enabled?ge(T.seats[e]?.mode)??"off":"off")}function dn(e,t,r=!1){if(!(!e||!T.store))try{T.store.recordOutcome(e,t,r)}catch{}}async function pn(e,t,r,n={}){let o=Ve(e);if(o==="off")return null;let s=T.seats[e]??{mode:o},i=n.backend??s.backend??T.defaultBackend,a=Date.now();try{let c=await D(i).decide({state:t,questions:r,model:n.model??s.model,abortSignal:n.signal,timeoutMs:n.timeoutMs??s.timeoutMs}),l=fn(e,o,t,r,c,n,s);return{answers:c.answers,callId:l,mode:o}}catch(u){return gn(e,o,t,r,i,n,s,a,u),yn(e,i,u),null}}function fn(e,t,r,n,o,s,i){if(!T.store)return null;try{return T.store.recordCall({seat:e,mode:t,backend:o.meta.backend,model:o.model,meta:o.meta,state:r,questions:n,answers:o.answers,usage:o.usage,incumbent:Ge(s.incumbent),links:s.links,features:s.features,keepState:!i.redactState})}catch{return null}}function gn(e,t,r,n,o,s,i,a,u){if(T.store)try{T.store.recordCall({seat:e,mode:t,backend:o,model:s.model??i.model??"unknown",meta:{structureMode:"text",answerMode:"probabilities",retries:0,stateTruncated:!1,latencyMs:Date.now()-a},state:r,questions:n,answers:{},incumbent:Ge(s.incumbent),links:s.links,features:s.features,error:String(u?.message??u).slice(0,500),keepState:!i.redactState})}catch{}}function Ge(e){if(!e)return;let t={};for(let[r,n]of Object.entries(e))n!==void 0&&(t[r]={value:n});return t}function yn(e,t,r){let n=Date.now();n-Je<6e4||(Je=n,console.warn(`[decisions] seat "${e}" on backend "${t}" failed: ${r?.message??r}`))}var ye,T,ee,bn,Je,ze=E(()=>{C();V();ye={enabled:!1,defaultBackend:"local",seats:{},store:null},T={...ye},ee=!1;bn=["off","shadow","active"];Je=0});export{D as a,Ee as b,C as c,I as d,V as e,Lt as f,Me as g,Qt as h,je as i,me as j,Un as k,Ue as l,an as m,Xe as n,cn as o,un as p,ln as q,ge as r,$e as s,Ve as t,dn as u,pn as v,ze as w};
114
+ //# sourceMappingURL=chunk-AX2PWSRT.js.map