dna-sdk 0.9.0 → 0.10.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/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/index.d.ts +5 -1
- package/dist/index.js +4 -1
- package/package.json +1 -1
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
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { type EmitContext, type EmitResult, type EmitterPort } from "./index.js";
|
|
2
|
+
/** Render `value` as a Python string literal that round-trips exactly. JSON
|
|
3
|
+
* string syntax is a valid Python literal (`\n`, `\"`, `\\`, `\uXXXX`), single
|
|
4
|
+
* line — so the emitted `INSTRUCTIONS` constant is byte-equal-recoverable. */
|
|
5
|
+
export declare function pyStrLiteral(value: string): string;
|
|
6
|
+
/** `kb-search` → `kb_search` — a valid Python identifier for a tool stub. */
|
|
7
|
+
export declare function pyIdentifier(name: string): string;
|
|
8
|
+
/** The default case classifier — read the DNA signals the ctx already carries. */
|
|
9
|
+
export declare function classifyCase(ctx: EmitContext): string;
|
|
10
|
+
/** Resolve a `{framework × case}` template to its Mustache source — the abstract
|
|
11
|
+
* seam between an emitter and *where a template lives*. The MVP reads
|
|
12
|
+
* package-data ({@link PackageDataScaffoldResolver}); a future kernel-backed
|
|
13
|
+
* resolver returns a per-scope/per-tenant Scaffold Kind body, swappable with no
|
|
14
|
+
* emitter change. Returns null when the source has no template for the pair. */
|
|
15
|
+
export interface ScaffoldResolver {
|
|
16
|
+
resolve(framework: string, kase: string): string | null;
|
|
17
|
+
}
|
|
18
|
+
/** The MVP resolver: read `emit/scaffolds/<framework>/<case>.py.tmpl`. */
|
|
19
|
+
export declare class PackageDataScaffoldResolver implements ScaffoldResolver {
|
|
20
|
+
resolve(framework: string, kase: string): string | null;
|
|
21
|
+
}
|
|
22
|
+
/** Swap the active template resolver — the seam the Scaffold-as-Kind promotion
|
|
23
|
+
* (`s-scaffold-as-kind`) plugs into. */
|
|
24
|
+
export declare function setScaffoldResolver(resolver: ScaffoldResolver): ScaffoldResolver;
|
|
25
|
+
/** Resolve a `{framework × case}` template through the active resolver. */
|
|
26
|
+
export declare function resolveScaffold(framework: string, kase: string): string | null;
|
|
27
|
+
/** The outcome of {@link selectScaffold}: which case template to fill. */
|
|
28
|
+
export interface ScaffoldChoice {
|
|
29
|
+
/** The case actually selected (a template exists for it). */
|
|
30
|
+
case: string;
|
|
31
|
+
/** The template source (Mustache). */
|
|
32
|
+
template: string;
|
|
33
|
+
/** The case the classifier requested — differs from `case` on a fallback. */
|
|
34
|
+
requested: string;
|
|
35
|
+
}
|
|
36
|
+
/** Pick the `{framework × case}` template for `ctx`: classify, then resolve to a
|
|
37
|
+
* real template, falling back down the generality chain when needed. */
|
|
38
|
+
export declare function selectScaffold(framework: string, ctx: EmitContext, classify?: (ctx: EmitContext) => string, resolver?: ScaffoldResolver | null): ScaffoldChoice;
|
|
39
|
+
/** Base for a CODE-FIRST {@link EmitterPort}. A subclass is THIN — it declares the
|
|
40
|
+
* framework + target ids and supplies the framework-specific template variables
|
|
41
|
+
* ({@link renderContext}), losses, and field mapping; case selection, template
|
|
42
|
+
* fill, and the byte-equal invariant hook are inherited. */
|
|
43
|
+
export declare abstract class ScaffoldEmitter implements EmitterPort {
|
|
44
|
+
abstract readonly framework: string;
|
|
45
|
+
abstract readonly target: string;
|
|
46
|
+
readonly fileExtension: string;
|
|
47
|
+
/** Optional template-resolution override (defaults to the active resolver —
|
|
48
|
+
* package-data today, a Scaffold Kind resolver tomorrow). */
|
|
49
|
+
readonly resolver: ScaffoldResolver | null;
|
|
50
|
+
/** Framework-specific template variables (merged over the common ones). */
|
|
51
|
+
renderContext(_ctx: EmitContext, _case: string): Record<string, unknown>;
|
|
52
|
+
/** Framework-specific de-para losses (in addition to the common ones). */
|
|
53
|
+
losses(_ctx: EmitContext, _choice: ScaffoldChoice): string[];
|
|
54
|
+
/** Field-level de-para (`dnaField -> targetField`) for reporting. */
|
|
55
|
+
mapping(): Record<string, string>;
|
|
56
|
+
/** The case classifier — override to add a framework-specific case. */
|
|
57
|
+
classify(ctx: EmitContext): string;
|
|
58
|
+
private commonContext;
|
|
59
|
+
private commonLosses;
|
|
60
|
+
emit(ctx: EmitContext): EmitResult;
|
|
61
|
+
/** Byte-equal invariant hook: read the top-level `INSTRUCTIONS = <literal>`
|
|
62
|
+
* constant every scaffold template emits (a JSON/Python string literal on one
|
|
63
|
+
* line), uniform across all code-first targets regardless of constructor shape. */
|
|
64
|
+
extractInstructions(artifact: string): string | null;
|
|
65
|
+
}
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `emit/scaffold` — the CODE-FIRST flavor of the {@link EmitterPort} (TS twin of
|
|
3
|
+
* python `dna.emit.scaffold`).
|
|
4
|
+
*
|
|
5
|
+
* Some runtimes have NO declarative agent format — you construct an agent object
|
|
6
|
+
* in code (OpenAI Agents SDK, LangGraph, Agno, DeepAgents all expose a simple
|
|
7
|
+
* constructor). For those the emitter must produce SOURCE CODE, and it does so by
|
|
8
|
+
* **filling a curated template**, never by generating code ad-hoc — the template
|
|
9
|
+
* captures the framework's best-practice idiom; the emitter only routes and fills.
|
|
10
|
+
*
|
|
11
|
+
* The mechanism is a template library indexed by `{framework × case}` plus a tiny
|
|
12
|
+
* case classifier:
|
|
13
|
+
*
|
|
14
|
+
* scaffolds/<framework>/<case>.py.tmpl // curated best-practice idiom per case
|
|
15
|
+
* selectScaffold(framework, ctx) // inspect ctx's DNA signals → pick a case
|
|
16
|
+
*
|
|
17
|
+
* There is deliberately NO single "one template per framework": a prompt-only, a
|
|
18
|
+
* tool-calling (ReAct), and a structured-output agent are DIFFERENT structures in
|
|
19
|
+
* the SAME framework. The classifier reads the neutral {@link EmitContext} signals
|
|
20
|
+
* (no tools → `prompt-only`; tools → `with-tools`; `outputSchema` →
|
|
21
|
+
* `structured-output`) and falls back down a generality chain when a framework
|
|
22
|
+
* does not ship a case, recording the fallback as a loss.
|
|
23
|
+
*
|
|
24
|
+
* Future direction — Scaffold as a Kind: templates are read through an abstract
|
|
25
|
+
* seam, {@link resolveScaffold} (a {@link ScaffoldResolver}), NOT a hardcoded file
|
|
26
|
+
* path. The MVP resolver reads package-data, but the seam lets a second source
|
|
27
|
+
* plug in with no change to any emitter — a first-class **Scaffold Kind** resolved
|
|
28
|
+
* by the kernel (scope-aware, tenant-overridable). Tracked as `s-scaffold-as-kind`.
|
|
29
|
+
*/
|
|
30
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
31
|
+
import { dirname, join } from "node:path";
|
|
32
|
+
import { fileURLToPath } from "node:url";
|
|
33
|
+
import Mustache from "mustache";
|
|
34
|
+
import { EmitError } from "./index.js";
|
|
35
|
+
const HERE = dirname(fileURLToPath(import.meta.url));
|
|
36
|
+
/** The generality fallback chain (specific → general) per requested case. */
|
|
37
|
+
const FALLBACK = {
|
|
38
|
+
"structured-output": ["structured-output", "with-tools", "prompt-only"],
|
|
39
|
+
"with-tools": ["with-tools", "prompt-only"],
|
|
40
|
+
"prompt-only": ["prompt-only"],
|
|
41
|
+
};
|
|
42
|
+
/** Render `value` as a Python string literal that round-trips exactly. JSON
|
|
43
|
+
* string syntax is a valid Python literal (`\n`, `\"`, `\\`, `\uXXXX`), single
|
|
44
|
+
* line — so the emitted `INSTRUCTIONS` constant is byte-equal-recoverable. */
|
|
45
|
+
export function pyStrLiteral(value) {
|
|
46
|
+
return JSON.stringify(value);
|
|
47
|
+
}
|
|
48
|
+
/** `kb-search` → `kb_search` — a valid Python identifier for a tool stub. */
|
|
49
|
+
export function pyIdentifier(name) {
|
|
50
|
+
let ident = String(name)
|
|
51
|
+
.trim()
|
|
52
|
+
.replace(/[^0-9a-zA-Z_]/g, "_")
|
|
53
|
+
.replace(/^_+|_+$/g, "");
|
|
54
|
+
if (!ident)
|
|
55
|
+
ident = "tool";
|
|
56
|
+
if (/^[0-9]/.test(ident))
|
|
57
|
+
ident = `_${ident}`;
|
|
58
|
+
return ident;
|
|
59
|
+
}
|
|
60
|
+
/** The default case classifier — read the DNA signals the ctx already carries. */
|
|
61
|
+
export function classifyCase(ctx) {
|
|
62
|
+
if (ctx.outputSchema)
|
|
63
|
+
return "structured-output";
|
|
64
|
+
if (ctx.tools.length > 0)
|
|
65
|
+
return "with-tools";
|
|
66
|
+
return "prompt-only";
|
|
67
|
+
}
|
|
68
|
+
/** The MVP resolver: read `emit/scaffolds/<framework>/<case>.py.tmpl`. */
|
|
69
|
+
export class PackageDataScaffoldResolver {
|
|
70
|
+
resolve(framework, kase) {
|
|
71
|
+
const path = join(HERE, "scaffolds", framework, `${kase}.py.tmpl`);
|
|
72
|
+
return existsSync(path) ? readFileSync(path, "utf-8") : null;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
let activeResolver = new PackageDataScaffoldResolver();
|
|
76
|
+
/** Swap the active template resolver — the seam the Scaffold-as-Kind promotion
|
|
77
|
+
* (`s-scaffold-as-kind`) plugs into. */
|
|
78
|
+
export function setScaffoldResolver(resolver) {
|
|
79
|
+
activeResolver = resolver;
|
|
80
|
+
return resolver;
|
|
81
|
+
}
|
|
82
|
+
/** Resolve a `{framework × case}` template through the active resolver. */
|
|
83
|
+
export function resolveScaffold(framework, kase) {
|
|
84
|
+
return activeResolver.resolve(framework, kase);
|
|
85
|
+
}
|
|
86
|
+
/** Pick the `{framework × case}` template for `ctx`: classify, then resolve to a
|
|
87
|
+
* real template, falling back down the generality chain when needed. */
|
|
88
|
+
export function selectScaffold(framework, ctx, classify = classifyCase, resolver = null) {
|
|
89
|
+
const resolve = resolver
|
|
90
|
+
? (f, k) => resolver.resolve(f, k)
|
|
91
|
+
: resolveScaffold;
|
|
92
|
+
const requested = classify(ctx);
|
|
93
|
+
for (const kase of FALLBACK[requested] ?? [requested]) {
|
|
94
|
+
const template = resolve(framework, kase);
|
|
95
|
+
if (template !== null)
|
|
96
|
+
return { case: kase, template, requested };
|
|
97
|
+
}
|
|
98
|
+
throw new EmitError(`no scaffold template for framework '${framework}' ` +
|
|
99
|
+
`(looked for case '${requested}' and its fallbacks)`);
|
|
100
|
+
}
|
|
101
|
+
/** Base for a CODE-FIRST {@link EmitterPort}. A subclass is THIN — it declares the
|
|
102
|
+
* framework + target ids and supplies the framework-specific template variables
|
|
103
|
+
* ({@link renderContext}), losses, and field mapping; case selection, template
|
|
104
|
+
* fill, and the byte-equal invariant hook are inherited. */
|
|
105
|
+
export class ScaffoldEmitter {
|
|
106
|
+
fileExtension = "py";
|
|
107
|
+
/** Optional template-resolution override (defaults to the active resolver —
|
|
108
|
+
* package-data today, a Scaffold Kind resolver tomorrow). */
|
|
109
|
+
resolver = null;
|
|
110
|
+
// ── the hooks a subclass overrides ────────────────────────────────────────
|
|
111
|
+
/** Framework-specific template variables (merged over the common ones). */
|
|
112
|
+
renderContext(_ctx, _case) {
|
|
113
|
+
return {};
|
|
114
|
+
}
|
|
115
|
+
/** Framework-specific de-para losses (in addition to the common ones). */
|
|
116
|
+
losses(_ctx, _choice) {
|
|
117
|
+
return [];
|
|
118
|
+
}
|
|
119
|
+
/** Field-level de-para (`dnaField -> targetField`) for reporting. */
|
|
120
|
+
mapping() {
|
|
121
|
+
return {};
|
|
122
|
+
}
|
|
123
|
+
/** The case classifier — override to add a framework-specific case. */
|
|
124
|
+
classify(ctx) {
|
|
125
|
+
return classifyCase(ctx);
|
|
126
|
+
}
|
|
127
|
+
// ── the inherited machinery ───────────────────────────────────────────────
|
|
128
|
+
commonContext(ctx) {
|
|
129
|
+
return {
|
|
130
|
+
name: ctx.name,
|
|
131
|
+
name_literal: pyStrLiteral(ctx.name),
|
|
132
|
+
description: ctx.description,
|
|
133
|
+
has_description: Boolean(ctx.description),
|
|
134
|
+
// INSTRUCTIONS constant — byte-equal, recoverable via extractInstructions.
|
|
135
|
+
instructions_literal: pyStrLiteral(ctx.instructions),
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
commonLosses(ctx, choice) {
|
|
139
|
+
const losses = [
|
|
140
|
+
"composition structure — Soul reuse + wired Guardrails flatten to one " +
|
|
141
|
+
"`INSTRUCTIONS` string (a code-first agent has no `soul:`/`guardrails:` slot)",
|
|
142
|
+
"tenant overlay — a per-tenant persona without a fork has no code-first field",
|
|
143
|
+
"eval-as-contract — prompt invariants (EvalCases) have no code-first slot",
|
|
144
|
+
];
|
|
145
|
+
if (choice.case !== choice.requested) {
|
|
146
|
+
losses.push(`scaffold case — the '${choice.requested}' idiom is not shipped for ` +
|
|
147
|
+
`'${this.framework}'; fell back to the '${choice.case}' template ` +
|
|
148
|
+
"(structure closest to but not identical to the requested case)");
|
|
149
|
+
}
|
|
150
|
+
return losses;
|
|
151
|
+
}
|
|
152
|
+
emit(ctx) {
|
|
153
|
+
const choice = selectScaffold(this.framework, ctx, (c) => this.classify(c), this.resolver);
|
|
154
|
+
const variables = { ...this.commonContext(ctx), ...this.renderContext(ctx, choice.case) };
|
|
155
|
+
const artifact = Mustache.render(choice.template, variables);
|
|
156
|
+
const losses = [...this.commonLosses(ctx, choice), ...this.losses(ctx, choice)];
|
|
157
|
+
return {
|
|
158
|
+
artifact,
|
|
159
|
+
target: this.target,
|
|
160
|
+
filename: `${ctx.name}.${this.fileExtension}`,
|
|
161
|
+
losses,
|
|
162
|
+
mapping: this.mapping(),
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
/** Byte-equal invariant hook: read the top-level `INSTRUCTIONS = <literal>`
|
|
166
|
+
* constant every scaffold template emits (a JSON/Python string literal on one
|
|
167
|
+
* line), uniform across all code-first targets regardless of constructor shape. */
|
|
168
|
+
extractInstructions(artifact) {
|
|
169
|
+
const match = artifact.match(/^INSTRUCTIONS = (.+)$/m);
|
|
170
|
+
if (!match)
|
|
171
|
+
return null;
|
|
172
|
+
try {
|
|
173
|
+
return JSON.parse(match[1]);
|
|
174
|
+
}
|
|
175
|
+
catch {
|
|
176
|
+
return null;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"""DNA-emitted Agno agent: {{name}}.
|
|
2
|
+
|
|
3
|
+
Generated by `dna emit --target agno`. INSTRUCTIONS is the DNA-composed prompt
|
|
4
|
+
(Soul + guardrails + instruction), carried byte-equal from the source of truth —
|
|
5
|
+
edit the agent in DNA and re-emit, do not hand-edit the prompt here.
|
|
6
|
+
"""
|
|
7
|
+
from agno.agent import Agent
|
|
8
|
+
|
|
9
|
+
INSTRUCTIONS = {{{instructions_literal}}}
|
|
10
|
+
|
|
11
|
+
agent = Agent(
|
|
12
|
+
name={{{name_literal}}},
|
|
13
|
+
{{#has_model}} model={{{model_literal}}},
|
|
14
|
+
{{/has_model}} instructions=INSTRUCTIONS,
|
|
15
|
+
)
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"""DNA-emitted Agno agent (tool-calling): {{name}}.
|
|
2
|
+
|
|
3
|
+
Generated by `dna emit --target agno`. INSTRUCTIONS is the DNA-composed prompt
|
|
4
|
+
(Soul + guardrails + instruction), carried byte-equal. Each tool below is a STUB
|
|
5
|
+
scaffolded from a DNA Tool's name + description — wire its real implementation and
|
|
6
|
+
typed signature (Agno derives the tool schema from the function + docstring).
|
|
7
|
+
"""
|
|
8
|
+
from agno.agent import Agent
|
|
9
|
+
|
|
10
|
+
INSTRUCTIONS = {{{instructions_literal}}}
|
|
11
|
+
|
|
12
|
+
{{#tools}}
|
|
13
|
+
def {{func_name}}() -> str:
|
|
14
|
+
{{{docstring_literal}}}
|
|
15
|
+
raise NotImplementedError("DNA scaffold: implement the {{name}} tool")
|
|
16
|
+
|
|
17
|
+
{{/tools}}
|
|
18
|
+
agent = Agent(
|
|
19
|
+
name={{{name_literal}}},
|
|
20
|
+
{{#has_model}} model={{{model_literal}}},
|
|
21
|
+
{{/has_model}} instructions=INSTRUCTIONS,
|
|
22
|
+
tools=[{{tool_list}}],
|
|
23
|
+
)
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"""DNA-emitted DeepAgents agent: {{name}}.
|
|
2
|
+
|
|
3
|
+
Generated by `dna emit --target deepagents`. INSTRUCTIONS is the DNA-composed
|
|
4
|
+
prompt (Soul + guardrails + instruction), carried byte-equal from the source of
|
|
5
|
+
truth — edit the agent in DNA and re-emit, do not hand-edit the prompt here.
|
|
6
|
+
`system_prompt` sits in FRONT of the deep-agent's built-in harness prompt.
|
|
7
|
+
"""
|
|
8
|
+
from deepagents import create_deep_agent
|
|
9
|
+
|
|
10
|
+
INSTRUCTIONS = {{{instructions_literal}}}
|
|
11
|
+
|
|
12
|
+
agent = create_deep_agent(
|
|
13
|
+
{{#has_model}} model={{{model_literal}}},
|
|
14
|
+
{{/has_model}} tools=[],
|
|
15
|
+
system_prompt=INSTRUCTIONS,
|
|
16
|
+
)
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"""DNA-emitted DeepAgents agent (tool-calling): {{name}}.
|
|
2
|
+
|
|
3
|
+
Generated by `dna emit --target deepagents`. INSTRUCTIONS is the DNA-composed
|
|
4
|
+
prompt (Soul + guardrails + instruction), carried byte-equal. Each tool below is a
|
|
5
|
+
STUB scaffolded from a DNA Tool's name + description — wire its real implementation
|
|
6
|
+
and typed signature. `system_prompt` sits in FRONT of the deep-agent harness prompt.
|
|
7
|
+
"""
|
|
8
|
+
from deepagents import create_deep_agent
|
|
9
|
+
|
|
10
|
+
INSTRUCTIONS = {{{instructions_literal}}}
|
|
11
|
+
|
|
12
|
+
{{#tools}}
|
|
13
|
+
def {{func_name}}() -> str:
|
|
14
|
+
{{{docstring_literal}}}
|
|
15
|
+
raise NotImplementedError("DNA scaffold: implement the {{name}} tool")
|
|
16
|
+
|
|
17
|
+
{{/tools}}
|
|
18
|
+
agent = create_deep_agent(
|
|
19
|
+
{{#has_model}} model={{{model_literal}}},
|
|
20
|
+
{{/has_model}} tools=[{{tool_list}}],
|
|
21
|
+
system_prompt=INSTRUCTIONS,
|
|
22
|
+
)
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"""DNA-emitted LangGraph ReAct agent: {{name}}.
|
|
2
|
+
|
|
3
|
+
Generated by `dna emit --target langgraph`. INSTRUCTIONS is the DNA-composed
|
|
4
|
+
prompt (Soul + guardrails + instruction), carried byte-equal from the source of
|
|
5
|
+
truth — edit the agent in DNA and re-emit, do not hand-edit the prompt here.
|
|
6
|
+
"""
|
|
7
|
+
from langgraph.prebuilt import create_react_agent
|
|
8
|
+
|
|
9
|
+
INSTRUCTIONS = {{{instructions_literal}}}
|
|
10
|
+
|
|
11
|
+
agent = create_react_agent(
|
|
12
|
+
{{#has_model}} model={{{model_literal}}},
|
|
13
|
+
{{/has_model}} tools=[],
|
|
14
|
+
prompt=INSTRUCTIONS,
|
|
15
|
+
{{#has_name}} name={{{name_literal}}},
|
|
16
|
+
{{/has_name}})
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"""DNA-emitted LangGraph ReAct agent (tool-calling): {{name}}.
|
|
2
|
+
|
|
3
|
+
Generated by `dna emit --target langgraph`. INSTRUCTIONS is the DNA-composed
|
|
4
|
+
prompt (Soul + guardrails + instruction), carried byte-equal. Each @tool below is
|
|
5
|
+
a STUB scaffolded from a DNA Tool's name + description — wire its real
|
|
6
|
+
implementation and typed signature (LangChain derives the tool schema from them).
|
|
7
|
+
"""
|
|
8
|
+
from langchain_core.tools import tool
|
|
9
|
+
from langgraph.prebuilt import create_react_agent
|
|
10
|
+
|
|
11
|
+
INSTRUCTIONS = {{{instructions_literal}}}
|
|
12
|
+
|
|
13
|
+
{{#tools}}
|
|
14
|
+
@tool
|
|
15
|
+
def {{func_name}}() -> str:
|
|
16
|
+
{{{docstring_literal}}}
|
|
17
|
+
raise NotImplementedError("DNA scaffold: implement the {{name}} tool")
|
|
18
|
+
|
|
19
|
+
{{/tools}}
|
|
20
|
+
agent = create_react_agent(
|
|
21
|
+
{{#has_model}} model={{{model_literal}}},
|
|
22
|
+
{{/has_model}} tools=[{{tool_list}}],
|
|
23
|
+
prompt=INSTRUCTIONS,
|
|
24
|
+
{{#has_name}} name={{{name_literal}}},
|
|
25
|
+
{{/has_name}})
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"""DNA-emitted OpenAI Agents SDK agent: {{name}}.
|
|
2
|
+
|
|
3
|
+
Generated by `dna emit --target openai-agents`. INSTRUCTIONS is the DNA-composed
|
|
4
|
+
prompt (Soul + guardrails + instruction), carried byte-equal from the source of
|
|
5
|
+
truth — edit the agent in DNA and re-emit, do not hand-edit the prompt here.
|
|
6
|
+
"""
|
|
7
|
+
from agents import Agent
|
|
8
|
+
|
|
9
|
+
INSTRUCTIONS = {{{instructions_literal}}}
|
|
10
|
+
|
|
11
|
+
agent = Agent(
|
|
12
|
+
name={{{name_literal}}},
|
|
13
|
+
instructions=INSTRUCTIONS,
|
|
14
|
+
{{#has_model}} model={{{model_literal}}},
|
|
15
|
+
{{/has_model}})
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"""DNA-emitted OpenAI Agents SDK agent (tool-calling / ReAct): {{name}}.
|
|
2
|
+
|
|
3
|
+
Generated by `dna emit --target openai-agents`. INSTRUCTIONS is the DNA-composed
|
|
4
|
+
prompt (Soul + guardrails + instruction), carried byte-equal. Each @function_tool
|
|
5
|
+
below is a STUB scaffolded from a DNA Tool's name + description — wire its real
|
|
6
|
+
implementation and typed signature (the SDK derives the tool schema from them).
|
|
7
|
+
"""
|
|
8
|
+
from agents import Agent, function_tool
|
|
9
|
+
|
|
10
|
+
INSTRUCTIONS = {{{instructions_literal}}}
|
|
11
|
+
|
|
12
|
+
{{#tools}}
|
|
13
|
+
@function_tool
|
|
14
|
+
def {{func_name}}() -> str:
|
|
15
|
+
{{{docstring_literal}}}
|
|
16
|
+
raise NotImplementedError("DNA scaffold: implement the {{name}} tool")
|
|
17
|
+
|
|
18
|
+
{{/tools}}
|
|
19
|
+
agent = Agent(
|
|
20
|
+
name={{{name_literal}}},
|
|
21
|
+
instructions=INSTRUCTIONS,
|
|
22
|
+
{{#has_model}} model={{{model_literal}}},
|
|
23
|
+
{{/has_model}} tools=[{{tool_list}}],
|
|
24
|
+
)
|
package/dist/emit/vertex.d.ts
CHANGED
|
@@ -16,4 +16,7 @@ export declare class VertexEmitter implements EmitterPort {
|
|
|
16
16
|
* time, not part of this object. */
|
|
17
17
|
toAgentConfig(ctx: EmitContext): Record<string, unknown>;
|
|
18
18
|
emit(ctx: EmitContext): EmitResult;
|
|
19
|
+
/** Byte-equal invariant hook: read `instruction` back from the emitted ADK
|
|
20
|
+
* Agent Config YAML (the `# yaml-language-server` header is a YAML comment). */
|
|
21
|
+
extractInstructions(artifact: string): string | null;
|
|
19
22
|
}
|
package/dist/emit/vertex.js
CHANGED
|
@@ -152,4 +152,11 @@ export class VertexEmitter {
|
|
|
152
152
|
mapping,
|
|
153
153
|
};
|
|
154
154
|
}
|
|
155
|
+
/** Byte-equal invariant hook: read `instruction` back from the emitted ADK
|
|
156
|
+
* Agent Config YAML (the `# yaml-language-server` header is a YAML comment). */
|
|
157
|
+
extractInstructions(artifact) {
|
|
158
|
+
const config = yaml.load(artifact);
|
|
159
|
+
const value = config?.instruction;
|
|
160
|
+
return typeof value === "string" ? value : null;
|
|
161
|
+
}
|
|
155
162
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -26,10 +26,14 @@ 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";
|
|
29
|
+
export { emitAgent, buildEmitContext, registerEmitter, unregisterEmitter, getEmitter, availableTargets, EmitError, UnknownTarget, } from "./emit/index.js";
|
|
30
30
|
export type { EmitContext, EmitResult, EmitTool, EmitterPort, BuildEmitContextOpts, } from "./emit/index.js";
|
|
31
31
|
export { AgentFrameworkEmitter } from "./emit/agentFramework.js";
|
|
32
32
|
export { BedrockEmitter } from "./emit/bedrock.js";
|
|
33
|
+
export { VertexEmitter } from "./emit/vertex.js";
|
|
34
|
+
export { OpenAIAgentsEmitter } from "./emit/openaiAgents.js";
|
|
35
|
+
export { ScaffoldEmitter, selectScaffold, classifyCase, resolveScaffold, setScaffoldResolver, PackageDataScaffoldResolver, } from "./emit/scaffold.js";
|
|
36
|
+
export type { ScaffoldChoice, ScaffoldResolver } from "./emit/scaffold.js";
|
|
33
37
|
export type { LoadPromptsOptions } from "./prompts.js";
|
|
34
38
|
export { anchorScopesRoot, PackageScopeNotFound, DEFAULT_SUBPATH } from "./package-scope.js";
|
|
35
39
|
export { loadConfig, findConfig, CONFIG_FILENAME } from "./config.js";
|
package/dist/index.js
CHANGED
|
@@ -30,9 +30,12 @@ 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";
|
|
33
|
+
export { emitAgent, buildEmitContext, registerEmitter, unregisterEmitter, getEmitter, availableTargets, EmitError, UnknownTarget, } from "./emit/index.js";
|
|
34
34
|
export { AgentFrameworkEmitter } from "./emit/agentFramework.js";
|
|
35
35
|
export { BedrockEmitter } from "./emit/bedrock.js";
|
|
36
|
+
export { VertexEmitter } from "./emit/vertex.js";
|
|
37
|
+
export { OpenAIAgentsEmitter } from "./emit/openaiAgents.js";
|
|
38
|
+
export { ScaffoldEmitter, selectScaffold, classifyCase, resolveScaffold, setScaffoldResolver, PackageDataScaffoldResolver, } from "./emit/scaffold.js";
|
|
36
39
|
export { anchorScopesRoot, PackageScopeNotFound, DEFAULT_SUBPATH } from "./package-scope.js";
|
|
37
40
|
export { loadConfig, findConfig, CONFIG_FILENAME } from "./config.js";
|
|
38
41
|
export { sourceFromUrl, resolveDefaultFsUrl, UnsupportedSourceScheme } from "./adapters/source-url.js";
|