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,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resource — self-aware document wrapper that knows its own dependencies.
|
|
3
|
+
*
|
|
4
|
+
* Replaces Document with added kindRef linkage: a Resource can resolve
|
|
5
|
+
* its own dep_filters via `deps()` without needing the full ManifestInstance.
|
|
6
|
+
*/
|
|
7
|
+
// ---------------------------------------------------------------------------
|
|
8
|
+
// Resource
|
|
9
|
+
// ---------------------------------------------------------------------------
|
|
10
|
+
export class Resource {
|
|
11
|
+
apiVersion;
|
|
12
|
+
kind;
|
|
13
|
+
name;
|
|
14
|
+
raw;
|
|
15
|
+
typed;
|
|
16
|
+
origin;
|
|
17
|
+
kindRef;
|
|
18
|
+
_metadataRaw;
|
|
19
|
+
_specRaw;
|
|
20
|
+
constructor(opts) {
|
|
21
|
+
this.apiVersion = opts.apiVersion;
|
|
22
|
+
this.kind = opts.kind;
|
|
23
|
+
this.name = opts.name;
|
|
24
|
+
this._metadataRaw = opts.metadata ?? {};
|
|
25
|
+
this._specRaw = opts.spec ?? {};
|
|
26
|
+
this.raw = opts.raw ?? {};
|
|
27
|
+
this.typed = opts.typed ?? null;
|
|
28
|
+
this.origin = opts.origin ?? "local";
|
|
29
|
+
this.kindRef = opts.kindRef ?? null;
|
|
30
|
+
}
|
|
31
|
+
/** Always returns Record<string, unknown> — typed metadata when available, raw dict otherwise. */
|
|
32
|
+
get metadata() {
|
|
33
|
+
if (this.typed != null &&
|
|
34
|
+
typeof this.typed === "object" &&
|
|
35
|
+
"metadata" in this.typed) {
|
|
36
|
+
return this.typed.metadata;
|
|
37
|
+
}
|
|
38
|
+
return this._metadataRaw;
|
|
39
|
+
}
|
|
40
|
+
/** Always returns Record<string, unknown> — typed spec when available, raw dict otherwise. */
|
|
41
|
+
get spec() {
|
|
42
|
+
if (this.typed != null &&
|
|
43
|
+
typeof this.typed === "object" &&
|
|
44
|
+
"spec" in this.typed) {
|
|
45
|
+
return this.typed.spec;
|
|
46
|
+
}
|
|
47
|
+
return this._specRaw;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Resolve this resource's outgoing dependency edges using kindRef.depFilters().
|
|
51
|
+
*
|
|
52
|
+
* Returns one entry per dep_filter field that has a non-empty value in spec.
|
|
53
|
+
* Scalar spec values (e.g. `soul: "brad"`) become single-element name lists.
|
|
54
|
+
* Returns empty array when kindRef is null or depFilters() returns null.
|
|
55
|
+
*/
|
|
56
|
+
deps() {
|
|
57
|
+
if (!this.kindRef)
|
|
58
|
+
return [];
|
|
59
|
+
const filters = this.kindRef.depFilters();
|
|
60
|
+
if (!filters)
|
|
61
|
+
return [];
|
|
62
|
+
const spec = this.spec;
|
|
63
|
+
const result = [];
|
|
64
|
+
for (const [field, targetAlias] of Object.entries(filters)) {
|
|
65
|
+
const value = spec[field];
|
|
66
|
+
let names = [];
|
|
67
|
+
if (Array.isArray(value)) {
|
|
68
|
+
names = value.filter((v) => typeof v === "string");
|
|
69
|
+
}
|
|
70
|
+
else if (typeof value === "string" && value) {
|
|
71
|
+
names = [value];
|
|
72
|
+
}
|
|
73
|
+
if (names.length === 0)
|
|
74
|
+
continue;
|
|
75
|
+
result.push({ field, targetAlias, names });
|
|
76
|
+
}
|
|
77
|
+
return result;
|
|
78
|
+
}
|
|
79
|
+
/** Create a Resource from a raw dict. */
|
|
80
|
+
static fromRaw(raw, typed, origin, kindRef) {
|
|
81
|
+
const metadata = raw.metadata ?? {};
|
|
82
|
+
return new Resource({
|
|
83
|
+
apiVersion: raw.apiVersion ?? "",
|
|
84
|
+
kind: raw.kind ?? "",
|
|
85
|
+
name: metadata.name ?? "",
|
|
86
|
+
metadata,
|
|
87
|
+
spec: raw.spec ?? {},
|
|
88
|
+
raw,
|
|
89
|
+
typed,
|
|
90
|
+
origin,
|
|
91
|
+
kindRef,
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
toString() {
|
|
95
|
+
return `Resource(${this.apiVersion}/${this.kind}: ${this.name})`;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runtime — public facade over Kernel.
|
|
3
|
+
*
|
|
4
|
+
* Provides the new vocabulary: storage() instead of source(),
|
|
5
|
+
* manifest() instead of instance(). Extends Kernel for full
|
|
6
|
+
* backwards compatibility during the transition.
|
|
7
|
+
*/
|
|
8
|
+
import { Kernel } from "./index.js";
|
|
9
|
+
import type { SourcePort } from "./protocols.js";
|
|
10
|
+
import type { ManifestInstance } from "./instance.js";
|
|
11
|
+
export declare class Runtime extends Kernel {
|
|
12
|
+
/** Register a storage backend. Alias for source(). */
|
|
13
|
+
storage(s: SourcePort): void;
|
|
14
|
+
/** Load a manifest for a scope. Alias for instance(). */
|
|
15
|
+
manifest(scope: string, layers?: Record<string, string>): Promise<ManifestInstance>;
|
|
16
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runtime — public facade over Kernel.
|
|
3
|
+
*
|
|
4
|
+
* Provides the new vocabulary: storage() instead of source(),
|
|
5
|
+
* manifest() instead of instance(). Extends Kernel for full
|
|
6
|
+
* backwards compatibility during the transition.
|
|
7
|
+
*/
|
|
8
|
+
import { Kernel } from "./index.js";
|
|
9
|
+
export class Runtime extends Kernel {
|
|
10
|
+
/** Register a storage backend. Alias for source(). */
|
|
11
|
+
storage(s) {
|
|
12
|
+
this.source(s);
|
|
13
|
+
}
|
|
14
|
+
/** Load a manifest for a scope. Alias for instance(). */
|
|
15
|
+
async manifest(scope, layers) {
|
|
16
|
+
return this.instance(scope, layers);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Safety scanner — Tier 1 (regex) built-in, zero deps.
|
|
3
|
+
*
|
|
4
|
+
* Detects and masks PII (CPF, CNPJ, email, phone, credit card),
|
|
5
|
+
* prompt injection, banned words, and custom regex patterns.
|
|
6
|
+
*
|
|
7
|
+
* 1:1 parity with Python dna.safety.scanner.
|
|
8
|
+
*/
|
|
9
|
+
export interface Violation {
|
|
10
|
+
ruleType: string;
|
|
11
|
+
entity: string;
|
|
12
|
+
text: string;
|
|
13
|
+
start: number;
|
|
14
|
+
end: number;
|
|
15
|
+
replacement: string;
|
|
16
|
+
}
|
|
17
|
+
export interface ScanResult {
|
|
18
|
+
found: boolean;
|
|
19
|
+
violations: Violation[];
|
|
20
|
+
}
|
|
21
|
+
export interface Scanner {
|
|
22
|
+
available(): boolean;
|
|
23
|
+
scan(text: string): Violation[];
|
|
24
|
+
}
|
|
25
|
+
export declare class RegexScanner implements Scanner {
|
|
26
|
+
private patterns;
|
|
27
|
+
constructor(rules: Array<Record<string, unknown>>);
|
|
28
|
+
available(): boolean;
|
|
29
|
+
scan(text: string): Violation[];
|
|
30
|
+
private inferTier;
|
|
31
|
+
private buildPatterns;
|
|
32
|
+
}
|
|
33
|
+
export declare class ScannerPipeline {
|
|
34
|
+
private scanners;
|
|
35
|
+
constructor(rules: Array<Record<string, unknown>>);
|
|
36
|
+
scan(text: string): ScanResult;
|
|
37
|
+
apply(text: string, action: string): string;
|
|
38
|
+
}
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Safety scanner — Tier 1 (regex) built-in, zero deps.
|
|
3
|
+
*
|
|
4
|
+
* Detects and masks PII (CPF, CNPJ, email, phone, credit card),
|
|
5
|
+
* prompt injection, banned words, and custom regex patterns.
|
|
6
|
+
*
|
|
7
|
+
* 1:1 parity with Python dna.safety.scanner.
|
|
8
|
+
*/
|
|
9
|
+
// ---------------------------------------------------------------------------
|
|
10
|
+
// Validation helpers
|
|
11
|
+
// ---------------------------------------------------------------------------
|
|
12
|
+
function validateCPF(cpf) {
|
|
13
|
+
const digits = cpf.replace(/\D/g, "");
|
|
14
|
+
if (digits.length !== 11)
|
|
15
|
+
return false;
|
|
16
|
+
if (/^(\d)\1{10}$/.test(digits))
|
|
17
|
+
return false;
|
|
18
|
+
let sum = 0;
|
|
19
|
+
for (let i = 0; i < 9; i++)
|
|
20
|
+
sum += parseInt(digits[i]) * (10 - i);
|
|
21
|
+
let check = 11 - (sum % 11);
|
|
22
|
+
if (check >= 10)
|
|
23
|
+
check = 0;
|
|
24
|
+
if (parseInt(digits[9]) !== check)
|
|
25
|
+
return false;
|
|
26
|
+
sum = 0;
|
|
27
|
+
for (let i = 0; i < 10; i++)
|
|
28
|
+
sum += parseInt(digits[i]) * (11 - i);
|
|
29
|
+
check = 11 - (sum % 11);
|
|
30
|
+
if (check >= 10)
|
|
31
|
+
check = 0;
|
|
32
|
+
return parseInt(digits[10]) === check;
|
|
33
|
+
}
|
|
34
|
+
function validateCNPJ(cnpj) {
|
|
35
|
+
const digits = cnpj.replace(/\D/g, "");
|
|
36
|
+
if (digits.length !== 14)
|
|
37
|
+
return false;
|
|
38
|
+
const weights1 = [5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2];
|
|
39
|
+
const weights2 = [6, 5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2];
|
|
40
|
+
let sum = 0;
|
|
41
|
+
for (let i = 0; i < 12; i++)
|
|
42
|
+
sum += parseInt(digits[i]) * weights1[i];
|
|
43
|
+
let check = sum % 11 < 2 ? 0 : 11 - (sum % 11);
|
|
44
|
+
if (parseInt(digits[12]) !== check)
|
|
45
|
+
return false;
|
|
46
|
+
sum = 0;
|
|
47
|
+
for (let i = 0; i < 13; i++)
|
|
48
|
+
sum += parseInt(digits[i]) * weights2[i];
|
|
49
|
+
check = sum % 11 < 2 ? 0 : 11 - (sum % 11);
|
|
50
|
+
return parseInt(digits[13]) === check;
|
|
51
|
+
}
|
|
52
|
+
function maskKeepFormat(text, visibleEnd = 2) {
|
|
53
|
+
const digits = text.replace(/\D/g, "");
|
|
54
|
+
let idx = 0;
|
|
55
|
+
return text.replace(/\d/g, () => {
|
|
56
|
+
const d = idx++;
|
|
57
|
+
return d >= digits.length - visibleEnd ? digits[d] : "*";
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
const PII_PATTERNS = {
|
|
61
|
+
cpf: {
|
|
62
|
+
entity: "cpf",
|
|
63
|
+
regex: /\d{3}\.?\d{3}\.?\d{3}-?\d{2}/g,
|
|
64
|
+
maskFn: (m) => maskKeepFormat(m, 0),
|
|
65
|
+
validate: validateCPF,
|
|
66
|
+
},
|
|
67
|
+
cnpj: {
|
|
68
|
+
entity: "cnpj",
|
|
69
|
+
regex: /\d{2}\.?\d{3}\.?\d{3}\/?\d{4}-?\d{2}/g,
|
|
70
|
+
maskFn: (m) => maskKeepFormat(m, 0),
|
|
71
|
+
validate: validateCNPJ,
|
|
72
|
+
},
|
|
73
|
+
email: {
|
|
74
|
+
entity: "email",
|
|
75
|
+
regex: /[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/g,
|
|
76
|
+
maskFn: (m) => {
|
|
77
|
+
const [local, domain] = m.split("@");
|
|
78
|
+
return `${local[0]}***@${domain}`;
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
phone: {
|
|
82
|
+
entity: "phone",
|
|
83
|
+
regex: /\+?\d{1,3}[\s.-]?\(?\d{2,3}\)?[\s.-]?\d{4,5}[\s.-]?\d{4}/g,
|
|
84
|
+
maskFn: (m) => maskKeepFormat(m, 4),
|
|
85
|
+
},
|
|
86
|
+
credit_card: {
|
|
87
|
+
entity: "credit_card",
|
|
88
|
+
regex: /\d{4}[\s.-]?\d{4}[\s.-]?\d{4}[\s.-]?\d{4}/g,
|
|
89
|
+
maskFn: (m) => {
|
|
90
|
+
const d = m.replace(/\D/g, "");
|
|
91
|
+
return `****-****-****-${d.slice(-4)}`;
|
|
92
|
+
},
|
|
93
|
+
},
|
|
94
|
+
};
|
|
95
|
+
// ---------------------------------------------------------------------------
|
|
96
|
+
// RegexScanner — Tier 1, built-in, zero deps
|
|
97
|
+
// ---------------------------------------------------------------------------
|
|
98
|
+
export class RegexScanner {
|
|
99
|
+
patterns;
|
|
100
|
+
constructor(rules) {
|
|
101
|
+
this.patterns = [];
|
|
102
|
+
for (const rule of rules) {
|
|
103
|
+
if (rule.type !== "pii" &&
|
|
104
|
+
rule.type !== "prompt_injection" &&
|
|
105
|
+
rule.type !== "banned_words" &&
|
|
106
|
+
rule.type !== "custom_regex")
|
|
107
|
+
continue;
|
|
108
|
+
const tier = rule.tier ?? this.inferTier(rule);
|
|
109
|
+
if (tier !== "regex")
|
|
110
|
+
continue;
|
|
111
|
+
this.patterns.push(...this.buildPatterns(rule));
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
available() {
|
|
115
|
+
return true;
|
|
116
|
+
}
|
|
117
|
+
scan(text) {
|
|
118
|
+
const violations = [];
|
|
119
|
+
for (const { entity, regex, maskFn, validate } of this.patterns) {
|
|
120
|
+
// Create fresh regex to reset lastIndex
|
|
121
|
+
const re = new RegExp(regex.source, regex.flags);
|
|
122
|
+
let match;
|
|
123
|
+
while ((match = re.exec(text)) !== null) {
|
|
124
|
+
if (validate && !validate(match[0]))
|
|
125
|
+
continue;
|
|
126
|
+
violations.push({
|
|
127
|
+
ruleType: "pii",
|
|
128
|
+
entity,
|
|
129
|
+
text: match[0],
|
|
130
|
+
start: match.index,
|
|
131
|
+
end: match.index + match[0].length,
|
|
132
|
+
replacement: maskFn(match[0]),
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
return violations;
|
|
137
|
+
}
|
|
138
|
+
inferTier(rule) {
|
|
139
|
+
if (rule.type === "pii") {
|
|
140
|
+
const entities = rule.entities ?? [];
|
|
141
|
+
if (entities.some((e) => ["person", "location"].includes(e)))
|
|
142
|
+
return "ml";
|
|
143
|
+
return "regex";
|
|
144
|
+
}
|
|
145
|
+
return "regex";
|
|
146
|
+
}
|
|
147
|
+
buildPatterns(rule) {
|
|
148
|
+
const patterns = [];
|
|
149
|
+
if (rule.type === "pii") {
|
|
150
|
+
const entities = rule.entities ?? [];
|
|
151
|
+
for (const entity of entities) {
|
|
152
|
+
const p = PII_PATTERNS[entity];
|
|
153
|
+
if (p)
|
|
154
|
+
patterns.push(p);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
if (rule.type === "prompt_injection") {
|
|
158
|
+
patterns.push({
|
|
159
|
+
entity: "prompt_injection",
|
|
160
|
+
regex: /\b(ignore\s+(previous|above|all)\s+(instructions?|rules?|prompts?)|system\s+prompt|jailbreak|DAN\s+mode)\b/gi,
|
|
161
|
+
maskFn: () => "[BLOCKED]",
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
if (rule.type === "banned_words") {
|
|
165
|
+
const words = rule.words ?? [];
|
|
166
|
+
if (words.length > 0) {
|
|
167
|
+
const escaped = words.map((w) => w.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"));
|
|
168
|
+
patterns.push({
|
|
169
|
+
entity: "banned_word",
|
|
170
|
+
regex: new RegExp(`\\b(${escaped.join("|")})\\b`, "gi"),
|
|
171
|
+
maskFn: () => "[REDACTED]",
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
if (rule.type === "custom_regex") {
|
|
176
|
+
const regexPatterns = rule.patterns ?? [];
|
|
177
|
+
for (const p of regexPatterns) {
|
|
178
|
+
try {
|
|
179
|
+
patterns.push({
|
|
180
|
+
entity: "custom",
|
|
181
|
+
regex: new RegExp(p, "g"),
|
|
182
|
+
maskFn: () => "[REDACTED]",
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
catch {
|
|
186
|
+
/* invalid regex — skip */
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
return patterns;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
// ---------------------------------------------------------------------------
|
|
194
|
+
// ScannerPipeline
|
|
195
|
+
// ---------------------------------------------------------------------------
|
|
196
|
+
export class ScannerPipeline {
|
|
197
|
+
scanners;
|
|
198
|
+
constructor(rules) {
|
|
199
|
+
this.scanners = [new RegexScanner(rules)];
|
|
200
|
+
// Tier 2/3/4 would be added here if available
|
|
201
|
+
}
|
|
202
|
+
scan(text) {
|
|
203
|
+
const violations = [];
|
|
204
|
+
for (const scanner of this.scanners) {
|
|
205
|
+
if (scanner.available())
|
|
206
|
+
violations.push(...scanner.scan(text));
|
|
207
|
+
}
|
|
208
|
+
return { found: violations.length > 0, violations };
|
|
209
|
+
}
|
|
210
|
+
apply(text, action) {
|
|
211
|
+
const result = this.scan(text);
|
|
212
|
+
if (!result.found)
|
|
213
|
+
return text;
|
|
214
|
+
if (action === "block") {
|
|
215
|
+
throw new Error(`SafetyPolicy violation: ${result.violations.map((v) => v.entity).join(", ")}`);
|
|
216
|
+
}
|
|
217
|
+
if (action === "mask") {
|
|
218
|
+
// Sort violations by position descending to replace from end to start
|
|
219
|
+
const sorted = [...result.violations].sort((a, b) => b.start - a.start);
|
|
220
|
+
let masked = text;
|
|
221
|
+
for (const v of sorted) {
|
|
222
|
+
masked = masked.slice(0, v.start) + v.replacement + masked.slice(v.end);
|
|
223
|
+
}
|
|
224
|
+
return masked;
|
|
225
|
+
}
|
|
226
|
+
return text; // log: no modification
|
|
227
|
+
}
|
|
228
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { Kernel } from "./index.js";
|
|
2
|
+
/**
|
|
3
|
+
* A single file emitted by a document serializer. Re-exports the
|
|
4
|
+
* canonical shape from protocols.ts (L3 2026-05-25: now accepts
|
|
5
|
+
* `contentBytes?: Uint8Array` for binary entries alongside `content`).
|
|
6
|
+
*
|
|
7
|
+
* `relativePath` is relative to the manifest scope directory. The file-based
|
|
8
|
+
* adapter composing this helper is responsible for prefixing `<baseDir>/<scope>/`
|
|
9
|
+
* when it writes to disk.
|
|
10
|
+
*/
|
|
11
|
+
export type { SerializedFile } from "./protocols.js";
|
|
12
|
+
import type { SerializedFile as _SerializedFile } from "./protocols.js";
|
|
13
|
+
/**
|
|
14
|
+
* Turn a raw document into the files a file-based adapter should write.
|
|
15
|
+
*
|
|
16
|
+
* Pure computation — no I/O, no mutation of `raw`. Adapters compose this
|
|
17
|
+
* with their filesystem I/O (TauriWritableSource, FilesystemWritableSource
|
|
18
|
+
* when it ports to TS, etc.).
|
|
19
|
+
*
|
|
20
|
+
* Internally delegates to `kernel.serializeDocument`, which already
|
|
21
|
+
* encapsulates the Kind/Writer lookup + StorageDescriptor path prefixing.
|
|
22
|
+
* Extracting this as a named helper is a contract move: adapters should
|
|
23
|
+
* import `serializeRawToFiles`, not reach into the Kernel's internals.
|
|
24
|
+
*
|
|
25
|
+
* The scope argument is intentionally NOT exposed here — scope is a
|
|
26
|
+
* path-prefix concern the ADAPTER applies. `serializeDocument` treats it
|
|
27
|
+
* as "" internally, so paths come back relative to the scope root.
|
|
28
|
+
*/
|
|
29
|
+
export declare function serializeRawToFiles(raw: Record<string, unknown>, kernel: Kernel): readonly _SerializedFile[];
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Turn a raw document into the files a file-based adapter should write.
|
|
3
|
+
*
|
|
4
|
+
* Pure computation — no I/O, no mutation of `raw`. Adapters compose this
|
|
5
|
+
* with their filesystem I/O (TauriWritableSource, FilesystemWritableSource
|
|
6
|
+
* when it ports to TS, etc.).
|
|
7
|
+
*
|
|
8
|
+
* Internally delegates to `kernel.serializeDocument`, which already
|
|
9
|
+
* encapsulates the Kind/Writer lookup + StorageDescriptor path prefixing.
|
|
10
|
+
* Extracting this as a named helper is a contract move: adapters should
|
|
11
|
+
* import `serializeRawToFiles`, not reach into the Kernel's internals.
|
|
12
|
+
*
|
|
13
|
+
* The scope argument is intentionally NOT exposed here — scope is a
|
|
14
|
+
* path-prefix concern the ADAPTER applies. `serializeDocument` treats it
|
|
15
|
+
* as "" internally, so paths come back relative to the scope root.
|
|
16
|
+
*/
|
|
17
|
+
export function serializeRawToFiles(raw, kernel) {
|
|
18
|
+
const scope = "";
|
|
19
|
+
const kind = String(raw.kind ?? "");
|
|
20
|
+
const name = String((raw.metadata ?? {}).name ?? "");
|
|
21
|
+
const payload = kernel.serializeDocument(scope, kind, name, raw);
|
|
22
|
+
return payload.files;
|
|
23
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generic accessors for Document.spec — lets consumers read fields without
|
|
3
|
+
* knowing the kind's typed model. Returns undefined / [] / {} for missing
|
|
4
|
+
* fields; throws a clear error on type mismatch (wrong field type in the
|
|
5
|
+
* actual doc).
|
|
6
|
+
*
|
|
7
|
+
* Use these in extension code, renderers, and tools that operate across
|
|
8
|
+
* many kinds. For single-kind code that already branched on kind, using
|
|
9
|
+
* the typed model via `doc.typed` is fine.
|
|
10
|
+
*/
|
|
11
|
+
import type { Document } from "./document.js";
|
|
12
|
+
export declare function readSpecString(doc: Document | {
|
|
13
|
+
spec?: Record<string, unknown>;
|
|
14
|
+
}, field: string): string | undefined;
|
|
15
|
+
export declare function readSpecStringArray(doc: Document | {
|
|
16
|
+
spec?: Record<string, unknown>;
|
|
17
|
+
}, field: string): string[];
|
|
18
|
+
export declare function readSpecRecord(doc: Document | {
|
|
19
|
+
spec?: Record<string, unknown>;
|
|
20
|
+
}, field: string): Record<string, unknown>;
|
|
21
|
+
export declare function readSpecRecordArray(doc: Document | {
|
|
22
|
+
spec?: Record<string, unknown>;
|
|
23
|
+
}, field: string): Record<string, unknown>[];
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
function getSpec(doc) {
|
|
2
|
+
const spec = doc.spec;
|
|
3
|
+
return (spec && typeof spec === "object") ? spec : {};
|
|
4
|
+
}
|
|
5
|
+
export function readSpecString(doc, field) {
|
|
6
|
+
const v = getSpec(doc)[field];
|
|
7
|
+
if (v === undefined || v === null)
|
|
8
|
+
return undefined;
|
|
9
|
+
if (typeof v !== "string") {
|
|
10
|
+
throw new TypeError(`spec['${field}']: expected string, got ${typeof v}`);
|
|
11
|
+
}
|
|
12
|
+
return v;
|
|
13
|
+
}
|
|
14
|
+
export function readSpecStringArray(doc, field) {
|
|
15
|
+
const v = getSpec(doc)[field];
|
|
16
|
+
if (v === undefined || v === null)
|
|
17
|
+
return [];
|
|
18
|
+
if (!Array.isArray(v)) {
|
|
19
|
+
throw new TypeError(`spec['${field}']: expected array, got ${typeof v}`);
|
|
20
|
+
}
|
|
21
|
+
return v;
|
|
22
|
+
}
|
|
23
|
+
export function readSpecRecord(doc, field) {
|
|
24
|
+
const v = getSpec(doc)[field];
|
|
25
|
+
if (v === undefined || v === null)
|
|
26
|
+
return {};
|
|
27
|
+
if (typeof v !== "object" || Array.isArray(v)) {
|
|
28
|
+
throw new TypeError(`spec['${field}']: expected object, got ${typeof v}`);
|
|
29
|
+
}
|
|
30
|
+
return v;
|
|
31
|
+
}
|
|
32
|
+
export function readSpecRecordArray(doc, field) {
|
|
33
|
+
const v = getSpec(doc)[field];
|
|
34
|
+
if (v === undefined || v === null)
|
|
35
|
+
return [];
|
|
36
|
+
if (!Array.isArray(v)) {
|
|
37
|
+
throw new TypeError(`spec['${field}']: expected array, got ${typeof v}`);
|
|
38
|
+
}
|
|
39
|
+
return v;
|
|
40
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* StudioUIMetadata — UI declarations attached to a KindPort.
|
|
3
|
+
*
|
|
4
|
+
* Net-new TS twin of Python `dna.kernel.studio_ui.StudioUIMetadata`
|
|
5
|
+
* (studio_ui.py:54-215), added for the descriptor-expressiveness `ui:` field
|
|
6
|
+
* (spec 2026-06-11 D1). Reverses the previously-documented posture that `ui`
|
|
7
|
+
* was "a Studio-backend concern… intentionally not mirrored"
|
|
8
|
+
* (src/extensions/audit.ts) — byte-parity of `.kind.yaml` descriptors across
|
|
9
|
+
* runtimes requires BOTH the Python and the TS DeclarativeKindPort to parse the
|
|
10
|
+
* same `ui:` block into the same data and serialize it identically.
|
|
11
|
+
*
|
|
12
|
+
* Field-for-field mirror + `toDict()` (same omission rules as Python
|
|
13
|
+
* `to_dict()`) + `resolveLabel()` (same i18n fallback order as Python
|
|
14
|
+
* `resolve_label()`). There is no TS class golden for `ui` (TS Kind classes
|
|
15
|
+
* never carried it); the parity pin is structural — `toDict()` parity with
|
|
16
|
+
* Python via the shared fixture tests/fixtures/studio-ui-parity.json.
|
|
17
|
+
*/
|
|
18
|
+
/** Studio mode this Kind primarily belongs to. */
|
|
19
|
+
export type ModeId = "plan" | "build" | "quality" | "govern" | "cognitive";
|
|
20
|
+
/** CRUD action a route serves. */
|
|
21
|
+
export type UIAction = "list" | "detail" | "edit" | "create";
|
|
22
|
+
/** Locale → translated string. e.g. {en: 'Eval Cases', 'pt-BR': 'Casos'}. */
|
|
23
|
+
export type LabelI18n = Record<string, string>;
|
|
24
|
+
/**
|
|
25
|
+
* Canonical, ordered set of StudioUIMetadata field names. Single source of
|
|
26
|
+
* truth — `KindDefinitionSpec.from_raw`/zod validate `ui:` keys against THIS
|
|
27
|
+
* list (no second hardcoded list). Mirrors the Python dataclass field order.
|
|
28
|
+
*/
|
|
29
|
+
export declare const UI_METADATA_FIELDS: readonly ["mode", "in_sidebar", "display_order", "label", "icon", "description", "breadcrumb", "routes", "permissions", "note", "feature_flag"];
|
|
30
|
+
export type UIMetadataField = (typeof UI_METADATA_FIELDS)[number];
|
|
31
|
+
/** Constructor input — every field optional, mirroring the Python dataclass
|
|
32
|
+
* defaults (no mode, no sidebar, display_order 100, all others None/empty). */
|
|
33
|
+
export interface StudioUIMetadataInit {
|
|
34
|
+
mode?: ModeId | null;
|
|
35
|
+
in_sidebar?: boolean;
|
|
36
|
+
display_order?: number;
|
|
37
|
+
label?: string | LabelI18n | null;
|
|
38
|
+
icon?: string | null;
|
|
39
|
+
description?: string | LabelI18n | null;
|
|
40
|
+
breadcrumb?: string[] | null;
|
|
41
|
+
routes?: Record<string, string>;
|
|
42
|
+
permissions?: Record<string, string[] | string>;
|
|
43
|
+
note?: string | null;
|
|
44
|
+
feature_flag?: string | null;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* UI declarations for a Kind, consumed by Studio's manifest. Defaults are
|
|
48
|
+
* conservative: no mode, no sidebar, no routes — the Kind is invisible in
|
|
49
|
+
* Studio unless explicitly opted in. 1:1 with Python `StudioUIMetadata`.
|
|
50
|
+
*/
|
|
51
|
+
export declare class StudioUIMetadata {
|
|
52
|
+
readonly mode: ModeId | null;
|
|
53
|
+
readonly in_sidebar: boolean;
|
|
54
|
+
readonly display_order: number;
|
|
55
|
+
readonly label: string | LabelI18n | null;
|
|
56
|
+
readonly icon: string | null;
|
|
57
|
+
readonly description: string | LabelI18n | null;
|
|
58
|
+
readonly breadcrumb: string[] | null;
|
|
59
|
+
readonly routes: Record<string, string>;
|
|
60
|
+
readonly permissions: Record<string, string[] | string>;
|
|
61
|
+
readonly note: string | null;
|
|
62
|
+
readonly feature_flag: string | null;
|
|
63
|
+
constructor(init?: StudioUIMetadataInit);
|
|
64
|
+
/** The canonical field-name set (single source of truth for `ui:` validation). */
|
|
65
|
+
static fields(): readonly UIMetadataField[];
|
|
66
|
+
/**
|
|
67
|
+
* Serialize for the /kinds/manifest JSON response. Omits None/empty fields
|
|
68
|
+
* so the wire payload stays small — EXACTLY the Python `to_dict()` omission
|
|
69
|
+
* rules (studio_ui.py:161-192):
|
|
70
|
+
* - mode: only if not null
|
|
71
|
+
* - in_sidebar: only if true
|
|
72
|
+
* - display_order: only if !== 100
|
|
73
|
+
* - label/icon/description/note/feature_flag: only if not null
|
|
74
|
+
* - breadcrumb: only if not null (an empty list IS kept, like Python)
|
|
75
|
+
* - routes/permissions: only if non-empty
|
|
76
|
+
*/
|
|
77
|
+
toDict(): Record<string, unknown>;
|
|
78
|
+
/**
|
|
79
|
+
* Resolve i18n label for the requested locale. Fallback order (mirrors
|
|
80
|
+
* Python `resolve_label`):
|
|
81
|
+
* 1. Exact locale match (e.g. 'pt-BR').
|
|
82
|
+
* 2. Language-only fallback (e.g. 'pt' from 'pt-BR').
|
|
83
|
+
* 3. 'en' (project default).
|
|
84
|
+
* 4. null.
|
|
85
|
+
* A plain-string label is returned as-is regardless of locale.
|
|
86
|
+
*/
|
|
87
|
+
resolveLabel(locale?: string): string | null;
|
|
88
|
+
}
|