dna-sdk 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +58 -0
- package/dist/adapters/embedding/onnx.d.ts +32 -0
- package/dist/adapters/embedding/onnx.js +51 -0
- package/dist/adapters/filesystem/cache.d.ts +16 -0
- package/dist/adapters/filesystem/cache.js +113 -0
- package/dist/adapters/filesystem/index.d.ts +2 -0
- package/dist/adapters/filesystem/index.js +2 -0
- package/dist/adapters/filesystem/source.d.ts +82 -0
- package/dist/adapters/filesystem/source.js +357 -0
- package/dist/adapters/postgres/index.d.ts +2 -0
- package/dist/adapters/postgres/index.js +2 -0
- package/dist/adapters/postgres/migrations.d.ts +14 -0
- package/dist/adapters/postgres/migrations.js +88 -0
- package/dist/adapters/postgres/source.d.ts +172 -0
- package/dist/adapters/postgres/source.js +569 -0
- package/dist/adapters/resolvers/github.d.ts +12 -0
- package/dist/adapters/resolvers/github.js +47 -0
- package/dist/adapters/resolvers/http.d.ts +22 -0
- package/dist/adapters/resolvers/http.js +145 -0
- package/dist/adapters/resolvers/index.d.ts +3 -0
- package/dist/adapters/resolvers/index.js +3 -0
- package/dist/adapters/resolvers/local.d.ts +24 -0
- package/dist/adapters/resolvers/local.js +132 -0
- package/dist/adapters/search/driver.d.ts +45 -0
- package/dist/adapters/search/driver.js +138 -0
- package/dist/adapters/search/migrations.d.ts +26 -0
- package/dist/adapters/search/migrations.js +58 -0
- package/dist/adapters/search/rrf.d.ts +23 -0
- package/dist/adapters/search/rrf.js +42 -0
- package/dist/adapters/search/sqlite-vec.d.ts +66 -0
- package/dist/adapters/search/sqlite-vec.js +294 -0
- package/dist/bootstrap.d.ts +45 -0
- package/dist/bootstrap.js +134 -0
- package/dist/extensions/agentskills/DOCS.md +18 -0
- package/dist/extensions/agentskills.d.ts +28 -0
- package/dist/extensions/agentskills.js +252 -0
- package/dist/extensions/agentsmd/DOCS.md +18 -0
- package/dist/extensions/agentsmd.d.ts +22 -0
- package/dist/extensions/agentsmd.js +170 -0
- package/dist/extensions/audit/kinds/audit-log.kind.yaml +148 -0
- package/dist/extensions/audit.d.ts +25 -0
- package/dist/extensions/audit.js +62 -0
- package/dist/extensions/automation/kinds/automation.kind.yaml +296 -0
- package/dist/extensions/automation/query.d.ts +49 -0
- package/dist/extensions/automation/query.js +77 -0
- package/dist/extensions/automation/write-guards.d.ts +59 -0
- package/dist/extensions/automation/write-guards.js +153 -0
- package/dist/extensions/automation.d.ts +41 -0
- package/dist/extensions/automation.js +54 -0
- package/dist/extensions/collab.d.ts +16 -0
- package/dist/extensions/collab.js +79 -0
- package/dist/extensions/doc/kinds/doc.kind.yaml +118 -0
- package/dist/extensions/doc.d.ts +25 -0
- package/dist/extensions/doc.js +10 -0
- package/dist/extensions/eval/kinds/eval-baseline.kind.yaml +65 -0
- package/dist/extensions/eval/kinds/eval-case.kind.yaml +140 -0
- package/dist/extensions/eval/kinds/eval-run.kind.yaml +134 -0
- package/dist/extensions/eval/kinds/eval-suite.kind.yaml +87 -0
- package/dist/extensions/eval.d.ts +24 -0
- package/dist/extensions/eval.js +31 -0
- package/dist/extensions/evidence/kinds/evidence.kind.yaml +92 -0
- package/dist/extensions/evidence.d.ts +17 -0
- package/dist/extensions/evidence.js +94 -0
- package/dist/extensions/federation.d.ts +22 -0
- package/dist/extensions/federation.js +173 -0
- package/dist/extensions/guardrails/DOCS.md +20 -0
- package/dist/extensions/guardrails.d.ts +31 -0
- package/dist/extensions/guardrails.js +228 -0
- package/dist/extensions/helix/DOCS-Actor.md +20 -0
- package/dist/extensions/helix/DOCS-Agent.md +20 -0
- package/dist/extensions/helix/DOCS-UseCase.md +20 -0
- package/dist/extensions/helix/layers.d.ts +12 -0
- package/dist/extensions/helix/layers.js +18 -0
- package/dist/extensions/helix/write-guards.d.ts +70 -0
- package/dist/extensions/helix/write-guards.js +172 -0
- package/dist/extensions/helix.d.ts +29 -0
- package/dist/extensions/helix.js +963 -0
- package/dist/extensions/helix_extras.d.ts +124 -0
- package/dist/extensions/helix_extras.js +328 -0
- package/dist/extensions/hooks.d.ts +31 -0
- package/dist/extensions/hooks.js +247 -0
- package/dist/extensions/kinddef/DOCS.md +24 -0
- package/dist/extensions/kinddef.d.ts +40 -0
- package/dist/extensions/kinddef.js +171 -0
- package/dist/extensions/lesson.d.ts +6 -0
- package/dist/extensions/lesson.js +194 -0
- package/dist/extensions/modelreg/kinds/model-profile.kind.yaml +155 -0
- package/dist/extensions/modelreg.d.ts +30 -0
- package/dist/extensions/modelreg.js +38 -0
- package/dist/extensions/recognizer.d.ts +34 -0
- package/dist/extensions/recognizer.js +272 -0
- package/dist/extensions/research.d.ts +32 -0
- package/dist/extensions/research.js +292 -0
- package/dist/extensions/safety.d.ts +33 -0
- package/dist/extensions/safety.js +230 -0
- package/dist/extensions/sdlc/kinds/adr.kind.yaml +136 -0
- package/dist/extensions/sdlc/kinds/archive-proposal.kind.yaml +107 -0
- package/dist/extensions/sdlc/kinds/changelog.kind.yaml +108 -0
- package/dist/extensions/sdlc/kinds/cognitive-policy.kind.yaml +470 -0
- package/dist/extensions/sdlc/kinds/forecast.kind.yaml +125 -0
- package/dist/extensions/sdlc/kinds/insight.kind.yaml +87 -0
- package/dist/extensions/sdlc/kinds/kaizen.kind.yaml +106 -0
- package/dist/extensions/sdlc/kinds/lesson-learned.kind.yaml +270 -0
- package/dist/extensions/sdlc/kinds/narrative.kind.yaml +189 -0
- package/dist/extensions/sdlc/kinds/pattern-insight.kind.yaml +204 -0
- package/dist/extensions/sdlc/kinds/postmortem.kind.yaml +168 -0
- package/dist/extensions/sdlc/kinds/pre-mortem.kind.yaml +145 -0
- package/dist/extensions/sdlc/kinds/prompt-template.kind.yaml +109 -0
- package/dist/extensions/sdlc/kinds/retrospective.kind.yaml +186 -0
- package/dist/extensions/sdlc/kinds/risk-register.kind.yaml +178 -0
- package/dist/extensions/sdlc/kinds/saved-view.kind.yaml +127 -0
- package/dist/extensions/sdlc/kinds/status-report.kind.yaml +129 -0
- package/dist/extensions/sdlc/kinds/synthesis-run.kind.yaml +279 -0
- package/dist/extensions/sdlc/kinds/synthesizer-state.kind.yaml +196 -0
- package/dist/extensions/sdlc/kinds/workflow-event.kind.yaml +195 -0
- package/dist/extensions/sdlc.d.ts +32 -0
- package/dist/extensions/sdlc.js +1318 -0
- package/dist/extensions/soulspec/DOCS.md +19 -0
- package/dist/extensions/soulspec.d.ts +22 -0
- package/dist/extensions/soulspec.js +287 -0
- package/dist/extensions/tenant.d.ts +6 -0
- package/dist/extensions/tenant.js +291 -0
- package/dist/extensions/testkit.d.ts +24 -0
- package/dist/extensions/testkit.js +211 -0
- package/dist/index.d.ts +80 -0
- package/dist/index.js +102 -0
- package/dist/kernel/_text.d.ts +28 -0
- package/dist/kernel/_text.js +42 -0
- package/dist/kernel/bundle-handle.d.ts +83 -0
- package/dist/kernel/bundle-handle.js +184 -0
- package/dist/kernel/capabilities.d.ts +157 -0
- package/dist/kernel/capabilities.js +109 -0
- package/dist/kernel/collaborator-ports.d.ts +91 -0
- package/dist/kernel/collaborator-ports.js +1 -0
- package/dist/kernel/composition-resolver.d.ts +215 -0
- package/dist/kernel/composition-resolver.js +558 -0
- package/dist/kernel/composition.d.ts +10 -0
- package/dist/kernel/composition.js +1 -0
- package/dist/kernel/descriptor-loader.d.ts +13 -0
- package/dist/kernel/descriptor-loader.js +48 -0
- package/dist/kernel/document.d.ts +43 -0
- package/dist/kernel/document.js +71 -0
- package/dist/kernel/embedding.d.ts +28 -0
- package/dist/kernel/embedding.js +78 -0
- package/dist/kernel/errors.d.ts +62 -0
- package/dist/kernel/errors.js +82 -0
- package/dist/kernel/events.d.ts +6 -0
- package/dist/kernel/events.js +21 -0
- package/dist/kernel/evidence-capture.d.ts +69 -0
- package/dist/kernel/evidence-capture.js +138 -0
- package/dist/kernel/fs.d.ts +20 -0
- package/dist/kernel/fs.js +128 -0
- package/dist/kernel/generic-rw.d.ts +33 -0
- package/dist/kernel/generic-rw.js +235 -0
- package/dist/kernel/hooks.d.ts +172 -0
- package/dist/kernel/hooks.js +256 -0
- package/dist/kernel/index.d.ts +624 -0
- package/dist/kernel/index.js +1638 -0
- package/dist/kernel/instance.d.ts +211 -0
- package/dist/kernel/instance.js +487 -0
- package/dist/kernel/kind-registry.d.ts +183 -0
- package/dist/kernel/kind-registry.js +710 -0
- package/dist/kernel/kind_base.d.ts +106 -0
- package/dist/kernel/kind_base.js +244 -0
- package/dist/kernel/layer-resolver.d.ts +48 -0
- package/dist/kernel/layer-resolver.js +284 -0
- package/dist/kernel/lock-manager.d.ts +21 -0
- package/dist/kernel/lock-manager.js +43 -0
- package/dist/kernel/lock.d.ts +38 -0
- package/dist/kernel/lock.js +115 -0
- package/dist/kernel/meta.d.ts +135 -0
- package/dist/kernel/meta.js +817 -0
- package/dist/kernel/models.d.ts +3089 -0
- package/dist/kernel/models.js +764 -0
- package/dist/kernel/navigator.d.ts +36 -0
- package/dist/kernel/navigator.js +132 -0
- package/dist/kernel/port-surface.d.ts +33 -0
- package/dist/kernel/port-surface.js +180 -0
- package/dist/kernel/preview.d.ts +60 -0
- package/dist/kernel/preview.js +54 -0
- package/dist/kernel/prompt-budget.d.ts +39 -0
- package/dist/kernel/prompt-budget.js +43 -0
- package/dist/kernel/prompt-builder.d.ts +25 -0
- package/dist/kernel/prompt-builder.js +237 -0
- package/dist/kernel/protocols.d.ts +751 -0
- package/dist/kernel/protocols.js +493 -0
- package/dist/kernel/reports.d.ts +26 -0
- package/dist/kernel/reports.js +215 -0
- package/dist/kernel/resolver.d.ts +146 -0
- package/dist/kernel/resolver.js +232 -0
- package/dist/kernel/resource.d.ts +57 -0
- package/dist/kernel/resource.js +97 -0
- package/dist/kernel/runtime.d.ts +16 -0
- package/dist/kernel/runtime.js +18 -0
- package/dist/kernel/safety-scanner.d.ts +38 -0
- package/dist/kernel/safety-scanner.js +228 -0
- package/dist/kernel/serialize-to-files.d.ts +29 -0
- package/dist/kernel/serialize-to-files.js +23 -0
- package/dist/kernel/spec-access.d.ts +23 -0
- package/dist/kernel/spec-access.js +40 -0
- package/dist/kernel/studio_ui.d.ts +88 -0
- package/dist/kernel/studio_ui.js +135 -0
- package/dist/kernel/templates.d.ts +77 -0
- package/dist/kernel/templates.js +116 -0
- package/dist/kernel/tool-registry.d.ts +52 -0
- package/dist/kernel/tool-registry.js +89 -0
- package/dist/kernel/write-pipeline.d.ts +65 -0
- package/dist/kernel/write-pipeline.js +271 -0
- package/dist/kernel/writer-helpers.d.ts +24 -0
- package/dist/kernel/writer-helpers.js +42 -0
- package/dist/memory/decay.d.ts +31 -0
- package/dist/memory/decay.js +97 -0
- package/dist/memory/ecphory.d.ts +25 -0
- package/dist/memory/ecphory.js +178 -0
- package/dist/memory/encodingContext.d.ts +21 -0
- package/dist/memory/encodingContext.js +62 -0
- package/dist/memory/index.d.ts +23 -0
- package/dist/memory/index.js +23 -0
- package/dist/memory/memoryType.d.ts +7 -0
- package/dist/memory/memoryType.js +24 -0
- package/dist/memory/policy.d.ts +35 -0
- package/dist/memory/policy.js +35 -0
- package/dist/memory/retrieval.d.ts +21 -0
- package/dist/memory/retrieval.js +145 -0
- package/dist/testing/index.d.ts +8 -0
- package/dist/testing/index.js +8 -0
- package/dist/testing/recordSearchConformance.d.ts +43 -0
- package/dist/testing/recordSearchConformance.js +147 -0
- package/dist/viz/ascii.d.ts +7 -0
- package/dist/viz/ascii.js +60 -0
- package/dist/viz/health.d.ts +8 -0
- package/dist/viz/health.js +161 -0
- package/dist/viz/index.d.ts +10 -0
- package/dist/viz/index.js +10 -0
- package/dist/viz/matrix.d.ts +8 -0
- package/dist/viz/matrix.js +84 -0
- package/dist/viz/mermaid.d.ts +38 -0
- package/dist/viz/mermaid.js +706 -0
- package/package.json +82 -0
|
@@ -0,0 +1,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
|
+
}
|