dna-sdk 0.1.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/LICENSE +21 -0
- package/README.md +58 -0
- package/dist/adapters/embedding/onnx.d.ts +32 -0
- package/dist/adapters/embedding/onnx.js +51 -0
- package/dist/adapters/filesystem/cache.d.ts +16 -0
- package/dist/adapters/filesystem/cache.js +113 -0
- package/dist/adapters/filesystem/index.d.ts +2 -0
- package/dist/adapters/filesystem/index.js +2 -0
- package/dist/adapters/filesystem/source.d.ts +82 -0
- package/dist/adapters/filesystem/source.js +357 -0
- package/dist/adapters/postgres/index.d.ts +2 -0
- package/dist/adapters/postgres/index.js +2 -0
- package/dist/adapters/postgres/migrations.d.ts +14 -0
- package/dist/adapters/postgres/migrations.js +88 -0
- package/dist/adapters/postgres/source.d.ts +172 -0
- package/dist/adapters/postgres/source.js +569 -0
- package/dist/adapters/resolvers/github.d.ts +12 -0
- package/dist/adapters/resolvers/github.js +47 -0
- package/dist/adapters/resolvers/http.d.ts +22 -0
- package/dist/adapters/resolvers/http.js +145 -0
- package/dist/adapters/resolvers/index.d.ts +3 -0
- package/dist/adapters/resolvers/index.js +3 -0
- package/dist/adapters/resolvers/local.d.ts +24 -0
- package/dist/adapters/resolvers/local.js +132 -0
- package/dist/adapters/search/driver.d.ts +45 -0
- package/dist/adapters/search/driver.js +138 -0
- package/dist/adapters/search/migrations.d.ts +26 -0
- package/dist/adapters/search/migrations.js +58 -0
- package/dist/adapters/search/rrf.d.ts +23 -0
- package/dist/adapters/search/rrf.js +42 -0
- package/dist/adapters/search/sqlite-vec.d.ts +66 -0
- package/dist/adapters/search/sqlite-vec.js +294 -0
- package/dist/bootstrap.d.ts +45 -0
- package/dist/bootstrap.js +134 -0
- package/dist/extensions/agentskills/DOCS.md +18 -0
- package/dist/extensions/agentskills.d.ts +28 -0
- package/dist/extensions/agentskills.js +252 -0
- package/dist/extensions/agentsmd/DOCS.md +18 -0
- package/dist/extensions/agentsmd.d.ts +22 -0
- package/dist/extensions/agentsmd.js +170 -0
- package/dist/extensions/audit/kinds/audit-log.kind.yaml +148 -0
- package/dist/extensions/audit.d.ts +25 -0
- package/dist/extensions/audit.js +62 -0
- package/dist/extensions/automation/kinds/automation.kind.yaml +296 -0
- package/dist/extensions/automation/query.d.ts +49 -0
- package/dist/extensions/automation/query.js +77 -0
- package/dist/extensions/automation/write-guards.d.ts +59 -0
- package/dist/extensions/automation/write-guards.js +153 -0
- package/dist/extensions/automation.d.ts +41 -0
- package/dist/extensions/automation.js +54 -0
- package/dist/extensions/collab.d.ts +16 -0
- package/dist/extensions/collab.js +79 -0
- package/dist/extensions/doc/kinds/doc.kind.yaml +118 -0
- package/dist/extensions/doc.d.ts +25 -0
- package/dist/extensions/doc.js +10 -0
- package/dist/extensions/eval/kinds/eval-baseline.kind.yaml +65 -0
- package/dist/extensions/eval/kinds/eval-case.kind.yaml +140 -0
- package/dist/extensions/eval/kinds/eval-run.kind.yaml +134 -0
- package/dist/extensions/eval/kinds/eval-suite.kind.yaml +87 -0
- package/dist/extensions/eval.d.ts +24 -0
- package/dist/extensions/eval.js +31 -0
- package/dist/extensions/evidence/kinds/evidence.kind.yaml +92 -0
- package/dist/extensions/evidence.d.ts +17 -0
- package/dist/extensions/evidence.js +94 -0
- package/dist/extensions/federation.d.ts +22 -0
- package/dist/extensions/federation.js +173 -0
- package/dist/extensions/guardrails/DOCS.md +20 -0
- package/dist/extensions/guardrails.d.ts +31 -0
- package/dist/extensions/guardrails.js +228 -0
- package/dist/extensions/helix/DOCS-Actor.md +20 -0
- package/dist/extensions/helix/DOCS-Agent.md +20 -0
- package/dist/extensions/helix/DOCS-UseCase.md +20 -0
- package/dist/extensions/helix/layers.d.ts +12 -0
- package/dist/extensions/helix/layers.js +18 -0
- package/dist/extensions/helix/write-guards.d.ts +70 -0
- package/dist/extensions/helix/write-guards.js +172 -0
- package/dist/extensions/helix.d.ts +29 -0
- package/dist/extensions/helix.js +963 -0
- package/dist/extensions/helix_extras.d.ts +124 -0
- package/dist/extensions/helix_extras.js +328 -0
- package/dist/extensions/hooks.d.ts +31 -0
- package/dist/extensions/hooks.js +247 -0
- package/dist/extensions/kinddef/DOCS.md +24 -0
- package/dist/extensions/kinddef.d.ts +40 -0
- package/dist/extensions/kinddef.js +171 -0
- package/dist/extensions/lesson.d.ts +6 -0
- package/dist/extensions/lesson.js +194 -0
- package/dist/extensions/modelreg/kinds/model-profile.kind.yaml +155 -0
- package/dist/extensions/modelreg.d.ts +30 -0
- package/dist/extensions/modelreg.js +38 -0
- package/dist/extensions/recognizer.d.ts +34 -0
- package/dist/extensions/recognizer.js +272 -0
- package/dist/extensions/research.d.ts +32 -0
- package/dist/extensions/research.js +292 -0
- package/dist/extensions/safety.d.ts +33 -0
- package/dist/extensions/safety.js +230 -0
- package/dist/extensions/sdlc/kinds/adr.kind.yaml +136 -0
- package/dist/extensions/sdlc/kinds/archive-proposal.kind.yaml +107 -0
- package/dist/extensions/sdlc/kinds/changelog.kind.yaml +108 -0
- package/dist/extensions/sdlc/kinds/cognitive-policy.kind.yaml +470 -0
- package/dist/extensions/sdlc/kinds/forecast.kind.yaml +125 -0
- package/dist/extensions/sdlc/kinds/insight.kind.yaml +87 -0
- package/dist/extensions/sdlc/kinds/kaizen.kind.yaml +106 -0
- package/dist/extensions/sdlc/kinds/lesson-learned.kind.yaml +270 -0
- package/dist/extensions/sdlc/kinds/narrative.kind.yaml +189 -0
- package/dist/extensions/sdlc/kinds/pattern-insight.kind.yaml +204 -0
- package/dist/extensions/sdlc/kinds/postmortem.kind.yaml +168 -0
- package/dist/extensions/sdlc/kinds/pre-mortem.kind.yaml +145 -0
- package/dist/extensions/sdlc/kinds/prompt-template.kind.yaml +109 -0
- package/dist/extensions/sdlc/kinds/retrospective.kind.yaml +186 -0
- package/dist/extensions/sdlc/kinds/risk-register.kind.yaml +178 -0
- package/dist/extensions/sdlc/kinds/saved-view.kind.yaml +127 -0
- package/dist/extensions/sdlc/kinds/status-report.kind.yaml +129 -0
- package/dist/extensions/sdlc/kinds/synthesis-run.kind.yaml +279 -0
- package/dist/extensions/sdlc/kinds/synthesizer-state.kind.yaml +196 -0
- package/dist/extensions/sdlc/kinds/workflow-event.kind.yaml +195 -0
- package/dist/extensions/sdlc.d.ts +32 -0
- package/dist/extensions/sdlc.js +1318 -0
- package/dist/extensions/soulspec/DOCS.md +19 -0
- package/dist/extensions/soulspec.d.ts +22 -0
- package/dist/extensions/soulspec.js +287 -0
- package/dist/extensions/tenant.d.ts +6 -0
- package/dist/extensions/tenant.js +291 -0
- package/dist/extensions/testkit.d.ts +24 -0
- package/dist/extensions/testkit.js +211 -0
- package/dist/index.d.ts +80 -0
- package/dist/index.js +102 -0
- package/dist/kernel/_text.d.ts +28 -0
- package/dist/kernel/_text.js +42 -0
- package/dist/kernel/bundle-handle.d.ts +83 -0
- package/dist/kernel/bundle-handle.js +184 -0
- package/dist/kernel/capabilities.d.ts +157 -0
- package/dist/kernel/capabilities.js +109 -0
- package/dist/kernel/collaborator-ports.d.ts +91 -0
- package/dist/kernel/collaborator-ports.js +1 -0
- package/dist/kernel/composition-resolver.d.ts +215 -0
- package/dist/kernel/composition-resolver.js +558 -0
- package/dist/kernel/composition.d.ts +10 -0
- package/dist/kernel/composition.js +1 -0
- package/dist/kernel/descriptor-loader.d.ts +13 -0
- package/dist/kernel/descriptor-loader.js +48 -0
- package/dist/kernel/document.d.ts +43 -0
- package/dist/kernel/document.js +71 -0
- package/dist/kernel/embedding.d.ts +28 -0
- package/dist/kernel/embedding.js +78 -0
- package/dist/kernel/errors.d.ts +62 -0
- package/dist/kernel/errors.js +82 -0
- package/dist/kernel/events.d.ts +6 -0
- package/dist/kernel/events.js +21 -0
- package/dist/kernel/evidence-capture.d.ts +69 -0
- package/dist/kernel/evidence-capture.js +138 -0
- package/dist/kernel/fs.d.ts +20 -0
- package/dist/kernel/fs.js +128 -0
- package/dist/kernel/generic-rw.d.ts +33 -0
- package/dist/kernel/generic-rw.js +235 -0
- package/dist/kernel/hooks.d.ts +172 -0
- package/dist/kernel/hooks.js +256 -0
- package/dist/kernel/index.d.ts +624 -0
- package/dist/kernel/index.js +1638 -0
- package/dist/kernel/instance.d.ts +211 -0
- package/dist/kernel/instance.js +487 -0
- package/dist/kernel/kind-registry.d.ts +183 -0
- package/dist/kernel/kind-registry.js +710 -0
- package/dist/kernel/kind_base.d.ts +106 -0
- package/dist/kernel/kind_base.js +244 -0
- package/dist/kernel/layer-resolver.d.ts +48 -0
- package/dist/kernel/layer-resolver.js +284 -0
- package/dist/kernel/lock-manager.d.ts +21 -0
- package/dist/kernel/lock-manager.js +43 -0
- package/dist/kernel/lock.d.ts +38 -0
- package/dist/kernel/lock.js +115 -0
- package/dist/kernel/meta.d.ts +135 -0
- package/dist/kernel/meta.js +817 -0
- package/dist/kernel/models.d.ts +3089 -0
- package/dist/kernel/models.js +764 -0
- package/dist/kernel/navigator.d.ts +36 -0
- package/dist/kernel/navigator.js +132 -0
- package/dist/kernel/port-surface.d.ts +33 -0
- package/dist/kernel/port-surface.js +180 -0
- package/dist/kernel/preview.d.ts +60 -0
- package/dist/kernel/preview.js +54 -0
- package/dist/kernel/prompt-budget.d.ts +39 -0
- package/dist/kernel/prompt-budget.js +43 -0
- package/dist/kernel/prompt-builder.d.ts +25 -0
- package/dist/kernel/prompt-builder.js +237 -0
- package/dist/kernel/protocols.d.ts +751 -0
- package/dist/kernel/protocols.js +493 -0
- package/dist/kernel/reports.d.ts +26 -0
- package/dist/kernel/reports.js +215 -0
- package/dist/kernel/resolver.d.ts +146 -0
- package/dist/kernel/resolver.js +232 -0
- package/dist/kernel/resource.d.ts +57 -0
- package/dist/kernel/resource.js +97 -0
- package/dist/kernel/runtime.d.ts +16 -0
- package/dist/kernel/runtime.js +18 -0
- package/dist/kernel/safety-scanner.d.ts +38 -0
- package/dist/kernel/safety-scanner.js +228 -0
- package/dist/kernel/serialize-to-files.d.ts +29 -0
- package/dist/kernel/serialize-to-files.js +23 -0
- package/dist/kernel/spec-access.d.ts +23 -0
- package/dist/kernel/spec-access.js +40 -0
- package/dist/kernel/studio_ui.d.ts +88 -0
- package/dist/kernel/studio_ui.js +135 -0
- package/dist/kernel/templates.d.ts +77 -0
- package/dist/kernel/templates.js +116 -0
- package/dist/kernel/tool-registry.d.ts +52 -0
- package/dist/kernel/tool-registry.js +89 -0
- package/dist/kernel/write-pipeline.d.ts +65 -0
- package/dist/kernel/write-pipeline.js +271 -0
- package/dist/kernel/writer-helpers.d.ts +24 -0
- package/dist/kernel/writer-helpers.js +42 -0
- package/dist/memory/decay.d.ts +31 -0
- package/dist/memory/decay.js +97 -0
- package/dist/memory/ecphory.d.ts +25 -0
- package/dist/memory/ecphory.js +178 -0
- package/dist/memory/encodingContext.d.ts +21 -0
- package/dist/memory/encodingContext.js +62 -0
- package/dist/memory/index.d.ts +23 -0
- package/dist/memory/index.js +23 -0
- package/dist/memory/memoryType.d.ts +7 -0
- package/dist/memory/memoryType.js +24 -0
- package/dist/memory/policy.d.ts +35 -0
- package/dist/memory/policy.js +35 -0
- package/dist/memory/retrieval.d.ts +21 -0
- package/dist/memory/retrieval.js +145 -0
- package/dist/testing/index.d.ts +8 -0
- package/dist/testing/index.js +8 -0
- package/dist/testing/recordSearchConformance.d.ts +43 -0
- package/dist/testing/recordSearchConformance.js +147 -0
- package/dist/viz/ascii.d.ts +7 -0
- package/dist/viz/ascii.js +60 -0
- package/dist/viz/health.d.ts +8 -0
- package/dist/viz/health.js +161 -0
- package/dist/viz/index.d.ts +10 -0
- package/dist/viz/index.js +10 -0
- package/dist/viz/matrix.d.ts +8 -0
- package/dist/viz/matrix.js +84 -0
- package/dist/viz/mermaid.d.ts +38 -0
- package/dist/viz/mermaid.js +706 -0
- package/package.json +82 -0
|
@@ -0,0 +1,963 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HelixExtension — Module, Agent, Actor kinds.
|
|
3
|
+
*
|
|
4
|
+
* 1:1 parity with Python dna.v3.extensions.helix.
|
|
5
|
+
*/
|
|
6
|
+
import yaml from "js-yaml";
|
|
7
|
+
import { join as pathJoin } from "node:path";
|
|
8
|
+
import { nodeFS, readTextSafe, collectDir } from "../kernel/fs.js";
|
|
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";
|
|
11
|
+
import { SD } from "../kernel/protocols.js";
|
|
12
|
+
import { readSpecString, readSpecStringArray } from "../kernel/spec-access.js";
|
|
13
|
+
import { SettingKind, ThemeKind, UserProfileKind, CanvasKind } from "./helix_extras.js";
|
|
14
|
+
import { registerWriteGuards } from "./helix/write-guards.js";
|
|
15
|
+
const MOD_URL = import.meta.url;
|
|
16
|
+
// GenomeKind — Phase 16 (scope segregation)
|
|
17
|
+
//
|
|
18
|
+
// Replaces ModuleKind as the scope-root identity Kind. Carries catalog
|
|
19
|
+
// identity, versioning, runtime defaults, and external dependencies.
|
|
20
|
+
// Tenant overlay is field-level via OVERLAYABLE_FIELDS allowlist.
|
|
21
|
+
//
|
|
22
|
+
// 1:1 parity with Python dna.extensions.helix.GenomeKind.
|
|
23
|
+
// Phase 16 commit 3 — root flag transferred from Module to Genome
|
|
24
|
+
// once examples migrated to ``Genome.yaml``. Module's ``isRoot`` flips
|
|
25
|
+
// to false in lockstep so the "exactly one root Kind" invariant is
|
|
26
|
+
// preserved. Commit 4 removes ``isRoot`` from KindPort entirely.
|
|
27
|
+
// ---------------------------------------------------------------------------
|
|
28
|
+
class GenomeKind extends KindBase {
|
|
29
|
+
apiVersion = "github.com/ruinosus/dna/v1";
|
|
30
|
+
kind = "Genome";
|
|
31
|
+
alias = "helix-genome";
|
|
32
|
+
isSchemaAffecting = true;
|
|
33
|
+
isOverlayable = false;
|
|
34
|
+
scopeInheritable = false;
|
|
35
|
+
// Genome IS the catalog identity (Phase 3b ch1, i-112;
|
|
36
|
+
// s-write-path-despecialize). Py twin: GenomeKind.is_catalog_identity.
|
|
37
|
+
isCatalogIdentity = true;
|
|
38
|
+
origin = "github.com/ruinosus/dna";
|
|
39
|
+
isPromptTarget = false;
|
|
40
|
+
promptTargetPriority = 0;
|
|
41
|
+
flattenInContext = false;
|
|
42
|
+
storage = SD.root("Genome.yaml");
|
|
43
|
+
graphStyle = { fill: "#3B82F6", stroke: "#1D4ED8", textColor: "#fff" };
|
|
44
|
+
asciiIcon = "📦";
|
|
45
|
+
displayLabel = "Genome";
|
|
46
|
+
_sourceUrl = MOD_URL;
|
|
47
|
+
// Tenant-overlayable fields. Identity and versioning are NOT here on
|
|
48
|
+
// purpose: tenant overlays must not change owner, version, etc.
|
|
49
|
+
// Kernel enforces this allowlist (commit 2).
|
|
50
|
+
static OVERLAYABLE_FIELDS = new Set([
|
|
51
|
+
"default_agent",
|
|
52
|
+
"default_llm",
|
|
53
|
+
"budget",
|
|
54
|
+
"tags",
|
|
55
|
+
]);
|
|
56
|
+
docs = "A Genome is the scope-root identity document (Phase 16). It declares " +
|
|
57
|
+
"catalog identity (owner, owner_tenant, repository, visibility), " +
|
|
58
|
+
"versioning (version, changelog_url, deprecated), runtime defaults " +
|
|
59
|
+
"(default_agent, default_llm, budget, tags), and external dependencies. " +
|
|
60
|
+
"Replaces the legacy Module Kind. Layer policy moved to LayerPolicy " +
|
|
61
|
+
"docs at <scope>/policies/. Custom Kinds moved to KindDefinition docs " +
|
|
62
|
+
"at <scope>/kinds/.";
|
|
63
|
+
uiSchema = {
|
|
64
|
+
owner_tenant: { widget: "readonly", label: "Owner tenant", help: "null = platform-owned (catalog item).", order: 5 },
|
|
65
|
+
visibility: { widget: "select", label: "Visibility", options: ["public", "internal", "private"], help: "Who can discover and install this Genome.", order: 6 },
|
|
66
|
+
version: { widget: "text", label: "Version", help: "Semver. Opt-in. null = unversioned.", order: 7 },
|
|
67
|
+
changelog_url: { widget: "text", label: "Changelog URL", order: 8 },
|
|
68
|
+
deprecated: { widget: "checkbox", label: "Deprecated", order: 9 },
|
|
69
|
+
deprecated_message: { widget: "textarea", label: "Deprecated message", order: 10 },
|
|
70
|
+
default_agent: { widget: "text", label: "Default agent", help: "Tenant-overlayable.", order: 20 },
|
|
71
|
+
default_llm: { widget: "text", label: "Default LLM", help: "Tenant-overlayable.", order: 21 },
|
|
72
|
+
budget: { widget: "readonly", label: "Budget", help: "Tenant-overlayable.", order: 22 },
|
|
73
|
+
tags: { widget: "tags", label: "Tags", help: "Tenant-overlayable.", order: 23 },
|
|
74
|
+
owner: { widget: "text", label: "Owner", order: 30 },
|
|
75
|
+
repository: { widget: "text", label: "Repository", order: 31 },
|
|
76
|
+
dependencies: { widget: "readonly", label: "External dependencies", order: 90 },
|
|
77
|
+
};
|
|
78
|
+
// Genome has no inventory deps; ``dependencies`` is a list of external
|
|
79
|
+
// module refs resolved via ResolverPort. Composition validation walks
|
|
80
|
+
// scanner-discovered docs directly.
|
|
81
|
+
dependencies() { return null; }
|
|
82
|
+
schema() { return zodSpecToJsonSchema(GenomeSpecSchema); }
|
|
83
|
+
getDefaultAgentName(doc) {
|
|
84
|
+
return readSpecString(doc, "default_agent") ?? null;
|
|
85
|
+
}
|
|
86
|
+
parse(raw) {
|
|
87
|
+
return GenomeSchema.parse(raw);
|
|
88
|
+
}
|
|
89
|
+
describe(doc) {
|
|
90
|
+
const lines = [`Name: ${doc.name}`, `Kind: Genome`];
|
|
91
|
+
const ownerTenant = readSpecString(doc, "owner_tenant") ?? "platform";
|
|
92
|
+
lines.push(`Owner: ${ownerTenant}`);
|
|
93
|
+
const version = readSpecString(doc, "version");
|
|
94
|
+
if (version)
|
|
95
|
+
lines.push(`Version: ${version}`);
|
|
96
|
+
const visibility = readSpecString(doc, "visibility");
|
|
97
|
+
if (visibility)
|
|
98
|
+
lines.push(`Visibility: ${visibility}`);
|
|
99
|
+
const defaultAgent = readSpecString(doc, "default_agent");
|
|
100
|
+
if (defaultAgent)
|
|
101
|
+
lines.push(`Default: ${defaultAgent}`);
|
|
102
|
+
const spec = (doc.spec ?? {});
|
|
103
|
+
if (spec.deprecated) {
|
|
104
|
+
const msg = readSpecString(doc, "deprecated_message") ?? "";
|
|
105
|
+
lines.push(`Deprecated: ${msg}`);
|
|
106
|
+
}
|
|
107
|
+
return lines.join("\n");
|
|
108
|
+
}
|
|
109
|
+
summary(doc) {
|
|
110
|
+
const spec = (doc.spec ?? {});
|
|
111
|
+
return {
|
|
112
|
+
owner_tenant: spec.owner_tenant ?? null,
|
|
113
|
+
visibility: spec.visibility ?? null,
|
|
114
|
+
version: spec.version ?? null,
|
|
115
|
+
default_agent: spec.default_agent ?? null,
|
|
116
|
+
deprecated: Boolean(spec.deprecated),
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
preview(doc) {
|
|
120
|
+
const spec = (doc.spec ?? {});
|
|
121
|
+
const fields = [];
|
|
122
|
+
for (const label of ["owner_tenant", "visibility", "version", "default_agent", "default_llm"]) {
|
|
123
|
+
const value = spec[label];
|
|
124
|
+
if (value !== null && value !== undefined && value !== "") {
|
|
125
|
+
fields.push({ label, value: String(value) });
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
if (spec.deprecated) {
|
|
129
|
+
fields.push({ label: "deprecated", value: String(spec.deprecated_message ?? "true") });
|
|
130
|
+
}
|
|
131
|
+
const deps = spec.dependencies;
|
|
132
|
+
if (Array.isArray(deps) && deps.length > 0) {
|
|
133
|
+
fields.push({ label: "dependencies", value: `${deps.length} entries` });
|
|
134
|
+
}
|
|
135
|
+
if (fields.length === 0) {
|
|
136
|
+
return [{ kind: "empty", title: `Genome ${doc.name}` }];
|
|
137
|
+
}
|
|
138
|
+
return [{ kind: "fields", title: `Genome ${doc.name}`, fields }];
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
// ---------------------------------------------------------------------------
|
|
142
|
+
// LayerPolicyKind — Phase 16
|
|
143
|
+
//
|
|
144
|
+
// Overlay policy per (layer, kind) tuple. One LayerPolicy doc per layer
|
|
145
|
+
// dimension (e.g. tenant, branch). Lives at ``<scope>/policies/<id>.yaml``.
|
|
146
|
+
// Replaces the legacy ``Module.spec.layers`` field.
|
|
147
|
+
//
|
|
148
|
+
// 1:1 parity with Python dna.extensions.helix.LayerPolicyKind.
|
|
149
|
+
// ---------------------------------------------------------------------------
|
|
150
|
+
class LayerPolicyKind extends KindBase {
|
|
151
|
+
apiVersion = "github.com/ruinosus/dna/policy/v1";
|
|
152
|
+
kind = "LayerPolicy";
|
|
153
|
+
alias = "policy-layer-policy"; // s-kind-alias-convention-fix: <owner>-<kebab(kind)>; was "policy-layer"
|
|
154
|
+
isSchemaAffecting = true;
|
|
155
|
+
isOverlayable = false;
|
|
156
|
+
scopeInheritable = false;
|
|
157
|
+
origin = "github.com/ruinosus/dna/policy";
|
|
158
|
+
isPromptTarget = false;
|
|
159
|
+
promptTargetPriority = 0;
|
|
160
|
+
flattenInContext = false;
|
|
161
|
+
storage = SD.yaml("policies");
|
|
162
|
+
graphStyle = { fill: "#A855F7", stroke: "#7E22CE", textColor: "#fff" };
|
|
163
|
+
asciiIcon = "🔒";
|
|
164
|
+
displayLabel = "Layer Policies";
|
|
165
|
+
_sourceUrl = MOD_URL;
|
|
166
|
+
docs = "A LayerPolicy declares overlay rules for one layer dimension " +
|
|
167
|
+
"(tenant, branch, region, etc.). Kernel reads these docs to enforce " +
|
|
168
|
+
"write policy when a layer overlay is applied. Replaces the legacy " +
|
|
169
|
+
"Module.spec.layers field. Some Kinds are structurally non-overlayable " +
|
|
170
|
+
"(Genome, KindDefinition, LayerPolicy itself) — their policy is " +
|
|
171
|
+
"always locked regardless of doc contents.";
|
|
172
|
+
uiSchema = {
|
|
173
|
+
layer_id: { widget: "select", label: "Layer dimension", options: ["tenant", "branch", "region", "user"], help: "Which layer dimension this policy applies to.", order: 10 },
|
|
174
|
+
policies: { widget: "readonly", label: "Per-Kind policies", help: "Map of kind alias → policy (open/restricted/locked).", order: 20 },
|
|
175
|
+
};
|
|
176
|
+
dependencies() { return null; }
|
|
177
|
+
schema() { return zodSpecToJsonSchema(LayerPolicySpecSchema); }
|
|
178
|
+
parse(raw) {
|
|
179
|
+
const parsed = LayerPolicySchema.parse(raw);
|
|
180
|
+
// Mirror Python's LayerPolicySpec.from_raw normalization:
|
|
181
|
+
// lowercase + drop falsy values + drop non-string keys.
|
|
182
|
+
const normalized = {};
|
|
183
|
+
for (const [k, v] of Object.entries(parsed.spec.policies ?? {})) {
|
|
184
|
+
if (typeof k === "string" && k && v) {
|
|
185
|
+
normalized[k] = String(v).toLowerCase();
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
parsed.spec.policies = normalized;
|
|
189
|
+
return parsed;
|
|
190
|
+
}
|
|
191
|
+
describe(doc) {
|
|
192
|
+
const spec = (doc.spec ?? {});
|
|
193
|
+
const layerId = spec.layer_id ?? doc.name;
|
|
194
|
+
const policies = (spec.policies ?? {});
|
|
195
|
+
const n = Object.keys(policies).length;
|
|
196
|
+
return `Name: ${doc.name}\nKind: LayerPolicy\nLayer: ${layerId}\nRules: ${n}`;
|
|
197
|
+
}
|
|
198
|
+
summary(doc) {
|
|
199
|
+
const spec = (doc.spec ?? {});
|
|
200
|
+
const policies = (spec.policies ?? {});
|
|
201
|
+
return {
|
|
202
|
+
layer_id: spec.layer_id ?? null,
|
|
203
|
+
rule_count: Object.keys(policies).length,
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
preview(doc) {
|
|
207
|
+
const spec = (doc.spec ?? {});
|
|
208
|
+
const fields = [];
|
|
209
|
+
if (spec.layer_id) {
|
|
210
|
+
fields.push({ label: "layer_id", value: String(spec.layer_id) });
|
|
211
|
+
}
|
|
212
|
+
const policies = spec.policies;
|
|
213
|
+
if (policies && typeof policies === "object") {
|
|
214
|
+
for (const alias of Object.keys(policies).sort()) {
|
|
215
|
+
fields.push({ label: alias, value: String(policies[alias]) });
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
if (fields.length === 0) {
|
|
219
|
+
return [{ kind: "empty", title: `LayerPolicy ${doc.name}` }];
|
|
220
|
+
}
|
|
221
|
+
return [{ kind: "fields", title: `LayerPolicy ${doc.name}`, fields }];
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
// ---------------------------------------------------------------------------
|
|
225
|
+
// AgentKind
|
|
226
|
+
// ---------------------------------------------------------------------------
|
|
227
|
+
class AgentKind extends KindBase {
|
|
228
|
+
apiVersion = "github.com/ruinosus/dna/v1";
|
|
229
|
+
kind = "Agent";
|
|
230
|
+
alias = "helix-agent";
|
|
231
|
+
isSchemaAffecting = true;
|
|
232
|
+
origin = "github.com/ruinosus/dna";
|
|
233
|
+
isPromptTarget = true;
|
|
234
|
+
promptTargetPriority = 10;
|
|
235
|
+
flattenInContext = false;
|
|
236
|
+
storage = SD.bundle("agents", "AGENT.md");
|
|
237
|
+
graphStyle = { fill: "#F97316", stroke: "#EA580C", textColor: "#fff" };
|
|
238
|
+
asciiIcon = "🤖";
|
|
239
|
+
displayLabel = "Agents";
|
|
240
|
+
_sourceUrl = MOD_URL;
|
|
241
|
+
docs = "A Agent is the primary prompt target in a helix manifest — " +
|
|
242
|
+
"what actually runs when a user (or another agent) talks to the system. " +
|
|
243
|
+
"It carries an instruction, a model, and dep_filters declaring which " +
|
|
244
|
+
"Soul, Skills, Guardrails, and Actors it composes with. Bundle-based " +
|
|
245
|
+
"storage: agents/<name>/AGENT.md.";
|
|
246
|
+
uiSchema = {
|
|
247
|
+
instruction: {
|
|
248
|
+
widget: "markdown-toc",
|
|
249
|
+
label: "Instruction (AGENT.md)",
|
|
250
|
+
help: "The agent's main prompt body. Supports Mustache tags like {{soul_content}}.",
|
|
251
|
+
height: 520,
|
|
252
|
+
order: 10,
|
|
253
|
+
},
|
|
254
|
+
objective: { widget: "textarea", label: "Objective", order: 15 },
|
|
255
|
+
model: { widget: "text", label: "Model", order: 20 },
|
|
256
|
+
soul: { widget: "text", label: "Soul", help: "Name of the Soul doc to flatten into the prompt.", order: 30 },
|
|
257
|
+
skills: { widget: "tags", label: "Skills", order: 40 },
|
|
258
|
+
actors: { widget: "tags", label: "Actors this agent serves", order: 50 },
|
|
259
|
+
guardrails: { widget: "tags", label: "Guardrails", order: 60 },
|
|
260
|
+
tools: { widget: "tags", label: "Tools", order: 70 },
|
|
261
|
+
team_members: { widget: "tags", label: "Team members", order: 80 },
|
|
262
|
+
// s-mcp-servers-on-agent (Py parity) — MCPFederation refs.
|
|
263
|
+
mcp_servers: {
|
|
264
|
+
widget: "tags",
|
|
265
|
+
label: "MCP servers",
|
|
266
|
+
help: "MCPFederation doc names this agent consumes (e.g. 'drawio'). Remote tools load as first-class agent tools tagged mcp:<ref>. Entries may also be objects {ref, allowed_tools, timeout_s} for per-agent overrides.",
|
|
267
|
+
order: 87,
|
|
268
|
+
},
|
|
269
|
+
tags: { widget: "tags", label: "Tags", order: 90 },
|
|
270
|
+
};
|
|
271
|
+
depFilters() {
|
|
272
|
+
return {
|
|
273
|
+
soul: "soulspec-soul",
|
|
274
|
+
skills: "agentskills-skill",
|
|
275
|
+
guardrails: "guardrails-guardrail",
|
|
276
|
+
actors: "helix-actor",
|
|
277
|
+
tools: "helix-tool",
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
schema() { return zodSpecToJsonSchema(AgentSpecSchema); }
|
|
281
|
+
parse(raw) {
|
|
282
|
+
return AgentSchema.parse(raw);
|
|
283
|
+
}
|
|
284
|
+
describe(doc) {
|
|
285
|
+
const spec = doc.spec;
|
|
286
|
+
const meta = doc.metadata;
|
|
287
|
+
const name = meta.name ?? "";
|
|
288
|
+
const desc = meta.description ?? "";
|
|
289
|
+
const soul = spec.soul ?? "";
|
|
290
|
+
const skills = spec.skills ?? [];
|
|
291
|
+
const model = spec.model ?? "";
|
|
292
|
+
const lines = [`Name: ${name}`, `Kind: Agent`];
|
|
293
|
+
if (desc)
|
|
294
|
+
lines.push(`Desc: ${desc}`);
|
|
295
|
+
if (soul)
|
|
296
|
+
lines.push(`Soul: ${soul}`);
|
|
297
|
+
if (skills.length > 0)
|
|
298
|
+
lines.push(`Skills: ${skills.join(", ")} (${skills.length})`);
|
|
299
|
+
if (model)
|
|
300
|
+
lines.push(`Model: ${model}`);
|
|
301
|
+
return lines.join("\n");
|
|
302
|
+
}
|
|
303
|
+
summary(doc) {
|
|
304
|
+
const skills = readSpecStringArray(doc, "skills");
|
|
305
|
+
return { skills: skills.length, soul: readSpecString(doc, "soul") ?? null };
|
|
306
|
+
}
|
|
307
|
+
promptTemplate() {
|
|
308
|
+
return "{{{agent.instruction}}}\n\n{{{soul_content}}}";
|
|
309
|
+
}
|
|
310
|
+
preview(doc) {
|
|
311
|
+
const spec = (doc.spec ?? {});
|
|
312
|
+
const blocks = [];
|
|
313
|
+
const instruction = typeof spec.instruction === "string" ? spec.instruction : "";
|
|
314
|
+
if (instruction) {
|
|
315
|
+
// Show the raw template with mustache placeholders intact — Self
|
|
316
|
+
// means "the doc you're editing", not the compiled prompt.
|
|
317
|
+
blocks.push({ kind: "markdown", title: "AGENT.md (template)", body: instruction });
|
|
318
|
+
}
|
|
319
|
+
const meta = [];
|
|
320
|
+
if (typeof spec.model === "string")
|
|
321
|
+
meta.push({ label: "model", value: spec.model });
|
|
322
|
+
if (typeof spec.soul === "string")
|
|
323
|
+
meta.push({ label: "soul", value: spec.soul });
|
|
324
|
+
for (const f of ["skills", "guardrails", "tools"]) {
|
|
325
|
+
const arr = spec[f];
|
|
326
|
+
if (Array.isArray(arr) && arr.length > 0) {
|
|
327
|
+
meta.push({ label: f, value: arr.map(String).join(", ") });
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
if (meta.length > 0) {
|
|
331
|
+
blocks.push({ kind: "fields", title: "Metadata", fields: meta });
|
|
332
|
+
}
|
|
333
|
+
if (blocks.length === 0) {
|
|
334
|
+
return [{ kind: "empty", title: `Agent ${doc.name}` }];
|
|
335
|
+
}
|
|
336
|
+
return blocks;
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
// ---------------------------------------------------------------------------
|
|
340
|
+
// ActorKind
|
|
341
|
+
// ---------------------------------------------------------------------------
|
|
342
|
+
class ActorKind extends KindBase {
|
|
343
|
+
apiVersion = "github.com/ruinosus/dna/v1";
|
|
344
|
+
kind = "Actor";
|
|
345
|
+
alias = "helix-actor";
|
|
346
|
+
origin = "github.com/ruinosus/dna/actor";
|
|
347
|
+
isPromptTarget = false;
|
|
348
|
+
promptTargetPriority = 0;
|
|
349
|
+
flattenInContext = false;
|
|
350
|
+
storage = SD.yaml("actors");
|
|
351
|
+
graphStyle = { fill: "#EC4899", stroke: "#DB2777", textColor: "#fff" };
|
|
352
|
+
asciiIcon = "👤";
|
|
353
|
+
displayLabel = "Actors";
|
|
354
|
+
_sourceUrl = MOD_URL;
|
|
355
|
+
docs = "An Actor is a UML-canonical participant in the system — a human user, " +
|
|
356
|
+
"an external system, or a time/schedule trigger. The actorType field " +
|
|
357
|
+
"disambiguates human/system/time. Actors are referenced by UseCases and " +
|
|
358
|
+
"Agents via dep_filters.actors but are not prompt targets.";
|
|
359
|
+
uiSchema = {
|
|
360
|
+
role: { widget: "text", label: "Role", help: "Short job title or functional role.", order: 10 },
|
|
361
|
+
actor_type: {
|
|
362
|
+
widget: "select",
|
|
363
|
+
label: "Actor type",
|
|
364
|
+
help: "human = person/role; system = external service or upstream; time = scheduled trigger.",
|
|
365
|
+
order: 20,
|
|
366
|
+
},
|
|
367
|
+
goals: { widget: "list-markdown", label: "Goals", help: "What this actor is trying to achieve.", order: 30 },
|
|
368
|
+
pain_points: { widget: "list-markdown", label: "Pain points", order: 40 },
|
|
369
|
+
preferences: { widget: "readonly", label: "Preferences", help: "Nested object; edit in YAML for now.", order: 90 },
|
|
370
|
+
};
|
|
371
|
+
schema() { return zodSpecToJsonSchema(ActorSpecSchema); }
|
|
372
|
+
parse(raw) {
|
|
373
|
+
return ActorSchema.parse(raw);
|
|
374
|
+
}
|
|
375
|
+
summary() { return null; }
|
|
376
|
+
preview(doc) {
|
|
377
|
+
const spec = (doc.spec ?? {});
|
|
378
|
+
const fields = [];
|
|
379
|
+
if (typeof spec.role === "string")
|
|
380
|
+
fields.push({ label: "role", value: spec.role });
|
|
381
|
+
if (typeof spec.actor_type === "string")
|
|
382
|
+
fields.push({ label: "actor_type", value: spec.actor_type });
|
|
383
|
+
if (Array.isArray(spec.goals) && spec.goals.length > 0) {
|
|
384
|
+
fields.push({
|
|
385
|
+
label: "goals",
|
|
386
|
+
value: spec.goals.map((g) => `• ${String(g)}`).join("\n"),
|
|
387
|
+
});
|
|
388
|
+
}
|
|
389
|
+
if (Array.isArray(spec.pain_points) && spec.pain_points.length > 0) {
|
|
390
|
+
fields.push({
|
|
391
|
+
label: "pain_points",
|
|
392
|
+
value: spec.pain_points.map((g) => `• ${String(g)}`).join("\n"),
|
|
393
|
+
});
|
|
394
|
+
}
|
|
395
|
+
if (fields.length === 0) {
|
|
396
|
+
return [{ kind: "empty", title: `Actor ${doc.name}` }];
|
|
397
|
+
}
|
|
398
|
+
return [{ kind: "fields", title: `Actor ${doc.name}`, fields }];
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
// ---------------------------------------------------------------------------
|
|
402
|
+
// UseCaseKind
|
|
403
|
+
// ---------------------------------------------------------------------------
|
|
404
|
+
class UseCaseKind extends KindBase {
|
|
405
|
+
apiVersion = "github.com/ruinosus/dna/v1";
|
|
406
|
+
kind = "UseCase";
|
|
407
|
+
alias = "helix-usecase";
|
|
408
|
+
origin = "github.com/ruinosus/dna/usecase";
|
|
409
|
+
isPromptTarget = false;
|
|
410
|
+
promptTargetPriority = 0;
|
|
411
|
+
flattenInContext = false;
|
|
412
|
+
storage = SD.yaml("use_cases");
|
|
413
|
+
graphStyle = { fill: "#F59E0B", stroke: "#D97706", textColor: "#fff" };
|
|
414
|
+
asciiIcon = "📋";
|
|
415
|
+
displayLabel = "UseCases";
|
|
416
|
+
_sourceUrl = MOD_URL;
|
|
417
|
+
docs = "A UseCase is a UML-canonical use case: a goal-oriented interaction " +
|
|
418
|
+
"between Actors and the system. It composes primary_actor, supporting " +
|
|
419
|
+
"actors, agents, preconditions, main_flow, alternate_flows, " +
|
|
420
|
+
"postconditions, and success_criteria. Purely declarative — not a " +
|
|
421
|
+
"prompt target — consumed by tooling for traceability.";
|
|
422
|
+
uiSchema = {
|
|
423
|
+
primary_actor: { widget: "text", label: "Primary actor", help: "Name of the Actor doc that initiates this use case.", order: 10 },
|
|
424
|
+
supporting_actors: { widget: "tags", label: "Supporting actors", order: 20 },
|
|
425
|
+
agents: { widget: "tags", label: "Agents", help: "Agents that fulfill this use case.", order: 30 },
|
|
426
|
+
soul: { widget: "text", label: "Soul", help: "Name of the Soul that shapes the tone of this flow. Optional — overrides the agent's soul for this use case scope.", order: 40 },
|
|
427
|
+
skills: { widget: "tags", label: "Skills", help: "Skills required by the agents to fulfill this use case.", order: 50 },
|
|
428
|
+
tools: { widget: "tags", label: "Tools", help: "Tools the agents invoke during this use case.", order: 60 },
|
|
429
|
+
guardrails: { widget: "tags", label: "Guardrails", help: "Guardrails that apply specifically to this use case.", order: 70 },
|
|
430
|
+
preconditions: { widget: "list-markdown", label: "Preconditions", order: 80 },
|
|
431
|
+
main_flow: { widget: "list-markdown", label: "Main flow", help: "Ordered steps describing the happy path.", order: 90 },
|
|
432
|
+
alternate_flows: { widget: "readonly", label: "Alternate flows", help: "Named deviations. Nested object; edit in YAML.", order: 100 },
|
|
433
|
+
postconditions: { widget: "list-markdown", label: "Postconditions", order: 110 },
|
|
434
|
+
success_criteria: { widget: "list-markdown", label: "Success criteria", order: 120 },
|
|
435
|
+
};
|
|
436
|
+
depFilters() {
|
|
437
|
+
return {
|
|
438
|
+
primary_actor: "helix-actor",
|
|
439
|
+
supporting_actors: "helix-actor",
|
|
440
|
+
agents: "helix-agent",
|
|
441
|
+
soul: "soulspec-soul",
|
|
442
|
+
skills: "agentskills-skill",
|
|
443
|
+
tools: "helix-tool",
|
|
444
|
+
guardrails: "guardrails-guardrail",
|
|
445
|
+
};
|
|
446
|
+
}
|
|
447
|
+
schema() { return zodSpecToJsonSchema(UseCaseSpecSchema); }
|
|
448
|
+
parse(raw) {
|
|
449
|
+
return UseCaseSchema.parse(raw);
|
|
450
|
+
}
|
|
451
|
+
summary() { return null; }
|
|
452
|
+
preview(doc) {
|
|
453
|
+
const spec = (doc.spec ?? {});
|
|
454
|
+
const fields = [];
|
|
455
|
+
if (typeof spec.primary_actor === "string")
|
|
456
|
+
fields.push({ label: "primary_actor", value: spec.primary_actor });
|
|
457
|
+
for (const f of ["supporting_actors", "agents", "skills", "tools", "guardrails"]) {
|
|
458
|
+
const arr = spec[f];
|
|
459
|
+
if (Array.isArray(arr) && arr.length > 0) {
|
|
460
|
+
fields.push({ label: f, value: arr.map(String).join(", ") });
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
if (Array.isArray(spec.preconditions) && spec.preconditions.length > 0) {
|
|
464
|
+
fields.push({
|
|
465
|
+
label: "preconditions",
|
|
466
|
+
value: spec.preconditions.map((s) => `• ${String(s)}`).join("\n"),
|
|
467
|
+
});
|
|
468
|
+
}
|
|
469
|
+
if (Array.isArray(spec.main_flow) && spec.main_flow.length > 0) {
|
|
470
|
+
fields.push({
|
|
471
|
+
label: "main_flow",
|
|
472
|
+
value: spec.main_flow.map((s, i) => `${i + 1}. ${String(s)}`).join("\n"),
|
|
473
|
+
});
|
|
474
|
+
}
|
|
475
|
+
if (Array.isArray(spec.success_criteria) && spec.success_criteria.length > 0) {
|
|
476
|
+
fields.push({
|
|
477
|
+
label: "success_criteria",
|
|
478
|
+
value: spec.success_criteria.map((s) => `• ${String(s)}`).join("\n"),
|
|
479
|
+
});
|
|
480
|
+
}
|
|
481
|
+
if (fields.length === 0) {
|
|
482
|
+
return [{ kind: "empty", title: `UseCase ${doc.name}` }];
|
|
483
|
+
}
|
|
484
|
+
return [{ kind: "fields", title: `UseCase ${doc.name}`, fields }];
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
// ---------------------------------------------------------------------------
|
|
488
|
+
// ToolKind
|
|
489
|
+
// ---------------------------------------------------------------------------
|
|
490
|
+
class ToolKind extends KindBase {
|
|
491
|
+
apiVersion = "github.com/ruinosus/dna/v1";
|
|
492
|
+
kind = "Tool";
|
|
493
|
+
alias = "helix-tool";
|
|
494
|
+
isSchemaAffecting = true;
|
|
495
|
+
origin = "github.com/ruinosus/dna/tool";
|
|
496
|
+
isPromptTarget = false;
|
|
497
|
+
promptTargetPriority = 0;
|
|
498
|
+
flattenInContext = false;
|
|
499
|
+
storage = SD.yaml("tools");
|
|
500
|
+
graphStyle = { fill: "#14B8A6", stroke: "#0D9488", textColor: "#fff" };
|
|
501
|
+
asciiIcon = "🔧";
|
|
502
|
+
displayLabel = "Tools";
|
|
503
|
+
_sourceUrl = MOD_URL;
|
|
504
|
+
docs = "A Tool is a declarative, invocable capability an agent can call: an " +
|
|
505
|
+
"HTTP endpoint, an MCP server tool, a Python callable, a shell command, " +
|
|
506
|
+
"or a builtin. Bridges helix with OpenAI/Anthropic tool-calling " +
|
|
507
|
+
"conventions. Each Tool declares an input/output JSON Schema, an auth " +
|
|
508
|
+
"strategy, and read_only / requires_confirmation flags that the harness " +
|
|
509
|
+
"honors at runtime. Agents reference Tools via dep_filters.tools. " +
|
|
510
|
+
"Stored as tools/<name>.yaml — marketplace-shareable as standalone bundles.";
|
|
511
|
+
uiSchema = {
|
|
512
|
+
type: { widget: "select", label: "Invocation type", help: "How the tool is executed: http | mcp | python | shell | builtin.", order: 10 },
|
|
513
|
+
endpoint: { widget: "text", label: "HTTP endpoint", help: "URL called when type=http. Supports {placeholder} templating.", order: 20 },
|
|
514
|
+
method: { widget: "select", label: "HTTP method", order: 25 },
|
|
515
|
+
mcp_server: { widget: "text", label: "MCP server", help: "Server name when type=mcp.", order: 30 },
|
|
516
|
+
mcp_tool: { widget: "text", label: "MCP tool name", order: 35 },
|
|
517
|
+
python_module: { widget: "text", label: "Python module", help: "Dotted import path when type=python.", order: 40 },
|
|
518
|
+
python_callable: { widget: "text", label: "Python callable", help: "Attribute on the module (function or class).", order: 45 },
|
|
519
|
+
shell_command: { widget: "textarea", label: "Shell command", help: "Command template when type=shell. Never executed without confirmation.", order: 50 },
|
|
520
|
+
input_schema: {
|
|
521
|
+
widget: "code",
|
|
522
|
+
language: "yaml",
|
|
523
|
+
label: "Input schema (JSON Schema)",
|
|
524
|
+
help: "Validates the arguments the agent passes when invoking the tool.",
|
|
525
|
+
height: 260,
|
|
526
|
+
order: 60,
|
|
527
|
+
},
|
|
528
|
+
output_schema: {
|
|
529
|
+
widget: "code",
|
|
530
|
+
language: "yaml",
|
|
531
|
+
label: "Output schema (JSON Schema)",
|
|
532
|
+
help: "Describes the shape of the tool's response.",
|
|
533
|
+
height: 220,
|
|
534
|
+
order: 70,
|
|
535
|
+
},
|
|
536
|
+
auth_type: { widget: "select", label: "Auth type", help: "none | api_key | bearer | oauth2.", order: 80 },
|
|
537
|
+
auth_env_var: { widget: "text", label: "Auth env var", help: "Environment variable holding the credential.", order: 85 },
|
|
538
|
+
read_only: { widget: "checkbox", label: "Read-only", help: "Uncheck if the tool mutates state.", order: 90 },
|
|
539
|
+
requires_confirmation: { widget: "checkbox", label: "Requires confirmation", help: "Force user approval before each invocation.", order: 95 },
|
|
540
|
+
tags: { widget: "tags", label: "Tags", order: 100 },
|
|
541
|
+
examples: { widget: "readonly", label: "Examples", help: "Usage examples. Edit via YAML for now.", order: 110 },
|
|
542
|
+
};
|
|
543
|
+
schema() { return zodSpecToJsonSchema(ToolSpecSchema); }
|
|
544
|
+
parse(raw) {
|
|
545
|
+
return ToolSchema.parse(raw);
|
|
546
|
+
}
|
|
547
|
+
summary() { return null; }
|
|
548
|
+
preview(doc) {
|
|
549
|
+
const spec = (doc.spec ?? {});
|
|
550
|
+
const fields = [];
|
|
551
|
+
if (typeof spec.type === "string")
|
|
552
|
+
fields.push({ label: "type", value: spec.type });
|
|
553
|
+
if (typeof spec.endpoint === "string")
|
|
554
|
+
fields.push({ label: "endpoint", value: spec.endpoint });
|
|
555
|
+
if (typeof spec.method === "string")
|
|
556
|
+
fields.push({ label: "method", value: spec.method });
|
|
557
|
+
if (typeof spec.mcp_server === "string")
|
|
558
|
+
fields.push({ label: "mcp_server", value: spec.mcp_server });
|
|
559
|
+
if (typeof spec.mcp_tool === "string")
|
|
560
|
+
fields.push({ label: "mcp_tool", value: spec.mcp_tool });
|
|
561
|
+
if (typeof spec.python_module === "string")
|
|
562
|
+
fields.push({ label: "python_module", value: spec.python_module });
|
|
563
|
+
if (typeof spec.python_callable === "string")
|
|
564
|
+
fields.push({ label: "python_callable", value: spec.python_callable });
|
|
565
|
+
if (typeof spec.shell_command === "string")
|
|
566
|
+
fields.push({ label: "shell_command", value: spec.shell_command });
|
|
567
|
+
if (spec.input_schema)
|
|
568
|
+
fields.push({ label: "input_schema", value: JSON.stringify(spec.input_schema, null, 2) });
|
|
569
|
+
if (spec.output_schema)
|
|
570
|
+
fields.push({ label: "output_schema", value: JSON.stringify(spec.output_schema, null, 2) });
|
|
571
|
+
if (typeof spec.auth_type === "string")
|
|
572
|
+
fields.push({ label: "auth", value: spec.auth_type });
|
|
573
|
+
if (spec.read_only != null)
|
|
574
|
+
fields.push({ label: "read_only", value: String(spec.read_only) });
|
|
575
|
+
if (spec.requires_confirmation != null)
|
|
576
|
+
fields.push({ label: "requires_confirmation", value: String(spec.requires_confirmation) });
|
|
577
|
+
if (fields.length === 0) {
|
|
578
|
+
return [{ kind: "empty", title: `Tool ${doc.name}` }];
|
|
579
|
+
}
|
|
580
|
+
return [{ kind: "fields", title: `Tool ${doc.name}`, fields }];
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
// ---------------------------------------------------------------------------
|
|
584
|
+
// AgentReader / AgentWriter
|
|
585
|
+
// ---------------------------------------------------------------------------
|
|
586
|
+
const KNOWN_DIRS = new Set(["scripts", "references", "assets"]);
|
|
587
|
+
// AGENT.md frontmatter passthrough allowlist.
|
|
588
|
+
//
|
|
589
|
+
// Derived from ``AgentSpecSchema`` so adding a field to the
|
|
590
|
+
// Zod schema automatically opens it in the reader and writer — no
|
|
591
|
+
// separate allowlist to keep in sync. Two recurring bugs (the
|
|
592
|
+
// ``shell_sandbox`` 2026-05-08 drift, and ``codegraph`` /
|
|
593
|
+
// ``tool_groups`` / ``tests`` shortly after) were caused by exactly
|
|
594
|
+
// that drift.
|
|
595
|
+
//
|
|
596
|
+
// ``instruction`` is excluded: the reader fills it from the AGENT.md
|
|
597
|
+
// body (or via ``instruction_file`` resolution), never from a top-
|
|
598
|
+
// level frontmatter key. Allowing it here would let an authoring
|
|
599
|
+
// mistake (frontmatter ``instruction:``) silently shadow the body.
|
|
600
|
+
const SPEC_FIELDS = new Set(Object.keys(AgentSpecSchema.shape).filter((k) => k !== "instruction"));
|
|
601
|
+
// ---------------------------------------------------------------------------
|
|
602
|
+
// instruction_file resolver
|
|
603
|
+
// ---------------------------------------------------------------------------
|
|
604
|
+
function _resolveInstructionFile(fs, bundlePath, rel) {
|
|
605
|
+
if (typeof rel !== "string" || rel.length === 0) {
|
|
606
|
+
throw new Error("instruction_file must be a non-empty string");
|
|
607
|
+
}
|
|
608
|
+
if (rel.startsWith("/") || /^[A-Za-z]:/.test(rel)) {
|
|
609
|
+
throw new Error(`instruction_file must be a relative path, got ${JSON.stringify(rel)}`);
|
|
610
|
+
}
|
|
611
|
+
// Count '..' segments in the raw path to catch escape attempts
|
|
612
|
+
const upCount = rel.split("/").filter((p) => p === "..").length;
|
|
613
|
+
if (upCount > 3) {
|
|
614
|
+
throw new Error(`instruction_file exceeds depth cap (up_count=${upCount}, max=3): ${JSON.stringify(rel)}`);
|
|
615
|
+
}
|
|
616
|
+
// Resolve relative to the bundle directory (path.join handles '..' normalization)
|
|
617
|
+
const resolved = pathJoin(bundlePath, rel);
|
|
618
|
+
return fs.readFile(resolved);
|
|
619
|
+
}
|
|
620
|
+
export class AgentReader {
|
|
621
|
+
fs;
|
|
622
|
+
constructor(fs = nodeFS) {
|
|
623
|
+
this.fs = fs;
|
|
624
|
+
}
|
|
625
|
+
detect(bundle) {
|
|
626
|
+
const path = bundle.path ?? "";
|
|
627
|
+
return this.fs.exists(`${path}/AGENT.md`);
|
|
628
|
+
}
|
|
629
|
+
read(bundle) {
|
|
630
|
+
const path = bundle.path ?? "";
|
|
631
|
+
const agentMd = this.fs.readFile(`${path}/AGENT.md`);
|
|
632
|
+
const fm = this._parseFrontmatter(agentMd);
|
|
633
|
+
const name = fm.name || path.split("/").pop() || "";
|
|
634
|
+
const description = fm.description ?? "";
|
|
635
|
+
const labels = fm.labels ?? null;
|
|
636
|
+
// Extract body (after frontmatter)
|
|
637
|
+
const body = agentMd.replace(/^---\n[\s\S]*?---\n?/, "").trim();
|
|
638
|
+
const instructionFile = fm.instruction_file;
|
|
639
|
+
let spec;
|
|
640
|
+
if (instructionFile !== undefined) {
|
|
641
|
+
if (fm.instruction) {
|
|
642
|
+
throw new Error(`${path}: cannot set both frontmatter 'instruction' and 'instruction_file'`);
|
|
643
|
+
}
|
|
644
|
+
if (body.trim() !== "") {
|
|
645
|
+
throw new Error(`${path}: cannot set both AGENT.md body and instruction_file`);
|
|
646
|
+
}
|
|
647
|
+
const content = _resolveInstructionFile(this.fs, path, instructionFile);
|
|
648
|
+
spec = { instruction: content, instruction_file: instructionFile };
|
|
649
|
+
}
|
|
650
|
+
else {
|
|
651
|
+
spec = { instruction: body };
|
|
652
|
+
}
|
|
653
|
+
for (const field of SPEC_FIELDS) {
|
|
654
|
+
if (field in fm && field !== "instruction_file") {
|
|
655
|
+
spec[field] = fm[field];
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
// Collect known subdirectories
|
|
659
|
+
for (const dirName of KNOWN_DIRS) {
|
|
660
|
+
const sub = `${path}/${dirName}`;
|
|
661
|
+
if (this.fs.isDirectory(sub)) {
|
|
662
|
+
const files = collectDir(this.fs, sub, sub);
|
|
663
|
+
if (Object.keys(files).length > 0) {
|
|
664
|
+
spec[dirName] = files;
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
// Collect extra subdirectories
|
|
669
|
+
const extras = {};
|
|
670
|
+
const entries = this.fs.readDir(path);
|
|
671
|
+
for (const entry of entries) {
|
|
672
|
+
const full = `${path}/${entry}`;
|
|
673
|
+
if (!this.fs.isDirectory(full) || KNOWN_DIRS.has(entry))
|
|
674
|
+
continue;
|
|
675
|
+
const files = collectDir(this.fs, full, full);
|
|
676
|
+
if (Object.keys(files).length > 0) {
|
|
677
|
+
extras[entry] = files;
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
if (Object.keys(extras).length > 0) {
|
|
681
|
+
spec.extras = extras;
|
|
682
|
+
}
|
|
683
|
+
// Collect root-level extra files
|
|
684
|
+
const rootFiles = {};
|
|
685
|
+
for (const entry of entries) {
|
|
686
|
+
const full = `${path}/${entry}`;
|
|
687
|
+
if (!this.fs.isFile(full) || entry === "AGENT.md")
|
|
688
|
+
continue;
|
|
689
|
+
const text = readTextSafe(this.fs, full);
|
|
690
|
+
if (text !== null) {
|
|
691
|
+
rootFiles[entry] = text;
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
if (Object.keys(rootFiles).length > 0) {
|
|
695
|
+
spec.root_files = rootFiles;
|
|
696
|
+
}
|
|
697
|
+
const metadata = { name, description };
|
|
698
|
+
if (labels) {
|
|
699
|
+
metadata.labels = labels;
|
|
700
|
+
}
|
|
701
|
+
return {
|
|
702
|
+
apiVersion: "github.com/ruinosus/dna/v1",
|
|
703
|
+
kind: "Agent",
|
|
704
|
+
metadata,
|
|
705
|
+
spec,
|
|
706
|
+
};
|
|
707
|
+
}
|
|
708
|
+
_parseFrontmatter(text) {
|
|
709
|
+
const match = text.match(/^---\n([\s\S]*?)---\n?/);
|
|
710
|
+
if (!match)
|
|
711
|
+
return {};
|
|
712
|
+
return yaml.load(match[1]) ?? {};
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
export class AgentWriter {
|
|
716
|
+
fs;
|
|
717
|
+
constructor(fs = nodeFS) {
|
|
718
|
+
this.fs = fs;
|
|
719
|
+
}
|
|
720
|
+
canWrite(raw) {
|
|
721
|
+
return raw.kind === "Agent";
|
|
722
|
+
}
|
|
723
|
+
write(bundle, raw) {
|
|
724
|
+
const path = bundle.path ?? "";
|
|
725
|
+
this.fs.mkdir(path);
|
|
726
|
+
const spec = raw.spec ?? {};
|
|
727
|
+
const meta = raw.metadata ?? {};
|
|
728
|
+
const fm = {};
|
|
729
|
+
fm.name = meta.name || path.split("/").pop() || "";
|
|
730
|
+
if (meta.description)
|
|
731
|
+
fm.description = meta.description;
|
|
732
|
+
if (meta.labels)
|
|
733
|
+
fm.labels = meta.labels;
|
|
734
|
+
for (const field of SPEC_FIELDS) {
|
|
735
|
+
if (field in spec && spec[field]) {
|
|
736
|
+
fm[field] = spec[field];
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
const frontmatter = yaml.dump(fm, {
|
|
740
|
+
flowLevel: -1,
|
|
741
|
+
forceQuotes: false,
|
|
742
|
+
sortKeys: false,
|
|
743
|
+
});
|
|
744
|
+
const body = spec.instruction_file ? "" : (spec.instruction ?? "");
|
|
745
|
+
this.fs.writeFile(`${path}/AGENT.md`, `---\n${frontmatter}---\n\n${body}`);
|
|
746
|
+
// s-sync-s3 — emit the instruction_file FRAGMENT so the bundle is self-
|
|
747
|
+
// contained (twin of Py AgentWriter). Source: carried source_files entry,
|
|
748
|
+
// else the resolved inline instruction. Without this, writing to a fresh
|
|
749
|
+
// bundle left no instruction.md → the agent's instruction resolved empty.
|
|
750
|
+
const instructionFile = spec.instruction_file;
|
|
751
|
+
const sourceFiles = spec.source_files ?? {};
|
|
752
|
+
if (instructionFile) {
|
|
753
|
+
const frag = sourceFiles[instructionFile] ?? spec.instruction;
|
|
754
|
+
if (frag != null)
|
|
755
|
+
this.fs.writeFile(`${path}/${instructionFile}`, String(frag));
|
|
756
|
+
}
|
|
757
|
+
// Any remaining carried text source_files (binaries aren't handled by the
|
|
758
|
+
// string-only FS shim here — covered by the Postgres net for the runtime).
|
|
759
|
+
for (const [rel, content] of Object.entries(sourceFiles)) {
|
|
760
|
+
if (rel === "AGENT.md" || rel === instructionFile)
|
|
761
|
+
continue;
|
|
762
|
+
if (typeof content === "string")
|
|
763
|
+
this.fs.writeFile(`${path}/${rel}`, content);
|
|
764
|
+
}
|
|
765
|
+
// Write known subdirectories
|
|
766
|
+
for (const dirName of ["scripts", "references", "assets"]) {
|
|
767
|
+
const files = spec[dirName];
|
|
768
|
+
if (files != null && typeof files === "object") {
|
|
769
|
+
for (const [fname, fcontent] of Object.entries(files)) {
|
|
770
|
+
this.fs.writeFile(`${path}/${dirName}/${fname}`, fcontent);
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
// Write extras
|
|
775
|
+
const extras = spec.extras;
|
|
776
|
+
if (extras != null && typeof extras === "object") {
|
|
777
|
+
for (const [dirName, dirFiles] of Object.entries(extras)) {
|
|
778
|
+
if (typeof dirFiles === "object") {
|
|
779
|
+
for (const [fname, fcontent] of Object.entries(dirFiles)) {
|
|
780
|
+
this.fs.writeFile(`${path}/${dirName}/${fname}`, fcontent);
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
// Write root files
|
|
786
|
+
const rootFiles = spec.root_files;
|
|
787
|
+
if (rootFiles != null && typeof rootFiles === "object") {
|
|
788
|
+
for (const [fname, fcontent] of Object.entries(rootFiles)) {
|
|
789
|
+
this.fs.writeFile(`${path}/${fname}`, fcontent);
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
serialize(raw) {
|
|
794
|
+
const files = [];
|
|
795
|
+
const spec = raw.spec ?? {};
|
|
796
|
+
const meta = raw.metadata ?? {};
|
|
797
|
+
// 1. AGENT.md
|
|
798
|
+
const fm = {};
|
|
799
|
+
fm.name = meta.name || "";
|
|
800
|
+
if (meta.description)
|
|
801
|
+
fm.description = meta.description;
|
|
802
|
+
if (meta.labels)
|
|
803
|
+
fm.labels = meta.labels;
|
|
804
|
+
for (const field of SPEC_FIELDS) {
|
|
805
|
+
if (field in spec && spec[field])
|
|
806
|
+
fm[field] = spec[field];
|
|
807
|
+
}
|
|
808
|
+
const frontmatter = yaml.dump(fm, { flowLevel: -1, sortKeys: false });
|
|
809
|
+
const serializeBody = spec.instruction_file ? "" : (spec.instruction ?? "");
|
|
810
|
+
files.push({ relativePath: "AGENT.md", content: `---\n${frontmatter}---\n\n${serializeBody}` });
|
|
811
|
+
// s-sync-s3 — emit the instruction_file fragment + carried text
|
|
812
|
+
// source_files so the serialized bundle is self-contained (twin of write()).
|
|
813
|
+
const serInstrFile = spec.instruction_file;
|
|
814
|
+
const serSourceFiles = spec.source_files ?? {};
|
|
815
|
+
if (serInstrFile) {
|
|
816
|
+
const frag = serSourceFiles[serInstrFile] ?? spec.instruction;
|
|
817
|
+
if (frag != null)
|
|
818
|
+
files.push({ relativePath: serInstrFile, content: String(frag) });
|
|
819
|
+
}
|
|
820
|
+
for (const [rel, content] of Object.entries(serSourceFiles)) {
|
|
821
|
+
if (rel === "AGENT.md" || rel === serInstrFile)
|
|
822
|
+
continue;
|
|
823
|
+
if (typeof content === "string")
|
|
824
|
+
files.push({ relativePath: rel, content });
|
|
825
|
+
}
|
|
826
|
+
// 2. Known sub-directories
|
|
827
|
+
for (const dirName of ["scripts", "references", "assets"]) {
|
|
828
|
+
const dirFiles = spec[dirName];
|
|
829
|
+
if (dirFiles != null && typeof dirFiles === "object") {
|
|
830
|
+
for (const [fname, fcontent] of Object.entries(dirFiles)) {
|
|
831
|
+
files.push({ relativePath: `${dirName}/${fname}`, content: fcontent });
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
}
|
|
835
|
+
// 3. Extras
|
|
836
|
+
const extras = spec.extras;
|
|
837
|
+
if (extras != null && typeof extras === "object") {
|
|
838
|
+
for (const [dirName, dirFiles] of Object.entries(extras)) {
|
|
839
|
+
if (typeof dirFiles === "object") {
|
|
840
|
+
for (const [fname, fcontent] of Object.entries(dirFiles)) {
|
|
841
|
+
files.push({ relativePath: `${dirName}/${fname}`, content: fcontent });
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
// 4. Root files
|
|
847
|
+
const rootFiles = spec.root_files;
|
|
848
|
+
if (rootFiles != null && typeof rootFiles === "object") {
|
|
849
|
+
for (const [fname, fcontent] of Object.entries(rootFiles)) {
|
|
850
|
+
files.push({ relativePath: fname, content: fcontent });
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
return files;
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
// ---------------------------------------------------------------------------
|
|
857
|
+
// Extension
|
|
858
|
+
// ---------------------------------------------------------------------------
|
|
859
|
+
// ---------------------------------------------------------------------------
|
|
860
|
+
// Composition profile — declares how Agent composes with other kinds
|
|
861
|
+
// ---------------------------------------------------------------------------
|
|
862
|
+
const HELIX_PROFILE = {
|
|
863
|
+
orchestratorAlias: "helix-agent",
|
|
864
|
+
label: "Helix Agent",
|
|
865
|
+
slots: [
|
|
866
|
+
{
|
|
867
|
+
name: "soul",
|
|
868
|
+
targetAlias: "soulspec-soul",
|
|
869
|
+
cardinality: "one",
|
|
870
|
+
order: 1,
|
|
871
|
+
filterable: false,
|
|
872
|
+
timeline: { label: "Soul", itemLabel: "personality loaded" },
|
|
873
|
+
healthCheck: {
|
|
874
|
+
rule: "at-least-one",
|
|
875
|
+
severity: "warn",
|
|
876
|
+
issueKey: "agents_without_soul",
|
|
877
|
+
message: "Agent has no soul",
|
|
878
|
+
},
|
|
879
|
+
quadrant: null,
|
|
880
|
+
},
|
|
881
|
+
{
|
|
882
|
+
name: "skills",
|
|
883
|
+
targetAlias: "agentskills-skill",
|
|
884
|
+
cardinality: "many",
|
|
885
|
+
order: 2,
|
|
886
|
+
filterable: true,
|
|
887
|
+
timeline: { label: "Skills", itemLabel: "instruction loaded" },
|
|
888
|
+
healthCheck: null,
|
|
889
|
+
quadrant: { axis: "x", label: "Few Skills --> Many Skills", maxScale: 15 },
|
|
890
|
+
},
|
|
891
|
+
{
|
|
892
|
+
name: "guardrails",
|
|
893
|
+
targetAlias: "guardrails-guardrail",
|
|
894
|
+
cardinality: "many",
|
|
895
|
+
order: 3,
|
|
896
|
+
filterable: true,
|
|
897
|
+
timeline: { label: "Guardrails", itemLabel: "rules applied" },
|
|
898
|
+
healthCheck: {
|
|
899
|
+
rule: "at-least-one",
|
|
900
|
+
severity: "warn",
|
|
901
|
+
issueKey: "agents_without_guardrails",
|
|
902
|
+
message: "Agent has no guardrails",
|
|
903
|
+
},
|
|
904
|
+
quadrant: { axis: "y", label: "Few Guardrails --> Many Guardrails", maxScale: 10 },
|
|
905
|
+
},
|
|
906
|
+
{
|
|
907
|
+
name: "tools",
|
|
908
|
+
targetAlias: "helix-tool",
|
|
909
|
+
cardinality: "many",
|
|
910
|
+
order: 4,
|
|
911
|
+
filterable: false,
|
|
912
|
+
timeline: null,
|
|
913
|
+
healthCheck: null,
|
|
914
|
+
quadrant: null,
|
|
915
|
+
},
|
|
916
|
+
{
|
|
917
|
+
name: "actors",
|
|
918
|
+
targetAlias: "helix-actor",
|
|
919
|
+
cardinality: "many",
|
|
920
|
+
order: 5,
|
|
921
|
+
filterable: false,
|
|
922
|
+
timeline: null,
|
|
923
|
+
healthCheck: null,
|
|
924
|
+
quadrant: null,
|
|
925
|
+
},
|
|
926
|
+
],
|
|
927
|
+
};
|
|
928
|
+
// ---------------------------------------------------------------------------
|
|
929
|
+
// HelixExtension
|
|
930
|
+
// ---------------------------------------------------------------------------
|
|
931
|
+
export class HelixExtension {
|
|
932
|
+
fs;
|
|
933
|
+
name = "helix";
|
|
934
|
+
version = "1.0.0";
|
|
935
|
+
constructor(fs = nodeFS) {
|
|
936
|
+
this.fs = fs;
|
|
937
|
+
}
|
|
938
|
+
register(kernel) {
|
|
939
|
+
// Phase 16 cleanup — ModuleKind class deleted. GenomeKind is the
|
|
940
|
+
// canonical root identity Kind.
|
|
941
|
+
kernel.kind(new GenomeKind());
|
|
942
|
+
kernel.kind(new LayerPolicyKind());
|
|
943
|
+
kernel.kind(new AgentKind());
|
|
944
|
+
kernel.kind(new ToolKind());
|
|
945
|
+
kernel.kind(new ActorKind());
|
|
946
|
+
kernel.kind(new UseCaseKind());
|
|
947
|
+
// 2026-05-26 — absorbed from claude-code-templates catalog (MIT).
|
|
948
|
+
// Setting rounds out the Claude-Code-customization primitives that
|
|
949
|
+
// live alongside Skill / UA / Soul / Tool.
|
|
950
|
+
kernel.kind(new SettingKind());
|
|
951
|
+
kernel.kind(new ThemeKind());
|
|
952
|
+
kernel.kind(new UserProfileKind());
|
|
953
|
+
// s-jarvis-canvas (2026-05-27) — shared whiteboard JARVIS ↔ user.
|
|
954
|
+
kernel.kind(new CanvasKind());
|
|
955
|
+
kernel.reader(new AgentReader(this.fs));
|
|
956
|
+
kernel.writer(new AgentWriter(this.fs));
|
|
957
|
+
kernel.compositionProfile(HELIX_PROFILE);
|
|
958
|
+
// s-write-path-despecialize — Agent write rules (platform-agent
|
|
959
|
+
// fork guard, Kind-Writer contract) are pre_save VETO hooks owned by
|
|
960
|
+
// this extension, not kernel special-cases.
|
|
961
|
+
registerWriteGuards(kernel);
|
|
962
|
+
}
|
|
963
|
+
}
|