stylegallery 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (346) hide show
  1. package/AGENTS.md +156 -0
  2. package/CATALOG.md +69 -0
  3. package/DOMAINS.md +65 -0
  4. package/GOVERNANCE.md +222 -0
  5. package/GUIDE.md +59 -0
  6. package/README.md +313 -0
  7. package/consumer-reference/adapters/style-dictionary.config.mjs +36 -0
  8. package/consumer-reference/agent-native/README.md +157 -0
  9. package/consumer-reference/agent-native/registry.json +640 -0
  10. package/consumer-reference/agent-native/schema/agent-native.schema.json +848 -0
  11. package/consumer-reference/agent-native/schema/epistemic.schema.json +14 -0
  12. package/consumer-reference/agent-native/schema/execution.schema.json +12 -0
  13. package/consumer-reference/agent-native/schema/identity.schema.json +10 -0
  14. package/consumer-reference/agent-native/schema/learning.schema.json +12 -0
  15. package/consumer-reference/agent-native/schema/manifest.schema.json +6 -0
  16. package/consumer-reference/agent-native/schema/operation.schema.json +12 -0
  17. package/consumer-reference/agent-native/schema/protocol-binding.schema.json +9 -0
  18. package/consumer-reference/agent-native/schema/retrieval.schema.json +10 -0
  19. package/consumer-reference/baselines/calibration.json +260 -0
  20. package/consumer-reference/baselines/manifest.json +21 -0
  21. package/consumer-reference/contract.md +83 -0
  22. package/consumer-reference/fixtures/consumer-conformance/e2e-fixture.mjs +282 -0
  23. package/consumer-reference/fixtures/consumer-conformance/manifest.json +102 -0
  24. package/consumer-reference/fixtures/consumer-conformance/runtime-proof.mjs +32 -0
  25. package/consumer-reference/fixtures/consumer-conformance/valid-migration-result.json +18 -0
  26. package/consumer-reference/fixtures/consumer-conformance/valid-migration.json +142 -0
  27. package/consumer-reference/fixtures/invalid-stable-ended-support.json +19 -0
  28. package/consumer-reference/fixtures/promotion/invalid-blocking-evidence.json +26 -0
  29. package/consumer-reference/fixtures/promotion/invalid-count-only.json +29 -0
  30. package/consumer-reference/fixtures/promotion/invalid-missing-owner-migration.json +25 -0
  31. package/consumer-reference/fixtures/promotion/invalid-normative-bypass-no-regression.json +25 -0
  32. package/consumer-reference/fixtures/promotion/invalid-related-counted-independent.json +45 -0
  33. package/consumer-reference/fixtures/promotion/invalid-stable-ended-support.json +26 -0
  34. package/consumer-reference/fixtures/promotion/manifest.json +15 -0
  35. package/consumer-reference/fixtures/promotion/valid-deferred-example.json +67 -0
  36. package/consumer-reference/fixtures/promotion/valid-normative-bypass.json +67 -0
  37. package/consumer-reference/fixtures/token-portability/invalid-cycle.json +7 -0
  38. package/consumer-reference/fixtures/token-portability/invalid-dangling-alias.json +6 -0
  39. package/consumer-reference/fixtures/token-portability/invalid-extends.json +4 -0
  40. package/consumer-reference/fixtures/token-portability/invalid-json-pointer.json +7 -0
  41. package/consumer-reference/fixtures/token-portability/invalid-resolver.json +9 -0
  42. package/consumer-reference/fixtures/token-portability/invalid-type-mismatch.json +10 -0
  43. package/consumer-reference/fixtures/token-portability/invalid-unknown-reserved.json +6 -0
  44. package/consumer-reference/fixtures/token-portability/invalid-untested-type.json +6 -0
  45. package/consumer-reference/fixtures/token-portability/valid-border.json +14 -0
  46. package/consumer-reference/fixtures/token-portability/valid-reference.json +32 -0
  47. package/consumer-reference/fixtures/token-portability/valid-whole-alias.json +7 -0
  48. package/consumer-reference/fixtures/valid-deprecated.json +21 -0
  49. package/consumer-reference/fixtures/valid-experimental.json +19 -0
  50. package/consumer-reference/generated/manifest.json +40 -0
  51. package/consumer-reference/generated/tokens.css +12 -0
  52. package/consumer-reference/index.md +32 -0
  53. package/consumer-reference/policies/shared-experimental.json +37 -0
  54. package/consumer-reference/schema/ax-evidence.schema.json +30 -0
  55. package/consumer-reference/schema/baseline-manifest.schema.json +55 -0
  56. package/consumer-reference/schema/calibration-record.schema.json +190 -0
  57. package/consumer-reference/schema/capture-session.schema.json +161 -0
  58. package/consumer-reference/schema/component-state.schema.json +150 -0
  59. package/consumer-reference/schema/consumer-conformance-record.schema.json +365 -0
  60. package/consumer-reference/schema/dom-evidence.schema.json +38 -0
  61. package/consumer-reference/schema/evidence-record.schema.json +81 -0
  62. package/consumer-reference/schema/fixture-manifest.schema.json +32 -0
  63. package/consumer-reference/schema/item.schema.json +188 -0
  64. package/consumer-reference/schema/page-evidence-manifest.schema.json +216 -0
  65. package/consumer-reference/schema/page-evidence-session.schema.json +119 -0
  66. package/consumer-reference/schema/portable-tokens.schema.json +104 -0
  67. package/consumer-reference/schema/promotion-rfc.schema.json +219 -0
  68. package/consumer-reference/schema/runtime-evidence-manifest.schema.json +33 -0
  69. package/consumer-reference/schema/visual-evidence.schema.json +31 -0
  70. package/design-engineering/consumer-migration-readiness.md +124 -0
  71. package/design-engineering/index.md +28 -0
  72. package/design-engineering/interface-craft.md +107 -0
  73. package/design-engineering/reference-profiles/external-adaptation/index.md +12 -0
  74. package/design-engineering/reference-profiles/governed-local/editorial/components/button.component.json +125 -0
  75. package/design-engineering/reference-profiles/governed-local/editorial/evidence/button.evidence.json +3504 -0
  76. package/design-engineering/reference-profiles/governed-local/editorial/fixtures/button.fixture.json +65 -0
  77. package/design-engineering/reference-profiles/governed-local/editorial/generated/evidence-coverage.md +23 -0
  78. package/design-engineering/reference-profiles/governed-local/editorial/generated/keyboard-matrix.md +19 -0
  79. package/design-engineering/reference-profiles/governed-local/editorial/generated/state-matrix.md +21 -0
  80. package/design-engineering/reference-profiles/governed-local/editorial/local-foundations.json +12 -0
  81. package/design-engineering/reference-profiles/governed-local/editorial/profile.json +43 -0
  82. package/design-engineering/reference-profiles/governed-local/editorial/states/button.states.json +195 -0
  83. package/design-engineering/reference-profiles/governed-local/editorial/tokens.dtcg.json +17 -0
  84. package/design-engineering/reference-profiles/governed-local/index.md +26 -0
  85. package/design-engineering/reference-profiles/governed-local/terminal/components/button.component.json +125 -0
  86. package/design-engineering/reference-profiles/governed-local/terminal/evidence/button.evidence.json +3504 -0
  87. package/design-engineering/reference-profiles/governed-local/terminal/fixtures/button.fixture.json +65 -0
  88. package/design-engineering/reference-profiles/governed-local/terminal/generated/evidence-coverage.md +23 -0
  89. package/design-engineering/reference-profiles/governed-local/terminal/generated/keyboard-matrix.md +19 -0
  90. package/design-engineering/reference-profiles/governed-local/terminal/generated/state-matrix.md +21 -0
  91. package/design-engineering/reference-profiles/governed-local/terminal/local-foundations.json +12 -0
  92. package/design-engineering/reference-profiles/governed-local/terminal/profile.json +43 -0
  93. package/design-engineering/reference-profiles/governed-local/terminal/states/button.states.json +195 -0
  94. package/design-engineering/reference-profiles/governed-local/terminal/tokens.dtcg.json +17 -0
  95. package/design-engineering/reference-profiles/index.md +19 -0
  96. package/game-ui/classification.md +82 -0
  97. package/game-ui/index.md +35 -0
  98. package/game-ui/reference-record.md +62 -0
  99. package/game-ui/screen-hierarchy.md +107 -0
  100. package/game-ui/unity/architecture.md +166 -0
  101. package/game-ui/unity/cli-loop.md +84 -0
  102. package/game-ui/unity/data/unity-technologies-public-repositories.json +26654 -0
  103. package/game-ui/unity/org-wiki.md +146 -0
  104. package/game-ui/unity/repository-map.md +102 -0
  105. package/game-ui/unity/ui-systems.md +85 -0
  106. package/guides/decision-tree.md +149 -0
  107. package/guides/documentation-mode-taxonomy.md +157 -0
  108. package/guides/layout-brief.md +220 -0
  109. package/guides/vocabulary.md +148 -0
  110. package/guides/webpage-generation-workflow.md +185 -0
  111. package/index.md +36 -0
  112. package/layout/index.md +26 -0
  113. package/motion/index.md +24 -0
  114. package/motion/practice-reference.md +145 -0
  115. package/motion/review-workflow.md +86 -0
  116. package/motion/vocabulary.md +82 -0
  117. package/package.json +78 -0
  118. package/patterns/centering/center.md +87 -0
  119. package/patterns/centering/index.md +6 -0
  120. package/patterns/centering/super-center.md +89 -0
  121. package/patterns/containment/box.md +87 -0
  122. package/patterns/containment/clamped-card.md +86 -0
  123. package/patterns/containment/content-limiter.md +87 -0
  124. package/patterns/containment/fluid-styles.md +86 -0
  125. package/patterns/containment/index.md +8 -0
  126. package/patterns/grid-repetition/card-grid.md +88 -0
  127. package/patterns/grid-repetition/columns.md +87 -0
  128. package/patterns/grid-repetition/dense-grid.md +88 -0
  129. package/patterns/grid-repetition/grid-wrapper.md +89 -0
  130. package/patterns/grid-repetition/index.md +12 -0
  131. package/patterns/grid-repetition/masonry-approx.md +87 -0
  132. package/patterns/grid-repetition/page-grid.md +90 -0
  133. package/patterns/grid-repetition/ram-grid.md +88 -0
  134. package/patterns/grid-repetition/twelve-span-grid.md +92 -0
  135. package/patterns/in-line-grouping/badge-list.md +94 -0
  136. package/patterns/in-line-grouping/breadcrumb.md +89 -0
  137. package/patterns/in-line-grouping/cluster.md +89 -0
  138. package/patterns/in-line-grouping/deconstructed-pancake.md +92 -0
  139. package/patterns/in-line-grouping/index.md +13 -0
  140. package/patterns/in-line-grouping/pagination.md +89 -0
  141. package/patterns/in-line-grouping/reel.md +90 -0
  142. package/patterns/in-line-grouping/split-nav.md +97 -0
  143. package/patterns/in-line-grouping/tab-strip.md +88 -0
  144. package/patterns/in-line-grouping/wrap-row.md +89 -0
  145. package/patterns/index.md +13 -0
  146. package/patterns/media-fit/frame.md +95 -0
  147. package/patterns/media-fit/icon-frame.md +87 -0
  148. package/patterns/media-fit/index.md +6 -0
  149. package/patterns/overlay-exception/imposter.md +95 -0
  150. package/patterns/overlay-exception/index.md +6 -0
  151. package/patterns/overlay-exception/overlay-stack.md +89 -0
  152. package/patterns/split-sidebar/index.md +12 -0
  153. package/patterns/split-sidebar/list-detail.md +95 -0
  154. package/patterns/split-sidebar/main-with-rail.md +95 -0
  155. package/patterns/split-sidebar/media-object.md +102 -0
  156. package/patterns/split-sidebar/sidebar.md +101 -0
  157. package/patterns/split-sidebar/split-screen.md +92 -0
  158. package/patterns/split-sidebar/sticky-aside.md +97 -0
  159. package/patterns/split-sidebar/supporting-pane.md +95 -0
  160. package/patterns/split-sidebar/switcher.md +93 -0
  161. package/patterns/stacking/feed.md +88 -0
  162. package/patterns/stacking/index.md +8 -0
  163. package/patterns/stacking/line-up.md +95 -0
  164. package/patterns/stacking/stack.md +87 -0
  165. package/patterns/stacking/step-nav.md +88 -0
  166. package/patterns/viewport-shell/cover.md +90 -0
  167. package/patterns/viewport-shell/fixed-sidenav-shell.md +97 -0
  168. package/patterns/viewport-shell/holy-grail.md +112 -0
  169. package/patterns/viewport-shell/index.md +11 -0
  170. package/patterns/viewport-shell/panel-layout.md +95 -0
  171. package/patterns/viewport-shell/scroll-body-shell.md +93 -0
  172. package/patterns/viewport-shell/sticky-footer.md +92 -0
  173. package/patterns/viewport-shell/sticky-header.md +86 -0
  174. package/platform-guides/apple-interaction.md +89 -0
  175. package/platform-guides/index.md +26 -0
  176. package/quality/claim-records/index.md +3 -0
  177. package/quality/claim-records/samples.md +92 -0
  178. package/quality/claim-records/stylegallery-multidomain-scope.md +42 -0
  179. package/quality/claims.md +77 -0
  180. package/quality/evidence/accessibility.md +58 -0
  181. package/quality/evidence/consumer-migration.md +42 -0
  182. package/quality/evidence/design-rationale.md +34 -0
  183. package/quality/evidence/executable-evidence.md +79 -0
  184. package/quality/evidence/families.md +56 -0
  185. package/quality/evidence/hci-evaluation.md +36 -0
  186. package/quality/evidence/index.md +16 -0
  187. package/quality/evidence/pattern-contract-audit.md +96 -0
  188. package/quality/evidence/search-metadata-polyhierarchy.md +86 -0
  189. package/quality/evidence/sources.md +43 -0
  190. package/quality/evidence/tokens.md +33 -0
  191. package/quality/evidence/visual-qa.md +41 -0
  192. package/quality/gates/accessibility-evidence.md +60 -0
  193. package/quality/gates/consumer-migration-evidence.md +53 -0
  194. package/quality/gates/design-claim.md +56 -0
  195. package/quality/gates/harmony-evaluation.md +78 -0
  196. package/quality/gates/index.md +13 -0
  197. package/quality/gates/layout.md +52 -0
  198. package/quality/gates/rationale.md +53 -0
  199. package/quality/gates/visual-evidence.md +47 -0
  200. package/quality/index.md +55 -0
  201. package/quality/principles.md +58 -0
  202. package/recipes/article-page.md +52 -0
  203. package/recipes/command-surface.md +55 -0
  204. package/recipes/dashboard.md +56 -0
  205. package/recipes/form-flow.md +55 -0
  206. package/recipes/homepage.md +89 -0
  207. package/recipes/index.md +13 -0
  208. package/recipes/list-detail.md +55 -0
  209. package/recipes/primitive-to-recipe-matrix.md +83 -0
  210. package/recipes/saas-settings.md +52 -0
  211. package/scripts/agent-native/a2a-projection.mjs +248 -0
  212. package/scripts/agent-native/agui-projection.mjs +178 -0
  213. package/scripts/agent-native/canonical-json.mjs +109 -0
  214. package/scripts/agent-native/cli-adapter.mjs +68 -0
  215. package/scripts/agent-native/events.mjs +113 -0
  216. package/scripts/agent-native/execution.mjs +227 -0
  217. package/scripts/agent-native/fixture.mjs +159 -0
  218. package/scripts/agent-native/identity.mjs +184 -0
  219. package/scripts/agent-native/immutable-store.mjs +102 -0
  220. package/scripts/agent-native/knowledge.mjs +168 -0
  221. package/scripts/agent-native/learning-records.mjs +244 -0
  222. package/scripts/agent-native/learning.mjs +247 -0
  223. package/scripts/agent-native/mcp-adapter.mjs +173 -0
  224. package/scripts/agent-native/queries.mjs +173 -0
  225. package/scripts/agent-native/registry.mjs +154 -0
  226. package/scripts/agent-native/retrieval.mjs +171 -0
  227. package/scripts/agent-native/self-description.mjs +160 -0
  228. package/scripts/artifact-metadata.mjs +20 -0
  229. package/scripts/audit-evidence-freshness.mjs +308 -0
  230. package/scripts/baseline-contract.mjs +189 -0
  231. package/scripts/baseline-schema-parity.mjs +40 -0
  232. package/scripts/build-reference-artifacts.mjs +127 -0
  233. package/scripts/calibration-raw-contract.mjs +172 -0
  234. package/scripts/capture-session-contract.mjs +187 -0
  235. package/scripts/component-state-artifact-fixture.mjs +44 -0
  236. package/scripts/component-state-contract.mjs +99 -0
  237. package/scripts/component-state-semantics.mjs +128 -0
  238. package/scripts/component-state-workflow-contract.mjs +67 -0
  239. package/scripts/component-state-workflow-negative-cases.mjs +64 -0
  240. package/scripts/consumer-conformance-contract.mjs +198 -0
  241. package/scripts/consumer-evidence-governance-contract.mjs +210 -0
  242. package/scripts/consumer-reference-case-runner.mjs +93 -0
  243. package/scripts/consumer-reference-schema.mjs +155 -0
  244. package/scripts/create-component-state-session.mjs +100 -0
  245. package/scripts/create-page-evidence-session.mjs +139 -0
  246. package/scripts/domain-document-boundaries.mjs +40 -0
  247. package/scripts/domain-validation-checks.mjs +141 -0
  248. package/scripts/evidence-artifact-contract.mjs +191 -0
  249. package/scripts/finalize-component-state-evidence.mjs +165 -0
  250. package/scripts/finalize-page-evidence.mjs +197 -0
  251. package/scripts/generate-consumer-reference-evidence.mjs +154 -0
  252. package/scripts/generate-patterns.mjs +218 -0
  253. package/scripts/governance-matrix-contract.mjs +73 -0
  254. package/scripts/governance-matrix-negative-cases.mjs +114 -0
  255. package/scripts/governance-policy-contract.mjs +49 -0
  256. package/scripts/governance-test-fixture.mjs +321 -0
  257. package/scripts/governance-workflow-negative-cases.mjs +241 -0
  258. package/scripts/governed-profile-registry.mjs +125 -0
  259. package/scripts/json-schema-formats.mjs +31 -0
  260. package/scripts/markdown-structure.mjs +45 -0
  261. package/scripts/page-artifact-metadata.mjs +124 -0
  262. package/scripts/page-evidence-contract.mjs +297 -0
  263. package/scripts/page-evidence-fixture.mjs +271 -0
  264. package/scripts/pattern-data.mjs +325 -0
  265. package/scripts/profile-record-contract.mjs +140 -0
  266. package/scripts/promotion-attestation-contract.mjs +91 -0
  267. package/scripts/promotion-boundary-test-contract.mjs +92 -0
  268. package/scripts/promotion-fixture-inventory.mjs +109 -0
  269. package/scripts/promotion-governance-contract.mjs +28 -0
  270. package/scripts/reference-artifact-path-contract.mjs +28 -0
  271. package/scripts/reference-profile-contract.mjs +114 -0
  272. package/scripts/reference-token-contract.mjs +156 -0
  273. package/scripts/run-consumer-page-evidence-ci.mjs +170 -0
  274. package/scripts/sg-mcp.mjs +16 -0
  275. package/scripts/sg.mjs +27 -0
  276. package/scripts/strict-json.mjs +72 -0
  277. package/scripts/summarize-sentinel-calibration.mjs +98 -0
  278. package/scripts/test-agent-adapter-conformance.mjs +250 -0
  279. package/scripts/test-agent-conformance-receipt.mjs +106 -0
  280. package/scripts/test-agent-events.mjs +81 -0
  281. package/scripts/test-agent-execution-effects.mjs +114 -0
  282. package/scripts/test-agent-execution-fixtures.mjs +140 -0
  283. package/scripts/test-agent-execution-kernel.mjs +39 -0
  284. package/scripts/test-agent-execution-learning.mjs +130 -0
  285. package/scripts/test-agent-execution-lifecycle.mjs +143 -0
  286. package/scripts/test-agent-execution-safety.mjs +158 -0
  287. package/scripts/test-agent-execution-views.mjs +117 -0
  288. package/scripts/test-agent-identity-integrity-cases.mjs +160 -0
  289. package/scripts/test-agent-knowledge-kernel.mjs +183 -0
  290. package/scripts/test-agent-learning.mjs +246 -0
  291. package/scripts/test-agent-mcp.mjs +246 -0
  292. package/scripts/test-agent-native-schemas.mjs +215 -0
  293. package/scripts/test-agent-projections.mjs +249 -0
  294. package/scripts/test-agent-registry-mcp-integrity.mjs +215 -0
  295. package/scripts/test-agent-registry-mutations.mjs +110 -0
  296. package/scripts/test-audit-evidence-freshness.mjs +177 -0
  297. package/scripts/test-component-state-sentinel.mjs +35 -0
  298. package/scripts/test-component-state-source-contract.mjs +83 -0
  299. package/scripts/test-consumer-conformance-sentinel.mjs +165 -0
  300. package/scripts/test-consumer-reference-sentinel.mjs +31 -0
  301. package/scripts/test-generate-consumer-reference-evidence.mjs +124 -0
  302. package/scripts/test-json-cli-output.mjs +151 -0
  303. package/scripts/test-json-schema-formats.mjs +40 -0
  304. package/scripts/test-reference-adapters.mjs +199 -0
  305. package/scripts/test-renderer-purity.mjs +34 -0
  306. package/scripts/test-sg-cli.mjs +129 -0
  307. package/scripts/test-summarize-sentinel-calibration.mjs +226 -0
  308. package/scripts/test-validate-baseline-manifest.mjs +234 -0
  309. package/scripts/test-validate-component-state-artifacts.mjs +243 -0
  310. package/scripts/test-validate-component-state.mjs +106 -0
  311. package/scripts/test-validate-consumer-conformance.mjs +427 -0
  312. package/scripts/test-validate-consumer-reference-cli.mjs +69 -0
  313. package/scripts/test-validate-consumer-reference.mjs +263 -0
  314. package/scripts/test-validate-domains.mjs +357 -0
  315. package/scripts/test-validate-governance.mjs +292 -0
  316. package/scripts/test-validate-ia.mjs +262 -0
  317. package/scripts/test-validate-okf.mjs +73 -0
  318. package/scripts/test-validate-page-evidence.mjs +302 -0
  319. package/scripts/test-validate-patterns.mjs +157 -0
  320. package/scripts/test-validate-promotion-rfc.mjs +250 -0
  321. package/scripts/test-validate-reference-profiles.mjs +170 -0
  322. package/scripts/test-validate-unity-org-wiki.mjs +57 -0
  323. package/scripts/test-validate-unity-source-contracts.mjs +67 -0
  324. package/scripts/test-validate-vocabulary.mjs +174 -0
  325. package/scripts/test-validate-webpage-workflow.mjs +293 -0
  326. package/scripts/validate-baseline-manifest.mjs +44 -0
  327. package/scripts/validate-catalog.mjs +65 -0
  328. package/scripts/validate-component-state.mjs +141 -0
  329. package/scripts/validate-consumer-conformance.mjs +477 -0
  330. package/scripts/validate-consumer-reference.mjs +288 -0
  331. package/scripts/validate-domains.mjs +237 -0
  332. package/scripts/validate-governance.mjs +248 -0
  333. package/scripts/validate-ia.mjs +136 -0
  334. package/scripts/validate-links.mjs +66 -0
  335. package/scripts/validate-okf.mjs +104 -0
  336. package/scripts/validate-page-evidence.mjs +191 -0
  337. package/scripts/validate-patterns.mjs +246 -0
  338. package/scripts/validate-promotion-rfc.mjs +224 -0
  339. package/scripts/validate-reference-artifacts.mjs +125 -0
  340. package/scripts/validate-renderer-purity.mjs +56 -0
  341. package/scripts/validate-unity-org-wiki.mjs +146 -0
  342. package/scripts/validate-unity-source-contracts.mjs +107 -0
  343. package/scripts/validate-vocabulary.mjs +115 -0
  344. package/scripts/validate-webpage-workflow.mjs +254 -0
  345. package/scripts/visual-expectation-contract.mjs +20 -0
  346. package/scripts/workflow-action-contract.mjs +163 -0
