dna-sdk 0.9.0 → 0.11.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 (43) hide show
  1. package/dist/bootstrap.js +4 -0
  2. package/dist/config.d.ts +7 -0
  3. package/dist/config.js +10 -1
  4. package/dist/emit/agentFramework.d.ts +2 -0
  5. package/dist/emit/agentFramework.js +6 -0
  6. package/dist/emit/agno.d.ts +25 -0
  7. package/dist/emit/agno.js +65 -0
  8. package/dist/emit/bedrock.d.ts +3 -0
  9. package/dist/emit/bedrock.js +9 -0
  10. package/dist/emit/deepagents.d.ts +25 -0
  11. package/dist/emit/deepagents.js +71 -0
  12. package/dist/emit/index.d.ts +33 -2
  13. package/dist/emit/index.js +19 -1
  14. package/dist/emit/langgraph.d.ts +26 -0
  15. package/dist/emit/langgraph.js +70 -0
  16. package/dist/emit/openaiAgents.d.ts +26 -0
  17. package/dist/emit/openaiAgents.js +91 -0
  18. package/dist/emit/scaffold.d.ts +65 -0
  19. package/dist/emit/scaffold.js +179 -0
  20. package/dist/emit/scaffolds/agno/prompt-only.py.tmpl +15 -0
  21. package/dist/emit/scaffolds/agno/with-tools.py.tmpl +23 -0
  22. package/dist/emit/scaffolds/deepagents/prompt-only.py.tmpl +16 -0
  23. package/dist/emit/scaffolds/deepagents/with-tools.py.tmpl +22 -0
  24. package/dist/emit/scaffolds/langgraph/prompt-only.py.tmpl +16 -0
  25. package/dist/emit/scaffolds/langgraph/with-tools.py.tmpl +25 -0
  26. package/dist/emit/scaffolds/openai-agents/prompt-only.py.tmpl +15 -0
  27. package/dist/emit/scaffolds/openai-agents/with-tools.py.tmpl +24 -0
  28. package/dist/emit/vertex.d.ts +3 -0
  29. package/dist/emit/vertex.js +7 -0
  30. package/dist/extensions/cloud/kinds/tenant-plan.kind.yaml +105 -0
  31. package/dist/extensions/cloud/kinds/tier.kind.yaml +135 -0
  32. package/dist/extensions/cloud.d.ts +31 -0
  33. package/dist/extensions/cloud.js +39 -0
  34. package/dist/extensions/intel/kinds/insight.kind.yaml +148 -0
  35. package/dist/extensions/intel/kinds/intel-source.kind.yaml +117 -0
  36. package/dist/extensions/intel.d.ts +30 -0
  37. package/dist/extensions/intel.js +38 -0
  38. package/dist/extensions/sdlc/kinds/lesson-learned.kind.yaml +0 -1
  39. package/dist/index.d.ts +5 -1
  40. package/dist/index.js +4 -1
  41. package/dist/kernel/index.d.ts +44 -0
  42. package/dist/kernel/index.js +85 -0
  43. package/package.json +1 -1
package/dist/bootstrap.js CHANGED
@@ -38,8 +38,10 @@ import { DocExtension } from "./extensions/doc.js";
38
38
  import { ResearchExtension } from "./extensions/research.js";
39
39
  import { TestkitExtension } from "./extensions/testkit.js";
40
40
  import { ModelRegExtension } from "./extensions/modelreg.js";
41
+ import { CloudExtension } from "./extensions/cloud.js";
41
42
  import { AutomationExtension } from "./extensions/automation.js";
42
43
  import { EvalExtension } from "./extensions/eval.js";
44
+ import { IntelExtension } from "./extensions/intel.js";
43
45
  import { loadConfig } from "./config.js";
44
46
  import { resolveDefaultFsUrl, sourceFromUrl } from "./adapters/source-url.js";
45
47
  /**
@@ -69,8 +71,10 @@ function loadBuiltins(target) {
69
71
  new ResearchExtension(),
70
72
  new TestkitExtension(),
71
73
  new ModelRegExtension(),
74
+ new CloudExtension(),
72
75
  new AutomationExtension(),
73
76
  new EvalExtension(),
77
+ new IntelExtension(),
74
78
  ]) {
75
79
  target.load(ext);
76
80
  }
package/dist/config.d.ts CHANGED
@@ -11,6 +11,13 @@ export interface DnaConfig {
11
11
  search: SearchMode;
12
12
  /** Embedding provider selector (default `off`). */
13
13
  embedding: EmbeddingMode;
