@kody-ade/kody-engine 0.4.245 → 0.4.246

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 +128 -1
  2. package/package.json +22 -23
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.246",
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",
@@ -5958,6 +5958,20 @@ function asRoute(value) {
5958
5958
  }
5959
5959
  return route;
5960
5960
  }
5961
+ function asPreferredRunTime(value) {
5962
+ const raw = asRecord(value);
5963
+ if (!raw) return void 0;
5964
+ if (typeof raw.time !== "string" || !/^([01]\d|2[0-3]):[0-5]\d$/.test(raw.time)) return void 0;
5965
+ if (typeof raw.timezone !== "string" || raw.timezone.trim().length === 0) return void 0;
5966
+ return { time: raw.time, timezone: raw.timezone };
5967
+ }
5968
+ function asLoopTarget(value) {
5969
+ const raw = asRecord(value);
5970
+ if (!raw) return void 0;
5971
+ if (raw.type !== "goal" && raw.type !== "agentResponsibility") return void 0;
5972
+ if (typeof raw.id !== "string" || raw.id.trim().length === 0) return void 0;
5973
+ return { type: raw.type, id: raw.id };
5974
+ }
5961
5975
  function managedGoalFromState(state) {
5962
5976
  const extra = state.extra;
5963
5977
  const destination = asRecord(extra.destination);
@@ -5974,6 +5988,9 @@ function managedGoalFromState(state) {
5974
5988
  destination: { outcome: destination.outcome, evidence },
5975
5989
  agentResponsibilities,
5976
5990
  route,
5991
+ schedule: typeof extra.schedule === "string" ? extra.schedule : void 0,
5992
+ preferredRunTime: asPreferredRunTime(extra.preferredRunTime),
5993
+ loopTarget: asLoopTarget(extra.loopTarget),
5977
5994
  stage: typeof extra.stage === "string" ? extra.stage : void 0,
5978
5995
  facts,
5979
5996
  blockers
@@ -6572,6 +6589,41 @@ import * as path25 from "path";
6572
6589
  function isAgentResponsibilityCadenceGoal(goal, extra) {
6573
6590
  return extra.scheduleMode === "agentLoop" || extra.scheduler === "agentLoop" || goal.type === "standing" && goal.agentResponsibilities.length > 0;
6574
6591
  }
6592
+ function isGoalTargetLoop(goal) {
6593
+ return goal.type === "agentLoop" && goal.loopTarget?.type === "goal" && goal.loopTarget.id.trim().length > 0;
6594
+ }
6595
+ function planGoalTargetLoopSchedule(opts) {
6596
+ const now = opts.now ?? /* @__PURE__ */ new Date();
6597
+ const at = now.toISOString();
6598
+ const targetId = opts.goal.loopTarget?.type === "goal" ? opts.goal.loopTarget.id.trim() : "";
6599
+ if (!targetId) {
6600
+ const reason = "goal target loop missing target goal";
6601
+ return targetLoopDecision("blocked", reason, at);
6602
+ }
6603
+ const preferred = opts.goal.preferredRunTime;
6604
+ if (preferred) {
6605
+ const gate = preferredRunTimeGate(preferred, now, opts.previousScheduleState);
6606
+ if (!gate.ok) return targetLoopDecision("idle", gate.reason, at);
6607
+ }
6608
+ return {
6609
+ kind: "dispatch",
6610
+ reason: `dispatch goal ${targetId}`,
6611
+ dispatch: { action: "goal-manager", agentAction: "goal-manager", cliArgs: { goal: targetId } },
6612
+ scheduleState: {
6613
+ mode: "agentLoop",
6614
+ lastGoalTickAt: at,
6615
+ lastDecision: {
6616
+ kind: "dispatch",
6617
+ targetType: "goal",
6618
+ targetId,
6619
+ agentAction: "goal-manager",
6620
+ reason: preferred ? `preferred time ${preferred.time} ${preferred.timezone}` : "ready target loop tick",
6621
+ at
6622
+ },
6623
+ agentResponsibilities: {}
6624
+ }
6625
+ };
6626
+ }
6575
6627
  async function planGoalAgentResponsibilitySchedule(opts) {
6576
6628
  const jobsDir = opts.jobsDir ?? ".kody/agent-responsibilities";
6577
6629
  const jobsRoot = path25.join(opts.cwd, jobsDir);
@@ -6695,6 +6747,64 @@ function validIso(value) {
6695
6747
  function markAgentResponsibilitySelected(status, now) {
6696
6748
  return { ...status, lastFiredAt: now.toISOString() };
6697
6749
  }
6750
+ function targetLoopDecision(kind, reason, at) {
6751
+ return {
6752
+ kind,
6753
+ reason,
6754
+ scheduleState: {
6755
+ mode: "agentLoop",
6756
+ lastGoalTickAt: at,
6757
+ lastDecision: { kind, reason, at },
6758
+ agentResponsibilities: {}
6759
+ }
6760
+ };
6761
+ }
6762
+ function preferredRunTimeGate(preferred, now, previous) {
6763
+ const current = zonedTimeParts(now, preferred.timezone);
6764
+ if (!current) return { ok: false, reason: `invalid preferred timezone: ${preferred.timezone}` };
6765
+ const preferredMinute = preferredTimeToMinute(preferred.time);
6766
+ if (preferredMinute === null) return { ok: false, reason: `invalid preferred time: ${preferred.time}` };
6767
+ const currentMinute = current.hour * 60 + current.minute;
6768
+ if (currentMinute < preferredMinute) {
6769
+ return { ok: false, reason: `waiting preferred time ${preferred.time} ${preferred.timezone}` };
6770
+ }
6771
+ const lastDispatchAt = previous?.lastDecision.kind === "dispatch" ? previous.lastDecision.at : void 0;
6772
+ if (lastDispatchAt) {
6773
+ const last = zonedTimeParts(new Date(lastDispatchAt), preferred.timezone);
6774
+ if (last?.date === current.date) {
6775
+ return { ok: false, reason: `already dispatched today at preferred time ${preferred.time} ${preferred.timezone}` };
6776
+ }
6777
+ }
6778
+ return { ok: true };
6779
+ }
6780
+ function preferredTimeToMinute(value) {
6781
+ const match = value.match(/^([01]\d|2[0-3]):([0-5]\d)$/);
6782
+ if (!match) return null;
6783
+ return Number(match[1]) * 60 + Number(match[2]);
6784
+ }
6785
+ function zonedTimeParts(date, timezone) {
6786
+ try {
6787
+ const parts = new Intl.DateTimeFormat("en-CA", {
6788
+ timeZone: timezone,
6789
+ year: "numeric",
6790
+ month: "2-digit",
6791
+ day: "2-digit",
6792
+ hour: "2-digit",
6793
+ minute: "2-digit",
6794
+ hourCycle: "h23"
6795
+ }).formatToParts(date);
6796
+ const get = (type) => parts.find((part) => part.type === type)?.value;
6797
+ const year = get("year");
6798
+ const month = get("month");
6799
+ const day = get("day");
6800
+ const hour = get("hour");
6801
+ const minute = get("minute");
6802
+ if (!year || !month || !day || !hour || !minute) return null;
6803
+ return { date: `${year}-${month}-${day}`, hour: Number(hour), minute: Number(minute) };
6804
+ } catch {
6805
+ return null;
6806
+ }
6807
+ }
6698
6808
  var init_goalAgentResponsibilityScheduling = __esm({
6699
6809
  "src/scripts/goalAgentResponsibilityScheduling.ts"() {
6700
6810
  "use strict";
@@ -6806,6 +6916,23 @@ var init_advanceManagedGoal = __esm({
6806
6916
  ctx.output.reason = reason;
6807
6917
  return;
6808
6918
  }
6919
+ if (isGoalTargetLoop(managed)) {
6920
+ const previousScheduleState = goal.raw.extra.scheduleState && typeof goal.raw.extra.scheduleState === "object" ? goal.raw.extra.scheduleState : void 0;
6921
+ const decision2 = planGoalTargetLoopSchedule({ goal: managed, previousScheduleState });
6922
+ restoreGoalIdFact();
6923
+ goal.raw = writeManagedGoalToState({ ...goal.raw, state: goal.state }, managed);
6924
+ goal.raw.extra.scheduleState = decision2.scheduleState;
6925
+ ctx.data.managedGoalDecision = decision2;
6926
+ if (decision2.kind === "dispatch" && decision2.dispatch) {
6927
+ ctx.output.nextDispatch = {
6928
+ action: decision2.dispatch.action,
6929
+ agentAction: decision2.dispatch.agentAction,
6930
+ cliArgs: decision2.dispatch.cliArgs
6931
+ };
6932
+ }
6933
+ ctx.output.reason = decision2.reason;
6934
+ return;
6935
+ }
6809
6936
  if (isAgentResponsibilityCadenceGoal(managed, goal.raw.extra)) {
6810
6937
  const previousScheduleState = goal.raw.extra.scheduleState && typeof goal.raw.extra.scheduleState === "object" ? goal.raw.extra.scheduleState : void 0;
6811
6938
  const decision2 = await planGoalAgentResponsibilitySchedule({
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.246",
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",
@@ -12,26 +12,6 @@
12
12
  "templates",
13
13
  "kody.config.schema.json"
14
14
  ],
15
- "scripts": {
16
- "kody:run": "tsx bin/kody.ts",
17
- "serve": "tsx bin/kody.ts serve",
18
- "serve:vscode": "tsx bin/kody.ts serve vscode",
19
- "serve:claude": "tsx bin/kody.ts serve claude",
20
- "build": "tsup && node scripts/copy-assets.cjs",
21
- "check:modularity": "tsx scripts/check-script-modularity.ts",
22
- "pretest": "pnpm check:modularity",
23
- "test": "vitest run tests/unit tests/int --coverage",
24
- "posttest": "tsx scripts/check-coverage-floor.ts",
25
- "test:smoke": "vitest run tests/smoke --no-coverage",
26
- "test:e2e": "vitest run tests/e2e --no-coverage",
27
- "test:all": "vitest run tests --no-coverage",
28
- "typecheck": "tsc --noEmit",
29
- "lint": "biome check",
30
- "lint:fix": "biome check --write",
31
- "format": "biome format --write",
32
- "brain:publish": "docker buildx build --platform linux/amd64 -f runner/Dockerfile.brain -t ghcr.io/${KODY_BRAIN_GHCR_OWNER:-aharonyaircohen}/kody-brain:latest --push runner",
33
- "prepublishOnly": "pnpm typecheck && vitest run tests/unit tests/int --no-coverage && pnpm build"
34
- },
35
15
  "dependencies": {
36
16
  "@actions/cache": "^6.0.0",
37
17
  "@anthropic-ai/claude-agent-sdk": "0.2.119",
@@ -55,5 +35,24 @@
55
35
  "url": "git+https://github.com/aharonyaircohen/kody-engine.git"
56
36
  },
57
37
  "homepage": "https://github.com/aharonyaircohen/kody-engine",
58
- "bugs": "https://github.com/aharonyaircohen/kody-engine/issues"
59
- }
38
+ "bugs": "https://github.com/aharonyaircohen/kody-engine/issues",
39
+ "scripts": {
40
+ "kody:run": "tsx bin/kody.ts",
41
+ "serve": "tsx bin/kody.ts serve",
42
+ "serve:vscode": "tsx bin/kody.ts serve vscode",
43
+ "serve:claude": "tsx bin/kody.ts serve claude",
44
+ "build": "tsup && node scripts/copy-assets.cjs",
45
+ "check:modularity": "tsx scripts/check-script-modularity.ts",
46
+ "pretest": "pnpm check:modularity",
47
+ "test": "vitest run tests/unit tests/int --coverage",
48
+ "posttest": "tsx scripts/check-coverage-floor.ts",
49
+ "test:smoke": "vitest run tests/smoke --no-coverage",
50
+ "test:e2e": "vitest run tests/e2e --no-coverage",
51
+ "test:all": "vitest run tests --no-coverage",
52
+ "typecheck": "tsc --noEmit",
53
+ "lint": "biome check",
54
+ "lint:fix": "biome check --write",
55
+ "format": "biome format --write",
56
+ "brain:publish": "docker buildx build --platform linux/amd64 -f runner/Dockerfile.brain -t ghcr.io/${KODY_BRAIN_GHCR_OWNER:-aharonyaircohen}/kody-brain:latest --push runner"
57
+ }
58
+ }