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,184 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BundleHandle — source-agnostic interface for reading + writing a
|
|
3
|
+
* bundle's entries.
|
|
4
|
+
*
|
|
5
|
+
* 1:1 parity with python/dna/kernel/bundle_handle.py.
|
|
6
|
+
*
|
|
7
|
+
* Implementations:
|
|
8
|
+
* - `FilesystemBundleHandle` — wraps a real filesystem directory.
|
|
9
|
+
* - `DictBundleHandle` — in-memory, used by SQL/Postgres adapters
|
|
10
|
+
* (rehydrate bundle entries from `dna_bundle_entries` rows into
|
|
11
|
+
* a dict, hand to readers).
|
|
12
|
+
* - `PostgresBundleHandle` (Postgres adapter) — backed by
|
|
13
|
+
* `dna_bundle_entries` rows.
|
|
14
|
+
*
|
|
15
|
+
* Entry naming convention: a posix-style relative path inside the
|
|
16
|
+
* bundle. Top-level entries are bare names (`"SKILL.md"`,
|
|
17
|
+
* `"IDENTITY.md"`); nested entries use forward slashes
|
|
18
|
+
* (`"scripts/run.py"`, `"references/spec.md"`).
|
|
19
|
+
*
|
|
20
|
+
* v1.0 async refactor: read/write methods return `Promise<...>`.
|
|
21
|
+
* Async-everywhere is the contract — sync local-FS impls wrap their
|
|
22
|
+
* sync IO in `Promise.resolve` (true `fs/promises` migration is a
|
|
23
|
+
* v1.2 cleanup).
|
|
24
|
+
*/
|
|
25
|
+
import { access, readFile, readdir, stat, writeFile, mkdir } from "node:fs/promises";
|
|
26
|
+
import { join, dirname, basename, posix } from "node:path";
|
|
27
|
+
/** True iff the path exists. fs/promises doesn't ship a boolean
|
|
28
|
+
* `exists`; this is the canonical workaround. */
|
|
29
|
+
async function pathExists(p) {
|
|
30
|
+
try {
|
|
31
|
+
await access(p);
|
|
32
|
+
return true;
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
// ---------------------------------------------------------------------------
|
|
39
|
+
// Filesystem implementation
|
|
40
|
+
// ---------------------------------------------------------------------------
|
|
41
|
+
export class FilesystemBundleHandle {
|
|
42
|
+
_root;
|
|
43
|
+
constructor(_root) {
|
|
44
|
+
this._root = _root;
|
|
45
|
+
}
|
|
46
|
+
get name() { return basename(this._root); }
|
|
47
|
+
get path() { return this._root; }
|
|
48
|
+
async exists(entry) {
|
|
49
|
+
return pathExists(join(this._root, entry));
|
|
50
|
+
}
|
|
51
|
+
async readText(entry, encoding = "utf-8") {
|
|
52
|
+
const p = join(this._root, entry);
|
|
53
|
+
try {
|
|
54
|
+
return await readFile(p, encoding);
|
|
55
|
+
}
|
|
56
|
+
catch (e) {
|
|
57
|
+
if (e.code === "ENOENT") {
|
|
58
|
+
const err = new Error(`Bundle entry not found: ${entry}`);
|
|
59
|
+
err.code = "ENOENT";
|
|
60
|
+
throw err;
|
|
61
|
+
}
|
|
62
|
+
throw e;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
async readBytes(entry) {
|
|
66
|
+
const p = join(this._root, entry);
|
|
67
|
+
try {
|
|
68
|
+
return new Uint8Array(await readFile(p));
|
|
69
|
+
}
|
|
70
|
+
catch (e) {
|
|
71
|
+
if (e.code === "ENOENT") {
|
|
72
|
+
const err = new Error(`Bundle entry not found: ${entry}`);
|
|
73
|
+
err.code = "ENOENT";
|
|
74
|
+
throw err;
|
|
75
|
+
}
|
|
76
|
+
throw e;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
async iterEntries(recursive = false) {
|
|
80
|
+
if (!(await pathExists(this._root)))
|
|
81
|
+
return [];
|
|
82
|
+
const out = [];
|
|
83
|
+
if (recursive) {
|
|
84
|
+
const walk = async (dir, prefix) => {
|
|
85
|
+
const entries = (await readdir(dir)).sort();
|
|
86
|
+
for (const entry of entries) {
|
|
87
|
+
const full = join(dir, entry);
|
|
88
|
+
const rel = prefix ? posix.join(prefix, entry) : entry;
|
|
89
|
+
const st = await stat(full);
|
|
90
|
+
if (st.isDirectory())
|
|
91
|
+
await walk(full, rel);
|
|
92
|
+
else
|
|
93
|
+
out.push(rel);
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
await walk(this._root, "");
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
for (const entry of (await readdir(this._root)).sort())
|
|
100
|
+
out.push(entry);
|
|
101
|
+
}
|
|
102
|
+
return out;
|
|
103
|
+
}
|
|
104
|
+
async isFile(entry) {
|
|
105
|
+
const p = join(this._root, entry);
|
|
106
|
+
try {
|
|
107
|
+
return (await stat(p)).isFile();
|
|
108
|
+
}
|
|
109
|
+
catch {
|
|
110
|
+
return false;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
async writeText(entry, content, encoding = "utf-8") {
|
|
114
|
+
const p = join(this._root, entry);
|
|
115
|
+
await mkdir(dirname(p), { recursive: true });
|
|
116
|
+
await writeFile(p, content, encoding);
|
|
117
|
+
}
|
|
118
|
+
async writeBytes(entry, content) {
|
|
119
|
+
const p = join(this._root, entry);
|
|
120
|
+
await mkdir(dirname(p), { recursive: true });
|
|
121
|
+
await writeFile(p, Buffer.from(content));
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
// ---------------------------------------------------------------------------
|
|
125
|
+
// In-memory implementation (used by tests + SQL adapters)
|
|
126
|
+
// ---------------------------------------------------------------------------
|
|
127
|
+
export class DictBundleHandle {
|
|
128
|
+
name;
|
|
129
|
+
_entries;
|
|
130
|
+
constructor(name, entries = {}) {
|
|
131
|
+
this.name = name;
|
|
132
|
+
this._entries = new Map(Object.entries(entries));
|
|
133
|
+
}
|
|
134
|
+
get path() { return null; }
|
|
135
|
+
async exists(entry) {
|
|
136
|
+
if (this._entries.has(entry))
|
|
137
|
+
return true;
|
|
138
|
+
// Treat parent paths as "directory entries" — true if any key
|
|
139
|
+
// starts with `entry/`.
|
|
140
|
+
const prefix = entry.endsWith("/") ? entry : `${entry}/`;
|
|
141
|
+
for (const k of this._entries.keys())
|
|
142
|
+
if (k.startsWith(prefix))
|
|
143
|
+
return true;
|
|
144
|
+
return false;
|
|
145
|
+
}
|
|
146
|
+
async readText(entry, _encoding = "utf-8") {
|
|
147
|
+
const v = this._entries.get(entry);
|
|
148
|
+
if (v === undefined) {
|
|
149
|
+
const err = new Error(`Bundle entry not found: ${entry}`);
|
|
150
|
+
err.code = "ENOENT";
|
|
151
|
+
throw err;
|
|
152
|
+
}
|
|
153
|
+
return typeof v === "string" ? v : new TextDecoder().decode(v);
|
|
154
|
+
}
|
|
155
|
+
async readBytes(entry) {
|
|
156
|
+
const v = this._entries.get(entry);
|
|
157
|
+
if (v === undefined) {
|
|
158
|
+
const err = new Error(`Bundle entry not found: ${entry}`);
|
|
159
|
+
err.code = "ENOENT";
|
|
160
|
+
throw err;
|
|
161
|
+
}
|
|
162
|
+
return typeof v === "string" ? new TextEncoder().encode(v) : v;
|
|
163
|
+
}
|
|
164
|
+
async iterEntries(recursive = false) {
|
|
165
|
+
if (recursive)
|
|
166
|
+
return Array.from(this._entries.keys()).sort();
|
|
167
|
+
// Direct children only — extract first segment, deduplicate
|
|
168
|
+
const directChildren = new Set();
|
|
169
|
+
for (const k of this._entries.keys()) {
|
|
170
|
+
const slash = k.indexOf("/");
|
|
171
|
+
directChildren.add(slash === -1 ? k : k.substring(0, slash));
|
|
172
|
+
}
|
|
173
|
+
return Array.from(directChildren).sort();
|
|
174
|
+
}
|
|
175
|
+
async isFile(entry) {
|
|
176
|
+
return this._entries.has(entry);
|
|
177
|
+
}
|
|
178
|
+
async writeText(entry, content, _encoding = "utf-8") {
|
|
179
|
+
this._entries.set(entry, content);
|
|
180
|
+
}
|
|
181
|
+
async writeBytes(entry, content) {
|
|
182
|
+
this._entries.set(entry, content);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Optional capability interfaces for source/cache/etc adapters.
|
|
3
|
+
*
|
|
4
|
+
* 1:1 parity with python/dna/kernel/capabilities.py.
|
|
5
|
+
*
|
|
6
|
+
* H2 — Replaces ad-hoc structural checks (`"fetchBundleEntry" in src`)
|
|
7
|
+
* with discoverable, type-checkable interface assertions. A developer
|
|
8
|
+
* authoring a custom adapter sees the available capabilities by
|
|
9
|
+
* importing this module, instead of grepping the kernel source.
|
|
10
|
+
*
|
|
11
|
+
* Why these are separate from the core `SourcePort` interface:
|
|
12
|
+
*
|
|
13
|
+
* - The core interfaces (SourcePort, WritableSourcePort, CachePort,
|
|
14
|
+
* ...) define the **mandatory** contract every adapter must
|
|
15
|
+
* implement.
|
|
16
|
+
* - These capability interfaces define **optional** features —
|
|
17
|
+
* adapters can declare support by structurally matching the
|
|
18
|
+
* signature, OR omit the methods entirely without breaking the
|
|
19
|
+
* core contract.
|
|
20
|
+
*
|
|
21
|
+
* TypeScript's structural typing makes capability detection at runtime
|
|
22
|
+
* a method-presence check (`typeof src.fetchBundleEntry === "function"`).
|
|
23
|
+
* Use the helper guards (`isBundleEntryReadable`, etc.) for clarity.
|
|
24
|
+
*
|
|
25
|
+
* Adding a new capability:
|
|
26
|
+
* 1. Define `MyCapability` interface here.
|
|
27
|
+
* 2. Add an `isMyCapability(x): x is MyCapability` type guard.
|
|
28
|
+
* 3. Replace any `if ("method" in adapter)` checks with
|
|
29
|
+
* `if (isMyCapability(adapter))`.
|
|
30
|
+
* 4. Document the capability in `docs/PORT-CONTRACT.md`.
|
|
31
|
+
* 5. Cover it in `tests/portContract.test.ts`.
|
|
32
|
+
*/
|
|
33
|
+
import type { Kernel } from "./index.js";
|
|
34
|
+
/**
|
|
35
|
+
* Source adapter capability: fetch a single bundle entry by name.
|
|
36
|
+
*
|
|
37
|
+
* The kernel uses this to read large binary payloads (graph.json,
|
|
38
|
+
* tree.json, ...) without rehydrating the whole bundle through the
|
|
39
|
+
* Reader pipeline. Implementing adapters store bundle entries in
|
|
40
|
+
* their backing store (filesystem dir, `dna_bundle_entries` SQL
|
|
41
|
+
* table) and serve byte payloads directly.
|
|
42
|
+
*
|
|
43
|
+
* Tenant overlay routing: when `tenant` is provided and the adapter
|
|
44
|
+
* supports it, the tenant-scoped copy is preferred over the base
|
|
45
|
+
* layer.
|
|
46
|
+
*
|
|
47
|
+
* Throws `Error` (with code `ENOENT` or message containing "not
|
|
48
|
+
* found") when the bundle or entry is absent.
|
|
49
|
+
*/
|
|
50
|
+
export interface BundleEntryReadable {
|
|
51
|
+
fetchBundleEntry(scope: string, container: string, name: string, entry: string,
|
|
52
|
+
/**
|
|
53
|
+
* Optional knobs:
|
|
54
|
+
* - ``tenant`` — when set, prefer the tenant overlay row
|
|
55
|
+
* (with base-layer fallback).
|
|
56
|
+
* - ``kind`` — disambiguates SQL row collision when two
|
|
57
|
+
* Kinds share a bundle ``name`` in the same scope (e.g.
|
|
58
|
+
* a Skill ``foo`` and a GraphifyArtifact ``foo``).
|
|
59
|
+
* Filesystem adapters ignore this since each container
|
|
60
|
+
* is a directory namespace.
|
|
61
|
+
*/
|
|
62
|
+
options?: {
|
|
63
|
+
tenant?: string | null;
|
|
64
|
+
kind?: string | null;
|
|
65
|
+
}): Promise<Uint8Array>;
|
|
66
|
+
}
|
|
67
|
+
export declare function isBundleEntryReadable(x: unknown): x is BundleEntryReadable;
|
|
68
|
+
/**
|
|
69
|
+
* Source adapter capability: accept post-init kernel wiring.
|
|
70
|
+
*
|
|
71
|
+
* H2 unification: `Kernel.auto({ source })` previously had a
|
|
72
|
+
* hardcoded `source instanceof FilesystemWritableSource` check that
|
|
73
|
+
* wired `_writers` and `setKernel(k)`. SQLite/Postgres adapters
|
|
74
|
+
* required the same wiring but only got it via the harness's
|
|
75
|
+
* source factory — leaving direct
|
|
76
|
+
* `new Kernel().writableSource(new SqliteSource(...))` callers with
|
|
77
|
+
* a half-broken kernel.
|
|
78
|
+
*
|
|
79
|
+
* Adapters now declare attachability by implementing
|
|
80
|
+
* `attachKernel(kernel)`. The kernel calls this method on every
|
|
81
|
+
* source it accepts — uniformly. Implementations install the
|
|
82
|
+
* kernel's `_writers`, `_readers`, and (optionally) a back-ref to
|
|
83
|
+
* the kernel itself for the source's save path to consult
|
|
84
|
+
* `storageForKind`.
|
|
85
|
+
*
|
|
86
|
+
* Idempotent: calling twice with the same kernel produces the same
|
|
87
|
+
* wired state.
|
|
88
|
+
*/
|
|
89
|
+
export interface KernelAttachable {
|
|
90
|
+
attachKernel(kernel: Kernel): void;
|
|
91
|
+
}
|
|
92
|
+
export declare function isKernelAttachable(x: unknown): x is KernelAttachable;
|
|
93
|
+
/**
|
|
94
|
+
* Source adapter capability: per-Kind semver versioning.
|
|
95
|
+
*
|
|
96
|
+
* Backs the catalog versioning flow: a Kind that's `Versionable`
|
|
97
|
+
* supports `getVersion(scope, kind, name, versionId)` and
|
|
98
|
+
* `listVersions(...)`. The harness REST surface checks for this
|
|
99
|
+
* capability via `isVersionable` to decide whether to expose the
|
|
100
|
+
* `/catalog/{owner}/{name}/versions` endpoint (501 otherwise).
|
|
101
|
+
*
|
|
102
|
+
* Custom adapters that don't track per-doc versions can omit and
|
|
103
|
+
* the harness will degrade gracefully with a 501 response.
|
|
104
|
+
*/
|
|
105
|
+
export interface Versionable {
|
|
106
|
+
getVersion(scope: string, kind: string, name: string, versionId: string): Promise<Record<string, unknown>>;
|
|
107
|
+
}
|
|
108
|
+
export declare function isVersionable(x: unknown): x is Versionable;
|
|
109
|
+
/**
|
|
110
|
+
* Typed view of what a source adapter supports — TS twin of the Py
|
|
111
|
+
* `SourceCapabilities` dataclass (s-sourceport-contract-cleanup).
|
|
112
|
+
*
|
|
113
|
+
* Adapters DECLARE this explicitly (`capabilities()` returns a literal);
|
|
114
|
+
* the kernel consults {@link sourceCapabilities} instead of scattering
|
|
115
|
+
* `typeof src.query === "function"` feature-tests. {@link deriveCapabilities}
|
|
116
|
+
* (structural probing) survives as (a) the conformance-test oracle that
|
|
117
|
+
* keeps declarations honest and (b) the deprecated fallback for external
|
|
118
|
+
* adapters that don't declare yet.
|
|
119
|
+
*
|
|
120
|
+
* Py-only fields with no TS meaning are omitted on purpose:
|
|
121
|
+
* `write_kwargs`/`delete_kwargs`/`tenant_layer_writes` exist in Python
|
|
122
|
+
* because optional kwargs must be probed via `inspect.signature`; the TS
|
|
123
|
+
* write surface is an options bag, so there is nothing to probe.
|
|
124
|
+
*/
|
|
125
|
+
export interface SourceCapabilities {
|
|
126
|
+
/** Human-readable adapter label ("filesystem", "postgres", ...). */
|
|
127
|
+
source: string;
|
|
128
|
+
drafts: boolean;
|
|
129
|
+
versions: boolean;
|
|
130
|
+
layers: boolean;
|
|
131
|
+
bundleRead: boolean;
|
|
132
|
+
bundleWrite: boolean;
|
|
133
|
+
kernelAttachable: boolean;
|
|
134
|
+
/** Implements the L1 granular reads (independent flags on purpose —
|
|
135
|
+
* the TS FS source ships `loadOne` but not `listDocRefs`). */
|
|
136
|
+
granularList: boolean;
|
|
137
|
+
granularOne: boolean;
|
|
138
|
+
/** Implements `query`/`count` natively (FS: native but in-memory). */
|
|
139
|
+
queryPushdown: boolean;
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Build a {@link SourceCapabilities} for `source` by structural probing —
|
|
143
|
+
* the reflection oracle. In-repo adapters declare literals instead; the
|
|
144
|
+
* conformance test asserts declaration == derivation.
|
|
145
|
+
*/
|
|
146
|
+
export declare function deriveCapabilities(source: unknown, label: string): SourceCapabilities;
|
|
147
|
+
/**
|
|
148
|
+
* THE kernel-side accessor for a source's capabilities (TS twin of the
|
|
149
|
+
* Py `source_capabilities`). Resolution order (memoized per instance):
|
|
150
|
+
*
|
|
151
|
+
* 1. the adapter's own `capabilities()` returning a
|
|
152
|
+
* {@link SourceCapabilities} — the explicit declaration;
|
|
153
|
+
* 2. DEPRECATED fallback: {@link deriveCapabilities} structural probing,
|
|
154
|
+
* with a once-per-constructor console warning pointing at the
|
|
155
|
+
* migration (keeps external adapters working).
|
|
156
|
+
*/
|
|
157
|
+
export declare function sourceCapabilities(source: object): SourceCapabilities;
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Optional capability interfaces for source/cache/etc adapters.
|
|
3
|
+
*
|
|
4
|
+
* 1:1 parity with python/dna/kernel/capabilities.py.
|
|
5
|
+
*
|
|
6
|
+
* H2 — Replaces ad-hoc structural checks (`"fetchBundleEntry" in src`)
|
|
7
|
+
* with discoverable, type-checkable interface assertions. A developer
|
|
8
|
+
* authoring a custom adapter sees the available capabilities by
|
|
9
|
+
* importing this module, instead of grepping the kernel source.
|
|
10
|
+
*
|
|
11
|
+
* Why these are separate from the core `SourcePort` interface:
|
|
12
|
+
*
|
|
13
|
+
* - The core interfaces (SourcePort, WritableSourcePort, CachePort,
|
|
14
|
+
* ...) define the **mandatory** contract every adapter must
|
|
15
|
+
* implement.
|
|
16
|
+
* - These capability interfaces define **optional** features —
|
|
17
|
+
* adapters can declare support by structurally matching the
|
|
18
|
+
* signature, OR omit the methods entirely without breaking the
|
|
19
|
+
* core contract.
|
|
20
|
+
*
|
|
21
|
+
* TypeScript's structural typing makes capability detection at runtime
|
|
22
|
+
* a method-presence check (`typeof src.fetchBundleEntry === "function"`).
|
|
23
|
+
* Use the helper guards (`isBundleEntryReadable`, etc.) for clarity.
|
|
24
|
+
*
|
|
25
|
+
* Adding a new capability:
|
|
26
|
+
* 1. Define `MyCapability` interface here.
|
|
27
|
+
* 2. Add an `isMyCapability(x): x is MyCapability` type guard.
|
|
28
|
+
* 3. Replace any `if ("method" in adapter)` checks with
|
|
29
|
+
* `if (isMyCapability(adapter))`.
|
|
30
|
+
* 4. Document the capability in `docs/PORT-CONTRACT.md`.
|
|
31
|
+
* 5. Cover it in `tests/portContract.test.ts`.
|
|
32
|
+
*/
|
|
33
|
+
export function isBundleEntryReadable(x) {
|
|
34
|
+
return typeof x?.fetchBundleEntry === "function";
|
|
35
|
+
}
|
|
36
|
+
export function isKernelAttachable(x) {
|
|
37
|
+
return typeof x?.attachKernel === "function";
|
|
38
|
+
}
|
|
39
|
+
export function isVersionable(x) {
|
|
40
|
+
return typeof x?.getVersion === "function";
|
|
41
|
+
}
|
|
42
|
+
function _fn(x, name) {
|
|
43
|
+
return typeof x?.[name] === "function";
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Build a {@link SourceCapabilities} for `source` by structural probing —
|
|
47
|
+
* the reflection oracle. In-repo adapters declare literals instead; the
|
|
48
|
+
* conformance test asserts declaration == derivation.
|
|
49
|
+
*/
|
|
50
|
+
export function deriveCapabilities(source, label) {
|
|
51
|
+
return {
|
|
52
|
+
source: label,
|
|
53
|
+
drafts: _fn(source, "loadDrafts") && _fn(source, "publish"),
|
|
54
|
+
versions: _fn(source, "getVersion"),
|
|
55
|
+
layers: _fn(source, "loadLayer"),
|
|
56
|
+
bundleRead: _fn(source, "fetchBundleEntry"),
|
|
57
|
+
bundleWrite: _fn(source, "writeBundleEntry"),
|
|
58
|
+
kernelAttachable: _fn(source, "attachKernel"),
|
|
59
|
+
granularList: _fn(source, "listDocRefs"),
|
|
60
|
+
granularOne: _fn(source, "loadOne"),
|
|
61
|
+
queryPushdown: _fn(source, "query"),
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
const _capsCache = new WeakMap();
|
|
65
|
+
const _warnedUndeclared = new Set();
|
|
66
|
+
/**
|
|
67
|
+
* THE kernel-side accessor for a source's capabilities (TS twin of the
|
|
68
|
+
* Py `source_capabilities`). Resolution order (memoized per instance):
|
|
69
|
+
*
|
|
70
|
+
* 1. the adapter's own `capabilities()` returning a
|
|
71
|
+
* {@link SourceCapabilities} — the explicit declaration;
|
|
72
|
+
* 2. DEPRECATED fallback: {@link deriveCapabilities} structural probing,
|
|
73
|
+
* with a once-per-constructor console warning pointing at the
|
|
74
|
+
* migration (keeps external adapters working).
|
|
75
|
+
*/
|
|
76
|
+
export function sourceCapabilities(source) {
|
|
77
|
+
const cached = _capsCache.get(source);
|
|
78
|
+
if (cached)
|
|
79
|
+
return cached;
|
|
80
|
+
let caps = null;
|
|
81
|
+
const fn = source.capabilities;
|
|
82
|
+
if (typeof fn === "function") {
|
|
83
|
+
try {
|
|
84
|
+
const declared = fn.call(source);
|
|
85
|
+
if (declared !== null && typeof declared === "object"
|
|
86
|
+
&& typeof declared.source === "string"
|
|
87
|
+
&& typeof declared.queryPushdown === "boolean") {
|
|
88
|
+
caps = declared;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
caps = null; // a broken declaration degrades to derivation
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
if (caps === null) {
|
|
96
|
+
const ctor = source.constructor;
|
|
97
|
+
if (!_warnedUndeclared.has(ctor)) {
|
|
98
|
+
_warnedUndeclared.add(ctor);
|
|
99
|
+
const name = ctor?.name ?? "source";
|
|
100
|
+
console.warn(`[dna-sdk] ${name} does not declare SourceCapabilities via `
|
|
101
|
+
+ `capabilities(); deriving structurally (deprecated, `
|
|
102
|
+
+ `s-sourceport-contract-cleanup). Declare an explicit `
|
|
103
|
+
+ `SourceCapabilities on the adapter to silence this.`);
|
|
104
|
+
}
|
|
105
|
+
caps = deriveCapabilities(source, (source.constructor?.name ?? "source"));
|
|
106
|
+
}
|
|
107
|
+
_capsCache.set(source, caps);
|
|
108
|
+
return caps;
|
|
109
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Narrow role-interfaces the kernel's back-ref collaborators depend on — the TS
|
|
3
|
+
* twin of Python's `dna/kernel/collaborator_ports.py`
|
|
4
|
+
* (`s-kernel-decomp-f1`, épico `e-kernel-decomposition`).
|
|
5
|
+
*
|
|
6
|
+
* A kernel collaborator that holds `this._k = kernel` should declare the NARROW
|
|
7
|
+
* slice of the kernel it actually uses, not the whole `Kernel` — otherwise the
|
|
8
|
+
* back-ref is a god-interface pushed one layer down (any collaborator can touch
|
|
9
|
+
* anything → nothing is testable in isolation). Structural typing means the
|
|
10
|
+
* `Kernel` still satisfies each interface, so the kernel keeps passing `this`
|
|
11
|
+
* when it wires the collaborator — zero runtime change.
|
|
12
|
+
*
|
|
13
|
+
* Parity note: the TS kernel decomposition tracks Python. Extracted as back-ref
|
|
14
|
+
* classes with a narrow host today: `CompositionResolver`
|
|
15
|
+
* (`CompositionResolverHost`) and `KindRegistry` (`RegistryHost`). The remaining
|
|
16
|
+
* inline collaborators (instance_builder, query_engine, bundle_io, source_sync,
|
|
17
|
+
* layer_policy, invalidation) gain their narrow interfaces as those extractions
|
|
18
|
+
* land, keeping the contract convergent per spec §6.
|
|
19
|
+
*/
|
|
20
|
+
import type { HookRegistry } from "./hooks.js";
|
|
21
|
+
import type { KindPort, ReaderPort, SourcePort, WritableSourcePort } from "./protocols.js";
|
|
22
|
+
/**
|
|
23
|
+
* The kernel surface `CompositionResolver` consumes (twin of Py
|
|
24
|
+
* `CompositionResolverHost`, though TS is leaner: the resolution-chain walk +
|
|
25
|
+
* composition-rule read touch only these). The static `INHERIT_PARENT_SCOPE`
|
|
26
|
+
* is reached via `(k.constructor as typeof Kernel)` — an explicit cast that does
|
|
27
|
+
* not need to be part of this instance contract.
|
|
28
|
+
*/
|
|
29
|
+
export interface CompositionResolverHost {
|
|
30
|
+
/** The active read source (getter). */
|
|
31
|
+
readonly activeSource: SourcePort | null;
|
|
32
|
+
/** Granular per-doc cache read: `[scope, kind, name, tenantOrEmpty]`. */
|
|
33
|
+
_granularDoc(key: [string, string, string, string]): Promise<Record<string, unknown> | null>;
|
|
34
|
+
/** The ordered Catalog scope set for a tenant (cached, fail-soft). */
|
|
35
|
+
_catalogScopes(tenant: string | null, opts?: {
|
|
36
|
+
exclude?: Set<string>;
|
|
37
|
+
}): Promise<Array<[string, string | null]>>;
|
|
38
|
+
/** Persist the materialized composition back through the writable source. */
|
|
39
|
+
writeDocument(scope: string, kind: string, name: string, raw: Record<string, unknown>, options?: {
|
|
40
|
+
skipHooks?: boolean;
|
|
41
|
+
author?: string;
|
|
42
|
+
tenant?: string | null;
|
|
43
|
+
layer?: [string, string];
|
|
44
|
+
}): Promise<string>;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* The narrow slice of the Kernel the `KindRegistry` registration funnel needs
|
|
48
|
+
* (twin of Py `RegistryHost`, `s-kernel-decomp-f3-kindregistry`). The `kinds`
|
|
49
|
+
* map itself is OWNED by the registry — NOT reached through the host; this host
|
|
50
|
+
* is only the fan-out surface registration touches on the wider kernel: the
|
|
51
|
+
* hook registry (`kinddef_conflict` / `parse_error` events), the `_readers` list
|
|
52
|
+
* (the 2-phase-load rescan return gate), the generic reader/writer wiring, and
|
|
53
|
+
* the `_genericsResolved` flag it flips on every successful register. Every
|
|
54
|
+
* member is a genuine registration dependency; widening it is a code-review
|
|
55
|
+
* event (spec §3.1 / anti-goal §5.3).
|
|
56
|
+
*
|
|
57
|
+
* `_loadingExtOwner` (the per-`load()` alias-owner context) is read via
|
|
58
|
+
* optional chaining, so a kernel outside a `load()` call (where it is null) is
|
|
59
|
+
* still a valid host.
|
|
60
|
+
*/
|
|
61
|
+
/**
|
|
62
|
+
* The narrow slice of the Kernel the `WritePipeline` consumes (twin of Py
|
|
63
|
+
* `WriteHost`, `s-kernel-decomp-f2-writepipeline` — the anti-cosmetic F1 rule,
|
|
64
|
+
* spec §3.1). The TS write path is thinner than Python's (no invalidate tiers /
|
|
65
|
+
* OTel / catalog / observer fan-out — those "live in Python"), so this host is
|
|
66
|
+
* smaller than the Py `WriteHost`: just tenant reconciliation, the writable-
|
|
67
|
+
* source guard, the layer-policy check, the hook registry (`pre_save` veto +
|
|
68
|
+
* `post_save`/`post_delete`), and Kind identity. Widening it is a code-review
|
|
69
|
+
* event (anti-goal §5.3).
|
|
70
|
+
*/
|
|
71
|
+
export interface WriteHost {
|
|
72
|
+
readonly hooks: HookRegistry;
|
|
73
|
+
readonly tenant: string | null;
|
|
74
|
+
_kindScope(kind: string): string | null;
|
|
75
|
+
kindPortFor(kind: string, apiVersion?: string): KindPort | null;
|
|
76
|
+
_requireWritableSource(): WritableSourcePort;
|
|
77
|
+
_checkLayerPolicyAsync(scope: string, kind: string, name: string, raw: unknown, layer: [string, string]): Promise<void>;
|
|
78
|
+
}
|
|
79
|
+
export interface RegistryHost {
|
|
80
|
+
readonly hooks: HookRegistry;
|
|
81
|
+
/** The active reader list — read for the rescan gate; mutated by
|
|
82
|
+
* `_ensureGenericReadersWriters()`. */
|
|
83
|
+
_readers: ReaderPort[];
|
|
84
|
+
/** Flipped false on every successful register so the next
|
|
85
|
+
* `_ensureGenericReadersWriters()` re-resolves generic BUNDLE rw. */
|
|
86
|
+
_genericsResolved: boolean;
|
|
87
|
+
/** Per-`load()` alias-owner context (null outside a load). */
|
|
88
|
+
_loadingExtOwner: string | null;
|
|
89
|
+
/** Re-resolve generic BUNDLE readers/writers for newly registered kinds. */
|
|
90
|
+
_ensureGenericReadersWriters(): void;
|
|
91
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|