@kody-ade/kody-engine 0.4.152 → 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 +90 -57
- 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");
|
|
@@ -13106,7 +13134,10 @@ async function runExecutable(profileName, input) {
|
|
|
13106
13134
|
try {
|
|
13107
13135
|
model = parseProviderModel(modelSpec);
|
|
13108
13136
|
} catch (err) {
|
|
13109
|
-
return finishAndEnd({
|
|
13137
|
+
return finishAndEnd({
|
|
13138
|
+
exitCode: 99,
|
|
13139
|
+
reason: `agent.model invalid: ${err instanceof Error ? err.message : String(err)}`
|
|
13140
|
+
});
|
|
13110
13141
|
}
|
|
13111
13142
|
let litellm = null;
|
|
13112
13143
|
try {
|
|
@@ -13150,11 +13181,15 @@ async function runExecutable(profileName, input) {
|
|
|
13150
13181
|
const syntheticPath = ctx.data.syntheticPluginPath;
|
|
13151
13182
|
const pluginPaths = [...externalPlugins, ...syntheticPath ? [syntheticPath] : []];
|
|
13152
13183
|
const agents = loadSubagents(profile);
|
|
13184
|
+
const lm = litellm;
|
|
13153
13185
|
return runAgent({
|
|
13154
13186
|
prompt,
|
|
13155
13187
|
model,
|
|
13156
13188
|
cwd: input.cwd,
|
|
13157
|
-
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,
|
|
13158
13193
|
verbose: input.verbose,
|
|
13159
13194
|
quiet: input.quiet,
|
|
13160
13195
|
ndjsonDir,
|
|
@@ -13224,7 +13259,10 @@ async function runExecutable(profileName, input) {
|
|
|
13224
13259
|
} else if (!ctx.skipAgent) {
|
|
13225
13260
|
const prompt = ctx.data.prompt;
|
|
13226
13261
|
if (!prompt) {
|
|
13227
|
-
return finishAndEnd({
|
|
13262
|
+
return finishAndEnd({
|
|
13263
|
+
exitCode: 99,
|
|
13264
|
+
reason: "composePrompt did not produce a prompt (ctx.data.prompt missing)"
|
|
13265
|
+
});
|
|
13228
13266
|
}
|
|
13229
13267
|
emitEvent(input.cwd, { executable: profileName, kind: "agent_start" });
|
|
13230
13268
|
agentResult = await invokeAgent(prompt);
|
|
@@ -13285,10 +13323,7 @@ async function runExecutable(profileName, input) {
|
|
|
13285
13323
|
const runId = resolveRunId2();
|
|
13286
13324
|
const dir = pathMod.join(input.cwd, ".kody", "runs", runId, "crashes");
|
|
13287
13325
|
fsMod.mkdirSync(dir, { recursive: true });
|
|
13288
|
-
const file = pathMod.join(
|
|
13289
|
-
dir,
|
|
13290
|
-
`${label.replace(/[^a-zA-Z0-9_-]/g, "_")}-${Date.now()}.json`
|
|
13291
|
-
);
|
|
13326
|
+
const file = pathMod.join(dir, `${label.replace(/[^a-zA-Z0-9_-]/g, "_")}-${Date.now()}.json`);
|
|
13292
13327
|
fsMod.writeFileSync(
|
|
13293
13328
|
file,
|
|
13294
13329
|
JSON.stringify(
|
|
@@ -13328,10 +13363,8 @@ async function runExecutable(profileName, input) {
|
|
|
13328
13363
|
try {
|
|
13329
13364
|
const missing2 = verifyTaskArtifacts(taskArtifacts.absDir);
|
|
13330
13365
|
if (missing2.length > 0) {
|
|
13331
|
-
process.stderr.write(
|
|
13332
|
-
|
|
13333
|
-
`
|
|
13334
|
-
);
|
|
13366
|
+
process.stderr.write(`[task-artifacts] task ${taskArtifacts.taskId} missing: ${missing2.join(", ")}
|
|
13367
|
+
`);
|
|
13335
13368
|
}
|
|
13336
13369
|
} catch {
|
|
13337
13370
|
}
|
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",
|