dna-sdk 0.6.0 → 0.8.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.
@@ -0,0 +1,16 @@
1
+ import type { EmitContext, EmitResult, EmitterPort } from "./index.js";
2
+ /** `concierge-grounded` → `ConciergeGrounded`. */
3
+ export declare function camel(name: string): string;
4
+ /** Split a DNA model coordinate into agent-framework `{id, provider}`. */
5
+ export declare function splitModel(model: string | null, providerHint?: string | null): {
6
+ id: string;
7
+ provider: string;
8
+ } | null;
9
+ export declare class AgentFrameworkEmitter implements EmitterPort {
10
+ readonly target = "agent-framework";
11
+ readonly fileExtension = "agent.yaml";
12
+ /** The PURE de-para: {@link EmitContext} → the PromptAgent object. Field
13
+ * order is intentional and preserved by js-yaml (insertion order). */
14
+ toPromptAgent(ctx: EmitContext): Record<string, unknown>;
15
+ emit(ctx: EmitContext): EmitResult;
16
+ }
@@ -0,0 +1,127 @@
1
+ /**
2
+ * DNA → Microsoft **agent-framework** emitter (TS twin of python
3
+ * `dna.emit.agent_framework`). Materializes an {@link EmitContext} into the
4
+ * declarative `PromptAgent` YAML that `agent-framework-declarative`'s
5
+ * `AgentFactory` loads.
6
+ *
7
+ * The de-para (DNA field → PromptAgent field):
8
+ * metadata.name -> name (CamelCased id)
9
+ * metadata.description -> description
10
+ * Soul + guardrails + instruction -> instructions (flat — kernel-composed)
11
+ * spec.model (or Genome default_llm)-> model.{id, provider}
12
+ * spec.tools[] (Tool Kind surfaces) -> tools[] (kind: function)
13
+ * spec.output_schema -> outputSchema (only when present)
14
+ *
15
+ * `toPromptAgent` is the PURE de-para and is parity-critical: it must build the
16
+ * SAME object the Python `to_prompt_agent` builds from the same context.
17
+ */
18
+ import yaml from "js-yaml";
19
+ /** DNA provider token → agent-framework `model.provider` value. Unknown tokens
20
+ * pass through unchanged so a future provider needs no code change. */
21
+ const PROVIDER_MAP = {
22
+ azure: "AzureOpenAI",
23
+ azureopenai: "AzureOpenAI",
24
+ azure_openai: "AzureOpenAI",
25
+ openai: "OpenAI",
26
+ anthropic: "Anthropic",
27
+ foundry: "AzureAIFoundry",
28
+ azureaifoundry: "AzureAIFoundry",
29
+ };
30
+ /** Bare model with no provider token and no `--provider` → AzureOpenAI (the
31
+ * provider the spike proved). Documented default, never silently wrong. */
32
+ const DEFAULT_PROVIDER = "AzureOpenAI";
33
+ /** `concierge-grounded` → `ConciergeGrounded`. */
34
+ export function camel(name) {
35
+ return String(name)
36
+ .replace(/_/g, "-")
37
+ .split("-")
38
+ .filter(Boolean)
39
+ .map((p) => p.charAt(0).toUpperCase() + p.slice(1))
40
+ .join("");
41
+ }
42
+ /** Split a DNA model coordinate into agent-framework `{id, provider}`. */
43
+ export function splitModel(model, providerHint) {
44
+ if (!model)
45
+ return null;
46
+ let token = null;
47
+ let ident = model;
48
+ for (const sep of [":", "/"]) {
49
+ const i = model.indexOf(sep);
50
+ if (i >= 0) {
51
+ token = model.slice(0, i);
52
+ ident = model.slice(i + 1);
53
+ break;
54
+ }
55
+ }
56
+ let provider;
57
+ if (providerHint)
58
+ provider = providerHint;
59
+ else if (token)
60
+ provider = PROVIDER_MAP[token.trim().toLowerCase()] ?? token;
61
+ else
62
+ provider = DEFAULT_PROVIDER;
63
+ return { id: ident.trim(), provider };
64
+ }
65
+ function emitTools(tools) {
66
+ return tools.map((t) => {
67
+ const entry = {
68
+ name: t.name,
69
+ kind: "function", // AgentSchema function-tool kind (NOT `type`)
70
+ description: t.description ?? "",
71
+ };
72
+ if (t.parameters && Object.keys(t.parameters).length > 0) {
73
+ entry.parameters = t.parameters;
74
+ }
75
+ return entry;
76
+ });
77
+ }
78
+ export class AgentFrameworkEmitter {
79
+ target = "agent-framework";
80
+ fileExtension = "agent.yaml";
81
+ /** The PURE de-para: {@link EmitContext} → the PromptAgent object. Field
82
+ * order is intentional and preserved by js-yaml (insertion order). */
83
+ toPromptAgent(ctx) {
84
+ const providerHint = ctx.options?.provider ?? null;
85
+ const doc = { kind: "Prompt", name: camel(ctx.name) };
86
+ if (ctx.description)
87
+ doc.description = ctx.description;
88
+ const model = splitModel(ctx.model, providerHint);
89
+ if (model)
90
+ doc.model = model;
91
+ if (ctx.tools.length > 0)
92
+ doc.tools = emitTools(ctx.tools);
93
+ doc.instructions = ctx.instructions; // verbatim — the byte-equal gate
94
+ if (ctx.outputSchema)
95
+ doc.outputSchema = ctx.outputSchema;
96
+ return doc;
97
+ }
98
+ emit(ctx) {
99
+ const promptAgent = this.toPromptAgent(ctx);
100
+ const artifact = yaml.dump(promptAgent, { sortKeys: false, lineWidth: -1 });
101
+ const losses = [
102
+ "composition structure — Soul reuse + wired Guardrails flatten to one " +
103
+ "`instructions` string (no `soul:`/`guardrails:` slot in a PromptAgent)",
104
+ "tenant overlay — a per-tenant persona without a fork has no PromptAgent field",
105
+ "eval-as-contract — prompt invariants (EvalCases) have no PromptAgent slot",
106
+ ];
107
+ if (ctx.model === null) {
108
+ losses.push("model unbound in DNA and none supplied — emitted PromptAgent has no " +
109
+ "`model:` block; pass provider/model or set spec.model / Genome default_llm");
110
+ }
111
+ const mapping = {
112
+ "metadata.name": "name (CamelCase)",
113
+ "metadata.description": "description",
114
+ "buildPrompt (Soul+guardrails+instruction)": "instructions",
115
+ "spec.model / Genome.default_llm": "model.{id,provider}",
116
+ "spec.tools[] (Tool Kind)": "tools[] (kind: function)",
117
+ "spec.output_schema": "outputSchema",
118
+ };
119
+ return {
120
+ artifact,
121
+ target: this.target,
122
+ filename: `${ctx.name}.${this.fileExtension}`,
123
+ losses,
124
+ mapping,
125
+ };
126
+ }
127
+ }
@@ -0,0 +1,47 @@
1
+ /**
2
+ * DNA → **Amazon Bedrock Agent** emitter (TS twin of python
3
+ * `dna.emit.bedrock`). Materializes an {@link EmitContext} into an AWS
4
+ * **CloudFormation** template that declares an `AWS::Bedrock::Agent` resource —
5
+ * the managed, declarative Bedrock Agents service. The SECOND runtime the SAME
6
+ * DNA source emits to (the first is Microsoft agent-framework): the portability
7
+ * proof — author once, emit per runtime.
8
+ *
9
+ * Why Bedrock **Agents** (not Strands / AgentCore): only Bedrock Agents has a
10
+ * published *declarative* schema (`Instruction`, `FoundationModel`,
11
+ * `ActionGroups` with a `FunctionSchema`) that maps field-for-field from a DNA
12
+ * agent. Emitting a CloudFormation template gives a lintable, deployable artifact
13
+ * that needs no AWS credential to produce or validate structurally.
14
+ *
15
+ * The de-para (DNA field → CloudFormation `AWS::Bedrock::Agent` field):
16
+ * metadata.name -> Resources.<Camel>Agent.Properties.AgentName
17
+ * metadata.description -> Properties.Description (when present)
18
+ * Soul + guardrails + instruction -> Properties.Instruction (flat, BYTE-EQUAL)
19
+ * spec.model (or Genome default_llm)-> Properties.FoundationModel (provider token stripped)
20
+ * spec.tools[] (Tool Kind surfaces) -> Properties.ActionGroups[0].FunctionSchema.Functions[]
21
+ *
22
+ * `toTemplate` is the PURE de-para and is parity-critical: it must build the SAME
23
+ * object the Python `to_template` builds from the same context.
24
+ */
25
+ import type { EmitContext, EmitResult, EmitterPort } from "./index.js";
26
+ /** `concierge-grounded` → `ConciergeGrounded` (a valid CFN logical id). */
27
+ export declare function camel(name: string): string;
28
+ /** Project a DNA model coordinate → a Bedrock `FoundationModel` id. Bedrock
29
+ * encodes the provider inside the id (`anthropic.claude-v2`), so a DNA
30
+ * `prov:model` / `prov/model` provider token is dropped; a bare coordinate
31
+ * passes through unchanged. */
32
+ export declare function bedrockModelId(model: string | null): string | null;
33
+ /** Project a Tool's input JSON Schema → Bedrock `Function.Parameters`
34
+ * (`{name: {Type, Description, Required}}`). Returns the map + whether any type
35
+ * was coerced (for the loss report). */
36
+ export declare function emitParameters(inputSchema: Record<string, unknown>): {
37
+ params: Record<string, unknown>;
38
+ coerced: boolean;
39
+ };
40
+ export declare class BedrockEmitter implements EmitterPort {
41
+ readonly target = "bedrock";
42
+ readonly fileExtension = "bedrock.json";
43
+ /** The PURE de-para: {@link EmitContext} → the CloudFormation object. Field
44
+ * order is intentional and preserved by JSON.stringify (insertion order). */
45
+ toTemplate(ctx: EmitContext): Record<string, unknown>;
46
+ emit(ctx: EmitContext): EmitResult;
47
+ }
@@ -0,0 +1,169 @@
1
+ /** Bedrock `ParameterDetail.Type` allowed values. A JSON-Schema type outside
2
+ * this set (notably `object`) is coerced to `string` (the depth loss). */
3
+ const BEDROCK_PARAM_TYPES = new Set(["string", "number", "integer", "boolean", "array"]);
4
+ /** CloudFormation template format version — the stable, only value. */
5
+ const CFN_VERSION = "2010-09-09";
6
+ /** DNA provider tokens (the `prov:model` / `prov/model` prefixes DNA authors
7
+ * use). Stripped to expose the bare model id. Bedrock-native provider prefixes
8
+ * (anthropic./amazon./…) use a DOT and are NOT here, so a real Bedrock id (incl.
9
+ * a `:0` version suffix) passes through. */
10
+ const DNA_PROVIDER_TOKENS = new Set([
11
+ "azure", "azureopenai", "azure_openai", "openai", "foundry", "azureaifoundry",
12
+ "vertex", "google", "gemini",
13
+ ]);
14
+ /** `concierge-grounded` → `ConciergeGrounded` (a valid CFN logical id). */
15
+ export function camel(name) {
16
+ return String(name)
17
+ .replace(/_/g, "-")
18
+ .split("-")
19
+ .filter(Boolean)
20
+ .map((p) => p.charAt(0).toUpperCase() + p.slice(1))
21
+ .join("");
22
+ }
23
+ /** Project a DNA model coordinate → a Bedrock `FoundationModel` id. Bedrock
24
+ * encodes the provider inside the id (`anthropic.claude-v2`), so a DNA
25
+ * `prov:model` / `prov/model` provider token is dropped; a bare coordinate
26
+ * passes through unchanged. */
27
+ export function bedrockModelId(model) {
28
+ if (!model)
29
+ return null;
30
+ const ident = model.trim();
31
+ if (ident.toLowerCase().startsWith("arn:"))
32
+ return ident; // ARN — never split.
33
+ const slash = ident.indexOf("/");
34
+ if (slash >= 0)
35
+ return ident.slice(slash + 1).trim(); // DNA slash coordinate.
36
+ const colon = ident.indexOf(":");
37
+ if (colon >= 0) {
38
+ const token = ident.slice(0, colon).trim().toLowerCase();
39
+ if (DNA_PROVIDER_TOKENS.has(token))
40
+ return ident.slice(colon + 1).trim();
41
+ }
42
+ return ident; // bare / Bedrock-native id (keeps any `:version`).
43
+ }
44
+ /** Project a Tool's input JSON Schema → Bedrock `Function.Parameters`
45
+ * (`{name: {Type, Description, Required}}`). Returns the map + whether any type
46
+ * was coerced (for the loss report). */
47
+ export function emitParameters(inputSchema) {
48
+ const props = inputSchema?.properties;
49
+ if (!props || typeof props !== "object" || Object.keys(props).length === 0) {
50
+ return { params: {}, coerced: false };
51
+ }
52
+ const required = Array.isArray(inputSchema.required) ? inputSchema.required : [];
53
+ const requiredSet = new Set(required);
54
+ const out = {};
55
+ let coerced = false;
56
+ for (const [pname, raw] of Object.entries(props)) {
57
+ const pschema = (raw && typeof raw === "object" ? raw : {});
58
+ const jtype = pschema.type ?? "string";
59
+ let btype;
60
+ if (BEDROCK_PARAM_TYPES.has(jtype)) {
61
+ btype = jtype;
62
+ }
63
+ else {
64
+ btype = "string"; // object / unknown → flatten to string (recorded loss)
65
+ coerced = true;
66
+ }
67
+ const detail = { Type: btype };
68
+ if (pschema.description)
69
+ detail.Description = pschema.description;
70
+ detail.Required = requiredSet.has(pname);
71
+ out[pname] = detail;
72
+ }
73
+ return { params: out, coerced };
74
+ }
75
+ /** Project the agent's tools → a single Bedrock action group. The executor is
76
+ * `CustomControl: RETURN_CONTROL` — Bedrock returns the tool call to the CALLER
77
+ * (the faithful mapping for DNA's client-side tools; no Lambda ARN needed). */
78
+ function emitActionGroups(ctx) {
79
+ const functions = [];
80
+ let anyCoerced = false;
81
+ for (const t of ctx.tools) {
82
+ const fn = { Name: t.name };
83
+ if (t.description)
84
+ fn.Description = t.description;
85
+ const { params, coerced } = emitParameters((t.parameters ?? {}));
86
+ anyCoerced = anyCoerced || coerced;
87
+ if (Object.keys(params).length > 0)
88
+ fn.Parameters = params;
89
+ functions.push(fn);
90
+ }
91
+ const group = {
92
+ ActionGroupName: `${ctx.name}-actions`,
93
+ Description: `DNA-emitted tools for agent ${ctx.name}`,
94
+ ActionGroupExecutor: { CustomControl: "RETURN_CONTROL" },
95
+ FunctionSchema: { Functions: functions },
96
+ };
97
+ return { groups: [group], coerced: anyCoerced };
98
+ }
99
+ export class BedrockEmitter {
100
+ target = "bedrock";
101
+ fileExtension = "bedrock.json";
102
+ /** The PURE de-para: {@link EmitContext} → the CloudFormation object. Field
103
+ * order is intentional and preserved by JSON.stringify (insertion order). */
104
+ toTemplate(ctx) {
105
+ const logicalId = `${camel(ctx.name)}Agent`;
106
+ const props = { AgentName: ctx.name };
107
+ if (ctx.description)
108
+ props.Description = ctx.description;
109
+ const modelId = bedrockModelId(ctx.model);
110
+ if (modelId)
111
+ props.FoundationModel = modelId;
112
+ props.Instruction = ctx.instructions; // verbatim — the byte-equal gate
113
+ if (ctx.tools.length > 0)
114
+ props.ActionGroups = emitActionGroups(ctx).groups;
115
+ props.AutoPrepare = true;
116
+ return {
117
+ AWSTemplateFormatVersion: CFN_VERSION,
118
+ Description: `DNA-emitted Amazon Bedrock Agent: ${ctx.name}`,
119
+ Resources: {
120
+ [logicalId]: { Type: "AWS::Bedrock::Agent", Properties: props },
121
+ },
122
+ };
123
+ }
124
+ emit(ctx) {
125
+ const template = this.toTemplate(ctx);
126
+ const artifact = JSON.stringify(template, null, 2) + "\n";
127
+ const losses = [
128
+ "composition structure — Soul reuse + wired Guardrails flatten to one " +
129
+ "`Instruction` string (Bedrock `GuardrailConfiguration` is an " +
130
+ "ID-referenced Bedrock Guardrail, not DNA's composed guardrails)",
131
+ "tenant overlay — a per-tenant persona without a fork has no Bedrock Agent field",
132
+ "eval-as-contract — prompt invariants (EvalCases) have no Bedrock slot",
133
+ ];
134
+ if (ctx.tools.length > 0) {
135
+ losses.push("tool parameter depth — Bedrock `ParameterDetail` is a flat " +
136
+ "{Type, Description, Required} map (Type ∈ string|number|integer|" +
137
+ "boolean|array); JSON-Schema `default`, `enum`, nested object " +
138
+ "`properties`, and array `items` typing are dropped");
139
+ }
140
+ if (ctx.outputSchema) {
141
+ losses.push("output_schema — Bedrock Agent has no structured-response / " +
142
+ "output-schema field; the agent's typed output contract is dropped");
143
+ }
144
+ if (ctx.model === null) {
145
+ losses.push("model unbound in DNA and none supplied — emitted template has no " +
146
+ "`FoundationModel`; pass provider/model or set spec.model / Genome default_llm");
147
+ }
148
+ else {
149
+ losses.push("model coordinate — a DNA `azure/openai` coordinate is not a Bedrock " +
150
+ "foundation-model id; `FoundationModel` needs a Bedrock model id or " +
151
+ "inference-profile ARN, plus an `AgentResourceRoleArn` at deploy");
152
+ }
153
+ const mapping = {
154
+ "metadata.name": "Resources.<id>Agent.Properties.AgentName",
155
+ "metadata.description": "Properties.Description",
156
+ "buildPrompt (Soul+guardrails+instruction)": "Properties.Instruction (byte-equal)",
157
+ "spec.model / Genome.default_llm": "Properties.FoundationModel",
158
+ "spec.tools[] (Tool Kind)": "Properties.ActionGroups[].FunctionSchema.Functions[]",
159
+ "Tool.input_schema.properties": "Function.Parameters{Type,Description,Required}",
160
+ };
161
+ return {
162
+ artifact,
163
+ target: this.target,
164
+ filename: `${ctx.name}.${this.fileExtension}`,
165
+ losses,
166
+ mapping,
167
+ };
168
+ }
169
+ }
@@ -0,0 +1,87 @@
1
+ /**
2
+ * `emit` — the vendor-neutral EMITTER layer (TS twin of python `dna.emit`).
3
+ *
4
+ * DNA authors an agent ONCE — a persona (Soul), an instruction (Agent), wired
5
+ * Guardrails, and Tools — as declarative Kinds. This module materializes that
6
+ * one neutral definition into the NATIVE artifact each runtime framework
7
+ * consumes. The concrete first step of "DNA as the Terraform of agents": author
8
+ * once, emit per runtime, swap runtimes without rewriting the agent.
9
+ *
10
+ * Parity: the pure de-para (`toPromptAgent` in `agentFramework.ts`) mirrors the
11
+ * Python emitter field-for-field; the serialization (js-yaml vs PyYAML) is a
12
+ * rendering detail, so the parity contract is the emitted OBJECT, not the bytes.
13
+ *
14
+ * Shape: {@link EmitContext} (runtime-agnostic view of a composed agent) →
15
+ * {@link EmitterPort} (a target) → {@link EmitResult} (artifact + honest
16
+ * `losses`). {@link registerEmitter}/{@link getEmitter}/{@link availableTargets}
17
+ * are the pluggable registry — a new target is a class + one register call.
18
+ */
19
+ import type { ManifestInstance } from "../kernel/instance.js";
20
+ /** Runtime-agnostic view of ONE composed DNA agent. */
21
+ export interface EmitContext {
22
+ /** The DNA agent slug (`metadata.name`). */
23
+ name: string;
24
+ /** `metadata.description`, or "". */
25
+ description: string;
26
+ /** DNA-composed system prompt (Soul + guardrails + instruction, flat). The
27
+ * byte-equal gate: an emitter MUST carry it verbatim. */
28
+ instructions: string;
29
+ /** Raw DNA model coordinate (`openai:gpt-4o-mini` / `azure/gpt-4o` / bare), or
30
+ * null when the DNA leaves the model unbound. */
31
+ model: string | null;
32
+ /** Resolved tool surfaces (`spec.tools` → Tool Kind). */
33
+ tools: EmitTool[];
34
+ /** Optional response JSON Schema (`spec.output_schema`), or null. */
35
+ outputSchema: Record<string, unknown> | null;
36
+ /** Scope the agent was composed from (provenance). */
37
+ scope: string | null;
38
+ /** Per-emitter hints (e.g. a `--provider` override). */
39
+ options: Record<string, unknown>;
40
+ }
41
+ export interface EmitTool {
42
+ name: string;
43
+ description: string;
44
+ parameters: Record<string, unknown>;
45
+ }
46
+ /** The emitted native artifact + an honest account of the de-para. */
47
+ export interface EmitResult {
48
+ /** The serialized native artifact. */
49
+ artifact: string;
50
+ /** The target runtime id. */
51
+ target: string;
52
+ /** Suggested filename. */
53
+ filename: string;
54
+ /** DNA axes with NO slot in this target — what did NOT survive the emit. */
55
+ losses: string[];
56
+ /** Field-level de-para (`dnaField -> targetField`). */
57
+ mapping: Record<string, string>;
58
+ }
59
+ /** A runtime emitter — pure: reads an {@link EmitContext}, returns an
60
+ * {@link EmitResult}. No kernel I/O, no network. */
61
+ export interface EmitterPort {
62
+ readonly target: string;
63
+ readonly fileExtension: string;
64
+ emit(ctx: EmitContext): EmitResult;
65
+ }
66
+ export declare class EmitError extends Error {
67
+ }
68
+ export declare class UnknownTarget extends EmitError {
69
+ readonly target: string;
70
+ readonly available: string[];
71
+ constructor(target: string, available: string[]);
72
+ }
73
+ /** Register an emitter under its `target` (last registration wins). */
74
+ export declare function registerEmitter(emitter: EmitterPort): EmitterPort;
75
+ /** Look up a registered emitter or throw {@link UnknownTarget}. */
76
+ export declare function getEmitter(target: string): Promise<EmitterPort>;
77
+ /** Sorted list of registered target ids. */
78
+ export declare function availableTargets(): Promise<string[]>;
79
+ export interface BuildEmitContextOpts {
80
+ model?: string | null;
81
+ provider?: string | null;
82
+ }
83
+ /** Compose a DNA agent through the kernel and project it to an
84
+ * {@link EmitContext}. `mi` is a live ManifestInstance. */
85
+ export declare function buildEmitContext(mi: ManifestInstance, agent: string, opts?: BuildEmitContextOpts): Promise<EmitContext>;
86
+ /** Compose `agent` from `mi` and emit it for `target`. */
87
+ export declare function emitAgent(mi: ManifestInstance, agent: string, target: string, opts?: BuildEmitContextOpts): Promise<EmitResult>;
@@ -0,0 +1,97 @@
1
+ import { ToolLibrary } from "../tools.js";
2
+ export class EmitError extends Error {
3
+ }
4
+ export class UnknownTarget extends EmitError {
5
+ target;
6
+ available;
7
+ constructor(target, available) {
8
+ super(`no emitter registered for target '${target}'; available: ` +
9
+ (available.join(", ") || "(none)"));
10
+ this.target = target;
11
+ this.available = available;
12
+ this.name = "UnknownTarget";
13
+ }
14
+ }
15
+ // ── registry ──────────────────────────────────────────────────────────────
16
+ const EMITTER_REGISTRY = new Map();
17
+ let builtinsWired = false;
18
+ async function ensureBuiltins() {
19
+ if (builtinsWired)
20
+ return;
21
+ builtinsWired = true;
22
+ const { AgentFrameworkEmitter } = await import("./agentFramework.js");
23
+ const { BedrockEmitter } = await import("./bedrock.js");
24
+ for (const e of [new AgentFrameworkEmitter(), new BedrockEmitter()]) {
25
+ if (!EMITTER_REGISTRY.has(e.target))
26
+ EMITTER_REGISTRY.set(e.target, e);
27
+ }
28
+ }
29
+ /** Register an emitter under its `target` (last registration wins). */
30
+ export function registerEmitter(emitter) {
31
+ EMITTER_REGISTRY.set(emitter.target, emitter);
32
+ return emitter;
33
+ }
34
+ /** Look up a registered emitter or throw {@link UnknownTarget}. */
35
+ export async function getEmitter(target) {
36
+ await ensureBuiltins();
37
+ const e = EMITTER_REGISTRY.get(target);
38
+ if (!e)
39
+ throw new UnknownTarget(target, await availableTargets());
40
+ return e;
41
+ }
42
+ /** Sorted list of registered target ids. */
43
+ export async function availableTargets() {
44
+ await ensureBuiltins();
45
+ return [...EMITTER_REGISTRY.keys()].sort();
46
+ }
47
+ /** Compose a DNA agent through the kernel and project it to an
48
+ * {@link EmitContext}. `mi` is a live ManifestInstance. */
49
+ export async function buildEmitContext(mi, agent, opts = {}) {
50
+ const doc = mi.findAgent(agent);
51
+ if (!doc) {
52
+ throw new EmitError(`agent '${agent}' not found in scope '${mi.scope ?? "?"}'`);
53
+ }
54
+ const spec = (doc.spec ?? {});
55
+ const meta = (doc.metadata ?? {});
56
+ const description = meta.description ?? "";
57
+ const instructions = await mi.buildPrompt({ agent });
58
+ let resolvedModel = opts.model ?? spec.model ?? null;
59
+ if (!resolvedModel) {
60
+ const root = mi.root;
61
+ const rootSpec = (root?.spec ?? {});
62
+ resolvedModel = rootSpec.default_llm ?? null;
63
+ }
64
+ const tools = resolveTools(mi, spec);
65
+ const outputSchema = spec.output_schema ?? null;
66
+ return {
67
+ name: doc.name ?? agent,
68
+ description,
69
+ instructions,
70
+ model: resolvedModel,
71
+ tools,
72
+ outputSchema,
73
+ scope: mi.scope ?? null,
74
+ options: opts.provider ? { provider: opts.provider } : {},
75
+ };
76
+ }
77
+ function resolveTools(mi, spec) {
78
+ const names = spec.tools;
79
+ if (!names || names.length === 0)
80
+ return [];
81
+ const lib = new ToolLibrary(mi);
82
+ return names.map((name) => {
83
+ const surface = lib.get(name); // throws ToolNotFound (fail-loud)
84
+ return {
85
+ name,
86
+ description: surface.description,
87
+ parameters: { ...surface.parameters },
88
+ };
89
+ });
90
+ }
91
+ // ── high-level surface ──────────────────────────────────────────────────────
92
+ /** Compose `agent` from `mi` and emit it for `target`. */
93
+ export async function emitAgent(mi, agent, target, opts = {}) {
94
+ const emitter = await getEmitter(target);
95
+ const ctx = await buildEmitContext(mi, agent, opts);
96
+ return emitter.emit(ctx);
97
+ }
@@ -101,6 +101,14 @@ class ResearchKind extends KindBase {
101
101
  items: { type: "string" },
102
102
  default: [],
103
103
  },
