@kody-ade/kody-engine 0.4.495 → 0.4.497

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 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.495",
18
+ version: "0.4.497",
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",
@@ -2448,12 +2448,12 @@ async function readRuntimeSecretFromKody(name, env = process.env) {
2448
2448
  const body = await response.json();
2449
2449
  return typeof body.value === "string" ? body.value : null;
2450
2450
  }
2451
- async function writeRuntimeSecretToKody(name, value, env = process.env) {
2451
+ async function writeRuntimeSecretsToKody(secrets, env = process.env) {
2452
2452
  const token = await githubOidcToken(env);
2453
2453
  const response = await fetch(`${resolveKodyApiUrl(env)}/api/kody/engine/secret`, {
2454
2454
  method: "PUT",
2455
2455
  headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json" },
2456
- body: JSON.stringify({ name, value }),
2456
+ body: JSON.stringify({ secrets }),
2457
2457
  signal: AbortSignal.timeout(3e4)
2458
2458
  });
2459
2459
  if (!response.ok) throw new Error(`Kody secret upsert failed (${response.status})`);
@@ -4267,56 +4267,6 @@ var init_agencyBoundaryEval = __esm({
4267
4267
  }
4268
4268
  });
4269
4269
 
4270
- // src/scripts/capabilityExecutionEnvironment.ts
4271
- function capabilityInputEnvironment(input) {
4272
- const environment = {
4273
- KODY_CAPABILITY_INPUT: JSON.stringify(input ?? null)
4274
- };
4275
- if (!input || typeof input !== "object" || Array.isArray(input)) {
4276
- return environment;
4277
- }
4278
- for (const [name, value] of Object.entries(input)) {
4279
- if (value === void 0 || value === null) continue;
4280
- const key = environmentKey(name);
4281
- environment[`KODY_ARG_${key}`] = typeof value === "string" ? value : JSON.stringify(value);
4282
- }
4283
- return environment;
4284
- }
4285
- function capabilityConfigEnvironment(config) {
4286
- if (!config || typeof config !== "object" || Array.isArray(config)) return {};
4287
- return Object.fromEntries(
4288
- flattenConfig(config).map(([key, value]) => [
4289
- `KODY_CFG_${key}`,
4290
- value
4291
- ])
4292
- );
4293
- }
4294
- function flattenConfig(config, prefix = "") {
4295
- const entries = [];
4296
- for (const [name, value] of Object.entries(config)) {
4297
- if (value === null || value === void 0) continue;
4298
- const key = prefix ? `${prefix}_${environmentKey(name)}` : environmentKey(name);
4299
- if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
4300
- entries.push([key, String(value)]);
4301
- } else if (Array.isArray(value)) {
4302
- entries.push([key, JSON.stringify(value)]);
4303
- } else if (typeof value === "object") {
4304
- entries.push(
4305
- ...flattenConfig(value, key)
4306
- );
4307
- }
4308
- }
4309
- return entries;
4310
- }
4311
- function environmentKey(name) {
4312
- return name.toUpperCase().replace(/[^A-Z0-9]+/g, "_");
4313
- }
4314
- var init_capabilityExecutionEnvironment = __esm({
4315
- "src/scripts/capabilityExecutionEnvironment.ts"() {
4316
- "use strict";
4317
- }
4318
- });
4319
-
4320
4270
  // src/agency/capability-contract-validation.ts
4321
4271
  import Ajv from "ajv";
4322
4272
  function validateCapabilityContractValue(boundary, schema, value) {
@@ -4325,11 +4275,13 @@ function validateCapabilityContractValue(boundary, schema, value) {
4325
4275
  throw new CapabilityContractValidationError(boundary, validate.errors ?? []);
4326
4276
  }
4327
4277
  }
