dna-sdk 0.10.0 → 0.12.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 (36) hide show
  1. package/dist/bootstrap.js +6 -0
  2. package/dist/extensions/cloud/kinds/tenant-plan.kind.yaml +105 -0
  3. package/dist/extensions/cloud/kinds/tier.kind.yaml +135 -0
  4. package/dist/extensions/cloud.d.ts +31 -0
  5. package/dist/extensions/cloud.js +39 -0
  6. package/dist/extensions/guardrails.js +4 -0
  7. package/dist/extensions/helix.js +19 -6
  8. package/dist/extensions/intel/kinds/insight.kind.yaml +148 -0
  9. package/dist/extensions/intel/kinds/intel-source.kind.yaml +117 -0
  10. package/dist/extensions/intel.d.ts +30 -0
  11. package/dist/extensions/intel.js +38 -0
  12. package/dist/extensions/portfolio/kinds/membership.kind.yaml +114 -0
  13. package/dist/extensions/portfolio/kinds/organization.kind.yaml +99 -0
  14. package/dist/extensions/portfolio/kinds/project.kind.yaml +124 -0
  15. package/dist/extensions/portfolio/kinds/repo.kind.yaml +99 -0
  16. package/dist/extensions/portfolio/kinds/role.kind.yaml +96 -0
  17. package/dist/extensions/portfolio.d.ts +45 -0
  18. package/dist/extensions/portfolio.js +53 -0
  19. package/dist/extensions/sdlc/kinds/lesson-learned.kind.yaml +0 -1
  20. package/dist/index.d.ts +1 -0
  21. package/dist/kernel/composition-resolver.js +17 -1
  22. package/dist/kernel/index.d.ts +50 -0
  23. package/dist/kernel/index.js +98 -0
  24. package/dist/kernel/instance.d.ts +8 -0
  25. package/dist/kernel/instance.js +9 -0
  26. package/dist/kernel/kind_base.d.ts +10 -0
  27. package/dist/kernel/kind_base.js +36 -18
  28. package/dist/kernel/meta.d.ts +1 -0
  29. package/dist/kernel/meta.js +11 -1
  30. package/dist/kernel/models.d.ts +23 -23
  31. package/dist/kernel/models.js +8 -2
  32. package/dist/kernel/prompt-builder.d.ts +64 -0
  33. package/dist/kernel/prompt-builder.js +208 -3
  34. package/dist/kernel/write-pipeline.d.ts +6 -0
  35. package/dist/kernel/write-pipeline.js +7 -0
  36. package/package.json +1 -1
@@ -11,6 +11,7 @@
11
11
  import Mustache from "mustache";
12
12
  import { stripPromptBlock } from "./_text.js";
13
13
  import { AgentNotFound, UnknownLayout } from "./errors.js";
14
+ import { documentHash } from "./lock.js";
14
15
  // ---------------------------------------------------------------------------
15
16
  // PromptBuilder
16
17
  // ---------------------------------------------------------------------------
@@ -44,11 +45,14 @@ export class PromptBuilder {
44
45
  // instruction.
45
46
  throw new AgentNotFound(agentName);
46
47
  }
47
- // Build context — merge backwards-compat params into enabledSlots
48
+ // Build context — merge backwards-compat params into enabledSlots. An
49
+ // explicit empty array means "disable all", ONLY undefined means "no
50
+ // filter" — guarding on truthiness would treat `[]` as unset and leak
51
+ // every skill/guardrail into the prompt (load-bearing after i-031).
48
52
  const enabledSlots = { ...(opts?.enabledSlots ?? {}) };
49
- if (opts?.enabledSkills && !enabledSlots.skills)
53
+ if (opts?.enabledSkills !== undefined && !enabledSlots.skills)
50
54
  enabledSlots.skills = opts.enabledSkills;
51
- if (opts?.enabledGuardrails && !enabledSlots.guardrails)
55
+ if (opts?.enabledGuardrails !== undefined && !enabledSlots.guardrails)
52
56
  enabledSlots.guardrails = opts.enabledGuardrails;
53
57
  let ctx = await this._buildContext(agentDoc, extra, enabledSlots);