104
+ cited_by: {
105
+ type: "array",
106
+ items: { type: "string" },
107
+ default: [],
108
+ description: "Kind/name of docs that cite this Research as a grounding source. " +
109
+ "Auto-maintained by `dna sdlc cite Research/<name> --from <Kind>/<name>` " +
110
+ "— don't author by hand.",
111
+ },
104
112
  findings: {
105
113
  type: "array",
106
114
  items: {
package/dist/index.d.ts CHANGED
@@ -26,6 +26,10 @@ export { AgentNotFound, ToolNotFound, UnknownLayout } from "./kernel/errors.js";
26
26
  export { PromptLibrary, loadPrompts } from "./prompts.js";
27
27
  export { ToolLibrary, loadTools } from "./tools.js";
28
28
  export type { ToolSurface } from "./tools.js";
29
+ export { emitAgent, buildEmitContext, registerEmitter, getEmitter, availableTargets, EmitError, UnknownTarget, } from "./emit/index.js";
30
+ export type { EmitContext, EmitResult, EmitTool, EmitterPort, BuildEmitContextOpts, } from "./emit/index.js";
31
+ export { AgentFrameworkEmitter } from "./emit/agentFramework.js";
32
+ export { BedrockEmitter } from "./emit/bedrock.js";
29
33
  export type { LoadPromptsOptions } from "./prompts.js";
30
34
  export { anchorScopesRoot, PackageScopeNotFound, DEFAULT_SUBPATH } from "./package-scope.js";
31
35
  export { loadConfig, findConfig, CONFIG_FILENAME } from "./config.js";
package/dist/index.js CHANGED
@@ -30,6 +30,9 @@ export { createKernelWithBuiltins, quickInstance, createRuntimeWithBuiltins, qui
30
30
  export { AgentNotFound, ToolNotFound, UnknownLayout } from "./kernel/errors.js";
31
31
  export { PromptLibrary, loadPrompts } from "./prompts.js";
32
32
  export { ToolLibrary, loadTools } from "./tools.js";
33
+ export { emitAgent, buildEmitContext, registerEmitter, getEmitter, availableTargets, EmitError, UnknownTarget, } from "./emit/index.js";
34
+ export { AgentFrameworkEmitter } from "./emit/agentFramework.js";
35
+ export { BedrockEmitter } from "./emit/bedrock.js";
33
36
  export { anchorScopesRoot, PackageScopeNotFound, DEFAULT_SUBPATH } from "./package-scope.js";
34
37
  export { loadConfig, findConfig, CONFIG_FILENAME } from "./config.js";
35
38
  export { sourceFromUrl, resolveDefaultFsUrl, UnsupportedSourceScheme } from "./adapters/source-url.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dna-sdk",
3
- "version": "0.6.0",
3
+ "version": "0.8.0",
4
4
  "description": "DNA — Domain Notation of Anything (TypeScript SDK)",
5
5
  "type": "module",
6
6
  "license": "MIT",