dna-sdk 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (240) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +58 -0
  3. package/dist/adapters/embedding/onnx.d.ts +32 -0
  4. package/dist/adapters/embedding/onnx.js +51 -0
  5. package/dist/adapters/filesystem/cache.d.ts +16 -0
  6. package/dist/adapters/filesystem/cache.js +113 -0
  7. package/dist/adapters/filesystem/index.d.ts +2 -0
  8. package/dist/adapters/filesystem/index.js +2 -0
  9. package/dist/adapters/filesystem/source.d.ts +82 -0
  10. package/dist/adapters/filesystem/source.js +357 -0
  11. package/dist/adapters/postgres/index.d.ts +2 -0
  12. package/dist/adapters/postgres/index.js +2 -0
  13. package/dist/adapters/postgres/migrations.d.ts +14 -0
  14. package/dist/adapters/postgres/migrations.js +88 -0
  15. package/dist/adapters/postgres/source.d.ts +172 -0
  16. package/dist/adapters/postgres/source.js +569 -0
  17. package/dist/adapters/resolvers/github.d.ts +12 -0
  18. package/dist/adapters/resolvers/github.js +47 -0
  19. package/dist/adapters/resolvers/http.d.ts +22 -0
  20. package/dist/adapters/resolvers/http.js +145 -0
  21. package/dist/adapters/resolvers/index.d.ts +3 -0
  22. package/dist/adapters/resolvers/index.js +3 -0
  23. package/dist/adapters/resolvers/local.d.ts +24 -0
  24. package/dist/adapters/resolvers/local.js +132 -0
  25. package/dist/adapters/search/driver.d.ts +45 -0
  26. package/dist/adapters/search/driver.js +138 -0
  27. package/dist/adapters/search/migrations.d.ts +26 -0
  28. package/dist/adapters/search/migrations.js +58 -0
  29. package/dist/adapters/search/rrf.d.ts +23 -0
  30. package/dist/adapters/search/rrf.js +42 -0
  31. package/dist/adapters/search/sqlite-vec.d.ts +66 -0
  32. package/dist/adapters/search/sqlite-vec.js +294 -0
  33. package/dist/bootstrap.d.ts +45 -0
  34. package/dist/bootstrap.js +134 -0
  35. package/dist/extensions/agentskills/DOCS.md +18 -0
  36. package/dist/extensions/agentskills.d.ts +28 -0
  37. package/dist/extensions/agentskills.js +252 -0
  38. package/dist/extensions/agentsmd/DOCS.md +18 -0
  39. package/dist/extensions/agentsmd.d.ts +22 -0
  40. package/dist/extensions/agentsmd.js +170 -0
  41. package/dist/extensions/audit/kinds/audit-log.kind.yaml +148 -0
  42. package/dist/extensions/audit.d.ts +25 -0
  43. package/dist/extensions/audit.js +62 -0
  44. package/dist/extensions/automation/kinds/automation.kind.yaml +296 -0
  45. package/dist/extensions/automation/query.d.ts +49 -0
  46. package/dist/extensions/automation/query.js +77 -0
  47. package/dist/extensions/automation/write-guards.d.ts +59 -0
  48. package/dist/extensions/automation/write-guards.js +153 -0
  49. package/dist/extensions/automation.d.ts +41 -0
  50. package/dist/extensions/automation.js +54 -0
  51. package/dist/extensions/collab.d.ts +16 -0
  52. package/dist/extensions/collab.js +79 -0
  53. package/dist/extensions/doc/kinds/doc.kind.yaml +118 -0
  54. package/dist/extensions/doc.d.ts +25 -0
  55. package/dist/extensions/doc.js +10 -0
  56. package/dist/extensions/eval/kinds/eval-baseline.kind.yaml +65 -0
  57. package/dist/extensions/eval/kinds/eval-case.kind.yaml +140 -0
  58. package/dist/extensions/eval/kinds/eval-run.kind.yaml +134 -0
  59. package/dist/extensions/eval/kinds/eval-suite.kind.yaml +87 -0
  60. package/dist/extensions/eval.d.ts +24 -0
  61. package/dist/extensions/eval.js +31 -0
  62. package/dist/extensions/evidence/kinds/evidence.kind.yaml +92 -0
  63. package/dist/extensions/evidence.d.ts +17 -0
  64. package/dist/extensions/evidence.js +94 -0
  65. package/dist/extensions/federation.d.ts +22 -0
  66. package/dist/extensions/federation.js +173 -0
  67. package/dist/extensions/guardrails/DOCS.md +20 -0
  68. package/dist/extensions/guardrails.d.ts +31 -0
  69. package/dist/extensions/guardrails.js +228 -0
  70. package/dist/extensions/helix/DOCS-Actor.md +20 -0
  71. package/dist/extensions/helix/DOCS-Agent.md +20 -0
  72. package/dist/extensions/helix/DOCS-UseCase.md +20 -0
  73. package/dist/extensions/helix/layers.d.ts +12 -0
  74. package/dist/extensions/helix/layers.js +18 -0
  75. package/dist/extensions/helix/write-guards.d.ts +70 -0
  76. package/dist/extensions/helix/write-guards.js +172 -0
  77. package/dist/extensions/helix.d.ts +29 -0
  78. package/dist/extensions/helix.js +963 -0
  79. package/dist/extensions/helix_extras.d.ts +124 -0
  80. package/dist/extensions/helix_extras.js +328 -0
  81. package/dist/extensions/hooks.d.ts +31 -0
  82. package/dist/extensions/hooks.js +247 -0
  83. package/dist/extensions/kinddef/DOCS.md +24 -0
  84. package/dist/extensions/kinddef.d.ts +40 -0
  85. package/dist/extensions/kinddef.js +171 -0
  86. package/dist/extensions/lesson.d.ts +6 -0
  87. package/dist/extensions/lesson.js +194 -0
  88. package/dist/extensions/modelreg/kinds/model-profile.kind.yaml +155 -0
  89. package/dist/extensions/modelreg.d.ts +30 -0
  90. package/dist/extensions/modelreg.js +38 -0
  91. package/dist/extensions/recognizer.d.ts +34 -0
  92. package/dist/extensions/recognizer.js +272 -0
  93. package/dist/extensions/research.d.ts +32 -0
  94. package/dist/extensions/research.js +292 -0
  95. package/dist/extensions/safety.d.ts +33 -0
  96. package/dist/extensions/safety.js +230 -0
  97. package/dist/extensions/sdlc/kinds/adr.kind.yaml +136 -0
  98. package/dist/extensions/sdlc/kinds/archive-proposal.kind.yaml +107 -0
  99. package/dist/extensions/sdlc/kinds/changelog.kind.yaml +108 -0
  100. package/dist/extensions/sdlc/kinds/cognitive-policy.kind.yaml +470 -0
  101. package/dist/extensions/sdlc/kinds/forecast.kind.yaml +125 -0
  102. package/dist/extensions/sdlc/kinds/insight.kind.yaml +87 -0
  103. package/dist/extensions/sdlc/kinds/kaizen.kind.yaml +106 -0
  104. package/dist/extensions/sdlc/kinds/lesson-learned.kind.yaml +270 -0
  105. package/dist/extensions/sdlc/kinds/narrative.kind.yaml +189 -0
  106. package/dist/extensions/sdlc/kinds/pattern-insight.kind.yaml +204 -0
  107. package/dist/extensions/sdlc/kinds/postmortem.kind.yaml +168 -0
  108. package/dist/extensions/sdlc/kinds/pre-mortem.kind.yaml +145 -0
  109. package/dist/extensions/sdlc/kinds/prompt-template.kind.yaml +109 -0
  110. package/dist/extensions/sdlc/kinds/retrospective.kind.yaml +186 -0
  111. package/dist/extensions/sdlc/kinds/risk-register.kind.yaml +178 -0
  112. package/dist/extensions/sdlc/kinds/saved-view.kind.yaml +127 -0
  113. package/dist/extensions/sdlc/kinds/status-report.kind.yaml +129 -0
  114. package/dist/extensions/sdlc/kinds/synthesis-run.kind.yaml +279 -0
  115. package/dist/extensions/sdlc/kinds/synthesizer-state.kind.yaml +196 -0
  116. package/dist/extensions/sdlc/kinds/workflow-event.kind.yaml +195 -0
  117. package/dist/extensions/sdlc.d.ts +32 -0
  118. package/dist/extensions/sdlc.js +1318 -0
  119. package/dist/extensions/soulspec/DOCS.md +19 -0
  120. package/dist/extensions/soulspec.d.ts +22 -0
  121. package/dist/extensions/soulspec.js +287 -0
  122. package/dist/extensions/tenant.d.ts +6 -0
  123. package/dist/extensions/tenant.js +291 -0
  124. package/dist/extensions/testkit.d.ts +24 -0
  125. package/dist/extensions/testkit.js +211 -0
  126. package/dist/index.d.ts +80 -0
  127. package/dist/index.js +102 -0
  128. package/dist/kernel/_text.d.ts +28 -0
  129. package/dist/kernel/_text.js +42 -0
  130. package/dist/kernel/bundle-handle.d.ts +83 -0
  131. package/dist/kernel/bundle-handle.js +184 -0
  132. package/dist/kernel/capabilities.d.ts +157 -0
  133. package/dist/kernel/capabilities.js +109 -0
  134. package/dist/kernel/collaborator-ports.d.ts +91 -0
  135. package/dist/kernel/collaborator-ports.js +1 -0
  136. package/dist/kernel/composition-resolver.d.ts +215 -0
  137. package/dist/kernel/composition-resolver.js +558 -0
  138. package/dist/kernel/composition.d.ts +10 -0
  139. package/dist/kernel/composition.js +1 -0
  140. package/dist/kernel/descriptor-loader.d.ts +13 -0
  141. package/dist/kernel/descriptor-loader.js +48 -0
  142. package/dist/kernel/document.d.ts +43 -0
  143. package/dist/kernel/document.js +71 -0
  144. package/dist/kernel/embedding.d.ts +28 -0
  145. package/dist/kernel/embedding.js +78 -0
  146. package/dist/kernel/errors.d.ts +62 -0
  147. package/dist/kernel/errors.js +82 -0
  148. package/dist/kernel/events.d.ts +6 -0
  149. package/dist/kernel/events.js +21 -0
  150. package/dist/kernel/evidence-capture.d.ts +69 -0
  151. package/dist/kernel/evidence-capture.js +138 -0
  152. package/dist/kernel/fs.d.ts +20 -0
  153. package/dist/kernel/fs.js +128 -0
  154. package/dist/kernel/generic-rw.d.ts +33 -0
  155. package/dist/kernel/generic-rw.js +235 -0
  156. package/dist/kernel/hooks.d.ts +172 -0
  157. package/dist/kernel/hooks.js +256 -0
  158. package/dist/kernel/index.d.ts +624 -0
  159. package/dist/kernel/index.js +1638 -0
  160. package/dist/kernel/instance.d.ts +211 -0
  161. package/dist/kernel/instance.js +487 -0
  162. package/dist/kernel/kind-registry.d.ts +183 -0
  163. package/dist/kernel/kind-registry.js +710 -0
  164. package/dist/kernel/kind_base.d.ts +106 -0
  165. package/dist/kernel/kind_base.js +244 -0
  166. package/dist/kernel/layer-resolver.d.ts +48 -0
  167. package/dist/kernel/layer-resolver.js +284 -0
  168. package/dist/kernel/lock-manager.d.ts +21 -0
  169. package/dist/kernel/lock-manager.js +43 -0
  170. package/dist/kernel/lock.d.ts +38 -0
  171. package/dist/kernel/lock.js +115 -0
  172. package/dist/kernel/meta.d.ts +135 -0
  173. package/dist/kernel/meta.js +817 -0
  174. package/dist/kernel/models.d.ts +3089 -0
  175. package/dist/kernel/models.js +764 -0
  176. package/dist/kernel/navigator.d.ts +36 -0
  177. package/dist/kernel/navigator.js +132 -0
  178. package/dist/kernel/port-surface.d.ts +33 -0
  179. package/dist/kernel/port-surface.js +180 -0
  180. package/dist/kernel/preview.d.ts +60 -0
  181. package/dist/kernel/preview.js +54 -0
  182. package/dist/kernel/prompt-budget.d.ts +39 -0
  183. package/dist/kernel/prompt-budget.js +43 -0
  184. package/dist/kernel/prompt-builder.d.ts +25 -0
  185. package/dist/kernel/prompt-builder.js +237 -0
  186. package/dist/kernel/protocols.d.ts +751 -0
  187. package/dist/kernel/protocols.js +493 -0
  188. package/dist/kernel/reports.d.ts +26 -0
  189. package/dist/kernel/reports.js +215 -0
  190. package/dist/kernel/resolver.d.ts +146 -0
  191. package/dist/kernel/resolver.js +232 -0
  192. package/dist/kernel/resource.d.ts +57 -0
  193. package/dist/kernel/resource.js +97 -0
  194. package/dist/kernel/runtime.d.ts +16 -0
  195. package/dist/kernel/runtime.js +18 -0
  196. package/dist/kernel/safety-scanner.d.ts +38 -0
  197. package/dist/kernel/safety-scanner.js +228 -0
  198. package/dist/kernel/serialize-to-files.d.ts +29 -0
  199. package/dist/kernel/serialize-to-files.js +23 -0
  200. package/dist/kernel/spec-access.d.ts +23 -0
  201. package/dist/kernel/spec-access.js +40 -0
  202. package/dist/kernel/studio_ui.d.ts +88 -0
  203. package/dist/kernel/studio_ui.js +135 -0
  204. package/dist/kernel/templates.d.ts +77 -0
  205. package/dist/kernel/templates.js +116 -0
  206. package/dist/kernel/tool-registry.d.ts +52 -0
  207. package/dist/kernel/tool-registry.js +89 -0
  208. package/dist/kernel/write-pipeline.d.ts +65 -0
  209. package/dist/kernel/write-pipeline.js +271 -0
  210. package/dist/kernel/writer-helpers.d.ts +24 -0
  211. package/dist/kernel/writer-helpers.js +42 -0
  212. package/dist/memory/decay.d.ts +31 -0
  213. package/dist/memory/decay.js +97 -0
  214. package/dist/memory/ecphory.d.ts +25 -0
  215. package/dist/memory/ecphory.js +178 -0
  216. package/dist/memory/encodingContext.d.ts +21 -0
  217. package/dist/memory/encodingContext.js +62 -0
  218. package/dist/memory/index.d.ts +23 -0
  219. package/dist/memory/index.js +23 -0
  220. package/dist/memory/memoryType.d.ts +7 -0
  221. package/dist/memory/memoryType.js +24 -0
  222. package/dist/memory/policy.d.ts +35 -0
  223. package/dist/memory/policy.js +35 -0
  224. package/dist/memory/retrieval.d.ts +21 -0
  225. package/dist/memory/retrieval.js +145 -0
  226. package/dist/testing/index.d.ts +8 -0
  227. package/dist/testing/index.js +8 -0
  228. package/dist/testing/recordSearchConformance.d.ts +43 -0
  229. package/dist/testing/recordSearchConformance.js +147 -0
  230. package/dist/viz/ascii.d.ts +7 -0
  231. package/dist/viz/ascii.js +60 -0
  232. package/dist/viz/health.d.ts +8 -0
  233. package/dist/viz/health.js +161 -0
  234. package/dist/viz/index.d.ts +10 -0
  235. package/dist/viz/index.js +10 -0
  236. package/dist/viz/matrix.d.ts +8 -0
  237. package/dist/viz/matrix.js +84 -0
  238. package/dist/viz/mermaid.d.ts +38 -0
  239. package/dist/viz/mermaid.js +706 -0
  240. package/package.json +82 -0