54
58
  // Hook: pre_build_prompt — middleware can modify context
@@ -79,6 +83,207 @@ export class PromptBuilder {
79
83
  return prompt.replace(/\n+$/, "");
80
84
  }
81
85
  // -------------------------------------------------------------------------
86
+ // Explain mode — per-section provenance (s-dna-explain-provenance)
87
+ // -------------------------------------------------------------------------
88
+ /**
89
+ * Compose the agent AND return per-section provenance.
90
+ *
91
+ * Returns the composed prompt (byte-identical to {@link build} — explain
92
+ * mode never re-renders) plus a section→provenance map: the source artifact,
93
+ * content hash, version, and layer/overlay origin of each declared
94
+ * composition input (instruction, soul, skills, guardrails). Provenance is
95
+ * reconstructed from the kernel's own declared blocks (layout template +
96
+ * depFilters + flattenInContext) + the layer provenance `resolveDocument`
97
+ * already returns. 1:1 with Python `PromptBuilder.explain`.
98
+ */
99
+ async explain(opts) {
100
+ // Prompt = the ONE canonical composition (byte-equal gate: same path).
101
+ const prompt = await this.build(opts);
102
+ const tenant = opts?.tenant ?? null;
103
+ let agentName = opts?.agent ?? this._defaultAgentName();
104
+ const agentDoc = agentName ? this._findAgent(agentName) : null;
105
+ if (!agentDoc)
106
+ throw new AgentNotFound(agentName);
107
+ const slots = this._mergeSlots(opts);
108
+ const template = await this._effectiveTemplate(agentDoc);
109
+ const specs = this._sectionSpecs(agentDoc, template, slots);
110
+ const sections = [];
111
+ for (const [label, kp, docName] of specs) {
112
+ const base = await this._resolve(kp.kind, docName, null);
113
+ const effective = tenant ? await this._resolve(kp.kind, docName, tenant) : base;
114
+ sections.push(this._sectionProvenance(label, kp, docName, effective, base, tenant));
115
+ }
116
+ return { prompt, sections };
117
+ }
118
+ _defaultAgentName() {
119
+ const root = this.host.root;
120
+ if (root) {
121
+ const kp = this.host._kinds.get(`${root.apiVersion}\0${root.kind}`);
122
+ if (kp)
123
+ return kp.getDefaultAgentName(root);
124
+ }
125
+ return null;
126
+ }
127
+ _mergeSlots(opts) {
128
+ const slots = { ...(opts?.enabledSlots ?? {}) };
129
+ if (opts?.enabledSkills !== undefined && !slots.skills)
130
+ slots.skills = opts.enabledSkills;
131
+ if (opts?.enabledGuardrails !== undefined && !slots.guardrails)
132
+ slots.guardrails = opts.enabledGuardrails;
133
+ return slots;
134
+ }
135
+ async _effectiveTemplate(agentDoc) {
136
+ const spec = agentDoc.spec;
137
+ const kinds = this.host._kinds;
138
+ const agentKp = kinds.get(`${agentDoc.apiVersion}\0${agentDoc.kind}`);
139
+ let tmpl = spec.promptTemplate ?? spec.prompt_template ?? null;
140
+ if (!tmpl) {
141
+ const layout = spec.layout;
142
+ if (layout && agentKp)
143
+ tmpl = agentKp.layoutTemplate(layout);
144
+ else if (agentKp)
145
+ tmpl = agentKp.promptTemplate();
146
+ }
147
+ if (!tmpl)
148
+ return "";
149
+ if (tmpl.includes("/") || tmpl.endsWith(".mustache") || tmpl.endsWith(".md")) {
150
+ tmpl = await this.host.ref(tmpl);
151
+ }
152
+ return tmpl ?? "";
153
+ }
154
+ _kpByAlias(alias) {
155
+ const kinds = this.host._kinds;
156
+ for (const kp of kinds.values()) {
157
+ if (kp?.alias === alias)
158
+ return kp;
159
+ }
160
+ return null;
161
+ }
162
+ /**
163
+ * Reconstruct the ordered composition inputs → `[label, kp, name]`. A
164
+ * depFilter field contributes a section iff the layout actually renders it —
165
+ * a flatten Kind whose flatten var appears in the template (Soul →
166
+ * soul_content) or a Kind whose alias appears as a Mustache section
167
+ * (Skill/Guardrail). Non-prompt deps (tools, actors) fall out.
168
+ */
169
+ _sectionSpecs(agentDoc, template, enabledSlots) {
170
+ const specs = [];
171
+ const kinds = this.host._kinds;
172
+ const agentKp = kinds.get(`${agentDoc.apiVersion}\0${agentDoc.kind}`);
173
+ // The agent's own instruction always leads ({{{agent.instruction}}}).
174
+ specs.push(["instruction", agentKp, agentDoc.name]);
175
+ if (!agentKp)
176
+ return specs;
177
+ const filters = agentKp.depFilters() ?? {};
178
+ const agentSpec = agentDoc.spec;
179
+ for (const [specField, alias] of Object.entries(filters)) {
180
+ const declared = agentSpec[specField];
181
+ if (declared == null || declared === "" || (Array.isArray(declared) && declared.length === 0))
182
+ continue;
183
+ let names = typeof declared === "string" ? [declared] : [...declared];
184
+ if (specField in enabledSlots) {
185
+ const allowed = new Set(enabledSlots[specField]);
186
+ names = names.filter((n) => allowed.has(n));
187
+ }
188
+ if (names.length === 0)
189
+ continue;
190
+ const kp = this._kpByAlias(alias);
191
+ if (!kp)
192
+ continue;
193
+ if (!this._contributesToPrompt(kp, alias, names, template))
194
+ continue;
195
+ const singular = specField.endsWith("s") ? specField.slice(0, -1) : specField;
196
+ for (const n of names) {
197
+ const label = typeof declared === "string" ? singular : `${singular}:${n}`;
198
+ specs.push([label, kp, n]);
199
+ }
200
+ }
201
+ return specs;
202
+ }
203
+ _contributesToPrompt(kp, alias, names, template) {
204
+ if (kp?.flattenInContext) {
205
+ // Flatten Kind (Soul): spec string keys become top-level vars
206
+ // (soul_content). Contributes iff one of those vars is in template.
207
+ for (const doc of this.host.documents) {
208
+ if (doc.kind !== kp.kind || !names.includes(doc.name))
209
+ continue;
210
+ for (const [k, v] of Object.entries(doc.spec)) {
211
+ if (typeof v === "string" && (template.includes(`{{{${k}}}}`) || template.includes(`{{${k}}}`)))
212
+ return true;
213
+ }
214
+ }
215
+ return false;
216
+ }
217
+ // Section Kind (Skill/Guardrail): rendered as {{#alias}} ... {{/alias}}.
218
+ return template.includes(`{{#${alias}}}`);
219
+ }
220
+ async _resolve(kind, name, tenant) {
221
+ const kernel = this.host._kernel;
222
+ if (!kernel?.resolveDocument)
223
+ return null;
224
+ try {
225
+ return await kernel.resolveDocument(this.host.scope, kind, name, { tenant });
226
+ }
227
+ catch {
228
+ return null; // read path, fail-soft
229
+ }
230
+ }
231
+ static _docHash(raw) {
232
+ if (!raw || typeof raw !== "object")
233
+ return null;
234
+ try {
235
+ return documentHash(raw);
236
+ }
237
+ catch {
238
+ return null;
239
+ }
240
+ }
241
+ _sectionProvenance(section, kp, name, effective, base, tenant) {
242
+ const source = PromptBuilder._sourcePath(kp, name);
243
+ const effRaw = effective?.doc ?? null;
244
+ const baseRaw = base?.doc ?? null;
245
+ const hash = PromptBuilder._docHash(effRaw);
246
+ let version = null;
247
+ if (effRaw && typeof effRaw === "object") {
248
+ const meta = effRaw.metadata ?? {};
249
+ const v = meta?.version;
250
+ version = v != null ? String(v) : null;
251
+ }
252
+ // Layer origin from the base resolution (tenant-independent). The FS
253
+ // tenant→base fallback would otherwise stamp every section with the
254
+ // tenant even without an overlay.
255
+ let origin = "?";
256
+ let isInherited = false;
257
+ const originSrc = base ?? effective;
258
+ if (originSrc) {
259
+ const eff = originSrc.provenance?.effectiveLayer ?? null;
260
+ if (eff)
261
+ origin = eff.scope;
262
+ else if (originSrc.doc == null)
263
+ origin = "(not found)";
264
+ isInherited = Boolean(originSrc.isInherited);
265
+ }
266
+ // Overridden iff a tenant was requested AND the tenant-resolved content
267
+ // actually differs from the base content.
268
+ const overriddenByTenant = Boolean(tenant && hash !== null && hash !== PromptBuilder._docHash(baseRaw));
269
+ return { section, kind: kp?.kind ?? "?", name, source, hash, version, origin, isInherited, overriddenByTenant };
270
+ }
271
+ static _sourcePath(kp, name) {
272
+ const storage = kp?.storage;
273
+ if (!storage)
274
+ return "?";
275
+ const pattern = storage.pattern;
276
+ const container = storage.container ?? "";
277
+ const marker = storage.marker;
278
+ if (pattern === "bundle")
279
+ return container ? `${container}/${name}/${marker}` : `${name}/${marker}`;
280
+ if (pattern === "yaml")
281
+ return container ? `${container}/${name}.yaml` : `${name}.yaml`;
282
+ if (pattern === "root" || pattern === "standalone")
283
+ return marker ?? `${name}`;
284
+ return container ? `${container}/${name}` : name;
285
+ }
286
+ // -------------------------------------------------------------------------
82
287
  // Private helpers
