@kody-ade/kody-engine 0.4.245 → 0.4.247

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.
Files changed (2) hide show
  1. package/dist/bin/kody.js +167 -2
  2. package/package.json +1 -1
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.245",
18
+ version: "0.4.247",
19
19
  description: "kody \u2014 autonomous development engine. Single-session Claude Code agent behind a generic executor + declarative agentAction profiles.",
20
20
  license: "MIT",
21
21
  type: "module",
@@ -146,7 +146,7 @@ var init_claudeBinary = __esm({
146
146
  // src/issue.ts
147
147
  import { execFileSync } from "child_process";
148
148
  function ghToken() {
149
- return process.env.GH_PAT?.trim() || process.env.GH_TOKEN;
149
+ return process.env.GITHUB_TOKEN?.trim() || process.env.KODY_TOKEN?.trim() || process.env.GH_TOKEN?.trim() || process.env.GH_PAT?.trim();
150
150
  }
151
151
  function gh(args, options) {
152
152
  const token = ghToken();
@@ -658,6 +658,14 @@ function parseGoalActivations(raw) {
658
658
  const idPrefix = parseSlug(r.idPrefix, "company.activeGoals.idPrefix");
659
659
  if (idPrefix) entry.idPrefix = idPrefix;
660
660
  }
661
+ if (r.preferredRunTime !== void 0) {
662
+ const preferredRunTime = parsePreferredRunTime(r.preferredRunTime);
663
+ if (!preferredRunTime)
664
+ throw new Error(
665
+ `kody.config.json: company.activeGoals preferredRunTime must be { "time": "HH:MM", "timezone": "Area/Name" }`
666
+ );
667
+ entry.preferredRunTime = preferredRunTime;
668
+ }
661
669
  const facts = recordValue(r.facts);
662
670
  if (r.facts !== void 0 && !facts)
663
671
  throw new Error(`kody.config.json: company.activeGoals facts must be an object`);
@@ -666,6 +674,13 @@ function parseGoalActivations(raw) {
666
674
  }
667
675
  return out;
668
676
  }
677
+ function parsePreferredRunTime(raw) {
678
+ const r = recordValue(raw);
679
+ if (!r) return null;
680
+ if (typeof r.time !== "string" || !/^([01]\d|2[0-3]):[0-5]\d$/.test(r.time.trim())) return null;
681
+ if (typeof r.timezone !== "string" || !r.timezone.trim()) return null;
682
+ return { time: r.time.trim(), timezone: r.timezone.trim() };
683
+ }
669
684
  function parseSlugArray(raw, field) {
670
685
  if (!Array.isArray(raw)) throw new Error(`kody.config.json: ${field} must be an array of strings`);
671
686
  const out = [];
@@ -5958,6 +5973,20 @@ function asRoute(value) {
5958
5973
  }
5959
5974
  return route;
5960
5975
  }
5976
+ function asPreferredRunTime(value) {
5977
+ const raw = asRecord(value);
5978
+ if (!raw) return void 0;
5979
+ if (typeof raw.time !== "string" || !/^([01]\d|2[0-3]):[0-5]\d$/.test(raw.time)) return void 0;
5980
+ if (typeof raw.timezone !== "string" || raw.timezone.trim().length === 0) return void 0;
5981
+ return { time: raw.time, timezone: raw.timezone };
5982
+ }
5983
+ function asLoopTarget(value) {
5984
+ const raw = asRecord(value);
5985
+ if (!raw) return void 0;
5986
+ if (raw.type !== "goal" && raw.type !== "agentResponsibility") return void 0;
5987
+ if (typeof raw.id !== "string" || raw.id.trim().length === 0) return void 0;
5988
+ return { type: raw.type, id: raw.id };
5989
+ }
5961
5990
  function managedGoalFromState(state) {
5962
5991
  const extra = state.extra;
5963
5992
  const destination = asRecord(extra.destination);
@@ -5974,6 +6003,9 @@ function managedGoalFromState(state) {
5974
6003
  destination: { outcome: destination.outcome, evidence },
5975
6004
  agentResponsibilities,
5976
6005
  route,
6006
+ schedule: typeof extra.schedule === "string" ? extra.schedule : void 0,
6007
+ preferredRunTime: asPreferredRunTime(extra.preferredRunTime),
6008
+ loopTarget: asLoopTarget(extra.loopTarget),
5977
6009
  stage: typeof extra.stage === "string" ? extra.stage : void 0,
5978
6010
  facts,
5979
6011
  blockers
@@ -6572,6 +6604,41 @@ import * as path25 from "path";
6572
6604
  function isAgentResponsibilityCadenceGoal(goal, extra) {
6573
6605
  return extra.scheduleMode === "agentLoop" || extra.scheduler === "agentLoop" || goal.type === "standing" && goal.agentResponsibilities.length > 0;
6574
6606
  }
6607
+ function isGoalTargetLoop(goal) {
6608
+ return goal.type === "agentLoop" && goal.loopTarget?.type === "goal" && goal.loopTarget.id.trim().length > 0;
6609
+ }
6610
+ function planGoalTargetLoopSchedule(opts) {
6611
+ const now = opts.now ?? /* @__PURE__ */ new Date();
6612
+ const at = now.toISOString();
6613
+ const targetId = opts.goal.loopTarget?.type === "goal" ? opts.goal.loopTarget.id.trim() : "";
6614
+ if (!targetId) {
6615
+ const reason = "goal target loop missing target goal";
6616
+ return targetLoopDecision("blocked", reason, at);
6617
+ }
6618
+ const preferred = opts.goal.preferredRunTime;
6619
+ if (preferred) {
6620
+ const gate = preferredRunTimeGate(preferred, now, opts.previousScheduleState);
6621
+ if (!gate.ok) return targetLoopDecision("idle", gate.reason, at);
6622
+ }
6623
+ return {
6624
+ kind: "dispatch",
6625
+ reason: `dispatch goal ${targetId}`,
6626
+ dispatch: { action: "goal-manager", agentAction: "goal-manager", cliArgs: { goal: targetId } },
6627
+ scheduleState: {
6628
+ mode: "agentLoop",
6629
+ lastGoalTickAt: at,
6630
+ lastDecision: {
6631
+ kind: "dispatch",
6632
+ targetType: "goal",
6633
+ targetId,
6634
+ agentAction: "goal-manager",
6635
+ reason: preferred ? `preferred time ${preferred.time} ${preferred.timezone}` : "ready target loop tick",
6636
+ at
6637
+ },
6638
+ agentResponsibilities: {}
6639
+ }
6640
+ };
6641
+ }
6575
6642
  async function planGoalAgentResponsibilitySchedule(opts) {
6576
6643
  const jobsDir = opts.jobsDir ?? ".kody/agent-responsibilities";
6577
6644
  const jobsRoot = path25.join(opts.cwd, jobsDir);
@@ -6695,6 +6762,64 @@ function validIso(value) {
6695
6762
  function markAgentResponsibilitySelected(status, now) {
6696
6763
  return { ...status, lastFiredAt: now.toISOString() };
6697
6764
  }
6765
+ function targetLoopDecision(kind, reason, at) {
6766
+ return {
6767
+ kind,
6768
+ reason,
6769
+ scheduleState: {
6770
+ mode: "agentLoop",
6771
+ lastGoalTickAt: at,
6772
+ lastDecision: { kind, reason, at },
6773
+ agentResponsibilities: {}
6774
+ }
6775
+ };
6776
+ }
6777
+ function preferredRunTimeGate(preferred, now, previous) {
6778
+ const current = zonedTimeParts(now, preferred.timezone);
6779
+ if (!current) return { ok: false, reason: `invalid preferred timezone: ${preferred.timezone}` };
6780
+ const preferredMinute = preferredTimeToMinute(preferred.time);
6781
+ if (preferredMinute === null) return { ok: false, reason: `invalid preferred time: ${preferred.time}` };
6782
+ const currentMinute = current.hour * 60 + current.minute;
6783
+ if (currentMinute < preferredMinute) {
6784
+ return { ok: false, reason: `waiting preferred time ${preferred.time} ${preferred.timezone}` };
6785
+ }
6786
+ const lastDispatchAt = previous?.lastDecision.kind === "dispatch" ? previous.lastDecision.at : void 0;
6787
+ if (lastDispatchAt) {
6788
+ const last = zonedTimeParts(new Date(lastDispatchAt), preferred.timezone);
6789
+ if (last?.date === current.date) {
6790
+ return { ok: false, reason: `already dispatched today at preferred time ${preferred.time} ${preferred.timezone}` };
6791
+ }
6792
+ }
6793
+ return { ok: true };
6794
+ }
6795
+ function preferredTimeToMinute(value) {
6796
+ const match = value.match(/^([01]\d|2[0-3]):([0-5]\d)$/);
6797
+ if (!match) return null;
6798
+ return Number(match[1]) * 60 + Number(match[2]);
6799
+ }
6800
+ function zonedTimeParts(date, timezone) {
6801
+ try {
6802
+ const parts = new Intl.DateTimeFormat("en-CA", {
6803
+ timeZone: timezone,
6804
+ year: "numeric",
6805
+ month: "2-digit",
6806
+ day: "2-digit",
6807
+ hour: "2-digit",
6808
+ minute: "2-digit",
6809
+ hourCycle: "h23"
6810
+ }).formatToParts(date);
6811
+ const get = (type) => parts.find((part) => part.type === type)?.value;
6812
+ const year = get("year");
6813
+ const month = get("month");
6814
+ const day = get("day");
6815
+ const hour = get("hour");
6816
+ const minute = get("minute");
6817
+ if (!year || !month || !day || !hour || !minute) return null;
6818
+ return { date: `${year}-${month}-${day}`, hour: Number(hour), minute: Number(minute) };
6819
+ } catch {
6820
+ return null;
6821
+ }
6822
+ }
6698
6823
  var init_goalAgentResponsibilityScheduling = __esm({
6699
6824
  "src/scripts/goalAgentResponsibilityScheduling.ts"() {
6700
6825
  "use strict";
@@ -6806,6 +6931,23 @@ var init_advanceManagedGoal = __esm({
6806
6931
  ctx.output.reason = reason;
6807
6932
  return;
6808
6933
  }
6934
+ if (isGoalTargetLoop(managed)) {
6935
+ const previousScheduleState = goal.raw.extra.scheduleState && typeof goal.raw.extra.scheduleState === "object" ? goal.raw.extra.scheduleState : void 0;
6936
+ const decision2 = planGoalTargetLoopSchedule({ goal: managed, previousScheduleState });
6937
+ restoreGoalIdFact();
6938
+ goal.raw = writeManagedGoalToState({ ...goal.raw, state: goal.state }, managed);
6939
+ goal.raw.extra.scheduleState = decision2.scheduleState;
6940
+ ctx.data.managedGoalDecision = decision2;
6941
+ if (decision2.kind === "dispatch" && decision2.dispatch) {
6942
+ ctx.output.nextDispatch = {
6943
+ action: decision2.dispatch.action,
6944
+ agentAction: decision2.dispatch.agentAction,
6945
+ cliArgs: decision2.dispatch.cliArgs
6946
+ };
6947
+ }
6948
+ ctx.output.reason = decision2.reason;
6949
+ return;
6950
+ }
6809
6951
  if (isAgentResponsibilityCadenceGoal(managed, goal.raw.extra)) {
6810
6952
  const previousScheduleState = goal.raw.extra.scheduleState && typeof goal.raw.extra.scheduleState === "object" ? goal.raw.extra.scheduleState : void 0;
6811
6953
  const decision2 = await planGoalAgentResponsibilitySchedule({
@@ -17308,12 +17450,34 @@ function unpackAllSecrets(env = process.env) {
17308
17450
  }
17309
17451
  return count;
17310
17452
  }
17453
+ function recoverCheckoutToken(env = process.env, cwd = process.cwd()) {
17454
+ if (env.GITHUB_TOKEN?.trim()) return env.GITHUB_TOKEN.trim();
17455
+ let header = "";
17456
+ try {
17457
+ header = execFileSync25("git", ["config", "--local", "--get", "http.https://github.com/.extraheader"], {
17458
+ cwd,
17459
+ encoding: "utf-8",
17460
+ stdio: ["ignore", "pipe", "ignore"]
17461
+ }).trim();
17462
+ } catch {
17463
+ return void 0;
17464
+ }
17465
+ const match = /^AUTHORIZATION:\s+basic\s+(.+)$/i.exec(header);
17466
+ if (!match) return void 0;
17467
+ const decoded = Buffer.from(match[1], "base64").toString("utf-8");
17468
+ const token = decoded.includes(":") ? decoded.slice(decoded.indexOf(":") + 1).trim() : decoded.trim();
17469
+ if (!token) return void 0;
17470
+ env.GITHUB_TOKEN = token;
17471
+ process.stdout.write("\u2192 kody: GITHUB_TOKEN recovered from actions/checkout credentials\n");
17472
+ return token;
17473
+ }
17311
17474
  async function resolveAuthToken(env = process.env) {
17312
17475
  const creds = readAppCreds(env);
17313
17476
  if (creds) {
17314
17477
  try {
17315
17478
  const minted = await mintAppInstallationToken(creds);
17316
17479
  env.GH_TOKEN = minted;
17480
+ recoverCheckoutToken(env);
17317
17481
  process.stdout.write("\u2192 kody: GH_TOKEN minted from GitHub App (KODY_APP_ID/KODY_APP_PRIVATE_KEY)\n");
17318
17482
  return minted;
17319
17483
  } catch (err) {
@@ -17330,6 +17494,7 @@ async function resolveAuthToken(env = process.env) {
17330
17494
  const picked = sources.find(([, v]) => !!v);
17331
17495
  const token = picked?.[1];
17332
17496
  if (token && !env.GH_TOKEN) env.GH_TOKEN = token;
17497
+ recoverCheckoutToken(env);
17333
17498
  if (token) {
17334
17499
  process.stdout.write(`\u2192 kody: GH_TOKEN sourced from env.${picked[0]}
17335
17500
  `);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kody-ade/kody-engine",
3
- "version": "0.4.245",
3
+ "version": "0.4.247",
4
4
  "description": "kody — autonomous development engine. Single-session Claude Code agent behind a generic executor + declarative agentAction profiles.",
5
5
  "license": "MIT",
6
6
  "type": "module",