@kody-ade/kody-engine 0.4.569 → 0.4.571

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.
Files changed (2) hide show
  1. package/dist/bin/kody.js +66 -29
  2. package/package.json +1 -1
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.569",
18
+ version: "0.4.571",
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",
@@ -150,6 +150,29 @@ var init_claudeBinary = __esm({
150
150
  }
151
151
  });
152
152
 
153
+ // src/completionGuard.ts
154
+ function completionToolCutoffAt(startedAtMs, deadlineAtMs) {
155
+ const availableMs = Math.max(0, deadlineAtMs - startedAtMs);
156
+ const reserveMs = Math.min(MAX_COMPLETION_RESERVE_MS, Math.floor(availableMs / 3));
157
+ return deadlineAtMs - reserveMs;
158
+ }
159
+ function createCompletionToolGuard(cutoffAtMs, now = Date.now) {
160
+ return async () => {
161
+ if (now() < cutoffAtMs) return {};
162
+ return {
163
+ decision: "block",
164
+ reason: "The run has entered its reserved completion window. Do not call more tools. Use the evidence and changes already present, state any verification limits clearly, and return your final response now."
165
+ };
166
+ };
167
+ }
168
+ var MAX_COMPLETION_RESERVE_MS;
169
+ var init_completionGuard = __esm({
170
+ "src/completionGuard.ts"() {
171
+ "use strict";
172
+ MAX_COMPLETION_RESERVE_MS = 10 * 6e4;
173
+ }
174
+ });
175
+
153
176
  // src/config.ts
154
177
  import * as fs2 from "fs";
155
178
  import * as path2 from "path";
@@ -548,6 +571,29 @@ var init_config = __esm({
548
571
  }
549
572
  });
550
573
 
574
+ // src/fileEditGuards.ts
575
+ import * as fs3 from "fs";
576
+ import * as path3 from "path";
577
+ function createMissingParentWriteGuard(cwd) {
578
+ return async (input) => {
579
+ const toolInput = input.tool_input;
580
+ if (!toolInput || typeof toolInput !== "object" || Array.isArray(toolInput)) return {};
581
+ const filePath = toolInput.file_path;
582
+ if (typeof filePath !== "string" || filePath.length === 0) return {};
583
+ const resolvedPath = path3.resolve(cwd, filePath);
584
+ if (fs3.existsSync(path3.dirname(resolvedPath))) return {};
585
+ return {
586
+ decision: "block",
587
+ reason: `Cannot write ${resolvedPath}: its parent directory does not exist. Locate and edit the real repository source path first. If this task genuinely requires a new directory, create that directory explicitly before writing the file.`
588
+ };
589
+ };
590
+ }
591
+ var init_fileEditGuards = __esm({
592
+ "src/fileEditGuards.ts"() {
593
+ "use strict";
594
+ }
595
+ });
596
+
551
597
  // src/format.ts