14
+ /**
15
+ * Opaque passthrough of the `auth:` section — the SDK only checks it is a
16
+ * mapping; its detailed schema (`providers[]` — the pluggable N-provider IdP
17
+ * layer of the MCP runtime face) is owned by the consumer (the CLI's
18
+ * `_mcp_auth.parse_auth_providers`). `null` when the file has no `auth:`.
19
+ */
20
+ auth: Record<string, unknown> | null;
14
21
  /** Where it was loaded from (`null` for a synthesized default). */
15
22
  path: string | null;
16
23
  }
package/dist/config.js CHANGED
@@ -25,7 +25,7 @@ import yaml from "js-yaml";
25
25
  export const CONFIG_FILENAME = "dna.config.yaml";
26
26
  const VALID_SEARCH = ["off", "pgvector", "sqlite-vec"];
27
27
  const VALID_EMBEDDING = ["off", "fake", "onnx"];
28
- const KNOWN_KEYS = new Set(["source", "search", "embedding"]);
28
+ const KNOWN_KEYS = new Set(["source", "search", "embedding", "auth"]);
29
29
  /**
30
30
  * Path to `dna.config.yaml` in `start` (default: cwd), or `null` if absent.
31
31
  * Deliberately NOT a walk-up — a config's meaning is tied to the boot dir.
@@ -93,10 +93,19 @@ function parse(raw, path) {
93
93
  throw new Error(`${path}: \`embedding: ${embedding}\` is not valid — choose one of ` +
94
94
  `${JSON.stringify(VALID_EMBEDDING)}.`);
95
95
  }
96
+ const rawAuth = obj.auth;
97
+ if (rawAuth !== undefined &&
98
+ rawAuth !== null &&
99
+ (typeof rawAuth !== "object" || Array.isArray(rawAuth))) {
100
+ throw new Error(`${path}: \`auth:\` must be a mapping (its \`providers:\` list configures ` +
101
+ `the MCP IdP layer).`);
102
+ }
103
+ const auth = (rawAuth ?? null);
96
104
  return {
97
105
  source,
98
106
  search: search,
99
107
  embedding: embedding,
108
+ auth,
100
109
  path,
101
110
  };
102
111
  }
@@ -13,4 +13,6 @@ export declare class AgentFrameworkEmitter implements EmitterPort {
13
13
  * order is intentional and preserved by js-yaml (insertion order). */
14
14
  toPromptAgent(ctx: EmitContext): Record<string, unknown>;
15
15
  emit(ctx: EmitContext): EmitResult;
16
+ /** Byte-equal invariant hook: read `instructions` back from the emitted YAML. */
17
+ extractInstructions(artifact: string): string | null;
16
18
  }
@@ -124,4 +124,10 @@ export class AgentFrameworkEmitter {
124
124
  mapping,
125
125
  };
126
126
  }
127
+ /** Byte-equal invariant hook: read `instructions` back from the emitted YAML. */
128
+ extractInstructions(artifact) {
129
+ const doc = yaml.load(artifact);
130
+ const value = doc?.instructions;
131
+ return typeof value === "string" ? value : null;
132
+ }
127
133
  }
