@kody-ade/kody-engine 0.4.509 → 0.4.511
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 +96 -24
- 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.511",
|
|
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",
|
|
@@ -809,6 +809,16 @@ async function enforceSubagentModelInheritance(input) {
|
|
|
809
809
|
}
|
|
810
810
|
};
|
|
811
811
|
}
|
|
812
|
+
function createSubagentInvocationHook(invokedSubagents) {
|
|
813
|
+
return async (input) => {
|
|
814
|
+
const toolInput = input.tool_input;
|
|
815
|
+
if (toolInput && typeof toolInput === "object" && !Array.isArray(toolInput)) {
|
|
816
|
+
const subagentType = toolInput.subagent_type;
|
|
817
|
+
if (typeof subagentType === "string" && subagentType.length > 0) invokedSubagents.add(subagentType);
|
|
818
|
+
}
|
|
819
|
+
return {};
|
|
820
|
+
};
|
|
821
|
+
}
|
|
812
822
|
function splitFrontmatter(raw) {
|
|
813
823
|
const match = /^---\n([\s\S]*?)\n---\n?([\s\S]*)$/.exec(raw);
|
|
814
824
|
if (!match) return { fm: {}, body: raw.trim() };
|
|
@@ -1959,8 +1969,15 @@ function parseCapabilityContract(raw) {
|
|
|
1959
1969
|
if (timeoutMs !== void 0 && parsed.execution !== "script") {
|
|
1960
1970
|
throw new Error('contract.json timeoutMs is supported only when execution is "script"');
|
|
1961
1971
|
}
|
|
1972
|
+
const requiredSubagents = parsed.requiredSubagents === void 0 ? void 0 : Array.isArray(parsed.requiredSubagents) && parsed.requiredSubagents.length > 0 && parsed.requiredSubagents.every((name) => typeof name === "string" && /^[a-z][a-z0-9-]{0,63}$/.test(name)) ? [...new Set(parsed.requiredSubagents)] : null;
|
|
1973
|
+
if (requiredSubagents === null) {
|
|
1974
|
+
throw new Error("contract.json requiredSubagents must contain valid specialist names");
|
|
1975
|
+
}
|
|
1976
|
+
if (requiredSubagents && parsed.execution !== "agent") {
|
|
1977
|
+
throw new Error('contract.json requiredSubagents are supported only when execution is "agent"');
|
|
1978
|
+
}
|
|
1962
1979
|
const unsupported = Object.keys(parsed).filter(
|
|
1963
|
-
(key) => key !== "execution" && key !== "secrets" && key !== "timeoutMs" && key !== "input" && key !== "output"
|
|
1980
|
+
(key) => key !== "execution" && key !== "secrets" && key !== "timeoutMs" && key !== "requiredSubagents" && key !== "input" && key !== "output"
|
|
1964
1981
|
);
|
|
1965
1982
|
if (unsupported.length > 0) {
|
|
1966
1983
|
throw new Error(`contract.json contains unsupported fields: ${unsupported.join(", ")}`);
|
|
@@ -1969,6 +1986,7 @@ function parseCapabilityContract(raw) {
|
|
|
1969
1986
|
...parsed.execution ? { execution: parsed.execution } : {},
|
|
1970
1987
|
...secrets ? { secrets } : {},
|
|
1971
1988
|
...timeoutMs !== void 0 ? { timeoutMs } : {},
|
|
1989
|
+
...requiredSubagents ? { requiredSubagents } : {},
|
|
1972
1990
|
input: parsed.input,
|
|
1973
1991
|
output: parsed.output
|
|
1974
1992
|
};
|
|
@@ -3717,6 +3735,8 @@ async function runAgent(opts) {
|
|
|
3717
3735
|
let messageCount = 0;
|
|
3718
3736
|
let finalText = "";
|
|
3719
3737
|
let getSubmitted;
|
|
3738
|
+
const invokedSubagents = /* @__PURE__ */ new Set();
|
|
3739
|
+
const subagentInvocationHook = createSubagentInvocationHook(invokedSubagents);
|
|
3720
3740
|
for (let attempt = 0; ; attempt++) {
|
|
3721
3741
|
let ndjsonWriteFailed = false;
|
|
3722
3742
|
let ndjsonWriteError;
|
|
@@ -3751,6 +3771,12 @@ async function runAgent(opts) {
|
|
|
3751
3771
|
matcher: "Agent",
|
|
3752
3772
|
hooks: [enforceSubagentModelInheritance]
|
|
3753
3773
|
}
|
|
3774
|
+
],
|
|
3775
|
+
PostToolUse: [
|
|
3776
|
+
{
|
|
3777
|
+
matcher: "Agent",
|
|
3778
|
+
hooks: [subagentInvocationHook]
|
|
3779
|
+
}
|
|
3754
3780
|
]
|
|
3755
3781
|
}
|
|
3756
3782
|
};
|
|
@@ -4072,7 +4098,8 @@ async function runAgent(opts) {
|
|
|
4072
4098
|
durationMs: Date.now() - startedAt,
|
|
4073
4099
|
tokens,
|
|
4074
4100
|
costUsd,
|
|
4075
|
-
messageCount
|
|
4101
|
+
messageCount,
|
|
4102
|
+
invokedSubagents: [...invokedSubagents]
|
|
4076
4103
|
};
|
|
4077
4104
|
}
|
|
4078
4105
|
var DEFAULT_ALLOWED_TOOLS, DEFAULT_TURN_TIMEOUT_MS, MAX_CONNECTION_RETRIES, CONNECTION_RETRY_BASE_MS, MUTATING_FILE_TOOLS, BASH_WRITE_VERB, AGENT_KEEP_SECRETS;
|
|
@@ -4884,9 +4911,7 @@ var init_capability_contract_validation = __esm({
|
|
|
4884
4911
|
const property = error.keyword === "additionalProperties" && typeof error.params.additionalProperty === "string" ? ` (${error.params.additionalProperty})` : "";
|
|
4885
4912
|
return `${location}: ${error.message ?? error.keyword}${property}`;
|
|
4886
4913
|
}).join("; ");
|
|
4887
|
-
super(
|
|
4888
|
-
`Capability ${boundary} does not match its declared contract: ${details}`
|
|
4889
|
-
);
|
|
4914
|
+
super(`Capability ${boundary} does not match its declared contract: ${details}`);
|
|
4890
4915
|
this.boundary = boundary;
|
|
4891
4916
|
this.errors = errors;
|
|
4892
4917
|
this.name = "CapabilityContractValidationError";
|
|
@@ -7921,10 +7946,7 @@ function capabilityInputEnvironment(input) {
|
|
|
7921
7946
|
function capabilityConfigEnvironment(config) {
|
|
7922
7947
|
if (!config || typeof config !== "object" || Array.isArray(config)) return {};
|
|
7923
7948
|
return Object.fromEntries(
|
|
7924
|
-
flattenConfig(config).map(([key, value]) => [
|
|
7925
|
-
`KODY_CFG_${key}`,
|
|
7926
|
-
value
|
|
7927
|
-
])
|
|
7949
|
+
flattenConfig(config).map(([key, value]) => [`KODY_CFG_${key}`, value])
|
|
7928
7950
|
);
|
|
7929
7951
|
}
|
|
7930
7952
|
function flattenConfig(config, prefix = "") {
|
|
@@ -7937,9 +7959,7 @@ function flattenConfig(config, prefix = "") {
|
|
|
7937
7959
|
} else if (Array.isArray(value)) {
|
|
7938
7960
|
entries.push([key, JSON.stringify(value)]);
|
|
7939
7961
|
} else if (typeof value === "object") {
|
|
7940
|
-
entries.push(
|
|
7941
|
-
...flattenConfig(value, key)
|
|
7942
|
-
);
|
|
7962
|
+
entries.push(...flattenConfig(value, key));
|
|
7943
7963
|
}
|
|
7944
7964
|
}
|
|
7945
7965
|
return entries;
|
|
@@ -13619,9 +13639,7 @@ import { randomUUID } from "crypto";
|
|
|
13619
13639
|
function assertLoopDispatchesSucceeded(results) {
|
|
13620
13640
|
const failed = results.filter((result) => result.status === "failed");
|
|
13621
13641
|
if (failed.length === 0) return;
|
|
13622
|
-
throw new Error(
|
|
13623
|
-
`Loop dispatch failed: ${failed.map((result) => `${result.loopId}: ${result.reason}`).join("; ")}`
|
|
13624
|
-
);
|
|
13642
|
+
throw new Error(`Loop dispatch failed: ${failed.map((result) => `${result.loopId}: ${result.reason}`).join("; ")}`);
|
|
13625
13643
|
}
|
|
13626
13644
|
async function dispatchLoopsWith(input) {
|
|
13627
13645
|
const results = [];
|
|
@@ -15668,10 +15686,10 @@ var init_loadQaContext = __esm({
|
|
|
15668
15686
|
});
|
|
15669
15687
|
|
|
15670
15688
|
// src/scripts/loadSimpleCapability.ts
|
|
15689
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
15671
15690
|
import * as fs43 from "fs";
|
|
15672
|
-
import * as path40 from "path";
|
|
15673
15691
|
import * as os6 from "os";
|
|
15674
|
-
import
|
|
15692
|
+
import * as path40 from "path";
|
|
15675
15693
|
function registerCapabilitySubagents(profile, toolRoot, toolFiles) {
|
|
15676
15694
|
const subagentFiles = toolFiles.flatMap((file) => {
|
|
15677
15695
|
const match = /^agents\/([a-z][a-z0-9-]{0,63})\.md$/.exec(file);
|
|
@@ -15770,6 +15788,13 @@ var init_loadSimpleCapability = __esm({
|
|
|
15770
15788
|
if (ctx.data.capabilityExecution === "agent") {
|
|
15771
15789
|
registerCapabilitySubagents(profile, toolRoot, toolFiles);
|
|
15772
15790
|
}
|
|
15791
|
+
const requiredSubagents = capability.contract?.requiredSubagents ?? [];
|
|
15792
|
+
for (const required2 of requiredSubagents) {
|
|
15793
|
+
if (!profile.claudeCode.subagents.includes(required2)) {
|
|
15794
|
+
throw new Error(`Capability "${slug}" required specialist ${required2} is not available`);
|
|
15795
|
+
}
|
|
15796
|
+
}
|
|
15797
|
+
if (requiredSubagents.length > 0) ctx.data.requiredSubagents = requiredSubagents;
|
|
15773
15798
|
if (capability.contract?.execution === "script") {
|
|
15774
15799
|
ctx.data.capabilityScriptPath = path40.join(capability.dir, "tools", "run.sh");
|
|
15775
15800
|
ctx.data.capabilitySecretNames = capability.contract.secrets ?? [];
|
|
@@ -15827,7 +15852,14 @@ var init_loadSimpleCapability = __esm({
|
|
|
15827
15852
|
outputPath,
|
|
15828
15853
|
"The Engine reads this file as the authoritative result. Do not write markdown to it."
|
|
15829
15854
|
] : []
|
|
15830
|
-
] : ["Return one JSON value."]
|
|
15855
|
+
] : ["Return one JSON value."],
|
|
15856
|
+
...requiredSubagents.length > 0 ? [
|
|
15857
|
+
"",
|
|
15858
|
+
"## Required specialists",
|
|
15859
|
+
"",
|
|
15860
|
+
`You must invoke each of these private specialists before returning: ${requiredSubagents.join(", ")}.`,
|
|
15861
|
+
"The Engine verifies the actual Agent calls and blocks the capability if any are skipped."
|
|
15862
|
+
] : []
|
|
15831
15863
|
].join("\n");
|
|
15832
15864
|
};
|
|
15833
15865
|
}
|
|
@@ -16775,6 +16807,9 @@ var init_parseReproOutput = __esm({
|
|
|
16775
16807
|
|
|
16776
16808
|
// src/scripts/parseSimpleCapabilityOutput.ts
|
|
16777
16809
|
import * as fs45 from "fs";
|
|
16810
|
+
function stringList2(value) {
|
|
16811
|
+
return Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
|
|
16812
|
+
}
|
|
16778
16813
|
function readOutputFile(outputPath) {
|
|
16779
16814
|
if (!outputPath || !fs45.existsSync(outputPath)) return { found: false };
|
|
16780
16815
|
try {
|
|
@@ -16822,6 +16857,28 @@ var init_parseSimpleCapabilityOutput = __esm({
|
|
|
16822
16857
|
init_capability_contract_validation();
|
|
16823
16858
|
init_capabilityResult();
|
|
16824
16859
|
parseSimpleCapabilityOutput = async (ctx, _profile, agentResult) => {
|
|
16860
|
+
const requiredSubagents = stringList2(ctx.data.requiredSubagents);
|
|
16861
|
+
const invokedSubagents = new Set(agentResult?.invokedSubagents ?? []);
|
|
16862
|
+
const missingSubagents = requiredSubagents.filter((name) => !invokedSubagents.has(name));
|
|
16863
|
+
if (missingSubagents.length > 0) {
|
|
16864
|
+
const label = missingSubagents.length === 1 ? "specialist was" : "specialists were";
|
|
16865
|
+
const reason2 = `Required ${label} not invoked: ${missingSubagents.join(", ")}`;
|
|
16866
|
+
ctx.output.exitCode = 64;
|
|
16867
|
+
ctx.output.reason = reason2;
|
|
16868
|
+
ctx.data.capabilityOutput = { status: "blocked", reason: reason2, summary: reason2 };
|
|
16869
|
+
ctx.data.capabilityResults = [
|
|
16870
|
+
{
|
|
16871
|
+
version: 1,
|
|
16872
|
+
status: "blocked",
|
|
16873
|
+
summary: reason2,
|
|
16874
|
+
facts: {},
|
|
16875
|
+
artifacts: [],
|
|
16876
|
+
missingEvidence: [],
|
|
16877
|
+
blockers: [reason2]
|
|
16878
|
+
}
|
|
16879
|
+
];
|
|
16880
|
+
return;
|
|
16881
|
+
}
|
|
16825
16882
|
const outputPath = typeof ctx.data.capabilityOutputPath === "string" ? ctx.data.capabilityOutputPath : void 0;
|
|
16826
16883
|
const fileOutput = readOutputFile(outputPath);
|
|
16827
16884
|
const output = fileOutput.found ? fileOutput.value : Object.hasOwn(ctx.data, "capabilityScriptOutput") ? ctx.data.capabilityScriptOutput : parseOutput(agentResult?.finalText);
|
|
@@ -21076,9 +21133,7 @@ async function runImplementation(profileName, input) {
|
|
|
21076
21133
|
profile.inputs,
|
|
21077
21134
|
args,
|
|
21078
21135
|
profile.canonicalContract.capabilityId,
|
|
21079
|
-
Object.keys(
|
|
21080
|
-
profile.canonicalContract.inputSchema.properties ?? {}
|
|
21081
|
-
)
|
|
21136
|
+
Object.keys(profile.canonicalContract.inputSchema.properties ?? {})
|
|
21082
21137
|
)
|
|
21083
21138
|
);
|
|
21084
21139
|
}
|
|
@@ -22560,7 +22615,8 @@ async function runGraphCapabilityWorkflow(parent, workflow, capability, base, ch
|
|
|
22560
22615
|
}
|
|
22561
22616
|
const transition = selectWorkflowTransition(step, chainData, state.transitionCounts);
|
|
22562
22617
|
if (!transition) {
|
|
22563
|
-
const
|
|
22618
|
+
const exhausted = exhaustedWorkflowTransitions(step, chainData, state.transitionCounts);
|
|
22619
|
+
const reason = exhausted.length > 0 ? `workflow step ${step.id} reached iteration limit: ${exhausted.join(", ")}` : `workflow step ${step.id} has no available connection`;
|
|
22564
22620
|
state.status = "blocked";
|
|
22565
22621
|
state.blocker = reason;
|
|
22566
22622
|
await checkpoint?.(state);
|
|
@@ -22636,6 +22692,15 @@ function selectWorkflowTransition(step, data, counts) {
|
|
|
22636
22692
|
}
|
|
22637
22693
|
return fallback;
|
|
22638
22694
|
}
|
|
22695
|
+
function exhaustedWorkflowTransitions(step, data, counts) {
|
|
22696
|
+
return (step.next ?? []).flatMap((transition) => {
|
|
22697
|
+
if (transition.maxIterations === void 0) return [];
|
|
22698
|
+
const key = `${step.id}->${transition.to}`;
|
|
22699
|
+
if ((counts[key] ?? 0) < transition.maxIterations) return [];
|
|
22700
|
+
const matches = transition.default === true || !transition.when || conditionMatches(transition.when, workflowConditionContext(data));
|
|
22701
|
+
return matches ? [`${key} (${transition.maxIterations})`] : [];
|
|
22702
|
+
});
|
|
22703
|
+
}
|
|
22639
22704
|
function workflowResultConditionPaths(transitions) {
|
|
22640
22705
|
return transitions.flatMap(
|
|
22641
22706
|
(transition) => Object.keys(transition.when ?? {}).filter((path53) => path53.startsWith("result."))
|
|
@@ -24840,8 +24905,13 @@ Exit codes (inherited from kody run):
|
|
|
24840
24905
|
2 verify failed (no PR opened \u2014 branch pushed for inspection)
|
|
24841
24906
|
3 no commits to ship
|
|
24842
24907
|
4 PR creation failed
|
|
24908
|
+
64 capability or workflow blocked by an invalid or exhausted route
|
|
24909
|
+
124 execution timed out
|
|
24843
24910
|
99 wrapper crashed
|
|
24844
24911
|
`;
|
|
24912
|
+
function normalizeJobExitCode(exitCode) {
|
|
24913
|
+
return Number.isInteger(exitCode) && exitCode >= 0 && exitCode <= 255 ? exitCode : 99;
|
|
24914
|
+
}
|
|
24845
24915
|
function parseCiArgs(argv) {
|
|
24846
24916
|
const result = { errors: [] };
|
|
24847
24917
|
for (let i = 0; i < argv.length; i++) {
|
|
@@ -25332,7 +25402,9 @@ async function runCi(argv) {
|
|
|
25332
25402
|
}
|
|
25333
25403
|
);
|
|
25334
25404
|
const ec = result.exitCode;
|
|
25335
|
-
|
|
25405
|
+
if (ec !== 0 && result.reason) process.stderr.write(`[kody] ${result.reason}
|
|
25406
|
+
`);
|
|
25407
|
+
return normalizeJobExitCode(ec);
|
|
25336
25408
|
}
|
|
25337
25409
|
if (!args.issueNumber && !autoFallback && runRequestFanOut) {
|
|
25338
25410
|
return runScheduledFanOut(cwd, args, { force: runRequestFanOutForce });
|
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.511",
|
|
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",
|