@vibe-agent-toolkit/agent-skills 0.1.42 → 0.2.0-rc.10

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 (322) hide show
  1. package/README.md +5 -5
  2. package/dist/builder.d.ts.map +1 -1
  3. package/dist/builder.js +7 -17
  4. package/dist/builder.js.map +1 -1
  5. package/dist/content-type-routing.d.ts +32 -0
  6. package/dist/content-type-routing.d.ts.map +1 -1
  7. package/dist/content-type-routing.js +23 -0
  8. package/dist/content-type-routing.js.map +1 -1
  9. package/dist/files-config.d.ts +34 -1
  10. package/dist/files-config.d.ts.map +1 -1
  11. package/dist/files-config.js +277 -39
  12. package/dist/files-config.js.map +1 -1
  13. package/dist/fs-attribution.d.ts +45 -0
  14. package/dist/fs-attribution.d.ts.map +1 -0
  15. package/dist/fs-attribution.js +57 -0
  16. package/dist/fs-attribution.js.map +1 -0
  17. package/dist/import.d.ts.map +1 -1
  18. package/dist/import.js +0 -4
  19. package/dist/import.js.map +1 -1
  20. package/dist/index.d.ts +13 -8
  21. package/dist/index.d.ts.map +1 -1
  22. package/dist/index.js +25 -7
  23. package/dist/index.js.map +1 -1
  24. package/dist/inventory/detectors/declared-but-missing.d.ts +1 -1
  25. package/dist/inventory/detectors/declared-but-missing.d.ts.map +1 -1
  26. package/dist/inventory/detectors/marketplace-source-missing.d.ts +1 -1
  27. package/dist/inventory/detectors/marketplace-source-missing.d.ts.map +1 -1
  28. package/dist/inventory/detectors/present-but-undeclared.d.ts +1 -1
  29. package/dist/inventory/detectors/present-but-undeclared.d.ts.map +1 -1
  30. package/dist/inventory/detectors/reference-target-missing.d.ts +1 -1
  31. package/dist/inventory/detectors/reference-target-missing.d.ts.map +1 -1
  32. package/dist/inventory/index.d.ts +1 -1
  33. package/dist/inventory/index.d.ts.map +1 -1
  34. package/dist/inventory/index.js +1 -1
  35. package/dist/inventory/index.js.map +1 -1
  36. package/dist/inventory/serialize.d.ts +4 -4
  37. package/dist/inventory/serialize.d.ts.map +1 -1
  38. package/dist/inventory/serialize.js +6 -6
  39. package/dist/inventory/serialize.js.map +1 -1
  40. package/dist/inventory/types.d.ts +8 -0
  41. package/dist/inventory/types.d.ts.map +1 -1
  42. package/dist/inventory/types.js.map +1 -1
  43. package/dist/link-graph-members.d.ts +18 -0
  44. package/dist/link-graph-members.d.ts.map +1 -0
  45. package/dist/link-graph-members.js +18 -0
  46. package/dist/link-graph-members.js.map +1 -0
  47. package/dist/parsers/frontmatter-parser.d.ts.map +1 -1
  48. package/dist/parsers/frontmatter-parser.js +30 -6
  49. package/dist/parsers/frontmatter-parser.js.map +1 -1
  50. package/dist/plugin-distribution-layout.d.ts +70 -6
  51. package/dist/plugin-distribution-layout.d.ts.map +1 -1
  52. package/dist/plugin-distribution-layout.js +84 -11
  53. package/dist/plugin-distribution-layout.js.map +1 -1
  54. package/dist/post-build-checks.d.ts +38 -1
  55. package/dist/post-build-checks.d.ts.map +1 -1
  56. package/dist/post-build-checks.js +68 -17
  57. package/dist/post-build-checks.js.map +1 -1
  58. package/dist/projection/skill-extent.d.ts +247 -0
  59. package/dist/projection/skill-extent.d.ts.map +1 -0
  60. package/dist/projection/skill-extent.js +560 -0
  61. package/dist/projection/skill-extent.js.map +1 -0
  62. package/dist/schemas/agent-skill-frontmatter.js +1 -1
  63. package/dist/schemas/agent-skill-frontmatter.js.map +1 -1
  64. package/dist/schemas/allowed-tools.d.ts +31 -0
  65. package/dist/schemas/allowed-tools.d.ts.map +1 -0
  66. package/dist/schemas/allowed-tools.js +88 -0
  67. package/dist/schemas/allowed-tools.js.map +1 -0
  68. package/dist/schemas/index.d.ts +1 -0
  69. package/dist/schemas/index.d.ts.map +1 -1
  70. package/dist/schemas/index.js +1 -0
  71. package/dist/schemas/index.js.map +1 -1
  72. package/dist/schemas/installed-plugins-registry.d.ts.map +1 -1
  73. package/dist/schemas/installed-plugins-registry.js +7 -1
  74. package/dist/schemas/installed-plugins-registry.js.map +1 -1
  75. package/dist/schemas/marketplace-manifest.d.ts.map +1 -1
  76. package/dist/schemas/marketplace-manifest.js +17 -6
  77. package/dist/schemas/marketplace-manifest.js.map +1 -1
  78. package/dist/skill-identity.d.ts +18 -3
  79. package/dist/skill-identity.d.ts.map +1 -1
  80. package/dist/skill-identity.js +25 -6
  81. package/dist/skill-identity.js.map +1 -1
  82. package/dist/skill-packager.d.ts +144 -20
  83. package/dist/skill-packager.d.ts.map +1 -1
  84. package/dist/skill-packager.js +447 -105
  85. package/dist/skill-packager.js.map +1 -1
  86. package/dist/skill-source/content-hash.d.ts.map +1 -1
  87. package/dist/skill-source/content-hash.js +21 -10
  88. package/dist/skill-source/content-hash.js.map +1 -1
  89. package/dist/skill-source/fetch-cache.d.ts.map +1 -1
  90. package/dist/skill-source/fetch-cache.js +0 -5
  91. package/dist/skill-source/fetch-cache.js.map +1 -1
  92. package/dist/skill-source/git-clone.d.ts +1 -1
  93. package/dist/skill-source/git-clone.d.ts.map +1 -1
  94. package/dist/skill-source/git-clone.js +10 -10
  95. package/dist/skill-source/git-clone.js.map +1 -1
  96. package/dist/skill-source/sources/url-source.d.ts.map +1 -1
  97. package/dist/skill-source/sources/url-source.js +2 -2
  98. package/dist/skill-source/sources/url-source.js.map +1 -1
  99. package/dist/skill-source/stage.d.ts.map +1 -1
  100. package/dist/skill-source/stage.js +0 -6
  101. package/dist/skill-source/stage.js.map +1 -1
  102. package/dist/skill-test/baseline-delta.d.ts +1575 -0
  103. package/dist/skill-test/baseline-delta.d.ts.map +1 -0
  104. package/dist/skill-test/baseline-delta.js +272 -0
  105. package/dist/skill-test/baseline-delta.js.map +1 -0
  106. package/dist/skill-test/baseline-integrity.d.ts +944 -0
  107. package/dist/skill-test/baseline-integrity.d.ts.map +1 -0
  108. package/dist/skill-test/baseline-integrity.js +3090 -0
  109. package/dist/skill-test/baseline-integrity.js.map +1 -0
  110. package/dist/skill-test/build-hook.d.ts +3 -2
  111. package/dist/skill-test/build-hook.d.ts.map +1 -1
  112. package/dist/skill-test/build-hook.js +4 -5
  113. package/dist/skill-test/build-hook.js.map +1 -1
  114. package/dist/skill-test/configure-writer.js +1 -1
  115. package/dist/skill-test/configure-writer.js.map +1 -1
  116. package/dist/skill-test/declared-env.d.ts +5 -4
  117. package/dist/skill-test/declared-env.d.ts.map +1 -1
  118. package/dist/skill-test/declared-env.js +8 -9
  119. package/dist/skill-test/declared-env.js.map +1 -1
  120. package/dist/skill-test/eval-executor.d.ts +21 -5
  121. package/dist/skill-test/eval-executor.d.ts.map +1 -1
  122. package/dist/skill-test/eval-executor.js +6 -6
  123. package/dist/skill-test/eval-executor.js.map +1 -1
  124. package/dist/skill-test/eval-fragment.d.ts +106 -28
  125. package/dist/skill-test/eval-fragment.d.ts.map +1 -1
  126. package/dist/skill-test/eval-fragment.js +78 -16
  127. package/dist/skill-test/eval-fragment.js.map +1 -1
  128. package/dist/skill-test/eval-grader.d.ts +65 -7
  129. package/dist/skill-test/eval-grader.d.ts.map +1 -1
  130. package/dist/skill-test/eval-grader.js +263 -64
  131. package/dist/skill-test/eval-grader.js.map +1 -1
  132. package/dist/skill-test/eval-inputs.d.ts +110 -8
  133. package/dist/skill-test/eval-inputs.d.ts.map +1 -1
  134. package/dist/skill-test/eval-inputs.js +177 -22
  135. package/dist/skill-test/eval-inputs.js.map +1 -1
  136. package/dist/skill-test/eval-lint.d.ts +17 -4
  137. package/dist/skill-test/eval-lint.d.ts.map +1 -1
  138. package/dist/skill-test/eval-lint.js +22 -9
  139. package/dist/skill-test/eval-lint.js.map +1 -1
  140. package/dist/skill-test/eval-suite-isolation.d.ts.map +1 -1
  141. package/dist/skill-test/eval-suite-isolation.js +8 -5
  142. package/dist/skill-test/eval-suite-isolation.js.map +1 -1
  143. package/dist/skill-test/evals-template.d.ts +1 -1
  144. package/dist/skill-test/evals-template.d.ts.map +1 -1
  145. package/dist/skill-test/evals-template.js +1 -3
  146. package/dist/skill-test/evals-template.js.map +1 -1
  147. package/dist/skill-test/executor-prompt.d.ts +15 -7
  148. package/dist/skill-test/executor-prompt.d.ts.map +1 -1
  149. package/dist/skill-test/executor-prompt.js +10 -7
  150. package/dist/skill-test/executor-prompt.js.map +1 -1
  151. package/dist/skill-test/failure-reason.d.ts +113 -0
  152. package/dist/skill-test/failure-reason.d.ts.map +1 -0
  153. package/dist/skill-test/failure-reason.js +124 -0
  154. package/dist/skill-test/failure-reason.js.map +1 -0
  155. package/dist/skill-test/fragment-merge.d.ts +3 -20
  156. package/dist/skill-test/fragment-merge.d.ts.map +1 -1
  157. package/dist/skill-test/fragment-merge.js +67 -8
  158. package/dist/skill-test/fragment-merge.js.map +1 -1
  159. package/dist/skill-test/grader-prompt.d.ts +29 -9
  160. package/dist/skill-test/grader-prompt.d.ts.map +1 -1
  161. package/dist/skill-test/grader-prompt.js +264 -68
  162. package/dist/skill-test/grader-prompt.js.map +1 -1
  163. package/dist/skill-test/grader-text.d.ts +145 -0
  164. package/dist/skill-test/grader-text.d.ts.map +1 -0
  165. package/dist/skill-test/grader-text.js +410 -0
  166. package/dist/skill-test/grader-text.js.map +1 -0
  167. package/dist/skill-test/grading-adapter.d.ts +38 -2
  168. package/dist/skill-test/grading-adapter.d.ts.map +1 -1
  169. package/dist/skill-test/grading-adapter.js +57 -9
  170. package/dist/skill-test/grading-adapter.js.map +1 -1
  171. package/dist/skill-test/grading-schema.d.ts +160 -5
  172. package/dist/skill-test/grading-schema.d.ts.map +1 -1
  173. package/dist/skill-test/grading-schema.js +66 -7
  174. package/dist/skill-test/grading-schema.js.map +1 -1
  175. package/dist/skill-test/harness-location.d.ts +3 -2
  176. package/dist/skill-test/harness-location.d.ts.map +1 -1
  177. package/dist/skill-test/harness-location.js +5 -16
  178. package/dist/skill-test/harness-location.js.map +1 -1
  179. package/dist/skill-test/index.d.ts +2 -2
  180. package/dist/skill-test/index.d.ts.map +1 -1
  181. package/dist/skill-test/index.js +2 -2
  182. package/dist/skill-test/index.js.map +1 -1
  183. package/dist/skill-test/lock.d.ts +58 -1
  184. package/dist/skill-test/lock.d.ts.map +1 -1
  185. package/dist/skill-test/lock.js +78 -6
  186. package/dist/skill-test/lock.js.map +1 -1
  187. package/dist/skill-test/pipeline.d.ts +19 -1
  188. package/dist/skill-test/pipeline.d.ts.map +1 -1
  189. package/dist/skill-test/pipeline.js +21 -10
  190. package/dist/skill-test/pipeline.js.map +1 -1
  191. package/dist/skill-test/preflight.d.ts +1 -1
  192. package/dist/skill-test/preflight.d.ts.map +1 -1
  193. package/dist/skill-test/preflight.js +59 -16
  194. package/dist/skill-test/preflight.js.map +1 -1
  195. package/dist/skill-test/prompt-invariants.d.ts +4 -2
  196. package/dist/skill-test/prompt-invariants.d.ts.map +1 -1
  197. package/dist/skill-test/prompt-invariants.js +5 -4
  198. package/dist/skill-test/prompt-invariants.js.map +1 -1
  199. package/dist/skill-test/run-harness.d.ts +665 -85
  200. package/dist/skill-test/run-harness.d.ts.map +1 -1
  201. package/dist/skill-test/run-harness.js +1522 -237
  202. package/dist/skill-test/run-harness.js.map +1 -1
  203. package/dist/skill-test/staging.d.ts +20 -8
  204. package/dist/skill-test/staging.d.ts.map +1 -1
  205. package/dist/skill-test/staging.js +25 -18
  206. package/dist/skill-test/staging.js.map +1 -1
  207. package/dist/skill-test/tier-plan.d.ts +1 -1
  208. package/dist/skill-test/tier-plan.js +1 -1
  209. package/dist/skill-test/tool-eval-schema.d.ts +6 -6
  210. package/dist/skill-test/tool-eval-schema.js +2 -2
  211. package/dist/skill-test/vendor-manifest.d.ts +2 -1
  212. package/dist/skill-test/vendor-manifest.d.ts.map +1 -1
  213. package/dist/skill-test/vendor-manifest.js +10 -10
  214. package/dist/skill-test/vendor-manifest.js.map +1 -1
  215. package/dist/test-input.d.ts +59 -8
  216. package/dist/test-input.d.ts.map +1 -1
  217. package/dist/test-input.js +64 -19
  218. package/dist/test-input.js.map +1 -1
  219. package/dist/validators/agent-instruction-presence.d.ts +1 -1
  220. package/dist/validators/agent-instruction-presence.d.ts.map +1 -1
  221. package/dist/validators/agent-instruction-presence.js +45 -2
  222. package/dist/validators/agent-instruction-presence.js.map +1 -1
  223. package/dist/validators/bundled-resource-link-detection.d.ts +6 -4
  224. package/dist/validators/bundled-resource-link-detection.d.ts.map +1 -1
  225. package/dist/validators/bundled-resource-link-detection.js +39 -39
  226. package/dist/validators/bundled-resource-link-detection.js.map +1 -1
  227. package/dist/validators/compat-detectors.d.ts +18 -1
  228. package/dist/validators/compat-detectors.d.ts.map +1 -1
  229. package/dist/validators/compat-detectors.js +47 -20
  230. package/dist/validators/compat-detectors.js.map +1 -1
  231. package/dist/validators/cross-skill-dependency-detection.d.ts +1 -1
  232. package/dist/validators/cross-skill-dependency-detection.d.ts.map +1 -1
  233. package/dist/validators/cross-skill-dependency-detection.js +1 -1
  234. package/dist/validators/cross-skill-dependency-detection.js.map +1 -1
  235. package/dist/validators/description-style-detection.d.ts +1 -1
  236. package/dist/validators/description-style-detection.d.ts.map +1 -1
  237. package/dist/validators/description-style-detection.js +1 -1
  238. package/dist/validators/description-style-detection.js.map +1 -1
  239. package/dist/validators/format-detection.d.ts +3 -0
  240. package/dist/validators/format-detection.d.ts.map +1 -1
  241. package/dist/validators/format-detection.js +41 -26
  242. package/dist/validators/format-detection.js.map +1 -1
  243. package/dist/validators/frontmatter-validation.d.ts +1 -1
  244. package/dist/validators/frontmatter-validation.d.ts.map +1 -1
  245. package/dist/validators/frontmatter-validation.js +2 -2
  246. package/dist/validators/frontmatter-validation.js.map +1 -1
  247. package/dist/validators/imperative-body-detection.d.ts +1 -1
  248. package/dist/validators/imperative-body-detection.d.ts.map +1 -1
  249. package/dist/validators/imperative-body-detection.js +1 -1
  250. package/dist/validators/imperative-body-detection.js.map +1 -1
  251. package/dist/validators/index.d.ts +1 -1
  252. package/dist/validators/index.d.ts.map +1 -1
  253. package/dist/validators/kebab-case-detection.d.ts +1 -1
  254. package/dist/validators/kebab-case-detection.d.ts.map +1 -1
  255. package/dist/validators/kebab-case-detection.js +1 -1
  256. package/dist/validators/kebab-case-detection.js.map +1 -1
  257. package/dist/validators/marketplace-validator.d.ts.map +1 -1
  258. package/dist/validators/marketplace-validator.js +7 -2
  259. package/dist/validators/marketplace-validator.js.map +1 -1
  260. package/dist/validators/mcp-tool-qualification.d.ts +158 -0
  261. package/dist/validators/mcp-tool-qualification.d.ts.map +1 -0
  262. package/dist/validators/mcp-tool-qualification.js +467 -0
  263. package/dist/validators/mcp-tool-qualification.js.map +1 -0
  264. package/dist/validators/packaged-size-limit.d.ts +296 -0
  265. package/dist/validators/packaged-size-limit.d.ts.map +1 -0
  266. package/dist/validators/packaged-size-limit.js +492 -0
  267. package/dist/validators/packaged-size-limit.js.map +1 -0
  268. package/dist/validators/packaging-validator.d.ts +149 -10
  269. package/dist/validators/packaging-validator.d.ts.map +1 -1
  270. package/dist/validators/packaging-validator.js +371 -55
  271. package/dist/validators/packaging-validator.js.map +1 -1
  272. package/dist/validators/plugin-hosted-shape.d.ts +4 -2
  273. package/dist/validators/plugin-hosted-shape.d.ts.map +1 -1
  274. package/dist/validators/plugin-hosted-shape.js +6 -7
  275. package/dist/validators/plugin-hosted-shape.js.map +1 -1
  276. package/dist/validators/plugin-recommended-fields.d.ts +1 -1
  277. package/dist/validators/plugin-recommended-fields.d.ts.map +1 -1
  278. package/dist/validators/plugin-recommended-fields.js +1 -1
  279. package/dist/validators/plugin-recommended-fields.js.map +1 -1
  280. package/dist/validators/referenced-path-missing.d.ts +211 -0
  281. package/dist/validators/referenced-path-missing.d.ts.map +1 -0
  282. package/dist/validators/referenced-path-missing.js +378 -0
  283. package/dist/validators/referenced-path-missing.js.map +1 -0
  284. package/dist/validators/registry-validator.d.ts.map +1 -1
  285. package/dist/validators/registry-validator.js +1 -2
  286. package/dist/validators/registry-validator.js.map +1 -1
  287. package/dist/validators/rule-engine/index.d.ts +1 -1
  288. package/dist/validators/rule-engine/rule-context.d.ts +20 -3
  289. package/dist/validators/rule-engine/rule-context.d.ts.map +1 -1
  290. package/dist/validators/rule-engine/rule-context.js +3 -1
  291. package/dist/validators/rule-engine/rule-context.js.map +1 -1
  292. package/dist/validators/rule-engine/rule-engine.d.ts +2 -2
  293. package/dist/validators/rule-engine/rule-engine.d.ts.map +1 -1
  294. package/dist/validators/rule-engine/rule-engine.js +52 -13
  295. package/dist/validators/rule-engine/rule-engine.js.map +1 -1
  296. package/dist/validators/skill-validator.d.ts.map +1 -1
  297. package/dist/validators/skill-validator.js +91 -54
  298. package/dist/validators/skill-validator.js.map +1 -1
  299. package/dist/validators/source-only-codes.js +1 -1
  300. package/dist/validators/source-only-codes.js.map +1 -1
  301. package/dist/validators/types.d.ts +21 -1
  302. package/dist/validators/types.d.ts.map +1 -1
  303. package/dist/validators/unified-validator.d.ts.map +1 -1
  304. package/dist/validators/unified-validator.js +4 -5
  305. package/dist/validators/unified-validator.js.map +1 -1
  306. package/dist/validators/validation-rules.d.ts +4 -4
  307. package/dist/validators/validation-rules.d.ts.map +1 -1
  308. package/dist/validators/validation-rules.js +9 -38
  309. package/dist/validators/validation-rules.js.map +1 -1
  310. package/dist/validators/walker-to-issues.d.ts +9 -3
  311. package/dist/validators/walker-to-issues.d.ts.map +1 -1
  312. package/dist/validators/walker-to-issues.js +64 -19
  313. package/dist/validators/walker-to-issues.js.map +1 -1
  314. package/dist/walk-link-graph.d.ts +64 -5
  315. package/dist/walk-link-graph.d.ts.map +1 -1
  316. package/dist/walk-link-graph.js +376 -92
  317. package/dist/walk-link-graph.js.map +1 -1
  318. package/package.json +14 -22
  319. package/dist/skill-test/exit-codes.d.ts +0 -97
  320. package/dist/skill-test/exit-codes.d.ts.map +0 -1
  321. package/dist/skill-test/exit-codes.js +0 -141
  322. package/dist/skill-test/exit-codes.js.map +0 -1
