@kody-ade/kody-engine 0.4.151 → 0.4.153
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 +103 -57
- package/dist/executables/sync/profile.json +8 -0
- package/package.json +1 -1
package/dist/bin/kody.js
CHANGED
|
@@ -1061,7 +1061,7 @@ var init_loadPriorArt = __esm({
|
|
|
1061
1061
|
// package.json
|
|
1062
1062
|
var package_default = {
|
|
1063
1063
|
name: "@kody-ade/kody-engine",
|
|
1064
|
-
version: "0.4.
|
|
1064
|
+
version: "0.4.153",
|
|
1065
1065
|
description: "kody \u2014 autonomous development engine. Single-session Claude Code agent behind a generic executor + declarative executable profiles.",
|
|
1066
1066
|
license: "MIT",
|
|
1067
1067
|
type: "module",
|
|
@@ -1952,6 +1952,14 @@ async function runAgent(opts) {
|
|
|
1952
1952
|
`[kody agent] transient connection error (attempt ${attempt + 1}/${MAX_CONNECTION_RETRIES + 1}); retrying in ${Math.round(delayMs / 1e3)}s: ${errorMessage}
|
|
1953
1953
|
`
|
|
1954
1954
|
);
|
|
1955
|
+
if (opts.ensureBackend) {
|
|
1956
|
+
try {
|
|
1957
|
+
await opts.ensureBackend();
|
|
1958
|
+
} catch (e) {
|
|
1959
|
+
process.stderr.write(`[kody agent] backend recovery failed: ${e instanceof Error ? e.message : String(e)}
|
|
1960
|
+
`);
|
|
1961
|
+
}
|
|
1962
|
+
}
|
|
1955
1963
|
await new Promise((r) => setTimeout(r, delayMs));
|
|
1956
1964
|
}
|
|
1957
1965
|
const submittedState = getSubmitted?.();
|
|
@@ -3850,65 +3858,85 @@ function generateLitellmConfigYaml(model) {
|
|
|
3850
3858
|
""
|
|
3851
3859
|
].join("\n");
|
|
3852
3860
|
}
|
|
3853
|
-
|
|
3854
|
-
if (!needsLitellmProxy(model)) return null;
|
|
3855
|
-
if (await checkLitellmHealth(url)) {
|
|
3856
|
-
return { url, kill: () => {
|
|
3857
|
-
} };
|
|
3858
|
-
}
|
|
3859
|
-
let cmd = "litellm";
|
|
3861
|
+
function resolveLitellmCommand() {
|
|
3860
3862
|
try {
|
|
3861
3863
|
execFileSync4("which", ["litellm"], { timeout: 3e3, stdio: "pipe" });
|
|
3864
|
+
return "litellm";
|
|
3862
3865
|
} catch {
|
|
3863
3866
|
try {
|
|
3864
3867
|
execFileSync4("python3", ["-c", "import litellm"], { timeout: 1e4, stdio: "pipe" });
|
|
3865
|
-
|
|
3868
|
+
return "python3";
|
|
3866
3869
|
} catch {
|
|
3867
3870
|
throw new Error("litellm not installed \u2014 run: pip install 'litellm[proxy]'");
|
|
3868
3871
|
}
|
|
3869
3872
|
}
|
|
3870
|
-
|
|
3871
|
-
|
|
3873
|
+
}
|
|
3874
|
+
async function startLitellmIfNeeded(model, projectDir, url = LITELLM_DEFAULT_URL) {
|
|
3875
|
+
if (!needsLitellmProxy(model)) return null;
|
|
3876
|
+
const cmd = resolveLitellmCommand();
|
|
3872
3877
|
const portMatch = url.match(/:(\d+)/);
|
|
3873
3878
|
const port = portMatch ? portMatch[1] : "4000";
|
|
3874
|
-
const
|
|
3875
|
-
|
|
3876
|
-
|
|
3877
|
-
const
|
|
3878
|
-
|
|
3879
|
-
|
|
3880
|
-
|
|
3881
|
-
|
|
3882
|
-
|
|
3883
|
-
|
|
3884
|
-
|
|
3885
|
-
|
|
3886
|
-
|
|
3887
|
-
|
|
3888
|
-
|
|
3889
|
-
|
|
3890
|
-
|
|
3891
|
-
|
|
3892
|
-
try {
|
|
3893
|
-
child.kill();
|
|
3894
|
-
} catch {
|
|
3895
|
-
}
|
|
3896
|
-
}
|
|
3897
|
-
};
|
|
3879
|
+
const childEnv = stripBlockingEnv({ ...process.env, ...readDotenvApiKeys(projectDir) });
|
|
3880
|
+
let child;
|
|
3881
|
+
let logPath;
|
|
3882
|
+
const spawnProxy = () => {
|
|
3883
|
+
const configPath = path13.join(os2.tmpdir(), `kody-litellm-${Date.now()}.yaml`);
|
|
3884
|
+
fs14.writeFileSync(configPath, generateLitellmConfigYaml(model));
|
|
3885
|
+
const args = cmd === "litellm" ? ["--config", configPath, "--port", port] : ["-m", "litellm", "--config", configPath, "--port", port];
|
|
3886
|
+
const nextLogPath = path13.join(os2.tmpdir(), `kody-litellm-${Date.now()}.log`);
|
|
3887
|
+
const outFd = fs14.openSync(nextLogPath, "w");
|
|
3888
|
+
child = spawn3(cmd, args, { stdio: ["ignore", outFd, outFd], detached: true, env: childEnv });
|
|
3889
|
+
fs14.closeSync(outFd);
|
|
3890
|
+
logPath = nextLogPath;
|
|
3891
|
+
};
|
|
3892
|
+
const waitForHealth = async () => {
|
|
3893
|
+
const deadline = Date.now() + resolveLitellmTimeoutMs();
|
|
3894
|
+
while (Date.now() < deadline) {
|
|
3895
|
+
await new Promise((r) => setTimeout(r, LITELLM_HEALTH_POLL_INTERVAL_MS));
|
|
3896
|
+
if (await checkLitellmHealth(url)) return true;
|
|
3898
3897
|
}
|
|
3898
|
+
return false;
|
|
3899
|
+
};
|
|
3900
|
+
const readLogTail = () => {
|
|
3901
|
+
if (!logPath) return "";
|
|
3902
|
+
try {
|
|
3903
|
+
return fs14.readFileSync(logPath, "utf-8").slice(-2e3);
|
|
3904
|
+
} catch {
|
|
3905
|
+
return "";
|
|
3906
|
+
}
|
|
3907
|
+
};
|
|
3908
|
+
const killChild = () => {
|
|
3909
|
+
try {
|
|
3910
|
+
child?.kill();
|
|
3911
|
+
} catch {
|
|
3912
|
+
}
|
|
3913
|
+
};
|
|
3914
|
+
const ensureHealthy = async () => {
|
|
3915
|
+
if (await checkLitellmHealth(url)) return true;
|
|
3916
|
+
const tail = readLogTail();
|
|
3917
|
+
process.stderr.write(
|
|
3918
|
+
`[kody litellm] proxy unreachable mid-run; restarting.${tail ? ` Last log:
|
|
3919
|
+
${tail}
|
|
3920
|
+
` : "\n"}`
|
|
3921
|
+
);
|
|
3922
|
+
killChild();
|
|
3923
|
+
spawnProxy();
|
|
3924
|
+
return waitForHealth();
|
|
3925
|
+
};
|
|
3926
|
+
if (await checkLitellmHealth(url)) {
|
|
3927
|
+
return { url, kill: killChild, ensureHealthy };
|
|
3899
3928
|
}
|
|
3900
|
-
|
|
3901
|
-
|
|
3902
|
-
|
|
3903
|
-
|
|
3904
|
-
|
|
3905
|
-
|
|
3906
|
-
|
|
3907
|
-
|
|
3929
|
+
spawnProxy();
|
|
3930
|
+
if (!await waitForHealth()) {
|
|
3931
|
+
const tail = readLogTail();
|
|
3932
|
+
killChild();
|
|
3933
|
+
const seconds = Math.round(resolveLitellmTimeoutMs() / 1e3);
|
|
3934
|
+
throw new Error(
|
|
3935
|
+
`LiteLLM proxy failed to start within ${seconds}s (KODY_LITELLM_TIMEOUT_SEC overrides). Log tail:
|
|
3936
|
+
${tail}`
|
|
3937
|
+
);
|
|
3908
3938
|
}
|
|
3909
|
-
|
|
3910
|
-
throw new Error(`LiteLLM proxy failed to start within ${seconds}s (KODY_LITELLM_TIMEOUT_SEC overrides). Log tail:
|
|
3911
|
-
${logTail}`);
|
|
3939
|
+
return { url, kill: killChild, ensureHealthy };
|
|
3912
3940
|
}
|
|
3913
3941
|
function readDotenvApiKeys(projectDir) {
|
|
3914
3942
|
const dotenvPath = path13.join(projectDir, ".env");
|
|
@@ -12097,6 +12125,11 @@ function postKodyComment(target, issueNumber, state, next, cwd) {
|
|
|
12097
12125
|
// src/scripts/syncFlow.ts
|
|
12098
12126
|
import { execFileSync as execFileSync25 } from "child_process";
|
|
12099
12127
|
init_issue();
|
|
12128
|
+
var DONE2 = {
|
|
12129
|
+
label: "kody:done",
|
|
12130
|
+
color: "0e8a16",
|
|
12131
|
+
description: "kody: PR ready for human review/merge"
|
|
12132
|
+
};
|
|
12100
12133
|
var syncFlow = async (ctx, _profile, args) => {
|
|
12101
12134
|
const announceOnSuccess = Boolean(args?.announceOnSuccess);
|
|
12102
12135
|
const prNumber = ctx.args.pr;
|
|
@@ -12134,6 +12167,7 @@ var syncFlow = async (ctx, _profile, args) => {
|
|
|
12134
12167
|
if (announceOnSuccess) {
|
|
12135
12168
|
ctx.output.exitCode = 0;
|
|
12136
12169
|
ctx.output.reason = `already up to date with origin/${baseBranch}`;
|
|
12170
|
+
restoreDone(prNumber, ctx.cwd);
|
|
12137
12171
|
}
|
|
12138
12172
|
return;
|
|
12139
12173
|
}
|
|
@@ -12148,8 +12182,15 @@ var syncFlow = async (ctx, _profile, args) => {
|
|
|
12148
12182
|
if (announceOnSuccess) {
|
|
12149
12183
|
ctx.output.exitCode = 0;
|
|
12150
12184
|
ctx.output.reason = `merged origin/${baseBranch} into ${ctx.data.branch}`;
|
|
12185
|
+
restoreDone(prNumber, ctx.cwd);
|
|
12151
12186
|
}
|
|
12152
12187
|
};
|
|
12188
|
+
function restoreDone(prNumber, cwd) {
|
|
12189
|
+
try {
|
|
12190
|
+
setKodyLabel(prNumber, DONE2, cwd);
|
|
12191
|
+
} catch {
|
|
12192
|
+
}
|
|
12193
|
+
}
|
|
12153
12194
|
function bail2(ctx, prNumber, reason) {
|
|
12154
12195
|
ctx.skipAgent = true;
|
|
12155
12196
|
ctx.output.exitCode = 1;
|
|
@@ -13093,7 +13134,10 @@ async function runExecutable(profileName, input) {
|
|
|
13093
13134
|
try {
|
|
13094
13135
|
model = parseProviderModel(modelSpec);
|
|
13095
13136
|
} catch (err) {
|
|
13096
|
-
return finishAndEnd({
|
|
13137
|
+
return finishAndEnd({
|
|
13138
|
+
exitCode: 99,
|
|
13139
|
+
reason: `agent.model invalid: ${err instanceof Error ? err.message : String(err)}`
|
|
13140
|
+
});
|
|
13097
13141
|
}
|
|
13098
13142
|
let litellm = null;
|
|
13099
13143
|
try {
|
|
@@ -13137,11 +13181,15 @@ async function runExecutable(profileName, input) {
|
|
|
13137
13181
|
const syntheticPath = ctx.data.syntheticPluginPath;
|
|
13138
13182
|
const pluginPaths = [...externalPlugins, ...syntheticPath ? [syntheticPath] : []];
|
|
13139
13183
|
const agents = loadSubagents(profile);
|
|
13184
|
+
const lm = litellm;
|
|
13140
13185
|
return runAgent({
|
|
13141
13186
|
prompt,
|
|
13142
13187
|
model,
|
|
13143
13188
|
cwd: input.cwd,
|
|
13144
|
-
litellmUrl:
|
|
13189
|
+
litellmUrl: lm?.url ?? null,
|
|
13190
|
+
// On a connection drop mid-run, restart the (possibly crashed) proxy
|
|
13191
|
+
// before the agent retries. No-op for direct-Anthropic runs (lm null).
|
|
13192
|
+
ensureBackend: lm ? () => lm.ensureHealthy().then(() => void 0) : void 0,
|
|
13145
13193
|
verbose: input.verbose,
|
|
13146
13194
|
quiet: input.quiet,
|
|
13147
13195
|
ndjsonDir,
|
|
@@ -13211,7 +13259,10 @@ async function runExecutable(profileName, input) {
|
|
|
13211
13259
|
} else if (!ctx.skipAgent) {
|
|
13212
13260
|
const prompt = ctx.data.prompt;
|
|
13213
13261
|
if (!prompt) {
|
|
13214
|
-
return finishAndEnd({
|
|
13262
|
+
return finishAndEnd({
|
|
13263
|
+
exitCode: 99,
|
|
13264
|
+
reason: "composePrompt did not produce a prompt (ctx.data.prompt missing)"
|
|
13265
|
+
});
|
|
13215
13266
|
}
|
|
13216
13267
|
emitEvent(input.cwd, { executable: profileName, kind: "agent_start" });
|
|
13217
13268
|
agentResult = await invokeAgent(prompt);
|
|
@@ -13272,10 +13323,7 @@ async function runExecutable(profileName, input) {
|
|
|
13272
13323
|
const runId = resolveRunId2();
|
|
13273
13324
|
const dir = pathMod.join(input.cwd, ".kody", "runs", runId, "crashes");
|
|
13274
13325
|
fsMod.mkdirSync(dir, { recursive: true });
|
|
13275
|
-
const file = pathMod.join(
|
|
13276
|
-
dir,
|
|
13277
|
-
`${label.replace(/[^a-zA-Z0-9_-]/g, "_")}-${Date.now()}.json`
|
|
13278
|
-
);
|
|
13326
|
+
const file = pathMod.join(dir, `${label.replace(/[^a-zA-Z0-9_-]/g, "_")}-${Date.now()}.json`);
|
|
13279
13327
|
fsMod.writeFileSync(
|
|
13280
13328
|
file,
|
|
13281
13329
|
JSON.stringify(
|
|
@@ -13315,10 +13363,8 @@ async function runExecutable(profileName, input) {
|
|
|
13315
13363
|
try {
|
|
13316
13364
|
const missing2 = verifyTaskArtifacts(taskArtifacts.absDir);
|
|
13317
13365
|
if (missing2.length > 0) {
|
|
13318
|
-
process.stderr.write(
|
|
13319
|
-
|
|
13320
|
-
`
|
|
13321
|
-
);
|
|
13366
|
+
process.stderr.write(`[task-artifacts] task ${taskArtifacts.taskId} missing: ${missing2.join(", ")}
|
|
13367
|
+
`);
|
|
13322
13368
|
}
|
|
13323
13369
|
} catch {
|
|
13324
13370
|
}
|
|
@@ -27,6 +27,14 @@
|
|
|
27
27
|
"cliTools": [],
|
|
28
28
|
"scripts": {
|
|
29
29
|
"preflight": [
|
|
30
|
+
{
|
|
31
|
+
"script": "setLifecycleLabel",
|
|
32
|
+
"with": {
|
|
33
|
+
"label": "kody:syncing",
|
|
34
|
+
"color": "c5def5",
|
|
35
|
+
"description": "kody: syncing PR with base"
|
|
36
|
+
}
|
|
37
|
+
},
|
|
30
38
|
{
|
|
31
39
|
"script": "syncFlow",
|
|
32
40
|
"with": { "announceOnSuccess": true }
|
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.153",
|
|
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",
|