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.
- package/dist/bootstrap.js +6 -0
- package/dist/extensions/cloud/kinds/tenant-plan.kind.yaml +105 -0
- package/dist/extensions/cloud/kinds/tier.kind.yaml +135 -0
- package/dist/extensions/cloud.d.ts +31 -0
- package/dist/extensions/cloud.js +39 -0
- package/dist/extensions/guardrails.js +4 -0
- package/dist/extensions/helix.js +19 -6
- package/dist/extensions/intel/kinds/insight.kind.yaml +148 -0
- package/dist/extensions/intel/kinds/intel-source.kind.yaml +117 -0
- package/dist/extensions/intel.d.ts +30 -0
- package/dist/extensions/intel.js +38 -0
- package/dist/extensions/portfolio/kinds/membership.kind.yaml +114 -0
- package/dist/extensions/portfolio/kinds/organization.kind.yaml +99 -0
- package/dist/extensions/portfolio/kinds/project.kind.yaml +124 -0
- package/dist/extensions/portfolio/kinds/repo.kind.yaml +99 -0
- package/dist/extensions/portfolio/kinds/role.kind.yaml +96 -0
- package/dist/extensions/portfolio.d.ts +45 -0
- package/dist/extensions/portfolio.js +53 -0
- package/dist/extensions/sdlc/kinds/lesson-learned.kind.yaml +0 -1
- package/dist/index.d.ts +1 -0
- package/dist/kernel/composition-resolver.js +17 -1
- package/dist/kernel/index.d.ts +50 -0
- package/dist/kernel/index.js +98 -0
- package/dist/kernel/instance.d.ts +8 -0
- package/dist/kernel/instance.js +9 -0
- package/dist/kernel/kind_base.d.ts +10 -0
- package/dist/kernel/kind_base.js +36 -18
- package/dist/kernel/meta.d.ts +1 -0
- package/dist/kernel/meta.js +11 -1
- package/dist/kernel/models.d.ts +23 -23
- package/dist/kernel/models.js +8 -2
- package/dist/kernel/prompt-builder.d.ts +64 -0
- package/dist/kernel/prompt-builder.js +208 -3
- package/dist/kernel/write-pipeline.d.ts +6 -0
- package/dist/kernel/write-pipeline.js +7 -0
- 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) {
|