@kody-ade/kody-engine 0.4.469 → 0.4.470

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.469",
18
+ version: "0.4.470",
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",
@@ -1730,9 +1730,7 @@ function parseCapabilityContract(raw) {
1730
1730
  if (parsed.execution !== void 0 && parsed.execution !== "agent" && parsed.execution !== "script") {
1731
1731
  throw new Error('contract.json execution must be "agent" or "script"');
1732
1732
  }
1733
- const unsupported = Object.keys(parsed).filter(
1734
- (key) => key !== "execution" && key !== "input" && key !== "output"
1735
- );
1733
+ const unsupported = Object.keys(parsed).filter((key) => key !== "execution" && key !== "input" && key !== "output");
1736
1734
  if (unsupported.length > 0) {
1737
1735
  throw new Error(`contract.json contains unsupported fields: ${unsupported.join(", ")}`);
1738
1736
  }
@@ -13141,11 +13139,14 @@ function emptyCatalog() {
13141
13139
  function addDefinition(catalog, document) {
13142
13140
  if (document.schemaVersion !== 1) throw new Error(`Unsupported Agency Definition schema: ${document.schemaVersion}`);
13143
13141
  if (document.kind === "intent") add(catalog.intents, createIntentDefinition(document.data), document.recordId);
13144
- else if (document.kind === "operation") add(catalog.operations, createOperationDefinition(document.data), document.recordId);
13142
+ else if (document.kind === "operation")
13143
+ add(catalog.operations, createOperationDefinition(document.data), document.recordId);
13145
13144
  else if (document.kind === "goal") add(catalog.goals, createGoalDefinition(document.data), document.recordId);
13146
13145
  else if (document.kind === "loop") add(catalog.loops, createLoopDefinition(document.data), document.recordId);
13147
- else if (document.kind === "workflow") add(catalog.workflows, createWorkflowDefinition(document.data), document.recordId);
13148
- else if (document.kind === "capability") add(catalog.capabilities, createCapabilityDefinition(document.data), document.recordId);
13146
+ else if (document.kind === "workflow")
13147
+ add(catalog.workflows, createWorkflowDefinition(document.data), document.recordId);
13148
+ else if (document.kind === "capability")
13149
+ add(catalog.capabilities, createCapabilityDefinition(document.data), document.recordId);
13149
13150
  else add(catalog.agents, createAgentDefinition(document.data), document.recordId);
13150
13151
  }
13151
13152
  function add(collection, definition, revision) {
@@ -13191,19 +13192,23 @@ var init_agencyModelRepository = __esm({
13191
13192
  async listManagedWork(catalog) {
13192
13193
  const definitions = catalog ?? await this.loadCatalog();
13193
13194
  const managed = [
13194
- ...Array.from(definitions.goals.values(), ({ definition, revision }) => ({ definition, revision, kind: "goal" })),
13195
- ...Array.from(definitions.loops.values(), ({ definition, revision }) => ({ definition, revision, kind: "loop" }))
13195
+ ...Array.from(definitions.goals.values(), ({ definition, revision }) => ({
13196
+ definition,
13197
+ revision,
13198
+ kind: "goal"
13199
+ })),
13200
+ ...Array.from(definitions.loops.values(), ({ definition, revision }) => ({
13201
+ definition,
13202
+ revision,
13203
+ kind: "loop"
13204
+ }))
13196
13205
  ];
13197
13206
  return Promise.all(
13198
13207
  managed.map(async (record2) => ({
13199
13208
  definition: record2.definition,
13200
13209
  revision: record2.revision,
13201
13210
  state: parseState(
13202
- await this.backend.getAgencyState(
13203
- this.tenantId,
13204
- record2.kind,
13205
- record2.definition.id
13206
- ),
13211
+ await this.backend.getAgencyState(this.tenantId, record2.kind, record2.definition.id),
13207
13212
  record2.definition,
13208
13213
  record2.kind
13209
13214
  )
@@ -13249,11 +13254,7 @@ var init_agencyModelRepository = __esm({
13249
13254
  const state = createGoalState({
13250
13255
  definitionId: record2.definition.id,
13251
13256
  lifecycle: previous?.lifecycle ?? "draft",
13252
- progress: goalProgressFromOutputs(
13253
- record2.definition,
13254
- record2.revision,
13255
- await this.listOutputs()
13256
- ),
13257
+ progress: goalProgressFromOutputs(record2.definition, record2.revision, await this.listOutputs()),
13257
13258
  blockers: previous?.blockers ?? [],
13258
13259
  updatedAt
13259
13260
  });
@@ -13264,56 +13265,6 @@ var init_agencyModelRepository = __esm({
13264
13265
  }
13265
13266
  });
13266
13267
 
13267
- // src/goal/triggerDispatcher.ts
13268
- function decideTrigger(input) {
13269
- if (!input.state) return { kind: "skip", reason: "loop has no runtime state" };
13270
- if (input.state.lifecycle !== "active") {
13271
- return { kind: "skip", reason: `loop is ${input.state.lifecycle}` };
13272
- }
13273
- const trigger = input.definition.trigger;
13274
- if (input.manualRequestId?.trim()) {
13275
- return {
13276
- kind: "fire",
13277
- reason: "manual trigger was requested",
13278
- scheduledAt: input.now.toISOString(),
13279
- idempotencyKey: `${input.definition.id}:manual:${input.manualRequestId.trim()}`
13280
- };
13281
- }
13282
- if (trigger.type === "manual") {
13283
- return { kind: "skip", reason: "manual trigger was not requested" };
13284
- }
13285
- if (trigger.type !== "schedule") {
13286
- return { kind: "skip", reason: `${trigger.type} trigger is not enabled yet` };
13287
- }
13288
- const interval = parseInterval(trigger.every);
13289
- const anchor = input.state.lastFiredAt ? Date.parse(input.state.lastFiredAt) : input.now.getTime() - interval;
13290
- const dueAt = anchor + interval;
13291
- if (input.now.getTime() < dueAt) {
13292
- return { kind: "skip", reason: "scheduled trigger is not due", nextEligibleAt: new Date(dueAt).toISOString() };
13293
- }
13294
- const elapsedIntervals = Math.max(1, Math.floor((input.now.getTime() - anchor) / interval));
13295
- const scheduledAt = new Date(anchor + elapsedIntervals * interval).toISOString();
13296
- return {
13297
- kind: "fire",
13298
- reason: "scheduled trigger is due",
13299
- scheduledAt,
13300
- idempotencyKey: `${input.definition.id}:schedule:${scheduledAt}`
13301
- };
13302
- }
13303
- function parseInterval(value) {
13304
- const match = value.trim().match(/^(\d+)(m|h|d)$/);
13305
- if (!match) throw new Error(`Unsupported schedule interval: ${value}`);
13306
- const amount = Number(match[1]);
13307
- if (!Number.isSafeInteger(amount) || amount < 1) throw new Error(`Unsupported schedule interval: ${value}`);
13308
- const unit = match[2] === "m" ? 6e4 : match[2] === "h" ? 36e5 : 864e5;
13309
- return amount * unit;
13310
- }
13311
- var init_triggerDispatcher = __esm({
13312
- "src/goal/triggerDispatcher.ts"() {
13313
- "use strict";
13314
- }
13315
- });
13316
-
13317
13268
  // src/goal/policyResolver.ts
13318
13269
  import { createHash as createHash5 } from "crypto";
13319
13270
  function resolveDispatchPolicy(input) {
@@ -13338,10 +13289,7 @@ function resolveDispatchPolicy(input) {
13338
13289
  },
13339
13290
  operation,
13340
13291
  intents,
13341
- trace: [
13342
- pinned("trigger" in input.owner.definition ? "loop" : "goal", input.owner),
13343
- input.target
13344
- ],
13292
+ trace: [pinned("trigger" in input.owner.definition ? "loop" : "goal", input.owner), input.target],
13345
13293
  requiresApproval
13346
13294
  };
13347
13295
  }
@@ -13402,6 +13350,56 @@ var init_policyResolver = __esm({
13402
13350
  }
13403
13351
  });
13404
13352
 
13353
+ // src/goal/triggerDispatcher.ts
13354
+ function decideTrigger(input) {
13355
+ if (!input.state) return { kind: "skip", reason: "loop has no runtime state" };
13356
+ if (input.state.lifecycle !== "active") {
13357
+ return { kind: "skip", reason: `loop is ${input.state.lifecycle}` };
13358
+ }
13359
+ const trigger = input.definition.trigger;
13360
+ if (input.manualRequestId?.trim()) {
13361
+ return {
13362
+ kind: "fire",
13363
+ reason: "manual trigger was requested",
13364
+ scheduledAt: input.now.toISOString(),
13365
+ idempotencyKey: `${input.definition.id}:manual:${input.manualRequestId.trim()}`
13366
+ };
13367
+ }
13368
+ if (trigger.type === "manual") {
13369
+ return { kind: "skip", reason: "manual trigger was not requested" };
13370
+ }
13371
+ if (trigger.type !== "schedule") {
13372
+ return { kind: "skip", reason: `${trigger.type} trigger is not enabled yet` };
13373
+ }
13374
+ const interval = parseInterval(trigger.every);
13375
+ const anchor = input.state.lastFiredAt ? Date.parse(input.state.lastFiredAt) : input.now.getTime() - interval;
13376
+ const dueAt = anchor + interval;
13377
+ if (input.now.getTime() < dueAt) {
13378
+ return { kind: "skip", reason: "scheduled trigger is not due", nextEligibleAt: new Date(dueAt).toISOString() };
13379
+ }
13380
+ const elapsedIntervals = Math.max(1, Math.floor((input.now.getTime() - anchor) / interval));
13381
+ const scheduledAt = new Date(anchor + elapsedIntervals * interval).toISOString();
13382
+ return {
13383
+ kind: "fire",
13384
+ reason: "scheduled trigger is due",
13385
+ scheduledAt,
13386
+ idempotencyKey: `${input.definition.id}:schedule:${scheduledAt}`
13387
+ };
13388
+ }
13389
+ function parseInterval(value) {
13390
+ const match = value.trim().match(/^(\d+)(m|h|d)$/);
13391
+ if (!match) throw new Error(`Unsupported schedule interval: ${value}`);
13392
+ const amount = Number(match[1]);
13393
+ if (!Number.isSafeInteger(amount) || amount < 1) throw new Error(`Unsupported schedule interval: ${value}`);
13394
+ const unit = match[2] === "m" ? 6e4 : match[2] === "h" ? 36e5 : 864e5;
13395
+ return amount * unit;
13396
+ }
13397
+ var init_triggerDispatcher = __esm({
13398
+ "src/goal/triggerDispatcher.ts"() {
13399
+ "use strict";
13400
+ }
13401
+ });
13402
+
13405
13403
  // src/scripts/dispatchAgencyLoops.ts
13406
13404
  import { randomUUID } from "crypto";
13407
13405
  import {
@@ -13469,10 +13467,7 @@ async function dispatchAgencyLoopsWith(input) {
13469
13467
  { length: Math.max(0, maxAttempts - 1) },
13470
13468
  (_, index) => failurePolicy.backoffSeconds * 2 ** index
13471
13469
  ).reduce((sum, seconds) => sum + seconds, 0);
13472
- const leaseSeconds = Math.min(
13473
- budget.maxDurationSeconds,
13474
- maxAttempts * timeoutSeconds + backoffBudgetSeconds
13475
- );
13470
+ const leaseSeconds = Math.min(budget.maxDurationSeconds, maxAttempts * timeoutSeconds + backoffBudgetSeconds);
13476
13471
  const leaseUntil = new Date(input.now.getTime() + leaseSeconds * 1e3).toISOString();
13477
13472
  const reservationId = `reservation-${randomUUID()}`;
13478
13473
  const correlationId = `corr-${randomUUID()}`;
@@ -13730,8 +13725,8 @@ var init_dispatchAgencyLoops = __esm({
13730
13725
  "src/scripts/dispatchAgencyLoops.ts"() {
13731
13726
  "use strict";
13732
13727
  init_agencyModelRepository();
13733
- init_triggerDispatcher();
13734
13728
  init_policyResolver();
13729
+ init_triggerDispatcher();
13735
13730
  init_job();
13736
13731
  init_state_backend();
13737
13732
  dispatchAgencyLoops = async (ctx) => {
@@ -14108,8 +14103,8 @@ var dispatchSimpleLoops;
14108
14103
  var init_dispatchSimpleLoops = __esm({
14109
14104
  "src/scripts/dispatchSimpleLoops.ts"() {
14110
14105
  "use strict";
14111
- init_loopDefinitions();
14112
14106
  init_job();
14107
+ init_loopDefinitions();
14113
14108
  init_state_backend();
14114
14109
  dispatchSimpleLoops = async (ctx) => {
14115
14110
  const tenantId2 = repositoryTenant2(ctx.config);
@@ -14121,10 +14116,8 @@ var init_dispatchSimpleLoops = __esm({
14121
14116
  force,
14122
14117
  ...requestedLoopId ? { loopId: requestedLoopId } : {}
14123
14118
  });
14124
- process.stdout.write(
14125
- `\u2192 kody: Loop scheduler found ${due.length} runnable Loop(s)${force ? " (manual)" : ""}
14126
- `
14127
- );
14119
+ process.stdout.write(`\u2192 kody: Loop scheduler found ${due.length} runnable Loop(s)${force ? " (manual)" : ""}
14120
+ `);
14128
14121
  const backend = createStateBackendFromEnv();
14129
14122
  const results = [];
14130
14123
  for (const loop of due) {
@@ -14169,10 +14162,8 @@ var init_dispatchSimpleLoops = __esm({
14169
14162
  results.push({ loopId: loop.id, status, reason: result.reason ?? status });
14170
14163
  }
14171
14164
  for (const result of results) {
14172
- process.stdout.write(
14173
- `\u2192 kody: Loop ${result.loopId} ${result.status}: ${result.reason}
14174
- `
14175
- );
14165
+ process.stdout.write(`\u2192 kody: Loop ${result.loopId} ${result.status}: ${result.reason}
14166
+ `);
14176
14167
  }
14177
14168
  ctx.data.simpleLoopDispatchResults = results;
14178
14169
  };
@@ -19741,76 +19732,12 @@ var init_runScheduledImplementationTick = __esm({
19741
19732
  }
19742
19733
  });
19743
19734
 
19744
- // src/scripts/runTickScript.ts
19745
- import * as fs47 from "fs";
19746
- import * as path45 from "path";
19747
- var runTickScript;
19748
- var init_runTickScript = __esm({
19749
- "src/scripts/runTickScript.ts"() {
19750
- "use strict";
19751
- init_capabilityFolders();
19752
- init_definition_paths();
19753
- init_jobState();
19754
- init_tickShellRunner();
19755
- runTickScript = async (ctx, _profile, args) => {
19756
- ctx.skipAgent = true;
19757
- const jobsDir = String(args?.jobsDir ?? capabilitiesRoot(ctx.cwd));
19758
- const slugArg = String(args?.slugArg ?? "job");
19759
- const fenceLabel = String(args?.fenceLabel ?? "kody-job-next-state");
19760
- const slug = String(ctx.args[slugArg] ?? "").trim();
19761
- if (!slug) {
19762
- ctx.output.exitCode = 99;
19763
- ctx.output.reason = `runTickScript: ctx.args.${slugArg} must be a non-empty slug`;
19764
- return;
19765
- }
19766
- const capability = readCapabilityFolder(path45.resolve(ctx.cwd, jobsDir), slug);
19767
- if (!capability) {
19768
- ctx.output.exitCode = 99;
19769
- ctx.output.reason = `runTickScript: capability folder not found or incomplete: ${path45.resolve(ctx.cwd, jobsDir, slug)}`;
19770
- return;
19771
- }
19772
- const tickScript = capability.config.tickScript;
19773
- if (!tickScript) {
19774
- ctx.output.exitCode = 99;
19775
- ctx.output.reason = `runTickScript: capability ${slug} has no \`tickScript\` in profile.json \u2014 route via capability-tick instead`;
19776
- return;
19777
- }
19778
- const scriptPath = path45.isAbsolute(tickScript) ? tickScript : path45.join(ctx.cwd, tickScript);
19779
- if (!fs47.existsSync(scriptPath)) {
19780
- ctx.output.exitCode = 99;
19781
- ctx.output.reason = `runTickScript: tickScript not found: ${scriptPath}`;
19782
- return;
19783
- }
19784
- const backend = resolveBackend({ config: ctx.config, cwd: ctx.cwd, jobsDir });
19785
- let loaded;
19786
- try {
19787
- loaded = await backend.load(slug);
19788
- } catch (err) {
19789
- ctx.output.exitCode = 99;
19790
- ctx.output.reason = `runTickScript: state load failed: ${err instanceof Error ? err.message : String(err)}`;
19791
- return;
19792
- }
19793
- ctx.data.jobSlug = slug;
19794
- ctx.data.jobState = loaded;
19795
- runTickShellAndParse({
19796
- ctx,
19797
- loaded,
19798
- scriptPath,
19799
- displayName: "runTickScript",
19800
- reasonSubject: tickScript,
19801
- fenceLabel,
19802
- force: Boolean(ctx.args.force)
19803
- });
19804
- };
19805
- }
19806
- });
19807
-
19808
19735
  // src/scripts/runSimpleCapabilityScript.ts
19809
19736
  import { spawnSync as spawnSync3 } from "child_process";
19810
- import * as fs48 from "fs";
19737
+ import * as fs47 from "fs";
19811
19738
  function isRegularFile2(filePath) {
19812
19739
  try {
19813
- const stat = fs48.lstatSync(filePath);
19740
+ const stat = fs47.lstatSync(filePath);
19814
19741
  return stat.isFile() && !stat.isSymbolicLink();
19815
19742
  } catch {
19816
19743
  return false;
@@ -19873,6 +19800,70 @@ var init_runSimpleCapabilityScript = __esm({
19873
19800
  }
19874
19801
  });
19875
19802
 
19803
+ // src/scripts/runTickScript.ts
19804
+ import * as fs48 from "fs";
19805
+ import * as path45 from "path";
19806
+ var runTickScript;
19807
+ var init_runTickScript = __esm({
19808
+ "src/scripts/runTickScript.ts"() {
19809
+ "use strict";
19810
+ init_capabilityFolders();
19811
+ init_definition_paths();
19812
+ init_jobState();
19813
+ init_tickShellRunner();
19814
+ runTickScript = async (ctx, _profile, args) => {
19815
+ ctx.skipAgent = true;
19816
+ const jobsDir = String(args?.jobsDir ?? capabilitiesRoot(ctx.cwd));
19817
+ const slugArg = String(args?.slugArg ?? "job");
19818
+ const fenceLabel = String(args?.fenceLabel ?? "kody-job-next-state");
19819
+ const slug = String(ctx.args[slugArg] ?? "").trim();
19820
+ if (!slug) {
19821
+ ctx.output.exitCode = 99;
19822
+ ctx.output.reason = `runTickScript: ctx.args.${slugArg} must be a non-empty slug`;
19823
+ return;
19824
+ }
19825
+ const capability = readCapabilityFolder(path45.resolve(ctx.cwd, jobsDir), slug);
19826
+ if (!capability) {
19827
+ ctx.output.exitCode = 99;
19828
+ ctx.output.reason = `runTickScript: capability folder not found or incomplete: ${path45.resolve(ctx.cwd, jobsDir, slug)}`;
19829
+ return;
19830
+ }
19831
+ const tickScript = capability.config.tickScript;
19832
+ if (!tickScript) {
19833
+ ctx.output.exitCode = 99;
19834
+ ctx.output.reason = `runTickScript: capability ${slug} has no \`tickScript\` in profile.json \u2014 route via capability-tick instead`;
19835
+ return;
19836
+ }
19837
+ const scriptPath = path45.isAbsolute(tickScript) ? tickScript : path45.join(ctx.cwd, tickScript);
19838
+ if (!fs48.existsSync(scriptPath)) {
19839
+ ctx.output.exitCode = 99;
19840
+ ctx.output.reason = `runTickScript: tickScript not found: ${scriptPath}`;
19841
+ return;
19842
+ }
19843
+ const backend = resolveBackend({ config: ctx.config, cwd: ctx.cwd, jobsDir });
19844
+ let loaded;
19845
+ try {
19846
+ loaded = await backend.load(slug);
19847
+ } catch (err) {
19848
+ ctx.output.exitCode = 99;
19849
+ ctx.output.reason = `runTickScript: state load failed: ${err instanceof Error ? err.message : String(err)}`;
19850
+ return;
19851
+ }
19852
+ ctx.data.jobSlug = slug;
19853
+ ctx.data.jobState = loaded;
19854
+ runTickShellAndParse({
19855
+ ctx,
19856
+ loaded,
19857
+ scriptPath,
19858
+ displayName: "runTickScript",
19859
+ reasonSubject: tickScript,
19860
+ fenceLabel,
19861
+ force: Boolean(ctx.args.force)
19862
+ });
19863
+ };
19864
+ }
19865
+ });
19866
+
19876
19867
  // src/scripts/saveManagedGoalState.ts
19877
19868
  var saveManagedGoalState;
19878
19869
  var init_saveManagedGoalState = __esm({
@@ -21116,8 +21107,8 @@ var init_scripts = __esm({
21116
21107
  init_runFlow();
21117
21108
  init_runPreviewBuild();
21118
21109
  init_runScheduledImplementationTick();
21119
- init_runTickScript();
21120
21110
  init_runSimpleCapabilityScript();
21111
+ init_runTickScript();
21121
21112
  init_saveManagedGoalState();
21122
21113
  init_saveTaskState();
21123
21114
  init_setCommentTarget();
@@ -25463,10 +25454,8 @@ function installLitellmIfNeeded(cwd) {
25463
25454
  if (installCode === 0) return 0;
25464
25455
  try {
25465
25456
  execFileSync24("python3", ["-c", "import litellm"], { stdio: "pipe" });
25466
- process.stderr.write(
25467
- `\u2192 kody: pip exited ${installCode}, but litellm is importable; continuing
25468
- `
25469
- );
25457
+ process.stderr.write(`\u2192 kody: pip exited ${installCode}, but litellm is importable; continuing
25458
+ `);
25470
25459
  return 0;
25471
25460
  } catch {
25472
25461
  return installCode;
@@ -0,0 +1,3 @@
1
+ # Initialize Kody
2
+
3
+ Scaffold the consumer repository configuration and generated GitHub Actions launcher.
@@ -0,0 +1,36 @@
1
+ {
2
+ "name": "init",
3
+ "role": "utility",
4
+ "describe": "Scaffold a consumer repository with Kody configuration and its generated launcher.",
5
+ "inputs": [
6
+ {
7
+ "name": "force",
8
+ "flag": "--force",
9
+ "type": "bool",
10
+ "required": false,
11
+ "describe": "Overwrite existing generated files instead of skipping them."
12
+ }
13
+ ],
14
+ "claudeCode": {
15
+ "model": "inherit",
16
+ "permissionMode": "acceptEdits",
17
+ "maxTurns": null,
18
+ "systemPromptAppend": null,
19
+ "tools": [],
20
+ "hooks": [],
21
+ "skills": [],
22
+ "commands": [],
23
+ "subagents": [],
24
+ "plugins": [],
25
+ "mcpServers": []
26
+ },
27
+ "cliTools": [],
28
+ "scripts": {
29
+ "preflight": [
30
+ {
31
+ "script": "initFlow"
32
+ }
33
+ ],
34
+ "postflight": []
35
+ }
36
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kody-ade/kody-engine",
3
- "version": "0.4.469",
3
+ "version": "0.4.470",
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",
@@ -76,8 +76,6 @@ jobs:
76
76
  - uses: actions/setup-python@v5
77
77
  with:
78
78
  python-version: "3.12"
79
- cache: pip
80
- cache-dependency-path: .kody-pip-requirements.txt
81
79
 
82
80
  - uses: astral-sh/setup-uv@v6
83
81