4328
- function capabilityContractInput(inputs, args, capabilityId) {
4278
+ function capabilityContractInput(inputs, args, capabilityId, contractProperties = []) {
4329
4279
  const isGenericRunnerInput = inputs.some((input) => input.name === "input") && Object.hasOwn(args, "input");
4330
4280
  if (!isGenericRunnerInput) {
4331
- const isParameterlessGenericRunner = (inputs.some((input) => input.name === "capability") || args.capability === capabilityId) && Object.hasOwn(args, "capability");
4332
- return isParameterlessGenericRunner ? {} : args;
4281
+ const isParameterlessGenericRunner = (inputs.some((input) => input.name === "capability") || args.capability === capabilityId || !contractProperties.includes("capability")) && Object.hasOwn(args, "capability");
4282
+ if (!isParameterlessGenericRunner) return args;
4283
+ const { capability: _routingCapability, ...businessArgs } = args;
4284
+ return businessArgs;
4333
4285
  }
4334
4286
  const value = args.input;
4335
4287
  if (typeof value !== "string") return value;
@@ -6947,12 +6899,16 @@ function resolveLitellmCommand() {
6947
6899
  throw new Error("litellm is importable but its console script was not found next to python3");
6948
6900
  }
6949
6901
  }
6950
- async function startLitellmIfNeeded(model, projectDir, url = LITELLM_DEFAULT_URL) {
6902
+ async function startLitellmIfNeeded(model, projectDir, url = LITELLM_DEFAULT_URL, runtimeEnvironment = {}) {
6951
6903
  if (!needsLitellmProxy(model)) return null;
6952
6904
  const cmd = resolveLitellmCommand();
6953
6905
  let activeUrl = url.replace(/\/+$/, "");
6954
6906
  const modelGroups = litellmModelGroups(model);
6955
- const childEnv = stripBlockingEnv({ ...process.env, ...readDotenvApiKeys(projectDir) });
6907
+ const childEnv = stripBlockingEnv({
6908
+ ...process.env,
6909
+ ...readDotenvApiKeys(projectDir),
6910
+ ...runtimeEnvironment
6911
+ });
6956
6912
  let child;
6957
6913
  let logPath;
6958
6914
  const spawnProxy = () => {
@@ -7330,6 +7286,256 @@ var init_runtimeCleanup = __esm({
7330
7286
  }
7331
7287
  });
7332
7288
 
7289
+ // src/backendVault.ts
7290
+ import { createDecipheriv, createHash as createHash4 } from "crypto";
7291
+ function cacheKey(owner, repo, masterKey) {
7292
+ const keyHash = createHash4("sha256").update(masterKey).digest("hex").slice(0, 16);
7293
+ return `${owner}/${repo}:${keyHash}`.toLowerCase();
7294
+ }
7295
+ function decryptVault(payload, masterKey) {
7296
+ const parts = payload.split(":");
7297
+ if (parts.length !== 4 || parts[0] !== "v1") {
7298
+ throw new Error("invalid vault payload format");
7299
+ }
7300
+ const [, ivB64, ctB64, tagB64] = parts;
7301
+ const iv = Buffer.from(ivB64, "base64");
7302
+ const ct = Buffer.from(ctB64, "base64");
7303
+ const tag = Buffer.from(tagB64, "base64");
7304
+ const decipher = createDecipheriv("aes-256-gcm", masterKey, iv);
7305
+ decipher.setAuthTag(tag);
7306
+ return Buffer.concat([decipher.update(ct), decipher.final()]).toString("utf8");
7307
+ }
7308
+ async function readVaultSecrets(opts) {
7309
+ const key = cacheKey(opts.owner, opts.repo, opts.masterKey);
7310
+ const hit = cache.get(key);
7311
+ if (hit && hit.expiresAt > Date.now()) return hit.secrets;
7312
+ const record2 = await createStateBackendFromEnv().getRepoDoc(`${opts.owner}/${opts.repo}`, VAULT_PATH);
7313
+ const raw = record2?.doc;
7314
+ const ciphertext = raw && typeof raw === "object" && !Array.isArray(raw) && typeof raw.ciphertext === "string" ? raw.ciphertext.trim() : "";
7315
+ if (!ciphertext) {
7316
+ cache.set(key, { secrets: {}, expiresAt: Date.now() + CACHE_TTL_MS });
7317
+ return {};
7318
+ }
7319
+ const doc = JSON.parse(decryptVault(ciphertext, opts.masterKey));
7320
+ const flat = {};
7321
+ for (const [name, entry] of Object.entries(doc.secrets ?? {})) {
7322
+ if (entry && typeof entry.value === "string") flat[name] = entry.value;
7323
+ }
7324
+ cache.set(key, { secrets: flat, expiresAt: Date.now() + CACHE_TTL_MS });
7325
+ return flat;
7326
+ }
7327
+ async function readRepoSecret(opts) {
7328
+ const secrets = await readVaultSecrets(opts);
7329
+ const v = secrets[opts.name];
7330
+ return v?.trim() ? v : null;
7331
+ }
7332
+ async function readRepoSecrets(opts) {
7333
+ return readVaultSecrets(opts);
7334
+ }
7335
+ var VAULT_PATH, CACHE_TTL_MS, cache;
7336
+ var init_backendVault = __esm({
7337
+ "src/backendVault.ts"() {
7338
+ "use strict";
7339
+ init_state_backend();
7340
+ VAULT_PATH = "secrets.enc";
7341
+ CACHE_TTL_MS = 6e4;
7342
+ cache = /* @__PURE__ */ new Map();
7343
+ }
7344
+ });
7345
+
7346
+ // src/pool/keys.ts
7347
+ import { hkdfSync } from "crypto";
7348
+ function masterKeyBytes(raw) {
7349
+ const v = raw.trim();
7350
+ if (!v) throw new Error("KODY_MASTER_KEY is empty");
7351
+ if (/^[0-9a-fA-F]+$/.test(v) && v.length === 64) {
7352
+ return Buffer.from(v, "hex");
7353
+ }
7354
+ return Buffer.from(v.replace(/-/g, "+").replace(/_/g, "/"), "base64");
7355
+ }
7356
+ function deriveKey(master, info, length = 32) {
7357
+ return Buffer.from(hkdfSync("sha256", master, Buffer.alloc(0), info, length)).toString("hex");
7358
+ }
7359
+ function derivePoolApiKey(master) {
7360
+ return deriveKey(master, POOL_API_KEY_INFO);
7361
+ }
7362
+ function deriveRunnerApiKey(master) {
7363
+ return deriveKey(master, RUNNER_API_KEY_INFO);
7364
+ }
7365
+ function bearerOk(headerAuth, xApiKey, expected) {
7366
+ const x = (xApiKey ?? "").trim();
7367
+ if (x && timingEqual(x, expected)) return true;
7368
+ const a = (headerAuth ?? "").trim();
7369
+ if (a.toLowerCase().startsWith("bearer ")) {
7370
+ return timingEqual(a.slice(7).trim(), expected);
7371
+ }
7372
+ return false;
7373
+ }
7374
+ function timingEqual(a, b) {
7375
+ if (a.length !== b.length) return false;
7376
+ let diff = 0;
7377
+ for (let i = 0; i < a.length; i++) diff |= a.charCodeAt(i) ^ b.charCodeAt(i);
7378
+ return diff === 0;
7379
+ }
7380
+ var POOL_API_KEY_INFO, RUNNER_API_KEY_INFO;
7381
+ var init_keys = __esm({
7382
+ "src/pool/keys.ts"() {
7383
+ "use strict";
7384
+ POOL_API_KEY_INFO = "kody-pool-api:v1";
7385
+ RUNNER_API_KEY_INFO = "kody-runner-api:v1";
7386
+ }
7387
+ });
7388
+
7389
+ // src/scripts/runtimeSecrets.ts
7390
+ function envSecret(name, env) {
7391
+ const value = env[name]?.trim() ? env[name] : "";
7392
+ return value ? { value, source: "env" } : { value: "", source: "missing" };
7393
+ }
7394
+ async function resolveRuntimeSecret(name, ctx, opts = {}) {
7395
+ const env = opts.env ?? process.env;
7396
+ if (hasGitHubActionsIdentity(env)) {
7397
+ try {
7398
+ const value = await readRuntimeSecretFromKody(name, env);
7399
+ if (value) return { value, source: "vault" };
7400
+ return envSecret(name, env);
7401
+ } catch (err) {
7402
+ const fallback = envSecret(name, env);
7403
+ return {
7404
+ ...fallback,
7405
+ warning: `Kody secret read failed for ${name}: ${err instanceof Error ? err.message : String(err)}`
7406
+ };
7407
+ }
7408
+ }
7409
+ const masterRaw = env.KODY_MASTER_KEY?.trim() ?? "";
7410
+ if (!masterRaw || !env.CONVEX_URL?.trim() || !env.KODY_SERVICE_KEY?.trim()) return envSecret(name, env);
7411
+ try {
7412
+ const masterKey = masterKeyBytes(masterRaw);
7413
+ if (masterKey.length !== 32) {
7414
+ throw new Error("KODY_MASTER_KEY must decode to 32 bytes");
7415
+ }
7416
+ const value = await readRepoSecret({
7417
+ owner: ctx.config.github.owner,
7418
+ repo: ctx.config.github.repo,
7419
+ name,
7420
+ masterKey
7421
+ });
7422
+ if (value) return { value, source: "vault" };
7423
+ } catch (err) {
7424
+ const fallback = envSecret(name, env);
7425
+ return {
7426
+ ...fallback,
7427
+ warning: `vault read failed for ${name}: ${err instanceof Error ? err.message : String(err)}`
7428
+ };
7429
+ }
7430
+ return envSecret(name, env);
7431
+ }
7432
+ async function resolveRuntimeSecrets(names, ctx, opts = {}) {
7433
+ const declared = Array.isArray(names) ? [...new Set(names.filter((name) => typeof name === "string" && /^[A-Z][A-Z0-9_]*$/.test(name)))] : [];
7434
+ const resolved = [];
7435
+ for (const name of declared) {
7436
+ resolved.push({ name, result: await resolveRuntimeSecret(name, ctx, opts) });
7437
+ }
7438
+ const warnings = resolved.flatMap(({ result }) => result.warning ? [result.warning] : []);
7439
+ const fallbackSecrets = Object.fromEntries(
7440
+ resolved.flatMap(({ name, result }) => result.source === "env" && result.value ? [[name, result.value]] : [])
7441
+ );
7442
+ if (hasGitHubActionsIdentity(opts.env ?? process.env) && Object.keys(fallbackSecrets).length > 0) {
7443
+ try {
7444
+ await writeRuntimeSecretsToKody(fallbackSecrets, opts.env ?? process.env);
7445
+ } catch (err) {
7446
+ warnings.push(`Kody secret migration failed: ${err instanceof Error ? err.message : String(err)}`);
7447
+ }
7448
+ }
7449
+ return {
7450
+ environment: Object.fromEntries(
7451
+ resolved.flatMap(({ name, result }) => result.value ? [[name, result.value]] : [])
7452
+ ),
7453
+ warnings
7454
+ };
7455
+ }
7456
+ var init_runtimeSecrets = __esm({
7457
+ "src/scripts/runtimeSecrets.ts"() {
7458
+ "use strict";
7459
+ init_backendVault();
7460
+ init_kody_api_client();
7461
+ init_keys();
7462
+ }
7463
+ });
7464
+
7465
+ // src/runtimeModelEnvironment.ts
7466
+ async function resolveRuntimeModelEnvironment(model, ctx) {
7467
+ const name = model.apiKeyEnvVar ?? providerApiKeyEnvVar(model.provider);
7468
+ const result = await resolveRuntimeSecret(name, ctx);
7469
+ const warnings = result.warning ? [result.warning] : [];
7470
+ if (!result.value) {
7471
+ return {
7472
+ environment: {},
7473
+ warnings: [...warnings, `Model credential ${name} is missing from the Kody vault.`]
7474
+ };
7475
+ }
7476
+ return {
7477
+ environment: { [name]: result.value },
7478
+ warnings
7479
+ };
7480
+ }
7481
+ var init_runtimeModelEnvironment = __esm({
7482
+ "src/runtimeModelEnvironment.ts"() {
7483
+ "use strict";
7484
+ init_config();
7485
+ init_runtimeSecrets();
7486
+ }
7487
+ });
7488
+
7489
+ // src/scripts/capabilityExecutionEnvironment.ts
7490
+ function capabilityInputEnvironment(input) {
7491
+ const environment = {
7492
+ KODY_CAPABILITY_INPUT: JSON.stringify(input ?? null)
7493
+ };
7494
+ if (!input || typeof input !== "object" || Array.isArray(input)) {
7495
+ return environment;
7496
+ }
7497
+ for (const [name, value] of Object.entries(input)) {
7498
+ if (value === void 0 || value === null) continue;
7499
+ const key = environmentKey(name);
7500
+ environment[`KODY_ARG_${key}`] = typeof value === "string" ? value : JSON.stringify(value);
7501
+ }
7502
+ return environment;
7503
+ }
7504
+ function capabilityConfigEnvironment(config) {
7505
+ if (!config || typeof config !== "object" || Array.isArray(config)) return {};
7506
+ return Object.fromEntries(
7507
+ flattenConfig(config).map(([key, value]) => [
7508
+ `KODY_CFG_${key}`,
7509
+ value
7510
+ ])
7511
+ );
7512
+ }
7513
+ function flattenConfig(config, prefix = "") {
7514
+ const entries = [];
7515
+ for (const [name, value] of Object.entries(config)) {
7516
+ if (value === null || value === void 0) continue;
7517
+ const key = prefix ? `${prefix}_${environmentKey(name)}` : environmentKey(name);
7518
+ if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
7519
+ entries.push([key, String(value)]);
7520
+ } else if (Array.isArray(value)) {
7521
+ entries.push([key, JSON.stringify(value)]);
7522
+ } else if (typeof value === "object") {
7523
+ entries.push(
7524
+ ...flattenConfig(value, key)
7525
+ );
7526
+ }
7527
+ }
7528
+ return entries;
7529
+ }
7530
+ function environmentKey(name) {
7531
+ return name.toUpperCase().replace(/[^A-Z0-9]+/g, "_");
7532
+ }
7533
+ var init_capabilityExecutionEnvironment = __esm({
7534
+ "src/scripts/capabilityExecutionEnvironment.ts"() {
7535
+ "use strict";
7536
+ }
7537
+ });
7538
+
7333
7539
  // src/scripts/evaluateAgencyBoundaries.ts
7334
7540
  function shouldEvaluateAgencyBoundaries(data, profile) {
7335
7541
  return Boolean(agencyBoundaryCapabilityKind(data, profile));
@@ -11570,12 +11776,12 @@ var init_classifyByLabel = __esm({
11570
11776
  });
11571
11777
 
11572
11778
  // src/scripts/commitAndPush.ts
11573
- import { createHash as createHash4 } from "crypto";
11779
+ import { createHash as createHash5 } from "crypto";
11574
11780
  import * as fs31 from "fs";
11575
11781
  import * as path29 from "path";
11576
11782
  function sentinelPathForStage(cwd, profileName, workflowExecutionKey) {
11577
11783
  const runId = resolveRunId();
11578
- const executionSuffix = typeof workflowExecutionKey === "string" && workflowExecutionKey.length > 0 ? `-${createHash4("sha256").update(workflowExecutionKey).digest("hex").slice(0, 16)}` : "";
11784
+ const executionSuffix = typeof workflowExecutionKey === "string" && workflowExecutionKey.length > 0 ? `-${createHash5("sha256").update(workflowExecutionKey).digest("hex").slice(0, 16)}` : "";
11579
11785
  return runtimeStatePath(cwd, "agent-runs", runId, `commit-${profileName}${executionSuffix}.lock`);
11580
11786
  }
11581
11787
  var DEFAULT_COMMIT_MESSAGE, commitAndPush2;
@@ -15349,173 +15555,6 @@ var init_kodyVariables = __esm({
15349
15555
  }
15350
15556
  });
15351
15557
 
15352
- // src/backendVault.ts
15353
- import { createDecipheriv, createHash as createHash5 } from "crypto";
15354
- function cacheKey(owner, repo, masterKey) {
15355
- const keyHash = createHash5("sha256").update(masterKey).digest("hex").slice(0, 16);
15356
- return `${owner}/${repo}:${keyHash}`.toLowerCase();
15357
- }
15358
- function decryptVault(payload, masterKey) {
15359
- const parts = payload.split(":");
15360
- if (parts.length !== 4 || parts[0] !== "v1") {
15361
- throw new Error("invalid vault payload format");
15362
- }
15363
- const [, ivB64, ctB64, tagB64] = parts;
15364
- const iv = Buffer.from(ivB64, "base64");
15365
- const ct = Buffer.from(ctB64, "base64");
15366
- const tag = Buffer.from(tagB64, "base64");
15367
- const decipher = createDecipheriv("aes-256-gcm", masterKey, iv);
15368
- decipher.setAuthTag(tag);
15369
- return Buffer.concat([decipher.update(ct), decipher.final()]).toString("utf8");
15370
- }
15371
- async function readVaultSecrets(opts) {
15372
- const key = cacheKey(opts.owner, opts.repo, opts.masterKey);
15373
- const hit = cache.get(key);
15374
- if (hit && hit.expiresAt > Date.now()) return hit.secrets;
15375
- const record2 = await createStateBackendFromEnv().getRepoDoc(`${opts.owner}/${opts.repo}`, VAULT_PATH);
15376
- const raw = record2?.doc;
15377
- const ciphertext = raw && typeof raw === "object" && !Array.isArray(raw) && typeof raw.ciphertext === "string" ? raw.ciphertext.trim() : "";
15378
- if (!ciphertext) {
15379
- cache.set(key, { secrets: {}, expiresAt: Date.now() + CACHE_TTL_MS });
15380
- return {};
15381
- }
15382
- const doc = JSON.parse(decryptVault(ciphertext, opts.masterKey));
15383
- const flat = {};
15384
- for (const [name, entry] of Object.entries(doc.secrets ?? {})) {
15385
- if (entry && typeof entry.value === "string") flat[name] = entry.value;
15386
- }
15387
- cache.set(key, { secrets: flat, expiresAt: Date.now() + CACHE_TTL_MS });
15388
- return flat;
15389
- }
15390
- async function readRepoSecret(opts) {
15391
- const secrets = await readVaultSecrets(opts);
15392
- const v = secrets[opts.name];
15393
- return v?.trim() ? v : null;
15394
- }
15395
- async function readRepoSecrets(opts) {
15396
- return readVaultSecrets(opts);
15397
- }
15398
- var VAULT_PATH, CACHE_TTL_MS, cache;
15399
- var init_backendVault = __esm({
15400
- "src/backendVault.ts"() {
15401
- "use strict";
15402
- init_state_backend();
15403
- VAULT_PATH = "secrets.enc";
15404
- CACHE_TTL_MS = 6e4;
15405
- cache = /* @__PURE__ */ new Map();
15406
- }
15407
- });
15408
-
15409
- // src/pool/keys.ts
15410
- import { hkdfSync } from "crypto";
15411
- function masterKeyBytes(raw) {
15412
- const v = raw.trim();
15413
- if (!v) throw new Error("KODY_MASTER_KEY is empty");
15414
- if (/^[0-9a-fA-F]+$/.test(v) && v.length === 64) {
15415
- return Buffer.from(v, "hex");
15416
- }
15417
- return Buffer.from(v.replace(/-/g, "+").replace(/_/g, "/"), "base64");
15418
- }
15419
- function deriveKey(master, info, length = 32) {
15420
- return Buffer.from(hkdfSync("sha256", master, Buffer.alloc(0), info, length)).toString("hex");
15421
- }
15422
- function derivePoolApiKey(master) {
15423
- return deriveKey(master, POOL_API_KEY_INFO);
15424
- }
15425
- function deriveRunnerApiKey(master) {
15426
- return deriveKey(master, RUNNER_API_KEY_INFO);
15427
- }
15428
- function bearerOk(headerAuth, xApiKey, expected) {
15429
- const x = (xApiKey ?? "").trim();
15430
- if (x && timingEqual(x, expected)) return true;
15431
- const a = (headerAuth ?? "").trim();
15432
- if (a.toLowerCase().startsWith("bearer ")) {
15433
- return timingEqual(a.slice(7).trim(), expected);
15434
- }
15435
- return false;
15436
- }
15437
- function timingEqual(a, b) {
15438
- if (a.length !== b.length) return false;
15439
- let diff = 0;
15440
- for (let i = 0; i < a.length; i++) diff |= a.charCodeAt(i) ^ b.charCodeAt(i);
15441
- return diff === 0;
15442
- }
15443
- var POOL_API_KEY_INFO, RUNNER_API_KEY_INFO;
15444
- var init_keys = __esm({
15445
- "src/pool/keys.ts"() {
15446
- "use strict";
15447
- POOL_API_KEY_INFO = "kody-pool-api:v1";
15448
- RUNNER_API_KEY_INFO = "kody-runner-api:v1";
15449
- }
15450
- });
15451
-
15452
- // src/scripts/runtimeSecrets.ts
15453
- function envSecret(name, env) {
15454
- const value = env[name]?.trim() ? env[name] : "";
15455
- return value ? { value, source: "env" } : { value: "", source: "missing" };
15456
- }
15457
- async function resolveRuntimeSecret(name, ctx, opts = {}) {
15458
- const env = opts.env ?? process.env;
15459
- if (hasGitHubActionsIdentity(env)) {
15460
- try {
15461
- const value = await readRuntimeSecretFromKody(name, env);
15462
- if (value) return { value, source: "vault" };
15463
- const fallback = envSecret(name, env);
15464
- if (fallback.value) await writeRuntimeSecretToKody(name, fallback.value, env);
15465
- return fallback;
15466
- } catch (err) {
15467
- const fallback = envSecret(name, env);
15468
- return {
15469
- ...fallback,
15470
- warning: `Kody secret read failed for ${name}: ${err instanceof Error ? err.message : String(err)}`
15471
- };
15472
- }
15473
- }
15474
- const masterRaw = env.KODY_MASTER_KEY?.trim() ?? "";
15475
- if (!masterRaw || !env.CONVEX_URL?.trim() || !env.KODY_SERVICE_KEY?.trim()) return envSecret(name, env);
15476
- try {
15477
- const masterKey = masterKeyBytes(masterRaw);
15478
- if (masterKey.length !== 32) {
15479
- throw new Error("KODY_MASTER_KEY must decode to 32 bytes");
15480
- }
15481
- const value = await readRepoSecret({
15482
- owner: ctx.config.github.owner,
15483
- repo: ctx.config.github.repo,
15484
- name,
15485
- masterKey
15486
- });
15487
- if (value) return { value, source: "vault" };
15488
- } catch (err) {
15489
- const fallback = envSecret(name, env);
15490
- return {
15491
- ...fallback,
15492
- warning: `vault read failed for ${name}: ${err instanceof Error ? err.message : String(err)}`
15493
- };
15494
- }
15495
- return envSecret(name, env);
15496
- }
15497
- async function resolveRuntimeSecrets(names, ctx, opts = {}) {
15498
- const declared = Array.isArray(names) ? [...new Set(names.filter((name) => typeof name === "string" && /^[A-Z][A-Z0-9_]*$/.test(name)))] : [];
15499
- const resolved = [];
15500
- for (const name of declared) {
15501
- resolved.push({ name, result: await resolveRuntimeSecret(name, ctx, opts) });
15502
- }
15503
- return {
15504
- environment: Object.fromEntries(
15505
- resolved.flatMap(({ name, result }) => result.value ? [[name, result.value]] : [])
15506
- ),
15507
- warnings: resolved.flatMap(({ result }) => result.warning ? [result.warning] : [])
15508
- };
15509
- }
15510
- var init_runtimeSecrets = __esm({
15511
- "src/scripts/runtimeSecrets.ts"() {
15512
- "use strict";
15513
- init_backendVault();
15514
- init_kody_api_client();
15515
- init_keys();
15516
- }
15517
- });
15518
-
15519
15558
  // src/scripts/loadQaContext.ts
15520
15559
  import * as fs42 from "fs";
15521
15560
  import * as path39 from "path";
@@ -20991,7 +21030,10 @@ async function runImplementation(profileName, input) {
20991
21030
  capabilityContractInput(
20992
21031
  profile.inputs,
20993
21032
  args,
20994
- profile.canonicalContract.capabilityId
21033
+ profile.canonicalContract.capabilityId,
21034
+ Object.keys(
21035
+ profile.canonicalContract.inputSchema.properties ?? {}
21036
+ )
20995
21037
  )
20996
21038
  );
20997
21039
  }
@@ -21038,6 +21080,7 @@ async function runImplementation(profileName, input) {
21038
21080
  });