@@ -0,0 +1,173 @@
1
+ import { canonicalize, deepFreeze } from "./canonical-json.mjs";
2
+ import { parseStableRef, parseVersionId } from "./identity.mjs";
3
+ import { aggregateEvidence } from "./knowledge.mjs";
4
+ import { buildContextPackage, buildViewSnapshot } from "./retrieval.mjs";
5
+
6
+ export class QueryError extends TypeError {
7
+ constructor(code, message, recordPath = "") {
8
+ super(message);
9
+ this.name = "QueryError";
10
+ this.code = code;
11
+ if (recordPath) this.path = recordPath;
12
+ }
13
+ }
14
+
15
+ function fail(code, message, recordPath = "") {
16
+ throw new QueryError(code, message, recordPath);
17
+ }
18
+
19
+ function compare(left, right) {
20
+ return left < right ? -1 : left > right ? 1 : 0;
21
+ }
22
+
23
+ function inputReference(input) {
24
+ if (typeof input === "string") return input;
25
+ if (!input || typeof input !== "object" || Array.isArray(input)) return undefined;
26
+ return input.reference ?? input.ref ?? input.stable_ref ?? input.stableRef ?? input.version_id ?? input.versionId;
27
+ }
28
+
29
+ function indexFixture(fixture) {
30
+ if (!fixture || !Array.isArray(fixture.records)) fail("registry_fixture_invalid", "fixture.records must be an array");
31
+ const records = [...fixture.records].sort((left, right) => compare(left.stable_ref, right.stable_ref) || compare(left.version_id, right.version_id));
32
+ const byVersion = new Map(records.map((record) => [record.version_id, record]));
33
+ const byStableRef = new Map();
34
+ for (const record of records) byStableRef.set(record.stable_ref, record);
35
+ return { byStableRef, byVersion, records };
36
+ }
37
+
38
+ function normalizeReference(input) {
39
+ const reference = inputReference(input);
40
+ if (typeof reference !== "string" || reference.length === 0) {
41
+ fail("argument_value_required", "a StableRef or VersionID is required", "reference");
42
+ }
43
+ if (reference.includes("@")) {
44
+ try { return { reference, version: true, stableRef: parseVersionId(reference).stable_ref }; } catch {
45
+ fail("version_id_malformed", "reference must be StableRef@sha256:<64 lowercase hex>", "reference");
46
+ }
47
+ }
48
+ try { return { reference, version: false, stableRef: parseStableRef(reference).stable_ref }; } catch {
49
+ fail("stable_ref_malformed", "reference must match sg:<kind>/<kebab-id>", "reference");
50
+ }
51
+ }
52
+
53
+ export function resolveRecord(fixture, input) {
54
+ const normalized = normalizeReference(input);
55
+ const index = indexFixture(fixture);
56
+ const record = normalized.version ? index.byVersion.get(normalized.reference) : index.byStableRef.get(normalized.stableRef);
57
+ if (!record) {
58
+ const code = normalized.version ? "version_id_unknown" : "stable_ref_unknown";
59
+ fail(code, `no immutable fixture record exists for ${normalized.reference}`, "reference");
60
+ }
61
+ return record;
62
+ }
63
+
64
+ function linked(records, kind, claimRef) {
65
+ return records.filter((record) => record.record_kind === kind && record.claim_ref === claimRef);
66
+ }
67
+
68
+ function claimsFor(index, subject) {
69
+ if (subject.record_kind === "claim") return [subject];
70
+ const declared = new Set(Array.isArray(subject.claim_refs) ? subject.claim_refs : []);
71
+ return index.records.filter((record) => record.record_kind === "claim"
72
+ && (record.subject_ref === subject.stable_ref || declared.has(record.stable_ref)));
73
+ }
74
+
75
+ export function queryClaims(fixture, input) {
76
+ const subject = resolveRecord(fixture, input);
77
+ const index = indexFixture(fixture);
78
+ const claims = claimsFor(index, subject).map((claim) => {
79
+ const evidence = linked(index.records, "evidence_link", claim.stable_ref);
80
+ const validations = linked(index.records, "validation_report", claim.stable_ref);
81
+ const governance = linked(index.records, "governance_decision", claim.stable_ref);
82
+ const policy = linked(index.records, "policy_disposition", claim.stable_ref);
83
+ return {
84
+ aggregate: aggregateEvidence({ claim, evidence_links: evidence }),
85
+ claim,
86
+ evidence,
87
+ governance,
88
+ policy_dispositions: policy,
89
+ validations,
90
+ };
91
+ });
92
+ return deepFreeze({ claims, subject_ref: subject.stable_ref, subject_version_id: subject.version_id });
93
+ }
94
+
95
+ function contextMembers(fixture, subject) {
96
+ const graph = queryClaims(fixture, subject.stable_ref);
97
+ const related = graph.claims.flatMap((entry) => [
98
+ entry.claim,
99
+ ...entry.evidence,
100
+ ...entry.validations,
101
+ ...entry.governance,
102
+ ...entry.policy_dispositions,
103
+ ]);
104
+ const unique = new Map([subject, ...related].map((record) => [record.version_id, record]));
105
+ return [...unique.values()];
106
+ }
107
+
108
+ function refSlug(stableRef) {
109
+ return stableRef.replace(/^sg:/, "").replace("/", "-");
110
+ }
111
+
112
+ export function queryContext(fixture, input) {
113
+ const subject = resolveRecord(fixture, input);
114
+ const members = contextMembers(fixture, subject);
115
+ const slug = refSlug(subject.stable_ref);
116
+ const snapshot = buildViewSnapshot({
117
+ heads: [fixture.manifest.version_id],
118
+ max_members: 64,
119
+ members,
120
+ policy: "sg:policy/editorial-profile-read",
121
+ snapshot_ref: `sg:view/${slug}-snapshot`,
122
+ view_spec: { stable_ref: `sg:view/${slug}-snapshot`, subject_ref: subject.stable_ref },
123
+ });
124
+ return buildContextPackage({
125
+ budget: { tokens: 32768 },
126
+ context_ref: `sg:view/${slug}-context`,
127
+ members,
128
+ policy: "sg:policy/editorial-profile-read",
129
+ query: subject.stable_ref,
130
+ query_class: "stable_ref",
131
+ retriever: "fixture-registry@1",
132
+ snapshot,
133
+ });
134
+ }
135
+
136
+ export function queryOperations(operationSpecs) {
137
+ if (!Array.isArray(operationSpecs)) fail("registry_operations_invalid", "operation specs must be an array");
138
+ const operations = [...operationSpecs].sort((left, right) => compare(left.name, right.name));
139
+ return deepFreeze({ operations });
140
+ }
141
+
142
+ export function queryDiscover(description) {
143
+ if (!description || typeof description !== "object" || Array.isArray(description)) {
144
+ fail("self_description_invalid", "discover requires a self-description object");
145
+ }
146
+ return description;
147
+ }
148
+
149
+ export function queryRetrieve(fixture, input = {}) {
150
+ if (!input || typeof input !== "object" || Array.isArray(input)) fail("retrieve_input_invalid", "retrieve input must be an object");
151
+ const query = input.query ?? "";
152
+ if (typeof query !== "string") fail("retrieve_query_invalid", "retrieve query must be a string", "query");
153
+ const limit = input.limit ?? 32;
154
+ if (!Number.isInteger(limit) || limit < 0 || limit > 256) fail("retrieve_limit_invalid", "retrieve limit must be an integer from 0 to 256", "limit");
155
+ const needle = query.toLowerCase();
156
+ const members = indexFixture(fixture).records
157
+ .filter((record) => needle.length === 0 || canonicalize(record).toLowerCase().includes(needle))
158
+ .slice(0, limit);
159
+ return buildViewSnapshot({
160
+ heads: [fixture.manifest.version_id],
161
+ max_members: limit,
162
+ members,
163
+ snapshot_ref: "sg:view/registry-retrieval",
164
+ view_spec: { query, stable_ref: "sg:view/registry-retrieval" },
165
+ });
166
+ }
167
+
168
+ export const resolve = resolveRecord;
169
+ export const claims = queryClaims;
170
+ export const context = queryContext;
171
+ export const ops = queryOperations;
172
+ export const discover = queryDiscover;
173
+ export const retrieve = queryRetrieve;
@@ -0,0 +1,154 @@
1
+ import { deepFreeze } from "./canonical-json.mjs";
2
+ import {
3
+ createTask,
4
+ reconcileEffect,
5
+ recordEffectAttempt,
6
+ startRun,
7
+ } from "./execution.mjs";
8
+ import { agentNativeFixture } from "./fixture.mjs";
9
+ import {
10
+ createProposal,
11
+ decideProposal,
12
+ promoteProposal,
13
+ verifyProposal,
14
+ } from "./learning.mjs";
15
+ import {
16
+ queryClaims,
17
+ queryContext,
18
+ queryDiscover,
19
+ queryOperations,
20
+ queryRetrieve,
21
+ resolveRecord,
22
+ } from "./queries.mjs";
23
+ import { createSelfDescription } from "./self-description.mjs";
24
+
25
+ export class RegistryError extends TypeError {
26
+ constructor(code, message, recordPath = "") {
27
+ super(message);
28
+ this.name = "RegistryError";
29
+ this.code = code;
30
+ if (recordPath) this.path = recordPath;
31
+ }
32
+ }
33
+
34
+ function failure(code, message, recordPath = "") {
35
+ return recordPath ? { code, message, path: recordPath } : { code, message };
36
+ }
37
+
38
+ function compareFailures(left, right) {
39
+ const leftKey = `${left.code}\u0000${left.path ?? ""}\u0000${left.message}`;
40
+ const rightKey = `${right.code}\u0000${right.path ?? ""}\u0000${right.message}`;
41
+ return leftKey < rightKey ? -1 : leftKey > rightKey ? 1 : 0;
42
+ }
43
+
44
+ function failed(operation, failures) {
45
+ return deepFreeze({ failures: [...failures].sort(compareFailures), ok: false, operation });
46
+ }
47
+
48
+ function succeeded(operation, result) {
49
+ return deepFreeze({ ok: true, operation, result });
50
+ }
51
+
52
+ function validateValue(schema, value, recordPath) {
53
+ if (schema.type === "string" && typeof value !== "string") return failure("operation_input_invalid", `${recordPath} must be a string`, recordPath);
54
+ if (schema.type === "integer" && !Number.isInteger(value)) return failure("operation_input_invalid", `${recordPath} must be an integer`, recordPath);
55
+ if (typeof schema.minimum === "number" && value < schema.minimum) return failure("operation_input_invalid", `${recordPath} must be at least ${schema.minimum}`, recordPath);
56
+ return undefined;
57
+ }
58
+
59
+ function validateInput(spec, input) {
60
+ if (!input || typeof input !== "object" || Array.isArray(input)) {
61
+ return [failure("operation_input_invalid", "operation input must be an object")];
62
+ }
63
+ const schema = spec.input_schema ?? { type: "object" };
64
+ const properties = schema.properties ?? {};
65
+ const failures = [];
66
+ for (const key of schema.required ?? []) {
67
+ if (!Object.hasOwn(input, key)) failures.push(failure("operation_input_required", `${key} is required`, key));
68
+ }
69
+ if (schema.additionalProperties === false) {
70
+ for (const key of Object.keys(input)) if (!Object.hasOwn(properties, key)) {
71
+ failures.push(failure("operation_input_unknown", `${key} is not accepted by ${spec.name}`, key));
72
+ }
73
+ }
74
+ for (const [key, value] of Object.entries(input)) {
75
+ if (!Object.hasOwn(properties, key)) continue;
76
+ const issue = validateValue(properties[key], value, key);
77
+ if (issue) failures.push(issue);
78
+ }
79
+ return failures;
80
+ }
81
+
82
+ function handlers(fixture, operations, selfDescription) {
83
+ return new Map([
84
+ ["claims", (input) => queryClaims(fixture, input)],
85
+ ["context", (input) => queryContext(fixture, input)],
86
+ ["discover", () => queryDiscover(selfDescription)],
87
+ ["effect.record", (input) => recordEffectAttempt(input)],
88
+ ["effect.reconcile", (input) => reconcileEffect(input)],
89
+ ["ops", () => queryOperations(operations)],
90
+ ["proposal.create", (input) => createProposal(input)],
91
+ ["proposal.decide", (input) => decideProposal(input)],
92
+ ["proposal.promote", (input) => promoteProposal(input)],
93
+ ["proposal.verify", (input) => verifyProposal(input)],
94
+ ["resolve", (input) => resolveRecord(fixture, input)],
95
+ ["retrieve", (input) => queryRetrieve(fixture, input)],
96
+ ["run.start", (input) => startRun(input)],
97
+ ["task.create", (input) => createTask(input)],
98
+ ]);
99
+ }
100
+
101
+ function assertOperationMetadata(operations, operationHandlers) {
102
+ for (const operation of operations) {
103
+ if (typeof operation.read_only !== "boolean") {
104
+ throw new RegistryError("operation_read_only_required", `${operation.name} must declare read_only`);
105
+ }
106
+ if (operation.read_only && operation.effect_class !== "NONE") {
107
+ throw new RegistryError("operation_read_only_effect_invalid", `${operation.name} cannot be read-only with ${operation.effect_class} effects`);
108
+ }
109
+ if (!operationHandlers.has(operation.name)) {
110
+ throw new RegistryError("operation_handler_missing", `${operation.name} has no common-registry handler`);
111
+ }
112
+ }
113
+ }
114
+
115
+ export function createOperationRegistry({ fixture = agentNativeFixture } = {}) {
116
+ const operations = fixture.records
117
+ .filter((record) => record.record_kind === "operation")
118
+ .sort((left, right) => left.name < right.name ? -1 : left.name > right.name ? 1 : 0);
119
+ if (new Set(operations.map((operation) => operation.name)).size !== operations.length) {
120
+ throw new RegistryError("operation_name_duplicate", "operation names must be unique");
121
+ }
122
+ const selfDescription = createSelfDescription({ fixture, operations });
123
+ const operationByName = new Map(operations.map((operation) => [operation.name, operation]));
124
+ const operationHandlers = handlers(fixture, operations, selfDescription);
125
+ assertOperationMetadata(operations, operationHandlers);
126
+ const invoke = (operation, input = {}) => {
127
+ if (typeof operation !== "string" || operation.length === 0) {
128
+ return failed(null, [failure("operation_required", "operation name is required")]);
129
+ }
130
+ const spec = operationByName.get(operation);
131
+ if (!spec || !operationHandlers.has(operation)) {
132
+ return failed(operation, [failure("operation_unknown", `unknown operation ${operation}`, "operation")]);
133
+ }
134
+ const inputFailures = validateInput(spec, input);
135
+ if (inputFailures.length > 0) return failed(operation, inputFailures);
136
+ try { return succeeded(operation, operationHandlers.get(operation)(input)); } catch (error) {
137
+ return failed(operation, [failure(error?.code ?? "operation_failed", error instanceof Error ? error.message : String(error), error?.path)]);
138
+ }
139
+ };
140
+ return Object.freeze({ fixture, invoke, operations: deepFreeze([...operations]), selfDescription });
141
+ }
142
+
143
+ export const agentNativeRegistry = createOperationRegistry();
144
+ export const readOnlyOperationNames = deepFreeze(agentNativeRegistry.operations
145
+ .filter((operation) => operation.read_only)
146
+ .map((operation) => operation.name));
147
+
148
+ export function invokeOperation(operation, input = {}, registry = agentNativeRegistry) {
149
+ if (!registry || typeof registry.invoke !== "function") throw new RegistryError("registry_invalid", "registry must expose invoke");
150
+ return registry.invoke(operation, input);
151
+ }
152
+
153
+ export const invokeRegistryOperation = invokeOperation;
154
+ export const operationRegistry = agentNativeRegistry;
@@ -0,0 +1,171 @@
1
+ import { cloneAndFreeze, canonicalize, hashCanonical } from "./canonical-json.mjs";
2
+ import { createStableRef } from "./identity.mjs";
3
+
4
+ function pick(value, ...keys) {
5
+ if (!value || typeof value !== "object") return undefined;
6
+ for (const key of keys) if (Object.hasOwn(value, key)) return value[key];
7
+ return undefined;
8
+ }
9
+
10
+ function objectInput(value, name) {
11
+ if (!value || typeof value !== "object" || Array.isArray(value)) throw new TypeError(`${name} expects an object`);
12
+ return value;
13
+ }
14
+
15
+ function copy(value) {
16
+ return value === undefined ? undefined : structuredClone(value);
17
+ }
18
+
19
+ function canonicalFreeze(value) {
20
+ return cloneAndFreeze(JSON.parse(canonicalize(value)));
21
+ }
22
+
23
+ function refOf(value) {
24
+ return pick(value, "stable_ref", "stableRef", "ref", "id") ?? "";
25
+ }
26
+
27
+ function versionOf(value) {
28
+ return pick(value, "version_id", "versionId", "version") ?? "";
29
+ }
30
+
31
+ function memberReference(member) {
32
+ const reference = {};
33
+ const stableRef = refOf(member);
34
+ const versionId = versionOf(member);
35
+ const contentHash = pick(member, "content_sha256", "contentHash", "sha256");
36
+ if (stableRef) reference.stable_ref = stableRef;
37
+ if (versionId) reference.version_id = versionId;
38
+ if (contentHash) reference.content_sha256 = contentHash;
39
+ return reference;
40
+ }
41
+
42
+ function sortedUniqueStrings(value) {
43
+ if (value === undefined || value === null) return [];
44
+ if (!Array.isArray(value)) throw new TypeError("heads must be an array");
45
+ return [...new Set(value.map((item) => {
46
+ if (typeof item !== "string" || item.length === 0) throw new TypeError("head IDs must be non-empty strings");
47
+ return item;
48
+ }))].sort();
49
+ }
50
+
51
+ function normalizeMembers(value, maxMembers = 256) {
52
+ const source = value ?? [];
53
+ if (!Array.isArray(source)) throw new TypeError("members must be an array");
54
+ if (!Number.isInteger(maxMembers) || maxMembers < 0) throw new TypeError("maxMembers must be a non-negative integer");
55
+ const members = source.map((member) => {
56
+ if (!member || typeof member !== "object" || Array.isArray(member)) throw new TypeError("members must contain objects");
57
+ return copy(member);
58
+ });
59
+ members.sort((left, right) => {
60
+ const leftRef = String(refOf(left));
61
+ const rightRef = String(refOf(right));
62
+ const byRef = leftRef < rightRef ? -1 : leftRef > rightRef ? 1 : 0;
63
+ if (byRef) return byRef;
64
+ const leftVersion = String(versionOf(left));
65
+ const rightVersion = String(versionOf(right));
66
+ const byVersion = leftVersion < rightVersion ? -1 : leftVersion > rightVersion ? 1 : 0;
67
+ if (byVersion) return byVersion;
68
+ const leftCanonical = canonicalize(left);
69
+ const rightCanonical = canonicalize(right);
70
+ return leftCanonical < rightCanonical ? -1 : leftCanonical > rightCanonical ? 1 : 0;
71
+ });
72
+ return { members: members.slice(0, maxMembers), truncated: members.length > maxMembers };
73
+ }
74
+
75
+ function snapshotIdentity(payload, stableRef, recordKind) {
76
+ const versionPayload = {
77
+ schema_version: "1.0",
78
+ record_kind: recordKind,
79
+ stable_ref: stableRef,
80
+ ...payload,
81
+ };
82
+ const digest = hashCanonical(versionPayload);
83
+ return {
84
+ version_id: `${stableRef}@${digest}`,
85
+ sha256: digest,
86
+ cache_key: `${stableRef}@${digest}`,
87
+ };
88
+ }
89
+
90
+ /** Build a bounded, immutable, source-preserving retrieval view. */
91
+ export function buildViewSnapshot(input) {
92
+ const source = objectInput(input, "buildViewSnapshot");
93
+ const rawView = pick(source, "view_spec", "viewSpec") ?? {};
94
+ const viewSpec = copy(rawView);
95
+ const stableRef = pick(source, "snapshot_ref", "snapshotRef")
96
+ ?? pick(viewSpec, "stable_ref", "stableRef")
97
+ ?? "sg:view/snapshot";
98
+ const canonicalStableRef = createStableRef(stableRef);
99
+ const requestedLimit = pick(source, "max_members", "maxMembers", "limit");
100
+ const maxMembers = requestedLimit === undefined ? 256 : Number(requestedLimit);
101
+ if (!Number.isInteger(maxMembers) || maxMembers < 0) throw new TypeError("maxMembers must be a non-negative integer");
102
+ const normalized = normalizeMembers(pick(source, "members", "entries", "items"), maxMembers);
103
+ const payload = {
104
+ as_of: pick(source, "as_of", "asOf"),
105
+ builder_version: pick(source, "builder_version", "builderVersion") ?? "retriever@1",
106
+ heads: sortedUniqueStrings(pick(source, "heads", "event_heads", "eventHeads")),
107
+ members: normalized.members,
108
+ policy: copy(pick(source, "policy", "policy_ref", "policyRef")),
109
+ snapshot_ref: canonicalStableRef,
110
+ truncated: normalized.truncated,
111
+ transaction_at: pick(source, "transaction_at", "transactionAt", "transaction_watermark", "transactionWatermark"),
112
+ valid_at: pick(source, "valid_at", "validAt", "valid_watermark", "validWatermark"),
113
+ view_spec: viewSpec,
114
+ };
115
+ for (const [key, value] of Object.entries(payload)) if (value === undefined) delete payload[key];
116
+ const supplied = pick(source, "version_id", "versionId");
117
+ const identity = snapshotIdentity(payload, canonicalStableRef, "view_snapshot");
118
+ if (supplied !== undefined && supplied !== identity.version_id) throw new TypeError("snapshot version_id does not match content");
119
+ return canonicalFreeze({ schema_version: "1.0", record_kind: "view_snapshot", stable_ref: canonicalStableRef, ...payload, ...identity });
120
+ }
121
+
122
+ export const createViewSnapshot = buildViewSnapshot;
123
+
124
+ function estimateTokens(member) {
125
+ const text = pick(member, "content", "text", "summary") ?? canonicalize(member);
126
+ return Math.max(1, Math.ceil(String(text).length / 4));
127
+ }
128
+
129
+ /** Build a deterministic, budget-bounded context package linked to a snapshot. */
130
+ export function buildContextPackage(input) {
131
+ const source = objectInput(input, "buildContextPackage");
132
+ const snapshot = pick(source, "snapshot");
133
+ const view = snapshot && typeof snapshot === "object"
134
+ ? snapshot
135
+ : buildViewSnapshot({ ...source, viewSpec: { stableRef: "sg:view/context" } });
136
+ const suppliedMembers = pick(source, "members", "entries", "items") ?? pick(view, "members", "entries", "items") ?? [];
137
+ const normalized = normalizeMembers(suppliedMembers, Number(pick(source, "max_members", "maxMembers") ?? 256));
138
+ const budgetInput = pick(source, "budget") ?? {};
139
+ const tokenBudget = Number(typeof budgetInput === "number" ? budgetInput : pick(budgetInput, "tokens", "token_budget", "tokenBudget") ?? 0);
140
+ if (!Number.isInteger(tokenBudget) || tokenBudget < 0) throw new TypeError("budget.tokens must be a non-negative integer");
141
+ let usedTokens = 0;
142
+ const members = [];
143
+ for (const member of normalized.members) {
144
+ const cost = estimateTokens(member);
145
+ if (usedTokens + cost > tokenBudget) break;
146
+ members.push(member);
147
+ usedTokens += cost;
148
+ }
149
+ const payload = {
150
+ budget: { tokens: tokenBudget, used_tokens: usedTokens, truncated: members.length < normalized.members.length },
151
+ members,
152
+ member_refs: members.map(memberReference).filter((member) => Object.keys(member).length > 0),
153
+ member_manifest: members.map(memberReference).filter((member) => Object.keys(member).length > 0),
154
+ policy: copy(pick(source, "policy", "policy_ref", "policyRef") ?? pick(view, "policy", "policy_ref", "policyRef")),
155
+ query: pick(source, "query"),
156
+ query_class: pick(source, "query_class", "queryClass"),
157
+ retriever: pick(source, "retriever"),
158
+ selectors: copy(pick(source, "selectors", "selector")),
159
+ snapshot: pick(view, "version_id", "versionId") ?? pick(view, "snapshot_ref", "snapshotRef", "stable_ref", "stableRef"),
160
+ snapshot_ref: pick(view, "snapshot_ref", "snapshotRef", "stable_ref", "stableRef"),
161
+ snapshot_version_id: pick(view, "version_id", "versionId"),
162
+ assembly_receipt: copy(pick(source, "assembly_receipt", "assemblyReceipt")),
163
+ };
164
+ for (const [key, value] of Object.entries(payload)) if (value === undefined) delete payload[key];
165
+ const stableRef = pick(source, "context_ref", "contextRef") ?? "sg:view/context";
166
+ const canonicalStableRef = createStableRef(stableRef);
167
+ const identity = snapshotIdentity(payload, canonicalStableRef, "context_package");
168
+ return canonicalFreeze({ schema_version: "1.0", record_kind: "context_package", stable_ref: canonicalStableRef, ...payload, ...identity });
169
+ }
170
+
171
+ export const createContextPackage = buildContextPackage;
@@ -0,0 +1,160 @@
1
+ import crypto from "node:crypto";
2
+ import { deepFreeze, hashCanonical } from "./canonical-json.mjs";
3
+ import { createVersionId } from "./identity.mjs";
4
+
5
+ export const AGENT_REF = "sg:agent/stylegallery";
6
+ export const CONFORMANCE_PROFILE_REF = "sg:profile/agent-native-read-conformance";
7
+
8
+ export class SelfDescriptionError extends TypeError {
9
+ constructor(code, message) {
10
+ super(message);
11
+ this.name = "SelfDescriptionError";
12
+ this.code = code;
13
+ }
14
+ }
15
+
16
+ function fail(code, message) {
17
+ throw new SelfDescriptionError(code, message);
18
+ }
19
+
20
+ function byRef(fixture, stableRef) {
21
+ const record = fixture.records.find((item) => item.stable_ref === stableRef);
22
+ if (!record) fail("self_description_record_missing", `required fixture record ${stableRef} is missing`);
23
+ return record;
24
+ }
25
+
26
+ function sha256(value) {
27
+ return crypto.createHash("sha256").update(value).digest("hex");
28
+ }
29
+
30
+ function receiptVersion(receipt) {
31
+ const payload = { ...receipt };
32
+ delete payload.version_id;
33
+ delete payload.versionId;
34
+ return createVersionId({ stableRef: receipt.stable_ref, payload });
35
+ }
36
+
37
+ /**
38
+ * Verify an execution receipt without executing its command again. The
39
+ * receipt is an immutable, content-addressed observation: its fixture source
40
+ * VersionID, command, normalized result digest, and independent verifier are
41
+ * all checked before a conformance claim can be projected as verified.
42
+ */
43
+ export function verifyConformanceReceipt({ fixture, receipt, source }) {
44
+ if (!receipt || receipt.record_kind !== "execution_receipt") {
45
+ fail("self_description_receipt_kind_invalid", "conformance evidence must be an execution receipt");
46
+ }
47
+ if (!source || source.record_kind !== "executable_fixture") {
48
+ fail("self_description_receipt_source_invalid", "execution receipt must reference an executable fixture");
49
+ }
50
+ if (receipt.fixture_ref !== fixture.fixture_ref) {
51
+ fail("self_description_receipt_fixture_mismatch", "execution receipt fixture_ref does not match the loaded fixture");
52
+ }
53
+ if (receipt.fixture_record_ref !== source.stable_ref || receipt.fixture_version_id !== source.version_id) {
54
+ fail("self_description_receipt_version_mismatch", "execution receipt is not bound to the executable fixture VersionID");
55
+ }
56
+ if (receipt.command !== source.command) {
57
+ fail("self_description_receipt_command_mismatch", "execution receipt command does not match the executable fixture");
58
+ }
59
+ const result = receipt.result;
60
+ if (!result || typeof result !== "object" || Array.isArray(result)) {
61
+ fail("self_description_receipt_result_invalid", "execution receipt must contain a normalized result object");
62
+ }
63
+ if (result.exit_code !== source.expected?.exit_code || result.report_ok !== source.expected?.report_ok) {
64
+ fail("self_description_receipt_result_mismatch", "execution receipt result does not match fixture expectations");
65
+ }
66
+ const expectedStderrHash = sha256(String(source.expected?.stderr ?? ""));
67
+ if (result.stderr_sha256 !== expectedStderrHash) {
68
+ fail("self_description_receipt_stderr_mismatch", "execution receipt stderr digest does not match fixture expectations");
69
+ }
70
+ if (typeof result.stderr_sha256 !== "string" || !/^[a-f0-9]{64}$/.test(result.stderr_sha256)) {
71
+ fail("self_description_receipt_digest_invalid", "execution receipt stderr digest must be lowercase sha256 hex");
72
+ }
73
+ if (receipt.result_sha256 !== hashCanonical(result)) {
74
+ fail("self_description_receipt_result_digest_mismatch", "execution receipt result_sha256 does not match canonical result bytes");
75
+ }
76
+ if (receipt.version_id !== receiptVersion(receipt)) {
77
+ fail("self_description_receipt_version_digest_mismatch", "execution receipt VersionID does not match immutable receipt bytes");
78
+ }
79
+ const verification = receipt.verification;
80
+ if (!verification || verification.status !== "PASS" || verification.method !== "captured-command-result") {
81
+ fail("self_description_receipt_unverified", "execution receipt must carry a PASS captured-command verification");
82
+ }
83
+ const verifier = fixture.records.find((record) => record.stable_ref === verification.verifier_ref);
84
+ if (!verifier || verifier.record_kind !== "validator" || verifier.validator_version !== verification.verifier_version) {
85
+ fail("self_description_receipt_verifier_invalid", "execution receipt verifier is not a fixture validator version");
86
+ }
87
+ return deepFreeze({ receipt, source, verifier });
88
+ }
89
+
90
+ function conformanceClaims(fixture, profile) {
91
+ const refs = Array.isArray(profile.claim_refs) ? profile.claim_refs : [];
92
+ return refs.map((claimRef) => {
93
+ const claim = byRef(fixture, claimRef);
94
+ const evidence = fixture.records.filter((record) => record.record_kind === "evidence_link" && record.claim_ref === claimRef);
95
+ const sources = evidence.map((link) => byRef(fixture, link.source_ref));
96
+ if (sources.length === 0 || sources.some((source) => source.record_kind !== "executable_fixture")) {
97
+ fail("self_description_evidence_not_executable", `conformance claim ${claimRef} must point only at executable fixture evidence`);
98
+ }
99
+ const validations = fixture.records.filter((record) => record.record_kind === "validation_report" && record.claim_ref === claimRef);
100
+ const execution_receipts = sources.map((source) => {
101
+ const receipts = fixture.records.filter((record) => record.record_kind === "execution_receipt" && record.fixture_record_ref === source.stable_ref);
102
+ if (receipts.length !== 1) {
103
+ fail("self_description_execution_receipt_missing", `conformance source ${source.stable_ref} must have exactly one immutable execution receipt`);
104
+ }
105
+ return verifyConformanceReceipt({ fixture, receipt: receipts[0], source });
106
+ });
107
+ return { claim, evidence, executable_sources: sources, execution_receipts, validations };
108
+ });
109
+ }
110
+
111
+ function operationSummary(operation) {
112
+ return {
113
+ adapters: operation.adapters,
114
+ effect_class: operation.effect_class,
115
+ idempotent: operation.idempotent,
116
+ name: operation.name,
117
+ read_only: operation.read_only,
118
+ stable_ref: operation.stable_ref,
119
+ version_id: operation.version_id,
120
+ };
121
+ }
122
+
123
+ export function createSelfDescription({ fixture, operations }) {
124
+ if (!fixture || !Array.isArray(fixture.records) || !fixture.manifest) {
125
+ fail("self_description_fixture_invalid", "self-description requires an immutable fixture");
126
+ }
127
+ if (!Array.isArray(operations)) fail("self_description_operations_invalid", "operations must be an array");
128
+ const agent = byRef(fixture, AGENT_REF);
129
+ const conformanceProfile = byRef(fixture, CONFORMANCE_PROFILE_REF);
130
+ if (conformanceProfile.conformance_level !== "fixture_verified") {
131
+ fail("self_description_profile_unverified", "conformance profile must declare fixture_verified only after receipt verification");
132
+ }
133
+ const summaries = operations.map(operationSummary).sort((left, right) => left.name < right.name ? -1 : left.name > right.name ? 1 : 0);
134
+ return deepFreeze({
135
+ agent,
136
+ conformance: {
137
+ claims: conformanceClaims(fixture, conformanceProfile),
138
+ profile: conformanceProfile,
139
+ status: "fixture_verified",
140
+ },
141
+ manifest: {
142
+ entry_count: fixture.manifest.entries.length,
143
+ manifest_ref: fixture.manifest.manifest_ref,
144
+ sha256: fixture.manifest.sha256,
145
+ version_id: fixture.manifest.version_id,
146
+ },
147
+ operation_digest: hashCanonical(summaries),
148
+ operations: summaries,
149
+ protocol_surfaces: {
150
+ a2a: { mode: "task_projection", protocol: "A2A v1" },
151
+ ag_ui: { mode: "event_projection", protocol: "AG-UI 0.0.57" },
152
+ cli: { commands: summaries.filter((item) => item.adapters.includes("cli")).map((item) => item.name), format: "json" },
153
+ mcp: { mode: "read_only", operations: summaries.filter((item) => item.read_only).map((item) => item.name), protocol: "MCP v1" },
154
+ },
155
+ schema_version: "1.0",
156
+ });
157
+ }
158
+
159
+ export const describeAgentNative = createSelfDescription;
160
+ export const createConformanceProfile = createSelfDescription;
@@ -0,0 +1,20 @@
1
+ import crypto from "node:crypto";
2
+
3
+ const pngSignature = Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]);
4
+
5
+ function pngDimensions(bytes) {
6
+ if (bytes.length < 24 || !bytes.subarray(0, 8).equals(pngSignature) || bytes.toString("ascii", 12, 16) !== "IHDR") return undefined;
7
+ const width = bytes.readUInt32BE(16);
8
+ const height = bytes.readUInt32BE(20);
9
+ return width > 0 && height > 0 ? { height, width } : undefined;
10
+ }
11
+
12
+ export function artifactMetadata(bytes, mediaType) {
13
+ const metadata = {
14
+ byte_length: bytes.length,
15
+ media_type: mediaType,
16
+ sha256: `sha256:${crypto.createHash("sha256").update(bytes).digest("hex")}`,
17
+ };
18
+ if (mediaType === "image/png") Object.assign(metadata, pngDimensions(bytes));
19
+ return metadata;
20
+ }