@kody-ade/kody-engine 0.4.183 → 0.4.185

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
@@ -1309,7 +1309,7 @@ var init_loadPriorArt = __esm({
1309
1309
  // package.json
1310
1310
  var package_default = {
1311
1311
  name: "@kody-ade/kody-engine",
1312
- version: "0.4.183",
1312
+ version: "0.4.185",
1313
1313
  description: "kody \u2014 autonomous development engine. Single-session Claude Code agent behind a generic executor + declarative executable profiles.",
1314
1314
  license: "MIT",
1315
1315
  type: "module",
@@ -1348,7 +1348,6 @@ var package_default = {
1348
1348
  "@biomejs/biome": "^2.4.12",
1349
1349
  "@types/node": "^22.5.4",
1350
1350
  "@vitest/coverage-v8": "^4.1.4",
1351
- playwright: "^1.60.0",
1352
1351
  tsup: "^8.5.1",
1353
1352
  tsx: "^4.21.0",
1354
1353
  typescript: "~5.7.0",
@@ -1409,10 +1408,10 @@ var HttpSink = class {
1409
1408
  signal: AbortSignal.timeout(5e3)
1410
1409
  });
1411
1410
  if (!res.ok) {
1412
- this.logger.warn(`HttpSink POST ${url} \u2192 ${res.status}`);
1411
+ this.logger.warn(`HttpSink POST ${redactUrl(url)} \u2192 ${res.status}`);
1413
1412
  }
1414
1413
  } catch (err) {
1415
- this.logger.warn(`HttpSink POST ${url} failed: ${err instanceof Error ? err.message : String(err)}`);
1414
+ this.logger.warn(`HttpSink POST ${redactUrl(url)} failed: ${err instanceof Error ? err.message : String(err)}`);
1416
1415
  }
1417
1416
  }
1418
1417
  };
@@ -1425,6 +1424,9 @@ var TeeSink = class {
1425
1424
  await Promise.all(this.sinks.map((s) => s.emit(event)));
1426
1425
  }
1427
1426
  };
