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,145 @@
1
+ /**
2
+ * HttpResolver — ResolverPort for http:/https: URIs.
3
+ *
4
+ * 1:1 parity with Python dna.v3.adapters.resolvers.http.
5
+ *
6
+ * Fetches manifest documents from HTTP endpoints. Supports two modes:
7
+ *
8
+ * 1. **Index mode** (default): GET {uri}/index.json → list of {kind, name, path}.
9
+ * Each item is fetched individually: GET {uri}/{path} → raw dict.
10
+ *
11
+ * 2. **Bundle mode** (fallback): GET {uri} → list of raw dicts directly.
12
+ */
13
+ import { mkdirSync, writeFileSync, mkdtempSync } from "node:fs";
14
+ import { join } from "node:path";
15
+ import { tmpdir } from "node:os";
16
+ import { ResolveNotFoundError, ResolveNetworkError, ResolveError, } from "../../kernel/protocols.js";
17
+ import { rejectLegacyShorthand } from "./local.js";
18
+ export class HttpResolver {
19
+ cacheKey(uri) {
20
+ const safe = uri.replace(/[^a-zA-Z0-9_-]/g, "-").replace(/^-+|-+$/g, "");
21
+ return `http-${safe}`.slice(0, 120);
22
+ }
23
+ async resolve(uri, dep) {
24
+ const baseUrl = uri.replace(/\/$/, "");
25
+ const requested = this._collectRequested(dep);
26
+ // Try index mode first
27
+ try {
28
+ const index = this._fetchJson(`${baseUrl}/index.json`);
29
+ if (Array.isArray(index)) {
30
+ return this._resolveFromIndex(baseUrl, index, requested);
31
+ }
32
+ }
33
+ catch (e) {
34
+ if (e instanceof ResolveError) {
35
+ // Fall through to bundle mode
36
+ }
37
+ else {
38
+ throw e;
39
+ }
40
+ }
41
+ // Fallback: bundle mode
42
+ try {
43
+ const bundle = this._fetchJson(baseUrl);
44
+ if (Array.isArray(bundle)) {
45
+ return this._resolveFromBundle(bundle, requested);
46
+ }
47
+ }
48
+ catch (e) {
49
+ if (e instanceof ResolveError) {
50
+ throw new ResolveError(`HTTP resolve failed for ${uri}: ${e.message}`);
51
+ }
52
+ throw e;
53
+ }
54
+ throw new ResolveError(`HTTP endpoint returned unexpected format: ${uri}`);
55
+ }
56
+ _fetchJson(url) {
57
+ const result = Bun.spawnSync(["curl", "-sf", "-H", "Accept: application/json", url]);
58
+ if (result.exitCode !== 0) {
59
+ const stderr = result.stderr.toString();
60
+ // curl exit code 22 = HTTP 4xx/5xx, 6 = DNS failure, 28 = timeout
61
+ if (stderr.includes("404") || result.stdout.toString().includes("404")) {
62
+ throw new ResolveNotFoundError(`Not found: ${url}`);
63
+ }
64
+ if (result.exitCode === 6 || result.exitCode === 7 || result.exitCode === 28) {
65
+ throw new ResolveNetworkError(`Network error fetching: ${url}`);
66
+ }
67
+ throw new ResolveError(`curl failed (exit ${result.exitCode}): ${url}`);
68
+ }
69
+ const body = result.stdout.toString();
70
+ if (!body.trim()) {
71
+ throw new ResolveNotFoundError(`Empty response from: ${url}`);
72
+ }
73
+ try {
74
+ return JSON.parse(body);
75
+ }
76
+ catch (e) {
77
+ throw new ResolveError(`Invalid JSON from ${url}: ${e}`);
78
+ }
79
+ }
80
+ _resolveFromIndex(baseUrl, index, requested) {
81
+ const items = [];
82
+ for (const entry of index) {
83
+ const kind = entry.kind ?? "";
84
+ const name = entry.name ?? "";
85
+ const path = entry.path ?? "";
86
+ if (requested && !this._matchesRequest(kind, name, requested)) {
87
+ continue;
88
+ }
89
+ let raw;
90
+ try {
91
+ raw = this._fetchJson(`${baseUrl}/${path}`);
92
+ }
93
+ catch {
94
+ continue;
95
+ }
96
+ const tmp = mkdtempSync(join(tmpdir(), "dna-http-"));
97
+ const itemDir = join(tmp, name);
98
+ mkdirSync(itemDir, { recursive: true });
99
+ writeFileSync(join(itemDir, "manifest.yaml"), JSON.stringify(raw, null, 2));
100
+ items.push({ name, kind, sourcePath: itemDir });
101
+ }
102
+ return items;
103
+ }
104
+ _resolveFromBundle(bundle, requested) {
105
+ const items = [];
106
+ for (const raw of bundle) {
107
+ const kind = raw.kind ?? "";
108
+ const metadata = raw.metadata ?? {};
109
+ const name = metadata.name ?? "";
110
+ if (!name)
111
+ continue;
112
+ if (requested && !this._matchesRequest(kind, name, requested)) {
113
+ continue;
114
+ }
115
+ const tmp = mkdtempSync(join(tmpdir(), "dna-http-"));
116
+ const itemDir = join(tmp, name);
117
+ mkdirSync(itemDir, { recursive: true });
118
+ writeFileSync(join(itemDir, "manifest.yaml"), JSON.stringify(raw, null, 2));
119
+ items.push({ name, kind, sourcePath: itemDir });
120
+ }
121
+ return items;
122
+ }
123
+ _collectRequested(dep) {
124
+ // i-010 — same normalization contract as LocalResolver._collectRequested:
125
+ // the legacy pre-v3 shorthand throws ResolveError before any fetch.
126
+ rejectLegacyShorthand(dep);
127
+ const result = {};
128
+ const items = dep.items ?? [];
129
+ for (const item of items) {
130
+ const kind = item.kind ?? "";
131
+ if (kind) {
132
+ result[kind] = item.names ?? [];
133
+ }
134
+ }
135
+ return Object.keys(result).length > 0 ? result : null;
136
+ }
137
+ _matchesRequest(kind, name, requested) {
138
+ if (!(kind in requested))
139
+ return false;
140
+ const names = requested[kind];
141
+ if (!names || names.length === 0)
142
+ return true;
143
+ return names.includes(name);
144
+ }
145
+ }
@@ -0,0 +1,3 @@
1
+ export { LocalResolver } from "./local.js";
2
+ export { HttpResolver } from "./http.js";
3
+ export { GitHubResolver } from "./github.js";
@@ -0,0 +1,3 @@
1
+ export { LocalResolver } from "./local.js";
2
+ export { HttpResolver } from "./http.js";
3
+ export { GitHubResolver } from "./github.js";
@@ -0,0 +1,24 @@
1
+ /**
2
+ * LocalResolver — ResolverPort for local: URIs.
3
+ *
4
+ * 1:1 parity with Python dna.v3.adapters.resolvers.local.
5
+ */
6
+ import type { ResolvedItem, ResolverPort } from "../../kernel/protocols.js";
7
+ export declare const LEGACY_SHORTHAND_KEYS: readonly ["skills", "souls", "agents", "actors", "guardrails"];
8
+ /**
9
+ * Throw ResolveError if the dep uses the dead pre-v3 category shorthand.
10
+ *
11
+ * Shared by the local, github and http resolvers so the legacy format
12
+ * fails loud (an entry in `mi.resolveErrors`) with a rewrite recipe
13
+ * instead of silently resolving at the wrong granularity.
14
+ */
15
+ export declare function rejectLegacyShorthand(dep: Record<string, unknown>): void;
16
+ export declare class LocalResolver implements ResolverPort {
17
+ readonly baseDir: string | null;
18
+ constructor(baseDir?: string);
19
+ cacheKey(uri: string): string;
20
+ resolve(uri: string, dep: Record<string, unknown>): Promise<ResolvedItem[]>;
21
+ private _collectRequested;
22
+ private _resolveAll;
23
+ private _resolveByCategory;
24
+ }
@@ -0,0 +1,132 @@
1
+ /**
2
+ * LocalResolver — ResolverPort for local: URIs.
3
+ *
4
+ * 1:1 parity with Python dna.v3.adapters.resolvers.local.
5
+ */
6
+ import { existsSync, readdirSync, statSync } from "node:fs";
7
+ import { resolve, join } from "node:path";
8
+ import { ResolveError } from "../../kernel/protocols.js";
9
+ // i-009 / i-010 — pre-v3 dependency shorthand: category-list keys at the dep
10
+ // top level (`skills: [...]`). DEAD format — the Genome contract is
11
+ // `items: [{kind, names}]` and no resolver ever read the shorthand, so it
12
+ // silently fell through to _resolveAll with the wrong granularity
13
+ // (bundle SUBDIRECTORIES instead of bundles). Rejected loudly instead.
14
+ // Twin of LEGACY_SHORTHAND_KEYS / reject_legacy_shorthand in
15
+ // dna/adapters/resolvers/local.py — same keys, same message.
16
+ export const LEGACY_SHORTHAND_KEYS = [
17
+ "skills",
18
+ "souls",
19
+ "agents",
20
+ "actors",
21
+ "guardrails",
22
+ ];
23
+ /**
24
+ * Throw ResolveError if the dep uses the dead pre-v3 category shorthand.
25
+ *
26
+ * Shared by the local, github and http resolvers so the legacy format
27
+ * fails loud (an entry in `mi.resolveErrors`) with a rewrite recipe
28
+ * instead of silently resolving at the wrong granularity.
29
+ */
30
+ export function rejectLegacyShorthand(dep) {
31
+ const legacy = LEGACY_SHORTHAND_KEYS.filter((k) => Array.isArray(dep[k]));
32
+ if (legacy.length > 0) {
33
+ const source = typeof dep.source === "string" ? dep.source : "<unknown>";
34
+ const singular = legacy[0].replace(/s+$/, "");
35
+ const kind = singular.charAt(0).toUpperCase() + singular.slice(1);
36
+ throw new ResolveError(`Dependency '${source}' uses the legacy '${legacy[0]}:' shorthand, ` +
37
+ `which is no longer read. Rewrite it in the v3 items format:\n` +
38
+ ` items:\n` +
39
+ ` - kind: ${kind}\n` +
40
+ ` names: [...]`);
41
+ }
42
+ }
43
+ export class LocalResolver {
44
+ baseDir;
45
+ constructor(baseDir) {
46
+ this.baseDir = baseDir ? resolve(baseDir) : null;
47
+ }
48
+ cacheKey(uri) {
49
+ const path = uri.replace(/^local:/, "");
50
+ const safe = path.replace(/[^a-zA-Z0-9_-]/g, "-").replace(/^-+|-+$/g, "");
51
+ return `local-${safe}`;
52
+ }
53
+ async resolve(uri, dep) {
54
+ const pathStr = uri.replace(/^local:/, "");
55
+ let localPath = pathStr;
56
+ if (!localPath.startsWith("/") && this.baseDir) {
57
+ localPath = resolve(this.baseDir, pathStr);
58
+ }
59
+ if (!existsSync(localPath)) {
60
+ return [];
61
+ }
62
+ // Normalize dep format
63
+ const requested = this._collectRequested(dep);
64
+ if (requested) {
65
+ return this._resolveByCategory(localPath, requested);
66
+ }
67
+ return this._resolveAll(localPath);
68
+ }
69
+ _collectRequested(dep) {
70
+ // The legacy pre-v3 shorthand (`skills: [...]` at the dep top level)
71
+ // throws ResolveError (i-009/i-010) — see `rejectLegacyShorthand`.
72
+ rejectLegacyShorthand(dep);
73
+ const result = {};
74
+ const items = dep.items ?? [];
75
+ for (const item of items) {
76
+ const kind = item.kind ?? "";
77
+ if (kind) {
78
+ const category = kind.toLowerCase() + "s"; // Skill -> skills
79
+ result[category] = item.names ?? []; // [] = all
80
+ }
81
+ }
82
+ return Object.keys(result).length > 0 ? result : null;
83
+ }
84
+ _resolveAll(source) {
85
+ const items = [];
86
+ const entries = readdirSync(source).sort();
87
+ for (const entry of entries) {
88
+ const full = join(source, entry);
89
+ if (!statSync(full).isDirectory() || entry.startsWith("."))
90
+ continue;
91
+ // Recurse into category dirs (skills/, souls/, agents/)
92
+ const subEntries = readdirSync(full).sort();
93
+ for (const sub of subEntries) {
94
+ const itemDir = join(full, sub);
95
+ if (statSync(itemDir).isDirectory()) {
96
+ items.push({ name: sub, kind: "", sourcePath: itemDir });
97
+ }
98
+ }
99
+ }
100
+ return items;
101
+ }
102
+ _resolveByCategory(source, requested) {
103
+ const items = [];
104
+ for (const [category, names] of Object.entries(requested)) {
105
+ const categoryDir = join(source, category);
106
+ if (!existsSync(categoryDir))
107
+ continue;
108
+ if (names.length > 0) {
109
+ // Import specific names
110
+ for (const name of names) {
111
+ for (const candidate of [join(categoryDir, name), join(source, name)]) {
112
+ if (existsSync(candidate) && statSync(candidate).isDirectory()) {
113
+ items.push({ name, kind: "", sourcePath: candidate });
114
+ break;
115
+ }
116
+ }
117
+ }
118
+ }
119
+ else {
120
+ // Import all from category
121
+ const entries = readdirSync(categoryDir).sort();
122
+ for (const entry of entries) {
123
+ const subdir = join(categoryDir, entry);
124
+ if (statSync(subdir).isDirectory()) {
125
+ items.push({ name: entry, kind: "", sourcePath: subdir });
126
+ }
127
+ }
128
+ }
129
+ }
130
+ return items;
131
+ }
132
+ }
@@ -0,0 +1,45 @@
1
+ /**
2
+ * SQLite driver shim for the sqlite-vec search provider — the ONE place that
3
+ * papers over the two runtimes DNA's TS SDK runs under, so the provider itself
4
+ * is runtime-agnostic:
5
+ *
6
+ * - Bun (the test runner) → `bun:sqlite`. Bun auto-loads SQLite on the first
7
+ * `Database` open and extension loading must be enabled BEFORE that, via
8
+ * `Database.setCustomSQLite(libPath)`. We probe for a libsqlite3 that allows
9
+ * extensions (env `DNA_SQLITE_LIB`, then common macOS/Linux paths) exactly
10
+ * once, before opening anything. Bun's own bundled SQLite already allows it
11
+ * on most Linux builds, so the probe is a best-effort fallback.
12
+ * - Node ≥22.5 → `node:sqlite` (`DatabaseSync`, `{ allowExtension: true }`),
13
+ * which bundles a SQLite compiled with extension loading — no external lib.
14
+ *
15
+ * Both expose the tiny synchronous surface the provider needs. sqlite-vec is
16
+ * loaded into every opened connection. If no runtime can load the extension,
17
+ * `openSearchDb` throws `SqliteVecUnavailableError` — the provider surfaces it
18
+ * and callers (CLI, conformance kit) degrade/skip with a clear reason rather
19
+ * than crash.
20
+ */
21
+ /** The minimal synchronous SQLite surface the provider drives. */
22
+ export interface SearchDb {
23
+ exec(sql: string): void;
24
+ run(sql: string, params?: unknown[]): {
25
+ lastInsertRowid: number;
26
+ };
27
+ get<T = Record<string, unknown>>(sql: string, params?: unknown[]): T | undefined;
28
+ all<T = Record<string, unknown>>(sql: string, params?: unknown[]): T[];
29
+ close(): void;
30
+ }
31
+ /** Thrown when no available runtime can load the sqlite-vec extension. */
32
+ export declare class SqliteVecUnavailableError extends Error {
33
+ constructor(message: string, options?: {
34
+ cause?: unknown;
35
+ });
36
+ }
37
+ /**
38
+ * Open a SQLite connection with sqlite-vec loaded, using whichever runtime
39
+ * driver is available. Throws {@link SqliteVecUnavailableError} when neither
40
+ * can load the extension.
41
+ */
42
+ export declare function openSearchDb(path: string): Promise<SearchDb>;
43
+ /** Pack a float vector into sqlite-vec's float32 blob layout (parity with the
44
+ * Python `_serialize_f32`). */
45
+ export declare function serializeF32(vector: number[]): Uint8Array;
@@ -0,0 +1,138 @@
1
+ /**
2
+ * SQLite driver shim for the sqlite-vec search provider — the ONE place that
3
+ * papers over the two runtimes DNA's TS SDK runs under, so the provider itself
4
+ * is runtime-agnostic:
5
+ *
6
+ * - Bun (the test runner) → `bun:sqlite`. Bun auto-loads SQLite on the first
7
+ * `Database` open and extension loading must be enabled BEFORE that, via
8
+ * `Database.setCustomSQLite(libPath)`. We probe for a libsqlite3 that allows
9
+ * extensions (env `DNA_SQLITE_LIB`, then common macOS/Linux paths) exactly
10
+ * once, before opening anything. Bun's own bundled SQLite already allows it
11
+ * on most Linux builds, so the probe is a best-effort fallback.
12
+ * - Node ≥22.5 → `node:sqlite` (`DatabaseSync`, `{ allowExtension: true }`),
13
+ * which bundles a SQLite compiled with extension loading — no external lib.
14
+ *
15
+ * Both expose the tiny synchronous surface the provider needs. sqlite-vec is
16
+ * loaded into every opened connection. If no runtime can load the extension,
17
+ * `openSearchDb` throws `SqliteVecUnavailableError` — the provider surfaces it
18
+ * and callers (CLI, conformance kit) degrade/skip with a clear reason rather
19
+ * than crash.
20
+ */
21
+ /** Thrown when no available runtime can load the sqlite-vec extension. */
22
+ export class SqliteVecUnavailableError extends Error {
23
+ constructor(message, options) {
24
+ super(message, options);
25
+ this.name = "SqliteVecUnavailableError";
26
+ }
27
+ }
28
+ const isBun = typeof globalThis.Bun !== "undefined";
29
+ // Candidate libsqlite3 paths that permit extension loading. An explicit
30
+ // `DNA_SQLITE_LIB` override wins on any platform. Otherwise we only probe on
31
+ // macOS: Apple's system libsqlite3 (which Bun uses there) has extension loading
32
+ // DISABLED, so we point Bun at Homebrew's build. On Linux, Bun's bundled SQLite
33
+ // already allows loadExtension, so we add NO candidate and trust it — forcing a
34
+ // distro lib could regress if that build omitted extension support.
35
+ function libCandidates() {
36
+ const out = [];
37
+ const proc = globalThis.process;
38
+ if (proc?.env?.DNA_SQLITE_LIB)
39
+ out.push(proc.env.DNA_SQLITE_LIB);
40
+ if (proc?.platform === "darwin") {
41
+ out.push("/opt/homebrew/opt/sqlite/lib/libsqlite3.dylib", "/usr/local/opt/sqlite/lib/libsqlite3.dylib");
42
+ }
43
+ return out;
44
+ }
45
+ let bunCustomSqliteChosen = false;
46
+ async function chooseBunSqlite() {
47
+ if (bunCustomSqliteChosen)
48
+ return;
49
+ bunCustomSqliteChosen = true; // decide once, before the first open
50
+ const { Database } = (await import("bun:sqlite"));
51
+ // Only set a custom lib if one exists on disk; otherwise trust Bun's bundled
52
+ // SQLite (extension loading works on most Linux builds out of the box).
53
+ let fs;
54
+ try {
55
+ fs = (await import("node:fs"));
56
+ }
57
+ catch {
58
+ return;
59
+ }
60
+ for (const path of libCandidates()) {
61
+ if (fs.existsSync(path)) {
62
+ try {
63
+ Database.setCustomSQLite(path);
64
+ }
65
+ catch {
66
+ /* already loaded or unsupported — fall through to bundled */
67
+ }
68
+ return;
69
+ }
70
+ }
71
+ }
72
+ async function openBun(path) {
73
+ await chooseBunSqlite();
74
+ const { Database } = (await import("bun:sqlite"));
75
+ const sqliteVec = await import("sqlite-vec");
76
+ const db = new Database(path);
77
+ try {
78
+ sqliteVec.load(db);
79
+ }
80
+ catch (err) {
81
+ db.close();
82
+ throw new SqliteVecUnavailableError("bun:sqlite could not load the sqlite-vec extension — set DNA_SQLITE_LIB "
83
+ + "to a libsqlite3 that permits extension loading (e.g. Homebrew's "
84
+ + "/opt/homebrew/opt/sqlite/lib/libsqlite3.dylib).", { cause: err });
85
+ }
86
+ return {
87
+ exec: (sql) => db.exec(sql),
88
+ run: (sql, params = []) => {
89
+ const info = db.query(sql).run(...params);
90
+ return { lastInsertRowid: Number(info.lastInsertRowid) };
91
+ },
92
+ get: (sql, params = []) => db.query(sql).get(...params),
93
+ all: (sql, params = []) => db.query(sql).all(...params),
94
+ close: () => db.close(),
95
+ };
96
+ }
97
+ async function openNode(path) {
98
+ let DatabaseSync;
99
+ try {
100
+ ({ DatabaseSync } = (await import("node:sqlite")));
101
+ }
102
+ catch (err) {
103
+ throw new SqliteVecUnavailableError("node:sqlite is unavailable (needs Node ≥22.5 with --experimental-sqlite "
104
+ + "or ≥23.5). Run under Bun, or upgrade Node.", { cause: err });
105
+ }
106
+ const sqliteVec = await import("sqlite-vec");
107
+ const db = new DatabaseSync(path, { allowExtension: true });
108
+ try {
109
+ db.loadExtension(sqliteVec.getLoadablePath());
110
+ }
111
+ catch (err) {
112
+ db.close();
113
+ throw new SqliteVecUnavailableError("node:sqlite could not load the sqlite-vec extension.", { cause: err });
114
+ }
115
+ return {
116
+ exec: (sql) => db.exec(sql),
117
+ run: (sql, params = []) => {
118
+ const info = db.prepare(sql).run(...params);
119
+ return { lastInsertRowid: Number(info.lastInsertRowid) };
120
+ },
121
+ get: (sql, params = []) => db.prepare(sql).get(...params),
122
+ all: (sql, params = []) => db.prepare(sql).all(...params),
123
+ close: () => db.close(),
124
+ };
125
+ }
126
+ /**
127
+ * Open a SQLite connection with sqlite-vec loaded, using whichever runtime
128
+ * driver is available. Throws {@link SqliteVecUnavailableError} when neither
129
+ * can load the extension.
130
+ */
131
+ export async function openSearchDb(path) {
132
+ return isBun ? openBun(path) : openNode(path);
133
+ }
134
+ /** Pack a float vector into sqlite-vec's float32 blob layout (parity with the
135
+ * Python `_serialize_f32`). */
136
+ export function serializeF32(vector) {
137
+ return new Uint8Array(new Float32Array(vector).buffer);
138
+ }
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Forward-only numbered migrations for the sqlite-vec search store (TS twin of
3
+ * `dna/adapters/search/migrations.py`).
4
+ *
5
+ * The debt rsh-memory-similarity-evolution (f-embeddings-ddl-debt) calls out:
6
+ * the embeddings sidecar had no owning migration upstream. Here the store's
7
+ * schema is OWNED by a numbered, append-only, idempotent migration recorded in
8
+ * the store's own `schema_migrations` control table. The vec0 virtual table
9
+ * needs the embedding width baked in, so `buildMigrations(dims)` interpolates
10
+ * it — the migration still owns the DDL, it just parametrizes the vector width.
11
+ *
12
+ * The TS SDK has no shared migration runner (only the Postgres source has its
13
+ * own), so {@link runSearchMigrations} is the small local analog of the Python
14
+ * `run_migrations` contract: same forward-only/append-only/idempotent-boot
15
+ * semantics, over the synchronous {@link SearchDb} surface.
16
+ */
17
+ import type { SearchDb } from "./driver.js";
18
+ /** version → one executescript payload (SQLite dialect). Parity with the Py
19
+ * `build_migrations(dims)`. */
20
+ export declare function buildMigrations(dims: number): Record<number, string>;
21
+ /**
22
+ * Apply every pending migration in ascending version order (forward-only,
23
+ * append-only, idempotent boot). Returns the versions applied by THIS call —
24
+ * `[]` when the store is already up to date (the idempotent re-boot case).
25
+ */
26
+ export declare function runSearchMigrations(db: SearchDb, migrations: Record<number, string>): number[];
@@ -0,0 +1,58 @@
1
+ /** version → one executescript payload (SQLite dialect). Parity with the Py
2
+ * `build_migrations(dims)`. */
3
+ export function buildMigrations(dims) {
4
+ if (dims < 1)
5
+ throw new Error(`embedding dims must be positive, got ${dims}`);
6
+ return {
7
+ 1: `
8
+ CREATE TABLE IF NOT EXISTS search_docs (
9
+ rowid INTEGER PRIMARY KEY AUTOINCREMENT,
10
+ scope TEXT NOT NULL,
11
+ kind TEXT NOT NULL,
12
+ name TEXT NOT NULL,
13
+ tenant TEXT NOT NULL DEFAULT '',
14
+ text_hash TEXT NOT NULL,
15
+ title TEXT,
16
+ snippet TEXT,
17
+ text TEXT NOT NULL,
18
+ UNIQUE (scope, kind, name, tenant)
19
+ );
20
+ CREATE INDEX IF NOT EXISTS search_docs_lookup
21
+ ON search_docs (scope, kind, tenant);
22
+ CREATE VIRTUAL TABLE IF NOT EXISTS search_vec USING vec0(
23
+ doc_rowid INTEGER PRIMARY KEY,
24
+ embedding float[${dims}]
25
+ );
26
+ CREATE VIRTUAL TABLE IF NOT EXISTS search_fts USING fts5(text);
27
+ CREATE TABLE IF NOT EXISTS search_meta (
28
+ key TEXT PRIMARY KEY,
29
+ value TEXT NOT NULL
30
+ );
31
+ `,
32
+ };
33
+ }
34
+ /**
35
+ * Apply every pending migration in ascending version order (forward-only,
36
+ * append-only, idempotent boot). Returns the versions applied by THIS call —
37
+ * `[]` when the store is already up to date (the idempotent re-boot case).
38
+ */
39
+ export function runSearchMigrations(db, migrations) {
40
+ db.exec("CREATE TABLE IF NOT EXISTS schema_migrations "
41
+ + "(version INTEGER PRIMARY KEY, applied_at TEXT NOT NULL)");
42
+ const applied = new Set(db.all("SELECT version FROM schema_migrations").map((r) => r.version));
43
+ const appliedNow = [];
44
+ const versions = Object.keys(migrations)
45
+ .map(Number)
46
+ .sort((a, b) => a - b);
47
+ for (const version of versions) {
48
+ if (applied.has(version))
49
+ continue;
50
+ db.exec(migrations[version]);
51
+ db.run("INSERT INTO schema_migrations (version, applied_at) VALUES (?, ?)", [
52
+ version,
53
+ new Date().toISOString(),
54
+ ]);
55
+ appliedNow.push(version);
56
+ }
57
+ return appliedNow;
58
+ }
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Reciprocal Rank Fusion — the pure, deterministic hybrid-fusion core.
3
+ *
4
+ * TypeScript twin of `dna/adapters/search/rrf.py`. RRF (Cormack et al. 2009)
5
+ * fuses several independently-ranked lists using only each item's RANK within
6
+ * each list — never the raw scores, which are incomparable across a cosine
7
+ * dense list and a BM25 lexical list. Each item accrues `1 / (k + rank)` (rank
8
+ * 1-based) from every list it appears in; items are ordered by descending fused
9
+ * score, ties broken by id ascending — fully deterministic and bit-identical to
10
+ * the Python implementation.
11
+ */
12
+ /** RRF smoothing constant (the paper's value; the de-facto default). */
13
+ export declare const DEFAULT_RRF_K = 60;
14
+ /**
15
+ * Fuse ranked id-lists into one ranking via Reciprocal Rank Fusion.
16
+ *
17
+ * @param rankedLists each inner array is item ids in rank order (best first).
18
+ * An id may appear across lists; duplicates WITHIN one list count at their
19
+ * first (best) rank only.
20
+ * @param k RRF smoothing constant (`DEFAULT_RRF_K`); must be > 0.
21
+ * @returns `[id, fusedScore]` pairs sorted by fused score desc, ties by id asc.
22
+ */
23
+ export declare function reciprocalRankFusion(rankedLists: string[][], k?: number): Array<[string, number]>;
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Reciprocal Rank Fusion — the pure, deterministic hybrid-fusion core.
3
+ *
4
+ * TypeScript twin of `dna/adapters/search/rrf.py`. RRF (Cormack et al. 2009)
5
+ * fuses several independently-ranked lists using only each item's RANK within
6
+ * each list — never the raw scores, which are incomparable across a cosine
7
+ * dense list and a BM25 lexical list. Each item accrues `1 / (k + rank)` (rank
8
+ * 1-based) from every list it appears in; items are ordered by descending fused
9
+ * score, ties broken by id ascending — fully deterministic and bit-identical to
10
+ * the Python implementation.
11
+ */
12
+ /** RRF smoothing constant (the paper's value; the de-facto default). */
13
+ export const DEFAULT_RRF_K = 60;
14
+ /**
15
+ * Fuse ranked id-lists into one ranking via Reciprocal Rank Fusion.
16
+ *
17
+ * @param rankedLists each inner array is item ids in rank order (best first).
18
+ * An id may appear across lists; duplicates WITHIN one list count at their
19
+ * first (best) rank only.
20
+ * @param k RRF smoothing constant (`DEFAULT_RRF_K`); must be > 0.
21
+ * @returns `[id, fusedScore]` pairs sorted by fused score desc, ties by id asc.
22
+ */
23
+ export function reciprocalRankFusion(rankedLists, k = DEFAULT_RRF_K) {
24
+ if (k <= 0)
25
+ throw new Error(`RRF k must be positive, got ${k}`);
26
+ const scores = new Map();
27
+ for (const ranked of rankedLists) {
28
+ const seen = new Set();
29
+ ranked.forEach((docId, i) => {
30
+ if (seen.has(docId))
31
+ return; // first (best) rank wins for a repeated id
32
+ seen.add(docId);
33
+ const rank = i + 1;
34
+ scores.set(docId, (scores.get(docId) ?? 0) + 1 / (k + rank));
35
+ });
36
+ }
37
+ return [...scores.entries()].sort((a, b) => {
38
+ if (b[1] !== a[1])
39
+ return b[1] - a[1]; // score desc
40
+ return a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0; // id asc
41
+ });
42
+ }