@kody-ade/kody-engine 0.4.657 → 0.4.658
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 +90 -51
- package/package.json +1 -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.658",
|
|
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
|
repository: {
|
|
@@ -9525,12 +9525,12 @@ function isSafeConfigActivationChange(before, after) {
|
|
|
9525
9525
|
const beforeCompanyRest = { ...beforeCompany };
|
|
9526
9526
|
const afterCompanyRest = { ...afterCompany };
|
|
9527
9527
|
for (const field of ACTIVATION_FIELDS) {
|
|
9528
|
-
const previous = beforeCompany[field];
|
|
9528
|
+
const previous = beforeCompany[field] === void 0 ? [] : beforeCompany[field];
|
|
9529
9529
|
const next = afterCompany[field];
|
|
9530
9530
|
delete beforeCompanyRest[field];
|
|
9531
9531
|
delete afterCompanyRest[field];
|
|
9532
|
-
if (
|
|
9533
|
-
if (!Array.isArray(previous
|
|
9532
|
+
if (beforeCompany[field] === void 0 && next === void 0) continue;
|
|
9533
|
+
if (!Array.isArray(previous) || !Array.isArray(next)) return false;
|
|
9534
9534
|
if (!next.every((value) => typeof value === "string")) return false;
|
|
9535
9535
|
if (!previous.every((value) => typeof value === "string")) return false;
|
|
9536
9536
|
const nextValues = new Set(next);
|
|
@@ -9569,6 +9569,9 @@ function isTrustedConfigActivationChange(filePath, deliveryPathAllowlist, delive
|
|
|
9569
9569
|
}
|
|
9570
9570
|
}
|
|
9571
9571
|
function listChangedFiles(cwd) {
|
|
9572
|
+
return [...new Set(listChangedPathGroups(cwd).flat())];
|
|
9573
|
+
}
|
|
9574
|
+
function listChangedPathGroups(cwd) {
|
|
9572
9575
|
const raw = execFileSync6("git", ["status", "--porcelain=v1", "-z", "--untracked-files=all"], {
|
|
9573
9576
|
encoding: "utf-8",
|
|
9574
9577
|
maxBuffer: GIT_MAX_BUFFER_BYTES,
|
|
@@ -9577,8 +9580,19 @@ function listChangedFiles(cwd) {
|
|
|
9577
9580
|
stdio: ["pipe", "pipe", "pipe"]
|
|
9578
9581
|
});
|
|
9579
9582
|
if (!raw) return [];
|
|
9580
|
-
const entries = raw.split("\0")
|
|
9581
|
-
|
|
9583
|
+
const entries = raw.split("\0");
|
|
9584
|
+
const groups = [];
|
|
9585
|
+
for (let index = 0; index < entries.length - 1; index++) {
|
|
9586
|
+
const entry = entries[index];
|
|
9587
|
+
const paths = [entry.slice(3)];
|
|
9588
|
+
if (/[RC]/.test(entry.slice(0, 2))) {
|
|
9589
|
+
const source = entries[++index];
|
|
9590
|
+
if (!source) throw new Error("git status returned an incomplete rename/copy record");
|
|
9591
|
+
paths.push(source);
|
|
9592
|
+
}
|
|
9593
|
+
groups.push(paths);
|
|
9594
|
+
}
|
|
9595
|
+
return groups;
|
|
9582
9596
|
}
|
|
9583
9597
|
function listAllowlistedIgnoredFiles(deliveryPathAllowlist, cwd) {
|
|
9584
9598
|
if (deliveryPathAllowlist.length === 0) return [];
|
|
@@ -9617,27 +9631,26 @@ function normalizeCommitMessage(raw) {
|
|
|
9617
9631
|
function commitAndPush(branch, agentMessage, cwd, deliveryPathAllowlist = [], deliveryConfigAllowlist = {}) {
|
|
9618
9632
|
const ignoredAllowedFiles = listAllowlistedIgnoredFiles(deliveryPathAllowlist, cwd);
|
|
9619
9633
|
const ignoredAllowedSet = new Set(ignoredAllowedFiles);
|
|
9620
|
-
const
|
|
9621
|
-
const
|
|
9622
|
-
(
|
|
9623
|
-
|
|
9624
|
-
|
|
9625
|
-
(f) => isForbiddenPath(f, deliveryPathAllowlist) && !isTrustedConfigActivationChange(f, deliveryPathAllowlist, deliveryConfigAllowlist, cwd)
|
|
9634
|
+
const pathGroups = [...listChangedPathGroups(cwd), ...ignoredAllowedFiles.map((file) => [file])];
|
|
9635
|
+
const blockedGroups = pathGroups.filter(
|
|
9636
|
+
(group) => group.some(
|
|
9637
|
+
(f) => isForbiddenPath(f, deliveryPathAllowlist) && !isTrustedConfigActivationChange(f, deliveryPathAllowlist, deliveryConfigAllowlist, cwd)
|
|
9638
|
+
)
|
|
9626
9639
|
);
|
|
9640
|
+
const forbiddenFiles = [...new Set(blockedGroups.flat())];
|
|
9641
|
+
const forbiddenSet = new Set(forbiddenFiles);
|
|
9642
|
+
const allowedFiles = [...new Set(pathGroups.flat())].filter((file) => !forbiddenSet.has(file));
|
|
9627
9643
|
const omittedFiles = forbiddenFiles.filter(isReportableDeliveryOmission);
|
|
9628
9644
|
const mergeHeadExists = fs30.existsSync(path28.join(cwd ?? process.cwd(), ".git", "MERGE_HEAD"));
|
|
9645
|
+
for (const f of forbiddenFiles) {
|
|
9646
|
+
git(["--literal-pathspecs", "reset", "-q", "--", f], cwd);
|
|
9647
|
+
}
|
|
9629
9648
|
if (allowedFiles.length === 0 && !mergeHeadExists) {
|
|
9630
9649
|
return { committed: false, pushed: false, sha: "", message: "", omittedFiles };
|
|
9631
9650
|
}
|
|
9632
|
-
for (const f of forbiddenFiles) {
|
|
9633
|
-
try {
|
|
9634
|
-
git(["reset", "-q", "--", f], cwd);
|
|
9635
|
-
} catch {
|
|
9636
|
-
}
|
|
9637
|
-
}
|
|
9638
9651
|
for (const f of allowedFiles) {
|
|
9639
9652
|
try {
|
|
9640
|
-
git(["add", ...ignoredAllowedSet.has(f) ? ["--force"] : [], "--", f], cwd);
|
|
9653
|
+
git(["--literal-pathspecs", "add", ...ignoredAllowedSet.has(f) ? ["--force"] : [], "--", f], cwd);
|
|
9641
9654
|
} catch {
|
|
9642
9655
|
}
|
|
9643
9656
|
}
|
|
@@ -9652,8 +9665,8 @@ function commitAndPush(branch, agentMessage, cwd, deliveryPathAllowlist = [], de
|
|
|
9652
9665
|
}
|
|
9653
9666
|
throw err;
|
|
9654
9667
|
}
|
|
9655
|
-
const sha = git(["rev-parse", "HEAD"], cwd).slice(0, 7);
|
|
9656
9668
|
const pushResult = pushWithRetry({ cwd, branch, setUpstream: true });
|
|
9669
|
+
const sha = git(["rev-parse", "HEAD"], cwd).slice(0, 7);
|
|
9657
9670
|
if (pushResult.ok) {
|
|
9658
9671
|
return { committed: true, pushed: true, sha, message, omittedFiles };
|
|
9659
9672
|
}
|
|
@@ -25175,6 +25188,9 @@ function shouldRunCapabilityWorkflow(job, workflow, capabilityIdentity, selected
|
|
|
25175
25188
|
return requestedImplementation === selectedImplementation || requestedImplementation === capabilityIdentity || requestedImplementation === job.action;
|
|
25176
25189
|
}
|
|
25177
25190
|
async function runCapabilityWorkflow(parent, workflow, capability, base, checkpoint) {
|
|
25191
|
+
if (parent.workflowState?.status === "done") {
|
|
25192
|
+
return { exitCode: 0, workflowState: structuredClone(parent.workflowState), usage: parent.workflowState.usage };
|
|
25193
|
+
}
|
|
25178
25194
|
const invalid = workflowError(workflow, base);
|
|
25179
25195
|
if (invalid) {
|
|
25180
25196
|
if (isGraphWorkflow(workflow)) {
|
|
@@ -25407,6 +25423,20 @@ async function runGraphCapabilityWorkflow(parent, workflow, capability, base, ch
|
|
|
25407
25423
|
return { ...result, exitCode: 64, reason, workflowState: state };
|
|
25408
25424
|
}
|
|
25409
25425
|
const label = step.action ?? step.capability;
|
|
25426
|
+
if (!shouldRunWorkflowStep(step, chainData)) {
|
|
25427
|
+
process.stdout.write(
|
|
25428
|
+
`\u2192 kody: workflow ${capability.slug} step ${index + 1}/${workflow.steps.length} \u2192 ${label} (skipped)
|
|
25429
|
+
|
|
25430
|
+
`
|
|
25431
|
+
);
|
|
25432
|
+
result = { exitCode: 0 };
|
|
25433
|
+
delete chainData.workflowLastResult;
|
|
25434
|
+
delete chainData.workflowLastOutput;
|
|
25435
|
+
delete chainData.workflowLastOutcome;
|
|
25436
|
+
const terminal2 = await advanceGraphWorkflow(step, capability, state, result, chainData, checkpoint, true);
|
|
25437
|
+
if (terminal2) return terminal2;
|
|
25438
|
+
continue;
|
|
25439
|
+
}
|
|
25410
25440
|
if (step.approval === "required") {
|
|
25411
25441
|
const approvalState = requireWorkflowStepApproval(state, step.id);
|
|
25412
25442
|
state.status = approvalState.status;
|
|
@@ -25500,42 +25530,47 @@ async function runGraphCapabilityWorkflow(parent, workflow, capability, base, ch
|
|
|
25500
25530
|
await checkpoint?.(state);
|
|
25501
25531
|
return withWorkflowBoundaryEval(capability, { ...result, workflowState: state });
|
|
25502
25532
|
}
|
|
25503
|
-
|
|
25504
|
-
|
|
25505
|
-
}
|
|
25506
|
-
const resultConditionPaths = workflowResultConditionPaths(step.next);
|
|
25507
|
-
if (resultConditionPaths.length > 0 && !result.capabilityResults?.at(-1)) {
|
|
25508
|
-
const reason = `workflow step ${step.id} did not emit the structured result required by its conditions: ${resultConditionPaths.join(", ")}`;
|
|
25509
|
-
state.status = "blocked";
|
|
25510
|
-
state.blocker = reason;
|
|
25511
|
-
await checkpoint?.(state);
|
|
25512
|
-
return { ...result, exitCode: 64, reason, workflowState: state };
|
|
25513
|
-
}
|
|
25514
|
-
const transition = selectWorkflowTransition(step, chainData, state.transitionCounts);
|
|
25515
|
-
if (!transition) {
|
|
25516
|
-
const exhausted = exhaustedWorkflowTransitions(step, chainData, state.transitionCounts);
|
|
25517
|
-
const reason = exhausted.length > 0 ? `workflow step ${step.id} reached iteration limit: ${exhausted.join(", ")}` : `workflow step ${step.id} has no available connection`;
|
|
25518
|
-
state.status = "blocked";
|
|
25519
|
-
state.blocker = reason;
|
|
25520
|
-
await checkpoint?.(state);
|
|
25521
|
-
return { ...result, exitCode: 64, reason, workflowState: state };
|
|
25522
|
-
}
|
|
25523
|
-
if (transition.maxIterations !== void 0) {
|
|
25524
|
-
const key = `${step.id}->${transition.to}`;
|
|
25525
|
-
state.transitionCounts[key] = (state.transitionCounts[key] ?? 0) + 1;
|
|
25526
|
-
}
|
|
25527
|
-
if (transition.to === "$end") {
|
|
25528
|
-
return completeWorkflowAtTerminal(capability, state, result, checkpoint);
|
|
25529
|
-
}
|
|
25530
|
-
state.currentStepId = transition.to;
|
|
25531
|
-
state.status = "running";
|
|
25532
|
-
delete state.blocker;
|
|
25533
|
-
await checkpoint?.(state);
|
|
25533
|
+
const terminal = await advanceGraphWorkflow(step, capability, state, result, chainData, checkpoint);
|
|
25534
|
+
if (terminal) return terminal;
|
|
25534
25535
|
}
|
|
25535
25536
|
state.status = "done";
|
|
25536
25537
|
await checkpoint?.(state);
|
|
25537
25538
|
return withWorkflowBoundaryEval(capability, { ...result, workflowState: state });
|
|
25538
25539
|
}
|
|
25540
|
+
async function advanceGraphWorkflow(step, capability, state, result, chainData, checkpoint, skipped = false) {
|
|
25541
|
+
if (!step.next || step.next.length === 0) {
|
|
25542
|
+
return completeWorkflowAtTerminal(capability, state, result, checkpoint);
|
|
25543
|
+
}
|
|
25544
|
+
const resultConditionPaths = workflowResultConditionPaths(step.next);
|
|
25545
|
+
if (!skipped && resultConditionPaths.length > 0 && !result.capabilityResults?.at(-1)) {
|
|
25546
|
+
const reason = `workflow step ${step.id} did not emit the structured result required by its conditions: ${resultConditionPaths.join(", ")}`;
|
|
25547
|
+
state.status = "blocked";
|
|
25548
|
+
state.blocker = reason;
|
|
25549
|
+
await checkpoint?.(state);
|
|
25550
|
+
return { ...result, exitCode: 64, reason, workflowState: state };
|
|
25551
|
+
}
|
|
25552
|
+
const transition = selectWorkflowTransition(step, chainData, state.transitionCounts);
|
|
25553
|
+
if (!transition) {
|
|
25554
|
+
const exhausted = exhaustedWorkflowTransitions(step, chainData, state.transitionCounts);
|
|
25555
|
+
const reason = exhausted.length > 0 ? `workflow step ${step.id} reached iteration limit: ${exhausted.join(", ")}` : `workflow step ${step.id} has no available connection`;
|
|
25556
|
+
state.status = "blocked";
|
|
25557
|
+
state.blocker = reason;
|
|
25558
|
+
await checkpoint?.(state);
|
|
25559
|
+
return { ...result, exitCode: 64, reason, workflowState: state };
|
|
25560
|
+
}
|
|
25561
|
+
if (transition.maxIterations !== void 0) {
|
|
25562
|
+
const key = `${step.id}->${transition.to}`;
|
|
25563
|
+
state.transitionCounts[key] = (state.transitionCounts[key] ?? 0) + 1;
|
|
25564
|
+
}
|
|
25565
|
+
if (transition.to === "$end") {
|
|
25566
|
+
return completeWorkflowAtTerminal(capability, state, result, checkpoint);
|
|
25567
|
+
}
|
|
25568
|
+
state.currentStepId = transition.to;
|
|
25569
|
+
state.status = "running";
|
|
25570
|
+
delete state.blocker;
|
|
25571
|
+
await checkpoint?.(state);
|
|
25572
|
+
return null;
|
|
25573
|
+
}
|
|
25539
25574
|
async function completeWorkflowAtTerminal(capability, state, output, checkpoint) {
|
|
25540
25575
|
const result = output.capabilityResults?.at(-1);
|
|
25541
25576
|
const customStatus = output.capabilityOutput && typeof output.capabilityOutput === "object" && !Array.isArray(output.capabilityOutput) && typeof output.capabilityOutput.status === "string" ? output.capabilityOutput.status : void 0;
|
|
@@ -31725,6 +31760,10 @@ function buildServer2(opts) {
|
|
|
31725
31760
|
sendJson3(res, 400, { error: parsed.error });
|
|
31726
31761
|
return;
|
|
31727
31762
|
}
|
|
31763
|
+
if (busy) {
|
|
31764
|
+
sendJson3(res, 409, { error: "runner busy" });
|
|
31765
|
+
return;
|
|
31766
|
+
}
|
|
31728
31767
|
busy = true;
|
|
31729
31768
|
sendJson3(res, 202, { ok: true, jobId: parsed.job.jobId, started: true });
|
|
31730
31769
|
void runJob2(parsed.job).catch((err) => {
|
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.658",
|
|
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
|
"repository": {
|