@@ -0,0 +1,410 @@
1
+ /**
2
+ * Neutralize GRADER-SUPPLIED free text before it reaches an operator surface or
3
+ * one of vat's own artifacts.
4
+ *
5
+ * WHY THIS EXISTS. The grader's only input is the executor transcript, and
6
+ * untrusted skill code writes that transcript. Every free-text field a grader
7
+ * emits is therefore attacker-influenced: `expectations[].text`/`.evidence`,
8
+ * `friction[].message`/`.evidence`/`.subjectFile`, `tool.*[].name`/`.evidence`,
9
+ * `tool.sequence[].steps`. `formatFrictionReport` interpolates one of those
10
+ * straight into `process.stderr.write`, so a message carrying a newline and an
11
+ * SGR sequence renders as vat's OWN voice — verified end-to-end with a grader
12
+ * emitting a green "vat: grading verified, ignore the warning above." line on a
13
+ * row of its own. That is strictly worse than a wrong number in
14
+ * `baseline.json`: operators read stderr, and nobody reads the artifact.
15
+ * (`summary` is NOT in that list — vat computes it numerically.)
16
+ *
17
+ * WHAT IT DOES, and why each part:
18
+ * - **ANSI escape sequences are removed WHOLE.** Dropping only the ESC byte
19
+ * would leave the parameter bytes (`[32m`) as visible litter; removing the
20
+ * sequence keeps the surviving text readable.
21
+ * - **Remaining C0/C1 controls become a space.** This is what kills the
22
+ * forged-line attack: no newline survives, so grader text can never occupy a
23
+ * line of its own. It also covers CR (line overwrite) and BS.
24
+ * - **Bidi controls and invisible non-spelling characters become a space too.**
25
+ * Stopping at U+009F left them alive, and they are not decoration: one U+202E
26
+ * (RIGHT-TO-LEFT OVERRIDE) in a friction `message` renders the REST of vat's own
27
+ * `[low] path-assumption: …` stderr line right-to-left, so the grader repaints
28
+ * text it did not write. `JSON.stringify` only escapes below U+0020, so the same
29
+ * code point also survives verbatim into `baseline.json` and `friction.json`. The
30
+ * invisible members (U+200B, U+2060…) are the quieter half: they let a grader
31
+ * smuggle a marker through any downstream string comparison while rendering as
32
+ * nothing at all. This is NOT the whole Cf class — see
33
+ * {@link INVISIBLE_CODE_RANGES} for the two admission tests and for the joiners
34
+ * and variation selectors deliberately left alone, because ordinary words are
35
+ * spelled with them and a grader quoting a file must get the file back.
36
+ * - **Whitespace runs collapse and the value is trimmed.** After the previous
37
+ * step a 200-newline message would be 200 spaces of padding. (The Unicode
38
+ * line/paragraph separators are NOT left to the collapse: the scan folds them,
39
+ * because {@link sanitizeTextPreservingLines} has no collapse to leave them to.)
40
+ * - **Length is capped.** Bounds a flooding grader. The cap is generous enough
41
+ * for real evidence prose; the untruncated value is preserved nowhere, which
42
+ * is deliberate — an artifact nobody can trust to render is worse than a
43
+ * short one.
44
+ *
45
+ * A value that was non-empty but sanitizes away to nothing becomes
46
+ * {@link UNPRINTABLE_PLACEHOLDER} rather than the empty string, so a nonsense
47
+ * grader value cannot turn a `.min(1)` schema field into a hard run failure.
48
+ *
49
+ * SCANNED, NOT REGEXED. The obvious implementation is three regexes over
50
+ * control-character classes. Those need literal ESC/NUL bytes in this source
51
+ * file (`\u` escapes get normalized INTO literal bytes by editors and tools on
52
+ * the way in), which makes the module binary to `grep` and unreviewable in a
53
+ * diff. A forward scan over code units says the same thing in plain ASCII.
54
+ *
55
+ * WHERE IT IS APPLIED. Not once, and not redundantly — each of these is a distinct
56
+ * crossing from untrusted bytes to an operator surface or a vat artifact, and none
57
+ * of them is downstream of another:
58
+ * - `eval-fragment.ts` — `parseEvalFragment` walks EVERY string entering vat from a
59
+ * grader, plus the schema path/message it quotes when the parse fails.
60
+ * - `eval-grader.ts` — the grader-invocation error text, and both sides of the
61
+ * declared-vs-observed executable-name comparison (names are compared AFTER
62
+ * sanitizing, so an invisible smuggled into one side cannot split the match).
63
+ * - `run-harness.ts` — `formatFrictionReport`, the stderr render, which re-reads
64
+ * `friction.json` from a harness dir that same-uid skill code can reach.
65
+ * - `eval-inputs.ts` — `quoteSuiteText` / `quoteSuiteBlock`, for SUITE-authored text
66
+ * (untrusted too: `resolveEvalSuitePath` will harvest a suite out of a fetched
67
+ * artifact, i.e. out of the skill under test).
68
+ * - `eval-lint.ts` — `lintToolExpectationExecutables`, via `quoteSuiteText`. Every
69
+ * executable name it quotes comes from the suite (`toolExpectations.*`, an
70
+ * unconstrained `z.array(z.string().min(1))` on a `.passthrough()` entry) or from
71
+ * the subject skill's own manifest, and `run-harness.ts` writes the advisory to
72
+ * stderr with no sanitizer of its own — at Step 5.5, AHEAD of the
73
+ * `--i-understand-this-runs-skill-code` gate and ahead of the `--dry-run`
74
+ * short-circuit, so this one paints a run that spawned nothing.
75
+ * - `baseline-integrity.ts` — `BaselineScanDegradation.detail`, built from a `cd`
76
+ * argument lifted verbatim out of the CONTROL ARM's transcript. Sanitized where the
77
+ * detail is CONSTRUCTED, not at the stderr write, so the one call covers both
78
+ * surfaces: `run-harness.ts` (`baselineContaminationFor`) writes it to stderr AND
79
+ * vat stamps the same bytes into `baselineIntegrity.degraded` in `baseline.json` —
80
+ * which a stderr-side fix would miss, because `degraded` is attached to the
81
+ * fragment AFTER `parseEvalFragment` has sanitized it.
82
+ * - `grading-adapter.ts` — the schema path and message quoted when the aggregate
83
+ * `grading.json` parse fails.
84
+ *
85
+ * That list is the whole of it, and keeping it whole is the point: this file's own
86
+ * standard is that a function's docblock and its behaviour disagreeing is how a
87
+ * security claim rots, and a LIST that has fallen behind the call sites rots the same
88
+ * way in the other direction — it under-claims, so a reader goes looking for a hole
89
+ * that was closed and trusts a boundary that was never added. `git grep
90
+ * 'sanitizeGraderText\|sanitizeTextPreservingLines'` is the check.
91
+ */
92
+ /**
93
+ * Longest grader-supplied free-text value vat will carry. ~20 terminal lines —
94
+ * enough for a real evidence excerpt, small enough that one item cannot own the
95
+ * operator's screen.
96
+ */
97
+ export const MAX_GRADER_TEXT_LENGTH = 2000;
98
+ /** Substituted for a non-empty value that sanitizes away to nothing. */
99
+ export const UNPRINTABLE_PLACEHOLDER = '(unprintable)';
100
+ const TRUNCATION_MARKER = '... (truncated)';
101
+ const ESC = 0x1b;
102
+ const BEL = 0x07;
103
+ const C0_MAX = 0x1f;
104
+ const DEL = 0x7f;
105
+ const C1_MAX = 0x9f;
106
+ const LEFT_BRACKET = 0x5b;
107
+ const RIGHT_BRACKET = 0x5d;
108
+ const BACKSLASH = 0x5c;
109
+ /** A CSI sequence ends at its first byte in `@`..`~`. */
110
+ const CSI_FINAL_MIN = 0x40;
111
+ const CSI_FINAL_MAX = 0x7e;
112
+ /** Intermediate bytes of an nF escape sequence (` `..`/`), e.g. the `(` of `ESC ( B`. */
113
+ const INTERMEDIATE_MIN = 0x20;
114
+ const INTERMEDIATE_MAX = 0x2f;
115
+ /** The leading half of a surrogate pair; see {@link capLength}. */
116
+ const HIGH_SURROGATE_MIN = 0xd800;
117
+ const HIGH_SURROGATE_MAX = 0xdbff;
118
+ /** Above this, a code point is written as a surrogate PAIR; see {@link capLength}. */
119
+ const MAX_BMP_CODE_POINT = 0xffff;
120
+ const WHITESPACE_RUN = /\s+/gu;
121
+ /** Guard against a pathologically nested grader fragment; the strict parse rejects it anyway. */
122
+ const MAX_SANITIZE_DEPTH = 20;
123
+ /** Line feed — the ONE control {@link sanitizeTextPreservingLines} keeps. */
124
+ const LINE_FEED = 0x0a;
125
+ /**
126
+ * Invisible code points above U+009F that are neutralized exactly like a C0/C1
127
+ * control, because they do the same job by other means.
128
+ *
129
+ * ⚠️ WRITTEN AS NUMBERS, like every other constant in this file, and for the same
130
+ * reason: a `\u202E` escape in a regex literal gets normalized into the real code
131
+ * point by editors and tooling on the way in, which makes the module unreviewable
132
+ * in a diff and unfindable by `grep`. Build test inputs with `String.fromCodePoint`.
133
+ *
134
+ * THE RULE, and it is not "the Cf class". Two admission tests; a code point has to
135
+ * pass one of them:
136
+ *
137
+ * (a) **It is a bidi or shaping control** — it changes how the text AROUND it is
138
+ * ordered or directed, which is the forgery this module exists to stop. U+202E
139
+ * is the verified case: it renders the REST of vat's own
140
+ * `[low] path-assumption: …` stderr line right-to-left.
141
+ * (b) **It is invisible AND spells nothing** — no word in any script is written with
142
+ * it, so removing it cannot change what a quotation says, while keeping it lets
143
+ * a grader hide a difference from a reader and from every downstream string
144
+ * comparison (`eval-grader.ts` compares tool names across both sides of a run,
145
+ * sanitized on each).
146
+ *
147
+ * Member by member:
148
+ * - U+00AD SOFT HYPHEN — (b) a hyphenation HINT; no word is spelled with one.
149
+ * - U+061C ARABIC LETTER MARK — (a) the Arabic analogue of LRM/RLM.
150
+ * - U+200B ZERO WIDTH SPACE — (b) a line-break hint, invisible, spells nothing.
151
+ * - U+200E…U+200F LRM/RLM — (a) directional MARKS: they re-resolve the direction of
152
+ * the neutral characters beside them, so they reorder text they did not write.
153
+ * - U+2028…U+2029 LINE and PARAGRAPH SEPARATOR — some renderers break a line on
154
+ * them, which is the "occupy a line of your own" attack spelled with a different
155
+ * code point. Folded to a space, never to LF, even in the line-preserving path.
156
+ * - U+202A…U+202E — (a) the bidi embeddings and OVERRIDES.
157
+ * - U+2060…U+2064 — (b) word joiner and the invisible math operators.
158
+ * - U+2066…U+2069 — (a) the bidi ISOLATES (U+2066 LRI verified surviving).
159
+ * - U+206A…U+206F — (a) the deprecated shaping controls (inhibit symmetric swapping,
160
+ * Arabic form shaping, nominal digit shapes); absent from modern text entirely.
161
+ * - U+FEFF ZERO WIDTH NO-BREAK SPACE — (b). It is `\s`, so {@link sanitizeGraderText}'s
162
+ * whitespace collapse would eat it anyway; {@link sanitizeTextPreservingLines} has
163
+ * no collapse and used to ship it to stderr intact, so it is neutralized HERE.
164
+ * - U+FFF9…U+FFFB — (b) interlinear annotation, which hides its own payload.
165
+ *
166
+ * DELIBERATELY ABSENT, because they fail BOTH tests — zero directional power, and
167
+ * real words ARE spelled with them:
168
+ * - U+FE00…U+FE0F variation selectors (category Mn, not Cf). How `⚠️` and most emoji
169
+ * are spelled, vat's own banner glyph included.
170
+ * - U+200C ZERO WIDTH NON-JOINER — orthographically load-bearing in Persian, Hindi,
171
+ * Bengali and Malayalam. `می<ZWNJ>خواهم` is two words; folding it to a space gave
172
+ * back a different string than the file the grader was quoting.
173
+ * - U+200D ZERO WIDTH JOINER — how a multi-person emoji and a flag sequence are
174
+ * spelled. Folding it turned `👨<ZWJ>👩<ZWJ>👧` into three separate glyphs.
175
+ * - U+180E MONGOLIAN VOWEL SEPARATOR — orthographic in Mongolian (it selects the
176
+ * letterform of the syllable before it) and reorders nothing.
177
+ *
178
+ * The residual cost of that exclusion is real and accepted: a grader can still
179
+ * smuggle an invisible joiner into a string a human compares by eye. It is the same
180
+ * trade the variation-selector exclusion always made, and the standard this file
181
+ * already stated — "dropping them would mangle ordinary text" — decides it the same
182
+ * way. What it must NOT do is state that standard and then break it.
183
+ */
184
+ const INVISIBLE_CODE_RANGES = [
185
+ [0x00ad, 0x00ad],
186
+ [0x061c, 0x061c],
187
+ [0x200b, 0x200b],
188
+ [0x200e, 0x200f],
189
+ [0x2028, 0x2029],
190
+ [0x202a, 0x202e],
191
+ [0x2060, 0x2064],
192
+ [0x2066, 0x206f],
193
+ [0xfeff, 0xfeff],
194
+ [0xfff9, 0xfffb],
195
+ ];
196
+ /** Lowest code point in {@link INVISIBLE_CODE_RANGES}, so ASCII bails on one compare. */
197
+ const INVISIBLE_CODE_MIN = 0x00ad;
198
+ function isInvisibleCode(code) {
199
+ if (code < INVISIBLE_CODE_MIN)
200
+ return false;
201
+ return INVISIBLE_CODE_RANGES.some(([low, high]) => code >= low && code <= high);
202
+ }
203
+ function isControlCode(code) {
204
+ return code <= C0_MAX || (code >= DEL && code <= C1_MAX) || isInvisibleCode(code);
205
+ }
206
+ /** Index of the first code unit AFTER a CSI sequence whose parameters start at `from`. */
207
+ function endOfCsi(value, from) {
208
+ let i = from;
209
+ while (i < value.length) {
210
+ const code = value.codePointAt(i) ?? Number.NaN;
211
+ i += 1;
212
+ if (code >= CSI_FINAL_MIN && code <= CSI_FINAL_MAX)
213
+ return i;
214
+ }
215
+ return i;
216
+ }
217
+ /** Index of the first code unit AFTER an OSC sequence whose payload starts at `from`. */
218
+ function endOfOsc(value, from) {
219
+ let i = from;
220
+ while (i < value.length) {
221
+ const code = value.codePointAt(i) ?? Number.NaN;
222
+ if (code === BEL)
223
+ return i + 1;
224
+ // String Terminator is ESC followed by a backslash; a bare ESC ends it too.
225
+ if (code === ESC)
226
+ return i + (value.codePointAt(i + 1) === BACKSLASH ? 2 : 1);
227
+ i += 1;
228
+ }
229
+ return i;
230
+ }
231
+ /**
232
+ * Index of the first code unit AFTER an nF sequence (`ESC` intermediates final),
233
+ * e.g. the charset selection `ESC ( B`, whose intermediates start at `from`.
234
+ */
235
+ function endOfNf(value, from) {
236
+ let i = from;
237
+ while (i < value.length) {
238
+ const code = value.codePointAt(i) ?? Number.NaN;
239
+ i += 1;
240
+ if (code < INTERMEDIATE_MIN || code > INTERMEDIATE_MAX)
241
+ return i;
242
+ }
243
+ return i;
244
+ }
245
+ /** Index of the first code unit AFTER the escape sequence introduced by the ESC at `start`. */
246
+ function endOfEscapeSequence(value, start) {
247
+ if (start + 1 >= value.length)
248
+ return start + 1;
249
+ const second = value.codePointAt(start + 1) ?? Number.NaN;
250
+ if (second === LEFT_BRACKET)
251
+ return endOfCsi(value, start + 2);
252
+ if (second === RIGHT_BRACKET)
253
+ return endOfOsc(value, start + 2);
254
+ if (second >= INTERMEDIATE_MIN && second <= INTERMEDIATE_MAX)
255
+ return endOfNf(value, start + 1);
256
+ // Any remaining two-code-unit form (RIS, NEL, ...).
257
+ return start + 2;
258
+ }
259
+ /**
260
+ * Drop every escape sequence and map every remaining control code to a space.
261
+ *
262
+ * `preserveNewlines` keeps LF — and ONLY LF — as itself, for
263
+ * {@link sanitizeTextPreservingLines}. CR still becomes a space even then: the
264
+ * line-overwrite attack it carries is not made safe by the caller wanting lines.
265
+ */
266
+ function stripEscapesAndControls(value, preserveNewlines = false) {
267
+ const out = [];
268
+ let i = 0;
269
+ while (i < value.length) {
270
+ const code = value.codePointAt(i) ?? Number.NaN;
271
+ if (code === ESC) {
272
+ i = endOfEscapeSequence(value, i);
273
+ }
274
+ else if (isControlCode(code)) {
275
+ out.push(preserveNewlines && code === LINE_FEED ? '\n' : ' ');
276
+ i += 1;
277
+ }
278
+ else {
279
+ out.push(value.charAt(i));
280
+ i += 1;
281
+ }
282
+ }
283
+ return out.join('');
284
+ }
285
+ /**
286
+ * Truncate to at most `max` code units, spending the tail on a marker that says so.
287
+ *
288
+ * THE CUT BACKS OFF ONE CODE UNIT rather than landing between the halves of a
289
+ * surrogate pair. A naive `slice` on a value ending in an astral character (1984
290
+ * ASCII characters plus one emoji is enough) leaves a LONE HIGH SURROGATE, which
291
+ * persists into `grading.json` and `baseline.json`. The artifact stays valid JSON —
292
+ * Node's `JSON.stringify` is well-formed and escapes it — but a consumer decoding
293
+ * it gets an unpaired surrogate, and stderr renders U+FFFD. Well-formedness is a
294
+ * correctness property of what this function emits, so it is enforced here.
295
+ *
296
+ * GRAPHEME CLUSTERS ARE DELIBERATELY NOT HANDLED. A cut inside a cluster — between
297
+ * a base and its combining mark, or inside a ZWJ emoji sequence (now that the
298
+ * joiners survive, see {@link INVISIBLE_CODE_RANGES}) — yields a WELL-FORMED string
299
+ * that renders as two glyphs instead of one. That is cosmetic. Segmenting for it
300
+ * would mean `Intl.Segmenter` over the whole value on every capped field, for a
301
+ * locale-dependent answer, to fix an appearance rather than a defect. The line is
302
+ * well-formedness, not beauty.
303
+ */
304
+ function capLength(value, max) {
305
+ if (value.length <= max)
306
+ return value;
307
+ const cut = max - TRUNCATION_MARKER.length;
308
+ // A high surrogate immediately before the cut has its partner AT the cut (or is
309
+ // itself already lone) — either way it must not be the last thing kept.
310
+ // `codePointAt` reports the COMBINED code point when a well-formed pair STARTS at the
311
+ // index, so a value above the BMP means the cut would fall between the two halves. A
312
+ // value still inside the high-surrogate block means that half never had a partner and
313
+ // is already lone. Either way it must not be the last thing kept. (A LOW surrogate
314
+ // reads back as itself, which is correct: its partner is at `cut - 2`, so the pair
315
+ // survives the cut intact.)
316
+ const last = value.codePointAt(cut - 1) ?? Number.NaN;
317
+ const cutSplitsPair = last > MAX_BMP_CODE_POINT || (last >= HIGH_SURROGATE_MIN && last <= HIGH_SURROGATE_MAX);
318
+ const safeCut = cutSplitsPair ? cut - 1 : cut;
319
+ return value.slice(0, safeCut) + TRUNCATION_MARKER;
320
+ }
321
+ /** Sanitize one grader-supplied string. Pure. See the module docblock for the rules. */
322
+ export function sanitizeGraderText(value) {
323
+ const collapsed = stripEscapesAndControls(value).replaceAll(WHITESPACE_RUN, ' ').trim();
324
+ if (collapsed.length === 0)
325
+ return value.length === 0 ? '' : UNPRINTABLE_PLACEHOLDER;
326
+ return capLength(collapsed, MAX_GRADER_TEXT_LENGTH);
327
+ }
328
+ /**
329
+ * Most lines {@link sanitizeTextPreservingLines} will let through, and the total
330
+ * budget across them.
331
+ *
332
+ * Two caps rather than one because the two floods are different shapes: 4000
333
+ * characters on ONE line is a wrapped paragraph, 4000 NEWLINES is the operator's
334
+ * whole scrollback. The line cap is the one that protects the screen; the length
335
+ * cap protects `Summary:` and the artifact.
336
+ */
337
+ const MAX_MULTILINE_TEXT_LINES = 40;
338
+ const MAX_MULTILINE_TEXT_LENGTH = 4000;
339
+ /**
340
+ * The LINE-PRESERVING sanitizer, for untrusted text whose STRUCTURE is the
341
+ * message.
342
+ *
343
+ * WHY IT EXISTS RATHER THAN A SECOND CALL TO {@link sanitizeGraderText}. Zod's
344
+ * `.strict()`/schema failure text is a multi-line, indented list of issues, one
345
+ * per offending path, and it is the only thing telling an adopter WHICH eval in a
346
+ * 200-eval suite is malformed. Folding it with the single-line sanitizer collapses
347
+ * every issue onto one 2000-character line and then truncates it — so the common
348
+ * case (a real typo in a real suite) gets materially worse in exchange for
349
+ * neutralizing bytes the suite author almost never wrote. This keeps the shape and
350
+ * still removes everything that can paint: escape sequences whole, every C0/C1
351
+ * control except LF, and every member of {@link INVISIBLE_CODE_RANGES}.
352
+ *
353
+ * THAT LAST CLAUSE USED TO BE FALSE, and the sibling's whitespace collapse was why.
354
+ * U+FEFF, U+2028 and U+2029 were left OUT of the scan on the grounds that they are
355
+ * `\s` and "the collapse eats them" — true of exactly one of the two consumers.
356
+ * This function has no collapse, so all three reached `process.stderr` through
357
+ * `quoteSuiteBlock` -> `EvalInputError` intact, two of them being separators some
358
+ * renderers break a line on. They are neutralized in the SCAN now, which is the only
359
+ * place both consumers share. A function's docblock and its behaviour disagreeing is
360
+ * how a security claim rots; on a terminal-bound path, the code moves to meet the
361
+ * docblock and not the other way round.
362
+ *
363
+ * It is NOT a replacement for {@link sanitizeGraderText} and must not be used for
364
+ * grader free text: a surviving newline is exactly what lets a value occupy a line
365
+ * of its own and impersonate vat's voice. Use it only where the caller's own
366
+ * message already spans lines and the untrusted part is quoted INSIDE it.
367
+ */
368
+ export function sanitizeTextPreservingLines(value) {
369
+ const stripped = stripEscapesAndControls(value, true).trim();
370
+ if (stripped.length === 0)
371
+ return value.length === 0 ? '' : UNPRINTABLE_PLACEHOLDER;
372
+ const lines = stripped.split('\n');
373
+ const kept = lines.slice(0, MAX_MULTILINE_TEXT_LINES);
374
+ if (lines.length > kept.length) {
375
+ kept.push(`... and ${lines.length - kept.length} more line(s) (truncated)`);
376
+ }
377
+ return capLength(kept.join('\n'), MAX_MULTILINE_TEXT_LENGTH);
378
+ }
379
+ function walk(value, skipKeys, depth) {
380
+ if (depth > MAX_SANITIZE_DEPTH)
381
+ return value;
382
+ if (typeof value === 'string')
383
+ return sanitizeGraderText(value);
384
+ if (Array.isArray(value))
385
+ return value.map((item) => walk(item, skipKeys, depth + 1));
386
+ if (typeof value === 'object' && value !== null) {
387
+ const out = {};
388
+ for (const [key, val] of Object.entries(value)) {
389
+ out[key] = skipKeys.has(key) ? val : walk(val, skipKeys, depth + 1);
390
+ }
391
+ return out;
392
+ }
393
+ return value;
394
+ }
395
+ /**
396
+ * Sanitize every string in an arbitrary parsed-JSON value, recursively.
397
+ *
398
+ * Deliberately field-BLIND: a per-field allowlist is a list somebody has to
399
+ * remember to extend, and these schemas grow (`mustSucceed` and
400
+ * `sequence[].steps` both arrived after the fields above them). Walking every
401
+ * string means a new grader-controlled field is covered the day it is added.
402
+ *
403
+ * `skipKeys` is REQUIRED, not defaulted — the one value that must survive
404
+ * byte-exact is the integrity nonce, and a caller that forgets it should have
405
+ * to say so.
406
+ */
407
+ export function sanitizeGraderTextDeep(value, skipKeys) {
408
+ return walk(value, skipKeys, 0);
409
+ }
410
+ //# sourceMappingURL=grader-text.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"grader-text.js","sourceRoot":"","sources":["../../src/skill-test/grader-text.ts"],"names":[],"mappings":"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"}
@@ -1,10 +1,12 @@
1
+ import { VatError } from '@vibe-agent-toolkit/utils';
1
2
  /**
2
3
  * Thrown when grading.json does not match the canonical skill-creator shape
3
4
  * (see grading-schema.ts / skill-creator references/schemas.md). vat refuses to
4
5
  * limp along on malformed grading data — a wrong shape silently flowing
5
6
  * downstream causes confusing failures far from the real cause.
6
7
  */
7
- export declare class GradingSkewError extends Error {
8
+ export declare class GradingSkewError extends VatError {
9
+ readonly reason: "internal";
8
10
  constructor(message: string);
9
11
  }
10
12
  export interface NormalizedGrading {
@@ -12,13 +14,31 @@ export interface NormalizedGrading {
12
14
  passed: number;
13
15
  total: number;
14
16
  };
17
+ /**
18
+ * Every graded expectation, flattened across all evals. `evalId` names the eval
19
+ * the entry came from — it is what lets a reader line the two `--baseline`
20
+ * artifacts up PER EVAL instead of only in aggregate. Optional for the same
21
+ * reason `runNonce` is: {@link parseGradingJson} builds a `NormalizedGrading`
22
+ * from an externally produced grading.json, which carries no such attribution.
23
+ */
15
24
  expectations: {
16
25
  text: string;
17
26
  passed: boolean;
18
27
  evidence?: string;
28
+ evalId?: string;
19
29
  }[];
20
30
  /** Per-run integrity nonce the experimenter copied from its prompt (if present). */
21
31
  runNonce?: string;
32
+ /**
33
+ * Which `--baseline` arm produced this report: `'with'` (the skill declared —
34
+ * grading.json) or `'without'` (the control — baseline.json). Stamped by
35
+ * {@link import('./fragment-merge.js').mergeFragmentsToGrading}, which is the
36
+ * only producer that knows the arm. Optional for the same reason `runNonce` is:
37
+ * {@link parseGradingJson} normalizes an externally produced grading.json that
38
+ * legitimately carries no arm, and inventing one there would be a lie about
39
+ * provenance rather than a missing field.
40
+ */
41
+ arm?: 'with' | 'without';
22
42
  }
23
43
  /**
24
44
  * Thrown when a per-eval grader fragment's integrity nonce is absent or does not
@@ -27,7 +47,23 @@ export interface NormalizedGrading {
27
47
  * prompted — most likely forged or left behind by untrusted skill code in the
28
48
  * shared sandbox — so the verdict merged from it cannot be trusted.
29
49
  */
30
- export declare class GradingNonceError extends Error {
50
+ export declare class GradingNonceError extends VatError {
51
+ readonly reason: "internal";
52
+ constructor(message: string);
53
+ }
54
+ /**
55
+ * Thrown when a per-eval grader fragment's `arm` disagrees with the arm the
56
+ * merge was told it is assembling. Deliberately distinct from
57
+ * {@link GradingNonceError}: a fragment can carry a perfectly valid nonce for
58
+ * THIS run and still belong to the other arm, and that is a different failure —
59
+ * not a forgery, but a mix-up that would fold a control-arm verdict into the
60
+ * treatment number (or the reverse). Either way the merged total is no longer a
61
+ * measurement of one arm, which is the entire premise of the `--baseline` A/B,
62
+ * so the merge refuses rather than emit a mislabelled artifact that downstream
63
+ * readers would take as authoritative.
64
+ */
65
+ export declare class GradingArmError extends VatError {
66
+ readonly reason: "internal";
31
67
  constructor(message: string);
32
68
  }
33
69
  export declare function parseGradingJson(raw: unknown): NormalizedGrading;
@@ -1 +1 @@
1
- {"version":3,"file":"grading-adapter.d.ts","sourceRoot":"","sources":["../../src/skill-test/grading-adapter.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AACH,qBAAa,gBAAiB,SAAQ,KAAK;gBAC7B,OAAO,EAAE,MAAM;CAQ5B;AAED,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IAC3C,YAAY,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,OAAO,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IACrE,oFAAoF;IACpF,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;GAMG;AACH,qBAAa,iBAAkB,SAAQ,KAAK;gBAC9B,OAAO,EAAE,MAAM;CAS5B;AAaD,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,OAAO,GAAG,iBAAiB,CAwBhE;AAED,sFAAsF;AACtF,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,OAAO,CAAC;CACpB;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,iBAAiB,GAAG,cAAc,CAqB1E"}
1
+ {"version":3,"file":"grading-adapter.d.ts","sourceRoot":"","sources":["../../src/skill-test/grading-adapter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,2BAA2B,CAAC;AAKrD;;;;;GAKG;AACH,qBAAa,gBAAiB,SAAQ,QAAQ;IAC5C,QAAQ,CAAC,MAAM,EAAG,UAAU,CAAU;gBAC1B,OAAO,EAAE,MAAM;CAQ5B;AAED,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IAC3C;;;;;;OAMG;IACH,YAAY,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,OAAO,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IACtF,oFAAoF;IACpF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;;;;;OAQG;IACH,GAAG,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC1B;AAED;;;;;;GAMG;AACH,qBAAa,iBAAkB,SAAQ,QAAQ;IAC7C,QAAQ,CAAC,MAAM,EAAG,UAAU,CAAU;gBAC1B,OAAO,EAAE,MAAM;CAS5B;AAED;;;;;;;;;;GAUG;AACH,qBAAa,eAAgB,SAAQ,QAAQ;IAC3C,QAAQ,CAAC,MAAM,EAAG,UAAU,CAAU;gBAC1B,OAAO,EAAE,MAAM;CAU5B;AAaD,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,OAAO,GAAG,iBAAiB,CAiDhE;AAED,sFAAsF;AACtF,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,OAAO,CAAC;CACpB;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,iBAAiB,GAAG,cAAc,CAqB1E"}
@@ -1,3 +1,5 @@
1
+ import { VatError } from '@vibe-agent-toolkit/utils';
2
+ import { sanitizeGraderText } from './grader-text.js';
1
3
  import { GradingReportSchema } from './grading-schema.js';
2
4
  /**
3
5
  * Thrown when grading.json does not match the canonical skill-creator shape
@@ -5,12 +7,12 @@ import { GradingReportSchema } from './grading-schema.js';
5
7
  * limp along on malformed grading data — a wrong shape silently flowing
6
8
  * downstream causes confusing failures far from the real cause.
7
9
  */
8
- export class GradingSkewError extends Error {
10
+ export class GradingSkewError extends VatError {
11
+ reason = 'internal';
9
12
  constructor(message) {
10
- super(`grading.json shape skew: ${message}. Expected skill-creator's grading.json shape ` +
13
+ super('GRADING_SKEW', `grading.json shape skew: ${message}. Expected skill-creator's grading.json shape ` +
11
14
  '(a single flat object with top-level `expectations` and `summary`); see ' +
12
15
  'docs/skill-test-grading-schema.md. Re-sync the vendored skill-creator / adopted shapes.');
13
- this.name = 'GradingSkewError';
14
16
  }
15
17
  }
16
18
  /**
@@ -20,13 +22,34 @@ export class GradingSkewError extends Error {
20
22
  * prompted — most likely forged or left behind by untrusted skill code in the
21
23
  * shared sandbox — so the verdict merged from it cannot be trusted.
22
24
  */
23
- export class GradingNonceError extends Error {
25
+ export class GradingNonceError extends VatError {
26
+ reason = 'internal';
24
27
  constructor(message) {
25
- super(`grader integrity check failed: ${message}. The harness stamps a secret per-run ` +
28
+ super('GRADING_NONCE', `grader integrity check failed: ${message}. The harness stamps a secret per-run ` +
26
29
  'nonce into each grader prompt (delivered only via stdin, never written to disk) ' +
27
30
  'and requires every per-eval grader fragment to echo it; a missing or wrong nonce ' +
28
31
  'means the fragment was not produced by the grader we prompted and is rejected.');
29
- this.name = 'GradingNonceError';
32
+ }
33
+ }
34
+ /**
35
+ * Thrown when a per-eval grader fragment's `arm` disagrees with the arm the
36
+ * merge was told it is assembling. Deliberately distinct from
37
+ * {@link GradingNonceError}: a fragment can carry a perfectly valid nonce for
38
+ * THIS run and still belong to the other arm, and that is a different failure —
39
+ * not a forgery, but a mix-up that would fold a control-arm verdict into the
40
+ * treatment number (or the reverse). Either way the merged total is no longer a
41
+ * measurement of one arm, which is the entire premise of the `--baseline` A/B,
42
+ * so the merge refuses rather than emit a mislabelled artifact that downstream
43
+ * readers would take as authoritative.
44
+ */
45
+ export class GradingArmError extends VatError {
46
+ reason = 'internal';
47
+ constructor(message) {
48
+ super('GRADING_ARM', `grader fragment arm mismatch: ${message}. Each --baseline arm is merged into its own ` +
49
+ 'artifact (grading.json = the WITH arm, baseline.json = the WITHOUT/control arm), and ' +
50
+ 'every fragment merged into one must come from that arm — a fragment with no `arm` ' +
51
+ "belongs to the default 'with' arm. Mixing arms would make the merged pass count a " +
52
+ 'measurement of neither.');
30
53
  }
31
54
  }
32
55
  /**
@@ -48,19 +71,44 @@ export function parseGradingJson(raw) {
48
71
  'grading.json must be ONE flat object whose top-level `expectations` lists every ' +
49
72
  'graded expectation across all evals');
50
73
  }
74
+ // Both halves are attacker-reachable and land in an operator-facing message: a
75
+ // `.strict()` rejection puts the grader-CHOSEN KEY inside zod's own issue message,
76
+ // and `path` is built from that same key. Sanitize both — this is the route that
77
+ // has already been found twice in sibling modules, and it must not reopen here just
78
+ // because today's only callers are tests.
51
79
  const firstIssue = result.error.issues[0];
52
- const path = firstIssue?.path.join('.') ?? '(root)';
53
- throw new GradingSkewError(`missing/invalid field at "${path}" (${firstIssue?.message ?? 'unknown'})`);
80
+ const path = sanitizeGraderText(firstIssue?.path.join('.') ?? '(root)');
81
+ const detail = sanitizeGraderText(firstIssue?.message ?? 'unknown');
82
+ throw new GradingSkewError(`missing/invalid field at "${path}" (${detail})`);
54
83
  }
55
- const { summary, expectations, runNonce } = result.data;
84
+ const { summary, expectations, runNonce, arm } = result.data;
85
+ // Pass `evalId`/`arm` through WHEN PRESENT. The docblocks on NormalizedGrading
86
+ // explain why they are optional — an externally produced grading.json legitimately
87
+ // carries no attribution, and inventing one would be a lie about provenance. That
88
+ // argues for omitting an ABSENT field, not for discarding a present one: the schema
89
+ // already parses both, so dropping them silently narrowed the declared type and
90
+ // threw away exactly the key a reader needs to line the two --baseline artifacts
91
+ // up per eval.
92
+ //
93
+ // 📌 STATE OF PLAY, so nobody mistakes this for load-bearing code: NOTHING IN
94
+ // SHIPPED CODE READS THESE TWO TODAY. `parseGradingJson` itself is reachable only
95
+ // from the package barrel and this module's own test file — the harness builds its
96
+ // NormalizedGrading from per-eval fragments, not by re-parsing an aggregate
97
+ // grading.json — so the two spreads are written-but-never-read outside tests. They
98
+ // stay anyway, because the cost of keeping a parsed field is zero and the cost of
99
+ // reopening the discard is a reader who cannot attribute an expectation to an eval
100
+ // or an arm. Treat "no production caller" as a reason to leave this alone, not as
101
+ // permission to delete it.
56
102
  return {
57
103
  summary: { passed: summary.passed, total: summary.total },
58
104
  expectations: expectations.map(e => ({
59
105
  text: e.text,
60
106
  passed: e.passed,
61
107
  ...(e.evidence === undefined ? {} : { evidence: e.evidence }),
108
+ ...(e.evalId === undefined ? {} : { evalId: e.evalId }),
62
109
  })),
63
110
  ...(runNonce === undefined ? {} : { runNonce }),
111
+ ...(arm === undefined ? {} : { arm }),
64
112
  };
65
113
  }
66
114
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"grading-adapter.js","sourceRoot":"","sources":["../../src/skill-test/grading-adapter.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"grading-adapter.js","sourceRoot":"","sources":["../../src/skill-test/grading-adapter.ts"],"names":[],"mappings":"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"}