@kody-ade/kody-engine 0.4.595 → 0.4.597

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.
Files changed (2) hide show
  1. package/dist/bin/kody.js +97 -23
  2. 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.595",
18
+ version: "0.4.597",
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",
@@ -4180,7 +4180,6 @@ async function runAgent(opts) {
4180
4180
  env.ANTHROPIC_BASE_URL = opts.litellmUrl;
4181
4181
  env.ANTHROPIC_API_KEY = getAnthropicApiKeyOrDummy();
4182
4182
  }
4183
- if (opts.stopOnRateLimit) env.CLAUDE_CODE_MAX_RETRIES = "0";
4184
4183
  const startedAt = Date.now();
4185
4184
  const turnTimeoutMs = resolveTurnTimeoutMs(opts);
4186
4185
  const completionGuard = typeof opts.deadlineAtMs === "number" ? createCompletionToolGuard(
@@ -4229,7 +4228,7 @@ async function runAgent(opts) {
4229
4228
  }
4230
4229
  try {
4231
4230
  const queryOptions = {
4232
- model: opts.litellmUrl ? litellmModelGroup(opts.model) : opts.model.model,
4231
+ model: opts.litellmUrl ? opts.litellmModelGroupOverride ?? litellmModelGroup(opts.model) : opts.model.model,
4233
4232
  cwd: opts.cwd,
4234
4233
  // Fresh array (never mutate the shared DEFAULT_ALLOWED_TOOLS const) so
4235
4234
  // opt-in tools like fetch_repo can be appended below.
@@ -7851,6 +7850,43 @@ function generateLitellmConfigYaml(model) {
7851
7850
  ]);
7852
7851
  return ["model_list:", ...modelEntries, "", "litellm_settings:", " drop_params: true", ""].join("\n");
7853
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
+ }
7854
7890
  function litellmModelGroups(model) {
7855
7891
  const primary = litellmModelGroup(model);
7856
7892
  return Array.from(/* @__PURE__ */ new Set([primary, ...CLAUDE_CODE_PROXY_MODEL_ALIASES]));
@@ -7923,9 +7959,29 @@ function resolveLitellmCommand() {
7923
7959
  }
7924
7960
  async function startLitellmIfNeeded(model, projectDir, url = LITELLM_DEFAULT_URL, runtimeEnvironment = {}) {
7925
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;
7926
7982
  const cmd = resolveLitellmCommand();
7927
- let activeUrl = url.replace(/\/+$/, "");
7928
- const modelGroups = litellmModelGroups(model);
7983
+ let activeUrl = input.url.replace(/\/+$/, "");
7984
+ const modelGroups = input.requiredModelGroups;
7929
7985
  const childEnv = stripBlockingEnv({
7930
7986
  ...process.env,
7931
7987
  ...readDotenvApiKeys(projectDir),
@@ -7937,7 +7993,7 @@ async function startLitellmIfNeeded(model, projectDir, url = LITELLM_DEFAULT_URL
7937
7993
  const portMatch = activeUrl.match(/:(\d+)/);
7938
7994
  const port = portMatch ? portMatch[1] : "4000";
7939
7995
  const configPath = path27.join(os4.tmpdir(), `kody-local-litellm-${Date.now()}.yaml`);
7940
- fs28.writeFileSync(configPath, generateLitellmConfigYaml(model));
7996
+ fs28.writeFileSync(configPath, input.configYaml);
7941
7997
  const args = ["--config", configPath, "--port", port];
7942
7998
  const nextLogPath = path27.join(os4.tmpdir(), `kody-local-litellm-${Date.now()}.log`);
7943
7999
  const outFd = fs28.openSync(nextLogPath, "w");
@@ -8063,7 +8119,7 @@ function stripBlockingEnv(env) {
8063
8119
  delete out.AI_BASE_URL;
8064
8120
  return out;
8065
8121
  }
8066
- 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;
8067
8123
  var init_litellm = __esm({
8068
8124
  "src/litellm.ts"() {
8069
8125
  "use strict";
@@ -8072,6 +8128,7 @@ var init_litellm = __esm({
8072
8128
  DEFAULT_LITELLM_STARTUP_TIMEOUT_SEC = 150;
8073
8129
  LITELLM_HEALTH_POLL_INTERVAL_MS = 2e3;
8074
8130
  CLAUDE_CODE_PROXY_MODEL_ALIASES = ["claude-haiku-4-5-20251001", "haiku"];
8131
+ AUTOMATIC_LITELLM_MODEL_GROUP = "kody-automatic-0";
8075
8132
  }
8076
8133
  });
8077
8134
 
@@ -22455,6 +22512,19 @@ async function runImplementation(profileName, input) {
22455
22512
  const syntheticPath = ctx.data.syntheticPluginPath;
22456
22513
  const pluginPaths = [...externalPlugins, ...syntheticPath ? [syntheticPath] : []];
22457
22514
  const agents = loadSubagents(profile);
22515
+ if (modelSpec === "automatic") {
22516
+ const automaticEnvironment = {};
22517
+ for (const candidate of modelCandidates) {
22518
+ const resolved2 = await resolveRuntimeModelEnvironment(candidate, ctx);
22519
+ Object.assign(automaticEnvironment, resolved2.environment);
22520
+ for (const warning of resolved2.warnings) process.stderr.write(`\u26A0 ${warning}
22521
+ `);
22522
+ }
22523
+ const lm = await startAutomaticLitellm(modelCandidates, input.cwd, automaticEnvironment);
22524
+ activeLiteLLM.add(lm);
22525
+ const result = await invokeModel(modelCandidates[0], lm, automaticEnvironment, lm.modelGroup);
22526
+ return result;
22527
+ }
22458
22528
  let finalResult;
22459
22529
  for (let candidateIndex = 0; candidateIndex < modelCandidates.length; candidateIndex++) {
22460
22530
  const candidate = modelCandidates[candidateIndex];
@@ -22474,7 +22544,24 @@ async function runImplementation(profileName, input) {
22474
22544
  }
22475
22545
  ctx.data.jobModelProvider = candidate.provider;
22476
22546
  ctx.data.jobModelName = candidate.model;
22477
- const result = await runAgent({
22547
+ const result = await invokeModel(candidate, lm, runtimeModelEnvironment);
22548
+ finalResult = result;
22549
+ const next = modelCandidates[candidateIndex + 1];
22550
+ if (!next || result.outcome !== "failed" || result.outcomeKind !== "rate_limit" || result.safeToReplay !== true) {
22551
+ return result;
22552
+ }
22553
+ process.stderr.write(
22554
+ `[kody agent] ${candidate.spec ?? candidate.model} is rate limited; continuing with ${next.spec ?? next.model}
22555
+ `
22556
+ );
22557
+ if (lm) {
22558
+ lm.kill();
22559
+ activeLiteLLM.delete(lm);
22560
+ }
22561
+ }
22562
+ return finalResult;
22563
+ async function invokeModel(candidate, lm, runtimeModelEnvironment, litellmModelGroupOverride) {
22564
+ return runAgent({
22478
22565
  prompt,
22479
22566
  model: candidate,
22480
22567
  cwd: input.cwd,
@@ -22483,6 +22570,7 @@ async function runImplementation(profileName, input) {
22483
22570
  ...runtimeModelEnvironment
22484
22571
  },
22485
22572
  litellmUrl: lm?.url ?? null,
22573
+ litellmModelGroupOverride,
22486
22574
  // On a connection drop mid-run, restart the (possibly crashed) proxy
22487
22575
  // before the agent retries. No-op for direct-Anthropic runs (lm null).
22488
22576
  ensureBackend: lm ? () => lm.ensureHealthy().then(() => void 0) : void 0,
@@ -22492,7 +22580,7 @@ async function runImplementation(profileName, input) {
22492
22580
  verbose: input.verbose,
22493
22581
  quiet: input.quiet,
22494
22582
  abortController: input.abortController,
22495
- stopOnRateLimit: candidateIndex < modelCandidates.length - 1,
22583
+ stopOnRateLimit: false,
22496
22584
  deadlineAtMs: input.deadlineAtMs,
22497
22585
  ndjsonDir,
22498
22586
  additionalDirectories: agentTaskArtifacts ? [agentTaskArtifacts.absDir] : void 0,
@@ -22542,21 +22630,7 @@ async function runImplementation(profileName, input) {
22542
22630
  schema: ctx.data.capabilityOutputSchema
22543
22631
  } : void 0
22544
22632
  });
22545
- finalResult = result;
22546
- const next = modelCandidates[candidateIndex + 1];
22547
- if (!next || result.outcome !== "failed" || result.outcomeKind !== "rate_limit" || result.safeToReplay !== true) {
22548
- return result;
22549
- }
22550
- process.stderr.write(
22551
- `[kody agent] ${candidate.spec ?? candidate.model} is rate limited; continuing with ${next.spec ?? next.model}
22552
- `
22553
- );
22554
- if (lm) {
22555
- lm.kill();
22556
- activeLiteLLM.delete(lm);
22557
- }
22558
22633
  }
22559
- return finalResult;
22560
22634
  };
22561
22635
  ctx.data.__invokeAgent = invokeAgent;
22562
22636
  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.595",
3
+ "version": "0.4.597",
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",