@kody-ade/kody-engine 0.4.606 → 0.4.610

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/README.md CHANGED
@@ -113,7 +113,7 @@ model key at runtime; user secrets are not copied into Actions. `KODY_TOKEN`
113
113
  remains an optional GitHub authentication fallback when Kody must trigger
114
114
  downstream workflows or modify `.github/workflows/*`.
115
115
 
116
- The consumer workflow listens on `issue_comment` for `@kody ...` dispatch and `workflow_dispatch` for manual runs, chat mode, and scheduled wakeups.
116
+ The consumer workflow listens on `issue_comment` for `@kody ...` dispatch and `workflow_dispatch` for manual runs and chat mode. Convex owns scheduled Loop wakeups.
117
117
 
118
118
  ## Commands
119
119
 
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.606",
18
+ version: "0.4.610",
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",
@@ -2353,7 +2353,7 @@ function parseCapabilityRequirements(raw) {
2353
2353
  if (raw === void 0) return void 0;
2354
2354
  if (!isPlainObject(raw)) throw new Error("contract.json requirements must be an object");
2355
2355
  const unsupported = Object.keys(raw).filter(
2356
- (key) => key !== "browser" && key !== "qaCredentials" && key !== "githubTestToken" && key !== "qaAccountCredentials" && key !== "browserOnly"
2356
+ (key) => key !== "browser" && key !== "qaCredentials" && key !== "githubTestToken" && key !== "qaAccountCredentials" && key !== "qaAccountModelSettings" && key !== "browserOnly"
2357
2357
  );
2358
2358
  if (unsupported.length > 0) {
2359
2359
  throw new Error(`contract.json requirements contains unsupported fields: ${unsupported.join(", ")}`);
@@ -2372,10 +2372,13 @@ function parseCapabilityRequirements(raw) {
2372
2372
  ))) {
2373
2373
  throw new Error("contract.json requirements.qaAccountCredentials must contain valid credential names");
2374
2374
  }
2375
+ if (raw.qaAccountModelSettings !== void 0 && !isPlainObject(raw.qaAccountModelSettings)) {
2376
+ throw new Error("contract.json requirements.qaAccountModelSettings must be an object");
2377
+ }
2375
2378
  if (raw.browserOnly !== void 0 && typeof raw.browserOnly !== "boolean") {
2376
2379
  throw new Error("contract.json requirements.browserOnly must be boolean");
2377
2380
  }
2378
- if ((raw.qaCredentials === true || raw.githubTestToken === true || raw.qaAccountCredentials !== void 0 || raw.browserOnly === true) && raw.browser !== true) {
2381
+ if ((raw.qaCredentials === true || raw.githubTestToken === true || raw.qaAccountCredentials !== void 0 || raw.qaAccountModelSettings !== void 0 || raw.browserOnly === true) && raw.browser !== true) {
2379
2382
  throw new Error("contract.json authentication requirements require browser");
2380
2383
  }
2381
2384
  const requirements = {
@@ -2383,6 +2386,7 @@ function parseCapabilityRequirements(raw) {
2383
2386
  ...raw.qaCredentials === true ? { qaCredentials: true } : {},
2384
2387
  ...raw.githubTestToken === true ? { githubTestToken: true } : {},
2385
2388
  ...Array.isArray(raw.qaAccountCredentials) ? { qaAccountCredentials: [...new Set(raw.qaAccountCredentials)] } : {},
2389
+ ...isPlainObject(raw.qaAccountModelSettings) ? { qaAccountModelSettings: raw.qaAccountModelSettings } : {},
2386
2390
  ...raw.browserOnly === true ? { browserOnly: true } : {}
2387
2391
  };
2388
2392
  return Object.keys(requirements).length > 0 ? requirements : void 0;
@@ -3154,6 +3158,13 @@ function createStateBackendFromEnv(env = process.env, client) {
3154
3158
  });
3155
3159
  return Array.isArray(result) ? result : [];
3156
3160
  },
