@kody-ade/kody-engine 0.4.607 → 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.607",
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",
@@ -3158,6 +3158,13 @@ function createStateBackendFromEnv(env = process.env, client) {
3158
3158
  });
3159
3159
  return Array.isArray(result) ? result : [];
3160
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
+ },
3161
3168
  async get(tenantId2, taskKey, kind) {
3162
3169
  const result = await transport.query(anyApi.taskState.get, {
3163
3170
  tenantId: requireTenant(tenantId2),
@@ -3176,6 +3183,26 @@ function createStateBackendFromEnv(env = process.env, client) {
3176
3183
  ...expectedUpdatedAt ? { expectedUpdatedAt } : {}
3177
3184
  });
3178
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
+ },
3179
3206
  async getRepoDoc(tenantId2, kind) {
3180
3207
  const result = await transport.query(anyApi.repoDocs.get, {
3181
3208
  tenantId: requireTenant(tenantId2),
@@ -14586,6 +14613,18 @@ function mergeLoopDefinitions(repositoryLoops, runtimeLoops) {
14586
14613
  }
14587
14614
  return [...byId.values()].sort((left, right) => left.id.localeCompare(right.id));
14588
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
+ }
14589
14628
  function loopDispatchSlot(loop, now, force, nonce) {
14590
14629
  return force ? `manual:${now.toISOString()}:${nonce}` : dueSlot(loop, now);
14591
14630
  }
@@ -14647,6 +14686,14 @@ var init_dispatchLoops = __esm({
14647
14686
  const requestedLoopId = typeof ctx.args.loop === "string" ? ctx.args.loop.trim() : "";
14648
14687
  const backend = createStateBackendFromEnv();
14649
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
+ );
14650
14697
  const due = selectRunnableLoops(loops, now, {
14651
14698
  force,
14652
14699
  ...requestedLoopId ? { loopId: requestedLoopId } : {}
@@ -16442,14 +16489,105 @@ ${truncate2(issue2.body, FINDING_BODY_MAX_BYTES)}`;
16442
16489
  }
16443
16490
  });
16444
16491
 
16445
- // src/scripts/kodyVariables.ts
16492
+ // src/scripts/loadLiveAgent.ts
16446
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";
16447
16585
  import * as path41 from "path";
16448
16586
  function readKodyVariables(cwd) {
16449
16587
  const full = path41.join(cwd, KODY_VARIABLES_REL_PATH);
16450
16588
  let raw;
16451
16589
  try {
16452
- raw = fs43.readFileSync(full, "utf-8");
16590
+ raw = fs44.readFileSync(full, "utf-8");
16453
16591
  } catch {
16454
16592
  return {};
16455
16593
  }
@@ -16474,7 +16612,7 @@ var init_kodyVariables = __esm({
16474
16612
  });
16475
16613
 
16476
16614
  // src/scripts/loadQaContext.ts
16477
- import * as fs44 from "fs";
16615
+ import * as fs45 from "fs";
16478
16616
  import * as path42 from "path";
16479
16617
  function parseSlugList(value) {
16480
16618
  const inner = value.startsWith("[") && value.endsWith("]") ? value.slice(1, -1) : value;
@@ -16505,17 +16643,17 @@ function readProfileAgents(raw) {
16505
16643
  }
16506
16644
  function readProfile(cwd) {
16507
16645
  const dir = path42.join(cwd, CONTEXT_DIR_REL_PATH);
16508
- if (!fs44.existsSync(dir)) return "";
16646
+ if (!fs45.existsSync(dir)) return "";
16509
16647
  let entries;
16510
16648
  try {
16511
- entries = fs44.readdirSync(dir).filter((f) => f.endsWith(".md")).sort();
16649
+ entries = fs45.readdirSync(dir).filter((f) => f.endsWith(".md")).sort();
16512
16650
  } catch {
16513
16651
  return "";
16514
16652
  }
16515
16653
  const blocks = [];
16516
16654
  for (const file of entries) {
16517
16655
  try {
16518
- const raw = fs44.readFileSync(path42.join(dir, file), "utf-8");
16656
+ const raw = fs45.readFileSync(path42.join(dir, file), "utf-8");
16519
16657
  const { agent, body } = readProfileAgents(raw);
16520
16658
  if (!agent.includes(QA_AGENT) && !agent.includes(ALL_AGENTS)) continue;
16521
16659
  blocks.push(`## ${file}
@@ -16565,7 +16703,7 @@ var init_loadQaContext = __esm({
16565
16703
 
16566
16704
  // src/scripts/loadSimpleCapability.ts
16567
16705
  import { randomUUID as randomUUID2 } from "crypto";
16568
- import * as fs45 from "fs";
16706
+ import * as fs46 from "fs";
16569
16707
  import * as os6 from "os";
16570
16708
  import * as path43 from "path";
16571
16709
  function registerCapabilitySubagents(profile, toolRoot, toolFiles) {
@@ -16580,7 +16718,7 @@ function registerCapabilitySubagents(profile, toolRoot, toolFiles) {
16580
16718
  profile.subagentTemplates = {
16581
16719
  ...profile.subagentTemplates ?? {},
16582
16720
  ...Object.fromEntries(
16583
- 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")])
16584
16722
  )
16585
16723
  };
16586
16724
  if (!profile.claudeCode.tools.includes("Agent")) {
@@ -16621,10 +16759,10 @@ function scalar(value) {
16621
16759
  return value;
16622
16760
  }
16623
16761
  function listFiles(root) {
16624
- if (!fs45.existsSync(root)) return [];
16762
+ if (!fs46.existsSync(root)) return [];
16625
16763
  const files = [];
16626
16764
  const visit = (dir) => {
16627
- for (const entry of fs45.readdirSync(dir, { withFileTypes: true })) {
16765
+ for (const entry of fs46.readdirSync(dir, { withFileTypes: true })) {
16628
16766
  const absolute = path43.join(dir, entry.name);
16629
16767
  if (entry.isSymbolicLink()) continue;
16630
16768
  if (entry.isDirectory()) visit(absolute);
@@ -16716,7 +16854,7 @@ var init_loadSimpleCapability = __esm({
16716
16854
  ...skillFiles.flatMap((file) => [
16717
16855
  `### ${file}`,
16718
16856
  "",
16719
- fs45.readFileSync(path43.join(skillRoot, file), "utf-8"),
16857
+ fs46.readFileSync(path43.join(skillRoot, file), "utf-8"),
16720
16858
  ""
16721
16859
  ])
16722
16860
  ] : [],
@@ -16756,7 +16894,7 @@ var init_loadSimpleCapability = __esm({
16756
16894
  });
16757
16895
 
16758
16896
  // src/taskContext.ts
16759
- import * as fs46 from "fs";
16897
+ import * as fs47 from "fs";
16760
16898
  import * as path44 from "path";
16761
16899
  function buildTaskContext(args) {
16762
16900
  return {
@@ -16773,9 +16911,9 @@ function buildTaskContext(args) {
16773
16911
  function persistTaskContext(cwd, ctx) {
16774
16912
  try {
16775
16913
  const dir = runtimeStatePath(cwd, "agent-runs", ctx.runId);
16776
- fs46.mkdirSync(dir, { recursive: true });
16914
+ fs47.mkdirSync(dir, { recursive: true });
16777
16915
  const file = path44.join(dir, "task-context.json");
16778
- fs46.writeFileSync(file, `${JSON.stringify(ctx, null, 2)}
16916
+ fs47.writeFileSync(file, `${JSON.stringify(ctx, null, 2)}
16779
16917
  `);
16780
16918
  return file;
16781
16919
  } catch (err) {
@@ -17696,7 +17834,7 @@ var init_parseReproOutput = __esm({
17696
17834
  });
17697
17835
 
17698
17836
  // src/scripts/parseSimpleCapabilityOutput.ts
17699
- import * as fs47 from "fs";
17837
+ import * as fs48 from "fs";
17700
17838
  function acceptAuthoritativeCapabilityOutput(ctx, profile, output) {
17701
17839
  ctx.data.agentDone = true;
17702
17840
  delete ctx.data.agentFailureReason;
@@ -17713,11 +17851,11 @@ function stringList2(value) {
17713
17851
  return Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
17714
17852
  }
17715
17853
  function readOutputFile(outputPath) {
17716
- if (!outputPath || !fs47.existsSync(outputPath)) return { found: false };
17854
+ if (!outputPath || !fs48.existsSync(outputPath)) return { found: false };
17717
17855
  try {
17718
- return { found: true, value: JSON.parse(fs47.readFileSync(outputPath, "utf-8")) };
17856
+ return { found: true, value: JSON.parse(fs48.readFileSync(outputPath, "utf-8")) };
17719
17857
  } finally {
17720
- fs47.rmSync(outputPath, { force: true });
17858
+ fs48.rmSync(outputPath, { force: true });
17721
17859
  }
17722
17860
  }
17723
17861
  function parseOutput(text2) {
@@ -18339,7 +18477,7 @@ var init_postResearchComment = __esm({
18339
18477
  });
18340
18478
 
18341
18479
  // src/scripts/prepareBrowserAuth.ts
18342
- import * as fs48 from "fs";
18480
+ import * as fs49 from "fs";
18343
18481
  import * as os7 from "os";
18344
18482
  import * as path45 from "path";
18345
18483
  function appendAuthMessage(ctx, message) {
@@ -18385,8 +18523,8 @@ async function githubJson(url, token, checkName) {
18385
18523
  throw new Error(`GitHub ${checkName} check failed`);
18386
18524
  }
18387
18525
  function writeKodyStorageState(input) {
18388
- const directory = fs48.mkdtempSync(path45.join(os7.tmpdir(), "kody-browser-auth-"));
18389
- fs48.chmodSync(directory, 448);
18526
+ const directory = fs49.mkdtempSync(path45.join(os7.tmpdir(), "kody-browser-auth-"));
18527
+ fs49.chmodSync(directory, 448);
18390
18528
  const file = path45.join(directory, "storage-state.json");
18391
18529
  const now = Date.now();
18392
18530
  const repoEntry = {
@@ -18418,7 +18556,7 @@ function writeKodyStorageState(input) {
18418
18556
  }
18419
18557
  ]
18420
18558
  };
18421
- fs48.writeFileSync(file, JSON.stringify(storageState), { mode: 384 });
18559
+ fs49.writeFileSync(file, JSON.stringify(storageState), { mode: 384 });
18422
18560
  return { directory, file, auth };
18423
18561
  }
18424
18562
  function parseSetCookie(value, hostname) {
@@ -18448,10 +18586,10 @@ function parseSetCookie(value, hostname) {
18448
18586
  }
18449
18587
  function writeCookieStorageState(targetUrl, setCookies) {
18450
18588
  const target = new URL(targetUrl);
18451
- const directory = fs48.mkdtempSync(path45.join(os7.tmpdir(), "kody-browser-auth-"));
18452
- fs48.chmodSync(directory, 448);
18589
+ const directory = fs49.mkdtempSync(path45.join(os7.tmpdir(), "kody-browser-auth-"));
18590
+ fs49.chmodSync(directory, 448);
18453
18591
  const file = path45.join(directory, "storage-state.json");
18454
- fs48.writeFileSync(
18592
+ fs49.writeFileSync(
18455
18593
  file,
18456
18594
  JSON.stringify({
18457
18595
  cookies: setCookies.map((cookie) => parseSetCookie(cookie, target.hostname)),
@@ -18472,9 +18610,9 @@ function currentStorageStatePath(args) {
18472
18610
  function browserSessionCookieHeader(profile, targetUrl) {
18473
18611
  const playwright = profile.claudeCode.mcpServers.find((server) => server.name === "playwright");
18474
18612
  const storagePath = currentStorageStatePath(playwright?.args ?? []);
18475
- if (!storagePath || !fs48.existsSync(storagePath)) return void 0;
18613
+ if (!storagePath || !fs49.existsSync(storagePath)) return void 0;
18476
18614
  const hostname = new URL(targetUrl).hostname;
18477
- const state = JSON.parse(fs48.readFileSync(storagePath, "utf-8"));
18615
+ const state = JSON.parse(fs49.readFileSync(storagePath, "utf-8"));
18478
18616
  const cookies = (state.cookies ?? []).filter((cookie) => {
18479
18617
  const domain = cookie.domain.replace(/^\./, "");
18480
18618
  return hostname === domain || hostname.endsWith(`.${domain}`);
@@ -18541,9 +18679,9 @@ async function prepareAccountModelSettings(ctx, profile, input) {
18541
18679
  return true;
18542
18680
  }
18543
18681
  function mergeStorageStates(existingPath, nextPath) {
18544
- if (existingPath === nextPath || !fs48.existsSync(existingPath)) return;
18545
- const existing = JSON.parse(fs48.readFileSync(existingPath, "utf-8"));
18546
- 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"));
18547
18685
  const cookies = /* @__PURE__ */ new Map();
18548
18686
  for (const cookie of [...existing.cookies ?? [], ...next.cookies ?? []]) {
18549
18687
  cookies.set(`${cookie.name}\0${cookie.domain}\0${cookie.path}`, cookie);
@@ -18557,7 +18695,7 @@ function mergeStorageStates(existingPath, nextPath) {
18557
18695
  }
18558
18696
  origins.set(entry.origin, { origin: entry.origin, localStorage: [...localStorage.values()] });
18559
18697
  }
18560
- fs48.writeFileSync(
18698
+ fs49.writeFileSync(
18561
18699
  nextPath,
18562
18700
  JSON.stringify({ cookies: [...cookies.values()], origins: [...origins.values()] }),
18563
18701
  { mode: 384 }
@@ -18654,7 +18792,7 @@ async function prepareKodyRepositoryBrowserAuth(ctx, profile, input) {
18654
18792
  configurePlaywright(profile, state.file);
18655
18793
  const authDirectory = state.directory;
18656
18794
  registerRuntimeCleanup(ctx, () => {
18657
- fs48.rmSync(authDirectory, { recursive: true, force: true });
18795
+ fs49.rmSync(authDirectory, { recursive: true, force: true });
18658
18796
  });
18659
18797
  appendAuthMessage(
18660
18798
  ctx,
@@ -18662,7 +18800,7 @@ async function prepareKodyRepositoryBrowserAuth(ctx, profile, input) {
18662
18800
  );
18663
18801
  return true;
18664
18802
  } catch (error) {
18665
- if (state) fs48.rmSync(state.directory, { recursive: true, force: true });
18803
+ if (state) fs49.rmSync(state.directory, { recursive: true, force: true });
18666
18804
  const reason = error instanceof Error ? error.message : String(error);
18667
18805
  appendAuthMessage(
18668
18806
  ctx,
@@ -18689,11 +18827,11 @@ async function prepareEmailPasswordBrowserAuth(ctx, profile, input) {
18689
18827
  state = writeCookieStorageState(input.targetUrl, cookies);
18690
18828
  configurePlaywright(profile, state.file);
18691
18829
  const authDirectory = state.directory;
18692
- registerRuntimeCleanup(ctx, () => fs48.rmSync(authDirectory, { recursive: true, force: true }));
18830
+ registerRuntimeCleanup(ctx, () => fs49.rmSync(authDirectory, { recursive: true, force: true }));
18693
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.";
18694
18832
  return true;
18695
18833
  } catch (error) {
18696
- if (state) fs48.rmSync(state.directory, { recursive: true, force: true });
18834
+ if (state) fs49.rmSync(state.directory, { recursive: true, force: true });
18697
18835
  const reason = error instanceof Error ? error.message : String(error);
18698
18836
  ctx.data.qaAuthBlock = `Auth: the engine could not prepare the app login (${reason}). Note this authenticated surface as a gap.`;
18699
18837
  return false;
@@ -20607,7 +20745,7 @@ var init_tickShellRunner = __esm({
20607
20745
  });
20608
20746
 
20609
20747
  // src/scripts/runScheduledImplementationTick.ts
20610
- import * as fs49 from "fs";
20748
+ import * as fs50 from "fs";
20611
20749
  import * as path48 from "path";
20612
20750
  var runScheduledImplementationTick;
20613
20751
  var init_runScheduledImplementationTick = __esm({
@@ -20636,7 +20774,7 @@ var init_runScheduledImplementationTick = __esm({
20636
20774
  return;
20637
20775
  }
20638
20776
  const shellPath = path48.join(profile.dir, shell);
20639
- if (!fs49.existsSync(shellPath)) {
20777
+ if (!fs50.existsSync(shellPath)) {
20640
20778
  ctx.output.exitCode = 99;
20641
20779
  ctx.output.reason = `runScheduledImplementationTick: shell not found: ${shell} (looked in ${profile.dir})`;
20642
20780
  return;
@@ -20668,13 +20806,13 @@ var init_runScheduledImplementationTick = __esm({
20668
20806
 
20669
20807
  // src/scripts/runSimpleCapabilityScript.ts
20670
20808
  import { spawnSync as spawnSync3 } from "child_process";
20671
- import * as fs50 from "fs";
20809
+ import * as fs51 from "fs";
20672
20810
  function formatDuration2(timeoutMs) {
20673
20811
  return timeoutMs % 6e4 === 0 ? `${timeoutMs / 6e4} minutes` : `${timeoutMs}ms`;
20674
20812
  }
20675
20813
  function isRegularFile2(filePath) {
20676
20814
  try {
20677
- const stat = fs50.lstatSync(filePath);
20815
+ const stat = fs51.lstatSync(filePath);
20678
20816
  return stat.isFile() && !stat.isSymbolicLink();
20679
20817
  } catch {
20680
20818
  return false;
@@ -20753,7 +20891,7 @@ var init_runSimpleCapabilityScript = __esm({
20753
20891
  });
20754
20892
 
20755
20893
  // src/scripts/runTickScript.ts
20756
- import * as fs51 from "fs";
20894
+ import * as fs52 from "fs";
20757
20895
  import * as path49 from "path";
20758
20896
  var runTickScript;
20759
20897
  var init_runTickScript = __esm({
@@ -20787,7 +20925,7 @@ var init_runTickScript = __esm({
20787
20925
  return;
20788
20926
  }
20789
20927
  const scriptPath = path49.isAbsolute(tickScript) ? tickScript : path49.join(ctx.cwd, tickScript);
20790
- if (!fs51.existsSync(scriptPath)) {
20928
+ if (!fs52.existsSync(scriptPath)) {
20791
20929
  ctx.output.exitCode = 99;
20792
20930
  ctx.output.reason = `runTickScript: tickScript not found: ${scriptPath}`;
20793
20931
  return;
@@ -20835,6 +20973,41 @@ var init_saveManagedGoalState = __esm({
20835
20973
  }
20836
20974
  });
20837
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
+
20838
21011
  // src/scripts/setCommentTarget.ts
20839
21012
  var setCommentTarget;
20840
21013
  var init_setCommentTarget = __esm({
@@ -21952,7 +22125,7 @@ var init_warmupMcp = __esm({
21952
22125
  });
21953
22126
 
21954
22127
  // src/scripts/writeAgentRunSummary.ts
21955
- import * as fs52 from "fs";
22128
+ import * as fs53 from "fs";
21956
22129
  var writeAgentRunSummary;
21957
22130
  var init_writeAgentRunSummary = __esm({
21958
22131
  "src/scripts/writeAgentRunSummary.ts"() {
@@ -21978,7 +22151,7 @@ var init_writeAgentRunSummary = __esm({
21978
22151
  if (reason) lines.push(`- **Reason:** ${reason}`);
21979
22152
  lines.push("");
21980
22153
  try {
21981
- fs52.appendFileSync(summaryPath, `${lines.join("\n")}
22154
+ fs53.appendFileSync(summaryPath, `${lines.join("\n")}
21982
22155
  `);
21983
22156
  } catch {
21984
22157
  }
@@ -22135,6 +22308,7 @@ var init_scripts = __esm({
22135
22308
  init_loadIssueStateComment();
22136
22309
  init_loadJobFromFile();
22137
22310
  init_loadLinkedFinding();
22311
+ init_loadLiveAgent();
22138
22312
  init_loadMemoryContext();
22139
22313
  init_loadPriorArt();
22140
22314
  init_loadQaContext();
@@ -22183,6 +22357,7 @@ var init_scripts = __esm({
22183
22357
  init_runSimpleCapabilityScript();
22184
22358
  init_runTickScript();
22185
22359
  init_saveManagedGoalState();
22360
+ init_saveLiveAgentState();
22186
22361
  init_saveTaskState();
22187
22362
  init_setCommentTarget();
22188
22363
  init_setLifecycleLabel();
@@ -22222,6 +22397,7 @@ var init_scripts = __esm({
22222
22397
  loadConventions,
22223
22398
  loadCoverageRules,
22224
22399
  loadLinkedFinding,
22400
+ loadLiveAgent,
22225
22401
  loadMemoryContext,
22226
22402
  loadPriorArt,
22227
22403
  loadQaContext,
@@ -22257,6 +22433,7 @@ var init_scripts = __esm({
22257
22433
  saveManagedGoalState
22258
22434
  };
22259
22435
  postflightScripts = {
22436
+ saveLiveAgentState,
22260
22437
  parseSimpleCapabilityOutput,
22261
22438
  parseAgentResult: parseAgentResult2,
22262
22439
  parseIssueStateFromAgentResult,
@@ -22316,7 +22493,7 @@ var init_scripts = __esm({
22316
22493
  });
22317
22494
 
22318
22495
  // src/stateWorkspace.ts
22319
- import * as fs53 from "fs";
22496
+ import * as fs54 from "fs";
22320
22497
  import * as path51 from "path";
22321
22498
  function tenantId(config) {
22322
22499
  const owner = config.github?.owner?.trim() || process.env.GITHUB_REPOSITORY?.split("/")[0]?.trim();
@@ -22325,8 +22502,8 @@ function tenantId(config) {
22325
22502
  }
22326
22503
  function writeRuntimeFile(cwd, relativePath, content) {
22327
22504
  const target = path51.join(cwd, RUNTIME_ROOT, relativePath);
22328
- fs53.mkdirSync(path51.dirname(target), { recursive: true });
22329
- fs53.writeFileSync(target, content, "utf8");
22505
+ fs54.mkdirSync(path51.dirname(target), { recursive: true });
22506
+ fs54.writeFileSync(target, content, "utf8");
22330
22507
  }
22331
22508
  function record(value) {
22332
22509
  return value && typeof value === "object" && !Array.isArray(value) ? value : null;
@@ -22347,8 +22524,8 @@ function memoryIndex(docs) {
22347
22524
  if (lines.length === 0) return "";
22348
22525
  return ["# Kody memory index", "", "One line per backend memory document.", "", ...lines, ""].join("\n");
22349
22526
  }
22350
- async function hydratePrefix(backend, tenant, cwd, prefix) {
22351
- const docs = await backend.listRepoDocs(tenant, prefix);
22527
+ async function hydratePrefix(backend, tenant2, cwd, prefix) {
22528
+ const docs = await backend.listRepoDocs(tenant2, prefix);
22352
22529
  for (const doc of docs) {
22353
22530
  const slug = doc.kind.slice(prefix.length);
22354
22531
  if (!/^[a-z0-9][a-z0-9_-]{0,63}$/.test(slug)) continue;
@@ -22361,8 +22538,8 @@ async function hydratePrefix(backend, tenant, cwd, prefix) {
22361
22538
  if (index) writeRuntimeFile(cwd, "memory/INDEX.md", index);
22362
22539
  }
22363
22540
  }
22364
- async function hydrateSingleton(backend, tenant, cwd, kind, relativePath) {
22365
- const doc = await backend.getRepoDoc(tenant, kind);
22541
+ async function hydrateSingleton(backend, tenant2, cwd, kind, relativePath) {
22542
+ const doc = await backend.getRepoDoc(tenant2, kind);
22366
22543
  if (!doc) return;
22367
22544
  const body = stringField6(doc.doc, "body");
22368
22545
  if (body !== null) {
@@ -22372,8 +22549,8 @@ async function hydrateSingleton(backend, tenant, cwd, kind, relativePath) {
22372
22549
  writeRuntimeFile(cwd, relativePath, `${JSON.stringify(doc.doc, null, 2)}
22373
22550
  `);
22374
22551
  }
22375
- async function hydrateWorkflows(backend, tenant, cwd) {
22376
- for (const workflow of await backend.listWorkflows(tenant)) {
22552
+ async function hydrateWorkflows(backend, tenant2, cwd) {
22553
+ for (const workflow of await backend.listWorkflows(tenant2)) {
22377
22554
  if (!/^[a-z0-9][a-z0-9_-]{0,79}$/.test(workflow.workflowId)) continue;
22378
22555
  writeRuntimeFile(
22379
22556
  cwd,
@@ -22384,25 +22561,25 @@ async function hydrateWorkflows(backend, tenant, cwd) {
22384
22561
  }
22385
22562
  }
22386
22563
  async function hydrateStateWorkspace(config, cwd, backendOverride) {
22387
- const tenant = tenantId(config);
22564
+ const tenant2 = tenantId(config);
22388
22565
  const configured = hasStateBackendConfig();
22389
- if (!tenant || !configured) {
22566
+ if (!tenant2 || !configured) {
22390
22567
  if (process.env.GITHUB_ACTIONS === "true")
22391
22568
  throw new Error("Kody backend access is required for runtime workspace documents");
22392
22569
  return;
22393
22570
  }
22394
- const key = `${path51.resolve(cwd)}|${tenant}`;
22571
+ const key = `${path51.resolve(cwd)}|${tenant2}`;
22395
22572
  if (hydratedWorkspaces.has(key)) return;
22396
22573
  const backend = backendOverride ?? createStateBackendFromEnv();
22397
22574
  const root = path51.join(cwd, RUNTIME_ROOT);
22398
- fs53.rmSync(root, { recursive: true, force: true });
22575
+ fs54.rmSync(root, { recursive: true, force: true });
22399
22576
  await Promise.all([
22400
- hydratePrefix(backend, tenant, cwd, "context:"),
22401
- hydratePrefix(backend, tenant, cwd, "memory:"),
22402
- hydrateSingleton(backend, tenant, cwd, "instructions", "instructions.md"),
22403
- hydrateSingleton(backend, tenant, cwd, "system-prompt", "system-prompt.md"),
22404
- hydrateSingleton(backend, tenant, cwd, "variables", "variables.json"),
22405
- 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)
22406
22583
  ]);
22407
22584
  hydratedWorkspaces.add(key);
22408
22585
  }
@@ -22482,7 +22659,7 @@ var init_tools = __esm({
22482
22659
 
22483
22660
  // src/executor.ts
22484
22661
  import { spawn as spawn8 } from "child_process";
22485
- import * as fs54 from "fs";
22662
+ import * as fs55 from "fs";
22486
22663
  import * as os8 from "os";
22487
22664
  import * as path52 from "path";
22488
22665
  function isMutatingPostflight(scriptName) {
@@ -23269,7 +23446,7 @@ function resolveProfilePath(profileName, cwd = process.cwd()) {
23269
23446
  // fallback
23270
23447
  ];
23271
23448
  for (const c of candidates) {
23272
- if (fs54.existsSync(c)) return c;
23449
+ if (fs55.existsSync(c)) return c;
23273
23450
  }
23274
23451
  return candidates[0];
23275
23452
  }
@@ -23385,7 +23562,7 @@ function resolveShellTimeoutMs(entry) {
23385
23562
  async function runShellEntry(entry, ctx, profile) {
23386
23563
  const shellName = entry.shell;
23387
23564
  const shellPath = path52.join(profile.dir, shellName);
23388
- if (!fs54.existsSync(shellPath)) {
23565
+ if (!fs55.existsSync(shellPath)) {
23389
23566
  ctx.skipAgent = true;
23390
23567
  ctx.output.exitCode = 99;
23391
23568
  ctx.output.reason = `shell script not found: ${shellName} (looked in ${profile.dir})`;
@@ -23459,9 +23636,9 @@ async function runShellEntry(entry, ctx, profile) {
23459
23636
  }
23460
23637
  let sideChannelText = "";
23461
23638
  try {
23462
- if (fs54.existsSync(outputFile)) {
23463
- sideChannelText = fs54.readFileSync(outputFile, "utf-8");
23464
- fs54.rmSync(outputFile, { force: true });
23639
+ if (fs55.existsSync(outputFile)) {
23640
+ sideChannelText = fs55.readFileSync(outputFile, "utf-8");
23641
+ fs55.rmSync(outputFile, { force: true });
23465
23642
  }
23466
23643
  } catch {
23467
23644
  }
@@ -26194,7 +26371,7 @@ async function hydrateDefinitionsFromEnv(cwd = process.cwd(), env = process.env)
26194
26371
 
26195
26372
  // src/kody-cli.ts
26196
26373
  import { execFileSync as execFileSync24 } from "child_process";
26197
- import * as fs55 from "fs";
26374
+ import * as fs56 from "fs";
26198
26375
  import * as path53 from "path";
26199
26376
 
26200
26377
  // src/app-auth.ts
@@ -26242,26 +26419,6 @@ async function ghApp(jwt, apiPath, method = "GET") {
26242
26419
  }
26243
26420
  return await res.json();
26244
26421
  }
26245
- async function ghAppPage(authToken, apiPath) {
26246
- const res = await fetch(`${GH_API}${apiPath}`, {
26247
- headers: {
26248
- Authorization: `Bearer ${authToken}`,
26249
- Accept: "application/vnd.github+json",
26250
- "X-GitHub-Api-Version": "2022-11-28",
26251
- "User-Agent": "kody-engine"
26252
- }
26253
- });
26254
- if (!res.ok) {
26255
- const body = await res.text().catch(() => "");
26256
- throw new Error(
26257
- `GitHub App API GET ${apiPath} \u2192 ${res.status} ${res.statusText}${body ? `: ${body.slice(0, 200)}` : ""}`
26258
- );
26259
- }
26260
- return {
26261
- data: await res.json(),
26262
- hasNext: /rel="next"/.test(res.headers.get("link") ?? "")
26263
- };
26264
- }
26265
26422
  function readAppCreds(env = process.env) {
26266
26423
  const appId = env.KODY_APP_ID?.trim();
26267
26424
  const privateKey = env.KODY_APP_PRIVATE_KEY;
@@ -26286,36 +26443,6 @@ async function mintAppInstallationToken(creds) {
26286
26443
  const tok = await ghApp(jwt, `/app/installations/${installationId}/access_tokens`, "POST");
26287
26444
  return tok.token;
26288
26445
  }
26289
- async function discoverAppRepositories(creds) {
26290
- const jwt = buildAppJwt(creds.appId, creds.privateKey);
26291
- const installations = [];
26292
- for (let page = 1; ; page++) {
26293
- const result = await ghAppPage(jwt, `/app/installations?per_page=100&page=${page}`);
26294
- installations.push(...result.data.filter((item) => Number.isInteger(item.id) && item.id > 0));
26295
- if (!result.hasNext) break;
26296
- }
26297
- const byRepo = /* @__PURE__ */ new Map();
26298
- for (const installation of installations) {
26299
- const token = await mintAppInstallationToken({
26300
- appId: creds.appId,
26301
- privateKey: creds.privateKey,
26302
- installationId: String(installation.id)
26303
- });
26304
- for (let page = 1; ; page++) {
26305
- const result = await ghAppPage(
26306
- token,
26307
- `/installation/repositories?per_page=100&page=${page}`
26308
- );
26309
- for (const repository of result.data.repositories ?? []) {
26310
- const repo = repository.full_name?.trim();
26311
- if (!repo || !/^[^/\s]+\/[^/\s]+$/.test(repo)) continue;
26312
- byRepo.set(repo.toLowerCase(), { repo, token });
26313
- }
26314
- if (!result.hasNext) break;
26315
- }
26316
- }
26317
- return [...byRepo.values()].sort((left, right) => left.repo.localeCompare(right.repo));
26318
- }
26319
26446
 
26320
26447
  // src/kody-cli.ts
26321
26448
  init_capabilityFolders();
@@ -26997,9 +27124,9 @@ async function resolveAuthToken(env = process.env) {
26997
27124
  return void 0;
26998
27125
  }
26999
27126
  function detectPackageManager2(cwd) {
27000
- if (fs55.existsSync(path53.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
27001
- if (fs55.existsSync(path53.join(cwd, "yarn.lock"))) return "yarn";
27002
- 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";
27003
27130
  return "npm";
27004
27131
  }
27005
27132
  function shouldChainScheduledWatch(match) {
@@ -27102,8 +27229,8 @@ function postFailureTail(issueNumber, cwd, reason) {
27102
27229
  const logPath = lastRunLogPath(cwd);
27103
27230
  let tail = "";
27104
27231
  try {
27105
- if (fs55.existsSync(logPath)) {
27106
- const content = fs55.readFileSync(logPath, "utf-8");
27232
+ if (fs56.existsSync(logPath)) {
27233
+ const content = fs56.readFileSync(logPath, "utf-8");
27107
27234
  tail = content.slice(-3e3);
27108
27235
  }
27109
27236
  } catch {
@@ -27198,9 +27325,9 @@ async function runCi(argv) {
27198
27325
  forceRunCliArgs = { goal: envForceMessage };
27199
27326
  }
27200
27327
  }
27201
- 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)) {
27202
27329
  try {
27203
- const evt = JSON.parse(fs55.readFileSync(dispatchEventPath, "utf-8"));
27330
+ const evt = JSON.parse(fs56.readFileSync(dispatchEventPath, "utf-8"));
27204
27331
  const inputs = objectValue2(evt.inputs);
27205
27332
  const issueInput = parseInt(String(inputs?.issue_number ?? ""), 10);
27206
27333
  const sessionInput = String(inputs?.sessionId ?? "");
@@ -27615,7 +27742,7 @@ init_repoWorkspace();
27615
27742
 
27616
27743
  // src/scripts/brainTurnLog.ts
27617
27744
  init_runtimePaths();
27618
- import * as fs56 from "fs";
27745
+ import * as fs57 from "fs";
27619
27746
  import * as path54 from "path";
27620
27747
  import posixPath4 from "path/posix";
27621
27748
  var live = /* @__PURE__ */ new Map();
@@ -27624,8 +27751,8 @@ function brainEventsFilePath(dir, chatId) {
27624
27751
  }
27625
27752
  function lastPersistedSeq(dir, chatId) {
27626
27753
  const p = brainEventsFilePath(dir, chatId);
27627
- if (!fs56.existsSync(p)) return 0;
27628
- 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);
27629
27756
  if (lines.length === 0) return 0;
27630
27757
  try {
27631
27758
  return JSON.parse(lines[lines.length - 1]).seq || 0;
@@ -27635,9 +27762,9 @@ function lastPersistedSeq(dir, chatId) {
27635
27762
  }
27636
27763
  function readSince(dir, chatId, since) {
27637
27764
  const p = brainEventsFilePath(dir, chatId);
27638
- if (!fs56.existsSync(p)) return [];
27765
+ if (!fs57.existsSync(p)) return [];
27639
27766
  const out = [];
27640
- for (const line of fs56.readFileSync(p, "utf-8").split("\n")) {
27767
+ for (const line of fs57.readFileSync(p, "utf-8").split("\n")) {
27641
27768
  if (!line) continue;
27642
27769
  try {
27643
27770
  const rec = JSON.parse(line);
@@ -27663,12 +27790,12 @@ function beginTurn(dir, chatId) {
27663
27790
  };
27664
27791
  live.set(chatId, state);
27665
27792
  const p = brainEventsFilePath(dir, chatId);
27666
- fs56.mkdirSync(path54.dirname(p), { recursive: true });
27793
+ fs57.mkdirSync(path54.dirname(p), { recursive: true });
27667
27794
  return (event) => {
27668
27795
  state.seq += 1;
27669
27796
  const rec = { seq: state.seq, turn, ts: Date.now(), event };
27670
27797
  try {
27671
- fs56.appendFileSync(p, `${JSON.stringify(rec)}
27798
+ fs57.appendFileSync(p, `${JSON.stringify(rec)}
27672
27799
  `);
27673
27800
  } catch (err) {
27674
27801
  process.stderr.write(
@@ -27707,7 +27834,7 @@ function endTurnIfUnterminated(dir, chatId, errMessage) {
27707
27834
  event: { type: "error", error: errMessage || "turn ended unexpectedly", chatId }
27708
27835
  };
27709
27836
  try {
27710
- fs56.appendFileSync(brainEventsFilePath(dir, chatId), `${JSON.stringify(rec)}
27837
+ fs57.appendFileSync(brainEventsFilePath(dir, chatId), `${JSON.stringify(rec)}
27711
27838
  `);
27712
27839
  } catch {
27713
27840
  }
@@ -29694,55 +29821,6 @@ async function brainTerminalAgent(options) {
29694
29821
 
29695
29822
  // src/servers/pool-serve.ts
29696
29823
  import { createServer as createServer5 } from "http";
29697
-
29698
- // src/pool/agency-loop-tick.ts
29699
- function normalizeRepositories(repositories) {
29700
- const unique = /* @__PURE__ */ new Set();
29701
- for (const raw of repositories) {
29702
- const repo = raw.trim().toLowerCase();
29703
- if (/^[^/\s]+\/[^/\s]+$/.test(repo)) unique.add(repo);
29704
- }
29705
- return [...unique].sort();
29706
- }
29707
- async function runAgencyLoopTick(deps) {
29708
- const repositories = normalizeRepositories(await deps.discover());
29709
- if (repositories.length === 0) {
29710
- deps.log("no consumer agencies discovered \u2014 nothing to tick");
29711
- return { discovered: 0, claimed: 0 };
29712
- }
29713
- deps.log(
29714
- `running scheduled fan-out for ${repositories.length} consumer agenc${repositories.length === 1 ? "y" : "ies"}`
29715
- );
29716
- const clock = deps.now ?? Date.now;
29717
- let claimed = 0;
29718
- for (const repository of repositories) {
29719
- const [owner, repo] = repository.split("/");
29720
- try {
29721
- const jobId = `sched-${owner}-${repo}-${clock()}`;
29722
- const result = await deps.claim(owner, repo, {
29723
- jobId,
29724
- repo: repository,
29725
- runRequest: {
29726
- requestId: jobId,
29727
- target: { type: "workflow", id: "scheduled-fanout" },
29728
- intent: "tick",
29729
- source: "schedule"
29730
- }
29731
- });
29732
- if (result.ok) {
29733
- claimed++;
29734
- deps.log(`[${repository}] scheduled fan-out claimed ${result.machineId}`);
29735
- } else {
29736
- deps.log(`[${repository}] scheduled fan-out skipped: ${result.reason ?? "runner unavailable"}`);
29737
- }
29738
- } catch (error) {
29739
- deps.log(`[${repository}] scheduled fan-out error: ${error instanceof Error ? error.message : String(error)}`);
29740
- }
29741
- }
29742
- return { discovered: repositories.length, claimed };
29743
- }
29744
-
29745
- // src/servers/pool-serve.ts
29746
29824
  init_keys();
29747
29825
 
29748
29826
  // src/pool/registry.ts
@@ -30378,30 +30456,6 @@ async function poolServe() {
30378
30456
  const tick = setInterval(() => {
30379
30457
  registry.resyncAll().catch((err) => log(`resync tick failed: ${err instanceof Error ? err.message : String(err)}`));
30380
30458
  }, refillMs);
30381
- const discoverAgencies = async () => {
30382
- if (!appCreds) return registry.activeRepos();
30383
- const repositories = await discoverAppRepositories(appCreds);
30384
- for (const access of repositories) repoTokens.set(access.repo.toLowerCase(), access.token);
30385
- return [.../* @__PURE__ */ new Set([...repositories.map((access) => access.repo), ...registry.activeRepos()])];
30386
- };
30387
- let agencyTickInFlight = null;
30388
- const runLoopTick = () => {
30389
- if (agencyTickInFlight) return agencyTickInFlight;
30390
- agencyTickInFlight = runAgencyLoopTick({
30391
- discover: discoverAgencies,
30392
- claim: (owner, repo, req) => registry.claim(owner, repo, req),
30393
- log
30394
- }).catch((err) => log(`agency Loop tick failed: ${err instanceof Error ? err.message : String(err)}`)).finally(() => {
30395
- agencyTickInFlight = null;
30396
- });
30397
- return agencyTickInFlight;
30398
- };
30399
- const loopTickEnabled = (process.env.POOL_LOOP_TICK ?? process.env.POOL_CAPABILITY_TICK ?? "1") !== "0";
30400
- const loopTickMs = envInt2(
30401
- process.env.POOL_LOOP_TICK_MS ? "POOL_LOOP_TICK_MS" : "POOL_CAPABILITY_TICK_MS",
30402
- 15 * 6e4
30403
- );
30404
- const loopTick = loopTickEnabled ? setInterval(() => void runLoopTick(), loopTickMs) : null;
30405
30459
  const server = createServer5(async (req, res) => {
30406
30460
  try {
30407
30461
  if (!req.method || !req.url) return sendJson2(res, 400, { error: "bad request" });
@@ -30455,11 +30509,9 @@ async function poolServe() {
30455
30509
  resolve24();
30456
30510
  });
30457
30511
  });
30458
- if (loopTickEnabled) void runLoopTick();
30459
30512
  const shutdown = (signal) => {
30460
30513
  log(`${signal} \u2014 shutting down`);
30461
30514
  clearInterval(tick);
30462
- if (loopTick) clearInterval(loopTick);
30463
30515
  server.close(() => process.exit(0));
30464
30516
  };
30465
30517
  process.once("SIGINT", () => shutdown("SIGINT"));
@@ -30471,7 +30523,7 @@ async function poolServe() {
30471
30523
 
30472
30524
  // src/servers/runner-serve.ts
30473
30525
  import { spawn as spawn10 } from "child_process";
30474
- import * as fs57 from "fs";
30526
+ import * as fs58 from "fs";
30475
30527
  import { createServer as createServer6 } from "http";
30476
30528
  var DEFAULT_PORT2 = 8080;
30477
30529
  var DEFAULT_WORKDIR = "/workspace/repo";
@@ -30547,8 +30599,8 @@ async function defaultRunJob(job) {
30547
30599
  const workdir = process.env.RUNNER_WORKDIR ?? DEFAULT_WORKDIR;
30548
30600
  const branch = job.ref ?? "main";
30549
30601
  const authUrl = `https://x-access-token:${job.githubToken}@github.com/${job.repo}.git`;
30550
- fs57.rmSync(workdir, { recursive: true, force: true });
30551
- fs57.mkdirSync(workdir, { recursive: true });
30602
+ fs58.rmSync(workdir, { recursive: true, force: true });
30603
+ fs58.mkdirSync(workdir, { recursive: true });
30552
30604
  const allSecrets = typeof job.allSecrets === "string" ? job.allSecrets : JSON.stringify(job.allSecrets ?? {});
30553
30605
  const target = job.runRequest.target;
30554
30606
  const interactive = target.type === "chat";
@@ -31136,7 +31188,7 @@ async function main(argv = process.argv.slice(2)) {
31136
31188
  const args = parseArgs(argv);
31137
31189
  const cwdFlag = argv.indexOf("--cwd");
31138
31190
  const definitionCwd = cwdFlag >= 0 && argv[cwdFlag + 1] ? argv[cwdFlag + 1] : process.cwd();
31139
- 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));
31140
31192
  if (shouldHydrate) {
31141
31193
  try {
31142
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.607",
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: