dna-sdk 0.5.0 → 0.7.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.
@@ -7,7 +7,8 @@ import yaml from "js-yaml";
7
7
  import { join as pathJoin } from "node:path";
8
8
  import { nodeFS, readTextSafe, collectDir } from "../kernel/fs.js";
9
9
  import { KindBase } from "../kernel/kind_base.js";
10
- import { AgentSchema, ActorSchema, UseCaseSchema, ToolSchema, AgentSpecSchema, ActorSpecSchema, UseCaseSpecSchema, ToolSpecSchema, GenomeSchema, GenomeSpecSchema, LayerPolicySchema, LayerPolicySpecSchema, zodSpecToJsonSchema } from "../kernel/models.js";
10
+ import { AgentSchema, ActorSchema, UseCaseSchema, AgentSpecSchema, ActorSpecSchema, UseCaseSpecSchema, GenomeSchema, GenomeSpecSchema, LayerPolicySchema, LayerPolicySpecSchema, zodSpecToJsonSchema } from "../kernel/models.js";
11
+ import { loadDescriptors } from "../kernel/descriptor-loader.js";
11
12
  import { SD } from "../kernel/protocols.js";
12
13
  import { readSpecString, readSpecStringArray } from "../kernel/spec-access.js";
13
14
  import { SettingKind, ThemeKind, UserProfileKind, CanvasKind } from "./helix_extras.js";
@@ -529,102 +530,6 @@ class UseCaseKind extends KindBase {
529
530
  }
530
531
  }
531
532
  // ---------------------------------------------------------------------------
532
- // ToolKind
533
- // ---------------------------------------------------------------------------
534
- class ToolKind extends KindBase {
535
- apiVersion = "github.com/ruinosus/dna/v1";
536
- kind = "Tool";
537
- alias = "helix-tool";
538
- isSchemaAffecting = true;
539
- origin = "github.com/ruinosus/dna/tool";
540
- isPromptTarget = false;
541
- promptTargetPriority = 0;
542
- flattenInContext = false;
543
- storage = SD.yaml("tools");
544
- graphStyle = { fill: "#14B8A6", stroke: "#0D9488", textColor: "#fff" };
545
- asciiIcon = "🔧";
546
- displayLabel = "Tools";
547
- _sourceUrl = MOD_URL;
548
- docs = "A Tool is a declarative, invocable capability an agent can call: an " +
549
- "HTTP endpoint, an MCP server tool, a Python callable, a shell command, " +
550
- "or a builtin. Bridges helix with OpenAI/Anthropic tool-calling " +
551
- "conventions. Each Tool declares an input/output JSON Schema, an auth " +
552
- "strategy, and read_only / requires_confirmation flags that the harness " +
553
- "honors at runtime. Agents reference Tools via dep_filters.tools. " +
554
- "Stored as tools/<name>.yaml — marketplace-shareable as standalone bundles.";
555
- uiSchema = {
556
- type: { widget: "select", label: "Invocation type", help: "How the tool is executed: http | mcp | python | shell | builtin.", order: 10 },
557
- endpoint: { widget: "text", label: "HTTP endpoint", help: "URL called when type=http. Supports {placeholder} templating.", order: 20 },
558
- method: { widget: "select", label: "HTTP method", order: 25 },
559
- mcp_server: { widget: "text", label: "MCP server", help: "Server name when type=mcp.", order: 30 },
560
- mcp_tool: { widget: "text", label: "MCP tool name", order: 35 },
561
- python_module: { widget: "text", label: "Python module", help: "Dotted import path when type=python.", order: 40 },
562
- python_callable: { widget: "text", label: "Python callable", help: "Attribute on the module (function or class).", order: 45 },
563
- shell_command: { widget: "textarea", label: "Shell command", help: "Command template when type=shell. Never executed without confirmation.", order: 50 },
564
- input_schema: {
565
- widget: "code",
566
- language: "yaml",
567
- label: "Input schema (JSON Schema)",
568
- help: "Validates the arguments the agent passes when invoking the tool.",
569
- height: 260,
570
- order: 60,
571
- },
572
- output_schema: {
573
- widget: "code",
574
- language: "yaml",
575
- label: "Output schema (JSON Schema)",
576
- help: "Describes the shape of the tool's response.",
577
- height: 220,
578
- order: 70,
579
- },
580
- auth_type: { widget: "select", label: "Auth type", help: "none | api_key | bearer | oauth2.", order: 80 },
581
- auth_env_var: { widget: "text", label: "Auth env var", help: "Environment variable holding the credential.", order: 85 },
582
- read_only: { widget: "checkbox", label: "Read-only", help: "Uncheck if the tool mutates state.", order: 90 },
583
- requires_confirmation: { widget: "checkbox", label: "Requires confirmation", help: "Force user approval before each invocation.", order: 95 },
584
- tags: { widget: "tags", label: "Tags", order: 100 },
585
- examples: { widget: "readonly", label: "Examples", help: "Usage examples. Edit via YAML for now.", order: 110 },
586
- };
587
- schema() { return zodSpecToJsonSchema(ToolSpecSchema); }
588
- parse(raw) {
589
- return ToolSchema.parse(raw);
590
- }
591
- summary() { return null; }
592
- preview(doc) {
593
- const spec = (doc.spec ?? {});
594
- const fields = [];
595
- if (typeof spec.type === "string")
596
- fields.push({ label: "type", value: spec.type });
597
- if (typeof spec.endpoint === "string")
598
- fields.push({ label: "endpoint", value: spec.endpoint });
599
- if (typeof spec.method === "string")
600
- fields.push({ label: "method", value: spec.method });
601
- if (typeof spec.mcp_server === "string")
602
- fields.push({ label: "mcp_server", value: spec.mcp_server });
603
- if (typeof spec.mcp_tool === "string")
604
- fields.push({ label: "mcp_tool", value: spec.mcp_tool });
605
- if (typeof spec.python_module === "string")
606
- fields.push({ label: "python_module", value: spec.python_module });
607
- if (typeof spec.python_callable === "string")
608
- fields.push({ label: "python_callable", value: spec.python_callable });
609
- if (typeof spec.shell_command === "string")
610
- fields.push({ label: "shell_command", value: spec.shell_command });
611
- if (spec.input_schema)
612
- fields.push({ label: "input_schema", value: JSON.stringify(spec.input_schema, null, 2) });
613
- if (spec.output_schema)
614
- fields.push({ label: "output_schema", value: JSON.stringify(spec.output_schema, null, 2) });
615
- if (typeof spec.auth_type === "string")
616
- fields.push({ label: "auth", value: spec.auth_type });
617
- if (spec.read_only != null)
618
- fields.push({ label: "read_only", value: String(spec.read_only) });
619
- if (spec.requires_confirmation != null)
620
- fields.push({ label: "requires_confirmation", value: String(spec.requires_confirmation) });
621
- if (fields.length === 0) {
622
- return [{ kind: "empty", title: `Tool ${doc.name}` }];
623
- }
624
- return [{ kind: "fields", title: `Tool ${doc.name}`, fields }];
625
- }
626
- }
627
- // ---------------------------------------------------------------------------
628
533
  // AgentReader / AgentWriter
