@kody-ade/kody-engine 0.4.137 → 0.4.139
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
|
@@ -312,6 +312,52 @@ var init_verifyMcp = __esm({
|
|
|
312
312
|
}
|
|
313
313
|
});
|
|
314
314
|
|
|
315
|
+
// src/submitMcp.ts
|
|
316
|
+
var submitMcp_exports = {};
|
|
317
|
+
__export(submitMcp_exports, {
|
|
318
|
+
buildSubmitMcpServer: () => buildSubmitMcpServer
|
|
319
|
+
});
|
|
320
|
+
import { createSdkMcpServer as createSdkMcpServer2, tool as tool2 } from "@anthropic-ai/claude-agent-sdk";
|
|
321
|
+
import { z as z2 } from "zod";
|
|
322
|
+
function buildSubmitMcpServer() {
|
|
323
|
+
let submitted;
|
|
324
|
+
const submitTool = tool2(
|
|
325
|
+
"submit_state",
|
|
326
|
+
"Persist this tick's next state. Call this EXACTLY ONCE, at the very end, when you've finished your work \u2014 it is the ONLY way your decision is saved. Pass your next `cursor` (string), your next `data` (object \u2014 carry prior data forward and mutate what you acted on), and `done` (boolean). After calling it you are finished; do not take further actions.",
|
|
327
|
+
{
|
|
328
|
+
cursor: z2.string().describe('The next cursor value (e.g. "idle"). Must be a non-empty string.'),
|
|
329
|
+
data: z2.record(z2.string(), z2.unknown()).describe("The next `data` object. Carry forward prior data and mutate only what you acted on this tick."),
|
|
330
|
+
done: z2.boolean().describe("true only if this duty is permanently finished; evergreen duties stay false.")
|
|
331
|
+
},
|
|
332
|
+
async (args) => {
|
|
333
|
+
submitted = {
|
|
334
|
+
cursor: String(args.cursor ?? ""),
|
|
335
|
+
data: args.data ?? {},
|
|
336
|
+
done: Boolean(args.done)
|
|
337
|
+
};
|
|
338
|
+
return {
|
|
339
|
+
content: [
|
|
340
|
+
{
|
|
341
|
+
type: "text",
|
|
342
|
+
text: "State recorded. You are done for this tick \u2014 no further action needed."
|
|
343
|
+
}
|
|
344
|
+
]
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
);
|
|
348
|
+
const server = createSdkMcpServer2({
|
|
349
|
+
name: "kody-submit",
|
|
350
|
+
version: "0.1.0",
|
|
351
|
+
tools: [submitTool]
|
|
352
|
+
});
|
|
353
|
+
return { server, getSubmitted: () => submitted };
|
|
354
|
+
}
|
|
355
|
+
var init_submitMcp = __esm({
|
|
356
|
+
"src/submitMcp.ts"() {
|
|
357
|
+
"use strict";
|
|
358
|
+
}
|
|
359
|
+
});
|
|
360
|
+
|
|
315
361
|
// src/issue.ts
|
|
316
362
|
import { execFileSync } from "child_process";
|
|
317
363
|
function ghToken() {
|
|
@@ -880,7 +926,7 @@ var init_loadPriorArt = __esm({
|
|
|
880
926
|
// package.json
|
|
881
927
|
var package_default = {
|
|
882
928
|
name: "@kody-ade/kody-engine",
|
|
883
|
-
version: "0.4.
|
|
929
|
+
version: "0.4.139",
|
|
884
930
|
description: "kody \u2014 autonomous development engine. Single-session Claude Code agent behind a generic executor + declarative executable profiles.",
|
|
885
931
|
license: "MIT",
|
|
886
932
|
type: "module",
|
|
@@ -1514,6 +1560,7 @@ async function runAgent(opts) {
|
|
|
1514
1560
|
const turnTimeoutMs = resolveTurnTimeoutMs(opts);
|
|
1515
1561
|
let ndjsonWriteFailed = false;
|
|
1516
1562
|
let ndjsonWriteError;
|
|
1563
|
+
let getSubmitted;
|
|
1517
1564
|
try {
|
|
1518
1565
|
const queryOptions = {
|
|
1519
1566
|
model: opts.model.model,
|
|
@@ -1541,6 +1588,12 @@ async function runAgent(opts) {
|
|
|
1541
1588
|
});
|
|
1542
1589
|
mcpEntries.push(["kody-verify", verifyServer]);
|
|
1543
1590
|
}
|
|
1591
|
+
if (opts.enableSubmitTool) {
|
|
1592
|
+
const { buildSubmitMcpServer: buildSubmitMcpServer2 } = await Promise.resolve().then(() => (init_submitMcp(), submitMcp_exports));
|
|
1593
|
+
const submitHandle = buildSubmitMcpServer2();
|
|
1594
|
+
getSubmitted = submitHandle.getSubmitted;
|
|
1595
|
+
mcpEntries.push(["kody-submit", submitHandle.server]);
|
|
1596
|
+
}
|
|
1544
1597
|
if (mcpEntries.length > 0) {
|
|
1545
1598
|
queryOptions.mcpServers = Object.fromEntries(mcpEntries);
|
|
1546
1599
|
}
|
|
@@ -1706,10 +1759,12 @@ async function runAgent(opts) {
|
|
|
1706
1759
|
`);
|
|
1707
1760
|
}
|
|
1708
1761
|
const finalText = resultTexts.join("\n\n---\n\n");
|
|
1762
|
+
const submittedState = getSubmitted?.();
|
|
1709
1763
|
return {
|
|
1710
1764
|
outcome,
|
|
1711
1765
|
outcomeKind,
|
|
1712
1766
|
finalText,
|
|
1767
|
+
...submittedState ? { submittedState } : {},
|
|
1713
1768
|
error: errorMessage,
|
|
1714
1769
|
ndjsonPath,
|
|
1715
1770
|
durationMs: Date.now() - startedAt,
|
|
@@ -3186,6 +3241,7 @@ function parseClaudeCode(p, raw) {
|
|
|
3186
3241
|
systemPromptAppend: typeof r.systemPromptAppend === "string" ? r.systemPromptAppend : null,
|
|
3187
3242
|
cacheable: r.cacheable === true,
|
|
3188
3243
|
enableVerifyTool: r.enableVerifyTool === true,
|
|
3244
|
+
enableSubmitTool: r.enableSubmitTool === true,
|
|
3189
3245
|
verifyAttempts: typeof r.verifyAttempts === "number" && r.verifyAttempts > 0 ? r.verifyAttempts : null,
|
|
3190
3246
|
tools,
|
|
3191
3247
|
hooks: Array.isArray(r.hooks) ? r.hooks : [],
|
|
@@ -7879,7 +7935,7 @@ function getRecentFailedRunsForPr(prNumber, limit, cwd) {
|
|
|
7879
7935
|
} catch {
|
|
7880
7936
|
return [];
|
|
7881
7937
|
}
|
|
7882
|
-
if (!headBranch
|
|
7938
|
+
if (!headBranch) return [];
|
|
7883
7939
|
try {
|
|
7884
7940
|
const out = gh3(
|
|
7885
7941
|
[
|
|
@@ -7898,7 +7954,7 @@ function getRecentFailedRunsForPr(prNumber, limit, cwd) {
|
|
|
7898
7954
|
);
|
|
7899
7955
|
const parsed = JSON.parse(out);
|
|
7900
7956
|
if (!Array.isArray(parsed)) return [];
|
|
7901
|
-
|
|
7957
|
+
const runs = parsed.map((r) => ({
|
|
7902
7958
|
id: String(r.databaseId ?? ""),
|
|
7903
7959
|
workflowName: r.workflowName ?? "",
|
|
7904
7960
|
headBranch: r.headBranch ?? headBranch,
|
|
@@ -7906,7 +7962,8 @@ function getRecentFailedRunsForPr(prNumber, limit, cwd) {
|
|
|
7906
7962
|
conclusion: r.conclusion ?? "failure",
|
|
7907
7963
|
url: r.url ?? "",
|
|
7908
7964
|
createdAt: r.createdAt ?? ""
|
|
7909
|
-
}))
|
|
7965
|
+
}));
|
|
7966
|
+
return runs.sort((a, b) => (a.headSha === headSha ? 0 : 1) - (b.headSha === headSha ? 0 : 1));
|
|
7910
7967
|
} catch {
|
|
7911
7968
|
return [];
|
|
7912
7969
|
}
|
|
@@ -7970,7 +8027,7 @@ var fixCiFlow = async (ctx) => {
|
|
|
7970
8027
|
bail(
|
|
7971
8028
|
ctx,
|
|
7972
8029
|
prNumber,
|
|
7973
|
-
`no actionable failed workflow run found for PR #${prNumber}
|
|
8030
|
+
`no actionable failed workflow run found for PR #${prNumber} (scanned up to ${RUN_LOOKBACK} recent failed runs on the branch, head commit first \u2014 none were actionable CI failures with fetchable logs; pass --run-id to target a specific run)`
|
|
7974
8031
|
);
|
|
7975
8032
|
return;
|
|
7976
8033
|
}
|
|
@@ -9312,6 +9369,17 @@ var parseJobStateFromAgentResult = async (ctx, _profile, agentResult, args) => {
|
|
|
9312
9369
|
}
|
|
9313
9370
|
const loaded = ctx.data.jobState;
|
|
9314
9371
|
const prevRev = loaded?.state.rev ?? 0;
|
|
9372
|
+
const submitted = agentResult.submittedState;
|
|
9373
|
+
if (submitted && typeof submitted.cursor === "string" && submitted.cursor.length > 0) {
|
|
9374
|
+
ctx.data.nextJobState = {
|
|
9375
|
+
version: 1,
|
|
9376
|
+
rev: prevRev + 1,
|
|
9377
|
+
cursor: submitted.cursor,
|
|
9378
|
+
data: submitted.data ?? {},
|
|
9379
|
+
done: Boolean(submitted.done)
|
|
9380
|
+
};
|
|
9381
|
+
return;
|
|
9382
|
+
}
|
|
9315
9383
|
const result = extractNextStateFromText(agentResult.finalText, fenceLabel, prevRev);
|
|
9316
9384
|
if (result.error) {
|
|
9317
9385
|
ctx.data.nextStateParseError = result.error.startsWith("missing `") ? `agent did not emit a \`${fenceLabel}\` fenced block` : result.error;
|
|
@@ -12511,21 +12579,21 @@ function firstRequiredFailure(results, tools) {
|
|
|
12511
12579
|
}
|
|
12512
12580
|
return null;
|
|
12513
12581
|
}
|
|
12514
|
-
function verifyOne(
|
|
12515
|
-
const result = { name:
|
|
12516
|
-
let present = runShell(
|
|
12517
|
-
if (!present &&
|
|
12518
|
-
runShell(
|
|
12519
|
-
present = runShell(
|
|
12582
|
+
function verifyOne(tool3, cwd) {
|
|
12583
|
+
const result = { name: tool3.name, present: false, verified: false };
|
|
12584
|
+
let present = runShell(tool3.install.checkCommand, cwd);
|
|
12585
|
+
if (!present && tool3.install.installCommand) {
|
|
12586
|
+
runShell(tool3.install.installCommand, cwd, 12e4);
|
|
12587
|
+
present = runShell(tool3.install.checkCommand, cwd);
|
|
12520
12588
|
}
|
|
12521
12589
|
result.present = present;
|
|
12522
12590
|
if (!present) {
|
|
12523
|
-
result.error = `tool "${
|
|
12591
|
+
result.error = `tool "${tool3.name}" not on PATH (check: ${tool3.install.checkCommand})`;
|
|
12524
12592
|
return result;
|
|
12525
12593
|
}
|
|
12526
|
-
const verified = runShell(
|
|
12594
|
+
const verified = runShell(tool3.verify, cwd);
|
|
12527
12595
|
result.verified = verified;
|
|
12528
|
-
if (!verified) result.error = `tool "${
|
|
12596
|
+
if (!verified) result.error = `tool "${tool3.name}" failed verify: ${tool3.verify}`;
|
|
12529
12597
|
return result;
|
|
12530
12598
|
}
|
|
12531
12599
|
function runShell(cmd, cwd, timeoutMs = 3e4) {
|
|
@@ -12665,6 +12733,7 @@ async function runExecutable(profileName, input) {
|
|
|
12665
12733
|
systemPromptAppend: [DISCIPLINE, profile.claudeCode.systemPromptAppend, taskArtifacts?.promptAddendum].filter((s) => typeof s === "string" && s.length > 0).join("\n\n") || void 0,
|
|
12666
12734
|
cacheable: profile.claudeCode.cacheable,
|
|
12667
12735
|
enableVerifyTool: profile.claudeCode.enableVerifyTool,
|
|
12736
|
+
enableSubmitTool: profile.claudeCode.enableSubmitTool,
|
|
12668
12737
|
verifyToolMaxAttempts: profile.claudeCode.verifyAttempts ?? null,
|
|
12669
12738
|
verifyConfig: profile.claudeCode.enableVerifyTool ? config : void 0,
|
|
12670
12739
|
executableName: profileName,
|
|
@@ -32,23 +32,25 @@ This is the state you wrote at the end of the previous tick (or `null` if this i
|
|
|
32
32
|
2. **Re-read the job body.** It may have changed since the last tick.
|
|
33
33
|
3. **Execute exactly the work the body's `## Job` section describes**, subject to its `## Allowed Commands` and `## Restrictions`. Use the `## State` section to interpret and update `data`.
|
|
34
34
|
4. **Optionally post a short narration** wherever the job tells you to (typically a PR comment alongside the action). Keep it terse.
|
|
35
|
-
5. **
|
|
35
|
+
5. **Submit the new state** by calling the `submit_state` tool (see contract below). Do not include `version` or `rev` — the postflight script manages those.
|
|
36
36
|
|
|
37
37
|
## Output contract (MANDATORY, exactly once, at the end)
|
|
38
38
|
|
|
39
|
-
|
|
39
|
+
Call the **`submit_state`** tool exactly once, as the final step, with your next state:
|
|
40
40
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
"done": <true|false>
|
|
47
|
-
}
|
|
48
|
-
```
|
|
49
|
-
````
|
|
41
|
+
- `cursor` — your next cursor (string, e.g. `"idle"`).
|
|
42
|
+
- `data` — your next `data` object. Carry forward prior `data` and mutate only what you acted on this tick.
|
|
43
|
+
- `done` — `true` only if the duty is permanently finished; evergreen duties stay `false`.
|
|
44
|
+
|
|
45
|
+
This is the ONLY way your decision is saved. If you don't call it, the tick fails and the state is NOT updated — on the next wake you'll see the same prior state and can retry.
|
|
50
46
|
|
|
51
|
-
|
|
47
|
+
> Backstop (legacy): if the `submit_state` tool is unavailable, end your reply with the same JSON in a single fenced block tagged `kody-job-next-state` instead:
|
|
48
|
+
>
|
|
49
|
+
> ````
|
|
50
|
+
> ```kody-job-next-state
|
|
51
|
+
> { "cursor": "<next>", "data": { ... }, "done": <true|false> }
|
|
52
|
+
> ```
|
|
53
|
+
> ````
|
|
52
54
|
|
|
53
55
|
## Rules
|
|
54
56
|
|
|
@@ -228,6 +228,13 @@ export interface ClaudeCodeSpec {
|
|
|
228
228
|
* Default false.
|
|
229
229
|
*/
|
|
230
230
|
enableVerifyTool?: boolean
|
|
231
|
+
/**
|
|
232
|
+
* Opt-in: expose an in-process `submit_state` tool the agent calls to
|
|
233
|
+
* persist its next state, instead of relying on a trailing fenced
|
|
234
|
+
* `kody-job-next-state` block it must remember to emit. Used by job-tick.
|
|
235
|
+
* The fenced block stays supported as a fallback. Default false.
|
|
236
|
+
*/
|
|
237
|
+
enableSubmitTool?: boolean
|
|
231
238
|
/**
|
|
232
239
|
* Hard cap on verify-tool invocations per agent session when
|
|
233
240
|
* `enableVerifyTool` is true. Default 4 (≈3 fix iterations after the
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kody-ade/kody-engine",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.139",
|
|
4
4
|
"description": "kody — autonomous development engine. Single-session Claude Code agent behind a generic executor + declarative executable profiles.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|