@kody-ade/kody-engine 0.4.638 → 0.4.640

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.638",
18
+ version: "0.4.640",
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
  repository: {
@@ -180,49 +180,6 @@ var init_completionGuard = __esm({
180
180
  }
181
181
  });
182
182
 
183
- // src/submitStateGuard.ts
184
- function createSubmitStateActivityGuard() {
185
- let hasActivity = false;
186
- return {
187
- recordToolUse: async (input) => {
188
- if (input?.tool_name !== SUBMIT_STATE_TOOL) hasActivity = true;
189
- return {};
190
- },
191
- requireActivity: async () => hasActivity ? {} : {
192
- decision: "block",
193
- reason: ACTIVITY_REQUIRED_REASON
194
- }
195
- };
196
- }
197
- function createPostSubmitToolGuard(getSubmitted) {
198
- return async () => getSubmitted() ? {
199
- decision: "block",
200
- reason: STATE_ALREADY_SUBMITTED_REASON
201
- } : {};
202
- }
203
- function createSubmitStateStopHook(getSubmitted) {
204
- let finalChanceGiven = false;
205
- return async () => {
206
- if (getSubmitted()) return {};
207
- if (finalChanceGiven) return {};
208
- finalChanceGiven = true;
209
- return {
210
- decision: "block",
211
- reason: FINAL_STATE_CHANCE_REASON
212
- };
213
- };
214
- }
215
- var FINAL_STATE_CHANCE_REASON, STATE_ALREADY_SUBMITTED_REASON, ACTIVITY_REQUIRED_REASON, SUBMIT_STATE_TOOL;
216
- var init_submitStateGuard = __esm({
217
- "src/submitStateGuard.ts"() {
218
- "use strict";
219
- FINAL_STATE_CHANCE_REASON = "Before finishing, call `submit_state` exactly once with the next non-empty `cursor`, compact durable `data`, and the correct `done` value. Do not perform more work; submit only the continuation state now.";
220
- STATE_ALREADY_SUBMITTED_REASON = "Continuation state is already submitted for this cycle. Do not call more tools or perform more work; return your final response now.";
221
- ACTIVITY_REQUIRED_REASON = "Do not submit continuation state yet. First use a tool to observe current evidence or perform the next responsibility action for this cycle.";
222
- SUBMIT_STATE_TOOL = "mcp__kody-submit__submit_state";
223
- }
224
- });
225
-
226
183
  // src/config.ts
227
184
  import * as fs2 from "fs";
228
185
  import * as path3 from "path";
@@ -1193,6 +1150,49 @@ var init_subagents = __esm({
1193
1150
  }
1194
1151
  });
1195
1152
 
1153
+ // src/submitStateGuard.ts
1154
+ function createSubmitStateActivityGuard() {
1155
+ let hasActivity = false;
1156
+ return {
1157
+ recordToolUse: async (input) => {
1158
+ if (input?.tool_name !== SUBMIT_STATE_TOOL) hasActivity = true;
1159
+ return {};
1160
+ },
1161
+ requireActivity: async () => hasActivity ? {} : {
1162
+ decision: "block",
1163
+ reason: ACTIVITY_REQUIRED_REASON
1164
+ }
1165
+ };
1166
+ }
1167
+ function createPostSubmitToolGuard(getSubmitted) {
1168
+ return async () => getSubmitted() ? {
1169
+ decision: "block",
1170
+ reason: STATE_ALREADY_SUBMITTED_REASON
1171
+ } : {};
1172
+ }
1173
+ function createSubmitStateStopHook(getSubmitted) {
1174
+ let finalChanceGiven = false;
1175
+ return async () => {
1176
+ if (getSubmitted()) return {};
1177
+ if (finalChanceGiven) return {};
1178
+ finalChanceGiven = true;
1179
+ return {
1180
+ decision: "block",
1181
+ reason: FINAL_STATE_CHANCE_REASON
1182
+ };
1183
+ };
1184
+ }
1185
+ var FINAL_STATE_CHANCE_REASON, STATE_ALREADY_SUBMITTED_REASON, ACTIVITY_REQUIRED_REASON, SUBMIT_STATE_TOOL;
1186
+ var init_submitStateGuard = __esm({
1187
+ "src/submitStateGuard.ts"() {
1188
+ "use strict";
1189
+ FINAL_STATE_CHANCE_REASON = "Before finishing, call `submit_state` exactly once with the next non-empty `cursor`, compact durable `data`, and the correct `done` value. Do not perform more work; submit only the continuation state now.";
1190
+ STATE_ALREADY_SUBMITTED_REASON = "Continuation state is already submitted for this cycle. Do not call more tools or perform more work; return your final response now.";
1191
+ ACTIVITY_REQUIRED_REASON = "Do not submit continuation state yet. First use a tool to observe current evidence or perform the next responsibility action for this cycle.";
1192
+ SUBMIT_STATE_TOOL = "mcp__kody-submit__submit_state";
1193
+ }
1194
+ });
1195
+
1196
1196
  // src/events.ts
