@kody-ade/kody-engine 0.4.467 → 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.467",
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
@@ -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" };
@@ -27541,10 +27512,12 @@ async function runAgencyLoopTick(deps) {
27541
27512
  for (const repository of repositories) {
27542
27513
  const [owner, repo] = repository.split("/");
27543
27514
  try {
27515
+ const jobId = `sched-${owner}-${repo}-${clock()}`;
27544
27516
  const result = await deps.claim(owner, repo, {
27545
- jobId: `sched-${owner}-${repo}-${clock()}`,
27517
+ jobId,
27546
27518
  repo: repository,
27547
27519
  runRequest: {
27520
+ requestId: jobId,
27548
27521
  target: { type: "workflow", id: "scheduled-fanout" },
27549
27522
  intent: "tick",
27550
27523
  source: "schedule"
@@ -28131,16 +28104,8 @@ function parseClaimRequest(body) {
28131
28104
  if (!jobId) return { error: "jobId required" };
28132
28105
  const repo = typeof b.repo === "string" ? b.repo.trim() : "";
28133
28106
  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
- });
28107
+ if (b.runRequest === void 0) return { error: "runRequest required" };
28108
+ const runRequest = parseEngineExecutionRequest(b.runRequest);
28144
28109
  if ("error" in runRequest) return { error: runRequest.error };
28145
28110
  const req = { jobId, repo, runRequest: runRequest.request };
28146
28111
  if (req.runRequest.target.type === "issue") {
@@ -28155,55 +28120,6 @@ function parseClaimRequest(body) {
28155
28120
  if (typeof b.dashboardUrl === "string" && b.dashboardUrl.trim()) req.dashboardUrl = b.dashboardUrl.trim();
28156
28121
  return { req };
28157
28122
  }
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
28123
  async function poolServe() {
28208
28124
  const masterRaw = process.env.KODY_MASTER_KEY?.trim();
28209
28125
  if (!masterRaw) throw new Error("KODY_MASTER_KEY required for pool-serve");
@@ -28401,16 +28317,8 @@ function parseJob(body) {
28401
28317
  if (!/^[^/\s]+\/[^/\s]+$/.test(repo)) return { error: "repo must be 'owner/name'" };
28402
28318
  const githubToken = typeof b.githubToken === "string" ? b.githubToken.trim() : "";
28403
28319
  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
- });
28320
+ if (b.runRequest === void 0) return { error: "runRequest required" };
28321
+ const runRequest = parseEngineExecutionRequest(b.runRequest);
28414
28322
  if ("error" in runRequest) return { error: runRequest.error };
28415
28323
  const job = { jobId, repo, githubToken, runRequest: runRequest.request };
28416
28324
  if (job.runRequest.target.type === "issue") {
@@ -28429,57 +28337,6 @@ function parseJob(body) {
28429
28337
  }
28430
28338
  return { job };
28431
28339
  }
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
28340
  async function defaultRunJob(job) {
28484
28341
  const workdir = process.env.RUNNER_WORKDIR ?? DEFAULT_WORKDIR;
28485
28342
  const branch = job.ref ?? "main";
@@ -28619,7 +28476,7 @@ async function runnerServe() {
28619
28476
  init_config();
28620
28477
  init_litellm();
28621
28478
  import { spawn as spawn10 } from "child_process";
28622
- function parseTarget2(positional) {
28479
+ function parseTarget(positional) {
28623
28480
  if (!Array.isArray(positional) || positional.length === 0) return "none";
28624
28481
  const first = String(positional[0]).toLowerCase();
28625
28482
  if (first === "vscode" || first === "code") return "vscode";
@@ -28634,7 +28491,7 @@ function buildProxyEnv(url) {
28634
28491
  };
28635
28492
  }
28636
28493
  async function serve(opts) {
28637
- const target = parseTarget2(opts.args);
28494
+ const target = parseTarget(opts.args);
28638
28495
  const model = parseProviderModel(opts.config.agent.model);
28639
28496
  const usesProxy = needsLitellmProxy(model);
28640
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.467",
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",