@kody-ade/kody-engine 0.4.129 → 0.4.131

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/bin/kody.js CHANGED
@@ -880,7 +880,7 @@ var init_loadPriorArt = __esm({
880
880
  // package.json
881
881
  var package_default = {
882
882
  name: "@kody-ade/kody-engine",
883
- version: "0.4.129",
883
+ version: "0.4.131",
884
884
  description: "kody \u2014 autonomous development engine. Single-session Claude Code agent behind a generic executor + declarative executable profiles.",
885
885
  license: "MIT",
886
886
  type: "module",
@@ -6499,6 +6499,44 @@ function stripQuotes(value) {
6499
6499
  // src/scripts/jobState/contentsApiBackend.ts
6500
6500
  init_issue();
6501
6501
 
6502
+ // src/stateBranch.ts
6503
+ init_issue();
6504
+ var STATE_BRANCH = "kody-state";
6505
+ function is404(err) {
6506
+ const msg = err instanceof Error ? err.message : String(err);
6507
+ return /HTTP 404/i.test(msg) || /Not Found/i.test(msg);
6508
+ }
6509
+ function ensureStateBranch(owner, repo, cwd) {
6510
+ try {
6511
+ gh(["api", `/repos/${owner}/${repo}/git/ref/heads/${STATE_BRANCH}`], { cwd });
6512
+ return;
6513
+ } catch (err) {
6514
+ if (!is404(err)) throw err;
6515
+ }
6516
+ const repoInfo = JSON.parse(gh(["api", `/repos/${owner}/${repo}`], { cwd }));
6517
+ const defaultBranch2 = repoInfo.default_branch;
6518
+ if (!defaultBranch2) {
6519
+ throw new Error(`ensureStateBranch: could not resolve default branch for ${owner}/${repo}`);
6520
+ }
6521
+ const headRef = JSON.parse(
6522
+ gh(["api", `/repos/${owner}/${repo}/git/ref/heads/${defaultBranch2}`], { cwd })
6523
+ );
6524
+ const sha = headRef.object?.sha;
6525
+ if (!sha) {
6526
+ throw new Error(`ensureStateBranch: could not resolve head sha for ${owner}/${repo}@${defaultBranch2}`);
6527
+ }
6528
+ try {
6529
+ gh(["api", "--method", "POST", `/repos/${owner}/${repo}/git/refs`, "--input", "-"], {
6530
+ cwd,
6531
+ input: JSON.stringify({ ref: `refs/heads/${STATE_BRANCH}`, sha })
6532
+ });
6533
+ } catch (err) {
6534
+ const msg = err instanceof Error ? err.message : String(err);
6535
+ if (/already exists/i.test(msg) || /HTTP 422/i.test(msg)) return;
6536
+ throw err;
6537
+ }
6538
+ }
6539
+
6502
6540
  // src/scripts/issueStateComment.ts
6503
6541
  init_issue();
6504
6542
  function isStateEnvelope(x) {
@@ -6617,7 +6655,9 @@ var ContentsApiBackend = class {
6617
6655
  const filePath = stateFilePath(this.jobsDir, slug);
6618
6656
  let raw = "";
6619
6657
  try {
6620
- raw = gh(["api", `/repos/${this.owner}/${this.repo}/contents/${filePath}`], { cwd: this.cwd });
6658
+ raw = gh(["api", `/repos/${this.owner}/${this.repo}/contents/${filePath}?ref=${STATE_BRANCH}`], {
6659
+ cwd: this.cwd
6660
+ });
6621
6661
  } catch (err) {
6622
6662
  const msg = err instanceof Error ? err.message : String(err);
6623
6663
  if (/HTTP 404/i.test(msg) || /Not Found/i.test(msg)) {
@@ -6658,9 +6698,12 @@ var ContentsApiBackend = class {
6658
6698
  const body = JSON.stringify(next, null, 2) + "\n";
6659
6699
  const payload = {
6660
6700
  message: `chore(jobs): update state for ${slug} (rev ${next.rev})`,
6661
- content: Buffer.from(body, "utf-8").toString("base64")
6701
+ content: Buffer.from(body, "utf-8").toString("base64"),
6702
+ // Commit to the dedicated state branch instead of the default branch.
6703
+ branch: STATE_BRANCH
6662
6704
  };
6663
6705
  if (typeof loaded.handle === "string") payload.sha = loaded.handle;
6706
+ ensureStateBranch(this.owner, this.repo, this.cwd);
6664
6707
  gh(["api", "--method", "PUT", `/repos/${this.owner}/${this.repo}/contents/${loaded.path}`, "--input", "-"], {
6665
6708
  cwd: this.cwd,
6666
6709
  input: JSON.stringify(payload)
@@ -11195,11 +11238,13 @@ var saveGoalState = async (ctx) => {
11195
11238
  ctx.skipAgent = true;
11196
11239
  return;
11197
11240
  }
11241
+ const prev = goal.raw;
11242
+ const changed = !prev || prev.state !== goal.state || prev.lastDispatchedIssue !== goal.lastDispatchedIssue;
11198
11243
  const updated = {
11199
- ...goal.raw ?? { state: goal.state, extra: {} },
11244
+ ...prev ?? { state: goal.state, extra: {} },
11200
11245
  state: goal.state,
11201
11246
  lastDispatchedIssue: goal.lastDispatchedIssue,
11202
- updatedAt: nowIso()
11247
+ updatedAt: changed ? nowIso() : prev?.updatedAt
11203
11248
  };
11204
11249
  writeGoalState(ctx.cwd, goal.id, updated);
11205
11250
  ctx.skipAgent = true;
@@ -11891,6 +11936,69 @@ function sleep3(ms) {
11891
11936
  return new Promise((res) => setTimeout(res, ms));
11892
11937
  }
11893
11938
 
11939
+ // src/scripts/appendCompanyActivity.ts
11940
+ init_issue();
11941
+ function resolveTrigger(force) {
11942
+ const event = process.env.GITHUB_EVENT_NAME ?? "";
11943
+ if (event === "schedule") return "schedule";
11944
+ if (force || event === "issue_comment" || event === "workflow_dispatch")
11945
+ return "manual";
11946
+ return "event";
11947
+ }
11948
+ function appendLine(owner, repo, cwd, record) {
11949
+ const filePath = `.kody/activity/${record.ts.slice(0, 10)}.jsonl`;
11950
+ let existing = "";
11951
+ let sha;
11952
+ try {
11953
+ const out = gh(["api", `/repos/${owner}/${repo}/contents/${filePath}`], { cwd });
11954
+ const json = JSON.parse(out);
11955
+ if (json.sha) sha = json.sha;
11956
+ if (json.content) existing = Buffer.from(json.content, "base64").toString("utf-8");
11957
+ } catch {
11958
+ }
11959
+ const body = `${existing}${JSON.stringify(record)}
11960
+ `;
11961
+ const payload = {
11962
+ message: `chore(activity): ${record.action}`,
11963
+ content: Buffer.from(body, "utf-8").toString("base64")
11964
+ };
11965
+ if (sha) payload.sha = sha;
11966
+ gh(
11967
+ ["api", "--method", "PUT", `/repos/${owner}/${repo}/contents/${filePath}`, "--input", "-"],
11968
+ { cwd, input: JSON.stringify(payload) }
11969
+ );
11970
+ }
11971
+ var appendCompanyActivity = async (ctx, _profile, agentResult) => {
11972
+ try {
11973
+ const owner = ctx.config?.github?.owner;
11974
+ const repo = ctx.config?.github?.repo;
11975
+ const duty = String(ctx.data.jobSlug ?? ctx.args?.job ?? "").trim();
11976
+ if (!owner || !repo || !duty) return;
11977
+ const dutyTitle = ctx.data.jobTitle ?? null;
11978
+ const staff = ctx.data.workerSlug || null;
11979
+ const staffTitle = ctx.data.workerTitle || null;
11980
+ const force = ctx.args?.force === true;
11981
+ const record = {
11982
+ ts: (/* @__PURE__ */ new Date()).toISOString(),
11983
+ action: `Ran duty: ${dutyTitle ?? duty}`,
11984
+ duty,
11985
+ dutyTitle,
11986
+ staff,
11987
+ staffTitle,
11988
+ trigger: resolveTrigger(force),
11989
+ outcome: agentResult?.outcome ?? "unknown",
11990
+ durationMs: agentResult?.durationMs ?? null,
11991
+ runUrl: getRunUrl() || null
11992
+ };
11993
+ appendLine(owner, repo, ctx.cwd, record);
11994
+ } catch (err) {
11995
+ process.stderr.write(
11996
+ `[activity] company-activity append failed: ${err instanceof Error ? err.message : String(err)}
11997
+ `
11998
+ );
11999
+ }
12000
+ };
12001
+
11894
12002
  // src/scripts/warmupMcp.ts
11895
12003
  import { spawn as spawn7 } from "child_process";
11896
12004
  var PER_SERVER_TIMEOUT_MS = 6e4;
@@ -12073,7 +12181,7 @@ var writeIssueStateComment = async (ctx, _profile, _agentResult, args) => {
12073
12181
  };
12074
12182
 
12075
12183
  // src/scripts/writeJobStateFile.ts
12076
- var writeJobStateFile = async (ctx, _profile, _agentResult, args) => {
12184
+ var writeJobStateFile = async (ctx, _profile, agentResult, args) => {
12077
12185
  const parseError = ctx.data.nextStateParseError;
12078
12186
  if (parseError) {
12079
12187
  process.stderr.write(`[kody] job state write skipped: ${parseError}
@@ -12094,7 +12202,11 @@ var writeJobStateFile = async (ctx, _profile, _agentResult, args) => {
12094
12202
  ...next,
12095
12203
  data: {
12096
12204
  ...next.data,
12097
- lastFiredAt: (/* @__PURE__ */ new Date()).toISOString()
12205
+ lastFiredAt: (/* @__PURE__ */ new Date()).toISOString(),
12206
+ ...agentResult ? {
12207
+ lastOutcome: agentResult.outcome,
12208
+ lastDurationMs: agentResult.durationMs ?? null
12209
+ } : {}
12098
12210
  }
12099
12211
  };
12100
12212
  const jobsDir = String(args?.jobsDir ?? ".kody/duties");
@@ -12185,6 +12297,7 @@ var postflightScripts = {
12185
12297
  parseReproOutput,
12186
12298
  writeIssueStateComment,
12187
12299
  writeJobStateFile,
12300
+ appendCompanyActivity,
12188
12301
  requireFeedbackActions,
12189
12302
  requirePlanDeviations,
12190
12303
  verify,
@@ -68,6 +68,9 @@
68
68
  },
69
69
  {
70
70
  "script": "writeJobStateFile"
71
+ },
72
+ {
73
+ "script": "appendCompanyActivity"
71
74
  }
72
75
  ]
73
76
  }
@@ -63,6 +63,9 @@
63
63
  "with": {
64
64
  "jobsDir": ".kody/duties"
65
65
  }
66
+ },
67
+ {
68
+ "script": "appendCompanyActivity"
66
69
  }
67
70
  ]
68
71
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kody-ade/kody-engine",
3
- "version": "0.4.129",
3
+ "version": "0.4.131",
4
4
  "description": "kody — autonomous development engine. Single-session Claude Code agent behind a generic executor + declarative executable profiles.",
5
5
  "license": "MIT",
6
6
  "type": "module",