@kody-ade/kody-engine 0.4.550 → 0.4.552

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/bin/kody.js CHANGED
@@ -15,7 +15,7 @@ var init_package = __esm({
15
15
  "package.json"() {
16
16
  package_default = {
17
17
  name: "@kody-ade/kody-engine",
18
- version: "0.4.550",
18
+ version: "0.4.552",
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",
@@ -989,7 +989,7 @@ function buildVerifyEnv(source = process.env) {
989
989
  return env;
990
990
  }
991
991
  function runCommand(command, cwd) {
992
- return new Promise((resolve19) => {
992
+ return new Promise((resolve20) => {
993
993
  const start = Date.now();
994
994
  const child = spawn(command, {
995
995
  cwd,
@@ -1018,11 +1018,11 @@ function runCommand(command, cwd) {
1018
1018
  child.on("exit", (code) => {
1019
1019
  clearTimeout(timer);
1020
1020
  const tail = Buffer.concat(buffers).toString("utf-8").slice(-TAIL_CHARS);
1021
- resolve19({ exitCode: code ?? -1, durationMs: Date.now() - start, tail });
1021
+ resolve20({ exitCode: code ?? -1, durationMs: Date.now() - start, tail });
1022
1022
  });
1023
1023
  child.on("error", (err) => {
1024
1024
  clearTimeout(timer);
1025
- resolve19({ exitCode: -1, durationMs: Date.now() - start, tail: err.message });
1025
+ resolve20({ exitCode: -1, durationMs: Date.now() - start, tail: err.message });
1026
1026
  });
1027
1027
  });
1028
1028
  }
@@ -1331,7 +1331,7 @@ function cmsHeaders(opts) {
1331
1331
  }
1332
1332
  };
1333
1333
  }
1334
- async function callDashboardCms(opts, path53, init = {}) {
1334
+ async function callDashboardCms(opts, path54, init = {}) {
1335
1335
  const baseUrl = dashboardBaseUrl(opts);
1336
1336
  if (!baseUrl) {
1337
1337
  return {
@@ -1343,7 +1343,7 @@ async function callDashboardCms(opts, path53, init = {}) {
1343
1343
  const headerResult = cmsHeaders(opts);
1344
1344
  if (!headerResult.ok) return headerResult;
1345
1345
  try {
1346
- const res = await fetch(`${baseUrl}${path53}`, {
1346
+ const res = await fetch(`${baseUrl}${path54}`, {
1347
1347
  ...init,
1348
1348
  headers: {
1349
1349
  ...headerResult.headers,
@@ -1415,8 +1415,8 @@ function documentArg(value) {
1415
1415
  function normalizeCmsDocumentIdInput(input) {
1416
1416
  const trimmed = stripWrappingQuotes(input.trim());
1417
1417
  const withoutQuery = trimmed.split(/[?#]/, 1)[0] ?? trimmed;
1418
- const path53 = parseDocumentPath(withoutQuery);
1419
- return path53 ?? parseDocumentIdSegment(withoutQuery) ?? withoutQuery;
1418
+ const path54 = parseDocumentPath(withoutQuery);
1419
+ return path54 ?? parseDocumentIdSegment(withoutQuery) ?? withoutQuery;
1420
1420
  }
1421
1421
  function stripWrappingQuotes(value) {
1422
1422
  let current = value;
@@ -1427,9 +1427,9 @@ function stripWrappingQuotes(value) {
1427
1427
  }
1428
1428
  }
1429
1429
  function parseDocumentPath(value) {
1430
- const path53 = value.startsWith("http://") || value.startsWith("https://") ? urlPathname(value) : value;
1431
- if (!path53?.includes("/content/entries/")) return null;
1432
- const parts = path53.split("/").filter(Boolean).map(decodePathPart);
1430
+ const path54 = value.startsWith("http://") || value.startsWith("https://") ? urlPathname(value) : value;
1431
+ if (!path54?.includes("/content/entries/")) return null;
1432
+ const parts = path54.split("/").filter(Boolean).map(decodePathPart);
1433
1433
  const entriesIndex = parts.findIndex((part, index) => part === "content" && parts[index + 1] === "entries");
1434
1434
  const idPart = parts[entriesIndex + 3];
1435
1435
  if (!idPart || idPart === "new") return null;
@@ -1997,7 +1997,9 @@ function parseCapabilityContract(raw) {
1997
1997
  function parseCapabilityRequirements(raw) {
1998
1998
  if (raw === void 0) return void 0;
1999
1999
  if (!isPlainObject(raw)) throw new Error("contract.json requirements must be an object");
2000
- const unsupported = Object.keys(raw).filter((key) => key !== "browser" && key !== "qaCredentials");
2000
+ const unsupported = Object.keys(raw).filter(
2001
+ (key) => key !== "browser" && key !== "qaCredentials" && key !== "githubTestToken" && key !== "browserOnly"
2002
+ );
2001
2003
  if (unsupported.length > 0) {
2002
2004
  throw new Error(`contract.json requirements contains unsupported fields: ${unsupported.join(", ")}`);
2003
2005
  }
@@ -2007,12 +2009,20 @@ function parseCapabilityRequirements(raw) {
2007
2009
  if (raw.qaCredentials !== void 0 && typeof raw.qaCredentials !== "boolean") {
2008
2010
  throw new Error("contract.json requirements.qaCredentials must be boolean");
2009
2011
  }
2010
- if (raw.qaCredentials === true && raw.browser !== true) {
2011
- throw new Error("contract.json requirements.qaCredentials requires browser");
2012
+ if (raw.githubTestToken !== void 0 && typeof raw.githubTestToken !== "boolean") {
2013
+ throw new Error("contract.json requirements.githubTestToken must be boolean");
2014
+ }
2015
+ if (raw.browserOnly !== void 0 && typeof raw.browserOnly !== "boolean") {
2016
+ throw new Error("contract.json requirements.browserOnly must be boolean");
2017
+ }
2018
+ if ((raw.qaCredentials === true || raw.githubTestToken === true || raw.browserOnly === true) && raw.browser !== true) {
2019
+ throw new Error("contract.json authentication requirements require browser");
2012
2020
  }
2013
2021
  const requirements = {
2014
2022
  ...raw.browser === true ? { browser: true } : {},
2015
- ...raw.qaCredentials === true ? { qaCredentials: true } : {}
2023
+ ...raw.qaCredentials === true ? { qaCredentials: true } : {},
2024
+ ...raw.githubTestToken === true ? { githubTestToken: true } : {},
2025
+ ...raw.browserOnly === true ? { browserOnly: true } : {}
2016
2026
  };
2017
2027
  return Object.keys(requirements).length > 0 ? requirements : void 0;
2018
2028
  }
@@ -2027,8 +2037,8 @@ function isRegularFile(filePath) {
2027
2037
  function schemaPropertyPaths(schema, prefix) {
2028
2038
  const properties = isPlainObject(schema.properties) ? schema.properties : {};
2029
2039
  return Object.entries(properties).flatMap(([name, property]) => {
2030
- const path53 = `${prefix}.${name}`;
2031
- return isPlainObject(property) ? [path53, ...schemaPropertyPaths(property, path53)] : [path53];
2040
+ const path54 = `${prefix}.${name}`;
2041
+ return isPlainObject(property) ? [path54, ...schemaPropertyPaths(property, path54)] : [path54];
2032
2042
  });
2033
2043
  }
2034
2044
  function parseCapabilityBody(raw, slug) {
@@ -3620,7 +3630,7 @@ var init_repoWorkspace = __esm({
3620
3630
  defaultCloneRepo = (repo, token, dir) => {
3621
3631
  fs9.mkdirSync(path10.dirname(dir), { recursive: true });
3622
3632
  const clone = buildCloneProcess(repo, token);
3623
- return new Promise((resolve19, reject) => {
3633
+ return new Promise((resolve20, reject) => {
3624
3634
  const child = spawn2("git", ["clone", "--depth=1", clone.url, dir], {
3625
3635
  env: clone.env,
3626
3636
  stdio: "inherit"
@@ -3640,7 +3650,7 @@ var init_repoWorkspace = __esm({
3640
3650
  }
3641
3651
  } catch {
3642
3652
  }
3643
- resolve19();
3653
+ resolve20();
3644
3654
  });
3645
3655
  child.on("error", reject);
3646
3656
  });
@@ -3971,10 +3981,10 @@ async function runAgent(opts) {
3971
3981
  let timer;
3972
3982
  let next;
3973
3983
  if (turnTimeoutMs > 0) {
3974
- const timeoutPromise = new Promise((resolve19) => {
3984
+ const timeoutPromise = new Promise((resolve20) => {
3975
3985
  timer = setTimeout(() => {
3976
3986
  timedOut = true;
3977
- resolve19({ done: true, value: void 0 });
3987
+ resolve20({ done: true, value: void 0 });
3978
3988
  }, turnTimeoutMs);
3979
3989
  });
3980
3990
  next = await Promise.race([nextPromise, timeoutPromise]);
@@ -3990,7 +4000,7 @@ async function runAgent(opts) {
3990
4000
  try {
3991
4001
  await Promise.race([
3992
4002
  iterator.return(void 0).catch(() => void 0),
3993
- new Promise((resolve19) => setTimeout(resolve19, 1e4).unref())
4003
+ new Promise((resolve20) => setTimeout(resolve20, 1e4).unref())
3994
4004
  ]);
3995
4005
  } catch {
3996
4006
  }
@@ -4614,15 +4624,15 @@ function validateWorkflow(value, options = {}) {
4614
4624
  }
4615
4625
  return issues;
4616
4626
  }
4617
- function validateInputBindings(value, path53, issues, declaredInputs) {
4627
+ function validateInputBindings(value, path54, issues, declaredInputs) {
4618
4628
  if (value === void 0) return;
4619
4629
  const bindings = asRecord(value);
4620
4630
  if (!bindings || Object.keys(bindings).length === 0) {
4621
- issue(issues, "invalid_inputs", path53, "workflow step inputs must contain at least one named mapping");
4631
+ issue(issues, "invalid_inputs", path54, "workflow step inputs must contain at least one named mapping");
4622
4632
  return;
4623
4633
  }
4624
4634
  for (const [name, value2] of Object.entries(bindings)) {
4625
- const bindingPath = `${path53}.${name}`;
4635
+ const bindingPath = `${path54}.${name}`;
4626
4636
  if (!/^[A-Za-z_][A-Za-z0-9_-]*$/.test(name)) {
4627
4637
  issue(issues, "invalid_input_name", bindingPath, `workflow input name ${name} is invalid`);
4628
4638
  }
@@ -4641,7 +4651,7 @@ function validateInputBindings(value, path53, issues, declaredInputs) {
4641
4651
  }
4642
4652
  }
4643
4653
  }
4644
- function validateInputBindingSources(value, path53, issues, capabilitiesByStep, capabilityOutputs) {
4654
+ function validateInputBindingSources(value, path54, issues, capabilitiesByStep, capabilityOutputs) {
4645
4655
  const bindings = asRecord(value);
4646
4656
  if (!bindings) return;
4647
4657
  for (const [name, rawBinding] of Object.entries(bindings)) {
@@ -4654,7 +4664,7 @@ function validateInputBindingSources(value, path53, issues, capabilitiesByStep,
4654
4664
  issue(
4655
4665
  issues,
4656
4666
  "missing_input_step",
4657
- `${path53}.${name}.from`,
4667
+ `${path54}.${name}.from`,
4658
4668
  `workflow input mapping references missing step ${sourceStep ?? "<none>"}`
4659
4669
  );
4660
4670
  continue;
@@ -4665,7 +4675,7 @@ function validateInputBindingSources(value, path53, issues, capabilitiesByStep,
4665
4675
  issue(
4666
4676
  issues,
4667
4677
  "undeclared_step_output",
4668
- `${path53}.${name}.from`,
4678
+ `${path54}.${name}.from`,
4669
4679
  `workflow input mapping reads ${outputPath}, but step ${sourceStep} does not declare it`
4670
4680
  );
4671
4681
  }
@@ -4674,11 +4684,11 @@ function validateInputBindingSources(value, path53, issues, capabilitiesByStep,
4674
4684
  function formatWorkflowValidationIssues(issues) {
4675
4685
  return issues.map((entry) => `${entry.path}: ${entry.message}`);
4676
4686
  }
4677
- function validateDataMatch(value, path53, issues, capabilityOutputs) {
4687
+ function validateDataMatch(value, path54, issues, capabilityOutputs) {
4678
4688
  if (value === void 0) return;
4679
4689
  const match = asRecord(value);
4680
4690
  if (!match || Object.keys(match).length === 0) {
4681
- issue(issues, "invalid_condition", path53, "workflow condition must contain at least one match");
4691
+ issue(issues, "invalid_condition", path54, "workflow condition must contain at least one match");
4682
4692
  return;
4683
4693
  }
4684
4694
  for (const [field, expected] of Object.entries(match)) {
@@ -4686,7 +4696,7 @@ function validateDataMatch(value, path53, issues, capabilityOutputs) {
4686
4696
  issue(
4687
4697
  issues,
4688
4698
  "invalid_data_path",
4689
- `${path53}.${field}`,
4699
+ `${path54}.${field}`,
4690
4700
  `workflow condition must read from facts, evidence, artifacts, result, workflow, or lastOutcome`
4691
4701
  );
4692
4702
  }
@@ -4694,12 +4704,12 @@ function validateDataMatch(value, path53, issues, capabilityOutputs) {
4694
4704
  issue(
4695
4705
  issues,
4696
4706
  "undeclared_result_path",
4697
- `${path53}.${field}`,
4707
+ `${path54}.${field}`,
4698
4708
  `workflow condition reads ${field}, but the source capability does not declare it`
4699
4709
  );
4700
4710
  }
4701
4711
  if (!isComparable(expected)) {
4702
- issue(issues, "invalid_condition_value", `${path53}.${field}`, "workflow condition value must be a JSON scalar");
4712
+ issue(issues, "invalid_condition_value", `${path54}.${field}`, "workflow condition value must be a JSON scalar");
4703
4713
  }
4704
4714
  }
4705
4715
  }
@@ -4723,8 +4733,8 @@ function isJsonValue(value) {
4723
4733
  if (!value || typeof value !== "object") return false;
4724
4734
  return Object.values(value).every(isJsonValue);
4725
4735
  }
4726
- function issue(issues, code, path53, message) {
4727
- issues.push({ code, path: path53, message });
4736
+ function issue(issues, code, path54, message) {
4737
+ issues.push({ code, path: path54, message });
4728
4738
  }
4729
4739
  var SAFE_NAME, SAFE_STEP_ID, SAFE_DATA_PATH, SAFE_INPUT_SOURCE, SUPPORTED_STEP_FIELDS, SUPPORTED_TRANSITION_FIELDS;
4730
4740
  var init_workflowValidation = __esm({
@@ -7600,11 +7610,11 @@ async function nextAvailableLitellmUrl(url) {
7600
7610
  throw new Error(`no free LiteLLM port found after ${startPort}`);
7601
7611
  }
7602
7612
  function canListen(port, host) {
7603
- return new Promise((resolve19) => {
7613
+ return new Promise((resolve20) => {
7604
7614
  const server = net.createServer();
7605
- server.once("error", () => resolve19(false));
7615
+ server.once("error", () => resolve20(false));
7606
7616
  server.once("listening", () => {
7607
- server.close(() => resolve19(true));
7617
+ server.close(() => resolve20(true));
7608
7618
  });
7609
7619
  server.listen(port, host);
7610
7620
  });
@@ -9027,9 +9037,9 @@ import * as fs28 from "fs";
9027
9037
  function stageGoalRunLogEvent(data, goalId, event, at = nowIso()) {
9028
9038
  const logs = goalRunLogs(data);
9029
9039
  const existing = logs[goalId];
9030
- const path53 = existing?.path ?? goalRunLogPath(goalId, data);
9040
+ const path54 = existing?.path ?? goalRunLogPath(goalId, data);
9031
9041
  logs[goalId] = {
9032
- path: path53,
9042
+ path: path54,
9033
9043
  events: [...existing?.events ?? [], buildGoalRunLogEvent(data, goalId, event, at)]
9034
9044
  };
9035
9045
  }
@@ -9906,15 +9916,15 @@ var init_backendStateBackend = __esm({
9906
9916
  this.jobsDir = opts.jobsDir.replace(/\/+$/, "");
9907
9917
  }
9908
9918
  async load(slug) {
9909
- const path53 = stateFilePath(this.jobsDir, slug);
9919
+ const path54 = stateFilePath(this.jobsDir, slug);
9910
9920
  const loaded = await createStateBackendFromEnv().get(this.tenantId, `capabilities/${slug}`, "job-state");
9911
9921
  if (!loaded) {
9912
- return { path: path53, handle: null, state: initialStateEnvelope("seed"), created: true };
9922
+ return { path: path54, handle: null, state: initialStateEnvelope("seed"), created: true };
9913
9923
  }
9914
9924
  if (!isStateEnvelope(loaded.doc)) {
9915
9925
  throw new Error(`BackendStateBackend: capabilities/${slug} is not a StateEnvelope`);
9916
9926
  }
9917
- return { path: path53, handle: loaded.updatedAt, state: loaded.doc, created: false };
9927
+ return { path: path54, handle: loaded.updatedAt, state: loaded.doc, created: false };
9918
9928
  }
9919
9929
  async save(loaded, next) {
9920
9930
  if (!loaded.created && isStateUnchanged(loaded.state, next)) return false;
@@ -15343,13 +15353,13 @@ var init_loadCapabilityState = __esm({
15343
15353
  function isCompanyIntentId(value) {
15344
15354
  return SLUG_RE2.test(value);
15345
15355
  }
15346
- function normalizeCompanyIntent(path53, raw) {
15356
+ function normalizeCompanyIntent(path54, raw) {
15347
15357
  if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
15348
- throw new Error(`${path53}: intent must be JSON object`);
15358
+ throw new Error(`${path54}: intent must be JSON object`);
15349
15359
  }
15350
15360
  const input = raw;
15351
15361
  const id = stringField4(input.id);
15352
- if (!id || !isCompanyIntentId(id)) throw new Error(`${path53}: invalid intent id`);
15362
+ if (!id || !isCompanyIntentId(id)) throw new Error(`${path54}: invalid intent id`);
15353
15363
  const createdAt = stringField4(input.createdAt) || nowIso();
15354
15364
  const updatedAt = stringField4(input.updatedAt) || createdAt;
15355
15365
  const description = stringField4(input.description);
@@ -15511,7 +15521,7 @@ function retryDelaysMs() {
15511
15521
  }
15512
15522
  function sleep(ms) {
15513
15523
  if (ms <= 0) return Promise.resolve();
15514
- return new Promise((resolve19) => setTimeout(resolve19, ms));
15524
+ return new Promise((resolve20) => setTimeout(resolve20, ms));
15515
15525
  }
15516
15526
  async function fetchGoalStateWithRetry(config, goalId, cwd) {
15517
15527
  let state = await fetchGoalStateAsync(config, goalId, cwd);
@@ -16528,19 +16538,19 @@ function parseAgencyModelProposal(raw) {
16528
16538
  function normalizeBundleFiles(bundle) {
16529
16539
  const seen = /* @__PURE__ */ new Set();
16530
16540
  return bundle.files.map((file, index) => {
16531
- const path53 = file.path.replace(/^\/+/, "");
16532
- const parts = path53.split("/");
16533
- if (!path53 || file.path.startsWith("/") || file.path.includes("\\") || parts.some((part) => !part || part === "." || part === "..")) {
16541
+ const path54 = file.path.replace(/^\/+/, "");
16542
+ const parts = path54.split("/");
16543
+ if (!path54 || file.path.startsWith("/") || file.path.includes("\\") || parts.some((part) => !part || part === "." || part === "..")) {
16534
16544
  throw new Error(`openAgencyModelReviewPr: files[${index}].path is unsafe`);
16535
16545
  }
16536
16546
  if (!/^(agents\/[^/]+\.md|capabilities\/[^/]+\/.+|goals\/(?:templates\/)?[^/]+\/.+|workflows\/[^/]+\.json)$/.test(
16537
- path53
16547
+ path54
16538
16548
  )) {
16539
16549
  throw new Error(`openAgencyModelReviewPr: files[${index}].path is not a supported definition path`);
16540
16550
  }
16541
- if (seen.has(path53)) throw new Error(`openAgencyModelReviewPr: duplicate generated file path: ${path53}`);
16542
- seen.add(path53);
16543
- return { path: path53, content: file.content.replace(/\r\n?/g, "\n") };
16551
+ if (seen.has(path54)) throw new Error(`openAgencyModelReviewPr: duplicate generated file path: ${path54}`);
16552
+ seen.add(path54);
16553
+ return { path: path54, content: file.content.replace(/\r\n?/g, "\n") };
16544
16554
  });
16545
16555
  }
16546
16556
  function buildProposalId(issueNumber, bundle, sourceLabel) {
@@ -17951,16 +17961,67 @@ var init_prepareCapabilityDelivery = __esm({
17951
17961
  });
17952
17962
 
17953
17963
  // src/scripts/prepareSimpleCapabilityRuntime.ts
17964
+ import { isIP } from "net";
17965
+ import * as path43 from "path";
17954
17966
  function requirementsFrom(ctx) {
17955
17967
  const raw = ctx.data.capabilityRequirements;
17956
17968
  return raw && typeof raw === "object" && !Array.isArray(raw) ? raw : {};
17957
17969
  }
17958
- function configureBrowser(profile) {
17970
+ function isPrivateTargetHost(hostname) {
17971
+ const host = hostname.toLowerCase();
17972
+ if (host === "localhost" || host.endsWith(".localhost") || host.endsWith(".local")) return true;
17973
+ if (isIP(host) === 4) {
17974
+ const [a, b] = host.split(".").map(Number);
17975
+ return a === 10 || a === 127 || a === 169 && b === 254 || a === 172 && b >= 16 && b <= 31 || a === 192 && b === 168;
17976
+ }
17977
+ if (isIP(host) === 6) {
17978
+ return host === "::1" || host.startsWith("fc") || host.startsWith("fd") || host.startsWith("fe8");
17979
+ }
17980
+ return false;
17981
+ }
17982
+ function browserRuntime(ctx, requirements) {
17983
+ if (!requirements.browserOnly) return PLAYWRIGHT_SERVER;
17984
+ const input = ctx.data.capabilityInput;
17985
+ if (!input || typeof input !== "object" || Array.isArray(input)) {
17986
+ throw new Error("Restricted browser capability requires object input");
17987
+ }
17988
+ const targetUrl = input.targetUrl;
17989
+ const qualityRunId = input.qualityRunId;
17990
+ let origin = "";
17991
+ try {
17992
+ const parsed = new URL(typeof targetUrl === "string" ? targetUrl : "");
17993
+ if (parsed.protocol === "https:" && !parsed.username && !parsed.password && !isPrivateTargetHost(parsed.hostname)) {
17994
+ origin = parsed.origin;
17995
+ }
17996
+ } catch {
17997
+ }
17998
+ if (!origin) throw new Error("Restricted browser capability requires a public HTTPS targetUrl");
17999
+ if (typeof qualityRunId !== "string" || !/^[A-Za-z0-9_-]{1,200}$/.test(qualityRunId)) {
18000
+ throw new Error("Restricted browser capability requires a valid qualityRunId");
18001
+ }
18002
+ return {
18003
+ ...PLAYWRIGHT_SERVER,
18004
+ args: [
18005
+ ...PLAYWRIGHT_SERVER.args,
18006
+ "--allowed-origins",
18007
+ origin,
18008
+ "--output-dir",
18009
+ path43.resolve(ctx.cwd, "test-results", "quality-runs", qualityRunId)
18010
+ ]
18011
+ };
18012
+ }
18013
+ function configureBrowser(ctx, profile, requirements) {
18014
+ const server = browserRuntime(ctx, requirements);
18015
+ if (requirements.browserOnly) {
18016
+ profile.claudeCode.tools = ["Write"];
18017
+ profile.claudeCode.permissionMode = "default";
18018
+ profile.claudeCode.maxTurns = Math.min(profile.claudeCode.maxTurns ?? 50, 50);
18019
+ }
17959
18020
  if (!profile.claudeCode.tools.includes("mcp__playwright")) {
17960
18021
  profile.claudeCode.tools = [...profile.claudeCode.tools, "mcp__playwright"];
17961
18022
  }
17962
18023
  if (!profile.claudeCode.mcpServers.some(({ name }) => name === PLAYWRIGHT_SERVER.name)) {
17963
- profile.claudeCode.mcpServers = [...profile.claudeCode.mcpServers, PLAYWRIGHT_SERVER];
18024
+ profile.claudeCode.mcpServers = [...profile.claudeCode.mcpServers, server];
17964
18025
  }
17965
18026
  }
17966
18027
  function appendPrompt(ctx, section) {
@@ -17972,6 +18033,7 @@ var init_prepareSimpleCapabilityRuntime = __esm({
17972
18033
  "src/scripts/prepareSimpleCapabilityRuntime.ts"() {
17973
18034
  "use strict";
17974
18035
  init_loadQaContext();
18036
+ init_runtimeSecrets();
17975
18037
  PLAYWRIGHT_SERVER = {
17976
18038
  name: "playwright",
17977
18039
  command: "npx",
@@ -17980,19 +18042,36 @@ var init_prepareSimpleCapabilityRuntime = __esm({
17980
18042
  prepareSimpleCapabilityRuntime = async (ctx, profile) => {
17981
18043
  const requirements = requirementsFrom(ctx);
17982
18044
  if (!requirements.browser) return;
17983
- configureBrowser(profile);
17984
- if (!requirements.qaCredentials) return;
17985
- await loadQaContext(ctx, profile);
17986
- appendPrompt(
17987
- ctx,
17988
- [
17989
- "## QA authentication",
17990
- "",
17991
- String(ctx.data.qaAuthBlock ?? ""),
17992
- "",
17993
- "If the changed surface requires authentication and the credentials are missing or the login is rejected, return a blocked result with a safe explanation. Do not include usernames, passwords, tokens, or other credential values in the result."
17994
- ].join("\n")
17995
- );
18045
+ configureBrowser(ctx, profile, requirements);
18046
+ if (requirements.qaCredentials) {
18047
+ await loadQaContext(ctx, profile);
18048
+ appendPrompt(
18049
+ ctx,
18050
+ [
18051
+ "## QA authentication",
18052
+ "",
18053
+ String(ctx.data.qaAuthBlock ?? ""),
18054
+ "",
18055
+ "If the changed surface requires authentication and the credentials are missing or the login is rejected, return a blocked result with a safe explanation. Do not include usernames, passwords, tokens, or other credential values in the result."
18056
+ ].join("\n")
18057
+ );
18058
+ }
18059
+ if (requirements.githubTestToken) {
18060
+ const token = await resolveRuntimeSecret("E2E_GITHUB_TOKEN", ctx);
18061
+ appendPrompt(
18062
+ ctx,
18063
+ token.value ? [
18064
+ "## Protected GitHub test login",
18065
+ "",
18066
+ `A protected GitHub test token is available: \`${token.value}\``,
18067
+ "Use it only if the target application asks for a GitHub personal access token; never include the token in screenshots, output, logs, files, or messages."
18068
+ ].join("\n") : [
18069
+ "## Protected GitHub test login",
18070
+ "",
18071
+ "E2E_GITHUB_TOKEN is not configured. If this Quality Scenario requires GitHub token authentication, return a blocked result."
18072
+ ].join("\n")
18073
+ );
18074
+ }
17996
18075
  };
17997
18076
  }
17998
18077
  });
@@ -18279,9 +18358,9 @@ function latestResult(raw, agentResult) {
18279
18358
  function recordField4(value) {
18280
18359
  return value && typeof value === "object" && !Array.isArray(value) ? value : null;
18281
18360
  }
18282
- function resolveDotted(root, path53) {
18283
- if (!path53) return void 0;
18284
- return path53.split(".").reduce((value, key) => recordField4(value)?.[key], root);
18361
+ function resolveDotted(root, path54) {
18362
+ if (!path54) return void 0;
18363
+ return path54.split(".").reduce((value, key) => recordField4(value)?.[key], root);
18285
18364
  }
18286
18365
  function stringValue5(value) {
18287
18366
  return typeof value === "string" && value.trim() ? value.trim() : null;
@@ -19123,7 +19202,7 @@ var init_previewBuildHelpers = __esm({
19123
19202
  // src/scripts/previewBuildRun.ts
19124
19203
  import { spawn as spawn5 } from "child_process";
19125
19204
  async function runCmd(cmd, args, opts = {}) {
19126
- await new Promise((resolve19, reject) => {
19205
+ await new Promise((resolve20, reject) => {
19127
19206
  const child = spawn5(cmd, args, {
19128
19207
  cwd: opts.cwd,
19129
19208
  env: { ...process.env, ...opts.env ?? {} },
@@ -19135,7 +19214,7 @@ async function runCmd(cmd, args, opts = {}) {
19135
19214
  }
19136
19215
  child.on("error", reject);
19137
19216
  child.on("close", (code) => {
19138
- if (code === 0) resolve19();
19217
+ if (code === 0) resolve20();
19139
19218
  else reject(new Error(`${cmd} ${args.join(" ")} exited ${code}`));
19140
19219
  });
19141
19220
  });
@@ -19207,12 +19286,12 @@ fi
19207
19286
 
19208
19287
  // src/scripts/runPreviewBuild.ts
19209
19288
  import { copyFile, writeFile } from "fs/promises";
19210
- import * as path43 from "path";
19289
+ import * as path44 from "path";
19211
19290
  import { fileURLToPath as fileURLToPath2 } from "url";
19212
19291
  function bundledDockerfilePath(mode) {
19213
- const here = path43.dirname(fileURLToPath2(import.meta.url));
19292
+ const here = path44.dirname(fileURLToPath2(import.meta.url));
19214
19293
  const file = mode === "dev" ? "default-Dockerfile.preview.dev" : "default-Dockerfile.preview.prod";
19215
- return path43.join(here, "preview-build-templates", file);
19294
+ return path44.join(here, "preview-build-templates", file);
19216
19295
  }
19217
19296
  function required(name) {
19218
19297
  const v = (process.env[name] ?? "").trim();
@@ -19447,10 +19526,10 @@ var init_runPreviewBuild = __esm({
19447
19526
  console.log(`[preview-build] vault: ${Object.keys(buildEnv).length} secrets, mode=${buildMode}`);
19448
19527
  if (Object.keys(buildEnv).length > 0) {
19449
19528
  const lines = Object.entries(buildEnv).map(([k, v]) => `${k}=${JSON.stringify(v)}`);
19450
- await writeFile(path43.join(ctx.cwd, ".env.production.local"), `${lines.join("\n")}
19529
+ await writeFile(path44.join(ctx.cwd, ".env.production.local"), `${lines.join("\n")}
19451
19530
  `, "utf8");
19452
19531
  }
19453
- const consumerDockerfile = path43.join(ctx.cwd, "Dockerfile.preview");
19532
+ const consumerDockerfile = path44.join(ctx.cwd, "Dockerfile.preview");
19454
19533
  const { stat } = await import("fs/promises");
19455
19534
  let hasConsumerDockerfile = false;
19456
19535
  try {
@@ -19635,7 +19714,7 @@ var init_tickShellRunner = __esm({
19635
19714
 
19636
19715
  // src/scripts/runScheduledImplementationTick.ts
19637
19716
  import * as fs47 from "fs";
19638
- import * as path44 from "path";
19717
+ import * as path45 from "path";
19639
19718
  var runScheduledImplementationTick;
19640
19719
  var init_runScheduledImplementationTick = __esm({
19641
19720
  "src/scripts/runScheduledImplementationTick.ts"() {
@@ -19656,13 +19735,13 @@ var init_runScheduledImplementationTick = __esm({
19656
19735
  ctx.output.reason = `runScheduledImplementationTick: args.slug or ctx.args.${slugArg} must be non-empty capability slug`;
19657
19736
  return;
19658
19737
  }
19659
- const capability = resolveCapabilityFolder(slug, path44.resolve(ctx.cwd, jobsDir));
19738
+ const capability = resolveCapabilityFolder(slug, path45.resolve(ctx.cwd, jobsDir));
19660
19739
  if (!capability) {
19661
19740
  ctx.output.exitCode = 99;
19662
19741
  ctx.output.reason = `runScheduledImplementationTick: capability folder not found or incomplete: ${slug} (searched ${jobsDir} and company store)`;
19663
19742
  return;
19664
19743
  }
19665
- const shellPath = path44.join(profile.dir, shell);
19744
+ const shellPath = path45.join(profile.dir, shell);
19666
19745
  if (!fs47.existsSync(shellPath)) {
19667
19746
  ctx.output.exitCode = 99;
19668
19747
  ctx.output.reason = `runScheduledImplementationTick: shell not found: ${shell} (looked in ${profile.dir})`;
@@ -19781,7 +19860,7 @@ var init_runSimpleCapabilityScript = __esm({
19781
19860
 
19782
19861
  // src/scripts/runTickScript.ts
19783
19862
  import * as fs49 from "fs";
19784
- import * as path45 from "path";
19863
+ import * as path46 from "path";
19785
19864
  var runTickScript;
19786
19865
  var init_runTickScript = __esm({
19787
19866
  "src/scripts/runTickScript.ts"() {
@@ -19801,10 +19880,10 @@ var init_runTickScript = __esm({
19801
19880
  ctx.output.reason = `runTickScript: ctx.args.${slugArg} must be a non-empty slug`;
19802
19881
  return;
19803
19882
  }
19804
- const capability = readCapabilityFolder(path45.resolve(ctx.cwd, jobsDir), slug);
19883
+ const capability = readCapabilityFolder(path46.resolve(ctx.cwd, jobsDir), slug);
19805
19884
  if (!capability) {
19806
19885
  ctx.output.exitCode = 99;
19807
- ctx.output.reason = `runTickScript: capability folder not found or incomplete: ${path45.resolve(ctx.cwd, jobsDir, slug)}`;
19886
+ ctx.output.reason = `runTickScript: capability folder not found or incomplete: ${path46.resolve(ctx.cwd, jobsDir, slug)}`;
19808
19887
  return;
19809
19888
  }
19810
19889
  const tickScript = capability.config.tickScript;
@@ -19813,7 +19892,7 @@ var init_runTickScript = __esm({
19813
19892
  ctx.output.reason = `runTickScript: capability ${slug} has no \`tickScript\` in profile.json \u2014 route via capability-tick instead`;
19814
19893
  return;
19815
19894
  }
19816
- const scriptPath = path45.isAbsolute(tickScript) ? tickScript : path45.join(ctx.cwd, tickScript);
19895
+ const scriptPath = path46.isAbsolute(tickScript) ? tickScript : path46.join(ctx.cwd, tickScript);
19817
19896
  if (!fs49.existsSync(scriptPath)) {
19818
19897
  ctx.output.exitCode = 99;
19819
19898
  ctx.output.reason = `runTickScript: tickScript not found: ${scriptPath}`;
@@ -20096,7 +20175,7 @@ var init_syncFlow = __esm({
20096
20175
  });
20097
20176
 
20098
20177
  // src/scripts/validateAgencyModelProposal.ts
20099
- import * as path46 from "path";
20178
+ import * as path47 from "path";
20100
20179
  function validateModelBundle(bundle, expectedKind, options = {}) {
20101
20180
  const failures = [];
20102
20181
  validateOneModel(bundle.model, bundle.files, "model", true, failures, expectedKind, options);
@@ -20414,7 +20493,7 @@ var init_validateAgencyModelProposal = __esm({
20414
20493
  const bundle = parseAgencyModelProposal(raw);
20415
20494
  const expectedKind = readExpectedModelKind(args);
20416
20495
  const failures = validateModelBundle(bundle, expectedKind, {
20417
- capabilityRoot: path46.join(ctx.cwd, ".kody", "capabilities")
20496
+ capabilityRoot: path47.join(ctx.cwd, ".kody", "capabilities")
20418
20497
  });
20419
20498
  if (failures.length > 0) {
20420
20499
  throw new Error(`validateAgencyModelProposal: ${failures.join("; ")}`);
@@ -20477,7 +20556,7 @@ function stripAnsi2(s) {
20477
20556
  return s.replace(ANSI_RE2, "");
20478
20557
  }
20479
20558
  function runCommand2(command, cwd) {
20480
- return new Promise((resolve19) => {
20559
+ return new Promise((resolve20) => {
20481
20560
  const child = spawn6(command, {
20482
20561
  cwd,
20483
20562
  shell: true,
@@ -20504,11 +20583,11 @@ function runCommand2(command, cwd) {
20504
20583
  }, TEST_TIMEOUT_MS);
20505
20584
  child.on("exit", (code) => {
20506
20585
  clearTimeout(timer);
20507
- resolve19({ exitCode: code ?? -1, output: Buffer.concat(buffers).toString("utf-8") });
20586
+ resolve20({ exitCode: code ?? -1, output: Buffer.concat(buffers).toString("utf-8") });
20508
20587
  });
20509
20588
  child.on("error", (err) => {
20510
20589
  clearTimeout(timer);
20511
- resolve19({ exitCode: -1, output: err.message });
20590
+ resolve20({ exitCode: -1, output: err.message });
20512
20591
  });
20513
20592
  });
20514
20593
  }
@@ -20914,21 +20993,21 @@ function lineStream(stream) {
20914
20993
  tryDeliver();
20915
20994
  });
20916
20995
  return {
20917
- next: (timeoutMs) => new Promise((resolve19) => {
20996
+ next: (timeoutMs) => new Promise((resolve20) => {
20918
20997
  if (queue.length > 0) {
20919
- resolve19(queue.shift());
20998
+ resolve20(queue.shift());
20920
20999
  return;
20921
21000
  }
20922
21001
  if (ended) {
20923
- resolve19(null);
21002
+ resolve20(null);
20924
21003
  return;
20925
21004
  }
20926
- waiter = resolve19;
21005
+ waiter = resolve20;
20927
21006
  const t = setTimeout(
20928
21007
  () => {
20929
- if (waiter === resolve19) {
21008
+ if (waiter === resolve20) {
20930
21009
  waiter = null;
20931
- resolve19(null);
21010
+ resolve20(null);
20932
21011
  }
20933
21012
  },
20934
21013
  Math.max(0, timeoutMs)
@@ -21330,15 +21409,15 @@ var init_scripts = __esm({
21330
21409
 
21331
21410
  // src/stateWorkspace.ts
21332
21411
  import * as fs51 from "fs";
21333
- import * as path47 from "path";
21412
+ import * as path48 from "path";
21334
21413
  function tenantId(config) {
21335
21414
  const owner = config.github?.owner?.trim() || process.env.GITHUB_REPOSITORY?.split("/")[0]?.trim();
21336
21415
  const repo = config.github?.repo?.trim() || process.env.GITHUB_REPOSITORY?.split("/")[1]?.trim();
21337
21416
  return owner && repo ? `${owner}/${repo}` : null;
21338
21417
  }
21339
21418
  function writeRuntimeFile(cwd, relativePath, content) {
21340
- const target = path47.join(cwd, RUNTIME_ROOT, relativePath);
21341
- fs51.mkdirSync(path47.dirname(target), { recursive: true });
21419
+ const target = path48.join(cwd, RUNTIME_ROOT, relativePath);
21420
+ fs51.mkdirSync(path48.dirname(target), { recursive: true });
21342
21421
  fs51.writeFileSync(target, content, "utf8");
21343
21422
  }
21344
21423
  function record(value) {
@@ -21404,10 +21483,10 @@ async function hydrateStateWorkspace(config, cwd, backendOverride) {
21404
21483
  throw new Error("Kody backend access is required for runtime workspace documents");
21405
21484
  return;
21406
21485
  }
21407
- const key = `${path47.resolve(cwd)}|${tenant}`;
21486
+ const key = `${path48.resolve(cwd)}|${tenant}`;
21408
21487
  if (hydratedWorkspaces.has(key)) return;
21409
21488
  const backend = backendOverride ?? createStateBackendFromEnv();
21410
- const root = path47.join(cwd, RUNTIME_ROOT);
21489
+ const root = path48.join(cwd, RUNTIME_ROOT);
21411
21490
  fs51.rmSync(root, { recursive: true, force: true });
21412
21491
  await Promise.all([
21413
21492
  hydratePrefix(backend, tenant, cwd, "context:"),
@@ -21424,7 +21503,7 @@ var init_stateWorkspace = __esm({
21424
21503
  "src/stateWorkspace.ts"() {
21425
21504
  "use strict";
21426
21505
  init_state_backend();
21427
- RUNTIME_ROOT = path47.join(".kody-engine", "runtime");
21506
+ RUNTIME_ROOT = path48.join(".kody-engine", "runtime");
21428
21507
  hydratedWorkspaces = /* @__PURE__ */ new Set();
21429
21508
  }
21430
21509
  });
@@ -21497,7 +21576,7 @@ var init_tools = __esm({
21497
21576
  import { spawn as spawn8 } from "child_process";
21498
21577
  import * as fs52 from "fs";
21499
21578
  import * as os8 from "os";
21500
- import * as path48 from "path";
21579
+ import * as path49 from "path";
21501
21580
  function isMutatingPostflight(scriptName) {
21502
21581
  return MUTATING_POSTFLIGHTS.has(scriptName ?? "");
21503
21582
  }
@@ -21749,7 +21828,7 @@ async function runImplementation(profileName, input) {
21749
21828
  const jobWhyBlock = typeof ctx.data.jobWhy === "string" ? operatorRequestBlock(ctx.data.jobWhy) : null;
21750
21829
  const jobRefBlock = jobReferenceBlock(profileName, profile, ctx.data);
21751
21830
  const invokeAgent = async (prompt) => {
21752
- const externalPlugins = (profile.claudeCode.plugins ?? []).map((p) => path48.isAbsolute(p) ? p : path48.resolve(profile.dir, p)).filter((p) => p.length > 0);
21831
+ const externalPlugins = (profile.claudeCode.plugins ?? []).map((p) => path49.isAbsolute(p) ? p : path49.resolve(profile.dir, p)).filter((p) => p.length > 0);
21753
21832
  const syntheticPath = ctx.data.syntheticPluginPath;
21754
21833
  const pluginPaths = [...externalPlugins, ...syntheticPath ? [syntheticPath] : []];
21755
21834
  const agents = loadSubagents(profile);
@@ -22225,13 +22304,13 @@ function clearStampedLifecycleLabels(profile, ctx) {
22225
22304
  function resolveProfilePath(profileName, cwd = process.cwd()) {
22226
22305
  const found = resolveImplementation(profileName, getRuntimeProfileRootsForCwd(cwd));
22227
22306
  if (found) return found;
22228
- const here = path48.dirname(new URL(import.meta.url).pathname);
22307
+ const here = path49.dirname(new URL(import.meta.url).pathname);
22229
22308
  const candidates = [
22230
- path48.join(here, "implementations", profileName, "profile.json"),
22309
+ path49.join(here, "implementations", profileName, "profile.json"),
22231
22310
  // same-dir sibling (dev)
22232
- path48.join(here, "..", "implementations", profileName, "profile.json"),
22311
+ path49.join(here, "..", "implementations", profileName, "profile.json"),
22233
22312
  // up one (prod: dist/bin → dist/implementations)
22234
- path48.join(here, "..", "src", "implementations", profileName, "profile.json")
22313
+ path49.join(here, "..", "src", "implementations", profileName, "profile.json")
22235
22314
  // fallback
22236
22315
  ];
22237
22316
  for (const c of candidates) {
@@ -22350,7 +22429,7 @@ function resolveShellTimeoutMs(entry) {
22350
22429
  }
22351
22430
  async function runShellEntry(entry, ctx, profile) {
22352
22431
  const shellName = entry.shell;
22353
- const shellPath = path48.join(profile.dir, shellName);
22432
+ const shellPath = path49.join(profile.dir, shellName);
22354
22433
  if (!fs52.existsSync(shellPath)) {
22355
22434
  ctx.skipAgent = true;
22356
22435
  ctx.output.exitCode = 99;
@@ -22358,7 +22437,7 @@ async function runShellEntry(entry, ctx, profile) {
22358
22437
  return;
22359
22438
  }
22360
22439
  const positional = entry.with ? Object.values(entry.with).map((v) => String(v)) : [];
22361
- const outputFile = path48.join(
22440
+ const outputFile = path49.join(
22362
22441
  os8.tmpdir(),
22363
22442
  `kody-shell-output-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`
22364
22443
  );
@@ -22388,14 +22467,14 @@ async function runShellEntry(entry, ctx, profile) {
22388
22467
  let killTimer;
22389
22468
  let escalateTimer;
22390
22469
  const result = await new Promise(
22391
- (resolve19) => {
22470
+ (resolve20) => {
22392
22471
  let settled = false;
22393
22472
  const settle = (code, signal, spawnErr) => {
22394
22473
  if (settled) return;
22395
22474
  settled = true;
22396
22475
  if (killTimer) clearTimeout(killTimer);
22397
22476
  if (escalateTimer) clearTimeout(escalateTimer);
22398
- resolve19({ code, signal, spawnErr });
22477
+ resolve20({ code, signal, spawnErr });
22399
22478
  };
22400
22479
  child.on("error", (err) => settle(null, null, err));
22401
22480
  child.on("close", (code, signal) => settle(code, signal));
@@ -23283,11 +23362,11 @@ function exhaustedWorkflowTransitions(step, data, counts) {
23283
23362
  }
23284
23363
  function workflowResultConditionPaths(transitions) {
23285
23364
  return transitions.flatMap(
23286
- (transition) => Object.keys(transition.when ?? {}).filter((path53) => path53.startsWith("result."))
23365
+ (transition) => Object.keys(transition.when ?? {}).filter((path54) => path54.startsWith("result."))
23287
23366
  );
23288
23367
  }
23289
23368
  function conditionMatches(condition, context) {
23290
- return Object.entries(condition).every(([path53, expected]) => valueMatches(resolveDottedPath2(context, path53), expected));
23369
+ return Object.entries(condition).every(([path54, expected]) => valueMatches(resolveDottedPath2(context, path54), expected));
23291
23370
  }
23292
23371
  function withWorkflowBoundaryEval(capability, result) {
23293
23372
  const capabilityKind = capability.config.capabilityKind;
@@ -23724,7 +23803,7 @@ function translateOpenAISseToBrain(opts) {
23724
23803
 
23725
23804
  // src/servers/brain-serve.ts
23726
23805
  import { createServer as createServer2 } from "http";
23727
- import * as path51 from "path";
23806
+ import * as path52 from "path";
23728
23807
 
23729
23808
  // src/chat/loop.ts
23730
23809
  init_agent();
@@ -23878,9 +23957,9 @@ var CodexAppServerClient = class {
23878
23957
  await this.request("thread/resume", { threadId });
23879
23958
  }
23880
23959
  async runTurn(args) {
23881
- await new Promise((resolve19, reject) => {
23960
+ await new Promise((resolve20, reject) => {
23882
23961
  this.process.turnWaiters.set(args.threadId, {
23883
- resolve: resolve19,
23962
+ resolve: resolve20,
23884
23963
  reject,
23885
23964
  onNotification: args.onNotification,
23886
23965
  queue: Promise.resolve()
@@ -23897,8 +23976,8 @@ var CodexAppServerClient = class {
23897
23976
  }
23898
23977
  request(method, params) {
23899
23978
  const id = this.process.nextId++;
23900
- return new Promise((resolve19, reject) => {
23901
- this.process.pending.set(id, { resolve: resolve19, reject });
23979
+ return new Promise((resolve20, reject) => {
23980
+ this.process.pending.set(id, { resolve: resolve20, reject });
23902
23981
  this.process.child.stdin.write(`${JSON.stringify({ method, id, params })}
23903
23982
  `);
23904
23983
  });
@@ -24965,7 +25044,7 @@ async function hydrateDefinitionsFromEnv(cwd = process.cwd(), env = process.env)
24965
25044
  // src/kody-cli.ts
24966
25045
  import { execFileSync as execFileSync24 } from "child_process";
24967
25046
  import * as fs53 from "fs";
24968
- import * as path49 from "path";
25047
+ import * as path50 from "path";
24969
25048
 
24970
25049
  // src/app-auth.ts
24971
25050
  import { createSign } from "crypto";
@@ -25767,9 +25846,9 @@ async function resolveAuthToken(env = process.env) {
25767
25846
  return void 0;
25768
25847
  }
25769
25848
  function detectPackageManager2(cwd) {
25770
- if (fs53.existsSync(path49.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
25771
- if (fs53.existsSync(path49.join(cwd, "yarn.lock"))) return "yarn";
25772
- if (fs53.existsSync(path49.join(cwd, "bun.lockb"))) return "bun";
25849
+ if (fs53.existsSync(path50.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
25850
+ if (fs53.existsSync(path50.join(cwd, "yarn.lock"))) return "yarn";
25851
+ if (fs53.existsSync(path50.join(cwd, "bun.lockb"))) return "bun";
25773
25852
  return "npm";
25774
25853
  }
25775
25854
  function shouldChainScheduledWatch(match) {
@@ -25902,7 +25981,7 @@ async function runCi(argv) {
25902
25981
  return 0;
25903
25982
  }
25904
25983
  const args = parseCiArgs(argv);
25905
- const cwd = args.cwd ? path49.resolve(args.cwd) : process.cwd();
25984
+ const cwd = args.cwd ? path50.resolve(args.cwd) : process.cwd();
25906
25985
  try {
25907
25986
  const n = unpackAllSecrets();
25908
25987
  if (n > 0) process.stdout.write(`\u2192 kody: unpacked ${n} secret(s) from ALL_SECRETS
@@ -26386,7 +26465,7 @@ init_repoWorkspace();
26386
26465
  // src/scripts/brainTurnLog.ts
26387
26466
  init_runtimePaths();
26388
26467
  import * as fs54 from "fs";
26389
- import * as path50 from "path";
26468
+ import * as path51 from "path";
26390
26469
  import posixPath4 from "path/posix";
26391
26470
  var live = /* @__PURE__ */ new Map();
26392
26471
  function brainEventsFilePath(dir, chatId) {
@@ -26433,7 +26512,7 @@ function beginTurn(dir, chatId) {
26433
26512
  };
26434
26513
  live.set(chatId, state);
26435
26514
  const p = brainEventsFilePath(dir, chatId);
26436
- fs54.mkdirSync(path50.dirname(p), { recursive: true });
26515
+ fs54.mkdirSync(path51.dirname(p), { recursive: true });
26437
26516
  return (event) => {
26438
26517
  state.seq += 1;
26439
26518
  const rec = { seq: state.seq, turn, ts: Date.now(), event };
@@ -26571,17 +26650,17 @@ function authOk(req, expected) {
26571
26650
  return false;
26572
26651
  }
26573
26652
  function readJsonBody(req) {
26574
- return new Promise((resolve19, reject) => {
26653
+ return new Promise((resolve20, reject) => {
26575
26654
  const chunks = [];
26576
26655
  req.on("data", (c) => chunks.push(c));
26577
26656
  req.on("end", () => {
26578
26657
  const raw = Buffer.concat(chunks).toString("utf-8");
26579
26658
  if (!raw.trim()) {
26580
- resolve19({});
26659
+ resolve20({});
26581
26660
  return;
26582
26661
  }
26583
26662
  try {
26584
- resolve19(JSON.parse(raw));
26663
+ resolve20(JSON.parse(raw));
26585
26664
  } catch (err) {
26586
26665
  reject(err instanceof Error ? err : new Error(String(err)));
26587
26666
  }
@@ -26873,7 +26952,7 @@ function buildServer(opts) {
26873
26952
  const runTurn = opts.runTurn ?? runChatTurn;
26874
26953
  const createStore = opts.createStore ?? createSessionStore;
26875
26954
  const cloneRepo = opts.cloneRepo ?? defaultCloneRepo;
26876
- const reposRoot = opts.reposRoot ?? path51.join(path51.dirname(path51.resolve(opts.cwd)), "repos");
26955
+ const reposRoot = opts.reposRoot ?? path52.join(path52.dirname(path52.resolve(opts.cwd)), "repos");
26877
26956
  return createServer2(async (req, res) => {
26878
26957
  if (!req.method || !req.url) {
26879
26958
  sendJson(res, 400, { error: "bad request" });
@@ -26954,11 +27033,11 @@ async function brainServe(opts) {
26954
27033
  litellmUrl,
26955
27034
  driver
26956
27035
  });
26957
- await new Promise((resolve19) => {
27036
+ await new Promise((resolve20) => {
26958
27037
  server.listen(port, "0.0.0.0", () => {
26959
27038
  process.stdout.write(`[brain-serve] listening on 0.0.0.0:${port} (cwd=${opts.cwd})
26960
27039
  `);
26961
- resolve19();
27040
+ resolve20();
26962
27041
  });
26963
27042
  });
26964
27043
  const shutdown = (signal) => {
@@ -27213,14 +27292,14 @@ async function startBrainProxy(opts) {
27213
27292
  const { httpServer, handler } = buildBrainProxy(opts);
27214
27293
  const port = opts.port ?? 0;
27215
27294
  const host = opts.host ?? "127.0.0.1";
27216
- await new Promise((resolve19) => httpServer.listen(port, host, () => resolve19()));
27295
+ await new Promise((resolve20) => httpServer.listen(port, host, () => resolve20()));
27217
27296
  const addr = httpServer.address();
27218
27297
  return {
27219
27298
  httpServer,
27220
27299
  port: addr.port,
27221
27300
  url: `http://${host}:${addr.port}`,
27222
- stop: () => new Promise((resolve19) => {
27223
- httpServer.close(() => resolve19());
27301
+ stop: () => new Promise((resolve20) => {
27302
+ httpServer.close(() => resolve20());
27224
27303
  }),
27225
27304
  handler
27226
27305
  };
@@ -27370,23 +27449,23 @@ function buildMcpHttpServer(opts) {
27370
27449
  httpServer,
27371
27450
  routes,
27372
27451
  port,
27373
- stop: () => new Promise((resolve19) => {
27452
+ stop: () => new Promise((resolve20) => {
27374
27453
  let pending = transports.size;
27375
27454
  if (pending === 0) {
27376
- httpServer.close(() => resolve19());
27455
+ httpServer.close(() => resolve20());
27377
27456
  return;
27378
27457
  }
27379
27458
  for (const transport of transports.values()) {
27380
27459
  void transport.close().finally(() => {
27381
27460
  pending--;
27382
- if (pending === 0) httpServer.close(() => resolve19());
27461
+ if (pending === 0) httpServer.close(() => resolve20());
27383
27462
  });
27384
27463
  }
27385
27464
  })
27386
27465
  };
27387
27466
  }
27388
27467
  function listenMcpHttpServer(server, host = "127.0.0.1") {
27389
- return new Promise((resolve19, reject) => {
27468
+ return new Promise((resolve20, reject) => {
27390
27469
  server.httpServer.once("error", reject);
27391
27470
  server.httpServer.listen(server.port, host, () => {
27392
27471
  server.httpServer.off("error", reject);
@@ -27394,7 +27473,7 @@ function listenMcpHttpServer(server, host = "127.0.0.1") {
27394
27473
  if (addr && typeof addr === "object") {
27395
27474
  server.port = addr.port;
27396
27475
  }
27397
- resolve19();
27476
+ resolve20();
27398
27477
  });
27399
27478
  });
27400
27479
  }
@@ -27477,7 +27556,7 @@ async function loadConfigSafe() {
27477
27556
  }
27478
27557
 
27479
27558
  // src/chat-cli.ts
27480
- import * as path52 from "path";
27559
+ import * as path53 from "path";
27481
27560
 
27482
27561
  // src/chat/inbox.ts
27483
27562
  import { execFileSync as execFileSync25 } from "child_process";
@@ -27544,7 +27623,7 @@ async function waitForNextUserMessage(opts) {
27544
27623
  }
27545
27624
  }
27546
27625
  function sleep3(ms) {
27547
- return new Promise((resolve19) => setTimeout(resolve19, ms));
27626
+ return new Promise((resolve20) => setTimeout(resolve20, ms));
27548
27627
  }
27549
27628
  function currentBranch(cwd) {
27550
27629
  try {
@@ -27768,7 +27847,7 @@ async function runChat(argv) {
27768
27847
  ${CHAT_HELP}`);
27769
27848
  return 64;
27770
27849
  }
27771
- const cwd = args.cwd ? path52.resolve(args.cwd) : process.cwd();
27850
+ const cwd = args.cwd ? path53.resolve(args.cwd) : process.cwd();
27772
27851
  const sessionId = args.sessionId;
27773
27852
  const runRequest = readRunRequestFromEnv();
27774
27853
  if (runRequest && "request" in runRequest) {
@@ -27964,8 +28043,8 @@ var FlyClient = class {
27964
28043
  get fetch() {
27965
28044
  return this.opts.fetchImpl ?? fetch;
27966
28045
  }
27967
- async call(path53, init = {}) {
27968
- const res = await this.fetch(`${FLY_API_BASE}${path53}`, {
28046
+ async call(path54, init = {}) {
28047
+ const res = await this.fetch(`${FLY_API_BASE}${path54}`, {
27969
28048
  method: init.method ?? "GET",
27970
28049
  headers: {
27971
28050
  Authorization: `Bearer ${this.opts.token}`,
@@ -27976,7 +28055,7 @@ var FlyClient = class {
27976
28055
  if (res.status === 404 && init.allow404) return null;
27977
28056
  if (!res.ok) {
27978
28057
  const text2 = await res.text().catch(() => "");
27979
- throw new Error(`Fly API ${res.status} on ${path53}: ${text2.slice(0, 200) || res.statusText}`);
28058
+ throw new Error(`Fly API ${res.status} on ${path54}: ${text2.slice(0, 200) || res.statusText}`);
27980
28059
  }
27981
28060
  if (res.status === 204) return null;
27982
28061
  const raw = await res.text();
@@ -28489,14 +28568,14 @@ function sendJson2(res, status, body) {
28489
28568
  res.end(JSON.stringify(body));
28490
28569
  }
28491
28570
  function readJsonBody2(req) {
28492
- return new Promise((resolve19, reject) => {
28571
+ return new Promise((resolve20, reject) => {
28493
28572
  const chunks = [];
28494
28573
  req.on("data", (c) => chunks.push(c));
28495
28574
  req.on("end", () => {
28496
28575
  const raw = Buffer.concat(chunks).toString("utf-8");
28497
- if (!raw.trim()) return resolve19({});
28576
+ if (!raw.trim()) return resolve20({});
28498
28577
  try {
28499
- resolve19(JSON.parse(raw));
28578
+ resolve20(JSON.parse(raw));
28500
28579
  } catch (err) {
28501
28580
  reject(err instanceof Error ? err : new Error(String(err)));
28502
28581
  }
@@ -28650,10 +28729,10 @@ async function poolServe() {
28650
28729
  }
28651
28730
  });
28652
28731
  const apiHost = process.env.POOL_API_HOST ?? "::";
28653
- await new Promise((resolve19) => {
28732
+ await new Promise((resolve20) => {
28654
28733
  server.listen(apiPort, apiHost, () => {
28655
28734
  log(`listening on ${apiHost}:${apiPort} (min=${min}, app=${app}, region=${region})`);
28656
- resolve19();
28735
+ resolve20();
28657
28736
  });
28658
28737
  });
28659
28738
  if (loopTickEnabled) void runLoopTick();
@@ -28693,17 +28772,17 @@ function authOk2(req, expected) {
28693
28772
  return false;
28694
28773
  }
28695
28774
  function readJsonBody3(req) {
28696
- return new Promise((resolve19, reject) => {
28775
+ return new Promise((resolve20, reject) => {
28697
28776
  const chunks = [];
28698
28777
  req.on("data", (c) => chunks.push(c));
28699
28778
  req.on("end", () => {
28700
28779
  const raw = Buffer.concat(chunks).toString("utf-8");
28701
28780
  if (!raw.trim()) {
28702
- resolve19({});
28781
+ resolve20({});
28703
28782
  return;
28704
28783
  }
28705
28784
  try {
28706
- resolve19(JSON.parse(raw));
28785
+ resolve20(JSON.parse(raw));
28707
28786
  } catch (err) {
28708
28787
  reject(err instanceof Error ? err : new Error(String(err)));
28709
28788
  }
@@ -28778,13 +28857,13 @@ async function defaultRunJob(job) {
28778
28857
  ...interactive && job.idleExitMs ? { KODY_IDLE_EXIT_MS: String(job.idleExitMs) } : {},
28779
28858
  ...interactive && job.hardCapMs ? { KODY_HARD_CAP_MS: String(job.hardCapMs) } : {}
28780
28859
  };
28781
- const run = (cmd, args, cwd) => new Promise((resolve19) => {
28860
+ const run = (cmd, args, cwd) => new Promise((resolve20) => {
28782
28861
  const child = spawn9(cmd, args, { stdio: "inherit", env: childEnv, cwd });
28783
- child.on("exit", (code) => resolve19(code ?? 0));
28862
+ child.on("exit", (code) => resolve20(code ?? 0));
28784
28863
  child.on("error", (err) => {
28785
28864
  process.stderr.write(`[runner-serve] ${cmd} failed: ${err.message}
28786
28865
  `);
28787
- resolve19(1);
28866
+ resolve20(1);
28788
28867
  });
28789
28868
  });
28790
28869
  process.stdout.write(`[runner-serve] job ${job.jobId}: cloning ${job.repo}@${branch}
@@ -28860,11 +28939,11 @@ async function runnerServe() {
28860
28939
  const port = Number(process.env.PORT ?? DEFAULT_PORT2);
28861
28940
  const server = buildServer2({ apiKey });
28862
28941
  const host = process.env.RUNNER_HOST ?? "::";
28863
- await new Promise((resolve19) => {
28942
+ await new Promise((resolve20) => {
28864
28943
  server.listen(port, host, () => {
28865
28944
  process.stdout.write(`[runner-serve] listening on ${host}:${port} (idle, awaiting job)
28866
28945
  `);
28867
- resolve19();
28946
+ resolve20();
28868
28947
  });
28869
28948
  });
28870
28949
  const shutdown = (signal) => {
@@ -28933,14 +29012,14 @@ async function serve(opts) {
28933
29012
  `);
28934
29013
  const args = ["--dangerously-skip-permissions", "--model", model.model];
28935
29014
  const child = spawn10("claude", args, { stdio: "inherit", env: editorEnv, cwd: opts.cwd });
28936
- const exitCode = await new Promise((resolve19) => {
28937
- child.on("exit", (code) => resolve19(code ?? 0));
29015
+ const exitCode = await new Promise((resolve20) => {
29016
+ child.on("exit", (code) => resolve20(code ?? 0));
28938
29017
  child.on("error", (err) => {
28939
29018
  process.stderr.write(`[kody serve] failed to launch Claude Code: ${err.message}
28940
29019
  `);
28941
29020
  process.stderr.write(` Install: https://docs.anthropic.com/claude/docs/claude-code
28942
29021
  `);
28943
- resolve19(1);
29022
+ resolve20(1);
28944
29023
  });
28945
29024
  });
28946
29025
  killProxy();
File without changes
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@kody-ade/kody-engine",
3
- "version": "0.4.550",
4
- "description": "kody \u2014 autonomous development engine. Single-session Claude Code agent behind a generic executor + declarative implementation profiles.",
3
+ "version": "0.4.552",
4
+ "description": "kody autonomous development engine. Single-session Claude Code agent behind a generic executor + declarative implementation profiles.",
5
5
  "license": "MIT",
6
6
  "type": "module",
7
7
  "bin": {
@@ -12,29 +12,6 @@
12
12
  "templates",
13
13
  "kody.config.schema.json"
14
14
  ],
15
- "scripts": {
16
- "kody:run": "tsx bin/kody.ts",
17
- "serve": "tsx bin/kody.ts serve",
18
- "serve:vscode": "tsx bin/kody.ts serve vscode",
19
- "serve:claude": "tsx bin/kody.ts serve claude",
20
- "clean:dist": "node scripts/clean-dist.cjs",
21
- "build": "pnpm clean:dist && tsup && node scripts/copy-assets.cjs",
22
- "check:modularity": "tsx scripts/check-script-modularity.ts",
23
- "pretest": "pnpm check:modularity",
24
- "test": "vitest run tests/unit tests/int --coverage",
25
- "posttest": "tsx scripts/check-coverage-floor.ts",
26
- "test:smoke": "vitest run tests/smoke --no-coverage",
27
- "test:e2e": "vitest run tests/e2e --no-coverage",
28
- "test:runtime-services": "node --test \"tests/runtime-services/*.test.mjs\"",
29
- "test:all": "vitest run tests --no-coverage",
30
- "typecheck": "tsc --noEmit",
31
- "lint": "biome check",
32
- "lint:fix": "biome check --write",
33
- "format": "biome format --write",
34
- "verify:package": "node scripts/verify-package-tarball.cjs",
35
- "brain:publish": "docker buildx build --platform linux/amd64 -f runner/Dockerfile.brain --build-arg KODY_ENGINE_REF=$(git rev-parse HEAD) -t ghcr.io/${KODY_BRAIN_GHCR_OWNER:-aharonyaircohen}/kody-brain:latest --push runner",
36
- "prepublishOnly": "pnpm typecheck && pnpm test:runtime-services && pnpm build && pnpm verify:package"
37
- },
38
15
  "dependencies": {
39
16
  "@actions/cache": "^6.0.0",
40
17
  "@anthropic-ai/claude-agent-sdk": "0.2.119",
@@ -61,5 +38,27 @@
61
38
  "url": "git+https://github.com/aharonyaircohen/kody-engine.git"
62
39
  },
63
40
  "homepage": "https://github.com/aharonyaircohen/kody-engine",
64
- "bugs": "https://github.com/aharonyaircohen/kody-engine/issues"
65
- }
41
+ "bugs": "https://github.com/aharonyaircohen/kody-engine/issues",
42
+ "scripts": {
43
+ "kody:run": "tsx bin/kody.ts",
44
+ "serve": "tsx bin/kody.ts serve",
45
+ "serve:vscode": "tsx bin/kody.ts serve vscode",
46
+ "serve:claude": "tsx bin/kody.ts serve claude",
47
+ "clean:dist": "node scripts/clean-dist.cjs",
48
+ "build": "pnpm clean:dist && tsup && node scripts/copy-assets.cjs",
49
+ "check:modularity": "tsx scripts/check-script-modularity.ts",
50
+ "pretest": "pnpm check:modularity",
51
+ "test": "vitest run tests/unit tests/int --coverage",
52
+ "posttest": "tsx scripts/check-coverage-floor.ts",
53
+ "test:smoke": "vitest run tests/smoke --no-coverage",
54
+ "test:e2e": "vitest run tests/e2e --no-coverage",
55
+ "test:runtime-services": "node --test \"tests/runtime-services/*.test.mjs\"",
56
+ "test:all": "vitest run tests --no-coverage",
57
+ "typecheck": "tsc --noEmit",
58
+ "lint": "biome check",
59
+ "lint:fix": "biome check --write",
60
+ "format": "biome format --write",
61
+ "verify:package": "node scripts/verify-package-tarball.cjs",
62
+ "brain:publish": "docker buildx build --platform linux/amd64 -f runner/Dockerfile.brain --build-arg KODY_ENGINE_REF=$(git rev-parse HEAD) -t ghcr.io/${KODY_BRAIN_GHCR_OWNER:-aharonyaircohen}/kody-brain:latest --push runner"
63
+ }
64
+ }