@kody-ade/kody-engine 0.4.415 → 0.4.417

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.415",
18
+ version: "0.4.417",
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",
@@ -52,7 +52,7 @@ var init_package = __esm({
52
52
  dependencies: {
53
53
  "@actions/cache": "^6.0.0",
54
54
  "@anthropic-ai/claude-agent-sdk": "0.2.119",
55
- "@kody-ade/agency-domain": "0.4.0",
55
+ "@kody-ade/agency-domain": "0.5.0",
56
56
  "@modelcontextprotocol/sdk": "^1.29.0",
57
57
  convex: "^1.17.0",
58
58
  zod: "^4.0.0"
@@ -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") } : {}
@@ -3413,6 +3415,7 @@ async function runAgent(opts) {
3413
3415
  let outcomeKind = "generic_failed";
3414
3416
  let errorMessage2;
3415
3417
  let tokens = { input: 0, output: 0, cacheRead: 0, cacheCreate: 0 };
3418
+ let costUsd = 0;
3416
3419
  let messageCount = 0;
3417
3420
  let finalText = "";
3418
3421
  let getSubmitted;
@@ -3429,6 +3432,7 @@ async function runAgent(opts) {
3429
3432
  outcomeKind = "generic_failed";
3430
3433
  errorMessage2 = void 0;
3431
3434
  tokens = { input: 0, output: 0, cacheRead: 0, cacheCreate: 0 };
3435
+ costUsd = 0;
3432
3436
  messageCount = 0;
3433
3437
  let sawMutatingTool = false;
3434
3438
  let sawTerminalSuccess = false;
@@ -3553,6 +3557,7 @@ async function runAgent(opts) {
3553
3557
  };
3554
3558
  }
3555
3559
  queryOptions.settingSources = opts.settingSources ?? ["project", "local"];
3560
+ if (opts.abortController) queryOptions.abortController = opts.abortController;
3556
3561
  const stableBinary = ensureStableClaudeBinary();
3557
3562
  if (stableBinary) {
3558
3563
  const sdkBinaryPathOption = ["pathToClaudeCode", "Exec", "utable"].join("");
@@ -3681,6 +3686,8 @@ async function runAgent(opts) {
3681
3686
  }
3682
3687
  }
3683
3688
  if (m.type === "result") {
3689
+ const reportedCost = Number(m.total_cost_usd ?? 0);
3690
+ if (Number.isFinite(reportedCost) && reportedCost >= 0) costUsd = reportedCost;
3684
3691
  if (m.subtype === "success") {
3685
3692
  outcome = "completed";
3686
3693
  outcomeKind = "ok";
@@ -3758,6 +3765,7 @@ async function runAgent(opts) {
3758
3765
  ndjsonPath,
3759
3766
  durationMs: Date.now() - startedAt,
3760
3767
  tokens,
3768
+ costUsd,
3761
3769
  messageCount
3762
3770
  };
3763
3771
  }
@@ -13036,7 +13044,6 @@ function addDefinition(catalog, document) {
13036
13044
  }
13037
13045
  function add(collection, definition, revision) {
13038
13046
  const mutable = collection;
13039
- if (mutable.has(definition.id)) throw new Error(`Duplicate Agency Definition: ${definition.id}`);
13040
13047
  mutable.set(definition.id, { definition, revision });
13041
13048
  }
13042
13049
  function validateRelationships(catalog) {
@@ -13096,7 +13103,10 @@ var init_agencyModelRepository = __esm({
13096
13103
  async loadCatalog() {
13097
13104
  const documents = await this.backend.listAgencyDefinitions(this.tenantId);
13098
13105
  const catalog = emptyCatalog();
13099
- for (const document of documents) addDefinition(catalog, document);
13106
+ const ordered = [...documents].sort(
13107
+ (left, right) => left.createdAt.localeCompare(right.createdAt) || left.recordId.localeCompare(right.recordId)
13108
+ );
13109
+ for (const document of ordered) addDefinition(catalog, document);
13100
13110
  validateRelationships(catalog);
13101
13111
  return catalog;
13102
13112
  }
@@ -13199,7 +13209,7 @@ function resolveDispatchPolicy(input) {
13199
13209
  });
13200
13210
  const policy = mergePolicies(intents.map(({ definition }) => definition.policy));
13201
13211
  const constraints = intents.flatMap(({ definition }) => definition.constraints);
13202
- assertAuthorized(policy, constraints, input.target, input.approved === true);
13212
+ const requiresApproval = assertAuthorized(policy, constraints, input.target);
13203
13213
  const snapshotValue = { policy, constraints };
13204
13214
  return {
13205
13215
  snapshot: {
@@ -13211,7 +13221,8 @@ function resolveDispatchPolicy(input) {
13211
13221
  trace: [
13212
13222
  pinned("trigger" in input.owner.definition ? "loop" : "goal", input.owner),
13213
13223
  input.target
13214
- ]
13224
+ ],
13225
+ requiresApproval
13215
13226
  };
13216
13227
  }
13217
13228
  function mergePolicies(policies) {
@@ -13235,7 +13246,7 @@ function mergePolicies(policies) {
13235
13246
  riskyActions: unique(policies.flatMap(({ riskyActions }) => riskyActions))
13236
13247
  };
13237
13248
  }
13238
- function assertAuthorized(policy, constraints, target, approved) {
13249
+ function assertAuthorized(policy, constraints, target) {
13239
13250
  const action = `${target.kind}:${target.id}`;
13240
13251
  const matches = (patterns) => patterns.some((pattern) => pattern === "*" || pattern === target.id || pattern === action);
13241
13252
  if (matches(policy.authority.deny)) throw new Error(`Dispatch blocked: authority denies "${action}"`);
@@ -13244,7 +13255,7 @@ function assertAuthorized(policy, constraints, target, approved) {
13244
13255
  const denied = matchingConstraints.find(({ effect }) => effect === "deny");
13245
13256
  if (denied) throw new Error(`Dispatch blocked by constraint "${denied.id}": ${denied.rule}`);
13246
13257
  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}"`);
13258
+ return requiresApproval;
13248
13259
  }
13249
13260
  function pinned(kind, record2) {
13250
13261
  return { kind, id: record2.definition.id, revision: record2.revision };
@@ -13322,17 +13333,47 @@ async function dispatchAgencyLoopsWith(input) {
13322
13333
  results.push({ loopId: record2.definition.id, decision: "skipped", reason });
13323
13334
  continue;
13324
13335
  }
13325
- 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,
13336
+ const failurePolicy = record2.definition.reconciliationPolicy.failure;
13337
+ const budget = policy.snapshot.policy.budget;
13338
+ const maxAttempts = Math.min(failurePolicy.maxAttempts, budget.maxRuns);
13339
+ const timeoutSeconds = Math.min(failurePolicy.timeoutSeconds, budget.maxDurationSeconds);
13340
+ const backoffBudgetSeconds = Array.from(
13341
+ { length: Math.max(0, maxAttempts - 1) },
13342
+ (_, index) => failurePolicy.backoffSeconds * 2 ** index
13343
+ ).reduce((sum, seconds) => sum + seconds, 0);
13344
+ const leaseSeconds = Math.min(
13345
+ budget.maxDurationSeconds,
13346
+ maxAttempts * timeoutSeconds + backoffBudgetSeconds
13347
+ );
13348
+ const leaseUntil = new Date(input.now.getTime() + leaseSeconds * 1e3).toISOString();
13349
+ const reservationId = `reservation-${randomUUID()}`;
13350
+ const correlationId = `corr-${randomUUID()}`;
13351
+ const trace = [policy.trace[0], ...target.intermediate, target.reference];
13352
+ const reservation = await input.backend.reserveAgencyDispatch(input.tenantId, {
13353
+ idempotencyKey: decision.idempotencyKey,
13354
+ loopId: record2.definition.id,
13330
13355
  decision,
13331
13356
  leaseUntil,
13357
+ reservationId,
13358
+ correlationId,
13359
+ policyHash: policy.snapshot.hash,
13360
+ effectivePolicy: policy.snapshot,
13361
+ definitionRefs: trace,
13362
+ maxConcurrentRuns: policy.snapshot.policy.maxConcurrentRuns,
13363
+ requiresApproval: policy.requiresApproval,
13364
+ approvalScopeKind: "loop",
13365
+ approvalScopeId: record2.definition.id,
13366
+ approvalAction: `${target.reference.kind}:${target.reference.id}`,
13332
13367
  now
13333
- );
13368
+ });
13334
13369
  if (!reservation.acquired) {
13335
- results.push({ loopId: record2.definition.id, decision: "duplicate", reason: "trigger firing already reserved" });
13370
+ const duplicate = reservation.reason === "duplicate";
13371
+ 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";
13372
+ results.push({
13373
+ loopId: record2.definition.id,
13374
+ decision: duplicate ? "duplicate" : "skipped",
13375
+ reason
13376
+ });
13336
13377
  continue;
13337
13378
  }
13338
13379
  const runningState = createLoopState2({
@@ -13344,40 +13385,82 @@ async function dispatchAgencyLoopsWith(input) {
13344
13385
  updatedAt: now
13345
13386
  });
13346
13387
  await repository.saveState(runningState, "loop", now);
13347
- const runId = `run-${randomUUID()}`;
13348
- const correlationId = `corr-${randomUUID()}`;
13349
- const activeRun = createRun({
13350
- id: runId,
13351
- status: "running",
13352
- origin: { kind: "loop", id: record2.definition.id, revision: record2.revision },
13353
- target: target.reference,
13354
- trace: [policy.trace[0], ...target.intermediate, target.reference],
13355
- effectivePolicy: policy.snapshot,
13356
- correlationId,
13357
- startedAt: now
13358
- });
13359
13388
  try {
13360
- await input.backend.createAgencyModelRun(
13361
- input.tenantId,
13362
- target.reference.kind,
13363
- target.reference.id,
13364
- activeRun,
13365
- now
13366
- );
13367
- const output = await input.run(target.job);
13368
- const succeeded = output.exitCode === 0;
13389
+ let attempts = 0;
13390
+ let tokens = 0;
13391
+ let costUsd = 0;
13392
+ let succeeded = false;
13393
+ let reason = "target failed";
13394
+ let finalRunId;
13395
+ const budgetDeadline = Date.now() + budget.maxDurationSeconds * 1e3;
13396
+ for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
13397
+ const remainingMilliseconds = budgetDeadline - Date.now();
13398
+ if (remainingMilliseconds <= 0) {
13399
+ reason = "policy duration budget exhausted";
13400
+ break;
13401
+ }
13402
+ attempts = attempt;
13403
+ const startedAt = (/* @__PURE__ */ new Date()).toISOString();
13404
+ const runId = `run-${randomUUID()}`;
13405
+ finalRunId = runId;
13406
+ const activeRun = createRun({
13407
+ id: runId,
13408
+ status: "running",
13409
+ origin: { kind: "loop", id: record2.definition.id, revision: record2.revision },
13410
+ target: target.reference,
13411
+ trace,
13412
+ effectivePolicy: policy.snapshot,
13413
+ correlationId,
13414
+ startedAt
13415
+ });
13416
+ await input.backend.createAgencyModelRun(
13417
+ input.tenantId,
13418
+ target.reference.kind,
13419
+ target.reference.id,
13420
+ activeRun,
13421
+ startedAt
13422
+ );
13423
+ const attemptResult = await runAttempt(
13424
+ input.run,
13425
+ target.job,
13426
+ Math.min(timeoutSeconds, remainingMilliseconds / 1e3)
13427
+ );
13428
+ tokens += attemptResult.usage?.tokens ?? 0;
13429
+ costUsd += attemptResult.usage?.costUsd ?? 0;
13430
+ const finishedAt2 = (/* @__PURE__ */ new Date()).toISOString();
13431
+ succeeded = attemptResult.exitCode === 0;
13432
+ reason = attemptResult.reason ?? (succeeded ? "target dispatched" : "target failed");
13433
+ const usage = {
13434
+ tokens: attemptResult.usage?.tokens ?? 0,
13435
+ costUsd: attemptResult.usage?.costUsd ?? 0,
13436
+ durationSeconds: Math.max(0, (Date.parse(finishedAt2) - Date.parse(startedAt)) / 1e3)
13437
+ };
13438
+ if (tokens > budget.maxTokens || costUsd > budget.maxCostUsd) {
13439
+ succeeded = false;
13440
+ reason = tokens > budget.maxTokens ? "policy token budget exhausted" : "policy cost budget exhausted";
13441
+ }
13442
+ await input.backend.finishAgencyModelRun(
13443
+ input.tenantId,
13444
+ terminalRun(activeRun, succeeded ? "succeeded" : "failed", finishedAt2, usage),
13445
+ finishedAt2
13446
+ );
13447
+ if (tokens > budget.maxTokens || costUsd > budget.maxCostUsd) break;
13448
+ if (succeeded || attempt === maxAttempts) break;
13449
+ const backoffMilliseconds = failurePolicy.backoffSeconds * 2 ** (attempt - 1) * 1e3;
13450
+ if (Date.now() + backoffMilliseconds >= budgetDeadline) {
13451
+ reason = "policy duration budget exhausted during retry backoff";
13452
+ break;
13453
+ }
13454
+ await wait(backoffMilliseconds);
13455
+ }
13369
13456
  const finishedAt = (/* @__PURE__ */ new Date()).toISOString();
13370
- await input.backend.finishAgencyModelRun(
13371
- input.tenantId,
13372
- terminalRun(activeRun, succeeded ? "succeeded" : "failed", finishedAt),
13373
- finishedAt
13374
- );
13375
13457
  await input.backend.finishAgencyDispatch(
13376
13458
  input.tenantId,
13377
13459
  decision.idempotencyKey,
13378
- succeeded ? "dispatched" : "failed",
13460
+ reservationId,
13461
+ succeeded ? "dispatched" : "dead-letter",
13379
13462
  finishedAt,
13380
- runId
13463
+ finalRunId
13381
13464
  );
13382
13465
  await repository.saveState(
13383
13466
  createLoopState2({
@@ -13392,13 +13475,18 @@ async function dispatchAgencyLoopsWith(input) {
13392
13475
  results.push({
13393
13476
  loopId: record2.definition.id,
13394
13477
  decision: succeeded ? "dispatched" : "failed",
13395
- reason: output.reason ?? (succeeded ? "target dispatched" : "target failed")
13478
+ reason: succeeded ? reason : `${reason}; dead-lettered after ${attempts} attempt${attempts === 1 ? "" : "s"}`
13396
13479
  });
13397
13480
  } catch (error) {
13398
13481
  const reason = error instanceof Error ? error.message : String(error);
13399
13482
  const finishedAt = (/* @__PURE__ */ new Date()).toISOString();
13400
- 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);
13483
+ await input.backend.finishAgencyDispatch(
13484
+ input.tenantId,
13485
+ decision.idempotencyKey,
13486
+ reservationId,
13487
+ "dead-letter",
13488
+ finishedAt
13489
+ );
13402
13490
  results.push({ loopId: record2.definition.id, decision: "failed", reason });
13403
13491
  }
13404
13492
  }
@@ -13421,8 +13509,32 @@ function resolveTarget(loop, catalog) {
13421
13509
  job: target.kind === "workflow" ? { workflow: target.id, cliArgs: {}, flavor: "scheduled" } : { capability: target.id, cliArgs: {}, flavor: "scheduled" }
13422
13510
  };
13423
13511
  }
13424
- function terminalRun(active, status, finishedAt) {
13425
- return createRun({ ...active, status, finishedAt });
13512
+ function terminalRun(active, status, finishedAt, usage) {
13513
+ return createRun({ ...active, status, finishedAt, usage });
13514
+ }
13515
+ async function runAttempt(run, job, timeoutSeconds) {
13516
+ const abortController = new AbortController();
13517
+ let timer;
13518
+ try {
13519
+ return await Promise.race([
13520
+ run(job, abortController),
13521
+ new Promise((resolve17) => {
13522
+ timer = setTimeout(() => {
13523
+ abortController.abort();
13524
+ resolve17({ exitCode: 124, reason: `target timed out after ${formatSeconds(timeoutSeconds)}s` });
13525
+ }, timeoutSeconds * 1e3);
13526
+ })
13527
+ ]);
13528
+ } finally {
13529
+ if (timer) clearTimeout(timer);
13530
+ }
13531
+ }
13532
+ function formatSeconds(seconds) {
13533
+ return Number.isInteger(seconds) ? String(seconds) : seconds.toFixed(3).replace(/0+$/, "").replace(/\.$/, "");
13534
+ }
13535
+ async function wait(milliseconds) {
13536
+ if (milliseconds <= 0) return;
13537
+ await new Promise((resolve17) => setTimeout(resolve17, milliseconds));
13426
13538
  }
13427
13539
  function repositoryTenant(config) {
13428
13540
  const owner = config.github?.owner?.trim() || process.env.GITHUB_REPOSITORY?.split("/")[0]?.trim();
@@ -13446,7 +13558,14 @@ var init_dispatchAgencyLoops = __esm({
13446
13558
  tenantId: tenantId2,
13447
13559
  backend,
13448
13560
  now: /* @__PURE__ */ new Date(),
13449
- run: (job) => runJob(job, { cwd: ctx.cwd, config: ctx.config, verbose: ctx.verbose, quiet: ctx.quiet, chain: false })
13561
+ run: (job, abortController) => runJob(job, {
13562
+ cwd: ctx.cwd,
13563
+ config: ctx.config,
13564
+ verbose: ctx.verbose,
13565
+ quiet: ctx.quiet,
13566
+ chain: false,
13567
+ abortController
13568
+ })
13450
13569
  });
13451
13570
  ctx.data.agencyLoopDispatchResults = results;
13452
13571
  };
@@ -20658,6 +20777,7 @@ async function runImplementation(profileName, input) {
20658
20777
  isBackendHealthy: lm ? () => lm.isHealthy() : void 0,
20659
20778
  verbose: input.verbose,
20660
20779
  quiet: input.quiet,
20780
+ abortController: input.abortController,
20661
20781
  ndjsonDir,
20662
20782
  additionalDirectories: agentTaskArtifacts ? [agentTaskArtifacts.absDir] : void 0,
20663
20783
  allowedToolsOverride: profile.claudeCode.tools,
@@ -20767,6 +20887,10 @@ async function runImplementation(profileName, input) {
20767
20887
  reason: err instanceof Error ? err.message : String(err)
20768
20888
  });
20769
20889
  }
20890
+ ctx.output.usage = {
20891
+ tokens: agentResult.tokens ? agentResult.tokens.input + agentResult.tokens.output + agentResult.tokens.cacheRead + agentResult.tokens.cacheCreate : 0,
20892
+ costUsd: agentResult.costUsd ?? 0
20893
+ };
20770
20894
  emitEvent(input.cwd, {
20771
20895
  implementation: profileName,
20772
20896
  kind: "agent_end",
@@ -21609,6 +21733,7 @@ async function runCapabilityImplementationStep(valid, profileName, capabilityIde
21609
21733
  skipConfig: base.skipConfig,
21610
21734
  verbose: base.verbose,
21611
21735
  quiet: base.quiet,
21736
+ abortController: base.abortController,
21612
21737
  preloadedData: Object.keys(preloadedData).length > 0 ? preloadedData : void 0
21613
21738
  };
21614
21739
  const shouldApplyResolvedCapabilityArgs = valid.implementation === void 0 && resolvedCapability && profileName === resolvedCapability.implementation;
@@ -453,6 +453,7 @@ export interface Context {
453
453
  exitCode: number
454
454
  prUrl?: string
455
455
  reason?: string
456
+ usage?: { tokens: number; costUsd: number }
456
457
  /**
457
458
  * In-process hand-off to the next stage. A stage (e.g. `classify`) sets
458
459
  * this so the orchestrator runs the chosen sub-orchestrator
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.417",
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,32 +12,10 @@
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",
40
- "@kody-ade/agency-domain": "0.4.0",
18
+ "@kody-ade/agency-domain": "0.5.0",
41
19
  "@modelcontextprotocol/sdk": "^1.29.0",
42
20
  "convex": "^1.17.0",
43
21
  "zod": "^4.0.0"
@@ -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
+ }