@kody-ade/kody-engine 0.4.566 → 0.4.568

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.566",
18
+ version: "0.4.568",
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",
@@ -1143,14 +1143,23 @@ function buildVerifyEnv(source = process.env) {
1143
1143
  env.CI = source.CI ?? "1";
1144
1144
  return env;
1145
1145
  }
1146
- function runCommand(command, cwd) {
1146
+ function abortMessage(signal) {
1147
+ const reason = signal.reason;
1148
+ return reason instanceof Error ? reason.message : typeof reason === "string" ? reason : "verification aborted";
1149
+ }
1150
+ function runCommand(command, cwd, signal) {
1147
1151
  return new Promise((resolve23) => {
1148
1152
  const start = Date.now();
1153
+ if (signal?.aborted) {
1154
+ resolve23({ exitCode: -1, durationMs: 0, tail: abortMessage(signal) });
1155
+ return;
1156
+ }
1149
1157
  const child = spawn(command, {
1150
1158
  cwd,
1151
1159
  shell: true,
1152
1160
  env: buildVerifyEnv(),
1153
- stdio: ["ignore", "pipe", "pipe"]
1161
+ stdio: ["ignore", "pipe", "pipe"],
1162
+ detached: process.platform !== "win32"
1154
1163
  });
1155
1164
  const buffers = [];
1156
1165
  let totalSize = 0;
@@ -1164,24 +1173,46 @@ function runCommand(command, cwd) {
1164
1173
  };
1165
1174
  child.stdout?.on("data", collect);
1166
1175
  child.stderr?.on("data", collect);
1176
+ let settled = false;
1177
+ const killTree = (killSignal) => {
1178
+ try {
1179
+ if (process.platform !== "win32" && child.pid) process.kill(-child.pid, killSignal);
1180
+ else child.kill(killSignal);
1181
+ } catch {
1182
+ child.kill(killSignal);
1183
+ }
1184
+ };
1185
+ const finish = (exitCode, extraTail = "") => {
1186
+ if (settled) return;
1187
+ settled = true;
1188
+ clearTimeout(timer);
1189
+ signal?.removeEventListener("abort", onAbort);
1190
+ const output = Buffer.concat(buffers).toString("utf-8");
1191
+ const tail = [output, extraTail].filter(Boolean).join("\n").slice(-TAIL_CHARS);
1192
+ resolve23({ exitCode, durationMs: Date.now() - start, tail });
1193
+ };
1194
+ const terminate = () => {
1195
+ killTree("SIGTERM");
1196
+ setTimeout(() => killTree("SIGKILL"), 5e3).unref();
1197
+ };
1198
+ const onAbort = () => {
1199
+ terminate();
1200
+ finish(-1, signal ? abortMessage(signal) : "verification aborted");
1201
+ };
1202
+ signal?.addEventListener("abort", onAbort, { once: true });
1167
1203
  const timer = setTimeout(() => {
1168
- child.kill("SIGTERM");
1169
- setTimeout(() => {
1170
- if (!child.killed) child.kill("SIGKILL");
1171
- }, 5e3);
1204
+ terminate();
1205
+ finish(-1, "verification command timed out");
1172
1206
  }, COMMAND_TIMEOUT_MS);
1173
1207
  child.on("exit", (code) => {
1174
- clearTimeout(timer);
1175
- const tail = Buffer.concat(buffers).toString("utf-8").slice(-TAIL_CHARS);
1176
- resolve23({ exitCode: code ?? -1, durationMs: Date.now() - start, tail });
1208
+ finish(code ?? -1);
1177
1209
  });
1178
1210
  child.on("error", (err) => {
1179
- clearTimeout(timer);
1180
- resolve23({ exitCode: -1, durationMs: Date.now() - start, tail: err.message });
1211
+ finish(-1, err.message);
1181
1212
  });
1182
1213
  });
1183
1214
  }
1184
- async function verifyAll(config, cwd) {
1215
+ async function verifyAll(config, cwd, opts) {
1185
1216
  const commands = [];
1186
1217
  if (config.quality.typecheck) commands.push({ name: "typecheck", cmd: config.quality.typecheck });
1187
1218
  if (config.quality.testUnit) commands.push({ name: "test", cmd: config.quality.testUnit });
@@ -1190,20 +1221,21 @@ async function verifyAll(config, cwd) {
1190
1221
  const failed = [];
1191
1222
  const details = {};
1192
1223
  for (const { name, cmd } of commands) {
1193
- const result = await runCommand(cmd, cwd);
1224
+ const result = await runCommand(cmd, cwd, opts?.signal);
1194
1225
  details[name] = result;
1195
1226
  if (result.exitCode !== 0) failed.push(name);
1196
1227
  }
1197
1228
  return { ok: failed.length === 0, failed, details };
1198
1229
  }
1199
- async function applyTestRetries(initial, testCommand, cwd, runner, testRetries = DEFAULT_TEST_RETRIES) {
1230
+ async function applyTestRetries(initial, testCommand, cwd, runner, testRetries = DEFAULT_TEST_RETRIES, signal) {
1200
1231
  if (initial.ok) return { ...initial, recovered: [] };
1201
1232
  const recovered = [];
1202
1233
  const details = { ...initial.details };
1203
1234
  let failed = [...initial.failed];
1204
1235
  if (failed.includes("test") && testCommand && testRetries > 0) {
1205
1236
  for (let attempt = 1; attempt <= testRetries; attempt++) {
1206
- const retry = await runner(testCommand, cwd);
1237
+ if (signal?.aborted) break;
1238
+ const retry = await runner(testCommand, cwd, signal);
1207
1239
  details[`test (retry ${attempt})`] = retry;
1208
1240
  if (retry.exitCode === 0) {
1209
1241
  failed = failed.filter((f) => f !== "test");
@@ -1215,8 +1247,8 @@ async function applyTestRetries(initial, testCommand, cwd, runner, testRetries =
1215
1247
  return { ok: failed.length === 0, failed, details, recovered };
1216
1248
  }
1217
1249
  async function verifyAllWithRetry(config, cwd, opts) {
1218
- const initial = await verifyAll(config, cwd);
1219
- return applyTestRetries(initial, config.quality.testUnit, cwd, runCommand, opts?.testRetries);
1250
+ const initial = await verifyAll(config, cwd, { signal: opts?.signal });
1251
+ return applyTestRetries(initial, config.quality.testUnit, cwd, runCommand, opts?.testRetries, opts?.signal);
1220
1252
  }
1221
1253
  function stripAnsi(s) {
1222
1254
  return s.replace(ANSI_RE, "");
@@ -20889,7 +20921,7 @@ var init_verifyReproFails = __esm({
20889
20921
  // src/scripts/verifyWithRetry.ts
20890
20922
  async function runVerify(ctx) {
20891
20923
  try {
20892
- const result = await verifyAllWithRetry(ctx.config, ctx.cwd);
20924
+ const result = await verifyAllWithRetry(ctx.config, ctx.cwd, { signal: ctx.abortSignal });
20893
20925
  ctx.data.verifyOk = result.ok;
20894
20926
  ctx.data.verifyReason = result.ok ? "" : summarizeFailure(result);
20895
20927
  ctx.data.verifyRecovered = result.recovered ?? [];
@@ -20934,6 +20966,10 @@ var init_verifyWithRetry = __esm({
20934
20966
  verifyWithRetry = async (ctx) => {
20935
20967
  await runVerify(ctx);
20936
20968
  if (ctx.data.verifyOk !== false) return;
20969
+ if (ctx.abortSignal?.aborted) {
20970
+ downgradeActionOnFailure(ctx);
20971
+ return;
20972
+ }
20937
20973
  if (!ctx.data.agentDone) {
20938
20974
  downgradeActionOnFailure(ctx);
20939
20975
  return;
@@ -20970,6 +21006,10 @@ var init_verifyWithRetry = __esm({
20970
21006
  process.stderr.write(`[kody] verify retry crashed: ${err instanceof Error ? err.message : String(err)}
20971
21007
  `);
20972
21008
  }
21009
+ if (ctx.abortSignal?.aborted) {
21010
+ downgradeActionOnFailure(ctx);
21011
+ return;
21012
+ }
20973
21013
  await runVerify(ctx);
20974
21014
  if (ctx.data.verifyOk === true) {
20975
21015
  upgradeActionOnPass(ctx);
@@ -21973,6 +22013,7 @@ async function runImplementation(profileName, input) {
21973
22013
  config,
21974
22014
  verbose: input.verbose,
21975
22015
  quiet: input.quiet,
22016
+ abortSignal: input.abortController?.signal,
21976
22017
  // Phase 5 foundation: seed ctx.data with any preloaded values handed
21977
22018
  // in by a parent (typically a container loop). Loaders that see
21978
22019
  // their field already populated take the fast path and skip the
@@ -22054,6 +22095,10 @@ async function runImplementation(profileName, input) {
22054
22095
  const jobWhyBlock = typeof ctx.data.jobWhy === "string" ? operatorRequestBlock(ctx.data.jobWhy) : null;
22055
22096
  const jobRefBlock = jobReferenceBlock(profileName, profile, ctx.data);
22056
22097
  const invokeAgent = async (prompt) => {
22098
+ if (input.abortController?.signal.aborted) {
22099
+ const reason = input.abortController.signal.reason;
22100
+ throw reason instanceof Error ? reason : new Error("agent invocation aborted");
22101
+ }
22057
22102
  const externalPlugins = (profile.claudeCode.plugins ?? []).map((p) => path51.isAbsolute(p) ? p : path51.resolve(profile.dir, p)).filter((p) => p.length > 0);
22058
22103
  const syntheticPath = ctx.data.syntheticPluginPath;
22059
22104
  const pluginPaths = [...externalPlugins, ...syntheticPath ? [syntheticPath] : []];
@@ -22340,6 +22385,7 @@ async function runImplementation(profileName, input) {
22340
22385
  exitCode: ctx.output.exitCode ?? 0,
22341
22386
  prUrl: ctx.output.prUrl,
22342
22387
  reason: ctx.output.reason,
22388
+ action: ctx.data.action,
22343
22389
  nextDispatch: ctx.output.nextDispatch,
22344
22390
  nextJob: ctx.output.nextJob,
22345
22391
  afterNextJob: ctx.output.afterNextJob,
@@ -23937,7 +23983,7 @@ function workflowStepAbortController(parent, timeoutSeconds) {
23937
23983
  };
23938
23984
  }
23939
23985
  function workflowOutcome(result) {
23940
- return result.taskState?.core.lastOutcome ?? null;
23986
+ return result.taskState?.core.lastOutcome ?? result.action ?? null;
23941
23987
  }
23942
23988
  function workflowConditionContext(data) {
23943
23989
  const lastOutcome = data.workflowLastOutcome;
@@ -456,6 +456,8 @@ export interface Context {
456
456
  /** Stream-output verbosity. */
457
457
  verbose?: boolean
458
458
  quiet?: boolean
459
+ /** Cancellation owned by the enclosing job or workflow step. */
460
+ abortSignal?: AbortSignal
459
461
  /** Opaque bag scripts populate during preflight (issue, pr, diff, logs, …). */
460
462
  data: Record<string, unknown>
461
463
  /** Final output the executor returns. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kody-ade/kody-engine",
3
- "version": "0.4.566",
3
+ "version": "0.4.568",
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",