@kody-ade/kody-engine 0.4.150 → 0.4.151
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
|
@@ -1061,7 +1061,7 @@ var init_loadPriorArt = __esm({
|
|
|
1061
1061
|
// package.json
|
|
1062
1062
|
var package_default = {
|
|
1063
1063
|
name: "@kody-ade/kody-engine",
|
|
1064
|
-
version: "0.4.
|
|
1064
|
+
version: "0.4.151",
|
|
1065
1065
|
description: "kody \u2014 autonomous development engine. Single-session Claude Code agent behind a generic executor + declarative executable profiles.",
|
|
1066
1066
|
license: "MIT",
|
|
1067
1067
|
type: "module",
|
|
@@ -1662,11 +1662,27 @@ function resolveTurnTimeoutMs(opts) {
|
|
|
1662
1662
|
if (Number.isFinite(envSec) && envSec <= 0) return 0;
|
|
1663
1663
|
return DEFAULT_TURN_TIMEOUT_MS;
|
|
1664
1664
|
}
|
|
1665
|
+
var MAX_CONNECTION_RETRIES = 2;
|
|
1666
|
+
var CONNECTION_RETRY_BASE_MS = 2e3;
|
|
1667
|
+
function isTransientConnectionError(msg) {
|
|
1668
|
+
if (!msg) return false;
|
|
1669
|
+
return /ConnectionRefused|ECONNREFUSED|ECONNRESET|ETIMEDOUT|EHOSTUNREACH|ENOTFOUND|socket hang up|Unable to connect to API|fetch failed/i.test(
|
|
1670
|
+
msg
|
|
1671
|
+
);
|
|
1672
|
+
}
|
|
1673
|
+
var MUTATING_FILE_TOOLS = /* @__PURE__ */ new Set(["Edit", "Write", "MultiEdit", "NotebookEdit"]);
|
|
1674
|
+
var BASH_WRITE_VERB = /\b(git\s+(commit|push|merge|rebase|tag|reset|cherry-pick)|gh\s+(pr|issue|release)\s+(create|comment|edit|close|merge|review|reopen)|gh\s+api\b[^|&]*-X\s*(POST|PUT|PATCH|DELETE)|npm\s+publish)\b/i;
|
|
1675
|
+
function toolMayMutate(name, input) {
|
|
1676
|
+
if (!name) return false;
|
|
1677
|
+
if (MUTATING_FILE_TOOLS.has(name)) return true;
|
|
1678
|
+
if (name.startsWith("mcp__kody-submit__")) return true;
|
|
1679
|
+
if (name === "Bash") return BASH_WRITE_VERB.test(String(input?.command ?? ""));
|
|
1680
|
+
return false;
|
|
1681
|
+
}
|
|
1665
1682
|
async function runAgent(opts) {
|
|
1666
1683
|
const ndjsonDir = opts.ndjsonDir ?? path7.join(opts.cwd, ".kody");
|
|
1667
1684
|
fs7.mkdirSync(ndjsonDir, { recursive: true });
|
|
1668
1685
|
const ndjsonPath = path7.join(ndjsonDir, "last-run.jsonl");
|
|
1669
|
-
const fullLog = fs7.createWriteStream(ndjsonPath, { flags: "w" });
|
|
1670
1686
|
const env = {
|
|
1671
1687
|
...process.env,
|
|
1672
1688
|
SKIP_HOOKS: "1",
|
|
@@ -1685,227 +1701,259 @@ async function runAgent(opts) {
|
|
|
1685
1701
|
env.ANTHROPIC_BASE_URL = opts.litellmUrl;
|
|
1686
1702
|
env.ANTHROPIC_API_KEY = getAnthropicApiKeyOrDummy();
|
|
1687
1703
|
}
|
|
1688
|
-
const
|
|
1704
|
+
const startedAt = Date.now();
|
|
1705
|
+
const turnTimeoutMs = resolveTurnTimeoutMs(opts);
|
|
1689
1706
|
let outcome = "failed";
|
|
1690
1707
|
let outcomeKind = "generic_failed";
|
|
1691
1708
|
let errorMessage;
|
|
1692
|
-
|
|
1709
|
+
let tokens = { input: 0, output: 0, cacheRead: 0, cacheCreate: 0 };
|
|
1693
1710
|
let messageCount = 0;
|
|
1694
|
-
|
|
1695
|
-
const turnTimeoutMs = resolveTurnTimeoutMs(opts);
|
|
1696
|
-
let ndjsonWriteFailed = false;
|
|
1697
|
-
let ndjsonWriteError;
|
|
1711
|
+
let finalText = "";
|
|
1698
1712
|
let getSubmitted;
|
|
1699
|
-
|
|
1700
|
-
const
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
if (s.args) cfg.args = s.args;
|
|
1714
|
-
if (s.env) cfg.env = s.env;
|
|
1715
|
-
mcpEntries.push([s.name, cfg]);
|
|
1716
|
-
}
|
|
1717
|
-
}
|
|
1718
|
-
if (opts.enableVerifyTool && opts.verifyConfig) {
|
|
1719
|
-
const { buildVerifyMcpServer: buildVerifyMcpServer2 } = await Promise.resolve().then(() => (init_verifyMcp(), verifyMcp_exports));
|
|
1720
|
-
const verifyServer = buildVerifyMcpServer2({
|
|
1721
|
-
config: opts.verifyConfig,
|
|
1713
|
+
for (let attempt = 0; ; attempt++) {
|
|
1714
|
+
const fullLog = fs7.createWriteStream(ndjsonPath, { flags: "w" });
|
|
1715
|
+
const resultTexts = [];
|
|
1716
|
+
outcome = "failed";
|
|
1717
|
+
outcomeKind = "generic_failed";
|
|
1718
|
+
errorMessage = void 0;
|
|
1719
|
+
tokens = { input: 0, output: 0, cacheRead: 0, cacheCreate: 0 };
|
|
1720
|
+
messageCount = 0;
|
|
1721
|
+
let ndjsonWriteFailed = false;
|
|
1722
|
+
let ndjsonWriteError;
|
|
1723
|
+
let sawMutatingTool = false;
|
|
1724
|
+
try {
|
|
1725
|
+
const queryOptions = {
|
|
1726
|
+
model: opts.model.model,
|
|
1722
1727
|
cwd: opts.cwd,
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
if (opts.enableSubmitTool) {
|
|
1729
|
-
const { buildSubmitMcpServer: buildSubmitMcpServer2 } = await Promise.resolve().then(() => (init_submitMcp(), submitMcp_exports));
|
|
1730
|
-
const submitHandle = buildSubmitMcpServer2();
|
|
1731
|
-
getSubmitted = submitHandle.getSubmitted;
|
|
1732
|
-
mcpEntries.push(["kody-submit", submitHandle.server]);
|
|
1733
|
-
}
|
|
1734
|
-
if (opts.enableFetchRepoTool && opts.reposRoot) {
|
|
1735
|
-
const { buildFetchRepoMcpServer: buildFetchRepoMcpServer2 } = await Promise.resolve().then(() => (init_fetchRepoMcp(), fetchRepoMcp_exports));
|
|
1736
|
-
const fetchServer = buildFetchRepoMcpServer2({
|
|
1737
|
-
reposRoot: opts.reposRoot,
|
|
1738
|
-
repoToken: opts.repoToken
|
|
1739
|
-
});
|
|
1740
|
-
mcpEntries.push(["kody-fetch-repo", fetchServer]);
|
|
1741
|
-
queryOptions.allowedTools.push("mcp__kody-fetch-repo__fetch_repo");
|
|
1742
|
-
queryOptions.additionalDirectories = [opts.reposRoot];
|
|
1743
|
-
}
|
|
1744
|
-
if (mcpEntries.length > 0) {
|
|
1745
|
-
queryOptions.mcpServers = Object.fromEntries(mcpEntries);
|
|
1746
|
-
}
|
|
1747
|
-
if (opts.pluginPaths && opts.pluginPaths.length > 0) {
|
|
1748
|
-
queryOptions.plugins = opts.pluginPaths.map((p) => ({ type: "local", path: p }));
|
|
1749
|
-
}
|
|
1750
|
-
if (opts.agents && Object.keys(opts.agents).length > 0) {
|
|
1751
|
-
queryOptions.agents = opts.agents;
|
|
1752
|
-
}
|
|
1753
|
-
if (typeof opts.maxTurns === "number" && opts.maxTurns > 0) {
|
|
1754
|
-
queryOptions.maxTurns = opts.maxTurns;
|
|
1755
|
-
}
|
|
1756
|
-
if (typeof opts.maxThinkingTokens === "number" && opts.maxThinkingTokens > 0) {
|
|
1757
|
-
queryOptions.maxThinkingTokens = opts.maxThinkingTokens;
|
|
1758
|
-
}
|
|
1759
|
-
if (typeof opts.systemPromptAppend === "string" && opts.systemPromptAppend.length > 0) {
|
|
1760
|
-
const systemPrompt = {
|
|
1761
|
-
type: "preset",
|
|
1762
|
-
preset: "claude_code",
|
|
1763
|
-
append: opts.systemPromptAppend
|
|
1764
|
-
};
|
|
1765
|
-
if (opts.cacheable) systemPrompt.excludeDynamicSections = true;
|
|
1766
|
-
queryOptions.systemPrompt = systemPrompt;
|
|
1767
|
-
} else if (opts.cacheable) {
|
|
1768
|
-
queryOptions.systemPrompt = {
|
|
1769
|
-
type: "preset",
|
|
1770
|
-
preset: "claude_code",
|
|
1771
|
-
excludeDynamicSections: true
|
|
1728
|
+
// Fresh array (never mutate the shared DEFAULT_ALLOWED_TOOLS const) so
|
|
1729
|
+
// opt-in tools like fetch_repo can be appended below.
|
|
1730
|
+
allowedTools: [...opts.allowedToolsOverride ?? DEFAULT_ALLOWED_TOOLS],
|
|
1731
|
+
permissionMode: opts.permissionModeOverride ?? "acceptEdits",
|
|
1732
|
+
env
|
|
1772
1733
|
};
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
let next;
|
|
1790
|
-
if (turnTimeoutMs > 0) {
|
|
1791
|
-
const timeoutPromise = new Promise((resolve6) => {
|
|
1792
|
-
timer = setTimeout(() => {
|
|
1793
|
-
timedOut = true;
|
|
1794
|
-
resolve6({ done: true, value: void 0 });
|
|
1795
|
-
}, turnTimeoutMs);
|
|
1734
|
+
const mcpEntries = [];
|
|
1735
|
+
if (opts.mcpServers && opts.mcpServers.length > 0) {
|
|
1736
|
+
for (const s of opts.mcpServers) {
|
|
1737
|
+
const cfg = { command: s.command };
|
|
1738
|
+
if (s.args) cfg.args = s.args;
|
|
1739
|
+
if (s.env) cfg.env = s.env;
|
|
1740
|
+
mcpEntries.push([s.name, cfg]);
|
|
1741
|
+
}
|
|
1742
|
+
}
|
|
1743
|
+
if (opts.enableVerifyTool && opts.verifyConfig) {
|
|
1744
|
+
const { buildVerifyMcpServer: buildVerifyMcpServer2 } = await Promise.resolve().then(() => (init_verifyMcp(), verifyMcp_exports));
|
|
1745
|
+
const verifyServer = buildVerifyMcpServer2({
|
|
1746
|
+
config: opts.verifyConfig,
|
|
1747
|
+
cwd: opts.cwd,
|
|
1748
|
+
executable: opts.executableName ?? "agent",
|
|
1749
|
+
maxAttempts: typeof opts.verifyToolMaxAttempts === "number" && opts.verifyToolMaxAttempts > 0 ? opts.verifyToolMaxAttempts : void 0
|
|
1796
1750
|
});
|
|
1797
|
-
|
|
1798
|
-
if (timer) clearTimeout(timer);
|
|
1799
|
-
} else {
|
|
1800
|
-
next = await nextPromise;
|
|
1751
|
+
mcpEntries.push(["kody-verify", verifyServer]);
|
|
1801
1752
|
}
|
|
1802
|
-
if (
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1753
|
+
if (opts.enableSubmitTool) {
|
|
1754
|
+
const { buildSubmitMcpServer: buildSubmitMcpServer2 } = await Promise.resolve().then(() => (init_submitMcp(), submitMcp_exports));
|
|
1755
|
+
const submitHandle = buildSubmitMcpServer2();
|
|
1756
|
+
getSubmitted = submitHandle.getSubmitted;
|
|
1757
|
+
mcpEntries.push(["kody-submit", submitHandle.server]);
|
|
1758
|
+
}
|
|
1759
|
+
if (opts.enableFetchRepoTool && opts.reposRoot) {
|
|
1760
|
+
const { buildFetchRepoMcpServer: buildFetchRepoMcpServer2 } = await Promise.resolve().then(() => (init_fetchRepoMcp(), fetchRepoMcp_exports));
|
|
1761
|
+
const fetchServer = buildFetchRepoMcpServer2({
|
|
1762
|
+
reposRoot: opts.reposRoot,
|
|
1763
|
+
repoToken: opts.repoToken
|
|
1764
|
+
});
|
|
1765
|
+
mcpEntries.push(["kody-fetch-repo", fetchServer]);
|
|
1766
|
+
queryOptions.allowedTools.push("mcp__kody-fetch-repo__fetch_repo");
|
|
1767
|
+
queryOptions.additionalDirectories = [opts.reposRoot];
|
|
1768
|
+
}
|
|
1769
|
+
if (mcpEntries.length > 0) {
|
|
1770
|
+
queryOptions.mcpServers = Object.fromEntries(mcpEntries);
|
|
1771
|
+
}
|
|
1772
|
+
if (opts.pluginPaths && opts.pluginPaths.length > 0) {
|
|
1773
|
+
queryOptions.plugins = opts.pluginPaths.map((p) => ({ type: "local", path: p }));
|
|
1774
|
+
}
|
|
1775
|
+
if (opts.agents && Object.keys(opts.agents).length > 0) {
|
|
1776
|
+
queryOptions.agents = opts.agents;
|
|
1777
|
+
}
|
|
1778
|
+
if (typeof opts.maxTurns === "number" && opts.maxTurns > 0) {
|
|
1779
|
+
queryOptions.maxTurns = opts.maxTurns;
|
|
1780
|
+
}
|
|
1781
|
+
if (typeof opts.maxThinkingTokens === "number" && opts.maxThinkingTokens > 0) {
|
|
1782
|
+
queryOptions.maxThinkingTokens = opts.maxThinkingTokens;
|
|
1783
|
+
}
|
|
1784
|
+
if (typeof opts.systemPromptAppend === "string" && opts.systemPromptAppend.length > 0) {
|
|
1785
|
+
const systemPrompt = {
|
|
1786
|
+
type: "preset",
|
|
1787
|
+
preset: "claude_code",
|
|
1788
|
+
append: opts.systemPromptAppend
|
|
1789
|
+
};
|
|
1790
|
+
if (opts.cacheable) systemPrompt.excludeDynamicSections = true;
|
|
1791
|
+
queryOptions.systemPrompt = systemPrompt;
|
|
1792
|
+
} else if (opts.cacheable) {
|
|
1793
|
+
queryOptions.systemPrompt = {
|
|
1794
|
+
type: "preset",
|
|
1795
|
+
preset: "claude_code",
|
|
1796
|
+
excludeDynamicSections: true
|
|
1797
|
+
};
|
|
1798
|
+
}
|
|
1799
|
+
queryOptions.settingSources = opts.settingSources ?? ["project", "local"];
|
|
1800
|
+
const stableBinary = ensureStableClaudeBinary();
|
|
1801
|
+
if (stableBinary) {
|
|
1802
|
+
queryOptions.pathToClaudeCodeExecutable = stableBinary;
|
|
1803
|
+
}
|
|
1804
|
+
const result = query({
|
|
1805
|
+
prompt: opts.prompt,
|
|
1806
|
+
// biome-ignore lint/suspicious/noExplicitAny: SDK options type is narrow; mcpServers is runtime-passthrough.
|
|
1807
|
+
options: queryOptions
|
|
1808
|
+
});
|
|
1809
|
+
const iterator = typeof result[Symbol.asyncIterator] === "function" ? result[Symbol.asyncIterator]() : result;
|
|
1810
|
+
while (true) {
|
|
1811
|
+
const nextPromise = iterator.next();
|
|
1812
|
+
let timedOut = false;
|
|
1813
|
+
let timer;
|
|
1814
|
+
let next;
|
|
1815
|
+
if (turnTimeoutMs > 0) {
|
|
1816
|
+
const timeoutPromise = new Promise((resolve6) => {
|
|
1817
|
+
timer = setTimeout(() => {
|
|
1818
|
+
timedOut = true;
|
|
1819
|
+
resolve6({ done: true, value: void 0 });
|
|
1820
|
+
}, turnTimeoutMs);
|
|
1821
|
+
});
|
|
1822
|
+
next = await Promise.race([nextPromise, timeoutPromise]);
|
|
1823
|
+
if (timer) clearTimeout(timer);
|
|
1824
|
+
} else {
|
|
1825
|
+
next = await nextPromise;
|
|
1826
|
+
}
|
|
1827
|
+
if (timedOut) {
|
|
1828
|
+
outcome = "failed";
|
|
1829
|
+
outcomeKind = "stalled";
|
|
1830
|
+
errorMessage = `agent stalled: no SDK message in ${Math.round(turnTimeoutMs / 1e3)}s`;
|
|
1831
|
+
if (typeof iterator.return === "function") {
|
|
1832
|
+
try {
|
|
1833
|
+
await iterator.return(void 0);
|
|
1834
|
+
} catch {
|
|
1835
|
+
}
|
|
1810
1836
|
}
|
|
1837
|
+
break;
|
|
1811
1838
|
}
|
|
1812
|
-
break;
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
try {
|
|
1818
|
-
fullLog.write(`${JSON.stringify(msg)}
|
|
1839
|
+
if (next.done) break;
|
|
1840
|
+
const msg = next.value;
|
|
1841
|
+
messageCount++;
|
|
1842
|
+
try {
|
|
1843
|
+
fullLog.write(`${JSON.stringify(msg)}
|
|
1819
1844
|
`);
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1845
|
+
} catch (e) {
|
|
1846
|
+
ndjsonWriteFailed = true;
|
|
1847
|
+
ndjsonWriteError = e instanceof Error ? e.message : String(e);
|
|
1848
|
+
}
|
|
1849
|
+
const line = renderEvent(msg, { verbose: opts.verbose, quiet: opts.quiet });
|
|
1850
|
+
if (line) process.stdout.write(`${line}
|
|
1826
1851
|
`);
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
}
|
|
1837
|
-
} else if (block.type === "tool_use") {
|
|
1838
|
-
const b = block;
|
|
1839
|
-
await opts.onProgress({
|
|
1840
|
-
kind: "tool_use",
|
|
1841
|
-
name: b.name ?? "tool",
|
|
1842
|
-
input: b.input,
|
|
1843
|
-
id: b.id
|
|
1844
|
-
});
|
|
1845
|
-
} else if (block.type === "tool_result") {
|
|
1846
|
-
const b = block;
|
|
1847
|
-
const content = typeof b.content === "string" ? b.content : (() => {
|
|
1848
|
-
try {
|
|
1849
|
-
return JSON.stringify(b.content);
|
|
1850
|
-
} catch {
|
|
1851
|
-
return "";
|
|
1852
|
+
const m = msg;
|
|
1853
|
+
if (opts.onProgress) {
|
|
1854
|
+
const blocks = m.message?.content ?? [];
|
|
1855
|
+
for (const block of blocks) {
|
|
1856
|
+
try {
|
|
1857
|
+
if (block.type === "thinking") {
|
|
1858
|
+
const t = block.thinking;
|
|
1859
|
+
if (typeof t === "string" && t.length > 0) {
|
|
1860
|
+
await opts.onProgress({ kind: "thinking", thinking: t });
|
|
1852
1861
|
}
|
|
1853
|
-
}
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1862
|
+
} else if (block.type === "tool_use") {
|
|
1863
|
+
const b = block;
|
|
1864
|
+
await opts.onProgress({
|
|
1865
|
+
kind: "tool_use",
|
|
1866
|
+
name: b.name ?? "tool",
|
|
1867
|
+
input: b.input,
|
|
1868
|
+
id: b.id
|
|
1869
|
+
});
|
|
1870
|
+
} else if (block.type === "tool_result") {
|
|
1871
|
+
const b = block;
|
|
1872
|
+
const content = typeof b.content === "string" ? b.content : (() => {
|
|
1873
|
+
try {
|
|
1874
|
+
return JSON.stringify(b.content);
|
|
1875
|
+
} catch {
|
|
1876
|
+
return "";
|
|
1877
|
+
}
|
|
1878
|
+
})();
|
|
1879
|
+
await opts.onProgress({
|
|
1880
|
+
kind: "tool_result",
|
|
1881
|
+
toolUseId: b.tool_use_id,
|
|
1882
|
+
content,
|
|
1883
|
+
isError: b.is_error
|
|
1884
|
+
});
|
|
1885
|
+
} else if (block.type === "text") {
|
|
1886
|
+
const b = block;
|
|
1887
|
+
if (typeof b.text === "string" && b.text.length > 0) {
|
|
1888
|
+
await opts.onProgress({ kind: "text", text: b.text });
|
|
1889
|
+
}
|
|
1890
|
+
}
|
|
1891
|
+
} catch {
|
|
1892
|
+
}
|
|
1893
|
+
}
|
|
1894
|
+
}
|
|
1895
|
+
const usage = m.usage;
|
|
1896
|
+
if (usage && typeof usage === "object") {
|
|
1897
|
+
const i = Number(usage.input_tokens ?? 0);
|
|
1898
|
+
const o = Number(usage.output_tokens ?? 0);
|
|
1899
|
+
const cr = Number(usage.cache_read_input_tokens ?? 0);
|
|
1900
|
+
const cc = Number(usage.cache_creation_input_tokens ?? 0);
|
|
1901
|
+
if (Number.isFinite(i)) tokens.input += i;
|
|
1902
|
+
if (Number.isFinite(o)) tokens.output += o;
|
|
1903
|
+
if (Number.isFinite(cr)) tokens.cacheRead += cr;
|
|
1904
|
+
if (Number.isFinite(cc)) tokens.cacheCreate += cc;
|
|
1905
|
+
}
|
|
1906
|
+
if (!sawMutatingTool) {
|
|
1907
|
+
const blocks = m.message?.content ?? [];
|
|
1908
|
+
for (const block of blocks) {
|
|
1909
|
+
if (block.type === "tool_use") {
|
|
1861
1910
|
const b = block;
|
|
1862
|
-
if (
|
|
1863
|
-
|
|
1911
|
+
if (toolMayMutate(b.name, b.input)) {
|
|
1912
|
+
sawMutatingTool = true;
|
|
1913
|
+
break;
|
|
1864
1914
|
}
|
|
1865
1915
|
}
|
|
1866
|
-
} catch {
|
|
1867
1916
|
}
|
|
1868
1917
|
}
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
}
|
|
1881
|
-
if (m.type === "result") {
|
|
1882
|
-
if (m.subtype === "success") {
|
|
1883
|
-
outcome = "completed";
|
|
1884
|
-
outcomeKind = "ok";
|
|
1885
|
-
const text = (typeof m.result === "string" ? m.result : "").trim();
|
|
1886
|
-
if (text) resultTexts.push(text);
|
|
1887
|
-
} else {
|
|
1888
|
-
outcome = "failed";
|
|
1889
|
-
outcomeKind = classifySubtype(m.subtype);
|
|
1890
|
-
errorMessage = `result subtype: ${m.subtype ?? "unknown"}`;
|
|
1918
|
+
if (m.type === "result") {
|
|
1919
|
+
if (m.subtype === "success") {
|
|
1920
|
+
outcome = "completed";
|
|
1921
|
+
outcomeKind = "ok";
|
|
1922
|
+
const text = (typeof m.result === "string" ? m.result : "").trim();
|
|
1923
|
+
if (text) resultTexts.push(text);
|
|
1924
|
+
} else {
|
|
1925
|
+
outcome = "failed";
|
|
1926
|
+
outcomeKind = classifySubtype(m.subtype);
|
|
1927
|
+
errorMessage = `result subtype: ${m.subtype ?? "unknown"}`;
|
|
1928
|
+
}
|
|
1891
1929
|
}
|
|
1892
1930
|
}
|
|
1931
|
+
} catch (e) {
|
|
1932
|
+
outcome = "failed";
|
|
1933
|
+
outcomeKind = "model_error";
|
|
1934
|
+
errorMessage = e instanceof Error ? e.message : String(e);
|
|
1935
|
+
} finally {
|
|
1936
|
+
try {
|
|
1937
|
+
fullLog.end();
|
|
1938
|
+
} catch {
|
|
1939
|
+
}
|
|
1893
1940
|
}
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
try {
|
|
1900
|
-
fullLog.end();
|
|
1901
|
-
} catch {
|
|
1941
|
+
if (ndjsonWriteFailed) {
|
|
1942
|
+
process.stderr.write(
|
|
1943
|
+
`[kody agent] NDJSON write failed (post-mortem may be incomplete): ${ndjsonWriteError ?? "unknown error"}
|
|
1944
|
+
`
|
|
1945
|
+
);
|
|
1902
1946
|
}
|
|
1947
|
+
finalText = resultTexts.join("\n\n---\n\n");
|
|
1948
|
+
const shouldRetry = outcome === "failed" && attempt < MAX_CONNECTION_RETRIES && !sawMutatingTool && isTransientConnectionError(errorMessage);
|
|
1949
|
+
if (!shouldRetry) break;
|
|
1950
|
+
const delayMs = CONNECTION_RETRY_BASE_MS * 2 ** attempt;
|
|
1951
|
+
process.stderr.write(
|
|
1952
|
+
`[kody agent] transient connection error (attempt ${attempt + 1}/${MAX_CONNECTION_RETRIES + 1}); retrying in ${Math.round(delayMs / 1e3)}s: ${errorMessage}
|
|
1953
|
+
`
|
|
1954
|
+
);
|
|
1955
|
+
await new Promise((r) => setTimeout(r, delayMs));
|
|
1903
1956
|
}
|
|
1904
|
-
if (ndjsonWriteFailed) {
|
|
1905
|
-
process.stderr.write(`[kody agent] NDJSON write failed (post-mortem may be incomplete): ${ndjsonWriteError ?? "unknown error"}
|
|
1906
|
-
`);
|
|
1907
|
-
}
|
|
1908
|
-
const finalText = resultTexts.join("\n\n---\n\n");
|
|
1909
1957
|
const submittedState = getSubmitted?.();
|
|
1910
1958
|
return {
|
|
1911
1959
|
outcome,
|
|
@@ -7850,6 +7898,8 @@ var FAILED = {
|
|
|
7850
7898
|
description: "kody: flow failed"
|
|
7851
7899
|
};
|
|
7852
7900
|
var finalizeTerminal = async (ctx) => {
|
|
7901
|
+
const flow = ctx.data.taskState?.flow;
|
|
7902
|
+
if (flow?.issueNumber) return;
|
|
7853
7903
|
const target = ctx.data.commentTargetType ?? "issue";
|
|
7854
7904
|
const issueNumber = ctx.args.issue;
|
|
7855
7905
|
const targetNumber = ctx.data.commentTargetNumber ?? issueNumber;
|
|
@@ -27,14 +27,6 @@
|
|
|
27
27
|
"cliTools": [],
|
|
28
28
|
"scripts": {
|
|
29
29
|
"preflight": [
|
|
30
|
-
{
|
|
31
|
-
"script": "setLifecycleLabel",
|
|
32
|
-
"with": {
|
|
33
|
-
"label": "kody:syncing",
|
|
34
|
-
"color": "c5def5",
|
|
35
|
-
"description": "kody: syncing PR with base"
|
|
36
|
-
}
|
|
37
|
-
},
|
|
38
30
|
{
|
|
39
31
|
"script": "syncFlow",
|
|
40
32
|
"with": { "announceOnSuccess": true }
|
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.151",
|
|
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",
|