@@ -0,0 +1,66 @@
1
+ /** The slice of the kernel this provider needs (embedding surface). */
2
+ export interface EmbeddingKernel {
3
+ embed(texts: string[]): Promise<number[][]>;
4
+ readonly embeddingDims: number;
5
+ readonly embeddingModelId: string;
6
+ }
7
+ /** A record to index. `text` is used verbatim if present; otherwise derived
8
+ * from `raw` via {@link documentText}. */
9
+ export interface SearchRecord {
10
+ scope: string;
11
+ kind: string;
12
+ name: string;
13
+ tenant?: string;
14
+ text?: string;
15
+ raw?: Record<string, unknown>;
16
+ title?: string;
17
+ snippet?: string;
18
+ }
19
+ export interface SearchHit extends Record<string, unknown> {
20
+ scope: string;
21
+ kind: string;
22
+ name: string;
23
+ score: number;
24
+ }
25
+ /** An id to delete: `{ scope, kind, name, tenant? }`. */
26
+ export interface RecordId {
27
+ scope: string;
28
+ kind: string;
29
+ name: string;
30
+ tenant?: string;
31
+ }
32
+ /** Derive the searchable text blob for a raw document — every string value in
33
+ * `spec` plus `metadata.name`, in document order (parity with the Python
34
+ * `document_text`). */
35
+ export declare function documentText(raw: Record<string, unknown>): string;
36
+ export declare class SqliteVecRecordSearchProvider {
37
+ private readonly kernel;
38
+ private readonly dbDir;
39
+ private readonly singlePath;
40
+ private readonly rrfK;
41
+ private readonly conns;
42
+ constructor(kernel: EmbeddingKernel, opts?: {
43
+ dbDir?: string;
44
+ dbPath?: string;
45
+ rrfK?: number;
46
+ });
47
+ private pathFor;
48
+ private connFor;
49
+ private pinIdentity;
50
+ /** Index (upsert) records into their scope's store. Idempotent by text hash
51
+ * (re-indexing unchanged text is skipped). Returns the count actually
52
+ * (re)embedded. */
53
+ index(records: SearchRecord[]): Promise<number>;
54
+ private upsert;
55
+ /** Delete indexed records. Returns the number of rows removed. */
56
+ delete(ids: RecordId[]): Promise<number>;
57
+ search(opts: {
58
+ scope: string;
59
+ queryText: string;
60
+ kind?: string | null;
61
+ k?: number;
62
+ tenant?: string;
63
+ }): Promise<SearchHit[]>;
64
+ private resolveMeta;
65
+ close(): void;
66
+ }
@@ -0,0 +1,294 @@
1
+ /**
2
+ * SqliteVecRecordSearchProvider — the embeddable default RecordSearchProvider
3
+ * (TS twin of `dna/adapters/search/sqlite_vec.py`).
4
+ *
5
+ * The FIRST real implementation of the `RecordSearchProvider` port on the TS
6
+ * side. Hybrid search inside one SQLite file per scope, offline, no server:
7
+ *
8
+ * - dense — sqlite-vec `vec0` KNN over `kernel.embed()` vectors (the
9
+ * deterministic `FakeEmbeddingProvider` floor by default).
10
+ * - lexical — FTS5 BM25 over the same text.
11
+ * - fusion — Reciprocal Rank Fusion (`reciprocalRankFusion`, the pure
12
+ * function shared with the Python twin).
13
+ *
14
+ * The store schema is OWNED by a numbered migration (`migrations.ts`), closing
15
+ * f-embeddings-ddl-debt. Overlay/tenant-aware: `tenant` is a column ('' = base);
16
+ * a tenant search reads base ∪ overlay and the overlay row shadows the base row
17
+ * for the same `(kind, name)`.
18
+ *
19
+ * SQLite access goes through {@link openSearchDb} (bun:sqlite or node:sqlite);
20
+ * sqlite-vec is an OPTIONAL peer dep (`sqlite-vec`), never in the default graph.
21
+ * Both are exercised by the shared record-search conformance kit with the fake
22
+ * embedder, offline.
23
+ */
24
+ import { sha256 } from "js-sha256";
25
+ import { DEFAULT_RRF_K, reciprocalRankFusion } from "./rrf.js";
26
+ import { buildMigrations, runSearchMigrations } from "./migrations.js";
27
+ import { openSearchDb, serializeF32 } from "./driver.js";
28
+ const OVERFETCH = 4;
29
+ const MIN_CANDIDATES = 40;
30
+ /** Derive the searchable text blob for a raw document — every string value in
31
+ * `spec` plus `metadata.name`, in document order (parity with the Python
32
+ * `document_text`). */
33
+ export function documentText(raw) {
34
+ const parts = [];
35
+ const meta = raw.metadata ?? {};
36
+ const name = meta.name ?? raw.name;
37
+ if (typeof name === "string")
38
+ parts.push(name);
39
+ const walk = (node) => {
40
+ if (typeof node === "string")
41
+ parts.push(node);
42
+ else if (Array.isArray(node))
43
+ node.forEach(walk);
44
+ else if (node && typeof node === "object")
45
+ Object.values(node).forEach(walk);
46
+ };
47
+ walk(raw.spec ?? {});
48
+ return parts.filter((p) => p).join("\n");
49
+ }
50
+ export class SqliteVecRecordSearchProvider {
51
+ kernel;
52
+ dbDir;
53
+ singlePath;
54
+ rrfK;
55
+ conns = new Map();
56
+ constructor(kernel, opts = {}) {
57
+ this.kernel = kernel;
58
+ let dbDir = opts.dbDir ?? null;
59
+ if (dbDir === null && !opts.dbPath) {
60
+ const env = globalThis
61
+ .process?.env;
62
+ dbDir = env?.DNA_SEARCH_DIR ?? ".dna-search";
63
+ }
64
+ this.dbDir = opts.dbPath ? null : dbDir;
65
+ this.singlePath = opts.dbPath ?? null;
66
+ this.rrfK = opts.rrfK ?? DEFAULT_RRF_K;
67
+ }
68
+ // ------------------------------------------------------------------
69
+ // store / schema (migration-owned)
70
+ // ------------------------------------------------------------------
71
+ pathFor(scope) {
72
+ if (this.singlePath)
73
+ return this.singlePath;
74
+ const safe = scope.replace(/[/\\]/g, "_");
75
+ return `${this.dbDir}/${safe}.db`;
76
+ }
77
+ async connFor(scope) {
78
+ const path = this.pathFor(scope);
79
+ const existing = this.conns.get(path);
80
+ if (existing)
81
+ return existing;
82
+ if (this.dbDir && !this.singlePath)
83
+ await ensureDir(this.dbDir);
84
+ const db = await openSearchDb(path);
85
+ runSearchMigrations(db, buildMigrations(this.kernel.embeddingDims));
86
+ this.pinIdentity(db);
87
+ this.conns.set(path, db);
88
+ return db;
89
+ }
90
+ pinIdentity(db) {
91
+ const dims = String(this.kernel.embeddingDims);
92
+ const modelId = this.kernel.embeddingModelId;
93
+ const rows = db.all("SELECT key, value FROM search_meta");
94
+ if (rows.length === 0) {
95
+ db.run("INSERT INTO search_meta (key, value) VALUES (?, ?)", ["embedding_dims", dims]);
96
+ db.run("INSERT INTO search_meta (key, value) VALUES (?, ?)", ["embedding_model_id", modelId]);
97
+ return;
98
+ }
99
+ const meta = Object.fromEntries(rows.map((r) => [r.key, r.value]));
100
+ if (meta.embedding_dims !== dims || meta.embedding_model_id !== modelId) {
101
+ throw new Error(`search store was built for embedding space (${meta.embedding_model_id}, `
102
+ + `dims=${meta.embedding_dims}) but the active provider is (${modelId}, `
103
+ + `dims=${dims}) — the vectors are incomparable. Use a fresh store dir `
104
+ + "or re-index.");
105
+ }
106
+ }
107
+ // ------------------------------------------------------------------
108
+ // index / delete
109
+ // ------------------------------------------------------------------
110
+ /** Index (upsert) records into their scope's store. Idempotent by text hash
111
+ * (re-indexing unchanged text is skipped). Returns the count actually
112
+ * (re)embedded. */
113
+ async index(records) {
114
+ if (records.length === 0)
115
+ return 0;
116
+ const pending = records.map((rec) => {
117
+ const text = rec.text ?? documentText(rec.raw ?? {});
118
+ return { rec, text, hash: sha256(text) };
119
+ });
120
+ const toEmbed = [];
121
+ for (const item of pending) {
122
+ const db = await this.connFor(item.rec.scope);
123
+ const existing = db.get("SELECT text_hash FROM search_docs WHERE scope=? AND kind=? AND name=? AND tenant=?", [item.rec.scope, item.rec.kind, item.rec.name, item.rec.tenant ?? ""]);
124
+ if (existing && existing.text_hash === item.hash)
125
+ continue;
126
+ toEmbed.push(item);
127
+ }
128
+ if (toEmbed.length === 0)
129
+ return 0;
130
+ const vectors = await this.kernel.embed(toEmbed.map((i) => i.text));
131
+ toEmbed.forEach((item, i) => this.upsert(item.rec, item.text, item.hash, vectors[i]));
132
+ return toEmbed.length;
133
+ }
134
+ upsert(rec, text, textHash, vector) {
135
+ const db = this.conns.get(this.pathFor(rec.scope));
136
+ const tenant = rec.tenant ?? "";
137
+ const title = rec.title ?? null;
138
+ const snippet = rec.snippet ?? snippetOf(text);
139
+ const row = db.get("SELECT rowid FROM search_docs WHERE scope=? AND kind=? AND name=? AND tenant=?", [rec.scope, rec.kind, rec.name, tenant]);
140
+ let rowid;
141
+ if (!row) {
142
+ const info = db.run("INSERT INTO search_docs (scope, kind, name, tenant, text_hash, title, snippet, text) "
143
+ + "VALUES (?,?,?,?,?,?,?,?)", [rec.scope, rec.kind, rec.name, tenant, textHash, title, snippet, text]);
144
+ rowid = info.lastInsertRowid;
145
+ }
146
+ else {
147
+ rowid = row.rowid;
148
+ db.run("UPDATE search_docs SET text_hash=?, title=?, snippet=?, text=? WHERE rowid=?", [
149
+ textHash, title, snippet, text, rowid,
150
+ ]);
151
+ db.run("DELETE FROM search_vec WHERE doc_rowid=?", [BigInt(rowid)]);
152
+ db.run("DELETE FROM search_fts WHERE rowid=?", [BigInt(rowid)]);
153
+ }
154
+ // vec0 (and fts5) require an INTEGER rowid binding — node:sqlite binds a JS
155
+ // number as a double and vec0 rejects it, so pass BigInt (both drivers OK).
156
+ db.run("INSERT INTO search_vec (doc_rowid, embedding) VALUES (?, ?)", [
157
+ BigInt(rowid), serializeF32(vector),
158
+ ]);
159
+ db.run("INSERT INTO search_fts (rowid, text) VALUES (?, ?)", [BigInt(rowid), text]);
160
+ }
161
+ /** Delete indexed records. Returns the number of rows removed. */
162
+ async delete(ids) {
163
+ let removed = 0;
164
+ for (const id of ids) {
165
+ const db = await this.connFor(id.scope);
166
+ const row = db.get("SELECT rowid FROM search_docs WHERE scope=? AND kind=? AND name=? AND tenant=?", [id.scope, id.kind, id.name, id.tenant ?? ""]);
167
+ if (!row)
168
+ continue;
169
+ const rowid = BigInt(row.rowid);
170
+ db.run("DELETE FROM search_vec WHERE doc_rowid=?", [rowid]);
171
+ db.run("DELETE FROM search_fts WHERE rowid=?", [rowid]);
172
+ db.run("DELETE FROM search_docs WHERE rowid=?", [rowid]);
173
+ removed += 1;
174
+ }
175
+ return removed;
176
+ }
177
+ // ------------------------------------------------------------------
178
+ // search (RecordSearchProvider)
179
+ // ------------------------------------------------------------------
180
+ async search(opts) {
181
+ const { scope, queryText } = opts;
182
+ const kind = opts.kind ?? null;
183
+ const k = opts.k ?? 10;
184
+ const tenant = opts.tenant ?? "";
185
+ if (!queryText.trim() || k <= 0)
186
+ return [];
187
+ const db = await this.connFor(scope);
188
+ const overfetch = Math.max(MIN_CANDIDATES, k * OVERFETCH);
189
+ const queryVec = (await this.kernel.embed([queryText]))[0];
190
+ let denseRanked = [];
191
+ if (queryVec.some((x) => x !== 0)) {
192
+ denseRanked = db
193
+ .all("SELECT doc_rowid FROM search_vec WHERE embedding MATCH ? ORDER BY distance LIMIT ?", [serializeF32(queryVec), overfetch])
194
+ .map((r) => r.doc_rowid);
195
+ }
196
+ let lexicalRanked = [];
197
+ const ftsQuery = ftsQueryOf(queryText);
198
+ if (ftsQuery) {
199
+ try {
200
+ lexicalRanked = db
201
+ .all("SELECT rowid FROM search_fts WHERE search_fts MATCH ? ORDER BY bm25(search_fts) LIMIT ?", [ftsQuery, overfetch])
202
+ .map((r) => r.rowid);
203
+ }
204
+ catch {
205
+ lexicalRanked = [];
206
+ }
207
+ }
208
+ if (denseRanked.length === 0 && lexicalRanked.length === 0)
209
+ return [];
210
+ const fused = reciprocalRankFusion([denseRanked.map(String), lexicalRanked.map(String)], this.rrfK);
211
+ const densePos = new Map(denseRanked.map((r, i) => [r, i + 1]));
212
+ const lexicalPos = new Map(lexicalRanked.map((r, i) => [r, i + 1]));
213
+ const rowids = fused.map(([rid]) => Number(rid));
214
+ const meta = this.resolveMeta(db, rowids);
215
+ const best = new Map();
216
+ for (const [rid, score] of fused) {
217
+ const rowid = Number(rid);
218
+ const m = meta.get(rowid);
219
+ if (!m)
220
+ continue;
221
+ if (kind !== null && m.kind !== kind)
222
+ continue;
223
+ const rowTenant = m.tenant ?? "";
224
+ if (rowTenant !== "" && rowTenant !== (tenant || ""))
225
+ continue; // other tenant — never leaks
226
+ const key = `${m.kind}${m.name}`;
227
+ const prev = best.get(key);
228
+ const hit = makeHit(scope, m, score, densePos.get(rowid), lexicalPos.get(rowid));
229
+ if (!prev) {
230
+ best.set(key, hit);
231
+ }
232
+ else {
233
+ const thisIsOverlay = rowTenant !== "" && rowTenant === (tenant || "");
234
+ if (thisIsOverlay && prev._tenant === "")
235
+ best.set(key, hit);
236
+ }
237
+ }
238
+ const hits = [...best.values()].sort((a, b) => b.score - a.score);
239
+ for (const h of hits)
240
+ delete h._tenant;
241
+ return hits.slice(0, k);
242
+ }
243
+ resolveMeta(db, rowids) {
244
+ if (rowids.length === 0)
245
+ return new Map();
246
+ const placeholders = rowids.map(() => "?").join(",");
247
+ const rows = db.all(`SELECT rowid, scope, kind, name, tenant, title, snippet FROM search_docs `
248
+ + `WHERE rowid IN (${placeholders})`, rowids);
249
+ return new Map(rows.map((r) => [r.rowid, r]));
250
+ }
251
+ close() {
252
+ for (const db of this.conns.values()) {
253
+ try {
254
+ db.close();
255
+ }
256
+ catch {
257
+ /* ignore */
258
+ }
259
+ }
260
+ this.conns.clear();
261
+ }
262
+ }
263
+ function makeHit(scope, m, score, rankDense, rankLexical) {
264
+ const hit = {
265
+ scope, kind: m.kind, name: m.name, score, _tenant: m.tenant ?? "",
266
+ };
267
+ if (m.title)
268
+ hit.title = m.title;
269
+ if (m.snippet)
270
+ hit.snippet = m.snippet;
271
+ if (rankDense !== undefined)
272
+ hit.rank_dense = rankDense;
273
+ if (rankLexical !== undefined)
274
+ hit.rank_lexical = rankLexical;
275
+ return hit;
276
+ }
277
+ function snippetOf(text, maxLen = 200) {
278
+ const flat = text.split(/\s+/).filter(Boolean).join(" ");
279
+ return flat.length > maxLen ? flat.slice(0, maxLen) + "…" : flat;
280
+ }
281
+ /** OR of quoted alphanumeric tokens — parity with the Python `_fts_query`. */
282
+ function ftsQueryOf(queryText) {
283
+ const tokens = queryText.toLowerCase().match(/[a-z0-9]+/g) ?? [];
284
+ return tokens.map((t) => `"${t}"`).join(" OR ");
285
+ }
286
+ async function ensureDir(dir) {
287
+ try {
288
+ const fs = (await import("node:fs"));
289
+ fs.mkdirSync(dir, { recursive: true });
290
+ }
291
+ catch {
292
+ /* best effort — open will surface a real failure */
293
+ }
294
+ }
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Opinionated SDK bootstrap.
3
+ *
4
+ * The kernel itself (src/kernel/*) is extension-agnostic by design — it
5
+ * knows about the 5-port architecture and nothing else. This module is
6
+ * where the SDK ships its default wiring: the six built-in extensions,
7
+ * the default resolvers, and the quick-start helpers used by examples
8
+ * and tests.
9
+ *
10
+ * Callers that want a minimal kernel can `import { Kernel }` directly
11
+ * and register exactly the extensions they need. Callers that want the
12
+ * batteries-included experience use `createKernelWithBuiltins()` or
13
+ * `quickInstance()`.
14
+ */
15
+ import { Kernel } from "./kernel/index.js";
16
+ import { Runtime } from "./kernel/runtime.js";
17
+ import type { ManifestInstance } from "./kernel/instance.js";
18
+ /**
19
+ * Return a fresh Kernel with all six built-in extensions registered.
20
+ * No source, cache, or resolvers wired — callers are responsible for
21
+ * that. Use `quickInstance()` if you want the full batteries-included
22
+ * shortcut.
23
+ */
24
+ export declare function createKernelWithBuiltins(): Kernel;
25
+ /**
26
+ * Batteries-included shortcut: kernel + all built-in extensions +
27
+ * default filesystem source / cache / resolvers, then returns a
28
+ * `ManifestInstance` for the given scope. Equivalent to the old
29
+ * `Kernel.quick(scope, baseDir)` static method that used to live on
30
+ * the kernel class itself.
31
+ */
32
+ export declare function quickInstance(scope: string, baseDir?: string): Promise<ManifestInstance>;
33
+ /**
34
+ * Return a fresh Runtime with all six built-in extensions registered.
35
+ * No source, cache, or resolvers wired — callers are responsible for
36
+ * that. Use `quickManifest()` if you want the full batteries-included
37
+ * shortcut.
38
+ */
39
+ export declare function createRuntimeWithBuiltins(): Runtime;
40
+ /**
41
+ * Batteries-included shortcut using Runtime: all built-in extensions +
42
+ * default filesystem source / cache / resolvers, then returns a
43
+ * `ManifestInstance` for the given scope.
44
+ */
45
+ export declare function quickManifest(scope: string, baseDir?: string): Promise<ManifestInstance>;
@@ -0,0 +1,134 @@
1
+ /**
2
+ * Opinionated SDK bootstrap.
3
+ *
4
+ * The kernel itself (src/kernel/*) is extension-agnostic by design — it
5
+ * knows about the 5-port architecture and nothing else. This module is
6
+ * where the SDK ships its default wiring: the six built-in extensions,
7
+ * the default resolvers, and the quick-start helpers used by examples
8
+ * and tests.
9
+ *
10
+ * Callers that want a minimal kernel can `import { Kernel }` directly
11
+ * and register exactly the extensions they need. Callers that want the
12
+ * batteries-included experience use `createKernelWithBuiltins()` or
13
+ * `quickInstance()`.
14
+ */
15
+ import { Kernel } from "./kernel/index.js";
16
+ import { Runtime } from "./kernel/runtime.js";
17
+ import { FilesystemSource } from "./adapters/filesystem/source.js";
18
+ import { FilesystemCache } from "./adapters/filesystem/cache.js";
19
+ import { LocalResolver } from "./adapters/resolvers/local.js";
20
+ import { HttpResolver } from "./adapters/resolvers/http.js";
21
+ import { GitHubResolver } from "./adapters/resolvers/github.js";
22
+ import { HelixExtension } from "./extensions/helix.js";
23
+ import { AgentSkillsExtension } from "./extensions/agentskills.js";
24
+ import { SoulSpecExtension } from "./extensions/soulspec.js";
25
+ import { AgentsMdExtension } from "./extensions/agentsmd.js";
26
+ import { GuardrailExtension } from "./extensions/guardrails.js";
27
+ import { HookExtension } from "./extensions/hooks.js";
28
+ import { KindDefinitionExtension } from "./extensions/kinddef.js";
29
+ import { SafetyPolicyExtension } from "./extensions/safety.js";
30
+ import { RecognizerExtension } from "./extensions/recognizer.js";
31
+ import { FederationExtension } from "./extensions/federation.js";
32
+ import { SdlcExtension } from "./extensions/sdlc.js";
33
+ import { AuditExtension } from "./extensions/audit.js";
34
+ import { LessonExtension } from "./extensions/lesson.js";
35
+ import { TenantExtension } from "./extensions/tenant.js";
36
+ import { EvidenceExtension } from "./extensions/evidence.js";
37
+ import { DocExtension } from "./extensions/doc.js";
38
+ import { ResearchExtension } from "./extensions/research.js";
39
+ import { TestkitExtension } from "./extensions/testkit.js";
40
+ import { ModelRegExtension } from "./extensions/modelreg.js";
41
+ import { AutomationExtension } from "./extensions/automation.js";
42
+ import { EvalExtension } from "./extensions/eval.js";
43
+ /**
44
+ * The canonical built-in extension set. Loaded into BOTH a Kernel and a
45
+ * Runtime through this SINGLE list so the two bootstraps can never drift
46
+ * (s-export-unwired-ts-extensions — they had, and Community/Research/Sdlc were
47
+ * loaded by neither). Add a built-in here once and both paths get it.
48
+ */
49
+ function loadBuiltins(target) {
50
+ for (const ext of [
51
+ new HelixExtension(),
52
+ new AgentSkillsExtension(),
53
+ new SoulSpecExtension(),
54
+ new AgentsMdExtension(),
55
+ new GuardrailExtension(),
56
+ new HookExtension(),
57
+ new KindDefinitionExtension(),
58
+ new SafetyPolicyExtension(),
59
+ new RecognizerExtension(),
60
+ new FederationExtension(),
61
+ new SdlcExtension(),
62
+ new AuditExtension(),
63
+ new LessonExtension(),
64
+ new TenantExtension(),
65
+ new EvidenceExtension(),
66
+ new DocExtension(),
67
+ new ResearchExtension(),
68
+ new TestkitExtension(),
69
+ new ModelRegExtension(),
70
+ new AutomationExtension(),
71
+ new EvalExtension(),
72
+ ]) {
73
+ target.load(ext);
74
+ }
75
+ // s-alias-generated-not-typed — com TODAS as extensions carregadas, todo
76
+ // dep_filter builtin deve resolver pra um alias registrado. Antes: alias
77
+ // com typo degradava o prompt silenciosamente (warning enterrado); agora
78
+ // o boot falha apontando o Kind + campo + alias. Runtime herda de Kernel,
79
+ // então AMBOS os caminhos (createKernelWithBuiltins/createRuntimeWithBuiltins)
80
+ // passam por aqui — o twin TS do gate no fim de Kernel.auto() (Python).
81
+ target
82
+ .validateDepFilters?.();
83
+ return target;
84
+ }
85
+ /**
86
+ * Return a fresh Kernel with all six built-in extensions registered.
87
+ * No source, cache, or resolvers wired — callers are responsible for
88
+ * that. Use `quickInstance()` if you want the full batteries-included
89
+ * shortcut.
90
+ */
91
+ export function createKernelWithBuiltins() {
92
+ return loadBuiltins(new Kernel());
93
+ }
94
+ /**
95
+ * Batteries-included shortcut: kernel + all built-in extensions +
96
+ * default filesystem source / cache / resolvers, then returns a
97
+ * `ManifestInstance` for the given scope. Equivalent to the old
98
+ * `Kernel.quick(scope, baseDir)` static method that used to live on
99
+ * the kernel class itself.
100
+ */
101
+ export async function quickInstance(scope, baseDir = ".dna") {
102
+ const k = createKernelWithBuiltins();
103
+ k.source(new FilesystemSource(baseDir));
104
+ k.cache(new FilesystemCache(baseDir));
105
+ k.resolver("local", new LocalResolver(baseDir));
106
+ k.resolver("http", new HttpResolver());
107
+ k.resolver("https", new HttpResolver());
108
+ k.resolver("github", new GitHubResolver());
109
+ return k.instance(scope);
110
+ }
111
+ /**
112
+ * Return a fresh Runtime with all six built-in extensions registered.
113
+ * No source, cache, or resolvers wired — callers are responsible for
114
+ * that. Use `quickManifest()` if you want the full batteries-included
115
+ * shortcut.
116
+ */
117
+ export function createRuntimeWithBuiltins() {
118
+ return loadBuiltins(new Runtime());
119
+ }
120
+ /**
121
+ * Batteries-included shortcut using Runtime: all built-in extensions +
122
+ * default filesystem source / cache / resolvers, then returns a
123
+ * `ManifestInstance` for the given scope.
124
+ */
125
+ export async function quickManifest(scope, baseDir = ".dna") {
126
+ const rt = createRuntimeWithBuiltins();
127
+ rt.storage(new FilesystemSource(baseDir));
128
+ rt.cache(new FilesystemCache(baseDir));
129
+ rt.resolver("local", new LocalResolver(baseDir));
130
+ rt.resolver("http", new HttpResolver());
131
+ rt.resolver("https", new HttpResolver());
132
+ rt.resolver("github", new GitHubResolver());
133
+ return rt.manifest(scope);
134
+ }
@@ -0,0 +1,18 @@
1
+ # Skill
2
+
3
+ A Skill is a modular, progressively-disclosed unit of know-how. It is stored
4
+ as a bundle rooted on a `SKILL.md` marker file plus optional `scripts/`,
5
+ `references/`, and `assets/` directories. The shape is compatible with
6
+ Anthropic's Agent Skills format
7
+ (https://docs.anthropic.com/en/docs/agents/skills).
8
+
9
+ Skills are NOT flattened into the agent's system prompt. Instead, the harness
10
+ (e.g. DeepAgents' `SkillsMiddleware`) exposes a short catalogue of
11
+ `{name, description}` entries in the system prompt; the agent decides when to
12
+ read the full `SKILL.md` on demand via the `read` tool. This keeps the context
13
+ window lean while still giving the agent access to deep procedural knowledge.
14
+
15
+ **Composition:** Agents reference Skills via their `dep_filters.skills`
16
+ (alias `agentskills-skill`). A single Skill can be shared across many agents
17
+ or modules, resolved either locally or from remote dependencies via the
18
+ resolver ports.
@@ -0,0 +1,28 @@
1
+ /**
2
+ * AgentSkillsExtension — Skill kind + SkillReader.
3
+ *
4
+ * 1:1 parity with Python dna.v3.extensions.agentskills.
5
+ */
6
+ import type { BundleHandle } from "../kernel/bundle-handle.js";
7
+ import type { ExtensionHost, Extension, ReaderPort, SerializedFile, WriterPort } from "../kernel/protocols.js";
8
+ import type { FSLike } from "../kernel/fs.js";
9
+ export declare class SkillReader implements ReaderPort {
10
+ private fs;
11
+ constructor(fs: FSLike);
12
+ detect(bundle: BundleHandle): boolean;
13
+ read(bundle: BundleHandle): Record<string, unknown>;
14
+ }
15
+ export declare class SkillWriter implements WriterPort {
16
+ private fs;
17
+ constructor(fs: FSLike);
18
+ canWrite(raw: Record<string, unknown>): boolean;
19
+ write(bundle: BundleHandle, raw: Record<string, unknown>): void;
20
+ serialize(raw: Record<string, unknown>): SerializedFile[];
21
+ }
22
+ export declare class AgentSkillsExtension implements Extension {
23
+ private fs;
24
+ readonly name = "agentskills";
25
+ readonly version = "1.0.0";
26
+ constructor(fs?: FSLike);
27
+ register(kernel: ExtensionHost): void;
28
+ }