1427
+ function redactUrl(url) {
1428
+ return url.replace(/([?&]token=)[^&]+/gi, "$1***");
1429
+ }
1428
1430
  function withSessionParam(baseUrl, sessionId) {
1429
1431
  const joiner = baseUrl.includes("?") ? "&" : "?";
1430
1432
  return `${baseUrl}${joiner}sessionId=${encodeURIComponent(sessionId)}`;
@@ -1969,6 +1971,7 @@ async function runAgent(opts) {
1969
1971
  let ndjsonWriteFailed = false;
1970
1972
  let ndjsonWriteError;
1971
1973
  let sawMutatingTool = false;
1974
+ let sawTerminalSuccess = false;
1972
1975
  try {
1973
1976
  const queryOptions = {
1974
1977
  model: opts.model.model,
@@ -2180,6 +2183,7 @@ async function runAgent(opts) {
2180
2183
  if (m.subtype === "success") {
2181
2184
  outcome = "completed";
2182
2185
  outcomeKind = "ok";
2186
+ sawTerminalSuccess = true;
2183
2187
  const text = (typeof m.result === "string" ? m.result : "").trim();
2184
2188
  if (text) resultTexts.push(text);
2185
2189
  } else {
@@ -2190,9 +2194,13 @@ async function runAgent(opts) {
2190
2194
  }
2191
2195
  }
2192
2196
  } catch (e) {
2193
- outcome = "failed";
2194
- outcomeKind = "model_error";
2195
- errorMessage = e instanceof Error ? e.message : String(e);
2197
+ if (sawTerminalSuccess) {
2198
+ errorMessage = e instanceof Error ? e.message : String(e);
2199
+ } else {
2200
+ outcome = "failed";
2201
+ outcomeKind = "model_error";
2202
+ errorMessage = e instanceof Error ? e.message : String(e);
2203
+ }
2196
2204
  } finally {
2197
2205
  try {
2198
2206
  fullLog.end();
@@ -4533,7 +4541,7 @@ var FORBIDDEN_PATH_PREFIXES = [
4533
4541
  "build/"
4534
4542
  ];
4535
4543
  var ALLOWED_PATH_PREFIXES = [".kody/memory/", ".kody/tasks/"];
4536
- var FORBIDDEN_PATH_EXACT = /* @__PURE__ */ new Set([".env", ".kody-pip-requirements.txt"]);
4544
+ var FORBIDDEN_PATH_EXACT = /* @__PURE__ */ new Set([".env", ".kody-pip-requirements.txt", "kody.config.json"]);
4537
4545
  var FORBIDDEN_PATH_SUFFIXES = [".log"];
4538
4546
  var CONVENTIONAL_PREFIXES = [
4539
4547
  "feat:",
@@ -7546,13 +7554,33 @@ var ContentsApiBackend = class {
7546
7554
  };
7547
7555
  if (typeof loaded.handle === "string") payload.sha = loaded.handle;
7548
7556
  ensureStateBranch(this.owner, this.repo, this.cwd);
7549
- gh(["api", "--method", "PUT", `/repos/${this.owner}/${this.repo}/contents/${loaded.path}`, "--input", "-"], {
7557
+ try {
7558
+ this.put(loaded.path, payload);
7559
+ } catch (err) {
7560
+ if (!isShaConflict(err)) throw err;
7561
+ const current = this.load(slug);
7562
+ if (!current.created && isStateUnchanged(current.state, next)) return false;
7563
+ if (typeof current.handle === "string") payload.sha = current.handle;
7564
+ else delete payload.sha;
7565
+ process.stderr.write(
7566
+ `[kody] jobState: concurrent write detected for ${slug}; reloaded SHA and retrying (last-write-wins)
7567
+ `
7568
+ );
7569
+ this.put(loaded.path, payload);
7570
+ }
7571
+ return true;
7572
+ }
7573
+ put(filePath, payload) {
7574
+ gh(["api", "--method", "PUT", `/repos/${this.owner}/${this.repo}/contents/${filePath}`, "--input", "-"], {
7550
7575
  cwd: this.cwd,
7551
7576
  input: JSON.stringify(payload)
7552
7577
  });
7553
- return true;
7554
7578
  }
7555
7579
  };
7580
+ function isShaConflict(err) {
7581
+ const msg = err instanceof Error ? err.message : String(err);
7582
+ return /HTTP 409/i.test(msg) || /HTTP 422/i.test(msg) || /does not match|is at|but expected/i.test(msg);
7583
+ }
7556
7584
 
7557
7585
  // src/scripts/jobState/localFileBackend.ts
7558
7586
  import * as fs28 from "fs";
@@ -7655,7 +7683,9 @@ var LocalFileBackend = class {
7655
7683
  const absPath = path27.join(this.cwd, loaded.path);
7656
7684
  fs28.mkdirSync(path27.dirname(absPath), { recursive: true });
7657
7685
  const body = JSON.stringify(next, null, 2) + "\n";
7658
- fs28.writeFileSync(absPath, body, "utf-8");
7686
+ const tmpPath = `${absPath}.${process.pid}.tmp`;
7687
+ fs28.writeFileSync(tmpPath, body, "utf-8");
7688
+ fs28.renameSync(tmpPath, absPath);
7659
7689
  return true;
7660
7690
  }
7661
7691
  cacheKeyPrefix() {
@@ -12858,8 +12888,6 @@ var saveTaskState = async (ctx, profile) => {
12858
12888
  if (!target || !number || !state) return;
12859
12889
  const executable = profile.name;
12860
12890
  const action = ctx.data.action ?? synthesizeAction(ctx);
12861
- if (ctx.output.prUrl && !state.core.prUrl) state.core.prUrl = ctx.output.prUrl;
12862
- if (typeof ctx.data.runUrl === "string") state.core.runUrl = ctx.data.runUrl;
12863
12891
  const next = reduce(state, executable, action, profile.phase);
12864
12892
  if (ctx.output.prUrl) next.core.prUrl = ctx.output.prUrl;
12865
12893
  if (typeof ctx.data.runUrl === "string") next.core.runUrl = ctx.data.runUrl;
@@ -29,6 +29,16 @@
29
29
  "verify": "gh auth status",
30
30
  "usage": "",
31
31
  "allowedUses": ["api", "issue", "pr"]
32
+ },
33
+ {
34
+ "name": "python3",
35
+ "install": {
36
+ "required": true,
37
+ "checkCommand": "command -v python3"
38
+ },
39
+ "verify": "python3 --version",
40
+ "usage": "scheduler.sh parses each goal's state.json to read its 'state' field",
41
+ "allowedUses": []
32
42
  }
33
43
  ],
34
44
  "inputArtifacts": [],
@@ -11,6 +11,15 @@ set -euo pipefail
11
11
 
12
12
  goals_dir=".kody/goals"
13
13
 
14
+ # python3 parses each goal's state.json below. It is declared as a required
15
+ # cliTool in profile.json, but guard here too: without this check a missing
16
+ # interpreter silently makes EVERY goal read state="" → treated as inactive →
17
+ # nothing ticks, reported as a misleading success. Fail loud instead.
18
+ if ! command -v python3 >/dev/null 2>&1; then
19
+ echo "[goal-scheduler] FATAL: python3 not found on PATH (required to read goal state)" >&2
20
+ exit 1
21
+ fi
22
+
14
23
  # Goal state lives on the dedicated `kody-state` branch, not the default branch
15
24
  # (keeps `chore(goals): …` churn out of code history). Materialize it into the
16
25
  # working tree so the glob below sees current state. Best-effort: `kody-state`
File without changes
File without changes
File without changes
File without changes
File without changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kody-ade/kody-engine",
3
- "version": "0.4.183",
3
+ "version": "0.4.185",
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,24 +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
- "build": "tsup && node scripts/copy-assets.cjs",
21
- "check:modularity": "tsx scripts/check-script-modularity.ts",
22
- "pretest": "pnpm check:modularity",
23
- "test": "vitest run tests/unit tests/int --coverage",
24
- "test:smoke": "vitest run tests/smoke --no-coverage",
25
- "test:e2e": "vitest run tests/e2e --no-coverage",
26
- "test:all": "vitest run tests --no-coverage",
27
- "typecheck": "tsc --noEmit",
28
- "lint": "biome check",
29
- "lint:fix": "biome check --write",
30
- "format": "biome format --write",
31
- "prepublishOnly": "pnpm build"
32
- },
33
15
  "dependencies": {
34
16
  "@actions/cache": "^6.0.0",
35
17
  "@anthropic-ai/claude-agent-sdk": "0.2.119",
@@ -39,7 +21,6 @@
39
21
  "@biomejs/biome": "^2.4.12",
40
22
  "@types/node": "^22.5.4",
41
23
  "@vitest/coverage-v8": "^4.1.4",
42
- "playwright": "^1.60.0",
43
24
  "tsup": "^8.5.1",
44
25
  "tsx": "^4.21.0",
45
26
  "typescript": "~5.7.0",
@@ -53,5 +34,22 @@
53
34
  "url": "git+https://github.com/aharonyaircohen/kody-engine.git"
54
35
  },
55
36
  "homepage": "https://github.com/aharonyaircohen/kody-engine",
56
- "bugs": "https://github.com/aharonyaircohen/kody-engine/issues"
57
- }
37
+ "bugs": "https://github.com/aharonyaircohen/kody-engine/issues",
38
+ "scripts": {
39
+ "kody:run": "tsx bin/kody.ts",
40
+ "serve": "tsx bin/kody.ts serve",
41
+ "serve:vscode": "tsx bin/kody.ts serve vscode",
42
+ "serve:claude": "tsx bin/kody.ts serve claude",
43
+ "build": "tsup && node scripts/copy-assets.cjs",
44
+ "check:modularity": "tsx scripts/check-script-modularity.ts",
45
+ "pretest": "pnpm check:modularity",
46
+ "test": "vitest run tests/unit tests/int --coverage",
47
+ "test:smoke": "vitest run tests/smoke --no-coverage",
48
+ "test:e2e": "vitest run tests/e2e --no-coverage",
49
+ "test:all": "vitest run tests --no-coverage",
50
+ "typecheck": "tsc --noEmit",
51
+ "lint": "biome check",
52
+ "lint:fix": "biome check --write",
53
+ "format": "biome format --write"
54
+ }
55
+ }