@kody-ade/kody-engine 0.4.409 → 0.4.411

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.409",
18
+ version: "0.4.411",
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",
@@ -746,7 +746,7 @@ function buildVerifyEnv(source = process.env) {
746
746
  return env;
747
747
  }
748
748
  function runCommand(command, cwd) {
749
- return new Promise((resolve16) => {
749
+ return new Promise((resolve17) => {
750
750
  const start = Date.now();
751
751
  const child = spawn(command, {
752
752
  cwd,
@@ -775,11 +775,11 @@ function runCommand(command, cwd) {
775
775
  child.on("exit", (code) => {
776
776
  clearTimeout(timer);
777
777
  const tail = Buffer.concat(buffers).toString("utf-8").slice(-TAIL_CHARS);
778
- resolve16({ exitCode: code ?? -1, durationMs: Date.now() - start, tail });
778
+ resolve17({ exitCode: code ?? -1, durationMs: Date.now() - start, tail });
779
779
  });
780
780
  child.on("error", (err) => {
781
781
  clearTimeout(timer);
782
- resolve16({ exitCode: -1, durationMs: Date.now() - start, tail: err.message });
782
+ resolve17({ exitCode: -1, durationMs: Date.now() - start, tail: err.message });
783
783
  });
784
784
  });
785
785
  }
@@ -1088,7 +1088,7 @@ function cmsHeaders(opts) {
1088
1088
  }
1089
1089
  };
1090
1090
  }
1091
- async function callDashboardCms(opts, path51, init = {}) {
1091
+ async function callDashboardCms(opts, path52, init = {}) {
1092
1092
  const baseUrl = dashboardBaseUrl(opts);
1093
1093
  if (!baseUrl) {
1094
1094
  return {
@@ -1100,7 +1100,7 @@ async function callDashboardCms(opts, path51, init = {}) {
1100
1100
  const headerResult = cmsHeaders(opts);
1101
1101
  if (!headerResult.ok) return headerResult;
1102
1102
  try {
1103
- const res = await fetch(`${baseUrl}${path51}`, {
1103
+ const res = await fetch(`${baseUrl}${path52}`, {
1104
1104
  ...init,
1105
1105
  headers: {
1106
1106
  ...headerResult.headers,
@@ -1172,8 +1172,8 @@ function documentArg(value) {
1172
1172
  function normalizeCmsDocumentIdInput(input) {
1173
1173
  const trimmed = stripWrappingQuotes(input.trim());
1174
1174
  const withoutQuery = trimmed.split(/[?#]/, 1)[0] ?? trimmed;
1175
- const path51 = parseDocumentPath(withoutQuery);
1176
- return path51 ?? parseDocumentIdSegment(withoutQuery) ?? withoutQuery;
1175
+ const path52 = parseDocumentPath(withoutQuery);
1176
+ return path52 ?? parseDocumentIdSegment(withoutQuery) ?? withoutQuery;
1177
1177
  }
1178
1178
  function stripWrappingQuotes(value) {
1179
1179
  let current = value;
@@ -1184,9 +1184,9 @@ function stripWrappingQuotes(value) {
1184
1184
  }
1185
1185
  }
1186
1186
  function parseDocumentPath(value) {
1187
- const path51 = value.startsWith("http://") || value.startsWith("https://") ? urlPathname(value) : value;
1188
- if (!path51?.includes("/content/entries/")) return null;
1189
- const parts = path51.split("/").filter(Boolean).map(decodePathPart);
1187
+ const path52 = value.startsWith("http://") || value.startsWith("https://") ? urlPathname(value) : value;
1188
+ if (!path52?.includes("/content/entries/")) return null;
1189
+ const parts = path52.split("/").filter(Boolean).map(decodePathPart);
1190
1190
  const entriesIndex = parts.findIndex((part, index) => part === "content" && parts[index + 1] === "entries");
1191
1191
  const idPart = parts[entriesIndex + 3];
1192
1192
  if (!idPart || idPart === "new") return null;
@@ -2219,6 +2219,108 @@ var init_convex_client = __esm({
2219
2219
  }
2220
2220
  });
2221
2221
 
2222
+ // src/kody-api-client.ts
2223
+ import { getFunctionName } from "convex/server";
2224
+ function apiUrl(env) {
2225
+ return (env.KODY_API_URL?.trim() || DEFAULT_KODY_API_URL).replace(/\/$/, "");
2226
+ }
2227
+ function oidcRequestUrl(env) {
2228
+ const raw = env.ACTIONS_ID_TOKEN_REQUEST_URL?.trim();
2229
+ if (!raw) throw new Error("GitHub Actions OIDC request URL is unavailable");
2230
+ const url = new URL(raw);
2231
+ if (url.protocol !== "https:") throw new Error("GitHub Actions OIDC request URL must use HTTPS");
2232
+ url.searchParams.set("audience", OIDC_AUDIENCE);
2233
+ return url;
2234
+ }
2235
+ function tokenExpiry(token) {
2236
+ try {
2237
+ const payload = JSON.parse(Buffer.from(token.split(".")[1] ?? "", "base64url").toString("utf8"));
2238
+ return typeof payload.exp === "number" ? payload.exp * 1e3 : Date.now() + 4 * 6e4;
2239
+ } catch {
2240
+ return Date.now() + 4 * 6e4;
2241
+ }
2242
+ }
2243
+ async function githubOidcToken(env, force = false) {
2244
+ if (!force && cachedToken && cachedToken.expiresAt - 6e4 > Date.now()) return cachedToken.value;
2245
+ const requestToken = env.ACTIONS_ID_TOKEN_REQUEST_TOKEN?.trim();
2246
+ if (!requestToken) throw new Error("GitHub Actions OIDC request token is unavailable");
2247
+ const response = await fetch(oidcRequestUrl(env), {
2248
+ headers: { Authorization: `Bearer ${requestToken}` },
2249
+ signal: AbortSignal.timeout(15e3)
2250
+ });
2251
+ if (!response.ok) throw new Error(`GitHub Actions identity request failed (${response.status})`);
2252
+ const body = await response.json();
2253
+ if (typeof body.value !== "string" || !body.value) throw new Error("GitHub Actions identity response was invalid");
2254
+ cachedToken = { value: body.value, expiresAt: tokenExpiry(body.value) };
2255
+ return body.value;
2256
+ }
2257
+ function operationName(fn) {
2258
+ return getFunctionName(fn).replace(":", ".");
2259
+ }
2260
+ async function callKodyApi(kind, fn, args, env) {
2261
+ const call = async (forceToken) => {
2262
+ const token = await githubOidcToken(env, forceToken);
2263
+ return fetch(`${apiUrl(env)}/api/kody/engine/backend`, {
2264
+ method: "POST",
2265
+ headers: {
2266
+ Authorization: `Bearer ${token}`,
2267
+ "Content-Type": "application/json"
2268
+ },
2269
+ body: JSON.stringify({ kind, operation: operationName(fn), args }),
2270
+ signal: AbortSignal.timeout(3e4)
2271
+ });
2272
+ };
2273
+ let response = await call(false);
2274
+ if (response.status === 401) response = await call(true);
2275
+ if (!response.ok) throw new Error(`Kody backend request failed (${response.status})`);
2276
+ const body = await response.json();
2277
+ return body.result;
2278
+ }
2279
+ function hasGitHubActionsIdentity(env = process.env) {
2280
+ return Boolean(
2281
+ env.GITHUB_ACTIONS === "true" && env.ACTIONS_ID_TOKEN_REQUEST_URL?.trim() && env.ACTIONS_ID_TOKEN_REQUEST_TOKEN?.trim()
2282
+ );
2283
+ }
2284
+ function createKodyApiBackendClient(env = process.env) {
2285
+ if (!hasGitHubActionsIdentity(env)) throw new Error("GitHub Actions workflow identity is unavailable");
2286
+ return {
2287
+ query: (fn, args) => callKodyApi("query", fn, args, env),
2288
+ mutation: (fn, args) => callKodyApi("mutation", fn, args, env)
2289
+ };
2290
+ }
2291
+ async function readRuntimeSecretFromKody(name, env = process.env) {
2292
+ const token = await githubOidcToken(env);
2293
+ const response = await fetch(`${apiUrl(env)}/api/kody/engine/secret`, {
2294
+ method: "POST",
2295
+ headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json" },
2296
+ body: JSON.stringify({ name }),
2297
+ signal: AbortSignal.timeout(3e4)
2298
+ });
2299
+ if (response.status === 404) return null;
2300
+ if (!response.ok) throw new Error(`Kody secret request failed (${response.status})`);
2301
+ const body = await response.json();
2302
+ return typeof body.value === "string" ? body.value : null;
2303
+ }
2304
+ async function readPreviewContextFromKody(env = process.env) {
2305
+ const token = await githubOidcToken(env);
2306
+ const response = await fetch(`${apiUrl(env)}/api/kody/engine/preview-context`, {
2307
+ method: "POST",
2308
+ headers: { Authorization: `Bearer ${token}` },
2309
+ signal: AbortSignal.timeout(3e4)
2310
+ });
2311
+ if (!response.ok) throw new Error(`Kody preview context request failed (${response.status})`);
2312
+ return await response.json();
2313
+ }
2314
+ var DEFAULT_KODY_API_URL, OIDC_AUDIENCE, cachedToken;
2315
+ var init_kody_api_client = __esm({
2316
+ "src/kody-api-client.ts"() {
2317
+ "use strict";
2318
+ DEFAULT_KODY_API_URL = "https://kody-dashboard-khaki.vercel.app";
2319
+ OIDC_AUDIENCE = "kody-api";
2320
+ cachedToken = null;
2321
+ }
2322
+ });
2323
+
2222
2324
  // src/state-backend.ts
2223
2325
  import { anyApi } from "convex/server";
2224
2326
  function requireTenant(tenantId2) {
@@ -2236,10 +2338,16 @@ function requireNonEmpty(value, name) {
2236
2338
  return normalized;
2237
2339
  }
2238
2340
  function createStateBackendFromEnv(env = process.env, client) {
2239
- const url = env.CONVEX_URL?.trim();
2240
- const serviceKey = env.KODY_SERVICE_KEY?.trim();
2241
- if (!url || !serviceKey) throw new Error("CONVEX_URL and KODY_SERVICE_KEY are required");
2242
- const transport = client ?? createConvexClientFromEnv(env);
2341
+ let transport = client;
2342
+ if (!transport && hasGitHubActionsIdentity(env)) transport = createKodyApiBackendClient(env);
2343
+ if (!transport) {
2344
+ const url = env.CONVEX_URL?.trim();
2345
+ const serviceKey = env.KODY_SERVICE_KEY?.trim();
2346
+ if (!url || !serviceKey) {
2347
+ throw new Error("GitHub Actions identity or direct Kody backend credentials are required");
2348
+ }
2349
+ transport = createConvexClientFromEnv(env);
2350
+ }
2243
2351
  return {
2244
2352
  async get(tenantId2, taskKey, kind) {
2245
2353
  const result = await transport.query(anyApi.taskState.get, {
@@ -2412,10 +2520,14 @@ function createStateBackendFromEnv(env = process.env, client) {
2412
2520
  }
2413
2521
  };
2414
2522
  }
2523
+ function hasStateBackendConfig(env = process.env) {
2524
+ return hasGitHubActionsIdentity(env) || Boolean(env.CONVEX_URL?.trim() && env.KODY_SERVICE_KEY?.trim());
2525
+ }
2415
2526
  var init_state_backend = __esm({
2416
2527
  "src/state-backend.ts"() {
2417
2528
  "use strict";
2418
2529
  init_convex_client();
2530
+ init_kody_api_client();
2419
2531
  }
2420
2532
  });
2421
2533
 
@@ -3041,7 +3153,7 @@ var init_repoWorkspace = __esm({
3041
3153
  defaultCloneRepo = (repo, token, dir) => {
3042
3154
  fs7.mkdirSync(path8.dirname(dir), { recursive: true });
3043
3155
  const clone = buildCloneProcess(repo, token);
3044
- return new Promise((resolve16, reject) => {
3156
+ return new Promise((resolve17, reject) => {
3045
3157
  const child = spawn2("git", ["clone", "--depth=1", clone.url, dir], {
3046
3158
  env: clone.env,
3047
3159
  stdio: "inherit"
@@ -3061,7 +3173,7 @@ var init_repoWorkspace = __esm({
3061
3173
  }
3062
3174
  } catch {
3063
3175
  }
3064
- resolve16();
3176
+ resolve17();
3065
3177
  });
3066
3178
  child.on("error", reject);
3067
3179
  });
@@ -3372,10 +3484,10 @@ async function runAgent(opts) {
3372
3484
  let timer;
3373
3485
  let next;
3374
3486
  if (turnTimeoutMs > 0) {
3375
- const timeoutPromise = new Promise((resolve16) => {
3487
+ const timeoutPromise = new Promise((resolve17) => {
3376
3488
  timer = setTimeout(() => {
3377
3489
  timedOut = true;
3378
- resolve16({ done: true, value: void 0 });
3490
+ resolve17({ done: true, value: void 0 });
3379
3491
  }, turnTimeoutMs);
3380
3492
  });
3381
3493
  next = await Promise.race([nextPromise, timeoutPromise]);
@@ -3391,7 +3503,7 @@ async function runAgent(opts) {
3391
3503
  try {
3392
3504
  await Promise.race([
3393
3505
  iterator.return(void 0).catch(() => void 0),
3394
- new Promise((resolve16) => setTimeout(resolve16, 1e4).unref())
3506
+ new Promise((resolve17) => setTimeout(resolve17, 1e4).unref())
3395
3507
  ]);
3396
3508
  } catch {
3397
3509
  }
@@ -3657,7 +3769,7 @@ function prepareTaskArtifactsDir(cwd, taskId) {
3657
3769
  function initializeTaskArtifacts(artifacts, metadata = { taskType: "job" }) {
3658
3770
  const startedAt = (/* @__PURE__ */ new Date()).toISOString();
3659
3771
  const defaults = {
3660
- "context.json": JSON.stringify(
3772
+ "context.json": `${JSON.stringify(
3661
3773
  {
3662
3774
  taskId: artifacts.taskId,
3663
3775
  taskType: metadata.taskType,
@@ -3674,7 +3786,8 @@ function initializeTaskArtifacts(artifacts, metadata = { taskType: "job" }) {
3674
3786
  },
3675
3787
  null,
3676
3788
  2
3677
- ) + "\n",
3789
+ )}
3790
+ `,
3678
3791
  "memory-recs.json": "[]\n",
3679
3792
  "followups.json": "[]\n",
3680
3793
  "handoff-notes.md": "Baseline handoff: the agent did not provide additional notes.\n"
@@ -3699,12 +3812,10 @@ function verifyTaskArtifacts(absDir) {
3699
3812
  }
3700
3813
  async function persistTaskArtifactsToState(config, _cwd, artifacts) {
3701
3814
  const tenantId2 = config.github?.owner && config.github.repo ? `${config.github.owner}/${config.github.repo}` : process.env.GITHUB_REPOSITORY?.trim();
3702
- if (process.env.GITHUB_ACTIONS === "true" && (!process.env.CONVEX_URL || !process.env.KODY_SERVICE_KEY || !tenantId2)) {
3703
- throw new Error(
3704
- "Convex artifact backend is required in GitHub Actions (CONVEX_URL, KODY_SERVICE_KEY, and repository identity)"
3705
- );
3815
+ if (process.env.GITHUB_ACTIONS === "true" && (!hasStateBackendConfig() || !tenantId2)) {
3816
+ throw new Error("Kody backend access and repository identity are required in GitHub Actions");
3706
3817
  }
3707
- if (process.env.CONVEX_URL && process.env.KODY_SERVICE_KEY && tenantId2) {
3818
+ if (hasStateBackendConfig() && tenantId2) {
3708
3819
  const backend = createStateBackendFromEnv();
3709
3820
  for (const file of TASK_ARTIFACT_FILES) {
3710
3821
  const full = path11.join(artifacts.absDir, file);
@@ -5834,7 +5945,7 @@ var init_loadMemoryContext = __esm({
5834
5945
  loadMemoryContext = async (ctx) => {
5835
5946
  if (typeof ctx.data.memoryContext === "string") return;
5836
5947
  const tenantId2 = ctx.config.github?.owner && ctx.config.github.repo ? `${ctx.config.github.owner}/${ctx.config.github.repo}` : process.env.GITHUB_REPOSITORY?.trim();
5837
- if (process.env.CONVEX_URL && process.env.KODY_SERVICE_KEY && tenantId2) {
5948
+ if (hasStateBackendConfig() && tenantId2) {
5838
5949
  try {
5839
5950
  const backend = createStateBackendFromEnv();
5840
5951
  const docs = await backend.listRepoDocs(tenantId2, "memory:");
@@ -6559,11 +6670,11 @@ async function nextAvailableLitellmUrl(url) {
6559
6670
  throw new Error(`no free LiteLLM port found after ${startPort}`);
6560
6671
  }
6561
6672
  function canListen(port, host) {
6562
- return new Promise((resolve16) => {
6673
+ return new Promise((resolve17) => {
6563
6674
  const server = net.createServer();
6564
- server.once("error", () => resolve16(false));
6675
+ server.once("error", () => resolve17(false));
6565
6676
  server.once("listening", () => {
6566
- server.close(() => resolve16(true));
6677
+ server.close(() => resolve17(true));
6567
6678
  });
6568
6679
  server.listen(port, host);
6569
6680
  });
@@ -7724,9 +7835,9 @@ import * as fs26 from "fs";
7724
7835
  function stageGoalRunLogEvent(data, goalId, event, at = nowIso()) {
7725
7836
  const logs = goalRunLogs(data);
7726
7837
  const existing = logs[goalId];
7727
- const path51 = existing?.path ?? goalRunLogPath(goalId, data);
7838
+ const path52 = existing?.path ?? goalRunLogPath(goalId, data);
7728
7839
  logs[goalId] = {
7729
- path: path51,
7840
+ path: path52,
7730
7841
  events: [...existing?.events ?? [], buildGoalRunLogEvent(data, goalId, event, at)]
7731
7842
  };
7732
7843
  }
@@ -7851,7 +7962,7 @@ function buildGoalRunLogEvent(data, goalId, event, at) {
7851
7962
  if (context !== void 0) base.dispatchContext = context;
7852
7963
  return base;
7853
7964
  }
7854
- function enrichGoalRunLogEvent(config, data, logPath, event) {
7965
+ function enrichGoalRunLogEvent(config, data, _logPath, event) {
7855
7966
  const trigger = event.trigger ?? triggerContext();
7856
7967
  const job = event.job ?? jobContext(data);
7857
7968
  const run = event.run ?? runContext(data);
@@ -8142,7 +8253,7 @@ function backendTenant(config) {
8142
8253
  return owner && repo ? `${owner}/${repo}` : null;
8143
8254
  }
8144
8255
  function decodeGoal(doc) {
8145
- if (!doc || !doc.state || typeof doc.state !== "object" || Array.isArray(doc.state)) return null;
8256
+ if (!doc?.state || typeof doc.state !== "object" || Array.isArray(doc.state)) return null;
8146
8257
  const state = doc.state;
8147
8258
  if (typeof state.state !== "string" || !state.extra || typeof state.extra !== "object") return null;
8148
8259
  return state;
@@ -8699,11 +8810,11 @@ function validateWorkflow(value, options = {}) {
8699
8810
  function formatWorkflowValidationIssues(issues) {
8700
8811
  return issues.map((entry) => `${entry.path}: ${entry.message}`);
8701
8812
  }
8702
- function validateDataMatch(value, path51, issues, capabilityOutputs) {
8813
+ function validateDataMatch(value, path52, issues, capabilityOutputs) {
8703
8814
  if (value === void 0) return;
8704
8815
  const match = asRecord2(value);
8705
8816
  if (!match || Object.keys(match).length === 0) {
8706
- issue(issues, "invalid_condition", path51, "workflow condition must contain at least one match");
8817
+ issue(issues, "invalid_condition", path52, "workflow condition must contain at least one match");
8707
8818
  return;
8708
8819
  }
8709
8820
  for (const [field, expected] of Object.entries(match)) {
@@ -8711,7 +8822,7 @@ function validateDataMatch(value, path51, issues, capabilityOutputs) {
8711
8822
  issue(
8712
8823
  issues,
8713
8824
  "invalid_data_path",
8714
- `${path51}.${field}`,
8825
+ `${path52}.${field}`,
8715
8826
  `workflow condition must read from facts, evidence, artifacts, result, workflow, or lastOutcome`
8716
8827
  );
8717
8828
  }
@@ -8719,12 +8830,12 @@ function validateDataMatch(value, path51, issues, capabilityOutputs) {
8719
8830
  issue(
8720
8831
  issues,
8721
8832
  "undeclared_result_path",
8722
- `${path51}.${field}`,
8833
+ `${path52}.${field}`,
8723
8834
  `workflow condition reads ${field}, but the source capability does not declare it`
8724
8835
  );
8725
8836
  }
8726
8837
  if (!isComparable(expected)) {
8727
- issue(issues, "invalid_condition_value", `${path51}.${field}`, "workflow condition value must be a JSON scalar");
8838
+ issue(issues, "invalid_condition_value", `${path52}.${field}`, "workflow condition value must be a JSON scalar");
8728
8839
  }
8729
8840
  }
8730
8841
  }
@@ -8742,8 +8853,8 @@ function isComparable(value) {
8742
8853
  if (value === null || ["string", "number", "boolean"].includes(typeof value)) return true;
8743
8854
  return Array.isArray(value) && value.length > 0 && value.every((item) => isComparable(item) && !Array.isArray(item));
8744
8855
  }
8745
- function issue(issues, code, path51, message) {
8746
- issues.push({ code, path: path51, message });
8856
+ function issue(issues, code, path52, message) {
8857
+ issues.push({ code, path: path52, message });
8747
8858
  }
8748
8859
  var SAFE_NAME, SAFE_STEP_ID, SAFE_DATA_PATH, SUPPORTED_STEP_FIELDS, SUPPORTED_TRANSITION_FIELDS;
8749
8860
  var init_workflowValidation = __esm({
@@ -9001,15 +9112,15 @@ var init_backendStateBackend = __esm({
9001
9112
  this.jobsDir = opts.jobsDir.replace(/\/+$/, "");
9002
9113
  }
9003
9114
  async load(slug) {
9004
- const path51 = stateFilePath(this.jobsDir, slug);
9115
+ const path52 = stateFilePath(this.jobsDir, slug);
9005
9116
  const loaded = await createStateBackendFromEnv().get(this.tenantId, `capabilities/${slug}`, "job-state");
9006
9117
  if (!loaded) {
9007
- return { path: path51, handle: null, state: initialStateEnvelope("seed"), created: true };
9118
+ return { path: path52, handle: null, state: initialStateEnvelope("seed"), created: true };
9008
9119
  }
9009
9120
  if (!isStateEnvelope(loaded.doc)) {
9010
9121
  throw new Error(`BackendStateBackend: capabilities/${slug} is not a StateEnvelope`);
9011
9122
  }
9012
- return { path: path51, handle: loaded.updatedAt, state: loaded.doc, created: false };
9123
+ return { path: path52, handle: loaded.updatedAt, state: loaded.doc, created: false };
9013
9124
  }
9014
9125
  async save(loaded, next) {
9015
9126
  if (!loaded.created && isStateUnchanged(loaded.state, next)) return false;
@@ -9535,7 +9646,7 @@ function subjectCandidates(kind, id, state) {
9535
9646
  return [...ids].map((candidate) => ({ kind, id: candidate }));
9536
9647
  }
9537
9648
  async function firstTrustOverride(ctx, subjects) {
9538
- const backendConfigured = Boolean(process.env.CONVEX_URL?.trim() && process.env.KODY_SERVICE_KEY?.trim());
9649
+ const backendConfigured = hasStateBackendConfig();
9539
9650
  if (!backendConfigured) return null;
9540
9651
  const repoSlug = ctx.config.github?.owner && ctx.config.github?.repo ? `${ctx.config.github.owner}/${ctx.config.github.repo}` : "";
9541
9652
  for (const subject of subjects) {
@@ -9751,6 +9862,7 @@ var init_advanceManagedGoal = __esm({
9751
9862
  init_typeDefinitions();
9752
9863
  init_issue();
9753
9864
  init_registry();
9865
+ init_state_backend();
9754
9866
  init_trustPolicy();
9755
9867
  init_workflowDefinitions();
9756
9868
  init_goalCapabilityScheduling();
@@ -10048,12 +10160,12 @@ function resolveTrigger(force) {
10048
10160
  }
10049
10161
  async function appendActivity(ctx, record2) {
10050
10162
  const tenantId2 = ctx.config.github?.owner && ctx.config.github.repo ? `${ctx.config.github.owner}/${ctx.config.github.repo}` : process.env.GITHUB_REPOSITORY;
10051
- if (process.env.CONVEX_URL?.trim() && process.env.KODY_SERVICE_KEY?.trim() && tenantId2) {
10163
+ if (hasStateBackendConfig() && tenantId2) {
10052
10164
  await createStateBackendFromEnv().appendDailyLog(tenantId2, "activity", record2.ts.slice(0, 10), record2);
10053
10165
  return;
10054
10166
  }
10055
10167
  if (process.env.GITHUB_ACTIONS === "true") {
10056
- throw new Error("Convex backend is required for company activity in GitHub Actions");
10168
+ throw new Error("Kody backend access is required for company activity in GitHub Actions");
10057
10169
  }
10058
10170
  }
10059
10171
  var appendCompanyActivity;
@@ -13804,14 +13916,33 @@ var init_fixFlow = __esm({
13804
13916
  }
13805
13917
  });
13806
13918
 
13807
- // src/scripts/initFlow.ts
13808
- import { execFileSync as execFileSync14 } from "child_process";
13919
+ // src/workflow-template.ts
13809
13920
  import * as fs35 from "fs";
13810
13921
  import * as path33 from "path";
13922
+ import { fileURLToPath } from "url";
13923
+ function loadKodyWorkflowTemplate() {
13924
+ const here = path33.dirname(fileURLToPath(import.meta.url));
13925
+ const candidates = [path33.resolve(here, "../templates/kody.yml"), path33.resolve(here, "../../templates/kody.yml")];
13926
+ const source = candidates.find((candidate) => fs35.existsSync(candidate));
13927
+ if (!source) throw new Error(`Kody workflow template is missing: ${KODY_WORKFLOW_TEMPLATE_PATH}`);
13928
+ return fs35.readFileSync(source, "utf8");
13929
+ }
13930
+ var KODY_WORKFLOW_TEMPLATE_PATH;
13931
+ var init_workflow_template = __esm({
13932
+ "src/workflow-template.ts"() {
13933
+ "use strict";
13934
+ KODY_WORKFLOW_TEMPLATE_PATH = "templates/kody.yml";
13935
+ }
13936
+ });
13937
+
13938
+ // src/scripts/initFlow.ts
13939
+ import { execFileSync as execFileSync14 } from "child_process";
13940
+ import * as fs36 from "fs";
13941
+ import * as path34 from "path";
13811
13942
  function detectPackageManager(cwd) {
13812
- if (fs35.existsSync(path33.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
13813
- if (fs35.existsSync(path33.join(cwd, "yarn.lock"))) return "yarn";
13814
- if (fs35.existsSync(path33.join(cwd, "bun.lockb"))) return "bun";
13943
+ if (fs36.existsSync(path34.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
13944
+ if (fs36.existsSync(path34.join(cwd, "yarn.lock"))) return "yarn";
13945
+ if (fs36.existsSync(path34.join(cwd, "bun.lockb"))) return "bun";
13815
13946
  return "npm";
13816
13947
  }
13817
13948
  function qualityCommandsFor(pm) {
@@ -13883,22 +14014,22 @@ function performInit(cwd, force) {
13883
14014
  const pm = detectPackageManager(cwd);
13884
14015
  const ownerRepo = detectOwnerRepo(cwd);
13885
14016
  const defaultBranch = defaultBranchFromGit(cwd);
13886
- const configPath = path33.join(cwd, "kody.config.json");
13887
- if (fs35.existsSync(configPath) && !force) {
14017
+ const configPath = path34.join(cwd, "kody.config.json");
14018
+ if (fs36.existsSync(configPath) && !force) {
13888
14019
  skipped.push("kody.config.json");
13889
14020
  } else {
13890
14021
  const cfg = makeConfig(pm, ownerRepo, defaultBranch);
13891
- fs35.writeFileSync(configPath, `${JSON.stringify(cfg, null, 2)}
14022
+ fs36.writeFileSync(configPath, `${JSON.stringify(cfg, null, 2)}
13892
14023
  `);
13893
14024
  wrote.push("kody.config.json");
13894
14025
  }
13895
- const workflowDir = path33.join(cwd, ".github", "workflows");
13896
- const workflowPath = path33.join(workflowDir, "kody.yml");
13897
- if (fs35.existsSync(workflowPath) && !force) {
14026
+ const workflowDir = path34.join(cwd, ".github", "workflows");
14027
+ const workflowPath = path34.join(workflowDir, "kody.yml");
14028
+ if (fs36.existsSync(workflowPath) && !force) {
13898
14029
  skipped.push(".github/workflows/kody.yml");
13899
14030
  } else {
13900
- fs35.mkdirSync(workflowDir, { recursive: true });
13901
- fs35.writeFileSync(workflowPath, WORKFLOW_TEMPLATE);
14031
+ fs36.mkdirSync(workflowDir, { recursive: true });
14032
+ fs36.writeFileSync(workflowPath, loadKodyWorkflowTemplate());
13902
14033
  wrote.push(".github/workflows/kody.yml");
13903
14034
  }
13904
14035
  for (const exe of listImplementations()) {
@@ -13909,12 +14040,12 @@ function performInit(cwd, force) {
13909
14040
  continue;
13910
14041
  }
13911
14042
  if (profile.kind !== "scheduled" || !profile.schedule) continue;
13912
- const target = path33.join(workflowDir, `kody-${exe.name}.yml`);
13913
- if (fs35.existsSync(target) && !force) {
14043
+ const target = path34.join(workflowDir, `kody-${exe.name}.yml`);
14044
+ if (fs36.existsSync(target) && !force) {
13914
14045
  skipped.push(`.github/workflows/kody-${exe.name}.yml`);
13915
14046
  continue;
13916
14047
  }
13917
- fs35.writeFileSync(target, renderScheduledWorkflow(exe.name, profile.schedule));
14048
+ fs36.writeFileSync(target, renderScheduledWorkflow(exe.name, profile.schedule));
13918
14049
  wrote.push(`.github/workflows/kody-${exe.name}.yml`);
13919
14050
  }
13920
14051
  let labels;
@@ -13960,7 +14091,7 @@ jobs:
13960
14091
  run: npx -y -p @kody-ade/kody-engine@latest kody-engine implementation ${name}
13961
14092
  `;
13962
14093
  }
13963
- var WORKFLOW_TEMPLATE, initFlow;
14094
+ var initFlow;
13964
14095
  var init_initFlow = __esm({
13965
14096
  "src/scripts/initFlow.ts"() {
13966
14097
  "use strict";
@@ -13968,67 +14099,7 @@ var init_initFlow = __esm({
13968
14099
  init_lifecycleLabels();
13969
14100
  init_profile();
13970
14101
  init_registry();
13971
- WORKFLOW_TEMPLATE = `# Drop this file at .github/workflows/kody.yml in your repo.
13972
- #
13973
- # Triggers: @kody comment on an issue or PR, or manual workflow_dispatch.
13974
- # Everything else (install deps, set up LiteLLM, run the agent, open the PR)
13975
- # is handled inside the @kody-ade/kody-engine package.
13976
- #
13977
- # Required repo secrets: at least one model provider key (e.g. MINIMAX_API_KEY,
13978
- # ANTHROPIC_API_KEY). kody reads any *_API_KEY secret automatically via
13979
- # toJSON(secrets) \u2014 no need to list them here.
13980
- #
13981
- # Recommended: KODY_TOKEN secret \u2014 a PAT or GitHub App token with repo
13982
- # scope so kody's pushes trigger downstream CI and PR-body edits succeed.
13983
-
13984
- name: kody
13985
-
13986
- on:
13987
- workflow_dispatch:
13988
- inputs:
13989
- issue_number:
13990
- description: "GitHub issue number"
13991
- required: true
13992
- type: string
13993
- capability:
13994
- description: "Capability action to run (default: run)"
13995
- required: false
13996
- type: string
13997
- default: ""
13998
- issue_comment:
13999
- types: [created]
14000
-
14001
- jobs:
14002
- run:
14003
- if: >-
14004
- \${{ github.event_name == 'workflow_dispatch' ||
14005
- (github.event_name == 'issue_comment' &&
14006
- contains(github.event.comment.body, '@kody')) }}
14007
- runs-on: ubuntu-latest
14008
- timeout-minutes: 60
14009
- permissions:
14010
- issues: write
14011
- pull-requests: write
14012
- contents: write
14013
- actions: read
14014
- steps:
14015
- - uses: actions/checkout@v4
14016
- with:
14017
- fetch-depth: 0
14018
- token: \${{ secrets.KODY_TOKEN || github.token }}
14019
-
14020
- - uses: actions/setup-node@v4
14021
- with:
14022
- node-version: 22
14023
-
14024
- - uses: actions/setup-python@v5
14025
- with:
14026
- python-version: "3.12"
14027
-
14028
- - env:
14029
- ALL_SECRETS: \${{ toJSON(secrets) }}
14030
- run: npx -y -p @kody-ade/kody-engine@latest kody-engine ci
14031
- `;
14102
+ init_workflow_template();
14032
14103
  initFlow = async (ctx) => {
14033
14104
  const force = ctx.args.force === true;
14034
14105
  const cwd = ctx.cwd;
@@ -14062,7 +14133,7 @@ Nothing to do. All files already present. (Use --force to overwrite.)
14062
14133
  });
14063
14134
 
14064
14135
  // src/scripts/loadAgentAdhoc.ts
14065
- import * as fs36 from "fs";
14136
+ import * as fs37 from "fs";
14066
14137
  function resolveMessage(messageArg) {
14067
14138
  const fromComment = readCommentBody();
14068
14139
  if (fromComment) return stripDirective(fromComment);
@@ -14070,9 +14141,9 @@ function resolveMessage(messageArg) {
14070
14141
  }
14071
14142
  function readCommentBody() {
14072
14143
  const eventPath = process.env.GITHUB_EVENT_PATH;
14073
- if (!eventPath || !fs36.existsSync(eventPath)) return "";
14144
+ if (!eventPath || !fs37.existsSync(eventPath)) return "";
14074
14145
  try {
14075
- const event = JSON.parse(fs36.readFileSync(eventPath, "utf-8"));
14146
+ const event = JSON.parse(fs37.readFileSync(eventPath, "utf-8"));
14076
14147
  return String(event.comment?.body ?? "");
14077
14148
  } catch {
14078
14149
  return "";
@@ -14126,10 +14197,10 @@ var init_loadAgentAdhoc = __esm({
14126
14197
  throw new Error("loadAgentAdhoc: ctx.args.agent must be a non-empty slug");
14127
14198
  }
14128
14199
  const agentPath = resolveAgentFile(ctx.cwd, agentSlug, agentsDir);
14129
- if (!fs36.existsSync(agentPath)) {
14200
+ if (!fs37.existsSync(agentPath)) {
14130
14201
  throw new Error(`loadAgentAdhoc: agent identity not found: ${agentPath}`);
14131
14202
  }
14132
- const { title, body } = parseAgentFile(fs36.readFileSync(agentPath, "utf-8"), agentSlug);
14203
+ const { title, body } = parseAgentFile(fs37.readFileSync(agentPath, "utf-8"), agentSlug);
14133
14204
  const message = resolveMessage(ctx.args.message);
14134
14205
  if (!message) {
14135
14206
  throw new Error(
@@ -14201,13 +14272,13 @@ var init_loadCapabilityState = __esm({
14201
14272
  function isCompanyIntentId(value) {
14202
14273
  return SLUG_RE.test(value);
14203
14274
  }
14204
- function normalizeCompanyIntent(path51, raw) {
14275
+ function normalizeCompanyIntent(path52, raw) {
14205
14276
  if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
14206
- throw new Error(`${path51}: intent must be JSON object`);
14277
+ throw new Error(`${path52}: intent must be JSON object`);
14207
14278
  }
14208
14279
  const input = raw;
14209
14280
  const id = stringField4(input.id);
14210
- if (!id || !isCompanyIntentId(id)) throw new Error(`${path51}: invalid intent id`);
14281
+ if (!id || !isCompanyIntentId(id)) throw new Error(`${path52}: invalid intent id`);
14211
14282
  const createdAt = stringField4(input.createdAt) || nowIso();
14212
14283
  const updatedAt = stringField4(input.updatedAt) || createdAt;
14213
14284
  const description = stringField4(input.description);
@@ -14369,7 +14440,7 @@ function retryDelaysMs() {
14369
14440
  }
14370
14441
  function sleep(ms) {
14371
14442
  if (ms <= 0) return Promise.resolve();
14372
- return new Promise((resolve16) => setTimeout(resolve16, ms));
14443
+ return new Promise((resolve17) => setTimeout(resolve17, ms));
14373
14444
  }
14374
14445
  async function fetchGoalStateWithRetry(config, goalId, cwd) {
14375
14446
  let state = await fetchGoalStateAsync(config, goalId, cwd);
@@ -14498,8 +14569,8 @@ var init_loadIssueStateComment = __esm({
14498
14569
  });
14499
14570
 
14500
14571
  // src/scripts/loadJobFromFile.ts
14501
- import * as fs37 from "fs";
14502
- import * as path34 from "path";
14572
+ import * as fs38 from "fs";
14573
+ import * as path35 from "path";
14503
14574
  function parseJobFile(raw, slug) {
14504
14575
  let stripped = raw;
14505
14576
  if (stripped.startsWith("---\n")) {
@@ -14538,10 +14609,10 @@ var init_loadJobFromFile = __esm({
14538
14609
  if (!slug) {
14539
14610
  throw new Error(`loadJobFromFile: ctx.args.${slugArg} must be a non-empty slug`);
14540
14611
  }
14541
- const capability = resolveCapabilityFolder(slug, path34.resolve(ctx.cwd, jobsDir));
14612
+ const capability = resolveCapabilityFolder(slug, path35.resolve(ctx.cwd, jobsDir));
14542
14613
  if (!capability) {
14543
14614
  throw new Error(
14544
- `loadJobFromFile: capability folder not found or incomplete: ${path34.resolve(ctx.cwd, jobsDir, slug)}`
14615
+ `loadJobFromFile: capability folder not found or incomplete: ${path35.resolve(ctx.cwd, jobsDir, slug)}`
14545
14616
  );
14546
14617
  }
14547
14618
  const { title, body, config } = capability;
@@ -14551,12 +14622,12 @@ var init_loadJobFromFile = __esm({
14551
14622
  let agentIdentity = "";
14552
14623
  if (agentSlug) {
14553
14624
  const agentPath = resolveAgentFile(ctx.cwd, agentSlug, agentsDir);
14554
- if (!fs37.existsSync(agentPath)) {
14625
+ if (!fs38.existsSync(agentPath)) {
14555
14626
  throw new Error(
14556
14627
  `loadJobFromFile: capability '${slug}' declares agent '${agentSlug}' but ${agentPath} does not exist`
14557
14628
  );
14558
14629
  }
14559
- const agentRaw = fs37.readFileSync(agentPath, "utf-8");
14630
+ const agentRaw = fs38.readFileSync(agentPath, "utf-8");
14560
14631
  const parsed = parseJobFile(agentRaw, agentSlug);
14561
14632
  agentTitle = parsed.title;
14562
14633
  agentIdentity = parsed.body;
@@ -14636,13 +14707,13 @@ ${truncate2(issue2.body, FINDING_BODY_MAX_BYTES)}`;
14636
14707
  });
14637
14708
 
14638
14709
  // src/scripts/kodyVariables.ts
14639
- import * as fs38 from "fs";
14640
- import * as path35 from "path";
14710
+ import * as fs39 from "fs";
14711
+ import * as path36 from "path";
14641
14712
  function readKodyVariables(cwd) {
14642
- const full = path35.join(cwd, KODY_VARIABLES_REL_PATH);
14713
+ const full = path36.join(cwd, KODY_VARIABLES_REL_PATH);
14643
14714
  let raw;
14644
14715
  try {
14645
- raw = fs38.readFileSync(full, "utf-8");
14716
+ raw = fs39.readFileSync(full, "utf-8");
14646
14717
  } catch {
14647
14718
  return {};
14648
14719
  }
@@ -14773,6 +14844,18 @@ function envSecret(name, env) {
14773
14844
  }
14774
14845
  async function resolveRuntimeSecret(name, ctx, opts = {}) {
14775
14846
  const env = opts.env ?? process.env;
14847
+ if (hasGitHubActionsIdentity(env)) {
14848
+ try {
14849
+ const value = await readRuntimeSecretFromKody(name, env);
14850
+ return value ? { value, source: "vault" } : envSecret(name, env);
14851
+ } catch (err) {
14852
+ const fallback = envSecret(name, env);
14853
+ return {
14854
+ ...fallback,
14855
+ warning: `Kody secret read failed for ${name}: ${err instanceof Error ? err.message : String(err)}`
14856
+ };
14857
+ }
14858
+ }
14776
14859
  const masterRaw = env.KODY_MASTER_KEY?.trim() ?? "";
14777
14860
  if (!masterRaw || !env.CONVEX_URL?.trim() || !env.KODY_SERVICE_KEY?.trim()) return envSecret(name, env);
14778
14861
  try {
@@ -14800,13 +14883,14 @@ var init_runtimeSecrets = __esm({
14800
14883
  "src/scripts/runtimeSecrets.ts"() {
14801
14884
  "use strict";
14802
14885
  init_backendVault();
14886
+ init_kody_api_client();
14803
14887
  init_keys();
14804
14888
  }
14805
14889
  });
14806
14890
 
14807
14891
  // src/scripts/loadQaContext.ts
14808
- import * as fs39 from "fs";
14809
- import * as path36 from "path";
14892
+ import * as fs40 from "fs";
14893
+ import * as path37 from "path";
14810
14894
  function parseSlugList(value) {
14811
14895
  const inner = value.startsWith("[") && value.endsWith("]") ? value.slice(1, -1) : value;
14812
14896
  return inner.split(",").map(
@@ -14835,18 +14919,18 @@ function readProfileAgents(raw) {
14835
14919
  return { agent: agent ?? legacy ?? ["kody"], body };
14836
14920
  }
14837
14921
  function readProfile(cwd) {
14838
- const dir = path36.join(cwd, CONTEXT_DIR_REL_PATH);
14839
- if (!fs39.existsSync(dir)) return "";
14922
+ const dir = path37.join(cwd, CONTEXT_DIR_REL_PATH);
14923
+ if (!fs40.existsSync(dir)) return "";
14840
14924
  let entries;
14841
14925
  try {
14842
- entries = fs39.readdirSync(dir).filter((f) => f.endsWith(".md")).sort();
14926
+ entries = fs40.readdirSync(dir).filter((f) => f.endsWith(".md")).sort();
14843
14927
  } catch {
14844
14928
  return "";
14845
14929
  }
14846
14930
  const blocks = [];
14847
14931
  for (const file of entries) {
14848
14932
  try {
14849
- const raw = fs39.readFileSync(path36.join(dir, file), "utf-8");
14933
+ const raw = fs40.readFileSync(path37.join(dir, file), "utf-8");
14850
14934
  const { agent, body } = readProfileAgents(raw);
14851
14935
  if (!agent.includes(QA_AGENT) && !agent.includes(ALL_AGENTS)) continue;
14852
14936
  blocks.push(`## ${file}
@@ -14895,8 +14979,8 @@ var init_loadQaContext = __esm({
14895
14979
  });
14896
14980
 
14897
14981
  // src/taskContext.ts
14898
- import * as fs40 from "fs";
14899
- import * as path37 from "path";
14982
+ import * as fs41 from "fs";
14983
+ import * as path38 from "path";
14900
14984
  function buildTaskContext(args) {
14901
14985
  return {
14902
14986
  schemaVersion: TASK_CONTEXT_SCHEMA_VERSION,
@@ -14912,9 +14996,9 @@ function buildTaskContext(args) {
14912
14996
  function persistTaskContext(cwd, ctx) {
14913
14997
  try {
14914
14998
  const dir = runtimeStatePath(cwd, "agent-runs", ctx.runId);
14915
- fs40.mkdirSync(dir, { recursive: true });
14916
- const file = path37.join(dir, "task-context.json");
14917
- fs40.writeFileSync(file, `${JSON.stringify(ctx, null, 2)}
14999
+ fs41.mkdirSync(dir, { recursive: true });
15000
+ const file = path38.join(dir, "task-context.json");
15001
+ fs41.writeFileSync(file, `${JSON.stringify(ctx, null, 2)}
14918
15002
  `);
14919
15003
  return file;
14920
15004
  } catch (err) {
@@ -15341,19 +15425,19 @@ function parseAgencyModelProposal(raw) {
15341
15425
  function normalizeBundleFiles(bundle) {
15342
15426
  const seen = /* @__PURE__ */ new Set();
15343
15427
  return bundle.files.map((file, index) => {
15344
- const path51 = file.path.replace(/^\/+/, "");
15345
- const parts = path51.split("/");
15346
- if (!path51 || file.path.startsWith("/") || file.path.includes("\\") || parts.some((part) => !part || part === "." || part === "..")) {
15428
+ const path52 = file.path.replace(/^\/+/, "");
15429
+ const parts = path52.split("/");
15430
+ if (!path52 || file.path.startsWith("/") || file.path.includes("\\") || parts.some((part) => !part || part === "." || part === "..")) {
15347
15431
  throw new Error(`openAgencyModelReviewPr: files[${index}].path is unsafe`);
15348
15432
  }
15349
15433
  if (!/^(agents\/[^/]+\.md|capabilities\/[^/]+\/.+|goals\/(?:templates\/)?[^/]+\/.+|workflows\/[^/]+\.json)$/.test(
15350
- path51
15434
+ path52
15351
15435
  )) {
15352
15436
  throw new Error(`openAgencyModelReviewPr: files[${index}].path is not a supported definition path`);
15353
15437
  }
15354
- if (seen.has(path51)) throw new Error(`openAgencyModelReviewPr: duplicate generated file path: ${path51}`);
15355
- seen.add(path51);
15356
- return { path: path51, content: file.content.replace(/\r\n?/g, "\n") };
15438
+ if (seen.has(path52)) throw new Error(`openAgencyModelReviewPr: duplicate generated file path: ${path52}`);
15439
+ seen.add(path52);
15440
+ return { path: path52, content: file.content.replace(/\r\n?/g, "\n") };
15357
15441
  });
15358
15442
  }
15359
15443
  function buildProposalId(issueNumber, bundle, sourceLabel) {
@@ -16297,9 +16381,9 @@ var init_postResearchComment = __esm({
16297
16381
  });
16298
16382
 
16299
16383
  // src/scripts/prepareBrowserAuth.ts
16300
- import * as fs41 from "fs";
16384
+ import * as fs42 from "fs";
16301
16385
  import * as os6 from "os";
16302
- import * as path38 from "path";
16386
+ import * as path39 from "path";
16303
16387
  function appendAuthMessage(ctx, message) {
16304
16388
  const current = typeof ctx.data.qaAuthBlock === "string" ? ctx.data.qaAuthBlock.trim() : "";
16305
16389
  ctx.data.qaAuthBlock = current ? `${current}
@@ -16338,9 +16422,9 @@ async function githubJson(url, token) {
16338
16422
  return await response.json();
16339
16423
  }
16340
16424
  function writeKodyStorageState(input) {
16341
- const directory = fs41.mkdtempSync(path38.join(os6.tmpdir(), "kody-browser-auth-"));
16342
- fs41.chmodSync(directory, 448);
16343
- const file = path38.join(directory, "storage-state.json");
16425
+ const directory = fs42.mkdtempSync(path39.join(os6.tmpdir(), "kody-browser-auth-"));
16426
+ fs42.chmodSync(directory, 448);
16427
+ const file = path39.join(directory, "storage-state.json");
16344
16428
  const now = Date.now();
16345
16429
  const repoEntry = {
16346
16430
  repoUrl: input.repoUrl,
@@ -16370,7 +16454,7 @@ function writeKodyStorageState(input) {
16370
16454
  }
16371
16455
  ]
16372
16456
  };
16373
- fs41.writeFileSync(file, JSON.stringify(storageState), { mode: 384 });
16457
+ fs42.writeFileSync(file, JSON.stringify(storageState), { mode: 384 });
16374
16458
  return { directory, file };
16375
16459
  }
16376
16460
  function configurePlaywright(profile, storageStatePath) {
@@ -16452,7 +16536,7 @@ async function prepareMethod(ctx, profile, method) {
16452
16536
  configurePlaywright(profile, state.file);
16453
16537
  const authDirectory = state.directory;
16454
16538
  registerRuntimeCleanup(ctx, () => {
16455
- fs41.rmSync(authDirectory, { recursive: true, force: true });
16539
+ fs42.rmSync(authDirectory, { recursive: true, force: true });
16456
16540
  });
16457
16541
  appendAuthMessage(
16458
16542
  ctx,
@@ -16460,7 +16544,7 @@ async function prepareMethod(ctx, profile, method) {
16460
16544
  );
16461
16545
  return true;
16462
16546
  } catch (error) {
16463
- if (state) fs41.rmSync(state.directory, { recursive: true, force: true });
16547
+ if (state) fs42.rmSync(state.directory, { recursive: true, force: true });
16464
16548
  const reason = error instanceof Error ? error.message : String(error);
16465
16549
  appendAuthMessage(
16466
16550
  ctx,
@@ -16573,9 +16657,9 @@ function latestResult(raw, agentResult) {
16573
16657
  function recordField4(value) {
16574
16658
  return value && typeof value === "object" && !Array.isArray(value) ? value : null;
16575
16659
  }
16576
- function resolveDotted(root, path51) {
16577
- if (!path51) return void 0;
16578
- return path51.split(".").reduce((value, key) => recordField4(value)?.[key], root);
16660
+ function resolveDotted(root, path52) {
16661
+ if (!path52) return void 0;
16662
+ return path52.split(".").reduce((value, key) => recordField4(value)?.[key], root);
16579
16663
  }
16580
16664
  function stringValue4(value) {
16581
16665
  return typeof value === "string" && value.trim() ? value.trim() : null;
@@ -16618,7 +16702,7 @@ var init_publishReport = __esm({
16618
16702
  });
16619
16703
  const runId = generatedAt.replace(/\.\d{3}Z$/, "Z").replace(/:/g, "-");
16620
16704
  const tenantId2 = ctx.config.github?.owner && ctx.config.github.repo ? `${ctx.config.github.owner}/${ctx.config.github.repo}` : process.env.GITHUB_REPOSITORY;
16621
- if (process.env.CONVEX_URL?.trim() && process.env.KODY_SERVICE_KEY?.trim() && tenantId2) {
16705
+ if (hasStateBackendConfig() && tenantId2) {
16622
16706
  await createStateBackendFromEnv().saveReport(
16623
16707
  tenantId2,
16624
16708
  slug,
@@ -16634,7 +16718,7 @@ var init_publishReport = __esm({
16634
16718
  generatedAt
16635
16719
  );
16636
16720
  } else if (process.env.GITHUB_ACTIONS === "true") {
16637
- throw new Error("Convex backend is required for reports in GitHub Actions");
16721
+ throw new Error("Kody backend access is required for reports in GitHub Actions");
16638
16722
  }
16639
16723
  };
16640
16724
  }
@@ -17447,53 +17531,14 @@ function basePreviewAppName(repo) {
17447
17531
  }
17448
17532
  return `kp-${shortHash(owner)}-${shortHash(name)}-base`;
17449
17533
  }
17450
- function decryptVaultPayload(payload, keyRaw) {
17451
- const parts = payload.split(":");
17452
- if (parts.length !== 4 || parts[0] !== "v1") {
17453
- throw new Error("invalid vault payload format");
17454
- }
17455
- const [, ivB64, ctB64, tagB64] = parts;
17456
- const key = decodeMasterKey(keyRaw);
17457
- if (key.length !== 32) {
17458
- throw new Error("KODY_MASTER_KEY must decode to 32 bytes");
17459
- }
17460
- const iv = Buffer.from(ivB64, "base64");
17461
- const ct = Buffer.from(ctB64, "base64");
17462
- const tag = Buffer.from(tagB64, "base64");
17463
- const decipher = createDecipheriv2("aes-256-gcm", key, iv);
17464
- decipher.setAuthTag(tag);
17465
- return Buffer.concat([decipher.update(ct), decipher.final()]).toString("utf8");
17466
- }
17467
- function decodeMasterKey(keyRaw) {
17468
- if (/^[0-9a-fA-F]{64}$/.test(keyRaw)) return Buffer.from(keyRaw, "hex");
17469
- return Buffer.from(keyRaw.replace(/-/g, "+").replace(/_/g, "/"), "base64");
17470
- }
17471
- function derivePreviewVerifyKey(masterKeyRaw) {
17472
- const masterKey = decodeMasterKey(masterKeyRaw);
17473
- if (masterKey.length !== 32) {
17474
- throw new Error("KODY_MASTER_KEY must decode to 32 bytes");
17475
- }
17476
- return Buffer.from(hkdfSync2("sha256", masterKey, Buffer.alloc(0), PREVIEW_KEY_INFO, 32)).toString("hex");
17477
- }
17478
17534
  function previewRuntimeEnv(args) {
17479
17535
  return {
17480
17536
  ...args.buildEnv,
17481
- KODY_PREVIEW_VERIFY_KEY: derivePreviewVerifyKey(args.masterKey),
17537
+ KODY_PREVIEW_VERIFY_KEY: args.previewVerifyKey,
17482
17538
  KODY_REPO_CONTEXT: args.repo,
17483
17539
  KODY_PR: String(args.pr)
17484
17540
  };
17485
17541
  }
17486
- function buildEnvFromVault(doc) {
17487
- const buildEnv = {};
17488
- for (const [name, entry] of Object.entries(doc.secrets ?? {})) {
17489
- if (!entry?.value) continue;
17490
- if (NEVER_PASS_TO_BUILD.has(name)) continue;
17491
- buildEnv[name] = entry.value;
17492
- }
17493
- const raw = doc.secrets?.KODY_PREVIEW_BUILD_MODE?.value;
17494
- const buildMode = raw?.toLowerCase().trim() === "dev" ? "dev" : "prod";
17495
- return { buildEnv, buildMode };
17496
- }
17497
17542
  function formatPreviewComment(args) {
17498
17543
  return [
17499
17544
  "<!-- kody-fly-preview -->",
@@ -17505,30 +17550,16 @@ function formatPreviewComment(args) {
17505
17550
  function defaultImageTag(repo, ref) {
17506
17551
  return createHash4("sha256").update(`${repo}@${ref}`).digest("hex").slice(0, 12);
17507
17552
  }
17508
- var NEVER_PASS_TO_BUILD, PREVIEW_KEY_INFO;
17509
17553
  var init_previewBuildHelpers = __esm({
17510
17554
  "src/scripts/previewBuildHelpers.ts"() {
17511
17555
  "use strict";
17512
- NEVER_PASS_TO_BUILD = /* @__PURE__ */ new Set([
17513
- "FLY_API_TOKEN",
17514
- "FLY_ORG_SLUG",
17515
- "FLY_DEFAULT_REGION",
17516
- "KODY_MASTER_KEY",
17517
- // Preview-config knob; consumed by the dispatcher before spawn.
17518
- "KODY_PREVIEW_BUILD_MODE",
17519
- "KODY_PREVIEW_VERIFY_KEY",
17520
- "KODY_REPO_CONTEXT",
17521
- "KODY_PR",
17522
- "KODY_BRANCH"
17523
- ]);
17524
- PREVIEW_KEY_INFO = "kody-preview:v1";
17525
17556
  }
17526
17557
  });
17527
17558
 
17528
17559
  // src/scripts/previewBuildRun.ts
17529
17560
  import { spawn as spawn5 } from "child_process";
17530
17561
  async function runCmd(cmd, args, opts = {}) {
17531
- await new Promise((resolve16, reject) => {
17562
+ await new Promise((resolve17, reject) => {
17532
17563
  const child = spawn5(cmd, args, {
17533
17564
  cwd: opts.cwd,
17534
17565
  env: { ...process.env, ...opts.env ?? {} },
@@ -17540,7 +17571,7 @@ async function runCmd(cmd, args, opts = {}) {
17540
17571
  }
17541
17572
  child.on("error", reject);
17542
17573
  child.on("close", (code) => {
17543
- if (code === 0) resolve16();
17574
+ if (code === 0) resolve17();
17544
17575
  else reject(new Error(`${cmd} ${args.join(" ")} exited ${code}`));
17545
17576
  });
17546
17577
  });
@@ -17612,12 +17643,12 @@ fi
17612
17643
 
17613
17644
  // src/scripts/runPreviewBuild.ts
17614
17645
  import { copyFile, writeFile } from "fs/promises";
17615
- import * as path39 from "path";
17616
- import { fileURLToPath } from "url";
17646
+ import * as path40 from "path";
17647
+ import { fileURLToPath as fileURLToPath2 } from "url";
17617
17648
  function bundledDockerfilePath(mode) {
17618
- const here = path39.dirname(fileURLToPath(import.meta.url));
17649
+ const here = path40.dirname(fileURLToPath2(import.meta.url));
17619
17650
  const file = mode === "dev" ? "default-Dockerfile.preview.dev" : "default-Dockerfile.preview.prod";
17620
- return path39.join(here, "preview-build-templates", file);
17651
+ return path40.join(here, "preview-build-templates", file);
17621
17652
  }
17622
17653
  function required(name) {
17623
17654
  const v = (process.env[name] ?? "").trim();
@@ -17630,16 +17661,6 @@ function flyHeaders(token) {
17630
17661
  "Content-Type": "application/json"
17631
17662
  };
17632
17663
  }
17633
- async function fetchVaultDoc(repo, masterKey) {
17634
- const [owner, name] = repo.split("/", 2);
17635
- if (!owner || !name) throw new Error(`invalid GITHUB_REPOSITORY "${repo}"`);
17636
- const record2 = await createStateBackendFromEnv().getRepoDoc(`${owner}/${name}`, "secrets.enc");
17637
- const raw = record2?.doc;
17638
- const payload = raw && typeof raw === "object" && !Array.isArray(raw) && typeof raw.ciphertext === "string" ? raw.ciphertext : "";
17639
- if (!payload) throw new Error("backend vault is empty \u2014 save secrets from the dashboard first");
17640
- const plaintext = decryptVaultPayload(payload, masterKey);
17641
- return JSON.parse(plaintext);
17642
- }
17643
17664
  async function flyAppExists(name, token) {
17644
17665
  const res = await fetch(`${FLY_MACHINES}/apps/${encodeURIComponent(name)}`, {
17645
17666
  headers: flyHeaders(token),
@@ -17814,7 +17835,7 @@ var FLY_MACHINES, FLY_GRAPHQL, REQ_TIMEOUT_MS2, runPreviewBuild;
17814
17835
  var init_runPreviewBuild = __esm({
17815
17836
  "src/scripts/runPreviewBuild.ts"() {
17816
17837
  "use strict";
17817
- init_state_backend();
17838
+ init_kody_api_client();
17818
17839
  init_previewBuildHelpers();
17819
17840
  init_previewBuildNamespace();
17820
17841
  init_previewBuildRun();
@@ -17831,12 +17852,10 @@ var init_runPreviewBuild = __esm({
17831
17852
  }
17832
17853
  let repo;
17833
17854
  let ref;
17834
- let masterKey;
17835
17855
  let ghToken3;
17836
17856
  try {
17837
17857
  repo = required("GITHUB_REPOSITORY");
17838
17858
  ref = required("GITHUB_SHA");
17839
- masterKey = required("KODY_MASTER_KEY");
17840
17859
  ghToken3 = (process.env.KODY_TOKEN ?? process.env.GH_TOKEN ?? process.env.GITHUB_TOKEN ?? process.env.GH_PAT ?? "").trim();
17841
17860
  if (!ghToken3) {
17842
17861
  throw new Error("GitHub auth token missing (KODY_TOKEN / GH_TOKEN / GITHUB_TOKEN / GH_PAT all empty)");
@@ -17850,24 +17869,24 @@ var init_runPreviewBuild = __esm({
17850
17869
  const appName = previewAppName(repo, pr);
17851
17870
  const tag = defaultImageTag(repo, ref);
17852
17871
  try {
17853
- const doc = await fetchVaultDoc(repo, masterKey);
17854
- const { buildEnv, buildMode } = buildEnvFromVault(doc);
17855
- const flyToken = doc.secrets?.FLY_API_TOKEN?.value?.trim();
17872
+ const previewContext = await readPreviewContextFromKody();
17873
+ const { buildEnv, buildMode } = previewContext;
17874
+ const flyToken = previewContext.flyApiToken?.trim();
17856
17875
  if (!flyToken) {
17857
17876
  ctx.output.exitCode = 99;
17858
17877
  ctx.output.reason = "runPreviewBuild: vault has no FLY_API_TOKEN \u2014 add it via the dashboard's /secrets page";
17859
17878
  return;
17860
17879
  }
17861
- const orgSlug = doc.secrets?.FLY_ORG_SLUG?.value?.trim() || (process.env.FLY_ORG_SLUG ?? "personal").trim();
17862
- const region = doc.secrets?.FLY_DEFAULT_REGION?.value?.trim() || (process.env.FLY_REGION ?? "fra").trim();
17863
- const nscTenantId = doc.secrets?.NSC_TENANT_ID?.value?.trim() || "";
17880
+ const orgSlug = previewContext.flyOrgSlug?.trim() || (process.env.FLY_ORG_SLUG ?? "personal").trim();
17881
+ const region = previewContext.flyRegion?.trim() || (process.env.FLY_REGION ?? "fra").trim();
17882
+ const nscTenantId = previewContext.namespaceTenantId?.trim() || "";
17864
17883
  console.log(`[preview-build] vault: ${Object.keys(buildEnv).length} secrets, mode=${buildMode}`);
17865
17884
  if (Object.keys(buildEnv).length > 0) {
17866
17885
  const lines = Object.entries(buildEnv).map(([k, v]) => `${k}=${JSON.stringify(v)}`);
17867
- await writeFile(path39.join(ctx.cwd, ".env.production.local"), `${lines.join("\n")}
17886
+ await writeFile(path40.join(ctx.cwd, ".env.production.local"), `${lines.join("\n")}
17868
17887
  `, "utf8");
17869
17888
  }
17870
- const consumerDockerfile = path39.join(ctx.cwd, "Dockerfile.preview");
17889
+ const consumerDockerfile = path40.join(ctx.cwd, "Dockerfile.preview");
17871
17890
  const { stat } = await import("fs/promises");
17872
17891
  let hasConsumerDockerfile = false;
17873
17892
  try {
@@ -17942,7 +17961,7 @@ var init_runPreviewBuild = __esm({
17942
17961
  appName,
17943
17962
  region,
17944
17963
  image: `registry.fly.io/${appName}:${tag}`,
17945
- env: previewRuntimeEnv({ buildEnv, masterKey, pr, repo })
17964
+ env: previewRuntimeEnv({ buildEnv, previewVerifyKey: previewContext.previewVerifyKey, pr, repo })
17946
17965
  },
17947
17966
  flyToken
17948
17967
  );
@@ -18051,8 +18070,8 @@ var init_tickShellRunner = __esm({
18051
18070
  });
18052
18071
 
18053
18072
  // src/scripts/runScheduledImplementationTick.ts
18054
- import * as fs42 from "fs";
18055
- import * as path40 from "path";
18073
+ import * as fs43 from "fs";
18074
+ import * as path41 from "path";
18056
18075
  var runScheduledImplementationTick;
18057
18076
  var init_runScheduledImplementationTick = __esm({
18058
18077
  "src/scripts/runScheduledImplementationTick.ts"() {
@@ -18073,14 +18092,14 @@ var init_runScheduledImplementationTick = __esm({
18073
18092
  ctx.output.reason = `runScheduledImplementationTick: args.slug or ctx.args.${slugArg} must be non-empty capability slug`;
18074
18093
  return;
18075
18094
  }
18076
- const capability = resolveCapabilityFolder(slug, path40.resolve(ctx.cwd, jobsDir));
18095
+ const capability = resolveCapabilityFolder(slug, path41.resolve(ctx.cwd, jobsDir));
18077
18096
  if (!capability) {
18078
18097
  ctx.output.exitCode = 99;
18079
18098
  ctx.output.reason = `runScheduledImplementationTick: capability folder not found or incomplete: ${slug} (searched ${jobsDir} and company store)`;
18080
18099
  return;
18081
18100
  }
18082
- const shellPath = path40.join(profile.dir, shell);
18083
- if (!fs42.existsSync(shellPath)) {
18101
+ const shellPath = path41.join(profile.dir, shell);
18102
+ if (!fs43.existsSync(shellPath)) {
18084
18103
  ctx.output.exitCode = 99;
18085
18104
  ctx.output.reason = `runScheduledImplementationTick: shell not found: ${shell} (looked in ${profile.dir})`;
18086
18105
  return;
@@ -18111,8 +18130,8 @@ var init_runScheduledImplementationTick = __esm({
18111
18130
  });
18112
18131
 
18113
18132
  // src/scripts/runTickScript.ts
18114
- import * as fs43 from "fs";
18115
- import * as path41 from "path";
18133
+ import * as fs44 from "fs";
18134
+ import * as path42 from "path";
18116
18135
  var runTickScript;
18117
18136
  var init_runTickScript = __esm({
18118
18137
  "src/scripts/runTickScript.ts"() {
@@ -18132,10 +18151,10 @@ var init_runTickScript = __esm({
18132
18151
  ctx.output.reason = `runTickScript: ctx.args.${slugArg} must be a non-empty slug`;
18133
18152
  return;
18134
18153
  }
18135
- const capability = readCapabilityFolder(path41.resolve(ctx.cwd, jobsDir), slug);
18154
+ const capability = readCapabilityFolder(path42.resolve(ctx.cwd, jobsDir), slug);
18136
18155
  if (!capability) {
18137
18156
  ctx.output.exitCode = 99;
18138
- ctx.output.reason = `runTickScript: capability folder not found or incomplete: ${path41.resolve(ctx.cwd, jobsDir, slug)}`;
18157
+ ctx.output.reason = `runTickScript: capability folder not found or incomplete: ${path42.resolve(ctx.cwd, jobsDir, slug)}`;
18139
18158
  return;
18140
18159
  }
18141
18160
  const tickScript = capability.config.tickScript;
@@ -18144,8 +18163,8 @@ var init_runTickScript = __esm({
18144
18163
  ctx.output.reason = `runTickScript: capability ${slug} has no \`tickScript\` in profile.json \u2014 route via capability-tick instead`;
18145
18164
  return;
18146
18165
  }
18147
- const scriptPath = path41.isAbsolute(tickScript) ? tickScript : path41.join(ctx.cwd, tickScript);
18148
- if (!fs43.existsSync(scriptPath)) {
18166
+ const scriptPath = path42.isAbsolute(tickScript) ? tickScript : path42.join(ctx.cwd, tickScript);
18167
+ if (!fs44.existsSync(scriptPath)) {
18149
18168
  ctx.output.exitCode = 99;
18150
18169
  ctx.output.reason = `runTickScript: tickScript not found: ${scriptPath}`;
18151
18170
  return;
@@ -18427,7 +18446,7 @@ var init_syncFlow = __esm({
18427
18446
  });
18428
18447
 
18429
18448
  // src/scripts/validateAgencyModelProposal.ts
18430
- import * as path42 from "path";
18449
+ import * as path43 from "path";
18431
18450
  function validateModelBundle(bundle, expectedKind, options = {}) {
18432
18451
  const failures = [];
18433
18452
  validateOneModel(bundle.model, bundle.files, "model", true, failures, expectedKind, options);
@@ -18753,7 +18772,7 @@ var init_validateAgencyModelProposal = __esm({
18753
18772
  const bundle = parseAgencyModelProposal(raw);
18754
18773
  const expectedKind = readExpectedModelKind(args);
18755
18774
  const failures = validateModelBundle(bundle, expectedKind, {
18756
- capabilityRoot: path42.join(ctx.cwd, ".kody", "capabilities")
18775
+ capabilityRoot: path43.join(ctx.cwd, ".kody", "capabilities")
18757
18776
  });
18758
18777
  if (failures.length > 0) {
18759
18778
  throw new Error(`validateAgencyModelProposal: ${failures.join("; ")}`);
@@ -18816,7 +18835,7 @@ function stripAnsi2(s) {
18816
18835
  return s.replace(ANSI_RE2, "");
18817
18836
  }
18818
18837
  function runCommand2(command, cwd) {
18819
- return new Promise((resolve16) => {
18838
+ return new Promise((resolve17) => {
18820
18839
  const child = spawn6(command, {
18821
18840
  cwd,
18822
18841
  shell: true,
@@ -18843,11 +18862,11 @@ function runCommand2(command, cwd) {
18843
18862
  }, TEST_TIMEOUT_MS);
18844
18863
  child.on("exit", (code) => {
18845
18864
  clearTimeout(timer);
18846
- resolve16({ exitCode: code ?? -1, output: Buffer.concat(buffers).toString("utf-8") });
18865
+ resolve17({ exitCode: code ?? -1, output: Buffer.concat(buffers).toString("utf-8") });
18847
18866
  });
18848
18867
  child.on("error", (err) => {
18849
18868
  clearTimeout(timer);
18850
- resolve16({ exitCode: -1, output: err.message });
18869
+ resolve17({ exitCode: -1, output: err.message });
18851
18870
  });
18852
18871
  });
18853
18872
  }
@@ -19253,21 +19272,21 @@ function lineStream(stream) {
19253
19272
  tryDeliver();
19254
19273
  });
19255
19274
  return {
19256
- next: (timeoutMs) => new Promise((resolve16) => {
19275
+ next: (timeoutMs) => new Promise((resolve17) => {
19257
19276
  if (queue.length > 0) {
19258
- resolve16(queue.shift());
19277
+ resolve17(queue.shift());
19259
19278
  return;
19260
19279
  }
19261
19280
  if (ended) {
19262
- resolve16(null);
19281
+ resolve17(null);
19263
19282
  return;
19264
19283
  }
19265
- waiter = resolve16;
19284
+ waiter = resolve17;
19266
19285
  const t = setTimeout(
19267
19286
  () => {
19268
- if (waiter === resolve16) {
19287
+ if (waiter === resolve17) {
19269
19288
  waiter = null;
19270
- resolve16(null);
19289
+ resolve17(null);
19271
19290
  }
19272
19291
  },
19273
19292
  Math.max(0, timeoutMs)
@@ -19304,7 +19323,7 @@ var init_warmupMcp = __esm({
19304
19323
  });
19305
19324
 
19306
19325
  // src/scripts/writeAgentRunSummary.ts
19307
- import * as fs44 from "fs";
19326
+ import * as fs45 from "fs";
19308
19327
  var writeAgentRunSummary;
19309
19328
  var init_writeAgentRunSummary = __esm({
19310
19329
  "src/scripts/writeAgentRunSummary.ts"() {
@@ -19330,7 +19349,7 @@ var init_writeAgentRunSummary = __esm({
19330
19349
  if (reason) lines.push(`- **Reason:** ${reason}`);
19331
19350
  lines.push("");
19332
19351
  try {
19333
- fs44.appendFileSync(summaryPath, `${lines.join("\n")}
19352
+ fs45.appendFileSync(summaryPath, `${lines.join("\n")}
19334
19353
  `);
19335
19354
  } catch {
19336
19355
  }
@@ -19656,17 +19675,17 @@ var init_scripts = __esm({
19656
19675
  });
19657
19676
 
19658
19677
  // src/stateWorkspace.ts
19659
- import * as fs45 from "fs";
19660
- import * as path43 from "path";
19678
+ import * as fs46 from "fs";
19679
+ import * as path44 from "path";
19661
19680
  function tenantId(config) {
19662
19681
  const owner = config.github?.owner?.trim() || process.env.GITHUB_REPOSITORY?.split("/")[0]?.trim();
19663
19682
  const repo = config.github?.repo?.trim() || process.env.GITHUB_REPOSITORY?.split("/")[1]?.trim();
19664
19683
  return owner && repo ? `${owner}/${repo}` : null;
19665
19684
  }
19666
19685
  function writeRuntimeFile(cwd, relativePath, content) {
19667
- const target = path43.join(cwd, RUNTIME_ROOT, relativePath);
19668
- fs45.mkdirSync(path43.dirname(target), { recursive: true });
19669
- fs45.writeFileSync(target, content, "utf8");
19686
+ const target = path44.join(cwd, RUNTIME_ROOT, relativePath);
19687
+ fs46.mkdirSync(path44.dirname(target), { recursive: true });
19688
+ fs46.writeFileSync(target, content, "utf8");
19670
19689
  }
19671
19690
  function record(value) {
19672
19691
  return value && typeof value === "object" && !Array.isArray(value) ? value : null;
@@ -19725,17 +19744,17 @@ async function hydrateWorkflows(backend, tenant, cwd) {
19725
19744
  }
19726
19745
  async function hydrateStateWorkspace(config, cwd, backendOverride) {
19727
19746
  const tenant = tenantId(config);
19728
- const configured = Boolean(process.env.CONVEX_URL?.trim()) && Boolean(process.env.KODY_SERVICE_KEY?.trim());
19747
+ const configured = hasStateBackendConfig();
19729
19748
  if (!tenant || !configured) {
19730
19749
  if (process.env.GITHUB_ACTIONS === "true")
19731
- throw new Error("Convex backend is required for runtime workspace documents");
19750
+ throw new Error("Kody backend access is required for runtime workspace documents");
19732
19751
  return;
19733
19752
  }
19734
- const key = `${path43.resolve(cwd)}|${tenant}`;
19753
+ const key = `${path44.resolve(cwd)}|${tenant}`;
19735
19754
  if (hydratedWorkspaces.has(key)) return;
19736
19755
  const backend = backendOverride ?? createStateBackendFromEnv();
19737
- const root = path43.join(cwd, RUNTIME_ROOT);
19738
- fs45.rmSync(root, { recursive: true, force: true });
19756
+ const root = path44.join(cwd, RUNTIME_ROOT);
19757
+ fs46.rmSync(root, { recursive: true, force: true });
19739
19758
  await Promise.all([
19740
19759
  hydratePrefix(backend, tenant, cwd, "context:"),
19741
19760
  hydratePrefix(backend, tenant, cwd, "memory:"),
@@ -19751,7 +19770,7 @@ var init_stateWorkspace = __esm({
19751
19770
  "src/stateWorkspace.ts"() {
19752
19771
  "use strict";
19753
19772
  init_state_backend();
19754
- RUNTIME_ROOT = path43.join(".kody-engine", "runtime");
19773
+ RUNTIME_ROOT = path44.join(".kody-engine", "runtime");
19755
19774
  hydratedWorkspaces = /* @__PURE__ */ new Set();
19756
19775
  }
19757
19776
  });
@@ -19822,9 +19841,9 @@ var init_tools = __esm({
19822
19841
 
19823
19842
  // src/executor.ts
19824
19843
  import { spawn as spawn8 } from "child_process";
19825
- import * as fs46 from "fs";
19844
+ import * as fs47 from "fs";
19826
19845
  import * as os7 from "os";
19827
- import * as path44 from "path";
19846
+ import * as path45 from "path";
19828
19847
  function isMutatingPostflight(scriptName) {
19829
19848
  return MUTATING_POSTFLIGHTS.has(scriptName ?? "");
19830
19849
  }
@@ -20056,7 +20075,7 @@ async function runImplementation(profileName, input) {
20056
20075
  const jobWhyBlock = typeof ctx.data.jobWhy === "string" ? operatorRequestBlock(ctx.data.jobWhy) : null;
20057
20076
  const jobRefBlock = jobReferenceBlock(profileName, profile, ctx.data);
20058
20077
  const invokeAgent = async (prompt) => {
20059
- const externalPlugins = (profile.claudeCode.plugins ?? []).map((p) => path44.isAbsolute(p) ? p : path44.resolve(profile.dir, p)).filter((p) => p.length > 0);
20078
+ const externalPlugins = (profile.claudeCode.plugins ?? []).map((p) => path45.isAbsolute(p) ? p : path45.resolve(profile.dir, p)).filter((p) => p.length > 0);
20060
20079
  const syntheticPath = ctx.data.syntheticPluginPath;
20061
20080
  const pluginPaths = [...externalPlugins, ...syntheticPath ? [syntheticPath] : []];
20062
20081
  const agents = loadSubagents(profile);
@@ -20494,17 +20513,17 @@ function clearStampedLifecycleLabels(profile, ctx) {
20494
20513
  function resolveProfilePath(profileName) {
20495
20514
  const found = resolveImplementation(profileName);
20496
20515
  if (found) return found;
20497
- const here = path44.dirname(new URL(import.meta.url).pathname);
20516
+ const here = path45.dirname(new URL(import.meta.url).pathname);
20498
20517
  const candidates = [
20499
- path44.join(here, "implementations", profileName, "profile.json"),
20518
+ path45.join(here, "implementations", profileName, "profile.json"),
20500
20519
  // same-dir sibling (dev)
20501
- path44.join(here, "..", "implementations", profileName, "profile.json"),
20520
+ path45.join(here, "..", "implementations", profileName, "profile.json"),
20502
20521
  // up one (prod: dist/bin → dist/implementations)
20503
- path44.join(here, "..", "src", "implementations", profileName, "profile.json")
20522
+ path45.join(here, "..", "src", "implementations", profileName, "profile.json")
20504
20523
  // fallback
20505
20524
  ];
20506
20525
  for (const c of candidates) {
20507
- if (fs46.existsSync(c)) return c;
20526
+ if (fs47.existsSync(c)) return c;
20508
20527
  }
20509
20528
  return candidates[0];
20510
20529
  }
@@ -20619,15 +20638,15 @@ function resolveShellTimeoutMs(entry) {
20619
20638
  }
20620
20639
  async function runShellEntry(entry, ctx, profile) {
20621
20640
  const shellName = entry.shell;
20622
- const shellPath = path44.join(profile.dir, shellName);
20623
- if (!fs46.existsSync(shellPath)) {
20641
+ const shellPath = path45.join(profile.dir, shellName);
20642
+ if (!fs47.existsSync(shellPath)) {
20624
20643
  ctx.skipAgent = true;
20625
20644
  ctx.output.exitCode = 99;
20626
20645
  ctx.output.reason = `shell script not found: ${shellName} (looked in ${profile.dir})`;
20627
20646
  return;
20628
20647
  }
20629
20648
  const positional = entry.with ? Object.values(entry.with).map((v) => String(v)) : [];
20630
- const outputFile = path44.join(
20649
+ const outputFile = path45.join(
20631
20650
  os7.tmpdir(),
20632
20651
  `kody-shell-output-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`
20633
20652
  );
@@ -20662,14 +20681,14 @@ async function runShellEntry(entry, ctx, profile) {
20662
20681
  let killTimer;
20663
20682
  let escalateTimer;
20664
20683
  const result = await new Promise(
20665
- (resolve16) => {
20684
+ (resolve17) => {
20666
20685
  let settled = false;
20667
20686
  const settle = (code, signal, spawnErr) => {
20668
20687
  if (settled) return;
20669
20688
  settled = true;
20670
20689
  if (killTimer) clearTimeout(killTimer);
20671
20690
  if (escalateTimer) clearTimeout(escalateTimer);
20672
- resolve16({ code, signal, spawnErr });
20691
+ resolve17({ code, signal, spawnErr });
20673
20692
  };
20674
20693
  child.on("error", (err) => settle(null, null, err));
20675
20694
  child.on("close", (code, signal) => settle(code, signal));
@@ -20699,9 +20718,9 @@ async function runShellEntry(entry, ctx, profile) {
20699
20718
  }
20700
20719
  let sideChannelText = "";
20701
20720
  try {
20702
- if (fs46.existsSync(outputFile)) {
20703
- sideChannelText = fs46.readFileSync(outputFile, "utf-8");
20704
- fs46.rmSync(outputFile, { force: true });
20721
+ if (fs47.existsSync(outputFile)) {
20722
+ sideChannelText = fs47.readFileSync(outputFile, "utf-8");
20723
+ fs47.rmSync(outputFile, { force: true });
20705
20724
  }
20706
20725
  } catch {
20707
20726
  }
@@ -20872,7 +20891,7 @@ __export(job_exports, {
20872
20891
  stableJobKey: () => stableJobKey,
20873
20892
  validateJob: () => validateJob
20874
20893
  });
20875
- import * as path45 from "path";
20894
+ import * as path46 from "path";
20876
20895
  function newJobId(flavor) {
20877
20896
  localJobSeq += 1;
20878
20897
  const runId = process.env.GITHUB_RUN_ID;
@@ -21335,11 +21354,11 @@ function selectWorkflowTransition(step, data, counts) {
21335
21354
  }
21336
21355
  function workflowResultConditionPaths(transitions) {
21337
21356
  return transitions.flatMap(
21338
- (transition) => Object.keys(transition.when ?? {}).filter((path51) => path51.startsWith("result."))
21357
+ (transition) => Object.keys(transition.when ?? {}).filter((path52) => path52.startsWith("result."))
21339
21358
  );
21340
21359
  }
21341
21360
  function conditionMatches(condition, context) {
21342
- return Object.entries(condition).every(([path51, expected]) => valueMatches(resolveDottedPath2(context, path51), expected));
21361
+ return Object.entries(condition).every(([path52, expected]) => valueMatches(resolveDottedPath2(context, path52), expected));
21343
21362
  }
21344
21363
  function withWorkflowBoundaryEval(capability, result) {
21345
21364
  const capabilityKind = capability.config.capabilityKind;
@@ -21497,7 +21516,7 @@ function loadCapabilityContext(slug, cwd) {
21497
21516
  return resolveCapabilityFolder(slug, hydratedCapabilitiesRoot(cwd));
21498
21517
  }
21499
21518
  function hydratedCapabilitiesRoot(cwd) {
21500
- return path45.join(cwd, ".kody-engine", "definitions", "capabilities");
21519
+ return path46.join(cwd, ".kody-engine", "definitions", "capabilities");
21501
21520
  }
21502
21521
  function loadWorkflowContext(slug, base) {
21503
21522
  if (!slug || !base.config || !isWorkflowDefinitionId(slug)) return null;
@@ -21660,7 +21679,7 @@ function translateOpenAISseToBrain(opts) {
21660
21679
 
21661
21680
  // src/servers/brain-serve.ts
21662
21681
  import { createServer as createServer2 } from "http";
21663
- import * as path48 from "path";
21682
+ import * as path49 from "path";
21664
21683
 
21665
21684
  // src/chat/loop.ts
21666
21685
  init_agent();
@@ -21814,9 +21833,9 @@ var CodexAppServerClient = class {
21814
21833
  await this.request("thread/resume", { threadId });
21815
21834
  }
21816
21835
  async runTurn(args) {
21817
- await new Promise((resolve16, reject) => {
21836
+ await new Promise((resolve17, reject) => {
21818
21837
  this.process.turnWaiters.set(args.threadId, {
21819
- resolve: resolve16,
21838
+ resolve: resolve17,
21820
21839
  reject,
21821
21840
  onNotification: args.onNotification,
21822
21841
  queue: Promise.resolve()
@@ -21833,8 +21852,8 @@ var CodexAppServerClient = class {
21833
21852
  }
21834
21853
  request(method, params) {
21835
21854
  const id = this.process.nextId++;
21836
- return new Promise((resolve16, reject) => {
21837
- this.process.pending.set(id, { resolve: resolve16, reject });
21855
+ return new Promise((resolve17, reject) => {
21856
+ this.process.pending.set(id, { resolve: resolve17, reject });
21838
21857
  this.process.child.stdin.write(`${JSON.stringify({ method, id, params })}
21839
21858
  `);
21840
21859
  });
@@ -22702,8 +22721,8 @@ init_config();
22702
22721
 
22703
22722
  // src/kody-cli.ts
22704
22723
  import { execFileSync as execFileSync24 } from "child_process";
22705
- import * as fs47 from "fs";
22706
- import * as path46 from "path";
22724
+ import * as fs48 from "fs";
22725
+ import * as path47 from "path";
22707
22726
 
22708
22727
  // src/app-auth.ts
22709
22728
  import { createSign } from "crypto";
@@ -23471,6 +23490,20 @@ function recoverCheckoutToken(env = process.env, cwd = process.cwd()) {
23471
23490
  return token;
23472
23491
  }
23473
23492
  async function resolveAuthToken(env = process.env) {
23493
+ const readySources = [
23494
+ ["GH_PAT", env.GH_PAT],
23495
+ ["KODY_TOKEN", env.KODY_TOKEN],
23496
+ ["GH_TOKEN", env.GH_TOKEN]
23497
+ ];
23498
+ const ready = readySources.find(([, value]) => !!value?.trim());
23499
+ if (ready?.[1]) {
23500
+ const token2 = ready[1].trim();
23501
+ env.GH_TOKEN = token2;
23502
+ recoverCheckoutToken(env);
23503
+ process.stdout.write(`\u2192 kody: GH_TOKEN sourced from env.${ready[0]}
23504
+ `);
23505
+ return token2;
23506
+ }
23474
23507
  const creds = readAppCreds(env);
23475
23508
  if (creds) {
23476
23509
  try {
@@ -23484,12 +23517,7 @@ async function resolveAuthToken(env = process.env) {
23484
23517
  `);
23485
23518
  }
23486
23519
  }
23487
- const sources = [
23488
- ["KODY_TOKEN", env.KODY_TOKEN],
23489
- ["GH_TOKEN", env.GH_TOKEN],
23490
- ["GITHUB_TOKEN", env.GITHUB_TOKEN],
23491
- ["GH_PAT", env.GH_PAT]
23492
- ];
23520
+ const sources = [["GITHUB_TOKEN", env.GITHUB_TOKEN]];
23493
23521
  const picked = sources.find(([, v]) => !!v);
23494
23522
  const token = picked?.[1];
23495
23523
  if (token && !env.GH_TOKEN) env.GH_TOKEN = token;
@@ -23505,9 +23533,9 @@ async function resolveAuthToken(env = process.env) {
23505
23533
  return void 0;
23506
23534
  }
23507
23535
  function detectPackageManager2(cwd) {
23508
- if (fs47.existsSync(path46.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
23509
- if (fs47.existsSync(path46.join(cwd, "yarn.lock"))) return "yarn";
23510
- if (fs47.existsSync(path46.join(cwd, "bun.lockb"))) return "bun";
23536
+ if (fs48.existsSync(path47.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
23537
+ if (fs48.existsSync(path47.join(cwd, "yarn.lock"))) return "yarn";
23538
+ if (fs48.existsSync(path47.join(cwd, "bun.lockb"))) return "bun";
23511
23539
  return "npm";
23512
23540
  }
23513
23541
  function shouldChainScheduledWatch(match) {
@@ -23600,8 +23628,8 @@ function postFailureTail(issueNumber, cwd, reason) {
23600
23628
  const logPath = lastRunLogPath(cwd);
23601
23629
  let tail = "";
23602
23630
  try {
23603
- if (fs47.existsSync(logPath)) {
23604
- const content = fs47.readFileSync(logPath, "utf-8");
23631
+ if (fs48.existsSync(logPath)) {
23632
+ const content = fs48.readFileSync(logPath, "utf-8");
23605
23633
  tail = content.slice(-3e3);
23606
23634
  }
23607
23635
  } catch {
@@ -23630,7 +23658,7 @@ async function runCi(argv) {
23630
23658
  return 0;
23631
23659
  }
23632
23660
  const args = parseCiArgs(argv);
23633
- const cwd = args.cwd ? path46.resolve(args.cwd) : process.cwd();
23661
+ const cwd = args.cwd ? path47.resolve(args.cwd) : process.cwd();
23634
23662
  try {
23635
23663
  const n = unpackAllSecrets();
23636
23664
  if (n > 0) process.stdout.write(`\u2192 kody: unpacked ${n} secret(s) from ALL_SECRETS
@@ -23689,9 +23717,9 @@ async function runCi(argv) {
23689
23717
  forceRunCliArgs = { goal: envForceMessage };
23690
23718
  }
23691
23719
  }
23692
- if (!args.issueNumber && !autoFallback && !forceRunAction && !runRequestFanOut && eventName === "workflow_dispatch" && dispatchEventPath && fs47.existsSync(dispatchEventPath)) {
23720
+ if (!args.issueNumber && !autoFallback && !forceRunAction && !runRequestFanOut && eventName === "workflow_dispatch" && dispatchEventPath && fs48.existsSync(dispatchEventPath)) {
23693
23721
  try {
23694
- const evt = JSON.parse(fs47.readFileSync(dispatchEventPath, "utf-8"));
23722
+ const evt = JSON.parse(fs48.readFileSync(dispatchEventPath, "utf-8"));
23695
23723
  const inputs = objectValue2(evt.inputs);
23696
23724
  const issueInput = parseInt(String(inputs?.issue_number ?? ""), 10);
23697
23725
  const sessionInput = String(inputs?.sessionId ?? "");
@@ -24064,8 +24092,8 @@ init_repoWorkspace();
24064
24092
 
24065
24093
  // src/scripts/brainTurnLog.ts
24066
24094
  init_runtimePaths();
24067
- import * as fs48 from "fs";
24068
- import * as path47 from "path";
24095
+ import * as fs49 from "fs";
24096
+ import * as path48 from "path";
24069
24097
  import posixPath4 from "path/posix";
24070
24098
  var live = /* @__PURE__ */ new Map();
24071
24099
  function brainEventsFilePath(dir, chatId) {
@@ -24073,8 +24101,8 @@ function brainEventsFilePath(dir, chatId) {
24073
24101
  }
24074
24102
  function lastPersistedSeq(dir, chatId) {
24075
24103
  const p = brainEventsFilePath(dir, chatId);
24076
- if (!fs48.existsSync(p)) return 0;
24077
- const lines = fs48.readFileSync(p, "utf-8").split("\n").filter(Boolean);
24104
+ if (!fs49.existsSync(p)) return 0;
24105
+ const lines = fs49.readFileSync(p, "utf-8").split("\n").filter(Boolean);
24078
24106
  if (lines.length === 0) return 0;
24079
24107
  try {
24080
24108
  return JSON.parse(lines[lines.length - 1]).seq || 0;
@@ -24084,9 +24112,9 @@ function lastPersistedSeq(dir, chatId) {
24084
24112
  }
24085
24113
  function readSince(dir, chatId, since) {
24086
24114
  const p = brainEventsFilePath(dir, chatId);
24087
- if (!fs48.existsSync(p)) return [];
24115
+ if (!fs49.existsSync(p)) return [];
24088
24116
  const out = [];
24089
- for (const line of fs48.readFileSync(p, "utf-8").split("\n")) {
24117
+ for (const line of fs49.readFileSync(p, "utf-8").split("\n")) {
24090
24118
  if (!line) continue;
24091
24119
  try {
24092
24120
  const rec = JSON.parse(line);
@@ -24112,12 +24140,12 @@ function beginTurn(dir, chatId) {
24112
24140
  };
24113
24141
  live.set(chatId, state);
24114
24142
  const p = brainEventsFilePath(dir, chatId);
24115
- fs48.mkdirSync(path47.dirname(p), { recursive: true });
24143
+ fs49.mkdirSync(path48.dirname(p), { recursive: true });
24116
24144
  return (event) => {
24117
24145
  state.seq += 1;
24118
24146
  const rec = { seq: state.seq, turn, ts: Date.now(), event };
24119
24147
  try {
24120
- fs48.appendFileSync(p, `${JSON.stringify(rec)}
24148
+ fs49.appendFileSync(p, `${JSON.stringify(rec)}
24121
24149
  `);
24122
24150
  } catch (err) {
24123
24151
  process.stderr.write(
@@ -24156,7 +24184,7 @@ function endTurnIfUnterminated(dir, chatId, errMessage) {
24156
24184
  event: { type: "error", error: errMessage || "turn ended unexpectedly", chatId }
24157
24185
  };
24158
24186
  try {
24159
- fs48.appendFileSync(brainEventsFilePath(dir, chatId), `${JSON.stringify(rec)}
24187
+ fs49.appendFileSync(brainEventsFilePath(dir, chatId), `${JSON.stringify(rec)}
24160
24188
  `);
24161
24189
  } catch {
24162
24190
  }
@@ -24250,17 +24278,17 @@ function authOk(req, expected) {
24250
24278
  return false;
24251
24279
  }
24252
24280
  function readJsonBody(req) {
24253
- return new Promise((resolve16, reject) => {
24281
+ return new Promise((resolve17, reject) => {
24254
24282
  const chunks = [];
24255
24283
  req.on("data", (c) => chunks.push(c));
24256
24284
  req.on("end", () => {
24257
24285
  const raw = Buffer.concat(chunks).toString("utf-8");
24258
24286
  if (!raw.trim()) {
24259
- resolve16({});
24287
+ resolve17({});
24260
24288
  return;
24261
24289
  }
24262
24290
  try {
24263
- resolve16(JSON.parse(raw));
24291
+ resolve17(JSON.parse(raw));
24264
24292
  } catch (err) {
24265
24293
  reject(err instanceof Error ? err : new Error(String(err)));
24266
24294
  }
@@ -24353,12 +24381,12 @@ var BrokerSink = class {
24353
24381
  const be = translateChatEvent(event, this.chatId);
24354
24382
  if (!be) return;
24355
24383
  this.emitToLog(be);
24356
- if (this.tenantId && hasStateBackendConfig()) {
24384
+ if (this.tenantId && hasStateBackendConfig2()) {
24357
24385
  await createStateBackendFromEnv().appendChatEvent(this.tenantId, this.chatId, be);
24358
24386
  }
24359
24387
  }
24360
24388
  };
24361
- function hasStateBackendConfig(env = process.env) {
24389
+ function hasStateBackendConfig2(env = process.env) {
24362
24390
  return Boolean(env.CONVEX_URL?.trim() && env.KODY_SERVICE_KEY?.trim());
24363
24391
  }
24364
24392
  var chatQueues = /* @__PURE__ */ new Map();
@@ -24519,7 +24547,7 @@ function buildServer(opts) {
24519
24547
  const runTurn = opts.runTurn ?? runChatTurn;
24520
24548
  const createStore = opts.createStore ?? createSessionStore;
24521
24549
  const cloneRepo = opts.cloneRepo ?? defaultCloneRepo;
24522
- const reposRoot = opts.reposRoot ?? path48.join(path48.dirname(path48.resolve(opts.cwd)), "repos");
24550
+ const reposRoot = opts.reposRoot ?? path49.join(path49.dirname(path49.resolve(opts.cwd)), "repos");
24523
24551
  return createServer2(async (req, res) => {
24524
24552
  if (!req.method || !req.url) {
24525
24553
  sendJson(res, 400, { error: "bad request" });
@@ -24600,11 +24628,11 @@ async function brainServe(opts) {
24600
24628
  litellmUrl,
24601
24629
  driver
24602
24630
  });
24603
- await new Promise((resolve16) => {
24631
+ await new Promise((resolve17) => {
24604
24632
  server.listen(port, "0.0.0.0", () => {
24605
24633
  process.stdout.write(`[brain-serve] listening on 0.0.0.0:${port} (cwd=${opts.cwd})
24606
24634
  `);
24607
- resolve16();
24635
+ resolve17();
24608
24636
  });
24609
24637
  });
24610
24638
  const shutdown = (signal) => {
@@ -24859,14 +24887,14 @@ async function startBrainProxy(opts) {
24859
24887
  const { httpServer, handler } = buildBrainProxy(opts);
24860
24888
  const port = opts.port ?? 0;
24861
24889
  const host = opts.host ?? "127.0.0.1";
24862
- await new Promise((resolve16) => httpServer.listen(port, host, () => resolve16()));
24890
+ await new Promise((resolve17) => httpServer.listen(port, host, () => resolve17()));
24863
24891
  const addr = httpServer.address();
24864
24892
  return {
24865
24893
  httpServer,
24866
24894
  port: addr.port,
24867
24895
  url: `http://${host}:${addr.port}`,
24868
- stop: () => new Promise((resolve16) => {
24869
- httpServer.close(() => resolve16());
24896
+ stop: () => new Promise((resolve17) => {
24897
+ httpServer.close(() => resolve17());
24870
24898
  }),
24871
24899
  handler
24872
24900
  };
@@ -25016,23 +25044,23 @@ function buildMcpHttpServer(opts) {
25016
25044
  httpServer,
25017
25045
  routes,
25018
25046
  port,
25019
- stop: () => new Promise((resolve16) => {
25047
+ stop: () => new Promise((resolve17) => {
25020
25048
  let pending = transports.size;
25021
25049
  if (pending === 0) {
25022
- httpServer.close(() => resolve16());
25050
+ httpServer.close(() => resolve17());
25023
25051
  return;
25024
25052
  }
25025
25053
  for (const transport of transports.values()) {
25026
25054
  void transport.close().finally(() => {
25027
25055
  pending--;
25028
- if (pending === 0) httpServer.close(() => resolve16());
25056
+ if (pending === 0) httpServer.close(() => resolve17());
25029
25057
  });
25030
25058
  }
25031
25059
  })
25032
25060
  };
25033
25061
  }
25034
25062
  function listenMcpHttpServer(server, host = "127.0.0.1") {
25035
- return new Promise((resolve16, reject) => {
25063
+ return new Promise((resolve17, reject) => {
25036
25064
  server.httpServer.once("error", reject);
25037
25065
  server.httpServer.listen(server.port, host, () => {
25038
25066
  server.httpServer.off("error", reject);
@@ -25040,7 +25068,7 @@ function listenMcpHttpServer(server, host = "127.0.0.1") {
25040
25068
  if (addr && typeof addr === "object") {
25041
25069
  server.port = addr.port;
25042
25070
  }
25043
- resolve16();
25071
+ resolve17();
25044
25072
  });
25045
25073
  });
25046
25074
  }
@@ -25123,7 +25151,7 @@ async function loadConfigSafe() {
25123
25151
  }
25124
25152
 
25125
25153
  // src/chat-cli.ts
25126
- import * as path49 from "path";
25154
+ import * as path50 from "path";
25127
25155
 
25128
25156
  // src/chat/inbox.ts
25129
25157
  import { execFileSync as execFileSync25 } from "child_process";
@@ -25190,7 +25218,7 @@ async function waitForNextUserMessage(opts) {
25190
25218
  }
25191
25219
  }
25192
25220
  function sleep3(ms) {
25193
- return new Promise((resolve16) => setTimeout(resolve16, ms));
25221
+ return new Promise((resolve17) => setTimeout(resolve17, ms));
25194
25222
  }
25195
25223
  function currentBranch(cwd) {
25196
25224
  try {
@@ -25404,7 +25432,7 @@ async function runChat(argv) {
25404
25432
  ${CHAT_HELP}`);
25405
25433
  return 64;
25406
25434
  }
25407
- const cwd = args.cwd ? path49.resolve(args.cwd) : process.cwd();
25435
+ const cwd = args.cwd ? path50.resolve(args.cwd) : process.cwd();
25408
25436
  const sessionId = args.sessionId;
25409
25437
  const runRequest = readRunRequestFromEnv();
25410
25438
  if (runRequest && "request" in runRequest) {
@@ -25525,8 +25553,8 @@ init_config();
25525
25553
  // src/definition-hydration.ts
25526
25554
  init_state_backend();
25527
25555
  import { createHash as createHash5 } from "crypto";
25528
- import * as fs49 from "fs";
25529
- import * as path50 from "path";
25556
+ import * as fs50 from "fs";
25557
+ import * as path51 from "path";
25530
25558
  var SLUG_RE2 = /^[a-z0-9][a-z0-9_-]{0,63}$/;
25531
25559
  function assertSafeDefinitionPath(filePath) {
25532
25560
  const segments = filePath.split("/");
@@ -25560,32 +25588,32 @@ function writeDefinition(root, kind, definition) {
25560
25588
  if (kind === "agent") {
25561
25589
  const raw = bundle.files["agent.md"];
25562
25590
  if (typeof raw !== "string") throw new Error(`agent definition ${definition.slug} is missing agent.md`);
25563
- fs49.writeFileSync(path50.join(root, "agents", `${definition.slug}.md`), raw, "utf8");
25591
+ fs50.writeFileSync(path51.join(root, "agents", `${definition.slug}.md`), raw, "utf8");
25564
25592
  return;
25565
25593
  }
25566
25594
  if (kind === "goal") {
25567
- const goalRoot = path50.join(root, "goals", definition.slug);
25595
+ const goalRoot = path51.join(root, "goals", definition.slug);
25568
25596
  for (const [filePath, contents] of Object.entries(bundle.files)) {
25569
- const target = path50.join(goalRoot, filePath);
25570
- fs49.mkdirSync(path50.dirname(target), { recursive: true });
25571
- fs49.writeFileSync(target, contents, "utf8");
25597
+ const target = path51.join(goalRoot, filePath);
25598
+ fs50.mkdirSync(path51.dirname(target), { recursive: true });
25599
+ fs50.writeFileSync(target, contents, "utf8");
25572
25600
  }
25573
25601
  return;
25574
25602
  }
25575
- const capabilityRoot = path50.join(root, "capabilities", definition.slug);
25603
+ const capabilityRoot = path51.join(root, "capabilities", definition.slug);
25576
25604
  for (const [filePath, contents] of Object.entries(bundle.files)) {
25577
- const target = path50.join(capabilityRoot, filePath);
25578
- fs49.mkdirSync(path50.dirname(target), { recursive: true });
25579
- fs49.writeFileSync(target, contents, "utf8");
25605
+ const target = path51.join(capabilityRoot, filePath);
25606
+ fs50.mkdirSync(path51.dirname(target), { recursive: true });
25607
+ fs50.writeFileSync(target, contents, "utf8");
25580
25608
  }
25581
25609
  }
25582
25610
  async function hydrateDefinitions(options) {
25583
- const root = path50.join(options.cwd, ".kody-engine", "definitions");
25611
+ const root = path51.join(options.cwd, ".kody-engine", "definitions");
25584
25612
  const staging = `${root}.tmp-${process.pid}-${Date.now()}`;
25585
- fs49.rmSync(staging, { recursive: true, force: true });
25586
- fs49.mkdirSync(path50.join(staging, "agents"), { recursive: true });
25587
- fs49.mkdirSync(path50.join(staging, "capabilities"), { recursive: true });
25588
- fs49.mkdirSync(path50.join(staging, "goals"), { recursive: true });
25613
+ fs50.rmSync(staging, { recursive: true, force: true });
25614
+ fs50.mkdirSync(path51.join(staging, "agents"), { recursive: true });
25615
+ fs50.mkdirSync(path51.join(staging, "capabilities"), { recursive: true });
25616
+ fs50.mkdirSync(path51.join(staging, "goals"), { recursive: true });
25589
25617
  try {
25590
25618
  const [capabilities, agents, goals] = await Promise.all([
25591
25619
  options.backend.listDefinitions(options.tenantId, "capability"),
@@ -25611,22 +25639,21 @@ async function hydrateDefinitions(options) {
25611
25639
  hydratedAt: (/* @__PURE__ */ new Date()).toISOString(),
25612
25640
  versions: Object.fromEntries(Object.entries(versions).sort(([left], [right]) => left.localeCompare(right)))
25613
25641
  };
25614
- fs49.writeFileSync(path50.join(staging, "manifest.json"), `${JSON.stringify(manifest, null, 2)}
25642
+ fs50.writeFileSync(path51.join(staging, "manifest.json"), `${JSON.stringify(manifest, null, 2)}
25615
25643
  `, "utf8");
25616
- fs49.rmSync(root, { recursive: true, force: true });
25617
- fs49.renameSync(staging, root);
25644
+ fs50.rmSync(root, { recursive: true, force: true });
25645
+ fs50.renameSync(staging, root);
25618
25646
  return { root, tenantId: options.tenantId, versions: manifest.versions };
25619
25647
  } catch (error) {
25620
- fs49.rmSync(staging, { recursive: true, force: true });
25648
+ fs50.rmSync(staging, { recursive: true, force: true });
25621
25649
  throw error;
25622
25650
  }
25623
25651
  }
25624
25652
  async function hydrateDefinitionsFromEnv(cwd = process.cwd(), env = process.env) {
25625
25653
  const tenantId2 = env.GITHUB_REPOSITORY?.trim();
25626
- const hasCredentials = Boolean(env.CONVEX_URL?.trim()) && Boolean(env.KODY_SERVICE_KEY?.trim());
25627
- if (!hasCredentials) {
25654
+ if (!hasStateBackendConfig(env)) {
25628
25655
  if (env.GITHUB_ACTIONS === "true") {
25629
- throw new Error("CONVEX_URL and KODY_SERVICE_KEY are required for backend definitions in GitHub Actions");
25656
+ throw new Error("GitHub Actions workflow identity is required for backend definitions");
25630
25657
  }
25631
25658
  return null;
25632
25659
  }
@@ -25711,8 +25738,8 @@ var FlyClient = class {
25711
25738
  get fetch() {
25712
25739
  return this.opts.fetchImpl ?? fetch;
25713
25740
  }
25714
- async call(path51, init = {}) {
25715
- const res = await this.fetch(`${FLY_API_BASE}${path51}`, {
25741
+ async call(path52, init = {}) {
25742
+ const res = await this.fetch(`${FLY_API_BASE}${path52}`, {
25716
25743
  method: init.method ?? "GET",
25717
25744
  headers: {
25718
25745
  Authorization: `Bearer ${this.opts.token}`,
@@ -25723,7 +25750,7 @@ var FlyClient = class {
25723
25750
  if (res.status === 404 && init.allow404) return null;
25724
25751
  if (!res.ok) {
25725
25752
  const text2 = await res.text().catch(() => "");
25726
- throw new Error(`Fly API ${res.status} on ${path51}: ${text2.slice(0, 200) || res.statusText}`);
25753
+ throw new Error(`Fly API ${res.status} on ${path52}: ${text2.slice(0, 200) || res.statusText}`);
25727
25754
  }
25728
25755
  if (res.status === 204) return null;
25729
25756
  const raw = await res.text();
@@ -26236,14 +26263,14 @@ function sendJson2(res, status, body) {
26236
26263
  res.end(JSON.stringify(body));
26237
26264
  }
26238
26265
  function readJsonBody2(req) {
26239
- return new Promise((resolve16, reject) => {
26266
+ return new Promise((resolve17, reject) => {
26240
26267
  const chunks = [];
26241
26268
  req.on("data", (c) => chunks.push(c));
26242
26269
  req.on("end", () => {
26243
26270
  const raw = Buffer.concat(chunks).toString("utf-8");
26244
- if (!raw.trim()) return resolve16({});
26271
+ if (!raw.trim()) return resolve17({});
26245
26272
  try {
26246
- resolve16(JSON.parse(raw));
26273
+ resolve17(JSON.parse(raw));
26247
26274
  } catch (err) {
26248
26275
  reject(err instanceof Error ? err : new Error(String(err)));
26249
26276
  }
@@ -26454,10 +26481,10 @@ async function poolServe() {
26454
26481
  }
26455
26482
  });
26456
26483
  const apiHost = process.env.POOL_API_HOST ?? "::";
26457
- await new Promise((resolve16) => {
26484
+ await new Promise((resolve17) => {
26458
26485
  server.listen(apiPort, apiHost, () => {
26459
26486
  log(`listening on ${apiHost}:${apiPort} (min=${min}, app=${app}, region=${region})`);
26460
- resolve16();
26487
+ resolve17();
26461
26488
  });
26462
26489
  });
26463
26490
  if (loopTickEnabled) void runLoopTick();
@@ -26476,7 +26503,7 @@ async function poolServe() {
26476
26503
 
26477
26504
  // src/servers/runner-serve.ts
26478
26505
  import { spawn as spawn9 } from "child_process";
26479
- import * as fs50 from "fs";
26506
+ import * as fs51 from "fs";
26480
26507
  import { createServer as createServer6 } from "http";
26481
26508
  var DEFAULT_PORT2 = 8080;
26482
26509
  var DEFAULT_WORKDIR = "/workspace/repo";
@@ -26497,17 +26524,17 @@ function authOk2(req, expected) {
26497
26524
  return false;
26498
26525
  }
26499
26526
  function readJsonBody3(req) {
26500
- return new Promise((resolve16, reject) => {
26527
+ return new Promise((resolve17, reject) => {
26501
26528
  const chunks = [];
26502
26529
  req.on("data", (c) => chunks.push(c));
26503
26530
  req.on("end", () => {
26504
26531
  const raw = Buffer.concat(chunks).toString("utf-8");
26505
26532
  if (!raw.trim()) {
26506
- resolve16({});
26533
+ resolve17({});
26507
26534
  return;
26508
26535
  }
26509
26536
  try {
26510
- resolve16(JSON.parse(raw));
26537
+ resolve17(JSON.parse(raw));
26511
26538
  } catch (err) {
26512
26539
  reject(err instanceof Error ? err : new Error(String(err)));
26513
26540
  }
@@ -26611,8 +26638,8 @@ async function defaultRunJob(job) {
26611
26638
  const workdir = process.env.RUNNER_WORKDIR ?? DEFAULT_WORKDIR;
26612
26639
  const branch = job.ref ?? "main";
26613
26640
  const authUrl = `https://x-access-token:${job.githubToken}@github.com/${job.repo}.git`;
26614
- fs50.rmSync(workdir, { recursive: true, force: true });
26615
- fs50.mkdirSync(workdir, { recursive: true });
26641
+ fs51.rmSync(workdir, { recursive: true, force: true });
26642
+ fs51.mkdirSync(workdir, { recursive: true });
26616
26643
  const allSecrets = typeof job.allSecrets === "string" ? job.allSecrets : JSON.stringify(job.allSecrets ?? {});
26617
26644
  const target = job.runRequest.target;
26618
26645
  const interactive = target.type === "chat";
@@ -26641,13 +26668,13 @@ async function defaultRunJob(job) {
26641
26668
  ...interactive && job.idleExitMs ? { KODY_IDLE_EXIT_MS: String(job.idleExitMs) } : {},
26642
26669
  ...interactive && job.hardCapMs ? { KODY_HARD_CAP_MS: String(job.hardCapMs) } : {}
26643
26670
  };
26644
- const run = (cmd, args, cwd) => new Promise((resolve16) => {
26671
+ const run = (cmd, args, cwd) => new Promise((resolve17) => {
26645
26672
  const child = spawn9(cmd, args, { stdio: "inherit", env: childEnv, cwd });
26646
- child.on("exit", (code) => resolve16(code ?? 0));
26673
+ child.on("exit", (code) => resolve17(code ?? 0));
26647
26674
  child.on("error", (err) => {
26648
26675
  process.stderr.write(`[runner-serve] ${cmd} failed: ${err.message}
26649
26676
  `);
26650
- resolve16(1);
26677
+ resolve17(1);
26651
26678
  });
26652
26679
  });
26653
26680
  process.stdout.write(`[runner-serve] job ${job.jobId}: cloning ${job.repo}@${branch}
@@ -26723,11 +26750,11 @@ async function runnerServe() {
26723
26750
  const port = Number(process.env.PORT ?? DEFAULT_PORT2);
26724
26751
  const server = buildServer2({ apiKey });
26725
26752
  const host = process.env.RUNNER_HOST ?? "::";
26726
- await new Promise((resolve16) => {
26753
+ await new Promise((resolve17) => {
26727
26754
  server.listen(port, host, () => {
26728
26755
  process.stdout.write(`[runner-serve] listening on ${host}:${port} (idle, awaiting job)
26729
26756
  `);
26730
- resolve16();
26757
+ resolve17();
26731
26758
  });
26732
26759
  });
26733
26760
  const shutdown = (signal) => {
@@ -26796,14 +26823,14 @@ async function serve(opts) {
26796
26823
  `);
26797
26824
  const args = ["--dangerously-skip-permissions", "--model", model.model];
26798
26825
  const child = spawn10("claude", args, { stdio: "inherit", env: editorEnv, cwd: opts.cwd });
26799
- const exitCode = await new Promise((resolve16) => {
26800
- child.on("exit", (code) => resolve16(code ?? 0));
26826
+ const exitCode = await new Promise((resolve17) => {
26827
+ child.on("exit", (code) => resolve17(code ?? 0));
26801
26828
  child.on("error", (err) => {
26802
26829
  process.stderr.write(`[kody serve] failed to launch Claude Code: ${err.message}
26803
26830
  `);
26804
26831
  process.stderr.write(` Install: https://docs.anthropic.com/claude/docs/claude-code
26805
26832
  `);
26806
- resolve16(1);
26833
+ resolve17(1);
26807
26834
  });
26808
26835
  });
26809
26836
  killProxy();
@@ -27187,7 +27214,7 @@ function parseArgs(argv) {
27187
27214
  }
27188
27215
  async function main(argv = process.argv.slice(2)) {
27189
27216
  unpackAllSecrets();
27190
- const cwdFlag = argv.findIndex((value) => value === "--cwd");
27217
+ const cwdFlag = argv.indexOf("--cwd");
27191
27218
  const definitionCwd = cwdFlag >= 0 && argv[cwdFlag + 1] ? argv[cwdFlag + 1] : process.cwd();
27192
27219
  const shouldHydrate = Boolean(process.env.CONVEX_URL?.trim()) || process.env.GITHUB_ACTIONS === "true" && Boolean(process.env.GITHUB_EVENT_NAME);
27193
27220
  if (shouldHydrate) {