@kody-ade/kody-engine 0.4.445 → 0.4.446

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.445",
18
+ version: "0.4.446",
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",
@@ -13798,6 +13798,204 @@ var init_dispatchAgencyLoops = __esm({
13798
13798
  }
13799
13799
  });
13800
13800
 
13801
+ // src/loopDefinitions.ts
13802
+ import * as fs36 from "fs";
13803
+ import * as path34 from "path";
13804
+ function normalizeLoopDefinition(value) {
13805
+ if (!value || typeof value !== "object" || Array.isArray(value)) return null;
13806
+ const raw = value;
13807
+ if (Object.keys(raw).some((key) => !["id", "trigger", "target", "input", "enabled"].includes(key))) return null;
13808
+ if (typeof raw.id !== "string" || !ID.test(raw.id)) return null;
13809
+ if (typeof raw.enabled !== "boolean") return null;
13810
+ if (!isObject(raw.input) || !isObject(raw.trigger) || !isObject(raw.target)) return null;
13811
+ const targetKind = raw.target.kind;
13812
+ const targetId = raw.target.id;
13813
+ if (targetKind !== "workflow" && targetKind !== "capability" || typeof targetId !== "string" || !ID.test(targetId)) {
13814
+ return null;
13815
+ }
13816
+ const trigger = normalizeTrigger(raw.trigger);
13817
+ if (!trigger) return null;
13818
+ return {
13819
+ id: raw.id,
13820
+ trigger,
13821
+ target: { kind: targetKind, id: targetId },
13822
+ input: raw.input,
13823
+ enabled: raw.enabled
13824
+ };
13825
+ }
13826
+ function readLoopDefinition(cwd, id) {
13827
+ if (!ID.test(id)) return null;
13828
+ const roots = [path34.join(cwd, ".kody-engine", "runtime"), definitionsRoot(cwd)];
13829
+ for (const root of roots) {
13830
+ const filePath = path34.join(root, "loops", id, "loop.json");
13831
+ if (!fs36.existsSync(filePath)) continue;
13832
+ try {
13833
+ const loop = normalizeLoopDefinition(JSON.parse(fs36.readFileSync(filePath, "utf8")));
13834
+ if (loop?.id === id) return loop;
13835
+ process.stderr.write(`[kody] invalid simple Loop definition: ${filePath}
13836
+ `);
13837
+ } catch {
13838
+ process.stderr.write(`[kody] unreadable simple Loop definition: ${filePath}
13839
+ `);
13840
+ }
13841
+ }
13842
+ process.stderr.write(
13843
+ `[kody] simple Loop not found: ${id} (${roots.map((root) => path34.join(root, "loops", id, "loop.json")).join(", ")})
13844
+ `
13845
+ );
13846
+ return null;
13847
+ }
13848
+ function listLoopDefinitions(cwd) {
13849
+ const roots = [path34.join(cwd, ".kody-engine", "runtime"), definitionsRoot(cwd)];
13850
+ const byId = /* @__PURE__ */ new Map();
13851
+ for (const root of roots.reverse()) {
13852
+ const loopsDir = path34.join(root, "loops");
13853
+ if (!fs36.existsSync(loopsDir)) continue;
13854
+ for (const id of fs36.readdirSync(loopsDir).sort()) {
13855
+ if (!ID.test(id)) continue;
13856
+ const filePath = path34.join(loopsDir, id, "loop.json");
13857
+ if (!fs36.existsSync(filePath)) continue;
13858
+ try {
13859
+ const loop = normalizeLoopDefinition(JSON.parse(fs36.readFileSync(filePath, "utf8")));
13860
+ if (loop?.id === id) byId.set(id, loop);
13861
+ } catch {
13862
+ process.stderr.write(`[kody] unreadable simple Loop definition: ${filePath}
13863
+ `);
13864
+ }
13865
+ }
13866
+ }
13867
+ return [...byId.values()].sort((left, right) => left.id.localeCompare(right.id));
13868
+ }
13869
+ function normalizeTrigger(raw) {
13870
+ if (raw.type === "manual" && Object.keys(raw).length === 1) return { type: "manual" };
13871
+ if (raw.type === "schedule" && typeof raw.every === "string" && /^\d+[mhd]$/.test(raw.every)) {
13872
+ if (raw.at === void 0 && Object.keys(raw).every((key) => key === "type" || key === "every")) {
13873
+ return { type: "schedule", every: raw.every };
13874
+ }
13875
+ if (isObject(raw.at) && typeof raw.at.time === "string" && typeof raw.at.timezone === "string" && Object.keys(raw.at).every((key) => key === "time" || key === "timezone") && Object.keys(raw).every((key) => key === "type" || key === "every" || key === "at")) {
13876
+ return { type: "schedule", every: raw.every, at: { time: raw.at.time, timezone: raw.at.timezone } };
13877
+ }
13878
+ }
13879
+ if ((raw.type === "event" || raw.type === "webhook") && typeof raw.event === "string" && raw.event.trim() && Object.keys(raw).every((key) => key === "type" || key === "event")) {
13880
+ return { type: raw.type, event: raw.event.trim() };
13881
+ }
13882
+ if (raw.type === "condition" && typeof raw.expression === "string" && raw.expression.trim() && Object.keys(raw).every((key) => key === "type" || key === "expression")) {
13883
+ return { type: "condition", expression: raw.expression.trim() };
13884
+ }
13885
+ return null;
13886
+ }
13887
+ function isObject(value) {
13888
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
13889
+ }
13890
+ var ID;
13891
+ var init_loopDefinitions = __esm({
13892
+ "src/loopDefinitions.ts"() {
13893
+ "use strict";
13894
+ init_definition_paths();
13895
+ ID = /^[a-z0-9][a-z0-9_-]{0,79}$/;
13896
+ }
13897
+ });
13898
+
13899
+ // src/scripts/dispatchSimpleLoops.ts
13900
+ import { randomUUID as randomUUID2 } from "crypto";
13901
+ function dueSlot(loop, now) {
13902
+ if (!loop.enabled || loop.trigger.type !== "schedule") return null;
13903
+ const match = /^(\d+)([mhd])$/.exec(loop.trigger.every);
13904
+ if (!match) return null;
13905
+ const amount = Number(match[1]);
13906
+ const unit = match[2];
13907
+ const milliseconds = amount * (unit === "m" ? 6e4 : unit === "h" ? 36e5 : 864e5);
13908
+ const slot = Math.floor(now.getTime() / milliseconds) * milliseconds;
13909
+ if (!loop.trigger.at) return new Date(slot).toISOString();
13910
+ const parts = new Intl.DateTimeFormat("en-CA", {
13911
+ timeZone: loop.trigger.at.timezone,
13912
+ year: "numeric",
13913
+ month: "2-digit",
13914
+ day: "2-digit",
13915
+ hour: "2-digit",
13916
+ minute: "2-digit",
13917
+ hourCycle: "h23"
13918
+ }).formatToParts(now);
13919
+ const hour = parts.find((part) => part.type === "hour")?.value;
13920
+ const minute = parts.find((part) => part.type === "minute")?.value;
13921
+ const [targetHour, targetMinute] = loop.trigger.at.time.split(":").map(Number);
13922
+ const localMinute = Number(hour) * 60 + Number(minute);
13923
+ const targetLocalMinute = Number(targetHour) * 60 + Number(targetMinute);
13924
+ const windowMinutes = Math.max(1, Math.ceil(Number(process.env.KODY_SCHEDULE_WINDOW_SEC || 300) / 60));
13925
+ if (localMinute < targetLocalMinute || localMinute >= targetLocalMinute + windowMinutes) return null;
13926
+ const year = parts.find((part) => part.type === "year")?.value;
13927
+ const month = parts.find((part) => part.type === "month")?.value;
13928
+ const day = parts.find((part) => part.type === "day")?.value;
13929
+ return `${year}-${month}-${day}T${loop.trigger.at.time}[${loop.trigger.at.timezone}]`;
13930
+ }
13931
+ function loopJob(loop) {
13932
+ const cliArgs = Object.fromEntries(
13933
+ Object.entries(loop.input).map(([key, value]) => [key, typeof value === "string" ? value : JSON.stringify(value)])
13934
+ );
13935
+ return loop.target.kind === "workflow" ? { workflow: loop.target.id, cliArgs, flavor: "scheduled" } : { capability: loop.target.id, cliArgs, flavor: "scheduled" };
13936
+ }
13937
+ function repositoryTenant2(config) {
13938
+ const [envOwner, envRepo] = (process.env.GITHUB_REPOSITORY ?? "").split("/");
13939
+ const owner = config.github?.owner?.trim() || envOwner?.trim();
13940
+ const repo = config.github?.repo?.trim() || envRepo?.trim();
13941
+ return owner && repo ? `${owner}/${repo}` : null;
13942
+ }
13943
+ var dispatchSimpleLoops;
13944
+ var init_dispatchSimpleLoops = __esm({
13945
+ "src/scripts/dispatchSimpleLoops.ts"() {
13946
+ "use strict";
13947
+ init_loopDefinitions();
13948
+ init_job();
13949
+ init_state_backend();
13950
+ dispatchSimpleLoops = async (ctx) => {
13951
+ const tenantId2 = repositoryTenant2(ctx.config);
13952
+ if (!tenantId2) throw new Error("Repository identity is required for Loop dispatch");
13953
+ const now = /* @__PURE__ */ new Date();
13954
+ const due = listLoopDefinitions(ctx.cwd).filter((loop) => dueSlot(loop, now) !== null);
13955
+ const backend = createStateBackendFromEnv();
13956
+ const results = [];
13957
+ for (const loop of due) {
13958
+ const slot = dueSlot(loop, now);
13959
+ if (!slot) continue;
13960
+ const reservationId = `reservation-${randomUUID2()}`;
13961
+ const idempotencyKey = `${loop.id}:${slot}`;
13962
+ const claimed = await backend.reserveAgencyDispatch(tenantId2, {
13963
+ idempotencyKey,
13964
+ loopId: loop.id,
13965
+ decision: { kind: "fire", reason: "local Loop schedule is due", scheduledAt: slot },
13966
+ leaseUntil: new Date(now.getTime() + 6 * 60 * 60 * 1e3).toISOString(),
13967
+ reservationId,
13968
+ correlationId: `corr-${randomUUID2()}`,
13969
+ policyHash: `loop:${loop.id}`,
13970
+ effectivePolicy: { source: "repository" },
13971
+ definitionRefs: [{ kind: "loop", id: loop.id }],
13972
+ maxConcurrentRuns: 1,
13973
+ requiresApproval: false,
13974
+ approvalScopeKind: "loop",
13975
+ approvalScopeId: loop.id,
13976
+ approvalAction: `${loop.target.kind}:${loop.target.id}`,
13977
+ now: now.toISOString()
13978
+ });
13979
+ if (!claimed.acquired) {
13980
+ results.push({ loopId: loop.id, status: "skipped", reason: claimed.reason ?? "already claimed" });
13981
+ continue;
13982
+ }
13983
+ const result = await runJob(loopJob(loop), {
13984
+ cwd: ctx.cwd,
13985
+ config: ctx.config,
13986
+ verbose: ctx.verbose,
13987
+ quiet: ctx.quiet,
13988
+ chain: false
13989
+ });
13990
+ const status = result.exitCode === 0 ? "dispatched" : "failed";
13991
+ await backend.finishAgencyDispatch(tenantId2, idempotencyKey, reservationId, status, (/* @__PURE__ */ new Date()).toISOString());
13992
+ results.push({ loopId: loop.id, status, reason: result.reason ?? status });
13993
+ }
13994
+ ctx.data.simpleLoopDispatchResults = results;
13995
+ };
13996
+ }
13997
+ });
13998
+
13801
13999
  // src/jobIdentity.ts
13802
14000
  function stableJobKey(job) {
13803
14001
  const capability = job.workflow ?? job.capability ?? job.action;
@@ -14818,15 +15016,15 @@ var init_fixFlow = __esm({
14818
15016
  });
14819
15017
 
14820
15018
  // src/workflow-template.ts
14821
- import * as fs36 from "fs";
14822
- import * as path34 from "path";
15019
+ import * as fs37 from "fs";
15020
+ import * as path35 from "path";
14823
15021
  import { fileURLToPath } from "url";
14824
15022
  function loadKodyWorkflowTemplate() {
14825
- const here = path34.dirname(fileURLToPath(import.meta.url));
14826
- const candidates = [path34.resolve(here, "../templates/kody.yml"), path34.resolve(here, "../../templates/kody.yml")];
14827
- const source = candidates.find((candidate) => fs36.existsSync(candidate));
15023
+ const here = path35.dirname(fileURLToPath(import.meta.url));
15024
+ const candidates = [path35.resolve(here, "../templates/kody.yml"), path35.resolve(here, "../../templates/kody.yml")];
15025
+ const source = candidates.find((candidate) => fs37.existsSync(candidate));
14828
15026
  if (!source) throw new Error(`Kody workflow template is missing: ${KODY_WORKFLOW_TEMPLATE_PATH}`);
14829
- return fs36.readFileSync(source, "utf8");
15027
+ return fs37.readFileSync(source, "utf8");
14830
15028
  }
14831
15029
  var KODY_WORKFLOW_TEMPLATE_PATH;
14832
15030
  var init_workflow_template = __esm({
@@ -14838,12 +15036,12 @@ var init_workflow_template = __esm({
14838
15036
 
14839
15037
  // src/scripts/initFlow.ts
14840
15038
  import { execFileSync as execFileSync14 } from "child_process";
14841
- import * as fs37 from "fs";
14842
- import * as path35 from "path";
15039
+ import * as fs38 from "fs";
15040
+ import * as path36 from "path";
14843
15041
  function detectPackageManager(cwd) {
14844
- if (fs37.existsSync(path35.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
14845
- if (fs37.existsSync(path35.join(cwd, "yarn.lock"))) return "yarn";
14846
- if (fs37.existsSync(path35.join(cwd, "bun.lockb"))) return "bun";
15042
+ if (fs38.existsSync(path36.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
15043
+ if (fs38.existsSync(path36.join(cwd, "yarn.lock"))) return "yarn";
15044
+ if (fs38.existsSync(path36.join(cwd, "bun.lockb"))) return "bun";
14847
15045
  return "npm";
14848
15046
  }
14849
15047
  function qualityCommandsFor(pm) {
@@ -14915,22 +15113,22 @@ function performInit(cwd, force) {
14915
15113
  const pm = detectPackageManager(cwd);
14916
15114
  const ownerRepo = detectOwnerRepo(cwd);
14917
15115
  const defaultBranch = defaultBranchFromGit(cwd);
14918
- const configPath = path35.join(cwd, "kody.config.json");
14919
- if (fs37.existsSync(configPath) && !force) {
15116
+ const configPath = path36.join(cwd, "kody.config.json");
15117
+ if (fs38.existsSync(configPath) && !force) {
14920
15118
  skipped.push("kody.config.json");
14921
15119
  } else {
14922
15120
  const cfg = makeConfig(pm, ownerRepo, defaultBranch);
14923
- fs37.writeFileSync(configPath, `${JSON.stringify(cfg, null, 2)}
15121
+ fs38.writeFileSync(configPath, `${JSON.stringify(cfg, null, 2)}
14924
15122
  `);
14925
15123
  wrote.push("kody.config.json");
14926
15124
  }
14927
- const workflowDir = path35.join(cwd, ".github", "workflows");
14928
- const workflowPath = path35.join(workflowDir, "kody.yml");
14929
- if (fs37.existsSync(workflowPath) && !force) {
15125
+ const workflowDir = path36.join(cwd, ".github", "workflows");
15126
+ const workflowPath = path36.join(workflowDir, "kody.yml");
15127
+ if (fs38.existsSync(workflowPath) && !force) {
14930
15128
  skipped.push(".github/workflows/kody.yml");
14931
15129
  } else {
14932
- fs37.mkdirSync(workflowDir, { recursive: true });
14933
- fs37.writeFileSync(workflowPath, loadKodyWorkflowTemplate());
15130
+ fs38.mkdirSync(workflowDir, { recursive: true });
15131
+ fs38.writeFileSync(workflowPath, loadKodyWorkflowTemplate());
14934
15132
  wrote.push(".github/workflows/kody.yml");
14935
15133
  }
14936
15134
  for (const exe of listRuntimeProfilesForCwd(cwd)) {
@@ -14941,12 +15139,12 @@ function performInit(cwd, force) {
14941
15139
  continue;
14942
15140
  }
14943
15141
  if (profile.kind !== "scheduled" || !profile.schedule) continue;
14944
- const target = path35.join(workflowDir, `kody-${exe.name}.yml`);
14945
- if (fs37.existsSync(target) && !force) {
15142
+ const target = path36.join(workflowDir, `kody-${exe.name}.yml`);
15143
+ if (fs38.existsSync(target) && !force) {
14946
15144
  skipped.push(`.github/workflows/kody-${exe.name}.yml`);
14947
15145
  continue;
14948
15146
  }
14949
- fs37.writeFileSync(target, renderScheduledWorkflow(exe.name, profile.schedule));
15147
+ fs38.writeFileSync(target, renderScheduledWorkflow(exe.name, profile.schedule));
14950
15148
  wrote.push(`.github/workflows/kody-${exe.name}.yml`);
14951
15149
  }
14952
15150
  let labels;
@@ -15034,7 +15232,7 @@ Nothing to do. All files already present. (Use --force to overwrite.)
15034
15232
  });
15035
15233
 
15036
15234
  // src/scripts/loadAgentAdhoc.ts
15037
- import * as fs38 from "fs";
15235
+ import * as fs39 from "fs";
15038
15236
  function resolveMessage(messageArg) {
15039
15237
  const fromComment = readCommentBody();
15040
15238
  if (fromComment) return stripDirective(fromComment);
@@ -15042,9 +15240,9 @@ function resolveMessage(messageArg) {
15042
15240
  }
15043
15241
  function readCommentBody() {
15044
15242
  const eventPath = process.env.GITHUB_EVENT_PATH;
15045
- if (!eventPath || !fs38.existsSync(eventPath)) return "";
15243
+ if (!eventPath || !fs39.existsSync(eventPath)) return "";
15046
15244
  try {
15047
- const event = JSON.parse(fs38.readFileSync(eventPath, "utf-8"));
15245
+ const event = JSON.parse(fs39.readFileSync(eventPath, "utf-8"));
15048
15246
  return String(event.comment?.body ?? "");
15049
15247
  } catch {
15050
15248
  return "";
@@ -15098,10 +15296,10 @@ var init_loadAgentAdhoc = __esm({
15098
15296
  throw new Error("loadAgentAdhoc: ctx.args.agent must be a non-empty slug");
15099
15297
  }
15100
15298
  const agentPath = resolveAgentFile(ctx.cwd, agentSlug, agentsDir);
15101
- if (!fs38.existsSync(agentPath)) {
15299
+ if (!fs39.existsSync(agentPath)) {
15102
15300
  throw new Error(`loadAgentAdhoc: agent identity not found: ${agentPath}`);
15103
15301
  }
15104
- const { title, body } = parseAgentFile(fs38.readFileSync(agentPath, "utf-8"), agentSlug);
15302
+ const { title, body } = parseAgentFile(fs39.readFileSync(agentPath, "utf-8"), agentSlug);
15105
15303
  const message = resolveMessage(ctx.args.message);
15106
15304
  if (!message) {
15107
15305
  throw new Error(
@@ -15170,8 +15368,8 @@ var init_loadCapabilityState = __esm({
15170
15368
  });
15171
15369
 
15172
15370
  // src/scripts/loadSimpleCapability.ts
15173
- import * as fs39 from "fs";
15174
- import * as path36 from "path";
15371
+ import * as fs40 from "fs";
15372
+ import * as path37 from "path";
15175
15373
  function parseInput(supplied) {
15176
15374
  if (typeof supplied !== "string") return supplied;
15177
15375
  try {
@@ -15218,14 +15416,14 @@ function capabilityEnvironment(input) {
15218
15416
  return environment;
15219
15417
  }
15220
15418
  function listFiles(root) {
15221
- if (!fs39.existsSync(root)) return [];
15419
+ if (!fs40.existsSync(root)) return [];
15222
15420
  const files = [];
15223
15421
  const visit = (dir) => {
15224
- for (const entry of fs39.readdirSync(dir, { withFileTypes: true })) {
15225
- const absolute = path36.join(dir, entry.name);
15422
+ for (const entry of fs40.readdirSync(dir, { withFileTypes: true })) {
15423
+ const absolute = path37.join(dir, entry.name);
15226
15424
  if (entry.isSymbolicLink()) continue;
15227
15425
  if (entry.isDirectory()) visit(absolute);
15228
- else if (entry.isFile()) files.push(path36.relative(root, absolute));
15426
+ else if (entry.isFile()) files.push(path37.relative(root, absolute));
15229
15427
  }
15230
15428
  };
15231
15429
  visit(root);
@@ -15246,8 +15444,8 @@ var init_loadSimpleCapability = __esm({
15246
15444
  if (!capability) {
15247
15445
  throw new Error(`Capability "${slug}" is not a valid simple capability folder`);
15248
15446
  }
15249
- const toolRoot = path36.join(capability.dir, "tools");
15250
- const skillRoot = path36.join(capability.dir, "skills");
15447
+ const toolRoot = path37.join(capability.dir, "tools");
15448
+ const skillRoot = path37.join(capability.dir, "skills");
15251
15449
  const toolFiles = listFiles(toolRoot);
15252
15450
  const skillFiles = listFiles(skillRoot);
15253
15451
  const input = parseInput(ctx.args.input);
@@ -15271,7 +15469,7 @@ var init_loadSimpleCapability = __esm({
15271
15469
  ...skillFiles.flatMap((file) => [
15272
15470
  `### ${file}`,
15273
15471
  "",
15274
- fs39.readFileSync(path36.join(skillRoot, file), "utf-8"),
15472
+ fs40.readFileSync(path37.join(skillRoot, file), "utf-8"),
15275
15473
  ""
15276
15474
  ])
15277
15475
  ] : [],
@@ -15280,7 +15478,7 @@ var init_loadSimpleCapability = __esm({
15280
15478
  "## Tools",
15281
15479
  "",
15282
15480
  "Inspect or run these capability-owned files when needed:",
15283
- ...toolFiles.map((file) => `- ${path36.join(toolRoot, file)}`)
15481
+ ...toolFiles.map((file) => `- ${path37.join(toolRoot, file)}`)
15284
15482
  ] : []
15285
15483
  ].join("\n");
15286
15484
  };
@@ -15588,8 +15786,8 @@ var init_loadIssueStateComment = __esm({
15588
15786
  });
15589
15787
 
15590
15788
  // src/scripts/loadJobFromFile.ts
15591
- import * as fs40 from "fs";
15592
- import * as path37 from "path";
15789
+ import * as fs41 from "fs";
15790
+ import * as path38 from "path";
15593
15791
  function parseJobFile(raw, slug) {
15594
15792
  let stripped = raw;
15595
15793
  if (stripped.startsWith("---\n")) {
@@ -15628,10 +15826,10 @@ var init_loadJobFromFile = __esm({
15628
15826
  if (!slug) {
15629
15827
  throw new Error(`loadJobFromFile: ctx.args.${slugArg} must be a non-empty slug`);
15630
15828
  }
15631
- const capability = resolveCapabilityFolder(slug, path37.resolve(ctx.cwd, jobsDir));
15829
+ const capability = resolveCapabilityFolder(slug, path38.resolve(ctx.cwd, jobsDir));
15632
15830
  if (!capability) {
15633
15831
  throw new Error(
15634
- `loadJobFromFile: capability folder not found or incomplete: ${path37.resolve(ctx.cwd, jobsDir, slug)}`
15832
+ `loadJobFromFile: capability folder not found or incomplete: ${path38.resolve(ctx.cwd, jobsDir, slug)}`
15635
15833
  );
15636
15834
  }
15637
15835
  const { title, body, config } = capability;
@@ -15641,12 +15839,12 @@ var init_loadJobFromFile = __esm({
15641
15839
  let agentIdentity = "";
15642
15840
  if (agentSlug) {
15643
15841
  const agentPath = resolveAgentFile(ctx.cwd, agentSlug, agentsDir);
15644
- if (!fs40.existsSync(agentPath)) {
15842
+ if (!fs41.existsSync(agentPath)) {
15645
15843
  throw new Error(
15646
15844
  `loadJobFromFile: capability '${slug}' declares agent '${agentSlug}' but ${agentPath} does not exist`
15647
15845
  );
15648
15846
  }
15649
- const agentRaw = fs40.readFileSync(agentPath, "utf-8");
15847
+ const agentRaw = fs41.readFileSync(agentPath, "utf-8");
15650
15848
  const parsed = parseJobFile(agentRaw, agentSlug);
15651
15849
  agentTitle = parsed.title;
15652
15850
  agentIdentity = parsed.body;
@@ -15726,13 +15924,13 @@ ${truncate2(issue2.body, FINDING_BODY_MAX_BYTES)}`;
15726
15924
  });
15727
15925
 
15728
15926
  // src/scripts/kodyVariables.ts
15729
- import * as fs41 from "fs";
15730
- import * as path38 from "path";
15927
+ import * as fs42 from "fs";
15928
+ import * as path39 from "path";
15731
15929
  function readKodyVariables(cwd) {
15732
- const full = path38.join(cwd, KODY_VARIABLES_REL_PATH);
15930
+ const full = path39.join(cwd, KODY_VARIABLES_REL_PATH);
15733
15931
  let raw;
15734
15932
  try {
15735
- raw = fs41.readFileSync(full, "utf-8");
15933
+ raw = fs42.readFileSync(full, "utf-8");
15736
15934
  } catch {
15737
15935
  return {};
15738
15936
  }
@@ -15908,8 +16106,8 @@ var init_runtimeSecrets = __esm({
15908
16106
  });
15909
16107
 
15910
16108
  // src/scripts/loadQaContext.ts
15911
- import * as fs42 from "fs";
15912
- import * as path39 from "path";
16109
+ import * as fs43 from "fs";
16110
+ import * as path40 from "path";
15913
16111
  function parseSlugList(value) {
15914
16112
  const inner = value.startsWith("[") && value.endsWith("]") ? value.slice(1, -1) : value;
15915
16113
  return inner.split(",").map(
@@ -15938,18 +16136,18 @@ function readProfileAgents(raw) {
15938
16136
  return { agent: agent ?? legacy ?? ["kody"], body };
15939
16137
  }
15940
16138
  function readProfile(cwd) {
15941
- const dir = path39.join(cwd, CONTEXT_DIR_REL_PATH);
15942
- if (!fs42.existsSync(dir)) return "";
16139
+ const dir = path40.join(cwd, CONTEXT_DIR_REL_PATH);
16140
+ if (!fs43.existsSync(dir)) return "";
15943
16141
  let entries;
15944
16142
  try {
15945
- entries = fs42.readdirSync(dir).filter((f) => f.endsWith(".md")).sort();
16143
+ entries = fs43.readdirSync(dir).filter((f) => f.endsWith(".md")).sort();
15946
16144
  } catch {
15947
16145
  return "";
15948
16146
  }
15949
16147
  const blocks = [];
15950
16148
  for (const file of entries) {
15951
16149
  try {
15952
- const raw = fs42.readFileSync(path39.join(dir, file), "utf-8");
16150
+ const raw = fs43.readFileSync(path40.join(dir, file), "utf-8");
15953
16151
  const { agent, body } = readProfileAgents(raw);
15954
16152
  if (!agent.includes(QA_AGENT) && !agent.includes(ALL_AGENTS)) continue;
15955
16153
  blocks.push(`## ${file}
@@ -15998,8 +16196,8 @@ var init_loadQaContext = __esm({
15998
16196
  });
15999
16197
 
16000
16198
  // src/taskContext.ts
16001
- import * as fs43 from "fs";
16002
- import * as path40 from "path";
16199
+ import * as fs44 from "fs";
16200
+ import * as path41 from "path";
16003
16201
  function buildTaskContext(args) {
16004
16202
  return {
16005
16203
  schemaVersion: TASK_CONTEXT_SCHEMA_VERSION,
@@ -16015,9 +16213,9 @@ function buildTaskContext(args) {
16015
16213
  function persistTaskContext(cwd, ctx) {
16016
16214
  try {
16017
16215
  const dir = runtimeStatePath(cwd, "agent-runs", ctx.runId);
16018
- fs43.mkdirSync(dir, { recursive: true });
16019
- const file = path40.join(dir, "task-context.json");
16020
- fs43.writeFileSync(file, `${JSON.stringify(ctx, null, 2)}
16216
+ fs44.mkdirSync(dir, { recursive: true });
16217
+ const file = path41.join(dir, "task-context.json");
16218
+ fs44.writeFileSync(file, `${JSON.stringify(ctx, null, 2)}
16021
16219
  `);
16022
16220
  return file;
16023
16221
  } catch (err) {
@@ -16857,7 +17055,7 @@ function parseSingleJsonCandidate(candidates) {
16857
17055
  }
16858
17056
  return parsed.length === 1 ? { found: true, value: parsed[0] } : { found: false };
16859
17057
  }
16860
- function isObject(value) {
17058
+ function isObject2(value) {
16861
17059
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
16862
17060
  }
16863
17061
  function stringValue4(value) {
@@ -16875,8 +17073,8 @@ var init_parseSimpleCapabilityOutput = __esm({
16875
17073
  return;
16876
17074
  }
16877
17075
  ctx.data.capabilityOutput = output;
16878
- const result = isObject(output) ? output : {};
16879
- const data = isObject(result.data) ? result.data : isObject(output) ? output : { output };
17076
+ const result = isObject2(output) ? output : {};
17077
+ const data = isObject2(result.data) ? result.data : isObject2(output) ? output : { output };
16880
17078
  const summary = typeof result.summary === "string" ? result.summary : "Capability completed";
16881
17079
  const reason = typeof result.reason === "string" ? result.reason : summary;
16882
17080
  const prUrl = stringValue4(data.pullRequestUrl) ?? stringValue4(data.prUrl) ?? stringValue4(result.pullRequestUrl) ?? stringValue4(result.prUrl);
@@ -17467,9 +17665,9 @@ var init_postResearchComment = __esm({
17467
17665
  });
17468
17666
 
17469
17667
  // src/scripts/prepareBrowserAuth.ts
17470
- import * as fs44 from "fs";
17668
+ import * as fs45 from "fs";
17471
17669
  import * as os6 from "os";
17472
- import * as path41 from "path";
17670
+ import * as path42 from "path";
17473
17671
  function appendAuthMessage(ctx, message) {
17474
17672
  const current = typeof ctx.data.qaAuthBlock === "string" ? ctx.data.qaAuthBlock.trim() : "";
17475
17673
  ctx.data.qaAuthBlock = current ? `${current}
@@ -17508,9 +17706,9 @@ async function githubJson(url, token) {
17508
17706
  return await response.json();
17509
17707
  }
17510
17708
  function writeKodyStorageState(input) {
17511
- const directory = fs44.mkdtempSync(path41.join(os6.tmpdir(), "kody-browser-auth-"));
17512
- fs44.chmodSync(directory, 448);
17513
- const file = path41.join(directory, "storage-state.json");
17709
+ const directory = fs45.mkdtempSync(path42.join(os6.tmpdir(), "kody-browser-auth-"));
17710
+ fs45.chmodSync(directory, 448);
17711
+ const file = path42.join(directory, "storage-state.json");
17514
17712
  const now = Date.now();
17515
17713
  const repoEntry = {
17516
17714
  repoUrl: input.repoUrl,
@@ -17540,7 +17738,7 @@ function writeKodyStorageState(input) {
17540
17738
  }
17541
17739
  ]
17542
17740
  };
17543
- fs44.writeFileSync(file, JSON.stringify(storageState), { mode: 384 });
17741
+ fs45.writeFileSync(file, JSON.stringify(storageState), { mode: 384 });
17544
17742
  return { directory, file };
17545
17743
  }
17546
17744
  function configurePlaywright(profile, storageStatePath) {
@@ -17622,7 +17820,7 @@ async function prepareMethod(ctx, profile, method) {
17622
17820
  configurePlaywright(profile, state.file);
17623
17821
  const authDirectory = state.directory;
17624
17822
  registerRuntimeCleanup(ctx, () => {
17625
- fs44.rmSync(authDirectory, { recursive: true, force: true });
17823
+ fs45.rmSync(authDirectory, { recursive: true, force: true });
17626
17824
  });
17627
17825
  appendAuthMessage(
17628
17826
  ctx,
@@ -17630,7 +17828,7 @@ async function prepareMethod(ctx, profile, method) {
17630
17828
  );
17631
17829
  return true;
17632
17830
  } catch (error) {
17633
- if (state) fs44.rmSync(state.directory, { recursive: true, force: true });
17831
+ if (state) fs45.rmSync(state.directory, { recursive: true, force: true });
17634
17832
  const reason = error instanceof Error ? error.message : String(error);
17635
17833
  appendAuthMessage(
17636
17834
  ctx,
@@ -18729,12 +18927,12 @@ fi
18729
18927
 
18730
18928
  // src/scripts/runPreviewBuild.ts
18731
18929
  import { copyFile, writeFile } from "fs/promises";
18732
- import * as path42 from "path";
18930
+ import * as path43 from "path";
18733
18931
  import { fileURLToPath as fileURLToPath2 } from "url";
18734
18932
  function bundledDockerfilePath(mode) {
18735
- const here = path42.dirname(fileURLToPath2(import.meta.url));
18933
+ const here = path43.dirname(fileURLToPath2(import.meta.url));
18736
18934
  const file = mode === "dev" ? "default-Dockerfile.preview.dev" : "default-Dockerfile.preview.prod";
18737
- return path42.join(here, "preview-build-templates", file);
18935
+ return path43.join(here, "preview-build-templates", file);
18738
18936
  }
18739
18937
  function required(name) {
18740
18938
  const v = (process.env[name] ?? "").trim();
@@ -18969,10 +19167,10 @@ var init_runPreviewBuild = __esm({
18969
19167
  console.log(`[preview-build] vault: ${Object.keys(buildEnv).length} secrets, mode=${buildMode}`);
18970
19168
  if (Object.keys(buildEnv).length > 0) {
18971
19169
  const lines = Object.entries(buildEnv).map(([k, v]) => `${k}=${JSON.stringify(v)}`);
18972
- await writeFile(path42.join(ctx.cwd, ".env.production.local"), `${lines.join("\n")}
19170
+ await writeFile(path43.join(ctx.cwd, ".env.production.local"), `${lines.join("\n")}
18973
19171
  `, "utf8");
18974
19172
  }
18975
- const consumerDockerfile = path42.join(ctx.cwd, "Dockerfile.preview");
19173
+ const consumerDockerfile = path43.join(ctx.cwd, "Dockerfile.preview");
18976
19174
  const { stat } = await import("fs/promises");
18977
19175
  let hasConsumerDockerfile = false;
18978
19176
  try {
@@ -19156,8 +19354,8 @@ var init_tickShellRunner = __esm({
19156
19354
  });
19157
19355
 
19158
19356
  // src/scripts/runScheduledImplementationTick.ts
19159
- import * as fs45 from "fs";
19160
- import * as path43 from "path";
19357
+ import * as fs46 from "fs";
19358
+ import * as path44 from "path";
19161
19359
  var runScheduledImplementationTick;
19162
19360
  var init_runScheduledImplementationTick = __esm({
19163
19361
  "src/scripts/runScheduledImplementationTick.ts"() {
@@ -19178,14 +19376,14 @@ var init_runScheduledImplementationTick = __esm({
19178
19376
  ctx.output.reason = `runScheduledImplementationTick: args.slug or ctx.args.${slugArg} must be non-empty capability slug`;
19179
19377
  return;
19180
19378
  }
19181
- const capability = resolveCapabilityFolder(slug, path43.resolve(ctx.cwd, jobsDir));
19379
+ const capability = resolveCapabilityFolder(slug, path44.resolve(ctx.cwd, jobsDir));
19182
19380
  if (!capability) {
19183
19381
  ctx.output.exitCode = 99;
19184
19382
  ctx.output.reason = `runScheduledImplementationTick: capability folder not found or incomplete: ${slug} (searched ${jobsDir} and company store)`;
19185
19383
  return;
19186
19384
  }
19187
- const shellPath = path43.join(profile.dir, shell);
19188
- if (!fs45.existsSync(shellPath)) {
19385
+ const shellPath = path44.join(profile.dir, shell);
19386
+ if (!fs46.existsSync(shellPath)) {
19189
19387
  ctx.output.exitCode = 99;
19190
19388
  ctx.output.reason = `runScheduledImplementationTick: shell not found: ${shell} (looked in ${profile.dir})`;
19191
19389
  return;
@@ -19216,8 +19414,8 @@ var init_runScheduledImplementationTick = __esm({
19216
19414
  });
19217
19415
 
19218
19416
  // src/scripts/runTickScript.ts
19219
- import * as fs46 from "fs";
19220
- import * as path44 from "path";
19417
+ import * as fs47 from "fs";
19418
+ import * as path45 from "path";
19221
19419
  var runTickScript;
19222
19420
  var init_runTickScript = __esm({
19223
19421
  "src/scripts/runTickScript.ts"() {
@@ -19237,10 +19435,10 @@ var init_runTickScript = __esm({
19237
19435
  ctx.output.reason = `runTickScript: ctx.args.${slugArg} must be a non-empty slug`;
19238
19436
  return;
19239
19437
  }
19240
- const capability = readCapabilityFolder(path44.resolve(ctx.cwd, jobsDir), slug);
19438
+ const capability = readCapabilityFolder(path45.resolve(ctx.cwd, jobsDir), slug);
19241
19439
  if (!capability) {
19242
19440
  ctx.output.exitCode = 99;
19243
- ctx.output.reason = `runTickScript: capability folder not found or incomplete: ${path44.resolve(ctx.cwd, jobsDir, slug)}`;
19441
+ ctx.output.reason = `runTickScript: capability folder not found or incomplete: ${path45.resolve(ctx.cwd, jobsDir, slug)}`;
19244
19442
  return;
19245
19443
  }
19246
19444
  const tickScript = capability.config.tickScript;
@@ -19249,8 +19447,8 @@ var init_runTickScript = __esm({
19249
19447
  ctx.output.reason = `runTickScript: capability ${slug} has no \`tickScript\` in profile.json \u2014 route via capability-tick instead`;
19250
19448
  return;
19251
19449
  }
19252
- const scriptPath = path44.isAbsolute(tickScript) ? tickScript : path44.join(ctx.cwd, tickScript);
19253
- if (!fs46.existsSync(scriptPath)) {
19450
+ const scriptPath = path45.isAbsolute(tickScript) ? tickScript : path45.join(ctx.cwd, tickScript);
19451
+ if (!fs47.existsSync(scriptPath)) {
19254
19452
  ctx.output.exitCode = 99;
19255
19453
  ctx.output.reason = `runTickScript: tickScript not found: ${scriptPath}`;
19256
19454
  return;
@@ -19532,7 +19730,7 @@ var init_syncFlow = __esm({
19532
19730
  });
19533
19731
 
19534
19732
  // src/scripts/validateAgencyModelProposal.ts
19535
- import * as path45 from "path";
19733
+ import * as path46 from "path";
19536
19734
  function validateModelBundle(bundle, expectedKind, options = {}) {
19537
19735
  const failures = [];
19538
19736
  validateOneModel(bundle.model, bundle.files, "model", true, failures, expectedKind, options);
@@ -19850,7 +20048,7 @@ var init_validateAgencyModelProposal = __esm({
19850
20048
  const bundle = parseAgencyModelProposal(raw);
19851
20049
  const expectedKind = readExpectedModelKind(args);
19852
20050
  const failures = validateModelBundle(bundle, expectedKind, {
19853
- capabilityRoot: path45.join(ctx.cwd, ".kody", "capabilities")
20051
+ capabilityRoot: path46.join(ctx.cwd, ".kody", "capabilities")
19854
20052
  });
19855
20053
  if (failures.length > 0) {
19856
20054
  throw new Error(`validateAgencyModelProposal: ${failures.join("; ")}`);
@@ -20401,7 +20599,7 @@ var init_warmupMcp = __esm({
20401
20599
  });
20402
20600
 
20403
20601
  // src/scripts/writeAgentRunSummary.ts
20404
- import * as fs47 from "fs";
20602
+ import * as fs48 from "fs";
20405
20603
  var writeAgentRunSummary;
20406
20604
  var init_writeAgentRunSummary = __esm({
20407
20605
  "src/scripts/writeAgentRunSummary.ts"() {
@@ -20427,7 +20625,7 @@ var init_writeAgentRunSummary = __esm({
20427
20625
  if (reason) lines.push(`- **Reason:** ${reason}`);
20428
20626
  lines.push("");
20429
20627
  try {
20430
- fs47.appendFileSync(summaryPath, `${lines.join("\n")}
20628
+ fs48.appendFileSync(summaryPath, `${lines.join("\n")}
20431
20629
  `);
20432
20630
  } catch {
20433
20631
  }
@@ -20564,6 +20762,7 @@ var init_scripts = __esm({
20564
20762
  init_dispatchCapabilityTicks();
20565
20763
  init_dispatchClassified();
20566
20764
  init_dispatchAgencyLoops();
20765
+ init_dispatchSimpleLoops();
20567
20766
  init_dispatchNextTaskJob();
20568
20767
  init_ensurePr();
20569
20768
  init_evaluateAgencyBoundaries();
@@ -20688,6 +20887,7 @@ var init_scripts = __esm({
20688
20887
  warmupMcp,
20689
20888
  dispatchCapabilityTicks,
20690
20889
  dispatchAgencyLoops,
20890
+ dispatchSimpleLoops,
20691
20891
  dispatchCapabilityFileTicks,
20692
20892
  planTaskJobs,
20693
20893
  dispatchNextTaskJob,
@@ -20759,17 +20959,17 @@ var init_scripts = __esm({
20759
20959
  });
20760
20960
 
20761
20961
  // src/stateWorkspace.ts
20762
- import * as fs48 from "fs";
20763
- import * as path46 from "path";
20962
+ import * as fs49 from "fs";
20963
+ import * as path47 from "path";
20764
20964
  function tenantId(config) {
20765
20965
  const owner = config.github?.owner?.trim() || process.env.GITHUB_REPOSITORY?.split("/")[0]?.trim();
20766
20966
  const repo = config.github?.repo?.trim() || process.env.GITHUB_REPOSITORY?.split("/")[1]?.trim();
20767
20967
  return owner && repo ? `${owner}/${repo}` : null;
20768
20968
  }
20769
20969
  function writeRuntimeFile(cwd, relativePath, content) {
20770
- const target = path46.join(cwd, RUNTIME_ROOT, relativePath);
20771
- fs48.mkdirSync(path46.dirname(target), { recursive: true });
20772
- fs48.writeFileSync(target, content, "utf8");
20970
+ const target = path47.join(cwd, RUNTIME_ROOT, relativePath);
20971
+ fs49.mkdirSync(path47.dirname(target), { recursive: true });
20972
+ fs49.writeFileSync(target, content, "utf8");
20773
20973
  }
20774
20974
  function record(value) {
20775
20975
  return value && typeof value === "object" && !Array.isArray(value) ? value : null;
@@ -20834,11 +21034,11 @@ async function hydrateStateWorkspace(config, cwd, backendOverride) {
20834
21034
  throw new Error("Kody backend access is required for runtime workspace documents");
20835
21035
  return;
20836
21036
  }
20837
- const key = `${path46.resolve(cwd)}|${tenant}`;
21037
+ const key = `${path47.resolve(cwd)}|${tenant}`;
20838
21038
  if (hydratedWorkspaces.has(key)) return;
20839
21039
  const backend = backendOverride ?? createStateBackendFromEnv();
20840
- const root = path46.join(cwd, RUNTIME_ROOT);
20841
- fs48.rmSync(root, { recursive: true, force: true });
21040
+ const root = path47.join(cwd, RUNTIME_ROOT);
21041
+ fs49.rmSync(root, { recursive: true, force: true });
20842
21042
  await Promise.all([
20843
21043
  hydratePrefix(backend, tenant, cwd, "context:"),
20844
21044
  hydratePrefix(backend, tenant, cwd, "memory:"),
@@ -20854,7 +21054,7 @@ var init_stateWorkspace = __esm({
20854
21054
  "src/stateWorkspace.ts"() {
20855
21055
  "use strict";
20856
21056
  init_state_backend();
20857
- RUNTIME_ROOT = path46.join(".kody-engine", "runtime");
21057
+ RUNTIME_ROOT = path47.join(".kody-engine", "runtime");
20858
21058
  hydratedWorkspaces = /* @__PURE__ */ new Set();
20859
21059
  }
20860
21060
  });
@@ -20925,9 +21125,9 @@ var init_tools = __esm({
20925
21125
 
20926
21126
  // src/executor.ts
20927
21127
  import { spawn as spawn8 } from "child_process";
20928
- import * as fs49 from "fs";
21128
+ import * as fs50 from "fs";
20929
21129
  import * as os7 from "os";
20930
- import * as path47 from "path";
21130
+ import * as path48 from "path";
20931
21131
  function isMutatingPostflight(scriptName) {
20932
21132
  return MUTATING_POSTFLIGHTS.has(scriptName ?? "");
20933
21133
  }
@@ -21168,7 +21368,7 @@ async function runImplementation(profileName, input) {
21168
21368
  const jobWhyBlock = typeof ctx.data.jobWhy === "string" ? operatorRequestBlock(ctx.data.jobWhy) : null;
21169
21369
  const jobRefBlock = jobReferenceBlock(profileName, profile, ctx.data);
21170
21370
  const invokeAgent = async (prompt) => {
21171
- const externalPlugins = (profile.claudeCode.plugins ?? []).map((p) => path47.isAbsolute(p) ? p : path47.resolve(profile.dir, p)).filter((p) => p.length > 0);
21371
+ const externalPlugins = (profile.claudeCode.plugins ?? []).map((p) => path48.isAbsolute(p) ? p : path48.resolve(profile.dir, p)).filter((p) => p.length > 0);
21172
21372
  const syntheticPath = ctx.data.syntheticPluginPath;
21173
21373
  const pluginPaths = [...externalPlugins, ...syntheticPath ? [syntheticPath] : []];
21174
21374
  const agents = loadSubagents(profile);
@@ -21632,17 +21832,17 @@ function clearStampedLifecycleLabels(profile, ctx) {
21632
21832
  function resolveProfilePath(profileName, cwd = process.cwd()) {
21633
21833
  const found = resolveImplementation(profileName, getRuntimeProfileRootsForCwd(cwd));
21634
21834
  if (found) return found;
21635
- const here = path47.dirname(new URL(import.meta.url).pathname);
21835
+ const here = path48.dirname(new URL(import.meta.url).pathname);
21636
21836
  const candidates = [
21637
- path47.join(here, "implementations", profileName, "profile.json"),
21837
+ path48.join(here, "implementations", profileName, "profile.json"),
21638
21838
  // same-dir sibling (dev)
21639
- path47.join(here, "..", "implementations", profileName, "profile.json"),
21839
+ path48.join(here, "..", "implementations", profileName, "profile.json"),
21640
21840
  // up one (prod: dist/bin → dist/implementations)
21641
- path47.join(here, "..", "src", "implementations", profileName, "profile.json")
21841
+ path48.join(here, "..", "src", "implementations", profileName, "profile.json")
21642
21842
  // fallback
21643
21843
  ];
21644
21844
  for (const c of candidates) {
21645
- if (fs49.existsSync(c)) return c;
21845
+ if (fs50.existsSync(c)) return c;
21646
21846
  }
21647
21847
  return candidates[0];
21648
21848
  }
@@ -21757,15 +21957,15 @@ function resolveShellTimeoutMs(entry) {
21757
21957
  }
21758
21958
  async function runShellEntry(entry, ctx, profile) {
21759
21959
  const shellName = entry.shell;
21760
- const shellPath = path47.join(profile.dir, shellName);
21761
- if (!fs49.existsSync(shellPath)) {
21960
+ const shellPath = path48.join(profile.dir, shellName);
21961
+ if (!fs50.existsSync(shellPath)) {
21762
21962
  ctx.skipAgent = true;
21763
21963
  ctx.output.exitCode = 99;
21764
21964
  ctx.output.reason = `shell script not found: ${shellName} (looked in ${profile.dir})`;
21765
21965
  return;
21766
21966
  }
21767
21967
  const positional = entry.with ? Object.values(entry.with).map((v) => String(v)) : [];
21768
- const outputFile = path47.join(
21968
+ const outputFile = path48.join(
21769
21969
  os7.tmpdir(),
21770
21970
  `kody-shell-output-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`
21771
21971
  );
@@ -21837,9 +22037,9 @@ async function runShellEntry(entry, ctx, profile) {
21837
22037
  }
21838
22038
  let sideChannelText = "";
21839
22039
  try {
21840
- if (fs49.existsSync(outputFile)) {
21841
- sideChannelText = fs49.readFileSync(outputFile, "utf-8");
21842
- fs49.rmSync(outputFile, { force: true });
22040
+ if (fs50.existsSync(outputFile)) {
22041
+ sideChannelText = fs50.readFileSync(outputFile, "utf-8");
22042
+ fs50.rmSync(outputFile, { force: true });
21843
22043
  }
21844
22044
  } catch {
21845
22045
  }
@@ -22011,7 +22211,7 @@ __export(job_exports, {
22011
22211
  stableJobKey: () => stableJobKey,
22012
22212
  validateJob: () => validateJob
22013
22213
  });
22014
- import * as path48 from "path";
22214
+ import * as path49 from "path";
22015
22215
  function newJobId(flavor) {
22016
22216
  localJobSeq += 1;
22017
22217
  const runId = process.env.GITHUB_RUN_ID;
@@ -22712,7 +22912,7 @@ function loadCapabilityContext(slug, cwd) {
22712
22912
  return resolveCapabilityFolder(slug, hydratedCapabilitiesRoot(cwd));
22713
22913
  }
22714
22914
  function hydratedCapabilitiesRoot(cwd) {
22715
- return path48.join(cwd, ".kody-engine", "definitions", "capabilities");
22915
+ return path49.join(cwd, ".kody-engine", "definitions", "capabilities");
22716
22916
  }
22717
22917
  function loadWorkflowContext(slug, base) {
22718
22918
  if (!slug || !isWorkflowDefinitionId(slug)) return null;
@@ -24688,82 +24888,7 @@ function readRunRequestFromEnv(env = process.env) {
24688
24888
 
24689
24889
  // src/kody-cli.ts
24690
24890
  init_runtimePaths();
24691
-
24692
- // src/loopDefinitions.ts
24693
- init_definition_paths();
24694
- import * as fs50 from "fs";
24695
- import * as path49 from "path";
24696
- var ID = /^[a-z0-9][a-z0-9_-]{0,79}$/;
24697
- function normalizeLoopDefinition(value) {
24698
- if (!value || typeof value !== "object" || Array.isArray(value)) return null;
24699
- const raw = value;
24700
- if (Object.keys(raw).some((key) => !["id", "trigger", "target", "input", "enabled"].includes(key))) return null;
24701
- if (typeof raw.id !== "string" || !ID.test(raw.id)) return null;
24702
- if (typeof raw.enabled !== "boolean") return null;
24703
- if (!isObject2(raw.input) || !isObject2(raw.trigger) || !isObject2(raw.target)) return null;
24704
- const targetKind = raw.target.kind;
24705
- const targetId = raw.target.id;
24706
- if (targetKind !== "workflow" && targetKind !== "capability" || typeof targetId !== "string" || !ID.test(targetId)) {
24707
- return null;
24708
- }
24709
- const trigger = normalizeTrigger(raw.trigger);
24710
- if (!trigger) return null;
24711
- return {
24712
- id: raw.id,
24713
- trigger,
24714
- target: { kind: targetKind, id: targetId },
24715
- input: raw.input,
24716
- enabled: raw.enabled
24717
- };
24718
- }
24719
- function readLoopDefinition(cwd, id) {
24720
- if (!ID.test(id)) return null;
24721
- const roots = [
24722
- path49.join(cwd, ".kody-engine", "runtime"),
24723
- definitionsRoot(cwd)
24724
- ];
24725
- for (const root of roots) {
24726
- const filePath = path49.join(root, "loops", id, "loop.json");
24727
- if (!fs50.existsSync(filePath)) continue;
24728
- try {
24729
- const loop = normalizeLoopDefinition(JSON.parse(fs50.readFileSync(filePath, "utf8")));
24730
- if (loop?.id === id) return loop;
24731
- process.stderr.write(`[kody] invalid simple Loop definition: ${filePath}
24732
- `);
24733
- } catch {
24734
- process.stderr.write(`[kody] unreadable simple Loop definition: ${filePath}
24735
- `);
24736
- }
24737
- }
24738
- process.stderr.write(
24739
- `[kody] simple Loop not found: ${id} (${roots.map((root) => path49.join(root, "loops", id, "loop.json")).join(", ")})
24740
- `
24741
- );
24742
- return null;
24743
- }
24744
- function normalizeTrigger(raw) {
24745
- if (raw.type === "manual" && Object.keys(raw).length === 1) return { type: "manual" };
24746
- if (raw.type === "schedule" && typeof raw.every === "string" && /^\d+[mhd]$/.test(raw.every)) {
24747
- if (raw.at === void 0 && Object.keys(raw).every((key) => key === "type" || key === "every")) {
24748
- return { type: "schedule", every: raw.every };
24749
- }
24750
- if (isObject2(raw.at) && typeof raw.at.time === "string" && typeof raw.at.timezone === "string" && Object.keys(raw.at).every((key) => key === "time" || key === "timezone") && Object.keys(raw).every((key) => key === "type" || key === "every" || key === "at")) {
24751
- return { type: "schedule", every: raw.every, at: { time: raw.at.time, timezone: raw.at.timezone } };
24752
- }
24753
- }
24754
- if ((raw.type === "event" || raw.type === "webhook") && typeof raw.event === "string" && raw.event.trim() && Object.keys(raw).every((key) => key === "type" || key === "event")) {
24755
- return { type: raw.type, event: raw.event.trim() };
24756
- }
24757
- if (raw.type === "condition" && typeof raw.expression === "string" && raw.expression.trim() && Object.keys(raw).every((key) => key === "type" || key === "expression")) {
24758
- return { type: "condition", expression: raw.expression.trim() };
24759
- }
24760
- return null;
24761
- }
24762
- function isObject2(value) {
24763
- return Boolean(value) && typeof value === "object" && !Array.isArray(value);
24764
- }
24765
-
24766
- // src/kody-cli.ts
24891
+ init_loopDefinitions();
24767
24892
  init_stateWorkspace();
24768
24893
  init_workflowDefinitions();
24769
24894
  var FAILED_DISPATCH_LABEL = {
@@ -26445,7 +26570,7 @@ init_config();
26445
26570
  init_fetchRepoMcp();
26446
26571
 
26447
26572
  // src/servers/mcpHttpServer.ts
26448
- import { randomUUID as randomUUID2 } from "crypto";
26573
+ import { randomUUID as randomUUID3 } from "crypto";
26449
26574
  import { createServer as createServer4 } from "http";
26450
26575
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
26451
26576
  import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
@@ -26454,7 +26579,7 @@ function buildMcpHttpServer(opts) {
26454
26579
  const transports = /* @__PURE__ */ new Map();
26455
26580
  for (const route of opts.routes) {
26456
26581
  const transport = new StreamableHTTPServerTransport({
26457
- sessionIdGenerator: () => randomUUID2()
26582
+ sessionIdGenerator: () => randomUUID3()
26458
26583
  });
26459
26584
  transports.set(route.path, transport);
26460
26585
  routes.set(route.path, route.name);
@@ -1,8 +1,5 @@
1
1
  {
2
2
  "internal": true,
3
- "role": "watch",
4
- "kind": "scheduled",
5
- "schedule": "*/5 * * * *",
6
3
  "claudeCode": {
7
4
  "model": "inherit",
8
5
  "permissionMode": "default",
@@ -33,9 +30,6 @@
33
30
  "outputArtifacts": [],
34
31
  "scripts": {
35
32
  "preflight": [
36
- {
37
- "script": "dispatchAgencyLoops"
38
- },
39
33
  {
40
34
  "shell": "scheduler.sh",
41
35
  "timeoutSec": 1800
@@ -0,0 +1,32 @@
1
+ {
2
+ "internal": true,
3
+ "role": "watch",
4
+ "kind": "scheduled",
5
+ "schedule": "*/5 * * * *",
6
+ "claudeCode": {
7
+ "model": "inherit",
8
+ "permissionMode": "default",
9
+ "maxTurns": 0,
10
+ "maxThinkingTokens": null,
11
+ "systemPromptAppend": null,
12
+ "tools": [],
13
+ "hooks": [],
14
+ "skills": [],
15
+ "commands": [],
16
+ "subagents": [],
17
+ "plugins": [],
18
+ "mcpServers": []
19
+ },
20
+ "cliTools": [],
21
+ "inputArtifacts": [],
22
+ "outputArtifacts": [],
23
+ "scripts": {
24
+ "preflight": [
25
+ { "script": "dispatchSimpleLoops" },
26
+ { "script": "skipAgent" }
27
+ ],
28
+ "postflight": []
29
+ },
30
+ "inputs": [],
31
+ "name": "loop-scheduler"
32
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kody-ade/kody-engine",
3
- "version": "0.4.445",
3
+ "version": "0.4.446",
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",