@kody-ade/kody-engine 0.4.332 → 0.4.334
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/bin/kody.js
CHANGED
|
@@ -15,7 +15,7 @@ var init_package = __esm({
|
|
|
15
15
|
"package.json"() {
|
|
16
16
|
package_default = {
|
|
17
17
|
name: "@kody-ade/kody-engine",
|
|
18
|
-
version: "0.4.
|
|
18
|
+
version: "0.4.334",
|
|
19
19
|
description: "kody \u2014 autonomous development engine. Single-session Claude Code agent behind a generic executor + declarative executable profiles.",
|
|
20
20
|
license: "MIT",
|
|
21
21
|
type: "module",
|
|
@@ -18278,7 +18278,7 @@ function validateOneModel(rawModel, files, label, strictSingleModel, failures, e
|
|
|
18278
18278
|
if (!docsUsed.includes(doc)) failures.push(`${label} docsUsed missing ${doc}`);
|
|
18279
18279
|
}
|
|
18280
18280
|
validateFilesForKind(kind, slug2, files, strictSingleModel, failures);
|
|
18281
|
-
validateModelShape(kind, model, failures);
|
|
18281
|
+
validateModelShape(kind, model, files, slug2, failures);
|
|
18282
18282
|
}
|
|
18283
18283
|
}
|
|
18284
18284
|
function validateFilesForKind(kind, slug2, files, strictSingleModel, failures) {
|
|
@@ -18296,8 +18296,12 @@ function validateFilesForKind(kind, slug2, files, strictSingleModel, failures) {
|
|
|
18296
18296
|
if (strictSingleModel) rejectOtherRoots(paths, [`capabilities/${slug2}/`], "capability", failures);
|
|
18297
18297
|
const profile = parseJsonFile(files, `capabilities/${slug2}/profile.json`, failures);
|
|
18298
18298
|
if (profile) {
|
|
18299
|
+
const profileSlug = stringField6(profile.slug);
|
|
18299
18300
|
const profileName = stringField6(profile.name);
|
|
18300
|
-
if (
|
|
18301
|
+
if (profileSlug && profileSlug !== slug2) failures.push("capability profile slug must match model.slug");
|
|
18302
|
+
if (!profileSlug && profileName && profileName !== slug2) {
|
|
18303
|
+
failures.push("capability profile name must match model.slug when slug is absent");
|
|
18304
|
+
}
|
|
18301
18305
|
if (profile.agent !== void 0)
|
|
18302
18306
|
failures.push("capability profile must not set agent; agent wiring belongs outside capability creation");
|
|
18303
18307
|
if (!["observe", "act", "verify"].includes(stringField6(profile.capabilityKind))) {
|
|
@@ -18312,13 +18316,17 @@ function validateFilesForKind(kind, slug2, files, strictSingleModel, failures) {
|
|
|
18312
18316
|
if (kind === "agentLoop") {
|
|
18313
18317
|
if (!paths.some((filePath) => filePath.endsWith("/state.json")))
|
|
18314
18318
|
failures.push("agentLoop must produce a state.json file");
|
|
18315
|
-
if (strictSingleModel) rejectOtherRoots(paths, ["goals/
|
|
18319
|
+
if (strictSingleModel) rejectOtherRoots(paths, ["goals/", "loops/", "capabilities/"], "agentLoop", failures);
|
|
18316
18320
|
}
|
|
18317
18321
|
if (kind === "workflow") {
|
|
18318
18322
|
requirePath(paths, `capabilities/${slug2}/profile.json`, "workflow capability profile", failures);
|
|
18319
18323
|
const profile = parseJsonFile(files, `capabilities/${slug2}/profile.json`, failures);
|
|
18320
|
-
if (profile
|
|
18321
|
-
|
|
18324
|
+
if (profile) {
|
|
18325
|
+
const hasWorkflowObject = Boolean(profile.workflow && typeof profile.workflow === "object");
|
|
18326
|
+
const hasTopLevelSteps = Array.isArray(profile.steps) && profile.steps.length > 0;
|
|
18327
|
+
if (!hasWorkflowObject && !hasTopLevelSteps) {
|
|
18328
|
+
failures.push("workflow profile must include workflow object or top-level steps");
|
|
18329
|
+
}
|
|
18322
18330
|
}
|
|
18323
18331
|
}
|
|
18324
18332
|
}
|
|
@@ -18336,13 +18344,18 @@ function validateFactoryAssembly(models, failures) {
|
|
|
18336
18344
|
const input = model;
|
|
18337
18345
|
const kind = stringField6(input.kind);
|
|
18338
18346
|
if (kind === "goal") {
|
|
18339
|
-
for (const capability of
|
|
18347
|
+
for (const capability of capabilityRefs(input)) {
|
|
18340
18348
|
if (!available.has(`capability:${capability}`)) {
|
|
18341
18349
|
failures.push(`goal ${stringField6(input.slug)} references missing capability ${capability}`);
|
|
18342
18350
|
}
|
|
18343
18351
|
}
|
|
18344
18352
|
}
|
|
18345
18353
|
if (kind === "workflow") {
|
|
18354
|
+
for (const capability of stringArray5(input.steps)) {
|
|
18355
|
+
if (!available.has(`capability:${capability}`)) {
|
|
18356
|
+
failures.push(`workflow ${stringField6(input.slug)} references missing capability ${capability}`);
|
|
18357
|
+
}
|
|
18358
|
+
}
|
|
18346
18359
|
for (const step of arrayObjects(input.steps)) {
|
|
18347
18360
|
const capability = stringField6(step.capability);
|
|
18348
18361
|
if (capability && !available.has(`capability:${capability}`)) {
|
|
@@ -18351,22 +18364,29 @@ function validateFactoryAssembly(models, failures) {
|
|
|
18351
18364
|
}
|
|
18352
18365
|
}
|
|
18353
18366
|
if (kind === "agentLoop") {
|
|
18354
|
-
const
|
|
18355
|
-
if (
|
|
18356
|
-
|
|
18357
|
-
|
|
18358
|
-
|
|
18359
|
-
|
|
18360
|
-
|
|
18361
|
-
|
|
18367
|
+
const target = wakeTarget(input);
|
|
18368
|
+
if (target) {
|
|
18369
|
+
if (!available.has(`${target.kind}:${target.slug}`)) {
|
|
18370
|
+
failures.push(`agentLoop ${stringField6(input.slug)} references missing ${target.kind} ${target.slug}`);
|
|
18371
|
+
}
|
|
18372
|
+
} else {
|
|
18373
|
+
const targetSlug = stringField6(input.target);
|
|
18374
|
+
const targetExists = ["goal", "workflow", "capability"].some(
|
|
18375
|
+
(kindName) => available.has(`${kindName}:${targetSlug}`)
|
|
18376
|
+
);
|
|
18377
|
+
if (targetSlug && !targetExists) {
|
|
18378
|
+
failures.push(`agentLoop ${stringField6(input.slug)} references missing target ${targetSlug}`);
|
|
18362
18379
|
}
|
|
18363
18380
|
}
|
|
18364
18381
|
}
|
|
18365
18382
|
}
|
|
18366
18383
|
}
|
|
18367
|
-
function validateModelShape(kind, model, failures) {
|
|
18384
|
+
function validateModelShape(kind, model, files, slug2, failures) {
|
|
18368
18385
|
if (kind === "agent") {
|
|
18369
|
-
|
|
18386
|
+
const agentFile = textFile(files, `agents/${slug2}.md`);
|
|
18387
|
+
if (!stringArray5(model.owns).includes("identity") && !containsWord(agentFile, "identity")) {
|
|
18388
|
+
failures.push("agent owns must include identity");
|
|
18389
|
+
}
|
|
18370
18390
|
requireStringArrayIncludes(model.doesNotOwn, "tasks", "agent doesNotOwn", failures);
|
|
18371
18391
|
}
|
|
18372
18392
|
if (kind === "capability") {
|
|
@@ -18381,16 +18401,21 @@ function validateModelShape(kind, model, failures) {
|
|
|
18381
18401
|
requireStringArrayIncludes(model.doesNotOwn, "goal progress", "capability doesNotOwn", failures);
|
|
18382
18402
|
}
|
|
18383
18403
|
if (kind === "goal") {
|
|
18384
|
-
|
|
18385
|
-
if (!
|
|
18404
|
+
const goalState = parseJsonContent(textFile(files, `goals/templates/${slug2}/state.json`));
|
|
18405
|
+
if (!stringField6(model.outcome) && !stringField6(goalState?.outcome))
|
|
18406
|
+
failures.push("goal model must declare outcome");
|
|
18407
|
+
if (evidenceRefs(model).length === 0 && evidenceRefs(goalState).length === 0) {
|
|
18386
18408
|
failures.push("goal model evidence must be non-empty");
|
|
18387
|
-
|
|
18409
|
+
}
|
|
18410
|
+
if (capabilityRefs(model).length === 0 && capabilityRefs(goalState).length === 0) {
|
|
18388
18411
|
failures.push("goal model capabilities must be non-empty");
|
|
18389
18412
|
}
|
|
18390
18413
|
}
|
|
18391
18414
|
if (kind === "agentLoop") {
|
|
18392
18415
|
if (!stringField6(model.cadence)) failures.push("agentLoop model must declare cadence");
|
|
18393
|
-
|
|
18416
|
+
const loopState = parseJsonContent(firstStateFile(files, slug2));
|
|
18417
|
+
const hasTarget = Boolean(wakeTarget(model)) || Boolean(stringField6(model.target)) || Boolean(wakeTarget(loopState)) || Boolean(stringField6(loopState?.target)) || Boolean(loopTargetString(loopState));
|
|
18418
|
+
if (!hasTarget) {
|
|
18394
18419
|
failures.push("agentLoop model must declare wakeTarget object");
|
|
18395
18420
|
}
|
|
18396
18421
|
}
|
|
@@ -18398,6 +18423,48 @@ function validateModelShape(kind, model, failures) {
|
|
|
18398
18423
|
if (!Array.isArray(model.steps) || model.steps.length === 0) failures.push("workflow model steps must be non-empty");
|
|
18399
18424
|
}
|
|
18400
18425
|
}
|
|
18426
|
+
function capabilityRefs(value) {
|
|
18427
|
+
return [...stringArray5(value?.capabilities), ...stringArray5(value?.allowedCapabilities)];
|
|
18428
|
+
}
|
|
18429
|
+
function evidenceRefs(value) {
|
|
18430
|
+
if (!value) return [];
|
|
18431
|
+
if (Array.isArray(value.evidence)) return value.evidence.map((item) => String(item).trim()).filter(Boolean);
|
|
18432
|
+
if (value.evidence && typeof value.evidence === "object") return Object.keys(value.evidence);
|
|
18433
|
+
return [];
|
|
18434
|
+
}
|
|
18435
|
+
function wakeTarget(value) {
|
|
18436
|
+
const raw = value?.wakeTarget;
|
|
18437
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return null;
|
|
18438
|
+
const target = raw;
|
|
18439
|
+
const type = stringField6(target.type);
|
|
18440
|
+
const slug2 = stringField6(target.slug);
|
|
18441
|
+
if ((type === "goal" || type === "workflow" || type === "capability") && slug2) return { kind: type, slug: slug2 };
|
|
18442
|
+
return null;
|
|
18443
|
+
}
|
|
18444
|
+
function loopTargetString(value) {
|
|
18445
|
+
const loop = value?.loop;
|
|
18446
|
+
if (!loop || typeof loop !== "object" || Array.isArray(loop)) return "";
|
|
18447
|
+
return stringField6(loop.target);
|
|
18448
|
+
}
|
|
18449
|
+
function textFile(files, wantedPath) {
|
|
18450
|
+
return files.find((item) => normalizeBundlePath(item.path) === wantedPath)?.content ?? "";
|
|
18451
|
+
}
|
|
18452
|
+
function firstStateFile(files, slug2) {
|
|
18453
|
+
const normalizedSlug = `${slug2}/state.json`;
|
|
18454
|
+
return files.find((item) => normalizeBundlePath(item.path).endsWith(normalizedSlug))?.content ?? "";
|
|
18455
|
+
}
|
|
18456
|
+
function parseJsonContent(content) {
|
|
18457
|
+
if (!content.trim()) return null;
|
|
18458
|
+
try {
|
|
18459
|
+
const parsed = JSON.parse(content);
|
|
18460
|
+
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
|
|
18461
|
+
} catch {
|
|
18462
|
+
return null;
|
|
18463
|
+
}
|
|
18464
|
+
}
|
|
18465
|
+
function containsWord(content, word) {
|
|
18466
|
+
return new RegExp(`\\b${word}\\b`, "i").test(content);
|
|
18467
|
+
}
|
|
18401
18468
|
function parseJsonFile(files, wantedPath, failures) {
|
|
18402
18469
|
const file = files.find((item) => normalizeBundlePath(item.path) === wantedPath);
|
|
18403
18470
|
if (!file) return null;
|
|
@@ -38,6 +38,27 @@ Use these contracts when deciding whether a file belongs in the bundle:
|
|
|
38
38
|
|
|
39
39
|
If one generated model needs information from another, reference the other model by slug. Do not copy that model's responsibility into the file.
|
|
40
40
|
|
|
41
|
+
Use these exact model kinds and required file shapes:
|
|
42
|
+
|
|
43
|
+
| Model kind | Required files |
|
|
44
|
+
| --- | --- |
|
|
45
|
+
| `agent` | `agents/<slug>.md` |
|
|
46
|
+
| `goal` | `goals/templates/<slug>/state.json` |
|
|
47
|
+
| `agentLoop` | a `state.json` under `goals/<slug>/` or `loops/<slug>/` |
|
|
48
|
+
| `workflow` | `capabilities/<slug>/profile.json` containing a `workflow` object or top-level `steps` |
|
|
49
|
+
| `capability` | `capabilities/<slug>/profile.json` and `capabilities/<slug>/capability.md` |
|
|
50
|
+
|
|
51
|
+
Do not use `kind: "loop"`; the loop model kind is `agentLoop`.
|
|
52
|
+
Do not use `agents/<slug>/identity.json`, `goals/<slug>/goal.json`, `workflows/<slug>/workflow.json`, or `executables/<slug>/...`.
|
|
53
|
+
|
|
54
|
+
Each `models[]` entry must also carry the creator's canonical model metadata:
|
|
55
|
+
|
|
56
|
+
- Agent models include `"owns": ["identity", "judgment", "boundaries"]` and `doesNotOwn` includes `"tasks"`.
|
|
57
|
+
- Goal models include `"outcome"`, non-empty `"evidence"`, and non-empty `"capabilities"`. Use `"capabilities"`, not only `"allowedCapabilities"`, in `models[]`.
|
|
58
|
+
- Loop models use `"kind": "agentLoop"` and include `"wakeTarget": {"type": "goal|workflow|capability", "slug": "target-slug"}`. Use `"wakeTarget"`, not only `"target"`, in `models[]`.
|
|
59
|
+
- Workflow models include non-empty `"steps"` as objects with a `"capability"` slug.
|
|
60
|
+
- Capability models include `"capabilityKind"`, `"ability"`, `"inputs"`, `"outputs"`, `"allowedActions"`, `"forbiddenActions"`, and `doesNotOwn` includes `"agent identity"` and `"goal progress"`.
|
|
61
|
+
|
|
41
62
|
Capability files must be shaped by ability, kind, interface, and constraints. Do not include fields or prose that make the capability depend on who asked for it, which workflow calls it, which goal consumes it, which loop wakes it, or which agent may run it.
|
|
42
63
|
|
|
43
64
|
Use the current Kody vocabulary:
|
|
@@ -63,6 +63,8 @@ Required files:
|
|
|
63
63
|
- `capabilities/<slug>/profile.json`
|
|
64
64
|
- `capabilities/<slug>/capability.md`
|
|
65
65
|
|
|
66
|
+
In `profile.json`, include `"slug": "<slug>"`. The `"name"` field may be a display name, but if `slug` is absent then `name` must equal the slug.
|
|
67
|
+
|
|
66
68
|
Add colocated prompt/scripts only if the capability needs a new implementation. Reuse existing capabilities, implementation profiles, skills, or scripts when they fit.
|
|
67
69
|
|
|
68
70
|
# Final Output Contract
|
|
@@ -50,6 +50,8 @@ Do not call Bash, Write, Edit, mkdir, cat, tee, printf, python, node, git, gh, o
|
|
|
50
50
|
|
|
51
51
|
Prefer placing workflow steps on the public capability that owns the composed action. Do not create a workflow when a single capability is enough.
|
|
52
52
|
|
|
53
|
+
Put the workflow contract in `capabilities/<slug>/profile.json`. Prefer a `workflow` object with `steps`; top-level `steps` are only for existing profiles that already use that shape.
|
|
54
|
+
|
|
53
55
|
# Final Output Contract
|
|
54
56
|
|
|
55
57
|
If the request is too ambiguous to produce one review-ready Workflow model, output one line:
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kody-ade/kody-engine",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.334",
|
|
4
4
|
"description": "kody — autonomous development engine. Single-session Claude Code agent behind a generic executor + declarative executable profiles.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|