552
598
  function renderEvent(msg, opts = {}) {
553
599
  if (opts.quiet) {
@@ -669,29 +715,6 @@ var init_format = __esm({
669
715
  }
670
716
  });
671
717
 
672
- // src/fileEditGuards.ts
673
- import * as fs3 from "fs";
674
- import * as path3 from "path";
675
- function createMissingParentWriteGuard(cwd) {
676
- return async (input) => {
677
- const toolInput = input.tool_input;
678
- if (!toolInput || typeof toolInput !== "object" || Array.isArray(toolInput)) return {};
679
- const filePath = toolInput.file_path;
680
- if (typeof filePath !== "string" || filePath.length === 0) return {};
681
- const resolvedPath = path3.resolve(cwd, filePath);
682
- if (fs3.existsSync(path3.dirname(resolvedPath))) return {};
683
- return {
684
- decision: "block",
685
- reason: `Cannot write ${resolvedPath}: its parent directory does not exist. Locate and edit the real repository source path first. If this task genuinely requires a new directory, create that directory explicitly before writing the file.`
686
- };
687
- };
688
- }
689
- var init_fileEditGuards = __esm({
690
- "src/fileEditGuards.ts"() {
691
- "use strict";
692
- }
693
- });
694
-
695
718
  // src/agency/capability-contract-validation.ts
696
719
  import Ajv from "ajv";
697
720
  function createCapabilityContractValueValidator(compile) {
@@ -4062,6 +4085,7 @@ async function runAgent(opts) {
4062
4085
  }
4063
4086
  const startedAt = Date.now();
4064
4087
  const turnTimeoutMs = resolveTurnTimeoutMs(opts);
4088
+ const completionGuard = typeof opts.deadlineAtMs === "number" ? createCompletionToolGuard(completionToolCutoffAt(startedAt, opts.deadlineAtMs)) : null;
4065
4089
  let outcome = "failed";
4066
4090
  let outcomeKind = "generic_failed";
4067
4091
  let errorMessage2;
@@ -4106,6 +4130,11 @@ async function runAgent(opts) {
4106
4130
  env,
4107
4131
  hooks: {
4108
4132
  PreToolUse: [
4133
+ ...completionGuard ? [
4134
+ {
4135
+ hooks: [completionGuard]
4136
+ }
4137
+ ] : [],
4109
4138
  {
4110
4139
  matcher: "Agent",
4111
4140
  hooks: [enforceSubagentModelInheritance]
@@ -4229,11 +4258,13 @@ async function runAgent(opts) {
4229
4258
  } else if (typeof opts.maxThinkingTokens === "number" && opts.maxThinkingTokens > 0) {
4230
4259
  queryOptions.maxThinkingTokens = opts.maxThinkingTokens;
4231
4260
  }
4232
- if (typeof opts.systemPromptAppend === "string" && opts.systemPromptAppend.length > 0) {
4261
+ const completionNotice = completionGuard ? "Work within the enforced run time. Tool access closes before the hard deadline to reserve time for a final response. When that happens, stop using tools and finish immediately." : null;
4262
+ const systemPromptAppend = [opts.systemPromptAppend, completionNotice].filter((value) => typeof value === "string" && value.length > 0).join("\n\n");
4263
+ if (systemPromptAppend.length > 0) {
4233
4264
  const systemPrompt = {
4234
4265
  type: "preset",
4235
4266
  preset: "claude_code",
4236
- append: opts.systemPromptAppend
4267
+ append: systemPromptAppend
4237
4268
  };
4238
4269
  if (opts.cacheable) systemPrompt.excludeDynamicSections = true;
4239
4270
  queryOptions.systemPrompt = systemPrompt;
@@ -4463,9 +4494,10 @@ var init_agent = __esm({
4463
4494
  "src/agent.ts"() {
4464
4495
  "use strict";
4465
4496
  init_claudeBinary();
4497
+ init_completionGuard();
4466
4498
  init_config();
4467
- init_format();
4468
4499
  init_fileEditGuards();
4500
+ init_format();
4469
4501
  init_outputContractHooks();
4470
4502
  init_runtimePaths();
4471
4503
  init_subagents();
@@ -22138,6 +22170,7 @@ async function runImplementation(profileName, input) {
22138
22170
  verbose: input.verbose,
22139
22171
  quiet: input.quiet,
22140
22172
  abortController: input.abortController,
22173
+ deadlineAtMs: input.deadlineAtMs,
22141
22174
  ndjsonDir,
22142
22175
  additionalDirectories: agentTaskArtifacts ? [agentTaskArtifacts.absDir] : void 0,
22143
22176
  allowedToolsOverride: profile.claudeCode.tools,
@@ -23324,6 +23357,7 @@ async function runCapabilityImplementationStep(valid, profileName, capabilityIde
23324
23357
  verbose: base.verbose,
23325
23358
  quiet: base.quiet,
23326
23359
  abortController: base.abortController,
23360
+ deadlineAtMs: base.deadlineAtMs,
23327
23361
  preloadedData: Object.keys(preloadedData).length > 0 ? preloadedData : void 0
23328
23362
  };
23329
23363
  const shouldApplyResolvedCapabilityArgs = valid.implementation === void 0 && resolvedCapability && profileName === resolvedCapability.implementation;
@@ -23637,6 +23671,7 @@ async function runGraphCapabilityWorkflow(parent, workflow, capability, base, ch
23637
23671
  result = await runJob(child, {
23638
23672
  ...base,
23639
23673
  abortController: stepAbort.controller,
23674
+ deadlineAtMs: stepAbort.deadlineAtMs,
23640
23675
  preloadedData: {
23641
23676
  ...chainData,
23642
23677
  runSubjectType: "capability",
@@ -23966,8 +24001,9 @@ function canContinueWorkflow(step, outcome) {
23966
24001
  return step.continueOn.includes(outcome.type);
23967
24002
  }
23968
24003
  function workflowStepAbortController(parent, timeoutSeconds) {
23969
- if (!timeoutSeconds) return { controller: parent, cleanup: () => void 0 };
24004
+ if (!timeoutSeconds) return { controller: parent, deadlineAtMs: void 0, cleanup: () => void 0 };
23970
24005
  const controller = new AbortController();
24006
+ const deadlineAtMs = Date.now() + timeoutSeconds * 1e3;
23971
24007
  const forwardParentAbort = () => controller.abort(parent?.signal.reason);
23972
24008
  if (parent?.signal.aborted) forwardParentAbort();
23973
24009
  else parent?.signal.addEventListener("abort", forwardParentAbort, { once: true });
@@ -23977,6 +24013,7 @@ function workflowStepAbortController(parent, timeoutSeconds) {
23977
24013
  timer.unref?.();
23978
24014
  return {
23979
24015
  controller,
24016
+ deadlineAtMs,
23980
24017
  cleanup: () => {
23981
24018
  clearTimeout(timer);
23982
24019
  parent?.signal.removeEventListener("abort", forwardParentAbort);
@@ -23984,7 +24021,7 @@ function workflowStepAbortController(parent, timeoutSeconds) {
23984
24021
  };
23985
24022
  }
23986
24023
  function workflowOutcome(result) {
23987
- return result.taskState?.core.lastOutcome ?? result.action ?? null;
24024
+ return result.action ?? result.taskState?.core.lastOutcome ?? null;
23988
24025
  }
23989
24026
  function workflowStepDeadlineResult(result, controller, timeoutSeconds) {
23990
24027
  if (!timeoutSeconds || !controller?.signal.aborted || result.exitCode === 0) return result;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kody-ade/kody-engine",
3
- "version": "0.4.569",
3
+ "version": "0.4.571",
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",