@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 +287 -231
- package/dist/runtime-services/goal-scheduler/scheduler.sh +0 -0
- package/package.json +25 -26
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.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
|
|
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({
|
|
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
|
-
|
|
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({
|
|
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
|
|
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 ? `-${
|
|
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:
|
|
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.
|
|
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
|
+
}
|