1197
1197
  var events_exports = {};
1198
1198
  __export(events_exports, {
@@ -3589,10 +3589,19 @@ __export(capabilityMcp_exports, {
3589
3589
  parseCapabilityTrustMode: () => parseCapabilityTrustMode,
3590
3590
  readCheckRuns: () => readCheckRuns,
3591
3591
  readThread: () => readThread,
3592
+ selectCapabilityToolDefinitions: () => selectCapabilityToolDefinitions,
3592
3593
  startCapability: () => startCapability
3593
3594
  });
3594
3595
  import { createSdkMcpServer as createSdkMcpServer4, tool as tool4 } from "@anthropic-ai/claude-agent-sdk";
3595
3596
  import { z as z3 } from "zod";
3597
+ function selectCapabilityToolDefinitions(definitions, allowedToolNames) {
3598
+ if (!allowedToolNames) return definitions;
3599
+ const available = new Set(definitions.map((definition) => definition.name));
3600
+ const unknown = allowedToolNames.filter((name) => !available.has(name));
3601
+ if (unknown.length > 0) throw new Error(`Unknown capability MCP tools: ${unknown.join(", ")}`);
3602
+ const allowed = new Set(allowedToolNames);
3603
+ return definitions.filter((definition) => allowed.has(definition.name));
3604
+ }
3596
3605
  function summarizeCiStatus(rollup) {
3597
3606
  if (!Array.isArray(rollup) || rollup.length === 0) return "UNKNOWN";
3598
3607
  let hasRunning = false;
@@ -4099,46 +4108,71 @@ function capabilityToolDefinitions(opts) {
4099
4108
  const reportRunId = typeof args.reportRunId === "string" ? args.reportRunId : void 0;
4100
4109
  const evidence = typeof args.evidence === "string" ? args.evidence.trim() : "";
4101
4110
  const backend = createStateBackendFromEnv();
4102
- const existing = await backend.getRepoDoc(opts.repoSlug, `todo:${slug}`);
4103
4111
  const now = (/* @__PURE__ */ new Date()).toISOString();
4104
- const current = existing?.doc && typeof existing.doc === "object" && !Array.isArray(existing.doc) ? existing.doc : {};
4105
- const currentItems = Array.isArray(current.items) ? current.items.filter((item) => Boolean(item && typeof item === "object" && !Array.isArray(item))) : [];
4106
- const previous = currentItems.find((item) => item.id === itemId);
4107
4112
  const completed = status === "resolved";
4108
- const previousMeta = previous?.meta && typeof previous.meta === "object" && !Array.isArray(previous.meta) ? previous.meta : {};
4109
- const unchanged = Boolean(
4110
- previous && previous.completed === completed && previous.title === title && String(previous.body ?? "") === evidence && previousMeta.reportSlug === reportSlug
4111
- );
4112
- if (unchanged) {
4113
- return { content: [{ type: "text", text: JSON.stringify({ changed: false, slug, status }) }] };
4114
- }
4115
- const nextItem = {
4116
- id: itemId,
4117
- title,
4118
- body: evidence,
4119
- assignee: null,
4120
- completed,
4121
- createdAt: typeof previous?.createdAt === "string" ? previous.createdAt : now,
4122
- completedAt: completed ? now : null,
4123
- meta: {
4124
- ...previousMeta,
4125
- source: "live-agent",
4126
- reportSlug,
4127
- ...reportRunId ? { reportRunId } : {},
4128
- status
4113
+ let lastError = new Error(`Todo ${slug}/${itemId} was not visible after reconciliation`);
4114
+ for (let attempt = 0; attempt < 3; attempt += 1) {
4115
+ try {
4116
+ const existing = await backend.getRepoDoc(opts.repoSlug, `todo:${slug}`);
4117
+ const current = existing?.doc && typeof existing.doc === "object" && !Array.isArray(existing.doc) ? existing.doc : {};
4118
+ const currentItems = Array.isArray(current.items) ? current.items.filter(
4119
+ (item) => Boolean(item && typeof item === "object" && !Array.isArray(item))
4120
+ ) : [];
4121
+ const previous = currentItems.find((item) => item.id === itemId);
4122
+ const previousMeta = previous?.meta && typeof previous.meta === "object" && !Array.isArray(previous.meta) ? previous.meta : {};
4123
+ const unchanged = Boolean(
4124
+ previous && previous.completed === completed && previous.title === title && String(previous.body ?? "") === evidence && previousMeta.reportSlug === reportSlug
4125
+ );
4126
+ if (unchanged) {
4127
+ return {
4128
+ content: [{ type: "text", text: JSON.stringify({ changed: false, verified: true, slug, status }) }]
4129
+ };
4130
+ }
4131
+ const nextItem = {
4132
+ id: itemId,
4133
+ title,
4134
+ body: evidence,
4135
+ assignee: null,
4136
+ completed,
4137
+ createdAt: typeof previous?.createdAt === "string" ? previous.createdAt : now,
4138
+ completedAt: completed ? now : null,
4139
+ meta: {
4140
+ ...previousMeta,
4141
+ source: "live-agent",
4142
+ reportSlug,
4143
+ ...reportRunId ? { reportRunId } : {},
4144
+ status
4145
+ }
4146
+ };
4147
+ const items = previous ? currentItems.map((item) => item.id === itemId ? nextItem : item) : [...currentItems, nextItem];
4148
+ const doc = {
4149
+ ...current,
4150
+ version: 1,
4151
+ title,
4152
+ description,
4153
+ createdAt: typeof current.createdAt === "string" ? current.createdAt : now,
4154
+ items
4155
+ };
4156
+ await backend.saveRepoDoc(opts.repoSlug, `todo:${slug}`, doc, existing?.updatedAt);
4157
+ const saved = await backend.getRepoDoc(opts.repoSlug, `todo:${slug}`);
4158
+ const savedItems = saved?.doc && typeof saved.doc === "object" && !Array.isArray(saved.doc) && Array.isArray(saved.doc.items) ? saved.doc.items : [];
4159
+ const savedItem = savedItems.find(
4160
+ (item) => Boolean(
4161
+ item && typeof item === "object" && !Array.isArray(item) && item.id === itemId
4162
+ )
4163
+ );
4164
+ const savedMeta = savedItem?.meta && typeof savedItem.meta === "object" && !Array.isArray(savedItem.meta) ? savedItem.meta : {};
4165
+ if (savedItem && savedItem.completed === completed && savedItem.title === title && String(savedItem.body ?? "") === evidence && savedMeta.reportSlug === reportSlug) {
4166
+ return {
4167
+ content: [{ type: "text", text: JSON.stringify({ changed: true, verified: true, slug, status }) }]
4168
+ };
4169
+ }
4170
+ lastError = new Error(`Todo ${slug}/${itemId} was not visible after reconciliation attempt ${attempt + 1}`);
4171
+ } catch (error) {
4172
+ lastError = error;
4129
4173
  }
4130
- };
4131
- const items = previous ? currentItems.map((item) => item.id === itemId ? nextItem : item) : [...currentItems, nextItem];
4132
- const doc = {
4133
- ...current,
4134
- version: 1,
4135
- title,
4136
- description,
4137
- createdAt: typeof current.createdAt === "string" ? current.createdAt : now,
4138
- items
4139
- };
4140
- await backend.saveRepoDoc(opts.repoSlug, `todo:${slug}`, doc, existing?.updatedAt);
4141
- return { content: [{ type: "text", text: JSON.stringify({ changed: true, slug, status }) }] };
4174
+ }
4175
+ throw lastError;
4142
4176
  }
4143
4177
  };
4144
4178
  const cmsTools = dashboardCmsToolDefinitions({
@@ -4163,7 +4197,7 @@ function capabilityToolDefinitions(opts) {
4163
4197
  ];
4164
4198
  }
4165
4199
  function buildCapabilityMcpServer(opts) {
4166
- const definitions = capabilityToolDefinitions(opts);
4200
+ const definitions = selectCapabilityToolDefinitions(capabilityToolDefinitions(opts), opts.allowedToolNames);
4167
4201
  const tools = definitions.map(
4168
4202
  (def) => tool4(
4169
4203
  def.name,
@@ -4186,8 +4220,8 @@ var init_capabilityMcp = __esm({
4186
4220
  init_dashboardCmsMcp();
4187
4221
  init_issue();
4188
4222
  init_registry();
4189
- init_trustPolicy();
4190
4223
  init_state_backend();
4224
+ init_trustPolicy();
4191
4225
  FAIL_CONCLUSIONS = /* @__PURE__ */ new Set(["FAILURE", "TIMED_OUT", "ACTION_REQUIRED", "STARTUP_FAILURE", "CANCELLED"]);
4192
4226
  RUNNING_STATUSES = /* @__PURE__ */ new Set(["IN_PROGRESS", "QUEUED", "PENDING", "WAITING", "REQUESTED"]);
4193
4227
  THREAD_BODY_MAX = 4e3;
@@ -4588,6 +4622,7 @@ async function runAgent(opts) {
4588
4622
  const capabilityHandle = buildCapabilityMcpServer2({
4589
4623
  repoSlug: opts.capabilityRepoSlug,
4590
4624
  operatorMention: opts.capabilityOperatorMention ?? "",
4625
+ ...opts.capabilityToolNames ? { allowedToolNames: opts.capabilityToolNames } : {},
4591
4626
  ...opts.capabilityDefaultBranch ? { defaultBranch: opts.capabilityDefaultBranch } : {},
4592
4627
  ...opts.capabilitySlug ? { capabilitySlug: opts.capabilitySlug } : {}
4593
4628
  });
@@ -4904,13 +4939,13 @@ var init_agent = __esm({
4904
4939
  "use strict";
4905
4940
  init_claudeBinary();
4906
4941
  init_completionGuard();
4907
- init_submitStateGuard();
4908
4942
  init_config();
4909
4943
  init_fileEditGuards();
4910
4944
  init_format();
4911
4945
  init_outputContractHooks();
4912
4946
  init_runtimePaths();
4913
4947
  init_subagents();
4948
+ init_submitStateGuard();
4914
4949
  DEFAULT_ALLOWED_TOOLS = ["Bash", "Edit", "Read", "Write", "Glob", "Grep"];
4915
4950
  DEFAULT_TURN_TIMEOUT_MS = 6e5;
4916
4951
  MAX_CONNECTION_RETRIES = 2;
@@ -23396,6 +23431,7 @@ async function runImplementation(profileName, input) {
23396
23431
  // to know the palette — it just forwards the flag so agent.ts can spin
23397
23432
  // up the in-process `kody-capability` MCP server with the right context.
23398
23433
  enableCapabilityTool: Array.isArray(ctx.data.capabilityTools) && ctx.data.capabilityTools.length > 0,
23434
+ capabilityToolNames: Array.isArray(ctx.data.capabilityTools) ? ctx.data.capabilityTools.filter((name) => typeof name === "string") : void 0,
23399
23435
  enableDashboardCmsTool: Boolean(
23400
23436
  ctx.data.capabilityRequirements && typeof ctx.data.capabilityRequirements === "object" && !Array.isArray(ctx.data.capabilityRequirements) && ctx.data.capabilityRequirements.cms === true
23401
23437
  ),
File without changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kody-ade/kody-engine",
3
- "version": "0.4.638",
3
+ "version": "0.4.640",
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
  "repository": {
@@ -17,6 +17,30 @@
17
17
  "docs/delivery-boundary.md",
18
18
  "kody.config.schema.json"
19
19
  ],
20
+ "scripts": {
21
+ "kody:run": "tsx bin/kody.ts",
22
+ "serve": "tsx bin/kody.ts serve",
23
+ "serve:vscode": "tsx bin/kody.ts serve vscode",
24
+ "serve:claude": "tsx bin/kody.ts serve claude",
25
+ "clean:dist": "node scripts/clean-dist.cjs",
26
+ "build": "pnpm clean:dist && tsup && node scripts/copy-assets.cjs",
27
+ "check:modularity": "tsx scripts/check-script-modularity.ts",
28
+ "pretest": "pnpm check:modularity",
29
+ "test": "vitest run tests/unit tests/int --coverage",
30
+ "posttest": "tsx scripts/check-coverage-floor.ts",
31
+ "test:smoke": "vitest run tests/smoke --no-coverage",
32
+ "test:e2e": "vitest run tests/e2e --no-coverage",
33
+ "verify:live-release": "tsx scripts/live-release-gate.ts",
34
+ "test:runtime-services": "node --test \"tests/runtime-services/*.test.mjs\"",
35
+ "test:all": "vitest run tests --no-coverage",
36
+ "typecheck": "tsc --noEmit",
37
+ "lint": "biome check",
38
+ "lint:fix": "biome check --write",
39
+ "format": "biome format --write",
40
+ "verify:package": "node scripts/verify-package-tarball.cjs",
41
+ "brain:publish": "docker buildx build --platform linux/amd64 -f runner/Dockerfile.brain --build-arg KODY_ENGINE_REF=$(git rev-parse HEAD) -t ghcr.io/${KODY_BRAIN_GHCR_OWNER:-aharonyaircohen}/kody-brain:latest --push runner",
42
+ "prepublishOnly": "pnpm typecheck && pnpm test:runtime-services && pnpm build && pnpm verify:package"
43
+ },
20
44
  "dependencies": {
21
45
  "@actions/cache": "^6.0.0",
22
46
  "@anthropic-ai/claude-agent-sdk": "0.2.119",
@@ -39,28 +63,5 @@
39
63
  "node": ">=22"
40
64
  },
41
65
  "homepage": "https://github.com/aharonyaircohen/kody-engine",
42
- "bugs": "https://github.com/aharonyaircohen/kody-engine/issues",
43
- "scripts": {
44
- "kody:run": "tsx bin/kody.ts",
45
- "serve": "tsx bin/kody.ts serve",
46
- "serve:vscode": "tsx bin/kody.ts serve vscode",
47
- "serve:claude": "tsx bin/kody.ts serve claude",
48
- "clean:dist": "node scripts/clean-dist.cjs",
49
- "build": "pnpm clean:dist && tsup && node scripts/copy-assets.cjs",
50
- "check:modularity": "tsx scripts/check-script-modularity.ts",
51
- "pretest": "pnpm check:modularity",
52
- "test": "vitest run tests/unit tests/int --coverage",
53
- "posttest": "tsx scripts/check-coverage-floor.ts",
54
- "test:smoke": "vitest run tests/smoke --no-coverage",
55
- "test:e2e": "vitest run tests/e2e --no-coverage",
56
- "verify:live-release": "tsx scripts/live-release-gate.ts",
57
- "test:runtime-services": "node --test \"tests/runtime-services/*.test.mjs\"",
58
- "test:all": "vitest run tests --no-coverage",
59
- "typecheck": "tsc --noEmit",
60
- "lint": "biome check",
61
- "lint:fix": "biome check --write",
62
- "format": "biome format --write",
63
- "verify:package": "node scripts/verify-package-tarball.cjs",
64
- "brain:publish": "docker buildx build --platform linux/amd64 -f runner/Dockerfile.brain --build-arg KODY_ENGINE_REF=$(git rev-parse HEAD) -t ghcr.io/${KODY_BRAIN_GHCR_OWNER:-aharonyaircohen}/kody-brain:latest --push runner"
65
- }
66
- }
66
+ "bugs": "https://github.com/aharonyaircohen/kody-engine/issues"
67
+ }