@kody-ade/kody-engine 0.4.415 → 0.4.416

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 +49 -21
  2. package/package.json +24 -25
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.415",
18
+ version: "0.4.416",
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",
@@ -2476,14 +2476,15 @@ function createStateBackendFromEnv(env = process.env, client) {
2476
2476
  });
2477
2477
  return Array.isArray(result) ? result : [];
2478
2478
  },
2479
- async reserveAgencyDispatch(tenantId2, idempotencyKey, loopId, decision, leaseUntil, now) {
2479
+ async reserveAgencyDispatch(tenantId2, reservation) {
2480
2480
  const result = await transport.mutation(anyApi.agencyModel.reserveDispatch, {
2481
2481
  tenantId: requireTenant(tenantId2),
2482
- idempotencyKey: requireNonEmpty(idempotencyKey, "idempotencyKey"),
2483
- loopId: requireNonEmpty(loopId, "loopId"),
2484
- decision,
2485
- leaseUntil,
2486
- now
2482
+ ...reservation,
2483
+ idempotencyKey: requireNonEmpty(reservation.idempotencyKey, "idempotencyKey"),
2484
+ loopId: requireNonEmpty(reservation.loopId, "loopId"),
2485
+ reservationId: requireNonEmpty(reservation.reservationId, "reservationId"),
2486
+ correlationId: requireNonEmpty(reservation.correlationId, "correlationId"),
2487
+ policyHash: requireNonEmpty(reservation.policyHash, "policyHash")
2487
2488
  });
2488
2489
  return result;
2489
2490
  },
@@ -2496,10 +2497,11 @@ function createStateBackendFromEnv(env = process.env, client) {
2496
2497
  now
2497
2498
  });
2498
2499
  },
