@hawkeyexl/manni 4.5.0 → 4.7.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 +1 -1
- package/dist/batch-U6S2JUUN.js +41 -0
- package/dist/build-4RN6WBCP.js +32 -0
- package/dist/calibrate-6VIZYR4L.js +36 -0
- package/dist/capture-DP3NXAZE.js +34 -0
- package/dist/{check-RQIIIURI.js → check-ERVNGKLC.js} +13 -14
- package/dist/check-IWHUPAZM.js +886 -0
- package/dist/check-IWHUPAZM.js.map +1 -0
- package/dist/{check-ESNS7ZBC.js → check-OAJWC3WD.js} +7 -5
- package/dist/check-STHXZIZO.js +26 -0
- package/dist/{chunk-TA42STP7.js → chunk-2ZWTSRE5.js} +4 -4
- package/dist/{chunk-F2OVHJNC.js → chunk-4Z2MN5FR.js} +11 -11
- package/dist/{chunk-HVFR66GQ.js → chunk-5UB47BEV.js} +3280 -283
- package/dist/chunk-5UB47BEV.js.map +1 -0
- package/dist/{chunk-TCC737IS.js → chunk-5VWDIWOH.js} +4 -4
- package/dist/{chunk-B7XUEQF3.js → chunk-6KKZXB2O.js} +7 -5
- package/dist/{chunk-B7XUEQF3.js.map → chunk-6KKZXB2O.js.map} +1 -1
- package/dist/{chunk-ELZ2IIOF.js → chunk-6URSCTTX.js} +2 -2
- package/dist/{chunk-K3U5F2QT.js → chunk-6VDBP5UV.js} +12 -12
- package/dist/chunk-7EL5LLKD.js +233 -0
- package/dist/chunk-7EL5LLKD.js.map +1 -0
- package/dist/{chunk-C3KI45RF.js → chunk-7XZ6INKO.js} +4 -4
- package/dist/{chunk-ZX43SRZR.js → chunk-AMEILOVV.js} +8 -6
- package/dist/{chunk-ZX43SRZR.js.map → chunk-AMEILOVV.js.map} +1 -1
- package/dist/{chunk-SNEHX4VE.js → chunk-B5CMM57M.js} +2 -2
- package/dist/chunk-BWCWTHYS.js +146 -0
- package/dist/chunk-BWCWTHYS.js.map +1 -0
- package/dist/{chunk-QLUMKWQQ.js → chunk-C2TJG5KV.js} +10 -6
- package/dist/{chunk-QLUMKWQQ.js.map → chunk-C2TJG5KV.js.map} +1 -1
- package/dist/{chunk-GAZFKZHM.js → chunk-CEVU3GFF.js} +6 -6
- package/dist/chunk-CI55ABCK.js +502 -0
- package/dist/chunk-CI55ABCK.js.map +1 -0
- package/dist/{chunk-QCBICNKC.js → chunk-CR3HZERW.js} +202 -54
- package/dist/chunk-CR3HZERW.js.map +1 -0
- package/dist/chunk-CU57HT2E.js +116 -0
- package/dist/chunk-CU57HT2E.js.map +1 -0
- package/dist/chunk-CWXZ2MRT.js +222 -0
- package/dist/chunk-CWXZ2MRT.js.map +1 -0
- package/dist/chunk-D2LBN44V.js +134 -0
- package/dist/chunk-D2LBN44V.js.map +1 -0
- package/dist/chunk-D6RO54QD.js +674 -0
- package/dist/chunk-D6RO54QD.js.map +1 -0
- package/dist/{chunk-52PDIZKV.js → chunk-ECVQNGTH.js} +3 -3
- package/dist/chunk-FQGLRJGX.js +174 -0
- package/dist/chunk-FQGLRJGX.js.map +1 -0
- package/dist/{chunk-NGK3N6F2.js → chunk-GCC52YZD.js} +3 -3
- package/dist/chunk-HX44GB4Z.js +30 -0
- package/dist/chunk-HX44GB4Z.js.map +1 -0
- package/dist/{chunk-NN4ASVTY.js → chunk-IIFADWWI.js} +4 -4
- package/dist/{chunk-OIZ3XMKB.js → chunk-IOL74S4J.js} +4 -4
- package/dist/{chunk-UK2I3ACY.js → chunk-IZBBXKMJ.js} +4 -4
- package/dist/{chunk-YA63PVWI.js → chunk-K52CS4KT.js} +12 -10
- package/dist/{chunk-YA63PVWI.js.map → chunk-K52CS4KT.js.map} +1 -1
- package/dist/{chunk-7OQAU4EA.js → chunk-LDKIIZDW.js} +16 -12
- package/dist/{chunk-7OQAU4EA.js.map → chunk-LDKIIZDW.js.map} +1 -1
- package/dist/{chunk-QY3DOZMS.js → chunk-LONRQBGV.js} +4 -4
- package/dist/{chunk-EL2BZTDL.js → chunk-LXJEAV2V.js} +13 -11
- package/dist/{chunk-EL2BZTDL.js.map → chunk-LXJEAV2V.js.map} +1 -1
- package/dist/{chunk-XWFNO5PO.js → chunk-MIBWI5H7.js} +3 -3
- package/dist/{chunk-527HHQPS.js → chunk-MSMB6XOS.js} +6 -4
- package/dist/{chunk-527HHQPS.js.map → chunk-MSMB6XOS.js.map} +1 -1
- package/dist/{chunk-C6HZ6C5T.js → chunk-NIFEEIGQ.js} +2 -2
- package/dist/{chunk-JQU7FHYH.js → chunk-NZ6TNSCU.js} +10 -10
- package/dist/{chunk-3R5Y3XOL.js → chunk-OOEMKOX6.js} +29 -591
- package/dist/chunk-OOEMKOX6.js.map +1 -0
- package/dist/{chunk-MZC62HPZ.js → chunk-PAOVVHQX.js} +2 -2
- package/dist/chunk-PNX43DYW.js +849 -0
- package/dist/chunk-PNX43DYW.js.map +1 -0
- package/dist/chunk-QCHVR4ZM.js +286 -0
- package/dist/chunk-QCHVR4ZM.js.map +1 -0
- package/dist/{chunk-SJO2FTPG.js → chunk-QP5X66TO.js} +73 -1304
- package/dist/chunk-QP5X66TO.js.map +1 -0
- package/dist/{chunk-HQFVJDEN.js → chunk-QQNPVW3J.js} +19 -19
- package/dist/chunk-TPVOOTMN.js +532 -0
- package/dist/chunk-TPVOOTMN.js.map +1 -0
- package/dist/{chunk-IA7BMIPE.js → chunk-UZKQSWHJ.js} +2 -2
- package/dist/chunk-WQWKYAAK.js +86 -0
- package/dist/chunk-WQWKYAAK.js.map +1 -0
- package/dist/{chunk-CP4ZM43V.js → chunk-WTEILBKC.js} +2 -2
- package/dist/{chunk-LJ2RDH3F.js → chunk-XG6PVB5S.js} +1 -23
- package/dist/chunk-XG6PVB5S.js.map +1 -0
- package/dist/{chunk-Q35YIDBV.js → chunk-XKBEHKWO.js} +29 -264
- package/dist/chunk-XKBEHKWO.js.map +1 -0
- package/dist/{chunk-BOIF2XGG.js → chunk-XXDXIIQ4.js} +2 -2
- package/dist/{chunk-6XTQDFGP.js → chunk-YSCZL4U5.js} +3 -3
- package/dist/{chunk-6XTQDFGP.js.map → chunk-YSCZL4U5.js.map} +1 -1
- package/dist/{chunk-7ZUCEMCY.js → chunk-ZEXGGGLW.js} +9 -11
- package/dist/{chunk-7ZUCEMCY.js.map → chunk-ZEXGGGLW.js.map} +1 -1
- package/dist/{chunk-DX32P2DT.js → chunk-ZKAKA54B.js} +2 -2
- package/dist/{chunk-TVIX653Q.js → chunk-ZKCNPKUO.js} +4 -4
- package/dist/{chunk-DE7JSWXV.js → chunk-ZNE25EAR.js} +8 -6
- package/dist/{chunk-DE7JSWXV.js.map → chunk-ZNE25EAR.js.map} +1 -1
- package/dist/{chunk-FLF6AECQ.js → chunk-ZPP75QWG.js} +17 -15
- package/dist/{chunk-FLF6AECQ.js.map → chunk-ZPP75QWG.js.map} +1 -1
- package/dist/{cli-PRZHQ3QB.js → cli-37SRAYNE.js} +14 -15
- package/dist/{cli-PRZHQ3QB.js.map → cli-37SRAYNE.js.map} +1 -1
- package/dist/{cli-TYGOHJOJ.js → cli-HXWYUKGJ.js} +225 -58
- package/dist/cli-HXWYUKGJ.js.map +1 -0
- package/dist/{cli-HUIMNJT6.js → cli-IRHPPUFJ.js} +19 -20
- package/dist/{cli-HUIMNJT6.js.map → cli-IRHPPUFJ.js.map} +1 -1
- package/dist/{cli-6PGVHP7Y.js → cli-KIID535Y.js} +17 -19
- package/dist/{cli-6PGVHP7Y.js.map → cli-KIID535Y.js.map} +1 -1
- package/dist/{cli-AAG3N4NB.js → cli-MTDGQIIV.js} +17 -18
- package/dist/{cli-AAG3N4NB.js.map → cli-MTDGQIIV.js.map} +1 -1
- package/dist/{cli-E3H6ZP4I.js → cli-NM6FZ54R.js} +5 -3
- package/dist/{cli-E3H6ZP4I.js.map → cli-NM6FZ54R.js.map} +1 -1
- package/dist/{cli-MHTYPVOD.js → cli-XIODOEUG.js} +113 -20
- package/dist/{cli-MHTYPVOD.js.map → cli-XIODOEUG.js.map} +1 -1
- package/dist/{cli-YW6VWBHX.js → cli-YL3IDR52.js} +10 -10
- package/dist/{cli-QKIJOXGW.js → cli-ZQUJYQBA.js} +17 -17
- package/dist/cli.js +11 -11
- package/dist/config-O6UBGEDK.js +26 -0
- package/dist/conformance-ALLPNGEQ.js +18 -0
- package/dist/docmeta.js +9 -9
- package/dist/github-UCYLM3DR.js +26 -0
- package/dist/github-VEJFO6JJ.js +31 -0
- package/dist/host-6GURH5HE.js +23 -0
- package/dist/index.d.ts +72 -4
- package/dist/index.js +176 -166
- package/dist/index.js.map +1 -1
- package/dist/{json-4DCKDGUV.js → json-6F52FSXZ.js} +12 -13
- package/dist/lint-X2GNINK6.js +29 -0
- package/dist/list-DP3O3CWB.js +30 -0
- package/dist/list-P77RZFZF.js +28 -0
- package/dist/{page-QEDB7EVU.js → page-WQCMBNRD.js} +11 -12
- package/dist/prepare-52QOK7M4.js +155 -0
- package/dist/prepare-52QOK7M4.js.map +1 -0
- package/dist/{pretty-SQX33UM2.js → pretty-5MMMNSWF.js} +13 -14
- package/dist/{pretty-5DGEBZSV.js → pretty-H4DQREMW.js} +12 -13
- package/dist/provider-AMGRDY6V.js +34 -0
- package/dist/release-CTYRHZL4.js +36 -0
- package/dist/release-CTYRHZL4.js.map +1 -0
- package/dist/{reporters-YELDDYJD.js → reporters-SU3VUKKX.js} +7 -5
- package/dist/{reporters-OHSI3RMV.js → reporters-WIHBEMPG.js} +11 -12
- package/dist/reporters-WIHBEMPG.js.map +1 -0
- package/dist/run-L2DWJ26K.js +32 -0
- package/dist/run-L2DWJ26K.js.map +1 -0
- package/dist/{term-2WMDZ4PY.js → term-PJIJQZNF.js} +16 -17
- package/dist/{term-2WMDZ4PY.js.map → term-PJIJQZNF.js.map} +1 -1
- package/dist/validate-XCDSPURG.js +17 -0
- package/dist/validate-XCDSPURG.js.map +1 -0
- package/package.json +2 -2
- package/plugin/manni/.claude-plugin/plugin.json +1 -1
- package/plugin/manni/hooks/hooks.json +47 -0
- package/dist/batch-GOE3UXOD.js +0 -36
- package/dist/build-M7BMEHL2.js +0 -33
- package/dist/calibrate-6RRAILUY.js +0 -31
- package/dist/capture-RJPQ6B4N.js +0 -32
- package/dist/check-BQD64X3N.js +0 -27
- package/dist/chunk-3R5Y3XOL.js.map +0 -1
- package/dist/chunk-3YHAYXBC.js +0 -164
- package/dist/chunk-3YHAYXBC.js.map +0 -1
- package/dist/chunk-HVFR66GQ.js.map +0 -1
- package/dist/chunk-LJ2RDH3F.js.map +0 -1
- package/dist/chunk-NAZRLJAK.js +0 -3090
- package/dist/chunk-NAZRLJAK.js.map +0 -1
- package/dist/chunk-Q35YIDBV.js.map +0 -1
- package/dist/chunk-QCBICNKC.js.map +0 -1
- package/dist/chunk-SJO2FTPG.js.map +0 -1
- package/dist/chunk-TU2WUBVZ.js +0 -2
- package/dist/cli-TYGOHJOJ.js.map +0 -1
- package/dist/github-3WMSTNTK.js +0 -32
- package/dist/github-OXSJQ4PS.js +0 -27
- package/dist/lint-YDHVVZHT.js +0 -30
- package/dist/list-3ZO2NIYO.js +0 -31
- package/dist/list-EJH6VAOW.js +0 -29
- package/dist/run-IP6NZHAG.js +0 -33
- package/dist/validate-G6HH3YXL.js +0 -16
- /package/dist/{batch-GOE3UXOD.js.map → batch-U6S2JUUN.js.map} +0 -0
- /package/dist/{build-M7BMEHL2.js.map → build-4RN6WBCP.js.map} +0 -0
- /package/dist/{calibrate-6RRAILUY.js.map → calibrate-6VIZYR4L.js.map} +0 -0
- /package/dist/{capture-RJPQ6B4N.js.map → capture-DP3NXAZE.js.map} +0 -0
- /package/dist/{check-BQD64X3N.js.map → check-ERVNGKLC.js.map} +0 -0
- /package/dist/{check-ESNS7ZBC.js.map → check-OAJWC3WD.js.map} +0 -0
- /package/dist/{check-RQIIIURI.js.map → check-STHXZIZO.js.map} +0 -0
- /package/dist/{chunk-TA42STP7.js.map → chunk-2ZWTSRE5.js.map} +0 -0
- /package/dist/{chunk-F2OVHJNC.js.map → chunk-4Z2MN5FR.js.map} +0 -0
- /package/dist/{chunk-TCC737IS.js.map → chunk-5VWDIWOH.js.map} +0 -0
- /package/dist/{chunk-ELZ2IIOF.js.map → chunk-6URSCTTX.js.map} +0 -0
- /package/dist/{chunk-K3U5F2QT.js.map → chunk-6VDBP5UV.js.map} +0 -0
- /package/dist/{chunk-C3KI45RF.js.map → chunk-7XZ6INKO.js.map} +0 -0
- /package/dist/{chunk-SNEHX4VE.js.map → chunk-B5CMM57M.js.map} +0 -0
- /package/dist/{chunk-GAZFKZHM.js.map → chunk-CEVU3GFF.js.map} +0 -0
- /package/dist/{chunk-52PDIZKV.js.map → chunk-ECVQNGTH.js.map} +0 -0
- /package/dist/{chunk-NGK3N6F2.js.map → chunk-GCC52YZD.js.map} +0 -0
- /package/dist/{chunk-NN4ASVTY.js.map → chunk-IIFADWWI.js.map} +0 -0
- /package/dist/{chunk-OIZ3XMKB.js.map → chunk-IOL74S4J.js.map} +0 -0
- /package/dist/{chunk-UK2I3ACY.js.map → chunk-IZBBXKMJ.js.map} +0 -0
- /package/dist/{chunk-QY3DOZMS.js.map → chunk-LONRQBGV.js.map} +0 -0
- /package/dist/{chunk-XWFNO5PO.js.map → chunk-MIBWI5H7.js.map} +0 -0
- /package/dist/{chunk-C6HZ6C5T.js.map → chunk-NIFEEIGQ.js.map} +0 -0
- /package/dist/{chunk-JQU7FHYH.js.map → chunk-NZ6TNSCU.js.map} +0 -0
- /package/dist/{chunk-MZC62HPZ.js.map → chunk-PAOVVHQX.js.map} +0 -0
- /package/dist/{chunk-HQFVJDEN.js.map → chunk-QQNPVW3J.js.map} +0 -0
- /package/dist/{chunk-IA7BMIPE.js.map → chunk-UZKQSWHJ.js.map} +0 -0
- /package/dist/{chunk-CP4ZM43V.js.map → chunk-WTEILBKC.js.map} +0 -0
- /package/dist/{chunk-BOIF2XGG.js.map → chunk-XXDXIIQ4.js.map} +0 -0
- /package/dist/{chunk-DX32P2DT.js.map → chunk-ZKAKA54B.js.map} +0 -0
- /package/dist/{chunk-TVIX653Q.js.map → chunk-ZKCNPKUO.js.map} +0 -0
- /package/dist/{chunk-TU2WUBVZ.js.map → cli-YL3IDR52.js.map} +0 -0
- /package/dist/{cli-QKIJOXGW.js.map → cli-ZQUJYQBA.js.map} +0 -0
- /package/dist/{cli-YW6VWBHX.js.map → config-O6UBGEDK.js.map} +0 -0
- /package/dist/{github-3WMSTNTK.js.map → conformance-ALLPNGEQ.js.map} +0 -0
- /package/dist/{github-OXSJQ4PS.js.map → github-UCYLM3DR.js.map} +0 -0
- /package/dist/{json-4DCKDGUV.js.map → github-VEJFO6JJ.js.map} +0 -0
- /package/dist/{lint-YDHVVZHT.js.map → host-6GURH5HE.js.map} +0 -0
- /package/dist/{list-3ZO2NIYO.js.map → json-6F52FSXZ.js.map} +0 -0
- /package/dist/{list-EJH6VAOW.js.map → lint-X2GNINK6.js.map} +0 -0
- /package/dist/{page-QEDB7EVU.js.map → list-DP3O3CWB.js.map} +0 -0
- /package/dist/{pretty-5DGEBZSV.js.map → list-P77RZFZF.js.map} +0 -0
- /package/dist/{pretty-SQX33UM2.js.map → page-WQCMBNRD.js.map} +0 -0
- /package/dist/{reporters-OHSI3RMV.js.map → pretty-5MMMNSWF.js.map} +0 -0
- /package/dist/{reporters-YELDDYJD.js.map → pretty-H4DQREMW.js.map} +0 -0
- /package/dist/{run-IP6NZHAG.js.map → provider-AMGRDY6V.js.map} +0 -0
- /package/dist/{validate-G6HH3YXL.js.map → reporters-SU3VUKKX.js.map} +0 -0
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
TracevalsError
|
|
4
|
+
} from "./chunk-UKZNLVJW.js";
|
|
5
|
+
|
|
6
|
+
// src/tracevals/judge/redact.ts
|
|
7
|
+
var SECRET_NAME = "(?:(?:[A-Za-z0-9]+[_\\-.])*(?:api[_\\-.]?key|access[_\\-.]?key|secret[_\\-.]?key|auth[_\\-.]?token|access[_\\-.]?token|refresh[_\\-.]?token|client[_\\-.]?secret|session[_\\-.]?token|key|token|secret|password|passwd|passphrase|credentials?|authorization)|AWS_[A-Za-z0-9_]+)";
|
|
8
|
+
var REDACTION_PATTERNS = [
|
|
9
|
+
{
|
|
10
|
+
label: "private-key",
|
|
11
|
+
pattern: /-----BEGIN [A-Z0-9 ]*PRIVATE KEY-----[\s\S]*?-----END [A-Z0-9 ]*PRIVATE KEY-----/g,
|
|
12
|
+
replacement: "[redacted:private-key]"
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
// `sk-`, `sk-ant-…`, `pk-`, `rk-`: the OpenAI/Anthropic/Stripe family.
|
|
16
|
+
label: "api-key",
|
|
17
|
+
pattern: /\b[sprk]k-(?:[A-Za-z0-9]+-)*[A-Za-z0-9_-]{16,}/g,
|
|
18
|
+
replacement: "[redacted:api-key]"
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
label: "aws-access-key-id",
|
|
22
|
+
pattern: /\b(?:AKIA|ASIA|AROA|AIDA)[0-9A-Z]{16}\b/g,
|
|
23
|
+
replacement: "[redacted:aws-access-key-id]"
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
label: "github-token",
|
|
27
|
+
pattern: /\b(?:gh[pousr]_[A-Za-z0-9]{20,}|github_pat_[A-Za-z0-9_]{20,})\b/g,
|
|
28
|
+
replacement: "[redacted:github-token]"
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
label: "slack-token",
|
|
32
|
+
pattern: /\bxox[abeoprs]-[A-Za-z0-9-]{10,}/g,
|
|
33
|
+
replacement: "[redacted:slack-token]"
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
label: "google-api-key",
|
|
37
|
+
pattern: /\bAIza[0-9A-Za-z_-]{35}\b/g,
|
|
38
|
+
replacement: "[redacted:google-api-key]"
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
label: "jwt",
|
|
42
|
+
pattern: /\beyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}/g,
|
|
43
|
+
replacement: "[redacted:jwt]"
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
// `Authorization: Bearer …`, `Basic …`, `Token …`. The scheme is kept so
|
|
47
|
+
// the judge can still see that authentication happened.
|
|
48
|
+
//
|
|
49
|
+
// Deliberately *not* case-insensitive as a whole: `basic` and `token` are
|
|
50
|
+
// ordinary English words, and `/i` would redact "basic configuration" as an
|
|
51
|
+
// auth header. `bearer` is not, so it gets both spellings.
|
|
52
|
+
label: "auth-token",
|
|
53
|
+
pattern: /\b([Bb]earer|Basic|Token)\s+[A-Za-z0-9\-._~+/]{12,}={0,2}/g,
|
|
54
|
+
replacement: "$1 [redacted:auth-token]"
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
// JSON member — the shape a tool input renders as.
|
|
58
|
+
label: "secret-member",
|
|
59
|
+
pattern: new RegExp(`("${SECRET_NAME}"\\s*:\\s*)"[^"]*"`, "gi"),
|
|
60
|
+
replacement: '$1"[redacted:secret-value]"'
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
// Shell / `.env` assignment. The variable name is kept: knowing that a
|
|
64
|
+
// secret was set, and which one, is often the adherence question.
|
|
65
|
+
label: "secret-assignment",
|
|
66
|
+
pattern: new RegExp(
|
|
67
|
+
`\\b(${SECRET_NAME})(\\s*=\\s*)(?:"[^"\\n]*"|'[^'\\n]*'|[^\\s"'\\n]+)`,
|
|
68
|
+
"gi"
|
|
69
|
+
),
|
|
70
|
+
replacement: "$1$2[redacted:secret-value]"
|
|
71
|
+
}
|
|
72
|
+
];
|
|
73
|
+
var CUSTOM_PLACEHOLDER = "[redacted]";
|
|
74
|
+
function makeRedactor(extra = []) {
|
|
75
|
+
const custom = compileRedactPatterns(extra).map(
|
|
76
|
+
(pattern) => [pattern, CUSTOM_PLACEHOLDER]
|
|
77
|
+
);
|
|
78
|
+
const all = [
|
|
79
|
+
...REDACTION_PATTERNS.map(
|
|
80
|
+
({ pattern, replacement }) => [pattern, replacement]
|
|
81
|
+
),
|
|
82
|
+
...custom
|
|
83
|
+
];
|
|
84
|
+
return (text) => {
|
|
85
|
+
let out = text;
|
|
86
|
+
for (const [pattern, replacement] of all) {
|
|
87
|
+
out = out.replace(pattern, replacement);
|
|
88
|
+
}
|
|
89
|
+
return out;
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
function compileRedactPatterns(sources) {
|
|
93
|
+
return sources.map((source) => {
|
|
94
|
+
try {
|
|
95
|
+
return new RegExp(source, "g");
|
|
96
|
+
} catch (err) {
|
|
97
|
+
throw new TracevalsError(
|
|
98
|
+
`invalid config: judge.redact pattern ${JSON.stringify(source)} is not a valid regular expression: ${err instanceof Error ? err.message : String(err)}`
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// src/tracevals/capture/types.ts
|
|
105
|
+
var MANIFEST_VERSION = 1;
|
|
106
|
+
var DEFAULT_CAPTURE_DIR = ".manni/tracevals/sessions";
|
|
107
|
+
|
|
108
|
+
export {
|
|
109
|
+
REDACTION_PATTERNS,
|
|
110
|
+
CUSTOM_PLACEHOLDER,
|
|
111
|
+
makeRedactor,
|
|
112
|
+
compileRedactPatterns,
|
|
113
|
+
MANIFEST_VERSION,
|
|
114
|
+
DEFAULT_CAPTURE_DIR
|
|
115
|
+
};
|
|
116
|
+
//# sourceMappingURL=chunk-CU57HT2E.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/tracevals/judge/redact.ts","../src/tracevals/capture/types.ts"],"sourcesContent":["/**\n * Redaction for the judge digest (ADR 01020).\n *\n * `renderTrace` builds the only trace-derived text that leaves this machine,\n * and a session transcript routinely carries credentials: a `Bash` input with\n * an `Authorization` header, an `.env` file echoed into a message, a key pasted\n * into a prompt. Every block that enters the digest is filtered through here\n * first.\n *\n * This is **best-effort pattern matching, not a guarantee.** It recognizes\n * shapes — vendor key prefixes, auth schemes, secret-looking assignment names —\n * so it catches the common accidents and cannot catch an arbitrary secret that\n * looks like ordinary prose. `--deterministic-only` is the only setting that\n * guarantees nothing leaves the machine.\n *\n * Two properties the rest of the system leans on:\n *\n * - **Stable output.** A given input always redacts to the same text, so\n * `sha256(renderedTrace)` stays a usable judge cache key.\n * - **Idempotent.** Placeholders contain no `=`, no `:` inside quotes, and no\n * vendor prefix, so redacting redacted text is a no-op.\n */\nimport { TracevalsError } from \"../types.js\";\n\nexport interface RedactionPattern {\n /** Kebab name; appears in the placeholder so a reader knows what went. */\n label: string;\n pattern: RegExp;\n /** Replacement template; `$1`-style references into `pattern` are allowed. */\n replacement: string;\n}\n\n/**\n * Names that mark a value as secret, in the shapes they appear in shell\n * assignments and in JSON object members (tool inputs render as JSON).\n *\n * The whole name must match, which is what keeps `monkey` out of the `key`\n * alternative: the `(?:[A-Za-z0-9]+[_\\-.])*` prefix only consumes segments that\n * end in a separator. `api[_\\-.]?key` covers `apiKey` and `API_KEY` alike under\n * the `i` flag.\n */\nconst SECRET_NAME =\n \"(?:(?:[A-Za-z0-9]+[_\\\\-.])*(?:api[_\\\\-.]?key|access[_\\\\-.]?key|secret[_\\\\-.]?key|auth[_\\\\-.]?token|access[_\\\\-.]?token|refresh[_\\\\-.]?token|client[_\\\\-.]?secret|session[_\\\\-.]?token|key|token|secret|password|passwd|passphrase|credentials?|authorization)|AWS_[A-Za-z0-9_]+)\";\n\n/**\n * Applied in order. Broad structural shapes first (a private key block spans\n * lines and would otherwise be chewed up by the narrower rules), then vendor\n * prefixes, then name-driven assignment forms.\n */\nexport const REDACTION_PATTERNS: readonly RedactionPattern[] = [\n {\n label: \"private-key\",\n pattern:\n /-----BEGIN [A-Z0-9 ]*PRIVATE KEY-----[\\s\\S]*?-----END [A-Z0-9 ]*PRIVATE KEY-----/g,\n replacement: \"[redacted:private-key]\",\n },\n {\n // `sk-`, `sk-ant-…`, `pk-`, `rk-`: the OpenAI/Anthropic/Stripe family.\n label: \"api-key\",\n pattern: /\\b[sprk]k-(?:[A-Za-z0-9]+-)*[A-Za-z0-9_-]{16,}/g,\n replacement: \"[redacted:api-key]\",\n },\n {\n label: \"aws-access-key-id\",\n pattern: /\\b(?:AKIA|ASIA|AROA|AIDA)[0-9A-Z]{16}\\b/g,\n replacement: \"[redacted:aws-access-key-id]\",\n },\n {\n label: \"github-token\",\n pattern: /\\b(?:gh[pousr]_[A-Za-z0-9]{20,}|github_pat_[A-Za-z0-9_]{20,})\\b/g,\n replacement: \"[redacted:github-token]\",\n },\n {\n label: \"slack-token\",\n pattern: /\\bxox[abeoprs]-[A-Za-z0-9-]{10,}/g,\n replacement: \"[redacted:slack-token]\",\n },\n {\n label: \"google-api-key\",\n pattern: /\\bAIza[0-9A-Za-z_-]{35}\\b/g,\n replacement: \"[redacted:google-api-key]\",\n },\n {\n label: \"jwt\",\n pattern: /\\beyJ[A-Za-z0-9_-]{8,}\\.[A-Za-z0-9_-]{8,}\\.[A-Za-z0-9_-]{8,}/g,\n replacement: \"[redacted:jwt]\",\n },\n {\n // `Authorization: Bearer …`, `Basic …`, `Token …`. The scheme is kept so\n // the judge can still see that authentication happened.\n //\n // Deliberately *not* case-insensitive as a whole: `basic` and `token` are\n // ordinary English words, and `/i` would redact \"basic configuration\" as an\n // auth header. `bearer` is not, so it gets both spellings.\n label: \"auth-token\",\n pattern: /\\b([Bb]earer|Basic|Token)\\s+[A-Za-z0-9\\-._~+/]{12,}={0,2}/g,\n replacement: \"$1 [redacted:auth-token]\",\n },\n {\n // JSON member — the shape a tool input renders as.\n label: \"secret-member\",\n pattern: new RegExp(`(\"${SECRET_NAME}\"\\\\s*:\\\\s*)\"[^\"]*\"`, \"gi\"),\n replacement: '$1\"[redacted:secret-value]\"',\n },\n {\n // Shell / `.env` assignment. The variable name is kept: knowing that a\n // secret was set, and which one, is often the adherence question.\n label: \"secret-assignment\",\n pattern: new RegExp(\n `\\\\b(${SECRET_NAME})(\\\\s*=\\\\s*)(?:\"[^\"\\\\n]*\"|'[^'\\\\n]*'|[^\\\\s\"'\\\\n]+)`,\n \"gi\",\n ),\n replacement: \"$1$2[redacted:secret-value]\",\n },\n];\n\n/** Placeholder used for a pattern the config supplied; it has no label. */\nexport const CUSTOM_PLACEHOLDER = \"[redacted]\";\n\nexport type Redactor = (text: string) => string;\n\n/**\n * Build the redactor for a run. Configured patterns are applied **after** the\n * built-ins and never replace them: a project adding one pattern of its own\n * must not silently lose the whole floor, which is the same reasoning that\n * makes `--require` additive (ADR 01017).\n *\n * A pattern that will not compile is an operational error raised here, not a\n * throw from inside the judge and never a silent skip — a dropped pattern is a\n * silent leak.\n */\nexport function makeRedactor(extra: readonly string[] = []): Redactor {\n // Compiled once and reused across every block of a render. Safe with a `g`\n // regex because `String.prototype.replace` sets `lastIndex` to 0 before it\n // starts and leaves it at 0 — unlike `exec`/`test`, where the shared instance\n // state would make a reused pattern skip matches.\n const custom = compileRedactPatterns(extra).map(\n (pattern) => [pattern, CUSTOM_PLACEHOLDER] as const,\n );\n const all: ReadonlyArray<readonly [RegExp, string]> = [\n ...REDACTION_PATTERNS.map(\n ({ pattern, replacement }) => [pattern, replacement] as const,\n ),\n ...custom,\n ];\n return (text) => {\n let out = text;\n for (const [pattern, replacement] of all) {\n out = out.replace(pattern, replacement);\n }\n return out;\n };\n}\n\n/**\n * Compile configured pattern sources, or say which one is unusable. Exported so\n * `parseConfig()` can reject a bad pattern at load time rather than at the\n * moment a digest is about to be sent.\n */\nexport function compileRedactPatterns(sources: readonly string[]): RegExp[] {\n return sources.map((source) => {\n try {\n return new RegExp(source, \"g\");\n } catch (err) {\n throw new TracevalsError(\n `invalid config: judge.redact pattern ${JSON.stringify(source)} is not a valid regular expression: ${\n err instanceof Error ? err.message : String(err)\n }`,\n );\n }\n });\n}\n","/**\n * The session manifest: what the project's instruction artifacts said, and\n * which commit they said it at, **when the session started** (ADR 01024).\n *\n * ADR 01016 established that the availability roster — what the session was\n * offered — is recoverable from the transcript itself, retroactively and on any\n * machine. Two things are not: the artifact **bodies** and the **git SHA**. The\n * transcript injects skill instructions into the model's context without ever\n * recording their text, so \"the SKILL.md says X now; did it say X then?\" has no\n * answer inside the trace. ADR 01021 answers it with mtime, which is a\n * heuristic that a `git checkout` defeats wholesale.\n *\n * A manifest closes that gap by recording, at session start, a sha256 per\n * instruction artifact. `run` then compares — and a mismatch is exact where\n * mtime was a guess.\n *\n * **A manifest is a claim, not proof.** It is written by the capturing device\n * and travels with the repository, so it is evidence *about that device*. It\n * never reaches an eval outcome, never moves the exit code, and never turns a\n * `skipped` into a `pass`; the only thing it may quiet is the mtime warning,\n * whose own question it answers exactly.\n *\n * Types and constants only, with no dependency of its own beyond\n * `ArtifactType` — `artifacts/resolve.ts` consumes a manifest and\n * `capture/build.ts` produces one from `artifacts/discover.ts`, so anything\n * shared has to sit below both or the two would import each other.\n */\nimport type { ArtifactType } from \"../artifacts/types.js\";\n\n/**\n * Manifest format version. Bumped when the *shape* changes incompatibly; a\n * manifest from a higher version is ignored rather than half-read, because a\n * partial read of an evidence file is worse than no evidence.\n */\nexport const MANIFEST_VERSION = 1;\n\n/** Default directory, under the state directory the judge cache already uses. */\nexport const DEFAULT_CAPTURE_DIR = \".manni/tracevals/sessions\";\n\nexport interface ManifestArtifact {\n /** Reference name, spelled as the coverage table spells it. */\n name: string;\n type: ArtifactType;\n /**\n * Path relative to `root`, with POSIX separators — never absolute, so the\n * manifest still joins after the repository is checked out somewhere else.\n */\n path: string;\n /** Lowercase hex sha256 of the file's bytes at capture time. */\n sha256: string;\n bytes: number;\n}\n\n/** What `git` could answer about the project at capture time. */\nexport interface ManifestGit {\n /** The commit the session ran against. */\n sha: string;\n branch?: string;\n /** Tracked files differed from the commit, so `sha` alone is incomplete. */\n dirty: boolean;\n}\n\nexport interface SessionManifest {\n version: number;\n sessionId: string;\n /** When `capture` ran, ISO-8601. */\n capturedAt: string;\n /** The hook that supplied the payload, or `manual` for a direct run. */\n hookEvent: string;\n /** How the session started or why it ended, when the payload said so. */\n reason?: string;\n /** Where the session file will be written. A correlation key, never read. */\n transcriptPath?: string;\n /** Project root the artifact paths are relative to. */\n root: string;\n git?: ManifestGit;\n /**\n * Which machine captured this. An opaque digest rather than the hostname:\n * the question a reader has is \"same machine or not\", and that does not need\n * the name of the machine to answer.\n */\n device: { id: string; platform: string };\n tool: { name: string; version: string };\n artifacts: ManifestArtifact[];\n /** The resolved `tracevals:` section, redacted. Recorded, not consumed. */\n config: unknown;\n}\n\nexport type ContentCheckStatus = \"match\" | \"mismatch\" | \"skipped\";\n\n/**\n * Whether an artifact still says what the manifest recorded. Three-valued on\n * purpose: `skipped` is not a shade of `match`, and collapsing them would let\n * an absent manifest read as a clean bill of health.\n */\nexport interface ContentCheck {\n status: ContentCheckStatus;\n /** Why no comparison was possible. Present only on `skipped`. */\n reason?: string;\n /** The digest recorded at capture time. */\n expected?: string;\n /** The digest of the file as it is now. */\n actual?: string;\n}\n\n/** The manifest `run` consulted, summarised for the report. */\nexport interface ManifestReport {\n /** Where it was read from. */\n path: string;\n sessionId: string;\n capturedAt: string;\n /** The commit the session ran against, when git could answer. */\n gitSha?: string;\n /** Coverage rows the manifest could speak to, and what it said. */\n matched: number;\n changed: number;\n /** Rows it had nothing to say about; those keep the mtime heuristic. */\n unrecorded: number;\n}\n"],"mappings":";;;;;;AAyCA,IAAM,cACJ;AAOK,IAAM,qBAAkD;AAAA,EAC7D;AAAA,IACE,OAAO;AAAA,IACP,SACE;AAAA,IACF,aAAa;AAAA,EACf;AAAA,EACA;AAAA;AAAA,IAEE,OAAO;AAAA,IACP,SAAS;AAAA,IACT,aAAa;AAAA,EACf;AAAA,EACA;AAAA,IACE,OAAO;AAAA,IACP,SAAS;AAAA,IACT,aAAa;AAAA,EACf;AAAA,EACA;AAAA,IACE,OAAO;AAAA,IACP,SAAS;AAAA,IACT,aAAa;AAAA,EACf;AAAA,EACA;AAAA,IACE,OAAO;AAAA,IACP,SAAS;AAAA,IACT,aAAa;AAAA,EACf;AAAA,EACA;AAAA,IACE,OAAO;AAAA,IACP,SAAS;AAAA,IACT,aAAa;AAAA,EACf;AAAA,EACA;AAAA,IACE,OAAO;AAAA,IACP,SAAS;AAAA,IACT,aAAa;AAAA,EACf;AAAA,EACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAOE,OAAO;AAAA,IACP,SAAS;AAAA,IACT,aAAa;AAAA,EACf;AAAA,EACA;AAAA;AAAA,IAEE,OAAO;AAAA,IACP,SAAS,IAAI,OAAO,KAAK,WAAW,sBAAsB,IAAI;AAAA,IAC9D,aAAa;AAAA,EACf;AAAA,EACA;AAAA;AAAA;AAAA,IAGE,OAAO;AAAA,IACP,SAAS,IAAI;AAAA,MACX,OAAO,WAAW;AAAA,MAClB;AAAA,IACF;AAAA,IACA,aAAa;AAAA,EACf;AACF;AAGO,IAAM,qBAAqB;AAc3B,SAAS,aAAa,QAA2B,CAAC,GAAa;AAKpE,QAAM,SAAS,sBAAsB,KAAK,EAAE;AAAA,IAC1C,CAAC,YAAY,CAAC,SAAS,kBAAkB;AAAA,EAC3C;AACA,QAAM,MAAgD;AAAA,IACpD,GAAG,mBAAmB;AAAA,MACpB,CAAC,EAAE,SAAS,YAAY,MAAM,CAAC,SAAS,WAAW;AAAA,IACrD;AAAA,IACA,GAAG;AAAA,EACL;AACA,SAAO,CAAC,SAAS;AACf,QAAI,MAAM;AACV,eAAW,CAAC,SAAS,WAAW,KAAK,KAAK;AACxC,YAAM,IAAI,QAAQ,SAAS,WAAW;AAAA,IACxC;AACA,WAAO;AAAA,EACT;AACF;AAOO,SAAS,sBAAsB,SAAsC;AAC1E,SAAO,QAAQ,IAAI,CAAC,WAAW;AAC7B,QAAI;AACF,aAAO,IAAI,OAAO,QAAQ,GAAG;AAAA,IAC/B,SAAS,KAAK;AACZ,YAAM,IAAI;AAAA,QACR,wCAAwC,KAAK,UAAU,MAAM,CAAC,uCAC5D,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CACjD;AAAA,MACF;AAAA,IACF;AAAA,EACF,CAAC;AACH;;;ACzIO,IAAM,mBAAmB;AAGzB,IAAM,sBAAsB;","names":[]}
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/tracevals/graders/util.ts
|
|
4
|
+
function finding(plan, message) {
|
|
5
|
+
return {
|
|
6
|
+
evalName: plan.evalName,
|
|
7
|
+
artifact: plan.artifact.path,
|
|
8
|
+
message,
|
|
9
|
+
severity: plan.severity
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
function fail(plan, message) {
|
|
13
|
+
return { findings: [finding(plan, message)] };
|
|
14
|
+
}
|
|
15
|
+
var pass = { findings: [] };
|
|
16
|
+
function optionsError(kind, message) {
|
|
17
|
+
return { findings: [], error: `${kind}: ${message}` };
|
|
18
|
+
}
|
|
19
|
+
function firstError(...checks) {
|
|
20
|
+
return checks.find((check) => check !== void 0);
|
|
21
|
+
}
|
|
22
|
+
function requiredString(options, key) {
|
|
23
|
+
const value = options[key];
|
|
24
|
+
if (typeof value !== "string" || value.length === 0) {
|
|
25
|
+
return `options.${key} is required`;
|
|
26
|
+
}
|
|
27
|
+
return void 0;
|
|
28
|
+
}
|
|
29
|
+
function optionalString(options, key) {
|
|
30
|
+
const value = options[key];
|
|
31
|
+
if (value === void 0) return void 0;
|
|
32
|
+
if (typeof value !== "string" || value.length === 0) {
|
|
33
|
+
return `options.${key} must be a non-empty string`;
|
|
34
|
+
}
|
|
35
|
+
return void 0;
|
|
36
|
+
}
|
|
37
|
+
function optionalEnum(options, key, allowed) {
|
|
38
|
+
const value = options[key];
|
|
39
|
+
if (value === void 0) return void 0;
|
|
40
|
+
if (typeof value !== "string" || !allowed.includes(value)) {
|
|
41
|
+
return `options.${key} must be one of: ${allowed.join(", ")}`;
|
|
42
|
+
}
|
|
43
|
+
return void 0;
|
|
44
|
+
}
|
|
45
|
+
function optionalNumber(options, key, bounds = {}) {
|
|
46
|
+
const value = options[key];
|
|
47
|
+
if (value === void 0) return void 0;
|
|
48
|
+
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
49
|
+
return `options.${key} must be a finite number`;
|
|
50
|
+
}
|
|
51
|
+
if (bounds.integer === true && !Number.isInteger(value)) {
|
|
52
|
+
return `options.${key} must be a whole number`;
|
|
53
|
+
}
|
|
54
|
+
if (bounds.min !== void 0 && value < bounds.min) {
|
|
55
|
+
return `options.${key} must be at least ${bounds.min}`;
|
|
56
|
+
}
|
|
57
|
+
return void 0;
|
|
58
|
+
}
|
|
59
|
+
function optionalBoolean(options, key) {
|
|
60
|
+
if (options[key] !== void 0 && typeof options[key] !== "boolean") {
|
|
61
|
+
return `options.${key} must be a boolean`;
|
|
62
|
+
}
|
|
63
|
+
return void 0;
|
|
64
|
+
}
|
|
65
|
+
function orderedBounds(options, minKey, maxKey) {
|
|
66
|
+
const min = options[minKey];
|
|
67
|
+
const max = options[maxKey];
|
|
68
|
+
if (typeof min === "number" && typeof max === "number" && min > max) {
|
|
69
|
+
return `options.${maxKey} must be greater than or equal to options.${minKey}`;
|
|
70
|
+
}
|
|
71
|
+
return void 0;
|
|
72
|
+
}
|
|
73
|
+
function requireOneOf(options, keys) {
|
|
74
|
+
if (keys.some((key) => options[key] !== void 0)) return void 0;
|
|
75
|
+
return `at least one of ${keys.map((k) => `options.${k}`).join(" or ")} is required`;
|
|
76
|
+
}
|
|
77
|
+
function windowFor(trace, plan) {
|
|
78
|
+
return windowOf(trace, plan.artifact);
|
|
79
|
+
}
|
|
80
|
+
function windowOf(trace, artifact) {
|
|
81
|
+
const { name, type } = artifact;
|
|
82
|
+
if (type === "skill") return skillWindow(trace, name);
|
|
83
|
+
if (type === "agent") return agentWindow(trace, name);
|
|
84
|
+
if (type === "slash-command") return slashCommandWindow(trace, name);
|
|
85
|
+
return {
|
|
86
|
+
scope: "session",
|
|
87
|
+
label: "whole session",
|
|
88
|
+
empty: false,
|
|
89
|
+
events: trace.events,
|
|
90
|
+
toolCalls: trace.toolCalls,
|
|
91
|
+
fileAccesses: trace.fileAccesses,
|
|
92
|
+
skillInvocations: trace.skillInvocations,
|
|
93
|
+
userMessages: trace.userMessages,
|
|
94
|
+
assistantTexts: trace.assistantTexts,
|
|
95
|
+
turnCount: trace.turnCount
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
function skippedWindow(window) {
|
|
99
|
+
return { findings: [], skipped: window.reason ?? "the artifact governed no turns" };
|
|
100
|
+
}
|
|
101
|
+
function skillWindow(trace, name) {
|
|
102
|
+
return injectionWindow(trace, "skill", `skill "${name}"`, (s) => s.name === name);
|
|
103
|
+
}
|
|
104
|
+
function slashCommandWindow(trace, name) {
|
|
105
|
+
return injectionWindow(
|
|
106
|
+
trace,
|
|
107
|
+
"slash-command",
|
|
108
|
+
`slash command "/${name}"`,
|
|
109
|
+
(s) => s.name === name && s.via === "command-injection"
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
function injectionWindow(trace, scope, label, opensHere) {
|
|
113
|
+
const opens = trace.skillInvocations.filter(opensHere).map((s) => s.index);
|
|
114
|
+
if (opens.length === 0) {
|
|
115
|
+
return emptyWindow(
|
|
116
|
+
scope,
|
|
117
|
+
label,
|
|
118
|
+
`${label} was never invoked in this trace, so it governed no turns`
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
const chainOf = (index) => trace.events[index]?.branchId;
|
|
122
|
+
const own = new Set(opens.map(chainOf));
|
|
123
|
+
const boundaries = trace.skillInvocations.filter((s) => own.has(chainOf(s.index))).map((s) => s.index).sort((a, b) => a - b);
|
|
124
|
+
const spans = opens.map((start) => ({
|
|
125
|
+
start,
|
|
126
|
+
end: boundaries.find((b) => b > start) ?? Number.POSITIVE_INFINITY
|
|
127
|
+
}));
|
|
128
|
+
return materialize(trace, scope, label, spans, void 0, own);
|
|
129
|
+
}
|
|
130
|
+
function agentWindow(trace, name) {
|
|
131
|
+
const label = `agent "${name}"`;
|
|
132
|
+
const mine = trace.subagentBranches.filter((b) => b.agentType === name);
|
|
133
|
+
if (mine.length === 0) {
|
|
134
|
+
const spawned = trace.agentSpawns.some((a) => a.subagentType === name);
|
|
135
|
+
return emptyWindow(
|
|
136
|
+
"agent",
|
|
137
|
+
label,
|
|
138
|
+
spawned ? `${label} was spawned but recorded no subagent turns (no inline sidechain records and no sidecar transcript), so it governed no turns` : `${label} was never spawned in this trace, so it governed no turns`
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
const branchIds = new Set(mine.map((b) => b.branchId));
|
|
142
|
+
for (const branch of trace.subagentBranches) {
|
|
143
|
+
if (branchIds.has(branch.branchId)) continue;
|
|
144
|
+
const nested = mine.some(
|
|
145
|
+
(m) => m.startIndex <= branch.startIndex && branch.endIndex <= m.endIndex
|
|
146
|
+
);
|
|
147
|
+
if (nested) branchIds.add(branch.branchId);
|
|
148
|
+
}
|
|
149
|
+
const spans = mine.map((b) => ({ start: b.startIndex, end: b.endIndex }));
|
|
150
|
+
return materialize(
|
|
151
|
+
trace,
|
|
152
|
+
"agent",
|
|
153
|
+
label,
|
|
154
|
+
spans,
|
|
155
|
+
branchIds,
|
|
156
|
+
new Set(mine.map((b) => b.branchId))
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
function emptyWindow(scope, label, reason) {
|
|
160
|
+
return {
|
|
161
|
+
scope,
|
|
162
|
+
label,
|
|
163
|
+
empty: true,
|
|
164
|
+
reason,
|
|
165
|
+
events: [],
|
|
166
|
+
toolCalls: [],
|
|
167
|
+
fileAccesses: [],
|
|
168
|
+
skillInvocations: [],
|
|
169
|
+
userMessages: [],
|
|
170
|
+
assistantTexts: [],
|
|
171
|
+
turnCount: 0
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
function materialize(trace, scope, label, spans, branchIds, own) {
|
|
175
|
+
const inSpan = (index) => spans.some((s) => index >= s.start && index < s.end);
|
|
176
|
+
const excluded = /* @__PURE__ */ new Set();
|
|
177
|
+
if (branchIds !== void 0) {
|
|
178
|
+
for (const event of trace.events) {
|
|
179
|
+
if (!inSpan(event.index)) continue;
|
|
180
|
+
if (event.branchId === void 0 || !branchIds.has(event.branchId)) {
|
|
181
|
+
excluded.add(event.index);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
const inWindow = (index) => inSpan(index) && !excluded.has(index);
|
|
186
|
+
const events = trace.events.filter((e) => inWindow(e.index));
|
|
187
|
+
const textOf = (kind) => events.filter((e) => e.kind === kind && own.has(e.branchId) && e.text).map((e) => e.text);
|
|
188
|
+
const userMessages = textOf("user");
|
|
189
|
+
return {
|
|
190
|
+
scope,
|
|
191
|
+
label,
|
|
192
|
+
empty: false,
|
|
193
|
+
events,
|
|
194
|
+
toolCalls: trace.toolCalls.filter((c) => inWindow(c.index)),
|
|
195
|
+
fileAccesses: trace.fileAccesses.filter((a) => inWindow(a.index)),
|
|
196
|
+
skillInvocations: trace.skillInvocations.filter((s) => inWindow(s.index)),
|
|
197
|
+
userMessages,
|
|
198
|
+
assistantTexts: textOf("assistant"),
|
|
199
|
+
turnCount: userMessages.length
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
export {
|
|
204
|
+
finding,
|
|
205
|
+
fail,
|
|
206
|
+
pass,
|
|
207
|
+
optionsError,
|
|
208
|
+
firstError,
|
|
209
|
+
requiredString,
|
|
210
|
+
optionalString,
|
|
211
|
+
optionalEnum,
|
|
212
|
+
optionalNumber,
|
|
213
|
+
optionalBoolean,
|
|
214
|
+
orderedBounds,
|
|
215
|
+
requireOneOf,
|
|
216
|
+
windowFor,
|
|
217
|
+
windowOf,
|
|
218
|
+
skippedWindow,
|
|
219
|
+
emptyWindow,
|
|
220
|
+
materialize
|
|
221
|
+
};
|
|
222
|
+
//# sourceMappingURL=chunk-CWXZ2MRT.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/tracevals/graders/util.ts"],"sourcesContent":["/** Shared helpers for deterministic graders. */\nimport type { EvalPlan } from \"../core/plan.js\";\nimport type {\n FileAccess,\n SkillInvocation,\n ToolCall,\n Trace,\n TraceEvent,\n} from \"../trace/types.js\";\nimport type { Finding, GradeResult } from \"./types.js\";\n\nexport function finding(plan: EvalPlan, message: string): Finding {\n return {\n evalName: plan.evalName,\n artifact: plan.artifact.path,\n message,\n severity: plan.severity,\n };\n}\n\nexport function fail(plan: EvalPlan, message: string): GradeResult {\n return { findings: [finding(plan, message)] };\n}\n\nexport const pass: GradeResult = { findings: [] };\n\nexport function optionsError(kind: string, message: string): GradeResult {\n return { findings: [], error: `${kind}: ${message}` };\n}\n\n/** An option-validation outcome: a message when invalid, undefined when fine. */\nexport type OptionCheck = string | undefined;\n\nexport type Options = Record<string, unknown>;\n\n/** First failing check, so callers read as a flat list of constraints. */\nexport function firstError(...checks: OptionCheck[]): OptionCheck {\n return checks.find((check) => check !== undefined);\n}\n\nexport function requiredString(options: Options, key: string): OptionCheck {\n const value = options[key];\n if (typeof value !== \"string\" || value.length === 0) {\n return `options.${key} is required`;\n }\n return undefined;\n}\n\nexport function optionalString(options: Options, key: string): OptionCheck {\n const value = options[key];\n if (value === undefined) return undefined;\n if (typeof value !== \"string\" || value.length === 0) {\n return `options.${key} must be a non-empty string`;\n }\n return undefined;\n}\n\nexport function optionalEnum(\n options: Options,\n key: string,\n allowed: readonly string[],\n): OptionCheck {\n const value = options[key];\n if (value === undefined) return undefined;\n if (typeof value !== \"string\" || !allowed.includes(value)) {\n return `options.${key} must be one of: ${allowed.join(\", \")}`;\n }\n return undefined;\n}\n\nexport function optionalNumber(\n options: Options,\n key: string,\n bounds: { min?: number; integer?: boolean } = {},\n): OptionCheck {\n const value = options[key];\n if (value === undefined) return undefined;\n // Number.isFinite also rejects NaN and both infinities.\n if (typeof value !== \"number\" || !Number.isFinite(value)) {\n return `options.${key} must be a finite number`;\n }\n if (bounds.integer === true && !Number.isInteger(value)) {\n return `options.${key} must be a whole number`;\n }\n if (bounds.min !== undefined && value < bounds.min) {\n return `options.${key} must be at least ${bounds.min}`;\n }\n return undefined;\n}\n\nexport function optionalBoolean(options: Options, key: string): OptionCheck {\n if (options[key] !== undefined && typeof options[key] !== \"boolean\") {\n return `options.${key} must be a boolean`;\n }\n return undefined;\n}\n\n/** Rejects min > max once both are present and already known to be numbers. */\nexport function orderedBounds(\n options: Options,\n minKey: string,\n maxKey: string,\n): OptionCheck {\n const min = options[minKey];\n const max = options[maxKey];\n if (typeof min === \"number\" && typeof max === \"number\" && min > max) {\n return `options.${maxKey} must be greater than or equal to options.${minKey}`;\n }\n return undefined;\n}\n\n/**\n * Rejects a criterion that configures no bound at all. Such a criterion can\n * never fail, so it reads as coverage while asserting nothing.\n */\nexport function requireOneOf(options: Options, keys: string[]): OptionCheck {\n if (keys.some((key) => options[key] !== undefined)) return undefined;\n return `at least one of ${keys.map((k) => `options.${k}`).join(\" or \")} is required`;\n}\n\n// ── Scoped grading ───────────────────────────────────────────────\n\n/**\n * The slice of a trace an artifact was actually governing (ADR 01015). The\n * window is derived from the artifact's *type*, never declared: a skill governs\n * from its invocation until the next skill takes over, an agent governs its own\n * branch, and project rules govern the whole session.\n */\nexport interface TraceWindow {\n /**\n * Which rule produced this window. `turn` is the last turn of a session or\n * a subagent's whole run, cut by `rules/turn.ts` (proposal 0079).\n */\n scope: \"session\" | \"skill\" | \"agent\" | \"slash-command\" | \"turn\";\n /** Names the window in reasons and in the judge digest. */\n label: string;\n /**\n * True when the artifact governed no turn at all. Graders must report\n * `skipped` on an empty window — never a pass, which would be a verdict\n * about turns the artifact had no part in.\n */\n empty: boolean;\n /** Why the window is empty; set only when `empty`. */\n reason?: string;\n events: TraceEvent[];\n toolCalls: ToolCall[];\n fileAccesses: FileAccess[];\n skillInvocations: SkillInvocation[];\n /** Prompts on the window's own chain; nested subagent turns are excluded. */\n userMessages: string[];\n assistantTexts: string[];\n turnCount: number;\n}\n\n/** Half-open ordinal range over `trace.events`; `end` may be Infinity. */\nexport interface Span {\n start: number;\n end: number;\n}\n\nexport function windowFor(trace: Trace, plan: EvalPlan): TraceWindow {\n return windowOf(trace, plan.artifact);\n}\n\n/** `windowFor` keyed on the artifact alone, for callers with no eval plan. */\nexport function windowOf(\n trace: Trace,\n artifact: Pick<EvalPlan[\"artifact\"], \"name\" | \"type\">,\n): TraceWindow {\n const { name, type } = artifact;\n if (type === \"skill\") return skillWindow(trace, name);\n if (type === \"agent\") return agentWindow(trace, name);\n if (type === \"slash-command\") return slashCommandWindow(trace, name);\n // Project rules govern everything, so the window is the trace itself —\n // handed back by reference, so a session-scoped eval grades exactly what it\n // graded before scoping existed.\n return {\n scope: \"session\",\n label: \"whole session\",\n empty: false,\n events: trace.events,\n toolCalls: trace.toolCalls,\n fileAccesses: trace.fileAccesses,\n skillInvocations: trace.skillInvocations,\n userMessages: trace.userMessages,\n assistantTexts: trace.assistantTexts,\n turnCount: trace.turnCount,\n };\n}\n\n/** A grader's response to an empty window: a stated skip, never a verdict. */\nexport function skippedWindow(window: TraceWindow): GradeResult {\n return { findings: [], skipped: window.reason ?? \"the artifact governed no turns\" };\n}\n\nfunction skillWindow(trace: Trace, name: string): TraceWindow {\n // A skill loads either way: through the `Skill` tool, or by someone typing\n // its slash form.\n return injectionWindow(trace, \"skill\", `skill \"${name}\"`, (s) => s.name === name);\n}\n\n/**\n * A slash command's window is a skill's window, because the mechanism is the\n * same one: an instruction set injected at a point in the session, in force\n * until the next injection takes over (ADR 01023). Only the opening differs —\n * a `Skill` tool call is not the slash-command mechanism, so it can close a\n * command's window but never open it.\n */\nfunction slashCommandWindow(trace: Trace, name: string): TraceWindow {\n return injectionWindow(\n trace,\n \"slash-command\",\n `slash command \"/${name}\"`,\n (s) => s.name === name && s.via === \"command-injection\",\n );\n}\n\n/**\n * The window an injected instruction set governed: from each of its own\n * injections until the next injection of any kind — including another of its\n * own, which simply reopens the window — or to the end of the session.\n */\nfunction injectionWindow(\n trace: Trace,\n scope: \"skill\" | \"slash-command\",\n label: string,\n opensHere: (invocation: SkillInvocation) => boolean,\n): TraceWindow {\n const opens = trace.skillInvocations.filter(opensHere).map((s) => s.index);\n if (opens.length === 0) {\n return emptyWindow(\n scope,\n label,\n `${label} was never invoked in this trace, so it governed no turns`,\n );\n }\n // An instruction set injected inside a subagent branch governs that branch's\n // chain; one injected on the main chain governs the main chain.\n const chainOf = (index: number): string | undefined =>\n trace.events[index]?.branchId;\n const own = new Set<string | undefined>(opens.map(chainOf));\n // **Boundaries are drawn from the opening chain only** (ADR 01015: the next\n // invocation *on the same chain*). Drawn from every invocation, a subagent\n // that loaded a skill of its own closed the parent's window at the spawn —\n // so a `not-used` eval passed on turns it never saw, and the judge digest\n // stopped where the subagent started.\n const boundaries = trace.skillInvocations\n .filter((s) => own.has(chainOf(s.index)))\n .map((s) => s.index)\n .sort((a, b) => a - b);\n const spans = opens.map((start) => ({\n start,\n end: boundaries.find((b) => b > start) ?? Number.POSITIVE_INFINITY,\n }));\n return materialize(trace, scope, label, spans, undefined, own);\n}\n\nfunction agentWindow(trace: Trace, name: string): TraceWindow {\n const label = `agent \"${name}\"`;\n const mine = trace.subagentBranches.filter((b) => b.agentType === name);\n if (mine.length === 0) {\n const spawned = trace.agentSpawns.some((a) => a.subagentType === name);\n return emptyWindow(\n \"agent\",\n label,\n spawned\n ? `${label} was spawned but recorded no subagent turns (no inline ` +\n `sidechain records and no sidecar transcript), so it governed no turns`\n : `${label} was never spawned in this trace, so it governed no turns`,\n );\n }\n // A branch's span already covers everything nested under it (ADR 01014), so\n // one containment pass reaches subagents at any depth.\n const branchIds = new Set(mine.map((b) => b.branchId));\n for (const branch of trace.subagentBranches) {\n if (branchIds.has(branch.branchId)) continue;\n const nested = mine.some(\n (m) => m.startIndex <= branch.startIndex && branch.endIndex <= m.endIndex,\n );\n if (nested) branchIds.add(branch.branchId);\n }\n const spans = mine.map((b) => ({ start: b.startIndex, end: b.endIndex }));\n return materialize(\n trace,\n \"agent\",\n label,\n spans,\n branchIds,\n new Set<string | undefined>(mine.map((b) => b.branchId)),\n );\n}\n\nexport function emptyWindow(\n scope: TraceWindow[\"scope\"],\n label: string,\n reason: string,\n): TraceWindow {\n return {\n scope,\n label,\n empty: true,\n reason,\n events: [],\n toolCalls: [],\n fileAccesses: [],\n skillInvocations: [],\n userMessages: [],\n assistantTexts: [],\n turnCount: 0,\n };\n}\n\n/**\n * Cut every derived list to the spans. `branchIds`, when given, additionally\n * drops events that fall inside an *inline* branch's bounding range without\n * belonging to it — a sidecar branch is contiguous and loses nothing here, but\n * an inline one can enclose interleaved main-chain turns (ADR 01014).\n *\n * `own` names the chain the window is anchored to. Prompts and assistant text\n * are taken from that chain alone, so a subagent spawned inside the window\n * contributes tool calls and file accesses but not turns.\n */\nexport function materialize(\n trace: Trace,\n scope: TraceWindow[\"scope\"],\n label: string,\n spans: Span[],\n branchIds: Set<string> | undefined,\n own: Set<string | undefined>,\n): TraceWindow {\n const inSpan = (index: number): boolean =>\n spans.some((s) => index >= s.start && index < s.end);\n const excluded = new Set<number>();\n if (branchIds !== undefined) {\n for (const event of trace.events) {\n if (!inSpan(event.index)) continue;\n if (event.branchId === undefined || !branchIds.has(event.branchId)) {\n excluded.add(event.index);\n }\n }\n }\n const inWindow = (index: number): boolean =>\n inSpan(index) && !excluded.has(index);\n\n const events = trace.events.filter((e) => inWindow(e.index));\n const textOf = (kind: TraceEvent[\"kind\"]): string[] =>\n events\n .filter((e) => e.kind === kind && own.has(e.branchId) && e.text)\n .map((e) => e.text as string);\n const userMessages = textOf(\"user\");\n\n return {\n scope,\n label,\n empty: false,\n events,\n toolCalls: trace.toolCalls.filter((c) => inWindow(c.index)),\n fileAccesses: trace.fileAccesses.filter((a) => inWindow(a.index)),\n skillInvocations: trace.skillInvocations.filter((s) => inWindow(s.index)),\n userMessages,\n assistantTexts: textOf(\"assistant\"),\n turnCount: userMessages.length,\n };\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
palette
|
|
4
|
+
} from "./chunk-RNVYGWIC.js";
|
|
5
|
+
|
|
6
|
+
// src/tracevals/rules/local.ts
|
|
7
|
+
import {
|
|
8
|
+
ensureModel,
|
|
9
|
+
fits,
|
|
10
|
+
modelState,
|
|
11
|
+
nodeLlamaCppStatus
|
|
12
|
+
} from "@hawkeyexl/inference";
|
|
13
|
+
function libraryLocalModels(connections) {
|
|
14
|
+
const modelsDir = connections["llama-cpp"]?.modelsDir;
|
|
15
|
+
const options = modelsDir === void 0 ? {} : { modelsDirectory: modelsDir };
|
|
16
|
+
return {
|
|
17
|
+
runtimePresent: async () => (await nodeLlamaCppStatus()).state === "present",
|
|
18
|
+
state: (model) => modelState(model, options),
|
|
19
|
+
fits: (model) => fits(model, options),
|
|
20
|
+
ensure: (model) => ensureModel(model, options)
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
var GB = 1024 ** 3;
|
|
24
|
+
|
|
25
|
+
// src/tracevals/reporters/conformance.ts
|
|
26
|
+
var count = (n, one, many) => `${String(n)} ${n === 1 ? one : many}`;
|
|
27
|
+
function renderCheck(report, opts = {}) {
|
|
28
|
+
const pc = palette(opts.color === true);
|
|
29
|
+
const lines = [];
|
|
30
|
+
for (const source of report.sources) {
|
|
31
|
+
const findings = report.findings.filter((f) => f.source === source.path);
|
|
32
|
+
if (findings.length === 0) continue;
|
|
33
|
+
lines.push(source.path);
|
|
34
|
+
for (const f of findings) {
|
|
35
|
+
const mark = f.outcome !== "fail" ? pc.yellow("?") : f.severity === "error" ? pc.red("\u2716") : pc.yellow("!");
|
|
36
|
+
lines.push(` ${mark} ${f.rule} ${f.text}`);
|
|
37
|
+
lines.push(` ${pc.dim(`${f.observed} (${f.confidence.toFixed(2)})`)}`);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
lines.push(...sessionSoFar(report));
|
|
41
|
+
for (const warning of report.warnings) lines.push(`${pc.yellow("!")} ${warning}`);
|
|
42
|
+
if (lines.length > 0) lines.push("");
|
|
43
|
+
lines.push(closing(report));
|
|
44
|
+
return lines.join("\n");
|
|
45
|
+
}
|
|
46
|
+
function sessionSoFar(report) {
|
|
47
|
+
const rules = report.session?.rules ?? [];
|
|
48
|
+
if (rules.length === 0) return [];
|
|
49
|
+
const flagged = rules.filter((r) => r.broken > 0 || r.needsReview > 0);
|
|
50
|
+
const lines = flagged.map((r) => {
|
|
51
|
+
const counts = [
|
|
52
|
+
["broken", r.broken],
|
|
53
|
+
["repaired", r.repaired],
|
|
54
|
+
["needs review", r.needsReview],
|
|
55
|
+
["followed", r.followed]
|
|
56
|
+
];
|
|
57
|
+
const said = counts.filter(([, n]) => n > 0).map(([word, n]) => `${word} ${String(n)}`);
|
|
58
|
+
return ` ${r.source}#${r.rule} ${said.join(", ")}`;
|
|
59
|
+
});
|
|
60
|
+
const held = rules.length - flagged.length;
|
|
61
|
+
if (held > 0) {
|
|
62
|
+
lines.push(` ${count(held, "rule", "rules")} held every turn ${held === 1 ? "it" : "they"} applied to.`);
|
|
63
|
+
}
|
|
64
|
+
return ["Session so far", ...lines];
|
|
65
|
+
}
|
|
66
|
+
function closing(report) {
|
|
67
|
+
const head = `Last turn of ${report.sessionId.slice(0, 8)}:`;
|
|
68
|
+
const { summary } = report;
|
|
69
|
+
const scope = `${count(summary.rules, "rule", "rules")} from ${count(summary.sources, "source", "sources")}`;
|
|
70
|
+
switch (report.skipped) {
|
|
71
|
+
case "empty-turn":
|
|
72
|
+
return `${head} nothing happened after the last prompt.`;
|
|
73
|
+
case "no-sources":
|
|
74
|
+
return `${head} no rule sources governed it.`;
|
|
75
|
+
case "not-applicable":
|
|
76
|
+
return `${head} ${scope}, none apply to this turn.`;
|
|
77
|
+
case null: {
|
|
78
|
+
const broken = summary.fail > 0 ? `${String(summary.fail)} broken` : "None broken";
|
|
79
|
+
const review = summary.needsReview > 0 ? `, ${String(summary.needsReview)} needs review` : "";
|
|
80
|
+
const reported = summary.reported > 0 ? `, ${String(summary.reported)} reported` : "";
|
|
81
|
+
return `${head} ${scope}. ${broken}${review}${reported}.`;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
var NOT_SET_UP = "tracevals conformance is not set up; nothing to prepare.";
|
|
86
|
+
function renderPrepare(report) {
|
|
87
|
+
const lines = [];
|
|
88
|
+
const said = /* @__PURE__ */ new Set();
|
|
89
|
+
for (const m of report.models) {
|
|
90
|
+
if (m.state === "hosted" || said.has(m.model)) continue;
|
|
91
|
+
said.add(m.model);
|
|
92
|
+
const name = `${m.model} (${m.provider})`;
|
|
93
|
+
lines.push(
|
|
94
|
+
m.state === "downloaded" ? `${name} downloaded, ${((m.bytes ?? 0) / GB).toFixed(2)} GB.` : `${name} is ${m.state}.`
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
const ex = report.extraction;
|
|
98
|
+
if (ex !== null) {
|
|
99
|
+
const files = (n) => `${count(n, "file", "files")} that ${n === 1 ? "applies" : "apply"} to every session`;
|
|
100
|
+
if (ex.extracted > 0) {
|
|
101
|
+
lines.push(
|
|
102
|
+
`Extracted ${count(ex.rules, "rule", "rules")} from ${files(ex.extracted)}.` + (ex.cached > 0 ? ` ${String(ex.cached)} more ${ex.cached === 1 ? "was" : "were"} cached.` : "")
|
|
103
|
+
);
|
|
104
|
+
} else if (ex.cached > 0) {
|
|
105
|
+
lines.push(`${files(ex.cached)} ${ex.cached === 1 ? "is" : "are"} cached. Nothing to extract.`);
|
|
106
|
+
} else {
|
|
107
|
+
lines.push("No file applies to every session. Nothing to extract.");
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
for (const warning of report.warnings) lines.push(warning);
|
|
111
|
+
return lines.join("\n");
|
|
112
|
+
}
|
|
113
|
+
var NO_HOST = "No model host is running.";
|
|
114
|
+
function renderRelease(report) {
|
|
115
|
+
if (!report.hostStopped && report.released.length === 0 && report.unloaded.length === 0) {
|
|
116
|
+
return NO_HOST;
|
|
117
|
+
}
|
|
118
|
+
const unloaded = report.unloaded.length > 0 ? `Unloaded ${report.unloaded.join(", ")}` : "";
|
|
119
|
+
const stopped = report.hostStopped ? "stopped the model host" : "";
|
|
120
|
+
const action = [unloaded, stopped].filter((s) => s !== "").join(" and ");
|
|
121
|
+
const held = count(report.released.length, "session", "sessions");
|
|
122
|
+
return `${action === "" ? "Released the lease" : action}. ${held} held it.`;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export {
|
|
126
|
+
libraryLocalModels,
|
|
127
|
+
GB,
|
|
128
|
+
renderCheck,
|
|
129
|
+
NOT_SET_UP,
|
|
130
|
+
renderPrepare,
|
|
131
|
+
NO_HOST,
|
|
132
|
+
renderRelease
|
|
133
|
+
};
|
|
134
|
+
//# sourceMappingURL=chunk-D2LBN44V.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/tracevals/rules/local.ts","../src/tracevals/reporters/conformance.ts"],"sourcesContent":["/**\n * What tracevals asks of a local model without loading it (proposal 0079,\n * gates 5, 8 and 9, and `prepare`). The inference library owns every answer;\n * this is the one seam, so a test can say \"downloading\" without a runtime.\n */\nimport {\n ensureModel,\n fits,\n modelState,\n nodeLlamaCppStatus,\n type EnsureModelResult,\n type FitsResult,\n type LlamaModelState,\n} from \"@hawkeyexl/inference\";\nimport type { ProviderConnections } from \"../../shared/providers.js\";\n\nexport interface LocalModels {\n /** Whether the llama.cpp binding is importable now, installing nothing. */\n runtimePresent(): Promise<boolean>;\n /** `ready`, `downloading` or `missing`, read without loading or fetching. */\n state(model: string): Promise<LlamaModelState>;\n /** The memory probe `auto` tiering uses. */\n fits(model: string): Promise<FitsResult>;\n /** Fetch the runtime and the weights, without loading either. */\n ensure(model: string): Promise<EnsureModelResult>;\n}\n\n/** The library's lifecycle calls, pointed at the family's models directory. */\nexport function libraryLocalModels(connections: ProviderConnections): LocalModels {\n const modelsDir = connections[\"llama-cpp\"]?.modelsDir;\n const options = modelsDir === undefined ? {} : { modelsDirectory: modelsDir };\n return {\n runtimePresent: async () => (await nodeLlamaCppStatus()).state === \"present\",\n state: (model) => modelState(model, options),\n fits: (model) => fits(model, options),\n ensure: (model) => ensureModel(model, options),\n };\n}\n\n/** Bytes in a gigabyte as the messages count them. */\nexport const GB = 1024 ** 3;\n","/**\n * Pretty output for `check`, `prepare` and `release` (proposal 0079). JSON is\n * each report as it stands, so only prose lives here. Colour is the caller's\n * decision, made once by `colorFor`.\n */\nimport { palette } from \"../../shared/color.js\";\nimport type { CheckReport } from \"../commands/check.js\";\nimport type { PrepareReport } from \"../commands/prepare.js\";\nimport type { ReleaseReport } from \"../commands/release.js\";\nimport { GB } from \"../rules/local.js\";\n\nconst count = (n: number, one: string, many: string): string =>\n `${String(n)} ${n === 1 ? one : many}`;\n\n/** Findings grouped under their source, then the closing line. */\nexport function renderCheck(report: CheckReport, opts: { color?: boolean } = {}): string {\n const pc = palette(opts.color === true);\n const lines: string[] = [];\n for (const source of report.sources) {\n const findings = report.findings.filter((f) => f.source === source.path);\n if (findings.length === 0) continue;\n lines.push(source.path);\n for (const f of findings) {\n const mark =\n f.outcome !== \"fail\" ? pc.yellow(\"?\") : f.severity === \"error\" ? pc.red(\"✖\") : pc.yellow(\"!\");\n lines.push(` ${mark} ${f.rule} ${f.text}`);\n lines.push(` ${pc.dim(`${f.observed} (${f.confidence.toFixed(2)})`)}`);\n }\n }\n lines.push(...sessionSoFar(report));\n for (const warning of report.warnings) lines.push(`${pc.yellow(\"!\")} ${warning}`);\n if (lines.length > 0) lines.push(\"\");\n lines.push(closing(report));\n return lines.join(\"\\n\");\n}\n\n/** The ledger's rules that were ever broken or sent to review, and a count of the rest. */\nfunction sessionSoFar(report: CheckReport): string[] {\n const rules = report.session?.rules ?? [];\n if (rules.length === 0) return [];\n const flagged = rules.filter((r) => r.broken > 0 || r.needsReview > 0);\n const lines = flagged.map((r) => {\n const counts = [\n [\"broken\", r.broken],\n [\"repaired\", r.repaired],\n [\"needs review\", r.needsReview],\n [\"followed\", r.followed],\n ] as const;\n const said = counts.filter(([, n]) => n > 0).map(([word, n]) => `${word} ${String(n)}`);\n return ` ${r.source}#${r.rule} ${said.join(\", \")}`;\n });\n const held = rules.length - flagged.length;\n if (held > 0) {\n lines.push(` ${count(held, \"rule\", \"rules\")} held every turn ${held === 1 ? \"it\" : \"they\"} applied to.`);\n }\n return [\"Session so far\", ...lines];\n}\n\nfunction closing(report: CheckReport): string {\n const head = `Last turn of ${report.sessionId.slice(0, 8)}:`;\n const { summary } = report;\n // \"sources\", because a typed prompt is not a file (proposal 0080).\n const scope = `${count(summary.rules, \"rule\", \"rules\")} from ${count(summary.sources, \"source\", \"sources\")}`;\n switch (report.skipped) {\n case \"empty-turn\":\n return `${head} nothing happened after the last prompt.`;\n case \"no-sources\":\n return `${head} no rule sources governed it.`;\n case \"not-applicable\":\n return `${head} ${scope}, none apply to this turn.`;\n case null: {\n const broken = summary.fail > 0 ? `${String(summary.fail)} broken` : \"None broken\";\n const review = summary.needsReview > 0 ? `, ${String(summary.needsReview)} needs review` : \"\";\n const reported = summary.reported > 0 ? `, ${String(summary.reported)} reported` : \"\";\n return `${head} ${scope}. ${broken}${review}${reported}.`;\n }\n }\n}\n\nexport const NOT_SET_UP = \"tracevals conformance is not set up; nothing to prepare.\";\n\nexport function renderPrepare(report: PrepareReport): string {\n const lines: string[] = [];\n const said = new Set<string>();\n for (const m of report.models) {\n if (m.state === \"hosted\" || said.has(m.model)) continue;\n said.add(m.model);\n const name = `${m.model} (${m.provider})`;\n lines.push(\n m.state === \"downloaded\"\n ? `${name} downloaded, ${((m.bytes ?? 0) / GB).toFixed(2)} GB.`\n : `${name} is ${m.state}.`,\n );\n }\n const ex = report.extraction;\n if (ex !== null) {\n const files = (n: number): string =>\n `${count(n, \"file\", \"files\")} that ${n === 1 ? \"applies\" : \"apply\"} to every session`;\n if (ex.extracted > 0) {\n lines.push(\n `Extracted ${count(ex.rules, \"rule\", \"rules\")} from ${files(ex.extracted)}.` +\n (ex.cached > 0 ? ` ${String(ex.cached)} more ${ex.cached === 1 ? \"was\" : \"were\"} cached.` : \"\"),\n );\n } else if (ex.cached > 0) {\n lines.push(`${files(ex.cached)} ${ex.cached === 1 ? \"is\" : \"are\"} cached. Nothing to extract.`);\n } else {\n lines.push(\"No file applies to every session. Nothing to extract.\");\n }\n }\n for (const warning of report.warnings) lines.push(warning);\n return lines.join(\"\\n\");\n}\n\nexport const NO_HOST = \"No model host is running.\";\n\nexport function renderRelease(report: ReleaseReport): string {\n if (!report.hostStopped && report.released.length === 0 && report.unloaded.length === 0) {\n return NO_HOST;\n }\n const unloaded = report.unloaded.length > 0 ? `Unloaded ${report.unloaded.join(\", \")}` : \"\";\n const stopped = report.hostStopped ? \"stopped the model host\" : \"\";\n const action = [unloaded, stopped].filter((s) => s !== \"\").join(\" and \");\n const held = count(report.released.length, \"session\", \"sessions\");\n return `${action === \"\" ? \"Released the lease\" : action}. ${held} held it.`;\n}\n"],"mappings":"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","names":[]}
|