83
288
  // -------------------------------------------------------------------------
84
289
  _findAgent(name) {
@@ -1,4 +1,5 @@
1
1
  import type { WriteHost } from "./collaborator-ports.js";
2
+ import { type KindPort } from "./protocols.js";
2
3
  export declare class WritePipeline {
3
4
  private readonly _k;
4
5
  constructor(kernel: WriteHost);
@@ -24,6 +25,11 @@ export declare class WritePipeline {
24
25
  * - Didactic failure (install #26 pattern): names the field, the
25
26
  * violation, and points at `dna kind show <Kind>`.
26
27
  */
28
+ /** Public pre-flight twin of `_validateSpecSchema` — the SAME check the
29
+ * write path enforces, exposed so read-only callers (dry-run / pre-flight)
30
+ * catch a schema-violating doc BEFORE the write (i-validation-shallow).
31
+ * Delegates to Kernel.validateDocument. */
32
+ validateSpecSchema(scope: string, kind: string, name: string, raw: Record<string, unknown>, port: KindPort | null): void;
27
33
  private _validateSpecSchema;
28
34
  /**
29
35
  * Reconcile tenant + layer args + Kernel.tenant + KindPort.scope.
@@ -36,6 +36,13 @@ export class WritePipeline {
36
36
  * - Didactic failure (install #26 pattern): names the field, the
37
37
  * violation, and points at `dna kind show <Kind>`.
38
38
  */
39
+ /** Public pre-flight twin of `_validateSpecSchema` — the SAME check the
40
+ * write path enforces, exposed so read-only callers (dry-run / pre-flight)
41
+ * catch a schema-violating doc BEFORE the write (i-validation-shallow).
42
+ * Delegates to Kernel.validateDocument. */
43
+ validateSpecSchema(scope, kind, name, raw, port) {
44
+ this._validateSpecSchema(scope, kind, name, raw, port);
45
+ }
39
46
  _validateSpecSchema(scope, kind, name, raw, port) {
40
47
  const mode = WritePipeline._validationMode();
41
48
  if (mode === "off" || port === null || typeof raw !== "object" || raw === null) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dna-sdk",
3
- "version": "0.10.0",
3
+ "version": "0.12.0",
4
4
  "description": "DNA — Domain Notation of Anything (TypeScript SDK)",
5
5
  "type": "module",
6
6
  "license": "MIT",