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.
- package/dist/bootstrap.js +4 -0
- package/dist/config.d.ts +7 -0
- package/dist/config.js +10 -1
- package/dist/emit/agentFramework.d.ts +2 -0
- package/dist/emit/agentFramework.js +6 -0
- package/dist/emit/agno.d.ts +25 -0
- package/dist/emit/agno.js +65 -0
- package/dist/emit/bedrock.d.ts +3 -0
- package/dist/emit/bedrock.js +9 -0
- package/dist/emit/deepagents.d.ts +25 -0
- package/dist/emit/deepagents.js +71 -0
- package/dist/emit/index.d.ts +33 -2
- package/dist/emit/index.js +19 -1
- package/dist/emit/langgraph.d.ts +26 -0
- package/dist/emit/langgraph.js +70 -0
- package/dist/emit/openaiAgents.d.ts +26 -0
- package/dist/emit/openaiAgents.js +91 -0
- package/dist/emit/scaffold.d.ts +65 -0
- package/dist/emit/scaffold.js +179 -0
- package/dist/emit/scaffolds/agno/prompt-only.py.tmpl +15 -0
- package/dist/emit/scaffolds/agno/with-tools.py.tmpl +23 -0
- package/dist/emit/scaffolds/deepagents/prompt-only.py.tmpl +16 -0
- package/dist/emit/scaffolds/deepagents/with-tools.py.tmpl +22 -0
- package/dist/emit/scaffolds/langgraph/prompt-only.py.tmpl +16 -0
- package/dist/emit/scaffolds/langgraph/with-tools.py.tmpl +25 -0
- package/dist/emit/scaffolds/openai-agents/prompt-only.py.tmpl +15 -0
- package/dist/emit/scaffolds/openai-agents/with-tools.py.tmpl +24 -0
- package/dist/emit/vertex.d.ts +3 -0
- package/dist/emit/vertex.js +7 -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/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/sdlc/kinds/lesson-learned.kind.yaml +0 -1
- package/dist/index.d.ts +5 -1
- package/dist/index.js +4 -1
- package/dist/kernel/index.d.ts +44 -0
- package/dist/kernel/index.js +85 -0
- 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
|
+
}
|
package/dist/emit/bedrock.d.ts
CHANGED
|
@@ -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
|
}
|
package/dist/emit/bedrock.js
CHANGED
|
@@ -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
|
+
}
|
package/dist/emit/index.d.ts
CHANGED
|
@@ -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
|
|
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. */
|
package/dist/emit/index.js
CHANGED
|
@@ -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
|
-
|
|
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
|
+
}
|