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.
- package/LICENSE +21 -0
- package/README.md +58 -0
- package/dist/adapters/embedding/onnx.d.ts +32 -0
- package/dist/adapters/embedding/onnx.js +51 -0
- package/dist/adapters/filesystem/cache.d.ts +16 -0
- package/dist/adapters/filesystem/cache.js +113 -0
- package/dist/adapters/filesystem/index.d.ts +2 -0
- package/dist/adapters/filesystem/index.js +2 -0
- package/dist/adapters/filesystem/source.d.ts +82 -0
- package/dist/adapters/filesystem/source.js +357 -0
- package/dist/adapters/postgres/index.d.ts +2 -0
- package/dist/adapters/postgres/index.js +2 -0
- package/dist/adapters/postgres/migrations.d.ts +14 -0
- package/dist/adapters/postgres/migrations.js +88 -0
- package/dist/adapters/postgres/source.d.ts +172 -0
- package/dist/adapters/postgres/source.js +569 -0
- package/dist/adapters/resolvers/github.d.ts +12 -0
- package/dist/adapters/resolvers/github.js +47 -0
- package/dist/adapters/resolvers/http.d.ts +22 -0
- package/dist/adapters/resolvers/http.js +145 -0
- package/dist/adapters/resolvers/index.d.ts +3 -0
- package/dist/adapters/resolvers/index.js +3 -0
- package/dist/adapters/resolvers/local.d.ts +24 -0
- package/dist/adapters/resolvers/local.js +132 -0
- package/dist/adapters/search/driver.d.ts +45 -0
- package/dist/adapters/search/driver.js +138 -0
- package/dist/adapters/search/migrations.d.ts +26 -0
- package/dist/adapters/search/migrations.js +58 -0
- package/dist/adapters/search/rrf.d.ts +23 -0
- package/dist/adapters/search/rrf.js +42 -0
- package/dist/adapters/search/sqlite-vec.d.ts +66 -0
- package/dist/adapters/search/sqlite-vec.js +294 -0
- package/dist/bootstrap.d.ts +45 -0
- package/dist/bootstrap.js +134 -0
- package/dist/extensions/agentskills/DOCS.md +18 -0
- package/dist/extensions/agentskills.d.ts +28 -0
- package/dist/extensions/agentskills.js +252 -0
- package/dist/extensions/agentsmd/DOCS.md +18 -0
- package/dist/extensions/agentsmd.d.ts +22 -0
- package/dist/extensions/agentsmd.js +170 -0
- package/dist/extensions/audit/kinds/audit-log.kind.yaml +148 -0
- package/dist/extensions/audit.d.ts +25 -0
- package/dist/extensions/audit.js +62 -0
- package/dist/extensions/automation/kinds/automation.kind.yaml +296 -0
- package/dist/extensions/automation/query.d.ts +49 -0
- package/dist/extensions/automation/query.js +77 -0
- package/dist/extensions/automation/write-guards.d.ts +59 -0
- package/dist/extensions/automation/write-guards.js +153 -0
- package/dist/extensions/automation.d.ts +41 -0
- package/dist/extensions/automation.js +54 -0
- package/dist/extensions/collab.d.ts +16 -0
- package/dist/extensions/collab.js +79 -0
- package/dist/extensions/doc/kinds/doc.kind.yaml +118 -0
- package/dist/extensions/doc.d.ts +25 -0
- package/dist/extensions/doc.js +10 -0
- package/dist/extensions/eval/kinds/eval-baseline.kind.yaml +65 -0
- package/dist/extensions/eval/kinds/eval-case.kind.yaml +140 -0
- package/dist/extensions/eval/kinds/eval-run.kind.yaml +134 -0
- package/dist/extensions/eval/kinds/eval-suite.kind.yaml +87 -0
- package/dist/extensions/eval.d.ts +24 -0
- package/dist/extensions/eval.js +31 -0
- package/dist/extensions/evidence/kinds/evidence.kind.yaml +92 -0
- package/dist/extensions/evidence.d.ts +17 -0
- package/dist/extensions/evidence.js +94 -0
- package/dist/extensions/federation.d.ts +22 -0
- package/dist/extensions/federation.js +173 -0
- package/dist/extensions/guardrails/DOCS.md +20 -0
- package/dist/extensions/guardrails.d.ts +31 -0
- package/dist/extensions/guardrails.js +228 -0
- package/dist/extensions/helix/DOCS-Actor.md +20 -0
- package/dist/extensions/helix/DOCS-Agent.md +20 -0
- package/dist/extensions/helix/DOCS-UseCase.md +20 -0
- package/dist/extensions/helix/layers.d.ts +12 -0
- package/dist/extensions/helix/layers.js +18 -0
- package/dist/extensions/helix/write-guards.d.ts +70 -0
- package/dist/extensions/helix/write-guards.js +172 -0
- package/dist/extensions/helix.d.ts +29 -0
- package/dist/extensions/helix.js +963 -0
- package/dist/extensions/helix_extras.d.ts +124 -0
- package/dist/extensions/helix_extras.js +328 -0
- package/dist/extensions/hooks.d.ts +31 -0
- package/dist/extensions/hooks.js +247 -0
- package/dist/extensions/kinddef/DOCS.md +24 -0
- package/dist/extensions/kinddef.d.ts +40 -0
- package/dist/extensions/kinddef.js +171 -0
- package/dist/extensions/lesson.d.ts +6 -0
- package/dist/extensions/lesson.js +194 -0
- package/dist/extensions/modelreg/kinds/model-profile.kind.yaml +155 -0
- package/dist/extensions/modelreg.d.ts +30 -0
- package/dist/extensions/modelreg.js +38 -0
- package/dist/extensions/recognizer.d.ts +34 -0
- package/dist/extensions/recognizer.js +272 -0
- package/dist/extensions/research.d.ts +32 -0
- package/dist/extensions/research.js +292 -0
- package/dist/extensions/safety.d.ts +33 -0
- package/dist/extensions/safety.js +230 -0
- package/dist/extensions/sdlc/kinds/adr.kind.yaml +136 -0
- package/dist/extensions/sdlc/kinds/archive-proposal.kind.yaml +107 -0
- package/dist/extensions/sdlc/kinds/changelog.kind.yaml +108 -0
- package/dist/extensions/sdlc/kinds/cognitive-policy.kind.yaml +470 -0
- package/dist/extensions/sdlc/kinds/forecast.kind.yaml +125 -0
- package/dist/extensions/sdlc/kinds/insight.kind.yaml +87 -0
- package/dist/extensions/sdlc/kinds/kaizen.kind.yaml +106 -0
- package/dist/extensions/sdlc/kinds/lesson-learned.kind.yaml +270 -0
- package/dist/extensions/sdlc/kinds/narrative.kind.yaml +189 -0
- package/dist/extensions/sdlc/kinds/pattern-insight.kind.yaml +204 -0
- package/dist/extensions/sdlc/kinds/postmortem.kind.yaml +168 -0
- package/dist/extensions/sdlc/kinds/pre-mortem.kind.yaml +145 -0
- package/dist/extensions/sdlc/kinds/prompt-template.kind.yaml +109 -0
- package/dist/extensions/sdlc/kinds/retrospective.kind.yaml +186 -0
- package/dist/extensions/sdlc/kinds/risk-register.kind.yaml +178 -0
- package/dist/extensions/sdlc/kinds/saved-view.kind.yaml +127 -0
- package/dist/extensions/sdlc/kinds/status-report.kind.yaml +129 -0
- package/dist/extensions/sdlc/kinds/synthesis-run.kind.yaml +279 -0
- package/dist/extensions/sdlc/kinds/synthesizer-state.kind.yaml +196 -0
- package/dist/extensions/sdlc/kinds/workflow-event.kind.yaml +195 -0
- package/dist/extensions/sdlc.d.ts +32 -0
- package/dist/extensions/sdlc.js +1318 -0
- package/dist/extensions/soulspec/DOCS.md +19 -0
- package/dist/extensions/soulspec.d.ts +22 -0
- package/dist/extensions/soulspec.js +287 -0
- package/dist/extensions/tenant.d.ts +6 -0
- package/dist/extensions/tenant.js +291 -0
- package/dist/extensions/testkit.d.ts +24 -0
- package/dist/extensions/testkit.js +211 -0
- package/dist/index.d.ts +80 -0
- package/dist/index.js +102 -0
- package/dist/kernel/_text.d.ts +28 -0
- package/dist/kernel/_text.js +42 -0
- package/dist/kernel/bundle-handle.d.ts +83 -0
- package/dist/kernel/bundle-handle.js +184 -0
- package/dist/kernel/capabilities.d.ts +157 -0
- package/dist/kernel/capabilities.js +109 -0
- package/dist/kernel/collaborator-ports.d.ts +91 -0
- package/dist/kernel/collaborator-ports.js +1 -0
- package/dist/kernel/composition-resolver.d.ts +215 -0
- package/dist/kernel/composition-resolver.js +558 -0
- package/dist/kernel/composition.d.ts +10 -0
- package/dist/kernel/composition.js +1 -0
- package/dist/kernel/descriptor-loader.d.ts +13 -0
- package/dist/kernel/descriptor-loader.js +48 -0
- package/dist/kernel/document.d.ts +43 -0
- package/dist/kernel/document.js +71 -0
- package/dist/kernel/embedding.d.ts +28 -0
- package/dist/kernel/embedding.js +78 -0
- package/dist/kernel/errors.d.ts +62 -0
- package/dist/kernel/errors.js +82 -0
- package/dist/kernel/events.d.ts +6 -0
- package/dist/kernel/events.js +21 -0
- package/dist/kernel/evidence-capture.d.ts +69 -0
- package/dist/kernel/evidence-capture.js +138 -0
- package/dist/kernel/fs.d.ts +20 -0
- package/dist/kernel/fs.js +128 -0
- package/dist/kernel/generic-rw.d.ts +33 -0
- package/dist/kernel/generic-rw.js +235 -0
- package/dist/kernel/hooks.d.ts +172 -0
- package/dist/kernel/hooks.js +256 -0
- package/dist/kernel/index.d.ts +624 -0
- package/dist/kernel/index.js +1638 -0
- package/dist/kernel/instance.d.ts +211 -0
- package/dist/kernel/instance.js +487 -0
- package/dist/kernel/kind-registry.d.ts +183 -0
- package/dist/kernel/kind-registry.js +710 -0
- package/dist/kernel/kind_base.d.ts +106 -0
- package/dist/kernel/kind_base.js +244 -0
- package/dist/kernel/layer-resolver.d.ts +48 -0
- package/dist/kernel/layer-resolver.js +284 -0
- package/dist/kernel/lock-manager.d.ts +21 -0
- package/dist/kernel/lock-manager.js +43 -0
- package/dist/kernel/lock.d.ts +38 -0
- package/dist/kernel/lock.js +115 -0
- package/dist/kernel/meta.d.ts +135 -0
- package/dist/kernel/meta.js +817 -0
- package/dist/kernel/models.d.ts +3089 -0
- package/dist/kernel/models.js +764 -0
- package/dist/kernel/navigator.d.ts +36 -0
- package/dist/kernel/navigator.js +132 -0
- package/dist/kernel/port-surface.d.ts +33 -0
- package/dist/kernel/port-surface.js +180 -0
- package/dist/kernel/preview.d.ts +60 -0
- package/dist/kernel/preview.js +54 -0
- package/dist/kernel/prompt-budget.d.ts +39 -0
- package/dist/kernel/prompt-budget.js +43 -0
- package/dist/kernel/prompt-builder.d.ts +25 -0
- package/dist/kernel/prompt-builder.js +237 -0
- package/dist/kernel/protocols.d.ts +751 -0
- package/dist/kernel/protocols.js +493 -0
- package/dist/kernel/reports.d.ts +26 -0
- package/dist/kernel/reports.js +215 -0
- package/dist/kernel/resolver.d.ts +146 -0
- package/dist/kernel/resolver.js +232 -0
- package/dist/kernel/resource.d.ts +57 -0
- package/dist/kernel/resource.js +97 -0
- package/dist/kernel/runtime.d.ts +16 -0
- package/dist/kernel/runtime.js +18 -0
- package/dist/kernel/safety-scanner.d.ts +38 -0
- package/dist/kernel/safety-scanner.js +228 -0
- package/dist/kernel/serialize-to-files.d.ts +29 -0
- package/dist/kernel/serialize-to-files.js +23 -0
- package/dist/kernel/spec-access.d.ts +23 -0
- package/dist/kernel/spec-access.js +40 -0
- package/dist/kernel/studio_ui.d.ts +88 -0
- package/dist/kernel/studio_ui.js +135 -0
- package/dist/kernel/templates.d.ts +77 -0
- package/dist/kernel/templates.js +116 -0
- package/dist/kernel/tool-registry.d.ts +52 -0
- package/dist/kernel/tool-registry.js +89 -0
- package/dist/kernel/write-pipeline.d.ts +65 -0
- package/dist/kernel/write-pipeline.js +271 -0
- package/dist/kernel/writer-helpers.d.ts +24 -0
- package/dist/kernel/writer-helpers.js +42 -0
- package/dist/memory/decay.d.ts +31 -0
- package/dist/memory/decay.js +97 -0
- package/dist/memory/ecphory.d.ts +25 -0
- package/dist/memory/ecphory.js +178 -0
- package/dist/memory/encodingContext.d.ts +21 -0
- package/dist/memory/encodingContext.js +62 -0
- package/dist/memory/index.d.ts +23 -0
- package/dist/memory/index.js +23 -0
- package/dist/memory/memoryType.d.ts +7 -0
- package/dist/memory/memoryType.js +24 -0
- package/dist/memory/policy.d.ts +35 -0
- package/dist/memory/policy.js +35 -0
- package/dist/memory/retrieval.d.ts +21 -0
- package/dist/memory/retrieval.js +145 -0
- package/dist/testing/index.d.ts +8 -0
- package/dist/testing/index.js +8 -0
- package/dist/testing/recordSearchConformance.d.ts +43 -0
- package/dist/testing/recordSearchConformance.js +147 -0
- package/dist/viz/ascii.d.ts +7 -0
- package/dist/viz/ascii.js +60 -0
- package/dist/viz/health.d.ts +8 -0
- package/dist/viz/health.js +161 -0
- package/dist/viz/index.d.ts +10 -0
- package/dist/viz/index.js +10 -0
- package/dist/viz/matrix.d.ts +8 -0
- package/dist/viz/matrix.js +84 -0
- package/dist/viz/mermaid.d.ts +38 -0
- package/dist/viz/mermaid.js +706 -0
- 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>;
|