@kody-ade/kody-engine 0.4.274 → 0.4.276

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.274",
18
+ version: "0.4.276",
19
19
  description: "kody \u2014 autonomous development engine. Single-session Claude Code agent behind a generic executor + declarative executable profiles.",
20
20
  license: "MIT",
21
21
  type: "module",
@@ -8808,7 +8808,7 @@ function refreshGoalDashboardReport(input) {
8808
8808
  if (!REPORT_SLUG_RE.test(input.goalId)) {
8809
8809
  throw new Error(`goal report: invalid goal id "${input.goalId}"`);
8810
8810
  }
8811
- const filePath = `reports/${input.goalId}.md`;
8811
+ const filePath = goalDashboardReportRunPath(input.goalId);
8812
8812
  const body = goalReportBody(
8813
8813
  input.goalId,
8814
8814
  input.state,
@@ -8816,17 +8816,15 @@ function refreshGoalDashboardReport(input) {
8816
8816
  latestGoalRunLogEvent(input.data, input.goalId),
8817
8817
  evidenceItems
8818
8818
  );
8819
- const current = readStateText(input.config, input.cwd, filePath);
8820
- if (current?.content === body) {
8821
- const report2 = { slug: input.goalId, path: current.path, changed: false };
8822
- recordGoalReport(input.data, report2);
8823
- return report2;
8824
- }
8825
- upsertStateText(input.config, input.cwd, filePath, body, `chore(reports): refresh ${input.goalId}`);
8819
+ writeStateText(input.config, input.cwd, filePath, body, `chore(reports): add ${input.goalId} run`);
8826
8820
  const report = { slug: input.goalId, path: filePath, changed: true };
8827
8821
  recordGoalReport(input.data, report);
8828
8822
  return report;
8829
8823
  }
8824
+ function goalDashboardReportRunPath(goalId, now = /* @__PURE__ */ new Date()) {
8825
+ const runId = now.toISOString().replace(/\.\d{3}Z$/, "Z").replace(/:/g, "-");
8826
+ return `reports/${goalId}/runs/${runId}.md`;
8827
+ }
8830
8828
  function capabilityEvidenceOutput(evidence) {
8831
8829
  return {
8832
8830
  kind: "capability-evidence",
@@ -19581,6 +19579,68 @@ init_job();
19581
19579
  init_registry();
19582
19580
  init_runtimePaths();
19583
19581
  init_stateWorkspace();
19582
+
19583
+ // src/run-request.ts
19584
+ var RUN_REQUEST_ENV = "KODY_RUN_REQUEST_JSON";
19585
+ var INTENTS = /* @__PURE__ */ new Set(["continue", "manage", "run", "tick"]);
19586
+ var SOURCES = /* @__PURE__ */ new Set(["dashboard", "github", "schedule"]);
19587
+ var TARGET_TYPES = /* @__PURE__ */ new Set(["chat", "goal", "issue", "workflow"]);
19588
+ function isRecord(value) {
19589
+ return typeof value === "object" && value !== null && !Array.isArray(value);
19590
+ }
19591
+ function readString(value) {
19592
+ return typeof value === "string" ? value.trim() : "";
19593
+ }
19594
+ function parseTarget(input) {
19595
+ if (!isRecord(input)) return { error: "runRequest.target must be an object" };
19596
+ const type = readString(input.type);
19597
+ if (!TARGET_TYPES.has(type)) return { error: "runRequest.target.type is invalid" };
19598
+ if (type === "issue") {
19599
+ const id2 = Number(input.id);
19600
+ if (!Number.isInteger(id2) || id2 <= 0) return { error: "runRequest.target.id must be a positive issue number" };
19601
+ return { target: { type, id: id2 } };
19602
+ }
19603
+ const id = readString(input.id);
19604
+ if (!id) return { error: "runRequest.target.id is required" };
19605
+ return { target: { type, id } };
19606
+ }
19607
+ function parseRunRequest(input) {
19608
+ let body = input;
19609
+ if (typeof input === "string") {
19610
+ const raw = input.trim();
19611
+ if (!raw) return { error: `${RUN_REQUEST_ENV} is empty` };
19612
+ try {
19613
+ body = JSON.parse(raw);
19614
+ } catch {
19615
+ return { error: `${RUN_REQUEST_ENV} must be valid JSON` };
19616
+ }
19617
+ }
19618
+ if (!isRecord(body)) return { error: "runRequest must be an object" };
19619
+ const parsedTarget = parseTarget(body.target);
19620
+ if ("error" in parsedTarget) return parsedTarget;
19621
+ const intent = readString(body.intent);
19622
+ if (!INTENTS.has(intent)) return { error: "runRequest.intent is invalid" };
19623
+ const source = readString(body.source);
19624
+ if (!SOURCES.has(source)) return { error: "runRequest.source is invalid" };
19625
+ if (body.input !== void 0 && !isRecord(body.input)) {
19626
+ return { error: "runRequest.input must be an object when provided" };
19627
+ }
19628
+ return {
19629
+ request: {
19630
+ target: parsedTarget.target,
19631
+ intent,
19632
+ source,
19633
+ ...body.input !== void 0 ? { input: body.input } : {}
19634
+ }
19635
+ };
19636
+ }
19637
+ function readRunRequestFromEnv(env = process.env) {
19638
+ const raw = env[RUN_REQUEST_ENV];
19639
+ if (raw == null || raw.trim() === "") return null;
19640
+ return parseRunRequest(raw);
19641
+ }
19642
+
19643
+ // src/kody-cli.ts
19584
19644
  var CI_HELP = `kody ci \u2014 minimal-YAML autonomous engineer (CI preflight + run)
19585
19645
 
19586
19646
  Usage:
@@ -19639,6 +19699,30 @@ function parseCiArgs(argv) {
19639
19699
  }
19640
19700
  return result;
19641
19701
  }
19702
+ function routeRunRequest(request) {
19703
+ const { target, intent } = request;
19704
+ if (target.type === "chat" || target.type === "issue") return { kind: "ignore" };
19705
+ if (target.type === "goal") {
19706
+ if (intent !== "manage" && intent !== "run" && intent !== "tick") {
19707
+ return { kind: "error", error: `goal target does not support intent '${intent}'` };
19708
+ }
19709
+ return {
19710
+ kind: "action",
19711
+ action: "goal-manager",
19712
+ cliArgs: { goal: target.id }
19713
+ };
19714
+ }
19715
+ if (target.type === "workflow") {
19716
+ if (target.id === "scheduled-fanout") {
19717
+ return { kind: "fanout", force: intent === "run" };
19718
+ }
19719
+ if (intent !== "run" && intent !== "tick") {
19720
+ return { kind: "error", error: `workflow target does not support intent '${intent}'` };
19721
+ }
19722
+ return { kind: "action", action: target.id, cliArgs: {} };
19723
+ }
19724
+ return { kind: "error", error: "unsupported run request target" };
19725
+ }
19642
19726
  function unpackAllSecrets(env = process.env) {
19643
19727
  const raw = env.ALL_SECRETS;
19644
19728
  if (!raw) return 0;
@@ -19850,15 +19934,38 @@ async function runCi(argv) {
19850
19934
  let manualWorkflowDispatch = false;
19851
19935
  let forceRunAction = null;
19852
19936
  let forceRunCliArgs = {};
19937
+ let runRequestFanOut = false;
19938
+ let runRequestFanOutForce = false;
19939
+ const parsedRunRequest = readRunRequestFromEnv();
19940
+ if (parsedRunRequest && "error" in parsedRunRequest) {
19941
+ process.stderr.write(`[kody] ${parsedRunRequest.error}
19942
+ `);
19943
+ return 64;
19944
+ }
19945
+ if (!args.issueNumber && !autoFallback && parsedRunRequest && "request" in parsedRunRequest) {
19946
+ const route = routeRunRequest(parsedRunRequest.request);
19947
+ if (route.kind === "error") {
19948
+ process.stderr.write(`[kody] ${route.error}
19949
+ `);
19950
+ return 64;
19951
+ }
19952
+ if (route.kind === "fanout") {
19953
+ runRequestFanOut = true;
19954
+ runRequestFanOutForce = route.force;
19955
+ } else if (route.kind === "action") {
19956
+ forceRunAction = route.action;
19957
+ forceRunCliArgs = route.cliArgs;
19958
+ }
19959
+ }
19853
19960
  const envForceAction = (process.env.KODY_FORCE_ACTION ?? "").trim();
19854
19961
  const envForceMessage = (process.env.KODY_FORCE_MESSAGE ?? "").trim();
19855
- if (!args.issueNumber && !autoFallback && envForceAction) {
19962
+ if (!args.issueNumber && !autoFallback && !forceRunAction && !runRequestFanOut && envForceAction) {
19856
19963
  forceRunAction = envForceAction;
19857
19964
  if (envForceAction === "goal-manager" && envForceMessage) {
19858
19965
  forceRunCliArgs = { goal: envForceMessage };
19859
19966
  }
19860
19967
  }
19861
- if (!args.issueNumber && !autoFallback && !forceRunAction && eventName === "workflow_dispatch" && dispatchEventPath && fs46.existsSync(dispatchEventPath)) {
19968
+ if (!args.issueNumber && !autoFallback && !forceRunAction && !runRequestFanOut && eventName === "workflow_dispatch" && dispatchEventPath && fs46.existsSync(dispatchEventPath)) {
19862
19969
  try {
19863
19970
  const evt = JSON.parse(fs46.readFileSync(dispatchEventPath, "utf-8"));
19864
19971
  const issueInput = parseInt(String(evt?.inputs?.issue_number ?? ""), 10);
@@ -19950,6 +20057,9 @@ async function runCi(argv) {
19950
20057
  const ec = result.exitCode;
19951
20058
  return ec === 0 || ec === 1 || ec === 2 ? ec : 99;
19952
20059
  }
20060
+ if (!args.issueNumber && !autoFallback && runRequestFanOut) {
20061
+ return runScheduledFanOut(cwd, args, { force: runRequestFanOutForce });
20062
+ }
19953
20063
  if (!args.issueNumber && !autoFallback && (eventName === "schedule" || manualWorkflowDispatch)) {
19954
20064
  return runScheduledFanOut(cwd, args, { force: manualWorkflowDispatch });
19955
20065
  }
@@ -21573,6 +21683,10 @@ function parseChatArgs(argv, env = process.env) {
21573
21683
  else if (arg?.startsWith("--")) result.errors.push(`unknown arg: ${arg}`);
21574
21684
  else if (arg) result.errors.push(`unexpected positional: ${arg}`);
21575
21685
  }
21686
+ const runRequest = readRunRequestFromEnv(env);
21687
+ if (runRequest && "error" in runRequest) result.errors.push(runRequest.error);
21688
+ const chatTarget = runRequest && "request" in runRequest && runRequest.request.target.type === "chat" ? runRequest.request.target.id : void 0;
21689
+ result.sessionId = result.sessionId ?? chatTarget;
21576
21690
  result.sessionId = result.sessionId ?? env.SESSION_ID ?? void 0;
21577
21691
  result.initMessage = result.initMessage ?? env.INIT_MESSAGE ?? void 0;
21578
21692
  result.model = result.model ?? env.MODEL ?? void 0;
@@ -21766,7 +21880,11 @@ async function runCapabilityFallbackTick(deps) {
21766
21880
  const res = await deps.claim(owner, repo, {
21767
21881
  jobId: `sched-${owner}-${repo}-${clock()}`,
21768
21882
  repo: tag,
21769
- mode: "scheduled"
21883
+ runRequest: {
21884
+ target: { type: "workflow", id: "scheduled-fanout" },
21885
+ intent: "tick",
21886
+ source: "schedule"
21887
+ }
21770
21888
  });
21771
21889
  if (res.ok) {
21772
21890
  claimed++;
@@ -22341,7 +22459,7 @@ var PoolRegistry = class {
22341
22459
  jobId: req.jobId,
22342
22460
  repo: `${owner}/${repo}`,
22343
22461
  githubToken: this.cfg.githubToken,
22344
- mode: req.mode ?? "issue",
22462
+ runRequest: req.runRequest,
22345
22463
  issueNumber: req.issueNumber,
22346
22464
  sessionId: req.sessionId,
22347
22465
  idleExitMs: req.idleExitMs,
@@ -22427,24 +22545,78 @@ function parseClaimRequest(body) {
22427
22545
  const repo = typeof b.repo === "string" ? b.repo.trim() : "";
22428
22546
  if (!/^[^/\s]+\/[^/\s]+$/.test(repo)) return { error: "repo must be 'owner/name'" };
22429
22547
  const mode = b.mode === "interactive" ? "interactive" : b.mode === "scheduled" ? "scheduled" : "issue";
22430
- const req = { jobId, repo, mode };
22431
- if (mode === "issue") {
22432
- const issueNumber = Number(b.issueNumber);
22433
- if (!Number.isInteger(issueNumber) || issueNumber <= 0) return { error: "issueNumber required for issue mode" };
22434
- req.issueNumber = issueNumber;
22435
- } else if (mode === "interactive") {
22436
- const sessionId = typeof b.sessionId === "string" ? b.sessionId.trim() : "";
22437
- if (!sessionId) return { error: "sessionId required for interactive mode" };
22438
- req.sessionId = sessionId;
22548
+ const action = typeof b.action === "string" && b.action.trim() ? b.action.trim() : void 0;
22549
+ const message = typeof b.message === "string" && b.message.trim() ? b.message.trim() : void 0;
22550
+ const runRequest = b.runRequest !== void 0 ? parseRunRequest(b.runRequest) : synthesizeLegacyClaimRequest({
22551
+ mode,
22552
+ issueNumber: b.issueNumber,
22553
+ sessionId: b.sessionId,
22554
+ action,
22555
+ message
22556
+ });
22557
+ if ("error" in runRequest) return { error: runRequest.error };
22558
+ const req = { jobId, repo, runRequest: runRequest.request };
22559
+ if (req.runRequest.target.type === "issue") {
22560
+ req.issueNumber = req.runRequest.target.id;
22561
+ } else if (req.runRequest.target.type === "chat") {
22562
+ req.sessionId = req.runRequest.target.id;
22439
22563
  if (Number.isFinite(Number(b.idleExitMs))) req.idleExitMs = Number(b.idleExitMs);
22440
22564
  if (Number.isFinite(Number(b.hardCapMs))) req.hardCapMs = Number(b.hardCapMs);
22441
22565
  }
22442
22566
  if (typeof b.ref === "string" && b.ref.trim()) req.ref = b.ref.trim();
22443
22567
  if (typeof b.model === "string" && b.model.trim()) req.model = b.model.trim();
22444
- if (typeof b.sessionId === "string" && b.sessionId.trim()) req.sessionId = b.sessionId.trim();
22445
22568
  if (typeof b.dashboardUrl === "string" && b.dashboardUrl.trim()) req.dashboardUrl = b.dashboardUrl.trim();
22446
22569
  return { req };
22447
22570
  }
22571
+ function synthesizeLegacyClaimRequest(input) {
22572
+ if (input.mode === "issue") {
22573
+ const issueNumber = Number(input.issueNumber);
22574
+ if (!Number.isInteger(issueNumber) || issueNumber <= 0) return { error: "issueNumber required for issue mode" };
22575
+ return {
22576
+ request: {
22577
+ target: { type: "issue", id: issueNumber },
22578
+ intent: "run",
22579
+ source: "dashboard"
22580
+ }
22581
+ };
22582
+ }
22583
+ if (input.mode === "interactive") {
22584
+ const sessionId = typeof input.sessionId === "string" ? input.sessionId.trim() : "";
22585
+ if (!sessionId) return { error: "sessionId required for interactive mode" };
22586
+ return {
22587
+ request: {
22588
+ target: { type: "chat", id: sessionId },
22589
+ intent: "continue",
22590
+ source: "dashboard"
22591
+ }
22592
+ };
22593
+ }
22594
+ if (input.action === "goal-manager" && input.message) {
22595
+ return {
22596
+ request: {
22597
+ target: { type: "goal", id: input.message },
22598
+ intent: "manage",
22599
+ source: "dashboard"
22600
+ }
22601
+ };
22602
+ }
22603
+ if (input.action) {
22604
+ return {
22605
+ request: {
22606
+ target: { type: "workflow", id: input.action },
22607
+ intent: "run",
22608
+ source: "dashboard"
22609
+ }
22610
+ };
22611
+ }
22612
+ return {
22613
+ request: {
22614
+ target: { type: "workflow", id: "scheduled-fanout" },
22615
+ intent: "tick",
22616
+ source: "schedule"
22617
+ }
22618
+ };
22619
+ }
22448
22620
  async function poolServe() {
22449
22621
  const masterRaw = process.env.KODY_MASTER_KEY?.trim();
22450
22622
  if (!masterRaw) throw new Error("KODY_MASTER_KEY required for pool-serve");
@@ -22612,33 +22784,85 @@ function parseJob(body) {
22612
22784
  if (!/^[^/\s]+\/[^/\s]+$/.test(repo)) return { error: "repo must be 'owner/name'" };
22613
22785
  const githubToken = typeof b.githubToken === "string" ? b.githubToken.trim() : "";
22614
22786
  if (!githubToken) return { error: "githubToken required" };
22787
+ const action = typeof b.action === "string" && b.action.trim() ? b.action.trim() : void 0;
22788
+ const message = typeof b.message === "string" && b.message.trim() ? b.message.trim() : void 0;
22615
22789
  const mode = b.mode === "interactive" ? "interactive" : b.mode === "scheduled" ? "scheduled" : "issue";
22616
- const job = { jobId, repo, githubToken, mode };
22617
- if (mode === "issue") {
22618
- const issueNumber = Number(b.issueNumber);
22619
- if (!Number.isInteger(issueNumber) || issueNumber <= 0) {
22620
- return { error: "issueNumber (positive integer) required for issue mode" };
22621
- }
22622
- job.issueNumber = issueNumber;
22623
- } else if (mode === "interactive") {
22624
- const sessionId = typeof b.sessionId === "string" ? b.sessionId.trim() : "";
22625
- if (!sessionId) return { error: "sessionId required for interactive mode" };
22626
- job.sessionId = sessionId;
22790
+ const runRequest = b.runRequest !== void 0 ? parseRunRequest(b.runRequest) : synthesizeLegacyRunRequest({
22791
+ mode,
22792
+ issueNumber: b.issueNumber,
22793
+ sessionId: b.sessionId,
22794
+ action,
22795
+ message
22796
+ });
22797
+ if ("error" in runRequest) return { error: runRequest.error };
22798
+ const job = { jobId, repo, githubToken, runRequest: runRequest.request };
22799
+ if (job.runRequest.target.type === "issue") {
22800
+ job.issueNumber = job.runRequest.target.id;
22801
+ } else if (job.runRequest.target.type === "chat") {
22802
+ job.sessionId = job.runRequest.target.id;
22627
22803
  if (Number.isFinite(Number(b.idleExitMs))) job.idleExitMs = Number(b.idleExitMs);
22628
22804
  if (Number.isFinite(Number(b.hardCapMs))) job.hardCapMs = Number(b.hardCapMs);
22629
22805
  }
22630
22806
  if (typeof b.ref === "string" && b.ref.trim()) job.ref = b.ref.trim();
22631
22807
  if (typeof b.model === "string" && b.model.trim()) job.model = b.model.trim();
22632
22808
  if (typeof b.reasoningEffort === "string" && b.reasoningEffort.trim()) job.reasoningEffort = b.reasoningEffort.trim();
22633
- if (typeof b.action === "string" && b.action.trim()) job.action = b.action.trim();
22634
- if (typeof b.message === "string" && b.message.trim()) job.message = b.message.trim();
22635
- if (typeof b.sessionId === "string" && b.sessionId.trim()) job.sessionId = b.sessionId.trim();
22636
22809
  if (typeof b.dashboardUrl === "string" && b.dashboardUrl.trim()) job.dashboardUrl = b.dashboardUrl.trim();
22637
22810
  if (b.allSecrets && (typeof b.allSecrets === "object" || typeof b.allSecrets === "string")) {
22638
22811
  job.allSecrets = b.allSecrets;
22639
22812
  }
22640
22813
  return { job };
22641
22814
  }
22815
+ function synthesizeLegacyRunRequest(input) {
22816
+ if (input.mode === "issue") {
22817
+ const issueNumber = Number(input.issueNumber);
22818
+ if (!Number.isInteger(issueNumber) || issueNumber <= 0) {
22819
+ return { error: "issueNumber (positive integer) required for issue mode" };
22820
+ }
22821
+ return {
22822
+ request: {
22823
+ target: { type: "issue", id: issueNumber },
22824
+ intent: "run",
22825
+ source: "dashboard"
22826
+ }
22827
+ };
22828
+ }
22829
+ if (input.mode === "interactive") {
22830
+ const sessionId = typeof input.sessionId === "string" ? input.sessionId.trim() : "";
22831
+ if (!sessionId) return { error: "sessionId required for interactive mode" };
22832
+ return {
22833
+ request: {
22834
+ target: { type: "chat", id: sessionId },
22835
+ intent: "continue",
22836
+ source: "dashboard"
22837
+ }
22838
+ };
22839
+ }
22840
+ if (input.action === "goal-manager" && input.message) {
22841
+ return {
22842
+ request: {
22843
+ target: { type: "goal", id: input.message },
22844
+ intent: "manage",
22845
+ source: "dashboard"
22846
+ }
22847
+ };
22848
+ }
22849
+ if (input.action) {
22850
+ return {
22851
+ request: {
22852
+ target: { type: "workflow", id: input.action },
22853
+ intent: "run",
22854
+ source: "dashboard"
22855
+ }
22856
+ };
22857
+ }
22858
+ return {
22859
+ request: {
22860
+ target: { type: "workflow", id: "scheduled-fanout" },
22861
+ intent: "tick",
22862
+ source: "schedule"
22863
+ }
22864
+ };
22865
+ }
22642
22866
  async function defaultRunJob(job) {
22643
22867
  const workdir = process.env.RUNNER_WORKDIR ?? DEFAULT_WORKDIR;
22644
22868
  const branch = job.ref ?? "main";
@@ -22646,20 +22870,17 @@ async function defaultRunJob(job) {
22646
22870
  fs51.rmSync(workdir, { recursive: true, force: true });
22647
22871
  fs51.mkdirSync(workdir, { recursive: true });
22648
22872
  const allSecrets = typeof job.allSecrets === "string" ? job.allSecrets : JSON.stringify(job.allSecrets ?? {});
22649
- const interactive = job.mode === "interactive";
22650
- const scheduled = job.mode === "scheduled";
22651
- const runMode = interactive ? "interactive" : scheduled ? "scheduled" : "issue";
22873
+ const target = job.runRequest.target;
22874
+ const interactive = target.type === "chat";
22875
+ const scheduled = target.type === "goal" || target.type === "workflow";
22876
+ const issueNumber = target.type === "issue" ? target.id : void 0;
22877
+ const sessionId = target.type === "chat" ? target.id : void 0;
22652
22878
  const childEnv = {
22653
22879
  ...process.env,
22654
22880
  REPO: job.repo,
22655
22881
  REF: branch,
22656
22882
  GITHUB_TOKEN: job.githubToken,
22657
- KODY_RUN_MODE: runMode,
22658
- // Scheduled mode drives the engine down the same path GitHub Actions' cron
22659
- // takes (runScheduledFanOut → due capabilities/goals). Bare `kody` routes on this.
22660
- ...scheduled ? { GITHUB_EVENT_NAME: "schedule" } : {},
22661
- ...job.action ? { KODY_FORCE_ACTION: job.action } : {},
22662
- ...job.message ? { KODY_FORCE_MESSAGE: job.message } : {},
22883
+ [RUN_REQUEST_ENV]: JSON.stringify(job.runRequest),
22663
22884
  // GITHUB_REPOSITORY + GH_TOKEN are normally injected by GitHub Actions.
22664
22885
  // The engine's interactive mode needs GITHUB_REPOSITORY to resolve the
22665
22886
  // configured state repo and persist chat.ready / events (the durable signal
@@ -22667,11 +22888,9 @@ async function defaultRunJob(job) {
22667
22888
  // and the session never appears "ready". GH_TOKEN auths the `gh` CLI.
22668
22889
  GITHUB_REPOSITORY: job.repo,
22669
22890
  GH_TOKEN: job.githubToken,
22670
- // Issue mode carries ISSUE_NUMBER. Interactive mode leaves it empty and
22671
- // sets SESSION_ID so the engine boots a chat session.
22672
- ISSUE_NUMBER: interactive || scheduled ? "" : String(job.issueNumber),
22891
+ ISSUE_NUMBER: issueNumber ? String(issueNumber) : "",
22673
22892
  ALL_SECRETS: allSecrets,
22674
- SESSION_ID: job.sessionId ?? "",
22893
+ SESSION_ID: sessionId ?? "",
22675
22894
  DASHBOARD_URL: job.dashboardUrl ?? "",
22676
22895
  MODEL: job.model ?? "",
22677
22896
  REASONING_EFFORT: job.reasoningEffort ?? "",
@@ -22700,7 +22919,7 @@ async function defaultRunJob(job) {
22700
22919
  const authorEmail = process.env.GIT_AUTHOR_EMAIL ?? "kody-bot@users.noreply.github.com";
22701
22920
  await run("git", ["config", "user.name", authorName], workdir);
22702
22921
  await run("git", ["config", "user.email", authorEmail], workdir);
22703
- const jobDesc = interactive ? `interactive session ${job.sessionId}` : scheduled ? "scheduled fan-out" : `running issue #${job.issueNumber}`;
22922
+ const jobDesc = interactive ? `chat session ${sessionId}` : scheduled ? `${target.type} ${target.id}` : `issue #${issueNumber}`;
22704
22923
  process.stdout.write(`[runner-serve] job ${job.jobId}: ${jobDesc}
22705
22924
  `);
22706
22925
  const runCode = await run("kody", [], workdir);
@@ -22783,7 +23002,7 @@ async function runnerServe() {
22783
23002
  init_config();
22784
23003
  init_litellm();
22785
23004
  import { spawn as spawn9 } from "child_process";
22786
- function parseTarget(positional) {
23005
+ function parseTarget2(positional) {
22787
23006
  if (!Array.isArray(positional) || positional.length === 0) return "none";
22788
23007
  const first = String(positional[0]).toLowerCase();
22789
23008
  if (first === "vscode" || first === "code") return "vscode";
@@ -22798,7 +23017,7 @@ function buildProxyEnv(url) {
22798
23017
  };
22799
23018
  }
22800
23019
  async function serve(opts) {
22801
- const target = parseTarget(opts.args);
23020
+ const target = parseTarget2(opts.args);
22802
23021
  const model = parseProviderModel(opts.config.agent.model);
22803
23022
  const usesProxy = needsLitellmProxy(model);
22804
23023
  let handle = null;
@@ -23112,6 +23331,17 @@ function envRunMode(env = process.env) {
23112
23331
  }
23113
23332
  return { ...result, errors: [`unknown KODY_RUN_MODE: ${mode}`] };
23114
23333
  }
23334
+ function envRunRequest(env = process.env) {
23335
+ const result = { command: "help", errors: [] };
23336
+ const parsed = readRunRequestFromEnv(env);
23337
+ if (!parsed) return null;
23338
+ if ("error" in parsed) return { ...result, errors: [parsed.error] };
23339
+ const { target } = parsed.request;
23340
+ if (target.type === "chat") return { ...result, command: "chat", chatArgv: [] };
23341
+ if (target.type === "issue") return { ...result, command: "ci", ciArgv: ["--issue", String(target.id)] };
23342
+ if (target.type === "goal" || target.type === "workflow") return { ...result, command: "ci", ciArgv: [] };
23343
+ return { ...result, errors: [`unsupported runRequest target: ${target.type ?? "unknown"}`] };
23344
+ }
23115
23345
  var HELP_TEXT = `kody-engine \u2014 single-session autonomous engineer
23116
23346
 
23117
23347
  Usage:
@@ -23148,6 +23378,8 @@ Exit codes:
23148
23378
  function parseArgs(argv) {
23149
23379
  const result = { command: "help", errors: [] };
23150
23380
  if (argv.length === 0) {
23381
+ const runRequest = envRunRequest();
23382
+ if (runRequest) return runRequest;
23151
23383
  const mode = envRunMode();
23152
23384
  if (mode) return mode;
23153
23385
  if (process.env.SESSION_ID) return { ...result, command: "chat", chatArgv: [] };
@@ -498,6 +498,6 @@ export interface Job {
498
498
  flavor: JobFlavor
499
499
  /** Manual force-run (bypass cadence) for a scheduled job. */
500
500
  force?: boolean
501
- /** Ask the owning goal/loop to refresh reports/<goal-or-loop>.md after its persisted decision. */
501
+ /** Ask the owning goal/loop to write a report run after its persisted decision. */
502
502
  saveReport?: boolean
503
503
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kody-ade/kody-engine",
3
- "version": "0.4.274",
3
+ "version": "0.4.276",
4
4
  "description": "kody — autonomous development engine. Single-session Claude Code agent behind a generic executor + declarative executable profiles.",
5
5
  "license": "MIT",
6
6
  "type": "module",