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,493 @@
1
+ /**
2
+ * v3 Kernel Protocols — the 5 ports + shared types.
3
+ *
4
+ * Parity with Python dna.kernel.protocols is SURFACE-TRACKED, not
5
+ * assumed (s-dna-port-surface-parity): every port's member list — and every
6
+ * INTENTIONAL asymmetry, with its justification — lives in the shared
7
+ * fixture `tests/parity-fixtures/port-surface-parity.json`, enforced by
8
+ * `tests/port-surface-parity.test.ts` (via the keyof-bound PORT_SURFACE
9
+ * manifest in `./port-surface.ts`) and by the Python twin
10
+ * `packages/sdk-py/tests/test_port_surface_parity.py` (real Protocol
11
+ * introspection). Adding/removing a member on either side without updating
12
+ * the fixture turns the suites red.
13
+ */
14
+ // ---------------------------------------------------------------------------
15
+ // Layer policy
16
+ // ---------------------------------------------------------------------------
17
+ export const LayerPolicy = {
18
+ OPEN: "open",
19
+ RESTRICTED: "restricted",
20
+ LOCKED: "locked",
21
+ };
22
+ // ---------------------------------------------------------------------------
23
+ // Tenant scope (Kubernetes-style: each KindPort declares its scope)
24
+ // ---------------------------------------------------------------------------
25
+ /**
26
+ * Whether a Kind's documents belong to a tenant or are globally shared.
27
+ *
28
+ * Mirrors the Kubernetes CRD `scope: Namespaced | Cluster` model. Each
29
+ * KindPort declares its scope; the kernel enforces it on every write.
30
+ *
31
+ * - `TENANTED` (default): documents belong to one tenant. Writing
32
+ * requires a tenant arg; reading is filtered by tenant.
33
+ * Agent, EvalCase, EvalRun, AssessmentRun, Finding, etc.
34
+ * - `GLOBAL`: documents are shared across all tenants. Writes must
35
+ * not pass a tenant; reads ignore the bound tenant.
36
+ * Doc, KindDefinition, Module-level configs, etc.
37
+ */
38
+ export const TenantScope = {
39
+ TENANTED: "tenanted",
40
+ GLOBAL: "global",
41
+ };
42
+ /** Reserved tenant slugs — never accepted as user input. */
43
+ export const RESERVED_TENANT_SLUGS = new Set([
44
+ "_global",
45
+ "_legacy",
46
+ "_system",
47
+ "",
48
+ ]);
49
+ /**
50
+ * Raised when a TENANTED kind is written without a tenant arg.
51
+ *
52
+ * Bind a tenant on construction (`new Kernel({ tenant: X })`) or
53
+ * per-call (`kernel.withTenant(X).writeDocument(...)`).
54
+ */
55
+ export class TenantRequired extends Error {
56
+ constructor(message) {
57
+ super(message);
58
+ this.name = "TenantRequired";
59
+ Object.setPrototypeOf(this, new.target.prototype);
60
+ }
61
+ }
62
+ /**
63
+ * Raised when a GLOBAL kind is written with a tenant arg.
64
+ *
65
+ * Global kinds (Doc, KindDefinition, ...) are shared across
66
+ * tenants. Writes must explicitly omit `tenant`.
67
+ */
68
+ export class TenantNotAllowed extends Error {
69
+ constructor(message) {
70
+ super(message);
71
+ this.name = "TenantNotAllowed";
72
+ Object.setPrototypeOf(this, new.target.prototype);
73
+ }
74
+ }
75
+ /**
76
+ * Raised when `writeDocument` vetoes a doc whose `spec` violates the Kind's
77
+ * declared JSON Schema (`KindPort.schema()`).
78
+ *
79
+ * s-write-path-validation (i-008): the kernel used to validate schemas only
80
+ * at SCAN/read (the fail-soft `parse_error` channel) — a shape-broken doc
81
+ * would persist and explode later, far from the author. Now every
82
+ * `writeDocument` validates the spec at write time when the Kind declares a
83
+ * schema; Kinds without a schema stay permissive.
84
+ * Mode knob: `DNA_WRITE_VALIDATION=enforce|warn|off`.
85
+ */
86
+ export class SpecValidationError extends Error {
87
+ constructor(message) {
88
+ super(message);
89
+ this.name = "SpecValidationError";
90
+ Object.setPrototypeOf(this, new.target.prototype);
91
+ }
92
+ }
93
+ /** Raised when a tenant slug is reserved or empty. */
94
+ export class InvalidTenantSlug extends Error {
95
+ constructor(message) {
96
+ super(message);
97
+ this.name = "InvalidTenantSlug";
98
+ Object.setPrototypeOf(this, new.target.prototype);
99
+ }
100
+ }
101
+ /**
102
+ * Validate a tenant slug.
103
+ *
104
+ * Phase 1 only checks the reserved set + non-empty/length. Character
105
+ * rules (DNS-label, lowercase) are NOT enforced at the kernel boundary
106
+ * so existing tests/data using uppercase ("T1", "Acme") keep working.
107
+ * Path-traversal safety lives in the adapter.
108
+ *
109
+ * Phase 2 may tighten to k8s namespace rules (`[a-z0-9-]{1,63}`) once
110
+ * the migration is complete.
111
+ */
112
+ export function validateTenantSlug(tenant) {
113
+ if (tenant === null || tenant === undefined)
114
+ return;
115
+ if (RESERVED_TENANT_SLUGS.has(tenant)) {
116
+ const reserved = Array.from(RESERVED_TENANT_SLUGS).sort();
117
+ throw new InvalidTenantSlug(`tenant slug ${JSON.stringify(tenant)} is reserved (one of ${JSON.stringify(reserved)})`);
118
+ }
119
+ if (tenant.length < 1 || tenant.length > 253) {
120
+ throw new InvalidTenantSlug(`tenant slug ${JSON.stringify(tenant)} must be 1-253 chars (got ${tenant.length})`);
121
+ }
122
+ }
123
+ /**
124
+ * Raised when a write to a layer violates the declared LayerPolicy in
125
+ * `Module.spec.layers`. Thrown by `Kernel.writeDocument` before the adapter
126
+ * is touched. Harness endpoints translate to HTTP 403.
127
+ */
128
+ export class LayerPolicyViolationError extends Error {
129
+ constructor(message) {
130
+ super(message);
131
+ this.name = "LayerPolicyViolationError";
132
+ Object.setPrototypeOf(this, LayerPolicyViolationError.prototype);
133
+ }
134
+ }
135
+ export function isCompositionValid(result) {
136
+ return result.missing.length === 0;
137
+ }
138
+ /**
139
+ * Raised when a query filter / order_by is malformed in a way the
140
+ * adapter can detect statically (unknown operator, …). 1:1 with the Py
141
+ * `QueryError`.
142
+ */
143
+ export class QueryError extends Error {
144
+ constructor(message) {
145
+ super(message);
146
+ this.name = "QueryError";
147
+ Object.setPrototypeOf(this, new.target.prototype);
148
+ }
149
+ }
150
+ /** Operators recognized by `matchFilter` — the Py `_PG_OP_MAP` set + `in`. */
151
+ export const QUERY_OPS = new Set([
152
+ "eq", "in", "like", "gt", "gte", "lt", "lte", "neq",
153
+ ]);
154
+ function isPlainObject(v) {
155
+ return typeof v === "object" && v !== null && !Array.isArray(v);
156
+ }
157
+ /**
158
+ * Deep structural equality for filter matching. Py uses `==` which
159
+ * deep-compares dicts/lists; mirror that. Best-effort divergence: Py
160
+ * `1 == 1.0 == True` numeric cross-type equality is NOT replicated for
161
+ * booleans (JS `1 !== true`) — filters should use the field's real type.
162
+ */
163
+ function deepEqual(a, b) {
164
+ if (a === b)
165
+ return true;
166
+ if (Array.isArray(a) && Array.isArray(b)) {
167
+ return a.length === b.length && a.every((v, i) => deepEqual(v, b[i]));
168
+ }
169
+ if (isPlainObject(a) && isPlainObject(b)) {
170
+ const ka = Object.keys(a);
171
+ const kb = Object.keys(b);
172
+ return (ka.length === kb.length
173
+ && ka.every((k) => k in b && deepEqual(a[k], b[k])));
174
+ }
175
+ return false;
176
+ }
177
+ /**
178
+ * Walk a dotted `fieldPath` through `doc`. Unprefixed paths resolve under
179
+ * `spec.`; `name` is a reserved short for `metadata.name`. Returns `null`
180
+ * when any segment is missing (never `undefined` — parity with Py `None`).
181
+ */
182
+ export function resolveFieldPath(doc, path) {
183
+ if (path === "name") {
184
+ const meta = doc.metadata;
185
+ return isPlainObject(meta) ? (meta.name ?? null) : null;
186
+ }
187
+ if (path === "kind")
188
+ return doc.kind ?? null;
189
+ if (path === "apiVersion")
190
+ return doc.apiVersion ?? null;
191
+ const segments = path.startsWith("metadata.") || path.startsWith("spec.") || path.startsWith("apiVersion.")
192
+ ? path.split(".")
193
+ : ["spec", ...path.split(".")];
194
+ let cur = doc;
195
+ for (const seg of segments) {
196
+ if (!isPlainObject(cur))
197
+ return null;
198
+ cur = cur[seg];
199
+ if (cur === null || cur === undefined)
200
+ return null;
201
+ }
202
+ return cur ?? null;
203
+ }
204
+ /**
205
+ * Evaluate a `QueryFilter` against a single doc. Unknown operators throw
206
+ * `QueryError` so callers can debug instead of silently matching. 1:1
207
+ * port of the Py `_match_filter` (only single-key dicts enter the
208
+ * operator branch; anything else is shorthand equality).
209
+ */
210
+ export function matchFilter(doc, filter) {
211
+ for (const [path, expected] of Object.entries(filter)) {
212
+ const actual = resolveFieldPath(doc, path);
213
+ if (isPlainObject(expected) && Object.keys(expected).length === 1) {
214
+ const [op, val] = Object.entries(expected)[0];
215
+ if (!QUERY_OPS.has(op)) {
216
+ throw new QueryError(`unknown query operator ${JSON.stringify(op)} on field `
217
+ + `${JSON.stringify(path)}; valid: ${[...QUERY_OPS].sort().join(", ")}`);
218
+ }
219
+ if (op === "eq" && !deepEqual(actual, val))
220
+ return false;
221
+ if (op === "neq" && deepEqual(actual, val))
222
+ return false;
223
+ if (op === "in") {
224
+ // Py: `actual not in (val or ())` — null/empty list matches nothing.
225
+ // DIVERGENCE vs Py: Py `actual in <str>` does substring membership on
226
+ // a malformed string operand; TS coerces non-array to [] (matches nothing).
227
+ const candidates = Array.isArray(val) ? val : [];
228
+ if (!candidates.some((v) => deepEqual(actual, v)))
229
+ return false;
230
+ }
231
+ if (op === "like") {
232
+ if (actual === null || typeof val !== "string")
233
+ return false;
234
+ // SQL LIKE: % = any, _ = single char. Tokenize so ONLY literals
235
+ // are regex-escaped (same approach as the Py fallback).
236
+ const parts = [];
237
+ for (const ch of val) {
238
+ if (ch === "%")
239
+ parts.push(".*");
240
+ else if (ch === "_")
241
+ parts.push(".");
242
+ else
243
+ parts.push(ch.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"));
244
+ }
245
+ if (!new RegExp(`^${parts.join("")}$`).test(String(actual)))
246
+ return false;
247
+ }
248
+ // Range ops: null never matches (Py: `actual is not None and …`).
249
+ // DIVERGENCE vs Py: Py raises TypeError on cross-type range compares;
250
+ // TS coerces (NaN) and silently excludes the row — don't rely on
251
+ // cross-type range filters.
252
+ /* eslint-disable @typescript-eslint/no-explicit-any */
253
+ if (op === "gt" && !(actual !== null && actual > val))
254
+ return false;
255
+ if (op === "gte" && !(actual !== null && actual >= val))
256
+ return false;
257
+ if (op === "lt" && !(actual !== null && actual < val))
258
+ return false;
259
+ if (op === "lte" && !(actual !== null && actual <= val))
260
+ return false;
261
+ /* eslint-enable @typescript-eslint/no-explicit-any */
262
+ }
263
+ else if (!deepEqual(actual, expected)) {
264
+ // Shorthand: {"status": "in-progress"} == {"status": {"eq": ...}}
265
+ return false;
266
+ }
267
+ }
268
+ return true;
269
+ }
270
+ /**
271
+ * Stable sort `rows` by each `orderBy` field, last-first to achieve the
272
+ * desired primary/secondary precedence. Prefixed `-` means descending.
273
+ * `null` values sort LAST regardless of direction: the None-flag is
274
+ * `(v === null) !== descending` — immune to the reversed comparator
275
+ * (i-121: parity with PG `DESC NULLS LAST`; a plain `v === null` flag
276
+ * would flip under reversal and shove nulls to the FRONT in DESC).
277
+ *
278
+ * Mixed-type values (number + string across rows on the same field) fall
279
+ * back to stringified compare to mirror the Py TypeError fallback. Does
280
+ * not mutate the input. Best-effort like the Py protocol-default —
281
+ * adapters with native push-down use the backend's type semantics.
282
+ */
283
+ export function applyOrderBy(rows, orderBy) {
284
+ let out = rows.slice();
285
+ for (const spec of [...orderBy].reverse()) {
286
+ const descending = spec.startsWith("-");
287
+ const path = descending ? spec.slice(1) : spec;
288
+ const decorated = out.map((row) => {
289
+ const v = resolveFieldPath(row, path);
290
+ return { row, v, flag: (v === null) !== descending };
291
+ });
292
+ // Py first pass keeps int/float (and bool, a Py int subtype) numeric
293
+ // and stringifies the rest; a mixed numeric/string field raises
294
+ // TypeError there → stringify-everything fallback. Detect the mix
295
+ // upfront instead of try/catch.
296
+ const isNumeric = (v) => typeof v === "number" || typeof v === "boolean";
297
+ const nonNull = decorated.filter((d) => d.v !== null);
298
+ const mixed = nonNull.some((d) => isNumeric(d.v)) && nonNull.some((d) => !isNumeric(d.v));
299
+ const sortable = (v) => {
300
+ if (v === null)
301
+ return "";
302
+ if (mixed)
303
+ return v ? String(v) : ""; // Py fallback: str(v or "")
304
+ return isNumeric(v) ? Number(v) : String(v);
305
+ };
306
+ const keyed = decorated.map((d) => ({ ...d, key: sortable(d.v) }));
307
+ const cmp = (a, b) => {
308
+ if (a.flag !== b.flag)
309
+ return a.flag ? 1 : -1;
310
+ if (typeof a.key === "number" && typeof b.key === "number") {
311
+ return a.key - b.key;
312
+ }
313
+ const sa = String(a.key);
314
+ const sb = String(b.key);
315
+ return sa < sb ? -1 : sa > sb ? 1 : 0;
316
+ };
317
+ // JS sort is stable; the negated comparator under `descending`
318
+ // preserves original order for ties — same as Py `reverse=True`.
319
+ keyed.sort((a, b) => (descending ? -cmp(a, b) : cmp(a, b)));
320
+ out = keyed.map((d) => d.row);
321
+ }
322
+ return out;
323
+ }
324
+ /**
325
+ * In-memory query core — mirror of the Py `SourcePort.query`
326
+ * protocol-default minus the IO: kind filter first (cheap), then
327
+ * `matchFilter`, then `applyOrderBy`, then limit/offset paging.
328
+ * `FilesystemSource.query` is `loadAll` + this; the shared parity
329
+ * fixture drives it directly.
330
+ */
331
+ export function queryDocs(docs, kind, opts = {}) {
332
+ let kindDocs = docs.filter((d) => d.kind === kind);
333
+ if (opts.filter && Object.keys(opts.filter).length > 0) {
334
+ kindDocs = kindDocs.filter((d) => matchFilter(d, opts.filter));
335
+ }
336
+ if (opts.orderBy && opts.orderBy.length > 0) {
337
+ kindDocs = applyOrderBy(kindDocs, opts.orderBy);
338
+ }
339
+ const start = opts.offset ?? 0;
340
+ const end = opts.limit != null ? start + opts.limit : undefined;
341
+ return kindDocs.slice(start, end);
342
+ }
343
+ /**
344
+ * In-memory count core — mirror of the Py `SourcePort.count`
345
+ * protocol-default: total of docs matching the filter, optionally
346
+ * grouped by a field path. Groups ordered by count DESC, then key ASC
347
+ * with `null` LAST (matches PG `ORDER BY count DESC, key ASC NULLS
348
+ * LAST` and the spirit of i-121).
349
+ */
350
+ export function countDocs(docs, kind, opts = {}) {
351
+ const rows = queryDocs(docs, kind, { filter: opts.filter });
352
+ const total = rows.length;
353
+ let groups = null;
354
+ if (opts.groupBy != null) {
355
+ const counter = new Map();
356
+ for (const row of rows) {
357
+ const v = resolveFieldPath(row, opts.groupBy);
358
+ // DIVERGENCE vs Py (unreachable with primitive keys, pinned for record):
359
+ // Py str(True)="True" / str(dict)="{'k': 1}"; JS String(true)="true" /
360
+ // String({})="[object Object]". Complex group keys should be avoided.
361
+ const key = v === null || ["string", "number", "boolean"].includes(typeof v)
362
+ ? v
363
+ : String(v);
364
+ counter.set(key, (counter.get(key) ?? 0) + 1);
365
+ }
366
+ groups = [...counter.entries()]
367
+ .map(([key, count]) => ({ key, count }))
368
+ .sort((a, b) => {
369
+ if (a.count !== b.count)
370
+ return b.count - a.count;
371
+ if ((a.key === null) !== (b.key === null))
372
+ return a.key === null ? 1 : -1;
373
+ const sa = String(a.key);
374
+ const sb = String(b.key);
375
+ return sa < sb ? -1 : sa > sb ? 1 : 0;
376
+ });
377
+ }
378
+ return { total, groups };
379
+ }
380
+ // ---------------------------------------------------------------------------
381
+ // Error hierarchy
382
+ // ---------------------------------------------------------------------------
383
+ export class ResolveError extends Error {
384
+ constructor(message) {
385
+ super(message);
386
+ this.name = "ResolveError";
387
+ Object.setPrototypeOf(this, new.target.prototype);
388
+ }
389
+ }
390
+ export class ResolveNotFoundError extends ResolveError {
391
+ constructor(message) {
392
+ super(message);
393
+ this.name = "ResolveNotFoundError";
394
+ }
395
+ }
396
+ export class ResolveAuthError extends ResolveError {
397
+ constructor(message) {
398
+ super(message);
399
+ this.name = "ResolveAuthError";
400
+ }
401
+ }
402
+ export class ResolveNetworkError extends ResolveError {
403
+ constructor(message) {
404
+ super(message);
405
+ this.name = "ResolveNetworkError";
406
+ }
407
+ }
408
+ export const SD = {
409
+ bundle(container, marker, bodyAs = "text", bodyField = "instruction") {
410
+ return { container, pattern: "bundle", marker, bodyAs, bodyField };
411
+ },
412
+ yaml(container) {
413
+ return { container, pattern: "yaml" };
414
+ },
415
+ root(filename = "manifest.yaml") {
416
+ return { container: "", pattern: "root", marker: filename };
417
+ },
418
+ standalone(filename, bodyAs = "text", bodyField = "content") {
419
+ return { container: "", pattern: "standalone", marker: filename, bodyAs, bodyField };
420
+ },
421
+ };
422
+ export function defaultVisibleInBackend(storage) {
423
+ if (!storage)
424
+ return false;
425
+ if (storage.pattern === "bundle" || storage.pattern === "standalone")
426
+ return true;
427
+ return false;
428
+ }
429
+ export function resolveVisibleInBackend(kp) {
430
+ // `visibleInBackend` is a declared optional member of the
431
+ // KindPresentation slice (s-dna-kindport-descriptor-schema) — typed
432
+ // access, no `as any`. Unset/null falls back to the storage default.
433
+ const explicit = kp.visibleInBackend;
434
+ if (explicit !== undefined && explicit !== null)
435
+ return Boolean(explicit);
436
+ return defaultVisibleInBackend(kp.storage ?? null);
437
+ }
438
+ /**
439
+ * Phase 16 — Kinds the kernel needs registered/parsed BEFORE
440
+ * ``loadAll`` fires. Order is meaningful: KindDefinition first
441
+ * (custom Kinds need to be registered before parsing other docs),
442
+ * LayerPolicy next (kernel reads at write-time), Genome last
443
+ * (root identity).
444
+ */
445
+ export const BOOTSTRAP_KIND_NAMES = ["KindDefinition", "LayerPolicy", "Genome"];
446
+ /**
447
+ * Phase 16 helper — return the Genome doc for ``scope`` (or ``null``
448
+ * if missing). Pulls bootstrap docs and filters for the Genome Kind.
449
+ * Tenant-aware: when ``opts.tenant`` is set, the underlying adapter
450
+ * applies tenant-overlay routing (tenant-published Genome shadows
451
+ * platform).
452
+ *
453
+ * 1:1 parity with Python ``package_doc_for_scope``.
454
+ */
455
+ export async function packageDocForScope(source, scope, opts) {
456
+ const bootstrap = await source.loadBootstrapDocs(scope, opts);
457
+ for (const d of bootstrap) {
458
+ if (d.kind === "Genome")
459
+ return d;
460
+ }
461
+ return null;
462
+ }
463
+ /**
464
+ * Concrete ToolPort implementation, stored in the kernel's ToolRegistry.
465
+ * Twin of the Py `ToolDefinition` dataclass — the `callable` init field
466
+ * holds the framework-native invocable; `getCallable()` is the canonical
467
+ * accessor (never serialize or wrap-replace it).
468
+ */
469
+ export class ToolDefinition {
470
+ name;
471
+ group;
472
+ description;
473
+ summary;
474
+ argsSchema;
475
+ hitl;
476
+ scope;
477
+ source;
478
+ _callable;
479
+ constructor(init) {
480
+ this.name = init.name;
481
+ this.group = init.group ?? null;
482
+ this.description = init.description ?? "";
483
+ this.summary = init.summary ?? "";
484
+ this.argsSchema = init.argsSchema ?? {};
485
+ this.hitl = init.hitl ?? false;
486
+ this.scope = init.scope ?? null;
487
+ this.source = init.source ?? "";
488
+ this._callable = init.callable ?? null;
489
+ }
490
+ getCallable() {
491
+ return this._callable;
492
+ }
493
+ }
@@ -0,0 +1,26 @@
1
+ /**
2
+ * ReportBuilder — structured markdown reports for eval, findings, evidence, compliance.
3
+ *
4
+ * Accessed via `mi.reports.evalSummary()`, `mi.reports.findingsSummary()`,
5
+ * `mi.reports.evidenceManifest()`, `mi.reports.complianceMatrix(framework)`.
6
+ *
7
+ * Each report returns Markdown with YAML frontmatter.
8
+ *
9
+ * 1:1 parity with Python dna.kernel.reports.
10
+ */
11
+ import type { ManifestInstance } from "./instance.js";
12
+ export declare class ReportBuilder {
13
+ private readonly _mi;
14
+ constructor(mi: ManifestInstance);
15
+ /** Generate an eval summary report grouped by suite.
16
+ * If `suite` is given, only that suite is included. */
17
+ evalSummary(suite?: string): string;
18
+ /** Generate a findings summary grouped by severity.
19
+ * Filters out findings with severity below `minSeverity`. */
20
+ findingsSummary(minSeverity?: string): string;
21
+ /** Generate a manifest of all evidence documents. */
22
+ evidenceManifest(): string;
23
+ /** Generate a compliance matrix mapping findings to regulatory articles.
24
+ * Supported frameworks: lgpd, gdpr, nist_ai_rmf. */
25
+ complianceMatrix(framework: string): string;
26
+ }