@kody-ade/kody-engine 0.4.585 → 0.4.587
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 +68 -11
- package/package.json +1 -1
- package/templates/kody.yml +5 -1
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.587",
|
|
19
19
|
description: "kody \u2014 autonomous development engine. Single-session Claude Code agent behind a generic executor + declarative implementation profiles.",
|
|
20
20
|
license: "MIT",
|
|
21
21
|
type: "module",
|
|
@@ -2189,6 +2189,7 @@ function readCapabilityFolder(root, slug) {
|
|
|
2189
2189
|
...contract.execution ? { execution: contract.execution } : {},
|
|
2190
2190
|
...contract.deliveryPolicy ? { deliveryPolicy: contract.deliveryPolicy } : {},
|
|
2191
2191
|
...contract.deliveryPathAllowlist ? { deliveryPathAllowlist: contract.deliveryPathAllowlist } : {},
|
|
2192
|
+
...contract.deliveryConfigAllowlist ? { deliveryConfigAllowlist: contract.deliveryConfigAllowlist } : {},
|
|
2192
2193
|
input: contract.input,
|
|
2193
2194
|
output: contract.output
|
|
2194
2195
|
} : {},
|
|
@@ -2229,11 +2230,18 @@ function parseCapabilityContract(raw) {
|
|
|
2229
2230
|
throw new Error('contract.json requiredSubagents are supported only when execution is "agent"');
|
|
2230
2231
|
}
|
|
2231
2232
|
const deliveryPathAllowlist = parseDeliveryPathAllowlist(parsed.deliveryPathAllowlist);
|
|
2233
|
+
const deliveryConfigAllowlist = parseDeliveryConfigAllowlist(parsed.deliveryConfigAllowlist);
|
|
2232
2234
|
if (deliveryPathAllowlist && parsed.execution !== "agent") {
|
|
2233
2235
|
throw new Error('contract.json deliveryPathAllowlist is supported only when execution is "agent"');
|
|
2234
2236
|
}
|
|
2237
|
+
if (deliveryConfigAllowlist && parsed.execution !== "agent") {
|
|
2238
|
+
throw new Error('contract.json deliveryConfigAllowlist is supported only when execution is "agent"');
|
|
2239
|
+
}
|
|
2240
|
+
if (deliveryConfigAllowlist && Object.keys(deliveryConfigAllowlist).some((filePath) => !deliveryPathAllowlist?.includes(filePath))) {
|
|
2241
|
+
throw new Error("contract.json deliveryConfigAllowlist files must also be deliveryPathAllowlist entries");
|
|
2242
|
+
}
|
|
2235
2243
|
const unsupported = Object.keys(parsed).filter(
|
|
2236
|
-
(key) => key !== "execution" && key !== "deliveryPolicy" && key !== "deliveryPathAllowlist" && key !== "requirements" && key !== "secrets" && key !== "timeoutMs" && key !== "requiredSubagents" && key !== "input" && key !== "output"
|
|
2244
|
+
(key) => key !== "execution" && key !== "deliveryPolicy" && key !== "deliveryPathAllowlist" && key !== "deliveryConfigAllowlist" && key !== "requirements" && key !== "secrets" && key !== "timeoutMs" && key !== "requiredSubagents" && key !== "input" && key !== "output"
|
|
2237
2245
|
);
|
|
2238
2246
|
if (unsupported.length > 0) {
|
|
2239
2247
|
throw new Error(`contract.json contains unsupported fields: ${unsupported.join(", ")}`);
|
|
@@ -2248,6 +2256,7 @@ function parseCapabilityContract(raw) {
|
|
|
2248
2256
|
...parsed.execution ? { execution: parsed.execution } : {},
|
|
2249
2257
|
...parsed.deliveryPolicy === "checkpoint" ? { deliveryPolicy: "checkpoint" } : {},
|
|
2250
2258
|
...deliveryPathAllowlist ? { deliveryPathAllowlist } : {},
|
|
2259
|
+
...deliveryConfigAllowlist ? { deliveryConfigAllowlist } : {},
|
|
2251
2260
|
...requirements ? { requirements } : {},
|
|
2252
2261
|
...secrets ? { secrets } : {},
|
|
2253
2262
|
...timeoutMs !== void 0 ? { timeoutMs } : {},
|
|
@@ -2256,6 +2265,23 @@ function parseCapabilityContract(raw) {
|
|
|
2256
2265
|
output: parsed.output
|
|
2257
2266
|
};
|
|
2258
2267
|
}
|
|
2268
|
+
function parseDeliveryConfigAllowlist(raw) {
|
|
2269
|
+
if (raw === void 0) return void 0;
|
|
2270
|
+
if (!isPlainObject(raw) || Object.keys(raw).length === 0) {
|
|
2271
|
+
throw new Error("contract.json deliveryConfigAllowlist must be a non-empty object");
|
|
2272
|
+
}
|
|
2273
|
+
const parsed = {};
|
|
2274
|
+
for (const [filePath, paths] of Object.entries(raw)) {
|
|
2275
|
+
if (filePath !== "kody.config.json" || !Array.isArray(paths) || paths.length === 0 || paths.length > 32) {
|
|
2276
|
+
throw new Error("contract.json deliveryConfigAllowlist contains an unsupported config file or path list");
|
|
2277
|
+
}
|
|
2278
|
+
if (!paths.every((value) => typeof value === "string" && /^[A-Za-z][A-Za-z0-9]*(?:\.[A-Za-z][A-Za-z0-9]*)*$/.test(value))) {
|
|
2279
|
+
throw new Error("contract.json deliveryConfigAllowlist must contain safe dotted config paths");
|
|
2280
|
+
}
|
|
2281
|
+
parsed[filePath] = [...new Set(paths)];
|
|
2282
|
+
}
|
|
2283
|
+
return parsed;
|
|
2284
|
+
}
|
|
2259
2285
|
function parseDeliveryPathAllowlist(raw) {
|
|
2260
2286
|
if (raw === void 0) return void 0;
|
|
2261
2287
|
if (!Array.isArray(raw) || raw.length === 0 || raw.length > 64) {
|
|
@@ -8734,12 +8760,32 @@ function isSafeConfigActivationChange(before, after) {
|
|
|
8734
8760
|
}
|
|
8735
8761
|
return isDeepStrictEqual(beforeCompanyRest, afterCompanyRest);
|
|
8736
8762
|
}
|
|
8737
|
-
function
|
|
8763
|
+
function isSafeConfigChange(before, after, allowedPaths = []) {
|
|
8764
|
+
if (!isRecord(before) || !isRecord(after)) return false;
|
|
8765
|
+
const beforeCopy = structuredClone(before);
|
|
8766
|
+
const afterCopy = structuredClone(after);
|
|
8767
|
+
for (const dottedPath of allowedPaths) {
|
|
8768
|
+
deleteConfigPath(beforeCopy, dottedPath);
|
|
8769
|
+
deleteConfigPath(afterCopy, dottedPath);
|
|
8770
|
+
}
|
|
8771
|
+
return isSafeConfigActivationChange(beforeCopy, afterCopy);
|
|
8772
|
+
}
|
|
8773
|
+
function deleteConfigPath(value, dottedPath) {
|
|
8774
|
+
const segments = dottedPath.split(".");
|
|
8775
|
+
let cursor = value;
|
|
8776
|
+
for (const segment of segments.slice(0, -1)) {
|
|
8777
|
+
const next = cursor[segment];
|
|
8778
|
+
if (!isRecord(next)) return;
|
|
8779
|
+
cursor = next;
|
|
8780
|
+
}
|
|
8781
|
+
delete cursor[segments.at(-1)];
|
|
8782
|
+
}
|
|
8783
|
+
function isTrustedConfigActivationChange(filePath, deliveryPathAllowlist, deliveryConfigAllowlist, cwd) {
|
|
8738
8784
|
if (filePath !== "kody.config.json" || !deliveryPathAllowlist.includes(filePath)) return false;
|
|
8739
8785
|
try {
|
|
8740
8786
|
const before = JSON.parse(git(["show", "HEAD:kody.config.json"], cwd));
|
|
8741
8787
|
const after = JSON.parse(fs29.readFileSync(path28.join(cwd ?? process.cwd(), filePath), "utf-8"));
|
|
8742
|
-
return
|
|
8788
|
+
return isSafeConfigChange(before, after, deliveryConfigAllowlist[filePath] ?? []);
|
|
8743
8789
|
} catch {
|
|
8744
8790
|
return false;
|
|
8745
8791
|
}
|
|
@@ -8790,19 +8836,19 @@ function normalizeCommitMessage(raw) {
|
|
|
8790
8836
|
}
|
|
8791
8837
|
return `chore: ${trimmed}`;
|
|
8792
8838
|
}
|
|
8793
|
-
function commitAndPush(branch, agentMessage, cwd, deliveryPathAllowlist = []) {
|
|
8839
|
+
function commitAndPush(branch, agentMessage, cwd, deliveryPathAllowlist = [], deliveryConfigAllowlist = {}) {
|
|
8794
8840
|
const ignoredAllowedFiles = listAllowlistedIgnoredFiles(deliveryPathAllowlist, cwd);
|
|
8795
8841
|
const ignoredAllowedSet = new Set(ignoredAllowedFiles);
|
|
8796
8842
|
const allChanged = [.../* @__PURE__ */ new Set([...listChangedFiles(cwd), ...ignoredAllowedFiles])];
|
|
8797
8843
|
const allowedFiles = allChanged.filter(
|
|
8798
|
-
(f) => !isForbiddenPath(f, deliveryPathAllowlist) || isTrustedConfigActivationChange(f, deliveryPathAllowlist, cwd)
|
|
8844
|
+
(f) => !isForbiddenPath(f, deliveryPathAllowlist) || isTrustedConfigActivationChange(f, deliveryPathAllowlist, deliveryConfigAllowlist, cwd)
|
|
8799
8845
|
);
|
|
8800
8846
|
const mergeHeadExists = fs29.existsSync(path28.join(cwd ?? process.cwd(), ".git", "MERGE_HEAD"));
|
|
8801
8847
|
if (allowedFiles.length === 0 && !mergeHeadExists) {
|
|
8802
8848
|
return { committed: false, pushed: false, sha: "", message: "" };
|
|
8803
8849
|
}
|
|
8804
8850
|
const forbiddenFiles = allChanged.filter(
|
|
8805
|
-
(f) => isForbiddenPath(f, deliveryPathAllowlist) && !isTrustedConfigActivationChange(f, deliveryPathAllowlist, cwd)
|
|
8851
|
+
(f) => isForbiddenPath(f, deliveryPathAllowlist) && !isTrustedConfigActivationChange(f, deliveryPathAllowlist, deliveryConfigAllowlist, cwd)
|
|
8806
8852
|
);
|
|
8807
8853
|
for (const f of forbiddenFiles) {
|
|
8808
8854
|
try {
|
|
@@ -12472,8 +12518,15 @@ var init_commitAndPush = __esm({
|
|
|
12472
12518
|
}
|
|
12473
12519
|
const message = ctx.data.commitMessage || DEFAULT_COMMIT_MESSAGE;
|
|
12474
12520
|
const deliveryPathAllowlist = Array.isArray(ctx.data.deliveryPathAllowlist) ? ctx.data.deliveryPathAllowlist : [];
|
|
12521
|
+
const deliveryConfigAllowlist = ctx.data.deliveryConfigAllowlist && typeof ctx.data.deliveryConfigAllowlist === "object" ? ctx.data.deliveryConfigAllowlist : {};
|
|
12475
12522
|
try {
|
|
12476
|
-
const result2 = commitAndPush(
|
|
12523
|
+
const result2 = commitAndPush(
|
|
12524
|
+
branch,
|
|
12525
|
+
message,
|
|
12526
|
+
ctx.cwd,
|
|
12527
|
+
deliveryPathAllowlist,
|
|
12528
|
+
deliveryConfigAllowlist
|
|
12529
|
+
);
|
|
12477
12530
|
ctx.data.commitResult = result2;
|
|
12478
12531
|
const postCommitFiles = result2.committed ? listFilesInCommit("HEAD", ctx.cwd) : listChangedFiles(ctx.cwd);
|
|
12479
12532
|
ctx.data.changedFiles = postCommitFiles.filter(
|
|
@@ -16456,6 +16509,9 @@ var init_loadSimpleCapability = __esm({
|
|
|
16456
16509
|
if (capability.contract?.deliveryPathAllowlist) {
|
|
16457
16510
|
ctx.data.deliveryPathAllowlist = capability.contract.deliveryPathAllowlist;
|
|
16458
16511
|
}
|
|
16512
|
+
if (capability.contract?.deliveryConfigAllowlist) {
|
|
16513
|
+
ctx.data.deliveryConfigAllowlist = capability.contract.deliveryConfigAllowlist;
|
|
16514
|
+
}
|
|
16459
16515
|
if (capability.contract?.requirements) {
|
|
16460
16516
|
ctx.data.capabilityRequirements = capability.contract.requirements;
|
|
16461
16517
|
}
|
|
@@ -17481,7 +17537,7 @@ var init_parseReproOutput = __esm({
|
|
|
17481
17537
|
|
|
17482
17538
|
// src/scripts/parseSimpleCapabilityOutput.ts
|
|
17483
17539
|
import * as fs47 from "fs";
|
|
17484
|
-
function
|
|
17540
|
+
function acceptAuthoritativeCapabilityOutput(ctx, profile, output) {
|
|
17485
17541
|
ctx.data.agentDone = true;
|
|
17486
17542
|
delete ctx.data.agentFailureReason;
|
|
17487
17543
|
const summary = isObject2(output) && typeof output.summary === "string" ? output.summary.trim() : "";
|
|
@@ -17571,7 +17627,8 @@ var init_parseSimpleCapabilityOutput = __esm({
|
|
|
17571
17627
|
}
|
|
17572
17628
|
const outputPath = typeof ctx.data.capabilityOutputPath === "string" ? ctx.data.capabilityOutputPath : void 0;
|
|
17573
17629
|
const fileOutput = readOutputFile(outputPath);
|
|
17574
|
-
const
|
|
17630
|
+
const hasScriptOutput = Object.hasOwn(ctx.data, "capabilityScriptOutput");
|
|
17631
|
+
const output = fileOutput.found ? fileOutput.value : hasScriptOutput ? ctx.data.capabilityScriptOutput : parseOutput(agentResult?.finalText);
|
|
17575
17632
|
if (output === void 0) {
|
|
17576
17633
|
const reason2 = agentResult?.outcomeKind === "out_of_turns" ? "Capability execution limit reached" : "Capability execution ended before returning a result";
|
|
17577
17634
|
const blocked = {
|
|
@@ -17618,7 +17675,7 @@ var init_parseSimpleCapabilityOutput = __esm({
|
|
|
17618
17675
|
return;
|
|
17619
17676
|
}
|
|
17620
17677
|
}
|
|
17621
|
-
if (fileOutput.found)
|
|
17678
|
+
if (fileOutput.found || hasScriptOutput) acceptAuthoritativeCapabilityOutput(ctx, profile, output);
|
|
17622
17679
|
ctx.data.capabilityOutput = output;
|
|
17623
17680
|
const structuredResult = parseCapabilityResult(output);
|
|
17624
17681
|
if (structuredResult) {
|
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.587",
|
|
4
4
|
"description": "kody — autonomous development engine. Single-session Claude Code agent behind a generic executor + declarative implementation profiles.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
package/templates/kody.yml
CHANGED
|
@@ -14,6 +14,10 @@ on:
|
|
|
14
14
|
description: 'GitHub issue number (agent mode)'
|
|
15
15
|
type: string
|
|
16
16
|
default: ''
|
|
17
|
+
requestId:
|
|
18
|
+
description: 'Unique Kody execution request ID'
|
|
19
|
+
type: string
|
|
20
|
+
default: ''
|
|
17
21
|
sessionId:
|
|
18
22
|
description: 'Chat session ID (chat mode, from Kody Dashboard)'
|
|
19
23
|
type: string
|
|
@@ -55,7 +59,7 @@ jobs:
|
|
|
55
59
|
runs-on: ubuntu-latest
|
|
56
60
|
timeout-minutes: 360
|
|
57
61
|
concurrency:
|
|
58
|
-
group: kody-${{ inputs.sessionId || inputs.issue_number || github.event.issue.number || github.sha }}
|
|
62
|
+
group: kody-${{ inputs.requestId || inputs.sessionId || inputs.issue_number || github.event.issue.number || github.sha }}
|
|
59
63
|
cancel-in-progress: false
|
|
60
64
|
permissions:
|
|
61
65
|
issues: write
|