@kody-ade/kody-engine 0.4.467 → 0.4.469

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.467",
18
+ version: "0.4.469",
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
@@ -25322,6 +25282,16 @@ function routeRunRequest(request) {
25322
25282
  cliArgs: { goal: target.id }
25323
25283
  };
25324
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
+ }
25325
25295
  if (target.type === "workflow") {
25326
25296
  if (target.id === "scheduled-fanout") {
25327
25297
  return { kind: "fanout", force: intent === "run" };
@@ -25329,7 +25299,8 @@ function routeRunRequest(request) {
25329
25299
  if (intent !== "run" && intent !== "tick") {
25330
25300
  return { kind: "error", error: `workflow target does not support intent '${intent}'` };
25331
25301
  }
25332
- 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;
25333
25304
  return { kind: "action", action: target.id, cliArgs: {}, ...workflowRunId ? { workflowRunId } : {} };
25334
25305
  }
25335
25306
  return { kind: "error", error: "unsupported run request target" };
@@ -25590,6 +25561,7 @@ async function runCi(argv) {
25590
25561
  }
25591
25562
  if (parsedRunRequest && "request" in parsedRunRequest) {
25592
25563
  }
25564
+ const explicitLoopRequest = parsedRunRequest && "request" in parsedRunRequest && parsedRunRequest.request.target.type === "loop";
25593
25565
  if (!args.issueNumber && !autoFallback && parsedRunRequest && "request" in parsedRunRequest) {
25594
25566
  const route = routeRunRequest(parsedRunRequest.request);
25595
25567
  if (route.kind === "error") {
@@ -25660,11 +25632,11 @@ async function runCi(argv) {
25660
25632
  cliArgs: directExecution.cliArgs,
25661
25633
  source: "project-folder"
25662
25634
  } : null);
25663
- const workflowRoute = manualGoalManager || capabilityRoute || !readWorkflowDefinition(config, cwd, forceRunAction) ? void 0 : {
25635
+ const workflowRoute = manualGoalManager || capabilityRoute || explicitLoopRequest || !readWorkflowDefinition(config, cwd, forceRunAction) ? void 0 : {
25664
25636
  workflow: forceRunAction,
25665
25637
  cliArgs: {}
25666
25638
  };
25667
- const loop = manualGoalManager || capabilityRoute || workflowRoute ? null : readLoopDefinition(cwd, forceRunAction);
25639
+ const loop = manualGoalManager || capabilityRoute || workflowRoute || explicitLoopRequest ? null : readLoopDefinition(cwd, forceRunAction);
25668
25640
  const loopRoute = loop ? loop.target.kind === "workflow" ? { workflow: loop.target.id, cliArgs: loop.input } : { capability: loop.target.id, action: loop.target.id, cliArgs: loop.input } : void 0;
25669
25641
  const scheduledWatchRoute = manualGoalManager || capabilityRoute || workflowRoute || loopRoute ? void 0 : dispatchScheduledWatches({ force: true, cwd }).find(
25670
25642
  (match) => match.action === forceRunAction || match.capability === forceRunAction || match.implementation === forceRunAction
@@ -27541,10 +27513,12 @@ async function runAgencyLoopTick(deps) {
27541
27513
  for (const repository of repositories) {
27542
27514
  const [owner, repo] = repository.split("/");
27543
27515
  try {
27516
+ const jobId = `sched-${owner}-${repo}-${clock()}`;
27544
27517
  const result = await deps.claim(owner, repo, {
27545
- jobId: `sched-${owner}-${repo}-${clock()}`,
27518
+ jobId,
27546
27519
  repo: repository,
27547
27520
  runRequest: {
27521
+ requestId: jobId,
27548
27522
  target: { type: "workflow", id: "scheduled-fanout" },
27549
27523
  intent: "tick",
27550
27524
  source: "schedule"
@@ -28131,16 +28105,8 @@ function parseClaimRequest(body) {
28131
28105
  if (!jobId) return { error: "jobId required" };
28132
28106
  const repo = typeof b.repo === "string" ? b.repo.trim() : "";
28133
28107
  if (!/^[^/\s]+\/[^/\s]+$/.test(repo)) return { error: "repo must be 'owner/name'" };
28134
- const mode = b.mode === "interactive" ? "interactive" : b.mode === "scheduled" ? "scheduled" : "issue";
28135
- const action = typeof b.action === "string" && b.action.trim() ? b.action.trim() : void 0;
28136
- const message = typeof b.message === "string" && b.message.trim() ? b.message.trim() : void 0;
28137
- const runRequest = b.runRequest !== void 0 ? parseRunRequest(b.runRequest) : synthesizeLegacyClaimRequest({
28138
- mode,
28139
- issueNumber: b.issueNumber,
28140
- sessionId: b.sessionId,
28141
- action,
28142
- message
28143
- });
28108
+ if (b.runRequest === void 0) return { error: "runRequest required" };
28109
+ const runRequest = parseEngineExecutionRequest(b.runRequest);
28144
28110
  if ("error" in runRequest) return { error: runRequest.error };
28145
28111
  const req = { jobId, repo, runRequest: runRequest.request };
28146
28112
  if (req.runRequest.target.type === "issue") {
@@ -28155,55 +28121,6 @@ function parseClaimRequest(body) {
28155
28121
  if (typeof b.dashboardUrl === "string" && b.dashboardUrl.trim()) req.dashboardUrl = b.dashboardUrl.trim();
28156
28122
  return { req };
28157
28123
  }
28158
- function synthesizeLegacyClaimRequest(input) {
28159
- if (input.mode === "issue") {
28160
- const issueNumber = Number(input.issueNumber);
28161
- if (!Number.isInteger(issueNumber) || issueNumber <= 0) return { error: "issueNumber required for issue mode" };
28162
- return {
28163
- request: {
28164
- target: { type: "issue", id: issueNumber },
28165
- intent: "run",
28166
- source: "dashboard"
28167
- }
28168
- };
28169
- }
28170
- if (input.mode === "interactive") {
28171
- const sessionId = typeof input.sessionId === "string" ? input.sessionId.trim() : "";
28172
- if (!sessionId) return { error: "sessionId required for interactive mode" };
28173
- return {
28174
- request: {
28175
- target: { type: "chat", id: sessionId },
28176
- intent: "continue",
28177
- source: "dashboard"
28178
- }
28179
- };
28180
- }
28181
- if (input.action === "goal-manager" && input.message) {
28182
- return {
28183
- request: {
28184
- target: { type: "goal", id: input.message },
28185
- intent: "manage",
28186
- source: "dashboard"
28187
- }
28188
- };
28189
- }
28190
- if (input.action) {
28191
- return {
28192
- request: {
28193
- target: { type: "workflow", id: input.action },
28194
- intent: "run",
28195
- source: "dashboard"
28196
- }
28197
- };
28198
- }
28199
- return {
28200
- request: {
28201
- target: { type: "workflow", id: "scheduled-fanout" },
28202
- intent: "tick",
28203
- source: "schedule"
28204
- }
28205
- };
28206
- }
28207
28124
  async function poolServe() {
28208
28125
  const masterRaw = process.env.KODY_MASTER_KEY?.trim();
28209
28126
  if (!masterRaw) throw new Error("KODY_MASTER_KEY required for pool-serve");
@@ -28401,16 +28318,8 @@ function parseJob(body) {
28401
28318
  if (!/^[^/\s]+\/[^/\s]+$/.test(repo)) return { error: "repo must be 'owner/name'" };
28402
28319
  const githubToken = typeof b.githubToken === "string" ? b.githubToken.trim() : "";
28403
28320
  if (!githubToken) return { error: "githubToken required" };
28404
- const action = typeof b.action === "string" && b.action.trim() ? b.action.trim() : void 0;
28405
- const message = typeof b.message === "string" && b.message.trim() ? b.message.trim() : void 0;
28406
- const mode = b.mode === "interactive" ? "interactive" : b.mode === "scheduled" ? "scheduled" : "issue";
28407
- const runRequest = b.runRequest !== void 0 ? parseRunRequest(b.runRequest) : synthesizeLegacyRunRequest({
28408
- mode,
28409
- issueNumber: b.issueNumber,
28410
- sessionId: b.sessionId,
28411
- action,
28412
- message
28413
- });
28321
+ if (b.runRequest === void 0) return { error: "runRequest required" };
28322
+ const runRequest = parseEngineExecutionRequest(b.runRequest);
28414
28323
  if ("error" in runRequest) return { error: runRequest.error };
28415
28324
  const job = { jobId, repo, githubToken, runRequest: runRequest.request };
28416
28325
  if (job.runRequest.target.type === "issue") {
@@ -28429,57 +28338,6 @@ function parseJob(body) {
28429
28338
  }
28430
28339
  return { job };
28431
28340
  }
28432
- function synthesizeLegacyRunRequest(input) {
28433
- if (input.mode === "issue") {
28434
- const issueNumber = Number(input.issueNumber);
28435
- if (!Number.isInteger(issueNumber) || issueNumber <= 0) {
28436
- return { error: "issueNumber (positive integer) required for issue mode" };
28437
- }
28438
- return {
28439
- request: {
28440
- target: { type: "issue", id: issueNumber },
28441
- intent: "run",
28442
- source: "dashboard"
28443
- }
28444
- };
28445
- }
28446
- if (input.mode === "interactive") {
28447
- const sessionId = typeof input.sessionId === "string" ? input.sessionId.trim() : "";
28448
- if (!sessionId) return { error: "sessionId required for interactive mode" };
28449
- return {
28450
- request: {
28451
- target: { type: "chat", id: sessionId },
28452
- intent: "continue",
28453
- source: "dashboard"
28454
- }
28455
- };
28456
- }
28457
- if (input.action === "goal-manager" && input.message) {
28458
- return {
28459
- request: {
28460
- target: { type: "goal", id: input.message },
28461
- intent: "manage",
28462
- source: "dashboard"
28463
- }
28464
- };
28465
- }
28466
- if (input.action) {
28467
- return {
28468
- request: {
28469
- target: { type: "workflow", id: input.action },
28470
- intent: "run",
28471
- source: "dashboard"
28472
- }
28473
- };
28474
- }
28475
- return {
28476
- request: {
28477
- target: { type: "workflow", id: "scheduled-fanout" },
28478
- intent: "tick",
28479
- source: "schedule"
28480
- }
28481
- };
28482
- }
28483
28341
  async function defaultRunJob(job) {
28484
28342
  const workdir = process.env.RUNNER_WORKDIR ?? DEFAULT_WORKDIR;
28485
28343
  const branch = job.ref ?? "main";
@@ -28619,7 +28477,7 @@ async function runnerServe() {
28619
28477
  init_config();
28620
28478
  init_litellm();
28621
28479
  import { spawn as spawn10 } from "child_process";
28622
- function parseTarget2(positional) {
28480
+ function parseTarget(positional) {
28623
28481
  if (!Array.isArray(positional) || positional.length === 0) return "none";
28624
28482
  const first = String(positional[0]).toLowerCase();
28625
28483
  if (first === "vscode" || first === "code") return "vscode";
@@ -28634,7 +28492,7 @@ function buildProxyEnv(url) {
28634
28492
  };
28635
28493
  }
28636
28494
  async function serve(opts) {
28637
- const target = parseTarget2(opts.args);
28495
+ const target = parseTarget(opts.args);
28638
28496
  const model = parseProviderModel(opts.config.agent.model);
28639
28497
  const usesProxy = needsLitellmProxy(model);
28640
28498
  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.467",
3
+ "version": "0.4.469",
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",