@kody-ade/kody-engine 0.4.564 → 0.4.565

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.564",
18
+ version: "0.4.565",
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",
@@ -669,6 +669,29 @@ var init_format = __esm({
669
669
  }
670
670
  });
671
671
 
672
+ // src/fileEditGuards.ts
673
+ import * as fs3 from "fs";
674
+ import * as path3 from "path";
675
+ function createMissingParentWriteGuard(cwd) {
676
+ return async (input) => {
677
+ const toolInput = input.tool_input;
678
+ if (!toolInput || typeof toolInput !== "object" || Array.isArray(toolInput)) return {};
679
+ const filePath = toolInput.file_path;
680
+ if (typeof filePath !== "string" || filePath.length === 0) return {};
681
+ const resolvedPath = path3.resolve(cwd, filePath);
682
+ if (fs3.existsSync(path3.dirname(resolvedPath))) return {};
683
+ return {
684
+ decision: "block",
685
+ reason: `Cannot write ${resolvedPath}: its parent directory does not exist. Locate and edit the real repository source path first. If this task genuinely requires a new directory, create that directory explicitly before writing the file.`
686
+ };
687
+ };
688
+ }
689
+ var init_fileEditGuards = __esm({
690
+ "src/fileEditGuards.ts"() {
691
+ "use strict";
692
+ }
693
+ });
694
+
672
695
  // src/agency/capability-contract-validation.ts
673
696
  import Ajv from "ajv";