629
534
  // ---------------------------------------------------------------------------
630
535
  const KNOWN_DIRS = new Set(["scripts", "references", "assets"]);
@@ -985,9 +890,15 @@ export class HelixExtension {
985
890
  kernel.kind(new GenomeKind());
986
891
  kernel.kind(new LayerPolicyKind());
987
892
  kernel.kind(new AgentKind());
988
- kernel.kind(new ToolKind());
989
893
  kernel.kind(new ActorKind());
990
894
  kernel.kind(new UseCaseKind());
895
+ // Tool (helix-tool) ships as a descriptor — helix/kinds/tool.kind.yaml
896
+ // (f-dna-tools-as-data / s-tool-kind-descriptor). It WAS a hand-written
897
+ // ToolKind class; migrated to a record-plane descriptor per the repo's
898
+ // own ratchet (record Kinds are data, not classes).
899
+ for (const raw of loadDescriptors(import.meta.url, "helix/kinds")) {
900
+ kernel.kindFromDescriptor(raw);
901
+ }
991
902
  // 2026-05-26 — absorbed from claude-code-templates catalog (MIT).
992
903
  // Setting rounds out the Claude-Code-customization primitives that
993
904
  // live alongside Skill / UA / Soul / Tool.
package/dist/index.d.ts CHANGED
@@ -22,8 +22,15 @@ export { ReportBuilder } from "./kernel/reports.js";
22
22
  export { serializeRawToFiles } from "./kernel/serialize-to-files.js";
23
23
  export * from "./viz/index.js";
24
24
  export { createKernelWithBuiltins, quickInstance, createRuntimeWithBuiltins, quickManifest, fromConfig } from "./bootstrap.js";
25
- export { AgentNotFound, UnknownLayout } from "./kernel/errors.js";
25
+ export { AgentNotFound, ToolNotFound, UnknownLayout } from "./kernel/errors.js";
26
26
  export { PromptLibrary, loadPrompts } from "./prompts.js";
27
+ export { ToolLibrary, loadTools } from "./tools.js";
28
+ export type { ToolSurface } from "./tools.js";
29
+ export { emitAgent, buildEmitContext, registerEmitter, getEmitter, availableTargets, EmitError, UnknownTarget, } from "./emit/index.js";
30
+ export type { EmitContext, EmitResult, EmitTool, EmitterPort, BuildEmitContextOpts, } from "./emit/index.js";
31
+ export { AgentFrameworkEmitter } from "./emit/agentFramework.js";
32
+ export type { LoadPromptsOptions } from "./prompts.js";
33
+ export { anchorScopesRoot, PackageScopeNotFound, DEFAULT_SUBPATH } from "./package-scope.js";
27
34
  export { loadConfig, findConfig, CONFIG_FILENAME } from "./config.js";
28
35
  export type { DnaConfig, SearchMode, EmbeddingMode } from "./config.js";
29
36
  export { sourceFromUrl, resolveDefaultFsUrl, UnsupportedSourceScheme } from "./adapters/source-url.js";
package/dist/index.js CHANGED
@@ -27,8 +27,12 @@ export * from "./viz/index.js";
27
27
  export { createKernelWithBuiltins, quickInstance, createRuntimeWithBuiltins, quickManifest, fromConfig } from "./bootstrap.js";
28
28
  // DX consumer surface (s-dx-*): fail-loud prompt building + the collapse-the-
29
29
  // shim helper + declarative port wiring.
30
- export { AgentNotFound, UnknownLayout } from "./kernel/errors.js";
30
+ export { AgentNotFound, ToolNotFound, UnknownLayout } from "./kernel/errors.js";
31
31
  export { PromptLibrary, loadPrompts } from "./prompts.js";
32
+ export { ToolLibrary, loadTools } from "./tools.js";
33
+ export { emitAgent, buildEmitContext, registerEmitter, getEmitter, availableTargets, EmitError, UnknownTarget, } from "./emit/index.js";
34
+ export { AgentFrameworkEmitter } from "./emit/agentFramework.js";
35
+ export { anchorScopesRoot, PackageScopeNotFound, DEFAULT_SUBPATH } from "./package-scope.js";
32
36
  export { loadConfig, findConfig, CONFIG_FILENAME } from "./config.js";
33
37
  export { sourceFromUrl, resolveDefaultFsUrl, UnsupportedSourceScheme } from "./adapters/source-url.js";
34
38
  export { HookRegistry, KNOWN_HOOK_NAMES } from "./kernel/hooks.js";
@@ -31,6 +31,24 @@ export declare class AgentNotFound extends Error {
31
31
  readonly agent: string | null;
32
32
  constructor(agent: string | null);
33
33
  }
34
+ /**
35
+ * `loadTools(scope).get(name)` was asked for a Tool that no `Tool` document in
36
+ * the scope declares (missing, renamed, or in another scope).
37
+ *
38
+ * Fail-loud contract (s-load-tools-helper), the twin of `AgentNotFound`: the
39
+ * agent-facing tool surface is data, so a miss must throw a typed error —
40
+ * never return an empty surface that would silently reach a model as a tool
41
+ * with no description.
42
+ *
43
+ * 1:1 parity with python `dna.ToolNotFound` (a `LookupError` subclass).
44
+ * Exported publicly from the package root.
45
+ */
46
+ export declare class ToolNotFound extends Error {
47
+ readonly toolName: string | null;
48
+ readonly scope: string | null;
49
+ readonly available: string[];
50
+ constructor(name: string | null, scope?: string | null, available?: string[]);
51
+ }
34
52
  /**
35
53
  * `buildPrompt` hit an Agent whose `layout:` names a preset the Kind does not
36
54
  * offer (s-dx-named-layouts).
@@ -36,6 +36,33 @@ export class AgentNotFound extends Error {
36
36
  Object.setPrototypeOf(this, new.target.prototype);
37
37
  }
38
38
  }
39
+ /**
40
+ * `loadTools(scope).get(name)` was asked for a Tool that no `Tool` document in
41
+ * the scope declares (missing, renamed, or in another scope).
42
+ *
43
+ * Fail-loud contract (s-load-tools-helper), the twin of `AgentNotFound`: the
44
+ * agent-facing tool surface is data, so a miss must throw a typed error —
45
+ * never return an empty surface that would silently reach a model as a tool
46
+ * with no description.
47
+ *
48
+ * 1:1 parity with python `dna.ToolNotFound` (a `LookupError` subclass).
49
+ * Exported publicly from the package root.
50
+ */
51
+ export class ToolNotFound extends Error {
52
+ toolName;
53
+ scope;
54
+ available;
55
+ constructor(name, scope = null, available = []) {
56
+ const where = scope ? ` in scope '${scope}'` : "";
57
+ const hint = available.length ? ` — available: ${available.join(", ")}` : "";
58
+ super(`Tool '${name}' not found${where}${hint}`);
59
+ this.name = "ToolNotFound";
60
+ this.toolName = name;
61
+ this.scope = scope;
62
+ this.available = available;
63
+ Object.setPrototypeOf(this, new.target.prototype);
64
+ }
65
+ }
39
66
  /**
40
67
  * `buildPrompt` hit an Agent whose `layout:` names a preset the Kind does not
41
68
  * offer (s-dx-named-layouts).
@@ -115,7 +115,9 @@ export function generateAlias(owner, kind) {
115
115
  export const EXPLICIT_ALIAS_ALLOWLIST = new Set([
116
116
  // helix
117
117
  "helix-genome", "helix-agent", "helix-actor",
118
- "helix-usecase", "helix-tool", "policy-layer-policy",
118
+ // helix-tool migrated to a descriptor (s-tool-kind-descriptor): its alias
119
+ // lives in helix/kinds/tool.kind.yaml (parity-critical) — shrink-only.
120
+ "helix-usecase", "policy-layer-policy",
119
121
  "helix-canvas",
120
122
  "helix-setting", "helix-theme", "helix-user-profile",
121
123
  // sdlc (classes; descriptors are outside the ratchet)
@@ -1166,197 +1166,6 @@ export declare const UseCaseSchema: z.ZodObject<{
1166
1166
  } | undefined;
1167
1167
  }>;
1168
1168
  export type TypedUseCase = z.output<typeof UseCaseSchema>;
1169
- export declare const ToolTypeEnum: z.ZodEnum<["http", "mcp", "python", "shell", "builtin"]>;
1170
- export declare const ToolAuthTypeEnum: z.ZodEnum<["none", "api_key", "bearer", "oauth2"]>;
1171
- export declare const ToolSpecSchema: z.ZodObject<{
1172
- type: z.ZodDefault<z.ZodEnum<["http", "mcp", "python", "shell", "builtin"]>>;
1173
- endpoint: z.ZodDefault<z.ZodOptional<z.ZodString>>;
1174
- method: z.ZodDefault<z.ZodOptional<z.ZodString>>;
1175
- mcp_server: z.ZodDefault<z.ZodOptional<z.ZodString>>;
1176
- mcp_tool: z.ZodDefault<z.ZodOptional<z.ZodString>>;
1177
- python_module: z.ZodDefault<z.ZodOptional<z.ZodString>>;
1178
- python_callable: z.ZodDefault<z.ZodOptional<z.ZodString>>;
1179
- shell_command: z.ZodDefault<z.ZodOptional<z.ZodString>>;
1180
- input_schema: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
1181
- output_schema: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
1182
- auth_type: z.ZodDefault<z.ZodEnum<["none", "api_key", "bearer", "oauth2"]>>;
1183
- auth_env_var: z.ZodDefault<z.ZodOptional<z.ZodString>>;
1184
- read_only: z.ZodDefault<z.ZodBoolean>;
1185
- requires_confirmation: z.ZodDefault<z.ZodBoolean>;
1186
- tags: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
1187
- examples: z.ZodDefault<z.ZodArray<z.ZodRecord<z.ZodString, z.ZodUnknown>, "many">>;
1188
- }, "strip", z.ZodTypeAny, {
1189
- type: "http" | "mcp" | "python" | "shell" | "builtin";
1190
- tags: string[];
1191
- input_schema: Record<string, unknown>;
1192
- endpoint: string;
1193
- method: string;
1194
- mcp_server: string;
1195
- mcp_tool: string;
1196
- python_module: string;
1197
- python_callable: string;
1198
- shell_command: string;
1199
- output_schema: Record<string, unknown>;
1200
- auth_type: "none" | "api_key" | "bearer" | "oauth2";
1201
- auth_env_var: string;
1202
- read_only: boolean;
1203
- requires_confirmation: boolean;
1204
- examples: Record<string, unknown>[];
1205
- }, {
1206
- type?: "http" | "mcp" | "python" | "shell" | "builtin" | undefined;
1207
- tags?: string[] | undefined;
1208
- input_schema?: Record<string, unknown> | undefined;
1209
- endpoint?: string | undefined;
1210
- method?: string | undefined;
1211
- mcp_server?: string | undefined;
1212
- mcp_tool?: string | undefined;
1213
- python_module?: string | undefined;
1214
- python_callable?: string | undefined;
1215
- shell_command?: string | undefined;
1216
- output_schema?: Record<string, unknown> | undefined;
1217
- auth_type?: "none" | "api_key" | "bearer" | "oauth2" | undefined;
1218
- auth_env_var?: string | undefined;
1219
- read_only?: boolean | undefined;
1220
- requires_confirmation?: boolean | undefined;
1221
- examples?: Record<string, unknown>[] | undefined;
1222
- }>;
1223
- export declare const ToolSchema: z.ZodObject<{
1224
- apiVersion: z.ZodLiteral<"github.com/ruinosus/dna/v1">;
1225
- kind: z.ZodLiteral<"Tool">;
1226
- metadata: z.ZodObject<{
1227
- name: z.ZodString;
1228
- description: z.ZodDefault<z.ZodOptional<z.ZodString>>;
1229
- version: z.ZodDefault<z.ZodOptional<z.ZodString>>;
1230
- icon: z.ZodDefault<z.ZodOptional<z.ZodString>>;
1231
- group: z.ZodDefault<z.ZodOptional<z.ZodString>>;
1232
- labels: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodString>>;
1233
- }, "strip", z.ZodTypeAny, {
1234
- name: string;
1235
- description: string;
1236
- version: string;
1237
- icon: string;
1238
- group: string;
1239
- labels: Record<string, string>;
1240
- }, {
1241
- name: string;
1242
- description?: string | undefined;
1243
- version?: string | undefined;
1244
- icon?: string | undefined;
1245
- group?: string | undefined;
1246
- labels?: Record<string, string> | undefined;
1247
- }>;
1248
- spec: z.ZodDefault<z.ZodObject<{
1249
- type: z.ZodDefault<z.ZodEnum<["http", "mcp", "python", "shell", "builtin"]>>;
1250
- endpoint: z.ZodDefault<z.ZodOptional<z.ZodString>>;
1251
- method: z.ZodDefault<z.ZodOptional<z.ZodString>>;
1252
- mcp_server: z.ZodDefault<z.ZodOptional<z.ZodString>>;
1253
- mcp_tool: z.ZodDefault<z.ZodOptional<z.ZodString>>;
1254
- python_module: z.ZodDefault<z.ZodOptional<z.ZodString>>;
1255
- python_callable: z.ZodDefault<z.ZodOptional<z.ZodString>>;
1256
- shell_command: z.ZodDefault<z.ZodOptional<z.ZodString>>;
1257
- input_schema: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
1258
- output_schema: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
1259
- auth_type: z.ZodDefault<z.ZodEnum<["none", "api_key", "bearer", "oauth2"]>>;
1260
- auth_env_var: z.ZodDefault<z.ZodOptional<z.ZodString>>;
1261
- read_only: z.ZodDefault<z.ZodBoolean>;
1262
- requires_confirmation: z.ZodDefault<z.ZodBoolean>;
1263
- tags: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
1264
- examples: z.ZodDefault<z.ZodArray<z.ZodRecord<z.ZodString, z.ZodUnknown>, "many">>;
1265
- }, "strip", z.ZodTypeAny, {
1266
- type: "http" | "mcp" | "python" | "shell" | "builtin";
1267
- tags: string[];
1268
- input_schema: Record<string, unknown>;
1269
- endpoint: string;
1270
- method: string;
1271
- mcp_server: string;
1272
- mcp_tool: string;
1273
- python_module: string;
1274
- python_callable: string;
1275
- shell_command: string;
1276
- output_schema: Record<string, unknown>;
1277
- auth_type: "none" | "api_key" | "bearer" | "oauth2";
1278
- auth_env_var: string;
1279
- read_only: boolean;
1280
- requires_confirmation: boolean;
1281
- examples: Record<string, unknown>[];
1282
- }, {
1283
- type?: "http" | "mcp" | "python" | "shell" | "builtin" | undefined;
1284
- tags?: string[] | undefined;
1285
- input_schema?: Record<string, unknown> | undefined;
1286
- endpoint?: string | undefined;
1287
- method?: string | undefined;
1288
- mcp_server?: string | undefined;
1289
- mcp_tool?: string | undefined;
1290
- python_module?: string | undefined;
1291
- python_callable?: string | undefined;
1292
- shell_command?: string | undefined;
1293
- output_schema?: Record<string, unknown> | undefined;
1294
- auth_type?: "none" | "api_key" | "bearer" | "oauth2" | undefined;
1295
- auth_env_var?: string | undefined;
1296
- read_only?: boolean | undefined;
1297
- requires_confirmation?: boolean | undefined;
1298
- examples?: Record<string, unknown>[] | undefined;
1299
- }>>;
1300
- }, "strip", z.ZodTypeAny, {
1301
- metadata: {
1302
- name: string;
1303
- description: string;
1304
- version: string;
1305
- icon: string;
1306
- group: string;
1307
- labels: Record<string, string>;
1308
- };
1309
- spec: {
1310
- type: "http" | "mcp" | "python" | "shell" | "builtin";
1311
- tags: string[];
1312
- input_schema: Record<string, unknown>;
1313
- endpoint: string;
1314
- method: string;
1315
- mcp_server: string;
1316
- mcp_tool: string;
1317
- python_module: string;
1318
- python_callable: string;
1319
- shell_command: string;
1320
- output_schema: Record<string, unknown>;
1321
- auth_type: "none" | "api_key" | "bearer" | "oauth2";
1322
- auth_env_var: string;
1323
- read_only: boolean;
1324
- requires_confirmation: boolean;
1325
- examples: Record<string, unknown>[];
1326
- };
1327
- apiVersion: "github.com/ruinosus/dna/v1";
1328
- kind: "Tool";
1329
- }, {
1330
- metadata: {
1331
- name: string;
1332
- description?: string | undefined;
1333
- version?: string | undefined;
1334
- icon?: string | undefined;
1335
- group?: string | undefined;
1336
- labels?: Record<string, string> | undefined;
1337
- };
1338
- apiVersion: "github.com/ruinosus/dna/v1";
1339
- kind: "Tool";
1340
- spec?: {
1341
- type?: "http" | "mcp" | "python" | "shell" | "builtin" | undefined;
1342
- tags?: string[] | undefined;
1343
- input_schema?: Record<string, unknown> | undefined;
1344
- endpoint?: string | undefined;
1345
- method?: string | undefined;
1346
- mcp_server?: string | undefined;
1347
- mcp_tool?: string | undefined;
1348
- python_module?: string | undefined;
1349
- python_callable?: string | undefined;
1350
- shell_command?: string | undefined;
1351
- output_schema?: Record<string, unknown> | undefined;
1352
- auth_type?: "none" | "api_key" | "bearer" | "oauth2" | undefined;
1353
- auth_env_var?: string | undefined;
1354
- read_only?: boolean | undefined;
1355
- requires_confirmation?: boolean | undefined;
1356
- examples?: Record<string, unknown>[] | undefined;
1357
- } | undefined;
1358
- }>;
1359
- export type TypedTool = z.output<typeof ToolSchema>;
1360
1169
  export declare const SkillSpecSchema: z.ZodObject<{
1361
1170
  instruction: z.ZodDefault<z.ZodOptional<z.ZodString>>;
1362
1171
  scripts: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodString>>;
@@ -335,38 +335,6 @@ export const UseCaseSchema = z.object({
335
335
  spec: UseCaseSpecSchema.default({}),
336
336
  });
337
337
  // ---------------------------------------------------------------------------
338
- // Tool (github.com/ruinosus/dna/v1)
339
- //
340
- // Declarative, invocable capability an agent can call. Bridges helix with
341
- // OpenAI/Anthropic tool-calling conventions.
342
- // ---------------------------------------------------------------------------
343
- export const ToolTypeEnum = z.enum(["http", "mcp", "python", "shell", "builtin"]);
344
- export const ToolAuthTypeEnum = z.enum(["none", "api_key", "bearer", "oauth2"]);
345
- export const ToolSpecSchema = z.object({
346
- type: ToolTypeEnum.default("builtin"),
347
- endpoint: z.string().optional().default(""),
348
- method: z.string().optional().default("POST"),
349
- mcp_server: z.string().optional().default(""),
350
- mcp_tool: z.string().optional().default(""),
351
- python_module: z.string().optional().default(""),
352
- python_callable: z.string().optional().default(""),
353
- shell_command: z.string().optional().default(""),
354
- input_schema: z.record(z.unknown()).default({}),
355
- output_schema: z.record(z.unknown()).default({}),
356
- auth_type: ToolAuthTypeEnum.default("none"),
357
- auth_env_var: z.string().optional().default(""),
358
- read_only: z.boolean().default(true),
359
- requires_confirmation: z.boolean().default(false),
360
- tags: z.array(z.string()).default([]),
361
- examples: z.array(z.record(z.unknown())).default([]),
362
- });
363
- export const ToolSchema = z.object({
364
- apiVersion: z.literal("github.com/ruinosus/dna/v1"),
365
- kind: z.literal("Tool"),
366
- metadata: MetadataSchema,
367
- spec: ToolSpecSchema.default({}),
368
- });
369
- // ---------------------------------------------------------------------------
370
338
  // Skill (agentskills.io/v1)
371
339
  // ---------------------------------------------------------------------------
372
340
  export const SkillSpecSchema = z.object({
@@ -0,0 +1,18 @@
1
+ /** The conventional scopes-root sub-directory inside a package (matches the
2
+ * repo's own `.dna/<scope>/` layout). */
3
+ export declare const DEFAULT_SUBPATH = ".dna";
4
+ /** Raised when an `anchor` package / subpath can't be resolved to a real
5
+ * on-disk scopes-root directory. */
6
+ export declare class PackageScopeNotFound extends Error {
7
+ constructor(message: string);
8
+ }
9
+ /**
10
+ * Resolve the `.dna` scopes-root embedded in package `anchor`.
11
+ *
12
+ * `anchor` is a resolvable package specifier (e.g. `"app"`); `subpath` is the
13
+ * scopes-root dir inside it (default `.dna`). Returns the concrete filesystem
14
+ * path of `<anchor-package>/<subpath>` — the `baseDir` a FilesystemSource
15
+ * consumes. Fails loud (`PackageScopeNotFound`) when the package cannot be
16
+ * resolved or the subpath does not exist in the installed package.
17
+ */
18
+ export declare function anchorScopesRoot(anchor: string, subpath?: string): string;
@@ -0,0 +1,87 @@
1
+ /**
2
+ * Resolve a DNA scope embedded as PACKAGE DATA (`s-scope-as-package-data`).
3
+ *
4
+ * 1:1 parity with python `dna/package_scope.py`. A consumer that deploys an
5
+ * app used to make the scope travel by hand — a brittle
6
+ * `path.resolve(__dirname, "../../.dna")` plus a manual `COPY .dna` in the
7
+ * Dockerfile. The image is the *app*, not the repo; forget the COPY and the
8
+ * app boots with no scope. This module owns the deploy-safe alternative:
9
+ * resolve the scope from INSIDE the installed package.
10
+ *
11
+ * The TS mechanism mirrors how the SDK finds its OWN bundled `*.kind.yaml`
12
+ * (see `kernel/descriptor-loader.ts`): resolve relative to a module. Here the
13
+ * anchor is a package NAME, so we resolve its `package.json` via
14
+ * `createRequire(import.meta.url)` — the package root is that file's dir —
15
+ * then join the scopes-root subpath (`.dna` by default). `npm`/`bun`/`pnpm`
16
+ * install the package UNPACKED into `node_modules`, and the package's `files`
17
+ * field carries the scope into the published tarball and into a Docker image,
18
+ * so resolution works from a source checkout, an installed dependency, and a
19
+ * container whose CWD is not the repo — zero path navigation, zero manual copy.
20
+ *
21
+ * Read-only by nature: package data is composition input, never a write
22
+ * target. To WRITE a scope, use a filesystem or postgres source.
23
+ */
24
+ import { createRequire } from "node:module";
25
+ import { existsSync } from "node:fs";
26
+ import { dirname, isAbsolute, join } from "node:path";
27
+ /** The conventional scopes-root sub-directory inside a package (matches the
28
+ * repo's own `.dna/<scope>/` layout). */
29
+ export const DEFAULT_SUBPATH = ".dna";
30
+ /** Raised when an `anchor` package / subpath can't be resolved to a real
31
+ * on-disk scopes-root directory. */
32
+ export class PackageScopeNotFound extends Error {
33
+ constructor(message) {
34
+ super(message);
35
+ this.name = "PackageScopeNotFound";
36
+ Object.setPrototypeOf(this, new.target.prototype);
37
+ }
38
+ }
39
+ const require = createRequire(import.meta.url);
40
+ /**
41
+ * Resolve the `.dna` scopes-root embedded in package `anchor`.
42
+ *
43
+ * `anchor` is a resolvable package specifier (e.g. `"app"`); `subpath` is the
44
+ * scopes-root dir inside it (default `.dna`). Returns the concrete filesystem
45
+ * path of `<anchor-package>/<subpath>` — the `baseDir` a FilesystemSource
46
+ * consumes. Fails loud (`PackageScopeNotFound`) when the package cannot be
47
+ * resolved or the subpath does not exist in the installed package.
48
+ */
49
+ export function anchorScopesRoot(anchor, subpath = DEFAULT_SUBPATH) {
50
+ const pkgRoot = resolvePackageRoot(anchor);
51
+ const base = subpath ? join(pkgRoot, subpath) : pkgRoot;
52
+ if (!existsSync(base)) {
53
+ throw new PackageScopeNotFound(`anchor '${anchor}' is installed, but its scopes-root '${subpath}' was ` +
54
+ `not found at ${base}. Declare the scope files as package data so ` +
55
+ `they ship in the tarball/image: add the scopes dir to the package's ` +
56
+ `"files" array in package.json (e.g. "files": ["dist", "${subpath}"]). ` +
57
+ `See the guide "Shipping a scope with your app".`);
58
+ }
59
+ return base;
60
+ }
61
+ /** Locate a package's root dir from its name, via its `package.json`. */
62
+ function resolvePackageRoot(anchor) {
63
+ // Primary: resolve the package's package.json — its dir IS the package root.
64
+ // (A package can gate this behind "exports"; the example adds the standard
65
+ // `"./package.json": "./package.json"` so it always resolves.)
66
+ try {
67
+ return dirname(require.resolve(join(anchor, "package.json")));
68
+ }
69
+ catch {
70
+ // Fallback: an absolute/relative path anchor (a directory, not a package
71
+ // name) — treat it as the package root directly.
72
+ if (isAbsolute(anchor) && existsSync(anchor))
73
+ return anchor;
74
+ try {
75
+ // Last resort: resolve the package entry point and use its dir. Only
76
+ // reliable for a flat single-file package, but better than nothing.
77
+ return dirname(require.resolve(anchor));
78
+ }
79
+ catch (err) {
80
+ throw new PackageScopeNotFound(`anchor '${anchor}' is not resolvable — it must be an INSTALLED ` +
81
+ `package that embeds the scope as package data (npm/bun install it, ` +
82
+ `and list the scopes dir in the package's "files" so it ships). ` +
83
+ `If it uses "exports", add "./package.json": "./package.json". ` +
84
+ `Original resolve error: ${err.message}`);
85
+ }
86
+ }
87
+ }
package/dist/prompts.d.ts CHANGED
@@ -17,11 +17,34 @@ export declare class PromptLibrary {
17
17
  /** Names of every prompt-target document in the scope, sorted. */
18
18
  names(): string[];
19
19
  }
20
+ /** Options for {@link loadPrompts}. */
21
+ export interface LoadPromptsOptions {
22
+ /**
23
+ * The directory that holds `<scope>/` (the `.dna` scopes root), following
24
+ * the {@link quickInstance} convention.
25
+ */
26
+ baseDir?: string;
27
+ /**
28
+ * A package specifier whose package data embeds the scope. When given, the
29
+ * scope is resolved from INSIDE the installed package (via its
30
+ * `package.json`), so it TRAVELS with the app — an `npm`/`bun install`
31
+ * carries the package data into the published tarball and into a Docker
32
+ * image, and resolution works identically from a source checkout, an
33
+ * installed dependency, or a container whose CWD is not the repo (no
34
+ * `path.resolve(__dirname, "../..")` navigation, no manual `COPY .dna`).
35
+ * A scope embedded via `anchor` is READ-ONLY. See the guide "Shipping a
36
+ * scope with your app".
37
+ */
38
+ anchor?: string;
39
+ }
20
40
  /**
21
41
  * Compose the prompts of `scope` behind a {@link PromptLibrary}.
22
42
  *
23
- * `baseDir` follows the {@link quickInstance} convention the directory that
24
- * holds `<scope>/` (the `.dna` scopes root). Omitted → the `DNA_BASE_DIR` env
25
- * var, then `.dna` in the cwd.
43
+ * Precedence for the scopes-root (first one set wins):
44
+ *
45
+ * `opts.baseDir` > `$DNA_BASE_DIR` > `opts.anchor` (package data) > `.dna`
46
+ *
47
+ * The legacy positional-string form (`loadPrompts(scope, "/path/.dna")`) is
48
+ * still accepted for back-compat and is treated as `baseDir`.
26
49
  */
27
- export declare function loadPrompts(scope: string, baseDir?: string): Promise<PromptLibrary>;
50
+ export declare function loadPrompts(scope: string, opts?: string | LoadPromptsOptions): Promise<PromptLibrary>;