@hawkeyexl/manni 4.3.0 → 4.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (224) hide show
  1. package/README.md +9 -1
  2. package/dist/batch-DFRCLY77.js +36 -0
  3. package/dist/build-Q5C6SX5J.js +31 -0
  4. package/dist/calibrate-XQILKXS3.js +31 -0
  5. package/dist/capture-JQMOC3BW.js +32 -0
  6. package/dist/check-HYNGXYJV.js +18 -0
  7. package/dist/check-HYNGXYJV.js.map +1 -0
  8. package/dist/check-NFZQLJKN.js +27 -0
  9. package/dist/check-NFZQLJKN.js.map +1 -0
  10. package/dist/check-U54XV6BP.js +40 -0
  11. package/dist/check-U54XV6BP.js.map +1 -0
  12. package/dist/chunk-252LNOEG.js +622 -0
  13. package/dist/chunk-252LNOEG.js.map +1 -0
  14. package/dist/chunk-2H7UOFLK.js +11 -0
  15. package/dist/chunk-2H7UOFLK.js.map +1 -0
  16. package/dist/chunk-2U3ORT6D.js +230 -0
  17. package/dist/chunk-2U3ORT6D.js.map +1 -0
  18. package/dist/chunk-3AFUD75K.js +23 -0
  19. package/dist/chunk-3AFUD75K.js.map +1 -0
  20. package/dist/{chunk-3R23C5DT.js → chunk-4TSWU2SU.js} +2 -14
  21. package/dist/chunk-4TSWU2SU.js.map +1 -0
  22. package/dist/chunk-5KHIFVD2.js +1192 -0
  23. package/dist/chunk-5KHIFVD2.js.map +1 -0
  24. package/dist/chunk-5YXBLLAC.js +2165 -0
  25. package/dist/chunk-5YXBLLAC.js.map +1 -0
  26. package/dist/chunk-644JVKJ4.js +3872 -0
  27. package/dist/chunk-644JVKJ4.js.map +1 -0
  28. package/dist/chunk-6OYEI2VV.js +88 -0
  29. package/dist/chunk-6OYEI2VV.js.map +1 -0
  30. package/dist/chunk-77AINBYM.js +19 -0
  31. package/dist/chunk-77AINBYM.js.map +1 -0
  32. package/dist/chunk-77HX34KD.js +1287 -0
  33. package/dist/chunk-77HX34KD.js.map +1 -0
  34. package/dist/{chunk-MOQXWXSG.js → chunk-7QMCHA4N.js} +32 -240
  35. package/dist/chunk-7QMCHA4N.js.map +1 -0
  36. package/dist/chunk-BUXDLRDT.js +455 -0
  37. package/dist/chunk-BUXDLRDT.js.map +1 -0
  38. package/dist/chunk-C6OH6CNF.js +388 -0
  39. package/dist/chunk-C6OH6CNF.js.map +1 -0
  40. package/dist/chunk-CK7SKVHR.js +1042 -0
  41. package/dist/chunk-CK7SKVHR.js.map +1 -0
  42. package/dist/chunk-CYWHFSRT.js +6737 -0
  43. package/dist/chunk-CYWHFSRT.js.map +1 -0
  44. package/dist/chunk-ERMSO34F.js +17 -0
  45. package/dist/chunk-ERMSO34F.js.map +1 -0
  46. package/dist/{chunk-IOYXPBCL.js → chunk-G3T47CFV.js} +1 -7
  47. package/dist/{chunk-IOYXPBCL.js.map → chunk-G3T47CFV.js.map} +1 -1
  48. package/dist/chunk-G7NJSREW.js +979 -0
  49. package/dist/chunk-G7NJSREW.js.map +1 -0
  50. package/dist/chunk-GLP5PSXB.js +72 -0
  51. package/dist/chunk-GLP5PSXB.js.map +1 -0
  52. package/dist/chunk-H2DLH2JI.js +198 -0
  53. package/dist/chunk-H2DLH2JI.js.map +1 -0
  54. package/dist/chunk-H6FZ66XR.js +634 -0
  55. package/dist/chunk-H6FZ66XR.js.map +1 -0
  56. package/dist/{chunk-CI2GOU3A.js → chunk-IEQHSCZG.js} +56 -137
  57. package/dist/chunk-IEQHSCZG.js.map +1 -0
  58. package/dist/chunk-IKEWBMCN.js +98 -0
  59. package/dist/chunk-IKEWBMCN.js.map +1 -0
  60. package/dist/chunk-JOHKIAVH.js +5440 -0
  61. package/dist/chunk-JOHKIAVH.js.map +1 -0
  62. package/dist/chunk-JVTNMVYU.js +10921 -0
  63. package/dist/chunk-JVTNMVYU.js.map +1 -0
  64. package/dist/chunk-K3D6FPV6.js +164 -0
  65. package/dist/chunk-K3D6FPV6.js.map +1 -0
  66. package/dist/{chunk-MFDD3YGU.js → chunk-KCGV52AM.js} +69 -48
  67. package/dist/{chunk-MFDD3YGU.js.map → chunk-KCGV52AM.js.map} +1 -1
  68. package/dist/chunk-LAKAWRV5.js +1651 -0
  69. package/dist/chunk-LAKAWRV5.js.map +1 -0
  70. package/dist/chunk-LJ2RDH3F.js +49 -0
  71. package/dist/chunk-LJ2RDH3F.js.map +1 -0
  72. package/dist/chunk-M2JCK5F6.js +805 -0
  73. package/dist/chunk-M2JCK5F6.js.map +1 -0
  74. package/dist/chunk-MNH7QO4Z.js +15 -0
  75. package/dist/chunk-MNH7QO4Z.js.map +1 -0
  76. package/dist/{chunk-RVVCA4WL.js → chunk-MYC3O5EJ.js} +4 -4
  77. package/dist/chunk-MYMBCTDE.js +387 -0
  78. package/dist/chunk-MYMBCTDE.js.map +1 -0
  79. package/dist/chunk-NCPWXHDO.js +683 -0
  80. package/dist/chunk-NCPWXHDO.js.map +1 -0
  81. package/dist/chunk-NWZ2SLYL.js +2957 -0
  82. package/dist/chunk-NWZ2SLYL.js.map +1 -0
  83. package/dist/chunk-OBFENF3U.js +934 -0
  84. package/dist/chunk-OBFENF3U.js.map +1 -0
  85. package/dist/chunk-OWBRDNRZ.js +16 -0
  86. package/dist/chunk-OWBRDNRZ.js.map +1 -0
  87. package/dist/{chunk-KTSJ2ZHA.js → chunk-PAU352SV.js} +5 -3
  88. package/dist/{chunk-KTSJ2ZHA.js.map → chunk-PAU352SV.js.map} +1 -1
  89. package/dist/chunk-PCE7QHXJ.js +68 -0
  90. package/dist/chunk-PCE7QHXJ.js.map +1 -0
  91. package/dist/chunk-PCHZY2I7.js +299 -0
  92. package/dist/chunk-PCHZY2I7.js.map +1 -0
  93. package/dist/chunk-PHF3D7ER.js +1042 -0
  94. package/dist/chunk-PHF3D7ER.js.map +1 -0
  95. package/dist/chunk-Q35YIDBV.js +1202 -0
  96. package/dist/chunk-Q35YIDBV.js.map +1 -0
  97. package/dist/chunk-RLR4TKUZ.js +4398 -0
  98. package/dist/chunk-RLR4TKUZ.js.map +1 -0
  99. package/dist/chunk-RNVYGWIC.js +30 -0
  100. package/dist/chunk-RNVYGWIC.js.map +1 -0
  101. package/dist/chunk-SE4JN5XN.js +19 -0
  102. package/dist/chunk-SE4JN5XN.js.map +1 -0
  103. package/dist/chunk-TIUTIOFB.js +25 -0
  104. package/dist/chunk-TIUTIOFB.js.map +1 -0
  105. package/dist/chunk-TU2WUBVZ.js +2 -0
  106. package/dist/chunk-TU2WUBVZ.js.map +1 -0
  107. package/dist/chunk-TZOYZK2E.js +98 -0
  108. package/dist/chunk-TZOYZK2E.js.map +1 -0
  109. package/dist/chunk-UKZNLVJW.js +17 -0
  110. package/dist/chunk-UKZNLVJW.js.map +1 -0
  111. package/dist/chunk-UZC7LDAQ.js +178 -0
  112. package/dist/chunk-UZC7LDAQ.js.map +1 -0
  113. package/dist/chunk-V46OILVC.js +3090 -0
  114. package/dist/chunk-V46OILVC.js.map +1 -0
  115. package/dist/chunk-WQE54PNH.js +1975 -0
  116. package/dist/chunk-WQE54PNH.js.map +1 -0
  117. package/dist/chunk-X3BPBDGQ.js +52 -0
  118. package/dist/chunk-X3BPBDGQ.js.map +1 -0
  119. package/dist/chunk-XSLV76YK.js +54 -0
  120. package/dist/chunk-XSLV76YK.js.map +1 -0
  121. package/dist/{chunk-QROCFW5S.js → chunk-XYLUCIKQ.js} +3 -3
  122. package/dist/chunk-YEUYKR62.js +72 -0
  123. package/dist/chunk-YEUYKR62.js.map +1 -0
  124. package/dist/{chunk-ESJ5W7EV.js → chunk-YFQBFNQS.js} +8 -94
  125. package/dist/chunk-YFQBFNQS.js.map +1 -0
  126. package/dist/chunk-YIL4XLLJ.js +1661 -0
  127. package/dist/chunk-YIL4XLLJ.js.map +1 -0
  128. package/dist/chunk-YNHZHSSM.js +41 -0
  129. package/dist/chunk-YNHZHSSM.js.map +1 -0
  130. package/dist/chunk-YTCZRLTU.js +96 -0
  131. package/dist/chunk-YTCZRLTU.js.map +1 -0
  132. package/dist/chunk-YYBFZLAJ.js +374 -0
  133. package/dist/chunk-YYBFZLAJ.js.map +1 -0
  134. package/dist/chunk-ZXRAE55H.js +31 -0
  135. package/dist/chunk-ZXRAE55H.js.map +1 -0
  136. package/dist/cli-2QPR2OZG.js +232 -0
  137. package/dist/cli-2QPR2OZG.js.map +1 -0
  138. package/dist/cli-4IOPGZHR.js +1203 -0
  139. package/dist/cli-4IOPGZHR.js.map +1 -0
  140. package/dist/cli-5FL73WHA.js +346 -0
  141. package/dist/cli-5FL73WHA.js.map +1 -0
  142. package/dist/cli-C262QOWM.js +739 -0
  143. package/dist/cli-C262QOWM.js.map +1 -0
  144. package/dist/cli-LJLLIQB2.js +423 -0
  145. package/dist/cli-LJLLIQB2.js.map +1 -0
  146. package/dist/cli-OFPZ5MAS.js +626 -0
  147. package/dist/cli-OFPZ5MAS.js.map +1 -0
  148. package/dist/cli-SHSMSRQY.js +28 -0
  149. package/dist/cli-SHSMSRQY.js.map +1 -0
  150. package/dist/cli-SRHFUZHA.js +473 -0
  151. package/dist/cli-SRHFUZHA.js.map +1 -0
  152. package/dist/cli-URZQXL4K.js +552 -0
  153. package/dist/cli-URZQXL4K.js.map +1 -0
  154. package/dist/cli.d.ts +12 -2
  155. package/dist/cli.js +109 -7651
  156. package/dist/cli.js.map +1 -1
  157. package/dist/docmeta.js +15 -3
  158. package/dist/docmeta.js.map +1 -1
  159. package/dist/github-FE6KCNYJ.js +27 -0
  160. package/dist/github-FE6KCNYJ.js.map +1 -0
  161. package/dist/github-QXL3VMK2.js +32 -0
  162. package/dist/github-QXL3VMK2.js.map +1 -0
  163. package/dist/index.d.ts +65 -3
  164. package/dist/index.js +407 -337
  165. package/dist/index.js.map +1 -1
  166. package/dist/json-7WV7KL6H.js +17 -0
  167. package/dist/json-7WV7KL6H.js.map +1 -0
  168. package/dist/json-SGZ6ZRFD.js +35 -0
  169. package/dist/json-SGZ6ZRFD.js.map +1 -0
  170. package/dist/lint-DL4LJRAY.js +28 -0
  171. package/dist/lint-DL4LJRAY.js.map +1 -0
  172. package/dist/list-6BAXTZUM.js +29 -0
  173. package/dist/list-6BAXTZUM.js.map +1 -0
  174. package/dist/list-7BKNYPSB.js +31 -0
  175. package/dist/list-7BKNYPSB.js.map +1 -0
  176. package/dist/page-3U2GCFNE.js +41 -0
  177. package/dist/page-3U2GCFNE.js.map +1 -0
  178. package/dist/picker-3XF36MJF.js +14 -0
  179. package/dist/picker-3XF36MJF.js.map +1 -0
  180. package/dist/pretty-2H57QVTY.js +43 -0
  181. package/dist/pretty-2H57QVTY.js.map +1 -0
  182. package/dist/pretty-N6DO4OOL.js +37 -0
  183. package/dist/pretty-N6DO4OOL.js.map +1 -0
  184. package/dist/reporters-M6LJ2IYU.js +21 -0
  185. package/dist/reporters-M6LJ2IYU.js.map +1 -0
  186. package/dist/reporters-N6GHXSS7.js +42 -0
  187. package/dist/reporters-N6GHXSS7.js.map +1 -0
  188. package/dist/reporters-UCNEV7PH.js +71 -0
  189. package/dist/reporters-UCNEV7PH.js.map +1 -0
  190. package/dist/run-XZDAREJE.js +33 -0
  191. package/dist/run-XZDAREJE.js.map +1 -0
  192. package/dist/term-5ICGKPC3.js +1592 -0
  193. package/dist/term-5ICGKPC3.js.map +1 -0
  194. package/dist/types-Y6TXHM7M.js +10 -0
  195. package/dist/types-Y6TXHM7M.js.map +1 -0
  196. package/dist/validate-GQRM77YN.js +16 -0
  197. package/dist/validate-GQRM77YN.js.map +1 -0
  198. package/package.json +1 -1
  199. package/plugin/manni/.claude-plugin/plugin.json +9 -0
  200. package/plugin/manni/hooks/hooks.json +52 -0
  201. package/plugin/manni/skills/check/SKILL.md +20 -0
  202. package/plugin/manni/skills/evals/SKILL.md +27 -0
  203. package/plugin/manni/skills/fix/SKILL.md +56 -0
  204. package/plugin/manni/skills/setup/SKILL.md +41 -0
  205. package/dist/batch-7JJWXWSF.js +0 -18
  206. package/dist/calibrate-IA2ARFMH.js +0 -13
  207. package/dist/capture-C32JXIFN.js +0 -15
  208. package/dist/chunk-3R23C5DT.js.map +0 -1
  209. package/dist/chunk-CI2GOU3A.js.map +0 -1
  210. package/dist/chunk-DP2ZA3WH.js +0 -26518
  211. package/dist/chunk-DP2ZA3WH.js.map +0 -1
  212. package/dist/chunk-ESJ5W7EV.js.map +0 -1
  213. package/dist/chunk-MOQXWXSG.js.map +0 -1
  214. package/dist/chunk-O6RVQKK6.js +0 -27170
  215. package/dist/chunk-O6RVQKK6.js.map +0 -1
  216. package/dist/picker-6HRMETE7.js +0 -12
  217. package/plugin/tracevals/.claude-plugin/plugin.json +0 -9
  218. package/plugin/tracevals/hooks/hooks.json +0 -17
  219. /package/dist/{batch-7JJWXWSF.js.map → batch-DFRCLY77.js.map} +0 -0
  220. /package/dist/{calibrate-IA2ARFMH.js.map → build-Q5C6SX5J.js.map} +0 -0
  221. /package/dist/{capture-C32JXIFN.js.map → calibrate-XQILKXS3.js.map} +0 -0
  222. /package/dist/{picker-6HRMETE7.js.map → capture-JQMOC3BW.js.map} +0 -0
  223. /package/dist/{chunk-RVVCA4WL.js.map → chunk-MYC3O5EJ.js.map} +0 -0
  224. /package/dist/{chunk-QROCFW5S.js.map → chunk-XYLUCIKQ.js.map} +0 -0
