@kody-ade/kody-engine 0.4.373 → 0.4.375
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 +965 -284
- package/dist/implementations/types.ts +15 -0
- package/package.json +25 -24
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.375",
|
|
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,291 @@ 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_STEP_ID.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_STEP_ID, 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_STEP_ID = /^[A-Za-z][A-Za-z0-9_-]*$/;
|
|
9022
|
+
SAFE_DATA_PATH = /^(facts|evidence|artifacts|result|workflow|lastOutcome)(?:\.[A-Za-z_][A-Za-z0-9_-]*)+$/;
|
|
9023
|
+
SUPPORTED_STEP_FIELDS = /* @__PURE__ */ new Set([
|
|
9024
|
+
"id",
|
|
9025
|
+
"capability",
|
|
9026
|
+
"action",
|
|
9027
|
+
"implementation",
|
|
9028
|
+
"evidence",
|
|
9029
|
+
"target",
|
|
9030
|
+
"targetFact",
|
|
9031
|
+
"reason",
|
|
9032
|
+
"agent",
|
|
9033
|
+
"cliArgs",
|
|
9034
|
+
"inputs",
|
|
9035
|
+
"next",
|
|
9036
|
+
"runWhen",
|
|
9037
|
+
"continueOn",
|
|
9038
|
+
"saveReport",
|
|
9039
|
+
"report"
|
|
9040
|
+
]);
|
|
9041
|
+
SUPPORTED_TRANSITION_FIELDS = /* @__PURE__ */ new Set(["to", "when", "default", "maxIterations"]);
|
|
9042
|
+
}
|
|
9043
|
+
});
|
|
9044
|
+
|
|
8729
9045
|
// src/workflowDefinitions.ts
|
|
8730
9046
|
import * as fs28 from "fs";
|
|
8731
9047
|
import * as path26 from "path";
|
|
@@ -8742,7 +9058,18 @@ function normalizeWorkflowDefinition(value) {
|
|
|
8742
9058
|
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
8743
9059
|
const raw = value;
|
|
8744
9060
|
const name = typeof raw.name === "string" ? raw.name.trim() : "";
|
|
8745
|
-
const
|
|
9061
|
+
const hasGraphConnections = Array.isArray(raw.steps) && raw.steps.some(
|
|
9062
|
+
(step) => step && typeof step === "object" && !Array.isArray(step) && (step.next !== void 0 || step.inputs !== void 0)
|
|
9063
|
+
);
|
|
9064
|
+
if (hasGraphConnections) {
|
|
9065
|
+
if (validateWorkflow({ steps: raw.steps, ...raw.startAt !== void 0 ? { startAt: raw.startAt } : {} }).length > 0) {
|
|
9066
|
+
return null;
|
|
9067
|
+
}
|
|
9068
|
+
}
|
|
9069
|
+
const workflow = parseCapabilityWorkflow({
|
|
9070
|
+
steps: raw.steps,
|
|
9071
|
+
startAt: raw.startAt
|
|
9072
|
+
});
|
|
8746
9073
|
const steps = workflow?.steps;
|
|
8747
9074
|
const capabilities = steps ? steps.map((step) => step.capability) : normalizeWorkflowCapabilities(raw.capabilities);
|
|
8748
9075
|
if (!name || capabilities.length === 0) return null;
|
|
@@ -8752,6 +9079,7 @@ function normalizeWorkflowDefinition(value) {
|
|
|
8752
9079
|
capabilities,
|
|
8753
9080
|
...raw.runWithoutApproval === true ? { runWithoutApproval: true } : {},
|
|
8754
9081
|
...steps ? { steps } : {},
|
|
9082
|
+
...workflow?.startAt ? { startAt: workflow.startAt } : {},
|
|
8755
9083
|
...typeof raw.createdAt === "string" ? { createdAt: raw.createdAt } : {},
|
|
8756
9084
|
...typeof raw.updatedAt === "string" ? { updatedAt: raw.updatedAt } : {}
|
|
8757
9085
|
};
|
|
@@ -8793,7 +9121,8 @@ function normalizeWorkflowCapabilities(value) {
|
|
|
8793
9121
|
}
|
|
8794
9122
|
function workflowDefinitionToConfig(workflow) {
|
|
8795
9123
|
return {
|
|
8796
|
-
steps: workflow.steps ?? workflow.capabilities.map((capability) => ({ capability }))
|
|
9124
|
+
steps: workflow.steps ?? workflow.capabilities.map((capability) => ({ capability })),
|
|
9125
|
+
...workflow.startAt ? { startAt: workflow.startAt } : {}
|
|
8797
9126
|
};
|
|
8798
9127
|
}
|
|
8799
9128
|
function readCompanyStoreWorkflowDefinition(id) {
|
|
@@ -8817,6 +9146,7 @@ var init_workflowDefinitions = __esm({
|
|
|
8817
9146
|
init_capabilityFolders();
|
|
8818
9147
|
init_companyStore();
|
|
8819
9148
|
init_stateRepo();
|
|
9149
|
+
init_workflowValidation();
|
|
8820
9150
|
WORKFLOW_ID_PATTERN = /^[a-z0-9][a-z0-9_-]{0,79}$/;
|
|
8821
9151
|
CAPABILITY_ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9_-]{0,79}$/;
|
|
8822
9152
|
}
|
|
@@ -9639,7 +9969,7 @@ function readSimpleGoalTaskSummary(goalId, cwd) {
|
|
|
9639
9969
|
);
|
|
9640
9970
|
const issues = JSON.parse(raw);
|
|
9641
9971
|
const total = issues.length;
|
|
9642
|
-
const open = issues.filter((
|
|
9972
|
+
const open = issues.filter((issue2) => String(issue2.state ?? "").toLowerCase() === "open").length;
|
|
9643
9973
|
return { total, open };
|
|
9644
9974
|
}
|
|
9645
9975
|
function previousDispatchWasTargetInstance(managed, previousScheduleState) {
|
|
@@ -9688,7 +10018,7 @@ function findExistingGoalIssue(goalId, cwd) {
|
|
|
9688
10018
|
const marker = goalIssueMarker(goalId);
|
|
9689
10019
|
const raw = gh(["issue", "list", "--state", "all", "--limit", "100", "--json", "number,body"], { cwd });
|
|
9690
10020
|
const issues = JSON.parse(raw);
|
|
9691
|
-
const match = issues.find((
|
|
10021
|
+
const match = issues.find((issue2) => typeof issue2.number === "number" && issue2.body?.includes(marker));
|
|
9692
10022
|
return match?.number ?? null;
|
|
9693
10023
|
}
|
|
9694
10024
|
function createGoalIssue(goal, goalId, cwd) {
|
|
@@ -10537,13 +10867,13 @@ function ensureNeedsFixIssue(ctx, goalId, state, evidence, evidenceKey) {
|
|
|
10537
10867
|
const evidenceState = parseGoalEvidenceState(state.extra.evidenceState);
|
|
10538
10868
|
const progress = evidenceState[evidenceKey];
|
|
10539
10869
|
if (progress?.issue) return state;
|
|
10540
|
-
const
|
|
10870
|
+
const issue2 = findExistingNeedsFixIssue(goalId, evidenceKey, ctx.cwd) ?? createNeedsFixIssue(goalId, evidenceKey, evidence, ctx.cwd);
|
|
10541
10871
|
const nextEvidenceState = mergeGoalEvidenceProgress(evidenceState, evidenceKey, {
|
|
10542
10872
|
resultClass: "needsFix",
|
|
10543
10873
|
attempts: progress?.attempts ?? 1,
|
|
10544
10874
|
reason: evidence.summary,
|
|
10545
|
-
nextAction: `fix issue #${
|
|
10546
|
-
issue,
|
|
10875
|
+
nextAction: `fix issue #${issue2}`,
|
|
10876
|
+
issue: issue2,
|
|
10547
10877
|
updatedAt: nowIso()
|
|
10548
10878
|
});
|
|
10549
10879
|
return {
|
|
@@ -10552,7 +10882,7 @@ function ensureNeedsFixIssue(ctx, goalId, state, evidence, evidenceKey) {
|
|
|
10552
10882
|
...state.extra,
|
|
10553
10883
|
evidenceState: nextEvidenceState,
|
|
10554
10884
|
reason: evidence.summary,
|
|
10555
|
-
nextAction: `fix issue #${
|
|
10885
|
+
nextAction: `fix issue #${issue2}`
|
|
10556
10886
|
}
|
|
10557
10887
|
};
|
|
10558
10888
|
}
|
|
@@ -10570,7 +10900,7 @@ function findExistingNeedsFixIssue(goalId, evidence, cwd) {
|
|
|
10570
10900
|
const marker = needsFixIssueMarker(goalId, evidence);
|
|
10571
10901
|
const raw = gh(["issue", "list", "--state", "all", "--limit", "100", "--json", "number,body"], { cwd });
|
|
10572
10902
|
const issues = JSON.parse(raw);
|
|
10573
|
-
const match = issues.find((
|
|
10903
|
+
const match = issues.find((issue2) => typeof issue2.number === "number" && issue2.body?.includes(marker));
|
|
10574
10904
|
return match?.number ?? null;
|
|
10575
10905
|
}
|
|
10576
10906
|
function createNeedsFixIssue(goalId, evidence, result, cwd) {
|
|
@@ -11008,8 +11338,8 @@ var init_classifyByLabel = __esm({
|
|
|
11008
11338
|
"use strict";
|
|
11009
11339
|
VALID_CLASSES = /* @__PURE__ */ new Set(["feature", "bug", "spec", "chore"]);
|
|
11010
11340
|
classifyByLabel = async (ctx) => {
|
|
11011
|
-
const
|
|
11012
|
-
const labels =
|
|
11341
|
+
const issue2 = ctx.data.issue;
|
|
11342
|
+
const labels = issue2?.labels;
|
|
11013
11343
|
if (!labels || labels.length === 0) return;
|
|
11014
11344
|
const cfgMap = ctx.config.classify?.labelMap;
|
|
11015
11345
|
const map = cfgMap ?? defaultLabelMap();
|
|
@@ -11535,19 +11865,19 @@ function buildGoalName(scope, verdict) {
|
|
|
11535
11865
|
const verdictTag = verdict === "UNKNOWN" ? "REPORT" : verdict;
|
|
11536
11866
|
return `QA: ${focus} \u2014 ${verdictTag} \u2014 ${todayIso()}`.slice(0, 240);
|
|
11537
11867
|
}
|
|
11538
|
-
function splitReport(
|
|
11539
|
-
const open =
|
|
11868
|
+
function splitReport(text2) {
|
|
11869
|
+
const open = text2.indexOf(REPORT_JSON_OPEN);
|
|
11540
11870
|
if (open < 0) {
|
|
11541
|
-
const fallback = parseFallbackFindingsJson(
|
|
11871
|
+
const fallback = parseFallbackFindingsJson(text2);
|
|
11542
11872
|
if (fallback) return fallback;
|
|
11543
|
-
return { markdown:
|
|
11873
|
+
return { markdown: text2.trim(), data: null, jsonError: "no JSON block marker" };
|
|
11544
11874
|
}
|
|
11545
|
-
const closeRel =
|
|
11875
|
+
const closeRel = text2.slice(open + REPORT_JSON_OPEN.length).indexOf(REPORT_JSON_CLOSE);
|
|
11546
11876
|
if (closeRel < 0) {
|
|
11547
|
-
return { markdown:
|
|
11877
|
+
return { markdown: text2.slice(0, open).trim(), data: null, jsonError: "JSON block not terminated" };
|
|
11548
11878
|
}
|
|
11549
11879
|
const closeAbs = open + REPORT_JSON_OPEN.length + closeRel;
|
|
11550
|
-
const rawJson =
|
|
11880
|
+
const rawJson = text2.slice(open + REPORT_JSON_OPEN.length, closeAbs).trim();
|
|
11551
11881
|
const fenced = rawJson.match(/^```(?:json)?\s*([\s\S]*?)\s*```$/);
|
|
11552
11882
|
const cleanJson = fenced ? fenced[1].trim() : rawJson;
|
|
11553
11883
|
let parsed = null;
|
|
@@ -11562,11 +11892,11 @@ function splitReport(text) {
|
|
|
11562
11892
|
} catch (err) {
|
|
11563
11893
|
parseError = err instanceof Error ? err.message : String(err);
|
|
11564
11894
|
}
|
|
11565
|
-
const markdown =
|
|
11895
|
+
const markdown = text2.slice(0, open).trim();
|
|
11566
11896
|
return { markdown, data: parsed, jsonError: parseError };
|
|
11567
11897
|
}
|
|
11568
|
-
function parseFallbackFindingsJson(
|
|
11569
|
-
const fences = [...
|
|
11898
|
+
function parseFallbackFindingsJson(text2) {
|
|
11899
|
+
const fences = [...text2.matchAll(/```(?:json)?\s*([\s\S]*?)\s*```/gi)];
|
|
11570
11900
|
for (let i = fences.length - 1; i >= 0; i--) {
|
|
11571
11901
|
const match = fences[i];
|
|
11572
11902
|
const raw = match[1]?.trim();
|
|
@@ -11575,18 +11905,18 @@ function parseFallbackFindingsJson(text) {
|
|
|
11575
11905
|
const parsed = JSON.parse(raw);
|
|
11576
11906
|
if (!parsed || !Array.isArray(parsed.findings)) {
|
|
11577
11907
|
return {
|
|
11578
|
-
markdown: removeFence(
|
|
11908
|
+
markdown: removeFence(text2, match).trim(),
|
|
11579
11909
|
data: null,
|
|
11580
11910
|
jsonError: "fallback JSON missing 'findings' array"
|
|
11581
11911
|
};
|
|
11582
11912
|
}
|
|
11583
11913
|
return {
|
|
11584
|
-
markdown: removeFence(
|
|
11914
|
+
markdown: removeFence(text2, match).trim(),
|
|
11585
11915
|
data: { findings: parsed.findings.map((f, idx) => normalizeFallbackFinding(f, idx)) }
|
|
11586
11916
|
};
|
|
11587
11917
|
} catch (err) {
|
|
11588
11918
|
return {
|
|
11589
|
-
markdown: removeFence(
|
|
11919
|
+
markdown: removeFence(text2, match).trim(),
|
|
11590
11920
|
data: null,
|
|
11591
11921
|
jsonError: err instanceof Error ? err.message : String(err)
|
|
11592
11922
|
};
|
|
@@ -11594,10 +11924,10 @@ function parseFallbackFindingsJson(text) {
|
|
|
11594
11924
|
}
|
|
11595
11925
|
return null;
|
|
11596
11926
|
}
|
|
11597
|
-
function removeFence(
|
|
11927
|
+
function removeFence(text2, match) {
|
|
11598
11928
|
const start = match.index ?? -1;
|
|
11599
|
-
if (start < 0) return
|
|
11600
|
-
return `${
|
|
11929
|
+
if (start < 0) return text2;
|
|
11930
|
+
return `${text2.slice(0, start)}${text2.slice(start + match[0].length)}`;
|
|
11601
11931
|
}
|
|
11602
11932
|
function normalizeFallbackFinding(raw, idx) {
|
|
11603
11933
|
const finding = raw && typeof raw === "object" ? raw : {};
|
|
@@ -11654,9 +11984,9 @@ function loadManifest(cwd) {
|
|
|
11654
11984
|
return { number: null, manifest: { version: 1, goals: [] } };
|
|
11655
11985
|
}
|
|
11656
11986
|
if (arr.length === 0) return { number: null, manifest: { version: 1, goals: [] } };
|
|
11657
|
-
const
|
|
11658
|
-
const manifest = parseManifestBody(
|
|
11659
|
-
return { number:
|
|
11987
|
+
const issue2 = arr[0];
|
|
11988
|
+
const manifest = parseManifestBody(issue2.body);
|
|
11989
|
+
return { number: issue2.number, manifest };
|
|
11660
11990
|
}
|
|
11661
11991
|
function parseManifestBody(body) {
|
|
11662
11992
|
if (!body) return { version: 1, goals: [] };
|
|
@@ -11882,8 +12212,8 @@ ${markdown}`, ctx.cwd);
|
|
|
11882
12212
|
const failed = [];
|
|
11883
12213
|
for (const f of findings) {
|
|
11884
12214
|
try {
|
|
11885
|
-
const
|
|
11886
|
-
opened.push({ ...
|
|
12215
|
+
const issue2 = createTaskIssue(f, goalId, manifestIssueNumber, ctx.cwd);
|
|
12216
|
+
opened.push({ ...issue2, severity: f.severity });
|
|
11887
12217
|
} catch (err) {
|
|
11888
12218
|
const reason = err instanceof Error ? err.message : String(err);
|
|
11889
12219
|
failed.push({ title: f.title, reason });
|
|
@@ -12025,8 +12355,8 @@ var init_deriveQaScopeFromIssue = __esm({
|
|
|
12025
12355
|
if (!Number.isFinite(issueNumber) || issueNumber <= 0) return;
|
|
12026
12356
|
let title = "";
|
|
12027
12357
|
try {
|
|
12028
|
-
const
|
|
12029
|
-
title = (
|
|
12358
|
+
const issue2 = getIssue(issueNumber, ctx.cwd);
|
|
12359
|
+
title = (issue2.title ?? "").trim();
|
|
12030
12360
|
} catch (err) {
|
|
12031
12361
|
process.stderr.write(
|
|
12032
12362
|
`[kody] deriveQaScopeFromIssue: could not read #${issueNumber}: ${err instanceof Error ? err.message : String(err)}
|
|
@@ -12631,28 +12961,28 @@ var init_dispatchCapabilityTicks = __esm({
|
|
|
12631
12961
|
process.stdout.write(`[jobs] ticking ${issues.length} issue(s) via ${targetImplementation}
|
|
12632
12962
|
`);
|
|
12633
12963
|
const results = [];
|
|
12634
|
-
for (const
|
|
12635
|
-
process.stdout.write(`[jobs] \u2192 tick #${
|
|
12964
|
+
for (const issue2 of issues) {
|
|
12965
|
+
process.stdout.write(`[jobs] \u2192 tick #${issue2.number}: ${issue2.title}
|
|
12636
12966
|
`);
|
|
12637
12967
|
try {
|
|
12638
12968
|
const out = await runJob(
|
|
12639
12969
|
mintScheduledJob({
|
|
12640
12970
|
capability: targetImplementation,
|
|
12641
12971
|
implementation: targetImplementation,
|
|
12642
|
-
cliArgs: { [issueArg]:
|
|
12972
|
+
cliArgs: { [issueArg]: issue2.number }
|
|
12643
12973
|
}),
|
|
12644
12974
|
{ cwd: ctx.cwd, config: ctx.config, verbose: ctx.verbose, quiet: ctx.quiet, chain: false }
|
|
12645
12975
|
);
|
|
12646
|
-
results.push({ issue:
|
|
12976
|
+
results.push({ issue: issue2.number, exitCode: out.exitCode, reason: out.reason });
|
|
12647
12977
|
if (out.exitCode !== 0) {
|
|
12648
|
-
process.stderr.write(`[jobs] tick #${
|
|
12978
|
+
process.stderr.write(`[jobs] tick #${issue2.number} failed (exit ${out.exitCode}): ${out.reason ?? ""}
|
|
12649
12979
|
`);
|
|
12650
12980
|
}
|
|
12651
12981
|
} catch (err) {
|
|
12652
12982
|
const msg = err instanceof Error ? err.message : String(err);
|
|
12653
|
-
process.stderr.write(`[jobs] tick #${
|
|
12983
|
+
process.stderr.write(`[jobs] tick #${issue2.number} crashed: ${msg}
|
|
12654
12984
|
`);
|
|
12655
|
-
results.push({ issue:
|
|
12985
|
+
results.push({ issue: issue2.number, exitCode: 99, reason: msg });
|
|
12656
12986
|
}
|
|
12657
12987
|
}
|
|
12658
12988
|
ctx.data.jobTickResults = results;
|
|
@@ -12921,7 +13251,14 @@ function updateExistingPr(existing, body, draft, cwd, preserveBody) {
|
|
|
12921
13251
|
const promotedTitle = existing.title?.replace(/^\[WIP\]\s*/, "");
|
|
12922
13252
|
if (promotedTitle && promotedTitle !== existing.title) {
|
|
12923
13253
|
gh(
|
|
12924
|
-
[
|
|
13254
|
+
[
|
|
13255
|
+
"api",
|
|
13256
|
+
"--method",
|
|
13257
|
+
"PATCH",
|
|
13258
|
+
`repos/${owner}/${repo}/pulls/${existing.number}`,
|
|
13259
|
+
"-f",
|
|
13260
|
+
`title=${promotedTitle}`
|
|
13261
|
+
],
|
|
12925
13262
|
{ cwd, preferRepoToken: true }
|
|
12926
13263
|
);
|
|
12927
13264
|
}
|
|
@@ -13067,10 +13404,10 @@ var init_ensurePr = __esm({
|
|
|
13067
13404
|
const failureReason = computeFailureReason(ctx);
|
|
13068
13405
|
const isFailure = failureReason.length > 0;
|
|
13069
13406
|
const changedFiles = ctx.data.changedFiles ?? [];
|
|
13070
|
-
const
|
|
13407
|
+
const issue2 = ctx.data.issue;
|
|
13071
13408
|
const pr = ctx.data.pr;
|
|
13072
13409
|
const targetNumber = Number(ctx.data.commentTargetNumber ?? 0);
|
|
13073
|
-
const title =
|
|
13410
|
+
const title = issue2?.title ?? pr?.title ?? `kody changes`;
|
|
13074
13411
|
const baseBranch = ctx.data.baseBranch;
|
|
13075
13412
|
try {
|
|
13076
13413
|
const result = ensurePr({
|
|
@@ -13121,12 +13458,12 @@ var init_failOnceTaskJob = __esm({
|
|
|
13121
13458
|
init_jobIdentity();
|
|
13122
13459
|
failOnceTaskJob = async (ctx, profile) => {
|
|
13123
13460
|
ctx.skipAgent = true;
|
|
13124
|
-
const
|
|
13461
|
+
const issue2 = typeof ctx.args.issue === "number" ? ctx.args.issue : void 0;
|
|
13125
13462
|
const fallbackJob = {
|
|
13126
13463
|
capability: profile.action ?? profile.name,
|
|
13127
13464
|
implementation: profile.name,
|
|
13128
13465
|
flavor: "instant",
|
|
13129
|
-
...typeof
|
|
13466
|
+
...typeof issue2 === "number" ? { target: issue2, cliArgs: { issue: issue2 } } : { cliArgs: {} }
|
|
13130
13467
|
};
|
|
13131
13468
|
const jobKey = typeof ctx.data.jobKey === "string" ? ctx.data.jobKey : stableJobKey(fallbackJob);
|
|
13132
13469
|
const state = ctx.data.taskState;
|
|
@@ -14112,13 +14449,13 @@ function companyIntentPath(id) {
|
|
|
14112
14449
|
assertIntentId(id);
|
|
14113
14450
|
return `intents/${id}/intent.json`;
|
|
14114
14451
|
}
|
|
14115
|
-
function normalizeCompanyIntent(
|
|
14452
|
+
function normalizeCompanyIntent(path52, raw) {
|
|
14116
14453
|
if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
|
|
14117
|
-
throw new Error(`${
|
|
14454
|
+
throw new Error(`${path52}: intent must be JSON object`);
|
|
14118
14455
|
}
|
|
14119
14456
|
const input = raw;
|
|
14120
14457
|
const id = stringField5(input.id);
|
|
14121
|
-
if (!id || !isCompanyIntentId(id)) throw new Error(`${
|
|
14458
|
+
if (!id || !isCompanyIntentId(id)) throw new Error(`${path52}: invalid intent id`);
|
|
14122
14459
|
const createdAt = stringField5(input.createdAt) || nowIso();
|
|
14123
14460
|
const updatedAt = stringField5(input.updatedAt) || createdAt;
|
|
14124
14461
|
const description = stringField5(input.description);
|
|
@@ -14158,8 +14495,8 @@ function listCompanyIntents(config, cwd) {
|
|
|
14158
14495
|
const records = [];
|
|
14159
14496
|
for (const entry of entries) {
|
|
14160
14497
|
if (entry.type !== "dir" || !entry.name || !isCompanyIntentId(entry.name)) continue;
|
|
14161
|
-
const
|
|
14162
|
-
const file = readStateText(config, cwd,
|
|
14498
|
+
const path52 = companyIntentPath(entry.name);
|
|
14499
|
+
const file = readStateText(config, cwd, path52);
|
|
14163
14500
|
if (!file) continue;
|
|
14164
14501
|
records.push({
|
|
14165
14502
|
id: entry.name,
|
|
@@ -14368,14 +14705,14 @@ var init_loadIssueContext = __esm({
|
|
|
14368
14705
|
if (!ctx.data.commentTargetNumber) ctx.data.commentTargetNumber = issueNumber;
|
|
14369
14706
|
return;
|
|
14370
14707
|
}
|
|
14371
|
-
const
|
|
14708
|
+
const issue2 = getIssue(issueNumber, ctx.cwd);
|
|
14372
14709
|
const cfgCtx = ctx.config.issueContext ?? {};
|
|
14373
14710
|
const limit = cfgCtx.commentLimit ?? DEFAULT_COMMENT_LIMIT;
|
|
14374
14711
|
const maxBytes = cfgCtx.commentMaxBytes ?? DEFAULT_COMMENT_MAX_BYTES;
|
|
14375
|
-
const commentsFormatted = formatIssueComments(
|
|
14376
|
-
const labels =
|
|
14712
|
+
const commentsFormatted = formatIssueComments(issue2.comments, limit, maxBytes);
|
|
14713
|
+
const labels = issue2.labels ?? [];
|
|
14377
14714
|
const labelsFormatted = labels.length === 0 ? "(no labels)" : labels.map((l) => `\`${l}\``).join(", ");
|
|
14378
|
-
ctx.data.issue = { ...
|
|
14715
|
+
ctx.data.issue = { ...issue2, commentsFormatted, labelsFormatted };
|
|
14379
14716
|
ctx.data.commentTargetType = "issue";
|
|
14380
14717
|
ctx.data.commentTargetNumber = issueNumber;
|
|
14381
14718
|
};
|
|
@@ -14404,11 +14741,11 @@ var init_loadIssueStateComment = __esm({
|
|
|
14404
14741
|
if (!owner || !repo) {
|
|
14405
14742
|
throw new Error("loadIssueStateComment: ctx.config.github.owner/repo must be set");
|
|
14406
14743
|
}
|
|
14407
|
-
const
|
|
14744
|
+
const issue2 = getIssue(issueNumber, ctx.cwd);
|
|
14408
14745
|
const loaded = findStateComment(owner, repo, issueNumber, marker, ctx.cwd);
|
|
14409
14746
|
ctx.data.stateMarker = marker;
|
|
14410
|
-
ctx.data.issueIntent =
|
|
14411
|
-
ctx.data.issueTitle =
|
|
14747
|
+
ctx.data.issueIntent = issue2.body;
|
|
14748
|
+
ctx.data.issueTitle = issue2.title;
|
|
14412
14749
|
ctx.data.issueNumber = String(issueNumber);
|
|
14413
14750
|
ctx.data.issueStateComment = loaded;
|
|
14414
14751
|
ctx.data.issueStateJson = loaded ? JSON.stringify(loaded.state, null, 2) : "null";
|
|
@@ -14538,15 +14875,15 @@ var init_loadLinkedFinding = __esm({
|
|
|
14538
14875
|
if (!pr) return;
|
|
14539
14876
|
const findingNumber = resolveFindingNumber(pr);
|
|
14540
14877
|
if (!findingNumber) return;
|
|
14541
|
-
let
|
|
14878
|
+
let issue2;
|
|
14542
14879
|
try {
|
|
14543
|
-
|
|
14880
|
+
issue2 = getIssue(findingNumber, ctx.cwd);
|
|
14544
14881
|
} catch {
|
|
14545
14882
|
return;
|
|
14546
14883
|
}
|
|
14547
|
-
ctx.data.linkedFinding = `Issue #${
|
|
14884
|
+
ctx.data.linkedFinding = `Issue #${issue2.number}: ${issue2.title}
|
|
14548
14885
|
|
|
14549
|
-
${truncate(
|
|
14886
|
+
${truncate(issue2.body, FINDING_BODY_MAX_BYTES)}`;
|
|
14550
14887
|
};
|
|
14551
14888
|
}
|
|
14552
14889
|
});
|
|
@@ -14751,8 +15088,8 @@ async function writeGithubStateTextWithConfig(opts) {
|
|
|
14751
15088
|
);
|
|
14752
15089
|
}
|
|
14753
15090
|
}
|
|
14754
|
-
function jsonlLines(
|
|
14755
|
-
return
|
|
15091
|
+
function jsonlLines(text2) {
|
|
15092
|
+
return text2.split("\n").filter((line) => line.length > 0);
|
|
14756
15093
|
}
|
|
14757
15094
|
function renderJsonl(lines) {
|
|
14758
15095
|
return lines.length > 0 ? `${lines.join("\n")}
|
|
@@ -15052,14 +15389,14 @@ var init_loadTaskContext = __esm({
|
|
|
15052
15389
|
loadTaskContext = async (ctx) => {
|
|
15053
15390
|
const runId = resolveRunId();
|
|
15054
15391
|
const rawIssue = ctx.data.issue;
|
|
15055
|
-
const
|
|
15392
|
+
const issue2 = rawIssue ? {
|
|
15056
15393
|
...rawIssue,
|
|
15057
15394
|
commentsFormatted: rawIssue.commentsFormatted ?? "",
|
|
15058
15395
|
labelsFormatted: rawIssue.labelsFormatted ?? ""
|
|
15059
15396
|
} : void 0;
|
|
15060
15397
|
const taskContext = buildTaskContext({
|
|
15061
15398
|
runId,
|
|
15062
|
-
issue,
|
|
15399
|
+
issue: issue2,
|
|
15063
15400
|
conventions: ctx.data.conventions,
|
|
15064
15401
|
priorArt: typeof ctx.data.priorArt === "string" ? ctx.data.priorArt : "",
|
|
15065
15402
|
memoryContext: typeof ctx.data.memoryContext === "string" ? ctx.data.memoryContext : "",
|
|
@@ -15414,6 +15751,12 @@ var init_notifyTerminal = __esm({
|
|
|
15414
15751
|
});
|
|
15415
15752
|
|
|
15416
15753
|
// src/scripts/openAgencyModelReviewPr.ts
|
|
15754
|
+
function isDryRun(ctx) {
|
|
15755
|
+
const arg = ctx.args.dry_run ?? ctx.args.dryRun;
|
|
15756
|
+
if (arg === true) return true;
|
|
15757
|
+
if (typeof arg === "string" && ["1", "true", "yes"].includes(arg.trim().toLowerCase())) return true;
|
|
15758
|
+
return ["1", "true", "yes"].includes((process.env.KODY_DRY_RUN ?? "").trim().toLowerCase());
|
|
15759
|
+
}
|
|
15417
15760
|
function parseAgencyModelProposal(raw) {
|
|
15418
15761
|
const jsonText = stripJsonFence(raw);
|
|
15419
15762
|
let parsed;
|
|
@@ -15483,9 +15826,9 @@ function normalizeBundleFiles(ctx, bundle) {
|
|
|
15483
15826
|
});
|
|
15484
15827
|
}
|
|
15485
15828
|
function stripJsonFence(raw) {
|
|
15486
|
-
const
|
|
15487
|
-
const fence =
|
|
15488
|
-
return (fence ? fence[1] :
|
|
15829
|
+
const text2 = raw.trim();
|
|
15830
|
+
const fence = text2.match(/^```(?:json)?\s*\n([\s\S]*?)\n```$/i);
|
|
15831
|
+
return (fence ? fence[1] : text2).trim();
|
|
15489
15832
|
}
|
|
15490
15833
|
function readIssueNumber(ctx) {
|
|
15491
15834
|
const issueNumber = ctx.args.issue;
|
|
@@ -15495,9 +15838,9 @@ function readIssueNumber(ctx) {
|
|
|
15495
15838
|
return issueNumber;
|
|
15496
15839
|
}
|
|
15497
15840
|
function readRequiredJsonString(value, field) {
|
|
15498
|
-
const
|
|
15499
|
-
if (!
|
|
15500
|
-
return
|
|
15841
|
+
const text2 = readJsonString(value, field).trim();
|
|
15842
|
+
if (!text2) throw new Error(`openAgencyModelReviewPr: ${field} must be a non-empty string`);
|
|
15843
|
+
return text2;
|
|
15501
15844
|
}
|
|
15502
15845
|
function readJsonString(value, field) {
|
|
15503
15846
|
if (typeof value !== "string") throw new Error(`openAgencyModelReviewPr: ${field} must be a string`);
|
|
@@ -15577,6 +15920,16 @@ var init_openAgencyModelReviewPr = __esm({
|
|
|
15577
15920
|
const stateRepo = parseStateRepo(ctx.config);
|
|
15578
15921
|
const baseBranch = "main";
|
|
15579
15922
|
const branch = buildStatePrBranchName(sourceLabel, issueNumber, bundle.title);
|
|
15923
|
+
if (isDryRun(ctx)) {
|
|
15924
|
+
ctx.data.agencyModelReviewPr = {
|
|
15925
|
+
dryRun: true,
|
|
15926
|
+
repo: `${stateRepo.owner}/${stateRepo.repo}`,
|
|
15927
|
+
branch,
|
|
15928
|
+
base: baseBranch,
|
|
15929
|
+
files: normalizedFiles.map((file) => file.targetPath)
|
|
15930
|
+
};
|
|
15931
|
+
return;
|
|
15932
|
+
}
|
|
15580
15933
|
const baseRef = ghJson(
|
|
15581
15934
|
["api", `/repos/${stateRepo.owner}/${stateRepo.repo}/git/ref/heads/${baseBranch}`],
|
|
15582
15935
|
ctx.cwd
|
|
@@ -15689,10 +16042,10 @@ function ensureLabel2(cwd) {
|
|
|
15689
16042
|
return false;
|
|
15690
16043
|
}
|
|
15691
16044
|
}
|
|
15692
|
-
function markIssueWithReportLabel(
|
|
16045
|
+
function markIssueWithReportLabel(issue2, cwd) {
|
|
15693
16046
|
if (!ensureLabel2(cwd)) return;
|
|
15694
16047
|
try {
|
|
15695
|
-
gh(["issue", "edit", String(
|
|
16048
|
+
gh(["issue", "edit", String(issue2), "--add-label", QA_LABEL], { cwd });
|
|
15696
16049
|
} catch {
|
|
15697
16050
|
}
|
|
15698
16051
|
}
|
|
@@ -15831,13 +16184,13 @@ function isPartialEnvelope(x) {
|
|
|
15831
16184
|
function escapeRegex(s) {
|
|
15832
16185
|
return s.replace(/[-/\\^$*+?.()|[\]{}]/g, "\\$&");
|
|
15833
16186
|
}
|
|
15834
|
-
function extractFencedBlock(
|
|
16187
|
+
function extractFencedBlock(text2, label) {
|
|
15835
16188
|
const re = new RegExp(`\`\`\`${escapeRegex(label)}\\s*\\n([\\s\\S]*?)\\n\`\`\``, "m");
|
|
15836
|
-
const m = re.exec(
|
|
16189
|
+
const m = re.exec(text2);
|
|
15837
16190
|
return m ? m[1].trim() : null;
|
|
15838
16191
|
}
|
|
15839
|
-
function extractNextStateFromText(
|
|
15840
|
-
const inner = extractFencedBlock(
|
|
16192
|
+
function extractNextStateFromText(text2, fenceLabel, prevRev) {
|
|
16193
|
+
const inner = extractFencedBlock(text2, fenceLabel);
|
|
15841
16194
|
if (inner === null) {
|
|
15842
16195
|
return { error: `missing \`${fenceLabel}\` fenced block` };
|
|
15843
16196
|
}
|
|
@@ -15956,15 +16309,15 @@ var init_parseJobStateFromAgentResult = __esm({
|
|
|
15956
16309
|
});
|
|
15957
16310
|
|
|
15958
16311
|
// src/scripts/parseReproOutput.ts
|
|
15959
|
-
function extractTestPath(
|
|
15960
|
-
const m =
|
|
16312
|
+
function extractTestPath(text2) {
|
|
16313
|
+
const m = text2.match(/^[\s>*_#`~-]*TEST_PATH[\s>*_#`~-]*\s*:\s*(.+?)\s*$/im);
|
|
15961
16314
|
if (!m) return "";
|
|
15962
16315
|
return stripMarkdownEmphasis2(m[1] ?? "");
|
|
15963
16316
|
}
|
|
15964
|
-
function extractFailureSignatureBlock(
|
|
15965
|
-
const startIdx =
|
|
16317
|
+
function extractFailureSignatureBlock(text2) {
|
|
16318
|
+
const startIdx = text2.search(/(?:^|\n)[ \t]*FAILURE_SIGNATURE\s*:[ \t]*/i);
|
|
15966
16319
|
if (startIdx === -1) return "";
|
|
15967
|
-
const afterMarker =
|
|
16320
|
+
const afterMarker = text2.slice(startIdx).replace(/^[\s\S]*?FAILURE_SIGNATURE\s*:[ \t]*\n?/i, "");
|
|
15968
16321
|
const stopRe = /(?:^|\n)[ \t]*(?:COMMIT_MSG|PR_SUMMARY|TEST_PATH)\s*:/i;
|
|
15969
16322
|
const stopIdx = afterMarker.search(stopRe);
|
|
15970
16323
|
let block = stopIdx === -1 ? afterMarker : afterMarker.slice(0, stopIdx);
|
|
@@ -15981,14 +16334,14 @@ function normalizeFailureSignatureBlock(block) {
|
|
|
15981
16334
|
const jsonObject = extractFirstJsonObject(s);
|
|
15982
16335
|
return jsonObject || s;
|
|
15983
16336
|
}
|
|
15984
|
-
function extractFirstJsonObject(
|
|
15985
|
-
const start =
|
|
16337
|
+
function extractFirstJsonObject(text2) {
|
|
16338
|
+
const start = text2.indexOf("{");
|
|
15986
16339
|
if (start === -1) return "";
|
|
15987
16340
|
let depth = 0;
|
|
15988
16341
|
let inString = false;
|
|
15989
16342
|
let escaped = false;
|
|
15990
|
-
for (let i = start; i <
|
|
15991
|
-
const ch =
|
|
16343
|
+
for (let i = start; i < text2.length; i++) {
|
|
16344
|
+
const ch = text2[i];
|
|
15992
16345
|
if (inString) {
|
|
15993
16346
|
if (escaped) {
|
|
15994
16347
|
escaped = false;
|
|
@@ -16005,7 +16358,7 @@ function extractFirstJsonObject(text) {
|
|
|
16005
16358
|
depth++;
|
|
16006
16359
|
} else if (ch === "}") {
|
|
16007
16360
|
depth--;
|
|
16008
|
-
if (depth === 0) return
|
|
16361
|
+
if (depth === 0) return text2.slice(start, i + 1).trim();
|
|
16009
16362
|
}
|
|
16010
16363
|
}
|
|
16011
16364
|
return "";
|
|
@@ -16031,9 +16384,9 @@ var init_parseReproOutput = __esm({
|
|
|
16031
16384
|
"use strict";
|
|
16032
16385
|
parseReproOutput = async (ctx, _profile, agentResult) => {
|
|
16033
16386
|
if (!agentResult || ctx.data.agentDone === false) return;
|
|
16034
|
-
const
|
|
16035
|
-
const testPath = extractTestPath(
|
|
16036
|
-
const signatureRaw = extractFailureSignatureBlock(
|
|
16387
|
+
const text2 = agentResult.finalText ?? "";
|
|
16388
|
+
const testPath = extractTestPath(text2);
|
|
16389
|
+
const signatureRaw = extractFailureSignatureBlock(text2);
|
|
16037
16390
|
if (!testPath) {
|
|
16038
16391
|
downgrade(ctx, "reproduce missing TEST_PATH line in final message");
|
|
16039
16392
|
return;
|
|
@@ -16212,8 +16565,8 @@ var init_planTaskJobs = __esm({
|
|
|
16212
16565
|
ctx.output.reason = "planTaskJobs requires --issue";
|
|
16213
16566
|
return;
|
|
16214
16567
|
}
|
|
16215
|
-
const
|
|
16216
|
-
const specs = parseTaskJobSpecs(
|
|
16568
|
+
const issue2 = ctx.data.issue;
|
|
16569
|
+
const specs = parseTaskJobSpecs(issue2?.body ?? "");
|
|
16217
16570
|
if (specs.length === 0) {
|
|
16218
16571
|
ctx.skipAgent = true;
|
|
16219
16572
|
ctx.output.exitCode = 64;
|
|
@@ -16543,8 +16896,8 @@ var init_promoteQaGoal = __esm({
|
|
|
16543
16896
|
}
|
|
16544
16897
|
let report;
|
|
16545
16898
|
try {
|
|
16546
|
-
const
|
|
16547
|
-
const reportComment = [...
|
|
16899
|
+
const issue2 = getIssue(issueNum, ctx.cwd);
|
|
16900
|
+
const reportComment = [...issue2.comments].reverse().find((c) => c.body.includes(REPORT_JSON_OPEN2));
|
|
16548
16901
|
if (!reportComment) {
|
|
16549
16902
|
ctx.output.exitCode = 3;
|
|
16550
16903
|
ctx.output.reason = `no QA report (${REPORT_JSON_OPEN2} \u2026) found on issue #${issueNum}`;
|
|
@@ -16611,9 +16964,9 @@ function latestResult(raw, agentResult) {
|
|
|
16611
16964
|
function recordField6(value) {
|
|
16612
16965
|
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
16613
16966
|
}
|
|
16614
|
-
function resolveDotted(root,
|
|
16615
|
-
if (!
|
|
16616
|
-
return
|
|
16967
|
+
function resolveDotted(root, path52) {
|
|
16968
|
+
if (!path52) return void 0;
|
|
16969
|
+
return path52.split(".").reduce((value, key) => recordField6(value)?.[key], root);
|
|
16617
16970
|
}
|
|
16618
16971
|
function stringValue4(value) {
|
|
16619
16972
|
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
@@ -16655,13 +17008,7 @@ var init_publishReport = __esm({
|
|
|
16655
17008
|
...publication.reviewArea ? { reviewArea: publication.reviewArea } : {}
|
|
16656
17009
|
});
|
|
16657
17010
|
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
|
-
);
|
|
17011
|
+
writeStateText(ctx.config, ctx.cwd, `reports/${slug}/runs/${runId}.md`, markdown, `chore(reports): add ${slug} run`);
|
|
16665
17012
|
};
|
|
16666
17013
|
}
|
|
16667
17014
|
});
|
|
@@ -17369,15 +17716,15 @@ var init_runFlow = __esm({
|
|
|
17369
17716
|
init_issue();
|
|
17370
17717
|
runFlow = async (ctx) => {
|
|
17371
17718
|
const issueNumber = ctx.args.issue;
|
|
17372
|
-
const
|
|
17719
|
+
const issue2 = getIssue(issueNumber, ctx.cwd);
|
|
17373
17720
|
const cfgCtx = ctx.config.issueContext ?? {};
|
|
17374
17721
|
const commentsFormatted = formatIssueComments(
|
|
17375
|
-
|
|
17722
|
+
issue2.comments,
|
|
17376
17723
|
cfgCtx.commentLimit ?? DEFAULT_COMMENT_LIMIT,
|
|
17377
17724
|
cfgCtx.commentMaxBytes ?? DEFAULT_COMMENT_MAX_BYTES
|
|
17378
17725
|
);
|
|
17379
|
-
ctx.data.issue = { ...
|
|
17380
|
-
if (
|
|
17726
|
+
ctx.data.issue = { ...issue2, commentsFormatted };
|
|
17727
|
+
if (issue2.isPullRequest) {
|
|
17381
17728
|
ctx.data.commentTargetType = "pr";
|
|
17382
17729
|
ctx.data.commentTargetNumber = issueNumber;
|
|
17383
17730
|
ctx.skipAgent = true;
|
|
@@ -17401,7 +17748,7 @@ var init_runFlow = __esm({
|
|
|
17401
17748
|
}
|
|
17402
17749
|
const branchInfo = ensureFeatureBranch(
|
|
17403
17750
|
issueNumber,
|
|
17404
|
-
|
|
17751
|
+
issue2.title,
|
|
17405
17752
|
ctx.config.git.defaultBranch,
|
|
17406
17753
|
ctx.cwd,
|
|
17407
17754
|
base ?? void 0
|
|
@@ -17650,8 +17997,8 @@ async function flyCreateApp(name, orgSlug, token) {
|
|
|
17650
17997
|
});
|
|
17651
17998
|
if (res.status === 422) return;
|
|
17652
17999
|
if (!res.ok) {
|
|
17653
|
-
const
|
|
17654
|
-
throw new Error(`createApp ${name}: ${res.status} ${
|
|
18000
|
+
const text2 = await res.text().catch(() => "");
|
|
18001
|
+
throw new Error(`createApp ${name}: ${res.status} ${text2.slice(0, 200)}`);
|
|
17655
18002
|
}
|
|
17656
18003
|
}
|
|
17657
18004
|
async function flyAllocateSharedIps(appName, token) {
|
|
@@ -17758,9 +18105,9 @@ async function flyCreatePreviewMachine(args, token) {
|
|
|
17758
18105
|
const { id } = await res.json();
|
|
17759
18106
|
return id;
|
|
17760
18107
|
}
|
|
17761
|
-
const
|
|
17762
|
-
lastErr = new Error(`createPreviewMachine ${res.status}: ${
|
|
17763
|
-
if (!/MANIFEST_UNKNOWN|manifest unknown/i.test(
|
|
18108
|
+
const text2 = await res.text().catch(() => "");
|
|
18109
|
+
lastErr = new Error(`createPreviewMachine ${res.status}: ${text2.slice(0, 300)}`);
|
|
18110
|
+
if (!/MANIFEST_UNKNOWN|manifest unknown/i.test(text2)) break;
|
|
17764
18111
|
await new Promise((r) => setTimeout(r, 2e3 * (attempt + 1)));
|
|
17765
18112
|
}
|
|
17766
18113
|
throw lastErr ?? new Error("createPreviewMachine failed (unknown)");
|
|
@@ -18199,8 +18546,8 @@ var init_setCommentTarget = __esm({
|
|
|
18199
18546
|
|
|
18200
18547
|
// src/scripts/setLifecycleLabel.ts
|
|
18201
18548
|
function resolveTargetNumber(args) {
|
|
18202
|
-
const
|
|
18203
|
-
if (typeof
|
|
18549
|
+
const issue2 = args.issue;
|
|
18550
|
+
if (typeof issue2 === "number" && Number.isFinite(issue2)) return issue2;
|
|
18204
18551
|
const pr = args.pr;
|
|
18205
18552
|
if (typeof pr === "number" && Number.isFinite(pr)) return pr;
|
|
18206
18553
|
return void 0;
|
|
@@ -18415,9 +18762,10 @@ var init_syncFlow = __esm({
|
|
|
18415
18762
|
});
|
|
18416
18763
|
|
|
18417
18764
|
// src/scripts/validateAgencyModelProposal.ts
|
|
18418
|
-
|
|
18765
|
+
import * as path43 from "path";
|
|
18766
|
+
function validateModelBundle(bundle, expectedKind, options = {}) {
|
|
18419
18767
|
const failures = [];
|
|
18420
|
-
validateOneModel(bundle.model, bundle.files, "model", true, failures, expectedKind);
|
|
18768
|
+
validateOneModel(bundle.model, bundle.files, "model", true, failures, expectedKind, options);
|
|
18421
18769
|
return failures;
|
|
18422
18770
|
}
|
|
18423
18771
|
function readExpectedModelKind(args) {
|
|
@@ -18427,7 +18775,7 @@ function readExpectedModelKind(args) {
|
|
|
18427
18775
|
"validateAgencyModelProposal: with.modelKind must be intent, operation, agent, capability, goal, agentLoop, or workflow"
|
|
18428
18776
|
);
|
|
18429
18777
|
}
|
|
18430
|
-
function validateOneModel(rawModel, files, label, strictSingleModel, failures, expectedKind) {
|
|
18778
|
+
function validateOneModel(rawModel, files, label, strictSingleModel, failures, expectedKind, options = {}) {
|
|
18431
18779
|
if (!rawModel || typeof rawModel !== "object" || Array.isArray(rawModel)) {
|
|
18432
18780
|
failures.push(`${label} must be an object`);
|
|
18433
18781
|
return;
|
|
@@ -18445,11 +18793,11 @@ function validateOneModel(rawModel, files, label, strictSingleModel, failures, e
|
|
|
18445
18793
|
for (const doc of REQUIRED_DOCS[kind]) {
|
|
18446
18794
|
if (!docsUsed.includes(doc)) failures.push(`${label} docsUsed missing ${doc}`);
|
|
18447
18795
|
}
|
|
18448
|
-
validateFilesForKind(kind, slug, files, strictSingleModel, failures);
|
|
18796
|
+
validateFilesForKind(kind, slug, files, strictSingleModel, failures, options);
|
|
18449
18797
|
validateModelShape(kind, model, files, slug, failures);
|
|
18450
18798
|
}
|
|
18451
18799
|
}
|
|
18452
|
-
function validateFilesForKind(kind, slug, files, strictSingleModel, failures) {
|
|
18800
|
+
function validateFilesForKind(kind, slug, files, strictSingleModel, failures, options) {
|
|
18453
18801
|
const paths = files.map((file) => normalizeBundlePath(file.path));
|
|
18454
18802
|
if (paths.some((filePath) => filePath === "implementations" || filePath.startsWith("implementations/"))) {
|
|
18455
18803
|
failures.push("files must not use obsolete implementation storage");
|
|
@@ -18496,6 +18844,7 @@ function validateFilesForKind(kind, slug, files, strictSingleModel, failures) {
|
|
|
18496
18844
|
}
|
|
18497
18845
|
if (kind === "workflow") {
|
|
18498
18846
|
requirePath(paths, `capabilities/${slug}/profile.json`, "workflow capability profile", failures);
|
|
18847
|
+
requirePath(paths, `capabilities/${slug}/capability.md`, "workflow capability body", failures);
|
|
18499
18848
|
const profile = parseJsonFile(files, `capabilities/${slug}/profile.json`, failures);
|
|
18500
18849
|
if (profile) {
|
|
18501
18850
|
if (profile.capabilityKind !== void 0) {
|
|
@@ -18505,6 +18854,30 @@ function validateFilesForKind(kind, slug, files, strictSingleModel, failures) {
|
|
|
18505
18854
|
const hasTopLevelSteps = Array.isArray(profile.steps) && profile.steps.length > 0;
|
|
18506
18855
|
if (!hasWorkflowObject && !hasTopLevelSteps) {
|
|
18507
18856
|
failures.push("workflow profile must include workflow object or top-level steps");
|
|
18857
|
+
} else {
|
|
18858
|
+
const workflow = hasWorkflowObject ? profile.workflow : { steps: profile.steps, ...profile.startAt !== void 0 ? { startAt: profile.startAt } : {} };
|
|
18859
|
+
const known = options.capabilityRoot ? getCapabilityRoots(options.capabilityRoot).flatMap((root) => listCapabilityFolderSlugs(root)) : [];
|
|
18860
|
+
const uniqueKnown = [...new Set(known)];
|
|
18861
|
+
const capabilityInputs = /* @__PURE__ */ new Map();
|
|
18862
|
+
if (options.capabilityRoot) {
|
|
18863
|
+
for (const capability of uniqueKnown) {
|
|
18864
|
+
const inputs = getCapabilityActionInputs(capability, options.capabilityRoot);
|
|
18865
|
+
if (inputs) {
|
|
18866
|
+
capabilityInputs.set(
|
|
18867
|
+
capability,
|
|
18868
|
+
new Set(inputs.flatMap((input) => [input.name, input.flag.replace(/^--/, "")]))
|
|
18869
|
+
);
|
|
18870
|
+
}
|
|
18871
|
+
}
|
|
18872
|
+
}
|
|
18873
|
+
failures.push(
|
|
18874
|
+
...formatWorkflowValidationIssues(
|
|
18875
|
+
validateWorkflow(workflow, {
|
|
18876
|
+
...uniqueKnown.length > 0 ? { knownCapabilities: new Set(uniqueKnown) } : {},
|
|
18877
|
+
...capabilityInputs.size > 0 ? { capabilityInputs } : {}
|
|
18878
|
+
})
|
|
18879
|
+
)
|
|
18880
|
+
);
|
|
18508
18881
|
}
|
|
18509
18882
|
}
|
|
18510
18883
|
}
|
|
@@ -18692,6 +19065,9 @@ var REQUIRED_DOCS, validateAgencyModelProposal;
|
|
|
18692
19065
|
var init_validateAgencyModelProposal = __esm({
|
|
18693
19066
|
"src/scripts/validateAgencyModelProposal.ts"() {
|
|
18694
19067
|
"use strict";
|
|
19068
|
+
init_capabilityFolders();
|
|
19069
|
+
init_registry();
|
|
19070
|
+
init_workflowValidation();
|
|
18695
19071
|
init_openAgencyModelReviewPr();
|
|
18696
19072
|
REQUIRED_DOCS = {
|
|
18697
19073
|
intent: ["docs/intents.md", "docs/engine-company.md"],
|
|
@@ -18707,7 +19083,9 @@ var init_validateAgencyModelProposal = __esm({
|
|
|
18707
19083
|
const raw = String(ctx.data.prSummary ?? "");
|
|
18708
19084
|
const bundle = parseAgencyModelProposal(raw);
|
|
18709
19085
|
const expectedKind = readExpectedModelKind(args);
|
|
18710
|
-
const failures = validateModelBundle(bundle, expectedKind
|
|
19086
|
+
const failures = validateModelBundle(bundle, expectedKind, {
|
|
19087
|
+
capabilityRoot: path43.join(ctx.cwd, ".kody", "capabilities")
|
|
19088
|
+
});
|
|
18711
19089
|
if (failures.length > 0) {
|
|
18712
19090
|
throw new Error(`validateAgencyModelProposal: ${failures.join("; ")}`);
|
|
18713
19091
|
}
|
|
@@ -19266,9 +19644,9 @@ var init_writeAgentRunSummary = __esm({
|
|
|
19266
19644
|
const summaryPath = process.env.GITHUB_STEP_SUMMARY;
|
|
19267
19645
|
if (!summaryPath) return;
|
|
19268
19646
|
const implementation = profile.name;
|
|
19269
|
-
const
|
|
19647
|
+
const issue2 = ctx.args.issue;
|
|
19270
19648
|
const pr = ctx.args.pr;
|
|
19271
|
-
const target =
|
|
19649
|
+
const target = issue2 ? `issue #${issue2}` : pr ? `PR #${pr}` : "(unknown)";
|
|
19272
19650
|
const prUrl = ctx.output.prUrl;
|
|
19273
19651
|
const exitCode = ctx.output.exitCode ?? 0;
|
|
19274
19652
|
const reason = ctx.output.reason;
|
|
@@ -19608,38 +19986,38 @@ import { execFileSync as execFileSync24 } from "child_process";
|
|
|
19608
19986
|
import * as crypto3 from "crypto";
|
|
19609
19987
|
import * as fs45 from "fs";
|
|
19610
19988
|
import * as os7 from "os";
|
|
19611
|
-
import * as
|
|
19989
|
+
import * as path44 from "path";
|
|
19612
19990
|
function writeLocalFile(cwd, relativePath, content) {
|
|
19613
|
-
const fullPath =
|
|
19614
|
-
fs45.mkdirSync(
|
|
19991
|
+
const fullPath = path44.join(cwd, relativePath);
|
|
19992
|
+
fs45.mkdirSync(path44.dirname(fullPath), { recursive: true });
|
|
19615
19993
|
fs45.writeFileSync(fullPath, content);
|
|
19616
19994
|
}
|
|
19617
19995
|
function copyPath(source, target) {
|
|
19618
19996
|
const st = fs45.lstatSync(source);
|
|
19619
19997
|
fs45.rmSync(target, { recursive: true, force: true });
|
|
19620
19998
|
if (st.isSymbolicLink()) return;
|
|
19621
|
-
fs45.mkdirSync(
|
|
19999
|
+
fs45.mkdirSync(path44.dirname(target), { recursive: true });
|
|
19622
20000
|
fs45.cpSync(source, target, { recursive: true, force: true });
|
|
19623
20001
|
}
|
|
19624
20002
|
function overlayDirectoryChildren(cwd, sourceDir, localDir) {
|
|
19625
20003
|
if (!fs45.existsSync(sourceDir)) return;
|
|
19626
20004
|
for (const entry of fs45.readdirSync(sourceDir, { withFileTypes: true })) {
|
|
19627
|
-
const source =
|
|
19628
|
-
const target =
|
|
20005
|
+
const source = path44.join(sourceDir, entry.name);
|
|
20006
|
+
const target = path44.join(cwd, localDir, entry.name);
|
|
19629
20007
|
copyPath(source, target);
|
|
19630
20008
|
}
|
|
19631
20009
|
}
|
|
19632
20010
|
function hydrateStateWorkspace(config, cwd) {
|
|
19633
20011
|
if (process.env.VITEST && process.env[TEST_FETCH_ENV] !== "1") return;
|
|
19634
20012
|
const parsed = parseStateRepo(config);
|
|
19635
|
-
const hydrateKey = `${
|
|
20013
|
+
const hydrateKey = `${path44.resolve(cwd)}|${parsed.owner}/${parsed.repo}|${parsed.basePath}|${parsed.branch}`;
|
|
19636
20014
|
if (hydratedWorkspaces.has(hydrateKey)) return;
|
|
19637
20015
|
const snapshotRoot = fetchStateSnapshot(parsed);
|
|
19638
20016
|
for (const mapping of DIR_MAPPINGS) {
|
|
19639
|
-
overlayDirectoryChildren(cwd,
|
|
20017
|
+
overlayDirectoryChildren(cwd, path44.join(snapshotRoot, mapping.stateDir), mapping.localDir);
|
|
19640
20018
|
}
|
|
19641
20019
|
for (const mapping of FILE_MAPPINGS) {
|
|
19642
|
-
const source =
|
|
20020
|
+
const source = path44.join(snapshotRoot, mapping.statePath);
|
|
19643
20021
|
if (fs45.existsSync(source) && !fs45.lstatSync(source).isSymbolicLink() && fs45.statSync(source).isFile()) {
|
|
19644
20022
|
writeLocalFile(cwd, mapping.localPath, fs45.readFileSync(source, "utf-8"));
|
|
19645
20023
|
}
|
|
@@ -19647,11 +20025,11 @@ function hydrateStateWorkspace(config, cwd) {
|
|
|
19647
20025
|
hydratedWorkspaces.add(hydrateKey);
|
|
19648
20026
|
}
|
|
19649
20027
|
function fetchStateSnapshot(parsed) {
|
|
19650
|
-
const cacheDir =
|
|
20028
|
+
const cacheDir = path44.join(cacheRoot2(), cacheKey3(parsed));
|
|
19651
20029
|
const url = `https://github.com/${parsed.owner}/${parsed.repo}.git`;
|
|
19652
20030
|
try {
|
|
19653
|
-
fs45.mkdirSync(
|
|
19654
|
-
if (!fs45.existsSync(
|
|
20031
|
+
fs45.mkdirSync(path44.dirname(cacheDir), { recursive: true });
|
|
20032
|
+
if (!fs45.existsSync(path44.join(cacheDir, ".git"))) {
|
|
19655
20033
|
fs45.rmSync(cacheDir, { recursive: true, force: true });
|
|
19656
20034
|
runGit3(["clone", "--no-checkout", "--filter=blob:none", url, cacheDir]);
|
|
19657
20035
|
}
|
|
@@ -19667,10 +20045,10 @@ function fetchStateSnapshot(parsed) {
|
|
|
19667
20045
|
`stateWorkspace: failed to fetch ${parsed.owner}/${parsed.repo}:${parsed.basePath}@${parsed.branch}: ${msg}`
|
|
19668
20046
|
);
|
|
19669
20047
|
}
|
|
19670
|
-
return
|
|
20048
|
+
return path44.join(cacheDir, parsed.basePath);
|
|
19671
20049
|
}
|
|
19672
20050
|
function cacheRoot2() {
|
|
19673
|
-
return process.env[CACHE_ENV2]?.trim() ||
|
|
20051
|
+
return process.env[CACHE_ENV2]?.trim() || path44.join(os7.homedir(), ".cache", "kody", "state-repo");
|
|
19674
20052
|
}
|
|
19675
20053
|
function cacheKey3(parsed) {
|
|
19676
20054
|
return crypto3.createHash("sha256").update(`${parsed.owner}/${parsed.repo}#${parsed.branch}#${parsed.basePath}`).digest("hex").slice(0, 24);
|
|
@@ -19710,16 +20088,16 @@ var init_stateWorkspace = __esm({
|
|
|
19710
20088
|
"use strict";
|
|
19711
20089
|
init_stateRepo();
|
|
19712
20090
|
DIR_MAPPINGS = [
|
|
19713
|
-
{ stateDir: "capabilities", localDir:
|
|
19714
|
-
{ stateDir: "agents", localDir:
|
|
19715
|
-
{ stateDir: "context", localDir:
|
|
19716
|
-
{ stateDir: "memory", localDir:
|
|
20091
|
+
{ stateDir: "capabilities", localDir: path44.join(".kody", "capabilities") },
|
|
20092
|
+
{ stateDir: "agents", localDir: path44.join(".kody", "agents") },
|
|
20093
|
+
{ stateDir: "context", localDir: path44.join(".kody", "context") },
|
|
20094
|
+
{ stateDir: "memory", localDir: path44.join(".kody", "memory") }
|
|
19717
20095
|
];
|
|
19718
20096
|
FILE_MAPPINGS = [
|
|
19719
|
-
{ statePath: "instructions.md", localPath:
|
|
19720
|
-
{ statePath: "system-prompt.md", localPath:
|
|
19721
|
-
{ statePath: "variables.json", localPath:
|
|
19722
|
-
{ statePath: "secrets.enc", localPath:
|
|
20097
|
+
{ statePath: "instructions.md", localPath: path44.join(".kody", "instructions.md") },
|
|
20098
|
+
{ statePath: "system-prompt.md", localPath: path44.join(".kody", "system-prompt.md") },
|
|
20099
|
+
{ statePath: "variables.json", localPath: path44.join(".kody", "variables.json") },
|
|
20100
|
+
{ statePath: "secrets.enc", localPath: path44.join(".kody", "secrets.enc") }
|
|
19723
20101
|
];
|
|
19724
20102
|
CACHE_ENV2 = "KODY_STATE_REPO_CACHE";
|
|
19725
20103
|
TEST_FETCH_ENV = "KODY_STATE_WORKSPACE_FETCH_FOR_TESTS";
|
|
@@ -19795,7 +20173,7 @@ var init_tools = __esm({
|
|
|
19795
20173
|
import { spawn as spawn7 } from "child_process";
|
|
19796
20174
|
import * as fs46 from "fs";
|
|
19797
20175
|
import * as os8 from "os";
|
|
19798
|
-
import * as
|
|
20176
|
+
import * as path45 from "path";
|
|
19799
20177
|
function isMutatingPostflight(scriptName) {
|
|
19800
20178
|
return MUTATING_POSTFLIGHTS.has(scriptName ?? "");
|
|
19801
20179
|
}
|
|
@@ -19823,9 +20201,9 @@ function collectShellSideChannels(ctx, stdout) {
|
|
|
19823
20201
|
}
|
|
19824
20202
|
}
|
|
19825
20203
|
function operatorRequestBlock(why) {
|
|
19826
|
-
const
|
|
19827
|
-
if (!
|
|
19828
|
-
const safe =
|
|
20204
|
+
const text2 = why.trim();
|
|
20205
|
+
if (!text2) return null;
|
|
20206
|
+
const safe = text2.replace(/-{3,}\s*END UNTRUSTED INPUT\s*-{3,}/gi, "[END UNTRUSTED INPUT]");
|
|
19829
20207
|
return [
|
|
19830
20208
|
"## The request that triggered this run",
|
|
19831
20209
|
"",
|
|
@@ -20018,7 +20396,7 @@ async function runImplementation(profileName, input) {
|
|
|
20018
20396
|
const jobWhyBlock = typeof ctx.data.jobWhy === "string" ? operatorRequestBlock(ctx.data.jobWhy) : null;
|
|
20019
20397
|
const jobRefBlock = jobReferenceBlock(profileName, profile, ctx.data);
|
|
20020
20398
|
const invokeAgent = async (prompt) => {
|
|
20021
|
-
const externalPlugins = (profile.claudeCode.plugins ?? []).map((p) =>
|
|
20399
|
+
const externalPlugins = (profile.claudeCode.plugins ?? []).map((p) => path45.isAbsolute(p) ? p : path45.resolve(profile.dir, p)).filter((p) => p.length > 0);
|
|
20022
20400
|
const syntheticPath = ctx.data.syntheticPluginPath;
|
|
20023
20401
|
const pluginPaths = [...externalPlugins, ...syntheticPath ? [syntheticPath] : []];
|
|
20024
20402
|
const agents = loadSubagents(profile);
|
|
@@ -20267,7 +20645,8 @@ async function runImplementation(profileName, input) {
|
|
|
20267
20645
|
nextDispatch: ctx.output.nextDispatch,
|
|
20268
20646
|
nextJob: ctx.output.nextJob,
|
|
20269
20647
|
afterNextJob: ctx.output.afterNextJob,
|
|
20270
|
-
taskState: ctx.data.taskState
|
|
20648
|
+
taskState: ctx.data.taskState,
|
|
20649
|
+
capabilityResults: Array.isArray(ctx.data.capabilityResults) ? ctx.data.capabilityResults : void 0
|
|
20271
20650
|
});
|
|
20272
20651
|
} catch (err) {
|
|
20273
20652
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -20455,13 +20834,13 @@ function clearStampedLifecycleLabels(profile, ctx) {
|
|
|
20455
20834
|
function resolveProfilePath(profileName) {
|
|
20456
20835
|
const found = resolveImplementation(profileName);
|
|
20457
20836
|
if (found) return found;
|
|
20458
|
-
const here =
|
|
20837
|
+
const here = path45.dirname(new URL(import.meta.url).pathname);
|
|
20459
20838
|
const candidates = [
|
|
20460
|
-
|
|
20839
|
+
path45.join(here, "implementations", profileName, "profile.json"),
|
|
20461
20840
|
// same-dir sibling (dev)
|
|
20462
|
-
|
|
20841
|
+
path45.join(here, "..", "implementations", profileName, "profile.json"),
|
|
20463
20842
|
// up one (prod: dist/bin → dist/implementations)
|
|
20464
|
-
|
|
20843
|
+
path45.join(here, "..", "src", "implementations", profileName, "profile.json")
|
|
20465
20844
|
// fallback
|
|
20466
20845
|
];
|
|
20467
20846
|
for (const c of candidates) {
|
|
@@ -20580,7 +20959,7 @@ function resolveShellTimeoutMs(entry) {
|
|
|
20580
20959
|
}
|
|
20581
20960
|
async function runShellEntry(entry, ctx, profile) {
|
|
20582
20961
|
const shellName = entry.shell;
|
|
20583
|
-
const shellPath =
|
|
20962
|
+
const shellPath = path45.join(profile.dir, shellName);
|
|
20584
20963
|
if (!fs46.existsSync(shellPath)) {
|
|
20585
20964
|
ctx.skipAgent = true;
|
|
20586
20965
|
ctx.output.exitCode = 99;
|
|
@@ -20588,7 +20967,7 @@ async function runShellEntry(entry, ctx, profile) {
|
|
|
20588
20967
|
return;
|
|
20589
20968
|
}
|
|
20590
20969
|
const positional = entry.with ? Object.values(entry.with).map((v) => String(v)) : [];
|
|
20591
|
-
const outputFile =
|
|
20970
|
+
const outputFile = path45.join(
|
|
20592
20971
|
os8.tmpdir(),
|
|
20593
20972
|
`kody-shell-output-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`
|
|
20594
20973
|
);
|
|
@@ -20756,6 +21135,68 @@ var init_executor = __esm({
|
|
|
20756
21135
|
}
|
|
20757
21136
|
});
|
|
20758
21137
|
|
|
21138
|
+
// src/workflowRunState.ts
|
|
21139
|
+
function workflowRunStatePath(workflowId, runId) {
|
|
21140
|
+
if (!SAFE_ID.test(workflowId)) throw new Error(`invalid workflow id ${workflowId}`);
|
|
21141
|
+
if (!SAFE_ID.test(runId)) throw new Error(`invalid workflow run id ${runId}`);
|
|
21142
|
+
return `workflows/${workflowId}/runs/${runId}.json`;
|
|
21143
|
+
}
|
|
21144
|
+
function parseWorkflowRunState(raw) {
|
|
21145
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return null;
|
|
21146
|
+
const state = raw;
|
|
21147
|
+
if (state.status !== "running" && state.status !== "blocked" && state.status !== "failed" && state.status !== "done")
|
|
21148
|
+
return null;
|
|
21149
|
+
const completedStepIds = Array.isArray(state.completedStepIds) ? state.completedStepIds.filter((value) => typeof value === "string") : [];
|
|
21150
|
+
const transitionCounts = state.transitionCounts && typeof state.transitionCounts === "object" && !Array.isArray(state.transitionCounts) ? Object.fromEntries(
|
|
21151
|
+
Object.entries(state.transitionCounts).filter(
|
|
21152
|
+
(entry) => typeof entry[1] === "number" && Number.isInteger(entry[1]) && entry[1] >= 0
|
|
21153
|
+
)
|
|
21154
|
+
) : {};
|
|
21155
|
+
const facts = state.facts && typeof state.facts === "object" && !Array.isArray(state.facts) ? state.facts : {};
|
|
21156
|
+
const evidenceEntries = state.evidence && typeof state.evidence === "object" && !Array.isArray(state.evidence) ? Object.entries(state.evidence).filter((entry) => typeof entry[1] === "boolean") : [];
|
|
21157
|
+
const artifacts = Array.isArray(state.artifacts) ? state.artifacts.filter(
|
|
21158
|
+
(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")
|
|
21159
|
+
) : [];
|
|
21160
|
+
return {
|
|
21161
|
+
status: state.status,
|
|
21162
|
+
...typeof state.currentStepId === "string" ? { currentStepId: state.currentStepId } : {},
|
|
21163
|
+
completedStepIds,
|
|
21164
|
+
transitionCounts,
|
|
21165
|
+
facts: { ...facts },
|
|
21166
|
+
evidence: Object.fromEntries(evidenceEntries),
|
|
21167
|
+
artifacts: artifacts.map((artifact) => ({ ...artifact })),
|
|
21168
|
+
...typeof state.blocker === "string" ? { blocker: state.blocker } : {}
|
|
21169
|
+
};
|
|
21170
|
+
}
|
|
21171
|
+
function readWorkflowRunState(config, cwd, workflowId, runId) {
|
|
21172
|
+
const file = readStateText(config, cwd, workflowRunStatePath(workflowId, runId));
|
|
21173
|
+
if (!file) return null;
|
|
21174
|
+
try {
|
|
21175
|
+
return parseWorkflowRunState(JSON.parse(file.content));
|
|
21176
|
+
} catch {
|
|
21177
|
+
return null;
|
|
21178
|
+
}
|
|
21179
|
+
}
|
|
21180
|
+
function writeWorkflowRunState(config, cwd, workflowId, runId, state) {
|
|
21181
|
+
const path52 = workflowRunStatePath(workflowId, runId);
|
|
21182
|
+
upsertStateText(
|
|
21183
|
+
config,
|
|
21184
|
+
cwd,
|
|
21185
|
+
path52,
|
|
21186
|
+
`${JSON.stringify(state, null, 2)}
|
|
21187
|
+
`,
|
|
21188
|
+
`chore(workflows): update ${workflowId} run ${runId}`
|
|
21189
|
+
);
|
|
21190
|
+
}
|
|
21191
|
+
var SAFE_ID;
|
|
21192
|
+
var init_workflowRunState = __esm({
|
|
21193
|
+
"src/workflowRunState.ts"() {
|
|
21194
|
+
"use strict";
|
|
21195
|
+
init_stateRepo();
|
|
21196
|
+
SAFE_ID = /^[a-z0-9][a-z0-9_-]{0,79}$/;
|
|
21197
|
+
}
|
|
21198
|
+
});
|
|
21199
|
+
|
|
20759
21200
|
// src/job.ts
|
|
20760
21201
|
var job_exports = {};
|
|
20761
21202
|
__export(job_exports, {
|
|
@@ -20768,7 +21209,7 @@ __export(job_exports, {
|
|
|
20768
21209
|
stableJobKey: () => stableJobKey,
|
|
20769
21210
|
validateJob: () => validateJob
|
|
20770
21211
|
});
|
|
20771
|
-
import * as
|
|
21212
|
+
import * as path46 from "path";
|
|
20772
21213
|
function newJobId(flavor) {
|
|
20773
21214
|
localJobSeq += 1;
|
|
20774
21215
|
const runId = process.env.GITHUB_RUN_ID;
|
|
@@ -20800,6 +21241,8 @@ function validateJob(input) {
|
|
|
20800
21241
|
target: typeof j.target === "number" ? j.target : void 0,
|
|
20801
21242
|
cliArgs: j.cliArgs ?? {},
|
|
20802
21243
|
workflowFacts: j.workflowFacts && typeof j.workflowFacts === "object" && !Array.isArray(j.workflowFacts) ? j.workflowFacts : void 0,
|
|
21244
|
+
workflowState: parseWorkflowRunState(j.workflowState) ?? void 0,
|
|
21245
|
+
workflowRunId: typeof j.workflowRunId === "string" && j.workflowRunId.trim() ? j.workflowRunId.trim() : void 0,
|
|
20803
21246
|
evidence: parseJobEvidence(j),
|
|
20804
21247
|
flavor: j.flavor,
|
|
20805
21248
|
force: j.force === true,
|
|
@@ -20834,7 +21277,7 @@ function parseJobEvidence(job) {
|
|
|
20834
21277
|
async function runJob(job, base) {
|
|
20835
21278
|
const valid = validateJob(job);
|
|
20836
21279
|
const action = valid.action ?? valid.capability;
|
|
20837
|
-
const projectCapabilitiesRoot =
|
|
21280
|
+
const projectCapabilitiesRoot = path46.join(base.cwd, ".kody", "capabilities");
|
|
20838
21281
|
const resolvedCapability = !valid.workflow && action ? resolveCapabilityAction(action, projectCapabilitiesRoot) : null;
|
|
20839
21282
|
const capabilityIdentity = valid.capability ?? resolvedCapability?.capability;
|
|
20840
21283
|
const capabilityContext = valid.workflow ? null : loadCapabilityContext(capabilityIdentity, base.cwd);
|
|
@@ -20852,8 +21295,17 @@ async function runJob(job, base) {
|
|
|
20852
21295
|
const profileName = explicitImplementation ?? capabilitySelectedImplementation;
|
|
20853
21296
|
if (workflow && shouldRunCapabilityWorkflow(valid, workflow, workflowIdentity, capabilitySelectedImplementation, base)) {
|
|
20854
21297
|
const workflowCapability = capabilityContext ?? workflowContext;
|
|
20855
|
-
const
|
|
20856
|
-
|
|
21298
|
+
const persistedState = valid.workflowRunId && workflowIdentity && base.config ? readWorkflowRunState(base.config, base.cwd, workflowIdentity, valid.workflowRunId) : null;
|
|
21299
|
+
const workflowJob = {
|
|
21300
|
+
...workflowContext && !valid.why ? { ...valid, why: workflowContext.body } : valid,
|
|
21301
|
+
...valid.workflowState ?? persistedState ? { workflowState: valid.workflowState ?? persistedState ?? void 0 } : {}
|
|
21302
|
+
};
|
|
21303
|
+
const checkpoint = valid.workflowRunId && workflowIdentity && base.config ? (state) => writeWorkflowRunState(base.config, base.cwd, workflowIdentity, valid.workflowRunId, state) : void 0;
|
|
21304
|
+
const result = await runCapabilityWorkflow(workflowJob, workflow, workflowCapability, base, checkpoint);
|
|
21305
|
+
if (valid.workflowRunId && workflowIdentity && base.config && result.workflowState) {
|
|
21306
|
+
writeWorkflowRunState(base.config, base.cwd, workflowIdentity, valid.workflowRunId, result.workflowState);
|
|
21307
|
+
}
|
|
21308
|
+
return result;
|
|
20857
21309
|
}
|
|
20858
21310
|
if (!profileName) {
|
|
20859
21311
|
throw new InvalidJobError(`job capability resolves to no implementation: ${capabilityIdentity ?? action}`);
|
|
@@ -20933,7 +21385,22 @@ function shouldRunCapabilityWorkflow(job, workflow, capabilityIdentity, selected
|
|
|
20933
21385
|
if (!requestedImplementation) return true;
|
|
20934
21386
|
return requestedImplementation === selectedImplementation || requestedImplementation === capabilityIdentity || requestedImplementation === job.action;
|
|
20935
21387
|
}
|
|
20936
|
-
async function runCapabilityWorkflow(parent, workflow, capability, base) {
|
|
21388
|
+
async function runCapabilityWorkflow(parent, workflow, capability, base, checkpoint) {
|
|
21389
|
+
const invalid = workflowError(workflow, base);
|
|
21390
|
+
if (invalid) {
|
|
21391
|
+
if (isGraphWorkflow(workflow)) {
|
|
21392
|
+
const state = initialWorkflowState(parent, workflow);
|
|
21393
|
+
state.status = "blocked";
|
|
21394
|
+
state.blocker = invalid;
|
|
21395
|
+
checkpoint?.(state);
|
|
21396
|
+
return { exitCode: 64, reason: invalid, workflowState: state };
|
|
21397
|
+
}
|
|
21398
|
+
return { exitCode: 64, reason: invalid };
|
|
21399
|
+
}
|
|
21400
|
+
if (isGraphWorkflow(workflow)) return runGraphCapabilityWorkflow(parent, workflow, capability, base, checkpoint);
|
|
21401
|
+
return runLinearCapabilityWorkflow(parent, workflow, capability, base);
|
|
21402
|
+
}
|
|
21403
|
+
async function runLinearCapabilityWorkflow(parent, workflow, capability, base) {
|
|
20937
21404
|
let chainData = {
|
|
20938
21405
|
...base.preloadedData ?? {},
|
|
20939
21406
|
runSubjectType: "workflow",
|
|
@@ -20997,6 +21464,200 @@ async function runCapabilityWorkflow(parent, workflow, capability, base) {
|
|
|
20997
21464
|
}
|
|
20998
21465
|
return withWorkflowBoundaryEval(capability, result);
|
|
20999
21466
|
}
|
|
21467
|
+
function isGraphWorkflow(workflow) {
|
|
21468
|
+
return workflow.startAt !== void 0 || workflow.steps.some((step) => step.id !== void 0 || step.next !== void 0 || step.inputs !== void 0);
|
|
21469
|
+
}
|
|
21470
|
+
function workflowError(workflow, base) {
|
|
21471
|
+
const projectCapabilitiesRoot = path46.join(base.cwd, ".kody", "capabilities");
|
|
21472
|
+
const knownCapabilities = /* @__PURE__ */ new Set();
|
|
21473
|
+
const capabilityInputs = /* @__PURE__ */ new Map();
|
|
21474
|
+
for (const step of workflow.steps) {
|
|
21475
|
+
const action = step.action ?? step.capability;
|
|
21476
|
+
const resolvedAction = resolveCapabilityAction(action, projectCapabilitiesRoot);
|
|
21477
|
+
const resolvedFolder = resolveCapabilityFolder(step.capability, projectCapabilitiesRoot);
|
|
21478
|
+
if (!resolvedAction && !resolvedFolder) continue;
|
|
21479
|
+
knownCapabilities.add(step.capability);
|
|
21480
|
+
const inputs = getCapabilityActionInputs(action, projectCapabilitiesRoot);
|
|
21481
|
+
if (inputs) {
|
|
21482
|
+
capabilityInputs.set(
|
|
21483
|
+
step.capability,
|
|
21484
|
+
new Set(inputs.flatMap((input) => [input.name, input.flag.replace(/^--/, "")]))
|
|
21485
|
+
);
|
|
21486
|
+
}
|
|
21487
|
+
}
|
|
21488
|
+
return formatWorkflowValidationIssues(validateWorkflow(workflow, { knownCapabilities, capabilityInputs }))[0] ?? null;
|
|
21489
|
+
}
|
|
21490
|
+
function initialWorkflowState(parent, workflow) {
|
|
21491
|
+
const prior = parent.workflowState;
|
|
21492
|
+
if (prior?.status === "done") {
|
|
21493
|
+
return {
|
|
21494
|
+
...prior,
|
|
21495
|
+
status: "done",
|
|
21496
|
+
completedStepIds: [...prior.completedStepIds],
|
|
21497
|
+
transitionCounts: { ...prior.transitionCounts },
|
|
21498
|
+
facts: { ...prior.facts },
|
|
21499
|
+
evidence: { ...prior.evidence },
|
|
21500
|
+
artifacts: prior.artifacts.map((artifact) => ({ ...artifact }))
|
|
21501
|
+
};
|
|
21502
|
+
}
|
|
21503
|
+
const firstStepId = workflow.startAt ?? workflow.steps[0]?.id;
|
|
21504
|
+
const currentStepId = prior?.currentStepId ?? firstStepId;
|
|
21505
|
+
return {
|
|
21506
|
+
status: "running",
|
|
21507
|
+
...currentStepId ? { currentStepId } : {},
|
|
21508
|
+
completedStepIds: [...prior?.completedStepIds ?? []],
|
|
21509
|
+
transitionCounts: { ...prior?.transitionCounts ?? {} },
|
|
21510
|
+
facts: { ...parent.workflowFacts ?? {}, ...prior?.facts ?? {} },
|
|
21511
|
+
evidence: { ...prior?.evidence ?? {} },
|
|
21512
|
+
artifacts: (prior?.artifacts ?? []).map((artifact) => ({ ...artifact }))
|
|
21513
|
+
};
|
|
21514
|
+
}
|
|
21515
|
+
function workflowChainData(parent, capability, base, state) {
|
|
21516
|
+
return {
|
|
21517
|
+
...base.preloadedData ?? {},
|
|
21518
|
+
runSubjectType: "workflow",
|
|
21519
|
+
runSubjectId: capability.slug,
|
|
21520
|
+
runSubjectLabel: capability.title,
|
|
21521
|
+
runSubjectWorkflow: capability.slug,
|
|
21522
|
+
workflowCapability: capability.slug,
|
|
21523
|
+
workflowTitle: capability.title,
|
|
21524
|
+
workflowStepCount: capability.config.workflow?.steps.length ?? 0,
|
|
21525
|
+
workflowIssueNumber: workflowIssueNumber(parent),
|
|
21526
|
+
workflowFacts: state.facts,
|
|
21527
|
+
workflowEvidence: state.evidence,
|
|
21528
|
+
workflowArtifacts: state.artifacts,
|
|
21529
|
+
workflowStack: [
|
|
21530
|
+
...Array.isArray(base.preloadedData?.workflowStack) ? base.preloadedData.workflowStack.filter((entry) => typeof entry === "string") : [],
|
|
21531
|
+
capability.slug
|
|
21532
|
+
]
|
|
21533
|
+
};
|
|
21534
|
+
}
|
|
21535
|
+
async function runGraphCapabilityWorkflow(parent, workflow, capability, base, checkpoint) {
|
|
21536
|
+
const state = initialWorkflowState(parent, workflow);
|
|
21537
|
+
let chainData = workflowChainData(parent, capability, base, state);
|
|
21538
|
+
let result = { exitCode: 0 };
|
|
21539
|
+
let executedSteps = 0;
|
|
21540
|
+
const maxExecutedSteps = 1e3;
|
|
21541
|
+
while (state.currentStepId) {
|
|
21542
|
+
executedSteps += 1;
|
|
21543
|
+
if (executedSteps > maxExecutedSteps) {
|
|
21544
|
+
const reason = `workflow ${capability.slug} exceeded ${maxExecutedSteps} executed steps`;
|
|
21545
|
+
state.status = "blocked";
|
|
21546
|
+
state.blocker = reason;
|
|
21547
|
+
checkpoint?.(state);
|
|
21548
|
+
return { ...result, exitCode: 64, reason, workflowState: state };
|
|
21549
|
+
}
|
|
21550
|
+
const index = workflow.steps.findIndex((step2) => step2.id === state.currentStepId);
|
|
21551
|
+
const step = workflow.steps[index];
|
|
21552
|
+
if (!step) {
|
|
21553
|
+
const reason = `workflow ${capability.slug} current step ${state.currentStepId} is missing`;
|
|
21554
|
+
state.status = "blocked";
|
|
21555
|
+
state.blocker = reason;
|
|
21556
|
+
checkpoint?.(state);
|
|
21557
|
+
return { ...result, exitCode: 64, reason, workflowState: state };
|
|
21558
|
+
}
|
|
21559
|
+
const label = step.action ?? step.capability;
|
|
21560
|
+
checkpoint?.(state);
|
|
21561
|
+
let child;
|
|
21562
|
+
try {
|
|
21563
|
+
child = workflowStepToJob(step, parent, chainData);
|
|
21564
|
+
} catch (error) {
|
|
21565
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
21566
|
+
state.status = "blocked";
|
|
21567
|
+
state.blocker = reason;
|
|
21568
|
+
checkpoint?.(state);
|
|
21569
|
+
return { exitCode: 64, reason, workflowState: state };
|
|
21570
|
+
}
|
|
21571
|
+
process.stdout.write(
|
|
21572
|
+
`\u2192 kody: workflow ${capability.slug} step ${index + 1}/${workflow.steps.length} \u2192 ${label}
|
|
21573
|
+
|
|
21574
|
+
`
|
|
21575
|
+
);
|
|
21576
|
+
result = await runJob(child, {
|
|
21577
|
+
...base,
|
|
21578
|
+
preloadedData: {
|
|
21579
|
+
...chainData,
|
|
21580
|
+
workflowStep: step.id,
|
|
21581
|
+
workflowStepIndex: index + 1,
|
|
21582
|
+
workflowStepReason: step.reason,
|
|
21583
|
+
workflowContinueOn: step.continueOn ?? []
|
|
21584
|
+
}
|
|
21585
|
+
});
|
|
21586
|
+
mergeWorkflowResults(state, result.capabilityResults);
|
|
21587
|
+
const outcome = workflowOutcome(result);
|
|
21588
|
+
const prUrl = result.prUrl ?? result.taskState?.core.prUrl ?? (typeof chainData.workflowPrUrl === "string" ? chainData.workflowPrUrl : void 0);
|
|
21589
|
+
chainData = {
|
|
21590
|
+
...workflowChainData(parent, capability, base, state),
|
|
21591
|
+
...result.taskState ? { taskState: result.taskState } : {},
|
|
21592
|
+
...outcome ? { workflowLastOutcome: outcome } : {},
|
|
21593
|
+
...result.capabilityResults?.at(-1) ? { workflowLastResult: result.capabilityResults.at(-1) } : {},
|
|
21594
|
+
...prUrl ? { workflowPrUrl: prUrl } : {},
|
|
21595
|
+
...parsePrNumber5(prUrl) ? { workflowPrNumber: parsePrNumber5(prUrl) } : {}
|
|
21596
|
+
};
|
|
21597
|
+
if (!state.completedStepIds.includes(step.id)) state.completedStepIds.push(step.id);
|
|
21598
|
+
if (result.exitCode !== 0 && !canContinueWorkflow(step, outcome)) {
|
|
21599
|
+
state.status = "failed";
|
|
21600
|
+
state.blocker = result.reason ?? `workflow step ${step.id} failed`;
|
|
21601
|
+
checkpoint?.(state);
|
|
21602
|
+
return withWorkflowBoundaryEval(capability, { ...result, workflowState: state });
|
|
21603
|
+
}
|
|
21604
|
+
if (!step.next || step.next.length === 0) {
|
|
21605
|
+
state.status = "done";
|
|
21606
|
+
delete state.currentStepId;
|
|
21607
|
+
delete state.blocker;
|
|
21608
|
+
checkpoint?.(state);
|
|
21609
|
+
return withWorkflowBoundaryEval(capability, { ...result, workflowState: state });
|
|
21610
|
+
}
|
|
21611
|
+
const transition = selectWorkflowTransition(step, chainData, state.transitionCounts);
|
|
21612
|
+
if (!transition) {
|
|
21613
|
+
const reason = `workflow step ${step.id} has no available connection`;
|
|
21614
|
+
state.status = "blocked";
|
|
21615
|
+
state.blocker = reason;
|
|
21616
|
+
checkpoint?.(state);
|
|
21617
|
+
return { ...result, exitCode: 64, reason, workflowState: state };
|
|
21618
|
+
}
|
|
21619
|
+
if (transition.maxIterations !== void 0) {
|
|
21620
|
+
const key = `${step.id}->${transition.to}`;
|
|
21621
|
+
state.transitionCounts[key] = (state.transitionCounts[key] ?? 0) + 1;
|
|
21622
|
+
}
|
|
21623
|
+
state.currentStepId = transition.to;
|
|
21624
|
+
state.status = "running";
|
|
21625
|
+
delete state.blocker;
|
|
21626
|
+
checkpoint?.(state);
|
|
21627
|
+
}
|
|
21628
|
+
state.status = "done";
|
|
21629
|
+
checkpoint?.(state);
|
|
21630
|
+
return withWorkflowBoundaryEval(capability, { ...result, workflowState: state });
|
|
21631
|
+
}
|
|
21632
|
+
function mergeWorkflowResults(state, results) {
|
|
21633
|
+
for (const result of results ?? []) {
|
|
21634
|
+
Object.assign(state.facts, result.facts);
|
|
21635
|
+
Object.assign(state.evidence, result.evidence ?? {});
|
|
21636
|
+
for (const artifact of result.artifacts) {
|
|
21637
|
+
if (!state.artifacts.some(
|
|
21638
|
+
(existing) => existing.label === artifact.label && existing.url === artifact.url && existing.path === artifact.path
|
|
21639
|
+
)) {
|
|
21640
|
+
state.artifacts.push({ ...artifact });
|
|
21641
|
+
}
|
|
21642
|
+
}
|
|
21643
|
+
}
|
|
21644
|
+
}
|
|
21645
|
+
function selectWorkflowTransition(step, data, counts) {
|
|
21646
|
+
let fallback = null;
|
|
21647
|
+
for (const transition of step.next ?? []) {
|
|
21648
|
+
const key = `${step.id}->${transition.to}`;
|
|
21649
|
+
if (transition.maxIterations !== void 0 && (counts[key] ?? 0) >= transition.maxIterations) continue;
|
|
21650
|
+
if (transition.default === true) {
|
|
21651
|
+
fallback ??= transition;
|
|
21652
|
+
continue;
|
|
21653
|
+
}
|
|
21654
|
+
if (!transition.when || conditionMatches(transition.when, workflowConditionContext(data))) return transition;
|
|
21655
|
+
}
|
|
21656
|
+
return fallback;
|
|
21657
|
+
}
|
|
21658
|
+
function conditionMatches(condition, context) {
|
|
21659
|
+
return Object.entries(condition).every(([path52, expected]) => valueMatches(resolveDottedPath2(context, path52), expected));
|
|
21660
|
+
}
|
|
21000
21661
|
function withWorkflowBoundaryEval(capability, result) {
|
|
21001
21662
|
const capabilityKind = capability.config.capabilityKind;
|
|
21002
21663
|
if (!capabilityKind) return result;
|
|
@@ -21017,8 +21678,18 @@ function withWorkflowBoundaryEval(capability, result) {
|
|
|
21017
21678
|
}
|
|
21018
21679
|
function workflowStepToJob(step, parent, chainData) {
|
|
21019
21680
|
const action = step.action ?? step.capability;
|
|
21681
|
+
const mappedArgs = {};
|
|
21682
|
+
const conditionContext = workflowConditionContext(chainData);
|
|
21683
|
+
for (const [name, mapping] of Object.entries(step.inputs ?? {})) {
|
|
21684
|
+
const value = resolveDottedPath2(conditionContext, mapping.from);
|
|
21685
|
+
if (value === void 0) {
|
|
21686
|
+
throw new InvalidJobError(`workflow step ${step.id ?? action} needs missing input ${mapping.from}`);
|
|
21687
|
+
}
|
|
21688
|
+
mappedArgs[name] = value;
|
|
21689
|
+
}
|
|
21020
21690
|
const rawArgs = {
|
|
21021
21691
|
...parent.cliArgs,
|
|
21692
|
+
...mappedArgs,
|
|
21022
21693
|
...step.cliArgs ?? {}
|
|
21023
21694
|
};
|
|
21024
21695
|
const targetNumber = workflowStepTargetNumber(step, parent, chainData);
|
|
@@ -21052,9 +21723,7 @@ function workflowStepToJob(step, parent, chainData) {
|
|
|
21052
21723
|
function shouldRunWorkflowStep(step, data) {
|
|
21053
21724
|
if (!step.runWhen) return true;
|
|
21054
21725
|
const context = workflowConditionContext(data);
|
|
21055
|
-
return
|
|
21056
|
-
([path51, expected]) => valueMatches(resolveDottedPath2(context, path51), expected)
|
|
21057
|
-
);
|
|
21726
|
+
return conditionMatches(step.runWhen, context);
|
|
21058
21727
|
}
|
|
21059
21728
|
function canContinueWorkflow(step, outcome) {
|
|
21060
21729
|
if (!outcome || !step.continueOn || step.continueOn.length === 0) return false;
|
|
@@ -21065,10 +21734,16 @@ function workflowOutcome(result) {
|
|
|
21065
21734
|
}
|
|
21066
21735
|
function workflowConditionContext(data) {
|
|
21067
21736
|
const lastOutcome = data.workflowLastOutcome;
|
|
21737
|
+
const lastResult = data.workflowLastResult;
|
|
21068
21738
|
return {
|
|
21069
21739
|
...data,
|
|
21740
|
+
facts: data.workflowFacts ?? {},
|
|
21741
|
+
evidence: data.workflowEvidence ?? {},
|
|
21742
|
+
artifacts: data.workflowArtifacts ?? [],
|
|
21743
|
+
result: lastResult,
|
|
21070
21744
|
workflow: {
|
|
21071
21745
|
lastOutcome,
|
|
21746
|
+
lastResult,
|
|
21072
21747
|
issueNumber: data.workflowIssueNumber,
|
|
21073
21748
|
prNumber: data.workflowPrNumber,
|
|
21074
21749
|
prUrl: data.workflowPrUrl
|
|
@@ -21136,7 +21811,7 @@ function composeStepWhy(parentWhy, step) {
|
|
|
21136
21811
|
}
|
|
21137
21812
|
function loadCapabilityContext(slug, cwd) {
|
|
21138
21813
|
if (!slug) return null;
|
|
21139
|
-
return resolveCapabilityFolder(slug,
|
|
21814
|
+
return resolveCapabilityFolder(slug, path46.join(cwd, ".kody", "capabilities"));
|
|
21140
21815
|
}
|
|
21141
21816
|
function loadWorkflowContext(slug, base) {
|
|
21142
21817
|
if (!slug || !base.config || !isWorkflowDefinitionId(slug)) return null;
|
|
@@ -21175,6 +21850,8 @@ var init_job = __esm({
|
|
|
21175
21850
|
init_executor();
|
|
21176
21851
|
init_registry();
|
|
21177
21852
|
init_workflowDefinitions();
|
|
21853
|
+
init_workflowRunState();
|
|
21854
|
+
init_workflowValidation();
|
|
21178
21855
|
init_jobIdentity();
|
|
21179
21856
|
init_jobIdentity();
|
|
21180
21857
|
DEFAULT_INSTANT_AGENT = "kody";
|
|
@@ -21297,7 +21974,7 @@ function translateOpenAISseToBrain(opts) {
|
|
|
21297
21974
|
// src/servers/brain-serve.ts
|
|
21298
21975
|
import * as fs49 from "fs";
|
|
21299
21976
|
import { createServer as createServer2 } from "http";
|
|
21300
|
-
import * as
|
|
21977
|
+
import * as path49 from "path";
|
|
21301
21978
|
|
|
21302
21979
|
// src/chat/loop.ts
|
|
21303
21980
|
init_agent();
|
|
@@ -21827,8 +22504,8 @@ async function runOpenAIChatTurn(args) {
|
|
|
21827
22504
|
})
|
|
21828
22505
|
});
|
|
21829
22506
|
if (!response.ok) {
|
|
21830
|
-
const
|
|
21831
|
-
const error = `OpenAI-compatible model request failed ${response.status}${
|
|
22507
|
+
const text2 = await response.text().catch(() => "");
|
|
22508
|
+
const error = `OpenAI-compatible model request failed ${response.status}${text2 ? `: ${text2.slice(0, 500)}` : ""}`;
|
|
21832
22509
|
await emit(opts.sink, "chat.error", opts.sessionId, "error", { error });
|
|
21833
22510
|
return { exitCode: 99, error };
|
|
21834
22511
|
}
|
|
@@ -21866,8 +22543,8 @@ function extractOpenAIReply(payload) {
|
|
|
21866
22543
|
return content.map((part) => {
|
|
21867
22544
|
if (typeof part === "string") return part;
|
|
21868
22545
|
if (part && typeof part === "object" && "text" in part) {
|
|
21869
|
-
const
|
|
21870
|
-
return typeof
|
|
22546
|
+
const text2 = part.text;
|
|
22547
|
+
return typeof text2 === "string" ? text2 : "";
|
|
21871
22548
|
}
|
|
21872
22549
|
return "";
|
|
21873
22550
|
}).join("");
|
|
@@ -21985,7 +22662,7 @@ init_config();
|
|
|
21985
22662
|
// src/kody-cli.ts
|
|
21986
22663
|
import { execFileSync as execFileSync26 } from "child_process";
|
|
21987
22664
|
import * as fs47 from "fs";
|
|
21988
|
-
import * as
|
|
22665
|
+
import * as path47 from "path";
|
|
21989
22666
|
|
|
21990
22667
|
// src/app-auth.ts
|
|
21991
22668
|
import { createSign } from "crypto";
|
|
@@ -22256,7 +22933,7 @@ function autoDispatch(opts) {
|
|
|
22256
22933
|
}
|
|
22257
22934
|
if (eventName !== "issue_comment") return null;
|
|
22258
22935
|
const comment = objectValue(event.comment);
|
|
22259
|
-
const
|
|
22936
|
+
const issue2 = objectValue(event.issue);
|
|
22260
22937
|
const user = objectValue(comment?.user);
|
|
22261
22938
|
const rawBody = String(comment?.body ?? "");
|
|
22262
22939
|
const authorLogin = String(user?.login ?? "");
|
|
@@ -22265,8 +22942,8 @@ function autoDispatch(opts) {
|
|
|
22265
22942
|
const isBotAuthor = authorLogin === "kody-bot" || authorType === "Bot";
|
|
22266
22943
|
if (!associationAllowed(event, opts?.config)) return null;
|
|
22267
22944
|
const body = rawBody;
|
|
22268
|
-
const targetNum = Number(
|
|
22269
|
-
const isPr = !!
|
|
22945
|
+
const targetNum = Number(issue2?.number ?? 0);
|
|
22946
|
+
const isPr = !!issue2?.pull_request;
|
|
22270
22947
|
if (!targetNum) return null;
|
|
22271
22948
|
const afterTag = extractAfterTag(body);
|
|
22272
22949
|
const firstTokenRaw = extractSubcommand(afterTag);
|
|
@@ -22350,7 +23027,7 @@ function autoDispatchTyped(opts) {
|
|
|
22350
23027
|
return { kind: "silent", reason: "GHA event payload unreadable" };
|
|
22351
23028
|
}
|
|
22352
23029
|
const comment = objectValue(event.comment);
|
|
22353
|
-
const
|
|
23030
|
+
const issue2 = objectValue(event.issue);
|
|
22354
23031
|
const user = objectValue(comment?.user);
|
|
22355
23032
|
const rawBody = String(comment?.body ?? "");
|
|
22356
23033
|
const authorLogin = String(user?.login ?? "");
|
|
@@ -22358,8 +23035,8 @@ function autoDispatchTyped(opts) {
|
|
|
22358
23035
|
if (!hasKodyMention(rawBody)) {
|
|
22359
23036
|
return { kind: "silent", reason: "comment does not mention @kody" };
|
|
22360
23037
|
}
|
|
22361
|
-
const targetNum = Number(
|
|
22362
|
-
const isPr = !!
|
|
23038
|
+
const targetNum = Number(issue2?.number ?? 0);
|
|
23039
|
+
const isPr = !!issue2?.pull_request;
|
|
22363
23040
|
if (!targetNum) {
|
|
22364
23041
|
return { kind: "silent", reason: "comment has no associated issue/PR number" };
|
|
22365
23042
|
}
|
|
@@ -22703,7 +23380,8 @@ function routeRunRequest(request) {
|
|
|
22703
23380
|
if (intent !== "run" && intent !== "tick") {
|
|
22704
23381
|
return { kind: "error", error: `workflow target does not support intent '${intent}'` };
|
|
22705
23382
|
}
|
|
22706
|
-
|
|
23383
|
+
const workflowRunId = typeof request.input?.runId === "string" && /^[a-z0-9][a-z0-9_-]{0,79}$/.test(request.input.runId) ? request.input.runId : void 0;
|
|
23384
|
+
return { kind: "action", action: target.id, cliArgs: {}, ...workflowRunId ? { workflowRunId } : {} };
|
|
22707
23385
|
}
|
|
22708
23386
|
return { kind: "error", error: "unsupported run request target" };
|
|
22709
23387
|
}
|
|
@@ -22782,9 +23460,9 @@ async function resolveAuthToken(env = process.env) {
|
|
|
22782
23460
|
return void 0;
|
|
22783
23461
|
}
|
|
22784
23462
|
function detectPackageManager2(cwd) {
|
|
22785
|
-
if (fs47.existsSync(
|
|
22786
|
-
if (fs47.existsSync(
|
|
22787
|
-
if (fs47.existsSync(
|
|
23463
|
+
if (fs47.existsSync(path47.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
|
|
23464
|
+
if (fs47.existsSync(path47.join(cwd, "yarn.lock"))) return "yarn";
|
|
23465
|
+
if (fs47.existsSync(path47.join(cwd, "bun.lockb"))) return "bun";
|
|
22788
23466
|
return "npm";
|
|
22789
23467
|
}
|
|
22790
23468
|
function shouldChainScheduledWatch(match) {
|
|
@@ -22907,7 +23585,7 @@ async function runCi(argv) {
|
|
|
22907
23585
|
return 0;
|
|
22908
23586
|
}
|
|
22909
23587
|
const args = parseCiArgs(argv);
|
|
22910
|
-
const cwd = args.cwd ?
|
|
23588
|
+
const cwd = args.cwd ? path47.resolve(args.cwd) : process.cwd();
|
|
22911
23589
|
try {
|
|
22912
23590
|
const n = unpackAllSecrets();
|
|
22913
23591
|
if (n > 0) process.stdout.write(`\u2192 kody: unpacked ${n} secret(s) from ALL_SECRETS
|
|
@@ -22931,6 +23609,7 @@ async function runCi(argv) {
|
|
|
22931
23609
|
let manualWorkflowDispatch = false;
|
|
22932
23610
|
let forceRunAction = null;
|
|
22933
23611
|
let forceRunCliArgs = {};
|
|
23612
|
+
let forceWorkflowRunId;
|
|
22934
23613
|
let runRequestFanOut = false;
|
|
22935
23614
|
let runRequestFanOutForce = false;
|
|
22936
23615
|
const parsedRunRequest = readRunRequestFromEnv();
|
|
@@ -22955,6 +23634,7 @@ async function runCi(argv) {
|
|
|
22955
23634
|
} else if (route.kind === "action") {
|
|
22956
23635
|
forceRunAction = route.action;
|
|
22957
23636
|
forceRunCliArgs = route.cliArgs;
|
|
23637
|
+
forceWorkflowRunId = route.workflowRunId;
|
|
22958
23638
|
}
|
|
22959
23639
|
}
|
|
22960
23640
|
const envForceAction = (process.env.KODY_FORCE_ACTION ?? "").trim();
|
|
@@ -23052,6 +23732,7 @@ async function runCi(argv) {
|
|
|
23052
23732
|
capability: route.capability,
|
|
23053
23733
|
workflow: route.workflow,
|
|
23054
23734
|
implementation: route.implementation,
|
|
23735
|
+
workflowRunId: forceWorkflowRunId,
|
|
23055
23736
|
cliArgs: { ...route.cliArgs, ...forceRunCliArgs },
|
|
23056
23737
|
flavor: "instant",
|
|
23057
23738
|
force: true
|
|
@@ -23341,7 +24022,7 @@ init_repoWorkspace();
|
|
|
23341
24022
|
// src/scripts/brainTurnLog.ts
|
|
23342
24023
|
init_runtimePaths();
|
|
23343
24024
|
import * as fs48 from "fs";
|
|
23344
|
-
import * as
|
|
24025
|
+
import * as path48 from "path";
|
|
23345
24026
|
import posixPath4 from "path/posix";
|
|
23346
24027
|
var live = /* @__PURE__ */ new Map();
|
|
23347
24028
|
function brainEventsFilePath(dir, chatId) {
|
|
@@ -23391,7 +24072,7 @@ function beginTurn(dir, chatId) {
|
|
|
23391
24072
|
};
|
|
23392
24073
|
live.set(chatId, state);
|
|
23393
24074
|
const p = brainEventsFilePath(dir, chatId);
|
|
23394
|
-
fs48.mkdirSync(
|
|
24075
|
+
fs48.mkdirSync(path48.dirname(p), { recursive: true });
|
|
23395
24076
|
return (event) => {
|
|
23396
24077
|
state.seq += 1;
|
|
23397
24078
|
const rec = { seq: state.seq, turn, ts: Date.now(), event };
|
|
@@ -23769,7 +24450,7 @@ async function handleChatTurn(req, res, chatId, opts) {
|
|
|
23769
24450
|
);
|
|
23770
24451
|
}
|
|
23771
24452
|
}
|
|
23772
|
-
fs49.mkdirSync(
|
|
24453
|
+
fs49.mkdirSync(path49.dirname(sessionFile), { recursive: true });
|
|
23773
24454
|
appendTurn(sessionFile, {
|
|
23774
24455
|
role: "user",
|
|
23775
24456
|
content: message,
|
|
@@ -23844,7 +24525,7 @@ async function handleChatTurn(req, res, chatId, opts) {
|
|
|
23844
24525
|
function buildServer(opts) {
|
|
23845
24526
|
const runTurn = opts.runTurn ?? runChatTurn;
|
|
23846
24527
|
const cloneRepo = opts.cloneRepo ?? defaultCloneRepo;
|
|
23847
|
-
const reposRoot = opts.reposRoot ??
|
|
24528
|
+
const reposRoot = opts.reposRoot ?? path49.join(path49.dirname(path49.resolve(opts.cwd)), "repos");
|
|
23848
24529
|
return createServer2(async (req, res) => {
|
|
23849
24530
|
if (!req.method || !req.url) {
|
|
23850
24531
|
sendJson(res, 400, { error: "bad request" });
|
|
@@ -24448,7 +25129,7 @@ async function loadConfigSafe() {
|
|
|
24448
25129
|
|
|
24449
25130
|
// src/chat-cli.ts
|
|
24450
25131
|
import * as fs51 from "fs";
|
|
24451
|
-
import * as
|
|
25132
|
+
import * as path51 from "path";
|
|
24452
25133
|
|
|
24453
25134
|
// src/chat/inbox.ts
|
|
24454
25135
|
import { execFileSync as execFileSync27 } from "child_process";
|
|
@@ -24521,9 +25202,9 @@ function currentBranch(cwd) {
|
|
|
24521
25202
|
// src/chat/state-sync.ts
|
|
24522
25203
|
init_stateRepo();
|
|
24523
25204
|
import * as fs50 from "fs";
|
|
24524
|
-
import * as
|
|
24525
|
-
function jsonlLines2(
|
|
24526
|
-
return
|
|
25205
|
+
import * as path50 from "path";
|
|
25206
|
+
function jsonlLines2(text2) {
|
|
25207
|
+
return text2.split("\n").filter((line) => line.length > 0);
|
|
24527
25208
|
}
|
|
24528
25209
|
function renderJsonl2(lines) {
|
|
24529
25210
|
return lines.length > 0 ? `${lines.join("\n")}
|
|
@@ -24541,7 +25222,7 @@ function syncJsonlFileFromState(opts) {
|
|
|
24541
25222
|
const local = fs50.existsSync(opts.localPath) ? fs50.readFileSync(opts.localPath, "utf-8") : "";
|
|
24542
25223
|
const next = mergeJsonl2(local, remote.content);
|
|
24543
25224
|
if (next === local) return;
|
|
24544
|
-
fs50.mkdirSync(
|
|
25225
|
+
fs50.mkdirSync(path50.dirname(opts.localPath), { recursive: true });
|
|
24545
25226
|
fs50.writeFileSync(opts.localPath, next);
|
|
24546
25227
|
}
|
|
24547
25228
|
function persistJsonlFileToState(opts) {
|
|
@@ -24811,7 +25492,7 @@ async function runChat(argv) {
|
|
|
24811
25492
|
${CHAT_HELP}`);
|
|
24812
25493
|
return 64;
|
|
24813
25494
|
}
|
|
24814
|
-
const cwd = args.cwd ?
|
|
25495
|
+
const cwd = args.cwd ? path51.resolve(args.cwd) : process.cwd();
|
|
24815
25496
|
const sessionId = args.sessionId;
|
|
24816
25497
|
const runRequest = readRunRequestFromEnv();
|
|
24817
25498
|
if (runRequest && "request" in runRequest) {
|
|
@@ -24991,8 +25672,8 @@ var FlyClient = class {
|
|
|
24991
25672
|
get fetch() {
|
|
24992
25673
|
return this.opts.fetchImpl ?? fetch;
|
|
24993
25674
|
}
|
|
24994
|
-
async call(
|
|
24995
|
-
const res = await this.fetch(`${FLY_API_BASE}${
|
|
25675
|
+
async call(path52, init = {}) {
|
|
25676
|
+
const res = await this.fetch(`${FLY_API_BASE}${path52}`, {
|
|
24996
25677
|
method: init.method ?? "GET",
|
|
24997
25678
|
headers: {
|
|
24998
25679
|
Authorization: `Bearer ${this.opts.token}`,
|
|
@@ -25002,8 +25683,8 @@ var FlyClient = class {
|
|
|
25002
25683
|
});
|
|
25003
25684
|
if (res.status === 404 && init.allow404) return null;
|
|
25004
25685
|
if (!res.ok) {
|
|
25005
|
-
const
|
|
25006
|
-
throw new Error(`Fly API ${res.status} on ${
|
|
25686
|
+
const text2 = await res.text().catch(() => "");
|
|
25687
|
+
throw new Error(`Fly API ${res.status} on ${path52}: ${text2.slice(0, 200) || res.statusText}`);
|
|
25007
25688
|
}
|
|
25008
25689
|
if (res.status === 204) return null;
|
|
25009
25690
|
const raw = await res.text();
|
|
@@ -26350,11 +27031,11 @@ function envRunMode(env = process.env) {
|
|
|
26350
27031
|
return { ...result, command: "ci", ciArgv: [] };
|
|
26351
27032
|
}
|
|
26352
27033
|
if (mode === "issue") {
|
|
26353
|
-
const
|
|
26354
|
-
if (!
|
|
27034
|
+
const issue2 = (env.ISSUE_NUMBER ?? "").trim();
|
|
27035
|
+
if (!issue2) {
|
|
26355
27036
|
return { ...result, errors: ["KODY_RUN_MODE=issue requires ISSUE_NUMBER"] };
|
|
26356
27037
|
}
|
|
26357
|
-
return { ...result, command: "ci", ciArgv: ["--issue",
|
|
27038
|
+
return { ...result, command: "ci", ciArgv: ["--issue", issue2] };
|
|
26358
27039
|
}
|
|
26359
27040
|
return { ...result, errors: [`unknown KODY_RUN_MODE: ${mode}`] };
|
|
26360
27041
|
}
|