@@ -0,0 +1,25 @@
1
+ /**
2
+ * DNA → **Agno** emitter (code-first, `agno.agent`; TS twin of python
3
+ * `dna.emit.agno`).
4
+ *
5
+ * Agno is code-first — you construct `Agent(name=..., model=..., instructions=...,
6
+ * tools=[...])`, there is no declarative agent file to map onto. So this target is a
7
+ * {@link ScaffoldEmitter}: it fills a curated `{agno × case}` template rather than
8
+ * generating code ad-hoc, and the emitted `INSTRUCTIONS` constant is byte-equal to
9
+ * the DNA-composed prompt.
10
+ *
11
+ * Model: the DNA coordinate is PRESERVED as a string (Agno accepts a `provider:model`
12
+ * string). Cases: `prompt-only` (no tools) and `with-tools` (one plain-function stub
13
+ * per DNA Tool — Agno auto-wraps callables as tools). `structured-output` (Agno's
14
+ * `output_schema`) is not shipped yet and falls back with a recorded loss.
15
+ */
16
+ import { ScaffoldEmitter, type ScaffoldChoice } from "./scaffold.js";
17
+ import type { EmitContext } from "./index.js";
18
+ export declare class AgnoEmitter extends ScaffoldEmitter {
19
+ readonly framework = "agno";
20
+ readonly target = "agno";
21
+ readonly fileExtension = "py";
22
+ renderContext(ctx: EmitContext, _case: string): Record<string, unknown>;
23
+ losses(ctx: EmitContext, _choice: ScaffoldChoice): string[];
24
+ mapping(): Record<string, string>;
25
+ }
@@ -0,0 +1,65 @@
1
+ /**
2
+ * DNA → **Agno** emitter (code-first, `agno.agent`; TS twin of python
3
+ * `dna.emit.agno`).
4
+ *
5
+ * Agno is code-first — you construct `Agent(name=..., model=..., instructions=...,
6
+ * tools=[...])`, there is no declarative agent file to map onto. So this target is a
7
+ * {@link ScaffoldEmitter}: it fills a curated `{agno × case}` template rather than
8
+ * generating code ad-hoc, and the emitted `INSTRUCTIONS` constant is byte-equal to
9
+ * the DNA-composed prompt.
10
+ *
11
+ * Model: the DNA coordinate is PRESERVED as a string (Agno accepts a `provider:model`
12
+ * string). Cases: `prompt-only` (no tools) and `with-tools` (one plain-function stub
13
+ * per DNA Tool — Agno auto-wraps callables as tools). `structured-output` (Agno's
14
+ * `output_schema`) is not shipped yet and falls back with a recorded loss.
15
+ */
16
+ import { pyIdentifier, pyStrLiteral, ScaffoldEmitter } from "./scaffold.js";
17
+ export class AgnoEmitter extends ScaffoldEmitter {
18
+ framework = "agno";
19
+ target = "agno";
20
+ fileExtension = "py";
21
+ renderContext(ctx, _case) {
22
+ const tools = ctx.tools.map((t) => ({
23
+ name: t.name,
24
+ func_name: pyIdentifier(t.name),
25
+ docstring_literal: pyStrLiteral(t.description || t.name),
26
+ }));
27
+ return {
28
+ has_model: ctx.model !== null,
29
+ model_literal: ctx.model ? pyStrLiteral(ctx.model) : "",
30
+ tools,
31
+ tool_list: tools.map((t) => t.func_name).join(", "),
32
+ };
33
+ }
34
+ losses(ctx, _choice) {
35
+ const out = [];
36
+ if (ctx.tools.length > 0) {
37
+ out.push("tool body — each tool is a scaffolded STUB (a bare callable + " +
38
+ "`raise NotImplementedError`); its real implementation and typed signature " +
39
+ "must be wired (Agno derives the tool schema from the function signature + " +
40
+ "docstring)");
41
+ }
42
+ if (ctx.model === null) {
43
+ out.push("model unbound in DNA and none supplied — emitted `Agent(...)` has no " +
44
+ "`model=`; supply one at wire-up (Agno requires a model)");
45
+ }
46
+ else {
47
+ out.push("model coordinate — the DNA coordinate is carried verbatim as a string; this " +
48
+ "assumes Agno's string-model resolution. A specific `Model` object (e.g. " +
49
+ "`OpenAIChat(id=...)`) is the alternative at wire-up");
50
+ }
51
+ if (ctx.outputSchema) {
52
+ out.push("output_schema — map DNA's `spec.output_schema` to `Agent(output_schema=...)` " +
53
+ "(a Pydantic model) by hand; the scaffold does not synthesize the class");
54
+ }
55
+ return out;
56
+ }
57
+ mapping() {
58
+ return {
59
+ "buildPrompt (Soul+guardrails+instruction)": "INSTRUCTIONS constant (byte-equal)",
60
+ "metadata.name": "Agent(name=...)",
61
+ "spec.model / Genome.default_llm": "Agent(model=...) (DNA coordinate preserved)",
62
+ "spec.tools[] (Tool Kind)": "plain-function stubs → Agent(tools=[...])",
63
+ };
64
+ }
65
+ }
@@ -44,4 +44,7 @@ export declare class BedrockEmitter implements EmitterPort {
44
44
  * order is intentional and preserved by JSON.stringify (insertion order). */
45
45
  toTemplate(ctx: EmitContext): Record<string, unknown>;
46
46
  emit(ctx: EmitContext): EmitResult;
47
+ /** Byte-equal invariant hook: read `Properties.Instruction` back from the
48
+ * emitted CloudFormation template. */
49
+ extractInstructions(artifact: string): string | null;
47
50
  }
@@ -166,4 +166,13 @@ export class BedrockEmitter {
166
166
  mapping,
167
167
  };
168
168
  }
169
+ /** Byte-equal invariant hook: read `Properties.Instruction` back from the
170
+ * emitted CloudFormation template. */
171
+ extractInstructions(artifact) {
172
+ const template = JSON.parse(artifact);
173
+ const resources = template.Resources ?? {};
174
+ const first = Object.values(resources)[0];
175
+ const value = first?.Properties?.Instruction;
176
+ return typeof value === "string" ? value : null;
177
+ }
169
178
  }
