agentix-cli 0.22.0 → 0.25.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 (187) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +102 -103
  3. package/agentx.example.json +9 -3
  4. package/dist/App-6Y7B3YQH.js +8 -0
  5. package/dist/App-6Y7B3YQH.js.map +1 -0
  6. package/dist/ChatApp-KIEUJ2DS.js +7 -0
  7. package/dist/ChatApp-KIEUJ2DS.js.map +1 -0
  8. package/dist/{agent-Y4U4R3ZZ.js → agent-QGZIAZMY.js} +2 -2
  9. package/dist/architect-SEPWMWS4.js +2 -0
  10. package/dist/{board-dashboard-2CPBWFOB.js → board-dashboard-CQ5F4GPD.js} +3756 -1010
  11. package/dist/board-dashboard-CQ5F4GPD.js.map +1 -0
  12. package/dist/builtin-FRKAQMFS.js +2 -0
  13. package/dist/chunk-2WVX3LEC.js +2 -0
  14. package/dist/chunk-2WVX3LEC.js.map +1 -0
  15. package/dist/chunk-3WQFCIB6.js +24 -0
  16. package/dist/chunk-3WQFCIB6.js.map +1 -0
  17. package/dist/chunk-53GCFZPR.js +116 -0
  18. package/dist/chunk-53GCFZPR.js.map +1 -0
  19. package/dist/{chunk-IKBHHTIT.js → chunk-5U35WAZF.js} +2 -2
  20. package/dist/chunk-67Q4DCPS.js +54 -0
  21. package/dist/chunk-67Q4DCPS.js.map +1 -0
  22. package/dist/chunk-76Z4VHDX.js +8 -0
  23. package/dist/chunk-76Z4VHDX.js.map +1 -0
  24. package/dist/{chunk-7P5QRX5W.js → chunk-BFXONUUN.js} +2 -2
  25. package/dist/{chunk-P3TJHGO5.js → chunk-FDMWOPEL.js} +2 -2
  26. package/dist/chunk-FDWOECJY.js +54 -0
  27. package/dist/chunk-FDWOECJY.js.map +1 -0
  28. package/dist/chunk-G57RZCXM.js +5 -0
  29. package/dist/chunk-G57RZCXM.js.map +1 -0
  30. package/dist/{chunk-NOF4HSGK.js → chunk-H5D7ATXS.js} +2 -2
  31. package/dist/chunk-HA3XI7Z2.js +155 -0
  32. package/dist/chunk-HA3XI7Z2.js.map +1 -0
  33. package/dist/chunk-HR4JSRZS.js +3 -0
  34. package/dist/{chunk-A6BKSZQA.js.map → chunk-HR4JSRZS.js.map} +1 -1
  35. package/dist/chunk-I3SIXTJO.js +44 -0
  36. package/dist/chunk-I3SIXTJO.js.map +1 -0
  37. package/dist/chunk-ISEBLHWX.js +118 -0
  38. package/dist/chunk-ISEBLHWX.js.map +1 -0
  39. package/dist/{chunk-4ITQP3XL.js → chunk-JP5IEXR7.js} +2 -2
  40. package/dist/chunk-JXWAGMI2.js +13 -0
  41. package/dist/chunk-JXWAGMI2.js.map +1 -0
  42. package/dist/chunk-KUCCW2VG.js +431 -0
  43. package/dist/chunk-KUCCW2VG.js.map +1 -0
  44. package/dist/chunk-LDHPMJ7M.js +257 -0
  45. package/dist/chunk-LDHPMJ7M.js.map +1 -0
  46. package/dist/{chunk-WHP4I44J.js → chunk-LNXCJWOU.js} +29 -30
  47. package/dist/chunk-LNXCJWOU.js.map +1 -0
  48. package/dist/chunk-M4AMWP3H.js +178 -0
  49. package/dist/chunk-M4AMWP3H.js.map +1 -0
  50. package/dist/chunk-NGSIRE4X.js +126 -0
  51. package/dist/chunk-NGSIRE4X.js.map +1 -0
  52. package/dist/{chunk-CNNLREHD.js → chunk-P4VWIVKN.js} +268 -410
  53. package/dist/chunk-P4VWIVKN.js.map +1 -0
  54. package/dist/chunk-Q6YO7LYE.js +47 -0
  55. package/dist/chunk-Q6YO7LYE.js.map +1 -0
  56. package/dist/chunk-TMKLHTCG.js +971 -0
  57. package/dist/chunk-TMKLHTCG.js.map +1 -0
  58. package/dist/chunk-TRJTTBE2.js +7 -0
  59. package/dist/chunk-TRJTTBE2.js.map +1 -0
  60. package/dist/chunk-U2CKL755.js +4 -0
  61. package/dist/chunk-U2CKL755.js.map +1 -0
  62. package/dist/chunk-UCQ2N3KO.js +9 -0
  63. package/dist/chunk-UCQ2N3KO.js.map +1 -0
  64. package/dist/chunk-W4YR2RO2.js +2 -0
  65. package/dist/chunk-W4YR2RO2.js.map +1 -0
  66. package/dist/cli.js +1 -1
  67. package/dist/cli.js.map +1 -1
  68. package/dist/client-GGVVLOOF.js +2 -0
  69. package/dist/{compaction-BTUJJ3LJ.js → compaction-NWEMOSHA.js} +3 -3
  70. package/dist/config-7FSZFRTQ.js +2 -0
  71. package/dist/config-mutator-POCOYYJ4.js +2 -0
  72. package/dist/{context-planner-I7ID7XF3.js → context-planner-45GEWKGL.js} +2 -2
  73. package/dist/doctor-DHLBEKA4.js +2 -0
  74. package/dist/heal-HZ3EQD52.js +2 -0
  75. package/dist/index.d.ts +1244 -225
  76. package/dist/index.js +12 -12
  77. package/dist/index.js.map +1 -1
  78. package/dist/memory-extract-AENFVVO5.js +2 -0
  79. package/dist/processes-UIC4XXA6.js +2 -0
  80. package/dist/program-WVAVJFND.js +2 -0
  81. package/dist/projects-api-KRME2YKZ.js +2 -0
  82. package/dist/projects-api-KRME2YKZ.js.map +1 -0
  83. package/dist/projects-mutate-3NHVG437.js +7 -0
  84. package/dist/projects-mutate-3NHVG437.js.map +1 -0
  85. package/dist/providers-L27BJ5A6.js +2 -0
  86. package/dist/registry-T36ZIAWV.js +2 -0
  87. package/dist/rotation-memo-B3EXXSEH.js +13 -0
  88. package/dist/rotation-memo-B3EXXSEH.js.map +1 -0
  89. package/dist/scheduler-GH7TIKYS.js +2 -0
  90. package/dist/surface-usage-PRL6Z4U3.js +2 -0
  91. package/dist/wiki-M6YNRFC4.js +2 -0
  92. package/dist/workflows/templates/branching.yaml +75 -0
  93. package/dist/workflows/templates/extract.yaml +59 -0
  94. package/dist/workflows/templates/linear.yaml +37 -0
  95. package/dist/workflows/templates/retry.yaml +61 -0
  96. package/dist/workspace-setup-TBQAHYHJ.js +2 -0
  97. package/package.json +11 -3
  98. package/dist/board-dashboard-2CPBWFOB.js.map +0 -1
  99. package/dist/chunk-2HXSJYAJ.js +0 -15
  100. package/dist/chunk-2HXSJYAJ.js.map +0 -1
  101. package/dist/chunk-5F3J55LG.js +0 -2
  102. package/dist/chunk-5F3J55LG.js.map +0 -1
  103. package/dist/chunk-A6BKSZQA.js +0 -3
  104. package/dist/chunk-ABVZHJUO.js +0 -7
  105. package/dist/chunk-ABVZHJUO.js.map +0 -1
  106. package/dist/chunk-BHSPAP4U.js +0 -44
  107. package/dist/chunk-BHSPAP4U.js.map +0 -1
  108. package/dist/chunk-CDNWCYF7.js +0 -2
  109. package/dist/chunk-CDNWCYF7.js.map +0 -1
  110. package/dist/chunk-CNNLREHD.js.map +0 -1
  111. package/dist/chunk-CO4YCIIA.js +0 -13
  112. package/dist/chunk-CO4YCIIA.js.map +0 -1
  113. package/dist/chunk-EW27SCCC.js +0 -2
  114. package/dist/chunk-EW27SCCC.js.map +0 -1
  115. package/dist/chunk-GOLZ6NEL.js +0 -111
  116. package/dist/chunk-GOLZ6NEL.js.map +0 -1
  117. package/dist/chunk-JGXE2YNV.js +0 -46
  118. package/dist/chunk-JGXE2YNV.js.map +0 -1
  119. package/dist/chunk-KX626FN4.js +0 -6
  120. package/dist/chunk-KX626FN4.js.map +0 -1
  121. package/dist/chunk-MTRWXRFF.js +0 -380
  122. package/dist/chunk-MTRWXRFF.js.map +0 -1
  123. package/dist/chunk-N67JZAXU.js +0 -2
  124. package/dist/chunk-N67JZAXU.js.map +0 -1
  125. package/dist/chunk-OPKQNLGX.js +0 -450
  126. package/dist/chunk-OPKQNLGX.js.map +0 -1
  127. package/dist/chunk-OZOQKPJV.js +0 -102
  128. package/dist/chunk-OZOQKPJV.js.map +0 -1
  129. package/dist/chunk-VYLZ7UYM.js +0 -118
  130. package/dist/chunk-VYLZ7UYM.js.map +0 -1
  131. package/dist/chunk-WHP4I44J.js.map +0 -1
  132. package/dist/chunk-WJXU5F4Z.js +0 -53
  133. package/dist/chunk-WJXU5F4Z.js.map +0 -1
  134. package/dist/chunk-X5D5AV5N.js +0 -833
  135. package/dist/chunk-X5D5AV5N.js.map +0 -1
  136. package/dist/config-BLOKGMER.js +0 -2
  137. package/dist/config-mutator-RG7QY2EL.js +0 -2
  138. package/dist/doctor-ZTECTUHX.js +0 -2
  139. package/dist/editor-chat-RVLCKKB7.js +0 -137
  140. package/dist/editor-chat-RVLCKKB7.js.map +0 -1
  141. package/dist/heal-YEQYWX2L.js +0 -2
  142. package/dist/inbox-BIN2S5XN.js +0 -2
  143. package/dist/memory-extract-KRCHD3OP.js +0 -2
  144. package/dist/processes-RJ6RDP32.js +0 -2
  145. package/dist/program-GZOJIWTB.js +0 -2
  146. package/dist/providers-WVCMIW67.js +0 -2
  147. package/dist/registry-QZIUSQ5J.js +0 -2
  148. package/dist/rotation-memo-GMTZPMNE.js +0 -13
  149. package/dist/rotation-memo-GMTZPMNE.js.map +0 -1
  150. package/dist/scheduler-B2BQPV4M.js +0 -2
  151. package/dist/slack-6P7ZJ32Y.js +0 -3
  152. package/dist/slack-6P7ZJ32Y.js.map +0 -1
  153. package/dist/store-BAPDZHXE.js +0 -2
  154. package/dist/task-store-RHISEU6V.js +0 -2
  155. package/dist/telegram-PIYDRM46.js +0 -3
  156. package/dist/telegram-PIYDRM46.js.map +0 -1
  157. package/dist/types-ZRLHHCGB.js +0 -2
  158. package/dist/web/workflow-editor.global.js +0 -1452
  159. package/dist/web/workflow-editor.global.js.map +0 -1
  160. package/dist/whatsapp-PFRDMUMT.js +0 -3
  161. package/dist/whatsapp-PFRDMUMT.js.map +0 -1
  162. package/dist/wiki-53W5NLZV.js +0 -2
  163. package/dist/workspace-setup-DQCZX2NE.js +0 -2
  164. /package/dist/{agent-Y4U4R3ZZ.js.map → agent-QGZIAZMY.js.map} +0 -0
  165. /package/dist/{config-BLOKGMER.js.map → architect-SEPWMWS4.js.map} +0 -0
  166. /package/dist/{config-mutator-RG7QY2EL.js.map → builtin-FRKAQMFS.js.map} +0 -0
  167. /package/dist/{chunk-IKBHHTIT.js.map → chunk-5U35WAZF.js.map} +0 -0
  168. /package/dist/{chunk-7P5QRX5W.js.map → chunk-BFXONUUN.js.map} +0 -0
  169. /package/dist/{chunk-P3TJHGO5.js.map → chunk-FDMWOPEL.js.map} +0 -0
  170. /package/dist/{chunk-NOF4HSGK.js.map → chunk-H5D7ATXS.js.map} +0 -0
  171. /package/dist/{chunk-4ITQP3XL.js.map → chunk-JP5IEXR7.js.map} +0 -0
  172. /package/dist/{doctor-ZTECTUHX.js.map → client-GGVVLOOF.js.map} +0 -0
  173. /package/dist/{compaction-BTUJJ3LJ.js.map → compaction-NWEMOSHA.js.map} +0 -0
  174. /package/dist/{heal-YEQYWX2L.js.map → config-7FSZFRTQ.js.map} +0 -0
  175. /package/dist/{inbox-BIN2S5XN.js.map → config-mutator-POCOYYJ4.js.map} +0 -0
  176. /package/dist/{context-planner-I7ID7XF3.js.map → context-planner-45GEWKGL.js.map} +0 -0
  177. /package/dist/{memory-extract-KRCHD3OP.js.map → doctor-DHLBEKA4.js.map} +0 -0
  178. /package/dist/{processes-RJ6RDP32.js.map → heal-HZ3EQD52.js.map} +0 -0
  179. /package/dist/{program-GZOJIWTB.js.map → memory-extract-AENFVVO5.js.map} +0 -0
  180. /package/dist/{providers-WVCMIW67.js.map → processes-UIC4XXA6.js.map} +0 -0
  181. /package/dist/{registry-QZIUSQ5J.js.map → program-WVAVJFND.js.map} +0 -0
  182. /package/dist/{scheduler-B2BQPV4M.js.map → providers-L27BJ5A6.js.map} +0 -0
  183. /package/dist/{store-BAPDZHXE.js.map → registry-T36ZIAWV.js.map} +0 -0
  184. /package/dist/{task-store-RHISEU6V.js.map → scheduler-GH7TIKYS.js.map} +0 -0
  185. /package/dist/{types-ZRLHHCGB.js.map → surface-usage-PRL6Z4U3.js.map} +0 -0
  186. /package/dist/{wiki-53W5NLZV.js.map → wiki-M6YNRFC4.js.map} +0 -0
  187. /package/dist/{workspace-setup-DQCZX2NE.js.map → workspace-setup-TBQAHYHJ.js.map} +0 -0
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/daemon/ui/tokens.ts","../src/daemon/ui/components.css.ts","../src/daemon/topbar.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. Previously each page file (live, boards, admin,\n// graph, agent, setup) carried its own copy of :root + [data-theme=light] +\n// [data-theme=crt] — ~60 lines of CSS duplicated 6 times. Now they import\n// AX_TOKENS_CSS and drop it in their <style> block once.\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 --ax-bg: oklch(0.16 0.010 265);\n --ax-bg-elev: oklch(0.19 0.012 265);\n --ax-surface: oklch(0.21 0.012 265);\n --ax-surface-2: oklch(0.24 0.014 265);\n --ax-surface-3: oklch(0.27 0.016 265);\n --ax-border: oklch(0.29 0.014 265);\n --ax-border-2: oklch(0.35 0.016 265);\n --ax-text: oklch(0.95 0.005 265);\n --ax-text-2: oklch(0.80 0.008 265);\n --ax-muted: oklch(0.60 0.010 265);\n --ax-accent: oklch(0.78 0.13 165);\n --ax-accent-2: oklch(0.55 0.11 165);\n --ax-warn: oklch(0.80 0.14 75);\n --ax-err: oklch(0.68 0.19 25);\n --ax-info: oklch(0.78 0.10 220);\n --ax-radius: 6px;\n --ax-radius-lg: 8px;\n --ax-radius-sm: 5px;\n --ax-pad: 16px;\n --ax-pad-sm: 10px;\n --ax-gap: 12px;\n --ax-font: \"IBM Plex Sans\", -apple-system, \"Segoe UI\", sans-serif;\n --ax-mono: \"IBM Plex Mono\", ui-monospace, \"SF Mono\", Consolas, monospace;\n --ax-fs: 13px;\n --ax-fs-sm: 12px;\n --ax-fs-xs: 11px;\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 --green: 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: dark;\n}\n[data-theme=\"light\"] {\n --ax-bg: oklch(0.98 0.002 265);\n --ax-bg-elev: oklch(0.96 0.003 265);\n --ax-surface: oklch(0.99 0.002 265);\n --ax-surface-2: oklch(0.955 0.003 265);\n --ax-surface-3: oklch(0.92 0.004 265);\n --ax-border: oklch(0.88 0.006 265);\n --ax-border-2: oklch(0.78 0.008 265);\n --ax-text: oklch(0.22 0.010 265);\n --ax-text-2: oklch(0.36 0.010 265);\n --ax-muted: oklch(0.54 0.010 265);\n --ax-accent: oklch(0.55 0.14 165);\n color-scheme: light;\n}\n[data-theme=\"crt\"] {\n --ax-bg: #05140a;\n --ax-bg-elev: #061a0d;\n --ax-surface: #08201f;\n --ax-surface-2: #0b2922;\n --ax-surface-3: #0e3329;\n --ax-border: #164a30;\n --ax-border-2: #1f6a44;\n --ax-text: #b7ffcc;\n --ax-text-2: #83e3a8;\n --ax-muted: #4f9a73;\n --ax-accent: #6dff9e;\n --ax-font: \"IBM Plex Mono\", ui-monospace, monospace;\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; font-feature-settings: \"ss01\", \"cv01\";\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; }\na:hover { text-decoration: underline; }\n.ax-muted { color: var(--ax-muted); }\n.ax-accent { color: var(--ax-accent); }`\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: 1px 7px; border-radius: 3px; font-size: var(--ax-fs-xs); line-height: 16px;\n border: 1px solid var(--ax-border-2); color: var(--ax-text-2); background: transparent;\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: transparent; color: var(--ax-text-2);\n border: 1px solid var(--ax-border-2); border-radius: var(--ax-radius);\n padding: 7px 14px; font: inherit; font-size: var(--ax-fs); cursor: pointer;\n transition: border-color 0.1s, color 0.1s, background 0.1s;\n line-height: 1.4;\n}\n.ax-btn:hover:not(:disabled) { color: var(--ax-text); border-color: var(--ax-accent); }\n.ax-btn:disabled { opacity: 0.5; cursor: not-allowed; }\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: 600;\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);\n}\n\n.ax-btn--danger {\n color: var(--ax-err);\n border-color: color-mix(in oklch, var(--ax-err) 50%, var(--ax-border-2));\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: 10px; line-height: 16px; border-radius: 3px; color: var(--ax-muted);\n border: 1px solid var(--ax-border); background: var(--ax-surface);\n}\n\n/* --- Cards --- */\n.ax-card {\n background: var(--ax-surface); border: 1px solid var(--ax-border);\n border-radius: var(--ax-radius); padding: var(--ax-pad);\n}\n\n/* Step cards used by the setup wizard / onboarding-style flows. */\n.ax-step {\n background: var(--ax-surface); border: 1px solid var(--ax-border);\n border-radius: 8px; 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: 4px;\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: 1px 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: 1px solid var(--ax-border-2); border-radius: var(--ax-radius);\n padding: 8px 10px; 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: 3px;\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: 1px 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: 3px;\n border: 1px 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: 10px;\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: 1px 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: 1px solid var(--ax-border); border-radius: 4px;\n color: var(--ax-text);\n}\n\n/* --- Avatars (coloured initial square) --- */\n.ax-avatar {\n width: 36px; height: 36px; border-radius: 9px; flex-shrink: 0;\n background: var(--ax-surface-2); border: 1px 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: 10px; background: var(--ax-surface-2);\n color: var(--ax-text-2); border: 1px 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: 1px 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: 10px;\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: 1px 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: 9px; 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: 1px solid var(--ax-accent); color: var(--ax-text);\n padding: 10px 16px; border-radius: 6px; 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: 1px solid var(--ax-border-2);\n border-radius: 12px; 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: 1px 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: 1px solid var(--ax-border);\n}\n\n/* --- Mesh hero (enable toggle + SVG network viz) --- */\n.ax-mesh-hero {\n background: var(--ax-surface); border: 1px 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: 1px 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: 9px; 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: 1px 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: 1px solid var(--ax-border); border-radius: 5px;\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: 1px solid var(--ax-border); border-radius: 6px; 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: 4px; 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: 1px 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: 1px solid var(--ax-border-2);\n color: var(--ax-accent); padding: 3px 10px; border-radius: 6px;\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: 6px;\n background: var(--ax-surface); border: 1px 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: 1px solid var(--ax-border);\n padding: 1px 6px; border-radius: 3px; 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: 1px 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: 1px solid var(--ax-border); border-radius: 5px; 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: 4px;\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: 1px solid var(--ax-border);\n border-radius: 5px; 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: 1px 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: 3px; 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: 1px 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: 6px;\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: 6px;\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: 2px; 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: 9px; 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: 1px 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: 1px solid var(--ax-border); border-radius: 5px; 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: 3px; 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: 1px solid var(--ax-border); border-radius: 6px;\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: 4px; 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: 1px solid var(--ax-border);\n padding: 7px; border-radius: 5px; 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: 4px; 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: 3px;\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: 3px;\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: 1px solid var(--ax-border); border-radius: 5px; 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: 3px;\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: 1px 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: 7px;\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: 3px;\n border: 1px solid var(--ax-border);\n}\n\n/* Channel-binding rows (agent-specific view) */\n.ax-chan-row {\n background: var(--ax-bg-elev); border: 1px 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: 7px;\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: 1px 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: 10px;\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: 1px 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: 1px 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: 1px 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: 1px 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: 5px;\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: 1px 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: 1px 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: 1px 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: 1px 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: 1px solid var(--ax-border); border-radius: 5px; 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: 3px; 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: 1px 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: 1px solid var(--ax-border);\n border-radius: 10px; 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: 12px;\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: 1px 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: 1px solid var(--ax-border);\n border-radius: 12px; 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: 1px 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: 1px solid var(--ax-border);\n border-radius: 6px; 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: 6px; 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/* 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","// --- Shared top-chrome for every dashboard surface ---\n//\n// Four pages render in different files (live + kanban + glossary in\n// board-dashboard.ts, settings in admin-panel.ts) but must share: design\n// tokens, fonts, theme switcher, brand, primary tab nav, and a mesh-node\n// selector. This module exports the CSS + HTML snippets so each page\n// composes exactly the same chrome and we never drift them again.\n//\n// Nothing here is page-specific — callers pass in { activeTab, subtitle,\n// subheader? } and compose their own <main> below.\n\nexport type TopbarTab = \"live\" | \"boards\" | \"admin\" | \"graph\" | \"glossary\" | \"workflows\" | \"health\" | \"cost\" | \"wiki\"\n\nexport interface TopbarPeer {\n /** Stable id: primary node id, or URL for configured daemons */\n id: string\n name: string\n /** Absolute URL of the peer's dashboard (http://host:port) — selector\n * navigates to <url>/admin when switched. */\n dashboardUrl: string\n /** True for the node hosting this dashboard (locks the default). */\n primary?: boolean\n /** Optional; shown in the dropdown as \"managing via token\" hint. */\n tokenScope?: string\n}\n\nexport interface TopbarOpts {\n activeTab: TopbarTab\n /** Text after the brand, e.g. \"Live\" / \"Settings\" / \"Boards\". */\n subtitle: string\n /** Optional HTML inserted as a second row under the topbar. */\n subheader?: string\n /** Peers to list in the mesh selector. */\n peers?: TopbarPeer[]\n /** id of the peer currently being managed — defaults to the primary. */\n currentPeerId?: string\n /** Optional right-side HTML added before the theme switcher (clocks,\n * connection dots, etc.). */\n rightExtras?: string\n}\n\n/**\n * Font link tags + tiny inline bootstrap script. Callers drop this in <head>.\n */\nexport const TOPBAR_HEAD = `<link rel=\"preconnect\" href=\"https://fonts.googleapis.com\">\n<link rel=\"preconnect\" href=\"https://fonts.gstatic.com\" crossorigin>\n<link href=\"https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;500;600&family=IBM+Plex+Sans:wght@400;500;600;700&display=swap\" rel=\"stylesheet\">\n<script>(function(){try{var t=localStorage.getItem('ax-theme')||'dark';document.documentElement.setAttribute('data-theme',t)}catch(e){}})();</script>`\n\n/**\n * CSS for topbar + subheader + theme switch + mesh selector. No token\n * definitions — those are per-page because each page's CSS block owns its\n * own :root overrides.\n */\nexport const TOPBAR_CSS = `\n.ax-topbar{display:flex;align-items:center;justify-content:space-between;\n padding:8px 18px;border-bottom:1px solid var(--ax-border);\n background:var(--ax-bg-elev);position:sticky;top:0;z-index:20;gap:10px;flex-wrap:nowrap}\n.ax-topbar__left{display:flex;align-items:center;gap:18px;min-width:0}\n.ax-brand{display:flex;align-items:center;gap:10px;font-weight:600;letter-spacing:-0.01em;flex-shrink:0}\n.ax-brand__mark{font-family:var(--ax-mono);font-size:12px;padding:2px 8px;\n border:1px solid var(--ax-border-2);color:var(--ax-accent);border-radius:4px}\n.ax-brand__name{font-size:14px}\n.ax-brand__subtitle{font-size:11px;color:var(--ax-muted);\n border-left:1px solid var(--ax-border);padding-left:12px;margin-left:4px}\n.ax-topbar__tabs{display:flex;gap:2px;min-width:0;overflow-x:auto}\n.ax-topbar__tab{background:transparent;border:none;color:var(--ax-text-2);\n padding:8px 14px;font:inherit;cursor:pointer;font-size:12px;text-decoration:none;\n border-bottom:2px solid transparent;letter-spacing:-0.005em;white-space:nowrap}\n.ax-topbar__tab:hover{color:var(--ax-text)}\n.ax-topbar__tab.is-active{color:var(--ax-text);border-bottom-color:var(--ax-accent)}\n.ax-topbar__right{display:flex;align-items:center;gap:10px;font-size:11px;\n color:var(--ax-muted);flex-shrink:0}\n.ax-topbar__right .ax-mono{color:var(--ax-text-2)}\n\n/* Mesh selector */\n.ax-mesh-sel{position:relative;display:inline-flex;align-items:center;gap:6px;\n padding:4px 10px;border:1px solid var(--ax-border-2);border-radius:4px;\n background:var(--ax-surface);cursor:pointer;font:inherit;font-size:11px;\n color:var(--ax-text-2);white-space:nowrap}\n.ax-mesh-sel:hover{border-color:var(--ax-accent);color:var(--ax-text)}\n.ax-mesh-sel.is-proxy{background:color-mix(in oklch,var(--ax-accent) 14%,var(--ax-surface));\n border-color:color-mix(in oklch,var(--ax-accent) 45%,var(--ax-border-2));color:var(--ax-accent)}\n.ax-mesh-sel .lbl{font-size:9px;letter-spacing:0.06em;text-transform:uppercase;color:var(--ax-muted);margin-right:2px}\n.ax-mesh-sel .name{font-weight:500;color:var(--ax-text)}\n.ax-mesh-sel .chev{font-size:9px;opacity:0.6;margin-left:2px}\n.ax-mesh-menu{position:absolute;top:calc(100% + 4px);right:0;min-width:240px;\n background:var(--ax-surface);border:1px solid var(--ax-border-2);\n border-radius:6px;box-shadow:0 8px 24px rgba(0,0,0,0.4);padding:4px;z-index:30;display:none}\n.ax-mesh-sel.is-open .ax-mesh-menu{display:block}\n.ax-mesh-menu a{display:block;padding:7px 10px;border-radius:4px;text-decoration:none;\n color:var(--ax-text-2);font-size:12px}\n.ax-mesh-menu a:hover{background:var(--ax-surface-2);color:var(--ax-text)}\n.ax-mesh-menu a.is-current{background:color-mix(in oklch,var(--ax-accent) 12%,var(--ax-surface));\n color:var(--ax-accent);border:1px solid color-mix(in oklch,var(--ax-accent) 30%,var(--ax-border-2))}\n.ax-mesh-menu a .row{display:flex;align-items:center;gap:8px}\n.ax-mesh-menu a .row .dot{width:6px;height:6px;border-radius:50%;background:var(--ax-accent)}\n.ax-mesh-menu a.is-offline .row .dot{background:var(--ax-border-2)}\n.ax-mesh-menu a .url{font-family:var(--ax-mono);font-size:10px;color:var(--ax-muted);margin-top:2px}\n\n/* Theme switcher segmented control */\n.ax-theme-switch{display:inline-flex;border:1px solid var(--ax-border-2);\n border-radius:4px;overflow:hidden}\n.ax-theme-switch button{background:transparent;border:none;color:var(--ax-muted);\n padding:3px 9px;font:inherit;font-size:10px;cursor:pointer;letter-spacing:0.04em;\n text-transform:uppercase;font-family:var(--ax-mono);border-right:1px solid var(--ax-border-2)}\n.ax-theme-switch button:last-child{border-right:none}\n.ax-theme-switch button:hover{color:var(--ax-text);background:var(--ax-surface-2,var(--ax-surface))}\n.ax-theme-switch button.is-active{color:var(--ax-accent);\n background:color-mix(in oklch,var(--ax-accent) 12%,var(--ax-surface))}\n\n/* Subheader — page-specific toolbar below the main topbar. Kanban uses it\n * for the board picker + search + filters; others can use it for context\n * bars if they want. */\n.ax-subheader{display:flex;align-items:center;gap:10px;padding:10px 18px;\n background:var(--ax-bg-elev);border-bottom:1px solid var(--ax-border);\n flex-wrap:wrap}\n.ax-subheader > * {flex-shrink:0}\n.ax-subheader .spacer{flex:1}`\n\n/**\n * Shared click wiring for the theme switcher + mesh selector. Safe to call\n * multiple times — only binds once per element.\n */\nexport const TOPBAR_SCRIPT = `<script>\n(function(){\n function wireTheme(){\n var sw = document.querySelectorAll('[data-theme-opt]');\n if (!sw.length) return;\n var current = document.documentElement.getAttribute('data-theme') || 'dark';\n sw.forEach(function(b){\n if (b.dataset.wired) return; b.dataset.wired = '1';\n if (b.getAttribute('data-theme-opt') === current) b.classList.add('is-active');\n b.addEventListener('click', function(){\n var t = b.getAttribute('data-theme-opt');\n document.documentElement.setAttribute('data-theme', t);\n try { localStorage.setItem('ax-theme', t); } catch (e) {}\n sw.forEach(function(x){ x.classList.toggle('is-active', x === b); });\n });\n });\n }\n function currentPeer(){\n try { return localStorage.getItem('ax-peer') || 'primary'; } catch { return 'primary'; }\n }\n function setCurrentPeer(id){\n try {\n if (id && id !== 'primary') localStorage.setItem('ax-peer', id);\n else localStorage.removeItem('ax-peer');\n } catch {}\n }\n function reflectPeerPill(){\n var sel = document.querySelector('.ax-mesh-sel');\n if (!sel) return;\n var cur = currentPeer();\n sel.classList.toggle('is-proxy', cur !== 'primary');\n // If a proxy is active, update the visible pill label + highlight.\n var picked = null;\n sel.querySelectorAll('.ax-mesh-menu a').forEach(function(a){\n a.classList.toggle('is-current', a.getAttribute('data-peer-id') === cur || (cur === 'primary' && a.getAttribute('data-peer-id') === 'primary'));\n if (a.getAttribute('data-peer-id') === cur) picked = a;\n });\n if (picked) {\n var name = picked.querySelector('span'); // first <span> inside .row\n var visibleName = sel.querySelector('.name');\n if (name && visibleName) visibleName.textContent = name.textContent.replace(/ · primary/, '');\n }\n }\n function wireMesh(){\n var sel = document.querySelector('.ax-mesh-sel');\n if (!sel || sel.dataset.wired) return; sel.dataset.wired = '1';\n sel.addEventListener('click', function(e){\n var link = e.target.closest('.ax-mesh-menu a[data-peer-action=\"proxy\"]');\n if (link) {\n e.preventDefault();\n setCurrentPeer(link.getAttribute('data-peer-id'));\n reflectPeerPill();\n sel.classList.remove('is-open');\n // Reload so the admin panel re-reads state from the selected peer.\n location.reload();\n return;\n }\n if (e.target.closest('.ax-mesh-menu a')) return; // plain navigate anchor\n sel.classList.toggle('is-open');\n });\n document.addEventListener('click', function(e){\n if (!sel.contains(e.target)) sel.classList.remove('is-open');\n });\n reflectPeerPill();\n }\n function wireHostRewrite(){\n try {\n var h = location.hostname || 'localhost';\n document.querySelectorAll('.ax-topbar__tab[href*=\"__HOST__\"]').forEach(function(a){\n a.href = a.getAttribute('href').replace(/__HOST__/g, h);\n });\n } catch (e) {}\n }\n /**\n * Make the topbar peer selector actually drive page content. When the\n * operator picks a non-primary peer, every /api/admin/* request gets an\n * X-Agentx-Peer header (and every EventSource on that path gets a ?peer=\n * query, since EventSource can't set headers). The dashboard's existing\n * proxy handler picks it up and forwards to the chosen peer.\n *\n * Without this hook, the activity-graph (and other admin pages) silently\n * keep showing only the local node's data even after the operator picks\n * a different peer in the topbar.\n */\n function wirePeerProxy(){\n if (window.__axPeerWired) return; window.__axPeerWired = true;\n var origFetch = window.fetch.bind(window);\n window.fetch = function(input, init){\n try {\n var peer = currentPeer();\n if (peer && peer !== 'primary') {\n var url = typeof input === 'string' ? input : (input && input.url) || '';\n if (url.indexOf('/api/admin/') === 0 || url.indexOf('/api/admin/') > 0) {\n init = init || {};\n init.headers = new Headers(init.headers || {});\n init.headers.set('X-Agentx-Peer', peer);\n }\n }\n } catch (e) {}\n return origFetch(input, init);\n };\n var OrigES = window.EventSource;\n if (OrigES) {\n window.EventSource = function(url, cfg){\n try {\n var peer = currentPeer();\n if (peer && peer !== 'primary' && typeof url === 'string' && url.indexOf('/api/admin/') >= 0) {\n url += (url.indexOf('?') >= 0 ? '&' : '?') + 'peer=' + encodeURIComponent(peer);\n }\n } catch (e) {}\n return new OrigES(url, cfg);\n };\n window.EventSource.prototype = OrigES.prototype;\n window.EventSource.CONNECTING = OrigES.CONNECTING;\n window.EventSource.OPEN = OrigES.OPEN;\n window.EventSource.CLOSED = OrigES.CLOSED;\n }\n }\n function wire(){ wireHostRewrite(); wireTheme(); wireMesh(); wirePeerProxy(); }\n if (document.readyState === 'loading') {\n document.addEventListener('DOMContentLoaded', wire);\n } else { wire(); }\n})();\n</script>`\n\n/**\n * Render the topbar element. Caller places this at the start of <body>.\n * Emits a matching .ax-subheader row only when `subheader` HTML is provided.\n */\nexport function renderTopbar(opts: TopbarOpts): string {\n const peers = opts.peers && opts.peers.length > 0 ? opts.peers : [{ id: \"local\", name: \"this daemon\", dashboardUrl: \"\", primary: true }]\n const currentId = opts.currentPeerId || peers.find((p) => p.primary)?.id || peers[0].id\n const current = peers.find((p) => p.id === currentId) || peers[0]\n\n const meshMenu = peers.map((p) => {\n const isCurrent = p.id === currentId\n const primaryMark = p.primary ? ' <span style=\"color:var(--ax-muted);font-size:10px\">· primary</span>' : \"\"\n const proxyable = !!p.tokenScope && !p.primary\n // Proxyable peers: use data-peer-id so the click handler switches locally\n // via localStorage + reload. Un-proxyable (no token): legacy navigate.\n const href = proxyable\n ? \"#\"\n : p.dashboardUrl\n ? `${p.dashboardUrl.replace(/\\/+$/, \"\")}/admin`\n : \"#\"\n const dataAttr = proxyable ? ` data-peer-id=\"${esc(p.id)}\" data-peer-action=\"proxy\"` : p.primary ? ` data-peer-id=\"primary\" data-peer-action=\"proxy\"` : \"\"\n const hint = proxyable\n ? '<div class=\"url\" style=\"color:var(--ax-accent)\">manage from this dashboard ✓</div>'\n : p.tokenScope\n ? `<div class=\"url\">${esc(p.tokenScope)}</div>`\n : p.dashboardUrl\n ? '<div class=\"url\" style=\"color:var(--ax-muted)\">(open peer dashboard ↗)</div>'\n : \"\"\n return `<a href=\"${esc(href)}\" class=\"${isCurrent ? \"is-current\" : \"\"}\"${dataAttr}>\n <div class=\"row\"><span class=\"dot\"></span><span>${esc(p.name)}${primaryMark}</span></div>\n ${p.dashboardUrl ? `<div class=\"url\">${esc(p.dashboardUrl)}</div>` : \"\"}\n ${hint}\n </a>`\n }).join(\"\")\n\n const meshSelector = `<div class=\"ax-mesh-sel\" aria-haspopup=\"menu\">\n <span class=\"lbl\">managing</span>\n <span class=\"name\">${esc(current.name)}</span>\n <span class=\"chev\">▾</span>\n <div class=\"ax-mesh-menu\" role=\"menu\">${meshMenu}</div>\n </div>`\n\n // Graph tab is intentionally not listed — the page is still reachable at\n // /admin/graph for debugging the classifier, but we don't promote it in the\n // main nav until it earns its keep (wiki absorb writing graphPath, or\n // graph-based routing).\n //\n // Health = SRE/platform-health (routing rules, rotations, errors-with-stack).\n // Renamed from \"Observability\" — that name suggested business-flow but\n // the page only ever covered platform health.\n // Cost = top-level page (was a tab inside Observability + a standalone\n // `agentx usage serve` on port 4201). Both folded here.\n // Wiki = embedded view over the running wiki server (`agentx wiki serve`,\n // default port 4200). One-stop nav; the wiki server is still where the\n // actual rendering happens.\n const tabs = [\n { id: \"live\", label: \"Live\", href: \"/live\", external: false },\n { id: \"boards\", label: \"Boards\", href: \"/\", external: false },\n { id: \"workflows\", label: \"Workflows\", href: \"/workflows\", external: false },\n { id: \"graph\", label: \"Activity Graph\", href: \"/admin/activity-graph\", external: false },\n { id: \"graph\", label: \"Ledger\", href: \"/admin/ledger\", external: false },\n { id: \"health\", label: \"Health\", href: \"/admin/health\", external: false },\n { id: \"cost\", label: \"Cost\", href: \"/admin/cost\", external: false },\n { id: \"wiki\", label: \"Wiki\", href: \"/admin/wiki\", external: false },\n { id: \"admin\", label: \"Settings\", href: \"/admin\", external: false },\n { id: \"glossary\", label: \"Glossary\", href: \"/glossary\", external: false },\n ].map((t) => {\n const cls = t.id === opts.activeTab ? \"ax-topbar__tab is-active\" : \"ax-topbar__tab\"\n const extAttrs = t.external ? ` target=\"_blank\" rel=\"noopener\"` : \"\"\n const extGlyph = t.external ? ' <span style=\"font-size:9px;opacity:0.6\">↗</span>' : \"\"\n return `<a href=\"${t.href}\" class=\"${cls}\"${extAttrs}>${t.label}${extGlyph}</a>`\n }).join(\"\")\n\n const header = `<header class=\"ax-topbar\">\n <div class=\"ax-topbar__left\">\n <div class=\"ax-brand\">\n <span class=\"ax-brand__mark\">AX</span>\n <span class=\"ax-brand__name\">AgentX</span>\n <span class=\"ax-brand__subtitle\">${esc(opts.subtitle)}</span>\n </div>\n <nav class=\"ax-topbar__tabs\">${tabs}</nav>\n </div>\n <div class=\"ax-topbar__right\">\n ${opts.rightExtras || \"\"}\n ${meshSelector}\n <div class=\"ax-theme-switch\" role=\"tablist\" aria-label=\"Theme\">\n <button data-theme-opt=\"dark\">Dark</button>\n <button data-theme-opt=\"light\">Light</button>\n <button data-theme-opt=\"crt\">CRT</button>\n </div>\n </div>\n</header>${opts.subheader ? `<div class=\"ax-subheader\">${opts.subheader}</div>` : \"\"}`\n return header\n}\n\nfunction esc(s: string): string {\n return String(s || \"\").replace(/[&<>\"']/g, (c) => ({ \"&\": \"&amp;\", \"<\": \"&lt;\", \">\": \"&gt;\", '\"': \"&quot;\", \"'\": \"&#39;\" }[c] as string))\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 { 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 /** 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}${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${TOPBAR_SCRIPT}\n${opts.scripts || \"\"}\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":"AAUO,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;yCCDtB,IAAMC,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;GCmC1B,IAAMC,EAAc;AAAA;AAAA;AAAA,uJAUdC,EAAa;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,+BAsEbC,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,WAiItB,SAASC,EAAaC,EAA0B,CACrD,IAAMC,EAAQD,EAAK,OAASA,EAAK,MAAM,OAAS,EAAIA,EAAK,MAAQ,CAAC,CAAE,GAAI,QAAS,KAAM,cAAe,aAAc,GAAI,QAAS,EAAK,CAAC,EACjIE,EAAYF,EAAK,eAAiBC,EAAM,KAAME,GAAMA,EAAE,OAAO,GAAG,IAAMF,EAAM,CAAC,EAAE,GAC/EG,EAAUH,EAAM,KAAME,GAAMA,EAAE,KAAOD,CAAS,GAAKD,EAAM,CAAC,EAE1DI,EAAWJ,EAAM,IAAKE,GAAM,CAChC,IAAMG,EAAYH,EAAE,KAAOD,EACrBK,EAAcJ,EAAE,QAAU,0EAAyE,GACnGK,EAAY,CAAC,CAACL,EAAE,YAAc,CAACA,EAAE,QAGjCM,EAAOD,EACT,IACAL,EAAE,aACA,GAAGA,EAAE,aAAa,QAAQ,OAAQ,EAAE,UACpC,IACAO,EAAWF,EAAY,kBAAkBG,EAAIR,EAAE,EAAE,8BAAgCA,EAAE,QAAU,mDAAqD,GAClJS,EAAOJ,EACT,0FACAL,EAAE,WACA,oBAAoBQ,EAAIR,EAAE,UAAU,UACpCA,EAAE,aACA,oFACA,GACR,MAAO,YAAYQ,EAAIF,CAAI,aAAaH,EAAY,aAAe,MAAMI;AAAA,wDACrBC,EAAIR,EAAE,IAAI,IAAII;AAAA,QAC9DJ,EAAE,aAAe,oBAAoBQ,EAAIR,EAAE,YAAY,UAAY;AAAA,QACnES;AAAA,SAEN,CAAC,EAAE,KAAK,EAAE,EAEJC,EAAe;AAAA;AAAA,yBAEEF,EAAIP,EAAQ,IAAI;AAAA;AAAA,4CAEGC;AAAA,UAgBpCS,EAAO,CACX,CAAE,GAAI,OAAQ,MAAO,OAAQ,KAAM,QAAS,SAAU,EAAM,EAC5D,CAAE,GAAI,SAAU,MAAO,SAAU,KAAM,IAAK,SAAU,EAAM,EAC5D,CAAE,GAAI,YAAa,MAAO,YAAa,KAAM,aAAc,SAAU,EAAM,EAC3E,CAAE,GAAI,QAAS,MAAO,iBAAkB,KAAM,wBAAyB,SAAU,EAAM,EACvF,CAAE,GAAI,QAAS,MAAO,SAAU,KAAM,gBAAiB,SAAU,EAAM,EACvE,CAAE,GAAI,SAAU,MAAO,SAAU,KAAM,gBAAiB,SAAU,EAAM,EACxE,CAAE,GAAI,OAAQ,MAAO,OAAQ,KAAM,cAAe,SAAU,EAAM,EAClE,CAAE,GAAI,OAAQ,MAAO,OAAQ,KAAM,cAAe,SAAU,EAAM,EAClE,CAAE,GAAI,QAAS,MAAO,WAAY,KAAM,SAAU,SAAU,EAAM,EAClE,CAAE,GAAI,WAAY,MAAO,WAAY,KAAM,YAAa,SAAU,EAAM,CAC1E,EAAE,IAAKC,GAAM,CACX,IAAMC,EAAMD,EAAE,KAAOf,EAAK,UAAY,2BAA6B,iBAC7DiB,EAAWF,EAAE,SAAW,kCAAoC,GAC5DG,EAAWH,EAAE,SAAW,yDAAsD,GACpF,MAAO,YAAYA,EAAE,gBAAgBC,KAAOC,KAAYF,EAAE,QAAQG,OACpE,CAAC,EAAE,KAAK,EAAE,EAqBV,MAnBe;AAAA;AAAA;AAAA;AAAA;AAAA,yCAKwBP,EAAIX,EAAK,QAAQ;AAAA;AAAA,mCAEvBc;AAAA;AAAA;AAAA,MAG7Bd,EAAK,aAAe;AAAA,MACpBa;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,WAOKb,EAAK,UAAY,6BAA6BA,EAAK,kBAAoB,IAElF,CAEA,SAASW,EAAIQ,EAAmB,CAC9B,OAAO,OAAOA,GAAK,EAAE,EAAE,QAAQ,WAAaC,IAAO,CAAE,IAAK,QAAS,IAAK,OAAQ,IAAK,OAAQ,IAAK,SAAU,IAAK,OAAQ,GAAEA,CAAC,CAAY,CAC1I,CCrRO,SAASC,EAAYC,EAAyB,CACnD,IAAMC,EAAM,GAAGC;AAAA,EAAkBC;AAAA,EAAsBC,IAAaJ,EAAK,IAAM;AAAA,EAAOA,EAAK,IAAM,KAE3FK,EAASL,EAAK,eACdA,EAAK,YAAc,SACnB,GACAM,EAAa,CACX,UAAWN,EAAK,UAChB,SAAUA,EAAK,SACf,UAAWA,EAAK,UAChB,MAAOA,EAAK,MACZ,cAAeA,EAAK,aACtB,CAAC,GAEDO,EAAOP,EAAK,OAASA,EAAK,KAAO,SAASA,EAAK,cAErD,MAAO;AAAA;AAAA;AAAA;AAAA;AAAA,SAKAQ,EAAYR,EAAK,KAAK;AAAA,EAC7BS;AAAA,EACAT,EAAK,YAAc;AAAA,SACZC;AAAA;AAAA;AAAA,EAGPI;AAAA,EACAE;AAAA,EACAG;AAAA,EACAV,EAAK,SAAW;AAAA;AAAA,QAGlB,CAEA,SAASQ,EAAYG,EAAmB,CACtC,OAAO,OAAOA,GAAK,EAAE,EAAE,QAAQ,SAAWC,IAAO,CAAE,IAAK,OAAQ,IAAK,OAAQ,IAAK,OAAQ,GAAEA,CAAC,CAAY,CAC3G,CCnGO,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","AX_COMPONENTS_CSS","TOPBAR_HEAD","TOPBAR_CSS","TOPBAR_SCRIPT","renderTopbar","opts","peers","currentId","p","current","meshMenu","isCurrent","primaryMark","proxyable","href","dataAttr","esc","hint","meshSelector","tabs","t","cls","extAttrs","extGlyph","s","c","renderShell","opts","css","AX_TOKENS_CSS","AX_COMPONENTS_CSS","TOPBAR_CSS","header","renderTopbar","main","escapeTitle","TOPBAR_HEAD","TOPBAR_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,13 +0,0 @@
1
- import{z as e}from"zod";import{readFileSync as x,existsSync as f}from"fs";import{resolve as p}from"path";import{z as t}from"zod";var S=t.enum(["mon","tue","wed","thu","fri","sat","sun"]),j=t.object({days:t.array(S).default(["mon","tue","wed","thu","fri"]),start:t.string().regex(/^\d{2}:\d{2}$/),end:t.string().regex(/^\d{2}:\d{2}$/),lunch:t.object({start:t.string().regex(/^\d{2}:\d{2}$/),end:t.string().regex(/^\d{2}:\d{2}$/)}).optional()}),w=t.object({title:t.string(),responsibilities:t.array(t.string()).default([]),sopPath:t.string().optional(),kpis:t.array(t.string()).default([])}),C=t.object({role:t.string(),reportsTo:t.string().optional(),schedule:j,utilizationTarget:t.number().min(0).max(1).default(.8)}),v=t.object({channel:t.string(),chatId:t.string(),accountId:t.string().optional()}),T=t.discriminatedUnion("type",[t.object({type:t.literal("backlog"),path:t.string().default(".agentx/backlog.md")}),t.object({type:t.literal("gitlab"),projects:t.array(t.string()).default([])}),t.object({type:t.literal("wiki"),path:t.string(),glob:t.string().default("**/*.md")})]),$=t.object({id:t.string(),pm:t.string().optional(),client:t.string().optional()}),D=t.object({channel:t.string().optional(),chatId:t.string().optional(),username:t.string().optional(),senderId:t.string().optional(),client:t.string(),project:t.string().optional(),displayName:t.string().optional()}),m=t.object({enabled:t.boolean().default(!1),timezone:t.string().default("UTC"),mainChannel:v,workSource:T,roles:t.record(t.string(),w).default({}),orgChart:t.record(t.string(),C).default({}),projects:t.array($).default([]),contactMap:t.array(D).default([]),workTickMinutes:t.number().int().min(1).max(60).default(15),idleQueueThreshold:t.number().int().min(0).default(0),standup:t.object({enabled:t.boolean().default(!0),plansDir:t.string().default(".agentx/plans"),maxAgentsPerDay:t.number().int().min(1).max(100).default(20),dryRun:t.boolean().default(!1)}).default({})});import{z as n}from"zod";var I=n.object({name:n.string(),color:n.string().regex(/^#[0-9a-f]{6}$/i).default("#6366f1"),description:n.string().optional()}),L=n.object({id:n.string().regex(/^[a-z0-9_-]+$/,"column id must be lowercase slug"),title:n.string(),kind:n.enum(["open-backlog","scoped-label","closed","label"]).default("label"),mapsToLabel:n.string().optional(),scopedLabel:n.string().optional(),scopedPrefix:n.string().default("Status"),accent:n.string().optional()}),z=n.object({enabled:n.boolean().default(!0),staleDoingMinutes:n.number().int().min(1).default(45),respectLunchBreak:n.boolean().default(!0),respectSchedule:n.boolean().default(!0),action:n.enum(["badge","notify"]).default("badge")}),B=n.discriminatedUnion("type",[n.object({type:n.literal("gitlab"),projects:n.array(n.string()).min(1)})]),P=[{id:"open",title:"Open",kind:"open-backlog",scopedPrefix:"Status",accent:"#6b7280"},{id:"todo",title:"To Do",kind:"scoped-label",scopedLabel:"Status::To Do",scopedPrefix:"Status",accent:"#fb7a35"},{id:"doing",title:"Doing",kind:"scoped-label",scopedLabel:"Status::Doing",scopedPrefix:"Status",accent:"#22c55e"},{id:"onhold",title:"On Hold",kind:"scoped-label",scopedLabel:"Status::On Hold",scopedPrefix:"Status",accent:"#f59e0b"},{id:"review",title:"Review",kind:"scoped-label",scopedLabel:"Status::Review",scopedPrefix:"Status",accent:"#3b82f6"},{id:"closed",title:"Closed",kind:"closed",scopedPrefix:"Status",accent:"#6b7280"}],A=n.object({id:n.string().regex(/^[a-z0-9_-]+$/,"board id must be lowercase slug"),name:n.string(),source:B,primaryToolLabel:n.string().optional(),labels:n.array(I).default([]),columns:n.array(L).min(1).default(P),timeRangeDays:n.number().int().min(1).max(365).default(30),closedWindowDays:n.number().int().min(1).max(365).default(30),reconciliation:z.default({})}),h=n.array(A).default([]),M=n.object({name:n.string(),url:n.string(),dashboardUrl:n.string().optional(),token:n.string().optional(),dashboardToken:n.string().optional()}),y=n.object({enabled:n.boolean().default(!1),port:n.number().int().default(4202),bind:n.string().default("127.0.0.1"),token:n.string().optional(),daemonUrl:n.string().default("http://localhost:18800"),daemons:n.array(M).default([]),draftAgent:n.string().optional()}).default({});function Y(a,i){let s=new Set(a.labels||[]);if(a.state==="closed"){let r=i.find(l=>l.kind==="closed");if(r)return r.id}for(let r of i)if(r.kind==="scoped-label"&&r.scopedLabel&&s.has(r.scopedLabel))return r.id;for(let r of i)if(r.kind==="label"&&r.mapsToLabel&&s.has(r.mapsToLabel))return r.id;let o=i.find(r=>r.kind==="open-backlog");if(o){let r=o.scopedPrefix+"::";return[...s].some(c=>c.startsWith(r))?null:o.id}return i[0]?.id??null}function ee(a,i){let s=[],o=[],r=!1,l=!1;return i.kind==="scoped-label"&&i.scopedLabel?s.push(i.scopedLabel):i.kind==="label"&&i.mapsToLabel?s.push(i.mapsToLabel):i.kind==="closed"&&(r=!0),a&&(a.kind==="scoped-label"&&a.scopedLabel?o.push(a.scopedLabel):a.kind==="label"&&a.mapsToLabel?o.push(a.mapsToLabel):a.kind==="closed"&&i.kind!=="closed"&&(l=!0)),{addLabels:s.length?s:void 0,removeLabels:o.length?o:void 0,closeIssue:r||void 0,reopen:l||void 0}}function R(a){let i=p(a,".env");if(!f(i))return;let s=x(i,"utf-8");for(let o of s.split(`
2
- `)){let r=o.trim();if(!r||r.startsWith("#"))continue;let l=r.indexOf("=");if(l===-1)continue;let c=r.slice(0,l).trim(),d=r.slice(l+1).trim();process.env[c]||(process.env[c]=d)}}var E=e.object({apiKey:e.string().optional(),defaultModel:e.string().optional(),baseUrl:e.string().optional()}),O=e.object({command:e.string(),args:e.array(e.string()).default([]),env:e.record(e.string(),e.string()).optional()}),U=e.object({name:e.string(),workspace:e.string(),tier:e.enum(["claude-code","sdk","orchestrator"]).default("claude-code"),provider:e.string().optional(),model:e.string().optional(),systemPrompt:e.string().optional(),mentions:e.array(e.string()).default([]),intents:e.array(e.string()).default([]),maxDelegationDepth:e.number().int().min(0).max(50).default(5),mcp:e.record(e.string(),O).optional(),contextStrategy:e.enum(["layered","planner"]).optional(),contextReferences:e.boolean().default(!1),maxConcurrent:e.number().default(1),maxExecutionMinutes:e.number().int().min(1).max(240).default(20),permissionMode:e.string().default("default"),persistentProcess:e.boolean().default(!1),queueMode:e.enum(["collect","followup","drop"]).default("collect"),heartbeat:e.object({enabled:e.boolean().default(!1),intervalMinutes:e.number().default(30),prompt:e.string().default("Check inbox, pending tasks, and system health. Report anything that needs attention."),channel:e.string().default("heartbeat")}).default({}),access:e.enum(["private","public"]).default("private")}),_=e.object({token:e.string(),agentBinding:e.string(),allowFrom:e.array(e.string()).optional(),pollInbound:e.boolean().default(!0)}),N=e.object({telegram:e.object({enabled:e.boolean().default(!1),accounts:e.record(e.string(),_).default({}),policy:e.object({dm:e.enum(["pair","block"]).default("pair"),group:e.enum(["mention-required","all"]).default("mention-required"),allowFrom:e.array(e.string()).optional()}).default({})}).default({}),whatsapp:e.object({enabled:e.boolean().default(!1),sessionDir:e.string().default(".agentx/whatsapp-sessions"),defaultAgent:e.string().optional(),allowFrom:e.array(e.string()).optional(),routes:e.array(e.object({contact:e.string().optional(),group:e.string().optional(),agent:e.string()})).default([]),ingest:e.object({enabled:e.boolean().default(!1),mode:e.enum(["metadata-only","messages"]).default("metadata-only"),allowContacts:e.array(e.string()).default([]),allowGroups:e.array(e.string()).default([]),denyContacts:e.array(e.string()).default([]),denyGroups:e.array(e.string()).default([]),messageCap:e.number().int().min(1).max(500).default(50),historyDays:e.number().int().min(1).max(365).default(30),contactRefreshDays:e.number().int().min(1).max(90).default(7),throttle:e.object({minMsBetweenCalls:e.number().int().min(100).default(1500),maxCallsPerMinute:e.number().int().min(1).default(20),maxChatsPerSweep:e.number().int().min(1).default(25)}).default({}),retentionDays:e.number().int().min(0).default(0)}).default({})}).default({}),discord:e.object({enabled:e.boolean().default(!1),token:e.string().optional(),agentBinding:e.string().optional()}).default({}),slack:e.object({enabled:e.boolean().default(!1),botToken:e.string().optional(),appToken:e.string().optional(),agentBinding:e.string().optional()}).default({}),gitlab:e.object({enabled:e.boolean().default(!1),webhookPort:e.number().default(18810),webhookSecret:e.string().optional(),host:e.string().default("https://gitlab.com"),token:e.string().optional(),routes:e.array(e.object({project:e.string(),agent:e.string()})).default([]),agentMappings:e.array(e.object({agentId:e.string(),gitlabUsernames:e.array(e.string()).default([]),keywords:e.array(e.string()).default([]),token:e.string().optional(),node:e.string().optional()})).default([])}).default({}),github:e.object({enabled:e.boolean().default(!1),token:e.string().optional(),tokenFile:e.string().optional(),appId:e.number().optional(),clientId:e.string().optional(),privateKeyFile:e.string().optional(),webhookSecret:e.string().optional(),routes:e.array(e.object({repo:e.string(),agent:e.string()})).default([]),agentMappings:e.array(e.object({agentId:e.string(),githubUsernames:e.array(e.string()).default([]),token:e.string().optional(),tokenFile:e.string().optional(),node:e.string().optional()})).default([])}).default({}),webrtc:e.object({enabled:e.boolean().default(!1),stunServers:e.array(e.string()).default(["stun:stun.l.google.com:19302"]),turnServers:e.array(e.object({urls:e.string(),username:e.string().optional(),credential:e.string().optional()})).default([]),allowedCallers:e.array(e.string()).default([]),ringNotify:e.array(e.object({channel:e.string(),chatId:e.string(),accountId:e.string().optional()})).default([]),callUrlBase:e.string().optional(),bot:e.object({enabled:e.boolean().default(!1),defaultAgentId:e.string().optional(),whisperBackend:e.enum(["auto","mlx","openai"]).default("auto"),whisperModel:e.string().optional(),whisperLanguage:e.string().default("auto"),mlxBinary:e.string().optional(),transcriptChannel:e.object({channel:e.string(),chatId:e.string(),accountId:e.string().optional()}).optional(),maxCallMinutes:e.number().int().min(1).max(240).default(30)}).default({})}).default({})}),F=e.object({enabled:e.boolean().default(!0),schedule:e.string(),timezone:e.string().default("UTC"),agent:e.string(),prompt:e.string(),timeout:e.number().default(600),model:e.string().optional(),maxOutputTokens:e.number().int().min(50).max(8e3).optional(),onError:e.union([e.enum(["log","notify","disable"]),e.array(e.enum(["log","notify","disable"]))]).default("log").transform(a=>Array.isArray(a)?a:[a]),notify:e.object({channel:e.string(),chatId:e.string(),accountId:e.string().optional()}).optional()}),W=e.object({name:e.string(),triggers:e.array(e.object({pattern:e.string(),channel:e.string().optional()})),allowedContacts:e.array(e.string()).optional(),agent:e.string(),prompt:e.string(),schedule:e.string().optional(),timezone:e.string().default("UTC"),notify:e.object({channel:e.string(),chatId:e.string(),accountId:e.string().optional()}).optional()}),q=e.object({url:e.string(),name:e.string(),token:e.string().optional()}),H=e.object({enabled:e.boolean().default(!1),peers:e.array(q).default([]),discovery:e.enum(["static","mdns"]).default("static"),healthCheck:e.object({interval:e.number().default(60),timeout:e.number().default(10)}).default({})}),J=e.object({enabled:e.boolean().default(!1),baseDir:e.string().default(".agentx/graph"),draftAgent:e.string().optional(),reviewAgent:e.string().optional(),autoApproveStructure:e.enum(["strict","extend-leaves","any"]).default("extend-leaves"),autoApproveConfidence:e.number().min(0).max(1).default(1),classifierModel:e.string().default("claude-haiku-4-5-20251001"),retrievalWeights:e.object({graph:e.number().min(0).default(.6),bm25:e.number().min(0).default(.4)}).default({})}).default({}),K=e.object({longTaskThreshold:e.number().default(30),destination:e.object({channel:e.string(),chatId:e.string(),accountId:e.string().optional()}).optional(),on:e.object({taskComplete:e.boolean().default(!0),taskError:e.boolean().default(!0),taskQueued:e.boolean().default(!1)}).default({})}).default({}),G=e.object({node:e.object({id:e.string(),name:e.string(),bind:e.string().default("127.0.0.1:18800"),defaultAgent:e.string().optional()}),providers:e.record(e.string(),E).default({}),agents:e.record(e.string(),U).default({}),channels:N.default({}),crons:e.record(e.string(),F).default({}),services:e.record(e.string(),W).default({}),notifications:K,mesh:H.default({}),business:m.optional(),boards:h,dashboard:y,graph:J,workflows:e.object({enabled:e.boolean().default(!1),dir:e.string().default(".agentx/workflows"),editor:e.enum(["disabled","readonly","edit"]).default("edit")}).default({}),webhooks:e.array(e.object({id:e.string().regex(/^[a-z0-9][a-z0-9_-]*$/,"webhook id must be lowercase slug"),source:e.enum(["gitlab","github","sentry","stripe","discord","slack","custom"]),agentId:e.string(),secretEnv:e.string().optional(),description:e.string().optional(),enabled:e.boolean().default(!0),node:e.string().optional(),triggers:e.record(e.string(),e.string()).optional(),defaultWorkflow:e.string().optional()})).default([]),session:e.object({staleMinutes:e.number().int().min(1).max(1440).default(45),maxTurnsPerSession:e.number().int().min(2).max(200).default(15),tierTwoThresholdTokens:e.number().int().min(5e4).max(2e5).default(195e3),contextStrategy:e.enum(["layered","planner"]).default("layered"),maxClaudeCodeDispatchesPerHour:e.number().int().min(1).max(1e4).default(80),maxClaudeCodeDispatchesPer5h:e.number().int().min(1).max(5e4).default(180)}).default({}),plugins:e.array(e.string()).default([])});function g(a){if(typeof a=="string")return a.replace(/\$\{(\w+)\}/g,(i,s)=>process.env[s]||"");if(Array.isArray(a))return a.map(g);if(a!==null&&typeof a=="object"){let i={};for(let[s,o]of Object.entries(a))i[s]=g(o);return i}return a}function Q(a,i){let s=[],o=new Set;for(let r of a){let l=r.path.join("."),c=/^channels\.telegram\.accounts\.([^.]+)\.token$/.exec(l);if(c&&r.message.toLowerCase().includes("required")){let d=c[1],u=`tg:${d}`;if(o.has(u))continue;o.add(u),s.push(`Hint: Telegram account "${d}" is enabled but has no bot token.
3
- Either:
4
- A) Open ${i} and set channels.telegram.accounts.${d}.token to your bot token (or "\${TG_${d.toUpperCase()}_BOT_TOKEN}" + the matching .env entry).
5
- B) If you don't need Telegram on this instance, remove the entire "telegram" block under "channels" (or set channels.telegram.enabled to false AND drop accounts.${d}).`)}}return s}function se(a){let i=a?[a]:[p(process.cwd(),"agentx.json"),p(process.cwd(),".agentx/config.json")];R(process.cwd());let s,o;for(let d of i)if(f(d)){s=x(d,"utf-8"),o=d;break}if(!s||!o)throw new Error(`No config found. Create agentx.json or .agentx/config.json
6
- Searched: ${i.join(", ")}`);let r;try{r=JSON.parse(s)}catch(d){throw new Error(`Invalid JSON in ${o}: ${d.message}`)}let l=g(r),c=G.safeParse(l);if(!c.success){let d=c.error.issues.map(b=>` ${b.path.join(".")}: ${b.message}`).join(`
7
- `),u=Q(c.error.issues,o),k=u.length?`
8
-
9
- ${u.join(`
10
-
11
- `)}`:"";throw new Error(`Config validation failed (${o}):
12
- ${d}${k}`)}return c.data}function le(a){let i=[];for(let[s,o]of Object.entries(a.agents)){if(!f(o.workspace)){i.push(`Agent "${s}": workspace not found at ${o.workspace}`);continue}if(o.tier==="claude-code"){let c=p(o.workspace,".claude");f(c)||i.push(`Agent "${s}": no .claude/ directory in workspace ${o.workspace}. Claude Code native features (hooks, MCP, skills) won't be available.`)}let r=o.provider||"claude",l=a.providers[r];o.tier!=="claude-code"&&(!l||!l.apiKey)&&i.push(`Agent "${s}": provider "${r}" has no API key configured. Set providers.${r}.apiKey in config or use tier "claude-code" for subscription.`)}for(let[s,o]of Object.entries(a.crons))a.agents[o.agent]||i.push(`Cron "${s}": references unknown agent "${o.agent}"`);if(a.channels.telegram.enabled)for(let[s,o]of Object.entries(a.channels.telegram.accounts))a.agents[o.agentBinding]||i.push(`Telegram account "${s}": references unknown agent "${o.agentBinding}"`);return i}export{Y as a,ee as b,G as c,g as d,se as e,le as f};
13
- //# sourceMappingURL=chunk-CO4YCIIA.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/daemon/config.ts","../src/business/config.ts","../src/boards/config.ts"],"sourcesContent":["import { z } from \"zod\"\nimport { readFileSync, existsSync } from \"fs\"\nimport { resolve } from \"path\"\nimport { businessConfigSchema } from \"@/business/config\"\nimport { boardsConfigSchema, dashboardConfigSchema } from \"@/boards/config\"\n\n/**\n * Load .env file into process.env (simple, no dependency).\n */\nfunction loadDotEnv(dir: string): void {\n const envPath = resolve(dir, \".env\")\n if (!existsSync(envPath)) return\n\n const content = readFileSync(envPath, \"utf-8\")\n for (const line of content.split(\"\\n\")) {\n const trimmed = line.trim()\n if (!trimmed || trimmed.startsWith(\"#\")) continue\n const eqIdx = trimmed.indexOf(\"=\")\n if (eqIdx === -1) continue\n const key = trimmed.slice(0, eqIdx).trim()\n const value = trimmed.slice(eqIdx + 1).trim()\n if (!process.env[key]) {\n process.env[key] = value\n }\n }\n}\n\n// --- Daemon configuration schema & loader ---\n\nconst providerConfigSchema = z.object({\n apiKey: z.string().optional(),\n defaultModel: z.string().optional(),\n baseUrl: z.string().optional(),\n})\n\nconst mcpServerSchema = z.object({\n command: z.string(),\n args: z.array(z.string()).default([]),\n env: z.record(z.string(), z.string()).optional(),\n})\n\nconst agentConfigSchema = z.object({\n name: z.string(),\n workspace: z.string(),\n tier: z.enum([\"claude-code\", \"sdk\", \"orchestrator\"]).default(\"claude-code\"),\n provider: z.string().optional(),\n model: z.string().optional(),\n systemPrompt: z.string().optional(),\n mentions: z.array(z.string()).default([]),\n /** Phase 5 — typed capabilities (drop-condition fallback). Free-form\n * list of intent strings this agent is allowed to handle. When set,\n * the org-chart `canHandle(agentId, project, intent)` check rejects\n * dispatches with intents not in this list. When empty/unset\n * (the default), the agent is permissive — handles any intent.\n * Intent matching is exact-string for now; a glob/prefix layer can\n * come later if it produces real rejections.\n * Example: [\"issue.opened\", \"issue.commented\", \"merge_request.opened\"]\n * Example: [\"cron.fired\", \"message.received\"] */\n intents: z.array(z.string()).default([]),\n /** Phase 8 — capability-bounded security. Max distinct upstream\n * agents in the delegation chain on the same (project, subject)\n * before a dispatch to THIS agent is refused. The ledger itself\n * provides the chain — `decideAndCommit` walks recent decisions on\n * the subject and counts distinct agents. Default 5; set to 0 to\n * disable for an agent that's always called as the bottom of a\n * chain. The check prevents cascade loops where A → B → A → B → ...\n * blows past sane chain depth. */\n maxDelegationDepth: z.number().int().min(0).max(50).default(5),\n /** MCP servers this agent's Claude Code session should load. Synced\n * to <workspace>/.mcp.json at daemon boot via agent-mcp.ts. Operator\n * edits to .mcp.json are respected (see SyncResult.skipped-operator-owned). */\n mcp: z.record(z.string(), mcpServerSchema).optional(),\n /** Per-agent override for the global `session.contextStrategy`. Lets\n * one agent run `planner` (smaller upfront context, more tool-driven\n * exploration) while siblings stay on `layered`. Used for agents that\n * consistently bloat their cache via large workspace reads. */\n contextStrategy: z.enum([\"layered\", \"planner\"]).optional(),\n /** When true, the registry resolves references-recipes for this agent's\n * workspace and renders a deterministic [Verified References] block at\n * priority 4.7. Off by default — flip on per agent (pm-ksi, devops-agent,\n * etc.) once a `references/` registry exists in the agent's workspace\n * or repo root. See src/agents/references/. */\n contextReferences: z.boolean().default(false),\n maxConcurrent: z.number().default(1),\n /** Hard wall-clock cap on a single Claude Code invocation. Exceeding the\n * cap sends SIGTERM (exit 143). Default 20 min — bump for devops/coder\n * agents that do long investigations or multi-file refactors. */\n maxExecutionMinutes: z.number().int().min(1).max(240).default(20),\n permissionMode: z.string().default(\"default\"),\n /** When true, this agent's claude-code dispatches reuse a long-lived\n * subprocess per (channel, chatId) instead of spawning a fresh\n * `claude -p` per turn. Driven over stdin via stream-json input,\n * which keeps the prompt cache warm across turns within a chat\n * (turn 1 cache_create=12897 → turn 2 cache_create=20 + cache_read\n * =24575 in the 2026-05-03 spike — 3-5× latency win on chat-shaped\n * workloads). When the registry can't allocate a slot (global or\n * per-agent cap exceeded), the dispatch falls back to spawn-per-task\n * silently with a warning log. Other tiers (sdk, orchestrator)\n * ignore this flag. Default false until the persistent path\n * finishes its soak; flip per-agent first, then per-fleet.\n * See docs/architecture/persistent-claude-process.md for the design. */\n persistentProcess: z.boolean().default(false),\n queueMode: z.enum([\"collect\", \"followup\", \"drop\"]).default(\"collect\"),\n heartbeat: z.object({\n enabled: z.boolean().default(false),\n intervalMinutes: z.number().default(30),\n prompt: z.string().default(\"Check inbox, pending tasks, and system health. Report anything that needs attention.\"),\n channel: z.string().default(\"heartbeat\"),\n }).default({}),\n /** Reachability via the external public API.\n * - \"private\" (default): this agent is daemon-internal + channel-bound only.\n * - \"public\": external apps can POST /api/public/agents/<id>/messages\n * with a scoped token (agent:<id> or agent:*). */\n access: z.enum([\"private\", \"public\"]).default(\"private\"),\n})\n\nconst telegramAccountSchema = z.object({\n token: z.string(),\n agentBinding: z.string(),\n /** Per-account sender allowlist. When set, OVERRIDES the global\n * `policy.allowFrom`. Entries accept:\n * - numeric user id (\"1816212449\") — matches sender (from.id)\n * - numeric chat id (\"-1003861455814\") — matches chat (chat.id)\n * - \"@username\" — matches sender username\n * A message is dispatched iff at least one entry matches. */\n allowFrom: z.array(z.string()).optional(),\n /** When false, the daemon keeps the bot token registered so outbound\n * `send()` / ring notifications work, but does NOT long-poll for inbound\n * updates. Use this on nodes where the bound agent lives on a different\n * daemon — otherwise two daemons race on the same `getUpdates` cursor and\n * Telegram responds 409 Conflict on every poll.\n * Defaults to true (poll) so existing single-node setups are unchanged. */\n pollInbound: z.boolean().default(true),\n})\n\nconst channelsConfigSchema = z.object({\n telegram: z.object({\n enabled: z.boolean().default(false),\n accounts: z.record(z.string(), telegramAccountSchema).default({}),\n policy: z.object({\n dm: z.enum([\"pair\", \"block\"]).default(\"pair\"),\n group: z.enum([\"mention-required\", \"all\"]).default(\"mention-required\"),\n /** Global sender allowlist applied to every account that doesn't set\n * its own `allowFrom`. When neither is configured, every incoming\n * message is dropped — closed by default. Same entry forms as the\n * per-account list (user id, chat id, @username). */\n allowFrom: z.array(z.string()).optional(),\n }).default({}),\n }).default({}),\n whatsapp: z.object({\n enabled: z.boolean().default(false),\n sessionDir: z.string().default(\".agentx/whatsapp-sessions\"),\n defaultAgent: z.string().optional(),\n allowFrom: z.array(z.string()).optional(),\n routes: z.array(z.object({\n contact: z.string().optional(),\n group: z.string().optional(),\n agent: z.string(),\n })).default([]),\n /** Data-source ingestion — turns WhatsApp from a messaging-only channel\n * into a source that seeds the wiki with contact/group metadata (and\n * optionally bounded message windows) per an explicit allowlist.\n * Default-deny: `enabled: false` and empty allowlists mean nothing is\n * ingested. See docs/reference/whatsapp-ingest.md for the full story. */\n ingest: z.object({\n enabled: z.boolean().default(false),\n /** `metadata-only` pulls contact/group info; `messages` additionally\n * pulls the last `messageCap` messages per allowlisted chat. */\n mode: z.enum([\"metadata-only\", \"messages\"]).default(\"metadata-only\"),\n /** Phone numbers or JIDs. Substring match, same semantics as allowFrom. */\n allowContacts: z.array(z.string()).default([]),\n allowGroups: z.array(z.string()).default([]),\n denyContacts: z.array(z.string()).default([]),\n denyGroups: z.array(z.string()).default([]),\n /** Per-chat cap when mode = \"messages\". Keeps the raw-entry size bounded. */\n messageCap: z.number().int().min(1).max(500).default(50),\n /** Max age (days) of messages to consider for the bounded window. */\n historyDays: z.number().int().min(1).max(365).default(30),\n /** Skip re-writing a contact entry unless this many days have passed\n * OR the profile hash differs. Prevents churn on unchanged profiles. */\n contactRefreshDays: z.number().int().min(1).max(90).default(7),\n /** Safety rails on live Baileys reads — personal-account accounts\n * can get throttled/banned under burst reads. */\n throttle: z.object({\n minMsBetweenCalls: z.number().int().min(100).default(1500),\n maxCallsPerMinute: z.number().int().min(1).default(20),\n maxChatsPerSweep: z.number().int().min(1).default(25),\n }).default({}),\n /** Purge absorbed raw entries older than this many days. `0` = never. */\n retentionDays: z.number().int().min(0).default(0),\n }).default({}),\n }).default({}),\n discord: z.object({\n enabled: z.boolean().default(false),\n token: z.string().optional(),\n agentBinding: z.string().optional(),\n }).default({}),\n slack: z.object({\n enabled: z.boolean().default(false),\n /** xoxb-... — bot user OAuth token from the Slack app's \"OAuth & Permissions\" tab. */\n botToken: z.string().optional(),\n /** xapp-... — app-level token with connections:write scope (for Socket Mode). */\n appToken: z.string().optional(),\n agentBinding: z.string().optional(),\n }).default({}),\n gitlab: z.object({\n enabled: z.boolean().default(false),\n webhookPort: z.number().default(18810),\n webhookSecret: z.string().optional(),\n host: z.string().default(\"https://gitlab.com\"),\n token: z.string().optional(),\n routes: z.array(z.object({\n project: z.string(),\n agent: z.string(),\n })).default([]),\n agentMappings: z.array(z.object({\n agentId: z.string(),\n gitlabUsernames: z.array(z.string()).default([]),\n keywords: z.array(z.string()).default([]),\n token: z.string().optional(),\n /** If set, the agent lives on a remote mesh peer (node id). Forces the\n * username→agent map to resolve to this mapping even when a local\n * agent's token resolves to the same GitLab user — prevents collisions\n * like two agents both claiming @devops-noqta. */\n node: z.string().optional(),\n })).default([]),\n }).default({}),\n github: z.object({\n enabled: z.boolean().default(false),\n /** GitHub PAT or env var (${GITHUB_TOKEN}) for posting comments back. */\n token: z.string().optional(),\n /** Path to file containing the token (first line read at startup). */\n tokenFile: z.string().optional(),\n /** GitHub App ID (legacy issuer). */\n appId: z.number().optional(),\n /** GitHub App Client ID (preferred JWT issuer per GitHub's updated docs). */\n clientId: z.string().optional(),\n /** Path to the GitHub App private key PEM file. */\n privateKeyFile: z.string().optional(),\n /** Webhook secret for validating X-Hub-Signature-256. */\n webhookSecret: z.string().optional(),\n /** Repo → agent routing. Use \"owner/repo\" or \"*\" for default. */\n routes: z.array(z.object({\n repo: z.string(),\n agent: z.string(),\n })).default([]),\n /** Per-agent identity mappings (GitHub usernames, tokens, mesh nodes). */\n agentMappings: z.array(z.object({\n agentId: z.string(),\n githubUsernames: z.array(z.string()).default([]),\n token: z.string().optional(),\n tokenFile: z.string().optional(),\n node: z.string().optional(),\n })).default([]),\n }).default({}),\n webrtc: z.object({\n enabled: z.boolean().default(false),\n /** ICE STUN servers for NAT discovery. Default is Google's public STUN. */\n stunServers: z.array(z.string()).default([\"stun:stun.l.google.com:19302\"]),\n /** Optional TURN relays (required when both peers are behind symmetric NAT). */\n turnServers: z.array(z.object({\n urls: z.string(),\n username: z.string().optional(),\n credential: z.string().optional(),\n })).default([]),\n /** Peer names permitted to *initiate* a call into this daemon. Empty = allow all mesh peers. */\n allowedCallers: z.array(z.string()).default([]),\n /** Where to send \"someone is calling\" notifications when an inbound ring\n * arrives. Each entry is delivered via the matching channel adapter —\n * same plumbing as any other outbound message. Empty disables notifications. */\n ringNotify: z.array(z.object({\n channel: z.string(),\n chatId: z.string(),\n accountId: z.string().optional(),\n })).default([]),\n /** Base URL used when building the tap-to-join link in ring notifications.\n * Defaults to `http://<node.bind>` — override when the daemon's public\n * hostname differs (e.g. HTTPS-terminated tunnel, Tailscale MagicDNS). */\n callUrlBase: z.string().optional(),\n /** AI participant (\"bot\") joins calls when a browser opens with `?bot=<id>`.\n * v1 is transcribe-only — bot consumes remote audio, transcribes via\n * Whisper, posts chunks to the configured channel. No TTS-back. */\n bot: z.object({\n enabled: z.boolean().default(false),\n /** Default agent id used to attribute the transcript when the URL\n * doesn't override via `?bot=<other-agent>`. */\n defaultAgentId: z.string().optional(),\n /** \"auto\" tries mlx-whisper, falls back to OpenAI; explicit forces. */\n whisperBackend: z.enum([\"auto\", \"mlx\", \"openai\"]).default(\"auto\"),\n whisperModel: z.string().optional(),\n whisperLanguage: z.string().default(\"auto\"),\n /** Absolute path to mlx_whisper if not on the daemon's PATH (common\n * on macOS launchd, where ~/.pyenv/shims is missing from the env). */\n mlxBinary: z.string().optional(),\n /** Where to send each transcribed chunk. Same shape as ringNotify[]. */\n transcriptChannel: z.object({\n channel: z.string(),\n chatId: z.string(),\n accountId: z.string().optional(),\n }).optional(),\n /** Hard cap so a forgotten bot doesn't run forever. */\n maxCallMinutes: z.number().int().min(1).max(240).default(30),\n }).default({}),\n }).default({}),\n})\n\nconst cronJobSchema = z.object({\n enabled: z.boolean().default(true),\n schedule: z.string(),\n timezone: z.string().default(\"UTC\"),\n agent: z.string(),\n prompt: z.string(),\n timeout: z.number().default(600),\n model: z.string().optional(),\n /** Soft cap on output length. Claude Code CLI has no hard flag for this,\n * but appending an instruction to the prompt is reliably honored. Use\n * 1500 for briefs, 500 for status pings, 300 for pure classifiers. */\n maxOutputTokens: z.number().int().min(50).max(8000).optional(),\n onError: z.union([\n z.enum([\"log\", \"notify\", \"disable\"]),\n z.array(z.enum([\"log\", \"notify\", \"disable\"])),\n ]).default(\"log\").transform(v => Array.isArray(v) ? v : [v]),\n notify: z.object({\n channel: z.string(),\n chatId: z.string(),\n accountId: z.string().optional(),\n }).optional(),\n})\n\nconst serviceSchema = z.object({\n name: z.string(),\n triggers: z.array(z.object({\n pattern: z.string(),\n channel: z.string().optional(),\n })),\n allowedContacts: z.array(z.string()).optional(),\n agent: z.string(),\n prompt: z.string(),\n schedule: z.string().optional(),\n timezone: z.string().default(\"UTC\"),\n notify: z.object({\n channel: z.string(),\n chatId: z.string(),\n accountId: z.string().optional(),\n }).optional(),\n})\n\nconst meshPeerSchema = z.object({\n url: z.string(),\n name: z.string(),\n token: z.string().optional(),\n})\n\nconst meshConfigSchema = z.object({\n enabled: z.boolean().default(false),\n peers: z.array(meshPeerSchema).default([]),\n discovery: z.enum([\"static\", \"mdns\"]).default(\"static\"),\n healthCheck: z.object({\n interval: z.number().default(60),\n timeout: z.number().default(10),\n }).default({}),\n})\n\n/** Intent Knowledge Graph — fixed-axis, LLM-proposed taxonomy used by the\n * Intent layer in context.ts and (eventually) wiki retrieval. Off by default\n * so existing installs see no change. */\nconst graphConfigSchema = z.object({\n enabled: z.boolean().default(false),\n /** Where the schema/nodes/classifications live. Relative to cwd. */\n baseDir: z.string().default(\".agentx/graph\"),\n /** Which agent makes LLM classification proposals. Falls back to\n * `dashboard.draftAgent` at call sites if unset. */\n draftAgent: z.string().optional(),\n /** Agent used by `agentx graph review` to triage pending classifications.\n * Should be an agent with the wiki skill so it can call `wiki query` for\n * context before deciding approve/reject. Falls back to draftAgent. */\n reviewAgent: z.string().optional(),\n /** Structural auto-approval policy. Evaluated against each classification\n * independently of `autoApproveConfidence`; either triggers approval.\n * - \"strict\" : never approve via structure — every classification\n * waits for human review\n * - \"extend-leaves\" : auto-approve when the proposed path (a) reuses only\n * existing nodes, or (b) adds exactly one NEW node at\n * the deepest level. Structural changes (new mid-path\n * node, new root) still queue for review. Default.\n * - \"any\" : auto-approve every classification regardless of\n * structural change\n */\n autoApproveStructure: z.enum([\"strict\", \"extend-leaves\", \"any\"]).default(\"extend-leaves\"),\n /** Minimum classifier confidence (0..1) to auto-approve. OR'd with the\n * structural policy — either hitting the threshold lets the classification\n * bypass the pending queue. 1.0 (default) disables this knob, so approval\n * is driven by `autoApproveStructure` alone. Lower it to e.g. 0.7 if you\n * also want high-confidence structural changes auto-approved. */\n autoApproveConfidence: z.number().min(0).max(1).default(1.0),\n /** Anthropic model id used for the direct classifier call (Phase 2 of\n * classifier-retire). Default haiku — classification is metadata, not\n * work, so we use the cheapest fast model. Override only if Haiku is\n * rate-limited or you want to A/B test. */\n classifierModel: z.string().default(\"claude-haiku-4-5-20251001\"),\n /** Weights for the wiki hybrid retrieval score. Path-ancestry match vs\n * BM25 over article text. Sum need not be 1. */\n retrievalWeights: z.object({\n graph: z.number().min(0).default(0.6),\n bm25: z.number().min(0).default(0.4),\n }).default({}),\n}).default({})\n\nconst notificationsSchema = z.object({\n /** Send notification when task takes longer than this (seconds). 0 = disabled. */\n longTaskThreshold: z.number().default(30),\n /** Where to send notifications */\n destination: z.object({\n channel: z.string(),\n chatId: z.string(),\n accountId: z.string().optional(),\n }).optional(),\n /** Notify on these events */\n on: z.object({\n taskComplete: z.boolean().default(true),\n taskError: z.boolean().default(true),\n taskQueued: z.boolean().default(false),\n }).default({}),\n}).default({})\n\nexport const daemonConfigSchema = z.object({\n node: z.object({\n id: z.string(),\n name: z.string(),\n bind: z.string().default(\"127.0.0.1:18800\"),\n /** Default agent for voice/API calls that don't specify one (e.g. Siri) */\n defaultAgent: z.string().optional(),\n }),\n providers: z.record(z.string(), providerConfigSchema).default({}),\n agents: z.record(z.string(), agentConfigSchema).default({}),\n channels: channelsConfigSchema.default({}),\n crons: z.record(z.string(), cronJobSchema).default({}),\n services: z.record(z.string(), serviceSchema).default({}),\n notifications: notificationsSchema,\n mesh: meshConfigSchema.default({}),\n business: businessConfigSchema.optional(),\n boards: boardsConfigSchema,\n dashboard: dashboardConfigSchema,\n graph: graphConfigSchema,\n /** Workflow engine — declarative state machines that bind channel events\n * to agents. Off by default; existing installs see no change until\n * flipped. Definitions live under `dir` (default .agentx/workflows/). */\n workflows: z.object({\n enabled: z.boolean().default(false),\n dir: z.string().default(\".agentx/workflows\"),\n /** Controls whether the dashboard exposes the visual editor. \"readonly\"\n * serves the list + run timelines but strips write controls from the\n * page. \"disabled\" hides the tab entirely. */\n editor: z.enum([\"disabled\", \"readonly\", \"edit\"]).default(\"edit\"),\n }).default({}),\n /** Registered inbound webhooks — an inventory the dashboard manages. Each\n * entry binds an (agent, source) pair to an optional signing secret. The\n * actual inbound URL is always POST /webhook/<agentId>/<source>. */\n webhooks: z.array(z.object({\n id: z.string().regex(/^[a-z0-9][a-z0-9_-]*$/, \"webhook id must be lowercase slug\"),\n source: z.enum([\"gitlab\", \"github\", \"sentry\", \"stripe\", \"discord\", \"slack\", \"custom\"]),\n agentId: z.string(),\n secretEnv: z.string().optional(),\n description: z.string().optional(),\n enabled: z.boolean().default(true),\n /** Route to a mesh peer instead of local execution. */\n node: z.string().optional(),\n /** Per-event-type workflow routing. Keys are platform event-type\n * strings — the format the platform's HTTP header uses, optionally\n * joined with the action. Examples:\n * GitHub: \"issues.opened\", \"pull_request.synchronize\",\n * \"push\" (action-less events use the bare type)\n * GitLab: \"Note Hook\", \"Merge Request Hook\"\n * Values are workflow ids registered via `agentx workflow create`.\n * When the inbound event-type matches a key, the named workflow is\n * dispatched; if no key matches, the webhook falls through to\n * `defaultWorkflow` (or the agent itself). Closes the recurring\n * GitHub problem of multiple event types collapsing to a single\n * workflow. */\n triggers: z.record(z.string(), z.string()).optional(),\n /** Workflow id used when no `triggers` entry matches the event-type.\n * Backward-compatible: pre-existing webhooks without `triggers`\n * always hit this path. */\n defaultWorkflow: z.string().optional(),\n })).default([]),\n /** Session cache-reuse policy. Controls when we drop a Claude `--resume`\n * session and rebuild the prompt from scratch.\n * - `staleMinutes`: idle timeout before rotation. Longer is cheaper at\n * Opus rates but lets a session accumulate tool-result bloat across an\n * all-day chat.\n * - `maxTurnsPerSession`: hard cap on turns per Claude session. Prevents\n * unbounded `--resume` growth. When hit, next turn starts fresh with\n * the compacted summary + recent-messages context.\n * - `tierTwoThresholdTokens`: if the prior turn's (input + cacheRead +\n * cacheCreate) exceeded this, rotate proactively. Claude bills tier-2\n * at 1.5× above 200K total input, so rotating before that threshold\n * undercuts the multiplier. */\n session: z.object({\n staleMinutes: z.number().int().min(1).max(1440).default(45),\n maxTurnsPerSession: z.number().int().min(2).max(200).default(15),\n tierTwoThresholdTokens: z.number().int().min(50_000).max(200_000).default(195_000),\n /** Context assembly strategy:\n * - \"layered\" (default): the classic stacked layers — session history,\n * memory, cross-chat, wiki hint all appended every turn.\n * - \"planner\": a Haiku pre-call decides what to retrieve before the\n * main agent runs. Always-on core is kept (channel/scope/landscape/\n * identity/intent + last 3 turns verbatim); history blob, wiki hint,\n * and cross-chat are replaced by planner-selected bundles. Per-task\n * overrides via AgentTask.contextStrategy for A/B benchmarking. */\n contextStrategy: z.enum([\"layered\", \"planner\"]).default(\"layered\"),\n /** Soft dispatch budget for claude-code-tier agents (shared OAuth pools\n * one counter across the fleet). Warms at 80% of the cap; short-circuits\n * cold dispatches (no warm Claude session) when exceeded. Warm sessions\n * are always allowed through. Defaults sized for Max 5×:\n * - maxClaudeCodeDispatchesPerHour: 80 (headroom under typical hourly cap)\n * - maxClaudeCodeDispatchesPer5h: 180 (headroom under ~225/5h tier)\n * Raise for Max 20×, lower if your workload stays under the cap naturally. */\n maxClaudeCodeDispatchesPerHour: z.number().int().min(1).max(10_000).default(80),\n maxClaudeCodeDispatchesPer5h: z.number().int().min(1).max(50_000).default(180),\n }).default({}),\n /** Move B — JS/TS plugins. Each entry is an installed npm package name\n * (e.g. `agentx-plugin-mattermost` or `@noqta/plugin-mattermost`); the\n * loader does a dynamic `import(name)` at boot, validates the manifest,\n * and calls plugin.setup(ctx). Plugins can register channel adapters\n * via ctx.addChannel() and subscribe to bus events via ctx.on(). Empty\n * default — installs that don't list plugins are unaffected. Plugin\n * authoring guide: docs/architecture/plugins.md. */\n plugins: z.array(z.string()).default([]),\n})\n\nexport type DaemonConfig = z.infer<typeof daemonConfigSchema>\nexport type AgentDef = z.infer<typeof agentConfigSchema>\nexport type CronJobDef = z.infer<typeof cronJobSchema>\nexport type MeshPeer = z.infer<typeof meshPeerSchema>\n\n/**\n * Expand environment variables in strings: ${VAR_NAME} -> process.env.VAR_NAME\n */\nexport function expandEnvVars(obj: unknown): unknown {\n if (typeof obj === \"string\") {\n return obj.replace(/\\$\\{(\\w+)\\}/g, (_match, name) => process.env[name] || \"\")\n }\n if (Array.isArray(obj)) {\n return obj.map(expandEnvVars)\n }\n if (obj !== null && typeof obj === \"object\") {\n const result: Record<string, unknown> = {}\n for (const [key, value] of Object.entries(obj as Record<string, unknown>)) {\n result[key] = expandEnvVars(value)\n }\n return result\n }\n return obj\n}\n\n/**\n * Translate raw Zod issues into actionable, copy-pasteable fixes for the\n * patterns we see in real first-run failures (missing tokens, missing\n * required env vars, etc.). Falls through silently for unknown patterns —\n * the raw `issues` block is always shown above this hint block.\n */\nfunction friendlyConfigHints(issues: ReadonlyArray<z.ZodIssue>, configPath: string): string[] {\n const hints: string[] = []\n const seen = new Set<string>()\n for (const issue of issues) {\n const path = issue.path.join(\".\")\n // Telegram account block exists but token is empty/missing — by far the\n // most common first-run trip. Tell the operator exactly which two\n // recoveries are valid, with the literal config path to edit.\n const tgTokenMatch = /^channels\\.telegram\\.accounts\\.([^.]+)\\.token$/.exec(path)\n if (tgTokenMatch && issue.message.toLowerCase().includes(\"required\")) {\n const account = tgTokenMatch[1]\n const key = `tg:${account}`\n if (seen.has(key)) continue\n seen.add(key)\n hints.push(\n `Hint: Telegram account \"${account}\" is enabled but has no bot token.\\n` +\n ` Either:\\n` +\n ` A) Open ${configPath} and set channels.telegram.accounts.${account}.token to your bot token (or \"\\${TG_${account.toUpperCase()}_BOT_TOKEN}\" + the matching .env entry).\\n` +\n ` B) If you don't need Telegram on this instance, remove the entire \"telegram\" block under \"channels\" (or set channels.telegram.enabled to false AND drop accounts.${account}).`\n )\n }\n }\n return hints\n}\n\n/**\n * Load daemon config from agentx.json, with env var expansion and validation.\n */\nexport function loadDaemonConfig(configPath?: string): DaemonConfig {\n const paths = configPath\n ? [configPath]\n : [\n resolve(process.cwd(), \"agentx.json\"),\n resolve(process.cwd(), \".agentx/config.json\"),\n ]\n\n // Load .env from same directory as config search\n loadDotEnv(process.cwd())\n\n let raw: string | undefined\n let foundPath: string | undefined\n\n for (const p of paths) {\n if (existsSync(p)) {\n raw = readFileSync(p, \"utf-8\")\n foundPath = p\n break\n }\n }\n\n if (!raw || !foundPath) {\n throw new Error(\n `No config found. Create agentx.json or .agentx/config.json\\n` +\n `Searched: ${paths.join(\", \")}`\n )\n }\n\n let parsed: unknown\n try {\n parsed = JSON.parse(raw)\n } catch (e: any) {\n throw new Error(`Invalid JSON in ${foundPath}: ${e.message}`)\n }\n\n // Expand environment variables\n const expanded = expandEnvVars(parsed)\n\n // Validate\n const result = daemonConfigSchema.safeParse(expanded)\n if (!result.success) {\n const issues = result.error.issues\n .map((i) => ` ${i.path.join(\".\")}: ${i.message}`)\n .join(\"\\n\")\n const hints = friendlyConfigHints(result.error.issues, foundPath)\n const hintBlock = hints.length ? `\\n\\n${hints.join(\"\\n\\n\")}` : \"\"\n throw new Error(`Config validation failed (${foundPath}):\\n${issues}${hintBlock}`)\n }\n\n return result.data\n}\n\n/**\n * Validate agent workspace directories exist and have .claude/ setup.\n */\nexport function validateWorkspaces(config: DaemonConfig): string[] {\n const warnings: string[] = []\n\n for (const [id, agent] of Object.entries(config.agents)) {\n if (!existsSync(agent.workspace)) {\n warnings.push(`Agent \"${id}\": workspace not found at ${agent.workspace}`)\n continue\n }\n\n if (agent.tier === \"claude-code\") {\n const claudeDir = resolve(agent.workspace, \".claude\")\n if (!existsSync(claudeDir)) {\n warnings.push(\n `Agent \"${id}\": no .claude/ directory in workspace ${agent.workspace}. ` +\n `Claude Code native features (hooks, MCP, skills) won't be available.`\n )\n }\n }\n\n // Check provider availability\n const providerName = agent.provider || \"claude\"\n const providerConfig = config.providers[providerName]\n if (agent.tier !== \"claude-code\" && (!providerConfig || !providerConfig.apiKey)) {\n warnings.push(\n `Agent \"${id}\": provider \"${providerName}\" has no API key configured. ` +\n `Set providers.${providerName}.apiKey in config or use tier \"claude-code\" for subscription.`\n )\n }\n }\n\n // Validate cron agent bindings\n for (const [id, cron] of Object.entries(config.crons)) {\n if (!config.agents[cron.agent]) {\n warnings.push(`Cron \"${id}\": references unknown agent \"${cron.agent}\"`)\n }\n }\n\n // Validate channel agent bindings\n if (config.channels.telegram.enabled) {\n for (const [name, account] of Object.entries(config.channels.telegram.accounts)) {\n if (!config.agents[account.agentBinding]) {\n warnings.push(\n `Telegram account \"${name}\": references unknown agent \"${account.agentBinding}\"`\n )\n }\n }\n }\n\n return warnings\n}\n","import { z } from \"zod\"\n\n// --- Business layer configuration schema ---\n// Optional top-level `business` key in agentx.json.\n// When enabled, AgentX simulates a team with roles, schedules, and a work pool.\n\nconst dayEnum = z.enum([\"mon\", \"tue\", \"wed\", \"thu\", \"fri\", \"sat\", \"sun\"])\n\nconst scheduleSchema = z.object({\n days: z.array(dayEnum).default([\"mon\", \"tue\", \"wed\", \"thu\", \"fri\"]),\n start: z.string().regex(/^\\d{2}:\\d{2}$/), // \"09:00\"\n end: z.string().regex(/^\\d{2}:\\d{2}$/), // \"17:00\"\n lunch: z.object({\n start: z.string().regex(/^\\d{2}:\\d{2}$/),\n end: z.string().regex(/^\\d{2}:\\d{2}$/),\n }).optional(),\n})\n\nconst roleSchema = z.object({\n title: z.string(),\n responsibilities: z.array(z.string()).default([]),\n sopPath: z.string().optional(),\n kpis: z.array(z.string()).default([]),\n})\n\nconst orgEntrySchema = z.object({\n role: z.string(),\n reportsTo: z.string().optional(),\n schedule: scheduleSchema,\n utilizationTarget: z.number().min(0).max(1).default(0.8),\n})\n\nconst channelRefSchema = z.object({\n channel: z.string(),\n chatId: z.string(),\n accountId: z.string().optional(),\n})\n\nconst workSourceSchema = z.discriminatedUnion(\"type\", [\n z.object({\n type: z.literal(\"backlog\"),\n path: z.string().default(\".agentx/backlog.md\"),\n }),\n z.object({\n type: z.literal(\"gitlab\"),\n projects: z.array(z.string()).default([]), // empty = all configured gitlab projects\n }),\n z.object({\n type: z.literal(\"wiki\"),\n path: z.string(),\n glob: z.string().default(\"**/*.md\"),\n }),\n])\n\n/** Per-project metadata. Phase 3 of the architectural rescue —\n * `pm` is the agentId responsible for gating dispatches on this\n * project. Optional; when null, dispatches don't go through a PM\n * gate. The wiring lives in `decideAndCommit` (gated by a flag,\n * separate commit). */\nconst projectSchema = z.object({\n /** Project identifier. For GitLab/GitHub: \"owner/repo\". For\n * noqta-internal projects: a stable string. */\n id: z.string(),\n /** AgentId of the PM for this project. Looked up by\n * `Organization.pmFor(project)` at dispatch time. */\n pm: z.string().optional(),\n /** Optional client this project belongs to. Used by the activity\n * graph to attribute non-projected events (Telegram chats, free-\n * text WhatsApp, cron jobs) to the right client. Falls back to\n * the leading segment of `id` (split on \"/\") when unset. */\n client: z.string().optional(),\n})\n\n/** Maps a chat or sender to a client/project so free-text channels\n * (telegram/whatsapp/slack/discord/email) get attributed correctly\n * in the activity graph. Without this, every chat lands in the\n * fallback \"internal\" bucket — the operator can't tell which client\n * the agent was helping.\n *\n * Match priority: chatId > username > channel-default. First match\n * wins. All fields except `client` are optional; `project` falls\n * back to `${client}/_chat` when unset. */\nconst contactMapSchema = z.object({\n /** Channel kind, e.g. \"telegram\", \"whatsapp\", \"slack\", \"discord\". */\n channel: z.string().optional(),\n /** Native chat id (\"-100...\" for tg groups, JID for WhatsApp). */\n chatId: z.string().optional(),\n /** Sender username/handle as exposed by the channel adapter. */\n username: z.string().optional(),\n /** Numeric sender id when username is unstable. */\n senderId: z.string().optional(),\n /** Client this contact belongs to. Required. */\n client: z.string(),\n /** Project this contact's traffic should attribute to. Defaults to\n * `${client}/_chat`. */\n project: z.string().optional(),\n /** Display name override for the initiator pill. */\n displayName: z.string().optional(),\n})\n\nexport const businessConfigSchema = z.object({\n enabled: z.boolean().default(false),\n timezone: z.string().default(\"UTC\"),\n mainChannel: channelRefSchema,\n workSource: workSourceSchema,\n roles: z.record(z.string(), roleSchema).default({}),\n orgChart: z.record(z.string(), orgEntrySchema).default({}),\n /** Per-project metadata. Phase 3 — defaults to empty array, so\n * existing agentx.json files validate without modification. */\n projects: z.array(projectSchema).default([]),\n /** Maps free-text chat senders to a client/project so the activity\n * graph can attribute Telegram/WhatsApp/etc. to the right client\n * instead of the catch-all \"internal\" bucket. */\n contactMap: z.array(contactMapSchema).default([]),\n /** Work-tick cadence in minutes during business hours. Default 15. */\n workTickMinutes: z.number().int().min(1).max(60).default(15),\n /** Max queue depth for an idle agent before skipping the work tick (avoids piling up). */\n idleQueueThreshold: z.number().int().min(0).default(0),\n /** Standup-tick gating + plan-driven priorities. The day-cycle's morning\n * STANDUP used to fire unconditionally per agent — operators reported it\n * as \"mechanical\" because it dispatched a Claude turn even when no human\n * had set a plan for the day. Now: the cycle reads\n * .agentx/plans/<date>.md (day → week → month fallback) and either\n * injects the plan as standup priorities, or posts a \"no plan today\"\n * notification to mainChannel and dispatches no agent. */\n standup: z.object({\n /** Master switch. When false, fireStandup is a no-op (silent). */\n enabled: z.boolean().default(true),\n /** Plans directory, relative to the daemon CWD. */\n plansDir: z.string().default(\".agentx/plans\"),\n /** Hard ceiling on how many agents the standup tick can dispatch in a\n * single morning — guards against an org-chart misconfiguration\n * fanning out to dozens of Claude calls before anyone notices. */\n maxAgentsPerDay: z.number().int().min(1).max(100).default(20),\n /** When true, even with a plan present the cycle won't dispatch — only\n * posts the resolved plan to mainChannel for human review. Useful as\n * a kill-switch while iterating on plan formats. */\n dryRun: z.boolean().default(false),\n }).default({}),\n})\n\nexport type BusinessConfig = z.infer<typeof businessConfigSchema>\nexport type BusinessRole = z.infer<typeof roleSchema>\nexport type BusinessOrgEntry = z.infer<typeof orgEntrySchema>\nexport type BusinessSchedule = z.infer<typeof scheduleSchema>\nexport type BusinessWorkSource = z.infer<typeof workSourceSchema>\nexport type BusinessChannelRef = z.infer<typeof channelRefSchema>\nexport type BusinessProject = z.infer<typeof projectSchema>\n","import { z } from \"zod\"\n\n// --- Kanban board configuration ---\n//\n// Top-level optional keys in agentx.json, independent of the business layer:\n//\n// boards: array of kanban boards, each backed by a WorkSource\n// dashboard: HTTP server controlling whether the board UI serves\n//\n// Both default to empty / disabled so existing installs see no change.\n\nexport const boardLabelSchema = z.object({\n /** Label name — used verbatim as the GitLab label string. */\n name: z.string(),\n color: z.string().regex(/^#[0-9a-f]{6}$/i).default(\"#6366f1\"),\n description: z.string().optional(),\n})\n\n/**\n * A board column. `kind` controls what issues land here and how drag-to-this-column\n * mutates the issue:\n *\n * - \"open-backlog\": opened issues that have NO scoped label matching `scopedPrefix`\n * (default \"Status\"). Dragging INTO this column removes any existing scoped label.\n * - \"scoped-label\": opened issues carrying `scopedLabel` (e.g. \"Status::Doing\").\n * Scoped labels are mutually exclusive by prefix, so adding the new one lets\n * GitLab auto-remove the old one (Premium), but we still explicitly strip the\n * previous column's label for compatibility.\n * - \"closed\": all closed issues. Dragging here closes the issue; dragging away\n * reopens it.\n * - \"label\" (default, legacy): opened issues with `mapsToLabel`; flat add/remove.\n */\nexport const boardColumnSchema = z.object({\n id: z.string().regex(/^[a-z0-9_-]+$/, \"column id must be lowercase slug\"),\n title: z.string(),\n kind: z.enum([\"open-backlog\", \"scoped-label\", \"closed\", \"label\"]).default(\"label\"),\n /** For kind=\"label\": label written on entry, removed on exit. */\n mapsToLabel: z.string().optional(),\n /** For kind=\"scoped-label\": the full scoped label (e.g. \"Status::Doing\"). */\n scopedLabel: z.string().optional(),\n /** For kind=\"open-backlog\": scoped prefix that marks \"claimed\" cards to exclude. */\n scopedPrefix: z.string().default(\"Status\"),\n /** CSS color or hex — shown as the column's top accent bar. */\n accent: z.string().optional(),\n})\nexport type BoardColumnId = string\n\nexport const boardReconcileSchema = z.object({\n enabled: z.boolean().default(true),\n staleDoingMinutes: z.number().int().min(1).default(45),\n respectLunchBreak: z.boolean().default(true),\n respectSchedule: z.boolean().default(true),\n /** Badge only for P1/P3; auto-notify + auto-demote deferred. */\n action: z.enum([\"badge\", \"notify\"]).default(\"badge\"),\n})\n\nexport const boardSourceSchema = z.discriminatedUnion(\"type\", [\n z.object({\n type: z.literal(\"gitlab\"),\n /** GitLab project paths (e.g. \"mtgl/mtgl-system-v2\"). At least one required. */\n projects: z.array(z.string()).min(1),\n }),\n // future: backlog, wiki\n])\n\n/**\n * GitLab-style default board — six columns:\n * Open (backlog) → To Do → Doing → On Hold → Review → Closed\n *\n * Drives the `Status::*` scoped label taxonomy. Moving a card adds/removes the\n * column's scoped label; moving INTO Closed closes the issue; moving OUT of\n * Closed reopens it; moving INTO Open strips any Status::* label.\n */\nexport const DEFAULT_COLUMNS: Array<z.infer<typeof boardColumnSchema>> = [\n { id: \"open\", title: \"Open\", kind: \"open-backlog\", scopedPrefix: \"Status\", accent: \"#6b7280\" },\n { id: \"todo\", title: \"To Do\", kind: \"scoped-label\", scopedLabel: \"Status::To Do\", scopedPrefix: \"Status\", accent: \"#fb7a35\" },\n { id: \"doing\", title: \"Doing\", kind: \"scoped-label\", scopedLabel: \"Status::Doing\", scopedPrefix: \"Status\", accent: \"#22c55e\" },\n { id: \"onhold\", title: \"On Hold\", kind: \"scoped-label\", scopedLabel: \"Status::On Hold\", scopedPrefix: \"Status\", accent: \"#f59e0b\" },\n { id: \"review\", title: \"Review\", kind: \"scoped-label\", scopedLabel: \"Status::Review\", scopedPrefix: \"Status\", accent: \"#3b82f6\" },\n { id: \"closed\", title: \"Closed\", kind: \"closed\", scopedPrefix: \"Status\", accent: \"#6b7280\" },\n]\n\nexport const boardSchema = z.object({\n id: z.string().regex(/^[a-z0-9_-]+$/, \"board id must be lowercase slug\"),\n name: z.string(),\n source: boardSourceSchema,\n /** If set, ANDed into every listAll query and shown as a baseline chip in the UI. */\n primaryToolLabel: z.string().optional(),\n labels: z.array(boardLabelSchema).default([]),\n columns: z.array(boardColumnSchema).min(1).default(DEFAULT_COLUMNS),\n timeRangeDays: z.number().int().min(1).max(365).default(30),\n /** Maximum closed-issue window (days) shown in the Closed column. */\n closedWindowDays: z.number().int().min(1).max(365).default(30),\n reconciliation: boardReconcileSchema.default({}),\n})\n\nexport const boardsConfigSchema = z.array(boardSchema).default([])\n\nexport const dashboardDaemonSchema = z.object({\n name: z.string(),\n /** Daemon HTTP URL (the one serving /agents, /health, /task). */\n url: z.string(),\n /** Optional board-dashboard URL if we want to proxy admin writes to this\n * peer. Defaults to `url` with port :18800/:19900 rewritten to :4202,\n * matching our convention that the dashboard runs alongside the daemon. */\n dashboardUrl: z.string().optional(),\n /** Bearer for the daemon's HTTP API (live snapshot, task stream). */\n token: z.string().optional(),\n /** Bearer for the peer's dashboard when proxying admin calls. Defaults to\n * `token` when unset. */\n dashboardToken: z.string().optional(),\n})\n\nexport const dashboardConfigSchema = z.object({\n enabled: z.boolean().default(false),\n port: z.number().int().default(4202),\n bind: z.string().default(\"127.0.0.1\"),\n /** Opt-in bearer token for writes. If unset, writes are unauthenticated. */\n token: z.string().optional(),\n /** Primary daemon the dashboard talks to for live view + A2A. */\n daemonUrl: z.string().default(\"http://localhost:18800\"),\n /** Additional daemons to poll (beyond peers discovered via primary's /mesh). */\n daemons: z.array(dashboardDaemonSchema).default([]),\n /** Default agent used for AI-assisted drafting in the create-issue flow. */\n draftAgent: z.string().optional(),\n}).default({})\n\nexport type BoardConfig = z.infer<typeof boardSchema>\nexport type BoardLabel = z.infer<typeof boardLabelSchema>\nexport type BoardColumn = z.infer<typeof boardColumnSchema>\nexport type BoardSource = z.infer<typeof boardSourceSchema>\nexport type BoardReconcile = z.infer<typeof boardReconcileSchema>\nexport type DashboardConfig = z.infer<typeof dashboardConfigSchema>\n\n/**\n * Decide which column an item lives in. Walks columns in order; the first\n * matching column wins. Items with `state: \"closed\"` always land in the\n * first `kind: \"closed\"` column; otherwise the first `scoped-label` whose\n * `scopedLabel` is present wins; otherwise the first `label` column whose\n * `mapsToLabel` is present; otherwise the first `open-backlog` column.\n *\n * Returns `null` when the item carries a scoped label matching an\n * `open-backlog` column's `scopedPrefix` but no `scoped-label` column claims\n * it — those are off-board workflow states (e.g. `Status::Done` when no\n * Done column exists) and must not leak into the backlog.\n */\nexport function deriveStage(\n item: { state?: \"opened\" | \"closed\"; labels?: string[] },\n columns: BoardColumn[],\n): BoardColumnId | null {\n const set = new Set(item.labels || [])\n if (item.state === \"closed\") {\n const closed = columns.find((c) => c.kind === \"closed\")\n if (closed) return closed.id\n }\n for (const c of columns) {\n if (c.kind === \"scoped-label\" && c.scopedLabel && set.has(c.scopedLabel)) return c.id\n }\n for (const c of columns) {\n if (c.kind === \"label\" && c.mapsToLabel && set.has(c.mapsToLabel)) return c.id\n }\n const backlog = columns.find((c) => c.kind === \"open-backlog\")\n if (backlog) {\n // If the item has any scoped-prefix label, it's not an open-backlog candidate.\n const prefix = backlog.scopedPrefix + \"::\"\n const hasScoped = [...set].some((l) => l.startsWith(prefix))\n return hasScoped ? null : backlog.id\n }\n return columns[0]?.id ?? null\n}\n\n/**\n * Describes how to mutate an issue to move it between two columns.\n * `closeIssue` / `reopen` take precedence over label mutations on the same action.\n */\nexport interface ColumnTransition {\n addLabels?: string[]\n removeLabels?: string[]\n closeIssue?: boolean\n reopen?: boolean\n}\n\nexport function transitionDiff(\n fromColumn: BoardColumn | undefined,\n toColumn: BoardColumn,\n): ColumnTransition {\n const add: string[] = []\n const remove: string[] = []\n let closeIssue = false\n let reopen = false\n\n // Handle target column.\n if (toColumn.kind === \"scoped-label\" && toColumn.scopedLabel) {\n add.push(toColumn.scopedLabel)\n } else if (toColumn.kind === \"label\" && toColumn.mapsToLabel) {\n add.push(toColumn.mapsToLabel)\n } else if (toColumn.kind === \"closed\") {\n closeIssue = true\n }\n\n // Handle source column — strip its label; if it was \"closed\", reopen.\n if (fromColumn) {\n if (fromColumn.kind === \"scoped-label\" && fromColumn.scopedLabel) {\n remove.push(fromColumn.scopedLabel)\n } else if (fromColumn.kind === \"label\" && fromColumn.mapsToLabel) {\n remove.push(fromColumn.mapsToLabel)\n } else if (fromColumn.kind === \"closed\" && toColumn.kind !== \"closed\") {\n reopen = true\n }\n }\n\n return {\n addLabels: add.length ? add : undefined,\n removeLabels: remove.length ? remove : undefined,\n closeIssue: closeIssue || undefined,\n reopen: reopen || undefined,\n }\n}\n"],"mappings":"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","names":["z","readFileSync","existsSync","resolve","z","dayEnum","scheduleSchema","roleSchema","orgEntrySchema","channelRefSchema","workSourceSchema","projectSchema","contactMapSchema","businessConfigSchema","z","boardLabelSchema","boardColumnSchema","boardReconcileSchema","boardSourceSchema","DEFAULT_COLUMNS","boardSchema","boardsConfigSchema","dashboardDaemonSchema","dashboardConfigSchema","deriveStage","item","columns","set","closed","c","backlog","prefix","l","transitionDiff","fromColumn","toColumn","add","remove","closeIssue","reopen","loadDotEnv","dir","envPath","resolve","existsSync","content","readFileSync","line","trimmed","eqIdx","key","value","providerConfigSchema","z","mcpServerSchema","agentConfigSchema","telegramAccountSchema","channelsConfigSchema","cronJobSchema","v","serviceSchema","meshPeerSchema","meshConfigSchema","graphConfigSchema","notificationsSchema","daemonConfigSchema","businessConfigSchema","boardsConfigSchema","dashboardConfigSchema","expandEnvVars","obj","_match","name","result","friendlyConfigHints","issues","configPath","hints","seen","issue","path","tgTokenMatch","account","loadDaemonConfig","paths","raw","foundPath","p","parsed","e","expanded","i","hintBlock","validateWorkspaces","config","warnings","id","agent","claudeDir","providerName","providerConfig","cron"]}
@@ -1,2 +0,0 @@
1
- import{existsSync as i,mkdirSync as d,readdirSync as g,readFileSync as f,unlinkSync as m,writeFileSync as y}from"fs";import{resolve as a}from"path";import{z as n}from"zod";var A=n.object({channel:n.enum(["telegram","whatsapp","slack","discord","email"]),handle:n.string().min(1),preferredForTasks:n.boolean().optional()}),u=n.object({id:n.string().regex(/^actor:[a-z0-9][a-z0-9_-]*$/,"actor id must be 'actor:<slug>'"),name:n.string().min(1),email:n.string().email().optional(),channels:n.array(A).min(1,"actor must have at least one channel handle"),timezone:n.string().optional(),created:n.string().optional(),updated:n.string().optional()}),b=n.union([n.object({actor:n.string()}),n.object({role:n.string()})]),R=n.enum(["first-available","round-robin","all","manager-of"]),h=n.object({id:n.string().regex(/^role:[a-z0-9][a-z0-9_-]*$/,"role id must be 'role:<slug>'"),name:n.string().min(1),members:n.array(b).default([]),assignmentStrategy:R.default("first-available"),rotationCursor:n.number().int().default(0),created:n.string().optional(),updated:n.string().optional()});function D(c){return c.startsWith("actor:")?{kind:"actor",id:c}:c.startsWith("role:")?{kind:"role",id:c}:null}var S=class{actorsDir;rolesDir;constructor(t={}){let e=t.baseDir??a(process.cwd(),".agentx");this.actorsDir=a(e,"actors"),this.rolesDir=a(e,"roles"),d(this.actorsDir,{recursive:!0}),d(this.rolesDir,{recursive:!0})}actorPath(t){let e=t.replace(/^actor:/,"");return a(this.actorsDir,`${e}.json`)}listActors(){if(!i(this.actorsDir))return[];let t=[];for(let e of g(this.actorsDir,{withFileTypes:!0}))if(!(!e.isFile()||!e.name.endsWith(".json")))try{let r=JSON.parse(f(a(this.actorsDir,e.name),"utf-8")),s=u.safeParse(r);s.success&&t.push(s.data)}catch{}return t.sort((e,r)=>e.id.localeCompare(r.id))}getActor(t){let e=this.actorPath(t);if(!i(e))return null;try{let r=JSON.parse(f(e,"utf-8")),s=u.safeParse(r);return s.success?s.data:null}catch{return null}}saveActor(t){let e=u.parse({...t,created:t.created??new Date().toISOString(),updated:new Date().toISOString()});return y(this.actorPath(e.id),JSON.stringify(e,null,2)),e}deleteActor(t){let e=this.actorPath(t);return i(e)?(m(e),!0):!1}rolePath(t){let e=t.replace(/^role:/,"");return a(this.rolesDir,`${e}.json`)}listRoles(){if(!i(this.rolesDir))return[];let t=[];for(let e of g(this.rolesDir,{withFileTypes:!0}))if(!(!e.isFile()||!e.name.endsWith(".json")))try{let r=JSON.parse(f(a(this.rolesDir,e.name),"utf-8")),s=h.safeParse(r);s.success&&t.push(s.data)}catch{}return t.sort((e,r)=>e.id.localeCompare(r.id))}getRole(t){let e=this.rolePath(t);if(!i(e))return null;try{let r=JSON.parse(f(e,"utf-8")),s=h.safeParse(r);return s.success?s.data:null}catch{return null}}saveRole(t){let e=h.parse({...t,created:t.created??new Date().toISOString(),updated:new Date().toISOString()});return y(this.rolePath(e.id),JSON.stringify(e,null,2)),e}deleteRole(t){let e=this.rolePath(t);return i(e)?(m(e),!0):!1}resolveMembers(t){if(t.kind==="actor")return[t.id];let e=new Set,r=[],s=o=>{if(e.has(o))return;e.add(o);let p=this.getRole(o);if(p)for(let l of p.members)"actor"in l?r.includes(l.actor)||r.push(l.actor):s(l.role)};return s(t.id),r}pickAssignees(t){let e=this.resolveMembers(t);if(e.length===0)return[];if(t.kind==="actor")return e;let r=this.getRole(t.id);if(!r)return e.slice(0,1);switch(r.assignmentStrategy){case"all":return e;case"round-robin":{let s=r.rotationCursor%e.length,o=e[s];return this.saveRole({...r,rotationCursor:(r.rotationCursor+1)%Math.max(e.length,1)}),[o]}case"first-available":case"manager-of":default:return[e[0]]}}channelFor(t,e){let r=this.getActor(t);return r?r.channels.find(o=>o.channel===e)?.handle??null:null}preferredChannel(t){let e=this.getActor(t);return!e||e.channels.length===0?null:e.channels.find(r=>r.preferredForTasks)??e.channels[0]}};export{D as a,S as b};
2
- //# sourceMappingURL=chunk-EW27SCCC.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/actors/store.ts","../src/actors/types.ts"],"sourcesContent":["import { existsSync, mkdirSync, readdirSync, readFileSync, unlinkSync, writeFileSync } from \"fs\"\nimport { resolve } from \"path\"\nimport {\n actorSchema,\n roleSchema,\n type Actor,\n type ActorChannel,\n type Role,\n type AssigneeRef,\n} from \"./types\"\n\n// --- Actor / Role filesystem store ---\n//\n// Two sibling directories under .agentx/:\n// actors/<actor-id>.json\n// roles/<role-id>.json\n//\n// The store exposes CRUD + two resolution helpers needed by the workflow\n// engine:\n// resolveMembers(assignee) — role or actor ref → flat actor id list\n// (handles nested roles, strategy picks who sees\n// the task)\n// channelFor(actorId, ch) — actor id + preferred channel → handle string\n\nexport interface ActorStoreOptions {\n baseDir?: string\n}\n\nexport class ActorStore {\n readonly actorsDir: string\n readonly rolesDir: string\n\n constructor(opts: ActorStoreOptions = {}) {\n const root = opts.baseDir ?? resolve(process.cwd(), \".agentx\")\n this.actorsDir = resolve(root, \"actors\")\n this.rolesDir = resolve(root, \"roles\")\n mkdirSync(this.actorsDir, { recursive: true })\n mkdirSync(this.rolesDir, { recursive: true })\n }\n\n // --- Actors ---\n\n private actorPath(id: string): string {\n const safe = id.replace(/^actor:/, \"\")\n return resolve(this.actorsDir, `${safe}.json`)\n }\n\n listActors(): Actor[] {\n if (!existsSync(this.actorsDir)) return []\n const out: Actor[] = []\n for (const entry of readdirSync(this.actorsDir, { withFileTypes: true })) {\n if (!entry.isFile() || !entry.name.endsWith(\".json\")) continue\n try {\n const raw = JSON.parse(readFileSync(resolve(this.actorsDir, entry.name), \"utf-8\"))\n const parsed = actorSchema.safeParse(raw)\n if (parsed.success) out.push(parsed.data)\n } catch {\n // skip malformed\n }\n }\n return out.sort((a, b) => a.id.localeCompare(b.id))\n }\n\n getActor(id: string): Actor | null {\n const full = this.actorPath(id)\n if (!existsSync(full)) return null\n try {\n const raw = JSON.parse(readFileSync(full, \"utf-8\"))\n const parsed = actorSchema.safeParse(raw)\n return parsed.success ? parsed.data : null\n } catch {\n return null\n }\n }\n\n saveActor(actor: Actor): Actor {\n const parsed = actorSchema.parse({\n ...actor,\n created: actor.created ?? new Date().toISOString(),\n updated: new Date().toISOString(),\n })\n writeFileSync(this.actorPath(parsed.id), JSON.stringify(parsed, null, 2))\n return parsed\n }\n\n deleteActor(id: string): boolean {\n const full = this.actorPath(id)\n if (!existsSync(full)) return false\n unlinkSync(full)\n return true\n }\n\n // --- Roles ---\n\n private rolePath(id: string): string {\n const safe = id.replace(/^role:/, \"\")\n return resolve(this.rolesDir, `${safe}.json`)\n }\n\n listRoles(): Role[] {\n if (!existsSync(this.rolesDir)) return []\n const out: Role[] = []\n for (const entry of readdirSync(this.rolesDir, { withFileTypes: true })) {\n if (!entry.isFile() || !entry.name.endsWith(\".json\")) continue\n try {\n const raw = JSON.parse(readFileSync(resolve(this.rolesDir, entry.name), \"utf-8\"))\n const parsed = roleSchema.safeParse(raw)\n if (parsed.success) out.push(parsed.data)\n } catch {\n // skip\n }\n }\n return out.sort((a, b) => a.id.localeCompare(b.id))\n }\n\n getRole(id: string): Role | null {\n const full = this.rolePath(id)\n if (!existsSync(full)) return null\n try {\n const raw = JSON.parse(readFileSync(full, \"utf-8\"))\n const parsed = roleSchema.safeParse(raw)\n return parsed.success ? parsed.data : null\n } catch {\n return null\n }\n }\n\n saveRole(role: Role): Role {\n const parsed = roleSchema.parse({\n ...role,\n created: role.created ?? new Date().toISOString(),\n updated: new Date().toISOString(),\n })\n writeFileSync(this.rolePath(parsed.id), JSON.stringify(parsed, null, 2))\n return parsed\n }\n\n deleteRole(id: string): boolean {\n const full = this.rolePath(id)\n if (!existsSync(full)) return false\n unlinkSync(full)\n return true\n }\n\n // --- Resolution ---\n\n /** Flatten a role (or actor) reference to a concrete list of actor ids.\n * Handles nested roles (role containing role). Cycles are guarded. */\n resolveMembers(assignee: AssigneeRef): string[] {\n if (assignee.kind === \"actor\") return [assignee.id]\n const seen = new Set<string>()\n const out: string[] = []\n const walk = (roleId: string) => {\n if (seen.has(roleId)) return\n seen.add(roleId)\n const role = this.getRole(roleId)\n if (!role) return\n for (const member of role.members) {\n if (\"actor\" in member) {\n if (!out.includes(member.actor)) out.push(member.actor)\n } else {\n walk(member.role)\n }\n }\n }\n walk(assignee.id)\n return out\n }\n\n /** Pick which members of an assignee actually see a task right now,\n * obeying the role's assignment strategy. Returns one or more actor ids. */\n pickAssignees(assignee: AssigneeRef): string[] {\n const members = this.resolveMembers(assignee)\n if (members.length === 0) return []\n if (assignee.kind === \"actor\") return members\n const role = this.getRole(assignee.id)\n if (!role) return members.slice(0, 1)\n switch (role.assignmentStrategy) {\n case \"all\":\n return members\n case \"round-robin\": {\n const idx = role.rotationCursor % members.length\n const picked = members[idx]\n this.saveRole({ ...role, rotationCursor: (role.rotationCursor + 1) % Math.max(members.length, 1) })\n return [picked]\n }\n case \"first-available\":\n case \"manager-of\":\n default:\n return [members[0]]\n }\n }\n\n /** Look up an actor's handle for a given channel. Returns null if the\n * actor has no handle on that channel. */\n channelFor(actorId: string, channel: ActorChannel[\"channel\"]): string | null {\n const actor = this.getActor(actorId)\n if (!actor) return null\n const match = actor.channels.find((c) => c.channel === channel)\n return match?.handle ?? null\n }\n\n /** Pick the delivery channel for user-task notifications. Prefers the\n * channel marked preferredForTasks, else the first channel. */\n preferredChannel(actorId: string): ActorChannel | null {\n const actor = this.getActor(actorId)\n if (!actor || actor.channels.length === 0) return null\n return actor.channels.find((c) => c.preferredForTasks) ?? actor.channels[0]\n }\n}\n","import { z } from \"zod\"\n\n// --- Actor + Role primitives ---\n//\n// An Actor is a human participant in a business activity. They carry one or\n// more channel handles (a Telegram user id, a WhatsApp phone, a Slack member\n// id, an email address) so the workflow engine can deliver user tasks to\n// wherever they actually read messages.\n//\n// A Role is a named group of Actors (or nested Roles). User tasks may be\n// assigned to a Role instead of a specific Actor — the engine uses the Role's\n// assignmentStrategy to pick which member(s) actually see the task.\n//\n// Both are stored as JSON under .agentx/actors/ and .agentx/roles/.\n\nexport const actorChannelSchema = z.object({\n channel: z.enum([\"telegram\", \"whatsapp\", \"slack\", \"discord\", \"email\"]),\n /** Channel-native handle: telegram user id, phone number (E.164), slack\n * member id, discord user id, email address. Stable per channel. */\n handle: z.string().min(1),\n /** If true, user-task notifications for this actor land on this channel\n * first. At most one channel should be marked preferred; ties break in\n * array order. */\n preferredForTasks: z.boolean().optional(),\n})\nexport type ActorChannel = z.infer<typeof actorChannelSchema>\n\nexport const actorSchema = z.object({\n id: z.string().regex(/^actor:[a-z0-9][a-z0-9_-]*$/, \"actor id must be 'actor:<slug>'\"),\n name: z.string().min(1),\n email: z.string().email().optional(),\n channels: z.array(actorChannelSchema).min(1, \"actor must have at least one channel handle\"),\n timezone: z.string().optional(),\n created: z.string().optional(),\n updated: z.string().optional(),\n})\nexport type Actor = z.infer<typeof actorSchema>\n\nexport const roleMemberSchema = z.union([\n z.object({ actor: z.string() }),\n z.object({ role: z.string() }),\n])\nexport type RoleMember = z.infer<typeof roleMemberSchema>\n\nexport const assignmentStrategySchema = z.enum([\n \"first-available\", // first member in order\n \"round-robin\", // rotate across members (state persisted on the role)\n \"all\", // assign to every member simultaneously\n \"manager-of\", // resolve via actor.managerOf relationship (future)\n])\nexport type AssignmentStrategy = z.infer<typeof assignmentStrategySchema>\n\nexport const roleSchema = z.object({\n id: z.string().regex(/^role:[a-z0-9][a-z0-9_-]*$/, \"role id must be 'role:<slug>'\"),\n name: z.string().min(1),\n members: z.array(roleMemberSchema).default([]),\n assignmentStrategy: assignmentStrategySchema.default(\"first-available\"),\n /** Mutable rotation cursor for round-robin. Incremented on assign. */\n rotationCursor: z.number().int().default(0),\n created: z.string().optional(),\n updated: z.string().optional(),\n})\nexport type Role = z.infer<typeof roleSchema>\n\n// Reference shape used in workflow node configs: either an actor id or a role\n// id. Encoded as a string with the typed prefix so templating stays simple.\nexport type ActorRef = { kind: \"actor\"; id: string }\nexport type RoleRef = { kind: \"role\"; id: string }\nexport type AssigneeRef = ActorRef | RoleRef\n\nexport function parseAssigneeRef(raw: string): AssigneeRef | null {\n if (raw.startsWith(\"actor:\")) return { kind: \"actor\", id: raw }\n if (raw.startsWith(\"role:\")) return { kind: \"role\", id: raw }\n return null\n}\n"],"mappings":"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","names":["existsSync","mkdirSync","readdirSync","readFileSync","unlinkSync","writeFileSync","resolve","z","actorChannelSchema","actorSchema","roleMemberSchema","assignmentStrategySchema","roleSchema","parseAssigneeRef","raw","ActorStore","opts","root","resolve","mkdirSync","id","safe","existsSync","out","entry","readdirSync","raw","readFileSync","parsed","actorSchema","a","b","full","actor","writeFileSync","unlinkSync","roleSchema","role","assignee","seen","walk","roleId","member","members","idx","picked","actorId","channel","c"]}