@kody-ade/kody-engine 0.4.446 → 0.4.448

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 +58 -13
  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.446",
18
+ version: "0.4.448",
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",
@@ -1795,11 +1795,11 @@ function parseWorkflowTransitions(value) {
1795
1795
  const transitions = rawTransitions.map((raw) => {
1796
1796
  if (typeof raw === "string") {
1797
1797
  const to2 = raw.trim();
1798
- return isSafeStepId(to2) ? { to: to2 } : null;
1798
+ return to2 === "$end" || isSafeStepId(to2) ? { to: to2 } : null;
1799
1799
  }
1800
1800
  if (!isPlainObject(raw)) return null;
1801
1801
  const to = stringField(raw.to);
1802
- if (!to || !isSafeStepId(to)) return null;
1802
+ if (!to || to !== "$end" && !isSafeStepId(to)) return null;
1803
1803
  const maxIterations = typeof raw.maxIterations === "number" && Number.isInteger(raw.maxIterations) && raw.maxIterations > 0 ? raw.maxIterations : void 0;
1804
1804
  return {
1805
1805
  to,
@@ -1862,11 +1862,11 @@ function definitionsRoot(cwd = process.cwd()) {
1862
1862
  const override = process.env.KODY_DEFINITIONS_ROOT?.trim();
1863
1863
  const overrideCwd = process.env.KODY_DEFINITIONS_ROOT_CWD?.trim();
1864
1864
  if (override && overrideCwd && path6.resolve(cwd) === path6.resolve(overrideCwd)) {
1865
- return path6.resolve(override);
1865
+ return storeCatalogRoot(path6.resolve(override));
1866
1866
  }
1867
1867
  const hydrated = path6.join(cwd, ".kody-engine", "definitions");
1868
1868
  if (fs5.existsSync(hydrated)) return hydrated;
1869
- return override ? path6.resolve(override) : hydrated;
1869
+ return override ? storeCatalogRoot(path6.resolve(override)) : hydrated;
1870
1870
  }
1871
1871
  function hasExplicitDefinitionsRoot(cwd = process.cwd(), env = process.env) {
1872
1872
  const root = env.KODY_DEFINITIONS_ROOT?.trim();
@@ -1874,13 +1874,36 @@ function hasExplicitDefinitionsRoot(cwd = process.cwd(), env = process.env) {
1874
1874
  return Boolean(root && rootCwd && path6.resolve(cwd) === path6.resolve(rootCwd));
1875
1875
  }
1876
1876
  function capabilitiesRoot(cwd = process.cwd()) {
1877
- return path6.join(definitionsRoot(cwd), "capabilities");
1877
+ return storeAssetRoot(cwd, "capabilities") ?? path6.join(definitionsRoot(cwd), "capabilities");
1878
1878
  }
1879
1879
  function implementationsRoot(cwd = process.cwd()) {
1880
1880
  return path6.join(definitionsRoot(cwd), "implementations");
1881
1881
  }
1882
1882
  function agentsRoot(cwd = process.cwd()) {
1883
- return path6.join(definitionsRoot(cwd), "agents");
1883
+ return storeAssetRoot(cwd, "agent") ?? path6.join(definitionsRoot(cwd), "agents");
1884
+ }
1885
+ function storeCatalogRoot(root) {
1886
+ const manifest = readStoreManifest(root);
1887
+ const roots = ["capabilities", "workflows", "loops"].map((kind) => manifest?.assetRoots?.[kind]).filter((value) => typeof value === "string" && Boolean(value.trim())).map((value) => path6.dirname(value));
1888
+ return roots.length === 3 && new Set(roots).size === 1 ? path6.join(root, roots[0]) : root;
1889
+ }
1890
+ function storeAssetRoot(cwd, kind) {
1891
+ const override = process.env.KODY_DEFINITIONS_ROOT?.trim();
1892
+ if (!override) return null;
1893
+ const overrideCwd = process.env.KODY_DEFINITIONS_ROOT_CWD?.trim();
1894
+ if (overrideCwd && path6.resolve(cwd) !== path6.resolve(overrideCwd)) return null;
1895
+ const root = path6.resolve(override);
1896
+ const configured = readStoreManifest(root)?.assetRoots?.[kind];
1897
+ return typeof configured === "string" && configured.trim() ? path6.join(root, configured) : null;
1898
+ }
1899
+ function readStoreManifest(root) {
1900
+ const file = path6.join(root, "kody-store.json");
1901
+ if (!fs5.existsSync(file)) return null;
1902
+ try {
1903
+ return JSON.parse(fs5.readFileSync(file, "utf8"));
1904
+ } catch {
1905
+ return null;
1906
+ }
1884
1907
  }
1885
1908
  var init_definition_paths = __esm({
1886
1909
  "src/definition-paths.ts"() {
@@ -8959,10 +8982,13 @@ function validateWorkflow(value, options = {}) {
8959
8982
  }
8960
8983
  }
8961
8984
  const target = text(raw.to);
8962
- if (!target || !SAFE_STEP_ID.test(target)) {
8985
+ if (!target || target !== "$end" && !SAFE_STEP_ID.test(target)) {
8963
8986
  issue(issues, "invalid_transition_target", `${base}.to`, "workflow connection must name a valid target step");
8964
8987
  return;
8965
8988
  }
8989
+ if (target === "$end") {
8990
+ return;
8991
+ }
8966
8992
  if (!seen.has(target)) {
8967
8993
  issue(
8968
8994
  issues,
@@ -13898,6 +13924,9 @@ var init_loopDefinitions = __esm({
13898
13924
 
13899
13925
  // src/scripts/dispatchSimpleLoops.ts
13900
13926
  import { randomUUID as randomUUID2 } from "crypto";
13927
+ function loopDispatchSlot(loop, now, force, nonce) {
13928
+ return force ? `manual:${now.toISOString()}:${nonce}` : dueSlot(loop, now);
13929
+ }
13901
13930
  function dueSlot(loop, now) {
13902
13931
  if (!loop.enabled || loop.trigger.type !== "schedule") return null;
13903
13932
  const match = /^(\d+)([mhd])$/.exec(loop.trigger.every);
@@ -13951,18 +13980,25 @@ var init_dispatchSimpleLoops = __esm({
13951
13980
  const tenantId2 = repositoryTenant2(ctx.config);
13952
13981
  if (!tenantId2) throw new Error("Repository identity is required for Loop dispatch");
13953
13982
  const now = /* @__PURE__ */ new Date();
13954
- const due = listLoopDefinitions(ctx.cwd).filter((loop) => dueSlot(loop, now) !== null);
13983
+ const force = ctx.data.jobForce === true;
13984
+ const due = listLoopDefinitions(ctx.cwd).filter(
13985
+ (loop) => loop.enabled && loop.trigger.type === "schedule" && (force || dueSlot(loop, now) !== null)
13986
+ );
13955
13987
  const backend = createStateBackendFromEnv();
13956
13988
  const results = [];
13957
13989
  for (const loop of due) {
13958
- const slot = dueSlot(loop, now);
13990
+ const slot = loopDispatchSlot(loop, now, force, randomUUID2());
13959
13991
  if (!slot) continue;
13960
13992
  const reservationId = `reservation-${randomUUID2()}`;
13961
13993
  const idempotencyKey = `${loop.id}:${slot}`;
13962
13994
  const claimed = await backend.reserveAgencyDispatch(tenantId2, {
13963
13995
  idempotencyKey,
13964
13996
  loopId: loop.id,
13965
- decision: { kind: "fire", reason: "local Loop schedule is due", scheduledAt: slot },
13997
+ decision: {
13998
+ kind: "fire",
13999
+ reason: force ? "manual Loop run requested" : "local Loop schedule is due",
14000
+ scheduledAt: slot
14001
+ },
13966
14002
  leaseUntil: new Date(now.getTime() + 6 * 60 * 60 * 1e3).toISOString(),
13967
14003
  reservationId,
13968
14004
  correlationId: `corr-${randomUUID2()}`,
@@ -22687,6 +22723,13 @@ async function runGraphCapabilityWorkflow(parent, workflow, capability, base, ch
22687
22723
  const key = `${step.id}->${transition.to}`;
22688
22724
  state.transitionCounts[key] = (state.transitionCounts[key] ?? 0) + 1;
22689
22725
  }
22726
+ if (transition.to === "$end") {
22727
+ state.status = "done";
22728
+ delete state.currentStepId;
22729
+ delete state.blocker;
22730
+ await checkpoint?.(state);
22731
+ return withWorkflowBoundaryEval(capability, { ...result, workflowState: state });
22732
+ }
22690
22733
  state.currentStepId = transition.to;
22691
22734
  state.status = "running";
22692
22735
  delete state.blocker;
@@ -22862,7 +22905,7 @@ function valueMatches(actual, expected) {
22862
22905
  }
22863
22906
  function workflowStepTargetNumber(step, parent, chainData) {
22864
22907
  if (step.target === "pr") return workflowPrNumber(chainData) ?? workflowTargetFactNumber(step, chainData);
22865
- if (step.target === "issue") return workflowIssueNumber(parent);
22908
+ if (step.target === "issue") return workflowIssueNumber(parent) ?? workflowTargetFactNumber(step, chainData);
22866
22909
  return typeof parent.target === "number" ? parent.target : targetFromCliArgs(parent.cliArgs);
22867
22910
  }
22868
22911
  function workflowResumeStartIndex(steps, evidence) {
@@ -22940,6 +22983,7 @@ function mintScheduledJob(input) {
22940
22983
  agent: input.agent,
22941
22984
  cliArgs: input.cliArgs ?? {},
22942
22985
  flavor: "scheduled",
22986
+ force: input.force === true,
22943
22987
  saveReport: input.saveReport === true
22944
22988
  };
22945
22989
  }
@@ -25614,7 +25658,8 @@ async function runScheduledFanOut(cwd, args, opts) {
25614
25658
  action: match.action,
25615
25659
  capability: match.capability,
25616
25660
  implementation: match.implementation,
25617
- cliArgs: match.cliArgs
25661
+ cliArgs: match.cliArgs,
25662
+ force: opts.force
25618
25663
  }),
25619
25664
  {
25620
25665
  cwd,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kody-ade/kody-engine",
3
- "version": "0.4.446",
3
+ "version": "0.4.448",
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",