@kody-ade/kody-engine 0.4.613 → 0.4.618

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
@@ -115,6 +115,12 @@ downstream workflows or modify `.github/workflows/*`.
115
115
 
116
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
+ Live chat transcripts remain in Kody's canonical backend. In GitHub Actions,
119
+ the Engine uses the workflow's short-lived OIDC identity to call Dashboard's
120
+ repository-scoped backend endpoint; consumer repositories must not receive
121
+ `CONVEX_URL` or `KODY_SERVICE_KEY`. Local and server runtimes may connect
122
+ directly with those two variables when no GitHub Actions identity exists.
123
+
118
124
  ## Commands
119
125
 
120
126
  ```
package/dist/bin/kody.js CHANGED
@@ -15,9 +15,13 @@ var init_package = __esm({
15
15
  "package.json"() {
16
16
  package_default = {
17
17
  name: "@kody-ade/kody-engine",
18
- version: "0.4.613",
18
+ version: "0.4.618",
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
+ repository: {
22
+ type: "git",
23
+ url: "git+https://github.com/aharonyaircohen/kody-engine.git"
24
+ },
21
25
  type: "module",
22
26
  bin: {
23
27
  "kody-engine": "dist/bin/kody.js"
@@ -72,10 +76,6 @@ var init_package = __esm({
72
76
  engines: {
73
77
  node: ">=22"
74
78
  },
75
- repository: {
76
- type: "git",
77
- url: "git+https://github.com/aharonyaircohen/kody-engine.git"
78
- },
79
79
  homepage: "https://github.com/aharonyaircohen/kody-engine",
80
80
  bugs: "https://github.com/aharonyaircohen/kody-engine/issues"
81
81
  };
@@ -2322,7 +2322,9 @@ function parseDeliveryConfigAllowlist(raw) {
2322
2322
  if (filePath !== "kody.config.json" || !Array.isArray(paths) || paths.length === 0 || paths.length > 32) {
2323
2323
  throw new Error("contract.json deliveryConfigAllowlist contains an unsupported config file or path list");
2324
2324
  }
2325
- if (!paths.every((value) => typeof value === "string" && /^[A-Za-z][A-Za-z0-9]*(?:\.[A-Za-z][A-Za-z0-9]*)*$/.test(value))) {
2325
+ if (!paths.every(
2326
+ (value) => typeof value === "string" && /^[A-Za-z][A-Za-z0-9]*(?:\.[A-Za-z][A-Za-z0-9]*)*$/.test(value)
2327
+ )) {
2326
2328
  throw new Error("contract.json deliveryConfigAllowlist must contain safe dotted config paths");
2327
2329
  }
2328
2330
  parsed[filePath] = [...new Set(paths)];
@@ -2367,9 +2369,7 @@ function parseCapabilityRequirements(raw) {
2367
2369
  if (raw.githubTestToken !== void 0 && typeof raw.githubTestToken !== "boolean") {
2368
2370
  throw new Error("contract.json requirements.githubTestToken must be boolean");
2369
2371
  }
2370
- if (raw.qaAccountCredentials !== void 0 && (!Array.isArray(raw.qaAccountCredentials) || raw.qaAccountCredentials.length === 0 || !raw.qaAccountCredentials.every(
2371
- (name) => typeof name === "string" && /^[A-Z][A-Z0-9_]{0,127}$/.test(name)
2372
- ))) {
2372
+ if (raw.qaAccountCredentials !== void 0 && (!Array.isArray(raw.qaAccountCredentials) || raw.qaAccountCredentials.length === 0 || !raw.qaAccountCredentials.every((name) => typeof name === "string" && /^[A-Z][A-Z0-9_]{0,127}$/.test(name)))) {
2373
2373
  throw new Error("contract.json requirements.qaAccountCredentials must contain valid credential names");
2374
2374
  }
2375
2375
  if (raw.qaAccountModelSettings !== void 0 && !isPlainObject(raw.qaAccountModelSettings)) {
@@ -8910,9 +8910,7 @@ function abortUnfinishedGitOps(cwd) {
8910
8910
  return aborted;
8911
8911
  }
8912
8912
  function isExplicitlyAllowed(p, allowlist) {
8913
- return allowlist.some(
8914
- (entry) => entry.endsWith("/**") ? p.startsWith(entry.slice(0, -2)) : p === entry
8915
- );
8913
+ return allowlist.some((entry) => entry.endsWith("/**") ? p.startsWith(entry.slice(0, -2)) : p === entry);
8916
8914
  }
8917
8915
  function isForbiddenPath(p, deliveryPathAllowlist = []) {
8918
8916
  if (FORBIDDEN_PATH_EXACT.has(p)) return true;
@@ -12714,18 +12712,10 @@ var init_commitAndPush = __esm({
12714
12712
  const deliveryPathAllowlist = Array.isArray(ctx.data.deliveryPathAllowlist) ? ctx.data.deliveryPathAllowlist : [];
12715
12713
  const deliveryConfigAllowlist = ctx.data.deliveryConfigAllowlist && typeof ctx.data.deliveryConfigAllowlist === "object" ? ctx.data.deliveryConfigAllowlist : {};
12716
12714
  try {
12717
- const result2 = commitAndPush(
12718
- branch,
12719
- message,
12720
- ctx.cwd,
12721
- deliveryPathAllowlist,
12722
- deliveryConfigAllowlist
12723
- );
12715
+ const result2 = commitAndPush(branch, message, ctx.cwd, deliveryPathAllowlist, deliveryConfigAllowlist);
12724
12716
  ctx.data.commitResult = result2;
12725
12717
  const postCommitFiles = result2.committed ? listFilesInCommit("HEAD", ctx.cwd) : listChangedFiles(ctx.cwd);
12726
- ctx.data.changedFiles = postCommitFiles.filter(
12727
- (f) => !isForbiddenPath(f, deliveryPathAllowlist)
12728
- );
12718
+ ctx.data.changedFiles = postCommitFiles.filter((f) => !isForbiddenPath(f, deliveryPathAllowlist));
12729
12719
  if (result2.committed && !result2.pushed) {
12730
12720
  const reason = result2.pushError ?? "push failed (no error detail)";
12731
12721
  ctx.data.commitCrash = reason;
@@ -16553,8 +16543,8 @@ var loadLiveAgent;
16553
16543
  var init_loadLiveAgent = __esm({
16554
16544
  "src/scripts/loadLiveAgent.ts"() {
16555
16545
  "use strict";
16556
- init_definition_paths();
16557
16546
  init_agents();
16547
+ init_definition_paths();
16558
16548
  init_state_backend();
16559
16549
  loadLiveAgent = async (ctx, profile) => {
16560
16550
  const agent = String(ctx.args.agent ?? ctx.data.jobAgent ?? "").trim();
@@ -18594,10 +18584,7 @@ function parseSetCookie(value, hostname) {
18594
18584
  const attributes = /* @__PURE__ */ new Map();
18595
18585
  for (const part of parts) {
18596
18586
  const index = part.indexOf("=");
18597
- attributes.set(
18598
- (index < 0 ? part : part.slice(0, index)).toLowerCase(),
18599
- index < 0 ? "" : part.slice(index + 1)
18600
- );
18587
+ attributes.set((index < 0 ? part : part.slice(0, index)).toLowerCase(), index < 0 ? "" : part.slice(index + 1));
18601
18588
  }
18602
18589
  const sameSite = attributes.get("samesite")?.toLowerCase();
18603
18590
  return {
@@ -18722,11 +18709,9 @@ function mergeStorageStates(existingPath, nextPath) {
18722
18709
  }
18723
18710
  origins.set(entry.origin, { origin: entry.origin, localStorage: [...localStorage.values()] });
18724
18711
  }
18725
- fs49.writeFileSync(
18726
- nextPath,
18727
- JSON.stringify({ cookies: [...cookies.values()], origins: [...origins.values()] }),
18728
- { mode: 384 }
18729
- );
18712
+ fs49.writeFileSync(nextPath, JSON.stringify({ cookies: [...cookies.values()], origins: [...origins.values()] }), {
18713
+ mode: 384
18714
+ });
18730
18715
  }
18731
18716
  function configurePlaywright(profile, storageStatePath) {
18732
18717
  const playwright = profile.claudeCode.mcpServers.find((server) => server.name === "playwright");
@@ -20742,12 +20727,19 @@ function buildTickChildEnv(parent, force, loaded) {
20742
20727
  "GH_PAT",
20743
20728
  "GITHUB_TOKEN",
20744
20729
  "GITHUB_REPOSITORY",
20730
+ "GITHUB_REPOSITORY_ID",
20731
+ "GITHUB_REPOSITORY_OWNER_ID",
20745
20732
  "GITHUB_REF",
20746
20733
  "GITHUB_SHA",
20747
20734
  "GITHUB_RUN_ID",
20748
20735
  "GITHUB_RUN_ATTEMPT",
20736
+ "GITHUB_SERVER_URL",
20737
+ "GITHUB_EVENT_NAME",
20749
20738
  "GITHUB_WORKFLOW",
20739
+ "GITHUB_WORKFLOW_REF",
20750
20740
  "GITHUB_ACTIONS",
20741
+ "ACTIONS_ID_TOKEN_REQUEST_URL",
20742
+ "ACTIONS_ID_TOKEN_REQUEST_TOKEN",
20751
20743
  "CI",
20752
20744
  "KODY_DRY_RUN",
20753
20745
  "KODY_NO_COMMIT"
@@ -20981,25 +20973,6 @@ var init_runTickScript = __esm({
20981
20973
  }
20982
20974
  });
20983
20975
 
20984
- // src/scripts/saveManagedGoalState.ts
20985
- var saveManagedGoalState;
20986
- var init_saveManagedGoalState = __esm({
20987
- "src/scripts/saveManagedGoalState.ts"() {
20988
- "use strict";
20989
- init_state2();
20990
- saveManagedGoalState = async (ctx) => {
20991
- ctx.skipAgent = true;
20992
- const goal = ctx.data.goal;
20993
- if (!goal?.raw) return;
20994
- const original = typeof ctx.data.goalOriginalStateText === "string" ? ctx.data.goalOriginalStateText : "";
20995
- const changedBeforeTimestamp = original !== serializeGoalState(goal.raw);
20996
- const updated = changedBeforeTimestamp ? { ...goal.raw, updatedAt: nowIso() } : goal.raw;
20997
- ctx.data.goalPersistState = updated;
20998
- ctx.data.goalPersistChanged = original !== serializeGoalState(updated);
20999
- };
21000
- }
21001
- });
21002
-
21003
20976
  // src/scripts/saveLiveAgentState.ts
21004
20977
  var saveLiveAgentState;
21005
20978
  var init_saveLiveAgentState = __esm({
@@ -21035,6 +21008,25 @@ var init_saveLiveAgentState = __esm({
21035
21008
  }
21036
21009
  });
21037
21010
 
21011
+ // src/scripts/saveManagedGoalState.ts
21012
+ var saveManagedGoalState;
21013
+ var init_saveManagedGoalState = __esm({
21014
+ "src/scripts/saveManagedGoalState.ts"() {
21015
+ "use strict";
21016
+ init_state2();
21017
+ saveManagedGoalState = async (ctx) => {
21018
+ ctx.skipAgent = true;
21019
+ const goal = ctx.data.goal;
21020
+ if (!goal?.raw) return;
21021
+ const original = typeof ctx.data.goalOriginalStateText === "string" ? ctx.data.goalOriginalStateText : "";
21022
+ const changedBeforeTimestamp = original !== serializeGoalState(goal.raw);
21023
+ const updated = changedBeforeTimestamp ? { ...goal.raw, updatedAt: nowIso() } : goal.raw;
21024
+ ctx.data.goalPersistState = updated;
21025
+ ctx.data.goalPersistChanged = original !== serializeGoalState(updated);
21026
+ };
21027
+ }
21028
+ });
21029
+
21038
21030
  // src/scripts/setCommentTarget.ts
21039
21031
  var setCommentTarget;
21040
21032
  var init_setCommentTarget = __esm({
@@ -22383,8 +22375,8 @@ var init_scripts = __esm({
22383
22375
  init_runScheduledImplementationTick();
22384
22376
  init_runSimpleCapabilityScript();
22385
22377
  init_runTickScript();
22386
- init_saveManagedGoalState();
22387
22378
  init_saveLiveAgentState();
22379
+ init_saveManagedGoalState();
22388
22380
  init_saveTaskState();
22389
22381
  init_setCommentTarget();
22390
22382
  init_setLifecycleLabel();
@@ -25587,6 +25579,7 @@ function makeRunId(sessionId, suffix) {
25587
25579
  }
25588
25580
 
25589
25581
  // src/chat/session-store.ts
25582
+ init_kody_api_client();
25590
25583
  init_convex_client();
25591
25584
  import { anyApi as anyApi2 } from "convex/server";
25592
25585
  function currentEpochTurns(result) {
@@ -25614,11 +25607,12 @@ function createSessionStore(opts) {
25614
25607
  warn: (message) => process.stderr.write(`[kody:chat:store] ${message}
