@kody-ade/kody-engine 0.4.552 → 0.4.554

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.552",
18
+ version: "0.4.554",
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",
@@ -668,6 +668,111 @@ var init_format = __esm({
668
668
  }
669
669
  });
670
670
 
671
+ // src/agency/capability-contract-validation.ts
672
+ import Ajv from "ajv";
673
+ function validateCapabilityContractValue(boundary, schema, value) {
674
+ const validate = validator.compile(schema);
675
+ if (!validate(value)) {
676
+ throw new CapabilityContractValidationError(boundary, validate.errors ?? []);
677
+ }
678
+ }
679
+ function capabilityContractInput(inputs, args, capabilityId, contractProperties = []) {
680
+ const isGenericRunnerInput = inputs.some((input) => input.name === "input") && Object.hasOwn(args, "input");
681
+ if (!isGenericRunnerInput) {
682
+ const isParameterlessGenericRunner = (inputs.some((input) => input.name === "capability") || args.capability === capabilityId || !contractProperties.includes("capability")) && Object.hasOwn(args, "capability");
683
+ if (!isParameterlessGenericRunner) return args;
684
+ const { capability: _routingCapability, ...businessArgs } = args;
685
+ return businessArgs;
686
+ }
687
+ const value = args.input;
688
+ if (typeof value !== "string") return value;
689
+ try {
690
+ return JSON.parse(value);
691
+ } catch {
692
+ return value;
693
+ }
694
+ }
695
+ var validator, CapabilityContractValidationError;
696
+ var init_capability_contract_validation = __esm({
697
+ "src/agency/capability-contract-validation.ts"() {
698
+ "use strict";
699
+ validator = new Ajv({
700
+ allErrors: true,
701
+ strict: true,
702
+ validateFormats: false
703
+ });
704
+ CapabilityContractValidationError = class extends Error {
705
+ constructor(boundary, errors) {
706
+ const details = errors.map((error) => {
707
+ const location = error.instancePath || "$";
708
+ const property = error.keyword === "additionalProperties" && typeof error.params.additionalProperty === "string" ? ` (${error.params.additionalProperty})` : "";
709
+ return `${location}: ${error.message ?? error.keyword}${property}`;
710
+ }).join("; ");
711
+ super(`Capability ${boundary} does not match its declared contract: ${details}`);
712
+ this.boundary = boundary;
713
+ this.errors = errors;
714
+ this.name = "CapabilityContractValidationError";
715
+ }
716
+ boundary;
717
+ errors;
718
+ };
719
+ }
720
+ });
721
+
722
+ // src/outputContractHooks.ts
723
+ import * as fs3 from "fs";
724
+ import * as path3 from "path";
725
+ function outputContractError(contract) {
726
+ let value;
727
+ try {
728
+ value = JSON.parse(fs3.readFileSync(contract.path, "utf8"));
729
+ } catch (error) {
730
+ return `The required output file is missing or is not valid JSON: ${error instanceof Error ? error.message : String(error)}`;
731
+ }
732
+ try {
733
+ validateCapabilityContractValue("output", contract.schema, value);
734
+ return null;
735
+ } catch (error) {
736
+ return error instanceof Error ? error.message : String(error);
737
+ }
738
+ }
739
+ function correctionMessage(contract, error) {
740
+ return `The authoritative output does not match its required contract: ${error}. Please overwrite ${contract.path} with only the required JSON shape before finishing.`;
741
+ }
742
+ function createOutputContractPostWriteHook(contract) {
743
+ const expectedPath = path3.resolve(contract.path);
744
+ return async (input) => {
745
+ const toolInput = input.tool_input;
746
+ if (!toolInput || typeof toolInput !== "object" || Array.isArray(toolInput)) return {};
747
+ const filePath = toolInput.file_path;
748
+ if (typeof filePath !== "string" || path3.resolve(filePath) !== expectedPath) return {};
749
+ const error = outputContractError(contract);
750
+ if (!error) return {};
751
+ return {
752
+ hookSpecificOutput: {
753
+ hookEventName: "PostToolUse",
754
+ additionalContext: correctionMessage(contract, error)
755
+ }
756
+ };
757
+ };
758
+ }
759
+ function createOutputContractStopHook(contract) {
760
+ return async () => {
761
+ const error = outputContractError(contract);
762
+ if (!error) return {};
763
+ return {
764
+ decision: "block",
765
+ reason: correctionMessage(contract, error)
766
+ };
767
+ };
768
+ }
769
+ var init_outputContractHooks = __esm({
770
+ "src/outputContractHooks.ts"() {
771
+ "use strict";
772
+ init_capability_contract_validation();
773
+ }
774
+ });
775
+
671
776
  // src/runtimePaths.ts
672
777
  var runtimePaths_exports = {};
