@kody-ade/kody-engine 0.4.285 → 0.4.286

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 +37 -10
  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.285",
18
+ version: "0.4.286",
19
19
  description: "kody \u2014 autonomous development engine. Single-session Claude Code agent behind a generic executor + declarative executable profiles.",
20
20
  license: "MIT",
21
21
  type: "module",
@@ -7270,12 +7270,10 @@ var init_stateStore = __esm({
7270
7270
  // src/goal/targetLoopResolution.ts
7271
7271
  import * as fs26 from "fs";
7272
7272
  import * as path24 from "path";
7273
- function resolveGoalLoopTarget(config, cwd, loopGoalId, loopGoal, now) {
7273
+ function resolveActiveGoalLoopTarget(config, cwd, loopGoalId, loopGoal) {
7274
7274
  const targetId = loopGoal.loopTarget?.id.trim() ?? "";
7275
7275
  assertSafeGoalId(targetId, "loop target");
7276
- if (!hasExplicitStateRepo(config)) {
7277
- return { targetId, templateId: targetId, reason: "literal target; state repo not configured" };
7278
- }
7276
+ if (!hasExplicitStateRepo(config)) return null;
7279
7277
  const activeInstance = findActiveTargetInstance(config, cwd, loopGoalId, targetId);
7280
7278
  if (activeInstance) {
7281
7279
  return {
@@ -7288,6 +7286,17 @@ function resolveGoalLoopTarget(config, cwd, loopGoalId, loopGoal, now) {
7288
7286
  if (directTarget?.state === "active") {
7289
7287
  return { targetId, templateId: targetId, reason: "active target goal" };
7290
7288
  }
7289
+ return null;
7290
+ }
7291
+ function resolveGoalLoopTarget(config, cwd, loopGoalId, loopGoal, now) {
7292
+ const targetId = loopGoal.loopTarget?.id.trim() ?? "";
7293
+ assertSafeGoalId(targetId, "loop target");
7294
+ if (!hasExplicitStateRepo(config)) {
7295
+ return { targetId, templateId: targetId, reason: "literal target; state repo not configured" };
7296
+ }
7297
+ const activeTarget = resolveActiveGoalLoopTarget(config, cwd, loopGoalId, loopGoal);
7298
+ if (activeTarget) return activeTarget;
7299
+ const directTarget = fetchGoalState(config, targetId, cwd);
7291
7300
  const template = loadGoalTemplate(cwd, targetId);
7292
7301
  if (!template) {
7293
7302
  if (directTarget) {
@@ -7961,7 +7970,9 @@ function planTargetLoopSchedule(opts) {
7961
7970
  }
7962
7971
  const preferred = opts.goal.preferredRunTime;
7963
7972
  if (preferred) {
7964
- const gate = preferredRunTimeGate(preferred, now, opts.previousScheduleState);
7973
+ const gate = preferredRunTimeGate(preferred, now, opts.previousScheduleState, {
7974
+ allowRepeatAfterCompletedTarget: opts.allowRepeatAfterCompletedTarget === true
7975
+ });
7965
7976
  if (!gate.ok) return targetLoopDecision("idle", gate.reason, at);
7966
7977
  }
7967
7978
  const dispatchTargetId = target.type === "goal" && opts.resolvedGoalTargetId?.trim() ? opts.resolvedGoalTargetId.trim() : targetId;
@@ -8123,7 +8134,7 @@ function targetLoopDecision(kind, reason, at) {
8123
8134
  }
8124
8135
  };
8125
8136
  }
8126
- function preferredRunTimeGate(preferred, now, previous) {
8137
+ function preferredRunTimeGate(preferred, now, previous, opts) {
8127
8138
  const current = zonedTimeParts(now, preferred.timezone);
8128
8139
  if (!current) return { ok: false, reason: `invalid preferred timezone: ${preferred.timezone}` };
8129
8140
  const preferredMinute = preferredTimeToMinute(preferred.time);
@@ -8135,7 +8146,7 @@ function preferredRunTimeGate(preferred, now, previous) {
8135
8146
  const lastDispatchAt = previous?.lastDecision.kind === "dispatch" ? previous.lastDecision.at : void 0;
8136
8147
  if (lastDispatchAt) {
8137
8148
  const last = zonedTimeParts(new Date(lastDispatchAt), preferred.timezone);
8138
- if (last?.date === current.date) {
8149
+ if (last?.date === current.date && opts?.allowRepeatAfterCompletedTarget !== true) {
8139
8150
  return { ok: false, reason: `already dispatched today at preferred time ${preferred.time} ${preferred.timezone}` };
8140
8151
  }
8141
8152
  }
@@ -8269,6 +8280,14 @@ function readSimpleGoalTaskSummary(goalId, cwd) {
8269
8280
  const open = issues.filter((issue) => String(issue.state ?? "").toLowerCase() === "open").length;
8270
8281
  return { total, open };
8271
8282
  }
8283
+ function previousDispatchWasTargetInstance(managed, previousScheduleState) {
8284
+ const targetId = managed.loopTarget?.id.trim();
8285
+ const previous = previousScheduleState?.lastDecision;
8286
+ if (!targetId || previous?.kind !== "dispatch" || !("targetType" in previous) || previous.targetType !== "goal") {
8287
+ return false;
8288
+ }
8289
+ return previous.targetId === targetId || previous.targetId.startsWith(`${targetId}-`);
8290
+ }
8272
8291
  function ensureIssueFactIfNeeded(goal, goalId, cwd) {
8273
8292
  if (!routeNeedsIssueFact(goal)) return;
8274
8293
  const existing = normalizeIssueNumber(goal.facts.issue);
@@ -8401,7 +8420,14 @@ var init_advanceManagedGoal = __esm({
8401
8420
  const beforeSnapshot = goalRunLogSnapshot(goal.id, goal.state, managed);
8402
8421
  const previousScheduleState = goal.raw.extra.scheduleState && typeof goal.raw.extra.scheduleState === "object" ? goal.raw.extra.scheduleState : void 0;
8403
8422
  const now = goalLoopNow();
8404
- let decision2 = planTargetLoopSchedule({ goal: managed, previousScheduleState, now });
8423
+ const activeTarget = isGoalTargetLoop(managed) ? resolveActiveGoalLoopTarget(ctx.config, ctx.cwd, goal.id, managed) : null;
8424
+ const allowRepeatAfterCompletedTarget = isGoalTargetLoop(managed) && !activeTarget && previousDispatchWasTargetInstance(managed, previousScheduleState);
8425
+ let decision2 = planTargetLoopSchedule({
8426
+ goal: managed,
8427
+ previousScheduleState,
8428
+ now,
8429
+ allowRepeatAfterCompletedTarget
8430
+ });
8405
8431
  let targetResolution;
8406
8432
  if (decision2.kind === "dispatch" && decision2.dispatch && isGoalTargetLoop(managed)) {
8407
8433
  targetResolution = resolveGoalLoopTarget(ctx.config, ctx.cwd, goal.id, managed, now);
@@ -8409,7 +8435,8 @@ var init_advanceManagedGoal = __esm({
8409
8435
  goal: managed,
8410
8436
  previousScheduleState,
8411
8437
  now,
8412
- resolvedGoalTargetId: targetResolution.targetId
8438
+ resolvedGoalTargetId: targetResolution.targetId,
8439
+ allowRepeatAfterCompletedTarget
8413
8440
  });
8414
8441
  }
8415
8442
  restoreGoalIdFact();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kody-ade/kody-engine",
3
- "version": "0.4.285",
3
+ "version": "0.4.286",
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",