dna-sdk 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 (240) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +58 -0
  3. package/dist/adapters/embedding/onnx.d.ts +32 -0
  4. package/dist/adapters/embedding/onnx.js +51 -0
  5. package/dist/adapters/filesystem/cache.d.ts +16 -0
  6. package/dist/adapters/filesystem/cache.js +113 -0
  7. package/dist/adapters/filesystem/index.d.ts +2 -0
  8. package/dist/adapters/filesystem/index.js +2 -0
  9. package/dist/adapters/filesystem/source.d.ts +82 -0
  10. package/dist/adapters/filesystem/source.js +357 -0
  11. package/dist/adapters/postgres/index.d.ts +2 -0
  12. package/dist/adapters/postgres/index.js +2 -0
  13. package/dist/adapters/postgres/migrations.d.ts +14 -0
  14. package/dist/adapters/postgres/migrations.js +88 -0
  15. package/dist/adapters/postgres/source.d.ts +172 -0
  16. package/dist/adapters/postgres/source.js +569 -0
  17. package/dist/adapters/resolvers/github.d.ts +12 -0
  18. package/dist/adapters/resolvers/github.js +47 -0
  19. package/dist/adapters/resolvers/http.d.ts +22 -0
  20. package/dist/adapters/resolvers/http.js +145 -0
  21. package/dist/adapters/resolvers/index.d.ts +3 -0
  22. package/dist/adapters/resolvers/index.js +3 -0
  23. package/dist/adapters/resolvers/local.d.ts +24 -0
  24. package/dist/adapters/resolvers/local.js +132 -0
  25. package/dist/adapters/search/driver.d.ts +45 -0
  26. package/dist/adapters/search/driver.js +138 -0
  27. package/dist/adapters/search/migrations.d.ts +26 -0
  28. package/dist/adapters/search/migrations.js +58 -0
  29. package/dist/adapters/search/rrf.d.ts +23 -0
  30. package/dist/adapters/search/rrf.js +42 -0
  31. package/dist/adapters/search/sqlite-vec.d.ts +66 -0
  32. package/dist/adapters/search/sqlite-vec.js +294 -0
  33. package/dist/bootstrap.d.ts +45 -0
  34. package/dist/bootstrap.js +134 -0
  35. package/dist/extensions/agentskills/DOCS.md +18 -0
  36. package/dist/extensions/agentskills.d.ts +28 -0
  37. package/dist/extensions/agentskills.js +252 -0
  38. package/dist/extensions/agentsmd/DOCS.md +18 -0
  39. package/dist/extensions/agentsmd.d.ts +22 -0
  40. package/dist/extensions/agentsmd.js +170 -0
  41. package/dist/extensions/audit/kinds/audit-log.kind.yaml +148 -0
  42. package/dist/extensions/audit.d.ts +25 -0
  43. package/dist/extensions/audit.js +62 -0
  44. package/dist/extensions/automation/kinds/automation.kind.yaml +296 -0
  45. package/dist/extensions/automation/query.d.ts +49 -0
  46. package/dist/extensions/automation/query.js +77 -0
  47. package/dist/extensions/automation/write-guards.d.ts +59 -0
  48. package/dist/extensions/automation/write-guards.js +153 -0
  49. package/dist/extensions/automation.d.ts +41 -0
  50. package/dist/extensions/automation.js +54 -0
  51. package/dist/extensions/collab.d.ts +16 -0
  52. package/dist/extensions/collab.js +79 -0
  53. package/dist/extensions/doc/kinds/doc.kind.yaml +118 -0
  54. package/dist/extensions/doc.d.ts +25 -0
  55. package/dist/extensions/doc.js +10 -0
  56. package/dist/extensions/eval/kinds/eval-baseline.kind.yaml +65 -0
  57. package/dist/extensions/eval/kinds/eval-case.kind.yaml +140 -0
  58. package/dist/extensions/eval/kinds/eval-run.kind.yaml +134 -0
  59. package/dist/extensions/eval/kinds/eval-suite.kind.yaml +87 -0
  60. package/dist/extensions/eval.d.ts +24 -0
  61. package/dist/extensions/eval.js +31 -0
  62. package/dist/extensions/evidence/kinds/evidence.kind.yaml +92 -0
  63. package/dist/extensions/evidence.d.ts +17 -0
  64. package/dist/extensions/evidence.js +94 -0
  65. package/dist/extensions/federation.d.ts +22 -0
  66. package/dist/extensions/federation.js +173 -0
  67. package/dist/extensions/guardrails/DOCS.md +20 -0
  68. package/dist/extensions/guardrails.d.ts +31 -0
  69. package/dist/extensions/guardrails.js +228 -0
  70. package/dist/extensions/helix/DOCS-Actor.md +20 -0
  71. package/dist/extensions/helix/DOCS-Agent.md +20 -0
  72. package/dist/extensions/helix/DOCS-UseCase.md +20 -0
  73. package/dist/extensions/helix/layers.d.ts +12 -0
  74. package/dist/extensions/helix/layers.js +18 -0
  75. package/dist/extensions/helix/write-guards.d.ts +70 -0
  76. package/dist/extensions/helix/write-guards.js +172 -0
  77. package/dist/extensions/helix.d.ts +29 -0
  78. package/dist/extensions/helix.js +963 -0
  79. package/dist/extensions/helix_extras.d.ts +124 -0
  80. package/dist/extensions/helix_extras.js +328 -0
  81. package/dist/extensions/hooks.d.ts +31 -0
  82. package/dist/extensions/hooks.js +247 -0
  83. package/dist/extensions/kinddef/DOCS.md +24 -0
  84. package/dist/extensions/kinddef.d.ts +40 -0
  85. package/dist/extensions/kinddef.js +171 -0
  86. package/dist/extensions/lesson.d.ts +6 -0
  87. package/dist/extensions/lesson.js +194 -0
  88. package/dist/extensions/modelreg/kinds/model-profile.kind.yaml +155 -0
  89. package/dist/extensions/modelreg.d.ts +30 -0
  90. package/dist/extensions/modelreg.js +38 -0
  91. package/dist/extensions/recognizer.d.ts +34 -0
  92. package/dist/extensions/recognizer.js +272 -0
  93. package/dist/extensions/research.d.ts +32 -0
  94. package/dist/extensions/research.js +292 -0
  95. package/dist/extensions/safety.d.ts +33 -0
  96. package/dist/extensions/safety.js +230 -0
  97. package/dist/extensions/sdlc/kinds/adr.kind.yaml +136 -0
  98. package/dist/extensions/sdlc/kinds/archive-proposal.kind.yaml +107 -0
  99. package/dist/extensions/sdlc/kinds/changelog.kind.yaml +108 -0
  100. package/dist/extensions/sdlc/kinds/cognitive-policy.kind.yaml +470 -0
  101. package/dist/extensions/sdlc/kinds/forecast.kind.yaml +125 -0
  102. package/dist/extensions/sdlc/kinds/insight.kind.yaml +87 -0
  103. package/dist/extensions/sdlc/kinds/kaizen.kind.yaml +106 -0
  104. package/dist/extensions/sdlc/kinds/lesson-learned.kind.yaml +270 -0
  105. package/dist/extensions/sdlc/kinds/narrative.kind.yaml +189 -0
  106. package/dist/extensions/sdlc/kinds/pattern-insight.kind.yaml +204 -0
  107. package/dist/extensions/sdlc/kinds/postmortem.kind.yaml +168 -0
  108. package/dist/extensions/sdlc/kinds/pre-mortem.kind.yaml +145 -0
  109. package/dist/extensions/sdlc/kinds/prompt-template.kind.yaml +109 -0
  110. package/dist/extensions/sdlc/kinds/retrospective.kind.yaml +186 -0
  111. package/dist/extensions/sdlc/kinds/risk-register.kind.yaml +178 -0
  112. package/dist/extensions/sdlc/kinds/saved-view.kind.yaml +127 -0
  113. package/dist/extensions/sdlc/kinds/status-report.kind.yaml +129 -0
  114. package/dist/extensions/sdlc/kinds/synthesis-run.kind.yaml +279 -0
  115. package/dist/extensions/sdlc/kinds/synthesizer-state.kind.yaml +196 -0
  116. package/dist/extensions/sdlc/kinds/workflow-event.kind.yaml +195 -0
  117. package/dist/extensions/sdlc.d.ts +32 -0
  118. package/dist/extensions/sdlc.js +1318 -0
  119. package/dist/extensions/soulspec/DOCS.md +19 -0
  120. package/dist/extensions/soulspec.d.ts +22 -0
  121. package/dist/extensions/soulspec.js +287 -0
  122. package/dist/extensions/tenant.d.ts +6 -0
  123. package/dist/extensions/tenant.js +291 -0
  124. package/dist/extensions/testkit.d.ts +24 -0
  125. package/dist/extensions/testkit.js +211 -0
  126. package/dist/index.d.ts +80 -0
  127. package/dist/index.js +102 -0
  128. package/dist/kernel/_text.d.ts +28 -0
  129. package/dist/kernel/_text.js +42 -0
  130. package/dist/kernel/bundle-handle.d.ts +83 -0
  131. package/dist/kernel/bundle-handle.js +184 -0
  132. package/dist/kernel/capabilities.d.ts +157 -0
  133. package/dist/kernel/capabilities.js +109 -0
  134. package/dist/kernel/collaborator-ports.d.ts +91 -0
  135. package/dist/kernel/collaborator-ports.js +1 -0
  136. package/dist/kernel/composition-resolver.d.ts +215 -0
  137. package/dist/kernel/composition-resolver.js +558 -0
  138. package/dist/kernel/composition.d.ts +10 -0
  139. package/dist/kernel/composition.js +1 -0
  140. package/dist/kernel/descriptor-loader.d.ts +13 -0
  141. package/dist/kernel/descriptor-loader.js +48 -0
  142. package/dist/kernel/document.d.ts +43 -0
  143. package/dist/kernel/document.js +71 -0
  144. package/dist/kernel/embedding.d.ts +28 -0
  145. package/dist/kernel/embedding.js +78 -0
  146. package/dist/kernel/errors.d.ts +62 -0
  147. package/dist/kernel/errors.js +82 -0
  148. package/dist/kernel/events.d.ts +6 -0
  149. package/dist/kernel/events.js +21 -0
  150. package/dist/kernel/evidence-capture.d.ts +69 -0
  151. package/dist/kernel/evidence-capture.js +138 -0
  152. package/dist/kernel/fs.d.ts +20 -0
  153. package/dist/kernel/fs.js +128 -0
  154. package/dist/kernel/generic-rw.d.ts +33 -0
  155. package/dist/kernel/generic-rw.js +235 -0
  156. package/dist/kernel/hooks.d.ts +172 -0
  157. package/dist/kernel/hooks.js +256 -0
  158. package/dist/kernel/index.d.ts +624 -0
  159. package/dist/kernel/index.js +1638 -0
  160. package/dist/kernel/instance.d.ts +211 -0
  161. package/dist/kernel/instance.js +487 -0
  162. package/dist/kernel/kind-registry.d.ts +183 -0
  163. package/dist/kernel/kind-registry.js +710 -0
  164. package/dist/kernel/kind_base.d.ts +106 -0
  165. package/dist/kernel/kind_base.js +244 -0
  166. package/dist/kernel/layer-resolver.d.ts +48 -0
  167. package/dist/kernel/layer-resolver.js +284 -0
  168. package/dist/kernel/lock-manager.d.ts +21 -0
  169. package/dist/kernel/lock-manager.js +43 -0
  170. package/dist/kernel/lock.d.ts +38 -0
  171. package/dist/kernel/lock.js +115 -0
  172. package/dist/kernel/meta.d.ts +135 -0
  173. package/dist/kernel/meta.js +817 -0
  174. package/dist/kernel/models.d.ts +3089 -0
  175. package/dist/kernel/models.js +764 -0
  176. package/dist/kernel/navigator.d.ts +36 -0
  177. package/dist/kernel/navigator.js +132 -0
  178. package/dist/kernel/port-surface.d.ts +33 -0
  179. package/dist/kernel/port-surface.js +180 -0
  180. package/dist/kernel/preview.d.ts +60 -0
  181. package/dist/kernel/preview.js +54 -0
  182. package/dist/kernel/prompt-budget.d.ts +39 -0
  183. package/dist/kernel/prompt-budget.js +43 -0
  184. package/dist/kernel/prompt-builder.d.ts +25 -0
  185. package/dist/kernel/prompt-builder.js +237 -0
  186. package/dist/kernel/protocols.d.ts +751 -0
  187. package/dist/kernel/protocols.js +493 -0
  188. package/dist/kernel/reports.d.ts +26 -0
  189. package/dist/kernel/reports.js +215 -0
  190. package/dist/kernel/resolver.d.ts +146 -0
  191. package/dist/kernel/resolver.js +232 -0
  192. package/dist/kernel/resource.d.ts +57 -0
  193. package/dist/kernel/resource.js +97 -0
  194. package/dist/kernel/runtime.d.ts +16 -0
  195. package/dist/kernel/runtime.js +18 -0
  196. package/dist/kernel/safety-scanner.d.ts +38 -0
  197. package/dist/kernel/safety-scanner.js +228 -0
  198. package/dist/kernel/serialize-to-files.d.ts +29 -0
  199. package/dist/kernel/serialize-to-files.js +23 -0
  200. package/dist/kernel/spec-access.d.ts +23 -0
  201. package/dist/kernel/spec-access.js +40 -0
  202. package/dist/kernel/studio_ui.d.ts +88 -0
  203. package/dist/kernel/studio_ui.js +135 -0
  204. package/dist/kernel/templates.d.ts +77 -0
  205. package/dist/kernel/templates.js +116 -0
  206. package/dist/kernel/tool-registry.d.ts +52 -0
  207. package/dist/kernel/tool-registry.js +89 -0
  208. package/dist/kernel/write-pipeline.d.ts +65 -0
  209. package/dist/kernel/write-pipeline.js +271 -0
  210. package/dist/kernel/writer-helpers.d.ts +24 -0
  211. package/dist/kernel/writer-helpers.js +42 -0
  212. package/dist/memory/decay.d.ts +31 -0
  213. package/dist/memory/decay.js +97 -0
  214. package/dist/memory/ecphory.d.ts +25 -0
  215. package/dist/memory/ecphory.js +178 -0
  216. package/dist/memory/encodingContext.d.ts +21 -0
  217. package/dist/memory/encodingContext.js +62 -0
  218. package/dist/memory/index.d.ts +23 -0
  219. package/dist/memory/index.js +23 -0
  220. package/dist/memory/memoryType.d.ts +7 -0
  221. package/dist/memory/memoryType.js +24 -0
  222. package/dist/memory/policy.d.ts +35 -0
  223. package/dist/memory/policy.js +35 -0
  224. package/dist/memory/retrieval.d.ts +21 -0
  225. package/dist/memory/retrieval.js +145 -0
  226. package/dist/testing/index.d.ts +8 -0
  227. package/dist/testing/index.js +8 -0
  228. package/dist/testing/recordSearchConformance.d.ts +43 -0
  229. package/dist/testing/recordSearchConformance.js +147 -0
  230. package/dist/viz/ascii.d.ts +7 -0
  231. package/dist/viz/ascii.js +60 -0
  232. package/dist/viz/health.d.ts +8 -0
  233. package/dist/viz/health.js +161 -0
  234. package/dist/viz/index.d.ts +10 -0
  235. package/dist/viz/index.js +10 -0
  236. package/dist/viz/matrix.d.ts +8 -0
  237. package/dist/viz/matrix.js +84 -0
  238. package/dist/viz/mermaid.d.ts +38 -0
  239. package/dist/viz/mermaid.js +706 -0
  240. package/package.json +82 -0
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Document — unified wrapper for all manifest documents.
3
+ *
4
+ * 1:1 parity with Python dna.v3.kernel.document.
5
+ *
6
+ * v1.0 — `Document<SpecT>` generic typing. Consumers can declare
7
+ * spec type at the call site without a runtime cost:
8
+ *
9
+ * const doc = mi.documents.find((d) => d.kind === "Asset" && d.name === "x") as Document<AssetSpec>;
10
+ * doc.spec.summary?.byte_count; // type-checker validated
11
+ *
12
+ * Bare `Document` defaults to `Record<string, unknown>` so existing
13
+ * untyped code continues working. Purely additive — no API change
14
+ * for callers that don't opt in.
15
+ */
16
+ export class Document {
17
+ apiVersion;
18
+ kind;
19
+ name;
20
+ raw;
21
+ typed;
22
+ origin;
23
+ _metadataRaw;
24
+ _specRaw;
25
+ constructor(opts) {
26
+ this.apiVersion = opts.apiVersion;
27
+ this.kind = opts.kind;
28
+ this.name = opts.name;
29
+ this._metadataRaw = opts.metadata ?? {};
30
+ this._specRaw = opts.spec ?? {};
31
+ this.raw = opts.raw ?? {};
32
+ this.typed = opts.typed ?? null;
33
+ this.origin = opts.origin ?? "local";
34
+ }
35
+ /** Always returns Record<string, unknown> — typed metadata when available, raw dict otherwise. */
36
+ get metadata() {
37
+ if (this.typed != null &&
38
+ typeof this.typed === "object" &&
39
+ "metadata" in this.typed) {
40
+ return this.typed.metadata;
41
+ }
42
+ return this._metadataRaw;
43
+ }
44
+ /** Returns the spec, typed as `SpecT` when consumers parameterize
45
+ * the Document. Runtime: still a plain dict. */
46
+ get spec() {
47
+ if (this.typed != null &&
48
+ typeof this.typed === "object" &&
49
+ "spec" in this.typed) {
50
+ return this.typed.spec;
51
+ }
52
+ return this._specRaw;
53
+ }
54
+ /** Create a Document from a raw dict. */
55
+ static fromRaw(raw, typed, origin) {
56
+ const metadata = raw.metadata ?? {};
57
+ return new Document({
58
+ apiVersion: raw.apiVersion ?? "",
59
+ kind: raw.kind ?? "",
60
+ name: metadata.name ?? "",
61
+ metadata,
62
+ spec: raw.spec ?? {},
63
+ raw,
64
+ typed,
65
+ origin,
66
+ });
67
+ }
68
+ toString() {
69
+ return `Document(${this.apiVersion}/${this.kind}: ${this.name})`;
70
+ }
71
+ }
@@ -0,0 +1,28 @@
1
+ import type { EmbeddingPort } from "./protocols.js";
2
+ /**
3
+ * Default dimensionality of the fake space. Matches all-MiniLM-L6-v2 (the real
4
+ * ONNX provider) so swapping providers keeps the vector length — and any
5
+ * downstream vector-store column width — stable.
6
+ */
7
+ export declare const FAKE_EMBEDDING_DIMS = 384;
8
+ /**
9
+ * Stable identity of this embedding space. Versioned so a future change to the
10
+ * hashing scheme is a NEW space (old vectors stay honestly incomparable).
11
+ */
12
+ export declare const FAKE_EMBEDDING_MODEL_ID = "dna-fake-hash-v1";
13
+ /**
14
+ * Deterministic, L2-normalized hash embedding of a single string. Bit-identical
15
+ * to the Python `fake_embed_one`. Empty/tokenless text → all-zeros (an all-zero
16
+ * vector is honestly "no signal", never normalized).
17
+ */
18
+ export declare function fakeEmbedOne(text: string, dims?: number): number[];
19
+ /**
20
+ * Zero-dependency `EmbeddingPort` — the offline/CI default. Structurally
21
+ * satisfies `EmbeddingPort` (`modelId`, `dims`, async `embed`).
22
+ */
23
+ export declare class FakeEmbeddingProvider implements EmbeddingPort {
24
+ readonly modelId = "dna-fake-hash-v1";
25
+ readonly dims: number;
26
+ constructor(dims?: number);
27
+ embed(texts: string[]): Promise<number[][]>;
28
+ }
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Deterministic hash-based embedding — the zero-dependency offline floor.
3
+ *
4
+ * TypeScript twin of `dna/kernel/embedding.py`. This is the default
5
+ * `EmbeddingPort` the kernel falls back to when no real provider (e.g. the
6
+ * optional ONNX all-MiniLM-L6-v2 adapter) is registered. It is NOT a semantic
7
+ * embedder — it is a stable, content-addressed vector so the search plane and
8
+ * its tests have *something* deterministic to run against in CI and offline,
9
+ * without pulling any ML dependency.
10
+ *
11
+ * Parity contract (rsh-memory-similarity-evolution → rec-embedding-port): this
12
+ * produces the BIT-IDENTICAL vector to the Python `fake_embed_one` for the same
13
+ * string — guaranteed *by construction*:
14
+ *
15
+ * 1. Tokenization is `[a-z0-9]+` over the lower-cased text — identical token
16
+ * lists in TS (`String.match`) and Py (`re.findall`) for ASCII input.
17
+ * 2. Each token is hashed with SHA-256 (`js-sha256` / stdlib `hashlib`); the
18
+ * first 4 bytes pick a dimension (big-endian uint32 mod `dims`) and the
19
+ * 5th byte's low bit picks a sign (±1). Accumulation is INTEGER, so the
20
+ * pre-normalization vector is exact on both sides regardless of order.
21
+ * 3. L2-normalization divides each integer component by `sqrt(sum(cᵢ²))`.
22
+ * The sum-of-squares is an exact integer; `sqrt` and division are both
23
+ * IEEE-754 correctly-rounded, so the resulting doubles are bit-identical.
24
+ *
25
+ * The golden fixture `tests/parity-fixtures/fake-embedding-golden.json` pins a
26
+ * handful of strings to their exact vectors; both language suites assert
27
+ * against it.
28
+ */
29
+ import { sha256 } from "js-sha256";
30
+ /**
31
+ * Default dimensionality of the fake space. Matches all-MiniLM-L6-v2 (the real
32
+ * ONNX provider) so swapping providers keeps the vector length — and any
33
+ * downstream vector-store column width — stable.
34
+ */
35
+ export const FAKE_EMBEDDING_DIMS = 384;
36
+ /**
37
+ * Stable identity of this embedding space. Versioned so a future change to the
38
+ * hashing scheme is a NEW space (old vectors stay honestly incomparable).
39
+ */
40
+ export const FAKE_EMBEDDING_MODEL_ID = "dna-fake-hash-v1";
41
+ const TOKEN_RE = /[a-z0-9]+/g;
42
+ /**
43
+ * Deterministic, L2-normalized hash embedding of a single string. Bit-identical
44
+ * to the Python `fake_embed_one`. Empty/tokenless text → all-zeros (an all-zero
45
+ * vector is honestly "no signal", never normalized).
46
+ */
47
+ export function fakeEmbedOne(text, dims = FAKE_EMBEDDING_DIMS) {
48
+ const counts = new Array(dims).fill(0);
49
+ const tokens = text.toLowerCase().match(TOKEN_RE) ?? [];
50
+ for (const token of tokens) {
51
+ const bytes = sha256.array(token); // 32 bytes, 0..255
52
+ // big-endian uint32 of bytes[0..4) — `>>> 0` forces unsigned.
53
+ const idx = (((bytes[0] << 24) | (bytes[1] << 16) | (bytes[2] << 8) | bytes[3]) >>> 0) % dims;
54
+ const sign = bytes[4] & 1 ? 1 : -1;
55
+ counts[idx] += sign;
56
+ }
57
+ let normSq = 0;
58
+ for (const c of counts)
59
+ normSq += c * c;
60
+ if (normSq === 0)
61
+ return counts; // already all-zeros
62
+ const norm = Math.sqrt(normSq);
63
+ return counts.map((c) => c / norm);
64
+ }
65
+ /**
66
+ * Zero-dependency `EmbeddingPort` — the offline/CI default. Structurally
67
+ * satisfies `EmbeddingPort` (`modelId`, `dims`, async `embed`).
68
+ */
69
+ export class FakeEmbeddingProvider {
70
+ modelId = FAKE_EMBEDDING_MODEL_ID;
71
+ dims;
72
+ constructor(dims = FAKE_EMBEDDING_DIMS) {
73
+ this.dims = dims;
74
+ }
75
+ async embed(texts) {
76
+ return texts.map((t) => fakeEmbedOne(t, this.dims));
77
+ }
78
+ }
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Boot-time registration errors raised by Kernel.
3
+ *
4
+ * 1:1 parity with python/dna/kernel/errors.py.
5
+ *
6
+ * H1 — Boot-time validation. The Kernel's `kind/reader/writer/load`
7
+ * registration methods used to be `this._x.push(y)` no-ops with zero
8
+ * validation. Result: a typo in `detect()` made the Reader silently
9
+ * ignored; two bundle Readers using the same marker file collided
10
+ * (first-registered wins, depending on entry-point alphabetical order);
11
+ * duplicate `(apiVersion, kind)` registrations silently overwrote.
12
+ *
13
+ * These errors surface those problems at boot time instead of as silent
14
+ * runtime drift.
15
+ */
16
+ /** Base class for kernel registration validation failures. */
17
+ export declare class KernelRegistrationError extends Error {
18
+ constructor(message?: string);
19
+ }
20
+ /**
21
+ * A KindPort failed Protocol conformance, uniqueness, or marker
22
+ * collision checks at `kernel.kind(port)` time.
23
+ *
24
+ * Examples:
25
+ * - Duplicate `(apiVersion, kind)` tuple
26
+ * - Duplicate `alias` across registered Kinds
27
+ * - Two BUNDLE-pattern Kinds declaring the same
28
+ * `(storage.container, storage.marker)` pair
29
+ * - Object passed doesn't satisfy the `KindPort` interface
30
+ */
31
+ export declare class KindRegistrationError extends KernelRegistrationError {
32
+ constructor(message?: string);
33
+ }
34
+ /**
35
+ * A ReaderPort failed structural conformance or owner-binding checks
36
+ * at `kernel.reader(r)` time.
37
+ *
38
+ * Examples:
39
+ * - Object missing `detect` or `read` methods
40
+ * - Reader claims a `(container, marker)` pair already owned by
41
+ * another reader
42
+ */
43
+ export declare class ReaderRegistrationError extends KernelRegistrationError {
44
+ constructor(message?: string);
45
+ }
46
+ /**
47
+ * A WriterPort failed structural conformance checks at
48
+ * `kernel.writer(w)` time. Object missing `canWrite` or `write` is
49
+ * the typical case.
50
+ */
51
+ export declare class WriterRegistrationError extends KernelRegistrationError {
52
+ constructor(message?: string);
53
+ }
54
+ /**
55
+ * An Extension failed structural checks at `kernel.load(ext)` time.
56
+ * Object missing `register` callable, or its `register` raised an
57
+ * unexpected exception that wasn't routed through the
58
+ * `extension_error` hook.
59
+ */
60
+ export declare class ExtensionLoadError extends KernelRegistrationError {
61
+ constructor(message?: string);
62
+ }
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Boot-time registration errors raised by Kernel.
3
+ *
4
+ * 1:1 parity with python/dna/kernel/errors.py.
5
+ *
6
+ * H1 — Boot-time validation. The Kernel's `kind/reader/writer/load`
7
+ * registration methods used to be `this._x.push(y)` no-ops with zero
8
+ * validation. Result: a typo in `detect()` made the Reader silently
9
+ * ignored; two bundle Readers using the same marker file collided
10
+ * (first-registered wins, depending on entry-point alphabetical order);
11
+ * duplicate `(apiVersion, kind)` registrations silently overwrote.
12
+ *
13
+ * These errors surface those problems at boot time instead of as silent
14
+ * runtime drift.
15
+ */
16
+ /** Base class for kernel registration validation failures. */
17
+ export class KernelRegistrationError extends Error {
18
+ constructor(message) {
19
+ super(message);
20
+ this.name = "KernelRegistrationError";
21
+ Object.setPrototypeOf(this, new.target.prototype);
22
+ }
23
+ }
24
+ /**
25
+ * A KindPort failed Protocol conformance, uniqueness, or marker
26
+ * collision checks at `kernel.kind(port)` time.
27
+ *
28
+ * Examples:
29
+ * - Duplicate `(apiVersion, kind)` tuple
30
+ * - Duplicate `alias` across registered Kinds
31
+ * - Two BUNDLE-pattern Kinds declaring the same
32
+ * `(storage.container, storage.marker)` pair
33
+ * - Object passed doesn't satisfy the `KindPort` interface
34
+ */
35
+ export class KindRegistrationError extends KernelRegistrationError {
36
+ constructor(message) {
37
+ super(message);
38
+ this.name = "KindRegistrationError";
39
+ Object.setPrototypeOf(this, new.target.prototype);
40
+ }
41
+ }
42
+ /**
43
+ * A ReaderPort failed structural conformance or owner-binding checks
44
+ * at `kernel.reader(r)` time.
45
+ *
46
+ * Examples:
47
+ * - Object missing `detect` or `read` methods
48
+ * - Reader claims a `(container, marker)` pair already owned by
49
+ * another reader
50
+ */
51
+ export class ReaderRegistrationError extends KernelRegistrationError {
52
+ constructor(message) {
53
+ super(message);
54
+ this.name = "ReaderRegistrationError";
55
+ Object.setPrototypeOf(this, new.target.prototype);
56
+ }
57
+ }
58
+ /**
59
+ * A WriterPort failed structural conformance checks at
60
+ * `kernel.writer(w)` time. Object missing `canWrite` or `write` is
61
+ * the typical case.
62
+ */
63
+ export class WriterRegistrationError extends KernelRegistrationError {
64
+ constructor(message) {
65
+ super(message);
66
+ this.name = "WriterRegistrationError";
67
+ Object.setPrototypeOf(this, new.target.prototype);
68
+ }
69
+ }
70
+ /**
71
+ * An Extension failed structural checks at `kernel.load(ext)` time.
72
+ * Object missing `register` callable, or its `register` raised an
73
+ * unexpected exception that wasn't routed through the
74
+ * `extension_error` hook.
75
+ */
76
+ export class ExtensionLoadError extends KernelRegistrationError {
77
+ constructor(message) {
78
+ super(message);
79
+ this.name = "ExtensionLoadError";
80
+ Object.setPrototypeOf(this, new.target.prototype);
81
+ }
82
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Derive event types from Kind + update status for HookRegistry post_save.
3
+ * 1:1 parity with Python dna.kernel.events.
4
+ */
5
+ export declare const DELETE_EVENT_TYPE = "document_deleted";
6
+ export declare function deriveEventType(kind: string, isUpdate: boolean): string;
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Derive event types from Kind + update status for HookRegistry post_save.
3
+ * 1:1 parity with Python dna.kernel.events.
4
+ */
5
+ export const DELETE_EVENT_TYPE = "document_deleted";
6
+ const FIXED_EVENTS = {
7
+ EvalRun: "eval_run_completed",
8
+ EvalBaseline: "baseline_pinned",
9
+ };
10
+ const SPLIT_EVENTS = {
11
+ Finding: ["finding_created", "finding_status_changed"],
12
+ };
13
+ export function deriveEventType(kind, isUpdate) {
14
+ const fixed = FIXED_EVENTS[kind];
15
+ if (fixed)
16
+ return fixed;
17
+ const split = SPLIT_EVENTS[kind];
18
+ if (split)
19
+ return isUpdate ? split[1] : split[0];
20
+ return isUpdate ? "document_modified" : "document_created";
21
+ }
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Evidence capture handler — creates Evidence documents with SHA-256 hashes.
3
+ *
4
+ * ``computeContentHash`` produces a deterministic SHA-256 digest of
5
+ * canonical JSON (recursively sorted keys, no whitespace) so that key
6
+ * ordering never affects the hash.
7
+ *
8
+ * 1:1 parity with Python dna.extensions.evidence.builder.
9
+ */
10
+ import type { HookContext } from "./hooks.js";
11
+ /**
12
+ * Check whether *eventType* should be auto-captured per *policySpec*.
13
+ *
14
+ * Generic policy-evaluation logic (reads a plain EvidencePolicy spec dict),
15
+ * kernel-owned so the microkernel's capture handler needs no extension
16
+ * import (s-invert-evidence-capture-dep). EvidenceExtension re-exports this
17
+ * as its public API.
18
+ */
19
+ export declare function shouldCapture(policySpec: Record<string, unknown>, eventType: string): boolean;
20
+ /**
21
+ * SHA-256 of canonical JSON (async, browser-compatible).
22
+ *
23
+ * Uses Web Crypto API (available in Node 18+, Bun, and all browsers).
24
+ * Matches Python: ``json.dumps(sort_keys=True, separators=(",", ":"))``
25
+ */
26
+ export declare function computeContentHash(content: unknown): Promise<string>;
27
+ /**
28
+ * Derive the suite name from kind + spec, with explicit override.
29
+ *
30
+ * - EvalRun -> spec.suite
31
+ * - Finding -> spec.source
32
+ * - Explicit parameter always wins.
33
+ * - Non-eval kinds return null.
34
+ */
35
+ export declare function extractSuite(kind: string, spec: Record<string, unknown>, explicit: string | null): string | null;
36
+ export declare function buildEvidenceDoc(opts: {
37
+ eventType: string;
38
+ kind: string;
39
+ name: string;
40
+ spec: Record<string, unknown>;
41
+ author: string;
42
+ suite?: string | null;
43
+ }): Promise<Record<string, unknown>>;
44
+ /**
45
+ * The runtime capabilities the evidence-capture handler needs from the
46
+ * kernel it captures — a read surface (`instance()._all()`, the MI internal non-deprecated twin) plus the write
47
+ * path. Narrower than the full Kernel on purpose; the EvidenceExtension
48
+ * feature-tests these members before wiring the handler
49
+ * (s-dna-extension-host-contract).
50
+ */
51
+ export interface EvidenceCaptureHost {
52
+ instance(scope: string): {
53
+ _all(kind: string): {
54
+ spec: Record<string, unknown>;
55
+ }[];
56
+ };
57
+ writeDocument(scope: string, kind: string, name: string, raw: Record<string, unknown>, options?: {
58
+ skipHooks?: boolean;
59
+ author?: string;
60
+ }): Promise<void>;
61
+ }
62
+ /**
63
+ * Create a ``post_save`` handler that auto-captures Evidence documents.
64
+ *
65
+ * The handler inspects EvidencePolicy documents in the current scope to
66
+ * decide whether to capture. Evidence documents themselves are skipped
67
+ * to avoid infinite loops, and the resulting write uses ``skipHooks: true``.
68
+ */
69
+ export declare function makeEvidenceCaptureHandler(kernel: EvidenceCaptureHost): (ctx: HookContext) => void;
@@ -0,0 +1,138 @@
1
+ /**
2
+ * Evidence capture handler — creates Evidence documents with SHA-256 hashes.
3
+ *
4
+ * ``computeContentHash`` produces a deterministic SHA-256 digest of
5
+ * canonical JSON (recursively sorted keys, no whitespace) so that key
6
+ * ordering never affects the hash.
7
+ *
8
+ * 1:1 parity with Python dna.extensions.evidence.builder.
9
+ */
10
+ const EVAL_KINDS = new Set(["EvalRun", "EvalBaseline", "Finding"]);
11
+ // ---------------------------------------------------------------------------
12
+ // Policy evaluation
13
+ // ---------------------------------------------------------------------------
14
+ /**
15
+ * Check whether *eventType* should be auto-captured per *policySpec*.
16
+ *
17
+ * Generic policy-evaluation logic (reads a plain EvidencePolicy spec dict),
18
+ * kernel-owned so the microkernel's capture handler needs no extension
19
+ * import (s-invert-evidence-capture-dep). EvidenceExtension re-exports this
20
+ * as its public API.
21
+ */
22
+ export function shouldCapture(policySpec, eventType) {
23
+ if (policySpec.auto_capture === false)
24
+ return false;
25
+ const events = policySpec.events;
26
+ if (!Array.isArray(events))
27
+ return false;
28
+ return events.includes(eventType);
29
+ }
30
+ // ---------------------------------------------------------------------------
31
+ // Canonical JSON helpers
32
+ // ---------------------------------------------------------------------------
33
+ /** Recursively sort object keys for canonical JSON (Python parity). */
34
+ function sortDeep(val) {
35
+ if (val === null || val === undefined || typeof val !== "object")
36
+ return val;
37
+ if (Array.isArray(val))
38
+ return val.map(sortDeep);
39
+ const sorted = {};
40
+ for (const key of Object.keys(val).sort()) {
41
+ sorted[key] = sortDeep(val[key]);
42
+ }
43
+ return sorted;
44
+ }
45
+ /**
46
+ * SHA-256 of canonical JSON (async, browser-compatible).
47
+ *
48
+ * Uses Web Crypto API (available in Node 18+, Bun, and all browsers).
49
+ * Matches Python: ``json.dumps(sort_keys=True, separators=(",", ":"))``
50
+ */
51
+ export async function computeContentHash(content) {
52
+ const canonical = JSON.stringify(sortDeep(content));
53
+ const data = new TextEncoder().encode(canonical);
54
+ const hashBuffer = await globalThis.crypto.subtle.digest("SHA-256", data);
55
+ return Array.from(new Uint8Array(hashBuffer))
56
+ .map((b) => b.toString(16).padStart(2, "0"))
57
+ .join("");
58
+ }
59
+ // ---------------------------------------------------------------------------
60
+ // Suite extraction
61
+ // ---------------------------------------------------------------------------
62
+ /**
63
+ * Derive the suite name from kind + spec, with explicit override.
64
+ *
65
+ * - EvalRun -> spec.suite
66
+ * - Finding -> spec.source
67
+ * - Explicit parameter always wins.
68
+ * - Non-eval kinds return null.
69
+ */
70
+ export function extractSuite(kind, spec, explicit) {
71
+ if (explicit)
72
+ return explicit;
73
+ if (!EVAL_KINDS.has(kind))
74
+ return null;
75
+ // The spec may be flat ({suite: "x"}) or nested ({spec: {suite: "x"}})
76
+ // depending on whether the caller passed the raw doc or just the spec field.
77
+ const inner = spec.spec ?? spec;
78
+ return inner.suite || inner.source || null;
79
+ }
80
+ // ---------------------------------------------------------------------------
81
+ // Evidence document builder
82
+ // ---------------------------------------------------------------------------
83
+ export async function buildEvidenceDoc(opts) {
84
+ const now = new Date();
85
+ const suite = extractSuite(opts.kind, opts.spec, opts.suite ?? null);
86
+ const sha256 = await computeContentHash(opts.spec);
87
+ return {
88
+ apiVersion: "github.com/ruinosus/dna/evidence/v1",
89
+ kind: "Evidence",
90
+ metadata: { name: `ev-${opts.eventType}-${sha256.slice(0, 12)}` },
91
+ spec: {
92
+ event_type: opts.eventType,
93
+ sha256,
94
+ captured_at: now.toISOString(),
95
+ author: opts.author,
96
+ document_ref: `${opts.kind}:${opts.name}`,
97
+ suite,
98
+ snapshot: opts.spec,
99
+ },
100
+ };
101
+ }
102
+ /**
103
+ * Create a ``post_save`` handler that auto-captures Evidence documents.
104
+ *
105
+ * The handler inspects EvidencePolicy documents in the current scope to
106
+ * decide whether to capture. Evidence documents themselves are skipped
107
+ * to avoid infinite loops, and the resulting write uses ``skipHooks: true``.
108
+ */
109
+ export function makeEvidenceCaptureHandler(kernel) {
110
+ return (ctx) => {
111
+ const { kind, name, data } = ctx;
112
+ const eventType = data.event_type || "";
113
+ // Never capture evidence about evidence (avoids infinite loop)
114
+ if (kind === "Evidence")
115
+ return;
116
+ const mi = kernel.instance(ctx.scope);
117
+ const policies = mi._all("EvidencePolicy");
118
+ if (!policies.some((p) => shouldCapture(p.spec, eventType)))
119
+ return;
120
+ // Async: build doc then write (fire-and-forget from hook perspective)
121
+ buildEvidenceDoc({
122
+ eventType,
123
+ kind: kind || "",
124
+ name: name || "",
125
+ spec: data.spec || {},
126
+ author: data.author || "unknown",
127
+ suite: data.suite,
128
+ })
129
+ .then((doc) => {
130
+ const evidenceName = doc.metadata.name;
131
+ return kernel.writeDocument(ctx.scope, "Evidence", evidenceName, doc, {
132
+ skipHooks: true,
133
+ author: "evidence-capture",
134
+ });
135
+ })
136
+ .catch((e) => console.warn(`Evidence capture failed for ${kind}:${name}:`, e));
137
+ };
138
+ }
@@ -0,0 +1,20 @@
1
+ /**
2
+ * FSLike — filesystem abstraction for readers/writers.
3
+ *
4
+ * Allows the SDK to run in Node.js (nodeFS), Tauri/webview (VFS adapter),
5
+ * or in-memory (createMemoryFS for tests).
6
+ */
7
+ export interface FSLike {
8
+ exists(path: string): boolean;
9
+ readFile(path: string): string;
10
+ readDir(path: string): string[];
11
+ isDirectory(path: string): boolean;
12
+ isFile(path: string): boolean;
13
+ writeFile(path: string, content: string): void;
14
+ mkdir(path: string): void;
15
+ }
16
+ export declare const nodeFS: FSLike;
17
+ export declare function createMemoryFS(initial?: Record<string, string>): FSLike;
18
+ export declare function relativePath(root: string, full: string): string;
19
+ export declare function readTextSafe(fs: FSLike, path: string): string | null;
20
+ export declare function collectDir(fs: FSLike, directory: string, root: string): Record<string, string>;