@kody-ade/kody-engine 0.4.373 → 0.4.374
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 +964 -284
- package/dist/implementations/types.ts +15 -0
- 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.
|
|
18
|
+
version: "0.4.374",
|
|
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",
|
|
@@ -1064,8 +1064,8 @@ function formatAssistant(msg, _opts) {
|
|
|
1064
1064
|
const lines = [];
|
|
1065
1065
|
for (const block of content) {
|
|
1066
1066
|
if (block.type === "text") {
|
|
1067
|
-
const
|
|
1068
|
-
if (
|
|
1067
|
+
const text2 = block.text.trim();
|
|
1068
|
+
if (text2) lines.push(text2);
|
|
1069
1069
|
} else if (block.type === "tool_use") {
|
|
1070
1070
|
const tu = block;
|
|
1071
1071
|
lines.push(`\u2192 ${tu.name}${summarizeToolInput(tu.name, tu.input)}`);
|
|
@@ -1079,14 +1079,14 @@ function formatUserToolResult(msg, opts) {
|
|
|
1079
1079
|
for (const block of content) {
|
|
1080
1080
|
if (block.type === "tool_result") {
|
|
1081
1081
|
const tr = block;
|
|
1082
|
-
const
|
|
1083
|
-
const lineCount =
|
|
1084
|
-
const sizeBytes =
|
|
1082
|
+
const text2 = stringifyToolContent(tr.content);
|
|
1083
|
+
const lineCount = text2.split("\n").length;
|
|
1084
|
+
const sizeBytes = text2.length;
|
|
1085
1085
|
const flag = tr.is_error ? " ERROR" : "";
|
|
1086
1086
|
const summary = ` \u21B3${flag} ${lineCount} lines, ${formatBytes(sizeBytes)}`;
|
|
1087
1087
|
if (opts.verbose) {
|
|
1088
1088
|
lines.push(`${summary}
|
|
1089
|
-
${truncate2(
|
|
1089
|
+
${truncate2(text2, 4e3)}`);
|
|
1090
1090
|
} else {
|
|
1091
1091
|
lines.push(summary);
|
|
1092
1092
|
}
|
|
@@ -1612,7 +1612,7 @@ function cmsHeaders(opts) {
|
|
|
1612
1612
|
}
|
|
1613
1613
|
};
|
|
1614
1614
|
}
|
|
1615
|
-
async function callDashboardCms(opts,
|
|
1615
|
+
async function callDashboardCms(opts, path52, init = {}) {
|
|
1616
1616
|
const baseUrl = dashboardBaseUrl(opts);
|
|
1617
1617
|
if (!baseUrl) {
|
|
1618
1618
|
return {
|
|
@@ -1624,7 +1624,7 @@ async function callDashboardCms(opts, path51, init = {}) {
|
|
|
1624
1624
|
const headerResult = cmsHeaders(opts);
|
|
1625
1625
|
if (!headerResult.ok) return headerResult;
|
|
1626
1626
|
try {
|
|
1627
|
-
const res = await fetch(`${baseUrl}${
|
|
1627
|
+
const res = await fetch(`${baseUrl}${path52}`, {
|
|
1628
1628
|
...init,
|
|
1629
1629
|
headers: {
|
|
1630
1630
|
...headerResult.headers,
|
|
@@ -1696,8 +1696,8 @@ function documentArg(value) {
|
|
|
1696
1696
|
function normalizeCmsDocumentIdInput(input) {
|
|
1697
1697
|
const trimmed = stripWrappingQuotes(input.trim());
|
|
1698
1698
|
const withoutQuery = trimmed.split(/[?#]/, 1)[0] ?? trimmed;
|
|
1699
|
-
const
|
|
1700
|
-
return
|
|
1699
|
+
const path52 = parseDocumentPath(withoutQuery);
|
|
1700
|
+
return path52 ?? parseDocumentIdSegment(withoutQuery) ?? withoutQuery;
|
|
1701
1701
|
}
|
|
1702
1702
|
function stripWrappingQuotes(value) {
|
|
1703
1703
|
let current = value;
|
|
@@ -1708,9 +1708,9 @@ function stripWrappingQuotes(value) {
|
|
|
1708
1708
|
}
|
|
1709
1709
|
}
|
|
1710
1710
|
function parseDocumentPath(value) {
|
|
1711
|
-
const
|
|
1712
|
-
if (!
|
|
1713
|
-
const parts =
|
|
1711
|
+
const path52 = value.startsWith("http://") || value.startsWith("https://") ? urlPathname(value) : value;
|
|
1712
|
+
if (!path52?.includes("/content/entries/")) return null;
|
|
1713
|
+
const parts = path52.split("/").filter(Boolean).map(decodePathPart);
|
|
1714
1714
|
const entriesIndex = parts.findIndex((part, index) => part === "content" && parts[index + 1] === "entries");
|
|
1715
1715
|
const idPart = parts[entriesIndex + 3];
|
|
1716
1716
|
if (!idPart || idPart === "new") return null;
|
|
@@ -1970,7 +1970,12 @@ function stringList(value) {
|
|
|
1970
1970
|
function parseCapabilityWorkflow(value) {
|
|
1971
1971
|
const stepsRaw = Array.isArray(value) ? value : value && typeof value === "object" && Array.isArray(value.steps) ? value.steps : [];
|
|
1972
1972
|
const steps = stepsRaw.map(parseWorkflowStep).filter((step) => step !== null);
|
|
1973
|
-
|
|
1973
|
+
if (steps.length === 0) return void 0;
|
|
1974
|
+
const startAt = value && typeof value === "object" && !Array.isArray(value) ? stringField(value.startAt) : void 0;
|
|
1975
|
+
return {
|
|
1976
|
+
steps,
|
|
1977
|
+
...startAt && isSafeSlug(startAt) ? { startAt } : {}
|
|
1978
|
+
};
|
|
1974
1979
|
}
|
|
1975
1980
|
function parseWorkflowStep(value) {
|
|
1976
1981
|
if (typeof value === "string") {
|
|
@@ -1982,6 +1987,7 @@ function parseWorkflowStep(value) {
|
|
|
1982
1987
|
const capability = stringField(raw.capability ?? raw.action);
|
|
1983
1988
|
if (!capability || !isSafeSlug(capability)) return null;
|
|
1984
1989
|
const implementation = stringField(raw.implementation);
|
|
1990
|
+
const id = stringField(raw.id);
|
|
1985
1991
|
const action = stringField(raw.action);
|
|
1986
1992
|
const evidence = stringField(raw.evidence);
|
|
1987
1993
|
const agent = stringField(raw.agent);
|
|
@@ -1989,9 +1995,12 @@ function parseWorkflowStep(value) {
|
|
|
1989
1995
|
const target = stringField(raw.target);
|
|
1990
1996
|
const targetFact = stringField(raw.targetFact ?? raw.target_fact);
|
|
1991
1997
|
const cliArgs = raw.cliArgs;
|
|
1998
|
+
const inputs = parseWorkflowInputs(raw.inputs);
|
|
1999
|
+
const next = parseWorkflowTransitions(raw.next);
|
|
1992
2000
|
const report = parseReportPublication(raw.report);
|
|
1993
2001
|
return {
|
|
1994
2002
|
capability,
|
|
2003
|
+
...id && isSafeSlug(id) ? { id } : {},
|
|
1995
2004
|
...action && isSafeSlug(action) ? { action } : {},
|
|
1996
2005
|
...implementation && isSafeSlug(implementation) ? { implementation } : {},
|
|
1997
2006
|
...evidence ? { evidence } : {},
|
|
@@ -2000,12 +2009,45 @@ function parseWorkflowStep(value) {
|
|
|
2000
2009
|
...agent && isSafeSlug(agent) ? { agent } : {},
|
|
2001
2010
|
...reason ? { reason } : {},
|
|
2002
2011
|
...cliArgs && typeof cliArgs === "object" && !Array.isArray(cliArgs) ? { cliArgs } : {},
|
|
2012
|
+
...inputs ? { inputs } : {},
|
|
2013
|
+
...next ? { next } : {},
|
|
2003
2014
|
...isPlainObject(raw.runWhen) ? { runWhen: raw.runWhen } : {},
|
|
2004
2015
|
...stringList(raw.continueOn ?? raw.continue_on).length > 0 ? { continueOn: stringList(raw.continueOn ?? raw.continue_on) } : {},
|
|
2005
2016
|
...raw.saveReport === true ? { saveReport: true } : {},
|
|
2006
2017
|
...report ? { report } : {}
|
|
2007
2018
|
};
|
|
2008
2019
|
}
|
|
2020
|
+
function parseWorkflowInputs(value) {
|
|
2021
|
+
if (!isPlainObject(value)) return void 0;
|
|
2022
|
+
const inputs = {};
|
|
2023
|
+
for (const [name, raw] of Object.entries(value)) {
|
|
2024
|
+
if (!isSafeSlug(name) || !isPlainObject(raw)) continue;
|
|
2025
|
+
const from = stringField(raw.from);
|
|
2026
|
+
if (!from) continue;
|
|
2027
|
+
inputs[name] = { from };
|
|
2028
|
+
}
|
|
2029
|
+
return Object.keys(inputs).length > 0 ? inputs : void 0;
|
|
2030
|
+
}
|
|
2031
|
+
function parseWorkflowTransitions(value) {
|
|
2032
|
+
const rawTransitions = Array.isArray(value) ? value : value === void 0 ? [] : [value];
|
|
2033
|
+
const transitions = rawTransitions.map((raw) => {
|
|
2034
|
+
if (typeof raw === "string") {
|
|
2035
|
+
const to2 = raw.trim();
|
|
2036
|
+
return isSafeSlug(to2) ? { to: to2 } : null;
|
|
2037
|
+
}
|
|
2038
|
+
if (!isPlainObject(raw)) return null;
|
|
2039
|
+
const to = stringField(raw.to);
|
|
2040
|
+
if (!to || !isSafeSlug(to)) return null;
|
|
2041
|
+
const maxIterations = typeof raw.maxIterations === "number" && Number.isInteger(raw.maxIterations) && raw.maxIterations > 0 ? raw.maxIterations : void 0;
|
|
2042
|
+
return {
|
|
2043
|
+
to,
|
|
2044
|
+
...isPlainObject(raw.when) ? { when: raw.when } : {},
|
|
2045
|
+
...raw.default === true ? { default: true } : {},
|
|
2046
|
+
...maxIterations ? { maxIterations } : {}
|
|
2047
|
+
};
|
|
2048
|
+
}).filter((transition) => transition !== null);
|
|
2049
|
+
return transitions.length > 0 ? transitions : void 0;
|
|
2050
|
+
}
|
|
2009
2051
|
function parseReportPublication(value) {
|
|
2010
2052
|
if (!isPlainObject(value)) return void 0;
|
|
2011
2053
|
const type = stringField(value.type);
|
|
@@ -2317,8 +2359,8 @@ function resolveCapabilityFolder(slug, projectCapabilitiesRoot = getProjectCapab
|
|
|
2317
2359
|
}
|
|
2318
2360
|
return null;
|
|
2319
2361
|
}
|
|
2320
|
-
function getCapabilityActionInputs(action) {
|
|
2321
|
-
const resolved = resolveCapabilityAction(action);
|
|
2362
|
+
function getCapabilityActionInputs(action, projectCapabilitiesRoot = getProjectCapabilitiesRoot()) {
|
|
2363
|
+
const resolved = resolveCapabilityAction(action, projectCapabilitiesRoot);
|
|
2322
2364
|
if (!resolved) return null;
|
|
2323
2365
|
return getProfileInputs(resolved.implementation);
|
|
2324
2366
|
}
|
|
@@ -2637,20 +2679,20 @@ function readLedger(label) {
|
|
|
2637
2679
|
const raw = gh(["issue", "list", "--state", "open", "--label", label, "--limit", "5", "--json", "number,body"]);
|
|
2638
2680
|
const issues = JSON.parse(raw);
|
|
2639
2681
|
if (issues.length === 0) return { found: false, payload: null };
|
|
2640
|
-
const
|
|
2641
|
-
const body =
|
|
2682
|
+
const issue2 = issues.sort((a, b) => a.number - b.number)[0];
|
|
2683
|
+
const body = issue2?.body ?? "";
|
|
2642
2684
|
const startIdx = body.indexOf(startTag);
|
|
2643
2685
|
const endIdx = body.indexOf(endTag);
|
|
2644
2686
|
if (startIdx === -1 || endIdx === -1 || endIdx <= startIdx) {
|
|
2645
|
-
return { found: true, issueNumber:
|
|
2687
|
+
return { found: true, issueNumber: issue2?.number, payload: null };
|
|
2646
2688
|
}
|
|
2647
2689
|
const between = body.slice(startIdx + startTag.length, endIdx);
|
|
2648
2690
|
const fenceMatch = between.match(/```json\s*([\s\S]*?)```/);
|
|
2649
|
-
if (!fenceMatch) return { found: true, issueNumber:
|
|
2691
|
+
if (!fenceMatch) return { found: true, issueNumber: issue2?.number, payload: null };
|
|
2650
2692
|
try {
|
|
2651
|
-
return { found: true, issueNumber:
|
|
2693
|
+
return { found: true, issueNumber: issue2?.number, payload: JSON.parse(fenceMatch[1]) };
|
|
2652
2694
|
} catch {
|
|
2653
|
-
return { found: true, issueNumber:
|
|
2695
|
+
return { found: true, issueNumber: issue2?.number, payload: null };
|
|
2654
2696
|
}
|
|
2655
2697
|
} catch (err) {
|
|
2656
2698
|
return { found: false, payload: { error: err instanceof Error ? err.message : String(err) } };
|
|
@@ -2698,12 +2740,7 @@ function readCheckRuns(repoSlug, ref, ignoreNames) {
|
|
|
2698
2740
|
let rawStatuses = "";
|
|
2699
2741
|
try {
|
|
2700
2742
|
rawStatuses = gh(
|
|
2701
|
-
[
|
|
2702
|
-
"api",
|
|
2703
|
-
`repos/${repoSlug}/commits/${sha}/status`,
|
|
2704
|
-
"--jq",
|
|
2705
|
-
".statuses[] | {context, state, target_url}"
|
|
2706
|
-
],
|
|
2743
|
+
["api", `repos/${repoSlug}/commits/${sha}/status`, "--jq", ".statuses[] | {context, state, target_url}"],
|
|
2707
2744
|
ghOptions
|
|
2708
2745
|
);
|
|
2709
2746
|
} catch {
|
|
@@ -2745,14 +2782,14 @@ ${marker}`
|
|
|
2745
2782
|
return { error: err instanceof Error ? err.message : String(err) };
|
|
2746
2783
|
}
|
|
2747
2784
|
}
|
|
2748
|
-
function ensureComment(repoSlug,
|
|
2785
|
+
function ensureComment(repoSlug, issue2, key, body) {
|
|
2749
2786
|
const marker = commentMarker(key);
|
|
2750
2787
|
try {
|
|
2751
|
-
const raw = gh(["issue", "view", String(
|
|
2788
|
+
const raw = gh(["issue", "view", String(issue2), "-R", repoSlug, "--json", "comments"]);
|
|
2752
2789
|
const parsed = JSON.parse(raw);
|
|
2753
2790
|
const already = (parsed.comments ?? []).some((c) => (c.body ?? "").includes(marker));
|
|
2754
2791
|
if (already) return { posted: false };
|
|
2755
|
-
gh(["issue", "comment", String(
|
|
2792
|
+
gh(["issue", "comment", String(issue2), "-R", repoSlug, "--body-file", "-"], { input: `${body}
|
|
2756
2793
|
|
|
2757
2794
|
${marker}` });
|
|
2758
2795
|
return { posted: true };
|
|
@@ -2794,9 +2831,9 @@ function dispatchWorkflow(workflowFile, capability, issueNumber, repoSlug, ref)
|
|
|
2794
2831
|
return { ok: false, error: err instanceof Error ? err.message : String(err) };
|
|
2795
2832
|
}
|
|
2796
2833
|
}
|
|
2797
|
-
function startCapability(workflowFile, name,
|
|
2834
|
+
function startCapability(workflowFile, name, issue2, repoSlug, ref) {
|
|
2798
2835
|
const acceptsIssue = capabilityAcceptsIssue(name);
|
|
2799
|
-
const forwardedIssue = acceptsIssue === false ? void 0 :
|
|
2836
|
+
const forwardedIssue = acceptsIssue === false ? void 0 : issue2;
|
|
2800
2837
|
return dispatchWorkflow(workflowFile, name, forwardedIssue, repoSlug, ref);
|
|
2801
2838
|
}
|
|
2802
2839
|
function capabilityAcceptsIssue(capability) {
|
|
@@ -2862,8 +2899,8 @@ function capabilityToolDefinitions(opts) {
|
|
|
2862
2899
|
handler: async (args) => {
|
|
2863
2900
|
const pr = Number(args.pr);
|
|
2864
2901
|
const result = dispatchVerb(workflowFile, opts.repoSlug, verb, pr);
|
|
2865
|
-
const
|
|
2866
|
-
return { content: [{ type: "text", text }] };
|
|
2902
|
+
const text2 = result.ok ? `Dispatched \`${verb}\` on PR #${pr}. The repair runs in its own workflow_dispatch \u2014 wait for the next tick to see the new headSha.` : `Dispatch failed for \`${verb}\` on PR #${pr}: ${result.error}`;
|
|
2903
|
+
return { content: [{ type: "text", text: text2 }] };
|
|
2867
2904
|
}
|
|
2868
2905
|
});
|
|
2869
2906
|
const syncTool = makeDispatch(
|
|
@@ -2889,8 +2926,8 @@ function capabilityToolDefinitions(opts) {
|
|
|
2889
2926
|
const pr = Number(args.pr);
|
|
2890
2927
|
const body = String(args.body ?? "");
|
|
2891
2928
|
const result = postRecommendation(opts.repoSlug, pr, opts.operatorMention, body, opts.capabilitySlug);
|
|
2892
|
-
const
|
|
2893
|
-
return { content: [{ type: "text", text }] };
|
|
2929
|
+
const text2 = result.ok ? result.posted ? `Recommendation posted on PR #${pr}.` : `Recommendation already exists on PR #${pr}; skipped.` : `Recommendation failed on PR #${pr}: ${result.error}`;
|
|
2930
|
+
return { content: [{ type: "text", text: text2 }] };
|
|
2894
2931
|
}
|
|
2895
2932
|
};
|
|
2896
2933
|
const ledgerTool = {
|
|
@@ -2964,10 +3001,10 @@ function capabilityToolDefinitions(opts) {
|
|
|
2964
3001
|
body: z3.string().min(1).describe("Comment body markdown.")
|
|
2965
3002
|
},
|
|
2966
3003
|
handler: async (args) => {
|
|
2967
|
-
const
|
|
3004
|
+
const issue2 = Number(args.issue);
|
|
2968
3005
|
const key = String(args.key ?? "");
|
|
2969
3006
|
const body = String(args.body ?? "");
|
|
2970
|
-
const result = ensureComment(opts.repoSlug,
|
|
3007
|
+
const result = ensureComment(opts.repoSlug, issue2, key, body);
|
|
2971
3008
|
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
2972
3009
|
}
|
|
2973
3010
|
};
|
|
@@ -2982,19 +3019,13 @@ function capabilityToolDefinitions(opts) {
|
|
|
2982
3019
|
handler: async (args) => {
|
|
2983
3020
|
const name = String(args.name ?? "");
|
|
2984
3021
|
const rawIssue = args.issue ?? args.issueNumber;
|
|
2985
|
-
const
|
|
2986
|
-
if (
|
|
3022
|
+
const issue2 = rawIssue == null ? void 0 : Number(rawIssue);
|
|
3023
|
+
if (issue2 !== void 0 && (!Number.isFinite(issue2) || issue2 <= 0)) {
|
|
2987
3024
|
return { content: [{ type: "text", text: "Start failed: `issue` must be a positive number when provided." }] };
|
|
2988
3025
|
}
|
|
2989
|
-
const result = startCapability(
|
|
2990
|
-
|
|
2991
|
-
|
|
2992
|
-
issue,
|
|
2993
|
-
opts.repoSlug,
|
|
2994
|
-
opts.defaultBranch
|
|
2995
|
-
);
|
|
2996
|
-
const text = JSON.stringify(result);
|
|
2997
|
-
return { content: [{ type: "text", text }] };
|
|
3026
|
+
const result = startCapability(workflowFile, name, issue2, opts.repoSlug, opts.defaultBranch);
|
|
3027
|
+
const text2 = JSON.stringify(result);
|
|
3028
|
+
return { content: [{ type: "text", text: text2 }] };
|
|
2998
3029
|
}
|
|
2999
3030
|
};
|
|
3000
3031
|
const cmsTools = dashboardCmsToolDefinitions({
|
|
@@ -3209,8 +3240,8 @@ function classifySubtype(subtype) {
|
|
|
3209
3240
|
if (lower.includes("error")) return "model_error";
|
|
3210
3241
|
return "generic_failed";
|
|
3211
3242
|
}
|
|
3212
|
-
function isClaudeLoginRequiredText(
|
|
3213
|
-
const normalized =
|
|
3243
|
+
function isClaudeLoginRequiredText(text2) {
|
|
3244
|
+
const normalized = text2.toLowerCase();
|
|
3214
3245
|
return normalized.includes("not logged in") && normalized.includes("/login");
|
|
3215
3246
|
}
|
|
3216
3247
|
function resolveTurnTimeoutMs(opts) {
|
|
@@ -3549,9 +3580,9 @@ async function runAgent(opts) {
|
|
|
3549
3580
|
outcome = "completed";
|
|
3550
3581
|
outcomeKind = "ok";
|
|
3551
3582
|
sawTerminalSuccess = true;
|
|
3552
|
-
const
|
|
3553
|
-
if (isClaudeLoginRequiredText(
|
|
3554
|
-
if (
|
|
3583
|
+
const text2 = (typeof m.result === "string" ? m.result : "").trim();
|
|
3584
|
+
if (isClaudeLoginRequiredText(text2)) sawLoginRequired = true;
|
|
3585
|
+
if (text2) resultTexts.push(text2);
|
|
3555
3586
|
} else {
|
|
3556
3587
|
outcome = "failed";
|
|
3557
3588
|
outcomeKind = classifySubtype(m.subtype);
|
|
@@ -3966,9 +3997,9 @@ var init_agencyBoundaryEval = __esm({
|
|
|
3966
3997
|
});
|
|
3967
3998
|
|
|
3968
3999
|
// src/capabilityReport.ts
|
|
3969
|
-
function parseCapabilityReportsFromText(
|
|
4000
|
+
function parseCapabilityReportsFromText(text2) {
|
|
3970
4001
|
const reports = [];
|
|
3971
|
-
for (const match of
|
|
4002
|
+
for (const match of text2.matchAll(REPORT_LINE)) {
|
|
3972
4003
|
const raw = match[1]?.trim();
|
|
3973
4004
|
if (!raw) continue;
|
|
3974
4005
|
try {
|
|
@@ -4096,9 +4127,9 @@ var init_evidenceState = __esm({
|
|
|
4096
4127
|
});
|
|
4097
4128
|
|
|
4098
4129
|
// src/capabilityResult.ts
|
|
4099
|
-
function parseCapabilityResultsFromText(
|
|
4130
|
+
function parseCapabilityResultsFromText(text2) {
|
|
4100
4131
|
const results = [];
|
|
4101
|
-
for (const match of
|
|
4132
|
+
for (const match of text2.matchAll(RESULT_LINE)) {
|
|
4102
4133
|
const raw = match[1]?.trim();
|
|
4103
4134
|
if (!raw) continue;
|
|
4104
4135
|
try {
|
|
@@ -5419,8 +5450,8 @@ function loadProjectConventions(projectDir) {
|
|
|
5419
5450
|
return out;
|
|
5420
5451
|
}
|
|
5421
5452
|
function parseAgentResult(finalText) {
|
|
5422
|
-
const
|
|
5423
|
-
if (!
|
|
5453
|
+
const text2 = (finalText || "").trim();
|
|
5454
|
+
if (!text2)
|
|
5424
5455
|
return {
|
|
5425
5456
|
done: false,
|
|
5426
5457
|
commitMessage: "",
|
|
@@ -5434,7 +5465,7 @@ function parseAgentResult(finalText) {
|
|
|
5434
5465
|
const MARKDOWN_PREFIX = "[\\s>*_#`~\\-]*";
|
|
5435
5466
|
const FAILED_RE = new RegExp(`(?:^|\\n)${MARKDOWN_PREFIX}FAILED${MARKDOWN_PREFIX}\\s*:\\s*(.+?)\\s*$`, "s");
|
|
5436
5467
|
const DONE_RE = new RegExp(`(?:^|\\n)${MARKDOWN_PREFIX}DONE\\b`);
|
|
5437
|
-
const scanText = stripFencedCodeBlocks(
|
|
5468
|
+
const scanText = stripFencedCodeBlocks(text2);
|
|
5438
5469
|
const failedMatch = scanText.match(FAILED_RE);
|
|
5439
5470
|
if (failedMatch) {
|
|
5440
5471
|
return {
|
|
@@ -5449,32 +5480,32 @@ function parseAgentResult(finalText) {
|
|
|
5449
5480
|
};
|
|
5450
5481
|
}
|
|
5451
5482
|
const hasDoneMarker = DONE_RE.test(scanText);
|
|
5452
|
-
const hasCommitMsg = /^[\s>*_#`~-]*COMMIT_MSG\s*:/im.test(
|
|
5453
|
-
const hasPrSummary = /^[\s>*_#`~-]*PR_SUMMARY\s*:/im.test(
|
|
5483
|
+
const hasCommitMsg = /^[\s>*_#`~-]*COMMIT_MSG\s*:/im.test(text2);
|
|
5484
|
+
const hasPrSummary = /^[\s>*_#`~-]*PR_SUMMARY\s*:/im.test(text2);
|
|
5454
5485
|
const markerMissing = !hasDoneMarker && !hasCommitMsg && !hasPrSummary;
|
|
5455
|
-
const commitMatch =
|
|
5486
|
+
const commitMatch = text2.match(/^[\s>*_#`~-]*COMMIT_MSG[\s>*_#`~-]*\s*:\s*(.+)$/im);
|
|
5456
5487
|
const commitMessage = commitMatch ? stripMarkdownEmphasis(commitMatch[1]) : "";
|
|
5457
5488
|
const feedbackActions = extractBlock(
|
|
5458
|
-
|
|
5489
|
+
text2,
|
|
5459
5490
|
/(?:^|\n)[ \t]*FEEDBACK_ACTIONS\s*:[ \t]*\n/i,
|
|
5460
5491
|
/(?:^|\n)[ \t]*(?:PLAN_DEVIATIONS|COMMIT_MSG|PR_SUMMARY|PRIOR_ART)\s*:/i
|
|
5461
5492
|
);
|
|
5462
5493
|
let planDeviations = extractBlock(
|
|
5463
|
-
|
|
5494
|
+
text2,
|
|
5464
5495
|
/(?:^|\n)[ \t]*PLAN_DEVIATIONS\s*:[ \t]*\n/i,
|
|
5465
5496
|
/(?:^|\n)[ \t]*(?:COMMIT_MSG|PR_SUMMARY|FEEDBACK_ACTIONS|PRIOR_ART)\s*:/i
|
|
5466
5497
|
);
|
|
5467
5498
|
if (!planDeviations) {
|
|
5468
|
-
const inline =
|
|
5499
|
+
const inline = text2.match(/(?:^|\n)[ \t]*PLAN_DEVIATIONS\s*:[ \t]*(.+?)[ \t]*(?:\n|$)/i);
|
|
5469
5500
|
if (inline) planDeviations = inline[1].trim();
|
|
5470
5501
|
}
|
|
5471
5502
|
let priorArt = "";
|
|
5472
|
-
const priorArtInline =
|
|
5503
|
+
const priorArtInline = text2.match(/(?:^|\n)[ \t]*PRIOR_ART\s*:[ \t]*(.+?)[ \t]*(?:\n|$)/i);
|
|
5473
5504
|
if (priorArtInline) priorArt = priorArtInline[1].trim();
|
|
5474
|
-
const summaryStart =
|
|
5505
|
+
const summaryStart = text2.search(/(^|\n)[ \t]*PR_SUMMARY\s*:[ \t]*\n/i);
|
|
5475
5506
|
let prSummary = "";
|
|
5476
5507
|
if (summaryStart !== -1) {
|
|
5477
|
-
const afterMarker =
|
|
5508
|
+
const afterMarker = text2.slice(summaryStart).replace(/^[\s\S]*?PR_SUMMARY\s*:[ \t]*\n/i, "");
|
|
5478
5509
|
prSummary = afterMarker.replace(/\n\s*```\s*$/g, "").replace(/```\s*$/g, "").trim();
|
|
5479
5510
|
}
|
|
5480
5511
|
return {
|
|
@@ -5494,10 +5525,10 @@ function stripMarkdownEmphasis(s) {
|
|
|
5494
5525
|
function stripFencedCodeBlocks(s) {
|
|
5495
5526
|
return s.replace(/```[\s\S]*?```/g, "").replace(/~~~[\s\S]*?~~~/g, "");
|
|
5496
5527
|
}
|
|
5497
|
-
function extractBlock(
|
|
5498
|
-
const startIdx =
|
|
5528
|
+
function extractBlock(text2, startMarker, endMarker) {
|
|
5529
|
+
const startIdx = text2.search(startMarker);
|
|
5499
5530
|
if (startIdx === -1) return "";
|
|
5500
|
-
const afterStart =
|
|
5531
|
+
const afterStart = text2.slice(startIdx).replace(startMarker, "");
|
|
5501
5532
|
const endIdx = afterStart.search(endMarker);
|
|
5502
5533
|
const body = endIdx === -1 ? afterStart : afterStart.slice(0, endIdx);
|
|
5503
5534
|
return body.replace(/\n\s*```\s*$/g, "").trim();
|
|
@@ -5673,9 +5704,9 @@ function collectPages(memoryAbs) {
|
|
|
5673
5704
|
}
|
|
5674
5705
|
function extractQueryTerms(ctx) {
|
|
5675
5706
|
const terms = [];
|
|
5676
|
-
const
|
|
5707
|
+
const issue2 = ctx.data.issue;
|
|
5677
5708
|
const pr = ctx.data.pr;
|
|
5678
|
-
if (
|
|
5709
|
+
if (issue2?.title) terms.push(...tokenize(issue2.title));
|
|
5679
5710
|
if (pr?.title) terms.push(...tokenize(pr.title));
|
|
5680
5711
|
return Array.from(new Set(terms)).slice(0, 20);
|
|
5681
5712
|
}
|
|
@@ -7689,10 +7720,10 @@ var init_state2 = __esm({
|
|
|
7689
7720
|
"use strict";
|
|
7690
7721
|
VALID_STATES = /* @__PURE__ */ new Set(["active", "abandoned", "closed", "done"]);
|
|
7691
7722
|
GoalStateError = class extends Error {
|
|
7692
|
-
constructor(
|
|
7693
|
-
super(`Invalid goal state at ${
|
|
7723
|
+
constructor(path52, message) {
|
|
7724
|
+
super(`Invalid goal state at ${path52}:
|
|
7694
7725
|
${message}`);
|
|
7695
|
-
this.path =
|
|
7726
|
+
this.path = path52;
|
|
7696
7727
|
this.name = "GoalStateError";
|
|
7697
7728
|
}
|
|
7698
7729
|
path;
|
|
@@ -7705,9 +7736,9 @@ import * as fs25 from "fs";
|
|
|
7705
7736
|
function stageGoalRunLogEvent(data, goalId, event, at = nowIso()) {
|
|
7706
7737
|
const logs = goalRunLogs(data);
|
|
7707
7738
|
const existing = logs[goalId];
|
|
7708
|
-
const
|
|
7739
|
+
const path52 = existing?.path ?? goalRunLogPath(goalId, data);
|
|
7709
7740
|
logs[goalId] = {
|
|
7710
|
-
path:
|
|
7741
|
+
path: path52,
|
|
7711
7742
|
events: [...existing?.events ?? [], buildGoalRunLogEvent(data, goalId, event, at)]
|
|
7712
7743
|
};
|
|
7713
7744
|
}
|
|
@@ -8726,6 +8757,290 @@ var init_typeDefinitions = __esm({
|
|
|
8726
8757
|
}
|
|
8727
8758
|
});
|
|
8728
8759
|
|
|
8760
|
+
// src/workflowValidation.ts
|
|
8761
|
+
function validateWorkflow(value, options = {}) {
|
|
8762
|
+
const issues = [];
|
|
8763
|
+
const workflow = asRecord2(value);
|
|
8764
|
+
const rawSteps = Array.isArray(value) ? value : Array.isArray(workflow?.steps) ? workflow.steps : [];
|
|
8765
|
+
const maxSteps = options.maxSteps ?? 100;
|
|
8766
|
+
const maxTransitions = options.maxTransitionsPerStep ?? 20;
|
|
8767
|
+
const maxLoopIterations = options.maxLoopIterations ?? 100;
|
|
8768
|
+
if (rawSteps.length === 0) {
|
|
8769
|
+
issue(issues, "steps_required", "steps", "workflow must contain at least one step");
|
|
8770
|
+
return issues;
|
|
8771
|
+
}
|
|
8772
|
+
if (rawSteps.length > maxSteps) {
|
|
8773
|
+
issue(issues, "too_many_steps", "steps", `workflow has ${rawSteps.length} steps; maximum is ${maxSteps}`);
|
|
8774
|
+
}
|
|
8775
|
+
const graphMode = workflow?.startAt !== void 0 || rawSteps.some((entry) => {
|
|
8776
|
+
const step = asRecord2(entry);
|
|
8777
|
+
return Boolean(step && (step.id !== void 0 || step.next !== void 0 || step.inputs !== void 0));
|
|
8778
|
+
});
|
|
8779
|
+
const steps = rawSteps.map(
|
|
8780
|
+
(entry) => typeof entry === "string" ? { capability: entry } : asRecord2(entry)
|
|
8781
|
+
);
|
|
8782
|
+
const ids = [];
|
|
8783
|
+
steps.forEach((step, index) => {
|
|
8784
|
+
const base = `steps[${index}]`;
|
|
8785
|
+
if (!step) {
|
|
8786
|
+
issue(issues, "invalid_step", base, "workflow step must be a capability name or an object");
|
|
8787
|
+
return;
|
|
8788
|
+
}
|
|
8789
|
+
for (const field of Object.keys(step)) {
|
|
8790
|
+
if (!SUPPORTED_STEP_FIELDS.has(field)) {
|
|
8791
|
+
issue(issues, "unsupported_step_field", `${base}.${field}`, `workflow step field ${field} is not supported`);
|
|
8792
|
+
}
|
|
8793
|
+
}
|
|
8794
|
+
const capability = text(step.capability ?? step.action);
|
|
8795
|
+
if (!capability || !SAFE_NAME.test(capability)) {
|
|
8796
|
+
issue(issues, "invalid_capability", `${base}.capability`, "workflow step must name a valid capability");
|
|
8797
|
+
} else if (options.knownCapabilities && !options.knownCapabilities.has(capability)) {
|
|
8798
|
+
issue(
|
|
8799
|
+
issues,
|
|
8800
|
+
"unknown_capability",
|
|
8801
|
+
`${base}.capability`,
|
|
8802
|
+
`workflow step references unknown capability ${capability}`
|
|
8803
|
+
);
|
|
8804
|
+
}
|
|
8805
|
+
if (graphMode) {
|
|
8806
|
+
const id = text(step.id);
|
|
8807
|
+
if (!id || !SAFE_NAME.test(id)) {
|
|
8808
|
+
issue(issues, "invalid_step_id", `${base}.id`, "graph workflow steps must each have a valid id");
|
|
8809
|
+
} else {
|
|
8810
|
+
ids.push(id);
|
|
8811
|
+
}
|
|
8812
|
+
}
|
|
8813
|
+
validateDataMatch(step.runWhen, `${base}.runWhen`, issues);
|
|
8814
|
+
const inputs = asRecord2(step.inputs);
|
|
8815
|
+
if (step.inputs !== void 0 && !inputs) {
|
|
8816
|
+
issue(issues, "invalid_inputs", `${base}.inputs`, "workflow step inputs must be an object");
|
|
8817
|
+
}
|
|
8818
|
+
if (inputs) {
|
|
8819
|
+
for (const [name, mapping] of Object.entries(inputs)) {
|
|
8820
|
+
const inputPath = `${base}.inputs.${name}`;
|
|
8821
|
+
if (!SAFE_NAME.test(name)) issue(issues, "invalid_input_name", inputPath, `invalid input name ${name}`);
|
|
8822
|
+
const from = text(asRecord2(mapping)?.from);
|
|
8823
|
+
if (!from || !SAFE_DATA_PATH.test(from)) {
|
|
8824
|
+
issue(
|
|
8825
|
+
issues,
|
|
8826
|
+
"invalid_data_path",
|
|
8827
|
+
`${inputPath}.from`,
|
|
8828
|
+
`workflow input ${name} must read from facts, evidence, artifacts, result, workflow, or lastOutcome`
|
|
8829
|
+
);
|
|
8830
|
+
}
|
|
8831
|
+
const declared = capability ? options.capabilityInputs?.get(capability) : void 0;
|
|
8832
|
+
if (declared && !declared.has(name)) {
|
|
8833
|
+
issue(
|
|
8834
|
+
issues,
|
|
8835
|
+
"unknown_capability_input",
|
|
8836
|
+
inputPath,
|
|
8837
|
+
`capability ${capability} does not declare input ${name}`
|
|
8838
|
+
);
|
|
8839
|
+
}
|
|
8840
|
+
}
|
|
8841
|
+
}
|
|
8842
|
+
});
|
|
8843
|
+
if (!graphMode) return issues;
|
|
8844
|
+
const seen = /* @__PURE__ */ new Set();
|
|
8845
|
+
ids.forEach((id, index) => {
|
|
8846
|
+
if (seen.has(id)) issue(issues, "duplicate_step_id", `steps[${index}].id`, `workflow step id ${id} is duplicated`);
|
|
8847
|
+
seen.add(id);
|
|
8848
|
+
});
|
|
8849
|
+
const startAt = text(workflow?.startAt) ?? text(steps[0]?.id);
|
|
8850
|
+
if (!startAt || !seen.has(startAt)) {
|
|
8851
|
+
issue(issues, "missing_start_step", "startAt", `workflow startAt references missing step ${startAt ?? "<none>"}`);
|
|
8852
|
+
}
|
|
8853
|
+
const adjacency = /* @__PURE__ */ new Map();
|
|
8854
|
+
steps.forEach((step, index) => {
|
|
8855
|
+
if (!step) return;
|
|
8856
|
+
const id = text(step.id);
|
|
8857
|
+
if (!id) return;
|
|
8858
|
+
const transitions = transitionList(step.next);
|
|
8859
|
+
adjacency.set(id, []);
|
|
8860
|
+
if (transitions.length > maxTransitions) {
|
|
8861
|
+
issue(
|
|
8862
|
+
issues,
|
|
8863
|
+
"too_many_transitions",
|
|
8864
|
+
`steps[${index}].next`,
|
|
8865
|
+
`workflow step ${id} has ${transitions.length} connections; maximum is ${maxTransitions}`
|
|
8866
|
+
);
|
|
8867
|
+
}
|
|
8868
|
+
const defaults = transitions.filter((transition) => asRecord2(transition)?.default === true);
|
|
8869
|
+
const conditionals = transitions.filter((transition) => asRecord2(transition)?.when !== void 0);
|
|
8870
|
+
const unconditional = transitions.filter((transition) => {
|
|
8871
|
+
const raw = asRecord2(transition);
|
|
8872
|
+
return typeof transition === "string" || Boolean(raw && raw.when === void 0 && raw.default !== true && raw.maxIterations === void 0);
|
|
8873
|
+
});
|
|
8874
|
+
if (defaults.length > 1) {
|
|
8875
|
+
issue(
|
|
8876
|
+
issues,
|
|
8877
|
+
"multiple_default_transitions",
|
|
8878
|
+
`steps[${index}].next`,
|
|
8879
|
+
`workflow step ${id} has more than one default connection`
|
|
8880
|
+
);
|
|
8881
|
+
}
|
|
8882
|
+
if (conditionals.length > 0 && defaults.length !== 1) {
|
|
8883
|
+
issue(
|
|
8884
|
+
issues,
|
|
8885
|
+
"missing_default_transition",
|
|
8886
|
+
`steps[${index}].next`,
|
|
8887
|
+
`workflow step ${id} has conditions and needs one default connection`
|
|
8888
|
+
);
|
|
8889
|
+
}
|
|
8890
|
+
if (unconditional.length > 1 || unconditional.length > 0 && transitions.length > 1) {
|
|
8891
|
+
issue(
|
|
8892
|
+
issues,
|
|
8893
|
+
"ambiguous_transition",
|
|
8894
|
+
`steps[${index}].next`,
|
|
8895
|
+
`workflow step ${id} mixes an unconditional connection with other connections`
|
|
8896
|
+
);
|
|
8897
|
+
}
|
|
8898
|
+
transitions.forEach((transition, transitionIndex) => {
|
|
8899
|
+
const raw = typeof transition === "string" ? { to: transition } : asRecord2(transition);
|
|
8900
|
+
const base = `steps[${index}].next[${transitionIndex}]`;
|
|
8901
|
+
if (!raw) {
|
|
8902
|
+
issue(issues, "invalid_transition", base, "workflow connection must be a step id or an object");
|
|
8903
|
+
return;
|
|
8904
|
+
}
|
|
8905
|
+
for (const field of Object.keys(raw)) {
|
|
8906
|
+
if (!SUPPORTED_TRANSITION_FIELDS.has(field)) {
|
|
8907
|
+
issue(
|
|
8908
|
+
issues,
|
|
8909
|
+
"unsupported_transition_field",
|
|
8910
|
+
`${base}.${field}`,
|
|
8911
|
+
`workflow connection field ${field} is not supported`
|
|
8912
|
+
);
|
|
8913
|
+
}
|
|
8914
|
+
}
|
|
8915
|
+
const target = text(raw.to);
|
|
8916
|
+
if (!target || !SAFE_NAME.test(target)) {
|
|
8917
|
+
issue(issues, "invalid_transition_target", `${base}.to`, "workflow connection must name a valid target step");
|
|
8918
|
+
return;
|
|
8919
|
+
}
|
|
8920
|
+
if (!seen.has(target)) {
|
|
8921
|
+
issue(
|
|
8922
|
+
issues,
|
|
8923
|
+
"missing_transition_target",
|
|
8924
|
+
`${base}.to`,
|
|
8925
|
+
`workflow step ${id} connects to missing step ${target}`
|
|
8926
|
+
);
|
|
8927
|
+
} else {
|
|
8928
|
+
adjacency.get(id)?.push(target);
|
|
8929
|
+
}
|
|
8930
|
+
if (raw.default === true && raw.when !== void 0) {
|
|
8931
|
+
issue(issues, "conflicting_transition", base, "workflow connection cannot be both conditional and default");
|
|
8932
|
+
}
|
|
8933
|
+
if (raw.when !== void 0) validateDataMatch(raw.when, `${base}.when`, issues);
|
|
8934
|
+
const targetIndex = ids.indexOf(target ?? "");
|
|
8935
|
+
const iterations = raw.maxIterations;
|
|
8936
|
+
if (targetIndex >= 0 && targetIndex <= index) {
|
|
8937
|
+
if (!Number.isInteger(iterations) || Number(iterations) < 1) {
|
|
8938
|
+
issue(
|
|
8939
|
+
issues,
|
|
8940
|
+
"unbounded_loop",
|
|
8941
|
+
`${base}.maxIterations`,
|
|
8942
|
+
`workflow loop ${id}->${target} must set maxIterations`
|
|
8943
|
+
);
|
|
8944
|
+
} else if (Number(iterations) > maxLoopIterations) {
|
|
8945
|
+
issue(
|
|
8946
|
+
issues,
|
|
8947
|
+
"loop_limit_too_high",
|
|
8948
|
+
`${base}.maxIterations`,
|
|
8949
|
+
`workflow loop ${id}->${target} exceeds maximum ${maxLoopIterations}`
|
|
8950
|
+
);
|
|
8951
|
+
}
|
|
8952
|
+
} else if (iterations !== void 0 && (!Number.isInteger(iterations) || Number(iterations) < 1)) {
|
|
8953
|
+
issue(issues, "invalid_loop_limit", `${base}.maxIterations`, "maxIterations must be a positive integer");
|
|
8954
|
+
}
|
|
8955
|
+
});
|
|
8956
|
+
});
|
|
8957
|
+
if (startAt && seen.has(startAt)) {
|
|
8958
|
+
const reachable = /* @__PURE__ */ new Set();
|
|
8959
|
+
const pending = [startAt];
|
|
8960
|
+
while (pending.length > 0) {
|
|
8961
|
+
const id = pending.pop();
|
|
8962
|
+
if (reachable.has(id)) continue;
|
|
8963
|
+
reachable.add(id);
|
|
8964
|
+
pending.push(...adjacency.get(id) ?? []);
|
|
8965
|
+
}
|
|
8966
|
+
ids.forEach((id, index) => {
|
|
8967
|
+
if (!reachable.has(id)) issue(issues, "unreachable_step", `steps[${index}]`, `workflow step ${id} is unreachable`);
|
|
8968
|
+
});
|
|
8969
|
+
if (![...reachable].some((id) => (adjacency.get(id) ?? []).length === 0)) {
|
|
8970
|
+
issue(issues, "missing_terminal_step", "steps", "workflow has no reachable final step");
|
|
8971
|
+
}
|
|
8972
|
+
}
|
|
8973
|
+
return issues;
|
|
8974
|
+
}
|
|
8975
|
+
function formatWorkflowValidationIssues(issues) {
|
|
8976
|
+
return issues.map((entry) => `${entry.path}: ${entry.message}`);
|
|
8977
|
+
}
|
|
8978
|
+
function validateDataMatch(value, path52, issues) {
|
|
8979
|
+
if (value === void 0) return;
|
|
8980
|
+
const match = asRecord2(value);
|
|
8981
|
+
if (!match || Object.keys(match).length === 0) {
|
|
8982
|
+
issue(issues, "invalid_condition", path52, "workflow condition must contain at least one match");
|
|
8983
|
+
return;
|
|
8984
|
+
}
|
|
8985
|
+
for (const [field, expected] of Object.entries(match)) {
|
|
8986
|
+
if (!SAFE_DATA_PATH.test(field)) {
|
|
8987
|
+
issue(
|
|
8988
|
+
issues,
|
|
8989
|
+
"invalid_data_path",
|
|
8990
|
+
`${path52}.${field}`,
|
|
8991
|
+
`workflow condition must read from facts, evidence, artifacts, result, workflow, or lastOutcome`
|
|
8992
|
+
);
|
|
8993
|
+
}
|
|
8994
|
+
if (!isComparable(expected)) {
|
|
8995
|
+
issue(issues, "invalid_condition_value", `${path52}.${field}`, "workflow condition value must be a JSON scalar");
|
|
8996
|
+
}
|
|
8997
|
+
}
|
|
8998
|
+
}
|
|
8999
|
+
function transitionList(value) {
|
|
9000
|
+
if (value === void 0) return [];
|
|
9001
|
+
return Array.isArray(value) ? value : [value];
|
|
9002
|
+
}
|
|
9003
|
+
function asRecord2(value) {
|
|
9004
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
9005
|
+
}
|
|
9006
|
+
function text(value) {
|
|
9007
|
+
return typeof value === "string" && value.trim() ? value.trim() : void 0;
|
|
9008
|
+
}
|
|
9009
|
+
function isComparable(value) {
|
|
9010
|
+
if (value === null || ["string", "number", "boolean"].includes(typeof value)) return true;
|
|
9011
|
+
return Array.isArray(value) && value.length > 0 && value.every((item) => isComparable(item) && !Array.isArray(item));
|
|
9012
|
+
}
|
|
9013
|
+
function issue(issues, code, path52, message) {
|
|
9014
|
+
issues.push({ code, path: path52, message });
|
|
9015
|
+
}
|
|
9016
|
+
var SAFE_NAME, SAFE_DATA_PATH, SUPPORTED_STEP_FIELDS, SUPPORTED_TRANSITION_FIELDS;
|
|
9017
|
+
var init_workflowValidation = __esm({
|
|
9018
|
+
"src/workflowValidation.ts"() {
|
|
9019
|
+
"use strict";
|
|
9020
|
+
SAFE_NAME = /^[a-z][a-z0-9-]*$/;
|
|
9021
|
+
SAFE_DATA_PATH = /^(facts|evidence|artifacts|result|workflow|lastOutcome)(?:\.[A-Za-z_][A-Za-z0-9_-]*)+$/;
|
|
9022
|
+
SUPPORTED_STEP_FIELDS = /* @__PURE__ */ new Set([
|
|
9023
|
+
"id",
|
|
9024
|
+
"capability",
|
|
9025
|
+
"action",
|
|
9026
|
+
"implementation",
|
|
9027
|
+
"evidence",
|
|
9028
|
+
"target",
|
|
9029
|
+
"targetFact",
|
|
9030
|
+
"reason",
|
|
9031
|
+
"agent",
|
|
9032
|
+
"cliArgs",
|
|
9033
|
+
"inputs",
|
|
9034
|
+
"next",
|
|
9035
|
+
"runWhen",
|
|
9036
|
+
"continueOn",
|
|
9037
|
+
"saveReport",
|
|
9038
|
+
"report"
|
|
9039
|
+
]);
|
|
9040
|
+
SUPPORTED_TRANSITION_FIELDS = /* @__PURE__ */ new Set(["to", "when", "default", "maxIterations"]);
|
|
9041
|
+
}
|
|
9042
|
+
});
|
|
9043
|
+
|
|
8729
9044
|
// src/workflowDefinitions.ts
|
|
8730
9045
|
import * as fs28 from "fs";
|
|
8731
9046
|
import * as path26 from "path";
|
|
@@ -8742,7 +9057,18 @@ function normalizeWorkflowDefinition(value) {
|
|
|
8742
9057
|
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
8743
9058
|
const raw = value;
|
|
8744
9059
|
const name = typeof raw.name === "string" ? raw.name.trim() : "";
|
|
8745
|
-
const
|
|
9060
|
+
const hasGraphConnections = Array.isArray(raw.steps) && raw.steps.some(
|
|
9061
|
+
(step) => step && typeof step === "object" && !Array.isArray(step) && (step.next !== void 0 || step.inputs !== void 0)
|
|
9062
|
+
);
|
|
9063
|
+
if (hasGraphConnections) {
|
|
9064
|
+
if (validateWorkflow({ steps: raw.steps, ...raw.startAt !== void 0 ? { startAt: raw.startAt } : {} }).length > 0) {
|
|
9065
|
+
return null;
|
|
9066
|
+
}
|
|
9067
|
+
}
|
|
9068
|
+
const workflow = parseCapabilityWorkflow({
|
|
9069
|
+
steps: raw.steps,
|
|
9070
|
+
startAt: raw.startAt
|
|
9071
|
+
});
|
|
8746
9072
|
const steps = workflow?.steps;
|
|
8747
9073
|
const capabilities = steps ? steps.map((step) => step.capability) : normalizeWorkflowCapabilities(raw.capabilities);
|
|
8748
9074
|
if (!name || capabilities.length === 0) return null;
|
|
@@ -8752,6 +9078,7 @@ function normalizeWorkflowDefinition(value) {
|
|
|
8752
9078
|
capabilities,
|
|
8753
9079
|
...raw.runWithoutApproval === true ? { runWithoutApproval: true } : {},
|
|
8754
9080
|
...steps ? { steps } : {},
|
|
9081
|
+
...workflow?.startAt ? { startAt: workflow.startAt } : {},
|
|
8755
9082
|
...typeof raw.createdAt === "string" ? { createdAt: raw.createdAt } : {},
|
|
8756
9083
|
...typeof raw.updatedAt === "string" ? { updatedAt: raw.updatedAt } : {}
|
|
8757
9084
|
};
|
|
@@ -8793,7 +9120,8 @@ function normalizeWorkflowCapabilities(value) {
|
|
|
8793
9120
|
}
|
|
8794
9121
|
function workflowDefinitionToConfig(workflow) {
|
|
8795
9122
|
return {
|
|
8796
|
-
steps: workflow.steps ?? workflow.capabilities.map((capability) => ({ capability }))
|
|
9123
|
+
steps: workflow.steps ?? workflow.capabilities.map((capability) => ({ capability })),
|
|
9124
|
+
...workflow.startAt ? { startAt: workflow.startAt } : {}
|
|
8797
9125
|
};
|
|
8798
9126
|
}
|
|
8799
9127
|
function readCompanyStoreWorkflowDefinition(id) {
|
|
@@ -8817,6 +9145,7 @@ var init_workflowDefinitions = __esm({
|
|
|
8817
9145
|
init_capabilityFolders();
|
|
8818
9146
|
init_companyStore();
|
|
8819
9147
|
init_stateRepo();
|
|
9148
|
+
init_workflowValidation();
|
|
8820
9149
|
WORKFLOW_ID_PATTERN = /^[a-z0-9][a-z0-9_-]{0,79}$/;
|
|
8821
9150
|
CAPABILITY_ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9_-]{0,79}$/;
|
|
8822
9151
|
}
|
|
@@ -9639,7 +9968,7 @@ function readSimpleGoalTaskSummary(goalId, cwd) {
|
|
|
9639
9968
|
);
|
|
9640
9969
|
const issues = JSON.parse(raw);
|
|
9641
9970
|
const total = issues.length;
|
|
9642
|
-
const open = issues.filter((
|
|
9971
|
+
const open = issues.filter((issue2) => String(issue2.state ?? "").toLowerCase() === "open").length;
|
|
9643
9972
|
return { total, open };
|
|
9644
9973
|
}
|
|
9645
9974
|
function previousDispatchWasTargetInstance(managed, previousScheduleState) {
|
|
@@ -9688,7 +10017,7 @@ function findExistingGoalIssue(goalId, cwd) {
|
|
|
9688
10017
|
const marker = goalIssueMarker(goalId);
|
|
9689
10018
|
const raw = gh(["issue", "list", "--state", "all", "--limit", "100", "--json", "number,body"], { cwd });
|
|
9690
10019
|
const issues = JSON.parse(raw);
|
|
9691
|
-
const match = issues.find((
|
|
10020
|
+
const match = issues.find((issue2) => typeof issue2.number === "number" && issue2.body?.includes(marker));
|
|
9692
10021
|
return match?.number ?? null;
|
|
9693
10022
|
}
|
|
9694
10023
|
function createGoalIssue(goal, goalId, cwd) {
|
|
@@ -10537,13 +10866,13 @@ function ensureNeedsFixIssue(ctx, goalId, state, evidence, evidenceKey) {
|
|
|
10537
10866
|
const evidenceState = parseGoalEvidenceState(state.extra.evidenceState);
|
|
10538
10867
|
const progress = evidenceState[evidenceKey];
|
|
10539
10868
|
if (progress?.issue) return state;
|
|
10540
|
-
const
|
|
10869
|
+
const issue2 = findExistingNeedsFixIssue(goalId, evidenceKey, ctx.cwd) ?? createNeedsFixIssue(goalId, evidenceKey, evidence, ctx.cwd);
|
|
10541
10870
|
const nextEvidenceState = mergeGoalEvidenceProgress(evidenceState, evidenceKey, {
|
|
10542
10871
|
resultClass: "needsFix",
|
|
10543
10872
|
attempts: progress?.attempts ?? 1,
|
|
10544
10873
|
reason: evidence.summary,
|
|
10545
|
-
nextAction: `fix issue #${
|
|
10546
|
-
issue,
|
|
10874
|
+
nextAction: `fix issue #${issue2}`,
|
|
10875
|
+
issue: issue2,
|
|
10547
10876
|
updatedAt: nowIso()
|
|
10548
10877
|
});
|
|
10549
10878
|
return {
|
|
@@ -10552,7 +10881,7 @@ function ensureNeedsFixIssue(ctx, goalId, state, evidence, evidenceKey) {
|
|
|
10552
10881
|
...state.extra,
|
|
10553
10882
|
evidenceState: nextEvidenceState,
|
|
10554
10883
|
reason: evidence.summary,
|
|
10555
|
-
nextAction: `fix issue #${
|
|
10884
|
+
nextAction: `fix issue #${issue2}`
|
|
10556
10885
|
}
|
|
10557
10886
|
};
|
|
10558
10887
|
}
|
|
@@ -10570,7 +10899,7 @@ function findExistingNeedsFixIssue(goalId, evidence, cwd) {
|
|
|
10570
10899
|
const marker = needsFixIssueMarker(goalId, evidence);
|
|
10571
10900
|
const raw = gh(["issue", "list", "--state", "all", "--limit", "100", "--json", "number,body"], { cwd });
|
|
10572
10901
|
const issues = JSON.parse(raw);
|
|
10573
|
-
const match = issues.find((
|
|
10902
|
+
const match = issues.find((issue2) => typeof issue2.number === "number" && issue2.body?.includes(marker));
|
|
10574
10903
|
return match?.number ?? null;
|
|
10575
10904
|
}
|
|
10576
10905
|
function createNeedsFixIssue(goalId, evidence, result, cwd) {
|
|
@@ -11008,8 +11337,8 @@ var init_classifyByLabel = __esm({
|
|
|
11008
11337
|
"use strict";
|
|
11009
11338
|
VALID_CLASSES = /* @__PURE__ */ new Set(["feature", "bug", "spec", "chore"]);
|
|
11010
11339
|
classifyByLabel = async (ctx) => {
|
|
11011
|
-
const
|
|
11012
|
-
const labels =
|
|
11340
|
+
const issue2 = ctx.data.issue;
|
|
11341
|
+
const labels = issue2?.labels;
|
|
11013
11342
|
if (!labels || labels.length === 0) return;
|
|
11014
11343
|
const cfgMap = ctx.config.classify?.labelMap;
|
|
11015
11344
|
const map = cfgMap ?? defaultLabelMap();
|
|
@@ -11535,19 +11864,19 @@ function buildGoalName(scope, verdict) {
|
|
|
11535
11864
|
const verdictTag = verdict === "UNKNOWN" ? "REPORT" : verdict;
|
|
11536
11865
|
return `QA: ${focus} \u2014 ${verdictTag} \u2014 ${todayIso()}`.slice(0, 240);
|
|
11537
11866
|
}
|
|
11538
|
-
function splitReport(
|
|
11539
|
-
const open =
|
|
11867
|
+
function splitReport(text2) {
|
|
11868
|
+
const open = text2.indexOf(REPORT_JSON_OPEN);
|
|
11540
11869
|
if (open < 0) {
|
|
11541
|
-
const fallback = parseFallbackFindingsJson(
|
|
11870
|
+
const fallback = parseFallbackFindingsJson(text2);
|
|
11542
11871
|
if (fallback) return fallback;
|
|
11543
|
-
return { markdown:
|
|
11872
|
+
return { markdown: text2.trim(), data: null, jsonError: "no JSON block marker" };
|
|
11544
11873
|
}
|
|
11545
|
-
const closeRel =
|
|
11874
|
+
const closeRel = text2.slice(open + REPORT_JSON_OPEN.length).indexOf(REPORT_JSON_CLOSE);
|
|
11546
11875
|
if (closeRel < 0) {
|
|
11547
|
-
return { markdown:
|
|
11876
|
+
return { markdown: text2.slice(0, open).trim(), data: null, jsonError: "JSON block not terminated" };
|
|
11548
11877
|
}
|
|
11549
11878
|
const closeAbs = open + REPORT_JSON_OPEN.length + closeRel;
|
|
11550
|
-
const rawJson =
|
|
11879
|
+
const rawJson = text2.slice(open + REPORT_JSON_OPEN.length, closeAbs).trim();
|
|
11551
11880
|
const fenced = rawJson.match(/^```(?:json)?\s*([\s\S]*?)\s*```$/);
|
|
11552
11881
|
const cleanJson = fenced ? fenced[1].trim() : rawJson;
|
|
11553
11882
|
let parsed = null;
|
|
@@ -11562,11 +11891,11 @@ function splitReport(text) {
|
|
|
11562
11891
|
} catch (err) {
|
|
11563
11892
|
parseError = err instanceof Error ? err.message : String(err);
|
|
11564
11893
|
}
|
|
11565
|
-
const markdown =
|
|
11894
|
+
const markdown = text2.slice(0, open).trim();
|
|
11566
11895
|
return { markdown, data: parsed, jsonError: parseError };
|
|
11567
11896
|
}
|
|
11568
|
-
function parseFallbackFindingsJson(
|
|
11569
|
-
const fences = [...
|
|
11897
|
+
function parseFallbackFindingsJson(text2) {
|
|
11898
|
+
const fences = [...text2.matchAll(/```(?:json)?\s*([\s\S]*?)\s*```/gi)];
|
|
11570
11899
|
for (let i = fences.length - 1; i >= 0; i--) {
|
|
11571
11900
|
const match = fences[i];
|
|
11572
11901
|
const raw = match[1]?.trim();
|
|
@@ -11575,18 +11904,18 @@ function parseFallbackFindingsJson(text) {
|
|
|
11575
11904
|
const parsed = JSON.parse(raw);
|
|
11576
11905
|
if (!parsed || !Array.isArray(parsed.findings)) {
|
|
11577
11906
|
return {
|
|
11578
|
-
markdown: removeFence(
|
|
11907
|
+
markdown: removeFence(text2, match).trim(),
|
|
11579
11908
|
data: null,
|
|
11580
11909
|
jsonError: "fallback JSON missing 'findings' array"
|
|
11581
11910
|
};
|
|
11582
11911
|
}
|
|
11583
11912
|
return {
|
|
11584
|
-
markdown: removeFence(
|
|
11913
|
+
markdown: removeFence(text2, match).trim(),
|
|
11585
11914
|
data: { findings: parsed.findings.map((f, idx) => normalizeFallbackFinding(f, idx)) }
|
|
11586
11915
|
};
|
|
11587
11916
|
} catch (err) {
|
|
11588
11917
|
return {
|
|
11589
|
-
markdown: removeFence(
|
|
11918
|
+
markdown: removeFence(text2, match).trim(),
|
|
11590
11919
|
data: null,
|
|
11591
11920
|
jsonError: err instanceof Error ? err.message : String(err)
|
|
11592
11921
|
};
|
|
@@ -11594,10 +11923,10 @@ function parseFallbackFindingsJson(text) {
|
|
|
11594
11923
|
}
|
|
11595
11924
|
return null;
|
|
11596
11925
|
}
|
|
11597
|
-
function removeFence(
|
|
11926
|
+
function removeFence(text2, match) {
|
|
11598
11927
|
const start = match.index ?? -1;
|
|
11599
|
-
if (start < 0) return
|
|
11600
|
-
return `${
|
|
11928
|
+
if (start < 0) return text2;
|
|
11929
|
+
return `${text2.slice(0, start)}${text2.slice(start + match[0].length)}`;
|
|
11601
11930
|
}
|
|
11602
11931
|
function normalizeFallbackFinding(raw, idx) {
|
|
11603
11932
|
const finding = raw && typeof raw === "object" ? raw : {};
|
|
@@ -11654,9 +11983,9 @@ function loadManifest(cwd) {
|
|
|
11654
11983
|
return { number: null, manifest: { version: 1, goals: [] } };
|
|
11655
11984
|
}
|
|
11656
11985
|
if (arr.length === 0) return { number: null, manifest: { version: 1, goals: [] } };
|
|
11657
|
-
const
|
|
11658
|
-
const manifest = parseManifestBody(
|
|
11659
|
-
return { number:
|
|
11986
|
+
const issue2 = arr[0];
|
|
11987
|
+
const manifest = parseManifestBody(issue2.body);
|
|
11988
|
+
return { number: issue2.number, manifest };
|
|
11660
11989
|
}
|
|
11661
11990
|
function parseManifestBody(body) {
|
|
11662
11991
|
if (!body) return { version: 1, goals: [] };
|
|
@@ -11882,8 +12211,8 @@ ${markdown}`, ctx.cwd);
|
|
|
11882
12211
|
const failed = [];
|
|
11883
12212
|
for (const f of findings) {
|
|
11884
12213
|
try {
|
|
11885
|
-
const
|
|
11886
|
-
opened.push({ ...
|
|
12214
|
+
const issue2 = createTaskIssue(f, goalId, manifestIssueNumber, ctx.cwd);
|
|
12215
|
+
opened.push({ ...issue2, severity: f.severity });
|
|
11887
12216
|
} catch (err) {
|
|
11888
12217
|
const reason = err instanceof Error ? err.message : String(err);
|
|
11889
12218
|
failed.push({ title: f.title, reason });
|
|
@@ -12025,8 +12354,8 @@ var init_deriveQaScopeFromIssue = __esm({
|
|
|
12025
12354
|
if (!Number.isFinite(issueNumber) || issueNumber <= 0) return;
|
|
12026
12355
|
let title = "";
|
|
12027
12356
|
try {
|
|
12028
|
-
const
|
|
12029
|
-
title = (
|
|
12357
|
+
const issue2 = getIssue(issueNumber, ctx.cwd);
|
|
12358
|
+
title = (issue2.title ?? "").trim();
|
|
12030
12359
|
} catch (err) {
|
|
12031
12360
|
process.stderr.write(
|
|
12032
12361
|
`[kody] deriveQaScopeFromIssue: could not read #${issueNumber}: ${err instanceof Error ? err.message : String(err)}
|
|
@@ -12631,28 +12960,28 @@ var init_dispatchCapabilityTicks = __esm({
|
|
|
12631
12960
|
process.stdout.write(`[jobs] ticking ${issues.length} issue(s) via ${targetImplementation}
|
|
12632
12961
|
`);
|
|
12633
12962
|
const results = [];
|
|
12634
|
-
for (const
|
|
12635
|
-
process.stdout.write(`[jobs] \u2192 tick #${
|
|
12963
|
+
for (const issue2 of issues) {
|
|
12964
|
+
process.stdout.write(`[jobs] \u2192 tick #${issue2.number}: ${issue2.title}
|
|
12636
12965
|
`);
|
|
12637
12966
|
try {
|
|
12638
12967
|
const out = await runJob(
|
|
12639
12968
|
mintScheduledJob({
|
|
12640
12969
|
capability: targetImplementation,
|
|
12641
12970
|
implementation: targetImplementation,
|
|
12642
|
-
cliArgs: { [issueArg]:
|
|
12971
|
+
cliArgs: { [issueArg]: issue2.number }
|
|
12643
12972
|
}),
|
|
12644
12973
|
{ cwd: ctx.cwd, config: ctx.config, verbose: ctx.verbose, quiet: ctx.quiet, chain: false }
|
|
12645
12974
|
);
|
|
12646
|
-
results.push({ issue:
|
|
12975
|
+
results.push({ issue: issue2.number, exitCode: out.exitCode, reason: out.reason });
|
|
12647
12976
|
if (out.exitCode !== 0) {
|
|
12648
|
-
process.stderr.write(`[jobs] tick #${
|
|
12977
|
+
process.stderr.write(`[jobs] tick #${issue2.number} failed (exit ${out.exitCode}): ${out.reason ?? ""}
|
|
12649
12978
|
`);
|
|
12650
12979
|
}
|
|
12651
12980
|
} catch (err) {
|
|
12652
12981
|
const msg = err instanceof Error ? err.message : String(err);
|
|
12653
|
-
process.stderr.write(`[jobs] tick #${
|
|
12982
|
+
process.stderr.write(`[jobs] tick #${issue2.number} crashed: ${msg}
|
|
12654
12983
|
`);
|
|
12655
|
-
results.push({ issue:
|
|
12984
|
+
results.push({ issue: issue2.number, exitCode: 99, reason: msg });
|
|
12656
12985
|
}
|
|
12657
12986
|
}
|
|
12658
12987
|
ctx.data.jobTickResults = results;
|
|
@@ -12921,7 +13250,14 @@ function updateExistingPr(existing, body, draft, cwd, preserveBody) {
|
|
|
12921
13250
|
const promotedTitle = existing.title?.replace(/^\[WIP\]\s*/, "");
|
|
12922
13251
|
if (promotedTitle && promotedTitle !== existing.title) {
|
|
12923
13252
|
gh(
|
|
12924
|
-
[
|
|
13253
|
+
[
|
|
13254
|
+
"api",
|
|
13255
|
+
"--method",
|
|
13256
|
+
"PATCH",
|
|
13257
|
+
`repos/${owner}/${repo}/pulls/${existing.number}`,
|
|
13258
|
+
"-f",
|
|
13259
|
+
`title=${promotedTitle}`
|
|
13260
|
+
],
|
|
12925
13261
|
{ cwd, preferRepoToken: true }
|
|
12926
13262
|
);
|
|
12927
13263
|
}
|
|
@@ -13067,10 +13403,10 @@ var init_ensurePr = __esm({
|
|
|
13067
13403
|
const failureReason = computeFailureReason(ctx);
|
|
13068
13404
|
const isFailure = failureReason.length > 0;
|
|
13069
13405
|
const changedFiles = ctx.data.changedFiles ?? [];
|
|
13070
|
-
const
|
|
13406
|
+
const issue2 = ctx.data.issue;
|
|
13071
13407
|
const pr = ctx.data.pr;
|
|
13072
13408
|
const targetNumber = Number(ctx.data.commentTargetNumber ?? 0);
|
|
13073
|
-
const title =
|
|
13409
|
+
const title = issue2?.title ?? pr?.title ?? `kody changes`;
|
|
13074
13410
|
const baseBranch = ctx.data.baseBranch;
|
|
13075
13411
|
try {
|
|
13076
13412
|
const result = ensurePr({
|
|
@@ -13121,12 +13457,12 @@ var init_failOnceTaskJob = __esm({
|
|
|
13121
13457
|
init_jobIdentity();
|
|
13122
13458
|
failOnceTaskJob = async (ctx, profile) => {
|
|
13123
13459
|
ctx.skipAgent = true;
|
|
13124
|
-
const
|
|
13460
|
+
const issue2 = typeof ctx.args.issue === "number" ? ctx.args.issue : void 0;
|
|
13125
13461
|
const fallbackJob = {
|
|
13126
13462
|
capability: profile.action ?? profile.name,
|
|
13127
13463
|
implementation: profile.name,
|
|
13128
13464
|
flavor: "instant",
|
|
13129
|
-
...typeof
|
|
13465
|
+
...typeof issue2 === "number" ? { target: issue2, cliArgs: { issue: issue2 } } : { cliArgs: {} }
|
|
13130
13466
|
};
|
|
13131
13467
|
const jobKey = typeof ctx.data.jobKey === "string" ? ctx.data.jobKey : stableJobKey(fallbackJob);
|
|
13132
13468
|
const state = ctx.data.taskState;
|
|
@@ -14112,13 +14448,13 @@ function companyIntentPath(id) {
|
|
|
14112
14448
|
assertIntentId(id);
|
|
14113
14449
|
return `intents/${id}/intent.json`;
|
|
14114
14450
|
}
|
|
14115
|
-
function normalizeCompanyIntent(
|
|
14451
|
+
function normalizeCompanyIntent(path52, raw) {
|
|
14116
14452
|
if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
|
|
14117
|
-
throw new Error(`${
|
|
14453
|
+
throw new Error(`${path52}: intent must be JSON object`);
|
|
14118
14454
|
}
|
|
14119
14455
|
const input = raw;
|
|
14120
14456
|
const id = stringField5(input.id);
|
|
14121
|
-
if (!id || !isCompanyIntentId(id)) throw new Error(`${
|
|
14457
|
+
if (!id || !isCompanyIntentId(id)) throw new Error(`${path52}: invalid intent id`);
|
|
14122
14458
|
const createdAt = stringField5(input.createdAt) || nowIso();
|
|
14123
14459
|
const updatedAt = stringField5(input.updatedAt) || createdAt;
|
|
14124
14460
|
const description = stringField5(input.description);
|
|
@@ -14158,8 +14494,8 @@ function listCompanyIntents(config, cwd) {
|
|
|
14158
14494
|
const records = [];
|
|
14159
14495
|
for (const entry of entries) {
|
|
14160
14496
|
if (entry.type !== "dir" || !entry.name || !isCompanyIntentId(entry.name)) continue;
|
|
14161
|
-
const
|
|
14162
|
-
const file = readStateText(config, cwd,
|
|
14497
|
+
const path52 = companyIntentPath(entry.name);
|
|
14498
|
+
const file = readStateText(config, cwd, path52);
|
|
14163
14499
|
if (!file) continue;
|
|
14164
14500
|
records.push({
|
|
14165
14501
|
id: entry.name,
|
|
@@ -14368,14 +14704,14 @@ var init_loadIssueContext = __esm({
|
|
|
14368
14704
|
if (!ctx.data.commentTargetNumber) ctx.data.commentTargetNumber = issueNumber;
|
|
14369
14705
|
return;
|
|
14370
14706
|
}
|
|
14371
|
-
const
|
|
14707
|
+
const issue2 = getIssue(issueNumber, ctx.cwd);
|
|
14372
14708
|
const cfgCtx = ctx.config.issueContext ?? {};
|
|
14373
14709
|
const limit = cfgCtx.commentLimit ?? DEFAULT_COMMENT_LIMIT;
|
|
14374
14710
|
const maxBytes = cfgCtx.commentMaxBytes ?? DEFAULT_COMMENT_MAX_BYTES;
|
|
14375
|
-
const commentsFormatted = formatIssueComments(
|
|
14376
|
-
const labels =
|
|
14711
|
+
const commentsFormatted = formatIssueComments(issue2.comments, limit, maxBytes);
|
|
14712
|
+
const labels = issue2.labels ?? [];
|
|
14377
14713
|
const labelsFormatted = labels.length === 0 ? "(no labels)" : labels.map((l) => `\`${l}\``).join(", ");
|
|
14378
|
-
ctx.data.issue = { ...
|
|
14714
|
+
ctx.data.issue = { ...issue2, commentsFormatted, labelsFormatted };
|
|
14379
14715
|
ctx.data.commentTargetType = "issue";
|
|
14380
14716
|
ctx.data.commentTargetNumber = issueNumber;
|
|
14381
14717
|
};
|
|
@@ -14404,11 +14740,11 @@ var init_loadIssueStateComment = __esm({
|
|
|
14404
14740
|
if (!owner || !repo) {
|
|
14405
14741
|
throw new Error("loadIssueStateComment: ctx.config.github.owner/repo must be set");
|
|
14406
14742
|
}
|
|
14407
|
-
const
|
|
14743
|
+
const issue2 = getIssue(issueNumber, ctx.cwd);
|
|
14408
14744
|
const loaded = findStateComment(owner, repo, issueNumber, marker, ctx.cwd);
|
|
14409
14745
|
ctx.data.stateMarker = marker;
|
|
14410
|
-
ctx.data.issueIntent =
|
|
14411
|
-
ctx.data.issueTitle =
|
|
14746
|
+
ctx.data.issueIntent = issue2.body;
|
|
14747
|
+
ctx.data.issueTitle = issue2.title;
|
|
14412
14748
|
ctx.data.issueNumber = String(issueNumber);
|
|
14413
14749
|
ctx.data.issueStateComment = loaded;
|
|
14414
14750
|
ctx.data.issueStateJson = loaded ? JSON.stringify(loaded.state, null, 2) : "null";
|
|
@@ -14538,15 +14874,15 @@ var init_loadLinkedFinding = __esm({
|
|
|
14538
14874
|
if (!pr) return;
|
|
14539
14875
|
const findingNumber = resolveFindingNumber(pr);
|
|
14540
14876
|
if (!findingNumber) return;
|
|
14541
|
-
let
|
|
14877
|
+
let issue2;
|
|
14542
14878
|
try {
|
|
14543
|
-
|
|
14879
|
+
issue2 = getIssue(findingNumber, ctx.cwd);
|
|
14544
14880
|
} catch {
|
|
14545
14881
|
return;
|
|
14546
14882
|
}
|
|
14547
|
-
ctx.data.linkedFinding = `Issue #${
|
|
14883
|
+
ctx.data.linkedFinding = `Issue #${issue2.number}: ${issue2.title}
|
|
14548
14884
|
|
|
14549
|
-
${truncate(
|
|
14885
|
+
${truncate(issue2.body, FINDING_BODY_MAX_BYTES)}`;
|
|
14550
14886
|
};
|
|
14551
14887
|
}
|
|
14552
14888
|
});
|
|
@@ -14751,8 +15087,8 @@ async function writeGithubStateTextWithConfig(opts) {
|
|
|
14751
15087
|
);
|
|
14752
15088
|
}
|
|
14753
15089
|
}
|
|
14754
|
-
function jsonlLines(
|
|
14755
|
-
return
|
|
15090
|
+
function jsonlLines(text2) {
|
|
15091
|
+
return text2.split("\n").filter((line) => line.length > 0);
|
|
14756
15092
|
}
|
|
14757
15093
|
function renderJsonl(lines) {
|
|
14758
15094
|
return lines.length > 0 ? `${lines.join("\n")}
|
|
@@ -15052,14 +15388,14 @@ var init_loadTaskContext = __esm({
|
|
|
15052
15388
|
loadTaskContext = async (ctx) => {
|
|
15053
15389
|
const runId = resolveRunId();
|
|
15054
15390
|
const rawIssue = ctx.data.issue;
|
|
15055
|
-
const
|
|
15391
|
+
const issue2 = rawIssue ? {
|
|
15056
15392
|
...rawIssue,
|
|
15057
15393
|
commentsFormatted: rawIssue.commentsFormatted ?? "",
|
|
15058
15394
|
labelsFormatted: rawIssue.labelsFormatted ?? ""
|
|
15059
15395
|
} : void 0;
|
|
15060
15396
|
const taskContext = buildTaskContext({
|
|
15061
15397
|
runId,
|
|
15062
|
-
issue,
|
|
15398
|
+
issue: issue2,
|
|
15063
15399
|
conventions: ctx.data.conventions,
|
|
15064
15400
|
priorArt: typeof ctx.data.priorArt === "string" ? ctx.data.priorArt : "",
|
|
15065
15401
|
memoryContext: typeof ctx.data.memoryContext === "string" ? ctx.data.memoryContext : "",
|
|
@@ -15414,6 +15750,12 @@ var init_notifyTerminal = __esm({
|
|
|
15414
15750
|
});
|
|
15415
15751
|
|
|
15416
15752
|
// src/scripts/openAgencyModelReviewPr.ts
|
|
15753
|
+
function isDryRun(ctx) {
|
|
15754
|
+
const arg = ctx.args.dry_run ?? ctx.args.dryRun;
|
|
15755
|
+
if (arg === true) return true;
|
|
15756
|
+
if (typeof arg === "string" && ["1", "true", "yes"].includes(arg.trim().toLowerCase())) return true;
|
|
15757
|
+
return ["1", "true", "yes"].includes((process.env.KODY_DRY_RUN ?? "").trim().toLowerCase());
|
|
15758
|
+
}
|
|
15417
15759
|
function parseAgencyModelProposal(raw) {
|
|
15418
15760
|
const jsonText = stripJsonFence(raw);
|
|
15419
15761
|
let parsed;
|
|
@@ -15483,9 +15825,9 @@ function normalizeBundleFiles(ctx, bundle) {
|
|
|
15483
15825
|
});
|
|
15484
15826
|
}
|
|
15485
15827
|
function stripJsonFence(raw) {
|
|
15486
|
-
const
|
|
15487
|
-
const fence =
|
|
15488
|
-
return (fence ? fence[1] :
|
|
15828
|
+
const text2 = raw.trim();
|
|
15829
|
+
const fence = text2.match(/^```(?:json)?\s*\n([\s\S]*?)\n```$/i);
|
|
15830
|
+
return (fence ? fence[1] : text2).trim();
|
|
15489
15831
|
}
|
|
15490
15832
|
function readIssueNumber(ctx) {
|
|
15491
15833
|
const issueNumber = ctx.args.issue;
|
|
@@ -15495,9 +15837,9 @@ function readIssueNumber(ctx) {
|
|
|
15495
15837
|
return issueNumber;
|
|
15496
15838
|
}
|
|
15497
15839
|
function readRequiredJsonString(value, field) {
|
|
15498
|
-
const
|
|
15499
|
-
if (!
|
|
15500
|
-
return
|
|
15840
|
+
const text2 = readJsonString(value, field).trim();
|
|
15841
|
+
if (!text2) throw new Error(`openAgencyModelReviewPr: ${field} must be a non-empty string`);
|
|
15842
|
+
return text2;
|
|
15501
15843
|
}
|
|
15502
15844
|
function readJsonString(value, field) {
|
|
15503
15845
|
if (typeof value !== "string") throw new Error(`openAgencyModelReviewPr: ${field} must be a string`);
|
|
@@ -15577,6 +15919,16 @@ var init_openAgencyModelReviewPr = __esm({
|
|
|
15577
15919
|
const stateRepo = parseStateRepo(ctx.config);
|
|
15578
15920
|
const baseBranch = "main";
|
|
15579
15921
|
const branch = buildStatePrBranchName(sourceLabel, issueNumber, bundle.title);
|
|
15922
|
+
if (isDryRun(ctx)) {
|
|
15923
|
+
ctx.data.agencyModelReviewPr = {
|
|
15924
|
+
dryRun: true,
|
|
15925
|
+
repo: `${stateRepo.owner}/${stateRepo.repo}`,
|
|
15926
|
+
branch,
|
|
15927
|
+
base: baseBranch,
|
|
15928
|
+
files: normalizedFiles.map((file) => file.targetPath)
|
|
15929
|
+
};
|
|
15930
|
+
return;
|
|
15931
|
+
}
|
|
15580
15932
|
const baseRef = ghJson(
|
|
15581
15933
|
["api", `/repos/${stateRepo.owner}/${stateRepo.repo}/git/ref/heads/${baseBranch}`],
|
|
15582
15934
|
ctx.cwd
|
|
@@ -15689,10 +16041,10 @@ function ensureLabel2(cwd) {
|
|
|
15689
16041
|
return false;
|
|
15690
16042
|
}
|
|
15691
16043
|
}
|
|
15692
|
-
function markIssueWithReportLabel(
|
|
16044
|
+
function markIssueWithReportLabel(issue2, cwd) {
|
|
15693
16045
|
if (!ensureLabel2(cwd)) return;
|
|
15694
16046
|
try {
|
|
15695
|
-
gh(["issue", "edit", String(
|
|
16047
|
+
gh(["issue", "edit", String(issue2), "--add-label", QA_LABEL], { cwd });
|
|
15696
16048
|
} catch {
|
|
15697
16049
|
}
|
|
15698
16050
|
}
|
|
@@ -15831,13 +16183,13 @@ function isPartialEnvelope(x) {
|
|
|
15831
16183
|
function escapeRegex(s) {
|
|
15832
16184
|
return s.replace(/[-/\\^$*+?.()|[\]{}]/g, "\\$&");
|
|
15833
16185
|
}
|
|
15834
|
-
function extractFencedBlock(
|
|
16186
|
+
function extractFencedBlock(text2, label) {
|
|
15835
16187
|
const re = new RegExp(`\`\`\`${escapeRegex(label)}\\s*\\n([\\s\\S]*?)\\n\`\`\``, "m");
|
|
15836
|
-
const m = re.exec(
|
|
16188
|
+
const m = re.exec(text2);
|
|
15837
16189
|
return m ? m[1].trim() : null;
|
|
15838
16190
|
}
|
|
15839
|
-
function extractNextStateFromText(
|
|
15840
|
-
const inner = extractFencedBlock(
|
|
16191
|
+
function extractNextStateFromText(text2, fenceLabel, prevRev) {
|
|
16192
|
+
const inner = extractFencedBlock(text2, fenceLabel);
|
|
15841
16193
|
if (inner === null) {
|
|
15842
16194
|
return { error: `missing \`${fenceLabel}\` fenced block` };
|
|
15843
16195
|
}
|
|
@@ -15956,15 +16308,15 @@ var init_parseJobStateFromAgentResult = __esm({
|
|
|
15956
16308
|
});
|
|
15957
16309
|
|
|
15958
16310
|
// src/scripts/parseReproOutput.ts
|
|
15959
|
-
function extractTestPath(
|
|
15960
|
-
const m =
|
|
16311
|
+
function extractTestPath(text2) {
|
|
16312
|
+
const m = text2.match(/^[\s>*_#`~-]*TEST_PATH[\s>*_#`~-]*\s*:\s*(.+?)\s*$/im);
|
|
15961
16313
|
if (!m) return "";
|
|
15962
16314
|
return stripMarkdownEmphasis2(m[1] ?? "");
|
|
15963
16315
|
}
|
|
15964
|
-
function extractFailureSignatureBlock(
|
|
15965
|
-
const startIdx =
|
|
16316
|
+
function extractFailureSignatureBlock(text2) {
|
|
16317
|
+
const startIdx = text2.search(/(?:^|\n)[ \t]*FAILURE_SIGNATURE\s*:[ \t]*/i);
|
|
15966
16318
|
if (startIdx === -1) return "";
|
|
15967
|
-
const afterMarker =
|
|
16319
|
+
const afterMarker = text2.slice(startIdx).replace(/^[\s\S]*?FAILURE_SIGNATURE\s*:[ \t]*\n?/i, "");
|
|
15968
16320
|
const stopRe = /(?:^|\n)[ \t]*(?:COMMIT_MSG|PR_SUMMARY|TEST_PATH)\s*:/i;
|
|
15969
16321
|
const stopIdx = afterMarker.search(stopRe);
|
|
15970
16322
|
let block = stopIdx === -1 ? afterMarker : afterMarker.slice(0, stopIdx);
|
|
@@ -15981,14 +16333,14 @@ function normalizeFailureSignatureBlock(block) {
|
|
|
15981
16333
|
const jsonObject = extractFirstJsonObject(s);
|
|
15982
16334
|
return jsonObject || s;
|
|
15983
16335
|
}
|
|
15984
|
-
function extractFirstJsonObject(
|
|
15985
|
-
const start =
|
|
16336
|
+
function extractFirstJsonObject(text2) {
|
|
16337
|
+
const start = text2.indexOf("{");
|
|
15986
16338
|
if (start === -1) return "";
|
|
15987
16339
|
let depth = 0;
|
|
15988
16340
|
let inString = false;
|
|
15989
16341
|
let escaped = false;
|
|
15990
|
-
for (let i = start; i <
|
|
15991
|
-
const ch =
|
|
16342
|
+
for (let i = start; i < text2.length; i++) {
|
|
16343
|
+
const ch = text2[i];
|
|
15992
16344
|
if (inString) {
|
|
15993
16345
|
if (escaped) {
|
|
15994
16346
|
escaped = false;
|
|
@@ -16005,7 +16357,7 @@ function extractFirstJsonObject(text) {
|
|
|
16005
16357
|
depth++;
|
|
16006
16358
|
} else if (ch === "}") {
|
|
16007
16359
|
depth--;
|
|
16008
|
-
if (depth === 0) return
|
|
16360
|
+
if (depth === 0) return text2.slice(start, i + 1).trim();
|
|
16009
16361
|
}
|
|
16010
16362
|
}
|
|
16011
16363
|
return "";
|
|
@@ -16031,9 +16383,9 @@ var init_parseReproOutput = __esm({
|
|
|
16031
16383
|
"use strict";
|
|
16032
16384
|
parseReproOutput = async (ctx, _profile, agentResult) => {
|
|
16033
16385
|
if (!agentResult || ctx.data.agentDone === false) return;
|
|
16034
|
-
const
|
|
16035
|
-
const testPath = extractTestPath(
|
|
16036
|
-
const signatureRaw = extractFailureSignatureBlock(
|
|
16386
|
+
const text2 = agentResult.finalText ?? "";
|
|
16387
|
+
const testPath = extractTestPath(text2);
|
|
16388
|
+
const signatureRaw = extractFailureSignatureBlock(text2);
|
|
16037
16389
|
if (!testPath) {
|
|
16038
16390
|
downgrade(ctx, "reproduce missing TEST_PATH line in final message");
|
|
16039
16391
|
return;
|
|
@@ -16212,8 +16564,8 @@ var init_planTaskJobs = __esm({
|
|
|
16212
16564
|
ctx.output.reason = "planTaskJobs requires --issue";
|
|
16213
16565
|
return;
|
|
16214
16566
|
}
|
|
16215
|
-
const
|
|
16216
|
-
const specs = parseTaskJobSpecs(
|
|
16567
|
+
const issue2 = ctx.data.issue;
|
|
16568
|
+
const specs = parseTaskJobSpecs(issue2?.body ?? "");
|
|
16217
16569
|
if (specs.length === 0) {
|
|
16218
16570
|
ctx.skipAgent = true;
|
|
16219
16571
|
ctx.output.exitCode = 64;
|
|
@@ -16543,8 +16895,8 @@ var init_promoteQaGoal = __esm({
|
|
|
16543
16895
|
}
|
|
16544
16896
|
let report;
|
|
16545
16897
|
try {
|
|
16546
|
-
const
|
|
16547
|
-
const reportComment = [...
|
|
16898
|
+
const issue2 = getIssue(issueNum, ctx.cwd);
|
|
16899
|
+
const reportComment = [...issue2.comments].reverse().find((c) => c.body.includes(REPORT_JSON_OPEN2));
|
|
16548
16900
|
if (!reportComment) {
|
|
16549
16901
|
ctx.output.exitCode = 3;
|
|
16550
16902
|
ctx.output.reason = `no QA report (${REPORT_JSON_OPEN2} \u2026) found on issue #${issueNum}`;
|
|
@@ -16611,9 +16963,9 @@ function latestResult(raw, agentResult) {
|
|
|
16611
16963
|
function recordField6(value) {
|
|
16612
16964
|
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
16613
16965
|
}
|
|
16614
|
-
function resolveDotted(root,
|
|
16615
|
-
if (!
|
|
16616
|
-
return
|
|
16966
|
+
function resolveDotted(root, path52) {
|
|
16967
|
+
if (!path52) return void 0;
|
|
16968
|
+
return path52.split(".").reduce((value, key) => recordField6(value)?.[key], root);
|
|
16617
16969
|
}
|
|
16618
16970
|
function stringValue4(value) {
|
|
16619
16971
|
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
@@ -16655,13 +17007,7 @@ var init_publishReport = __esm({
|
|
|
16655
17007
|
...publication.reviewArea ? { reviewArea: publication.reviewArea } : {}
|
|
16656
17008
|
});
|
|
16657
17009
|
const runId = generatedAt.replace(/\.\d{3}Z$/, "Z").replace(/:/g, "-");
|
|
16658
|
-
writeStateText(
|
|
16659
|
-
ctx.config,
|
|
16660
|
-
ctx.cwd,
|
|
16661
|
-
`reports/${slug}/runs/${runId}.md`,
|
|
16662
|
-
markdown,
|
|
16663
|
-
`chore(reports): add ${slug} run`
|
|
16664
|
-
);
|
|
17010
|
+
writeStateText(ctx.config, ctx.cwd, `reports/${slug}/runs/${runId}.md`, markdown, `chore(reports): add ${slug} run`);
|
|
16665
17011
|
};
|
|
16666
17012
|
}
|
|
16667
17013
|
});
|
|
@@ -17369,15 +17715,15 @@ var init_runFlow = __esm({
|
|
|
17369
17715
|
init_issue();
|
|
17370
17716
|
runFlow = async (ctx) => {
|
|
17371
17717
|
const issueNumber = ctx.args.issue;
|
|
17372
|
-
const
|
|
17718
|
+
const issue2 = getIssue(issueNumber, ctx.cwd);
|
|
17373
17719
|
const cfgCtx = ctx.config.issueContext ?? {};
|
|
17374
17720
|
const commentsFormatted = formatIssueComments(
|
|
17375
|
-
|
|
17721
|
+
issue2.comments,
|
|
17376
17722
|
cfgCtx.commentLimit ?? DEFAULT_COMMENT_LIMIT,
|
|
17377
17723
|
cfgCtx.commentMaxBytes ?? DEFAULT_COMMENT_MAX_BYTES
|
|
17378
17724
|
);
|
|
17379
|
-
ctx.data.issue = { ...
|
|
17380
|
-
if (
|
|
17725
|
+
ctx.data.issue = { ...issue2, commentsFormatted };
|
|
17726
|
+
if (issue2.isPullRequest) {
|
|
17381
17727
|
ctx.data.commentTargetType = "pr";
|
|
17382
17728
|
ctx.data.commentTargetNumber = issueNumber;
|
|
17383
17729
|
ctx.skipAgent = true;
|
|
@@ -17401,7 +17747,7 @@ var init_runFlow = __esm({
|
|
|
17401
17747
|
}
|
|
17402
17748
|
const branchInfo = ensureFeatureBranch(
|
|
17403
17749
|
issueNumber,
|
|
17404
|
-
|
|
17750
|
+
issue2.title,
|
|
17405
17751
|
ctx.config.git.defaultBranch,
|
|
17406
17752
|
ctx.cwd,
|
|
17407
17753
|
base ?? void 0
|
|
@@ -17650,8 +17996,8 @@ async function flyCreateApp(name, orgSlug, token) {
|
|
|
17650
17996
|
});
|
|
17651
17997
|
if (res.status === 422) return;
|
|
17652
17998
|
if (!res.ok) {
|
|
17653
|
-
const
|
|
17654
|
-
throw new Error(`createApp ${name}: ${res.status} ${
|
|
17999
|
+
const text2 = await res.text().catch(() => "");
|
|
18000
|
+
throw new Error(`createApp ${name}: ${res.status} ${text2.slice(0, 200)}`);
|
|
17655
18001
|
}
|
|
17656
18002
|
}
|
|
17657
18003
|
async function flyAllocateSharedIps(appName, token) {
|
|
@@ -17758,9 +18104,9 @@ async function flyCreatePreviewMachine(args, token) {
|
|
|
17758
18104
|
const { id } = await res.json();
|
|
17759
18105
|
return id;
|
|
17760
18106
|
}
|
|
17761
|
-
const
|
|
17762
|
-
lastErr = new Error(`createPreviewMachine ${res.status}: ${
|
|
17763
|
-
if (!/MANIFEST_UNKNOWN|manifest unknown/i.test(
|
|
18107
|
+
const text2 = await res.text().catch(() => "");
|
|
18108
|
+
lastErr = new Error(`createPreviewMachine ${res.status}: ${text2.slice(0, 300)}`);
|
|
18109
|
+
if (!/MANIFEST_UNKNOWN|manifest unknown/i.test(text2)) break;
|
|
17764
18110
|
await new Promise((r) => setTimeout(r, 2e3 * (attempt + 1)));
|
|
17765
18111
|
}
|
|
17766
18112
|
throw lastErr ?? new Error("createPreviewMachine failed (unknown)");
|
|
@@ -18199,8 +18545,8 @@ var init_setCommentTarget = __esm({
|
|
|
18199
18545
|
|
|
18200
18546
|
// src/scripts/setLifecycleLabel.ts
|
|
18201
18547
|
function resolveTargetNumber(args) {
|
|
18202
|
-
const
|
|
18203
|
-
if (typeof
|
|
18548
|
+
const issue2 = args.issue;
|
|
18549
|
+
if (typeof issue2 === "number" && Number.isFinite(issue2)) return issue2;
|
|
18204
18550
|
const pr = args.pr;
|
|
18205
18551
|
if (typeof pr === "number" && Number.isFinite(pr)) return pr;
|
|
18206
18552
|
return void 0;
|
|
@@ -18415,9 +18761,10 @@ var init_syncFlow = __esm({
|
|
|
18415
18761
|
});
|
|
18416
18762
|
|
|
18417
18763
|
// src/scripts/validateAgencyModelProposal.ts
|
|
18418
|
-
|
|
18764
|
+
import * as path43 from "path";
|
|
18765
|
+
function validateModelBundle(bundle, expectedKind, options = {}) {
|
|
18419
18766
|
const failures = [];
|
|
18420
|
-
validateOneModel(bundle.model, bundle.files, "model", true, failures, expectedKind);
|
|
18767
|
+
validateOneModel(bundle.model, bundle.files, "model", true, failures, expectedKind, options);
|
|
18421
18768
|
return failures;
|
|
18422
18769
|
}
|
|
18423
18770
|
function readExpectedModelKind(args) {
|
|
@@ -18427,7 +18774,7 @@ function readExpectedModelKind(args) {
|
|
|
18427
18774
|
"validateAgencyModelProposal: with.modelKind must be intent, operation, agent, capability, goal, agentLoop, or workflow"
|
|
18428
18775
|
);
|
|
18429
18776
|
}
|
|
18430
|
-
function validateOneModel(rawModel, files, label, strictSingleModel, failures, expectedKind) {
|
|
18777
|
+
function validateOneModel(rawModel, files, label, strictSingleModel, failures, expectedKind, options = {}) {
|
|
18431
18778
|
if (!rawModel || typeof rawModel !== "object" || Array.isArray(rawModel)) {
|
|
18432
18779
|
failures.push(`${label} must be an object`);
|
|
18433
18780
|
return;
|
|
@@ -18445,11 +18792,11 @@ function validateOneModel(rawModel, files, label, strictSingleModel, failures, e
|
|
|
18445
18792
|
for (const doc of REQUIRED_DOCS[kind]) {
|
|
18446
18793
|
if (!docsUsed.includes(doc)) failures.push(`${label} docsUsed missing ${doc}`);
|
|
18447
18794
|
}
|
|
18448
|
-
validateFilesForKind(kind, slug, files, strictSingleModel, failures);
|
|
18795
|
+
validateFilesForKind(kind, slug, files, strictSingleModel, failures, options);
|
|
18449
18796
|
validateModelShape(kind, model, files, slug, failures);
|
|
18450
18797
|
}
|
|
18451
18798
|
}
|
|
18452
|
-
function validateFilesForKind(kind, slug, files, strictSingleModel, failures) {
|
|
18799
|
+
function validateFilesForKind(kind, slug, files, strictSingleModel, failures, options) {
|
|
18453
18800
|
const paths = files.map((file) => normalizeBundlePath(file.path));
|
|
18454
18801
|
if (paths.some((filePath) => filePath === "implementations" || filePath.startsWith("implementations/"))) {
|
|
18455
18802
|
failures.push("files must not use obsolete implementation storage");
|
|
@@ -18496,6 +18843,7 @@ function validateFilesForKind(kind, slug, files, strictSingleModel, failures) {
|
|
|
18496
18843
|
}
|
|
18497
18844
|
if (kind === "workflow") {
|
|
18498
18845
|
requirePath(paths, `capabilities/${slug}/profile.json`, "workflow capability profile", failures);
|
|
18846
|
+
requirePath(paths, `capabilities/${slug}/capability.md`, "workflow capability body", failures);
|
|
18499
18847
|
const profile = parseJsonFile(files, `capabilities/${slug}/profile.json`, failures);
|
|
18500
18848
|
if (profile) {
|
|
18501
18849
|
if (profile.capabilityKind !== void 0) {
|
|
@@ -18505,6 +18853,30 @@ function validateFilesForKind(kind, slug, files, strictSingleModel, failures) {
|
|
|
18505
18853
|
const hasTopLevelSteps = Array.isArray(profile.steps) && profile.steps.length > 0;
|
|
18506
18854
|
if (!hasWorkflowObject && !hasTopLevelSteps) {
|
|
18507
18855
|
failures.push("workflow profile must include workflow object or top-level steps");
|
|
18856
|
+
} else {
|
|
18857
|
+
const workflow = hasWorkflowObject ? profile.workflow : { steps: profile.steps, ...profile.startAt !== void 0 ? { startAt: profile.startAt } : {} };
|
|
18858
|
+
const known = options.capabilityRoot ? getCapabilityRoots(options.capabilityRoot).flatMap((root) => listCapabilityFolderSlugs(root)) : [];
|
|
18859
|
+
const uniqueKnown = [...new Set(known)];
|
|
18860
|
+
const capabilityInputs = /* @__PURE__ */ new Map();
|
|
18861
|
+
if (options.capabilityRoot) {
|
|
18862
|
+
for (const capability of uniqueKnown) {
|
|
18863
|
+
const inputs = getCapabilityActionInputs(capability, options.capabilityRoot);
|
|
18864
|
+
if (inputs) {
|
|
18865
|
+
capabilityInputs.set(
|
|
18866
|
+
capability,
|
|
18867
|
+
new Set(inputs.flatMap((input) => [input.name, input.flag.replace(/^--/, "")]))
|
|
18868
|
+
);
|
|
18869
|
+
}
|
|
18870
|
+
}
|
|
18871
|
+
}
|
|
18872
|
+
failures.push(
|
|
18873
|
+
...formatWorkflowValidationIssues(
|
|
18874
|
+
validateWorkflow(workflow, {
|
|
18875
|
+
...uniqueKnown.length > 0 ? { knownCapabilities: new Set(uniqueKnown) } : {},
|
|
18876
|
+
...capabilityInputs.size > 0 ? { capabilityInputs } : {}
|
|
18877
|
+
})
|
|
18878
|
+
)
|
|
18879
|
+
);
|
|
18508
18880
|
}
|
|
18509
18881
|
}
|
|
18510
18882
|
}
|
|
@@ -18692,6 +19064,9 @@ var REQUIRED_DOCS, validateAgencyModelProposal;
|
|
|
18692
19064
|
var init_validateAgencyModelProposal = __esm({
|
|
18693
19065
|
"src/scripts/validateAgencyModelProposal.ts"() {
|
|
18694
19066
|
"use strict";
|
|
19067
|
+
init_capabilityFolders();
|
|
19068
|
+
init_registry();
|
|
19069
|
+
init_workflowValidation();
|
|
18695
19070
|
init_openAgencyModelReviewPr();
|
|
18696
19071
|
REQUIRED_DOCS = {
|
|
18697
19072
|
intent: ["docs/intents.md", "docs/engine-company.md"],
|
|
@@ -18707,7 +19082,9 @@ var init_validateAgencyModelProposal = __esm({
|
|
|
18707
19082
|
const raw = String(ctx.data.prSummary ?? "");
|
|
18708
19083
|
const bundle = parseAgencyModelProposal(raw);
|
|
18709
19084
|
const expectedKind = readExpectedModelKind(args);
|
|
18710
|
-
const failures = validateModelBundle(bundle, expectedKind
|
|
19085
|
+
const failures = validateModelBundle(bundle, expectedKind, {
|
|
19086
|
+
capabilityRoot: path43.join(ctx.cwd, ".kody", "capabilities")
|
|
19087
|
+
});
|
|
18711
19088
|
if (failures.length > 0) {
|
|
18712
19089
|
throw new Error(`validateAgencyModelProposal: ${failures.join("; ")}`);
|
|
18713
19090
|
}
|
|
@@ -19266,9 +19643,9 @@ var init_writeAgentRunSummary = __esm({
|
|
|
19266
19643
|
const summaryPath = process.env.GITHUB_STEP_SUMMARY;
|
|
19267
19644
|
if (!summaryPath) return;
|
|
19268
19645
|
const implementation = profile.name;
|
|
19269
|
-
const
|
|
19646
|
+
const issue2 = ctx.args.issue;
|
|
19270
19647
|
const pr = ctx.args.pr;
|
|
19271
|
-
const target =
|
|
19648
|
+
const target = issue2 ? `issue #${issue2}` : pr ? `PR #${pr}` : "(unknown)";
|
|
19272
19649
|
const prUrl = ctx.output.prUrl;
|
|
19273
19650
|
const exitCode = ctx.output.exitCode ?? 0;
|
|
19274
19651
|
const reason = ctx.output.reason;
|
|
@@ -19608,38 +19985,38 @@ import { execFileSync as execFileSync24 } from "child_process";
|
|
|
19608
19985
|
import * as crypto3 from "crypto";
|
|
19609
19986
|
import * as fs45 from "fs";
|
|
19610
19987
|
import * as os7 from "os";
|
|
19611
|
-
import * as
|
|
19988
|
+
import * as path44 from "path";
|
|
19612
19989
|
function writeLocalFile(cwd, relativePath, content) {
|
|
19613
|
-
const fullPath =
|
|
19614
|
-
fs45.mkdirSync(
|
|
19990
|
+
const fullPath = path44.join(cwd, relativePath);
|
|
19991
|
+
fs45.mkdirSync(path44.dirname(fullPath), { recursive: true });
|
|
19615
19992
|
fs45.writeFileSync(fullPath, content);
|
|
19616
19993
|
}
|
|
19617
19994
|
function copyPath(source, target) {
|
|
19618
19995
|
const st = fs45.lstatSync(source);
|
|
19619
19996
|
fs45.rmSync(target, { recursive: true, force: true });
|
|
19620
19997
|
if (st.isSymbolicLink()) return;
|
|
19621
|
-
fs45.mkdirSync(
|
|
19998
|
+
fs45.mkdirSync(path44.dirname(target), { recursive: true });
|
|
19622
19999
|
fs45.cpSync(source, target, { recursive: true, force: true });
|
|
19623
20000
|
}
|
|
19624
20001
|
function overlayDirectoryChildren(cwd, sourceDir, localDir) {
|
|
19625
20002
|
if (!fs45.existsSync(sourceDir)) return;
|
|
19626
20003
|
for (const entry of fs45.readdirSync(sourceDir, { withFileTypes: true })) {
|
|
19627
|
-
const source =
|
|
19628
|
-
const target =
|
|
20004
|
+
const source = path44.join(sourceDir, entry.name);
|
|
20005
|
+
const target = path44.join(cwd, localDir, entry.name);
|
|
19629
20006
|
copyPath(source, target);
|
|
19630
20007
|
}
|
|
19631
20008
|
}
|
|
19632
20009
|
function hydrateStateWorkspace(config, cwd) {
|
|
19633
20010
|
if (process.env.VITEST && process.env[TEST_FETCH_ENV] !== "1") return;
|
|
19634
20011
|
const parsed = parseStateRepo(config);
|
|
19635
|
-
const hydrateKey = `${
|
|
20012
|
+
const hydrateKey = `${path44.resolve(cwd)}|${parsed.owner}/${parsed.repo}|${parsed.basePath}|${parsed.branch}`;
|
|
19636
20013
|
if (hydratedWorkspaces.has(hydrateKey)) return;
|
|
19637
20014
|
const snapshotRoot = fetchStateSnapshot(parsed);
|
|
19638
20015
|
for (const mapping of DIR_MAPPINGS) {
|
|
19639
|
-
overlayDirectoryChildren(cwd,
|
|
20016
|
+
overlayDirectoryChildren(cwd, path44.join(snapshotRoot, mapping.stateDir), mapping.localDir);
|
|
19640
20017
|
}
|
|
19641
20018
|
for (const mapping of FILE_MAPPINGS) {
|
|
19642
|
-
const source =
|
|
20019
|
+
const source = path44.join(snapshotRoot, mapping.statePath);
|
|
19643
20020
|
if (fs45.existsSync(source) && !fs45.lstatSync(source).isSymbolicLink() && fs45.statSync(source).isFile()) {
|
|
19644
20021
|
writeLocalFile(cwd, mapping.localPath, fs45.readFileSync(source, "utf-8"));
|
|
19645
20022
|
}
|
|
@@ -19647,11 +20024,11 @@ function hydrateStateWorkspace(config, cwd) {
|
|
|
19647
20024
|
hydratedWorkspaces.add(hydrateKey);
|
|
19648
20025
|
}
|
|
19649
20026
|
function fetchStateSnapshot(parsed) {
|
|
19650
|
-
const cacheDir =
|
|
20027
|
+
const cacheDir = path44.join(cacheRoot2(), cacheKey3(parsed));
|
|
19651
20028
|
const url = `https://github.com/${parsed.owner}/${parsed.repo}.git`;
|
|
19652
20029
|
try {
|
|
19653
|
-
fs45.mkdirSync(
|
|
19654
|
-
if (!fs45.existsSync(
|
|
20030
|
+
fs45.mkdirSync(path44.dirname(cacheDir), { recursive: true });
|
|
20031
|
+
if (!fs45.existsSync(path44.join(cacheDir, ".git"))) {
|
|
19655
20032
|
fs45.rmSync(cacheDir, { recursive: true, force: true });
|
|
19656
20033
|
runGit3(["clone", "--no-checkout", "--filter=blob:none", url, cacheDir]);
|
|
19657
20034
|
}
|
|
@@ -19667,10 +20044,10 @@ function fetchStateSnapshot(parsed) {
|
|
|
19667
20044
|
`stateWorkspace: failed to fetch ${parsed.owner}/${parsed.repo}:${parsed.basePath}@${parsed.branch}: ${msg}`
|
|
19668
20045
|
);
|
|
19669
20046
|
}
|
|
19670
|
-
return
|
|
20047
|
+
return path44.join(cacheDir, parsed.basePath);
|
|
19671
20048
|
}
|
|
19672
20049
|
function cacheRoot2() {
|
|
19673
|
-
return process.env[CACHE_ENV2]?.trim() ||
|
|
20050
|
+
return process.env[CACHE_ENV2]?.trim() || path44.join(os7.homedir(), ".cache", "kody", "state-repo");
|
|
19674
20051
|
}
|
|
19675
20052
|
function cacheKey3(parsed) {
|
|
19676
20053
|
return crypto3.createHash("sha256").update(`${parsed.owner}/${parsed.repo}#${parsed.branch}#${parsed.basePath}`).digest("hex").slice(0, 24);
|
|
@@ -19710,16 +20087,16 @@ var init_stateWorkspace = __esm({
|
|
|
19710
20087
|
"use strict";
|
|
19711
20088
|
init_stateRepo();
|
|
19712
20089
|
DIR_MAPPINGS = [
|
|
19713
|
-
{ stateDir: "capabilities", localDir:
|
|
19714
|
-
{ stateDir: "agents", localDir:
|
|
19715
|
-
{ stateDir: "context", localDir:
|
|
19716
|
-
{ stateDir: "memory", localDir:
|
|
20090
|
+
{ stateDir: "capabilities", localDir: path44.join(".kody", "capabilities") },
|
|
20091
|
+
{ stateDir: "agents", localDir: path44.join(".kody", "agents") },
|
|
20092
|
+
{ stateDir: "context", localDir: path44.join(".kody", "context") },
|
|
20093
|
+
{ stateDir: "memory", localDir: path44.join(".kody", "memory") }
|
|
19717
20094
|
];
|
|
19718
20095
|
FILE_MAPPINGS = [
|
|
19719
|
-
{ statePath: "instructions.md", localPath:
|
|
19720
|
-
{ statePath: "system-prompt.md", localPath:
|
|
19721
|
-
{ statePath: "variables.json", localPath:
|
|
19722
|
-
{ statePath: "secrets.enc", localPath:
|
|
20096
|
+
{ statePath: "instructions.md", localPath: path44.join(".kody", "instructions.md") },
|
|
20097
|
+
{ statePath: "system-prompt.md", localPath: path44.join(".kody", "system-prompt.md") },
|
|
20098
|
+
{ statePath: "variables.json", localPath: path44.join(".kody", "variables.json") },
|
|
20099
|
+
{ statePath: "secrets.enc", localPath: path44.join(".kody", "secrets.enc") }
|
|
19723
20100
|
];
|
|
19724
20101
|
CACHE_ENV2 = "KODY_STATE_REPO_CACHE";
|
|
19725
20102
|
TEST_FETCH_ENV = "KODY_STATE_WORKSPACE_FETCH_FOR_TESTS";
|
|
@@ -19795,7 +20172,7 @@ var init_tools = __esm({
|
|
|
19795
20172
|
import { spawn as spawn7 } from "child_process";
|
|
19796
20173
|
import * as fs46 from "fs";
|
|
19797
20174
|
import * as os8 from "os";
|
|
19798
|
-
import * as
|
|
20175
|
+
import * as path45 from "path";
|
|
19799
20176
|
function isMutatingPostflight(scriptName) {
|
|
19800
20177
|
return MUTATING_POSTFLIGHTS.has(scriptName ?? "");
|
|
19801
20178
|
}
|
|
@@ -19823,9 +20200,9 @@ function collectShellSideChannels(ctx, stdout) {
|
|
|
19823
20200
|
}
|
|
19824
20201
|
}
|
|
19825
20202
|
function operatorRequestBlock(why) {
|
|
19826
|
-
const
|
|
19827
|
-
if (!
|
|
19828
|
-
const safe =
|
|
20203
|
+
const text2 = why.trim();
|
|
20204
|
+
if (!text2) return null;
|
|
20205
|
+
const safe = text2.replace(/-{3,}\s*END UNTRUSTED INPUT\s*-{3,}/gi, "[END UNTRUSTED INPUT]");
|
|
19829
20206
|
return [
|
|
19830
20207
|
"## The request that triggered this run",
|
|
19831
20208
|
"",
|
|
@@ -20018,7 +20395,7 @@ async function runImplementation(profileName, input) {
|
|
|
20018
20395
|
const jobWhyBlock = typeof ctx.data.jobWhy === "string" ? operatorRequestBlock(ctx.data.jobWhy) : null;
|
|
20019
20396
|
const jobRefBlock = jobReferenceBlock(profileName, profile, ctx.data);
|
|
20020
20397
|
const invokeAgent = async (prompt) => {
|
|
20021
|
-
const externalPlugins = (profile.claudeCode.plugins ?? []).map((p) =>
|
|
20398
|
+
const externalPlugins = (profile.claudeCode.plugins ?? []).map((p) => path45.isAbsolute(p) ? p : path45.resolve(profile.dir, p)).filter((p) => p.length > 0);
|
|
20022
20399
|
const syntheticPath = ctx.data.syntheticPluginPath;
|
|
20023
20400
|
const pluginPaths = [...externalPlugins, ...syntheticPath ? [syntheticPath] : []];
|
|
20024
20401
|
const agents = loadSubagents(profile);
|
|
@@ -20267,7 +20644,8 @@ async function runImplementation(profileName, input) {
|
|
|
20267
20644
|
nextDispatch: ctx.output.nextDispatch,
|
|
20268
20645
|
nextJob: ctx.output.nextJob,
|
|
20269
20646
|
afterNextJob: ctx.output.afterNextJob,
|
|
20270
|
-
taskState: ctx.data.taskState
|
|
20647
|
+
taskState: ctx.data.taskState,
|
|
20648
|
+
capabilityResults: Array.isArray(ctx.data.capabilityResults) ? ctx.data.capabilityResults : void 0
|
|
20271
20649
|
});
|
|
20272
20650
|
} catch (err) {
|
|
20273
20651
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -20455,13 +20833,13 @@ function clearStampedLifecycleLabels(profile, ctx) {
|
|
|
20455
20833
|
function resolveProfilePath(profileName) {
|
|
20456
20834
|
const found = resolveImplementation(profileName);
|
|
20457
20835
|
if (found) return found;
|
|
20458
|
-
const here =
|
|
20836
|
+
const here = path45.dirname(new URL(import.meta.url).pathname);
|
|
20459
20837
|
const candidates = [
|
|
20460
|
-
|
|
20838
|
+
path45.join(here, "implementations", profileName, "profile.json"),
|
|
20461
20839
|
// same-dir sibling (dev)
|
|
20462
|
-
|
|
20840
|
+
path45.join(here, "..", "implementations", profileName, "profile.json"),
|
|
20463
20841
|
// up one (prod: dist/bin → dist/implementations)
|
|
20464
|
-
|
|
20842
|
+
path45.join(here, "..", "src", "implementations", profileName, "profile.json")
|
|
20465
20843
|
// fallback
|
|
20466
20844
|
];
|
|
20467
20845
|
for (const c of candidates) {
|
|
@@ -20580,7 +20958,7 @@ function resolveShellTimeoutMs(entry) {
|
|
|
20580
20958
|
}
|
|
20581
20959
|
async function runShellEntry(entry, ctx, profile) {
|
|
20582
20960
|
const shellName = entry.shell;
|
|
20583
|
-
const shellPath =
|
|
20961
|
+
const shellPath = path45.join(profile.dir, shellName);
|
|
20584
20962
|
if (!fs46.existsSync(shellPath)) {
|
|
20585
20963
|
ctx.skipAgent = true;
|
|
20586
20964
|
ctx.output.exitCode = 99;
|
|
@@ -20588,7 +20966,7 @@ async function runShellEntry(entry, ctx, profile) {
|
|
|
20588
20966
|
return;
|
|
20589
20967
|
}
|
|
20590
20968
|
const positional = entry.with ? Object.values(entry.with).map((v) => String(v)) : [];
|
|
20591
|
-
const outputFile =
|
|
20969
|
+
const outputFile = path45.join(
|
|
20592
20970
|
os8.tmpdir(),
|
|
20593
20971
|
`kody-shell-output-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`
|
|
20594
20972
|
);
|
|
@@ -20756,6 +21134,68 @@ var init_executor = __esm({
|
|
|
20756
21134
|
}
|
|
20757
21135
|
});
|
|
20758
21136
|
|
|
21137
|
+
// src/workflowRunState.ts
|
|
21138
|
+
function workflowRunStatePath(workflowId, runId) {
|
|
21139
|
+
if (!SAFE_ID.test(workflowId)) throw new Error(`invalid workflow id ${workflowId}`);
|
|
21140
|
+
if (!SAFE_ID.test(runId)) throw new Error(`invalid workflow run id ${runId}`);
|
|
21141
|
+
return `workflows/${workflowId}/runs/${runId}.json`;
|
|
21142
|
+
}
|
|
21143
|
+
function parseWorkflowRunState(raw) {
|
|
21144
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return null;
|
|
21145
|
+
const state = raw;
|
|
21146
|
+
if (state.status !== "running" && state.status !== "blocked" && state.status !== "failed" && state.status !== "done")
|
|
21147
|
+
return null;
|
|
21148
|
+
const completedStepIds = Array.isArray(state.completedStepIds) ? state.completedStepIds.filter((value) => typeof value === "string") : [];
|
|
21149
|
+
const transitionCounts = state.transitionCounts && typeof state.transitionCounts === "object" && !Array.isArray(state.transitionCounts) ? Object.fromEntries(
|
|
21150
|
+
Object.entries(state.transitionCounts).filter(
|
|
21151
|
+
(entry) => typeof entry[1] === "number" && Number.isInteger(entry[1]) && entry[1] >= 0
|
|
21152
|
+
)
|
|
21153
|
+
) : {};
|
|
21154
|
+
const facts = state.facts && typeof state.facts === "object" && !Array.isArray(state.facts) ? state.facts : {};
|
|
21155
|
+
const evidenceEntries = state.evidence && typeof state.evidence === "object" && !Array.isArray(state.evidence) ? Object.entries(state.evidence).filter((entry) => typeof entry[1] === "boolean") : [];
|
|
21156
|
+
const artifacts = Array.isArray(state.artifacts) ? state.artifacts.filter(
|
|
21157
|
+
(artifact) => !!artifact && typeof artifact === "object" && typeof artifact.label === "string" && (artifact.url === void 0 || typeof artifact.url === "string") && (artifact.path === void 0 || typeof artifact.path === "string")
|
|
21158
|
+
) : [];
|
|
21159
|
+
return {
|
|
21160
|
+
status: state.status,
|
|
21161
|
+
...typeof state.currentStepId === "string" ? { currentStepId: state.currentStepId } : {},
|
|
21162
|
+
completedStepIds,
|
|
21163
|
+
transitionCounts,
|
|
21164
|
+
facts: { ...facts },
|
|
21165
|
+
evidence: Object.fromEntries(evidenceEntries),
|
|
21166
|
+
artifacts: artifacts.map((artifact) => ({ ...artifact })),
|
|
21167
|
+
...typeof state.blocker === "string" ? { blocker: state.blocker } : {}
|
|
21168
|
+
};
|
|
21169
|
+
}
|
|
21170
|
+
function readWorkflowRunState(config, cwd, workflowId, runId) {
|
|
21171
|
+
const file = readStateText(config, cwd, workflowRunStatePath(workflowId, runId));
|
|
21172
|
+
if (!file) return null;
|
|
21173
|
+
try {
|
|
21174
|
+
return parseWorkflowRunState(JSON.parse(file.content));
|
|
21175
|
+
} catch {
|
|
21176
|
+
return null;
|
|
21177
|
+
}
|
|
21178
|
+
}
|
|
21179
|
+
function writeWorkflowRunState(config, cwd, workflowId, runId, state) {
|
|
21180
|
+
const path52 = workflowRunStatePath(workflowId, runId);
|
|
21181
|
+
upsertStateText(
|
|
21182
|
+
config,
|
|
21183
|
+
cwd,
|
|
21184
|
+
path52,
|
|
21185
|
+
`${JSON.stringify(state, null, 2)}
|
|
21186
|
+
`,
|
|
21187
|
+
`chore(workflows): update ${workflowId} run ${runId}`
|
|
21188
|
+
);
|
|
21189
|
+
}
|
|
21190
|
+
var SAFE_ID;
|
|
21191
|
+
var init_workflowRunState = __esm({
|
|
21192
|
+
"src/workflowRunState.ts"() {
|
|
21193
|
+
"use strict";
|
|
21194
|
+
init_stateRepo();
|
|
21195
|
+
SAFE_ID = /^[a-z0-9][a-z0-9_-]{0,79}$/;
|
|
21196
|
+
}
|
|
21197
|
+
});
|
|
21198
|
+
|
|
20759
21199
|
// src/job.ts
|
|
20760
21200
|
var job_exports = {};
|
|
20761
21201
|
__export(job_exports, {
|
|
@@ -20768,7 +21208,7 @@ __export(job_exports, {
|
|
|
20768
21208
|
stableJobKey: () => stableJobKey,
|
|
20769
21209
|
validateJob: () => validateJob
|
|
20770
21210
|
});
|
|
20771
|
-
import * as
|
|
21211
|
+
import * as path46 from "path";
|
|
20772
21212
|
function newJobId(flavor) {
|
|
20773
21213
|
localJobSeq += 1;
|
|
20774
21214
|
const runId = process.env.GITHUB_RUN_ID;
|
|
@@ -20800,6 +21240,8 @@ function validateJob(input) {
|
|
|
20800
21240
|
target: typeof j.target === "number" ? j.target : void 0,
|
|
20801
21241
|
cliArgs: j.cliArgs ?? {},
|
|
20802
21242
|
workflowFacts: j.workflowFacts && typeof j.workflowFacts === "object" && !Array.isArray(j.workflowFacts) ? j.workflowFacts : void 0,
|
|
21243
|
+
workflowState: parseWorkflowRunState(j.workflowState) ?? void 0,
|
|
21244
|
+
workflowRunId: typeof j.workflowRunId === "string" && j.workflowRunId.trim() ? j.workflowRunId.trim() : void 0,
|
|
20803
21245
|
evidence: parseJobEvidence(j),
|
|
20804
21246
|
flavor: j.flavor,
|
|
20805
21247
|
force: j.force === true,
|
|
@@ -20834,7 +21276,7 @@ function parseJobEvidence(job) {
|
|
|
20834
21276
|
async function runJob(job, base) {
|
|
20835
21277
|
const valid = validateJob(job);
|
|
20836
21278
|
const action = valid.action ?? valid.capability;
|
|
20837
|
-
const projectCapabilitiesRoot =
|
|
21279
|
+
const projectCapabilitiesRoot = path46.join(base.cwd, ".kody", "capabilities");
|
|
20838
21280
|
const resolvedCapability = !valid.workflow && action ? resolveCapabilityAction(action, projectCapabilitiesRoot) : null;
|
|
20839
21281
|
const capabilityIdentity = valid.capability ?? resolvedCapability?.capability;
|
|
20840
21282
|
const capabilityContext = valid.workflow ? null : loadCapabilityContext(capabilityIdentity, base.cwd);
|
|
@@ -20852,8 +21294,17 @@ async function runJob(job, base) {
|
|
|
20852
21294
|
const profileName = explicitImplementation ?? capabilitySelectedImplementation;
|
|
20853
21295
|
if (workflow && shouldRunCapabilityWorkflow(valid, workflow, workflowIdentity, capabilitySelectedImplementation, base)) {
|
|
20854
21296
|
const workflowCapability = capabilityContext ?? workflowContext;
|
|
20855
|
-
const
|
|
20856
|
-
|
|
21297
|
+
const persistedState = valid.workflowRunId && workflowIdentity && base.config ? readWorkflowRunState(base.config, base.cwd, workflowIdentity, valid.workflowRunId) : null;
|
|
21298
|
+
const workflowJob = {
|
|
21299
|
+
...workflowContext && !valid.why ? { ...valid, why: workflowContext.body } : valid,
|
|
21300
|
+
...valid.workflowState ?? persistedState ? { workflowState: valid.workflowState ?? persistedState ?? void 0 } : {}
|
|
21301
|
+
};
|
|
21302
|
+
const checkpoint = valid.workflowRunId && workflowIdentity && base.config ? (state) => writeWorkflowRunState(base.config, base.cwd, workflowIdentity, valid.workflowRunId, state) : void 0;
|
|
21303
|
+
const result = await runCapabilityWorkflow(workflowJob, workflow, workflowCapability, base, checkpoint);
|
|
21304
|
+
if (valid.workflowRunId && workflowIdentity && base.config && result.workflowState) {
|
|
21305
|
+
writeWorkflowRunState(base.config, base.cwd, workflowIdentity, valid.workflowRunId, result.workflowState);
|
|
21306
|
+
}
|
|
21307
|
+
return result;
|
|
20857
21308
|
}
|
|
20858
21309
|
if (!profileName) {
|
|
20859
21310
|
throw new InvalidJobError(`job capability resolves to no implementation: ${capabilityIdentity ?? action}`);
|
|
@@ -20933,7 +21384,22 @@ function shouldRunCapabilityWorkflow(job, workflow, capabilityIdentity, selected
|
|
|
20933
21384
|
if (!requestedImplementation) return true;
|
|
20934
21385
|
return requestedImplementation === selectedImplementation || requestedImplementation === capabilityIdentity || requestedImplementation === job.action;
|
|
20935
21386
|
}
|
|
20936
|
-
async function runCapabilityWorkflow(parent, workflow, capability, base) {
|
|
21387
|
+
async function runCapabilityWorkflow(parent, workflow, capability, base, checkpoint) {
|
|
21388
|
+
const invalid = workflowError(workflow, base);
|
|
21389
|
+
if (invalid) {
|
|
21390
|
+
if (isGraphWorkflow(workflow)) {
|
|
21391
|
+
const state = initialWorkflowState(parent, workflow);
|
|
21392
|
+
state.status = "blocked";
|
|
21393
|
+
state.blocker = invalid;
|
|
21394
|
+
checkpoint?.(state);
|
|
21395
|
+
return { exitCode: 64, reason: invalid, workflowState: state };
|
|
21396
|
+
}
|
|
21397
|
+
return { exitCode: 64, reason: invalid };
|
|
21398
|
+
}
|
|
21399
|
+
if (isGraphWorkflow(workflow)) return runGraphCapabilityWorkflow(parent, workflow, capability, base, checkpoint);
|
|
21400
|
+
return runLinearCapabilityWorkflow(parent, workflow, capability, base);
|
|
21401
|
+
}
|
|
21402
|
+
async function runLinearCapabilityWorkflow(parent, workflow, capability, base) {
|
|
20937
21403
|
let chainData = {
|
|
20938
21404
|
...base.preloadedData ?? {},
|
|
20939
21405
|
runSubjectType: "workflow",
|
|
@@ -20997,6 +21463,200 @@ async function runCapabilityWorkflow(parent, workflow, capability, base) {
|
|
|
20997
21463
|
}
|
|
20998
21464
|
return withWorkflowBoundaryEval(capability, result);
|
|
20999
21465
|
}
|
|
21466
|
+
function isGraphWorkflow(workflow) {
|
|
21467
|
+
return workflow.startAt !== void 0 || workflow.steps.some((step) => step.id !== void 0 || step.next !== void 0 || step.inputs !== void 0);
|
|
21468
|
+
}
|
|
21469
|
+
function workflowError(workflow, base) {
|
|
21470
|
+
const projectCapabilitiesRoot = path46.join(base.cwd, ".kody", "capabilities");
|
|
21471
|
+
const knownCapabilities = /* @__PURE__ */ new Set();
|
|
21472
|
+
const capabilityInputs = /* @__PURE__ */ new Map();
|
|
21473
|
+
for (const step of workflow.steps) {
|
|
21474
|
+
const action = step.action ?? step.capability;
|
|
21475
|
+
const resolvedAction = resolveCapabilityAction(action, projectCapabilitiesRoot);
|
|
21476
|
+
const resolvedFolder = resolveCapabilityFolder(step.capability, projectCapabilitiesRoot);
|
|
21477
|
+
if (!resolvedAction && !resolvedFolder) continue;
|
|
21478
|
+
knownCapabilities.add(step.capability);
|
|
21479
|
+
const inputs = getCapabilityActionInputs(action, projectCapabilitiesRoot);
|
|
21480
|
+
if (inputs) {
|
|
21481
|
+
capabilityInputs.set(
|
|
21482
|
+
step.capability,
|
|
21483
|
+
new Set(inputs.flatMap((input) => [input.name, input.flag.replace(/^--/, "")]))
|
|
21484
|
+
);
|
|
21485
|
+
}
|
|
21486
|
+
}
|
|
21487
|
+
return formatWorkflowValidationIssues(validateWorkflow(workflow, { knownCapabilities, capabilityInputs }))[0] ?? null;
|
|
21488
|
+
}
|
|
21489
|
+
function initialWorkflowState(parent, workflow) {
|
|
21490
|
+
const prior = parent.workflowState;
|
|
21491
|
+
if (prior?.status === "done") {
|
|
21492
|
+
return {
|
|
21493
|
+
...prior,
|
|
21494
|
+
status: "done",
|
|
21495
|
+
completedStepIds: [...prior.completedStepIds],
|
|
21496
|
+
transitionCounts: { ...prior.transitionCounts },
|
|
21497
|
+
facts: { ...prior.facts },
|
|
21498
|
+
evidence: { ...prior.evidence },
|
|
21499
|
+
artifacts: prior.artifacts.map((artifact) => ({ ...artifact }))
|
|
21500
|
+
};
|
|
21501
|
+
}
|
|
21502
|
+
const firstStepId = workflow.startAt ?? workflow.steps[0]?.id;
|
|
21503
|
+
const currentStepId = prior?.currentStepId ?? firstStepId;
|
|
21504
|
+
return {
|
|
21505
|
+
status: "running",
|
|
21506
|
+
...currentStepId ? { currentStepId } : {},
|
|
21507
|
+
completedStepIds: [...prior?.completedStepIds ?? []],
|
|
21508
|
+
transitionCounts: { ...prior?.transitionCounts ?? {} },
|
|
21509
|
+
facts: { ...parent.workflowFacts ?? {}, ...prior?.facts ?? {} },
|
|
21510
|
+
evidence: { ...prior?.evidence ?? {} },
|
|
21511
|
+
artifacts: (prior?.artifacts ?? []).map((artifact) => ({ ...artifact }))
|
|
21512
|
+
};
|
|
21513
|
+
}
|
|
21514
|
+
function workflowChainData(parent, capability, base, state) {
|
|
21515
|
+
return {
|
|
21516
|
+
...base.preloadedData ?? {},
|
|
21517
|
+
runSubjectType: "workflow",
|
|
21518
|
+
runSubjectId: capability.slug,
|
|
21519
|
+
runSubjectLabel: capability.title,
|
|
21520
|
+
runSubjectWorkflow: capability.slug,
|
|
21521
|
+
workflowCapability: capability.slug,
|
|
21522
|
+
workflowTitle: capability.title,
|
|
21523
|
+
workflowStepCount: capability.config.workflow?.steps.length ?? 0,
|
|
21524
|
+
workflowIssueNumber: workflowIssueNumber(parent),
|
|
21525
|
+
workflowFacts: state.facts,
|
|
21526
|
+
workflowEvidence: state.evidence,
|
|
21527
|
+
workflowArtifacts: state.artifacts,
|
|
21528
|
+
workflowStack: [
|
|
21529
|
+
...Array.isArray(base.preloadedData?.workflowStack) ? base.preloadedData.workflowStack.filter((entry) => typeof entry === "string") : [],
|
|
21530
|
+
capability.slug
|
|
21531
|
+
]
|
|
21532
|
+
};
|
|
21533
|
+
}
|
|
21534
|
+
async function runGraphCapabilityWorkflow(parent, workflow, capability, base, checkpoint) {
|
|
21535
|
+
const state = initialWorkflowState(parent, workflow);
|
|
21536
|
+
let chainData = workflowChainData(parent, capability, base, state);
|
|
21537
|
+
let result = { exitCode: 0 };
|
|
21538
|
+
let executedSteps = 0;
|
|
21539
|
+
const maxExecutedSteps = 1e3;
|
|
21540
|
+
while (state.currentStepId) {
|
|
21541
|
+
executedSteps += 1;
|
|
21542
|
+
if (executedSteps > maxExecutedSteps) {
|
|
21543
|
+
const reason = `workflow ${capability.slug} exceeded ${maxExecutedSteps} executed steps`;
|
|
21544
|
+
state.status = "blocked";
|
|
21545
|
+
state.blocker = reason;
|
|
21546
|
+
checkpoint?.(state);
|
|
21547
|
+
return { ...result, exitCode: 64, reason, workflowState: state };
|
|
21548
|
+
}
|
|
21549
|
+
const index = workflow.steps.findIndex((step2) => step2.id === state.currentStepId);
|
|
21550
|
+
const step = workflow.steps[index];
|
|
21551
|
+
if (!step) {
|
|
21552
|
+
const reason = `workflow ${capability.slug} current step ${state.currentStepId} is missing`;
|
|
21553
|
+
state.status = "blocked";
|
|
21554
|
+
state.blocker = reason;
|
|
21555
|
+
checkpoint?.(state);
|
|
21556
|
+
return { ...result, exitCode: 64, reason, workflowState: state };
|
|
21557
|
+
}
|
|
21558
|
+
const label = step.action ?? step.capability;
|
|
21559
|
+
checkpoint?.(state);
|
|
21560
|
+
let child;
|
|
21561
|
+
try {
|
|
21562
|
+
child = workflowStepToJob(step, parent, chainData);
|
|
21563
|
+
} catch (error) {
|
|
21564
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
21565
|
+
state.status = "blocked";
|
|
21566
|
+
state.blocker = reason;
|
|
21567
|
+
checkpoint?.(state);
|
|
21568
|
+
return { exitCode: 64, reason, workflowState: state };
|
|
21569
|
+
}
|
|
21570
|
+
process.stdout.write(
|
|
21571
|
+
`\u2192 kody: workflow ${capability.slug} step ${index + 1}/${workflow.steps.length} \u2192 ${label}
|
|
21572
|
+
|
|
21573
|
+
`
|
|
21574
|
+
);
|
|
21575
|
+
result = await runJob(child, {
|
|
21576
|
+
...base,
|
|
21577
|
+
preloadedData: {
|
|
21578
|
+
...chainData,
|
|
21579
|
+
workflowStep: step.id,
|
|
21580
|
+
workflowStepIndex: index + 1,
|
|
21581
|
+
workflowStepReason: step.reason,
|
|
21582
|
+
workflowContinueOn: step.continueOn ?? []
|
|
21583
|
+
}
|
|
21584
|
+
});
|
|
21585
|
+
mergeWorkflowResults(state, result.capabilityResults);
|
|
21586
|
+
const outcome = workflowOutcome(result);
|
|
21587
|
+
const prUrl = result.prUrl ?? result.taskState?.core.prUrl ?? (typeof chainData.workflowPrUrl === "string" ? chainData.workflowPrUrl : void 0);
|
|
21588
|
+
chainData = {
|
|
21589
|
+
...workflowChainData(parent, capability, base, state),
|
|
21590
|
+
...result.taskState ? { taskState: result.taskState } : {},
|
|
21591
|
+
...outcome ? { workflowLastOutcome: outcome } : {},
|
|
21592
|
+
...result.capabilityResults?.at(-1) ? { workflowLastResult: result.capabilityResults.at(-1) } : {},
|
|
21593
|
+
...prUrl ? { workflowPrUrl: prUrl } : {},
|
|
21594
|
+
...parsePrNumber5(prUrl) ? { workflowPrNumber: parsePrNumber5(prUrl) } : {}
|
|
21595
|
+
};
|
|
21596
|
+
if (!state.completedStepIds.includes(step.id)) state.completedStepIds.push(step.id);
|
|
21597
|
+
if (result.exitCode !== 0 && !canContinueWorkflow(step, outcome)) {
|
|
21598
|
+
state.status = "failed";
|
|
21599
|
+
state.blocker = result.reason ?? `workflow step ${step.id} failed`;
|
|
21600
|
+
checkpoint?.(state);
|
|
21601
|
+
return withWorkflowBoundaryEval(capability, { ...result, workflowState: state });
|
|
21602
|
+
}
|
|
21603
|
+
if (!step.next || step.next.length === 0) {
|
|
21604
|
+
state.status = "done";
|
|
21605
|
+
delete state.currentStepId;
|
|
21606
|
+
delete state.blocker;
|
|
21607
|
+
checkpoint?.(state);
|
|
21608
|
+
return withWorkflowBoundaryEval(capability, { ...result, workflowState: state });
|
|
21609
|
+
}
|
|
21610
|
+
const transition = selectWorkflowTransition(step, chainData, state.transitionCounts);
|
|
21611
|
+
if (!transition) {
|
|
21612
|
+
const reason = `workflow step ${step.id} has no available connection`;
|
|
21613
|
+
state.status = "blocked";
|
|
21614
|
+
state.blocker = reason;
|
|
21615
|
+
checkpoint?.(state);
|
|
21616
|
+
return { ...result, exitCode: 64, reason, workflowState: state };
|
|
21617
|
+
}
|
|
21618
|
+
if (transition.maxIterations !== void 0) {
|
|
21619
|
+
const key = `${step.id}->${transition.to}`;
|
|
21620
|
+
state.transitionCounts[key] = (state.transitionCounts[key] ?? 0) + 1;
|
|
21621
|
+
}
|
|
21622
|
+
state.currentStepId = transition.to;
|
|
21623
|
+
state.status = "running";
|
|
21624
|
+
delete state.blocker;
|
|
21625
|
+
checkpoint?.(state);
|
|
21626
|
+
}
|
|
21627
|
+
state.status = "done";
|
|
21628
|
+
checkpoint?.(state);
|
|
21629
|
+
return withWorkflowBoundaryEval(capability, { ...result, workflowState: state });
|
|
21630
|
+
}
|
|
21631
|
+
function mergeWorkflowResults(state, results) {
|
|
21632
|
+
for (const result of results ?? []) {
|
|
21633
|
+
Object.assign(state.facts, result.facts);
|
|
21634
|
+
Object.assign(state.evidence, result.evidence ?? {});
|
|
21635
|
+
for (const artifact of result.artifacts) {
|
|
21636
|
+
if (!state.artifacts.some(
|
|
21637
|
+
(existing) => existing.label === artifact.label && existing.url === artifact.url && existing.path === artifact.path
|
|
21638
|
+
)) {
|
|
21639
|
+
state.artifacts.push({ ...artifact });
|
|
21640
|
+
}
|
|
21641
|
+
}
|
|
21642
|
+
}
|
|
21643
|
+
}
|
|
21644
|
+
function selectWorkflowTransition(step, data, counts) {
|
|
21645
|
+
let fallback = null;
|
|
21646
|
+
for (const transition of step.next ?? []) {
|
|
21647
|
+
const key = `${step.id}->${transition.to}`;
|
|
21648
|
+
if (transition.maxIterations !== void 0 && (counts[key] ?? 0) >= transition.maxIterations) continue;
|
|
21649
|
+
if (transition.default === true) {
|
|
21650
|
+
fallback ??= transition;
|
|
21651
|
+
continue;
|
|
21652
|
+
}
|
|
21653
|
+
if (!transition.when || conditionMatches(transition.when, workflowConditionContext(data))) return transition;
|
|
21654
|
+
}
|
|
21655
|
+
return fallback;
|
|
21656
|
+
}
|
|
21657
|
+
function conditionMatches(condition, context) {
|
|
21658
|
+
return Object.entries(condition).every(([path52, expected]) => valueMatches(resolveDottedPath2(context, path52), expected));
|
|
21659
|
+
}
|
|
21000
21660
|
function withWorkflowBoundaryEval(capability, result) {
|
|
21001
21661
|
const capabilityKind = capability.config.capabilityKind;
|
|
21002
21662
|
if (!capabilityKind) return result;
|
|
@@ -21017,8 +21677,18 @@ function withWorkflowBoundaryEval(capability, result) {
|
|
|
21017
21677
|
}
|
|
21018
21678
|
function workflowStepToJob(step, parent, chainData) {
|
|
21019
21679
|
const action = step.action ?? step.capability;
|
|
21680
|
+
const mappedArgs = {};
|
|
21681
|
+
const conditionContext = workflowConditionContext(chainData);
|
|
21682
|
+
for (const [name, mapping] of Object.entries(step.inputs ?? {})) {
|
|
21683
|
+
const value = resolveDottedPath2(conditionContext, mapping.from);
|
|
21684
|
+
if (value === void 0) {
|
|
21685
|
+
throw new InvalidJobError(`workflow step ${step.id ?? action} needs missing input ${mapping.from}`);
|
|
21686
|
+
}
|
|
21687
|
+
mappedArgs[name] = value;
|
|
21688
|
+
}
|
|
21020
21689
|
const rawArgs = {
|
|
21021
21690
|
...parent.cliArgs,
|
|
21691
|
+
...mappedArgs,
|
|
21022
21692
|
...step.cliArgs ?? {}
|
|
21023
21693
|
};
|
|
21024
21694
|
const targetNumber = workflowStepTargetNumber(step, parent, chainData);
|
|
@@ -21052,9 +21722,7 @@ function workflowStepToJob(step, parent, chainData) {
|
|
|
21052
21722
|
function shouldRunWorkflowStep(step, data) {
|
|
21053
21723
|
if (!step.runWhen) return true;
|
|
21054
21724
|
const context = workflowConditionContext(data);
|
|
21055
|
-
return
|
|
21056
|
-
([path51, expected]) => valueMatches(resolveDottedPath2(context, path51), expected)
|
|
21057
|
-
);
|
|
21725
|
+
return conditionMatches(step.runWhen, context);
|
|
21058
21726
|
}
|
|
21059
21727
|
function canContinueWorkflow(step, outcome) {
|
|
21060
21728
|
if (!outcome || !step.continueOn || step.continueOn.length === 0) return false;
|
|
@@ -21065,10 +21733,16 @@ function workflowOutcome(result) {
|
|
|
21065
21733
|
}
|
|
21066
21734
|
function workflowConditionContext(data) {
|
|
21067
21735
|
const lastOutcome = data.workflowLastOutcome;
|
|
21736
|
+
const lastResult = data.workflowLastResult;
|
|
21068
21737
|
return {
|
|
21069
21738
|
...data,
|
|
21739
|
+
facts: data.workflowFacts ?? {},
|
|
21740
|
+
evidence: data.workflowEvidence ?? {},
|
|
21741
|
+
artifacts: data.workflowArtifacts ?? [],
|
|
21742
|
+
result: lastResult,
|
|
21070
21743
|
workflow: {
|
|
21071
21744
|
lastOutcome,
|
|
21745
|
+
lastResult,
|
|
21072
21746
|
issueNumber: data.workflowIssueNumber,
|
|
21073
21747
|
prNumber: data.workflowPrNumber,
|
|
21074
21748
|
prUrl: data.workflowPrUrl
|
|
@@ -21136,7 +21810,7 @@ function composeStepWhy(parentWhy, step) {
|
|
|
21136
21810
|
}
|
|
21137
21811
|
function loadCapabilityContext(slug, cwd) {
|
|
21138
21812
|
if (!slug) return null;
|
|
21139
|
-
return resolveCapabilityFolder(slug,
|
|
21813
|
+
return resolveCapabilityFolder(slug, path46.join(cwd, ".kody", "capabilities"));
|
|
21140
21814
|
}
|
|
21141
21815
|
function loadWorkflowContext(slug, base) {
|
|
21142
21816
|
if (!slug || !base.config || !isWorkflowDefinitionId(slug)) return null;
|
|
@@ -21175,6 +21849,8 @@ var init_job = __esm({
|
|
|
21175
21849
|
init_executor();
|
|
21176
21850
|
init_registry();
|
|
21177
21851
|
init_workflowDefinitions();
|
|
21852
|
+
init_workflowRunState();
|
|
21853
|
+
init_workflowValidation();
|
|
21178
21854
|
init_jobIdentity();
|
|
21179
21855
|
init_jobIdentity();
|
|
21180
21856
|
DEFAULT_INSTANT_AGENT = "kody";
|
|
@@ -21297,7 +21973,7 @@ function translateOpenAISseToBrain(opts) {
|
|
|
21297
21973
|
// src/servers/brain-serve.ts
|
|
21298
21974
|
import * as fs49 from "fs";
|
|
21299
21975
|
import { createServer as createServer2 } from "http";
|
|
21300
|
-
import * as
|
|
21976
|
+
import * as path49 from "path";
|
|
21301
21977
|
|
|
21302
21978
|
// src/chat/loop.ts
|
|
21303
21979
|
init_agent();
|
|
@@ -21827,8 +22503,8 @@ async function runOpenAIChatTurn(args) {
|
|
|
21827
22503
|
})
|
|
21828
22504
|
});
|
|
21829
22505
|
if (!response.ok) {
|
|
21830
|
-
const
|
|
21831
|
-
const error = `OpenAI-compatible model request failed ${response.status}${
|
|
22506
|
+
const text2 = await response.text().catch(() => "");
|
|
22507
|
+
const error = `OpenAI-compatible model request failed ${response.status}${text2 ? `: ${text2.slice(0, 500)}` : ""}`;
|
|
21832
22508
|
await emit(opts.sink, "chat.error", opts.sessionId, "error", { error });
|
|
21833
22509
|
return { exitCode: 99, error };
|
|
21834
22510
|
}
|
|
@@ -21866,8 +22542,8 @@ function extractOpenAIReply(payload) {
|
|
|
21866
22542
|
return content.map((part) => {
|
|
21867
22543
|
if (typeof part === "string") return part;
|
|
21868
22544
|
if (part && typeof part === "object" && "text" in part) {
|
|
21869
|
-
const
|
|
21870
|
-
return typeof
|
|
22545
|
+
const text2 = part.text;
|
|
22546
|
+
return typeof text2 === "string" ? text2 : "";
|
|
21871
22547
|
}
|
|
21872
22548
|
return "";
|
|
21873
22549
|
}).join("");
|
|
@@ -21985,7 +22661,7 @@ init_config();
|
|
|
21985
22661
|
// src/kody-cli.ts
|
|
21986
22662
|
import { execFileSync as execFileSync26 } from "child_process";
|
|
21987
22663
|
import * as fs47 from "fs";
|
|
21988
|
-
import * as
|
|
22664
|
+
import * as path47 from "path";
|
|
21989
22665
|
|
|
21990
22666
|
// src/app-auth.ts
|
|
21991
22667
|
import { createSign } from "crypto";
|
|
@@ -22256,7 +22932,7 @@ function autoDispatch(opts) {
|
|
|
22256
22932
|
}
|
|
22257
22933
|
if (eventName !== "issue_comment") return null;
|
|
22258
22934
|
const comment = objectValue(event.comment);
|
|
22259
|
-
const
|
|
22935
|
+
const issue2 = objectValue(event.issue);
|
|
22260
22936
|
const user = objectValue(comment?.user);
|
|
22261
22937
|
const rawBody = String(comment?.body ?? "");
|
|
22262
22938
|
const authorLogin = String(user?.login ?? "");
|
|
@@ -22265,8 +22941,8 @@ function autoDispatch(opts) {
|
|
|
22265
22941
|
const isBotAuthor = authorLogin === "kody-bot" || authorType === "Bot";
|
|
22266
22942
|
if (!associationAllowed(event, opts?.config)) return null;
|
|
22267
22943
|
const body = rawBody;
|
|
22268
|
-
const targetNum = Number(
|
|
22269
|
-
const isPr = !!
|
|
22944
|
+
const targetNum = Number(issue2?.number ?? 0);
|
|
22945
|
+
const isPr = !!issue2?.pull_request;
|
|
22270
22946
|
if (!targetNum) return null;
|
|
22271
22947
|
const afterTag = extractAfterTag(body);
|
|
22272
22948
|
const firstTokenRaw = extractSubcommand(afterTag);
|
|
@@ -22350,7 +23026,7 @@ function autoDispatchTyped(opts) {
|
|
|
22350
23026
|
return { kind: "silent", reason: "GHA event payload unreadable" };
|
|
22351
23027
|
}
|
|
22352
23028
|
const comment = objectValue(event.comment);
|
|
22353
|
-
const
|
|
23029
|
+
const issue2 = objectValue(event.issue);
|
|
22354
23030
|
const user = objectValue(comment?.user);
|
|
22355
23031
|
const rawBody = String(comment?.body ?? "");
|
|
22356
23032
|
const authorLogin = String(user?.login ?? "");
|
|
@@ -22358,8 +23034,8 @@ function autoDispatchTyped(opts) {
|
|
|
22358
23034
|
if (!hasKodyMention(rawBody)) {
|
|
22359
23035
|
return { kind: "silent", reason: "comment does not mention @kody" };
|
|
22360
23036
|
}
|
|
22361
|
-
const targetNum = Number(
|
|
22362
|
-
const isPr = !!
|
|
23037
|
+
const targetNum = Number(issue2?.number ?? 0);
|
|
23038
|
+
const isPr = !!issue2?.pull_request;
|
|
22363
23039
|
if (!targetNum) {
|
|
22364
23040
|
return { kind: "silent", reason: "comment has no associated issue/PR number" };
|
|
22365
23041
|
}
|
|
@@ -22703,7 +23379,8 @@ function routeRunRequest(request) {
|
|
|
22703
23379
|
if (intent !== "run" && intent !== "tick") {
|
|
22704
23380
|
return { kind: "error", error: `workflow target does not support intent '${intent}'` };
|
|
22705
23381
|
}
|
|
22706
|
-
|
|
23382
|
+
const workflowRunId = typeof request.input?.runId === "string" && /^[a-z0-9][a-z0-9_-]{0,79}$/.test(request.input.runId) ? request.input.runId : void 0;
|
|
23383
|
+
return { kind: "action", action: target.id, cliArgs: {}, ...workflowRunId ? { workflowRunId } : {} };
|
|
22707
23384
|
}
|
|
22708
23385
|
return { kind: "error", error: "unsupported run request target" };
|
|
22709
23386
|
}
|
|
@@ -22782,9 +23459,9 @@ async function resolveAuthToken(env = process.env) {
|
|
|
22782
23459
|
return void 0;
|
|
22783
23460
|
}
|
|
22784
23461
|
function detectPackageManager2(cwd) {
|
|
22785
|
-
if (fs47.existsSync(
|
|
22786
|
-
if (fs47.existsSync(
|
|
22787
|
-
if (fs47.existsSync(
|
|
23462
|
+
if (fs47.existsSync(path47.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
|
|
23463
|
+
if (fs47.existsSync(path47.join(cwd, "yarn.lock"))) return "yarn";
|
|
23464
|
+
if (fs47.existsSync(path47.join(cwd, "bun.lockb"))) return "bun";
|
|
22788
23465
|
return "npm";
|
|
22789
23466
|
}
|
|
22790
23467
|
function shouldChainScheduledWatch(match) {
|
|
@@ -22907,7 +23584,7 @@ async function runCi(argv) {
|
|
|
22907
23584
|
return 0;
|
|
22908
23585
|
}
|
|
22909
23586
|
const args = parseCiArgs(argv);
|
|
22910
|
-
const cwd = args.cwd ?
|
|
23587
|
+
const cwd = args.cwd ? path47.resolve(args.cwd) : process.cwd();
|
|
22911
23588
|
try {
|
|
22912
23589
|
const n = unpackAllSecrets();
|
|
22913
23590
|
if (n > 0) process.stdout.write(`\u2192 kody: unpacked ${n} secret(s) from ALL_SECRETS
|
|
@@ -22931,6 +23608,7 @@ async function runCi(argv) {
|
|
|
22931
23608
|
let manualWorkflowDispatch = false;
|
|
22932
23609
|
let forceRunAction = null;
|
|
22933
23610
|
let forceRunCliArgs = {};
|
|
23611
|
+
let forceWorkflowRunId;
|
|
22934
23612
|
let runRequestFanOut = false;
|
|
22935
23613
|
let runRequestFanOutForce = false;
|
|
22936
23614
|
const parsedRunRequest = readRunRequestFromEnv();
|
|
@@ -22955,6 +23633,7 @@ async function runCi(argv) {
|
|
|
22955
23633
|
} else if (route.kind === "action") {
|
|
22956
23634
|
forceRunAction = route.action;
|
|
22957
23635
|
forceRunCliArgs = route.cliArgs;
|
|
23636
|
+
forceWorkflowRunId = route.workflowRunId;
|
|
22958
23637
|
}
|
|
22959
23638
|
}
|
|
22960
23639
|
const envForceAction = (process.env.KODY_FORCE_ACTION ?? "").trim();
|
|
@@ -23052,6 +23731,7 @@ async function runCi(argv) {
|
|
|
23052
23731
|
capability: route.capability,
|
|
23053
23732
|
workflow: route.workflow,
|
|
23054
23733
|
implementation: route.implementation,
|
|
23734
|
+
workflowRunId: forceWorkflowRunId,
|
|
23055
23735
|
cliArgs: { ...route.cliArgs, ...forceRunCliArgs },
|
|
23056
23736
|
flavor: "instant",
|
|
23057
23737
|
force: true
|
|
@@ -23341,7 +24021,7 @@ init_repoWorkspace();
|
|
|
23341
24021
|
// src/scripts/brainTurnLog.ts
|
|
23342
24022
|
init_runtimePaths();
|
|
23343
24023
|
import * as fs48 from "fs";
|
|
23344
|
-
import * as
|
|
24024
|
+
import * as path48 from "path";
|
|
23345
24025
|
import posixPath4 from "path/posix";
|
|
23346
24026
|
var live = /* @__PURE__ */ new Map();
|
|
23347
24027
|
function brainEventsFilePath(dir, chatId) {
|
|
@@ -23391,7 +24071,7 @@ function beginTurn(dir, chatId) {
|
|
|
23391
24071
|
};
|
|
23392
24072
|
live.set(chatId, state);
|
|
23393
24073
|
const p = brainEventsFilePath(dir, chatId);
|
|
23394
|
-
fs48.mkdirSync(
|
|
24074
|
+
fs48.mkdirSync(path48.dirname(p), { recursive: true });
|
|
23395
24075
|
return (event) => {
|
|
23396
24076
|
state.seq += 1;
|
|
23397
24077
|
const rec = { seq: state.seq, turn, ts: Date.now(), event };
|
|
@@ -23769,7 +24449,7 @@ async function handleChatTurn(req, res, chatId, opts) {
|
|
|
23769
24449
|
);
|
|
23770
24450
|
}
|
|
23771
24451
|
}
|
|
23772
|
-
fs49.mkdirSync(
|
|
24452
|
+
fs49.mkdirSync(path49.dirname(sessionFile), { recursive: true });
|
|
23773
24453
|
appendTurn(sessionFile, {
|
|
23774
24454
|
role: "user",
|
|
23775
24455
|
content: message,
|
|
@@ -23844,7 +24524,7 @@ async function handleChatTurn(req, res, chatId, opts) {
|
|
|
23844
24524
|
function buildServer(opts) {
|
|
23845
24525
|
const runTurn = opts.runTurn ?? runChatTurn;
|
|
23846
24526
|
const cloneRepo = opts.cloneRepo ?? defaultCloneRepo;
|
|
23847
|
-
const reposRoot = opts.reposRoot ??
|
|
24527
|
+
const reposRoot = opts.reposRoot ?? path49.join(path49.dirname(path49.resolve(opts.cwd)), "repos");
|
|
23848
24528
|
return createServer2(async (req, res) => {
|
|
23849
24529
|
if (!req.method || !req.url) {
|
|
23850
24530
|
sendJson(res, 400, { error: "bad request" });
|
|
@@ -24448,7 +25128,7 @@ async function loadConfigSafe() {
|
|
|
24448
25128
|
|
|
24449
25129
|
// src/chat-cli.ts
|
|
24450
25130
|
import * as fs51 from "fs";
|
|
24451
|
-
import * as
|
|
25131
|
+
import * as path51 from "path";
|
|
24452
25132
|
|
|
24453
25133
|
// src/chat/inbox.ts
|
|
24454
25134
|
import { execFileSync as execFileSync27 } from "child_process";
|
|
@@ -24521,9 +25201,9 @@ function currentBranch(cwd) {
|
|
|
24521
25201
|
// src/chat/state-sync.ts
|
|
24522
25202
|
init_stateRepo();
|
|
24523
25203
|
import * as fs50 from "fs";
|
|
24524
|
-
import * as
|
|
24525
|
-
function jsonlLines2(
|
|
24526
|
-
return
|
|
25204
|
+
import * as path50 from "path";
|
|
25205
|
+
function jsonlLines2(text2) {
|
|
25206
|
+
return text2.split("\n").filter((line) => line.length > 0);
|
|
24527
25207
|
}
|
|
24528
25208
|
function renderJsonl2(lines) {
|
|
24529
25209
|
return lines.length > 0 ? `${lines.join("\n")}
|
|
@@ -24541,7 +25221,7 @@ function syncJsonlFileFromState(opts) {
|
|
|
24541
25221
|
const local = fs50.existsSync(opts.localPath) ? fs50.readFileSync(opts.localPath, "utf-8") : "";
|
|
24542
25222
|
const next = mergeJsonl2(local, remote.content);
|
|
24543
25223
|
if (next === local) return;
|
|
24544
|
-
fs50.mkdirSync(
|
|
25224
|
+
fs50.mkdirSync(path50.dirname(opts.localPath), { recursive: true });
|
|
24545
25225
|
fs50.writeFileSync(opts.localPath, next);
|
|
24546
25226
|
}
|
|
24547
25227
|
function persistJsonlFileToState(opts) {
|
|
@@ -24811,7 +25491,7 @@ async function runChat(argv) {
|
|
|
24811
25491
|
${CHAT_HELP}`);
|
|
24812
25492
|
return 64;
|
|
24813
25493
|
}
|
|
24814
|
-
const cwd = args.cwd ?
|
|
25494
|
+
const cwd = args.cwd ? path51.resolve(args.cwd) : process.cwd();
|
|
24815
25495
|
const sessionId = args.sessionId;
|
|
24816
25496
|
const runRequest = readRunRequestFromEnv();
|
|
24817
25497
|
if (runRequest && "request" in runRequest) {
|
|
@@ -24991,8 +25671,8 @@ var FlyClient = class {
|
|
|
24991
25671
|
get fetch() {
|
|
24992
25672
|
return this.opts.fetchImpl ?? fetch;
|
|
24993
25673
|
}
|
|
24994
|
-
async call(
|
|
24995
|
-
const res = await this.fetch(`${FLY_API_BASE}${
|
|
25674
|
+
async call(path52, init = {}) {
|
|
25675
|
+
const res = await this.fetch(`${FLY_API_BASE}${path52}`, {
|
|
24996
25676
|
method: init.method ?? "GET",
|
|
24997
25677
|
headers: {
|
|
24998
25678
|
Authorization: `Bearer ${this.opts.token}`,
|
|
@@ -25002,8 +25682,8 @@ var FlyClient = class {
|
|
|
25002
25682
|
});
|
|
25003
25683
|
if (res.status === 404 && init.allow404) return null;
|
|
25004
25684
|
if (!res.ok) {
|
|
25005
|
-
const
|
|
25006
|
-
throw new Error(`Fly API ${res.status} on ${
|
|
25685
|
+
const text2 = await res.text().catch(() => "");
|
|
25686
|
+
throw new Error(`Fly API ${res.status} on ${path52}: ${text2.slice(0, 200) || res.statusText}`);
|
|
25007
25687
|
}
|
|
25008
25688
|
if (res.status === 204) return null;
|
|
25009
25689
|
const raw = await res.text();
|
|
@@ -26350,11 +27030,11 @@ function envRunMode(env = process.env) {
|
|
|
26350
27030
|
return { ...result, command: "ci", ciArgv: [] };
|
|
26351
27031
|
}
|
|
26352
27032
|
if (mode === "issue") {
|
|
26353
|
-
const
|
|
26354
|
-
if (!
|
|
27033
|
+
const issue2 = (env.ISSUE_NUMBER ?? "").trim();
|
|
27034
|
+
if (!issue2) {
|
|
26355
27035
|
return { ...result, errors: ["KODY_RUN_MODE=issue requires ISSUE_NUMBER"] };
|
|
26356
27036
|
}
|
|
26357
|
-
return { ...result, command: "ci", ciArgv: ["--issue",
|
|
27037
|
+
return { ...result, command: "ci", ciArgv: ["--issue", issue2] };
|
|
26358
27038
|
}
|
|
26359
27039
|
return { ...result, errors: [`unknown KODY_RUN_MODE: ${mode}`] };
|
|
26360
27040
|
}
|