@kody-ade/kody-engine 0.4.466 → 0.4.468

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.466",
18
+ version: "0.4.468",
19
19
  description: "kody \u2014 autonomous development engine. Single-session Claude Code agent behind a generic executor + declarative implementation profiles.",
20
20
  license: "MIT",
21
21
  type: "module",
@@ -54,6 +54,7 @@ var init_package = __esm({
54
54
  "@actions/cache": "^6.0.0",
55
55
  "@anthropic-ai/claude-agent-sdk": "0.2.119",
56
56
  "@kody-ade/agency-domain": "0.5.1",
57
+ "@kody-ade/engine-contracts": "0.1.0",
57
58
  "@modelcontextprotocol/sdk": "^1.29.0",
58
59
  ajv: "^8.18.0",
59
60
  convex: "^1.17.0",
@@ -14053,6 +14054,11 @@ var init_loopDefinitions = __esm({
14053
14054
 
14054
14055
  // src/scripts/dispatchSimpleLoops.ts
14055
14056
  import { randomUUID as randomUUID2 } from "crypto";
14057
+ function selectRunnableLoops(loops, now, options) {
14058
+ return loops.filter(
14059
+ (loop) => loop.enabled && loop.trigger.type === "schedule" && (!options.loopId || loop.id === options.loopId) && (options.force || dueSlot(loop, now) !== null)
14060
+ );
14061
+ }
14056
14062
  function loopDispatchSlot(loop, now, force, nonce) {
14057
14063
  return force ? `manual:${now.toISOString()}:${nonce}` : dueSlot(loop, now);
14058
14064
  }
@@ -14110,9 +14116,11 @@ var init_dispatchSimpleLoops = __esm({
14110
14116
  if (!tenantId2) throw new Error("Repository identity is required for Loop dispatch");
14111
14117
  const now = /* @__PURE__ */ new Date();
14112
14118
  const force = ctx.data.jobForce === true;
14113
- const due = listLoopDefinitions(ctx.cwd).filter(
14114
- (loop) => loop.enabled && loop.trigger.type === "schedule" && (force || dueSlot(loop, now) !== null)
14115
- );
14119
+ const requestedLoopId = typeof ctx.args.loop === "string" ? ctx.args.loop.trim() : "";
14120
+ const due = selectRunnableLoops(listLoopDefinitions(ctx.cwd), now, {
14121
+ force,
14122
+ ...requestedLoopId ? { loopId: requestedLoopId } : {}
14123
+ });
14116
14124
  process.stdout.write(
14117
14125
  `\u2192 kody: Loop scheduler found ${due.length} runnable Loop(s)${force ? " (manual)" : ""}
14118
14126
  `
@@ -25172,63 +25180,15 @@ init_loopDefinitions();
25172
25180
  init_registry();
25173
25181
 
25174
25182
  // src/run-request.ts
25175
- var RUN_REQUEST_ENV = "KODY_RUN_REQUEST_JSON";
25176
- var INTENTS = /* @__PURE__ */ new Set(["continue", "manage", "run", "tick"]);
25177
- var SOURCES = /* @__PURE__ */ new Set(["dashboard", "github", "schedule"]);
25178
- var TARGET_TYPES = /* @__PURE__ */ new Set(["chat", "goal", "issue", "workflow"]);
25179
- function isRecord2(value) {
25180
- return typeof value === "object" && value !== null && !Array.isArray(value);
25181
- }
25182
- function readString(value) {
25183
- return typeof value === "string" ? value.trim() : "";
25184
- }
25185
- function parseTarget(input) {
25186
- if (!isRecord2(input)) return { error: "runRequest.target must be an object" };
25187
- const type = readString(input.type);
25188
- if (!TARGET_TYPES.has(type)) return { error: "runRequest.target.type is invalid" };
25189
- if (type === "issue") {
25190
- const id2 = Number(input.id);
25191
- if (!Number.isInteger(id2) || id2 <= 0) return { error: "runRequest.target.id must be a positive issue number" };
25192
- return { target: { type, id: id2 } };
25193
- }
25194
- const id = readString(input.id);
25195
- if (!id) return { error: "runRequest.target.id is required" };
25196
- return { target: { type, id } };
25197
- }
25198
- function parseRunRequest(input) {
25199
- let body = input;
25200
- if (typeof input === "string") {
25201
- const raw = input.trim();
25202
- if (!raw) return { error: `${RUN_REQUEST_ENV} is empty` };
25203
- try {
25204
- body = JSON.parse(raw);
25205
- } catch {
25206
- return { error: `${RUN_REQUEST_ENV} must be valid JSON` };
25207
- }
25208
- }
25209
- if (!isRecord2(body)) return { error: "runRequest must be an object" };
25210
- const parsedTarget = parseTarget(body.target);
25211
- if ("error" in parsedTarget) return parsedTarget;
25212
- const intent = readString(body.intent);
25213
- if (!INTENTS.has(intent)) return { error: "runRequest.intent is invalid" };
25214
- const source = readString(body.source);
25215
- if (!SOURCES.has(source)) return { error: "runRequest.source is invalid" };
25216
- if (body.input !== void 0 && !isRecord2(body.input)) {
25217
- return { error: "runRequest.input must be an object when provided" };
25218
- }
25219
- return {
25220
- request: {
25221
- target: parsedTarget.target,
25222
- intent,
25223
- source,
25224
- ...body.input !== void 0 ? { input: body.input } : {}
25225
- }
25226
- };
25227
- }
25183
+ import {
25184
+ ENGINE_EXECUTION_REQUEST_ENV,
25185
+ parseEngineExecutionRequest
25186
+ } from "@kody-ade/engine-contracts";
25187
+ var RUN_REQUEST_ENV = ENGINE_EXECUTION_REQUEST_ENV;
25228
25188
  function readRunRequestFromEnv(env = process.env) {
25229
25189
  const raw = env[RUN_REQUEST_ENV];
25230
25190
  if (raw == null || raw.trim() === "") return null;
25231
- return parseRunRequest(raw);
25191
+ return parseEngineExecutionRequest(raw);
25232
25192
  }
25233
25193
 
25234
25194
  // src/kody-cli.ts
@@ -25306,6 +25266,9 @@ function parseCiArgs(argv) {
25306
25266
  }
25307
25267
  return result;
25308
25268
  }
25269
+ function isScheduledActionsWake(env = process.env) {
25270
+ return env.GITHUB_EVENT_NAME === "schedule" || env.GITHUB_ACTIONS === "true" && !env.GITHUB_EVENT_NAME && !env.GITHUB_EVENT_PATH;
25271
+ }
25309
25272
  function routeRunRequest(request) {
25310
25273
  const { target, intent } = request;
25311
25274
  if (target.type === "chat" || target.type === "issue") return { kind: "ignore" };
@@ -25319,6 +25282,16 @@ function routeRunRequest(request) {
25319
25282
  cliArgs: { goal: target.id }
25320
25283
  };
25321
25284
  }
25285
+ if (target.type === "loop") {
25286
+ if (intent !== "run" && intent !== "tick") {
25287
+ return { kind: "error", error: `loop target does not support intent '${intent}'` };
25288
+ }
25289
+ return {
25290
+ kind: "action",
25291
+ action: "loop-scheduler",
25292
+ cliArgs: { loop: target.id }
25293
+ };
25294
+ }
25322
25295
  if (target.type === "workflow") {
25323
25296
  if (target.id === "scheduled-fanout") {
25324
25297
  return { kind: "fanout", force: intent === "run" };
@@ -25326,7 +25299,8 @@ function routeRunRequest(request) {
25326
25299
  if (intent !== "run" && intent !== "tick") {
25327
25300
  return { kind: "error", error: `workflow target does not support intent '${intent}'` };
25328
25301
  }
25329
- const workflowRunId = typeof request.input?.runId === "string" && /^[a-z0-9][a-z0-9_-]{0,79}$/.test(request.input.runId) ? request.input.runId : void 0;
25302
+ const requestedRunId = typeof request.input?.runId === "string" ? request.input.runId : request.requestId;
25303
+ const workflowRunId = /^[a-zA-Z0-9][a-zA-Z0-9_-]{0,127}$/.test(requestedRunId) ? requestedRunId : void 0;
25330
25304
  return { kind: "action", action: target.id, cliArgs: {}, ...workflowRunId ? { workflowRunId } : {} };
25331
25305
  }
25332
25306
  return { kind: "error", error: "unsupported run request target" };
@@ -25571,6 +25545,7 @@ async function runCi(argv) {
25571
25545
  }
25572
25546
  const autoFallback = !args.issueNumber ? autoDispatch({ config: earlyConfig }) : null;
25573
25547
  const eventName = process.env.GITHUB_EVENT_NAME;
25548
+ const scheduledActionsWake = isScheduledActionsWake();
25574
25549
  const dispatchEventPath = process.env.GITHUB_EVENT_PATH;
25575
25550
  let manualWorkflowDispatch = false;
25576
25551
  let forceRunAction = null;
@@ -25745,7 +25720,7 @@ async function runCi(argv) {
25745
25720
  if (!args.issueNumber && !autoFallback && runRequestFanOut) {
25746
25721
  return runScheduledFanOut(cwd, args, { force: runRequestFanOutForce });
25747
25722
  }
25748
- if (!args.issueNumber && !autoFallback && (eventName === "schedule" || manualWorkflowDispatch)) {
25723
+ if (!args.issueNumber && !autoFallback && (scheduledActionsWake || manualWorkflowDispatch)) {
25749
25724
  return runScheduledFanOut(cwd, args, { force: manualWorkflowDispatch });
25750
25725
  }
25751
25726
  if (!args.issueNumber && !autoFallback && process.env.GITHUB_EVENT_NAME) {
@@ -27537,10 +27512,12 @@ async function runAgencyLoopTick(deps) {
27537
27512
  for (const repository of repositories) {
27538
27513
  const [owner, repo] = repository.split("/");
27539
27514
  try {
27515
+ const jobId = `sched-${owner}-${repo}-${clock()}`;
27540
27516
  const result = await deps.claim(owner, repo, {
27541
- jobId: `sched-${owner}-${repo}-${clock()}`,
27517
+ jobId,
27542
27518
  repo: repository,
27543
27519
  runRequest: {
27520
+ requestId: jobId,
27544
27521
  target: { type: "workflow", id: "scheduled-fanout" },
27545
27522
  intent: "tick",
27546
27523
  source: "schedule"
@@ -28127,16 +28104,8 @@ function parseClaimRequest(body) {
28127
28104
  if (!jobId) return { error: "jobId required" };
28128
28105
  const repo = typeof b.repo === "string" ? b.repo.trim() : "";
28129
28106
  if (!/^[^/\s]+\/[^/\s]+$/.test(repo)) return { error: "repo must be 'owner/name'" };
28130
- const mode = b.mode === "interactive" ? "interactive" : b.mode === "scheduled" ? "scheduled" : "issue";
28131
- const action = typeof b.action === "string" && b.action.trim() ? b.action.trim() : void 0;
28132
- const message = typeof b.message === "string" && b.message.trim() ? b.message.trim() : void 0;
28133
- const runRequest = b.runRequest !== void 0 ? parseRunRequest(b.runRequest) : synthesizeLegacyClaimRequest({
28134
- mode,
28135
- issueNumber: b.issueNumber,
28136
- sessionId: b.sessionId,
28137
- action,
28138
- message
28139
- });
28107
+ if (b.runRequest === void 0) return { error: "runRequest required" };
28108
+ const runRequest = parseEngineExecutionRequest(b.runRequest);
28140
28109
  if ("error" in runRequest) return { error: runRequest.error };
28141
28110
  const req = { jobId, repo, runRequest: runRequest.request };
28142
28111
  if (req.runRequest.target.type === "issue") {
@@ -28151,55 +28120,6 @@ function parseClaimRequest(body) {
28151
28120
  if (typeof b.dashboardUrl === "string" && b.dashboardUrl.trim()) req.dashboardUrl = b.dashboardUrl.trim();
28152
28121
  return { req };
28153
28122
  }
28154
- function synthesizeLegacyClaimRequest(input) {
28155
- if (input.mode === "issue") {
28156
- const issueNumber = Number(input.issueNumber);
28157
- if (!Number.isInteger(issueNumber) || issueNumber <= 0) return { error: "issueNumber required for issue mode" };
28158
- return {
28159
- request: {
28160
- target: { type: "issue", id: issueNumber },
28161
- intent: "run",
28162
- source: "dashboard"
28163
- }
28164
- };
28165
- }
28166
- if (input.mode === "interactive") {
28167
- const sessionId = typeof input.sessionId === "string" ? input.sessionId.trim() : "";
28168
- if (!sessionId) return { error: "sessionId required for interactive mode" };
28169
- return {
28170
- request: {
28171
- target: { type: "chat", id: sessionId },
28172
- intent: "continue",
28173
- source: "dashboard"
28174
- }
28175
- };
28176
- }
28177
- if (input.action === "goal-manager" && input.message) {
28178
- return {
28179
- request: {
28180
- target: { type: "goal", id: input.message },
28181
- intent: "manage",
28182
- source: "dashboard"
28183
- }
28184
- };
28185
- }
28186
- if (input.action) {
28187
- return {
28188
- request: {
28189
- target: { type: "workflow", id: input.action },
28190
- intent: "run",
28191
- source: "dashboard"
28192
- }
28193
- };
28194
- }
28195
- return {
28196
- request: {
28197
- target: { type: "workflow", id: "scheduled-fanout" },
28198
- intent: "tick",
28199
- source: "schedule"
28200
- }
28201
- };
28202
- }
28203
28123
  async function poolServe() {
28204
28124
  const masterRaw = process.env.KODY_MASTER_KEY?.trim();
28205
28125
  if (!masterRaw) throw new Error("KODY_MASTER_KEY required for pool-serve");
@@ -28397,16 +28317,8 @@ function parseJob(body) {
28397
28317
  if (!/^[^/\s]+\/[^/\s]+$/.test(repo)) return { error: "repo must be 'owner/name'" };
28398
28318
  const githubToken = typeof b.githubToken === "string" ? b.githubToken.trim() : "";
28399
28319
  if (!githubToken) return { error: "githubToken required" };
28400
- const action = typeof b.action === "string" && b.action.trim() ? b.action.trim() : void 0;
28401
- const message = typeof b.message === "string" && b.message.trim() ? b.message.trim() : void 0;
28402
- const mode = b.mode === "interactive" ? "interactive" : b.mode === "scheduled" ? "scheduled" : "issue";
28403
- const runRequest = b.runRequest !== void 0 ? parseRunRequest(b.runRequest) : synthesizeLegacyRunRequest({
28404
- mode,
28405
- issueNumber: b.issueNumber,
28406
- sessionId: b.sessionId,
28407
- action,
28408
- message
28409
- });
28320
+ if (b.runRequest === void 0) return { error: "runRequest required" };
28321
+ const runRequest = parseEngineExecutionRequest(b.runRequest);
28410
28322
  if ("error" in runRequest) return { error: runRequest.error };
28411
28323
  const job = { jobId, repo, githubToken, runRequest: runRequest.request };
28412
28324
  if (job.runRequest.target.type === "issue") {
@@ -28425,57 +28337,6 @@ function parseJob(body) {
28425
28337
  }
28426
28338
  return { job };
28427
28339
  }
28428
- function synthesizeLegacyRunRequest(input) {
28429
- if (input.mode === "issue") {
28430
- const issueNumber = Number(input.issueNumber);
28431
- if (!Number.isInteger(issueNumber) || issueNumber <= 0) {
28432
- return { error: "issueNumber (positive integer) required for issue mode" };
28433
- }
28434
- return {
28435
- request: {
28436
- target: { type: "issue", id: issueNumber },
28437
- intent: "run",
28438
- source: "dashboard"
28439
- }
28440
- };
28441
- }
28442
- if (input.mode === "interactive") {
28443
- const sessionId = typeof input.sessionId === "string" ? input.sessionId.trim() : "";
28444
- if (!sessionId) return { error: "sessionId required for interactive mode" };
28445
- return {
28446
- request: {
28447
- target: { type: "chat", id: sessionId },
28448
- intent: "continue",
28449
- source: "dashboard"
28450
- }
28451
- };
28452
- }
28453
- if (input.action === "goal-manager" && input.message) {
28454
- return {
28455
- request: {
28456
- target: { type: "goal", id: input.message },
28457
- intent: "manage",
28458
- source: "dashboard"
28459
- }
28460
- };
28461
- }
28462
- if (input.action) {
28463
- return {
28464
- request: {
28465
- target: { type: "workflow", id: input.action },
28466
- intent: "run",
28467
- source: "dashboard"
28468
- }
28469
- };
28470
- }
28471
- return {
28472
- request: {
28473
- target: { type: "workflow", id: "scheduled-fanout" },
28474
- intent: "tick",
28475
- source: "schedule"
28476
- }
28477
- };
28478
- }
28479
28340
  async function defaultRunJob(job) {
28480
28341
  const workdir = process.env.RUNNER_WORKDIR ?? DEFAULT_WORKDIR;
28481
28342
  const branch = job.ref ?? "main";
@@ -28615,7 +28476,7 @@ async function runnerServe() {
28615
28476
  init_config();
28616
28477
  init_litellm();
28617
28478
  import { spawn as spawn10 } from "child_process";
28618
- function parseTarget2(positional) {
28479
+ function parseTarget(positional) {
28619
28480
  if (!Array.isArray(positional) || positional.length === 0) return "none";
28620
28481
  const first = String(positional[0]).toLowerCase();
28621
28482
  if (first === "vscode" || first === "code") return "vscode";
@@ -28630,7 +28491,7 @@ function buildProxyEnv(url) {
28630
28491
  };
28631
28492
  }
28632
28493
  async function serve(opts) {
28633
- const target = parseTarget2(opts.args);
28494
+ const target = parseTarget(opts.args);
28634
28495
  const model = parseProviderModel(opts.config.agent.model);
28635
28496
  const usesProxy = needsLitellmProxy(model);
28636
28497
  let handle = null;
@@ -27,6 +27,14 @@
27
27
  ],
28
28
  "postflight": []
29
29
  },
30
- "inputs": [],
30
+ "inputs": [
31
+ {
32
+ "name": "loop",
33
+ "flag": "--loop",
34
+ "type": "string",
35
+ "required": false,
36
+ "description": "Optional Loop id to run without dispatching other Loops."
37
+ }
38
+ ],
31
39
  "name": "loop-scheduler"
32
40
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kody-ade/kody-engine",
3
- "version": "0.4.466",
3
+ "version": "0.4.468",
4
4
  "description": "kody — autonomous development engine. Single-session Claude Code agent behind a generic executor + declarative implementation profiles.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -16,6 +16,7 @@
16
16
  "@actions/cache": "^6.0.0",
17
17
  "@anthropic-ai/claude-agent-sdk": "0.2.119",
18
18
  "@kody-ade/agency-domain": "0.5.1",
19
+ "@kody-ade/engine-contracts": "0.1.0",
19
20
  "@modelcontextprotocol/sdk": "^1.29.0",
20
21
  "ajv": "^8.18.0",
21
22
  "convex": "^1.17.0",