2499
- async finishAgencyDispatch(tenantId2, idempotencyKey, status, now, runId) {
2500
+ async finishAgencyDispatch(tenantId2, idempotencyKey, reservationId, status, now, runId) {
2500
2501
  await transport.mutation(anyApi.agencyModel.finishDispatch, {
2501
2502
  tenantId: requireTenant(tenantId2),
2502
2503
  idempotencyKey: requireNonEmpty(idempotencyKey, "idempotencyKey"),
2504
+ reservationId: requireNonEmpty(reservationId, "reservationId"),
2503
2505
  status,
2504
2506
  now,
2505
2507
  ...runId ? { runId: requireNonEmpty(runId, "runId") } : {}
@@ -13199,7 +13201,7 @@ function resolveDispatchPolicy(input) {
13199
13201
  });
13200
13202
  const policy = mergePolicies(intents.map(({ definition }) => definition.policy));
13201
13203
  const constraints = intents.flatMap(({ definition }) => definition.constraints);
13202
- assertAuthorized(policy, constraints, input.target, input.approved === true);
13204
+ const requiresApproval = assertAuthorized(policy, constraints, input.target);
13203
13205
  const snapshotValue = { policy, constraints };
13204
13206
  return {
13205
13207
  snapshot: {
@@ -13211,7 +13213,8 @@ function resolveDispatchPolicy(input) {
13211
13213
  trace: [
13212
13214
  pinned("trigger" in input.owner.definition ? "loop" : "goal", input.owner),
13213
13215
  input.target
13214
- ]
13216
+ ],
13217
+ requiresApproval
13215
13218
  };
13216
13219
  }
13217
13220
  function mergePolicies(policies) {
@@ -13235,7 +13238,7 @@ function mergePolicies(policies) {
13235
13238
  riskyActions: unique(policies.flatMap(({ riskyActions }) => riskyActions))
13236
13239
  };
13237
13240
  }
13238
- function assertAuthorized(policy, constraints, target, approved) {
13241
+ function assertAuthorized(policy, constraints, target) {
13239
13242
  const action = `${target.kind}:${target.id}`;
13240
13243
  const matches = (patterns) => patterns.some((pattern) => pattern === "*" || pattern === target.id || pattern === action);
13241
13244
  if (matches(policy.authority.deny)) throw new Error(`Dispatch blocked: authority denies "${action}"`);
@@ -13244,7 +13247,7 @@ function assertAuthorized(policy, constraints, target, approved) {
13244
13247
  const denied = matchingConstraints.find(({ effect }) => effect === "deny");
13245
13248
  if (denied) throw new Error(`Dispatch blocked by constraint "${denied.id}": ${denied.rule}`);
13246
13249
  const requiresApproval = policy.approval === "all-actions" || policy.approval === "risky-actions" && matches(policy.riskyActions) || matchingConstraints.some(({ effect }) => effect === "require-approval");
13247
- if (requiresApproval && !approved) throw new Error(`Dispatch blocked: approval is required for "${action}"`);
13250
+ return requiresApproval;
13248
13251
  }
13249
13252
  function pinned(kind, record2) {
13250
13253
  return { kind, id: record2.definition.id, revision: record2.revision };
@@ -13323,16 +13326,34 @@ async function dispatchAgencyLoopsWith(input) {
13323
13326
  continue;
13324
13327
  }
13325
13328
  const leaseUntil = new Date(input.now.getTime() + 15 * 6e4).toISOString();
13326
- const reservation = await input.backend.reserveAgencyDispatch(
13327
- input.tenantId,
13328
- decision.idempotencyKey,
13329
- record2.definition.id,
13329
+ const reservationId = `reservation-${randomUUID()}`;
13330
+ const correlationId = `corr-${randomUUID()}`;
13331
+ const trace = [policy.trace[0], ...target.intermediate, target.reference];
13332
+ const reservation = await input.backend.reserveAgencyDispatch(input.tenantId, {
13333
+ idempotencyKey: decision.idempotencyKey,
13334
+ loopId: record2.definition.id,
13330
13335
  decision,
13331
13336
  leaseUntil,
13337
+ reservationId,
13338
+ correlationId,
13339
+ policyHash: policy.snapshot.hash,
13340
+ effectivePolicy: policy.snapshot,
13341
+ definitionRefs: trace,
13342
+ maxConcurrentRuns: policy.snapshot.policy.maxConcurrentRuns,
13343
+ requiresApproval: policy.requiresApproval,
13344
+ approvalScopeKind: "loop",
13345
+ approvalScopeId: record2.definition.id,
13346
+ approvalAction: `${target.reference.kind}:${target.reference.id}`,
13332
13347
  now
13333
- );
13348
+ });
13334
13349
  if (!reservation.acquired) {
13335
- results.push({ loopId: record2.definition.id, decision: "duplicate", reason: "trigger firing already reserved" });
13350
+ const duplicate = reservation.reason === "duplicate";
13351
+ const reason = reservation.reason === "approval-required" ? "dispatch is waiting for approval" : reservation.reason === "concurrency-limit" ? "dispatch is waiting for policy capacity" : "trigger firing already reserved";
13352
+ results.push({
13353
+ loopId: record2.definition.id,
13354
+ decision: duplicate ? "duplicate" : "skipped",
13355
+ reason
13356
+ });
13336
13357
  continue;
13337
13358
  }
13338
13359
  const runningState = createLoopState2({
@@ -13345,13 +13366,12 @@ async function dispatchAgencyLoopsWith(input) {
13345
13366
  });
13346
13367
  await repository.saveState(runningState, "loop", now);
13347
13368
  const runId = `run-${randomUUID()}`;
13348
- const correlationId = `corr-${randomUUID()}`;
13349
13369
  const activeRun = createRun({
13350
13370
  id: runId,
13351
13371
  status: "running",
13352
13372
  origin: { kind: "loop", id: record2.definition.id, revision: record2.revision },
13353
13373
  target: target.reference,
13354
- trace: [policy.trace[0], ...target.intermediate, target.reference],
13374
+ trace,
13355
13375
  effectivePolicy: policy.snapshot,
13356
13376
  correlationId,
13357
13377
  startedAt: now
@@ -13375,6 +13395,7 @@ async function dispatchAgencyLoopsWith(input) {
13375
13395
  await input.backend.finishAgencyDispatch(
13376
13396
  input.tenantId,
13377
13397
  decision.idempotencyKey,
13398
+ reservationId,
13378
13399
  succeeded ? "dispatched" : "failed",
13379
13400
  finishedAt,
13380
13401
  runId
@@ -13398,7 +13419,14 @@ async function dispatchAgencyLoopsWith(input) {
13398
13419
  const reason = error instanceof Error ? error.message : String(error);
13399
13420
  const finishedAt = (/* @__PURE__ */ new Date()).toISOString();
13400
13421
  await input.backend.finishAgencyModelRun(input.tenantId, terminalRun(activeRun, "failed", finishedAt), finishedAt).catch(() => void 0);
13401
- await input.backend.finishAgencyDispatch(input.tenantId, decision.idempotencyKey, "failed", finishedAt, runId);
13422
+ await input.backend.finishAgencyDispatch(
13423
+ input.tenantId,
13424
+ decision.idempotencyKey,
13425
+ reservationId,
13426
+ "failed",
13427
+ finishedAt,
13428
+ runId
13429
+ );
13402
13430
  results.push({ loopId: record2.definition.id, decision: "failed", reason });
13403
13431
  }
13404
13432
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kody-ade/kody-engine",
3
- "version": "0.4.415",
3
+ "version": "0.4.416",
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,28 +12,6 @@
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
- "test:all": "vitest run tests --no-coverage",
29
- "typecheck": "tsc --noEmit",
30
- "lint": "biome check",
31
- "lint:fix": "biome check --write",
32
- "format": "biome format --write",
33
- "verify:package": "node scripts/verify-package-tarball.cjs",
34
- "brain:publish": "docker buildx build --platform linux/amd64 -f runner/Dockerfile.brain -t ghcr.io/${KODY_BRAIN_GHCR_OWNER:-aharonyaircohen}/kody-brain:latest --push runner",
35
- "prepublishOnly": "pnpm typecheck && vitest run tests/unit tests/int --no-coverage && pnpm build && pnpm verify:package"
36
- },
37
15
  "dependencies": {
38
16
  "@actions/cache": "^6.0.0",
39
17
  "@anthropic-ai/claude-agent-sdk": "0.2.119",
@@ -59,5 +37,26 @@
59
37
  "url": "git+https://github.com/aharonyaircohen/kody-engine.git"
60
38
  },
61
39
  "homepage": "https://github.com/aharonyaircohen/kody-engine",
62
- "bugs": "https://github.com/aharonyaircohen/kody-engine/issues"
63
- }
40
+ "bugs": "https://github.com/aharonyaircohen/kody-engine/issues",
41
+ "scripts": {
42
+ "kody:run": "tsx bin/kody.ts",
43
+ "serve": "tsx bin/kody.ts serve",
44
+ "serve:vscode": "tsx bin/kody.ts serve vscode",
45
+ "serve:claude": "tsx bin/kody.ts serve claude",
46
+ "clean:dist": "node scripts/clean-dist.cjs",
47
+ "build": "pnpm clean:dist && tsup && node scripts/copy-assets.cjs",
48
+ "check:modularity": "tsx scripts/check-script-modularity.ts",
49
+ "pretest": "pnpm check:modularity",
50
+ "test": "vitest run tests/unit tests/int --coverage",
51
+ "posttest": "tsx scripts/check-coverage-floor.ts",
52
+ "test:smoke": "vitest run tests/smoke --no-coverage",
53
+ "test:e2e": "vitest run tests/e2e --no-coverage",
54
+ "test:all": "vitest run tests --no-coverage",
55
+ "typecheck": "tsc --noEmit",
56
+ "lint": "biome check",
57
+ "lint:fix": "biome check --write",
58
+ "format": "biome format --write",
59
+ "verify:package": "node scripts/verify-package-tarball.cjs",
60
+ "brain:publish": "docker buildx build --platform linux/amd64 -f runner/Dockerfile.brain -t ghcr.io/${KODY_BRAIN_GHCR_OWNER:-aharonyaircohen}/kody-brain:latest --push runner"
61
+ }
62
+ }