@kody-ade/kody-engine 0.4.401 → 0.4.403

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/bin/kody.js +836 -538
  2. package/package.json +24 -25
package/dist/bin/kody.js CHANGED
@@ -15,7 +15,7 @@ var init_package = __esm({
15
15
  "package.json"() {
16
16
  package_default = {
17
17
  name: "@kody-ade/kody-engine",
18
- version: "0.4.401",
18
+ version: "0.4.403",
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",
@@ -1088,7 +1088,7 @@ function cmsHeaders(opts) {
1088
1088
  }
1089
1089
  };
1090
1090
  }
1091
- async function callDashboardCms(opts, path50, init = {}) {
1091
+ async function callDashboardCms(opts, path51, init = {}) {
1092
1092
  const baseUrl = dashboardBaseUrl(opts);
1093
1093
  if (!baseUrl) {
1094
1094
  return {
@@ -1100,7 +1100,7 @@ async function callDashboardCms(opts, path50, init = {}) {
1100
1100
  const headerResult = cmsHeaders(opts);
1101
1101
  if (!headerResult.ok) return headerResult;
1102
1102
  try {
1103
- const res = await fetch(`${baseUrl}${path50}`, {
1103
+ const res = await fetch(`${baseUrl}${path51}`, {
1104
1104
  ...init,
1105
1105
  headers: {
1106
1106
  ...headerResult.headers,
@@ -1172,8 +1172,8 @@ function documentArg(value) {
1172
1172
  function normalizeCmsDocumentIdInput(input) {
1173
1173
  const trimmed = stripWrappingQuotes(input.trim());
1174
1174
  const withoutQuery = trimmed.split(/[?#]/, 1)[0] ?? trimmed;
1175
- const path50 = parseDocumentPath(withoutQuery);
1176
- return path50 ?? parseDocumentIdSegment(withoutQuery) ?? withoutQuery;
1175
+ const path51 = parseDocumentPath(withoutQuery);
1176
+ return path51 ?? parseDocumentIdSegment(withoutQuery) ?? withoutQuery;
1177
1177
  }
1178
1178
  function stripWrappingQuotes(value) {
1179
1179
  let current = value;
@@ -1184,9 +1184,9 @@ function stripWrappingQuotes(value) {
1184
1184
  }
1185
1185
  }
1186
1186
  function parseDocumentPath(value) {
1187
- const path50 = value.startsWith("http://") || value.startsWith("https://") ? urlPathname(value) : value;
1188
- if (!path50?.includes("/content/entries/")) return null;
1189
- const parts = path50.split("/").filter(Boolean).map(decodePathPart);
1187
+ const path51 = value.startsWith("http://") || value.startsWith("https://") ? urlPathname(value) : value;
1188
+ if (!path51?.includes("/content/entries/")) return null;
1189
+ const parts = path51.split("/").filter(Boolean).map(decodePathPart);
1190
1190
  const entriesIndex = parts.findIndex((part, index) => part === "content" && parts[index + 1] === "entries");
1191
1191
  const idPart = parts[entriesIndex + 3];
1192
1192
  if (!idPart || idPart === "new") return null;
@@ -3770,7 +3770,7 @@ var init_task_artifacts = __esm({
3770
3770
 
3771
3771
  // src/gha.ts
3772
3772
  import { execFileSync as execFileSync2 } from "child_process";
3773
- import * as fs16 from "fs";
3773
+ import * as fs17 from "fs";
3774
3774
  function getRunUrl() {
3775
3775
  const server = process.env.GITHUB_SERVER_URL;
3776
3776
  const repo = process.env.GITHUB_REPOSITORY;
@@ -3781,10 +3781,10 @@ function getRunUrl() {
3781
3781
  function reactToTriggerComment(cwd) {
3782
3782
  if (process.env.GITHUB_EVENT_NAME !== "issue_comment") return;
3783
3783
  const eventPath = process.env.GITHUB_EVENT_PATH;
3784
- if (!eventPath || !fs16.existsSync(eventPath)) return;
3784
+ if (!eventPath || !fs17.existsSync(eventPath)) return;
3785
3785
  let event = null;
3786
3786
  try {
3787
- event = JSON.parse(fs16.readFileSync(eventPath, "utf-8"));
3787
+ event = JSON.parse(fs17.readFileSync(eventPath, "utf-8"));
3788
3788
  } catch {
3789
3789
  return;
3790
3790
  }
@@ -4313,31 +4313,31 @@ var init_lifecycles = __esm({
4313
4313
  });
4314
4314
 
4315
4315
  // src/scripts/buildSyntheticPlugin.ts
4316
- import * as fs17 from "fs";
4316
+ import * as fs18 from "fs";
4317
4317
  import * as os3 from "os";
4318
- import * as path16 from "path";
4318
+ import * as path17 from "path";
4319
4319
  function getPluginsCatalogRoot() {
4320
- const here = path16.dirname(new URL(import.meta.url).pathname);
4320
+ const here = path17.dirname(new URL(import.meta.url).pathname);
4321
4321
  const candidates = [
4322
- path16.join(here, "..", "plugins"),
4322
+ path17.join(here, "..", "plugins"),
4323
4323
  // dev: src/scripts → src/plugins
4324
- path16.join(here, "..", "..", "plugins"),
4324
+ path17.join(here, "..", "..", "plugins"),
4325
4325
  // built: dist/scripts → dist/plugins
4326
- path16.join(here, "..", "..", "src", "plugins")
4326
+ path17.join(here, "..", "..", "src", "plugins")
4327
4327
  // fallback
4328
4328
  ];
4329
4329
  for (const c of candidates) {
4330
- if (fs17.existsSync(c) && fs17.statSync(c).isDirectory()) return c;
4330
+ if (fs18.existsSync(c) && fs18.statSync(c).isDirectory()) return c;
4331
4331
  }
4332
4332
  return candidates[0];
4333
4333
  }
4334
4334
  function copyDir(src, dst) {
4335
- fs17.mkdirSync(dst, { recursive: true });
4336
- for (const ent of fs17.readdirSync(src, { withFileTypes: true })) {
4337
- const s = path16.join(src, ent.name);
4338
- const d = path16.join(dst, ent.name);
4335
+ fs18.mkdirSync(dst, { recursive: true });
4336
+ for (const ent of fs18.readdirSync(src, { withFileTypes: true })) {
4337
+ const s = path17.join(src, ent.name);
4338
+ const d = path17.join(dst, ent.name);
4339
4339
  if (ent.isDirectory()) copyDir(s, d);
4340
- else if (ent.isFile()) fs17.copyFileSync(s, d);
4340
+ else if (ent.isFile()) fs18.copyFileSync(s, d);
4341
4341
  }
4342
4342
  }
4343
4343
  var buildSyntheticPlugin;
@@ -4350,45 +4350,45 @@ var init_buildSyntheticPlugin = __esm({
4350
4350
  if (!needsSynthetic) return;
4351
4351
  const catalog = getPluginsCatalogRoot();
4352
4352
  const runId = `${profile.name}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
4353
- const root = path16.join(os3.tmpdir(), `kody-synth-${runId}`);
4354
- fs17.mkdirSync(path16.join(root, ".claude-plugin"), { recursive: true });
4353
+ const root = path17.join(os3.tmpdir(), `kody-synth-${runId}`);
4354
+ fs18.mkdirSync(path17.join(root, ".claude-plugin"), { recursive: true });
4355
4355
  const resolvePart = (bucket, entry) => {
4356
- const local = path16.join(profile.dir, bucket, entry);
4357
- if (fs17.existsSync(local)) return local;
4358
- const central = path16.join(catalog, bucket, entry);
4359
- if (fs17.existsSync(central)) return central;
4356
+ const local = path17.join(profile.dir, bucket, entry);
4357
+ if (fs18.existsSync(local)) return local;
4358
+ const central = path17.join(catalog, bucket, entry);
4359
+ if (fs18.existsSync(central)) return central;
4360
4360
  throw new Error(
4361
4361
  `buildSyntheticPlugin: ${bucket} entry '${entry}' not found in implementation dir (${profile.dir}/${bucket}/) or catalog (${catalog}/${bucket}/)`
4362
4362
  );
4363
4363
  };
4364
4364
  if (cc.skills.length > 0) {
4365
- const dst = path16.join(root, "skills");
4366
- fs17.mkdirSync(dst, { recursive: true });
4365
+ const dst = path17.join(root, "skills");
4366
+ fs18.mkdirSync(dst, { recursive: true });
4367
4367
  for (const name of cc.skills) {
4368
- copyDir(resolvePart("skills", name), path16.join(dst, name));
4368
+ copyDir(resolvePart("skills", name), path17.join(dst, name));
4369
4369
  }
4370
4370
  }
4371
4371
  if (cc.commands.length > 0) {
4372
- const dst = path16.join(root, "commands");
4373
- fs17.mkdirSync(dst, { recursive: true });
4372
+ const dst = path17.join(root, "commands");
4373
+ fs18.mkdirSync(dst, { recursive: true });
4374
4374
  for (const name of cc.commands) {
4375
- fs17.copyFileSync(resolvePart("commands", `${name}.md`), path16.join(dst, `${name}.md`));
4375
+ fs18.copyFileSync(resolvePart("commands", `${name}.md`), path17.join(dst, `${name}.md`));
4376
4376
  }
4377
4377
  }
4378
4378
  if (cc.hooks.length > 0) {
4379
- const dst = path16.join(root, "hooks");
4380
- fs17.mkdirSync(dst, { recursive: true });
4379
+ const dst = path17.join(root, "hooks");
4380
+ fs18.mkdirSync(dst, { recursive: true });
4381
4381
  const merged = { hooks: {} };
4382
4382
  for (const name of cc.hooks) {
4383
4383
  const src = resolvePart("hooks", `${name}.json`);
4384
- const parsed = JSON.parse(fs17.readFileSync(src, "utf-8"));
4384
+ const parsed = JSON.parse(fs18.readFileSync(src, "utf-8"));
4385
4385
  for (const [event, entries] of Object.entries(parsed.hooks ?? {})) {
4386
4386
  if (!Array.isArray(entries)) continue;
4387
4387
  if (!merged.hooks[event]) merged.hooks[event] = [];
4388
4388
  merged.hooks[event].push(...entries);
4389
4389
  }
4390
4390
  }
4391
- fs17.writeFileSync(path16.join(dst, "hooks.json"), `${JSON.stringify(merged, null, 2)}
4391
+ fs18.writeFileSync(path17.join(dst, "hooks.json"), `${JSON.stringify(merged, null, 2)}
4392
4392
  `);
4393
4393
  }
4394
4394
  const manifest = {
@@ -4398,7 +4398,7 @@ var init_buildSyntheticPlugin = __esm({
4398
4398
  };
4399
4399
  if (cc.skills.length > 0) manifest.skills = ["./skills/"];
4400
4400
  if (cc.commands.length > 0) manifest.commands = ["./commands/"];
4401
- fs17.writeFileSync(path16.join(root, ".claude-plugin", "plugin.json"), `${JSON.stringify(manifest, null, 2)}
4401
+ fs18.writeFileSync(path17.join(root, ".claude-plugin", "plugin.json"), `${JSON.stringify(manifest, null, 2)}
4402
4402
  `);
4403
4403
  ctx.data.syntheticPluginPath = root;
4404
4404
  };
@@ -4406,8 +4406,8 @@ var init_buildSyntheticPlugin = __esm({
4406
4406
  });
4407
4407
 
4408
4408
  // src/subagents.ts
4409
- import * as fs18 from "fs";
4410
- import * as path17 from "path";
4409
+ import * as fs19 from "fs";
4410
+ import * as path18 from "path";
4411
4411
  function splitFrontmatter(raw) {
4412
4412
  const match = /^---\n([\s\S]*?)\n---\n?([\s\S]*)$/.exec(raw);
4413
4413
  if (!match) return { fm: {}, body: raw.trim() };
@@ -4420,10 +4420,10 @@ function splitFrontmatter(raw) {
4420
4420
  return { fm, body: (match[2] ?? "").trim() };
4421
4421
  }
4422
4422
  function resolveAgentFile2(profileDir, name) {
4423
- const local = path17.join(profileDir, "agents", `${name}.md`);
4424
- if (fs18.existsSync(local)) return local;
4425
- const central = path17.join(getPluginsCatalogRoot(), "agents", `${name}.md`);
4426
- if (fs18.existsSync(central)) return central;
4423
+ const local = path18.join(profileDir, "agents", `${name}.md`);
4424
+ if (fs19.existsSync(local)) return local;
4425
+ const central = path18.join(getPluginsCatalogRoot(), "agents", `${name}.md`);
4426
+ if (fs19.existsSync(central)) return central;
4427
4427
  throw new Error(`loadSubagents: agent '${name}' not found in ${profileDir}/agents/ or shared catalog`);
4428
4428
  }
4429
4429
  function captureSubagentTemplates(profile) {
@@ -4432,7 +4432,7 @@ function captureSubagentTemplates(profile) {
4432
4432
  const out = {};
4433
4433
  for (const name of names) {
4434
4434
  try {
4435
- out[name] = fs18.readFileSync(resolveAgentFile2(profile.dir, name), "utf-8");
4435
+ out[name] = fs19.readFileSync(resolveAgentFile2(profile.dir, name), "utf-8");
4436
4436
  } catch {
4437
4437
  }
4438
4438
  }
@@ -4443,7 +4443,7 @@ function loadSubagents(profile) {
4443
4443
  if (!names || names.length === 0) return void 0;
4444
4444
  const agents = {};
4445
4445
  for (const name of names) {
4446
- const raw = profile.subagentTemplates?.[name] ?? fs18.readFileSync(resolveAgentFile2(profile.dir, name), "utf-8");
4446
+ const raw = profile.subagentTemplates?.[name] ?? fs19.readFileSync(resolveAgentFile2(profile.dir, name), "utf-8");
4447
4447
  const { fm, body } = splitFrontmatter(raw);
4448
4448
  if (!body) throw new Error(`loadSubagents: agent '${name}' has an empty prompt body`);
4449
4449
  const def = {
@@ -4467,15 +4467,15 @@ var init_subagents = __esm({
4467
4467
  });
4468
4468
 
4469
4469
  // src/profile.ts
4470
- import * as fs19 from "fs";
4471
- import * as path18 from "path";
4470
+ import * as fs20 from "fs";
4471
+ import * as path19 from "path";
4472
4472
  function loadProfile(profilePath) {
4473
- if (!fs19.existsSync(profilePath)) {
4473
+ if (!fs20.existsSync(profilePath)) {
4474
4474
  throw new ProfileError(profilePath, "file not found");
4475
4475
  }
4476
4476
  let raw;
4477
4477
  try {
4478
- raw = JSON.parse(fs19.readFileSync(profilePath, "utf-8"));
4478
+ raw = JSON.parse(fs20.readFileSync(profilePath, "utf-8"));
4479
4479
  } catch (err) {
4480
4480
  throw new ProfileError(profilePath, `invalid JSON: ${err instanceof Error ? err.message : String(err)}`);
4481
4481
  }
@@ -4486,7 +4486,7 @@ function loadProfile(profilePath) {
4486
4486
  const unknownKeys = Object.keys(r).filter((k) => !KNOWN_PROFILE_KEYS.has(k));
4487
4487
  if (unknownKeys.length > 0) {
4488
4488
  process.stderr.write(
4489
- `[kody profile] ${path18.basename(path18.dirname(profilePath))}: unknown top-level keys ignored: ${unknownKeys.join(", ")}
4489
+ `[kody profile] ${path19.basename(path19.dirname(profilePath))}: unknown top-level keys ignored: ${unknownKeys.join(", ")}
4490
4490
  `
4491
4491
  );
4492
4492
  }
@@ -4496,7 +4496,7 @@ function loadProfile(profilePath) {
4496
4496
  if (!refPath) {
4497
4497
  throw new ProfileError(profilePath, `capability references unknown implementation '${execRef}'`);
4498
4498
  }
4499
- if (path18.resolve(refPath) === path18.resolve(profilePath)) {
4499
+ if (path19.resolve(refPath) === path19.resolve(profilePath)) {
4500
4500
  } else {
4501
4501
  const base = loadProfile(refPath);
4502
4502
  return {
@@ -4594,8 +4594,8 @@ function loadProfile(profilePath) {
4594
4594
  // Phase 5 in-process handoff opt-in. Default false; containers
4595
4595
  // flip to true after end-to-end verification.
4596
4596
  preloadContext: r.preloadContext === true,
4597
- dir: path18.dirname(profilePath),
4598
- promptTemplates: readPromptTemplates(path18.dirname(profilePath))
4597
+ dir: path19.dirname(profilePath),
4598
+ promptTemplates: readPromptTemplates(path19.dirname(profilePath))
4599
4599
  };
4600
4600
  if (lifecycle) {
4601
4601
  applyLifecycle(profile, profilePath);
@@ -4638,17 +4638,17 @@ function readPromptTemplates(dir) {
4638
4638
  const out = {};
4639
4639
  const read = (p) => {
4640
4640
  try {
4641
- out[p] = fs19.readFileSync(p, "utf-8");
4641
+ out[p] = fs20.readFileSync(p, "utf-8");
4642
4642
  } catch {
4643
4643
  }
4644
4644
  };
4645
- read(path18.join(dir, "prompt.md"));
4646
- read(path18.join(dir, "capability.md"));
4647
- read(path18.join(dir, "capability.md"));
4645
+ read(path19.join(dir, "prompt.md"));
4646
+ read(path19.join(dir, "capability.md"));
4647
+ read(path19.join(dir, "capability.md"));
4648
4648
  try {
4649
- const promptsDir = path18.join(dir, "prompts");
4650
- for (const ent of fs19.readdirSync(promptsDir)) {
4651
- if (ent.endsWith(".md")) read(path18.join(promptsDir, ent));
4649
+ const promptsDir = path19.join(dir, "prompts");
4650
+ for (const ent of fs20.readdirSync(promptsDir)) {
4651
+ if (ent.endsWith(".md")) read(path19.join(promptsDir, ent));
4652
4652
  }
4653
4653
  } catch {
4654
4654
  }
@@ -5422,16 +5422,16 @@ var init_state = __esm({
5422
5422
  });
5423
5423
 
5424
5424
  // src/prompt.ts
5425
- import * as fs20 from "fs";
5426
- import * as path19 from "path";
5425
+ import * as fs21 from "fs";
5426
+ import * as path20 from "path";
5427
5427
  function loadProjectConventions(projectDir) {
5428
5428
  const out = [];
5429
5429
  for (const rel of CONVENTION_FILES) {
5430
- const abs = path19.join(projectDir, rel);
5431
- if (!fs20.existsSync(abs)) continue;
5430
+ const abs = path20.join(projectDir, rel);
5431
+ if (!fs21.existsSync(abs)) continue;
5432
5432
  let content;
5433
5433
  try {
5434
- content = fs20.readFileSync(abs, "utf-8");
5434
+ content = fs21.readFileSync(abs, "utf-8");
5435
5435
  } catch {
5436
5436
  continue;
5437
5437
  }
@@ -5666,8 +5666,8 @@ var loadMemoryContext_exports = {};
5666
5666
  __export(loadMemoryContext_exports, {
5667
5667
  loadMemoryContext: () => loadMemoryContext
5668
5668
  });
5669
- import * as fs21 from "fs";
5670
- import * as path20 from "path";
5669
+ import * as fs22 from "fs";
5670
+ import * as path21 from "path";
5671
5671
  function formatBlockFromBackend(docs) {
5672
5672
  const pages = docs.flatMap((record2) => {
5673
5673
  if (!record2.doc || typeof record2.doc !== "object") return [];
@@ -5690,21 +5690,21 @@ function collectPages(memoryAbs) {
5690
5690
  walkMd(memoryAbs, (file) => {
5691
5691
  let stat;
5692
5692
  try {
5693
- stat = fs21.statSync(file);
5693
+ stat = fs22.statSync(file);
5694
5694
  } catch {
5695
5695
  return;
5696
5696
  }
5697
5697
  let raw;
5698
5698
  try {
5699
- raw = fs21.readFileSync(file, "utf-8");
5699
+ raw = fs22.readFileSync(file, "utf-8");
5700
5700
  } catch {
5701
5701
  return;
5702
5702
  }
5703
5703
  const fm = raw.match(/^---\s*\n([\s\S]*?)\n---/);
5704
- const title = fm?.[1]?.match(/^title:\s*(.+)$/m)?.[1]?.trim() ?? path20.basename(file, ".md");
5704
+ const title = fm?.[1]?.match(/^title:\s*(.+)$/m)?.[1]?.trim() ?? path21.basename(file, ".md");
5705
5705
  const updated = fm?.[1]?.match(/^updated:\s*([0-9T:.+\-Z]+)/m)?.[1]?.trim() ?? "";
5706
5706
  out.push({
5707
- relPath: path20.relative(memoryAbs, file),
5707
+ relPath: path21.relative(memoryAbs, file),
5708
5708
  title,
5709
5709
  updated,
5710
5710
  content: raw.length > PER_PAGE_MAX_BYTES ? raw.slice(0, PER_PAGE_MAX_BYTES) + TRUNCATED_SUFFIX2 : raw,
@@ -5772,16 +5772,16 @@ function walkMd(root, visit) {
5772
5772
  const dir = stack.pop();
5773
5773
  let names;
5774
5774
  try {
5775
- names = fs21.readdirSync(dir);
5775
+ names = fs22.readdirSync(dir);
5776
5776
  } catch {
5777
5777
  continue;
5778
5778
  }
5779
5779
  for (const name of names) {
5780
5780
  if (name.startsWith(".")) continue;
5781
- const full = path20.join(dir, name);
5781
+ const full = path21.join(dir, name);
5782
5782
  let stat;
5783
5783
  try {
5784
- stat = fs21.statSync(full);
5784
+ stat = fs22.statSync(full);
5785
5785
  } catch {
5786
5786
  continue;
5787
5787
  }
@@ -5816,8 +5816,8 @@ var init_loadMemoryContext = __esm({
5816
5816
  }
5817
5817
  return;
5818
5818
  }
5819
- const memoryAbs = path20.join(ctx.cwd, MEMORY_DIR_RELATIVE);
5820
- if (!fs21.existsSync(memoryAbs)) {
5819
+ const memoryAbs = path21.join(ctx.cwd, MEMORY_DIR_RELATIVE);
5820
+ if (!fs22.existsSync(memoryAbs)) {
5821
5821
  ctx.data.memoryContext = "";
5822
5822
  return;
5823
5823
  }
@@ -5861,11 +5861,11 @@ var init_loadCoverageRules = __esm({
5861
5861
 
5862
5862
  // src/container.ts
5863
5863
  import { execFileSync as execFileSync3 } from "child_process";
5864
- import * as fs22 from "fs";
5864
+ import * as fs23 from "fs";
5865
5865
  function getProfileInputsForChild(profileName, _cwd) {
5866
5866
  try {
5867
5867
  const profilePath = resolveProfilePath(profileName);
5868
- if (!fs22.existsSync(profilePath)) return null;
5868
+ if (!fs23.existsSync(profilePath)) return null;
5869
5869
  return loadProfile(profilePath).inputs;
5870
5870
  } catch {
5871
5871
  return null;
@@ -6328,11 +6328,11 @@ var init_lifecycleLabels = __esm({
6328
6328
  });
6329
6329
 
6330
6330
  // src/litellm.ts
6331
- import { execFileSync as execFileSync4, spawn as spawn3 } from "child_process";
6332
- import * as fs23 from "fs";
6331
+ import { execFileSync as execFileSync4, spawn as spawn4 } from "child_process";
6332
+ import * as fs24 from "fs";
6333
6333
  import * as net from "net";
6334
6334
  import * as os4 from "os";
6335
- import * as path21 from "path";
6335
+ import * as path22 from "path";
6336
6336
  async function checkLitellmHealth(url) {
6337
6337
  try {
6338
6338
  const response = await fetch(`${url}/health`, { signal: AbortSignal.timeout(3e3) });
@@ -6401,6 +6401,8 @@ function locateLitellmScript() {
6401
6401
  }
6402
6402
  }
6403
6403
  function resolveLitellmCommand() {
6404
+ const imageScript = "/opt/venv/bin/litellm";
6405
+ if (fs24.existsSync(imageScript)) return imageScript;
6404
6406
  try {
6405
6407
  execFileSync4("which", ["litellm"], { timeout: 3e3, stdio: "pipe" });
6406
6408
  return "litellm";
@@ -6436,13 +6438,13 @@ async function startLitellmIfNeeded(model, projectDir, url = LITELLM_DEFAULT_URL
6436
6438
  const spawnProxy = () => {
6437
6439
  const portMatch = activeUrl.match(/:(\d+)/);
6438
6440
  const port = portMatch ? portMatch[1] : "4000";
6439
- const configPath = path21.join(os4.tmpdir(), `kody-local-litellm-${Date.now()}.yaml`);
6440
- fs23.writeFileSync(configPath, generateLitellmConfigYaml(model));
6441
+ const configPath = path22.join(os4.tmpdir(), `kody-local-litellm-${Date.now()}.yaml`);
6442
+ fs24.writeFileSync(configPath, generateLitellmConfigYaml(model));
6441
6443
  const args = ["--config", configPath, "--port", port];
6442
- const nextLogPath = path21.join(os4.tmpdir(), `kody-local-litellm-${Date.now()}.log`);
6443
- const outFd = fs23.openSync(nextLogPath, "w");
6444
- child = spawn3(cmd, args, { stdio: ["ignore", outFd, outFd], detached: true, env: childEnv });
6445
- fs23.closeSync(outFd);
6444
+ const nextLogPath = path22.join(os4.tmpdir(), `kody-local-litellm-${Date.now()}.log`);
6445
+ const outFd = fs24.openSync(nextLogPath, "w");
6446
+ child = spawn4(cmd, args, { stdio: ["ignore", outFd, outFd], detached: true, env: childEnv });
6447
+ fs24.closeSync(outFd);
6446
6448
  logPath = nextLogPath;
6447
6449
  };
6448
6450
  const waitForHealth = async () => {
@@ -6456,7 +6458,7 @@ async function startLitellmIfNeeded(model, projectDir, url = LITELLM_DEFAULT_URL
6456
6458
  const readLogTail = () => {
6457
6459
  if (!logPath) return "";
6458
6460
  try {
6459
- return fs23.readFileSync(logPath, "utf-8").slice(-2e3);
6461
+ return fs24.readFileSync(logPath, "utf-8").slice(-2e3);
6460
6462
  } catch {
6461
6463
  return "";
6462
6464
  }
@@ -6539,10 +6541,10 @@ function canListen(port, host) {
6539
6541
  });
6540
6542
  }
6541
6543
  function readDotenvApiKeys(projectDir) {
6542
- const dotenvPath = path21.join(projectDir, ".env");
6543
- if (!fs23.existsSync(dotenvPath)) return {};
6544
+ const dotenvPath = path22.join(projectDir, ".env");
6545
+ if (!fs24.existsSync(dotenvPath)) return {};
6544
6546
  const result = {};
6545
- for (const rawLine of fs23.readFileSync(dotenvPath, "utf-8").split("\n")) {
6547
+ for (const rawLine of fs24.readFileSync(dotenvPath, "utf-8").split("\n")) {
6546
6548
  const line = rawLine.trim();
6547
6549
  if (!line || line.startsWith("#")) continue;
6548
6550
  const match = line.match(/^([A-Z_][A-Z0-9_]*_API_KEY)=(.*)$/);
@@ -6939,8 +6941,8 @@ var init_pushWithRetry = __esm({
6939
6941
 
6940
6942
  // src/commit.ts
6941
6943
  import { execFileSync as execFileSync6 } from "child_process";
6942
- import * as fs24 from "fs";
6943
- import * as path22 from "path";
6944
+ import * as fs25 from "fs";
6945
+ import * as path23 from "path";
6944
6946
  function isGitHubYamlPath(filePath) {
6945
6947
  const normalized = filePath.replace(/^\.\/+/, "");
6946
6948
  return normalized.startsWith(".github/") && /\.ya?ml$/i.test(normalized);
@@ -6982,18 +6984,18 @@ function ensureGitIdentity(cwd) {
6982
6984
  }
6983
6985
  function abortUnfinishedGitOps(cwd) {
6984
6986
  const aborted = [];
6985
- const gitDir = path22.join(cwd ?? process.cwd(), ".git");
6986
- if (!fs24.existsSync(gitDir)) return aborted;
6987
- if (fs24.existsSync(path22.join(gitDir, "MERGE_HEAD"))) {
6987
+ const gitDir = path23.join(cwd ?? process.cwd(), ".git");
6988
+ if (!fs25.existsSync(gitDir)) return aborted;
6989
+ if (fs25.existsSync(path23.join(gitDir, "MERGE_HEAD"))) {
6988
6990
  if (tryGit(["merge", "--abort"], cwd)) aborted.push("merge");
6989
6991
  }
6990
- if (fs24.existsSync(path22.join(gitDir, "CHERRY_PICK_HEAD"))) {
6992
+ if (fs25.existsSync(path23.join(gitDir, "CHERRY_PICK_HEAD"))) {
6991
6993
  if (tryGit(["cherry-pick", "--abort"], cwd)) aborted.push("cherry-pick");
6992
6994
  }
6993
- if (fs24.existsSync(path22.join(gitDir, "REVERT_HEAD"))) {
6995
+ if (fs25.existsSync(path23.join(gitDir, "REVERT_HEAD"))) {
6994
6996
  if (tryGit(["revert", "--abort"], cwd)) aborted.push("revert");
6995
6997
  }
6996
- if (fs24.existsSync(path22.join(gitDir, "rebase-merge")) || fs24.existsSync(path22.join(gitDir, "rebase-apply"))) {
6998
+ if (fs25.existsSync(path23.join(gitDir, "rebase-merge")) || fs25.existsSync(path23.join(gitDir, "rebase-apply"))) {
6997
6999
  if (tryGit(["rebase", "--abort"], cwd)) aborted.push("rebase");
6998
7000
  }
6999
7001
  try {
@@ -7050,7 +7052,7 @@ function normalizeCommitMessage(raw) {
7050
7052
  function commitAndPush(branch, agentMessage, cwd) {
7051
7053
  const allChanged = listChangedFiles(cwd);
7052
7054
  const allowedFiles = allChanged.filter((f) => !isForbiddenPath(f));
7053
- const mergeHeadExists = fs24.existsSync(path22.join(cwd ?? process.cwd(), ".git", "MERGE_HEAD"));
7055
+ const mergeHeadExists = fs25.existsSync(path23.join(cwd ?? process.cwd(), ".git", "MERGE_HEAD"));
7054
7056
  if (allowedFiles.length === 0 && !mergeHeadExists) {
7055
7057
  return { committed: false, pushed: false, sha: "", message: "" };
7056
7058
  }
@@ -7690,13 +7692,13 @@ var init_state2 = __esm({
7690
7692
  });
7691
7693
 
7692
7694
  // src/goal/runLog.ts
7693
- import * as fs25 from "fs";
7695
+ import * as fs26 from "fs";
7694
7696
  function stageGoalRunLogEvent(data, goalId, event, at = nowIso()) {
7695
7697
  const logs = goalRunLogs(data);
7696
7698
  const existing = logs[goalId];
7697
- const path50 = existing?.path ?? goalRunLogPath(goalId, data);
7699
+ const path51 = existing?.path ?? goalRunLogPath(goalId, data);
7698
7700
  logs[goalId] = {
7699
- path: path50,
7701
+ path: path51,
7700
7702
  events: [...existing?.events ?? [], buildGoalRunLogEvent(data, goalId, event, at)]
7701
7703
  };
7702
7704
  }
@@ -8032,8 +8034,8 @@ function readGithubEvent() {
8032
8034
  const eventPath = process.env.GITHUB_EVENT_PATH;
8033
8035
  if (!eventPath) return null;
8034
8036
  try {
8035
- if (!fs25.existsSync(eventPath)) return null;
8036
- const parsed = JSON.parse(fs25.readFileSync(eventPath, "utf-8"));
8037
+ if (!fs26.existsSync(eventPath)) return null;
8038
+ const parsed = JSON.parse(fs26.readFileSync(eventPath, "utf-8"));
8037
8039
  return recordValue3(parsed);
8038
8040
  } catch {
8039
8041
  return null;
@@ -8143,8 +8145,8 @@ var init_stateStore = __esm({
8143
8145
  });
8144
8146
 
8145
8147
  // src/goal/targetLoopResolution.ts
8146
- import * as fs26 from "fs";
8147
- import * as path23 from "path";
8148
+ import * as fs27 from "fs";
8149
+ import * as path24 from "path";
8148
8150
  async function resolveActiveGoalLoopTarget(config, cwd, loopGoalId, loopGoal) {
8149
8151
  const targetId = loopGoal.loopTarget?.id.trim() ?? "";
8150
8152
  assertSafeGoalId(targetId, "loop target");
@@ -8222,11 +8224,11 @@ function goalInstanceTime(state) {
8222
8224
  return Number.isNaN(parsed) ? 0 : parsed;
8223
8225
  }
8224
8226
  function loadGoalTemplate(cwd, targetId) {
8225
- return readJsonObject(path23.join(cwd, ".kody-engine", "definitions", "goals", targetId, "state.json"));
8227
+ return readJsonObject(path24.join(cwd, ".kody-engine", "definitions", "goals", targetId, "state.json"));
8226
8228
  }
8227
8229
  function readJsonObject(filePath) {
8228
- if (!fs26.existsSync(filePath)) return null;
8229
- const parsed = JSON.parse(fs26.readFileSync(filePath, "utf8"));
8230
+ if (!fs27.existsSync(filePath)) return null;
8231
+ const parsed = JSON.parse(fs27.readFileSync(filePath, "utf8"));
8230
8232
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
8231
8233
  throw new Error(`goal template ${filePath} must be a JSON object`);
8232
8234
  }
@@ -8669,11 +8671,11 @@ function validateWorkflow(value, options = {}) {
8669
8671
  function formatWorkflowValidationIssues(issues) {
8670
8672
  return issues.map((entry) => `${entry.path}: ${entry.message}`);
8671
8673
  }
8672
- function validateDataMatch(value, path50, issues, capabilityOutputs) {
8674
+ function validateDataMatch(value, path51, issues, capabilityOutputs) {
8673
8675
  if (value === void 0) return;
8674
8676
  const match = asRecord2(value);
8675
8677
  if (!match || Object.keys(match).length === 0) {
8676
- issue(issues, "invalid_condition", path50, "workflow condition must contain at least one match");
8678
+ issue(issues, "invalid_condition", path51, "workflow condition must contain at least one match");
8677
8679
  return;
8678
8680
  }
8679
8681
  for (const [field, expected] of Object.entries(match)) {
@@ -8681,7 +8683,7 @@ function validateDataMatch(value, path50, issues, capabilityOutputs) {
8681
8683
  issue(
8682
8684
  issues,
8683
8685
  "invalid_data_path",
8684
- `${path50}.${field}`,
8686
+ `${path51}.${field}`,
8685
8687
  `workflow condition must read from facts, evidence, artifacts, result, workflow, or lastOutcome`
8686
8688
  );
8687
8689
  }
@@ -8689,12 +8691,12 @@ function validateDataMatch(value, path50, issues, capabilityOutputs) {
8689
8691
  issue(
8690
8692
  issues,
8691
8693
  "undeclared_result_path",
8692
- `${path50}.${field}`,
8694
+ `${path51}.${field}`,
8693
8695
  `workflow condition reads ${field}, but the source capability does not declare it`
8694
8696
  );
8695
8697
  }
8696
8698
  if (!isComparable(expected)) {
8697
- issue(issues, "invalid_condition_value", `${path50}.${field}`, "workflow condition value must be a JSON scalar");
8699
+ issue(issues, "invalid_condition_value", `${path51}.${field}`, "workflow condition value must be a JSON scalar");
8698
8700
  }
8699
8701
  }
8700
8702
  }
@@ -8712,8 +8714,8 @@ function isComparable(value) {
8712
8714
  if (value === null || ["string", "number", "boolean"].includes(typeof value)) return true;
8713
8715
  return Array.isArray(value) && value.length > 0 && value.every((item) => isComparable(item) && !Array.isArray(item));
8714
8716
  }
8715
- function issue(issues, code, path50, message) {
8716
- issues.push({ code, path: path50, message });
8717
+ function issue(issues, code, path51, message) {
8718
+ issues.push({ code, path: path51, message });
8717
8719
  }
8718
8720
  var SAFE_NAME, SAFE_STEP_ID, SAFE_DATA_PATH, SUPPORTED_STEP_FIELDS, SUPPORTED_TRANSITION_FIELDS;
8719
8721
  var init_workflowValidation = __esm({
@@ -8745,8 +8747,8 @@ var init_workflowValidation = __esm({
8745
8747
  });
8746
8748
 
8747
8749
  // src/workflowDefinitions.ts
8748
- import * as fs27 from "fs";
8749
- import * as path24 from "path";
8750
+ import * as fs28 from "fs";
8751
+ import * as path25 from "path";
8750
8752
  function isWorkflowDefinitionId(value) {
8751
8753
  return WORKFLOW_ID_PATTERN.test(value);
8752
8754
  }
@@ -8787,14 +8789,14 @@ function normalizeWorkflowDefinition(value) {
8787
8789
  };
8788
8790
  }
8789
8791
  function readWorkflowDefinition(_config, cwd, id) {
8790
- const filePath = path24.join(cwd ?? process.cwd(), ".kody-engine", "runtime", workflowDefinitionPath(id));
8791
- if (!fs27.existsSync(filePath)) return null;
8792
- return parseWorkflowDefinition(fs27.readFileSync(filePath, "utf8"));
8792
+ const filePath = path25.join(cwd ?? process.cwd(), ".kody-engine", "runtime", workflowDefinitionPath(id));
8793
+ if (!fs28.existsSync(filePath)) return null;
8794
+ return parseWorkflowDefinition(fs28.readFileSync(filePath, "utf8"));
8793
8795
  }
8794
8796
  function workflowDefinitionToCapabilityFolder(id, workflow, source = workflowDefinitionPath(id)) {
8795
8797
  return {
8796
8798
  slug: id,
8797
- dir: path24.dirname(source),
8799
+ dir: path25.dirname(source),
8798
8800
  profilePath: source,
8799
8801
  bodyPath: source,
8800
8802
  title: workflow.name,
@@ -8971,15 +8973,15 @@ var init_backendStateBackend = __esm({
8971
8973
  this.jobsDir = opts.jobsDir.replace(/\/+$/, "");
8972
8974
  }
8973
8975
  async load(slug) {
8974
- const path50 = stateFilePath(this.jobsDir, slug);
8976
+ const path51 = stateFilePath(this.jobsDir, slug);
8975
8977
  const loaded = await createStateBackendFromEnv().get(this.tenantId, `capabilities/${slug}`, "job-state");
8976
8978
  if (!loaded) {
8977
- return { path: path50, handle: null, state: initialStateEnvelope("seed"), created: true };
8979
+ return { path: path51, handle: null, state: initialStateEnvelope("seed"), created: true };
8978
8980
  }
8979
8981
  if (!isStateEnvelope(loaded.doc)) {
8980
8982
  throw new Error(`BackendStateBackend: capabilities/${slug} is not a StateEnvelope`);
8981
8983
  }
8982
- return { path: path50, handle: loaded.updatedAt, state: loaded.doc, created: false };
8984
+ return { path: path51, handle: loaded.updatedAt, state: loaded.doc, created: false };
8983
8985
  }
8984
8986
  async save(loaded, next) {
8985
8987
  if (!loaded.created && isStateUnchanged(loaded.state, next)) return false;
@@ -8999,8 +9001,8 @@ var init_backendStateBackend = __esm({
8999
9001
  });
9000
9002
 
9001
9003
  // src/scripts/jobState/localFileBackend.ts
9002
- import * as fs28 from "fs";
9003
- import * as path25 from "path";
9004
+ import * as fs29 from "fs";
9005
+ import * as path26 from "path";
9004
9006
  function sanitizeKey(s) {
9005
9007
  return s.replace(/[^A-Za-z0-9._-]/g, "-");
9006
9008
  }
@@ -9056,7 +9058,7 @@ var init_localFileBackend = __esm({
9056
9058
  if (!opts.owner || !opts.repo) throw new Error("LocalFileBackend: owner and repo are required");
9057
9059
  this.cwd = opts.cwd;
9058
9060
  this.jobsDir = opts.jobsDir;
9059
- this.absDir = path25.resolve(opts.cwd, opts.jobsDir);
9061
+ this.absDir = path26.resolve(opts.cwd, opts.jobsDir);
9060
9062
  this.owner = opts.owner;
9061
9063
  this.repo = opts.repo;
9062
9064
  this.cache = opts.cache ?? defaultCacheAdapter();
@@ -9071,7 +9073,7 @@ var init_localFileBackend = __esm({
9071
9073
  `);
9072
9074
  return;
9073
9075
  }
9074
- fs28.mkdirSync(this.absDir, { recursive: true });
9076
+ fs29.mkdirSync(this.absDir, { recursive: true });
9075
9077
  const prefix = this.cacheKeyPrefix();
9076
9078
  const probeKey = `${prefix}probe-${Date.now()}`;
9077
9079
  try {
@@ -9100,7 +9102,7 @@ var init_localFileBackend = __esm({
9100
9102
  `);
9101
9103
  return;
9102
9104
  }
9103
- if (!fs28.existsSync(this.absDir)) {
9105
+ if (!fs29.existsSync(this.absDir)) {
9104
9106
  return;
9105
9107
  }
9106
9108
  const key = `${this.cacheKeyPrefix()}${process.env.GITHUB_RUN_ID ?? "norunid"}-${Date.now()}`;
@@ -9116,11 +9118,11 @@ var init_localFileBackend = __esm({
9116
9118
  }
9117
9119
  load(slug) {
9118
9120
  const relPath = stateFilePath(this.jobsDir, slug);
9119
- const absPath = path25.resolve(this.cwd, relPath);
9120
- if (!fs28.existsSync(absPath)) {
9121
+ const absPath = path26.resolve(this.cwd, relPath);
9122
+ if (!fs29.existsSync(absPath)) {
9121
9123
  return { path: relPath, handle: null, state: initialStateEnvelope("seed"), created: true };
9122
9124
  }
9123
- const raw = fs28.readFileSync(absPath, "utf-8");
9125
+ const raw = fs29.readFileSync(absPath, "utf-8");
9124
9126
  let parsed;
9125
9127
  try {
9126
9128
  parsed = JSON.parse(raw);
@@ -9137,13 +9139,13 @@ var init_localFileBackend = __esm({
9137
9139
  if (!loaded.created && isStateUnchanged(loaded.state, next)) {
9138
9140
  return false;
9139
9141
  }
9140
- const absPath = path25.resolve(this.cwd, loaded.path);
9141
- fs28.mkdirSync(path25.dirname(absPath), { recursive: true });
9142
+ const absPath = path26.resolve(this.cwd, loaded.path);
9143
+ fs29.mkdirSync(path26.dirname(absPath), { recursive: true });
9142
9144
  const body = `${JSON.stringify(next, null, 2)}
9143
9145
  `;
9144
9146
  const tmpPath = `${absPath}.${process.pid}.tmp`;
9145
- fs28.writeFileSync(tmpPath, body, "utf-8");
9146
- fs28.renameSync(tmpPath, absPath);
9147
+ fs29.writeFileSync(tmpPath, body, "utf-8");
9148
+ fs29.renameSync(tmpPath, absPath);
9147
9149
  return true;
9148
9150
  }
9149
9151
  cacheKeyPrefix() {
@@ -9175,7 +9177,7 @@ var init_jobState = __esm({
9175
9177
  });
9176
9178
 
9177
9179
  // src/scripts/goalCapabilityScheduling.ts
9178
- import * as path26 from "path";
9180
+ import * as path27 from "path";
9179
9181
  function isCapabilityCadenceGoal(goal, extra) {
9180
9182
  return extra.scheduleMode === "agentLoop" || extra.scheduler === "agentLoop" || goal.type === "standing" && goal.capabilities.length > 0;
9181
9183
  }
@@ -9231,7 +9233,7 @@ function planTargetLoopSchedule(opts) {
9231
9233
  }
9232
9234
  async function planGoalCapabilitySchedule(opts) {
9233
9235
  const jobsDir = opts.jobsDir ?? capabilitiesRoot(opts.cwd);
9234
- const jobsRoot = path26.resolve(opts.cwd, jobsDir);
9236
+ const jobsRoot = path27.resolve(opts.cwd, jobsDir);
9235
9237
  const now = opts.now ?? /* @__PURE__ */ new Date();
9236
9238
  const at = now.toISOString();
9237
9239
  const backend = resolveBackend({ config: opts.config, cwd: opts.cwd, jobsDir });
@@ -11046,8 +11048,8 @@ var init_classifyByLabel = __esm({
11046
11048
  });
11047
11049
 
11048
11050
  // src/scripts/commitAndPush.ts
11049
- import * as fs29 from "fs";
11050
- import * as path27 from "path";
11051
+ import * as fs30 from "fs";
11052
+ import * as path28 from "path";
11051
11053
  function sentinelPathForStage(cwd, profileName) {
11052
11054
  const runId = resolveRunId();
11053
11055
  return runtimeStatePath(cwd, "agent-runs", runId, `commit-${profileName}.lock`);
@@ -11068,9 +11070,9 @@ var init_commitAndPush = __esm({
11068
11070
  }
11069
11071
  const idempotencyEnabled = process.env.KODY_COMMIT_IDEMPOTENCY !== "0";
11070
11072
  const sentinel = idempotencyEnabled ? sentinelPathForStage(ctx.cwd, profile.name) : null;
11071
- if (sentinel && fs29.existsSync(sentinel)) {
11073
+ if (sentinel && fs30.existsSync(sentinel)) {
11072
11074
  try {
11073
- const replay = JSON.parse(fs29.readFileSync(sentinel, "utf-8"));
11075
+ const replay = JSON.parse(fs30.readFileSync(sentinel, "utf-8"));
11074
11076
  ctx.data.commitResult = replay.commitResult ?? { committed: false, pushed: false };
11075
11077
  if (Array.isArray(replay.changedFiles)) ctx.data.changedFiles = replay.changedFiles;
11076
11078
  if (typeof replay.hasCommitsAhead === "boolean") ctx.data.hasCommitsAhead = replay.hasCommitsAhead;
@@ -11130,8 +11132,8 @@ var init_commitAndPush = __esm({
11130
11132
  const result = ctx.data.commitResult;
11131
11133
  if (sentinel && result?.committed) {
11132
11134
  try {
11133
- fs29.mkdirSync(path27.dirname(sentinel), { recursive: true });
11134
- fs29.writeFileSync(
11135
+ fs30.mkdirSync(path28.dirname(sentinel), { recursive: true });
11136
+ fs30.writeFileSync(
11135
11137
  sentinel,
11136
11138
  JSON.stringify(
11137
11139
  {
@@ -11225,8 +11227,8 @@ var init_commitGoalState = __esm({
11225
11227
  });
11226
11228
 
11227
11229
  // src/scripts/composePrompt.ts
11228
- import * as fs30 from "fs";
11229
- import * as path28 from "path";
11230
+ import * as fs31 from "fs";
11231
+ import * as path29 from "path";
11230
11232
  function fenceUntrusted(value) {
11231
11233
  if (value.trim().length === 0) return value;
11232
11234
  const safe = value.replace(/-{3,}\s*END UNTRUSTED INPUT\s*-{3,}/gi, "[END UNTRUSTED INPUT]");
@@ -11350,10 +11352,10 @@ var init_composePrompt = __esm({
11350
11352
  const explicit = ctx.data.promptTemplate;
11351
11353
  const mode = ctx.args.mode;
11352
11354
  const candidates = [
11353
- explicit ? path28.join(profile.dir, explicit) : null,
11354
- mode ? path28.join(profile.dir, "prompts", `${mode}.md`) : null,
11355
- path28.join(profile.dir, "prompt.md"),
11356
- path28.join(profile.dir, "capability.md")
11355
+ explicit ? path29.join(profile.dir, explicit) : null,
11356
+ mode ? path29.join(profile.dir, "prompts", `${mode}.md`) : null,
11357
+ path29.join(profile.dir, "prompt.md"),
11358
+ path29.join(profile.dir, "capability.md")
11357
11359
  ].filter(Boolean);
11358
11360
  let templatePath = "";
11359
11361
  let template = "";
@@ -11366,7 +11368,7 @@ var init_composePrompt = __esm({
11366
11368
  break;
11367
11369
  }
11368
11370
  try {
11369
- template = fs30.readFileSync(c, "utf-8");
11371
+ template = fs31.readFileSync(c, "utf-8");
11370
11372
  templatePath = c;
11371
11373
  break;
11372
11374
  } catch (err) {
@@ -11377,7 +11379,7 @@ var init_composePrompt = __esm({
11377
11379
  if (!templatePath) {
11378
11380
  let dirState;
11379
11381
  try {
11380
- dirState = `dir contents: [${fs30.readdirSync(profile.dir).join(", ")}]`;
11382
+ dirState = `dir contents: [${fs31.readdirSync(profile.dir).join(", ")}]`;
11381
11383
  } catch (err) {
11382
11384
  dirState = `readdir(${profile.dir}) failed: ${err?.code ?? String(err)}`;
11383
11385
  }
@@ -12066,19 +12068,19 @@ var init_deriveQaScopeFromIssue = __esm({
12066
12068
 
12067
12069
  // src/scripts/diagMcp.ts
12068
12070
  import { execFileSync as execFileSync9 } from "child_process";
12069
- import * as fs31 from "fs";
12071
+ import * as fs32 from "fs";
12070
12072
  import * as os5 from "os";
12071
- import * as path29 from "path";
12073
+ import * as path30 from "path";
12072
12074
  var diagMcp;
12073
12075
  var init_diagMcp = __esm({
12074
12076
  "src/scripts/diagMcp.ts"() {
12075
12077
  "use strict";
12076
12078
  diagMcp = async (_ctx) => {
12077
12079
  const home = os5.homedir();
12078
- const cacheDir = path29.join(home, ".cache", "ms-playwright");
12080
+ const cacheDir = path30.join(home, ".cache", "ms-playwright");
12079
12081
  let entries = [];
12080
12082
  try {
12081
- entries = fs31.readdirSync(cacheDir);
12083
+ entries = fs32.readdirSync(cacheDir);
12082
12084
  } catch {
12083
12085
  }
12084
12086
  const hasChromium = entries.some((e) => e.startsWith("chromium"));
@@ -12106,13 +12108,13 @@ var init_diagMcp = __esm({
12106
12108
  });
12107
12109
 
12108
12110
  // src/scripts/frameworkDetectors.ts
12109
- import * as fs32 from "fs";
12110
- import * as path30 from "path";
12111
+ import * as fs33 from "fs";
12112
+ import * as path31 from "path";
12111
12113
  function detectFrameworks(cwd) {
12112
12114
  const out = [];
12113
12115
  let deps = {};
12114
12116
  try {
12115
- const pkg = JSON.parse(fs32.readFileSync(path30.join(cwd, "package.json"), "utf-8"));
12117
+ const pkg = JSON.parse(fs33.readFileSync(path31.join(cwd, "package.json"), "utf-8"));
12116
12118
  deps = { ...pkg.dependencies, ...pkg.devDependencies };
12117
12119
  } catch {
12118
12120
  return out;
@@ -12149,25 +12151,25 @@ function detectFrameworks(cwd) {
12149
12151
  }
12150
12152
  function findFile(cwd, candidates) {
12151
12153
  for (const c of candidates) {
12152
- if (fs32.existsSync(path30.join(cwd, c))) return c;
12154
+ if (fs33.existsSync(path31.join(cwd, c))) return c;
12153
12155
  }
12154
12156
  return null;
12155
12157
  }
12156
12158
  function discoverPayloadCollections(cwd) {
12157
12159
  const out = [];
12158
12160
  for (const dir of COLLECTION_DIRS) {
12159
- const full = path30.join(cwd, dir);
12160
- if (!fs32.existsSync(full)) continue;
12161
+ const full = path31.join(cwd, dir);
12162
+ if (!fs33.existsSync(full)) continue;
12161
12163
  let files;
12162
12164
  try {
12163
- files = fs32.readdirSync(full).filter((f) => f.endsWith(".ts") || f.endsWith(".tsx"));
12165
+ files = fs33.readdirSync(full).filter((f) => f.endsWith(".ts") || f.endsWith(".tsx"));
12164
12166
  } catch {
12165
12167
  continue;
12166
12168
  }
12167
12169
  for (const file of files) {
12168
12170
  try {
12169
- const filePath = path30.join(full, file);
12170
- const content = fs32.readFileSync(filePath, "utf-8").slice(0, 1e4);
12171
+ const filePath = path31.join(full, file);
12172
+ const content = fs33.readFileSync(filePath, "utf-8").slice(0, 1e4);
12171
12173
  const slugMatch = content.match(/slug:\s*['"]([a-z0-9-]+)['"]/);
12172
12174
  if (!slugMatch) continue;
12173
12175
  const slug = slugMatch[1];
@@ -12181,7 +12183,7 @@ function discoverPayloadCollections(cwd) {
12181
12183
  out.push({
12182
12184
  name,
12183
12185
  slug,
12184
- filePath: path30.relative(cwd, filePath),
12186
+ filePath: path31.relative(cwd, filePath),
12185
12187
  fields: fields.slice(0, 20),
12186
12188
  hasAdmin
12187
12189
  });
@@ -12194,28 +12196,28 @@ function discoverPayloadCollections(cwd) {
12194
12196
  function discoverAdminComponents(cwd, collections) {
12195
12197
  const out = [];
12196
12198
  for (const dir of ADMIN_COMPONENT_DIRS) {
12197
- const full = path30.join(cwd, dir);
12198
- if (!fs32.existsSync(full)) continue;
12199
+ const full = path31.join(cwd, dir);
12200
+ if (!fs33.existsSync(full)) continue;
12199
12201
  let entries;
12200
12202
  try {
12201
- entries = fs32.readdirSync(full, { withFileTypes: true });
12203
+ entries = fs33.readdirSync(full, { withFileTypes: true });
12202
12204
  } catch {
12203
12205
  continue;
12204
12206
  }
12205
12207
  for (const entry of entries) {
12206
- const entryPath = path30.join(full, entry.name);
12208
+ const entryPath = path31.join(full, entry.name);
12207
12209
  let name;
12208
12210
  let filePath;
12209
12211
  if (entry.isDirectory()) {
12210
12212
  const indexFile = ["index.tsx", "index.ts", "index.jsx", "index.js"].find(
12211
- (f) => fs32.existsSync(path30.join(entryPath, f))
12213
+ (f) => fs33.existsSync(path31.join(entryPath, f))
12212
12214
  );
12213
12215
  if (!indexFile) continue;
12214
12216
  name = entry.name;
12215
- filePath = path30.relative(cwd, path30.join(entryPath, indexFile));
12217
+ filePath = path31.relative(cwd, path31.join(entryPath, indexFile));
12216
12218
  } else if (/\.(tsx?|jsx?)$/.test(entry.name)) {
12217
12219
  name = entry.name.replace(/\.(tsx?|jsx?)$/, "");
12218
- filePath = path30.relative(cwd, entryPath);
12220
+ filePath = path31.relative(cwd, entryPath);
12219
12221
  } else {
12220
12222
  continue;
12221
12223
  }
@@ -12223,7 +12225,7 @@ function discoverAdminComponents(cwd, collections) {
12223
12225
  if (collections) {
12224
12226
  for (const col of collections) {
12225
12227
  try {
12226
- const colContent = fs32.readFileSync(path30.join(cwd, col.filePath), "utf-8");
12228
+ const colContent = fs33.readFileSync(path31.join(cwd, col.filePath), "utf-8");
12227
12229
  if (colContent.includes(name)) {
12228
12230
  usedInCollection = col.slug;
12229
12231
  break;
@@ -12241,8 +12243,8 @@ function scanApiRoutes(cwd) {
12241
12243
  const out = [];
12242
12244
  const appDirs = ["src/app", "app"];
12243
12245
  for (const appDir of appDirs) {
12244
- const apiDir = path30.join(cwd, appDir, "api");
12245
- if (!fs32.existsSync(apiDir)) continue;
12246
+ const apiDir = path31.join(cwd, appDir, "api");
12247
+ if (!fs33.existsSync(apiDir)) continue;
12246
12248
  walkApiRoutes(apiDir, "/api", cwd, out);
12247
12249
  break;
12248
12250
  }
@@ -12251,14 +12253,14 @@ function scanApiRoutes(cwd) {
12251
12253
  function walkApiRoutes(dir, prefix, cwd, out) {
12252
12254
  let entries;
12253
12255
  try {
12254
- entries = fs32.readdirSync(dir, { withFileTypes: true });
12256
+ entries = fs33.readdirSync(dir, { withFileTypes: true });
12255
12257
  } catch {
12256
12258
  return;
12257
12259
  }
12258
12260
  const routeFile = entries.find((e) => e.isFile() && /^route\.(ts|js|tsx|jsx)$/.test(e.name));
12259
12261
  if (routeFile) {
12260
12262
  try {
12261
- const content = fs32.readFileSync(path30.join(dir, routeFile.name), "utf-8").slice(0, 5e3);
12263
+ const content = fs33.readFileSync(path31.join(dir, routeFile.name), "utf-8").slice(0, 5e3);
12262
12264
  const methods = HTTP_METHODS.filter(
12263
12265
  (m) => new RegExp(`export\\s+(?:async\\s+)?function\\s+${m}\\b`).test(content)
12264
12266
  );
@@ -12266,7 +12268,7 @@ function walkApiRoutes(dir, prefix, cwd, out) {
12266
12268
  out.push({
12267
12269
  path: prefix,
12268
12270
  methods,
12269
- filePath: path30.relative(cwd, path30.join(dir, routeFile.name))
12271
+ filePath: path31.relative(cwd, path31.join(dir, routeFile.name))
12270
12272
  });
12271
12273
  }
12272
12274
  } catch {
@@ -12277,7 +12279,7 @@ function walkApiRoutes(dir, prefix, cwd, out) {
12277
12279
  if (entry.name === "node_modules" || entry.name === ".next") continue;
12278
12280
  let segment = entry.name;
12279
12281
  if (segment.startsWith("(") && segment.endsWith(")")) {
12280
- walkApiRoutes(path30.join(dir, entry.name), prefix, cwd, out);
12282
+ walkApiRoutes(path31.join(dir, entry.name), prefix, cwd, out);
12281
12283
  continue;
12282
12284
  }
12283
12285
  if (segment.startsWith("[[") && segment.endsWith("]]")) {
@@ -12285,16 +12287,16 @@ function walkApiRoutes(dir, prefix, cwd, out) {
12285
12287
  } else if (segment.startsWith("[") && segment.endsWith("]")) {
12286
12288
  segment = `:${segment.slice(1, -1)}`;
12287
12289
  }
12288
- walkApiRoutes(path30.join(dir, entry.name), `${prefix}/${segment}`, cwd, out);
12290
+ walkApiRoutes(path31.join(dir, entry.name), `${prefix}/${segment}`, cwd, out);
12289
12291
  }
12290
12292
  }
12291
12293
  function scanEnvVars(cwd) {
12292
12294
  const candidates = [".env.example", ".env.local.example", ".env.template"];
12293
12295
  for (const envFile of candidates) {
12294
- const envPath = path30.join(cwd, envFile);
12295
- if (!fs32.existsSync(envPath)) continue;
12296
+ const envPath = path31.join(cwd, envFile);
12297
+ if (!fs33.existsSync(envPath)) continue;
12296
12298
  try {
12297
- const content = fs32.readFileSync(envPath, "utf-8");
12299
+ const content = fs33.readFileSync(envPath, "utf-8");
12298
12300
  const vars = [];
12299
12301
  for (const line of content.split("\n")) {
12300
12302
  const trimmed = line.trim();
@@ -12339,8 +12341,8 @@ var init_frameworkDetectors = __esm({
12339
12341
  });
12340
12342
 
12341
12343
  // src/scripts/discoverQaContext.ts
12342
- import * as fs33 from "fs";
12343
- import * as path31 from "path";
12344
+ import * as fs34 from "fs";
12345
+ import * as path32 from "path";
12344
12346
  function runQaDiscovery(cwd) {
12345
12347
  const out = {
12346
12348
  routes: [],
@@ -12371,9 +12373,9 @@ function runQaDiscovery(cwd) {
12371
12373
  }
12372
12374
  function detectDevServer(cwd, out) {
12373
12375
  try {
12374
- const pkg = JSON.parse(fs33.readFileSync(path31.join(cwd, "package.json"), "utf-8"));
12376
+ const pkg = JSON.parse(fs34.readFileSync(path32.join(cwd, "package.json"), "utf-8"));
12375
12377
  const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
12376
- const pm = fs33.existsSync(path31.join(cwd, "pnpm-lock.yaml")) ? "pnpm" : fs33.existsSync(path31.join(cwd, "yarn.lock")) ? "yarn" : fs33.existsSync(path31.join(cwd, "bun.lockb")) ? "bun" : "npm";
12378
+ const pm = fs34.existsSync(path32.join(cwd, "pnpm-lock.yaml")) ? "pnpm" : fs34.existsSync(path32.join(cwd, "yarn.lock")) ? "yarn" : fs34.existsSync(path32.join(cwd, "bun.lockb")) ? "bun" : "npm";
12377
12379
  if (pkg.scripts?.dev) out.devCommand = `${pm} dev`;
12378
12380
  if (allDeps.next || allDeps.nuxt) out.devPort = 3e3;
12379
12381
  else if (allDeps.vite) out.devPort = 5173;
@@ -12383,8 +12385,8 @@ function detectDevServer(cwd, out) {
12383
12385
  function scanFrontendRoutes(cwd, out) {
12384
12386
  const appDirs = ["src/app", "app"];
12385
12387
  for (const appDir of appDirs) {
12386
- const full = path31.join(cwd, appDir);
12387
- if (!fs33.existsSync(full)) continue;
12388
+ const full = path32.join(cwd, appDir);
12389
+ if (!fs34.existsSync(full)) continue;
12388
12390
  walkFrontendRoutes(full, "", out);
12389
12391
  break;
12390
12392
  }
@@ -12392,7 +12394,7 @@ function scanFrontendRoutes(cwd, out) {
12392
12394
  function walkFrontendRoutes(dir, prefix, out) {
12393
12395
  let entries;
12394
12396
  try {
12395
- entries = fs33.readdirSync(dir, { withFileTypes: true });
12397
+ entries = fs34.readdirSync(dir, { withFileTypes: true });
12396
12398
  } catch {
12397
12399
  return;
12398
12400
  }
@@ -12409,7 +12411,7 @@ function walkFrontendRoutes(dir, prefix, out) {
12409
12411
  if (entry.name === "node_modules" || entry.name === ".next") continue;
12410
12412
  let segment = entry.name;
12411
12413
  if (segment.startsWith("(") && segment.endsWith(")")) {
12412
- walkFrontendRoutes(path31.join(dir, entry.name), prefix, out);
12414
+ walkFrontendRoutes(path32.join(dir, entry.name), prefix, out);
12413
12415
  continue;
12414
12416
  }
12415
12417
  if (segment.startsWith("[[") && segment.endsWith("]]")) {
@@ -12417,7 +12419,7 @@ function walkFrontendRoutes(dir, prefix, out) {
12417
12419
  } else if (segment.startsWith("[") && segment.endsWith("]")) {
12418
12420
  segment = `:${segment.slice(1, -1)}`;
12419
12421
  }
12420
- walkFrontendRoutes(path31.join(dir, entry.name), `${prefix}/${segment}`, out);
12422
+ walkFrontendRoutes(path32.join(dir, entry.name), `${prefix}/${segment}`, out);
12421
12423
  }
12422
12424
  }
12423
12425
  function detectAuthFiles(cwd, out) {
@@ -12434,23 +12436,23 @@ function detectAuthFiles(cwd, out) {
12434
12436
  "src/app/api/oauth"
12435
12437
  ];
12436
12438
  for (const c of candidates) {
12437
- if (fs33.existsSync(path31.join(cwd, c))) out.authFiles.push(c);
12439
+ if (fs34.existsSync(path32.join(cwd, c))) out.authFiles.push(c);
12438
12440
  }
12439
12441
  }
12440
12442
  function detectRoles(cwd, out) {
12441
12443
  const rolePaths = ["src/types", "src/lib", "src/utils", "src/constants", "src/access", "src/collections"];
12442
12444
  for (const rp of rolePaths) {
12443
- const dir = path31.join(cwd, rp);
12444
- if (!fs33.existsSync(dir)) continue;
12445
+ const dir = path32.join(cwd, rp);
12446
+ if (!fs34.existsSync(dir)) continue;
12445
12447
  let files;
12446
12448
  try {
12447
- files = fs33.readdirSync(dir).filter((f) => f.endsWith(".ts") || f.endsWith(".tsx"));
12449
+ files = fs34.readdirSync(dir).filter((f) => f.endsWith(".ts") || f.endsWith(".tsx"));
12448
12450
  } catch {
12449
12451
  continue;
12450
12452
  }
12451
12453
  for (const f of files) {
12452
12454
  try {
12453
- const content = fs33.readFileSync(path31.join(dir, f), "utf-8").slice(0, 5e3);
12455
+ const content = fs34.readFileSync(path32.join(dir, f), "utf-8").slice(0, 5e3);
12454
12456
  const roleMatches = content.match(/(?:role|Role|ROLE)\s*[=:]\s*['"](\w+)['"]/g);
12455
12457
  if (roleMatches) {
12456
12458
  for (const m of roleMatches) {
@@ -13731,12 +13733,12 @@ var init_fixFlow = __esm({
13731
13733
 
13732
13734
  // src/scripts/initFlow.ts
13733
13735
  import { execFileSync as execFileSync14 } from "child_process";
13734
- import * as fs34 from "fs";
13735
- import * as path32 from "path";
13736
+ import * as fs35 from "fs";
13737
+ import * as path33 from "path";
13736
13738
  function detectPackageManager(cwd) {
13737
- if (fs34.existsSync(path32.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
13738
- if (fs34.existsSync(path32.join(cwd, "yarn.lock"))) return "yarn";
13739
- if (fs34.existsSync(path32.join(cwd, "bun.lockb"))) return "bun";
13739
+ if (fs35.existsSync(path33.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
13740
+ if (fs35.existsSync(path33.join(cwd, "yarn.lock"))) return "yarn";
13741
+ if (fs35.existsSync(path33.join(cwd, "bun.lockb"))) return "bun";
13740
13742
  return "npm";
13741
13743
  }
13742
13744
  function qualityCommandsFor(pm) {
@@ -13808,22 +13810,22 @@ function performInit(cwd, force) {
13808
13810
  const pm = detectPackageManager(cwd);
13809
13811
  const ownerRepo = detectOwnerRepo(cwd);
13810
13812
  const defaultBranch = defaultBranchFromGit(cwd);
13811
- const configPath = path32.join(cwd, "kody.config.json");
13812
- if (fs34.existsSync(configPath) && !force) {
13813
+ const configPath = path33.join(cwd, "kody.config.json");
13814
+ if (fs35.existsSync(configPath) && !force) {
13813
13815
  skipped.push("kody.config.json");
13814
13816
  } else {
13815
13817
  const cfg = makeConfig(pm, ownerRepo, defaultBranch);
13816
- fs34.writeFileSync(configPath, `${JSON.stringify(cfg, null, 2)}
13818
+ fs35.writeFileSync(configPath, `${JSON.stringify(cfg, null, 2)}
13817
13819
  `);
13818
13820
  wrote.push("kody.config.json");
13819
13821
  }
13820
- const workflowDir = path32.join(cwd, ".github", "workflows");
13821
- const workflowPath = path32.join(workflowDir, "kody.yml");
13822
- if (fs34.existsSync(workflowPath) && !force) {
13822
+ const workflowDir = path33.join(cwd, ".github", "workflows");
13823
+ const workflowPath = path33.join(workflowDir, "kody.yml");
13824
+ if (fs35.existsSync(workflowPath) && !force) {
13823
13825
  skipped.push(".github/workflows/kody.yml");
13824
13826
  } else {
13825
- fs34.mkdirSync(workflowDir, { recursive: true });
13826
- fs34.writeFileSync(workflowPath, WORKFLOW_TEMPLATE);
13827
+ fs35.mkdirSync(workflowDir, { recursive: true });
13828
+ fs35.writeFileSync(workflowPath, WORKFLOW_TEMPLATE);
13827
13829
  wrote.push(".github/workflows/kody.yml");
13828
13830
  }
13829
13831
  for (const exe of listImplementations()) {
@@ -13834,12 +13836,12 @@ function performInit(cwd, force) {
13834
13836
  continue;
13835
13837
  }
13836
13838
  if (profile.kind !== "scheduled" || !profile.schedule) continue;
13837
- const target = path32.join(workflowDir, `kody-${exe.name}.yml`);
13838
- if (fs34.existsSync(target) && !force) {
13839
+ const target = path33.join(workflowDir, `kody-${exe.name}.yml`);
13840
+ if (fs35.existsSync(target) && !force) {
13839
13841
  skipped.push(`.github/workflows/kody-${exe.name}.yml`);
13840
13842
  continue;
13841
13843
  }
13842
- fs34.writeFileSync(target, renderScheduledWorkflow(exe.name, profile.schedule));
13844
+ fs35.writeFileSync(target, renderScheduledWorkflow(exe.name, profile.schedule));
13843
13845
  wrote.push(`.github/workflows/kody-${exe.name}.yml`);
13844
13846
  }
13845
13847
  let labels;
@@ -13987,7 +13989,7 @@ Nothing to do. All files already present. (Use --force to overwrite.)
13987
13989
  });
13988
13990
 
13989
13991
  // src/scripts/loadAgentAdhoc.ts
13990
- import * as fs35 from "fs";
13992
+ import * as fs36 from "fs";
13991
13993
  function resolveMessage(messageArg) {
13992
13994
  const fromComment = readCommentBody();
13993
13995
  if (fromComment) return stripDirective(fromComment);
@@ -13995,9 +13997,9 @@ function resolveMessage(messageArg) {
13995
13997
  }
13996
13998
  function readCommentBody() {
13997
13999
  const eventPath = process.env.GITHUB_EVENT_PATH;
13998
- if (!eventPath || !fs35.existsSync(eventPath)) return "";
14000
+ if (!eventPath || !fs36.existsSync(eventPath)) return "";
13999
14001
  try {
14000
- const event = JSON.parse(fs35.readFileSync(eventPath, "utf-8"));
14002
+ const event = JSON.parse(fs36.readFileSync(eventPath, "utf-8"));
14001
14003
  return String(event.comment?.body ?? "");
14002
14004
  } catch {
14003
14005
  return "";
@@ -14051,10 +14053,10 @@ var init_loadAgentAdhoc = __esm({
14051
14053
  throw new Error("loadAgentAdhoc: ctx.args.agent must be a non-empty slug");
14052
14054
  }
14053
14055
  const agentPath = resolveAgentFile(ctx.cwd, agentSlug, agentsDir);
14054
- if (!fs35.existsSync(agentPath)) {
14056
+ if (!fs36.existsSync(agentPath)) {
14055
14057
  throw new Error(`loadAgentAdhoc: agent identity not found: ${agentPath}`);
14056
14058
  }
14057
- const { title, body } = parseAgentFile(fs35.readFileSync(agentPath, "utf-8"), agentSlug);
14059
+ const { title, body } = parseAgentFile(fs36.readFileSync(agentPath, "utf-8"), agentSlug);
14058
14060
  const message = resolveMessage(ctx.args.message);
14059
14061
  if (!message) {
14060
14062
  throw new Error(
@@ -14126,13 +14128,13 @@ var init_loadCapabilityState = __esm({
14126
14128
  function isCompanyIntentId(value) {
14127
14129
  return SLUG_RE.test(value);
14128
14130
  }
14129
- function normalizeCompanyIntent(path50, raw) {
14131
+ function normalizeCompanyIntent(path51, raw) {
14130
14132
  if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
14131
- throw new Error(`${path50}: intent must be JSON object`);
14133
+ throw new Error(`${path51}: intent must be JSON object`);
14132
14134
  }
14133
14135
  const input = raw;
14134
14136
  const id = stringField4(input.id);
14135
- if (!id || !isCompanyIntentId(id)) throw new Error(`${path50}: invalid intent id`);
14137
+ if (!id || !isCompanyIntentId(id)) throw new Error(`${path51}: invalid intent id`);
14136
14138
  const createdAt = stringField4(input.createdAt) || nowIso();
14137
14139
  const updatedAt = stringField4(input.updatedAt) || createdAt;
14138
14140
  const description = stringField4(input.description);
@@ -14423,8 +14425,8 @@ var init_loadIssueStateComment = __esm({
14423
14425
  });
14424
14426
 
14425
14427
  // src/scripts/loadJobFromFile.ts
14426
- import * as fs36 from "fs";
14427
- import * as path33 from "path";
14428
+ import * as fs37 from "fs";
14429
+ import * as path34 from "path";
14428
14430
  function parseJobFile(raw, slug) {
14429
14431
  let stripped = raw;
14430
14432
  if (stripped.startsWith("---\n")) {
@@ -14463,10 +14465,10 @@ var init_loadJobFromFile = __esm({
14463
14465
  if (!slug) {
14464
14466
  throw new Error(`loadJobFromFile: ctx.args.${slugArg} must be a non-empty slug`);
14465
14467
  }
14466
- const capability = resolveCapabilityFolder(slug, path33.resolve(ctx.cwd, jobsDir));
14468
+ const capability = resolveCapabilityFolder(slug, path34.resolve(ctx.cwd, jobsDir));
14467
14469
  if (!capability) {
14468
14470
  throw new Error(
14469
- `loadJobFromFile: capability folder not found or incomplete: ${path33.resolve(ctx.cwd, jobsDir, slug)}`
14471
+ `loadJobFromFile: capability folder not found or incomplete: ${path34.resolve(ctx.cwd, jobsDir, slug)}`
14470
14472
  );
14471
14473
  }
14472
14474
  const { title, body, config } = capability;
@@ -14476,12 +14478,12 @@ var init_loadJobFromFile = __esm({
14476
14478
  let agentIdentity = "";
14477
14479
  if (agentSlug) {
14478
14480
  const agentPath = resolveAgentFile(ctx.cwd, agentSlug, agentsDir);
14479
- if (!fs36.existsSync(agentPath)) {
14481
+ if (!fs37.existsSync(agentPath)) {
14480
14482
  throw new Error(
14481
14483
  `loadJobFromFile: capability '${slug}' declares agent '${agentSlug}' but ${agentPath} does not exist`
14482
14484
  );
14483
14485
  }
14484
- const agentRaw = fs36.readFileSync(agentPath, "utf-8");
14486
+ const agentRaw = fs37.readFileSync(agentPath, "utf-8");
14485
14487
  const parsed = parseJobFile(agentRaw, agentSlug);
14486
14488
  agentTitle = parsed.title;
14487
14489
  agentIdentity = parsed.body;
@@ -14561,13 +14563,13 @@ ${truncate2(issue2.body, FINDING_BODY_MAX_BYTES)}`;
14561
14563
  });
14562
14564
 
14563
14565
  // src/scripts/kodyVariables.ts
14564
- import * as fs37 from "fs";
14565
- import * as path34 from "path";
14566
+ import * as fs38 from "fs";
14567
+ import * as path35 from "path";
14566
14568
  function readKodyVariables(cwd) {
14567
- const full = path34.join(cwd, KODY_VARIABLES_REL_PATH);
14569
+ const full = path35.join(cwd, KODY_VARIABLES_REL_PATH);
14568
14570
  let raw;
14569
14571
  try {
14570
- raw = fs37.readFileSync(full, "utf-8");
14572
+ raw = fs38.readFileSync(full, "utf-8");
14571
14573
  } catch {
14572
14574
  return {};
14573
14575
  }
@@ -14730,8 +14732,8 @@ var init_runtimeSecrets = __esm({
14730
14732
  });
14731
14733
 
14732
14734
  // src/scripts/loadQaContext.ts
14733
- import * as fs38 from "fs";
14734
- import * as path35 from "path";
14735
+ import * as fs39 from "fs";
14736
+ import * as path36 from "path";
14735
14737
  function parseSlugList(value) {
14736
14738
  const inner = value.startsWith("[") && value.endsWith("]") ? value.slice(1, -1) : value;
14737
14739
  return inner.split(",").map(
@@ -14760,18 +14762,18 @@ function readProfileAgents(raw) {
14760
14762
  return { agent: agent ?? legacy ?? ["kody"], body };
14761
14763
  }
14762
14764
  function readProfile(cwd) {
14763
- const dir = path35.join(cwd, CONTEXT_DIR_REL_PATH);
14764
- if (!fs38.existsSync(dir)) return "";
14765
+ const dir = path36.join(cwd, CONTEXT_DIR_REL_PATH);
14766
+ if (!fs39.existsSync(dir)) return "";
14765
14767
  let entries;
14766
14768
  try {
14767
- entries = fs38.readdirSync(dir).filter((f) => f.endsWith(".md")).sort();
14769
+ entries = fs39.readdirSync(dir).filter((f) => f.endsWith(".md")).sort();
14768
14770
  } catch {
14769
14771
  return "";
14770
14772
  }
14771
14773
  const blocks = [];
14772
14774
  for (const file of entries) {
14773
14775
  try {
14774
- const raw = fs38.readFileSync(path35.join(dir, file), "utf-8");
14776
+ const raw = fs39.readFileSync(path36.join(dir, file), "utf-8");
14775
14777
  const { agent, body } = readProfileAgents(raw);
14776
14778
  if (!agent.includes(QA_AGENT) && !agent.includes(ALL_AGENTS)) continue;
14777
14779
  blocks.push(`## ${file}
@@ -14820,8 +14822,8 @@ var init_loadQaContext = __esm({
14820
14822
  });
14821
14823
 
14822
14824
  // src/taskContext.ts
14823
- import * as fs39 from "fs";
14824
- import * as path36 from "path";
14825
+ import * as fs40 from "fs";
14826
+ import * as path37 from "path";
14825
14827
  function buildTaskContext(args) {
14826
14828
  return {
14827
14829
  schemaVersion: TASK_CONTEXT_SCHEMA_VERSION,
@@ -14837,9 +14839,9 @@ function buildTaskContext(args) {
14837
14839
  function persistTaskContext(cwd, ctx) {
14838
14840
  try {
14839
14841
  const dir = runtimeStatePath(cwd, "agent-runs", ctx.runId);
14840
- fs39.mkdirSync(dir, { recursive: true });
14841
- const file = path36.join(dir, "task-context.json");
14842
- fs39.writeFileSync(file, `${JSON.stringify(ctx, null, 2)}
14842
+ fs40.mkdirSync(dir, { recursive: true });
14843
+ const file = path37.join(dir, "task-context.json");
14844
+ fs40.writeFileSync(file, `${JSON.stringify(ctx, null, 2)}
14843
14845
  `);
14844
14846
  return file;
14845
14847
  } catch (err) {
@@ -15266,19 +15268,19 @@ function parseAgencyModelProposal(raw) {
15266
15268
  function normalizeBundleFiles(bundle) {
15267
15269
  const seen = /* @__PURE__ */ new Set();
15268
15270
  return bundle.files.map((file, index) => {
15269
- const path50 = file.path.replace(/^\/+/, "");
15270
- const parts = path50.split("/");
15271
- if (!path50 || file.path.startsWith("/") || file.path.includes("\\") || parts.some((part) => !part || part === "." || part === "..")) {
15271
+ const path51 = file.path.replace(/^\/+/, "");
15272
+ const parts = path51.split("/");
15273
+ if (!path51 || file.path.startsWith("/") || file.path.includes("\\") || parts.some((part) => !part || part === "." || part === "..")) {
15272
15274
  throw new Error(`openAgencyModelReviewPr: files[${index}].path is unsafe`);
15273
15275
  }
15274
15276
  if (!/^(agents\/[^/]+\.md|capabilities\/[^/]+\/.+|goals\/(?:templates\/)?[^/]+\/.+|workflows\/[^/]+\.json)$/.test(
15275
- path50
15277
+ path51
15276
15278
  )) {
15277
15279
  throw new Error(`openAgencyModelReviewPr: files[${index}].path is not a supported definition path`);
15278
15280
  }
15279
- if (seen.has(path50)) throw new Error(`openAgencyModelReviewPr: duplicate generated file path: ${path50}`);
15280
- seen.add(path50);
15281
- return { path: path50, content: file.content.replace(/\r\n?/g, "\n") };
15281
+ if (seen.has(path51)) throw new Error(`openAgencyModelReviewPr: duplicate generated file path: ${path51}`);
15282
+ seen.add(path51);
15283
+ return { path: path51, content: file.content.replace(/\r\n?/g, "\n") };
15282
15284
  });
15283
15285
  }
15284
15286
  function buildProposalId(issueNumber, bundle, sourceLabel) {
@@ -16222,9 +16224,9 @@ var init_postResearchComment = __esm({
16222
16224
  });
16223
16225
 
16224
16226
  // src/scripts/prepareBrowserAuth.ts
16225
- import * as fs40 from "fs";
16227
+ import * as fs41 from "fs";
16226
16228
  import * as os6 from "os";
16227
- import * as path37 from "path";
16229
+ import * as path38 from "path";
16228
16230
  function appendAuthMessage(ctx, message) {
16229
16231
  const current = typeof ctx.data.qaAuthBlock === "string" ? ctx.data.qaAuthBlock.trim() : "";
16230
16232
  ctx.data.qaAuthBlock = current ? `${current}
@@ -16263,9 +16265,9 @@ async function githubJson(url, token) {
16263
16265
  return await response.json();
16264
16266
  }
16265
16267
  function writeKodyStorageState(input) {
16266
- const directory = fs40.mkdtempSync(path37.join(os6.tmpdir(), "kody-browser-auth-"));
16267
- fs40.chmodSync(directory, 448);
16268
- const file = path37.join(directory, "storage-state.json");
16268
+ const directory = fs41.mkdtempSync(path38.join(os6.tmpdir(), "kody-browser-auth-"));
16269
+ fs41.chmodSync(directory, 448);
16270
+ const file = path38.join(directory, "storage-state.json");
16269
16271
  const now = Date.now();
16270
16272
  const repoEntry = {
16271
16273
  repoUrl: input.repoUrl,
@@ -16295,7 +16297,7 @@ function writeKodyStorageState(input) {
16295
16297
  }
16296
16298
  ]
16297
16299
  };
16298
- fs40.writeFileSync(file, JSON.stringify(storageState), { mode: 384 });
16300
+ fs41.writeFileSync(file, JSON.stringify(storageState), { mode: 384 });
16299
16301
  return { directory, file };
16300
16302
  }
16301
16303
  function configurePlaywright(profile, storageStatePath) {
@@ -16377,7 +16379,7 @@ async function prepareMethod(ctx, profile, method) {
16377
16379
  configurePlaywright(profile, state.file);
16378
16380
  const authDirectory = state.directory;
16379
16381
  registerRuntimeCleanup(ctx, () => {
16380
- fs40.rmSync(authDirectory, { recursive: true, force: true });
16382
+ fs41.rmSync(authDirectory, { recursive: true, force: true });
16381
16383
  });
16382
16384
  appendAuthMessage(
16383
16385
  ctx,
@@ -16385,7 +16387,7 @@ async function prepareMethod(ctx, profile, method) {
16385
16387
  );
16386
16388
  return true;
16387
16389
  } catch (error) {
16388
- if (state) fs40.rmSync(state.directory, { recursive: true, force: true });
16390
+ if (state) fs41.rmSync(state.directory, { recursive: true, force: true });
16389
16391
  const reason = error instanceof Error ? error.message : String(error);
16390
16392
  appendAuthMessage(
16391
16393
  ctx,
@@ -16498,9 +16500,9 @@ function latestResult(raw, agentResult) {
16498
16500
  function recordField4(value) {
16499
16501
  return value && typeof value === "object" && !Array.isArray(value) ? value : null;
16500
16502
  }
16501
- function resolveDotted(root, path50) {
16502
- if (!path50) return void 0;
16503
- return path50.split(".").reduce((value, key) => recordField4(value)?.[key], root);
16503
+ function resolveDotted(root, path51) {
16504
+ if (!path51) return void 0;
16505
+ return path51.split(".").reduce((value, key) => recordField4(value)?.[key], root);
16504
16506
  }
16505
16507
  function stringValue4(value) {
16506
16508
  return typeof value === "string" && value.trim() ? value.trim() : null;
@@ -17451,10 +17453,10 @@ var init_previewBuildHelpers = __esm({
17451
17453
  });
17452
17454
 
17453
17455
  // src/scripts/previewBuildRun.ts
17454
- import { spawn as spawn4 } from "child_process";
17456
+ import { spawn as spawn5 } from "child_process";
17455
17457
  async function runCmd(cmd, args, opts = {}) {
17456
17458
  await new Promise((resolve16, reject) => {
17457
- const child = spawn4(cmd, args, {
17459
+ const child = spawn5(cmd, args, {
17458
17460
  cwd: opts.cwd,
17459
17461
  env: { ...process.env, ...opts.env ?? {} },
17460
17462
  stdio: opts.input ? ["pipe", "inherit", "inherit"] : "inherit"
@@ -17537,12 +17539,12 @@ fi
17537
17539
 
17538
17540
  // src/scripts/runPreviewBuild.ts
17539
17541
  import { copyFile, writeFile } from "fs/promises";
17540
- import * as path38 from "path";
17542
+ import * as path39 from "path";
17541
17543
  import { fileURLToPath } from "url";
17542
17544
  function bundledDockerfilePath(mode) {
17543
- const here = path38.dirname(fileURLToPath(import.meta.url));
17545
+ const here = path39.dirname(fileURLToPath(import.meta.url));
17544
17546
  const file = mode === "dev" ? "default-Dockerfile.preview.dev" : "default-Dockerfile.preview.prod";
17545
- return path38.join(here, "preview-build-templates", file);
17547
+ return path39.join(here, "preview-build-templates", file);
17546
17548
  }
17547
17549
  function required(name) {
17548
17550
  const v = (process.env[name] ?? "").trim();
@@ -17789,10 +17791,10 @@ var init_runPreviewBuild = __esm({
17789
17791
  console.log(`[preview-build] vault: ${Object.keys(buildEnv).length} secrets, mode=${buildMode}`);
17790
17792
  if (Object.keys(buildEnv).length > 0) {
17791
17793
  const lines = Object.entries(buildEnv).map(([k, v]) => `${k}=${JSON.stringify(v)}`);
17792
- await writeFile(path38.join(ctx.cwd, ".env.production.local"), `${lines.join("\n")}
17794
+ await writeFile(path39.join(ctx.cwd, ".env.production.local"), `${lines.join("\n")}
17793
17795
  `, "utf8");
17794
17796
  }
17795
- const consumerDockerfile = path38.join(ctx.cwd, "Dockerfile.preview");
17797
+ const consumerDockerfile = path39.join(ctx.cwd, "Dockerfile.preview");
17796
17798
  const { stat } = await import("fs/promises");
17797
17799
  let hasConsumerDockerfile = false;
17798
17800
  try {
@@ -17976,8 +17978,8 @@ var init_tickShellRunner = __esm({
17976
17978
  });
17977
17979
 
17978
17980
  // src/scripts/runScheduledImplementationTick.ts
17979
- import * as fs41 from "fs";
17980
- import * as path39 from "path";
17981
+ import * as fs42 from "fs";
17982
+ import * as path40 from "path";
17981
17983
  var runScheduledImplementationTick;
17982
17984
  var init_runScheduledImplementationTick = __esm({
17983
17985
  "src/scripts/runScheduledImplementationTick.ts"() {
@@ -17998,14 +18000,14 @@ var init_runScheduledImplementationTick = __esm({
17998
18000
  ctx.output.reason = `runScheduledImplementationTick: args.slug or ctx.args.${slugArg} must be non-empty capability slug`;
17999
18001
  return;
18000
18002
  }
18001
- const capability = resolveCapabilityFolder(slug, path39.resolve(ctx.cwd, jobsDir));
18003
+ const capability = resolveCapabilityFolder(slug, path40.resolve(ctx.cwd, jobsDir));
18002
18004
  if (!capability) {
18003
18005
  ctx.output.exitCode = 99;
18004
18006
  ctx.output.reason = `runScheduledImplementationTick: capability folder not found or incomplete: ${slug} (searched ${jobsDir} and company store)`;
18005
18007
  return;
18006
18008
  }
18007
- const shellPath = path39.join(profile.dir, shell);
18008
- if (!fs41.existsSync(shellPath)) {
18009
+ const shellPath = path40.join(profile.dir, shell);
18010
+ if (!fs42.existsSync(shellPath)) {
18009
18011
  ctx.output.exitCode = 99;
18010
18012
  ctx.output.reason = `runScheduledImplementationTick: shell not found: ${shell} (looked in ${profile.dir})`;
18011
18013
  return;
@@ -18036,8 +18038,8 @@ var init_runScheduledImplementationTick = __esm({
18036
18038
  });
18037
18039
 
18038
18040
  // src/scripts/runTickScript.ts
18039
- import * as fs42 from "fs";
18040
- import * as path40 from "path";
18041
+ import * as fs43 from "fs";
18042
+ import * as path41 from "path";
18041
18043
  var runTickScript;
18042
18044
  var init_runTickScript = __esm({
18043
18045
  "src/scripts/runTickScript.ts"() {
@@ -18057,10 +18059,10 @@ var init_runTickScript = __esm({
18057
18059
  ctx.output.reason = `runTickScript: ctx.args.${slugArg} must be a non-empty slug`;
18058
18060
  return;
18059
18061
  }
18060
- const capability = readCapabilityFolder(path40.resolve(ctx.cwd, jobsDir), slug);
18062
+ const capability = readCapabilityFolder(path41.resolve(ctx.cwd, jobsDir), slug);
18061
18063
  if (!capability) {
18062
18064
  ctx.output.exitCode = 99;
18063
- ctx.output.reason = `runTickScript: capability folder not found or incomplete: ${path40.resolve(ctx.cwd, jobsDir, slug)}`;
18065
+ ctx.output.reason = `runTickScript: capability folder not found or incomplete: ${path41.resolve(ctx.cwd, jobsDir, slug)}`;
18064
18066
  return;
18065
18067
  }
18066
18068
  const tickScript = capability.config.tickScript;
@@ -18069,8 +18071,8 @@ var init_runTickScript = __esm({
18069
18071
  ctx.output.reason = `runTickScript: capability ${slug} has no \`tickScript\` in profile.json \u2014 route via capability-tick instead`;
18070
18072
  return;
18071
18073
  }
18072
- const scriptPath = path40.isAbsolute(tickScript) ? tickScript : path40.join(ctx.cwd, tickScript);
18073
- if (!fs42.existsSync(scriptPath)) {
18074
+ const scriptPath = path41.isAbsolute(tickScript) ? tickScript : path41.join(ctx.cwd, tickScript);
18075
+ if (!fs43.existsSync(scriptPath)) {
18074
18076
  ctx.output.exitCode = 99;
18075
18077
  ctx.output.reason = `runTickScript: tickScript not found: ${scriptPath}`;
18076
18078
  return;
@@ -18352,7 +18354,7 @@ var init_syncFlow = __esm({
18352
18354
  });
18353
18355
 
18354
18356
  // src/scripts/validateAgencyModelProposal.ts
18355
- import * as path41 from "path";
18357
+ import * as path42 from "path";
18356
18358
  function validateModelBundle(bundle, expectedKind, options = {}) {
18357
18359
  const failures = [];
18358
18360
  validateOneModel(bundle.model, bundle.files, "model", true, failures, expectedKind, options);
@@ -18678,7 +18680,7 @@ var init_validateAgencyModelProposal = __esm({
18678
18680
  const bundle = parseAgencyModelProposal(raw);
18679
18681
  const expectedKind = readExpectedModelKind(args);
18680
18682
  const failures = validateModelBundle(bundle, expectedKind, {
18681
- capabilityRoot: path41.join(ctx.cwd, ".kody", "capabilities")
18683
+ capabilityRoot: path42.join(ctx.cwd, ".kody", "capabilities")
18682
18684
  });
18683
18685
  if (failures.length > 0) {
18684
18686
  throw new Error(`validateAgencyModelProposal: ${failures.join("; ")}`);
@@ -18723,7 +18725,7 @@ var init_verify2 = __esm({
18723
18725
  });
18724
18726
 
18725
18727
  // src/scripts/verifyReproFails.ts
18726
- import { spawn as spawn5 } from "child_process";
18728
+ import { spawn as spawn6 } from "child_process";
18727
18729
  function composeTestCommand(baseCmd, testPath) {
18728
18730
  const trimmed = baseCmd.trim();
18729
18731
  const lower = trimmed.toLowerCase();
@@ -18742,7 +18744,7 @@ function stripAnsi2(s) {
18742
18744
  }
18743
18745
  function runCommand2(command, cwd) {
18744
18746
  return new Promise((resolve16) => {
18745
- const child = spawn5(command, {
18747
+ const child = spawn6(command, {
18746
18748
  cwd,
18747
18749
  shell: true,
18748
18750
  env: { ...process.env, HUSKY: "0", SKIP_HOOKS: "1", CI: process.env.CI ?? "1" },
@@ -19072,9 +19074,9 @@ var init_waitForCi = __esm({
19072
19074
  });
19073
19075
 
19074
19076
  // src/scripts/warmupMcp.ts
19075
- import { spawn as spawn6 } from "child_process";
19077
+ import { spawn as spawn7 } from "child_process";
19076
19078
  async function warmupOne(command, args, env) {
19077
- const child = spawn6(command, args, {
19079
+ const child = spawn7(command, args, {
19078
19080
  stdio: ["pipe", "pipe", "pipe"],
19079
19081
  env: env ? { ...process.env, ...env } : process.env
19080
19082
  });
@@ -19229,7 +19231,7 @@ var init_warmupMcp = __esm({
19229
19231
  });
19230
19232
 
19231
19233
  // src/scripts/writeAgentRunSummary.ts
19232
- import * as fs43 from "fs";
19234
+ import * as fs44 from "fs";
19233
19235
  var writeAgentRunSummary;
19234
19236
  var init_writeAgentRunSummary = __esm({
19235
19237
  "src/scripts/writeAgentRunSummary.ts"() {
@@ -19255,7 +19257,7 @@ var init_writeAgentRunSummary = __esm({
19255
19257
  if (reason) lines.push(`- **Reason:** ${reason}`);
19256
19258
  lines.push("");
19257
19259
  try {
19258
- fs43.appendFileSync(summaryPath, `${lines.join("\n")}
19260
+ fs44.appendFileSync(summaryPath, `${lines.join("\n")}
19259
19261
  `);
19260
19262
  } catch {
19261
19263
  }
@@ -19581,17 +19583,17 @@ var init_scripts = __esm({
19581
19583
  });
19582
19584
 
19583
19585
  // src/stateWorkspace.ts
19584
- import * as fs44 from "fs";
19585
- import * as path42 from "path";
19586
+ import * as fs45 from "fs";
19587
+ import * as path43 from "path";
19586
19588
  function tenantId(config) {
19587
19589
  const owner = config.github?.owner?.trim() || process.env.GITHUB_REPOSITORY?.split("/")[0]?.trim();
19588
19590
  const repo = config.github?.repo?.trim() || process.env.GITHUB_REPOSITORY?.split("/")[1]?.trim();
19589
19591
  return owner && repo ? `${owner}/${repo}` : null;
19590
19592
  }
19591
19593
  function writeRuntimeFile(cwd, relativePath, content) {
19592
- const target = path42.join(cwd, RUNTIME_ROOT, relativePath);
19593
- fs44.mkdirSync(path42.dirname(target), { recursive: true });
19594
- fs44.writeFileSync(target, content, "utf8");
19594
+ const target = path43.join(cwd, RUNTIME_ROOT, relativePath);
19595
+ fs45.mkdirSync(path43.dirname(target), { recursive: true });
19596
+ fs45.writeFileSync(target, content, "utf8");
19595
19597
  }
19596
19598
  function record(value) {
19597
19599
  return value && typeof value === "object" && !Array.isArray(value) ? value : null;
@@ -19656,11 +19658,11 @@ async function hydrateStateWorkspace(config, cwd, backendOverride) {
19656
19658
  throw new Error("Convex backend is required for runtime workspace documents");
19657
19659
  return;
19658
19660
  }
19659
- const key = `${path42.resolve(cwd)}|${tenant}`;
19661
+ const key = `${path43.resolve(cwd)}|${tenant}`;
19660
19662
  if (hydratedWorkspaces.has(key)) return;
19661
19663
  const backend = backendOverride ?? createStateBackendFromEnv();
19662
- const root = path42.join(cwd, RUNTIME_ROOT);
19663
- fs44.rmSync(root, { recursive: true, force: true });
19664
+ const root = path43.join(cwd, RUNTIME_ROOT);
19665
+ fs45.rmSync(root, { recursive: true, force: true });
19664
19666
  await Promise.all([
19665
19667
  hydratePrefix(backend, tenant, cwd, "context:"),
19666
19668
  hydratePrefix(backend, tenant, cwd, "memory:"),
@@ -19676,7 +19678,7 @@ var init_stateWorkspace = __esm({
19676
19678
  "src/stateWorkspace.ts"() {
19677
19679
  "use strict";
19678
19680
  init_state_backend();
19679
- RUNTIME_ROOT = path42.join(".kody-engine", "runtime");
19681
+ RUNTIME_ROOT = path43.join(".kody-engine", "runtime");
19680
19682
  hydratedWorkspaces = /* @__PURE__ */ new Set();
19681
19683
  }
19682
19684
  });
@@ -19746,10 +19748,10 @@ var init_tools = __esm({
19746
19748
  });
19747
19749
 
19748
19750
  // src/executor.ts
19749
- import { spawn as spawn7 } from "child_process";
19750
- import * as fs45 from "fs";
19751
+ import { spawn as spawn8 } from "child_process";
19752
+ import * as fs46 from "fs";
19751
19753
  import * as os7 from "os";
19752
- import * as path43 from "path";
19754
+ import * as path44 from "path";
19753
19755
  function isMutatingPostflight(scriptName) {
19754
19756
  return MUTATING_POSTFLIGHTS.has(scriptName ?? "");
19755
19757
  }
@@ -19977,7 +19979,7 @@ async function runImplementation(profileName, input) {
19977
19979
  const jobWhyBlock = typeof ctx.data.jobWhy === "string" ? operatorRequestBlock(ctx.data.jobWhy) : null;
19978
19980
  const jobRefBlock = jobReferenceBlock(profileName, profile, ctx.data);
19979
19981
  const invokeAgent = async (prompt) => {
19980
- const externalPlugins = (profile.claudeCode.plugins ?? []).map((p) => path43.isAbsolute(p) ? p : path43.resolve(profile.dir, p)).filter((p) => p.length > 0);
19982
+ const externalPlugins = (profile.claudeCode.plugins ?? []).map((p) => path44.isAbsolute(p) ? p : path44.resolve(profile.dir, p)).filter((p) => p.length > 0);
19981
19983
  const syntheticPath = ctx.data.syntheticPluginPath;
19982
19984
  const pluginPaths = [...externalPlugins, ...syntheticPath ? [syntheticPath] : []];
19983
19985
  const agents = loadSubagents(profile);
@@ -20415,17 +20417,17 @@ function clearStampedLifecycleLabels(profile, ctx) {
20415
20417
  function resolveProfilePath(profileName) {
20416
20418
  const found = resolveImplementation(profileName);
20417
20419
  if (found) return found;
20418
- const here = path43.dirname(new URL(import.meta.url).pathname);
20420
+ const here = path44.dirname(new URL(import.meta.url).pathname);
20419
20421
  const candidates = [
20420
- path43.join(here, "implementations", profileName, "profile.json"),
20422
+ path44.join(here, "implementations", profileName, "profile.json"),
20421
20423
  // same-dir sibling (dev)
20422
- path43.join(here, "..", "implementations", profileName, "profile.json"),
20424
+ path44.join(here, "..", "implementations", profileName, "profile.json"),
20423
20425
  // up one (prod: dist/bin → dist/implementations)
20424
- path43.join(here, "..", "src", "implementations", profileName, "profile.json")
20426
+ path44.join(here, "..", "src", "implementations", profileName, "profile.json")
20425
20427
  // fallback
20426
20428
  ];
20427
20429
  for (const c of candidates) {
20428
- if (fs45.existsSync(c)) return c;
20430
+ if (fs46.existsSync(c)) return c;
20429
20431
  }
20430
20432
  return candidates[0];
20431
20433
  }
@@ -20540,15 +20542,15 @@ function resolveShellTimeoutMs(entry) {
20540
20542
  }
20541
20543
  async function runShellEntry(entry, ctx, profile) {
20542
20544
  const shellName = entry.shell;
20543
- const shellPath = path43.join(profile.dir, shellName);
20544
- if (!fs45.existsSync(shellPath)) {
20545
+ const shellPath = path44.join(profile.dir, shellName);
20546
+ if (!fs46.existsSync(shellPath)) {
20545
20547
  ctx.skipAgent = true;
20546
20548
  ctx.output.exitCode = 99;
20547
20549
  ctx.output.reason = `shell script not found: ${shellName} (looked in ${profile.dir})`;
20548
20550
  return;
20549
20551
  }
20550
20552
  const positional = entry.with ? Object.values(entry.with).map((v) => String(v)) : [];
20551
- const outputFile = path43.join(
20553
+ const outputFile = path44.join(
20552
20554
  os7.tmpdir(),
20553
20555
  `kody-shell-output-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`
20554
20556
  );
@@ -20561,7 +20563,7 @@ async function runShellEntry(entry, ctx, profile) {
20561
20563
  env[`KODY_CFG_${k}`] = v;
20562
20564
  }
20563
20565
  const timeoutMs = resolveShellTimeoutMs(entry);
20564
- const child = spawn7("bash", [shellPath, ...positional], {
20566
+ const child = spawn8("bash", [shellPath, ...positional], {
20565
20567
  cwd: ctx.cwd,
20566
20568
  env,
20567
20569
  stdio: ["pipe", "pipe", "pipe"],
@@ -20620,9 +20622,9 @@ async function runShellEntry(entry, ctx, profile) {
20620
20622
  }
20621
20623
  let sideChannelText = "";
20622
20624
  try {
20623
- if (fs45.existsSync(outputFile)) {
20624
- sideChannelText = fs45.readFileSync(outputFile, "utf-8");
20625
- fs45.rmSync(outputFile, { force: true });
20625
+ if (fs46.existsSync(outputFile)) {
20626
+ sideChannelText = fs46.readFileSync(outputFile, "utf-8");
20627
+ fs46.rmSync(outputFile, { force: true });
20626
20628
  }
20627
20629
  } catch {
20628
20630
  }
@@ -20792,7 +20794,7 @@ __export(job_exports, {
20792
20794
  stableJobKey: () => stableJobKey,
20793
20795
  validateJob: () => validateJob
20794
20796
  });
20795
- import * as path44 from "path";
20797
+ import * as path45 from "path";
20796
20798
  function newJobId(flavor) {
20797
20799
  localJobSeq += 1;
20798
20800
  const runId = process.env.GITHUB_RUN_ID;
@@ -21255,11 +21257,11 @@ function selectWorkflowTransition(step, data, counts) {
21255
21257
  }
21256
21258
  function workflowResultConditionPaths(transitions) {
21257
21259
  return transitions.flatMap(
21258
- (transition) => Object.keys(transition.when ?? {}).filter((path50) => path50.startsWith("result."))
21260
+ (transition) => Object.keys(transition.when ?? {}).filter((path51) => path51.startsWith("result."))
21259
21261
  );
21260
21262
  }
21261
21263
  function conditionMatches(condition, context) {
21262
- return Object.entries(condition).every(([path50, expected]) => valueMatches(resolveDottedPath2(context, path50), expected));
21264
+ return Object.entries(condition).every(([path51, expected]) => valueMatches(resolveDottedPath2(context, path51), expected));
21263
21265
  }
21264
21266
  function withWorkflowBoundaryEval(capability, result) {
21265
21267
  const capabilityKind = capability.config.capabilityKind;
@@ -21417,7 +21419,7 @@ function loadCapabilityContext(slug, cwd) {
21417
21419
  return resolveCapabilityFolder(slug, hydratedCapabilitiesRoot(cwd));
21418
21420
  }
21419
21421
  function hydratedCapabilitiesRoot(cwd) {
21420
- return path44.join(cwd, ".kody-engine", "definitions", "capabilities");
21422
+ return path45.join(cwd, ".kody-engine", "definitions", "capabilities");
21421
21423
  }
21422
21424
  function loadWorkflowContext(slug, base) {
21423
21425
  if (!slug || !base.config || !isWorkflowDefinitionId(slug)) return null;
@@ -21580,7 +21582,7 @@ function translateOpenAISseToBrain(opts) {
21580
21582
 
21581
21583
  // src/servers/brain-serve.ts
21582
21584
  import { createServer as createServer2 } from "http";
21583
- import * as path47 from "path";
21585
+ import * as path48 from "path";
21584
21586
 
21585
21587
  // src/chat/loop.ts
21586
21588
  init_agent();
@@ -21646,12 +21648,293 @@ function prepareAttachments(turns, cwd, sessionId) {
21646
21648
  return { turns: rewritten, imagePaths };
21647
21649
  }
21648
21650
 
21649
- // src/chat/events.ts
21651
+ // src/chat/codex-app-server.ts
21652
+ import { spawn as spawn3 } from "child_process";
21650
21653
  import * as fs12 from "fs";
21651
21654
  import * as path13 from "path";
21655
+ import { createInterface } from "readline";
21656
+ function codexThreadStartParams(args) {
21657
+ return {
21658
+ cwd: args.cwd,
21659
+ developerInstructions: args.developerInstructions
21660
+ };
21661
+ }
21662
+ function codexTurnStartParams(args) {
21663
+ return {
21664
+ threadId: args.threadId,
21665
+ input: [{ type: "text", text: args.message }]
21666
+ };
21667
+ }
21668
+ function translateCodexNotification(notification, chatId) {
21669
+ const params = notification.params ?? {};
21670
+ if (notification.method === "item/agentMessage/delta") {
21671
+ const delta = params.delta;
21672
+ return typeof delta === "string" && delta.length > 0 ? [{ type: "text", text: delta, chatId }] : [];
21673
+ }
21674
+ if (notification.method === "turn/completed") {
21675
+ return [{ type: "done", chatId }];
21676
+ }
21677
+ if (notification.method === "error") {
21678
+ const message = params.message;
21679
+ return [
21680
+ {
21681
+ type: "error",
21682
+ error: typeof message === "string" ? message : "Codex app-server error",
21683
+ chatId
21684
+ }
21685
+ ];
21686
+ }
21687
+ if (notification.method === "item/started") {
21688
+ const item = params.item;
21689
+ if (!item || typeof item !== "object") return [];
21690
+ const record2 = item;
21691
+ return [
21692
+ {
21693
+ type: "tool_use",
21694
+ name: typeof record2.type === "string" ? record2.type : "tool",
21695
+ input: record2,
21696
+ chatId
21697
+ }
21698
+ ];
21699
+ }
21700
+ return [];
21701
+ }
21702
+ var CodexAppServerClient = class {
21703
+ process;
21704
+ initialized = false;
21705
+ constructor(options) {
21706
+ const child = (options.spawnImpl ?? spawn3)("codex", ["app-server", "--stdio"], {
21707
+ cwd: options.cwd,
21708
+ stdio: ["pipe", "pipe", "pipe"]
21709
+ });
21710
+ this.process = {
21711
+ child,
21712
+ nextId: 1,
21713
+ pending: /* @__PURE__ */ new Map(),
21714
+ turnWaiters: /* @__PURE__ */ new Map()
21715
+ };
21716
+ const lines = createInterface({ input: child.stdout });
21717
+ lines.on("line", (line) => this.handleLine(line));
21718
+ child.on("error", (error) => this.fail(error));
21719
+ child.on("exit", (code, signal) => {
21720
+ this.fail(new Error(`codex app-server exited (${code ?? `signal ${signal}`})`));
21721
+ });
21722
+ }
21723
+ async startThread(args) {
21724
+ await this.ensureInitialized();
21725
+ const result = await this.request("thread/start", codexThreadStartParams(args));
21726
+ const thread = result.thread;
21727
+ if (!thread || typeof thread !== "object") {
21728
+ throw new Error("Codex app-server returned no thread");
21729
+ }
21730
+ const id = thread.id;
21731
+ if (typeof id !== "string" || !id) throw new Error("Codex app-server returned an invalid thread id");
21732
+ return id;
21733
+ }
21734
+ async resumeThread(threadId) {
21735
+ await this.ensureInitialized();
21736
+ await this.request("thread/resume", { threadId });
21737
+ }
21738
+ async runTurn(args) {
21739
+ await new Promise((resolve16, reject) => {
21740
+ this.process.turnWaiters.set(args.threadId, {
21741
+ resolve: resolve16,
21742
+ reject,
21743
+ onNotification: args.onNotification,
21744
+ queue: Promise.resolve()
21745
+ });
21746
+ void this.request("turn/start", codexTurnStartParams(args)).catch((error) => {
21747
+ this.process.turnWaiters.delete(args.threadId);
21748
+ reject(error);
21749
+ });
21750
+ });
21751
+ }
21752
+ close() {
21753
+ this.process.child.kill();
21754
+ this.fail(new Error("Codex app-server client closed"));
21755
+ }
21756
+ request(method, params) {
21757
+ const id = this.process.nextId++;
21758
+ return new Promise((resolve16, reject) => {
21759
+ this.process.pending.set(id, { resolve: resolve16, reject });
21760
+ this.process.child.stdin.write(`${JSON.stringify({ method, id, params })}
21761
+ `);
21762
+ });
21763
+ }
21764
+ notify(method, params) {
21765
+ this.process.child.stdin.write(`${JSON.stringify({ method, params })}
21766
+ `);
21767
+ }
21768
+ async ensureInitialized() {
21769
+ if (this.initialized) return;
21770
+ await this.request("initialize", {
21771
+ clientInfo: { name: "kody-brain", version: "0.1.0" }
21772
+ });
21773
+ this.notify("initialized", {});
21774
+ this.initialized = true;
21775
+ }
21776
+ handleLine(line) {
21777
+ let message;
21778
+ try {
21779
+ message = JSON.parse(line);
21780
+ } catch {
21781
+ return;
21782
+ }
21783
+ if (typeof message.id === "number") {
21784
+ const pending = this.process.pending.get(message.id);
21785
+ if (!pending) return;
21786
+ this.process.pending.delete(message.id);
21787
+ if (message.error && typeof message.error === "object") {
21788
+ const error = message.error;
21789
+ pending.reject(new Error(typeof error.message === "string" ? error.message : "Codex request failed"));
21790
+ } else {
21791
+ pending.resolve(message.result ?? {});
21792
+ }
21793
+ return;
21794
+ }
21795
+ if (typeof message.method !== "string") return;
21796
+ const notification = {
21797
+ method: message.method,
21798
+ params: typeof message.params === "object" && message.params !== null ? message.params : {}
21799
+ };
21800
+ const threadId = notification.params?.threadId;
21801
+ if (typeof threadId !== "string") return;
21802
+ const waiter = this.process.turnWaiters.get(threadId);
21803
+ if (!waiter) return;
21804
+ waiter.queue = waiter.queue.then(() => waiter.onNotification(notification));
21805
+ if (notification.method === "turn/completed" || notification.method === "error") {
21806
+ this.process.turnWaiters.delete(threadId);
21807
+ void waiter.queue.then(() => {
21808
+ if (notification.method === "error") {
21809
+ waiter.reject(new Error(String(notification.params?.message ?? "Codex turn failed")));
21810
+ } else {
21811
+ waiter.resolve();
21812
+ }
21813
+ }, waiter.reject);
21814
+ }
21815
+ }
21816
+ fail(error) {
21817
+ for (const pending of this.process.pending.values()) pending.reject(error);
21818
+ this.process.pending.clear();
21819
+ for (const waiter of this.process.turnWaiters.values()) waiter.reject(error);
21820
+ this.process.turnWaiters.clear();
21821
+ }
21822
+ };
21823
+ var clients = /* @__PURE__ */ new Map();
21824
+ function threadMapPath(cwd) {
21825
+ return path13.join(cwd, ".kody-engine", "runtime", "codex-threads.json");
21826
+ }
21827
+ function readThreadMap(cwd) {
21828
+ try {
21829
+ const value = JSON.parse(fs12.readFileSync(threadMapPath(cwd), "utf8"));
21830
+ if (!value || typeof value !== "object" || Array.isArray(value)) return {};
21831
+ return Object.fromEntries(
21832
+ Object.entries(value).filter(
21833
+ (entry) => typeof entry[1] === "string"
21834
+ )
21835
+ );
21836
+ } catch {
21837
+ return {};
21838
+ }
21839
+ }
21840
+ function writeThreadMap(cwd, map) {
21841
+ const file = threadMapPath(cwd);
21842
+ fs12.mkdirSync(path13.dirname(file), { recursive: true });
21843
+ fs12.writeFileSync(file, `${JSON.stringify(map, null, 2)}
21844
+ `);
21845
+ }
21846
+ async function runCodexChatTurn(args) {
21847
+ const { opts, turns, systemPrompt, store } = args;
21848
+ const last = turns.at(-1);
21849
+ if (!last || last.role !== "user") {
21850
+ const error = "last turn is not a user message";
21851
+ await opts.sink.emit({
21852
+ event: "chat.error",
21853
+ payload: { error },
21854
+ runId: opts.sessionId,
21855
+ emittedAt: (/* @__PURE__ */ new Date()).toISOString()
21856
+ });
21857
+ return { exitCode: 64, error };
21858
+ }
21859
+ const client = clients.get(opts.cwd) ?? new CodexAppServerClient({ cwd: opts.cwd });
21860
+ clients.set(opts.cwd, client);
21861
+ const map = readThreadMap(opts.cwd);
21862
+ let threadId = map[opts.sessionId];
21863
+ if (threadId) {
21864
+ try {
21865
+ await client.resumeThread(threadId);
21866
+ } catch {
21867
+ threadId = void 0;
21868
+ }
21869
+ }
21870
+ threadId ??= await client.startThread({ cwd: opts.cwd, developerInstructions: systemPrompt });
21871
+ if (!map[opts.sessionId]) {
21872
+ map[opts.sessionId] = threadId;
21873
+ writeThreadMap(opts.cwd, map);
21874
+ }
21875
+ const replyParts = [];
21876
+ try {
21877
+ await client.runTurn({
21878
+ threadId,
21879
+ message: last.content,
21880
+ onNotification: async (notification) => {
21881
+ for (const event of translateCodexNotification(notification, opts.sessionId)) {
21882
+ if (event.type === "text" && event.text) {
21883
+ replyParts.push(event.text);
21884
+ await opts.sink.emit({
21885
+ event: "chat.message",
21886
+ payload: { sessionId: opts.sessionId, role: "assistant", content: event.text },
21887
+ runId: opts.sessionId,
21888
+ emittedAt: (/* @__PURE__ */ new Date()).toISOString()
21889
+ });
21890
+ } else if (event.type === "tool_use") {
21891
+ await opts.sink.emit({
21892
+ event: "chat.tool",
21893
+ payload: { phase: "use", name: event.name, input: event.input },
21894
+ runId: opts.sessionId,
21895
+ emittedAt: (/* @__PURE__ */ new Date()).toISOString()
21896
+ });
21897
+ }
21898
+ }
21899
+ }
21900
+ });
21901
+ } catch (error) {
21902
+ const message = error instanceof Error ? error.message : String(error);
21903
+ await opts.sink.emit({
21904
+ event: "chat.error",
21905
+ payload: { error: message },
21906
+ runId: opts.sessionId,
21907
+ emittedAt: (/* @__PURE__ */ new Date()).toISOString()
21908
+ });
21909
+ return { exitCode: 99, error: message };
21910
+ }
21911
+ const reply = replyParts.join("").trim();
21912
+ if (!reply) {
21913
+ const error = "Codex completed without producing a reply";
21914
+ await opts.sink.emit({
21915
+ event: "chat.error",
21916
+ payload: { error },
21917
+ runId: opts.sessionId,
21918
+ emittedAt: (/* @__PURE__ */ new Date()).toISOString()
21919
+ });
21920
+ return { exitCode: 99, error };
21921
+ }
21922
+ await store.appendTurn({ role: "assistant", content: reply, timestamp: (/* @__PURE__ */ new Date()).toISOString() });
21923
+ await opts.sink.emit({
21924
+ event: "chat.done",
21925
+ payload: { sessionId: opts.sessionId },
21926
+ runId: opts.sessionId,
21927
+ emittedAt: (/* @__PURE__ */ new Date()).toISOString()
21928
+ });
21929
+ return { exitCode: 0, reply };
21930
+ }
21931
+
21932
+ // src/chat/events.ts
21933
+ import * as fs13 from "fs";
21934
+ import * as path14 from "path";
21652
21935
  import posixPath2 from "path/posix";
21653
21936
  function eventsFilePath(cwd, sessionId) {
21654
- return path13.join(cwd, ".kody-engine", "runtime", "events", `${sessionId}.jsonl`);
21937
+ return path14.join(cwd, ".kody-engine", "runtime", "events", `${sessionId}.jsonl`);
21655
21938
  }
21656
21939
  var FileSink = class {
21657
21940
  constructor(file) {
@@ -21659,8 +21942,8 @@ var FileSink = class {
21659
21942
  }
21660
21943
  file;
21661
21944
  async emit(event) {
21662
- fs12.mkdirSync(path13.dirname(this.file), { recursive: true });
21663
- fs12.appendFileSync(this.file, `${JSON.stringify(event)}
21945
+ fs13.mkdirSync(path14.dirname(this.file), { recursive: true });
21946
+ fs13.appendFileSync(this.file, `${JSON.stringify(event)}
21664
21947
  `);
21665
21948
  }
21666
21949
  };
@@ -21716,163 +21999,69 @@ function makeRunId(sessionId, suffix) {
21716
21999
  // src/chat/session-store.ts
21717
22000
  init_convex_client();
21718
22001
  import { anyApi as anyApi2 } from "convex/server";
21719
-
21720
- // src/chat/session.ts
21721
- import * as fs13 from "fs";
21722
- import * as path14 from "path";
21723
- import posixPath3 from "path/posix";
21724
- function sessionFilePath(cwd, sessionId) {
21725
- return path14.join(cwd, ".kody-engine", "runtime", "sessions", `${sessionId}.jsonl`);
21726
- }
21727
- function readMeta(file) {
21728
- if (!fs13.existsSync(file)) return null;
21729
- const raw = fs13.readFileSync(file, "utf-8");
21730
- const firstLine2 = raw.split("\n", 1)[0]?.trim();
21731
- if (!firstLine2) return null;
21732
- try {
21733
- const parsed = JSON.parse(firstLine2);
21734
- if (parsed.type !== "meta") return null;
21735
- if (parsed.mode !== "one-shot" && parsed.mode !== "interactive") return null;
21736
- return parsed;
21737
- } catch {
21738
- return null;
21739
- }
21740
- }
21741
- function readSession(file) {
21742
- if (!fs13.existsSync(file)) return [];
21743
- const raw = fs13.readFileSync(file, "utf-8").trim();
21744
- if (!raw) return [];
21745
- const turns = [];
21746
- for (const line of raw.split("\n")) {
21747
- if (!line.trim()) continue;
21748
- try {
21749
- const parsed = JSON.parse(line);
21750
- if (parsed.role !== "user" && parsed.role !== "assistant") continue;
21751
- if (typeof parsed.content !== "string") continue;
21752
- turns.push(parsed);
21753
- } catch {
21754
- }
21755
- }
21756
- return turns;
21757
- }
21758
- function appendTurn(file, turn) {
21759
- fs13.mkdirSync(path14.dirname(file), { recursive: true });
21760
- const line = JSON.stringify({
21761
- role: turn.role,
21762
- content: turn.content,
21763
- timestamp: turn.timestamp,
21764
- toolCalls: turn.toolCalls ?? []
21765
- });
21766
- fs13.appendFileSync(file, `${line}
21767
- `);
21768
- }
21769
- function seedInitialMessage(file, message) {
21770
- if (!message.trim()) return false;
21771
- const turns = readSession(file);
21772
- const lastUser = [...turns].reverse().find((t) => t.role === "user");
21773
- if (lastUser && lastUser.content === message) return false;
21774
- appendTurn(file, {
21775
- role: "user",
21776
- content: message,
21777
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
21778
- });
21779
- return true;
22002
+ function currentEpochTurns(result) {
22003
+ const ordered = [...result.entries].sort((left, right) => left.seq - right.seq);
22004
+ const lastHandoffSeq = ordered.filter((item) => item.entry.kind === "agent-handoff").at(-1)?.seq ?? -1;
22005
+ return ordered.flatMap(
22006
+ (item) => item.seq > lastHandoffSeq && item.entry.kind === "message" && (item.entry.status === "committed" || item.entry.status === "pending") ? [
22007
+ {
22008
+ role: item.entry.role,
22009
+ content: item.entry.content,
22010
+ timestamp: item.entry.createdAt,
22011
+ toolCalls: []
22012
+ }
22013
+ ] : []
22014
+ );
21780
22015
  }
21781
-
21782
- // src/chat/session-store.ts
21783
- function isChatTurn(value) {
21784
- if (!value || typeof value !== "object") return false;
21785
- const t = value;
21786
- return (t.role === "user" || t.role === "assistant") && typeof t.content === "string";
22016
+ function entryKey(sessionId, turn) {
22017
+ const safeTime = turn.timestamp.replace(/[^a-zA-Z0-9._-]/g, "-");
22018
+ return `engine-${sessionId}-${turn.role}-${safeTime}`;
21787
22019
  }
21788
22020
  function createSessionStore(opts) {
21789
22021
  const logger = opts.logger ?? {
21790
- info: (m) => process.stdout.write(`[kody:chat:store] ${m}
22022
+ info: (message) => process.stdout.write(`[kody:chat:store] ${message}
21791
22023
  `),
21792
- warn: (m) => process.stderr.write(`[kody:chat:store] ${m}
22024
+ warn: (message) => process.stderr.write(`[kody:chat:store] ${message}
21793
22025
  `)
21794
22026
  };
21795
22027
  const client = opts.client !== void 0 ? opts.client : createConvexClientFromEnv();
21796
22028
  const tenantId2 = opts.tenantId ?? process.env.GITHUB_REPOSITORY ?? "";
21797
- if (client && tenantId2) {
21798
- logger.info(`session ${opts.sessionId}: using Convex transcript store (tenant ${tenantId2})`);
21799
- return createConvexStore({
21800
- client,
22029
+ if (!client || !tenantId2) {
22030
+ throw new Error(
22031
+ "Canonical Convex conversation storage is required (CONVEX_URL, KODY_SERVICE_KEY, and GITHUB_REPOSITORY)"
22032
+ );
22033
+ }
22034
+ logger.info(`conversation ${opts.sessionId}: using canonical Convex store (tenant ${tenantId2})`);
22035
+ const read = async () => {
22036
+ const result = await client.query(anyApi2.conversations.get, {
21801
22037
  tenantId: tenantId2,
21802
- sessionId: opts.sessionId,
21803
- sessionFile: opts.sessionFile,
21804
- logger
22038
+ conversationId: opts.sessionId
21805
22039
  });
21806
- }
21807
- if (client && !tenantId2) {
21808
- if (process.env.GITHUB_ACTIONS === "true") {
21809
- throw new Error("Convex chat backend requires GITHUB_REPOSITORY in GitHub Actions");
21810
- }
21811
- logger.warn(`session ${opts.sessionId}: Convex configured without a tenant; using local runtime storage`);
21812
- } else {
21813
- if (process.env.GITHUB_ACTIONS === "true") {
21814
- throw new Error("Convex chat backend is required in GitHub Actions (CONVEX_URL and KODY_SERVICE_KEY)");
21815
- }
21816
- logger.info(`session ${opts.sessionId}: backend unavailable; using local runtime storage`);
21817
- }
21818
- return createLocalStore(opts.sessionFile);
21819
- }
21820
- function createLocalStore(sessionFile) {
21821
- return {
21822
- backend: "local",
21823
- readTurns: async () => readSession(sessionFile),
21824
- appendTurn: async (turn) => {
21825
- appendTurn(sessionFile, turn);
21826
- }
21827
- };
21828
- }
21829
- function normalizeTurn(turn) {
21830
- return {
21831
- role: turn.role,
21832
- content: turn.content,
21833
- timestamp: turn.timestamp,
21834
- toolCalls: turn.toolCalls ?? []
21835
- };
21836
- }
21837
- function createConvexStore(args) {
21838
- const { client, tenantId: tenantId2, sessionId, sessionFile, logger } = args;
21839
- let sessionUpserted = false;
21840
- const appendToConvex = async (turn) => {
21841
- if (!sessionUpserted) {
21842
- try {
21843
- const meta = readMeta(sessionFile) ?? { type: "meta", mode: "one-shot" };
21844
- await client.mutation(anyApi2.chatSessions.upsert, {
21845
- tenantId: tenantId2,
21846
- sessionId,
21847
- meta,
21848
- updatedAt: (/* @__PURE__ */ new Date()).toISOString()
21849
- });
21850
- sessionUpserted = true;
21851
- } catch (err) {
21852
- logger.warn(
21853
- `session ${sessionId}: chatSessions.upsert failed: ${err instanceof Error ? err.message : String(err)}`
21854
- );
21855
- }
21856
- }
21857
- await client.mutation(anyApi2.chatTurns.append, { tenantId: tenantId2, sessionId, turn });
22040
+ if (!result) throw new Error(`Conversation not found: ${opts.sessionId}`);
22041
+ return result;
21858
22042
  };
21859
22043
  return {
21860
22044
  backend: "convex",
21861
- readTurns: async () => {
21862
- const docs = await client.query(anyApi2.chatTurns.list, { tenantId: tenantId2, sessionId });
21863
- const convexTurns = [...docs].sort((a, b) => a.seq - b.seq).map((doc) => doc.turn).filter(isChatTurn);
21864
- if (convexTurns.length > 0) return convexTurns;
21865
- const tail = readSession(sessionFile).map(normalizeTurn);
21866
- if (tail.length === 0) return convexTurns;
21867
- logger.info(`session ${sessionId}: importing ${tail.length} runtime turn(s) into Convex`);
21868
- for (const turn of tail) {
21869
- await appendToConvex(turn);
21870
- }
21871
- return tail;
21872
- },
22045
+ readActiveAgent: async () => (await read()).conversation.activeAgent,
22046
+ readTurns: async () => currentEpochTurns(await read()),
21873
22047
  appendTurn: async (turn) => {
21874
- const normalized = normalizeTurn(turn);
21875
- await appendToConvex(normalized);
22048
+ const result = await read();
22049
+ const id = entryKey(opts.sessionId, turn);
22050
+ await client.mutation(anyApi2.conversations.appendEntry, {
22051
+ tenantId: tenantId2,
22052
+ conversationId: opts.sessionId,
22053
+ entryId: id,
22054
+ idempotencyKey: id,
22055
+ entry: {
22056
+ kind: "message",
22057
+ role: turn.role,
22058
+ author: turn.role === "assistant" ? { kind: "agent", ...result.conversation.activeAgent } : { kind: "user", actorId: "engine-runtime" },
22059
+ content: turn.content,
22060
+ status: "committed",
22061
+ turnId: id,
22062
+ createdAt: turn.timestamp
22063
+ }
22064
+ });
21876
22065
  }
21877
22066
  };
21878
22067
  }
@@ -22018,6 +22207,9 @@ function buildImplementationCatalog() {
22018
22207
  }
22019
22208
  return lines.join("\n");
22020
22209
  }
22210
+ function shouldWriteTaskArtifacts(driver) {
22211
+ return driver !== "codex-app-server";
22212
+ }
22021
22213
  async function runChatTurn(opts) {
22022
22214
  const store = opts.store ?? createSessionStore({ sessionId: opts.sessionId, sessionFile: opts.sessionFile });
22023
22215
  const turns = await store.readTurns();
@@ -22034,18 +22226,19 @@ async function runChatTurn(opts) {
22034
22226
  }
22035
22227
  const { turns: promptTurns, imagePaths } = prepareAttachments(turns, opts.cwd, opts.sessionId);
22036
22228
  const basePrompt = opts.systemPrompt ?? readSystemPromptOverride(opts.cwd) ?? (opts.model.protocol === "openai" ? OPENAI_CHAT_SYSTEM_PROMPT : CHAT_SYSTEM_PROMPT);
22037
- const agentIdentityBlock = readAgentIdentityBlock(opts.cwd, opts.agentIdentity);
22229
+ const activeAgent = await store.readActiveAgent();
22230
+ const agentIdentityBlock = readAgentIdentityBlock(opts.cwd, opts.agentIdentity ?? { slug: activeAgent.slug });
22038
22231
  const catalog = buildImplementationCatalog();
22039
22232
  const taskArtifactsPaths = {
22040
22233
  ...prepareTaskArtifactsDir(opts.cwd, opts.sessionId),
22041
22234
  taskKey: `sessions/${opts.sessionId}`
22042
22235
  };
22043
22236
  initializeTaskArtifacts(taskArtifactsPaths, { taskType: "chat", target: opts.sessionId });
22044
- const artifactAddendum = taskArtifactsPromptAddendum({
22237
+ const artifactAddendum = shouldWriteTaskArtifacts(opts.driver) ? taskArtifactsPromptAddendum({
22045
22238
  taskId: taskArtifactsPaths.taskId,
22046
22239
  taskType: "chat",
22047
22240
  relDir: taskArtifactsPaths.relDir
22048
- });
22241
+ }) : null;
22049
22242
  const contextBlock = readContextBlock(opts.cwd);
22050
22243
  const memoryBlock = readMemoryIndexBlock(opts.cwd);
22051
22244
  const instructionsBlock = readInstructionsBlock(opts.cwd);
@@ -22074,6 +22267,14 @@ async function runChatTurn(opts) {
22074
22267
  artifactAddendum
22075
22268
  ].filter((s) => typeof s === "string" && s.length > 0).join("\n\n");
22076
22269
  const prompt = buildPrompt(promptTurns);
22270
+ if (opts.driver === "codex-app-server") {
22271
+ return runCodexChatTurn({
22272
+ opts,
22273
+ turns: promptTurns,
22274
+ systemPrompt,
22275
+ store
22276
+ });
22277
+ }
22077
22278
  if (opts.model.protocol === "openai" && opts.litellmUrl) {
22078
22279
  return runOpenAIChatTurn({
22079
22280
  opts,
@@ -22363,13 +22564,90 @@ _\u2026 (instructions truncated)_` : trimmed;
22363
22564
  ].join("\n");
22364
22565
  }
22365
22566
 
22567
+ // src/chat/runtime-drivers.ts
22568
+ var RUNTIME_DRIVERS = {
22569
+ native: "native",
22570
+ "codex app-server": "codex-app-server",
22571
+ "codex-app-server": "codex-app-server"
22572
+ };
22573
+ function resolveBrainDriver(runtime) {
22574
+ const normalized = runtime.trim().toLowerCase();
22575
+ const driver = RUNTIME_DRIVERS[normalized];
22576
+ if (!driver) {
22577
+ throw new Error(`Unsupported Brain runtime: ${runtime}`);
22578
+ }
22579
+ return driver;
22580
+ }
22581
+
22582
+ // src/chat/session.ts
22583
+ import * as fs15 from "fs";
22584
+ import * as path16 from "path";
22585
+ import posixPath3 from "path/posix";
22586
+ function sessionFilePath(cwd, sessionId) {
22587
+ return path16.join(cwd, ".kody-engine", "runtime", "sessions", `${sessionId}.jsonl`);
22588
+ }
22589
+ function readMeta(file) {
22590
+ if (!fs15.existsSync(file)) return null;
22591
+ const raw = fs15.readFileSync(file, "utf-8");
22592
+ const firstLine2 = raw.split("\n", 1)[0]?.trim();
22593
+ if (!firstLine2) return null;
22594
+ try {
22595
+ const parsed = JSON.parse(firstLine2);
22596
+ if (parsed.type !== "meta") return null;
22597
+ if (parsed.mode !== "one-shot" && parsed.mode !== "interactive") return null;
22598
+ return parsed;
22599
+ } catch {
22600
+ return null;
22601
+ }
22602
+ }
22603
+ function readSession(file) {
22604
+ if (!fs15.existsSync(file)) return [];
22605
+ const raw = fs15.readFileSync(file, "utf-8").trim();
22606
+ if (!raw) return [];
22607
+ const turns = [];
22608
+ for (const line of raw.split("\n")) {
22609
+ if (!line.trim()) continue;
22610
+ try {
22611
+ const parsed = JSON.parse(line);
22612
+ if (parsed.role !== "user" && parsed.role !== "assistant") continue;
22613
+ if (typeof parsed.content !== "string") continue;
22614
+ turns.push(parsed);
22615
+ } catch {
22616
+ }
22617
+ }
22618
+ return turns;
22619
+ }
22620
+ function appendTurn(file, turn) {
22621
+ fs15.mkdirSync(path16.dirname(file), { recursive: true });
22622
+ const line = JSON.stringify({
22623
+ role: turn.role,
22624
+ content: turn.content,
22625
+ timestamp: turn.timestamp,
22626
+ toolCalls: turn.toolCalls ?? []
22627
+ });
22628
+ fs15.appendFileSync(file, `${line}
22629
+ `);
22630
+ }
22631
+ function seedInitialMessage(file, message) {
22632
+ if (!message.trim()) return false;
22633
+ const turns = readSession(file);
22634
+ const lastUser = [...turns].reverse().find((t) => t.role === "user");
22635
+ if (lastUser && lastUser.content === message) return false;
22636
+ appendTurn(file, {
22637
+ role: "user",
22638
+ content: message,
22639
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
22640
+ });
22641
+ return true;
22642
+ }
22643
+
22366
22644
  // src/servers/brain-serve.ts
22367
22645
  init_config();
22368
22646
 
22369
22647
  // src/kody-cli.ts
22370
22648
  import { execFileSync as execFileSync24 } from "child_process";
22371
- import * as fs46 from "fs";
22372
- import * as path45 from "path";
22649
+ import * as fs47 from "fs";
22650
+ import * as path46 from "path";
22373
22651
 
22374
22652
  // src/app-auth.ts
22375
22653
  import { createSign } from "crypto";
@@ -22496,7 +22774,7 @@ init_config();
22496
22774
 
22497
22775
  // src/dispatch.ts
22498
22776
  init_config();
22499
- import * as fs15 from "fs";
22777
+ import * as fs16 from "fs";
22500
22778
 
22501
22779
  // src/cron-match.ts
22502
22780
  var FIELD_BOUNDS = [
@@ -22598,10 +22876,10 @@ function autoDispatch(opts) {
22598
22876
  }
22599
22877
  const eventName = process.env.GITHUB_EVENT_NAME;
22600
22878
  const eventPath = process.env.GITHUB_EVENT_PATH;
22601
- if (!eventName || !eventPath || !fs15.existsSync(eventPath)) return null;
22879
+ if (!eventName || !eventPath || !fs16.existsSync(eventPath)) return null;
22602
22880
  let event = {};
22603
22881
  try {
22604
- event = JSON.parse(fs15.readFileSync(eventPath, "utf-8"));
22882
+ event = JSON.parse(fs16.readFileSync(eventPath, "utf-8"));
22605
22883
  } catch {
22606
22884
  return null;
22607
22885
  }
@@ -22725,7 +23003,7 @@ function autoDispatchTyped(opts) {
22725
23003
  if (legacy) return { kind: "route", ...legacy };
22726
23004
  const eventName = process.env.GITHUB_EVENT_NAME;
22727
23005
  const eventPath = process.env.GITHUB_EVENT_PATH;
22728
- if (!eventName || !eventPath || !fs15.existsSync(eventPath)) {
23006
+ if (!eventName || !eventPath || !fs16.existsSync(eventPath)) {
22729
23007
  return { kind: "silent", reason: "no GHA event context" };
22730
23008
  }
22731
23009
  if (eventName !== "issue_comment") {
@@ -22733,7 +23011,7 @@ function autoDispatchTyped(opts) {
22733
23011
  }
22734
23012
  let event = {};
22735
23013
  try {
22736
- event = JSON.parse(fs15.readFileSync(eventPath, "utf-8"));
23014
+ event = JSON.parse(fs16.readFileSync(eventPath, "utf-8"));
22737
23015
  } catch {
22738
23016
  return { kind: "silent", reason: "GHA event payload unreadable" };
22739
23017
  }
@@ -22787,7 +23065,7 @@ function dispatchScheduledWatches(opts) {
22787
23065
  for (const exe of listImplementations()) {
22788
23066
  let raw;
22789
23067
  try {
22790
- raw = fs15.readFileSync(exe.profilePath, "utf-8");
23068
+ raw = fs16.readFileSync(exe.profilePath, "utf-8");
22791
23069
  } catch {
22792
23070
  continue;
22793
23071
  }
@@ -23171,9 +23449,9 @@ async function resolveAuthToken(env = process.env) {
23171
23449
  return void 0;
23172
23450
  }
23173
23451
  function detectPackageManager2(cwd) {
23174
- if (fs46.existsSync(path45.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
23175
- if (fs46.existsSync(path45.join(cwd, "yarn.lock"))) return "yarn";
23176
- if (fs46.existsSync(path45.join(cwd, "bun.lockb"))) return "bun";
23452
+ if (fs47.existsSync(path46.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
23453
+ if (fs47.existsSync(path46.join(cwd, "yarn.lock"))) return "yarn";
23454
+ if (fs47.existsSync(path46.join(cwd, "bun.lockb"))) return "bun";
23177
23455
  return "npm";
23178
23456
  }
23179
23457
  function shouldChainScheduledWatch(match) {
@@ -23266,8 +23544,8 @@ function postFailureTail(issueNumber, cwd, reason) {
23266
23544
  const logPath = lastRunLogPath(cwd);
23267
23545
  let tail = "";
23268
23546
  try {
23269
- if (fs46.existsSync(logPath)) {
23270
- const content = fs46.readFileSync(logPath, "utf-8");
23547
+ if (fs47.existsSync(logPath)) {
23548
+ const content = fs47.readFileSync(logPath, "utf-8");
23271
23549
  tail = content.slice(-3e3);
23272
23550
  }
23273
23551
  } catch {
@@ -23296,7 +23574,7 @@ async function runCi(argv) {
23296
23574
  return 0;
23297
23575
  }
23298
23576
  const args = parseCiArgs(argv);
23299
- const cwd = args.cwd ? path45.resolve(args.cwd) : process.cwd();
23577
+ const cwd = args.cwd ? path46.resolve(args.cwd) : process.cwd();
23300
23578
  try {
23301
23579
  const n = unpackAllSecrets();
23302
23580
  if (n > 0) process.stdout.write(`\u2192 kody: unpacked ${n} secret(s) from ALL_SECRETS
@@ -23355,9 +23633,9 @@ async function runCi(argv) {
23355
23633
  forceRunCliArgs = { goal: envForceMessage };
23356
23634
  }
23357
23635
  }
23358
- if (!args.issueNumber && !autoFallback && !forceRunAction && !runRequestFanOut && eventName === "workflow_dispatch" && dispatchEventPath && fs46.existsSync(dispatchEventPath)) {
23636
+ if (!args.issueNumber && !autoFallback && !forceRunAction && !runRequestFanOut && eventName === "workflow_dispatch" && dispatchEventPath && fs47.existsSync(dispatchEventPath)) {
23359
23637
  try {
23360
- const evt = JSON.parse(fs46.readFileSync(dispatchEventPath, "utf-8"));
23638
+ const evt = JSON.parse(fs47.readFileSync(dispatchEventPath, "utf-8"));
23361
23639
  const inputs = objectValue2(evt.inputs);
23362
23640
  const issueInput = parseInt(String(inputs?.issue_number ?? ""), 10);
23363
23641
  const sessionInput = String(inputs?.sessionId ?? "");
@@ -23730,8 +24008,8 @@ init_repoWorkspace();
23730
24008
 
23731
24009
  // src/scripts/brainTurnLog.ts
23732
24010
  init_runtimePaths();
23733
- import * as fs47 from "fs";
23734
- import * as path46 from "path";
24011
+ import * as fs48 from "fs";
24012
+ import * as path47 from "path";
23735
24013
  import posixPath4 from "path/posix";
23736
24014
  var live = /* @__PURE__ */ new Map();
23737
24015
  function brainEventsFilePath(dir, chatId) {
@@ -23739,8 +24017,8 @@ function brainEventsFilePath(dir, chatId) {
23739
24017
  }
23740
24018
  function lastPersistedSeq(dir, chatId) {
23741
24019
  const p = brainEventsFilePath(dir, chatId);
23742
- if (!fs47.existsSync(p)) return 0;
23743
- const lines = fs47.readFileSync(p, "utf-8").split("\n").filter(Boolean);
24020
+ if (!fs48.existsSync(p)) return 0;
24021
+ const lines = fs48.readFileSync(p, "utf-8").split("\n").filter(Boolean);
23744
24022
  if (lines.length === 0) return 0;
23745
24023
  try {
23746
24024
  return JSON.parse(lines[lines.length - 1]).seq || 0;
@@ -23750,9 +24028,9 @@ function lastPersistedSeq(dir, chatId) {
23750
24028
  }
23751
24029
  function readSince(dir, chatId, since) {
23752
24030
  const p = brainEventsFilePath(dir, chatId);
23753
- if (!fs47.existsSync(p)) return [];
24031
+ if (!fs48.existsSync(p)) return [];
23754
24032
  const out = [];
23755
- for (const line of fs47.readFileSync(p, "utf-8").split("\n")) {
24033
+ for (const line of fs48.readFileSync(p, "utf-8").split("\n")) {
23756
24034
  if (!line) continue;
23757
24035
  try {
23758
24036
  const rec = JSON.parse(line);
@@ -23778,12 +24056,12 @@ function beginTurn(dir, chatId) {
23778
24056
  };
23779
24057
  live.set(chatId, state);
23780
24058
  const p = brainEventsFilePath(dir, chatId);
23781
- fs47.mkdirSync(path46.dirname(p), { recursive: true });
24059
+ fs48.mkdirSync(path47.dirname(p), { recursive: true });
23782
24060
  return (event) => {
23783
24061
  state.seq += 1;
23784
24062
  const rec = { seq: state.seq, turn, ts: Date.now(), event };
23785
24063
  try {
23786
- fs47.appendFileSync(p, `${JSON.stringify(rec)}
24064
+ fs48.appendFileSync(p, `${JSON.stringify(rec)}
23787
24065
  `);
23788
24066
  } catch (err) {
23789
24067
  process.stderr.write(
@@ -23822,7 +24100,7 @@ function endTurnIfUnterminated(dir, chatId, errMessage) {
23822
24100
  event: { type: "error", error: errMessage || "turn ended unexpectedly", chatId }
23823
24101
  };
23824
24102
  try {
23825
- fs47.appendFileSync(brainEventsFilePath(dir, chatId), `${JSON.stringify(rec)}
24103
+ fs48.appendFileSync(brainEventsFilePath(dir, chatId), `${JSON.stringify(rec)}
23826
24104
  `);
23827
24105
  } catch {
23828
24106
  }
@@ -24019,9 +24297,14 @@ var BrokerSink = class {
24019
24297
  const be = translateChatEvent(event, this.chatId);
24020
24298
  if (!be) return;
24021
24299
  this.emitToLog(be);
24022
- if (this.tenantId) await createStateBackendFromEnv().appendChatEvent(this.tenantId, this.chatId, be);
24300
+ if (this.tenantId && hasStateBackendConfig()) {
24301
+ await createStateBackendFromEnv().appendChatEvent(this.tenantId, this.chatId, be);
24302
+ }
24023
24303
  }
24024
24304
  };
24305
+ function hasStateBackendConfig(env = process.env) {
24306
+ return Boolean(env.CONVEX_URL?.trim() && env.KODY_SERVICE_KEY?.trim());
24307
+ }
24025
24308
  var chatQueues = /* @__PURE__ */ new Map();
24026
24309
  function enqueue(chatId, fn) {
24027
24310
  const prev = chatQueues.get(chatId) ?? Promise.resolve();
@@ -24081,8 +24364,18 @@ async function handleChatTurn(req, res, chatId, opts) {
24081
24364
  const dashboardUrl = strField(body, "dashboardUrl");
24082
24365
  const storeRepoUrl = strField(body, "storeRepoUrl");
24083
24366
  const storeRef = strField(body, "storeRef");
24367
+ const runtime = strField(body, "runtime");
24084
24368
  const allowCrossRepo = boolField(body, "allowCrossRepo");
24085
24369
  const agentIdentity = agentIdentityField(body);
24370
+ let turnDriver = opts.driver;
24371
+ if (runtime) {
24372
+ try {
24373
+ turnDriver = resolveBrainDriver(runtime);
24374
+ } catch (err) {
24375
+ sendJson(res, 400, { error: err instanceof Error ? err.message : String(err) });
24376
+ return;
24377
+ }
24378
+ }
24086
24379
  openSseStream(res, chatId);
24087
24380
  if (repo) {
24088
24381
  emitSse(res, {
@@ -24112,8 +24405,7 @@ async function handleChatTurn(req, res, chatId, opts) {
24112
24405
  }
24113
24406
  const stateToken = repoToken || envGithubToken();
24114
24407
  const sessionFile = sessionFilePath(agentCwd, chatId);
24115
- const brainEventsFile = brainEventsFilePath(agentCwd, chatId);
24116
- const sessionStore = createSessionStore({
24408
+ const sessionStore = opts.createStore({
24117
24409
  sessionId: chatId,
24118
24410
  sessionFile,
24119
24411
  ...repo ? { tenantId: repo } : {}
@@ -24142,6 +24434,7 @@ async function handleChatTurn(req, res, chatId, opts) {
24142
24434
  ...allowCrossRepo ? { enableFetchRepoTool: true } : {},
24143
24435
  repoToken,
24144
24436
  ...agentIdentity ? { agentIdentity } : {},
24437
+ ...turnDriver ? { driver: turnDriver } : {},
24145
24438
  ...dashboardUrl && repo && stateToken ? {
24146
24439
  cmsDashboardUrl: dashboardUrl,
24147
24440
  cmsRepoSlug: repo,
@@ -24167,8 +24460,9 @@ async function handleChatTurn(req, res, chatId, opts) {
24167
24460
  }
24168
24461
  function buildServer(opts) {
24169
24462
  const runTurn = opts.runTurn ?? runChatTurn;
24463
+ const createStore = opts.createStore ?? createSessionStore;
24170
24464
  const cloneRepo = opts.cloneRepo ?? defaultCloneRepo;
24171
- const reposRoot = opts.reposRoot ?? path47.join(path47.dirname(path47.resolve(opts.cwd)), "repos");
24465
+ const reposRoot = opts.reposRoot ?? path48.join(path48.dirname(path48.resolve(opts.cwd)), "repos");
24172
24466
  return createServer2(async (req, res) => {
24173
24467
  if (!req.method || !req.url) {
24174
24468
  sendJson(res, 400, { error: "bad request" });
@@ -24196,7 +24490,9 @@ function buildServer(opts) {
24196
24490
  cloneRepo,
24197
24491
  model: opts.model,
24198
24492
  litellmUrl: opts.litellmUrl,
24199
- runTurn
24493
+ runTurn,
24494
+ createStore,
24495
+ driver: opts.driver
24200
24496
  });
24201
24497
  return;
24202
24498
  }
@@ -24223,11 +24519,12 @@ async function brainServe(opts) {
24223
24519
  }
24224
24520
  const apiKey = getApiKey();
24225
24521
  const port = Number(process.env.PORT ?? DEFAULT_PORT);
24522
+ const driver = process.env.BRAIN_DRIVER?.trim() === "codex-app-server" ? "codex-app-server" : "native";
24226
24523
  const model = parseModelRuntimeConfig(
24227
24524
  process.env.MODEL?.trim() || "claude/claude-haiku-4-5-20251001",
24228
24525
  process.env.KODY_MODEL_CONFIG
24229
24526
  );
24230
- const usesProxy = needsLitellmProxy(model);
24527
+ const usesProxy = driver === "native" && needsLitellmProxy(model);
24231
24528
  let handle = null;
24232
24529
  if (usesProxy) {
24233
24530
  process.stdout.write(`[brain-serve] starting LiteLLM proxy for ${model.provider}/${model.model}...
@@ -24243,7 +24540,8 @@ async function brainServe(opts) {
24243
24540
  // Per-repo clones live here; defaults to a `repos` sibling of cwd.
24244
24541
  reposRoot: process.env.BRAIN_REPOS_ROOT?.trim() || void 0,
24245
24542
  model,
24246
- litellmUrl
24543
+ litellmUrl,
24544
+ driver
24247
24545
  });
24248
24546
  await new Promise((resolve16) => {
24249
24547
  server.listen(port, "0.0.0.0", () => {
@@ -24768,8 +25066,8 @@ async function loadConfigSafe() {
24768
25066
  }
24769
25067
 
24770
25068
  // src/chat-cli.ts
24771
- import * as fs48 from "fs";
24772
- import * as path48 from "path";
25069
+ import * as fs49 from "fs";
25070
+ import * as path49 from "path";
24773
25071
 
24774
25072
  // src/chat/inbox.ts
24775
25073
  import { execFileSync as execFileSync25 } from "child_process";
@@ -25041,7 +25339,7 @@ async function runChat(argv) {
25041
25339
  ${CHAT_HELP}`);
25042
25340
  return 64;
25043
25341
  }
25044
- const cwd = args.cwd ? path48.resolve(args.cwd) : process.cwd();
25342
+ const cwd = args.cwd ? path49.resolve(args.cwd) : process.cwd();
25045
25343
  const sessionId = args.sessionId;
25046
25344
  const runRequest = readRunRequestFromEnv();
25047
25345
  if (runRequest && "request" in runRequest) {
@@ -25102,7 +25400,7 @@ ${CHAT_HELP}`);
25102
25400
  const sink = buildSink(cwd, sessionId, args.dashboardUrl);
25103
25401
  const meta = readMeta(sessionFile);
25104
25402
  process.stdout.write(
25105
- `\u2192 kody:chat: session file=${sessionFile} exists=${fs48.existsSync(sessionFile)} meta=${meta ? meta.mode : "none"}
25403
+ `\u2192 kody:chat: session file=${sessionFile} exists=${fs49.existsSync(sessionFile)} meta=${meta ? meta.mode : "none"}
25106
25404
  `
25107
25405
  );
25108
25406
  try {
@@ -25148,8 +25446,8 @@ init_config();
25148
25446
  // src/definition-hydration.ts
25149
25447
  init_state_backend();
25150
25448
  import { createHash as createHash5 } from "crypto";
25151
- import * as fs49 from "fs";
25152
- import * as path49 from "path";
25449
+ import * as fs50 from "fs";
25450
+ import * as path50 from "path";
25153
25451
  var SLUG_RE2 = /^[a-z0-9][a-z0-9_-]{0,63}$/;
25154
25452
  function assertSafeDefinitionPath(filePath) {
25155
25453
  const segments = filePath.split("/");
@@ -25183,32 +25481,32 @@ function writeDefinition(root, kind, definition) {
25183
25481
  if (kind === "agent") {
25184
25482
  const raw = bundle.files["agent.md"];
25185
25483
  if (typeof raw !== "string") throw new Error(`agent definition ${definition.slug} is missing agent.md`);
25186
- fs49.writeFileSync(path49.join(root, "agents", `${definition.slug}.md`), raw, "utf8");
25484
+ fs50.writeFileSync(path50.join(root, "agents", `${definition.slug}.md`), raw, "utf8");
25187
25485
  return;
25188
25486
  }
25189
25487
  if (kind === "goal") {
25190
- const goalRoot = path49.join(root, "goals", definition.slug);
25488
+ const goalRoot = path50.join(root, "goals", definition.slug);
25191
25489
  for (const [filePath, contents] of Object.entries(bundle.files)) {
25192
- const target = path49.join(goalRoot, filePath);
25193
- fs49.mkdirSync(path49.dirname(target), { recursive: true });
25194
- fs49.writeFileSync(target, contents, "utf8");
25490
+ const target = path50.join(goalRoot, filePath);
25491
+ fs50.mkdirSync(path50.dirname(target), { recursive: true });
25492
+ fs50.writeFileSync(target, contents, "utf8");
25195
25493
  }
25196
25494
  return;
25197
25495
  }
25198
- const capabilityRoot = path49.join(root, "capabilities", definition.slug);
25496
+ const capabilityRoot = path50.join(root, "capabilities", definition.slug);
25199
25497
  for (const [filePath, contents] of Object.entries(bundle.files)) {
25200
- const target = path49.join(capabilityRoot, filePath);
25201
- fs49.mkdirSync(path49.dirname(target), { recursive: true });
25202
- fs49.writeFileSync(target, contents, "utf8");
25498
+ const target = path50.join(capabilityRoot, filePath);
25499
+ fs50.mkdirSync(path50.dirname(target), { recursive: true });
25500
+ fs50.writeFileSync(target, contents, "utf8");
25203
25501
  }
25204
25502
  }
25205
25503
  async function hydrateDefinitions(options) {
25206
- const root = path49.join(options.cwd, ".kody-engine", "definitions");
25504
+ const root = path50.join(options.cwd, ".kody-engine", "definitions");
25207
25505
  const staging = `${root}.tmp-${process.pid}-${Date.now()}`;
25208
- fs49.rmSync(staging, { recursive: true, force: true });
25209
- fs49.mkdirSync(path49.join(staging, "agents"), { recursive: true });
25210
- fs49.mkdirSync(path49.join(staging, "capabilities"), { recursive: true });
25211
- fs49.mkdirSync(path49.join(staging, "goals"), { recursive: true });
25506
+ fs50.rmSync(staging, { recursive: true, force: true });
25507
+ fs50.mkdirSync(path50.join(staging, "agents"), { recursive: true });
25508
+ fs50.mkdirSync(path50.join(staging, "capabilities"), { recursive: true });
25509
+ fs50.mkdirSync(path50.join(staging, "goals"), { recursive: true });
25212
25510
  try {
25213
25511
  const [capabilities, agents, goals] = await Promise.all([
25214
25512
  options.backend.listDefinitions(options.tenantId, "capability"),
@@ -25234,13 +25532,13 @@ async function hydrateDefinitions(options) {
25234
25532
  hydratedAt: (/* @__PURE__ */ new Date()).toISOString(),
25235
25533
  versions: Object.fromEntries(Object.entries(versions).sort(([left], [right]) => left.localeCompare(right)))
25236
25534
  };
25237
- fs49.writeFileSync(path49.join(staging, "manifest.json"), `${JSON.stringify(manifest, null, 2)}
25535
+ fs50.writeFileSync(path50.join(staging, "manifest.json"), `${JSON.stringify(manifest, null, 2)}
25238
25536
  `, "utf8");
25239
- fs49.rmSync(root, { recursive: true, force: true });
25240
- fs49.renameSync(staging, root);
25537
+ fs50.rmSync(root, { recursive: true, force: true });
25538
+ fs50.renameSync(staging, root);
25241
25539
  return { root, tenantId: options.tenantId, versions: manifest.versions };
25242
25540
  } catch (error) {
25243
- fs49.rmSync(staging, { recursive: true, force: true });
25541
+ fs50.rmSync(staging, { recursive: true, force: true });
25244
25542
  throw error;
25245
25543
  }
25246
25544
  }
@@ -25334,8 +25632,8 @@ var FlyClient = class {
25334
25632
  get fetch() {
25335
25633
  return this.opts.fetchImpl ?? fetch;
25336
25634
  }
25337
- async call(path50, init = {}) {
25338
- const res = await this.fetch(`${FLY_API_BASE}${path50}`, {
25635
+ async call(path51, init = {}) {
25636
+ const res = await this.fetch(`${FLY_API_BASE}${path51}`, {
25339
25637
  method: init.method ?? "GET",
25340
25638
  headers: {
25341
25639
  Authorization: `Bearer ${this.opts.token}`,
@@ -25346,7 +25644,7 @@ var FlyClient = class {
25346
25644
  if (res.status === 404 && init.allow404) return null;
25347
25645
  if (!res.ok) {
25348
25646
  const text2 = await res.text().catch(() => "");
25349
- throw new Error(`Fly API ${res.status} on ${path50}: ${text2.slice(0, 200) || res.statusText}`);
25647
+ throw new Error(`Fly API ${res.status} on ${path51}: ${text2.slice(0, 200) || res.statusText}`);
25350
25648
  }
25351
25649
  if (res.status === 204) return null;
25352
25650
  const raw = await res.text();
@@ -26098,8 +26396,8 @@ async function poolServe() {
26098
26396
  }
26099
26397
 
26100
26398
  // src/servers/runner-serve.ts
26101
- import { spawn as spawn8 } from "child_process";
26102
- import * as fs50 from "fs";
26399
+ import { spawn as spawn9 } from "child_process";
26400
+ import * as fs51 from "fs";
26103
26401
  import { createServer as createServer6 } from "http";
26104
26402
  var DEFAULT_PORT2 = 8080;
26105
26403
  var DEFAULT_WORKDIR = "/workspace/repo";
@@ -26234,8 +26532,8 @@ async function defaultRunJob(job) {
26234
26532
  const workdir = process.env.RUNNER_WORKDIR ?? DEFAULT_WORKDIR;
26235
26533
  const branch = job.ref ?? "main";
26236
26534
  const authUrl = `https://x-access-token:${job.githubToken}@github.com/${job.repo}.git`;
26237
- fs50.rmSync(workdir, { recursive: true, force: true });
26238
- fs50.mkdirSync(workdir, { recursive: true });
26535
+ fs51.rmSync(workdir, { recursive: true, force: true });
26536
+ fs51.mkdirSync(workdir, { recursive: true });
26239
26537
  const allSecrets = typeof job.allSecrets === "string" ? job.allSecrets : JSON.stringify(job.allSecrets ?? {});
26240
26538
  const target = job.runRequest.target;
26241
26539
  const interactive = target.type === "chat";
@@ -26265,7 +26563,7 @@ async function defaultRunJob(job) {
26265
26563
  ...interactive && job.hardCapMs ? { KODY_HARD_CAP_MS: String(job.hardCapMs) } : {}
26266
26564
  };
26267
26565
  const run = (cmd, args, cwd) => new Promise((resolve16) => {
26268
- const child = spawn8(cmd, args, { stdio: "inherit", env: childEnv, cwd });
26566
+ const child = spawn9(cmd, args, { stdio: "inherit", env: childEnv, cwd });
26269
26567
  child.on("exit", (code) => resolve16(code ?? 0));
26270
26568
  child.on("error", (err) => {
26271
26569
  process.stderr.write(`[runner-serve] ${cmd} failed: ${err.message}
@@ -26368,7 +26666,7 @@ async function runnerServe() {
26368
26666
  // src/servers/serve.ts
26369
26667
  init_config();
26370
26668
  init_litellm();
26371
- import { spawn as spawn9 } from "child_process";
26669
+ import { spawn as spawn10 } from "child_process";
26372
26670
  function parseTarget2(positional) {
26373
26671
  if (!Array.isArray(positional) || positional.length === 0) return "none";
26374
26672
  const first = String(positional[0]).toLowerCase();
@@ -26418,7 +26716,7 @@ async function serve(opts) {
26418
26716
  if (usesProxy) process.stdout.write(` ANTHROPIC_BASE_URL=${url}
26419
26717
  `);
26420
26718
  const args = ["--dangerously-skip-permissions", "--model", model.model];
26421
- const child = spawn9("claude", args, { stdio: "inherit", env: editorEnv, cwd: opts.cwd });
26719
+ const child = spawn10("claude", args, { stdio: "inherit", env: editorEnv, cwd: opts.cwd });
26422
26720
  const exitCode = await new Promise((resolve16) => {
26423
26721
  child.on("exit", (code) => resolve16(code ?? 0));
26424
26722
  child.on("error", (err) => {
@@ -26438,7 +26736,7 @@ async function serve(opts) {
26438
26736
  if (usesProxy) process.stdout.write(` ANTHROPIC_BASE_URL=${url}
26439
26737
  `);
26440
26738
  try {
26441
- const code = spawn9("code", [opts.cwd], { stdio: "inherit", env: editorEnv, detached: true });
26739
+ const code = spawn10("code", [opts.cwd], { stdio: "inherit", env: editorEnv, detached: true });
26442
26740
  code.on("error", (err) => {
26443
26741
  process.stderr.write(`[kody serve] failed to launch VS Code: ${err.message}
26444
26742
  `);