blun-king-cli 9.0.0 → 9.1.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/LIESMICH.txt +32 -13
- package/README.md +39 -20
- package/bin/blun.js +248 -160
- package/bin/core-bootstrap.js +47 -0
- package/bin/king.js +277 -1
- package/bin/launcher-mode.js +15 -3
- package/bin/launcher-runtime.js +293 -0
- package/bin/plugin-bootstrap.js +11 -4
- package/bin/private-paths.js +1577 -10
- package/bin/update-lease.js +399 -0
- package/bin/update-notice.js +1245 -0
- package/blun.mjs +26606 -25239
- package/dist-web/assets/{CodeBlockNode-Bsvp6ZEy.js → CodeBlockNode-CXnTLMNr.js} +1 -1
- package/dist-web/assets/{CodeBlockShell.vue_vue_type_style_index_0_lang-ta7hLjia.js → CodeBlockShell.vue_vue_type_style_index_0_lang-CCF8Ntnq.js} +1 -1
- package/dist-web/assets/{DesignSystemView-BmDDe28A.js → DesignSystemView-BbMOu-XO.js} +1 -1
- package/dist-web/assets/{Tooltip-nEEfaYVY.js → Tooltip-CFQEzqC-.js} +1 -1
- package/dist-web/assets/abnfDiagram-N423BO3Z-CsByy-GW.js +1 -0
- package/dist-web/assets/abnfDiagram-N423BO3Z-Z1APZG5m.js +1 -0
- package/dist-web/assets/arc-C-LXCaja.js +1 -0
- package/dist-web/assets/arc-C4VyWloT.js +1 -0
- package/dist-web/assets/architectureDiagram-T3A2C74G-DZq4L18e.js +36 -0
- package/dist-web/assets/architectureDiagram-T3A2C74G-DpddbUcM.js +36 -0
- package/dist-web/assets/blockDiagram-VBNYF7ZC-BV4sIf98.js +132 -0
- package/dist-web/assets/blockDiagram-VBNYF7ZC-BqccOSMr.js +132 -0
- package/dist-web/assets/{c4Diagram-AAUBKEIU-DtzacMnD.js → c4Diagram-5PPSVZJV-0MRkZlgP.js} +1 -1
- package/dist-web/assets/{c4Diagram-AAUBKEIU-D1VdMx8d.js → c4Diagram-5PPSVZJV-OHAcJrTy.js} +1 -1
- package/dist-web/assets/channel-B2VOQOI-.js +1 -0
- package/dist-web/assets/channel-DKjwM05G.js +1 -0
- package/dist-web/assets/{chunk-ND2GUHAM-DJoY9wNJ.js → chunk-2GRJ4B5K-DqJHs8Fw.js} +1 -1
- package/dist-web/assets/{chunk-ND2GUHAM-C4-rwdcv.js → chunk-2GRJ4B5K-oO6nqeLA.js} +1 -1
- package/dist-web/assets/{chunk-QZHKN3VN-C7YGaShq.js → chunk-2Q5K7J3B-B1nCd91o.js} +1 -1
- package/dist-web/assets/{chunk-QZHKN3VN-_Rz9_NuS.js → chunk-2Q5K7J3B-BC0Hsfqy.js} +1 -1
- package/dist-web/assets/chunk-5RXB4S5H-BfZa4yDH.js +231 -0
- package/dist-web/assets/chunk-5RXB4S5H-neG70kZx.js +231 -0
- package/dist-web/assets/{chunk-FMBD7UC4-DlLNDUx6.js → chunk-5VM5RSS4-l7biGqeH.js} +1 -1
- package/dist-web/assets/{chunk-FMBD7UC4-B2zs_Y-d.js → chunk-5VM5RSS4-u00AcpiJ.js} +1 -1
- package/dist-web/assets/chunk-6Q2QTUOP-BO-F11Gs.js +88 -0
- package/dist-web/assets/chunk-6Q2QTUOP-RUVRPBzG.js +88 -0
- package/dist-web/assets/{chunk-727SXJPM-Dn5wkDO_.js → chunk-GF5L2VYU-Ba-86KTd.js} +5 -5
- package/dist-web/assets/{chunk-727SXJPM-Dk2Xa3bA.js → chunk-GF5L2VYU-BtC69fw0.js} +5 -5
- package/dist-web/assets/{chunk-4BX2VUAB-DxZViaHS.js → chunk-JWPE2WC7-DIOubEqr.js} +1 -1
- package/dist-web/assets/{chunk-4BX2VUAB-WqrE2gaw.js → chunk-JWPE2WC7-u8Pcfhwh.js} +1 -1
- package/dist-web/assets/{chunk-2J33WTMH-Dm_s0_4f.js → chunk-KBJHAD2P-DDUeH_Ba.js} +1 -1
- package/dist-web/assets/{chunk-2J33WTMH-w4sdiKFO.js → chunk-KBJHAD2P-DS3BL64y.js} +1 -1
- package/dist-web/assets/chunk-RYQCIY6F-B9gFMVbS.js +1 -0
- package/dist-web/assets/chunk-RYQCIY6F-xg3EO0le.js +1 -0
- package/dist-web/assets/{chunk-55IACEB6-B5dE1-Um.js → chunk-XXDRQBXY-B60H0rRC.js} +1 -1
- package/dist-web/assets/{chunk-55IACEB6-B12zGIFz.js → chunk-XXDRQBXY-Bn1Xz6TQ.js} +1 -1
- package/dist-web/assets/classDiagram-JCYQIIEL-BA1TjAxj.js +1 -0
- package/dist-web/assets/classDiagram-JCYQIIEL-BNU7Qn4k.js +1 -0
- package/dist-web/assets/classDiagram-v2-OCEON4UE-BA1TjAxj.js +1 -0
- package/dist-web/assets/classDiagram-v2-OCEON4UE-BNU7Qn4k.js +1 -0
- package/dist-web/assets/{cose-bilkent-S5V4N54A-Bi19n8m-.js → cose-bilkent-JH36ORCC-4Ybddg08.js} +1 -1
- package/dist-web/assets/{cose-bilkent-S5V4N54A-c8o8NFp5.js → cose-bilkent-JH36ORCC-DqigO3T4.js} +1 -1
- package/dist-web/assets/cynefin-VYW2F7L2-C-EfYT07.js +178 -0
- package/dist-web/assets/cynefin-VYW2F7L2-rpD93tQ-.js +178 -0
- package/dist-web/assets/cynefinDiagram-MW4NZA55-DWZPLJcD.js +62 -0
- package/dist-web/assets/cynefinDiagram-MW4NZA55-DbTqnkVJ.js +62 -0
- package/dist-web/assets/dagre-VZM6K2ZE-DcM8aEVY.js +4 -0
- package/dist-web/assets/dagre-VZM6K2ZE-M_HVNtqP.js +4 -0
- package/dist-web/assets/diagram-7IWD3JNH-DoNximNF.js +30 -0
- package/dist-web/assets/diagram-7IWD3JNH-UymgsBNa.js +30 -0
- package/dist-web/assets/{diagram-KO2AKTUF-CFQs13i5.js → diagram-B4RE2ZJO-BVA2EhnC.js} +3 -3
- package/dist-web/assets/{diagram-KO2AKTUF-agFIuPJW.js → diagram-B4RE2ZJO-BiYu5WPF.js} +3 -3
- package/dist-web/assets/{diagram-LMA3HP47-TMIXizxR.js → diagram-LBJQPF4R-BD3kkgOO.js} +2 -2
- package/dist-web/assets/{diagram-LMA3HP47-He5zlyMt.js → diagram-LBJQPF4R-m3a1Euj3.js} +2 -2
- package/dist-web/assets/diagram-Q27KOJAE-D1mYQ_cI.js +24 -0
- package/dist-web/assets/diagram-Q27KOJAE-hy_T73PK.js +24 -0
- package/dist-web/assets/diagram-UB23O5K3-BWkoQZ4A.js +41 -0
- package/dist-web/assets/diagram-UB23O5K3-BtX8u6HG.js +41 -0
- package/dist-web/assets/ebnfDiagram-BXEA7PRR-BKs9JUHW.js +1 -0
- package/dist-web/assets/ebnfDiagram-BXEA7PRR-CBAnFkRF.js +1 -0
- package/dist-web/assets/erDiagram-JOGREHBK-3vKcWsxm.js +85 -0
- package/dist-web/assets/erDiagram-JOGREHBK-BIpd6dTf.js +85 -0
- package/dist-web/assets/flowDiagram-UKHOOZJN-D9wBbHm2.js +156 -0
- package/dist-web/assets/flowDiagram-UKHOOZJN-Z83i2UWI.js +156 -0
- package/dist-web/assets/ganttDiagram-PKOTCBZU-Cd_dViF-.js +292 -0
- package/dist-web/assets/ganttDiagram-PKOTCBZU-DFrb_S1d.js +292 -0
- package/dist-web/assets/{gitGraphDiagram-PVQCEYII-BSDFfwdY.js → gitGraphDiagram-DS77QQ5N-BiEEtTo7.js} +3 -3
- package/dist-web/assets/{gitGraphDiagram-PVQCEYII-DMbTATyw.js → gitGraphDiagram-DS77QQ5N-Dp3Ar-hi.js} +3 -3
- package/dist-web/assets/{graph-BwjfAU3j.js → graph-CAUpWmoK.js} +1 -1
- package/dist-web/assets/{graph--OzhPTMs.js → graph-DOmOIIwC.js} +1 -1
- package/dist-web/assets/{index-W6VnSrkB.js → index-Ba1OvLjf.js} +2 -2
- package/dist-web/assets/{index-CkVgGDpx.js → index-CiPA8Kex.js} +4 -4
- package/dist-web/assets/{index-BzK4vJBu.js → index-DdEakFJM.js} +1 -1
- package/dist-web/assets/{index10-Czg_oF-W.js → index10-CNr4U-Nw.js} +1 -1
- package/dist-web/assets/{index11-BjzCZuCr.js → index11-Dto_yZNz.js} +1 -1
- package/dist-web/assets/{index5-DlVoNkCD.js → index5-B2PldaO2.js} +1 -1
- package/dist-web/assets/{index6-BxiXA2yo.js → index6-CBdZmmyx.js} +1 -1
- package/dist-web/assets/{index7-BPdza6d1.js → index7-Dz9oCsUF.js} +1 -1
- package/dist-web/assets/{index8-Q_XzGC1Z.js → index8-B6k25jtU.js} +2 -2
- package/dist-web/assets/{infoDiagram-5YYISTIA-2JF3XEdD.js → infoDiagram-6WML65LV-BSa2g04G.js} +1 -1
- package/dist-web/assets/{infoDiagram-5YYISTIA-UrQusrJq.js → infoDiagram-6WML65LV-D4ph2sIH.js} +1 -1
- package/dist-web/assets/{ishikawaDiagram-YF4QCWOH-7RVUL-vB.js → ishikawaDiagram-WSZJBQD7-DjKjbMm9.js} +5 -5
- package/dist-web/assets/{ishikawaDiagram-YF4QCWOH-C5OstRVV.js → ishikawaDiagram-WSZJBQD7-Hm36UOQe.js} +5 -5
- package/dist-web/assets/{journeyDiagram-JHISSGLW-B34fCMu9.js → journeyDiagram-NVQOT4AX-DttHDfPQ.js} +1 -1
- package/dist-web/assets/{journeyDiagram-JHISSGLW-yctdo4bX.js → journeyDiagram-NVQOT4AX-MvWWPNDV.js} +1 -1
- package/dist-web/assets/{kanban-definition-UN3LZRKU-BS4FBZsQ.js → kanban-definition-27J2QSJJ-C5gB-W0d.js} +7 -7
- package/dist-web/assets/{kanban-definition-UN3LZRKU-CxBCv27E.js → kanban-definition-27J2QSJJ-Cu-E_cXA.js} +5 -5
- package/dist-web/assets/layout-CvP6o9AK.js +1 -0
- package/dist-web/assets/layout-D-LzfAck.js +1 -0
- package/dist-web/assets/{linear-3mB6q2-g.js → linear-B3HWgQI2.js} +1 -1
- package/dist-web/assets/{linear-BKCnUo31.js → linear-BRGe4-d6.js} +1 -1
- package/dist-web/assets/map-CWKZqZjr.js +1 -0
- package/dist-web/assets/map-DxJ2ADlA.js +1 -0
- package/dist-web/assets/mermaid.core-D-9Sx1No.js +314 -0
- package/dist-web/assets/mermaidParser.worker-PTFfzpoB.js +318 -0
- package/dist-web/assets/{mindmap-definition-RKZ34NQL-BmJZpjh6.js → mindmap-definition-FAOFIHXS-C4vnms3i.js} +2 -2
- package/dist-web/assets/{mindmap-definition-RKZ34NQL-1mjWUcf6.js → mindmap-definition-FAOFIHXS-Cg_hoVIq.js} +1 -1
- package/dist-web/assets/pegDiagram-VL7TDLO6-B2fFEFUH.js +1 -0
- package/dist-web/assets/pegDiagram-VL7TDLO6-CSX9hitD.js +1 -0
- package/dist-web/assets/pieDiagram-7S7Q4E2Y-0L0mxahZ.js +39 -0
- package/dist-web/assets/pieDiagram-7S7Q4E2Y-C7T0Cmw1.js +39 -0
- package/dist-web/assets/{quadrantDiagram-W4KKPZXB-BmT_qM09.js → quadrantDiagram-CIZ2JOQS-BBy7w4kD.js} +3 -3
- package/dist-web/assets/{quadrantDiagram-W4KKPZXB-C0kPuyNP.js → quadrantDiagram-CIZ2JOQS-U483z0er.js} +3 -3
- package/dist-web/assets/railroadDiagram-AXF67PYL-D80a2_DJ.js +1 -0
- package/dist-web/assets/railroadDiagram-AXF67PYL-DX8y0MzW.js +1 -0
- package/dist-web/assets/{requirementDiagram-4Y6WPE33-C0YP_Amt.js → requirementDiagram-LRYGKXZP-CeyHfq8t.js} +3 -3
- package/dist-web/assets/{requirementDiagram-4Y6WPE33-CZrxH1Y2.js → requirementDiagram-LRYGKXZP-DGD5J_4k.js} +3 -3
- package/dist-web/assets/sankeyDiagram-W5VNT64P-CCISkkrR.js +40 -0
- package/dist-web/assets/sankeyDiagram-W5VNT64P-CT5PdZz2.js +40 -0
- package/dist-web/assets/sequenceDiagram-SI44F4Z6-BeMzHWXI.js +162 -0
- package/dist-web/assets/sequenceDiagram-SI44F4Z6-jQfm2XS7.js +162 -0
- package/dist-web/assets/sizeCapture-X5ZJPWSS-BQsTdtrv.js +1 -0
- package/dist-web/assets/sizeCapture-X5ZJPWSS-lRZsmCoS.js +1 -0
- package/dist-web/assets/stateDiagram-OKZ733FA-ClCgTdUm.js +1 -0
- package/dist-web/assets/stateDiagram-OKZ733FA-Cv59z5YT.js +1 -0
- package/dist-web/assets/stateDiagram-v2-UEYNNEHI-R8jTc--F.js +1 -0
- package/dist-web/assets/stateDiagram-v2-UEYNNEHI-fi_lrTAx.js +1 -0
- package/dist-web/assets/swimlanes-SLNWSIFB-BCdOUN05.js +2 -0
- package/dist-web/assets/swimlanes-SLNWSIFB-Cu5l7SuR.js +1 -0
- package/dist-web/assets/swimlanesDiagram-ULZ7WXOC-BUd_uYK3.js +8 -0
- package/dist-web/assets/swimlanesDiagram-ULZ7WXOC-CalrsgPK.js +8 -0
- package/dist-web/assets/{timeline-definition-PNZ67QCA-DsVZdrZa.js → timeline-definition-Z64GVDOM-CMjnh4bu.js} +4 -4
- package/dist-web/assets/{timeline-definition-PNZ67QCA-DnB6Usl0.js → timeline-definition-Z64GVDOM-DpWutSKa.js} +4 -4
- package/dist-web/assets/vennDiagram-T6HMQDX7-DKJW_8oU.js +34 -0
- package/dist-web/assets/vennDiagram-T6HMQDX7-DVC5JVsc.js +34 -0
- package/dist-web/assets/{vue.runtime.esm-bundler-pgHGheCW.js → vue.runtime.esm-bundler-BwwRtQwQ.js} +1 -1
- package/dist-web/assets/{wardleyDiagram-YWT4CUSO-oBauTCEw.js → wardleyDiagram-T6FBY63Y-BK2mrq7L.js} +3 -3
- package/dist-web/assets/{wardleyDiagram-YWT4CUSO-DCiyDQNB.js → wardleyDiagram-T6FBY63Y-oUvgepFn.js} +3 -3
- package/dist-web/assets/xychartDiagram-ELKLHX3M-DI6n79q0.js +7 -0
- package/dist-web/assets/xychartDiagram-ELKLHX3M-DUJu9F7-.js +7 -0
- package/dist-web/index.html +1 -1
- package/dist-web/vis/index.html.gz +0 -0
- package/native/win32/prebuilds/win32-arm64/win32-console-mode.node +0 -0
- package/native/win32/prebuilds/win32-x64/win32-console-mode.node +0 -0
- package/package.json +9 -11
- package/skills/screenshot-lesen/SKILL.md +0 -1
- package/skills/web-lesen/SKILL.md +0 -1
- package/telegram-plugin/dist/bridge.mjs +7 -25
- package/telegram-plugin/dist/mcp-server.mjs +236 -71
- package/dist-web/assets/arc-4xPfymbf.js +0 -1
- package/dist-web/assets/arc-Doj0wRZ0.js +0 -1
- package/dist-web/assets/architectureDiagram-3BPJPVTR-BGih283a.js +0 -36
- package/dist-web/assets/architectureDiagram-3BPJPVTR-CQ09RrbH.js +0 -36
- package/dist-web/assets/blockDiagram-GPEHLZMM-3ShMf1gu.js +0 -132
- package/dist-web/assets/blockDiagram-GPEHLZMM-CAtDjkJt.js +0 -132
- package/dist-web/assets/channel-DNkUo9e6.js +0 -1
- package/dist-web/assets/channel-DcnUTIGB.js +0 -1
- package/dist-web/assets/chunk-AQP2D5EJ-FS8-f8lF.js +0 -231
- package/dist-web/assets/chunk-AQP2D5EJ-UKhb4V4r.js +0 -231
- package/dist-web/assets/classDiagram-4FO5ZUOK-B1ZO8EbE.js +0 -1
- package/dist-web/assets/classDiagram-4FO5ZUOK-xk6cM9Kq.js +0 -1
- package/dist-web/assets/classDiagram-v2-Q7XG4LA2-B1ZO8EbE.js +0 -1
- package/dist-web/assets/classDiagram-v2-Q7XG4LA2-xk6cM9Kq.js +0 -1
- package/dist-web/assets/dagre-BM42HDAG-CmUO9qKN.js +0 -4
- package/dist-web/assets/dagre-BM42HDAG-DLMfLCoV.js +0 -4
- package/dist-web/assets/diagram-2AECGRRQ-DQTst0OH.js +0 -43
- package/dist-web/assets/diagram-2AECGRRQ-L74iLMqd.js +0 -43
- package/dist-web/assets/diagram-5GNKFQAL-BiBm7CJR.js +0 -10
- package/dist-web/assets/diagram-5GNKFQAL-DoE6q7H1.js +0 -10
- package/dist-web/assets/diagram-OG6HWLK6-BHCt1E0n.js +0 -24
- package/dist-web/assets/diagram-OG6HWLK6-DRyxjFMa.js +0 -24
- package/dist-web/assets/erDiagram-TEJ5UH35-BoDczjGG.js +0 -85
- package/dist-web/assets/erDiagram-TEJ5UH35-yd6SOv_7.js +0 -85
- package/dist-web/assets/flowDiagram-I6XJVG4X-DHoceI7r.js +0 -162
- package/dist-web/assets/flowDiagram-I6XJVG4X-DkYNQv0R.js +0 -162
- package/dist-web/assets/ganttDiagram-6RSMTGT7-Cg1TQBc0.js +0 -292
- package/dist-web/assets/ganttDiagram-6RSMTGT7-Z6klo7KK.js +0 -292
- package/dist-web/assets/layout-C1ojF0zw.js +0 -1
- package/dist-web/assets/layout-SsrduOYp.js +0 -1
- package/dist-web/assets/mermaid.core-D_lKT0dR.js +0 -309
- package/dist-web/assets/mermaidParser.worker-Dx4jPi9z.js +0 -313
- package/dist-web/assets/pieDiagram-4H26LBE5-BU41t2A_.js +0 -30
- package/dist-web/assets/pieDiagram-4H26LBE5-BkwDAvbt.js +0 -30
- package/dist-web/assets/sankeyDiagram-5OEKKPKP-De_o7hDr.js +0 -40
- package/dist-web/assets/sankeyDiagram-5OEKKPKP-Dvuj6bMA.js +0 -40
- package/dist-web/assets/sequenceDiagram-3UESZ5HK-DuCrgPyA.js +0 -162
- package/dist-web/assets/sequenceDiagram-3UESZ5HK-VAxffBe7.js +0 -162
- package/dist-web/assets/stateDiagram-AJRCARHV-CYLupxM7.js +0 -1
- package/dist-web/assets/stateDiagram-AJRCARHV-nDExQydZ.js +0 -1
- package/dist-web/assets/stateDiagram-v2-BHNVJYJU-BhEc7Thx.js +0 -1
- package/dist-web/assets/stateDiagram-v2-BHNVJYJU-BzwD_BIl.js +0 -1
- package/dist-web/assets/vennDiagram-CIIHVFJN-BBHQmCFf.js +0 -34
- package/dist-web/assets/vennDiagram-CIIHVFJN-Cfj-K9NW.js +0 -34
- package/dist-web/assets/wardley-L42UT6IY-BJFn8eDD.js +0 -173
- package/dist-web/assets/wardley-L42UT6IY-BSu8-4hg.js +0 -173
- package/dist-web/assets/xychartDiagram-2RQKCTM6-C2iZpl9I.js +0 -7
- package/dist-web/assets/xychartDiagram-2RQKCTM6-Db_VoDBV.js +0 -7
- package/mnemo/access_routes.js +0 -692
- package/mnemo/agent_governance.js +0 -4242
- package/mnemo/agent_mail.js +0 -901
- package/mnemo/bootstrap_auto.js +0 -137
- package/mnemo/brief_coordination.js +0 -226
- package/mnemo/code_read_tools.js +0 -375
- package/mnemo/context_preview_tools.js +0 -603
- package/mnemo/embeddings.js +0 -66
- package/mnemo/external_repo_ops.js +0 -575
- package/mnemo/facts/example-project-rules.json +0 -90
- package/mnemo/facts/example.json +0 -34
- package/mnemo/identity_schema.sql +0 -139
- package/mnemo/journal_schema.js +0 -561
- package/mnemo/loop_doctor_tools.js +0 -661
- package/mnemo/mail_secret_refs.js +0 -150
- package/mnemo/mcp.js +0 -9309
- package/mnemo/memory_consolidation.js +0 -1914
- package/mnemo/memory_health_tools.js +0 -165
- package/mnemo/package.json +0 -79
- package/mnemo/protected_scope_gate.js +0 -627
- package/mnemo/resource_access_control.js +0 -684
- package/mnemo/runtime_governance.js +0 -1256
- package/mnemo/runtime_turn_gate.js +0 -862
- package/mnemo/sandbox.js +0 -143
- package/mnemo/schema.sql +0 -389
- package/mnemo/shared_utils.js +0 -763
- package/mnemo/skills/agent-auto-resume/SKILL.md +0 -56
- package/mnemo/skills/agent_hand/SKILL.md +0 -43
- package/mnemo/skills/agent_hand/run.js +0 -63
- package/mnemo/skills/book_flight/SKILL.md +0 -34
- package/mnemo/skills/external_repo_review/SKILL.md +0 -43
- package/mnemo/skills/external_repo_review/run.js +0 -73
- package/mnemo/skills/pay_invoice/SKILL.md +0 -34
- package/mnemo/team_quality_ops.js +0 -944
- package/mnemo/timeline_report_tools.js +0 -810
- package/mnemo/write_gate_risk.js +0 -80
- package/mnemo/writer_health.js +0 -152
- package/skills/doku-ingestion/SKILL.md +0 -48
- package/skills/doku-ingestion/ingest_docs.py +0 -133
|
@@ -1,944 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
const TRAINING_STATUS_ACTIVE = "active";
|
|
4
|
-
const SITE_JSON_FIELDS = [
|
|
5
|
-
"target_urls",
|
|
6
|
-
"paths",
|
|
7
|
-
"forbidden_hosts",
|
|
8
|
-
"required_locales",
|
|
9
|
-
"header_rules",
|
|
10
|
-
"menu_rules",
|
|
11
|
-
"footer_rules",
|
|
12
|
-
"logo_rules",
|
|
13
|
-
"auth_rules",
|
|
14
|
-
"pricing_rules",
|
|
15
|
-
"checkout_rules",
|
|
16
|
-
"mobile_viewports",
|
|
17
|
-
"desktop_viewports",
|
|
18
|
-
"required_checks",
|
|
19
|
-
];
|
|
20
|
-
|
|
21
|
-
const TEAM_QUALITY_TOOL_DEFS = {
|
|
22
|
-
mem_agent_scorecard: {
|
|
23
|
-
description: "Score one agent or all agents from real Mnemo work signals: actions, briefs, guard failures, quality findings, autonomy tasks, and training rules.",
|
|
24
|
-
inputSchema: {
|
|
25
|
-
type: "object",
|
|
26
|
-
properties: {
|
|
27
|
-
agent_name: { type: "string" },
|
|
28
|
-
days: { type: "integer", default: 14 },
|
|
29
|
-
include_doc: { type: "boolean" },
|
|
30
|
-
},
|
|
31
|
-
},
|
|
32
|
-
},
|
|
33
|
-
mem_agent_scoreboard: {
|
|
34
|
-
description: "Render a compact team scoreboard with blockers, score, guard issues, open findings, pending briefs, and training notes.",
|
|
35
|
-
inputSchema: {
|
|
36
|
-
type: "object",
|
|
37
|
-
properties: {
|
|
38
|
-
days: { type: "integer", default: 14 },
|
|
39
|
-
limit: { type: "integer", default: 50 },
|
|
40
|
-
},
|
|
41
|
-
},
|
|
42
|
-
},
|
|
43
|
-
mem_agent_training_rule_upsert: {
|
|
44
|
-
description: "Store a durable training rule for one agent, one project, or the whole team. Use when owner feedback or a repeated mistake must never be forgotten.",
|
|
45
|
-
inputSchema: {
|
|
46
|
-
type: "object",
|
|
47
|
-
properties: {
|
|
48
|
-
id: { type: "integer" },
|
|
49
|
-
agent_name: { type: "string" },
|
|
50
|
-
scope: { type: "string" },
|
|
51
|
-
project: { type: "string" },
|
|
52
|
-
rule_kind: { type: "string" },
|
|
53
|
-
title: { type: "string" },
|
|
54
|
-
body: { type: "string" },
|
|
55
|
-
source_kind: { type: "string" },
|
|
56
|
-
source_id: { type: "string" },
|
|
57
|
-
severity: { type: "string" },
|
|
58
|
-
status: { type: "string" },
|
|
59
|
-
created_by: { type: "string" },
|
|
60
|
-
},
|
|
61
|
-
required: ["title", "body"],
|
|
62
|
-
},
|
|
63
|
-
},
|
|
64
|
-
mem_agent_training_rules: {
|
|
65
|
-
description: "List active training rules relevant to an agent, project, or scope.",
|
|
66
|
-
inputSchema: {
|
|
67
|
-
type: "object",
|
|
68
|
-
properties: {
|
|
69
|
-
agent_name: { type: "string" },
|
|
70
|
-
project: { type: "string" },
|
|
71
|
-
scope: { type: "string" },
|
|
72
|
-
status: { type: "string" },
|
|
73
|
-
limit: { type: "integer" },
|
|
74
|
-
},
|
|
75
|
-
},
|
|
76
|
-
},
|
|
77
|
-
mem_correction_capture: {
|
|
78
|
-
description: "Turn owner/reviewer feedback into a scar event plus a durable agent training rule so the team does not repeat the same mistake.",
|
|
79
|
-
inputSchema: {
|
|
80
|
-
type: "object",
|
|
81
|
-
properties: {
|
|
82
|
-
agent_name: { type: "string" },
|
|
83
|
-
project: { type: "string" },
|
|
84
|
-
text: { type: "string" },
|
|
85
|
-
title: { type: "string" },
|
|
86
|
-
category: { type: "string" },
|
|
87
|
-
severity: { type: "string" },
|
|
88
|
-
source_kind: { type: "string" },
|
|
89
|
-
source_id: { type: "string" },
|
|
90
|
-
created_by: { type: "string" },
|
|
91
|
-
make_rule: { type: "boolean" },
|
|
92
|
-
create_finding: { type: "boolean" },
|
|
93
|
-
finding_url: { type: "string" },
|
|
94
|
-
},
|
|
95
|
-
required: ["text"],
|
|
96
|
-
},
|
|
97
|
-
},
|
|
98
|
-
mem_site_contract_set: {
|
|
99
|
-
description: "Store the canonical website contract for a project: canonical source, targets, paths, forbidden hosts, locales, header/menu/footer/logo/auth/pricing/checkout/mobile rules, and required checks.",
|
|
100
|
-
inputSchema: {
|
|
101
|
-
type: "object",
|
|
102
|
-
properties: {
|
|
103
|
-
project: { type: "string" },
|
|
104
|
-
canonical_url: { type: "string" },
|
|
105
|
-
target_urls: { type: "array", items: { type: "string" } },
|
|
106
|
-
paths: { type: "array", items: { type: "string" } },
|
|
107
|
-
forbidden_hosts: { type: "array", items: { type: "string" } },
|
|
108
|
-
required_locales: { type: "array", items: { type: "string" } },
|
|
109
|
-
header_rules: { type: "object" },
|
|
110
|
-
menu_rules: { type: "object" },
|
|
111
|
-
footer_rules: { type: "object" },
|
|
112
|
-
logo_rules: { type: "object" },
|
|
113
|
-
auth_rules: { type: "object" },
|
|
114
|
-
pricing_rules: { type: "object" },
|
|
115
|
-
checkout_rules: { type: "object" },
|
|
116
|
-
mobile_viewports: { type: "array" },
|
|
117
|
-
desktop_viewports: { type: "array" },
|
|
118
|
-
required_checks: { type: "array", items: { type: "string" } },
|
|
119
|
-
notes: { type: "string" },
|
|
120
|
-
updated_by: { type: "string" },
|
|
121
|
-
},
|
|
122
|
-
required: ["project"],
|
|
123
|
-
},
|
|
124
|
-
},
|
|
125
|
-
mem_site_contract_get: {
|
|
126
|
-
description: "Read the website contract for one project, merged with registry and project-rule hints when available.",
|
|
127
|
-
inputSchema: {
|
|
128
|
-
type: "object",
|
|
129
|
-
properties: {
|
|
130
|
-
project: { type: "string" },
|
|
131
|
-
},
|
|
132
|
-
required: ["project"],
|
|
133
|
-
},
|
|
134
|
-
},
|
|
135
|
-
mem_site_contract_list: {
|
|
136
|
-
description: "List projects with stored site contracts.",
|
|
137
|
-
inputSchema: {
|
|
138
|
-
type: "object",
|
|
139
|
-
properties: {
|
|
140
|
-
limit: { type: "integer" },
|
|
141
|
-
},
|
|
142
|
-
},
|
|
143
|
-
},
|
|
144
|
-
mem_site_golden_check_plan: {
|
|
145
|
-
description: "Build the mandatory golden-check plan for a website change, including audit command, browser evidence, mobile/desktop, locale, logo, menu, footer, and forbidden-domain checks.",
|
|
146
|
-
inputSchema: {
|
|
147
|
-
type: "object",
|
|
148
|
-
properties: {
|
|
149
|
-
project: { type: "string" },
|
|
150
|
-
agent_name: { type: "string" },
|
|
151
|
-
change_summary: { type: "string" },
|
|
152
|
-
include_browser_steps: { type: "boolean" },
|
|
153
|
-
},
|
|
154
|
-
required: ["project"],
|
|
155
|
-
},
|
|
156
|
-
},
|
|
157
|
-
mem_site_golden_check_report: {
|
|
158
|
-
description: "Persist the actual golden-check result and optionally create quality findings for failed checks.",
|
|
159
|
-
inputSchema: {
|
|
160
|
-
type: "object",
|
|
161
|
-
properties: {
|
|
162
|
-
project: { type: "string" },
|
|
163
|
-
agent_name: { type: "string" },
|
|
164
|
-
status: { type: "string" },
|
|
165
|
-
command: { type: "string" },
|
|
166
|
-
summary: { type: "string" },
|
|
167
|
-
evidence: { type: "object" },
|
|
168
|
-
findings: { type: "array" },
|
|
169
|
-
create_findings: { type: "boolean" },
|
|
170
|
-
},
|
|
171
|
-
required: ["project", "status"],
|
|
172
|
-
},
|
|
173
|
-
},
|
|
174
|
-
mem_site_golden_check_history: {
|
|
175
|
-
description: "List recent golden-check runs for one project or all projects.",
|
|
176
|
-
inputSchema: {
|
|
177
|
-
type: "object",
|
|
178
|
-
properties: {
|
|
179
|
-
project: { type: "string" },
|
|
180
|
-
limit: { type: "integer" },
|
|
181
|
-
},
|
|
182
|
-
},
|
|
183
|
-
},
|
|
184
|
-
};
|
|
185
|
-
|
|
186
|
-
function ensureTeamQualityTables(db) {
|
|
187
|
-
db.exec(`
|
|
188
|
-
CREATE TABLE IF NOT EXISTS correction_pattern (
|
|
189
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
190
|
-
pattern TEXT NOT NULL UNIQUE,
|
|
191
|
-
classifier TEXT NOT NULL,
|
|
192
|
-
actor_scope TEXT,
|
|
193
|
-
trait_to_adjust TEXT,
|
|
194
|
-
delta REAL,
|
|
195
|
-
hit_count INTEGER NOT NULL DEFAULT 0,
|
|
196
|
-
last_hit_at TEXT
|
|
197
|
-
);
|
|
198
|
-
CREATE TABLE IF NOT EXISTS scar_event (
|
|
199
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
200
|
-
scar_slug TEXT NOT NULL,
|
|
201
|
-
triggering_memory_id INTEGER REFERENCES memory(id) ON DELETE SET NULL,
|
|
202
|
-
pattern_id INTEGER REFERENCES correction_pattern(id) ON DELETE SET NULL,
|
|
203
|
-
trait_delta_applied REAL NOT NULL DEFAULT 0,
|
|
204
|
-
notes TEXT,
|
|
205
|
-
occurred_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ','now'))
|
|
206
|
-
);
|
|
207
|
-
CREATE INDEX IF NOT EXISTS idx_scar_event_pattern ON scar_event(pattern_id);
|
|
208
|
-
CREATE INDEX IF NOT EXISTS idx_scar_event_occurred ON scar_event(occurred_at);
|
|
209
|
-
CREATE TABLE IF NOT EXISTS scar_high_water (
|
|
210
|
-
k TEXT PRIMARY KEY,
|
|
211
|
-
v INTEGER NOT NULL DEFAULT 0
|
|
212
|
-
);
|
|
213
|
-
CREATE TABLE IF NOT EXISTS agent_training_rule (
|
|
214
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
215
|
-
agent_name TEXT,
|
|
216
|
-
scope TEXT NOT NULL DEFAULT 'global',
|
|
217
|
-
project TEXT,
|
|
218
|
-
rule_kind TEXT NOT NULL,
|
|
219
|
-
title TEXT NOT NULL,
|
|
220
|
-
body TEXT NOT NULL,
|
|
221
|
-
source_kind TEXT,
|
|
222
|
-
source_id TEXT,
|
|
223
|
-
severity TEXT NOT NULL DEFAULT 'M',
|
|
224
|
-
status TEXT NOT NULL DEFAULT 'active',
|
|
225
|
-
created_by TEXT,
|
|
226
|
-
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ','now')),
|
|
227
|
-
updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ','now'))
|
|
228
|
-
);
|
|
229
|
-
CREATE INDEX IF NOT EXISTS idx_training_agent_status ON agent_training_rule(agent_name, status, updated_at DESC);
|
|
230
|
-
CREATE INDEX IF NOT EXISTS idx_training_scope_status ON agent_training_rule(scope, status, updated_at DESC);
|
|
231
|
-
CREATE INDEX IF NOT EXISTS idx_training_project_status ON agent_training_rule(project, status, updated_at DESC);
|
|
232
|
-
CREATE TABLE IF NOT EXISTS site_contract (
|
|
233
|
-
project TEXT PRIMARY KEY,
|
|
234
|
-
canonical_url TEXT,
|
|
235
|
-
target_urls TEXT,
|
|
236
|
-
paths TEXT,
|
|
237
|
-
forbidden_hosts TEXT,
|
|
238
|
-
required_locales TEXT,
|
|
239
|
-
header_rules TEXT,
|
|
240
|
-
menu_rules TEXT,
|
|
241
|
-
footer_rules TEXT,
|
|
242
|
-
logo_rules TEXT,
|
|
243
|
-
auth_rules TEXT,
|
|
244
|
-
pricing_rules TEXT,
|
|
245
|
-
checkout_rules TEXT,
|
|
246
|
-
mobile_viewports TEXT,
|
|
247
|
-
desktop_viewports TEXT,
|
|
248
|
-
required_checks TEXT,
|
|
249
|
-
notes TEXT,
|
|
250
|
-
updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ','now')),
|
|
251
|
-
updated_by TEXT
|
|
252
|
-
);
|
|
253
|
-
CREATE TABLE IF NOT EXISTS golden_check_run (
|
|
254
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
255
|
-
project TEXT NOT NULL,
|
|
256
|
-
agent_name TEXT,
|
|
257
|
-
status TEXT NOT NULL,
|
|
258
|
-
command TEXT,
|
|
259
|
-
summary TEXT,
|
|
260
|
-
evidence_json TEXT,
|
|
261
|
-
findings_json TEXT,
|
|
262
|
-
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ','now'))
|
|
263
|
-
);
|
|
264
|
-
CREATE INDEX IF NOT EXISTS idx_golden_project_time ON golden_check_run(project, created_at DESC);
|
|
265
|
-
CREATE INDEX IF NOT EXISTS idx_golden_status ON golden_check_run(status, created_at DESC);
|
|
266
|
-
`);
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
function handleTeamQualityTool(db, name, args) {
|
|
270
|
-
if (!TEAM_QUALITY_TOOL_DEFS[name]) return { handled: false };
|
|
271
|
-
const a = args || {};
|
|
272
|
-
ensureTeamQualityTables(db);
|
|
273
|
-
switch (name) {
|
|
274
|
-
case "mem_agent_scorecard":
|
|
275
|
-
return { handled: true, result: agentScorecard(db, a) };
|
|
276
|
-
case "mem_agent_scoreboard":
|
|
277
|
-
return { handled: true, result: agentScoreboard(db, a) };
|
|
278
|
-
case "mem_agent_training_rule_upsert":
|
|
279
|
-
return { handled: true, result: trainingRuleUpsert(db, a) };
|
|
280
|
-
case "mem_agent_training_rules":
|
|
281
|
-
return { handled: true, result: trainingRules(db, a) };
|
|
282
|
-
case "mem_correction_capture":
|
|
283
|
-
return { handled: true, result: correctionCapture(db, a) };
|
|
284
|
-
case "mem_site_contract_set":
|
|
285
|
-
return { handled: true, result: siteContractSet(db, a) };
|
|
286
|
-
case "mem_site_contract_get":
|
|
287
|
-
return { handled: true, result: siteContractGet(db, a) };
|
|
288
|
-
case "mem_site_contract_list":
|
|
289
|
-
return { handled: true, result: siteContractList(db, a) };
|
|
290
|
-
case "mem_site_golden_check_plan":
|
|
291
|
-
return { handled: true, result: siteGoldenCheckPlan(db, a) };
|
|
292
|
-
case "mem_site_golden_check_report":
|
|
293
|
-
return { handled: true, result: siteGoldenCheckReport(db, a) };
|
|
294
|
-
case "mem_site_golden_check_history":
|
|
295
|
-
return { handled: true, result: siteGoldenCheckHistory(db, a) };
|
|
296
|
-
default:
|
|
297
|
-
return { handled: false };
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
function agentScorecard(db, a) {
|
|
302
|
-
const days = clampInt(a.days, 14, 1, 365);
|
|
303
|
-
const since = new Date(Date.now() - days * 86400000).toISOString();
|
|
304
|
-
const agents = collectAgents(db, a.agent_name, since);
|
|
305
|
-
const cards = agents.map((agent) => buildAgentCard(db, agent, since, days));
|
|
306
|
-
cards.sort((x, y) => x.score - y.score || y.findings.open_high - x.findings.open_high || x.agent_name.localeCompare(y.agent_name));
|
|
307
|
-
const result = {
|
|
308
|
-
since,
|
|
309
|
-
days,
|
|
310
|
-
count: cards.length,
|
|
311
|
-
agents: cards,
|
|
312
|
-
summary: {
|
|
313
|
-
average_score: cards.length ? Math.round(cards.reduce((n, c) => n + c.score, 0) / cards.length) : null,
|
|
314
|
-
needs_training: cards.filter((c) => c.needs_training).length,
|
|
315
|
-
blocked: cards.filter((c) => c.status === "block").length,
|
|
316
|
-
attention: cards.filter((c) => c.status === "attention").length,
|
|
317
|
-
},
|
|
318
|
-
};
|
|
319
|
-
if (a.agent_name) result.card = cards[0] || null;
|
|
320
|
-
if (a.include_doc) result.doc = formatScoreboard(result, clampInt(a.limit, 50, 1, 200));
|
|
321
|
-
return result;
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
function agentScoreboard(db, a) {
|
|
325
|
-
const board = agentScorecard(db, { days: a.days, include_doc: true, limit: a.limit });
|
|
326
|
-
return board;
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
function buildAgentCard(db, agent, since, days) {
|
|
330
|
-
const actions = safeGet(db, `
|
|
331
|
-
SELECT
|
|
332
|
-
COUNT(*) AS total,
|
|
333
|
-
SUM(CASE WHEN lower(status) IN ('done','ok','pass','ready','approved','reviewed','complete','completed','resolved') THEN 1 ELSE 0 END) AS done,
|
|
334
|
-
SUM(CASE WHEN lower(status) IN ('error','failed','fail') THEN 1 ELSE 0 END) AS failed,
|
|
335
|
-
SUM(CASE WHEN lower(status) IN ('block','blocked') THEN 1 ELSE 0 END) AS blocked,
|
|
336
|
-
SUM(CASE WHEN (lower(action_kind) LIKE '%guard%' OR lower(topic) LIKE '%guard%' OR lower(action_kind) IN ('pre_action_check','agent_preflight')) THEN 1 ELSE 0 END) AS guard_total,
|
|
337
|
-
SUM(CASE WHEN (lower(action_kind) LIKE '%guard%' OR lower(topic) LIKE '%guard%' OR lower(action_kind) IN ('pre_action_check','agent_preflight')) AND lower(status) IN ('block','blocked','missing','error','failed','fail') THEN 1 ELSE 0 END) AS guard_blocked,
|
|
338
|
-
SUM(CASE WHEN lower(status) IN ('pending','started','open') AND julianday(started_at) < julianday('now','-6 hours') THEN 1 ELSE 0 END) AS stale
|
|
339
|
-
FROM agent_action
|
|
340
|
-
WHERE agent_name=? AND started_at>=?
|
|
341
|
-
`, [agent, since], zeroActionStats());
|
|
342
|
-
|
|
343
|
-
const briefs = safeGet(db, `
|
|
344
|
-
SELECT
|
|
345
|
-
COUNT(*) AS total,
|
|
346
|
-
SUM(CASE WHEN lower(status)='pending' THEN 1 ELSE 0 END) AS pending,
|
|
347
|
-
SUM(CASE WHEN lower(status)='dispatched' THEN 1 ELSE 0 END) AS dispatched,
|
|
348
|
-
SUM(CASE WHEN lower(status) IN ('done','complete','completed','resolved') THEN 1 ELSE 0 END) AS done,
|
|
349
|
-
SUM(CASE WHEN lower(status) IN ('failed','error','blocked') THEN 1 ELSE 0 END) AS failed
|
|
350
|
-
FROM agent_brief
|
|
351
|
-
WHERE agent_name=? AND created_at>=?
|
|
352
|
-
`, [agent, since], zeroBriefStats());
|
|
353
|
-
|
|
354
|
-
const findings = safeGet(db, `
|
|
355
|
-
SELECT
|
|
356
|
-
COUNT(*) AS total,
|
|
357
|
-
SUM(CASE WHEN status='open' THEN 1 ELSE 0 END) AS open,
|
|
358
|
-
SUM(CASE WHEN status='open' AND severity IN ('H','critical') THEN 1 ELSE 0 END) AS open_high,
|
|
359
|
-
SUM(CASE WHEN status<>'open' THEN 1 ELSE 0 END) AS closed
|
|
360
|
-
FROM quality_finding
|
|
361
|
-
WHERE source_agent=? AND created_at>=?
|
|
362
|
-
`, [agent, since], zeroFindingStats());
|
|
363
|
-
|
|
364
|
-
const tasks = safeGet(db, `
|
|
365
|
-
SELECT
|
|
366
|
-
COUNT(*) AS total,
|
|
367
|
-
SUM(CASE WHEN lower(status) IN ('done','reviewed','approved','resolved') THEN 1 ELSE 0 END) AS done,
|
|
368
|
-
SUM(CASE WHEN lower(status) IN ('open','claimed','review') THEN 1 ELSE 0 END) AS open,
|
|
369
|
-
SUM(CASE WHEN lower(status) IN ('block','blocked') THEN 1 ELSE 0 END) AS blocked
|
|
370
|
-
FROM autonomy_task
|
|
371
|
-
WHERE (assigned_agent=? OR reviewer_agent=?) AND created_at>=?
|
|
372
|
-
`, [agent, agent, since], zeroTaskStats());
|
|
373
|
-
|
|
374
|
-
const training = safeAll(db, `
|
|
375
|
-
SELECT id, agent_name, scope, project, rule_kind, title, severity, updated_at
|
|
376
|
-
FROM agent_training_rule
|
|
377
|
-
WHERE status='active'
|
|
378
|
-
AND (agent_name IS NULL OR agent_name=? OR scope='global')
|
|
379
|
-
ORDER BY CASE severity WHEN 'critical' THEN 0 WHEN 'H' THEN 1 WHEN 'M' THEN 2 ELSE 3 END, updated_at DESC
|
|
380
|
-
LIMIT 8
|
|
381
|
-
`, [agent]);
|
|
382
|
-
|
|
383
|
-
let score = 100;
|
|
384
|
-
score -= num(actions.failed) * 4;
|
|
385
|
-
score -= num(actions.blocked) * 5;
|
|
386
|
-
score -= num(actions.guard_blocked) * 8;
|
|
387
|
-
score -= num(actions.stale) * 3;
|
|
388
|
-
score -= num(briefs.failed) * 4;
|
|
389
|
-
score -= Math.max(0, num(briefs.pending) + num(briefs.dispatched) - 5) * 2;
|
|
390
|
-
score -= num(findings.open_high) * 12;
|
|
391
|
-
score -= Math.max(0, num(findings.open) - num(findings.open_high)) * 4;
|
|
392
|
-
score -= num(tasks.blocked) * 5;
|
|
393
|
-
score += Math.min(num(actions.done), 20) * 0.4;
|
|
394
|
-
score += Math.min(num(tasks.done), 10) * 0.5;
|
|
395
|
-
score = Math.max(0, Math.min(100, Math.round(score)));
|
|
396
|
-
|
|
397
|
-
const trainingBrief = [];
|
|
398
|
-
if (!num(actions.total)) trainingBrief.push("No recent action trail: verify the loop is online and logging work.");
|
|
399
|
-
if (num(actions.guard_blocked)) trainingBrief.push("Guard misses detected: reload session/project context and pass pre-work before execution.");
|
|
400
|
-
if (num(findings.open_high)) trainingBrief.push("High open findings from this agent: fix or hand off before new live work.");
|
|
401
|
-
if (num(briefs.pending) + num(briefs.dispatched) > 5) trainingBrief.push("Brief backlog is growing: pull, process, or explicitly reassign.");
|
|
402
|
-
if (num(actions.failed) || num(briefs.failed) || num(tasks.blocked)) trainingBrief.push("Failures/blockers must become durable rules or findings, not chat-only notes.");
|
|
403
|
-
if (!trainingBrief.length && training.length) trainingBrief.push("Active training rules exist: review them at session start.");
|
|
404
|
-
|
|
405
|
-
const status = score < 70 || num(findings.open_high) || num(actions.guard_blocked) ? "block" : (score < 85 || trainingBrief.length ? "attention" : "ok");
|
|
406
|
-
return {
|
|
407
|
-
agent_name: agent,
|
|
408
|
-
status,
|
|
409
|
-
score,
|
|
410
|
-
window_days: days,
|
|
411
|
-
actions: normalizeStats(actions),
|
|
412
|
-
briefs: normalizeStats(briefs),
|
|
413
|
-
findings: normalizeStats(findings),
|
|
414
|
-
autonomy_tasks: normalizeStats(tasks),
|
|
415
|
-
training_rules: training,
|
|
416
|
-
needs_training: status !== "ok",
|
|
417
|
-
training_brief: trainingBrief,
|
|
418
|
-
};
|
|
419
|
-
}
|
|
420
|
-
|
|
421
|
-
function trainingRuleUpsert(db, a) {
|
|
422
|
-
const title = cleanText(a.title, 160);
|
|
423
|
-
const body = String(a.body || "").trim();
|
|
424
|
-
if (!title || !body) return { error: "title and body required" };
|
|
425
|
-
const ruleKind = cleanText(a.rule_kind || "training", 60) || "training";
|
|
426
|
-
const scope = cleanText(a.scope || "global", 80) || "global";
|
|
427
|
-
const severity = validSeverity(a.severity || "M");
|
|
428
|
-
const status = cleanText(a.status || TRAINING_STATUS_ACTIVE, 30) || TRAINING_STATUS_ACTIVE;
|
|
429
|
-
if (a.id) {
|
|
430
|
-
const info = db.prepare(`
|
|
431
|
-
UPDATE agent_training_rule
|
|
432
|
-
SET agent_name=?, scope=?, project=?, rule_kind=?, title=?, body=?, source_kind=?, source_id=?, severity=?, status=?, created_by=COALESCE(created_by, ?), updated_at=strftime('%Y-%m-%dT%H:%M:%fZ','now')
|
|
433
|
-
WHERE id=?
|
|
434
|
-
`).run(a.agent_name || null, scope, a.project || null, ruleKind, title, body, a.source_kind || null, a.source_id || null, severity, status, a.created_by || null, a.id);
|
|
435
|
-
return { ok: info.changes > 0, id: a.id, action: "updated", status };
|
|
436
|
-
}
|
|
437
|
-
const existing = db.prepare(`
|
|
438
|
-
SELECT id FROM agent_training_rule
|
|
439
|
-
WHERE title=? AND scope=? AND COALESCE(agent_name,'')=COALESCE(?, '') AND COALESCE(project,'')=COALESCE(?, '') AND status='active'
|
|
440
|
-
ORDER BY id DESC LIMIT 1
|
|
441
|
-
`).get(title, scope, a.agent_name || null, a.project || null);
|
|
442
|
-
if (existing) {
|
|
443
|
-
db.prepare(`
|
|
444
|
-
UPDATE agent_training_rule
|
|
445
|
-
SET body=?, rule_kind=?, source_kind=?, source_id=?, severity=?, created_by=COALESCE(created_by, ?), updated_at=strftime('%Y-%m-%dT%H:%M:%fZ','now')
|
|
446
|
-
WHERE id=?
|
|
447
|
-
`).run(body, ruleKind, a.source_kind || null, a.source_id || null, severity, a.created_by || null, existing.id);
|
|
448
|
-
return { ok: true, id: existing.id, action: "refreshed", status: "active" };
|
|
449
|
-
}
|
|
450
|
-
const info = db.prepare(`
|
|
451
|
-
INSERT INTO agent_training_rule (agent_name, scope, project, rule_kind, title, body, source_kind, source_id, severity, status, created_by)
|
|
452
|
-
VALUES (?,?,?,?,?,?,?,?,?,?,?)
|
|
453
|
-
`).run(a.agent_name || null, scope, a.project || null, ruleKind, title, body, a.source_kind || null, a.source_id || null, severity, status, a.created_by || null);
|
|
454
|
-
logAction(db, a.created_by || a.agent_name || "training-rule", "agent_training_rule_upsert", a.agent_name || scope, "done", { id: info.lastInsertRowid, title, severity }, "training");
|
|
455
|
-
return { ok: true, id: info.lastInsertRowid, action: "created", status };
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
function trainingRules(db, a) {
|
|
459
|
-
const where = ["status=?"];
|
|
460
|
-
const params = [a.status || TRAINING_STATUS_ACTIVE];
|
|
461
|
-
if (a.agent_name) {
|
|
462
|
-
where.push("(agent_name IS NULL OR agent_name=? OR scope='global')");
|
|
463
|
-
params.push(a.agent_name);
|
|
464
|
-
}
|
|
465
|
-
if (a.project) {
|
|
466
|
-
where.push("(project IS NULL OR project=?)");
|
|
467
|
-
params.push(a.project);
|
|
468
|
-
}
|
|
469
|
-
if (a.scope) {
|
|
470
|
-
where.push("(scope=? OR scope='global')");
|
|
471
|
-
params.push(a.scope);
|
|
472
|
-
}
|
|
473
|
-
params.push(clampInt(a.limit, 100, 1, 500));
|
|
474
|
-
const rows = db.prepare(`
|
|
475
|
-
SELECT * FROM agent_training_rule
|
|
476
|
-
WHERE ${where.join(" AND ")}
|
|
477
|
-
ORDER BY CASE severity WHEN 'critical' THEN 0 WHEN 'H' THEN 1 WHEN 'M' THEN 2 ELSE 3 END, updated_at DESC
|
|
478
|
-
LIMIT ?
|
|
479
|
-
`).all(...params);
|
|
480
|
-
return { count: rows.length, rules: rows };
|
|
481
|
-
}
|
|
482
|
-
|
|
483
|
-
function correctionCapture(db, a) {
|
|
484
|
-
const text = String(a.text || "").trim();
|
|
485
|
-
if (!text) return { error: "text required" };
|
|
486
|
-
const agentName = a.agent_name || null;
|
|
487
|
-
const project = a.project || null;
|
|
488
|
-
const title = cleanText(a.title || firstSentence(text), 160) || "Correction captured";
|
|
489
|
-
const severity = validSeverity(a.severity || "H");
|
|
490
|
-
const category = cleanText(a.category || "correction", 80) || "correction";
|
|
491
|
-
const slug = slugify([project, agentName, title].filter(Boolean).join("-")) || "correction";
|
|
492
|
-
const created = { scar_event_id: null, training_rule: null, quality_finding: null };
|
|
493
|
-
|
|
494
|
-
const scarInfo = db.prepare("INSERT INTO scar_event (scar_slug, trait_delta_applied, notes) VALUES (?,?,?)")
|
|
495
|
-
.run(slug, 0, text);
|
|
496
|
-
created.scar_event_id = scarInfo.lastInsertRowid;
|
|
497
|
-
|
|
498
|
-
if (a.make_rule !== false) {
|
|
499
|
-
created.training_rule = trainingRuleUpsert(db, {
|
|
500
|
-
agent_name: agentName,
|
|
501
|
-
project,
|
|
502
|
-
scope: project ? "project" : "global",
|
|
503
|
-
rule_kind: "correction",
|
|
504
|
-
title,
|
|
505
|
-
body: text,
|
|
506
|
-
source_kind: a.source_kind || "correction_capture",
|
|
507
|
-
source_id: a.source_id || String(scarInfo.lastInsertRowid),
|
|
508
|
-
severity,
|
|
509
|
-
created_by: a.created_by || "owner-feedback",
|
|
510
|
-
});
|
|
511
|
-
}
|
|
512
|
-
|
|
513
|
-
if (a.create_finding && project) {
|
|
514
|
-
const info = db.prepare("INSERT INTO quality_finding (project, category, severity, title, url, expected, actual, source_agent, evidence_json) VALUES (?,?,?,?,?,?,?,?,?)")
|
|
515
|
-
.run(project, category, severity, title, a.finding_url || null, "The team must not repeat this correction.", text, agentName || a.created_by || null, JSON.stringify({ source: "mem_correction_capture", scar_event_id: scarInfo.lastInsertRowid }));
|
|
516
|
-
created.quality_finding = { id: info.lastInsertRowid, project, status: "open", severity };
|
|
517
|
-
}
|
|
518
|
-
|
|
519
|
-
logAction(db, a.created_by || "owner-feedback", "correction_capture", project || agentName || "global", "done", { title, agent_name: agentName, project, scar_event_id: scarInfo.lastInsertRowid }, "training");
|
|
520
|
-
return {
|
|
521
|
-
ok: true,
|
|
522
|
-
title,
|
|
523
|
-
severity,
|
|
524
|
-
agent_name: agentName,
|
|
525
|
-
project,
|
|
526
|
-
...created,
|
|
527
|
-
};
|
|
528
|
-
}
|
|
529
|
-
|
|
530
|
-
function siteContractSet(db, a) {
|
|
531
|
-
const project = cleanText(a.project, 160);
|
|
532
|
-
if (!project) return { error: "project required" };
|
|
533
|
-
const fields = ["project"];
|
|
534
|
-
const placeholders = ["?"];
|
|
535
|
-
const values = [project];
|
|
536
|
-
const updates = [];
|
|
537
|
-
for (const key of ["canonical_url", "notes", "updated_by"]) {
|
|
538
|
-
if (a[key] !== undefined) {
|
|
539
|
-
fields.push(key);
|
|
540
|
-
placeholders.push("?");
|
|
541
|
-
values.push(a[key] || null);
|
|
542
|
-
updates.push(key + "=excluded." + key);
|
|
543
|
-
}
|
|
544
|
-
}
|
|
545
|
-
for (const key of SITE_JSON_FIELDS) {
|
|
546
|
-
if (a[key] !== undefined) {
|
|
547
|
-
fields.push(key);
|
|
548
|
-
placeholders.push("?");
|
|
549
|
-
values.push(JSON.stringify(a[key]));
|
|
550
|
-
updates.push(key + "=excluded." + key);
|
|
551
|
-
}
|
|
552
|
-
}
|
|
553
|
-
if (!updates.length) updates.push("updated_at=strftime('%Y-%m-%dT%H:%M:%fZ','now')");
|
|
554
|
-
else updates.push("updated_at=strftime('%Y-%m-%dT%H:%M:%fZ','now')");
|
|
555
|
-
const sql = `INSERT INTO site_contract (${fields.join(",")}) VALUES (${placeholders.join(",")}) ON CONFLICT(project) DO UPDATE SET ${updates.join(", ")}`;
|
|
556
|
-
db.prepare(sql).run(...values);
|
|
557
|
-
logAction(db, a.updated_by || "site-contract", "site_contract_set", project, "done", { keys: Object.keys(a).filter((k) => k !== "project") }, "site_contract");
|
|
558
|
-
return { ok: true, project };
|
|
559
|
-
}
|
|
560
|
-
|
|
561
|
-
function siteContractGet(db, a) {
|
|
562
|
-
const project = cleanText(a.project, 160);
|
|
563
|
-
if (!project) return { error: "project required" };
|
|
564
|
-
const row = safeGet(db, "SELECT * FROM site_contract WHERE project=?", [project], null);
|
|
565
|
-
const registry = safeGet(db, "SELECT name, domain, live_url, staging_url, health_checklist FROM project_registry WHERE name=?", [project], null);
|
|
566
|
-
const rules = safeGet(db, "SELECT canonical_nav, allowed_domains, language_matrix, design_rules, pricing_rules, checkout_rules FROM project_rules WHERE project=?", [project], null);
|
|
567
|
-
const contract = row ? parseSiteContract(row) : defaultContract(project, registry, rules);
|
|
568
|
-
if (rules) {
|
|
569
|
-
contract.project_rules_hint = {
|
|
570
|
-
canonical_nav: json(rules.canonical_nav, null),
|
|
571
|
-
allowed_domains: json(rules.allowed_domains, null),
|
|
572
|
-
language_matrix: json(rules.language_matrix, null),
|
|
573
|
-
design_rules: json(rules.design_rules, null),
|
|
574
|
-
pricing_rules: json(rules.pricing_rules, null),
|
|
575
|
-
checkout_rules: json(rules.checkout_rules, null),
|
|
576
|
-
};
|
|
577
|
-
}
|
|
578
|
-
if (registry) contract.registry_hint = registry;
|
|
579
|
-
contract.audit_command = buildAuditCommand(contract);
|
|
580
|
-
contract.required_evidence = requiredSiteEvidence(contract);
|
|
581
|
-
return contract;
|
|
582
|
-
}
|
|
583
|
-
|
|
584
|
-
function siteContractList(db, a) {
|
|
585
|
-
const rows = db.prepare("SELECT project, canonical_url, updated_at, updated_by, notes FROM site_contract ORDER BY updated_at DESC LIMIT ?")
|
|
586
|
-
.all(clampInt(a.limit, 100, 1, 500));
|
|
587
|
-
return { count: rows.length, contracts: rows };
|
|
588
|
-
}
|
|
589
|
-
|
|
590
|
-
function siteGoldenCheckPlan(db, a) {
|
|
591
|
-
const contract = siteContractGet(db, { project: a.project });
|
|
592
|
-
if (contract.error) return contract;
|
|
593
|
-
const checks = requiredSiteEvidence(contract);
|
|
594
|
-
const command = buildAuditCommand(contract);
|
|
595
|
-
const browserSteps = a.include_browser_steps === false ? [] : [
|
|
596
|
-
"Open canonical source and each target URL at desktop viewport.",
|
|
597
|
-
"Open canonical source and each target URL at mobile viewport.",
|
|
598
|
-
"Compare header style, menu order, href targets, footer/legal links, and logo assets.",
|
|
599
|
-
"Confirm same-label menu items point to the target domain and expected target path, not the copied canonical domain.",
|
|
600
|
-
"Check each required locale route keeps the same language and does not fall back to the wrong locale.",
|
|
601
|
-
"Check legal footer links preserve the active locale on localized pages.",
|
|
602
|
-
"Check light and dark theme logos when the product supports themes.",
|
|
603
|
-
"Confirm mobile viewport meta exists and mobile header/menu is usable.",
|
|
604
|
-
"Record screenshots or explicit browser observations before reporting pass.",
|
|
605
|
-
];
|
|
606
|
-
const plan = {
|
|
607
|
-
project: contract.project,
|
|
608
|
-
change_summary: a.change_summary || null,
|
|
609
|
-
status: command ? "ready" : "blocked",
|
|
610
|
-
blockers: command ? [] : ["site contract needs canonical_url and at least one target_url"],
|
|
611
|
-
contract,
|
|
612
|
-
command,
|
|
613
|
-
checks,
|
|
614
|
-
browser_steps: browserSteps,
|
|
615
|
-
pass_condition: "No high findings, no forbidden domain leaks, required locales checked, mobile+desktop checked, relevant logos/themes checked, and findings reported with mem_site_golden_check_report.",
|
|
616
|
-
};
|
|
617
|
-
logAction(db, a.agent_name || "site-golden-plan", "site_golden_check_plan", contract.project, plan.status, { command, checks }, "site_contract");
|
|
618
|
-
return plan;
|
|
619
|
-
}
|
|
620
|
-
|
|
621
|
-
function siteGoldenCheckReport(db, a) {
|
|
622
|
-
const status = cleanText(a.status, 40) || "unknown";
|
|
623
|
-
const project = cleanText(a.project, 160);
|
|
624
|
-
if (!project) return { error: "project required" };
|
|
625
|
-
const findings = Array.isArray(a.findings) ? a.findings : [];
|
|
626
|
-
const info = db.prepare("INSERT INTO golden_check_run (project, agent_name, status, command, summary, evidence_json, findings_json) VALUES (?,?,?,?,?,?,?)")
|
|
627
|
-
.run(project, a.agent_name || null, status, a.command || null, a.summary || null, a.evidence ? JSON.stringify(a.evidence) : null, JSON.stringify(findings));
|
|
628
|
-
const created = [];
|
|
629
|
-
if (a.create_findings !== false) {
|
|
630
|
-
for (const group of groupGoldenFindings(findings, status)) {
|
|
631
|
-
const existing = db.prepare("SELECT id FROM quality_finding WHERE project=? AND title=? AND status='open' ORDER BY id DESC LIMIT 1")
|
|
632
|
-
.get(project, group.title);
|
|
633
|
-
const evidence = { source: "golden_check_run", run_id: info.lastInsertRowid, count: group.count, examples: group.examples, urls: group.urls };
|
|
634
|
-
const actual = `${group.count} ${group.type} finding(s). Examples: ${group.examples.map((x) => x.summary).join(" | ")}`;
|
|
635
|
-
if (existing) {
|
|
636
|
-
db.prepare("UPDATE quality_finding SET severity=?, url=?, expected=?, actual=?, source_agent=?, evidence_json=?, updated_at=strftime('%Y-%m-%dT%H:%M:%fZ','now') WHERE id=?")
|
|
637
|
-
.run(group.severity, group.urls[0] || null, group.expected, actual, a.agent_name || null, JSON.stringify(evidence), existing.id);
|
|
638
|
-
created.push({ id: existing.id, title: group.title, severity: group.severity, action: "updated", count: group.count });
|
|
639
|
-
} else {
|
|
640
|
-
const q = db.prepare("INSERT INTO quality_finding (project, category, severity, title, url, expected, actual, source_agent, evidence_json) VALUES (?,?,?,?,?,?,?,?,?)")
|
|
641
|
-
.run(project, group.category, group.severity, group.title, group.urls[0] || null, group.expected, actual, a.agent_name || null, JSON.stringify(evidence));
|
|
642
|
-
created.push({ id: q.lastInsertRowid, title: group.title, severity: group.severity, action: "created", count: group.count });
|
|
643
|
-
}
|
|
644
|
-
}
|
|
645
|
-
}
|
|
646
|
-
logAction(db, a.agent_name || "golden-check", "site_golden_check_report", project, status, { run_id: info.lastInsertRowid, findings: findings.length, created_findings: created.length }, "site_contract");
|
|
647
|
-
return { ok: true, id: info.lastInsertRowid, project, status, created_findings: created };
|
|
648
|
-
}
|
|
649
|
-
|
|
650
|
-
function siteGoldenCheckHistory(db, a) {
|
|
651
|
-
const where = [];
|
|
652
|
-
const params = [];
|
|
653
|
-
if (a.project) {
|
|
654
|
-
where.push("project=?");
|
|
655
|
-
params.push(a.project);
|
|
656
|
-
}
|
|
657
|
-
params.push(clampInt(a.limit, 50, 1, 500));
|
|
658
|
-
const rows = db.prepare(`
|
|
659
|
-
SELECT id, project, agent_name, status, command, summary, created_at
|
|
660
|
-
FROM golden_check_run
|
|
661
|
-
${where.length ? "WHERE " + where.join(" AND ") : ""}
|
|
662
|
-
ORDER BY created_at DESC
|
|
663
|
-
LIMIT ?
|
|
664
|
-
`).all(...params);
|
|
665
|
-
return { count: rows.length, runs: rows };
|
|
666
|
-
}
|
|
667
|
-
|
|
668
|
-
function groupGoldenFindings(findings, status) {
|
|
669
|
-
const groups = new Map();
|
|
670
|
-
for (const f of findings || []) {
|
|
671
|
-
if (!f || (!f.type && !f.title)) continue;
|
|
672
|
-
const type = cleanText(f.type || f.title || "golden_check_finding", 120);
|
|
673
|
-
const key = type;
|
|
674
|
-
const sev = validSeverity(f.severity || (status === "fail" ? "H" : "M"));
|
|
675
|
-
const current = groups.get(key) || {
|
|
676
|
-
type,
|
|
677
|
-
title: "Golden audit: " + type,
|
|
678
|
-
category: f.category || type || "site_contract",
|
|
679
|
-
severity: sev,
|
|
680
|
-
expected: f.expected || "Site contract passes for every checked path, locale, link, logo and viewport.",
|
|
681
|
-
count: 0,
|
|
682
|
-
urls: [],
|
|
683
|
-
examples: [],
|
|
684
|
-
};
|
|
685
|
-
current.severity = higherSeverity(current.severity, sev);
|
|
686
|
-
current.count += 1;
|
|
687
|
-
const url = f.url || f.href || null;
|
|
688
|
-
if (url && !current.urls.includes(url)) current.urls.push(url);
|
|
689
|
-
if (current.examples.length < 8) {
|
|
690
|
-
current.examples.push({
|
|
691
|
-
url,
|
|
692
|
-
label: f.label || f.text || null,
|
|
693
|
-
href: f.href || null,
|
|
694
|
-
status: f.status || null,
|
|
695
|
-
summary: summarizeGoldenFinding(f),
|
|
696
|
-
});
|
|
697
|
-
}
|
|
698
|
-
groups.set(key, current);
|
|
699
|
-
}
|
|
700
|
-
return Array.from(groups.values()).sort((a, b) => severityRank(a.severity) - severityRank(b.severity) || b.count - a.count);
|
|
701
|
-
}
|
|
702
|
-
|
|
703
|
-
function summarizeGoldenFinding(f) {
|
|
704
|
-
const parts = [];
|
|
705
|
-
if (f.type || f.title) parts.push(String(f.type || f.title));
|
|
706
|
-
if (f.label || f.text) parts.push("label=" + String(f.label || f.text).slice(0, 80));
|
|
707
|
-
if (f.href) parts.push("href=" + String(f.href).slice(0, 120));
|
|
708
|
-
if (f.url) parts.push("url=" + String(f.url).slice(0, 120));
|
|
709
|
-
if (f.status) parts.push("status=" + String(f.status));
|
|
710
|
-
return parts.join(" ");
|
|
711
|
-
}
|
|
712
|
-
|
|
713
|
-
function higherSeverity(a, b) {
|
|
714
|
-
return severityRank(b) < severityRank(a) ? b : a;
|
|
715
|
-
}
|
|
716
|
-
|
|
717
|
-
function severityRank(value) {
|
|
718
|
-
if (value === "critical") return 0;
|
|
719
|
-
if (value === "H") return 1;
|
|
720
|
-
if (value === "M") return 2;
|
|
721
|
-
return 3;
|
|
722
|
-
}
|
|
723
|
-
|
|
724
|
-
function defaultContract(project, registry, rules) {
|
|
725
|
-
const liveUrl = registry && (registry.live_url || registry.staging_url) || null;
|
|
726
|
-
const lang = rules ? json(rules.language_matrix, {}) || {} : {};
|
|
727
|
-
const locales = Array.isArray(lang.required_locales) ? lang.required_locales : (Array.isArray(lang.locales) ? lang.locales : []);
|
|
728
|
-
const paths = locales.length ? unique(["/"].concat(locales.map((l) => "/" + String(l).replace(/^\/+/, "")))) : ["/"];
|
|
729
|
-
return {
|
|
730
|
-
project,
|
|
731
|
-
canonical_url: liveUrl,
|
|
732
|
-
target_urls: liveUrl ? [liveUrl] : [],
|
|
733
|
-
paths,
|
|
734
|
-
forbidden_hosts: [],
|
|
735
|
-
required_locales: locales,
|
|
736
|
-
header_rules: {},
|
|
737
|
-
menu_rules: rules ? { canonical_nav: json(rules.canonical_nav, null) } : {},
|
|
738
|
-
footer_rules: {},
|
|
739
|
-
logo_rules: {},
|
|
740
|
-
auth_rules: {},
|
|
741
|
-
pricing_rules: rules ? json(rules.pricing_rules, {}) || {} : {},
|
|
742
|
-
checkout_rules: rules ? json(rules.checkout_rules, {}) || {} : {},
|
|
743
|
-
mobile_viewports: ["390x844"],
|
|
744
|
-
desktop_viewports: ["1440x900"],
|
|
745
|
-
required_checks: defaultRequiredChecks(),
|
|
746
|
-
notes: "Generated fallback. Store a real contract with mem_site_contract_set.",
|
|
747
|
-
updated_at: null,
|
|
748
|
-
updated_by: null,
|
|
749
|
-
_generated: true,
|
|
750
|
-
};
|
|
751
|
-
}
|
|
752
|
-
|
|
753
|
-
function parseSiteContract(row) {
|
|
754
|
-
const out = Object.assign({}, row);
|
|
755
|
-
for (const key of SITE_JSON_FIELDS) out[key] = json(out[key], defaultForSiteField(key));
|
|
756
|
-
if (!Array.isArray(out.paths) || !out.paths.length) out.paths = ["/"];
|
|
757
|
-
if (!Array.isArray(out.mobile_viewports) || !out.mobile_viewports.length) out.mobile_viewports = ["390x844"];
|
|
758
|
-
if (!Array.isArray(out.desktop_viewports) || !out.desktop_viewports.length) out.desktop_viewports = ["1440x900"];
|
|
759
|
-
if (!Array.isArray(out.required_checks) || !out.required_checks.length) out.required_checks = defaultRequiredChecks();
|
|
760
|
-
return out;
|
|
761
|
-
}
|
|
762
|
-
|
|
763
|
-
function defaultForSiteField(key) {
|
|
764
|
-
if (["target_urls", "paths", "forbidden_hosts", "required_locales", "mobile_viewports", "desktop_viewports", "required_checks"].includes(key)) return [];
|
|
765
|
-
return {};
|
|
766
|
-
}
|
|
767
|
-
|
|
768
|
-
function defaultRequiredChecks() {
|
|
769
|
-
return ["header_style", "menu_structure", "menu_href_targets", "footer_legal_links", "forbidden_domain_leaks", "locale_routes", "legal_locale_links", "light_dark_logos", "viewport_meta", "mobile", "desktop"];
|
|
770
|
-
}
|
|
771
|
-
|
|
772
|
-
function requiredSiteEvidence(contract) {
|
|
773
|
-
const required = Array.isArray(contract.required_checks) && contract.required_checks.length ? contract.required_checks : defaultRequiredChecks();
|
|
774
|
-
return {
|
|
775
|
-
required_checks: required,
|
|
776
|
-
paths: contract.paths || ["/"],
|
|
777
|
-
locales: contract.required_locales || [],
|
|
778
|
-
mobile_viewports: contract.mobile_viewports || ["390x844"],
|
|
779
|
-
desktop_viewports: contract.desktop_viewports || ["1440x900"],
|
|
780
|
-
must_record: [
|
|
781
|
-
"audit command output",
|
|
782
|
-
"browser/screenshot evidence when style or layout changed",
|
|
783
|
-
"same-label menu hrefs checked for target-domain and expected path",
|
|
784
|
-
"links checked for target-domain preservation",
|
|
785
|
-
"localized legal links checked for locale preservation",
|
|
786
|
-
"logo and dark-logo assets checked when applicable",
|
|
787
|
-
"open findings created for every failed check",
|
|
788
|
-
],
|
|
789
|
-
};
|
|
790
|
-
}
|
|
791
|
-
|
|
792
|
-
function buildAuditCommand(contract) {
|
|
793
|
-
if (!contract || !contract.canonical_url || !Array.isArray(contract.target_urls) || !contract.target_urls.length) return "";
|
|
794
|
-
const args = [
|
|
795
|
-
"node packages/core/bin/site-contract-audit.js",
|
|
796
|
-
"--canonical " + shellQuote(contract.canonical_url),
|
|
797
|
-
"--targets " + shellQuote(contract.target_urls.join(",")),
|
|
798
|
-
"--paths " + shellQuote((contract.paths && contract.paths.length ? contract.paths : ["/"]).join(",")),
|
|
799
|
-
];
|
|
800
|
-
if (Array.isArray(contract.forbidden_hosts) && contract.forbidden_hosts.length) {
|
|
801
|
-
args.push("--forbidden-hosts " + shellQuote(contract.forbidden_hosts.join(",")));
|
|
802
|
-
}
|
|
803
|
-
const allowed = allowedHostsFromContract(contract);
|
|
804
|
-
if (allowed.length) args.push("--allowed-hosts " + shellQuote(allowed.join(",")));
|
|
805
|
-
const logo = contract.logo_rules || {};
|
|
806
|
-
if (logo.require_dark_logo || logo.dark_logo_required || logo.dark_mode === true) args.push("--require-dark-logo");
|
|
807
|
-
args.push("--report");
|
|
808
|
-
args.push("--project " + shellQuote(contract.project));
|
|
809
|
-
return args.join(" ");
|
|
810
|
-
}
|
|
811
|
-
|
|
812
|
-
function allowedHostsFromContract(contract) {
|
|
813
|
-
const set = new Set();
|
|
814
|
-
const hint = contract && contract.project_rules_hint;
|
|
815
|
-
const allowed = hint && Array.isArray(hint.allowed_domains) ? hint.allowed_domains : [];
|
|
816
|
-
for (const item of allowed) {
|
|
817
|
-
const raw = String(item || "").trim();
|
|
818
|
-
if (!raw) continue;
|
|
819
|
-
try { set.add(new URL(raw.includes("://") ? raw : "https://" + raw).host); }
|
|
820
|
-
catch { set.add(raw.replace(/^https?:\/\//, "").replace(/\/.*$/, "")); }
|
|
821
|
-
}
|
|
822
|
-
return Array.from(set).filter(Boolean);
|
|
823
|
-
}
|
|
824
|
-
|
|
825
|
-
function collectAgents(db, agentName, since) {
|
|
826
|
-
if (agentName) return [agentName];
|
|
827
|
-
const set = new Set();
|
|
828
|
-
for (const row of safeAll(db, "SELECT agent_name FROM agent_registry WHERE agent_name IS NOT NULL", [])) set.add(row.agent_name);
|
|
829
|
-
for (const row of safeAll(db, "SELECT DISTINCT agent_name FROM agent_action WHERE agent_name IS NOT NULL AND started_at>=?", [since])) set.add(row.agent_name);
|
|
830
|
-
for (const row of safeAll(db, "SELECT DISTINCT agent_name FROM agent_brief WHERE agent_name IS NOT NULL AND created_at>=?", [since])) set.add(row.agent_name);
|
|
831
|
-
for (const row of safeAll(db, "SELECT DISTINCT source_agent AS agent_name FROM agent_brief WHERE source_agent IS NOT NULL AND created_at>=?", [since])) set.add(row.agent_name);
|
|
832
|
-
for (const row of safeAll(db, "SELECT DISTINCT assigned_agent AS agent_name FROM autonomy_task WHERE assigned_agent IS NOT NULL AND created_at>=?", [since])) set.add(row.agent_name);
|
|
833
|
-
for (const row of safeAll(db, "SELECT DISTINCT source_agent AS agent_name FROM quality_finding WHERE source_agent IS NOT NULL AND created_at>=?", [since])) set.add(row.agent_name);
|
|
834
|
-
return Array.from(set).filter(Boolean).sort();
|
|
835
|
-
}
|
|
836
|
-
|
|
837
|
-
function formatScoreboard(board, limit) {
|
|
838
|
-
const lines = ["# Agent Scoreboard", "", `Window: ${board.days} days | agents: ${board.count} | needs training: ${board.summary.needs_training}`, ""];
|
|
839
|
-
for (const c of board.agents.slice(0, limit)) {
|
|
840
|
-
lines.push(`## ${c.agent_name}`);
|
|
841
|
-
lines.push(`- Status: ${c.status} | score: ${c.score}`);
|
|
842
|
-
lines.push(`- Actions: ${c.actions.done}/${c.actions.total} done, ${c.actions.failed} failed, ${c.actions.guard_blocked} guard blocks`);
|
|
843
|
-
lines.push(`- Briefs: ${c.briefs.pending} pending, ${c.briefs.dispatched} dispatched, ${c.briefs.failed} failed`);
|
|
844
|
-
lines.push(`- Findings: ${c.findings.open} open (${c.findings.open_high} high/critical)`);
|
|
845
|
-
if (c.training_brief.length) lines.push(`- Training: ${c.training_brief.join(" ")}`);
|
|
846
|
-
lines.push("");
|
|
847
|
-
}
|
|
848
|
-
return lines.join("\n");
|
|
849
|
-
}
|
|
850
|
-
|
|
851
|
-
function logAction(db, agentName, kind, target, status, payload, topic) {
|
|
852
|
-
try {
|
|
853
|
-
db.prepare("INSERT INTO agent_action (agent_name, action_kind, target, status, payload_json, topic) VALUES (?,?,?,?,?,?)")
|
|
854
|
-
.run(agentName || "unknown", kind, target || null, status || "done", payload ? JSON.stringify(payload) : null, topic || null);
|
|
855
|
-
} catch {}
|
|
856
|
-
}
|
|
857
|
-
|
|
858
|
-
function safeGet(db, sql, params, fallback) {
|
|
859
|
-
try { return db.prepare(sql).get(...(params || [])) || fallback; } catch { return fallback; }
|
|
860
|
-
}
|
|
861
|
-
|
|
862
|
-
function safeAll(db, sql, params) {
|
|
863
|
-
try { return db.prepare(sql).all(...(params || [])); } catch { return []; }
|
|
864
|
-
}
|
|
865
|
-
|
|
866
|
-
function json(value, fallback) {
|
|
867
|
-
if (value == null || value === "") return fallback;
|
|
868
|
-
if (typeof value !== "string") return value;
|
|
869
|
-
try { return JSON.parse(value); } catch { return fallback === undefined ? value : fallback; }
|
|
870
|
-
}
|
|
871
|
-
|
|
872
|
-
function cleanText(value, max) {
|
|
873
|
-
const s = String(value || "").trim();
|
|
874
|
-
if (!s) return "";
|
|
875
|
-
return s.slice(0, max || 1000);
|
|
876
|
-
}
|
|
877
|
-
|
|
878
|
-
function firstSentence(text) {
|
|
879
|
-
const clean = String(text || "").replace(/\s+/g, " ").trim();
|
|
880
|
-
const m = clean.match(/^(.{1,140}?)(?:[.!?]\s|$)/);
|
|
881
|
-
return m ? m[1] : clean.slice(0, 140);
|
|
882
|
-
}
|
|
883
|
-
|
|
884
|
-
function slugify(value) {
|
|
885
|
-
return String(value || "")
|
|
886
|
-
.toLowerCase()
|
|
887
|
-
.replace(/[^a-z0-9]+/g, "-")
|
|
888
|
-
.replace(/^-+|-+$/g, "")
|
|
889
|
-
.slice(0, 120);
|
|
890
|
-
}
|
|
891
|
-
|
|
892
|
-
function validSeverity(value) {
|
|
893
|
-
return ["L", "M", "H", "critical"].includes(value) ? value : "M";
|
|
894
|
-
}
|
|
895
|
-
|
|
896
|
-
function clampInt(value, fallback, min, max) {
|
|
897
|
-
const n = Number.parseInt(value, 10);
|
|
898
|
-
if (!Number.isFinite(n)) return fallback;
|
|
899
|
-
return Math.max(min, Math.min(max, n));
|
|
900
|
-
}
|
|
901
|
-
|
|
902
|
-
function num(value) {
|
|
903
|
-
return Number(value || 0);
|
|
904
|
-
}
|
|
905
|
-
|
|
906
|
-
function normalizeStats(stats) {
|
|
907
|
-
const out = {};
|
|
908
|
-
for (const [k, v] of Object.entries(stats || {})) out[k] = num(v);
|
|
909
|
-
return out;
|
|
910
|
-
}
|
|
911
|
-
|
|
912
|
-
function zeroActionStats() {
|
|
913
|
-
return { total: 0, done: 0, failed: 0, blocked: 0, guard_total: 0, guard_blocked: 0, stale: 0 };
|
|
914
|
-
}
|
|
915
|
-
|
|
916
|
-
function zeroBriefStats() {
|
|
917
|
-
return { total: 0, pending: 0, dispatched: 0, done: 0, failed: 0 };
|
|
918
|
-
}
|
|
919
|
-
|
|
920
|
-
function zeroFindingStats() {
|
|
921
|
-
return { total: 0, open: 0, open_high: 0, closed: 0 };
|
|
922
|
-
}
|
|
923
|
-
|
|
924
|
-
function zeroTaskStats() {
|
|
925
|
-
return { total: 0, done: 0, open: 0, blocked: 0 };
|
|
926
|
-
}
|
|
927
|
-
|
|
928
|
-
function unique(arr) {
|
|
929
|
-
return Array.from(new Set((arr || []).filter(Boolean)));
|
|
930
|
-
}
|
|
931
|
-
|
|
932
|
-
function shellQuote(value) {
|
|
933
|
-
const s = String(value || "");
|
|
934
|
-
if (/^[A-Za-z0-9_./:,@%+=-]+$/.test(s)) return s;
|
|
935
|
-
return '"' + s.replace(/"/g, '\\"') + '"';
|
|
936
|
-
}
|
|
937
|
-
|
|
938
|
-
module.exports = {
|
|
939
|
-
TEAM_QUALITY_TOOL_DEFS,
|
|
940
|
-
SITE_JSON_FIELDS,
|
|
941
|
-
ensureTeamQualityTables,
|
|
942
|
-
handleTeamQualityTool,
|
|
943
|
-
buildAuditCommand,
|
|
944
|
-
};
|