@kody-ade/kody-engine 0.4.596 → 0.4.598
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 +127 -46
- 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.598",
|
|
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",
|
|
@@ -4228,7 +4228,7 @@ async function runAgent(opts) {
|
|
|
4228
4228
|
}
|
|
4229
4229
|
try {
|
|
4230
4230
|
const queryOptions = {
|
|
4231
|
-
model: opts.litellmUrl ? litellmModelGroup(opts.model) : opts.model.model,
|
|
4231
|
+
model: opts.litellmUrl ? opts.litellmModelGroupOverride ?? litellmModelGroup(opts.model) : opts.model.model,
|
|
4232
4232
|
cwd: opts.cwd,
|
|
4233
4233
|
// Fresh array (never mutate the shared DEFAULT_ALLOWED_TOOLS const) so
|
|
4234
4234
|
// opt-in tools like fetch_repo can be appended below.
|
|
@@ -7850,6 +7850,43 @@ function generateLitellmConfigYaml(model) {
|
|
|
7850
7850
|
]);
|
|
7851
7851
|
return ["model_list:", ...modelEntries, "", "litellm_settings:", " drop_params: true", ""].join("\n");
|
|
7852
7852
|
}
|
|
7853
|
+
function modelEntry(modelName, model) {
|
|
7854
|
+
const apiKeyVar = model.apiKeyEnvVar ?? providerApiKeyEnvVar(model.provider);
|
|
7855
|
+
const litellmProvider = model.litellmProvider ?? model.provider;
|
|
7856
|
+
return [
|
|
7857
|
+
` - model_name: ${modelName}`,
|
|
7858
|
+
` litellm_params:`,
|
|
7859
|
+
` model: ${litellmProvider}/${model.model}`,
|
|
7860
|
+
` api_key: os.environ/${apiKeyVar}`,
|
|
7861
|
+
...model.baseURL ? [` api_base: ${model.baseURL}`] : []
|
|
7862
|
+
];
|
|
7863
|
+
}
|
|
7864
|
+
function generateAutomaticLitellmConfigYaml(models) {
|
|
7865
|
+
if (models.length < 2) throw new Error("Automatic LiteLLM requires at least two models");
|
|
7866
|
+
const groups = models.map((_, index) => `kody-automatic-${index}`);
|
|
7867
|
+
const entries = models.flatMap((model, index) => modelEntry(groups[index], model));
|
|
7868
|
+
for (const alias of CLAUDE_CODE_PROXY_MODEL_ALIASES) entries.push(...modelEntry(alias, models[0]));
|
|
7869
|
+
const fallbackSources = [groups[0], ...CLAUDE_CODE_PROXY_MODEL_ALIASES, ...groups.slice(1, -1)];
|
|
7870
|
+
const fallbacks = fallbackSources.map((source) => {
|
|
7871
|
+
const sourceIndex = source.startsWith("kody-automatic-") ? Number(source.slice("kody-automatic-".length)) : 0;
|
|
7872
|
+
return ` - ${source}: [${groups.slice(sourceIndex + 1).join(", ")}]`;
|
|
7873
|
+
});
|
|
7874
|
+
return [
|
|
7875
|
+
"model_list:",
|
|
7876
|
+
...entries,
|
|
7877
|
+
"",
|
|
7878
|
+
"litellm_settings:",
|
|
7879
|
+
" drop_params: true",
|
|
7880
|
+
"",
|
|
7881
|
+
"router_settings:",
|
|
7882
|
+
" num_retries: 0",
|
|
7883
|
+
" allowed_fails: 0",
|
|
7884
|
+
" disable_cooldowns: true",
|
|
7885
|
+
" fallbacks:",
|
|
7886
|
+
...fallbacks,
|
|
7887
|
+
""
|
|
7888
|
+
].join("\n");
|
|
7889
|
+
}
|
|
7853
7890
|
function litellmModelGroups(model) {
|
|
7854
7891
|
const primary = litellmModelGroup(model);
|
|
7855
7892
|
return Array.from(/* @__PURE__ */ new Set([primary, ...CLAUDE_CODE_PROXY_MODEL_ALIASES]));
|
|
@@ -7922,9 +7959,29 @@ function resolveLitellmCommand() {
|
|
|
7922
7959
|
}
|
|
7923
7960
|
async function startLitellmIfNeeded(model, projectDir, url = LITELLM_DEFAULT_URL, runtimeEnvironment = {}) {
|
|
7924
7961
|
if (!needsLitellmProxy(model)) return null;
|
|
7962
|
+
return startLitellmProxy({
|
|
7963
|
+
projectDir,
|
|
7964
|
+
url,
|
|
7965
|
+
runtimeEnvironment,
|
|
7966
|
+
configYaml: generateLitellmConfigYaml(model),
|
|
7967
|
+
requiredModelGroups: litellmModelGroups(model)
|
|
7968
|
+
});
|
|
7969
|
+
}
|
|
7970
|
+
async function startAutomaticLitellm(models, projectDir, runtimeEnvironment = {}, url = LITELLM_DEFAULT_URL) {
|
|
7971
|
+
const handle = await startLitellmProxy({
|
|
7972
|
+
projectDir,
|
|
7973
|
+
url,
|
|
7974
|
+
runtimeEnvironment,
|
|
7975
|
+
configYaml: generateAutomaticLitellmConfigYaml(models),
|
|
7976
|
+
requiredModelGroups: [AUTOMATIC_LITELLM_MODEL_GROUP, ...CLAUDE_CODE_PROXY_MODEL_ALIASES]
|
|
7977
|
+
});
|
|
7978
|
+
return { ...handle, modelGroup: AUTOMATIC_LITELLM_MODEL_GROUP };
|
|
7979
|
+
}
|
|
7980
|
+
async function startLitellmProxy(input) {
|
|
7981
|
+
const { projectDir, runtimeEnvironment } = input;
|
|
7925
7982
|
const cmd = resolveLitellmCommand();
|
|
7926
|
-
let activeUrl = url.replace(/\/+$/, "");
|
|
7927
|
-
const modelGroups =
|
|
7983
|
+
let activeUrl = input.url.replace(/\/+$/, "");
|
|
7984
|
+
const modelGroups = input.requiredModelGroups;
|
|
7928
7985
|
const childEnv = stripBlockingEnv({
|
|
7929
7986
|
...process.env,
|
|
7930
7987
|
...readDotenvApiKeys(projectDir),
|
|
@@ -7936,7 +7993,7 @@ async function startLitellmIfNeeded(model, projectDir, url = LITELLM_DEFAULT_URL
|
|
|
7936
7993
|
const portMatch = activeUrl.match(/:(\d+)/);
|
|
7937
7994
|
const port = portMatch ? portMatch[1] : "4000";
|
|
7938
7995
|
const configPath = path27.join(os4.tmpdir(), `kody-local-litellm-${Date.now()}.yaml`);
|
|
7939
|
-
fs28.writeFileSync(configPath,
|
|
7996
|
+
fs28.writeFileSync(configPath, input.configYaml);
|
|
7940
7997
|
const args = ["--config", configPath, "--port", port];
|
|
7941
7998
|
const nextLogPath = path27.join(os4.tmpdir(), `kody-local-litellm-${Date.now()}.log`);
|
|
7942
7999
|
const outFd = fs28.openSync(nextLogPath, "w");
|
|
@@ -8062,7 +8119,7 @@ function stripBlockingEnv(env) {
|
|
|
8062
8119
|
delete out.AI_BASE_URL;
|
|
8063
8120
|
return out;
|
|
8064
8121
|
}
|
|
8065
|
-
var LITELLM_PIP_PACKAGES, DEFAULT_LITELLM_STARTUP_TIMEOUT_SEC, LITELLM_HEALTH_POLL_INTERVAL_MS, CLAUDE_CODE_PROXY_MODEL_ALIASES;
|
|
8122
|
+
var LITELLM_PIP_PACKAGES, DEFAULT_LITELLM_STARTUP_TIMEOUT_SEC, LITELLM_HEALTH_POLL_INTERVAL_MS, CLAUDE_CODE_PROXY_MODEL_ALIASES, AUTOMATIC_LITELLM_MODEL_GROUP;
|
|
8066
8123
|
var init_litellm = __esm({
|
|
8067
8124
|
"src/litellm.ts"() {
|
|
8068
8125
|
"use strict";
|
|
@@ -8071,6 +8128,7 @@ var init_litellm = __esm({
|
|
|
8071
8128
|
DEFAULT_LITELLM_STARTUP_TIMEOUT_SEC = 150;
|
|
8072
8129
|
LITELLM_HEALTH_POLL_INTERVAL_MS = 2e3;
|
|
8073
8130
|
CLAUDE_CODE_PROXY_MODEL_ALIASES = ["claude-haiku-4-5-20251001", "haiku"];
|
|
8131
|
+
AUTOMATIC_LITELLM_MODEL_GROUP = "kody-automatic-0";
|
|
8074
8132
|
}
|
|
8075
8133
|
});
|
|
8076
8134
|
|
|
@@ -18269,8 +18327,7 @@ function githubRepositoryParts(repoUrl) {
|
|
|
18269
18327
|
if (!parts[0] || !repo) throw new Error("repository URL is incomplete");
|
|
18270
18328
|
return { owner: parts[0], repo };
|
|
18271
18329
|
}
|
|
18272
|
-
function browserOrigin(
|
|
18273
|
-
const raw = typeof ctx.data.previewUrl === "string" ? ctx.data.previewUrl : "";
|
|
18330
|
+
function browserOrigin(raw) {
|
|
18274
18331
|
if (!raw) throw new Error("QA target URL is unavailable");
|
|
18275
18332
|
const parsed = new URL(raw);
|
|
18276
18333
|
if (parsed.protocol !== "https:" && parsed.protocol !== "http:") {
|
|
@@ -18355,6 +18412,17 @@ async function prepareMethod(ctx, profile, method) {
|
|
|
18355
18412
|
const { repositoryKey, credentialKey } = fieldsForKodyRepository(method);
|
|
18356
18413
|
const variables = readKodyVariables(ctx.cwd);
|
|
18357
18414
|
const repositoryUrl = variables[repositoryKey]?.trim() ?? "";
|
|
18415
|
+
const targetUrl = typeof ctx.data.previewUrl === "string" ? ctx.data.previewUrl : "";
|
|
18416
|
+
return prepareKodyRepositoryBrowserAuth(ctx, profile, {
|
|
18417
|
+
repositoryUrl,
|
|
18418
|
+
repositoryKey,
|
|
18419
|
+
credentialKey,
|
|
18420
|
+
methodName: method.name,
|
|
18421
|
+
targetUrl
|
|
18422
|
+
});
|
|
18423
|
+
}
|
|
18424
|
+
async function prepareKodyRepositoryBrowserAuth(ctx, profile, input) {
|
|
18425
|
+
const { repositoryUrl, credentialKey } = input;
|
|
18358
18426
|
const credential = await resolveRuntimeSecret(credentialKey, ctx);
|
|
18359
18427
|
ctx.data.qaAuthSecretSources = {
|
|
18360
18428
|
...ctx.data.qaAuthSecretSources ?? {},
|
|
@@ -18368,14 +18436,14 @@ async function prepareMethod(ctx, profile, method) {
|
|
|
18368
18436
|
if (!repositoryUrl) {
|
|
18369
18437
|
appendAuthMessage(
|
|
18370
18438
|
ctx,
|
|
18371
|
-
`Auth: ${
|
|
18439
|
+
`Auth: ${input.methodName} is incomplete because no \`${input.repositoryKey ?? "repository"}\` variable was found. Note this authenticated surface as a gap.`
|
|
18372
18440
|
);
|
|
18373
18441
|
return false;
|
|
18374
18442
|
}
|
|
18375
18443
|
if (!credential.value) {
|
|
18376
18444
|
appendAuthMessage(
|
|
18377
18445
|
ctx,
|
|
18378
|
-
`Auth: ${
|
|
18446
|
+
`Auth: ${input.methodName} is incomplete because no \`${credentialKey}\` secret was found. Note this authenticated surface as a gap.`
|
|
18379
18447
|
);
|
|
18380
18448
|
return false;
|
|
18381
18449
|
}
|
|
@@ -18394,7 +18462,7 @@ async function prepareMethod(ctx, profile, method) {
|
|
|
18394
18462
|
throw new Error("GitHub returned incomplete identity data");
|
|
18395
18463
|
}
|
|
18396
18464
|
state = writeKodyStorageState({
|
|
18397
|
-
origin: browserOrigin(
|
|
18465
|
+
origin: browserOrigin(input.targetUrl),
|
|
18398
18466
|
repoUrl: `https://github.com/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}`,
|
|
18399
18467
|
owner,
|
|
18400
18468
|
repo,
|
|
@@ -18408,7 +18476,7 @@ async function prepareMethod(ctx, profile, method) {
|
|
|
18408
18476
|
});
|
|
18409
18477
|
appendAuthMessage(
|
|
18410
18478
|
ctx,
|
|
18411
|
-
`Auth: ${
|
|
18479
|
+
`Auth: ${input.methodName} is already authenticated by the engine-provided browser session. The credential is not available to you; never request, reveal, or report it.`
|
|
18412
18480
|
);
|
|
18413
18481
|
return true;
|
|
18414
18482
|
} catch (error) {
|
|
@@ -18416,7 +18484,7 @@ async function prepareMethod(ctx, profile, method) {
|
|
|
18416
18484
|
const reason = error instanceof Error ? error.message : String(error);
|
|
18417
18485
|
appendAuthMessage(
|
|
18418
18486
|
ctx,
|
|
18419
|
-
`Auth: the engine could not prepare ${
|
|
18487
|
+
`Auth: the engine could not prepare ${input.methodName} (${reason}). Note this authenticated surface as a gap.`
|
|
18420
18488
|
);
|
|
18421
18489
|
return false;
|
|
18422
18490
|
}
|
|
@@ -18635,7 +18703,7 @@ var init_prepareSimpleCapabilityRuntime = __esm({
|
|
|
18635
18703
|
"src/scripts/prepareSimpleCapabilityRuntime.ts"() {
|
|
18636
18704
|
"use strict";
|
|
18637
18705
|
init_loadQaContext();
|
|
18638
|
-
|
|
18706
|
+
init_prepareBrowserAuth();
|
|
18639
18707
|
PLAYWRIGHT_SERVER = {
|
|
18640
18708
|
name: "playwright",
|
|
18641
18709
|
command: "npx",
|
|
@@ -18647,6 +18715,18 @@ var init_prepareSimpleCapabilityRuntime = __esm({
|
|
|
18647
18715
|
configureBrowser(ctx, profile, requirements);
|
|
18648
18716
|
if (requirements.qaCredentials) {
|
|
18649
18717
|
await loadQaContext(ctx, profile);
|
|
18718
|
+
}
|
|
18719
|
+
if (requirements.githubTestToken) {
|
|
18720
|
+
const capabilityInput = ctx.data.capabilityInput && typeof ctx.data.capabilityInput === "object" && !Array.isArray(ctx.data.capabilityInput) ? ctx.data.capabilityInput : {};
|
|
18721
|
+
const targetUrl = typeof capabilityInput.url === "string" ? capabilityInput.url : typeof capabilityInput.targetUrl === "string" ? capabilityInput.targetUrl : "";
|
|
18722
|
+
await prepareKodyRepositoryBrowserAuth(ctx, profile, {
|
|
18723
|
+
repositoryUrl: `https://github.com/${ctx.config.github.owner}/${ctx.config.github.repo}`,
|
|
18724
|
+
credentialKey: "E2E_GITHUB_TOKEN",
|
|
18725
|
+
methodName: "Kody protected QA login",
|
|
18726
|
+
targetUrl
|
|
18727
|
+
});
|
|
18728
|
+
}
|
|
18729
|
+
if (requirements.qaCredentials || requirements.githubTestToken) {
|
|
18650
18730
|
appendPrompt(
|
|
18651
18731
|
ctx,
|
|
18652
18732
|
[
|
|
@@ -18658,22 +18738,6 @@ var init_prepareSimpleCapabilityRuntime = __esm({
|
|
|
18658
18738
|
].join("\n")
|
|
18659
18739
|
);
|
|
18660
18740
|
}
|
|
18661
|
-
if (requirements.githubTestToken) {
|
|
18662
|
-
const token = await resolveRuntimeSecret("E2E_GITHUB_TOKEN", ctx);
|
|
18663
|
-
appendPrompt(
|
|
18664
|
-
ctx,
|
|
18665
|
-
token.value ? [
|
|
18666
|
-
"## Protected GitHub test login",
|
|
18667
|
-
"",
|
|
18668
|
-
`A protected GitHub test token is available: \`${token.value}\``,
|
|
18669
|
-
"Use it only if the target application asks for a GitHub personal access token; never include the token in screenshots, output, logs, files, or messages."
|
|
18670
|
-
].join("\n") : [
|
|
18671
|
-
"## Protected GitHub test login",
|
|
18672
|
-
"",
|
|
18673
|
-
"E2E_GITHUB_TOKEN is not configured. If this Quality Scenario requires GitHub token authentication, return a blocked result."
|
|
18674
|
-
].join("\n")
|
|
18675
|
-
);
|
|
18676
|
-
}
|
|
18677
18741
|
};
|
|
18678
18742
|
}
|
|
18679
18743
|
});
|
|
@@ -22454,6 +22518,19 @@ async function runImplementation(profileName, input) {
|
|
|
22454
22518
|
const syntheticPath = ctx.data.syntheticPluginPath;
|
|
22455
22519
|
const pluginPaths = [...externalPlugins, ...syntheticPath ? [syntheticPath] : []];
|
|
22456
22520
|
const agents = loadSubagents(profile);
|
|
22521
|
+
if (modelSpec === "automatic") {
|
|
22522
|
+
const automaticEnvironment = {};
|
|
22523
|
+
for (const candidate of modelCandidates) {
|
|
22524
|
+
const resolved2 = await resolveRuntimeModelEnvironment(candidate, ctx);
|
|
22525
|
+
Object.assign(automaticEnvironment, resolved2.environment);
|
|
22526
|
+
for (const warning of resolved2.warnings) process.stderr.write(`\u26A0 ${warning}
|
|
22527
|
+
`);
|
|
22528
|
+
}
|
|
22529
|
+
const lm = await startAutomaticLitellm(modelCandidates, input.cwd, automaticEnvironment);
|
|
22530
|
+
activeLiteLLM.add(lm);
|
|
22531
|
+
const result = await invokeModel(modelCandidates[0], lm, automaticEnvironment, lm.modelGroup);
|
|
22532
|
+
return result;
|
|
22533
|
+
}
|
|
22457
22534
|
let finalResult;
|
|
22458
22535
|
for (let candidateIndex = 0; candidateIndex < modelCandidates.length; candidateIndex++) {
|
|
22459
22536
|
const candidate = modelCandidates[candidateIndex];
|
|
@@ -22473,7 +22550,24 @@ async function runImplementation(profileName, input) {
|
|
|
22473
22550
|
}
|
|
22474
22551
|
ctx.data.jobModelProvider = candidate.provider;
|
|
22475
22552
|
ctx.data.jobModelName = candidate.model;
|
|
22476
|
-
const result = await
|
|
22553
|
+
const result = await invokeModel(candidate, lm, runtimeModelEnvironment);
|
|
22554
|
+
finalResult = result;
|
|
22555
|
+
const next = modelCandidates[candidateIndex + 1];
|
|
22556
|
+
if (!next || result.outcome !== "failed" || result.outcomeKind !== "rate_limit" || result.safeToReplay !== true) {
|
|
22557
|
+
return result;
|
|
22558
|
+
}
|
|
22559
|
+
process.stderr.write(
|
|
22560
|
+
`[kody agent] ${candidate.spec ?? candidate.model} is rate limited; continuing with ${next.spec ?? next.model}
|
|
22561
|
+
`
|
|
22562
|
+
);
|
|
22563
|
+
if (lm) {
|
|
22564
|
+
lm.kill();
|
|
22565
|
+
activeLiteLLM.delete(lm);
|
|
22566
|
+
}
|
|
22567
|
+
}
|
|
22568
|
+
return finalResult;
|
|
22569
|
+
async function invokeModel(candidate, lm, runtimeModelEnvironment, litellmModelGroupOverride) {
|
|
22570
|
+
return runAgent({
|
|
22477
22571
|
prompt,
|
|
22478
22572
|
model: candidate,
|
|
22479
22573
|
cwd: input.cwd,
|
|
@@ -22482,6 +22576,7 @@ async function runImplementation(profileName, input) {
|
|
|
22482
22576
|
...runtimeModelEnvironment
|
|
22483
22577
|
},
|
|
22484
22578
|
litellmUrl: lm?.url ?? null,
|
|
22579
|
+
litellmModelGroupOverride,
|
|
22485
22580
|
// On a connection drop mid-run, restart the (possibly crashed) proxy
|
|
22486
22581
|
// before the agent retries. No-op for direct-Anthropic runs (lm null).
|
|
22487
22582
|
ensureBackend: lm ? () => lm.ensureHealthy().then(() => void 0) : void 0,
|
|
@@ -22491,7 +22586,7 @@ async function runImplementation(profileName, input) {
|
|
|
22491
22586
|
verbose: input.verbose,
|
|
22492
22587
|
quiet: input.quiet,
|
|
22493
22588
|
abortController: input.abortController,
|
|
22494
|
-
stopOnRateLimit:
|
|
22589
|
+
stopOnRateLimit: false,
|
|
22495
22590
|
deadlineAtMs: input.deadlineAtMs,
|
|
22496
22591
|
ndjsonDir,
|
|
22497
22592
|
additionalDirectories: agentTaskArtifacts ? [agentTaskArtifacts.absDir] : void 0,
|
|
@@ -22541,21 +22636,7 @@ async function runImplementation(profileName, input) {
|
|
|
22541
22636
|
schema: ctx.data.capabilityOutputSchema
|
|
22542
22637
|
} : void 0
|
|
22543
22638
|
});
|
|
22544
|
-
finalResult = result;
|
|
22545
|
-
const next = modelCandidates[candidateIndex + 1];
|
|
22546
|
-
if (!next || result.outcome !== "failed" || result.outcomeKind !== "rate_limit" || result.safeToReplay !== true) {
|
|
22547
|
-
return result;
|
|
22548
|
-
}
|
|
22549
|
-
process.stderr.write(
|
|
22550
|
-
`[kody agent] ${candidate.spec ?? candidate.model} is rate limited; continuing with ${next.spec ?? next.model}
|
|
22551
|
-
`
|
|
22552
|
-
);
|
|
22553
|
-
if (lm) {
|
|
22554
|
-
lm.kill();
|
|
22555
|
-
activeLiteLLM.delete(lm);
|
|
22556
|
-
}
|
|
22557
22639
|
}
|
|
22558
|
-
return finalResult;
|
|
22559
22640
|
};
|
|
22560
22641
|
ctx.data.__invokeAgent = invokeAgent;
|
|
22561
22642
|
const cc = profile.claudeCode;
|
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.598",
|
|
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",
|