agentix-cli 0.23.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.
- package/LICENSE +21 -0
- package/README.md +102 -103
- package/agentx.example.json +9 -3
- package/dist/App-6Y7B3YQH.js +8 -0
- package/dist/App-6Y7B3YQH.js.map +1 -0
- package/dist/ChatApp-KIEUJ2DS.js +7 -0
- package/dist/ChatApp-KIEUJ2DS.js.map +1 -0
- package/dist/{agent-FSE4KRLX.js → agent-QGZIAZMY.js} +2 -2
- package/dist/architect-SEPWMWS4.js +2 -0
- package/dist/{board-dashboard-PUACKWAN.js → board-dashboard-CQ5F4GPD.js} +3756 -1010
- package/dist/board-dashboard-CQ5F4GPD.js.map +1 -0
- package/dist/builtin-FRKAQMFS.js +2 -0
- package/dist/chunk-2WVX3LEC.js +2 -0
- package/dist/chunk-2WVX3LEC.js.map +1 -0
- package/dist/chunk-3WQFCIB6.js +24 -0
- package/dist/chunk-3WQFCIB6.js.map +1 -0
- package/dist/chunk-53GCFZPR.js +116 -0
- package/dist/chunk-53GCFZPR.js.map +1 -0
- package/dist/{chunk-IKBHHTIT.js → chunk-5U35WAZF.js} +2 -2
- package/dist/chunk-67Q4DCPS.js +54 -0
- package/dist/chunk-67Q4DCPS.js.map +1 -0
- package/dist/chunk-76Z4VHDX.js +8 -0
- package/dist/chunk-76Z4VHDX.js.map +1 -0
- package/dist/{chunk-7P5QRX5W.js → chunk-BFXONUUN.js} +2 -2
- package/dist/{chunk-DXTIG5YV.js → chunk-FDMWOPEL.js} +2 -2
- package/dist/chunk-FDWOECJY.js +54 -0
- package/dist/chunk-FDWOECJY.js.map +1 -0
- package/dist/chunk-G57RZCXM.js +5 -0
- package/dist/chunk-G57RZCXM.js.map +1 -0
- package/dist/{chunk-NOF4HSGK.js → chunk-H5D7ATXS.js} +2 -2
- package/dist/chunk-HA3XI7Z2.js +155 -0
- package/dist/chunk-HA3XI7Z2.js.map +1 -0
- package/dist/chunk-HR4JSRZS.js +3 -0
- package/dist/{chunk-A6BKSZQA.js.map → chunk-HR4JSRZS.js.map} +1 -1
- package/dist/chunk-I3SIXTJO.js +44 -0
- package/dist/chunk-I3SIXTJO.js.map +1 -0
- package/dist/chunk-ISEBLHWX.js +118 -0
- package/dist/chunk-ISEBLHWX.js.map +1 -0
- package/dist/{chunk-4ITQP3XL.js → chunk-JP5IEXR7.js} +2 -2
- package/dist/chunk-JXWAGMI2.js +13 -0
- package/dist/chunk-JXWAGMI2.js.map +1 -0
- package/dist/chunk-KUCCW2VG.js +431 -0
- package/dist/chunk-KUCCW2VG.js.map +1 -0
- package/dist/chunk-LDHPMJ7M.js +257 -0
- package/dist/chunk-LDHPMJ7M.js.map +1 -0
- package/dist/{chunk-KWUWZQGV.js → chunk-LNXCJWOU.js} +29 -30
- package/dist/chunk-LNXCJWOU.js.map +1 -0
- package/dist/chunk-M4AMWP3H.js +178 -0
- package/dist/chunk-M4AMWP3H.js.map +1 -0
- package/dist/chunk-NGSIRE4X.js +126 -0
- package/dist/chunk-NGSIRE4X.js.map +1 -0
- package/dist/{chunk-CNNLREHD.js → chunk-P4VWIVKN.js} +268 -410
- package/dist/chunk-P4VWIVKN.js.map +1 -0
- package/dist/chunk-Q6YO7LYE.js +47 -0
- package/dist/chunk-Q6YO7LYE.js.map +1 -0
- package/dist/chunk-TMKLHTCG.js +971 -0
- package/dist/chunk-TMKLHTCG.js.map +1 -0
- package/dist/chunk-TRJTTBE2.js +7 -0
- package/dist/chunk-TRJTTBE2.js.map +1 -0
- package/dist/chunk-U2CKL755.js +4 -0
- package/dist/chunk-U2CKL755.js.map +1 -0
- package/dist/chunk-UCQ2N3KO.js +9 -0
- package/dist/chunk-UCQ2N3KO.js.map +1 -0
- package/dist/chunk-W4YR2RO2.js +2 -0
- package/dist/chunk-W4YR2RO2.js.map +1 -0
- package/dist/cli.js +1 -1
- package/dist/cli.js.map +1 -1
- package/dist/client-GGVVLOOF.js +2 -0
- package/dist/{compaction-BTUJJ3LJ.js → compaction-NWEMOSHA.js} +3 -3
- package/dist/config-7FSZFRTQ.js +2 -0
- package/dist/config-mutator-POCOYYJ4.js +2 -0
- package/dist/{context-planner-I7ID7XF3.js → context-planner-45GEWKGL.js} +2 -2
- package/dist/doctor-DHLBEKA4.js +2 -0
- package/dist/heal-HZ3EQD52.js +2 -0
- package/dist/index.d.ts +1193 -225
- package/dist/index.js +12 -12
- package/dist/index.js.map +1 -1
- package/dist/memory-extract-AENFVVO5.js +2 -0
- package/dist/processes-UIC4XXA6.js +2 -0
- package/dist/program-WVAVJFND.js +2 -0
- package/dist/projects-api-KRME2YKZ.js +2 -0
- package/dist/projects-api-KRME2YKZ.js.map +1 -0
- package/dist/projects-mutate-3NHVG437.js +7 -0
- package/dist/projects-mutate-3NHVG437.js.map +1 -0
- package/dist/providers-L27BJ5A6.js +2 -0
- package/dist/registry-T36ZIAWV.js +2 -0
- package/dist/rotation-memo-B3EXXSEH.js +13 -0
- package/dist/rotation-memo-B3EXXSEH.js.map +1 -0
- package/dist/scheduler-GH7TIKYS.js +2 -0
- package/dist/surface-usage-PRL6Z4U3.js +2 -0
- package/dist/wiki-M6YNRFC4.js +2 -0
- package/dist/workflows/templates/branching.yaml +75 -0
- package/dist/workflows/templates/extract.yaml +59 -0
- package/dist/workflows/templates/linear.yaml +37 -0
- package/dist/workflows/templates/retry.yaml +61 -0
- package/dist/workspace-setup-TBQAHYHJ.js +2 -0
- package/package.json +10 -3
- package/dist/board-dashboard-PUACKWAN.js.map +0 -1
- package/dist/builtin-TQKMR42Z.js +0 -2
- package/dist/chunk-3SHMHYPD.js +0 -450
- package/dist/chunk-3SHMHYPD.js.map +0 -1
- package/dist/chunk-4INOOHND.js +0 -6
- package/dist/chunk-4INOOHND.js.map +0 -1
- package/dist/chunk-5F3J55LG.js +0 -2
- package/dist/chunk-5F3J55LG.js.map +0 -1
- package/dist/chunk-72PAN7UI.js +0 -103
- package/dist/chunk-72PAN7UI.js.map +0 -1
- package/dist/chunk-A6BKSZQA.js +0 -3
- package/dist/chunk-BHSPAP4U.js +0 -44
- package/dist/chunk-BHSPAP4U.js.map +0 -1
- package/dist/chunk-BOUKM2PS.js +0 -163
- package/dist/chunk-BOUKM2PS.js.map +0 -1
- package/dist/chunk-CDNWCYF7.js +0 -2
- package/dist/chunk-CDNWCYF7.js.map +0 -1
- package/dist/chunk-CNNLREHD.js.map +0 -1
- package/dist/chunk-EK37JHJI.js +0 -13
- package/dist/chunk-EK37JHJI.js.map +0 -1
- package/dist/chunk-EQPUB7CZ.js +0 -685
- package/dist/chunk-EQPUB7CZ.js.map +0 -1
- package/dist/chunk-EW27SCCC.js +0 -2
- package/dist/chunk-EW27SCCC.js.map +0 -1
- package/dist/chunk-GOLZ6NEL.js +0 -111
- package/dist/chunk-GOLZ6NEL.js.map +0 -1
- package/dist/chunk-JGXE2YNV.js +0 -46
- package/dist/chunk-JGXE2YNV.js.map +0 -1
- package/dist/chunk-KWUWZQGV.js.map +0 -1
- package/dist/chunk-MTRWXRFF.js +0 -380
- package/dist/chunk-MTRWXRFF.js.map +0 -1
- package/dist/chunk-N67JZAXU.js +0 -2
- package/dist/chunk-N67JZAXU.js.map +0 -1
- package/dist/chunk-PD5Q7D6Z.js +0 -7
- package/dist/chunk-PD5Q7D6Z.js.map +0 -1
- package/dist/chunk-Q6CAFGXT.js +0 -118
- package/dist/chunk-Q6CAFGXT.js.map +0 -1
- package/dist/chunk-WJXU5F4Z.js +0 -53
- package/dist/chunk-WJXU5F4Z.js.map +0 -1
- package/dist/chunk-ZZFXI3CS.js +0 -8
- package/dist/chunk-ZZFXI3CS.js.map +0 -1
- package/dist/config-ADS54KLC.js +0 -2
- package/dist/config-mutator-WTRMBZTO.js +0 -2
- package/dist/doctor-T6RB4Q5X.js +0 -2
- package/dist/editor-chat-RVLCKKB7.js +0 -137
- package/dist/editor-chat-RVLCKKB7.js.map +0 -1
- package/dist/heal-YEQYWX2L.js +0 -2
- package/dist/inbox-BIN2S5XN.js +0 -2
- package/dist/memory-extract-KRCHD3OP.js +0 -2
- package/dist/processes-RJ6RDP32.js +0 -2
- package/dist/program-V7NGFB2R.js +0 -2
- package/dist/providers-WVCMIW67.js +0 -2
- package/dist/registry-42HEWCLA.js +0 -2
- package/dist/rotation-memo-GMTZPMNE.js +0 -13
- package/dist/rotation-memo-GMTZPMNE.js.map +0 -1
- package/dist/scheduler-C56ABAVP.js +0 -2
- package/dist/slack-6P7ZJ32Y.js +0 -3
- package/dist/slack-6P7ZJ32Y.js.map +0 -1
- package/dist/store-BAPDZHXE.js +0 -2
- package/dist/task-store-RHISEU6V.js +0 -2
- package/dist/telegram-PIYDRM46.js +0 -3
- package/dist/telegram-PIYDRM46.js.map +0 -1
- package/dist/types-ZRLHHCGB.js +0 -2
- package/dist/web/workflow-editor.global.js +0 -1452
- package/dist/web/workflow-editor.global.js.map +0 -1
- package/dist/whatsapp-PFRDMUMT.js +0 -3
- package/dist/whatsapp-PFRDMUMT.js.map +0 -1
- package/dist/wiki-53W5NLZV.js +0 -2
- package/dist/workspace-setup-DQCZX2NE.js +0 -2
- package/dist/workspace-setup-DQCZX2NE.js.map +0 -1
- /package/dist/{agent-FSE4KRLX.js.map → agent-QGZIAZMY.js.map} +0 -0
- /package/dist/{builtin-TQKMR42Z.js.map → architect-SEPWMWS4.js.map} +0 -0
- /package/dist/{config-ADS54KLC.js.map → builtin-FRKAQMFS.js.map} +0 -0
- /package/dist/{chunk-IKBHHTIT.js.map → chunk-5U35WAZF.js.map} +0 -0
- /package/dist/{chunk-7P5QRX5W.js.map → chunk-BFXONUUN.js.map} +0 -0
- /package/dist/{chunk-DXTIG5YV.js.map → chunk-FDMWOPEL.js.map} +0 -0
- /package/dist/{chunk-NOF4HSGK.js.map → chunk-H5D7ATXS.js.map} +0 -0
- /package/dist/{chunk-4ITQP3XL.js.map → chunk-JP5IEXR7.js.map} +0 -0
- /package/dist/{config-mutator-WTRMBZTO.js.map → client-GGVVLOOF.js.map} +0 -0
- /package/dist/{compaction-BTUJJ3LJ.js.map → compaction-NWEMOSHA.js.map} +0 -0
- /package/dist/{doctor-T6RB4Q5X.js.map → config-7FSZFRTQ.js.map} +0 -0
- /package/dist/{heal-YEQYWX2L.js.map → config-mutator-POCOYYJ4.js.map} +0 -0
- /package/dist/{context-planner-I7ID7XF3.js.map → context-planner-45GEWKGL.js.map} +0 -0
- /package/dist/{inbox-BIN2S5XN.js.map → doctor-DHLBEKA4.js.map} +0 -0
- /package/dist/{memory-extract-KRCHD3OP.js.map → heal-HZ3EQD52.js.map} +0 -0
- /package/dist/{processes-RJ6RDP32.js.map → memory-extract-AENFVVO5.js.map} +0 -0
- /package/dist/{program-V7NGFB2R.js.map → processes-UIC4XXA6.js.map} +0 -0
- /package/dist/{providers-WVCMIW67.js.map → program-WVAVJFND.js.map} +0 -0
- /package/dist/{registry-42HEWCLA.js.map → providers-L27BJ5A6.js.map} +0 -0
- /package/dist/{scheduler-C56ABAVP.js.map → registry-T36ZIAWV.js.map} +0 -0
- /package/dist/{store-BAPDZHXE.js.map → scheduler-GH7TIKYS.js.map} +0 -0
- /package/dist/{task-store-RHISEU6V.js.map → surface-usage-PRL6Z4U3.js.map} +0 -0
- /package/dist/{types-ZRLHHCGB.js.map → wiki-M6YNRFC4.js.map} +0 -0
- /package/dist/{wiki-53W5NLZV.js.map → workspace-setup-TBQAHYHJ.js.map} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/daemon/ui/tokens.ts","../src/daemon/ui/components.css.ts","../src/daemon/ui/shell.ts","../src/daemon/ui/util.ts","../src/daemon/ui/components.ts"],"sourcesContent":["// --- Design tokens for every dashboard surface ---\n//\n// SINGLE source of truth. Every page imports AX_TOKENS_CSS and drops it in\n// its <style> block once, so no page carries its own :root copy.\n//\n// The palette and shape language are ported from the agentina console\n// (packages/console/src/index.ts) — a deliberately plain, friendly system\n// built on three principles worth restating here, because they are as much\n// about what we remove as how it looks:\n//\n// 1. CONTACTS, NOT CONCEPTS — surface the people and agents you work with;\n// grants, sessions and adapters stay machinery.\n// 2. ONE SCREEN, ONE JOB — a flow is a short wizard with one obvious next\n// action, not a dense panel of every option.\n// 3. HONEST UI — nothing simulated. Offline is offline, denied is denied.\n//\n// What was ported and what was not:\n// - Ported: the four-colour brand palette, 2px borders, generous radii,\n// pill chips, the hard offset shadow (`0 4px 0 <darker>`) that makes\n// controls read as physical, and Outfit / Roboto Mono.\n// - NOT ported: light-only. agentina is a consumer console; this is a\n// dense ops dashboard people read at 2am, so the dark variant stays and\n// carries the same shape language with the palette lifted for contrast.\n// - Removed: the `crt` theme. Two well-maintained themes beat three, and\n// it had no users.\n//\n// Guideline for adding tokens: every token must be page-neutral. Page-\n// specific padding/gaps belong in the page's own CSS file, not here.\n\nexport const AX_TOKENS_CSS = `:root {\n /* --- Brand (agentina) --------------------------------------------- */\n --ax-blue: #2979FF;\n --ax-blue-d: #1B5FD9;\n --ax-blue-t: #E7F0FF;\n --ax-blue-e: #A9CBFF;\n --ax-green: #22B573;\n --ax-green-d: #178F58;\n --ax-green-e: #A9E8C9;\n --ax-amber: #FFB300;\n --ax-amber-e: #FFE08A;\n --ax-amber-t: #FFFCF5;\n --ax-red: #F23A3A;\n --ax-red-t: #FEECEC;\n --ax-red-e: #F9C1C1;\n\n /* --- Surfaces + text ---------------------------------------------- */\n --ax-bg: #f8f9fa;\n --ax-bg-elev: #f1f3f4;\n --ax-surface: #ffffff;\n --ax-surface-2: #f8f9fa;\n --ax-surface-3: #f1f3f4;\n --ax-border: #e8eaed;\n --ax-border-2: #dadce0;\n --ax-text: #202124;\n --ax-text-2: #5f6368;\n --ax-muted: #9aa0a6;\n\n /* --- Semantic ------------------------------------------------------ */\n --ax-accent: var(--ax-blue);\n --ax-accent-2: var(--ax-blue-d);\n --ax-accent-t: var(--ax-blue-t);\n --ax-ok: var(--ax-green);\n --ax-warn: var(--ax-amber);\n --ax-err: var(--ax-red);\n --ax-info: var(--ax-blue);\n\n /* --- Shape ---------------------------------------------------------\n The offset shadow is the signature move: a solid, un-blurred drop in a\n darker shade of the element's own colour. It reads as a physical edge\n rather than a glow, and it is what stops flat 2px-bordered boxes from\n looking like a wireframe. */\n --ax-border-w: 2px;\n --ax-radius: 16px;\n --ax-radius-lg: 20px;\n --ax-radius-sm: 12px;\n --ax-radius-pill: 999px;\n --ax-shadow: 0 3px 0 var(--ax-border);\n --ax-shadow-lg: 0 4px 0 var(--ax-border-2);\n --ax-shadow-accent: 0 4px 0 var(--ax-blue-d);\n --ax-shadow-ok: 0 4px 0 var(--ax-green-d);\n\n /* --- Spacing ------------------------------------------------------- */\n --ax-pad: 16px;\n --ax-pad-sm: 10px;\n --ax-gap: 12px;\n\n /* --- Type ---------------------------------------------------------- */\n --ax-font: \"Outfit\", -apple-system, \"Segoe UI\", system-ui, sans-serif;\n --ax-mono: \"Roboto Mono\", ui-monospace, \"SF Mono\", Consolas, monospace;\n --ax-fs: 14px;\n --ax-fs-sm: 13px;\n --ax-fs-xs: 12px;\n\n /* Legacy aliases so older CSS blocks keep rendering until they're ported. */\n --bg: var(--ax-bg);\n --card: var(--ax-surface);\n --node: var(--ax-bg-elev);\n --border: var(--ax-border);\n --text: var(--ax-text);\n --muted: var(--ax-muted);\n --accent: var(--ax-accent);\n --yellow: var(--ax-warn);\n --red: var(--ax-err);\n --blue: var(--ax-info);\n --gray: var(--ax-muted);\n color-scheme: light;\n}\n\n/* Dark is not a tint of light — the brand colours are lifted toward their\n tints so they keep contrast against a dark ground, and the offset shadow\n goes DARKER than the surface instead of lighter. Same shape language,\n inverted physics. */\n[data-theme=\"dark\"] {\n --ax-blue: #6BA5FF;\n --ax-blue-d: #1B5FD9;\n --ax-blue-t: #16233d;\n --ax-blue-e: #2f4d7a;\n --ax-green: #4ED89B;\n --ax-green-d: #178F58;\n --ax-green-e: #1e4a36;\n --ax-amber: #FFC948;\n --ax-amber-e: #4a3a12;\n --ax-amber-t: #2a2313;\n --ax-red: #FF6B6B;\n --ax-red-t: #3a1c1c;\n --ax-red-e: #6b2f2f;\n\n --ax-bg: #14161a;\n --ax-bg-elev: #191c21;\n --ax-surface: #1e2228;\n --ax-surface-2: #23272e;\n --ax-surface-3: #2a2f37;\n --ax-border: #2f353e;\n --ax-border-2: #3d444f;\n --ax-text: #f1f3f4;\n --ax-text-2: #b6bcc4;\n --ax-muted: #858c96;\n\n --ax-shadow: 0 3px 0 #0e1013;\n --ax-shadow-lg: 0 4px 0 #0e1013;\n --ax-shadow-accent: 0 4px 0 #14396e;\n --ax-shadow-ok: 0 4px 0 #11402c;\n color-scheme: dark;\n}\n\n/* Base element resets shared across every page. */\n* { box-sizing: border-box; }\nhtml, body {\n margin: 0; min-height: 100vh;\n background: var(--ax-bg); color: var(--ax-text);\n font-family: var(--ax-font); font-size: var(--ax-fs);\n -webkit-font-smoothing: antialiased;\n}\npre, code, .ax-mono {\n font-family: var(--ax-mono); font-variant-numeric: tabular-nums;\n letter-spacing: -0.01em;\n}\na { color: var(--ax-accent); text-decoration: none; font-weight: 600; }\na:hover { color: var(--ax-accent-2); text-decoration: underline; }\n.ax-muted { color: var(--ax-muted); }\n.ax-accent { color: var(--ax-accent); }`\n","// --- Shared component CSS ---\n//\n// Styles for the primitives in components.ts. Kept separate from tokens so a\n// page can opt-in to only what it uses (setup-wizard doesn't need stat-strip\n// styles, for example). Most pages just emit both blocks together.\n//\n// All class names are prefixed `ax-` — they won't collide with host-app or\n// per-page styles.\n\nexport const AX_COMPONENTS_CSS = `\n/* --- Dots --- */\n.ax-dot { display: inline-block; width: 7px; height: 7px; border-radius: 50%; background: var(--ax-muted); }\n.ax-dot--ok, .ax-dot--live { background: var(--ax-accent); }\n.ax-dot--warn { background: var(--ax-warn); }\n.ax-dot--err { background: var(--ax-err); }\n.ax-dot--off { background: var(--ax-border-2); }\n.ax-dot--pulse {\n box-shadow: 0 0 0 0 currentColor;\n animation: ax-pulse 1.8s infinite;\n color: var(--ax-accent);\n}\n@keyframes ax-pulse {\n 0% { box-shadow: 0 0 0 0 color-mix(in oklch, var(--ax-accent) 70%, transparent); }\n 70% { box-shadow: 0 0 0 6px color-mix(in oklch, var(--ax-accent) 0%, transparent); }\n 100% { box-shadow: 0 0 0 0 color-mix(in oklch, var(--ax-accent) 0%, transparent); }\n}\n\n/* --- Badges --- */\n.ax-badge {\n display: inline-flex; align-items: center; gap: 4px;\n padding: 2px 10px; border-radius: var(--ax-radius-pill); font-size: var(--ax-fs-xs); line-height: 18px;\n border: var(--ax-border-w) solid var(--ax-border-2); color: var(--ax-text-2);\n background: var(--ax-surface); font-weight: 600;\n}\n.ax-badge--mono {\n font-family: var(--ax-mono); letter-spacing: 0.02em;\n text-transform: uppercase; font-size: 10px;\n}\n.ax-badge--accent {\n color: var(--ax-accent);\n border-color: color-mix(in oklch, var(--ax-accent) 50%, transparent);\n}\n.ax-badge--live {\n color: var(--ax-accent);\n background: color-mix(in oklch, var(--ax-accent) 14%, transparent);\n border-color: color-mix(in oklch, var(--ax-accent) 60%, transparent);\n font-weight: 600; letter-spacing: 0.06em;\n}\n.ax-badge--warn {\n color: var(--ax-warn);\n border-color: color-mix(in oklch, var(--ax-warn) 50%, transparent);\n}\n.ax-badge--err {\n color: var(--ax-err);\n border-color: color-mix(in oklch, var(--ax-err) 50%, transparent);\n}\n.ax-badge--ghost {\n color: var(--ax-muted); border-color: var(--ax-border); background: var(--ax-surface-2);\n}\n\n/* --- Buttons --- */\n.ax-btn {\n background: var(--ax-surface); color: var(--ax-text-2);\n border: var(--ax-border-w) solid var(--ax-border-2); border-radius: var(--ax-radius);\n padding: 8px 16px; font: inherit; font-size: var(--ax-fs); font-weight: 600;\n cursor: pointer; line-height: 1.4;\n box-shadow: var(--ax-shadow);\n transition: border-color 0.1s, color 0.1s, background 0.1s, transform 0.06s, box-shadow 0.06s;\n}\n.ax-btn:hover:not(:disabled) { color: var(--ax-text); border-color: var(--ax-accent); }\n/* The offset shadow collapses on press so the control physically sinks —\n the whole point of an un-blurred drop rather than a glow. */\n.ax-btn:active:not(:disabled) { transform: translateY(2px); box-shadow: 0 1px 0 var(--ax-border); }\n.ax-btn:disabled { opacity: 0.5; cursor: not-allowed; box-shadow: none; }\n\n.ax-btn--primary {\n background: color-mix(in oklch, var(--ax-accent) 15%, var(--ax-surface));\n color: var(--ax-accent);\n border-color: color-mix(in oklch, var(--ax-accent) 50%, var(--ax-border-2));\n font-weight: 700;\n box-shadow: var(--ax-shadow-accent);\n}\n.ax-btn--primary:hover:not(:disabled) {\n background: color-mix(in oklch, var(--ax-accent) 25%, var(--ax-surface));\n border-color: var(--ax-accent);\n}\n\n.ax-btn--ghost {\n background: transparent; color: var(--ax-muted);\n border-color: var(--ax-border); box-shadow: none;\n}\n.ax-btn--ghost:active:not(:disabled) { transform: none; }\n\n.ax-btn--danger {\n color: var(--ax-err);\n border-color: var(--ax-red-e);\n box-shadow: 0 4px 0 var(--ax-red-e);\n}\n.ax-btn--danger:hover:not(:disabled) {\n background: color-mix(in oklch, var(--ax-err) 12%, var(--ax-surface));\n border-color: var(--ax-err);\n}\n\n/* --- Chips (read-only labels used in kanban filters, etc.) --- */\n.ax-chip {\n display: inline-block; padding: 0 6px; font-family: var(--ax-mono);\n font-size: 11px; line-height: 18px; border-radius: var(--ax-radius-pill); color: var(--ax-text-2);\n border: var(--ax-border-w) solid var(--ax-border); background: var(--ax-surface); font-weight: 600;\n}\n\n/* --- Cards --- */\n.ax-card {\n background: var(--ax-surface); border: var(--ax-border-w) solid var(--ax-border);\n border-radius: var(--ax-radius-lg); padding: var(--ax-pad);\n box-shadow: var(--ax-shadow);\n}\n\n/* Step cards used by the setup wizard / onboarding-style flows. */\n.ax-step {\n background: var(--ax-surface); border: var(--ax-border-w) solid var(--ax-border);\n border-radius: var(--ax-radius); padding: 20px 22px; margin: 14px 0;\n}\n.ax-step__head {\n font-size: var(--ax-fs-xs); font-weight: 600; text-transform: uppercase;\n letter-spacing: 0.08em; color: var(--ax-muted);\n margin: 0 0 14px; display: flex; align-items: center; gap: 10px;\n}\n.ax-step__num {\n width: 22px; height: 22px; border-radius: var(--ax-radius-sm);\n background: color-mix(in oklch, var(--ax-accent) 15%, var(--ax-surface));\n border: 1px solid color-mix(in oklch, var(--ax-accent) 45%, var(--ax-border-2));\n color: var(--ax-accent);\n display: inline-flex; align-items: center; justify-content: center;\n font-family: var(--ax-mono); font-size: 11px; font-weight: 700;\n letter-spacing: 0; text-transform: none;\n}\n\n/* --- Stat strip (the \"COUNT / label\" cards across the top of Live) --- */\n.ax-statstrip {\n display: grid; grid-template-columns: repeat(auto-fit, minmax(170px, 1fr));\n gap: var(--ax-gap); padding: var(--ax-pad) var(--ax-pad) 0;\n}\n.ax-stat {\n background: var(--ax-surface); border: var(--ax-border-w) solid var(--ax-border);\n border-radius: var(--ax-radius); padding: 14px 16px;\n}\n.ax-stat__label {\n font-size: var(--ax-fs-xs); text-transform: uppercase; letter-spacing: 0.06em;\n color: var(--ax-muted); display: flex; align-items: center; gap: 6px;\n}\n.ax-stat__value {\n font-size: 24px; font-weight: 600; margin-top: 4px; letter-spacing: -0.02em;\n font-family: var(--ax-mono); font-variant-numeric: tabular-nums;\n}\n.ax-stat__sub {\n font-size: var(--ax-fs-xs); color: var(--ax-muted); margin-top: 2px;\n overflow: hidden; text-overflow: ellipsis; white-space: nowrap;\n}\n.ax-stat--live .ax-stat__value { color: var(--ax-accent); }\n.ax-stat--warn .ax-stat__value { color: var(--ax-warn); }\n.ax-stat--err .ax-stat__value { color: var(--ax-err); }\n\n/* --- Forms --- */\n.ax-field { margin: 10px 0 0; }\n.ax-field:first-child { margin-top: 0; }\n.ax-field label {\n display: block; margin-bottom: 5px;\n font-size: var(--ax-fs-xs); color: var(--ax-muted);\n text-transform: uppercase; letter-spacing: 0.06em; font-weight: 500;\n}\n.ax-field label .ax-hint {\n text-transform: none; letter-spacing: 0; color: var(--ax-muted);\n font-weight: 400; font-size: var(--ax-fs-xs); margin-left: 6px;\n}\n.ax-field input, .ax-field textarea, .ax-field select {\n width: 100%;\n background: var(--ax-bg); color: var(--ax-text);\n border: var(--ax-border-w) solid var(--ax-border-2); border-radius: var(--ax-radius);\n padding: 11px 14px; font: inherit; font-size: var(--ax-fs);\n transition: border-color 0.1s ease;\n}\n.ax-field input:focus, .ax-field textarea:focus, .ax-field select:focus {\n outline: none; border-color: var(--ax-accent);\n box-shadow: 0 0 0 2px color-mix(in oklch, var(--ax-accent) 18%, transparent);\n}\n.ax-field textarea { resize: vertical; min-height: 72px; font-family: var(--ax-font); }\n.ax-field input::placeholder, .ax-field textarea::placeholder {\n color: var(--ax-muted); opacity: 0.7;\n}\n\n.ax-row { display: flex; gap: 12px; }\n.ax-row > * { flex: 1; }\n\n/* --- Section titles shared across pages --- */\n.ax-section-title {\n display: flex; justify-content: space-between; align-items: center;\n margin-bottom: 12px;\n}\n.ax-section-title__label {\n font-size: var(--ax-fs-xs); text-transform: uppercase;\n letter-spacing: 0.08em; color: var(--ax-muted);\n}\n.ax-section-title__right {\n display: flex; align-items: center; gap: 8px;\n font-size: var(--ax-fs-xs); color: var(--ax-muted);\n}\n\n/* ============================================================\n * Settings-page design system (also reusable on other pages).\n * Ported from the Stitch redesign mockup so the primitives can\n * be consumed by any page — not just /admin. Sections:\n * page-head — kicker + h1 + lead paragraph\n * health-strip — 4-up status cards with coloured dot\n * wit — dismissable \"what is this?\" info banner\n * section-head — icon chip + title + lead + right-aligned CTA\n * row-card — list row with avatar + info + actions + expand\n * avatar — coloured-initial square (teal/coral/amber/blue)\n * pill — small status pill with coloured dot (chip-flavor)\n * connectors — 2-up grid of connector cards\n * tab-count — mono count bubble on a tab\n * toast — bottom-right ephemeral notification\n * modal — simple centred dialog + backdrop\n * sec-label — uppercase mini-heading with optional CTA\n * ============================================================ */\n\n/* --- Page head --- */\n.ax-page-head {\n max-width: 1040px; margin: 0 auto; padding: 28px 24px 12px;\n}\n.ax-page-head .ax-kicker {\n display: inline-block; font-size: 10px; letter-spacing: 0.1em;\n color: var(--ax-muted); text-transform: uppercase; margin-bottom: 6px;\n font-family: var(--ax-mono);\n}\n.ax-page-head h1 {\n font-size: 22px; font-weight: 600; letter-spacing: -0.015em;\n margin: 0 0 4px; display: flex; align-items: center; gap: 10px;\n}\n.ax-page-head .ax-lead {\n color: var(--ax-text-2); margin: 0; font-size: 13px;\n max-width: 640px; line-height: 1.6;\n}\n.ax-page-head .ax-lead code {\n font-family: var(--ax-mono); font-size: 11.5px;\n background: var(--ax-surface-2); padding: 1px 6px; border-radius: var(--ax-radius-sm);\n}\n\n/* --- Health strip --- */\n.ax-health-strip {\n max-width: 1040px; margin: 18px auto 22px; padding: 0 24px;\n display: grid; grid-template-columns: repeat(4, 1fr); gap: 10px;\n}\n.ax-health-card {\n background: var(--ax-surface); border: var(--ax-border-w) solid var(--ax-border);\n border-radius: var(--ax-radius-sm); padding: 12px 14px;\n display: flex; align-items: center; gap: 11px;\n transition: border-color 120ms;\n}\n.ax-health-card:hover { border-color: var(--ax-border-2); }\n.ax-health-card .ax-hc-dot {\n width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0;\n}\n.ax-health-card .ax-hc-dot--ok {\n background: var(--ax-accent);\n box-shadow: 0 0 8px color-mix(in oklch, var(--ax-accent) 50%, transparent);\n}\n.ax-health-card .ax-hc-dot--warn { background: var(--ax-warn); }\n.ax-health-card .ax-hc-dot--off { background: var(--ax-border-2); }\n.ax-health-card .ax-hc-txt { flex: 1; min-width: 0; }\n.ax-health-card .ax-hc-num {\n font-size: 17px; font-weight: 600; letter-spacing: -0.015em;\n}\n.ax-health-card .ax-hc-lbl {\n font-size: 11px; color: var(--ax-muted); margin-top: 1px;\n}\n\n/* --- \"What is this?\" info banner (dismissable) --- */\n.ax-wit {\n background: color-mix(in oklch, var(--ax-info) 6%, var(--ax-bg-elev));\n border: 1px solid color-mix(in oklch, var(--ax-info) 22%, var(--ax-border));\n border-radius: var(--ax-radius-sm); padding: 12px 14px; margin-bottom: 18px;\n font-size: 12.5px; color: var(--ax-text-2); line-height: 1.6;\n display: flex; gap: 10px; align-items: flex-start;\n}\n.ax-wit__icon {\n width: 18px; height: 18px; border-radius: 50%;\n background: color-mix(in oklch, var(--ax-info) 18%, transparent);\n color: var(--ax-info);\n display: grid; place-items: center;\n font-size: 11px; font-weight: 600; flex-shrink: 0; margin-top: 1px;\n}\n.ax-wit b { color: var(--ax-text); font-weight: 600; }\n.ax-wit code {\n font-family: var(--ax-mono); font-size: 11.5px;\n background: var(--ax-bg); padding: 1px 5px; border-radius: var(--ax-radius-sm);\n border: var(--ax-border-w) solid var(--ax-border);\n}\n.ax-wit .ax-wit__dismiss {\n background: transparent; border: 0; color: var(--ax-muted);\n font-size: 16px; cursor: pointer; margin-left: auto;\n padding: 0 4px; line-height: 1;\n}\n.ax-wit .ax-wit__dismiss:hover { color: var(--ax-text); }\n\n/* --- Section head (icon + title + lead + right CTA) --- */\n.ax-section-head {\n display: flex; align-items: flex-start; gap: 16px; margin-bottom: 18px;\n}\n.ax-section-head__icon {\n width: 44px; height: 44px; border-radius: var(--ax-radius);\n background: color-mix(in oklch, var(--ax-accent) 10%, var(--ax-surface));\n border: 1px solid color-mix(in oklch, var(--ax-accent) 25%, var(--ax-border));\n display: grid; place-items: center; flex-shrink: 0;\n color: var(--ax-accent);\n}\n.ax-section-head__icon svg { width: 22px; height: 22px; }\n.ax-section-head__text { flex: 1; }\n.ax-section-head h2 {\n font-size: 18px; margin: 0 0 3px;\n font-weight: 600; letter-spacing: -0.01em;\n}\n.ax-section-head .ax-lead {\n color: var(--ax-text-2); margin: 0; font-size: 13px; line-height: 1.55;\n max-width: 640px;\n}\n\n/* --- Row cards (collapsible list items) --- */\n.ax-stack { display: flex; flex-direction: column; gap: 10px; }\n.ax-row-card {\n background: var(--ax-surface); border: var(--ax-border-w) solid var(--ax-border);\n border-radius: var(--ax-radius-lg); padding: 14px 16px;\n transition: border-color 160ms, transform 160ms;\n}\n.ax-row-card:hover { border-color: var(--ax-border-2); }\n.ax-row-card__top { display: flex; align-items: center; gap: 12px; }\n.ax-row-card__info { flex: 1; min-width: 0; }\n.ax-row-card__info .ax-name {\n font-size: 14px; font-weight: 600; letter-spacing: -0.005em;\n display: flex; align-items: center; gap: 8px;\n}\n.ax-row-card__info .ax-slug {\n font-family: var(--ax-mono); font-size: 11px;\n color: var(--ax-muted); margin-left: 4px;\n}\n.ax-row-card__info .ax-sub {\n font-size: 12px; color: var(--ax-text-2); margin-top: 3px;\n line-height: 1.5; display: flex; align-items: center; gap: 8px; flex-wrap: wrap;\n}\n.ax-row-card__actions { display: flex; gap: 6px; align-items: center; }\n.ax-row-card__details {\n display: none; margin-top: 14px; padding-top: 14px;\n border-top: 1px dashed var(--ax-border);\n font-size: 12.5px; color: var(--ax-text-2); line-height: 1.6;\n}\n.ax-row-card.is-open .ax-row-card__details { display: block; }\n.ax-row-card.is-open .ax-chev { transform: rotate(180deg); }\n.ax-chev { transition: transform 160ms; }\n\n.ax-detail-grid {\n display: grid; grid-template-columns: repeat(2, 1fr);\n gap: 12px 20px; margin-bottom: 12px;\n}\n.ax-detail-grid dt {\n font-size: 10px; letter-spacing: 0.08em; text-transform: uppercase;\n color: var(--ax-muted); margin-bottom: 3px; font-family: var(--ax-mono);\n}\n.ax-detail-grid dd {\n margin: 0; font-size: 12.5px; color: var(--ax-text);\n}\n.ax-triggers { display: flex; gap: 4px; flex-wrap: wrap; }\n.ax-trigger-pill {\n font-family: var(--ax-mono); font-size: 11px;\n padding: 2px 8px; background: var(--ax-bg);\n border: var(--ax-border-w) solid var(--ax-border); border-radius: var(--ax-radius-sm);\n color: var(--ax-text);\n}\n\n/* --- Avatars (coloured initial square) --- */\n.ax-avatar {\n width: 36px; height: 36px; border-radius: var(--ax-radius); flex-shrink: 0;\n background: var(--ax-surface-2); border: var(--ax-border-w) solid var(--ax-border-2);\n display: grid; place-items: center;\n font-weight: 600; font-size: 14px; letter-spacing: -0.01em;\n color: var(--ax-text);\n}\n.ax-avatar--teal {\n background: color-mix(in oklch, var(--ax-accent) 18%, var(--ax-surface));\n border-color: color-mix(in oklch, var(--ax-accent) 35%, var(--ax-border));\n color: var(--ax-accent);\n}\n.ax-avatar--coral {\n background: color-mix(in oklch, var(--ax-err) 14%, var(--ax-surface));\n border-color: color-mix(in oklch, var(--ax-err) 30%, var(--ax-border));\n color: var(--ax-err);\n}\n.ax-avatar--amber {\n background: color-mix(in oklch, var(--ax-warn) 14%, var(--ax-surface));\n border-color: color-mix(in oklch, var(--ax-warn) 30%, var(--ax-border));\n color: var(--ax-warn);\n}\n.ax-avatar--blue {\n background: color-mix(in oklch, var(--ax-info) 14%, var(--ax-surface));\n border-color: color-mix(in oklch, var(--ax-info) 30%, var(--ax-border));\n color: var(--ax-info);\n}\n\n/* --- Pills (status indicator with coloured dot) ---\n *\n * Different from .ax-badge: pills have a rounded (10px) shape, a small\n * leading coloured dot, and stronger semantics (ok/warn/off/info). Use\n * for \"live\", \"idle\", \"receiving\", \"stale\" states next to a name.\n */\n.ax-pill {\n display: inline-flex; align-items: center; gap: 5px;\n font-size: 11px; padding: 2px 8px; line-height: 16px;\n border-radius: var(--ax-radius); background: var(--ax-surface-2);\n color: var(--ax-text-2); border: var(--ax-border-w) solid var(--ax-border);\n white-space: nowrap;\n}\n.ax-pill .ax-pill__dot {\n width: 6px; height: 6px; border-radius: 50%; background: currentColor;\n}\n.ax-pill--ok {\n background: color-mix(in oklch, var(--ax-accent) 14%, transparent);\n color: var(--ax-accent);\n border-color: color-mix(in oklch, var(--ax-accent) 30%, transparent);\n}\n.ax-pill--warn {\n background: color-mix(in oklch, var(--ax-warn) 14%, transparent);\n color: var(--ax-warn);\n border-color: color-mix(in oklch, var(--ax-warn) 30%, transparent);\n}\n.ax-pill--off { color: var(--ax-muted); }\n.ax-pill--info {\n background: color-mix(in oklch, var(--ax-info) 14%, transparent);\n color: var(--ax-info);\n border-color: color-mix(in oklch, var(--ax-info) 30%, transparent);\n}\n\n/* --- Connectors grid (Channels tab) --- */\n.ax-connectors {\n display: grid; grid-template-columns: repeat(2, 1fr); gap: 12px;\n}\n.ax-connector {\n background: var(--ax-surface); border: var(--ax-border-w) solid var(--ax-border);\n border-radius: var(--ax-radius-lg); padding: 18px 18px 16px;\n cursor: pointer; position: relative;\n transition: border-color 160ms, transform 160ms, background 160ms;\n}\n.ax-connector:hover { border-color: var(--ax-border-2); background: var(--ax-surface-2); }\n.ax-connector.is-on {\n border-color: color-mix(in oklch, var(--ax-accent) 40%, var(--ax-border));\n}\n.ax-connector.is-active {\n border-color: var(--ax-accent);\n background: color-mix(in oklch, var(--ax-accent) 8%, var(--ax-surface));\n}\n.ax-connector__top {\n display: flex; align-items: center; gap: 12px; margin-bottom: 10px;\n}\n.ax-connector__logo {\n width: 40px; height: 40px; border-radius: var(--ax-radius);\n display: grid; place-items: center;\n font-weight: 700; font-size: 15px; letter-spacing: -0.02em; flex-shrink: 0;\n background: var(--ax-surface-2); border: var(--ax-border-w) solid var(--ax-border-2);\n color: var(--ax-text); font-family: var(--ax-mono);\n}\n.ax-connector__meta { flex: 1; }\n.ax-connector__name {\n font-weight: 600; font-size: 14px; letter-spacing: -0.005em;\n}\n.ax-connector__sub {\n font-size: 11.5px; color: var(--ax-muted); margin-top: 2px;\n}\n.ax-connector__status { margin-left: auto; }\n.ax-connector__desc {\n font-size: 12.5px; color: var(--ax-text-2); line-height: 1.5; min-height: 36px;\n}\n.ax-connector__foot {\n margin-top: 12px; display: flex; align-items: center; justify-content: space-between;\n font-size: 11.5px; color: var(--ax-muted);\n}\n.ax-connector__cta {\n color: var(--ax-accent); font-weight: 500;\n display: inline-flex; align-items: center; gap: 4px;\n}\n.ax-connector__cta svg { width: 12px; height: 12px; }\n\n/* --- Tab counts + warn indicator --- */\n.ax-tab-count {\n font-family: var(--ax-mono); font-size: 10px;\n padding: 1px 6px; background: var(--ax-surface-2);\n color: var(--ax-muted); border-radius: var(--ax-radius); line-height: 14px;\n margin-left: 6px;\n}\n.is-active .ax-tab-count {\n background: color-mix(in oklch, var(--ax-accent) 18%, transparent);\n color: var(--ax-accent);\n}\n.ax-tab-warn {\n width: 6px; height: 6px; border-radius: 50%; background: var(--ax-warn);\n display: inline-block; margin-left: 6px;\n}\n\n/* --- Section mini-labels --- */\n.ax-sec-label {\n display: flex; align-items: center; justify-content: space-between;\n margin: 22px 0 10px;\n}\n.ax-sec-label h3 {\n font-size: 12px; font-weight: 600; letter-spacing: 0.06em;\n text-transform: uppercase; color: var(--ax-muted);\n margin: 0; font-family: var(--ax-mono);\n}\n\n/* --- Toast --- */\n.ax-toast {\n position: fixed; bottom: 24px; right: 24px;\n background: var(--ax-surface-2);\n border: var(--ax-border-w) solid var(--ax-accent); color: var(--ax-text);\n padding: 10px 16px; border-radius: var(--ax-radius); font-size: 12.5px;\n display: flex; align-items: center; gap: 10px;\n box-shadow: 0 10px 30px rgba(0,0,0,0.3);\n opacity: 0; transform: translateY(10px); pointer-events: none;\n transition: opacity 200ms, transform 200ms; z-index: 100;\n}\n.ax-toast.is-show { opacity: 1; transform: none; }\n.ax-toast__icon { color: var(--ax-accent); }\n\n/* --- Modal (centred dialog + backdrop) --- */\n.ax-modal-bd {\n position: fixed; inset: 0;\n background: color-mix(in oklch, var(--ax-bg) 60%, black);\n display: none; align-items: center; justify-content: center;\n z-index: 200; padding: 24px;\n}\n.ax-modal-bd.is-show { display: flex; }\n.ax-modal {\n width: min(560px, 94vw); max-height: 86vh; overflow: auto;\n background: var(--ax-surface); border: var(--ax-border-w) solid var(--ax-border-2);\n border-radius: var(--ax-radius-lg); padding: 0;\n box-shadow: 0 20px 60px rgba(0,0,0,0.5);\n}\n.ax-modal > header {\n padding: 18px 22px 10px; border-bottom: var(--ax-border-w) solid var(--ax-border);\n display: flex; align-items: center; gap: 10px;\n}\n.ax-modal > header h3 {\n margin: 0; font-size: 16px; font-weight: 600; letter-spacing: -0.01em; flex: 1;\n}\n.ax-modal > header .ax-modal__close {\n background: transparent; border: 0; color: var(--ax-muted);\n font-size: 22px; cursor: pointer; line-height: 1; padding: 0 6px;\n}\n.ax-modal__body { padding: 18px 22px; }\n.ax-modal__foot {\n padding: 12px 22px 18px;\n display: flex; justify-content: flex-end; gap: 8px;\n border-top: var(--ax-border-w) solid var(--ax-border);\n}\n\n/* --- Mesh hero (enable toggle + SVG network viz) --- */\n.ax-mesh-hero {\n background: var(--ax-surface); border: var(--ax-border-w) solid var(--ax-border);\n border-radius: var(--ax-radius-lg); padding: 20px;\n display: grid; grid-template-columns: 1fr 240px; gap: 24px;\n align-items: center; margin-bottom: 18px;\n}\n.ax-mesh-hero h3 { margin: 0 0 4px; font-size: 15px; font-weight: 600; }\n.ax-mesh-hero p { margin: 0 0 12px; font-size: 12.5px; color: var(--ax-text-2); line-height: 1.5; }\n.ax-mesh-viz { position: relative; width: 240px; height: 140px; }\n.ax-mesh-dot {\n position: absolute; width: 44px; height: 44px; border-radius: 50%;\n background: var(--ax-surface-2); border: 2px solid var(--ax-border-2);\n display: grid; place-items: center;\n font-family: var(--ax-mono); font-size: 10px; font-weight: 600;\n color: var(--ax-text-2);\n}\n.ax-mesh-dot.self {\n background: color-mix(in oklch, var(--ax-accent) 18%, var(--ax-surface));\n border-color: var(--ax-accent); color: var(--ax-accent);\n}\n.ax-mesh-dot.empty {\n border-style: dashed; color: var(--ax-muted); font-size: 14px;\n}\n.ax-mesh-wires { position: absolute; inset: 0; }\n.ax-mesh-wires line { stroke: var(--ax-border-2); stroke-width: 1.5; stroke-dasharray: 3 3; }\n.ax-mesh-wires line.live { stroke: var(--ax-accent); stroke-dasharray: 0; opacity: 0.6; }\n\n/* Pill-shaped toggle (used for the mesh enable switch + similar binary actions). */\n.ax-mesh-toggle {\n display: inline-flex; align-items: center; gap: 10px;\n background: var(--ax-bg-elev); border: var(--ax-border-w) solid var(--ax-border);\n border-radius: 20px; padding: 4px 14px 4px 5px;\n cursor: pointer; user-select: none; font-size: 12px;\n}\n.ax-mesh-switch {\n width: 32px; height: 18px; background: var(--ax-surface-3);\n border-radius: var(--ax-radius); position: relative; transition: background 160ms;\n}\n.ax-mesh-switch::after {\n content: \"\"; position: absolute; top: 2px; left: 2px;\n width: 14px; height: 14px; border-radius: 50%;\n background: var(--ax-text-2); transition: all 160ms;\n}\n.ax-mesh-toggle.is-on .ax-mesh-switch {\n background: color-mix(in oklch, var(--ax-accent) 80%, transparent);\n}\n.ax-mesh-toggle.is-on .ax-mesh-switch::after {\n left: 16px; background: var(--ax-bg);\n}\n\n/* --- Schedule builder: sentence-builder + pill picks + day picker --- */\n.ax-builder {\n background: var(--ax-surface); border: var(--ax-border-w) solid var(--ax-border);\n border-radius: var(--ax-radius-lg); padding: 20px;\n}\n.ax-builder h3 {\n margin: 0 0 4px; font-size: 15px; font-weight: 600; letter-spacing: -0.005em;\n}\n.ax-builder__hint { margin: 0 0 16px; font-size: 12.5px; color: var(--ax-muted); }\n.ax-builder__lbl {\n display: block; font-size: 11px; color: var(--ax-muted);\n letter-spacing: 0.03em; text-transform: uppercase; margin: 12px 0 5px;\n font-weight: 500;\n}\n.ax-builder__lbl .opt {\n text-transform: none; letter-spacing: 0; font-weight: 400;\n opacity: 0.75; margin-left: 6px;\n}\n.ax-builder__inp {\n width: 100%; background: var(--ax-bg); color: var(--ax-text);\n border: var(--ax-border-w) solid var(--ax-border); border-radius: var(--ax-radius-sm);\n padding: 8px 11px; font: inherit; font-size: 13px;\n transition: border-color 120ms, box-shadow 120ms;\n}\n.ax-builder__inp:focus {\n outline: none; border-color: var(--ax-accent);\n box-shadow: 0 0 0 3px color-mix(in oklch, var(--ax-accent) 18%, transparent);\n}\ntextarea.ax-builder__inp { min-height: 80px; line-height: 1.5; resize: vertical; }\n\n.ax-mode-switch {\n display: inline-flex; background: var(--ax-surface-2);\n border: var(--ax-border-w) solid var(--ax-border); border-radius: var(--ax-radius); padding: 3px;\n margin-bottom: 14px;\n}\n.ax-mode-switch button {\n background: transparent; border: 0; color: var(--ax-muted);\n padding: 6px 14px; font: inherit; font-size: 12px; cursor: pointer;\n border-radius: var(--ax-radius-sm); font-weight: 500;\n}\n.ax-mode-switch button.is-active {\n background: var(--ax-surface); color: var(--ax-text);\n}\n\n.ax-sentence {\n display: flex; align-items: center; gap: 8px;\n font-size: 16px; line-height: 1.9; flex-wrap: wrap;\n margin-bottom: 14px; padding: 14px 16px;\n background: var(--ax-bg-elev); border: var(--ax-border-w) solid var(--ax-border);\n border-radius: var(--ax-radius-sm);\n}\n.ax-sentence > span { color: var(--ax-text-2); }\n.ax-pill-pick {\n background: var(--ax-surface); border: var(--ax-border-w) solid var(--ax-border-2);\n color: var(--ax-accent); padding: 3px 10px; border-radius: var(--ax-radius);\n font-size: 14px; font-family: inherit; cursor: pointer; font-weight: 500;\n}\n.ax-pill-pick:hover { background: var(--ax-surface-2); border-color: var(--ax-accent); }\n.ax-pill-pick--sm { font-size: 13px; padding: 3px 8px; }\n\n.ax-day-picker { display: flex; gap: 4px; }\n.ax-day-picker button {\n width: 30px; height: 30px; border-radius: var(--ax-radius);\n background: var(--ax-surface); border: var(--ax-border-w) solid var(--ax-border);\n color: var(--ax-muted); font: inherit; font-size: 11px; font-weight: 500;\n cursor: pointer;\n}\n.ax-day-picker button:hover { border-color: var(--ax-border-2); color: var(--ax-text); }\n.ax-day-picker button.is-active {\n background: color-mix(in oklch, var(--ax-accent) 20%, var(--ax-surface));\n border-color: var(--ax-accent); color: var(--ax-accent);\n}\n\n.ax-preview-box {\n background: color-mix(in oklch, var(--ax-accent) 5%, var(--ax-bg-elev));\n border: 1px solid color-mix(in oklch, var(--ax-accent) 20%, var(--ax-border));\n border-radius: var(--ax-radius-sm); padding: 12px 14px; margin-top: 12px;\n font-size: 12.5px; color: var(--ax-text-2);\n display: flex; align-items: center; gap: 10px;\n}\n.ax-preview-box svg { width: 15px; height: 15px; color: var(--ax-accent); flex-shrink: 0; }\n.ax-preview-box b { color: var(--ax-text); font-weight: 600; }\n\n.ax-cron-hints {\n display: grid; grid-template-columns: repeat(2, 1fr); gap: 6px 18px;\n margin-top: 10px; font-size: 11.5px; color: var(--ax-muted);\n}\n.ax-cron-hints code {\n font-family: var(--ax-mono); color: var(--ax-text);\n background: var(--ax-bg); border: var(--ax-border-w) solid var(--ax-border);\n padding: 1px 6px; border-radius: var(--ax-radius-sm); margin-right: 6px;\n}\n\n/* --- JSON tree viewer (Advanced tab) --- */\n.ax-jv-toolbar {\n display: flex; align-items: center; gap: 8px;\n padding: 8px 12px; background: var(--ax-bg-elev);\n border: var(--ax-border-w) solid var(--ax-border); border-bottom: 0;\n border-radius: var(--ax-radius) var(--ax-radius) 0 0;\n}\n.ax-jv-toolbar .ax-jv-spacer { flex: 1; }\n.ax-jv-toolbar .ax-jv-seg {\n display: inline-flex; background: var(--ax-surface);\n border: var(--ax-border-w) solid var(--ax-border); border-radius: var(--ax-radius-sm); padding: 2px;\n}\n.ax-jv-toolbar .ax-jv-seg button {\n background: transparent; border: 0; color: var(--ax-muted);\n padding: 4px 10px; font: inherit; font-size: 11px;\n cursor: pointer; border-radius: var(--ax-radius-sm);\n}\n.ax-jv-toolbar .ax-jv-seg button.is-active { background: var(--ax-surface-2); color: var(--ax-text); }\n.ax-jv-search {\n background: var(--ax-surface); border: var(--ax-border-w) solid var(--ax-border);\n border-radius: var(--ax-radius-sm); padding: 4px 9px; font: inherit; font-size: 12px;\n color: var(--ax-text); width: 180px;\n}\n.ax-jv-search:focus { outline: none; border-color: var(--ax-accent); }\n\n.ax-jv-viewer {\n background: var(--ax-bg-elev); border: var(--ax-border-w) solid var(--ax-border);\n border-top: 0; border-radius: 0 0 var(--ax-radius) var(--ax-radius);\n font-family: var(--ax-mono); font-size: 12.5px; line-height: 1.75;\n padding: 14px 18px; max-height: 620px; overflow: auto;\n}\n.ax-jv-line {\n display: flex; align-items: flex-start; padding: 1px 0; gap: 0;\n position: relative; border-radius: var(--ax-radius-sm); white-space: nowrap;\n}\n.ax-jv-line:hover { background: color-mix(in oklch, var(--ax-accent) 5%, transparent); }\n.ax-jv-line.is-match { background: color-mix(in oklch, var(--ax-warn) 16%, transparent); }\n.ax-jv-toggle {\n width: 12px; display: inline-block; color: var(--ax-muted);\n cursor: pointer; user-select: none; text-align: center;\n font-size: 9px; line-height: 1.75;\n}\n.ax-jv-toggle.empty { cursor: default; }\n.ax-jv-key { color: var(--ax-info); margin-right: 0; }\n.ax-jv-punc { color: var(--ax-muted); }\n.ax-jv-string { color: var(--ax-accent); }\n.ax-jv-number { color: var(--ax-warn); }\n.ax-jv-bool { color: var(--ax-info); font-weight: 500; }\n.ax-jv-null { color: var(--ax-muted); font-style: italic; }\n.ax-jv-comment {\n color: var(--ax-muted); font-family: var(--ax-font);\n font-style: italic; font-size: 11.5px; margin-left: 8px; opacity: 0.8;\n}\n.ax-jv-children {\n padding-left: 18px;\n border-left: 1px dashed color-mix(in oklch, var(--ax-border) 80%, transparent);\n margin-left: 4px;\n}\n.ax-jv-children.is-collapsed { display: none; }\n.ax-jv-collapsed-note {\n color: var(--ax-muted); font-size: 11px; font-style: italic; margin-left: 4px;\n}\n.ax-jv-empty-brace { color: var(--ax-muted); }\n\n/* ============================================================\n * Detail-page primitives (Agent manage, and any future\n * detail-view). Built around a 3-col layout:\n *\n * [rail 200px] [main] [test-drive 360px]\n *\n * Scales down: test-drive hides at 1180px, rail hides at 780px.\n * ============================================================ */\n\n/* Crumb */\n.ax-crumb {\n max-width: 1400px; margin: 0 auto; padding: 16px 24px 0;\n font-size: 12px; color: var(--ax-muted);\n display: flex; align-items: center; gap: 6px;\n}\n.ax-crumb a { color: var(--ax-muted); }\n.ax-crumb a:hover { color: var(--ax-accent); text-decoration: none; }\n.ax-crumb .ax-crumb__sep { color: var(--ax-border-2); }\n.ax-crumb .ax-crumb__cur { color: var(--ax-text); }\n\n/* Hero — large avatar + editable name + chip row + actions */\n.ax-hero {\n max-width: 1400px; margin: 0 auto; padding: 14px 24px 18px;\n display: flex; align-items: center; gap: 18px; flex-wrap: wrap;\n}\n.ax-hero__ava {\n width: 64px; height: 64px; border-radius: 14px;\n background: color-mix(in oklch, var(--ax-accent) 20%, var(--ax-surface));\n border: 1px solid color-mix(in oklch, var(--ax-accent) 40%, var(--ax-border));\n display: grid; place-items: center;\n font-weight: 700; font-size: 22px; letter-spacing: -0.02em;\n color: var(--ax-accent); flex-shrink: 0; position: relative;\n}\n.ax-hero__on {\n position: absolute; bottom: -2px; right: -2px;\n width: 16px; height: 16px; border-radius: 50%;\n background: var(--ax-accent); border: 3px solid var(--ax-bg);\n}\n.ax-hero__title { flex: 1; min-width: 260px; }\n.ax-hero h1 {\n margin: 0 0 3px; font-size: 22px; font-weight: 600;\n letter-spacing: -0.02em; display: flex; align-items: center; gap: 10px;\n}\n.ax-hero h1 input {\n background: transparent; border: 0; color: var(--ax-text);\n font: inherit; padding: 0; border-bottom: 1px dashed transparent;\n transition: border-color 120ms; max-width: 260px;\n}\n.ax-hero h1 input:hover { border-color: var(--ax-border-2); }\n.ax-hero h1 input:focus { outline: none; border-color: var(--ax-accent); }\n.ax-hero__sub {\n color: var(--ax-text-2); font-size: 13px; margin-top: 2px;\n display: flex; align-items: center; gap: 12px; flex-wrap: wrap;\n}\n.ax-hero__sub .ax-hero__dot {\n width: 3px; height: 3px; background: var(--ax-border-2); border-radius: 50%;\n}\n.ax-hero__actions { display: flex; gap: 8px; align-items: center; }\n\n/* Inline stats strip (smaller than the settings health strip — fits in\n * the detail page hero below the hero title). */\n.ax-stats-inline {\n max-width: 1400px; margin: 0 auto; padding: 0 24px;\n display: grid; grid-template-columns: repeat(4, 1fr); gap: 10px;\n}\n.ax-stat-inline {\n background: var(--ax-surface); border: var(--ax-border-w) solid var(--ax-border);\n border-radius: var(--ax-radius-sm); padding: 10px 12px;\n display: flex; align-items: center; gap: 11px;\n}\n.ax-stat-inline__icon {\n width: 26px; height: 26px; border-radius: var(--ax-radius);\n background: color-mix(in oklch, var(--ax-accent) 12%, var(--ax-surface-2));\n display: grid; place-items: center;\n color: var(--ax-accent); flex-shrink: 0;\n}\n.ax-stat-inline__icon svg { width: 14px; height: 14px; }\n.ax-stat-inline__v {\n font-size: 15px; font-weight: 600; letter-spacing: -0.01em; line-height: 1.1;\n}\n.ax-stat-inline__l {\n font-size: 11px; color: var(--ax-muted); margin-top: 1px;\n}\n\n/* Detail layout — 2-col rail + main. Overlays (test-drive, history, etc.)\n * dock separately via fixed positioning. */\n.ax-layout {\n max-width: 1400px; margin: 22px auto 0; padding: 0 24px 60px;\n display: grid; grid-template-columns: 200px 1fr;\n gap: 22px; align-items: flex-start;\n}\n\n/* Left rail — sticky nav list with grouped items */\n.ax-rail {\n position: sticky; top: 72px;\n display: flex; flex-direction: column; gap: 1px;\n}\n.ax-rail a {\n display: flex; align-items: center; gap: 10px;\n padding: 9px 11px; border-radius: var(--ax-radius);\n color: var(--ax-text-2); font-size: 13px; font-weight: 500;\n cursor: pointer; position: relative;\n transition: background 120ms, color 120ms;\n}\n.ax-rail a svg {\n width: 15px; height: 15px; flex-shrink: 0; color: var(--ax-muted);\n}\n.ax-rail a:hover {\n background: var(--ax-surface); color: var(--ax-text); text-decoration: none;\n}\n.ax-rail a:hover svg { color: var(--ax-text); }\n.ax-rail a.is-active {\n background: color-mix(in oklch, var(--ax-accent) 12%, var(--ax-surface));\n color: var(--ax-accent);\n}\n.ax-rail a.is-active svg { color: var(--ax-accent); }\n.ax-rail a.is-active::before {\n content: \"\"; position: absolute; left: -3px; top: 7px; bottom: 7px;\n width: 2px; border-radius: var(--ax-radius-sm); background: var(--ax-accent);\n}\n.ax-rail__count {\n margin-left: auto; font-family: var(--ax-mono);\n font-size: 10px; padding: 1px 6px; background: var(--ax-surface-2);\n color: var(--ax-muted); border-radius: var(--ax-radius); line-height: 14px;\n}\n.ax-rail a.is-active .ax-rail__count {\n background: color-mix(in oklch, var(--ax-accent) 22%, transparent);\n color: var(--ax-accent);\n}\n.ax-rail__sep { height: 1px; background: var(--ax-border); margin: 8px 4px; }\n.ax-rail__label {\n font-size: 10px; letter-spacing: 0.1em; text-transform: uppercase;\n color: var(--ax-muted); padding: 6px 11px 4px; font-family: var(--ax-mono);\n}\n\n/* Main panel containers (one per section) */\n.ax-panel {\n background: var(--ax-surface); border: var(--ax-border-w) solid var(--ax-border);\n border-radius: var(--ax-radius-lg); padding: 22px;\n}\n.ax-panel + .ax-panel { margin-top: 12px; }\n.ax-panel h2 {\n margin: 0 0 4px; font-size: 16px; font-weight: 600; letter-spacing: -0.01em;\n}\n.ax-panel .ax-lead {\n margin: 0 0 16px; font-size: 12.5px; color: var(--ax-text-2); line-height: 1.55;\n}\n.ax-panel__head {\n display: flex; align-items: flex-start; justify-content: space-between;\n gap: 12px; margin-bottom: 14px;\n}\n.ax-panel__head h2 { margin: 0 0 3px; }\n.ax-panel__tab { display: none; animation: ax-fade 180ms ease; }\n.ax-panel__tab.is-active { display: block; }\n@keyframes ax-fade {\n from { opacity: 0; transform: translateY(4px); }\n to { opacity: 1; transform: none; }\n}\n\n/* Grids for form rows */\n.ax-grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }\n.ax-grid-3 { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 12px; }\n\n/* Slider-style setting row — label + hint + segmented options */\n.ax-slider-row {\n display: grid; grid-template-columns: 140px 1fr auto;\n gap: 12px; align-items: center;\n padding: 10px 0; border-bottom: 1px dashed var(--ax-border);\n}\n.ax-slider-row:last-child { border-bottom: 0; }\n.ax-slider-row label { font-size: 12.5px; color: var(--ax-text); margin: 0; }\n.ax-slider-row .ax-hint {\n margin: 0; color: var(--ax-muted); font-size: 11.5px;\n}\n.ax-slider-row .ax-opts {\n display: flex; gap: 4px; background: var(--ax-bg);\n border: var(--ax-border-w) solid var(--ax-border); border-radius: var(--ax-radius-sm); padding: 2px;\n}\n.ax-slider-row .ax-opts button {\n background: transparent; border: 0; color: var(--ax-muted);\n padding: 5px 11px; font: inherit; font-size: 11.5px;\n cursor: pointer; border-radius: var(--ax-radius-sm); white-space: nowrap;\n}\n.ax-slider-row .ax-opts button.is-active {\n background: var(--ax-surface-2); color: var(--ax-text);\n}\n\n/* Sub-tabs (Guided/Freeform, CLAUDE.md/SOUL.md/IDENTITY.md) */\n.ax-subtabs {\n display: inline-flex; background: var(--ax-bg);\n border: var(--ax-border-w) solid var(--ax-border); border-radius: var(--ax-radius);\n padding: 3px; margin-bottom: 16px;\n}\n.ax-subtabs button {\n background: transparent; border: 0; color: var(--ax-muted);\n padding: 6px 14px; font: inherit; font-size: 12px;\n cursor: pointer; border-radius: var(--ax-radius-sm); font-weight: 500;\n}\n.ax-subtabs button.is-active {\n background: var(--ax-surface-2); color: var(--ax-text);\n}\n\n/* Trigger-word chips with inline input (for Wake words field) */\n.ax-triggers-edit {\n display: flex; flex-wrap: wrap; gap: 6px;\n background: var(--ax-bg); border: var(--ax-border-w) solid var(--ax-border);\n padding: 7px; border-radius: var(--ax-radius-sm); min-height: 38px;\n align-items: center; cursor: text;\n}\n.ax-triggers-edit:focus-within {\n border-color: var(--ax-accent);\n box-shadow: 0 0 0 3px color-mix(in oklch, var(--ax-accent) 18%, transparent);\n}\n.ax-trig-pill {\n display: inline-flex; align-items: center; gap: 6px;\n font-family: var(--ax-mono); font-size: 11px;\n padding: 3px 4px 3px 9px;\n background: color-mix(in oklch, var(--ax-accent) 16%, var(--ax-surface));\n border: 1px solid color-mix(in oklch, var(--ax-accent) 30%, var(--ax-border));\n border-radius: var(--ax-radius-sm); color: var(--ax-accent);\n}\n.ax-trig-pill button {\n background: transparent; border: 0; color: currentColor;\n opacity: 0.6; cursor: pointer; font-size: 13px; line-height: 1;\n padding: 0 2px; border-radius: var(--ax-radius-sm);\n}\n.ax-trig-pill button:hover {\n opacity: 1;\n background: color-mix(in oklch, var(--ax-accent) 20%, transparent);\n}\n.ax-triggers-edit input {\n flex: 1; min-width: 120px;\n background: transparent; border: 0; color: var(--ax-text);\n font: inherit; font-size: 12.5px;\n padding: 4px 6px; font-family: var(--ax-mono);\n}\n.ax-triggers-edit input:focus { outline: none; }\n\n/* Prompt editor (freeform CLAUDE.md) */\n.ax-prompt-edit { position: relative; }\n.ax-prompt-edit textarea { min-height: 220px; line-height: 1.6; }\n.ax-prompt-badges {\n position: absolute; top: 8px; right: 8px; display: flex; gap: 6px;\n}\n.ax-prompt-badges .ax-pbadge {\n font-size: 10px; padding: 3px 7px; border-radius: var(--ax-radius-sm);\n background: var(--ax-surface-2); color: var(--ax-muted);\n font-family: var(--ax-mono); letter-spacing: 0.03em;\n}\n\n/* Skill gallery */\n.ax-skill-toolbar {\n display: flex; gap: 10px; align-items: center;\n margin-bottom: 14px; flex-wrap: wrap;\n}\n.ax-search {\n position: relative; flex: 1; min-width: 220px;\n}\n.ax-search svg {\n position: absolute; left: 10px; top: 50%; transform: translateY(-50%);\n width: 13px; height: 13px; color: var(--ax-muted); pointer-events: none;\n}\n.ax-search input { width: 100%; padding-left: 30px; }\n.ax-skill-filter {\n display: flex; gap: 4px; background: var(--ax-bg);\n border: var(--ax-border-w) solid var(--ax-border); border-radius: var(--ax-radius-sm); padding: 2px;\n}\n.ax-skill-filter button {\n background: transparent; border: 0; color: var(--ax-muted);\n padding: 5px 10px; font: inherit; font-size: 11.5px;\n cursor: pointer; border-radius: var(--ax-radius-sm);\n}\n.ax-skill-filter button.is-active {\n background: var(--ax-surface-2); color: var(--ax-text);\n}\n.ax-skill-grid {\n display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));\n gap: 10px;\n}\n.ax-skill-card {\n background: var(--ax-bg-elev); border: var(--ax-border-w) solid var(--ax-border);\n border-radius: var(--ax-radius-lg); padding: 14px;\n display: flex; flex-direction: column; gap: 8px;\n cursor: pointer; position: relative; overflow: hidden;\n transition: border-color 160ms, transform 160ms, background 160ms;\n}\n.ax-skill-card:hover { border-color: var(--ax-border-2); transform: translateY(-1px); }\n.ax-skill-card.is-on {\n border-color: color-mix(in oklch, var(--ax-accent) 40%, var(--ax-border));\n background: color-mix(in oklch, var(--ax-accent) 5%, var(--ax-bg-elev));\n}\n.ax-skill-card.is-on::after {\n content: \"\"; position: absolute; top: 0; right: 0;\n width: 0; height: 0;\n border-left: 26px solid transparent;\n border-top: 26px solid var(--ax-accent);\n}\n.ax-skill-card.is-on::before {\n content: \"\"; position: absolute; top: 5px; right: 3px;\n width: 7px; height: 4px;\n border-left: 2px solid var(--ax-bg);\n border-bottom: 2px solid var(--ax-bg);\n transform: rotate(-45deg); z-index: 2;\n}\n.ax-skill-card__top { display: flex; align-items: center; gap: 10px; }\n.ax-skill-card__icon {\n width: 32px; height: 32px; border-radius: var(--ax-radius);\n background: var(--ax-surface-2);\n display: grid; place-items: center; flex-shrink: 0;\n color: var(--ax-accent);\n}\n.ax-skill-card__icon svg { width: 16px; height: 16px; }\n.ax-skill-card__name {\n font-weight: 600; font-size: 13px; letter-spacing: -0.005em;\n}\n.ax-skill-card__slug {\n font-family: var(--ax-mono); font-size: 10.5px;\n color: var(--ax-muted); margin-top: 1px;\n}\n.ax-skill-card__desc {\n font-size: 12px; color: var(--ax-text-2); line-height: 1.5;\n flex: 1; min-height: 32px;\n}\n.ax-skill-card__foot {\n display: flex; align-items: center; justify-content: space-between;\n font-size: 11px; color: var(--ax-muted);\n}\n\n/* Info callout (similar to wit but inline inside panels) */\n.ax-callout {\n background: color-mix(in oklch, var(--ax-info) 5%, var(--ax-bg-elev));\n border: 1px solid color-mix(in oklch, var(--ax-info) 20%, var(--ax-border));\n border-radius: var(--ax-radius-sm); padding: 12px 14px;\n font-size: 12.5px; color: var(--ax-text-2); line-height: 1.6;\n display: flex; gap: 10px; align-items: flex-start; margin-bottom: 16px;\n}\n.ax-callout__icon {\n width: 18px; height: 18px; border-radius: 50%;\n background: color-mix(in oklch, var(--ax-info) 18%, transparent);\n color: var(--ax-info); display: grid; place-items: center;\n font-size: 11px; font-weight: 600; flex-shrink: 0; margin-top: 1px;\n}\n.ax-callout b { color: var(--ax-text); }\n.ax-callout code {\n font-family: var(--ax-mono); font-size: 11.5px;\n background: var(--ax-bg); padding: 1px 5px; border-radius: var(--ax-radius-sm);\n border: var(--ax-border-w) solid var(--ax-border);\n}\n\n/* Channel-binding rows (agent-specific view) */\n.ax-chan-row {\n background: var(--ax-bg-elev); border: var(--ax-border-w) solid var(--ax-border);\n border-radius: var(--ax-radius-lg); padding: 12px 14px;\n display: flex; align-items: center; gap: 12px; margin-bottom: 8px;\n}\n.ax-chan-row__logo {\n width: 32px; height: 32px; border-radius: var(--ax-radius);\n display: grid; place-items: center;\n font-weight: 700; font-size: 13px; color: white; flex-shrink: 0;\n font-family: var(--ax-mono);\n}\n.ax-chan-row__info { flex: 1; }\n.ax-chan-row__name { font-size: 13px; font-weight: 600; }\n.ax-chan-row__desc {\n font-size: 12px; color: var(--ax-muted);\n margin-top: 2px; font-family: var(--ax-mono);\n}\n\n/* Handover viz (from-agent → to-agent arrow) */\n.ax-handover-viz {\n display: flex; align-items: center; justify-content: space-between;\n gap: 14px; padding: 16px 18px;\n background: var(--ax-bg-elev); border: var(--ax-border-w) solid var(--ax-border);\n border-radius: var(--ax-radius-lg); margin-bottom: 14px;\n}\n.ax-ho-side {\n display: flex; flex-direction: column; align-items: center;\n gap: 6px; flex: 0 0 130px;\n}\n.ax-ho-side__av {\n width: 42px; height: 42px; border-radius: var(--ax-radius);\n display: grid; place-items: center;\n font-weight: 600; font-size: 15px;\n}\n.ax-ho-side--from .ax-ho-side__av {\n background: color-mix(in oklch, var(--ax-accent) 18%, var(--ax-surface));\n border: 1px solid color-mix(in oklch, var(--ax-accent) 35%, var(--ax-border));\n color: var(--ax-accent);\n}\n.ax-ho-side--to .ax-ho-side__av {\n background: var(--ax-surface-2); border: var(--ax-border-w) solid var(--ax-border-2);\n color: var(--ax-text);\n}\n.ax-ho-side__nm { font-size: 12px; font-weight: 600; }\n.ax-ho-side__rl {\n font-size: 10px; color: var(--ax-muted);\n text-transform: uppercase; letter-spacing: 0.06em;\n font-family: var(--ax-mono);\n}\n.ax-ho-wire {\n flex: 1; height: 2px; position: relative;\n background: repeating-linear-gradient(90deg,\n var(--ax-border-2) 0, var(--ax-border-2) 4px,\n transparent 4px, transparent 8px);\n}\n.ax-ho-wire svg {\n position: absolute; right: -6px; top: 50%; transform: translateY(-50%);\n width: 16px; height: 16px; color: var(--ax-accent);\n}\n.ax-ho-list-row {\n display: flex; align-items: center; gap: 10px;\n padding: 10px 12px;\n background: var(--ax-bg-elev); border: var(--ax-border-w) solid var(--ax-border);\n border-radius: var(--ax-radius-sm); margin-bottom: 6px;\n font-size: 12.5px;\n}\n.ax-ho-list-row .ax-who { flex: 1; color: var(--ax-text-2); }\n.ax-ho-list-row .ax-who b {\n color: var(--ax-text); font-family: var(--ax-mono); font-weight: 500;\n}\n.ax-ho-list-row .ax-when { color: var(--ax-muted); font-size: 11px; }\n\n/* Activity cells + spark + session rows */\n.ax-activity-grid {\n display: grid; grid-template-columns: repeat(4, 1fr);\n gap: 10px; margin-bottom: 18px;\n}\n.ax-activity-cell {\n background: var(--ax-bg-elev); border: var(--ax-border-w) solid var(--ax-border);\n border-radius: var(--ax-radius-sm); padding: 14px;\n}\n.ax-activity-cell .ax-v {\n font-size: 20px; font-weight: 600; letter-spacing: -0.02em; line-height: 1.2;\n}\n.ax-activity-cell .ax-l {\n font-size: 11px; color: var(--ax-muted); margin-top: 2px;\n}\n.ax-activity-cell .ax-delta {\n font-size: 11px; margin-top: 6px;\n display: inline-flex; align-items: center; gap: 3px;\n}\n.ax-activity-cell .ax-delta--up { color: var(--ax-accent); }\n.ax-activity-cell .ax-delta--down { color: var(--ax-muted); }\n\n.ax-spark { width: 100%; height: 100px; margin: 10px 0 14px; }\n\n.ax-session {\n display: flex; align-items: center; gap: 10px;\n padding: 10px 12px;\n background: var(--ax-bg-elev); border: var(--ax-border-w) solid var(--ax-border);\n border-radius: var(--ax-radius-sm); margin-bottom: 6px;\n font-size: 12.5px;\n}\n.ax-session__ch {\n width: 24px; height: 24px; border-radius: var(--ax-radius-sm);\n display: grid; place-items: center;\n font-weight: 700; font-size: 10px; color: white;\n flex-shrink: 0; font-family: var(--ax-mono);\n}\n.ax-session__body { flex: 1; min-width: 0; }\n.ax-session__txt {\n color: var(--ax-text); overflow: hidden; text-overflow: ellipsis;\n white-space: nowrap;\n}\n.ax-session__meta {\n font-size: 11px; color: var(--ax-muted);\n margin-top: 1px; font-family: var(--ax-mono);\n}\n\n/* Log row (Conversations tab) */\n.ax-log-head {\n display: grid; grid-template-columns: 24px 90px 160px 1fr auto;\n gap: 12px; padding: 8px 10px;\n border-bottom: var(--ax-border-w) solid var(--ax-border-2);\n font-size: 10px; color: var(--ax-muted);\n text-transform: uppercase; letter-spacing: 0.06em; font-family: var(--ax-mono);\n}\n.ax-log-row {\n display: grid; grid-template-columns: 24px 90px 160px 1fr auto;\n gap: 12px; align-items: center; padding: 9px 10px;\n border-bottom: var(--ax-border-w) solid var(--ax-border); font-size: 12px;\n}\n.ax-log-row:hover { background: color-mix(in oklch, var(--ax-accent) 4%, transparent); }\n.ax-log-row .ax-sev {\n width: 8px; height: 8px; border-radius: 50%; margin-left: 8px;\n}\n.ax-log-row .ax-sev--ok { background: var(--ax-accent); }\n.ax-log-row .ax-sev--warn { background: var(--ax-warn); }\n.ax-log-row .ax-sev--err { background: var(--ax-err); }\n.ax-log-row .ax-t {\n font-family: var(--ax-mono); color: var(--ax-muted); font-size: 11px;\n}\n.ax-log-row .ax-who {\n font-family: var(--ax-mono); font-size: 11px; color: var(--ax-text-2);\n}\n.ax-log-row .ax-msg {\n color: var(--ax-text); overflow: hidden;\n text-overflow: ellipsis; white-space: nowrap;\n}\n.ax-log-row .ax-ms {\n color: var(--ax-muted); font-family: var(--ax-mono); font-size: 11px;\n}\n\n/* Danger zone */\n.ax-panel--danger {\n border-color: color-mix(in oklch, var(--ax-err) 30%, var(--ax-border));\n}\n.ax-danger-item {\n display: flex; align-items: center; gap: 14px;\n padding: 14px 0; border-bottom: 1px dashed var(--ax-border);\n}\n.ax-danger-item:last-child { border-bottom: 0; padding-bottom: 0; }\n.ax-danger-item:first-child { padding-top: 4px; }\n.ax-danger-item__info { flex: 1; }\n.ax-danger-item__name { font-size: 13px; font-weight: 600; margin-bottom: 3px; }\n.ax-danger-item__desc { font-size: 12px; color: var(--ax-muted); line-height: 1.5; }\n\n/* Test-drive panel — docked to the right edge, slides in from outside\n * the viewport when toggled. Similar to the Live page history-panel. */\n.ax-td-panel {\n position: fixed; top: 0; right: 0; bottom: 0; width: 420px;\n background: var(--ax-bg-elev); border-left: var(--ax-border-w) solid var(--ax-border);\n z-index: 900;\n display: flex; flex-direction: column;\n box-shadow: -8px 0 24px rgba(0,0,0,0.4);\n transform: translateX(100%); transition: transform 0.22s ease;\n}\n.ax-td-panel.is-open { transform: translateX(0); }\n@media (max-width: 520px) {\n .ax-td-panel { width: 100vw; }\n}\n.ax-td-head {\n padding: 12px 14px; border-bottom: var(--ax-border-w) solid var(--ax-border);\n background: var(--ax-bg-elev);\n display: flex; align-items: center; gap: 10px;\n}\n.ax-td-head__dot {\n width: 8px; height: 8px; border-radius: 50%;\n background: var(--ax-accent);\n box-shadow: 0 0 8px color-mix(in oklch, var(--ax-accent) 60%, transparent);\n animation: ax-pulse 2.2s infinite;\n}\n.ax-td-head__t { flex: 1; font-size: 12px; font-weight: 600; }\n.ax-td-head__sub {\n font-size: 11px; color: var(--ax-muted); font-family: var(--ax-mono);\n}\n.ax-td-head__seg {\n display: inline-flex; background: var(--ax-surface);\n border: var(--ax-border-w) solid var(--ax-border); border-radius: var(--ax-radius-sm); padding: 2px;\n}\n.ax-td-head__seg button {\n background: transparent; border: 0; color: var(--ax-muted);\n padding: 3px 8px; font: inherit; font-size: 10.5px;\n cursor: pointer; border-radius: var(--ax-radius-sm); font-family: var(--ax-mono);\n}\n.ax-td-head__seg button.is-active {\n background: var(--ax-surface-2); color: var(--ax-text);\n}\n\n.ax-td-scenarios {\n padding: 10px 14px; background: var(--ax-bg-elev);\n border-bottom: var(--ax-border-w) solid var(--ax-border);\n display: flex; gap: 5px; flex-wrap: wrap;\n}\n.ax-td-scenarios__lbl {\n font-size: 10px; color: var(--ax-muted);\n text-transform: uppercase; letter-spacing: 0.06em;\n font-family: var(--ax-mono); width: 100%; margin-bottom: 3px;\n}\n.ax-td-scenario {\n font-size: 11px; padding: 3px 8px;\n background: var(--ax-surface); border: var(--ax-border-w) solid var(--ax-border);\n border-radius: var(--ax-radius); color: var(--ax-text-2);\n cursor: pointer; transition: all 120ms;\n}\n.ax-td-scenario:hover { border-color: var(--ax-accent); color: var(--ax-accent); }\n\n.ax-td-chat {\n flex: 1; overflow-y: auto; padding: 14px;\n display: flex; flex-direction: column; gap: 8px;\n background: var(--ax-bg);\n}\n.ax-td-chat__msg-wrap {\n display: flex; flex-direction: column; gap: 2px; max-width: 86%;\n}\n.ax-td-chat__msg-wrap.is-user { align-self: flex-end; align-items: flex-end; }\n.ax-td-chat__msg-wrap.is-bot { align-self: flex-start; }\n.ax-td-chat__bubble {\n padding: 8px 12px; border-radius: var(--ax-radius-lg);\n font-size: 12.5px; line-height: 1.55; word-wrap: break-word;\n}\n.ax-td-chat__bubble.is-user {\n background: var(--ax-accent); color: var(--ax-bg);\n border-bottom-right-radius: 3px; font-weight: 500;\n}\n.ax-td-chat__bubble.is-bot {\n background: var(--ax-surface-2); color: var(--ax-text);\n border-bottom-left-radius: 3px; border: var(--ax-border-w) solid var(--ax-border);\n}\n.ax-td-chat__meta {\n font-size: 9.5px; color: var(--ax-muted);\n font-family: var(--ax-mono); padding: 0 4px;\n}\n.ax-td-chat__typing {\n align-self: flex-start; padding: 10px 14px;\n background: var(--ax-surface-2); border: var(--ax-border-w) solid var(--ax-border);\n border-radius: var(--ax-radius-lg); border-bottom-left-radius: 3px;\n display: inline-flex; gap: 4px;\n}\n.ax-td-chat__typing span {\n width: 6px; height: 6px; background: var(--ax-muted);\n border-radius: 50%; animation: axTypingBounce 1.2s infinite;\n}\n.ax-td-chat__typing span:nth-child(2) { animation-delay: 0.15s; }\n.ax-td-chat__typing span:nth-child(3) { animation-delay: 0.3s; }\n@keyframes axTypingBounce {\n 0%, 60%, 100% { transform: translateY(0); opacity: 0.4; }\n 30% { transform: translateY(-5px); opacity: 1; }\n}\n.ax-td-input {\n padding: 10px; border-top: var(--ax-border-w) solid var(--ax-border);\n background: var(--ax-bg-elev); display: flex; gap: 6px;\n}\n.ax-td-input input {\n flex: 1; background: var(--ax-bg); border: var(--ax-border-w) solid var(--ax-border);\n border-radius: var(--ax-radius); padding: 8px 11px;\n color: var(--ax-text); font: inherit; font-size: 12.5px;\n}\n.ax-td-input input:focus { outline: none; border-color: var(--ax-accent); }\n.ax-td-input button {\n background: var(--ax-accent); color: var(--ax-bg); border: 0;\n border-radius: var(--ax-radius); padding: 0 12px;\n cursor: pointer; font-weight: 600;\n}\n.ax-td-input button:hover {\n background: color-mix(in oklch, var(--ax-accent) 88%, white);\n}\n\n/* Inline task-queue chip on bot messages */\n.ax-td-task-chip {\n display: inline-flex; align-items: center; gap: 5px;\n margin-top: 5px; padding: 3px 8px;\n background: color-mix(in oklch, var(--ax-accent) 10%, var(--ax-surface));\n border: 1px solid color-mix(in oklch, var(--ax-accent) 35%, var(--ax-border));\n border-radius: var(--ax-radius); font-size: 10px; color: var(--ax-accent);\n font-family: var(--ax-mono); letter-spacing: 0.04em;\n cursor: default; user-select: none;\n}\n.ax-td-task-chip__dot {\n width: 6px; height: 6px; border-radius: 50%;\n background: var(--ax-accent); flex-shrink: 0;\n}\n.ax-td-task-chip--queued .ax-td-task-chip__dot {\n background: var(--ax-muted);\n}\n.ax-td-task-chip__id {\n font-family: var(--ax-mono); opacity: 0.7; font-size: 9px;\n}\n\n/* Section mini-label used throughout detail pages */\n.ax-sec-label-d {\n display: flex; align-items: center; justify-content: space-between;\n margin: 20px 0 10px;\n}\n.ax-sec-label-d h3 {\n font-size: 11px; font-weight: 600; letter-spacing: 0.06em;\n text-transform: uppercase; color: var(--ax-muted);\n margin: 0; font-family: var(--ax-mono);\n}\n\n/* --- Responsive collapses for the settings primitives --- */\n@media (max-width: 1180px) {\n .ax-layout { grid-template-columns: 180px 1fr; }\n}\n@media (max-width: 760px) {\n .ax-health-strip { grid-template-columns: repeat(2, 1fr); }\n .ax-connectors { grid-template-columns: 1fr; }\n .ax-detail-grid { grid-template-columns: 1fr; }\n .ax-mesh-hero { grid-template-columns: 1fr; }\n .ax-mesh-viz { margin: 0 auto; }\n .ax-cron-hints { grid-template-columns: 1fr; }\n .ax-layout { grid-template-columns: 1fr; }\n .ax-rail { display: none; }\n .ax-stats-inline { grid-template-columns: repeat(2, 1fr); }\n .ax-activity-grid { grid-template-columns: repeat(2, 1fr); }\n .ax-log-head, .ax-log-row { grid-template-columns: 8px 80px 1fr auto; }\n .ax-log-head span:nth-child(3), .ax-log-row .ax-who { display: none; }\n .ax-grid-2, .ax-grid-3 { grid-template-columns: 1fr; }\n}`\n","// --- Page shell renderer ---\n//\n// Every dashboard page used to hand-roll its own <html><head><body> wrapper:\n// meta tags, font links, theme bootstrap script, style tags, topbar markup,\n// closing tags. That's ~60 lines of boilerplate per page, identical across\n// ~8 pages. This module centralises it.\n//\n// A page calls `renderShell({ title, activeTab, subtitle, body, css, ... })`\n// and gets back a complete HTML document with:\n// - Correct <meta>, <title>, Plex font link, theme-bootstrap script\n// - AX_TOKENS_CSS (design tokens) + AX_COMPONENTS_CSS (badge/stat/card/...)\n// - TOPBAR_CSS (chrome) always included\n// - Page-specific CSS appended after\n// - renderTopbar() at the top\n// - <main> with the page body\n// - TOPBAR_SCRIPT (theme toggle + mesh selector) at the end\n// - Page-specific <script> appended after\n//\n// Pages that want a slimmer header (e.g. /setup, which has its own simplified\n// topbar) can pass `renderHeader: \"custom\"` and provide their own <header>.\n\nimport { AX_TOKENS_CSS } from \"./tokens\"\nimport { 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) => ({ \"<\": \"<\", \">\": \">\", \"&\": \"&\" }[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 \"&\": \"&\", \"<\": \"<\", \">\": \">\", '\"': \""\", \"'\": \"'\",\n }[c] as string))\n}\n\n/** Join class names, dropping falsy values. */\nexport function cx(...parts: Array<string | false | null | undefined>): string {\n return parts.filter(Boolean).join(\" \")\n}\n","// --- Shared UI components ---\n//\n// Small helpers that return HTML strings. Used by every page to produce\n// consistent badges, cards, form rows, buttons without copy-pasting.\n//\n// ### Philosophy\n//\n// - Pure functions: `string` in, `string` out. No classes, no DSL, no JSX.\n// - Caller composes: `cardHtml(children)` not `Card({children})`.\n// - Null/undefined opts render an empty string, so conditional composition\n// just inlines: `${badge(...) ?? \"\"}` without wrapper ifs.\n// - Variants are flat classnames, not object props: `badge(\"LIVE\", \"live\")`\n// not `badge({label: \"LIVE\", variant: \"live\"})`. Cheaper to read, easier\n// to grep for.\n\nimport { esc } from \"./util\"\n\n// --- Dots (status indicators) ---\n\nexport type DotKind = \"ok\" | \"live\" | \"warn\" | \"err\" | \"off\" | undefined\n\nexport function dot(kind?: DotKind, pulse = false): string {\n const classes = [\"ax-dot\", kind ? `ax-dot--${kind}` : \"\", pulse ? \"ax-dot--pulse\" : \"\"]\n .filter(Boolean).join(\" \")\n return `<span class=\"${classes}\"></span>`\n}\n\n// --- Badges (small pill labels) ---\n\nexport type BadgeKind = \"accent\" | \"live\" | \"warn\" | \"err\" | \"ghost\" | \"mono\" | undefined\n\nexport function badge(label: string, kind?: BadgeKind, extraClass = \"\"): string {\n const classes = [\"ax-badge\", kind ? `ax-badge--${kind}` : \"\", extraClass]\n .filter(Boolean).join(\" \")\n return `<span class=\"${classes}\">${esc(label)}</span>`\n}\n\n// --- Buttons ---\n\nexport interface BtnOpts {\n type?: \"button\" | \"submit\"\n primary?: boolean\n ghost?: boolean\n danger?: boolean\n id?: string\n disabled?: boolean\n extraClass?: string\n onclick?: string\n}\n\nexport function btn(label: string, opts: BtnOpts = {}): string {\n const classes = [\"ax-btn\",\n opts.primary ? \"ax-btn--primary\" : \"\",\n opts.ghost ? \"ax-btn--ghost\" : \"\",\n opts.danger ? \"ax-btn--danger\" : \"\",\n opts.extraClass || \"\",\n ].filter(Boolean).join(\" \")\n const attrs = [\n `type=\"${opts.type || \"button\"}\"`,\n `class=\"${classes}\"`,\n opts.id ? `id=\"${esc(opts.id)}\"` : \"\",\n opts.disabled ? \"disabled\" : \"\",\n opts.onclick ? `onclick=\"${esc(opts.onclick)}\"` : \"\",\n ].filter(Boolean).join(\" \")\n return `<button ${attrs}>${esc(label)}</button>`\n}\n\n// --- Stat cards (the \"NUMBER + label\" strip) ---\n\nexport interface StatOpts {\n label: string\n value: string\n sub?: string\n kind?: \"live\" | \"warn\" | \"err\"\n}\n\nexport function stat(opts: StatOpts): string {\n const cls = opts.kind ? `ax-stat ax-stat--${opts.kind}` : \"ax-stat\"\n return `<div class=\"${cls}\">\n <div class=\"ax-stat__label\">${esc(opts.label)}</div>\n <div class=\"ax-stat__value\">${esc(opts.value)}</div>\n ${opts.sub ? `<div class=\"ax-stat__sub\">${esc(opts.sub)}</div>` : \"\"}\n </div>`\n}\n\nexport function statStrip(stats: StatOpts[]): string {\n return `<div class=\"ax-statstrip\">${stats.map(stat).join(\"\")}</div>`\n}\n\n// --- Form field (label + input/textarea/select) ---\n\nexport interface FieldOpts {\n name: string\n label: string\n hint?: string\n type?: \"text\" | \"password\" | \"email\" | \"url\" | \"number\" | \"textarea\" | \"select\"\n value?: string\n placeholder?: string\n pattern?: string\n required?: boolean\n readonly?: boolean\n rows?: number\n options?: Array<{ value: string; label: string; selected?: boolean }>\n extraAttrs?: string\n}\n\nexport function field(opts: FieldOpts): string {\n const labelHtml = `<label>${esc(opts.label)}${\n opts.hint ? ` <span class=\"ax-hint\">${opts.hint}</span>` : \"\"\n }</label>`\n let controlHtml = \"\"\n const common = [\n `name=\"${esc(opts.name)}\"`,\n opts.required ? \"required\" : \"\",\n opts.readonly ? \"readonly\" : \"\",\n opts.placeholder ? `placeholder=\"${esc(opts.placeholder)}\"` : \"\",\n opts.extraAttrs || \"\",\n ].filter(Boolean).join(\" \")\n\n if (opts.type === \"textarea\") {\n controlHtml = `<textarea ${common} rows=\"${opts.rows || 3}\">${esc(opts.value || \"\")}</textarea>`\n } else if (opts.type === \"select\" && opts.options) {\n const options = opts.options.map(o =>\n `<option value=\"${esc(o.value)}\"${o.selected ? \" selected\" : \"\"}>${esc(o.label)}</option>`\n ).join(\"\")\n controlHtml = `<select ${common}>${options}</select>`\n } else {\n const type = opts.type || \"text\"\n controlHtml = `<input type=\"${type}\" ${common}`\n + (opts.value !== undefined ? ` value=\"${esc(opts.value)}\"` : \"\")\n + (opts.pattern ? ` pattern=\"${esc(opts.pattern)}\"` : \"\")\n + \" />\"\n }\n return `<div class=\"ax-field\">${labelHtml}${controlHtml}</div>`\n}\n\nexport function row(children: string[]): string {\n return `<div class=\"ax-row\">${children.join(\"\")}</div>`\n}\n\n// --- Cards ---\n\nexport function card(content: string, extraClass = \"\"): string {\n return `<div class=\"ax-card${extraClass ? \" \" + extraClass : \"\"}\">${content}</div>`\n}\n\nexport function stepCard(num: number | string, title: string, body: string): string {\n return `<section class=\"ax-step\">\n <div class=\"ax-step__head\"><span class=\"ax-step__num\">${esc(String(num))}</span>${esc(title)}</div>\n ${body}\n </section>`\n}\n\n// --- Small layout helpers ---\n\nexport function spacer(height = 12): string {\n return `<div style=\"height:${height}px\"></div>`\n}\n\n// ============================================================================\n// Settings-page primitives (from the Stitch redesign)\n// ============================================================================\n\n/** Page head — kicker + h1 + lead. Lives outside <main> so it can span wider. */\nexport interface PageHeadOpts {\n kicker?: string\n title: string\n lead?: string\n}\nexport function pageHead(opts: PageHeadOpts): string {\n return `<div class=\"ax-page-head\">\n ${opts.kicker ? `<div class=\"ax-kicker\">${esc(opts.kicker)}</div>` : \"\"}\n <h1>${esc(opts.title)}</h1>\n ${opts.lead ? `<p class=\"ax-lead\">${opts.lead}</p>` : \"\"}\n </div>`\n}\n\n/** 4-up health strip: colour-coded status cards across the top of a page. */\nexport interface HealthCardOpts {\n kind?: \"ok\" | \"warn\" | \"off\"\n num: string\n label: string\n}\nexport function healthCard(opts: HealthCardOpts): string {\n const kind = opts.kind || \"off\"\n return `<div class=\"ax-health-card\">\n <div class=\"ax-hc-dot ax-hc-dot--${kind}\"></div>\n <div class=\"ax-hc-txt\">\n <div class=\"ax-hc-num\">${esc(opts.num)}</div>\n <div class=\"ax-hc-lbl\">${esc(opts.label)}</div>\n </div>\n </div>`\n}\nexport function healthStrip(cards: HealthCardOpts[]): string {\n return `<div class=\"ax-health-strip\">${cards.map(healthCard).join(\"\")}</div>`\n}\n\n/**\n * \"What is this?\" banner. Renders a dismissable explanatory box for users\n * meeting a tab for the first time. Pass raw HTML for body — short runs\n * with <b> and <code> are the intended style.\n */\nexport interface WitOpts {\n bodyHtml: string\n /** Persist the dismissal under this key in localStorage. Optional. */\n persistKey?: string\n icon?: string\n}\nexport function witBanner(opts: WitOpts): string {\n const attr = opts.persistKey ? ` data-wit=\"${esc(opts.persistKey)}\"` : \"\"\n return `<div class=\"ax-wit\"${attr}>\n <span class=\"ax-wit__icon\">${esc(opts.icon || \"?\")}</span>\n <div>${opts.bodyHtml}</div>\n <button class=\"ax-wit__dismiss\" onclick=\"this.parentElement.style.display='none'\">×</button>\n </div>`\n}\n\n/**\n * Section header with icon chip + title + lead + optional right-aligned\n * action. Icon is raw SVG so the caller picks the glyph; feed a 24x24\n * stroked Feather-style icon for best fit.\n */\nexport interface SectionHeadOpts {\n icon: string\n title: string\n lead?: string\n actionHtml?: string\n}\nexport function sectionHead(opts: SectionHeadOpts): string {\n return `<div class=\"ax-section-head\">\n <div class=\"ax-section-head__icon\">${opts.icon}</div>\n <div class=\"ax-section-head__text\">\n <h2>${esc(opts.title)}</h2>\n ${opts.lead ? `<p class=\"ax-lead\">${esc(opts.lead)}</p>` : \"\"}\n </div>\n ${opts.actionHtml || \"\"}\n </div>`\n}\n\n/** Coloured-initial avatar — pass 1-2 letters; variant sets the tint. */\nexport type AvatarVariant = \"plain\" | \"teal\" | \"coral\" | \"amber\" | \"blue\"\nexport function avatar(initials: string, variant: AvatarVariant = \"plain\"): string {\n const cls = variant === \"plain\" ? \"ax-avatar\" : `ax-avatar ax-avatar--${variant}`\n return `<div class=\"${cls}\">${esc(initials)}</div>`\n}\n\n/**\n * Status pill with coloured dot — \"live\", \"idle\", \"receiving\", \"stale\".\n * Different from {@link badge}: pill has a rounded shape (10px radius) and\n * a leading dot. Prefer for attached-to-name status labels.\n */\nexport type PillKind = \"ok\" | \"warn\" | \"off\" | \"info\" | \"plain\"\nexport function pill(label: string, kind: PillKind = \"plain\", withDot = true): string {\n const cls = kind === \"plain\" ? \"ax-pill\" : `ax-pill ax-pill--${kind}`\n const dot = withDot && kind !== \"plain\" && kind !== \"off\" ? `<span class=\"ax-pill__dot\"></span>` : \"\"\n return `<span class=\"${cls}\">${dot}${esc(label)}</span>`\n}\n\n/**\n * Row card with colored avatar + info + actions + optional expandable\n * details section. Details are hidden behind `.is-open` on the card;\n * toggleCard() on the client flips it.\n */\nexport interface RowCardOpts {\n avatarHtml?: string\n /** Left block: title + pills + sub-line. Raw HTML. */\n infoHtml: string\n /** Right-aligned action cluster. Raw HTML (usually btns). */\n actionsHtml?: string\n /** Collapsible details. When provided, the card is collapsible. */\n detailsHtml?: string\n /** Initial open state. Default false. */\n open?: boolean\n /** Extra classes on the card wrapper. */\n extraClass?: string\n}\nexport function rowCard(opts: RowCardOpts): string {\n const cls = [\"ax-row-card\", opts.open ? \"is-open\" : \"\", opts.extraClass || \"\"]\n .filter(Boolean).join(\" \")\n return `<div class=\"${cls}\">\n <div class=\"ax-row-card__top\">\n ${opts.avatarHtml || \"\"}\n <div class=\"ax-row-card__info\">${opts.infoHtml}</div>\n ${opts.actionsHtml ? `<div class=\"ax-row-card__actions\">${opts.actionsHtml}</div>` : \"\"}\n </div>\n ${opts.detailsHtml ? `<div class=\"ax-row-card__details\">${opts.detailsHtml}</div>` : \"\"}\n </div>`\n}\n\n/**\n * Channel/webhook-style connector card — big logo, name, status pill,\n * description, foot row. Clickable (caller attaches onclick / wraps\n * in <a>).\n */\nexport interface ConnectorOpts {\n logoHtml: string\n name: string\n sub: string\n statusHtml?: string\n desc: string\n footLeft?: string\n footCta?: string\n isOn?: boolean\n onclick?: string\n extraStyle?: string\n}\nexport function connector(opts: ConnectorOpts): string {\n const cls = opts.isOn ? \"ax-connector is-on\" : \"ax-connector\"\n const onclick = opts.onclick ? ` onclick=\"${esc(opts.onclick)}\"` : \"\"\n const style = opts.extraStyle ? ` style=\"${esc(opts.extraStyle)}\"` : \"\"\n return `<div class=\"${cls}\"${onclick}${style}>\n <div class=\"ax-connector__top\">\n ${opts.logoHtml}\n <div class=\"ax-connector__meta\">\n <div class=\"ax-connector__name\">${esc(opts.name)}</div>\n <div class=\"ax-connector__sub\">${esc(opts.sub)}</div>\n </div>\n ${opts.statusHtml ? `<div class=\"ax-connector__status\">${opts.statusHtml}</div>` : \"\"}\n </div>\n <div class=\"ax-connector__desc\">${opts.desc}</div>\n <div class=\"ax-connector__foot\">\n <span>${esc(opts.footLeft || \"\")}</span>\n ${opts.footCta\n ? `<span class=\"ax-connector__cta\">${esc(opts.footCta)} <svg viewBox=\"0 0 12 12\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\"><path d=\"M4 2l4 4-4 4\"/></svg></span>`\n : \"\"}\n </div>\n </div>`\n}\n\n/** Empty state used when a tab has nothing yet. */\nexport interface EmptyOpts {\n title: string\n desc?: string\n actionHtml?: string\n}\nexport function emptyState(opts: EmptyOpts): string {\n return `<div class=\"ax-empty-card\" style=\"text-align:center;padding:42px 22px;background:var(--ax-surface);border:1px dashed var(--ax-border-2);border-radius:var(--ax-radius-lg);color:var(--ax-muted)\">\n <h3 style=\"margin:0 0 4px;color:var(--ax-text);font-size:15px;font-weight:600\">${esc(opts.title)}</h3>\n ${opts.desc ? `<p style=\"margin:0 0 16px;font-size:13px;line-height:1.55;max-width:380px;margin-left:auto;margin-right:auto\">${esc(opts.desc)}</p>` : \"\"}\n ${opts.actionHtml || \"\"}\n </div>`\n}\n\n/** Mini-label used to divide sections inside a tab. */\nexport interface SecLabelOpts {\n label: string\n rightHtml?: string\n}\nexport function secLabel(opts: SecLabelOpts): string {\n return `<div class=\"ax-sec-label\"><h3>${esc(opts.label)}</h3>${opts.rightHtml || \"\"}</div>`\n}\n\n/** Toast element + helper script. Include once per page that uses it. */\nexport const TOAST_HTML = `<div class=\"ax-toast\" id=\"ax-toast\">\n <svg class=\"ax-toast__icon\" width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><path d=\"M20 6L9 17l-5-5\"/></svg>\n <span id=\"ax-toast-msg\">Copied</span>\n</div>`\nexport const TOAST_SCRIPT = `\n let __axToastTimer;\n window.showToast = function(msg) {\n const el = document.getElementById('ax-toast');\n const m = document.getElementById('ax-toast-msg');\n if (!el || !m) return;\n m.textContent = msg;\n el.classList.add('is-show');\n clearTimeout(__axToastTimer);\n __axToastTimer = setTimeout(function(){ el.classList.remove('is-show'); }, 1800);\n };\n window.copyText = function(_btn, text) {\n try { navigator.clipboard.writeText(text); } catch (e) {}\n window.showToast('Copied to clipboard');\n };`\n\n/** Client script that wires `.ax-row-card` expand/collapse on any button\n * that calls `toggleCard(this)`. Include once per page. */\nexport const ROW_CARD_SCRIPT = `\n window.toggleCard = function(btn) {\n const card = btn.closest('.ax-row-card');\n if (card) card.classList.toggle('is-open');\n };`\n"],"mappings":"6DA6BO,IAAMA,EAAgB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;yCCpBtB,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;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;GC4D1B,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","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"]}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import{a as ie}from"./chunk-M4AMWP3H.js";import{b as oe}from"./chunk-AFRHKA7B.js";import{a as Ae,c as $e,d as Te,f as De,g as Pe,h as We}from"./chunk-53GCFZPR.js";import{a as y,b as j}from"./chunk-ISEBLHWX.js";import{existsSync as bt,mkdirSync as Ne,readFileSync as vt,writeFileSync as It,renameSync as St}from"fs";import{resolve as Ee,dirname as Rt}from"path";var xe=class{filePath;log;constructor(e={}){let t=e.baseDir??Ee(process.cwd(),".agentx");this.filePath=Ee(t,"handover.json"),this.log=e.log??console.error.bind(console,"[handover]"),Ne(t,{recursive:!0})}key(e,t,r){return`${e}::${t}::${r||"-"}`}load(){if(!bt(this.filePath))return{version:1,overrides:{}};try{let e=vt(this.filePath,"utf-8"),t=JSON.parse(e);return!t||typeof t!="object"?{version:1,overrides:{}}:(t.overrides||(t.overrides={}),t)}catch(e){return this.log(`load failed, starting empty: ${e?.message||e}`),{version:1,overrides:{}}}}save(e){Ne(Rt(this.filePath),{recursive:!0});let t=`${this.filePath}.tmp-${process.pid}`;It(t,JSON.stringify(e,null,2)),St(t,this.filePath)}get(e,t,r){let o=this.load().overrides[this.key(e,t,r)];if(o&&!(o.expiresAt&&Date.parse(o.expiresAt)<Date.now()))return o}set(e){if(!e.channel||!e.chatId||!e.toAgent)throw new Error("channel, chatId and toAgent are required");let t=this.load(),r={...e,createdAt:e.createdAt??new Date().toISOString()};return t.overrides[this.key(r.channel,r.chatId,r.accountId)]=r,this.save(t),r}remove(e,t,r){let s=this.load(),o=this.key(e,t,r);return o in s.overrides?(delete s.overrides[o],this.save(s),!0):!1}consumeSummary(e,t,r){let s=this.load(),o=s.overrides[this.key(e,t,r)];if(!o||!o.summary||o.summaryConsumedAt)return;let i=o.summary;return o.summaryConsumedAt=new Date().toISOString(),this.save(s),i}listActive(){let e=this.load(),t=Date.now();return Object.values(e.overrides).filter(r=>!r.expiresAt||Date.parse(r.expiresAt)>=t)}listByAgent(e){let t=this.listActive();return{incoming:t.filter(r=>r.toAgent===e),outgoing:t.filter(r=>r.fromAgent===e)}}};import{existsSync as b,mkdirSync as Et,readdirSync as z,readFileSync as G,unlinkSync as V,writeFileSync as de}from"fs";import{resolve as R,relative as le}from"path";import Oe from"js-yaml";var At=new Set(["branch","gateway.parallel","rule","signal.wait","subProcess","timer.boundary","checkpoint"]),k=class extends Error{constructor(t,r){super(t);this.mark=r;this.name="WorkflowYamlError"}};function x(n,e={}){if(!n||n.trim()==="")throw new k(`${A(e)}empty file \u2014 workflow YAML must contain at least an id, version, title, and one node.`);let t;try{t=Oe.loadAll(n)}catch(s){let o=s?.mark;throw new k(`${A(e)}YAML parse error: ${s?.reason??s?.message??s}`+(o?` (line ${o.line+1}, column ${o.column+1})`:""),o)}if(t.length===0||t.length===1&&t[0]==null)throw new k(`${A(e)}YAML produced no document.`);if(t.length>1)throw new k(`${A(e)}multi-document YAML is not supported in v1; one workflow per file.`);let r=t[0];if(typeof r!="object"||r===null||Array.isArray(r)){let s=Array.isArray(r)?"array":typeof r;throw new k(`${A(e)}top-level YAML must be a mapping (got ${s}).`)}return je(r,e)}function je(n,e={}){if(!("flow"in n))return n;let t=n.flow;if(t==null){let c={...n};return delete c.flow,c}if(!Array.isArray(t))throw new k(`${A(e)}flow must be an array of node ids (got ${typeof t}).`);for(let c of t)if(typeof c!="string")throw new k(`${A(e)}flow entries must be strings (got ${typeof c}).`);let r=t,s=Array.isArray(n.nodes)?n.nodes:[],o=new Map;for(let c of s)c&&typeof c=="object"&&typeof c.id=="string"&&typeof c.type=="string"&&o.set(c.id,c.type);for(let c of r){if(!o.has(c))throw new k(`${A(e)}flow references unknown node "${c}".`);let f=o.get(c);if(At.has(f))throw new k(`${A(e)}flow cannot include ${f} node "${c}"; use explicit edges for ${f} nodes.`)}let i=[];for(let c=0;c<r.length-1;c++)i.push({from:r[c],to:r[c+1]});let a=Array.isArray(n.edges)?n.edges:[],d=new Set,l=[];for(let c of a){if(!c||typeof c!="object")continue;let f=_e(c.from,c.fromPort,c.to);f&&!d.has(f)?(d.add(f),l.push(c)):f||l.push(c)}for(let c of i){let f=_e(c.from,void 0,c.to);f&&!d.has(f)&&(d.add(f),l.push(c))}let p={...n,edges:l};return delete p.flow,p}function _e(n,e,t){return typeof n!="string"||typeof t!="string"?null:`${n}::${typeof e=="string"?e:""}::${t}`}function A(n){return n.filePath?`${n.filePath}: `:""}function Y(n){return Oe.dump(n,{noRefs:!0,lineWidth:100,sortKeys:!1})}import{Document as $t,isMap as J,isSeq as Tt,isScalar as Fe,parseDocument as Dt}from"yaml";function Ce(n,e){let t=Dt(n);if(!t.contents||!J(t.contents)){let r=new $t(e);return t.commentBefore&&(r.commentBefore=t.commentBefore),String(r)}return ae(t,t.contents,e),String(t)}function ae(n,e,t){let r=Object.keys(t),s=new Set(r),o=new Map;for(let a of e.items){let d=Nt(a);d!==null&&o.set(d,a)}let i=[];for(let a of r){let d=t[a],l=o.get(a);l?(Pt(n,l,d),i.push(l)):i.push(n.createPair(a,d))}e.items=i}function Pt(n,e,t){let r=e.value;if(t==null){e.value=n.createNode(null);return}if(typeof t!="object"){e.value=n.createNode(t);return}if(Array.isArray(t)){Tt(r)?Wt(n,r,t):e.value=n.createNode(t);return}J(r)?ae(n,r,t):e.value=n.createNode(t)}function Wt(n,e,t){if(!(t.length>0&&t.every(i=>i&&typeof i=="object"&&typeof i.id=="string"))){let i=n.createNode(t);e.items=i.items;return}let s=new Map;for(let i of e.items)if(J(i)){let a=i.get("id",!0);Fe(a)&&typeof a.value=="string"&&s.set(a.value,i)}let o=[];for(let i of t){let a=i.id,d=s.get(a);d&&J(d)?(ae(n,d,i),o.push(d)):o.push(n.createNode(i))}e.items=o}function Nt(n){let e=n.key;return Fe(e)&&typeof e.value=="string"?e.value:typeof e=="string"?e:null}var ce=class{baseDir;constructor(e={}){this.baseDir=e.baseDir??R(process.cwd(),".agentx/workflows"),Et(this.baseDir,{recursive:!0})}pathFor(e){return this.pathForJson(e)}pathForJson(e){return R(this.baseDir,`${e}.json`)}pathForYaml(e){return R(this.baseDir,`${e}.yaml`)}isWorkflowFile(e){return e.startsWith("_")?!1:e.endsWith(".json")||e.endsWith(".yaml")||e.endsWith(".yml")}idFromFilename(e){return e.replace(/\.(json|ya?ml)$/,"")}readAndParse(e){let t=R(this.baseDir,e),r=G(t,"utf-8");return e.endsWith(".json")?JSON.parse(r):x(r,{filePath:e})}list(){if(!b(this.baseDir))return[];let e=new Map;for(let r of z(this.baseDir,{withFileTypes:!0})){if(!r.isFile()||!this.isWorkflowFile(r.name))continue;let s=this.idFromFilename(r.name),o=e.get(s)??[];o.push(r.name),e.set(s,o)}let t=[];for(let[,r]of e)if(!(r.length>1))try{let s=this.readAndParse(r[0]),o=y.safeParse(s);o.success&&t.push(o.data)}catch{}return t.sort((r,s)=>r.id.localeCompare(s.id))}validateAll(){let e=[];if(!b(this.baseDir))return e;let t=new Map;for(let r of z(this.baseDir,{withFileTypes:!0})){if(!r.isFile()||!this.isWorkflowFile(r.name))continue;let s=this.idFromFilename(r.name),o=t.get(s)??[];o.push(r.name),t.set(s,o)}for(let[r,s]of t){if(s.length>1){let c=[...s].sort();for(let f of c)e.push({id:r,path:le(this.baseDir,R(this.baseDir,f)),issues:[`duplicate workflow id "${r}" \u2014 found ${c.join(" and ")}; delete one to disambiguate`],isValid:!1});continue}let o=s[0],i=R(this.baseDir,o),a=le(this.baseDir,i),d;try{d=this.readAndParse(o)}catch(c){let f=c instanceof k?c.message:`parse error: ${c?.message??c}`;e.push({id:r,path:a,issues:[f],isValid:!1});continue}let l=y.safeParse(d);if(!l.success){let c=l.error.issues.map(f=>`${f.path.join(".")||"<root>"}: ${f.message}`);e.push({id:String(d?.id??r),path:a,issues:c,isValid:!1});continue}let p=j(l.data);e.push({workflow:l.data,issues:p,isValid:p.length===0})}return e}get(e){let t=this.pathForJson(e),r=this.pathForYaml(e),s=R(this.baseDir,`${e}.yml`),o=b(t),i=b(r)||b(s);if(o&&i)return null;if(o)try{let d=JSON.parse(G(t,"utf-8")),l=y.safeParse(d);return l.success?l.data:null}catch{return null}let a=b(r)?r:b(s)?s:null;if(a)try{let d=G(a,"utf-8"),l=x(d,{filePath:le(this.baseDir,a)}),p=y.safeParse(l);return p.success?p.data:null}catch{return null}if(!b(this.baseDir))return null;for(let d of z(this.baseDir,{withFileTypes:!0}))if(!(!d.isFile()||!this.isWorkflowFile(d.name)||this.idFromFilename(d.name)===e))try{let p=this.readAndParse(d.name),c=y.safeParse(p);if(c.success&&c.data.id===e)return c.data}catch{}return null}save(e,t={}){let r=new Date().toISOString().slice(0,10),s=y.parse({...e,created:e.created||r,updated:r}),o=this.pathForYaml(s.id),i=R(this.baseDir,`${s.id}.yml`),a=b(o)?o:b(i)?i:null;if(!a&&b(this.baseDir)){for(let d of z(this.baseDir,{withFileTypes:!0}))if(!(!d.isFile()||!this.isWorkflowFile(d.name))&&!d.name.endsWith(".json"))try{let l=this.readAndParse(d.name),p=y.safeParse(l);if(p.success&&p.data.id===s.id){a=R(this.baseDir,d.name);break}}catch{}}if(a&&t.convertFromYaml)return V(a),de(this.pathForJson(s.id),JSON.stringify(s,null,2)+`
|
|
2
|
+
`),s;if(a){let d=G(a,"utf-8");try{let l=Ce(d,s);return de(a,l),s}catch(l){let p=new Error(`yaml round-trip failed for "${s.id}" at ${a}: ${l?.message??l}`);throw p.yamlPath=a,p}}return de(this.pathForJson(s.id),JSON.stringify(s,null,2)+`
|
|
3
|
+
`),s}delete(e){let t=this.pathForJson(e),r=this.pathForYaml(e),s=R(this.baseDir,`${e}.yml`),o=!1;return b(t)&&(V(t),o=!0),b(r)&&(V(r),o=!0),b(s)&&(V(s),o=!0),o}};import{createHash as xt,randomUUID as _t}from"crypto";import{appendFileSync as Me,existsSync as _,mkdirSync as Le,readdirSync as He,readFileSync as ue,statSync as Ue,unlinkSync as qe,writeFileSync as Ot}from"fs";import{resolve as N}from"path";function jt(n){return n.replace(/[^a-zA-Z0-9._:#@-]/g,"_").slice(0,200)}function T(n,e,t){return xt("sha1").update(`${n}|${e}|${t}`).digest("hex").slice(0,16)}var pe=class{baseDir;runsDir;indexDir;nodeId;constructor(e){this.baseDir=e.baseDir??N(process.cwd(),".agentx/workflows"),this.runsDir=N(this.baseDir,"_runs"),this.indexDir=N(this.baseDir,"_index"),this.nodeId=e.nodeId,Le(this.runsDir,{recursive:!0}),Le(this.indexDir,{recursive:!0})}runPath(e){return N(this.runsDir,`${e}.jsonl`)}indexPath(e){return N(this.indexDir,`${e.backend}__${jt(e.id)}.json`)}create(e){let t=new Date().toISOString(),r=_t(),s={id:r,workflowId:e.workflowId,workflowVersion:2,homeNode:this.nodeId,status:"running",pausedAt:void 0,context:e.initialContext??{},pending:e.initialPending,entityRef:e.entityRef,history:[],parentRunId:e.parentRunId??null,parentNodeId:e.parentNodeId??null,rootRunId:e.rootRunId??r,depth:e.depth??0,joinCounters:{},createdAt:t,updatedAt:t};return this.appendSnapshot(s),this.writeIndex(e.entityRef,s.id),s}get(e){let t=this.runPath(e);if(!_(t))return null;let r=ue(t,"utf-8").trim();if(!r)return null;let s=null;for(let o of r.split(`
|
|
4
|
+
`)){let i;try{i=JSON.parse(o)}catch{continue}if(i.v===2){if(i.kind==="snapshot")s=i.run;else if(i.kind==="exec"&&s&&s.id===i.runId){let a=s;s={...a,history:[...a.history,i.entry],pending:i.pending,status:i.status??a.status,pausedAt:i.pausedAt===null?void 0:i.pausedAt??a.pausedAt,context:i.context??a.context,joinCounters:i.joinCounters??a.joinCounters,updatedAt:i.entry.at}}}}return s}getActiveByEntity(e){let t=this.indexPath(e);if(!_(t))return null;try{return JSON.parse(ue(t,"utf-8")).runId}catch{return null}}getHomeNodeByEntity(e){let t=this.indexPath(e);if(!_(t))return null;try{return JSON.parse(ue(t,"utf-8")).homeNode}catch{return null}}recordExecution(e){let t=this.get(e.runId);if(!t)return null;if(t.history.some(o=>o.idempotencyKey===e.entry.idempotencyKey))return t;let r={v:2,kind:"exec",runId:e.runId,entry:e.entry,pending:e.nextPending,status:e.status,pausedAt:e.pausedAt===null?null:e.pausedAt,context:e.context,joinCounters:e.joinCounters};Me(this.runPath(e.runId),JSON.stringify(r)+`
|
|
5
|
+
`);let s=e.status??t.status;return s==="completed"||s==="failed"||s==="canceled"?this.clearIndex(t.entityRef):this.writeIndex(t.entityRef,t.id),this.get(e.runId)}setStatus(e,t){let r=this.get(e);if(!r)return null;let s={...r,status:t,updatedAt:new Date().toISOString()};return this.appendSnapshot(s),t!=="running"&&t!=="paused"&&this.clearIndex(s.entityRef),s}list(e={}){if(!_(this.runsDir))return[];let t=He(this.runsDir).filter(s=>s.endsWith(".jsonl")).map(s=>({name:s,mtime:Ue(N(this.runsDir,s)).mtimeMs})).sort((s,o)=>o.mtime-s.mtime),r=[];for(let s of t){let o=this.get(s.name.replace(/\.jsonl$/,""));if(o&&!(e.workflowId&&o.workflowId!==e.workflowId)&&(r.push(o),e.limit&&r.length>=e.limit))break}return r}prune(e){if(!_(this.runsDir))return 0;let t=Date.now()-e.maxDays*24*60*60*1e3,r=[];for(let o of He(this.runsDir)){if(!o.endsWith(".jsonl"))continue;let i=N(this.runsDir,o),a=Ue(i).mtimeMs,d=this.get(o.replace(/\.jsonl$/,""));d&&(d.status==="running"||d.status==="paused"||r.push({file:i,run:d,mtime:a}))}r.sort((o,i)=>i.mtime-o.mtime);let s=0;for(let o=0;o<r.length;o++){let i=r[o],a=i.mtime<t,d=o>=e.maxRuns;if(a||d)try{qe(i.file),s++}catch{}}return s}appendSnapshot(e){let t={v:2,kind:"snapshot",run:e};Me(this.runPath(e.id),JSON.stringify(t)+`
|
|
6
|
+
`)}writeIndex(e,t){Ot(this.indexPath(e),JSON.stringify({runId:t,homeNode:this.nodeId,updatedAt:new Date().toISOString()},null,2)+`
|
|
7
|
+
`)}clearIndex(e){let t=this.indexPath(e);if(_(t))try{qe(t)}catch{}}};import{existsSync as Be,mkdirSync as Ft,readFileSync as Ct,unlinkSync as Mt,writeFileSync as Lt}from"fs";import{resolve as fe}from"path";import{z as O}from"zod";var Ke=O.object({version:O.literal(1),nodes:O.record(O.object({x:O.number(),y:O.number()}))}),ge=class{baseDir;layoutsDir;constructor(e={}){this.baseDir=e.baseDir??fe(process.cwd(),".agentx/workflows"),this.layoutsDir=fe(this.baseDir,"_layouts"),Ft(this.layoutsDir,{recursive:!0})}pathFor(e){let t=e.replace(/[^a-zA-Z0-9._-]/g,"_");return fe(this.layoutsDir,`${t}.json`)}get(e){let t=this.pathFor(e);if(!Be(t))return null;try{let r=Ke.safeParse(JSON.parse(Ct(t,"utf-8")));return r.success?r.data:null}catch{return null}}save(e,t){let r=Ke.parse(t);return Lt(this.pathFor(e),JSON.stringify(r,null,2)+`
|
|
8
|
+
`),r}sync(e,t){let r=this.get(e);if(!r)return null;let s={};for(let i of t)r.nodes[i]&&(s[i]=r.nodes[i]);let o={version:1,nodes:s};return this.save(e,o)}delete(e){let t=this.pathFor(e);return Be(t)?(Mt(t),!0):!1}};function Ye(n){return new Set(n.toLowerCase().replace(/[^a-z0-9\s]/g," ").split(/\s+/).filter(e=>e.length>3))}function Ht(n,e){if(n.size===0||e.size===0)return 0;let t=0;for(let r of n)e.has(r)&&t++;return t/Math.max(n.size,e.size)}function Ut(n,e){let t=Ye(n.message),r=e.filter(o=>o.state==="active"&&(o.status??"active")==="active"),s=null;for(let o of r){let i=0,a=[];if(o.ownerAgent&&o.ownerAgent===n.agentId&&(i+=.25,a.push("owner-agent")),n.channel&&o.tags.includes(n.channel)&&(i+=.15,a.push("channel-tag")),n.intentPath?.length&&o.intentPath.length){let c=n.intentPath.filter((f,g)=>o.intentPath[g]===f).length;c>0&&(i+=Math.min(.3,c*.1),a.push("intent-path"))}let d=[o.id,o.title,o.description??"",...o.tags].join(" "),l=Ht(t,Ye(d));l>0&&(i+=Math.min(.35,l),a.push("text-similarity")),(o.matchCount??0)>0&&(i+=Math.min(.1,o.matchCount*.01),a.push("prior-matches"));let p=Math.max(0,Math.min(1,i));(!s||p>s.confidence)&&(s={workflow:o,confidence:p,reasons:a})}return s&&s.confidence>0?s:null}function me(n){let{workflow:e,fromNodeId:t,selectedPort:r}=n,s=e.nodes.find(a=>a.id===t);if(!s)return{nextPending:[]};let o=e.edges.filter(a=>a.from===t);return{nextPending:(s.type==="branch"?o.filter(a=>(a.fromPort??"")===(r??"")):o).map(a=>a.to)}}function he(n,e){let t=n.config.cases??[],r=n.config.default??"";for(let s of t)if(qt(s.when,e))return s.to;return r}function qt(n,e){let t=n.params??{},r=String(t.path??""),s=Z(e,r);switch(n.kind){case"equals":return s===t.value;case"contains":return typeof s=="string"?s.includes(String(t.value??"")):Array.isArray(s)?s.some(o=>o===t.value):!1;case"matches":{if(typeof s!="string")return!1;try{return new RegExp(String(t.regex??"")).test(s)}catch{return!1}}case"exists":return s!=null&&s!=="";default:return!1}}function Z(n,e){if(!e)return;let t=e.split("."),r=n;for(let s of t){if(r==null||typeof r!="object")return;r=r[s]}return r}function X(n){let e=n.nodes.find(r=>r.type.startsWith("trigger."));if(!e)return null;let t=n.edges.filter(r=>r.from===e.id).map(r=>r.to);return{triggerId:e.id,pending:t}}function D(n,e){return n.nodes.find(t=>t.id===e)}function Bt(n,e,t){let r=n.split(".");if(r[0]==="env"){if(r.length!==2)return"";let o=r[1];if(!t.has(o))return"";let i=process.env[o];return typeof i=="string"?i:""}let s=e;for(let o of r){if(s==null||typeof s!="object")return"";s=s[o]}return s==null?"":typeof s=="object"?JSON.stringify(s):String(s)}function E(n,e,t={}){let r=new Set(t.envAllow||[]);return n.replace(/\{\{\s*([a-zA-Z_][a-zA-Z0-9_.-]*)\s*\}\}/g,(s,o)=>Bt(o,e,r))}function h(n,e,t={}){let r={};for(let[s,o]of Object.entries(n))typeof o=="string"?r[s]=E(o,e,t):Array.isArray(o)?r[s]=o.map(i=>typeof i=="string"?E(i,e,t):i):o&&typeof o=="object"?r[s]=h(o,e,t):r[s]=o;return r}var we=class{active=new Map;waiters=new Map;async acquire(e,t){let r=this.active.get(e)??0;if(r<t){this.active.set(e,r+1);return}return new Promise(s=>{let o=this.waiters.get(e)??[];o.push({resolve:()=>{this.active.set(e,(this.active.get(e)??0)+1),s()}}),this.waiters.set(e,o)})}release(e){let t=this.active.get(e)??0;this.active.set(e,Math.max(0,t-1));let r=this.waiters.get(e);r&&r.length>0&&r.shift().resolve()}stats(e){return{active:this.active.get(e)??0,waiting:this.waiters.get(e)?.length??0}}},Q=new we;function Je(n,e){return`${n}:${e}`}var F=async n=>({output:n.run.context[n.node.id]??{}}),Kt=async n=>{let e=n.node.config,t=String(e.agentId??""),r=String(e.prompt??"");if(!t)return{error:`agent node "${n.node.id}" missing config.agentId`};let s=E(r,n.run.context,{envAllow:n.workflow.envAllow}),o=typeof e.timeoutMinutes=="number"?e.timeoutMinutes:void 0,i=typeof e.maxConcurrent=="number"&&e.maxConcurrent>0?e.maxConcurrent:null,a=i?Je(n.workflow.id,n.node.id):null;if(a&&i){let l=Q.stats(a);l.active>=i&&n.log(`[node:${n.node.id}] gated: ${l.active} active (cap ${i}), ${l.waiting+1} waiting`),await Q.acquire(a,i)}let d=Date.now();try{let l=await n.agents.execute({agentId:t,message:s,workflowRunId:n.run.id,timeoutMinutes:o}),p=Date.now()-d;if(l.error){let g=l.errorKind??"unknown";return n.log(`[node:${n.node.id}] agent "${t}" failed (${g}): ${l.error}`),{error:`[${g}] ${l.error}`}}let f=String(e.resultParser??"noqta-result-token")==="json"?hn(l.content):Ge(l.content);return{output:{reply:l.content,result:f.result,json:f.json,taskId:l.taskId,durationMs:p}}}catch(l){return n.log(`[node:${n.node.id}] agent "${t}" threw: ${l.message}`),{error:l.message}}finally{a&&Q.release(a)}},Yt=async n=>{let e=he(n.node,n.run.context);return e?{output:{port:e},port:e}:(n.log(`[node:${n.node.id}] branch matched no case and no default port`),{output:{port:null}})},Jt=async n=>{let e=h(n.node.config,n.run.context,{envAllow:n.workflow.envAllow}),t=String(e.channel??""),r=String(e.chatId??""),s=String(e.text??"");if(!t||!r){let a=String(n.node.config.chatId??""),d=String(n.node.config.channel??""),l=[];return t||l.push(`channel config="${d}" rendered=""`),r||l.push(`chatId config="${a}" rendered=""`),(/\{\{\s*env\./.test(a)||/\{\{\s*env\./.test(d))&&l.push("(env.* templates only resolve for names listed in workflow.envAllow and actually set in process.env)"),{error:`action.send "${n.node.id}" needs channel + chatId \u2014 ${l.join("; ")}`}}let o=ke(n,e.accountId),i=n.channels[t];if(i?.send)try{return{output:{messageId:await i.send({channel:t,chatId:r,text:s,...o?{accountId:o}:{}})??null}}}catch(a){return{error:`action.send failed: ${a.message}`}}if(n.forwardChannelSend)try{return{output:{messageId:(await n.forwardChannelSend({channel:t,chatId:r,text:s,accountId:o})).messageId,viaMesh:!0}}}catch(a){return n.log(`[node:${n.node.id}] mesh-forward of "${t}" send failed: ${a.message}`),{error:`action.send via mesh failed: ${a.message}`}}return n.log(`[node:${n.node.id}] adapter "${t}" not available locally and no mesh forwarder`),{error:`channel "${t}" not available (no local adapter, no mesh route)`}},zt=async n=>{let e=h(n.node.config,n.run.context,{envAllow:n.workflow.envAllow}),t=String(e.channel??"gitlab"),r=String(e.project??""),s=String(e.title??""),o=String(e.description??""),i=Array.isArray(e.labels)?e.labels.map(String):[],a=Array.isArray(e.assignees)?e.assignees.map(String):[];if(!r||!s)return{error:`action.createIssue "${n.node.id}" needs project + title`};let d=be(n),l=n.channels[t];if(!l?.createIssue)return{error:`channel "${t}" does not support createIssue`};try{let p=await l.createIssue({project:r,title:s,description:o,labels:i,assignees:a,agentId:d});return p?{output:{issue:{iid:p.iid,url:p.url,webUrl:p.url}}}:{error:"createIssue returned null (see adapter log)"}}catch(p){return{error:`action.createIssue failed: ${p.message}`}}},Gt=async()=>({output:{}}),Vt=async n=>{let e=n.node.config,t=String(e.name??n.node.id),r=e.waitFor??{},s=e.resumeMatch??{};return{paused:!0,pausedAt:{kind:"checkpoint",nodeId:n.node.id,checkpointName:t,resumeMatch:{...r,...s}}}},Zt=async n=>{let e=h(n.node.config,n.run.context,{envAllow:n.workflow.envAllow}),t=String(e.channel??"gitlab"),r=String(e.project??""),s=String(e.iid??""),o=Array.isArray(e.add)?e.add.map(String):[],i=Array.isArray(e.remove)?e.remove.map(String):[];if(!r||!s)return{error:`action.setLabel "${n.node.id}" needs project + iid (try {{trigger.project}} / {{trigger.issue.iid}})`};if(o.length===0&&i.length===0)return{output:{skipped:!0,reason:"no labels to add or remove"}};let a=n.channels[t];if(!a?.setLabels)return{error:`channel "${t}" does not support setLabels`};let d=be(n),l=e.kind==="merge_request"?"merge_request":"issue";return{output:{labels:await a.setLabels({project:r,kind:l,iid:s,add:o,remove:i,agentId:d})??[],add:o,remove:i}}},Xt=async n=>{let e=h(n.node.config,n.run.context,{envAllow:n.workflow.envAllow}),t=String(e.channel??"gitlab"),r=String(e.project??""),s=String(e.iid??"");if(!r||!s)return{error:`action.readLabel "${n.node.id}" needs project + iid`};let o=n.channels[t];if(!o?.getLabels)return{error:`channel "${t}" does not support getLabels`};let i=e.kind==="merge_request"?"merge_request":"issue";return{output:{labels:await o.getLabels({project:r,kind:i,iid:s})??[]}}},Qt=async n=>{let e=h(n.node.config,n.run.context,{envAllow:n.workflow.envAllow}),t=String(e.channel??""),r=String(e.chatId??""),s=String(e.messageId??""),o=String(e.emoji??"\u{1F440}");if(!t||!r||!s)return{error:`action.react "${n.node.id}" needs channel + chatId + messageId`};let i=ke(n,e.accountId),a=n.channels[t];if(!a?.react)return{error:`channel "${t}" does not support react`};try{return await a.react(r,s,o,i),{output:{emoji:o}}}catch(d){return{error:`action.react failed: ${d.message}`}}},en=async n=>{let e=h(n.node.config,n.run.context,{envAllow:n.workflow.envAllow}),t=String(e.channel??""),r=String(e.chatId??""),s=String(e.messageId??""),o=String(e.text??""),i=e.parseMode?String(e.parseMode):void 0;if(!t||!r||!s||!o)return{error:`action.editMessage "${n.node.id}" needs channel + chatId + messageId + text`};let a=ke(n,e.accountId),d=n.channels[t];if(!d?.editMessage)return{error:`channel "${t}" does not support editMessage`};try{return{output:{edited:await d.editMessage(r,s,o,i,a)}}}catch(l){return{error:`action.editMessage failed: ${l.message}`}}},tn=async n=>{let e=h(n.node.config,n.run.context,{envAllow:n.workflow.envAllow}),t=String(e.channel??"gitlab"),r=String(e.chatId??""),s=Number(e.durationMs??0);if(!r||!s)return{error:`action.logTime "${n.node.id}" needs chatId + durationMs`};let o=n.channels[t];if(!o?.logTimeSpent)return{error:`channel "${t}" does not support logTimeSpent`};let i=be(n);try{return await o.logTimeSpent(r,s,i),{output:{durationMs:s}}}catch(a){return{error:`action.logTime failed: ${a.message}`}}},nn=async n=>{let e=h(n.node.config,n.run.context,{envAllow:n.workflow.envAllow}),t=String(e.url??"");if(!t)return{error:`action.callHTTP "${n.node.id}" needs url`};let r=String(e.method??"POST").toUpperCase(),s=e.headers&&typeof e.headers=="object"?e.headers:{"Content-Type":"application/json"},o;e.body!==void 0&&(o=typeof e.body=="string"?e.body:JSON.stringify(e.body));try{let i=await fetch(t,{method:r,headers:s,body:o}),a=await i.text(),d=a;try{d=JSON.parse(a)}catch{}return{output:{ok:i.ok,status:i.status,body:d}}}catch(i){return{error:`action.callHTTP failed: ${i.message}`}}},rn=async n=>{let e=h(n.node.config,n.run.context,{envAllow:n.workflow.envAllow}),t=String(e.actionId??e.id??"");if(!t)return{error:`action.run "${n.node.id}" needs actionId`};let r=e.inputs&&typeof e.inputs=="object"?e.inputs:{},{ActionStore:s}=await import("./store-I3HEWVLQ.js"),{runAction:o}=await import("./runner-BFYDWPUO.js"),i=new s().get(t);if(!i)return{error:`action.run "${n.node.id}": no action "${t}" in registry`};try{let a=await o(i,r),d={ok:a.ok,output:a.output,status:a.status,durationMs:a.durationMs};return a.errors!==void 0&&(d.errors=a.errors),a.ok?{output:d}:{output:d,error:`action "${t}" failed (status=${a.status})`}}catch(a){return{error:`action.run failed: ${a.message}`}}},sn=async n=>{let e=h(n.node.config,n.run.context,{envAllow:n.workflow.envAllow}),t=String(e.name??"");if(!t)return{error:`action.builtin "${n.node.id}" needs a "name" (e.g. "http.fetch")`};let r=e.input&&typeof e.input=="object"?e.input:{},{runBuiltin:s,getBuiltin:o}=await import("./builtin-FRKAQMFS.js");if(!o(t))return{error:`action.builtin "${n.node.id}": unknown built-in "${t}"`};try{return{output:await s(t,r)}}catch(i){let a=i?.name==="ZodError"?`validation: ${i.message}`:i?.message||String(i);return{error:`action.builtin "${t}" failed: ${a}`}}},on=async n=>{let e=n.node.config,t=n.run.context;return typeof e.path=="string"&&e.path?{output:{value:Z(t,e.path)}}:e.template&&typeof e.template=="object"?{output:h(e.template,t,{envAllow:n.workflow.envAllow})}:{output:{}}};function an(n){if(n!=null){if(typeof n=="number")return new Date(Date.now()+n*6e4).toISOString();if(typeof n=="object"&&n&&"minutes"in n){let e=Number(n.minutes);if(Number.isFinite(e))return new Date(Date.now()+e*6e4).toISOString()}if(typeof n=="string"){let e=/^P(?:([0-9]+)D)?(?:T(?:([0-9]+)H)?(?:([0-9]+)M)?(?:([0-9]+)S)?)?$/.exec(n);if(e){let t=Number(e[1]??0),r=Number(e[2]??0),s=Number(e[3]??0),o=Number(e[4]??0),i=((t*24+r)*60+s)*6e4+o*1e3;if(i>0)return new Date(Date.now()+i).toISOString()}}}}var dn=async n=>{let e=n.node.config,t=h(e,n.run.context,{envAllow:n.workflow.envAllow}),r=String(t.workflowId??"");if(!r)return{error:`subProcess "${n.node.id}" missing config.workflowId`};let s=n.run.depth??0,o=n.workflow.maxChildDepth??5;if(s+1>=o)return{error:`max child workflow depth ${o} exceeded at ${n.workflow.id} \u2192 ${r} (parent depth ${s})`};let i=e.inputMap,a={};if(i==="*")a={...n.run.context};else if(i&&typeof i=="object")for(let[d,l]of Object.entries(i))l&&typeof l=="object"&&!Array.isArray(l)?a[d]=h(l,n.run.context,{envAllow:n.workflow.envAllow}):typeof l=="string"&&(a[d]={value:E(l,n.run.context,{envAllow:n.workflow.envAllow})});return{paused:!0,pausedAt:{kind:"subProcess",nodeId:n.node.id,childRunId:"",childWorkflowId:r},spawnChild:{workflowId:r,input:a}}},ln=F,cn=async n=>{let e=n.node.config,t=h(e,n.run.context,{envAllow:n.workflow.envAllow}),r=String(t.name??"");if(!r)return{error:`signal.emit "${n.node.id}" missing config.name`};let s=t.scope==="global"?"global":"workflow",o=t.payload&&typeof t.payload=="object"?t.payload:{};return{output:{emittedAt:new Date().toISOString(),name:r,scope:s,payload:o},emitSignal:{name:r,scope:s,payload:o}}},un=async n=>{let e=n.node.config,t=h(e,n.run.context,{envAllow:n.workflow.envAllow}),r=String(t.name??"");if(!r)return{error:`signal.wait "${n.node.id}" missing config.name`};let s=t.scope==="global"?"global":"workflow",o=e.match&&typeof e.match=="object"?e.match:{};return{paused:!0,pausedAt:{kind:"signalWait",nodeId:n.node.id,signalName:r,match:o,scope:s}}},pn=async n=>{let e=n.node.config,t=h(e,n.run.context,{envAllow:n.workflow.envAllow}),r=an(t.after??e.after);return r?{paused:!0,pausedAt:{kind:"timerWait",nodeId:n.node.id,fireAt:r}}:{error:`timer.boundary "${n.node.id}" needs config.after (duration or minutes)`}},fn=async()=>({output:{}}),gn=async n=>{let e=n.node.config,r=(Array.isArray(e.inputs)?e.inputs:[]).map(i=>typeof i=="string"?E(i,n.run.context,{envAllow:n.workflow.envAllow}):i),s=Array.isArray(e.rules)?e.rules:[];for(let i of s){let a=Array.isArray(i.when)?i.when:[];if(a.length!==r.length)continue;let d=!0;for(let l=0;l<a.length;l++)if(!mn(a[l],r[l])){d=!1;break}if(d)return{output:{...i.output??{},matchedPort:i.to??""},port:i.to}}let o=e.default;return{output:{...o?.output??{},matchedPort:o?.to??""},port:o?.to}};function mn(n,e){if(n==null||n==="*")return!0;let t=String(n),r=typeof e=="number"?e:String(e??""),s=/^(>=|<=|>|<)\s*(-?\d+(?:\.\d+)?)$/.exec(t);if(s){let o=s[1],i=Number(s[2]),a=typeof r=="number"?r:Number(r);if(Number.isNaN(a))return!1;switch(o){case">":return a>i;case"<":return a<i;case">=":return a>=i;case"<=":return a<=i}}if(t.startsWith("!="))return String(r)!==t.slice(2);if(t.startsWith("/")&&t.endsWith("/")&&t.length>2)try{return new RegExp(t.slice(1,-1)).test(String(r))}catch{return!1}return String(r)===t}var ze={"trigger.channel":F,"trigger.manual":F,"trigger.cron":F,"trigger.hook":F,"trigger.form":ln,agent:Kt,branch:Yt,transform:on,"gateway.parallel":fn,rule:gn,"action.send":Jt,"action.createIssue":zt,"action.setLabel":Zt,"action.readLabel":Xt,"action.react":Qt,"action.editMessage":en,"action.logTime":tn,"action.callHTTP":nn,"action.run":rn,"action.builtin":sn,subProcess:dn,"signal.emit":cn,"signal.wait":un,"timer.boundary":pn,checkpoint:Vt,end:Gt};function ye(n){return ze[n]}function ke(n,e){if(typeof e=="string"&&e)return e;let t=n.workflow.nodes.find(o=>o.type.startsWith("trigger."));if(!t)return;let s=n.run.context[t.id]?.accountId;return typeof s=="string"&&s?s:void 0}function be(n){for(let e=n.run.history.length-1;e>=0;e--){let t=n.run.history[e],r=n.workflow.nodes.find(s=>s.id===t.nodeId);if(r?.type==="agent")return r.config.agentId??void 0}}function Ge(n){if(!n)return{};let e=n.match(/\bRESULT:\s*([a-z][a-z0-9_-]*)/i);if(e)return{result:e[1].toLowerCase()};let t=n.match(/\[(APPROVED|REJECTED|CHANGES[-_]REQUESTED|DONE|FAILED|SKIPPED)\]/i);return t?{result:t[1].toLowerCase().replace("_","-")}:{}}function hn(n){let{result:e}=Ge(n);if(!n)return{result:e};let t=n.match(/```(?:json)?\s*([\s\S]*?)```/);if(!t)return{result:e};try{return{result:e,json:JSON.parse(t[1])}}catch{return{result:e}}}var C=class{handlers=new Set;subscribe(e){return this.handlers.add(e),()=>this.handlers.delete(e)}emit(e){for(let t of this.handlers)try{Promise.resolve(t(e)).catch(()=>{})}catch{}}};function ee(n,e){if(n.name!==e.name||n.scope==="workflow"&&n.workflowId!==e.workflowId)return!1;for(let[t,r]of Object.entries(n.match??{}))if(!(r===void 0||r===""||r==="*")&&e.payload[t]!==r)return!1;return!0}function wn(n,e,t=Date.now){return{ts:t(),source:"workflow",sourceEventId:`${n.workflowId}:${n.eventId}`,project:n.project,subject:`workflow:${n.workflowId}:entity:${n.entityRef.backend}:${n.entityRef.id}`,intent:`workflow.${n.triggerSource}`,rawJson:e}}function yn(n){return n.claimed&&n.runId?{agentId:`workflow-run:${n.runId}`,outcome:"dispatched",reason:n.reason??"claimed"}:n.claimed&&!n.runId?{agentId:null,outcome:"halted",reason:n.reason??"claimed-but-no-run"}:{agentId:null,outcome:"halted",reason:n.reason??"not-claimed"}}function kn(n){return{decidedBy:"workflow-dispatcher",decide:()=>({agentId:n.agentId,outcome:n.outcome==="deduped"?"halted":n.outcome,reason:n.outcome==="deduped"?`legacy-dedup: ${n.reason??""}`.trim():n.reason??null})}}function Ve(n,e,t,r,s=Date.now){let o=wn(e,t,s),i=yn(r),a=kn(i),d=De(n,o,a,s);return Pe(n,"workflow",d,i,s),d}function Ze(n,e,t=Ae()){return n.prepare(`
|
|
9
|
+
INSERT INTO task_traces (
|
|
10
|
+
task_id, agent_id, channel, chat_id, workflow_run_id, workflow_id,
|
|
11
|
+
workflow_node_id, intent_event_id, intent_decided_by, resume_session_id,
|
|
12
|
+
model, status, started_at, message_preview, original_message
|
|
13
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'in-flight', ?, ?, ?)
|
|
14
|
+
`).run(t,e.agentId,e.channel??null,e.chatId??null,e.workflowRunId??null,e.workflowId??null,e.workflowNodeId??null,e.intentEventId??null,e.intentDecidedBy??null,e.resumeSessionId??null,e.model??null,Date.now(),e.messagePreview??null,e.originalMessage??null),t}function Xe(n,e,t){let r=Date.now();n.prepare(`
|
|
15
|
+
UPDATE task_traces SET
|
|
16
|
+
status = ?,
|
|
17
|
+
final_session_id = ?,
|
|
18
|
+
input_tokens = ?,
|
|
19
|
+
output_tokens = ?,
|
|
20
|
+
cache_read_tokens = ?,
|
|
21
|
+
cache_create_tokens = ?,
|
|
22
|
+
error = ?,
|
|
23
|
+
final_response = COALESCE(?, final_response),
|
|
24
|
+
finished_at = ?,
|
|
25
|
+
duration_ms = ? - started_at
|
|
26
|
+
WHERE task_id = ?
|
|
27
|
+
`).run(t.status,t.finalSessionId??null,t.inputTokens??null,t.outputTokens??null,t.cacheReadTokens??null,t.cacheCreateTokens??null,t.error??null,t.finalResponse??null,r,r,e)}function Cr(n,e,t){let r=Date.now();return n.transaction(()=>{let i=(n.prepare("SELECT MAX(seq) AS max_seq FROM task_trace_steps WHERE task_id = ?").get(e)?.max_seq??-1)+1;return n.prepare(`
|
|
28
|
+
INSERT INTO task_trace_steps (
|
|
29
|
+
task_id, seq, name, action, status, input_summary, output_summary, error, ms, started_at
|
|
30
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
31
|
+
`).run(e,i,t.name,t.action??null,t.status??null,t.inputSummary??null,t.outputSummary??null,t.error??null,t.ms??null,r),i})()}function Qe(n){return{taskId:n.task_id,agentId:n.agent_id,channel:n.channel??null,chatId:n.chat_id??null,workflowRunId:n.workflow_run_id??null,workflowId:n.workflow_id??null,workflowNodeId:n.workflow_node_id??null,intentEventId:n.intent_event_id??null,intentDecidedBy:n.intent_decided_by??null,resumeSessionId:n.resume_session_id??null,finalSessionId:n.final_session_id??null,model:n.model??null,status:n.status,startedAt:n.started_at,finishedAt:n.finished_at??null,durationMs:n.duration_ms??null,inputTokens:n.input_tokens??null,outputTokens:n.output_tokens??null,cacheReadTokens:n.cache_read_tokens??null,cacheCreateTokens:n.cache_create_tokens??null,error:n.error??null,messagePreview:n.message_preview??null,originalMessage:n.original_message??null,finalResponse:n.final_response??null}}function bn(n){return{taskId:n.task_id,seq:n.seq,name:n.name,action:n.action??null,status:n.status??null,inputSummary:n.input_summary??null,outputSummary:n.output_summary??null,error:n.error??null,ms:n.ms??null,startedAt:n.started_at}}function et(n,e){let t=n.prepare("SELECT * FROM task_traces WHERE task_id = ?").get(e);if(!t)return null;let r=n.prepare("SELECT * FROM task_trace_steps WHERE task_id = ? ORDER BY seq").all(e);return{task:Qe(t),steps:r.map(bn)}}function Mr(n){let e=Date.now();return n.prepare(`
|
|
32
|
+
UPDATE task_traces
|
|
33
|
+
SET status = 'canceled',
|
|
34
|
+
error = 'daemon-restart (orphaned in-flight)',
|
|
35
|
+
finished_at = ?
|
|
36
|
+
WHERE status = 'in-flight'
|
|
37
|
+
`).run(e).changes??0}function tt(n,e={}){let t=[],r=[];e.agentId&&(t.push("agent_id = ?"),r.push(e.agentId)),e.channel&&(t.push("channel = ?"),r.push(e.channel)),e.chatId&&(t.push("chat_id = ?"),r.push(e.chatId)),e.workflowRunId&&(t.push("workflow_run_id = ?"),r.push(e.workflowRunId)),e.status&&(t.push("status = ?"),r.push(e.status)),e.since!==void 0&&(t.push("started_at >= ?"),r.push(e.since)),e.until!==void 0&&(t.push("started_at <= ?"),r.push(e.until));let s=Math.max(1,Math.min(e.limit??100,1e3)),o=`
|
|
38
|
+
SELECT * FROM task_traces
|
|
39
|
+
${t.length>0?"WHERE "+t.join(" AND "):""}
|
|
40
|
+
ORDER BY started_at DESC
|
|
41
|
+
LIMIT ?
|
|
42
|
+
`;return r.push(s),n.prepare(o).all(...r).map(Qe)}var vn=4e3,In=8,ve=class{store;runs;nodeId;forwarder;channels;agents;log;timers;signals;events;ackTypingTimers=new Map;commitChains=new Map;constructor(e){this.store=e.store,this.runs=e.runs,this.nodeId=e.nodeId,this.forwarder=e.forwarder,this.channels=e.channels,this.agents=e.agents,this.log=e.log??(()=>{}),this.timers=e.timers??new oe({log:t=>this.log(t)}),this.signals=e.signals??new C,this.events=e.events,this.timers.onFire(t=>this.resumeFromTimer(t)),this.signals.subscribe(t=>this.resumeFromSignal(t))}emitRunEvent(e){if(this.events)try{this.events.publish({kind:"run",runId:e.runId,workflowId:e.workflowId,nodeId:e.nodeId,phase:e.phase,status:e.status,note:e.note,homeNode:e.homeNode})}catch{}}async commit(e,t){let s=(this.commitChains.get(e)??Promise.resolve()).then(()=>t(),()=>t());return this.commitChains.set(e,s.then(()=>{},()=>{})),s}emitSignal(e){let t={name:e.name,scope:e.scope??"global",workflowId:e.workflowId??"",payload:e.payload??{},emittedAt:new Date().toISOString()};if(this.signals.emit(t),this.events)try{this.events.publish({kind:"signal",name:t.name,scope:t.scope,workflowId:t.workflowId||void 0,payload:t.payload})}catch{}return t}async resumeFromSignal(e){let t=this.runs.list({limit:500});for(let r of t){if(r.status!=="paused"||!r.pausedAt||r.pausedAt.kind!=="signalWait")continue;let s={name:r.pausedAt.signalName,scope:r.pausedAt.scope,workflowId:r.workflowId,match:r.pausedAt.match??{}};if(!ee(s,e))continue;let o=this.store.list().find(i=>i.id===r.workflowId);o&&(await this.commit(r.id,()=>{let i=this.runs.get(r.id);if(!i||i.status!=="paused"||!i.pausedAt||i.pausedAt.kind!=="signalWait"||!ee({name:i.pausedAt.signalName,scope:i.pausedAt.scope,workflowId:i.workflowId,match:i.pausedAt.match??{}},e))return;let a=D(o,i.pausedAt.nodeId),d=a?o.edges.filter(p=>p.from===a.id).map(p=>p.to):[],l={receivedAt:new Date().toISOString(),name:e.name,payload:e.payload};this.runs.recordExecution({runId:i.id,entry:{at:l.receivedAt,nodeId:i.pausedAt.nodeId,inputKeys:[],status:"resumed",output:l,idempotencyKey:T(i.id,i.pausedAt.nodeId,`signal:${e.name}:${e.emittedAt}`)},nextPending:d,status:"running",pausedAt:null,context:{...i.context,[i.pausedAt.nodeId]:l}}),this.log(`[workflow:${o.id}] run ${i.id} resumed from signal "${e.name}"`),this.emitRunEvent({runId:i.id,workflowId:o.id,nodeId:i.pausedAt.nodeId,phase:"resumed",status:"running",note:`signal:${e.name}`})}),this.walk(o,r.id,`signal:${e.name}`).catch(i=>this.log(`[workflow:${o.id}] walk-after-signal failed: ${i.message}`)))}}async resumeFromTimer(e){let t=this.store.list().find(r=>r.id===(this.runs.get(e.runId)?.workflowId??""));t&&(await this.commit(e.runId,()=>{let r=this.runs.get(e.runId);if(!r||r.status!=="paused"||!r.pausedAt||r.pausedAt.kind!=="timerWait"||r.pausedAt.nodeId!==e.nodeId)return;let s=D(t,e.nodeId),o=s?t.edges.filter(d=>d.from===s.id).map(d=>d.to):[],i=new Date().toISOString(),a={firedAt:i,scheduledFor:e.fireAt};this.runs.recordExecution({runId:r.id,entry:{at:i,nodeId:e.nodeId,inputKeys:[],status:"resumed",output:a,idempotencyKey:T(r.id,e.nodeId,`timer:${e.id}`)},nextPending:o,status:"running",pausedAt:null,context:{...r.context,[e.nodeId]:a}}),this.log(`[workflow:${t.id}] run ${r.id} resumed from timer "${e.nodeId}" (fired ${i})`),this.emitRunEvent({runId:r.id,workflowId:t.id,nodeId:e.nodeId,phase:"resumed",status:"running",note:`timer:${e.id}`})}),this.walk(t,e.runId,`timer:${e.id}`).catch(r=>this.log(`[workflow:${t.id}] walk-after-timer failed: ${r.message}`)))}async dispatchWorkflow(e){let t=this.store.list().find(i=>i.id===e.workflowId);if(!t)return this.log(`[workflows] dispatchWorkflow: workflow "${e.workflowId}" not found`),{claimed:!1,run:null};let s=t.nodes.find(i=>i.type.startsWith("trigger."))?.config??{},o={source:e.trigger?.source??s.source??"hook",project:e.trigger?.project,repo:e.trigger?.repo,chat:e.trigger?.chat,labels:e.trigger?.labels};return this.dispatchOne(t,e.entityRef,e.event,o)}startChannelAck(e,t){let r=String(t.channel??""),s=String(t.chatId??"");if(!r||!s)return;let o=this.channels[r];if(!o)return;let i=typeof t.accountId=="string"?t.accountId:void 0,a=t.event,d=typeof a?.id=="string"?a.id:a?.id!=null?String(a.id):void 0;if(d&&o.react)try{Promise.resolve(o.react(s,d,"\u{1F440}",i)).catch(()=>{})}catch{}if(this.ackTypingTimers.has(e)||!o.sendTyping)return;let l=()=>{try{Promise.resolve(o.sendTyping(s,i)).catch(()=>{})}catch{}};l();let p=setInterval(l,vn);this.ackTypingTimers.set(e,p)}stopChannelAck(e){let t=this.ackTypingTimers.get(e);t&&(clearInterval(t),this.ackTypingTimers.delete(e))}async dispatch(e){let t=this.matchByTrigger(e.trigger,e.fromRemote),s=t.some(a=>a.fanOut)?t:t.slice(0,1),o=[],i=[];for(let a of s){let d=await this.dispatchOne(a,e.entityRef,e.event,e.trigger);d.claimed&&o.push(a),d.run&&i.push(d.run)}return!e.fromRemote&&this.forwarder?.broadcastTrigger&&Promise.resolve(this.forwarder.broadcastTrigger({trigger:e.trigger,entityRef:e.entityRef,event:e.event})).catch(a=>this.log(`[mesh-broadcast] ${a?.message??a}`)),{claimed:o,runs:i}}matchByTrigger(e,t){let r=this.store.list(),s=[];for(let o of r){if(o.state&&o.state!=="active")continue;if(t){if(!o.mesh?.allowRemote)continue;let l=o.mesh.peers;if(l&&l.length>0&&!l.includes(t.peer))continue}let i=o.nodes.find(l=>l.type.startsWith("trigger."));if(!i)continue;let a=i.config;if(a.source!==e.source||o.project&&e.project&&o.project!==e.project)continue;let d=a.filter;if(d){if(d.project&&d.project!=="*"&&d.project!==e.project||d.repo&&d.repo!=="*"&&d.repo!==e.repo||d.chat&&d.chat!=="*"&&te(e.source,d.chat)!==te(e.source,e.chat))continue;if(d.labels?.length){let l=new Set(e.labels??[]);if(!d.labels.some(c=>l.has(c)))continue}}s.push(o)}return s.sort((o,i)=>i.priority-o.priority||o.id.localeCompare(i.id))}async dispatchOne(e,t,r,s){let o=await this.dispatchOneLegacy(e,t,r,s);if(Te("workflow")!=="off")try{Ve($e(),{workflowId:e.id,eventId:r.id,triggerSource:s.source,project:s.project??null,entityRef:t},JSON.stringify({event:r,trigger:s,entityRef:t}),{claimed:o.claimed,runId:o.run?.id??null})}catch(i){this.log(`[ledger] workflow ${e.id} run ${o.run?.id??"?"} record failed: ${i?.message??i}`)}return o}async dispatchOneLegacy(e,t,r,s){let o=this.runs.getActiveByEntity(t),i=o?this.runs.getHomeNodeByEntity(t):null;if(o&&i&&i!==this.nodeId){if(!this.forwarder)return this.log(`[workflow:${e.id}] run ${o} home'd on "${i}" but no forwarder \u2014 dropping`),{claimed:!1,run:null};try{await this.forwarder.forwardTransition(i,{workflowId:e.id,event:r,entityRef:t})}catch(d){this.log(`[workflow:${e.id}] forward to "${i}" failed: ${d.message}`)}return{claimed:!0,run:null}}let a=o?this.runs.get(o):null;if(a)if(a.status==="paused"&&a.pausedAt){if(a.pausedAt.kind!=="checkpoint")return this.log(`[workflow:${e.id}] run ${a.id} paused on ${a.pausedAt.kind} \u2014 channel event bypasses workflow`),{claimed:!1,run:null};if(!Rn(a.pausedAt.resumeMatch,s,r))return this.log(`[workflow:${e.id}] run ${a.id} paused but event doesn't match resumeMatch \u2014 dropping`),{claimed:!1,run:null};let d=D(e,a.pausedAt.nodeId),l=d?e.edges.filter(w=>w.from===d.id).map(w=>w.to):[],p={at:new Date().toISOString(),nodeId:a.pausedAt.nodeId,inputKeys:[],status:"resumed",output:{event:r.payload},idempotencyKey:T(a.id,a.pausedAt.nodeId,`resume:${r.id}`)},c={...a.context,[a.pausedAt.nodeId]:{event:r.payload}},f=a.pausedAt.checkpointName,g=this.runs.recordExecution({runId:a.id,entry:p,nextPending:l,status:"running",pausedAt:null,context:c});g&&(a=g),this.log(`[workflow:${e.id}] run ${a.id} resumed from checkpoint "${f}"`),s.source.endsWith("-message")&&this.startChannelAck(a.id,r.payload)}else return a.status==="running"?(this.log(`[workflow:${e.id}] run ${a.id} already running; dropping concurrent event ${r.id}`),{claimed:!0,run:null}):(this.log(`[workflow:${e.id}] run ${a.id} is ${a.status}; dropping event ${r.id}`),{claimed:!1,run:null});else{let d=X(e);if(!d)return this.log(`[workflow:${e.id}] no trigger node found`),{claimed:!1,run:null};a=this.runs.create({workflowId:e.id,initialPending:d.pending,entityRef:t,initialContext:{[d.triggerId]:r.payload}}),this.log(`[workflow:${e.id}] run ${a.id} created from trigger "${d.triggerId}" for ${t.id}`),this.emitRunEvent({runId:a.id,workflowId:e.id,phase:"created",status:a.status,homeNode:a.homeNode}),s.source.endsWith("-message")&&this.startChannelAck(a.id,r.payload)}return this.walk(e,a.id,r.id),{claimed:!0,run:a}}async walk(e,t,r){try{await this.walkInner(e,t,r)}finally{this.stopChannelAck(t)}}async walkInner(e,t,r){let s=this.runs.get(t);if(s)for(;s.status==="running"&&s.pending.length>0;){let o=s.pending.slice(0,In);await Promise.allSettled(o.map(a=>this.executeNodeAndCommit(e,t,a,r)));let i=this.runs.get(t);if(!i||(s=i,s.status!=="running"))return}}async executeNodeAndCommit(e,t,r,s){let o=T(t,r,s),i=await this.commit(t,()=>{let u=this.runs.get(t);if(!u)return{kind:"done"};if(u.status!=="running")return{kind:"done"};let v=D(e,r);if(!v)return this.log(`[workflow:${e.id}] missing node "${r}" \u2014 marking run failed`),this.runs.recordExecution({runId:t,entry:{at:new Date().toISOString(),nodeId:r,inputKeys:[],status:"failed",idempotencyKey:o,note:"node definition missing from workflow"},nextPending:u.pending.filter(S=>S!==r),status:"failed"}),{kind:"done"};let P=ye(v.type);return P?u.history.some(S=>S.idempotencyKey===o)?(this.runs.recordExecution({runId:t,entry:{at:new Date().toISOString(),nodeId:r,inputKeys:[],status:"skipped",idempotencyKey:o+"_dedup",note:"duplicate execution dropped"},nextPending:u.pending.filter(S=>S!==r)}),{kind:"done"}):{kind:"go",run:u,node:v,handler:P}:(this.log(`[workflow:${e.id}] no handler for node type "${v.type}"`),this.runs.recordExecution({runId:t,entry:{at:new Date().toISOString(),nodeId:r,inputKeys:[],status:"failed",idempotencyKey:o,note:`no handler for node type "${v.type}"`},nextPending:u.pending.filter(S=>S!==r),status:"failed"}),{kind:"done"})});if(i.kind==="done")return;let{run:a,node:d,handler:l}=i,p=e.edges.filter(u=>u.to===r).map(u=>u.from),c;if(d.type!=="agent"){let u=ie();if(u)try{c=Ze(u,{agentId:`workflow:${d.type}`,channel:"workflow",chatId:`${e.id}:${r}`,workflowRunId:t,workflowId:e.id,workflowNodeId:r,messagePreview:`${d.type} ${r}`})}catch{}}let f=d.retry??{maxAttempts:1,backoffMs:1e3},g={error:"retry-loop did not run"},w=0;for(;w<f.maxAttempts;){w++;try{g=await l({workflow:e,run:a,node:d,channels:this.channels,agents:this.agents,forwardChannelSend:this.forwarder?.forwardChannelSend?.bind(this.forwarder),log:this.log})}catch(u){this.log(`[workflow:${e.id}] handler "${d.type}" threw: ${u.message}`),g={error:u.message}}if(!g.error||g.paused)break;if(w<f.maxAttempts){let u=f.backoffMs*Math.pow(2,w-1);this.log(`[workflow:${e.id}] node "${r}" failed (attempt ${w}/${f.maxAttempts}): ${g.error.slice(0,120)} \u2014 retrying in ${u}ms`),await new Promise(v=>setTimeout(v,u))}}if(c){let u=ie();if(u)try{Xe(u,c,{status:g.error?"error":"ok",error:g.error??null})}catch{}}await this.commit(t,async()=>{let u=this.runs.get(t);if(!u)return;let v=new Date().toISOString(),P=u.pending.filter(m=>m!==r);if(g.error){let m=u.status==="running"?"failed":void 0;this.runs.recordExecution({runId:t,entry:{at:v,nodeId:r,inputKeys:p,status:"failed",idempotencyKey:o,note:g.error.slice(0,200)},nextPending:P,status:m}),this.emitRunEvent({runId:t,workflowId:e.id,nodeId:r,phase:"failed",status:m??u.status,note:g.error.slice(0,200)});return}if(g.paused&&g.pausedAt){let m=g.pausedAt,$=null;if(g.spawnChild&&m.kind==="subProcess"){if($=await this.spawnChild({parent:u,parentNodeId:r,childWorkflowId:g.spawnChild.workflowId,input:g.spawnChild.input}),!$){this.runs.recordExecution({runId:t,entry:{at:v,nodeId:r,inputKeys:p,status:"failed",idempotencyKey:o,note:`subProcess: child workflow "${g.spawnChild.workflowId}" not found`},nextPending:P,status:"failed"});return}m={...m,childRunId:$.id}}if(this.runs.recordExecution({runId:t,entry:{at:v,nodeId:r,inputKeys:p,status:"paused",idempotencyKey:o},nextPending:P,status:"paused",pausedAt:m}),this.emitRunEvent({runId:t,workflowId:e.id,nodeId:r,phase:"paused",status:"paused",note:m.kind}),$&&this.kickChildWalk($),m.kind==="timerWait")try{this.timers.schedule({runId:t,workflowId:e.id,nodeId:m.nodeId,fireAt:m.fireAt,cancelKey:`${t}:${m.nodeId}`})}catch(Re){this.log(`[workflow:${e.id}] timer schedule failed: ${Re.message}`)}return}let S=g.output??{},gt={...u.context,[r]:S},{nextPending:mt}=me({workflow:e,fromNodeId:r,selectedPort:g.port}),se=[],q={...u.joinCounters??{}};for(let m of mt){let $=D(e,m);if(!($?.type==="gateway.parallel"&&String($.config?.mode??"fanOut")==="join")){se.push(m);continue}let K=q[m]??[];K.includes(r)||K.push(r),q[m]=K;let yt=e.edges.filter(kt=>kt.to===m).length;K.length>=yt&&(se.push(m),delete q[m])}let ht=[...P,...se],B=d.type==="end",wt=u.status==="running"?B?String(d.config.status??"completed"):"running":void 0,W=this.runs.recordExecution({runId:t,entry:{at:v,nodeId:r,inputKeys:p,status:"ok",output:S,idempotencyKey:o},nextPending:B?[]:ht,status:wt,context:gt,joinCounters:q});this.emitRunEvent({runId:t,workflowId:e.id,nodeId:r,phase:B?"completed":"ok",status:W?.status??u.status}),g.emitSignal&&this.emitSignal({name:g.emitSignal.name,scope:g.emitSignal.scope,workflowId:e.id,payload:g.emitSignal.payload}),B&&W&&W.parentRunId&&W.parentNodeId&&this.resumeParent({parentRunId:W.parentRunId,parentNodeId:W.parentNodeId,childRun:W}).catch(m=>this.log(`[workflow:${e.id}] resumeParent failed: ${m.message}`))})}async spawnChild(e){let t=this.store.list().find(a=>a.id===e.childWorkflowId);if(!t)return null;let r=X(t);if(!r)return this.log(`[workflow:${t.id}] no trigger node \u2014 cannot spawn as child`),null;let s={...e.input};s[r.triggerId]={...s[r.triggerId]??{},parentRunId:e.parent.id,parentNodeId:e.parentNodeId};let o={backend:"agentx-internal",id:`subprocess:${e.parent.id}:${e.parentNodeId}`},i=this.runs.create({workflowId:t.id,initialPending:r.pending,entityRef:o,initialContext:s,parentRunId:e.parent.id,parentNodeId:e.parentNodeId,rootRunId:e.parent.rootRunId??e.parent.id,depth:(e.parent.depth??0)+1});return this.log(`[workflow:${t.id}] child run ${i.id} spawned from ${e.parent.id}/${e.parentNodeId} (depth ${i.depth})`),i}kickChildWalk(e){let t=this.store.list().find(r=>r.id===e.workflowId);t&&this.walk(t,e.id,`spawn:${e.parentRunId}:${e.parentNodeId}`).catch(r=>this.log(`[workflow:${t.id}] child walk failed: ${r.message}`))}async resumeParent(e){let t=this.runs.get(e.parentRunId);if(!t)return;let r=this.store.list().find(s=>s.id===t.workflowId);r&&(await this.commit(e.parentRunId,()=>{let s=this.runs.get(e.parentRunId);if(!s||s.status!=="paused"||!s.pausedAt||s.pausedAt.kind!=="subProcess"||s.pausedAt.nodeId!==e.parentNodeId||s.pausedAt.childRunId!==e.childRun.id)return;let o=D(r,e.parentNodeId),i=o?r.edges.filter(d=>d.from===o.id).map(d=>d.to):[],a={childRunId:e.childRun.id,status:e.childRun.status,output:Sn(e.childRun)};this.runs.recordExecution({runId:s.id,entry:{at:new Date().toISOString(),nodeId:e.parentNodeId,inputKeys:[],status:"resumed",output:a,idempotencyKey:T(s.id,e.parentNodeId,`child:${e.childRun.id}`)},nextPending:i,status:"running",pausedAt:null,context:{...s.context,[e.parentNodeId]:a}}),this.log(`[workflow:${r.id}] parent run ${s.id} resumed from subProcess child ${e.childRun.id}`),this.emitRunEvent({runId:s.id,workflowId:r.id,nodeId:e.parentNodeId,phase:"resumed",status:"running",note:`child:${e.childRun.id}`})}),this.walk(r,e.parentRunId,`child:${e.childRun.id}`))}};function Sn(n){for(let e=n.history.length-1;e>=0;e--){let t=n.history[e];if(t.status==="ok"&&t.output&&Object.keys(t.output).length>0)return t.output}return{}}function Rn(n,e,t){let r=o=>{let i=n[o];if(i===void 0||i===""||i==="*")return!0;if(Array.isArray(i)){let a=new Set(e.labels??[]);return i.some(d=>a.has(String(d)))}return o==="chat"?te(e.source,String(i))===te(e.source,e.chat):e[o]===i};if(!r("source")||!r("project")||!r("repo")||!r("chat"))return!1;let s=n.eventIdLike;return!(typeof s=="string"&&s&&!t.id.includes(s))}function te(n,e){return e==null?"":n==="whatsapp-message"?e.replace(/@s\.whatsapp\.net$|@g\.us$/i,"").replace(/[\s+()]/g,""):e}function An(n){return{"pre:channel-message":async e=>{let t=e.msg;if(!t)return{};let r=$n(t.channel);if(!r)return{};let s=t.group?.id??t.sender.id,o={backend:t.channel,id:`${t.channel}:${s}`},{claimed:i}=await n.dispatch({trigger:{source:r,chat:s},entityRef:o,event:{id:`${t.channel}:${t.accountId}:${t.id}`,payload:Tn(t)}});return i.length===0?{}:i.every(d=>d.nodes.find(p=>p.type.startsWith("trigger."))?.config?.passthrough===!0)?{}:{blocked:!0,message:`handled by workflow${i.length>1?"s":""}: ${i.map(d=>d.id).join(", ")}`}},"on:gitlab-issue":async e=>{let t=e.project,r=e.iid,s=e.issueEvent;if(!t||r===void 0)return{};let o=Array.isArray(s?.object_attributes?.labels)?s.object_attributes.labels.map(d=>typeof d=="string"?d:d?.title).filter(Boolean):s?.labels?.map(d=>d?.title)??[],i=s?.assignees??s?.object_attributes?.assignee_ids??[],a={backend:"gitlab",id:`${t}#${r}`};return await n.dispatch({trigger:{source:"gitlab-issue",project:t,labels:o},entityRef:a,event:{id:`gitlab-issue:${t}:${r}:${s?.object_attributes?.updated_at||s?.object_attributes?.action||"evt"}`,payload:{issue:{iid:r,title:s?.object_attributes?.title,description:s?.object_attributes?.description,url:s?.object_attributes?.url,action:s?.object_attributes?.action,labels:o,assignees:i},project:t,channel:"gitlab",chatId:`${t}:issue:${r}`}}}),{}},"on:gitlab-mr":async e=>{let t=e.project,r=e.iid,s=e.mrEvent;if(!t||r===void 0)return{};let o=Array.isArray(e.labels)?e.labels:Array.isArray(s?.object_attributes?.labels)?s.object_attributes.labels.map(l=>typeof l=="string"?l:l?.title).filter(Boolean):[],i=s?.assignees??s?.object_attributes?.assignee_ids??[],a=s?.reviewers??[],d={backend:"gitlab",id:`${t}!${r}`};return await n.dispatch({trigger:{source:"gitlab-mr",project:t,labels:o},entityRef:d,event:{id:`gitlab-mr:${t}:${r}:${s?.object_attributes?.updated_at||s?.object_attributes?.action||"evt"}`,payload:{mr:{iid:r,title:e.title,description:e.description,url:e.url,action:e.action,state:e.state,source_branch:e.source_branch,target_branch:e.target_branch,labels:o,assignees:i,reviewers:a},project:t,channel:"gitlab",chatId:`${t}:merge_request:${r}`,iid:r,title:e.title,description:e.description,url:e.url,action:e.action,state:e.state,source_branch:e.source_branch,target_branch:e.target_branch,labels:o}}}),{}},"on:gitlab-note":async e=>{let t=e.project,r=e.noteId,s=e.noteableType,o=e.noteableIid,i=e.text,a=e.authorUsername,d=e.mentions??[];if(!t||!r||!s||!o)return{};let l=s==="merge_request"?{backend:"gitlab",id:`${t}!${o}`}:{backend:"gitlab",id:`${t}#${o}`};return await n.dispatch({trigger:{source:"gitlab-note",project:t},entityRef:l,event:{id:`gitlab-note:${t}:${s}:${o}:${r}`,payload:{note:{id:r,text:i,author:a,mentions:d,noteableType:s,noteableIid:o,noteableTitle:e.noteableTitle},project:t,channel:"gitlab",chatId:`${t}:${s}:${o}`,noteId:r,noteableType:s,noteableIid:o,text:i,author:a,mentions:d}}}),{}},"on:gitlab-pipeline":async e=>{let t=e.project,r=e.pipelineId,s=e.status,o=e.ref;if(!t||!s)return{};let a=e.raw?.merge_request?.iid;if(!a)return{};let d={backend:"gitlab",id:`${t}!${a}`};return await n.dispatch({trigger:{source:"gitlab-pipeline",project:t},entityRef:d,event:{id:`gitlab-pipeline:${t}:${r}:${s}`,payload:{pipeline:{id:r,status:s,ref:o},project:t,channel:"gitlab",chatId:`${t}:merge_request:${a}`}}}),{}}}}function $n(n){switch(n){case"telegram":return"telegram-message";case"whatsapp":return"whatsapp-message";case"discord":return"discord-message";case"slack":return"slack-message";default:return null}}function Tn(n){let e=n.group?.id??n.sender.id;return{channel:n.channel,chatId:e,accountId:n.accountId,text:n.text,fromJid:n.sender.id,sender:{id:n.sender.id,name:n.sender.name,username:n.sender.username},contact:n.channelMeta?.facts?{facts:n.channelMeta.facts}:void 0,group:n.group?{id:n.group.id,name:n.group.name}:void 0,channelMeta:n.channelMeta,replyTo:n.replyTo,replyToText:n.replyToText,media:n.media,event:{id:n.id,timestamp:n.timestamp?.toISOString?.()??new Date().toISOString()}}}function Dn(n){let e=n.store.list(),t=0,r=0;for(let s of e){if(s.state&&s.state!=="active"){n.log(`[workflows] ${s.id} state=${s.state} \u2014 skipping trigger registration`);continue}let o=s.nodes.find(i=>i.type.startsWith("trigger."));if(o){if(o.type==="trigger.cron"){let i=o.config;if(!i.spec){n.log(`[workflows] ${s.id} trigger.cron missing config.spec \u2014 skipping`);continue}Wn({workflowId:s.id,triggerNodeId:o.id,spec:i.spec,timezone:i.timezone??"UTC"},n.dispatcher,n.log),t++}else if(o.type==="trigger.hook"){let i=o.config;if(!i.event||!i.event.startsWith("on:")){n.log(`[workflows] ${s.id} trigger.hook config.event must start with "on:" \u2014 skipping`);continue}n.hooks.registerHandler(i.event,`workflows:${s.id}`,async a=>{try{let d=typeof a.project=="string"?a.project:void 0;if(s.project&&d&&s.project!==d)return{};if(i.filter){if(Array.isArray(i.filter.action)&&i.filter.action.length>0){let f=typeof a.action=="string"?a.action:void 0;if(!f||!i.filter.action.includes(f))return{}}if(Array.isArray(i.filter.mentions)&&i.filter.mentions.length>0){let f=Array.isArray(a.mentions)?a.mentions.map(u=>u.toLowerCase().replace(/^@/,"")):[];if(!i.filter.mentions.map(u=>u.toLowerCase().replace(/^@/,"")).some(u=>f.includes(u)))return{}}if(Array.isArray(i.filter.noteableType)&&i.filter.noteableType.length>0){let f=typeof a.noteableType=="string"?a.noteableType.toLowerCase():void 0,g=i.filter.noteableType.map(w=>w.toLowerCase());if(!f||!g.includes(f))return{}}if(Array.isArray(i.filter.assigneesAdded)&&i.filter.assigneesAdded.length>0){let f=Array.isArray(a.assigneesAdded)?a.assigneesAdded.map(u=>u.toLowerCase().replace(/^@/,"")):[];if(!i.filter.assigneesAdded.map(u=>u.toLowerCase().replace(/^@/,"")).some(u=>f.includes(u)))return{}}if(Array.isArray(i.filter.reviewersAdded)&&i.filter.reviewersAdded.length>0){let f=Array.isArray(a.reviewersAdded)?a.reviewersAdded.map(u=>u.toLowerCase().replace(/^@/,"")):[];if(!i.filter.reviewersAdded.map(u=>u.toLowerCase().replace(/^@/,"")).some(u=>f.includes(u)))return{}}if(Array.isArray(i.filter.labelsAdded)&&i.filter.labelsAdded.length>0){let f=Array.isArray(a.labelsAdded)?a.labelsAdded.map(u=>u.toLowerCase()):[];if(!i.filter.labelsAdded.map(u=>u.toLowerCase()).some(u=>f.includes(u)))return{}}}let l=Pn(s.id,i.event,a);if(await n.dispatcher.dispatchWorkflow({workflowId:s.id,entityRef:{backend:"hook",id:l},event:{id:`hook:${i.event}:${Date.now().toString(36)}`,payload:{hookEvent:i.event,...a}}}),i.passthrough)return{};let p=a.__workflowClaimed;return{modified:{__workflowClaimed:[...Array.isArray(p)?p:[],s.id]}}}catch(d){return n.log(`[workflows] ${s.id} hook dispatch failed: ${d.message}`),{}}},60),r++}}}return{cronTimers:t,hookSubscribers:r}}function Pn(n,e,t){let r=typeof t.project=="string"?t.project:"",s=t.iid!=null?String(t.iid):"";if(e==="on:gitlab-issue"&&r&&s)return`${n}:${e}:${r}#${s}`;if(e==="on:gitlab-mr"&&r&&s)return`${n}:${e}:${r}!${s}`;if(e==="on:gitlab-pipeline"&&r){let o=t.pipelineId!=null?String(t.pipelineId):"";if(o)return`${n}:${e}:${r}@pipeline:${o}`}return`${n}:${e}:${Date.now().toString(36)}-${Math.random().toString(36).slice(2,8)}`}function Wn(n,e,t){let r=()=>{let s;try{s=We(n.spec,new Date,n.timezone)}catch(i){t(`[workflows] ${n.workflowId} cron spec invalid: ${i.message}`);return}let o=Math.max(1e3,s.getTime()-Date.now());setTimeout(()=>{(async()=>{let i=`cron:${n.workflowId}:${s.toISOString()}`;try{await e.dispatchWorkflow({workflowId:n.workflowId,trigger:{source:"cron"},entityRef:{backend:"cron",id:`${n.workflowId}@${s.toISOString()}`},event:{id:i,payload:{now:s.toISOString(),spec:n.spec,workflowId:n.workflowId}}})}catch(a){t(`[workflows] ${n.workflowId} cron dispatch failed: ${a.message}`)}finally{r()}})()},o)};r()}import{existsSync as re,mkdirSync as Ie,readFileSync as En,readdirSync as xn,renameSync as _n,unlinkSync as On,writeFileSync as nt}from"fs";import{basename as rt,resolve as I}from"path";function M(n,e="workflow"){return n.toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"").slice(0,52)||e}function st(n,e=900){let t=String(n??"").trim();return t.length>e?`${t.slice(0,e)}...`:t}var jn={merge_request:"mr",pull_request:"mr",pr:"mr",push:"push",issue:"issue",issues:"issue"},Fn=[["prod",/\b(prod(?:uction)?|live)\b/],["staging",/\b(stag(?:ing|e)?|qa|preprod|pre-prod)\b/],["dev",/\b(dev|development|local)\b/]],Cn=[["rollback",/\b(rollback|revert|undo)\b/],["deploy",/\b(deploy|deployment|rollout|release|ship)\b/],["fix",/\b(fix|repair|patch|resolve|hotfix)\b/],["migrate",/\b(migration|migrate)\b/],["review",/\b(review|inspect|audit|approve)\b/],["triage",/\b(triage|classify|categorize|label)\b/],["implement",/\b(implement|add|build|create|introduce)\b/],["refactor",/\b(refactor|rename|restructure|cleanup)\b/],["test",/\b(test|verify|validate|qa)\b/],["update",/\b(update|upgrade|bump)\b/]];function L(n,e=[]){let t={},s=(n.chatId||"").split(":");if(s.length>=2&&s[0].includes("/")){let a=s[0],d=a.split("/").pop()||a;t.project=M(d,"project"),t.kind=M(jn[s[1]]||s[1],"task")}let o=String(n.messagePreview||"").toLowerCase();if(!!!(t.project&&t.kind)){for(let[a,d]of Cn)if(d.test(o)){t.intent=a;break}}for(let[a,d]of Fn)if(d.test(o)){t.environment=a;break}return t}function ot(n,e){let t=[n.project,n.kind,n.intent,n.environment,n.variation].filter(Boolean);if(t.length>=2)return t.map(o=>M(o)).join("-")+"-draft";let r=M(e.messagePreview||"","task").slice(0,32),s=e.taskId.slice(-6).toLowerCase();return`${r||"task"}-${s}-draft`}function Mn(n,e=[]){let t=new Set;n.channel&&t.add(n.channel),n.agentId&&t.add(n.agentId);for(let r of e)r.action&&t.add(M(r.action,"action"));return Array.from(t).slice(0,12)}function Ln(n){let e=n.filter(t=>t.status!=="error").filter(t=>t.name!=="system").slice(0,16);return e.length===0?"No structured steps were captured; use the original message and final result as the procedure evidence.":e.map((t,r)=>{let s=[t.name,t.action].filter(Boolean).join(":"),o=st(t.outputSummary||t.inputSummary||t.error||"",220);return`${r+1}. ${s}${o?` \u2014 ${o}`:""}`}).join(`
|
|
43
|
+
`)}function ne(n,e=[],t={}){let r=L(n,e),s=t.id||ot(r,n),o=t.sourceTaskIds?.length?t.sourceTaskIds:[n.taskId],i=st(n.messagePreview,72)||`Task ${n.taskId.slice(0,8)}`,a=["Replay the cleaned successful procedure represented by this absorbed task trace.","","Original task:","{{trigger.input.message}}","","Procedure evidence, with failed turns and incidental retries removed where possible:",Ln(e),"","Use the current run input values where provided. Keep the final answer concise and operational."].join(`
|
|
44
|
+
`);return y.parse({id:s,version:2,title:`Draft: ${i}`,description:`Generated from task trace ${n.taskId}. Review before promotion.`,status:"draft",state:"disabled",tags:Mn(n,e),entity:n.chatId||void 0,generatedFrom:t.generatedFrom??"task-trace",sourceTaskIds:o,confidence:t.confidence??.55,workflowVersion:"draft-1",ownerAgent:n.agentId,nodes:[{id:"trigger",type:"trigger.manual",config:{inputSchema:{type:"object",properties:{message:{type:"string"},taskId:{type:"string"}}}}},{id:"run_cleaned_task",type:"agent",config:{agentId:n.agentId,prompt:a}},{id:"done",type:"end",config:{status:"completed",output:"{{run_cleaned_task.reply}}"}}],edges:[{from:"trigger",to:"run_cleaned_task"},{from:"run_cleaned_task",to:"done"}],envAllow:[],retention:{maxRuns:500,maxDays:90},maxChildDepth:5})}async function it(n,e=[],t={}){let r=L(n,e),s=t.id||ot(r,n),o=t.sourceTaskIds?.length?t.sourceTaskIds:[n.taskId],i=t.confidence??.55;if(t.viaAgent)try{let{architectWorkflowViaAgent:a}=await import("./architect-SEPWMWS4.js"),d=await a(n,e,s,o,i,{agentId:t.viaAgent,daemonUrl:t.daemonUrl});return{workflow:d.workflow,usedLlm:!0,via:d.via}}catch(a){let d=a?.message||String(a);return t.log?.(`[architect] agent path (${t.viaAgent}) failed for ${n.taskId.slice(0,8)}, falling back to deterministic: ${d}`),{workflow:ne(n,e,t),usedLlm:!1,error:d}}if(t.model)try{let{architectWorkflowFromTrace:a}=await import("./architect-SEPWMWS4.js"),d=await a(n,e,s,o,i,{model:t.model});return{workflow:d.workflow,usedLlm:!0,via:`direct:${d.model}`}}catch(a){let d=a?.message||String(a);return t.log?.(`[architect] direct API failed for ${n.taskId.slice(0,8)}, falling back to deterministic: ${d}`),{workflow:ne(n,e,t),usedLlm:!1,error:d}}return{workflow:ne(n,e,t),usedLlm:!1}}function at(n){let e=y.safeParse(n);return e.success?j(e.data):e.error.issues.map(t=>`${t.path.join(".")||"<root>"}: ${t.message}`)}var Hn=new Set(["agent","action.run","action.send","action.builtin","action.callHTTP","action.createIssue","action.setLabel","action.readLabel","action.react","action.editMessage","action.logTime","transform","extract.structured","branch","rule","subProcess"]);function Un(n){return n.nodes.filter(t=>Hn.has(t.type)).length>0&&n.nodes.length>=3}function H(n=process.cwd()){return I(n,".agentx/workflows/_drafts")}function U(n){return I(n,"_drafts")}function qn(n=process.cwd()){return I(n,".agentx/workflows/_drafts/_rejected")}function Bn(n){return I(U(n),"_rejected")}function dt(n,e="yaml",t=process.cwd()){return I(H(t),`${n}.${e}`)}function Kn(n,e,t="yaml"){return I(U(e),`${n}.${t}`)}function Yn(n,e={}){let t=e.format??"yaml",r=e.workflowDir?U(e.workflowDir):H(e.baseDir);Ie(r,{recursive:!0});let s=e.workflowDir?Kn(n.id,e.workflowDir,t):dt(n.id,t,e.baseDir);if(!e.force&&re(s))throw new Error(`draft already exists: ${s}`);let o=t==="json"?JSON.stringify(n,null,2)+`
|
|
45
|
+
`:Y(n);return nt(s,o),s}function lt(n){let e=En(n,"utf-8"),t=/\.(ya?ml)$/i.test(n)?x(e,{filePath:rt(n)}):JSON.parse(e);return y.parse(t)}function Jn(n=process.cwd(),e={}){let t=e.workflowDir?U(e.workflowDir):H(n);return re(t)?xn(t,{withFileTypes:!0}).filter(r=>r.isFile()&&/\.(json|ya?ml)$/i.test(r.name)).map(r=>{let s=I(t,r.name);return{id:r.name.replace(/\.(json|ya?ml)$/i,""),path:s,workflow:lt(s)}}).sort((r,s)=>r.id.localeCompare(s.id)):[]}function Se(n,e=process.cwd(),t={}){let r=t.workflowDir?U(t.workflowDir):H(e);for(let s of["yaml","yml","json"]){let o=I(r,`${n}.${s}`);if(re(o))return{id:n,path:o,workflow:lt(o)}}return null}function zn(n,e={}){let t=Se(n,e.baseDir,{workflowDir:e.workflowDir});if(!t)throw new Error(`draft not found: ${n}`);let r=e.format??(t.path.endsWith(".json")?"json":"yaml"),s=y.parse({...t.workflow,status:"active",state:"active",updated:new Date().toISOString().slice(0,10)}),o=at(s);if(o.length)throw new Error(`draft is invalid: ${o.join("; ")}`);let i=e.workflowDir??I(e.baseDir??process.cwd(),".agentx/workflows"),a=I(i,`${s.id}.${r}`);if(!e.replace&&re(a))throw new Error(`workflow already exists: ${a}`);return Ie(i,{recursive:!0}),nt(a,r==="json"?JSON.stringify(s,null,2)+`
|
|
46
|
+
`:Y(s)),On(t.path),{workflow:s,from:t.path,to:a}}function Gn(n,e=process.cwd(),t={}){let r=Se(n,e,{workflowDir:t.workflowDir});if(!r)throw new Error(`draft not found: ${n}`);let s=t.workflowDir?Bn(t.workflowDir):qn(e);Ie(s,{recursive:!0});let o=I(s,`${Date.now().toString(36)}-${rt(r.path)}`);return _n(r.path,o),o}var Vn=30;function Zn(n,e={}){let t=e.minMessageLength??Vn;return tt(n,{agentId:e.agentId,status:"ok",since:e.since,limit:e.limit??1e3}).filter(r=>!r.workflowRunId).filter(r=>(r.messagePreview||"").trim().length>=t).filter(r=>!(r.chatId||"").startsWith("architect-"))}function Xn(n){let e=L(n);if(e.project&&e.kind)return[e.project,e.kind,e.intent,e.environment].filter(Boolean).join(":");let t=(n.messagePreview||"").toLowerCase().replace(/[^a-z0-9\s]/g," ").split(/\s+/).filter(r=>r.length>3).slice(0,6).join("-");return[n.channel||"unknown",t||"task"].join(":")}function Qn(n,e={}){let t=new Map;for(let s of n){let o=Xn(s),i=t.get(o)??[];i.push(s),t.set(o,i)}let r=Math.max(1,e.minClusterSize??3);return Array.from(t.entries()).map(([s,o])=>{let i=L(o[0]),a=(i.project?.05:0)+(i.kind?.05:0)+(i.intent?.1:0)+(i.environment?.05:0),d=.45+o.length*.1;return{key:s,traces:o.sort((l,p)=>p.startedAt-l.startedAt),confidence:Math.min(.95,d+a)}}).filter(s=>s.traces.length>=r).sort((s,o)=>o.confidence-s.confidence||o.traces.length-s.traces.length).slice(0,Math.max(1,e.max??10))}async function er(n,e,t={}){let r=[];for(let s of e){let o=s.traces[0],i=et(n,o.taskId),a=s.traces.map(l=>l.taskId),d=await it(o,i?.steps??[],{sourceTaskIds:a,confidence:s.confidence,generatedFrom:t.viaAgent||t.model?"llm-architect":"workflow-absorb",model:t.model,viaAgent:t.viaAgent,daemonUrl:t.daemonUrl,log:t.log});r.push({id:d.workflow.id,workflow:d.workflow,sourceTaskIds:a,confidence:s.confidence,reason:`${s.traces.length} similar successful task traces${d.usedLlm?` (LLM-architected via ${d.via})`:""}`,usedLlm:d.usedLlm,via:d.via})}return r}function ct(n){return n.nodes.find(r=>r.type.startsWith("trigger."))?.config?.inputSchema??null}function ut(n,e){if(!n||!e?.properties)return{};let t=n.match(/^([^:]+\/[^:]+):([^:]+):(.+)$/);if(!t)return{};let[,r,s,o]=t,i={};if(e.properties.project&&(i.project=r),s==="push"){let a=o.match(/^refs\/heads\/(.+)$/);a&&e.properties.ref&&(i.ref=a[1])}else{let a=Number(o);Number.isFinite(a)&&(e.properties.id&&(i.id=a),e.properties.iid&&(i.iid=a),e.properties.mrId&&(i.mrId=a),e.properties.issueId&&(i.issueId=a),e.properties.prId&&(i.prId=a))}return i}function pt(n,e){if(!n?.properties)return e;let t={...e};for(let[r,s]of Object.entries(n.properties))t[r]===void 0&&s&&"default"in s&&(t[r]=s.default);return t}function ft(n,e){return!n?.required||!Array.isArray(n.required)?[]:n.required.filter(t=>{let r=e[t];return r===void 0||r===""||r===null})}function tr(n,e){let t=ct(n);if(!t?.properties){let d={};for(let[l,p]of Object.entries(e))p!==void 0&&(d[l]=p);return{inputs:d,missing:[],filledFrom:{passthrough:Object.keys(d),chatId:[],defaults:[]}}}let r={};for(let d of["message","agentId","channel","chatId","senderId","senderUsername"])t.properties[d]&&e[d]!==void 0&&(r[d]=e[d]);let s=ut(e.chatId,t),o={...r,...s},i={...o};o=pt(t,o);let a=Object.keys(o).filter(d=>i[d]===void 0&&o[d]!==void 0);return{inputs:o,missing:ft(t,o),filledFrom:{passthrough:Object.keys(r),chatId:Object.keys(s),defaults:a}}}export{xe as a,k as b,x as c,Y as d,ce as e,pe as f,ge as g,Ze as h,Xe as i,Cr as j,et as k,Mr as l,tt as m,ve as n,An as o,Dn as p,ne as q,it as r,at as s,Un as t,Yn as u,Jn as v,Se as w,zn as x,Gn as y,Zn as z,Qn as A,er as B,Ut as C,tr as D};
|
|
47
|
+
//# sourceMappingURL=chunk-Q6YO7LYE.js.map
|