@@ -0,0 +1,1042 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ EVIDENCE_SCHEMA,
4
+ EVIDENCE_SYSTEM_PROMPT,
5
+ JUDGE_SYSTEM_PROMPT,
6
+ VerdictCache,
7
+ buildEvidenceUser,
8
+ buildUserContent,
9
+ cacheKey,
10
+ frontmatterSchema,
11
+ judgeCacheBody,
12
+ leadingFrontmatterFormat,
13
+ loadRunConfig,
14
+ readTarget,
15
+ renderEvidence,
16
+ runConfigOptions,
17
+ runEvals,
18
+ selfPreferenceOf,
19
+ sha256,
20
+ splitBody,
21
+ urlManifestRefusal
22
+ } from "./chunk-NWZ2SLYL.js";
23
+ import {
24
+ turnBudgetSkipReason
25
+ } from "./chunk-LJ2RDH3F.js";
26
+ import {
27
+ isExecutionGrant,
28
+ unknownGrantsMessage
29
+ } from "./chunk-YNHZHSSM.js";
30
+ import {
31
+ DocevalsError
32
+ } from "./chunk-SE4JN5XN.js";
33
+ import {
34
+ externalWriteWarnings,
35
+ offerExternalHomes
36
+ } from "./chunk-K3D6FPV6.js";
37
+ import {
38
+ LOCATION_KEYWORD,
39
+ Validator,
40
+ keyHome,
41
+ readManifestOrEmpty,
42
+ readManifestValue,
43
+ spliceManifestValue
44
+ } from "./chunk-RLR4TKUZ.js";
45
+ import {
46
+ writeFileAtomic
47
+ } from "./chunk-JVTNMVYU.js";
48
+ import {
49
+ assertKnownProvider,
50
+ assertLocalFlag,
51
+ assertModelHasProvider,
52
+ localNotice,
53
+ noticeOnce,
54
+ providerSpecFor,
55
+ resolveIdentity,
56
+ selectProvider,
57
+ warn
58
+ } from "./chunk-Q35YIDBV.js";
59
+ import {
60
+ errorMessage
61
+ } from "./chunk-G3T47CFV.js";
62
+
63
+ // src/docevals/judge/judge.ts
64
+ import {
65
+ computeConsensus,
66
+ runEnsemble,
67
+ zoneFor
68
+ } from "@hawkeyexl/inference";
69
+
70
+ // src/docevals/judge/verdict-schema.json
71
+ var verdict_schema_default = {
72
+ $schema: "https://json-schema.org/draft/2020-12/schema",
73
+ $id: "manni:docevals:verdict",
74
+ title: "manni docevals judge verdict",
75
+ type: "object",
76
+ required: ["claim", "observed", "match", "confidence", "reasoning"],
77
+ properties: {
78
+ claim: {
79
+ type: "string",
80
+ description: "The specific documented assertion under evaluation."
81
+ },
82
+ observed: {
83
+ type: "string",
84
+ description: "What was actually observed in the page, quoting evidence where possible."
85
+ },
86
+ match: {
87
+ enum: ["pass", "fail", "partial"],
88
+ description: "pass only if the assertion is fully satisfied; partial for partial compliance."
89
+ },
90
+ confidence: {
91
+ type: "number",
92
+ minimum: 0,
93
+ maximum: 1,
94
+ description: "Self-reported confidence in this verdict."
95
+ },
96
+ reasoning: {
97
+ type: "string",
98
+ description: "Why this conclusion was reached."
99
+ }
100
+ },
101
+ additionalProperties: false
102
+ };
103
+
104
+ // src/docevals/core/reviews.ts
105
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
106
+ import { dirname, join } from "node:path";
107
+ import { parse as parseYaml, stringify as stringifyYaml } from "yaml";
108
+ var REVIEWS_PATH = join(".manni/docevals", "reviews.yaml");
109
+ function loadReviews(root) {
110
+ const path = join(root, REVIEWS_PATH);
111
+ if (!existsSync(path)) return [];
112
+ const raw = parseYaml(readFileSync(path, "utf8"));
113
+ if (!Array.isArray(raw)) return [];
114
+ return raw;
115
+ }
116
+ function saveReviews(root, reviews) {
117
+ const path = join(root, REVIEWS_PATH);
118
+ mkdirSync(dirname(path), { recursive: true });
119
+ writeFileSync(path, stringifyYaml(reviews));
120
+ }
121
+ function recordReview(root, entry) {
122
+ const reviews = loadReviews(root).filter(
123
+ (r) => !(r.file === entry.file && r.evalName === entry.evalName)
124
+ );
125
+ reviews.push(entry);
126
+ saveReviews(root, reviews);
127
+ }
128
+ function findReview(reviews, file, evalName, body) {
129
+ const entry = reviews.find(
130
+ (r) => r.file === file && r.evalName === evalName
131
+ );
132
+ if (!entry) return void 0;
133
+ return entry.contentHash === sha256(body) ? entry : void 0;
134
+ }
135
+
136
+ // src/docevals/judge/provider.ts
137
+ import {
138
+ makeProvider as makeInferenceProvider
139
+ } from "@hawkeyexl/inference";
140
+ var CONFIG_KEY = "docevals.provider";
141
+ var toDocevalsError = (message) => new DocevalsError(message);
142
+ function selectProvider2(config, flags = {}, ev = {}) {
143
+ assertLocalFlag(flags, toDocevalsError);
144
+ return selectProvider(flags, [
145
+ {
146
+ ...ev.provider !== void 0 ? { provider: ev.provider } : {},
147
+ ...ev.model !== void 0 ? { model: ev.model } : {},
148
+ ...ev.origin !== void 0 ? { origin: ev.origin } : {}
149
+ },
150
+ {
151
+ ...config.provider !== null ? { provider: config.provider } : {},
152
+ ...config.model !== null ? { model: config.model } : {},
153
+ origin: CONFIG_KEY
154
+ },
155
+ {
156
+ ...config.providers.provider !== void 0 ? { provider: config.providers.provider } : {},
157
+ ...config.providers.model !== void 0 ? { model: config.providers.model } : {},
158
+ origin: "providers.provider"
159
+ }
160
+ ]);
161
+ }
162
+ function announceSelection(selection) {
163
+ const message = localNotice(selection);
164
+ if (message !== void 0) noticeOnce(message);
165
+ }
166
+ function assertProviderSelection(selection) {
167
+ assertKnownProvider(selection.provider, toDocevalsError);
168
+ assertModelHasProvider(selection.provider, selection.model, CONFIG_KEY, toDocevalsError);
169
+ }
170
+ function providerSpecFor2(config, selection) {
171
+ const spec = providerSpecFor(config.providers, selection);
172
+ switch (selection.provider) {
173
+ case "anthropic":
174
+ return { ...spec, anthropic: { toolName: "record_verdict" } };
175
+ case "openai":
176
+ return { ...spec, openai: { schemaName: "verdict" } };
177
+ default:
178
+ return spec;
179
+ }
180
+ }
181
+ async function resolveProviderIdentity(config, flags = {}, ev = {}) {
182
+ const selection = selectProvider2(config, flags, ev);
183
+ const { provider, model } = selection;
184
+ assertKnownProvider(provider, toDocevalsError);
185
+ assertModelHasProvider(provider, model, CONFIG_KEY, toDocevalsError);
186
+ announceSelection(selection);
187
+ return resolveIdentity(providerSpecFor2(config, { provider, model: model ?? null }), toDocevalsError);
188
+ }
189
+ function constructProvider(config, identity) {
190
+ try {
191
+ return makeInferenceProvider(providerSpecFor2(config, identity));
192
+ } catch (e) {
193
+ throw new DocevalsError(errorMessage(e));
194
+ }
195
+ }
196
+ async function makeProvider(config, flags = {}, ev = {}) {
197
+ return constructProvider(config, await resolveProviderIdentity(config, flags, ev));
198
+ }
199
+
200
+ // src/docevals/judge/judge.ts
201
+ import { resolve as resolvePath } from "node:path";
202
+ var verdictSchema = verdict_schema_default;
203
+ function makeJudge(deps) {
204
+ return async (targets, config, options) => {
205
+ const { provider, root } = deps;
206
+ const overridden = /* @__PURE__ */ new Map();
207
+ const buildProvider = deps.providerFor ?? ((ev) => makeProvider(config, options, ev));
208
+ const keyOf = (s) => `${s.provider}:${s.model ?? ""}`;
209
+ const defaultKey = keyOf(selectProvider2(config, options));
210
+ const providerFor = (ev, file) => {
211
+ const selection = selectProvider2(config, options, {
212
+ ...ev.provider !== void 0 ? { provider: ev.provider } : {},
213
+ ...ev.model !== void 0 ? { model: ev.model } : {},
214
+ origin: `eval "${ev.name}" in ${file}`
215
+ });
216
+ announceSelection(selection);
217
+ const key = keyOf(selection);
218
+ if (key === defaultKey) return Promise.resolve(provider);
219
+ let p = overridden.get(key);
220
+ if (p === void 0) {
221
+ p = (async () => {
222
+ assertProviderSelection(selection);
223
+ return buildProvider(ev);
224
+ })();
225
+ overridden.set(key, p);
226
+ }
227
+ return p;
228
+ };
229
+ const runsFor = (ev) => options.runs ?? ev.runs ?? config.judge.ensembleRuns;
230
+ const temperature = config.judge.temperature;
231
+ const chunkChars = options.chunkChars ?? config.judge.chunkChars;
232
+ const cache = new VerdictCache(
233
+ resolvePath(root, config.judge.cacheDir),
234
+ options.noCache !== true,
235
+ "manni-docevals"
236
+ );
237
+ const reviews = loadReviews(root);
238
+ const maxTurns = options.maxTurns ?? config.judge.maxTurns;
239
+ let turnsSpent = 0;
240
+ const results = [];
241
+ const configured = config.judge.concurrency;
242
+ const concurrency = Number.isInteger(configured) && configured >= 1 ? configured : config.defaults.concurrency;
243
+ let index = 0;
244
+ const judgeTarget = async (target) => {
245
+ const { plan, eval: ev } = target;
246
+ const start = Date.now();
247
+ const runsPerEval = runsFor(ev);
248
+ let judgeProvider;
249
+ try {
250
+ judgeProvider = await providerFor(ev, plan.page.file);
251
+ } catch (e) {
252
+ if (!(e instanceof DocevalsError)) throw e;
253
+ return {
254
+ evalName: ev.name,
255
+ type: ev.type,
256
+ grader: ev.grader,
257
+ file: plan.page.file,
258
+ outcome: "error",
259
+ skipReason: e.message,
260
+ durationMs: Date.now() - start
261
+ };
262
+ }
263
+ const preference = selfPreferenceOf(plan, ev, judgeProvider.modelName());
264
+ if (preference) warn(`${plan.page.file}: ${preference.message}`);
265
+ const selected = readTarget(ev.target, plan);
266
+ if (!selected.ok) {
267
+ return {
268
+ evalName: ev.name,
269
+ type: ev.type,
270
+ grader: ev.grader,
271
+ file: plan.page.file,
272
+ outcome: "error",
273
+ skipReason: selected.reason,
274
+ durationMs: Date.now() - start
275
+ };
276
+ }
277
+ const chunks = splitBody(selected.text, chunkChars);
278
+ let judged = selected.text;
279
+ let judgedLabel = selected.label;
280
+ const key = cacheKey(
281
+ judgeProvider.provider(),
282
+ judgeProvider.modelName(),
283
+ runsPerEval,
284
+ temperature,
285
+ judgeCacheBody(chunkChars, selected.text),
286
+ ev
287
+ );
288
+ const cached = cache.get(key) !== void 0;
289
+ if (chunks.length > 1 && !cached) {
290
+ const gathered = [];
291
+ for (const [i, chunk] of chunks.entries()) {
292
+ if (maxTurns != null && turnsSpent >= maxTurns) {
293
+ return {
294
+ evalName: ev.name,
295
+ type: ev.type,
296
+ grader: ev.grader,
297
+ file: plan.page.file,
298
+ outcome: "skipped",
299
+ skipReason: `${turnBudgetSkipReason(maxTurns)} (after ${String(i)} of ${String(chunks.length)} parts)`,
300
+ durationMs: Date.now() - start
301
+ };
302
+ }
303
+ turnsSpent += 1;
304
+ try {
305
+ const res = await judgeProvider.completeJSON({
306
+ system: EVIDENCE_SYSTEM_PROMPT,
307
+ user: buildEvidenceUser(ev, chunk, {
308
+ index: i,
309
+ total: chunks.length
310
+ }),
311
+ schema: EVIDENCE_SCHEMA,
312
+ temperature
313
+ });
314
+ const json = res.json;
315
+ gathered.push({
316
+ supports: json.supports ?? [],
317
+ contradicts: json.contradicts ?? []
318
+ });
319
+ } catch (e) {
320
+ return {
321
+ evalName: ev.name,
322
+ type: ev.type,
323
+ grader: ev.grader,
324
+ file: plan.page.file,
325
+ outcome: "error",
326
+ skipReason: `gathering evidence from part ${String(i + 1)} of ${String(chunks.length)} failed: ${errorMessage(e)}`,
327
+ durationMs: Date.now() - start
328
+ };
329
+ }
330
+ }
331
+ judged = renderEvidence(gathered, chunks.length);
332
+ judgedLabel = `${selected.label}, ${String(chunks.length)} parts`;
333
+ }
334
+ if (maxTurns != null && !cached) {
335
+ if (turnsSpent + runsPerEval > maxTurns) {
336
+ return {
337
+ evalName: ev.name,
338
+ type: ev.type,
339
+ grader: ev.grader,
340
+ file: plan.page.file,
341
+ outcome: "skipped",
342
+ skipReason: turnBudgetSkipReason(maxTurns),
343
+ durationMs: 0
344
+ };
345
+ }
346
+ turnsSpent += runsPerEval;
347
+ }
348
+ const runs = await runEnsemble({
349
+ provider: judgeProvider,
350
+ system: JUDGE_SYSTEM_PROMPT,
351
+ user: buildUserContent(ev, judged, judgedLabel),
352
+ runs: runsPerEval,
353
+ temperature,
354
+ schema: verdictSchema,
355
+ cache,
356
+ cacheKey: key,
357
+ label: "manni-docevals"
358
+ });
359
+ const consensusBase = computeConsensus(runs);
360
+ const zone = zoneFor(consensusBase, config.judge.zones);
361
+ const consensus = { ...consensusBase, zone };
362
+ let outcome = zone === "auto-pass" ? "pass" : zone === "auto-fail" ? "fail" : "needs-review";
363
+ let via;
364
+ if (outcome === "needs-review") {
365
+ const review = findReview(reviews, plan.page.file, ev.name, plan.page.body);
366
+ if (review) {
367
+ outcome = review.verdict;
368
+ via = "human-review";
369
+ }
370
+ }
371
+ return {
372
+ evalName: ev.name,
373
+ type: ev.type,
374
+ grader: ev.grader,
375
+ file: plan.page.file,
376
+ outcome,
377
+ consensus,
378
+ via,
379
+ ...preference ? { selfPreference: { axis: preference.axis, model: preference.model } } : {},
380
+ durationMs: Date.now() - start
381
+ };
382
+ };
383
+ const workers = Array.from(
384
+ { length: Math.min(concurrency, targets.length) },
385
+ async () => {
386
+ while (index < targets.length) {
387
+ const i = index++;
388
+ const target = targets[i];
389
+ if (target === void 0) break;
390
+ results[i] = await judgeTarget(target);
391
+ }
392
+ }
393
+ );
394
+ await Promise.all(workers);
395
+ return results;
396
+ };
397
+ }
398
+
399
+ // src/docevals/graders/scriptgen.ts
400
+ import { mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "node:fs";
401
+ import { basename, dirname as dirname2, extname, join as join2, relative, resolve as resolve2 } from "node:path";
402
+
403
+ // src/docevals/core/frontmatter-edit.ts
404
+ import { parseDocument, Document, YAMLSeq, isMap, isScalar } from "yaml";
405
+ var NAMESPACE = "docevals";
406
+ var BOM = "\uFEFF";
407
+ function splitYamlFrontmatter(content, path) {
408
+ const bom = content.startsWith(BOM) ? BOM : "";
409
+ const body = bom ? content.slice(1) : content;
410
+ const openMatch = /^---(\r?\n)/.exec(body);
411
+ if (!openMatch) {
412
+ throw new DocevalsError(
413
+ `${path}: no YAML frontmatter block to edit (only YAML frontmatter is editable)`
414
+ );
415
+ }
416
+ const eol = openMatch[1] === "\r\n" ? "\r\n" : "\n";
417
+ const [openLine = "", ...rest] = body.split(/(?<=\n)/);
418
+ let offset = openLine.length;
419
+ for (const line of rest) {
420
+ const stripped = line.replace(/\r?\n$/, "");
421
+ if (stripped === "---" || stripped === "...") {
422
+ const blockEnd = offset;
423
+ return {
424
+ open: bom + openLine,
425
+ block: body.slice(openLine.length, blockEnd),
426
+ suffix: body.slice(blockEnd),
427
+ eol
428
+ };
429
+ }
430
+ offset += line.length;
431
+ }
432
+ throw new DocevalsError(`${path}: unterminated frontmatter block`);
433
+ }
434
+ function applyUpdates(doc, node, updates) {
435
+ if (updates.grader !== void 0) node.set("grader", updates.grader);
436
+ if (updates.command !== void 0) {
437
+ const seq = doc.createNode(updates.command);
438
+ seq.flow = true;
439
+ node.set("command", seq);
440
+ }
441
+ const hash = updates["generated-assertion-hash"];
442
+ if (hash !== void 0) node.set("generated-assertion-hash", hash);
443
+ }
444
+ function evalSeq(doc) {
445
+ const node = doc.get("evals", true);
446
+ return node instanceof YAMLSeq ? node : void 0;
447
+ }
448
+ function findEvalNode(doc, evalName) {
449
+ const evals = evalSeq(doc);
450
+ if (!evals) return void 0;
451
+ for (const item of evals.items) {
452
+ if (isMap(item)) {
453
+ const name = item.get("id") ?? item.get("use");
454
+ if (name === evalName) return item;
455
+ }
456
+ }
457
+ return void 0;
458
+ }
459
+ function updatePageEval(content, path, evalName, updates) {
460
+ const { open, block, suffix, eol } = splitYamlFrontmatter(content, path);
461
+ const doc = parseDocument(block);
462
+ if (doc.errors.length > 0) {
463
+ throw new DocevalsError(
464
+ `${path}: cannot edit frontmatter \u2014 ${doc.errors[0]?.message ?? "parse error"}`
465
+ );
466
+ }
467
+ const node = findEvalNode(doc, evalName);
468
+ if (!node) {
469
+ throw new DocevalsError(
470
+ `${path}: eval "${evalName}" not found in frontmatter`
471
+ );
472
+ }
473
+ applyUpdates(doc, node, updates);
474
+ let newBlock = doc.toString();
475
+ if (eol === "\r\n") newBlock = newBlock.replace(/(?<!\r)\n/g, "\r\n");
476
+ return open + newBlock + suffix;
477
+ }
478
+ function updateConfigEval(configText, configPath, evalName, updates) {
479
+ const doc = parseDocument(configText);
480
+ if (doc.errors.length > 0) {
481
+ throw new DocevalsError(
482
+ `${configPath}: cannot edit config \u2014 ${doc.errors[0]?.message ?? "parse error"}`
483
+ );
484
+ }
485
+ const node = doc.getIn([NAMESPACE, "evals", evalName]);
486
+ if (!isMap(node)) {
487
+ throw new DocevalsError(
488
+ `${configPath}: eval "${evalName}" not found in config`
489
+ );
490
+ }
491
+ applyUpdates(doc, node, updates);
492
+ return doc.toString();
493
+ }
494
+ function entryObject(entry) {
495
+ const obj = { id: entry.id, assertion: entry.assertion };
496
+ if (entry.type !== void 0) obj.type = entry.type;
497
+ if (entry.grader !== void 0) obj.grader = entry.grader;
498
+ if (entry.evidence !== void 0) obj.evidence = entry.evidence;
499
+ if (entry.examples !== void 0) obj.examples = entry.examples;
500
+ if (entry.severity !== void 0) obj.severity = entry.severity;
501
+ return obj;
502
+ }
503
+ var META_PROVENANCE_KEY = "meta-provenance";
504
+ function assertNoCollision(seq, name, path) {
505
+ for (const item of seq.items) {
506
+ const existing = isMap(item) ? item.get("id") ?? item.get("use") : isScalar(item) ? item.value : void 0;
507
+ if (existing === name) {
508
+ throw new DocevalsError(
509
+ `${path}: eval "${name}" already exists in frontmatter`
510
+ );
511
+ }
512
+ }
513
+ }
514
+ function appendPageEvals(content, path, entries, metaProvenance) {
515
+ const format = leadingFrontmatterFormat(content);
516
+ if (format === "toml" || format === "json") {
517
+ throw new DocevalsError(
518
+ `${path}: only YAML frontmatter can be edited (found ${format} frontmatter)`
519
+ );
520
+ }
521
+ const bom = content.startsWith(BOM) ? BOM : "";
522
+ const stripped = bom ? content.slice(1) : content;
523
+ const eol = stripped.includes("\r\n") ? "\r\n" : "\n";
524
+ if (format === void 0) {
525
+ const doc2 = new Document(
526
+ entries.length === 0 ? {} : { evals: entries.map(entryObject) }
527
+ );
528
+ if (metaProvenance) doc2.set(META_PROVENANCE_KEY, doc2.createNode(metaProvenance));
529
+ let block2 = doc2.toString();
530
+ if (eol === "\r\n") block2 = block2.replace(/(?<!\r)\n/g, "\r\n");
531
+ return `${bom}---${eol}${block2}---${eol}${stripped}`;
532
+ }
533
+ const { open, block, suffix, eol: blockEol } = splitYamlFrontmatter(content, path);
534
+ const doc = parseDocument(block);
535
+ if (doc.errors.length > 0) {
536
+ throw new DocevalsError(
537
+ `${path}: cannot edit frontmatter \u2014 ${doc.errors[0]?.message ?? "parse error"}`
538
+ );
539
+ }
540
+ if (entries.length === 0) {
541
+ if (metaProvenance) doc.set(META_PROVENANCE_KEY, doc.createNode(metaProvenance));
542
+ let onlyEntry = doc.toString();
543
+ if (blockEol === "\r\n") onlyEntry = onlyEntry.replace(/(?<!\r)\n/g, "\r\n");
544
+ return open + onlyEntry + suffix;
545
+ }
546
+ let seq = evalSeq(doc);
547
+ if (!seq) {
548
+ const node = doc.get("evals", true);
549
+ if (node !== void 0) {
550
+ throw new DocevalsError(
551
+ `${path}: the evals key in frontmatter is not a list \u2014 expand the string shorthand into a list before appending`
552
+ );
553
+ }
554
+ seq = doc.createNode([]);
555
+ doc.set("evals", seq);
556
+ }
557
+ for (const entry of entries) {
558
+ assertNoCollision(seq, entry.id, path);
559
+ seq.add(doc.createNode(entryObject(entry)));
560
+ }
561
+ if (metaProvenance) doc.set(META_PROVENANCE_KEY, doc.createNode(metaProvenance));
562
+ let newBlock = doc.toString();
563
+ if (blockEol === "\r\n") newBlock = newBlock.replace(/(?<!\r)\n/g, "\r\n");
564
+ return open + newBlock + suffix;
565
+ }
566
+ function hasEditableEval(content, evalName) {
567
+ try {
568
+ const { block } = splitYamlFrontmatter(content, "");
569
+ const doc = parseDocument(block);
570
+ return findEvalNode(doc, evalName) !== void 0;
571
+ } catch {
572
+ return false;
573
+ }
574
+ }
575
+
576
+ // src/docevals/core/write-location.ts
577
+ import { resolve } from "node:path";
578
+ var EVALS_KEY = "evals";
579
+ var META_PROVENANCE_KEY2 = "meta-provenance";
580
+ function prefersExternal(key) {
581
+ const properties = frontmatterSchema.properties;
582
+ if (typeof properties !== "object" || properties === null) return false;
583
+ const property = properties[key];
584
+ if (typeof property !== "object" || property === null) return false;
585
+ return property[LOCATION_KEYWORD] === "external";
586
+ }
587
+ function noEntryMessage(label, join3, file, key) {
588
+ return `${label} carries no ${join3}, which ${file} joins on, so its ${key} has no entry there.`;
589
+ }
590
+ var EvalWriter = class _EvalWriter {
591
+ constructor(ctx, configSource) {
592
+ this.ctx = ctx;
593
+ this.configSource = configSource;
594
+ }
595
+ ctx;
596
+ configSource;
597
+ landed = [];
598
+ /**
599
+ * Build the writer for a run. `targets` are the positional paths as typed,
600
+ * which is where a page in no collection gets its proposed home from; a run
601
+ * that took its paths from the collections passes none.
602
+ *
603
+ * The context is built from the config docevals already loaded rather than
604
+ * by loading it again through meta. A `manni.config.yaml` carrying only a
605
+ * `docevals:` key is a single-tool file to the metadata tool, which refuses
606
+ * it; docevals reads it every run, and where its own evals go cannot depend
607
+ * on whether a sibling can read the same file. The collections are the
608
+ * declared ones, since a page named by path is still a member of whatever
609
+ * contains it. `config: null` leaves the page's schema set to the page,
610
+ * which is enough: the keys this writer homes are named outright in the
611
+ * relocation it plans.
612
+ */
613
+ static for(config, cwd, targets) {
614
+ const noConfig = config.configSource === null;
615
+ const collections = config.collections;
616
+ return new _EvalWriter(
617
+ {
618
+ cwd,
619
+ // Page labels are relative to the run's discovery root, so a manifest
620
+ // path this writer reports is spelled against the same directory
621
+ // `core/external.ts` reports a merged value's source against.
622
+ base: cwd,
623
+ noConfig,
624
+ config: null,
625
+ ...noConfig ? {} : { configDir: config.configDir, configPath: config.configPath },
626
+ collections,
627
+ declaredCollections: collections,
628
+ targets,
629
+ validator: new Validator()
630
+ },
631
+ config.configSource ?? cwd
632
+ );
633
+ }
634
+ /** Where `key` of the page labelled `label` goes. `data` is its metadata. */
635
+ async homeFor(label, data, key) {
636
+ const home = await keyHome(this.ctx, label, data, key);
637
+ if (home.kind === "url") {
638
+ return { kind: "url", collection: home.collection, file: home.file };
639
+ }
640
+ if (home.kind === "manifest") {
641
+ if (home.entry === void 0) {
642
+ return {
643
+ kind: "no-entry",
644
+ message: noEntryMessage(label, home.join, home.file, key)
645
+ };
646
+ }
647
+ return {
648
+ kind: "manifest",
649
+ file: home.file,
650
+ absPath: home.absPath,
651
+ join: home.join,
652
+ entry: home.entry,
653
+ perPage: home.perPage
654
+ };
655
+ }
656
+ return { kind: "page", prefersExternal: prefersExternal(key), proposed: home.home };
657
+ }
658
+ /**
659
+ * The refusal for an eval key a URL manifest owns, in chunk 1's wording. A
660
+ * run reaches it only where the load-time refusal could not: a manifest
661
+ * declared for a collection whose pages this run never merged.
662
+ */
663
+ urlRefusal(home, key) {
664
+ return new DocevalsError(urlManifestRefusal(this.configSource, home.collection, key));
665
+ }
666
+ /** Record that `key` stayed on the page, for the W1 and W2 lines. */
667
+ stayedOnPage(label, key, proposed) {
668
+ this.landed.push({ label, key, home: proposed });
669
+ }
670
+ /** The W1 and W2 lines for everything that stayed on a page. Without the prefix. */
671
+ warnings(dryRun) {
672
+ return this.landed.length === 0 ? [] : externalWriteWarnings(this.landed, dryRun);
673
+ }
674
+ /**
675
+ * Ask P1 once per collection the flagged writes would be homed in. Returns
676
+ * true when a relocation was applied, which leaves the caller's config,
677
+ * pages and this writer stale: rebuild all three before writing.
678
+ */
679
+ async offer(writes, hooks) {
680
+ if (writes.length === 0) return false;
681
+ const applied = await offerExternalHomes(this.ctx, writes, hooks);
682
+ return applied.size > 0;
683
+ }
684
+ /** The value the manifest holds for this page's entry, or `undefined`. */
685
+ async readManifest(home, key) {
686
+ return readManifestValue(await this.text(home.absPath, home.perPage), {
687
+ entry: home.entry,
688
+ key,
689
+ join: home.join
690
+ });
691
+ }
692
+ /** Write `value` into this page's entry, changing no other byte of the file. */
693
+ async writeManifest(home, key, value) {
694
+ const text = await this.text(home.absPath, home.perPage);
695
+ let spliced;
696
+ try {
697
+ spliced = spliceManifestValue(text, {
698
+ entry: home.entry,
699
+ key,
700
+ value,
701
+ join: home.join,
702
+ file: home.file
703
+ }).text;
704
+ } catch (e) {
705
+ throw new DocevalsError(errorMessage(e));
706
+ }
707
+ if (spliced !== text) {
708
+ await writeFileAtomic(home.absPath, spliced, { createParents: true });
709
+ }
710
+ }
711
+ /**
712
+ * The manifest's bytes. A per-page manifest that is not on disk yet is a
713
+ * page with nothing recorded, held as empty text and created by the write,
714
+ * exactly as `manni meta fill` holds one. A missing concrete manifest is
715
+ * refused, as it always was.
716
+ */
717
+ async text(absPath, perPage) {
718
+ try {
719
+ return await readManifestOrEmpty(absPath, perPage);
720
+ } catch (e) {
721
+ throw new DocevalsError(
722
+ `${resolve(absPath)} could not be read: ${errorMessage(e)}`
723
+ );
724
+ }
725
+ }
726
+ };
727
+ function manifestEvalList(value, file, entry) {
728
+ if (value === void 0 || value === null) return [];
729
+ if (Array.isArray(value)) return [...value];
730
+ throw new DocevalsError(
731
+ `${file}: the evals key of ${entry} is not a list \u2014 expand the string shorthand into a list before appending`
732
+ );
733
+ }
734
+ function updateManifestEval(list, name, updates) {
735
+ const index = list.findIndex((item) => named(item) === name);
736
+ const held = index === -1 ? void 0 : list[index];
737
+ if (held === void 0) return void 0;
738
+ const set = { ...held };
739
+ if (updates.grader !== void 0) set.grader = updates.grader;
740
+ if (updates.command !== void 0) set.command = updates.command;
741
+ const hash = updates["generated-assertion-hash"];
742
+ if (hash !== void 0) set["generated-assertion-hash"] = hash;
743
+ const next = [...list];
744
+ next[index] = set;
745
+ return next;
746
+ }
747
+ function named(item) {
748
+ if (typeof item !== "object" || item === null || Array.isArray(item)) return void 0;
749
+ const entry = item;
750
+ const id = entry.id ?? entry.use;
751
+ return typeof id === "string" ? id : void 0;
752
+ }
753
+
754
+ // src/docevals/graders/scriptgen.ts
755
+ var SCRIPTGEN_SYSTEM_PROMPT = [
756
+ "You write small, self-contained Node.js check scripts that verify a",
757
+ "deterministic assertion about a documentation page.",
758
+ "",
759
+ "Contract for every script:",
760
+ "- ES module (.mjs) using only Node.js built-in modules. No dependencies.",
761
+ "- The page's absolute path arrives as process.argv[2] (also MANNI_DOCEVALS_FILE).",
762
+ "- Exit 0 when the assertion holds, 1 when it fails, 2 on operational error.",
763
+ "- On failure, print a short human-readable reason to stderr.",
764
+ "- Deterministic: no network access, no spawning processes, no randomness.",
765
+ "- The page content may start with a YAML frontmatter block fenced by ---",
766
+ " lines; account for it when checking body content.",
767
+ "- Prefer simple, reviewable logic (regex/string checks) over cleverness.",
768
+ 'Respond with JSON: { "code": "<the full script source>" }.'
769
+ ].join("\n");
770
+ var SCRIPT_SCHEMA = {
771
+ type: "object",
772
+ required: ["code"],
773
+ properties: {
774
+ code: { type: "string", minLength: 1 },
775
+ notes: { type: "string" }
776
+ },
777
+ additionalProperties: false
778
+ };
779
+ function buildScriptgenUser(assertion, evalName, file, body) {
780
+ return [
781
+ `# Assertion to check`,
782
+ assertion,
783
+ "",
784
+ `# Eval name`,
785
+ evalName,
786
+ "",
787
+ `# Page path`,
788
+ file,
789
+ "",
790
+ "# Current page content (for grounding \u2014 the script must express the",
791
+ "# assertion generally, not hardcode this exact content)",
792
+ "",
793
+ body
794
+ ].join("\n");
795
+ }
796
+ function scriptLocationFor(target, config, root) {
797
+ const ev = target.eval;
798
+ const page = target.plan.page;
799
+ if (ev.source === "page") {
800
+ const pageDir = dirname2(page.absPath);
801
+ const pattern = config.scripts.dir;
802
+ const dir2 = pattern.includes("{docDir}") ? pattern.replaceAll("{docDir}", pageDir) : resolve2(root, pattern);
803
+ const base = basename(page.absPath, extname(page.absPath));
804
+ const scriptAbsPath2 = join2(dir2, `${base}.${ev.name}.mjs`);
805
+ const rel2 = relative(pageDir, scriptAbsPath2).replace(/\\/g, "/");
806
+ return { scriptAbsPath: scriptAbsPath2, command: ["node", rel2, "{file}"] };
807
+ }
808
+ const dir = resolve2(config.configDir, config.scripts.configDir);
809
+ const scriptAbsPath = join2(dir, `${ev.name}.mjs`);
810
+ const rel = relative(config.configDir, scriptAbsPath).replace(/\\/g, "/");
811
+ return { scriptAbsPath, command: ["node", rel, "{file}"] };
812
+ }
813
+ function header(assertion, evalName) {
814
+ return [
815
+ "// manni docevals generated check",
816
+ `// Eval: ${evalName}`,
817
+ `// Assertion: ${assertion.replace(/\s+/g, " ").trim()}`,
818
+ "// Exit 0 = pass, 1 = fail, 2 = operational error.",
819
+ ""
820
+ ].join("\n");
821
+ }
822
+ function makeGenerateScripts(deps) {
823
+ return async (targets, config, _options) => {
824
+ const generatedPaths = [];
825
+ const refusals = [];
826
+ const doneConfigEvals = /* @__PURE__ */ new Set();
827
+ let provider;
828
+ let writer;
829
+ const getWriter = () => writer ??= EvalWriter.for(config, deps.root, []);
830
+ for (const target of targets) {
831
+ const ev = target.eval;
832
+ if (!ev.assertion) continue;
833
+ if (ev.source === "config" && doneConfigEvals.has(ev.name)) continue;
834
+ const page = target.plan.page;
835
+ let home;
836
+ if (ev.source === "page") {
837
+ home = await getWriter().homeFor(
838
+ page.file,
839
+ page.frontmatter.data,
840
+ EVALS_KEY
841
+ );
842
+ if (home.kind === "url") {
843
+ throw getWriter().urlRefusal(home, EVALS_KEY);
844
+ }
845
+ if (home.kind === "no-entry") {
846
+ refusals.push({ file: page.file, evalName: ev.name, message: home.message });
847
+ continue;
848
+ }
849
+ }
850
+ if (provider === void 0) {
851
+ try {
852
+ provider = typeof deps.provider === "function" ? await deps.provider() : deps.provider;
853
+ } catch (e) {
854
+ if (!(e instanceof DocevalsError)) throw e;
855
+ return { generatedPaths, refusals, unavailable: e.message };
856
+ }
857
+ }
858
+ const location = scriptLocationFor(target, config, deps.root);
859
+ let code;
860
+ try {
861
+ const response = await provider.completeJSON({
862
+ system: SCRIPTGEN_SYSTEM_PROMPT,
863
+ user: buildScriptgenUser(
864
+ ev.assertion,
865
+ ev.name,
866
+ target.plan.page.file,
867
+ target.plan.page.body
868
+ ),
869
+ schema: SCRIPT_SCHEMA,
870
+ temperature: 0
871
+ });
872
+ const json = response.json;
873
+ if (typeof json.code !== "string" || json.code.length === 0) {
874
+ continue;
875
+ }
876
+ code = json.code;
877
+ } catch {
878
+ continue;
879
+ }
880
+ mkdirSync2(dirname2(location.scriptAbsPath), { recursive: true });
881
+ writeFileSync2(
882
+ location.scriptAbsPath,
883
+ // Exactly one final newline, whatever the model sent: the script is
884
+ // committed and reviewed, and a missing one shows in every diff.
885
+ `${header(ev.assertion, ev.name)}${code.trim()}
886
+ `
887
+ );
888
+ generatedPaths.push(
889
+ relative(deps.root, location.scriptAbsPath).replace(/\\/g, "/")
890
+ );
891
+ const updates = {
892
+ command: location.command,
893
+ "generated-assertion-hash": sha256(ev.assertion)
894
+ };
895
+ if (ev.source === "page" && home?.kind === "manifest") {
896
+ const at = getWriter();
897
+ const list = manifestEvalList(
898
+ await at.readManifest(home, EVALS_KEY),
899
+ home.file,
900
+ home.entry
901
+ );
902
+ const next = updateManifestEval(list, ev.name, updates);
903
+ if (next === void 0) {
904
+ throw new DocevalsError(
905
+ `${home.file}: eval "${ev.name}" not found in the entry for ${home.entry}`
906
+ );
907
+ }
908
+ await at.writeManifest(home, EVALS_KEY, next);
909
+ } else if (ev.source === "page") {
910
+ const abs = page.absPath;
911
+ const updated = updatePageEval(
912
+ readFileSync2(abs, "utf8"),
913
+ page.file,
914
+ ev.name,
915
+ updates
916
+ );
917
+ writeFileSync2(abs, updated);
918
+ } else {
919
+ const updated = updateConfigEval(
920
+ readFileSync2(config.configPath, "utf8"),
921
+ config.configPath,
922
+ ev.name,
923
+ updates
924
+ );
925
+ writeFileSync2(config.configPath, updated);
926
+ doneConfigEvals.add(ev.name);
927
+ }
928
+ if (ev.source === "config") {
929
+ for (const t of targets) {
930
+ if (t.eval.name === ev.name && t.eval.source === "config") {
931
+ t.eval.command = scriptLocationFor(t, config, deps.root).command;
932
+ t.eval.generatedAssertionHash = updates["generated-assertion-hash"];
933
+ }
934
+ }
935
+ } else {
936
+ target.eval.command = location.command;
937
+ target.eval.generatedAssertionHash = updates["generated-assertion-hash"];
938
+ }
939
+ }
940
+ return { generatedPaths, refusals };
941
+ };
942
+ }
943
+
944
+ // src/docevals/commands/run.ts
945
+ function asGrants(values) {
946
+ if (values === void 0) return void 0;
947
+ const unknown = values.filter((v) => !isExecutionGrant(v));
948
+ if (unknown.length > 0) {
949
+ throw new DocevalsError(unknownGrantsMessage(unknown));
950
+ }
951
+ return values;
952
+ }
953
+ async function runRun(paths, options = {}, engineOverrides = {}) {
954
+ const cwd = options.cwd ?? process.cwd();
955
+ const judgeOptions = {
956
+ provider: options.provider,
957
+ model: options.model,
958
+ local: options.local,
959
+ runs: options.runs,
960
+ chunkChars: options.chunkChars,
961
+ noCache: options.cache === false,
962
+ maxTurns: options.maxTurns ?? null
963
+ };
964
+ const config = loadRunConfig(runConfigOptions(paths, options), cwd);
965
+ assertProviderSelection(selectProvider2(config, judgeOptions));
966
+ let judge;
967
+ let generateScripts;
968
+ let resolving;
969
+ const provider = () => resolving ??= makeProvider(config, judgeOptions);
970
+ const wantsJudge = !("judge" in engineOverrides);
971
+ const wantsGeneration = !("generateScripts" in engineOverrides);
972
+ if (wantsJudge || wantsGeneration) {
973
+ if (!options.deterministicOnly) {
974
+ try {
975
+ const resolved = await provider();
976
+ if (wantsJudge) judge = makeJudge({ provider: resolved, root: cwd });
977
+ if (wantsGeneration && options.generate !== false) {
978
+ generateScripts = makeGenerateScripts({ provider: resolved, root: cwd });
979
+ }
980
+ } catch (e) {
981
+ if (options.aiOnly || !(e instanceof DocevalsError)) throw e;
982
+ warn(`provider unavailable \u2014 ${e.message}. Running deterministic evals only.`);
983
+ }
984
+ } else if (wantsGeneration && options.generate !== false) {
985
+ generateScripts = makeGenerateScripts({ provider, root: cwd });
986
+ }
987
+ }
988
+ return runEvals({
989
+ judge,
990
+ generateScripts,
991
+ config,
992
+ configPath: options.config,
993
+ noConfig: options.noConfig,
994
+ paths,
995
+ collection: options.collection,
996
+ exclude: options.exclude,
997
+ cwd: options.cwd,
998
+ deterministicOnly: options.deterministicOnly,
999
+ aiOnly: options.aiOnly,
1000
+ allowExecution: asGrants(options.allowExecution),
1001
+ execution: options.execution,
1002
+ generate: options.generate,
1003
+ failOnReview: options.failOnReview,
1004
+ evalNames: options.evalNames,
1005
+ suite: options.suite,
1006
+ since: options.since,
1007
+ newerThan: options.newerThan,
1008
+ baseline: options.baseline,
1009
+ writeBaseline: options.writeBaseline,
1010
+ toolVersion: options.toolVersion,
1011
+ judgeOptions,
1012
+ ...engineOverrides
1013
+ });
1014
+ }
1015
+
1016
+ export {
1017
+ loadReviews,
1018
+ recordReview,
1019
+ selectProvider2 as selectProvider,
1020
+ assertProviderSelection,
1021
+ providerSpecFor2 as providerSpecFor,
1022
+ resolveProviderIdentity,
1023
+ constructProvider,
1024
+ makeProvider,
1025
+ makeJudge,
1026
+ updatePageEval,
1027
+ updateConfigEval,
1028
+ entryObject,
1029
+ appendPageEvals,
1030
+ hasEditableEval,
1031
+ EVALS_KEY,
1032
+ META_PROVENANCE_KEY2 as META_PROVENANCE_KEY,
1033
+ EvalWriter,
1034
+ manifestEvalList,
1035
+ updateManifestEval,
1036
+ SCRIPTGEN_SYSTEM_PROMPT,
1037
+ buildScriptgenUser,
1038
+ scriptLocationFor,
1039
+ makeGenerateScripts,
1040
+ runRun
1041
+ };
1042
+ //# sourceMappingURL=chunk-PHF3D7ER.js.map