21039
21081
  }
21040
21082
  let litellm;
21083
+ let runtimeModelEnvironment;
21041
21084
  const ctx = {
21042
21085
  args,
21043
21086
  cwd: input.cwd,
@@ -21129,9 +21172,18 @@ async function runImplementation(profileName, input) {
21129
21172
  const syntheticPath = ctx.data.syntheticPluginPath;
21130
21173
  const pluginPaths = [...externalPlugins, ...syntheticPath ? [syntheticPath] : []];
21131
21174
  const agents = loadSubagents(profile);
21175
+ if (runtimeModelEnvironment === void 0) {
21176
+ runtimeModelEnvironment = {};
21177
+ if (needsLitellmProxy(model)) {
21178
+ const resolved2 = await resolveRuntimeModelEnvironment(model, ctx);
21179
+ runtimeModelEnvironment = resolved2.environment;
21180
+ for (const warning of resolved2.warnings) process.stderr.write(`\u26A0 ${warning}
21181
+ `);
21182
+ }
21183
+ }
21132
21184
  if (litellm === void 0) {
21133
21185
  try {
21134
- litellm = await startLitellmIfNeeded(model, input.cwd);
21186
+ litellm = await startLitellmIfNeeded(model, input.cwd, void 0, runtimeModelEnvironment);
21135
21187
  } catch (err) {
21136
21188
  throw new Error(`litellm startup failed: ${err instanceof Error ? err.message : String(err)}`);
21137
21189
  }
@@ -21141,7 +21193,10 @@ async function runImplementation(profileName, input) {
21141
21193
  prompt,
21142
21194
  model,
21143
21195
  cwd: input.cwd,
21144
- environment: ctx.data.capabilityEnvironment && typeof ctx.data.capabilityEnvironment === "object" && !Array.isArray(ctx.data.capabilityEnvironment) ? ctx.data.capabilityEnvironment : void 0,
21196
+ environment: {
21197
+ ...ctx.data.capabilityEnvironment && typeof ctx.data.capabilityEnvironment === "object" && !Array.isArray(ctx.data.capabilityEnvironment) ? ctx.data.capabilityEnvironment : {},
21198
+ ...runtimeModelEnvironment
21199
+ },
21145
21200
  litellmUrl: lm?.url ?? null,
21146
21201
  // On a connection drop mid-run, restart the (possibly crashed) proxy
21147
21202
  // before the agent retries. No-op for direct-Anthropic runs (lm null).
@@ -21833,7 +21888,6 @@ var MUTATING_POSTFLIGHTS, SHELL_MARKER_RE, TASK_ARTIFACT_WRITE_TOOLS, MAX_CHAIN_
21833
21888
  var init_executor = __esm({
21834
21889
  "src/executor.ts"() {
21835
21890
  "use strict";
21836
- init_capabilityExecutionEnvironment();
21837
21891
  init_capability_contract_validation();
21838
21892
  init_agent();
21839
21893
  init_agents();
@@ -21849,7 +21903,9 @@ var init_executor = __esm({
21849
21903
  init_registry();
21850
21904
  init_runIndex();
21851
21905
  init_runtimeCleanup();
21906
+ init_runtimeModelEnvironment();
21852
21907
  init_runtimePaths();
21908
+ init_capabilityExecutionEnvironment();
21853
21909
  init_evaluateAgencyBoundaries();
21854
21910
  init_scripts();
21855
21911
  init_stateWorkspace();
File without changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kody-ade/kody-engine",
3
- "version": "0.4.495",
3
+ "version": "0.4.497",
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",
@@ -12,29 +12,6 @@
12
12
  "templates",
13
13
  "kody.config.schema.json"
14
14
  ],
15
- "scripts": {
16
- "kody:run": "tsx bin/kody.ts",
17
- "serve": "tsx bin/kody.ts serve",
18
- "serve:vscode": "tsx bin/kody.ts serve vscode",
19
- "serve:claude": "tsx bin/kody.ts serve claude",
20
- "clean:dist": "node scripts/clean-dist.cjs",
21
- "build": "pnpm clean:dist && tsup && node scripts/copy-assets.cjs",
22
- "check:modularity": "tsx scripts/check-script-modularity.ts",
23
- "pretest": "pnpm check:modularity",
24
- "test": "vitest run tests/unit tests/int --coverage",
25
- "posttest": "tsx scripts/check-coverage-floor.ts",
26
- "test:smoke": "vitest run tests/smoke --no-coverage",
27
- "test:e2e": "vitest run tests/e2e --no-coverage",
28
- "test:runtime-services": "node --test \"tests/runtime-services/*.test.mjs\"",
29
- "test:all": "vitest run tests --no-coverage",
30
- "typecheck": "tsc --noEmit",
31
- "lint": "biome check",
32
- "lint:fix": "biome check --write",
33
- "format": "biome format --write",
34
- "verify:package": "node scripts/verify-package-tarball.cjs",
35
- "brain:publish": "docker buildx build --platform linux/amd64 -f runner/Dockerfile.brain --build-arg KODY_ENGINE_REF=$(git rev-parse HEAD) -t ghcr.io/${KODY_BRAIN_GHCR_OWNER:-aharonyaircohen}/kody-brain:latest --push runner",
36
- "prepublishOnly": "pnpm typecheck && vitest run tests/unit tests/int --no-coverage && pnpm test:runtime-services && pnpm build && pnpm verify:package"
37
- },
38
15
  "dependencies": {
39
16
  "@actions/cache": "^6.0.0",
40
17
  "@anthropic-ai/claude-agent-sdk": "0.2.119",
@@ -61,5 +38,27 @@
61
38
  "url": "git+https://github.com/aharonyaircohen/kody-engine.git"
62
39
  },
63
40
  "homepage": "https://github.com/aharonyaircohen/kody-engine",
64
- "bugs": "https://github.com/aharonyaircohen/kody-engine/issues"
65
- }
41
+ "bugs": "https://github.com/aharonyaircohen/kody-engine/issues",
42
+ "scripts": {
43
+ "kody:run": "tsx bin/kody.ts",
44
+ "serve": "tsx bin/kody.ts serve",
45
+ "serve:vscode": "tsx bin/kody.ts serve vscode",
46
+ "serve:claude": "tsx bin/kody.ts serve claude",
47
+ "clean:dist": "node scripts/clean-dist.cjs",
48
+ "build": "pnpm clean:dist && tsup && node scripts/copy-assets.cjs",
49
+ "check:modularity": "tsx scripts/check-script-modularity.ts",
50
+ "pretest": "pnpm check:modularity",
51
+ "test": "vitest run tests/unit tests/int --coverage",
52
+ "posttest": "tsx scripts/check-coverage-floor.ts",
53
+ "test:smoke": "vitest run tests/smoke --no-coverage",
54
+ "test:e2e": "vitest run tests/e2e --no-coverage",
55
+ "test:runtime-services": "node --test \"tests/runtime-services/*.test.mjs\"",
56
+ "test:all": "vitest run tests --no-coverage",
57
+ "typecheck": "tsc --noEmit",
58
+ "lint": "biome check",
59
+ "lint:fix": "biome check --write",
60
+ "format": "biome format --write",
61
+ "verify:package": "node scripts/verify-package-tarball.cjs",
62
+ "brain:publish": "docker buildx build --platform linux/amd64 -f runner/Dockerfile.brain --build-arg KODY_ENGINE_REF=$(git rev-parse HEAD) -t ghcr.io/${KODY_BRAIN_GHCR_OWNER:-aharonyaircohen}/kody-brain:latest --push runner"
63
+ }
64
+ }