@kody-ade/kody-engine 0.4.463 → 0.4.465

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.463",
18
+ version: "0.4.465",
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",
@@ -1688,6 +1688,9 @@ function readCapabilityFolder(root, slug) {
1688
1688
  try {
1689
1689
  const rawBody = fs4.readFileSync(bodyPath, "utf-8");
1690
1690
  const contract = fs4.existsSync(contractPath) ? parseCapabilityContract(fs4.readFileSync(contractPath, "utf-8")) : void 0;
1691
+ if (contract?.execution === "script" && !isRegularFile(path5.join(dir, "tools", "run.sh"))) {
1692
+ throw new Error('script-backed Capability requires a regular "tools/run.sh" file');
1693
+ }
1691
1694
  const { title, body } = parseCapabilityBody(rawBody, slug);
1692
1695
  return {
1693
1696
  slug,
@@ -1701,12 +1704,17 @@ function readCapabilityFolder(root, slug) {
1701
1704
  config: {
1702
1705
  action: slug,
1703
1706
  describe: title,
1707
+ ...contract?.execution ? { execution: contract.execution } : {},
1704
1708
  ...contract ? {
1705
1709
  inputSchema: contract.input,
1706
1710
  outputSchema: contract.output
1707
1711
  } : {}
1708
1712
  },
1709
- rawProfile: contract ? { input: contract.input, output: contract.output } : {},
1713
+ rawProfile: contract ? {
1714
+ ...contract.execution ? { execution: contract.execution } : {},
1715
+ input: contract.input,
1716
+ output: contract.output
1717
+ } : {},
1710
1718
  ...contract ? { contract } : {}
1711
1719
  };
1712
1720
  } catch {
@@ -1718,11 +1726,28 @@ function parseCapabilityContract(raw) {
1718
1726
  if (!isPlainObject(parsed) || !isPlainObject(parsed.input) || !isPlainObject(parsed.output)) {
1719
1727
  throw new Error("contract.json must contain input and output JSON schemas");
1720
1728
  }
1721
- const unsupported = Object.keys(parsed).filter((key) => key !== "input" && key !== "output");
1729
+ if (parsed.execution !== void 0 && parsed.execution !== "agent" && parsed.execution !== "script") {
1730
+ throw new Error('contract.json execution must be "agent" or "script"');
1731
+ }
1732
+ const unsupported = Object.keys(parsed).filter(
1733
+ (key) => key !== "execution" && key !== "input" && key !== "output"
1734
+ );
1722
1735
  if (unsupported.length > 0) {
1723
1736
  throw new Error(`contract.json contains unsupported fields: ${unsupported.join(", ")}`);
1724
1737
  }
1725
- return { input: parsed.input, output: parsed.output };
1738
+ return {
1739
+ ...parsed.execution ? { execution: parsed.execution } : {},
1740
+ input: parsed.input,
1741
+ output: parsed.output
1742
+ };
1743
+ }
1744
+ function isRegularFile(filePath) {
1745
+ try {
1746
+ const stat = fs4.lstatSync(filePath);
1747
+ return stat.isFile() && !stat.isSymbolicLink();
1748
+ } catch {
1749
+ return false;
1750
+ }
1726
1751
  }
1727
1752
  function schemaPropertyPaths(schema, prefix) {
1728
1753
  const properties = isPlainObject(schema.properties) ? schema.properties : {};
@@ -11469,11 +11494,13 @@ var init_classifyByLabel = __esm({
11469
11494
  });
11470
11495
 
11471
11496
  // src/scripts/commitAndPush.ts
11497
+ import { createHash as createHash4 } from "crypto";
11472
11498
  import * as fs31 from "fs";
11473
11499
  import * as path29 from "path";
11474
- function sentinelPathForStage(cwd, profileName) {
11500
+ function sentinelPathForStage(cwd, profileName, workflowExecutionKey) {
11475
11501
  const runId = resolveRunId();
11476
- return runtimeStatePath(cwd, "agent-runs", runId, `commit-${profileName}.lock`);
11502
+ const executionSuffix = typeof workflowExecutionKey === "string" && workflowExecutionKey.length > 0 ? `-${createHash4("sha256").update(workflowExecutionKey).digest("hex").slice(0, 16)}` : "";
11503
+ return runtimeStatePath(cwd, "agent-runs", runId, `commit-${profileName}${executionSuffix}.lock`);
11477
11504
  }
11478
11505
  var DEFAULT_COMMIT_MESSAGE, commitAndPush2;
11479
11506
  var init_commitAndPush = __esm({
@@ -11490,7 +11517,7 @@ var init_commitAndPush = __esm({
11490
11517
  return;
11491
11518
  }
11492
11519
  const idempotencyEnabled = process.env.KODY_COMMIT_IDEMPOTENCY !== "0";
11493
- const sentinel = idempotencyEnabled ? sentinelPathForStage(ctx.cwd, profile.name) : null;
11520
+ const sentinel = idempotencyEnabled ? sentinelPathForStage(ctx.cwd, profile.name, ctx.data.workflowExecutionKey) : null;
11494
11521
  if (sentinel && fs31.existsSync(sentinel)) {
11495
11522
  try {
11496
11523
  const replay = JSON.parse(fs31.readFileSync(sentinel, "utf-8"));
@@ -13287,7 +13314,7 @@ var init_triggerDispatcher = __esm({
13287
13314
  });
13288
13315
 
13289
13316
  // src/goal/policyResolver.ts
13290
- import { createHash as createHash4 } from "crypto";
13317
+ import { createHash as createHash5 } from "crypto";
13291
13318
  function resolveDispatchPolicy(input) {
13292
13319
  const operation = input.catalog.operations.get(input.owner.definition.operationId);
13293
13320
  if (!operation) throw new Error(`Dispatch blocked: Operation "${input.owner.definition.operationId}" is unresolved`);
@@ -13305,7 +13332,7 @@ function resolveDispatchPolicy(input) {
13305
13332
  const snapshotValue = { policy, constraints };
13306
13333
  return {
13307
13334
  snapshot: {
13308
- hash: createHash4("sha256").update(stableJson(snapshotValue)).digest("hex"),
13335
+ hash: createHash5("sha256").update(stableJson(snapshotValue)).digest("hex"),
13309
13336
  ...snapshotValue
13310
13337
  },
13311
13338
  operation,
@@ -15919,9 +15946,9 @@ var init_kodyVariables = __esm({
15919
15946
  });
15920
15947
 
15921
15948
  // src/backendVault.ts
15922
- import { createDecipheriv, createHash as createHash5 } from "crypto";
15949
+ import { createDecipheriv, createHash as createHash6 } from "crypto";
15923
15950
  function cacheKey(owner, repo, masterKey) {
15924
- const keyHash = createHash5("sha256").update(masterKey).digest("hex").slice(0, 16);
15951
+ const keyHash = createHash6("sha256").update(masterKey).digest("hex").slice(0, 16);
15925
15952
  return `${owner}/${repo}:${keyHash}`.toLowerCase();
15926
15953
  }
15927
15954
  function decryptVault(payload, masterKey) {
@@ -16247,6 +16274,10 @@ var init_loadSimpleCapability = __esm({
16247
16274
  }
16248
16275
  ctx.data.jobCapability = slug;
16249
16276
  ctx.data.capabilityInput = input;
16277
+ ctx.data.capabilityExecution = capability.contract?.execution ?? "agent";
16278
+ if (capability.contract?.execution === "script") {
16279
+ ctx.data.capabilityScriptPath = path40.join(capability.dir, "tools", "run.sh");
16280
+ }
16250
16281
  if (capability.config.outputSchema) {
16251
16282
  ctx.data.capabilityOutputSchema = capability.config.outputSchema;
16252
16283
  }
@@ -16704,7 +16735,7 @@ var init_notifyTerminal = __esm({
16704
16735
  });
16705
16736
 
16706
16737
  // src/scripts/openAgencyModelReviewPr.ts
16707
- import { createHash as createHash6 } from "crypto";
16738
+ import { createHash as createHash7 } from "crypto";
16708
16739
  function parseAgencyModelProposal(raw) {
16709
16740
  const text2 = raw.trim();
16710
16741
  const jsonText = (text2.match(/^```(?:json)?\s*\n([\s\S]*?)\n```$/i)?.[1] ?? text2).trim();
@@ -16756,7 +16787,7 @@ function normalizeBundleFiles(bundle) {
16756
16787
  });
16757
16788
  }
16758
16789
  function buildProposalId(issueNumber, bundle, sourceLabel) {
16759
- const digest = createHash6("sha256").update(JSON.stringify({ issueNumber, sourceLabel, title: bundle.title, files: normalizeBundleFiles(bundle) })).digest("hex").slice(0, 16);
16790
+ const digest = createHash7("sha256").update(JSON.stringify({ issueNumber, sourceLabel, title: bundle.title, files: normalizeBundleFiles(bundle) })).digest("hex").slice(0, 16);
16760
16791
  return `issue-${issueNumber}-${digest}`;
16761
16792
  }
16762
16793
  function isDryRun(ctx) {
@@ -17272,7 +17303,7 @@ var init_parseSimpleCapabilityOutput = __esm({
17272
17303
  "use strict";
17273
17304
  init_capability_contract_validation();
17274
17305
  parseSimpleCapabilityOutput = async (ctx, _profile, agentResult) => {
17275
- const output = parseOutput(agentResult?.finalText);
17306
+ const output = Object.hasOwn(ctx.data, "capabilityScriptOutput") ? ctx.data.capabilityScriptOutput : parseOutput(agentResult?.finalText);
17276
17307
  if (output === void 0) {
17277
17308
  const reason2 = agentResult?.outcomeKind === "out_of_turns" ? "Capability execution limit reached" : "Capability execution ended before returning a result";
17278
17309
  const blocked = {
@@ -19086,9 +19117,9 @@ var init_reviewFlow = __esm({
19086
19117
  });
19087
19118
 
19088
19119
  // src/scripts/previewBuildHelpers.ts
19089
- import { createDecipheriv as createDecipheriv2, createHash as createHash7, hkdfSync as hkdfSync2 } from "crypto";
19120
+ import { createDecipheriv as createDecipheriv2, createHash as createHash8, hkdfSync as hkdfSync2 } from "crypto";
19090
19121
  function shortHash(s) {
19091
- return createHash7("sha256").update(s).digest("hex").slice(0, 6);
19122
+ return createHash8("sha256").update(s).digest("hex").slice(0, 6);
19092
19123
  }
19093
19124
  function previewAppName(repo, pr) {
19094
19125
  const [owner, name] = repo.split("/");
@@ -19121,7 +19152,7 @@ function formatPreviewComment(args) {
19121
19152
  ].join("\n");
19122
19153
  }
19123
19154
  function defaultImageTag(repo, ref) {
19124
- return createHash7("sha256").update(`${repo}@${ref}`).digest("hex").slice(0, 12);
19155
+ return createHash8("sha256").update(`${repo}@${ref}`).digest("hex").slice(0, 12);
19125
19156
  }
19126
19157
  var init_previewBuildHelpers = __esm({
19127
19158
  "src/scripts/previewBuildHelpers.ts"() {
@@ -19766,6 +19797,74 @@ var init_runTickScript = __esm({
19766
19797
  }
19767
19798
  });
19768
19799
 
19800
+ // src/scripts/runSimpleCapabilityScript.ts
19801
+ import { spawnSync as spawnSync3 } from "child_process";
19802
+ import * as fs48 from "fs";
19803
+ function isRegularFile2(filePath) {
19804
+ try {
19805
+ const stat = fs48.lstatSync(filePath);
19806
+ return stat.isFile() && !stat.isSymbolicLink();
19807
+ } catch {
19808
+ return false;
19809
+ }
19810
+ }
19811
+ function isStringRecord(value) {
19812
+ return value !== null && typeof value === "object" && !Array.isArray(value) && Object.values(value).every((entry) => typeof entry === "string");
19813
+ }
19814
+ var SCRIPT_TIMEOUT_MS, SCRIPT_MAX_OUTPUT_BYTES, runSimpleCapabilityScript;
19815
+ var init_runSimpleCapabilityScript = __esm({
19816
+ "src/scripts/runSimpleCapabilityScript.ts"() {
19817
+ "use strict";
19818
+ init_tickShellRunner();
19819
+ SCRIPT_TIMEOUT_MS = 5 * 60 * 1e3;
19820
+ SCRIPT_MAX_OUTPUT_BYTES = 1024 * 1024;
19821
+ runSimpleCapabilityScript = async (ctx) => {
19822
+ ctx.skipAgent = true;
19823
+ const scriptPath = typeof ctx.data.capabilityScriptPath === "string" ? ctx.data.capabilityScriptPath : "";
19824
+ if (!scriptPath || !isRegularFile2(scriptPath)) {
19825
+ ctx.output.exitCode = 99;
19826
+ ctx.output.reason = 'Script-backed Capability requires a regular "tools/run.sh" entrypoint';
19827
+ return;
19828
+ }
19829
+ const capabilityEnvironment2 = isStringRecord(ctx.data.capabilityEnvironment) ? ctx.data.capabilityEnvironment : {};
19830
+ const result = spawnSync3("bash", [scriptPath], {
19831
+ cwd: ctx.cwd,
19832
+ env: {
19833
+ ...buildTickChildEnv(process.env, false),
19834
+ ...capabilityEnvironment2
19835
+ },
19836
+ stdio: ["ignore", "pipe", "pipe"],
19837
+ encoding: "utf-8",
19838
+ timeout: SCRIPT_TIMEOUT_MS,
19839
+ maxBuffer: SCRIPT_MAX_OUTPUT_BYTES
19840
+ });
19841
+ if (result.stderr) process.stderr.write(result.stderr);
19842
+ if (result.error) {
19843
+ const timedOut = result.error.code === "ETIMEDOUT";
19844
+ ctx.output.exitCode = timedOut ? 124 : 99;
19845
+ ctx.output.reason = timedOut ? "Capability script timed out after 5 minutes" : `Capability script failed to start: ${result.error.message}`;
19846
+ return;
19847
+ }
19848
+ if (result.signal) {
19849
+ ctx.output.exitCode = 124;
19850
+ ctx.output.reason = `Capability script was killed by ${result.signal}`;
19851
+ return;
19852
+ }
19853
+ if (result.status !== 0) {
19854
+ ctx.output.exitCode = result.status ?? 99;
19855
+ ctx.output.reason = `Capability script exited ${result.status ?? 99}`;
19856
+ return;
19857
+ }
19858
+ try {
19859
+ ctx.data.capabilityScriptOutput = JSON.parse(result.stdout ?? "");
19860
+ } catch {
19861
+ ctx.output.exitCode = 64;
19862
+ ctx.output.reason = "Capability script must return exactly one valid JSON value on stdout";
19863
+ }
19864
+ };
19865
+ }
19866
+ });
19867
+
19769
19868
  // src/scripts/saveManagedGoalState.ts
19770
19869
  var saveManagedGoalState;
19771
19870
  var init_saveManagedGoalState = __esm({
@@ -20781,7 +20880,7 @@ var init_warmupMcp = __esm({
20781
20880
  });
20782
20881
 
20783
20882
  // src/scripts/writeAgentRunSummary.ts
20784
- import * as fs48 from "fs";
20883
+ import * as fs49 from "fs";
20785
20884
  var writeAgentRunSummary;
20786
20885
  var init_writeAgentRunSummary = __esm({
20787
20886
  "src/scripts/writeAgentRunSummary.ts"() {
@@ -20807,7 +20906,7 @@ var init_writeAgentRunSummary = __esm({
20807
20906
  if (reason) lines.push(`- **Reason:** ${reason}`);
20808
20907
  lines.push("");
20809
20908
  try {
20810
- fs48.appendFileSync(summaryPath, `${lines.join("\n")}
20909
+ fs49.appendFileSync(summaryPath, `${lines.join("\n")}
20811
20910
  `);
20812
20911
  } catch {
20813
20912
  }
@@ -21010,6 +21109,7 @@ var init_scripts = __esm({
21010
21109
  init_runPreviewBuild();
21011
21110
  init_runScheduledImplementationTick();
21012
21111
  init_runTickScript();
21112
+ init_runSimpleCapabilityScript();
21013
21113
  init_saveManagedGoalState();
21014
21114
  init_saveTaskState();
21015
21115
  init_setCommentTarget();
@@ -21078,6 +21178,7 @@ var init_scripts = __esm({
21078
21178
  runScheduledImplementationTick,
21079
21179
  runScheduledExecutableTick: runScheduledImplementationTick,
21080
21180
  runTickScript,
21181
+ runSimpleCapabilityScript,
21081
21182
  runPreviewBuild,
21082
21183
  advanceManagedGoal,
21083
21184
  loadGoalState,
@@ -21143,7 +21244,7 @@ var init_scripts = __esm({
21143
21244
  });
21144
21245
 
21145
21246
  // src/stateWorkspace.ts
21146
- import * as fs49 from "fs";
21247
+ import * as fs50 from "fs";
21147
21248
  import * as path47 from "path";
21148
21249
  function tenantId(config) {
21149
21250
  const owner = config.github?.owner?.trim() || process.env.GITHUB_REPOSITORY?.split("/")[0]?.trim();
@@ -21152,8 +21253,8 @@ function tenantId(config) {
21152
21253
  }
21153
21254
  function writeRuntimeFile(cwd, relativePath, content) {
21154
21255
  const target = path47.join(cwd, RUNTIME_ROOT, relativePath);
21155
- fs49.mkdirSync(path47.dirname(target), { recursive: true });
21156
- fs49.writeFileSync(target, content, "utf8");
21256
+ fs50.mkdirSync(path47.dirname(target), { recursive: true });
21257
+ fs50.writeFileSync(target, content, "utf8");
21157
21258
  }
21158
21259
  function record(value) {
21159
21260
  return value && typeof value === "object" && !Array.isArray(value) ? value : null;
@@ -21222,7 +21323,7 @@ async function hydrateStateWorkspace(config, cwd, backendOverride) {
21222
21323
  if (hydratedWorkspaces.has(key)) return;
21223
21324
  const backend = backendOverride ?? createStateBackendFromEnv();
21224
21325
  const root = path47.join(cwd, RUNTIME_ROOT);
21225
- fs49.rmSync(root, { recursive: true, force: true });
21326
+ fs50.rmSync(root, { recursive: true, force: true });
21226
21327
  await Promise.all([
21227
21328
  hydratePrefix(backend, tenant, cwd, "context:"),
21228
21329
  hydratePrefix(backend, tenant, cwd, "memory:"),
@@ -21309,7 +21410,7 @@ var init_tools = __esm({
21309
21410
 
21310
21411
  // src/executor.ts
21311
21412
  import { spawn as spawn8 } from "child_process";
21312
- import * as fs50 from "fs";
21413
+ import * as fs51 from "fs";
21313
21414
  import * as os7 from "os";
21314
21415
  import * as path48 from "path";
21315
21416
  function isMutatingPostflight(scriptName) {
@@ -22027,7 +22128,7 @@ function resolveProfilePath(profileName, cwd = process.cwd()) {
22027
22128
  // fallback
22028
22129
  ];
22029
22130
  for (const c of candidates) {
22030
- if (fs50.existsSync(c)) return c;
22131
+ if (fs51.existsSync(c)) return c;
22031
22132
  }
22032
22133
  return candidates[0];
22033
22134
  }
@@ -22143,7 +22244,7 @@ function resolveShellTimeoutMs(entry) {
22143
22244
  async function runShellEntry(entry, ctx, profile) {
22144
22245
  const shellName = entry.shell;
22145
22246
  const shellPath = path48.join(profile.dir, shellName);
22146
- if (!fs50.existsSync(shellPath)) {
22247
+ if (!fs51.existsSync(shellPath)) {
22147
22248
  ctx.skipAgent = true;
22148
22249
  ctx.output.exitCode = 99;
22149
22250
  ctx.output.reason = `shell script not found: ${shellName} (looked in ${profile.dir})`;
@@ -22222,9 +22323,9 @@ async function runShellEntry(entry, ctx, profile) {
22222
22323
  }
22223
22324
  let sideChannelText = "";
22224
22325
  try {
22225
- if (fs50.existsSync(outputFile)) {
22226
- sideChannelText = fs50.readFileSync(outputFile, "utf-8");
22227
- fs50.rmSync(outputFile, { force: true });
22326
+ if (fs51.existsSync(outputFile)) {
22327
+ sideChannelText = fs51.readFileSync(outputFile, "utf-8");
22328
+ fs51.rmSync(outputFile, { force: true });
22228
22329
  }
22229
22330
  } catch {
22230
22331
  }
@@ -22717,6 +22818,7 @@ async function runLinearCapabilityWorkflow(parent, workflow, capability, base) {
22717
22818
  runSubjectLabel: label,
22718
22819
  workflowStep: label,
22719
22820
  workflowStepIndex: index + 1,
22821
+ workflowExecutionKey: [capability.slug, label, index + 1].join(":"),
22720
22822
  workflowStepReason: step.reason,
22721
22823
  workflowContinueOn: step.continueOn ?? []
22722
22824
  }
@@ -22853,6 +22955,12 @@ async function runGraphCapabilityWorkflow(parent, workflow, capability, base, ch
22853
22955
  runSubjectLabel: step.id,
22854
22956
  workflowStep: step.id,
22855
22957
  workflowStepIndex: index + 1,
22958
+ workflowExecutionKey: graphWorkflowExecutionKey(
22959
+ base.preloadedData?.workflowExecutionKey,
22960
+ capability.slug,
22961
+ step.id,
22962
+ state.transitionCounts
22963
+ ),
22856
22964
  workflowStepReason: step.reason,
22857
22965
  workflowContinueOn: step.continueOn ?? []
22858
22966
  }
@@ -22919,6 +23027,15 @@ async function runGraphCapabilityWorkflow(parent, workflow, capability, base, ch
22919
23027
  await checkpoint?.(state);
22920
23028
  return withWorkflowBoundaryEval(capability, { ...result, workflowState: state });
22921
23029
  }
23030
+ function graphWorkflowExecutionKey(parentExecutionKey, workflowSlug, stepId, transitionCounts) {
23031
+ const transitionHistory = Object.entries(transitionCounts).filter(([, count]) => count > 0).sort(([left], [right]) => left.localeCompare(right)).map(([transition, count]) => `${transition}=${count}`).join(",");
23032
+ return [
23033
+ typeof parentExecutionKey === "string" && parentExecutionKey.length > 0 ? parentExecutionKey : void 0,
23034
+ workflowSlug,
23035
+ stepId,
23036
+ transitionHistory || "initial"
23037
+ ].filter((part) => part !== void 0).join(":");
23038
+ }
22922
23039
  function mergeWorkflowResults(state, results) {
22923
23040
  for (const result of results ?? []) {
22924
23041
  Object.assign(state.facts, result.facts);
@@ -24478,7 +24595,7 @@ async function hydrateDefinitionsFromEnv(cwd = process.cwd(), env = process.env)
24478
24595
 
24479
24596
  // src/kody-cli.ts
24480
24597
  import { execFileSync as execFileSync24 } from "child_process";
24481
- import * as fs51 from "fs";
24598
+ import * as fs52 from "fs";
24482
24599
  import * as path49 from "path";
24483
24600
 
24484
24601
  // src/app-auth.ts
@@ -25298,9 +25415,9 @@ async function resolveAuthToken(env = process.env) {
25298
25415
  return void 0;
25299
25416
  }
25300
25417
  function detectPackageManager2(cwd) {
25301
- if (fs51.existsSync(path49.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
25302
- if (fs51.existsSync(path49.join(cwd, "yarn.lock"))) return "yarn";
25303
- if (fs51.existsSync(path49.join(cwd, "bun.lockb"))) return "bun";
25418
+ if (fs52.existsSync(path49.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
25419
+ if (fs52.existsSync(path49.join(cwd, "yarn.lock"))) return "yarn";
25420
+ if (fs52.existsSync(path49.join(cwd, "bun.lockb"))) return "bun";
25304
25421
  return "npm";
25305
25422
  }
25306
25423
  function shouldChainScheduledWatch(match) {
@@ -25404,8 +25521,8 @@ function postFailureTail(issueNumber, cwd, reason) {
25404
25521
  const logPath = lastRunLogPath(cwd);
25405
25522
  let tail = "";
25406
25523
  try {
25407
- if (fs51.existsSync(logPath)) {
25408
- const content = fs51.readFileSync(logPath, "utf-8");
25524
+ if (fs52.existsSync(logPath)) {
25525
+ const content = fs52.readFileSync(logPath, "utf-8");
25409
25526
  tail = content.slice(-3e3);
25410
25527
  }
25411
25528
  } catch {
@@ -25493,9 +25610,9 @@ async function runCi(argv) {
25493
25610
  forceRunCliArgs = { goal: envForceMessage };
25494
25611
  }
25495
25612
  }
25496
- if (!args.issueNumber && !autoFallback && !forceRunAction && !runRequestFanOut && eventName === "workflow_dispatch" && dispatchEventPath && fs51.existsSync(dispatchEventPath)) {
25613
+ if (!args.issueNumber && !autoFallback && !forceRunAction && !runRequestFanOut && eventName === "workflow_dispatch" && dispatchEventPath && fs52.existsSync(dispatchEventPath)) {
25497
25614
  try {
25498
- const evt = JSON.parse(fs51.readFileSync(dispatchEventPath, "utf-8"));
25615
+ const evt = JSON.parse(fs52.readFileSync(dispatchEventPath, "utf-8"));
25499
25616
  const inputs = objectValue2(evt.inputs);
25500
25617
  const issueInput = parseInt(String(inputs?.issue_number ?? ""), 10);
25501
25618
  const sessionInput = String(inputs?.sessionId ?? "");
@@ -25903,7 +26020,7 @@ init_repoWorkspace();
25903
26020
 
25904
26021
  // src/scripts/brainTurnLog.ts
25905
26022
  init_runtimePaths();
25906
- import * as fs52 from "fs";
26023
+ import * as fs53 from "fs";
25907
26024
  import * as path50 from "path";
25908
26025
  import posixPath4 from "path/posix";
25909
26026
  var live = /* @__PURE__ */ new Map();
@@ -25912,8 +26029,8 @@ function brainEventsFilePath(dir, chatId) {
25912
26029
  }
25913
26030
  function lastPersistedSeq(dir, chatId) {
25914
26031
  const p = brainEventsFilePath(dir, chatId);
25915
- if (!fs52.existsSync(p)) return 0;
25916
- const lines = fs52.readFileSync(p, "utf-8").split("\n").filter(Boolean);
26032
+ if (!fs53.existsSync(p)) return 0;
26033
+ const lines = fs53.readFileSync(p, "utf-8").split("\n").filter(Boolean);
25917
26034
  if (lines.length === 0) return 0;
25918
26035
  try {
25919
26036
  return JSON.parse(lines[lines.length - 1]).seq || 0;
@@ -25923,9 +26040,9 @@ function lastPersistedSeq(dir, chatId) {
25923
26040
  }
25924
26041
  function readSince(dir, chatId, since) {
25925
26042
  const p = brainEventsFilePath(dir, chatId);
25926
- if (!fs52.existsSync(p)) return [];
26043
+ if (!fs53.existsSync(p)) return [];
25927
26044
  const out = [];
25928
- for (const line of fs52.readFileSync(p, "utf-8").split("\n")) {
26045
+ for (const line of fs53.readFileSync(p, "utf-8").split("\n")) {
25929
26046
  if (!line) continue;
25930
26047
  try {
25931
26048
  const rec = JSON.parse(line);
@@ -25951,12 +26068,12 @@ function beginTurn(dir, chatId) {
25951
26068
  };
25952
26069
  live.set(chatId, state);
25953
26070
  const p = brainEventsFilePath(dir, chatId);
25954
- fs52.mkdirSync(path50.dirname(p), { recursive: true });
26071
+ fs53.mkdirSync(path50.dirname(p), { recursive: true });
25955
26072
  return (event) => {
25956
26073
  state.seq += 1;
25957
26074
  const rec = { seq: state.seq, turn, ts: Date.now(), event };
25958
26075
  try {
25959
- fs52.appendFileSync(p, `${JSON.stringify(rec)}
26076
+ fs53.appendFileSync(p, `${JSON.stringify(rec)}
25960
26077
  `);
25961
26078
  } catch (err) {
25962
26079
  process.stderr.write(
@@ -25995,7 +26112,7 @@ function endTurnIfUnterminated(dir, chatId, errMessage) {
25995
26112
  event: { type: "error", error: errMessage || "turn ended unexpectedly", chatId }
25996
26113
  };
25997
26114
  try {
25998
- fs52.appendFileSync(brainEventsFilePath(dir, chatId), `${JSON.stringify(rec)}
26115
+ fs53.appendFileSync(brainEventsFilePath(dir, chatId), `${JSON.stringify(rec)}
25999
26116
  `);
26000
26117
  } catch {
26001
26118
  }
@@ -28228,7 +28345,7 @@ async function poolServe() {
28228
28345
 
28229
28346
  // src/servers/runner-serve.ts
28230
28347
  import { spawn as spawn9 } from "child_process";
28231
- import * as fs53 from "fs";
28348
+ import * as fs54 from "fs";
28232
28349
  import { createServer as createServer6 } from "http";
28233
28350
  var DEFAULT_PORT2 = 8080;
28234
28351
  var DEFAULT_WORKDIR = "/workspace/repo";
@@ -28363,8 +28480,8 @@ async function defaultRunJob(job) {
28363
28480
  const workdir = process.env.RUNNER_WORKDIR ?? DEFAULT_WORKDIR;
28364
28481
  const branch = job.ref ?? "main";
28365
28482
  const authUrl = `https://x-access-token:${job.githubToken}@github.com/${job.repo}.git`;
28366
- fs53.rmSync(workdir, { recursive: true, force: true });
28367
- fs53.mkdirSync(workdir, { recursive: true });
28483
+ fs54.rmSync(workdir, { recursive: true, force: true });
28484
+ fs54.mkdirSync(workdir, { recursive: true });
28368
28485
  const allSecrets = typeof job.allSecrets === "string" ? job.allSecrets : JSON.stringify(job.allSecrets ?? {});
28369
28486
  const target = job.runRequest.target;
28370
28487
  const interactive = target.type === "chat";
@@ -74,7 +74,11 @@
74
74
  "scripts": {
75
75
  "preflight": [
76
76
  { "script": "loadSimpleCapability" },
77
- { "script": "prepareCapabilityDelivery" }
77
+ { "script": "prepareCapabilityDelivery" },
78
+ {
79
+ "script": "runSimpleCapabilityScript",
80
+ "runWhen": { "data.capabilityExecution": "script" }
81
+ }
78
82
  ],
79
83
  "postflight": [
80
84
  { "script": "parseSimpleCapabilityOutput" }
@@ -36,7 +36,13 @@
36
36
  },
37
37
  "cliTools": [],
38
38
  "scripts": {
39
- "preflight": [{ "script": "loadSimpleCapability" }],
39
+ "preflight": [
40
+ { "script": "loadSimpleCapability" },
41
+ {
42
+ "script": "runSimpleCapabilityScript",
43
+ "runWhen": { "data.capabilityExecution": "script" }
44
+ }
45
+ ],
40
46
  "postflight": [{ "script": "parseSimpleCapabilityOutput" }]
41
47
  },
42
48
  "inputArtifacts": [],
File without changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kody-ade/kody-engine",
3
- "version": "0.4.463",
3
+ "version": "0.4.465",
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
+ }