@kody-ade/kody-engine 0.4.42 → 0.4.44
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 +85 -11
- package/dist/executables/goal-scheduler/scheduler.sh +0 -0
- package/dist/executables/plan/profile.json +3 -21
- package/dist/executables/release-deploy/deploy.sh +0 -0
- package/dist/executables/release-prepare/prepare.sh +0 -0
- package/dist/executables/release-publish/publish.sh +0 -0
- package/dist/executables/resolve/apply-prefer.sh +0 -0
- package/dist/executables/revert/revert.sh +0 -0
- package/dist/executables/review/profile.json +3 -21
- package/kody.config.schema.json +8 -0
- package/package.json +14 -15
package/dist/bin/kody.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// package.json
|
|
4
4
|
var package_default = {
|
|
5
5
|
name: "@kody-ade/kody-engine",
|
|
6
|
-
version: "0.4.
|
|
6
|
+
version: "0.4.44",
|
|
7
7
|
description: "kody \u2014 autonomous development engine. Single-session Claude Code agent behind a generic executor + declarative executable profiles.",
|
|
8
8
|
license: "MIT",
|
|
9
9
|
type: "module",
|
|
@@ -391,6 +391,16 @@ function formatBytes(bytes) {
|
|
|
391
391
|
|
|
392
392
|
// src/agent.ts
|
|
393
393
|
var DEFAULT_ALLOWED_TOOLS = ["Bash", "Edit", "Read", "Write", "Glob", "Grep"];
|
|
394
|
+
var DEFAULT_TURN_TIMEOUT_MS = 3e5;
|
|
395
|
+
function resolveTurnTimeoutMs(opts) {
|
|
396
|
+
if (opts.maxTurnTimeoutMs !== void 0 && opts.maxTurnTimeoutMs !== null) {
|
|
397
|
+
return opts.maxTurnTimeoutMs > 0 ? opts.maxTurnTimeoutMs : 0;
|
|
398
|
+
}
|
|
399
|
+
const envSec = Number(process.env.KODY_TURN_TIMEOUT_SEC);
|
|
400
|
+
if (Number.isFinite(envSec) && envSec > 0) return Math.floor(envSec * 1e3);
|
|
401
|
+
if (Number.isFinite(envSec) && envSec <= 0) return 0;
|
|
402
|
+
return DEFAULT_TURN_TIMEOUT_MS;
|
|
403
|
+
}
|
|
394
404
|
async function runAgent(opts) {
|
|
395
405
|
const ndjsonDir = opts.ndjsonDir ?? path3.join(opts.cwd, ".kody");
|
|
396
406
|
fs3.mkdirSync(ndjsonDir, { recursive: true });
|
|
@@ -420,6 +430,9 @@ async function runAgent(opts) {
|
|
|
420
430
|
const tokens = { input: 0, output: 0, cacheRead: 0, cacheCreate: 0 };
|
|
421
431
|
let messageCount = 0;
|
|
422
432
|
const startedAt = Date.now();
|
|
433
|
+
const turnTimeoutMs = resolveTurnTimeoutMs(opts);
|
|
434
|
+
let ndjsonWriteFailed = false;
|
|
435
|
+
let ndjsonWriteError;
|
|
423
436
|
try {
|
|
424
437
|
const queryOptions = {
|
|
425
438
|
model: opts.model.model,
|
|
@@ -456,12 +469,44 @@ async function runAgent(opts) {
|
|
|
456
469
|
// biome-ignore lint/suspicious/noExplicitAny: SDK options type is narrow; mcpServers is runtime-passthrough.
|
|
457
470
|
options: queryOptions
|
|
458
471
|
});
|
|
459
|
-
|
|
472
|
+
const iterator = typeof result[Symbol.asyncIterator] === "function" ? result[Symbol.asyncIterator]() : result;
|
|
473
|
+
while (true) {
|
|
474
|
+
const nextPromise = iterator.next();
|
|
475
|
+
let timedOut = false;
|
|
476
|
+
let timer;
|
|
477
|
+
let next;
|
|
478
|
+
if (turnTimeoutMs > 0) {
|
|
479
|
+
const timeoutPromise = new Promise((resolve4) => {
|
|
480
|
+
timer = setTimeout(() => {
|
|
481
|
+
timedOut = true;
|
|
482
|
+
resolve4({ done: true, value: void 0 });
|
|
483
|
+
}, turnTimeoutMs);
|
|
484
|
+
});
|
|
485
|
+
next = await Promise.race([nextPromise, timeoutPromise]);
|
|
486
|
+
if (timer) clearTimeout(timer);
|
|
487
|
+
} else {
|
|
488
|
+
next = await nextPromise;
|
|
489
|
+
}
|
|
490
|
+
if (timedOut) {
|
|
491
|
+
outcome = "failed";
|
|
492
|
+
errorMessage = `agent stalled: no SDK message in ${Math.round(turnTimeoutMs / 1e3)}s`;
|
|
493
|
+
if (typeof iterator.return === "function") {
|
|
494
|
+
try {
|
|
495
|
+
await iterator.return(void 0);
|
|
496
|
+
} catch {
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
break;
|
|
500
|
+
}
|
|
501
|
+
if (next.done) break;
|
|
502
|
+
const msg = next.value;
|
|
460
503
|
messageCount++;
|
|
461
504
|
try {
|
|
462
505
|
fullLog.write(`${JSON.stringify(msg)}
|
|
463
506
|
`);
|
|
464
|
-
} catch {
|
|
507
|
+
} catch (e) {
|
|
508
|
+
ndjsonWriteFailed = true;
|
|
509
|
+
ndjsonWriteError = e instanceof Error ? e.message : String(e);
|
|
465
510
|
}
|
|
466
511
|
const line = renderEvent(msg, { verbose: opts.verbose, quiet: opts.quiet });
|
|
467
512
|
if (line) process.stdout.write(`${line}
|
|
@@ -498,6 +543,10 @@ async function runAgent(opts) {
|
|
|
498
543
|
} catch {
|
|
499
544
|
}
|
|
500
545
|
}
|
|
546
|
+
if (ndjsonWriteFailed) {
|
|
547
|
+
process.stderr.write(`[kody agent] NDJSON write failed (post-mortem may be incomplete): ${ndjsonWriteError ?? "unknown error"}
|
|
548
|
+
`);
|
|
549
|
+
}
|
|
501
550
|
const finalText = resultTexts.join("\n\n---\n\n");
|
|
502
551
|
return {
|
|
503
552
|
outcome,
|
|
@@ -2018,6 +2067,13 @@ async function checkLitellmHealth(url) {
|
|
|
2018
2067
|
return false;
|
|
2019
2068
|
}
|
|
2020
2069
|
}
|
|
2070
|
+
var DEFAULT_LITELLM_STARTUP_TIMEOUT_SEC = 60;
|
|
2071
|
+
var LITELLM_HEALTH_POLL_INTERVAL_MS = 2e3;
|
|
2072
|
+
function resolveLitellmTimeoutMs() {
|
|
2073
|
+
const envSec = Number(process.env.KODY_LITELLM_TIMEOUT_SEC);
|
|
2074
|
+
if (Number.isFinite(envSec) && envSec > 0) return Math.floor(envSec * 1e3);
|
|
2075
|
+
return DEFAULT_LITELLM_STARTUP_TIMEOUT_SEC * 1e3;
|
|
2076
|
+
}
|
|
2021
2077
|
function generateLitellmConfigYaml(model) {
|
|
2022
2078
|
const apiKeyVar = providerApiKeyEnvVar(model.provider);
|
|
2023
2079
|
return [
|
|
@@ -2063,8 +2119,10 @@ async function startLitellmIfNeeded(model, projectDir, url = LITELLM_DEFAULT_URL
|
|
|
2063
2119
|
env: stripBlockingEnv({ ...process.env, ...dotenvVars })
|
|
2064
2120
|
});
|
|
2065
2121
|
fs10.closeSync(outFd);
|
|
2066
|
-
|
|
2067
|
-
|
|
2122
|
+
const timeoutMs = resolveLitellmTimeoutMs();
|
|
2123
|
+
const deadline = Date.now() + timeoutMs;
|
|
2124
|
+
while (Date.now() < deadline) {
|
|
2125
|
+
await new Promise((r) => setTimeout(r, LITELLM_HEALTH_POLL_INTERVAL_MS));
|
|
2068
2126
|
if (await checkLitellmHealth(url)) {
|
|
2069
2127
|
return {
|
|
2070
2128
|
url,
|
|
@@ -2086,7 +2144,8 @@ async function startLitellmIfNeeded(model, projectDir, url = LITELLM_DEFAULT_URL
|
|
|
2086
2144
|
child.kill();
|
|
2087
2145
|
} catch {
|
|
2088
2146
|
}
|
|
2089
|
-
|
|
2147
|
+
const seconds = Math.round(timeoutMs / 1e3);
|
|
2148
|
+
throw new Error(`LiteLLM proxy failed to start within ${seconds}s (KODY_LITELLM_TIMEOUT_SEC overrides). Log tail:
|
|
2090
2149
|
${logTail}`);
|
|
2091
2150
|
}
|
|
2092
2151
|
function readDotenvApiKeys(projectDir) {
|
|
@@ -9100,7 +9159,8 @@ async function runExecutable(profileName, input) {
|
|
|
9100
9159
|
return finishAndEnd({ exitCode: 99, reason: `config error: ${err instanceof Error ? err.message : String(err)}` });
|
|
9101
9160
|
}
|
|
9102
9161
|
}
|
|
9103
|
-
const
|
|
9162
|
+
const perExecutableModel = config.agent.perExecutable?.[profileName];
|
|
9163
|
+
const modelSpec = perExecutableModel ? perExecutableModel : profile.claudeCode.model === "inherit" ? config.agent.model : profile.claudeCode.model;
|
|
9104
9164
|
let model;
|
|
9105
9165
|
try {
|
|
9106
9166
|
model = parseProviderModel(modelSpec);
|
|
@@ -10126,8 +10186,8 @@ async function runScheduledFanOut(cwd, args, opts) {
|
|
|
10126
10186
|
return 99;
|
|
10127
10187
|
}
|
|
10128
10188
|
const config = loadConfig(cwd);
|
|
10129
|
-
|
|
10130
|
-
|
|
10189
|
+
const serial = process.env.KODY_SERIAL_WATCHES === "1";
|
|
10190
|
+
const runWatch = async (match) => {
|
|
10131
10191
|
process.stdout.write(`
|
|
10132
10192
|
\u2192 kody: running watch \`${match.executable}\`
|
|
10133
10193
|
`);
|
|
@@ -10144,13 +10204,27 @@ async function runScheduledFanOut(cwd, args, opts) {
|
|
|
10144
10204
|
`[kody] watch \`${match.executable}\` exited ${result.exitCode}: ${result.reason ?? "(no reason)"}
|
|
10145
10205
|
`
|
|
10146
10206
|
);
|
|
10147
|
-
|
|
10207
|
+
return result.exitCode;
|
|
10148
10208
|
}
|
|
10209
|
+
return 0;
|
|
10149
10210
|
} catch (err) {
|
|
10150
10211
|
const msg = err instanceof Error ? err.message : String(err);
|
|
10151
10212
|
process.stderr.write(`[kody] watch \`${match.executable}\` crashed: ${msg}
|
|
10152
10213
|
`);
|
|
10153
|
-
|
|
10214
|
+
return 99;
|
|
10215
|
+
}
|
|
10216
|
+
};
|
|
10217
|
+
let worstExit = 0;
|
|
10218
|
+
if (serial) {
|
|
10219
|
+
for (const match of matches) {
|
|
10220
|
+
const code = await runWatch(match);
|
|
10221
|
+
if (code > worstExit) worstExit = code;
|
|
10222
|
+
}
|
|
10223
|
+
} else {
|
|
10224
|
+
const settled = await Promise.allSettled(matches.map((m) => runWatch(m)));
|
|
10225
|
+
for (const r of settled) {
|
|
10226
|
+
const code = r.status === "fulfilled" ? r.value : 99;
|
|
10227
|
+
if (code > worstExit) worstExit = code;
|
|
10154
10228
|
}
|
|
10155
10229
|
}
|
|
10156
10230
|
return worstExit;
|
|
File without changes
|
|
@@ -20,34 +20,16 @@
|
|
|
20
20
|
"Read",
|
|
21
21
|
"Grep",
|
|
22
22
|
"Glob",
|
|
23
|
-
"Bash"
|
|
24
|
-
"mcp__playwright"
|
|
23
|
+
"Bash"
|
|
25
24
|
],
|
|
26
25
|
"hooks": ["block-write"],
|
|
27
26
|
"skills": [],
|
|
28
27
|
"commands": [],
|
|
29
28
|
"subagents": [],
|
|
30
29
|
"plugins": [],
|
|
31
|
-
"mcpServers": [
|
|
32
|
-
{
|
|
33
|
-
"name": "playwright",
|
|
34
|
-
"command": "npx",
|
|
35
|
-
"args": ["-y", "--package=@playwright/mcp@latest", "--", "playwright-mcp"]
|
|
36
|
-
}
|
|
37
|
-
]
|
|
30
|
+
"mcpServers": []
|
|
38
31
|
},
|
|
39
|
-
"cliTools": [
|
|
40
|
-
{
|
|
41
|
-
"name": "playwright",
|
|
42
|
-
"install": {
|
|
43
|
-
"required": false,
|
|
44
|
-
"checkCommand": "ls \"$HOME/.cache/ms-playwright\" 2>/dev/null | grep -q '^chromium'",
|
|
45
|
-
"installCommand": "npx --yes playwright install --with-deps chromium"
|
|
46
|
-
},
|
|
47
|
-
"verify": "ls \"$HOME/.cache/ms-playwright\" 2>/dev/null | grep -q '^chromium'",
|
|
48
|
-
"usage": ""
|
|
49
|
-
}
|
|
50
|
-
],
|
|
32
|
+
"cliTools": [],
|
|
51
33
|
"scripts": {
|
|
52
34
|
"preflight": [
|
|
53
35
|
{
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -21,34 +21,16 @@
|
|
|
21
21
|
"Read",
|
|
22
22
|
"Grep",
|
|
23
23
|
"Glob",
|
|
24
|
-
"Bash"
|
|
25
|
-
"mcp__playwright"
|
|
24
|
+
"Bash"
|
|
26
25
|
],
|
|
27
26
|
"hooks": ["block-write"],
|
|
28
27
|
"skills": [],
|
|
29
28
|
"commands": [],
|
|
30
29
|
"subagents": [],
|
|
31
30
|
"plugins": [],
|
|
32
|
-
"mcpServers": [
|
|
33
|
-
{
|
|
34
|
-
"name": "playwright",
|
|
35
|
-
"command": "npx",
|
|
36
|
-
"args": ["-y", "--package=@playwright/mcp@latest", "--", "playwright-mcp"]
|
|
37
|
-
}
|
|
38
|
-
]
|
|
31
|
+
"mcpServers": []
|
|
39
32
|
},
|
|
40
|
-
"cliTools": [
|
|
41
|
-
{
|
|
42
|
-
"name": "playwright",
|
|
43
|
-
"install": {
|
|
44
|
-
"required": false,
|
|
45
|
-
"checkCommand": "ls \"$HOME/.cache/ms-playwright\" 2>/dev/null | grep -q '^chromium'",
|
|
46
|
-
"installCommand": "npx --yes playwright install --with-deps chromium"
|
|
47
|
-
},
|
|
48
|
-
"verify": "ls \"$HOME/.cache/ms-playwright\" 2>/dev/null | grep -q '^chromium'",
|
|
49
|
-
"usage": ""
|
|
50
|
-
}
|
|
51
|
-
],
|
|
33
|
+
"cliTools": [],
|
|
52
34
|
"scripts": {
|
|
53
35
|
"preflight": [
|
|
54
36
|
{
|
package/kody.config.schema.json
CHANGED
|
@@ -135,6 +135,14 @@
|
|
|
135
135
|
"description": "Single 'provider/model' string used by kody (single-session pipeline). Use 'claude/...' or 'anthropic/...' for direct Anthropic API; anything else routes through LiteLLM proxy.",
|
|
136
136
|
"examples": ["claude/claude-sonnet-4-6", "minimax/MiniMax-M2.7-highspeed"]
|
|
137
137
|
},
|
|
138
|
+
"perExecutable": {
|
|
139
|
+
"type": "object",
|
|
140
|
+
"description": "Per-executable model override. Wins over agent.model for the matching stage. Example: {\"classify\": \"claude/claude-haiku-4-5-20251001\", \"plan\": \"claude/claude-opus-4-7\"}.",
|
|
141
|
+
"additionalProperties": {
|
|
142
|
+
"type": "string",
|
|
143
|
+
"pattern": "^[^/]+/.+$"
|
|
144
|
+
}
|
|
145
|
+
},
|
|
138
146
|
"modelMap": {
|
|
139
147
|
"type": "object",
|
|
140
148
|
"description": "Maps model tiers to 'provider/model' strings. Use 'claude/...' or 'anthropic/...' for direct Anthropic API; anything else routes through LiteLLM proxy.",
|
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.44",
|
|
4
4
|
"description": "kody — autonomous development engine. Single-session Claude Code agent behind a generic executor + declarative executable profiles.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -12,18 +12,6 @@
|
|
|
12
12
|
"templates",
|
|
13
13
|
"kody.config.schema.json"
|
|
14
14
|
],
|
|
15
|
-
"scripts": {
|
|
16
|
-
"kody": "tsx bin/kody.ts",
|
|
17
|
-
"build": "tsup && node scripts/copy-assets.cjs",
|
|
18
|
-
"test": "vitest run tests/unit tests/int --no-coverage",
|
|
19
|
-
"test:e2e": "vitest run tests/e2e --no-coverage",
|
|
20
|
-
"test:all": "vitest run tests --no-coverage",
|
|
21
|
-
"typecheck": "tsc --noEmit",
|
|
22
|
-
"lint": "biome check",
|
|
23
|
-
"lint:fix": "biome check --write",
|
|
24
|
-
"format": "biome format --write",
|
|
25
|
-
"prepublishOnly": "pnpm build"
|
|
26
|
-
},
|
|
27
15
|
"dependencies": {
|
|
28
16
|
"@actions/cache": "^6.0.0",
|
|
29
17
|
"@anthropic-ai/claude-agent-sdk": "0.2.119"
|
|
@@ -44,5 +32,16 @@
|
|
|
44
32
|
"url": "git+https://github.com/aharonyaircohen/kody-engine.git"
|
|
45
33
|
},
|
|
46
34
|
"homepage": "https://github.com/aharonyaircohen/kody-engine",
|
|
47
|
-
"bugs": "https://github.com/aharonyaircohen/kody-engine/issues"
|
|
48
|
-
|
|
35
|
+
"bugs": "https://github.com/aharonyaircohen/kody-engine/issues",
|
|
36
|
+
"scripts": {
|
|
37
|
+
"kody": "tsx bin/kody.ts",
|
|
38
|
+
"build": "tsup && node scripts/copy-assets.cjs",
|
|
39
|
+
"test": "vitest run tests/unit tests/int --no-coverage",
|
|
40
|
+
"test:e2e": "vitest run tests/e2e --no-coverage",
|
|
41
|
+
"test:all": "vitest run tests --no-coverage",
|
|
42
|
+
"typecheck": "tsc --noEmit",
|
|
43
|
+
"lint": "biome check",
|
|
44
|
+
"lint:fix": "biome check --write",
|
|
45
|
+
"format": "biome format --write"
|
|
46
|
+
}
|
|
47
|
+
}
|