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,135 @@
1
+ /**
2
+ * StudioUIMetadata — UI declarations attached to a KindPort.
3
+ *
4
+ * Net-new TS twin of Python `dna.kernel.studio_ui.StudioUIMetadata`
5
+ * (studio_ui.py:54-215), added for the descriptor-expressiveness `ui:` field
6
+ * (spec 2026-06-11 D1). Reverses the previously-documented posture that `ui`
7
+ * was "a Studio-backend concern… intentionally not mirrored"
8
+ * (src/extensions/audit.ts) — byte-parity of `.kind.yaml` descriptors across
9
+ * runtimes requires BOTH the Python and the TS DeclarativeKindPort to parse the
10
+ * same `ui:` block into the same data and serialize it identically.
11
+ *
12
+ * Field-for-field mirror + `toDict()` (same omission rules as Python
13
+ * `to_dict()`) + `resolveLabel()` (same i18n fallback order as Python
14
+ * `resolve_label()`). There is no TS class golden for `ui` (TS Kind classes
15
+ * never carried it); the parity pin is structural — `toDict()` parity with
16
+ * Python via the shared fixture tests/fixtures/studio-ui-parity.json.
17
+ */
18
+ /**
19
+ * Canonical, ordered set of StudioUIMetadata field names. Single source of
20
+ * truth — `KindDefinitionSpec.from_raw`/zod validate `ui:` keys against THIS
21
+ * list (no second hardcoded list). Mirrors the Python dataclass field order.
22
+ */
23
+ export const UI_METADATA_FIELDS = [
24
+ "mode",
25
+ "in_sidebar",
26
+ "display_order",
27
+ "label",
28
+ "icon",
29
+ "description",
30
+ "breadcrumb",
31
+ "routes",
32
+ "permissions",
33
+ "note",
34
+ "feature_flag",
35
+ ];
36
+ /**
37
+ * UI declarations for a Kind, consumed by Studio's manifest. Defaults are
38
+ * conservative: no mode, no sidebar, no routes — the Kind is invisible in
39
+ * Studio unless explicitly opted in. 1:1 with Python `StudioUIMetadata`.
40
+ */
41
+ export class StudioUIMetadata {
42
+ mode;
43
+ in_sidebar;
44
+ display_order;
45
+ label;
46
+ icon;
47
+ description;
48
+ breadcrumb;
49
+ routes;
50
+ permissions;
51
+ note;
52
+ feature_flag;
53
+ constructor(init = {}) {
54
+ this.mode = init.mode ?? null;
55
+ this.in_sidebar = init.in_sidebar ?? false;
56
+ this.display_order = init.display_order ?? 100;
57
+ this.label = init.label ?? null;
58
+ this.icon = init.icon ?? null;
59
+ this.description = init.description ?? null;
60
+ this.breadcrumb = init.breadcrumb ?? null;
61
+ this.routes = init.routes ?? {};
62
+ this.permissions = init.permissions ?? {};
63
+ this.note = init.note ?? null;
64
+ this.feature_flag = init.feature_flag ?? null;
65
+ }
66
+ /** The canonical field-name set (single source of truth for `ui:` validation). */
67
+ static fields() {
68
+ return UI_METADATA_FIELDS;
69
+ }
70
+ /**
71
+ * Serialize for the /kinds/manifest JSON response. Omits None/empty fields
72
+ * so the wire payload stays small — EXACTLY the Python `to_dict()` omission
73
+ * rules (studio_ui.py:161-192):
74
+ * - mode: only if not null
75
+ * - in_sidebar: only if true
76
+ * - display_order: only if !== 100
77
+ * - label/icon/description/note/feature_flag: only if not null
78
+ * - breadcrumb: only if not null (an empty list IS kept, like Python)
79
+ * - routes/permissions: only if non-empty
80
+ */
81
+ toDict() {
82
+ const out = {};
83
+ if (this.mode !== null)
84
+ out.mode = this.mode;
85
+ if (this.in_sidebar)
86
+ out.in_sidebar = true;
87
+ if (this.display_order !== 100)
88
+ out.display_order = this.display_order;
89
+ if (this.label !== null)
90
+ out.label = this.label;
91
+ if (this.icon !== null)
92
+ out.icon = this.icon;
93
+ if (this.description !== null)
94
+ out.description = this.description;
95
+ if (this.breadcrumb !== null)
96
+ out.breadcrumb = [...this.breadcrumb];
97
+ if (Object.keys(this.routes).length > 0)
98
+ out.routes = { ...this.routes };
99
+ if (Object.keys(this.permissions).length > 0) {
100
+ const perms = {};
101
+ for (const [k, v] of Object.entries(this.permissions)) {
102
+ perms[k] = Array.isArray(v) ? [...v] : v;
103
+ }
104
+ out.permissions = perms;
105
+ }
106
+ if (this.note !== null)
107
+ out.note = this.note;
108
+ if (this.feature_flag !== null)
109
+ out.feature_flag = this.feature_flag;
110
+ return out;
111
+ }
112
+ /**
113
+ * Resolve i18n label for the requested locale. Fallback order (mirrors
114
+ * Python `resolve_label`):
115
+ * 1. Exact locale match (e.g. 'pt-BR').
116
+ * 2. Language-only fallback (e.g. 'pt' from 'pt-BR').
117
+ * 3. 'en' (project default).
118
+ * 4. null.
119
+ * A plain-string label is returned as-is regardless of locale.
120
+ */
121
+ resolveLabel(locale = "en") {
122
+ if (this.label === null)
123
+ return null;
124
+ if (typeof this.label === "string")
125
+ return this.label;
126
+ if (locale in this.label)
127
+ return this.label[locale];
128
+ if (locale.includes("-")) {
129
+ const base = locale.split("-", 1)[0];
130
+ if (base in this.label)
131
+ return this.label[base];
132
+ }
133
+ return this.label.en ?? null;
134
+ }
135
+ }
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Template contract — file-tree scaffolds shipped by Extensions.
3
+ *
4
+ * Templates let an Extension declare reusable scaffolds for its Kinds.
5
+ * Files live under an absolute path resolved by the Extension (e.g. via
6
+ * `import.meta.url`), so they survive bundling and editable installs.
7
+ * Kernel exposes `listTemplates()` + `scaffold()` to discover and
8
+ * materialize them.
9
+ *
10
+ * 1:1 parity with `dna.kernel.templates` (Python). Field
11
+ * naming is camelCased per this codebase's TS convention:
12
+ * files_root -> filesRoot
13
+ * owner_extension -> ownerExtension
14
+ * post_init_hint -> postInitHint
15
+ * upstream_ref -> upstreamRef
16
+ * on_conflict -> onConflict
17
+ */
18
+ /** Conflict policy when a destination file already exists. */
19
+ export type OnConflict = "error" | "skip" | "overwrite";
20
+ /**
21
+ * A scaffoldable file tree declared by an Extension.
22
+ *
23
+ * - `id` Namespaced identifier: `<extension>/<name>`.
24
+ * - `label` Human-friendly name shown in UIs.
25
+ * - `kind` Primary Kind this template scaffolds (may span
26
+ * multiple kinds in the file tree, but this is the
27
+ * headline one for filtering/grouping).
28
+ * - `description` One-line description.
29
+ * - `filesRoot` Absolute path to the root of the template tree on
30
+ * disk (typically resolved via `fileURLToPath` from
31
+ * the Extension's `import.meta.url`).
32
+ * - `ownerExtension` Name of the Extension that owns this template.
33
+ * - `postInitHint` Optional shell/cli snippet shown after scaffold
34
+ * (e.g. "cd .dna/<scope>/programs/research && bun
35
+ * install").
36
+ * - `upstreamRef` Optional upstream pin (e.g. a git sha of the
37
+ * source repo the template was cloned from).
38
+ */
39
+ export interface Template {
40
+ readonly id: string;
41
+ readonly label: string;
42
+ readonly kind: string;
43
+ readonly description: string;
44
+ readonly filesRoot: string;
45
+ readonly ownerExtension: string;
46
+ readonly postInitHint?: string;
47
+ readonly upstreamRef?: string;
48
+ }
49
+ /** Options accepted by {@link materialize}. */
50
+ export interface MaterializeOptions {
51
+ /** Absolute path where files will be written (created if missing). */
52
+ readonly targetRoot: string;
53
+ /** Conflict policy. Defaults to `"error"`. */
54
+ readonly onConflict?: OnConflict;
55
+ }
56
+ /**
57
+ * Copy every file under `template.filesRoot` into `opts.targetRoot`.
58
+ *
59
+ * Returns the list of written absolute paths. Binary-safe — files are
60
+ * read and written as raw `Buffer` bytes. Preserves relative directory
61
+ * structure.
62
+ *
63
+ * `onConflict`:
64
+ * - `"error"` (default): throw on any existing dest file
65
+ * - `"skip"`: leave existing dest files untouched
66
+ * - `"overwrite"`: replace existing dest files
67
+ *
68
+ * Throws:
69
+ * - `Error("unknown onConflict: ...")` on invalid policy value
70
+ * (runtime validation — TypeScript can catch most at compile time,
71
+ * but this mirrors Python's `ValueError` for defensive callers)
72
+ * - `Error("filesRoot does not exist: ...")` if the source tree is
73
+ * missing
74
+ * - `Error("destination exists: ...")` when `onConflict="error"` and
75
+ * the destination has a conflicting file
76
+ */
77
+ export declare function materialize(template: Template, opts: MaterializeOptions): string[];
@@ -0,0 +1,116 @@
1
+ /**
2
+ * Template contract — file-tree scaffolds shipped by Extensions.
3
+ *
4
+ * Templates let an Extension declare reusable scaffolds for its Kinds.
5
+ * Files live under an absolute path resolved by the Extension (e.g. via
6
+ * `import.meta.url`), so they survive bundling and editable installs.
7
+ * Kernel exposes `listTemplates()` + `scaffold()` to discover and
8
+ * materialize them.
9
+ *
10
+ * 1:1 parity with `dna.kernel.templates` (Python). Field
11
+ * naming is camelCased per this codebase's TS convention:
12
+ * files_root -> filesRoot
13
+ * owner_extension -> ownerExtension
14
+ * post_init_hint -> postInitHint
15
+ * upstream_ref -> upstreamRef
16
+ * on_conflict -> onConflict
17
+ */
18
+ import { readdirSync, readFileSync, statSync, writeFileSync, mkdirSync } from "node:fs";
19
+ import { dirname, join, relative, sep } from "node:path";
20
+ const VALID_ON_CONFLICT = new Set([
21
+ "error",
22
+ "skip",
23
+ "overwrite",
24
+ ]);
25
+ /** Walk `root` recursively and return the absolute paths of every regular
26
+ * file, sorted lexicographically for determinism (mirrors Python's
27
+ * `sorted(rglob("*"))`). */
28
+ function walkFiles(root) {
29
+ const out = [];
30
+ function visit(dir) {
31
+ const entries = readdirSync(dir).sort();
32
+ for (const name of entries) {
33
+ const full = join(dir, name);
34
+ const st = statSync(full);
35
+ if (st.isDirectory()) {
36
+ visit(full);
37
+ }
38
+ else if (st.isFile()) {
39
+ out.push(full);
40
+ }
41
+ // symlinks / sockets / etc. are ignored — same as Python's is_file()
42
+ }
43
+ }
44
+ visit(root);
45
+ out.sort();
46
+ return out;
47
+ }
48
+ function isDirectory(path) {
49
+ try {
50
+ return statSync(path).isDirectory();
51
+ }
52
+ catch {
53
+ return false;
54
+ }
55
+ }
56
+ function exists(path) {
57
+ try {
58
+ statSync(path);
59
+ return true;
60
+ }
61
+ catch {
62
+ return false;
63
+ }
64
+ }
65
+ /**
66
+ * Copy every file under `template.filesRoot` into `opts.targetRoot`.
67
+ *
68
+ * Returns the list of written absolute paths. Binary-safe — files are
69
+ * read and written as raw `Buffer` bytes. Preserves relative directory
70
+ * structure.
71
+ *
72
+ * `onConflict`:
73
+ * - `"error"` (default): throw on any existing dest file
74
+ * - `"skip"`: leave existing dest files untouched
75
+ * - `"overwrite"`: replace existing dest files
76
+ *
77
+ * Throws:
78
+ * - `Error("unknown onConflict: ...")` on invalid policy value
79
+ * (runtime validation — TypeScript can catch most at compile time,
80
+ * but this mirrors Python's `ValueError` for defensive callers)
81
+ * - `Error("filesRoot does not exist: ...")` if the source tree is
82
+ * missing
83
+ * - `Error("destination exists: ...")` when `onConflict="error"` and
84
+ * the destination has a conflicting file
85
+ */
86
+ export function materialize(template, opts) {
87
+ const onConflict = opts.onConflict ?? "error";
88
+ if (!VALID_ON_CONFLICT.has(onConflict)) {
89
+ throw new Error(`unknown onConflict: ${JSON.stringify(onConflict)}`);
90
+ }
91
+ if (!isDirectory(template.filesRoot)) {
92
+ throw new Error(`filesRoot does not exist: ${template.filesRoot}`);
93
+ }
94
+ mkdirSync(opts.targetRoot, { recursive: true });
95
+ const written = [];
96
+ for (const src of walkFiles(template.filesRoot)) {
97
+ const rel = relative(template.filesRoot, src);
98
+ // normalise path separators defensively (relative already returns
99
+ // platform-native separators; join below is platform-correct).
100
+ const dst = join(opts.targetRoot, ...rel.split(sep));
101
+ if (exists(dst)) {
102
+ if (onConflict === "error") {
103
+ throw new Error(`destination exists: ${dst}`);
104
+ }
105
+ if (onConflict === "skip") {
106
+ continue;
107
+ }
108
+ // "overwrite" → fall through
109
+ }
110
+ mkdirSync(dirname(dst), { recursive: true });
111
+ const buf = readFileSync(src); // Buffer — no encoding = binary-safe
112
+ writeFileSync(dst, buf);
113
+ written.push(dst);
114
+ }
115
+ return written;
116
+ }
@@ -0,0 +1,52 @@
1
+ /**
2
+ * ToolRegistry — the kernel's tool-definition registry. TS twin of the Py
3
+ * `dna.kernel.tool_registry.ToolRegistry` + the portable half of
4
+ * `tools.py` (READ_UMBRELLA_GROUPS / expand_group_aliases). Landed with
5
+ * s-dna-port-surface-parity (closes the "TS kernel has no tool registry"
6
+ * gap the ExtensionHost contract had to footnote).
7
+ *
8
+ * The Py `@dna_tool` decorator (langchain glue that harvests
9
+ * ToolDefinitions at function-definition time) is a JUSTIFIED Py-only
10
+ * asymmetry — see tests/parity-fixtures/port-surface-parity.json. TS
11
+ * registrants construct `ToolDefinition`s directly and call
12
+ * `kernel.tool(td)` inside `Extension.register()`.
13
+ *
14
+ * Tools are global (not tenant-scoped), so one registry is safely shared
15
+ * across `withTenant` shallow copies — same posture as the Py twin.
16
+ */
17
+ import type { ToolDefinition } from "./protocols.js";
18
+ /**
19
+ * 'read' umbrella group — not a real group on any tool; an alias that
20
+ * expands to {code, manifest, docs, eval} at filter time. 1:1 with the Py
21
+ * `READ_UMBRELLA_GROUPS`.
22
+ */
23
+ export declare const READ_UMBRELLA_GROUPS: ReadonlySet<string>;
24
+ /**
25
+ * Expand the 'read' umbrella into its constituent groups. Other group
26
+ * names pass through unchanged. 1:1 with the Py `expand_group_aliases`.
27
+ */
28
+ export declare function expandGroupAliases(groups?: Iterable<string> | null): Set<string>;
29
+ /** Name → ToolDefinition registry with group-aware filtering. */
30
+ export declare class ToolRegistry {
31
+ private readonly _tools;
32
+ /** Register a tool definition. Last-write-wins on same name (idempotent —
33
+ * factory-pattern registrants may re-register on every factory call). */
34
+ register(td: ToolDefinition): void;
35
+ /** Return a tool definition by name, or `null` if unknown. */
36
+ get(name: string): ToolDefinition | null;
37
+ /**
38
+ * Return registered tool definitions, optionally filtered.
39
+ *
40
+ * - `{ group: "cognitive" }` — exactly that group
41
+ * - `{ groups: ["cognitive", "manifest"] }` — union of groups
42
+ * - `{ groups: ["read"] }` — expands via the 'read' umbrella alias
43
+ *
44
+ * Pass nothing to get the full catalog. 1:1 with the Py `get_many`.
45
+ */
46
+ getMany(opts?: {
47
+ group?: string | null;
48
+ groups?: Iterable<string> | null;
49
+ }): ToolDefinition[];
50
+ /** Reverse-build `{group: [toolNames…]}` (names sorted) from the registry. */
51
+ groups(): Record<string, string[]>;
52
+ }
@@ -0,0 +1,89 @@
1
+ /**
2
+ * ToolRegistry — the kernel's tool-definition registry. TS twin of the Py
3
+ * `dna.kernel.tool_registry.ToolRegistry` + the portable half of
4
+ * `tools.py` (READ_UMBRELLA_GROUPS / expand_group_aliases). Landed with
5
+ * s-dna-port-surface-parity (closes the "TS kernel has no tool registry"
6
+ * gap the ExtensionHost contract had to footnote).
7
+ *
8
+ * The Py `@dna_tool` decorator (langchain glue that harvests
9
+ * ToolDefinitions at function-definition time) is a JUSTIFIED Py-only
10
+ * asymmetry — see tests/parity-fixtures/port-surface-parity.json. TS
11
+ * registrants construct `ToolDefinition`s directly and call
12
+ * `kernel.tool(td)` inside `Extension.register()`.
13
+ *
14
+ * Tools are global (not tenant-scoped), so one registry is safely shared
15
+ * across `withTenant` shallow copies — same posture as the Py twin.
16
+ */
17
+ /**
18
+ * 'read' umbrella group — not a real group on any tool; an alias that
19
+ * expands to {code, manifest, docs, eval} at filter time. 1:1 with the Py
20
+ * `READ_UMBRELLA_GROUPS`.
21
+ */
22
+ export const READ_UMBRELLA_GROUPS = new Set([
23
+ "code", "manifest", "docs", "eval",
24
+ ]);
25
+ /**
26
+ * Expand the 'read' umbrella into its constituent groups. Other group
27
+ * names pass through unchanged. 1:1 with the Py `expand_group_aliases`.
28
+ */
29
+ export function expandGroupAliases(groups) {
30
+ const out = new Set();
31
+ if (!groups)
32
+ return out;
33
+ for (const g of groups) {
34
+ if (g === "read") {
35
+ for (const u of READ_UMBRELLA_GROUPS)
36
+ out.add(u);
37
+ }
38
+ else {
39
+ out.add(g);
40
+ }
41
+ }
42
+ return out;
43
+ }
44
+ /** Name → ToolDefinition registry with group-aware filtering. */
45
+ export class ToolRegistry {
46
+ _tools = new Map();
47
+ /** Register a tool definition. Last-write-wins on same name (idempotent —
48
+ * factory-pattern registrants may re-register on every factory call). */
49
+ register(td) {
50
+ this._tools.set(td.name, td);
51
+ }
52
+ /** Return a tool definition by name, or `null` if unknown. */
53
+ get(name) {
54
+ return this._tools.get(name) ?? null;
55
+ }
56
+ /**
57
+ * Return registered tool definitions, optionally filtered.
58
+ *
59
+ * - `{ group: "cognitive" }` — exactly that group
60
+ * - `{ groups: ["cognitive", "manifest"] }` — union of groups
61
+ * - `{ groups: ["read"] }` — expands via the 'read' umbrella alias
62
+ *
63
+ * Pass nothing to get the full catalog. 1:1 with the Py `get_many`.
64
+ */
65
+ getMany(opts = {}) {
66
+ const group = opts.group ?? null;
67
+ const extra = opts.groups ? [...opts.groups] : [];
68
+ if (group === null && extra.length === 0) {
69
+ return [...this._tools.values()];
70
+ }
71
+ const wanted = expandGroupAliases([
72
+ ...(group !== null ? [group] : []),
73
+ ...extra,
74
+ ]);
75
+ return [...this._tools.values()].filter((td) => td.group !== null && wanted.has(td.group));
76
+ }
77
+ /** Reverse-build `{group: [toolNames…]}` (names sorted) from the registry. */
78
+ groups() {
79
+ const out = {};
80
+ for (const td of this._tools.values()) {
81
+ if (!td.group)
82
+ continue;
83
+ (out[td.group] ??= []).push(td.name);
84
+ }
85
+ for (const names of Object.values(out))
86
+ names.sort();
87
+ return out;
88
+ }
89
+ }
@@ -0,0 +1,65 @@
1
+ import type { WriteHost } from "./collaborator-ports.js";
2
+ export declare class WritePipeline {
3
+ private readonly _k;
4
+ constructor(kernel: WriteHost);
5
+ /** Read the write-validation mode knob. `enforce` (default) vetoes an
6
+ * invalid write; `warn` logs and persists; `off` skips the step. Read
7
+ * per-write (not memoized) so tests / operators can flip it live. */
8
+ private static _validationMode;
9
+ /**
10
+ * Validate `raw.spec` against the Kind's declared JSON Schema at WRITE
11
+ * time (s-write-path-validation, i-008 — the systemic gap found on the
12
+ * Automation work: the kernel only schema-validated at scan/read via the
13
+ * fail-soft `parse_error` channel, so a shape-broken doc persisted and
14
+ * exploded later, far from the author). Twin of Python
15
+ * `WritePipeline._validate_spec_schema`.
16
+ *
17
+ * Contract:
18
+ * - Kinds without a schema (`schema()` null/empty, or throwing) stay
19
+ * PERMISSIVE — validation is opt-in by data, as always.
20
+ * - `spec_defaults` (descriptor D5) are shallow-merged into the spec
21
+ * BEFORE validating, mirroring `DeclarativeKindPort.parse`.
22
+ * - Runs AFTER the `pre_save` veto hooks (Kind-owned cures mutate
23
+ * ctx.raw first), BEFORE persistence.
24
+ * - Didactic failure (install #26 pattern): names the field, the
25
+ * violation, and points at `dna kind show <Kind>`.
26
+ */
27
+ private _validateSpecSchema;
28
+ /**
29
+ * Reconcile tenant + layer args + Kernel.tenant + KindPort.scope.
30
+ * Returns `[effectiveTenant, residualLayer]`. Back-compat:
31
+ * `layer=("tenant", X)` is rewritten to `tenant=X` with a deprecation
32
+ * console.warn. Validation: TENANTED kind requires a tenant; GLOBAL kind
33
+ * forbids it. Moved verbatim from `Kernel._resolveTenantArg`.
34
+ */
35
+ resolveTenantArg(kind: string, tenant: string | null | undefined, layer: [string, string] | undefined): [string | null, [string, string] | undefined];
36
+ /** Real writeDocument body — the facade (`Kernel.writeDocument`) is a thin
37
+ * delegator. Persists through the writable source with the `pre_save` veto
38
+ * gate + `post_save` emission. */
39
+ write(scope: string, kind: string, name: string, raw: Record<string, unknown>, options?: {
40
+ skipHooks?: boolean;
41
+ author?: string;
42
+ tenant?: string | null;
43
+ layer?: [string, string];
44
+ }): Promise<string>;
45
+ /** Real deleteDocument body — the facade is a thin delegator. NOTE: deletes
46
+ * have NO pre_save veto gate (only writes do). */
47
+ delete(scope: string, kind: string, name: string, options?: {
48
+ skipHooks?: boolean;
49
+ author?: string;
50
+ tenant?: string | null;
51
+ layer?: [string, string];
52
+ }): Promise<void>;
53
+ /** Emit the post_save hook (shared by the new and legacy write paths).
54
+ * Async so async listeners (event bus publishers etc.) can await I/O. */
55
+ private emitPostSave;
56
+ /** Emit the post_delete hook (only from the new delegation path). */
57
+ private emitPostDelete;
58
+ /** Validate a SINGLE Kind-Writer target's slot↔schema contract. Moved from
59
+ * `Kernel._validateOneKindWriterEntry`. */
60
+ validateOneKindWriterEntry(target: string, creativeSlots: string[], systemSlots: Record<string, unknown>): void;
61
+ /** Validate a Kind-Writer Agent's slot↔schema contract. Fired by the
62
+ * Helix extension's Kind-Writer `pre_save` guard via the
63
+ * `kernel._validateKindWriter` shim. Moved from `Kernel._validateKindWriter`. */
64
+ validateKindWriter(spec: Record<string, unknown>): void;
65
+ }