@kody-ade/kody-engine 0.4.549 → 0.4.551

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.549",
18
+ version: "0.4.551",
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) {
@@ -17930,8 +17940,8 @@ var prepareCapabilityDelivery;
17930
17940
  var init_prepareCapabilityDelivery = __esm({
17931
17941
  "src/scripts/prepareCapabilityDelivery.ts"() {
17932
17942
  "use strict";
17933
- init_capabilityDelivery();
17934
17943
  init_branch();
17944
+ init_capabilityDelivery();
17935
17945
  init_runFlow();
17936
17946
  prepareCapabilityDelivery = async (ctx, profile) => {
17937
17947
  const target = capabilityDeliveryTarget(ctx.data.capabilityInput);
@@ -17951,16 +17961,66 @@ 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.maxTurns = Math.min(profile.claudeCode.maxTurns ?? 50, 50);
18018
+ }
17959
18019
  if (!profile.claudeCode.tools.includes("mcp__playwright")) {
17960
18020
  profile.claudeCode.tools = [...profile.claudeCode.tools, "mcp__playwright"];
17961
18021
  }
17962
18022
  if (!profile.claudeCode.mcpServers.some(({ name }) => name === PLAYWRIGHT_SERVER.name)) {
17963
- profile.claudeCode.mcpServers = [...profile.claudeCode.mcpServers, PLAYWRIGHT_SERVER];
18023
+ profile.claudeCode.mcpServers = [...profile.claudeCode.mcpServers, server];
17964
18024
  }
17965
18025
  }
17966
18026
  function appendPrompt(ctx, section) {
@@ -17972,6 +18032,7 @@ var init_prepareSimpleCapabilityRuntime = __esm({
17972
18032
  "src/scripts/prepareSimpleCapabilityRuntime.ts"() {
17973
18033
  "use strict";
17974
18034
  init_loadQaContext();
18035
+ init_runtimeSecrets();
17975
18036
  PLAYWRIGHT_SERVER = {
17976
18037
  name: "playwright",
17977
18038
  command: "npx",
@@ -17980,19 +18041,36 @@ var init_prepareSimpleCapabilityRuntime = __esm({
17980
18041
  prepareSimpleCapabilityRuntime = async (ctx, profile) => {
17981
18042
  const requirements = requirementsFrom(ctx);
17982
18043
  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
- );
18044
+ configureBrowser(ctx, profile, requirements);
18045
+ if (requirements.qaCredentials) {
18046
+ await loadQaContext(ctx, profile);
18047
+ appendPrompt(
18048
+ ctx,
18049
+ [
18050
+ "## QA authentication",
18051
+ "",
18052
+ String(ctx.data.qaAuthBlock ?? ""),
18053
+ "",
18054
+ "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."
18055
+ ].join("\n")
18056
+ );
18057
+ }
18058
+ if (requirements.githubTestToken) {
18059
+ const token = await resolveRuntimeSecret("E2E_GITHUB_TOKEN", ctx);
18060
+ appendPrompt(
18061
+ ctx,
18062
+ token.value ? [
18063
+ "## Protected GitHub test login",
18064
+ "",
18065
+ `A protected GitHub test token is available: \`${token.value}\``,
18066
+ "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."
18067
+ ].join("\n") : [
18068
+ "## Protected GitHub test login",
18069
+ "",
18070
+ "E2E_GITHUB_TOKEN is not configured. If this Quality Scenario requires GitHub token authentication, return a blocked result."
18071
+ ].join("\n")
18072
+ );
18073
+ }
17996
18074
  };
17997
18075
  }
17998
18076
  });
@@ -18279,9 +18357,9 @@ function latestResult(raw, agentResult) {
18279
18357
  function recordField4(value) {
18280
18358
  return value && typeof value === "object" && !Array.isArray(value) ? value : null;
18281
18359
  }
18282
- function resolveDotted(root, path53) {
18283
- if (!path53) return void 0;
18284
- return path53.split(".").reduce((value, key) => recordField4(value)?.[key], root);
18360
+ function resolveDotted(root, path54) {
18361
+ if (!path54) return void 0;
18362
+ return path54.split(".").reduce((value, key) => recordField4(value)?.[key], root);
18285
18363
  }
18286
18364
  function stringValue5(value) {
18287
18365
  return typeof value === "string" && value.trim() ? value.trim() : null;
@@ -19123,7 +19201,7 @@ var init_previewBuildHelpers = __esm({
19123
19201
  // src/scripts/previewBuildRun.ts
19124
19202
  import { spawn as spawn5 } from "child_process";
19125
19203
  async function runCmd(cmd, args, opts = {}) {
19126
- await new Promise((resolve19, reject) => {
19204
+ await new Promise((resolve20, reject) => {
19127
19205
  const child = spawn5(cmd, args, {
19128
19206
  cwd: opts.cwd,
19129
19207
  env: { ...process.env, ...opts.env ?? {} },
@@ -19135,7 +19213,7 @@ async function runCmd(cmd, args, opts = {}) {
19135
19213
  }
19136
19214
  child.on("error", reject);
19137
19215
  child.on("close", (code) => {
19138
- if (code === 0) resolve19();
19216
+ if (code === 0) resolve20();
19139
19217
  else reject(new Error(`${cmd} ${args.join(" ")} exited ${code}`));
19140
19218
  });
19141
19219
  });
@@ -19207,12 +19285,12 @@ fi
19207
19285
 
19208
19286
  // src/scripts/runPreviewBuild.ts
19209
19287
  import { copyFile, writeFile } from "fs/promises";
19210
- import * as path43 from "path";
19288
+ import * as path44 from "path";
19211
19289
  import { fileURLToPath as fileURLToPath2 } from "url";
19212
19290
  function bundledDockerfilePath(mode) {
19213
- const here = path43.dirname(fileURLToPath2(import.meta.url));
19291
+ const here = path44.dirname(fileURLToPath2(import.meta.url));
19214
19292
  const file = mode === "dev" ? "default-Dockerfile.preview.dev" : "default-Dockerfile.preview.prod";
19215
- return path43.join(here, "preview-build-templates", file);
19293
+ return path44.join(here, "preview-build-templates", file);
19216
19294
  }
19217
19295
  function required(name) {
19218
19296
  const v = (process.env[name] ?? "").trim();
@@ -19447,10 +19525,10 @@ var init_runPreviewBuild = __esm({
19447
19525
  console.log(`[preview-build] vault: ${Object.keys(buildEnv).length} secrets, mode=${buildMode}`);
19448
19526
  if (Object.keys(buildEnv).length > 0) {
19449
19527
  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")}
19528
+ await writeFile(path44.join(ctx.cwd, ".env.production.local"), `${lines.join("\n")}
19451
19529
  `, "utf8");
19452
19530
  }
19453
- const consumerDockerfile = path43.join(ctx.cwd, "Dockerfile.preview");
19531
+ const consumerDockerfile = path44.join(ctx.cwd, "Dockerfile.preview");
19454
19532
  const { stat } = await import("fs/promises");
19455
19533
  let hasConsumerDockerfile = false;
19456
19534
  try {
@@ -19635,7 +19713,7 @@ var init_tickShellRunner = __esm({
19635
19713
 
19636
19714
  // src/scripts/runScheduledImplementationTick.ts
19637
19715
  import * as fs47 from "fs";
19638
- import * as path44 from "path";
19716
+ import * as path45 from "path";
19639
19717
  var runScheduledImplementationTick;
19640
19718
  var init_runScheduledImplementationTick = __esm({
19641
19719
  "src/scripts/runScheduledImplementationTick.ts"() {
@@ -19656,13 +19734,13 @@ var init_runScheduledImplementationTick = __esm({
19656
19734
  ctx.output.reason = `runScheduledImplementationTick: args.slug or ctx.args.${slugArg} must be non-empty capability slug`;
19657
19735
  return;
19658
19736
  }
19659
- const capability = resolveCapabilityFolder(slug, path44.resolve(ctx.cwd, jobsDir));
19737
+ const capability = resolveCapabilityFolder(slug, path45.resolve(ctx.cwd, jobsDir));
19660
19738
  if (!capability) {
19661
19739
  ctx.output.exitCode = 99;
19662
19740
  ctx.output.reason = `runScheduledImplementationTick: capability folder not found or incomplete: ${slug} (searched ${jobsDir} and company store)`;
19663
19741
  return;
19664
19742
  }
19665
- const shellPath = path44.join(profile.dir, shell);
19743
+ const shellPath = path45.join(profile.dir, shell);
19666
19744
  if (!fs47.existsSync(shellPath)) {
19667
19745
  ctx.output.exitCode = 99;
19668
19746
  ctx.output.reason = `runScheduledImplementationTick: shell not found: ${shell} (looked in ${profile.dir})`;
@@ -19781,7 +19859,7 @@ var init_runSimpleCapabilityScript = __esm({
19781
19859
 
19782
19860
  // src/scripts/runTickScript.ts
19783
19861
  import * as fs49 from "fs";
19784
- import * as path45 from "path";
19862
+ import * as path46 from "path";
19785
19863
  var runTickScript;
19786
19864
  var init_runTickScript = __esm({
19787
19865
  "src/scripts/runTickScript.ts"() {
@@ -19801,10 +19879,10 @@ var init_runTickScript = __esm({
19801
19879
  ctx.output.reason = `runTickScript: ctx.args.${slugArg} must be a non-empty slug`;
19802
19880
  return;
19803
19881
  }
19804
- const capability = readCapabilityFolder(path45.resolve(ctx.cwd, jobsDir), slug);
19882
+ const capability = readCapabilityFolder(path46.resolve(ctx.cwd, jobsDir), slug);
19805
19883
  if (!capability) {
19806
19884
  ctx.output.exitCode = 99;
19807
- ctx.output.reason = `runTickScript: capability folder not found or incomplete: ${path45.resolve(ctx.cwd, jobsDir, slug)}`;
19885
+ ctx.output.reason = `runTickScript: capability folder not found or incomplete: ${path46.resolve(ctx.cwd, jobsDir, slug)}`;
19808
19886
  return;
19809
19887
  }
19810
19888
  const tickScript = capability.config.tickScript;
@@ -19813,7 +19891,7 @@ var init_runTickScript = __esm({
19813
19891
  ctx.output.reason = `runTickScript: capability ${slug} has no \`tickScript\` in profile.json \u2014 route via capability-tick instead`;
19814
19892
  return;
19815
19893
  }
19816
- const scriptPath = path45.isAbsolute(tickScript) ? tickScript : path45.join(ctx.cwd, tickScript);
19894
+ const scriptPath = path46.isAbsolute(tickScript) ? tickScript : path46.join(ctx.cwd, tickScript);
19817
19895
  if (!fs49.existsSync(scriptPath)) {
19818
19896
  ctx.output.exitCode = 99;
19819
19897
  ctx.output.reason = `runTickScript: tickScript not found: ${scriptPath}`;
@@ -20096,7 +20174,7 @@ var init_syncFlow = __esm({
20096
20174
  });
20097
20175
 
20098
20176
  // src/scripts/validateAgencyModelProposal.ts
20099
- import * as path46 from "path";
20177
+ import * as path47 from "path";
20100
20178
  function validateModelBundle(bundle, expectedKind, options = {}) {
20101
20179
  const failures = [];
20102
20180
  validateOneModel(bundle.model, bundle.files, "model", true, failures, expectedKind, options);
@@ -20414,7 +20492,7 @@ var init_validateAgencyModelProposal = __esm({
20414
20492
  const bundle = parseAgencyModelProposal(raw);
20415
20493
  const expectedKind = readExpectedModelKind(args);
20416
20494
  const failures = validateModelBundle(bundle, expectedKind, {
20417
- capabilityRoot: path46.join(ctx.cwd, ".kody", "capabilities")
20495
+ capabilityRoot: path47.join(ctx.cwd, ".kody", "capabilities")
20418
20496
  });
20419
20497
  if (failures.length > 0) {
20420
20498
  throw new Error(`validateAgencyModelProposal: ${failures.join("; ")}`);
@@ -20477,7 +20555,7 @@ function stripAnsi2(s) {
20477
20555
  return s.replace(ANSI_RE2, "");
20478
20556
  }
20479
20557
  function runCommand2(command, cwd) {
20480
- return new Promise((resolve19) => {
20558
+ return new Promise((resolve20) => {
20481
20559
  const child = spawn6(command, {
20482
20560
  cwd,
20483
20561
  shell: true,
@@ -20504,11 +20582,11 @@ function runCommand2(command, cwd) {
20504
20582
  }, TEST_TIMEOUT_MS);
20505
20583
  child.on("exit", (code) => {
20506
20584
  clearTimeout(timer);
20507
- resolve19({ exitCode: code ?? -1, output: Buffer.concat(buffers).toString("utf-8") });
20585
+ resolve20({ exitCode: code ?? -1, output: Buffer.concat(buffers).toString("utf-8") });
20508
20586
  });
20509
20587
  child.on("error", (err) => {
20510
20588
  clearTimeout(timer);
20511
- resolve19({ exitCode: -1, output: err.message });
20589
+ resolve20({ exitCode: -1, output: err.message });
20512
20590
  });
20513
20591
  });
20514
20592
  }
@@ -20914,21 +20992,21 @@ function lineStream(stream) {
20914
20992
  tryDeliver();
20915
20993
  });
20916
20994
  return {
20917
- next: (timeoutMs) => new Promise((resolve19) => {
20995
+ next: (timeoutMs) => new Promise((resolve20) => {
20918
20996
  if (queue.length > 0) {
20919
- resolve19(queue.shift());
20997
+ resolve20(queue.shift());
20920
20998
  return;
20921
20999
  }
20922
21000
  if (ended) {
20923
- resolve19(null);
21001
+ resolve20(null);
20924
21002
  return;
20925
21003
  }
20926
- waiter = resolve19;
21004
+ waiter = resolve20;
20927
21005
  const t = setTimeout(
20928
21006
  () => {
20929
- if (waiter === resolve19) {
21007
+ if (waiter === resolve20) {
20930
21008
  waiter = null;
20931
- resolve19(null);
21009
+ resolve20(null);
20932
21010
  }
20933
21011
  },
20934
21012
  Math.max(0, timeoutMs)
@@ -21330,15 +21408,15 @@ var init_scripts = __esm({
21330
21408
 
21331
21409
  // src/stateWorkspace.ts
21332
21410
  import * as fs51 from "fs";
21333
- import * as path47 from "path";
21411
+ import * as path48 from "path";
21334
21412
  function tenantId(config) {
21335
21413
  const owner = config.github?.owner?.trim() || process.env.GITHUB_REPOSITORY?.split("/")[0]?.trim();
21336
21414
  const repo = config.github?.repo?.trim() || process.env.GITHUB_REPOSITORY?.split("/")[1]?.trim();
21337
21415
  return owner && repo ? `${owner}/${repo}` : null;
21338
21416
  }
21339
21417
  function writeRuntimeFile(cwd, relativePath, content) {
21340
- const target = path47.join(cwd, RUNTIME_ROOT, relativePath);
21341
- fs51.mkdirSync(path47.dirname(target), { recursive: true });
21418
+ const target = path48.join(cwd, RUNTIME_ROOT, relativePath);
21419
+ fs51.mkdirSync(path48.dirname(target), { recursive: true });
21342
21420
  fs51.writeFileSync(target, content, "utf8");
21343
21421
  }
21344
21422
  function record(value) {
@@ -21404,10 +21482,10 @@ async function hydrateStateWorkspace(config, cwd, backendOverride) {
21404
21482
  throw new Error("Kody backend access is required for runtime workspace documents");
21405
21483
  return;
21406
21484
  }
21407
- const key = `${path47.resolve(cwd)}|${tenant}`;
21485
+ const key = `${path48.resolve(cwd)}|${tenant}`;
21408
21486
  if (hydratedWorkspaces.has(key)) return;
21409
21487
  const backend = backendOverride ?? createStateBackendFromEnv();
21410
- const root = path47.join(cwd, RUNTIME_ROOT);
21488
+ const root = path48.join(cwd, RUNTIME_ROOT);
21411
21489
  fs51.rmSync(root, { recursive: true, force: true });
21412
21490
  await Promise.all([
21413
21491
  hydratePrefix(backend, tenant, cwd, "context:"),
@@ -21424,7 +21502,7 @@ var init_stateWorkspace = __esm({
21424
21502
  "src/stateWorkspace.ts"() {
21425
21503
  "use strict";
21426
21504
  init_state_backend();
21427
- RUNTIME_ROOT = path47.join(".kody-engine", "runtime");
21505
+ RUNTIME_ROOT = path48.join(".kody-engine", "runtime");
21428
21506
  hydratedWorkspaces = /* @__PURE__ */ new Set();
21429
21507
  }
21430
21508
  });
@@ -21497,7 +21575,7 @@ var init_tools = __esm({
21497
21575
  import { spawn as spawn8 } from "child_process";
21498
21576
  import * as fs52 from "fs";
21499
21577
  import * as os8 from "os";
21500
- import * as path48 from "path";
21578
+ import * as path49 from "path";
21501
21579
  function isMutatingPostflight(scriptName) {
21502
21580
  return MUTATING_POSTFLIGHTS.has(scriptName ?? "");
21503
21581
  }
@@ -21749,7 +21827,7 @@ async function runImplementation(profileName, input) {
21749
21827
  const jobWhyBlock = typeof ctx.data.jobWhy === "string" ? operatorRequestBlock(ctx.data.jobWhy) : null;
21750
21828
  const jobRefBlock = jobReferenceBlock(profileName, profile, ctx.data);
21751
21829
  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);
21830
+ const externalPlugins = (profile.claudeCode.plugins ?? []).map((p) => path49.isAbsolute(p) ? p : path49.resolve(profile.dir, p)).filter((p) => p.length > 0);
21753
21831
  const syntheticPath = ctx.data.syntheticPluginPath;
21754
21832
  const pluginPaths = [...externalPlugins, ...syntheticPath ? [syntheticPath] : []];
21755
21833
  const agents = loadSubagents(profile);
@@ -22225,13 +22303,13 @@ function clearStampedLifecycleLabels(profile, ctx) {
22225
22303
  function resolveProfilePath(profileName, cwd = process.cwd()) {
22226
22304
  const found = resolveImplementation(profileName, getRuntimeProfileRootsForCwd(cwd));
22227
22305
  if (found) return found;
22228
- const here = path48.dirname(new URL(import.meta.url).pathname);
22306
+ const here = path49.dirname(new URL(import.meta.url).pathname);
22229
22307
  const candidates = [
22230
- path48.join(here, "implementations", profileName, "profile.json"),
22308
+ path49.join(here, "implementations", profileName, "profile.json"),
22231
22309
  // same-dir sibling (dev)
22232
- path48.join(here, "..", "implementations", profileName, "profile.json"),
22310
+ path49.join(here, "..", "implementations", profileName, "profile.json"),
22233
22311
  // up one (prod: dist/bin → dist/implementations)
22234
- path48.join(here, "..", "src", "implementations", profileName, "profile.json")
22312
+ path49.join(here, "..", "src", "implementations", profileName, "profile.json")
22235
22313
  // fallback
22236
22314
  ];
22237
22315
  for (const c of candidates) {
@@ -22350,7 +22428,7 @@ function resolveShellTimeoutMs(entry) {
22350
22428
  }
22351
22429
  async function runShellEntry(entry, ctx, profile) {
22352
22430
  const shellName = entry.shell;
22353
- const shellPath = path48.join(profile.dir, shellName);
22431
+ const shellPath = path49.join(profile.dir, shellName);
22354
22432
  if (!fs52.existsSync(shellPath)) {
22355
22433
  ctx.skipAgent = true;
22356
22434
  ctx.output.exitCode = 99;
@@ -22358,7 +22436,7 @@ async function runShellEntry(entry, ctx, profile) {
22358
22436
  return;
22359
22437
  }
22360
22438
  const positional = entry.with ? Object.values(entry.with).map((v) => String(v)) : [];
22361
- const outputFile = path48.join(
22439
+ const outputFile = path49.join(
22362
22440
  os8.tmpdir(),
22363
22441
  `kody-shell-output-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`
22364
22442
  );
@@ -22388,14 +22466,14 @@ async function runShellEntry(entry, ctx, profile) {
22388
22466
  let killTimer;
22389
22467
  let escalateTimer;
22390
22468
  const result = await new Promise(
22391
- (resolve19) => {
22469
+ (resolve20) => {
22392
22470
  let settled = false;
22393
22471
  const settle = (code, signal, spawnErr) => {
22394
22472
  if (settled) return;
22395
22473
  settled = true;
22396
22474
  if (killTimer) clearTimeout(killTimer);
22397
22475
  if (escalateTimer) clearTimeout(escalateTimer);
22398
- resolve19({ code, signal, spawnErr });
22476
+ resolve20({ code, signal, spawnErr });
22399
22477
  };
22400
22478
  child.on("error", (err) => settle(null, null, err));
22401
22479
  child.on("close", (code, signal) => settle(code, signal));
@@ -23283,11 +23361,11 @@ function exhaustedWorkflowTransitions(step, data, counts) {
23283
23361
  }
23284
23362
  function workflowResultConditionPaths(transitions) {
23285
23363
  return transitions.flatMap(
23286
- (transition) => Object.keys(transition.when ?? {}).filter((path53) => path53.startsWith("result."))
23364
+ (transition) => Object.keys(transition.when ?? {}).filter((path54) => path54.startsWith("result."))
23287
23365
  );
23288
23366
  }
23289
23367
  function conditionMatches(condition, context) {
23290
- return Object.entries(condition).every(([path53, expected]) => valueMatches(resolveDottedPath2(context, path53), expected));
23368
+ return Object.entries(condition).every(([path54, expected]) => valueMatches(resolveDottedPath2(context, path54), expected));
23291
23369
  }
23292
23370
  function withWorkflowBoundaryEval(capability, result) {
23293
23371
  const capabilityKind = capability.config.capabilityKind;
@@ -23724,7 +23802,7 @@ function translateOpenAISseToBrain(opts) {
23724
23802
 
23725
23803
  // src/servers/brain-serve.ts
23726
23804
  import { createServer as createServer2 } from "http";
23727
- import * as path51 from "path";
23805
+ import * as path52 from "path";
23728
23806
 
23729
23807
  // src/chat/loop.ts
23730
23808
  init_agent();
@@ -23878,9 +23956,9 @@ var CodexAppServerClient = class {
23878
23956
  await this.request("thread/resume", { threadId });
23879
23957
  }
23880
23958
  async runTurn(args) {
23881
- await new Promise((resolve19, reject) => {
23959
+ await new Promise((resolve20, reject) => {
23882
23960
  this.process.turnWaiters.set(args.threadId, {
23883
- resolve: resolve19,
23961
+ resolve: resolve20,
23884
23962
  reject,
23885
23963
  onNotification: args.onNotification,
23886
23964
  queue: Promise.resolve()
@@ -23897,8 +23975,8 @@ var CodexAppServerClient = class {
23897
23975
  }
23898
23976
  request(method, params) {
23899
23977
  const id = this.process.nextId++;
23900
- return new Promise((resolve19, reject) => {
23901
- this.process.pending.set(id, { resolve: resolve19, reject });
23978
+ return new Promise((resolve20, reject) => {
23979
+ this.process.pending.set(id, { resolve: resolve20, reject });
23902
23980
  this.process.child.stdin.write(`${JSON.stringify({ method, id, params })}
23903
23981
  `);
23904
23982
  });
@@ -24965,7 +25043,7 @@ async function hydrateDefinitionsFromEnv(cwd = process.cwd(), env = process.env)
24965
25043
  // src/kody-cli.ts
24966
25044
  import { execFileSync as execFileSync24 } from "child_process";
24967
25045
  import * as fs53 from "fs";
24968
- import * as path49 from "path";
25046
+ import * as path50 from "path";
24969
25047
 
24970
25048
  // src/app-auth.ts
24971
25049
  import { createSign } from "crypto";
@@ -25767,9 +25845,9 @@ async function resolveAuthToken(env = process.env) {
25767
25845
  return void 0;
25768
25846
  }
25769
25847
  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";
25848
+ if (fs53.existsSync(path50.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
25849
+ if (fs53.existsSync(path50.join(cwd, "yarn.lock"))) return "yarn";
25850
+ if (fs53.existsSync(path50.join(cwd, "bun.lockb"))) return "bun";
25773
25851
  return "npm";
25774
25852
  }
25775
25853
  function shouldChainScheduledWatch(match) {
@@ -25902,7 +25980,7 @@ async function runCi(argv) {
25902
25980
  return 0;
25903
25981
  }
25904
25982
  const args = parseCiArgs(argv);
25905
- const cwd = args.cwd ? path49.resolve(args.cwd) : process.cwd();
25983
+ const cwd = args.cwd ? path50.resolve(args.cwd) : process.cwd();
25906
25984
  try {
25907
25985
  const n = unpackAllSecrets();
25908
25986
  if (n > 0) process.stdout.write(`\u2192 kody: unpacked ${n} secret(s) from ALL_SECRETS
@@ -26386,7 +26464,7 @@ init_repoWorkspace();
26386
26464
  // src/scripts/brainTurnLog.ts
26387
26465
  init_runtimePaths();
26388
26466
  import * as fs54 from "fs";
26389
- import * as path50 from "path";
26467
+ import * as path51 from "path";
26390
26468
  import posixPath4 from "path/posix";
26391
26469
  var live = /* @__PURE__ */ new Map();
26392
26470
  function brainEventsFilePath(dir, chatId) {
@@ -26433,7 +26511,7 @@ function beginTurn(dir, chatId) {
26433
26511
  };
26434
26512
  live.set(chatId, state);
26435
26513
  const p = brainEventsFilePath(dir, chatId);
26436
- fs54.mkdirSync(path50.dirname(p), { recursive: true });
26514
+ fs54.mkdirSync(path51.dirname(p), { recursive: true });
26437
26515
  return (event) => {
26438
26516
  state.seq += 1;
26439
26517
  const rec = { seq: state.seq, turn, ts: Date.now(), event };
@@ -26571,17 +26649,17 @@ function authOk(req, expected) {
26571
26649
  return false;
26572
26650
  }
26573
26651
  function readJsonBody(req) {
26574
- return new Promise((resolve19, reject) => {
26652
+ return new Promise((resolve20, reject) => {
26575
26653
  const chunks = [];
26576
26654
  req.on("data", (c) => chunks.push(c));
26577
26655
  req.on("end", () => {
26578
26656
  const raw = Buffer.concat(chunks).toString("utf-8");
26579
26657
  if (!raw.trim()) {
26580
- resolve19({});
26658
+ resolve20({});
26581
26659
  return;
26582
26660
  }
26583
26661
  try {
26584
- resolve19(JSON.parse(raw));
26662
+ resolve20(JSON.parse(raw));
26585
26663
  } catch (err) {
26586
26664
  reject(err instanceof Error ? err : new Error(String(err)));
26587
26665
  }
@@ -26873,7 +26951,7 @@ function buildServer(opts) {
26873
26951
  const runTurn = opts.runTurn ?? runChatTurn;
26874
26952
  const createStore = opts.createStore ?? createSessionStore;
26875
26953
  const cloneRepo = opts.cloneRepo ?? defaultCloneRepo;
26876
- const reposRoot = opts.reposRoot ?? path51.join(path51.dirname(path51.resolve(opts.cwd)), "repos");
26954
+ const reposRoot = opts.reposRoot ?? path52.join(path52.dirname(path52.resolve(opts.cwd)), "repos");
26877
26955
  return createServer2(async (req, res) => {
26878
26956
  if (!req.method || !req.url) {
26879
26957
  sendJson(res, 400, { error: "bad request" });
@@ -26954,11 +27032,11 @@ async function brainServe(opts) {
26954
27032
  litellmUrl,
26955
27033
  driver
26956
27034
  });
26957
- await new Promise((resolve19) => {
27035
+ await new Promise((resolve20) => {
26958
27036
  server.listen(port, "0.0.0.0", () => {
26959
27037
  process.stdout.write(`[brain-serve] listening on 0.0.0.0:${port} (cwd=${opts.cwd})
26960
27038
  `);
26961
- resolve19();
27039
+ resolve20();
26962
27040
  });
26963
27041
  });
26964
27042
  const shutdown = (signal) => {
@@ -27213,14 +27291,14 @@ async function startBrainProxy(opts) {
27213
27291
  const { httpServer, handler } = buildBrainProxy(opts);
27214
27292
  const port = opts.port ?? 0;
27215
27293
  const host = opts.host ?? "127.0.0.1";
27216
- await new Promise((resolve19) => httpServer.listen(port, host, () => resolve19()));
27294
+ await new Promise((resolve20) => httpServer.listen(port, host, () => resolve20()));
27217
27295
  const addr = httpServer.address();
27218
27296
  return {
27219
27297
  httpServer,
27220
27298
  port: addr.port,
27221
27299
  url: `http://${host}:${addr.port}`,
27222
- stop: () => new Promise((resolve19) => {
27223
- httpServer.close(() => resolve19());
27300
+ stop: () => new Promise((resolve20) => {
27301
+ httpServer.close(() => resolve20());
27224
27302
  }),
27225
27303
  handler
27226
27304
  };
@@ -27370,23 +27448,23 @@ function buildMcpHttpServer(opts) {
27370
27448
  httpServer,
27371
27449
  routes,
27372
27450
  port,
27373
- stop: () => new Promise((resolve19) => {
27451
+ stop: () => new Promise((resolve20) => {
27374
27452
  let pending = transports.size;
27375
27453
  if (pending === 0) {
27376
- httpServer.close(() => resolve19());
27454
+ httpServer.close(() => resolve20());
27377
27455
  return;
27378
27456
  }
27379
27457
  for (const transport of transports.values()) {
27380
27458
  void transport.close().finally(() => {
27381
27459
  pending--;
27382
- if (pending === 0) httpServer.close(() => resolve19());
27460
+ if (pending === 0) httpServer.close(() => resolve20());
27383
27461
  });
27384
27462
  }
27385
27463
  })
27386
27464
  };
27387
27465
  }
27388
27466
  function listenMcpHttpServer(server, host = "127.0.0.1") {
27389
- return new Promise((resolve19, reject) => {
27467
+ return new Promise((resolve20, reject) => {
27390
27468
  server.httpServer.once("error", reject);
27391
27469
  server.httpServer.listen(server.port, host, () => {
27392
27470
  server.httpServer.off("error", reject);
@@ -27394,7 +27472,7 @@ function listenMcpHttpServer(server, host = "127.0.0.1") {
27394
27472
  if (addr && typeof addr === "object") {
27395
27473
  server.port = addr.port;
27396
27474
  }
27397
- resolve19();
27475
+ resolve20();
27398
27476
  });
27399
27477
  });
27400
27478
  }
@@ -27477,7 +27555,7 @@ async function loadConfigSafe() {
27477
27555
  }
27478
27556
 
27479
27557
  // src/chat-cli.ts
27480
- import * as path52 from "path";
27558
+ import * as path53 from "path";
27481
27559
 
27482
27560
  // src/chat/inbox.ts
27483
27561
  import { execFileSync as execFileSync25 } from "child_process";
@@ -27544,7 +27622,7 @@ async function waitForNextUserMessage(opts) {
27544
27622
  }
27545
27623
  }
27546
27624
  function sleep3(ms) {
27547
- return new Promise((resolve19) => setTimeout(resolve19, ms));
27625
+ return new Promise((resolve20) => setTimeout(resolve20, ms));
27548
27626
  }
27549
27627
  function currentBranch(cwd) {
27550
27628
  try {
@@ -27768,7 +27846,7 @@ async function runChat(argv) {
27768
27846
  ${CHAT_HELP}`);
27769
27847
  return 64;
27770
27848
  }
27771
- const cwd = args.cwd ? path52.resolve(args.cwd) : process.cwd();
27849
+ const cwd = args.cwd ? path53.resolve(args.cwd) : process.cwd();
27772
27850
  const sessionId = args.sessionId;
27773
27851
  const runRequest = readRunRequestFromEnv();
27774
27852
  if (runRequest && "request" in runRequest) {
@@ -27964,8 +28042,8 @@ var FlyClient = class {
27964
28042
  get fetch() {
27965
28043
  return this.opts.fetchImpl ?? fetch;
27966
28044
  }
27967
- async call(path53, init = {}) {
27968
- const res = await this.fetch(`${FLY_API_BASE}${path53}`, {
28045
+ async call(path54, init = {}) {
28046
+ const res = await this.fetch(`${FLY_API_BASE}${path54}`, {
27969
28047
  method: init.method ?? "GET",
27970
28048
  headers: {
27971
28049
  Authorization: `Bearer ${this.opts.token}`,
@@ -27976,7 +28054,7 @@ var FlyClient = class {
27976
28054
  if (res.status === 404 && init.allow404) return null;
27977
28055
  if (!res.ok) {
27978
28056
  const text2 = await res.text().catch(() => "");
27979
- throw new Error(`Fly API ${res.status} on ${path53}: ${text2.slice(0, 200) || res.statusText}`);
28057
+ throw new Error(`Fly API ${res.status} on ${path54}: ${text2.slice(0, 200) || res.statusText}`);
27980
28058
  }
27981
28059
  if (res.status === 204) return null;
27982
28060
  const raw = await res.text();
@@ -28489,14 +28567,14 @@ function sendJson2(res, status, body) {
28489
28567
  res.end(JSON.stringify(body));
28490
28568
  }
28491
28569
  function readJsonBody2(req) {
28492
- return new Promise((resolve19, reject) => {
28570
+ return new Promise((resolve20, reject) => {
28493
28571
  const chunks = [];
28494
28572
  req.on("data", (c) => chunks.push(c));
28495
28573
  req.on("end", () => {
28496
28574
  const raw = Buffer.concat(chunks).toString("utf-8");
28497
- if (!raw.trim()) return resolve19({});
28575
+ if (!raw.trim()) return resolve20({});
28498
28576
  try {
28499
- resolve19(JSON.parse(raw));
28577
+ resolve20(JSON.parse(raw));
28500
28578
  } catch (err) {
28501
28579
  reject(err instanceof Error ? err : new Error(String(err)));
28502
28580
  }
@@ -28650,10 +28728,10 @@ async function poolServe() {
28650
28728
  }
28651
28729
  });
28652
28730
  const apiHost = process.env.POOL_API_HOST ?? "::";
28653
- await new Promise((resolve19) => {
28731
+ await new Promise((resolve20) => {
28654
28732
  server.listen(apiPort, apiHost, () => {
28655
28733
  log(`listening on ${apiHost}:${apiPort} (min=${min}, app=${app}, region=${region})`);
28656
- resolve19();
28734
+ resolve20();
28657
28735
  });
28658
28736
  });
28659
28737
  if (loopTickEnabled) void runLoopTick();
@@ -28693,17 +28771,17 @@ function authOk2(req, expected) {
28693
28771
  return false;
28694
28772
  }
28695
28773
  function readJsonBody3(req) {
28696
- return new Promise((resolve19, reject) => {
28774
+ return new Promise((resolve20, reject) => {
28697
28775
  const chunks = [];
28698
28776
  req.on("data", (c) => chunks.push(c));
28699
28777
  req.on("end", () => {
28700
28778
  const raw = Buffer.concat(chunks).toString("utf-8");
28701
28779
  if (!raw.trim()) {
28702
- resolve19({});
28780
+ resolve20({});
28703
28781
  return;
28704
28782
  }
28705
28783
  try {
28706
- resolve19(JSON.parse(raw));
28784
+ resolve20(JSON.parse(raw));
28707
28785
  } catch (err) {
28708
28786
  reject(err instanceof Error ? err : new Error(String(err)));
28709
28787
  }
@@ -28778,13 +28856,13 @@ async function defaultRunJob(job) {
28778
28856
  ...interactive && job.idleExitMs ? { KODY_IDLE_EXIT_MS: String(job.idleExitMs) } : {},
28779
28857
  ...interactive && job.hardCapMs ? { KODY_HARD_CAP_MS: String(job.hardCapMs) } : {}
28780
28858
  };
28781
- const run = (cmd, args, cwd) => new Promise((resolve19) => {
28859
+ const run = (cmd, args, cwd) => new Promise((resolve20) => {
28782
28860
  const child = spawn9(cmd, args, { stdio: "inherit", env: childEnv, cwd });
28783
- child.on("exit", (code) => resolve19(code ?? 0));
28861
+ child.on("exit", (code) => resolve20(code ?? 0));
28784
28862
  child.on("error", (err) => {
28785
28863
  process.stderr.write(`[runner-serve] ${cmd} failed: ${err.message}
28786
28864
  `);
28787
- resolve19(1);
28865
+ resolve20(1);
28788
28866
  });
28789
28867
  });
28790
28868
  process.stdout.write(`[runner-serve] job ${job.jobId}: cloning ${job.repo}@${branch}
@@ -28860,11 +28938,11 @@ async function runnerServe() {
28860
28938
  const port = Number(process.env.PORT ?? DEFAULT_PORT2);
28861
28939
  const server = buildServer2({ apiKey });
28862
28940
  const host = process.env.RUNNER_HOST ?? "::";
28863
- await new Promise((resolve19) => {
28941
+ await new Promise((resolve20) => {
28864
28942
  server.listen(port, host, () => {
28865
28943
  process.stdout.write(`[runner-serve] listening on ${host}:${port} (idle, awaiting job)
28866
28944
  `);
28867
- resolve19();
28945
+ resolve20();
28868
28946
  });
28869
28947
  });
28870
28948
  const shutdown = (signal) => {
@@ -28933,14 +29011,14 @@ async function serve(opts) {
28933
29011
  `);
28934
29012
  const args = ["--dangerously-skip-permissions", "--model", model.model];
28935
29013
  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));
29014
+ const exitCode = await new Promise((resolve20) => {
29015
+ child.on("exit", (code) => resolve20(code ?? 0));
28938
29016
  child.on("error", (err) => {
28939
29017
  process.stderr.write(`[kody serve] failed to launch Claude Code: ${err.message}
28940
29018
  `);
28941
29019
  process.stderr.write(` Install: https://docs.anthropic.com/claude/docs/claude-code
28942
29020
  `);
28943
- resolve19(1);
29021
+ resolve20(1);
28944
29022
  });
28945
29023
  });
28946
29024
  killProxy();
@@ -36,7 +36,7 @@
36
36
  "claudeCode": {
37
37
  "model": "inherit",
38
38
  "permissionMode": "acceptEdits",
39
- "maxTurns": 60,
39
+ "maxTurns": 144,
40
40
  "maxThinkingTokens": null,
41
41
  "systemPromptAppend": null,
42
42
  "enableVerifyTool": true,
@@ -23,7 +23,7 @@
23
23
  "claudeCode": {
24
24
  "model": "inherit",
25
25
  "permissionMode": "acceptEdits",
26
- "maxTurns": 60,
26
+ "maxTurns": 144,
27
27
  "maxThinkingTokens": null,
28
28
  "systemPromptAppend": null,
29
29
  "tools": ["Read", "Bash", "Edit", "Write", "Glob", "Grep"],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kody-ade/kody-engine",
3
- "version": "0.4.549",
3
+ "version": "0.4.551",
4
4
  "description": "kody — autonomous development engine. Single-session Claude Code agent behind a generic executor + declarative implementation profiles.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -93,6 +93,6 @@ jobs:
93
93
  uses: actions/upload-artifact@v4
94
94
  with:
95
95
  name: kody-quality-${{ github.run_id }}
96
- path: apps/dashboard/test-results/live-ui-gate/
96
+ path: test-results/quality-runs/
97
97
  if-no-files-found: ignore
98
98
  retention-days: 30