@kody-ade/kody-engine 0.4.244 → 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.
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.244",
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",
@@ -194,8 +194,8 @@ function isRunningAsBot() {
194
194
  }
195
195
  function postIssueComment(issueNumber, body, cwd) {
196
196
  if (isRunningAsBot()) {
197
- const slug = detectBotDispatchShape(body);
198
- if (slug) throw new BotDispatchCommentError(slug);
197
+ const slug2 = detectBotDispatchShape(body);
198
+ if (slug2) throw new BotDispatchCommentError(slug2);
199
199
  }
200
200
  try {
201
201
  gh(["issue", "comment", String(issueNumber), "--body-file", "-"], { input: stripKodyMentions(body), cwd });
@@ -295,8 +295,8 @@ function getPrLatestReviewBody(prNumber, cwd) {
295
295
  }
296
296
  function postPrReviewComment(prNumber, body, cwd) {
297
297
  if (isRunningAsBot()) {
298
- const slug = detectBotDispatchShape(body);
299
- if (slug) throw new BotDispatchCommentError(slug);
298
+ const slug2 = detectBotDispatchShape(body);
299
+ if (slug2) throw new BotDispatchCommentError(slug2);
300
300
  }
301
301
  try {
302
302
  gh(["pr", "comment", String(prNumber), "--body-file", "-"], { input: stripKodyMentions(body), cwd });
@@ -313,9 +313,9 @@ var init_issue = __esm({
313
313
  "use strict";
314
314
  API_TIMEOUT_MS = 3e4;
315
315
  BotDispatchCommentError = class extends Error {
316
- constructor(slug) {
316
+ constructor(slug2) {
317
317
  super(
318
- `bot self-dispatch via @kody comments is banned. Refusing to post "@kody ${slug} \u2026" \u2014 use runAgentActionChain (same-run) or dispatchAgentAction (cross-run) instead. See docs/agent-responsibility-dispatch.md for the contract.`
318
+ `bot self-dispatch via @kody comments is banned. Refusing to post "@kody ${slug2} \u2026" \u2014 use runAgentActionChain (same-run) or dispatchAgentAction (cross-run) instead. See docs/agent-responsibility-dispatch.md for the contract.`
319
319
  );
320
320
  this.name = "BotDispatchCommentError";
321
321
  }
@@ -332,8 +332,8 @@ function is404(err) {
332
332
  const msg = err instanceof Error ? err.message : String(err);
333
333
  return /HTTP 404/i.test(msg) || /Not Found/i.test(msg);
334
334
  }
335
- function parseStateRepoSlug(slug, field = "stateRepo") {
336
- const value = slug.trim();
335
+ function parseStateRepoSlug(slug2, field = "stateRepo") {
336
+ const value = slug2.trim();
337
337
  let repoPath = value;
338
338
  if (/^https?:\/\//i.test(value)) {
339
339
  let parsed;
@@ -460,6 +460,18 @@ function appendStateLine(config, cwd, filePath, line, message) {
460
460
  `}`;
461
461
  writeStateText(config, cwd, filePath, next, message, prior?.sha);
462
462
  }
463
+ function listStateDirectory(config, cwd, dirPath) {
464
+ const targetPath = stateRepoPath(config, dirPath);
465
+ let raw = "";
466
+ try {
467
+ raw = gh(["api", apiPath(config, targetPath)], { cwd });
468
+ } catch (err) {
469
+ if (is404(err)) return [];
470
+ throw err;
471
+ }
472
+ const parsed = JSON.parse(raw);
473
+ return Array.isArray(parsed) ? parsed : [];
474
+ }
463
475
  var init_stateRepo = __esm({
464
476
  "src/stateRepo.ts"() {
465
477
  "use strict";
@@ -619,11 +631,11 @@ function parseGoalActivations(raw) {
619
631
  const seen = /* @__PURE__ */ new Set();
620
632
  for (const value of raw) {
621
633
  if (typeof value === "string") {
622
- const slug = parseSlug(value, "company.activeGoals");
623
- if (!slug) continue;
624
- if (!seen.has(slug)) {
625
- seen.add(slug);
626
- out.push(slug);
634
+ const slug2 = parseSlug(value, "company.activeGoals");
635
+ if (!slug2) continue;
636
+ if (!seen.has(slug2)) {
637
+ seen.add(slug2);
638
+ out.push(slug2);
627
639
  }
628
640
  continue;
629
641
  }
@@ -658,20 +670,20 @@ function parseSlugArray(raw, field) {
658
670
  if (!Array.isArray(raw)) throw new Error(`kody.config.json: ${field} must be an array of strings`);
659
671
  const out = [];
660
672
  for (const value of raw) {
661
- const slug = parseSlug(value, field);
662
- if (!slug) continue;
663
- out.push(slug);
673
+ const slug2 = parseSlug(value, field);
674
+ if (!slug2) continue;
675
+ out.push(slug2);
664
676
  }
665
677
  return [...new Set(out)];
666
678
  }
667
679
  function parseSlug(value, field) {
668
680
  if (typeof value !== "string") throw new Error(`kody.config.json: ${field} entries must be strings`);
669
- const slug = value.trim();
670
- if (!slug) return "";
671
- if (!/^[A-Za-z0-9][A-Za-z0-9._-]*$/.test(slug)) {
681
+ const slug2 = value.trim();
682
+ if (!slug2) return "";
683
+ if (!/^[A-Za-z0-9][A-Za-z0-9._-]*$/.test(slug2)) {
672
684
  throw new Error(`kody.config.json: ${field} contains invalid slug "${value}"`);
673
685
  }
674
- return slug;
686
+ return slug2;
675
687
  }
676
688
  function recordValue(raw) {
677
689
  return raw && typeof raw === "object" && !Array.isArray(raw) ? raw : void 0;
@@ -2303,56 +2315,6 @@ var init_agent = __esm({
2303
2315
  }
2304
2316
  });
2305
2317
 
2306
- // src/scripts/scheduleEvery.ts
2307
- function isScheduleEvery(value) {
2308
- return typeof value === "string" && SCHEDULE_EVERY_VALUES.includes(value);
2309
- }
2310
- function scheduleEveryToMs(every) {
2311
- const MIN = 60 * 1e3;
2312
- const HOUR = 60 * MIN;
2313
- const DAY = 24 * HOUR;
2314
- switch (every) {
2315
- case "15m":
2316
- return 15 * MIN;
2317
- case "30m":
2318
- return 30 * MIN;
2319
- case "1h":
2320
- return HOUR;
2321
- case "2h":
2322
- return 2 * HOUR;
2323
- case "6h":
2324
- return 6 * HOUR;
2325
- case "12h":
2326
- return 12 * HOUR;
2327
- case "1d":
2328
- return DAY;
2329
- case "3d":
2330
- return 3 * DAY;
2331
- case "7d":
2332
- return 7 * DAY;
2333
- case "manual":
2334
- return Number.POSITIVE_INFINITY;
2335
- }
2336
- }
2337
- var SCHEDULE_EVERY_VALUES;
2338
- var init_scheduleEvery = __esm({
2339
- "src/scripts/scheduleEvery.ts"() {
2340
- "use strict";
2341
- SCHEDULE_EVERY_VALUES = [
2342
- "15m",
2343
- "30m",
2344
- "1h",
2345
- "2h",
2346
- "6h",
2347
- "12h",
2348
- "1d",
2349
- "3d",
2350
- "7d",
2351
- "manual"
2352
- ];
2353
- }
2354
- });
2355
-
2356
2318
  // src/agent-responsibilityFolders.ts
2357
2319
  import * as fs6 from "fs";
2358
2320
  import * as path8 from "path";
@@ -2369,8 +2331,8 @@ function listAgentResponsibilityFolderSlugs(absDir) {
2369
2331
  function isAgentResponsibilityFolder(dir) {
2370
2332
  return fs6.existsSync(path8.join(dir, AGENT_RESPONSIBILITY_PROFILE_FILE)) && fs6.existsSync(path8.join(dir, AGENT_RESPONSIBILITY_BODY_FILE));
2371
2333
  }
2372
- function readAgentResponsibilityFolder(root, slug) {
2373
- const dir = path8.join(root, slug);
2334
+ function readAgentResponsibilityFolder(root, slug2) {
2335
+ const dir = path8.join(root, slug2);
2374
2336
  const profilePath = path8.join(dir, AGENT_RESPONSIBILITY_PROFILE_FILE);
2375
2337
  const bodyPath = path8.join(dir, AGENT_RESPONSIBILITY_BODY_FILE);
2376
2338
  if (!fs6.existsSync(profilePath) || !fs6.statSync(profilePath).isFile()) return null;
@@ -2378,9 +2340,9 @@ function readAgentResponsibilityFolder(root, slug) {
2378
2340
  try {
2379
2341
  const rawProfile = JSON.parse(fs6.readFileSync(profilePath, "utf-8"));
2380
2342
  const rawBody = fs6.readFileSync(bodyPath, "utf-8");
2381
- const { title, body } = parseAgentResponsibilityBody(rawBody, slug);
2343
+ const { title, body } = parseAgentResponsibilityBody(rawBody, slug2);
2382
2344
  return {
2383
- slug,
2345
+ slug: slug2,
2384
2346
  dir,
2385
2347
  profilePath,
2386
2348
  bodyPath,
@@ -2399,7 +2361,6 @@ function parseAgentResponsibilityConfig(raw) {
2399
2361
  return {
2400
2362
  action: stringField(raw.action),
2401
2363
  agentAction: stringField(raw.agentAction),
2402
- every: isScheduleEvery(raw.every) ? raw.every : void 0,
2403
2364
  tickScript: stringField(raw.tickScript),
2404
2365
  disabled: typeof raw.disabled === "boolean" ? raw.disabled : void 0,
2405
2366
  agent: stringField(raw.agent),
@@ -2413,11 +2374,11 @@ function parseAgentResponsibilityConfig(raw) {
2413
2374
  writesTo: stringList(raw.writesTo ?? raw.writes_to)
2414
2375
  };
2415
2376
  }
2416
- function parseAgentResponsibilityBody(raw, slug) {
2377
+ function parseAgentResponsibilityBody(raw, slug2) {
2417
2378
  const trimmed = raw.trim();
2418
2379
  const firstLine2 = trimmed.split("\n", 1)[0] ?? "";
2419
2380
  const h1 = /^#\s+(.+?)\s*$/.exec(firstLine2);
2420
- const title = h1 ? h1[1].trim() : humanizeSlug(slug);
2381
+ const title = h1 ? h1[1].trim() : humanizeSlug(slug2);
2421
2382
  const body = stripLeadingH1(raw);
2422
2383
  return { title, body };
2423
2384
  }
@@ -2431,8 +2392,8 @@ function stripLeadingH1(raw) {
2431
2392
  }
2432
2393
  return lines.slice(i).join("\n");
2433
2394
  }
2434
- function humanizeSlug(slug) {
2435
- return slug.split(/[-_]+/).filter(Boolean).map((part) => part[0].toUpperCase() + part.slice(1)).join(" ");
2395
+ function humanizeSlug(slug2) {
2396
+ return slug2.split(/[-_]+/).filter(Boolean).map((part) => part[0].toUpperCase() + part.slice(1)).join(" ");
2436
2397
  }
2437
2398
  function stringField(value) {
2438
2399
  return typeof value === "string" && value.trim().length > 0 ? value.trim() : void 0;
@@ -2454,7 +2415,6 @@ var AGENT_RESPONSIBILITY_PROFILE_FILE, AGENT_RESPONSIBILITY_BODY_FILE;
2454
2415
  var init_agent_responsibilityFolders = __esm({
2455
2416
  "src/agent-responsibilityFolders.ts"() {
2456
2417
  "use strict";
2457
- init_scheduleEvery();
2458
2418
  AGENT_RESPONSIBILITY_PROFILE_FILE = "profile.json";
2459
2419
  AGENT_RESPONSIBILITY_BODY_FILE = "agent-responsibility.md";
2460
2420
  }
@@ -2680,10 +2640,10 @@ function resolveAgentResponsibilityAction(action, projectAgentResponsibilitiesRo
2680
2640
  function hasAgentResponsibilityAction(action, projectAgentResponsibilitiesRoot = getProjectAgentResponsibilitiesRoot()) {
2681
2641
  return resolveAgentResponsibilityAction(action, projectAgentResponsibilitiesRoot) !== null;
2682
2642
  }
2683
- function resolveAgentResponsibilityFolder(slug, projectAgentResponsibilitiesRoot = getProjectAgentResponsibilitiesRoot()) {
2684
- if (!isSafeName(slug)) return null;
2643
+ function resolveAgentResponsibilityFolder(slug2, projectAgentResponsibilitiesRoot = getProjectAgentResponsibilitiesRoot()) {
2644
+ if (!isSafeName(slug2)) return null;
2685
2645
  for (const root of getAgentResponsibilityRoots(projectAgentResponsibilitiesRoot)) {
2686
- const agentResponsibility = readAgentResponsibilityFolder(root, slug);
2646
+ const agentResponsibility = readAgentResponsibilityFolder(root, slug2);
2687
2647
  if (agentResponsibility) return agentResponsibility;
2688
2648
  }
2689
2649
  return null;
@@ -2742,15 +2702,15 @@ function listAgentActionResponsibilityActions(root, source) {
2742
2702
  function listFolderAgentResponsibilityActions(root, source) {
2743
2703
  if (!fs8.existsSync(root) || !fs8.statSync(root).isDirectory()) return [];
2744
2704
  const out = [];
2745
- for (const slug of listAgentResponsibilityFolderSlugs(root)) {
2746
- if (!isSafeName(slug)) continue;
2747
- const agentResponsibility = readAgentResponsibilityFolder(root, slug);
2705
+ for (const slug2 of listAgentResponsibilityFolderSlugs(root)) {
2706
+ if (!isSafeName(slug2)) continue;
2707
+ const agentResponsibility = readAgentResponsibilityFolder(root, slug2);
2748
2708
  if (!agentResponsibility) continue;
2749
- const action = agentResponsibility.config.action ?? slug;
2709
+ const action = agentResponsibility.config.action ?? slug2;
2750
2710
  const { agentAction, cliArgs } = resolveAgentResponsibilityExecution(agentResponsibility);
2751
2711
  out.push({
2752
2712
  action,
2753
- agentResponsibility: slug,
2713
+ agentResponsibility: slug2,
2754
2714
  agentAction,
2755
2715
  cliArgs,
2756
2716
  source,
@@ -2765,15 +2725,15 @@ function listFolderAgentResponsibilityActions(root, source) {
2765
2725
  function listBuiltinAgentResponsibilityActions(root = getBuiltinAgentResponsibilitiesRoot()) {
2766
2726
  if (!fs8.existsSync(root) || !fs8.statSync(root).isDirectory()) return [];
2767
2727
  const out = [];
2768
- for (const slug of listAgentResponsibilityFolderSlugs(root)) {
2769
- if (!isSafeName(slug)) continue;
2770
- const agentResponsibility = readAgentResponsibilityFolder(root, slug);
2728
+ for (const slug2 of listAgentResponsibilityFolderSlugs(root)) {
2729
+ if (!isSafeName(slug2)) continue;
2730
+ const agentResponsibility = readAgentResponsibilityFolder(root, slug2);
2771
2731
  if (!agentResponsibility) continue;
2772
- const action = agentResponsibility.config.action ?? slug;
2773
- const agentAction = agentResponsibility.config.agentAction ?? slug;
2732
+ const action = agentResponsibility.config.action ?? slug2;
2733
+ const agentAction = agentResponsibility.config.agentAction ?? slug2;
2774
2734
  out.push({
2775
2735
  action,
2776
- agentResponsibility: slug,
2736
+ agentResponsibility: slug2,
2777
2737
  agentAction,
2778
2738
  cliArgs: {},
2779
2739
  source: "builtin",
@@ -3199,8 +3159,8 @@ function stripFrontmatter(raw) {
3199
3159
  const match = /^---\n[\s\S]*?\n---\n?([\s\S]*)$/.exec(raw);
3200
3160
  return (match ? match[1] : raw).trim();
3201
3161
  }
3202
- function loadAgentIdentity(cwd, slug, agentsDir = DEFAULT_AGENT_DIR) {
3203
- const trimmed = slug.trim();
3162
+ function loadAgentIdentity(cwd, slug2, agentsDir = DEFAULT_AGENT_DIR) {
3163
+ const trimmed = slug2.trim();
3204
3164
  if (!trimmed) throw new Error("loadAgentIdentity: empty agent slug");
3205
3165
  const agentPath = resolveAgentFile(cwd, trimmed, agentsDir);
3206
3166
  if (fs16.existsSync(agentPath)) {
@@ -3214,21 +3174,21 @@ function loadAgentIdentity(cwd, slug, agentsDir = DEFAULT_AGENT_DIR) {
3214
3174
  if (builtin) return builtin;
3215
3175
  throw new Error(`loadAgentIdentity: agent '${trimmed}' declared but ${agentPath} does not exist`);
3216
3176
  }
3217
- function resolveAgentFile(cwd, slug, agentsDir = DEFAULT_AGENT_DIR) {
3218
- const localPath = path16.join(cwd, agentsDir, `${slug}.md`);
3177
+ function resolveAgentFile(cwd, slug2, agentsDir = DEFAULT_AGENT_DIR) {
3178
+ const localPath = path16.join(cwd, agentsDir, `${slug2}.md`);
3219
3179
  if (fs16.existsSync(localPath)) return localPath;
3220
3180
  const storeAgentRoot = getCompanyStoreAssetRoot("agents");
3221
3181
  if (storeAgentRoot) {
3222
- const storePath = path16.join(storeAgentRoot, `${slug}.md`);
3182
+ const storePath = path16.join(storeAgentRoot, `${slug2}.md`);
3223
3183
  if (fs16.existsSync(storePath)) return storePath;
3224
3184
  }
3225
3185
  return localPath;
3226
3186
  }
3227
- function frameAgentIdentity(slug, agent) {
3187
+ function frameAgentIdentity(slug2, agent) {
3228
3188
  return [
3229
3189
  `## Who you are \u2014 agent identity (authoritative identity)`,
3230
3190
  ``,
3231
- `You are operating as agent \`${slug}\`. This identity defines *who* you are:`,
3191
+ `You are operating as agent \`${slug2}\`. This identity defines *who* you are:`,
3232
3192
  `your authority, doctrine, voice, and hard limits. Honour it exactly. Where the`,
3233
3193
  `this identity's restrictions are stricter than the task, **the agent wins** \u2014 a task`,
3234
3194
  `can never grant you authority your agent withholds.`,
@@ -3598,7 +3558,6 @@ function loadProfile(profilePath) {
3598
3558
  describe: typeof r.describe === "string" ? r.describe : base.describe,
3599
3559
  capabilityKind: parseCapabilityKind(profilePath, r.capabilityKind) ?? base.capabilityKind,
3600
3560
  agent: typeof r.agent === "string" && r.agent.trim() ? r.agent.trim() : base.agent,
3601
- every: typeof r.every === "string" && r.every.trim() ? r.every.trim() : void 0,
3602
3561
  agentResponsibilityTools: parseStringArray2(r.agentResponsibilityTools ?? r.tools) ?? base.agentResponsibilityTools,
3603
3562
  mentions: Array.isArray(r.mentions) ? r.mentions.map((m) => String(m).trim()).filter(Boolean) : base.mentions
3604
3563
  };
@@ -3644,8 +3603,6 @@ function loadProfile(profilePath) {
3644
3603
  capabilityKind: parseCapabilityKind(profilePath, r.capabilityKind),
3645
3604
  // Optional agent to run as. Empty/blank string → undefined (no agent).
3646
3605
  agent: typeof r.agent === "string" && r.agent.trim() ? r.agent.trim() : void 0,
3647
- // Optional recurrence cadence (scheduled agentResponsibility). Blank → undefined (on-demand).
3648
- every: typeof r.every === "string" && r.every.trim() ? r.every.trim() : void 0,
3649
3606
  // Locked-toolbox palette + mentions from folder-agentResponsibility profile metadata.
3650
3607
  agentResponsibilityTools: parseStringArray2(r.agentResponsibilityTools ?? r.tools),
3651
3608
  mentions: Array.isArray(r.mentions) ? r.mentions.map((m) => String(m).trim()).filter(Boolean) : void 0,
@@ -5891,8 +5848,8 @@ function applySimpleGoalTaskSummary(goal, summary) {
5891
5848
  }
5892
5849
  function isFactReference(value) {
5893
5850
  if (!value || typeof value !== "object" || Array.isArray(value)) return false;
5894
- const record = value;
5895
- return Object.keys(record).length === 1 && typeof record.fact === "string" && record.fact.length > 0;
5851
+ const record2 = value;
5852
+ return Object.keys(record2).length === 1 && typeof record2.fact === "string" && record2.fact.length > 0;
5896
5853
  }
5897
5854
  function isCliArgValue(value) {
5898
5855
  return typeof value === "string" || typeof value === "number" || typeof value === "boolean";
@@ -5967,7 +5924,8 @@ function planManagedGoalTick(goal) {
5967
5924
  stage: step.stage,
5968
5925
  agentResponsibility: step.agentResponsibility,
5969
5926
  agentAction: step.agentAction,
5970
- cliArgs: resolved.cliArgs
5927
+ cliArgs: resolved.cliArgs,
5928
+ ...step.saveReport === true ? { saveReport: true } : {}
5971
5929
  };
5972
5930
  }
5973
5931
  function asStringArray(value) {
@@ -5994,11 +5952,26 @@ function asRoute(value) {
5994
5952
  stage: raw.stage,
5995
5953
  agentResponsibility: raw.agentResponsibility,
5996
5954
  agentAction: typeof raw.agentAction === "string" ? raw.agentAction : void 0,
5997
- args: args ?? void 0
5955
+ args: args ?? void 0,
5956
+ saveReport: raw.saveReport === true
5998
5957
  });
5999
5958
  }
6000
5959
  return route;
6001
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
+ }
6002
5975
  function managedGoalFromState(state) {
6003
5976
  const extra = state.extra;
6004
5977
  const destination = asRecord(extra.destination);
@@ -6015,6 +5988,9 @@ function managedGoalFromState(state) {
6015
5988
  destination: { outcome: destination.outcome, evidence },
6016
5989
  agentResponsibilities,
6017
5990
  route,
5991
+ schedule: typeof extra.schedule === "string" ? extra.schedule : void 0,
5992
+ preferredRunTime: asPreferredRunTime(extra.preferredRunTime),
5993
+ loopTarget: asLoopTarget(extra.loopTarget),
6018
5994
  stage: typeof extra.stage === "string" ? extra.stage : void 0,
6019
5995
  facts,
6020
5996
  blockers
@@ -6088,10 +6064,10 @@ var init_state2 = __esm({
6088
6064
  "use strict";
6089
6065
  VALID_STATES = /* @__PURE__ */ new Set(["active", "abandoned", "closed", "done"]);
6090
6066
  GoalStateError = class extends Error {
6091
- constructor(path47, message) {
6092
- super(`Invalid goal state at ${path47}:
6067
+ constructor(path46, message) {
6068
+ super(`Invalid goal state at ${path46}:
6093
6069
  ${message}`);
6094
- this.path = path47;
6070
+ this.path = path46;
6095
6071
  this.name = "GoalStateError";
6096
6072
  }
6097
6073
  path;
@@ -6336,8 +6312,8 @@ function isStateUnchanged(prev, next) {
6336
6312
  if (prev.done !== next.done) return false;
6337
6313
  return JSON.stringify(prev.data) === JSON.stringify(next.data);
6338
6314
  }
6339
- function stateFilePath(jobsDir, slug) {
6340
- return `${jobsDir.replace(/\/+$/, "")}/${slug}/state.json`;
6315
+ function stateFilePath(jobsDir, slug2) {
6316
+ return `${jobsDir.replace(/\/+$/, "")}/${slug2}/state.json`;
6341
6317
  }
6342
6318
  function slugFromStateFilePath(filePath) {
6343
6319
  if (/\/state\.json$/i.test(filePath)) {
@@ -6379,8 +6355,8 @@ var init_contentsApiBackend = __esm({
6379
6355
  this.jobsDir = stateRepoJobsDir(opts.jobsDir);
6380
6356
  this.cwd = opts.cwd;
6381
6357
  }
6382
- load(slug) {
6383
- const filePath = stateFilePath(this.jobsDir, slug);
6358
+ load(slug2) {
6359
+ const filePath = stateFilePath(this.jobsDir, slug2);
6384
6360
  const loaded = readStateText(this.config, this.cwd, filePath);
6385
6361
  if (!loaded) {
6386
6362
  return { path: filePath, handle: null, state: initialStateEnvelope("seed"), created: true };
@@ -6400,20 +6376,20 @@ var init_contentsApiBackend = __esm({
6400
6376
  if (!loaded.created && isStateUnchanged(loaded.state, next)) {
6401
6377
  return false;
6402
6378
  }
6403
- const slug = slugFromStateFilePath(loaded.path);
6379
+ const slug2 = slugFromStateFilePath(loaded.path);
6404
6380
  const body = `${JSON.stringify(next, null, 2)}
6405
6381
  `;
6406
- const message = `chore(jobs): update state for ${slug} (rev ${next.rev})`;
6382
+ const message = `chore(jobs): update state for ${slug2} (rev ${next.rev})`;
6407
6383
  const sha = typeof loaded.handle === "string" ? loaded.handle : void 0;
6408
6384
  try {
6409
6385
  writeStateText(this.config, this.cwd, loaded.path, body, message, sha);
6410
6386
  } catch (err) {
6411
6387
  if (!isShaConflict(err)) throw err;
6412
- const current = this.load(slug);
6388
+ const current = this.load(slug2);
6413
6389
  if (!current.created && isStateUnchanged(current.state, next)) return false;
6414
6390
  const currentSha = typeof current.handle === "string" ? current.handle : void 0;
6415
6391
  process.stderr.write(
6416
- `[kody] jobState: concurrent write detected for ${slug}; reloaded SHA and retrying (last-write-wins)
6392
+ `[kody] jobState: concurrent write detected for ${slug2}; reloaded SHA and retrying (last-write-wins)
6417
6393
  `
6418
6394
  );
6419
6395
  writeStateText(this.config, this.cwd, loaded.path, body, message, currentSha);
@@ -6540,8 +6516,8 @@ var init_localFileBackend = __esm({
6540
6516
  `);
6541
6517
  }
6542
6518
  }
6543
- load(slug) {
6544
- const relPath = stateFilePath(this.jobsDir, slug);
6519
+ load(slug2) {
6520
+ const relPath = stateFilePath(this.jobsDir, slug2);
6545
6521
  const absPath = path24.join(this.cwd, relPath);
6546
6522
  if (!fs25.existsSync(absPath)) {
6547
6523
  return { path: relPath, handle: null, state: initialStateEnvelope("seed"), created: true };
@@ -6613,29 +6589,63 @@ import * as path25 from "path";
6613
6589
  function isAgentResponsibilityCadenceGoal(goal, extra) {
6614
6590
  return extra.scheduleMode === "agentLoop" || extra.scheduler === "agentLoop" || goal.type === "standing" && goal.agentResponsibilities.length > 0;
6615
6591
  }
6616
- async function planGoalAgentResponsibilitySchedule(opts) {
6592
+ function isGoalTargetLoop(goal) {
6593
+ return goal.type === "agentLoop" && goal.loopTarget?.type === "goal" && goal.loopTarget.id.trim().length > 0;
6594
+ }
6595
+ function planGoalTargetLoopSchedule(opts) {
6617
6596
  const now = opts.now ?? /* @__PURE__ */ new Date();
6618
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
+ }
6627
+ async function planGoalAgentResponsibilitySchedule(opts) {
6619
6628
  const jobsDir = opts.jobsDir ?? ".kody/agent-responsibilities";
6620
6629
  const jobsRoot = path25.join(opts.cwd, jobsDir);
6630
+ const now = opts.now ?? /* @__PURE__ */ new Date();
6631
+ const at = now.toISOString();
6621
6632
  const backend = resolveBackend({ config: opts.config, cwd: opts.cwd, jobsDir });
6622
6633
  const statuses = {};
6623
6634
  const blockers = [];
6624
- for (const slug of opts.goal.agentResponsibilities) {
6625
- const agentResponsibility2 = resolveAgentResponsibilityFolder(slug, jobsRoot);
6635
+ for (const slug2 of opts.goal.agentResponsibilities) {
6636
+ const agentResponsibility2 = resolveAgentResponsibilityFolder(slug2, jobsRoot);
6626
6637
  const status = await describeAgentResponsibilitySchedule(
6627
6638
  agentResponsibility2,
6628
- slug,
6639
+ slug2,
6629
6640
  backend,
6630
- now.getTime(),
6631
- opts.previousScheduleState?.agentResponsibilities[slug]
6641
+ opts.previousScheduleState?.agentResponsibilities[slug2]
6632
6642
  );
6633
- statuses[slug] = status;
6634
- if (status.state === "blocked") blockers.push(`${slug}: ${status.reason}`);
6643
+ statuses[slug2] = status;
6644
+ if (status.state === "blocked") blockers.push(`${slug2}: ${status.reason}`);
6635
6645
  }
6636
- const due = opts.goal.agentResponsibilities.map((slug) => statuses[slug]).find((status) => status?.state === "due");
6646
+ const due = opts.goal.agentResponsibilities.map((slug2) => statuses[slug2]).filter((status) => status?.state === "due").sort(compareOldestLastFired)[0];
6637
6647
  if (!due) {
6638
- const reason = blockers.length > 0 ? "no runnable due agentResponsibility; blocked agentResponsibilities need attention" : "no agentResponsibility due now";
6648
+ const reason = blockers.length > 0 ? "no runnable agentResponsibility; blocked agentResponsibilities need attention" : "no runnable agentResponsibility";
6639
6649
  const kind = blockers.length > 0 ? "blocked" : "idle";
6640
6650
  return {
6641
6651
  kind,
@@ -6682,29 +6692,19 @@ async function planGoalAgentResponsibilitySchedule(opts) {
6682
6692
  }
6683
6693
  };
6684
6694
  }
6685
- async function describeAgentResponsibilitySchedule(agentResponsibility, slug, backend, now, previous) {
6686
- if (!agentResponsibility) return { slug, state: "blocked", reason: "agentResponsibility folder missing" };
6695
+ async function describeAgentResponsibilitySchedule(agentResponsibility, slug2, backend, previous) {
6696
+ if (!agentResponsibility) return { slug: slug2, state: "blocked", reason: "agentResponsibility folder missing" };
6687
6697
  const { config } = agentResponsibility;
6688
6698
  if (config.disabled === true) {
6689
- return { slug, title: agentResponsibility.title, cadence: config.every, state: "disabled", reason: "disabled" };
6690
- }
6691
- if (config.every === "manual" || !config.every && !config.agent && config.agentAction) {
6692
- return { slug, title: agentResponsibility.title, cadence: config.every, state: "manual", reason: "manual only" };
6699
+ return { slug: slug2, title: agentResponsibility.title, state: "disabled", reason: "disabled" };
6693
6700
  }
6694
6701
  if (!config.agent || config.agent.trim().length === 0) {
6695
- return {
6696
- slug,
6697
- title: agentResponsibility.title,
6698
- cadence: config.every,
6699
- state: "blocked",
6700
- reason: "no agent assigned"
6701
- };
6702
+ return { slug: slug2, title: agentResponsibility.title, state: "blocked", reason: "no agent assigned" };
6702
6703
  }
6703
6704
  if (config.agentActions && config.agentActions.length > 1) {
6704
6705
  return {
6705
- slug,
6706
+ slug: slug2,
6706
6707
  title: agentResponsibility.title,
6707
- cadence: config.every,
6708
6708
  state: "blocked",
6709
6709
  reason: "multi-agentAction agentResponsibility needs task-jobs route"
6710
6710
  };
@@ -6712,79 +6712,104 @@ async function describeAgentResponsibilitySchedule(agentResponsibility, slug, ba
6712
6712
  let lastFiredAt = validIso(previous?.lastFiredAt) ? previous?.lastFiredAt : void 0;
6713
6713
  try {
6714
6714
  if (!lastFiredAt) {
6715
- const loaded = await backend.load(slug);
6715
+ const loaded = await backend.load(slug2);
6716
6716
  const raw = loaded.state.data?.lastFiredAt;
6717
6717
  if (typeof raw === "string" && validIso(raw)) lastFiredAt = raw;
6718
6718
  }
6719
6719
  } catch {
6720
6720
  return {
6721
- slug,
6722
- title: agentResponsibility.title,
6723
- cadence: config.every,
6724
- state: "due",
6725
- reason: "state unreadable; run to refresh"
6726
- };
6727
- }
6728
- if (!config.every) {
6729
- return {
6730
- slug,
6721
+ slug: slug2,
6731
6722
  title: agentResponsibility.title,
6732
6723
  state: "due",
6733
- reason: "no cadence; check every goal tick",
6734
- lastFiredAt
6735
- };
6736
- }
6737
- if (!lastFiredAt) {
6738
- return {
6739
- slug,
6740
- title: agentResponsibility.title,
6741
- cadence: config.every,
6742
- state: "due",
6743
- reason: `first check for ${config.every}`
6744
- };
6745
- }
6746
- const last = Date.parse(lastFiredAt);
6747
- const interval = scheduleEveryToMs(config.every);
6748
- const next = new Date(last + interval).toISOString();
6749
- if (now - last >= interval) {
6750
- return {
6751
- slug,
6752
- title: agentResponsibility.title,
6753
- cadence: config.every,
6754
- state: "due",
6755
- reason: `due ${config.every}`,
6756
- lastFiredAt,
6757
- nextEligibleAt: next
6724
+ reason: "state unreadable; ready for loop tick"
6758
6725
  };
6759
6726
  }
6760
6727
  return {
6761
- slug,
6728
+ slug: slug2,
6762
6729
  title: agentResponsibility.title,
6763
- cadence: config.every,
6764
- state: "waiting",
6765
- reason: `next due ${next}`,
6766
- lastFiredAt,
6767
- nextEligibleAt: next
6730
+ state: "due",
6731
+ reason: "ready for loop tick",
6732
+ lastFiredAt
6768
6733
  };
6769
6734
  }
6770
6735
  function dutyDispatch(agentResponsibility) {
6771
6736
  const { agentAction, cliArgs } = resolveAgentResponsibilityExecution(agentResponsibility);
6772
6737
  return { agentResponsibility: agentResponsibility.slug, agentAction, cliArgs };
6773
6738
  }
6739
+ function compareOldestLastFired(a, b) {
6740
+ const aTime = validIso(a.lastFiredAt) ? Date.parse(a.lastFiredAt) : Number.NEGATIVE_INFINITY;
6741
+ const bTime = validIso(b.lastFiredAt) ? Date.parse(b.lastFiredAt) : Number.NEGATIVE_INFINITY;
6742
+ return aTime - bTime;
6743
+ }
6774
6744
  function validIso(value) {
6775
6745
  return typeof value === "string" && !Number.isNaN(Date.parse(value));
6776
6746
  }
6777
6747
  function markAgentResponsibilitySelected(status, now) {
6778
- const lastFiredAt = now.toISOString();
6779
- const nextEligibleAt = status.cadence && status.cadence !== "manual" ? new Date(now.getTime() + scheduleEveryToMs(status.cadence)).toISOString() : status.nextEligibleAt;
6780
- return { ...status, lastFiredAt, nextEligibleAt };
6748
+ return { ...status, lastFiredAt: now.toISOString() };
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
+ }
6781
6807
  }
6782
6808
  var init_goalAgentResponsibilityScheduling = __esm({
6783
6809
  "src/scripts/goalAgentResponsibilityScheduling.ts"() {
6784
6810
  "use strict";
6785
6811
  init_registry();
6786
6812
  init_jobState();
6787
- init_scheduleEvery();
6788
6813
  }
6789
6814
  });
6790
6815
 
@@ -6812,8 +6837,8 @@ function routeNeedsIssueFact(goal) {
6812
6837
  return goal.route.some(
6813
6838
  (step) => Object.values(step.args ?? {}).some((value) => {
6814
6839
  if (!value || typeof value !== "object" || Array.isArray(value)) return false;
6815
- const record = value;
6816
- return Object.keys(record).length === 1 && record.fact === "issue";
6840
+ const record2 = value;
6841
+ return Object.keys(record2).length === 1 && record2.fact === "issue";
6817
6842
  })
6818
6843
  );
6819
6844
  }
@@ -6891,6 +6916,23 @@ var init_advanceManagedGoal = __esm({
6891
6916
  ctx.output.reason = reason;
6892
6917
  return;
6893
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
+ }
6894
6936
  if (isAgentResponsibilityCadenceGoal(managed, goal.raw.extra)) {
6895
6937
  const previousScheduleState = goal.raw.extra.scheduleState && typeof goal.raw.extra.scheduleState === "object" ? goal.raw.extra.scheduleState : void 0;
6896
6938
  const decision2 = await planGoalAgentResponsibilitySchedule({
@@ -6907,7 +6949,8 @@ var init_advanceManagedGoal = __esm({
6907
6949
  ctx.output.nextDispatch = {
6908
6950
  agentResponsibility: decision2.dispatch.agentResponsibility,
6909
6951
  agentAction: decision2.dispatch.agentAction,
6910
- cliArgs: decision2.dispatch.cliArgs
6952
+ cliArgs: decision2.dispatch.cliArgs,
6953
+ ...goal.raw.extra.saveReport === true ? { saveReport: true } : {}
6911
6954
  };
6912
6955
  }
6913
6956
  ctx.output.reason = decision2.reason;
@@ -6930,7 +6973,8 @@ var init_advanceManagedGoal = __esm({
6930
6973
  ctx.output.nextDispatch = {
6931
6974
  agentResponsibility: decision.agentResponsibility,
6932
6975
  agentAction: decision.agentAction,
6933
- cliArgs: decision.cliArgs
6976
+ cliArgs: decision.cliArgs,
6977
+ ...decision.saveReport === true ? { saveReport: true } : {}
6934
6978
  };
6935
6979
  ctx.output.reason = `dispatch ${decision.agentResponsibility} for ${decision.evidence}`;
6936
6980
  };
@@ -6944,9 +6988,9 @@ function resolveTrigger(force) {
6944
6988
  if (force || event === "issue_comment" || event === "workflow_dispatch") return "manual";
6945
6989
  return "event";
6946
6990
  }
6947
- function appendLine(ctx, record) {
6948
- const filePath = `activity/${record.ts.slice(0, 10)}.jsonl`;
6949
- appendStateLine(ctx.config, ctx.cwd, filePath, JSON.stringify(record), `chore(activity): ${record.action}`);
6991
+ function appendLine(ctx, record2) {
6992
+ const filePath = `activity/${record2.ts.slice(0, 10)}.jsonl`;
6993
+ appendStateLine(ctx.config, ctx.cwd, filePath, JSON.stringify(record2), `chore(activity): ${record2.action}`);
6950
6994
  }
6951
6995
  var appendCompanyActivity;
6952
6996
  var init_appendCompanyActivity = __esm({
@@ -6962,7 +7006,7 @@ var init_appendCompanyActivity = __esm({
6962
7006
  const agent = ctx.data.agentSlug || null;
6963
7007
  const agentTitle = ctx.data.agentTitle || null;
6964
7008
  const force = ctx.args?.force === true;
6965
- const record = {
7009
+ const record2 = {
6966
7010
  ts: (/* @__PURE__ */ new Date()).toISOString(),
6967
7011
  action: `Ran agentResponsibility: ${agentResponsibilityTitle ?? agentResponsibility}`,
6968
7012
  agentResponsibility,
@@ -6976,7 +7020,7 @@ var init_appendCompanyActivity = __esm({
6976
7020
  durationMs: agentResult?.durationMs ?? null,
6977
7021
  runUrl: getRunUrl() || null
6978
7022
  };
6979
- appendLine(ctx, record);
7023
+ appendLine(ctx, record2);
6980
7024
  } catch (err) {
6981
7025
  process.stderr.write(
6982
7026
  `[activity] company-activity append failed: ${err instanceof Error ? err.message : String(err)}
@@ -6987,6 +7031,338 @@ var init_appendCompanyActivity = __esm({
6987
7031
  }
6988
7032
  });
6989
7033
 
7034
+ // src/companyManagerDecision.ts
7035
+ function parseCompanyManagerDecisionText(finalText) {
7036
+ const raw = extractDecisionJson(finalText);
7037
+ const parsed = JSON.parse(raw);
7038
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
7039
+ throw new Error("company-manager decision must be JSON object");
7040
+ }
7041
+ const input = parsed;
7042
+ const actions = Array.isArray(input.actions) ? input.actions.map(parseAction) : [];
7043
+ return {
7044
+ summary: typeof input.summary === "string" ? input.summary.trim() : "",
7045
+ actions
7046
+ };
7047
+ }
7048
+ function buildManagedGoalState(action) {
7049
+ const at = nowIso();
7050
+ return {
7051
+ state: "active",
7052
+ createdAt: at,
7053
+ updatedAt: at,
7054
+ extra: {
7055
+ type: action.goalType ?? "release",
7056
+ destination: { outcome: action.outcome, evidence: action.evidence },
7057
+ agentResponsibilities: action.agentResponsibilities,
7058
+ route: action.route,
7059
+ facts: action.facts ?? {},
7060
+ blockers: [],
7061
+ createdByIntent: action.intentId,
7062
+ manager: "cto"
7063
+ }
7064
+ };
7065
+ }
7066
+ function buildAgentLoopState(action) {
7067
+ const at = nowIso();
7068
+ return {
7069
+ state: "active",
7070
+ createdAt: at,
7071
+ updatedAt: at,
7072
+ extra: {
7073
+ type: "agentLoop",
7074
+ scheduleMode: "agentLoop",
7075
+ schedule: action.every,
7076
+ destination: { outcome: action.outcome, evidence: [] },
7077
+ agentResponsibilities: action.agentResponsibilities,
7078
+ route: [],
7079
+ facts: {},
7080
+ blockers: [],
7081
+ createdByIntent: action.intentId,
7082
+ manager: "cto"
7083
+ }
7084
+ };
7085
+ }
7086
+ function extractDecisionJson(finalText) {
7087
+ const fence = finalText.match(/```(?:kody-company-manager-decision|json)\s*([\s\S]*?)```/i);
7088
+ if (fence?.[1]) return fence[1].trim();
7089
+ const line = finalText.match(/KODY_COMPANY_MANAGER_DECISION=(\{[\s\S]*\})/);
7090
+ if (line?.[1]) return line[1].trim();
7091
+ throw new Error("missing kody-company-manager-decision JSON");
7092
+ }
7093
+ function parseAction(value) {
7094
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
7095
+ throw new Error("company-manager action must be object");
7096
+ }
7097
+ const input = value;
7098
+ const kind = input.kind;
7099
+ if (kind === "createManagedGoal") return parseCreateManagedGoal(input);
7100
+ if (kind === "createAgentLoop") return parseCreateAgentLoop(input);
7101
+ if (kind === "setGoalLifecycle") return parseSetGoalLifecycle(input);
7102
+ if (kind === "updateIntentPortfolio") return parseUpdateIntentPortfolio(input);
7103
+ if (kind === "note") return parseNote(input);
7104
+ throw new Error(`unsupported company-manager action kind: ${String(kind)}`);
7105
+ }
7106
+ function parseCreateManagedGoal(input) {
7107
+ const route = Array.isArray(input.route) ? input.route.map(parseRouteStep) : [];
7108
+ if (route.length === 0) throw new Error("createManagedGoal requires route");
7109
+ const evidence = stringArray2(input.evidence);
7110
+ if (evidence.length === 0) throw new Error("createManagedGoal requires evidence");
7111
+ return {
7112
+ kind: "createManagedGoal",
7113
+ intentId: slug(input.intentId, "intentId"),
7114
+ id: slug(input.id, "id"),
7115
+ outcome: requiredString(input.outcome, "outcome"),
7116
+ goalType: typeof input.goalType === "string" && input.goalType.trim() ? input.goalType.trim() : void 0,
7117
+ evidence,
7118
+ agentResponsibilities: nonEmptyStringArray(input.agentResponsibilities, "agentResponsibilities"),
7119
+ route,
7120
+ facts: record(input.facts) ?? {},
7121
+ reason: requiredString(input.reason, "reason")
7122
+ };
7123
+ }
7124
+ function parseCreateAgentLoop(input) {
7125
+ return {
7126
+ kind: "createAgentLoop",
7127
+ intentId: slug(input.intentId, "intentId"),
7128
+ id: slug(input.id, "id"),
7129
+ outcome: requiredString(input.outcome, "outcome"),
7130
+ every: oneOf(input.every, ["manual", "1h", "1d", "7d", "30d"], "1d"),
7131
+ agentResponsibilities: nonEmptyStringArray(input.agentResponsibilities, "agentResponsibilities"),
7132
+ reason: requiredString(input.reason, "reason")
7133
+ };
7134
+ }
7135
+ function parseSetGoalLifecycle(input) {
7136
+ return {
7137
+ kind: "setGoalLifecycle",
7138
+ intentId: slug(input.intentId, "intentId"),
7139
+ id: slug(input.id, "id"),
7140
+ state: oneOf(input.state, ["active", "closed", "abandoned"], "active"),
7141
+ reason: requiredString(input.reason, "reason")
7142
+ };
7143
+ }
7144
+ function parseUpdateIntentPortfolio(input) {
7145
+ return {
7146
+ kind: "updateIntentPortfolio",
7147
+ intentId: slug(input.intentId, "intentId"),
7148
+ goals: stringArray2(input.goals).filter(isSlug),
7149
+ loops: stringArray2(input.loops).filter(isSlug),
7150
+ responsibilities: stringArray2(input.responsibilities).filter(isSlug),
7151
+ reason: requiredString(input.reason, "reason")
7152
+ };
7153
+ }
7154
+ function parseNote(input) {
7155
+ return {
7156
+ kind: "note",
7157
+ intentId: typeof input.intentId === "string" && isSlug(input.intentId) ? input.intentId : void 0,
7158
+ message: requiredString(input.message ?? input.content, "message")
7159
+ };
7160
+ }
7161
+ function parseRouteStep(value) {
7162
+ if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error("route step must be object");
7163
+ const input = value;
7164
+ return {
7165
+ stage: requiredString(input.stage, "route.stage"),
7166
+ evidence: requiredString(input.evidence, "route.evidence"),
7167
+ agentResponsibility: slug(input.agentResponsibility, "route.agentResponsibility"),
7168
+ ...typeof input.agentAction === "string" && input.agentAction.trim() ? { agentAction: input.agentAction.trim() } : {},
7169
+ ...record(input.args) ? { args: record(input.args) } : {}
7170
+ };
7171
+ }
7172
+ function slug(value, field) {
7173
+ const text = requiredString(value, field);
7174
+ if (!isSlug(text)) throw new Error(`${field} must be lowercase slug`);
7175
+ return text;
7176
+ }
7177
+ function isSlug(value) {
7178
+ return SLUG_RE.test(value);
7179
+ }
7180
+ function requiredString(value, field) {
7181
+ if (typeof value !== "string" || !value.trim()) throw new Error(`${field} is required`);
7182
+ return value.trim();
7183
+ }
7184
+ function stringArray2(value) {
7185
+ if (!Array.isArray(value)) return [];
7186
+ return value.filter((item) => typeof item === "string").map((item) => item.trim()).filter(Boolean);
7187
+ }
7188
+ function nonEmptyStringArray(value, field) {
7189
+ const values = stringArray2(value);
7190
+ if (values.length === 0) throw new Error(`${field} must not be empty`);
7191
+ return values;
7192
+ }
7193
+ function record(value) {
7194
+ return value && typeof value === "object" && !Array.isArray(value) ? { ...value } : null;
7195
+ }
7196
+ function oneOf(value, allowed, fallback) {
7197
+ return typeof value === "string" && allowed.includes(value) ? value : fallback;
7198
+ }
7199
+ var SLUG_RE;
7200
+ var init_companyManagerDecision = __esm({
7201
+ "src/companyManagerDecision.ts"() {
7202
+ "use strict";
7203
+ init_state2();
7204
+ SLUG_RE = /^[a-z][a-z0-9-]{0,63}$/;
7205
+ }
7206
+ });
7207
+
7208
+ // src/companyIntent.ts
7209
+ function isCompanyIntentId(value) {
7210
+ return SLUG_RE2.test(value);
7211
+ }
7212
+ function companyIntentPath(id) {
7213
+ assertIntentId(id);
7214
+ return `intents/${id}/intent.json`;
7215
+ }
7216
+ function normalizeCompanyIntent(path46, raw) {
7217
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
7218
+ throw new Error(`${path46}: intent must be JSON object`);
7219
+ }
7220
+ const input = raw;
7221
+ const id = stringField2(input.id);
7222
+ if (!id || !isCompanyIntentId(id)) throw new Error(`${path46}: invalid intent id`);
7223
+ const createdAt = stringField2(input.createdAt) || nowIso();
7224
+ const updatedAt = stringField2(input.updatedAt) || createdAt;
7225
+ return {
7226
+ version: 1,
7227
+ id,
7228
+ status: oneOf2(input.status, ["active", "paused", "archived"], "active"),
7229
+ for: stringField2(input.for),
7230
+ priority: numberField(input.priority, 100),
7231
+ posture: oneOf2(
7232
+ input.posture,
7233
+ ["confidence", "speed", "stability-recovery", "maintenance", "balanced"],
7234
+ "balanced"
7235
+ ),
7236
+ scope: {
7237
+ repos: stringArray3(recordField(input.scope)?.repos),
7238
+ areas: stringArray3(recordField(input.scope)?.areas)
7239
+ },
7240
+ principles: stringArray3(input.principles),
7241
+ metrics: stringArray3(input.metrics),
7242
+ policy: {
7243
+ release: normalizeReleasePolicy(recordField(recordField(input.policy)?.release)),
7244
+ automation: normalizeAutomationPolicy(recordField(recordField(input.policy)?.automation))
7245
+ },
7246
+ portfolio: {
7247
+ goals: stringArray3(recordField(input.portfolio)?.goals).filter(isCompanyIntentId),
7248
+ loops: stringArray3(recordField(input.portfolio)?.loops).filter(isCompanyIntentId),
7249
+ responsibilities: stringArray3(recordField(input.portfolio)?.responsibilities).filter(isCompanyIntentId)
7250
+ },
7251
+ manager: normalizeManager(recordField(input.manager)),
7252
+ createdAt,
7253
+ updatedAt
7254
+ };
7255
+ }
7256
+ function listCompanyIntents(config, cwd) {
7257
+ const entries = listStateDirectory(config, cwd, "intents");
7258
+ const records = [];
7259
+ for (const entry of entries) {
7260
+ if (entry.type !== "dir" || !entry.name || !isCompanyIntentId(entry.name)) continue;
7261
+ const path46 = companyIntentPath(entry.name);
7262
+ const file = readStateText(config, cwd, path46);
7263
+ if (!file) continue;
7264
+ records.push({
7265
+ id: entry.name,
7266
+ path: file.path,
7267
+ intent: normalizeCompanyIntent(file.path, JSON.parse(file.content))
7268
+ });
7269
+ }
7270
+ return records.sort((a, b) => a.intent.priority - b.intent.priority || a.id.localeCompare(b.id));
7271
+ }
7272
+ function readCompanyIntent(config, cwd, id) {
7273
+ const path46 = companyIntentPath(id);
7274
+ const file = readStateText(config, cwd, path46);
7275
+ if (!file) return null;
7276
+ return { id, path: file.path, intent: normalizeCompanyIntent(file.path, JSON.parse(file.content)) };
7277
+ }
7278
+ function writeCompanyIntent(config, cwd, intent, message = `chore(intents): update ${intent.id}`) {
7279
+ upsertStateText(config, cwd, companyIntentPath(intent.id), `${JSON.stringify(intent, null, 2)}
7280
+ `, message);
7281
+ }
7282
+ function appendCompanyIntentDecision(config, cwd, intentId, entry) {
7283
+ assertIntentId(intentId);
7284
+ appendStateLine(config, cwd, `intents/${intentId}/decisions.jsonl`, JSON.stringify(entry), `chore(intents): log ${intentId} decision`);
7285
+ }
7286
+ function listCompanyPortfolio(config, cwd) {
7287
+ const entries = listStateDirectory(config, cwd, "goals/instances");
7288
+ const goals = [];
7289
+ for (const entry of entries) {
7290
+ if (entry.type !== "dir" || !entry.name || !isCompanyIntentId(entry.name)) continue;
7291
+ const file = readStateText(config, cwd, `goals/instances/${entry.name}/state.json`);
7292
+ if (!file) continue;
7293
+ const state = parseGoalState(file.path, JSON.parse(file.content));
7294
+ const destination = recordField(state.extra.destination);
7295
+ goals.push({
7296
+ id: entry.name,
7297
+ state: state.state,
7298
+ type: stringField2(state.extra.type) || void 0,
7299
+ outcome: stringField2(destination?.outcome) || void 0,
7300
+ agentResponsibilities: stringArray3(state.extra.agentResponsibilities),
7301
+ isLoop: state.extra.scheduleMode === "agentLoop" || state.extra.type === "agentLoop",
7302
+ updatedAt: state.updatedAt
7303
+ });
7304
+ }
7305
+ return { goals: goals.sort((a, b) => a.id.localeCompare(b.id)) };
7306
+ }
7307
+ function writeCompanyGoalState(config, cwd, id, state, message) {
7308
+ assertIntentId(id);
7309
+ upsertStateText(config, cwd, `goals/instances/${id}/state.json`, serializeGoalState(state), message);
7310
+ }
7311
+ function assertIntentId(id) {
7312
+ if (!isCompanyIntentId(id)) throw new Error(`invalid intent/portfolio id: ${id}`);
7313
+ }
7314
+ function stringField2(value) {
7315
+ return typeof value === "string" ? value.trim() : "";
7316
+ }
7317
+ function numberField(value, fallback) {
7318
+ return typeof value === "number" && Number.isFinite(value) ? value : fallback;
7319
+ }
7320
+ function recordField(value) {
7321
+ return value && typeof value === "object" && !Array.isArray(value) ? value : null;
7322
+ }
7323
+ function stringArray3(value) {
7324
+ if (!Array.isArray(value)) return [];
7325
+ return value.filter((item) => typeof item === "string").map((item) => item.trim()).filter(Boolean);
7326
+ }
7327
+ function oneOf2(value, allowed, fallback) {
7328
+ return typeof value === "string" && allowed.includes(value) ? value : fallback;
7329
+ }
7330
+ function normalizeReleasePolicy(raw) {
7331
+ if (!raw) return void 0;
7332
+ return {
7333
+ cadence: oneOf2(raw.cadence, ["manual", "1d", "1w"], "manual"),
7334
+ qaDepth: oneOf2(raw.qaDepth, ["light", "standard", "strict"], "standard"),
7335
+ blockerLevel: oneOf2(raw.blockerLevel, ["low", "standard", "strict"], "standard"),
7336
+ approval: oneOf2(raw.approval, ["none", "before-production", "before-risky-actions"], "before-risky-actions")
7337
+ };
7338
+ }
7339
+ function normalizeAutomationPolicy(raw) {
7340
+ return {
7341
+ authority: "full-auto",
7342
+ maxConcurrentGoals: Math.max(1, Math.floor(numberField(raw?.maxConcurrentGoals, 1))),
7343
+ maxDailyActions: Math.max(1, Math.floor(numberField(raw?.maxDailyActions, 6))),
7344
+ requiresHumanFor: stringArray3(raw?.requiresHumanFor)
7345
+ };
7346
+ }
7347
+ function normalizeManager(raw) {
7348
+ return {
7349
+ agent: "cto",
7350
+ loop: "company-manager-loop",
7351
+ responsibility: "company-manager",
7352
+ reviewEvery: oneOf2(raw?.reviewEvery, ["1d", "1w"], "1d"),
7353
+ ...typeof raw?.lastReviewedAt === "string" ? { lastReviewedAt: raw.lastReviewedAt } : {}
7354
+ };
7355
+ }
7356
+ var SLUG_RE2;
7357
+ var init_companyIntent = __esm({
7358
+ "src/companyIntent.ts"() {
7359
+ "use strict";
7360
+ init_state2();
7361
+ init_stateRepo();
7362
+ SLUG_RE2 = /^[a-z][a-z0-9-]{0,63}$/;
7363
+ }
7364
+ });
7365
+
6990
7366
  // src/goal/stateStore.ts
6991
7367
  function statePath(goalId) {
6992
7368
  return `goals/instances/${goalId}/state.json`;
@@ -7008,6 +7384,133 @@ var init_stateStore = __esm({
7008
7384
  }
7009
7385
  });
7010
7386
 
7387
+ // src/scripts/applyCompanyManagerDecision.ts
7388
+ function applyAction(config, cwd, action) {
7389
+ if (action.kind === "createManagedGoal") {
7390
+ const existing = fetchGoalState(config, action.id, cwd);
7391
+ if (existing) return applied(action, false, "goal already exists");
7392
+ writeCompanyGoalState(config, cwd, action.id, buildManagedGoalState(action), `chore(goals): create ${action.id} from intent ${action.intentId}`);
7393
+ return applied(action, true, action.reason, action.id);
7394
+ }
7395
+ if (action.kind === "createAgentLoop") {
7396
+ const existing = fetchGoalState(config, action.id, cwd);
7397
+ if (existing) return applied(action, false, "loop already exists");
7398
+ writeCompanyGoalState(config, cwd, action.id, buildAgentLoopState(action), `chore(goals): create loop ${action.id} from intent ${action.intentId}`);
7399
+ return applied(action, true, action.reason, action.id);
7400
+ }
7401
+ if (action.kind === "setGoalLifecycle") {
7402
+ const state = fetchGoalState(config, action.id, cwd);
7403
+ if (!state) return applied(action, false, "goal/loop missing", action.id);
7404
+ const before = state.state;
7405
+ if (before === action.state) return applied(action, false, "state already set", action.id);
7406
+ const next = {
7407
+ ...state,
7408
+ state: action.state,
7409
+ updatedAt: nowIso(),
7410
+ extra: {
7411
+ ...state.extra,
7412
+ lifecycleChangedByIntent: action.intentId,
7413
+ lifecycleChangeReason: action.reason
7414
+ }
7415
+ };
7416
+ writeCompanyGoalState(config, cwd, action.id, next, `chore(goals): ${action.state} ${action.id} from intent ${action.intentId}`);
7417
+ return applied(action, true, action.reason, action.id);
7418
+ }
7419
+ if (action.kind === "updateIntentPortfolio") {
7420
+ const record2 = readCompanyIntent(config, cwd, action.intentId);
7421
+ if (!record2) return applied(action, false, "intent missing");
7422
+ const intent = {
7423
+ ...record2.intent,
7424
+ portfolio: {
7425
+ goals: mergeUnique(record2.intent.portfolio.goals, action.goals ?? []),
7426
+ loops: mergeUnique(record2.intent.portfolio.loops, action.loops ?? []),
7427
+ responsibilities: mergeUnique(record2.intent.portfolio.responsibilities, action.responsibilities ?? [])
7428
+ },
7429
+ updatedAt: nowIso()
7430
+ };
7431
+ writeCompanyIntent(config, cwd, intent, `chore(intents): update ${action.intentId} portfolio`);
7432
+ return applied(action, true, action.reason);
7433
+ }
7434
+ if (action.kind === "note") {
7435
+ return {
7436
+ kind: action.kind,
7437
+ intentId: action.intentId,
7438
+ changed: false,
7439
+ reason: action.message
7440
+ };
7441
+ }
7442
+ return { kind: "unknown", changed: false, reason: "unsupported action" };
7443
+ }
7444
+ function applied(action, changed, reason, resource) {
7445
+ return {
7446
+ kind: action.kind,
7447
+ intentId: "intentId" in action ? action.intentId : void 0,
7448
+ resource,
7449
+ changed,
7450
+ reason
7451
+ };
7452
+ }
7453
+ function mergeUnique(left, right) {
7454
+ return [.../* @__PURE__ */ new Set([...left, ...right])].sort();
7455
+ }
7456
+ function logAppliedCompanyManagerActions(config, cwd, appliedActions) {
7457
+ const at = nowIso();
7458
+ for (const action of appliedActions) {
7459
+ if (!action.intentId) continue;
7460
+ appendCompanyIntentDecision(config, cwd, action.intentId, {
7461
+ at,
7462
+ agent: "cto",
7463
+ intentId: action.intentId,
7464
+ action: action.kind,
7465
+ reason: action.reason,
7466
+ after: { changed: action.changed },
7467
+ resources: action.resource ? [action.resource] : []
7468
+ });
7469
+ }
7470
+ }
7471
+ var applyCompanyManagerDecision;
7472
+ var init_applyCompanyManagerDecision = __esm({
7473
+ "src/scripts/applyCompanyManagerDecision.ts"() {
7474
+ "use strict";
7475
+ init_companyManagerDecision();
7476
+ init_companyIntent();
7477
+ init_stateStore();
7478
+ init_state2();
7479
+ applyCompanyManagerDecision = async (ctx) => {
7480
+ const decision = ctx.data.companyManagerDecision;
7481
+ if (!decision || !Array.isArray(decision.actions)) return;
7482
+ if (ctx.output.exitCode !== 0) return;
7483
+ const applied2 = [];
7484
+ for (const action of decision.actions) {
7485
+ applied2.push(applyAction(ctx.config, ctx.cwd, action));
7486
+ }
7487
+ ctx.data.companyManagerApplied = applied2;
7488
+ ctx.data.companyManagerApplySummary = `company-manager applied ${applied2.filter((item) => item.changed).length}/${applied2.length} action(s)`;
7489
+ };
7490
+ }
7491
+ });
7492
+
7493
+ // src/scripts/appendCompanyIntentDecision.ts
7494
+ var appendCompanyIntentDecision2;
7495
+ var init_appendCompanyIntentDecision = __esm({
7496
+ "src/scripts/appendCompanyIntentDecision.ts"() {
7497
+ "use strict";
7498
+ init_applyCompanyManagerDecision();
7499
+ appendCompanyIntentDecision2 = async (ctx) => {
7500
+ const applied2 = ctx.data.companyManagerApplied;
7501
+ if (!applied2 || applied2.length === 0) return;
7502
+ try {
7503
+ logAppliedCompanyManagerActions(ctx.config, ctx.cwd, applied2);
7504
+ } catch (err) {
7505
+ process.stderr.write(
7506
+ `[company-manager] failed append intent decision log: ${err instanceof Error ? err.message : String(err)}
7507
+ `
7508
+ );
7509
+ }
7510
+ };
7511
+ }
7512
+ });
7513
+
7011
7514
  // src/scripts/applyAgentResponsibilityReports.ts
7012
7515
  function collectReports(raw, agentResult) {
7013
7516
  const out = [];
@@ -8214,7 +8717,7 @@ function discoverPayloadCollections(cwd) {
8214
8717
  const content = fs29.readFileSync(filePath, "utf-8").slice(0, 1e4);
8215
8718
  const slugMatch = content.match(/slug:\s*['"]([a-z0-9-]+)['"]/);
8216
8719
  if (!slugMatch) continue;
8217
- const slug = slugMatch[1];
8720
+ const slug2 = slugMatch[1];
8218
8721
  const name = file.replace(/\.(ts|tsx)$/, "");
8219
8722
  const fields = [];
8220
8723
  const fieldMatches = content.matchAll(/name:\s*['"]([a-zA-Z_][a-zA-Z0-9_]*)['"]/g);
@@ -8224,7 +8727,7 @@ function discoverPayloadCollections(cwd) {
8224
8727
  const hasAdmin = /components:\s*\{/.test(content) || /Field:\s*['"]/.test(content) || /Cell:\s*['"]/.test(content) || /views:\s*\{/.test(content);
8225
8728
  out.push({
8226
8729
  name,
8227
- slug,
8730
+ slug: slug2,
8228
8731
  filePath: path29.relative(cwd, filePath),
8229
8732
  fields: fields.slice(0, 20),
8230
8733
  hasAdmin
@@ -8621,425 +9124,34 @@ var init_dispatch = __esm({
8621
9124
  };
8622
9125
  ctx.data.action = action;
8623
9126
  if (state) state.core.lastOutcome = action;
8624
- return;
8625
- }
8626
- if (state?.flow) {
8627
- state.flow.step = next;
8628
- }
8629
- const usePr = target === "pr" && state?.core.prUrl;
8630
- const targetNumber = usePr ? parsePr(state.core.prUrl) ?? issueNumber : issueNumber;
8631
- ctx.output.nextDispatch = {
8632
- action: next,
8633
- cliArgs: usePr ? { pr: targetNumber } : { issue: targetNumber }
8634
- };
8635
- };
8636
- }
8637
- });
8638
-
8639
- // src/jobIdentity.ts
8640
- function stableJobKey(job) {
8641
- const agentResponsibility = job.agentResponsibility ?? job.action;
8642
- const agentAction = job.agentAction ?? agentResponsibility ?? "unknown";
8643
- if (job.flavor === "scheduled" && job.agentResponsibility)
8644
- return `scheduled:${job.agentResponsibility}:${agentAction}`;
8645
- const target = typeof job.target === "number" ? job.target : targetFromCliArgs(job.cliArgs);
8646
- const work = agentResponsibility && agentAction && agentAction !== agentResponsibility ? `${agentResponsibility}:${agentAction}` : agentResponsibility ?? agentAction;
8647
- return target === void 0 ? `${job.flavor}:${work}` : `${job.flavor}:${work}:${target}`;
8648
- }
8649
- function targetFromCliArgs(cliArgs) {
8650
- if (!cliArgs) return void 0;
8651
- for (const key of ["issue", "pr", "target", "issue_number"]) {
8652
- const value = cliArgs[key];
8653
- if (typeof value === "number" && Number.isFinite(value)) return value;
8654
- }
8655
- return void 0;
8656
- }
8657
- var init_jobIdentity = __esm({
8658
- "src/jobIdentity.ts"() {
8659
- "use strict";
8660
- }
8661
- });
8662
-
8663
- // src/scripts/planTaskJobs.ts
8664
- function parseTaskJobSpecs(body) {
8665
- const match = body.match(new RegExp(`<!--\\s*${TASK_JOBS_MARKER}\\s*([\\s\\S]*?)-->`));
8666
- if (!match) return [];
8667
- let parsed;
8668
- try {
8669
- parsed = JSON.parse(match[1].trim());
8670
- } catch (err) {
8671
- throw new Error(`task job plan is not valid JSON: ${err instanceof Error ? err.message : String(err)}`);
8672
- }
8673
- if (!Array.isArray(parsed)) throw new Error("task job plan must be a JSON array");
8674
- return parsed.map((entry, index) => normalizeSpec(entry, index));
8675
- }
8676
- function taskJobSpecToJob(spec, issueNumber) {
8677
- const cliArgs = spec.cliArgs ?? { issue: issueNumber };
8678
- const target = typeof spec.target === "number" ? spec.target : targetFromCliArgs(cliArgs) ?? issueNumber;
8679
- return {
8680
- agentResponsibility: spec.agentResponsibility ?? spec.agentAction,
8681
- agentAction: spec.agentAction,
8682
- why: spec.reason,
8683
- agent: spec.agent,
8684
- schedule: spec.schedule,
8685
- target,
8686
- cliArgs,
8687
- flavor: spec.flavor ?? "instant"
8688
- };
8689
- }
8690
- function normalizeSpec(input, index) {
8691
- if (!input || typeof input !== "object" || Array.isArray(input)) {
8692
- throw new Error(`task job plan entry ${index} must be an object`);
8693
- }
8694
- const raw = input;
8695
- const agentAction = typeof raw.agentAction === "string" ? raw.agentAction.trim() : "";
8696
- if (!/^[a-z][a-z0-9-]*$/.test(agentAction)) {
8697
- throw new Error(`task job plan entry ${index} must have a valid agentAction`);
8698
- }
8699
- const cliArgs = raw.cliArgs;
8700
- if (cliArgs !== void 0 && (!cliArgs || typeof cliArgs !== "object" || Array.isArray(cliArgs))) {
8701
- throw new Error(`task job plan entry ${index} cliArgs must be an object`);
8702
- }
8703
- const flavor = raw.flavor;
8704
- if (flavor !== void 0 && flavor !== "instant" && flavor !== "scheduled") {
8705
- throw new Error(`task job plan entry ${index} flavor must be "instant" or "scheduled"`);
8706
- }
8707
- return {
8708
- agentAction,
8709
- ...typeof raw.agentResponsibility === "string" && raw.agentResponsibility.trim() ? { agentResponsibility: raw.agentResponsibility.trim() } : {},
8710
- ...typeof raw.reason === "string" && raw.reason.trim() ? { reason: raw.reason.trim() } : {},
8711
- ...typeof raw.agent === "string" && raw.agent.trim() ? { agent: raw.agent.trim() } : {},
8712
- ...typeof raw.agent === "string" && raw.agent.trim() ? { agent: raw.agent.trim() } : {},
8713
- ...cliArgs ? { cliArgs } : {},
8714
- ...typeof raw.target === "number" && Number.isFinite(raw.target) ? { target: raw.target } : {},
8715
- ...flavor === "instant" || flavor === "scheduled" ? { flavor } : {},
8716
- ...typeof raw.schedule === "string" && raw.schedule.trim() ? { schedule: raw.schedule.trim() } : {}
8717
- };
8718
- }
8719
- function jobToPlannedTaskJob(job) {
8720
- return {
8721
- id: stableJobKey(job),
8722
- agentAction: job.agentAction ?? job.agentResponsibility ?? "unknown",
8723
- agentResponsibility: job.agentResponsibility ?? job.action ?? job.agentAction ?? "unknown",
8724
- ...job.agent ? { agent: job.agent } : {},
8725
- ...job.flavor ? { flavor: job.flavor } : {},
8726
- ...job.schedule ? { schedule: job.schedule } : {},
8727
- ...typeof job.target === "number" ? { target: job.target } : {},
8728
- ...job.why ? { reason: job.why } : {}
8729
- };
8730
- }
8731
- function assertUniqueJobIds(planned) {
8732
- const seen = /* @__PURE__ */ new Set();
8733
- for (const job of planned) {
8734
- if (seen.has(job.id)) throw new Error(`duplicate planned task job id: ${job.id}`);
8735
- seen.add(job.id);
8736
- }
8737
- }
8738
- var TASK_JOBS_MARKER, planTaskJobs;
8739
- var init_planTaskJobs = __esm({
8740
- "src/scripts/planTaskJobs.ts"() {
8741
- "use strict";
8742
- init_jobIdentity();
8743
- init_state();
8744
- TASK_JOBS_MARKER = "kody:task-jobs:v1";
8745
- planTaskJobs = async (ctx) => {
8746
- const issueNumber = ctx.args.issue;
8747
- if (!issueNumber) {
8748
- ctx.skipAgent = true;
8749
- ctx.output.exitCode = 64;
8750
- ctx.output.reason = "planTaskJobs requires --issue";
8751
- return;
8752
- }
8753
- const issue = ctx.data.issue;
8754
- const specs = parseTaskJobSpecs(issue?.body ?? "");
8755
- if (specs.length === 0) {
8756
- ctx.skipAgent = true;
8757
- ctx.output.exitCode = 64;
8758
- ctx.output.reason = `no ${TASK_JOBS_MARKER} block found on issue #${issueNumber}`;
8759
- return;
8760
- }
8761
- const jobs = specs.map((spec) => taskJobSpecToJob(spec, issueNumber));
8762
- const planned = jobs.map(jobToPlannedTaskJob);
8763
- assertUniqueJobIds(planned);
8764
- const prior = ctx.data.taskState ?? emptyState();
8765
- const next = upsertTaskJobs(prior, planned, (/* @__PURE__ */ new Date()).toISOString());
8766
- ctx.data.taskState = next;
8767
- ctx.data.plannedTaskJobs = jobs;
8768
- ctx.data.plannedTaskJobIds = planned.map((job) => job.id);
8769
- const target = ctx.data.commentTargetType;
8770
- const number = ctx.data.commentTargetNumber;
8771
- if (target && number) writeTaskState(target, number, next, ctx.cwd, ctx.config);
8772
- };
8773
- }
8774
- });
8775
-
8776
- // src/scripts/dispatchAgentResponsibilityFileTicks.ts
8777
- import * as path31 from "path";
8778
- async function decideShouldFire(every, slug, backend, now) {
8779
- if (!every) return { skip: false, reason: "no schedule (every cron tick)" };
8780
- if (every === "manual") {
8781
- return { skip: true, reason: "manual-only (no auto-fire; trigger via dashboard Run now)" };
8782
- }
8783
- let lastFiredAt = null;
8784
- try {
8785
- const loaded = await backend.load(slug);
8786
- const raw = loaded.state.data?.lastFiredAt;
8787
- if (typeof raw === "string") {
8788
- const ms = Date.parse(raw);
8789
- if (!Number.isNaN(ms)) lastFiredAt = ms;
8790
- }
8791
- } catch {
8792
- return { skip: false, reason: "state unreadable; firing" };
8793
- }
8794
- if (lastFiredAt === null) {
8795
- return { skip: false, reason: `first tick (every ${every})` };
8796
- }
8797
- const intervalMs = scheduleEveryToMs(every);
8798
- const elapsedMs = now - lastFiredAt;
8799
- if (elapsedMs >= intervalMs) {
8800
- return { skip: false, reason: `due (every ${every}, last ${formatAgo(elapsedMs)} ago)` };
8801
- }
8802
- const remainingMs = intervalMs - elapsedMs;
8803
- return {
8804
- skip: true,
8805
- reason: `every ${every}; ${formatAgo(elapsedMs)} since last tick, next in ${formatAgo(remainingMs)}`
8806
- };
8807
- }
8808
- function formatAgo(ms) {
8809
- const sec = Math.max(0, Math.round(ms / 1e3));
8810
- if (sec < 60) return `${sec}s`;
8811
- const min = Math.round(sec / 60);
8812
- if (min < 60) return `${min}m`;
8813
- const hr = Math.round(min / 60);
8814
- if (hr < 48) return `${hr}h`;
8815
- const day = Math.round(hr / 24);
8816
- return `${day}d`;
8817
- }
8818
- function parseAgentResponsibilityFilter(raw) {
8819
- return typeof raw === "string" && raw.trim().length > 0 ? raw.trim() : void 0;
8820
- }
8821
- function filterSlugs(slugs, onlyAgentResponsibility) {
8822
- return onlyAgentResponsibility ? slugs.filter((slug) => slug === onlyAgentResponsibility) : slugs;
8823
- }
8824
- function listActivatedAgentResponsibilitySlugs(projectRoot, storeRoot, activeStoreAgentResponsibilities) {
8825
- const seen = /* @__PURE__ */ new Set();
8826
- const out = [];
8827
- for (const slug of listAgentResponsibilityFolderSlugs(projectRoot)) {
8828
- if (seen.has(slug)) continue;
8829
- seen.add(slug);
8830
- out.push(slug);
8831
- }
8832
- const active = new Set(activeStoreAgentResponsibilities ?? []);
8833
- if (storeRoot && active.size > 0) {
8834
- for (const slug of listAgentResponsibilityFolderSlugs(storeRoot)) {
8835
- if (!active.has(slug) || seen.has(slug)) continue;
8836
- seen.add(slug);
8837
- out.push(slug);
8838
- }
8839
- }
8840
- return out.sort();
8841
- }
8842
- function createAgentResponsibilityTaskIssue(opts) {
8843
- const title = `AgentResponsibility ${opts.slug} - multi-agentAction task`;
8844
- const body = buildAgentResponsibilityTaskIssueBody(opts.slug, opts.body, opts.config);
8845
- const out = gh(["issue", "create", "--title", title, "--body-file", "-"], { input: body, cwd: opts.cwd });
8846
- const url = out.split("\n").map((line) => line.trim()).filter(Boolean).pop() ?? "";
8847
- const match = url.match(/\/issues\/(\d+)\b/);
8848
- if (!match) throw new Error(`gh issue create returned unexpected output: ${out}`);
8849
- return { number: Number(match[1]), url };
8850
- }
8851
- function buildAgentResponsibilityTaskIssueBody(slug, agentResponsibilityBody, config) {
8852
- const specs = (config.agentActions ?? []).map((agentAction) => ({
8853
- agentAction,
8854
- agentResponsibility: slug,
8855
- ...config.agent ? { agent: config.agent } : {},
8856
- reason: `AgentResponsibility \`${slug}\` slice for \`${agentAction}\`.`,
8857
- flavor: "scheduled",
8858
- ...config.every ? { schedule: config.every } : {}
8859
- }));
8860
- return [
8861
- `# AgentResponsibility task: ${slug}`,
8862
- "",
8863
- agentResponsibilityBody.trim() || "(no agentResponsibility body)",
8864
- "",
8865
- `<!-- ${TASK_JOBS_MARKER}`,
8866
- JSON.stringify(specs, null, 2),
8867
- "-->",
8868
- ""
8869
- ].join("\n");
8870
- }
8871
- async function stampFired(backend, slug, now, task) {
8872
- try {
8873
- const loaded = await backend.load(slug);
8874
- const nextData = {
8875
- ...loaded.state.data ?? {},
8876
- lastFiredAt: new Date(now).toISOString(),
8877
- ...task ? { lastTaskIssue: task.number, lastTaskUrl: task.url } : {}
8878
- };
8879
- await backend.save(loaded, { ...loaded.state, data: nextData });
8880
- } catch (err) {
8881
- process.stderr.write(`[jobs] failed to stamp lastFiredAt for ${slug}: ${String(err)}
8882
- `);
8883
- }
8884
- }
8885
- var dispatchAgentResponsibilityFileTicks;
8886
- var init_dispatchAgentResponsibilityFileTicks = __esm({
8887
- "src/scripts/dispatchAgentResponsibilityFileTicks.ts"() {
8888
- "use strict";
8889
- init_agent_responsibilityFolders();
8890
- init_issue();
8891
- init_job();
8892
- init_registry();
8893
- init_jobState();
8894
- init_planTaskJobs();
8895
- init_scheduleEvery();
8896
- dispatchAgentResponsibilityFileTicks = async (ctx, _profile, args) => {
8897
- ctx.skipAgent = true;
8898
- const targetAgentAction = String(args?.targetAgentAction ?? "");
8899
- if (!targetAgentAction) {
8900
- throw new Error("dispatchAgentResponsibilityFileTicks: `with.targetAgentAction` is required");
8901
- }
8902
- const jobsDir = String(args?.jobsDir ?? ".kody/agent-responsibilities");
8903
- const scriptedAgentAction = String(args?.scriptedAgentAction ?? "agent-responsibility-tick-scripted");
8904
- const slugArg = String(args?.slugArg ?? "agentResponsibility");
8905
- const backend = resolveBackend({ config: ctx.config, cwd: ctx.cwd, jobsDir });
8906
- if (backend.hydrate) {
8907
- await backend.hydrate();
8908
- }
8909
- try {
8910
- const onlyAgentResponsibility = parseAgentResponsibilityFilter(ctx.args.agentResponsibility);
8911
- if (args?.requireExplicitAgentResponsibility === true && !onlyAgentResponsibility) {
8912
- ctx.output.exitCode = 0;
8913
- ctx.output.reason = "scheduled agentResponsibility fan-out is owned by goal-scheduler";
8914
- process.stdout.write(
8915
- "[jobs] no flat agentResponsibility fan-out; goal-scheduler owns scheduled agentResponsibility decisions\n"
8916
- );
8917
- return;
8918
- }
8919
- const jobsPath = path31.join(ctx.cwd, jobsDir);
8920
- const storeJobsPath = getCompanyStoreAgentResponsibilitiesRoot();
8921
- const slugs = filterSlugs(
8922
- listActivatedAgentResponsibilitySlugs(jobsPath, storeJobsPath, ctx.config.company?.activeAgentResponsibilities),
8923
- onlyAgentResponsibility
8924
- );
8925
- ctx.data.jobSlugCount = slugs.length;
8926
- if (slugs.length === 0) {
8927
- const filter = onlyAgentResponsibility ? ` matching ${onlyAgentResponsibility}` : "";
8928
- process.stdout.write(`[jobs] no agentResponsibility folders${filter} in ${jobsDir}
8929
- `);
8930
- return;
8931
- }
8932
- const filtered = onlyAgentResponsibility ? ` matching ${onlyAgentResponsibility}` : "";
8933
- process.stdout.write(
8934
- `[jobs] ticking ${slugs.length} agent responsibility/ies${filtered} via ${targetAgentAction}
8935
- `
8936
- );
8937
- const results = [];
8938
- const now = Date.now();
8939
- for (const slug of slugs) {
8940
- const agentResponsibility = resolveAgentResponsibilityFolder(slug, jobsPath);
8941
- if (!agentResponsibility) {
8942
- process.stderr.write(
8943
- `[jobs] \u23ED skip ${slug}: agentResponsibility folder is missing profile.json or agent-responsibility.md
8944
- `
8945
- );
8946
- results.push({ slug, exitCode: 0, skipped: true, reason: "incomplete agentResponsibility folder" });
8947
- continue;
8948
- }
8949
- const config = agentResponsibility.config;
8950
- if (config.disabled === true) {
8951
- process.stdout.write(`[jobs] \u23ED skip ${slug}: disabled in profile.json
8952
- `);
8953
- results.push({ slug, exitCode: 0, skipped: true, reason: "disabled" });
8954
- continue;
8955
- }
8956
- if (!config.agent || config.agent.trim().length === 0) {
8957
- process.stderr.write(`[jobs] \u23ED skip ${slug}: no agent assigned (add "agent" to profile.json)
8958
- `);
8959
- results.push({ slug, exitCode: 0, skipped: true, reason: "no agent assigned" });
8960
- continue;
8961
- }
8962
- const decision = await decideShouldFire(config.every, slug, backend, now);
8963
- if (decision.skip) {
8964
- process.stdout.write(`[jobs] \u23ED skip ${slug}: ${decision.reason}
8965
- `);
8966
- results.push({ slug, exitCode: 0, skipped: true, reason: decision.reason });
8967
- continue;
8968
- }
8969
- if (config.agentActions && config.agentActions.length > 0) {
8970
- try {
8971
- const task = createAgentResponsibilityTaskIssue({
8972
- slug,
8973
- body: agentResponsibility.body,
8974
- config,
8975
- cwd: ctx.cwd
8976
- });
8977
- await stampFired(backend, slug, now, task);
8978
- process.stdout.write(`[jobs] \u2192 run ${slug} multi-agentAction task #${task.number} (task-jobs)
8979
- `);
8980
- const out = await runJob(
8981
- mintScheduledJob({
8982
- agentResponsibility: slug,
8983
- agentAction: "task-jobs",
8984
- schedule: config.every,
8985
- agent: config.agent,
8986
- cliArgs: { issue: task.number }
8987
- }),
8988
- { cwd: ctx.cwd, config: ctx.config, verbose: ctx.verbose, quiet: ctx.quiet }
8989
- );
8990
- results.push({ slug, exitCode: out.exitCode, reason: out.reason });
8991
- if (out.exitCode !== 0) {
8992
- process.stderr.write(`[jobs] task ${slug} failed (exit ${out.exitCode}): ${out.reason ?? ""}
8993
- `);
8994
- }
8995
- } catch (err) {
8996
- const msg = err instanceof Error ? err.message : String(err);
8997
- process.stderr.write(`[jobs] task ${slug} crashed: ${msg}
8998
- `);
8999
- results.push({ slug, exitCode: 99, reason: msg });
9000
- }
9001
- continue;
9002
- }
9003
- const slugTarget = config.tickScript ? scriptedAgentAction : config.agentAction ?? targetAgentAction;
9004
- const cliArgs = config.agentAction && !config.tickScript ? {} : { [slugArg]: slug };
9005
- process.stdout.write(`[jobs] \u2192 tick ${slug} (${slugTarget})
9006
- `);
9007
- try {
9008
- const out = await runJob(
9009
- mintScheduledJob({
9010
- agentResponsibility: slug,
9011
- agentAction: slugTarget,
9012
- schedule: config.every,
9013
- agent: config.agent,
9014
- cliArgs
9015
- }),
9016
- { cwd: ctx.cwd, config: ctx.config, verbose: ctx.verbose, quiet: ctx.quiet, chain: false }
9017
- );
9018
- results.push({ slug, exitCode: out.exitCode, reason: out.reason });
9019
- if (out.exitCode !== 0) {
9020
- process.stderr.write(`[jobs] tick ${slug} failed (exit ${out.exitCode}): ${out.reason ?? ""}
9021
- `);
9022
- }
9023
- } catch (err) {
9024
- const msg = err instanceof Error ? err.message : String(err);
9025
- process.stderr.write(`[jobs] tick ${slug} crashed: ${msg}
9026
- `);
9027
- results.push({ slug, exitCode: 99, reason: msg });
9028
- }
9029
- }
9030
- ctx.data.jobTickResults = results;
9031
- ctx.output.exitCode = 0;
9032
- } finally {
9033
- if (backend.persist) {
9034
- try {
9035
- await backend.persist();
9036
- } catch (err) {
9037
- const msg = err instanceof Error ? err.message : String(err);
9038
- process.stderr.write(`[jobs] backend persist failed: ${msg}
9039
- `);
9040
- }
9041
- }
9127
+ return;
9128
+ }
9129
+ if (state?.flow) {
9130
+ state.flow.step = next;
9042
9131
  }
9132
+ const usePr = target === "pr" && state?.core.prUrl;
9133
+ const targetNumber = usePr ? parsePr(state.core.prUrl) ?? issueNumber : issueNumber;
9134
+ ctx.output.nextDispatch = {
9135
+ action: next,
9136
+ cliArgs: usePr ? { pr: targetNumber } : { issue: targetNumber }
9137
+ };
9138
+ };
9139
+ }
9140
+ });
9141
+
9142
+ // src/scripts/dispatchAgentResponsibilityFileTicks.ts
9143
+ var dispatchAgentResponsibilityFileTicks;
9144
+ var init_dispatchAgentResponsibilityFileTicks = __esm({
9145
+ "src/scripts/dispatchAgentResponsibilityFileTicks.ts"() {
9146
+ "use strict";
9147
+ dispatchAgentResponsibilityFileTicks = async (ctx) => {
9148
+ ctx.skipAgent = true;
9149
+ ctx.data.jobTickResults = [];
9150
+ ctx.output.exitCode = 0;
9151
+ ctx.output.reason = "responsibility scheduling is owned by goals and loops";
9152
+ process.stdout.write(
9153
+ "[jobs] no flat agentResponsibility fan-out; goals and loops own scheduled responsibility decisions\n"
9154
+ );
9043
9155
  };
9044
9156
  }
9045
9157
  });
@@ -9147,6 +9259,30 @@ var init_dispatchClassified = __esm({
9147
9259
  }
9148
9260
  });
9149
9261
 
9262
+ // src/jobIdentity.ts
9263
+ function stableJobKey(job) {
9264
+ const agentResponsibility = job.agentResponsibility ?? job.action;
9265
+ const agentAction = job.agentAction ?? agentResponsibility ?? "unknown";
9266
+ if (job.flavor === "scheduled" && job.agentResponsibility)
9267
+ return `scheduled:${job.agentResponsibility}:${agentAction}`;
9268
+ const target = typeof job.target === "number" ? job.target : targetFromCliArgs(job.cliArgs);
9269
+ const work = agentResponsibility && agentAction && agentAction !== agentResponsibility ? `${agentResponsibility}:${agentAction}` : agentResponsibility ?? agentAction;
9270
+ return target === void 0 ? `${job.flavor}:${work}` : `${job.flavor}:${work}:${target}`;
9271
+ }
9272
+ function targetFromCliArgs(cliArgs) {
9273
+ if (!cliArgs) return void 0;
9274
+ for (const key of ["issue", "pr", "target", "issue_number"]) {
9275
+ const value = cliArgs[key];
9276
+ if (typeof value === "number" && Number.isFinite(value)) return value;
9277
+ }
9278
+ return void 0;
9279
+ }
9280
+ var init_jobIdentity = __esm({
9281
+ "src/jobIdentity.ts"() {
9282
+ "use strict";
9283
+ }
9284
+ });
9285
+
9150
9286
  // src/scripts/dispatchNextTaskJob.ts
9151
9287
  function taskJobToJob(job, issueArg) {
9152
9288
  const target = typeof job.target === "number" ? job.target : typeof issueArg === "number" ? issueArg : void 0;
@@ -9669,8 +9805,8 @@ function git3(args, cwd) {
9669
9805
  }).trim();
9670
9806
  }
9671
9807
  function deriveBranchName(issueNumber, title) {
9672
- const slug = title.toLowerCase().replace(/[^a-z0-9\s-]/g, "").replace(/\s+/g, "-").replace(/-+/g, "-").slice(0, 50).replace(/-$/, "");
9673
- return slug ? `${issueNumber}-${slug}` : `${issueNumber}-task`;
9808
+ const slug2 = title.toLowerCase().replace(/[^a-z0-9\s-]/g, "").replace(/\s+/g, "-").replace(/-+/g, "-").slice(0, 50).replace(/-$/, "");
9809
+ return slug2 ? `${issueNumber}-${slug2}` : `${issueNumber}-task`;
9674
9810
  }
9675
9811
  function getCurrentBranch(cwd) {
9676
9812
  return git3(["branch", "--show-current"], cwd);
@@ -10076,11 +10212,11 @@ var init_fixFlow = __esm({
10076
10212
  // src/scripts/initFlow.ts
10077
10213
  import { execFileSync as execFileSync15 } from "child_process";
10078
10214
  import * as fs31 from "fs";
10079
- import * as path32 from "path";
10215
+ import * as path31 from "path";
10080
10216
  function detectPackageManager(cwd) {
10081
- if (fs31.existsSync(path32.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
10082
- if (fs31.existsSync(path32.join(cwd, "yarn.lock"))) return "yarn";
10083
- if (fs31.existsSync(path32.join(cwd, "bun.lockb"))) return "bun";
10217
+ if (fs31.existsSync(path31.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
10218
+ if (fs31.existsSync(path31.join(cwd, "yarn.lock"))) return "yarn";
10219
+ if (fs31.existsSync(path31.join(cwd, "bun.lockb"))) return "bun";
10084
10220
  return "npm";
10085
10221
  }
10086
10222
  function qualityCommandsFor(pm) {
@@ -10152,7 +10288,7 @@ function performInit(cwd, force) {
10152
10288
  const pm = detectPackageManager(cwd);
10153
10289
  const ownerRepo = detectOwnerRepo(cwd);
10154
10290
  const defaultBranch2 = defaultBranchFromGit(cwd);
10155
- const configPath = path32.join(cwd, "kody.config.json");
10291
+ const configPath = path31.join(cwd, "kody.config.json");
10156
10292
  if (fs31.existsSync(configPath) && !force) {
10157
10293
  skipped.push("kody.config.json");
10158
10294
  } else {
@@ -10161,8 +10297,8 @@ function performInit(cwd, force) {
10161
10297
  `);
10162
10298
  wrote.push("kody.config.json");
10163
10299
  }
10164
- const workflowDir = path32.join(cwd, ".github", "workflows");
10165
- const workflowPath = path32.join(workflowDir, "kody.yml");
10300
+ const workflowDir = path31.join(cwd, ".github", "workflows");
10301
+ const workflowPath = path31.join(workflowDir, "kody.yml");
10166
10302
  if (fs31.existsSync(workflowPath) && !force) {
10167
10303
  skipped.push(".github/workflows/kody.yml");
10168
10304
  } else {
@@ -10170,8 +10306,8 @@ function performInit(cwd, force) {
10170
10306
  fs31.writeFileSync(workflowPath, WORKFLOW_TEMPLATE);
10171
10307
  wrote.push(".github/workflows/kody.yml");
10172
10308
  }
10173
- const agentsDir = path32.join(cwd, ".kody", "agents");
10174
- const agentPath = path32.join(agentsDir, "kody.md");
10309
+ const agentsDir = path31.join(cwd, ".kody", "agents");
10310
+ const agentPath = path31.join(agentsDir, "kody.md");
10175
10311
  if (fs31.existsSync(agentPath) && !force) {
10176
10312
  skipped.push(".kody/agents/kody.md");
10177
10313
  } else {
@@ -10187,7 +10323,7 @@ function performInit(cwd, force) {
10187
10323
  continue;
10188
10324
  }
10189
10325
  if (profile.kind !== "scheduled" || !profile.schedule) continue;
10190
- const target = path32.join(workflowDir, `kody-${exe.name}.yml`);
10326
+ const target = path31.join(workflowDir, `kody-${exe.name}.yml`);
10191
10327
  if (fs31.existsSync(target) && !force) {
10192
10328
  skipped.push(`.github/workflows/kody-${exe.name}.yml`);
10193
10329
  continue;
@@ -10385,7 +10521,7 @@ function stripDirective(body) {
10385
10521
  }
10386
10522
  return lines.slice(start).join("\n").trim();
10387
10523
  }
10388
- function parseAgentFile(raw, slug) {
10524
+ function parseAgentFile(raw, slug2) {
10389
10525
  const stripped = stripLeadingFrontmatter(raw);
10390
10526
  const trimmed = stripped.trim();
10391
10527
  const firstLine2 = trimmed.split("\n", 1)[0] ?? "";
@@ -10394,14 +10530,14 @@ function parseAgentFile(raw, slug) {
10394
10530
  const rest = trimmed.slice(firstLine2.length).replace(/^\n+/, "");
10395
10531
  return { title: h1[1].trim(), body: rest };
10396
10532
  }
10397
- return { title: humanizeSlug2(slug), body: trimmed };
10533
+ return { title: humanizeSlug2(slug2), body: trimmed };
10398
10534
  }
10399
10535
  function stripLeadingFrontmatter(raw) {
10400
10536
  const match = /^---\r?\n[\s\S]*?\r?\n---\r?\n?/.exec(raw);
10401
10537
  return match ? raw.slice(match[0].length) : raw;
10402
10538
  }
10403
- function humanizeSlug2(slug) {
10404
- return slug.split(/[-_]+/).filter((s) => s.length > 0).map((s) => s[0].toUpperCase() + s.slice(1)).join(" ");
10539
+ function humanizeSlug2(slug2) {
10540
+ return slug2.split(/[-_]+/).filter((s) => s.length > 0).map((s) => s[0].toUpperCase() + s.slice(1)).join(" ");
10405
10541
  }
10406
10542
  var loadAgentAdhoc;
10407
10543
  var init_loadAgentAdhoc = __esm({
@@ -10444,19 +10580,19 @@ var init_loadAgentResponsibilityState = __esm({
10444
10580
  AGENT_RESPONSIBILITY_TOOL_PALETTE = new Set(AGENT_RESPONSIBILITY_MCP_TOOL_NAMES);
10445
10581
  loadAgentResponsibilityState = async (ctx, profile, args) => {
10446
10582
  const jobsDir = String(args?.jobsDir ?? ".kody/agent-responsibilities");
10447
- const slug = profile.name;
10583
+ const slug2 = profile.name;
10448
10584
  const backend = resolveBackend({ config: ctx.config, cwd: ctx.cwd, jobsDir });
10449
10585
  if (backend.hydrate) await backend.hydrate();
10450
- const loaded = await backend.load(slug);
10451
- ctx.data.jobSlug = slug;
10586
+ const loaded = await backend.load(slug2);
10587
+ ctx.data.jobSlug = slug2;
10452
10588
  ctx.data.jobState = loaded;
10453
10589
  ctx.data.jobStateJson = JSON.stringify(loaded.state, null, 2);
10454
- ctx.data.agentResponsibilitySlug = slug;
10590
+ ctx.data.agentResponsibilitySlug = slug2;
10455
10591
  ctx.data.agentResponsibilityTitle = profile.describe;
10456
10592
  ctx.data.agentActionSlug = profile.agentAction ?? profile.name;
10457
10593
  ctx.data.agentSlug = profile.agent ?? "";
10458
10594
  ctx.data.agentTitle = "";
10459
- ctx.data.agentResponsibilitySchedule = profile.every ?? profile.schedule ?? "";
10595
+ ctx.data.agentResponsibilitySchedule = String(ctx.data.jobSchedule ?? "");
10460
10596
  const mentions = (profile.mentions ?? []).map((l) => `@${l}`).join(" ");
10461
10597
  ctx.data.mentions = mentions;
10462
10598
  const declaredTools = profile.agentResponsibilityTools ?? [];
@@ -10464,7 +10600,7 @@ var init_loadAgentResponsibilityState = __esm({
10464
10600
  const unknown = declaredTools.filter((name) => !AGENT_RESPONSIBILITY_TOOL_PALETTE.has(name));
10465
10601
  if (unknown.length > 0) {
10466
10602
  throw new Error(
10467
- `loadAgentResponsibilityState: agentResponsibility '${slug}' declared agentResponsibilityTools not in the kody-agentResponsibility palette: ${unknown.join(", ")}. Available: ${[...AGENT_RESPONSIBILITY_MCP_TOOL_NAMES].join(", ")}`
10603
+ `loadAgentResponsibilityState: agentResponsibility '${slug2}' declared agentResponsibilityTools not in the kody-agentResponsibility palette: ${unknown.join(", ")}. Available: ${[...AGENT_RESPONSIBILITY_MCP_TOOL_NAMES].join(", ")}`
10468
10604
  );
10469
10605
  }
10470
10606
  ctx.data.agentResponsibilityTools = declaredTools;
@@ -10478,6 +10614,43 @@ var init_loadAgentResponsibilityState = __esm({
10478
10614
  }
10479
10615
  });
10480
10616
 
10617
+ // src/scripts/loadCompanyIntents.ts
10618
+ var loadCompanyIntents;
10619
+ var init_loadCompanyIntents = __esm({
10620
+ "src/scripts/loadCompanyIntents.ts"() {
10621
+ "use strict";
10622
+ init_companyIntent();
10623
+ loadCompanyIntents = async (ctx) => {
10624
+ const intents = listCompanyIntents(ctx.config, ctx.cwd);
10625
+ const active = intents.filter((record2) => record2.intent.status === "active");
10626
+ ctx.data.companyIntents = intents;
10627
+ ctx.data.companyActiveIntents = active;
10628
+ ctx.data.companyIntentsJson = JSON.stringify(
10629
+ active.map((record2) => record2.intent),
10630
+ null,
10631
+ 2
10632
+ );
10633
+ if (active.length === 0) {
10634
+ ctx.output.reason = "no active company intents";
10635
+ }
10636
+ };
10637
+ }
10638
+ });
10639
+
10640
+ // src/scripts/loadCompanyPortfolio.ts
10641
+ var loadCompanyPortfolio;
10642
+ var init_loadCompanyPortfolio = __esm({
10643
+ "src/scripts/loadCompanyPortfolio.ts"() {
10644
+ "use strict";
10645
+ init_companyIntent();
10646
+ loadCompanyPortfolio = async (ctx) => {
10647
+ const portfolio = listCompanyPortfolio(ctx.config, ctx.cwd);
10648
+ ctx.data.companyPortfolio = portfolio;
10649
+ ctx.data.companyPortfolioJson = JSON.stringify(portfolio, null, 2);
10650
+ };
10651
+ }
10652
+ });
10653
+
10481
10654
  // src/scripts/loadGoalState.ts
10482
10655
  function retryDelaysMs() {
10483
10656
  const raw = process.env.KODY_GOAL_STATE_RETRY_DELAYS_MS?.trim();
@@ -10620,8 +10793,8 @@ var init_loadIssueStateComment = __esm({
10620
10793
 
10621
10794
  // src/scripts/loadJobFromFile.ts
10622
10795
  import * as fs33 from "fs";
10623
- import * as path33 from "path";
10624
- function parseJobFile(raw, slug) {
10796
+ import * as path32 from "path";
10797
+ function parseJobFile(raw, slug2) {
10625
10798
  let stripped = raw;
10626
10799
  if (stripped.startsWith("---\n")) {
10627
10800
  const end = stripped.indexOf("\n---\n", 4);
@@ -10636,10 +10809,10 @@ function parseJobFile(raw, slug) {
10636
10809
  const rest = trimmed.slice(firstLine2.length).replace(/^\n+/, "");
10637
10810
  return { title: h1[1].trim(), body: rest };
10638
10811
  }
10639
- return { title: humanizeSlug3(slug), body: trimmed };
10812
+ return { title: humanizeSlug3(slug2), body: trimmed };
10640
10813
  }
10641
- function humanizeSlug3(slug) {
10642
- return slug.split(/[-_]+/).filter((s) => s.length > 0).map((s) => s[0].toUpperCase() + s.slice(1)).join(" ");
10814
+ function humanizeSlug3(slug2) {
10815
+ return slug2.split(/[-_]+/).filter((s) => s.length > 0).map((s) => s[0].toUpperCase() + s.slice(1)).join(" ");
10643
10816
  }
10644
10817
  var AGENT_RESPONSIBILITY_TOOL_PALETTE2, loadJobFromFile;
10645
10818
  var init_loadJobFromFile = __esm({
@@ -10654,14 +10827,14 @@ var init_loadJobFromFile = __esm({
10654
10827
  const jobsDir = String(args?.jobsDir ?? ".kody/agent-responsibilities");
10655
10828
  const agentsDir = String(args?.agentsDir ?? ".kody/agents");
10656
10829
  const slugArg = String(args?.slugArg ?? "job");
10657
- const slug = String(ctx.args[slugArg] ?? "").trim();
10658
- if (!slug) {
10830
+ const slug2 = String(ctx.args[slugArg] ?? "").trim();
10831
+ if (!slug2) {
10659
10832
  throw new Error(`loadJobFromFile: ctx.args.${slugArg} must be a non-empty slug`);
10660
10833
  }
10661
- const agentResponsibility = resolveAgentResponsibilityFolder(slug, path33.join(ctx.cwd, jobsDir));
10834
+ const agentResponsibility = resolveAgentResponsibilityFolder(slug2, path32.join(ctx.cwd, jobsDir));
10662
10835
  if (!agentResponsibility) {
10663
10836
  throw new Error(
10664
- `loadJobFromFile: agentResponsibility folder not found or incomplete: ${path33.join(ctx.cwd, jobsDir, slug)}`
10837
+ `loadJobFromFile: agentResponsibility folder not found or incomplete: ${path32.join(ctx.cwd, jobsDir, slug2)}`
10665
10838
  );
10666
10839
  }
10667
10840
  const { title, body, config } = agentResponsibility;
@@ -10673,7 +10846,7 @@ var init_loadJobFromFile = __esm({
10673
10846
  const agentPath = resolveAgentFile(ctx.cwd, agentSlug, agentsDir);
10674
10847
  if (!fs33.existsSync(agentPath)) {
10675
10848
  throw new Error(
10676
- `loadJobFromFile: agentResponsibility '${slug}' declares agent '${agentSlug}' but ${agentPath} does not exist`
10849
+ `loadJobFromFile: agentResponsibility '${slug2}' declares agent '${agentSlug}' but ${agentPath} does not exist`
10677
10850
  );
10678
10851
  }
10679
10852
  const agentRaw = fs33.readFileSync(agentPath, "utf-8");
@@ -10682,28 +10855,28 @@ var init_loadJobFromFile = __esm({
10682
10855
  agentIdentity = parsed.body;
10683
10856
  }
10684
10857
  const backend = resolveBackend({ config: ctx.config, cwd: ctx.cwd, jobsDir });
10685
- const loaded = await backend.load(slug);
10686
- ctx.data.jobSlug = slug;
10858
+ const loaded = await backend.load(slug2);
10859
+ ctx.data.jobSlug = slug2;
10687
10860
  ctx.data.jobTitle = title;
10688
- ctx.data.jobIntent = body.replace(/\{\{\s*mentions\s*\}\}/g, mentions).replace(/\{\{\s*agentResponsibility\s*\}\}/g, slug);
10861
+ ctx.data.jobIntent = body.replace(/\{\{\s*mentions\s*\}\}/g, mentions).replace(/\{\{\s*agentResponsibility\s*\}\}/g, slug2);
10689
10862
  ctx.data.jobState = loaded;
10690
10863
  ctx.data.jobStateJson = JSON.stringify(loaded.state, null, 2);
10691
10864
  ctx.data.agentSlug = agentSlug;
10692
10865
  ctx.data.agentTitle = agentTitle;
10693
10866
  ctx.data.agentIdentity = agentIdentity;
10694
10867
  ctx.data.mentions = mentions;
10695
- ctx.data.agentResponsibilitySlug = slug;
10868
+ ctx.data.agentResponsibilitySlug = slug2;
10696
10869
  ctx.data.agentResponsibilityTitle = title;
10697
10870
  ctx.data.agentSlug = agentSlug;
10698
10871
  ctx.data.agentTitle = agentTitle;
10699
10872
  ctx.data.agentActionSlug = profile.name;
10700
- ctx.data.agentResponsibilitySchedule = config.every ?? "";
10873
+ ctx.data.agentResponsibilitySchedule = String(ctx.data.jobSchedule ?? "");
10701
10874
  const declaredTools = config.tools ?? [];
10702
10875
  if (declaredTools.length > 0) {
10703
10876
  const unknown = declaredTools.filter((name) => !AGENT_RESPONSIBILITY_TOOL_PALETTE2.has(name));
10704
10877
  if (unknown.length > 0) {
10705
10878
  throw new Error(
10706
- `loadJobFromFile: agentResponsibility '${slug}' declared tools not in the kody-agentResponsibility palette: ${unknown.join(", ")}. Available: ${[...AGENT_RESPONSIBILITY_MCP_TOOL_NAMES].join(", ")}`
10879
+ `loadJobFromFile: agentResponsibility '${slug2}' declared tools not in the kody-agentResponsibility palette: ${unknown.join(", ")}. Available: ${[...AGENT_RESPONSIBILITY_MCP_TOOL_NAMES].join(", ")}`
10707
10880
  );
10708
10881
  }
10709
10882
  const mcpToolNames = declaredTools.map((name) => `mcp__kody-agent-responsibility__${name}`);
@@ -10751,9 +10924,9 @@ ${truncate(issue.body, FINDING_BODY_MAX_BYTES)}`;
10751
10924
 
10752
10925
  // src/scripts/kodyVariables.ts
10753
10926
  import * as fs34 from "fs";
10754
- import * as path34 from "path";
10927
+ import * as path33 from "path";
10755
10928
  function readKodyVariables(cwd) {
10756
- const full = path34.join(cwd, KODY_VARIABLES_REL_PATH);
10929
+ const full = path33.join(cwd, KODY_VARIABLES_REL_PATH);
10757
10930
  let raw;
10758
10931
  try {
10759
10932
  raw = fs34.readFileSync(full, "utf-8");
@@ -10782,7 +10955,7 @@ var init_kodyVariables = __esm({
10782
10955
 
10783
10956
  // src/scripts/loadQaContext.ts
10784
10957
  import * as fs35 from "fs";
10785
- import * as path35 from "path";
10958
+ import * as path34 from "path";
10786
10959
  function parseSlugList(value) {
10787
10960
  const inner = value.startsWith("[") && value.endsWith("]") ? value.slice(1, -1) : value;
10788
10961
  return inner.split(",").map(
@@ -10811,7 +10984,7 @@ function readProfileAgents(raw) {
10811
10984
  return { agent: agent ?? legacy ?? ["kody"], body };
10812
10985
  }
10813
10986
  function readProfile(cwd) {
10814
- const dir = path35.join(cwd, CONTEXT_DIR_REL_PATH);
10987
+ const dir = path34.join(cwd, CONTEXT_DIR_REL_PATH);
10815
10988
  if (!fs35.existsSync(dir)) return "";
10816
10989
  let entries;
10817
10990
  try {
@@ -10822,7 +10995,7 @@ function readProfile(cwd) {
10822
10995
  const blocks = [];
10823
10996
  for (const file of entries) {
10824
10997
  try {
10825
- const raw = fs35.readFileSync(path35.join(dir, file), "utf-8");
10998
+ const raw = fs35.readFileSync(path34.join(dir, file), "utf-8");
10826
10999
  const { agent, body } = readProfileAgents(raw);
10827
11000
  if (!agent.includes(QA_AGENT) && !agent.includes(ALL_AGENTS)) continue;
10828
11001
  blocks.push(`## ${file}
@@ -10869,7 +11042,7 @@ var init_loadQaContext = __esm({
10869
11042
 
10870
11043
  // src/taskContext.ts
10871
11044
  import * as fs36 from "fs";
10872
- import * as path36 from "path";
11045
+ import * as path35 from "path";
10873
11046
  function buildTaskContext(args) {
10874
11047
  return {
10875
11048
  schemaVersion: TASK_CONTEXT_SCHEMA_VERSION,
@@ -10886,7 +11059,7 @@ function persistTaskContext(cwd, ctx) {
10886
11059
  try {
10887
11060
  const dir = runtimeStatePath(cwd, "agent-runs", ctx.runId);
10888
11061
  fs36.mkdirSync(dir, { recursive: true });
10889
- const file = path36.join(dir, "task-context.json");
11062
+ const file = path35.join(dir, "task-context.json");
10890
11063
  fs36.writeFileSync(file, `${JSON.stringify(ctx, null, 2)}
10891
11064
  `);
10892
11065
  return file;
@@ -11303,8 +11476,8 @@ function parseAgentFactoryBundle(raw) {
11303
11476
  return { title, summary, files };
11304
11477
  }
11305
11478
  function buildAgentFactoryBranchName(issueNumber, title, now = Date.now()) {
11306
- const slug = title.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 48).replace(/-+$/g, "");
11307
- const suffix = slug ? `-${slug}` : "";
11479
+ const slug2 = title.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 48).replace(/-+$/g, "");
11480
+ const suffix = slug2 ? `-${slug2}` : "";
11308
11481
  return `agent-factory/issue-${issueNumber}-${now.toString(36)}${suffix}`;
11309
11482
  }
11310
11483
  function normalizeBundleFiles(ctx, bundle) {
@@ -11537,21 +11710,21 @@ var init_openQaIssue = __esm({
11537
11710
  ctx.data.action = failedAction3(reason);
11538
11711
  return;
11539
11712
  }
11540
- const reportBody = agentResult.finalText.trim();
11541
- if (!reportBody) {
11713
+ const reportBody2 = agentResult.finalText.trim();
11714
+ if (!reportBody2) {
11542
11715
  process.stderr.write("qa-engineer: agent produced no report body\n");
11543
11716
  ctx.output.exitCode = 1;
11544
11717
  ctx.output.reason = "empty report body";
11545
11718
  ctx.data.action = failedAction3("empty report body");
11546
11719
  return;
11547
11720
  }
11548
- const verdict = detectVerdict(reportBody);
11721
+ const verdict = detectVerdict(reportBody2);
11549
11722
  ctx.data.qaVerdict = verdict;
11550
- ctx.data.qaReport = reportBody;
11723
+ ctx.data.qaReport = reportBody2;
11551
11724
  const existingIssue = ctx.args.issue;
11552
11725
  if (typeof existingIssue === "number" && Number.isFinite(existingIssue) && existingIssue > 0) {
11553
11726
  try {
11554
- postIssueComment(existingIssue, reportBody, ctx.cwd);
11727
+ postIssueComment(existingIssue, reportBody2, ctx.cwd);
11555
11728
  } catch (err) {
11556
11729
  const msg = err instanceof Error ? err.message : String(err);
11557
11730
  ctx.output.exitCode = 4;
@@ -11573,7 +11746,7 @@ QA_REPORT_POSTED=https://github.com/${ctx.config.github.owner}/${ctx.config.gith
11573
11746
  const hasLabel = ensureLabel2(ctx.cwd);
11574
11747
  let created;
11575
11748
  try {
11576
- created = createQaIssue(title, reportBody, hasLabel, ctx.cwd);
11749
+ created = createQaIssue(title, reportBody2, hasLabel, ctx.cwd);
11577
11750
  } catch (err) {
11578
11751
  const msg = err instanceof Error ? err.message : String(err);
11579
11752
  ctx.output.exitCode = 4;
@@ -11636,6 +11809,39 @@ var init_parseAgentResult = __esm({
11636
11809
  }
11637
11810
  });
11638
11811
 
11812
+ // src/scripts/parseCompanyManagerDecision.ts
11813
+ function makeAction3(type, payload) {
11814
+ return { type, payload, timestamp: (/* @__PURE__ */ new Date()).toISOString() };
11815
+ }
11816
+ var parseCompanyManagerDecision;
11817
+ var init_parseCompanyManagerDecision = __esm({
11818
+ "src/scripts/parseCompanyManagerDecision.ts"() {
11819
+ "use strict";
11820
+ init_companyManagerDecision();
11821
+ parseCompanyManagerDecision = async (ctx, _profile, agentResult) => {
11822
+ if (!agentResult) {
11823
+ ctx.data.companyManagerDecision = { summary: "", actions: [] };
11824
+ ctx.data.action = makeAction3("COMPANY_MANAGER_NOT_RUN", { reason: "no agent result" });
11825
+ return;
11826
+ }
11827
+ try {
11828
+ const decision = parseCompanyManagerDecisionText(agentResult.finalText);
11829
+ ctx.data.companyManagerDecision = decision;
11830
+ ctx.data.action = makeAction3("COMPANY_MANAGER_DECIDED", {
11831
+ summary: decision.summary,
11832
+ actionCount: decision.actions.length
11833
+ });
11834
+ } catch (err) {
11835
+ const reason = err instanceof Error ? err.message : String(err);
11836
+ ctx.data.companyManagerDecisionError = reason;
11837
+ ctx.data.action = makeAction3("COMPANY_MANAGER_FAILED", { reason });
11838
+ ctx.output.exitCode = 1;
11839
+ ctx.output.reason = reason;
11840
+ }
11841
+ };
11842
+ }
11843
+ });
11844
+
11639
11845
  // src/scripts/stateEnvelope.ts
11640
11846
  function isPartialEnvelope(x) {
11641
11847
  if (x === null || typeof x !== "object") return false;
@@ -11896,6 +12102,119 @@ var init_persistFlowState = __esm({
11896
12102
  }
11897
12103
  });
11898
12104
 
12105
+ // src/scripts/planTaskJobs.ts
12106
+ function parseTaskJobSpecs(body) {
12107
+ const match = body.match(new RegExp(`<!--\\s*${TASK_JOBS_MARKER}\\s*([\\s\\S]*?)-->`));
12108
+ if (!match) return [];
12109
+ let parsed;
12110
+ try {
12111
+ parsed = JSON.parse(match[1].trim());
12112
+ } catch (err) {
12113
+ throw new Error(`task job plan is not valid JSON: ${err instanceof Error ? err.message : String(err)}`);
12114
+ }
12115
+ if (!Array.isArray(parsed)) throw new Error("task job plan must be a JSON array");
12116
+ return parsed.map((entry, index) => normalizeSpec(entry, index));
12117
+ }
12118
+ function taskJobSpecToJob(spec, issueNumber) {
12119
+ const cliArgs = spec.cliArgs ?? { issue: issueNumber };
12120
+ const target = typeof spec.target === "number" ? spec.target : targetFromCliArgs(cliArgs) ?? issueNumber;
12121
+ return {
12122
+ agentResponsibility: spec.agentResponsibility ?? spec.agentAction,
12123
+ agentAction: spec.agentAction,
12124
+ why: spec.reason,
12125
+ agent: spec.agent,
12126
+ schedule: spec.schedule,
12127
+ target,
12128
+ cliArgs,
12129
+ flavor: spec.flavor ?? "instant"
12130
+ };
12131
+ }
12132
+ function normalizeSpec(input, index) {
12133
+ if (!input || typeof input !== "object" || Array.isArray(input)) {
12134
+ throw new Error(`task job plan entry ${index} must be an object`);
12135
+ }
12136
+ const raw = input;
12137
+ const agentAction = typeof raw.agentAction === "string" ? raw.agentAction.trim() : "";
12138
+ if (!/^[a-z][a-z0-9-]*$/.test(agentAction)) {
12139
+ throw new Error(`task job plan entry ${index} must have a valid agentAction`);
12140
+ }
12141
+ const cliArgs = raw.cliArgs;
12142
+ if (cliArgs !== void 0 && (!cliArgs || typeof cliArgs !== "object" || Array.isArray(cliArgs))) {
12143
+ throw new Error(`task job plan entry ${index} cliArgs must be an object`);
12144
+ }
12145
+ const flavor = raw.flavor;
12146
+ if (flavor !== void 0 && flavor !== "instant" && flavor !== "scheduled") {
12147
+ throw new Error(`task job plan entry ${index} flavor must be "instant" or "scheduled"`);
12148
+ }
12149
+ return {
12150
+ agentAction,
12151
+ ...typeof raw.agentResponsibility === "string" && raw.agentResponsibility.trim() ? { agentResponsibility: raw.agentResponsibility.trim() } : {},
12152
+ ...typeof raw.reason === "string" && raw.reason.trim() ? { reason: raw.reason.trim() } : {},
12153
+ ...typeof raw.agent === "string" && raw.agent.trim() ? { agent: raw.agent.trim() } : {},
12154
+ ...typeof raw.agent === "string" && raw.agent.trim() ? { agent: raw.agent.trim() } : {},
12155
+ ...cliArgs ? { cliArgs } : {},
12156
+ ...typeof raw.target === "number" && Number.isFinite(raw.target) ? { target: raw.target } : {},
12157
+ ...flavor === "instant" || flavor === "scheduled" ? { flavor } : {},
12158
+ ...typeof raw.schedule === "string" && raw.schedule.trim() ? { schedule: raw.schedule.trim() } : {}
12159
+ };
12160
+ }
12161
+ function jobToPlannedTaskJob(job) {
12162
+ return {
12163
+ id: stableJobKey(job),
12164
+ agentAction: job.agentAction ?? job.agentResponsibility ?? "unknown",
12165
+ agentResponsibility: job.agentResponsibility ?? job.action ?? job.agentAction ?? "unknown",
12166
+ ...job.agent ? { agent: job.agent } : {},
12167
+ ...job.flavor ? { flavor: job.flavor } : {},
12168
+ ...job.schedule ? { schedule: job.schedule } : {},
12169
+ ...typeof job.target === "number" ? { target: job.target } : {},
12170
+ ...job.why ? { reason: job.why } : {}
12171
+ };
12172
+ }
12173
+ function assertUniqueJobIds(planned) {
12174
+ const seen = /* @__PURE__ */ new Set();
12175
+ for (const job of planned) {
12176
+ if (seen.has(job.id)) throw new Error(`duplicate planned task job id: ${job.id}`);
12177
+ seen.add(job.id);
12178
+ }
12179
+ }
12180
+ var TASK_JOBS_MARKER, planTaskJobs;
12181
+ var init_planTaskJobs = __esm({
12182
+ "src/scripts/planTaskJobs.ts"() {
12183
+ "use strict";
12184
+ init_jobIdentity();
12185
+ init_state();
12186
+ TASK_JOBS_MARKER = "kody:task-jobs:v1";
12187
+ planTaskJobs = async (ctx) => {
12188
+ const issueNumber = ctx.args.issue;
12189
+ if (!issueNumber) {
12190
+ ctx.skipAgent = true;
12191
+ ctx.output.exitCode = 64;
12192
+ ctx.output.reason = "planTaskJobs requires --issue";
12193
+ return;
12194
+ }
12195
+ const issue = ctx.data.issue;
12196
+ const specs = parseTaskJobSpecs(issue?.body ?? "");
12197
+ if (specs.length === 0) {
12198
+ ctx.skipAgent = true;
12199
+ ctx.output.exitCode = 64;
12200
+ ctx.output.reason = `no ${TASK_JOBS_MARKER} block found on issue #${issueNumber}`;
12201
+ return;
12202
+ }
12203
+ const jobs = specs.map((spec) => taskJobSpecToJob(spec, issueNumber));
12204
+ const planned = jobs.map(jobToPlannedTaskJob);
12205
+ assertUniqueJobIds(planned);
12206
+ const prior = ctx.data.taskState ?? emptyState();
12207
+ const next = upsertTaskJobs(prior, planned, (/* @__PURE__ */ new Date()).toISOString());
12208
+ ctx.data.taskState = next;
12209
+ ctx.data.plannedTaskJobs = jobs;
12210
+ ctx.data.plannedTaskJobIds = planned.map((job) => job.id);
12211
+ const target = ctx.data.commentTargetType;
12212
+ const number = ctx.data.commentTargetNumber;
12213
+ if (target && number) writeTaskState(target, number, next, ctx.cwd, ctx.config);
12214
+ };
12215
+ }
12216
+ });
12217
+
11899
12218
  // src/scripts/postAgentSummaryComment.ts
11900
12219
  function postAgentSummaryComment(ctx, opts = {}) {
11901
12220
  if (!ctx.data.agentDone) return;
@@ -12195,7 +12514,7 @@ function tryAuditComment(issueNumber, body, cwd) {
12195
12514
  } catch {
12196
12515
  }
12197
12516
  }
12198
- function makeAction3(type, payload) {
12517
+ function makeAction4(type, payload) {
12199
12518
  return { type, payload, timestamp: (/* @__PURE__ */ new Date()).toISOString() };
12200
12519
  }
12201
12520
  function failedAction4(reason) {
@@ -12232,7 +12551,7 @@ var init_recordClassification = __esm({
12232
12551
  ctx.output.reason = "classify: no decision";
12233
12552
  return;
12234
12553
  }
12235
- ctx.data.action = makeAction3(`CLASSIFIED_AS_${classification.toUpperCase()}`, {
12554
+ ctx.data.action = makeAction4(`CLASSIFIED_AS_${classification.toUpperCase()}`, {
12236
12555
  classification,
12237
12556
  reason: reason ?? "",
12238
12557
  source: ctx.data.classificationSource ?? "agent"
@@ -13075,12 +13394,12 @@ fi
13075
13394
 
13076
13395
  // src/scripts/runPreviewBuild.ts
13077
13396
  import { copyFile, writeFile } from "fs/promises";
13078
- import * as path37 from "path";
13397
+ import * as path36 from "path";
13079
13398
  import { fileURLToPath } from "url";
13080
13399
  function bundledDockerfilePath(mode) {
13081
- const here = path37.dirname(fileURLToPath(import.meta.url));
13400
+ const here = path36.dirname(fileURLToPath(import.meta.url));
13082
13401
  const file = mode === "dev" ? "default-Dockerfile.preview.dev" : "default-Dockerfile.preview.prod";
13083
- return path37.join(here, "preview-build-templates", file);
13402
+ return path36.join(here, "preview-build-templates", file);
13084
13403
  }
13085
13404
  function required(name) {
13086
13405
  const v = (process.env[name] ?? "").trim();
@@ -13339,10 +13658,10 @@ var init_runPreviewBuild = __esm({
13339
13658
  console.log(`[preview-build] vault: ${Object.keys(buildEnv).length} secrets, mode=${buildMode}`);
13340
13659
  if (Object.keys(buildEnv).length > 0) {
13341
13660
  const lines = Object.entries(buildEnv).map(([k, v]) => `${k}=${JSON.stringify(v)}`);
13342
- await writeFile(path37.join(ctx.cwd, ".env.production.local"), `${lines.join("\n")}
13661
+ await writeFile(path36.join(ctx.cwd, ".env.production.local"), `${lines.join("\n")}
13343
13662
  `, "utf8");
13344
13663
  }
13345
- const consumerDockerfile = path37.join(ctx.cwd, "Dockerfile.preview");
13664
+ const consumerDockerfile = path36.join(ctx.cwd, "Dockerfile.preview");
13346
13665
  const { stat } = await import("fs/promises");
13347
13666
  let hasConsumerDockerfile = false;
13348
13667
  try {
@@ -13527,7 +13846,7 @@ var init_tickShellRunner = __esm({
13527
13846
 
13528
13847
  // src/scripts/runScheduledAgentActionTick.ts
13529
13848
  import * as fs37 from "fs";
13530
- import * as path38 from "path";
13849
+ import * as path37 from "path";
13531
13850
  var runScheduledAgentActionTick;
13532
13851
  var init_runScheduledAgentActionTick = __esm({
13533
13852
  "src/scripts/runScheduledAgentActionTick.ts"() {
@@ -13541,19 +13860,19 @@ var init_runScheduledAgentActionTick = __esm({
13541
13860
  const slugArg = String(args?.slugArg ?? "agentResponsibility");
13542
13861
  const fenceLabel = String(args?.fenceLabel ?? "kody-job-next-state");
13543
13862
  const shell = String(args?.shell ?? "tick.sh");
13544
- const slug = String(args?.slug ?? ctx.args[slugArg] ?? "").trim();
13545
- if (!slug) {
13863
+ const slug2 = String(args?.slug ?? ctx.args[slugArg] ?? "").trim();
13864
+ if (!slug2) {
13546
13865
  ctx.output.exitCode = 99;
13547
13866
  ctx.output.reason = `runScheduledAgentActionTick: args.slug or ctx.args.${slugArg} must be non-empty agentResponsibility slug`;
13548
13867
  return;
13549
13868
  }
13550
- const agentResponsibility = resolveAgentResponsibilityFolder(slug, path38.join(ctx.cwd, jobsDir));
13869
+ const agentResponsibility = resolveAgentResponsibilityFolder(slug2, path37.join(ctx.cwd, jobsDir));
13551
13870
  if (!agentResponsibility) {
13552
13871
  ctx.output.exitCode = 99;
13553
- ctx.output.reason = `runScheduledAgentActionTick: agentResponsibility folder not found or incomplete: ${slug} (searched ${jobsDir} and company store)`;
13872
+ ctx.output.reason = `runScheduledAgentActionTick: agentResponsibility folder not found or incomplete: ${slug2} (searched ${jobsDir} and company store)`;
13554
13873
  return;
13555
13874
  }
13556
- const shellPath = path38.join(profile.dir, shell);
13875
+ const shellPath = path37.join(profile.dir, shell);
13557
13876
  if (!fs37.existsSync(shellPath)) {
13558
13877
  ctx.output.exitCode = 99;
13559
13878
  ctx.output.reason = `runScheduledAgentActionTick: shell not found: ${shell} (looked in ${profile.dir})`;
@@ -13562,14 +13881,14 @@ var init_runScheduledAgentActionTick = __esm({
13562
13881
  const backend = resolveBackend({ config: ctx.config, cwd: ctx.cwd, jobsDir });
13563
13882
  let loaded;
13564
13883
  try {
13565
- loaded = await backend.load(slug);
13884
+ loaded = await backend.load(slug2);
13566
13885
  } catch (err) {
13567
13886
  ctx.output.exitCode = 99;
13568
13887
  ctx.output.reason = `runScheduledAgentActionTick: state load failed: ${err instanceof Error ? err.message : String(err)}`;
13569
13888
  return;
13570
13889
  }
13571
- ctx.data.jobSlug = slug;
13572
- ctx.data.agentResponsibilitySlug = slug;
13890
+ ctx.data.jobSlug = slug2;
13891
+ ctx.data.agentResponsibilitySlug = slug2;
13573
13892
  ctx.data.agentActionSlug = profile.name;
13574
13893
  ctx.data.jobState = loaded;
13575
13894
  runTickShellAndParse({
@@ -13586,7 +13905,7 @@ var init_runScheduledAgentActionTick = __esm({
13586
13905
 
13587
13906
  // src/scripts/runTickScript.ts
13588
13907
  import * as fs38 from "fs";
13589
- import * as path39 from "path";
13908
+ import * as path38 from "path";
13590
13909
  var runTickScript;
13591
13910
  var init_runTickScript = __esm({
13592
13911
  "src/scripts/runTickScript.ts"() {
@@ -13599,25 +13918,25 @@ var init_runTickScript = __esm({
13599
13918
  const jobsDir = String(args?.jobsDir ?? ".kody/agent-responsibilities");
13600
13919
  const slugArg = String(args?.slugArg ?? "job");
13601
13920
  const fenceLabel = String(args?.fenceLabel ?? "kody-job-next-state");
13602
- const slug = String(ctx.args[slugArg] ?? "").trim();
13603
- if (!slug) {
13921
+ const slug2 = String(ctx.args[slugArg] ?? "").trim();
13922
+ if (!slug2) {
13604
13923
  ctx.output.exitCode = 99;
13605
13924
  ctx.output.reason = `runTickScript: ctx.args.${slugArg} must be a non-empty slug`;
13606
13925
  return;
13607
13926
  }
13608
- const agentResponsibility = readAgentResponsibilityFolder(path39.join(ctx.cwd, jobsDir), slug);
13927
+ const agentResponsibility = readAgentResponsibilityFolder(path38.join(ctx.cwd, jobsDir), slug2);
13609
13928
  if (!agentResponsibility) {
13610
13929
  ctx.output.exitCode = 99;
13611
- ctx.output.reason = `runTickScript: agentResponsibility folder not found or incomplete: ${path39.join(ctx.cwd, jobsDir, slug)}`;
13930
+ ctx.output.reason = `runTickScript: agentResponsibility folder not found or incomplete: ${path38.join(ctx.cwd, jobsDir, slug2)}`;
13612
13931
  return;
13613
13932
  }
13614
13933
  const tickScript = agentResponsibility.config.tickScript;
13615
13934
  if (!tickScript) {
13616
13935
  ctx.output.exitCode = 99;
13617
- ctx.output.reason = `runTickScript: agentResponsibility ${slug} has no \`tickScript\` in profile.json \u2014 route via agent-responsibility-tick instead`;
13936
+ ctx.output.reason = `runTickScript: agentResponsibility ${slug2} has no \`tickScript\` in profile.json \u2014 route via agent-responsibility-tick instead`;
13618
13937
  return;
13619
13938
  }
13620
- const scriptPath = path39.isAbsolute(tickScript) ? tickScript : path39.join(ctx.cwd, tickScript);
13939
+ const scriptPath = path38.isAbsolute(tickScript) ? tickScript : path38.join(ctx.cwd, tickScript);
13621
13940
  if (!fs38.existsSync(scriptPath)) {
13622
13941
  ctx.output.exitCode = 99;
13623
13942
  ctx.output.reason = `runTickScript: tickScript not found: ${scriptPath}`;
@@ -13626,13 +13945,13 @@ var init_runTickScript = __esm({
13626
13945
  const backend = resolveBackend({ config: ctx.config, cwd: ctx.cwd, jobsDir });
13627
13946
  let loaded;
13628
13947
  try {
13629
- loaded = await backend.load(slug);
13948
+ loaded = await backend.load(slug2);
13630
13949
  } catch (err) {
13631
13950
  ctx.output.exitCode = 99;
13632
13951
  ctx.output.reason = `runTickScript: state load failed: ${err instanceof Error ? err.message : String(err)}`;
13633
13952
  return;
13634
13953
  }
13635
- ctx.data.jobSlug = slug;
13954
+ ctx.data.jobSlug = slug2;
13636
13955
  ctx.data.jobState = loaded;
13637
13956
  runTickShellAndParse({
13638
13957
  ctx,
@@ -14577,6 +14896,86 @@ var init_writeJobStateFile = __esm({
14577
14896
  }
14578
14897
  });
14579
14898
 
14899
+ // src/scripts/writeResponsibilityReport.ts
14900
+ function writeReport(ctx, profile, agentResult) {
14901
+ const slug2 = responsibilitySlug(ctx);
14902
+ if (!slug2) {
14903
+ fail2(ctx, "writeResponsibilityReport: missing responsibility slug");
14904
+ return;
14905
+ }
14906
+ if (!REPORT_SLUG_RE.test(slug2)) {
14907
+ fail2(ctx, `writeResponsibilityReport: invalid responsibility slug "${slug2}"`);
14908
+ return;
14909
+ }
14910
+ const body = reportBody(ctx, agentResult, profile);
14911
+ if (!body.trim()) {
14912
+ fail2(ctx, `writeResponsibilityReport: ${slug2} produced no report output`);
14913
+ return;
14914
+ }
14915
+ const filePath = `reports/${slug2}.md`;
14916
+ const current = readStateText(ctx.config, ctx.cwd, filePath);
14917
+ if (current?.content === body) {
14918
+ ctx.data.responsibilityReport = { slug: slug2, path: current.path, changed: false };
14919
+ return;
14920
+ }
14921
+ upsertStateText(
14922
+ ctx.config,
14923
+ ctx.cwd,
14924
+ filePath,
14925
+ body,
14926
+ `chore(reports): refresh ${slug2}`
14927
+ );
14928
+ ctx.data.responsibilityReport = { slug: slug2, path: filePath, changed: true };
14929
+ }
14930
+ function responsibilitySlug(ctx) {
14931
+ const candidates = [
14932
+ ctx.data.jobAgentResponsibility,
14933
+ ctx.data.agentResponsibilitySlug,
14934
+ ctx.data.jobSlug
14935
+ ];
14936
+ for (const candidate of candidates) {
14937
+ if (typeof candidate === "string" && candidate.trim()) return candidate.trim();
14938
+ }
14939
+ return null;
14940
+ }
14941
+ function reportBody(ctx, agentResult, profile) {
14942
+ const prSummary = ctx.data.prSummary;
14943
+ if (typeof prSummary === "string" && prSummary.trim()) return ensureTrailingNewline(prSummary.trim());
14944
+ if (agentResult?.finalText.trim()) return ensureTrailingNewline(agentResult.finalText.trim());
14945
+ const reason = ctx.output.reason || ctx.data.agentFailureReason || agentResult?.error;
14946
+ if (typeof reason === "string" && reason.trim()) {
14947
+ return `# ${profile.name}
14948
+
14949
+ FAILED: ${reason.trim()}
14950
+ `;
14951
+ }
14952
+ return "";
14953
+ }
14954
+ function fail2(ctx, reason) {
14955
+ ctx.output.reason = ctx.output.reason ? `${ctx.output.reason}; ${reason}` : reason;
14956
+ if (ctx.output.exitCode === 0) ctx.output.exitCode = 99;
14957
+ }
14958
+ function ensureTrailingNewline(value) {
14959
+ return value.endsWith("\n") ? value : `${value}
14960
+ `;
14961
+ }
14962
+ var REPORT_SLUG_RE, writeResponsibilityReport;
14963
+ var init_writeResponsibilityReport = __esm({
14964
+ "src/scripts/writeResponsibilityReport.ts"() {
14965
+ "use strict";
14966
+ init_stateRepo();
14967
+ REPORT_SLUG_RE = /^[a-z0-9][a-z0-9_-]{0,63}$/;
14968
+ writeResponsibilityReport = async (ctx, profile, agentResult) => {
14969
+ if (ctx.data.jobSaveReport !== true) return;
14970
+ try {
14971
+ writeReport(ctx, profile, agentResult);
14972
+ } catch (err) {
14973
+ fail2(ctx, `writeResponsibilityReport: ${err instanceof Error ? err.message : String(err)}`);
14974
+ }
14975
+ };
14976
+ }
14977
+ });
14978
+
14580
14979
  // src/scripts/index.ts
14581
14980
  var preflightScripts, postflightScripts, allScriptNames;
14582
14981
  var init_scripts = __esm({
@@ -14586,7 +14985,9 @@ var init_scripts = __esm({
14586
14985
  init_advanceFlow();
14587
14986
  init_advanceManagedGoal();
14588
14987
  init_appendCompanyActivity();
14988
+ init_appendCompanyIntentDecision();
14589
14989
  init_applyAgentResponsibilityReports();
14990
+ init_applyCompanyManagerDecision();
14590
14991
  init_buildSyntheticPlugin();
14591
14992
  init_checkCoverageWithRetry();
14592
14993
  init_classifyByLabel();
@@ -14611,6 +15012,8 @@ var init_scripts = __esm({
14611
15012
  init_initFlow();
14612
15013
  init_loadAgentAdhoc();
14613
15014
  init_loadAgentResponsibilityState();
15015
+ init_loadCompanyIntents();
15016
+ init_loadCompanyPortfolio();
14614
15017
  init_loadConventions();
14615
15018
  init_loadCoverageRules();
14616
15019
  init_loadGoalState();
@@ -14631,6 +15034,7 @@ var init_scripts = __esm({
14631
15034
  init_openAgentFactoryStatePr();
14632
15035
  init_openQaIssue();
14633
15036
  init_parseAgentResult();
15037
+ init_parseCompanyManagerDecision();
14634
15038
  init_parseIssueStateFromAgentResult();
14635
15039
  init_parseJobStateFromAgentResult();
14636
15040
  init_parseReproOutput();
@@ -14673,6 +15077,7 @@ var init_scripts = __esm({
14673
15077
  init_writeAgentRunSummary();
14674
15078
  init_writeIssueStateComment();
14675
15079
  init_writeJobStateFile();
15080
+ init_writeResponsibilityReport();
14676
15081
  preflightScripts = {
14677
15082
  runFlow,
14678
15083
  fixFlow,
@@ -14689,6 +15094,8 @@ var init_scripts = __esm({
14689
15094
  loadIssueStateComment,
14690
15095
  loadJobFromFile,
14691
15096
  loadAgentResponsibilityState,
15097
+ loadCompanyIntents,
15098
+ loadCompanyPortfolio,
14692
15099
  loadAgentAdhoc,
14693
15100
  loadConventions,
14694
15101
  loadCoverageRules,
@@ -14723,12 +15130,15 @@ var init_scripts = __esm({
14723
15130
  };
14724
15131
  postflightScripts = {
14725
15132
  parseAgentResult: parseAgentResult2,
15133
+ parseCompanyManagerDecision,
14726
15134
  parseIssueStateFromAgentResult,
14727
15135
  parseJobStateFromAgentResult,
14728
15136
  parseReproOutput,
14729
15137
  writeIssueStateComment,
14730
15138
  writeJobStateFile,
14731
15139
  appendCompanyActivity,
15140
+ appendCompanyIntentDecision: appendCompanyIntentDecision2,
15141
+ applyCompanyManagerDecision,
14732
15142
  requireFeedbackActions,
14733
15143
  requirePlanDeviations,
14734
15144
  verify,
@@ -14746,6 +15156,7 @@ var init_scripts = __esm({
14746
15156
  postReviewResult,
14747
15157
  persistArtifacts,
14748
15158
  writeAgentRunSummary,
15159
+ writeResponsibilityReport,
14749
15160
  saveTaskState,
14750
15161
  mirrorStateToPr,
14751
15162
  startFlow,
@@ -14842,7 +15253,7 @@ var init_tools = __esm({
14842
15253
  // src/executor.ts
14843
15254
  import { spawn as spawn7 } from "child_process";
14844
15255
  import * as fs40 from "fs";
14845
- import * as path40 from "path";
15256
+ import * as path39 from "path";
14846
15257
  function isMutatingPostflight(scriptName) {
14847
15258
  return MUTATING_POSTFLIGHTS.has(scriptName ?? "");
14848
15259
  }
@@ -15007,13 +15418,13 @@ async function runAgentAction(profileName, input) {
15007
15418
  })
15008
15419
  };
15009
15420
  })() : null;
15010
- const ndjsonDir = path40.join(input.cwd, ".kody");
15421
+ const ndjsonDir = path39.join(input.cwd, ".kody");
15011
15422
  const agentSlug = typeof profile.agent === "string" && profile.agent.length > 0 ? profile.agent : typeof ctx.data.jobAgent === "string" && ctx.data.jobAgent.length > 0 ? ctx.data.jobAgent : null;
15012
15423
  const agentIdentityBlock = agentSlug ? frameAgentIdentity(agentSlug, loadAgentIdentity(input.cwd, agentSlug)) : null;
15013
15424
  const jobWhyBlock = typeof ctx.data.jobWhy === "string" ? operatorRequestBlock(ctx.data.jobWhy) : null;
15014
15425
  const jobRefBlock = jobReferenceBlock(profileName, profile, ctx.data);
15015
15426
  const invokeAgent = async (prompt) => {
15016
- const externalPlugins = (profile.claudeCode.plugins ?? []).map((p) => path40.isAbsolute(p) ? p : path40.resolve(profile.dir, p)).filter((p) => p.length > 0);
15427
+ const externalPlugins = (profile.claudeCode.plugins ?? []).map((p) => path39.isAbsolute(p) ? p : path39.resolve(profile.dir, p)).filter((p) => p.length > 0);
15017
15428
  const syntheticPath = ctx.data.syntheticPluginPath;
15018
15429
  const pluginPaths = [...externalPlugins, ...syntheticPath ? [syntheticPath] : []];
15019
15430
  const agents = loadSubagents(profile);
@@ -15248,6 +15659,7 @@ async function runAgentAction(profileName, input) {
15248
15659
  outcome: postOutcome
15249
15660
  });
15250
15661
  }
15662
+ await writeResponsibilityReport(ctx, profile, agentResult);
15251
15663
  return finishAndEnd({
15252
15664
  exitCode: ctx.output.exitCode ?? 0,
15253
15665
  prUrl: ctx.output.prUrl,
@@ -15376,7 +15788,8 @@ function handoffToJob(handoff) {
15376
15788
  agentResponsibility: handoff.agentResponsibility,
15377
15789
  agentAction: handoff.agentAction,
15378
15790
  cliArgs: handoff.cliArgs,
15379
- flavor: "instant"
15791
+ flavor: "instant",
15792
+ saveReport: handoff.saveReport === true
15380
15793
  };
15381
15794
  }
15382
15795
  function clearStampedLifecycleLabels(profile, ctx) {
@@ -15395,13 +15808,13 @@ function clearStampedLifecycleLabels(profile, ctx) {
15395
15808
  function resolveProfilePath(profileName) {
15396
15809
  const found = resolveAgentAction(profileName);
15397
15810
  if (found) return found;
15398
- const here = path40.dirname(new URL(import.meta.url).pathname);
15811
+ const here = path39.dirname(new URL(import.meta.url).pathname);
15399
15812
  const candidates = [
15400
- path40.join(here, "agent-actions", profileName, "profile.json"),
15813
+ path39.join(here, "agent-actions", profileName, "profile.json"),
15401
15814
  // same-dir sibling (dev)
15402
- path40.join(here, "..", "agent-actions", profileName, "profile.json"),
15815
+ path39.join(here, "..", "agent-actions", profileName, "profile.json"),
15403
15816
  // up one (prod: dist/bin → dist/agent-actions)
15404
- path40.join(here, "..", "src", "agent-actions", profileName, "profile.json")
15817
+ path39.join(here, "..", "src", "agent-actions", profileName, "profile.json")
15405
15818
  // fallback
15406
15819
  ];
15407
15820
  for (const c of candidates) {
@@ -15503,7 +15916,7 @@ function resolveShellTimeoutMs(entry) {
15503
15916
  }
15504
15917
  async function runShellEntry(entry, ctx, profile) {
15505
15918
  const shellName = entry.shell;
15506
- const shellPath = path40.join(profile.dir, shellName);
15919
+ const shellPath = path39.join(profile.dir, shellName);
15507
15920
  if (!fs40.existsSync(shellPath)) {
15508
15921
  ctx.skipAgent = true;
15509
15922
  ctx.output.exitCode = 99;
@@ -15636,6 +16049,7 @@ var init_executor = __esm({
15636
16049
  init_profile();
15637
16050
  init_registry();
15638
16051
  init_scripts();
16052
+ init_writeResponsibilityReport();
15639
16053
  init_subagents();
15640
16054
  init_task_artifacts();
15641
16055
  init_tools();
@@ -15663,7 +16077,7 @@ __export(job_exports, {
15663
16077
  stableJobKey: () => stableJobKey,
15664
16078
  validateJob: () => validateJob
15665
16079
  });
15666
- import * as path41 from "path";
16080
+ import * as path40 from "path";
15667
16081
  function newJobId(flavor) {
15668
16082
  localJobSeq += 1;
15669
16083
  const runId = process.env.GITHUB_RUN_ID;
@@ -15694,13 +16108,14 @@ function validateJob(input) {
15694
16108
  target: typeof j.target === "number" ? j.target : void 0,
15695
16109
  cliArgs: j.cliArgs ?? {},
15696
16110
  flavor: j.flavor,
15697
- force: j.force === true
16111
+ force: j.force === true,
16112
+ saveReport: j.saveReport === true
15698
16113
  };
15699
16114
  }
15700
16115
  async function runJob(job, base) {
15701
16116
  const valid = validateJob(job);
15702
16117
  const action = valid.action ?? valid.agentResponsibility;
15703
- const projectAgentResponsibilitiesRoot = path41.join(base.cwd, ".kody", "agent-responsibilities");
16118
+ const projectAgentResponsibilitiesRoot = path40.join(base.cwd, ".kody", "agent-responsibilities");
15704
16119
  const resolvedAgentResponsibility = action ? resolveAgentResponsibilityAction(action, projectAgentResponsibilitiesRoot) : null;
15705
16120
  const agentResponsibilityIdentity = valid.agentResponsibility ?? resolvedAgentResponsibility?.agentResponsibility;
15706
16121
  const agentResponsibilityContext = loadAgentResponsibilityContext(agentResponsibilityIdentity, base.cwd);
@@ -15727,6 +16142,7 @@ async function runJob(job, base) {
15727
16142
  if (executableIdentity !== void 0 && executableIdentity.length > 0)
15728
16143
  preloadedData.jobAgentAction = executableIdentity;
15729
16144
  if (valid.schedule !== void 0 && valid.schedule.length > 0) preloadedData.jobSchedule = valid.schedule;
16145
+ if (valid.saveReport === true) preloadedData.jobSaveReport = true;
15730
16146
  if (agentResponsibilityContext) {
15731
16147
  preloadedData.agentResponsibilitySlug = agentResponsibilityContext.slug;
15732
16148
  preloadedData.agentResponsibilityTitle = agentResponsibilityContext.title;
@@ -15737,9 +16153,6 @@ async function runJob(job, base) {
15737
16153
  if (agentResponsibilityContext.config.agent && preloadedData.jobAgent === void 0) {
15738
16154
  preloadedData.jobAgent = agentResponsibilityContext.config.agent;
15739
16155
  }
15740
- if (agentResponsibilityContext.config.every && preloadedData.jobSchedule === void 0) {
15741
- preloadedData.jobSchedule = agentResponsibilityContext.config.every;
15742
- }
15743
16156
  if (agentResponsibilityContext.config.mentions && agentResponsibilityContext.config.mentions.length > 0) {
15744
16157
  preloadedData.mentions = agentResponsibilityContext.config.mentions.map((login) => `@${login}`).join(" ");
15745
16158
  }
@@ -15760,9 +16173,9 @@ async function runJob(job, base) {
15760
16173
  const run = base.chain === false ? runAgentAction : runAgentActionChain;
15761
16174
  return run(profileName, input);
15762
16175
  }
15763
- function loadAgentResponsibilityContext(slug, cwd) {
15764
- if (!slug) return null;
15765
- return resolveAgentResponsibilityFolder(slug, path41.join(cwd, ".kody", "agent-responsibilities"));
16176
+ function loadAgentResponsibilityContext(slug2, cwd) {
16177
+ if (!slug2) return null;
16178
+ return resolveAgentResponsibilityFolder(slug2, path40.join(cwd, ".kody", "agent-responsibilities"));
15766
16179
  }
15767
16180
  function mintInstantJob(dispatch2, opts) {
15768
16181
  return {
@@ -15784,7 +16197,8 @@ function mintScheduledJob(input) {
15784
16197
  schedule: input.schedule,
15785
16198
  agent: input.agent,
15786
16199
  cliArgs: input.cliArgs ?? {},
15787
- flavor: "scheduled"
16200
+ flavor: "scheduled",
16201
+ saveReport: input.saveReport === true
15788
16202
  };
15789
16203
  }
15790
16204
  var DEFAULT_INSTANT_AGENT, localJobSeq, InvalidJobError;
@@ -15915,7 +16329,7 @@ function translateOpenAISseToBrain(opts) {
15915
16329
  // src/servers/brain-serve.ts
15916
16330
  import * as fs43 from "fs";
15917
16331
  import { createServer } from "http";
15918
- import * as path44 from "path";
16332
+ import * as path43 from "path";
15919
16333
 
15920
16334
  // src/chat/loop.ts
15921
16335
  init_agent();
@@ -16458,7 +16872,7 @@ init_config();
16458
16872
  // src/kody-cli.ts
16459
16873
  import { execFileSync as execFileSync25 } from "child_process";
16460
16874
  import * as fs41 from "fs";
16461
- import * as path42 from "path";
16875
+ import * as path41 from "path";
16462
16876
 
16463
16877
  // src/app-auth.ts
16464
16878
  import { createSign } from "crypto";
@@ -17054,9 +17468,9 @@ async function resolveAuthToken(env = process.env) {
17054
17468
  return void 0;
17055
17469
  }
17056
17470
  function detectPackageManager2(cwd) {
17057
- if (fs41.existsSync(path42.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
17058
- if (fs41.existsSync(path42.join(cwd, "yarn.lock"))) return "yarn";
17059
- if (fs41.existsSync(path42.join(cwd, "bun.lockb"))) return "bun";
17471
+ if (fs41.existsSync(path41.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
17472
+ if (fs41.existsSync(path41.join(cwd, "yarn.lock"))) return "yarn";
17473
+ if (fs41.existsSync(path41.join(cwd, "bun.lockb"))) return "bun";
17060
17474
  return "npm";
17061
17475
  }
17062
17476
  function shellOut(cmd, args, cwd, stream = true) {
@@ -17143,7 +17557,7 @@ function configureGitIdentity(cwd) {
17143
17557
  }
17144
17558
  function postFailureTail(issueNumber, cwd, reason) {
17145
17559
  if (!issueNumber) return;
17146
- const logPath = path42.join(cwd, ".kody", "last-run.jsonl");
17560
+ const logPath = path41.join(cwd, ".kody", "last-run.jsonl");
17147
17561
  let tail = "";
17148
17562
  try {
17149
17563
  if (fs41.existsSync(logPath)) {
@@ -17172,7 +17586,7 @@ async function runCi(argv) {
17172
17586
  return 0;
17173
17587
  }
17174
17588
  const args = parseCiArgs(argv);
17175
- const cwd = args.cwd ? path42.resolve(args.cwd) : process.cwd();
17589
+ const cwd = args.cwd ? path41.resolve(args.cwd) : process.cwd();
17176
17590
  let earlyConfig;
17177
17591
  let earlyConfigError;
17178
17592
  try {
@@ -17514,10 +17928,10 @@ init_repoWorkspace();
17514
17928
 
17515
17929
  // src/scripts/brainTurnLog.ts
17516
17930
  import * as fs42 from "fs";
17517
- import * as path43 from "path";
17931
+ import * as path42 from "path";
17518
17932
  var live = /* @__PURE__ */ new Map();
17519
17933
  function eventsPath2(dir, chatId) {
17520
- return path43.join(dir, ".kody", "brain-events", `${chatId}.jsonl`);
17934
+ return path42.join(dir, ".kody", "brain-events", `${chatId}.jsonl`);
17521
17935
  }
17522
17936
  function lastPersistedSeq(dir, chatId) {
17523
17937
  const p = eventsPath2(dir, chatId);
@@ -17560,7 +17974,7 @@ function beginTurn(dir, chatId) {
17560
17974
  };
17561
17975
  live.set(chatId, state);
17562
17976
  const p = eventsPath2(dir, chatId);
17563
- fs42.mkdirSync(path43.dirname(p), { recursive: true });
17977
+ fs42.mkdirSync(path42.dirname(p), { recursive: true });
17564
17978
  return (event) => {
17565
17979
  state.seq += 1;
17566
17980
  const rec = { seq: state.seq, turn, ts: Date.now(), event };
@@ -17835,7 +18249,7 @@ async function handleChatTurn(req, res, chatId, opts) {
17835
18249
  const repo = strField(body, "repo");
17836
18250
  const repoToken = strField(body, "repoToken");
17837
18251
  const sessionFile = sessionFilePath(opts.cwd, chatId);
17838
- fs43.mkdirSync(path44.dirname(sessionFile), { recursive: true });
18252
+ fs43.mkdirSync(path43.dirname(sessionFile), { recursive: true });
17839
18253
  appendTurn(sessionFile, {
17840
18254
  role: "user",
17841
18255
  content: message,
@@ -17882,7 +18296,7 @@ async function handleChatTurn(req, res, chatId, opts) {
17882
18296
  function buildServer(opts) {
17883
18297
  const runTurn = opts.runTurn ?? runChatTurn;
17884
18298
  const cloneRepo = opts.cloneRepo ?? defaultCloneRepo;
17885
- const reposRoot = opts.reposRoot ?? path44.join(path44.dirname(path44.resolve(opts.cwd)), "repos");
18299
+ const reposRoot = opts.reposRoot ?? path43.join(path43.dirname(path43.resolve(opts.cwd)), "repos");
17886
18300
  return createServer(async (req, res) => {
17887
18301
  if (!req.method || !req.url) {
17888
18302
  sendJson(res, 400, { error: "bad request" });
@@ -18478,13 +18892,13 @@ async function loadConfigSafe() {
18478
18892
  // src/chat-cli.ts
18479
18893
  import { execFileSync as execFileSync28 } from "child_process";
18480
18894
  import * as fs45 from "fs";
18481
- import * as path46 from "path";
18895
+ import * as path45 from "path";
18482
18896
 
18483
18897
  // src/chat/modes/interactive.ts
18484
18898
  init_issue();
18485
18899
  import { execFileSync as execFileSync27 } from "child_process";
18486
18900
  import * as fs44 from "fs";
18487
- import * as path45 from "path";
18901
+ import * as path44 from "path";
18488
18902
 
18489
18903
  // src/chat/inbox.ts
18490
18904
  import { execFileSync as execFileSync26 } from "child_process";
@@ -18644,9 +19058,9 @@ function findNextUserTurn(turns, fromIdx) {
18644
19058
  return -1;
18645
19059
  }
18646
19060
  function commitTurn(cwd, sessionId, _verbose) {
18647
- const sessionRel = path45.relative(cwd, sessionFilePath(cwd, sessionId));
18648
- const eventsRel = path45.relative(cwd, eventsFilePath(cwd, sessionId));
18649
- const rels = [sessionRel, eventsRel].filter((p) => fs44.existsSync(path45.join(cwd, p)));
19061
+ const sessionRel = path44.relative(cwd, sessionFilePath(cwd, sessionId));
19062
+ const eventsRel = path44.relative(cwd, eventsFilePath(cwd, sessionId));
19063
+ const rels = [sessionRel, eventsRel].filter((p) => fs44.existsSync(path44.join(cwd, p)));
18650
19064
  if (rels.length === 0) return;
18651
19065
  const repository = process.env.GITHUB_REPOSITORY;
18652
19066
  if (!repository) {
@@ -18658,8 +19072,8 @@ function commitTurn(cwd, sessionId, _verbose) {
18658
19072
  }
18659
19073
  const branch = defaultBranch(cwd) ?? "main";
18660
19074
  for (const rel of rels) {
18661
- const repoPath = rel.split(path45.sep).join("/");
18662
- const localText = fs44.readFileSync(path45.join(cwd, rel), "utf-8");
19075
+ const repoPath = rel.split(path44.sep).join("/");
19076
+ const localText = fs44.readFileSync(path44.join(cwd, rel), "utf-8");
18663
19077
  putJsonlViaContents(repository, branch, repoPath, localText, sessionId, cwd);
18664
19078
  }
18665
19079
  }
@@ -18804,12 +19218,12 @@ function parseChatArgs(argv, env = process.env) {
18804
19218
  return result;
18805
19219
  }
18806
19220
  function commitChatFiles(cwd, sessionId, verbose) {
18807
- const sessionFile = path46.relative(cwd, sessionFilePath(cwd, sessionId));
18808
- const eventsFile = path46.relative(cwd, eventsFilePath(cwd, sessionId));
19221
+ const sessionFile = path45.relative(cwd, sessionFilePath(cwd, sessionId));
19222
+ const eventsFile = path45.relative(cwd, eventsFilePath(cwd, sessionId));
18809
19223
  const safeSession = sessionId.replace(/[^a-zA-Z0-9._-]/g, "_");
18810
- const tasksDir = path46.join(".kody", "tasks", safeSession);
19224
+ const tasksDir = path45.join(".kody", "tasks", safeSession);
18811
19225
  const candidatePaths = [sessionFile, eventsFile, tasksDir];
18812
- const paths = candidatePaths.filter((p) => fs45.existsSync(path46.join(cwd, p)));
19226
+ const paths = candidatePaths.filter((p) => fs45.existsSync(path45.join(cwd, p)));
18813
19227
  if (paths.length === 0) return;
18814
19228
  const opts = { cwd, stdio: verbose ? "inherit" : "pipe" };
18815
19229
  try {
@@ -18853,7 +19267,7 @@ async function runChat(argv) {
18853
19267
  ${CHAT_HELP}`);
18854
19268
  return 64;
18855
19269
  }
18856
- const cwd = args.cwd ? path46.resolve(args.cwd) : process.cwd();
19270
+ const cwd = args.cwd ? path45.resolve(args.cwd) : process.cwd();
18857
19271
  const sessionId = args.sessionId;
18858
19272
  const unpackedSecrets = unpackAllSecrets();
18859
19273
  if (unpackedSecrets > 0) {
@@ -19064,8 +19478,8 @@ var FlyClient = class {
19064
19478
  get fetch() {
19065
19479
  return this.opts.fetchImpl ?? fetch;
19066
19480
  }
19067
- async call(path47, init = {}) {
19068
- const res = await this.fetch(`${FLY_API_BASE}${path47}`, {
19481
+ async call(path46, init = {}) {
19482
+ const res = await this.fetch(`${FLY_API_BASE}${path46}`, {
19069
19483
  method: init.method ?? "GET",
19070
19484
  headers: {
19071
19485
  Authorization: `Bearer ${this.opts.token}`,
@@ -19076,7 +19490,7 @@ var FlyClient = class {
19076
19490
  if (res.status === 404 && init.allow404) return null;
19077
19491
  if (!res.ok) {
19078
19492
  const text = await res.text().catch(() => "");
19079
- throw new Error(`Fly API ${res.status} on ${path47}: ${text.slice(0, 200) || res.statusText}`);
19493
+ throw new Error(`Fly API ${res.status} on ${path46}: ${text.slice(0, 200) || res.statusText}`);
19080
19494
  }
19081
19495
  if (res.status === 204) return null;
19082
19496
  const raw = await res.text();