@kody-ade/kody-engine 0.4.560 → 0.4.562

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.
Files changed (2) hide show
  1. package/dist/bin/kody.js +99 -16
  2. package/package.json +1 -1
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.560",
18
+ version: "0.4.562",
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",
@@ -671,11 +671,22 @@ var init_format = __esm({
671
671
 
672
672
  // src/agency/capability-contract-validation.ts
673
673
  import Ajv from "ajv";
674
- function validateCapabilityContractValue(boundary, schema, value) {
675
- const validate = validator.compile(schema);
676
- if (!validate(value)) {
677
- throw new CapabilityContractValidationError(boundary, validate.errors ?? []);
678
- }
674
+ function createCapabilityContractValueValidator(compile) {
675
+ const compiled = /* @__PURE__ */ new WeakMap();
676
+ return (boundary, schema, value) => {
677
+ let validate = compiled.get(schema);
678
+ if (!validate) {
679
+ validate = compile(schema);
680
+ compiled.set(schema, validate);
681
+ }
682
+ if (!validate(value)) {
683
+ throw new CapabilityContractValidationError(boundary, validate.errors ?? []);
684
+ }
685
+ };
686
+ }
687
+ function validateCapabilityContractOutput(schema, value) {
688
+ if (value === void 0) throw new CapabilityContractMissingOutputError();
689
+ validateCapabilityContractValue("output", schema, value);
679
690
  }
680
691
  function capabilityContractInput(inputs, args, capabilityId, contractProperties = []) {
681
692
  const isGenericRunnerInput = inputs.some((input) => input.name === "input") && Object.hasOwn(args, "input");
@@ -693,7 +704,7 @@ function capabilityContractInput(inputs, args, capabilityId, contractProperties
693
704
  return value;
694
705
  }
695
706
  }
696
- var validator, CapabilityContractValidationError;
707
+ var validator, CapabilityContractValidationError, CapabilityContractMissingOutputError, validateCapabilityContractValue;
697
708
  var init_capability_contract_validation = __esm({
698
709
  "src/agency/capability-contract-validation.ts"() {
699
710
  "use strict";
@@ -717,6 +728,15 @@ var init_capability_contract_validation = __esm({
717
728
  boundary;
718
729
  errors;
719
730
  };
731
+ CapabilityContractMissingOutputError = class extends Error {
732
+ constructor() {
733
+ super("Capability output is missing despite its declared contract");
734
+ this.name = "CapabilityContractMissingOutputError";
735
+ }
736
+ };
737
+ validateCapabilityContractValue = createCapabilityContractValueValidator(
738
+ (schema) => validator.compile(schema)
739
+ );
720
740
  }
721
741
  });
722
742
 
@@ -731,7 +751,7 @@ function outputContractError(contract) {
731
751
  return `The required output file is missing or is not valid JSON: ${error instanceof Error ? error.message : String(error)}`;
732
752
  }
733
753
  try {
734
- validateCapabilityContractValue("output", contract.schema, value);
754
+ validateCapabilityContractOutput(contract.schema, value);
735
755
  return null;
736
756
  } catch (error) {
737
757
  return error instanceof Error ? error.message : String(error);
@@ -2012,20 +2032,52 @@ function listCapabilityFolderSlugs(absDir) {
2012
2032
  return entries.filter((e) => e.isDirectory() && !e.name.startsWith("_") && !e.name.startsWith(".")).filter((e) => isCapabilityFolder(path8.join(absDir, e.name))).map((e) => e.name).sort();
2013
2033
  }
2014
2034
  function isCapabilityFolder(dir) {
2015
- if (!fs7.existsSync(path8.join(dir, CAPABILITY_BODY_FILE))) return false;
2016
2035
  const entries = fs7.readdirSync(dir, { withFileTypes: true });
2036
+ const legacyBody = path8.join(dir, CAPABILITY_BODY_FILE);
2037
+ if (fs7.existsSync(legacyBody)) {
2038
+ return entries.every(
2039
+ (entry) => entry.name === CAPABILITY_BODY_FILE || entry.name === CAPABILITY_CONTRACT_FILE || entry.isDirectory() && (entry.name === "skills" || entry.name === "tools")
2040
+ );
2041
+ }
2042
+ const canonicalBody = path8.join(dir, CANONICAL_CAPABILITY_BODY_FILE);
2043
+ const canonicalDefinition = path8.join(dir, CANONICAL_CAPABILITY_DEFINITION_FILE);
2044
+ if (!fs7.existsSync(canonicalBody) || !fs7.existsSync(canonicalDefinition)) return false;
2017
2045
  return entries.every(
2018
- (entry) => entry.name === CAPABILITY_BODY_FILE || entry.name === CAPABILITY_CONTRACT_FILE || entry.isDirectory() && (entry.name === "skills" || entry.name === "tools")
2046
+ (entry) => entry.name === CANONICAL_CAPABILITY_BODY_FILE || entry.name === CANONICAL_CAPABILITY_DEFINITION_FILE
2019
2047
  );
2020
2048
  }
2021
2049
  function readCapabilityFolder(root, slug) {
2022
2050
  const dir = path8.join(root, slug);
2023
- const bodyPath = path8.join(dir, CAPABILITY_BODY_FILE);
2051
+ const legacyBodyPath = path8.join(dir, CAPABILITY_BODY_FILE);
2052
+ const canonicalBodyPath = path8.join(dir, CANONICAL_CAPABILITY_BODY_FILE);
2053
+ const bodyPath = fs7.existsSync(legacyBodyPath) ? legacyBodyPath : canonicalBodyPath;
2024
2054
  const contractPath = path8.join(dir, CAPABILITY_CONTRACT_FILE);
2025
2055
  if (!fs7.existsSync(bodyPath) || !fs7.statSync(bodyPath).isFile()) return null;
2026
2056
  if (!isCapabilityFolder(dir)) return null;
2027
2057
  try {
2028
2058
  const rawBody = fs7.readFileSync(bodyPath, "utf-8");
2059
+ if (bodyPath === canonicalBodyPath) {
2060
+ const definitionPath = path8.join(dir, CANONICAL_CAPABILITY_DEFINITION_FILE);
2061
+ const definition = JSON.parse(fs7.readFileSync(definitionPath, "utf-8"));
2062
+ if (definition.id !== slug || typeof definition.action !== "string") return null;
2063
+ const { title: title2, body: body2 } = parseCapabilityBody(rawBody, slug);
2064
+ return {
2065
+ slug,
2066
+ dir,
2067
+ profilePath: definitionPath,
2068
+ bodyPath,
2069
+ title: title2,
2070
+ body: body2,
2071
+ rawBody,
2072
+ config: {
2073
+ action: definition.action,
2074
+ describe: typeof definition.purpose === "string" ? definition.purpose : title2,
2075
+ ...isPlainObject(definition.inputSchema) ? { inputSchema: definition.inputSchema } : {},
2076
+ ...isPlainObject(definition.outputSchema) ? { outputSchema: definition.outputSchema } : {}
2077
+ },
2078
+ rawProfile: definition
2079
+ };
2080
+ }
2029
2081
  const contract = fs7.existsSync(contractPath) ? parseCapabilityContract(fs7.readFileSync(contractPath, "utf-8")) : void 0;
2030
2082
  if (contract?.execution === "script" && !isRegularFile(path8.join(dir, "tools", "run.sh"))) {
2031
2083
  throw new Error('script-backed Capability requires a regular "tools/run.sh" file');
@@ -2301,13 +2353,15 @@ function isSafeSlug(value) {
2301
2353
  function isSafeStepId(value) {
2302
2354
  return /^[A-Za-z][A-Za-z0-9_-]*$/.test(value) && !value.includes("..");
2303
2355
  }
2304
- var CAPABILITY_BODY_FILE, CAPABILITY_CONTRACT_FILE, CAPABILITY_PROFILE_FILE;
2356
+ var CAPABILITY_BODY_FILE, CAPABILITY_CONTRACT_FILE, CAPABILITY_PROFILE_FILE, CANONICAL_CAPABILITY_BODY_FILE, CANONICAL_CAPABILITY_DEFINITION_FILE;
2305
2357
  var init_capabilityFolders = __esm({
2306
2358
  "src/capabilityFolders.ts"() {
2307
2359
  "use strict";
2308
2360
  CAPABILITY_BODY_FILE = "instructions.md";
2309
2361
  CAPABILITY_CONTRACT_FILE = "contract.json";
2310
2362
  CAPABILITY_PROFILE_FILE = CAPABILITY_BODY_FILE;
2363
+ CANONICAL_CAPABILITY_BODY_FILE = "capability.md";
2364
+ CANONICAL_CAPABILITY_DEFINITION_FILE = "definition.json";
2311
2365
  }
2312
2366
  });
2313
2367
 
@@ -2498,6 +2552,7 @@ function resolveCapabilityFolder(slug, projectCapabilitiesRoot = getProjectCapab
2498
2552
  function getCapabilityActionInputs(action, projectCapabilitiesRoot = getProjectCapabilitiesRoot()) {
2499
2553
  const resolved = resolveCapabilityAction(action, projectCapabilitiesRoot);
2500
2554
  if (!resolved) return null;
2555
+ if (resolved.source === "builtin") return getProfileInputs(resolved.implementation);
2501
2556
  const capability = resolveCapabilityFolder(resolved.capability, projectCapabilitiesRoot);
2502
2557
  if (capability && !capability.config.workflow) {
2503
2558
  return [
@@ -17251,7 +17306,7 @@ var init_parseSimpleCapabilityOutput = __esm({
17251
17306
  const outputSchema = isObject2(ctx.data.capabilityOutputSchema) ? ctx.data.capabilityOutputSchema : void 0;
17252
17307
  if (outputSchema) {
17253
17308
  try {
17254
- validateCapabilityContractValue("output", outputSchema, output);
17309
+ validateCapabilityContractOutput(outputSchema, output);
17255
17310
  } catch (error) {
17256
17311
  const reason2 = error instanceof Error ? error.message : String(error);
17257
17312
  ctx.output.exitCode = 64;
@@ -22221,8 +22276,7 @@ async function runImplementation(profileName, input) {
22221
22276
  const capabilityResults = Array.isArray(ctx.data.capabilityResults) ? ctx.data.capabilityResults : void 0;
22222
22277
  if (profile.canonicalContract) {
22223
22278
  try {
22224
- validateCapabilityContractValue(
22225
- "output",
22279
+ validateCapabilityContractOutput(
22226
22280
  profile.canonicalContract.outputSchema,
22227
22281
  capabilityResults?.at(-1) ?? {
22228
22282
  exitCode: ctx.output.exitCode ?? 0,
@@ -23192,7 +23246,35 @@ async function runCapabilityImplementationStep(valid, profileName, capabilityIde
23192
23246
  input.cliArgs = simpleCapabilityRuntimeArgs(simpleCapabilityRuntime, capabilityIdentity, capabilityInput);
23193
23247
  }
23194
23248
  const run = base.chain === false ? runImplementation : runImplementationChain;
23195
- return run(profileName, input);
23249
+ const result = await run(profileName, input);
23250
+ return enforceCapabilityOutputContract(capabilityContext, result);
23251
+ }
23252
+ function enforceCapabilityOutputContract(capability, result) {
23253
+ const schema = capability?.config.outputSchema;
23254
+ if (result.exitCode !== 0 || !schema) {
23255
+ return result;
23256
+ }
23257
+ try {
23258
+ validateCapabilityContractOutput(schema, result.capabilityOutput);
23259
+ return result;
23260
+ } catch (error) {
23261
+ const reason = error instanceof Error ? error.message : String(error);
23262
+ const blocked = {
23263
+ version: 1,
23264
+ status: "blocked",
23265
+ summary: reason,
23266
+ facts: {},
23267
+ artifacts: [],
23268
+ missingEvidence: [],
23269
+ blockers: [reason]
23270
+ };
23271
+ return {
23272
+ ...result,
23273
+ exitCode: 64,
23274
+ reason,
23275
+ capabilityResults: [blocked]
23276
+ };
23277
+ }
23196
23278
  }
23197
23279
  function shouldRunCapabilityWorkflow(job, workflow, capabilityIdentity, selectedImplementation, base) {
23198
23280
  if (workflow.steps.length === 0) return false;
@@ -23909,6 +23991,7 @@ var DEFAULT_INSTANT_AGENT, localJobSeq, InvalidJobError;
23909
23991
  var init_job = __esm({
23910
23992
  "src/job.ts"() {
23911
23993
  "use strict";
23994
+ init_capability_contract_validation();
23912
23995
  init_agencyBoundaryEval();
23913
23996
  init_capabilityFolders();
23914
23997
  init_config();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kody-ade/kody-engine",
3
- "version": "0.4.560",
3
+ "version": "0.4.562",
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",