674
697
  function createCapabilityContractValueValidator(compile) {
@@ -741,12 +764,12 @@ var init_capability_contract_validation = __esm({
741
764
  });
742
765
 
743
766
  // src/outputContractHooks.ts
744
- import * as fs3 from "fs";
745
- import * as path3 from "path";
767
+ import * as fs4 from "fs";
768
+ import * as path4 from "path";
746
769
  function outputContractError(contract) {
747
770
  let value;
748
771
  try {
749
- value = JSON.parse(fs3.readFileSync(contract.path, "utf8"));
772
+ value = JSON.parse(fs4.readFileSync(contract.path, "utf8"));
750
773
  } catch (error) {
751
774
  return `The required output file is missing or is not valid JSON: ${error instanceof Error ? error.message : String(error)}`;
752
775
  }
@@ -761,12 +784,12 @@ function correctionMessage(contract, error) {
761
784
  return `The authoritative output does not match its required contract: ${error}. Please overwrite ${contract.path} with only the required JSON shape before finishing.`;
762
785
  }
763
786
  function createOutputContractPostWriteHook(contract) {
764
- const expectedPath = path3.resolve(contract.path);
787
+ const expectedPath = path4.resolve(contract.path);
765
788
  return async (input) => {
766
789
  const toolInput = input.tool_input;
767
790
  if (!toolInput || typeof toolInput !== "object" || Array.isArray(toolInput)) return {};
768
791
  const filePath = toolInput.file_path;
769
- if (typeof filePath !== "string" || path3.resolve(filePath) !== expectedPath) return {};
792
+ if (typeof filePath !== "string" || path4.resolve(filePath) !== expectedPath) return {};
770
793
  const error = outputContractError(contract);
771
794
  if (!error) return {};
772
795
  return {
@@ -779,7 +802,7 @@ function createOutputContractPostWriteHook(contract) {
779
802
  }
780
803
  function createOutputContractStopHook(contract) {
781
804
  return async () => {
782
- if (!fs3.existsSync(contract.path)) {
805
+ if (!fs4.existsSync(contract.path)) {
783
806
  return {
784
807
  decision: "block",
785
808
  reason: "Continue the Journey from the current page and complete the next unresolved user outcome. Do not write the result merely because you paused; write it only after the Journey passes, fails, or cannot safely continue."
@@ -810,21 +833,21 @@ __export(runtimePaths_exports, {
810
833
  });
811
834
  import { createHash } from "crypto";
812
835
  import * as os2 from "os";
813
- import * as path4 from "path";
836
+ import * as path5 from "path";
814
837
  function runtimeDirForCwd(cwd, ...parts) {
815
- const key = createHash("sha256").update(path4.resolve(cwd)).digest("hex").slice(0, 16);
816
- return path4.join(os2.tmpdir(), "kody-engine", key, ...parts);
838
+ const key = createHash("sha256").update(path5.resolve(cwd)).digest("hex").slice(0, 16);
839
+ return path5.join(os2.tmpdir(), "kody-engine", key, ...parts);
817
840
  }
818
841
  function runtimeStatePath(cwd, ...parts) {
819
842
  const configuredRoot = process.env.KODY_RUNTIME_DIR?.trim();
820
- const base = configuredRoot ? path4.resolve(configuredRoot) : runtimeDirForCwd(cwd);
821
- return path4.join(base, ...parts);
843
+ const base = configuredRoot ? path5.resolve(configuredRoot) : runtimeDirForCwd(cwd);
844
+ return path5.join(base, ...parts);
822
845
  }
823
846
  function agentRunDir(cwd) {
824
847
  return runtimeStatePath(cwd, "agent-runs");
825
848
  }
826
849
  function lastRunLogPath(cwd) {
827
- return path4.join(agentRunDir(cwd), "last-run.jsonl");
850
+ return path5.join(agentRunDir(cwd), "last-run.jsonl");
828
851
  }
829
852
  var init_runtimePaths = __esm({
830
853
  "src/runtimePaths.ts"() {
@@ -833,31 +856,31 @@ var init_runtimePaths = __esm({
833
856
  });
834
857
 
835
858
  // src/scripts/buildSyntheticPlugin.ts
836
- import * as fs4 from "fs";
859
+ import * as fs5 from "fs";
837
860
  import * as os3 from "os";
838
- import * as path5 from "path";
861
+ import * as path6 from "path";
839
862
  function getPluginsCatalogRoot() {
840
- const here = path5.dirname(new URL(import.meta.url).pathname);
863
+ const here = path6.dirname(new URL(import.meta.url).pathname);
841
864
  const candidates = [
842
- path5.join(here, "..", "plugins"),
865
+ path6.join(here, "..", "plugins"),
843
866
  // dev: src/scripts → src/plugins
844
- path5.join(here, "..", "..", "plugins"),
867
+ path6.join(here, "..", "..", "plugins"),
845
868
  // built: dist/scripts → dist/plugins
846
- path5.join(here, "..", "..", "src", "plugins")
869
+ path6.join(here, "..", "..", "src", "plugins")
847
870
  // fallback
848
871
  ];
849
872
  for (const c of candidates) {
850
- if (fs4.existsSync(c) && fs4.statSync(c).isDirectory()) return c;
873
+ if (fs5.existsSync(c) && fs5.statSync(c).isDirectory()) return c;
851
874
  }
852
875
  return candidates[0];
853
876
  }
854
877
  function copyDir(src, dst) {
855
- fs4.mkdirSync(dst, { recursive: true });
856
- for (const ent of fs4.readdirSync(src, { withFileTypes: true })) {
857
- const s = path5.join(src, ent.name);
858
- const d = path5.join(dst, ent.name);
878
+ fs5.mkdirSync(dst, { recursive: true });
879
+ for (const ent of fs5.readdirSync(src, { withFileTypes: true })) {
880
+ const s = path6.join(src, ent.name);
881
+ const d = path6.join(dst, ent.name);
859
882
  if (ent.isDirectory()) copyDir(s, d);
860
- else if (ent.isFile()) fs4.copyFileSync(s, d);
883
+ else if (ent.isFile()) fs5.copyFileSync(s, d);
861
884
  }
862
885
  }
863
886
  var buildSyntheticPlugin;
@@ -870,47 +893,47 @@ var init_buildSyntheticPlugin = __esm({
870
893
  if (!needsSynthetic) return;
871
894
  const catalog = getPluginsCatalogRoot();
872
895
  const runId = `${profile.name}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
873
- const root = path5.join(os3.tmpdir(), `kody-synth-${runId}`);
874
- fs4.mkdirSync(path5.join(root, ".claude-plugin"), { recursive: true });
896
+ const root = path6.join(os3.tmpdir(), `kody-synth-${runId}`);
897
+ fs5.mkdirSync(path6.join(root, ".claude-plugin"), { recursive: true });
875
898
  const resolvePart = (bucket, entry) => {
876
- const local = path5.join(profile.dir, bucket, entry);
877
- if (fs4.existsSync(local)) return local;
878
- const shared = path5.resolve(profile.dir, "..", "..", "shared", bucket, entry);
879
- if (fs4.existsSync(shared)) return shared;
880
- const central = path5.join(catalog, bucket, entry);
881
- if (fs4.existsSync(central)) return central;
899
+ const local = path6.join(profile.dir, bucket, entry);
900
+ if (fs5.existsSync(local)) return local;
901
+ const shared = path6.resolve(profile.dir, "..", "..", "shared", bucket, entry);
902
+ if (fs5.existsSync(shared)) return shared;
903
+ const central = path6.join(catalog, bucket, entry);
904
+ if (fs5.existsSync(central)) return central;
882
905
  throw new Error(
883
- `buildSyntheticPlugin: ${bucket} entry '${entry}' not found in implementation dir (${profile.dir}/${bucket}/), Store shared assets (${path5.dirname(shared)}/), or catalog (${catalog}/${bucket}/)`
906
+ `buildSyntheticPlugin: ${bucket} entry '${entry}' not found in implementation dir (${profile.dir}/${bucket}/), Store shared assets (${path6.dirname(shared)}/), or catalog (${catalog}/${bucket}/)`
884
907
  );
885
908
  };
886
909
  if (cc.skills.length > 0) {
887
- const dst = path5.join(root, "skills");
888
- fs4.mkdirSync(dst, { recursive: true });
910
+ const dst = path6.join(root, "skills");
911
+ fs5.mkdirSync(dst, { recursive: true });
889
912
  for (const name of cc.skills) {
890
- copyDir(resolvePart("skills", name), path5.join(dst, name));
913
+ copyDir(resolvePart("skills", name), path6.join(dst, name));
891
914
  }
892
915
  }
893
916
  if (cc.commands.length > 0) {
894
- const dst = path5.join(root, "commands");
895
- fs4.mkdirSync(dst, { recursive: true });
917
+ const dst = path6.join(root, "commands");
918
+ fs5.mkdirSync(dst, { recursive: true });
896
919
  for (const name of cc.commands) {
897
- fs4.copyFileSync(resolvePart("commands", `${name}.md`), path5.join(dst, `${name}.md`));
920
+ fs5.copyFileSync(resolvePart("commands", `${name}.md`), path6.join(dst, `${name}.md`));
898
921
  }
899
922
  }
900
923
  if (cc.hooks.length > 0) {
901
- const dst = path5.join(root, "hooks");
902
- fs4.mkdirSync(dst, { recursive: true });
924
+ const dst = path6.join(root, "hooks");
925
+ fs5.mkdirSync(dst, { recursive: true });
903
926
  const merged = { hooks: {} };
904
927
  for (const name of cc.hooks) {
905
928
  const src = resolvePart("hooks", `${name}.json`);
906
- const parsed = JSON.parse(fs4.readFileSync(src, "utf-8"));
929
+ const parsed = JSON.parse(fs5.readFileSync(src, "utf-8"));
907
930
  for (const [event, entries] of Object.entries(parsed.hooks ?? {})) {
908
931
  if (!Array.isArray(entries)) continue;
909
932
  if (!merged.hooks[event]) merged.hooks[event] = [];
910
933
  merged.hooks[event].push(...entries);
911
934
  }
912
935
  }
913
- fs4.writeFileSync(path5.join(dst, "hooks.json"), `${JSON.stringify(merged, null, 2)}
936
+ fs5.writeFileSync(path6.join(dst, "hooks.json"), `${JSON.stringify(merged, null, 2)}
914
937
  `);
915
938
  }
916
939
  const manifest = {
@@ -920,7 +943,7 @@ var init_buildSyntheticPlugin = __esm({
920
943
  };
921
944
  if (cc.skills.length > 0) manifest.skills = ["./skills/"];
922
945
  if (cc.commands.length > 0) manifest.commands = ["./commands/"];
923
- fs4.writeFileSync(path5.join(root, ".claude-plugin", "plugin.json"), `${JSON.stringify(manifest, null, 2)}
946
+ fs5.writeFileSync(path6.join(root, ".claude-plugin", "plugin.json"), `${JSON.stringify(manifest, null, 2)}
924
947
  `);
925
948
  ctx.data.syntheticPluginPath = root;
926
949
  };
@@ -928,8 +951,8 @@ var init_buildSyntheticPlugin = __esm({
928
951
  });
929
952
 
930
953
  // src/subagents.ts
931
- import * as fs5 from "fs";
932
- import * as path6 from "path";
954
+ import * as fs6 from "fs";
955
+ import * as path7 from "path";
933
956
  async function enforceSubagentModelInheritance(input) {
934
957
  const toolInput = input.tool_input;
935
958
  if (!toolInput || typeof toolInput !== "object" || Array.isArray(toolInput)) return {};
@@ -964,12 +987,12 @@ function splitFrontmatter(raw) {
964
987
  return { fm, body: (match[2] ?? "").trim() };
965
988
  }
966
989
  function resolveAgentFile(profileDir, name) {
967
- const local = path6.join(profileDir, "agents", `${name}.md`);
968
- if (fs5.existsSync(local)) return local;
969
- const shared = path6.resolve(profileDir, "..", "..", "shared", "agents", `${name}.md`);
970
- if (fs5.existsSync(shared)) return shared;
971
- const central = path6.join(getPluginsCatalogRoot(), "agents", `${name}.md`);
972
- if (fs5.existsSync(central)) return central;
990
+ const local = path7.join(profileDir, "agents", `${name}.md`);
991
+ if (fs6.existsSync(local)) return local;
992
+ const shared = path7.resolve(profileDir, "..", "..", "shared", "agents", `${name}.md`);
993
+ if (fs6.existsSync(shared)) return shared;
994
+ const central = path7.join(getPluginsCatalogRoot(), "agents", `${name}.md`);
995
+ if (fs6.existsSync(central)) return central;
973
996
  throw new Error(
974
997
  `loadSubagents: agent '${name}' not found in ${profileDir}/agents/, hydrated shared assets, or engine catalog`
975
998
  );
@@ -980,7 +1003,7 @@ function captureSubagentTemplates(profile) {
980
1003
  const out = {};
981
1004
  for (const name of names) {
982
1005
  try {
983
- out[name] = fs5.readFileSync(resolveAgentFile(profile.dir, name), "utf-8");
1006
+ out[name] = fs6.readFileSync(resolveAgentFile(profile.dir, name), "utf-8");
984
1007
  } catch {
985
1008
  }
986
1009
  }
@@ -991,7 +1014,7 @@ function loadSubagents(profile) {
991
1014
  if (!names || names.length === 0) return void 0;
992
1015
  const agents = {};
993
1016
  for (const name of names) {
994
- const raw = profile.subagentTemplates?.[name] ?? fs5.readFileSync(resolveAgentFile(profile.dir, name), "utf-8");
1017
+ const raw = profile.subagentTemplates?.[name] ?? fs6.readFileSync(resolveAgentFile(profile.dir, name), "utf-8");
995
1018
  const { fm, body } = splitFrontmatter(raw);
996
1019
  if (!body) throw new Error(`loadSubagents: agent '${name}' has an empty prompt body`);
997
1020
  const def = {
@@ -1024,8 +1047,8 @@ __export(events_exports, {
1024
1047
  resolveRunId: () => resolveRunId
1025
1048
  });
1026
1049
  import * as crypto from "crypto";
1027
- import * as fs6 from "fs";
1028
- import * as path7 from "path";
1050
+ import * as fs7 from "fs";
1051
+ import * as path8 from "path";
1029
1052
  function resolveRunId() {
1030
1053
  if (process.env.KODY_RUN_ID) {
1031
1054
  cachedRunId = process.env.KODY_RUN_ID;
@@ -1058,16 +1081,16 @@ function emitEvent(cwd, ev) {
1058
1081
  ...ev
1059
1082
  };
1060
1083
  const file = eventsPath(cwd, runId);
1061
- fs6.mkdirSync(path7.dirname(file), { recursive: true });
1062
- fs6.appendFileSync(file, `${JSON.stringify(fullEvent)}
1084
+ fs7.mkdirSync(path8.dirname(file), { recursive: true });
1085
+ fs7.appendFileSync(file, `${JSON.stringify(fullEvent)}
1063
1086
  `);
1064
1087
  } catch {
1065
1088
  }
1066
1089
  }
1067
1090
  function readEvents(cwd, runId) {
1068
1091
  const file = eventsPath(cwd, runId);
1069
- if (!fs6.existsSync(file)) return [];
1070
- const lines = fs6.readFileSync(file, "utf-8").split("\n");
1092
+ if (!fs7.existsSync(file)) return [];
1093
+ const lines = fs7.readFileSync(file, "utf-8").split("\n");
1071
1094
  const out = [];
1072
1095
  for (const line of lines) {
1073
1096
  const trimmed = line.trim();
@@ -1081,10 +1104,10 @@ function readEvents(cwd, runId) {
1081
1104
  }
1082
1105
  function listRuns(cwd) {
1083
1106
  const runsDir = runtimeStatePath(cwd, "agent-runs");
1084
- if (!fs6.existsSync(runsDir)) return [];
1085
- return fs6.readdirSync(runsDir).filter((name) => {
1107
+ if (!fs7.existsSync(runsDir)) return [];
1108
+ return fs7.readdirSync(runsDir).filter((name) => {
1086
1109
  try {
1087
- return fs6.statSync(path7.join(runsDir, name)).isDirectory();
1110
+ return fs7.statSync(path8.join(runsDir, name)).isDirectory();
1088
1111
  } catch {
1089
1112
  return false;
1090
1113
  }
@@ -1121,7 +1144,7 @@ function buildVerifyEnv(source = process.env) {
1121
1144
  return env;
1122
1145
  }
1123
1146
  function runCommand(command, cwd) {
1124
- return new Promise((resolve21) => {
1147
+ return new Promise((resolve23) => {
1125
1148
  const start = Date.now();
1126
1149
  const child = spawn(command, {
1127
1150
  cwd,
@@ -1150,11 +1173,11 @@ function runCommand(command, cwd) {
1150
1173
  child.on("exit", (code) => {
1151
1174
  clearTimeout(timer);
1152
1175
  const tail = Buffer.concat(buffers).toString("utf-8").slice(-TAIL_CHARS);
1153
- resolve21({ exitCode: code ?? -1, durationMs: Date.now() - start, tail });
1176
+ resolve23({ exitCode: code ?? -1, durationMs: Date.now() - start, tail });
1154
1177
  });
1155
1178
  child.on("error", (err) => {
1156
1179
  clearTimeout(timer);
1157
- resolve21({ exitCode: -1, durationMs: Date.now() - start, tail: err.message });
1180
+ resolve23({ exitCode: -1, durationMs: Date.now() - start, tail: err.message });
1158
1181
  });
1159
1182
  });
1160
1183
  }
@@ -1463,7 +1486,7 @@ function cmsHeaders(opts) {
1463
1486
  }
1464
1487
  };
1465
1488
  }
1466
- async function callDashboardCms(opts, path55, init = {}) {
1489
+ async function callDashboardCms(opts, path58, init = {}) {
1467
1490
  const baseUrl = dashboardBaseUrl(opts);
1468
1491
  if (!baseUrl) {
1469
1492
  return {
@@ -1475,7 +1498,7 @@ async function callDashboardCms(opts, path55, init = {}) {
1475
1498
  const headerResult = cmsHeaders(opts);
1476
1499
  if (!headerResult.ok) return headerResult;
1477
1500
  try {
1478
- const res = await fetch(`${baseUrl}${path55}`, {
1501
+ const res = await fetch(`${baseUrl}${path58}`, {
1479
1502
  ...init,
1480
1503
  headers: {
1481
1504
  ...headerResult.headers,
@@ -1547,8 +1570,8 @@ function documentArg(value) {
1547
1570
  function normalizeCmsDocumentIdInput(input) {
1548
1571
  const trimmed = stripWrappingQuotes(input.trim());
1549
1572
  const withoutQuery = trimmed.split(/[?#]/, 1)[0] ?? trimmed;
1550
- const path55 = parseDocumentPath(withoutQuery);
1551
- return path55 ?? parseDocumentIdSegment(withoutQuery) ?? withoutQuery;
1573
+ const path58 = parseDocumentPath(withoutQuery);
1574
+ return path58 ?? parseDocumentIdSegment(withoutQuery) ?? withoutQuery;
1552
1575
  }
1553
1576
  function stripWrappingQuotes(value) {
1554
1577
  let current = value;
@@ -1559,9 +1582,9 @@ function stripWrappingQuotes(value) {
1559
1582
  }
1560
1583
  }
1561
1584
  function parseDocumentPath(value) {
1562
- const path55 = value.startsWith("http://") || value.startsWith("https://") ? urlPathname(value) : value;
1563
- if (!path55?.includes("/content/entries/")) return null;
1564
- const parts = path55.split("/").filter(Boolean).map(decodePathPart);
1585
+ const path58 = value.startsWith("http://") || value.startsWith("https://") ? urlPathname(value) : value;
1586
+ if (!path58?.includes("/content/entries/")) return null;
1587
+ const parts = path58.split("/").filter(Boolean).map(decodePathPart);
1565
1588
  const entriesIndex = parts.findIndex((part, index) => part === "content" && parts[index + 1] === "entries");
1566
1589
  const idPart = parts[entriesIndex + 3];
1567
1590
  if (!idPart || idPart === "new") return null;
@@ -2006,8 +2029,8 @@ var init_issue = __esm({
2006
2029
  });
2007
2030
 
2008
2031
  // src/capabilityFolders.ts
2009
- import * as fs7 from "fs";
2010
- import * as path8 from "path";
2032
+ import * as fs8 from "fs";
2033
+ import * as path9 from "path";
2011
2034
  function capabilityOutputConditionPaths(config) {
2012
2035
  if (config.outputSchema) {
2013
2036
  return new Set(schemaPropertyPaths(config.outputSchema, "result"));
@@ -2022,43 +2045,43 @@ function capabilityOutputConditionPaths(config) {
2022
2045
  ]);
2023
2046
  }
2024
2047
  function listCapabilityFolderSlugs(absDir) {
2025
- if (!fs7.existsSync(absDir)) return [];
2048
+ if (!fs8.existsSync(absDir)) return [];
2026
2049
  let entries;
2027
2050
  try {
2028
- entries = fs7.readdirSync(absDir, { withFileTypes: true });
2051
+ entries = fs8.readdirSync(absDir, { withFileTypes: true });
2029
2052
  } catch {
2030
2053
  return [];
2031
2054
  }
2032
- 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();
2055
+ return entries.filter((e) => e.isDirectory() && !e.name.startsWith("_") && !e.name.startsWith(".")).filter((e) => isCapabilityFolder(path9.join(absDir, e.name))).map((e) => e.name).sort();
2033
2056
  }
2034
2057
  function isCapabilityFolder(dir) {
2035
- const entries = fs7.readdirSync(dir, { withFileTypes: true });
2036
- const legacyBody = path8.join(dir, CAPABILITY_BODY_FILE);
2037
- if (fs7.existsSync(legacyBody)) {
2058
+ const entries = fs8.readdirSync(dir, { withFileTypes: true });
2059
+ const legacyBody = path9.join(dir, CAPABILITY_BODY_FILE);
2060
+ if (fs8.existsSync(legacyBody)) {
2038
2061
  return entries.every(
2039
2062
  (entry) => entry.name === CAPABILITY_BODY_FILE || entry.name === CAPABILITY_CONTRACT_FILE || entry.isDirectory() && (entry.name === "skills" || entry.name === "tools")
2040
2063
  );
2041
2064
  }
2042
- const canonicalBody = path8.join(dir, CANONICAL_CAPABILITY_BODY_FILE);
2043
- const canonicalDefinition = path8.join(dir, CANONICAL_CAPABILITY_DEFINITION_FILE);
2044
- if (!fs7.existsSync(canonicalBody) || !fs7.existsSync(canonicalDefinition)) return false;
2065
+ const canonicalBody = path9.join(dir, CANONICAL_CAPABILITY_BODY_FILE);
2066
+ const canonicalDefinition = path9.join(dir, CANONICAL_CAPABILITY_DEFINITION_FILE);
2067
+ if (!fs8.existsSync(canonicalBody) || !fs8.existsSync(canonicalDefinition)) return false;
2045
2068
  return entries.every(
2046
2069
  (entry) => entry.name === CANONICAL_CAPABILITY_BODY_FILE || entry.name === CANONICAL_CAPABILITY_DEFINITION_FILE
2047
2070
  );
2048
2071
  }
2049
2072
  function readCapabilityFolder(root, slug) {
2050
- const dir = path8.join(root, slug);
2051
- const legacyBodyPath = path8.join(dir, CAPABILITY_BODY_FILE);
2052
- const canonicalBodyPath = path8.join(dir, CANONICAL_CAPABILITY_BODY_FILE);
2053
- const bodyPath = fs7.existsSync(legacyBodyPath) ? legacyBodyPath : canonicalBodyPath;
2054
- const contractPath = path8.join(dir, CAPABILITY_CONTRACT_FILE);
2055
- if (!fs7.existsSync(bodyPath) || !fs7.statSync(bodyPath).isFile()) return null;
2073
+ const dir = path9.join(root, slug);
2074
+ const legacyBodyPath = path9.join(dir, CAPABILITY_BODY_FILE);
2075
+ const canonicalBodyPath = path9.join(dir, CANONICAL_CAPABILITY_BODY_FILE);
2076
+ const bodyPath = fs8.existsSync(legacyBodyPath) ? legacyBodyPath : canonicalBodyPath;
2077
+ const contractPath = path9.join(dir, CAPABILITY_CONTRACT_FILE);
2078
+ if (!fs8.existsSync(bodyPath) || !fs8.statSync(bodyPath).isFile()) return null;
2056
2079
  if (!isCapabilityFolder(dir)) return null;
2057
2080
  try {
2058
- const rawBody = fs7.readFileSync(bodyPath, "utf-8");
2081
+ const rawBody = fs8.readFileSync(bodyPath, "utf-8");
2059
2082
  if (bodyPath === canonicalBodyPath) {
2060
- const definitionPath = path8.join(dir, CANONICAL_CAPABILITY_DEFINITION_FILE);
2061
- const definition = JSON.parse(fs7.readFileSync(definitionPath, "utf-8"));
2083
+ const definitionPath = path9.join(dir, CANONICAL_CAPABILITY_DEFINITION_FILE);
2084
+ const definition = JSON.parse(fs8.readFileSync(definitionPath, "utf-8"));
2062
2085
  if (definition.id !== slug || typeof definition.action !== "string") return null;
2063
2086
  const { title: title2, body: body2 } = parseCapabilityBody(rawBody, slug);
2064
2087
  return {
@@ -2078,8 +2101,8 @@ function readCapabilityFolder(root, slug) {
2078
2101
  rawProfile: definition
2079
2102
  };
2080
2103
  }
2081
- const contract = fs7.existsSync(contractPath) ? parseCapabilityContract(fs7.readFileSync(contractPath, "utf-8")) : void 0;
2082
- if (contract?.execution === "script" && !isRegularFile(path8.join(dir, "tools", "run.sh"))) {
2104
+ const contract = fs8.existsSync(contractPath) ? parseCapabilityContract(fs8.readFileSync(contractPath, "utf-8")) : void 0;
2105
+ if (contract?.execution === "script" && !isRegularFile(path9.join(dir, "tools", "run.sh"))) {
2083
2106
  throw new Error('script-backed Capability requires a regular "tools/run.sh" file');
2084
2107
  }
2085
2108
  const { title, body } = parseCapabilityBody(rawBody, slug);
@@ -2192,7 +2215,7 @@ function parseCapabilityRequirements(raw) {
2192
2215
  }
2193
2216
  function isRegularFile(filePath) {
2194
2217
  try {
2195
- const stat = fs7.lstatSync(filePath);
2218
+ const stat = fs8.lstatSync(filePath);
2196
2219
  return stat.isFile() && !stat.isSymbolicLink();
2197
2220
  } catch {
2198
2221
  return false;
@@ -2201,8 +2224,8 @@ function isRegularFile(filePath) {
2201
2224
  function schemaPropertyPaths(schema, prefix) {
2202
2225
  const properties = isPlainObject(schema.properties) ? schema.properties : {};
2203
2226
  return Object.entries(properties).flatMap(([name, property]) => {
2204
- const path55 = `${prefix}.${name}`;
2205
- return isPlainObject(property) ? [path55, ...schemaPropertyPaths(property, path55)] : [path55];
2227
+ const path58 = `${prefix}.${name}`;
2228
+ return isPlainObject(property) ? [path58, ...schemaPropertyPaths(property, path58)] : [path58];
2206
2229
  });
2207
2230
  }
2208
2231
  function parseCapabilityBody(raw, slug) {
@@ -2366,51 +2389,51 @@ var init_capabilityFolders = __esm({
2366
2389
  });
2367
2390
 
2368
2391
  // src/definition-paths.ts
2369
- import * as fs8 from "fs";
2370
- import * as path9 from "path";
2392
+ import * as fs9 from "fs";
2393
+ import * as path10 from "path";
2371
2394
  function definitionsRoot(cwd = process.cwd()) {
2372
2395
  const override = process.env.KODY_DEFINITIONS_ROOT?.trim();
2373
2396
  const overrideCwd = process.env.KODY_DEFINITIONS_ROOT_CWD?.trim();
2374
- if (override && overrideCwd && path9.resolve(cwd) === path9.resolve(overrideCwd)) {
2375
- return storeCatalogRoot(path9.resolve(override));
2397
+ if (override && overrideCwd && path10.resolve(cwd) === path10.resolve(overrideCwd)) {
2398
+ return storeCatalogRoot(path10.resolve(override));
2376
2399
  }
2377
- const hydrated = path9.join(cwd, ".kody-engine", "definitions");
2378
- if (fs8.existsSync(hydrated)) return hydrated;
2379
- return override ? storeCatalogRoot(path9.resolve(override)) : hydrated;
2400
+ const hydrated = path10.join(cwd, ".kody-engine", "definitions");
2401
+ if (fs9.existsSync(hydrated)) return hydrated;
2402
+ return override ? storeCatalogRoot(path10.resolve(override)) : hydrated;
2380
2403
  }
2381
2404
  function hasExplicitDefinitionsRoot(cwd = process.cwd(), env = process.env) {
2382
2405
  const root = env.KODY_DEFINITIONS_ROOT?.trim();
2383
2406
  const rootCwd = env.KODY_DEFINITIONS_ROOT_CWD?.trim();
2384
- return Boolean(root && rootCwd && path9.resolve(cwd) === path9.resolve(rootCwd));
2407
+ return Boolean(root && rootCwd && path10.resolve(cwd) === path10.resolve(rootCwd));
2385
2408
  }
2386
2409
  function capabilitiesRoot(cwd = process.cwd()) {
2387
- return storeAssetRoot(cwd, "capabilities") ?? path9.join(definitionsRoot(cwd), "capabilities");
2410
+ return storeAssetRoot(cwd, "capabilities") ?? path10.join(definitionsRoot(cwd), "capabilities");
2388
2411
  }
2389
2412
  function implementationsRoot(cwd = process.cwd()) {
2390
- return path9.join(definitionsRoot(cwd), "implementations");
2413
+ return path10.join(definitionsRoot(cwd), "implementations");
2391
2414
  }
2392
2415
  function agentsRoot(cwd = process.cwd()) {
2393
- return storeAssetRoot(cwd, "agent") ?? path9.join(definitionsRoot(cwd), "agents");
2416
+ return storeAssetRoot(cwd, "agent") ?? path10.join(definitionsRoot(cwd), "agents");
2394
2417
  }
2395
2418
  function storeCatalogRoot(root) {
2396
2419
  const manifest = readStoreManifest(root);
2397
- const roots = ["capabilities", "workflows", "loops"].map((kind) => manifest?.assetRoots?.[kind]).filter((value) => typeof value === "string" && Boolean(value.trim())).map((value) => path9.dirname(value));
2398
- return roots.length === 3 && new Set(roots).size === 1 ? path9.join(root, roots[0]) : root;
2420
+ const roots = ["capabilities", "workflows", "loops"].map((kind) => manifest?.assetRoots?.[kind]).filter((value) => typeof value === "string" && Boolean(value.trim())).map((value) => path10.dirname(value));
2421
+ return roots.length === 3 && new Set(roots).size === 1 ? path10.join(root, roots[0]) : root;
2399
2422
  }
2400
2423
  function storeAssetRoot(cwd, kind) {
2401
2424
  const override = process.env.KODY_DEFINITIONS_ROOT?.trim();
2402
2425
  if (!override) return null;
2403
2426
  const overrideCwd = process.env.KODY_DEFINITIONS_ROOT_CWD?.trim();
2404
- if (overrideCwd && path9.resolve(cwd) !== path9.resolve(overrideCwd)) return null;
2405
- const root = path9.resolve(override);
2427
+ if (overrideCwd && path10.resolve(cwd) !== path10.resolve(overrideCwd)) return null;
2428
+ const root = path10.resolve(override);
2406
2429
  const configured = readStoreManifest(root)?.assetRoots?.[kind];
2407
- return typeof configured === "string" && configured.trim() ? path9.join(root, configured) : null;
2430
+ return typeof configured === "string" && configured.trim() ? path10.join(root, configured) : null;
2408
2431
  }
2409
2432
  function readStoreManifest(root) {
2410
- const file = path9.join(root, "kody-store.json");
2411
- if (!fs8.existsSync(file)) return null;
2433
+ const file = path10.join(root, "kody-store.json");
2434
+ if (!fs9.existsSync(file)) return null;
2412
2435
  try {
2413
- return JSON.parse(fs8.readFileSync(file, "utf8"));
2436
+ return JSON.parse(fs9.readFileSync(file, "utf8"));
2414
2437
  } catch {
2415
2438
  return null;
2416
2439
  }
@@ -2422,32 +2445,32 @@ var init_definition_paths = __esm({
2422
2445
  });
2423
2446
 
2424
2447
  // src/registry.ts
2425
- import * as fs9 from "fs";
2426
- import * as path10 from "path";
2448
+ import * as fs10 from "fs";
2449
+ import * as path11 from "path";
2427
2450
  function getImplementationsRoot() {
2428
- const here = path10.dirname(new URL(import.meta.url).pathname);
2451
+ const here = path11.dirname(new URL(import.meta.url).pathname);
2429
2452
  const candidates = [
2430
- path10.join(here, "implementations"),
2453
+ path11.join(here, "implementations"),
2431
2454
  // dev: src/
2432
- path10.join(here, "..", "implementations"),
2455
+ path11.join(here, "..", "implementations"),
2433
2456
  // built: dist/bin → dist/implementations
2434
- path10.join(here, "..", "src", "implementations")
2457
+ path11.join(here, "..", "src", "implementations")
2435
2458
  // fallback
2436
2459
  ];
2437
2460
  for (const c of candidates) {
2438
- if (fs9.existsSync(c) && fs9.statSync(c).isDirectory()) return c;
2461
+ if (fs10.existsSync(c) && fs10.statSync(c).isDirectory()) return c;
2439
2462
  }
2440
2463
  return candidates[0];
2441
2464
  }
2442
2465
  function getRuntimeServicesRoot() {
2443
- const here = path10.dirname(new URL(import.meta.url).pathname);
2466
+ const here = path11.dirname(new URL(import.meta.url).pathname);
2444
2467
  const candidates = [
2445
- path10.join(here, "runtime-services"),
2446
- path10.join(here, "..", "runtime-services"),
2447
- path10.join(here, "..", "src", "runtime-services")
2468
+ path11.join(here, "runtime-services"),
2469
+ path11.join(here, "..", "runtime-services"),
2470
+ path11.join(here, "..", "src", "runtime-services")
2448
2471
  ];
2449
2472
  for (const candidate of candidates) {
2450
- if (fs9.existsSync(candidate) && fs9.statSync(candidate).isDirectory()) return candidate;
2473
+ if (fs10.existsSync(candidate) && fs10.statSync(candidate).isDirectory()) return candidate;
2451
2474
  }
2452
2475
  return candidates[0];
2453
2476
  }
@@ -2455,17 +2478,17 @@ function getProjectCapabilitiesRoot() {
2455
2478
  return capabilitiesRoot();
2456
2479
  }
2457
2480
  function getBuiltinCapabilitiesRoot() {
2458
- const here = path10.dirname(new URL(import.meta.url).pathname);
2481
+ const here = path11.dirname(new URL(import.meta.url).pathname);
2459
2482
  const candidates = [
2460
- path10.join(here, "capabilities"),
2483
+ path11.join(here, "capabilities"),
2461
2484
  // dev: src/
2462
- path10.join(here, "..", "capabilities"),
2485
+ path11.join(here, "..", "capabilities"),
2463
2486
  // built: dist/bin → dist/capabilities
2464
- path10.join(here, "..", "src", "capabilities")
2487
+ path11.join(here, "..", "src", "capabilities")
2465
2488
  // fallback
2466
2489
  ];
2467
2490
  for (const c of candidates) {
2468
- if (fs9.existsSync(c) && fs9.statSync(c).isDirectory()) return c;
2491
+ if (fs10.existsSync(c) && fs10.statSync(c).isDirectory()) return c;
2469
2492
  }
2470
2493
  return candidates[0];
2471
2494
  }
@@ -2488,14 +2511,14 @@ function listImplementations(roots = getImplementationRoots()) {
2488
2511
  const seen = /* @__PURE__ */ new Set();
2489
2512
  const out = [];
2490
2513
  for (const root of rootList) {
2491
- if (!fs9.existsSync(root)) continue;
2514
+ if (!fs10.existsSync(root)) continue;
2492
2515
  const requireImplementationProfile = isCapabilityRoot(root);
2493
- const entries = fs9.readdirSync(root, { withFileTypes: true });
2516
+ const entries = fs10.readdirSync(root, { withFileTypes: true });
2494
2517
  for (const ent of entries) {
2495
2518
  if (!ent.isDirectory()) continue;
2496
2519
  if (seen.has(ent.name)) continue;
2497
2520
  const profilePath = implementationRuntimePath(root, ent.name);
2498
- if (fs9.existsSync(profilePath) && fs9.statSync(profilePath).isFile() && isImplementationProfile(profilePath, requireImplementationProfile)) {
2521
+ if (fs10.existsSync(profilePath) && fs10.statSync(profilePath).isFile() && isImplementationProfile(profilePath, requireImplementationProfile)) {
2499
2522
  out.push({ name: ent.name, profilePath });
2500
2523
  seen.add(ent.name);
2501
2524
  }
@@ -2515,7 +2538,7 @@ function resolveImplementationCandidates(name, roots = getRuntimeProfileRootsFor
2515
2538
  const out = [];
2516
2539
  for (const root of rootList) {
2517
2540
  const profilePath = implementationRuntimePath(root, name);
2518
- if (fs9.existsSync(profilePath) && fs9.statSync(profilePath).isFile() && isImplementationProfile(profilePath, isCapabilityRoot(root))) {
2541
+ if (fs10.existsSync(profilePath) && fs10.statSync(profilePath).isFile() && isImplementationProfile(profilePath, isCapabilityRoot(root))) {
2519
2542
  out.push(profilePath);
2520
2543
  }
2521
2544
  }
@@ -2589,7 +2612,7 @@ function implementationDeclaresInput(implementation, inputName, cwd = process.cw
2589
2612
  const profilePath = resolveImplementation(implementation, getRuntimeProfileRootsForCwd(cwd));
2590
2613
  if (!profilePath) return false;
2591
2614
  try {
2592
- const document = JSON.parse(fs9.readFileSync(profilePath, "utf-8"));
2615
+ const document = JSON.parse(fs10.readFileSync(profilePath, "utf-8"));
2593
2616
  const raw = document.config ?? document;
2594
2617
  if (!Array.isArray(raw.inputs)) return false;
2595
2618
  return raw.inputs.some((entry) => {
@@ -2605,29 +2628,29 @@ function isSafeName(name) {
2605
2628
  return /^[a-z][a-z0-9-]*$/.test(name) && !name.includes("..");
2606
2629
  }
2607
2630
  function isCapabilityRoot(root) {
2608
- const normalized = path10.normalize(root);
2609
- if (path10.basename(normalized) === "capabilities") return true;
2631
+ const normalized = path11.normalize(root);
2632
+ if (path11.basename(normalized) === "capabilities") return true;
2610
2633
  const knownRoots = [getProjectCapabilitiesRoot(), getBuiltinCapabilitiesRoot()];
2611
- return knownRoots.some((candidate) => candidate && path10.normalize(candidate) === normalized);
2634
+ return knownRoots.some((candidate) => candidate && path11.normalize(candidate) === normalized);
2612
2635
  }
2613
2636
  function implementationRuntimePath(root, name) {
2614
- const runtimePath = path10.join(root, name, "runtime.json");
2615
- if (fs9.existsSync(runtimePath)) return runtimePath;
2616
- const internalProfilePath = path10.join(root, name, "profile.json");
2617
- if (fs9.existsSync(internalProfilePath)) return internalProfilePath;
2618
- return path10.join(root, name, CAPABILITY_PROFILE_FILE);
2637
+ const runtimePath = path11.join(root, name, "runtime.json");
2638
+ if (fs10.existsSync(runtimePath)) return runtimePath;
2639
+ const internalProfilePath = path11.join(root, name, "profile.json");
2640
+ if (fs10.existsSync(internalProfilePath)) return internalProfilePath;
2641
+ return path11.join(root, name, CAPABILITY_PROFILE_FILE);
2619
2642
  }
2620
2643
  function isImplementationProfile(profilePath, requireImplementationProfile) {
2621
2644
  if (!requireImplementationProfile) return true;
2622
2645
  try {
2623
- const raw = JSON.parse(fs9.readFileSync(profilePath, "utf-8"));
2646
+ const raw = JSON.parse(fs10.readFileSync(profilePath, "utf-8"));
2624
2647
  return typeof raw.role === "string" && PUBLIC_IMPLEMENTATION_ROLES.has(raw.role);
2625
2648
  } catch {
2626
2649
  return false;
2627
2650
  }
2628
2651
  }
2629
2652
  function listFolderCapabilityActions(root, source) {
2630
- if (!fs9.existsSync(root) || !fs9.statSync(root).isDirectory()) return [];
2653
+ if (!fs10.existsSync(root) || !fs10.statSync(root).isDirectory()) return [];
2631
2654
  const out = [];
2632
2655
  for (const slug of listCapabilityFolderSlugs(root)) {
2633
2656
  if (!isSafeName(slug)) continue;
@@ -2659,7 +2682,7 @@ function hasUnresolvedExplicitImplementation(capability, implementation) {
2659
2682
  return resolveImplementation(implementation) === null;
2660
2683
  }
2661
2684
  function listBuiltinCapabilityActions(root = getBuiltinCapabilitiesRoot()) {
2662
- if (!fs9.existsSync(root) || !fs9.statSync(root).isDirectory()) return [];
2685
+ if (!fs10.existsSync(root) || !fs10.statSync(root).isDirectory()) return [];
2663
2686
  const out = [];
2664
2687
  for (const slug of listCapabilityFolderSlugs(root)) {
2665
2688
  if (!isSafeName(slug)) continue;
@@ -2684,7 +2707,7 @@ function getProfileInputs(name, roots = getImplementationRoots()) {
2684
2707
  const profilePath = resolveImplementation(name, roots);
2685
2708
  if (!profilePath) return null;
2686
2709
  try {
2687
- const document = JSON.parse(fs9.readFileSync(profilePath, "utf-8"));
2710
+ const document = JSON.parse(fs10.readFileSync(profilePath, "utf-8"));
2688
2711
  if (!document || typeof document !== "object") return [];
2689
2712
  const raw = "config" in document && document.config && typeof document.config === "object" ? document.config : document;
2690
2713
  if (!Array.isArray(raw.inputs)) return [];
@@ -3781,8 +3804,8 @@ var init_capabilityMcp = __esm({
3781
3804
 
3782
3805
  // src/repoWorkspace.ts
3783
3806
  import { spawn as spawn2, spawnSync } from "child_process";
3784
- import * as fs10 from "fs";
3785
- import * as path11 from "path";
3807
+ import * as fs11 from "fs";
3808
+ import * as path12 from "path";
3786
3809
  function buildCloneProcess(repo, token, baseEnv = process.env) {
3787
3810
  const url = `https://github.com/${repo}.git`;
3788
3811
  const env = { ...baseEnv };
@@ -3797,10 +3820,10 @@ function buildCloneProcess(repo, token, baseEnv = process.env) {
3797
3820
  async function resolveAndClone(reposRoot, repo, repoToken, cloneRepo) {
3798
3821
  const name = repo?.trim();
3799
3822
  if (!name || !REPO_RE.test(name)) return null;
3800
- const root = path11.resolve(reposRoot);
3801
- const dir = path11.resolve(root, name);
3802
- if (dir !== root && !dir.startsWith(root + path11.sep)) return null;
3803
- if (fs10.existsSync(path11.join(dir, ".git"))) return dir;
3823
+ const root = path12.resolve(reposRoot);
3824
+ const dir = path12.resolve(root, name);
3825
+ if (dir !== root && !dir.startsWith(root + path12.sep)) return null;
3826
+ if (fs11.existsSync(path12.join(dir, ".git"))) return dir;
3804
3827
  const inflight = repoClones.get(dir);
3805
3828
  if (inflight) {
3806
3829
  await inflight;
@@ -3832,9 +3855,9 @@ var init_repoWorkspace = __esm({
3832
3855
  repoClones = /* @__PURE__ */ new Map();
3833
3856
  GIT_CREDENTIAL_HELPER = `!f() { if [ "$1" = get ] && [ -n "$GITHUB_TOKEN" ]; then printf '%s\\n' username=x-access-token "password=$GITHUB_TOKEN"; fi; }; f`;
3834
3857
  defaultCloneRepo = (repo, token, dir) => {
3835
- fs10.mkdirSync(path11.dirname(dir), { recursive: true });
3858
+ fs11.mkdirSync(path12.dirname(dir), { recursive: true });
3836
3859
  const clone = buildCloneProcess(repo, token);
3837
- return new Promise((resolve21, reject) => {
3860
+ return new Promise((resolve23, reject) => {
3838
3861
  const child = spawn2("git", ["clone", "--depth=1", clone.url, dir], {
3839
3862
  env: clone.env,
3840
3863
  stdio: "inherit"
@@ -3854,7 +3877,7 @@ var init_repoWorkspace = __esm({
3854
3877
  }
3855
3878
  } catch {
3856
3879
  }
3857
- resolve21();
3880
+ resolve23();
3858
3881
  });
3859
3882
  child.on("error", reject);
3860
3883
  });
@@ -3926,8 +3949,8 @@ var init_fetchRepoMcp = __esm({
3926
3949
  });
3927
3950
 
3928
3951
  // src/agent.ts
3929
- import * as fs11 from "fs";
3930
- import * as path12 from "path";
3952
+ import * as fs12 from "fs";
3953
+ import * as path13 from "path";
3931
3954
  import { query } from "@anthropic-ai/claude-agent-sdk";
3932
3955
  function classifySubtype(subtype) {
3933
3956
  if (!subtype) return "generic_failed";
@@ -3996,8 +4019,8 @@ function buildAgentEnvironment(baseEnv, repoToken, requestEnvironment) {
3996
4019
  }
3997
4020
  async function runAgent(opts) {
3998
4021
  const ndjsonDir = opts.ndjsonDir ?? agentRunDir(opts.cwd);
3999
- fs11.mkdirSync(ndjsonDir, { recursive: true });
4000
- const ndjsonPath = path12.join(ndjsonDir, "last-run.jsonl");
4022
+ fs12.mkdirSync(ndjsonDir, { recursive: true });
4023
+ const ndjsonPath = path13.join(ndjsonDir, "last-run.jsonl");
4001
4024
  const env = buildAgentEnvironment(process.env, opts.repoToken, opts.environment);
4002
4025
  if (opts.litellmUrl) {
4003
4026
  env.ANTHROPIC_BASE_URL = opts.litellmUrl;
@@ -4015,12 +4038,13 @@ async function runAgent(opts) {
4015
4038
  let getSubmitted;
4016
4039
  const invokedSubagents = /* @__PURE__ */ new Set();
4017
4040
  const subagentInvocationHook = createSubagentInvocationHook(invokedSubagents);
4041
+ const missingParentWriteGuard = createMissingParentWriteGuard(opts.cwd);
4018
4042
  const outputContractPostWriteHook = opts.outputContract ? createOutputContractPostWriteHook(opts.outputContract) : null;
4019
4043
  const outputContractStopHook = opts.outputContract ? createOutputContractStopHook(opts.outputContract) : null;
4020
4044
  for (let attempt = 0; ; attempt++) {
4021
4045
  let ndjsonWriteFailed = false;
4022
4046
  let ndjsonWriteError;
4023
- const fullLog = fs11.createWriteStream(ndjsonPath, { flags: "w" });
4047
+ const fullLog = fs12.createWriteStream(ndjsonPath, { flags: "w" });
4024
4048
  fullLog.on("error", (err) => {
4025
4049
  ndjsonWriteFailed = true;
4026
4050
  ndjsonWriteError = err instanceof Error ? err.message : String(err);
@@ -4051,6 +4075,10 @@ async function runAgent(opts) {
4051
4075
  {
4052
4076
  matcher: "Agent",
4053
4077
  hooks: [enforceSubagentModelInheritance]
4078
+ },
4079
+ {
4080
+ matcher: "Write",
4081
+ hooks: [missingParentWriteGuard]
4054
4082
  }
4055
4083
  ],
4056
4084
  PostToolUse: [
@@ -4201,10 +4229,10 @@ async function runAgent(opts) {
4201
4229
  let timer;
4202
4230
  let next;
4203
4231
  if (turnTimeoutMs > 0) {
4204
- const timeoutPromise = new Promise((resolve21) => {
4232
+ const timeoutPromise = new Promise((resolve23) => {
4205
4233
  timer = setTimeout(() => {
4206
4234
  timedOut = true;
4207
- resolve21({ done: true, value: void 0 });
4235
+ resolve23({ done: true, value: void 0 });
4208
4236
  }, turnTimeoutMs);
4209
4237
  });
4210
4238
  next = await Promise.race([nextPromise, timeoutPromise]);
@@ -4220,7 +4248,7 @@ async function runAgent(opts) {
4220
4248
  try {
4221
4249
  await Promise.race([
4222
4250
  iterator.return(void 0).catch(() => void 0),
4223
- new Promise((resolve21) => setTimeout(resolve21, 1e4).unref())
4251
+ new Promise((resolve23) => setTimeout(resolve23, 1e4).unref())
4224
4252
  ]);
4225
4253
  } catch {
4226
4254
  }
@@ -4403,6 +4431,7 @@ var init_agent = __esm({
4403
4431
  init_claudeBinary();
4404
4432
  init_config();
4405
4433
  init_format();
4434
+ init_fileEditGuards();
4406
4435
  init_outputContractHooks();
4407
4436
  init_runtimePaths();
4408
4437
  init_subagents();
@@ -4424,8 +4453,8 @@ var init_agent = __esm({
4424
4453
  });
4425
4454
 
4426
4455
  // src/agents.ts
4427
- import * as fs12 from "fs";
4428
- import * as path13 from "path";
4456
+ import * as fs13 from "fs";
4457
+ import * as path14 from "path";
4429
4458
  function stripFrontmatter(raw) {
4430
4459
  const match = /^---\n[\s\S]*?\n---\n?([\s\S]*)$/.exec(raw);
4431
4460
  return (match ? match[1] : raw).trim();
@@ -4434,8 +4463,8 @@ function loadAgentIdentity(cwd, slug, agentsDir = agentsRoot(cwd)) {
4434
4463
  const trimmed = slug.trim();
4435
4464
  if (!trimmed) throw new Error("loadAgentIdentity: empty agent slug");
4436
4465
  const agentPath = resolveAgentFile2(cwd, trimmed, agentsDir);
4437
- if (fs12.existsSync(agentPath)) {
4438
- const body = stripFrontmatter(fs12.readFileSync(agentPath, "utf-8"));
4466
+ if (fs13.existsSync(agentPath)) {
4467
+ const body = stripFrontmatter(fs13.readFileSync(agentPath, "utf-8"));
4439
4468
  if (body) return body;
4440
4469
  const builtinForEmpty = BUILTIN_AGENTS[trimmed];
4441
4470
  if (builtinForEmpty) return builtinForEmpty;
@@ -4446,8 +4475,8 @@ function loadAgentIdentity(cwd, slug, agentsDir = agentsRoot(cwd)) {
4446
4475
  throw new Error(`loadAgentIdentity: agent '${trimmed}' declared but ${agentPath} does not exist`);
4447
4476
  }
4448
4477
  function resolveAgentFile2(cwd, slug, agentsDir = agentsRoot(cwd)) {
4449
- const localPath = path13.resolve(cwd, agentsDir, `${slug}.md`);
4450
- if (fs12.existsSync(localPath)) return localPath;
4478
+ const localPath = path14.resolve(cwd, agentsDir, `${slug}.md`);
4479
+ if (fs13.existsSync(localPath)) return localPath;
4451
4480
  return localPath;
4452
4481
  }
4453
4482
  function frameAgentIdentity(slug, agent) {
@@ -4479,14 +4508,14 @@ var init_agents = __esm({
4479
4508
  });
4480
4509
 
4481
4510
  // src/task-artifacts.ts
4482
- import fs13 from "fs";
4483
- import path14 from "path";
4511
+ import fs14 from "fs";
4512
+ import path15 from "path";
4484
4513
  import posixPath from "path/posix";
4485
4514
  function prepareTaskArtifactsDir(cwd, taskId) {
4486
4515
  const safeId = String(taskId).replace(/[^a-zA-Z0-9._-]/g, "_");
4487
4516
  const absDir = runtimeStatePath(cwd, "task-artifacts", safeId);
4488
4517
  const relDir = absDir;
4489
- fs13.mkdirSync(absDir, { recursive: true });
4518
+ fs14.mkdirSync(absDir, { recursive: true });
4490
4519
  return { taskId: safeId, absDir, relDir };
4491
4520
  }
4492
4521
  function initializeTaskArtifacts(artifacts, metadata = { taskType: "job" }) {
@@ -4516,16 +4545,16 @@ function initializeTaskArtifacts(artifacts, metadata = { taskType: "job" }) {
4516
4545
  "handoff-notes.md": "Baseline handoff: the agent did not provide additional notes.\n"
4517
4546
  };
4518
4547
  for (const file of TASK_ARTIFACT_FILES) {
4519
- const full = path14.join(artifacts.absDir, file);
4520
- if (!fs13.existsSync(full)) fs13.writeFileSync(full, defaults[file], "utf8");
4548
+ const full = path15.join(artifacts.absDir, file);
4549
+ if (!fs14.existsSync(full)) fs14.writeFileSync(full, defaults[file], "utf8");
4521
4550
  }
4522
4551
  }
4523
4552
  function verifyTaskArtifacts(absDir) {
4524
4553
  const missing = [];
4525
4554
  for (const name of TASK_ARTIFACT_FILES) {
4526
- const full = path14.join(absDir, name);
4555
+ const full = path15.join(absDir, name);
4527
4556
  try {
4528
- const stat = fs13.statSync(full);
4557
+ const stat = fs14.statSync(full);
4529
4558
  if (!stat.isFile() || stat.size === 0) missing.push(name);
4530
4559
  } catch {
4531
4560
  missing.push(name);
@@ -4541,11 +4570,11 @@ async function persistTaskArtifactsToState(config, _cwd, artifacts) {
4541
4570
  if (hasStateBackendConfig() && tenantId2) {
4542
4571
  const backend = createStateBackendFromEnv();
4543
4572
  for (const file of TASK_ARTIFACT_FILES) {
4544
- const full = path14.join(artifacts.absDir, file);
4545
- if (!fs13.existsSync(full)) continue;
4546
- const stat = fs13.statSync(full);
4573
+ const full = path15.join(artifacts.absDir, file);
4574
+ if (!fs14.existsSync(full)) continue;
4575
+ const stat = fs14.statSync(full);
4547
4576
  if (!stat.isFile() || stat.size === 0) continue;
4548
- const content = fs13.readFileSync(full, "utf-8");
4577
+ const content = fs14.readFileSync(full, "utf-8");
4549
4578
  const kind = file.replace(/\.(json|md)$/, "");
4550
4579
  let doc = content;
4551
4580
  if (file.endsWith(".json")) {
@@ -4845,15 +4874,15 @@ function validateWorkflow(value, options = {}) {
4845
4874
  }
4846
4875
  return issues;
4847
4876
  }
4848
- function validateInputBindings(value, path55, issues, declaredInputs) {
4877
+ function validateInputBindings(value, path58, issues, declaredInputs) {
4849
4878
  if (value === void 0) return;
4850
4879
  const bindings = asRecord(value);
4851
4880
  if (!bindings || Object.keys(bindings).length === 0) {
4852
- issue(issues, "invalid_inputs", path55, "workflow step inputs must contain at least one named mapping");
4881
+ issue(issues, "invalid_inputs", path58, "workflow step inputs must contain at least one named mapping");
4853
4882
  return;
4854
4883
  }
4855
4884
  for (const [name, value2] of Object.entries(bindings)) {
4856
- const bindingPath = `${path55}.${name}`;
4885
+ const bindingPath = `${path58}.${name}`;
4857
4886
  if (!/^[A-Za-z_][A-Za-z0-9_-]*$/.test(name)) {
4858
4887
  issue(issues, "invalid_input_name", bindingPath, `workflow input name ${name} is invalid`);
4859
4888
  }
@@ -4872,7 +4901,7 @@ function validateInputBindings(value, path55, issues, declaredInputs) {
4872
4901
  }
4873
4902
  }
4874
4903
  }
4875
- function validateInputBindingSources(value, path55, issues, capabilitiesByStep, capabilityOutputs) {
4904
+ function validateInputBindingSources(value, path58, issues, capabilitiesByStep, capabilityOutputs) {
4876
4905
  const bindings = asRecord(value);
4877
4906
  if (!bindings) return;
4878
4907
  for (const [name, rawBinding] of Object.entries(bindings)) {
@@ -4885,7 +4914,7 @@ function validateInputBindingSources(value, path55, issues, capabilitiesByStep,
4885
4914
  issue(
4886
4915
  issues,
4887
4916
  "missing_input_step",
4888
- `${path55}.${name}.from`,
4917
+ `${path58}.${name}.from`,
4889
4918
  `workflow input mapping references missing step ${sourceStep ?? "<none>"}`
4890
4919
  );
4891
4920
  continue;
@@ -4896,7 +4925,7 @@ function validateInputBindingSources(value, path55, issues, capabilitiesByStep,
4896
4925
  issue(
4897
4926
  issues,
4898
4927
  "undeclared_step_output",
4899
- `${path55}.${name}.from`,
4928
+ `${path58}.${name}.from`,
4900
4929
  `workflow input mapping reads ${outputPath}, but step ${sourceStep} does not declare it`
4901
4930
  );
4902
4931
  }
@@ -4905,11 +4934,11 @@ function validateInputBindingSources(value, path55, issues, capabilitiesByStep,
4905
4934
  function formatWorkflowValidationIssues(issues) {
4906
4935
  return issues.map((entry) => `${entry.path}: ${entry.message}`);
4907
4936
  }
4908
- function validateDataMatch(value, path55, issues, capabilityOutputs) {
4937
+ function validateDataMatch(value, path58, issues, capabilityOutputs) {
4909
4938
  if (value === void 0) return;
4910
4939
  const match = asRecord(value);
4911
4940
  if (!match || Object.keys(match).length === 0) {
4912
- issue(issues, "invalid_condition", path55, "workflow condition must contain at least one match");
4941
+ issue(issues, "invalid_condition", path58, "workflow condition must contain at least one match");
4913
4942
  return;
4914
4943
  }
4915
4944
  for (const [field, expected] of Object.entries(match)) {
@@ -4917,7 +4946,7 @@ function validateDataMatch(value, path55, issues, capabilityOutputs) {
4917
4946
  issue(
4918
4947
  issues,
4919
4948
  "invalid_data_path",
4920
- `${path55}.${field}`,
4949
+ `${path58}.${field}`,
4921
4950
  `workflow condition must read from facts, evidence, artifacts, result, workflow, or lastOutcome`
4922
4951
  );
4923
4952
  }
@@ -4925,12 +4954,12 @@ function validateDataMatch(value, path55, issues, capabilityOutputs) {
4925
4954
  issue(
4926
4955
  issues,
4927
4956
  "undeclared_result_path",
4928
- `${path55}.${field}`,
4957
+ `${path58}.${field}`,
4929
4958
  `workflow condition reads ${field}, but the source capability does not declare it`
4930
4959
  );
4931
4960
  }
4932
4961
  if (!isComparable(expected)) {
4933
- issue(issues, "invalid_condition_value", `${path55}.${field}`, "workflow condition value must be a JSON scalar");
4962
+ issue(issues, "invalid_condition_value", `${path58}.${field}`, "workflow condition value must be a JSON scalar");
4934
4963
  }
4935
4964
  }
4936
4965
  }
@@ -4954,8 +4983,8 @@ function isJsonValue(value) {
4954
4983
  if (!value || typeof value !== "object") return false;
4955
4984
  return Object.values(value).every(isJsonValue);
4956
4985
  }
4957
- function issue(issues, code, path55, message) {
4958
- issues.push({ code, path: path55, message });
4986
+ function issue(issues, code, path58, message) {
4987
+ issues.push({ code, path: path58, message });
4959
4988
  }
4960
4989
  var SAFE_NAME, SAFE_STEP_ID, SAFE_DATA_PATH, SAFE_INPUT_SOURCE, SUPPORTED_STEP_FIELDS, SUPPORTED_TRANSITION_FIELDS;
4961
4990
  var init_workflowValidation = __esm({
@@ -4987,8 +5016,8 @@ var init_workflowValidation = __esm({
4987
5016
  });
4988
5017
 
4989
5018
  // src/workflowDefinitions.ts
4990
- import * as fs19 from "fs";
4991
- import * as path20 from "path";
5019
+ import * as fs20 from "fs";
5020
+ import * as path21 from "path";
4992
5021
  function isWorkflowDefinitionId(value) {
4993
5022
  return WORKFLOW_ID_PATTERN.test(value);
4994
5023
  }
@@ -5033,12 +5062,12 @@ function readWorkflowDefinition(_config, cwd, id) {
5033
5062
  const root = cwd ?? process.cwd();
5034
5063
  const relativePath = workflowDefinitionPath(id);
5035
5064
  const candidates = [
5036
- path20.join(root, ".kody-engine", "runtime", relativePath),
5037
- path20.join(definitionsRoot(root), relativePath)
5065
+ path21.join(root, ".kody-engine", "runtime", relativePath),
5066
+ path21.join(definitionsRoot(root), relativePath)
5038
5067
  ];
5039
5068
  for (const filePath of candidates) {
5040
- if (!fs19.existsSync(filePath)) continue;
5041
- const workflow = parseWorkflowDefinition(fs19.readFileSync(filePath, "utf8"));
5069
+ if (!fs20.existsSync(filePath)) continue;
5070
+ const workflow = parseWorkflowDefinition(fs20.readFileSync(filePath, "utf8"));
5042
5071
  if (workflow) return workflow;
5043
5072
  }
5044
5073
  return null;
@@ -5046,7 +5075,7 @@ function readWorkflowDefinition(_config, cwd, id) {
5046
5075
  function workflowDefinitionToCapabilityFolder(id, workflow, source = workflowDefinitionPath(id)) {
5047
5076
  return {
5048
5077
  slug: id,
5049
- dir: path20.dirname(source),
5078
+ dir: path21.dirname(source),
5050
5079
  profilePath: source,
5051
5080
  bodyPath: source,
5052
5081
  title: workflow.name,
@@ -5102,7 +5131,7 @@ var init_workflowDefinitions = __esm({
5102
5131
 
5103
5132
  // src/gha.ts
5104
5133
  import { execFileSync as execFileSync2 } from "child_process";
5105
- import * as fs22 from "fs";
5134
+ import * as fs23 from "fs";
5106
5135
  function getRunUrl() {
5107
5136
  const server = process.env.GITHUB_SERVER_URL;
5108
5137
  const repo = process.env.GITHUB_REPOSITORY;
@@ -5113,10 +5142,10 @@ function getRunUrl() {
5113
5142
  function reactToTriggerComment(cwd) {
5114
5143
  if (process.env.GITHUB_EVENT_NAME !== "issue_comment") return;
5115
5144
  const eventPath = process.env.GITHUB_EVENT_PATH;
5116
- if (!eventPath || !fs22.existsSync(eventPath)) return;
5145
+ if (!eventPath || !fs23.existsSync(eventPath)) return;
5117
5146
  let event = null;
5118
5147
  try {
5119
- event = JSON.parse(fs22.readFileSync(eventPath, "utf-8"));
5148
+ event = JSON.parse(fs23.readFileSync(eventPath, "utf-8"));
5120
5149
  } catch {
5121
5150
  return;
5122
5151
  }
@@ -5646,15 +5675,15 @@ var init_lifecycles = __esm({
5646
5675
 
5647
5676
  // src/profile.ts
5648
5677
  import { createHash as createHash3 } from "crypto";
5649
- import * as fs23 from "fs";
5650
- import * as path22 from "path";
5678
+ import * as fs24 from "fs";
5679
+ import * as path23 from "path";
5651
5680
  function loadProfile(profilePath) {
5652
- if (!fs23.existsSync(profilePath)) {
5681
+ if (!fs24.existsSync(profilePath)) {
5653
5682
  throw new ProfileError(profilePath, "file not found");
5654
5683
  }
5655
5684
  let raw;
5656
5685
  try {
5657
- raw = JSON.parse(fs23.readFileSync(profilePath, "utf-8"));
5686
+ raw = JSON.parse(fs24.readFileSync(profilePath, "utf-8"));
5658
5687
  } catch (err) {
5659
5688
  throw new ProfileError(profilePath, `invalid JSON: ${err instanceof Error ? err.message : String(err)}`);
5660
5689
  }
@@ -5666,7 +5695,7 @@ function loadProfile(profilePath) {
5666
5695
  const unknownKeys = Object.keys(r).filter((k) => !KNOWN_PROFILE_KEYS.has(k));
5667
5696
  if (unknownKeys.length > 0) {
5668
5697
  process.stderr.write(
5669
- `[kody profile] ${path22.basename(path22.dirname(profilePath))}: unknown top-level keys ignored: ${unknownKeys.join(", ")}
5698
+ `[kody profile] ${path23.basename(path23.dirname(profilePath))}: unknown top-level keys ignored: ${unknownKeys.join(", ")}
5670
5699
  `
5671
5700
  );
5672
5701
  }
@@ -5676,7 +5705,7 @@ function loadProfile(profilePath) {
5676
5705
  if (!refPath) {
5677
5706
  throw new ProfileError(profilePath, `capability references unknown implementation '${execRef}'`);
5678
5707
  }
5679
- if (path22.resolve(refPath) === path22.resolve(profilePath)) {
5708
+ if (path23.resolve(refPath) === path23.resolve(profilePath)) {
5680
5709
  } else {
5681
5710
  const base = loadProfile(refPath);
5682
5711
  return {
@@ -5774,8 +5803,8 @@ function loadProfile(profilePath) {
5774
5803
  // Phase 5 in-process handoff opt-in. Default false; containers
5775
5804
  // flip to true after end-to-end verification.
5776
5805
  preloadContext: r.preloadContext === true,
5777
- dir: path22.dirname(profilePath),
5778
- promptTemplates: readPromptTemplates(path22.dirname(profilePath))
5806
+ dir: path23.dirname(profilePath),
5807
+ promptTemplates: readPromptTemplates(path23.dirname(profilePath))
5779
5808
  };
5780
5809
  if (lifecycle) {
5781
5810
  applyLifecycle(profile, profilePath);
@@ -5810,19 +5839,19 @@ function loadProfile(profilePath) {
5810
5839
  return profile;
5811
5840
  }
5812
5841
  function compileRuntimeDocument(runtimePath, document) {
5813
- if (path22.basename(runtimePath) !== "runtime.json") return document;
5842
+ if (path23.basename(runtimePath) !== "runtime.json") return document;
5814
5843
  if (document.adapter !== "kody-engine-profile") {
5815
5844
  throw new ProfileError(runtimePath, "unsupported runtime adapter document");
5816
5845
  }
5817
- const implementationDir = path22.dirname(runtimePath);
5818
- const implementation = readJsonObject(path22.join(implementationDir, "definition.json"), "Implementation definition");
5819
- const definitionsRoot2 = path22.dirname(path22.dirname(implementationDir));
5846
+ const implementationDir = path23.dirname(runtimePath);
5847
+ const implementation = readJsonObject(path23.join(implementationDir, "definition.json"), "Implementation definition");
5848
+ const definitionsRoot2 = path23.dirname(path23.dirname(implementationDir));
5820
5849
  const capabilityId = implementation.capabilityRef && typeof implementation.capabilityRef === "object" && !Array.isArray(implementation.capabilityRef) ? implementation.capabilityRef.id : void 0;
5821
5850
  if (typeof capabilityId !== "string" || !capabilityId) {
5822
5851
  throw new ProfileError(runtimePath, "Implementation capabilityRef is invalid");
5823
5852
  }
5824
5853
  const capability = readJsonObject(
5825
- path22.join(definitionsRoot2, "capabilities", capabilityId, "definition.json"),
5854
+ path23.join(definitionsRoot2, "capabilities", capabilityId, "definition.json"),
5826
5855
  "Capability definition"
5827
5856
  );
5828
5857
  const {
@@ -5861,7 +5890,7 @@ function canonical(value) {
5861
5890
  }
5862
5891
  function readJsonObject(filePath, label) {
5863
5892
  try {
5864
- const value = JSON.parse(fs23.readFileSync(filePath, "utf-8"));
5893
+ const value = JSON.parse(fs24.readFileSync(filePath, "utf-8"));
5865
5894
  if (!value || typeof value !== "object" || Array.isArray(value)) {
5866
5895
  throw new Error("must be an object");
5867
5896
  }
@@ -5879,17 +5908,17 @@ function readPromptTemplates(dir) {
5879
5908
  const out = {};
5880
5909
  const read = (p) => {
5881
5910
  try {
5882
- out[p] = fs23.readFileSync(p, "utf-8");
5911
+ out[p] = fs24.readFileSync(p, "utf-8");
5883
5912
  } catch {
5884
5913
  }
5885
5914
  };
5886
- read(path22.join(dir, "prompt.md"));
5887
- read(path22.join(dir, "capability.md"));
5888
- read(path22.join(dir, "capability.md"));
5915
+ read(path23.join(dir, "prompt.md"));
5916
+ read(path23.join(dir, "capability.md"));
5917
+ read(path23.join(dir, "capability.md"));
5889
5918
  try {
5890
- const promptsDir = path22.join(dir, "prompts");
5891
- for (const ent of fs23.readdirSync(promptsDir)) {
5892
- if (ent.endsWith(".md")) read(path22.join(promptsDir, ent));
5919
+ const promptsDir = path23.join(dir, "prompts");
5920
+ for (const ent of fs24.readdirSync(promptsDir)) {
5921
+ if (ent.endsWith(".md")) read(path23.join(promptsDir, ent));
5893
5922
  }
5894
5923
  } catch {
5895
5924
  }
@@ -6666,16 +6695,16 @@ var init_state = __esm({
6666
6695
  });
6667
6696
 
6668
6697
  // src/prompt.ts
6669
- import * as fs24 from "fs";
6670
- import * as path23 from "path";
6698
+ import * as fs25 from "fs";
6699
+ import * as path24 from "path";
6671
6700
  function loadProjectConventions(projectDir) {
6672
6701
  const out = [];
6673
6702
  for (const rel of CONVENTION_FILES) {
6674
- const abs = path23.join(projectDir, rel);
6675
- if (!fs24.existsSync(abs)) continue;
6703
+ const abs = path24.join(projectDir, rel);
6704
+ if (!fs25.existsSync(abs)) continue;
6676
6705
  let content;
6677
6706
  try {
6678
- content = fs24.readFileSync(abs, "utf-8");
6707
+ content = fs25.readFileSync(abs, "utf-8");
6679
6708
  } catch {
6680
6709
  continue;
6681
6710
  }
@@ -6910,8 +6939,8 @@ var loadMemoryContext_exports = {};
6910
6939
  __export(loadMemoryContext_exports, {
6911
6940
  loadMemoryContext: () => loadMemoryContext
6912
6941
  });
6913
- import * as fs25 from "fs";
6914
- import * as path24 from "path";
6942
+ import * as fs26 from "fs";
6943
+ import * as path25 from "path";
6915
6944
  function formatBlockFromBackend(docs) {
6916
6945
  const pages = docs.flatMap((record2) => {
6917
6946
  if (!record2.doc || typeof record2.doc !== "object") return [];
@@ -6934,21 +6963,21 @@ function collectPages(memoryAbs) {
6934
6963
  walkMd(memoryAbs, (file) => {
6935
6964
  let stat;
6936
6965
  try {
6937
- stat = fs25.statSync(file);
6966
+ stat = fs26.statSync(file);
6938
6967
  } catch {
6939
6968
  return;
6940
6969
  }
6941
6970
  let raw;
6942
6971
  try {
6943
- raw = fs25.readFileSync(file, "utf-8");
6972
+ raw = fs26.readFileSync(file, "utf-8");
6944
6973
  } catch {
6945
6974
  return;
6946
6975
  }
6947
6976
  const fm = raw.match(/^---\s*\n([\s\S]*?)\n---/);
6948
- const title = fm?.[1]?.match(/^title:\s*(.+)$/m)?.[1]?.trim() ?? path24.basename(file, ".md");
6977
+ const title = fm?.[1]?.match(/^title:\s*(.+)$/m)?.[1]?.trim() ?? path25.basename(file, ".md");
6949
6978
  const updated = fm?.[1]?.match(/^updated:\s*([0-9T:.+\-Z]+)/m)?.[1]?.trim() ?? "";
6950
6979
  out.push({
6951
- relPath: path24.relative(memoryAbs, file),
6980
+ relPath: path25.relative(memoryAbs, file),
6952
6981
  title,
6953
6982
  updated,
6954
6983
  content: raw.length > PER_PAGE_MAX_BYTES ? raw.slice(0, PER_PAGE_MAX_BYTES) + TRUNCATED_SUFFIX2 : raw,
@@ -7016,16 +7045,16 @@ function walkMd(root, visit) {
7016
7045
  const dir = stack.pop();
7017
7046
  let names;
7018
7047
  try {
7019
- names = fs25.readdirSync(dir);
7048
+ names = fs26.readdirSync(dir);
7020
7049
  } catch {
7021
7050
  continue;
7022
7051
  }
7023
7052
  for (const name of names) {
7024
7053
  if (name.startsWith(".")) continue;
7025
- const full = path24.join(dir, name);
7054
+ const full = path25.join(dir, name);
7026
7055
  let stat;
7027
7056
  try {
7028
- stat = fs25.statSync(full);
7057
+ stat = fs26.statSync(full);
7029
7058
  } catch {
7030
7059
  continue;
7031
7060
  }
@@ -7060,8 +7089,8 @@ var init_loadMemoryContext = __esm({
7060
7089
  }
7061
7090
  return;
7062
7091
  }
7063
- const memoryAbs = path24.join(ctx.cwd, MEMORY_DIR_RELATIVE);
7064
- if (!fs25.existsSync(memoryAbs)) {
7092
+ const memoryAbs = path25.join(ctx.cwd, MEMORY_DIR_RELATIVE);
7093
+ if (!fs26.existsSync(memoryAbs)) {
7065
7094
  ctx.data.memoryContext = "";
7066
7095
  return;
7067
7096
  }
@@ -7105,11 +7134,11 @@ var init_loadCoverageRules = __esm({
7105
7134
 
7106
7135
  // src/container.ts
7107
7136
  import { execFileSync as execFileSync3 } from "child_process";
7108
- import * as fs26 from "fs";
7137
+ import * as fs27 from "fs";
7109
7138
  function getProfileInputsForChild(profileName, _cwd) {
7110
7139
  try {
7111
7140
  const profilePath = resolveProfilePath(profileName);
7112
- if (!fs26.existsSync(profilePath)) return null;
7141
+ if (!fs27.existsSync(profilePath)) return null;
7113
7142
  return loadProfile(profilePath).inputs;
7114
7143
  } catch {
7115
7144
  return null;
@@ -7573,10 +7602,10 @@ var init_lifecycleLabels = __esm({
7573
7602
 
7574
7603
  // src/litellm.ts
7575
7604
  import { execFileSync as execFileSync4, spawn as spawn4 } from "child_process";
7576
- import * as fs27 from "fs";
7605
+ import * as fs28 from "fs";
7577
7606
  import * as net from "net";
7578
7607
  import * as os4 from "os";
7579
- import * as path25 from "path";
7608
+ import * as path26 from "path";
7580
7609
  async function checkLitellmHealth(url) {
7581
7610
  try {
7582
7611
  const response = await fetch(`${url}/health`, { signal: AbortSignal.timeout(3e3) });
@@ -7646,7 +7675,7 @@ function locateLitellmScript() {
7646
7675
  }
7647
7676
  function resolveLitellmCommand() {
7648
7677
  const imageScript = "/opt/venv/bin/litellm";
7649
- if (fs27.existsSync(imageScript)) return imageScript;
7678
+ if (fs28.existsSync(imageScript)) return imageScript;
7650
7679
  try {
7651
7680
  execFileSync4("which", ["litellm"], { timeout: 3e3, stdio: "pipe" });
7652
7681
  return "litellm";
@@ -7689,13 +7718,13 @@ async function startLitellmIfNeeded(model, projectDir, url = LITELLM_DEFAULT_URL
7689
7718
  const spawnProxy = () => {
7690
7719
  const portMatch = activeUrl.match(/:(\d+)/);
7691
7720
  const port = portMatch ? portMatch[1] : "4000";
7692
- const configPath = path25.join(os4.tmpdir(), `kody-local-litellm-${Date.now()}.yaml`);
7693
- fs27.writeFileSync(configPath, generateLitellmConfigYaml(model));
7721
+ const configPath = path26.join(os4.tmpdir(), `kody-local-litellm-${Date.now()}.yaml`);
7722
+ fs28.writeFileSync(configPath, generateLitellmConfigYaml(model));
7694
7723
  const args = ["--config", configPath, "--port", port];
7695
- const nextLogPath = path25.join(os4.tmpdir(), `kody-local-litellm-${Date.now()}.log`);
7696
- const outFd = fs27.openSync(nextLogPath, "w");
7724
+ const nextLogPath = path26.join(os4.tmpdir(), `kody-local-litellm-${Date.now()}.log`);
7725
+ const outFd = fs28.openSync(nextLogPath, "w");
7697
7726
  child = spawn4(cmd, args, { stdio: ["ignore", outFd, outFd], detached: true, env: childEnv });
7698
- fs27.closeSync(outFd);
7727
+ fs28.closeSync(outFd);
7699
7728
  logPath = nextLogPath;
7700
7729
  };
7701
7730
  const waitForHealth = async () => {
@@ -7709,7 +7738,7 @@ async function startLitellmIfNeeded(model, projectDir, url = LITELLM_DEFAULT_URL
7709
7738
  const readLogTail = () => {
7710
7739
  if (!logPath) return "";
7711
7740
  try {
7712
- return fs27.readFileSync(logPath, "utf-8").slice(-2e3);
7741
+ return fs28.readFileSync(logPath, "utf-8").slice(-2e3);
7713
7742
  } catch {
7714
7743
  return "";
7715
7744
  }
@@ -7782,20 +7811,20 @@ async function nextAvailableLitellmUrl(url) {
7782
7811
  throw new Error(`no free LiteLLM port found after ${startPort}`);
7783
7812
  }
7784
7813
  function canListen(port, host) {
7785
- return new Promise((resolve21) => {
7814
+ return new Promise((resolve23) => {
7786
7815
  const server = net.createServer();
7787
- server.once("error", () => resolve21(false));
7816
+ server.once("error", () => resolve23(false));
7788
7817
  server.once("listening", () => {
7789
- server.close(() => resolve21(true));
7818
+ server.close(() => resolve23(true));
7790
7819
  });
7791
7820
  server.listen(port, host);
7792
7821
  });
7793
7822
  }
7794
7823
  function readDotenvApiKeys(projectDir) {
7795
- const dotenvPath = path25.join(projectDir, ".env");
7796
- if (!fs27.existsSync(dotenvPath)) return {};
7824
+ const dotenvPath = path26.join(projectDir, ".env");
7825
+ if (!fs28.existsSync(dotenvPath)) return {};
7797
7826
  const result = {};
7798
- for (const rawLine of fs27.readFileSync(dotenvPath, "utf-8").split("\n")) {
7827
+ for (const rawLine of fs28.readFileSync(dotenvPath, "utf-8").split("\n")) {
7799
7828
  const line = rawLine.trim();
7800
7829
  if (!line || line.startsWith("#")) continue;
7801
7830
  const match = line.match(/^([A-Z_][A-Z0-9_]*_API_KEY)=(.*)$/);
@@ -8454,8 +8483,8 @@ var init_pushWithRetry = __esm({
8454
8483
 
8455
8484
  // src/commit.ts
8456
8485
  import { execFileSync as execFileSync6 } from "child_process";
8457
- import * as fs28 from "fs";
8458
- import * as path26 from "path";
8486
+ import * as fs29 from "fs";
8487
+ import * as path27 from "path";
8459
8488
  function isGitHubYamlPath(filePath) {
8460
8489
  const normalized = filePath.replace(/^\.\/+/, "");
8461
8490
  return normalized.startsWith(".github/") && /\.ya?ml$/i.test(normalized);
@@ -8497,18 +8526,18 @@ function ensureGitIdentity(cwd) {
8497
8526
  }
8498
8527
  function abortUnfinishedGitOps(cwd) {
8499
8528
  const aborted = [];
8500
- const gitDir = path26.join(cwd ?? process.cwd(), ".git");
8501
- if (!fs28.existsSync(gitDir)) return aborted;
8502
- if (fs28.existsSync(path26.join(gitDir, "MERGE_HEAD"))) {
8529
+ const gitDir = path27.join(cwd ?? process.cwd(), ".git");
8530
+ if (!fs29.existsSync(gitDir)) return aborted;
8531
+ if (fs29.existsSync(path27.join(gitDir, "MERGE_HEAD"))) {
8503
8532
  if (tryGit(["merge", "--abort"], cwd)) aborted.push("merge");
8504
8533
  }
8505
- if (fs28.existsSync(path26.join(gitDir, "CHERRY_PICK_HEAD"))) {
8534
+ if (fs29.existsSync(path27.join(gitDir, "CHERRY_PICK_HEAD"))) {
8506
8535
  if (tryGit(["cherry-pick", "--abort"], cwd)) aborted.push("cherry-pick");
8507
8536
  }
8508
- if (fs28.existsSync(path26.join(gitDir, "REVERT_HEAD"))) {
8537
+ if (fs29.existsSync(path27.join(gitDir, "REVERT_HEAD"))) {
8509
8538
  if (tryGit(["revert", "--abort"], cwd)) aborted.push("revert");
8510
8539
  }
8511
- if (fs28.existsSync(path26.join(gitDir, "rebase-merge")) || fs28.existsSync(path26.join(gitDir, "rebase-apply"))) {
8540
+ if (fs29.existsSync(path27.join(gitDir, "rebase-merge")) || fs29.existsSync(path27.join(gitDir, "rebase-apply"))) {
8512
8541
  if (tryGit(["rebase", "--abort"], cwd)) aborted.push("rebase");
8513
8542
  }
8514
8543
  try {
@@ -8565,7 +8594,7 @@ function normalizeCommitMessage(raw) {
8565
8594
  function commitAndPush(branch, agentMessage, cwd) {
8566
8595
  const allChanged = listChangedFiles(cwd);
8567
8596
  const allowedFiles = allChanged.filter((f) => !isForbiddenPath(f));
8568
- const mergeHeadExists = fs28.existsSync(path26.join(cwd ?? process.cwd(), ".git", "MERGE_HEAD"));
8597
+ const mergeHeadExists = fs29.existsSync(path27.join(cwd ?? process.cwd(), ".git", "MERGE_HEAD"));
8569
8598
  if (allowedFiles.length === 0 && !mergeHeadExists) {
8570
8599
  return { committed: false, pushed: false, sha: "", message: "" };
8571
8600
  }
@@ -9205,13 +9234,13 @@ var init_state2 = __esm({
9205
9234
  });
9206
9235
 
9207
9236
  // src/goal/runLog.ts
9208
- import * as fs29 from "fs";
9237
+ import * as fs30 from "fs";
9209
9238
  function stageGoalRunLogEvent(data, goalId, event, at = nowIso()) {
9210
9239
  const logs = goalRunLogs(data);
9211
9240
  const existing = logs[goalId];
9212
- const path55 = existing?.path ?? goalRunLogPath(goalId, data);
9241
+ const path58 = existing?.path ?? goalRunLogPath(goalId, data);
9213
9242
  logs[goalId] = {
9214
- path: path55,
9243
+ path: path58,
9215
9244
  events: [...existing?.events ?? [], buildGoalRunLogEvent(data, goalId, event, at)]
9216
9245
  };
9217
9246
  }
@@ -9547,8 +9576,8 @@ function readGithubEvent() {
9547
9576
  const eventPath = process.env.GITHUB_EVENT_PATH;
9548
9577
  if (!eventPath) return null;
9549
9578
  try {
9550
- if (!fs29.existsSync(eventPath)) return null;
9551
- const parsed = JSON.parse(fs29.readFileSync(eventPath, "utf-8"));
9579
+ if (!fs30.existsSync(eventPath)) return null;
9580
+ const parsed = JSON.parse(fs30.readFileSync(eventPath, "utf-8"));
9552
9581
  return recordValue3(parsed);
9553
9582
  } catch {
9554
9583
  return null;
@@ -9658,8 +9687,8 @@ var init_stateStore = __esm({
9658
9687
  });
9659
9688
 
9660
9689
  // src/goal/targetLoopResolution.ts
9661
- import * as fs30 from "fs";
9662
- import * as path27 from "path";
9690
+ import * as fs31 from "fs";
9691
+ import * as path28 from "path";
9663
9692
  async function resolveActiveGoalLoopTarget(config, cwd, loopGoalId, loopGoal) {
9664
9693
  const targetId = loopGoal.loopTarget?.id.trim() ?? "";
9665
9694
  assertSafeGoalId(targetId, "loop target");
@@ -9737,11 +9766,11 @@ function goalInstanceTime(state) {
9737
9766
  return Number.isNaN(parsed) ? 0 : parsed;
9738
9767
  }
9739
9768
  function loadGoalTemplate(cwd, targetId) {
9740
- return readJsonObject2(path27.join(cwd, ".kody-engine", "definitions", "goals", targetId, "state.json"));
9769
+ return readJsonObject2(path28.join(cwd, ".kody-engine", "definitions", "goals", targetId, "state.json"));
9741
9770
  }
9742
9771
  function readJsonObject2(filePath) {
9743
- if (!fs30.existsSync(filePath)) return null;
9744
- const parsed = JSON.parse(fs30.readFileSync(filePath, "utf8"));
9772
+ if (!fs31.existsSync(filePath)) return null;
9773
+ const parsed = JSON.parse(fs31.readFileSync(filePath, "utf8"));
9745
9774
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
9746
9775
  throw new Error(`goal template ${filePath} must be a JSON object`);
9747
9776
  }
@@ -10088,15 +10117,15 @@ var init_backendStateBackend = __esm({
10088
10117
  this.jobsDir = opts.jobsDir.replace(/\/+$/, "");
10089
10118
  }
10090
10119
  async load(slug) {
10091
- const path55 = stateFilePath(this.jobsDir, slug);
10120
+ const path58 = stateFilePath(this.jobsDir, slug);
10092
10121
  const loaded = await createStateBackendFromEnv().get(this.tenantId, `capabilities/${slug}`, "job-state");
10093
10122
  if (!loaded) {
10094
- return { path: path55, handle: null, state: initialStateEnvelope("seed"), created: true };
10123
+ return { path: path58, handle: null, state: initialStateEnvelope("seed"), created: true };
10095
10124
  }
10096
10125
  if (!isStateEnvelope(loaded.doc)) {
10097
10126
  throw new Error(`BackendStateBackend: capabilities/${slug} is not a StateEnvelope`);
10098
10127
  }
10099
- return { path: path55, handle: loaded.updatedAt, state: loaded.doc, created: false };
10128
+ return { path: path58, handle: loaded.updatedAt, state: loaded.doc, created: false };
10100
10129
  }
10101
10130
  async save(loaded, next) {
10102
10131
  if (!loaded.created && isStateUnchanged(loaded.state, next)) return false;
@@ -10116,8 +10145,8 @@ var init_backendStateBackend = __esm({
10116
10145
  });
10117
10146
 
10118
10147
  // src/scripts/jobState/localFileBackend.ts
10119
- import * as fs31 from "fs";
10120
- import * as path28 from "path";
10148
+ import * as fs32 from "fs";
10149
+ import * as path29 from "path";
10121
10150
  function sanitizeKey(s) {
10122
10151
  return s.replace(/[^A-Za-z0-9._-]/g, "-");
10123
10152
  }
@@ -10173,7 +10202,7 @@ var init_localFileBackend = __esm({
10173
10202
  if (!opts.owner || !opts.repo) throw new Error("LocalFileBackend: owner and repo are required");
10174
10203
  this.cwd = opts.cwd;
10175
10204
  this.jobsDir = opts.jobsDir;
10176
- this.absDir = path28.resolve(opts.cwd, opts.jobsDir);
10205
+ this.absDir = path29.resolve(opts.cwd, opts.jobsDir);
10177
10206
  this.owner = opts.owner;
10178
10207
  this.repo = opts.repo;
10179
10208
  this.cache = opts.cache ?? defaultCacheAdapter();
@@ -10188,7 +10217,7 @@ var init_localFileBackend = __esm({
10188
10217
  `);
10189
10218
  return;
10190
10219
  }
10191
- fs31.mkdirSync(this.absDir, { recursive: true });
10220
+ fs32.mkdirSync(this.absDir, { recursive: true });
10192
10221
  const prefix = this.cacheKeyPrefix();
10193
10222
  const probeKey = `${prefix}probe-${Date.now()}`;
10194
10223
  try {
@@ -10217,7 +10246,7 @@ var init_localFileBackend = __esm({
10217
10246
  `);
10218
10247
  return;
10219
10248
  }
10220
- if (!fs31.existsSync(this.absDir)) {
10249
+ if (!fs32.existsSync(this.absDir)) {
10221
10250
  return;
10222
10251
  }
10223
10252
  const key = `${this.cacheKeyPrefix()}${process.env.GITHUB_RUN_ID ?? "norunid"}-${Date.now()}`;
@@ -10233,11 +10262,11 @@ var init_localFileBackend = __esm({
10233
10262
  }
10234
10263
  load(slug) {
10235
10264
  const relPath = stateFilePath(this.jobsDir, slug);
10236
- const absPath = path28.resolve(this.cwd, relPath);
10237
- if (!fs31.existsSync(absPath)) {
10265
+ const absPath = path29.resolve(this.cwd, relPath);
10266
+ if (!fs32.existsSync(absPath)) {
10238
10267
  return { path: relPath, handle: null, state: initialStateEnvelope("seed"), created: true };
10239
10268
  }
10240
- const raw = fs31.readFileSync(absPath, "utf-8");
10269
+ const raw = fs32.readFileSync(absPath, "utf-8");
10241
10270
  let parsed;
10242
10271
  try {
10243
10272
  parsed = JSON.parse(raw);
@@ -10254,13 +10283,13 @@ var init_localFileBackend = __esm({
10254
10283
  if (!loaded.created && isStateUnchanged(loaded.state, next)) {
10255
10284
  return false;
10256
10285
  }
10257
- const absPath = path28.resolve(this.cwd, loaded.path);
10258
- fs31.mkdirSync(path28.dirname(absPath), { recursive: true });
10286
+ const absPath = path29.resolve(this.cwd, loaded.path);
10287
+ fs32.mkdirSync(path29.dirname(absPath), { recursive: true });
10259
10288
  const body = `${JSON.stringify(next, null, 2)}
10260
10289
  `;
10261
10290
  const tmpPath = `${absPath}.${process.pid}.tmp`;
10262
- fs31.writeFileSync(tmpPath, body, "utf-8");
10263
- fs31.renameSync(tmpPath, absPath);
10291
+ fs32.writeFileSync(tmpPath, body, "utf-8");
10292
+ fs32.renameSync(tmpPath, absPath);
10264
10293
  return true;
10265
10294
  }
10266
10295
  cacheKeyPrefix() {
@@ -10292,7 +10321,7 @@ var init_jobState = __esm({
10292
10321
  });
10293
10322
 
10294
10323
  // src/scripts/goalCapabilityScheduling.ts
10295
- import * as path29 from "path";
10324
+ import * as path30 from "path";
10296
10325
  function isCapabilityCadenceGoal(goal, extra) {
10297
10326
  return extra.scheduleMode === "agentLoop" || extra.scheduler === "agentLoop" || goal.type === "standing" && goal.capabilities.length > 0;
10298
10327
  }
@@ -10348,7 +10377,7 @@ function planTargetLoopSchedule(opts) {
10348
10377
  }
10349
10378
  async function planGoalCapabilitySchedule(opts) {
10350
10379
  const jobsDir = opts.jobsDir ?? capabilitiesRoot(opts.cwd);
10351
- const jobsRoot = path29.resolve(opts.cwd, jobsDir);
10380
+ const jobsRoot = path30.resolve(opts.cwd, jobsDir);
10352
10381
  const now = opts.now ?? /* @__PURE__ */ new Date();
10353
10382
  const at = now.toISOString();
10354
10383
  const backend = resolveBackend({ config: opts.config, cwd: opts.cwd, jobsDir });
@@ -12164,8 +12193,8 @@ var init_classifyByLabel = __esm({
12164
12193
 
12165
12194
  // src/scripts/commitAndPush.ts
12166
12195
  import { createHash as createHash5 } from "crypto";
12167
- import * as fs32 from "fs";
12168
- import * as path30 from "path";
12196
+ import * as fs33 from "fs";
12197
+ import * as path31 from "path";
12169
12198
  function sentinelPathForStage(cwd, profileName, workflowExecutionKey) {
12170
12199
  const runId = resolveRunId();
12171
12200
  const executionSuffix = typeof workflowExecutionKey === "string" && workflowExecutionKey.length > 0 ? `-${createHash5("sha256").update(workflowExecutionKey).digest("hex").slice(0, 16)}` : "";
@@ -12187,9 +12216,9 @@ var init_commitAndPush = __esm({
12187
12216
  }
12188
12217
  const idempotencyEnabled = process.env.KODY_COMMIT_IDEMPOTENCY !== "0";
12189
12218
  const sentinel = idempotencyEnabled ? sentinelPathForStage(ctx.cwd, profile.name, ctx.data.workflowExecutionKey) : null;
12190
- if (sentinel && fs32.existsSync(sentinel)) {
12219
+ if (sentinel && fs33.existsSync(sentinel)) {
12191
12220
  try {
12192
- const replay = JSON.parse(fs32.readFileSync(sentinel, "utf-8"));
12221
+ const replay = JSON.parse(fs33.readFileSync(sentinel, "utf-8"));
12193
12222
  ctx.data.commitResult = replay.commitResult ?? { committed: false, pushed: false };
12194
12223
  if (Array.isArray(replay.changedFiles)) ctx.data.changedFiles = replay.changedFiles;
12195
12224
  if (typeof replay.hasCommitsAhead === "boolean") ctx.data.hasCommitsAhead = replay.hasCommitsAhead;
@@ -12249,8 +12278,8 @@ var init_commitAndPush = __esm({
12249
12278
  const result = ctx.data.commitResult;
12250
12279
  if (sentinel && result?.committed) {
12251
12280
  try {
12252
- fs32.mkdirSync(path30.dirname(sentinel), { recursive: true });
12253
- fs32.writeFileSync(
12281
+ fs33.mkdirSync(path31.dirname(sentinel), { recursive: true });
12282
+ fs33.writeFileSync(
12254
12283
  sentinel,
12255
12284
  JSON.stringify(
12256
12285
  {
@@ -12344,8 +12373,8 @@ var init_commitGoalState = __esm({
12344
12373
  });
12345
12374
 
12346
12375
  // src/scripts/composePrompt.ts
12347
- import * as fs33 from "fs";
12348
- import * as path31 from "path";
12376
+ import * as fs34 from "fs";
12377
+ import * as path32 from "path";
12349
12378
  function fenceUntrusted(value) {
12350
12379
  if (value.trim().length === 0) return value;
12351
12380
  const safe = value.replace(/-{3,}\s*END UNTRUSTED INPUT\s*-{3,}/gi, "[END UNTRUSTED INPUT]");
@@ -12469,10 +12498,10 @@ var init_composePrompt = __esm({
12469
12498
  const explicit = ctx.data.promptTemplate;
12470
12499
  const mode = ctx.args.mode;
12471
12500
  const candidates = [
12472
- explicit ? path31.join(profile.dir, explicit) : null,
12473
- mode ? path31.join(profile.dir, "prompts", `${mode}.md`) : null,
12474
- path31.join(profile.dir, "prompt.md"),
12475
- path31.join(profile.dir, "capability.md")
12501
+ explicit ? path32.join(profile.dir, explicit) : null,
12502
+ mode ? path32.join(profile.dir, "prompts", `${mode}.md`) : null,
12503
+ path32.join(profile.dir, "prompt.md"),
12504
+ path32.join(profile.dir, "capability.md")
12476
12505
  ].filter(Boolean);
12477
12506
  let templatePath = "";
12478
12507
  let template = "";
@@ -12485,7 +12514,7 @@ var init_composePrompt = __esm({
12485
12514
  break;
12486
12515
  }
12487
12516
  try {
12488
- template = fs33.readFileSync(c, "utf-8");
12517
+ template = fs34.readFileSync(c, "utf-8");
12489
12518
  templatePath = c;
12490
12519
  break;
12491
12520
  } catch (err) {
@@ -12496,7 +12525,7 @@ var init_composePrompt = __esm({
12496
12525
  if (!templatePath) {
12497
12526
  let dirState;
12498
12527
  try {
12499
- dirState = `dir contents: [${fs33.readdirSync(profile.dir).join(", ")}]`;
12528
+ dirState = `dir contents: [${fs34.readdirSync(profile.dir).join(", ")}]`;
12500
12529
  } catch (err) {
12501
12530
  dirState = `readdir(${profile.dir}) failed: ${err?.code ?? String(err)}`;
12502
12531
  }
@@ -13230,19 +13259,19 @@ var init_deriveQaScopeFromIssue = __esm({
13230
13259
 
13231
13260
  // src/scripts/diagMcp.ts
13232
13261
  import { execFileSync as execFileSync9 } from "child_process";
13233
- import * as fs34 from "fs";
13262
+ import * as fs35 from "fs";
13234
13263
  import * as os5 from "os";
13235
- import * as path32 from "path";
13264
+ import * as path33 from "path";
13236
13265
  var diagMcp;
13237
13266
  var init_diagMcp = __esm({
13238
13267
  "src/scripts/diagMcp.ts"() {
13239
13268
  "use strict";
13240
13269
  diagMcp = async (_ctx) => {
13241
13270
  const home = os5.homedir();
13242
- const cacheDir = path32.join(home, ".cache", "ms-playwright");
13271
+ const cacheDir = path33.join(home, ".cache", "ms-playwright");
13243
13272
  let entries = [];
13244
13273
  try {
13245
- entries = fs34.readdirSync(cacheDir);
13274
+ entries = fs35.readdirSync(cacheDir);
13246
13275
  } catch {
13247
13276
  }
13248
13277
  const hasChromium = entries.some((e) => e.startsWith("chromium"));
@@ -13270,13 +13299,13 @@ var init_diagMcp = __esm({
13270
13299
  });
13271
13300
 
13272
13301
  // src/scripts/frameworkDetectors.ts
13273
- import * as fs35 from "fs";
13274
- import * as path33 from "path";
13302
+ import * as fs36 from "fs";
13303
+ import * as path34 from "path";
13275
13304
  function detectFrameworks(cwd) {
13276
13305
  const out = [];
13277
13306
  let deps = {};
13278
13307
  try {
13279
- const pkg = JSON.parse(fs35.readFileSync(path33.join(cwd, "package.json"), "utf-8"));
13308
+ const pkg = JSON.parse(fs36.readFileSync(path34.join(cwd, "package.json"), "utf-8"));
13280
13309
  deps = { ...pkg.dependencies, ...pkg.devDependencies };
13281
13310
  } catch {
13282
13311
  return out;
@@ -13313,25 +13342,25 @@ function detectFrameworks(cwd) {
13313
13342
  }
13314
13343
  function findFile(cwd, candidates) {
13315
13344
  for (const c of candidates) {
13316
- if (fs35.existsSync(path33.join(cwd, c))) return c;
13345
+ if (fs36.existsSync(path34.join(cwd, c))) return c;
13317
13346
  }
13318
13347
  return null;
13319
13348
  }
13320
13349
  function discoverPayloadCollections(cwd) {
13321
13350
  const out = [];
13322
13351
  for (const dir of COLLECTION_DIRS) {
13323
- const full = path33.join(cwd, dir);
13324
- if (!fs35.existsSync(full)) continue;
13352
+ const full = path34.join(cwd, dir);
13353
+ if (!fs36.existsSync(full)) continue;
13325
13354
  let files;
13326
13355
  try {
13327
- files = fs35.readdirSync(full).filter((f) => f.endsWith(".ts") || f.endsWith(".tsx"));
13356
+ files = fs36.readdirSync(full).filter((f) => f.endsWith(".ts") || f.endsWith(".tsx"));
13328
13357
  } catch {
13329
13358
  continue;
13330
13359
  }
13331
13360
  for (const file of files) {
13332
13361
  try {
13333
- const filePath = path33.join(full, file);
13334
- const content = fs35.readFileSync(filePath, "utf-8").slice(0, 1e4);
13362
+ const filePath = path34.join(full, file);
13363
+ const content = fs36.readFileSync(filePath, "utf-8").slice(0, 1e4);
13335
13364
  const slugMatch = content.match(/slug:\s*['"]([a-z0-9-]+)['"]/);
13336
13365
  if (!slugMatch) continue;
13337
13366
  const slug = slugMatch[1];
@@ -13345,7 +13374,7 @@ function discoverPayloadCollections(cwd) {
13345
13374
  out.push({
13346
13375
  name,
13347
13376
  slug,
13348
- filePath: path33.relative(cwd, filePath),
13377
+ filePath: path34.relative(cwd, filePath),
13349
13378
  fields: fields.slice(0, 20),
13350
13379
  hasAdmin
13351
13380
  });
@@ -13358,28 +13387,28 @@ function discoverPayloadCollections(cwd) {
13358
13387
  function discoverAdminComponents(cwd, collections) {
13359
13388
  const out = [];
13360
13389
  for (const dir of ADMIN_COMPONENT_DIRS) {
13361
- const full = path33.join(cwd, dir);
13362
- if (!fs35.existsSync(full)) continue;
13390
+ const full = path34.join(cwd, dir);
13391
+ if (!fs36.existsSync(full)) continue;
13363
13392
  let entries;
13364
13393
  try {
13365
- entries = fs35.readdirSync(full, { withFileTypes: true });
13394
+ entries = fs36.readdirSync(full, { withFileTypes: true });
13366
13395
  } catch {
13367
13396
  continue;
13368
13397
  }
13369
13398
  for (const entry of entries) {
13370
- const entryPath = path33.join(full, entry.name);
13399
+ const entryPath = path34.join(full, entry.name);
13371
13400
  let name;
13372
13401
  let filePath;
13373
13402
  if (entry.isDirectory()) {
13374
13403
  const indexFile = ["index.tsx", "index.ts", "index.jsx", "index.js"].find(
13375
- (f) => fs35.existsSync(path33.join(entryPath, f))
13404
+ (f) => fs36.existsSync(path34.join(entryPath, f))
13376
13405
  );
13377
13406
  if (!indexFile) continue;
13378
13407
  name = entry.name;
13379
- filePath = path33.relative(cwd, path33.join(entryPath, indexFile));
13408
+ filePath = path34.relative(cwd, path34.join(entryPath, indexFile));
13380
13409
  } else if (/\.(tsx?|jsx?)$/.test(entry.name)) {
13381
13410
  name = entry.name.replace(/\.(tsx?|jsx?)$/, "");
13382
- filePath = path33.relative(cwd, entryPath);
13411
+ filePath = path34.relative(cwd, entryPath);
13383
13412
  } else {
13384
13413
  continue;
13385
13414
  }
@@ -13387,7 +13416,7 @@ function discoverAdminComponents(cwd, collections) {
13387
13416
  if (collections) {
13388
13417
  for (const col of collections) {
13389
13418
  try {
13390
- const colContent = fs35.readFileSync(path33.join(cwd, col.filePath), "utf-8");
13419
+ const colContent = fs36.readFileSync(path34.join(cwd, col.filePath), "utf-8");
13391
13420
  if (colContent.includes(name)) {
13392
13421
  usedInCollection = col.slug;
13393
13422
  break;
@@ -13405,8 +13434,8 @@ function scanApiRoutes(cwd) {
13405
13434
  const out = [];
13406
13435
  const appDirs = ["src/app", "app"];
13407
13436
  for (const appDir of appDirs) {
13408
- const apiDir = path33.join(cwd, appDir, "api");
13409
- if (!fs35.existsSync(apiDir)) continue;
13437
+ const apiDir = path34.join(cwd, appDir, "api");
13438
+ if (!fs36.existsSync(apiDir)) continue;
13410
13439
  walkApiRoutes(apiDir, "/api", cwd, out);
13411
13440
  break;
13412
13441
  }
@@ -13415,14 +13444,14 @@ function scanApiRoutes(cwd) {
13415
13444
  function walkApiRoutes(dir, prefix, cwd, out) {
13416
13445
  let entries;
13417
13446
  try {
13418
- entries = fs35.readdirSync(dir, { withFileTypes: true });
13447
+ entries = fs36.readdirSync(dir, { withFileTypes: true });
13419
13448
  } catch {
13420
13449
  return;
13421
13450
  }
13422
13451
  const routeFile = entries.find((e) => e.isFile() && /^route\.(ts|js|tsx|jsx)$/.test(e.name));
13423
13452
  if (routeFile) {
13424
13453
  try {
13425
- const content = fs35.readFileSync(path33.join(dir, routeFile.name), "utf-8").slice(0, 5e3);
13454
+ const content = fs36.readFileSync(path34.join(dir, routeFile.name), "utf-8").slice(0, 5e3);
13426
13455
  const methods = HTTP_METHODS.filter(
13427
13456
  (m) => new RegExp(`export\\s+(?:async\\s+)?function\\s+${m}\\b`).test(content)
13428
13457
  );
@@ -13430,7 +13459,7 @@ function walkApiRoutes(dir, prefix, cwd, out) {
13430
13459
  out.push({
13431
13460
  path: prefix,
13432
13461
  methods,
13433
- filePath: path33.relative(cwd, path33.join(dir, routeFile.name))
13462
+ filePath: path34.relative(cwd, path34.join(dir, routeFile.name))
13434
13463
  });
13435
13464
  }
13436
13465
  } catch {
@@ -13441,7 +13470,7 @@ function walkApiRoutes(dir, prefix, cwd, out) {
13441
13470
  if (entry.name === "node_modules" || entry.name === ".next") continue;
13442
13471
  let segment = entry.name;
13443
13472
  if (segment.startsWith("(") && segment.endsWith(")")) {
13444
- walkApiRoutes(path33.join(dir, entry.name), prefix, cwd, out);
13473
+ walkApiRoutes(path34.join(dir, entry.name), prefix, cwd, out);
13445
13474
  continue;
13446
13475
  }
13447
13476
  if (segment.startsWith("[[") && segment.endsWith("]]")) {
@@ -13449,16 +13478,16 @@ function walkApiRoutes(dir, prefix, cwd, out) {
13449
13478
  } else if (segment.startsWith("[") && segment.endsWith("]")) {
13450
13479
  segment = `:${segment.slice(1, -1)}`;
13451
13480
  }
13452
- walkApiRoutes(path33.join(dir, entry.name), `${prefix}/${segment}`, cwd, out);
13481
+ walkApiRoutes(path34.join(dir, entry.name), `${prefix}/${segment}`, cwd, out);
13453
13482
  }
13454
13483
  }
13455
13484
  function scanEnvVars(cwd) {
13456
13485
  const candidates = [".env.example", ".env.local.example", ".env.template"];
13457
13486
  for (const envFile of candidates) {
13458
- const envPath = path33.join(cwd, envFile);
13459
- if (!fs35.existsSync(envPath)) continue;
13487
+ const envPath = path34.join(cwd, envFile);
13488
+ if (!fs36.existsSync(envPath)) continue;
13460
13489
  try {
13461
- const content = fs35.readFileSync(envPath, "utf-8");
13490
+ const content = fs36.readFileSync(envPath, "utf-8");
13462
13491
  const vars = [];
13463
13492
  for (const line of content.split("\n")) {
13464
13493
  const trimmed = line.trim();
@@ -13503,8 +13532,8 @@ var init_frameworkDetectors = __esm({
13503
13532
  });
13504
13533
 
13505
13534
  // src/scripts/discoverQaContext.ts
13506
- import * as fs36 from "fs";
13507
- import * as path34 from "path";
13535
+ import * as fs37 from "fs";
13536
+ import * as path35 from "path";
13508
13537
  function runQaDiscovery(cwd) {
13509
13538
  const out = {
13510
13539
  routes: [],
@@ -13535,9 +13564,9 @@ function runQaDiscovery(cwd) {
13535
13564
  }
13536
13565
  function detectDevServer(cwd, out) {
13537
13566
  try {
13538
- const pkg = JSON.parse(fs36.readFileSync(path34.join(cwd, "package.json"), "utf-8"));
13567
+ const pkg = JSON.parse(fs37.readFileSync(path35.join(cwd, "package.json"), "utf-8"));
13539
13568
  const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
13540
- 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";
13569
+ const pm = fs37.existsSync(path35.join(cwd, "pnpm-lock.yaml")) ? "pnpm" : fs37.existsSync(path35.join(cwd, "yarn.lock")) ? "yarn" : fs37.existsSync(path35.join(cwd, "bun.lockb")) ? "bun" : "npm";
13541
13570
  if (pkg.scripts?.dev) out.devCommand = `${pm} dev`;
13542
13571
  if (allDeps.next || allDeps.nuxt) out.devPort = 3e3;
13543
13572
  else if (allDeps.vite) out.devPort = 5173;
@@ -13547,8 +13576,8 @@ function detectDevServer(cwd, out) {
13547
13576
  function scanFrontendRoutes(cwd, out) {
13548
13577
  const appDirs = ["src/app", "app"];
13549
13578
  for (const appDir of appDirs) {
13550
- const full = path34.join(cwd, appDir);
13551
- if (!fs36.existsSync(full)) continue;
13579
+ const full = path35.join(cwd, appDir);
13580
+ if (!fs37.existsSync(full)) continue;
13552
13581
  walkFrontendRoutes(full, "", out);
13553
13582
  break;
13554
13583
  }
@@ -13556,7 +13585,7 @@ function scanFrontendRoutes(cwd, out) {
13556
13585
  function walkFrontendRoutes(dir, prefix, out) {
13557
13586
  let entries;
13558
13587
  try {
13559
- entries = fs36.readdirSync(dir, { withFileTypes: true });
13588
+ entries = fs37.readdirSync(dir, { withFileTypes: true });
13560
13589
  } catch {
13561
13590
  return;
13562
13591
  }
@@ -13573,7 +13602,7 @@ function walkFrontendRoutes(dir, prefix, out) {
13573
13602
  if (entry.name === "node_modules" || entry.name === ".next") continue;
13574
13603
  let segment = entry.name;
13575
13604
  if (segment.startsWith("(") && segment.endsWith(")")) {
13576
- walkFrontendRoutes(path34.join(dir, entry.name), prefix, out);
13605
+ walkFrontendRoutes(path35.join(dir, entry.name), prefix, out);
13577
13606
  continue;
13578
13607
  }
13579
13608
  if (segment.startsWith("[[") && segment.endsWith("]]")) {
@@ -13581,7 +13610,7 @@ function walkFrontendRoutes(dir, prefix, out) {
13581
13610
  } else if (segment.startsWith("[") && segment.endsWith("]")) {
13582
13611
  segment = `:${segment.slice(1, -1)}`;
13583
13612
  }
13584
- walkFrontendRoutes(path34.join(dir, entry.name), `${prefix}/${segment}`, out);
13613
+ walkFrontendRoutes(path35.join(dir, entry.name), `${prefix}/${segment}`, out);
13585
13614
  }
13586
13615
  }
13587
13616
  function detectAuthFiles(cwd, out) {
@@ -13598,23 +13627,23 @@ function detectAuthFiles(cwd, out) {
13598
13627
  "src/app/api/oauth"
13599
13628
  ];
13600
13629
  for (const c of candidates) {
13601
- if (fs36.existsSync(path34.join(cwd, c))) out.authFiles.push(c);
13630
+ if (fs37.existsSync(path35.join(cwd, c))) out.authFiles.push(c);
13602
13631
  }
13603
13632
  }
13604
13633
  function detectRoles(cwd, out) {
13605
13634
  const rolePaths = ["src/types", "src/lib", "src/utils", "src/constants", "src/access", "src/collections"];
13606
13635
  for (const rp of rolePaths) {
13607
- const dir = path34.join(cwd, rp);
13608
- if (!fs36.existsSync(dir)) continue;
13636
+ const dir = path35.join(cwd, rp);
13637
+ if (!fs37.existsSync(dir)) continue;
13609
13638
  let files;
13610
13639
  try {
13611
- files = fs36.readdirSync(dir).filter((f) => f.endsWith(".ts") || f.endsWith(".tsx"));
13640
+ files = fs37.readdirSync(dir).filter((f) => f.endsWith(".ts") || f.endsWith(".tsx"));
13612
13641
  } catch {
13613
13642
  continue;
13614
13643
  }
13615
13644
  for (const f of files) {
13616
13645
  try {
13617
- const content = fs36.readFileSync(path34.join(dir, f), "utf-8").slice(0, 5e3);
13646
+ const content = fs37.readFileSync(path35.join(dir, f), "utf-8").slice(0, 5e3);
13618
13647
  const roleMatches = content.match(/(?:role|Role|ROLE)\s*[=:]\s*['"](\w+)['"]/g);
13619
13648
  if (roleMatches) {
13620
13649
  for (const m of roleMatches) {
@@ -13875,8 +13904,8 @@ var init_dispatchClassified = __esm({
13875
13904
  });
13876
13905
 
13877
13906
  // src/loopDefinitions.ts
13878
- import * as fs37 from "fs";
13879
- import * as path35 from "path";
13907
+ import * as fs38 from "fs";
13908
+ import * as path36 from "path";
13880
13909
  function normalizeLoopDefinition(value) {
13881
13910
  if (!value || typeof value !== "object" || Array.isArray(value)) return null;
13882
13911
  const raw = value;
@@ -13903,10 +13932,10 @@ function readLoopDefinition(cwd, id) {
13903
13932
  if (!ID.test(id)) return null;
13904
13933
  const roots = loopRoots(cwd);
13905
13934
  for (const root of roots) {
13906
- const filePath = path35.join(root, "loops", id, "loop.json");
13907
- if (!fs37.existsSync(filePath)) continue;
13935
+ const filePath = path36.join(root, "loops", id, "loop.json");
13936
+ if (!fs38.existsSync(filePath)) continue;
13908
13937
  try {
13909
- const loop = normalizeLoopDefinition(JSON.parse(fs37.readFileSync(filePath, "utf8")));
13938
+ const loop = normalizeLoopDefinition(JSON.parse(fs38.readFileSync(filePath, "utf8")));
13910
13939
  if (loop?.id === id) return loop;
13911
13940
  process.stderr.write(`[kody] invalid Loop definition: ${filePath}
13912
13941
  `);
@@ -13916,7 +13945,7 @@ function readLoopDefinition(cwd, id) {
13916
13945
  }
13917
13946
  }
13918
13947
  process.stderr.write(
13919
- `[kody] Loop not found: ${id} (${roots.map((root) => path35.join(root, "loops", id, "loop.json")).join(", ")})
13948
+ `[kody] Loop not found: ${id} (${roots.map((root) => path36.join(root, "loops", id, "loop.json")).join(", ")})
13920
13949
  `
13921
13950
  );
13922
13951
  return null;
@@ -13925,14 +13954,14 @@ function listLoopDefinitions(cwd) {
13925
13954
  const roots = loopRoots(cwd);
13926
13955
  const byId = /* @__PURE__ */ new Map();
13927
13956
  for (const root of roots.reverse()) {
13928
- const loopsDir = path35.join(root, "loops");
13929
- if (!fs37.existsSync(loopsDir)) continue;
13930
- for (const id of fs37.readdirSync(loopsDir).sort()) {
13957
+ const loopsDir = path36.join(root, "loops");
13958
+ if (!fs38.existsSync(loopsDir)) continue;
13959
+ for (const id of fs38.readdirSync(loopsDir).sort()) {
13931
13960
  if (!ID.test(id)) continue;
13932
- const filePath = path35.join(loopsDir, id, "loop.json");
13933
- if (!fs37.existsSync(filePath)) continue;
13961
+ const filePath = path36.join(loopsDir, id, "loop.json");
13962
+ if (!fs38.existsSync(filePath)) continue;
13934
13963
  try {
13935
- const loop = normalizeLoopDefinition(JSON.parse(fs37.readFileSync(filePath, "utf8")));
13964
+ const loop = normalizeLoopDefinition(JSON.parse(fs38.readFileSync(filePath, "utf8")));
13936
13965
  if (loop?.id === id) byId.set(id, loop);
13937
13966
  } catch {
13938
13967
  process.stderr.write(`[kody] unreadable Loop definition: ${filePath}
@@ -13944,8 +13973,8 @@ function listLoopDefinitions(cwd) {
13944
13973
  }
13945
13974
  function loopRoots(cwd) {
13946
13975
  return [
13947
- path35.join(cwd, ".kody-engine", "runtime"),
13948
- path35.join(cwd, ".kody-engine", "definitions"),
13976
+ path36.join(cwd, ".kody-engine", "runtime"),
13977
+ path36.join(cwd, ".kody-engine", "definitions"),
13949
13978
  definitionsRoot(cwd)
13950
13979
  ].filter((root, index, roots) => roots.indexOf(root) === index);
13951
13980
  }
@@ -15223,15 +15252,15 @@ var init_fixFlow = __esm({
15223
15252
  });
15224
15253
 
15225
15254
  // src/workflow-template.ts
15226
- import * as fs38 from "fs";
15227
- import * as path36 from "path";
15255
+ import * as fs39 from "fs";
15256
+ import * as path37 from "path";
15228
15257
  import { fileURLToPath } from "url";
15229
15258
  function loadKodyWorkflowTemplate() {
15230
- const here = path36.dirname(fileURLToPath(import.meta.url));
15231
- const candidates = [path36.resolve(here, "../templates/kody.yml"), path36.resolve(here, "../../templates/kody.yml")];
15232
- const source = candidates.find((candidate) => fs38.existsSync(candidate));
15259
+ const here = path37.dirname(fileURLToPath(import.meta.url));
15260
+ const candidates = [path37.resolve(here, "../templates/kody.yml"), path37.resolve(here, "../../templates/kody.yml")];
15261
+ const source = candidates.find((candidate) => fs39.existsSync(candidate));
15233
15262
  if (!source) throw new Error(`Kody workflow template is missing: ${KODY_WORKFLOW_TEMPLATE_PATH}`);
15234
- return fs38.readFileSync(source, "utf8");
15263
+ return fs39.readFileSync(source, "utf8");
15235
15264
  }
15236
15265
  var KODY_WORKFLOW_TEMPLATE_PATH;
15237
15266
  var init_workflow_template = __esm({
@@ -15243,12 +15272,12 @@ var init_workflow_template = __esm({
15243
15272
 
15244
15273
  // src/scripts/initFlow.ts
15245
15274
  import { execFileSync as execFileSync14 } from "child_process";
15246
- import * as fs39 from "fs";
15247
- import * as path37 from "path";
15275
+ import * as fs40 from "fs";
15276
+ import * as path38 from "path";
15248
15277
  function detectPackageManager(cwd) {
15249
- if (fs39.existsSync(path37.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
15250
- if (fs39.existsSync(path37.join(cwd, "yarn.lock"))) return "yarn";
15251
- if (fs39.existsSync(path37.join(cwd, "bun.lockb"))) return "bun";
15278
+ if (fs40.existsSync(path38.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
15279
+ if (fs40.existsSync(path38.join(cwd, "yarn.lock"))) return "yarn";
15280
+ if (fs40.existsSync(path38.join(cwd, "bun.lockb"))) return "bun";
15252
15281
  return "npm";
15253
15282
  }
15254
15283
  function qualityCommandsFor(pm) {
@@ -15320,22 +15349,22 @@ function performInit(cwd, force) {
15320
15349
  const pm = detectPackageManager(cwd);
15321
15350
  const ownerRepo = detectOwnerRepo(cwd);
15322
15351
  const defaultBranch = defaultBranchFromGit(cwd);
15323
- const configPath = path37.join(cwd, "kody.config.json");
15324
- if (fs39.existsSync(configPath) && !force) {
15352
+ const configPath = path38.join(cwd, "kody.config.json");
15353
+ if (fs40.existsSync(configPath) && !force) {
15325
15354
  skipped.push("kody.config.json");
15326
15355
  } else {
15327
15356
  const cfg = makeConfig(pm, ownerRepo, defaultBranch);
15328
- fs39.writeFileSync(configPath, `${JSON.stringify(cfg, null, 2)}
15357
+ fs40.writeFileSync(configPath, `${JSON.stringify(cfg, null, 2)}
15329
15358
  `);
15330
15359
  wrote.push("kody.config.json");
15331
15360
  }
15332
- const workflowDir = path37.join(cwd, ".github", "workflows");
15333
- const workflowPath = path37.join(workflowDir, "kody.yml");
15334
- if (fs39.existsSync(workflowPath) && !force) {
15361
+ const workflowDir = path38.join(cwd, ".github", "workflows");
15362
+ const workflowPath = path38.join(workflowDir, "kody.yml");
15363
+ if (fs40.existsSync(workflowPath) && !force) {
15335
15364
  skipped.push(".github/workflows/kody.yml");
15336
15365
  } else {
15337
- fs39.mkdirSync(workflowDir, { recursive: true });
15338
- fs39.writeFileSync(workflowPath, loadKodyWorkflowTemplate());
15366
+ fs40.mkdirSync(workflowDir, { recursive: true });
15367
+ fs40.writeFileSync(workflowPath, loadKodyWorkflowTemplate());
15339
15368
  wrote.push(".github/workflows/kody.yml");
15340
15369
  }
15341
15370
  let labels;
@@ -15386,7 +15415,7 @@ Nothing to do. All files already present. (Use --force to overwrite.)
15386
15415
  });
15387
15416
 
15388
15417
  // src/scripts/loadAgentAdhoc.ts
15389
- import * as fs40 from "fs";
15418
+ import * as fs41 from "fs";
15390
15419
  function resolveMessage(messageArg) {
15391
15420
  const fromComment = readCommentBody();
15392
15421
  if (fromComment) return stripDirective(fromComment);
@@ -15394,9 +15423,9 @@ function resolveMessage(messageArg) {
15394
15423
  }
15395
15424
  function readCommentBody() {
15396
15425
  const eventPath = process.env.GITHUB_EVENT_PATH;
15397
- if (!eventPath || !fs40.existsSync(eventPath)) return "";
15426
+ if (!eventPath || !fs41.existsSync(eventPath)) return "";
15398
15427
  try {
15399
- const event = JSON.parse(fs40.readFileSync(eventPath, "utf-8"));
15428
+ const event = JSON.parse(fs41.readFileSync(eventPath, "utf-8"));
15400
15429
  return String(event.comment?.body ?? "");
15401
15430
  } catch {
15402
15431
  return "";
@@ -15450,10 +15479,10 @@ var init_loadAgentAdhoc = __esm({
15450
15479
  throw new Error("loadAgentAdhoc: ctx.args.agent must be a non-empty slug");
15451
15480
  }
15452
15481
  const agentPath = resolveAgentFile2(ctx.cwd, agentSlug, agentsDir);
15453
- if (!fs40.existsSync(agentPath)) {
15482
+ if (!fs41.existsSync(agentPath)) {
15454
15483
  throw new Error(`loadAgentAdhoc: agent identity not found: ${agentPath}`);
15455
15484
  }
15456
- const { title, body } = parseAgentFile(fs40.readFileSync(agentPath, "utf-8"), agentSlug);
15485
+ const { title, body } = parseAgentFile(fs41.readFileSync(agentPath, "utf-8"), agentSlug);
15457
15486
  const message = resolveMessage(ctx.args.message);
15458
15487
  if (!message) {
15459
15488
  throw new Error(
@@ -15525,13 +15554,13 @@ var init_loadCapabilityState = __esm({
15525
15554
  function isCompanyIntentId(value) {
15526
15555
  return SLUG_RE2.test(value);
15527
15556
  }
15528
- function normalizeCompanyIntent(path55, raw) {
15557
+ function normalizeCompanyIntent(path58, raw) {
15529
15558
  if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
15530
- throw new Error(`${path55}: intent must be JSON object`);
15559
+ throw new Error(`${path58}: intent must be JSON object`);
15531
15560
  }
15532
15561
  const input = raw;
15533
15562
  const id = stringField4(input.id);
15534
- if (!id || !isCompanyIntentId(id)) throw new Error(`${path55}: invalid intent id`);
15563
+ if (!id || !isCompanyIntentId(id)) throw new Error(`${path58}: invalid intent id`);
15535
15564
  const createdAt = stringField4(input.createdAt) || nowIso();
15536
15565
  const updatedAt = stringField4(input.updatedAt) || createdAt;
15537
15566
  const description = stringField4(input.description);
@@ -15693,7 +15722,7 @@ function retryDelaysMs() {
15693
15722
  }
15694
15723
  function sleep(ms) {
15695
15724
  if (ms <= 0) return Promise.resolve();
15696
- return new Promise((resolve21) => setTimeout(resolve21, ms));
15725
+ return new Promise((resolve23) => setTimeout(resolve23, ms));
15697
15726
  }
15698
15727
  async function fetchGoalStateWithRetry(config, goalId, cwd) {
15699
15728
  let state = await fetchGoalStateAsync(config, goalId, cwd);
@@ -15822,8 +15851,8 @@ var init_loadIssueStateComment = __esm({
15822
15851
  });
15823
15852
 
15824
15853
  // src/scripts/loadJobFromFile.ts
15825
- import * as fs41 from "fs";
15826
- import * as path38 from "path";
15854
+ import * as fs42 from "fs";
15855
+ import * as path39 from "path";
15827
15856
  function parseJobFile(raw, slug) {
15828
15857
  let stripped = raw;
15829
15858
  if (stripped.startsWith("---\n")) {
@@ -15862,10 +15891,10 @@ var init_loadJobFromFile = __esm({
15862
15891
  if (!slug) {
15863
15892
  throw new Error(`loadJobFromFile: ctx.args.${slugArg} must be a non-empty slug`);
15864
15893
  }
15865
- const capability = resolveCapabilityFolder(slug, path38.resolve(ctx.cwd, jobsDir));
15894
+ const capability = resolveCapabilityFolder(slug, path39.resolve(ctx.cwd, jobsDir));
15866
15895
  if (!capability) {
15867
15896
  throw new Error(
15868
- `loadJobFromFile: capability folder not found or incomplete: ${path38.resolve(ctx.cwd, jobsDir, slug)}`
15897
+ `loadJobFromFile: capability folder not found or incomplete: ${path39.resolve(ctx.cwd, jobsDir, slug)}`
15869
15898
  );
15870
15899
  }
15871
15900
  const { title, body, config } = capability;
@@ -15875,12 +15904,12 @@ var init_loadJobFromFile = __esm({
15875
15904
  let agentIdentity = "";
15876
15905
  if (agentSlug) {
15877
15906
  const agentPath = resolveAgentFile2(ctx.cwd, agentSlug, agentsDir);
15878
- if (!fs41.existsSync(agentPath)) {
15907
+ if (!fs42.existsSync(agentPath)) {
15879
15908
  throw new Error(
15880
15909
  `loadJobFromFile: capability '${slug}' declares agent '${agentSlug}' but ${agentPath} does not exist`
15881
15910
  );
15882
15911
  }
15883
- const agentRaw = fs41.readFileSync(agentPath, "utf-8");
15912
+ const agentRaw = fs42.readFileSync(agentPath, "utf-8");
15884
15913
  const parsed = parseJobFile(agentRaw, agentSlug);
15885
15914
  agentTitle = parsed.title;
15886
15915
  agentIdentity = parsed.body;
@@ -15960,13 +15989,13 @@ ${truncate2(issue2.body, FINDING_BODY_MAX_BYTES)}`;
15960
15989
  });
15961
15990
 
15962
15991
  // src/scripts/kodyVariables.ts
15963
- import * as fs42 from "fs";
15964
- import * as path39 from "path";
15992
+ import * as fs43 from "fs";
15993
+ import * as path40 from "path";
15965
15994
  function readKodyVariables(cwd) {
15966
- const full = path39.join(cwd, KODY_VARIABLES_REL_PATH);
15995
+ const full = path40.join(cwd, KODY_VARIABLES_REL_PATH);
15967
15996
  let raw;
15968
15997
  try {
15969
- raw = fs42.readFileSync(full, "utf-8");
15998
+ raw = fs43.readFileSync(full, "utf-8");
15970
15999
  } catch {
15971
16000
  return {};
15972
16001
  }
@@ -15991,8 +16020,8 @@ var init_kodyVariables = __esm({
15991
16020
  });
15992
16021
 
15993
16022
  // src/scripts/loadQaContext.ts
15994
- import * as fs43 from "fs";
15995
- import * as path40 from "path";
16023
+ import * as fs44 from "fs";
16024
+ import * as path41 from "path";
15996
16025
  function parseSlugList(value) {
15997
16026
  const inner = value.startsWith("[") && value.endsWith("]") ? value.slice(1, -1) : value;
15998
16027
  return inner.split(",").map(
@@ -16021,18 +16050,18 @@ function readProfileAgents(raw) {
16021
16050
  return { agent: agent ?? legacy ?? ["kody"], body };
16022
16051
  }
16023
16052
  function readProfile(cwd) {
16024
- const dir = path40.join(cwd, CONTEXT_DIR_REL_PATH);
16025
- if (!fs43.existsSync(dir)) return "";
16053
+ const dir = path41.join(cwd, CONTEXT_DIR_REL_PATH);
16054
+ if (!fs44.existsSync(dir)) return "";
16026
16055
  let entries;
16027
16056
  try {
16028
- entries = fs43.readdirSync(dir).filter((f) => f.endsWith(".md")).sort();
16057
+ entries = fs44.readdirSync(dir).filter((f) => f.endsWith(".md")).sort();
16029
16058
  } catch {
16030
16059
  return "";
16031
16060
  }
16032
16061
  const blocks = [];
16033
16062
  for (const file of entries) {
16034
16063
  try {
16035
- const raw = fs43.readFileSync(path40.join(dir, file), "utf-8");
16064
+ const raw = fs44.readFileSync(path41.join(dir, file), "utf-8");
16036
16065
  const { agent, body } = readProfileAgents(raw);
16037
16066
  if (!agent.includes(QA_AGENT) && !agent.includes(ALL_AGENTS)) continue;
16038
16067
  blocks.push(`## ${file}
@@ -16082,9 +16111,9 @@ var init_loadQaContext = __esm({
16082
16111
 
16083
16112
  // src/scripts/loadSimpleCapability.ts
16084
16113
  import { randomUUID as randomUUID2 } from "crypto";
16085
- import * as fs44 from "fs";
16114
+ import * as fs45 from "fs";
16086
16115
  import * as os6 from "os";
16087
- import * as path41 from "path";
16116
+ import * as path42 from "path";
16088
16117
  function registerCapabilitySubagents(profile, toolRoot, toolFiles) {
16089
16118
  const subagentFiles = toolFiles.flatMap((file) => {
16090
16119
  const match = /^agents\/([a-z][a-z0-9-]{0,63})\.md$/.exec(file);
@@ -16097,7 +16126,7 @@ function registerCapabilitySubagents(profile, toolRoot, toolFiles) {
16097
16126
  profile.subagentTemplates = {
16098
16127
  ...profile.subagentTemplates ?? {},
16099
16128
  ...Object.fromEntries(
16100
- subagentFiles.map(({ name, file }) => [name, fs44.readFileSync(path41.join(toolRoot, file), "utf-8")])
16129
+ subagentFiles.map(({ name, file }) => [name, fs45.readFileSync(path42.join(toolRoot, file), "utf-8")])
16101
16130
  )
16102
16131
  };
16103
16132
  if (!profile.claudeCode.tools.includes("Agent")) {
@@ -16138,14 +16167,14 @@ function scalar(value) {
16138
16167
  return value;
16139
16168
  }
16140
16169
  function listFiles(root) {
16141
- if (!fs44.existsSync(root)) return [];
16170
+ if (!fs45.existsSync(root)) return [];
16142
16171
  const files = [];
16143
16172
  const visit = (dir) => {
16144
- for (const entry of fs44.readdirSync(dir, { withFileTypes: true })) {
16145
- const absolute = path41.join(dir, entry.name);
16173
+ for (const entry of fs45.readdirSync(dir, { withFileTypes: true })) {
16174
+ const absolute = path42.join(dir, entry.name);
16146
16175
  if (entry.isSymbolicLink()) continue;
16147
16176
  if (entry.isDirectory()) visit(absolute);
16148
- else if (entry.isFile()) files.push(path41.relative(root, absolute));
16177
+ else if (entry.isFile()) files.push(path42.relative(root, absolute));
16149
16178
  }
16150
16179
  };
16151
16180
  visit(root);
@@ -16168,8 +16197,8 @@ var init_loadSimpleCapability = __esm({
16168
16197
  if (!capability) {
16169
16198
  throw new Error(`Capability "${slug}" is not a valid simple capability folder`);
16170
16199
  }
16171
- const toolRoot = path41.join(capability.dir, "tools");
16172
- const skillRoot = path41.join(capability.dir, "skills");
16200
+ const toolRoot = path42.join(capability.dir, "tools");
16201
+ const skillRoot = path42.join(capability.dir, "skills");
16173
16202
  const toolFiles = listFiles(toolRoot);
16174
16203
  const skillFiles = listFiles(skillRoot);
16175
16204
  const parsedInput = parseInput(ctx.args.input);
@@ -16194,14 +16223,14 @@ var init_loadSimpleCapability = __esm({
16194
16223
  }
16195
16224
  if (requiredSubagents.length > 0) ctx.data.requiredSubagents = requiredSubagents;
16196
16225
  if (capability.contract?.execution === "script") {
16197
- ctx.data.capabilityScriptPath = path41.join(capability.dir, "tools", "run.sh");
16226
+ ctx.data.capabilityScriptPath = path42.join(capability.dir, "tools", "run.sh");
16198
16227
  ctx.data.capabilitySecretNames = capability.contract.secrets ?? [];
16199
16228
  ctx.data.capabilityScriptTimeoutMs = capability.contract.timeoutMs;
16200
16229
  }
16201
16230
  if (capability.config.outputSchema) {
16202
16231
  ctx.data.capabilityOutputSchema = capability.config.outputSchema;
16203
16232
  }
16204
- const outputPath = ctx.data.capabilityExecution === "agent" && capability.config.outputSchema ? path41.join(os6.tmpdir(), `kody-capability-output-${randomUUID2()}.json`) : void 0;
16233
+ const outputPath = ctx.data.capabilityExecution === "agent" && capability.config.outputSchema ? path42.join(os6.tmpdir(), `kody-capability-output-${randomUUID2()}.json`) : void 0;
16205
16234
  if (outputPath) ctx.data.capabilityOutputPath = outputPath;
16206
16235
  ctx.data.capabilityEnvironment = {
16207
16236
  ...capabilityInputEnvironment(input),
@@ -16224,7 +16253,7 @@ var init_loadSimpleCapability = __esm({
16224
16253
  ...skillFiles.flatMap((file) => [
16225
16254
  `### ${file}`,
16226
16255
  "",
16227
- fs44.readFileSync(path41.join(skillRoot, file), "utf-8"),
16256
+ fs45.readFileSync(path42.join(skillRoot, file), "utf-8"),
16228
16257
  ""
16229
16258
  ])
16230
16259
  ] : [],
@@ -16233,7 +16262,7 @@ var init_loadSimpleCapability = __esm({
16233
16262
  "## Tools",
16234
16263
  "",
16235
16264
  "Inspect or run these capability-owned files when needed:",
16236
- ...toolFiles.map((file) => `- ${path41.join(toolRoot, file)}`)
16265
+ ...toolFiles.map((file) => `- ${path42.join(toolRoot, file)}`)
16237
16266
  ] : [],
16238
16267
  "",
16239
16268
  ...capability.config.outputSchema ? [
@@ -16264,8 +16293,8 @@ var init_loadSimpleCapability = __esm({
16264
16293
  });
16265
16294
 
16266
16295
  // src/taskContext.ts
16267
- import * as fs45 from "fs";
16268
- import * as path42 from "path";
16296
+ import * as fs46 from "fs";
16297
+ import * as path43 from "path";
16269
16298
  function buildTaskContext(args) {
16270
16299
  return {
16271
16300
  schemaVersion: TASK_CONTEXT_SCHEMA_VERSION,
@@ -16281,9 +16310,9 @@ function buildTaskContext(args) {
16281
16310
  function persistTaskContext(cwd, ctx) {
16282
16311
  try {
16283
16312
  const dir = runtimeStatePath(cwd, "agent-runs", ctx.runId);
16284
- fs45.mkdirSync(dir, { recursive: true });
16285
- const file = path42.join(dir, "task-context.json");
16286
- fs45.writeFileSync(file, `${JSON.stringify(ctx, null, 2)}
16313
+ fs46.mkdirSync(dir, { recursive: true });
16314
+ const file = path43.join(dir, "task-context.json");
16315
+ fs46.writeFileSync(file, `${JSON.stringify(ctx, null, 2)}
16287
16316
  `);
16288
16317
  return file;
16289
16318
  } catch (err) {
@@ -16710,19 +16739,19 @@ function parseAgencyModelProposal(raw) {
16710
16739
  function normalizeBundleFiles(bundle) {
16711
16740
  const seen = /* @__PURE__ */ new Set();
16712
16741
  return bundle.files.map((file, index) => {
16713
- const path55 = file.path.replace(/^\/+/, "");
16714
- const parts = path55.split("/");
16715
- if (!path55 || file.path.startsWith("/") || file.path.includes("\\") || parts.some((part) => !part || part === "." || part === "..")) {
16742
+ const path58 = file.path.replace(/^\/+/, "");
16743
+ const parts = path58.split("/");
16744
+ if (!path58 || file.path.startsWith("/") || file.path.includes("\\") || parts.some((part) => !part || part === "." || part === "..")) {
16716
16745
  throw new Error(`openAgencyModelReviewPr: files[${index}].path is unsafe`);
16717
16746
  }
16718
16747
  if (!/^(agents\/[^/]+\.md|capabilities\/[^/]+\/.+|goals\/(?:templates\/)?[^/]+\/.+|workflows\/[^/]+\.json)$/.test(
16719
- path55
16748
+ path58
16720
16749
  )) {
16721
16750
  throw new Error(`openAgencyModelReviewPr: files[${index}].path is not a supported definition path`);
16722
16751
  }
16723
- if (seen.has(path55)) throw new Error(`openAgencyModelReviewPr: duplicate generated file path: ${path55}`);
16724
- seen.add(path55);
16725
- return { path: path55, content: file.content.replace(/\r\n?/g, "\n") };
16752
+ if (seen.has(path58)) throw new Error(`openAgencyModelReviewPr: duplicate generated file path: ${path58}`);
16753
+ seen.add(path58);
16754
+ return { path: path58, content: file.content.replace(/\r\n?/g, "\n") };
16726
16755
  });
16727
16756
  }
16728
16757
  function buildProposalId(issueNumber, bundle, sourceLabel) {
@@ -17204,16 +17233,16 @@ var init_parseReproOutput = __esm({
17204
17233
  });
17205
17234
 
17206
17235
  // src/scripts/parseSimpleCapabilityOutput.ts
17207
- import * as fs46 from "fs";
17236
+ import * as fs47 from "fs";
17208
17237
  function stringList2(value) {
17209
17238
  return Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
17210
17239
  }
17211
17240
  function readOutputFile(outputPath) {
17212
- if (!outputPath || !fs46.existsSync(outputPath)) return { found: false };
17241
+ if (!outputPath || !fs47.existsSync(outputPath)) return { found: false };
17213
17242
  try {
17214
- return { found: true, value: JSON.parse(fs46.readFileSync(outputPath, "utf-8")) };
17243
+ return { found: true, value: JSON.parse(fs47.readFileSync(outputPath, "utf-8")) };
17215
17244
  } finally {
17216
- fs46.rmSync(outputPath, { force: true });
17245
+ fs47.rmSync(outputPath, { force: true });
17217
17246
  }
17218
17247
  }
17219
17248
  function parseOutput(text2) {
@@ -17832,9 +17861,9 @@ var init_postResearchComment = __esm({
17832
17861
  });
17833
17862
 
17834
17863
  // src/scripts/prepareBrowserAuth.ts
17835
- import * as fs47 from "fs";
17864
+ import * as fs48 from "fs";
17836
17865
  import * as os7 from "os";
17837
- import * as path43 from "path";
17866
+ import * as path44 from "path";
17838
17867
  function appendAuthMessage(ctx, message) {
17839
17868
  const current = typeof ctx.data.qaAuthBlock === "string" ? ctx.data.qaAuthBlock.trim() : "";
17840
17869
  ctx.data.qaAuthBlock = current ? `${current}
@@ -17873,9 +17902,9 @@ async function githubJson(url, token) {
17873
17902
  return await response.json();
17874
17903
  }
17875
17904
  function writeKodyStorageState(input) {
17876
- const directory = fs47.mkdtempSync(path43.join(os7.tmpdir(), "kody-browser-auth-"));
17877
- fs47.chmodSync(directory, 448);
17878
- const file = path43.join(directory, "storage-state.json");
17905
+ const directory = fs48.mkdtempSync(path44.join(os7.tmpdir(), "kody-browser-auth-"));
17906
+ fs48.chmodSync(directory, 448);
17907
+ const file = path44.join(directory, "storage-state.json");
17879
17908
  const now = Date.now();
17880
17909
  const repoEntry = {
17881
17910
  repoUrl: input.repoUrl,
@@ -17905,7 +17934,7 @@ function writeKodyStorageState(input) {
17905
17934
  }
17906
17935
  ]
17907
17936
  };
17908
- fs47.writeFileSync(file, JSON.stringify(storageState), { mode: 384 });
17937
+ fs48.writeFileSync(file, JSON.stringify(storageState), { mode: 384 });
17909
17938
  return { directory, file };
17910
17939
  }
17911
17940
  function configurePlaywright(profile, storageStatePath) {
@@ -17987,7 +18016,7 @@ async function prepareMethod(ctx, profile, method) {
17987
18016
  configurePlaywright(profile, state.file);
17988
18017
  const authDirectory = state.directory;
17989
18018
  registerRuntimeCleanup(ctx, () => {
17990
- fs47.rmSync(authDirectory, { recursive: true, force: true });
18019
+ fs48.rmSync(authDirectory, { recursive: true, force: true });
17991
18020
  });
17992
18021
  appendAuthMessage(
17993
18022
  ctx,
@@ -17995,7 +18024,7 @@ async function prepareMethod(ctx, profile, method) {
17995
18024
  );
17996
18025
  return true;
17997
18026
  } catch (error) {
17998
- if (state) fs47.rmSync(state.directory, { recursive: true, force: true });
18027
+ if (state) fs48.rmSync(state.directory, { recursive: true, force: true });
17999
18028
  const reason = error instanceof Error ? error.message : String(error);
18000
18029
  appendAuthMessage(
18001
18030
  ctx,
@@ -18134,7 +18163,7 @@ var init_prepareCapabilityDelivery = __esm({
18134
18163
 
18135
18164
  // src/scripts/prepareSimpleCapabilityRuntime.ts
18136
18165
  import { isIP } from "net";
18137
- import * as path44 from "path";
18166
+ import * as path45 from "path";
18138
18167
  function requirementsFrom(ctx) {
18139
18168
  const raw = ctx.data.capabilityRequirements;
18140
18169
  return raw && typeof raw === "object" && !Array.isArray(raw) ? raw : {};
@@ -18178,7 +18207,7 @@ function browserRuntime(ctx, requirements) {
18178
18207
  "--allowed-origins",
18179
18208
  origin,
18180
18209
  "--output-dir",
18181
- path44.resolve(ctx.cwd, "test-results", "quality-runs", qualityRunId)
18210
+ path45.resolve(ctx.cwd, "test-results", "quality-runs", qualityRunId)
18182
18211
  ]
18183
18212
  };
18184
18213
  }
@@ -18543,9 +18572,9 @@ function latestResult(raw, agentResult) {
18543
18572
  function recordField4(value) {
18544
18573
  return value && typeof value === "object" && !Array.isArray(value) ? value : null;
18545
18574
  }
18546
- function resolveDotted(root, path55) {
18547
- if (!path55) return void 0;
18548
- return path55.split(".").reduce((value, key) => recordField4(value)?.[key], root);
18575
+ function resolveDotted(root, path58) {
18576
+ if (!path58) return void 0;
18577
+ return path58.split(".").reduce((value, key) => recordField4(value)?.[key], root);
18549
18578
  }
18550
18579
  function stringValue5(value) {
18551
18580
  return typeof value === "string" && value.trim() ? value.trim() : null;
@@ -19387,7 +19416,7 @@ var init_previewBuildHelpers = __esm({
19387
19416
  // src/scripts/previewBuildRun.ts
19388
19417
  import { spawn as spawn5 } from "child_process";
19389
19418
  async function runCmd(cmd, args, opts = {}) {
19390
- await new Promise((resolve21, reject) => {
19419
+ await new Promise((resolve23, reject) => {
19391
19420
  const child = spawn5(cmd, args, {
19392
19421
  cwd: opts.cwd,
19393
19422
  env: { ...process.env, ...opts.env ?? {} },
@@ -19399,7 +19428,7 @@ async function runCmd(cmd, args, opts = {}) {
19399
19428
  }
19400
19429
  child.on("error", reject);
19401
19430
  child.on("close", (code) => {
19402
- if (code === 0) resolve21();
19431
+ if (code === 0) resolve23();
19403
19432
  else reject(new Error(`${cmd} ${args.join(" ")} exited ${code}`));
19404
19433
  });
19405
19434
  });
@@ -19471,12 +19500,12 @@ fi
19471
19500
 
19472
19501
  // src/scripts/runPreviewBuild.ts
19473
19502
  import { copyFile, writeFile } from "fs/promises";
19474
- import * as path45 from "path";
19503
+ import * as path46 from "path";
19475
19504
  import { fileURLToPath as fileURLToPath2 } from "url";
19476
19505
  function bundledDockerfilePath(mode) {
19477
- const here = path45.dirname(fileURLToPath2(import.meta.url));
19506
+ const here = path46.dirname(fileURLToPath2(import.meta.url));
19478
19507
  const file = mode === "dev" ? "default-Dockerfile.preview.dev" : "default-Dockerfile.preview.prod";
19479
- return path45.join(here, "preview-build-templates", file);
19508
+ return path46.join(here, "preview-build-templates", file);
19480
19509
  }
19481
19510
  function required(name) {
19482
19511
  const v = (process.env[name] ?? "").trim();
@@ -19711,10 +19740,10 @@ var init_runPreviewBuild = __esm({
19711
19740
  console.log(`[preview-build] vault: ${Object.keys(buildEnv).length} secrets, mode=${buildMode}`);
19712
19741
  if (Object.keys(buildEnv).length > 0) {
19713
19742
  const lines = Object.entries(buildEnv).map(([k, v]) => `${k}=${JSON.stringify(v)}`);
19714
- await writeFile(path45.join(ctx.cwd, ".env.production.local"), `${lines.join("\n")}
19743
+ await writeFile(path46.join(ctx.cwd, ".env.production.local"), `${lines.join("\n")}
19715
19744
  `, "utf8");
19716
19745
  }
19717
- const consumerDockerfile = path45.join(ctx.cwd, "Dockerfile.preview");
19746
+ const consumerDockerfile = path46.join(ctx.cwd, "Dockerfile.preview");
19718
19747
  const { stat } = await import("fs/promises");
19719
19748
  let hasConsumerDockerfile = false;
19720
19749
  try {
@@ -19898,8 +19927,8 @@ var init_tickShellRunner = __esm({
19898
19927
  });
19899
19928
 
19900
19929
  // src/scripts/runScheduledImplementationTick.ts
19901
- import * as fs48 from "fs";
19902
- import * as path46 from "path";
19930
+ import * as fs49 from "fs";
19931
+ import * as path47 from "path";
19903
19932
  var runScheduledImplementationTick;
19904
19933
  var init_runScheduledImplementationTick = __esm({
19905
19934
  "src/scripts/runScheduledImplementationTick.ts"() {
@@ -19920,14 +19949,14 @@ var init_runScheduledImplementationTick = __esm({
19920
19949
  ctx.output.reason = `runScheduledImplementationTick: args.slug or ctx.args.${slugArg} must be non-empty capability slug`;
19921
19950
  return;
19922
19951
  }
19923
- const capability = resolveCapabilityFolder(slug, path46.resolve(ctx.cwd, jobsDir));
19952
+ const capability = resolveCapabilityFolder(slug, path47.resolve(ctx.cwd, jobsDir));
19924
19953
  if (!capability) {
19925
19954
  ctx.output.exitCode = 99;
19926
19955
  ctx.output.reason = `runScheduledImplementationTick: capability folder not found or incomplete: ${slug} (searched ${jobsDir} and company store)`;
19927
19956
  return;
19928
19957
  }
19929
- const shellPath = path46.join(profile.dir, shell);
19930
- if (!fs48.existsSync(shellPath)) {
19958
+ const shellPath = path47.join(profile.dir, shell);
19959
+ if (!fs49.existsSync(shellPath)) {
19931
19960
  ctx.output.exitCode = 99;
19932
19961
  ctx.output.reason = `runScheduledImplementationTick: shell not found: ${shell} (looked in ${profile.dir})`;
19933
19962
  return;
@@ -19959,13 +19988,13 @@ var init_runScheduledImplementationTick = __esm({
19959
19988
 
19960
19989
  // src/scripts/runSimpleCapabilityScript.ts
19961
19990
  import { spawnSync as spawnSync3 } from "child_process";
19962
- import * as fs49 from "fs";
19991
+ import * as fs50 from "fs";
19963
19992
  function formatDuration2(timeoutMs) {
19964
19993
  return timeoutMs % 6e4 === 0 ? `${timeoutMs / 6e4} minutes` : `${timeoutMs}ms`;
19965
19994
  }
19966
19995
  function isRegularFile2(filePath) {
19967
19996
  try {
19968
- const stat = fs49.lstatSync(filePath);
19997
+ const stat = fs50.lstatSync(filePath);
19969
19998
  return stat.isFile() && !stat.isSymbolicLink();
19970
19999
  } catch {
19971
20000
  return false;
@@ -20044,8 +20073,8 @@ var init_runSimpleCapabilityScript = __esm({
20044
20073
  });
20045
20074
 
20046
20075
  // src/scripts/runTickScript.ts
20047
- import * as fs50 from "fs";
20048
- import * as path47 from "path";
20076
+ import * as fs51 from "fs";
20077
+ import * as path48 from "path";
20049
20078
  var runTickScript;
20050
20079
  var init_runTickScript = __esm({
20051
20080
  "src/scripts/runTickScript.ts"() {
@@ -20065,10 +20094,10 @@ var init_runTickScript = __esm({
20065
20094
  ctx.output.reason = `runTickScript: ctx.args.${slugArg} must be a non-empty slug`;
20066
20095
  return;
20067
20096
  }
20068
- const capability = readCapabilityFolder(path47.resolve(ctx.cwd, jobsDir), slug);
20097
+ const capability = readCapabilityFolder(path48.resolve(ctx.cwd, jobsDir), slug);
20069
20098
  if (!capability) {
20070
20099
  ctx.output.exitCode = 99;
20071
- ctx.output.reason = `runTickScript: capability folder not found or incomplete: ${path47.resolve(ctx.cwd, jobsDir, slug)}`;
20100
+ ctx.output.reason = `runTickScript: capability folder not found or incomplete: ${path48.resolve(ctx.cwd, jobsDir, slug)}`;
20072
20101
  return;
20073
20102
  }
20074
20103
  const tickScript = capability.config.tickScript;
@@ -20077,8 +20106,8 @@ var init_runTickScript = __esm({
20077
20106
  ctx.output.reason = `runTickScript: capability ${slug} has no \`tickScript\` in profile.json \u2014 route via capability-tick instead`;
20078
20107
  return;
20079
20108
  }
20080
- const scriptPath = path47.isAbsolute(tickScript) ? tickScript : path47.join(ctx.cwd, tickScript);
20081
- if (!fs50.existsSync(scriptPath)) {
20109
+ const scriptPath = path48.isAbsolute(tickScript) ? tickScript : path48.join(ctx.cwd, tickScript);
20110
+ if (!fs51.existsSync(scriptPath)) {
20082
20111
  ctx.output.exitCode = 99;
20083
20112
  ctx.output.reason = `runTickScript: tickScript not found: ${scriptPath}`;
20084
20113
  return;
@@ -20360,7 +20389,7 @@ var init_syncFlow = __esm({
20360
20389
  });
20361
20390
 
20362
20391
  // src/scripts/validateAgencyModelProposal.ts
20363
- import * as path48 from "path";
20392
+ import * as path49 from "path";
20364
20393
  function validateModelBundle(bundle, expectedKind, options = {}) {
20365
20394
  const failures = [];
20366
20395
  validateOneModel(bundle.model, bundle.files, "model", true, failures, expectedKind, options);
@@ -20678,7 +20707,7 @@ var init_validateAgencyModelProposal = __esm({
20678
20707
  const bundle = parseAgencyModelProposal(raw);
20679
20708
  const expectedKind = readExpectedModelKind(args);
20680
20709
  const failures = validateModelBundle(bundle, expectedKind, {
20681
- capabilityRoot: path48.join(ctx.cwd, ".kody", "capabilities")
20710
+ capabilityRoot: path49.join(ctx.cwd, ".kody", "capabilities")
20682
20711
  });
20683
20712
  if (failures.length > 0) {
20684
20713
  throw new Error(`validateAgencyModelProposal: ${failures.join("; ")}`);
@@ -20741,7 +20770,7 @@ function stripAnsi2(s) {
20741
20770
  return s.replace(ANSI_RE2, "");
20742
20771
  }
20743
20772
  function runCommand2(command, cwd) {
20744
- return new Promise((resolve21) => {
20773
+ return new Promise((resolve23) => {
20745
20774
  const child = spawn6(command, {
20746
20775
  cwd,
20747
20776
  shell: true,
@@ -20768,11 +20797,11 @@ function runCommand2(command, cwd) {
20768
20797
  }, TEST_TIMEOUT_MS);
20769
20798
  child.on("exit", (code) => {
20770
20799
  clearTimeout(timer);
20771
- resolve21({ exitCode: code ?? -1, output: Buffer.concat(buffers).toString("utf-8") });
20800
+ resolve23({ exitCode: code ?? -1, output: Buffer.concat(buffers).toString("utf-8") });
20772
20801
  });
20773
20802
  child.on("error", (err) => {
20774
20803
  clearTimeout(timer);
20775
- resolve21({ exitCode: -1, output: err.message });
20804
+ resolve23({ exitCode: -1, output: err.message });
20776
20805
  });
20777
20806
  });
20778
20807
  }
@@ -21178,21 +21207,21 @@ function lineStream(stream) {
21178
21207
  tryDeliver();
21179
21208
  });
21180
21209
  return {
21181
- next: (timeoutMs) => new Promise((resolve21) => {
21210
+ next: (timeoutMs) => new Promise((resolve23) => {
21182
21211
  if (queue.length > 0) {
21183
- resolve21(queue.shift());
21212
+ resolve23(queue.shift());
21184
21213
  return;
21185
21214
  }
21186
21215
  if (ended) {
21187
- resolve21(null);
21216
+ resolve23(null);
21188
21217
  return;
21189
21218
  }
21190
- waiter = resolve21;
21219
+ waiter = resolve23;
21191
21220
  const t = setTimeout(
21192
21221
  () => {
21193
- if (waiter === resolve21) {
21222
+ if (waiter === resolve23) {
21194
21223
  waiter = null;
21195
- resolve21(null);
21224
+ resolve23(null);
21196
21225
  }
21197
21226
  },
21198
21227
  Math.max(0, timeoutMs)
@@ -21229,7 +21258,7 @@ var init_warmupMcp = __esm({
21229
21258
  });
21230
21259
 
21231
21260
  // src/scripts/writeAgentRunSummary.ts
21232
- import * as fs51 from "fs";
21261
+ import * as fs52 from "fs";
21233
21262
  var writeAgentRunSummary;
21234
21263
  var init_writeAgentRunSummary = __esm({
21235
21264
  "src/scripts/writeAgentRunSummary.ts"() {
@@ -21255,7 +21284,7 @@ var init_writeAgentRunSummary = __esm({
21255
21284
  if (reason) lines.push(`- **Reason:** ${reason}`);
21256
21285
  lines.push("");
21257
21286
  try {
21258
- fs51.appendFileSync(summaryPath, `${lines.join("\n")}
21287
+ fs52.appendFileSync(summaryPath, `${lines.join("\n")}
21259
21288
  `);
21260
21289
  } catch {
21261
21290
  }
@@ -21593,17 +21622,17 @@ var init_scripts = __esm({
21593
21622
  });
21594
21623
 
21595
21624
  // src/stateWorkspace.ts
21596
- import * as fs52 from "fs";
21597
- import * as path49 from "path";
21625
+ import * as fs53 from "fs";
21626
+ import * as path50 from "path";
21598
21627
  function tenantId(config) {
21599
21628
  const owner = config.github?.owner?.trim() || process.env.GITHUB_REPOSITORY?.split("/")[0]?.trim();
21600
21629
  const repo = config.github?.repo?.trim() || process.env.GITHUB_REPOSITORY?.split("/")[1]?.trim();
21601
21630
  return owner && repo ? `${owner}/${repo}` : null;
21602
21631
  }
21603
21632
  function writeRuntimeFile(cwd, relativePath, content) {
21604
- const target = path49.join(cwd, RUNTIME_ROOT, relativePath);
21605
- fs52.mkdirSync(path49.dirname(target), { recursive: true });
21606
- fs52.writeFileSync(target, content, "utf8");
21633
+ const target = path50.join(cwd, RUNTIME_ROOT, relativePath);
21634
+ fs53.mkdirSync(path50.dirname(target), { recursive: true });
21635
+ fs53.writeFileSync(target, content, "utf8");
21607
21636
  }
21608
21637
  function record(value) {
21609
21638
  return value && typeof value === "object" && !Array.isArray(value) ? value : null;
@@ -21668,11 +21697,11 @@ async function hydrateStateWorkspace(config, cwd, backendOverride) {
21668
21697
  throw new Error("Kody backend access is required for runtime workspace documents");
21669
21698
  return;
21670
21699
  }
21671
- const key = `${path49.resolve(cwd)}|${tenant}`;
21700
+ const key = `${path50.resolve(cwd)}|${tenant}`;
21672
21701
  if (hydratedWorkspaces.has(key)) return;
21673
21702
  const backend = backendOverride ?? createStateBackendFromEnv();
21674
- const root = path49.join(cwd, RUNTIME_ROOT);
21675
- fs52.rmSync(root, { recursive: true, force: true });
21703
+ const root = path50.join(cwd, RUNTIME_ROOT);
21704
+ fs53.rmSync(root, { recursive: true, force: true });
21676
21705
  await Promise.all([
21677
21706
  hydratePrefix(backend, tenant, cwd, "context:"),
21678
21707
  hydratePrefix(backend, tenant, cwd, "memory:"),
@@ -21688,7 +21717,7 @@ var init_stateWorkspace = __esm({
21688
21717
  "src/stateWorkspace.ts"() {
21689
21718
  "use strict";
21690
21719
  init_state_backend();
21691
- RUNTIME_ROOT = path49.join(".kody-engine", "runtime");
21720
+ RUNTIME_ROOT = path50.join(".kody-engine", "runtime");
21692
21721
  hydratedWorkspaces = /* @__PURE__ */ new Set();
21693
21722
  }
21694
21723
  });
@@ -21759,9 +21788,9 @@ var init_tools = __esm({
21759
21788
 
21760
21789
  // src/executor.ts
21761
21790
  import { spawn as spawn8 } from "child_process";
21762
- import * as fs53 from "fs";
21791
+ import * as fs54 from "fs";
21763
21792
  import * as os8 from "os";
21764
- import * as path50 from "path";
21793
+ import * as path51 from "path";
21765
21794
  function isMutatingPostflight(scriptName) {
21766
21795
  return MUTATING_POSTFLIGHTS.has(scriptName ?? "");
21767
21796
  }
@@ -22013,7 +22042,7 @@ async function runImplementation(profileName, input) {
22013
22042
  const jobWhyBlock = typeof ctx.data.jobWhy === "string" ? operatorRequestBlock(ctx.data.jobWhy) : null;
22014
22043
  const jobRefBlock = jobReferenceBlock(profileName, profile, ctx.data);
22015
22044
  const invokeAgent = async (prompt) => {
22016
- const externalPlugins = (profile.claudeCode.plugins ?? []).map((p) => path50.isAbsolute(p) ? p : path50.resolve(profile.dir, p)).filter((p) => p.length > 0);
22045
+ const externalPlugins = (profile.claudeCode.plugins ?? []).map((p) => path51.isAbsolute(p) ? p : path51.resolve(profile.dir, p)).filter((p) => p.length > 0);
22017
22046
  const syntheticPath = ctx.data.syntheticPluginPath;
22018
22047
  const pluginPaths = [...externalPlugins, ...syntheticPath ? [syntheticPath] : []];
22019
22048
  const agents = loadSubagents(profile);
@@ -22493,17 +22522,17 @@ function clearStampedLifecycleLabels(profile, ctx) {
22493
22522
  function resolveProfilePath(profileName, cwd = process.cwd()) {
22494
22523
  const found = resolveImplementation(profileName, getRuntimeProfileRootsForCwd(cwd));
22495
22524
  if (found) return found;
22496
- const here = path50.dirname(new URL(import.meta.url).pathname);
22525
+ const here = path51.dirname(new URL(import.meta.url).pathname);
22497
22526
  const candidates = [
22498
- path50.join(here, "implementations", profileName, "profile.json"),
22527
+ path51.join(here, "implementations", profileName, "profile.json"),
22499
22528
  // same-dir sibling (dev)
22500
- path50.join(here, "..", "implementations", profileName, "profile.json"),
22529
+ path51.join(here, "..", "implementations", profileName, "profile.json"),
22501
22530
  // up one (prod: dist/bin → dist/implementations)
22502
- path50.join(here, "..", "src", "implementations", profileName, "profile.json")
22531
+ path51.join(here, "..", "src", "implementations", profileName, "profile.json")
22503
22532
  // fallback
22504
22533
  ];
22505
22534
  for (const c of candidates) {
22506
- if (fs53.existsSync(c)) return c;
22535
+ if (fs54.existsSync(c)) return c;
22507
22536
  }
22508
22537
  return candidates[0];
22509
22538
  }
@@ -22618,15 +22647,15 @@ function resolveShellTimeoutMs(entry) {
22618
22647
  }
22619
22648
  async function runShellEntry(entry, ctx, profile) {
22620
22649
  const shellName = entry.shell;
22621
- const shellPath = path50.join(profile.dir, shellName);
22622
- if (!fs53.existsSync(shellPath)) {
22650
+ const shellPath = path51.join(profile.dir, shellName);
22651
+ if (!fs54.existsSync(shellPath)) {
22623
22652
  ctx.skipAgent = true;
22624
22653
  ctx.output.exitCode = 99;
22625
22654
  ctx.output.reason = `shell script not found: ${shellName} (looked in ${profile.dir})`;
22626
22655
  return;
22627
22656
  }
22628
22657
  const positional = entry.with ? Object.values(entry.with).map((v) => String(v)) : [];
22629
- const outputFile = path50.join(
22658
+ const outputFile = path51.join(
22630
22659
  os8.tmpdir(),
22631
22660
  `kody-shell-output-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`
22632
22661
  );
@@ -22656,14 +22685,14 @@ async function runShellEntry(entry, ctx, profile) {
22656
22685
  let killTimer;
22657
22686
  let escalateTimer;
22658
22687
  const result = await new Promise(
22659
- (resolve21) => {
22688
+ (resolve23) => {
22660
22689
  let settled = false;
22661
22690
  const settle = (code, signal, spawnErr) => {
22662
22691
  if (settled) return;
22663
22692
  settled = true;
22664
22693
  if (killTimer) clearTimeout(killTimer);
22665
22694
  if (escalateTimer) clearTimeout(escalateTimer);
22666
- resolve21({ code, signal, spawnErr });
22695
+ resolve23({ code, signal, spawnErr });
22667
22696
  };
22668
22697
  child.on("error", (err) => settle(null, null, err));
22669
22698
  child.on("close", (code, signal) => settle(code, signal));
@@ -22693,9 +22722,9 @@ async function runShellEntry(entry, ctx, profile) {
22693
22722
  }
22694
22723
  let sideChannelText = "";
22695
22724
  try {
22696
- if (fs53.existsSync(outputFile)) {
22697
- sideChannelText = fs53.readFileSync(outputFile, "utf-8");
22698
- fs53.rmSync(outputFile, { force: true });
22725
+ if (fs54.existsSync(outputFile)) {
22726
+ sideChannelText = fs54.readFileSync(outputFile, "utf-8");
22727
+ fs54.rmSync(outputFile, { force: true });
22699
22728
  }
22700
22729
  } catch {
22701
22730
  }
@@ -23699,11 +23728,11 @@ function exhaustedWorkflowTransitions(step, data, counts) {
23699
23728
  }
23700
23729
  function workflowResultConditionPaths(transitions) {
23701
23730
  return transitions.flatMap(
23702
- (transition) => Object.keys(transition.when ?? {}).filter((path55) => path55.startsWith("result."))
23731
+ (transition) => Object.keys(transition.when ?? {}).filter((path58) => path58.startsWith("result."))
23703
23732
  );
23704
23733
  }
23705
23734
  function conditionMatches(condition, context) {
23706
- return Object.entries(condition).every(([path55, expected]) => valueMatches(resolveDottedPath2(context, path55), expected));
23735
+ return Object.entries(condition).every(([path58, expected]) => valueMatches(resolveDottedPath2(context, path58), expected));
23707
23736
  }
23708
23737
  function withWorkflowBoundaryEval(capability, result) {
23709
23738
  const capabilityKind = capability.config.capabilityKind;
@@ -24133,7 +24162,7 @@ function translateOpenAISseToBrain(opts) {
24133
24162
 
24134
24163
  // src/servers/brain-serve.ts
24135
24164
  import { createServer as createServer2 } from "http";
24136
- import * as path53 from "path";
24165
+ import * as path54 from "path";
24137
24166
 
24138
24167
  // src/chat/loop.ts
24139
24168
  init_agent();
@@ -24141,13 +24170,13 @@ init_agents();
24141
24170
  init_config();
24142
24171
  init_registry();
24143
24172
  init_task_artifacts();
24144
- import * as fs17 from "fs";
24145
- import * as path18 from "path";
24173
+ import * as fs18 from "fs";
24174
+ import * as path19 from "path";
24146
24175
 
24147
24176
  // src/chat/attachments.ts
24148
24177
  init_runtimePaths();
24149
- import * as fs14 from "fs";
24150
- import * as path15 from "path";
24178
+ import * as fs15 from "fs";
24179
+ import * as path16 from "path";
24151
24180
  var INLINE_ATTACHMENT_RE = /(?:\[(?:Image|File): ([^\]]*)\]\n)?data:([\w.+-]+\/[\w.+-]+);base64,([A-Za-z0-9+/=]+)/g;
24152
24181
  var EXT_BY_MIME = {
24153
24182
  "image/png": "png",
@@ -24180,11 +24209,11 @@ function prepareAttachments(turns, cwd, sessionId) {
24180
24209
  if (!isImage) return `[File: ${name}]`;
24181
24210
  try {
24182
24211
  if (!dirEnsured) {
24183
- fs14.mkdirSync(dir, { recursive: true });
24212
+ fs15.mkdirSync(dir, { recursive: true });
24184
24213
  dirEnsured = true;
24185
24214
  }
24186
- const filePath = path15.join(dir, `${imageCounter}.${extFor(mime)}`);
24187
- fs14.writeFileSync(filePath, Buffer.from(data, "base64"));
24215
+ const filePath = path16.join(dir, `${imageCounter}.${extFor(mime)}`);
24216
+ fs15.writeFileSync(filePath, Buffer.from(data, "base64"));
24188
24217
  imageCounter += 1;
24189
24218
  imagePaths.push(filePath);
24190
24219
  return `[Image "${name}" is attached \u2014 saved to ${filePath}. Use the Read tool on that exact path to view it.]`;
@@ -24201,8 +24230,8 @@ function prepareAttachments(turns, cwd, sessionId) {
24201
24230
 
24202
24231
  // src/chat/codex-app-server.ts
24203
24232
  import { spawn as spawn3 } from "child_process";
24204
- import * as fs15 from "fs";
24205
- import * as path16 from "path";
24233
+ import * as fs16 from "fs";
24234
+ import * as path17 from "path";
24206
24235
  import { createInterface } from "readline";
24207
24236
  function codexThreadStartParams(args) {
24208
24237
  return {
@@ -24287,9 +24316,9 @@ var CodexAppServerClient = class {
24287
24316
  await this.request("thread/resume", { threadId });
24288
24317
  }
24289
24318
  async runTurn(args) {
24290
- await new Promise((resolve21, reject) => {
24319
+ await new Promise((resolve23, reject) => {
24291
24320
  this.process.turnWaiters.set(args.threadId, {
24292
- resolve: resolve21,
24321
+ resolve: resolve23,
24293
24322
  reject,
24294
24323
  onNotification: args.onNotification,
24295
24324
  queue: Promise.resolve()
@@ -24306,8 +24335,8 @@ var CodexAppServerClient = class {
24306
24335
  }
24307
24336
  request(method, params) {
24308
24337
  const id = this.process.nextId++;
24309
- return new Promise((resolve21, reject) => {
24310
- this.process.pending.set(id, { resolve: resolve21, reject });
24338
+ return new Promise((resolve23, reject) => {
24339
+ this.process.pending.set(id, { resolve: resolve23, reject });
24311
24340
  this.process.child.stdin.write(`${JSON.stringify({ method, id, params })}
24312
24341
  `);
24313
24342
  });
@@ -24373,11 +24402,11 @@ var CodexAppServerClient = class {
24373
24402
  };
24374
24403
  var clients = /* @__PURE__ */ new Map();
24375
24404
  function threadMapPath(cwd) {
24376
- return path16.join(cwd, ".kody-engine", "runtime", "codex-threads.json");
24405
+ return path17.join(cwd, ".kody-engine", "runtime", "codex-threads.json");
24377
24406
  }
24378
24407
  function readThreadMap(cwd) {
24379
24408
  try {
24380
- const value = JSON.parse(fs15.readFileSync(threadMapPath(cwd), "utf8"));
24409
+ const value = JSON.parse(fs16.readFileSync(threadMapPath(cwd), "utf8"));
24381
24410
  if (!value || typeof value !== "object" || Array.isArray(value)) return {};
24382
24411
  return Object.fromEntries(
24383
24412
  Object.entries(value).filter(
@@ -24390,8 +24419,8 @@ function readThreadMap(cwd) {
24390
24419
  }
24391
24420
  function writeThreadMap(cwd, map) {
24392
24421
  const file = threadMapPath(cwd);
24393
- fs15.mkdirSync(path16.dirname(file), { recursive: true });
24394
- fs15.writeFileSync(file, `${JSON.stringify(map, null, 2)}
24422
+ fs16.mkdirSync(path17.dirname(file), { recursive: true });
24423
+ fs16.writeFileSync(file, `${JSON.stringify(map, null, 2)}
24395
24424
  `);
24396
24425
  }
24397
24426
  async function runCodexChatTurn(args) {
@@ -24481,8 +24510,8 @@ async function runCodexChatTurn(args) {
24481
24510
  }
24482
24511
 
24483
24512
  // src/chat/events.ts
24484
- import * as fs16 from "fs";
24485
- import * as path17 from "path";
24513
+ import * as fs17 from "fs";
24514
+ import * as path18 from "path";
24486
24515
  import posixPath2 from "path/posix";
24487
24516
  var BackendEventSink = class {
24488
24517
  constructor(append, tenantId2, sessionId) {
@@ -24498,7 +24527,7 @@ var BackendEventSink = class {
24498
24527
  }
24499
24528
  };
24500
24529
  function eventsFilePath(cwd, sessionId) {
24501
- return path17.join(cwd, ".kody-engine", "runtime", "events", `${sessionId}.jsonl`);
24530
+ return path18.join(cwd, ".kody-engine", "runtime", "events", `${sessionId}.jsonl`);
24502
24531
  }
24503
24532
  var FileSink = class {
24504
24533
  constructor(file) {
@@ -24506,8 +24535,8 @@ var FileSink = class {
24506
24535
  }
24507
24536
  file;
24508
24537
  async emit(event) {
24509
- fs16.mkdirSync(path17.dirname(this.file), { recursive: true });
24510
- fs16.appendFileSync(this.file, `${JSON.stringify(event)}
24538
+ fs17.mkdirSync(path18.dirname(this.file), { recursive: true });
24539
+ fs17.appendFileSync(this.file, `${JSON.stringify(event)}
24511
24540
  `);
24512
24541
  }
24513
24542
  };
@@ -24772,7 +24801,7 @@ function buildImplementationCatalog() {
24772
24801
  const entries = [];
24773
24802
  for (const { name, profilePath } of discovered) {
24774
24803
  try {
24775
- const raw = JSON.parse(fs17.readFileSync(profilePath, "utf-8"));
24804
+ const raw = JSON.parse(fs18.readFileSync(profilePath, "utf-8"));
24776
24805
  const describe = typeof raw.describe === "string" ? raw.describe : "";
24777
24806
  const firstSentence = describe.split(/(?<=[.!?])\s+/, 1)[0] ?? "";
24778
24807
  entries.push({ name, describe: firstSentence.trim() });
@@ -24894,7 +24923,7 @@ async function runChatTurn(opts) {
24894
24923
  quiet: opts.quiet,
24895
24924
  additionalDirectories: [
24896
24925
  taskArtifactsPaths.absDir,
24897
- ...Array.from(new Set(imagePaths.map((p2) => path18.dirname(p2))))
24926
+ ...Array.from(new Set(imagePaths.map((p2) => path19.dirname(p2))))
24898
24927
  ],
24899
24928
  systemPromptAppend: systemPrompt,
24900
24929
  ...opts.reasoningEffort ? { reasoningEffort: opts.reasoningEffort } : {},
@@ -25082,10 +25111,10 @@ async function emit(sink, type, sessionId, suffix, payload) {
25082
25111
  var MEMORY_INDEX_REL = ".kody-engine/runtime/memory/INDEX.md";
25083
25112
  var MAX_INDEX_BYTES = 8e3;
25084
25113
  function readMemoryIndexBlock(cwd) {
25085
- const indexPath = path18.join(cwd, MEMORY_INDEX_REL);
25114
+ const indexPath = path19.join(cwd, MEMORY_INDEX_REL);
25086
25115
  let raw;
25087
25116
  try {
25088
- raw = fs17.readFileSync(indexPath, "utf-8");
25117
+ raw = fs18.readFileSync(indexPath, "utf-8");
25089
25118
  } catch {
25090
25119
  return "";
25091
25120
  }
@@ -25105,17 +25134,17 @@ _\u2026 (memory index truncated; use recall_search to read more)_` : trimmed;
25105
25134
  var CONTEXT_DIR_REL = ".kody-engine/runtime/context";
25106
25135
  var MAX_CONTEXT_BYTES = 12e3;
25107
25136
  function readContextBlock(cwd) {
25108
- const dir = path18.join(cwd, CONTEXT_DIR_REL);
25137
+ const dir = path19.join(cwd, CONTEXT_DIR_REL);
25109
25138
  let files;
25110
25139
  try {
25111
- files = fs17.readdirSync(dir).filter((f) => f.endsWith(".md")).sort();
25140
+ files = fs18.readdirSync(dir).filter((f) => f.endsWith(".md")).sort();
25112
25141
  } catch {
25113
25142
  return "";
25114
25143
  }
25115
25144
  const sections = [];
25116
25145
  for (const file of files) {
25117
25146
  try {
25118
- const content = fs17.readFileSync(path18.join(dir, file), "utf-8").trim();
25147
+ const content = fs18.readFileSync(path19.join(dir, file), "utf-8").trim();
25119
25148
  if (content) sections.push(`### ${file.replace(/\.md$/, "")}
25120
25149
 
25121
25150
  ${content}`);
@@ -25141,7 +25170,7 @@ var SYSTEM_PROMPT_OVERRIDE_REL = ".kody-engine/runtime/system-prompt.md";
25141
25170
  function readSystemPromptOverride(cwd) {
25142
25171
  let raw;
25143
25172
  try {
25144
- raw = fs17.readFileSync(path18.join(cwd, SYSTEM_PROMPT_OVERRIDE_REL), "utf-8");
25173
+ raw = fs18.readFileSync(path19.join(cwd, SYSTEM_PROMPT_OVERRIDE_REL), "utf-8");
25145
25174
  } catch {
25146
25175
  return null;
25147
25176
  }
@@ -25149,10 +25178,10 @@ function readSystemPromptOverride(cwd) {
25149
25178
  return trimmed.length > 0 ? trimmed : null;
25150
25179
  }
25151
25180
  function readInstructionsBlock(cwd) {
25152
- const instructionsPath = path18.join(cwd, INSTRUCTIONS_REL);
25181
+ const instructionsPath = path19.join(cwd, INSTRUCTIONS_REL);
25153
25182
  let raw;
25154
25183
  try {
25155
- raw = fs17.readFileSync(instructionsPath, "utf-8");
25184
+ raw = fs18.readFileSync(instructionsPath, "utf-8");
25156
25185
  } catch {
25157
25186
  return "";
25158
25187
  }
@@ -25186,15 +25215,15 @@ function resolveBrainDriver(runtime) {
25186
25215
  }
25187
25216
 
25188
25217
  // src/chat/session.ts
25189
- import * as fs18 from "fs";
25190
- import * as path19 from "path";
25218
+ import * as fs19 from "fs";
25219
+ import * as path20 from "path";
25191
25220
  import posixPath3 from "path/posix";
25192
25221
  function sessionFilePath(cwd, sessionId) {
25193
- return path19.join(cwd, ".kody-engine", "runtime", "sessions", `${sessionId}.jsonl`);
25222
+ return path20.join(cwd, ".kody-engine", "runtime", "sessions", `${sessionId}.jsonl`);
25194
25223
  }
25195
25224
  function readSession(file) {
25196
- if (!fs18.existsSync(file)) return [];
25197
- const raw = fs18.readFileSync(file, "utf-8").trim();
25225
+ if (!fs19.existsSync(file)) return [];
25226
+ const raw = fs19.readFileSync(file, "utf-8").trim();
25198
25227
  if (!raw) return [];
25199
25228
  const turns = [];
25200
25229
  for (const line of raw.split("\n")) {
@@ -25217,8 +25246,8 @@ init_config();
25217
25246
  init_state_backend();
25218
25247
  init_workflowDefinitions();
25219
25248
  import { createHash as createHash2 } from "crypto";
25220
- import * as fs20 from "fs";
25221
- import * as path21 from "path";
25249
+ import * as fs21 from "fs";
25250
+ import * as path22 from "path";
25222
25251
  var SLUG_RE = /^[a-z0-9][a-z0-9_-]{0,127}$/;
25223
25252
  var REPOSITORY_OWNED_NAMESPACES = ["loops"];
25224
25253
  function assertSafeDefinitionPath(filePath) {
@@ -25250,9 +25279,9 @@ function verifyDefinition(definition) {
25250
25279
  }
25251
25280
  function writeBundle(root, bundle) {
25252
25281
  for (const [filePath, contents] of Object.entries(bundle.files)) {
25253
- const target = path21.join(root, filePath);
25254
- fs20.mkdirSync(path21.dirname(target), { recursive: true });
25255
- fs20.writeFileSync(target, contents, "utf8");
25282
+ const target = path22.join(root, filePath);
25283
+ fs21.mkdirSync(path22.dirname(target), { recursive: true });
25284
+ fs21.writeFileSync(target, contents, "utf8");
25256
25285
  }
25257
25286
  }
25258
25287
  function writeDefinition(root, kind, definition) {
@@ -25260,22 +25289,22 @@ function writeDefinition(root, kind, definition) {
25260
25289
  if (kind === "agent") {
25261
25290
  const raw = bundle.files["agent.md"];
25262
25291
  if (typeof raw !== "string") throw new Error(`agent definition ${definition.slug} is missing agent.md`);
25263
- fs20.writeFileSync(path21.join(root, "agents", `${definition.slug}.md`), raw, "utf8");
25292
+ fs21.writeFileSync(path22.join(root, "agents", `${definition.slug}.md`), raw, "utf8");
25264
25293
  return;
25265
25294
  }
25266
25295
  if (kind === "goal") {
25267
- writeBundle(path21.join(root, "goals", definition.slug), bundle);
25296
+ writeBundle(path22.join(root, "goals", definition.slug), bundle);
25268
25297
  return;
25269
25298
  }
25270
25299
  if (kind === "implementation") {
25271
- writeBundle(path21.join(root, "implementations", definition.slug), bundle);
25300
+ writeBundle(path22.join(root, "implementations", definition.slug), bundle);
25272
25301
  return;
25273
25302
  }
25274
25303
  if (kind === "asset") {
25275
- writeBundle(path21.join(root, "shared"), bundle);
25304
+ writeBundle(path22.join(root, "shared"), bundle);
25276
25305
  return;
25277
25306
  }
25278
- writeBundle(path21.join(root, "capabilities", definition.slug), bundle);
25307
+ writeBundle(path22.join(root, "capabilities", definition.slug), bundle);
25279
25308
  }
25280
25309
  function writeWorkflow(root, document) {
25281
25310
  const workflow = normalizeWorkflowDefinition(document.definition);
@@ -25283,28 +25312,28 @@ function writeWorkflow(root, document) {
25283
25312
  const contents = `${JSON.stringify(workflow, null, 2)}
25284
25313
  `;
25285
25314
  const bundle = { schemaVersion: 1, files: { "workflow.json": contents } };
25286
- const target = path21.join(root, workflowDefinitionPath(document.workflowId));
25287
- fs20.mkdirSync(path21.dirname(target), { recursive: true });
25288
- fs20.writeFileSync(target, contents, "utf8");
25315
+ const target = path22.join(root, workflowDefinitionPath(document.workflowId));
25316
+ fs21.mkdirSync(path22.dirname(target), { recursive: true });
25317
+ fs21.writeFileSync(target, contents, "utf8");
25289
25318
  return definitionVersion(bundle);
25290
25319
  }
25291
25320
  function preserveRepositoryDefinitions(root, staging) {
25292
25321
  for (const namespace of REPOSITORY_OWNED_NAMESPACES) {
25293
- const source = path21.join(root, namespace);
25294
- if (!fs20.existsSync(source)) continue;
25295
- fs20.cpSync(source, path21.join(staging, namespace), { recursive: true });
25322
+ const source = path22.join(root, namespace);
25323
+ if (!fs21.existsSync(source)) continue;
25324
+ fs21.cpSync(source, path22.join(staging, namespace), { recursive: true });
25296
25325
  }
25297
25326
  }
25298
25327
  async function hydrateDefinitions(options) {
25299
- const root = path21.join(options.cwd, ".kody-engine", "definitions");
25328
+ const root = path22.join(options.cwd, ".kody-engine", "definitions");
25300
25329
  const staging = `${root}.tmp-${process.pid}-${Date.now()}`;
25301
- fs20.rmSync(staging, { recursive: true, force: true });
25302
- fs20.mkdirSync(path21.join(staging, "agents"), { recursive: true });
25303
- fs20.mkdirSync(path21.join(staging, "capabilities"), { recursive: true });
25304
- fs20.mkdirSync(path21.join(staging, "goals"), { recursive: true });
25305
- fs20.mkdirSync(path21.join(staging, "implementations"), { recursive: true });
25306
- fs20.mkdirSync(path21.join(staging, "shared"), { recursive: true });
25307
- fs20.mkdirSync(path21.join(staging, "workflows"), { recursive: true });
25330
+ fs21.rmSync(staging, { recursive: true, force: true });
25331
+ fs21.mkdirSync(path22.join(staging, "agents"), { recursive: true });
25332
+ fs21.mkdirSync(path22.join(staging, "capabilities"), { recursive: true });
25333
+ fs21.mkdirSync(path22.join(staging, "goals"), { recursive: true });
25334
+ fs21.mkdirSync(path22.join(staging, "implementations"), { recursive: true });
25335
+ fs21.mkdirSync(path22.join(staging, "shared"), { recursive: true });
25336
+ fs21.mkdirSync(path22.join(staging, "workflows"), { recursive: true });
25308
25337
  try {
25309
25338
  const [capabilities, agents, goals, implementations, assets, workflows] = await Promise.all([
25310
25339
  options.backend.listDefinitions(options.tenantId, "capability"),
@@ -25345,13 +25374,13 @@ async function hydrateDefinitions(options) {
25345
25374
  hydratedAt: (/* @__PURE__ */ new Date()).toISOString(),
25346
25375
  versions: Object.fromEntries(Object.entries(versions).sort(([left], [right]) => left.localeCompare(right)))
25347
25376
  };
25348
- fs20.writeFileSync(path21.join(staging, "manifest.json"), `${JSON.stringify(manifest, null, 2)}
25377
+ fs21.writeFileSync(path22.join(staging, "manifest.json"), `${JSON.stringify(manifest, null, 2)}
25349
25378
  `, "utf8");
25350
- fs20.rmSync(root, { recursive: true, force: true });
25351
- fs20.renameSync(staging, root);
25379
+ fs21.rmSync(root, { recursive: true, force: true });
25380
+ fs21.renameSync(staging, root);
25352
25381
  return { root, tenantId: options.tenantId, versions: manifest.versions };
25353
25382
  } catch (error) {
25354
- fs20.rmSync(staging, { recursive: true, force: true });
25383
+ fs21.rmSync(staging, { recursive: true, force: true });
25355
25384
  throw error;
25356
25385
  }
25357
25386
  }
@@ -25373,8 +25402,8 @@ async function hydrateDefinitionsFromEnv(cwd = process.cwd(), env = process.env)
25373
25402
 
25374
25403
  // src/kody-cli.ts
25375
25404
  import { execFileSync as execFileSync24 } from "child_process";
25376
- import * as fs54 from "fs";
25377
- import * as path51 from "path";
25405
+ import * as fs55 from "fs";
25406
+ import * as path52 from "path";
25378
25407
 
25379
25408
  // src/app-auth.ts
25380
25409
  import { createSign } from "crypto";
@@ -25503,7 +25532,7 @@ init_definition_paths();
25503
25532
 
25504
25533
  // src/dispatch.ts
25505
25534
  init_config();
25506
- import * as fs21 from "fs";
25535
+ import * as fs22 from "fs";
25507
25536
 
25508
25537
  // src/cron-match.ts
25509
25538
  var FIELD_BOUNDS = [
@@ -25610,10 +25639,10 @@ function autoDispatch(opts) {
25610
25639
  }
25611
25640
  const eventName = process.env.GITHUB_EVENT_NAME;
25612
25641
  const eventPath = process.env.GITHUB_EVENT_PATH;
25613
- if (!eventName || !eventPath || !fs21.existsSync(eventPath)) return null;
25642
+ if (!eventName || !eventPath || !fs22.existsSync(eventPath)) return null;
25614
25643
  let event = {};
25615
25644
  try {
25616
- event = JSON.parse(fs21.readFileSync(eventPath, "utf-8"));
25645
+ event = JSON.parse(fs22.readFileSync(eventPath, "utf-8"));
25617
25646
  } catch {
25618
25647
  return null;
25619
25648
  }
@@ -25737,7 +25766,7 @@ function autoDispatchTyped(opts) {
25737
25766
  if (legacy) return { kind: "route", ...legacy };
25738
25767
  const eventName = process.env.GITHUB_EVENT_NAME;
25739
25768
  const eventPath = process.env.GITHUB_EVENT_PATH;
25740
- if (!eventName || !eventPath || !fs21.existsSync(eventPath)) {
25769
+ if (!eventName || !eventPath || !fs22.existsSync(eventPath)) {
25741
25770
  return { kind: "silent", reason: "no GHA event context" };
25742
25771
  }
25743
25772
  if (eventName !== "issue_comment") {
@@ -25745,7 +25774,7 @@ function autoDispatchTyped(opts) {
25745
25774
  }
25746
25775
  let event = {};
25747
25776
  try {
25748
- event = JSON.parse(fs21.readFileSync(eventPath, "utf-8"));
25777
+ event = JSON.parse(fs22.readFileSync(eventPath, "utf-8"));
25749
25778
  } catch {
25750
25779
  return { kind: "silent", reason: "GHA event payload unreadable" };
25751
25780
  }
@@ -25799,7 +25828,7 @@ function dispatchScheduledWatches(opts) {
25799
25828
  for (const exe of listRuntimeProfilesForCwd(opts?.cwd ?? process.cwd())) {
25800
25829
  let raw;
25801
25830
  try {
25802
- raw = fs21.readFileSync(exe.profilePath, "utf-8");
25831
+ raw = fs22.readFileSync(exe.profilePath, "utf-8");
25803
25832
  } catch {
25804
25833
  continue;
25805
25834
  }
@@ -26176,9 +26205,9 @@ async function resolveAuthToken(env = process.env) {
26176
26205
  return void 0;
26177
26206
  }
26178
26207
  function detectPackageManager2(cwd) {
26179
- if (fs54.existsSync(path51.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
26180
- if (fs54.existsSync(path51.join(cwd, "yarn.lock"))) return "yarn";
26181
- if (fs54.existsSync(path51.join(cwd, "bun.lockb"))) return "bun";
26208
+ if (fs55.existsSync(path52.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
26209
+ if (fs55.existsSync(path52.join(cwd, "yarn.lock"))) return "yarn";
26210
+ if (fs55.existsSync(path52.join(cwd, "bun.lockb"))) return "bun";
26182
26211
  return "npm";
26183
26212
  }
26184
26213
  function shouldChainScheduledWatch(match) {
@@ -26281,8 +26310,8 @@ function postFailureTail(issueNumber, cwd, reason) {
26281
26310
  const logPath = lastRunLogPath(cwd);
26282
26311
  let tail = "";
26283
26312
  try {
26284
- if (fs54.existsSync(logPath)) {
26285
- const content = fs54.readFileSync(logPath, "utf-8");
26313
+ if (fs55.existsSync(logPath)) {
26314
+ const content = fs55.readFileSync(logPath, "utf-8");
26286
26315
  tail = content.slice(-3e3);
26287
26316
  }
26288
26317
  } catch {
@@ -26311,7 +26340,7 @@ async function runCi(argv) {
26311
26340
  return 0;
26312
26341
  }
26313
26342
  const args = parseCiArgs(argv);
26314
- const cwd = args.cwd ? path51.resolve(args.cwd) : process.cwd();
26343
+ const cwd = args.cwd ? path52.resolve(args.cwd) : process.cwd();
26315
26344
  try {
26316
26345
  const n = unpackAllSecrets();
26317
26346
  if (n > 0) process.stdout.write(`\u2192 kody: unpacked ${n} secret(s) from ALL_SECRETS
@@ -26377,9 +26406,9 @@ async function runCi(argv) {
26377
26406
  forceRunCliArgs = { goal: envForceMessage };
26378
26407
  }
26379
26408
  }
26380
- if (!args.issueNumber && !autoFallback && !forceRunAction && !runRequestFanOut && eventName === "workflow_dispatch" && dispatchEventPath && fs54.existsSync(dispatchEventPath)) {
26409
+ if (!args.issueNumber && !autoFallback && !forceRunAction && !runRequestFanOut && eventName === "workflow_dispatch" && dispatchEventPath && fs55.existsSync(dispatchEventPath)) {
26381
26410
  try {
26382
- const evt = JSON.parse(fs54.readFileSync(dispatchEventPath, "utf-8"));
26411
+ const evt = JSON.parse(fs55.readFileSync(dispatchEventPath, "utf-8"));
26383
26412
  const inputs = objectValue2(evt.inputs);
26384
26413
  const issueInput = parseInt(String(inputs?.issue_number ?? ""), 10);
26385
26414
  const sessionInput = String(inputs?.sessionId ?? "");
@@ -26794,8 +26823,8 @@ init_repoWorkspace();
26794
26823
 
26795
26824
  // src/scripts/brainTurnLog.ts
26796
26825
  init_runtimePaths();
26797
- import * as fs55 from "fs";
26798
- import * as path52 from "path";
26826
+ import * as fs56 from "fs";
26827
+ import * as path53 from "path";
26799
26828
  import posixPath4 from "path/posix";
26800
26829
  var live = /* @__PURE__ */ new Map();
26801
26830
  function brainEventsFilePath(dir, chatId) {
@@ -26803,8 +26832,8 @@ function brainEventsFilePath(dir, chatId) {
26803
26832
  }
26804
26833
  function lastPersistedSeq(dir, chatId) {
26805
26834
  const p = brainEventsFilePath(dir, chatId);
26806
- if (!fs55.existsSync(p)) return 0;
26807
- const lines = fs55.readFileSync(p, "utf-8").split("\n").filter(Boolean);
26835
+ if (!fs56.existsSync(p)) return 0;
26836
+ const lines = fs56.readFileSync(p, "utf-8").split("\n").filter(Boolean);
26808
26837
  if (lines.length === 0) return 0;
26809
26838
  try {
26810
26839
  return JSON.parse(lines[lines.length - 1]).seq || 0;
@@ -26814,9 +26843,9 @@ function lastPersistedSeq(dir, chatId) {
26814
26843
  }
26815
26844
  function readSince(dir, chatId, since) {
26816
26845
  const p = brainEventsFilePath(dir, chatId);
26817
- if (!fs55.existsSync(p)) return [];
26846
+ if (!fs56.existsSync(p)) return [];
26818
26847
  const out = [];
26819
- for (const line of fs55.readFileSync(p, "utf-8").split("\n")) {
26848
+ for (const line of fs56.readFileSync(p, "utf-8").split("\n")) {
26820
26849
  if (!line) continue;
26821
26850
  try {
26822
26851
  const rec = JSON.parse(line);
@@ -26842,12 +26871,12 @@ function beginTurn(dir, chatId) {
26842
26871
  };
26843
26872
  live.set(chatId, state);
26844
26873
  const p = brainEventsFilePath(dir, chatId);
26845
- fs55.mkdirSync(path52.dirname(p), { recursive: true });
26874
+ fs56.mkdirSync(path53.dirname(p), { recursive: true });
26846
26875
  return (event) => {
26847
26876
  state.seq += 1;
26848
26877
  const rec = { seq: state.seq, turn, ts: Date.now(), event };
26849
26878
  try {
26850
- fs55.appendFileSync(p, `${JSON.stringify(rec)}
26879
+ fs56.appendFileSync(p, `${JSON.stringify(rec)}
26851
26880
  `);
26852
26881
  } catch (err) {
26853
26882
  process.stderr.write(
@@ -26886,7 +26915,7 @@ function endTurnIfUnterminated(dir, chatId, errMessage) {
26886
26915
  event: { type: "error", error: errMessage || "turn ended unexpectedly", chatId }
26887
26916
  };
26888
26917
  try {
26889
- fs55.appendFileSync(brainEventsFilePath(dir, chatId), `${JSON.stringify(rec)}
26918
+ fs56.appendFileSync(brainEventsFilePath(dir, chatId), `${JSON.stringify(rec)}
26890
26919
  `);
26891
26920
  } catch {
26892
26921
  }
@@ -26980,17 +27009,17 @@ function authOk(req, expected) {
26980
27009
  return false;
26981
27010
  }
26982
27011
  function readJsonBody(req) {
26983
- return new Promise((resolve21, reject) => {
27012
+ return new Promise((resolve23, reject) => {
26984
27013
  const chunks = [];
26985
27014
  req.on("data", (c) => chunks.push(c));
26986
27015
  req.on("end", () => {
26987
27016
  const raw = Buffer.concat(chunks).toString("utf-8");
26988
27017
  if (!raw.trim()) {
26989
- resolve21({});
27018
+ resolve23({});
26990
27019
  return;
26991
27020
  }
26992
27021
  try {
26993
- resolve21(JSON.parse(raw));
27022
+ resolve23(JSON.parse(raw));
26994
27023
  } catch (err) {
26995
27024
  reject(err instanceof Error ? err : new Error(String(err)));
26996
27025
  }
@@ -27282,7 +27311,7 @@ function buildServer(opts) {
27282
27311
  const runTurn = opts.runTurn ?? runChatTurn;
27283
27312
  const createStore = opts.createStore ?? createSessionStore;
27284
27313
  const cloneRepo = opts.cloneRepo ?? defaultCloneRepo;
27285
- const reposRoot = opts.reposRoot ?? path53.join(path53.dirname(path53.resolve(opts.cwd)), "repos");
27314
+ const reposRoot = opts.reposRoot ?? path54.join(path54.dirname(path54.resolve(opts.cwd)), "repos");
27286
27315
  return createServer2(async (req, res) => {
27287
27316
  if (!req.method || !req.url) {
27288
27317
  sendJson(res, 400, { error: "bad request" });
@@ -27363,11 +27392,11 @@ async function brainServe(opts) {
27363
27392
  litellmUrl,
27364
27393
  driver
27365
27394
  });
27366
- await new Promise((resolve21) => {
27395
+ await new Promise((resolve23) => {
27367
27396
  server.listen(port, "0.0.0.0", () => {
27368
27397
  process.stdout.write(`[brain-serve] listening on 0.0.0.0:${port} (cwd=${opts.cwd})
27369
27398
  `);
27370
- resolve21();
27399
+ resolve23();
27371
27400
  });
27372
27401
  });
27373
27402
  const shutdown = (signal) => {
@@ -27622,14 +27651,14 @@ async function startBrainProxy(opts) {
27622
27651
  const { httpServer, handler } = buildBrainProxy(opts);
27623
27652
  const port = opts.port ?? 0;
27624
27653
  const host = opts.host ?? "127.0.0.1";
27625
- await new Promise((resolve21) => httpServer.listen(port, host, () => resolve21()));
27654
+ await new Promise((resolve23) => httpServer.listen(port, host, () => resolve23()));
27626
27655
  const addr = httpServer.address();
27627
27656
  return {
27628
27657
  httpServer,
27629
27658
  port: addr.port,
27630
27659
  url: `http://${host}:${addr.port}`,
27631
- stop: () => new Promise((resolve21) => {
27632
- httpServer.close(() => resolve21());
27660
+ stop: () => new Promise((resolve23) => {
27661
+ httpServer.close(() => resolve23());
27633
27662
  }),
27634
27663
  handler
27635
27664
  };
@@ -27779,23 +27808,23 @@ function buildMcpHttpServer(opts) {
27779
27808
  httpServer,
27780
27809
  routes,
27781
27810
  port,
27782
- stop: () => new Promise((resolve21) => {
27811
+ stop: () => new Promise((resolve23) => {
27783
27812
  let pending = transports.size;
27784
27813
  if (pending === 0) {
27785
- httpServer.close(() => resolve21());
27814
+ httpServer.close(() => resolve23());
27786
27815
  return;
27787
27816
  }
27788
27817
  for (const transport of transports.values()) {
27789
27818
  void transport.close().finally(() => {
27790
27819
  pending--;
27791
- if (pending === 0) httpServer.close(() => resolve21());
27820
+ if (pending === 0) httpServer.close(() => resolve23());
27792
27821
  });
27793
27822
  }
27794
27823
  })
27795
27824
  };
27796
27825
  }
27797
27826
  function listenMcpHttpServer(server, host = "127.0.0.1") {
27798
- return new Promise((resolve21, reject) => {
27827
+ return new Promise((resolve23, reject) => {
27799
27828
  server.httpServer.once("error", reject);
27800
27829
  server.httpServer.listen(server.port, host, () => {
27801
27830
  server.httpServer.off("error", reject);
@@ -27803,7 +27832,7 @@ function listenMcpHttpServer(server, host = "127.0.0.1") {
27803
27832
  if (addr && typeof addr === "object") {
27804
27833
  server.port = addr.port;
27805
27834
  }
27806
- resolve21();
27835
+ resolve23();
27807
27836
  });
27808
27837
  });
27809
27838
  }
@@ -27886,7 +27915,7 @@ async function loadConfigSafe() {
27886
27915
  }
27887
27916
 
27888
27917
  // src/chat-cli.ts
27889
- import * as path54 from "path";
27918
+ import * as path55 from "path";
27890
27919
 
27891
27920
  // src/chat/inbox.ts
27892
27921
  import { execFileSync as execFileSync25 } from "child_process";
@@ -27953,7 +27982,7 @@ async function waitForNextUserMessage(opts) {
27953
27982
  }
27954
27983
  }
27955
27984
  function sleep3(ms) {
27956
- return new Promise((resolve21) => setTimeout(resolve21, ms));
27985
+ return new Promise((resolve23) => setTimeout(resolve23, ms));
27957
27986
  }
27958
27987
  function currentBranch(cwd) {
27959
27988
  try {
@@ -28177,7 +28206,7 @@ async function runChat(argv) {
28177
28206
  ${CHAT_HELP}`);
28178
28207
  return 64;
28179
28208
  }
28180
- const cwd = args.cwd ? path54.resolve(args.cwd) : process.cwd();
28209
+ const cwd = args.cwd ? path55.resolve(args.cwd) : process.cwd();
28181
28210
  const sessionId = args.sessionId;
28182
28211
  const runRequest = readRunRequestFromEnv();
28183
28212
  if (runRequest && "request" in runRequest) {
@@ -28302,6 +28331,575 @@ init_definition_paths();
28302
28331
  init_job();
28303
28332
  init_registry();
28304
28333
 
28334
+ // src/servers/brain-terminal-agent.ts
28335
+ init_repoWorkspace();
28336
+ import * as path57 from "path";
28337
+ import { createInterface as createInterface2 } from "readline";
28338
+
28339
+ // src/terminal/brain-terminal-session.ts
28340
+ import { createHash as createHash9 } from "crypto";
28341
+ var MAX_CAPTURE_CHARS = 2e5;
28342
+ function requiredIdentifier(value, name, max = 240) {
28343
+ if (typeof value !== "string" || !value.trim() || value.length > max) {
28344
+ throw new Error(`${name} must be a non-empty string of at most ${max} characters`);
28345
+ }
28346
+ return value.trim();
28347
+ }
28348
+ function terminalSize(value, name) {
28349
+ if (!Number.isInteger(value) || Number(value) < 1 || Number(value) > 1e3) {
28350
+ throw new Error(`${name} must be an integer between 1 and 1000`);
28351
+ }
28352
+ return Number(value);
28353
+ }
28354
+ function revision(value) {
28355
+ if (value === void 0) return void 0;
28356
+ if (!Number.isInteger(value) || Number(value) < 0) {
28357
+ throw new Error("afterRevision must be a non-negative integer");
28358
+ }
28359
+ return Number(value);
28360
+ }
28361
+ function parseBrainTerminalOpenRequest(value) {
28362
+ if (!value || typeof value !== "object") throw new Error("open request must be an object");
28363
+ const request = value;
28364
+ if (request.type !== "open") throw new Error("first terminal message must be open");
28365
+ if (!request.session || typeof request.session !== "object") throw new Error("session is required");
28366
+ const session = request.session;
28367
+ if (!session.scope || typeof session.scope !== "object") throw new Error("session scope is required");
28368
+ const scope = session.scope;
28369
+ return {
28370
+ type: "open",
28371
+ session: {
28372
+ id: requiredIdentifier(session.id, "session.id"),
28373
+ scope: {
28374
+ owner: requiredIdentifier(scope.owner, "scope.owner", 100),
28375
+ repo: requiredIdentifier(scope.repo, "scope.repo", 100),
28376
+ conversationId: requiredIdentifier(scope.conversationId, "scope.conversationId")
28377
+ }
28378
+ },
28379
+ cwd: requiredIdentifier(request.cwd, "cwd", 1e3),
28380
+ afterRevision: revision(request.afterRevision),
28381
+ cols: terminalSize(request.cols, "cols"),
28382
+ rows: terminalSize(request.rows, "rows")
28383
+ };
28384
+ }
28385
+ function parseBrainTerminalStatusRequest(value) {
28386
+ if (!value || typeof value !== "object") throw new Error("status request must be an object");
28387
+ const request = value;
28388
+ if (request.type !== "status") throw new Error("terminal request must be status");
28389
+ return { type: "status", sessionId: requiredIdentifier(request.sessionId, "sessionId") };
28390
+ }
28391
+ function parseBrainTerminalCommand(value) {
28392
+ if (!value || typeof value !== "object") throw new Error("terminal command must be an object");
28393
+ const command = value;
28394
+ const sessionId = requiredIdentifier(command.sessionId, "sessionId");
28395
+ switch (command.type) {
28396
+ case "attach":
28397
+ return { type: "attach", sessionId, afterRevision: revision(command.afterRevision) };
28398
+ case "input": {
28399
+ const inputId = requiredIdentifier(command.inputId, "inputId");
28400
+ if (typeof command.data !== "string" || command.data.length === 0) {
28401
+ throw new Error("data must be a non-empty string");
28402
+ }
28403
+ return { type: "input", sessionId, inputId, data: command.data };
28404
+ }
28405
+ case "resize":
28406
+ return {
28407
+ type: "resize",
28408
+ sessionId,
28409
+ cols: terminalSize(command.cols, "cols"),
28410
+ rows: terminalSize(command.rows, "rows")
28411
+ };
28412
+ case "detach":
28413
+ return { type: "detach", sessionId };
28414
+ case "restart":
28415
+ return { type: "restart", sessionId };
28416
+ default:
28417
+ throw new Error("unknown terminal command");
28418
+ }
28419
+ }
28420
+ function sessionName(id) {
28421
+ return `kody_${createHash9("sha256").update(id).digest("hex").slice(0, 32)}`;
28422
+ }
28423
+ function stateEvent(session) {
28424
+ return {
28425
+ type: "state",
28426
+ sessionId: session.id,
28427
+ generation: session.generation,
28428
+ state: session.state,
28429
+ processId: session.processId
28430
+ };
28431
+ }
28432
+ function failedEvent(session, code, cause) {
28433
+ return {
28434
+ type: "failed",
28435
+ sessionId: session.id,
28436
+ generation: session.generation,
28437
+ code,
28438
+ message: cause instanceof Error ? cause.message : String(cause)
28439
+ };
28440
+ }
28441
+ var BrainTerminalSessionAgent = class {
28442
+ constructor(dependencies) {
28443
+ this.dependencies = dependencies;
28444
+ }
28445
+ dependencies;
28446
+ session = null;
28447
+ now() {
28448
+ return (this.dependencies.now?.() ?? /* @__PURE__ */ new Date()).toISOString();
28449
+ }
28450
+ requireSession() {
28451
+ if (!this.session) throw new Error("terminal session is not open");
28452
+ return this.session;
28453
+ }
28454
+ async persist(session) {
28455
+ this.session = session;
28456
+ await this.dependencies.store.write(session);
28457
+ }
28458
+ async open(rawRequest) {
28459
+ const request = parseBrainTerminalOpenRequest(rawRequest);
28460
+ let session = await this.dependencies.store.read(request.session.id);
28461
+ if (session && (session.scope.owner !== request.session.scope.owner || session.scope.repo !== request.session.scope.repo || session.scope.conversationId !== request.session.scope.conversationId)) {
28462
+ throw new Error("terminal session scope does not match stored identity");
28463
+ }
28464
+ if (!session) {
28465
+ session = {
28466
+ version: 1,
28467
+ id: request.session.id,
28468
+ scope: request.session.scope,
28469
+ sessionName: sessionName(request.session.id),
28470
+ cwd: request.cwd,
28471
+ generation: 1,
28472
+ state: "starting",
28473
+ revision: 0,
28474
+ output: "",
28475
+ processId: null,
28476
+ cols: request.cols,
28477
+ rows: request.rows,
28478
+ updatedAt: this.now()
28479
+ };
28480
+ await this.persist(session);
28481
+ let started;
28482
+ try {
28483
+ started = await this.dependencies.runtime.start(
28484
+ session.sessionName,
28485
+ session.cwd,
28486
+ session.cols,
28487
+ session.rows
28488
+ );
28489
+ } catch (cause) {
28490
+ session = { ...session, state: "failed", updatedAt: this.now() };
28491
+ await this.persist(session);
28492
+ return [failedEvent(session, "runtime_start_failed", cause)];
28493
+ }
28494
+ session = { ...session, state: "ready", processId: started.processId, updatedAt: this.now() };
28495
+ await this.persist(session);
28496
+ } else {
28497
+ const runtime = await this.dependencies.runtime.inspect(session.sessionName);
28498
+ session = {
28499
+ ...session,
28500
+ state: runtime.alive ? "ready" : session.state === "failed" ? "failed" : "exited",
28501
+ processId: runtime.processId,
28502
+ cols: request.cols,
28503
+ rows: request.rows,
28504
+ updatedAt: this.now()
28505
+ };
28506
+ if (runtime.alive) {
28507
+ await this.dependencies.runtime.resize(session.sessionName, request.cols, request.rows);
28508
+ }
28509
+ await this.persist(session);
28510
+ }
28511
+ const events = [stateEvent(session)];
28512
+ if (session.output && (request.afterRevision === void 0 || request.afterRevision < session.revision)) {
28513
+ events.push({
28514
+ type: "output",
28515
+ sessionId: session.id,
28516
+ generation: session.generation,
28517
+ revision: session.revision,
28518
+ data: `\x1B[2J\x1B[H${session.output}`
28519
+ });
28520
+ }
28521
+ return events;
28522
+ }
28523
+ async inspectStored(sessionId) {
28524
+ const id = requiredIdentifier(sessionId, "sessionId");
28525
+ const session = await this.dependencies.store.read(id);
28526
+ if (!session) return null;
28527
+ const runtime = await this.dependencies.runtime.inspect(session.sessionName);
28528
+ const next = {
28529
+ ...session,
28530
+ state: runtime.alive ? session.state : "exited",
28531
+ processId: runtime.processId,
28532
+ updatedAt: this.now()
28533
+ };
28534
+ await this.persist(next);
28535
+ return {
28536
+ id: next.id,
28537
+ generation: next.generation,
28538
+ state: next.state,
28539
+ revision: next.revision,
28540
+ processId: next.processId
28541
+ };
28542
+ }
28543
+ async status() {
28544
+ const session = this.requireSession();
28545
+ const runtime = await this.dependencies.runtime.inspect(session.sessionName);
28546
+ const nextState = runtime.alive ? session.state : session.state === "failed" ? "failed" : "exited";
28547
+ if (nextState !== session.state || runtime.processId !== session.processId) {
28548
+ await this.persist({
28549
+ ...session,
28550
+ state: nextState,
28551
+ processId: runtime.processId,
28552
+ updatedAt: this.now()
28553
+ });
28554
+ }
28555
+ const current = this.requireSession();
28556
+ return {
28557
+ id: current.id,
28558
+ generation: current.generation,
28559
+ state: current.state,
28560
+ revision: current.revision,
28561
+ processId: current.processId
28562
+ };
28563
+ }
28564
+ async captureOutput() {
28565
+ const session = this.requireSession();
28566
+ if (session.state !== "ready" && session.state !== "detached") return null;
28567
+ const output = (await this.dependencies.runtime.capture(session.sessionName)).slice(-MAX_CAPTURE_CHARS);
28568
+ if (output === session.output) return null;
28569
+ const next = {
28570
+ ...session,
28571
+ output,
28572
+ revision: session.revision + 1,
28573
+ updatedAt: this.now()
28574
+ };
28575
+ await this.persist(next);
28576
+ return {
28577
+ type: "output",
28578
+ sessionId: next.id,
28579
+ generation: next.generation,
28580
+ revision: next.revision,
28581
+ data: `\x1B[2J\x1B[H${output}`
28582
+ };
28583
+ }
28584
+ async detach() {
28585
+ const session = this.requireSession();
28586
+ const next = { ...session, state: "detached", updatedAt: this.now() };
28587
+ await this.persist(next);
28588
+ return stateEvent(next);
28589
+ }
28590
+ async command(rawCommand) {
28591
+ const command = parseBrainTerminalCommand(rawCommand);
28592
+ const session = this.requireSession();
28593
+ if (command.sessionId !== session.id) throw new Error("terminal command session identity mismatch");
28594
+ switch (command.type) {
28595
+ case "attach":
28596
+ return stateEvent(session);
28597
+ case "input":
28598
+ if (session.state !== "ready") throw new Error(`input is not allowed while terminal is ${session.state}`);
28599
+ await this.dependencies.runtime.input(session.sessionName, command.data);
28600
+ return {
28601
+ type: "input-accepted",
28602
+ sessionId: session.id,
28603
+ generation: session.generation,
28604
+ inputId: command.inputId
28605
+ };
28606
+ case "resize":
28607
+ if (session.state !== "ready") throw new Error(`resize is not allowed while terminal is ${session.state}`);
28608
+ await this.dependencies.runtime.resize(session.sessionName, command.cols, command.rows);
28609
+ await this.persist({ ...session, cols: command.cols, rows: command.rows, updatedAt: this.now() });
28610
+ return null;
28611
+ case "detach":
28612
+ return this.detach();
28613
+ case "restart": {
28614
+ if (session.state === "starting") throw new Error("restart is not allowed while terminal is starting");
28615
+ await this.dependencies.runtime.stop(session.sessionName);
28616
+ const starting = {
28617
+ ...session,
28618
+ generation: session.generation + 1,
28619
+ state: "starting",
28620
+ revision: 0,
28621
+ output: "",
28622
+ processId: null,
28623
+ updatedAt: this.now()
28624
+ };
28625
+ await this.persist(starting);
28626
+ let started;
28627
+ try {
28628
+ started = await this.dependencies.runtime.start(
28629
+ starting.sessionName,
28630
+ starting.cwd,
28631
+ starting.cols,
28632
+ starting.rows
28633
+ );
28634
+ } catch (cause) {
28635
+ const failed = { ...starting, state: "failed", updatedAt: this.now() };
28636
+ await this.persist(failed);
28637
+ return failedEvent(failed, "runtime_start_failed", cause);
28638
+ }
28639
+ const ready = { ...starting, state: "ready", processId: started.processId, updatedAt: this.now() };
28640
+ await this.persist(ready);
28641
+ return stateEvent(ready);
28642
+ }
28643
+ }
28644
+ }
28645
+ };
28646
+
28647
+ // src/terminal/brain-terminal-adapters.ts
28648
+ import { createHash as createHash10, randomBytes as randomBytes2 } from "crypto";
28649
+ import { mkdir, readFile, rename, writeFile as writeFile2 } from "fs/promises";
28650
+ import * as path56 from "path";
28651
+ import { spawn as spawn9 } from "child_process";
28652
+ function runTerminalCommand(command, args, input) {
28653
+ return new Promise((resolve23, reject) => {
28654
+ const child = spawn9(command, args, { stdio: ["pipe", "pipe", "pipe"] });
28655
+ const stdout = [];
28656
+ const stderr = [];
28657
+ child.stdout.on("data", (chunk) => stdout.push(chunk));
28658
+ child.stderr.on("data", (chunk) => stderr.push(chunk));
28659
+ child.on("error", reject);
28660
+ child.on("close", (code) => {
28661
+ resolve23({
28662
+ code: code ?? 1,
28663
+ stdout: Buffer.concat(stdout).toString("utf8"),
28664
+ stderr: Buffer.concat(stderr).toString("utf8")
28665
+ });
28666
+ });
28667
+ if (input !== void 0) child.stdin.end(input);
28668
+ else child.stdin.end();
28669
+ });
28670
+ }
28671
+ function storeKey(id) {
28672
+ return createHash10("sha256").update(id).digest("hex");
28673
+ }
28674
+ function isStoredSession(value) {
28675
+ if (!value || typeof value !== "object") return false;
28676
+ const session = value;
28677
+ return session.version === 1 && typeof session.id === "string" && typeof session.sessionName === "string" && typeof session.cwd === "string" && Number.isInteger(session.generation) && Number.isInteger(session.revision) && typeof session.output === "string" && typeof session.scope?.owner === "string" && typeof session.scope?.repo === "string" && typeof session.scope?.conversationId === "string";
28678
+ }
28679
+ var FileBrainTerminalMetadataStore = class {
28680
+ constructor(root) {
28681
+ this.root = root;
28682
+ }
28683
+ root;
28684
+ file(id) {
28685
+ return path56.join(this.root, `${storeKey(id)}.json`);
28686
+ }
28687
+ async read(id) {
28688
+ try {
28689
+ const parsed = JSON.parse(await readFile(this.file(id), "utf8"));
28690
+ if (!isStoredSession(parsed) || parsed.id !== id) {
28691
+ throw new Error("stored terminal session is invalid");
28692
+ }
28693
+ return parsed;
28694
+ } catch (error) {
28695
+ if (error.code === "ENOENT") return null;
28696
+ throw error;
28697
+ }
28698
+ }
28699
+ async write(session) {
28700
+ await mkdir(this.root, { recursive: true, mode: 448 });
28701
+ const target = this.file(session.id);
28702
+ const temporary = `${target}.${process.pid}.${randomBytes2(6).toString("hex")}.tmp`;
28703
+ await writeFile2(temporary, `${JSON.stringify(session)}
28704
+ `, { mode: 384 });
28705
+ await rename(temporary, target);
28706
+ }
28707
+ };
28708
+ function commandError(action, result) {
28709
+ const detail = result.stderr.trim() || result.stdout.trim() || `exit ${result.code}`;
28710
+ return new Error(`tmux ${action} failed: ${detail.slice(0, 500)}`);
28711
+ }
28712
+ var TmuxBrainTerminalRuntime = class {
28713
+ constructor(run = runTerminalCommand) {
28714
+ this.run = run;
28715
+ }
28716
+ run;
28717
+ async tmux(action, args, input) {
28718
+ const result = await this.run("tmux", args, input);
28719
+ if (result.code !== 0) throw commandError(action, result);
28720
+ return result;
28721
+ }
28722
+ async start(sessionName2, cwd, cols, rows) {
28723
+ await this.tmux("start", [
28724
+ "new-session",
28725
+ "-d",
28726
+ "-s",
28727
+ sessionName2,
28728
+ "-x",
28729
+ String(cols),
28730
+ "-y",
28731
+ String(rows),
28732
+ "-c",
28733
+ cwd,
28734
+ "/bin/bash",
28735
+ "-l"
28736
+ ]);
28737
+ await this.tmux("configure", ["set-option", "-t", sessionName2, "status", "off"]);
28738
+ await this.tmux("configure", ["set-option", "-t", sessionName2, "history-limit", "50000"]);
28739
+ await this.tmux("configure", ["set-option", "-w", "-t", sessionName2, "remain-on-exit", "on"]);
28740
+ const inspected = await this.inspect(sessionName2);
28741
+ if (!inspected.alive || inspected.processId === null) throw new Error("tmux terminal did not start");
28742
+ return { processId: inspected.processId };
28743
+ }
28744
+ async inspect(sessionName2) {
28745
+ const result = await this.run("tmux", [
28746
+ "list-panes",
28747
+ "-t",
28748
+ sessionName2,
28749
+ "-F",
28750
+ "#{pane_dead}:#{pane_pid}"
28751
+ ]);
28752
+ if (result.code !== 0) return { alive: false, processId: null };
28753
+ const [dead, pid] = result.stdout.trim().split(":");
28754
+ const processId = Number(pid);
28755
+ return {
28756
+ alive: dead === "0" && Number.isInteger(processId) && processId > 0,
28757
+ processId: Number.isInteger(processId) && processId > 0 ? processId : null
28758
+ };
28759
+ }
28760
+ async capture(sessionName2) {
28761
+ const alternate = await this.run("tmux", [
28762
+ "display-message",
28763
+ "-p",
28764
+ "-t",
28765
+ sessionName2,
28766
+ "#{alternate_on}"
28767
+ ]);
28768
+ if (alternate.code !== 0) throw commandError("inspect screen", alternate);
28769
+ const args = alternate.stdout.trim() === "1" ? ["capture-pane", "-p", "-e", "-t", sessionName2] : ["capture-pane", "-p", "-e", "-J", "-S", "-50000", "-t", sessionName2];
28770
+ return (await this.tmux("capture", args)).stdout;
28771
+ }
28772
+ async input(sessionName2, data) {
28773
+ const bufferName = `kody_${randomBytes2(8).toString("hex")}`;
28774
+ await this.tmux("load input", ["load-buffer", "-b", bufferName, "-"], data);
28775
+ await this.tmux("paste input", ["paste-buffer", "-d", "-b", bufferName, "-t", sessionName2]);
28776
+ }
28777
+ async resize(sessionName2, cols, rows) {
28778
+ await this.tmux("resize", [
28779
+ "resize-window",
28780
+ "-t",
28781
+ sessionName2,
28782
+ "-x",
28783
+ String(cols),
28784
+ "-y",
28785
+ String(rows)
28786
+ ]);
28787
+ }
28788
+ async stop(sessionName2) {
28789
+ const result = await this.run("tmux", ["kill-session", "-t", sessionName2]);
28790
+ if (result.code !== 0 && !/can't find session|no server running/i.test(result.stderr)) {
28791
+ throw commandError("stop", result);
28792
+ }
28793
+ }
28794
+ };
28795
+
28796
+ // src/servers/brain-terminal-agent.ts
28797
+ var DEFAULT_POLL_INTERVAL_MS = 150;
28798
+ function writeEvent(output, event) {
28799
+ output.write(`${JSON.stringify(event)}
28800
+ `);
28801
+ }
28802
+ async function brainTerminalAgent(options) {
28803
+ const input = options.input ?? process.stdin;
28804
+ const output = options.output ?? process.stdout;
28805
+ const error = options.error ?? process.stderr;
28806
+ const reposRoot = process.env.BRAIN_REPOS_ROOT?.trim() || path57.join(path57.dirname(path57.resolve(options.cwd)), "repos");
28807
+ const stateRoot = process.env.BRAIN_TERMINAL_STATE_ROOT?.trim() || path57.join(path57.dirname(reposRoot), ".kody", "terminal-sessions");
28808
+ const agent = new BrainTerminalSessionAgent({
28809
+ store: new FileBrainTerminalMetadataStore(stateRoot),
28810
+ runtime: new TmuxBrainTerminalRuntime()
28811
+ });
28812
+ const lines = createInterface2({ input, crlfDelay: Infinity });
28813
+ let opened = false;
28814
+ let poll = null;
28815
+ let pollRunning = false;
28816
+ const stopPoll = () => {
28817
+ if (poll) clearInterval(poll);
28818
+ poll = null;
28819
+ };
28820
+ const capture = async () => {
28821
+ if (pollRunning) return;
28822
+ pollRunning = true;
28823
+ try {
28824
+ const event = await agent.captureOutput();
28825
+ if (event) writeEvent(output, event);
28826
+ const status = await agent.status();
28827
+ if (status.state === "exited") {
28828
+ writeEvent(output, {
28829
+ type: "exited",
28830
+ sessionId: status.id,
28831
+ generation: status.generation
28832
+ });
28833
+ stopPoll();
28834
+ }
28835
+ } catch (cause) {
28836
+ error.write(`[brain-terminal-agent] capture failed: ${cause instanceof Error ? cause.message : String(cause)}
28837
+ `);
28838
+ } finally {
28839
+ pollRunning = false;
28840
+ }
28841
+ };
28842
+ try {
28843
+ for await (const line of lines) {
28844
+ if (!line.trim()) continue;
28845
+ const value = JSON.parse(line);
28846
+ if (!opened) {
28847
+ if (value && typeof value === "object" && value.type === "status") {
28848
+ const request = parseBrainTerminalStatusRequest(value);
28849
+ const status = await agent.inspectStored(request.sessionId);
28850
+ if (status) {
28851
+ writeEvent(output, {
28852
+ type: "state",
28853
+ sessionId: status.id,
28854
+ generation: status.generation,
28855
+ state: status.state,
28856
+ processId: status.processId
28857
+ });
28858
+ } else {
28859
+ writeEvent(output, {
28860
+ type: "failed",
28861
+ sessionId: request.sessionId,
28862
+ generation: 1,
28863
+ code: "session_not_found",
28864
+ message: "Terminal session not found"
28865
+ });
28866
+ }
28867
+ return 0;
28868
+ }
28869
+ const requested = parseBrainTerminalOpenRequest(value);
28870
+ const repo = `${requested.session.scope.owner}/${requested.session.scope.repo}`;
28871
+ const workspaceCwd = await ensureRepoCwd({
28872
+ baseCwd: options.cwd,
28873
+ reposRoot,
28874
+ repo,
28875
+ repoToken: process.env.KODY_TOKEN ?? process.env.GH_TOKEN ?? process.env.GITHUB_TOKEN ?? process.env.GH_PAT,
28876
+ cloneRepo: defaultCloneRepo
28877
+ });
28878
+ const events = await agent.open({ ...requested, cwd: workspaceCwd });
28879
+ for (const event2 of events) writeEvent(output, event2);
28880
+ opened = true;
28881
+ poll = setInterval(() => void capture(), options.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS);
28882
+ poll.unref?.();
28883
+ continue;
28884
+ }
28885
+ const command = parseBrainTerminalCommand(value);
28886
+ const event = await agent.command(command);
28887
+ if (event) writeEvent(output, event);
28888
+ if (command.type === "detach") stopPoll();
28889
+ }
28890
+ if (opened) await agent.detach();
28891
+ return 0;
28892
+ } catch (cause) {
28893
+ stopPoll();
28894
+ error.write(`[brain-terminal-agent] ${cause instanceof Error ? cause.message : String(cause)}
28895
+ `);
28896
+ return 1;
28897
+ } finally {
28898
+ stopPoll();
28899
+ lines.close();
28900
+ }
28901
+ }
28902
+
28305
28903
  // src/servers/pool-serve.ts
28306
28904
  import { createServer as createServer5 } from "http";
28307
28905
 
@@ -28373,8 +28971,8 @@ var FlyClient = class {
28373
28971
  get fetch() {
28374
28972
  return this.opts.fetchImpl ?? fetch;
28375
28973
  }
28376
- async call(path55, init = {}) {
28377
- const res = await this.fetch(`${FLY_API_BASE}${path55}`, {
28974
+ async call(path58, init = {}) {
28975
+ const res = await this.fetch(`${FLY_API_BASE}${path58}`, {
28378
28976
  method: init.method ?? "GET",
28379
28977
  headers: {
28380
28978
  Authorization: `Bearer ${this.opts.token}`,
@@ -28385,7 +28983,7 @@ var FlyClient = class {
28385
28983
  if (res.status === 404 && init.allow404) return null;
28386
28984
  if (!res.ok) {
28387
28985
  const text2 = await res.text().catch(() => "");
28388
- throw new Error(`Fly API ${res.status} on ${path55}: ${text2.slice(0, 200) || res.statusText}`);
28986
+ throw new Error(`Fly API ${res.status} on ${path58}: ${text2.slice(0, 200) || res.statusText}`);
28389
28987
  }
28390
28988
  if (res.status === 204) return null;
28391
28989
  const raw = await res.text();
@@ -28898,14 +29496,14 @@ function sendJson2(res, status, body) {
28898
29496
  res.end(JSON.stringify(body));
28899
29497
  }
28900
29498
  function readJsonBody2(req) {
28901
- return new Promise((resolve21, reject) => {
29499
+ return new Promise((resolve23, reject) => {
28902
29500
  const chunks = [];
28903
29501
  req.on("data", (c) => chunks.push(c));
28904
29502
  req.on("end", () => {
28905
29503
  const raw = Buffer.concat(chunks).toString("utf-8");
28906
- if (!raw.trim()) return resolve21({});
29504
+ if (!raw.trim()) return resolve23({});
28907
29505
  try {
28908
- resolve21(JSON.parse(raw));
29506
+ resolve23(JSON.parse(raw));
28909
29507
  } catch (err) {
28910
29508
  reject(err instanceof Error ? err : new Error(String(err)));
28911
29509
  }
@@ -29059,10 +29657,10 @@ async function poolServe() {
29059
29657
  }
29060
29658
  });
29061
29659
  const apiHost = process.env.POOL_API_HOST ?? "::";
29062
- await new Promise((resolve21) => {
29660
+ await new Promise((resolve23) => {
29063
29661
  server.listen(apiPort, apiHost, () => {
29064
29662
  log(`listening on ${apiHost}:${apiPort} (min=${min}, app=${app}, region=${region})`);
29065
- resolve21();
29663
+ resolve23();
29066
29664
  });
29067
29665
  });
29068
29666
  if (loopTickEnabled) void runLoopTick();
@@ -29080,8 +29678,8 @@ async function poolServe() {
29080
29678
  }
29081
29679
 
29082
29680
  // src/servers/runner-serve.ts
29083
- import { spawn as spawn9 } from "child_process";
29084
- import * as fs56 from "fs";
29681
+ import { spawn as spawn10 } from "child_process";
29682
+ import * as fs57 from "fs";
29085
29683
  import { createServer as createServer6 } from "http";
29086
29684
  var DEFAULT_PORT2 = 8080;
29087
29685
  var DEFAULT_WORKDIR = "/workspace/repo";
@@ -29102,17 +29700,17 @@ function authOk2(req, expected) {
29102
29700
  return false;
29103
29701
  }
29104
29702
  function readJsonBody3(req) {
29105
- return new Promise((resolve21, reject) => {
29703
+ return new Promise((resolve23, reject) => {
29106
29704
  const chunks = [];
29107
29705
  req.on("data", (c) => chunks.push(c));
29108
29706
  req.on("end", () => {
29109
29707
  const raw = Buffer.concat(chunks).toString("utf-8");
29110
29708
  if (!raw.trim()) {
29111
- resolve21({});
29709
+ resolve23({});
29112
29710
  return;
29113
29711
  }
29114
29712
  try {
29115
- resolve21(JSON.parse(raw));
29713
+ resolve23(JSON.parse(raw));
29116
29714
  } catch (err) {
29117
29715
  reject(err instanceof Error ? err : new Error(String(err)));
29118
29716
  }
@@ -29157,8 +29755,8 @@ async function defaultRunJob(job) {
29157
29755
  const workdir = process.env.RUNNER_WORKDIR ?? DEFAULT_WORKDIR;
29158
29756
  const branch = job.ref ?? "main";
29159
29757
  const authUrl = `https://x-access-token:${job.githubToken}@github.com/${job.repo}.git`;
29160
- fs56.rmSync(workdir, { recursive: true, force: true });
29161
- fs56.mkdirSync(workdir, { recursive: true });
29758
+ fs57.rmSync(workdir, { recursive: true, force: true });
29759
+ fs57.mkdirSync(workdir, { recursive: true });
29162
29760
  const allSecrets = typeof job.allSecrets === "string" ? job.allSecrets : JSON.stringify(job.allSecrets ?? {});
29163
29761
  const target = job.runRequest.target;
29164
29762
  const interactive = target.type === "chat";
@@ -29187,13 +29785,13 @@ async function defaultRunJob(job) {
29187
29785
  ...interactive && job.idleExitMs ? { KODY_IDLE_EXIT_MS: String(job.idleExitMs) } : {},
29188
29786
  ...interactive && job.hardCapMs ? { KODY_HARD_CAP_MS: String(job.hardCapMs) } : {}
29189
29787
  };
29190
- const run = (cmd, args, cwd) => new Promise((resolve21) => {
29191
- const child = spawn9(cmd, args, { stdio: "inherit", env: childEnv, cwd });
29192
- child.on("exit", (code) => resolve21(code ?? 0));
29788
+ const run = (cmd, args, cwd) => new Promise((resolve23) => {
29789
+ const child = spawn10(cmd, args, { stdio: "inherit", env: childEnv, cwd });
29790
+ child.on("exit", (code) => resolve23(code ?? 0));
29193
29791
  child.on("error", (err) => {
29194
29792
  process.stderr.write(`[runner-serve] ${cmd} failed: ${err.message}
29195
29793
  `);
29196
- resolve21(1);
29794
+ resolve23(1);
29197
29795
  });
29198
29796
  });
29199
29797
  process.stdout.write(`[runner-serve] job ${job.jobId}: cloning ${job.repo}@${branch}
@@ -29269,11 +29867,11 @@ async function runnerServe() {
29269
29867
  const port = Number(process.env.PORT ?? DEFAULT_PORT2);
29270
29868
  const server = buildServer2({ apiKey });
29271
29869
  const host = process.env.RUNNER_HOST ?? "::";
29272
- await new Promise((resolve21) => {
29870
+ await new Promise((resolve23) => {
29273
29871
  server.listen(port, host, () => {
29274
29872
  process.stdout.write(`[runner-serve] listening on ${host}:${port} (idle, awaiting job)
29275
29873
  `);
29276
- resolve21();
29874
+ resolve23();
29277
29875
  });
29278
29876
  });
29279
29877
  const shutdown = (signal) => {
@@ -29291,7 +29889,7 @@ async function runnerServe() {
29291
29889
  // src/servers/serve.ts
29292
29890
  init_config();
29293
29891
  init_litellm();
29294
- import { spawn as spawn10 } from "child_process";
29892
+ import { spawn as spawn11 } from "child_process";
29295
29893
  function parseTarget(positional) {
29296
29894
  if (!Array.isArray(positional) || positional.length === 0) return "none";
29297
29895
  const first = String(positional[0]).toLowerCase();
@@ -29341,15 +29939,15 @@ async function serve(opts) {
29341
29939
  if (usesProxy) process.stdout.write(` ANTHROPIC_BASE_URL=${url}
29342
29940
  `);
29343
29941
  const args = ["--dangerously-skip-permissions", "--model", model.model];
29344
- const child = spawn10("claude", args, { stdio: "inherit", env: editorEnv, cwd: opts.cwd });
29345
- const exitCode = await new Promise((resolve21) => {
29346
- child.on("exit", (code) => resolve21(code ?? 0));
29942
+ const child = spawn11("claude", args, { stdio: "inherit", env: editorEnv, cwd: opts.cwd });
29943
+ const exitCode = await new Promise((resolve23) => {
29944
+ child.on("exit", (code) => resolve23(code ?? 0));
29347
29945
  child.on("error", (err) => {
29348
29946
  process.stderr.write(`[kody serve] failed to launch Claude Code: ${err.message}
29349
29947
  `);
29350
29948
  process.stderr.write(` Install: https://docs.anthropic.com/claude/docs/claude-code
29351
29949
  `);
29352
- resolve21(1);
29950
+ resolve23(1);
29353
29951
  });
29354
29952
  });
29355
29953
  killProxy();
@@ -29361,7 +29959,7 @@ async function serve(opts) {
29361
29959
  if (usesProxy) process.stdout.write(` ANTHROPIC_BASE_URL=${url}
29362
29960
  `);
29363
29961
  try {
29364
- const code = spawn10("code", [opts.cwd], { stdio: "inherit", env: editorEnv, detached: true });
29962
+ const code = spawn11("code", [opts.cwd], { stdio: "inherit", env: editorEnv, detached: true });
29365
29963
  code.on("error", (err) => {
29366
29964
  process.stderr.write(`[kody serve] failed to launch VS Code: ${err.message}
29367
29965
  `);
@@ -29709,7 +30307,15 @@ function parseArgs(argv) {
29709
30307
  if (result.cliArgs.quiet === true) result.quiet = true;
29710
30308
  return result;
29711
30309
  }
29712
- const SERVER_VERBS = /* @__PURE__ */ new Set(["serve", "pool-serve", "runner-serve", "brain-serve", "brain-proxy", "mcp-http-server"]);
30310
+ const SERVER_VERBS = /* @__PURE__ */ new Set([
30311
+ "serve",
30312
+ "pool-serve",
30313
+ "runner-serve",
30314
+ "brain-serve",
30315
+ "brain-terminal-agent",
30316
+ "brain-proxy",
30317
+ "mcp-http-server"
30318
+ ]);
29713
30319
  if (SERVER_VERBS.has(cmd)) {
29714
30320
  result.command = "server";
29715
30321
  result.serverName = cmd;
@@ -29813,6 +30419,8 @@ ${HELP_TEXT}`);
29813
30419
  return await runnerServe();
29814
30420
  case "brain-serve":
29815
30421
  return await brainServe({ cwd: cwd2 });
30422
+ case "brain-terminal-agent":
30423
+ return await brainTerminalAgent({ cwd: cwd2 });
29816
30424
  case "brain-proxy":
29817
30425
  return await brainProxy();
29818
30426
  case "mcp-http-server":