3161
+ async replaceLoopWakeRegistrations(tenantId2, loopIds, updatedAt) {
3162
+ await transport.mutation(anyApi.loopWakes.replaceRegistrations, {
3163
+ tenantId: requireTenant(tenantId2),
3164
+ loopIds: [...new Set(loopIds.map((id) => requireNonEmpty(id, "loopId")))],
3165
+ updatedAt: requireNonEmpty(updatedAt, "updatedAt")
3166
+ });
3167
+ },
3157
3168
  async get(tenantId2, taskKey, kind) {
3158
3169
  const result = await transport.query(anyApi.taskState.get, {
3159
3170
  tenantId: requireTenant(tenantId2),
@@ -3172,6 +3183,26 @@ function createStateBackendFromEnv(env = process.env, client) {
3172
3183
  ...expectedUpdatedAt ? { expectedUpdatedAt } : {}
3173
3184
  });
3174
3185
  },
3186
+ async getAgentState(tenantId2, agent) {
3187
+ const result = await transport.query(anyApi.agentStates.get, {
3188
+ tenantId: requireTenant(tenantId2),
3189
+ agent: requireNonEmpty(agent, "agent")
3190
+ });
3191
+ return result ?? null;
3192
+ },
3193
+ async saveAgentState(tenantId2, state, expectedRevision) {
3194
+ await transport.mutation(anyApi.agentStates.save, {
3195
+ tenantId: requireTenant(tenantId2),
3196
+ state,
3197
+ ...expectedRevision === void 0 ? {} : { expectedRevision }
3198
+ });
3199
+ },
3200
+ async resetAgentState(tenantId2, agent) {
3201
+ await transport.mutation(anyApi.agentStates.reset, {
3202
+ tenantId: requireTenant(tenantId2),
3203
+ agent: requireNonEmpty(agent, "agent")
3204
+ });
3205
+ },
3175
3206
  async getRepoDoc(tenantId2, kind) {
3176
3207
  const result = await transport.query(anyApi.repoDocs.get, {
3177
3208
  tenantId: requireTenant(tenantId2),
@@ -14582,6 +14613,18 @@ function mergeLoopDefinitions(repositoryLoops, runtimeLoops) {
14582
14613
  }
14583
14614
  return [...byId.values()].sort((left, right) => left.id.localeCompare(right.id));
14584
14615
  }
14616
+ function loopWakeRegistrationIds(loops) {
14617
+ return loops.filter((loop) => loop.enabled && loop.trigger.type === "schedule").map((loop) => loop.id).sort();
14618
+ }
14619
+ async function syncLoopWakeRegistrations(backend, tenantId2, loops, updatedAt, log2) {
14620
+ try {
14621
+ await backend.replaceLoopWakeRegistrations(tenantId2, loopWakeRegistrationIds(loops), updatedAt);
14622
+ return true;
14623
+ } catch {
14624
+ log2("\u2192 kody: Convex Loop wake registration backfill skipped; existing Loop execution continues");
14625
+ return false;
14626
+ }
14627
+ }
14585
14628
  function loopDispatchSlot(loop, now, force, nonce) {
14586
14629
  return force ? `manual:${now.toISOString()}:${nonce}` : dueSlot(loop, now);
14587
14630
  }
@@ -14643,6 +14686,14 @@ var init_dispatchLoops = __esm({
14643
14686
  const requestedLoopId = typeof ctx.args.loop === "string" ? ctx.args.loop.trim() : "";
14644
14687
  const backend = createStateBackendFromEnv();
14645
14688
  const loops = mergeLoopDefinitions(listLoopDefinitions(ctx.cwd), await backend.listLoops(tenantId2));
14689
+ await syncLoopWakeRegistrations(
14690
+ backend,
14691
+ tenantId2,
14692
+ loops,
14693
+ now.toISOString(),
14694
+ (message) => process.stderr.write(`${message}
14695
+ `)
14696
+ );
14646
14697
  const due = selectRunnableLoops(loops, now, {
14647
14698
  force,
14648
14699
  ...requestedLoopId ? { loopId: requestedLoopId } : {}
@@ -16438,14 +16489,105 @@ ${truncate2(issue2.body, FINDING_BODY_MAX_BYTES)}`;
16438
16489
  }
16439
16490
  });
16440
16491
 
16441
- // src/scripts/kodyVariables.ts
16492
+ // src/scripts/loadLiveAgent.ts
16442
16493
  import * as fs43 from "fs";
16494
+ function tenant(config) {
16495
+ const [envOwner, envRepo] = (process.env.GITHUB_REPOSITORY ?? "").split("/");
16496
+ const owner = config.github?.owner?.trim() || envOwner;
16497
+ const repo = config.github?.repo?.trim() || envRepo;
16498
+ if (!owner || !repo) throw new Error("Repository identity is required for live Agent execution");
16499
+ return `${owner}/${repo}`;
16500
+ }
16501
+ function frontmatter(raw) {
16502
+ const match = /^---\n([\s\S]*?)\n---/.exec(raw);
16503
+ if (!match) return {};
16504
+ const result = {};
16505
+ for (const line of match[1].split("\n")) {
16506
+ const at = line.indexOf(":");
16507
+ if (at < 0) continue;
16508
+ const key = line.slice(0, at).trim();
16509
+ const value = line.slice(at + 1).trim();
16510
+ result[key] = value.startsWith("[") ? value.slice(1, -1).split(",").map((entry) => entry.trim()).filter(Boolean) : value;
16511
+ }
16512
+ return result;
16513
+ }
16514
+ function guidanceBody(row, agent) {
16515
+ if (typeof row.doc?.body !== "string") return null;
16516
+ const metadata = frontmatter(row.doc.body);
16517
+ const audience = Array.isArray(metadata.agent) ? metadata.agent : [metadata.agent ?? "*"];
16518
+ if (!audience.includes("*") && !audience.includes(agent)) return null;
16519
+ return row.doc.body.replace(/^---\n[\s\S]*?\n---\n?/, "").trim();
16520
+ }
16521
+ function intentBody(row) {
16522
+ if (typeof row.doc?.body !== "string") return null;
16523
+ return row.doc.body.replace(/^---\n[\s\S]*?\n---\n?/, "").trim() || null;
16524
+ }
16525
+ var loadLiveAgent;
16526
+ var init_loadLiveAgent = __esm({
16527
+ "src/scripts/loadLiveAgent.ts"() {
16528
+ "use strict";
16529
+ init_definition_paths();
16530
+ init_agents();
16531
+ init_state_backend();
16532
+ loadLiveAgent = async (ctx, profile) => {
16533
+ const agent = String(ctx.args.agent ?? ctx.data.jobAgent ?? "").trim();
16534
+ if (!agent) throw new Error("loadLiveAgent: agent is required");
16535
+ const file = resolveAgentFile2(ctx.cwd, agent, agentsRoot(ctx.cwd));
16536
+ const raw = fs43.existsSync(file) ? fs43.readFileSync(file, "utf8") : "";
16537
+ const metadata = frontmatter(raw);
16538
+ const assignedIntent = typeof metadata.primaryIntent === "string" ? metadata.primaryIntent : "";
16539
+ const requestedIntent = String(ctx.args.intent ?? "").trim();
16540
+ const intent = requestedIntent || assignedIntent;
16541
+ if (!intent || requestedIntent && assignedIntent !== requestedIntent) {
16542
+ throw new Error(`Live Agent '${agent}' does not have the requested primary Intent`);
16543
+ }
16544
+ const backend = createStateBackendFromEnv();
16545
+ const tenantId2 = tenant(ctx.config);
16546
+ const [stateRow, intentRow, policies, constraints, context] = await Promise.all([
16547
+ backend.getAgentState(tenantId2, agent),
16548
+ backend.getRepoDoc(tenantId2, `intent:${intent}`),
16549
+ backend.listRepoDocs(tenantId2, "policy:"),
16550
+ backend.listRepoDocs(tenantId2, "constraint:"),
16551
+ backend.listRepoDocs(tenantId2, "context:")
16552
+ ]);
16553
+ if (!stateRow) throw new Error(`Live Agent '${agent}' has no AgentState`);
16554
+ const state = stateRow.state;
16555
+ const selectedIntentBody = intentRow ? intentBody(intentRow) : null;
16556
+ if (!selectedIntentBody) throw new Error(`Primary Intent '${intent}' is missing`);
16557
+ const render = (rows) => rows.map((row) => guidanceBody(row, agent)).filter(Boolean).join("\n\n") || "None assigned.";
16558
+ ctx.data.agentIdentity = loadAgentIdentity(ctx.cwd, agent);
16559
+ ctx.data.liveAgentIntent = selectedIntentBody;
16560
+ ctx.data.liveAgentPolicies = render(policies);
16561
+ ctx.data.liveAgentConstraints = render(constraints);
16562
+ ctx.data.liveAgentContext = render(context);
16563
+ ctx.data.liveAgentCapabilities = Array.isArray(metadata.capabilities) ? metadata.capabilities.map((slug) => `- ${slug}`).join("\n") : "None assigned.";
16564
+ ctx.data.liveAgentSlug = agent;
16565
+ ctx.data.liveAgentPreviousRevision = Number(state.revision ?? 0);
16566
+ ctx.data.jobState = {
16567
+ state: {
16568
+ version: 1,
16569
+ rev: Number(state.revision ?? 0),
16570
+ cursor: String(state.cursor ?? "idle"),
16571
+ data: state.data ?? {},
16572
+ done: false
16573
+ }
16574
+ };
16575
+ ctx.data.jobStateJson = JSON.stringify(state, null, 2);
16576
+ ctx.data.capabilityTools = ["start_capability"];
16577
+ ctx.data.capabilityToolMode = "lock";
16578
+ profile.claudeCode.enableSubmitTool = true;
16579
+ };
16580
+ }
16581
+ });
16582
+
16583
+ // src/scripts/kodyVariables.ts
16584
+ import * as fs44 from "fs";
16443
16585
  import * as path41 from "path";
16444
16586
  function readKodyVariables(cwd) {
16445
16587
  const full = path41.join(cwd, KODY_VARIABLES_REL_PATH);
16446
16588
  let raw;
16447
16589
  try {
16448
- raw = fs43.readFileSync(full, "utf-8");
16590
+ raw = fs44.readFileSync(full, "utf-8");
16449
16591
  } catch {
16450
16592
  return {};
16451
16593
  }
@@ -16470,7 +16612,7 @@ var init_kodyVariables = __esm({
16470
16612
  });
16471
16613
 
16472
16614
  // src/scripts/loadQaContext.ts
16473
- import * as fs44 from "fs";
16615
+ import * as fs45 from "fs";
16474
16616
  import * as path42 from "path";
16475
16617
  function parseSlugList(value) {
16476
16618
  const inner = value.startsWith("[") && value.endsWith("]") ? value.slice(1, -1) : value;
@@ -16501,17 +16643,17 @@ function readProfileAgents(raw) {
16501
16643
  }
16502
16644
  function readProfile(cwd) {
16503
16645
  const dir = path42.join(cwd, CONTEXT_DIR_REL_PATH);
16504
- if (!fs44.existsSync(dir)) return "";
16646
+ if (!fs45.existsSync(dir)) return "";
16505
16647
  let entries;
16506
16648
  try {
16507
- entries = fs44.readdirSync(dir).filter((f) => f.endsWith(".md")).sort();
16649
+ entries = fs45.readdirSync(dir).filter((f) => f.endsWith(".md")).sort();
16508
16650
  } catch {
16509
16651
  return "";
16510
16652
  }
16511
16653
  const blocks = [];
16512
16654
  for (const file of entries) {
16513
16655
  try {
16514
- const raw = fs44.readFileSync(path42.join(dir, file), "utf-8");
16656
+ const raw = fs45.readFileSync(path42.join(dir, file), "utf-8");
16515
16657
  const { agent, body } = readProfileAgents(raw);
16516
16658
  if (!agent.includes(QA_AGENT) && !agent.includes(ALL_AGENTS)) continue;
16517
16659
  blocks.push(`## ${file}
@@ -16561,7 +16703,7 @@ var init_loadQaContext = __esm({
16561
16703
 
16562
16704
  // src/scripts/loadSimpleCapability.ts
16563
16705
  import { randomUUID as randomUUID2 } from "crypto";
16564
- import * as fs45 from "fs";
16706
+ import * as fs46 from "fs";
16565
16707
  import * as os6 from "os";
16566
16708
  import * as path43 from "path";
16567
16709
  function registerCapabilitySubagents(profile, toolRoot, toolFiles) {
@@ -16576,7 +16718,7 @@ function registerCapabilitySubagents(profile, toolRoot, toolFiles) {
16576
16718
  profile.subagentTemplates = {
16577
16719
  ...profile.subagentTemplates ?? {},
16578
16720
  ...Object.fromEntries(
16579
- subagentFiles.map(({ name, file }) => [name, fs45.readFileSync(path43.join(toolRoot, file), "utf-8")])
16721
+ subagentFiles.map(({ name, file }) => [name, fs46.readFileSync(path43.join(toolRoot, file), "utf-8")])
16580
16722
  )
16581
16723
  };
16582
16724
  if (!profile.claudeCode.tools.includes("Agent")) {
@@ -16617,10 +16759,10 @@ function scalar(value) {
16617
16759
  return value;
16618
16760
  }
16619
16761
  function listFiles(root) {
16620
- if (!fs45.existsSync(root)) return [];
16762
+ if (!fs46.existsSync(root)) return [];
16621
16763
  const files = [];
16622
16764
  const visit = (dir) => {
16623
- for (const entry of fs45.readdirSync(dir, { withFileTypes: true })) {
16765
+ for (const entry of fs46.readdirSync(dir, { withFileTypes: true })) {
16624
16766
  const absolute = path43.join(dir, entry.name);
16625
16767
  if (entry.isSymbolicLink()) continue;
16626
16768
  if (entry.isDirectory()) visit(absolute);
@@ -16712,7 +16854,7 @@ var init_loadSimpleCapability = __esm({
16712
16854
  ...skillFiles.flatMap((file) => [
16713
16855
  `### ${file}`,
16714
16856
  "",
16715
- fs45.readFileSync(path43.join(skillRoot, file), "utf-8"),
16857
+ fs46.readFileSync(path43.join(skillRoot, file), "utf-8"),
16716
16858
  ""
16717
16859
  ])
16718
16860
  ] : [],
@@ -16752,7 +16894,7 @@ var init_loadSimpleCapability = __esm({
16752
16894
  });
16753
16895
 
16754
16896
  // src/taskContext.ts
16755
- import * as fs46 from "fs";
16897
+ import * as fs47 from "fs";
16756
16898
  import * as path44 from "path";
16757
16899
  function buildTaskContext(args) {
16758
16900
  return {
@@ -16769,9 +16911,9 @@ function buildTaskContext(args) {
16769
16911
  function persistTaskContext(cwd, ctx) {
16770
16912
  try {
16771
16913
  const dir = runtimeStatePath(cwd, "agent-runs", ctx.runId);
16772
- fs46.mkdirSync(dir, { recursive: true });
16914
+ fs47.mkdirSync(dir, { recursive: true });
16773
16915
  const file = path44.join(dir, "task-context.json");
16774
- fs46.writeFileSync(file, `${JSON.stringify(ctx, null, 2)}
16916
+ fs47.writeFileSync(file, `${JSON.stringify(ctx, null, 2)}
16775
16917
  `);
16776
16918
  return file;
16777
16919
  } catch (err) {
@@ -17692,7 +17834,7 @@ var init_parseReproOutput = __esm({
17692
17834
  });
17693
17835
 
17694
17836
  // src/scripts/parseSimpleCapabilityOutput.ts
17695
- import * as fs47 from "fs";
17837
+ import * as fs48 from "fs";
17696
17838
  function acceptAuthoritativeCapabilityOutput(ctx, profile, output) {
17697
17839
  ctx.data.agentDone = true;
17698
17840
  delete ctx.data.agentFailureReason;
@@ -17709,11 +17851,11 @@ function stringList2(value) {
17709
17851
  return Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
17710
17852
  }
17711
17853
  function readOutputFile(outputPath) {
17712
- if (!outputPath || !fs47.existsSync(outputPath)) return { found: false };
17854
+ if (!outputPath || !fs48.existsSync(outputPath)) return { found: false };
17713
17855
  try {
17714
- return { found: true, value: JSON.parse(fs47.readFileSync(outputPath, "utf-8")) };
17856
+ return { found: true, value: JSON.parse(fs48.readFileSync(outputPath, "utf-8")) };
17715
17857
  } finally {
17716
- fs47.rmSync(outputPath, { force: true });
17858
+ fs48.rmSync(outputPath, { force: true });
17717
17859
  }
17718
17860
  }
17719
17861
  function parseOutput(text2) {
@@ -18335,7 +18477,7 @@ var init_postResearchComment = __esm({
18335
18477
  });
18336
18478
 
18337
18479
  // src/scripts/prepareBrowserAuth.ts
18338
- import * as fs48 from "fs";
18480
+ import * as fs49 from "fs";
18339
18481
  import * as os7 from "os";
18340
18482
  import * as path45 from "path";
18341
18483
  function appendAuthMessage(ctx, message) {
@@ -18381,8 +18523,8 @@ async function githubJson(url, token, checkName) {
18381
18523
  throw new Error(`GitHub ${checkName} check failed`);
18382
18524
  }
18383
18525
  function writeKodyStorageState(input) {
18384
- const directory = fs48.mkdtempSync(path45.join(os7.tmpdir(), "kody-browser-auth-"));
18385
- fs48.chmodSync(directory, 448);
18526
+ const directory = fs49.mkdtempSync(path45.join(os7.tmpdir(), "kody-browser-auth-"));
18527
+ fs49.chmodSync(directory, 448);
18386
18528
  const file = path45.join(directory, "storage-state.json");
18387
18529
  const now = Date.now();
18388
18530
  const repoEntry = {
@@ -18414,7 +18556,7 @@ function writeKodyStorageState(input) {
18414
18556
  }
18415
18557
  ]
18416
18558
  };
18417
- fs48.writeFileSync(file, JSON.stringify(storageState), { mode: 384 });
18559
+ fs49.writeFileSync(file, JSON.stringify(storageState), { mode: 384 });
18418
18560
  return { directory, file, auth };
18419
18561
  }
18420
18562
  function parseSetCookie(value, hostname) {
@@ -18444,10 +18586,10 @@ function parseSetCookie(value, hostname) {
18444
18586
  }
18445
18587
  function writeCookieStorageState(targetUrl, setCookies) {
18446
18588
  const target = new URL(targetUrl);
18447
- const directory = fs48.mkdtempSync(path45.join(os7.tmpdir(), "kody-browser-auth-"));
18448
- fs48.chmodSync(directory, 448);
18589
+ const directory = fs49.mkdtempSync(path45.join(os7.tmpdir(), "kody-browser-auth-"));
18590
+ fs49.chmodSync(directory, 448);
18449
18591
  const file = path45.join(directory, "storage-state.json");
18450
- fs48.writeFileSync(
18592
+ fs49.writeFileSync(
18451
18593
  file,
18452
18594
  JSON.stringify({
18453
18595
  cookies: setCookies.map((cookie) => parseSetCookie(cookie, target.hostname)),
@@ -18468,9 +18610,9 @@ function currentStorageStatePath(args) {
18468
18610
  function browserSessionCookieHeader(profile, targetUrl) {
18469
18611
  const playwright = profile.claudeCode.mcpServers.find((server) => server.name === "playwright");
18470
18612
  const storagePath = currentStorageStatePath(playwright?.args ?? []);
18471
- if (!storagePath || !fs48.existsSync(storagePath)) return void 0;
18613
+ if (!storagePath || !fs49.existsSync(storagePath)) return void 0;
18472
18614
  const hostname = new URL(targetUrl).hostname;
18473
- const state = JSON.parse(fs48.readFileSync(storagePath, "utf-8"));
18615
+ const state = JSON.parse(fs49.readFileSync(storagePath, "utf-8"));
18474
18616
  const cookies = (state.cookies ?? []).filter((cookie) => {
18475
18617
  const domain = cookie.domain.replace(/^\./, "");
18476
18618
  return hostname === domain || hostname.endsWith(`.${domain}`);
@@ -18517,10 +18659,29 @@ async function prepareAccountCredentials(ctx, profile, input) {
18517
18659
  appendAuthMessage(ctx, "Auth: the QA account's required model credentials are already prepared by the engine.");
18518
18660
  return true;
18519
18661
  }
18662
+ async function prepareAccountModelSettings(ctx, profile, input) {
18663
+ const cookie = browserSessionCookieHeader(profile, input.targetUrl);
18664
+ if (!cookie) {
18665
+ appendAuthMessage(ctx, "Auth: QA Chat model setup could not run because the app session is missing.");
18666
+ return false;
18667
+ }
18668
+ const origin = browserOrigin(input.targetUrl);
18669
+ const response = await fetch(`${origin}/api/kody/models`, {
18670
+ method: "PUT",
18671
+ headers: { "content-type": "application/json", cookie, origin },
18672
+ body: JSON.stringify(input.settings)
18673
+ });
18674
+ if (!response.ok) {
18675
+ appendAuthMessage(ctx, `Auth: QA Chat model setup returned ${response.status}.`);
18676
+ return false;
18677
+ }
18678
+ appendAuthMessage(ctx, "Auth: the QA account's Chat model is already prepared by the engine.");
18679
+ return true;
18680
+ }
18520
18681
  function mergeStorageStates(existingPath, nextPath) {
18521
- if (existingPath === nextPath || !fs48.existsSync(existingPath)) return;
18522
- const existing = JSON.parse(fs48.readFileSync(existingPath, "utf-8"));
18523
- const next = JSON.parse(fs48.readFileSync(nextPath, "utf-8"));
18682
+ if (existingPath === nextPath || !fs49.existsSync(existingPath)) return;
18683
+ const existing = JSON.parse(fs49.readFileSync(existingPath, "utf-8"));
18684
+ const next = JSON.parse(fs49.readFileSync(nextPath, "utf-8"));
18524
18685
  const cookies = /* @__PURE__ */ new Map();
18525
18686
  for (const cookie of [...existing.cookies ?? [], ...next.cookies ?? []]) {
18526
18687
  cookies.set(`${cookie.name}\0${cookie.domain}\0${cookie.path}`, cookie);
@@ -18534,7 +18695,7 @@ function mergeStorageStates(existingPath, nextPath) {
18534
18695
  }
18535
18696
  origins.set(entry.origin, { origin: entry.origin, localStorage: [...localStorage.values()] });
18536
18697
  }
18537
- fs48.writeFileSync(
18698
+ fs49.writeFileSync(
18538
18699
  nextPath,
18539
18700
  JSON.stringify({ cookies: [...cookies.values()], origins: [...origins.values()] }),
18540
18701
  { mode: 384 }
@@ -18631,7 +18792,7 @@ async function prepareKodyRepositoryBrowserAuth(ctx, profile, input) {
18631
18792
  configurePlaywright(profile, state.file);
18632
18793
  const authDirectory = state.directory;
18633
18794
  registerRuntimeCleanup(ctx, () => {
18634
- fs48.rmSync(authDirectory, { recursive: true, force: true });
18795
+ fs49.rmSync(authDirectory, { recursive: true, force: true });
18635
18796
  });
18636
18797
  appendAuthMessage(
18637
18798
  ctx,
@@ -18639,7 +18800,7 @@ async function prepareKodyRepositoryBrowserAuth(ctx, profile, input) {
18639
18800
  );
18640
18801
  return true;
18641
18802
  } catch (error) {
18642
- if (state) fs48.rmSync(state.directory, { recursive: true, force: true });
18803
+ if (state) fs49.rmSync(state.directory, { recursive: true, force: true });
18643
18804
  const reason = error instanceof Error ? error.message : String(error);
18644
18805
  appendAuthMessage(
18645
18806
  ctx,
@@ -18666,11 +18827,11 @@ async function prepareEmailPasswordBrowserAuth(ctx, profile, input) {
18666
18827
  state = writeCookieStorageState(input.targetUrl, cookies);
18667
18828
  configurePlaywright(profile, state.file);
18668
18829
  const authDirectory = state.directory;
18669
- registerRuntimeCleanup(ctx, () => fs48.rmSync(authDirectory, { recursive: true, force: true }));
18830
+ registerRuntimeCleanup(ctx, () => fs49.rmSync(authDirectory, { recursive: true, force: true }));
18670
18831
  ctx.data.qaAuthBlock = "Auth: the app is already signed in through an engine-provided browser session. The login credentials are not available to you; never request, reveal, or report them.";
18671
18832
  return true;
18672
18833
  } catch (error) {
18673
- if (state) fs48.rmSync(state.directory, { recursive: true, force: true });
18834
+ if (state) fs49.rmSync(state.directory, { recursive: true, force: true });
18674
18835
  const reason = error instanceof Error ? error.message : String(error);
18675
18836
  ctx.data.qaAuthBlock = `Auth: the engine could not prepare the app login (${reason}). Note this authenticated surface as a gap.`;
18676
18837
  return false;
@@ -18914,6 +19075,12 @@ var init_prepareSimpleCapabilityRuntime = __esm({
18914
19075
  targetUrl
18915
19076
  });
18916
19077
  }
19078
+ if (requirements.qaAccountModelSettings) {
19079
+ await prepareAccountModelSettings(ctx, profile, {
19080
+ settings: requirements.qaAccountModelSettings,
19081
+ targetUrl
19082
+ });
19083
+ }
18917
19084
  }
18918
19085
  if (requirements.githubTestToken) {
18919
19086
  const capabilityInput = ctx.data.capabilityInput && typeof ctx.data.capabilityInput === "object" && !Array.isArray(ctx.data.capabilityInput) ? ctx.data.capabilityInput : {};
@@ -20578,7 +20745,7 @@ var init_tickShellRunner = __esm({
20578
20745
  });
20579
20746
 
20580
20747
  // src/scripts/runScheduledImplementationTick.ts
20581
- import * as fs49 from "fs";
20748
+ import * as fs50 from "fs";
20582
20749
  import * as path48 from "path";
20583
20750
  var runScheduledImplementationTick;
20584
20751
  var init_runScheduledImplementationTick = __esm({
@@ -20607,7 +20774,7 @@ var init_runScheduledImplementationTick = __esm({
20607
20774
  return;
20608
20775
  }
20609
20776
  const shellPath = path48.join(profile.dir, shell);
20610
- if (!fs49.existsSync(shellPath)) {
20777
+ if (!fs50.existsSync(shellPath)) {
20611
20778
  ctx.output.exitCode = 99;
20612
20779
  ctx.output.reason = `runScheduledImplementationTick: shell not found: ${shell} (looked in ${profile.dir})`;
20613
20780
  return;
@@ -20639,13 +20806,13 @@ var init_runScheduledImplementationTick = __esm({
20639
20806
 
20640
20807
  // src/scripts/runSimpleCapabilityScript.ts
20641
20808
  import { spawnSync as spawnSync3 } from "child_process";
20642
- import * as fs50 from "fs";
20809
+ import * as fs51 from "fs";
20643
20810
  function formatDuration2(timeoutMs) {
20644
20811
  return timeoutMs % 6e4 === 0 ? `${timeoutMs / 6e4} minutes` : `${timeoutMs}ms`;
20645
20812
  }
20646
20813
  function isRegularFile2(filePath) {
20647
20814
  try {
20648
- const stat = fs50.lstatSync(filePath);
20815
+ const stat = fs51.lstatSync(filePath);
20649
20816
  return stat.isFile() && !stat.isSymbolicLink();
20650
20817
  } catch {
20651
20818
  return false;
@@ -20724,7 +20891,7 @@ var init_runSimpleCapabilityScript = __esm({
20724
20891
  });
20725
20892
 
20726
20893
  // src/scripts/runTickScript.ts
20727
- import * as fs51 from "fs";
20894
+ import * as fs52 from "fs";
20728
20895
  import * as path49 from "path";
20729
20896
  var runTickScript;
20730
20897
  var init_runTickScript = __esm({
@@ -20758,7 +20925,7 @@ var init_runTickScript = __esm({
20758
20925
  return;
20759
20926
  }
20760
20927
  const scriptPath = path49.isAbsolute(tickScript) ? tickScript : path49.join(ctx.cwd, tickScript);
20761
- if (!fs51.existsSync(scriptPath)) {
20928
+ if (!fs52.existsSync(scriptPath)) {
20762
20929
  ctx.output.exitCode = 99;
20763
20930
  ctx.output.reason = `runTickScript: tickScript not found: ${scriptPath}`;
20764
20931
  return;
@@ -20806,6 +20973,41 @@ var init_saveManagedGoalState = __esm({
20806
20973
  }
20807
20974
  });
20808
20975
 
20976
+ // src/scripts/saveLiveAgentState.ts
20977
+ var saveLiveAgentState;
20978
+ var init_saveLiveAgentState = __esm({
20979
+ "src/scripts/saveLiveAgentState.ts"() {
20980
+ "use strict";
20981
+ init_state_backend();
20982
+ saveLiveAgentState = async (ctx, _profile, agentResult) => {
20983
+ const agent = String(ctx.data.liveAgentSlug ?? "");
20984
+ const previousRevision = Number(ctx.data.liveAgentPreviousRevision ?? 0);
20985
+ const next = ctx.data.nextJobState;
20986
+ if (!agent || !next || typeof next.cursor !== "string" || !next.cursor) {
20987
+ throw new Error(String(ctx.data.nextStateParseError ?? "Live Agent did not submit valid continuation state"));
20988
+ }
20989
+ const [envOwner, envRepo] = (process.env.GITHUB_REPOSITORY ?? "").split("/");
20990
+ const owner = ctx.config.github?.owner?.trim() || envOwner;
20991
+ const repo = ctx.config.github?.repo?.trim() || envRepo;
20992
+ if (!owner || !repo) throw new Error("Repository identity is required for live Agent state");
20993
+ const summary = (agentResult?.finalText ?? "").trim().slice(0, 1e3);
20994
+ await createStateBackendFromEnv().saveAgentState(
20995
+ `${owner}/${repo}`,
20996
+ {
20997
+ version: 1,
20998
+ agent,
20999
+ revision: previousRevision + 1,
21000
+ cursor: next.cursor,
21001
+ summary,
21002
+ data: next.data && typeof next.data === "object" ? next.data : {},
21003
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
21004
+ },
21005
+ previousRevision
21006
+ );
21007
+ };
21008
+ }
21009
+ });
21010
+
20809
21011
  // src/scripts/setCommentTarget.ts
20810
21012
  var setCommentTarget;
20811
21013
  var init_setCommentTarget = __esm({
@@ -21923,7 +22125,7 @@ var init_warmupMcp = __esm({
21923
22125
  });
21924
22126
 
21925
22127
  // src/scripts/writeAgentRunSummary.ts
21926
- import * as fs52 from "fs";
22128
+ import * as fs53 from "fs";
21927
22129
  var writeAgentRunSummary;
21928
22130
  var init_writeAgentRunSummary = __esm({
21929
22131
  "src/scripts/writeAgentRunSummary.ts"() {
@@ -21949,7 +22151,7 @@ var init_writeAgentRunSummary = __esm({
21949
22151
  if (reason) lines.push(`- **Reason:** ${reason}`);
21950
22152
  lines.push("");
21951
22153
  try {
21952
- fs52.appendFileSync(summaryPath, `${lines.join("\n")}
22154
+ fs53.appendFileSync(summaryPath, `${lines.join("\n")}
21953
22155
  `);
21954
22156
  } catch {
21955
22157
  }
@@ -22106,6 +22308,7 @@ var init_scripts = __esm({
22106
22308
  init_loadIssueStateComment();
22107
22309
  init_loadJobFromFile();
22108
22310
  init_loadLinkedFinding();
22311
+ init_loadLiveAgent();
22109
22312
  init_loadMemoryContext();
22110
22313
  init_loadPriorArt();
22111
22314
  init_loadQaContext();
@@ -22154,6 +22357,7 @@ var init_scripts = __esm({
22154
22357
  init_runSimpleCapabilityScript();
22155
22358
  init_runTickScript();
22156
22359
  init_saveManagedGoalState();
22360
+ init_saveLiveAgentState();
22157
22361
  init_saveTaskState();
22158
22362
  init_setCommentTarget();
22159
22363
  init_setLifecycleLabel();
@@ -22193,6 +22397,7 @@ var init_scripts = __esm({
22193
22397
  loadConventions,
22194
22398
  loadCoverageRules,
22195
22399
  loadLinkedFinding,
22400
+ loadLiveAgent,
22196
22401
  loadMemoryContext,
22197
22402
  loadPriorArt,
22198
22403
  loadQaContext,
@@ -22228,6 +22433,7 @@ var init_scripts = __esm({
22228
22433
  saveManagedGoalState
22229
22434
  };
22230
22435
  postflightScripts = {
22436
+ saveLiveAgentState,
22231
22437
  parseSimpleCapabilityOutput,
22232
22438
  parseAgentResult: parseAgentResult2,
22233
22439
  parseIssueStateFromAgentResult,
@@ -22287,7 +22493,7 @@ var init_scripts = __esm({
22287
22493
  });
22288
22494
 
22289
22495
  // src/stateWorkspace.ts
22290
- import * as fs53 from "fs";
22496
+ import * as fs54 from "fs";
22291
22497
  import * as path51 from "path";
22292
22498
  function tenantId(config) {
22293
22499
  const owner = config.github?.owner?.trim() || process.env.GITHUB_REPOSITORY?.split("/")[0]?.trim();
@@ -22296,8 +22502,8 @@ function tenantId(config) {
22296
22502
  }
22297
22503
  function writeRuntimeFile(cwd, relativePath, content) {
22298
22504
  const target = path51.join(cwd, RUNTIME_ROOT, relativePath);
22299
- fs53.mkdirSync(path51.dirname(target), { recursive: true });
22300
- fs53.writeFileSync(target, content, "utf8");
22505
+ fs54.mkdirSync(path51.dirname(target), { recursive: true });
22506
+ fs54.writeFileSync(target, content, "utf8");
22301
22507
  }
22302
22508
  function record(value) {
22303
22509
  return value && typeof value === "object" && !Array.isArray(value) ? value : null;
@@ -22318,8 +22524,8 @@ function memoryIndex(docs) {
22318
22524
  if (lines.length === 0) return "";
22319
22525
  return ["# Kody memory index", "", "One line per backend memory document.", "", ...lines, ""].join("\n");
22320
22526
  }
22321
- async function hydratePrefix(backend, tenant, cwd, prefix) {
22322
- const docs = await backend.listRepoDocs(tenant, prefix);
22527
+ async function hydratePrefix(backend, tenant2, cwd, prefix) {
22528
+ const docs = await backend.listRepoDocs(tenant2, prefix);
22323
22529
  for (const doc of docs) {
22324
22530
  const slug = doc.kind.slice(prefix.length);
22325
22531
  if (!/^[a-z0-9][a-z0-9_-]{0,63}$/.test(slug)) continue;
@@ -22332,8 +22538,8 @@ async function hydratePrefix(backend, tenant, cwd, prefix) {
22332
22538
  if (index) writeRuntimeFile(cwd, "memory/INDEX.md", index);
22333
22539
  }
22334
22540
  }
22335
- async function hydrateSingleton(backend, tenant, cwd, kind, relativePath) {
22336
- const doc = await backend.getRepoDoc(tenant, kind);
22541
+ async function hydrateSingleton(backend, tenant2, cwd, kind, relativePath) {
22542
+ const doc = await backend.getRepoDoc(tenant2, kind);
22337
22543
  if (!doc) return;
22338
22544
  const body = stringField6(doc.doc, "body");
22339
22545
  if (body !== null) {
@@ -22343,8 +22549,8 @@ async function hydrateSingleton(backend, tenant, cwd, kind, relativePath) {
22343
22549
  writeRuntimeFile(cwd, relativePath, `${JSON.stringify(doc.doc, null, 2)}
22344
22550
  `);
22345
22551
  }
22346
- async function hydrateWorkflows(backend, tenant, cwd) {
22347
- for (const workflow of await backend.listWorkflows(tenant)) {
22552
+ async function hydrateWorkflows(backend, tenant2, cwd) {
22553
+ for (const workflow of await backend.listWorkflows(tenant2)) {
22348
22554
  if (!/^[a-z0-9][a-z0-9_-]{0,79}$/.test(workflow.workflowId)) continue;
22349
22555
  writeRuntimeFile(
22350
22556
  cwd,
@@ -22355,25 +22561,25 @@ async function hydrateWorkflows(backend, tenant, cwd) {
22355
22561
  }
22356
22562
  }
22357
22563
  async function hydrateStateWorkspace(config, cwd, backendOverride) {
22358
- const tenant = tenantId(config);
22564
+ const tenant2 = tenantId(config);
22359
22565
  const configured = hasStateBackendConfig();
22360
- if (!tenant || !configured) {
22566
+ if (!tenant2 || !configured) {
22361
22567
  if (process.env.GITHUB_ACTIONS === "true")
22362
22568
  throw new Error("Kody backend access is required for runtime workspace documents");
22363
22569
  return;
22364
22570
  }
22365
- const key = `${path51.resolve(cwd)}|${tenant}`;
22571
+ const key = `${path51.resolve(cwd)}|${tenant2}`;
22366
22572
  if (hydratedWorkspaces.has(key)) return;
22367
22573
  const backend = backendOverride ?? createStateBackendFromEnv();
22368
22574
  const root = path51.join(cwd, RUNTIME_ROOT);
22369
- fs53.rmSync(root, { recursive: true, force: true });
22575
+ fs54.rmSync(root, { recursive: true, force: true });
22370
22576
  await Promise.all([
22371
- hydratePrefix(backend, tenant, cwd, "context:"),
22372
- hydratePrefix(backend, tenant, cwd, "memory:"),
22373
- hydrateSingleton(backend, tenant, cwd, "instructions", "instructions.md"),
22374
- hydrateSingleton(backend, tenant, cwd, "system-prompt", "system-prompt.md"),
22375
- hydrateSingleton(backend, tenant, cwd, "variables", "variables.json"),
22376
- hydrateWorkflows(backend, tenant, cwd)
22577
+ hydratePrefix(backend, tenant2, cwd, "context:"),
22578
+ hydratePrefix(backend, tenant2, cwd, "memory:"),
22579
+ hydrateSingleton(backend, tenant2, cwd, "instructions", "instructions.md"),
22580
+ hydrateSingleton(backend, tenant2, cwd, "system-prompt", "system-prompt.md"),
22581
+ hydrateSingleton(backend, tenant2, cwd, "variables", "variables.json"),
22582
+ hydrateWorkflows(backend, tenant2, cwd)
22377
22583
  ]);
22378
22584
  hydratedWorkspaces.add(key);
22379
22585
  }
@@ -22453,7 +22659,7 @@ var init_tools = __esm({
22453
22659
 
22454
22660
  // src/executor.ts
22455
22661
  import { spawn as spawn8 } from "child_process";
22456
- import * as fs54 from "fs";
22662
+ import * as fs55 from "fs";
22457
22663
  import * as os8 from "os";
22458
22664
  import * as path52 from "path";
22459
22665
  function isMutatingPostflight(scriptName) {
@@ -23240,7 +23446,7 @@ function resolveProfilePath(profileName, cwd = process.cwd()) {
23240
23446
  // fallback
23241
23447
  ];
23242
23448
  for (const c of candidates) {
23243
- if (fs54.existsSync(c)) return c;
23449
+ if (fs55.existsSync(c)) return c;
23244
23450
  }
23245
23451
  return candidates[0];
23246
23452
  }
@@ -23356,7 +23562,7 @@ function resolveShellTimeoutMs(entry) {
23356
23562
  async function runShellEntry(entry, ctx, profile) {
23357
23563
  const shellName = entry.shell;
23358
23564
  const shellPath = path52.join(profile.dir, shellName);
23359
- if (!fs54.existsSync(shellPath)) {
23565
+ if (!fs55.existsSync(shellPath)) {
23360
23566
  ctx.skipAgent = true;
23361
23567
  ctx.output.exitCode = 99;
23362
23568
  ctx.output.reason = `shell script not found: ${shellName} (looked in ${profile.dir})`;
@@ -23430,9 +23636,9 @@ async function runShellEntry(entry, ctx, profile) {
23430
23636
  }
23431
23637
  let sideChannelText = "";
23432
23638
  try {
23433
- if (fs54.existsSync(outputFile)) {
23434
- sideChannelText = fs54.readFileSync(outputFile, "utf-8");
23435
- fs54.rmSync(outputFile, { force: true });
23639
+ if (fs55.existsSync(outputFile)) {
23640
+ sideChannelText = fs55.readFileSync(outputFile, "utf-8");
23641
+ fs55.rmSync(outputFile, { force: true });
23436
23642
  }
23437
23643
  } catch {
23438
23644
  }
@@ -26165,7 +26371,7 @@ async function hydrateDefinitionsFromEnv(cwd = process.cwd(), env = process.env)
26165
26371
 
26166
26372
  // src/kody-cli.ts
26167
26373
  import { execFileSync as execFileSync24 } from "child_process";
26168
- import * as fs55 from "fs";
26374
+ import * as fs56 from "fs";
26169
26375
  import * as path53 from "path";
26170
26376
 
26171
26377
  // src/app-auth.ts
@@ -26213,26 +26419,6 @@ async function ghApp(jwt, apiPath, method = "GET") {
26213
26419
  }
26214
26420
  return await res.json();
26215
26421
  }
26216
- async function ghAppPage(authToken, apiPath) {
26217
- const res = await fetch(`${GH_API}${apiPath}`, {
26218
- headers: {
26219
- Authorization: `Bearer ${authToken}`,
26220
- Accept: "application/vnd.github+json",
26221
- "X-GitHub-Api-Version": "2022-11-28",
26222
- "User-Agent": "kody-engine"
26223
- }
26224
- });
26225
- if (!res.ok) {
26226
- const body = await res.text().catch(() => "");
26227
- throw new Error(
26228
- `GitHub App API GET ${apiPath} \u2192 ${res.status} ${res.statusText}${body ? `: ${body.slice(0, 200)}` : ""}`
26229
- );
26230
- }
26231
- return {
26232
- data: await res.json(),
26233
- hasNext: /rel="next"/.test(res.headers.get("link") ?? "")
26234
- };
26235
- }
26236
26422
  function readAppCreds(env = process.env) {
26237
26423
  const appId = env.KODY_APP_ID?.trim();
26238
26424
  const privateKey = env.KODY_APP_PRIVATE_KEY;
@@ -26257,36 +26443,6 @@ async function mintAppInstallationToken(creds) {
26257
26443
  const tok = await ghApp(jwt, `/app/installations/${installationId}/access_tokens`, "POST");
26258
26444
  return tok.token;
26259
26445
  }
26260
- async function discoverAppRepositories(creds) {
26261
- const jwt = buildAppJwt(creds.appId, creds.privateKey);
26262
- const installations = [];
26263
- for (let page = 1; ; page++) {
26264
- const result = await ghAppPage(jwt, `/app/installations?per_page=100&page=${page}`);
26265
- installations.push(...result.data.filter((item) => Number.isInteger(item.id) && item.id > 0));
26266
- if (!result.hasNext) break;
26267
- }
26268
- const byRepo = /* @__PURE__ */ new Map();
26269
- for (const installation of installations) {
26270
- const token = await mintAppInstallationToken({
26271
- appId: creds.appId,
26272
- privateKey: creds.privateKey,
26273
- installationId: String(installation.id)
26274
- });
26275
- for (let page = 1; ; page++) {
26276
- const result = await ghAppPage(
26277
- token,
26278
- `/installation/repositories?per_page=100&page=${page}`
26279
- );
26280
- for (const repository of result.data.repositories ?? []) {
26281
- const repo = repository.full_name?.trim();
26282
- if (!repo || !/^[^/\s]+\/[^/\s]+$/.test(repo)) continue;
26283
- byRepo.set(repo.toLowerCase(), { repo, token });
26284
- }
26285
- if (!result.hasNext) break;
26286
- }
26287
- }
26288
- return [...byRepo.values()].sort((left, right) => left.repo.localeCompare(right.repo));
26289
- }
26290
26446
 
26291
26447
  // src/kody-cli.ts
26292
26448
  init_capabilityFolders();
@@ -26968,9 +27124,9 @@ async function resolveAuthToken(env = process.env) {
26968
27124
  return void 0;
26969
27125
  }
26970
27126
  function detectPackageManager2(cwd) {
26971
- if (fs55.existsSync(path53.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
26972
- if (fs55.existsSync(path53.join(cwd, "yarn.lock"))) return "yarn";
26973
- if (fs55.existsSync(path53.join(cwd, "bun.lockb"))) return "bun";
27127
+ if (fs56.existsSync(path53.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
27128
+ if (fs56.existsSync(path53.join(cwd, "yarn.lock"))) return "yarn";
27129
+ if (fs56.existsSync(path53.join(cwd, "bun.lockb"))) return "bun";
26974
27130
  return "npm";
26975
27131
  }
26976
27132
  function shouldChainScheduledWatch(match) {
@@ -27073,8 +27229,8 @@ function postFailureTail(issueNumber, cwd, reason) {
27073
27229
  const logPath = lastRunLogPath(cwd);
27074
27230
  let tail = "";
27075
27231
  try {
27076
- if (fs55.existsSync(logPath)) {
27077
- const content = fs55.readFileSync(logPath, "utf-8");
27232
+ if (fs56.existsSync(logPath)) {
27233
+ const content = fs56.readFileSync(logPath, "utf-8");
27078
27234
  tail = content.slice(-3e3);
27079
27235
  }
27080
27236
  } catch {
@@ -27169,9 +27325,9 @@ async function runCi(argv) {
27169
27325
  forceRunCliArgs = { goal: envForceMessage };
27170
27326
  }
27171
27327
  }
27172
- if (!args.issueNumber && !autoFallback && !forceRunAction && !runRequestFanOut && eventName === "workflow_dispatch" && dispatchEventPath && fs55.existsSync(dispatchEventPath)) {
27328
+ if (!args.issueNumber && !autoFallback && !forceRunAction && !runRequestFanOut && eventName === "workflow_dispatch" && dispatchEventPath && fs56.existsSync(dispatchEventPath)) {
27173
27329
  try {
27174
- const evt = JSON.parse(fs55.readFileSync(dispatchEventPath, "utf-8"));
27330
+ const evt = JSON.parse(fs56.readFileSync(dispatchEventPath, "utf-8"));
27175
27331
  const inputs = objectValue2(evt.inputs);
27176
27332
  const issueInput = parseInt(String(inputs?.issue_number ?? ""), 10);
27177
27333
  const sessionInput = String(inputs?.sessionId ?? "");
@@ -27586,7 +27742,7 @@ init_repoWorkspace();
27586
27742
 
27587
27743
  // src/scripts/brainTurnLog.ts
27588
27744
  init_runtimePaths();
27589
- import * as fs56 from "fs";
27745
+ import * as fs57 from "fs";
27590
27746
  import * as path54 from "path";
27591
27747
  import posixPath4 from "path/posix";
27592
27748
  var live = /* @__PURE__ */ new Map();
@@ -27595,8 +27751,8 @@ function brainEventsFilePath(dir, chatId) {
27595
27751
  }
27596
27752
  function lastPersistedSeq(dir, chatId) {
27597
27753
  const p = brainEventsFilePath(dir, chatId);
27598
- if (!fs56.existsSync(p)) return 0;
27599
- const lines = fs56.readFileSync(p, "utf-8").split("\n").filter(Boolean);
27754
+ if (!fs57.existsSync(p)) return 0;
27755
+ const lines = fs57.readFileSync(p, "utf-8").split("\n").filter(Boolean);
27600
27756
  if (lines.length === 0) return 0;
27601
27757
  try {
27602
27758
  return JSON.parse(lines[lines.length - 1]).seq || 0;
@@ -27606,9 +27762,9 @@ function lastPersistedSeq(dir, chatId) {
27606
27762
  }
27607
27763
  function readSince(dir, chatId, since) {
27608
27764
  const p = brainEventsFilePath(dir, chatId);
27609
- if (!fs56.existsSync(p)) return [];
27765
+ if (!fs57.existsSync(p)) return [];
27610
27766
  const out = [];
27611
- for (const line of fs56.readFileSync(p, "utf-8").split("\n")) {
27767
+ for (const line of fs57.readFileSync(p, "utf-8").split("\n")) {
27612
27768
  if (!line) continue;
27613
27769
  try {
27614
27770
  const rec = JSON.parse(line);
@@ -27634,12 +27790,12 @@ function beginTurn(dir, chatId) {
27634
27790
  };
27635
27791
  live.set(chatId, state);
27636
27792
  const p = brainEventsFilePath(dir, chatId);
27637
- fs56.mkdirSync(path54.dirname(p), { recursive: true });
27793
+ fs57.mkdirSync(path54.dirname(p), { recursive: true });
27638
27794
  return (event) => {
27639
27795
  state.seq += 1;
27640
27796
  const rec = { seq: state.seq, turn, ts: Date.now(), event };
27641
27797
  try {
27642
- fs56.appendFileSync(p, `${JSON.stringify(rec)}
27798
+ fs57.appendFileSync(p, `${JSON.stringify(rec)}
27643
27799
  `);
27644
27800
  } catch (err) {
27645
27801
  process.stderr.write(
@@ -27678,7 +27834,7 @@ function endTurnIfUnterminated(dir, chatId, errMessage) {
27678
27834
  event: { type: "error", error: errMessage || "turn ended unexpectedly", chatId }
27679
27835
  };
27680
27836
  try {
27681
- fs56.appendFileSync(brainEventsFilePath(dir, chatId), `${JSON.stringify(rec)}
27837
+ fs57.appendFileSync(brainEventsFilePath(dir, chatId), `${JSON.stringify(rec)}
27682
27838
  `);
27683
27839
  } catch {
27684
27840
  }
@@ -29665,55 +29821,6 @@ async function brainTerminalAgent(options) {
29665
29821
 
29666
29822
  // src/servers/pool-serve.ts
29667
29823
  import { createServer as createServer5 } from "http";
29668
-
29669
- // src/pool/agency-loop-tick.ts
29670
- function normalizeRepositories(repositories) {
29671
- const unique = /* @__PURE__ */ new Set();
29672
- for (const raw of repositories) {
29673
- const repo = raw.trim().toLowerCase();
29674
- if (/^[^/\s]+\/[^/\s]+$/.test(repo)) unique.add(repo);
29675
- }
29676
- return [...unique].sort();
29677
- }
29678
- async function runAgencyLoopTick(deps) {
29679
- const repositories = normalizeRepositories(await deps.discover());
29680
- if (repositories.length === 0) {
29681
- deps.log("no consumer agencies discovered \u2014 nothing to tick");
29682
- return { discovered: 0, claimed: 0 };
29683
- }
29684
- deps.log(
29685
- `running scheduled fan-out for ${repositories.length} consumer agenc${repositories.length === 1 ? "y" : "ies"}`
29686
- );
29687
- const clock = deps.now ?? Date.now;
29688
- let claimed = 0;
29689
- for (const repository of repositories) {
29690
- const [owner, repo] = repository.split("/");
29691
- try {
29692
- const jobId = `sched-${owner}-${repo}-${clock()}`;
29693
- const result = await deps.claim(owner, repo, {
29694
- jobId,
29695
- repo: repository,
29696
- runRequest: {
29697
- requestId: jobId,
29698
- target: { type: "workflow", id: "scheduled-fanout" },
29699
- intent: "tick",
29700
- source: "schedule"
29701
- }
29702
- });
29703
- if (result.ok) {
29704
- claimed++;
29705
- deps.log(`[${repository}] scheduled fan-out claimed ${result.machineId}`);
29706
- } else {
29707
- deps.log(`[${repository}] scheduled fan-out skipped: ${result.reason ?? "runner unavailable"}`);
29708
- }
29709
- } catch (error) {
29710
- deps.log(`[${repository}] scheduled fan-out error: ${error instanceof Error ? error.message : String(error)}`);
29711
- }
29712
- }
29713
- return { discovered: repositories.length, claimed };
29714
- }
29715
-
29716
- // src/servers/pool-serve.ts
29717
29824
  init_keys();
29718
29825
 
29719
29826
  // src/pool/registry.ts
@@ -30349,30 +30456,6 @@ async function poolServe() {
30349
30456
  const tick = setInterval(() => {
30350
30457
  registry.resyncAll().catch((err) => log(`resync tick failed: ${err instanceof Error ? err.message : String(err)}`));
30351
30458
  }, refillMs);
30352
- const discoverAgencies = async () => {
30353
- if (!appCreds) return registry.activeRepos();
30354
- const repositories = await discoverAppRepositories(appCreds);
30355
- for (const access of repositories) repoTokens.set(access.repo.toLowerCase(), access.token);
30356
- return [.../* @__PURE__ */ new Set([...repositories.map((access) => access.repo), ...registry.activeRepos()])];
30357
- };
30358
- let agencyTickInFlight = null;
30359
- const runLoopTick = () => {
30360
- if (agencyTickInFlight) return agencyTickInFlight;
30361
- agencyTickInFlight = runAgencyLoopTick({
30362
- discover: discoverAgencies,
30363
- claim: (owner, repo, req) => registry.claim(owner, repo, req),
30364
- log
30365
- }).catch((err) => log(`agency Loop tick failed: ${err instanceof Error ? err.message : String(err)}`)).finally(() => {
30366
- agencyTickInFlight = null;
30367
- });
30368
- return agencyTickInFlight;
30369
- };
30370
- const loopTickEnabled = (process.env.POOL_LOOP_TICK ?? process.env.POOL_CAPABILITY_TICK ?? "1") !== "0";
30371
- const loopTickMs = envInt2(
30372
- process.env.POOL_LOOP_TICK_MS ? "POOL_LOOP_TICK_MS" : "POOL_CAPABILITY_TICK_MS",
30373
- 15 * 6e4
30374
- );
30375
- const loopTick = loopTickEnabled ? setInterval(() => void runLoopTick(), loopTickMs) : null;
30376
30459
  const server = createServer5(async (req, res) => {
30377
30460
  try {
30378
30461
  if (!req.method || !req.url) return sendJson2(res, 400, { error: "bad request" });
@@ -30426,11 +30509,9 @@ async function poolServe() {
30426
30509
  resolve24();
30427
30510
  });
30428
30511
  });
30429
- if (loopTickEnabled) void runLoopTick();
30430
30512
  const shutdown = (signal) => {
30431
30513
  log(`${signal} \u2014 shutting down`);
30432
30514
  clearInterval(tick);
30433
- if (loopTick) clearInterval(loopTick);
30434
30515
  server.close(() => process.exit(0));
30435
30516
  };
30436
30517
  process.once("SIGINT", () => shutdown("SIGINT"));
@@ -30442,7 +30523,7 @@ async function poolServe() {
30442
30523
 
30443
30524
  // src/servers/runner-serve.ts
30444
30525
  import { spawn as spawn10 } from "child_process";
30445
- import * as fs57 from "fs";
30526
+ import * as fs58 from "fs";
30446
30527
  import { createServer as createServer6 } from "http";
30447
30528
  var DEFAULT_PORT2 = 8080;
30448
30529
  var DEFAULT_WORKDIR = "/workspace/repo";
@@ -30518,8 +30599,8 @@ async function defaultRunJob(job) {
30518
30599
  const workdir = process.env.RUNNER_WORKDIR ?? DEFAULT_WORKDIR;
30519
30600
  const branch = job.ref ?? "main";
30520
30601
  const authUrl = `https://x-access-token:${job.githubToken}@github.com/${job.repo}.git`;
30521
- fs57.rmSync(workdir, { recursive: true, force: true });
30522
- fs57.mkdirSync(workdir, { recursive: true });
30602
+ fs58.rmSync(workdir, { recursive: true, force: true });
30603
+ fs58.mkdirSync(workdir, { recursive: true });
30523
30604
  const allSecrets = typeof job.allSecrets === "string" ? job.allSecrets : JSON.stringify(job.allSecrets ?? {});
30524
30605
  const target = job.runRequest.target;
30525
30606
  const interactive = target.type === "chat";
@@ -31107,7 +31188,7 @@ async function main(argv = process.argv.slice(2)) {
31107
31188
  const args = parseArgs(argv);
31108
31189
  const cwdFlag = argv.indexOf("--cwd");
31109
31190
  const definitionCwd = cwdFlag >= 0 && argv[cwdFlag + 1] ? argv[cwdFlag + 1] : process.cwd();
31110
- const shouldHydrate = !(args.command === "server" && args.serverName === "brain-serve") && !hasExplicitDefinitionsRoot(definitionCwd) && (Boolean(process.env.CONVEX_URL?.trim()) || process.env.GITHUB_ACTIONS === "true" && Boolean(process.env.GITHUB_EVENT_NAME));
31191
+ const shouldHydrate = args.command !== "help" && args.command !== "version" && !(args.command === "server" && args.serverName === "brain-serve") && !hasExplicitDefinitionsRoot(definitionCwd) && (Boolean(process.env.CONVEX_URL?.trim()) || process.env.GITHUB_ACTIONS === "true" && Boolean(process.env.GITHUB_EVENT_NAME));
31111
31192
  if (shouldHydrate) {
31112
31193
  try {
31113
31194
  await hydrateDefinitionsFromEnv(definitionCwd);
@@ -0,0 +1,9 @@
1
+ # Live Agent
2
+
3
+ ## Purpose
4
+
5
+ Run one continuation cycle for a selected persistent Agent.
6
+
7
+ ## Instructions
8
+
9
+ Use the `live-agent` implementation to load the Agent, its assigned Intent and resources, then persist the next AgentState.
@@ -0,0 +1,32 @@
1
+ {
2
+ "id": "live-agent",
3
+ "action": "live-agent",
4
+ "purpose": "Run one continuation cycle for a selected persistent Agent.",
5
+ "inputSchema": {
6
+ "type": "object",
7
+ "properties": {
8
+ "agent": {
9
+ "type": "string",
10
+ "description": "Selected Agent slug."
11
+ },
12
+ "intent": {
13
+ "type": "string",
14
+ "description": "Primary Intent slug recorded by the Loop."
15
+ }
16
+ },
17
+ "required": ["agent"],
18
+ "additionalProperties": false
19
+ },
20
+ "outputSchema": {
21
+ "type": "object",
22
+ "properties": {
23
+ "cursor": { "type": "string" },
24
+ "data": { "type": "object", "additionalProperties": true }
25
+ },
26
+ "additionalProperties": true
27
+ },
28
+ "effects": ["agent-state"],
29
+ "permissions": [],
30
+ "success": "The Agent completes one cycle and persists its continuation state.",
31
+ "failure": "The Agent cycle fails or its continuation state cannot be persisted."
32
+ }
@@ -0,0 +1,30 @@
1
+ {
2
+ "name": "live-agent",
3
+ "role": "primitive",
4
+ "kind": "oneshot",
5
+ "inputs": [
6
+ { "name": "agent", "flag": "--agent", "type": "string", "required": true, "describe": "Live Agent slug." },
7
+ { "name": "intent", "flag": "--intent", "type": "string", "required": false, "describe": "Primary Intent slug recorded by the Loop." }
8
+ ],
9
+ "claudeCode": {
10
+ "model": "inherit",
11
+ "permissionMode": "default",
12
+ "maxTurns": 100,
13
+ "maxThinkingTokens": null,
14
+ "systemPromptAppend": null,
15
+ "enableSubmitTool": true,
16
+ "tools": ["mcp__kody-capability__start_capability", "mcp__kody-submit__submit_state"],
17
+ "hooks": [], "skills": [], "commands": [], "subagents": [], "plugins": [], "mcpServers": []
18
+ },
19
+ "cliTools": [],
20
+ "scripts": {
21
+ "preflight": [{ "script": "loadLiveAgent" }, { "script": "composePrompt" }],
22
+ "postflight": [
23
+ { "script": "parseJobStateFromAgentResult", "with": { "fenceLabel": "kody-agent-next-state" } },
24
+ { "script": "saveLiveAgentState" },
25
+ { "script": "appendCompanyActivity" }
26
+ ]
27
+ },
28
+ "inputArtifacts": [],
29
+ "outputArtifacts": []
30
+ }
@@ -0,0 +1,38 @@
1
+ You are a persistent Kody Agent completing one scheduled cycle.
2
+
3
+ ## Identity
4
+
5
+ {{agentIdentity}}
6
+
7
+ ## Primary Intent
8
+
9
+ {{liveAgentIntent}}
10
+
11
+ ## Policies
12
+
13
+ {{liveAgentPolicies}}
14
+
15
+ ## Constraints
16
+
17
+ {{liveAgentConstraints}}
18
+
19
+ ## Context
20
+
21
+ {{liveAgentContext}}
22
+
23
+ ## Assigned Capabilities
24
+
25
+ {{liveAgentCapabilities}}
26
+
27
+ ## Previous continuation
28
+
29
+ ```json
30
+ {{jobStateJson}}
31
+ ```
32
+
33
+ Inspect current conditions, decide the best next action toward the Intent, and use only assigned capabilities. Do not create work merely to appear active. If waiting is correct, record what is being awaited.
34
+
35
+ As your final action, call `submit_state` exactly once with:
36
+ - `cursor`: the next continuation cursor;
37
+ - `data`: compact durable continuation data;
38
+ - `done`: always `false` for a live Agent.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kody-ade/kody-engine",
3
- "version": "0.4.606",
3
+ "version": "0.4.610",
4
4
  "description": "kody — autonomous development engine. Single-session Claude Code agent behind a generic executor + declarative implementation profiles.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -50,8 +50,6 @@ on:
50
50
  types: [created]
51
51
  pull_request:
52
52
  types: [opened, synchronize, closed]
53
- schedule:
54
- - cron: '7/15 * * * *'
55
53
 
56
54
  jobs:
57
55
  run:
@@ -82,7 +80,7 @@ jobs:
82
80
  GH_PAT: ${{ secrets.GH_PAT }}
83
81
  KODY_TOKEN: ${{ secrets.KODY_TOKEN }}
84
82
  E2E_GITHUB_TOKEN: ${{ secrets.E2E_GITHUB_TOKEN }}
85
- OPENROUTER_API_KEY: ${{ secrets.OPENROUTER_API_KEY }}
83
+ MINIMAX_API_KEY: ${{ secrets.MINIMAX_API_KEY }}
86
84
  KODY_APP_ID: ${{ secrets.KODY_APP_ID }}
87
85
  KODY_APP_PRIVATE_KEY: ${{ secrets.KODY_APP_PRIVATE_KEY }}
88
86
  SESSION_ID: ${{ inputs.sessionId }}