@@ -0,0 +1,25 @@
1
+ /**
2
+ * DNA → **DeepAgents** emitter (code-first, `deepagents`; TS twin of python
3
+ * `dna.emit.deepagents`).
4
+ *
5
+ * DeepAgents (the LangChain "batteries-included agent harness") is code-first — you
6
+ * call `create_deep_agent(model=..., tools=[...], system_prompt="...")`, there is no
7
+ * declarative agent file to map onto. So this target is a {@link ScaffoldEmitter}: it
8
+ * fills a curated `{deepagents × case}` template rather than generating code ad-hoc,
9
+ * and the emitted `INSTRUCTIONS` constant is byte-equal to the DNA-composed prompt.
10
+ *
11
+ * `system_prompt` sits in FRONT of the deep-agent's built-in harness prompt (the
12
+ * planning / filesystem / sub-agent scaffolding the framework appends) — so the DNA
13
+ * prompt is a PREFIX of the effective system prompt. Model: the DNA coordinate is
14
+ * PRESERVED (resolved via `init_chat_model`). Cases: `prompt-only` and `with-tools`.
15
+ */
16
+ import { ScaffoldEmitter, type ScaffoldChoice } from "./scaffold.js";
17
+ import type { EmitContext } from "./index.js";
18
+ export declare class DeepAgentsEmitter extends ScaffoldEmitter {
19
+ readonly framework = "deepagents";
20
+ readonly target = "deepagents";
21
+ readonly fileExtension = "py";
22
+ renderContext(ctx: EmitContext, _case: string): Record<string, unknown>;
23
+ losses(ctx: EmitContext, _choice: ScaffoldChoice): string[];
24
+ mapping(): Record<string, string>;
25
+ }
@@ -0,0 +1,71 @@
1
+ /**
2
+ * DNA → **DeepAgents** emitter (code-first, `deepagents`; TS twin of python
3
+ * `dna.emit.deepagents`).
4
+ *
5
+ * DeepAgents (the LangChain "batteries-included agent harness") is code-first — you
6
+ * call `create_deep_agent(model=..., tools=[...], system_prompt="...")`, there is no
7
+ * declarative agent file to map onto. So this target is a {@link ScaffoldEmitter}: it
8
+ * fills a curated `{deepagents × case}` template rather than generating code ad-hoc,
9
+ * and the emitted `INSTRUCTIONS` constant is byte-equal to the DNA-composed prompt.
10
+ *
11
+ * `system_prompt` sits in FRONT of the deep-agent's built-in harness prompt (the
12
+ * planning / filesystem / sub-agent scaffolding the framework appends) — so the DNA
13
+ * prompt is a PREFIX of the effective system prompt. Model: the DNA coordinate is
14
+ * PRESERVED (resolved via `init_chat_model`). Cases: `prompt-only` and `with-tools`.
15
+ */
16
+ import { pyIdentifier, pyStrLiteral, ScaffoldEmitter } from "./scaffold.js";
17
+ export class DeepAgentsEmitter extends ScaffoldEmitter {
18
+ framework = "deepagents";
19
+ target = "deepagents";
20
+ fileExtension = "py";
21
+ renderContext(ctx, _case) {
22
+ const tools = ctx.tools.map((t) => ({
23
+ name: t.name,
24
+ func_name: pyIdentifier(t.name),
25
+ docstring_literal: pyStrLiteral(t.description || t.name),
26
+ }));
27
+ return {
28
+ has_model: ctx.model !== null,
29
+ model_literal: ctx.model ? pyStrLiteral(ctx.model) : "",
30
+ tools,
31
+ tool_list: tools.map((t) => t.func_name).join(", "),
32
+ };
33
+ }
34
+ losses(ctx, _choice) {
35
+ const out = [
36
+ "harness prompt — `system_prompt` sits in FRONT of the deep-agent's built-in " +
37
+ "harness prompt (planning / filesystem / sub-agent scaffolding), which the " +
38
+ "framework appends; the DNA prompt is a PREFIX of the effective system prompt, " +
39
+ "not the whole of it",
40
+ "metadata.name — `create_deep_agent` has no declarative name slot; the DNA agent " +
41
+ "name is not carried",
42
+ ];
43
+ if (ctx.tools.length > 0) {
44
+ out.push("tool body — each tool is a scaffolded STUB (a callable + " +
45
+ "`raise NotImplementedError`); its real implementation and typed signature " +
46
+ "must be wired");
47
+ }
48
+ if (ctx.model === null) {
49
+ out.push("model unbound in DNA and none supplied — emitted `create_deep_agent(...)` has " +
50
+ "no `model=`; the framework falls back to its default deep-agent model");
51
+ }
52
+ else {
53
+ out.push("model coordinate — the DNA coordinate is carried verbatim; " +
54
+ "`create_deep_agent` resolves it via `init_chat_model`, whose provider " +
55
+ "prefixes are `openai` / `anthropic` / `azure_openai` / `google_genai`; a " +
56
+ "DNA `azure/…` coordinate needs the `azure_openai:` prefix at wire-up");
57
+ }
58
+ if (ctx.outputSchema) {
59
+ out.push("output_schema — DNA's `spec.output_schema` has no direct `create_deep_agent` " +
60
+ "slot; enforce structured output via a middleware or a typed sub-agent by hand");
61
+ }
62
+ return out;
63
+ }
64
+ mapping() {
65
+ return {
66
+ "buildPrompt (Soul+guardrails+instruction)": "INSTRUCTIONS constant (byte-equal, PREFIX of system_prompt)",
67
+ "spec.model / Genome.default_llm": "create_deep_agent(model=...) (DNA coordinate preserved)",
68
+ "spec.tools[] (Tool Kind)": "callable stubs → create_deep_agent(tools=[...])",
69
+ };
70
+ }
71
+ }
@@ -7,9 +7,28 @@
7
7
  * consumes. The concrete first step of "DNA as the Terraform of agents": author
