@kody-ade/kody-engine 0.4.458 → 0.4.459

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.458",
18
+ version: "0.4.459",
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",
@@ -1651,15 +1651,7 @@ import * as fs4 from "fs";
1651
1651
  import * as path5 from "path";
1652
1652
  function capabilityOutputConditionPaths(config) {
1653
1653
  if (config.outputSchema) {
1654
- const properties = isPlainObject(config.outputSchema.properties) ? config.outputSchema.properties : void 0;
1655
- const factContract = isPlainObject(properties?.facts) ? properties.facts : void 0;
1656
- const facts = isPlainObject(factContract?.properties) ? factContract.properties : void 0;
1657
- return /* @__PURE__ */ new Set([
1658
- ...properties?.status ? ["result.status"] : [],
1659
- ...properties?.summary ? ["result.summary"] : [],
1660
- ...properties?.resultClass ? ["result.resultClass"] : [],
1661
- ...Object.keys(facts ?? {}).map((fact) => `result.facts.${fact}`)
1662
- ]);
1654
+ return new Set(schemaPropertyPaths(config.outputSchema, "result"));
1663
1655
  }
1664
1656
  const result = config.output?.result;
1665
1657
  if (!result) return /* @__PURE__ */ new Set();
@@ -1684,35 +1676,61 @@ function isCapabilityFolder(dir) {
1684
1676
  if (!fs4.existsSync(path5.join(dir, CAPABILITY_BODY_FILE))) return false;
1685
1677
  const entries = fs4.readdirSync(dir, { withFileTypes: true });
1686
1678
  return entries.every(
1687
- (entry) => entry.name === CAPABILITY_BODY_FILE || entry.isDirectory() && (entry.name === "skills" || entry.name === "tools")
1679
+ (entry) => entry.name === CAPABILITY_BODY_FILE || entry.name === CAPABILITY_CONTRACT_FILE || entry.isDirectory() && (entry.name === "skills" || entry.name === "tools")
1688
1680
  );
1689
1681
  }
1690
1682
  function readCapabilityFolder(root, slug) {
1691
1683
  const dir = path5.join(root, slug);
1692
1684
  const bodyPath = path5.join(dir, CAPABILITY_BODY_FILE);
1685
+ const contractPath = path5.join(dir, CAPABILITY_CONTRACT_FILE);
1693
1686
  if (!fs4.existsSync(bodyPath) || !fs4.statSync(bodyPath).isFile()) return null;
1694
1687
  if (!isCapabilityFolder(dir)) return null;
1695
1688
  try {
1696
1689
  const rawBody = fs4.readFileSync(bodyPath, "utf-8");
1690
+ const contract = fs4.existsSync(contractPath) ? parseCapabilityContract(fs4.readFileSync(contractPath, "utf-8")) : void 0;
1697
1691
  const { title, body } = parseCapabilityBody(rawBody, slug);
1698
1692
  return {
1699
1693
  slug,
1700
1694
  dir,
1701
1695
  profilePath: bodyPath,
1702
1696
  bodyPath,
1697
+ ...contract ? { contractPath } : {},
1703
1698
  title,
1704
1699
  body,
1705
1700
  rawBody,
1706
1701
  config: {
1707
1702
  action: slug,
1708
- describe: title
1703
+ describe: title,
1704
+ ...contract ? {
1705
+ inputSchema: contract.input,
1706
+ outputSchema: contract.output
1707
+ } : {}
1709
1708
  },
1710
- rawProfile: {}
1709
+ rawProfile: contract ? { input: contract.input, output: contract.output } : {},
1710
+ ...contract ? { contract } : {}
1711
1711
  };
1712
1712
  } catch {
1713
1713
  return null;
1714
1714
  }
1715
1715
  }
1716
+ function parseCapabilityContract(raw) {
1717
+ const parsed = JSON.parse(raw);
1718
+ if (!isPlainObject(parsed) || !isPlainObject(parsed.input) || !isPlainObject(parsed.output)) {
1719
+ throw new Error("contract.json must contain input and output JSON schemas");
1720
+ }
1721
+ const unsupported = Object.keys(parsed).filter((key) => key !== "input" && key !== "output");
1722
+ if (unsupported.length > 0) {
1723
+ throw new Error(`contract.json contains unsupported fields: ${unsupported.join(", ")}`);
1724
+ }
1725
+ return { input: parsed.input, output: parsed.output };
1726
+ }
1727
+ function schemaPropertyPaths(schema, prefix) {
1728
+ const properties = isPlainObject(schema.properties) ? schema.properties : {};
1729
+ return Object.entries(properties).flatMap(([name, property]) => {
1730
+ const path53 = `${prefix}.${name}`;
1731
+ return isPlainObject(property) ? [path53, ...schemaPropertyPaths(property, path53)] : [path53];
1732
+ });
1733
+ }
1716
1734
  function parseCapabilityBody(raw, slug) {
1717
1735
  const trimmed = raw.trim();
1718
1736
  const firstLine2 = trimmed.split("\n", 1)[0] ?? "";
@@ -1848,11 +1866,12 @@ function isSafeSlug(value) {
1848
1866
  function isSafeStepId(value) {
1849
1867
  return /^[A-Za-z][A-Za-z0-9_-]*$/.test(value) && !value.includes("..");
1850
1868
  }
1851
- var CAPABILITY_BODY_FILE, CAPABILITY_PROFILE_FILE;
1869
+ var CAPABILITY_BODY_FILE, CAPABILITY_CONTRACT_FILE, CAPABILITY_PROFILE_FILE;
1852
1870
  var init_capabilityFolders = __esm({
1853
1871
  "src/capabilityFolders.ts"() {
1854
1872
  "use strict";
1855
1873
  CAPABILITY_BODY_FILE = "instructions.md";
1874
+ CAPABILITY_CONTRACT_FILE = "contract.json";
1856
1875
  CAPABILITY_PROFILE_FILE = CAPABILITY_BODY_FILE;
1857
1876
  }
1858
1877
  });
@@ -4214,24 +4233,6 @@ var init_agencyBoundaryEval = __esm({
4214
4233
  }
4215
4234
  });
4216
4235
 
4217
- // src/capabilityDelivery.ts
4218
- function capabilityDeliveryTarget(input) {
4219
- if (!input || typeof input !== "object" || Array.isArray(input)) return null;
4220
- const value = input;
4221
- const issue2 = positiveInteger(value.issue);
4222
- const pr = positiveInteger(value.pr);
4223
- if (issue2 === null === (pr === null)) return null;
4224
- return issue2 === null ? { kind: "pr", number: pr } : { kind: "issue", number: issue2 };
4225
- }
4226
- function positiveInteger(value) {
4227
- return typeof value === "number" && Number.isInteger(value) && value > 0 ? value : null;
4228
- }
4229
- var init_capabilityDelivery = __esm({
4230
- "src/capabilityDelivery.ts"() {
4231
- "use strict";
4232
- }
4233
- });
4234
-
4235
4236
  // src/agency/capability-contract-validation.ts
4236
4237
  import Ajv from "ajv";
4237
4238
  function validateCapabilityContractValue(boundary, schema, value) {
@@ -4252,7 +4253,7 @@ var init_capability_contract_validation = __esm({
4252
4253
  CapabilityContractValidationError = class extends Error {
4253
4254
  constructor(boundary, errors) {
4254
4255
  super(
4255
- `Capability ${boundary} does not match its canonical contract: ${validator.errorsText([...errors], {
4256
+ `Capability ${boundary} does not match its declared contract: ${validator.errorsText([...errors], {
4256
4257
  separator: "; "
4257
4258
  })}`
4258
4259
  );
@@ -4370,7 +4371,7 @@ function parseGoalEvidenceProgress(value) {
4370
4371
  ...stringField2(raw.reason) ? { reason: stringField2(raw.reason) } : {},
4371
4372
  ...stringField2(raw.nextAction) ? { nextAction: stringField2(raw.nextAction) } : {},
4372
4373
  ...stringField2(raw.nextRetryAt) ? { nextRetryAt: stringField2(raw.nextRetryAt) } : {},
4373
- ...positiveInteger2(raw.issue) ? { issue: positiveInteger2(raw.issue) } : {},
4374
+ ...positiveInteger(raw.issue) ? { issue: positiveInteger(raw.issue) } : {},
4374
4375
  ...stringField2(raw.updatedAt) ? { updatedAt: stringField2(raw.updatedAt) } : {}
4375
4376
  };
4376
4377
  }
@@ -4386,7 +4387,7 @@ function definedProgressFields(update) {
4386
4387
  function stringField2(value) {
4387
4388
  return typeof value === "string" && value.trim() ? value.trim() : void 0;
4388
4389
  }
4389
- function positiveInteger2(value) {
4390
+ function positiveInteger(value) {
4390
4391
  if (typeof value === "number" && Number.isInteger(value) && value > 0) return value;
4391
4392
  return void 0;
4392
4393
  }
@@ -9010,6 +9011,13 @@ function validateWorkflow(value, options = {}) {
9010
9011
  issue(issues, "invalid_transition_target", `${base}.to`, "workflow connection must name a valid target step");
9011
9012
  return;
9012
9013
  }
9014
+ if (raw.default === true && raw.when !== void 0) {
9015
+ issue(issues, "conflicting_transition", base, "workflow connection cannot be both conditional and default");
9016
+ }
9017
+ if (raw.when !== void 0) {
9018
+ const outputPaths = options.capabilityOutputs?.get(sourceCapability ?? "");
9019
+ validateDataMatch(raw.when, `${base}.when`, issues, outputPaths);
9020
+ }
9013
9021
  if (target === "$end") {
9014
9022
  explicitEndSources.add(id);
9015
9023
  return;
@@ -9024,13 +9032,6 @@ function validateWorkflow(value, options = {}) {
9024
9032
  } else {
9025
9033
  adjacency.get(id)?.push(target);
9026
9034
  }
9027
- if (raw.default === true && raw.when !== void 0) {
9028
- issue(issues, "conflicting_transition", base, "workflow connection cannot be both conditional and default");
9029
- }
9030
- if (raw.when !== void 0) {
9031
- const outputPaths = options.capabilityOutputs?.get(sourceCapability ?? "");
9032
- validateDataMatch(raw.when, `${base}.when`, issues, outputPaths);
9033
- }
9034
9035
  const targetIndex = ids.indexOf(target ?? "");
9035
9036
  const iterations = raw.maxIterations;
9036
9037
  if (targetIndex >= 0 && targetIndex <= index) {
@@ -16222,6 +16223,7 @@ var loadSimpleCapability;
16222
16223
  var init_loadSimpleCapability = __esm({
16223
16224
  "src/scripts/loadSimpleCapability.ts"() {
16224
16225
  "use strict";
16226
+ init_capability_contract_validation();
16225
16227
  init_capabilityFolders();
16226
16228
  init_definition_paths();
16227
16229
  loadSimpleCapability = async (ctx) => {
@@ -16238,9 +16240,14 @@ var init_loadSimpleCapability = __esm({
16238
16240
  const toolFiles = listFiles(toolRoot);
16239
16241
  const skillFiles = listFiles(skillRoot);
16240
16242
  const input = parseInput(ctx.args.input);
16241
- const delivery = ctx.data.jobDelivery === "pull-request";
16243
+ if (capability.config.inputSchema) {
16244
+ validateCapabilityContractValue("input", capability.config.inputSchema, input);
16245
+ }
16242
16246
  ctx.data.jobCapability = slug;
16243
16247
  ctx.data.capabilityInput = input;
16248
+ if (capability.config.outputSchema) {
16249
+ ctx.data.capabilityOutputSchema = capability.config.outputSchema;
16250
+ }
16244
16251
  ctx.data.capabilityEnvironment = capabilityEnvironment(input);
16245
16252
  ctx.data.prompt = [
16246
16253
  capability.rawBody.trim(),
@@ -16251,21 +16258,6 @@ var init_loadSimpleCapability = __esm({
16251
16258
  JSON.stringify(input ?? null, null, 2),
16252
16259
  "```",
16253
16260
  "",
16254
- ...delivery ? [
16255
- "## Delivery",
16256
- "",
16257
- "The wrapper owns git commits, pushes, and pull requests. Do not run git or gh write commands.",
16258
- "Finish with exactly this structure:",
16259
- "",
16260
- "DONE",
16261
- "PLAN_DEVIATIONS: none",
16262
- "COMMIT_MSG: <conventional commit message>",
16263
- "PR_SUMMARY:",
16264
- "- <what changed>",
16265
- "```json",
16266
- '{"summary":"<result>","status":"changed"}',
16267
- "```"
16268
- ] : ["Return one JSON value."],
16269
16261
  ...skillFiles.length ? [
16270
16262
  "",
16271
16263
  "## Skills",
@@ -16283,7 +16275,17 @@ var init_loadSimpleCapability = __esm({
16283
16275
  "",
16284
16276
  "Inspect or run these capability-owned files when needed:",
16285
16277
  ...toolFiles.map((file) => `- ${path40.join(toolRoot, file)}`)
16286
- ] : []
16278
+ ] : [],
16279
+ "",
16280
+ ...capability.config.outputSchema ? [
16281
+ "## Output contract",
16282
+ "",
16283
+ "Produce one JSON result matching this schema:",
16284
+ "",
16285
+ "```json",
16286
+ JSON.stringify(capability.config.outputSchema, null, 2),
16287
+ "```"
16288
+ ] : ["Return one JSON value."]
16287
16289
  ].join("\n");
16288
16290
  };
16289
16291
  }
@@ -17266,6 +17268,7 @@ var parseSimpleCapabilityOutput;
17266
17268
  var init_parseSimpleCapabilityOutput = __esm({
17267
17269
  "src/scripts/parseSimpleCapabilityOutput.ts"() {
17268
17270
  "use strict";
17271
+ init_capability_contract_validation();
17269
17272
  parseSimpleCapabilityOutput = async (ctx, _profile, agentResult) => {
17270
17273
  const output = parseOutput(agentResult?.finalText);
17271
17274
  if (output === void 0) {
@@ -17290,6 +17293,29 @@ var init_parseSimpleCapabilityOutput = __esm({
17290
17293
  ];
17291
17294
  return;
17292
17295
  }
17296
+ const outputSchema = isObject2(ctx.data.capabilityOutputSchema) ? ctx.data.capabilityOutputSchema : void 0;
17297
+ if (outputSchema) {
17298
+ try {
17299
+ validateCapabilityContractValue("output", outputSchema, output);
17300
+ } catch (error) {
17301
+ const reason2 = error instanceof Error ? error.message : String(error);
17302
+ ctx.output.exitCode = 64;
17303
+ ctx.output.reason = reason2;
17304
+ ctx.data.capabilityOutput = output;
17305
+ ctx.data.capabilityResults = [
17306
+ {
17307
+ version: 1,
17308
+ status: "blocked",
17309
+ summary: reason2,
17310
+ facts: {},
17311
+ artifacts: [],
17312
+ missingEvidence: [],
17313
+ blockers: [reason2]
17314
+ }
17315
+ ];
17316
+ return;
17317
+ }
17318
+ }
17293
17319
  ctx.data.capabilityOutput = output;
17294
17320
  const result = isObject2(output) ? output : {};
17295
17321
  const data = isObject2(result.data) ? result.data : isObject2(output) ? output : { output };
@@ -17965,6 +17991,24 @@ var init_prepareBrowserAuth = __esm({
17965
17991
  }
17966
17992
  });
17967
17993
 
17994
+ // src/capabilityDelivery.ts
17995
+ function capabilityDeliveryTarget(input) {
17996
+ if (!input || typeof input !== "object" || Array.isArray(input)) return null;
17997
+ const value = input;
17998
+ const issue2 = positiveInteger2(value.issue);
17999
+ const pr = positiveInteger2(value.pr);
18000
+ if (issue2 === null === (pr === null)) return null;
18001
+ return issue2 === null ? { kind: "pr", number: pr } : { kind: "issue", number: issue2 };
18002
+ }
18003
+ function positiveInteger2(value) {
18004
+ return typeof value === "number" && Number.isInteger(value) && value > 0 ? value : null;
18005
+ }
18006
+ var init_capabilityDelivery = __esm({
18007
+ "src/capabilityDelivery.ts"() {
18008
+ "use strict";
18009
+ }
18010
+ });
18011
+
17968
18012
  // src/scripts/runFlow.ts
17969
18013
  function tryPost(issueNumber, body, cwd) {
17970
18014
  try {
@@ -22274,6 +22318,34 @@ var init_executor = __esm({
22274
22318
  }
22275
22319
  });
22276
22320
 
22321
+ // src/simpleCapabilityRuntime.ts
22322
+ function resolveSimpleCapabilityRuntime(implementation, delivery) {
22323
+ if (implementation !== SIMPLE_CAPABILITY_RUNTIME) return null;
22324
+ return {
22325
+ implementation: delivery ? DELIVERY_RUNTIMES[delivery] : SIMPLE_CAPABILITY_RUNTIME,
22326
+ ...delivery ? { delivery } : {}
22327
+ };
22328
+ }
22329
+ function simpleCapabilityRuntimeArgs(runtime, capability, input) {
22330
+ const deliveryTarget = runtime.delivery ? capabilityDeliveryTarget(input) : null;
22331
+ return {
22332
+ capability,
22333
+ ...input !== void 0 ? { input: JSON.stringify(input) } : {},
22334
+ ...deliveryTarget ? { [deliveryTarget.kind]: deliveryTarget.number } : {}
22335
+ };
22336
+ }
22337
+ var SIMPLE_CAPABILITY_RUNTIME, DELIVERY_RUNTIMES;
22338
+ var init_simpleCapabilityRuntime = __esm({
22339
+ "src/simpleCapabilityRuntime.ts"() {
22340
+ "use strict";
22341
+ init_capabilityDelivery();
22342
+ SIMPLE_CAPABILITY_RUNTIME = "capability-run";
22343
+ DELIVERY_RUNTIMES = {
22344
+ "pull-request": "capability-delivery"
22345
+ };
22346
+ }
22347
+ });
22348
+
22277
22349
  // src/workflowRunState.ts
22278
22350
  function workflowRunStatePath(workflowId, runId) {
22279
22351
  if (!SAFE_ID.test(workflowId)) throw new Error(`invalid workflow id ${workflowId}`);
@@ -22434,7 +22506,8 @@ async function runJob(job, base) {
22434
22506
  }
22435
22507
  const workflow = capabilityContext?.config.workflow ?? workflowContext?.config.workflow;
22436
22508
  const workflowIdentity = valid.workflow ?? capabilityIdentity ?? workflowContext?.slug;
22437
- const capabilitySelectedImplementation = valid.delivery === "pull-request" && resolvedCapability?.implementation === "capability-run" ? "capability-delivery" : resolvedCapability?.implementation ?? capabilityContext?.config.implementation ?? capabilityContext?.config.implementations?.[0] ?? (capabilityContext?.config.role ? capabilityContext.slug : void 0) ?? (capabilityContext?.config.tickScript ? "capability-tick-scripted" : void 0);
22509
+ const simpleCapabilityRuntime = resolveSimpleCapabilityRuntime(resolvedCapability?.implementation, valid.delivery);
22510
+ const capabilitySelectedImplementation = simpleCapabilityRuntime?.implementation ?? resolvedCapability?.implementation ?? capabilityContext?.config.implementation ?? capabilityContext?.config.implementations?.[0] ?? (capabilityContext?.config.role ? capabilityContext.slug : void 0) ?? (capabilityContext?.config.tickScript ? "capability-tick-scripted" : void 0);
22438
22511
  const profileName = explicitImplementation ?? capabilitySelectedImplementation;
22439
22512
  if (workflow && shouldRunCapabilityWorkflow(valid, workflow, workflowIdentity, capabilitySelectedImplementation, base)) {
22440
22513
  const workflowCapability = capabilityContext ?? workflowContext;
@@ -22519,6 +22592,7 @@ async function runDefaultCapabilityWorkflow(job, profileName, capabilityIdentity
22519
22592
  );
22520
22593
  }
22521
22594
  async function runCapabilityImplementationStep(valid, profileName, capabilityIdentity, capabilityContext, resolvedCapability, base) {
22595
+ const simpleCapabilityRuntime = resolveSimpleCapabilityRuntime(resolvedCapability?.implementation, valid.delivery);
22522
22596
  const preloadedData = { ...base.preloadedData ?? {} };
22523
22597
  preloadedData.jobId = newJobId(valid.flavor);
22524
22598
  preloadedData.jobKey = stableJobKey(valid);
@@ -22564,14 +22638,9 @@ async function runCapabilityImplementationStep(valid, profileName, capabilityIde
22564
22638
  };
22565
22639
  const shouldApplyResolvedCapabilityArgs = valid.implementation === void 0 && resolvedCapability && profileName === resolvedCapability.implementation;
22566
22640
  input.cliArgs = shouldApplyResolvedCapabilityArgs ? { ...resolvedCapability.cliArgs, ...input.cliArgs } : input.cliArgs;
22567
- if ((profileName === "capability-run" || profileName === "capability-delivery") && capabilityIdentity) {
22641
+ if (simpleCapabilityRuntime && profileName === simpleCapabilityRuntime.implementation && capabilityIdentity) {
22568
22642
  const capabilityInput = Object.keys(valid.cliArgs).length > 0 ? genericInputFromArgs(valid.cliArgs) : void 0;
22569
- const deliveryTarget = profileName === "capability-delivery" && capabilityInput && typeof capabilityInput === "object" ? capabilityDeliveryArgs(capabilityInput) : {};
22570
- input.cliArgs = {
22571
- capability: capabilityIdentity,
22572
- ...capabilityInput !== void 0 ? { input: JSON.stringify(capabilityInput) } : {},
22573
- ...deliveryTarget
22574
- };
22643
+ input.cliArgs = simpleCapabilityRuntimeArgs(simpleCapabilityRuntime, capabilityIdentity, capabilityInput);
22575
22644
  }
22576
22645
  const run = base.chain === false ? runImplementation : runImplementationChain;
22577
22646
  return run(profileName, input);
@@ -22937,10 +23006,6 @@ function workflowStepToJob(step, parent, chainData, cwd) {
22937
23006
  ...parent.resultTarget ? { resultTarget: parent.resultTarget } : {}
22938
23007
  };
22939
23008
  }
22940
- function capabilityDeliveryArgs(input) {
22941
- const target = capabilityDeliveryTarget(input);
22942
- return target ? { [target.kind]: target.number } : {};
22943
- }
22944
23009
  function usesGenericCapabilityInput(action, cwd) {
22945
23010
  const inputs = getCapabilityActionInputs(action, hydratedCapabilitiesRoot(cwd));
22946
23011
  return Boolean(inputs?.length === 1 && inputs[0]?.name === "input" && inputs[0]?.flag === "--input");
@@ -23105,12 +23170,12 @@ var init_job = __esm({
23105
23170
  "src/job.ts"() {
23106
23171
  "use strict";
23107
23172
  init_agencyBoundaryEval();
23108
- init_capabilityDelivery();
23109
23173
  init_capabilityFolders();
23110
23174
  init_definition_paths();
23111
23175
  init_executor();
23112
23176
  init_registry();
23113
23177
  init_runIndex();
23178
+ init_simpleCapabilityRuntime();
23114
23179
  init_state_backend();
23115
23180
  init_workflowDefinitions();
23116
23181
  init_workflowRunState();
@@ -1 +1,17 @@
1
1
  {{prompt}}
2
+
3
+ ## Delivery
4
+
5
+ The delivery wrapper owns git commits, pushes, and pull requests. Do not run
6
+ git or GitHub write commands.
7
+
8
+ After completing the capability work, finish with exactly this structure:
9
+
10
+ DONE
11
+ PLAN_DEVIATIONS: none
12
+ COMMIT_MSG: <conventional commit message>
13
+ PR_SUMMARY:
14
+ - <what changed>
15
+ ```json
16
+ <the capability result matching its output contract>
17
+ ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kody-ade/kody-engine",
3
- "version": "0.4.458",
3
+ "version": "0.4.459",
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",