25615
25608
  `)
25616
25609
  };
25617
- const client = opts.client !== void 0 ? opts.client : createConvexClientFromEnv();
25618
- const tenantId2 = opts.tenantId ?? process.env.GITHUB_REPOSITORY ?? "";
25610
+ const env = opts.env ?? process.env;
25611
+ const client = opts.client !== void 0 ? opts.client : hasGitHubActionsIdentity(env) ? createKodyApiBackendClient(env) : createConvexClientFromEnv(env);
25612
+ const tenantId2 = opts.tenantId ?? env.GITHUB_REPOSITORY ?? "";
25619
25613
  if (!client || !tenantId2) {
25620
25614
  throw new Error(
25621
- "Canonical Convex conversation storage is required (CONVEX_URL, KODY_SERVICE_KEY, and GITHUB_REPOSITORY)"
25615
+ "Canonical conversation storage requires GitHub Actions identity or direct Kody backend credentials"
25622
25616
  );
25623
25617
  }
25624
25618
  logger.info(`conversation ${opts.sessionId}: using canonical Convex store (tenant ${tenantId2})`);
@@ -27196,6 +27190,10 @@ function ensurePackageManagerInstalled(pm, cwd) {
27196
27190
  return shellOut("npm", ["install", "-g", spec], cwd);
27197
27191
  }
27198
27192
  function installDeps(pm, cwd) {
27193
+ if (!fs56.existsSync(path53.join(cwd, "package.json"))) {
27194
+ process.stdout.write("\u2192 kody: no package.json found \u2014 skipping consumer dependency install\n");
27195
+ return 0;
27196
+ }
27199
27197
  const ensureCode = ensurePackageManagerInstalled(pm, cwd);
27200
27198
  if (ensureCode !== 0) return ensureCode;
27201
27199
  const args = {
@@ -29289,8 +29287,137 @@ init_repoWorkspace();
29289
29287
  import * as path58 from "path";
29290
29288
  import { createInterface as createInterface2 } from "readline";
29291
29289
 
29290
+ // src/terminal/brain-terminal-adapters.ts
29291
+ import { spawn as spawn9 } from "child_process";
29292
+ import { createHash as createHash9, randomBytes as randomBytes2 } from "crypto";
29293
+ import { mkdir, readFile, rename, writeFile as writeFile2 } from "fs/promises";
29294
+ import * as path57 from "path";
29295
+ function runTerminalCommand(command, args, input) {
29296
+ return new Promise((resolve24, reject) => {
29297
+ const child = spawn9(command, args, { stdio: ["pipe", "pipe", "pipe"] });
29298
+ const stdout = [];
29299
+ const stderr = [];
29300
+ child.stdout.on("data", (chunk) => stdout.push(chunk));
29301
+ child.stderr.on("data", (chunk) => stderr.push(chunk));
29302
+ child.on("error", reject);
29303
+ child.on("close", (code) => {
29304
+ resolve24({
29305
+ code: code ?? 1,
29306
+ stdout: Buffer.concat(stdout).toString("utf8"),
29307
+ stderr: Buffer.concat(stderr).toString("utf8")
29308
+ });
29309
+ });
29310
+ if (input !== void 0) child.stdin.end(input);
29311
+ else child.stdin.end();
29312
+ });
29313
+ }
29314
+ function storeKey(id) {
29315
+ return createHash9("sha256").update(id).digest("hex");
29316
+ }
29317
+ function isStoredSession(value) {
29318
+ if (!value || typeof value !== "object") return false;
29319
+ const session = value;
29320
+ return session.version === 1 && typeof session.id === "string" && typeof session.sessionName === "string" && typeof session.cwd === "string" && Number.isInteger(session.generation) && Number.isInteger(session.revision) && typeof session.output === "string" && typeof session.scope?.owner === "string" && typeof session.scope?.repo === "string" && typeof session.scope?.conversationId === "string";
29321
+ }
29322
+ var FileBrainTerminalMetadataStore = class {
29323
+ constructor(root) {
29324
+ this.root = root;
29325
+ }
29326
+ root;
29327
+ file(id) {
29328
+ return path57.join(this.root, `${storeKey(id)}.json`);
29329
+ }
29330
+ async read(id) {
29331
+ try {
29332
+ const parsed = JSON.parse(await readFile(this.file(id), "utf8"));
29333
+ if (!isStoredSession(parsed) || parsed.id !== id) {
29334
+ throw new Error("stored terminal session is invalid");
29335
+ }
29336
+ return parsed;
29337
+ } catch (error) {
29338
+ if (error.code === "ENOENT") return null;
29339
+ throw error;
29340
+ }
29341
+ }
29342
+ async write(session) {
29343
+ await mkdir(this.root, { recursive: true, mode: 448 });
29344
+ const target = this.file(session.id);
29345
+ const temporary = `${target}.${process.pid}.${randomBytes2(6).toString("hex")}.tmp`;
29346
+ await writeFile2(temporary, `${JSON.stringify(session)}
29347
+ `, { mode: 384 });
29348
+ await rename(temporary, target);
29349
+ }
29350
+ };
29351
+ function commandError(action, result) {
29352
+ const detail = result.stderr.trim() || result.stdout.trim() || `exit ${result.code}`;
29353
+ return new Error(`tmux ${action} failed: ${detail.slice(0, 500)}`);
29354
+ }
29355
+ var TmuxBrainTerminalRuntime = class {
29356
+ constructor(run = runTerminalCommand) {
29357
+ this.run = run;
29358
+ }
29359
+ run;
29360
+ async tmux(action, args, input) {
29361
+ const result = await this.run("tmux", args, input);
29362
+ if (result.code !== 0) throw commandError(action, result);
29363
+ return result;
29364
+ }
29365
+ async start(sessionName2, cwd, cols, rows) {
29366
+ await this.tmux("start", [
29367
+ "new-session",
29368
+ "-d",
29369
+ "-s",
29370
+ sessionName2,
29371
+ "-x",
29372
+ String(cols),
29373
+ "-y",
29374
+ String(rows),
29375
+ "-c",
29376
+ cwd,
29377
+ "/bin/bash",
29378
+ "-l"
29379
+ ]);
29380
+ await this.tmux("configure", ["set-option", "-t", sessionName2, "status", "off"]);
29381
+ await this.tmux("configure", ["set-option", "-t", sessionName2, "history-limit", "50000"]);
29382
+ await this.tmux("configure", ["set-option", "-w", "-t", sessionName2, "remain-on-exit", "on"]);
29383
+ const inspected = await this.inspect(sessionName2);
29384
+ if (!inspected.alive || inspected.processId === null) throw new Error("tmux terminal did not start");
29385
+ return { processId: inspected.processId };
29386
+ }
29387
+ async inspect(sessionName2) {
29388
+ const result = await this.run("tmux", ["list-panes", "-t", sessionName2, "-F", "#{pane_dead}:#{pane_pid}"]);
29389
+ if (result.code !== 0) return { alive: false, processId: null };
29390
+ const [dead, pid] = result.stdout.trim().split(":");
29391
+ const processId = Number(pid);
29392
+ return {
29393
+ alive: dead === "0" && Number.isInteger(processId) && processId > 0,
29394
+ processId: Number.isInteger(processId) && processId > 0 ? processId : null
29395
+ };
29396
+ }
29397
+ async capture(sessionName2) {
29398
+ const alternate = await this.run("tmux", ["display-message", "-p", "-t", sessionName2, "#{alternate_on}"]);
29399
+ if (alternate.code !== 0) throw commandError("inspect screen", alternate);
29400
+ const args = alternate.stdout.trim() === "1" ? ["capture-pane", "-p", "-e", "-t", sessionName2] : ["capture-pane", "-p", "-e", "-J", "-S", "-50000", "-t", sessionName2];
29401
+ return (await this.tmux("capture", args)).stdout;
29402
+ }
29403
+ async input(sessionName2, data) {
29404
+ const bufferName = `kody_${randomBytes2(8).toString("hex")}`;
29405
+ await this.tmux("load input", ["load-buffer", "-b", bufferName, "-"], data);
29406
+ await this.tmux("paste input", ["paste-buffer", "-d", "-b", bufferName, "-t", sessionName2]);
29407
+ }
29408
+ async resize(sessionName2, cols, rows) {
29409
+ await this.tmux("resize", ["resize-window", "-t", sessionName2, "-x", String(cols), "-y", String(rows)]);
29410
+ }
29411
+ async stop(sessionName2) {
29412
+ const result = await this.run("tmux", ["kill-session", "-t", sessionName2]);
29413
+ if (result.code !== 0 && !/can't find session|no server running/i.test(result.stderr)) {
29414
+ throw commandError("stop", result);
29415
+ }
29416
+ }
29417
+ };
29418
+
29292
29419
  // src/terminal/brain-terminal-session.ts
29293
- import { createHash as createHash9 } from "crypto";
29420
+ import { createHash as createHash10 } from "crypto";
29294
29421
  var MAX_CAPTURE_CHARS = 2e5;
29295
29422
  function requiredIdentifier(value, name, max = 240) {
29296
29423
  if (typeof value !== "string" || !value.trim() || value.length > max) {
@@ -29371,7 +29498,7 @@ function parseBrainTerminalCommand(value) {
29371
29498
  }
29372
29499
  }
29373
29500
  function sessionName(id) {
29374
- return `kody_${createHash9("sha256").update(id).digest("hex").slice(0, 32)}`;
29501
+ return `kody_${createHash10("sha256").update(id).digest("hex").slice(0, 32)}`;
29375
29502
  }
29376
29503
  function stateEvent(session) {
29377
29504
  return {
@@ -29433,12 +29560,7 @@ var BrainTerminalSessionAgent = class {
29433
29560
  await this.persist(session);
29434
29561
  let started;
29435
29562
  try {
29436
- started = await this.dependencies.runtime.start(
29437
- session.sessionName,
29438
- session.cwd,
29439
- session.cols,
29440
- session.rows
29441
- );
29563
+ started = await this.dependencies.runtime.start(session.sessionName, session.cwd, session.cols, session.rows);
29442
29564
  } catch (cause) {
29443
29565
  session = { ...session, state: "failed", updatedAt: this.now() };
29444
29566
  await this.persist(session);
@@ -29597,155 +29719,6 @@ var BrainTerminalSessionAgent = class {
29597
29719
  }
29598
29720
  };
29599
29721
 
29600
- // src/terminal/brain-terminal-adapters.ts
29601
- import { createHash as createHash10, randomBytes as randomBytes2 } from "crypto";
29602
- import { mkdir, readFile, rename, writeFile as writeFile2 } from "fs/promises";
29603
- import * as path57 from "path";
29604
- import { spawn as spawn9 } from "child_process";
29605
- function runTerminalCommand(command, args, input) {
29606
- return new Promise((resolve24, reject) => {
29607
- const child = spawn9(command, args, { stdio: ["pipe", "pipe", "pipe"] });
29608
- const stdout = [];
29609
- const stderr = [];
29610
- child.stdout.on("data", (chunk) => stdout.push(chunk));
29611
- child.stderr.on("data", (chunk) => stderr.push(chunk));
29612
- child.on("error", reject);
29613
- child.on("close", (code) => {
29614
- resolve24({
29615
- code: code ?? 1,
29616
- stdout: Buffer.concat(stdout).toString("utf8"),
29617
- stderr: Buffer.concat(stderr).toString("utf8")
29618
- });
29619
- });
29620
- if (input !== void 0) child.stdin.end(input);
29621
- else child.stdin.end();
29622
- });
29623
- }
29624
- function storeKey(id) {
29625
- return createHash10("sha256").update(id).digest("hex");
29626
- }
29627
- function isStoredSession(value) {
29628
- if (!value || typeof value !== "object") return false;
29629
- const session = value;
29630
- return session.version === 1 && typeof session.id === "string" && typeof session.sessionName === "string" && typeof session.cwd === "string" && Number.isInteger(session.generation) && Number.isInteger(session.revision) && typeof session.output === "string" && typeof session.scope?.owner === "string" && typeof session.scope?.repo === "string" && typeof session.scope?.conversationId === "string";
29631
- }
29632
- var FileBrainTerminalMetadataStore = class {
29633
- constructor(root) {
29634
- this.root = root;
29635
- }
29636
- root;
29637
- file(id) {
29638
- return path57.join(this.root, `${storeKey(id)}.json`);
29639
- }
29640
- async read(id) {
29641
- try {
29642
- const parsed = JSON.parse(await readFile(this.file(id), "utf8"));
29643
- if (!isStoredSession(parsed) || parsed.id !== id) {
29644
- throw new Error("stored terminal session is invalid");
29645
- }
29646
- return parsed;
29647
- } catch (error) {
29648
- if (error.code === "ENOENT") return null;
29649
- throw error;
29650
- }
29651
- }
29652
- async write(session) {
29653
- await mkdir(this.root, { recursive: true, mode: 448 });
29654
- const target = this.file(session.id);
29655
- const temporary = `${target}.${process.pid}.${randomBytes2(6).toString("hex")}.tmp`;
29656
- await writeFile2(temporary, `${JSON.stringify(session)}
29657
- `, { mode: 384 });
29658
- await rename(temporary, target);
29659
- }
29660
- };
29661
- function commandError(action, result) {
29662
- const detail = result.stderr.trim() || result.stdout.trim() || `exit ${result.code}`;
29663
- return new Error(`tmux ${action} failed: ${detail.slice(0, 500)}`);
29664
- }
29665
- var TmuxBrainTerminalRuntime = class {
29666
- constructor(run = runTerminalCommand) {
29667
- this.run = run;
29668
- }
29669
- run;
29670
- async tmux(action, args, input) {
29671
- const result = await this.run("tmux", args, input);
29672
- if (result.code !== 0) throw commandError(action, result);
29673
- return result;
29674
- }
29675
- async start(sessionName2, cwd, cols, rows) {
29676
- await this.tmux("start", [
29677
- "new-session",
29678
- "-d",
29679
- "-s",
29680
- sessionName2,
29681
- "-x",
29682
- String(cols),
29683
- "-y",
29684
- String(rows),
29685
- "-c",
29686
- cwd,
29687
- "/bin/bash",
29688
- "-l"
29689
- ]);
29690
- await this.tmux("configure", ["set-option", "-t", sessionName2, "status", "off"]);
29691
- await this.tmux("configure", ["set-option", "-t", sessionName2, "history-limit", "50000"]);
29692
- await this.tmux("configure", ["set-option", "-w", "-t", sessionName2, "remain-on-exit", "on"]);
29693
- const inspected = await this.inspect(sessionName2);
29694
- if (!inspected.alive || inspected.processId === null) throw new Error("tmux terminal did not start");
29695
- return { processId: inspected.processId };
29696
- }
29697
- async inspect(sessionName2) {
29698
- const result = await this.run("tmux", [
29699
- "list-panes",
29700
- "-t",
29701
- sessionName2,
29702
- "-F",
29703
- "#{pane_dead}:#{pane_pid}"
29704
- ]);
29705
- if (result.code !== 0) return { alive: false, processId: null };
29706
- const [dead, pid] = result.stdout.trim().split(":");
29707
- const processId = Number(pid);
29708
- return {
29709
- alive: dead === "0" && Number.isInteger(processId) && processId > 0,
29710
- processId: Number.isInteger(processId) && processId > 0 ? processId : null
29711
- };
29712
- }
29713
- async capture(sessionName2) {
29714
- const alternate = await this.run("tmux", [
29715
- "display-message",
29716
- "-p",
29717
- "-t",
29718
- sessionName2,
29719
- "#{alternate_on}"
29720
- ]);
29721
- if (alternate.code !== 0) throw commandError("inspect screen", alternate);
29722
- const args = alternate.stdout.trim() === "1" ? ["capture-pane", "-p", "-e", "-t", sessionName2] : ["capture-pane", "-p", "-e", "-J", "-S", "-50000", "-t", sessionName2];
29723
- return (await this.tmux("capture", args)).stdout;
29724
- }
29725
- async input(sessionName2, data) {
29726
- const bufferName = `kody_${randomBytes2(8).toString("hex")}`;
29727
- await this.tmux("load input", ["load-buffer", "-b", bufferName, "-"], data);
29728
- await this.tmux("paste input", ["paste-buffer", "-d", "-b", bufferName, "-t", sessionName2]);
29729
- }
29730
- async resize(sessionName2, cols, rows) {
29731
- await this.tmux("resize", [
29732
- "resize-window",
29733
- "-t",
29734
- sessionName2,
29735
- "-x",
29736
- String(cols),
29737
- "-y",
29738
- String(rows)
29739
- ]);
29740
- }
29741
- async stop(sessionName2) {
29742
- const result = await this.run("tmux", ["kill-session", "-t", sessionName2]);
29743
- if (result.code !== 0 && !/can't find session|no server running/i.test(result.stderr)) {
29744
- throw commandError("stop", result);
29745
- }
29746
- }
29747
- };
29748
-
29749
29722
  // src/servers/brain-terminal-agent.ts
29750
29723
  var DEFAULT_POLL_INTERVAL_MS = 150;
29751
29724
  function writeEvent(output, event) {
File without changes
package/package.json CHANGED
@@ -1,8 +1,12 @@
1
1
  {
2
2
  "name": "@kody-ade/kody-engine",
3
- "version": "0.4.613",
4
- "description": "kody autonomous development engine. Single-session Claude Code agent behind a generic executor + declarative implementation profiles.",
3
+ "version": "0.4.618",
4
+ "description": "kody \u2014 autonomous development engine. Single-session Claude Code agent behind a generic executor + declarative implementation profiles.",
5
5
  "license": "MIT",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/aharonyaircohen/kody-engine.git"
9
+ },
6
10
  "type": "module",
7
11
  "bin": {
8
12
  "kody-engine": "dist/bin/kody.js"
@@ -12,6 +16,30 @@
12
16
  "templates",
13
17
  "kody.config.schema.json"
14
18
  ],
19
+ "scripts": {
20
+ "kody:run": "tsx bin/kody.ts",
21
+ "serve": "tsx bin/kody.ts serve",
22
+ "serve:vscode": "tsx bin/kody.ts serve vscode",
23
+ "serve:claude": "tsx bin/kody.ts serve claude",
24
+ "clean:dist": "node scripts/clean-dist.cjs",
25
+ "build": "pnpm clean:dist && tsup && node scripts/copy-assets.cjs",
26
+ "check:modularity": "tsx scripts/check-script-modularity.ts",
27
+ "pretest": "pnpm check:modularity",
28
+ "test": "vitest run tests/unit tests/int --coverage",
29
+ "posttest": "tsx scripts/check-coverage-floor.ts",
30
+ "test:smoke": "vitest run tests/smoke --no-coverage",
31
+ "test:e2e": "vitest run tests/e2e --no-coverage",
32
+ "verify:live-release": "tsx scripts/live-release-gate.ts",
33
+ "test:runtime-services": "node --test \"tests/runtime-services/*.test.mjs\"",
34
+ "test:all": "vitest run tests --no-coverage",
35
+ "typecheck": "tsc --noEmit",
36
+ "lint": "biome check",
37
+ "lint:fix": "biome check --write",
38
+ "format": "biome format --write",
39
+ "verify:package": "node scripts/verify-package-tarball.cjs",
40
+ "brain:publish": "docker buildx build --platform linux/amd64 -f runner/Dockerfile.brain --build-arg KODY_ENGINE_REF=$(git rev-parse HEAD) -t ghcr.io/${KODY_BRAIN_GHCR_OWNER:-aharonyaircohen}/kody-brain:latest --push runner",
41
+ "prepublishOnly": "pnpm typecheck && pnpm test:runtime-services && pnpm build && pnpm verify:package"
42
+ },
15
43
  "dependencies": {
16
44
  "@actions/cache": "^6.0.0",
17
45
  "@anthropic-ai/claude-agent-sdk": "0.2.119",
@@ -33,33 +61,6 @@
33
61
  "engines": {
34
62
  "node": ">=22"
35
63
  },
36
- "repository": {
37
- "type": "git",
38
- "url": "git+https://github.com/aharonyaircohen/kody-engine.git"
39
- },
40
64
  "homepage": "https://github.com/aharonyaircohen/kody-engine",
41
- "bugs": "https://github.com/aharonyaircohen/kody-engine/issues",
42
- "scripts": {
43
- "kody:run": "tsx bin/kody.ts",
44
- "serve": "tsx bin/kody.ts serve",
45
- "serve:vscode": "tsx bin/kody.ts serve vscode",
46
- "serve:claude": "tsx bin/kody.ts serve claude",
47
- "clean:dist": "node scripts/clean-dist.cjs",
48
- "build": "pnpm clean:dist && tsup && node scripts/copy-assets.cjs",
49
- "check:modularity": "tsx scripts/check-script-modularity.ts",
50
- "pretest": "pnpm check:modularity",
51
- "test": "vitest run tests/unit tests/int --coverage",
52
- "posttest": "tsx scripts/check-coverage-floor.ts",
53
- "test:smoke": "vitest run tests/smoke --no-coverage",
54
- "test:e2e": "vitest run tests/e2e --no-coverage",
55
- "verify:live-release": "tsx scripts/live-release-gate.ts",
56
- "test:runtime-services": "node --test \"tests/runtime-services/*.test.mjs\"",
57
- "test:all": "vitest run tests --no-coverage",
58
- "typecheck": "tsc --noEmit",
59
- "lint": "biome check",
60
- "lint:fix": "biome check --write",
61
- "format": "biome format --write",
62
- "verify:package": "node scripts/verify-package-tarball.cjs",
63
- "brain:publish": "docker buildx build --platform linux/amd64 -f runner/Dockerfile.brain --build-arg KODY_ENGINE_REF=$(git rev-parse HEAD) -t ghcr.io/${KODY_BRAIN_GHCR_OWNER:-aharonyaircohen}/kody-brain:latest --push runner"
64
- }
65
- }
65
+ "bugs": "https://github.com/aharonyaircohen/kody-engine/issues"
66
+ }
@@ -1,4 +1,5 @@
1
- # Generated by @kody-ade/kody-engine. Do not hand-edit consumer copies.
1
+ # Generated by @kody-ade/kody-engine. Do not edit this workflow.
2
+ # Fix behavior in Kody Engine or the Store definition that owns it, then regenerate.
2
3
  #
3
4
  # GitHub authentication fallback:
4
5
  # 1. GH_PAT or legacy KODY_TOKEN, when configured.