673
778
  __export(runtimePaths_exports, {
@@ -678,21 +783,21 @@ __export(runtimePaths_exports, {
678
783
  });
679
784
  import { createHash } from "crypto";
680
785
  import * as os2 from "os";
681
- import * as path3 from "path";
786
+ import * as path4 from "path";
682
787
  function runtimeDirForCwd(cwd, ...parts) {
683
- const key = createHash("sha256").update(path3.resolve(cwd)).digest("hex").slice(0, 16);
684
- return path3.join(os2.tmpdir(), "kody-engine", key, ...parts);
788
+ const key = createHash("sha256").update(path4.resolve(cwd)).digest("hex").slice(0, 16);
789
+ return path4.join(os2.tmpdir(), "kody-engine", key, ...parts);
685
790
  }
686
791
  function runtimeStatePath(cwd, ...parts) {
687
792
  const configuredRoot = process.env.KODY_RUNTIME_DIR?.trim();
688
- const base = configuredRoot ? path3.resolve(configuredRoot) : runtimeDirForCwd(cwd);
689
- return path3.join(base, ...parts);
793
+ const base = configuredRoot ? path4.resolve(configuredRoot) : runtimeDirForCwd(cwd);
794
+ return path4.join(base, ...parts);
690
795
  }
691
796
  function agentRunDir(cwd) {
692
797
  return runtimeStatePath(cwd, "agent-runs");
693
798
  }
694
799
  function lastRunLogPath(cwd) {
695
- return path3.join(agentRunDir(cwd), "last-run.jsonl");
800
+ return path4.join(agentRunDir(cwd), "last-run.jsonl");
696
801
  }
697
802
  var init_runtimePaths = __esm({
698
803
  "src/runtimePaths.ts"() {
@@ -701,31 +806,31 @@ var init_runtimePaths = __esm({
701
806
  });
702
807
 
703
808
  // src/scripts/buildSyntheticPlugin.ts
704
- import * as fs3 from "fs";
809
+ import * as fs4 from "fs";
705
810
  import * as os3 from "os";
706
- import * as path4 from "path";
811
+ import * as path5 from "path";
707
812
  function getPluginsCatalogRoot() {
708
- const here = path4.dirname(new URL(import.meta.url).pathname);
813
+ const here = path5.dirname(new URL(import.meta.url).pathname);
709
814
  const candidates = [
710
- path4.join(here, "..", "plugins"),
815
+ path5.join(here, "..", "plugins"),
711
816
  // dev: src/scripts → src/plugins
712
- path4.join(here, "..", "..", "plugins"),
817
+ path5.join(here, "..", "..", "plugins"),
713
818
  // built: dist/scripts → dist/plugins
714
- path4.join(here, "..", "..", "src", "plugins")
819
+ path5.join(here, "..", "..", "src", "plugins")
715
820
  // fallback
716
821
  ];
717
822
  for (const c of candidates) {
718
- if (fs3.existsSync(c) && fs3.statSync(c).isDirectory()) return c;
823
+ if (fs4.existsSync(c) && fs4.statSync(c).isDirectory()) return c;
719
824
  }
720
825
  return candidates[0];
721
826
  }
722
827
  function copyDir(src, dst) {
723
- fs3.mkdirSync(dst, { recursive: true });
724
- for (const ent of fs3.readdirSync(src, { withFileTypes: true })) {
725
- const s = path4.join(src, ent.name);
726
- const d = path4.join(dst, ent.name);
828
+ fs4.mkdirSync(dst, { recursive: true });
829
+ for (const ent of fs4.readdirSync(src, { withFileTypes: true })) {
830
+ const s = path5.join(src, ent.name);
831
+ const d = path5.join(dst, ent.name);
727
832
  if (ent.isDirectory()) copyDir(s, d);
728
- else if (ent.isFile()) fs3.copyFileSync(s, d);
833
+ else if (ent.isFile()) fs4.copyFileSync(s, d);
729
834
  }
730
835
  }
731
836
  var buildSyntheticPlugin;
@@ -738,47 +843,47 @@ var init_buildSyntheticPlugin = __esm({
738
843
  if (!needsSynthetic) return;
739
844
  const catalog = getPluginsCatalogRoot();
740
845
  const runId = `${profile.name}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
741
- const root = path4.join(os3.tmpdir(), `kody-synth-${runId}`);
742
- fs3.mkdirSync(path4.join(root, ".claude-plugin"), { recursive: true });
846
+ const root = path5.join(os3.tmpdir(), `kody-synth-${runId}`);
847
+ fs4.mkdirSync(path5.join(root, ".claude-plugin"), { recursive: true });
743
848
  const resolvePart = (bucket, entry) => {
744
- const local = path4.join(profile.dir, bucket, entry);
745
- if (fs3.existsSync(local)) return local;
746
- const shared = path4.resolve(profile.dir, "..", "..", "shared", bucket, entry);
747
- if (fs3.existsSync(shared)) return shared;
748
- const central = path4.join(catalog, bucket, entry);
749
- if (fs3.existsSync(central)) return central;
849
+ const local = path5.join(profile.dir, bucket, entry);
850
+ if (fs4.existsSync(local)) return local;
851
+ const shared = path5.resolve(profile.dir, "..", "..", "shared", bucket, entry);
852
+ if (fs4.existsSync(shared)) return shared;
853
+ const central = path5.join(catalog, bucket, entry);
854
+ if (fs4.existsSync(central)) return central;
750
855
  throw new Error(
751
- `buildSyntheticPlugin: ${bucket} entry '${entry}' not found in implementation dir (${profile.dir}/${bucket}/), Store shared assets (${path4.dirname(shared)}/), or catalog (${catalog}/${bucket}/)`
856
+ `buildSyntheticPlugin: ${bucket} entry '${entry}' not found in implementation dir (${profile.dir}/${bucket}/), Store shared assets (${path5.dirname(shared)}/), or catalog (${catalog}/${bucket}/)`
752
857
  );
753
858
  };
754
859
  if (cc.skills.length > 0) {
755
- const dst = path4.join(root, "skills");
756
- fs3.mkdirSync(dst, { recursive: true });
860
+ const dst = path5.join(root, "skills");
861
+ fs4.mkdirSync(dst, { recursive: true });
757
862
  for (const name of cc.skills) {
758
- copyDir(resolvePart("skills", name), path4.join(dst, name));
863
+ copyDir(resolvePart("skills", name), path5.join(dst, name));
759
864
  }
760
865
  }
761
866
  if (cc.commands.length > 0) {
762
- const dst = path4.join(root, "commands");
763
- fs3.mkdirSync(dst, { recursive: true });
867
+ const dst = path5.join(root, "commands");
868
+ fs4.mkdirSync(dst, { recursive: true });
764
869
  for (const name of cc.commands) {
765
- fs3.copyFileSync(resolvePart("commands", `${name}.md`), path4.join(dst, `${name}.md`));
870
+ fs4.copyFileSync(resolvePart("commands", `${name}.md`), path5.join(dst, `${name}.md`));
766
871
  }
767
872
  }
768
873
  if (cc.hooks.length > 0) {
769
- const dst = path4.join(root, "hooks");
770
- fs3.mkdirSync(dst, { recursive: true });
874
+ const dst = path5.join(root, "hooks");
875
+ fs4.mkdirSync(dst, { recursive: true });
771
876
  const merged = { hooks: {} };
772
877
  for (const name of cc.hooks) {
773
878
  const src = resolvePart("hooks", `${name}.json`);
774
- const parsed = JSON.parse(fs3.readFileSync(src, "utf-8"));
879
+ const parsed = JSON.parse(fs4.readFileSync(src, "utf-8"));
775
880
  for (const [event, entries] of Object.entries(parsed.hooks ?? {})) {
776
881
  if (!Array.isArray(entries)) continue;
777
882
  if (!merged.hooks[event]) merged.hooks[event] = [];
778
883
  merged.hooks[event].push(...entries);
779
884
  }
780
885
  }
781
- fs3.writeFileSync(path4.join(dst, "hooks.json"), `${JSON.stringify(merged, null, 2)}
886
+ fs4.writeFileSync(path5.join(dst, "hooks.json"), `${JSON.stringify(merged, null, 2)}
782
887
  `);
783
888
  }
784
889
  const manifest = {
@@ -788,7 +893,7 @@ var init_buildSyntheticPlugin = __esm({
788
893
  };
789
894
  if (cc.skills.length > 0) manifest.skills = ["./skills/"];
790
895
  if (cc.commands.length > 0) manifest.commands = ["./commands/"];
791
- fs3.writeFileSync(path4.join(root, ".claude-plugin", "plugin.json"), `${JSON.stringify(manifest, null, 2)}
896
+ fs4.writeFileSync(path5.join(root, ".claude-plugin", "plugin.json"), `${JSON.stringify(manifest, null, 2)}
792
897
  `);
793
898
  ctx.data.syntheticPluginPath = root;
794
899
  };
@@ -796,8 +901,8 @@ var init_buildSyntheticPlugin = __esm({
796
901
  });
797
902
 
798
903
  // src/subagents.ts
799
- import * as fs4 from "fs";
800
- import * as path5 from "path";
904
+ import * as fs5 from "fs";
905
+ import * as path6 from "path";
801
906
  async function enforceSubagentModelInheritance(input) {
802
907
  const toolInput = input.tool_input;
803
908
  if (!toolInput || typeof toolInput !== "object" || Array.isArray(toolInput)) return {};
@@ -832,12 +937,12 @@ function splitFrontmatter(raw) {
832
937
  return { fm, body: (match[2] ?? "").trim() };
833
938
  }
834
939
  function resolveAgentFile(profileDir, name) {
835
- const local = path5.join(profileDir, "agents", `${name}.md`);
836
- if (fs4.existsSync(local)) return local;
837
- const shared = path5.resolve(profileDir, "..", "..", "shared", "agents", `${name}.md`);
838
- if (fs4.existsSync(shared)) return shared;
839
- const central = path5.join(getPluginsCatalogRoot(), "agents", `${name}.md`);
840
- if (fs4.existsSync(central)) return central;
940
+ const local = path6.join(profileDir, "agents", `${name}.md`);
941
+ if (fs5.existsSync(local)) return local;
942
+ const shared = path6.resolve(profileDir, "..", "..", "shared", "agents", `${name}.md`);
943
+ if (fs5.existsSync(shared)) return shared;
944
+ const central = path6.join(getPluginsCatalogRoot(), "agents", `${name}.md`);
945
+ if (fs5.existsSync(central)) return central;
841
946
  throw new Error(
842
947
  `loadSubagents: agent '${name}' not found in ${profileDir}/agents/, hydrated shared assets, or engine catalog`
843
948
  );
@@ -848,7 +953,7 @@ function captureSubagentTemplates(profile) {
848
953
  const out = {};
849
954
  for (const name of names) {
850
955
  try {
851
- out[name] = fs4.readFileSync(resolveAgentFile(profile.dir, name), "utf-8");
956
+ out[name] = fs5.readFileSync(resolveAgentFile(profile.dir, name), "utf-8");
852
957
  } catch {
853
958
  }
854
959
  }
@@ -859,7 +964,7 @@ function loadSubagents(profile) {
859
964
  if (!names || names.length === 0) return void 0;
860
965
  const agents = {};
861
966
  for (const name of names) {
862
- const raw = profile.subagentTemplates?.[name] ?? fs4.readFileSync(resolveAgentFile(profile.dir, name), "utf-8");
967
+ const raw = profile.subagentTemplates?.[name] ?? fs5.readFileSync(resolveAgentFile(profile.dir, name), "utf-8");
863
968
  const { fm, body } = splitFrontmatter(raw);
864
969
  if (!body) throw new Error(`loadSubagents: agent '${name}' has an empty prompt body`);
865
970
  const def = {
@@ -892,8 +997,8 @@ __export(events_exports, {
892
997
  resolveRunId: () => resolveRunId
893
998
  });
894
999
  import * as crypto from "crypto";
895
- import * as fs5 from "fs";
896
- import * as path6 from "path";
1000
+ import * as fs6 from "fs";
1001
+ import * as path7 from "path";
897
1002
  function resolveRunId() {
898
1003
  if (process.env.KODY_RUN_ID) {
899
1004
  cachedRunId = process.env.KODY_RUN_ID;
@@ -926,16 +1031,16 @@ function emitEvent(cwd, ev) {
926
1031
  ...ev
927
1032
  };
928
1033
  const file = eventsPath(cwd, runId);
929
- fs5.mkdirSync(path6.dirname(file), { recursive: true });
930
- fs5.appendFileSync(file, `${JSON.stringify(fullEvent)}
1034
+ fs6.mkdirSync(path7.dirname(file), { recursive: true });
1035
+ fs6.appendFileSync(file, `${JSON.stringify(fullEvent)}
931
1036
  `);
932
1037
  } catch {
933
1038
  }
934
1039
  }
935
1040
  function readEvents(cwd, runId) {
936
1041
  const file = eventsPath(cwd, runId);
937
- if (!fs5.existsSync(file)) return [];
938
- const lines = fs5.readFileSync(file, "utf-8").split("\n");
1042
+ if (!fs6.existsSync(file)) return [];
1043
+ const lines = fs6.readFileSync(file, "utf-8").split("\n");
939
1044
  const out = [];
940
1045
  for (const line of lines) {
941
1046
  const trimmed = line.trim();
@@ -949,10 +1054,10 @@ function readEvents(cwd, runId) {
949
1054
  }
950
1055
  function listRuns(cwd) {
951
1056
  const runsDir = runtimeStatePath(cwd, "agent-runs");
952
- if (!fs5.existsSync(runsDir)) return [];
953
- return fs5.readdirSync(runsDir).filter((name) => {
1057
+ if (!fs6.existsSync(runsDir)) return [];
1058
+ return fs6.readdirSync(runsDir).filter((name) => {
954
1059
  try {
955
- return fs5.statSync(path6.join(runsDir, name)).isDirectory();
1060
+ return fs6.statSync(path7.join(runsDir, name)).isDirectory();
956
1061
  } catch {
957
1062
  return false;
958
1063
  }
@@ -989,7 +1094,7 @@ function buildVerifyEnv(source = process.env) {
989
1094
  return env;
990
1095
  }
991
1096
  function runCommand(command, cwd) {
992
- return new Promise((resolve20) => {
1097
+ return new Promise((resolve21) => {
993
1098
  const start = Date.now();
994
1099
  const child = spawn(command, {
995
1100
  cwd,
@@ -1018,11 +1123,11 @@ function runCommand(command, cwd) {
1018
1123
  child.on("exit", (code) => {
1019
1124
  clearTimeout(timer);
1020
1125
  const tail = Buffer.concat(buffers).toString("utf-8").slice(-TAIL_CHARS);
1021
- resolve20({ exitCode: code ?? -1, durationMs: Date.now() - start, tail });
1126
+ resolve21({ exitCode: code ?? -1, durationMs: Date.now() - start, tail });
1022
1127
  });
1023
1128
  child.on("error", (err) => {
1024
1129
  clearTimeout(timer);
1025
- resolve20({ exitCode: -1, durationMs: Date.now() - start, tail: err.message });
1130
+ resolve21({ exitCode: -1, durationMs: Date.now() - start, tail: err.message });
1026
1131
  });
1027
1132
  });
1028
1133
  }
@@ -1331,7 +1436,7 @@ function cmsHeaders(opts) {
1331
1436
  }
1332
1437
  };
1333
1438
  }
1334
- async function callDashboardCms(opts, path54, init = {}) {
1439
+ async function callDashboardCms(opts, path55, init = {}) {
1335
1440
  const baseUrl = dashboardBaseUrl(opts);
1336
1441
  if (!baseUrl) {
1337
1442
  return {
@@ -1343,7 +1448,7 @@ async function callDashboardCms(opts, path54, init = {}) {
1343
1448
  const headerResult = cmsHeaders(opts);
1344
1449
  if (!headerResult.ok) return headerResult;
1345
1450
  try {
1346
- const res = await fetch(`${baseUrl}${path54}`, {
1451
+ const res = await fetch(`${baseUrl}${path55}`, {
1347
1452
  ...init,
1348
1453
  headers: {
1349
1454
  ...headerResult.headers,
@@ -1415,8 +1520,8 @@ function documentArg(value) {
1415
1520
  function normalizeCmsDocumentIdInput(input) {
1416
1521
  const trimmed = stripWrappingQuotes(input.trim());
1417
1522
  const withoutQuery = trimmed.split(/[?#]/, 1)[0] ?? trimmed;
1418
- const path54 = parseDocumentPath(withoutQuery);
1419
- return path54 ?? parseDocumentIdSegment(withoutQuery) ?? withoutQuery;
1523
+ const path55 = parseDocumentPath(withoutQuery);
1524
+ return path55 ?? parseDocumentIdSegment(withoutQuery) ?? withoutQuery;
1420
1525
  }
1421
1526
  function stripWrappingQuotes(value) {
1422
1527
  let current = value;
@@ -1427,9 +1532,9 @@ function stripWrappingQuotes(value) {
1427
1532
  }
1428
1533
  }
1429
1534
  function parseDocumentPath(value) {
1430
- const path54 = value.startsWith("http://") || value.startsWith("https://") ? urlPathname(value) : value;
1431
- if (!path54?.includes("/content/entries/")) return null;
1432
- const parts = path54.split("/").filter(Boolean).map(decodePathPart);
1535
+ const path55 = value.startsWith("http://") || value.startsWith("https://") ? urlPathname(value) : value;
1536
+ if (!path55?.includes("/content/entries/")) return null;
1537
+ const parts = path55.split("/").filter(Boolean).map(decodePathPart);
1433
1538
  const entriesIndex = parts.findIndex((part, index) => part === "content" && parts[index + 1] === "entries");
1434
1539
  const idPart = parts[entriesIndex + 3];
1435
1540
  if (!idPart || idPart === "new") return null;
@@ -1874,8 +1979,8 @@ var init_issue = __esm({
1874
1979
  });
1875
1980
 
1876
1981
  // src/capabilityFolders.ts
1877
- import * as fs6 from "fs";
1878
- import * as path7 from "path";
1982
+ import * as fs7 from "fs";
1983
+ import * as path8 from "path";
1879
1984
  function capabilityOutputConditionPaths(config) {
1880
1985
  if (config.outputSchema) {
1881
1986
  return new Set(schemaPropertyPaths(config.outputSchema, "result"));
@@ -1890,32 +1995,32 @@ function capabilityOutputConditionPaths(config) {
1890
1995
  ]);
1891
1996
  }
1892
1997
  function listCapabilityFolderSlugs(absDir) {
1893
- if (!fs6.existsSync(absDir)) return [];
1998
+ if (!fs7.existsSync(absDir)) return [];
1894
1999
  let entries;
1895
2000
  try {
1896
- entries = fs6.readdirSync(absDir, { withFileTypes: true });
2001
+ entries = fs7.readdirSync(absDir, { withFileTypes: true });
1897
2002
  } catch {
1898
2003
  return [];
1899
2004
  }
1900
- return entries.filter((e) => e.isDirectory() && !e.name.startsWith("_") && !e.name.startsWith(".")).filter((e) => isCapabilityFolder(path7.join(absDir, e.name))).map((e) => e.name).sort();
2005
+ return entries.filter((e) => e.isDirectory() && !e.name.startsWith("_") && !e.name.startsWith(".")).filter((e) => isCapabilityFolder(path8.join(absDir, e.name))).map((e) => e.name).sort();
1901
2006
  }
1902
2007
  function isCapabilityFolder(dir) {
1903
- if (!fs6.existsSync(path7.join(dir, CAPABILITY_BODY_FILE))) return false;
1904
- const entries = fs6.readdirSync(dir, { withFileTypes: true });
2008
+ if (!fs7.existsSync(path8.join(dir, CAPABILITY_BODY_FILE))) return false;
2009
+ const entries = fs7.readdirSync(dir, { withFileTypes: true });
1905
2010
  return entries.every(
1906
2011
  (entry) => entry.name === CAPABILITY_BODY_FILE || entry.name === CAPABILITY_CONTRACT_FILE || entry.isDirectory() && (entry.name === "skills" || entry.name === "tools")
1907
2012
  );
1908
2013
  }
1909
2014
  function readCapabilityFolder(root, slug) {
1910
- const dir = path7.join(root, slug);
1911
- const bodyPath = path7.join(dir, CAPABILITY_BODY_FILE);
1912
- const contractPath = path7.join(dir, CAPABILITY_CONTRACT_FILE);
1913
- if (!fs6.existsSync(bodyPath) || !fs6.statSync(bodyPath).isFile()) return null;
2015
+ const dir = path8.join(root, slug);
2016
+ const bodyPath = path8.join(dir, CAPABILITY_BODY_FILE);
2017
+ const contractPath = path8.join(dir, CAPABILITY_CONTRACT_FILE);
2018
+ if (!fs7.existsSync(bodyPath) || !fs7.statSync(bodyPath).isFile()) return null;
1914
2019
  if (!isCapabilityFolder(dir)) return null;
1915
2020
  try {
1916
- const rawBody = fs6.readFileSync(bodyPath, "utf-8");
1917
- const contract = fs6.existsSync(contractPath) ? parseCapabilityContract(fs6.readFileSync(contractPath, "utf-8")) : void 0;
1918
- if (contract?.execution === "script" && !isRegularFile(path7.join(dir, "tools", "run.sh"))) {
2021
+ const rawBody = fs7.readFileSync(bodyPath, "utf-8");
2022
+ const contract = fs7.existsSync(contractPath) ? parseCapabilityContract(fs7.readFileSync(contractPath, "utf-8")) : void 0;
2023
+ if (contract?.execution === "script" && !isRegularFile(path8.join(dir, "tools", "run.sh"))) {
1919
2024
  throw new Error('script-backed Capability requires a regular "tools/run.sh" file');
1920
2025
  }
1921
2026
  const { title, body } = parseCapabilityBody(rawBody, slug);
@@ -2028,7 +2133,7 @@ function parseCapabilityRequirements(raw) {
2028
2133
  }
2029
2134
  function isRegularFile(filePath) {
2030
2135
  try {
2031
- const stat = fs6.lstatSync(filePath);
2136
+ const stat = fs7.lstatSync(filePath);
2032
2137
  return stat.isFile() && !stat.isSymbolicLink();
2033
2138
  } catch {
2034
2139
  return false;
@@ -2037,8 +2142,8 @@ function isRegularFile(filePath) {
2037
2142
  function schemaPropertyPaths(schema, prefix) {
2038
2143
  const properties = isPlainObject(schema.properties) ? schema.properties : {};
2039
2144
  return Object.entries(properties).flatMap(([name, property]) => {
2040
- const path54 = `${prefix}.${name}`;
2041
- return isPlainObject(property) ? [path54, ...schemaPropertyPaths(property, path54)] : [path54];
2145
+ const path55 = `${prefix}.${name}`;
2146
+ return isPlainObject(property) ? [path55, ...schemaPropertyPaths(property, path55)] : [path55];
2042
2147
  });
2043
2148
  }
2044
2149
  function parseCapabilityBody(raw, slug) {
@@ -2200,51 +2305,51 @@ var init_capabilityFolders = __esm({
2200
2305
  });
2201
2306
 
2202
2307
  // src/definition-paths.ts
2203
- import * as fs7 from "fs";
2204
- import * as path8 from "path";
2308
+ import * as fs8 from "fs";
2309
+ import * as path9 from "path";
2205
2310
  function definitionsRoot(cwd = process.cwd()) {
2206
2311
  const override = process.env.KODY_DEFINITIONS_ROOT?.trim();
2207
2312
  const overrideCwd = process.env.KODY_DEFINITIONS_ROOT_CWD?.trim();
2208
- if (override && overrideCwd && path8.resolve(cwd) === path8.resolve(overrideCwd)) {
2209
- return storeCatalogRoot(path8.resolve(override));
2313
+ if (override && overrideCwd && path9.resolve(cwd) === path9.resolve(overrideCwd)) {
2314
+ return storeCatalogRoot(path9.resolve(override));
2210
2315
  }
2211
- const hydrated = path8.join(cwd, ".kody-engine", "definitions");
2212
- if (fs7.existsSync(hydrated)) return hydrated;
2213
- return override ? storeCatalogRoot(path8.resolve(override)) : hydrated;
2316
+ const hydrated = path9.join(cwd, ".kody-engine", "definitions");
2317
+ if (fs8.existsSync(hydrated)) return hydrated;
2318
+ return override ? storeCatalogRoot(path9.resolve(override)) : hydrated;
2214
2319
  }
2215
2320
  function hasExplicitDefinitionsRoot(cwd = process.cwd(), env = process.env) {
2216
2321
  const root = env.KODY_DEFINITIONS_ROOT?.trim();
2217
2322
  const rootCwd = env.KODY_DEFINITIONS_ROOT_CWD?.trim();
2218
- return Boolean(root && rootCwd && path8.resolve(cwd) === path8.resolve(rootCwd));
2323
+ return Boolean(root && rootCwd && path9.resolve(cwd) === path9.resolve(rootCwd));
2219
2324
  }
2220
2325
  function capabilitiesRoot(cwd = process.cwd()) {
2221
- return storeAssetRoot(cwd, "capabilities") ?? path8.join(definitionsRoot(cwd), "capabilities");
2326
+ return storeAssetRoot(cwd, "capabilities") ?? path9.join(definitionsRoot(cwd), "capabilities");
2222
2327
  }
2223
2328
  function implementationsRoot(cwd = process.cwd()) {
2224
- return path8.join(definitionsRoot(cwd), "implementations");
2329
+ return path9.join(definitionsRoot(cwd), "implementations");
2225
2330
  }
2226
2331
  function agentsRoot(cwd = process.cwd()) {
2227
- return storeAssetRoot(cwd, "agent") ?? path8.join(definitionsRoot(cwd), "agents");
2332
+ return storeAssetRoot(cwd, "agent") ?? path9.join(definitionsRoot(cwd), "agents");
2228
2333
  }
2229
2334
  function storeCatalogRoot(root) {
2230
2335
  const manifest = readStoreManifest(root);
2231
- const roots = ["capabilities", "workflows", "loops"].map((kind) => manifest?.assetRoots?.[kind]).filter((value) => typeof value === "string" && Boolean(value.trim())).map((value) => path8.dirname(value));
2232
- return roots.length === 3 && new Set(roots).size === 1 ? path8.join(root, roots[0]) : root;
2336
+ const roots = ["capabilities", "workflows", "loops"].map((kind) => manifest?.assetRoots?.[kind]).filter((value) => typeof value === "string" && Boolean(value.trim())).map((value) => path9.dirname(value));
2337
+ return roots.length === 3 && new Set(roots).size === 1 ? path9.join(root, roots[0]) : root;
2233
2338
  }
2234
2339
  function storeAssetRoot(cwd, kind) {
2235
2340
  const override = process.env.KODY_DEFINITIONS_ROOT?.trim();
2236
2341
  if (!override) return null;
2237
2342
  const overrideCwd = process.env.KODY_DEFINITIONS_ROOT_CWD?.trim();
2238
- if (overrideCwd && path8.resolve(cwd) !== path8.resolve(overrideCwd)) return null;
2239
- const root = path8.resolve(override);
2343
+ if (overrideCwd && path9.resolve(cwd) !== path9.resolve(overrideCwd)) return null;
2344
+ const root = path9.resolve(override);
2240
2345
  const configured = readStoreManifest(root)?.assetRoots?.[kind];
2241
- return typeof configured === "string" && configured.trim() ? path8.join(root, configured) : null;
2346
+ return typeof configured === "string" && configured.trim() ? path9.join(root, configured) : null;
2242
2347
  }
2243
2348
  function readStoreManifest(root) {
2244
- const file = path8.join(root, "kody-store.json");
2245
- if (!fs7.existsSync(file)) return null;
2349
+ const file = path9.join(root, "kody-store.json");
2350
+ if (!fs8.existsSync(file)) return null;
2246
2351
  try {
2247
- return JSON.parse(fs7.readFileSync(file, "utf8"));
2352
+ return JSON.parse(fs8.readFileSync(file, "utf8"));
2248
2353
  } catch {
2249
2354
  return null;
2250
2355
  }
@@ -2256,32 +2361,32 @@ var init_definition_paths = __esm({
2256
2361
  });
2257
2362
 
2258
2363
  // src/registry.ts
2259
- import * as fs8 from "fs";
2260
- import * as path9 from "path";
2364
+ import * as fs9 from "fs";
2365
+ import * as path10 from "path";
2261
2366
  function getImplementationsRoot() {
2262
- const here = path9.dirname(new URL(import.meta.url).pathname);
2367
+ const here = path10.dirname(new URL(import.meta.url).pathname);
2263
2368
  const candidates = [
2264
- path9.join(here, "implementations"),
2369
+ path10.join(here, "implementations"),
2265
2370
  // dev: src/
2266
- path9.join(here, "..", "implementations"),
2371
+ path10.join(here, "..", "implementations"),
2267
2372
  // built: dist/bin → dist/implementations
2268
- path9.join(here, "..", "src", "implementations")
2373
+ path10.join(here, "..", "src", "implementations")
2269
2374
  // fallback
2270
2375
  ];
2271
2376
  for (const c of candidates) {
2272
- if (fs8.existsSync(c) && fs8.statSync(c).isDirectory()) return c;
2377
+ if (fs9.existsSync(c) && fs9.statSync(c).isDirectory()) return c;
2273
2378
  }
2274
2379
  return candidates[0];
2275
2380
  }
2276
2381
  function getRuntimeServicesRoot() {
2277
- const here = path9.dirname(new URL(import.meta.url).pathname);
2382
+ const here = path10.dirname(new URL(import.meta.url).pathname);
2278
2383
  const candidates = [
2279
- path9.join(here, "runtime-services"),
2280
- path9.join(here, "..", "runtime-services"),
2281
- path9.join(here, "..", "src", "runtime-services")
2384
+ path10.join(here, "runtime-services"),
2385
+ path10.join(here, "..", "runtime-services"),
2386
+ path10.join(here, "..", "src", "runtime-services")
2282
2387
  ];
2283
2388
  for (const candidate of candidates) {
2284
- if (fs8.existsSync(candidate) && fs8.statSync(candidate).isDirectory()) return candidate;
2389
+ if (fs9.existsSync(candidate) && fs9.statSync(candidate).isDirectory()) return candidate;
2285
2390
  }
2286
2391
  return candidates[0];
2287
2392
  }
@@ -2289,17 +2394,17 @@ function getProjectCapabilitiesRoot() {
2289
2394
  return capabilitiesRoot();
2290
2395
  }
2291
2396
  function getBuiltinCapabilitiesRoot() {
2292
- const here = path9.dirname(new URL(import.meta.url).pathname);
2397
+ const here = path10.dirname(new URL(import.meta.url).pathname);
2293
2398
  const candidates = [
2294
- path9.join(here, "capabilities"),
2399
+ path10.join(here, "capabilities"),
2295
2400
  // dev: src/
2296
- path9.join(here, "..", "capabilities"),
2401
+ path10.join(here, "..", "capabilities"),
2297
2402
  // built: dist/bin → dist/capabilities
2298
- path9.join(here, "..", "src", "capabilities")
2403
+ path10.join(here, "..", "src", "capabilities")
2299
2404
  // fallback
2300
2405
  ];
2301
2406
  for (const c of candidates) {
2302
- if (fs8.existsSync(c) && fs8.statSync(c).isDirectory()) return c;
2407
+ if (fs9.existsSync(c) && fs9.statSync(c).isDirectory()) return c;
2303
2408
  }
2304
2409
  return candidates[0];
2305
2410
  }
@@ -2322,14 +2427,14 @@ function listImplementations(roots = getImplementationRoots()) {
2322
2427
  const seen = /* @__PURE__ */ new Set();
2323
2428
  const out = [];
2324
2429
  for (const root of rootList) {
2325
- if (!fs8.existsSync(root)) continue;
2430
+ if (!fs9.existsSync(root)) continue;
2326
2431
  const requireImplementationProfile = isCapabilityRoot(root);
2327
- const entries = fs8.readdirSync(root, { withFileTypes: true });
2432
+ const entries = fs9.readdirSync(root, { withFileTypes: true });
2328
2433
  for (const ent of entries) {
2329
2434
  if (!ent.isDirectory()) continue;
2330
2435
  if (seen.has(ent.name)) continue;
2331
2436
  const profilePath = implementationRuntimePath(root, ent.name);
2332
- if (fs8.existsSync(profilePath) && fs8.statSync(profilePath).isFile() && isImplementationProfile(profilePath, requireImplementationProfile)) {
2437
+ if (fs9.existsSync(profilePath) && fs9.statSync(profilePath).isFile() && isImplementationProfile(profilePath, requireImplementationProfile)) {
2333
2438
  out.push({ name: ent.name, profilePath });
2334
2439
  seen.add(ent.name);
2335
2440
  }
@@ -2349,7 +2454,7 @@ function resolveImplementationCandidates(name, roots = getRuntimeProfileRootsFor
2349
2454
  const out = [];
2350
2455
  for (const root of rootList) {
2351
2456
  const profilePath = implementationRuntimePath(root, name);
2352
- if (fs8.existsSync(profilePath) && fs8.statSync(profilePath).isFile() && isImplementationProfile(profilePath, isCapabilityRoot(root))) {
2457
+ if (fs9.existsSync(profilePath) && fs9.statSync(profilePath).isFile() && isImplementationProfile(profilePath, isCapabilityRoot(root))) {
2353
2458
  out.push(profilePath);
2354
2459
  }
2355
2460
  }
@@ -2418,7 +2523,7 @@ function implementationDeclaresInput(implementation, inputName, cwd = process.cw
2418
2523
  const profilePath = resolveImplementation(implementation, getRuntimeProfileRootsForCwd(cwd));
2419
2524
  if (!profilePath) return false;
2420
2525
  try {
2421
- const document = JSON.parse(fs8.readFileSync(profilePath, "utf-8"));
2526
+ const document = JSON.parse(fs9.readFileSync(profilePath, "utf-8"));
2422
2527
  const raw = document.config ?? document;
2423
2528
  if (!Array.isArray(raw.inputs)) return false;
2424
2529
  return raw.inputs.some((entry) => {
@@ -2434,29 +2539,29 @@ function isSafeName(name) {
2434
2539
  return /^[a-z][a-z0-9-]*$/.test(name) && !name.includes("..");
2435
2540
  }
2436
2541
  function isCapabilityRoot(root) {
2437
- const normalized = path9.normalize(root);
2438
- if (path9.basename(normalized) === "capabilities") return true;
2542
+ const normalized = path10.normalize(root);
2543
+ if (path10.basename(normalized) === "capabilities") return true;
2439
2544
  const knownRoots = [getProjectCapabilitiesRoot(), getBuiltinCapabilitiesRoot()];
2440
- return knownRoots.some((candidate) => candidate && path9.normalize(candidate) === normalized);
2545
+ return knownRoots.some((candidate) => candidate && path10.normalize(candidate) === normalized);
2441
2546
  }
2442
2547
  function implementationRuntimePath(root, name) {
2443
- const runtimePath = path9.join(root, name, "runtime.json");
2444
- if (fs8.existsSync(runtimePath)) return runtimePath;
2445
- const internalProfilePath = path9.join(root, name, "profile.json");
2446
- if (fs8.existsSync(internalProfilePath)) return internalProfilePath;
2447
- return path9.join(root, name, CAPABILITY_PROFILE_FILE);
2548
+ const runtimePath = path10.join(root, name, "runtime.json");
2549
+ if (fs9.existsSync(runtimePath)) return runtimePath;
2550
+ const internalProfilePath = path10.join(root, name, "profile.json");
2551
+ if (fs9.existsSync(internalProfilePath)) return internalProfilePath;
2552
+ return path10.join(root, name, CAPABILITY_PROFILE_FILE);
2448
2553
  }
2449
2554
  function isImplementationProfile(profilePath, requireImplementationProfile) {
2450
2555
  if (!requireImplementationProfile) return true;
2451
2556
  try {
2452
- const raw = JSON.parse(fs8.readFileSync(profilePath, "utf-8"));
2557
+ const raw = JSON.parse(fs9.readFileSync(profilePath, "utf-8"));
2453
2558
  return typeof raw.role === "string" && PUBLIC_IMPLEMENTATION_ROLES.has(raw.role);
2454
2559
  } catch {
2455
2560
  return false;
2456
2561
  }
2457
2562
  }
2458
2563
  function listFolderCapabilityActions(root, source) {
2459
- if (!fs8.existsSync(root) || !fs8.statSync(root).isDirectory()) return [];
2564
+ if (!fs9.existsSync(root) || !fs9.statSync(root).isDirectory()) return [];
2460
2565
  const out = [];
2461
2566
  for (const slug of listCapabilityFolderSlugs(root)) {
2462
2567
  if (!isSafeName(slug)) continue;
@@ -2488,7 +2593,7 @@ function hasUnresolvedExplicitImplementation(capability, implementation) {
2488
2593
  return resolveImplementation(implementation) === null;
2489
2594
  }
2490
2595
  function listBuiltinCapabilityActions(root = getBuiltinCapabilitiesRoot()) {
2491
- if (!fs8.existsSync(root) || !fs8.statSync(root).isDirectory()) return [];
2596
+ if (!fs9.existsSync(root) || !fs9.statSync(root).isDirectory()) return [];
2492
2597
  const out = [];
2493
2598
  for (const slug of listCapabilityFolderSlugs(root)) {
2494
2599
  if (!isSafeName(slug)) continue;
@@ -2513,7 +2618,7 @@ function getProfileInputs(name, roots = getImplementationRoots()) {
2513
2618
  const profilePath = resolveImplementation(name, roots);
2514
2619
  if (!profilePath) return null;
2515
2620
  try {
2516
- const document = JSON.parse(fs8.readFileSync(profilePath, "utf-8"));
2621
+ const document = JSON.parse(fs9.readFileSync(profilePath, "utf-8"));
2517
2622
  if (!document || typeof document !== "object") return [];
2518
2623
  const raw = "config" in document && document.config && typeof document.config === "object" ? document.config : document;
2519
2624
  if (!Array.isArray(raw.inputs)) return [];
@@ -3577,8 +3682,8 @@ var init_capabilityMcp = __esm({
3577
3682
 
3578
3683
  // src/repoWorkspace.ts
3579
3684
  import { spawn as spawn2, spawnSync } from "child_process";
3580
- import * as fs9 from "fs";
3581
- import * as path10 from "path";
3685
+ import * as fs10 from "fs";
3686
+ import * as path11 from "path";
3582
3687
  function buildCloneProcess(repo, token, baseEnv = process.env) {
3583
3688
  const url = `https://github.com/${repo}.git`;
3584
3689
  const env = { ...baseEnv };
@@ -3593,10 +3698,10 @@ function buildCloneProcess(repo, token, baseEnv = process.env) {
3593
3698
  async function resolveAndClone(reposRoot, repo, repoToken, cloneRepo) {
3594
3699
  const name = repo?.trim();
3595
3700
  if (!name || !REPO_RE.test(name)) return null;
3596
- const root = path10.resolve(reposRoot);
3597
- const dir = path10.resolve(root, name);
3598
- if (dir !== root && !dir.startsWith(root + path10.sep)) return null;
3599
- if (fs9.existsSync(path10.join(dir, ".git"))) return dir;
3701
+ const root = path11.resolve(reposRoot);
3702
+ const dir = path11.resolve(root, name);
3703
+ if (dir !== root && !dir.startsWith(root + path11.sep)) return null;
3704
+ if (fs10.existsSync(path11.join(dir, ".git"))) return dir;
3600
3705
  const inflight = repoClones.get(dir);
3601
3706
  if (inflight) {
3602
3707
  await inflight;
@@ -3628,9 +3733,9 @@ var init_repoWorkspace = __esm({
3628
3733
  repoClones = /* @__PURE__ */ new Map();
3629
3734
  GIT_CREDENTIAL_HELPER = `!f() { if [ "$1" = get ] && [ -n "$GITHUB_TOKEN" ]; then printf '%s\\n' username=x-access-token "password=$GITHUB_TOKEN"; fi; }; f`;
3630
3735
  defaultCloneRepo = (repo, token, dir) => {
3631
- fs9.mkdirSync(path10.dirname(dir), { recursive: true });
3736
+ fs10.mkdirSync(path11.dirname(dir), { recursive: true });
3632
3737
  const clone = buildCloneProcess(repo, token);
3633
- return new Promise((resolve20, reject) => {
3738
+ return new Promise((resolve21, reject) => {
3634
3739
  const child = spawn2("git", ["clone", "--depth=1", clone.url, dir], {
3635
3740
  env: clone.env,
3636
3741
  stdio: "inherit"
@@ -3650,7 +3755,7 @@ var init_repoWorkspace = __esm({
3650
3755
  }
3651
3756
  } catch {
3652
3757
  }
3653
- resolve20();
3758
+ resolve21();
3654
3759
  });
3655
3760
  child.on("error", reject);
3656
3761
  });
@@ -3722,8 +3827,8 @@ var init_fetchRepoMcp = __esm({
3722
3827
  });
3723
3828
 
3724
3829
  // src/agent.ts
3725
- import * as fs10 from "fs";
3726
- import * as path11 from "path";
3830
+ import * as fs11 from "fs";
3831
+ import * as path12 from "path";
3727
3832
  import { query } from "@anthropic-ai/claude-agent-sdk";
3728
3833
  function classifySubtype(subtype) {
3729
3834
  if (!subtype) return "generic_failed";
@@ -3792,8 +3897,8 @@ function buildAgentEnvironment(baseEnv, repoToken, requestEnvironment) {
3792
3897
  }
3793
3898
  async function runAgent(opts) {
3794
3899
  const ndjsonDir = opts.ndjsonDir ?? agentRunDir(opts.cwd);
3795
- fs10.mkdirSync(ndjsonDir, { recursive: true });
3796
- const ndjsonPath = path11.join(ndjsonDir, "last-run.jsonl");
3900
+ fs11.mkdirSync(ndjsonDir, { recursive: true });
3901
+ const ndjsonPath = path12.join(ndjsonDir, "last-run.jsonl");
3797
3902
  const env = buildAgentEnvironment(process.env, opts.repoToken, opts.environment);
3798
3903
  if (opts.litellmUrl) {
3799
3904
  env.ANTHROPIC_BASE_URL = opts.litellmUrl;
@@ -3811,10 +3916,12 @@ async function runAgent(opts) {
3811
3916
  let getSubmitted;
3812
3917
  const invokedSubagents = /* @__PURE__ */ new Set();
3813
3918
  const subagentInvocationHook = createSubagentInvocationHook(invokedSubagents);
3919
+ const outputContractPostWriteHook = opts.outputContract ? createOutputContractPostWriteHook(opts.outputContract) : null;
3920
+ const outputContractStopHook = opts.outputContract ? createOutputContractStopHook(opts.outputContract) : null;
3814
3921
  for (let attempt = 0; ; attempt++) {
3815
3922
  let ndjsonWriteFailed = false;
3816
3923
  let ndjsonWriteError;
3817
- const fullLog = fs10.createWriteStream(ndjsonPath, { flags: "w" });
3924
+ const fullLog = fs11.createWriteStream(ndjsonPath, { flags: "w" });
3818
3925
  fullLog.on("error", (err) => {
3819
3926
  ndjsonWriteFailed = true;
3820
3927
  ndjsonWriteError = err instanceof Error ? err.message : String(err);
@@ -3837,6 +3944,7 @@ async function runAgent(opts) {
3837
3944
  // Fresh array (never mutate the shared DEFAULT_ALLOWED_TOOLS const) so
3838
3945
  // opt-in tools like fetch_repo can be appended below.
3839
3946
  allowedTools: [...opts.allowedToolsOverride ?? DEFAULT_ALLOWED_TOOLS],
3947
+ ...opts.disallowedToolsOverride?.length ? { disallowedTools: [...opts.disallowedToolsOverride] } : {},
3840
3948
  permissionMode: opts.permissionModeOverride ?? "acceptEdits",
3841
3949
  env,
3842
3950
  hooks: {
@@ -3850,8 +3958,21 @@ async function runAgent(opts) {
3850
3958
  {
3851
3959
  matcher: "Agent",
3852
3960
  hooks: [subagentInvocationHook]
3853
- }
3854
- ]
3961
+ },
3962
+ ...outputContractPostWriteHook ? [
3963
+ {
3964
+ matcher: "Write",
3965
+ hooks: [outputContractPostWriteHook]
3966
+ }
3967
+ ] : []
3968
+ ],
3969
+ ...outputContractStopHook ? {
3970
+ Stop: [
3971
+ {
3972
+ hooks: [outputContractStopHook]
3973
+ }
3974
+ ]
3975
+ } : {}
3855
3976
  }
3856
3977
  };
3857
3978
  const additionalDirectories = new Set(opts.additionalDirectories ?? []);
@@ -3981,10 +4102,10 @@ async function runAgent(opts) {
3981
4102
  let timer;
3982
4103
  let next;
3983
4104
  if (turnTimeoutMs > 0) {
3984
- const timeoutPromise = new Promise((resolve20) => {
4105
+ const timeoutPromise = new Promise((resolve21) => {
3985
4106
  timer = setTimeout(() => {
3986
4107
  timedOut = true;
3987
- resolve20({ done: true, value: void 0 });
4108
+ resolve21({ done: true, value: void 0 });
3988
4109
  }, turnTimeoutMs);
3989
4110
  });
3990
4111
  next = await Promise.race([nextPromise, timeoutPromise]);
@@ -4000,7 +4121,7 @@ async function runAgent(opts) {
4000
4121
  try {
4001
4122
  await Promise.race([
4002
4123
  iterator.return(void 0).catch(() => void 0),
4003
- new Promise((resolve20) => setTimeout(resolve20, 1e4).unref())
4124
+ new Promise((resolve21) => setTimeout(resolve21, 1e4).unref())
4004
4125
  ]);
4005
4126
  } catch {
4006
4127
  }
@@ -4183,6 +4304,7 @@ var init_agent = __esm({
4183
4304
  init_claudeBinary();
4184
4305
  init_config();
4185
4306
  init_format();
4307
+ init_outputContractHooks();
4186
4308
  init_runtimePaths();
4187
4309
  init_subagents();
4188
4310
  DEFAULT_ALLOWED_TOOLS = ["Bash", "Edit", "Read", "Write", "Glob", "Grep"];
@@ -4203,8 +4325,8 @@ var init_agent = __esm({
4203
4325
  });
4204
4326
 
4205
4327
  // src/agents.ts
4206
- import * as fs11 from "fs";
4207
- import * as path12 from "path";
4328
+ import * as fs12 from "fs";
4329
+ import * as path13 from "path";
4208
4330
  function stripFrontmatter(raw) {
4209
4331
  const match = /^---\n[\s\S]*?\n---\n?([\s\S]*)$/.exec(raw);
4210
4332
  return (match ? match[1] : raw).trim();
@@ -4213,8 +4335,8 @@ function loadAgentIdentity(cwd, slug, agentsDir = agentsRoot(cwd)) {
4213
4335
  const trimmed = slug.trim();
4214
4336
  if (!trimmed) throw new Error("loadAgentIdentity: empty agent slug");
4215
4337
  const agentPath = resolveAgentFile2(cwd, trimmed, agentsDir);
4216
- if (fs11.existsSync(agentPath)) {
4217
- const body = stripFrontmatter(fs11.readFileSync(agentPath, "utf-8"));
4338
+ if (fs12.existsSync(agentPath)) {
4339
+ const body = stripFrontmatter(fs12.readFileSync(agentPath, "utf-8"));
4218
4340
  if (body) return body;
4219
4341
  const builtinForEmpty = BUILTIN_AGENTS[trimmed];
4220
4342
  if (builtinForEmpty) return builtinForEmpty;
@@ -4225,8 +4347,8 @@ function loadAgentIdentity(cwd, slug, agentsDir = agentsRoot(cwd)) {
4225
4347
  throw new Error(`loadAgentIdentity: agent '${trimmed}' declared but ${agentPath} does not exist`);
4226
4348
  }
4227
4349
  function resolveAgentFile2(cwd, slug, agentsDir = agentsRoot(cwd)) {
4228
- const localPath = path12.resolve(cwd, agentsDir, `${slug}.md`);
4229
- if (fs11.existsSync(localPath)) return localPath;
4350
+ const localPath = path13.resolve(cwd, agentsDir, `${slug}.md`);
4351
+ if (fs12.existsSync(localPath)) return localPath;
4230
4352
  return localPath;
4231
4353
  }
4232
4354
  function frameAgentIdentity(slug, agent) {
@@ -4258,14 +4380,14 @@ var init_agents = __esm({
4258
4380
  });
4259
4381
 
4260
4382
  // src/task-artifacts.ts
4261
- import fs12 from "fs";
4262
- import path13 from "path";
4383
+ import fs13 from "fs";
4384
+ import path14 from "path";
4263
4385
  import posixPath from "path/posix";
4264
4386
  function prepareTaskArtifactsDir(cwd, taskId) {
4265
4387
  const safeId = String(taskId).replace(/[^a-zA-Z0-9._-]/g, "_");
4266
4388
  const absDir = runtimeStatePath(cwd, "task-artifacts", safeId);
4267
4389
  const relDir = absDir;
4268
- fs12.mkdirSync(absDir, { recursive: true });
4390
+ fs13.mkdirSync(absDir, { recursive: true });
4269
4391
  return { taskId: safeId, absDir, relDir };
4270
4392
  }
4271
4393
  function initializeTaskArtifacts(artifacts, metadata = { taskType: "job" }) {
@@ -4295,16 +4417,16 @@ function initializeTaskArtifacts(artifacts, metadata = { taskType: "job" }) {
4295
4417
  "handoff-notes.md": "Baseline handoff: the agent did not provide additional notes.\n"
4296
4418
  };
4297
4419
  for (const file of TASK_ARTIFACT_FILES) {
4298
- const full = path13.join(artifacts.absDir, file);
4299
- if (!fs12.existsSync(full)) fs12.writeFileSync(full, defaults[file], "utf8");
4420
+ const full = path14.join(artifacts.absDir, file);
4421
+ if (!fs13.existsSync(full)) fs13.writeFileSync(full, defaults[file], "utf8");
4300
4422
  }
4301
4423
  }
4302
4424
  function verifyTaskArtifacts(absDir) {
4303
4425
  const missing = [];
4304
4426
  for (const name of TASK_ARTIFACT_FILES) {
4305
- const full = path13.join(absDir, name);
4427
+ const full = path14.join(absDir, name);
4306
4428
  try {
4307
- const stat = fs12.statSync(full);
4429
+ const stat = fs13.statSync(full);
4308
4430
  if (!stat.isFile() || stat.size === 0) missing.push(name);
4309
4431
  } catch {
4310
4432
  missing.push(name);
@@ -4320,11 +4442,11 @@ async function persistTaskArtifactsToState(config, _cwd, artifacts) {
4320
4442
  if (hasStateBackendConfig() && tenantId2) {
4321
4443
  const backend = createStateBackendFromEnv();
4322
4444
  for (const file of TASK_ARTIFACT_FILES) {
4323
- const full = path13.join(artifacts.absDir, file);
4324
- if (!fs12.existsSync(full)) continue;
4325
- const stat = fs12.statSync(full);
4445
+ const full = path14.join(artifacts.absDir, file);
4446
+ if (!fs13.existsSync(full)) continue;
4447
+ const stat = fs13.statSync(full);
4326
4448
  if (!stat.isFile() || stat.size === 0) continue;
4327
- const content = fs12.readFileSync(full, "utf-8");
4449
+ const content = fs13.readFileSync(full, "utf-8");
4328
4450
  const kind = file.replace(/\.(json|md)$/, "");
4329
4451
  let doc = content;
4330
4452
  if (file.endsWith(".json")) {
@@ -4624,15 +4746,15 @@ function validateWorkflow(value, options = {}) {
4624
4746
  }
4625
4747
  return issues;
4626
4748
  }
4627
- function validateInputBindings(value, path54, issues, declaredInputs) {
4749
+ function validateInputBindings(value, path55, issues, declaredInputs) {
4628
4750
  if (value === void 0) return;
4629
4751
  const bindings = asRecord(value);
4630
4752
  if (!bindings || Object.keys(bindings).length === 0) {
4631
- issue(issues, "invalid_inputs", path54, "workflow step inputs must contain at least one named mapping");
4753
+ issue(issues, "invalid_inputs", path55, "workflow step inputs must contain at least one named mapping");
4632
4754
  return;
4633
4755
  }
4634
4756
  for (const [name, value2] of Object.entries(bindings)) {
4635
- const bindingPath = `${path54}.${name}`;
4757
+ const bindingPath = `${path55}.${name}`;
4636
4758
  if (!/^[A-Za-z_][A-Za-z0-9_-]*$/.test(name)) {
4637
4759
  issue(issues, "invalid_input_name", bindingPath, `workflow input name ${name} is invalid`);
4638
4760
  }
@@ -4651,7 +4773,7 @@ function validateInputBindings(value, path54, issues, declaredInputs) {
4651
4773
  }
4652
4774
  }
4653
4775
  }
4654
- function validateInputBindingSources(value, path54, issues, capabilitiesByStep, capabilityOutputs) {
4776
+ function validateInputBindingSources(value, path55, issues, capabilitiesByStep, capabilityOutputs) {
4655
4777
  const bindings = asRecord(value);
4656
4778
  if (!bindings) return;
4657
4779
  for (const [name, rawBinding] of Object.entries(bindings)) {
@@ -4664,7 +4786,7 @@ function validateInputBindingSources(value, path54, issues, capabilitiesByStep,
4664
4786
  issue(
4665
4787
  issues,
4666
4788
  "missing_input_step",
4667
- `${path54}.${name}.from`,
4789
+ `${path55}.${name}.from`,
4668
4790
  `workflow input mapping references missing step ${sourceStep ?? "<none>"}`
4669
4791
  );
4670
4792
  continue;
@@ -4675,7 +4797,7 @@ function validateInputBindingSources(value, path54, issues, capabilitiesByStep,
4675
4797
  issue(
4676
4798
  issues,
4677
4799
  "undeclared_step_output",
4678
- `${path54}.${name}.from`,
4800
+ `${path55}.${name}.from`,
4679
4801
  `workflow input mapping reads ${outputPath}, but step ${sourceStep} does not declare it`
4680
4802
  );
4681
4803
  }
@@ -4684,11 +4806,11 @@ function validateInputBindingSources(value, path54, issues, capabilitiesByStep,
4684
4806
  function formatWorkflowValidationIssues(issues) {
4685
4807
  return issues.map((entry) => `${entry.path}: ${entry.message}`);
4686
4808
  }
4687
- function validateDataMatch(value, path54, issues, capabilityOutputs) {
4809
+ function validateDataMatch(value, path55, issues, capabilityOutputs) {
4688
4810
  if (value === void 0) return;
4689
4811
  const match = asRecord(value);
4690
4812
  if (!match || Object.keys(match).length === 0) {
4691
- issue(issues, "invalid_condition", path54, "workflow condition must contain at least one match");
4813
+ issue(issues, "invalid_condition", path55, "workflow condition must contain at least one match");
4692
4814
  return;
4693
4815
  }
4694
4816
  for (const [field, expected] of Object.entries(match)) {
@@ -4696,7 +4818,7 @@ function validateDataMatch(value, path54, issues, capabilityOutputs) {
4696
4818
  issue(
4697
4819
  issues,
4698
4820
  "invalid_data_path",
4699
- `${path54}.${field}`,
4821
+ `${path55}.${field}`,
4700
4822
  `workflow condition must read from facts, evidence, artifacts, result, workflow, or lastOutcome`
4701
4823
  );
4702
4824
  }
@@ -4704,12 +4826,12 @@ function validateDataMatch(value, path54, issues, capabilityOutputs) {
4704
4826
  issue(
4705
4827
  issues,
4706
4828
  "undeclared_result_path",
4707
- `${path54}.${field}`,
4829
+ `${path55}.${field}`,
4708
4830
  `workflow condition reads ${field}, but the source capability does not declare it`
4709
4831
  );
4710
4832
  }
4711
4833
  if (!isComparable(expected)) {
4712
- issue(issues, "invalid_condition_value", `${path54}.${field}`, "workflow condition value must be a JSON scalar");
4834
+ issue(issues, "invalid_condition_value", `${path55}.${field}`, "workflow condition value must be a JSON scalar");
4713
4835
  }
4714
4836
  }
4715
4837
  }
@@ -4733,8 +4855,8 @@ function isJsonValue(value) {
4733
4855
  if (!value || typeof value !== "object") return false;
4734
4856
  return Object.values(value).every(isJsonValue);
4735
4857
  }
4736
- function issue(issues, code, path54, message) {
4737
- issues.push({ code, path: path54, message });
4858
+ function issue(issues, code, path55, message) {
4859
+ issues.push({ code, path: path55, message });
4738
4860
  }
4739
4861
  var SAFE_NAME, SAFE_STEP_ID, SAFE_DATA_PATH, SAFE_INPUT_SOURCE, SUPPORTED_STEP_FIELDS, SUPPORTED_TRANSITION_FIELDS;
4740
4862
  var init_workflowValidation = __esm({
@@ -4766,8 +4888,8 @@ var init_workflowValidation = __esm({
4766
4888
  });
4767
4889
 
4768
4890
  // src/workflowDefinitions.ts
4769
- import * as fs18 from "fs";
4770
- import * as path19 from "path";
4891
+ import * as fs19 from "fs";
4892
+ import * as path20 from "path";
4771
4893
  function isWorkflowDefinitionId(value) {
4772
4894
  return WORKFLOW_ID_PATTERN.test(value);
4773
4895
  }
@@ -4812,12 +4934,12 @@ function readWorkflowDefinition(_config, cwd, id) {
4812
4934
  const root = cwd ?? process.cwd();
4813
4935
  const relativePath = workflowDefinitionPath(id);
4814
4936
  const candidates = [
4815
- path19.join(root, ".kody-engine", "runtime", relativePath),
4816
- path19.join(definitionsRoot(root), relativePath)
4937
+ path20.join(root, ".kody-engine", "runtime", relativePath),
4938
+ path20.join(definitionsRoot(root), relativePath)
4817
4939
  ];
4818
4940
  for (const filePath of candidates) {
4819
- if (!fs18.existsSync(filePath)) continue;
4820
- const workflow = parseWorkflowDefinition(fs18.readFileSync(filePath, "utf8"));
4941
+ if (!fs19.existsSync(filePath)) continue;
4942
+ const workflow = parseWorkflowDefinition(fs19.readFileSync(filePath, "utf8"));
4821
4943
  if (workflow) return workflow;
4822
4944
  }
4823
4945
  return null;
@@ -4825,7 +4947,7 @@ function readWorkflowDefinition(_config, cwd, id) {
4825
4947
  function workflowDefinitionToCapabilityFolder(id, workflow, source = workflowDefinitionPath(id)) {
4826
4948
  return {
4827
4949
  slug: id,
4828
- dir: path19.dirname(source),
4950
+ dir: path20.dirname(source),
4829
4951
  profilePath: source,
4830
4952
  bodyPath: source,
4831
4953
  title: workflow.name,
@@ -4881,7 +5003,7 @@ var init_workflowDefinitions = __esm({
4881
5003
 
4882
5004
  // src/gha.ts
4883
5005
  import { execFileSync as execFileSync2 } from "child_process";
4884
- import * as fs21 from "fs";
5006
+ import * as fs22 from "fs";
4885
5007
  function getRunUrl() {
4886
5008
  const server = process.env.GITHUB_SERVER_URL;
4887
5009
  const repo = process.env.GITHUB_REPOSITORY;
@@ -4892,10 +5014,10 @@ function getRunUrl() {
4892
5014
  function reactToTriggerComment(cwd) {
4893
5015
  if (process.env.GITHUB_EVENT_NAME !== "issue_comment") return;
4894
5016
  const eventPath = process.env.GITHUB_EVENT_PATH;
4895
- if (!eventPath || !fs21.existsSync(eventPath)) return;
5017
+ if (!eventPath || !fs22.existsSync(eventPath)) return;
4896
5018
  let event = null;
4897
5019
  try {
4898
- event = JSON.parse(fs21.readFileSync(eventPath, "utf-8"));
5020
+ event = JSON.parse(fs22.readFileSync(eventPath, "utf-8"));
4899
5021
  } catch {
4900
5022
  return;
4901
5023
  }
@@ -5029,57 +5151,6 @@ var init_agencyBoundaryEval = __esm({
5029
5151
  }
5030
5152
  });
5031
5153
 
5032
- // src/agency/capability-contract-validation.ts
5033
- import Ajv from "ajv";
5034
- function validateCapabilityContractValue(boundary, schema, value) {
5035
- const validate = validator.compile(schema);
5036
- if (!validate(value)) {
5037
- throw new CapabilityContractValidationError(boundary, validate.errors ?? []);
5038
- }
5039
- }
5040
- function capabilityContractInput(inputs, args, capabilityId, contractProperties = []) {
5041
- const isGenericRunnerInput = inputs.some((input) => input.name === "input") && Object.hasOwn(args, "input");
5042
- if (!isGenericRunnerInput) {
5043
- const isParameterlessGenericRunner = (inputs.some((input) => input.name === "capability") || args.capability === capabilityId || !contractProperties.includes("capability")) && Object.hasOwn(args, "capability");
5044
- if (!isParameterlessGenericRunner) return args;
5045
- const { capability: _routingCapability, ...businessArgs } = args;
5046
- return businessArgs;
5047
- }
5048
- const value = args.input;
5049
- if (typeof value !== "string") return value;
5050
- try {
5051
- return JSON.parse(value);
5052
- } catch {
5053
- return value;
5054
- }
5055
- }
5056
- var validator, CapabilityContractValidationError;
5057
- var init_capability_contract_validation = __esm({
5058
- "src/agency/capability-contract-validation.ts"() {
5059
- "use strict";
5060
- validator = new Ajv({
5061
- allErrors: true,
5062
- strict: true,
5063
- validateFormats: false
5064
- });
5065
- CapabilityContractValidationError = class extends Error {
5066
- constructor(boundary, errors) {
5067
- const details = errors.map((error) => {
5068
- const location = error.instancePath || "$";
5069
- const property = error.keyword === "additionalProperties" && typeof error.params.additionalProperty === "string" ? ` (${error.params.additionalProperty})` : "";
5070
- return `${location}: ${error.message ?? error.keyword}${property}`;
5071
- }).join("; ");
5072
- super(`Capability ${boundary} does not match its declared contract: ${details}`);
5073
- this.boundary = boundary;
5074
- this.errors = errors;
5075
- this.name = "CapabilityContractValidationError";
5076
- }
5077
- boundary;
5078
- errors;
5079
- };
5080
- }
5081
- });
5082
-
5083
5154
  // src/capabilityReport.ts
5084
5155
  function parseCapabilityReportsFromText(text2) {
5085
5156
  const reports = [];
@@ -5476,15 +5547,15 @@ var init_lifecycles = __esm({
5476
5547
 
5477
5548
  // src/profile.ts
5478
5549
  import { createHash as createHash3 } from "crypto";
5479
- import * as fs22 from "fs";
5480
- import * as path21 from "path";
5550
+ import * as fs23 from "fs";
5551
+ import * as path22 from "path";
5481
5552
  function loadProfile(profilePath) {
5482
- if (!fs22.existsSync(profilePath)) {
5553
+ if (!fs23.existsSync(profilePath)) {
5483
5554
  throw new ProfileError(profilePath, "file not found");
5484
5555
  }
5485
5556
  let raw;
5486
5557
  try {
5487
- raw = JSON.parse(fs22.readFileSync(profilePath, "utf-8"));
5558
+ raw = JSON.parse(fs23.readFileSync(profilePath, "utf-8"));
5488
5559
  } catch (err) {
5489
5560
  throw new ProfileError(profilePath, `invalid JSON: ${err instanceof Error ? err.message : String(err)}`);
5490
5561
  }
@@ -5496,7 +5567,7 @@ function loadProfile(profilePath) {
5496
5567
  const unknownKeys = Object.keys(r).filter((k) => !KNOWN_PROFILE_KEYS.has(k));
5497
5568
  if (unknownKeys.length > 0) {
5498
5569
  process.stderr.write(
5499
- `[kody profile] ${path21.basename(path21.dirname(profilePath))}: unknown top-level keys ignored: ${unknownKeys.join(", ")}
5570
+ `[kody profile] ${path22.basename(path22.dirname(profilePath))}: unknown top-level keys ignored: ${unknownKeys.join(", ")}
5500
5571
  `
5501
5572
  );
5502
5573
  }
@@ -5506,7 +5577,7 @@ function loadProfile(profilePath) {
5506
5577
  if (!refPath) {
5507
5578
  throw new ProfileError(profilePath, `capability references unknown implementation '${execRef}'`);
5508
5579
  }
5509
- if (path21.resolve(refPath) === path21.resolve(profilePath)) {
5580
+ if (path22.resolve(refPath) === path22.resolve(profilePath)) {
5510
5581
  } else {
5511
5582
  const base = loadProfile(refPath);
5512
5583
  return {
@@ -5604,8 +5675,8 @@ function loadProfile(profilePath) {
5604
5675
  // Phase 5 in-process handoff opt-in. Default false; containers
5605
5676
  // flip to true after end-to-end verification.
5606
5677
  preloadContext: r.preloadContext === true,
5607
- dir: path21.dirname(profilePath),
5608
- promptTemplates: readPromptTemplates(path21.dirname(profilePath))
5678
+ dir: path22.dirname(profilePath),
5679
+ promptTemplates: readPromptTemplates(path22.dirname(profilePath))
5609
5680
  };
5610
5681
  if (lifecycle) {
5611
5682
  applyLifecycle(profile, profilePath);
@@ -5640,19 +5711,19 @@ function loadProfile(profilePath) {
5640
5711
  return profile;
5641
5712
  }
5642
5713
  function compileRuntimeDocument(runtimePath, document) {
5643
- if (path21.basename(runtimePath) !== "runtime.json") return document;
5714
+ if (path22.basename(runtimePath) !== "runtime.json") return document;
5644
5715
  if (document.adapter !== "kody-engine-profile") {
5645
5716
  throw new ProfileError(runtimePath, "unsupported runtime adapter document");
5646
5717
  }
5647
- const implementationDir = path21.dirname(runtimePath);
5648
- const implementation = readJsonObject(path21.join(implementationDir, "definition.json"), "Implementation definition");
5649
- const definitionsRoot2 = path21.dirname(path21.dirname(implementationDir));
5718
+ const implementationDir = path22.dirname(runtimePath);
5719
+ const implementation = readJsonObject(path22.join(implementationDir, "definition.json"), "Implementation definition");
5720
+ const definitionsRoot2 = path22.dirname(path22.dirname(implementationDir));
5650
5721
  const capabilityId = implementation.capabilityRef && typeof implementation.capabilityRef === "object" && !Array.isArray(implementation.capabilityRef) ? implementation.capabilityRef.id : void 0;
5651
5722
  if (typeof capabilityId !== "string" || !capabilityId) {
5652
5723
  throw new ProfileError(runtimePath, "Implementation capabilityRef is invalid");
5653
5724
  }
5654
5725
  const capability = readJsonObject(
5655
- path21.join(definitionsRoot2, "capabilities", capabilityId, "definition.json"),
5726
+ path22.join(definitionsRoot2, "capabilities", capabilityId, "definition.json"),
5656
5727
  "Capability definition"
5657
5728
  );
5658
5729
  const {
@@ -5691,7 +5762,7 @@ function canonical(value) {
5691
5762
  }
5692
5763
  function readJsonObject(filePath, label) {
5693
5764
  try {
5694
- const value = JSON.parse(fs22.readFileSync(filePath, "utf-8"));
5765
+ const value = JSON.parse(fs23.readFileSync(filePath, "utf-8"));
5695
5766
  if (!value || typeof value !== "object" || Array.isArray(value)) {
5696
5767
  throw new Error("must be an object");
5697
5768
  }
@@ -5709,17 +5780,17 @@ function readPromptTemplates(dir) {
5709
5780
  const out = {};
5710
5781
  const read = (p) => {
5711
5782
  try {
5712
- out[p] = fs22.readFileSync(p, "utf-8");
5783
+ out[p] = fs23.readFileSync(p, "utf-8");
5713
5784
  } catch {
5714
5785
  }
5715
5786
  };
5716
- read(path21.join(dir, "prompt.md"));
5717
- read(path21.join(dir, "capability.md"));
5718
- read(path21.join(dir, "capability.md"));
5787
+ read(path22.join(dir, "prompt.md"));
5788
+ read(path22.join(dir, "capability.md"));
5789
+ read(path22.join(dir, "capability.md"));
5719
5790
  try {
5720
- const promptsDir = path21.join(dir, "prompts");
5721
- for (const ent of fs22.readdirSync(promptsDir)) {
5722
- if (ent.endsWith(".md")) read(path21.join(promptsDir, ent));
5791
+ const promptsDir = path22.join(dir, "prompts");
5792
+ for (const ent of fs23.readdirSync(promptsDir)) {
5793
+ if (ent.endsWith(".md")) read(path22.join(promptsDir, ent));
5723
5794
  }
5724
5795
  } catch {
5725
5796
  }
@@ -5866,10 +5937,12 @@ function parseClaudeCode(p, raw) {
5866
5937
  throw new ProfileError(p, `claudeCode.permissionMode must be one of default|acceptEdits|plan|bypassPermissions`);
5867
5938
  }
5868
5939
  const tools = Array.isArray(r.tools) ? r.tools : [];
5940
+ const disallowedTools = Array.isArray(r.disallowedTools) ? r.disallowedTools : [];
5869
5941
  return {
5870
5942
  model: typeof r.model === "string" ? r.model : "inherit",
5871
5943
  permissionMode,
5872
5944
  maxTurns: typeof r.maxTurns === "number" ? r.maxTurns : null,
5945
+ disallowedTools,
5873
5946
  maxThinkingTokens: typeof r.maxThinkingTokens === "number" ? r.maxThinkingTokens : null,
5874
5947
  reasoningEffort: typeof r.reasoningEffort === "string" ? parseReasoningEffort(r.reasoningEffort) : null,
5875
5948
  maxTurnTimeoutSec: typeof r.maxTurnTimeoutSec === "number" ? r.maxTurnTimeoutSec : null,
@@ -6494,16 +6567,16 @@ var init_state = __esm({
6494
6567
  });
6495
6568
 
6496
6569
  // src/prompt.ts
6497
- import * as fs23 from "fs";
6498
- import * as path22 from "path";
6570
+ import * as fs24 from "fs";
6571
+ import * as path23 from "path";
6499
6572
  function loadProjectConventions(projectDir) {
6500
6573
  const out = [];
6501
6574
  for (const rel of CONVENTION_FILES) {
6502
- const abs = path22.join(projectDir, rel);
6503
- if (!fs23.existsSync(abs)) continue;
6575
+ const abs = path23.join(projectDir, rel);
6576
+ if (!fs24.existsSync(abs)) continue;
6504
6577
  let content;
6505
6578
  try {
6506
- content = fs23.readFileSync(abs, "utf-8");
6579
+ content = fs24.readFileSync(abs, "utf-8");
6507
6580
  } catch {
6508
6581
  continue;
6509
6582
  }
@@ -6738,8 +6811,8 @@ var loadMemoryContext_exports = {};
6738
6811
  __export(loadMemoryContext_exports, {
6739
6812
  loadMemoryContext: () => loadMemoryContext
6740
6813
  });
6741
- import * as fs24 from "fs";
6742
- import * as path23 from "path";
6814
+ import * as fs25 from "fs";
6815
+ import * as path24 from "path";
6743
6816
  function formatBlockFromBackend(docs) {
6744
6817
  const pages = docs.flatMap((record2) => {
6745
6818
  if (!record2.doc || typeof record2.doc !== "object") return [];
@@ -6762,21 +6835,21 @@ function collectPages(memoryAbs) {
6762
6835
  walkMd(memoryAbs, (file) => {
6763
6836
  let stat;
6764
6837
  try {
6765
- stat = fs24.statSync(file);
6838
+ stat = fs25.statSync(file);
6766
6839
  } catch {
6767
6840
  return;
6768
6841
  }
6769
6842
  let raw;
6770
6843
  try {
6771
- raw = fs24.readFileSync(file, "utf-8");
6844
+ raw = fs25.readFileSync(file, "utf-8");
6772
6845
  } catch {
6773
6846
  return;
6774
6847
  }
6775
6848
  const fm = raw.match(/^---\s*\n([\s\S]*?)\n---/);
6776
- const title = fm?.[1]?.match(/^title:\s*(.+)$/m)?.[1]?.trim() ?? path23.basename(file, ".md");
6849
+ const title = fm?.[1]?.match(/^title:\s*(.+)$/m)?.[1]?.trim() ?? path24.basename(file, ".md");
6777
6850
  const updated = fm?.[1]?.match(/^updated:\s*([0-9T:.+\-Z]+)/m)?.[1]?.trim() ?? "";
6778
6851
  out.push({
6779
- relPath: path23.relative(memoryAbs, file),
6852
+ relPath: path24.relative(memoryAbs, file),
6780
6853
  title,
6781
6854
  updated,
6782
6855
  content: raw.length > PER_PAGE_MAX_BYTES ? raw.slice(0, PER_PAGE_MAX_BYTES) + TRUNCATED_SUFFIX2 : raw,
@@ -6844,16 +6917,16 @@ function walkMd(root, visit) {
6844
6917
  const dir = stack.pop();
6845
6918
  let names;
6846
6919
  try {
6847
- names = fs24.readdirSync(dir);
6920
+ names = fs25.readdirSync(dir);
6848
6921
  } catch {
6849
6922
  continue;
6850
6923
  }
6851
6924
  for (const name of names) {
6852
6925
  if (name.startsWith(".")) continue;
6853
- const full = path23.join(dir, name);
6926
+ const full = path24.join(dir, name);
6854
6927
  let stat;
6855
6928
  try {
6856
- stat = fs24.statSync(full);
6929
+ stat = fs25.statSync(full);
6857
6930
  } catch {
6858
6931
  continue;
6859
6932
  }
@@ -6888,8 +6961,8 @@ var init_loadMemoryContext = __esm({
6888
6961
  }
6889
6962
  return;
6890
6963
  }
6891
- const memoryAbs = path23.join(ctx.cwd, MEMORY_DIR_RELATIVE);
6892
- if (!fs24.existsSync(memoryAbs)) {
6964
+ const memoryAbs = path24.join(ctx.cwd, MEMORY_DIR_RELATIVE);
6965
+ if (!fs25.existsSync(memoryAbs)) {
6893
6966
  ctx.data.memoryContext = "";
6894
6967
  return;
6895
6968
  }
@@ -6933,11 +7006,11 @@ var init_loadCoverageRules = __esm({
6933
7006
 
6934
7007
  // src/container.ts
6935
7008
  import { execFileSync as execFileSync3 } from "child_process";
6936
- import * as fs25 from "fs";
7009
+ import * as fs26 from "fs";
6937
7010
  function getProfileInputsForChild(profileName, _cwd) {
6938
7011
  try {
6939
7012
  const profilePath = resolveProfilePath(profileName);
6940
- if (!fs25.existsSync(profilePath)) return null;
7013
+ if (!fs26.existsSync(profilePath)) return null;
6941
7014
  return loadProfile(profilePath).inputs;
6942
7015
  } catch {
6943
7016
  return null;
@@ -7401,10 +7474,10 @@ var init_lifecycleLabels = __esm({
7401
7474
 
7402
7475
  // src/litellm.ts
7403
7476
  import { execFileSync as execFileSync4, spawn as spawn4 } from "child_process";
7404
- import * as fs26 from "fs";
7477
+ import * as fs27 from "fs";
7405
7478
  import * as net from "net";
7406
7479
  import * as os4 from "os";
7407
- import * as path24 from "path";
7480
+ import * as path25 from "path";
7408
7481
  async function checkLitellmHealth(url) {
7409
7482
  try {
7410
7483
  const response = await fetch(`${url}/health`, { signal: AbortSignal.timeout(3e3) });
@@ -7474,7 +7547,7 @@ function locateLitellmScript() {
7474
7547
  }
7475
7548
  function resolveLitellmCommand() {
7476
7549
  const imageScript = "/opt/venv/bin/litellm";
7477
- if (fs26.existsSync(imageScript)) return imageScript;
7550
+ if (fs27.existsSync(imageScript)) return imageScript;
7478
7551
  try {
7479
7552
  execFileSync4("which", ["litellm"], { timeout: 3e3, stdio: "pipe" });
7480
7553
  return "litellm";
@@ -7517,13 +7590,13 @@ async function startLitellmIfNeeded(model, projectDir, url = LITELLM_DEFAULT_URL
7517
7590
  const spawnProxy = () => {
7518
7591
  const portMatch = activeUrl.match(/:(\d+)/);
7519
7592
  const port = portMatch ? portMatch[1] : "4000";
7520
- const configPath = path24.join(os4.tmpdir(), `kody-local-litellm-${Date.now()}.yaml`);
7521
- fs26.writeFileSync(configPath, generateLitellmConfigYaml(model));
7593
+ const configPath = path25.join(os4.tmpdir(), `kody-local-litellm-${Date.now()}.yaml`);
7594
+ fs27.writeFileSync(configPath, generateLitellmConfigYaml(model));
7522
7595
  const args = ["--config", configPath, "--port", port];
7523
- const nextLogPath = path24.join(os4.tmpdir(), `kody-local-litellm-${Date.now()}.log`);
7524
- const outFd = fs26.openSync(nextLogPath, "w");
7596
+ const nextLogPath = path25.join(os4.tmpdir(), `kody-local-litellm-${Date.now()}.log`);
7597
+ const outFd = fs27.openSync(nextLogPath, "w");
7525
7598
  child = spawn4(cmd, args, { stdio: ["ignore", outFd, outFd], detached: true, env: childEnv });
7526
- fs26.closeSync(outFd);
7599
+ fs27.closeSync(outFd);
7527
7600
  logPath = nextLogPath;
7528
7601
  };
7529
7602
  const waitForHealth = async () => {
@@ -7537,7 +7610,7 @@ async function startLitellmIfNeeded(model, projectDir, url = LITELLM_DEFAULT_URL
7537
7610
  const readLogTail = () => {
7538
7611
  if (!logPath) return "";
7539
7612
  try {
7540
- return fs26.readFileSync(logPath, "utf-8").slice(-2e3);
7613
+ return fs27.readFileSync(logPath, "utf-8").slice(-2e3);
7541
7614
  } catch {
7542
7615
  return "";
7543
7616
  }
@@ -7610,20 +7683,20 @@ async function nextAvailableLitellmUrl(url) {
7610
7683
  throw new Error(`no free LiteLLM port found after ${startPort}`);
7611
7684
  }
7612
7685
  function canListen(port, host) {
7613
- return new Promise((resolve20) => {
7686
+ return new Promise((resolve21) => {
7614
7687
  const server = net.createServer();
7615
- server.once("error", () => resolve20(false));
7688
+ server.once("error", () => resolve21(false));
7616
7689
  server.once("listening", () => {
7617
- server.close(() => resolve20(true));
7690
+ server.close(() => resolve21(true));
7618
7691
  });
7619
7692
  server.listen(port, host);
7620
7693
  });
7621
7694
  }
7622
7695
  function readDotenvApiKeys(projectDir) {
7623
- const dotenvPath = path24.join(projectDir, ".env");
7624
- if (!fs26.existsSync(dotenvPath)) return {};
7696
+ const dotenvPath = path25.join(projectDir, ".env");
7697
+ if (!fs27.existsSync(dotenvPath)) return {};
7625
7698
  const result = {};
7626
- for (const rawLine of fs26.readFileSync(dotenvPath, "utf-8").split("\n")) {
7699
+ for (const rawLine of fs27.readFileSync(dotenvPath, "utf-8").split("\n")) {
7627
7700
  const line = rawLine.trim();
7628
7701
  if (!line || line.startsWith("#")) continue;
7629
7702
  const match = line.match(/^([A-Z_][A-Z0-9_]*_API_KEY)=(.*)$/);
@@ -8282,8 +8355,8 @@ var init_pushWithRetry = __esm({
8282
8355
 
8283
8356
  // src/commit.ts
8284
8357
  import { execFileSync as execFileSync6 } from "child_process";
8285
- import * as fs27 from "fs";
8286
- import * as path25 from "path";
8358
+ import * as fs28 from "fs";
8359
+ import * as path26 from "path";
8287
8360
  function isGitHubYamlPath(filePath) {
8288
8361
  const normalized = filePath.replace(/^\.\/+/, "");
8289
8362
  return normalized.startsWith(".github/") && /\.ya?ml$/i.test(normalized);
@@ -8325,18 +8398,18 @@ function ensureGitIdentity(cwd) {
8325
8398
  }
8326
8399
  function abortUnfinishedGitOps(cwd) {
8327
8400
  const aborted = [];
8328
- const gitDir = path25.join(cwd ?? process.cwd(), ".git");
8329
- if (!fs27.existsSync(gitDir)) return aborted;
8330
- if (fs27.existsSync(path25.join(gitDir, "MERGE_HEAD"))) {
8401
+ const gitDir = path26.join(cwd ?? process.cwd(), ".git");
8402
+ if (!fs28.existsSync(gitDir)) return aborted;
8403
+ if (fs28.existsSync(path26.join(gitDir, "MERGE_HEAD"))) {
8331
8404
  if (tryGit(["merge", "--abort"], cwd)) aborted.push("merge");
8332
8405
  }
8333
- if (fs27.existsSync(path25.join(gitDir, "CHERRY_PICK_HEAD"))) {
8406
+ if (fs28.existsSync(path26.join(gitDir, "CHERRY_PICK_HEAD"))) {
8334
8407
  if (tryGit(["cherry-pick", "--abort"], cwd)) aborted.push("cherry-pick");
8335
8408
  }
8336
- if (fs27.existsSync(path25.join(gitDir, "REVERT_HEAD"))) {
8409
+ if (fs28.existsSync(path26.join(gitDir, "REVERT_HEAD"))) {
8337
8410
  if (tryGit(["revert", "--abort"], cwd)) aborted.push("revert");
8338
8411
  }
8339
- if (fs27.existsSync(path25.join(gitDir, "rebase-merge")) || fs27.existsSync(path25.join(gitDir, "rebase-apply"))) {
8412
+ if (fs28.existsSync(path26.join(gitDir, "rebase-merge")) || fs28.existsSync(path26.join(gitDir, "rebase-apply"))) {
8340
8413
  if (tryGit(["rebase", "--abort"], cwd)) aborted.push("rebase");
8341
8414
  }
8342
8415
  try {
@@ -8393,7 +8466,7 @@ function normalizeCommitMessage(raw) {
8393
8466
  function commitAndPush(branch, agentMessage, cwd) {
8394
8467
  const allChanged = listChangedFiles(cwd);
8395
8468
  const allowedFiles = allChanged.filter((f) => !isForbiddenPath(f));
8396
- const mergeHeadExists = fs27.existsSync(path25.join(cwd ?? process.cwd(), ".git", "MERGE_HEAD"));
8469
+ const mergeHeadExists = fs28.existsSync(path26.join(cwd ?? process.cwd(), ".git", "MERGE_HEAD"));
8397
8470
  if (allowedFiles.length === 0 && !mergeHeadExists) {
8398
8471
  return { committed: false, pushed: false, sha: "", message: "" };
8399
8472
  }
@@ -9033,13 +9106,13 @@ var init_state2 = __esm({
9033
9106
  });
9034
9107
 
9035
9108
  // src/goal/runLog.ts
9036
- import * as fs28 from "fs";
9109
+ import * as fs29 from "fs";
9037
9110
  function stageGoalRunLogEvent(data, goalId, event, at = nowIso()) {
9038
9111
  const logs = goalRunLogs(data);
9039
9112
  const existing = logs[goalId];
9040
- const path54 = existing?.path ?? goalRunLogPath(goalId, data);
9113
+ const path55 = existing?.path ?? goalRunLogPath(goalId, data);
9041
9114
  logs[goalId] = {
9042
- path: path54,
9115
+ path: path55,
9043
9116
  events: [...existing?.events ?? [], buildGoalRunLogEvent(data, goalId, event, at)]
9044
9117
  };
9045
9118
  }
@@ -9375,8 +9448,8 @@ function readGithubEvent() {
9375
9448
  const eventPath = process.env.GITHUB_EVENT_PATH;
9376
9449
  if (!eventPath) return null;
9377
9450
  try {
9378
- if (!fs28.existsSync(eventPath)) return null;
9379
- const parsed = JSON.parse(fs28.readFileSync(eventPath, "utf-8"));
9451
+ if (!fs29.existsSync(eventPath)) return null;
9452
+ const parsed = JSON.parse(fs29.readFileSync(eventPath, "utf-8"));
9380
9453
  return recordValue3(parsed);
9381
9454
  } catch {
9382
9455
  return null;
@@ -9486,8 +9559,8 @@ var init_stateStore = __esm({
9486
9559
  });
9487
9560
 
9488
9561
  // src/goal/targetLoopResolution.ts
9489
- import * as fs29 from "fs";
9490
- import * as path26 from "path";
9562
+ import * as fs30 from "fs";
9563
+ import * as path27 from "path";
9491
9564
  async function resolveActiveGoalLoopTarget(config, cwd, loopGoalId, loopGoal) {
9492
9565
  const targetId = loopGoal.loopTarget?.id.trim() ?? "";
9493
9566
  assertSafeGoalId(targetId, "loop target");
@@ -9565,11 +9638,11 @@ function goalInstanceTime(state) {
9565
9638
  return Number.isNaN(parsed) ? 0 : parsed;
9566
9639
  }
9567
9640
  function loadGoalTemplate(cwd, targetId) {
9568
- return readJsonObject2(path26.join(cwd, ".kody-engine", "definitions", "goals", targetId, "state.json"));
9641
+ return readJsonObject2(path27.join(cwd, ".kody-engine", "definitions", "goals", targetId, "state.json"));
9569
9642
  }
9570
9643
  function readJsonObject2(filePath) {
9571
- if (!fs29.existsSync(filePath)) return null;
9572
- const parsed = JSON.parse(fs29.readFileSync(filePath, "utf8"));
9644
+ if (!fs30.existsSync(filePath)) return null;
9645
+ const parsed = JSON.parse(fs30.readFileSync(filePath, "utf8"));
9573
9646
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
9574
9647
  throw new Error(`goal template ${filePath} must be a JSON object`);
9575
9648
  }
@@ -9916,15 +9989,15 @@ var init_backendStateBackend = __esm({
9916
9989
  this.jobsDir = opts.jobsDir.replace(/\/+$/, "");
9917
9990
  }
9918
9991
  async load(slug) {
9919
- const path54 = stateFilePath(this.jobsDir, slug);
9992
+ const path55 = stateFilePath(this.jobsDir, slug);
9920
9993
  const loaded = await createStateBackendFromEnv().get(this.tenantId, `capabilities/${slug}`, "job-state");
9921
9994
  if (!loaded) {
9922
- return { path: path54, handle: null, state: initialStateEnvelope("seed"), created: true };
9995
+ return { path: path55, handle: null, state: initialStateEnvelope("seed"), created: true };
9923
9996
  }
9924
9997
  if (!isStateEnvelope(loaded.doc)) {
9925
9998
  throw new Error(`BackendStateBackend: capabilities/${slug} is not a StateEnvelope`);
9926
9999
  }
9927
- return { path: path54, handle: loaded.updatedAt, state: loaded.doc, created: false };
10000
+ return { path: path55, handle: loaded.updatedAt, state: loaded.doc, created: false };
9928
10001
  }
9929
10002
  async save(loaded, next) {
9930
10003
  if (!loaded.created && isStateUnchanged(loaded.state, next)) return false;
@@ -9944,8 +10017,8 @@ var init_backendStateBackend = __esm({
9944
10017
  });
9945
10018
 
9946
10019
  // src/scripts/jobState/localFileBackend.ts
9947
- import * as fs30 from "fs";
9948
- import * as path27 from "path";
10020
+ import * as fs31 from "fs";
10021
+ import * as path28 from "path";
9949
10022
  function sanitizeKey(s) {
9950
10023
  return s.replace(/[^A-Za-z0-9._-]/g, "-");
9951
10024
  }
@@ -10001,7 +10074,7 @@ var init_localFileBackend = __esm({
10001
10074
  if (!opts.owner || !opts.repo) throw new Error("LocalFileBackend: owner and repo are required");
10002
10075
  this.cwd = opts.cwd;
10003
10076
  this.jobsDir = opts.jobsDir;
10004
- this.absDir = path27.resolve(opts.cwd, opts.jobsDir);
10077
+ this.absDir = path28.resolve(opts.cwd, opts.jobsDir);
10005
10078
  this.owner = opts.owner;
10006
10079
  this.repo = opts.repo;
10007
10080
  this.cache = opts.cache ?? defaultCacheAdapter();
@@ -10016,7 +10089,7 @@ var init_localFileBackend = __esm({
10016
10089
  `);
10017
10090
  return;
10018
10091
  }
10019
- fs30.mkdirSync(this.absDir, { recursive: true });
10092
+ fs31.mkdirSync(this.absDir, { recursive: true });
10020
10093
  const prefix = this.cacheKeyPrefix();
10021
10094
  const probeKey = `${prefix}probe-${Date.now()}`;
10022
10095
  try {
@@ -10045,7 +10118,7 @@ var init_localFileBackend = __esm({
10045
10118
  `);
10046
10119
  return;
10047
10120
  }
10048
- if (!fs30.existsSync(this.absDir)) {
10121
+ if (!fs31.existsSync(this.absDir)) {
10049
10122
  return;
10050
10123
  }
10051
10124
  const key = `${this.cacheKeyPrefix()}${process.env.GITHUB_RUN_ID ?? "norunid"}-${Date.now()}`;
@@ -10061,11 +10134,11 @@ var init_localFileBackend = __esm({
10061
10134
  }
10062
10135
  load(slug) {
10063
10136
  const relPath = stateFilePath(this.jobsDir, slug);
10064
- const absPath = path27.resolve(this.cwd, relPath);
10065
- if (!fs30.existsSync(absPath)) {
10137
+ const absPath = path28.resolve(this.cwd, relPath);
10138
+ if (!fs31.existsSync(absPath)) {
10066
10139
  return { path: relPath, handle: null, state: initialStateEnvelope("seed"), created: true };
10067
10140
  }
10068
- const raw = fs30.readFileSync(absPath, "utf-8");
10141
+ const raw = fs31.readFileSync(absPath, "utf-8");
10069
10142
  let parsed;
10070
10143
  try {
10071
10144
  parsed = JSON.parse(raw);
@@ -10082,13 +10155,13 @@ var init_localFileBackend = __esm({
10082
10155
  if (!loaded.created && isStateUnchanged(loaded.state, next)) {
10083
10156
  return false;
10084
10157
  }
10085
- const absPath = path27.resolve(this.cwd, loaded.path);
10086
- fs30.mkdirSync(path27.dirname(absPath), { recursive: true });
10158
+ const absPath = path28.resolve(this.cwd, loaded.path);
10159
+ fs31.mkdirSync(path28.dirname(absPath), { recursive: true });
10087
10160
  const body = `${JSON.stringify(next, null, 2)}
10088
10161
  `;
10089
10162
  const tmpPath = `${absPath}.${process.pid}.tmp`;
10090
- fs30.writeFileSync(tmpPath, body, "utf-8");
10091
- fs30.renameSync(tmpPath, absPath);
10163
+ fs31.writeFileSync(tmpPath, body, "utf-8");
10164
+ fs31.renameSync(tmpPath, absPath);
10092
10165
  return true;
10093
10166
  }
10094
10167
  cacheKeyPrefix() {
@@ -10120,7 +10193,7 @@ var init_jobState = __esm({
10120
10193
  });
10121
10194
 
10122
10195
  // src/scripts/goalCapabilityScheduling.ts
10123
- import * as path28 from "path";
10196
+ import * as path29 from "path";
10124
10197
  function isCapabilityCadenceGoal(goal, extra) {
10125
10198
  return extra.scheduleMode === "agentLoop" || extra.scheduler === "agentLoop" || goal.type === "standing" && goal.capabilities.length > 0;
10126
10199
  }
@@ -10176,7 +10249,7 @@ function planTargetLoopSchedule(opts) {
10176
10249
  }
10177
10250
  async function planGoalCapabilitySchedule(opts) {
10178
10251
  const jobsDir = opts.jobsDir ?? capabilitiesRoot(opts.cwd);
10179
- const jobsRoot = path28.resolve(opts.cwd, jobsDir);
10252
+ const jobsRoot = path29.resolve(opts.cwd, jobsDir);
10180
10253
  const now = opts.now ?? /* @__PURE__ */ new Date();
10181
10254
  const at = now.toISOString();
10182
10255
  const backend = resolveBackend({ config: opts.config, cwd: opts.cwd, jobsDir });
@@ -11992,8 +12065,8 @@ var init_classifyByLabel = __esm({
11992
12065
 
11993
12066
  // src/scripts/commitAndPush.ts
11994
12067
  import { createHash as createHash5 } from "crypto";
11995
- import * as fs31 from "fs";
11996
- import * as path29 from "path";
12068
+ import * as fs32 from "fs";
12069
+ import * as path30 from "path";
11997
12070
  function sentinelPathForStage(cwd, profileName, workflowExecutionKey) {
11998
12071
  const runId = resolveRunId();
11999
12072
  const executionSuffix = typeof workflowExecutionKey === "string" && workflowExecutionKey.length > 0 ? `-${createHash5("sha256").update(workflowExecutionKey).digest("hex").slice(0, 16)}` : "";
@@ -12015,9 +12088,9 @@ var init_commitAndPush = __esm({
12015
12088
  }
12016
12089
  const idempotencyEnabled = process.env.KODY_COMMIT_IDEMPOTENCY !== "0";
12017
12090
  const sentinel = idempotencyEnabled ? sentinelPathForStage(ctx.cwd, profile.name, ctx.data.workflowExecutionKey) : null;
12018
- if (sentinel && fs31.existsSync(sentinel)) {
12091
+ if (sentinel && fs32.existsSync(sentinel)) {
12019
12092
  try {
12020
- const replay = JSON.parse(fs31.readFileSync(sentinel, "utf-8"));
12093
+ const replay = JSON.parse(fs32.readFileSync(sentinel, "utf-8"));
12021
12094
  ctx.data.commitResult = replay.commitResult ?? { committed: false, pushed: false };
12022
12095
  if (Array.isArray(replay.changedFiles)) ctx.data.changedFiles = replay.changedFiles;
12023
12096
  if (typeof replay.hasCommitsAhead === "boolean") ctx.data.hasCommitsAhead = replay.hasCommitsAhead;
@@ -12077,8 +12150,8 @@ var init_commitAndPush = __esm({
12077
12150
  const result = ctx.data.commitResult;
12078
12151
  if (sentinel && result?.committed) {
12079
12152
  try {
12080
- fs31.mkdirSync(path29.dirname(sentinel), { recursive: true });
12081
- fs31.writeFileSync(
12153
+ fs32.mkdirSync(path30.dirname(sentinel), { recursive: true });
12154
+ fs32.writeFileSync(
12082
12155
  sentinel,
12083
12156
  JSON.stringify(
12084
12157
  {
@@ -12172,8 +12245,8 @@ var init_commitGoalState = __esm({
12172
12245
  });
12173
12246
 
12174
12247
  // src/scripts/composePrompt.ts
12175
- import * as fs32 from "fs";
12176
- import * as path30 from "path";
12248
+ import * as fs33 from "fs";
12249
+ import * as path31 from "path";
12177
12250
  function fenceUntrusted(value) {
12178
12251
  if (value.trim().length === 0) return value;
12179
12252
  const safe = value.replace(/-{3,}\s*END UNTRUSTED INPUT\s*-{3,}/gi, "[END UNTRUSTED INPUT]");
@@ -12297,10 +12370,10 @@ var init_composePrompt = __esm({
12297
12370
  const explicit = ctx.data.promptTemplate;
12298
12371
  const mode = ctx.args.mode;
12299
12372
  const candidates = [
12300
- explicit ? path30.join(profile.dir, explicit) : null,
12301
- mode ? path30.join(profile.dir, "prompts", `${mode}.md`) : null,
12302
- path30.join(profile.dir, "prompt.md"),
12303
- path30.join(profile.dir, "capability.md")
12373
+ explicit ? path31.join(profile.dir, explicit) : null,
12374
+ mode ? path31.join(profile.dir, "prompts", `${mode}.md`) : null,
12375
+ path31.join(profile.dir, "prompt.md"),
12376
+ path31.join(profile.dir, "capability.md")
12304
12377
  ].filter(Boolean);
12305
12378
  let templatePath = "";
12306
12379
  let template = "";
@@ -12313,7 +12386,7 @@ var init_composePrompt = __esm({
12313
12386
  break;
12314
12387
  }
12315
12388
  try {
12316
- template = fs32.readFileSync(c, "utf-8");
12389
+ template = fs33.readFileSync(c, "utf-8");
12317
12390
  templatePath = c;
12318
12391
  break;
12319
12392
  } catch (err) {
@@ -12324,7 +12397,7 @@ var init_composePrompt = __esm({
12324
12397
  if (!templatePath) {
12325
12398
  let dirState;
12326
12399
  try {
12327
- dirState = `dir contents: [${fs32.readdirSync(profile.dir).join(", ")}]`;
12400
+ dirState = `dir contents: [${fs33.readdirSync(profile.dir).join(", ")}]`;
12328
12401
  } catch (err) {
12329
12402
  dirState = `readdir(${profile.dir}) failed: ${err?.code ?? String(err)}`;
12330
12403
  }
@@ -13058,19 +13131,19 @@ var init_deriveQaScopeFromIssue = __esm({
13058
13131
 
13059
13132
  // src/scripts/diagMcp.ts
13060
13133
  import { execFileSync as execFileSync9 } from "child_process";
13061
- import * as fs33 from "fs";
13134
+ import * as fs34 from "fs";
13062
13135
  import * as os5 from "os";
13063
- import * as path31 from "path";
13136
+ import * as path32 from "path";
13064
13137
  var diagMcp;
13065
13138
  var init_diagMcp = __esm({
13066
13139
  "src/scripts/diagMcp.ts"() {
13067
13140
  "use strict";
13068
13141
  diagMcp = async (_ctx) => {
13069
13142
  const home = os5.homedir();
13070
- const cacheDir = path31.join(home, ".cache", "ms-playwright");
13143
+ const cacheDir = path32.join(home, ".cache", "ms-playwright");
13071
13144
  let entries = [];
13072
13145
  try {
13073
- entries = fs33.readdirSync(cacheDir);
13146
+ entries = fs34.readdirSync(cacheDir);
13074
13147
  } catch {
13075
13148
  }
13076
13149
  const hasChromium = entries.some((e) => e.startsWith("chromium"));
@@ -13098,13 +13171,13 @@ var init_diagMcp = __esm({
13098
13171
  });
13099
13172
 
13100
13173
  // src/scripts/frameworkDetectors.ts
13101
- import * as fs34 from "fs";
13102
- import * as path32 from "path";
13174
+ import * as fs35 from "fs";
13175
+ import * as path33 from "path";
13103
13176
  function detectFrameworks(cwd) {
13104
13177
  const out = [];
13105
13178
  let deps = {};
13106
13179
  try {
13107
- const pkg = JSON.parse(fs34.readFileSync(path32.join(cwd, "package.json"), "utf-8"));
13180
+ const pkg = JSON.parse(fs35.readFileSync(path33.join(cwd, "package.json"), "utf-8"));
13108
13181
  deps = { ...pkg.dependencies, ...pkg.devDependencies };
13109
13182
  } catch {
13110
13183
  return out;
@@ -13141,25 +13214,25 @@ function detectFrameworks(cwd) {
13141
13214
  }
13142
13215
  function findFile(cwd, candidates) {
13143
13216
  for (const c of candidates) {
13144
- if (fs34.existsSync(path32.join(cwd, c))) return c;
13217
+ if (fs35.existsSync(path33.join(cwd, c))) return c;
13145
13218
  }
13146
13219
  return null;
13147
13220
  }
13148
13221
  function discoverPayloadCollections(cwd) {
13149
13222
  const out = [];
13150
13223
  for (const dir of COLLECTION_DIRS) {
13151
- const full = path32.join(cwd, dir);
13152
- if (!fs34.existsSync(full)) continue;
13224
+ const full = path33.join(cwd, dir);
13225
+ if (!fs35.existsSync(full)) continue;
13153
13226
  let files;
13154
13227
  try {
13155
- files = fs34.readdirSync(full).filter((f) => f.endsWith(".ts") || f.endsWith(".tsx"));
13228
+ files = fs35.readdirSync(full).filter((f) => f.endsWith(".ts") || f.endsWith(".tsx"));
13156
13229
  } catch {
13157
13230
  continue;
13158
13231
  }
13159
13232
  for (const file of files) {
13160
13233
  try {
13161
- const filePath = path32.join(full, file);
13162
- const content = fs34.readFileSync(filePath, "utf-8").slice(0, 1e4);
13234
+ const filePath = path33.join(full, file);
13235
+ const content = fs35.readFileSync(filePath, "utf-8").slice(0, 1e4);
13163
13236
  const slugMatch = content.match(/slug:\s*['"]([a-z0-9-]+)['"]/);
13164
13237
  if (!slugMatch) continue;
13165
13238
  const slug = slugMatch[1];
@@ -13173,7 +13246,7 @@ function discoverPayloadCollections(cwd) {
13173
13246
  out.push({
13174
13247
  name,
13175
13248
  slug,
13176
- filePath: path32.relative(cwd, filePath),
13249
+ filePath: path33.relative(cwd, filePath),
13177
13250
  fields: fields.slice(0, 20),
13178
13251
  hasAdmin
13179
13252
  });
@@ -13186,28 +13259,28 @@ function discoverPayloadCollections(cwd) {
13186
13259
  function discoverAdminComponents(cwd, collections) {
13187
13260
  const out = [];
13188
13261
  for (const dir of ADMIN_COMPONENT_DIRS) {
13189
- const full = path32.join(cwd, dir);
13190
- if (!fs34.existsSync(full)) continue;
13262
+ const full = path33.join(cwd, dir);
13263
+ if (!fs35.existsSync(full)) continue;
13191
13264
  let entries;
13192
13265
  try {
13193
- entries = fs34.readdirSync(full, { withFileTypes: true });
13266
+ entries = fs35.readdirSync(full, { withFileTypes: true });
13194
13267
  } catch {
13195
13268
  continue;
13196
13269
  }
13197
13270
  for (const entry of entries) {
13198
- const entryPath = path32.join(full, entry.name);
13271
+ const entryPath = path33.join(full, entry.name);
13199
13272
  let name;
13200
13273
  let filePath;
13201
13274
  if (entry.isDirectory()) {
13202
13275
  const indexFile = ["index.tsx", "index.ts", "index.jsx", "index.js"].find(
13203
- (f) => fs34.existsSync(path32.join(entryPath, f))
13276
+ (f) => fs35.existsSync(path33.join(entryPath, f))
13204
13277
  );
13205
13278
  if (!indexFile) continue;
13206
13279
  name = entry.name;
13207
- filePath = path32.relative(cwd, path32.join(entryPath, indexFile));
13280
+ filePath = path33.relative(cwd, path33.join(entryPath, indexFile));
13208
13281
  } else if (/\.(tsx?|jsx?)$/.test(entry.name)) {
13209
13282
  name = entry.name.replace(/\.(tsx?|jsx?)$/, "");
13210
- filePath = path32.relative(cwd, entryPath);
13283
+ filePath = path33.relative(cwd, entryPath);
13211
13284
  } else {
13212
13285
  continue;
13213
13286
  }
@@ -13215,7 +13288,7 @@ function discoverAdminComponents(cwd, collections) {
13215
13288
  if (collections) {
13216
13289
  for (const col of collections) {
13217
13290
  try {
13218
- const colContent = fs34.readFileSync(path32.join(cwd, col.filePath), "utf-8");
13291
+ const colContent = fs35.readFileSync(path33.join(cwd, col.filePath), "utf-8");
13219
13292
  if (colContent.includes(name)) {
13220
13293
  usedInCollection = col.slug;
13221
13294
  break;
@@ -13233,8 +13306,8 @@ function scanApiRoutes(cwd) {
13233
13306
  const out = [];
13234
13307
  const appDirs = ["src/app", "app"];
13235
13308
  for (const appDir of appDirs) {
13236
- const apiDir = path32.join(cwd, appDir, "api");
13237
- if (!fs34.existsSync(apiDir)) continue;
13309
+ const apiDir = path33.join(cwd, appDir, "api");
13310
+ if (!fs35.existsSync(apiDir)) continue;
13238
13311
  walkApiRoutes(apiDir, "/api", cwd, out);
13239
13312
  break;
13240
13313
  }
@@ -13243,14 +13316,14 @@ function scanApiRoutes(cwd) {
13243
13316
  function walkApiRoutes(dir, prefix, cwd, out) {
13244
13317
  let entries;
13245
13318
  try {
13246
- entries = fs34.readdirSync(dir, { withFileTypes: true });
13319
+ entries = fs35.readdirSync(dir, { withFileTypes: true });
13247
13320
  } catch {
13248
13321
  return;
13249
13322
  }
13250
13323
  const routeFile = entries.find((e) => e.isFile() && /^route\.(ts|js|tsx|jsx)$/.test(e.name));
13251
13324
  if (routeFile) {
13252
13325
  try {
13253
- const content = fs34.readFileSync(path32.join(dir, routeFile.name), "utf-8").slice(0, 5e3);
13326
+ const content = fs35.readFileSync(path33.join(dir, routeFile.name), "utf-8").slice(0, 5e3);
13254
13327
  const methods = HTTP_METHODS.filter(
13255
13328
  (m) => new RegExp(`export\\s+(?:async\\s+)?function\\s+${m}\\b`).test(content)
13256
13329
  );
@@ -13258,7 +13331,7 @@ function walkApiRoutes(dir, prefix, cwd, out) {
13258
13331
  out.push({
13259
13332
  path: prefix,
13260
13333
  methods,
13261
- filePath: path32.relative(cwd, path32.join(dir, routeFile.name))
13334
+ filePath: path33.relative(cwd, path33.join(dir, routeFile.name))
13262
13335
  });
13263
13336
  }
13264
13337
  } catch {
@@ -13269,7 +13342,7 @@ function walkApiRoutes(dir, prefix, cwd, out) {
13269
13342
  if (entry.name === "node_modules" || entry.name === ".next") continue;
13270
13343
  let segment = entry.name;
13271
13344
  if (segment.startsWith("(") && segment.endsWith(")")) {
13272
- walkApiRoutes(path32.join(dir, entry.name), prefix, cwd, out);
13345
+ walkApiRoutes(path33.join(dir, entry.name), prefix, cwd, out);
13273
13346
  continue;
13274
13347
  }
13275
13348
  if (segment.startsWith("[[") && segment.endsWith("]]")) {
@@ -13277,16 +13350,16 @@ function walkApiRoutes(dir, prefix, cwd, out) {
13277
13350
  } else if (segment.startsWith("[") && segment.endsWith("]")) {
13278
13351
  segment = `:${segment.slice(1, -1)}`;
13279
13352
  }
13280
- walkApiRoutes(path32.join(dir, entry.name), `${prefix}/${segment}`, cwd, out);
13353
+ walkApiRoutes(path33.join(dir, entry.name), `${prefix}/${segment}`, cwd, out);
13281
13354
  }
13282
13355
  }
13283
13356
  function scanEnvVars(cwd) {
13284
13357
  const candidates = [".env.example", ".env.local.example", ".env.template"];
13285
13358
  for (const envFile of candidates) {
13286
- const envPath = path32.join(cwd, envFile);
13287
- if (!fs34.existsSync(envPath)) continue;
13359
+ const envPath = path33.join(cwd, envFile);
13360
+ if (!fs35.existsSync(envPath)) continue;
13288
13361
  try {
13289
- const content = fs34.readFileSync(envPath, "utf-8");
13362
+ const content = fs35.readFileSync(envPath, "utf-8");
13290
13363
  const vars = [];
13291
13364
  for (const line of content.split("\n")) {
13292
13365
  const trimmed = line.trim();
@@ -13331,8 +13404,8 @@ var init_frameworkDetectors = __esm({
13331
13404
  });
13332
13405
 
13333
13406
  // src/scripts/discoverQaContext.ts
13334
- import * as fs35 from "fs";
13335
- import * as path33 from "path";
13407
+ import * as fs36 from "fs";
13408
+ import * as path34 from "path";
13336
13409
  function runQaDiscovery(cwd) {
13337
13410
  const out = {
13338
13411
  routes: [],
@@ -13363,9 +13436,9 @@ function runQaDiscovery(cwd) {
13363
13436
  }
13364
13437
  function detectDevServer(cwd, out) {
13365
13438
  try {
13366
- const pkg = JSON.parse(fs35.readFileSync(path33.join(cwd, "package.json"), "utf-8"));
13439
+ const pkg = JSON.parse(fs36.readFileSync(path34.join(cwd, "package.json"), "utf-8"));
13367
13440
  const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
13368
- const pm = fs35.existsSync(path33.join(cwd, "pnpm-lock.yaml")) ? "pnpm" : fs35.existsSync(path33.join(cwd, "yarn.lock")) ? "yarn" : fs35.existsSync(path33.join(cwd, "bun.lockb")) ? "bun" : "npm";
13441
+ const pm = fs36.existsSync(path34.join(cwd, "pnpm-lock.yaml")) ? "pnpm" : fs36.existsSync(path34.join(cwd, "yarn.lock")) ? "yarn" : fs36.existsSync(path34.join(cwd, "bun.lockb")) ? "bun" : "npm";
13369
13442
  if (pkg.scripts?.dev) out.devCommand = `${pm} dev`;
13370
13443
  if (allDeps.next || allDeps.nuxt) out.devPort = 3e3;
13371
13444
  else if (allDeps.vite) out.devPort = 5173;
@@ -13375,8 +13448,8 @@ function detectDevServer(cwd, out) {
13375
13448
  function scanFrontendRoutes(cwd, out) {
13376
13449
  const appDirs = ["src/app", "app"];
13377
13450
  for (const appDir of appDirs) {
13378
- const full = path33.join(cwd, appDir);
13379
- if (!fs35.existsSync(full)) continue;
13451
+ const full = path34.join(cwd, appDir);
13452
+ if (!fs36.existsSync(full)) continue;
13380
13453
  walkFrontendRoutes(full, "", out);
13381
13454
  break;
13382
13455
  }
@@ -13384,7 +13457,7 @@ function scanFrontendRoutes(cwd, out) {
13384
13457
  function walkFrontendRoutes(dir, prefix, out) {
13385
13458
  let entries;
13386
13459
  try {
13387
- entries = fs35.readdirSync(dir, { withFileTypes: true });
13460
+ entries = fs36.readdirSync(dir, { withFileTypes: true });
13388
13461
  } catch {
13389
13462
  return;
13390
13463
  }
@@ -13401,7 +13474,7 @@ function walkFrontendRoutes(dir, prefix, out) {
13401
13474
  if (entry.name === "node_modules" || entry.name === ".next") continue;
13402
13475
  let segment = entry.name;
13403
13476
  if (segment.startsWith("(") && segment.endsWith(")")) {
13404
- walkFrontendRoutes(path33.join(dir, entry.name), prefix, out);
13477
+ walkFrontendRoutes(path34.join(dir, entry.name), prefix, out);
13405
13478
  continue;
13406
13479
  }
13407
13480
  if (segment.startsWith("[[") && segment.endsWith("]]")) {
@@ -13409,7 +13482,7 @@ function walkFrontendRoutes(dir, prefix, out) {
13409
13482
  } else if (segment.startsWith("[") && segment.endsWith("]")) {
13410
13483
  segment = `:${segment.slice(1, -1)}`;
13411
13484
  }
13412
- walkFrontendRoutes(path33.join(dir, entry.name), `${prefix}/${segment}`, out);
13485
+ walkFrontendRoutes(path34.join(dir, entry.name), `${prefix}/${segment}`, out);
13413
13486
  }
13414
13487
  }
13415
13488
  function detectAuthFiles(cwd, out) {
@@ -13426,23 +13499,23 @@ function detectAuthFiles(cwd, out) {
13426
13499
  "src/app/api/oauth"
13427
13500
  ];
13428
13501
  for (const c of candidates) {
13429
- if (fs35.existsSync(path33.join(cwd, c))) out.authFiles.push(c);
13502
+ if (fs36.existsSync(path34.join(cwd, c))) out.authFiles.push(c);
13430
13503
  }
13431
13504
  }
13432
13505
  function detectRoles(cwd, out) {
13433
13506
  const rolePaths = ["src/types", "src/lib", "src/utils", "src/constants", "src/access", "src/collections"];
13434
13507
  for (const rp of rolePaths) {
13435
- const dir = path33.join(cwd, rp);
13436
- if (!fs35.existsSync(dir)) continue;
13508
+ const dir = path34.join(cwd, rp);
13509
+ if (!fs36.existsSync(dir)) continue;
13437
13510
  let files;
13438
13511
  try {
13439
- files = fs35.readdirSync(dir).filter((f) => f.endsWith(".ts") || f.endsWith(".tsx"));
13512
+ files = fs36.readdirSync(dir).filter((f) => f.endsWith(".ts") || f.endsWith(".tsx"));
13440
13513
  } catch {
13441
13514
  continue;
13442
13515
  }
13443
13516
  for (const f of files) {
13444
13517
  try {
13445
- const content = fs35.readFileSync(path33.join(dir, f), "utf-8").slice(0, 5e3);
13518
+ const content = fs36.readFileSync(path34.join(dir, f), "utf-8").slice(0, 5e3);
13446
13519
  const roleMatches = content.match(/(?:role|Role|ROLE)\s*[=:]\s*['"](\w+)['"]/g);
13447
13520
  if (roleMatches) {
13448
13521
  for (const m of roleMatches) {
@@ -13703,8 +13776,8 @@ var init_dispatchClassified = __esm({
13703
13776
  });
13704
13777
 
13705
13778
  // src/loopDefinitions.ts
13706
- import * as fs36 from "fs";
13707
- import * as path34 from "path";
13779
+ import * as fs37 from "fs";
13780
+ import * as path35 from "path";
13708
13781
  function normalizeLoopDefinition(value) {
13709
13782
  if (!value || typeof value !== "object" || Array.isArray(value)) return null;
13710
13783
  const raw = value;
@@ -13731,10 +13804,10 @@ function readLoopDefinition(cwd, id) {
13731
13804
  if (!ID.test(id)) return null;
13732
13805
  const roots = loopRoots(cwd);
13733
13806
  for (const root of roots) {
13734
- const filePath = path34.join(root, "loops", id, "loop.json");
13735
- if (!fs36.existsSync(filePath)) continue;
13807
+ const filePath = path35.join(root, "loops", id, "loop.json");
13808
+ if (!fs37.existsSync(filePath)) continue;
13736
13809
  try {
13737
- const loop = normalizeLoopDefinition(JSON.parse(fs36.readFileSync(filePath, "utf8")));
13810
+ const loop = normalizeLoopDefinition(JSON.parse(fs37.readFileSync(filePath, "utf8")));
13738
13811
  if (loop?.id === id) return loop;
13739
13812
  process.stderr.write(`[kody] invalid Loop definition: ${filePath}
13740
13813
  `);
@@ -13744,7 +13817,7 @@ function readLoopDefinition(cwd, id) {
13744
13817
  }
13745
13818
  }
13746
13819
  process.stderr.write(
13747
- `[kody] Loop not found: ${id} (${roots.map((root) => path34.join(root, "loops", id, "loop.json")).join(", ")})
13820
+ `[kody] Loop not found: ${id} (${roots.map((root) => path35.join(root, "loops", id, "loop.json")).join(", ")})
13748
13821
  `
13749
13822
  );
13750
13823
  return null;
@@ -13753,14 +13826,14 @@ function listLoopDefinitions(cwd) {
13753
13826
  const roots = loopRoots(cwd);
13754
13827
  const byId = /* @__PURE__ */ new Map();
13755
13828
  for (const root of roots.reverse()) {
13756
- const loopsDir = path34.join(root, "loops");
13757
- if (!fs36.existsSync(loopsDir)) continue;
13758
- for (const id of fs36.readdirSync(loopsDir).sort()) {
13829
+ const loopsDir = path35.join(root, "loops");
13830
+ if (!fs37.existsSync(loopsDir)) continue;
13831
+ for (const id of fs37.readdirSync(loopsDir).sort()) {
13759
13832
  if (!ID.test(id)) continue;
13760
- const filePath = path34.join(loopsDir, id, "loop.json");
13761
- if (!fs36.existsSync(filePath)) continue;
13833
+ const filePath = path35.join(loopsDir, id, "loop.json");
13834
+ if (!fs37.existsSync(filePath)) continue;
13762
13835
  try {
13763
- const loop = normalizeLoopDefinition(JSON.parse(fs36.readFileSync(filePath, "utf8")));
13836
+ const loop = normalizeLoopDefinition(JSON.parse(fs37.readFileSync(filePath, "utf8")));
13764
13837
  if (loop?.id === id) byId.set(id, loop);
13765
13838
  } catch {
13766
13839
  process.stderr.write(`[kody] unreadable Loop definition: ${filePath}
@@ -13772,8 +13845,8 @@ function listLoopDefinitions(cwd) {
13772
13845
  }
13773
13846
  function loopRoots(cwd) {
13774
13847
  return [
13775
- path34.join(cwd, ".kody-engine", "runtime"),
13776
- path34.join(cwd, ".kody-engine", "definitions"),
13848
+ path35.join(cwd, ".kody-engine", "runtime"),
13849
+ path35.join(cwd, ".kody-engine", "definitions"),
13777
13850
  definitionsRoot(cwd)
13778
13851
  ].filter((root, index, roots) => roots.indexOf(root) === index);
13779
13852
  }
@@ -15051,15 +15124,15 @@ var init_fixFlow = __esm({
15051
15124
  });
15052
15125
 
15053
15126
  // src/workflow-template.ts
15054
- import * as fs37 from "fs";
15055
- import * as path35 from "path";
15127
+ import * as fs38 from "fs";
15128
+ import * as path36 from "path";
15056
15129
  import { fileURLToPath } from "url";
15057
15130
  function loadKodyWorkflowTemplate() {
15058
- const here = path35.dirname(fileURLToPath(import.meta.url));
15059
- const candidates = [path35.resolve(here, "../templates/kody.yml"), path35.resolve(here, "../../templates/kody.yml")];
15060
- const source = candidates.find((candidate) => fs37.existsSync(candidate));
15131
+ const here = path36.dirname(fileURLToPath(import.meta.url));
15132
+ const candidates = [path36.resolve(here, "../templates/kody.yml"), path36.resolve(here, "../../templates/kody.yml")];
15133
+ const source = candidates.find((candidate) => fs38.existsSync(candidate));
15061
15134
  if (!source) throw new Error(`Kody workflow template is missing: ${KODY_WORKFLOW_TEMPLATE_PATH}`);
15062
- return fs37.readFileSync(source, "utf8");
15135
+ return fs38.readFileSync(source, "utf8");
15063
15136
  }
15064
15137
  var KODY_WORKFLOW_TEMPLATE_PATH;
15065
15138
  var init_workflow_template = __esm({
@@ -15071,12 +15144,12 @@ var init_workflow_template = __esm({
15071
15144
 
15072
15145
  // src/scripts/initFlow.ts
15073
15146
  import { execFileSync as execFileSync14 } from "child_process";
15074
- import * as fs38 from "fs";
15075
- import * as path36 from "path";
15147
+ import * as fs39 from "fs";
15148
+ import * as path37 from "path";
15076
15149
  function detectPackageManager(cwd) {
15077
- if (fs38.existsSync(path36.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
15078
- if (fs38.existsSync(path36.join(cwd, "yarn.lock"))) return "yarn";
15079
- if (fs38.existsSync(path36.join(cwd, "bun.lockb"))) return "bun";
15150
+ if (fs39.existsSync(path37.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
15151
+ if (fs39.existsSync(path37.join(cwd, "yarn.lock"))) return "yarn";
15152
+ if (fs39.existsSync(path37.join(cwd, "bun.lockb"))) return "bun";
15080
15153
  return "npm";
15081
15154
  }
15082
15155
  function qualityCommandsFor(pm) {
@@ -15148,22 +15221,22 @@ function performInit(cwd, force) {
15148
15221
  const pm = detectPackageManager(cwd);
15149
15222
  const ownerRepo = detectOwnerRepo(cwd);
15150
15223
  const defaultBranch = defaultBranchFromGit(cwd);
15151
- const configPath = path36.join(cwd, "kody.config.json");
15152
- if (fs38.existsSync(configPath) && !force) {
15224
+ const configPath = path37.join(cwd, "kody.config.json");
15225
+ if (fs39.existsSync(configPath) && !force) {
15153
15226
  skipped.push("kody.config.json");
15154
15227
  } else {
15155
15228
  const cfg = makeConfig(pm, ownerRepo, defaultBranch);
15156
- fs38.writeFileSync(configPath, `${JSON.stringify(cfg, null, 2)}
15229
+ fs39.writeFileSync(configPath, `${JSON.stringify(cfg, null, 2)}
15157
15230
  `);
15158
15231
  wrote.push("kody.config.json");
15159
15232
  }
15160
- const workflowDir = path36.join(cwd, ".github", "workflows");
15161
- const workflowPath = path36.join(workflowDir, "kody.yml");
15162
- if (fs38.existsSync(workflowPath) && !force) {
15233
+ const workflowDir = path37.join(cwd, ".github", "workflows");
15234
+ const workflowPath = path37.join(workflowDir, "kody.yml");
15235
+ if (fs39.existsSync(workflowPath) && !force) {
15163
15236
  skipped.push(".github/workflows/kody.yml");
15164
15237
  } else {
15165
- fs38.mkdirSync(workflowDir, { recursive: true });
15166
- fs38.writeFileSync(workflowPath, loadKodyWorkflowTemplate());
15238
+ fs39.mkdirSync(workflowDir, { recursive: true });
15239
+ fs39.writeFileSync(workflowPath, loadKodyWorkflowTemplate());
15167
15240
  wrote.push(".github/workflows/kody.yml");
15168
15241
  }
15169
15242
  let labels;
@@ -15214,7 +15287,7 @@ Nothing to do. All files already present. (Use --force to overwrite.)
15214
15287
  });
15215
15288
 
15216
15289
  // src/scripts/loadAgentAdhoc.ts
15217
- import * as fs39 from "fs";
15290
+ import * as fs40 from "fs";
15218
15291
  function resolveMessage(messageArg) {
15219
15292
  const fromComment = readCommentBody();
15220
15293
  if (fromComment) return stripDirective(fromComment);
@@ -15222,9 +15295,9 @@ function resolveMessage(messageArg) {
15222
15295
  }
15223
15296
  function readCommentBody() {
15224
15297
  const eventPath = process.env.GITHUB_EVENT_PATH;
15225
- if (!eventPath || !fs39.existsSync(eventPath)) return "";
15298
+ if (!eventPath || !fs40.existsSync(eventPath)) return "";
15226
15299
  try {
15227
- const event = JSON.parse(fs39.readFileSync(eventPath, "utf-8"));
15300
+ const event = JSON.parse(fs40.readFileSync(eventPath, "utf-8"));
15228
15301
  return String(event.comment?.body ?? "");
15229
15302
  } catch {
15230
15303
  return "";
@@ -15278,10 +15351,10 @@ var init_loadAgentAdhoc = __esm({
15278
15351
  throw new Error("loadAgentAdhoc: ctx.args.agent must be a non-empty slug");
15279
15352
  }
15280
15353
  const agentPath = resolveAgentFile2(ctx.cwd, agentSlug, agentsDir);
15281
- if (!fs39.existsSync(agentPath)) {
15354
+ if (!fs40.existsSync(agentPath)) {
15282
15355
  throw new Error(`loadAgentAdhoc: agent identity not found: ${agentPath}`);
15283
15356
  }
15284
- const { title, body } = parseAgentFile(fs39.readFileSync(agentPath, "utf-8"), agentSlug);
15357
+ const { title, body } = parseAgentFile(fs40.readFileSync(agentPath, "utf-8"), agentSlug);
15285
15358
  const message = resolveMessage(ctx.args.message);
15286
15359
  if (!message) {
15287
15360
  throw new Error(
@@ -15353,13 +15426,13 @@ var init_loadCapabilityState = __esm({
15353
15426
  function isCompanyIntentId(value) {
15354
15427
  return SLUG_RE2.test(value);
15355
15428
  }
15356
- function normalizeCompanyIntent(path54, raw) {
15429
+ function normalizeCompanyIntent(path55, raw) {
15357
15430
  if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
15358
- throw new Error(`${path54}: intent must be JSON object`);
15431
+ throw new Error(`${path55}: intent must be JSON object`);
15359
15432
  }
15360
15433
  const input = raw;
15361
15434
  const id = stringField4(input.id);
15362
- if (!id || !isCompanyIntentId(id)) throw new Error(`${path54}: invalid intent id`);
15435
+ if (!id || !isCompanyIntentId(id)) throw new Error(`${path55}: invalid intent id`);
15363
15436
  const createdAt = stringField4(input.createdAt) || nowIso();
15364
15437
  const updatedAt = stringField4(input.updatedAt) || createdAt;
15365
15438
  const description = stringField4(input.description);
@@ -15521,7 +15594,7 @@ function retryDelaysMs() {
15521
15594
  }
15522
15595
  function sleep(ms) {
15523
15596
  if (ms <= 0) return Promise.resolve();
15524
- return new Promise((resolve20) => setTimeout(resolve20, ms));
15597
+ return new Promise((resolve21) => setTimeout(resolve21, ms));
15525
15598
  }
15526
15599
  async function fetchGoalStateWithRetry(config, goalId, cwd) {
15527
15600
  let state = await fetchGoalStateAsync(config, goalId, cwd);
@@ -15650,8 +15723,8 @@ var init_loadIssueStateComment = __esm({
15650
15723
  });
15651
15724
 
15652
15725
  // src/scripts/loadJobFromFile.ts
15653
- import * as fs40 from "fs";
15654
- import * as path37 from "path";
15726
+ import * as fs41 from "fs";
15727
+ import * as path38 from "path";
15655
15728
  function parseJobFile(raw, slug) {
15656
15729
  let stripped = raw;
15657
15730
  if (stripped.startsWith("---\n")) {
@@ -15690,10 +15763,10 @@ var init_loadJobFromFile = __esm({
15690
15763
  if (!slug) {
15691
15764
  throw new Error(`loadJobFromFile: ctx.args.${slugArg} must be a non-empty slug`);
15692
15765
  }
15693
- const capability = resolveCapabilityFolder(slug, path37.resolve(ctx.cwd, jobsDir));
15766
+ const capability = resolveCapabilityFolder(slug, path38.resolve(ctx.cwd, jobsDir));
15694
15767
  if (!capability) {
15695
15768
  throw new Error(
15696
- `loadJobFromFile: capability folder not found or incomplete: ${path37.resolve(ctx.cwd, jobsDir, slug)}`
15769
+ `loadJobFromFile: capability folder not found or incomplete: ${path38.resolve(ctx.cwd, jobsDir, slug)}`
15697
15770
  );
15698
15771
  }
15699
15772
  const { title, body, config } = capability;
@@ -15703,12 +15776,12 @@ var init_loadJobFromFile = __esm({
15703
15776
  let agentIdentity = "";
15704
15777
  if (agentSlug) {
15705
15778
  const agentPath = resolveAgentFile2(ctx.cwd, agentSlug, agentsDir);
15706
- if (!fs40.existsSync(agentPath)) {
15779
+ if (!fs41.existsSync(agentPath)) {
15707
15780
  throw new Error(
15708
15781
  `loadJobFromFile: capability '${slug}' declares agent '${agentSlug}' but ${agentPath} does not exist`
15709
15782
  );
15710
15783
  }
15711
- const agentRaw = fs40.readFileSync(agentPath, "utf-8");
15784
+ const agentRaw = fs41.readFileSync(agentPath, "utf-8");
15712
15785
  const parsed = parseJobFile(agentRaw, agentSlug);
15713
15786
  agentTitle = parsed.title;
15714
15787
  agentIdentity = parsed.body;
@@ -15788,13 +15861,13 @@ ${truncate2(issue2.body, FINDING_BODY_MAX_BYTES)}`;
15788
15861
  });
15789
15862
 
15790
15863
  // src/scripts/kodyVariables.ts
15791
- import * as fs41 from "fs";
15792
- import * as path38 from "path";
15864
+ import * as fs42 from "fs";
15865
+ import * as path39 from "path";
15793
15866
  function readKodyVariables(cwd) {
15794
- const full = path38.join(cwd, KODY_VARIABLES_REL_PATH);
15867
+ const full = path39.join(cwd, KODY_VARIABLES_REL_PATH);
15795
15868
  let raw;
15796
15869
  try {
15797
- raw = fs41.readFileSync(full, "utf-8");
15870
+ raw = fs42.readFileSync(full, "utf-8");
15798
15871
  } catch {
15799
15872
  return {};
15800
15873
  }
@@ -15819,8 +15892,8 @@ var init_kodyVariables = __esm({
15819
15892
  });
15820
15893
 
15821
15894
  // src/scripts/loadQaContext.ts
15822
- import * as fs42 from "fs";
15823
- import * as path39 from "path";
15895
+ import * as fs43 from "fs";
15896
+ import * as path40 from "path";
15824
15897
  function parseSlugList(value) {
15825
15898
  const inner = value.startsWith("[") && value.endsWith("]") ? value.slice(1, -1) : value;
15826
15899
  return inner.split(",").map(
@@ -15849,18 +15922,18 @@ function readProfileAgents(raw) {
15849
15922
  return { agent: agent ?? legacy ?? ["kody"], body };
15850
15923
  }
15851
15924
  function readProfile(cwd) {
15852
- const dir = path39.join(cwd, CONTEXT_DIR_REL_PATH);
15853
- if (!fs42.existsSync(dir)) return "";
15925
+ const dir = path40.join(cwd, CONTEXT_DIR_REL_PATH);
15926
+ if (!fs43.existsSync(dir)) return "";
15854
15927
  let entries;
15855
15928
  try {
15856
- entries = fs42.readdirSync(dir).filter((f) => f.endsWith(".md")).sort();
15929
+ entries = fs43.readdirSync(dir).filter((f) => f.endsWith(".md")).sort();
15857
15930
  } catch {
15858
15931
  return "";
15859
15932
  }
15860
15933
  const blocks = [];
15861
15934
  for (const file of entries) {
15862
15935
  try {
15863
- const raw = fs42.readFileSync(path39.join(dir, file), "utf-8");
15936
+ const raw = fs43.readFileSync(path40.join(dir, file), "utf-8");
15864
15937
  const { agent, body } = readProfileAgents(raw);
15865
15938
  if (!agent.includes(QA_AGENT) && !agent.includes(ALL_AGENTS)) continue;
15866
15939
  blocks.push(`## ${file}
@@ -15910,9 +15983,9 @@ var init_loadQaContext = __esm({
15910
15983
 
15911
15984
  // src/scripts/loadSimpleCapability.ts
15912
15985
  import { randomUUID as randomUUID2 } from "crypto";
15913
- import * as fs43 from "fs";
15986
+ import * as fs44 from "fs";
15914
15987
  import * as os6 from "os";
15915
- import * as path40 from "path";
15988
+ import * as path41 from "path";
15916
15989
  function registerCapabilitySubagents(profile, toolRoot, toolFiles) {
15917
15990
  const subagentFiles = toolFiles.flatMap((file) => {
15918
15991
  const match = /^agents\/([a-z][a-z0-9-]{0,63})\.md$/.exec(file);
@@ -15925,7 +15998,7 @@ function registerCapabilitySubagents(profile, toolRoot, toolFiles) {
15925
15998
  profile.subagentTemplates = {
15926
15999
  ...profile.subagentTemplates ?? {},
15927
16000
  ...Object.fromEntries(
15928
- subagentFiles.map(({ name, file }) => [name, fs43.readFileSync(path40.join(toolRoot, file), "utf-8")])
16001
+ subagentFiles.map(({ name, file }) => [name, fs44.readFileSync(path41.join(toolRoot, file), "utf-8")])
15929
16002
  )
15930
16003
  };
15931
16004
  if (!profile.claudeCode.tools.includes("Agent")) {
@@ -15966,14 +16039,14 @@ function scalar(value) {
15966
16039
  return value;
15967
16040
  }
15968
16041
  function listFiles(root) {
15969
- if (!fs43.existsSync(root)) return [];
16042
+ if (!fs44.existsSync(root)) return [];
15970
16043
  const files = [];
15971
16044
  const visit = (dir) => {
15972
- for (const entry of fs43.readdirSync(dir, { withFileTypes: true })) {
15973
- const absolute = path40.join(dir, entry.name);
16045
+ for (const entry of fs44.readdirSync(dir, { withFileTypes: true })) {
16046
+ const absolute = path41.join(dir, entry.name);
15974
16047
  if (entry.isSymbolicLink()) continue;
15975
16048
  if (entry.isDirectory()) visit(absolute);
15976
- else if (entry.isFile()) files.push(path40.relative(root, absolute));
16049
+ else if (entry.isFile()) files.push(path41.relative(root, absolute));
15977
16050
  }
15978
16051
  };
15979
16052
  visit(root);
@@ -15996,8 +16069,8 @@ var init_loadSimpleCapability = __esm({
15996
16069
  if (!capability) {
15997
16070
  throw new Error(`Capability "${slug}" is not a valid simple capability folder`);
15998
16071
  }
15999
- const toolRoot = path40.join(capability.dir, "tools");
16000
- const skillRoot = path40.join(capability.dir, "skills");
16072
+ const toolRoot = path41.join(capability.dir, "tools");
16073
+ const skillRoot = path41.join(capability.dir, "skills");
16001
16074
  const toolFiles = listFiles(toolRoot);
16002
16075
  const skillFiles = listFiles(skillRoot);
16003
16076
  const parsedInput = parseInput(ctx.args.input);
@@ -16022,14 +16095,14 @@ var init_loadSimpleCapability = __esm({
16022
16095
  }
16023
16096
  if (requiredSubagents.length > 0) ctx.data.requiredSubagents = requiredSubagents;
16024
16097
  if (capability.contract?.execution === "script") {
16025
- ctx.data.capabilityScriptPath = path40.join(capability.dir, "tools", "run.sh");
16098
+ ctx.data.capabilityScriptPath = path41.join(capability.dir, "tools", "run.sh");
16026
16099
  ctx.data.capabilitySecretNames = capability.contract.secrets ?? [];
16027
16100
  ctx.data.capabilityScriptTimeoutMs = capability.contract.timeoutMs;
16028
16101
  }
16029
16102
  if (capability.config.outputSchema) {
16030
16103
  ctx.data.capabilityOutputSchema = capability.config.outputSchema;
16031
16104
  }
16032
- const outputPath = ctx.data.capabilityExecution === "agent" && capability.config.outputSchema ? path40.join(os6.tmpdir(), `kody-capability-output-${randomUUID2()}.json`) : void 0;
16105
+ const outputPath = ctx.data.capabilityExecution === "agent" && capability.config.outputSchema ? path41.join(os6.tmpdir(), `kody-capability-output-${randomUUID2()}.json`) : void 0;
16033
16106
  if (outputPath) ctx.data.capabilityOutputPath = outputPath;
16034
16107
  ctx.data.capabilityEnvironment = {
16035
16108
  ...capabilityInputEnvironment(input),
@@ -16052,7 +16125,7 @@ var init_loadSimpleCapability = __esm({
16052
16125
  ...skillFiles.flatMap((file) => [
16053
16126
  `### ${file}`,
16054
16127
  "",
16055
- fs43.readFileSync(path40.join(skillRoot, file), "utf-8"),
16128
+ fs44.readFileSync(path41.join(skillRoot, file), "utf-8"),
16056
16129
  ""
16057
16130
  ])
16058
16131
  ] : [],
@@ -16061,7 +16134,7 @@ var init_loadSimpleCapability = __esm({
16061
16134
  "## Tools",
16062
16135
  "",
16063
16136
  "Inspect or run these capability-owned files when needed:",
16064
- ...toolFiles.map((file) => `- ${path40.join(toolRoot, file)}`)
16137
+ ...toolFiles.map((file) => `- ${path41.join(toolRoot, file)}`)
16065
16138
  ] : [],
16066
16139
  "",
16067
16140
  ...capability.config.outputSchema ? [
@@ -16092,8 +16165,8 @@ var init_loadSimpleCapability = __esm({
16092
16165
  });
16093
16166
 
16094
16167
  // src/taskContext.ts
16095
- import * as fs44 from "fs";
16096
- import * as path41 from "path";
16168
+ import * as fs45 from "fs";
16169
+ import * as path42 from "path";
16097
16170
  function buildTaskContext(args) {
16098
16171
  return {
16099
16172
  schemaVersion: TASK_CONTEXT_SCHEMA_VERSION,
@@ -16109,9 +16182,9 @@ function buildTaskContext(args) {
16109
16182
  function persistTaskContext(cwd, ctx) {
16110
16183
  try {
16111
16184
  const dir = runtimeStatePath(cwd, "agent-runs", ctx.runId);
16112
- fs44.mkdirSync(dir, { recursive: true });
16113
- const file = path41.join(dir, "task-context.json");
16114
- fs44.writeFileSync(file, `${JSON.stringify(ctx, null, 2)}
16185
+ fs45.mkdirSync(dir, { recursive: true });
16186
+ const file = path42.join(dir, "task-context.json");
16187
+ fs45.writeFileSync(file, `${JSON.stringify(ctx, null, 2)}
16115
16188
  `);
16116
16189
  return file;
16117
16190
  } catch (err) {
@@ -16538,19 +16611,19 @@ function parseAgencyModelProposal(raw) {
16538
16611
  function normalizeBundleFiles(bundle) {
16539
16612
  const seen = /* @__PURE__ */ new Set();
16540
16613
  return bundle.files.map((file, index) => {
16541
- const path54 = file.path.replace(/^\/+/, "");
16542
- const parts = path54.split("/");
16543
- if (!path54 || file.path.startsWith("/") || file.path.includes("\\") || parts.some((part) => !part || part === "." || part === "..")) {
16614
+ const path55 = file.path.replace(/^\/+/, "");
16615
+ const parts = path55.split("/");
16616
+ if (!path55 || file.path.startsWith("/") || file.path.includes("\\") || parts.some((part) => !part || part === "." || part === "..")) {
16544
16617
  throw new Error(`openAgencyModelReviewPr: files[${index}].path is unsafe`);
16545
16618
  }
16546
16619
  if (!/^(agents\/[^/]+\.md|capabilities\/[^/]+\/.+|goals\/(?:templates\/)?[^/]+\/.+|workflows\/[^/]+\.json)$/.test(
16547
- path54
16620
+ path55
16548
16621
  )) {
16549
16622
  throw new Error(`openAgencyModelReviewPr: files[${index}].path is not a supported definition path`);
16550
16623
  }
16551
- if (seen.has(path54)) throw new Error(`openAgencyModelReviewPr: duplicate generated file path: ${path54}`);
16552
- seen.add(path54);
16553
- return { path: path54, content: file.content.replace(/\r\n?/g, "\n") };
16624
+ if (seen.has(path55)) throw new Error(`openAgencyModelReviewPr: duplicate generated file path: ${path55}`);
16625
+ seen.add(path55);
16626
+ return { path: path55, content: file.content.replace(/\r\n?/g, "\n") };
16554
16627
  });
16555
16628
  }
16556
16629
  function buildProposalId(issueNumber, bundle, sourceLabel) {
@@ -17032,16 +17105,16 @@ var init_parseReproOutput = __esm({
17032
17105
  });
17033
17106
 
17034
17107
  // src/scripts/parseSimpleCapabilityOutput.ts
17035
- import * as fs45 from "fs";
17108
+ import * as fs46 from "fs";
17036
17109
  function stringList2(value) {
17037
17110
  return Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
17038
17111
  }
17039
17112
  function readOutputFile(outputPath) {
17040
- if (!outputPath || !fs45.existsSync(outputPath)) return { found: false };
17113
+ if (!outputPath || !fs46.existsSync(outputPath)) return { found: false };
17041
17114
  try {
17042
- return { found: true, value: JSON.parse(fs45.readFileSync(outputPath, "utf-8")) };
17115
+ return { found: true, value: JSON.parse(fs46.readFileSync(outputPath, "utf-8")) };
17043
17116
  } finally {
17044
- fs45.rmSync(outputPath, { force: true });
17117
+ fs46.rmSync(outputPath, { force: true });
17045
17118
  }
17046
17119
  }
17047
17120
  function parseOutput(text2) {
@@ -17660,9 +17733,9 @@ var init_postResearchComment = __esm({
17660
17733
  });
17661
17734
 
17662
17735
  // src/scripts/prepareBrowserAuth.ts
17663
- import * as fs46 from "fs";
17736
+ import * as fs47 from "fs";
17664
17737
  import * as os7 from "os";
17665
- import * as path42 from "path";
17738
+ import * as path43 from "path";
17666
17739
  function appendAuthMessage(ctx, message) {
17667
17740
  const current = typeof ctx.data.qaAuthBlock === "string" ? ctx.data.qaAuthBlock.trim() : "";
17668
17741
  ctx.data.qaAuthBlock = current ? `${current}
@@ -17701,9 +17774,9 @@ async function githubJson(url, token) {
17701
17774
  return await response.json();
17702
17775
  }
17703
17776
  function writeKodyStorageState(input) {
17704
- const directory = fs46.mkdtempSync(path42.join(os7.tmpdir(), "kody-browser-auth-"));
17705
- fs46.chmodSync(directory, 448);
17706
- const file = path42.join(directory, "storage-state.json");
17777
+ const directory = fs47.mkdtempSync(path43.join(os7.tmpdir(), "kody-browser-auth-"));
17778
+ fs47.chmodSync(directory, 448);
17779
+ const file = path43.join(directory, "storage-state.json");
17707
17780
  const now = Date.now();
17708
17781
  const repoEntry = {
17709
17782
  repoUrl: input.repoUrl,
@@ -17733,7 +17806,7 @@ function writeKodyStorageState(input) {
17733
17806
  }
17734
17807
  ]
17735
17808
  };
17736
- fs46.writeFileSync(file, JSON.stringify(storageState), { mode: 384 });
17809
+ fs47.writeFileSync(file, JSON.stringify(storageState), { mode: 384 });
17737
17810
  return { directory, file };
17738
17811
  }
17739
17812
  function configurePlaywright(profile, storageStatePath) {
@@ -17815,7 +17888,7 @@ async function prepareMethod(ctx, profile, method) {
17815
17888
  configurePlaywright(profile, state.file);
17816
17889
  const authDirectory = state.directory;
17817
17890
  registerRuntimeCleanup(ctx, () => {
17818
- fs46.rmSync(authDirectory, { recursive: true, force: true });
17891
+ fs47.rmSync(authDirectory, { recursive: true, force: true });
17819
17892
  });
17820
17893
  appendAuthMessage(
17821
17894
  ctx,
@@ -17823,7 +17896,7 @@ async function prepareMethod(ctx, profile, method) {
17823
17896
  );
17824
17897
  return true;
17825
17898
  } catch (error) {
17826
- if (state) fs46.rmSync(state.directory, { recursive: true, force: true });
17899
+ if (state) fs47.rmSync(state.directory, { recursive: true, force: true });
17827
17900
  const reason = error instanceof Error ? error.message : String(error);
17828
17901
  appendAuthMessage(
17829
17902
  ctx,
@@ -17962,7 +18035,7 @@ var init_prepareCapabilityDelivery = __esm({
17962
18035
 
17963
18036
  // src/scripts/prepareSimpleCapabilityRuntime.ts
17964
18037
  import { isIP } from "net";
17965
- import * as path43 from "path";
18038
+ import * as path44 from "path";
17966
18039
  function requirementsFrom(ctx) {
17967
18040
  const raw = ctx.data.capabilityRequirements;
17968
18041
  return raw && typeof raw === "object" && !Array.isArray(raw) ? raw : {};
@@ -18006,7 +18079,7 @@ function browserRuntime(ctx, requirements) {
18006
18079
  "--allowed-origins",
18007
18080
  origin,
18008
18081
  "--output-dir",
18009
- path43.resolve(ctx.cwd, "test-results", "quality-runs", qualityRunId)
18082
+ path44.resolve(ctx.cwd, "test-results", "quality-runs", qualityRunId)
18010
18083
  ]
18011
18084
  };
18012
18085
  }
@@ -18014,8 +18087,21 @@ function configureBrowser(ctx, profile, requirements) {
18014
18087
  const server = browserRuntime(ctx, requirements);
18015
18088
  if (requirements.browserOnly) {
18016
18089
  profile.claudeCode.tools = ["Write"];
18090
+ profile.claudeCode.disallowedTools = [
18091
+ "Agent",
18092
+ "Bash",
18093
+ "Edit",
18094
+ "Glob",
18095
+ "Grep",
18096
+ "NotebookEdit",
18097
+ "Read",
18098
+ "Task",
18099
+ "TodoWrite",
18100
+ "WebFetch",
18101
+ "WebSearch"
18102
+ ];
18017
18103
  profile.claudeCode.permissionMode = "default";
18018
- profile.claudeCode.maxTurns = Math.min(profile.claudeCode.maxTurns ?? 50, 50);
18104
+ profile.claudeCode.maxTurns = Math.min(profile.claudeCode.maxTurns ?? 100, 100);
18019
18105
  }
18020
18106
  if (!profile.claudeCode.tools.includes("mcp__playwright")) {
18021
18107
  profile.claudeCode.tools = [...profile.claudeCode.tools, "mcp__playwright"];
@@ -18358,9 +18444,9 @@ function latestResult(raw, agentResult) {
18358
18444
  function recordField4(value) {
18359
18445
  return value && typeof value === "object" && !Array.isArray(value) ? value : null;
18360
18446
  }
18361
- function resolveDotted(root, path54) {
18362
- if (!path54) return void 0;
18363
- return path54.split(".").reduce((value, key) => recordField4(value)?.[key], root);
18447
+ function resolveDotted(root, path55) {
18448
+ if (!path55) return void 0;
18449
+ return path55.split(".").reduce((value, key) => recordField4(value)?.[key], root);
18364
18450
  }
18365
18451
  function stringValue5(value) {
18366
18452
  return typeof value === "string" && value.trim() ? value.trim() : null;
@@ -19202,7 +19288,7 @@ var init_previewBuildHelpers = __esm({
19202
19288
  // src/scripts/previewBuildRun.ts
19203
19289
  import { spawn as spawn5 } from "child_process";
19204
19290
  async function runCmd(cmd, args, opts = {}) {
19205
- await new Promise((resolve20, reject) => {
19291
+ await new Promise((resolve21, reject) => {
19206
19292
  const child = spawn5(cmd, args, {
19207
19293
  cwd: opts.cwd,
19208
19294
  env: { ...process.env, ...opts.env ?? {} },
@@ -19214,7 +19300,7 @@ async function runCmd(cmd, args, opts = {}) {
19214
19300
  }
19215
19301
  child.on("error", reject);
19216
19302
  child.on("close", (code) => {
19217
- if (code === 0) resolve20();
19303
+ if (code === 0) resolve21();
19218
19304
  else reject(new Error(`${cmd} ${args.join(" ")} exited ${code}`));
19219
19305
  });
19220
19306
  });
@@ -19286,12 +19372,12 @@ fi
19286
19372
 
19287
19373
  // src/scripts/runPreviewBuild.ts
19288
19374
  import { copyFile, writeFile } from "fs/promises";
19289
- import * as path44 from "path";
19375
+ import * as path45 from "path";
19290
19376
  import { fileURLToPath as fileURLToPath2 } from "url";
19291
19377
  function bundledDockerfilePath(mode) {
19292
- const here = path44.dirname(fileURLToPath2(import.meta.url));
19378
+ const here = path45.dirname(fileURLToPath2(import.meta.url));
19293
19379
  const file = mode === "dev" ? "default-Dockerfile.preview.dev" : "default-Dockerfile.preview.prod";
19294
- return path44.join(here, "preview-build-templates", file);
19380
+ return path45.join(here, "preview-build-templates", file);
19295
19381
  }
19296
19382
  function required(name) {
19297
19383
  const v = (process.env[name] ?? "").trim();
@@ -19526,10 +19612,10 @@ var init_runPreviewBuild = __esm({
19526
19612
  console.log(`[preview-build] vault: ${Object.keys(buildEnv).length} secrets, mode=${buildMode}`);
19527
19613
  if (Object.keys(buildEnv).length > 0) {
19528
19614
  const lines = Object.entries(buildEnv).map(([k, v]) => `${k}=${JSON.stringify(v)}`);
19529
- await writeFile(path44.join(ctx.cwd, ".env.production.local"), `${lines.join("\n")}
19615
+ await writeFile(path45.join(ctx.cwd, ".env.production.local"), `${lines.join("\n")}
19530
19616
  `, "utf8");
19531
19617
  }
19532
- const consumerDockerfile = path44.join(ctx.cwd, "Dockerfile.preview");
19618
+ const consumerDockerfile = path45.join(ctx.cwd, "Dockerfile.preview");
19533
19619
  const { stat } = await import("fs/promises");
19534
19620
  let hasConsumerDockerfile = false;
19535
19621
  try {
@@ -19713,8 +19799,8 @@ var init_tickShellRunner = __esm({
19713
19799
  });
19714
19800
 
19715
19801
  // src/scripts/runScheduledImplementationTick.ts
19716
- import * as fs47 from "fs";
19717
- import * as path45 from "path";
19802
+ import * as fs48 from "fs";
19803
+ import * as path46 from "path";
19718
19804
  var runScheduledImplementationTick;
19719
19805
  var init_runScheduledImplementationTick = __esm({
19720
19806
  "src/scripts/runScheduledImplementationTick.ts"() {
@@ -19735,14 +19821,14 @@ var init_runScheduledImplementationTick = __esm({
19735
19821
  ctx.output.reason = `runScheduledImplementationTick: args.slug or ctx.args.${slugArg} must be non-empty capability slug`;
19736
19822
  return;
19737
19823
  }
19738
- const capability = resolveCapabilityFolder(slug, path45.resolve(ctx.cwd, jobsDir));
19824
+ const capability = resolveCapabilityFolder(slug, path46.resolve(ctx.cwd, jobsDir));
19739
19825
  if (!capability) {
19740
19826
  ctx.output.exitCode = 99;
19741
19827
  ctx.output.reason = `runScheduledImplementationTick: capability folder not found or incomplete: ${slug} (searched ${jobsDir} and company store)`;
19742
19828
  return;
19743
19829
  }
19744
- const shellPath = path45.join(profile.dir, shell);
19745
- if (!fs47.existsSync(shellPath)) {
19830
+ const shellPath = path46.join(profile.dir, shell);
19831
+ if (!fs48.existsSync(shellPath)) {
19746
19832
  ctx.output.exitCode = 99;
19747
19833
  ctx.output.reason = `runScheduledImplementationTick: shell not found: ${shell} (looked in ${profile.dir})`;
19748
19834
  return;
@@ -19774,13 +19860,13 @@ var init_runScheduledImplementationTick = __esm({
19774
19860
 
19775
19861
  // src/scripts/runSimpleCapabilityScript.ts
19776
19862
  import { spawnSync as spawnSync3 } from "child_process";
19777
- import * as fs48 from "fs";
19863
+ import * as fs49 from "fs";
19778
19864
  function formatDuration2(timeoutMs) {
19779
19865
  return timeoutMs % 6e4 === 0 ? `${timeoutMs / 6e4} minutes` : `${timeoutMs}ms`;
19780
19866
  }
19781
19867
  function isRegularFile2(filePath) {
19782
19868
  try {
19783
- const stat = fs48.lstatSync(filePath);
19869
+ const stat = fs49.lstatSync(filePath);
19784
19870
  return stat.isFile() && !stat.isSymbolicLink();
19785
19871
  } catch {
19786
19872
  return false;
@@ -19859,8 +19945,8 @@ var init_runSimpleCapabilityScript = __esm({
19859
19945
  });
19860
19946
 
19861
19947
  // src/scripts/runTickScript.ts
19862
- import * as fs49 from "fs";
19863
- import * as path46 from "path";
19948
+ import * as fs50 from "fs";
19949
+ import * as path47 from "path";
19864
19950
  var runTickScript;
19865
19951
  var init_runTickScript = __esm({
19866
19952
  "src/scripts/runTickScript.ts"() {
@@ -19880,10 +19966,10 @@ var init_runTickScript = __esm({
19880
19966
  ctx.output.reason = `runTickScript: ctx.args.${slugArg} must be a non-empty slug`;
19881
19967
  return;
19882
19968
  }
19883
- const capability = readCapabilityFolder(path46.resolve(ctx.cwd, jobsDir), slug);
19969
+ const capability = readCapabilityFolder(path47.resolve(ctx.cwd, jobsDir), slug);
19884
19970
  if (!capability) {
19885
19971
  ctx.output.exitCode = 99;
19886
- ctx.output.reason = `runTickScript: capability folder not found or incomplete: ${path46.resolve(ctx.cwd, jobsDir, slug)}`;
19972
+ ctx.output.reason = `runTickScript: capability folder not found or incomplete: ${path47.resolve(ctx.cwd, jobsDir, slug)}`;
19887
19973
  return;
19888
19974
  }
19889
19975
  const tickScript = capability.config.tickScript;
@@ -19892,8 +19978,8 @@ var init_runTickScript = __esm({
19892
19978
  ctx.output.reason = `runTickScript: capability ${slug} has no \`tickScript\` in profile.json \u2014 route via capability-tick instead`;
19893
19979
  return;
19894
19980
  }
19895
- const scriptPath = path46.isAbsolute(tickScript) ? tickScript : path46.join(ctx.cwd, tickScript);
19896
- if (!fs49.existsSync(scriptPath)) {
19981
+ const scriptPath = path47.isAbsolute(tickScript) ? tickScript : path47.join(ctx.cwd, tickScript);
19982
+ if (!fs50.existsSync(scriptPath)) {
19897
19983
  ctx.output.exitCode = 99;
19898
19984
  ctx.output.reason = `runTickScript: tickScript not found: ${scriptPath}`;
19899
19985
  return;
@@ -20175,7 +20261,7 @@ var init_syncFlow = __esm({
20175
20261
  });
20176
20262
 
20177
20263
  // src/scripts/validateAgencyModelProposal.ts
20178
- import * as path47 from "path";
20264
+ import * as path48 from "path";
20179
20265
  function validateModelBundle(bundle, expectedKind, options = {}) {
20180
20266
  const failures = [];
20181
20267
  validateOneModel(bundle.model, bundle.files, "model", true, failures, expectedKind, options);
@@ -20493,7 +20579,7 @@ var init_validateAgencyModelProposal = __esm({
20493
20579
  const bundle = parseAgencyModelProposal(raw);
20494
20580
  const expectedKind = readExpectedModelKind(args);
20495
20581
  const failures = validateModelBundle(bundle, expectedKind, {
20496
- capabilityRoot: path47.join(ctx.cwd, ".kody", "capabilities")
20582
+ capabilityRoot: path48.join(ctx.cwd, ".kody", "capabilities")
20497
20583
  });
20498
20584
  if (failures.length > 0) {
20499
20585
  throw new Error(`validateAgencyModelProposal: ${failures.join("; ")}`);
@@ -20556,7 +20642,7 @@ function stripAnsi2(s) {
20556
20642
  return s.replace(ANSI_RE2, "");
20557
20643
  }
20558
20644
  function runCommand2(command, cwd) {
20559
- return new Promise((resolve20) => {
20645
+ return new Promise((resolve21) => {
20560
20646
  const child = spawn6(command, {
20561
20647
  cwd,
20562
20648
  shell: true,
@@ -20583,11 +20669,11 @@ function runCommand2(command, cwd) {
20583
20669
  }, TEST_TIMEOUT_MS);
20584
20670
  child.on("exit", (code) => {
20585
20671
  clearTimeout(timer);
20586
- resolve20({ exitCode: code ?? -1, output: Buffer.concat(buffers).toString("utf-8") });
20672
+ resolve21({ exitCode: code ?? -1, output: Buffer.concat(buffers).toString("utf-8") });
20587
20673
  });
20588
20674
  child.on("error", (err) => {
20589
20675
  clearTimeout(timer);
20590
- resolve20({ exitCode: -1, output: err.message });
20676
+ resolve21({ exitCode: -1, output: err.message });
20591
20677
  });
20592
20678
  });
20593
20679
  }
@@ -20993,21 +21079,21 @@ function lineStream(stream) {
20993
21079
  tryDeliver();
20994
21080
  });
20995
21081
  return {
20996
- next: (timeoutMs) => new Promise((resolve20) => {
21082
+ next: (timeoutMs) => new Promise((resolve21) => {
20997
21083
  if (queue.length > 0) {
20998
- resolve20(queue.shift());
21084
+ resolve21(queue.shift());
20999
21085
  return;
21000
21086
  }
21001
21087
  if (ended) {
21002
- resolve20(null);
21088
+ resolve21(null);
21003
21089
  return;
21004
21090
  }
21005
- waiter = resolve20;
21091
+ waiter = resolve21;
21006
21092
  const t = setTimeout(
21007
21093
  () => {
21008
- if (waiter === resolve20) {
21094
+ if (waiter === resolve21) {
21009
21095
  waiter = null;
21010
- resolve20(null);
21096
+ resolve21(null);
21011
21097
  }
21012
21098
  },
21013
21099
  Math.max(0, timeoutMs)
@@ -21044,7 +21130,7 @@ var init_warmupMcp = __esm({
21044
21130
  });
21045
21131
 
21046
21132
  // src/scripts/writeAgentRunSummary.ts
21047
- import * as fs50 from "fs";
21133
+ import * as fs51 from "fs";
21048
21134
  var writeAgentRunSummary;
21049
21135
  var init_writeAgentRunSummary = __esm({
21050
21136
  "src/scripts/writeAgentRunSummary.ts"() {
@@ -21070,7 +21156,7 @@ var init_writeAgentRunSummary = __esm({
21070
21156
  if (reason) lines.push(`- **Reason:** ${reason}`);
21071
21157
  lines.push("");
21072
21158
  try {
21073
- fs50.appendFileSync(summaryPath, `${lines.join("\n")}
21159
+ fs51.appendFileSync(summaryPath, `${lines.join("\n")}
21074
21160
  `);
21075
21161
  } catch {
21076
21162
  }
@@ -21408,17 +21494,17 @@ var init_scripts = __esm({
21408
21494
  });
21409
21495
 
21410
21496
  // src/stateWorkspace.ts
21411
- import * as fs51 from "fs";
21412
- import * as path48 from "path";
21497
+ import * as fs52 from "fs";
21498
+ import * as path49 from "path";
21413
21499
  function tenantId(config) {
21414
21500
  const owner = config.github?.owner?.trim() || process.env.GITHUB_REPOSITORY?.split("/")[0]?.trim();
21415
21501
  const repo = config.github?.repo?.trim() || process.env.GITHUB_REPOSITORY?.split("/")[1]?.trim();
21416
21502
  return owner && repo ? `${owner}/${repo}` : null;
21417
21503
  }
21418
21504
  function writeRuntimeFile(cwd, relativePath, content) {
21419
- const target = path48.join(cwd, RUNTIME_ROOT, relativePath);
21420
- fs51.mkdirSync(path48.dirname(target), { recursive: true });
21421
- fs51.writeFileSync(target, content, "utf8");
21505
+ const target = path49.join(cwd, RUNTIME_ROOT, relativePath);
21506
+ fs52.mkdirSync(path49.dirname(target), { recursive: true });
21507
+ fs52.writeFileSync(target, content, "utf8");
21422
21508
  }
21423
21509
  function record(value) {
21424
21510
  return value && typeof value === "object" && !Array.isArray(value) ? value : null;
@@ -21483,11 +21569,11 @@ async function hydrateStateWorkspace(config, cwd, backendOverride) {
21483
21569
  throw new Error("Kody backend access is required for runtime workspace documents");
21484
21570
  return;
21485
21571
  }
21486
- const key = `${path48.resolve(cwd)}|${tenant}`;
21572
+ const key = `${path49.resolve(cwd)}|${tenant}`;
21487
21573
  if (hydratedWorkspaces.has(key)) return;
21488
21574
  const backend = backendOverride ?? createStateBackendFromEnv();
21489
- const root = path48.join(cwd, RUNTIME_ROOT);
21490
- fs51.rmSync(root, { recursive: true, force: true });
21575
+ const root = path49.join(cwd, RUNTIME_ROOT);
21576
+ fs52.rmSync(root, { recursive: true, force: true });
21491
21577
  await Promise.all([
21492
21578
  hydratePrefix(backend, tenant, cwd, "context:"),
21493
21579
  hydratePrefix(backend, tenant, cwd, "memory:"),
@@ -21503,7 +21589,7 @@ var init_stateWorkspace = __esm({
21503
21589
  "src/stateWorkspace.ts"() {
21504
21590
  "use strict";
21505
21591
  init_state_backend();
21506
- RUNTIME_ROOT = path48.join(".kody-engine", "runtime");
21592
+ RUNTIME_ROOT = path49.join(".kody-engine", "runtime");
21507
21593
  hydratedWorkspaces = /* @__PURE__ */ new Set();
21508
21594
  }
21509
21595
  });
@@ -21574,9 +21660,9 @@ var init_tools = __esm({
21574
21660
 
21575
21661
  // src/executor.ts
21576
21662
  import { spawn as spawn8 } from "child_process";
21577
- import * as fs52 from "fs";
21663
+ import * as fs53 from "fs";
21578
21664
  import * as os8 from "os";
21579
- import * as path49 from "path";
21665
+ import * as path50 from "path";
21580
21666
  function isMutatingPostflight(scriptName) {
21581
21667
  return MUTATING_POSTFLIGHTS.has(scriptName ?? "");
21582
21668
  }
@@ -21828,7 +21914,7 @@ async function runImplementation(profileName, input) {
21828
21914
  const jobWhyBlock = typeof ctx.data.jobWhy === "string" ? operatorRequestBlock(ctx.data.jobWhy) : null;
21829
21915
  const jobRefBlock = jobReferenceBlock(profileName, profile, ctx.data);
21830
21916
  const invokeAgent = async (prompt) => {
21831
- const externalPlugins = (profile.claudeCode.plugins ?? []).map((p) => path49.isAbsolute(p) ? p : path49.resolve(profile.dir, p)).filter((p) => p.length > 0);
21917
+ const externalPlugins = (profile.claudeCode.plugins ?? []).map((p) => path50.isAbsolute(p) ? p : path50.resolve(profile.dir, p)).filter((p) => p.length > 0);
21832
21918
  const syntheticPath = ctx.data.syntheticPluginPath;
21833
21919
  const pluginPaths = [...externalPlugins, ...syntheticPath ? [syntheticPath] : []];
21834
21920
  const agents = loadSubagents(profile);
@@ -21870,6 +21956,7 @@ async function runImplementation(profileName, input) {
21870
21956
  ndjsonDir,
21871
21957
  additionalDirectories: agentTaskArtifacts ? [agentTaskArtifacts.absDir] : void 0,
21872
21958
  allowedToolsOverride: profile.claudeCode.tools,
21959
+ disallowedToolsOverride: profile.claudeCode.disallowedTools,
21873
21960
  permissionModeOverride: profile.claudeCode.permissionMode,
21874
21961
  mcpServers: profile.claudeCode.mcpServers.length > 0 ? profile.claudeCode.mcpServers : void 0,
21875
21962
  pluginPaths: pluginPaths.length > 0 ? pluginPaths : void 0,
@@ -21908,7 +21995,11 @@ async function runImplementation(profileName, input) {
21908
21995
  verifyToolMaxAttempts: profile.claudeCode.verifyAttempts ?? null,
21909
21996
  verifyConfig: profile.claudeCode.enableVerifyTool ? config : void 0,
21910
21997
  implementationName: profileName,
21911
- settingSources: profile.claudeCode.settingSources
21998
+ settingSources: profile.claudeCode.settingSources,
21999
+ outputContract: typeof ctx.data.capabilityOutputPath === "string" && ctx.data.capabilityOutputSchema && typeof ctx.data.capabilityOutputSchema === "object" && !Array.isArray(ctx.data.capabilityOutputSchema) ? {
22000
+ path: ctx.data.capabilityOutputPath,
22001
+ schema: ctx.data.capabilityOutputSchema
22002
+ } : void 0
21912
22003
  });
21913
22004
  };
21914
22005
  ctx.data.__invokeAgent = invokeAgent;
@@ -22304,17 +22395,17 @@ function clearStampedLifecycleLabels(profile, ctx) {
22304
22395
  function resolveProfilePath(profileName, cwd = process.cwd()) {
22305
22396
  const found = resolveImplementation(profileName, getRuntimeProfileRootsForCwd(cwd));
22306
22397
  if (found) return found;
22307
- const here = path49.dirname(new URL(import.meta.url).pathname);
22398
+ const here = path50.dirname(new URL(import.meta.url).pathname);
22308
22399
  const candidates = [
22309
- path49.join(here, "implementations", profileName, "profile.json"),
22400
+ path50.join(here, "implementations", profileName, "profile.json"),
22310
22401
  // same-dir sibling (dev)
22311
- path49.join(here, "..", "implementations", profileName, "profile.json"),
22402
+ path50.join(here, "..", "implementations", profileName, "profile.json"),
22312
22403
  // up one (prod: dist/bin → dist/implementations)
22313
- path49.join(here, "..", "src", "implementations", profileName, "profile.json")
22404
+ path50.join(here, "..", "src", "implementations", profileName, "profile.json")
22314
22405
  // fallback
22315
22406
  ];
22316
22407
  for (const c of candidates) {
22317
- if (fs52.existsSync(c)) return c;
22408
+ if (fs53.existsSync(c)) return c;
22318
22409
  }
22319
22410
  return candidates[0];
22320
22411
  }
@@ -22429,15 +22520,15 @@ function resolveShellTimeoutMs(entry) {
22429
22520
  }
22430
22521
  async function runShellEntry(entry, ctx, profile) {
22431
22522
  const shellName = entry.shell;
22432
- const shellPath = path49.join(profile.dir, shellName);
22433
- if (!fs52.existsSync(shellPath)) {
22523
+ const shellPath = path50.join(profile.dir, shellName);
22524
+ if (!fs53.existsSync(shellPath)) {
22434
22525
  ctx.skipAgent = true;
22435
22526
  ctx.output.exitCode = 99;
22436
22527
  ctx.output.reason = `shell script not found: ${shellName} (looked in ${profile.dir})`;
22437
22528
  return;
22438
22529
  }
22439
22530
  const positional = entry.with ? Object.values(entry.with).map((v) => String(v)) : [];
22440
- const outputFile = path49.join(
22531
+ const outputFile = path50.join(
22441
22532
  os8.tmpdir(),
22442
22533
  `kody-shell-output-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`
22443
22534
  );
@@ -22467,14 +22558,14 @@ async function runShellEntry(entry, ctx, profile) {
22467
22558
  let killTimer;
22468
22559
  let escalateTimer;
22469
22560
  const result = await new Promise(
22470
- (resolve20) => {
22561
+ (resolve21) => {
22471
22562
  let settled = false;
22472
22563
  const settle = (code, signal, spawnErr) => {
22473
22564
  if (settled) return;
22474
22565
  settled = true;
22475
22566
  if (killTimer) clearTimeout(killTimer);
22476
22567
  if (escalateTimer) clearTimeout(escalateTimer);
22477
- resolve20({ code, signal, spawnErr });
22568
+ resolve21({ code, signal, spawnErr });
22478
22569
  };
22479
22570
  child.on("error", (err) => settle(null, null, err));
22480
22571
  child.on("close", (code, signal) => settle(code, signal));
@@ -22504,9 +22595,9 @@ async function runShellEntry(entry, ctx, profile) {
22504
22595
  }
22505
22596
  let sideChannelText = "";
22506
22597
  try {
22507
- if (fs52.existsSync(outputFile)) {
22508
- sideChannelText = fs52.readFileSync(outputFile, "utf-8");
22509
- fs52.rmSync(outputFile, { force: true });
22598
+ if (fs53.existsSync(outputFile)) {
22599
+ sideChannelText = fs53.readFileSync(outputFile, "utf-8");
22600
+ fs53.rmSync(outputFile, { force: true });
22510
22601
  }
22511
22602
  } catch {
22512
22603
  }
@@ -23362,11 +23453,11 @@ function exhaustedWorkflowTransitions(step, data, counts) {
23362
23453
  }
23363
23454
  function workflowResultConditionPaths(transitions) {
23364
23455
  return transitions.flatMap(
23365
- (transition) => Object.keys(transition.when ?? {}).filter((path54) => path54.startsWith("result."))
23456
+ (transition) => Object.keys(transition.when ?? {}).filter((path55) => path55.startsWith("result."))
23366
23457
  );
23367
23458
  }
23368
23459
  function conditionMatches(condition, context) {
23369
- return Object.entries(condition).every(([path54, expected]) => valueMatches(resolveDottedPath2(context, path54), expected));
23460
+ return Object.entries(condition).every(([path55, expected]) => valueMatches(resolveDottedPath2(context, path55), expected));
23370
23461
  }
23371
23462
  function withWorkflowBoundaryEval(capability, result) {
23372
23463
  const capabilityKind = capability.config.capabilityKind;
@@ -23803,7 +23894,7 @@ function translateOpenAISseToBrain(opts) {
23803
23894
 
23804
23895
  // src/servers/brain-serve.ts
23805
23896
  import { createServer as createServer2 } from "http";
23806
- import * as path52 from "path";
23897
+ import * as path53 from "path";
23807
23898
 
23808
23899
  // src/chat/loop.ts
23809
23900
  init_agent();
@@ -23811,13 +23902,13 @@ init_agents();
23811
23902
  init_config();
23812
23903
  init_registry();
23813
23904
  init_task_artifacts();
23814
- import * as fs16 from "fs";
23815
- import * as path17 from "path";
23905
+ import * as fs17 from "fs";
23906
+ import * as path18 from "path";
23816
23907
 
23817
23908
  // src/chat/attachments.ts
23818
23909
  init_runtimePaths();
23819
- import * as fs13 from "fs";
23820
- import * as path14 from "path";
23910
+ import * as fs14 from "fs";
23911
+ import * as path15 from "path";
23821
23912
  var INLINE_ATTACHMENT_RE = /(?:\[(?:Image|File): ([^\]]*)\]\n)?data:([\w.+-]+\/[\w.+-]+);base64,([A-Za-z0-9+/=]+)/g;
23822
23913
  var EXT_BY_MIME = {
23823
23914
  "image/png": "png",
@@ -23850,11 +23941,11 @@ function prepareAttachments(turns, cwd, sessionId) {
23850
23941
  if (!isImage) return `[File: ${name}]`;
23851
23942
  try {
23852
23943
  if (!dirEnsured) {
23853
- fs13.mkdirSync(dir, { recursive: true });
23944
+ fs14.mkdirSync(dir, { recursive: true });
23854
23945
  dirEnsured = true;
23855
23946
  }
23856
- const filePath = path14.join(dir, `${imageCounter}.${extFor(mime)}`);
23857
- fs13.writeFileSync(filePath, Buffer.from(data, "base64"));
23947
+ const filePath = path15.join(dir, `${imageCounter}.${extFor(mime)}`);
23948
+ fs14.writeFileSync(filePath, Buffer.from(data, "base64"));
23858
23949
  imageCounter += 1;
23859
23950
  imagePaths.push(filePath);
23860
23951
  return `[Image "${name}" is attached \u2014 saved to ${filePath}. Use the Read tool on that exact path to view it.]`;
@@ -23871,8 +23962,8 @@ function prepareAttachments(turns, cwd, sessionId) {
23871
23962
 
23872
23963
  // src/chat/codex-app-server.ts
23873
23964
  import { spawn as spawn3 } from "child_process";
23874
- import * as fs14 from "fs";
23875
- import * as path15 from "path";
23965
+ import * as fs15 from "fs";
23966
+ import * as path16 from "path";
23876
23967
  import { createInterface } from "readline";
23877
23968
  function codexThreadStartParams(args) {
23878
23969
  return {
@@ -23957,9 +24048,9 @@ var CodexAppServerClient = class {
23957
24048
  await this.request("thread/resume", { threadId });
23958
24049
  }
23959
24050
  async runTurn(args) {
23960
- await new Promise((resolve20, reject) => {
24051
+ await new Promise((resolve21, reject) => {
23961
24052
  this.process.turnWaiters.set(args.threadId, {
23962
- resolve: resolve20,
24053
+ resolve: resolve21,
23963
24054
  reject,
23964
24055
  onNotification: args.onNotification,
23965
24056
  queue: Promise.resolve()
@@ -23976,8 +24067,8 @@ var CodexAppServerClient = class {
23976
24067
  }
23977
24068
  request(method, params) {
23978
24069
  const id = this.process.nextId++;
23979
- return new Promise((resolve20, reject) => {
23980
- this.process.pending.set(id, { resolve: resolve20, reject });
24070
+ return new Promise((resolve21, reject) => {
24071
+ this.process.pending.set(id, { resolve: resolve21, reject });
23981
24072
  this.process.child.stdin.write(`${JSON.stringify({ method, id, params })}
23982
24073
  `);
23983
24074
  });
@@ -24043,11 +24134,11 @@ var CodexAppServerClient = class {
24043
24134
  };
24044
24135
  var clients = /* @__PURE__ */ new Map();
24045
24136
  function threadMapPath(cwd) {
24046
- return path15.join(cwd, ".kody-engine", "runtime", "codex-threads.json");
24137
+ return path16.join(cwd, ".kody-engine", "runtime", "codex-threads.json");
24047
24138
  }
24048
24139
  function readThreadMap(cwd) {
24049
24140
  try {
24050
- const value = JSON.parse(fs14.readFileSync(threadMapPath(cwd), "utf8"));
24141
+ const value = JSON.parse(fs15.readFileSync(threadMapPath(cwd), "utf8"));
24051
24142
  if (!value || typeof value !== "object" || Array.isArray(value)) return {};
24052
24143
  return Object.fromEntries(
24053
24144
  Object.entries(value).filter(
@@ -24060,8 +24151,8 @@ function readThreadMap(cwd) {
24060
24151
  }
24061
24152
  function writeThreadMap(cwd, map) {
24062
24153
  const file = threadMapPath(cwd);
24063
- fs14.mkdirSync(path15.dirname(file), { recursive: true });
24064
- fs14.writeFileSync(file, `${JSON.stringify(map, null, 2)}
24154
+ fs15.mkdirSync(path16.dirname(file), { recursive: true });
24155
+ fs15.writeFileSync(file, `${JSON.stringify(map, null, 2)}
24065
24156
  `);
24066
24157
  }
24067
24158
  async function runCodexChatTurn(args) {
@@ -24151,8 +24242,8 @@ async function runCodexChatTurn(args) {
24151
24242
  }
24152
24243
 
24153
24244
  // src/chat/events.ts
24154
- import * as fs15 from "fs";
24155
- import * as path16 from "path";
24245
+ import * as fs16 from "fs";
24246
+ import * as path17 from "path";
24156
24247
  import posixPath2 from "path/posix";
24157
24248
  var BackendEventSink = class {
24158
24249
  constructor(append, tenantId2, sessionId) {
@@ -24168,7 +24259,7 @@ var BackendEventSink = class {
24168
24259
  }
24169
24260
  };
24170
24261
  function eventsFilePath(cwd, sessionId) {
24171
- return path16.join(cwd, ".kody-engine", "runtime", "events", `${sessionId}.jsonl`);
24262
+ return path17.join(cwd, ".kody-engine", "runtime", "events", `${sessionId}.jsonl`);
24172
24263
  }
24173
24264
  var FileSink = class {
24174
24265
  constructor(file) {
@@ -24176,8 +24267,8 @@ var FileSink = class {
24176
24267
  }
24177
24268
  file;
24178
24269
  async emit(event) {
24179
- fs15.mkdirSync(path16.dirname(this.file), { recursive: true });
24180
- fs15.appendFileSync(this.file, `${JSON.stringify(event)}
24270
+ fs16.mkdirSync(path17.dirname(this.file), { recursive: true });
24271
+ fs16.appendFileSync(this.file, `${JSON.stringify(event)}
24181
24272
  `);
24182
24273
  }
24183
24274
  };
@@ -24442,7 +24533,7 @@ function buildImplementationCatalog() {
24442
24533
  const entries = [];
24443
24534
  for (const { name, profilePath } of discovered) {
24444
24535
  try {
24445
- const raw = JSON.parse(fs16.readFileSync(profilePath, "utf-8"));
24536
+ const raw = JSON.parse(fs17.readFileSync(profilePath, "utf-8"));
24446
24537
  const describe = typeof raw.describe === "string" ? raw.describe : "";
24447
24538
  const firstSentence = describe.split(/(?<=[.!?])\s+/, 1)[0] ?? "";
24448
24539
  entries.push({ name, describe: firstSentence.trim() });
@@ -24564,7 +24655,7 @@ async function runChatTurn(opts) {
24564
24655
  quiet: opts.quiet,
24565
24656
  additionalDirectories: [
24566
24657
  taskArtifactsPaths.absDir,
24567
- ...Array.from(new Set(imagePaths.map((p2) => path17.dirname(p2))))
24658
+ ...Array.from(new Set(imagePaths.map((p2) => path18.dirname(p2))))
24568
24659
  ],
24569
24660
  systemPromptAppend: systemPrompt,
24570
24661
  ...opts.reasoningEffort ? { reasoningEffort: opts.reasoningEffort } : {},
@@ -24752,10 +24843,10 @@ async function emit(sink, type, sessionId, suffix, payload) {
24752
24843
  var MEMORY_INDEX_REL = ".kody-engine/runtime/memory/INDEX.md";
24753
24844
  var MAX_INDEX_BYTES = 8e3;
24754
24845
  function readMemoryIndexBlock(cwd) {
24755
- const indexPath = path17.join(cwd, MEMORY_INDEX_REL);
24846
+ const indexPath = path18.join(cwd, MEMORY_INDEX_REL);
24756
24847
  let raw;
24757
24848
  try {
24758
- raw = fs16.readFileSync(indexPath, "utf-8");
24849
+ raw = fs17.readFileSync(indexPath, "utf-8");
24759
24850
  } catch {
24760
24851
  return "";
24761
24852
  }
@@ -24775,17 +24866,17 @@ _\u2026 (memory index truncated; use recall_search to read more)_` : trimmed;
24775
24866
  var CONTEXT_DIR_REL = ".kody-engine/runtime/context";
24776
24867
  var MAX_CONTEXT_BYTES = 12e3;
24777
24868
  function readContextBlock(cwd) {
24778
- const dir = path17.join(cwd, CONTEXT_DIR_REL);
24869
+ const dir = path18.join(cwd, CONTEXT_DIR_REL);
24779
24870
  let files;
24780
24871
  try {
24781
- files = fs16.readdirSync(dir).filter((f) => f.endsWith(".md")).sort();
24872
+ files = fs17.readdirSync(dir).filter((f) => f.endsWith(".md")).sort();
24782
24873
  } catch {
24783
24874
  return "";
24784
24875
  }
24785
24876
  const sections = [];
24786
24877
  for (const file of files) {
24787
24878
  try {
24788
- const content = fs16.readFileSync(path17.join(dir, file), "utf-8").trim();
24879
+ const content = fs17.readFileSync(path18.join(dir, file), "utf-8").trim();
24789
24880
  if (content) sections.push(`### ${file.replace(/\.md$/, "")}
24790
24881
 
24791
24882
  ${content}`);
@@ -24811,7 +24902,7 @@ var SYSTEM_PROMPT_OVERRIDE_REL = ".kody-engine/runtime/system-prompt.md";
24811
24902
  function readSystemPromptOverride(cwd) {
24812
24903
  let raw;
24813
24904
  try {
24814
- raw = fs16.readFileSync(path17.join(cwd, SYSTEM_PROMPT_OVERRIDE_REL), "utf-8");
24905
+ raw = fs17.readFileSync(path18.join(cwd, SYSTEM_PROMPT_OVERRIDE_REL), "utf-8");
24815
24906
  } catch {
24816
24907
  return null;
24817
24908
  }
@@ -24819,10 +24910,10 @@ function readSystemPromptOverride(cwd) {
24819
24910
  return trimmed.length > 0 ? trimmed : null;
24820
24911
  }
24821
24912
  function readInstructionsBlock(cwd) {
24822
- const instructionsPath = path17.join(cwd, INSTRUCTIONS_REL);
24913
+ const instructionsPath = path18.join(cwd, INSTRUCTIONS_REL);
24823
24914
  let raw;
24824
24915
  try {
24825
- raw = fs16.readFileSync(instructionsPath, "utf-8");
24916
+ raw = fs17.readFileSync(instructionsPath, "utf-8");
24826
24917
  } catch {
24827
24918
  return "";
24828
24919
  }
@@ -24856,15 +24947,15 @@ function resolveBrainDriver(runtime) {
24856
24947
  }
24857
24948
 
24858
24949
  // src/chat/session.ts
24859
- import * as fs17 from "fs";
24860
- import * as path18 from "path";
24950
+ import * as fs18 from "fs";
24951
+ import * as path19 from "path";
24861
24952
  import posixPath3 from "path/posix";
24862
24953
  function sessionFilePath(cwd, sessionId) {
24863
- return path18.join(cwd, ".kody-engine", "runtime", "sessions", `${sessionId}.jsonl`);
24954
+ return path19.join(cwd, ".kody-engine", "runtime", "sessions", `${sessionId}.jsonl`);
24864
24955
  }
24865
24956
  function readSession(file) {
24866
- if (!fs17.existsSync(file)) return [];
24867
- const raw = fs17.readFileSync(file, "utf-8").trim();
24957
+ if (!fs18.existsSync(file)) return [];
24958
+ const raw = fs18.readFileSync(file, "utf-8").trim();
24868
24959
  if (!raw) return [];
24869
24960
  const turns = [];
24870
24961
  for (const line of raw.split("\n")) {
@@ -24887,8 +24978,8 @@ init_config();
24887
24978
  init_state_backend();
24888
24979
  init_workflowDefinitions();
24889
24980
  import { createHash as createHash2 } from "crypto";
24890
- import * as fs19 from "fs";
24891
- import * as path20 from "path";
24981
+ import * as fs20 from "fs";
24982
+ import * as path21 from "path";
24892
24983
  var SLUG_RE = /^[a-z0-9][a-z0-9_-]{0,127}$/;
24893
24984
  var REPOSITORY_OWNED_NAMESPACES = ["loops"];
24894
24985
  function assertSafeDefinitionPath(filePath) {
@@ -24920,9 +25011,9 @@ function verifyDefinition(definition) {
24920
25011
  }
24921
25012
  function writeBundle(root, bundle) {
24922
25013
  for (const [filePath, contents] of Object.entries(bundle.files)) {
24923
- const target = path20.join(root, filePath);
24924
- fs19.mkdirSync(path20.dirname(target), { recursive: true });
24925
- fs19.writeFileSync(target, contents, "utf8");
25014
+ const target = path21.join(root, filePath);
25015
+ fs20.mkdirSync(path21.dirname(target), { recursive: true });
25016
+ fs20.writeFileSync(target, contents, "utf8");
24926
25017
  }
24927
25018
  }
24928
25019
  function writeDefinition(root, kind, definition) {
@@ -24930,22 +25021,22 @@ function writeDefinition(root, kind, definition) {
24930
25021
  if (kind === "agent") {
24931
25022
  const raw = bundle.files["agent.md"];
24932
25023
  if (typeof raw !== "string") throw new Error(`agent definition ${definition.slug} is missing agent.md`);
24933
- fs19.writeFileSync(path20.join(root, "agents", `${definition.slug}.md`), raw, "utf8");
25024
+ fs20.writeFileSync(path21.join(root, "agents", `${definition.slug}.md`), raw, "utf8");
24934
25025
  return;
24935
25026
  }
24936
25027
  if (kind === "goal") {
24937
- writeBundle(path20.join(root, "goals", definition.slug), bundle);
25028
+ writeBundle(path21.join(root, "goals", definition.slug), bundle);
24938
25029
  return;
24939
25030
  }
24940
25031
  if (kind === "implementation") {
24941
- writeBundle(path20.join(root, "implementations", definition.slug), bundle);
25032
+ writeBundle(path21.join(root, "implementations", definition.slug), bundle);
24942
25033
  return;
24943
25034
  }
24944
25035
  if (kind === "asset") {
24945
- writeBundle(path20.join(root, "shared"), bundle);
25036
+ writeBundle(path21.join(root, "shared"), bundle);
24946
25037
  return;
24947
25038
  }
24948
- writeBundle(path20.join(root, "capabilities", definition.slug), bundle);
25039
+ writeBundle(path21.join(root, "capabilities", definition.slug), bundle);
24949
25040
  }
24950
25041
  function writeWorkflow(root, document) {
24951
25042
  const workflow = normalizeWorkflowDefinition(document.definition);
@@ -24953,28 +25044,28 @@ function writeWorkflow(root, document) {
24953
25044
  const contents = `${JSON.stringify(workflow, null, 2)}
24954
25045
  `;
24955
25046
  const bundle = { schemaVersion: 1, files: { "workflow.json": contents } };
24956
- const target = path20.join(root, workflowDefinitionPath(document.workflowId));
24957
- fs19.mkdirSync(path20.dirname(target), { recursive: true });
24958
- fs19.writeFileSync(target, contents, "utf8");
25047
+ const target = path21.join(root, workflowDefinitionPath(document.workflowId));
25048
+ fs20.mkdirSync(path21.dirname(target), { recursive: true });
25049
+ fs20.writeFileSync(target, contents, "utf8");
24959
25050
  return definitionVersion(bundle);
24960
25051
  }
24961
25052
  function preserveRepositoryDefinitions(root, staging) {
24962
25053
  for (const namespace of REPOSITORY_OWNED_NAMESPACES) {
24963
- const source = path20.join(root, namespace);
24964
- if (!fs19.existsSync(source)) continue;
24965
- fs19.cpSync(source, path20.join(staging, namespace), { recursive: true });
25054
+ const source = path21.join(root, namespace);
25055
+ if (!fs20.existsSync(source)) continue;
25056
+ fs20.cpSync(source, path21.join(staging, namespace), { recursive: true });
24966
25057
  }
24967
25058
  }
24968
25059
  async function hydrateDefinitions(options) {
24969
- const root = path20.join(options.cwd, ".kody-engine", "definitions");
25060
+ const root = path21.join(options.cwd, ".kody-engine", "definitions");
24970
25061
  const staging = `${root}.tmp-${process.pid}-${Date.now()}`;
24971
- fs19.rmSync(staging, { recursive: true, force: true });
24972
- fs19.mkdirSync(path20.join(staging, "agents"), { recursive: true });
24973
- fs19.mkdirSync(path20.join(staging, "capabilities"), { recursive: true });
24974
- fs19.mkdirSync(path20.join(staging, "goals"), { recursive: true });
24975
- fs19.mkdirSync(path20.join(staging, "implementations"), { recursive: true });
24976
- fs19.mkdirSync(path20.join(staging, "shared"), { recursive: true });
24977
- fs19.mkdirSync(path20.join(staging, "workflows"), { recursive: true });
25062
+ fs20.rmSync(staging, { recursive: true, force: true });
25063
+ fs20.mkdirSync(path21.join(staging, "agents"), { recursive: true });
25064
+ fs20.mkdirSync(path21.join(staging, "capabilities"), { recursive: true });
25065
+ fs20.mkdirSync(path21.join(staging, "goals"), { recursive: true });
25066
+ fs20.mkdirSync(path21.join(staging, "implementations"), { recursive: true });
25067
+ fs20.mkdirSync(path21.join(staging, "shared"), { recursive: true });
25068
+ fs20.mkdirSync(path21.join(staging, "workflows"), { recursive: true });
24978
25069
  try {
24979
25070
  const [capabilities, agents, goals, implementations, assets, workflows] = await Promise.all([
24980
25071
  options.backend.listDefinitions(options.tenantId, "capability"),
@@ -25015,13 +25106,13 @@ async function hydrateDefinitions(options) {
25015
25106
  hydratedAt: (/* @__PURE__ */ new Date()).toISOString(),
25016
25107
  versions: Object.fromEntries(Object.entries(versions).sort(([left], [right]) => left.localeCompare(right)))
25017
25108
  };
25018
- fs19.writeFileSync(path20.join(staging, "manifest.json"), `${JSON.stringify(manifest, null, 2)}
25109
+ fs20.writeFileSync(path21.join(staging, "manifest.json"), `${JSON.stringify(manifest, null, 2)}
25019
25110
  `, "utf8");
25020
- fs19.rmSync(root, { recursive: true, force: true });
25021
- fs19.renameSync(staging, root);
25111
+ fs20.rmSync(root, { recursive: true, force: true });
25112
+ fs20.renameSync(staging, root);
25022
25113
  return { root, tenantId: options.tenantId, versions: manifest.versions };
25023
25114
  } catch (error) {
25024
- fs19.rmSync(staging, { recursive: true, force: true });
25115
+ fs20.rmSync(staging, { recursive: true, force: true });
25025
25116
  throw error;
25026
25117
  }
25027
25118
  }
@@ -25043,8 +25134,8 @@ async function hydrateDefinitionsFromEnv(cwd = process.cwd(), env = process.env)
25043
25134
 
25044
25135
  // src/kody-cli.ts
25045
25136
  import { execFileSync as execFileSync24 } from "child_process";
25046
- import * as fs53 from "fs";
25047
- import * as path50 from "path";
25137
+ import * as fs54 from "fs";
25138
+ import * as path51 from "path";
25048
25139
 
25049
25140
  // src/app-auth.ts
25050
25141
  import { createSign } from "crypto";
@@ -25173,7 +25264,7 @@ init_definition_paths();
25173
25264
 
25174
25265
  // src/dispatch.ts
25175
25266
  init_config();
25176
- import * as fs20 from "fs";
25267
+ import * as fs21 from "fs";
25177
25268
 
25178
25269
  // src/cron-match.ts
25179
25270
  var FIELD_BOUNDS = [
@@ -25280,10 +25371,10 @@ function autoDispatch(opts) {
25280
25371
  }
25281
25372
  const eventName = process.env.GITHUB_EVENT_NAME;
25282
25373
  const eventPath = process.env.GITHUB_EVENT_PATH;
25283
- if (!eventName || !eventPath || !fs20.existsSync(eventPath)) return null;
25374
+ if (!eventName || !eventPath || !fs21.existsSync(eventPath)) return null;
25284
25375
  let event = {};
25285
25376
  try {
25286
- event = JSON.parse(fs20.readFileSync(eventPath, "utf-8"));
25377
+ event = JSON.parse(fs21.readFileSync(eventPath, "utf-8"));
25287
25378
  } catch {
25288
25379
  return null;
25289
25380
  }
@@ -25407,7 +25498,7 @@ function autoDispatchTyped(opts) {
25407
25498
  if (legacy) return { kind: "route", ...legacy };
25408
25499
  const eventName = process.env.GITHUB_EVENT_NAME;
25409
25500
  const eventPath = process.env.GITHUB_EVENT_PATH;
25410
- if (!eventName || !eventPath || !fs20.existsSync(eventPath)) {
25501
+ if (!eventName || !eventPath || !fs21.existsSync(eventPath)) {
25411
25502
  return { kind: "silent", reason: "no GHA event context" };
25412
25503
  }
25413
25504
  if (eventName !== "issue_comment") {
@@ -25415,7 +25506,7 @@ function autoDispatchTyped(opts) {
25415
25506
  }
25416
25507
  let event = {};
25417
25508
  try {
25418
- event = JSON.parse(fs20.readFileSync(eventPath, "utf-8"));
25509
+ event = JSON.parse(fs21.readFileSync(eventPath, "utf-8"));
25419
25510
  } catch {
25420
25511
  return { kind: "silent", reason: "GHA event payload unreadable" };
25421
25512
  }
@@ -25469,7 +25560,7 @@ function dispatchScheduledWatches(opts) {
25469
25560
  for (const exe of listRuntimeProfilesForCwd(opts?.cwd ?? process.cwd())) {
25470
25561
  let raw;
25471
25562
  try {
25472
- raw = fs20.readFileSync(exe.profilePath, "utf-8");
25563
+ raw = fs21.readFileSync(exe.profilePath, "utf-8");
25473
25564
  } catch {
25474
25565
  continue;
25475
25566
  }
@@ -25846,9 +25937,9 @@ async function resolveAuthToken(env = process.env) {
25846
25937
  return void 0;
25847
25938
  }
25848
25939
  function detectPackageManager2(cwd) {
25849
- if (fs53.existsSync(path50.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
25850
- if (fs53.existsSync(path50.join(cwd, "yarn.lock"))) return "yarn";
25851
- if (fs53.existsSync(path50.join(cwd, "bun.lockb"))) return "bun";
25940
+ if (fs54.existsSync(path51.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
25941
+ if (fs54.existsSync(path51.join(cwd, "yarn.lock"))) return "yarn";
25942
+ if (fs54.existsSync(path51.join(cwd, "bun.lockb"))) return "bun";
25852
25943
  return "npm";
25853
25944
  }
25854
25945
  function shouldChainScheduledWatch(match) {
@@ -25951,8 +26042,8 @@ function postFailureTail(issueNumber, cwd, reason) {
25951
26042
  const logPath = lastRunLogPath(cwd);
25952
26043
  let tail = "";
25953
26044
  try {
25954
- if (fs53.existsSync(logPath)) {
25955
- const content = fs53.readFileSync(logPath, "utf-8");
26045
+ if (fs54.existsSync(logPath)) {
26046
+ const content = fs54.readFileSync(logPath, "utf-8");
25956
26047
  tail = content.slice(-3e3);
25957
26048
  }
25958
26049
  } catch {
@@ -25981,7 +26072,7 @@ async function runCi(argv) {
25981
26072
  return 0;
25982
26073
  }
25983
26074
  const args = parseCiArgs(argv);
25984
- const cwd = args.cwd ? path50.resolve(args.cwd) : process.cwd();
26075
+ const cwd = args.cwd ? path51.resolve(args.cwd) : process.cwd();
25985
26076
  try {
25986
26077
  const n = unpackAllSecrets();
25987
26078
  if (n > 0) process.stdout.write(`\u2192 kody: unpacked ${n} secret(s) from ALL_SECRETS
@@ -26047,9 +26138,9 @@ async function runCi(argv) {
26047
26138
  forceRunCliArgs = { goal: envForceMessage };
26048
26139
  }
26049
26140
  }
26050
- if (!args.issueNumber && !autoFallback && !forceRunAction && !runRequestFanOut && eventName === "workflow_dispatch" && dispatchEventPath && fs53.existsSync(dispatchEventPath)) {
26141
+ if (!args.issueNumber && !autoFallback && !forceRunAction && !runRequestFanOut && eventName === "workflow_dispatch" && dispatchEventPath && fs54.existsSync(dispatchEventPath)) {
26051
26142
  try {
26052
- const evt = JSON.parse(fs53.readFileSync(dispatchEventPath, "utf-8"));
26143
+ const evt = JSON.parse(fs54.readFileSync(dispatchEventPath, "utf-8"));
26053
26144
  const inputs = objectValue2(evt.inputs);
26054
26145
  const issueInput = parseInt(String(inputs?.issue_number ?? ""), 10);
26055
26146
  const sessionInput = String(inputs?.sessionId ?? "");
@@ -26464,8 +26555,8 @@ init_repoWorkspace();
26464
26555
 
26465
26556
  // src/scripts/brainTurnLog.ts
26466
26557
  init_runtimePaths();
26467
- import * as fs54 from "fs";
26468
- import * as path51 from "path";
26558
+ import * as fs55 from "fs";
26559
+ import * as path52 from "path";
26469
26560
  import posixPath4 from "path/posix";
26470
26561
  var live = /* @__PURE__ */ new Map();
26471
26562
  function brainEventsFilePath(dir, chatId) {
@@ -26473,8 +26564,8 @@ function brainEventsFilePath(dir, chatId) {
26473
26564
  }
26474
26565
  function lastPersistedSeq(dir, chatId) {
26475
26566
  const p = brainEventsFilePath(dir, chatId);
26476
- if (!fs54.existsSync(p)) return 0;
26477
- const lines = fs54.readFileSync(p, "utf-8").split("\n").filter(Boolean);
26567
+ if (!fs55.existsSync(p)) return 0;
26568
+ const lines = fs55.readFileSync(p, "utf-8").split("\n").filter(Boolean);
26478
26569
  if (lines.length === 0) return 0;
26479
26570
  try {
26480
26571
  return JSON.parse(lines[lines.length - 1]).seq || 0;
@@ -26484,9 +26575,9 @@ function lastPersistedSeq(dir, chatId) {
26484
26575
  }
26485
26576
  function readSince(dir, chatId, since) {
26486
26577
  const p = brainEventsFilePath(dir, chatId);
26487
- if (!fs54.existsSync(p)) return [];
26578
+ if (!fs55.existsSync(p)) return [];
26488
26579
  const out = [];
26489
- for (const line of fs54.readFileSync(p, "utf-8").split("\n")) {
26580
+ for (const line of fs55.readFileSync(p, "utf-8").split("\n")) {
26490
26581
  if (!line) continue;
26491
26582
  try {
26492
26583
  const rec = JSON.parse(line);
@@ -26512,12 +26603,12 @@ function beginTurn(dir, chatId) {
26512
26603
  };
26513
26604
  live.set(chatId, state);
26514
26605
  const p = brainEventsFilePath(dir, chatId);
26515
- fs54.mkdirSync(path51.dirname(p), { recursive: true });
26606
+ fs55.mkdirSync(path52.dirname(p), { recursive: true });
26516
26607
  return (event) => {
26517
26608
  state.seq += 1;
26518
26609
  const rec = { seq: state.seq, turn, ts: Date.now(), event };
26519
26610
  try {
26520
- fs54.appendFileSync(p, `${JSON.stringify(rec)}
26611
+ fs55.appendFileSync(p, `${JSON.stringify(rec)}
26521
26612
  `);
26522
26613
  } catch (err) {
26523
26614
  process.stderr.write(
@@ -26556,7 +26647,7 @@ function endTurnIfUnterminated(dir, chatId, errMessage) {
26556
26647
  event: { type: "error", error: errMessage || "turn ended unexpectedly", chatId }
26557
26648
  };
26558
26649
  try {
26559
- fs54.appendFileSync(brainEventsFilePath(dir, chatId), `${JSON.stringify(rec)}
26650
+ fs55.appendFileSync(brainEventsFilePath(dir, chatId), `${JSON.stringify(rec)}
26560
26651
  `);
26561
26652
  } catch {
26562
26653
  }
@@ -26650,17 +26741,17 @@ function authOk(req, expected) {
26650
26741
  return false;
26651
26742
  }
26652
26743
  function readJsonBody(req) {
26653
- return new Promise((resolve20, reject) => {
26744
+ return new Promise((resolve21, reject) => {
26654
26745
  const chunks = [];
26655
26746
  req.on("data", (c) => chunks.push(c));
26656
26747
  req.on("end", () => {
26657
26748
  const raw = Buffer.concat(chunks).toString("utf-8");
26658
26749
  if (!raw.trim()) {
26659
- resolve20({});
26750
+ resolve21({});
26660
26751
  return;
26661
26752
  }
26662
26753
  try {
26663
- resolve20(JSON.parse(raw));
26754
+ resolve21(JSON.parse(raw));
26664
26755
  } catch (err) {
26665
26756
  reject(err instanceof Error ? err : new Error(String(err)));
26666
26757
  }
@@ -26952,7 +27043,7 @@ function buildServer(opts) {
26952
27043
  const runTurn = opts.runTurn ?? runChatTurn;
26953
27044
  const createStore = opts.createStore ?? createSessionStore;
26954
27045
  const cloneRepo = opts.cloneRepo ?? defaultCloneRepo;
26955
- const reposRoot = opts.reposRoot ?? path52.join(path52.dirname(path52.resolve(opts.cwd)), "repos");
27046
+ const reposRoot = opts.reposRoot ?? path53.join(path53.dirname(path53.resolve(opts.cwd)), "repos");
26956
27047
  return createServer2(async (req, res) => {
26957
27048
  if (!req.method || !req.url) {
26958
27049
  sendJson(res, 400, { error: "bad request" });
@@ -27033,11 +27124,11 @@ async function brainServe(opts) {
27033
27124
  litellmUrl,
27034
27125
  driver
27035
27126
  });
27036
- await new Promise((resolve20) => {
27127
+ await new Promise((resolve21) => {
27037
27128
  server.listen(port, "0.0.0.0", () => {
27038
27129
  process.stdout.write(`[brain-serve] listening on 0.0.0.0:${port} (cwd=${opts.cwd})
27039
27130
  `);
27040
- resolve20();
27131
+ resolve21();
27041
27132
  });
27042
27133
  });
27043
27134
  const shutdown = (signal) => {
@@ -27292,14 +27383,14 @@ async function startBrainProxy(opts) {
27292
27383
  const { httpServer, handler } = buildBrainProxy(opts);
27293
27384
  const port = opts.port ?? 0;
27294
27385
  const host = opts.host ?? "127.0.0.1";
27295
- await new Promise((resolve20) => httpServer.listen(port, host, () => resolve20()));
27386
+ await new Promise((resolve21) => httpServer.listen(port, host, () => resolve21()));
27296
27387
  const addr = httpServer.address();
27297
27388
  return {
27298
27389
  httpServer,
27299
27390
  port: addr.port,
27300
27391
  url: `http://${host}:${addr.port}`,
27301
- stop: () => new Promise((resolve20) => {
27302
- httpServer.close(() => resolve20());
27392
+ stop: () => new Promise((resolve21) => {
27393
+ httpServer.close(() => resolve21());
27303
27394
  }),
27304
27395
  handler
27305
27396
  };
@@ -27449,23 +27540,23 @@ function buildMcpHttpServer(opts) {
27449
27540
  httpServer,
27450
27541
  routes,
27451
27542
  port,
27452
- stop: () => new Promise((resolve20) => {
27543
+ stop: () => new Promise((resolve21) => {
27453
27544
  let pending = transports.size;
27454
27545
  if (pending === 0) {
27455
- httpServer.close(() => resolve20());
27546
+ httpServer.close(() => resolve21());
27456
27547
  return;
27457
27548
  }
27458
27549
  for (const transport of transports.values()) {
27459
27550
  void transport.close().finally(() => {
27460
27551
  pending--;
27461
- if (pending === 0) httpServer.close(() => resolve20());
27552
+ if (pending === 0) httpServer.close(() => resolve21());
27462
27553
  });
27463
27554
  }
27464
27555
  })
27465
27556
  };
27466
27557
  }
27467
27558
  function listenMcpHttpServer(server, host = "127.0.0.1") {
27468
- return new Promise((resolve20, reject) => {
27559
+ return new Promise((resolve21, reject) => {
27469
27560
  server.httpServer.once("error", reject);
27470
27561
  server.httpServer.listen(server.port, host, () => {
27471
27562
  server.httpServer.off("error", reject);
@@ -27473,7 +27564,7 @@ function listenMcpHttpServer(server, host = "127.0.0.1") {
27473
27564
  if (addr && typeof addr === "object") {
27474
27565
  server.port = addr.port;
27475
27566
  }
27476
- resolve20();
27567
+ resolve21();
27477
27568
  });
27478
27569
  });
27479
27570
  }
@@ -27556,7 +27647,7 @@ async function loadConfigSafe() {
27556
27647
  }
27557
27648
 
27558
27649
  // src/chat-cli.ts
27559
- import * as path53 from "path";
27650
+ import * as path54 from "path";
27560
27651
 
27561
27652
  // src/chat/inbox.ts
27562
27653
  import { execFileSync as execFileSync25 } from "child_process";
@@ -27623,7 +27714,7 @@ async function waitForNextUserMessage(opts) {
27623
27714
  }
27624
27715
  }
27625
27716
  function sleep3(ms) {
27626
- return new Promise((resolve20) => setTimeout(resolve20, ms));
27717
+ return new Promise((resolve21) => setTimeout(resolve21, ms));
27627
27718
  }
27628
27719
  function currentBranch(cwd) {
27629
27720
  try {
@@ -27847,7 +27938,7 @@ async function runChat(argv) {
27847
27938
  ${CHAT_HELP}`);
27848
27939
  return 64;
27849
27940
  }
27850
- const cwd = args.cwd ? path53.resolve(args.cwd) : process.cwd();
27941
+ const cwd = args.cwd ? path54.resolve(args.cwd) : process.cwd();
27851
27942
  const sessionId = args.sessionId;
27852
27943
  const runRequest = readRunRequestFromEnv();
27853
27944
  if (runRequest && "request" in runRequest) {
@@ -28043,8 +28134,8 @@ var FlyClient = class {
28043
28134
  get fetch() {
28044
28135
  return this.opts.fetchImpl ?? fetch;
28045
28136
  }
28046
- async call(path54, init = {}) {
28047
- const res = await this.fetch(`${FLY_API_BASE}${path54}`, {
28137
+ async call(path55, init = {}) {
28138
+ const res = await this.fetch(`${FLY_API_BASE}${path55}`, {
28048
28139
  method: init.method ?? "GET",
28049
28140
  headers: {
28050
28141
  Authorization: `Bearer ${this.opts.token}`,
@@ -28055,7 +28146,7 @@ var FlyClient = class {
28055
28146
  if (res.status === 404 && init.allow404) return null;
28056
28147
  if (!res.ok) {
28057
28148
  const text2 = await res.text().catch(() => "");
28058
- throw new Error(`Fly API ${res.status} on ${path54}: ${text2.slice(0, 200) || res.statusText}`);
28149
+ throw new Error(`Fly API ${res.status} on ${path55}: ${text2.slice(0, 200) || res.statusText}`);
28059
28150
  }
28060
28151
  if (res.status === 204) return null;
28061
28152
  const raw = await res.text();
@@ -28568,14 +28659,14 @@ function sendJson2(res, status, body) {
28568
28659
  res.end(JSON.stringify(body));
28569
28660
  }
28570
28661
  function readJsonBody2(req) {
28571
- return new Promise((resolve20, reject) => {
28662
+ return new Promise((resolve21, reject) => {
28572
28663
  const chunks = [];
28573
28664
  req.on("data", (c) => chunks.push(c));
28574
28665
  req.on("end", () => {
28575
28666
  const raw = Buffer.concat(chunks).toString("utf-8");
28576
- if (!raw.trim()) return resolve20({});
28667
+ if (!raw.trim()) return resolve21({});
28577
28668
  try {
28578
- resolve20(JSON.parse(raw));
28669
+ resolve21(JSON.parse(raw));
28579
28670
  } catch (err) {
28580
28671
  reject(err instanceof Error ? err : new Error(String(err)));
28581
28672
  }
@@ -28729,10 +28820,10 @@ async function poolServe() {
28729
28820
  }
28730
28821
  });
28731
28822
  const apiHost = process.env.POOL_API_HOST ?? "::";
28732
- await new Promise((resolve20) => {
28823
+ await new Promise((resolve21) => {
28733
28824
  server.listen(apiPort, apiHost, () => {
28734
28825
  log(`listening on ${apiHost}:${apiPort} (min=${min}, app=${app}, region=${region})`);
28735
- resolve20();
28826
+ resolve21();
28736
28827
  });
28737
28828
  });
28738
28829
  if (loopTickEnabled) void runLoopTick();
@@ -28751,7 +28842,7 @@ async function poolServe() {
28751
28842
 
28752
28843
  // src/servers/runner-serve.ts
28753
28844
  import { spawn as spawn9 } from "child_process";
28754
- import * as fs55 from "fs";
28845
+ import * as fs56 from "fs";
28755
28846
  import { createServer as createServer6 } from "http";
28756
28847
  var DEFAULT_PORT2 = 8080;
28757
28848
  var DEFAULT_WORKDIR = "/workspace/repo";
@@ -28772,17 +28863,17 @@ function authOk2(req, expected) {
28772
28863
  return false;
28773
28864
  }
28774
28865
  function readJsonBody3(req) {
28775
- return new Promise((resolve20, reject) => {
28866
+ return new Promise((resolve21, reject) => {
28776
28867
  const chunks = [];
28777
28868
  req.on("data", (c) => chunks.push(c));
28778
28869
  req.on("end", () => {
28779
28870
  const raw = Buffer.concat(chunks).toString("utf-8");
28780
28871
  if (!raw.trim()) {
28781
- resolve20({});
28872
+ resolve21({});
28782
28873
  return;
28783
28874
  }
28784
28875
  try {
28785
- resolve20(JSON.parse(raw));
28876
+ resolve21(JSON.parse(raw));
28786
28877
  } catch (err) {
28787
28878
  reject(err instanceof Error ? err : new Error(String(err)));
28788
28879
  }
@@ -28827,8 +28918,8 @@ async function defaultRunJob(job) {
28827
28918
  const workdir = process.env.RUNNER_WORKDIR ?? DEFAULT_WORKDIR;
28828
28919
  const branch = job.ref ?? "main";
28829
28920
  const authUrl = `https://x-access-token:${job.githubToken}@github.com/${job.repo}.git`;
28830
- fs55.rmSync(workdir, { recursive: true, force: true });
28831
- fs55.mkdirSync(workdir, { recursive: true });
28921
+ fs56.rmSync(workdir, { recursive: true, force: true });
28922
+ fs56.mkdirSync(workdir, { recursive: true });
28832
28923
  const allSecrets = typeof job.allSecrets === "string" ? job.allSecrets : JSON.stringify(job.allSecrets ?? {});
28833
28924
  const target = job.runRequest.target;
28834
28925
  const interactive = target.type === "chat";
@@ -28857,13 +28948,13 @@ async function defaultRunJob(job) {
28857
28948
  ...interactive && job.idleExitMs ? { KODY_IDLE_EXIT_MS: String(job.idleExitMs) } : {},
28858
28949
  ...interactive && job.hardCapMs ? { KODY_HARD_CAP_MS: String(job.hardCapMs) } : {}
28859
28950
  };
28860
- const run = (cmd, args, cwd) => new Promise((resolve20) => {
28951
+ const run = (cmd, args, cwd) => new Promise((resolve21) => {
28861
28952
  const child = spawn9(cmd, args, { stdio: "inherit", env: childEnv, cwd });
28862
- child.on("exit", (code) => resolve20(code ?? 0));
28953
+ child.on("exit", (code) => resolve21(code ?? 0));
28863
28954
  child.on("error", (err) => {
28864
28955
  process.stderr.write(`[runner-serve] ${cmd} failed: ${err.message}
28865
28956
  `);
28866
- resolve20(1);
28957
+ resolve21(1);
28867
28958
  });
28868
28959
  });
28869
28960
  process.stdout.write(`[runner-serve] job ${job.jobId}: cloning ${job.repo}@${branch}
@@ -28939,11 +29030,11 @@ async function runnerServe() {
28939
29030
  const port = Number(process.env.PORT ?? DEFAULT_PORT2);
28940
29031
  const server = buildServer2({ apiKey });
28941
29032
  const host = process.env.RUNNER_HOST ?? "::";
28942
- await new Promise((resolve20) => {
29033
+ await new Promise((resolve21) => {
28943
29034
  server.listen(port, host, () => {
28944
29035
  process.stdout.write(`[runner-serve] listening on ${host}:${port} (idle, awaiting job)
28945
29036
  `);
28946
- resolve20();
29037
+ resolve21();
28947
29038
  });
28948
29039
  });
28949
29040
  const shutdown = (signal) => {
@@ -29012,14 +29103,14 @@ async function serve(opts) {
29012
29103
  `);
29013
29104
  const args = ["--dangerously-skip-permissions", "--model", model.model];
29014
29105
  const child = spawn10("claude", args, { stdio: "inherit", env: editorEnv, cwd: opts.cwd });
29015
- const exitCode = await new Promise((resolve20) => {
29016
- child.on("exit", (code) => resolve20(code ?? 0));
29106
+ const exitCode = await new Promise((resolve21) => {
29107
+ child.on("exit", (code) => resolve21(code ?? 0));
29017
29108
  child.on("error", (err) => {
29018
29109
  process.stderr.write(`[kody serve] failed to launch Claude Code: ${err.message}
29019
29110
  `);
29020
29111
  process.stderr.write(` Install: https://docs.anthropic.com/claude/docs/claude-code
29021
29112
  `);
29022
- resolve20(1);
29113
+ resolve21(1);
29023
29114
  });
29024
29115
  });
29025
29116
  killProxy();