@kody-ade/kody-engine 0.4.575 → 0.4.577

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.575",
18
+ version: "0.4.577",
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",
@@ -3068,6 +3068,12 @@ function createStateBackendFromEnv(env = process.env, client) {
3068
3068
  transport = createConvexClientFromEnv(env);
3069
3069
  }
3070
3070
  return {
3071
+ async listLoops(tenantId2) {
3072
+ const result = await transport.query(anyApi.agencyRequestLoops.list, {
3073
+ tenantId: requireTenant(tenantId2)
3074
+ });
3075
+ return Array.isArray(result) ? result : [];
3076
+ },
3071
3077
  async get(tenantId2, taskKey, kind) {
3072
3078
  const result = await transport.query(anyApi.taskState.get, {
3073
3079
  tenantId: requireTenant(tenantId2),
@@ -14154,6 +14160,11 @@ var init_loopDefinitions = __esm({
14154
14160
 
14155
14161
  // src/scripts/dispatchLoops.ts
14156
14162
  import { randomUUID } from "crypto";
14163
+ function loopRunId(loopId, nonce) {
14164
+ const safeNonce = nonce.toLowerCase().replace(/[^a-z0-9_-]/g, "-");
14165
+ const loopPrefixLength = MAX_WORKFLOW_RUN_ID_LENGTH - "loop--".length - safeNonce.length;
14166
+ return `loop-${loopId.slice(0, Math.max(1, loopPrefixLength))}-${safeNonce}`;
14167
+ }
14157
14168
  function assertLoopDispatchesSucceeded(results) {
14158
14169
  const didNotRun = results.filter((result) => result.status === "failed" || result.status === "blocked");
14159
14170
  if (didNotRun.length === 0) return;
@@ -14195,7 +14206,7 @@ async function dispatchLoopsWith(input) {
14195
14206
  results.push({ loopId: loop.id, status: status2, reason: reason2 });
14196
14207
  continue;
14197
14208
  }
14198
- const runId = `loop-${loop.id}-${input.nonce()}`;
14209
+ const runId = loopRunId(loop.id, input.nonce());
14199
14210
  const startedAt = input.now.toISOString();
14200
14211
  const running = {
14201
14212
  id: runId,
@@ -14215,7 +14226,7 @@ async function dispatchLoopsWith(input) {
14215
14226
  let reason = "target did not complete";
14216
14227
  let runFailed = false;
14217
14228
  try {
14218
- const result = await input.run(loopJob(loop), runId);
14229
+ const result = await input.run(loopJob(loop, runId), runId, loop.id);
14219
14230
  exitCode = result.exitCode;
14220
14231
  reason = result.reason ?? (exitCode === 0 ? "dispatched" : "target failed");
14221
14232
  } catch (error) {
@@ -14285,6 +14296,14 @@ function selectRunnableLoops(loops, now, options) {
14285
14296
  (loop) => loop.enabled && (!options.loopId || loop.id === options.loopId) && (options.force || loop.trigger.type === "schedule" && dueSlot(loop, now) !== null)
14286
14297
  );
14287
14298
  }
14299
+ function mergeLoopDefinitions(repositoryLoops, runtimeLoops) {
14300
+ const byId = new Map(repositoryLoops.map((loop) => [loop.id, loop]));
14301
+ for (const candidate of runtimeLoops) {
14302
+ const loop = normalizeLoopDefinition(candidate);
14303
+ if (loop) byId.set(loop.id, loop);
14304
+ }
14305
+ return [...byId.values()].sort((left, right) => left.id.localeCompare(right.id));
14306
+ }
14288
14307
  function loopDispatchSlot(loop, now, force, nonce) {
14289
14308
  return force ? `manual:${now.toISOString()}:${nonce}` : dueSlot(loop, now);
14290
14309
  }
@@ -14318,9 +14337,9 @@ function dueSlot(loop, now) {
14318
14337
  const day = parts.find((part) => part.type === "day")?.value;
14319
14338
  return `${year}-${month}-${day}T${loop.trigger.at.time}[${loop.trigger.at.timezone}]`;
14320
14339
  }
14321
- function loopJob(loop) {
14340
+ function loopJob(loop, runId) {
14322
14341
  const cliArgs = { ...loop.input };
14323
- return loop.target.kind === "workflow" ? { workflow: loop.target.id, cliArgs, flavor: "scheduled" } : { capability: loop.target.id, cliArgs, flavor: "scheduled" };
14342
+ return loop.target.kind === "workflow" ? { workflow: loop.target.id, workflowRunId: runId, cliArgs, flavor: "scheduled" } : { capability: loop.target.id, cliArgs, flavor: "scheduled" };
14324
14343
  }
14325
14344
  function repositoryTenant(config) {
14326
14345
  const [envOwner, envRepo] = (process.env.GITHUB_REPOSITORY ?? "").split("/");
@@ -14328,7 +14347,7 @@ function repositoryTenant(config) {
14328
14347
  const repo = config.github?.repo?.trim() || envRepo?.trim();
14329
14348
  return owner && repo ? `${owner}/${repo}` : null;
14330
14349
  }
14331
- var LOOP_DISPATCH_LEASE_MS, LOOP_DISPATCH_RENEW_INTERVAL_MS, dispatchLoops;
14350
+ var LOOP_DISPATCH_LEASE_MS, LOOP_DISPATCH_RENEW_INTERVAL_MS, MAX_WORKFLOW_RUN_ID_LENGTH, dispatchLoops;
14332
14351
  var init_dispatchLoops = __esm({
14333
14352
  "src/scripts/dispatchLoops.ts"() {
14334
14353
  "use strict";
@@ -14337,19 +14356,21 @@ var init_dispatchLoops = __esm({
14337
14356
  init_state_backend();
14338
14357
  LOOP_DISPATCH_LEASE_MS = 10 * 60 * 1e3;
14339
14358
  LOOP_DISPATCH_RENEW_INTERVAL_MS = 60 * 1e3;
14359
+ MAX_WORKFLOW_RUN_ID_LENGTH = 80;
14340
14360
  dispatchLoops = async (ctx) => {
14341
14361
  const tenantId2 = repositoryTenant(ctx.config);
14342
14362
  if (!tenantId2) throw new Error("Repository identity is required for Loop dispatch");
14343
14363
  const now = /* @__PURE__ */ new Date();
14344
14364
  const force = ctx.data.jobForce === true;
14345
14365
  const requestedLoopId = typeof ctx.args.loop === "string" ? ctx.args.loop.trim() : "";
14346
- const due = selectRunnableLoops(listLoopDefinitions(ctx.cwd), now, {
14366
+ const backend = createStateBackendFromEnv();
14367
+ const loops = mergeLoopDefinitions(listLoopDefinitions(ctx.cwd), await backend.listLoops(tenantId2));
14368
+ const due = selectRunnableLoops(loops, now, {
14347
14369
  force,
14348
14370
  ...requestedLoopId ? { loopId: requestedLoopId } : {}
14349
14371
  });
14350
14372
  process.stdout.write(`\u2192 kody: Loop scheduler found ${due.length} runnable Loop(s)${force ? " (manual)" : ""}
14351
14373
  `);
14352
- const backend = createStateBackendFromEnv();
14353
14374
  const results = await dispatchLoopsWith({
14354
14375
  loops: due,
14355
14376
  tenantId: tenantId2,
@@ -14357,13 +14378,13 @@ var init_dispatchLoops = __esm({
14357
14378
  now,
14358
14379
  force,
14359
14380
  nonce: randomUUID,
14360
- run: (job, parentRunId) => runJob(job, {
14381
+ run: (job, parentRunId, loopId) => runJob(job, {
14361
14382
  cwd: ctx.cwd,
14362
14383
  config: ctx.config,
14363
14384
  verbose: ctx.verbose,
14364
14385
  quiet: ctx.quiet,
14365
14386
  chain: false,
14366
- preloadedData: { parentRunId }
14387
+ preloadedData: { parentRunId, loopId }
14367
14388
  })
14368
14389
  });
14369
14390
  for (const result of results) {
@@ -23364,6 +23385,7 @@ async function runJob(job, base) {
23364
23385
  await notifyWorkflowCompleted({
23365
23386
  workflowId: workflowIdentity,
23366
23387
  runId: valid.workflowRunId,
23388
+ ...typeof base.preloadedData?.loopId === "string" ? { loopId: base.preloadedData.loopId } : {},
23367
23389
  status: result.workflowState?.status === "blocked" ? "blocked" : result.exitCode === 0 ? "success" : "failed",
23368
23390
  ...result.reason ? { summary: result.reason } : {},
23369
23391
  ...Object.keys(facts).length > 0 ? { output: facts } : {}
File without changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kody-ade/kody-engine",
3
- "version": "0.4.575",
3
+ "version": "0.4.577",
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,6 +12,30 @@
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
+ "verify:live-release": "tsx scripts/live-release-gate.ts",
29
+ "test:runtime-services": "node --test \"tests/runtime-services/*.test.mjs\"",
30
+ "test:all": "vitest run tests --no-coverage",
31
+ "typecheck": "tsc --noEmit",
32
+ "lint": "biome check",
33
+ "lint:fix": "biome check --write",
34
+ "format": "biome format --write",
35
+ "verify:package": "node scripts/verify-package-tarball.cjs",
36
+ "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",
37
+ "prepublishOnly": "pnpm typecheck && pnpm test:runtime-services && pnpm build && pnpm verify:package"
38
+ },
15
39
  "dependencies": {
16
40
  "@actions/cache": "^6.0.0",
17
41
  "@anthropic-ai/claude-agent-sdk": "0.2.119",
@@ -38,28 +62,5 @@
38
62
  "url": "git+https://github.com/aharonyaircohen/kody-engine.git"
39
63
  },
40
64
  "homepage": "https://github.com/aharonyaircohen/kody-engine",
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
- "verify:live-release": "tsx scripts/live-release-gate.ts",
56
- "test:runtime-services": "node --test \"tests/runtime-services/*.test.mjs\"",
57
- "test:all": "vitest run tests --no-coverage",
58
- "typecheck": "tsc --noEmit",
59
- "lint": "biome check",
60
- "lint:fix": "biome check --write",
61
- "format": "biome format --write",
62
- "verify:package": "node scripts/verify-package-tarball.cjs",
63
- "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"
64
- }
65
- }
65
+ "bugs": "https://github.com/aharonyaircohen/kody-engine/issues"
66
+ }