agentix-cli 0.26.0 → 0.28.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/README.md +67 -118
- package/apps/mac-helper/Sources/AgentXHelper/AXTree.swift +159 -0
- package/apps/mac-helper/Sources/AgentXHelper/Brand.swift +63 -0
- package/apps/mac-helper/Sources/AgentXHelper/Focus.swift +97 -0
- package/apps/mac-helper/Sources/AgentXHelper/HUD.swift +179 -0
- package/apps/mac-helper/Sources/AgentXHelper/OCR.swift +117 -0
- package/apps/mac-helper/Sources/AgentXHelper/Pointer.swift +309 -0
- package/apps/mac-helper/Sources/AgentXHelper/Screens.swift +67 -0
- package/apps/mac-helper/Sources/AgentXHelper/Typer.swift +185 -0
- package/apps/mac-helper/Sources/AgentXHelper/Vision.swift +201 -0
- package/apps/mac-helper/Sources/AgentXHelper/main.swift +265 -0
- package/apps/mac-helper/build.sh +39 -0
- package/apps/mac-voice/Resources/Info.plist +18 -0
- package/apps/mac-voice/Sources/AgentXVoice/AgentClient.swift +108 -0
- package/apps/mac-voice/Sources/AgentXVoice/Brand.swift +123 -0
- package/apps/mac-voice/Sources/AgentXVoice/Config.swift +68 -0
- package/apps/mac-voice/Sources/AgentXVoice/Hold.swift +74 -0
- package/apps/mac-voice/Sources/AgentXVoice/Hotkey.swift +76 -0
- package/apps/mac-voice/Sources/AgentXVoice/Markdown.swift +140 -0
- package/apps/mac-voice/Sources/AgentXVoice/Panel.swift +275 -0
- package/apps/mac-voice/Sources/AgentXVoice/Progress.swift +147 -0
- package/apps/mac-voice/Sources/AgentXVoice/Recorder.swift +139 -0
- package/apps/mac-voice/Sources/AgentXVoice/ResultCard.swift +162 -0
- package/apps/mac-voice/Sources/AgentXVoice/SmartPaste.swift +72 -0
- package/apps/mac-voice/Sources/AgentXVoice/Speech.swift +192 -0
- package/apps/mac-voice/Sources/AgentXVoice/StepLabel.swift +159 -0
- package/apps/mac-voice/Sources/AgentXVoice/main.swift +380 -0
- package/apps/mac-voice/build.sh +32 -0
- package/dist/{App-BPNHIGUX.js → App-CKPH3HHF.js} +2 -2
- package/dist/{ChatApp-6XSSZB4U.js → ChatApp-MX7IG4K2.js} +2 -2
- package/dist/agent-2U574KBZ.js +2 -0
- package/dist/article-fields-L4D2BOGY.js +2 -0
- package/dist/article-quality-44ONVGLN.js +3 -0
- package/dist/article-quality-44ONVGLN.js.map +1 -0
- package/dist/{audit-JU3HWISB.js → audit-4245CX6P.js} +2 -2
- package/dist/backfill-MTD2B62I.js +6 -0
- package/dist/backfill-MTD2B62I.js.map +1 -0
- package/dist/{board-dashboard-M3372FLO.js → board-dashboard-QR7CVTKA.js} +1411 -453
- package/dist/board-dashboard-QR7CVTKA.js.map +1 -0
- package/dist/{builtin-FRKAQMFS.js → builtin-B67DMJ3M.js} +2 -2
- package/dist/calibration-ILS5OI44.js +2 -0
- package/dist/{chunk-V3U4OF7L.js → chunk-3LYM22IV.js} +46 -48
- package/dist/chunk-3LYM22IV.js.map +1 -0
- package/dist/chunk-3SCPRYJB.js +5 -0
- package/dist/chunk-3SCPRYJB.js.map +1 -0
- package/dist/chunk-4BR7D2OU.js +36 -0
- package/dist/chunk-4BR7D2OU.js.map +1 -0
- package/dist/{chunk-P4VWIVKN.js → chunk-52ZPELD2.js} +403 -29
- package/dist/chunk-52ZPELD2.js.map +1 -0
- package/dist/chunk-5IA2CYHN.js +5 -0
- package/dist/{chunk-O6YPEOQF.js.map → chunk-5IA2CYHN.js.map} +1 -1
- package/dist/chunk-64CAIB3C.js +9 -0
- package/dist/chunk-64CAIB3C.js.map +1 -0
- package/dist/{chunk-5U35WAZF.js → chunk-77OV7346.js} +6 -6
- package/dist/chunk-77OV7346.js.map +1 -0
- package/dist/chunk-AJ5RMU7X.js +2 -0
- package/dist/chunk-AJ5RMU7X.js.map +1 -0
- package/dist/chunk-AJONZWLT.js +2 -0
- package/dist/chunk-AJONZWLT.js.map +1 -0
- package/dist/chunk-BH3GTCZ6.js +2 -0
- package/dist/chunk-BH3GTCZ6.js.map +1 -0
- package/dist/chunk-E3UJHQGN.js +1189 -0
- package/dist/chunk-E3UJHQGN.js.map +1 -0
- package/dist/chunk-EVWEG3VD.js +3 -0
- package/dist/chunk-EVWEG3VD.js.map +1 -0
- package/dist/chunk-F4HZTZHW.js +2 -0
- package/dist/chunk-F4HZTZHW.js.map +1 -0
- package/dist/chunk-F6CNF7V3.js +25 -0
- package/dist/chunk-F6CNF7V3.js.map +1 -0
- package/dist/chunk-FIOB3MEX.js +2 -0
- package/dist/{chunk-H5D7ATXS.js → chunk-FKBGJYWQ.js} +5 -5
- package/dist/chunk-FKBGJYWQ.js.map +1 -0
- package/dist/{chunk-BVGJSPW4.js → chunk-G2HMRDUQ.js} +7 -7
- package/dist/chunk-G2HMRDUQ.js.map +1 -0
- package/dist/chunk-H2QQU2XV.js +109 -0
- package/dist/chunk-H2QQU2XV.js.map +1 -0
- package/dist/{chunk-LDHPMJ7M.js → chunk-IWMW5432.js} +3 -3
- package/dist/chunk-IWMW5432.js.map +1 -0
- package/dist/chunk-K33GIBSE.js +2 -0
- package/dist/chunk-K33GIBSE.js.map +1 -0
- package/dist/chunk-L2WSPY6S.js +2 -0
- package/dist/chunk-L2WSPY6S.js.map +1 -0
- package/dist/chunk-LCIREYTD.js +54 -0
- package/dist/chunk-LCIREYTD.js.map +1 -0
- package/dist/chunk-MHIMGM4G.js +126 -0
- package/dist/chunk-MHIMGM4G.js.map +1 -0
- package/dist/chunk-P5ZBBEPL.js +13 -0
- package/dist/chunk-P5ZBBEPL.js.map +1 -0
- package/dist/{chunk-JTJIPNXY.js → chunk-PNWNVOEZ.js} +1 -1
- package/dist/chunk-PNWNVOEZ.js.map +1 -0
- package/dist/chunk-QHTQPBHU.js +45 -0
- package/dist/chunk-QHTQPBHU.js.map +1 -0
- package/dist/chunk-S2MNL5V6.js +2 -0
- package/dist/chunk-S2MNL5V6.js.map +1 -0
- package/dist/chunk-SCTIUFL4.js +5 -0
- package/dist/{chunk-K2CAPYES.js.map → chunk-SCTIUFL4.js.map} +1 -1
- package/dist/chunk-ULLAE6LE.js +157 -0
- package/dist/chunk-ULLAE6LE.js.map +1 -0
- package/dist/{chunk-DJEZ2HVU.js → chunk-VDMYBPYB.js} +1 -1
- package/dist/chunk-VDMYBPYB.js.map +1 -0
- package/dist/chunk-VIH7ARPF.js +547 -0
- package/dist/chunk-VIH7ARPF.js.map +1 -0
- package/dist/chunk-W5C2GDNC.js +32 -0
- package/dist/{chunk-JP5IEXR7.js.map → chunk-W5C2GDNC.js.map} +1 -1
- package/dist/chunk-WPMX3VGI.js +3 -0
- package/dist/chunk-WPMX3VGI.js.map +1 -0
- package/dist/chunk-YLBERT7N.js +24 -0
- package/dist/chunk-YLBERT7N.js.map +1 -0
- package/dist/chunk-YLGIZRHV.js +114 -0
- package/dist/chunk-YLGIZRHV.js.map +1 -0
- package/dist/chunk-ZMYGXHPT.js +121 -0
- package/dist/chunk-ZMYGXHPT.js.map +1 -0
- package/dist/chunk-ZTEU5JVQ.js +2 -0
- package/dist/chunk-ZTEU5JVQ.js.map +1 -0
- package/dist/{chunk-2WVX3LEC.js → chunk-ZVP6GB4G.js} +1 -1
- package/dist/chunk-ZVP6GB4G.js.map +1 -0
- package/dist/cli.js +1 -1
- package/dist/cli.js.map +1 -1
- package/dist/client-ZLSXGPTV.js +2 -0
- package/dist/{compaction-NWEMOSHA.js → compaction-FD4R23HZ.js} +4 -4
- package/dist/config-HMW3EBZL.js +2 -0
- package/dist/config-mutator-4SZ5YEVD.js +2 -0
- package/dist/{conflict-detector-D7VIYPDI.js → conflict-detector-ZPOT3M2C.js} +2 -2
- package/dist/conflict-detector-ZPOT3M2C.js.map +1 -0
- package/dist/context-planner-AQHJMVY2.js +17 -0
- package/dist/context-planner-AQHJMVY2.js.map +1 -0
- package/dist/debug-3MEUO2P2.js +2 -0
- package/dist/{discover-XJXQXXIO.js → discover-D4VVRNAI.js} +2 -2
- package/dist/discover-D4VVRNAI.js.map +1 -0
- package/dist/doctor-LRYV3QLK.js +2 -0
- package/dist/editor-chat-OB7IQOB5.js +137 -0
- package/dist/editor-chat-OB7IQOB5.js.map +1 -0
- package/dist/entry-triage-LK34TSQD.js +2 -0
- package/dist/entry-triage-LK34TSQD.js.map +1 -0
- package/dist/facts-YVEJOM7O.js +22 -0
- package/dist/facts-YVEJOM7O.js.map +1 -0
- package/dist/heal-FVLBHYP7.js +2 -0
- package/dist/index.d.ts +1040 -64
- package/dist/index.js +12 -19
- package/dist/index.js.map +1 -1
- package/dist/{ingest-whatsapp-LZE62INA.js → ingest-whatsapp-NS4OWDOU.js} +2 -2
- package/dist/loader-4ZGTK7LD.js +2 -0
- package/dist/loader-BOKE5M5S.js +2 -0
- package/dist/memory-extract-F5ZHWY5E.js +2 -0
- package/dist/{migrate-v2-QO7SLKIM.js → migrate-v2-V4WN2VVM.js} +2 -2
- package/dist/ntfy-X7EPB3TG.js +4 -0
- package/dist/ntfy-X7EPB3TG.js.map +1 -0
- package/dist/processes-WU23U2OJ.js +2 -0
- package/dist/program-GSZ5AYAM.js +2 -0
- package/dist/{projects-api-KRME2YKZ.js → projects-api-QE6L65SK.js} +2 -2
- package/dist/{projects-mutate-3NHVG437.js → projects-mutate-MY7JTLSZ.js} +2 -2
- package/dist/providers-KO2DODIO.js +2 -0
- package/dist/query-SQDHE4HV.js +2 -0
- package/dist/questions-4B4KSPXG.js +3 -0
- package/dist/questions-4B4KSPXG.js.map +1 -0
- package/dist/recipes-7S4MC5OJ.js +2 -0
- package/dist/registry-LE6FWYZC.js +2 -0
- package/dist/review-candidates-RQU5KNZM.js +4 -0
- package/dist/review-candidates-RQU5KNZM.js.map +1 -0
- package/dist/review-store-B4YE3EFK.js +4 -0
- package/dist/review-store-B4YE3EFK.js.map +1 -0
- package/dist/{rotation-memo-B3EXXSEH.js → rotation-memo-7T75BLEX.js} +2 -2
- package/dist/routing-ILERJXB6.js +2 -0
- package/dist/runner-YRBVNYTY.js +2 -0
- package/dist/scheduler-BFM3EINS.js +2 -0
- package/dist/seat-7WM7II2B.js +2 -0
- package/dist/select-X4KVMOP5.js +2 -0
- package/dist/select-X4KVMOP5.js.map +1 -0
- package/dist/store-MCOA6N4F.js +2 -0
- package/dist/{subagent-WV7QKQ3L.js → subagent-2SJHSSJS.js} +2 -2
- package/dist/{surface-usage-PRL6Z4U3.js → surface-usage-NS74AXSE.js} +2 -2
- package/dist/timers-PCB4ZIBD.js +2 -0
- package/dist/triage-backtest-JNO7RKX7.js +2 -0
- package/dist/triage-backtest-JNO7RKX7.js.map +1 -0
- package/dist/types-H5ENIUIT.js +2 -0
- package/dist/types-OIFU3KSQ.js +2 -0
- package/dist/types-OIFU3KSQ.js.map +1 -0
- package/dist/web/workflow-editor.global.js +1542 -0
- package/dist/web/workflow-editor.global.js.map +1 -0
- package/dist/{whatsapp-state-XMTCSRXU.js → whatsapp-state-6D6FHHD2.js} +2 -2
- package/dist/wiki-2AUFSIKO.js +2 -0
- package/dist/wiki-2AUFSIKO.js.map +1 -0
- package/dist/workflows/templates/branching.yaml +1 -1
- package/dist/{workspace-setup-TBQAHYHJ.js → workspace-setup-B3F3TUDE.js} +2 -2
- package/dist/workspace-setup-B3F3TUDE.js.map +1 -0
- package/package.json +14 -3
- package/scripts/postinstall.mjs +80 -0
- package/dist/agent-5QMITZT6.js +0 -2
- package/dist/architect-SEPWMWS4.js +0 -2
- package/dist/board-dashboard-M3372FLO.js.map +0 -1
- package/dist/chunk-2WVX3LEC.js.map +0 -1
- package/dist/chunk-3WQFCIB6.js +0 -24
- package/dist/chunk-3WQFCIB6.js.map +0 -1
- package/dist/chunk-4YCH6IZV.js +0 -2
- package/dist/chunk-53NRX2NA.js +0 -2
- package/dist/chunk-53NRX2NA.js.map +0 -1
- package/dist/chunk-5U35WAZF.js.map +0 -1
- package/dist/chunk-BFXONUUN.js +0 -108
- package/dist/chunk-BFXONUUN.js.map +0 -1
- package/dist/chunk-BFZUG66X.js +0 -45
- package/dist/chunk-BFZUG66X.js.map +0 -1
- package/dist/chunk-BTHHSY7D.js +0 -13
- package/dist/chunk-BTHHSY7D.js.map +0 -1
- package/dist/chunk-BVGJSPW4.js.map +0 -1
- package/dist/chunk-D46VZ7K7.js +0 -116
- package/dist/chunk-D46VZ7K7.js.map +0 -1
- package/dist/chunk-DJEZ2HVU.js.map +0 -1
- package/dist/chunk-E5CXCI33.js +0 -155
- package/dist/chunk-E5CXCI33.js.map +0 -1
- package/dist/chunk-FDWOECJY.js +0 -54
- package/dist/chunk-FDWOECJY.js.map +0 -1
- package/dist/chunk-H5D7ATXS.js.map +0 -1
- package/dist/chunk-HR4JSRZS.js +0 -3
- package/dist/chunk-HR4JSRZS.js.map +0 -1
- package/dist/chunk-ISEBLHWX.js +0 -118
- package/dist/chunk-ISEBLHWX.js.map +0 -1
- package/dist/chunk-JP5IEXR7.js +0 -32
- package/dist/chunk-JTJIPNXY.js.map +0 -1
- package/dist/chunk-K2CAPYES.js +0 -5
- package/dist/chunk-LDHPMJ7M.js.map +0 -1
- package/dist/chunk-METYYZKQ.js +0 -1115
- package/dist/chunk-METYYZKQ.js.map +0 -1
- package/dist/chunk-MTAPOCGA.js +0 -47
- package/dist/chunk-MTAPOCGA.js.map +0 -1
- package/dist/chunk-O6YPEOQF.js +0 -5
- package/dist/chunk-P4VWIVKN.js.map +0 -1
- package/dist/chunk-SR3X25TN.js +0 -3
- package/dist/chunk-SR3X25TN.js.map +0 -1
- package/dist/chunk-UHE5FCPB.js +0 -431
- package/dist/chunk-UHE5FCPB.js.map +0 -1
- package/dist/chunk-V3U4OF7L.js.map +0 -1
- package/dist/chunk-VJUTQAPJ.js +0 -2
- package/dist/chunk-VJUTQAPJ.js.map +0 -1
- package/dist/chunk-VT5XYFDD.js +0 -126
- package/dist/chunk-VT5XYFDD.js.map +0 -1
- package/dist/client-HGKAC6VP.js +0 -2
- package/dist/config-A4RWEJHV.js +0 -2
- package/dist/config-mutator-MNSFUN7D.js +0 -2
- package/dist/conflict-detector-D7VIYPDI.js.map +0 -1
- package/dist/context-planner-45GEWKGL.js +0 -17
- package/dist/context-planner-45GEWKGL.js.map +0 -1
- package/dist/debug-N3B5NVJU.js +0 -2
- package/dist/discover-XJXQXXIO.js.map +0 -1
- package/dist/doctor-3Q36ZFE6.js +0 -2
- package/dist/heal-HZ3EQD52.js +0 -2
- package/dist/loader-5CA3O5GM.js +0 -2
- package/dist/loader-6IHUCPVO.js +0 -2
- package/dist/memory-extract-AENFVVO5.js +0 -2
- package/dist/processes-UIC4XXA6.js +0 -2
- package/dist/program-SAUNRCCD.js +0 -2
- package/dist/providers-L27BJ5A6.js +0 -2
- package/dist/query-HZYYRCLU.js +0 -2
- package/dist/recipes-EPLE2SNT.js +0 -2
- package/dist/registry-HK3WQ7BS.js +0 -2
- package/dist/runner-BFYDWPUO.js +0 -2
- package/dist/scheduler-6T2SU5ST.js +0 -2
- package/dist/store-I3HEWVLQ.js +0 -2
- package/dist/timers-5LPZN55J.js +0 -2
- package/dist/types-VECTMYTO.js +0 -2
- package/dist/types-Y6ZCZ52Z.js +0 -2
- package/dist/wiki-M6YNRFC4.js +0 -2
- /package/dist/{App-BPNHIGUX.js.map → App-CKPH3HHF.js.map} +0 -0
- /package/dist/{ChatApp-6XSSZB4U.js.map → ChatApp-MX7IG4K2.js.map} +0 -0
- /package/dist/{agent-5QMITZT6.js.map → agent-2U574KBZ.js.map} +0 -0
- /package/dist/{architect-SEPWMWS4.js.map → article-fields-L4D2BOGY.js.map} +0 -0
- /package/dist/{audit-JU3HWISB.js.map → audit-4245CX6P.js.map} +0 -0
- /package/dist/{builtin-FRKAQMFS.js.map → builtin-B67DMJ3M.js.map} +0 -0
- /package/dist/{chunk-4YCH6IZV.js.map → calibration-ILS5OI44.js.map} +0 -0
- /package/dist/{client-HGKAC6VP.js.map → chunk-FIOB3MEX.js.map} +0 -0
- /package/dist/{config-A4RWEJHV.js.map → client-ZLSXGPTV.js.map} +0 -0
- /package/dist/{compaction-NWEMOSHA.js.map → compaction-FD4R23HZ.js.map} +0 -0
- /package/dist/{config-mutator-MNSFUN7D.js.map → config-HMW3EBZL.js.map} +0 -0
- /package/dist/{debug-N3B5NVJU.js.map → config-mutator-4SZ5YEVD.js.map} +0 -0
- /package/dist/{doctor-3Q36ZFE6.js.map → debug-3MEUO2P2.js.map} +0 -0
- /package/dist/{heal-HZ3EQD52.js.map → doctor-LRYV3QLK.js.map} +0 -0
- /package/dist/{loader-5CA3O5GM.js.map → heal-FVLBHYP7.js.map} +0 -0
- /package/dist/{ingest-whatsapp-LZE62INA.js.map → ingest-whatsapp-NS4OWDOU.js.map} +0 -0
- /package/dist/{loader-6IHUCPVO.js.map → loader-4ZGTK7LD.js.map} +0 -0
- /package/dist/{memory-extract-AENFVVO5.js.map → loader-BOKE5M5S.js.map} +0 -0
- /package/dist/{processes-UIC4XXA6.js.map → memory-extract-F5ZHWY5E.js.map} +0 -0
- /package/dist/{migrate-v2-QO7SLKIM.js.map → migrate-v2-V4WN2VVM.js.map} +0 -0
- /package/dist/{program-SAUNRCCD.js.map → processes-WU23U2OJ.js.map} +0 -0
- /package/dist/{providers-L27BJ5A6.js.map → program-GSZ5AYAM.js.map} +0 -0
- /package/dist/{projects-api-KRME2YKZ.js.map → projects-api-QE6L65SK.js.map} +0 -0
- /package/dist/{projects-mutate-3NHVG437.js.map → projects-mutate-MY7JTLSZ.js.map} +0 -0
- /package/dist/{query-HZYYRCLU.js.map → providers-KO2DODIO.js.map} +0 -0
- /package/dist/{recipes-EPLE2SNT.js.map → query-SQDHE4HV.js.map} +0 -0
- /package/dist/{registry-HK3WQ7BS.js.map → recipes-7S4MC5OJ.js.map} +0 -0
- /package/dist/{runner-BFYDWPUO.js.map → registry-LE6FWYZC.js.map} +0 -0
- /package/dist/{rotation-memo-B3EXXSEH.js.map → rotation-memo-7T75BLEX.js.map} +0 -0
- /package/dist/{scheduler-6T2SU5ST.js.map → routing-ILERJXB6.js.map} +0 -0
- /package/dist/{store-I3HEWVLQ.js.map → runner-YRBVNYTY.js.map} +0 -0
- /package/dist/{surface-usage-PRL6Z4U3.js.map → scheduler-BFM3EINS.js.map} +0 -0
- /package/dist/{timers-5LPZN55J.js.map → seat-7WM7II2B.js.map} +0 -0
- /package/dist/{types-VECTMYTO.js.map → store-MCOA6N4F.js.map} +0 -0
- /package/dist/{subagent-WV7QKQ3L.js.map → subagent-2SJHSSJS.js.map} +0 -0
- /package/dist/{types-Y6ZCZ52Z.js.map → surface-usage-NS74AXSE.js.map} +0 -0
- /package/dist/{wiki-M6YNRFC4.js.map → timers-PCB4ZIBD.js.map} +0 -0
- /package/dist/{workspace-setup-TBQAHYHJ.js.map → types-H5ENIUIT.js.map} +0 -0
- /package/dist/{whatsapp-state-XMTCSRXU.js.map → whatsapp-state-6D6FHHD2.js.map} +0 -0
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/utils/workspace-env.ts"],"sourcesContent":["import { readFileSync, existsSync } from \"fs\"\nimport { resolve } from \"path\"\n\n// Loads per-agent secrets from the agent's workspace and tells the runtime\n// which keys the daemon's own .env injected, so we can strip those before\n// spawning the agent's claude process. Without this, every agent inherits\n// the daemon's GITLAB_TOKEN regardless of whether their workspace has its\n// own .env.gitlab — defeating per-agent identity boundaries (each agent\n// should commit/push as itself, not as the daemon's account).\n\n/**\n * Parse a dotenv-style file. Tolerates the `export KEY=VALUE` form used by\n * shell-sourced files like .env.gitlab on clawd-server.\n */\nfunction parseDotEnv(content: string): Array<[string, string]> {\n const out: Array<[string, string]> = []\n for (const raw of content.split(\"\\n\")) {\n const trimmed = raw.trim()\n if (!trimmed || trimmed.startsWith(\"#\")) continue\n const stripped = trimmed.startsWith(\"export \") ? trimmed.slice(7).trim() : trimmed\n const eq = stripped.indexOf(\"=\")\n if (eq === -1) continue\n const key = stripped.slice(0, eq).trim()\n let value = stripped.slice(eq + 1).trim()\n if (value.length >= 2 && ((value.startsWith('\"') && value.endsWith('\"')) || (value.startsWith(\"'\") && value.endsWith(\"'\")))) {\n value = value.slice(1, -1)\n }\n if (key) out.push([key, value])\n }\n return out\n}\n\n/**\n * Read .env then .env.gitlab from a workspace and return their merged keys.\n * .env.gitlab wins on collision — it's the more specific per-channel file.\n */\nexport function loadWorkspaceEnv(workspace: string): Record<string, string> {\n const merged: Record<string, string> = {}\n for (const fname of [\".env\", \".env.gitlab\"]) {\n const path = resolve(workspace, fname)\n if (!existsSync(path)) continue\n try {\n for (const [k, v] of parseDotEnv(readFileSync(path, \"utf-8\"))) {\n merged[k] = v\n }\n } catch { /* best-effort */ }\n }\n return merged\n}\n\n/**\n * Keys defined in the daemon's own .env file, cached per daemonCwd. The\n * runtime uses this to strip those keys from the child env before adding\n * the workspace overrides — so a daemon-level GITLAB_TOKEN never leaks to\n * an agent that hasn't put one in its own workspace.\n */\nconst daemonEnvKeysCache = new Map<string, Set<string>>()\n\nexport function getDaemonEnvKeys(daemonCwd: string = process.cwd()): Set<string> {\n const cached = daemonEnvKeysCache.get(daemonCwd)\n if (cached) return cached\n const path = resolve(daemonCwd, \".env\")\n let keys: Set<string>\n if (!existsSync(path)) {\n keys = new Set()\n } else {\n try {\n keys = new Set(parseDotEnv(readFileSync(path, \"utf-8\")).map(([k]) => k))\n } catch {\n keys = new Set()\n }\n }\n daemonEnvKeysCache.set(daemonCwd, keys)\n return keys\n}\n\n/** Test-only: clear the cache so a different daemonCwd is re-read. */\nexport function _resetDaemonEnvKeysCache(): void {\n daemonEnvKeysCache.clear()\n}\n\n/**\n * Build the env object passed to a spawned agent process. Starts from\n * parentEnv (default process.env), removes any keys that came from the\n * daemon's own .env (so daemon-level secrets don't leak), then layers in\n * workspace .env / .env.gitlab.\n *\n * - System env (PATH, HOME, USER, …) survives untouched.\n * - Daemon-level secrets (e.g. GITLAB_TOKEN in /home/clawd/agentx/.env) are\n * stripped UNLESS the workspace provides its own value for that key.\n * - Workspace env (per-agent .env / .env.gitlab) wins on the final merge.\n */\nexport function buildAgentEnv(\n workspace: string,\n parentEnv: NodeJS.ProcessEnv = process.env,\n daemonCwd: string = process.cwd(),\n): NodeJS.ProcessEnv {\n const workspaceEnv = loadWorkspaceEnv(workspace)\n const daemonOwnedKeys = getDaemonEnvKeys(daemonCwd)\n const child: NodeJS.ProcessEnv = { ...parentEnv }\n for (const k of daemonOwnedKeys) {\n if (!(k in workspaceEnv)) delete child[k]\n }\n for (const [k, v] of Object.entries(workspaceEnv)) {\n child[k] = v\n }\n return child\n}\n\n// Force the `claude` CLI to use OAuth/subscription billing. When\n// ANTHROPIC_API_KEY is present (env, systemd EnvironmentFile, daemon .env not\n// stripped because the workspace also defines it), the CLI silently switches\n// to the API-key path and any usage caps on that key surface as\n// \"You have reached your specified API usage limits...\". This caused\n// claude-code-tier agents to bypass the user's subscription. The same\n// stripping pattern lives in src/agent/providers/claude-code.ts for the\n// in-process provider — keep them in sync.\nexport function stripAnthropicApiKey(env: NodeJS.ProcessEnv): NodeJS.ProcessEnv {\n delete env.ANTHROPIC_API_KEY\n delete env.ANTHROPIC_API_KEY_OLD\n return env\n}\n"],"mappings":"AAAA,OAAS,gBAAAA,EAAc,cAAAC,MAAkB,KACzC,OAAS,WAAAC,MAAe,OAaxB,SAASC,EAAYC,EAA0C,CAC7D,IAAMC,EAA+B,CAAC,EACtC,QAAWC,KAAOF,EAAQ,MAAM;AAAA,CAAI,EAAG,CACrC,IAAMG,EAAUD,EAAI,KAAK,EACzB,GAAI,CAACC,GAAWA,EAAQ,WAAW,GAAG,EAAG,SACzC,IAAMC,EAAWD,EAAQ,WAAW,SAAS,EAAIA,EAAQ,MAAM,CAAC,EAAE,KAAK,EAAIA,EACrEE,EAAKD,EAAS,QAAQ,GAAG,EAC/B,GAAIC,IAAO,GAAI,SACf,IAAMC,EAAMF,EAAS,MAAM,EAAGC,CAAE,EAAE,KAAK,EACnCE,EAAQH,EAAS,MAAMC,EAAK,CAAC,EAAE,KAAK,EACpCE,EAAM,QAAU,IAAOA,EAAM,WAAW,GAAG,GAAKA,EAAM,SAAS,GAAG,GAAOA,EAAM,WAAW,GAAG,GAAKA,EAAM,SAAS,GAAG,KACtHA,EAAQA,EAAM,MAAM,EAAG,EAAE,GAEvBD,GAAKL,EAAI,KAAK,CAACK,EAAKC,CAAK,CAAC,EAEhC,OAAON,CACT,CAMO,SAASO,EAAiBC,EAA2C,CAC1E,IAAMC,EAAiC,CAAC,EACxC,QAAWC,IAAS,CAAC,OAAQ,aAAa,EAAG,CAC3C,IAAMC,EAAOd,EAAQW,EAAWE,CAAK,EACrC,GAAKd,EAAWe,CAAI,EACpB,GAAI,CACF,OAAW,CAACC,EAAGC,CAAC,IAAKf,EAAYH,EAAagB,EAAM,OAAO,CAAC,EAC1DF,EAAOG,CAAC,EAAIC,CAEhB,MAAE,CAA0B,EAE9B,OAAOJ,CACT,CAQA,IAAMK,EAAqB,IAAI,IAExB,SAASC,EAAiBC,EAAoB,QAAQ,IAAI,EAAgB,CAC/E,IAAMC,EAASH,EAAmB,IAAIE,CAAS,EAC/C,GAAIC,EAAQ,OAAOA,EACnB,IAAMN,EAAOd,EAAQmB,EAAW,MAAM,EAClCE,EACJ,GAAI,CAACtB,EAAWe,CAAI,EAClBO,EAAO,IAAI,QAEX,IAAI,CACFA,EAAO,IAAI,IAAIpB,EAAYH,EAAagB,EAAM,OAAO,CAAC,EAAE,IAAI,CAAC,CAACC,CAAC,IAAMA,CAAC,CAAC,CACzE,MAAE,CACAM,EAAO,IAAI,GACb,CAEF,OAAAJ,EAAmB,IAAIE,EAAWE,CAAI,EAC/BA,CACT,CAkBO,SAASC,EACdC,EACAC,EAA+B,QAAQ,IACvCC,EAAoB,QAAQ,IAAI,EACb,CACnB,IAAMC,EAAeC,EAAiBJ,CAAS,EACzCK,EAAkBC,EAAiBJ,CAAS,EAC5CK,EAA2B,CAAE,GAAGN,CAAU,EAChD,QAAWO,KAAKH,EACRG,KAAKL,GAAe,OAAOI,EAAMC,CAAC,EAE1C,OAAW,CAACA,EAAGC,CAAC,IAAK,OAAO,QAAQN,CAAY,EAC9CI,EAAMC,CAAC,EAAIC,EAEb,OAAOF,CACT,CAUO,SAASG,EAAqBC,EAA2C,CAC9E,cAAOA,EAAI,kBACX,OAAOA,EAAI,sBACJA,CACT","names":["readFileSync","existsSync","resolve","parseDotEnv","content","out","raw","trimmed","stripped","eq","key","value","loadWorkspaceEnv","workspace","merged","fname","path","k","v","daemonEnvKeysCache","getDaemonEnvKeys","daemonCwd","cached","keys","buildAgentEnv","workspace","parentEnv","daemonCwd","workspaceEnv","loadWorkspaceEnv","daemonOwnedKeys","getDaemonEnvKeys","child","k","v","stripAnthropicApiKey","env"]}
|
package/dist/chunk-ISEBLHWX.js
DELETED
|
@@ -1,118 +0,0 @@
|
|
|
1
|
-
import{z as e}from"zod";var P=e.enum(["trigger.channel","trigger.manual","trigger.cron","trigger.hook","trigger.form","agent","transform","branch","gateway.parallel","rule","action.send","action.createIssue","action.setLabel","action.readLabel","action.react","action.editMessage","action.logTime","action.callHTTP","action.run","action.builtin","subProcess","signal.emit","signal.wait","timer.boundary","checkpoint","end"]),R=e.enum(["equals","contains","matches","exists"]),F=e.object({kind:R,params:e.record(e.unknown()).default({})}),_=e.object({maxAttempts:e.number().int().min(1).max(10).default(1),backoffMs:e.number().int().min(0).max(6e4).default(1e3)}).default({maxAttempts:1,backoffMs:1e3}),$=e.object({id:e.string().min(1).regex(/^[a-zA-Z_][a-zA-Z0-9_-]*$/,"node id must be identifier-safe"),type:P,config:e.record(e.unknown()).default({}),retry:_,position:e.object({x:e.number(),y:e.number()}).optional()}),N=e.object({from:e.string(),fromPort:e.string().optional(),to:e.string(),toPort:e.string().optional(),label:e.string().optional()}),A=e.union([e.string(),e.date().transform(t=>t.toISOString())]),v=e.object({id:e.string().regex(/^[a-z0-9][a-z0-9_-]*$/,"workflow id must be lower-kebab"),version:e.literal(2).default(2),title:e.string().min(1),description:e.string().optional(),status:e.enum(["draft","review","active","deprecated"]).default("active"),tags:e.array(e.string()).default([]),project:e.string().optional(),entity:e.string().optional(),intentPath:e.array(e.string()).default([]),generatedFrom:e.string().optional(),sourceTaskIds:e.array(e.string()).default([]),confidence:e.number().min(0).max(1).optional(),workflowVersion:e.string().optional(),ownerAgent:e.string().optional(),lastMatchedAt:e.string().optional(),matchCount:e.number().int().min(0).default(0),state:e.enum(["active","disabled","quarantined"]).default("active"),priority:e.number().int().default(0),fanOut:e.boolean().default(!1),nodes:e.array($).refine(t=>t.length>0,{message:"workflow must have at least one node"}),edges:e.array(N).default([]),envAllow:e.array(e.string()).default([]),retention:e.object({maxRuns:e.number().int().positive().default(500),maxDays:e.number().int().positive().default(90)}).default({maxRuns:500,maxDays:90}),maxChildDepth:e.number().int().positive().default(5),mesh:e.object({allowRemote:e.boolean().default(!1),peers:e.array(e.string()).optional()}).default({allowRemote:!1}),created:A.optional(),updated:A.optional()});function k(t){let r=[],u=new Map(t.nodes.map(n=>[n.id,n])),l=t.nodes.filter(n=>n.type.startsWith("trigger."));l.length===0&&r.push("workflow must have exactly one trigger.* node (found 0)"),l.length>1&&r.push(`workflow has ${l.length} trigger.* nodes; v1 supports exactly one`);for(let n of t.edges)u.has(n.from)||r.push(`edge references missing node: from="${n.from}"`),u.has(n.to)||r.push(`edge references missing node: to="${n.to}"`);let a=l[0];if(a){let n=new Map;for(let s of t.edges)n.has(s.from)||n.set(s.from,[]),n.get(s.from).push(s.to);let o=new Set,c=[a.id];for(;c.length;){let s=c.pop();if(!o.has(s)){o.add(s);for(let g of n.get(s)??[])c.push(g)}}for(let s of t.nodes)o.has(s.id)||r.push(`node "${s.id}" is unreachable from trigger "${a.id}"`);t.nodes.some(s=>o.has(s.id)&&O(s.type))||r.push("no reachable `end`, `checkpoint`, `subProcess`, `signal.wait`, or `timer.boundary` node \u2014 the run cannot terminate or pause")}for(let n of t.nodes){if(n.type!=="branch"&&n.type!=="rule")continue;let o=n.type==="branch"?n.config.cases??[]:(n.config.rules??[]).filter(d=>typeof d.to=="string"),c=n.type==="branch"?n.config.default:n.config.default?.to,s=new Set(o.map(d=>d.to));c&&s.add(c);let g=t.edges.filter(d=>d.from===n.id);for(let d of g){let f=d.fromPort??"";s.has(f)||r.push(`${n.type} "${n.id}" has outgoing edge with port "${f}" not declared in cases or default`)}}let i=C(t);for(let n of i)r.push(`cycle without agent/checkpoint: ${n.join(" -> ")}`);return r}function O(t){return t==="end"||t==="checkpoint"||t==="subProcess"||t==="signal.wait"||t==="timer.boundary"}function M(t){return t==="agent"||t==="checkpoint"||t==="subProcess"||t==="signal.wait"||t==="timer.boundary"}function C(t){let r=new Map;for(let o of t.edges)r.has(o.from)||r.set(o.from,[]),r.get(o.from).push(o.to);let u=new Map(t.nodes.map(o=>[o.id,o])),l=[],a=new Map,i=[],n=o=>{if(a.get(o)===1){let c=i.indexOf(o);if(c>=0){let s=i.slice(c).concat(o);s.some(d=>{let f=u.get(d)?.type;return f?M(f):!1})||l.push(s)}return}if(a.get(o)!==2){a.set(o,1),i.push(o);for(let c of r.get(o)??[])n(c);i.pop(),a.set(o,2)}};for(let o of t.nodes)a.get(o.id)!==2&&n(o.id);return l}var D=e.enum(["running","paused","completed","failed","canceled"]),W=e.enum(["ok","failed","timeout","skipped","paused","resumed"]),z=e.object({at:e.string(),nodeId:e.string(),inputKeys:e.array(e.string()).default([]),status:W,output:e.record(e.unknown()).optional(),idempotencyKey:e.string(),note:e.string().optional()}),L=e.object({backend:e.string(),id:e.string()}),U=e.discriminatedUnion("kind",[e.object({kind:e.literal("checkpoint"),nodeId:e.string(),checkpointName:e.string(),resumeMatch:e.record(e.unknown()).default({})}),e.object({kind:e.literal("subProcess"),nodeId:e.string(),childRunId:e.string(),childWorkflowId:e.string()}),e.object({kind:e.literal("signalWait"),nodeId:e.string(),signalName:e.string(),match:e.record(e.unknown()).default({}),scope:e.enum(["workflow","global"]).default("workflow")}),e.object({kind:e.literal("timerWait"),nodeId:e.string(),fireAt:e.string()})]),V=e.object({id:e.string(),workflowId:e.string(),workflowVersion:e.literal(2),homeNode:e.string(),status:D,pausedAt:U.optional(),context:e.record(e.record(e.unknown())).default({}),pending:e.array(e.string()).default([]),joinCounters:e.record(e.array(e.string())).default({}),entityRef:L,history:e.array(z).default([]),parentRunId:e.string().nullable().default(null),parentNodeId:e.string().nullable().default(null),rootRunId:e.string().nullable().default(null),depth:e.number().int().min(0).default(0),createdAt:e.string(),updatedAt:e.string()});var q="https://api.anthropic.com/v1/messages",H="2023-06-01",Y="claude-sonnet-4-6",T=`You are a workflow architect for AgentX. Given a successful task trace, decompose it into a REUSABLE workflow DAG. The same workflow must apply across project, platform, and environment variations \u2014 extract anything that varies between runs into the trigger's inputSchema and reference it symbolically.
|
|
2
|
-
|
|
3
|
-
PARAMETERIZATION (this is the most important rule):
|
|
4
|
-
The procedure should be one workflow, not one workflow per project / branch / environment. When you see specific values in the trace, ask "would this be different on a future run of the same procedure?" \u2014 if yes, extract them as inputs.
|
|
5
|
-
|
|
6
|
-
Always extract into trigger.config.inputSchema.properties when the trace shows them:
|
|
7
|
-
- platform \u2014 "github" | "gitlab" (only when both are plausible for this work)
|
|
8
|
-
- project \u2014 full repo path or group/repo identifier ("mtgl/mtgl_system", "anis-marrouchi/agentx")
|
|
9
|
-
- environment \u2014 "staging" | "production" | "dev" (infer from hostnames, paths, message text)
|
|
10
|
-
- ref \u2014 branch / tag / commit ("master", "main", "release/2.4")
|
|
11
|
-
- id \u2014 issue / MR / PR number
|
|
12
|
-
- host \u2014 SSH target or service hostname
|
|
13
|
-
- agentId \u2014 only when the work could legitimately be assigned to different agents
|
|
14
|
-
|
|
15
|
-
Each input must be a typed JSON-Schema property under trigger.config.inputSchema.properties with type, description, and (where useful) enum, default, or example. List the actual required ones in trigger.config.inputSchema.required.
|
|
16
|
-
|
|
17
|
-
Reference inputs symbolically EVERYWHERE:
|
|
18
|
-
- BAD: "ssh root@staging.mtgl.com.sa 'cd /var/www/test_mtglbackend && git pull origin master'"
|
|
19
|
-
- GOOD: "ssh {{trigger.input.host}} 'cd {{trigger.input.path}} && git pull origin {{trigger.input.ref}}'"
|
|
20
|
-
|
|
21
|
-
Cross-reference between nodes the same way: {{previousNodeId.outputField}} for chained steps.
|
|
22
|
-
|
|
23
|
-
OUTPUT CONTRACT:
|
|
24
|
-
The end node must declare what the workflow produces \u2014 operators reading the workflow card need to see what comes out. Use end.config.output as a structured object literal of the form { status: "...", "<key>": "{{node.field}}" }, not a free-text string. Pull values from the nodes that produced them.
|
|
25
|
-
|
|
26
|
-
Decomposition rules:
|
|
27
|
-
- Group related tool calls into one semantic node. Five Bash calls that pull a repo, install deps, run migrations, restart services, and verify status are ONE deploy node, not five.
|
|
28
|
-
- Prefer LINEAR workflows. Use branch ONLY when the procedure had a clear conditional fork that future runs will also need (e.g. "if migration \u2192 backup db first; else skip"). Don't invent branches the trace didn't actually exercise.
|
|
29
|
-
- Use deterministic node types (action.run, action.builtin, transform) when the work is mechanical.
|
|
30
|
-
- Use an "agent" node only when the step genuinely requires LLM reasoning (drafting prose, classifying intent, synthesizing output).
|
|
31
|
-
|
|
32
|
-
Available node types and their configs:
|
|
33
|
-
- trigger.manual config: { inputSchema: { type:"object", properties: {...}, required: [...] } }
|
|
34
|
-
- agent config: { agentId: string, prompt: string }
|
|
35
|
-
- transform config: { expression: string } (jq-style data shaping)
|
|
36
|
-
- action.run config: { command: string } (shell, supports {{templates}})
|
|
37
|
-
- action.send config: { channel: string, chatId: string, text: string }
|
|
38
|
-
- action.builtin config: { name: string, input: object } (e.g. name: "http.fetch")
|
|
39
|
-
- extract.structured config: { prompt: string, schema: object }
|
|
40
|
-
- branch config: { cases: [{ when: <Condition>, to: <port-name> }, ...], default?: <port-name> }
|
|
41
|
-
- rule config: { rows: [...] } (DMN decision table)
|
|
42
|
-
- checkpoint config: { label: string }
|
|
43
|
-
- end config: { status: "completed"|"failed", output: object }
|
|
44
|
-
|
|
45
|
-
Branch/rule mechanics \u2014 read carefully:
|
|
46
|
-
- The "to" field in cases is a PORT NAME (a string label), NOT a node id.
|
|
47
|
-
- Each outgoing edge from a branch must have fromPort equal to one of the port names declared in cases or in default.
|
|
48
|
-
- The edge.to field is the destination node id.
|
|
49
|
-
|
|
50
|
-
WORKED EXAMPLE \u2014 a deploy workflow that handles ANY project/env/branch via inputs:
|
|
51
|
-
{
|
|
52
|
-
"title": "Deploy MR to environment",
|
|
53
|
-
"description": "Pull a branch onto a host, run migrations if present, rebuild Laravel caches. Same procedure for any project/env/branch \u2014 pass them as inputs.",
|
|
54
|
-
"nodes": [
|
|
55
|
-
{
|
|
56
|
-
"id": "trigger",
|
|
57
|
-
"type": "trigger.manual",
|
|
58
|
-
"config": {
|
|
59
|
-
"inputSchema": {
|
|
60
|
-
"type": "object",
|
|
61
|
-
"properties": {
|
|
62
|
-
"project": { "type": "string", "description": "group/repo (e.g. 'mtgl/mtgl_system')" },
|
|
63
|
-
"environment": { "type": "string", "enum": ["staging", "production", "dev"], "default": "staging" },
|
|
64
|
-
"host": { "type": "string", "description": "SSH target hostname (e.g. 'staging.mtgl.com.sa')" },
|
|
65
|
-
"path": { "type": "string", "description": "Absolute path on host where the project is deployed" },
|
|
66
|
-
"ref": { "type": "string", "description": "Branch/tag/commit to deploy", "default": "master" }
|
|
67
|
-
},
|
|
68
|
-
"required": ["project", "environment", "host", "path", "ref"]
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
},
|
|
72
|
-
{ "id": "pull", "type": "action.run", "config": { "command": "ssh {{trigger.input.host}} 'cd {{trigger.input.path}} && git pull origin {{trigger.input.ref}}'" } },
|
|
73
|
-
{ "id": "needs_backup", "type": "branch", "config": { "cases": [{ "when": { "kind": "matches", "params": { "path": "pull.stdout", "pattern": "migrate" } }, "to": "yes" }], "default": "no" } },
|
|
74
|
-
{ "id": "backup_db", "type": "action.run", "config": { "command": "ssh {{trigger.input.host}} 'pg_dump app > /backups/{{trigger.input.environment}}-$(date +%Y%m%d-%H%M%S).sql'" } },
|
|
75
|
-
{ "id": "migrate", "type": "action.run", "config": { "command": "ssh {{trigger.input.host}} 'cd {{trigger.input.path}} && php artisan migrate --force'" } },
|
|
76
|
-
{ "id": "rebuild_cache", "type": "action.run", "config": { "command": "ssh {{trigger.input.host}} 'cd {{trigger.input.path}} && php artisan config:cache && php artisan route:cache'" } },
|
|
77
|
-
{
|
|
78
|
-
"id": "done",
|
|
79
|
-
"type": "end",
|
|
80
|
-
"config": {
|
|
81
|
-
"status": "completed",
|
|
82
|
-
"output": {
|
|
83
|
-
"project": "{{trigger.input.project}}",
|
|
84
|
-
"environment": "{{trigger.input.environment}}",
|
|
85
|
-
"ref": "{{trigger.input.ref}}",
|
|
86
|
-
"deployedAt": "{{rebuild_cache.completedAt}}",
|
|
87
|
-
"migrated": "{{needs_backup.matchedPort}}"
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
],
|
|
92
|
-
"edges": [
|
|
93
|
-
{ "from": "trigger", "to": "pull" },
|
|
94
|
-
{ "from": "pull", "to": "needs_backup" },
|
|
95
|
-
{ "from": "needs_backup", "to": "backup_db", "fromPort": "yes" },
|
|
96
|
-
{ "from": "needs_backup", "to": "migrate", "fromPort": "no" },
|
|
97
|
-
{ "from": "backup_db", "to": "migrate" },
|
|
98
|
-
{ "from": "migrate", "to": "rebuild_cache" },
|
|
99
|
-
{ "from": "rebuild_cache", "to": "done" }
|
|
100
|
-
]
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
Notice three things:
|
|
104
|
-
1. The trigger declares a typed inputSchema. An operator running this workflow knows EXACTLY what to pass in.
|
|
105
|
-
2. Every command references {{trigger.input.X}}, never literal hostnames or paths.
|
|
106
|
-
3. The end node's output is a structured object so callers know what they get back.
|
|
107
|
-
|
|
108
|
-
Constraints:
|
|
109
|
-
- Exactly one trigger.* node and at least one "end" node.
|
|
110
|
-
- DAG only (no cycles).
|
|
111
|
-
- Every edge's from/to must reference an existing node id.
|
|
112
|
-
- Node ids match /^[a-z0-9][a-z0-9_]*$/.
|
|
113
|
-
- Don't invent fields not in the spec above. Don't add config keys you didn't see in this spec.
|
|
114
|
-
- The trigger MUST declare inputSchema unless the procedure genuinely takes no parameters.`,B={type:"object",required:["title","description","nodes","edges"],properties:{title:{type:"string",maxLength:120},description:{type:"string",maxLength:600},nodes:{type:"array",minItems:2,maxItems:30,items:{type:"object",required:["id","type","config"],properties:{id:{type:"string",pattern:"^[a-z0-9][a-z0-9_]*$",maxLength:48},type:{type:"string",enum:["trigger.manual","agent","transform","branch","rule","action.run","action.send","action.builtin","extract.structured","checkpoint","end"]},config:{type:"object"}}}},edges:{type:"array",maxItems:60,items:{type:"object",required:["from","to"],properties:{from:{type:"string"},to:{type:"string"},fromPort:{type:"string"},label:{type:"string"}}}}}};function b(t,r){let u=String(t??"").trim();return u.length>r?`${u.slice(0,r)}\u2026`:u}function E(t,r){let l=r.filter(a=>a.status!=="error"&&a.name!=="system").slice(0,40).map((a,i)=>{let n=[a.name,a.action].filter(Boolean).join(":"),o=b(a.inputSummary||"",280),c=b(a.outputSummary||"",280),s=[o&&`in=${o}`,c&&`out=${c}`].filter(Boolean).join(" | ");return`${i+1}. ${n}${s?` ${s}`:""}`});return["Original task message:",b(t.messagePreview||"",600),"",`Agent: ${t.agentId} (channel=${t.channel||"unknown"}, chatId=${t.chatId||"\u2014"})`,`Outcome: ${t.status}, steps=${r.length}`,"","Procedure evidence (filtered, in order):",...l].join(`
|
|
115
|
-
`)}async function I(t,r,u,l,a,i){let n=[{role:"user",content:l}];a&&(n.push({role:"assistant",content:"(previous attempt; will retry with corrections)"}),n.push({role:"user",content:a}));let o={model:r,max_tokens:i.maxTokens,system:u,messages:n,tools:[{name:"emit_workflow",description:"Return the workflow DAG. Must be valid against the schema.",input_schema:B}],tool_choice:{type:"tool",name:"emit_workflow"}},c=new AbortController,s=setTimeout(()=>c.abort(),i.timeoutMs),g;try{g=await i.fetchImpl(q,{method:"POST",headers:{"Content-Type":"application/json","x-api-key":t,"anthropic-version":H},body:JSON.stringify(o),signal:c.signal})}finally{clearTimeout(s)}if(!g.ok){let m=await g.text().catch(()=>"");throw new Error(`anthropic API ${g.status}: ${m.slice(0,500)}`)}let d=await g.json(),h=(Array.isArray(d.content)?d.content:[]).find(m=>m?.type==="tool_use"&&m?.name==="emit_workflow");if(!h||!h.input)throw new Error("anthropic API returned no emit_workflow tool_use");let p=d.usage??{};return{data:h.input,usage:{inputTokens:typeof p.input_tokens=="number"?p.input_tokens:0,outputTokens:typeof p.output_tokens=="number"?p.output_tokens:0}}}function S(t,r,u,l,a,i){let n=new Set;r.channel&&n.add(r.channel);for(let o of u)o.action&&n.add(o.action.toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-|-$/g,""));return v.parse({id:l,version:2,title:b(t.title,120)||`Draft: ${b(r.messagePreview,72)||r.taskId.slice(0,8)}`,description:b(t.description,600)||`LLM-architected from task trace ${r.taskId}.`,status:"draft",state:"disabled",tags:Array.from(n).slice(0,12),entity:r.chatId||void 0,generatedFrom:"llm-architect",sourceTaskIds:a,confidence:i,workflowVersion:"draft-1",ownerAgent:r.agentId,nodes:t.nodes,edges:t.edges,envAllow:[],retention:{maxRuns:500,maxDays:90},maxChildDepth:5})}function J(t){let r=t.match(/```(?:json)?\s*([\s\S]*?)```/i),u=r?r[1]:t,l=u.indexOf("{");if(l<0)return null;let a=0,i=!1,n=!1;for(let o=l;o<u.length;o++){let c=u[o];if(n){n=!1;continue}if(c==="\\"&&i){n=!0;continue}if(c==='"'){i=!i;continue}if(!i){if(c==="{")a++;else if(c==="}"&&(a--,a===0))return u.slice(l,o+1)}}return null}async function j(t,r,u,l,a,i){let n=l?["Your previous response failed workflow-schema validation with these issues:",l,"","Re-emit the corrected workflow as a single JSON object \u2014 same shape as before, but fix the listed issues. No prose, no code fences."].join(`
|
|
116
|
-
`):[r,"","---","","Task trace to architect:","",u,"","---","","Reply with ONLY a JSON object matching this shape (no prose, no fenced code block, no commentary):","{",' "title": "<short title>",',' "description": "<short description>",',' "nodes": [{ "id": "...", "type": "...", "config": {} }, ...],',' "edges": [{ "from": "...", "to": "...", "fromPort": "..." (optional), "label": "..." (optional) }, ...]',"}"].join(`
|
|
117
|
-
`),o=new AbortController,c=setTimeout(()=>o.abort(),i.timeoutMs),s;try{s=await i.fetchImpl(`${i.daemonUrl.replace(/\/+$/,"")}/task`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({agent:t,message:n,freshSession:l===null,context:{channel:"api",chatId:a}}),signal:o.signal})}finally{clearTimeout(c)}if(!s.ok){let y=await s.text().catch(()=>"");throw new Error(`agent /task ${s.status}: ${y.slice(0,500)}`)}let g=await s.json(),d=String(g.content??"").trim();if(g.error)throw new Error(`agent reported error: ${g.error}`);if(!d)throw new Error("agent returned empty content");let f=J(d);if(!f)throw new Error(`agent reply has no JSON object (first 200 chars: ${d.slice(0,200)})`);let h;try{h=JSON.parse(f)}catch(y){throw new Error(`agent reply JSON is unparseable: ${y.message}`)}if(!h||typeof h!="object")throw new Error("agent reply is not a JSON object");let p=h;if(!Array.isArray(p.nodes)||!Array.isArray(p.edges))throw new Error("agent reply missing nodes/edges arrays");let m=g.usage??{};return{data:p,usage:{inputTokens:typeof m.inputTokens=="number"?m.inputTokens:0,outputTokens:typeof m.outputTokens=="number"?m.outputTokens:0}}}async function Z(t,r,u,l,a,i={agentId:""}){if(!i.agentId)throw new Error("architectWorkflowViaAgent: agentId is required");let n=i.daemonUrl||"http://127.0.0.1:18800",o=i.fetchImpl||fetch,c={daemonUrl:n,timeoutMs:i.timeoutMs??18e4,fetchImpl:o},s=E(t,r),g=`architect-${t.taskId.toLowerCase()}`,d=await j(i.agentId,T,s,null,g,c),f=d.usage,h=d.data,p;try{p=S(h,t,r,u,l,a);let m=k(p);if(m.length)throw new Error(`lint: ${m.join("; ")}`);return{workflow:p,usage:f,via:`agent:${i.agentId}`}}catch(m){let y=await j(i.agentId,T,s,m.message,g,c);h=y.data,f={inputTokens:f.inputTokens+y.usage.inputTokens,outputTokens:f.outputTokens+y.usage.outputTokens},p=S(h,t,r,u,l,a);let w=k(p);if(w.length)throw new Error(`agent architect retry still invalid: ${w.join("; ")}`);return{workflow:p,usage:f,via:`agent:${i.agentId}`}}}async function Q(t,r,u,l,a,i={}){let n=process.env.ANTHROPIC_API_KEY;if(!n)throw new Error("ANTHROPIC_API_KEY not set; LLM architect unavailable");let o=i.model||Y,c=i.fetchImpl||fetch,s={maxTokens:i.maxTokens??4e3,timeoutMs:i.timeoutMs??9e4,fetchImpl:c},g=E(t,r),d=await I(n,o,T,g,null,s),f=d.data,h=d.usage,p;try{p=S(f,t,r,u,l,a);let m=k(p);if(m.length)throw new Error(`lint: ${m.join("; ")}`);return{workflow:p,usage:h,model:o}}catch(m){let y=`Your previous response failed validation: ${m.message}. Re-emit emit_workflow with the corrections. Keep the same overall structure but fix the listed issues.`,w=await I(n,o,T,g,y,s);f=w.data,h={inputTokens:h.inputTokens+w.usage.inputTokens,outputTokens:h.outputTokens+w.usage.outputTokens},p=S(f,t,r,u,l,a);let x=k(p);if(x.length)throw new Error(`LLM architect retry still invalid: ${x.join("; ")}`);return{workflow:p,usage:h,model:o}}}export{v as a,k as b,Z as c,Q as d};
|
|
118
|
-
//# sourceMappingURL=chunk-ISEBLHWX.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/workflows/types.ts","../src/workflows/architect.ts"],"sourcesContent":["import { z } from \"zod\"\n\n// --- Workflows (V2 — dataflow DAG) ---\n//\n// A workflow is a directed acyclic graph of nodes connected by edges.\n// A run executes the graph by walking from the trigger, accumulating each\n// node's output into a shared context. Prompts and action params template\n// against the context via `{{<nodeId>.<path>}}`.\n//\n// This replaces the previous state-machine model (states + transitions +\n// external label backend). The label-as-state pattern is re-expressible\n// via a `checkpoint` node + `action.setLabel` + `action.readLabel`.\n//\n// Shape on disk (.agentx/workflows/):\n// <id>.json — one workflow definition\n// _runs/<run-id>.jsonl — append-only run events (home-node only)\n// _index/<backend>__<entity>.json — entity -> active runId lookup\n//\n// JSON-on-disk: no YAML dep.\n\n// --------------------------- Node types -----------------------------------\n\nexport const nodeTypeSchema = z.enum([\n // Entry points\n \"trigger.channel\",\n \"trigger.manual\",\n \"trigger.cron\",\n \"trigger.hook\",\n \"trigger.form\",\n // Compute\n \"agent\",\n \"transform\",\n // Control flow\n \"branch\",\n \"gateway.parallel\",\n \"rule\",\n // Side-effect sinks (per channel verb). Each action.* maps 1:1 to an\n // existing channel-adapter method. New verbs arrive as new entries.\n \"action.send\",\n \"action.createIssue\",\n \"action.setLabel\",\n \"action.readLabel\",\n \"action.react\",\n \"action.editMessage\",\n \"action.logTime\",\n \"action.callHTTP\",\n // Registered action invocation — references an entry from the action\n // registry (.agentx/actions/<id>.json) by id, with templated inputs.\n // Lets operators define a reusable shell/http action once and call it\n // from many workflows without duplicating the command/url/headers.\n \"action.run\",\n // Built-in typed action invocation (improvement plan #6 + #9). Calls\n // a daemon-shipped action (http.fetch, mesh.delegate, extract.structured,\n // etc.) by name. Input is templated against the run context the same\n // way action.run is. Output is the validated builtin response so\n // downstream nodes can pipe typed data forward without reparsing.\n \"action.builtin\",\n // BPM: human tasks + composition + signals + intermediate timer\n \"subProcess\",\n \"signal.emit\",\n \"signal.wait\",\n \"timer.boundary\",\n // Persistence / pause\n \"checkpoint\",\n // Terminal\n \"end\",\n])\nexport type NodeType = z.infer<typeof nodeTypeSchema>\n\n// --------------------------- Condition (branch evaluator) -----------------\n\nexport const conditionKindSchema = z.enum([\n \"equals\", // params: { path, value }\n \"contains\", // params: { path, value }\n \"matches\", // params: { path, regex }\n \"exists\", // params: { path }\n])\nexport type ConditionKind = z.infer<typeof conditionKindSchema>\n\nexport const conditionSchema = z.object({\n kind: conditionKindSchema,\n params: z.record(z.unknown()).default({}),\n})\nexport type Condition = z.infer<typeof conditionSchema>\n\n// --------------------------- Nodes + edges --------------------------------\n\n/** Improvement plan #9b — per-node retry policy. When a node's\n * handler returns `{ error }` (or throws), the dispatcher re-runs it\n * up to `maxAttempts - 1` more times with exponential backoff\n * (`backoffMs * 2^(attempt-1)`). Once all attempts are exhausted,\n * the LAST error is what gets recorded as the node's failure.\n * Defaults: maxAttempts=1 (no retry), backoffMs=1000.\n *\n * Pause results (signalWait, timerWait) are NEVER retried —\n * pausing is a normal lifecycle transition, not a failure. Only\n * hard errors (`{error}` or thrown exceptions) trigger the retry. */\nexport const retryPolicySchema = z.object({\n maxAttempts: z.number().int().min(1).max(10).default(1),\n backoffMs: z.number().int().min(0).max(60_000).default(1000),\n}).default({ maxAttempts: 1, backoffMs: 1000 })\nexport type RetryPolicy = z.infer<typeof retryPolicySchema>\n\nexport const workflowNodeSchema = z.object({\n id: z.string().min(1).regex(/^[a-zA-Z_][a-zA-Z0-9_-]*$/, \"node id must be identifier-safe\"),\n type: nodeTypeSchema,\n /** Free-form per-type config. Each node handler validates its own shape\n * at execution time via a local Zod schema; keeping the top-level schema\n * permissive lets new node types ship without a round-trip through here. */\n config: z.record(z.unknown()).default({}),\n /** Per-node retry on hard errors. See RetryPolicy. */\n retry: retryPolicySchema,\n /** Optional UI position. Ignored by the engine; persisted to keep the\n * editor layout alongside the workflow (no separate _layouts file). */\n position: z.object({ x: z.number(), y: z.number() }).optional(),\n})\nexport type WorkflowNode = z.infer<typeof workflowNodeSchema>\n\nexport const workflowEdgeSchema = z.object({\n /** Source node id. */\n from: z.string(),\n /** Optional source port. Used by `branch` nodes to select an outgoing\n * case; default `\"out\"` for single-port nodes. */\n fromPort: z.string().optional(),\n /** Target node id. */\n to: z.string(),\n /** Reserved — `\"in\"` in v1. Kept for future multi-input nodes. */\n toPort: z.string().optional(),\n /** Optional label shown on the edge in the editor. */\n label: z.string().optional(),\n})\nexport type WorkflowEdge = z.infer<typeof workflowEdgeSchema>\n\n// --------------------------- Workflow -------------------------------------\n\n/** A date that may arrive as a string OR as a JS Date. YAML parsers coerce\n * an unquoted `2026-05-10` into a Date; JSON never does. Normalises to an\n * ISO string so downstream consumers see one type. */\nconst isoDateString = z.union([\n z.string(),\n z.date().transform((d) => d.toISOString()),\n])\n\nexport const workflowSchema = z.object({\n id: z.string().regex(/^[a-z0-9][a-z0-9_-]*$/, \"workflow id must be lower-kebab\"),\n /** Schema version. V2 = dataflow DAG. V1 workflows are not loadable. */\n version: z.literal(2).default(2),\n title: z.string().min(1),\n description: z.string().optional(),\n /** REVIEW METADATA ONLY — `status` never gates dispatch. The one and only\n * dispatch gate is `state` below: a workflow with status=draft but\n * state=active WILL fire; status=active with state=disabled will NOT.\n * (The matcher in matcher.ts is the single exception: it requires both\n * to be active before suggesting.) Generated workflows are saved as\n * status=draft + state=disabled so they validate but never fire until\n * an operator promotes them. If you're deciding \"will this run?\", read\n * `state`; if you're deciding \"has a human reviewed this?\", read\n * `status`. */\n status: z.enum([\"draft\", \"review\", \"active\", \"deprecated\"]).default(\"active\"),\n tags: z.array(z.string()).default([]),\n /** Project this workflow is scoped to, in `<org>/<repo>` form for\n * GitLab/GitHub or any operator-defined slug for non-VCS workflows.\n * Optional — workflows without a project field are global and match\n * any project's events. When set, the field is BOTH metadata (UI\n * grouping, project badges) AND a hard scope: the dispatcher and\n * the per-workflow hook handler in `triggers.ts` drop events whose\n * `ctx.project` differs from this field. Without that gate, two\n * workflows tagged for different projects but subscribed to the\n * same hook (e.g. on:gitlab-issue) both fire on every event — see\n * the cross-tenant leak that prompted this commit. Project rules\n * in `.agentx/projects/` still pre-filter at the webhook layer; the\n * workflow-level scope is a defence in depth. */\n project: z.string().optional(),\n entity: z.string().optional(),\n intentPath: z.array(z.string()).default([]),\n generatedFrom: z.string().optional(),\n sourceTaskIds: z.array(z.string()).default([]),\n confidence: z.number().min(0).max(1).optional(),\n workflowVersion: z.string().optional(),\n ownerAgent: z.string().optional(),\n lastMatchedAt: z.string().optional(),\n matchCount: z.number().int().min(0).default(0),\n /** THE dispatch gate (see `status` above for the review-metadata axis).\n * The dispatcher and trigger registrar honor this:\n * - active: normal — triggers register, runs create + advance\n * - disabled: operator kill switch (config or admin action). No new\n * triggers, no new runs. In-flight runs continue.\n * - quarantined: system set by the conflict detector when this workflow\n * would race with another dispatch path (e.g. a gitlab\n * agentMapping handles the same project the workflow\n * triggers on). Treated like `disabled` at runtime;\n * resolved by either fixing the conflict or operator\n * explicitly flipping the state back to `active`. */\n state: z.enum([\"active\", \"disabled\", \"quarantined\"]).default(\"active\"),\n /** Higher priority wins when multiple workflows match the same event.\n * Default 0. Ties broken by id. */\n priority: z.number().int().default(0),\n /** If true, every matching workflow runs in parallel (each with its own\n * run). Default: only the highest-priority match runs. */\n fanOut: z.boolean().default(false),\n nodes: z.array(workflowNodeSchema).refine(\n (ns) => ns.length > 0, { message: \"workflow must have at least one node\" },\n ),\n edges: z.array(workflowEdgeSchema).default([]),\n /** Env vars that templates + node configs may read via {{env.*}}. */\n envAllow: z.array(z.string()).default([]),\n retention: z.object({\n maxRuns: z.number().int().positive().default(500),\n maxDays: z.number().int().positive().default(90),\n }).default({ maxRuns: 500, maxDays: 90 }),\n /** Safety rail on sub-process nesting depth. A parent at depth N may\n * spawn children up to depth maxChildDepth - 1. Default 5. Override per\n * workflow if a deeper composition is genuinely needed. */\n maxChildDepth: z.number().int().positive().default(5),\n /** Mesh integration. By default workflows are local — only triggers\n * observed on the same node can fire them. Set `mesh.allowRemote: true`\n * to opt the workflow into cross-mesh trigger fan-out: when a peer\n * receives a channel event, it broadcasts it to mesh peers and any\n * matching workflow with allowRemote runs on the peer that owns its\n * definition. The opt-in is required because remote events carry\n * reduced trust (sender provenance is mesh-token only) and can\n * surprise authors who expected isolation. */\n mesh: z.object({\n allowRemote: z.boolean().default(false),\n /** Optional peer-name allowlist. When present, only triggers\n * broadcast from these named peers are honored. Empty/unset = any\n * authenticated peer. Useful for production workflows that should\n * only accept events from a specific upstream node. */\n peers: z.array(z.string()).optional(),\n }).default({ allowRemote: false }),\n // YAML turns an unquoted `2026-05-10` into a JS Date, so a hand-authored\n // workflow that omits the quotes was silently rejected by the schema and\n // dropped by WorkflowStore.list() — which swallows parse failures. Accept\n // both shapes and normalise to an ISO string.\n created: isoDateString.optional(),\n updated: isoDateString.optional(),\n})\nexport type Workflow = z.infer<typeof workflowSchema>\n\n// --------------------------- DAG linter -----------------------------------\n//\n// Replaces the FSM-era `lintWorkflow` from V1. Checks:\n// - exactly one trigger.* node (v1 rule; multi-trigger out of scope)\n// - every edge's from/to references an existing node\n// - no unreachable nodes (except the trigger) — DFS from trigger\n// - no cycles unless every cycle has an agent or checkpoint on it (those\n// are the nodes that can pause or consume external events; cycles\n// without either would infinite-loop the walker)\n// - every branch node's edge.fromPort matches a case in its config\n// - at least one `end` or `checkpoint` is reachable (runs must terminate)\n\nexport function lintWorkflow(wf: Workflow): string[] {\n const issues: string[] = []\n const byId = new Map(wf.nodes.map((n) => [n.id, n] as const))\n\n // 1. exactly one trigger\n const triggers = wf.nodes.filter((n) => n.type.startsWith(\"trigger.\"))\n if (triggers.length === 0) issues.push(\"workflow must have exactly one trigger.* node (found 0)\")\n if (triggers.length > 1) issues.push(`workflow has ${triggers.length} trigger.* nodes; v1 supports exactly one`)\n\n // 2. edge endpoints exist\n for (const e of wf.edges) {\n if (!byId.has(e.from)) issues.push(`edge references missing node: from=\"${e.from}\"`)\n if (!byId.has(e.to)) issues.push(`edge references missing node: to=\"${e.to}\"`)\n }\n\n // 3. reachability from trigger\n const trigger = triggers[0]\n if (trigger) {\n const adj = new Map<string, string[]>()\n for (const e of wf.edges) {\n if (!adj.has(e.from)) adj.set(e.from, [])\n adj.get(e.from)!.push(e.to)\n }\n const seen = new Set<string>()\n const stack = [trigger.id]\n while (stack.length) {\n const id = stack.pop()!\n if (seen.has(id)) continue\n seen.add(id)\n for (const next of adj.get(id) ?? []) stack.push(next)\n }\n for (const n of wf.nodes) {\n if (!seen.has(n.id)) issues.push(`node \"${n.id}\" is unreachable from trigger \"${trigger.id}\"`)\n }\n if (!wf.nodes.some((n) => seen.has(n.id) && isTerminalOrPauseNode(n.type))) {\n issues.push(\"no reachable `end`, `checkpoint`, `subProcess`, `signal.wait`, or `timer.boundary` node — the run cannot terminate or pause\")\n }\n }\n\n // 4. branch ports (also applies to DMN-style `rule` nodes whose rows\n // carry the same `{ to }` shape and a shared `default`).\n for (const n of wf.nodes) {\n if (n.type !== \"branch\" && n.type !== \"rule\") continue\n const cases = n.type === \"branch\"\n ? ((n.config.cases as Array<{ to: string }> | undefined) ?? [])\n : ((n.config.rules as Array<{ to?: string }> | undefined) ?? []).filter((r): r is { to: string } => typeof r.to === \"string\")\n const defaultPort = n.type === \"branch\"\n ? (n.config.default as string | undefined)\n : ((n.config.default as { to?: string } | undefined)?.to)\n const declared = new Set<string>(cases.map((c) => c.to))\n if (defaultPort) declared.add(defaultPort)\n const outgoing = wf.edges.filter((e) => e.from === n.id)\n for (const edge of outgoing) {\n const port = edge.fromPort ?? \"\"\n if (!declared.has(port)) {\n issues.push(`${n.type} \"${n.id}\" has outgoing edge with port \"${port}\" not declared in cases or default`)\n }\n }\n }\n\n // 5. cycle detection — only flag cycles that would infinite-loop (no agent\n // or checkpoint on the cycle).\n const onCycle = detectCyclesWithoutPauseNodes(wf)\n for (const nodeIds of onCycle) {\n issues.push(`cycle without agent/checkpoint: ${nodeIds.join(\" -> \")}`)\n }\n\n return issues\n}\n\nfunction isTerminalOrPauseNode(type: NodeType): boolean {\n return type === \"end\"\n || type === \"checkpoint\"\n || type === \"subProcess\"\n || type === \"signal.wait\"\n || type === \"timer.boundary\"\n}\n\nfunction isPauseCapableNode(type: NodeType): boolean {\n // Nodes that either pause the run (checkpoint, subProcess,\n // signal.wait, timer.boundary) or consume an external event (agent).\n // A cycle is safe if it crosses at least one such node — otherwise the\n // walker would spin forever.\n return type === \"agent\"\n || type === \"checkpoint\"\n || type === \"subProcess\"\n || type === \"signal.wait\"\n || type === \"timer.boundary\"\n}\n\nfunction detectCyclesWithoutPauseNodes(wf: Workflow): string[][] {\n const adj = new Map<string, string[]>()\n for (const e of wf.edges) {\n if (!adj.has(e.from)) adj.set(e.from, [])\n adj.get(e.from)!.push(e.to)\n }\n const byId = new Map(wf.nodes.map((n) => [n.id, n] as const))\n const results: string[][] = []\n const color = new Map<string, 0 | 1 | 2>() // 0=unvisited, 1=in-progress, 2=done\n const stack: string[] = []\n const dfs = (id: string) => {\n if (color.get(id) === 1) {\n // cycle: slice from first occurrence\n const i = stack.indexOf(id)\n if (i >= 0) {\n const cycle = stack.slice(i).concat(id)\n const hasPause = cycle.some((nid) => {\n const t = byId.get(nid)?.type\n return t ? isPauseCapableNode(t) : false\n })\n if (!hasPause) results.push(cycle)\n }\n return\n }\n if (color.get(id) === 2) return\n color.set(id, 1)\n stack.push(id)\n for (const next of adj.get(id) ?? []) dfs(next)\n stack.pop()\n color.set(id, 2)\n }\n for (const n of wf.nodes) if (color.get(n.id) !== 2) dfs(n.id)\n return results\n}\n\n// --------------------------- Run model ------------------------------------\n\nexport const runStatusSchema = z.enum([\n \"running\",\n \"paused\",\n \"completed\",\n \"failed\",\n \"canceled\",\n])\nexport type RunStatus = z.infer<typeof runStatusSchema>\n\nexport const nodeExecutionStatusSchema = z.enum([\n \"ok\",\n \"failed\",\n \"timeout\",\n \"skipped\",\n \"paused\",\n \"resumed\",\n])\nexport type NodeExecutionStatus = z.infer<typeof nodeExecutionStatusSchema>\n\nexport const nodeExecutionEntrySchema = z.object({\n at: z.string(), // ISO-8601\n nodeId: z.string(),\n /** Which upstream node outputs fed this execution. Usually one id, or\n * empty for the trigger. */\n inputKeys: z.array(z.string()).default([]),\n status: nodeExecutionStatusSchema,\n /** The node's output bundle, if successful. Stored here so the full run\n * history can be replayed without the live context. */\n output: z.record(z.unknown()).optional(),\n /** Per-(runId, nodeId, eventId) key used to drop duplicate webhook\n * deliveries when a run is resumed from a checkpoint. */\n idempotencyKey: z.string(),\n note: z.string().optional(),\n})\nexport type NodeExecutionEntry = z.infer<typeof nodeExecutionEntrySchema>\n\nexport const entityRefSchema = z.object({\n backend: z.string(),\n /** Stable identifier for the entity this run is scoped to. Used to key\n * the entity index so webhook re-entry can find the active run. */\n id: z.string(),\n})\nexport type EntityRef = z.infer<typeof entityRefSchema>\n\n// Discriminated `pausedAt` — each kind has its own resume path in the\n// dispatcher. The original checkpoint shape is the `checkpoint` variant;\n// new BPM nodes add subProcess / signalWait / timerWait.\nexport const pausedAtSchema = z.discriminatedUnion(\"kind\", [\n z.object({\n kind: z.literal(\"checkpoint\"),\n nodeId: z.string(),\n checkpointName: z.string(),\n /** Filter applied to incoming events for resume. Same shape as the\n * matching trigger.channel filter. */\n resumeMatch: z.record(z.unknown()).default({}),\n }),\n z.object({\n kind: z.literal(\"subProcess\"),\n nodeId: z.string(),\n childRunId: z.string(),\n childWorkflowId: z.string(),\n }),\n z.object({\n kind: z.literal(\"signalWait\"),\n nodeId: z.string(),\n signalName: z.string(),\n /** Optional field-match filter against the emitted signal payload. */\n match: z.record(z.unknown()).default({}),\n scope: z.enum([\"workflow\", \"global\"]).default(\"workflow\"),\n }),\n z.object({\n kind: z.literal(\"timerWait\"),\n nodeId: z.string(),\n /** ISO-8601 instant when the timer should fire. */\n fireAt: z.string(),\n }),\n])\nexport type PausedAt = z.infer<typeof pausedAtSchema>\n\nexport const workflowRunSchema = z.object({\n id: z.string(),\n workflowId: z.string(),\n workflowVersion: z.literal(2),\n homeNode: z.string(),\n status: runStatusSchema,\n pausedAt: pausedAtSchema.optional(),\n /** Output bundles keyed by producing node id. Downstream nodes read via\n * `{{<nodeId>.<path>}}` in templates. */\n context: z.record(z.record(z.unknown())).default({}),\n /** Node ids queued for execution next. The walk driver pops from this\n * until it's empty or a checkpoint pauses the run. */\n pending: z.array(z.string()).default([]),\n /** Per-`gateway.parallel(mode=join)` arrival tracker. Keys are join node\n * ids; values are the upstream node ids that have already delivered.\n * The join fires once when arrived.length === #incoming edges. Entries\n * are deleted from the map on fire so repeated passes (rare, but\n * possible via crash + retry) stay idempotent. */\n joinCounters: z.record(z.array(z.string())).default({}),\n entityRef: entityRefSchema,\n history: z.array(nodeExecutionEntrySchema).default([]),\n /** Parent run id if this run is a sub-process child. Root runs have no\n * parent. */\n parentRunId: z.string().nullable().default(null),\n /** The parent node in the parent run that spawned this child. Null on\n * root runs. */\n parentNodeId: z.string().nullable().default(null),\n /** Root ancestor. Self-referential on root runs. Lets queries fetch the\n * entire composition tree in one shot. */\n rootRunId: z.string().nullable().default(null),\n /** Depth from root. 0 on root runs, N on Nth-level descendants. */\n depth: z.number().int().min(0).default(0),\n createdAt: z.string(),\n updatedAt: z.string(),\n})\nexport type WorkflowRun = z.infer<typeof workflowRunSchema>\n","import type { TraceRecord, TraceStepRecord } from \"@/storage/traces\"\nimport { lintWorkflow, workflowSchema, type Workflow } from \"./types\"\n\n// --- LLM workflow architect ---\n//\n// The deterministic draft path produces a 3-node workflow: trigger → agent\n// → end, with the entire procedure crammed into the agent's prompt. That's\n// useful as a sanity-check skeleton but un-edittable in any meaningful\n// way — there's no DAG structure for an operator to refine.\n//\n// This module asks an LLM to do the structural work: read the trace + step\n// list, group related tool calls into semantic nodes, identify branching\n// points, and emit a proper workflow DAG. We force tool_use so the response\n// is structured JSON, validate against `workflowSchema`, and retry once\n// with the validation errors as feedback if the first attempt failed.\n//\n// Failure modes:\n// - ANTHROPIC_API_KEY missing → throw (caller falls back to deterministic)\n// - API error / timeout → throw (caller falls back to deterministic)\n// - Both attempts produce schema-invalid workflows → throw (caller falls back)\n//\n// Why direct API instead of the agent registry: same reasoning as\n// extract.structured — registry routing keeps a turn slot busy for >2s on\n// warm cache, the SDK adds 10MB+ of dependencies, and the auth path\n// (ANTHROPIC_API_KEY) is identical.\n\nexport interface ArchitectOptions {\n /** Anthropic model id. Defaults to claude-sonnet-4-6 — sonnet handles\n * multi-step decomposition well; haiku is cheaper but tends to flatten\n * branches. */\n model?: string\n /** Per-call timeout for each Anthropic round-trip. Default 90s. */\n timeoutMs?: number\n /** Cap output tokens. Default 4000 — enough for ~30-node workflows. */\n maxTokens?: number\n /** Hook used by tests to inject a fake fetch. Production uses global fetch. */\n fetchImpl?: typeof fetch\n}\n\nconst ANTHROPIC_API = \"https://api.anthropic.com/v1/messages\"\nconst ANTHROPIC_VERSION = \"2023-06-01\"\nconst DEFAULT_MODEL = \"claude-sonnet-4-6\"\n\nconst ARCHITECT_SYSTEM = `You are a workflow architect for AgentX. Given a successful task trace, decompose it into a REUSABLE workflow DAG. The same workflow must apply across project, platform, and environment variations — extract anything that varies between runs into the trigger's inputSchema and reference it symbolically.\n\nPARAMETERIZATION (this is the most important rule):\nThe procedure should be one workflow, not one workflow per project / branch / environment. When you see specific values in the trace, ask \"would this be different on a future run of the same procedure?\" — if yes, extract them as inputs.\n\nAlways extract into trigger.config.inputSchema.properties when the trace shows them:\n- platform — \"github\" | \"gitlab\" (only when both are plausible for this work)\n- project — full repo path or group/repo identifier (\"mtgl/mtgl_system\", \"anis-marrouchi/agentx\")\n- environment — \"staging\" | \"production\" | \"dev\" (infer from hostnames, paths, message text)\n- ref — branch / tag / commit (\"master\", \"main\", \"release/2.4\")\n- id — issue / MR / PR number\n- host — SSH target or service hostname\n- agentId — only when the work could legitimately be assigned to different agents\n\nEach input must be a typed JSON-Schema property under trigger.config.inputSchema.properties with type, description, and (where useful) enum, default, or example. List the actual required ones in trigger.config.inputSchema.required.\n\nReference inputs symbolically EVERYWHERE:\n- BAD: \"ssh root@staging.mtgl.com.sa 'cd /var/www/test_mtglbackend && git pull origin master'\"\n- GOOD: \"ssh {{trigger.input.host}} 'cd {{trigger.input.path}} && git pull origin {{trigger.input.ref}}'\"\n\nCross-reference between nodes the same way: {{previousNodeId.outputField}} for chained steps.\n\nOUTPUT CONTRACT:\nThe end node must declare what the workflow produces — operators reading the workflow card need to see what comes out. Use end.config.output as a structured object literal of the form { status: \"...\", \"<key>\": \"{{node.field}}\" }, not a free-text string. Pull values from the nodes that produced them.\n\nDecomposition rules:\n- Group related tool calls into one semantic node. Five Bash calls that pull a repo, install deps, run migrations, restart services, and verify status are ONE deploy node, not five.\n- Prefer LINEAR workflows. Use branch ONLY when the procedure had a clear conditional fork that future runs will also need (e.g. \"if migration → backup db first; else skip\"). Don't invent branches the trace didn't actually exercise.\n- Use deterministic node types (action.run, action.builtin, transform) when the work is mechanical.\n- Use an \"agent\" node only when the step genuinely requires LLM reasoning (drafting prose, classifying intent, synthesizing output).\n\nAvailable node types and their configs:\n- trigger.manual config: { inputSchema: { type:\"object\", properties: {...}, required: [...] } }\n- agent config: { agentId: string, prompt: string }\n- transform config: { expression: string } (jq-style data shaping)\n- action.run config: { command: string } (shell, supports {{templates}})\n- action.send config: { channel: string, chatId: string, text: string }\n- action.builtin config: { name: string, input: object } (e.g. name: \"http.fetch\")\n- extract.structured config: { prompt: string, schema: object }\n- branch config: { cases: [{ when: <Condition>, to: <port-name> }, ...], default?: <port-name> }\n- rule config: { rows: [...] } (DMN decision table)\n- checkpoint config: { label: string }\n- end config: { status: \"completed\"|\"failed\", output: object }\n\nBranch/rule mechanics — read carefully:\n- The \"to\" field in cases is a PORT NAME (a string label), NOT a node id.\n- Each outgoing edge from a branch must have fromPort equal to one of the port names declared in cases or in default.\n- The edge.to field is the destination node id.\n\nWORKED EXAMPLE — a deploy workflow that handles ANY project/env/branch via inputs:\n{\n \"title\": \"Deploy MR to environment\",\n \"description\": \"Pull a branch onto a host, run migrations if present, rebuild Laravel caches. Same procedure for any project/env/branch — pass them as inputs.\",\n \"nodes\": [\n {\n \"id\": \"trigger\",\n \"type\": \"trigger.manual\",\n \"config\": {\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\n \"project\": { \"type\": \"string\", \"description\": \"group/repo (e.g. 'mtgl/mtgl_system')\" },\n \"environment\": { \"type\": \"string\", \"enum\": [\"staging\", \"production\", \"dev\"], \"default\": \"staging\" },\n \"host\": { \"type\": \"string\", \"description\": \"SSH target hostname (e.g. 'staging.mtgl.com.sa')\" },\n \"path\": { \"type\": \"string\", \"description\": \"Absolute path on host where the project is deployed\" },\n \"ref\": { \"type\": \"string\", \"description\": \"Branch/tag/commit to deploy\", \"default\": \"master\" }\n },\n \"required\": [\"project\", \"environment\", \"host\", \"path\", \"ref\"]\n }\n }\n },\n { \"id\": \"pull\", \"type\": \"action.run\", \"config\": { \"command\": \"ssh {{trigger.input.host}} 'cd {{trigger.input.path}} && git pull origin {{trigger.input.ref}}'\" } },\n { \"id\": \"needs_backup\", \"type\": \"branch\", \"config\": { \"cases\": [{ \"when\": { \"kind\": \"matches\", \"params\": { \"path\": \"pull.stdout\", \"pattern\": \"migrate\" } }, \"to\": \"yes\" }], \"default\": \"no\" } },\n { \"id\": \"backup_db\", \"type\": \"action.run\", \"config\": { \"command\": \"ssh {{trigger.input.host}} 'pg_dump app > /backups/{{trigger.input.environment}}-$(date +%Y%m%d-%H%M%S).sql'\" } },\n { \"id\": \"migrate\", \"type\": \"action.run\", \"config\": { \"command\": \"ssh {{trigger.input.host}} 'cd {{trigger.input.path}} && php artisan migrate --force'\" } },\n { \"id\": \"rebuild_cache\", \"type\": \"action.run\", \"config\": { \"command\": \"ssh {{trigger.input.host}} 'cd {{trigger.input.path}} && php artisan config:cache && php artisan route:cache'\" } },\n {\n \"id\": \"done\",\n \"type\": \"end\",\n \"config\": {\n \"status\": \"completed\",\n \"output\": {\n \"project\": \"{{trigger.input.project}}\",\n \"environment\": \"{{trigger.input.environment}}\",\n \"ref\": \"{{trigger.input.ref}}\",\n \"deployedAt\": \"{{rebuild_cache.completedAt}}\",\n \"migrated\": \"{{needs_backup.matchedPort}}\"\n }\n }\n }\n ],\n \"edges\": [\n { \"from\": \"trigger\", \"to\": \"pull\" },\n { \"from\": \"pull\", \"to\": \"needs_backup\" },\n { \"from\": \"needs_backup\", \"to\": \"backup_db\", \"fromPort\": \"yes\" },\n { \"from\": \"needs_backup\", \"to\": \"migrate\", \"fromPort\": \"no\" },\n { \"from\": \"backup_db\", \"to\": \"migrate\" },\n { \"from\": \"migrate\", \"to\": \"rebuild_cache\" },\n { \"from\": \"rebuild_cache\", \"to\": \"done\" }\n ]\n}\n\nNotice three things:\n1. The trigger declares a typed inputSchema. An operator running this workflow knows EXACTLY what to pass in.\n2. Every command references {{trigger.input.X}}, never literal hostnames or paths.\n3. The end node's output is a structured object so callers know what they get back.\n\nConstraints:\n- Exactly one trigger.* node and at least one \"end\" node.\n- DAG only (no cycles).\n- Every edge's from/to must reference an existing node id.\n- Node ids match /^[a-z0-9][a-z0-9_]*$/.\n- Don't invent fields not in the spec above. Don't add config keys you didn't see in this spec.\n- The trigger MUST declare inputSchema unless the procedure genuinely takes no parameters.`\n\n/** JSON-Schema fed to the Anthropic forced tool_use. Shape mirrors the\n * subset of workflowSchema that the LLM should produce — the rest of the\n * workflow (status, sourceTaskIds, etc.) is filled in by the caller. */\nconst ARCHITECT_OUTPUT_SCHEMA: Record<string, unknown> = {\n type: \"object\",\n required: [\"title\", \"description\", \"nodes\", \"edges\"],\n properties: {\n title: { type: \"string\", maxLength: 120 },\n description: { type: \"string\", maxLength: 600 },\n nodes: {\n type: \"array\",\n minItems: 2,\n maxItems: 30,\n items: {\n type: \"object\",\n required: [\"id\", \"type\", \"config\"],\n properties: {\n id: { type: \"string\", pattern: \"^[a-z0-9][a-z0-9_]*$\", maxLength: 48 },\n type: {\n type: \"string\",\n enum: [\n \"trigger.manual\",\n \"agent\",\n \"transform\",\n \"branch\",\n \"rule\",\n \"action.run\",\n \"action.send\",\n \"action.builtin\",\n \"extract.structured\",\n \"checkpoint\",\n \"end\",\n ],\n },\n config: { type: \"object\" },\n },\n },\n },\n edges: {\n type: \"array\",\n maxItems: 60,\n items: {\n type: \"object\",\n required: [\"from\", \"to\"],\n properties: {\n from: { type: \"string\" },\n to: { type: \"string\" },\n fromPort: { type: \"string\" },\n label: { type: \"string\" },\n },\n },\n },\n },\n}\n\nfunction cap(s: string | null | undefined, n: number): string {\n const t = String(s ?? \"\").trim()\n return t.length > n ? `${t.slice(0, n)}…` : t\n}\n\nfunction summarizeTraceForPrompt(trace: TraceRecord, steps: TraceStepRecord[]): string {\n const useful = steps.filter((s) => s.status !== \"error\" && s.name !== \"system\").slice(0, 40)\n const lines = useful.map((s, i) => {\n const label = [s.name, s.action].filter(Boolean).join(\":\")\n const inSummary = cap(s.inputSummary || \"\", 280)\n const outSummary = cap(s.outputSummary || \"\", 280)\n const parts = [inSummary && `in=${inSummary}`, outSummary && `out=${outSummary}`].filter(Boolean).join(\" | \")\n return `${i + 1}. ${label}${parts ? ` ${parts}` : \"\"}`\n })\n return [\n `Original task message:`,\n cap(trace.messagePreview || \"\", 600),\n ``,\n `Agent: ${trace.agentId} (channel=${trace.channel || \"unknown\"}, chatId=${trace.chatId || \"—\"})`,\n `Outcome: ${trace.status}, steps=${steps.length}`,\n ``,\n `Procedure evidence (filtered, in order):`,\n ...lines,\n ].join(\"\\n\")\n}\n\ninterface ArchitectDraftShape {\n title: string\n description: string\n nodes: Array<{ id: string; type: string; config: Record<string, unknown> }>\n edges: Array<{ from: string; to: string; fromPort?: string; label?: string }>\n}\n\nasync function callAnthropic(\n apiKey: string,\n model: string,\n systemPrompt: string,\n userMessage: string,\n feedback: string | null,\n opts: { maxTokens: number; timeoutMs: number; fetchImpl: typeof fetch },\n): Promise<{ data: ArchitectDraftShape; usage: { inputTokens: number; outputTokens: number } }> {\n const messages: Array<{ role: string; content: string }> = [\n { role: \"user\", content: userMessage },\n ]\n if (feedback) {\n messages.push({\n role: \"assistant\",\n content: \"(previous attempt; will retry with corrections)\",\n })\n messages.push({ role: \"user\", content: feedback })\n }\n\n const requestBody = {\n model,\n max_tokens: opts.maxTokens,\n system: systemPrompt,\n messages,\n tools: [{\n name: \"emit_workflow\",\n description: \"Return the workflow DAG. Must be valid against the schema.\",\n input_schema: ARCHITECT_OUTPUT_SCHEMA,\n }],\n tool_choice: { type: \"tool\", name: \"emit_workflow\" },\n }\n\n const ctrl = new AbortController()\n const t = setTimeout(() => ctrl.abort(), opts.timeoutMs)\n let res: Response\n try {\n res = await opts.fetchImpl(ANTHROPIC_API, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"x-api-key\": apiKey,\n \"anthropic-version\": ANTHROPIC_VERSION,\n },\n body: JSON.stringify(requestBody),\n signal: ctrl.signal,\n })\n } finally {\n clearTimeout(t)\n }\n\n if (!res.ok) {\n const errBody = await res.text().catch(() => \"\")\n throw new Error(`anthropic API ${res.status}: ${errBody.slice(0, 500)}`)\n }\n const body: any = await res.json()\n const blocks = Array.isArray(body.content) ? body.content : []\n const toolUse = blocks.find((b: any) => b?.type === \"tool_use\" && b?.name === \"emit_workflow\")\n if (!toolUse || !toolUse.input) {\n throw new Error(\"anthropic API returned no emit_workflow tool_use\")\n }\n const usage = body.usage ?? {}\n return {\n data: toolUse.input as ArchitectDraftShape,\n usage: {\n inputTokens: typeof usage.input_tokens === \"number\" ? usage.input_tokens : 0,\n outputTokens: typeof usage.output_tokens === \"number\" ? usage.output_tokens : 0,\n },\n }\n}\n\nfunction assembleWorkflow(\n raw: ArchitectDraftShape,\n trace: TraceRecord,\n steps: TraceStepRecord[],\n inferredId: string,\n sourceTaskIds: string[],\n confidence: number,\n): Workflow {\n const channelTags = new Set<string>()\n if (trace.channel) channelTags.add(trace.channel)\n for (const s of steps) {\n if (s.action) channelTags.add(s.action.toLowerCase().replace(/[^a-z0-9]+/g, \"-\").replace(/^-|-$/g, \"\"))\n }\n return workflowSchema.parse({\n id: inferredId,\n version: 2,\n title: cap(raw.title, 120) || `Draft: ${cap(trace.messagePreview, 72) || trace.taskId.slice(0, 8)}`,\n description: cap(raw.description, 600) || `LLM-architected from task trace ${trace.taskId}.`,\n status: \"draft\",\n state: \"disabled\",\n tags: Array.from(channelTags).slice(0, 12),\n entity: trace.chatId || undefined,\n generatedFrom: \"llm-architect\",\n sourceTaskIds,\n confidence,\n workflowVersion: \"draft-1\",\n ownerAgent: trace.agentId,\n nodes: raw.nodes,\n edges: raw.edges,\n envAllow: [],\n retention: { maxRuns: 500, maxDays: 90 },\n maxChildDepth: 5,\n })\n}\n\n/** Pull the first balanced JSON object out of free-form agent reply text.\n * Agents tend to wrap JSON in fenced code blocks, prose preambles, or both.\n * We scan for `{` and walk forward counting braces, ignoring braces inside\n * string literals. Returns null if no balanced object is found. */\nfunction extractFirstJsonObject(text: string): string | null {\n // Strip code fences first — they're the common case and the brace walker\n // doesn't need to handle them, but they're free to remove.\n const fenced = text.match(/```(?:json)?\\s*([\\s\\S]*?)```/i)\n const haystack = fenced ? fenced[1] : text\n const start = haystack.indexOf(\"{\")\n if (start < 0) return null\n let depth = 0\n let inString = false\n let escape = false\n for (let i = start; i < haystack.length; i++) {\n const ch = haystack[i]\n if (escape) { escape = false; continue }\n if (ch === \"\\\\\" && inString) { escape = true; continue }\n if (ch === '\"') { inString = !inString; continue }\n if (inString) continue\n if (ch === \"{\") depth++\n else if (ch === \"}\") {\n depth--\n if (depth === 0) return haystack.slice(start, i + 1)\n }\n }\n return null\n}\n\n/** Dispatch a task to the agent registry over HTTP, ask the agent to emit\n * workflow JSON, parse + validate. Same fallback semantics as the direct\n * API path: throw on any failure so the caller can fall back to the\n * deterministic single-node draft. Uses the daemon's POST /task endpoint\n * rather than registry.execute() directly because the architect runs from\n * the CLI process which doesn't have the registry in scope. */\nasync function callAgent(\n agentId: string,\n systemPrompt: string,\n userMessage: string,\n feedback: string | null,\n chatId: string,\n opts: { daemonUrl: string; timeoutMs: number; fetchImpl: typeof fetch },\n): Promise<{ data: ArchitectDraftShape; usage: { inputTokens: number; outputTokens: number } }> {\n // First-attempt: full system + spec + trace. Retry: short corrective\n // message — the agent already has its own prior reply in context via\n // claude --resume, so we don't need to repeat the spec. Stable chatId\n // (passed in from the caller) keeps both calls in the same session.\n const fullMessage = feedback\n ? [\n `Your previous response failed workflow-schema validation with these issues:`,\n feedback,\n ``,\n `Re-emit the corrected workflow as a single JSON object — same shape as before, but fix the listed issues. No prose, no code fences.`,\n ].join(\"\\n\")\n : [\n systemPrompt,\n \"\",\n \"---\",\n \"\",\n \"Task trace to architect:\",\n \"\",\n userMessage,\n \"\",\n \"---\",\n \"\",\n `Reply with ONLY a JSON object matching this shape (no prose, no fenced code block, no commentary):`,\n `{`,\n ` \"title\": \"<short title>\",`,\n ` \"description\": \"<short description>\",`,\n ` \"nodes\": [{ \"id\": \"...\", \"type\": \"...\", \"config\": {} }, ...],`,\n ` \"edges\": [{ \"from\": \"...\", \"to\": \"...\", \"fromPort\": \"...\" (optional), \"label\": \"...\" (optional) }, ...]`,\n `}`,\n ].join(\"\\n\")\n\n const ctrl = new AbortController()\n const timer = setTimeout(() => ctrl.abort(), opts.timeoutMs)\n let res: Response\n try {\n res = await opts.fetchImpl(`${opts.daemonUrl.replace(/\\/+$/, \"\")}/task`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n agent: agentId,\n message: fullMessage,\n // Only the first call uses freshSession — the retry needs to see\n // the agent's prior reply (via --resume replay) so it can fix\n // its own output rather than start over from scratch.\n freshSession: feedback === null,\n context: { channel: \"api\", chatId },\n }),\n signal: ctrl.signal,\n })\n } finally {\n clearTimeout(timer)\n }\n if (!res.ok) {\n const errBody = await res.text().catch(() => \"\")\n throw new Error(`agent /task ${res.status}: ${errBody.slice(0, 500)}`)\n }\n const body: any = await res.json()\n const content = String(body.content ?? \"\").trim()\n if (body.error) throw new Error(`agent reported error: ${body.error}`)\n if (!content) throw new Error(\"agent returned empty content\")\n\n const jsonStr = extractFirstJsonObject(content)\n if (!jsonStr) throw new Error(`agent reply has no JSON object (first 200 chars: ${content.slice(0, 200)})`)\n let parsed: unknown\n try { parsed = JSON.parse(jsonStr) } catch (e: any) {\n throw new Error(`agent reply JSON is unparseable: ${e.message}`)\n }\n if (!parsed || typeof parsed !== \"object\") throw new Error(\"agent reply is not a JSON object\")\n const data = parsed as ArchitectDraftShape\n if (!Array.isArray(data.nodes) || !Array.isArray(data.edges)) {\n throw new Error(\"agent reply missing nodes/edges arrays\")\n }\n\n const usage = body.usage ?? {}\n return {\n data,\n usage: {\n inputTokens: typeof usage.inputTokens === \"number\" ? usage.inputTokens : 0,\n outputTokens: typeof usage.outputTokens === \"number\" ? usage.outputTokens : 0,\n },\n }\n}\n\n/** Architect a workflow via the agent registry instead of direct Anthropic\n * API. Mirrors `architectWorkflowFromTrace` but routes the LLM call through\n * POST /task → registry.execute → claude-code subprocess (or codex-cli).\n * Operators don't need a raw ANTHROPIC_API_KEY — the agent's own session\n * token handles auth. Trade-off: ~5-10s per call vs ~2s for direct API,\n * fine for cron use. */\nexport async function architectWorkflowViaAgent(\n trace: TraceRecord,\n steps: TraceStepRecord[],\n inferredId: string,\n sourceTaskIds: string[],\n confidence: number,\n opts: { agentId: string; daemonUrl?: string; timeoutMs?: number; fetchImpl?: typeof fetch } = { agentId: \"\" },\n): Promise<{ workflow: Workflow; usage: { inputTokens: number; outputTokens: number }; via: string }> {\n if (!opts.agentId) throw new Error(\"architectWorkflowViaAgent: agentId is required\")\n const daemonUrl = opts.daemonUrl || \"http://127.0.0.1:18800\"\n const fetchImpl = opts.fetchImpl || fetch\n const callOpts = {\n daemonUrl,\n timeoutMs: opts.timeoutMs ?? 180_000, // 3 min — agent dispatch is slower than direct API\n fetchImpl,\n }\n const userMessage = summarizeTraceForPrompt(trace, steps)\n // Stable per-trace chatId so the retry's --resume replay surfaces the\n // agent's first reply. SessionStore keys on (agentId, channel, chatId);\n // sessions persist past the call but the unique-per-trace shape means\n // unrelated absorbs don't share context.\n const chatId = `architect-${trace.taskId.toLowerCase()}`\n\n const first = await callAgent(opts.agentId, ARCHITECT_SYSTEM, userMessage, null, chatId, callOpts)\n let usage = first.usage\n let raw = first.data\n\n let workflow: Workflow\n try {\n workflow = assembleWorkflow(raw, trace, steps, inferredId, sourceTaskIds, confidence)\n const lintIssues = lintWorkflow(workflow)\n if (lintIssues.length) throw new Error(`lint: ${lintIssues.join(\"; \")}`)\n return { workflow, usage, via: `agent:${opts.agentId}` }\n } catch (err: any) {\n const retry = await callAgent(opts.agentId, ARCHITECT_SYSTEM, userMessage, err.message, chatId, callOpts)\n raw = retry.data\n usage = {\n inputTokens: usage.inputTokens + retry.usage.inputTokens,\n outputTokens: usage.outputTokens + retry.usage.outputTokens,\n }\n workflow = assembleWorkflow(raw, trace, steps, inferredId, sourceTaskIds, confidence)\n const lintIssues = lintWorkflow(workflow)\n if (lintIssues.length) throw new Error(`agent architect retry still invalid: ${lintIssues.join(\"; \")}`)\n return { workflow, usage, via: `agent:${opts.agentId}` }\n }\n}\n\n/** Architect a workflow from a task trace using an LLM. Throws on any\n * failure (caller falls back to deterministic single-node draft). */\nexport async function architectWorkflowFromTrace(\n trace: TraceRecord,\n steps: TraceStepRecord[],\n inferredId: string,\n sourceTaskIds: string[],\n confidence: number,\n opts: ArchitectOptions = {},\n): Promise<{ workflow: Workflow; usage: { inputTokens: number; outputTokens: number }; model: string }> {\n const apiKey = process.env.ANTHROPIC_API_KEY\n if (!apiKey) throw new Error(\"ANTHROPIC_API_KEY not set; LLM architect unavailable\")\n\n const model = opts.model || DEFAULT_MODEL\n const fetchImpl = opts.fetchImpl || fetch\n const callOpts = {\n maxTokens: opts.maxTokens ?? 4000,\n timeoutMs: opts.timeoutMs ?? 90_000,\n fetchImpl,\n }\n\n const userMessage = summarizeTraceForPrompt(trace, steps)\n\n // First attempt.\n const firstAttempt = await callAnthropic(apiKey, model, ARCHITECT_SYSTEM, userMessage, null, callOpts)\n let raw = firstAttempt.data\n let usage = firstAttempt.usage\n\n let workflow: Workflow\n try {\n workflow = assembleWorkflow(raw, trace, steps, inferredId, sourceTaskIds, confidence)\n const lintIssues = lintWorkflow(workflow)\n if (lintIssues.length) throw new Error(`lint: ${lintIssues.join(\"; \")}`)\n return { workflow, usage, model }\n } catch (err: any) {\n // Retry once with structured feedback so the model can correct.\n const feedback = `Your previous response failed validation: ${err.message}. Re-emit emit_workflow with the corrections. Keep the same overall structure but fix the listed issues.`\n const retry = await callAnthropic(apiKey, model, ARCHITECT_SYSTEM, userMessage, feedback, callOpts)\n raw = retry.data\n usage = {\n inputTokens: usage.inputTokens + retry.usage.inputTokens,\n outputTokens: usage.outputTokens + retry.usage.outputTokens,\n }\n workflow = assembleWorkflow(raw, trace, steps, inferredId, sourceTaskIds, confidence)\n const lintIssues = lintWorkflow(workflow)\n if (lintIssues.length) throw new Error(`LLM architect retry still invalid: ${lintIssues.join(\"; \")}`)\n return { workflow, usage, model }\n }\n}\n"],"mappings":"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","names":["z","nodeTypeSchema","conditionKindSchema","conditionSchema","retryPolicySchema","workflowNodeSchema","workflowEdgeSchema","isoDateString","d","workflowSchema","ns","lintWorkflow","wf","issues","byId","triggers","e","trigger","adj","seen","stack","id","next","n","isTerminalOrPauseNode","cases","r","defaultPort","declared","c","outgoing","edge","port","onCycle","detectCyclesWithoutPauseNodes","nodeIds","type","isPauseCapableNode","results","color","dfs","i","cycle","nid","t","runStatusSchema","nodeExecutionStatusSchema","nodeExecutionEntrySchema","entityRefSchema","pausedAtSchema","workflowRunSchema","ANTHROPIC_API","ANTHROPIC_VERSION","DEFAULT_MODEL","ARCHITECT_SYSTEM","ARCHITECT_OUTPUT_SCHEMA","cap","s","n","t","summarizeTraceForPrompt","trace","steps","lines","label","inSummary","outSummary","parts","callAnthropic","apiKey","model","systemPrompt","userMessage","feedback","opts","messages","requestBody","ctrl","res","errBody","body","toolUse","b","usage","assembleWorkflow","raw","inferredId","sourceTaskIds","confidence","channelTags","workflowSchema","extractFirstJsonObject","text","fenced","haystack","start","depth","inString","escape","i","ch","callAgent","agentId","chatId","fullMessage","timer","content","jsonStr","parsed","e","data","architectWorkflowViaAgent","daemonUrl","fetchImpl","callOpts","first","workflow","lintIssues","lintWorkflow","err","retry","architectWorkflowFromTrace","firstAttempt"]}
|
package/dist/chunk-JP5IEXR7.js
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import{a as w,b as y,c as x}from"./chunk-SFQUP3BP.js";import{m as g}from"./chunk-FDWOECJY.js";import{existsSync as d,promises as h}from"fs";import c from"path";import{execa as C}from"execa";var b={enabled:!0,maxAttempts:3,provider:"claude-code"},v=class{constructor(e,r,i){this.cwd=e;this.config={...b,...r},this.memory=i||new x(e)}config;memory;async detectAndHeal(e,r,i){if(!this.config.enabled)return{healed:!1,attempts:0,error:"Heal disabled",filesChanged:[]};let t=await this.runVerification();if(!t)return{healed:!0,attempts:0,error:"",filesChanged:[]};let n=0,s=t,a=[];for(;n<this.config.maxAttempts;){n++;let l=await this.generateFix(s,e,r,n);if(!l.files.length)break;for(let o of l.files){let m=c.isAbsolute(o.path)?o.path:c.resolve(this.cwd,o.path),p=c.dirname(m);await h.mkdir(p,{recursive:!0}),await h.writeFile(m,o.content,"utf8"),a.push(o.path)}let f=await this.runVerification();if(!f)return i&&await this.memory.recordHeal(i,t,!0,a),{healed:!0,attempts:n,error:t,fix:l.explanation,filesChanged:a};s=f}return i&&await this.memory.recordHeal(i,t,!1,a),{healed:!1,attempts:n,error:s,filesChanged:a}}async runVerification(){let e=[this.config.buildCommand,this.config.lintCommand,this.config.testCommand].filter(Boolean);if(!e.length){let r=await this.detectCommands();e.push(...r)}for(let r of e)try{let[i,...t]=r.split(" ");await C(i,t,{cwd:this.cwd,timeout:6e4,reject:!0})}catch(i){let t=i.stderr||"",n=i.stdout||"",s=i.message||"";return k(`Command failed: ${r}
|
|
2
|
-
${t}
|
|
3
|
-
${n}
|
|
4
|
-
${s}`)}return null}async detectCommands(){let e=[],r=c.resolve(this.cwd,"package.json");if(d(r))try{let t=JSON.parse(await h.readFile(r,"utf8")).scripts||{};t.build&&e.push("npm run build"),t.typecheck&&e.push("npm run typecheck"),t.lint&&e.push("npm run lint"),t.test&&e.push("npm run test")}catch{}return d(c.resolve(this.cwd,"pyproject.toml"))&&d(c.resolve(this.cwd,"conftest.py"))&&e.push("python -m pytest --tb=short -q"),e}async generateFix(e,r,i,t){let n=g(this.config.provider,this.config.apiKey),s=[];for(let o of r.slice(0,5)){let m=c.resolve(this.cwd,o);if(d(m)){let p=await h.readFile(m,"utf8");s.push(`## ${o}
|
|
5
|
-
\`\`\`
|
|
6
|
-
${p.slice(0,3e3)}
|
|
7
|
-
\`\`\``)}}let a=await w(this.cwd),l=[{role:"system",content:`You are agentx auto-heal. You fix errors in generated code.
|
|
8
|
-
|
|
9
|
-
Project tech stack: ${y(a)}
|
|
10
|
-
|
|
11
|
-
RULES:
|
|
12
|
-
- Fix ONLY the error \u2014 do not refactor or add features
|
|
13
|
-
- Output the COMPLETE fixed file content via create_files
|
|
14
|
-
- Be minimal \u2014 smallest change that fixes the error
|
|
15
|
-
- This is attempt ${t} \u2014 ${t>1?"previous fix attempts failed, try a different approach":"analyze carefully"}`},{role:"user",content:`The following code was generated for task: "${i}"
|
|
16
|
-
|
|
17
|
-
## Error
|
|
18
|
-
\`\`\`
|
|
19
|
-
${e}
|
|
20
|
-
\`\`\`
|
|
21
|
-
|
|
22
|
-
## Affected Files
|
|
23
|
-
${s.join(`
|
|
24
|
-
|
|
25
|
-
`)}
|
|
26
|
-
|
|
27
|
-
Fix the error. Use create_files with the corrected file content.`}],f=await n.generate(l,{model:this.config.model,maxTokens:8192});return{files:f.files,explanation:f.content}}};function k(u){let e=u.split(`
|
|
28
|
-
`).filter(r=>r.trim());return e.length>30?e.slice(0,30).join(`
|
|
29
|
-
`)+`
|
|
30
|
-
... (truncated)`:e.join(`
|
|
31
|
-
`)}export{v as a};
|
|
32
|
-
//# sourceMappingURL=chunk-JP5IEXR7.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/graph/types.ts"],"sourcesContent":["import { z } from \"zod\"\n\n// --- Intent Knowledge Graph types + Zod schemas ---\n//\n// A hierarchical, enumerable intent taxonomy with fixed axes per level.\n// See plan: /Users/macbookpro/.claude/plans/delightful-floating-diffie.md\n//\n// Shape on disk (.agentx/graph/):\n// schema.json — level definitions + required axes per level\n// nodes.json — tree of nodes: { id, level, parentId, axes }\n// classifications.jsonl — append-only log of message → path decisions\n// index.json — fingerprint → pathId snap-to-path cache\n\nexport const axisTypeSchema = z.enum([\"enum\", \"free\", \"ref\"])\nexport type AxisType = z.infer<typeof axisTypeSchema>\n\nexport const axisDefSchema = z.object({\n name: z.string().regex(/^[a-z][a-z0-9_]*$/i, \"axis name must be a simple identifier\"),\n type: axisTypeSchema,\n /** For type=\"enum\": closed set of allowed values. */\n values: z.array(z.string()).optional(),\n /** For type=\"ref\": which level's nodes may be referenced. */\n refLevel: z.string().optional(),\n /** If true, this axis may be absent from a node without failing validation.\n * Use for axes that apply conditionally (e.g. a department's \"lead\" ref\n * doesn't apply to a person-kind unit). Absent/false = required. */\n optional: z.boolean().optional(),\n /** Optional human-readable help, shown as tooltip in the admin UI. */\n description: z.string().optional(),\n})\nexport type AxisDef = z.infer<typeof axisDefSchema>\n\nexport const levelDefSchema = z.object({\n id: z.string().regex(/^[a-z][a-z0-9_-]*$/, \"level id must be lowercase slug\"),\n /** Axes every node at this level MUST carry. Order matters for UI rendering. */\n axes: z.array(axisDefSchema).min(1),\n description: z.string().optional(),\n})\nexport type LevelDef = z.infer<typeof levelDefSchema>\n\nexport const graphSchemaSchema = z.object({\n version: z.number().int().default(1),\n /** Ordered from root to leaf. Max 5 to keep the taxonomy scannable. */\n levels: z.array(levelDefSchema).min(1).max(5),\n /** Axis describing where a leaf classification came from (channel, cron, etc.). */\n leafInput: axisDefSchema.optional(),\n /** Free-form description of the expected result of a leaf classification. */\n leafOutput: axisDefSchema.optional(),\n})\nexport type GraphSchema = z.infer<typeof graphSchemaSchema>\n\n// Allow dots so verb-level node ids like \"review.merge-request\" or\n// \"deploy.staging\" parse cleanly (Phase 1 of the classifier-retire plan).\n// Dots are dot-namespaced verbs only — never the leading char.\nexport const nodeIdSchema = z.string().regex(/^[a-z0-9][a-z0-9._-]*$/, \"node id must be lowercase slug (alnum, '.', '_', '-')\")\n\nexport const graphNodeSchema = z.object({\n id: nodeIdSchema,\n /** Matches a `levels[*].id` from the schema. */\n level: z.string(),\n /** null for root-level nodes (level[0]); otherwise the id of a node at level-1. */\n parentId: nodeIdSchema.nullable(),\n /** Values for every axis declared at this level's schema entry. `ref` axes\n * hold the id of another node; `enum` axes hold one of the declared values;\n * `free` axes hold any string. */\n axes: z.record(z.string(), z.string()),\n createdAt: z.string(),\n createdBy: z.string().optional(),\n})\nexport type GraphNode = z.infer<typeof graphNodeSchema>\n\nexport const classificationSourceSchema = z.enum([\"llm\", \"user\", \"cache\"])\nexport type ClassificationSource = z.infer<typeof classificationSourceSchema>\n\nexport const classificationStatusSchema = z.enum([\"pending\", \"approved\", \"rejected\"])\nexport type ClassificationStatus = z.infer<typeof classificationStatusSchema>\n\nexport const classificationSchema = z.object({\n ts: z.string(),\n msgHash: z.string(),\n agentId: z.string().optional(),\n channel: z.string().optional(),\n sender: z.string().optional(),\n /** Node ids from root to leaf, one per level. Shorter-than-full paths are\n * allowed if the classifier was only confident down to a certain depth. */\n path: z.array(nodeIdSchema),\n /** Axis values asserted by the classifier for new nodes it proposed along\n * the path. Keyed by node id. */\n proposedAxes: z.record(z.string(), z.record(z.string(), z.string())).default({}),\n /** Leaf-level extras. */\n leaf: z.object({\n input: z.string().optional(),\n output: z.string().optional(),\n }).partial().default({}),\n source: classificationSourceSchema,\n status: classificationStatusSchema,\n confidence: z.number().min(0).max(1).optional(),\n /** Redacted first ~200 chars of the message, for the approval UI. */\n preview: z.string().optional(),\n})\nexport type Classification = z.infer<typeof classificationSchema>\n\n/** Row in the fingerprint → path snap cache. */\nexport const fingerprintEntrySchema = z.object({\n fingerprint: z.string(),\n path: z.array(nodeIdSchema),\n leaf: classificationSchema.shape.leaf,\n updatedAt: z.string(),\n})\nexport type FingerprintEntry = z.infer<typeof fingerprintEntrySchema>\n\n/** Nodes file is a plain list, not a nested tree — lookups are by id. */\nexport const nodesFileSchema = z.object({\n version: z.number().int().default(1),\n nodes: z.array(graphNodeSchema).default([]),\n})\nexport type NodesFile = z.infer<typeof nodesFileSchema>\n\n/** Index file — record of fingerprints. Hot path; kept as a map on disk. */\nexport const indexFileSchema = z.object({\n version: z.number().int().default(1),\n entries: z.record(z.string(), fingerprintEntrySchema).default({}),\n})\nexport type IndexFile = z.infer<typeof indexFileSchema>\n"],"mappings":"AAAA,OAAS,KAAAA,MAAS,MAaX,IAAMC,EAAiBD,EAAE,KAAK,CAAC,OAAQ,OAAQ,KAAK,CAAC,EAG/CE,EAAgBF,EAAE,OAAO,CACpC,KAAMA,EAAE,OAAO,EAAE,MAAM,qBAAsB,uCAAuC,EACpF,KAAMC,EAEN,OAAQD,EAAE,MAAMA,EAAE,OAAO,CAAC,EAAE,SAAS,EAErC,SAAUA,EAAE,OAAO,EAAE,SAAS,EAI9B,SAAUA,EAAE,QAAQ,EAAE,SAAS,EAE/B,YAAaA,EAAE,OAAO,EAAE,SAAS,CACnC,CAAC,EAGYG,EAAiBH,EAAE,OAAO,CACrC,GAAIA,EAAE,OAAO,EAAE,MAAM,qBAAsB,iCAAiC,EAE5E,KAAMA,EAAE,MAAME,CAAa,EAAE,IAAI,CAAC,EAClC,YAAaF,EAAE,OAAO,EAAE,SAAS,CACnC,CAAC,EAGYI,EAAoBJ,EAAE,OAAO,CACxC,QAASA,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,CAAC,EAEnC,OAAQA,EAAE,MAAMG,CAAc,EAAE,IAAI,CAAC,EAAE,IAAI,CAAC,EAE5C,UAAWD,EAAc,SAAS,EAElC,WAAYA,EAAc,SAAS,CACrC,CAAC,EAMYG,EAAeL,EAAE,OAAO,EAAE,MAAM,yBAA0B,uDAAuD,EAEjHM,EAAkBN,EAAE,OAAO,CACtC,GAAIK,EAEJ,MAAOL,EAAE,OAAO,EAEhB,SAAUK,EAAa,SAAS,EAIhC,KAAML,EAAE,OAAOA,EAAE,OAAO,EAAGA,EAAE,OAAO,CAAC,EACrC,UAAWA,EAAE,OAAO,EACpB,UAAWA,EAAE,OAAO,EAAE,SAAS,CACjC,CAAC,EAGYO,EAA6BP,EAAE,KAAK,CAAC,MAAO,OAAQ,OAAO,CAAC,EAG5DQ,EAA6BR,EAAE,KAAK,CAAC,UAAW,WAAY,UAAU,CAAC,EAGvES,EAAuBT,EAAE,OAAO,CAC3C,GAAIA,EAAE,OAAO,EACb,QAASA,EAAE,OAAO,EAClB,QAASA,EAAE,OAAO,EAAE,SAAS,EAC7B,QAASA,EAAE,OAAO,EAAE,SAAS,EAC7B,OAAQA,EAAE,OAAO,EAAE,SAAS,EAG5B,KAAMA,EAAE,MAAMK,CAAY,EAG1B,aAAcL,EAAE,OAAOA,EAAE,OAAO,EAAGA,EAAE,OAAOA,EAAE,OAAO,EAAGA,EAAE,OAAO,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC,EAE/E,KAAMA,EAAE,OAAO,CACb,MAAOA,EAAE,OAAO,EAAE,SAAS,EAC3B,OAAQA,EAAE,OAAO,EAAE,SAAS,CAC9B,CAAC,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC,EACvB,OAAQO,EACR,OAAQC,EACR,WAAYR,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,CAAC,EAAE,SAAS,EAE9C,QAASA,EAAE,OAAO,EAAE,SAAS,CAC/B,CAAC,EAIYU,EAAyBV,EAAE,OAAO,CAC7C,YAAaA,EAAE,OAAO,EACtB,KAAMA,EAAE,MAAMK,CAAY,EAC1B,KAAMI,EAAqB,MAAM,KACjC,UAAWT,EAAE,OAAO,CACtB,CAAC,EAIYW,EAAkBX,EAAE,OAAO,CACtC,QAASA,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,CAAC,EACnC,MAAOA,EAAE,MAAMM,CAAe,EAAE,QAAQ,CAAC,CAAC,CAC5C,CAAC,EAIYM,EAAkBZ,EAAE,OAAO,CACtC,QAASA,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,CAAC,EACnC,QAASA,EAAE,OAAOA,EAAE,OAAO,EAAGU,CAAsB,EAAE,QAAQ,CAAC,CAAC,CAClE,CAAC","names":["z","axisTypeSchema","axisDefSchema","levelDefSchema","graphSchemaSchema","nodeIdSchema","graphNodeSchema","classificationSourceSchema","classificationStatusSchema","classificationSchema","fingerprintEntrySchema","nodesFileSchema","indexFileSchema"]}
|
package/dist/chunk-K2CAPYES.js
DELETED
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
import{d as m,e as $}from"./chunk-BTHHSY7D.js";import{readFileSync as S,writeFileSync as x,existsSync as b}from"fs";import{resolve as f}from"path";function P(r){if(r)return f(r);let e=[f(process.cwd(),"agentx.json"),f(process.cwd(),".agentx/config.json")];for(let n of e)if(b(n))return n;return e[0]}function w(r){return JSON.parse(JSON.stringify(r))}async function v(r){try{let e=await fetch(`${r.replace(/\/$/,"")}/reload`,{method:"POST",signal:AbortSignal.timeout(3e3)});return e.ok?{ok:!0}:{ok:!1,reason:`HTTP ${e.status}`}}catch(e){return{ok:!1,reason:e?.cause?.code||e?.message||"unreachable"}}}async function T(r,e={}){let{validate:n=!0,dryRun:t=!1,reload:s=!0,configPath:u,daemonUrl:c}=e,o=P(u);if(!b(o))return{success:!1,path:o,error:`Config not found at ${o}. Run \`agentx init\` first.`};let d;try{d=S(o,"utf-8")}catch(a){return{success:!1,path:o,error:`Cannot read ${o}: ${a.message}`}}let g;try{g=JSON.parse(d)}catch(a){return{success:!1,path:o,error:`Invalid JSON in ${o}: ${a.message}`}}let i=w(g);try{await r(i)}catch(a){return{success:!1,path:o,error:`Mutator threw: ${a.message}`}}if(n){let a=$(i),l=m.safeParse(a);if(!l.success){let k=l.error.issues.map(y=>` ${y.path.join(".")||"<root>"}: ${y.message}`).join(`
|
|
2
|
-
`);return{success:!1,path:o,error:`Validation failed:
|
|
3
|
-
${k}`,after:i}}}if(t)return{success:!0,path:o,after:i,dryRun:!0};try{x(o,JSON.stringify(i,null,2)+`
|
|
4
|
-
`)}catch(a){return{success:!1,path:o,error:`Write failed: ${a.message}`,after:i}}let p=!1,h;if(s){let a=c||O(i),l=await v(a);l.ok?p=!0:h=l.reason}return{success:!0,path:o,after:i,reloaded:p,reloadSkipped:h}}function O(r){let e=r?.node?.bind;if(!e)return"http://127.0.0.1:18800";let[n,t]=e.split(":");return`http://${n==="0.0.0.0"||n==="::"?"127.0.0.1":n}:${t}`}function J(r,e,n){let t=e.split("."),s=r;for(let u=0;u<t.length-1;u++){let c=t[u];(s[c]===void 0||s[c]===null)&&(s[c]={}),s=s[c]}s[t[t.length-1]]=n}function N(r,e){let n=e.split("."),t=r;for(let s of n){if(t==null)return;t=t[s]}return t}function j(r,e){let n=e.split("."),t=r;for(let s=0;s<n.length-1;s++){if(t==null)return;t=t[n[s]]}t!=null&&delete t[n[n.length-1]]}export{T as a,J as b,N as c,j as d};
|
|
5
|
-
//# sourceMappingURL=chunk-K2CAPYES.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/daemon/topbar.ts"],"sourcesContent":["// --- Shared top-chrome for every dashboard surface ---\n//\n// Four pages render in different files (live + kanban + glossary in\n// board-dashboard.ts, settings in admin-panel.ts) but must share: design\n// tokens, fonts, theme switcher, brand, primary tab nav, and a mesh-node\n// selector. This module exports the CSS + HTML snippets so each page\n// composes exactly the same chrome and we never drift them again.\n//\n// Nothing here is page-specific — callers pass in { activeTab, subtitle,\n// subheader? } and compose their own <main> below.\n\nexport type TopbarTab = \"live\" | \"mesh\" | \"boards\" | \"admin\" | \"graph\" | \"glossary\" | \"workflows\" | \"health\" | \"cost\" | \"wiki\" | \"procedures\" | \"inbox\" | \"projects\"\n\nexport interface TopbarPeer {\n /** Stable id: primary node id, or URL for configured daemons */\n id: string\n name: string\n /** Absolute URL of the peer's dashboard (http://host:port) — selector\n * navigates to <url>/admin when switched. */\n dashboardUrl: string\n /** True for the node hosting this dashboard (locks the default). */\n primary?: boolean\n /** Optional; shown in the dropdown as \"managing via token\" hint. */\n tokenScope?: string\n}\n\nexport interface TopbarFeatures {\n /** Show the Team/Business Settings sub-tabs — config.business.enabled. */\n business?: boolean\n}\n\n// Set once at server start (board-dashboard + daemon both call it).\n//\n// This used to carry `boards` and `workflows` flags that gated top-level\n// tabs. The nav is now fixed (Live, Operations, Settings), so those flags had\n// nothing left to gate and were removed rather than left describing chrome\n// that no longer exists.\nlet topbarFeatures: TopbarFeatures = {}\n\nexport function setTopbarFeatures(features: TopbarFeatures): void {\n topbarFeatures = features\n}\n\nexport function getTopbarFeatures(): TopbarFeatures {\n return topbarFeatures\n}\n\nexport interface TopbarOpts {\n activeTab: TopbarTab\n /** Text after the brand, e.g. \"Live\" / \"Settings\" / \"Boards\". */\n subtitle: string\n /** Optional HTML inserted as a second row under the topbar. */\n subheader?: string\n /** Peers to list in the mesh selector. */\n peers?: TopbarPeer[]\n /** id of the peer currently being managed — defaults to the primary. */\n currentPeerId?: string\n /** Optional right-side HTML added before the theme switcher (clocks,\n * connection dots, etc.). */\n rightExtras?: string\n}\n\n/**\n * Font link tags + tiny inline bootstrap script. Callers drop this in <head>.\n */\n// Fonts degrade to the system stack when the box is offline or air-gapped —\n// the tokens list real fallbacks, so a failed font fetch costs typography,\n// never layout.\n//\n// The theme bootstrap runs before first paint to avoid a flash. A stored\n// \"crt\" from before that theme was removed falls back to dark rather than\n// leaving data-theme set to a value no stylesheet defines.\nexport const TOPBAR_HEAD = `<link rel=\"preconnect\" href=\"https://fonts.googleapis.com\">\n<link rel=\"preconnect\" href=\"https://fonts.gstatic.com\" crossorigin>\n<link href=\"https://fonts.googleapis.com/css2?family=Outfit:wght@400;500;600;700;800&family=Roboto+Mono:wght@400;500;600&display=swap\" rel=\"stylesheet\">\n<script>(function(){try{var t=localStorage.getItem('ax-theme');if(t!=='light'&&t!=='dark'){t='dark';localStorage.setItem('ax-theme',t)}document.documentElement.setAttribute('data-theme',t)}catch(e){document.documentElement.setAttribute('data-theme','dark')}})();</script>`\n\n/**\n * CSS for topbar + subheader + theme switch + mesh selector. No token\n * definitions — those are per-page because each page's CSS block owns its\n * own :root overrides.\n */\nexport const TOPBAR_CSS = `\n.ax-topbar{display:flex;align-items:center;justify-content:space-between;\n padding:8px 18px;border-bottom:1px solid var(--ax-border);\n background:var(--ax-bg-elev);position:sticky;top:0;z-index:20;gap:10px;flex-wrap:nowrap}\n.ax-topbar__left{display:flex;align-items:center;gap:18px;min-width:0}\n.ax-brand{display:flex;align-items:center;gap:10px;font-weight:600;letter-spacing:-0.01em;flex-shrink:0}\n.ax-brand__mark{font-family:var(--ax-mono);font-size:12px;padding:2px 8px;\n border:1px solid var(--ax-border-2);color:var(--ax-accent);border-radius:4px}\n.ax-brand__name{font-size:14px}\n.ax-brand__subtitle{font-size:11px;color:var(--ax-muted);\n border-left:1px solid var(--ax-border);padding-left:12px;margin-left:4px}\n.ax-topbar__tabs{display:flex;gap:14px;min-width:0;overflow-x:auto;align-items:center}\n/* Tier groups (System / Processes / Procedures) — visual separators\n * mirror the docs/architecture/three-tier.md hierarchy. */\n.ax-topbar__group{display:flex;align-items:center;gap:0;padding:0 4px;\n border-left:1px solid var(--ax-border);position:relative}\n.ax-topbar__group:first-child{border-left:none;padding-left:0}\n.ax-topbar__group-label{font:inherit;font-family:var(--ax-mono);font-size:9px;\n letter-spacing:0.08em;text-transform:uppercase;color:var(--ax-muted);\n padding:0 8px;white-space:nowrap;opacity:0.7}\n.ax-topbar__tab{background:transparent;border:none;color:var(--ax-text-2);\n padding:8px 12px;font:inherit;cursor:pointer;font-size:12px;text-decoration:none;\n border-bottom:2px solid transparent;letter-spacing:-0.005em;white-space:nowrap}\n.ax-topbar__tab:hover{color:var(--ax-text)}\n.ax-topbar__tab.is-active{color:var(--ax-text);border-bottom-color:var(--ax-accent)}\n.ax-topbar__right{display:flex;align-items:center;gap:10px;font-size:11px;\n color:var(--ax-muted);flex-shrink:0}\n.ax-topbar__right .ax-mono{color:var(--ax-text-2)}\n\n/* Mesh selector */\n.ax-mesh-sel{position:relative;display:inline-flex;align-items:center;gap:6px;\n padding:4px 10px;border:1px solid var(--ax-border-2);border-radius:4px;\n background:var(--ax-surface);cursor:pointer;font:inherit;font-size:11px;\n color:var(--ax-text-2);white-space:nowrap}\n.ax-mesh-sel:hover{border-color:var(--ax-accent);color:var(--ax-text)}\n.ax-mesh-sel.is-proxy{background:color-mix(in oklch,var(--ax-accent) 14%,var(--ax-surface));\n border-color:color-mix(in oklch,var(--ax-accent) 45%,var(--ax-border-2));color:var(--ax-accent)}\n.ax-mesh-sel .lbl{font-size:9px;letter-spacing:0.06em;text-transform:uppercase;color:var(--ax-muted);margin-right:2px}\n.ax-mesh-sel .name{font-weight:500;color:var(--ax-text)}\n.ax-mesh-sel .chev{font-size:9px;opacity:0.6;margin-left:2px}\n.ax-mesh-menu{position:absolute;top:calc(100% + 4px);right:0;min-width:240px;\n background:var(--ax-surface);border:1px solid var(--ax-border-2);\n border-radius:6px;box-shadow:0 8px 24px rgba(0,0,0,0.4);padding:4px;z-index:30;display:none}\n.ax-mesh-sel.is-open .ax-mesh-menu{display:block}\n.ax-mesh-menu a{display:block;padding:7px 10px;border-radius:4px;text-decoration:none;\n color:var(--ax-text-2);font-size:12px}\n.ax-mesh-menu a:hover{background:var(--ax-surface-2);color:var(--ax-text)}\n.ax-mesh-menu a.is-current{background:color-mix(in oklch,var(--ax-accent) 12%,var(--ax-surface));\n color:var(--ax-accent);border:1px solid color-mix(in oklch,var(--ax-accent) 30%,var(--ax-border-2))}\n.ax-mesh-menu a .row{display:flex;align-items:center;gap:8px}\n.ax-mesh-menu a .row .dot{width:6px;height:6px;border-radius:50%;background:var(--ax-accent)}\n.ax-mesh-menu a.is-offline .row .dot{background:var(--ax-border-2)}\n.ax-mesh-menu a .url{font-family:var(--ax-mono);font-size:10px;color:var(--ax-muted);margin-top:2px}\n\n/* Theme switcher segmented control */\n.ax-theme-switch{display:inline-flex;border:1px solid var(--ax-border-2);\n border-radius:4px;overflow:hidden}\n.ax-theme-switch button{background:transparent;border:none;color:var(--ax-muted);\n padding:3px 9px;font:inherit;font-size:10px;cursor:pointer;letter-spacing:0.04em;\n text-transform:uppercase;font-family:var(--ax-mono);border-right:1px solid var(--ax-border-2)}\n.ax-theme-switch button:last-child{border-right:none}\n.ax-theme-switch button:hover{color:var(--ax-text);background:var(--ax-surface-2,var(--ax-surface))}\n.ax-theme-switch button.is-active{color:var(--ax-accent);\n background:color-mix(in oklch,var(--ax-accent) 12%,var(--ax-surface))}\n\n/* Subheader — page-specific toolbar below the main topbar. Kanban uses it\n * for the board picker + search + filters; others can use it for context\n * bars if they want. */\n.ax-subheader{display:flex;align-items:center;gap:10px;padding:10px 18px;\n background:var(--ax-bg-elev);border-bottom:1px solid var(--ax-border);\n flex-wrap:wrap}\n.ax-subheader > * {flex-shrink:0}\n.ax-subheader .spacer{flex:1}`\n\n/**\n * Shared click wiring for the theme switcher + mesh selector. Safe to call\n * multiple times — only binds once per element.\n */\nexport const TOPBAR_SCRIPT = `<script>\n(function(){\n function wireTheme(){\n var sw = document.querySelectorAll('[data-theme-opt]');\n if (!sw.length) return;\n var current = document.documentElement.getAttribute('data-theme') || 'dark';\n sw.forEach(function(b){\n if (b.dataset.wired) return; b.dataset.wired = '1';\n if (b.getAttribute('data-theme-opt') === current) b.classList.add('is-active');\n b.addEventListener('click', function(){\n var t = b.getAttribute('data-theme-opt');\n document.documentElement.setAttribute('data-theme', t);\n try { localStorage.setItem('ax-theme', t); } catch (e) {}\n sw.forEach(function(x){ x.classList.toggle('is-active', x === b); });\n });\n });\n }\n function currentPeer(){\n try { return localStorage.getItem('ax-peer') || 'primary'; } catch { return 'primary'; }\n }\n function setCurrentPeer(id){\n try {\n if (id && id !== 'primary') localStorage.setItem('ax-peer', id);\n else localStorage.removeItem('ax-peer');\n } catch {}\n }\n function reflectPeerPill(){\n var sel = document.querySelector('.ax-mesh-sel');\n if (!sel) return;\n var cur = currentPeer();\n sel.classList.toggle('is-proxy', cur !== 'primary');\n // If a proxy is active, update the visible pill label + highlight.\n var picked = null;\n sel.querySelectorAll('.ax-mesh-menu a').forEach(function(a){\n a.classList.toggle('is-current', a.getAttribute('data-peer-id') === cur || (cur === 'primary' && a.getAttribute('data-peer-id') === 'primary'));\n if (a.getAttribute('data-peer-id') === cur) picked = a;\n });\n if (picked) {\n var name = picked.querySelector('span'); // first <span> inside .row\n var visibleName = sel.querySelector('.name');\n if (name && visibleName) visibleName.textContent = name.textContent.replace(/ · primary/, '');\n }\n }\n function wireMesh(){\n var sel = document.querySelector('.ax-mesh-sel');\n if (!sel || sel.dataset.wired) return; sel.dataset.wired = '1';\n sel.addEventListener('click', function(e){\n var link = e.target.closest('.ax-mesh-menu a[data-peer-action=\"proxy\"]');\n if (link) {\n e.preventDefault();\n setCurrentPeer(link.getAttribute('data-peer-id'));\n reflectPeerPill();\n sel.classList.remove('is-open');\n // Reload so the admin panel re-reads state from the selected peer.\n location.reload();\n return;\n }\n if (e.target.closest('.ax-mesh-menu a')) return; // plain navigate anchor\n sel.classList.toggle('is-open');\n });\n document.addEventListener('click', function(e){\n if (!sel.contains(e.target)) sel.classList.remove('is-open');\n });\n reflectPeerPill();\n }\n function wireHostRewrite(){\n try {\n var h = location.hostname || 'localhost';\n document.querySelectorAll('.ax-topbar__tab[href*=\"__HOST__\"]').forEach(function(a){\n a.href = a.getAttribute('href').replace(/__HOST__/g, h);\n });\n } catch (e) {}\n }\n /**\n * Make the topbar peer selector actually drive page content. When the\n * operator picks a non-primary peer, every /api/admin/* request gets an\n * X-Agentx-Peer header (and every EventSource on that path gets a ?peer=\n * query, since EventSource can't set headers). The dashboard's existing\n * proxy handler picks it up and forwards to the chosen peer.\n *\n * Without this hook, the activity-graph (and other admin pages) silently\n * keep showing only the local node's data even after the operator picks\n * a different peer in the topbar.\n */\n function wirePeerProxy(){\n if (window.__axPeerWired) return; window.__axPeerWired = true;\n // Path-prefix allowlist for the peer-proxy interceptor. Extend here\n // when a new admin-side surface needs to follow the mesh selector.\n // /api/workflows is the workflow dashboard's read/write API — it\n // lives outside /api/admin for legacy reasons, but the page is part\n // of the admin surface and should proxy when a non-primary peer is\n // selected (otherwise the workflows page on Mac shows local-only\n // YAMLs while the runtime lives on clawd).\n function shouldProxy(url){\n if (!url) return false;\n // Admin-side surfaces that follow the mesh selector. Each entry\n // is a path-prefix match. Extend when a new admin surface lands\n // outside /api/admin/* but should still proxy when the operator\n // picks a non-primary peer.\n // /api/admin/ — original admin surface (activity, ledger, …)\n // /api/workflows — workflow editor + observability page\n // /api/agents — agent dropdowns inside the workflow editor +\n // anywhere else the page asks \"which agents\n // exist on the selected node?\"\n return url.indexOf('/api/admin/') >= 0\n || url.indexOf('/api/workflows') >= 0\n || url.indexOf('/api/agents') >= 0;\n }\n var origFetch = window.fetch.bind(window);\n window.fetch = function(input, init){\n try {\n var peer = currentPeer();\n if (peer && peer !== 'primary') {\n var url = typeof input === 'string' ? input : (input && input.url) || '';\n if (shouldProxy(url)) {\n init = init || {};\n init.headers = new Headers(init.headers || {});\n init.headers.set('X-Agentx-Peer', peer);\n }\n }\n } catch (e) {}\n return origFetch(input, init);\n };\n var OrigES = window.EventSource;\n if (OrigES) {\n window.EventSource = function(url, cfg){\n try {\n var peer = currentPeer();\n if (peer && peer !== 'primary' && typeof url === 'string' && shouldProxy(url)) {\n url += (url.indexOf('?') >= 0 ? '&' : '?') + 'peer=' + encodeURIComponent(peer);\n }\n } catch (e) {}\n return new OrigES(url, cfg);\n };\n window.EventSource.prototype = OrigES.prototype;\n window.EventSource.CONNECTING = OrigES.CONNECTING;\n window.EventSource.OPEN = OrigES.OPEN;\n window.EventSource.CLOSED = OrigES.CLOSED;\n }\n }\n // wirePeerProxy() is synchronous and DOM-independent — it just wraps\n // window.fetch + window.EventSource. Fire it IMMEDIATELY (before\n // DOMContentLoaded) so deferred page bundles that issue their first\n // fetch on bundle-parse (e.g. the workflow editor's React useEffect)\n // see the wrapped fetch and pick up the X-Agentx-Peer header. Without\n // this, the workflow editor's load on Mac:4202 hit local /api/workflows\n // (instead of proxying to the selected peer), got a 404, and silently\n // fell back to blank-graph state — looking like a \"new workflow\".\n wirePeerProxy();\n function wire(){ wireHostRewrite(); wireTheme(); wireMesh(); /* wirePeerProxy already ran */ }\n if (document.readyState === 'loading') {\n document.addEventListener('DOMContentLoaded', wire);\n } else { wire(); }\n})();\n</script>`\n\n/**\n * Render the topbar element. Caller places this at the start of <body>.\n * Emits a matching .ax-subheader row only when `subheader` HTML is provided.\n */\nexport function renderTopbar(opts: TopbarOpts): string {\n const peers = opts.peers && opts.peers.length > 0 ? opts.peers : [{ id: \"local\", name: \"this daemon\", dashboardUrl: \"\", primary: true }]\n const currentId = opts.currentPeerId || peers.find((p) => p.primary)?.id || peers[0].id\n const current = peers.find((p) => p.id === currentId) || peers[0]\n\n const meshMenu = peers.map((p) => {\n const isCurrent = p.id === currentId\n const primaryMark = p.primary ? ' <span style=\"color:var(--ax-muted);font-size:10px\">· primary</span>' : \"\"\n const proxyable = !!p.tokenScope && !p.primary\n // Proxyable peers: use data-peer-id so the click handler switches locally\n // via localStorage + reload. Un-proxyable (no token): legacy navigate.\n const href = proxyable\n ? \"#\"\n : p.dashboardUrl\n ? `${p.dashboardUrl.replace(/\\/+$/, \"\")}/admin`\n : \"#\"\n const dataAttr = proxyable ? ` data-peer-id=\"${esc(p.id)}\" data-peer-action=\"proxy\"` : p.primary ? ` data-peer-id=\"primary\" data-peer-action=\"proxy\"` : \"\"\n const hint = proxyable\n ? '<div class=\"url\" style=\"color:var(--ax-accent)\">manage from this dashboard ✓</div>'\n : p.tokenScope\n ? `<div class=\"url\">${esc(p.tokenScope)}</div>`\n : p.dashboardUrl\n ? '<div class=\"url\" style=\"color:var(--ax-muted)\">(open peer dashboard ↗)</div>'\n : \"\"\n return `<a href=\"${esc(href)}\" class=\"${isCurrent ? \"is-current\" : \"\"}\"${dataAttr}>\n <div class=\"row\"><span class=\"dot\"></span><span>${esc(p.name)}${primaryMark}</span></div>\n ${p.dashboardUrl ? `<div class=\"url\">${esc(p.dashboardUrl)}</div>` : \"\"}\n ${hint}\n </a>`\n }).join(\"\")\n\n const meshSelector = `<div class=\"ax-mesh-sel\" aria-haspopup=\"menu\">\n <span class=\"lbl\">managing</span>\n <span class=\"name\">${esc(current.name)}</span>\n <span class=\"chev\">▾</span>\n <div class=\"ax-mesh-menu\" role=\"menu\">${meshMenu}</div>\n </div>`\n\n // Minimal, mesh-first nav. The primary group answers the four ops\n // questions in order: who's alive and what are they doing (Live),\n // what happened (Ledger), what did it cost (Cost), how is it wired\n // (Settings). A second \"Work\" group appears only when the operator\n // actually configured that surface (boards / workflows) — flags are\n // set once per process via setTopbarFeatures().\n //\n // Deliberately NOT in the nav (all still routable by URL):\n // /admin/ledger /admin/cost what happened, what it cost\n // /workflows /inbox BPM surfaces\n // / (boards) Kanban\n // /admin/health /admin/activity-graph /admin/graph /admin/projects\n // /procedures /admin/wiki /glossary /processes\n //\n // Three tabs are the whole nav. Every page above is one link away from Live\n // or Settings, and none of them is where an operator starts their day —\n // \"who is alive and what are they doing\" (Live) and \"how is this wired\"\n // (Settings) are. A tab bar that lists every surface an app HAS is a site\n // map, not navigation; it makes the two answers people actually want\n // harder to find, which is the opposite of what chrome is for.\n //\n // Removing a tab does not remove the page. If view counts later show a\n // surface earning its place, adding a tab back is one line.\n type Tab = { id: TopbarTab; label: string; href: string; external?: boolean }\n const groups: Array<{ name: string; tabs: Tab[] }> = [\n {\n name: \"Mesh\",\n tabs: [\n { id: \"live\", label: \"Live\", href: \"/live\" },\n { id: \"mesh\", label: \"Operations\", href: \"/mesh\" },\n { id: \"admin\", label: \"Settings\", href: \"/admin\" },\n ],\n },\n ]\n\n const tabs = groups.map((g) => {\n const inner = g.tabs.map((t) => {\n const cls = t.id === opts.activeTab ? \"ax-topbar__tab is-active\" : \"ax-topbar__tab\"\n const extAttrs = t.external ? ` target=\"_blank\" rel=\"noopener\"` : \"\"\n const extGlyph = t.external ? ' <span style=\"font-size:9px;opacity:0.6\">↗</span>' : \"\"\n return `<a href=\"${t.href}\" class=\"${cls}\"${extAttrs}>${t.label}${extGlyph}</a>`\n }).join(\"\")\n return `<div class=\"ax-topbar__group\" title=\"${esc(g.name)} tier\"><span class=\"ax-topbar__group-label\">${esc(g.name)}</span>${inner}</div>`\n }).join(\"\")\n\n const header = `<header class=\"ax-topbar\">\n <div class=\"ax-topbar__left\">\n <div class=\"ax-brand\">\n <span class=\"ax-brand__mark\">AX</span>\n <span class=\"ax-brand__name\">AgentX</span>\n <span class=\"ax-brand__subtitle\">${esc(opts.subtitle)}</span>\n </div>\n <nav class=\"ax-topbar__tabs\">${tabs}</nav>\n </div>\n <div class=\"ax-topbar__right\">\n ${opts.rightExtras || \"\"}\n ${meshSelector}\n <div class=\"ax-theme-switch\" role=\"tablist\" aria-label=\"Theme\">\n <button data-theme-opt=\"dark\">Dark</button>\n <button data-theme-opt=\"light\">Light</button>\n </div>\n </div>\n</header>${opts.subheader ? `<div class=\"ax-subheader\">${opts.subheader}</div>` : \"\"}`\n return header\n}\n\nfunction esc(s: string): string {\n return String(s || \"\").replace(/[&<>\"']/g, (c) => ({ \"&\": \"&\", \"<\": \"<\", \">\": \">\", '\"': \""\", \"'\": \"'\" }[c] as string))\n}\n"],"mappings":"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","names":["topbarFeatures","setTopbarFeatures","features","getTopbarFeatures","TOPBAR_HEAD","TOPBAR_CSS","TOPBAR_SCRIPT","renderTopbar","opts","peers","currentId","p","current","meshMenu","isCurrent","primaryMark","proxyable","href","dataAttr","esc","hint","meshSelector","tabs","g","inner","t","cls","extAttrs","extGlyph","s","c"]}
|