8
8
  * once, emit per runtime, swap runtimes without rewriting the agent.
9
9
  *
10
+ * The {@link EmitterPort} is a **first-class DNA port** — a documented contract
11
+ * on the same footing as the kernel's five ports, one layer OUT: the kernel
12
+ * *composes* the neutral agent; the EmitterPort *materializes* it for a runtime.
13
+ * See the `How to write an emitter` guide (`docs/guides/writing-an-emitter.md`).
14
+ *
15
+ * The contract has two surfaces: `buildEmitContext(mi, agent)` (the kernel-facing
16
+ * half — compose + project to the neutral {@link EmitContext}) and
17
+ * `EmitterPort.emit(ctx)` (the runtime-facing half a target implements — PURE,
18
+ * no kernel I/O). Its **central invariant**: the composed `instructions` in the
19
+ * emitted artifact is byte-equal to `mi.buildPrompt({agent})`. That invariant is
20
+ * checkable and *inheritable* via {@link EmitterPort.extractInstructions} — one
21
+ * generic test runs it over EVERY registered target.
22
+ *
23
+ * Two flavors satisfy the same port: **config-declarative** (a runtime with a
24
+ * published declarative schema — agent-framework / bedrock / vertex) and
25
+ * **scaffold-code** (a code-first runtime — see `scaffold.ts`; the `openai-agents`
26
+ * target is the reference).
27
+ *
10
28
  * Parity: the pure de-para (`toPromptAgent` in `agentFramework.ts`) mirrors the
11
29
  * 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.
30
+ * rendering detail, so the parity contract is the emitted OBJECT/behavior, not
31
+ * the bytes.
13
32
  *
14
33
  * Shape: {@link EmitContext} (runtime-agnostic view of a composed agent) →
