dna-sdk 0.5.0 → 0.6.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/adapters/source-url.d.ts +3 -0
- package/dist/adapters/source-url.js +28 -1
- package/dist/extensions/helix/kinds/tool.kind.yaml +240 -0
- package/dist/extensions/helix.js +9 -98
- package/dist/index.d.ts +5 -1
- package/dist/index.js +3 -1
- package/dist/kernel/errors.d.ts +18 -0
- package/dist/kernel/errors.js +27 -0
- package/dist/kernel/kind-registry.js +3 -1
- package/dist/kernel/models.d.ts +0 -191
- package/dist/kernel/models.js +0 -32
- package/dist/package-scope.d.ts +18 -0
- package/dist/package-scope.js +87 -0
- package/dist/prompts.d.ts +27 -4
- package/dist/prompts.js +21 -6
- package/dist/tools.d.ts +41 -0
- package/dist/tools.js +88 -0
- package/package.json +1 -1
|
@@ -10,6 +10,9 @@
|
|
|
10
10
|
* file:// <path> → FilesystemSource (read/write on disk)
|
|
11
11
|
* fs:// <path> → alias of file://
|
|
12
12
|
* <plain path> → treated as file://<path>
|
|
13
|
+
* pkg://<pkg>[/sub] → FilesystemSource (READ-ONLY) over a scope embedded
|
|
14
|
+
* as PACKAGE DATA of <pkg> (sub defaults to .dna);
|
|
15
|
+
* travels with the app (tarball / Docker)
|
|
13
16
|
* postgresql:// … → PostgresSource (node-postgres)
|
|
14
17
|
* postgres:// … → alias of postgresql://
|
|
15
18
|
* sqlite:// <path> → NOT SUPPORTED in the TS runtime (Python-only; the
|
|
@@ -16,6 +16,22 @@ function schemeOf(url) {
|
|
|
16
16
|
const m = /^([a-zA-Z][a-zA-Z0-9+.-]*):/.exec(url);
|
|
17
17
|
return (m ? m[1] : "file").toLowerCase();
|
|
18
18
|
}
|
|
19
|
+
/**
|
|
20
|
+
* Split `pkg://<package>[/<subpath>]` into `{ pkg, subpath }`. Parity with the
|
|
21
|
+
* python `_parse_pkg_url`: `pkg://app` → `{pkg:"app", subpath:""}`;
|
|
22
|
+
* `pkg://app/.dna` → `{pkg:"app", subpath:".dna"}`. The package is the netloc
|
|
23
|
+
* (a dotted `pkg://my.app` stays `my.app`).
|
|
24
|
+
*/
|
|
25
|
+
function parsePkgUrl(url) {
|
|
26
|
+
const m = /^pkg:\/\/([^/]*)(?:\/(.*))?$/.exec(url);
|
|
27
|
+
const pkg = m?.[1] ?? "";
|
|
28
|
+
if (!pkg) {
|
|
29
|
+
throw new UnsupportedSourceScheme(`pkg:// source URL is missing a package name — use ` +
|
|
30
|
+
`pkg://<package>[/<subpath>] (e.g. pkg://app or pkg://app/.dna). ` +
|
|
31
|
+
`Got: ${url}`);
|
|
32
|
+
}
|
|
33
|
+
return { pkg, subpath: m?.[2] ?? "" };
|
|
34
|
+
}
|
|
19
35
|
/**
|
|
20
36
|
* Build a source from a scheme URL (see module docstring).
|
|
21
37
|
*
|
|
@@ -29,6 +45,16 @@ export async function sourceFromUrl(url, opts = {}) {
|
|
|
29
45
|
const { FilesystemSource } = await import("./filesystem/source.js");
|
|
30
46
|
return new FilesystemSource(urlToFsPath(url) || url);
|
|
31
47
|
}
|
|
48
|
+
if (scheme === "pkg") {
|
|
49
|
+
// A scope embedded as PACKAGE DATA — resolve it from inside the installed
|
|
50
|
+
// package so it travels with the app (tarball / Docker), no path
|
|
51
|
+
// navigation and no manual copy. READ-ONLY: FilesystemSource has no write
|
|
52
|
+
// surface here (to write, use file:// or postgresql://).
|
|
53
|
+
const { FilesystemSource } = await import("./filesystem/source.js");
|
|
54
|
+
const { anchorScopesRoot, DEFAULT_SUBPATH } = await import("../package-scope.js");
|
|
55
|
+
const { pkg, subpath } = parsePkgUrl(url);
|
|
56
|
+
return new FilesystemSource(anchorScopesRoot(pkg, subpath || DEFAULT_SUBPATH));
|
|
57
|
+
}
|
|
32
58
|
if (base === "postgresql" || base === "postgres") {
|
|
33
59
|
const { PostgresSource } = await import("./postgres/source.js");
|
|
34
60
|
const src = new PostgresSource({ connectionString: url, schema: opts.schema });
|
|
@@ -41,7 +67,8 @@ export async function sourceFromUrl(url, opts = {}) {
|
|
|
41
67
|
`(filesystem) or postgresql:// here. Got: ${url}`);
|
|
42
68
|
}
|
|
43
69
|
throw new UnsupportedSourceScheme(`unsupported source URL scheme '${scheme}://' — the TS runtime ships ` +
|
|
44
|
-
`file:// (filesystem)
|
|
70
|
+
`file:// (filesystem), pkg:// (read-only package-data scope) and ` +
|
|
71
|
+
`postgresql:// adapters. Got: ${url}`);
|
|
45
72
|
}
|
|
46
73
|
/**
|
|
47
74
|
* The `file://` URL the SDK falls back to with no explicit config.
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
# Tool — a declarative, invocable capability an agent can call (record plane).
|
|
2
|
+
#
|
|
3
|
+
# "Tools as data" (f-dna-tools-as-data). The DNA already governs persona,
|
|
4
|
+
# instruction and guardrails declaratively; a Tool moves the AGENT-FACING
|
|
5
|
+
# surface of a tool into the same declarative plane — the ``description`` the
|
|
6
|
+
# model reads to decide whether to call it (``metadata.description``) and the
|
|
7
|
+
# ``parameters`` JSON Schema of its arguments (``spec.input_schema``). One
|
|
8
|
+
# source of truth, versioned, testable, and overridable per tenant, served
|
|
9
|
+
# identically to a Python backend (`@tool`) and a TypeScript frontend
|
|
10
|
+
# (CopilotKit `useCopilotAction`) via ``dna.load_tools`` / ``loadTools``.
|
|
11
|
+
#
|
|
12
|
+
# F3 migration (s-tool-kind-descriptor): this WAS a hand-written ``ToolKind``
|
|
13
|
+
# class (helix/__init__.py + helix.ts) with a ``TypedTool``/``ToolSpec``
|
|
14
|
+
# model. It is now a ``kinds/tool.kind.yaml`` descriptor — a record Kind
|
|
15
|
+
# expressed as data, per the repo's own ratchet (record Kinds are
|
|
16
|
+
# descriptors, never classes). The invocation surface (type/endpoint/mcp/
|
|
17
|
+
# python/shell + input/output schema + auth + read_only/requires_confirmation)
|
|
18
|
+
# is preserved verbatim; only the IMPLEMENTATION moved class → descriptor and
|
|
19
|
+
# the plane moved composition → record (a Tool is not a prompt target and
|
|
20
|
+
# never composes into an agent prompt, so it carries no composition signal —
|
|
21
|
+
# writing a Tool no longer invalidates the composition schema cache).
|
|
22
|
+
#
|
|
23
|
+
# Agents reference Tools by name via ``dep_filters.tools`` (unchanged — the
|
|
24
|
+
# alias ``helix-tool`` is stable). Stored as ``tools/<name>.yaml`` —
|
|
25
|
+
# marketplace-shareable as standalone bundles.
|
|
26
|
+
#
|
|
27
|
+
# tenant_scope intentionally NOT declared — permissive (base Tool + optional
|
|
28
|
+
# per-tenant overlay). A tenant may legitimately override a tool's
|
|
29
|
+
# description/parameters (the SaaS value hook); the base stays intact and the
|
|
30
|
+
# máxima "inheritable ⇒ never tenanted" holds.
|
|
31
|
+
#
|
|
32
|
+
# PARITY-CRITICAL package data: byte-identical mirror under both runtimes'
|
|
33
|
+
# helix/kinds/ (tests/test_descriptor_hash_parity.py enforces).
|
|
34
|
+
apiVersion: github.com/ruinosus/dna/core/v1
|
|
35
|
+
kind: KindDefinition
|
|
36
|
+
metadata:
|
|
37
|
+
name: tool
|
|
38
|
+
spec:
|
|
39
|
+
target_api_version: github.com/ruinosus/dna/v1
|
|
40
|
+
target_kind: Tool
|
|
41
|
+
alias: helix-tool
|
|
42
|
+
origin: github.com/ruinosus/dna/tool
|
|
43
|
+
plane: record
|
|
44
|
+
prompt_target: false
|
|
45
|
+
flatten_in_context: false
|
|
46
|
+
prompt_target_priority: 0
|
|
47
|
+
storage:
|
|
48
|
+
type: yaml
|
|
49
|
+
container: tools
|
|
50
|
+
schema:
|
|
51
|
+
type: object
|
|
52
|
+
properties:
|
|
53
|
+
type:
|
|
54
|
+
type: string
|
|
55
|
+
enum:
|
|
56
|
+
- http
|
|
57
|
+
- mcp
|
|
58
|
+
- python
|
|
59
|
+
- shell
|
|
60
|
+
- builtin
|
|
61
|
+
description: How the tool is executed. builtin | http | mcp | python
|
|
62
|
+
| shell.
|
|
63
|
+
endpoint:
|
|
64
|
+
type: string
|
|
65
|
+
description: URL called when type=http. Supports {placeholder}
|
|
66
|
+
templating.
|
|
67
|
+
method:
|
|
68
|
+
type: string
|
|
69
|
+
description: HTTP method when type=http (default POST).
|
|
70
|
+
mcp_server:
|
|
71
|
+
type: string
|
|
72
|
+
description: MCP server name when type=mcp.
|
|
73
|
+
mcp_tool:
|
|
74
|
+
type: string
|
|
75
|
+
description: Tool name on the MCP server when type=mcp.
|
|
76
|
+
python_module:
|
|
77
|
+
type: string
|
|
78
|
+
description: Dotted import path when type=python.
|
|
79
|
+
python_callable:
|
|
80
|
+
type: string
|
|
81
|
+
description: Attribute on the module (function or class) when
|
|
82
|
+
type=python.
|
|
83
|
+
shell_command:
|
|
84
|
+
type: string
|
|
85
|
+
description: Command template when type=shell. Never executed without
|
|
86
|
+
confirmation.
|
|
87
|
+
input_schema:
|
|
88
|
+
type: object
|
|
89
|
+
description: JSON Schema of the arguments the agent passes when
|
|
90
|
+
invoking the tool — the "parameters" the model fills in. Surfaced
|
|
91
|
+
as ``parameters`` by ``dna.load_tools`` / ``loadTools``.
|
|
92
|
+
output_schema:
|
|
93
|
+
type: object
|
|
94
|
+
description: JSON Schema describing the shape of the tool's response.
|
|
95
|
+
auth_type:
|
|
96
|
+
type: string
|
|
97
|
+
enum:
|
|
98
|
+
- none
|
|
99
|
+
- api_key
|
|
100
|
+
- bearer
|
|
101
|
+
- oauth2
|
|
102
|
+
description: Credential strategy for the invocation.
|
|
103
|
+
auth_env_var:
|
|
104
|
+
type: string
|
|
105
|
+
description: Environment variable holding the credential (e.g.
|
|
106
|
+
GITHUB_TOKEN).
|
|
107
|
+
read_only:
|
|
108
|
+
type: boolean
|
|
109
|
+
description: False = the tool may mutate state (DB writes, file
|
|
110
|
+
changes, external side effects).
|
|
111
|
+
requires_confirmation:
|
|
112
|
+
type: boolean
|
|
113
|
+
description: Force user approval before each invocation.
|
|
114
|
+
tags:
|
|
115
|
+
type: array
|
|
116
|
+
items:
|
|
117
|
+
type: string
|
|
118
|
+
description: Free-form labels for filtering and search.
|
|
119
|
+
examples:
|
|
120
|
+
type: array
|
|
121
|
+
items:
|
|
122
|
+
type: object
|
|
123
|
+
description: Usage examples ([{input, output}]).
|
|
124
|
+
ui:
|
|
125
|
+
mode: build
|
|
126
|
+
label:
|
|
127
|
+
en: Tools
|
|
128
|
+
pt-BR: Ferramentas
|
|
129
|
+
description:
|
|
130
|
+
en: '@tool functions available to agents.'
|
|
131
|
+
pt-BR: Ferramentas (@tool) disponíveis aos agentes.
|
|
132
|
+
routes:
|
|
133
|
+
list: docs/Tool
|
|
134
|
+
detail: docs/Tool/:name
|
|
135
|
+
permissions:
|
|
136
|
+
list: any
|
|
137
|
+
detail: any
|
|
138
|
+
in_sidebar: true
|
|
139
|
+
display_order: 53
|
|
140
|
+
ui_schema:
|
|
141
|
+
type:
|
|
142
|
+
widget: select
|
|
143
|
+
label: Invocation type
|
|
144
|
+
help: 'How the tool is executed: http | mcp | python | shell | builtin.'
|
|
145
|
+
order: 10
|
|
146
|
+
endpoint:
|
|
147
|
+
widget: text
|
|
148
|
+
label: HTTP endpoint
|
|
149
|
+
help: URL called when type=http. Supports {placeholder} templating.
|
|
150
|
+
order: 20
|
|
151
|
+
method:
|
|
152
|
+
widget: select
|
|
153
|
+
label: HTTP method
|
|
154
|
+
order: 25
|
|
155
|
+
mcp_server:
|
|
156
|
+
widget: text
|
|
157
|
+
label: MCP server
|
|
158
|
+
help: Server name when type=mcp.
|
|
159
|
+
order: 30
|
|
160
|
+
mcp_tool:
|
|
161
|
+
widget: text
|
|
162
|
+
label: MCP tool name
|
|
163
|
+
order: 35
|
|
164
|
+
python_module:
|
|
165
|
+
widget: text
|
|
166
|
+
label: Python module
|
|
167
|
+
help: Dotted import path when type=python.
|
|
168
|
+
order: 40
|
|
169
|
+
python_callable:
|
|
170
|
+
widget: text
|
|
171
|
+
label: Python callable
|
|
172
|
+
help: Attribute on the module (function or class).
|
|
173
|
+
order: 45
|
|
174
|
+
shell_command:
|
|
175
|
+
widget: textarea
|
|
176
|
+
label: Shell command
|
|
177
|
+
help: Command template when type=shell. Never executed without
|
|
178
|
+
confirmation.
|
|
179
|
+
order: 50
|
|
180
|
+
input_schema:
|
|
181
|
+
widget: code
|
|
182
|
+
language: yaml
|
|
183
|
+
label: Input schema (JSON Schema)
|
|
184
|
+
help: Validates the arguments the agent passes when invoking the tool.
|
|
185
|
+
height: 260
|
|
186
|
+
order: 60
|
|
187
|
+
output_schema:
|
|
188
|
+
widget: code
|
|
189
|
+
language: yaml
|
|
190
|
+
label: Output schema (JSON Schema)
|
|
191
|
+
help: Describes the shape of the tool's response.
|
|
192
|
+
height: 220
|
|
193
|
+
order: 70
|
|
194
|
+
auth_type:
|
|
195
|
+
widget: select
|
|
196
|
+
label: Auth type
|
|
197
|
+
help: none | api_key | bearer | oauth2.
|
|
198
|
+
order: 80
|
|
199
|
+
auth_env_var:
|
|
200
|
+
widget: text
|
|
201
|
+
label: Auth env var
|
|
202
|
+
help: Environment variable holding the credential.
|
|
203
|
+
order: 85
|
|
204
|
+
read_only:
|
|
205
|
+
widget: checkbox
|
|
206
|
+
label: Read-only
|
|
207
|
+
help: Uncheck if the tool mutates state (database writes, file changes,
|
|
208
|
+
external side effects).
|
|
209
|
+
order: 90
|
|
210
|
+
requires_confirmation:
|
|
211
|
+
widget: checkbox
|
|
212
|
+
label: Requires confirmation
|
|
213
|
+
help: Force user approval before each invocation.
|
|
214
|
+
order: 95
|
|
215
|
+
tags:
|
|
216
|
+
widget: tags
|
|
217
|
+
label: Tags
|
|
218
|
+
order: 100
|
|
219
|
+
examples:
|
|
220
|
+
widget: readonly
|
|
221
|
+
label: Examples
|
|
222
|
+
help: Usage examples. Nested list — edit via YAML for now.
|
|
223
|
+
order: 110
|
|
224
|
+
graph_style:
|
|
225
|
+
fill: '#14B8A6'
|
|
226
|
+
stroke: '#0D9488'
|
|
227
|
+
text_color: '#fff'
|
|
228
|
+
ascii_icon: 🔧
|
|
229
|
+
display_label: Tools
|
|
230
|
+
docs: A Tool is a declarative, invocable capability an agent can call — an
|
|
231
|
+
HTTP endpoint, an MCP server tool, a Python callable, a shell command, or
|
|
232
|
+
a builtin. It bridges DNA with OpenAI/Anthropic tool-calling conventions.
|
|
233
|
+
The agent-facing surface is its ``metadata.description`` (the text the
|
|
234
|
+
model reads to decide to call it) and its ``spec.input_schema`` (the
|
|
235
|
+
"parameters" JSON Schema of the arguments); ``dna.load_tools`` /
|
|
236
|
+
``loadTools`` serve exactly that surface, identically to Python and
|
|
237
|
+
TypeScript consumers from this one source. It also declares an auth
|
|
238
|
+
strategy and read_only / requires_confirmation flags the host honors at
|
|
239
|
+
runtime. Agents reference Tools via ``dep_filters.tools``. Stored as
|
|
240
|
+
``tools/<name>.yaml`` — marketplace-shareable as standalone bundles.
|
package/dist/extensions/helix.js
CHANGED
|
@@ -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,
|
|
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,12 @@ 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 type { LoadPromptsOptions } from "./prompts.js";
|
|
30
|
+
export { anchorScopesRoot, PackageScopeNotFound, DEFAULT_SUBPATH } from "./package-scope.js";
|
|
27
31
|
export { loadConfig, findConfig, CONFIG_FILENAME } from "./config.js";
|
|
28
32
|
export type { DnaConfig, SearchMode, EmbeddingMode } from "./config.js";
|
|
29
33
|
export { sourceFromUrl, resolveDefaultFsUrl, UnsupportedSourceScheme } from "./adapters/source-url.js";
|
package/dist/index.js
CHANGED
|
@@ -27,8 +27,10 @@ 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 { anchorScopesRoot, PackageScopeNotFound, DEFAULT_SUBPATH } from "./package-scope.js";
|
|
32
34
|
export { loadConfig, findConfig, CONFIG_FILENAME } from "./config.js";
|
|
33
35
|
export { sourceFromUrl, resolveDefaultFsUrl, UnsupportedSourceScheme } from "./adapters/source-url.js";
|
|
34
36
|
export { HookRegistry, KNOWN_HOOK_NAMES } from "./kernel/hooks.js";
|
package/dist/kernel/errors.d.ts
CHANGED
|
@@ -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).
|
package/dist/kernel/errors.js
CHANGED
|
@@ -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
|
-
|
|
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)
|
package/dist/kernel/models.d.ts
CHANGED
|
@@ -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>>;
|
package/dist/kernel/models.js
CHANGED
|
@@ -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
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
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,
|
|
50
|
+
export declare function loadPrompts(scope: string, opts?: string | LoadPromptsOptions): Promise<PromptLibrary>;
|
package/dist/prompts.js
CHANGED
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
* is `await`ed in TS but not in Python).
|
|
25
25
|
*/
|
|
26
26
|
import { quickInstance } from "./bootstrap.js";
|
|
27
|
+
import { anchorScopesRoot } from "./package-scope.js";
|
|
27
28
|
/** Lazy, cached view `agent name -> composed prompt` over one scope. */
|
|
28
29
|
export class PromptLibrary {
|
|
29
30
|
mi;
|
|
@@ -65,12 +66,26 @@ export class PromptLibrary {
|
|
|
65
66
|
/**
|
|
66
67
|
* Compose the prompts of `scope` behind a {@link PromptLibrary}.
|
|
67
68
|
*
|
|
68
|
-
*
|
|
69
|
-
*
|
|
70
|
-
*
|
|
69
|
+
* Precedence for the scopes-root (first one set wins):
|
|
70
|
+
*
|
|
71
|
+
* `opts.baseDir` > `$DNA_BASE_DIR` > `opts.anchor` (package data) > `.dna`
|
|
72
|
+
*
|
|
73
|
+
* The legacy positional-string form (`loadPrompts(scope, "/path/.dna")`) is
|
|
74
|
+
* still accepted for back-compat and is treated as `baseDir`.
|
|
71
75
|
*/
|
|
72
|
-
export async function loadPrompts(scope,
|
|
73
|
-
const
|
|
74
|
-
const mi = await quickInstance(scope,
|
|
76
|
+
export async function loadPrompts(scope, opts) {
|
|
77
|
+
const o = typeof opts === "string" ? { baseDir: opts } : opts ?? {};
|
|
78
|
+
const mi = await quickInstance(scope, resolveScopeBaseDir(o));
|
|
75
79
|
return new PromptLibrary(mi);
|
|
76
80
|
}
|
|
81
|
+
/** Pick the `.dna` scopes-root by the documented precedence. */
|
|
82
|
+
function resolveScopeBaseDir(o) {
|
|
83
|
+
if (o.baseDir !== undefined)
|
|
84
|
+
return o.baseDir;
|
|
85
|
+
const env = process.env.DNA_BASE_DIR;
|
|
86
|
+
if (env)
|
|
87
|
+
return env;
|
|
88
|
+
if (o.anchor !== undefined)
|
|
89
|
+
return anchorScopesRoot(o.anchor);
|
|
90
|
+
return ".dna";
|
|
91
|
+
}
|
package/dist/tools.d.ts
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { ToolNotFound } from "./kernel/errors.js";
|
|
2
|
+
import type { ManifestInstance } from "./kernel/instance.js";
|
|
3
|
+
export { ToolNotFound };
|
|
4
|
+
/**
|
|
5
|
+
* The agent-facing surface of a Tool — exactly what a tool-calling model is
|
|
6
|
+
* shown to decide whether, and how, to call it.
|
|
7
|
+
*/
|
|
8
|
+
export interface ToolSurface {
|
|
9
|
+
/** Natural-language description (`metadata.description`) — the text the
|
|
10
|
+
* model reads. */
|
|
11
|
+
readonly description: string;
|
|
12
|
+
/** JSON Schema of the arguments (`spec.input_schema`) — what the model
|
|
13
|
+
* fills in. Empty `{}` when the tool takes no args. */
|
|
14
|
+
readonly parameters: Record<string, unknown>;
|
|
15
|
+
}
|
|
16
|
+
/** Lazy, cached view `tool name -> ToolSurface` over one scope. */
|
|
17
|
+
export declare class ToolLibrary {
|
|
18
|
+
/** The underlying ManifestInstance (drop down for the full surface). */
|
|
19
|
+
readonly mi: ManifestInstance;
|
|
20
|
+
private readonly cache;
|
|
21
|
+
constructor(
|
|
22
|
+
/** The underlying ManifestInstance (drop down for the full surface). */
|
|
23
|
+
mi: ManifestInstance);
|
|
24
|
+
/**
|
|
25
|
+
* Project `name`'s agent-facing surface (cached). Throws {@link ToolNotFound}
|
|
26
|
+
* on a miss.
|
|
27
|
+
*/
|
|
28
|
+
get(name: string): ToolSurface;
|
|
29
|
+
/** True when `name` is a Tool document in the scope (no projection). */
|
|
30
|
+
has(name: string): boolean;
|
|
31
|
+
/** Names of every Tool document in the scope, sorted. */
|
|
32
|
+
names(): string[];
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Load the Tool surfaces of `scope` behind a {@link ToolLibrary}.
|
|
36
|
+
*
|
|
37
|
+
* `baseDir` follows the {@link quickInstance} convention — the directory that
|
|
38
|
+
* holds `<scope>/` (the `.dna` scopes root). Omitted → the `DNA_BASE_DIR` env
|
|
39
|
+
* var, then `.dna` in the cwd.
|
|
40
|
+
*/
|
|
41
|
+
export declare function loadTools(scope: string, baseDir?: string): Promise<ToolLibrary>;
|
package/dist/tools.js
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `loadTools` — the agent-facing tool surface, as data (TS twin of python
|
|
3
|
+
* `dna.load_tools`).
|
|
4
|
+
*
|
|
5
|
+
* The DNA already governs persona, instruction and guardrails declaratively.
|
|
6
|
+
* A **Tool** (record-plane Kind, `helix/kinds/tool.kind.yaml`) moves the last
|
|
7
|
+
* hard-coded piece into the same plane: the `description` the model reads to
|
|
8
|
+
* decide whether to call a tool, and the JSON Schema of its `parameters`.
|
|
9
|
+
* `loadTools` is the consumer one-liner — the twin of `loadPrompts`:
|
|
10
|
+
*
|
|
11
|
+
* ```ts
|
|
12
|
+
* import { loadTools } from "@ruinosus/dna";
|
|
13
|
+
*
|
|
14
|
+
* const tools = await loadTools("open-swe");
|
|
15
|
+
* const surface = tools.get("github-search"); // or throws ToolNotFound
|
|
16
|
+
* surface.description; // the text the model reads
|
|
17
|
+
* surface.parameters; // the args JSON Schema
|
|
18
|
+
* ```
|
|
19
|
+
*
|
|
20
|
+
* Because a Tool is ONE declarative document, the SAME surface is served to a
|
|
21
|
+
* Python backend (a `@tool` function's `description=`) and this TypeScript
|
|
22
|
+
* frontend (CopilotKit `useCopilotAction`) from one source of truth — the
|
|
23
|
+
* first place the Py↔TS descriptor parity pays off in a real consumer (see
|
|
24
|
+
* `examples/tools_as_data`).
|
|
25
|
+
*
|
|
26
|
+
* TS/Py asymmetry: `loadTools` is async (booting the kernel is async, exactly
|
|
27
|
+
* like `loadPrompts`), but `get(name)` is SYNC — reading a record document
|
|
28
|
+
* needs no async work (unlike `loadPrompts.get`, which awaits composition).
|
|
29
|
+
* A missing tool throws {@link ToolNotFound} (never an empty surface).
|
|
30
|
+
* Overlay-aware: a tenant overlay that overrides a tool's
|
|
31
|
+
* `metadata.description` / `spec.input_schema` is reflected here.
|
|
32
|
+
*/
|
|
33
|
+
import { quickInstance } from "./bootstrap.js";
|
|
34
|
+
import { ToolNotFound } from "./kernel/errors.js";
|
|
35
|
+
export { ToolNotFound };
|
|
36
|
+
/** Lazy, cached view `tool name -> ToolSurface` over one scope. */
|
|
37
|
+
export class ToolLibrary {
|
|
38
|
+
mi;
|
|
39
|
+
cache = new Map();
|
|
40
|
+
constructor(
|
|
41
|
+
/** The underlying ManifestInstance (drop down for the full surface). */
|
|
42
|
+
mi) {
|
|
43
|
+
this.mi = mi;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Project `name`'s agent-facing surface (cached). Throws {@link ToolNotFound}
|
|
47
|
+
* on a miss.
|
|
48
|
+
*/
|
|
49
|
+
get(name) {
|
|
50
|
+
const cached = this.cache.get(name);
|
|
51
|
+
if (cached !== undefined)
|
|
52
|
+
return cached;
|
|
53
|
+
const doc = this.mi._one("Tool", name);
|
|
54
|
+
if (doc === null) {
|
|
55
|
+
throw new ToolNotFound(name, this.mi.scope ?? null, this.names());
|
|
56
|
+
}
|
|
57
|
+
const meta = doc.metadata;
|
|
58
|
+
const spec = doc.spec;
|
|
59
|
+
const description = typeof meta.description === "string" ? meta.description : "";
|
|
60
|
+
const rawParams = spec.input_schema;
|
|
61
|
+
const parameters = rawParams && typeof rawParams === "object" && !Array.isArray(rawParams)
|
|
62
|
+
? { ...rawParams }
|
|
63
|
+
: {};
|
|
64
|
+
const surface = { description, parameters };
|
|
65
|
+
this.cache.set(name, surface);
|
|
66
|
+
return surface;
|
|
67
|
+
}
|
|
68
|
+
/** True when `name` is a Tool document in the scope (no projection). */
|
|
69
|
+
has(name) {
|
|
70
|
+
return this.names().includes(name);
|
|
71
|
+
}
|
|
72
|
+
/** Names of every Tool document in the scope, sorted. */
|
|
73
|
+
names() {
|
|
74
|
+
return this.mi._all("Tool").map((d) => d.name).sort();
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Load the Tool surfaces of `scope` behind a {@link ToolLibrary}.
|
|
79
|
+
*
|
|
80
|
+
* `baseDir` follows the {@link quickInstance} convention — the directory that
|
|
81
|
+
* holds `<scope>/` (the `.dna` scopes root). Omitted → the `DNA_BASE_DIR` env
|
|
82
|
+
* var, then `.dna` in the cwd.
|
|
83
|
+
*/
|
|
84
|
+
export async function loadTools(scope, baseDir) {
|
|
85
|
+
const resolved = baseDir ?? process.env.DNA_BASE_DIR ?? ".dna";
|
|
86
|
+
const mi = await quickInstance(scope, resolved);
|
|
87
|
+
return new ToolLibrary(mi);
|
|
88
|
+
}
|