15
34
  * {@link EmitterPort} (a target) → {@link EmitResult} (artifact + honest
@@ -57,11 +76,19 @@ export interface EmitResult {
57
76
  mapping: Record<string, string>;
58
77
  }
59
78
  /** A runtime emitter — pure: reads an {@link EmitContext}, returns an
60
- * {@link EmitResult}. No kernel I/O, no network. */
79
+ * {@link EmitResult}. No kernel I/O, no network.
80
+ *
81
+ * A conforming emitter provides identity (`target` / `fileExtension`), the
82
+ * materialization ({@link emit}), and the byte-equal invariant hook
83
+ * ({@link extractInstructions}) that makes the central invariant inheritable. */
61
84
  export interface EmitterPort {
62
85
  readonly target: string;
63
86
  readonly fileExtension: string;
64
87
  emit(ctx: EmitContext): EmitResult;
88
+ /** Recover the composed instruction embedded in `artifact` (the inverse of the
89
+ * instruction half of {@link emit}) — used by the generic byte-equal contract
90
+ * test. Returns null only when the target has no instruction slot at all. */
91
+ extractInstructions(artifact: string): string | null;
65
92
  }
66
93
  export declare class EmitError extends Error {
67
94
  }
@@ -72,6 +99,10 @@ export declare class UnknownTarget extends EmitError {
72
99
  }
73
100
  /** Register an emitter under its `target` (last registration wins). */
74
101
  export declare function registerEmitter(emitter: EmitterPort): EmitterPort;
102
+ /** Remove a registered emitter (a host override or a test double). Returns
103
+ * whether a target was actually removed. Parity with the Python side popping
104
+ * from `EMITTER_REGISTRY`. */
105
+ export declare function unregisterEmitter(target: string): boolean;
75
106
  /** Look up a registered emitter or throw {@link UnknownTarget}. */
76
107
  export declare function getEmitter(target: string): Promise<EmitterPort>;
77
108
  /** Sorted list of registered target ids. */
@@ -22,7 +22,19 @@ async function ensureBuiltins() {
22
22
  const { AgentFrameworkEmitter } = await import("./agentFramework.js");
23
23
  const { BedrockEmitter } = await import("./bedrock.js");
24
24
  const { VertexEmitter } = await import("./vertex.js");
25
- for (const e of [new AgentFrameworkEmitter(), new BedrockEmitter(), new VertexEmitter()]) {
25
+ const { OpenAIAgentsEmitter } = await import("./openaiAgents.js");
26
+ const { LanggraphEmitter } = await import("./langgraph.js");
27
+ const { AgnoEmitter } = await import("./agno.js");
28
+ const { DeepAgentsEmitter } = await import("./deepagents.js");
29
+ for (const e of [
30
+ new AgentFrameworkEmitter(),
31
+ new BedrockEmitter(),
32
+ new VertexEmitter(),
33
+ new OpenAIAgentsEmitter(),
34
+ new LanggraphEmitter(),
35
+ new AgnoEmitter(),
36
+ new DeepAgentsEmitter(),
37
+ ]) {
26
38
  if (!EMITTER_REGISTRY.has(e.target))
27
39
  EMITTER_REGISTRY.set(e.target, e);
28
40
  }
@@ -32,6 +44,12 @@ export function registerEmitter(emitter) {
32
44
  EMITTER_REGISTRY.set(emitter.target, emitter);
33
45
  return emitter;
34
46
  }
47
+ /** Remove a registered emitter (a host override or a test double). Returns
48
+ * whether a target was actually removed. Parity with the Python side popping
49
+ * from `EMITTER_REGISTRY`. */
50
+ export function unregisterEmitter(target) {
51
+ return EMITTER_REGISTRY.delete(target);
52
+ }
35
53
  /** Look up a registered emitter or throw {@link UnknownTarget}. */
36
54
  export async function getEmitter(target) {
37
55
  await ensureBuiltins();
@@ -0,0 +1,26 @@
1
+ /**
2
+ * DNA → **LangGraph** emitter (code-first, `langgraph.prebuilt`; TS twin of
3
+ * python `dna.emit.langgraph`).
4
+ *
5
+ * LangGraph is code-first — you build an agent by calling
6
+ * `create_react_agent(model, tools=[...], prompt="...")`, there is no declarative
7
+ * agent file to map onto. So this target is a {@link ScaffoldEmitter}: it fills a
8
+ * curated `{langgraph × case}` template rather than generating code ad-hoc, and the
9
+ * emitted `INSTRUCTIONS` constant is byte-equal to the DNA-composed prompt.
10
+ *
11
+ * Model: the DNA coordinate is PRESERVED (unlike openai-agents, which strips the
12
+ * provider token) — `create_react_agent` resolves the string via `init_chat_model`,
13
+ * which takes a `provider:model` coordinate. Cases: `prompt-only` (no tools) and
14
+ * `with-tools` (the ReAct idiom — one `@tool` stub per DNA Tool). `structured-output`
15
+ * (LangGraph's `response_format`) is not shipped yet and falls back with a loss.
16
+ */
17
+ import { ScaffoldEmitter, type ScaffoldChoice } from "./scaffold.js";
18
+ import type { EmitContext } from "./index.js";
19
+ export declare class LanggraphEmitter extends ScaffoldEmitter {
20
+ readonly framework = "langgraph";
21
+ readonly target = "langgraph";
22
+ readonly fileExtension = "py";
23
+ renderContext(ctx: EmitContext, _case: string): Record<string, unknown>;
24
+ losses(ctx: EmitContext, _choice: ScaffoldChoice): string[];
25
+ mapping(): Record<string, string>;
26
+ }
@@ -0,0 +1,70 @@
1
+ /**
2
+ * DNA → **LangGraph** emitter (code-first, `langgraph.prebuilt`; TS twin of
3
+ * python `dna.emit.langgraph`).
4
+ *
5
+ * LangGraph is code-first — you build an agent by calling
6
+ * `create_react_agent(model, tools=[...], prompt="...")`, there is no declarative
7
+ * agent file to map onto. So this target is a {@link ScaffoldEmitter}: it fills a
8
+ * curated `{langgraph × case}` template rather than generating code ad-hoc, and the
9
+ * emitted `INSTRUCTIONS` constant is byte-equal to the DNA-composed prompt.
10
+ *
11
+ * Model: the DNA coordinate is PRESERVED (unlike openai-agents, which strips the
12
+ * provider token) — `create_react_agent` resolves the string via `init_chat_model`,
13
+ * which takes a `provider:model` coordinate. Cases: `prompt-only` (no tools) and
14
+ * `with-tools` (the ReAct idiom — one `@tool` stub per DNA Tool). `structured-output`
15
+ * (LangGraph's `response_format`) is not shipped yet and falls back with a loss.
16
+ */
17
+ import { pyIdentifier, pyStrLiteral, ScaffoldEmitter } from "./scaffold.js";
18
+ export class LanggraphEmitter extends ScaffoldEmitter {
19
+ framework = "langgraph";
20
+ target = "langgraph";
21
+ fileExtension = "py";
22
+ renderContext(ctx, _case) {
23
+ const tools = ctx.tools.map((t) => ({
24
+ name: t.name,
25
+ func_name: pyIdentifier(t.name),
26
+ docstring_literal: pyStrLiteral(t.description || t.name),
27
+ }));
28
+ return {
29
+ has_model: ctx.model !== null,
30
+ model_literal: ctx.model ? pyStrLiteral(ctx.model) : "",
31
+ has_name: Boolean(ctx.name),
32
+ tools,
33
+ tool_list: tools.map((t) => t.func_name).join(", "),
34
+ };
35
+ }
36
+ losses(ctx, _choice) {
37
+ const out = [];
38
+ if (ctx.tools.length > 0) {
39
+ out.push("tool body — each `@tool` is a scaffolded STUB (name + " +
40
+ "`raise NotImplementedError`); its real implementation and typed signature " +
41
+ "must be wired (LangChain derives the tool schema from the function " +
42
+ "signature + docstring)");
43
+ }
44
+ if (ctx.model === null) {
45
+ out.push("model unbound in DNA and none supplied — `create_react_agent` REQUIRES a " +
46
+ "model; the emitted call omits `model=`, so supply one at wire-up");
47
+ }
48
+ else {
49
+ out.push("model coordinate — the DNA coordinate is carried verbatim; " +
50
+ "`create_react_agent` resolves it via `init_chat_model`, whose provider " +
51
+ "prefixes are `openai` / `anthropic` / `azure_openai` / `google_genai`; a " +
52
+ "DNA `azure/…` coordinate needs the `azure_openai:` prefix (or a model " +
53
+ "instance) at wire-up");
54
+ }
55
+ if (ctx.outputSchema) {
56
+ out.push("output_schema — map DNA's `spec.output_schema` to " +
57
+ "`create_react_agent(response_format=...)` (a Pydantic model) by hand; the " +
58
+ "scaffold does not synthesize the class");
59
+ }
60
+ return out;
61
+ }
62
+ mapping() {
63
+ return {
64
+ "buildPrompt (Soul+guardrails+instruction)": "INSTRUCTIONS constant (byte-equal)",
65
+ "metadata.name": "create_react_agent(name=...) (graph name)",
66
+ "spec.model / Genome.default_llm": "create_react_agent(model=...) (DNA coordinate preserved)",
67
+ "spec.tools[] (Tool Kind)": "@tool stubs → create_react_agent(tools=[...])",
68
+ };
69
+ }
70
+ }
@@ -0,0 +1,26 @@
1
+ /**
2
+ * DNA → **OpenAI Agents SDK** emitter (the first CODE-FIRST target; TS twin of
3
+ * python `dna.emit.openai_agents`).
4
+ *
5
+ * The OpenAI Agents SDK is code-first — you construct an agent in Python
6
+ * (`Agent(name=..., instructions=..., model=..., tools=[...])`) — so this target
7
+ * is a {@link ScaffoldEmitter}: it fills a curated `{openai-agents × case}`
8
+ * template rather than generating code ad-hoc, and the emitted `INSTRUCTIONS`
9
+ * constant is byte-equal to the DNA-composed prompt.
10
+ *
11
+ * Cases (selected from ctx signals): `prompt-only` (no tools) and `with-tools`
12
+ * (the ReAct idiom — one `@function_tool` stub per DNA Tool). `structured-output`
13
+ * is not shipped yet and falls back with a recorded loss.
14
+ */
15
+ import { ScaffoldEmitter, type ScaffoldChoice } from "./scaffold.js";
16
+ import type { EmitContext } from "./index.js";
17
+ /** Strip a DNA `prov:model` / `prov/model` coordinate to the bare id. */
18
+ export declare function bareModelId(model: string | null): string | null;
19
+ export declare class OpenAIAgentsEmitter extends ScaffoldEmitter {
20
+ readonly framework = "openai-agents";
21
+ readonly target = "openai-agents";
22
+ readonly fileExtension = "py";
23
+ renderContext(ctx: EmitContext, _case: string): Record<string, unknown>;
24
+ losses(ctx: EmitContext, _choice: ScaffoldChoice): string[];
25
+ mapping(): Record<string, string>;
26
+ }
@@ -0,0 +1,91 @@
1
+ /**
2
+ * DNA → **OpenAI Agents SDK** emitter (the first CODE-FIRST target; TS twin of
3
+ * python `dna.emit.openai_agents`).
4
+ *
5
+ * The OpenAI Agents SDK is code-first — you construct an agent in Python
6
+ * (`Agent(name=..., instructions=..., model=..., tools=[...])`) — so this target
7
+ * is a {@link ScaffoldEmitter}: it fills a curated `{openai-agents × case}`
8
+ * template rather than generating code ad-hoc, and the emitted `INSTRUCTIONS`
9
+ * constant is byte-equal to the DNA-composed prompt.
10
+ *
11
+ * Cases (selected from ctx signals): `prompt-only` (no tools) and `with-tools`
12
+ * (the ReAct idiom — one `@function_tool` stub per DNA Tool). `structured-output`
13
+ * is not shipped yet and falls back with a recorded loss.
14
+ */
15
+ import { pyIdentifier, pyStrLiteral, ScaffoldEmitter } from "./scaffold.js";
16
+ const DNA_PROVIDER_TOKENS = new Set([
17
+ "azure", "azureopenai", "azure_openai", "openai", "foundry", "azureaifoundry",
18
+ "vertex", "google", "gemini", "anthropic",
19
+ ]);
20
+ /** Strip a DNA `prov:model` / `prov/model` coordinate to the bare id. */
21
+ export function bareModelId(model) {
22
+ if (!model)
23
+ return null;
24
+ const ident = model.trim();
25
+ for (const sep of [":", "/"]) {
26
+ const i = ident.indexOf(sep);
27
+ if (i >= 0) {
28
+ const token = ident.slice(0, i).trim().toLowerCase();
29
+ if (DNA_PROVIDER_TOKENS.has(token))
30
+ return ident.slice(i + 1).trim();
31
+ }
32
+ }
33
+ return ident;
34
+ }
35
+ function isOpenAIModel(modelId) {
36
+ if (!modelId)
37
+ return false;
38
+ const m = modelId.trim().toLowerCase();
39
+ return m.startsWith("gpt-") || m.startsWith("o1") || m.startsWith("o3") || m.startsWith("o4");
40
+ }
41
+ export class OpenAIAgentsEmitter extends ScaffoldEmitter {
42
+ framework = "openai-agents";
43
+ target = "openai-agents";
44
+ fileExtension = "py";
45
+ renderContext(ctx, _case) {
46
+ const modelId = bareModelId(ctx.model);
47
+ const tools = ctx.tools.map((t) => ({
48
+ name: t.name,
49
+ func_name: pyIdentifier(t.name),
50
+ docstring_literal: pyStrLiteral(t.description || t.name),
51
+ }));
52
+ return {
53
+ has_model: modelId !== null,
54
+ model_literal: modelId ? pyStrLiteral(modelId) : "",
55
+ tools,
56
+ tool_list: tools.map((t) => t.func_name).join(", "),
57
+ };
58
+ }
59
+ losses(ctx, _choice) {
60
+ const out = [];
61
+ if (ctx.tools.length > 0) {
62
+ out.push("tool body — each `@function_tool` is a scaffolded STUB (name + " +
63
+ "`raise NotImplementedError`); its real implementation and typed " +
64
+ "signature must be wired (the OpenAI Agents SDK derives the tool schema " +
65
+ "from the Python function signature + docstring)");
66
+ }
67
+ const modelId = bareModelId(ctx.model);
68
+ if (modelId === null) {
69
+ out.push("model unbound in DNA and none supplied — emitted `Agent(...)` has no " +
70
+ "`model=`; the SDK falls back to its default");
71
+ }
72
+ else if (!isOpenAIModel(modelId)) {
73
+ out.push("model coordinate — the OpenAI Agents SDK `model=` takes an OpenAI model " +
74
+ "name or a `Model` object; a non-OpenAI coordinate needs a custom " +
75
+ "provider / `ModelSettings` at wire-up");
76
+ }
77
+ if (ctx.outputSchema) {
78
+ out.push("output_schema — map DNA's `spec.output_schema` to `Agent(output_type=...)` " +
79
+ "(a Pydantic/TypedDict class) by hand; the scaffold does not synthesize the class");
80
+ }
81
+ return out;
82
+ }
83
+ mapping() {
84
+ return {
85
+ "buildPrompt (Soul+guardrails+instruction)": "INSTRUCTIONS constant (byte-equal)",
86
+ "metadata.name": "Agent(name=...)",
87
+ "spec.model / Genome.default_llm": "Agent(model=...) (provider token stripped)",
88
+ "spec.tools[] (Tool Kind)": "@function_tool stubs → Agent(tools=[...])",
89
+ };
90
+ }
91
+ }