@kody-ade/kody-engine 0.4.379 → 0.4.381
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 +679 -165
- package/dist/implementations/types.ts +26 -0
- package/package.json +2 -1
package/dist/bin/kody.js
CHANGED
|
@@ -15,7 +15,7 @@ var init_package = __esm({
|
|
|
15
15
|
"package.json"() {
|
|
16
16
|
package_default = {
|
|
17
17
|
name: "@kody-ade/kody-engine",
|
|
18
|
-
version: "0.4.
|
|
18
|
+
version: "0.4.381",
|
|
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",
|
|
@@ -53,6 +53,7 @@ var init_package = __esm({
|
|
|
53
53
|
"@actions/cache": "^6.0.0",
|
|
54
54
|
"@anthropic-ai/claude-agent-sdk": "0.2.119",
|
|
55
55
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
56
|
+
convex: "^1.17.0",
|
|
56
57
|
zod: "^4.0.0"
|
|
57
58
|
},
|
|
58
59
|
devDependencies: {
|
|
@@ -1612,7 +1613,7 @@ function cmsHeaders(opts) {
|
|
|
1612
1613
|
}
|
|
1613
1614
|
};
|
|
1614
1615
|
}
|
|
1615
|
-
async function callDashboardCms(opts,
|
|
1616
|
+
async function callDashboardCms(opts, path53, init = {}) {
|
|
1616
1617
|
const baseUrl = dashboardBaseUrl(opts);
|
|
1617
1618
|
if (!baseUrl) {
|
|
1618
1619
|
return {
|
|
@@ -1624,7 +1625,7 @@ async function callDashboardCms(opts, path52, init = {}) {
|
|
|
1624
1625
|
const headerResult = cmsHeaders(opts);
|
|
1625
1626
|
if (!headerResult.ok) return headerResult;
|
|
1626
1627
|
try {
|
|
1627
|
-
const res = await fetch(`${baseUrl}${
|
|
1628
|
+
const res = await fetch(`${baseUrl}${path53}`, {
|
|
1628
1629
|
...init,
|
|
1629
1630
|
headers: {
|
|
1630
1631
|
...headerResult.headers,
|
|
@@ -1696,8 +1697,8 @@ function documentArg(value) {
|
|
|
1696
1697
|
function normalizeCmsDocumentIdInput(input) {
|
|
1697
1698
|
const trimmed = stripWrappingQuotes(input.trim());
|
|
1698
1699
|
const withoutQuery = trimmed.split(/[?#]/, 1)[0] ?? trimmed;
|
|
1699
|
-
const
|
|
1700
|
-
return
|
|
1700
|
+
const path53 = parseDocumentPath(withoutQuery);
|
|
1701
|
+
return path53 ?? parseDocumentIdSegment(withoutQuery) ?? withoutQuery;
|
|
1701
1702
|
}
|
|
1702
1703
|
function stripWrappingQuotes(value) {
|
|
1703
1704
|
let current = value;
|
|
@@ -1708,9 +1709,9 @@ function stripWrappingQuotes(value) {
|
|
|
1708
1709
|
}
|
|
1709
1710
|
}
|
|
1710
1711
|
function parseDocumentPath(value) {
|
|
1711
|
-
const
|
|
1712
|
-
if (!
|
|
1713
|
-
const parts =
|
|
1712
|
+
const path53 = value.startsWith("http://") || value.startsWith("https://") ? urlPathname(value) : value;
|
|
1713
|
+
if (!path53?.includes("/content/entries/")) return null;
|
|
1714
|
+
const parts = path53.split("/").filter(Boolean).map(decodePathPart);
|
|
1714
1715
|
const entriesIndex = parts.findIndex((part, index) => part === "content" && parts[index + 1] === "entries");
|
|
1715
1716
|
const idPart = parts[entriesIndex + 3];
|
|
1716
1717
|
if (!idPart || idPart === "new") return null;
|
|
@@ -1863,6 +1864,16 @@ var init_dashboardCmsMcp = __esm({
|
|
|
1863
1864
|
// src/capabilityFolders.ts
|
|
1864
1865
|
import * as fs4 from "fs";
|
|
1865
1866
|
import * as path6 from "path";
|
|
1867
|
+
function capabilityOutputConditionPaths(config) {
|
|
1868
|
+
const result = config.output?.result;
|
|
1869
|
+
if (!result) return /* @__PURE__ */ new Set();
|
|
1870
|
+
return /* @__PURE__ */ new Set([
|
|
1871
|
+
"result.status",
|
|
1872
|
+
"result.summary",
|
|
1873
|
+
"result.resultClass",
|
|
1874
|
+
...result.facts.map((fact) => `result.facts.${fact}`)
|
|
1875
|
+
]);
|
|
1876
|
+
}
|
|
1866
1877
|
function listCapabilityFolderSlugs(absDir) {
|
|
1867
1878
|
if (!fs4.existsSync(absDir)) return [];
|
|
1868
1879
|
let entries;
|
|
@@ -1923,9 +1934,17 @@ function parseCapabilityConfig(raw) {
|
|
|
1923
1934
|
stage: stringField(raw.stage),
|
|
1924
1935
|
readsFrom: stringList(raw.readsFrom ?? raw.reads_from),
|
|
1925
1936
|
writesTo: stringList(raw.writesTo ?? raw.writes_to),
|
|
1937
|
+
output: parseCapabilityOutput(raw.output),
|
|
1926
1938
|
workflow: parseCapabilityWorkflow(raw.workflow)
|
|
1927
1939
|
};
|
|
1928
1940
|
}
|
|
1941
|
+
function parseCapabilityOutput(raw) {
|
|
1942
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return void 0;
|
|
1943
|
+
const result = raw.result;
|
|
1944
|
+
if (!result || typeof result !== "object" || Array.isArray(result)) return void 0;
|
|
1945
|
+
const facts = stringList(result.facts);
|
|
1946
|
+
return { result: { facts } };
|
|
1947
|
+
}
|
|
1929
1948
|
function parseCapabilityKind(raw) {
|
|
1930
1949
|
return raw === "observe" || raw === "act" || raw === "verify" ? raw : void 0;
|
|
1931
1950
|
}
|
|
@@ -4645,6 +4664,7 @@ function loadProfile(profilePath) {
|
|
|
4645
4664
|
skills: parseStringArray2(r.skills),
|
|
4646
4665
|
prompt: typeof r.prompt === "string" && r.prompt.trim() ? r.prompt.trim() : void 0,
|
|
4647
4666
|
chatTools: parseStringArray2(r.chatTools),
|
|
4667
|
+
auth: parseAuth(profilePath, r.auth),
|
|
4648
4668
|
describe: typeof r.describe === "string" ? r.describe : "",
|
|
4649
4669
|
// Optional agent to run as. Empty/blank string → undefined (no agent).
|
|
4650
4670
|
agent: typeof r.agent === "string" && r.agent.trim() ? r.agent.trim() : void 0,
|
|
@@ -4752,6 +4772,80 @@ function parseStringArray2(raw) {
|
|
|
4752
4772
|
const values = raw.map((t) => String(t).trim()).filter(Boolean);
|
|
4753
4773
|
return values.length > 0 ? values : void 0;
|
|
4754
4774
|
}
|
|
4775
|
+
function rejectUnknownAuthFields(p, value, allowed, prefix) {
|
|
4776
|
+
const known = new Set(allowed);
|
|
4777
|
+
const unknown = Object.keys(value).find((key) => !known.has(key));
|
|
4778
|
+
if (unknown) throw new ProfileError(p, `${prefix} has unknown field "${unknown}"`);
|
|
4779
|
+
}
|
|
4780
|
+
function parseAuth(p, raw) {
|
|
4781
|
+
if (raw === void 0 || raw === null) return void 0;
|
|
4782
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
|
|
4783
|
+
throw new ProfileError(p, `"auth" must be an object`);
|
|
4784
|
+
}
|
|
4785
|
+
const auth = raw;
|
|
4786
|
+
rejectUnknownAuthFields(p, auth, ["methods"], "auth");
|
|
4787
|
+
const methodsRaw = auth.methods;
|
|
4788
|
+
if (!Array.isArray(methodsRaw) || methodsRaw.length === 0) {
|
|
4789
|
+
throw new ProfileError(p, `auth.methods must be a non-empty array`);
|
|
4790
|
+
}
|
|
4791
|
+
const methods = methodsRaw.map((methodRaw, methodIndex) => {
|
|
4792
|
+
if (!methodRaw || typeof methodRaw !== "object" || Array.isArray(methodRaw)) {
|
|
4793
|
+
throw new ProfileError(p, `auth.methods[${methodIndex}] must be an object`);
|
|
4794
|
+
}
|
|
4795
|
+
const method = methodRaw;
|
|
4796
|
+
rejectUnknownAuthFields(p, method, ["name", "strategy", "adapter", "fields"], `auth.methods[${methodIndex}]`);
|
|
4797
|
+
const name = method.name;
|
|
4798
|
+
if (typeof name !== "string" || !AUTH_TEXT_RE.test(name.trim())) {
|
|
4799
|
+
throw new ProfileError(p, `auth.methods[${methodIndex}].name must be a single-line string of 1-120 characters`);
|
|
4800
|
+
}
|
|
4801
|
+
if (method.strategy !== "browser-storage-state") {
|
|
4802
|
+
throw new ProfileError(p, `auth.methods[${methodIndex}].strategy must be browser-storage-state`);
|
|
4803
|
+
}
|
|
4804
|
+
if (method.adapter !== "kody-repository") {
|
|
4805
|
+
throw new ProfileError(p, `auth.methods[${methodIndex}].adapter must be kody-repository`);
|
|
4806
|
+
}
|
|
4807
|
+
if (!Array.isArray(method.fields) || method.fields.length === 0) {
|
|
4808
|
+
throw new ProfileError(p, `auth.methods[${methodIndex}].fields must be a non-empty array`);
|
|
4809
|
+
}
|
|
4810
|
+
const fields = method.fields.map((fieldRaw, fieldIndex) => {
|
|
4811
|
+
const prefix = `auth.methods[${methodIndex}].fields[${fieldIndex}]`;
|
|
4812
|
+
if (!fieldRaw || typeof fieldRaw !== "object" || Array.isArray(fieldRaw)) {
|
|
4813
|
+
throw new ProfileError(p, `${prefix} must be an object`);
|
|
4814
|
+
}
|
|
4815
|
+
const field = fieldRaw;
|
|
4816
|
+
rejectUnknownAuthFields(p, field, ["label", "source", "key"], prefix);
|
|
4817
|
+
if (typeof field.label !== "string" || !AUTH_TEXT_RE.test(field.label.trim())) {
|
|
4818
|
+
throw new ProfileError(p, `${prefix}.label must be a single-line string of 1-120 characters`);
|
|
4819
|
+
}
|
|
4820
|
+
if (field.source !== "variable" && field.source !== "secret") {
|
|
4821
|
+
throw new ProfileError(p, `${prefix}.source must be variable or secret`);
|
|
4822
|
+
}
|
|
4823
|
+
if (typeof field.key !== "string" || !AUTH_KEY_RE.test(field.key)) {
|
|
4824
|
+
throw new ProfileError(p, `${prefix}.key must be an uppercase variable or secret name`);
|
|
4825
|
+
}
|
|
4826
|
+
return {
|
|
4827
|
+
label: field.label.trim(),
|
|
4828
|
+
source: field.source,
|
|
4829
|
+
key: field.key
|
|
4830
|
+
};
|
|
4831
|
+
});
|
|
4832
|
+
const variableFields = fields.filter((field) => field.source === "variable");
|
|
4833
|
+
const secretFields = fields.filter((field) => field.source === "secret");
|
|
4834
|
+
if (variableFields.length !== 1 || secretFields.length !== 1) {
|
|
4835
|
+
throw new ProfileError(
|
|
4836
|
+
p,
|
|
4837
|
+
`auth.methods[${methodIndex}] kody-repository requires exactly one variable field and one secret field`
|
|
4838
|
+
);
|
|
4839
|
+
}
|
|
4840
|
+
return {
|
|
4841
|
+
name: name.trim(),
|
|
4842
|
+
strategy: method.strategy,
|
|
4843
|
+
adapter: method.adapter,
|
|
4844
|
+
fields
|
|
4845
|
+
};
|
|
4846
|
+
});
|
|
4847
|
+
return { methods };
|
|
4848
|
+
}
|
|
4755
4849
|
function parseCapabilityKind2(raw) {
|
|
4756
4850
|
return raw === "observe" || raw === "act" || raw === "verify" ? raw : void 0;
|
|
4757
4851
|
}
|
|
@@ -4996,7 +5090,7 @@ function parseScriptList(p, key, raw) {
|
|
|
4996
5090
|
}
|
|
4997
5091
|
return out;
|
|
4998
5092
|
}
|
|
4999
|
-
var VALID_INPUT_TYPES, VALID_PERMISSION_MODES, VALID_ROLES, VALID_CONTAINER_CHILD_TARGETS, VALID_PHASES, KNOWN_PROFILE_KEYS;
|
|
5093
|
+
var VALID_INPUT_TYPES, VALID_PERMISSION_MODES, VALID_ROLES, VALID_CONTAINER_CHILD_TARGETS, VALID_PHASES, KNOWN_PROFILE_KEYS, AUTH_KEY_RE, AUTH_TEXT_RE;
|
|
5000
5094
|
var init_profile = __esm({
|
|
5001
5095
|
"src/profile.ts"() {
|
|
5002
5096
|
"use strict";
|
|
@@ -5025,6 +5119,7 @@ var init_profile = __esm({
|
|
|
5025
5119
|
"skills",
|
|
5026
5120
|
"prompt",
|
|
5027
5121
|
"chatTools",
|
|
5122
|
+
"auth",
|
|
5028
5123
|
"agent",
|
|
5029
5124
|
"every",
|
|
5030
5125
|
"capabilityTools",
|
|
@@ -5058,6 +5153,8 @@ var init_profile = __esm({
|
|
|
5058
5153
|
"resetBetweenChildren",
|
|
5059
5154
|
"preloadContext"
|
|
5060
5155
|
]);
|
|
5156
|
+
AUTH_KEY_RE = /^[A-Z][A-Z0-9_]{0,127}$/;
|
|
5157
|
+
AUTH_TEXT_RE = /^[^\r\n]{1,120}$/;
|
|
5061
5158
|
}
|
|
5062
5159
|
});
|
|
5063
5160
|
|
|
@@ -6812,6 +6909,29 @@ var init_runIndex = __esm({
|
|
|
6812
6909
|
}
|
|
6813
6910
|
});
|
|
6814
6911
|
|
|
6912
|
+
// src/runtimeCleanup.ts
|
|
6913
|
+
function registeredCleanup(ctx) {
|
|
6914
|
+
return Array.isArray(ctx.data.__runtimeCleanup) ? ctx.data.__runtimeCleanup : [];
|
|
6915
|
+
}
|
|
6916
|
+
function registerRuntimeCleanup(ctx, cleanup) {
|
|
6917
|
+
ctx.data.__runtimeCleanup = [...registeredCleanup(ctx), cleanup];
|
|
6918
|
+
}
|
|
6919
|
+
function runRuntimeCleanup(ctx) {
|
|
6920
|
+
const callbacks = registeredCleanup(ctx);
|
|
6921
|
+
delete ctx.data.__runtimeCleanup;
|
|
6922
|
+
for (const cleanup of callbacks.reverse()) {
|
|
6923
|
+
try {
|
|
6924
|
+
cleanup();
|
|
6925
|
+
} catch {
|
|
6926
|
+
}
|
|
6927
|
+
}
|
|
6928
|
+
}
|
|
6929
|
+
var init_runtimeCleanup = __esm({
|
|
6930
|
+
"src/runtimeCleanup.ts"() {
|
|
6931
|
+
"use strict";
|
|
6932
|
+
}
|
|
6933
|
+
});
|
|
6934
|
+
|
|
6815
6935
|
// src/scripts/evaluateAgencyBoundaries.ts
|
|
6816
6936
|
function shouldEvaluateAgencyBoundaries(data, profile) {
|
|
6817
6937
|
return Boolean(agencyBoundaryCapabilityKind(data, profile));
|
|
@@ -7723,10 +7843,10 @@ var init_state2 = __esm({
|
|
|
7723
7843
|
"use strict";
|
|
7724
7844
|
VALID_STATES = /* @__PURE__ */ new Set(["active", "abandoned", "closed", "done"]);
|
|
7725
7845
|
GoalStateError = class extends Error {
|
|
7726
|
-
constructor(
|
|
7727
|
-
super(`Invalid goal state at ${
|
|
7846
|
+
constructor(path53, message) {
|
|
7847
|
+
super(`Invalid goal state at ${path53}:
|
|
7728
7848
|
${message}`);
|
|
7729
|
-
this.path =
|
|
7849
|
+
this.path = path53;
|
|
7730
7850
|
this.name = "GoalStateError";
|
|
7731
7851
|
}
|
|
7732
7852
|
path;
|
|
@@ -7739,9 +7859,9 @@ import * as fs25 from "fs";
|
|
|
7739
7859
|
function stageGoalRunLogEvent(data, goalId, event, at = nowIso()) {
|
|
7740
7860
|
const logs = goalRunLogs(data);
|
|
7741
7861
|
const existing = logs[goalId];
|
|
7742
|
-
const
|
|
7862
|
+
const path53 = existing?.path ?? goalRunLogPath(goalId, data);
|
|
7743
7863
|
logs[goalId] = {
|
|
7744
|
-
path:
|
|
7864
|
+
path: path53,
|
|
7745
7865
|
events: [...existing?.events ?? [], buildGoalRunLogEvent(data, goalId, event, at)]
|
|
7746
7866
|
};
|
|
7747
7867
|
}
|
|
@@ -8563,7 +8683,7 @@ function buildGoalTargetInstance(template, targetId, now) {
|
|
|
8563
8683
|
extra.template = targetId;
|
|
8564
8684
|
extra.sourceTemplate = targetId;
|
|
8565
8685
|
extra.templateId = targetId;
|
|
8566
|
-
if (!
|
|
8686
|
+
if (!isPlainObject3(extra.facts)) extra.facts = {};
|
|
8567
8687
|
if (!Array.isArray(extra.blockers)) extra.blockers = [];
|
|
8568
8688
|
const at = isoNoMs(now);
|
|
8569
8689
|
return {
|
|
@@ -8573,7 +8693,7 @@ function buildGoalTargetInstance(template, targetId, now) {
|
|
|
8573
8693
|
extra
|
|
8574
8694
|
};
|
|
8575
8695
|
}
|
|
8576
|
-
function
|
|
8696
|
+
function isPlainObject3(value) {
|
|
8577
8697
|
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
8578
8698
|
}
|
|
8579
8699
|
function assertSafeGoalId(value, label) {
|
|
@@ -8858,6 +8978,7 @@ function validateWorkflow(value, options = {}) {
|
|
|
8858
8978
|
if (!step) return;
|
|
8859
8979
|
const id = text(step.id);
|
|
8860
8980
|
if (!id) return;
|
|
8981
|
+
const sourceCapability = text(step.capability ?? step.action);
|
|
8861
8982
|
const transitions = transitionList(step.next);
|
|
8862
8983
|
adjacency.set(id, []);
|
|
8863
8984
|
if (transitions.length > maxTransitions) {
|
|
@@ -8933,7 +9054,10 @@ function validateWorkflow(value, options = {}) {
|
|
|
8933
9054
|
if (raw.default === true && raw.when !== void 0) {
|
|
8934
9055
|
issue(issues, "conflicting_transition", base, "workflow connection cannot be both conditional and default");
|
|
8935
9056
|
}
|
|
8936
|
-
if (raw.when !== void 0)
|
|
9057
|
+
if (raw.when !== void 0) {
|
|
9058
|
+
const outputPaths = options.capabilityOutputs?.get(sourceCapability ?? "");
|
|
9059
|
+
validateDataMatch(raw.when, `${base}.when`, issues, outputPaths);
|
|
9060
|
+
}
|
|
8937
9061
|
const targetIndex = ids.indexOf(target ?? "");
|
|
8938
9062
|
const iterations = raw.maxIterations;
|
|
8939
9063
|
if (targetIndex >= 0 && targetIndex <= index) {
|
|
@@ -8978,11 +9102,11 @@ function validateWorkflow(value, options = {}) {
|
|
|
8978
9102
|
function formatWorkflowValidationIssues(issues) {
|
|
8979
9103
|
return issues.map((entry) => `${entry.path}: ${entry.message}`);
|
|
8980
9104
|
}
|
|
8981
|
-
function validateDataMatch(value,
|
|
9105
|
+
function validateDataMatch(value, path53, issues, capabilityOutputs) {
|
|
8982
9106
|
if (value === void 0) return;
|
|
8983
9107
|
const match = asRecord2(value);
|
|
8984
9108
|
if (!match || Object.keys(match).length === 0) {
|
|
8985
|
-
issue(issues, "invalid_condition",
|
|
9109
|
+
issue(issues, "invalid_condition", path53, "workflow condition must contain at least one match");
|
|
8986
9110
|
return;
|
|
8987
9111
|
}
|
|
8988
9112
|
for (const [field, expected] of Object.entries(match)) {
|
|
@@ -8990,12 +9114,20 @@ function validateDataMatch(value, path52, issues) {
|
|
|
8990
9114
|
issue(
|
|
8991
9115
|
issues,
|
|
8992
9116
|
"invalid_data_path",
|
|
8993
|
-
`${
|
|
9117
|
+
`${path53}.${field}`,
|
|
8994
9118
|
`workflow condition must read from facts, evidence, artifacts, result, workflow, or lastOutcome`
|
|
8995
9119
|
);
|
|
8996
9120
|
}
|
|
9121
|
+
if (capabilityOutputs && field.startsWith("result.") && !capabilityOutputs.has(field)) {
|
|
9122
|
+
issue(
|
|
9123
|
+
issues,
|
|
9124
|
+
"undeclared_result_path",
|
|
9125
|
+
`${path53}.${field}`,
|
|
9126
|
+
`workflow condition reads ${field}, but the source capability does not declare it`
|
|
9127
|
+
);
|
|
9128
|
+
}
|
|
8997
9129
|
if (!isComparable(expected)) {
|
|
8998
|
-
issue(issues, "invalid_condition_value", `${
|
|
9130
|
+
issue(issues, "invalid_condition_value", `${path53}.${field}`, "workflow condition value must be a JSON scalar");
|
|
8999
9131
|
}
|
|
9000
9132
|
}
|
|
9001
9133
|
}
|
|
@@ -9013,8 +9145,8 @@ function isComparable(value) {
|
|
|
9013
9145
|
if (value === null || ["string", "number", "boolean"].includes(typeof value)) return true;
|
|
9014
9146
|
return Array.isArray(value) && value.length > 0 && value.every((item) => isComparable(item) && !Array.isArray(item));
|
|
9015
9147
|
}
|
|
9016
|
-
function issue(issues, code,
|
|
9017
|
-
issues.push({ code, path:
|
|
9148
|
+
function issue(issues, code, path53, message) {
|
|
9149
|
+
issues.push({ code, path: path53, message });
|
|
9018
9150
|
}
|
|
9019
9151
|
var SAFE_NAME, SAFE_STEP_ID, SAFE_DATA_PATH, SUPPORTED_STEP_FIELDS, SUPPORTED_TRANSITION_FIELDS;
|
|
9020
9152
|
var init_workflowValidation = __esm({
|
|
@@ -14452,13 +14584,13 @@ function companyIntentPath(id) {
|
|
|
14452
14584
|
assertIntentId(id);
|
|
14453
14585
|
return `intents/${id}/intent.json`;
|
|
14454
14586
|
}
|
|
14455
|
-
function normalizeCompanyIntent(
|
|
14587
|
+
function normalizeCompanyIntent(path53, raw) {
|
|
14456
14588
|
if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
|
|
14457
|
-
throw new Error(`${
|
|
14589
|
+
throw new Error(`${path53}: intent must be JSON object`);
|
|
14458
14590
|
}
|
|
14459
14591
|
const input = raw;
|
|
14460
14592
|
const id = stringField5(input.id);
|
|
14461
|
-
if (!id || !isCompanyIntentId(id)) throw new Error(`${
|
|
14593
|
+
if (!id || !isCompanyIntentId(id)) throw new Error(`${path53}: invalid intent id`);
|
|
14462
14594
|
const createdAt = stringField5(input.createdAt) || nowIso();
|
|
14463
14595
|
const updatedAt = stringField5(input.updatedAt) || createdAt;
|
|
14464
14596
|
const description = stringField5(input.description);
|
|
@@ -14498,8 +14630,8 @@ function listCompanyIntents(config, cwd) {
|
|
|
14498
14630
|
const records = [];
|
|
14499
14631
|
for (const entry of entries) {
|
|
14500
14632
|
if (entry.type !== "dir" || !entry.name || !isCompanyIntentId(entry.name)) continue;
|
|
14501
|
-
const
|
|
14502
|
-
const file = readStateText(config, cwd,
|
|
14633
|
+
const path53 = companyIntentPath(entry.name);
|
|
14634
|
+
const file = readStateText(config, cwd, path53);
|
|
14503
14635
|
if (!file) continue;
|
|
14504
14636
|
records.push({
|
|
14505
14637
|
id: entry.name,
|
|
@@ -16880,6 +17012,196 @@ var init_postResearchComment = __esm({
|
|
|
16880
17012
|
}
|
|
16881
17013
|
});
|
|
16882
17014
|
|
|
17015
|
+
// src/scripts/prepareBrowserAuth.ts
|
|
17016
|
+
import * as fs42 from "fs";
|
|
17017
|
+
import * as os7 from "os";
|
|
17018
|
+
import * as path40 from "path";
|
|
17019
|
+
function appendAuthMessage(ctx, message) {
|
|
17020
|
+
const current = typeof ctx.data.qaAuthBlock === "string" ? ctx.data.qaAuthBlock.trim() : "";
|
|
17021
|
+
ctx.data.qaAuthBlock = current ? `${current}
|
|
17022
|
+
|
|
17023
|
+
${message}` : message;
|
|
17024
|
+
}
|
|
17025
|
+
function githubRepositoryParts(repoUrl) {
|
|
17026
|
+
const parsed = new URL(repoUrl);
|
|
17027
|
+
if (parsed.protocol !== "https:" || parsed.hostname.toLowerCase() !== "github.com") {
|
|
17028
|
+
throw new Error("repository must be an https://github.com/owner/repo URL");
|
|
17029
|
+
}
|
|
17030
|
+
const parts = parsed.pathname.split("/").filter(Boolean);
|
|
17031
|
+
if (parts.length !== 2) throw new Error("repository URL must contain exactly owner/repo");
|
|
17032
|
+
const repo = parts[1].replace(/\.git$/, "");
|
|
17033
|
+
if (!parts[0] || !repo) throw new Error("repository URL is incomplete");
|
|
17034
|
+
return { owner: parts[0], repo };
|
|
17035
|
+
}
|
|
17036
|
+
function browserOrigin(ctx) {
|
|
17037
|
+
const raw = typeof ctx.data.previewUrl === "string" ? ctx.data.previewUrl : "";
|
|
17038
|
+
if (!raw) throw new Error("QA target URL is unavailable");
|
|
17039
|
+
const parsed = new URL(raw);
|
|
17040
|
+
if (parsed.protocol !== "https:" && parsed.protocol !== "http:") {
|
|
17041
|
+
throw new Error("QA target URL must use http or https");
|
|
17042
|
+
}
|
|
17043
|
+
return parsed.origin;
|
|
17044
|
+
}
|
|
17045
|
+
async function githubJson(url, token) {
|
|
17046
|
+
const response = await fetch(url, {
|
|
17047
|
+
headers: {
|
|
17048
|
+
Accept: "application/vnd.github+json",
|
|
17049
|
+
Authorization: `Bearer ${token}`,
|
|
17050
|
+
"X-GitHub-Api-Version": "2022-11-28"
|
|
17051
|
+
}
|
|
17052
|
+
});
|
|
17053
|
+
if (!response.ok) throw new Error(`GitHub returned ${response.status}`);
|
|
17054
|
+
return await response.json();
|
|
17055
|
+
}
|
|
17056
|
+
function writeKodyStorageState(input) {
|
|
17057
|
+
const directory = fs42.mkdtempSync(path40.join(os7.tmpdir(), "kody-browser-auth-"));
|
|
17058
|
+
fs42.chmodSync(directory, 448);
|
|
17059
|
+
const file = path40.join(directory, "storage-state.json");
|
|
17060
|
+
const now = Date.now();
|
|
17061
|
+
const repoEntry = {
|
|
17062
|
+
repoUrl: input.repoUrl,
|
|
17063
|
+
owner: input.owner,
|
|
17064
|
+
repo: input.repo,
|
|
17065
|
+
token: input.token,
|
|
17066
|
+
addedAt: now,
|
|
17067
|
+
isLogin: true,
|
|
17068
|
+
user: input.user
|
|
17069
|
+
};
|
|
17070
|
+
const auth = {
|
|
17071
|
+
repoUrl: input.repoUrl,
|
|
17072
|
+
owner: input.owner,
|
|
17073
|
+
repo: input.repo,
|
|
17074
|
+
token: input.token,
|
|
17075
|
+
user: input.user,
|
|
17076
|
+
loggedInAt: now,
|
|
17077
|
+
repos: [repoEntry],
|
|
17078
|
+
currentRepoIndex: 0
|
|
17079
|
+
};
|
|
17080
|
+
const storageState = {
|
|
17081
|
+
cookies: [],
|
|
17082
|
+
origins: [
|
|
17083
|
+
{
|
|
17084
|
+
origin: input.origin,
|
|
17085
|
+
localStorage: [{ name: "kody_auth", value: JSON.stringify(auth) }]
|
|
17086
|
+
}
|
|
17087
|
+
]
|
|
17088
|
+
};
|
|
17089
|
+
fs42.writeFileSync(file, JSON.stringify(storageState), { mode: 384 });
|
|
17090
|
+
return { directory, file };
|
|
17091
|
+
}
|
|
17092
|
+
function configurePlaywright(profile, storageStatePath) {
|
|
17093
|
+
const playwright = profile.claudeCode.mcpServers.find((server) => server.name === "playwright");
|
|
17094
|
+
if (!playwright) throw new Error("Playwright MCP server is not configured");
|
|
17095
|
+
const args = [];
|
|
17096
|
+
const currentArgs = playwright.args ?? [];
|
|
17097
|
+
for (let index = 0; index < currentArgs.length; index += 1) {
|
|
17098
|
+
const arg = currentArgs[index];
|
|
17099
|
+
if (arg === "--storage-state") {
|
|
17100
|
+
index += 1;
|
|
17101
|
+
continue;
|
|
17102
|
+
}
|
|
17103
|
+
if (arg.startsWith("--storage-state=")) continue;
|
|
17104
|
+
args.push(arg);
|
|
17105
|
+
}
|
|
17106
|
+
if (!args.includes("--isolated")) args.push("--isolated");
|
|
17107
|
+
args.push("--storage-state", storageStatePath);
|
|
17108
|
+
playwright.args = args;
|
|
17109
|
+
}
|
|
17110
|
+
function fieldsForKodyRepository(method) {
|
|
17111
|
+
const variables = method.fields.filter((field) => field.source === "variable");
|
|
17112
|
+
const secrets = method.fields.filter((field) => field.source === "secret");
|
|
17113
|
+
if (variables.length !== 1 || secrets.length !== 1) {
|
|
17114
|
+
throw new Error("kody-repository auth requires one variable field and one secret field");
|
|
17115
|
+
}
|
|
17116
|
+
return { repositoryKey: variables[0].key, credentialKey: secrets[0].key };
|
|
17117
|
+
}
|
|
17118
|
+
async function prepareMethod(ctx, profile, method) {
|
|
17119
|
+
const { repositoryKey, credentialKey } = fieldsForKodyRepository(method);
|
|
17120
|
+
const variables = readKodyVariables(ctx.cwd);
|
|
17121
|
+
const repositoryUrl = variables[repositoryKey]?.trim() ?? "";
|
|
17122
|
+
const credential = await resolveRuntimeSecret(credentialKey, ctx);
|
|
17123
|
+
ctx.data.qaAuthSecretSources = {
|
|
17124
|
+
...ctx.data.qaAuthSecretSources ?? {},
|
|
17125
|
+
[credentialKey]: credential.source
|
|
17126
|
+
};
|
|
17127
|
+
if (credential.warning) {
|
|
17128
|
+
const warnings = Array.isArray(ctx.data.qaAuthWarnings) ? ctx.data.qaAuthWarnings : [];
|
|
17129
|
+
ctx.data.qaAuthWarnings = [...warnings, credential.warning];
|
|
17130
|
+
}
|
|
17131
|
+
if (!repositoryUrl && !credential.value) return false;
|
|
17132
|
+
if (!repositoryUrl) {
|
|
17133
|
+
appendAuthMessage(
|
|
17134
|
+
ctx,
|
|
17135
|
+
`Auth: ${method.name} is incomplete because no \`${repositoryKey}\` variable was found. Note this authenticated surface as a gap.`
|
|
17136
|
+
);
|
|
17137
|
+
return false;
|
|
17138
|
+
}
|
|
17139
|
+
if (!credential.value) {
|
|
17140
|
+
appendAuthMessage(
|
|
17141
|
+
ctx,
|
|
17142
|
+
`Auth: ${method.name} is incomplete because no \`${credentialKey}\` secret was found. Note this authenticated surface as a gap.`
|
|
17143
|
+
);
|
|
17144
|
+
return false;
|
|
17145
|
+
}
|
|
17146
|
+
let state;
|
|
17147
|
+
try {
|
|
17148
|
+
const requested = githubRepositoryParts(repositoryUrl);
|
|
17149
|
+
const [user, repository] = await Promise.all([
|
|
17150
|
+
githubJson("https://api.github.com/user", credential.value),
|
|
17151
|
+
githubJson(
|
|
17152
|
+
`https://api.github.com/repos/${encodeURIComponent(requested.owner)}/${encodeURIComponent(requested.repo)}`,
|
|
17153
|
+
credential.value
|
|
17154
|
+
)
|
|
17155
|
+
]);
|
|
17156
|
+
const [owner, repo] = repository.full_name.split("/");
|
|
17157
|
+
if (!owner || !repo || !user.login || !user.avatar_url || typeof user.id !== "number") {
|
|
17158
|
+
throw new Error("GitHub returned incomplete identity data");
|
|
17159
|
+
}
|
|
17160
|
+
state = writeKodyStorageState({
|
|
17161
|
+
origin: browserOrigin(ctx),
|
|
17162
|
+
repoUrl: `https://github.com/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}`,
|
|
17163
|
+
owner,
|
|
17164
|
+
repo,
|
|
17165
|
+
token: credential.value,
|
|
17166
|
+
user
|
|
17167
|
+
});
|
|
17168
|
+
configurePlaywright(profile, state.file);
|
|
17169
|
+
const authDirectory = state.directory;
|
|
17170
|
+
registerRuntimeCleanup(ctx, () => {
|
|
17171
|
+
fs42.rmSync(authDirectory, { recursive: true, force: true });
|
|
17172
|
+
});
|
|
17173
|
+
appendAuthMessage(
|
|
17174
|
+
ctx,
|
|
17175
|
+
`Auth: ${method.name} is already authenticated by the engine-provided browser session. The credential is not available to you; never request, reveal, or report it.`
|
|
17176
|
+
);
|
|
17177
|
+
return true;
|
|
17178
|
+
} catch (error) {
|
|
17179
|
+
if (state) fs42.rmSync(state.directory, { recursive: true, force: true });
|
|
17180
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
17181
|
+
appendAuthMessage(
|
|
17182
|
+
ctx,
|
|
17183
|
+
`Auth: the engine could not prepare ${method.name} (${reason}). Note this authenticated surface as a gap.`
|
|
17184
|
+
);
|
|
17185
|
+
return false;
|
|
17186
|
+
}
|
|
17187
|
+
}
|
|
17188
|
+
var prepareBrowserAuth;
|
|
17189
|
+
var init_prepareBrowserAuth = __esm({
|
|
17190
|
+
"src/scripts/prepareBrowserAuth.ts"() {
|
|
17191
|
+
"use strict";
|
|
17192
|
+
init_runtimeCleanup();
|
|
17193
|
+
init_kodyVariables();
|
|
17194
|
+
init_runtimeSecrets();
|
|
17195
|
+
prepareBrowserAuth = async (ctx, profile) => {
|
|
17196
|
+
const methods = profile.auth?.methods ?? [];
|
|
17197
|
+
for (const method of methods) {
|
|
17198
|
+
if (method.strategy !== "browser-storage-state" || method.adapter !== "kody-repository") continue;
|
|
17199
|
+
if (await prepareMethod(ctx, profile, method)) return;
|
|
17200
|
+
}
|
|
17201
|
+
};
|
|
17202
|
+
}
|
|
17203
|
+
});
|
|
17204
|
+
|
|
16883
17205
|
// src/scripts/promoteQaGoal.ts
|
|
16884
17206
|
var REPORT_JSON_OPEN2, promoteQaGoal;
|
|
16885
17207
|
var init_promoteQaGoal = __esm({
|
|
@@ -16967,9 +17289,9 @@ function latestResult(raw, agentResult) {
|
|
|
16967
17289
|
function recordField6(value) {
|
|
16968
17290
|
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
16969
17291
|
}
|
|
16970
|
-
function resolveDotted(root,
|
|
16971
|
-
if (!
|
|
16972
|
-
return
|
|
17292
|
+
function resolveDotted(root, path53) {
|
|
17293
|
+
if (!path53) return void 0;
|
|
17294
|
+
return path53.split(".").reduce((value, key) => recordField6(value)?.[key], root);
|
|
16973
17295
|
}
|
|
16974
17296
|
function stringValue4(value) {
|
|
16975
17297
|
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
@@ -17948,12 +18270,12 @@ fi
|
|
|
17948
18270
|
|
|
17949
18271
|
// src/scripts/runPreviewBuild.ts
|
|
17950
18272
|
import { copyFile, writeFile } from "fs/promises";
|
|
17951
|
-
import * as
|
|
18273
|
+
import * as path41 from "path";
|
|
17952
18274
|
import { fileURLToPath } from "url";
|
|
17953
18275
|
function bundledDockerfilePath(mode) {
|
|
17954
|
-
const here =
|
|
18276
|
+
const here = path41.dirname(fileURLToPath(import.meta.url));
|
|
17955
18277
|
const file = mode === "dev" ? "default-Dockerfile.preview.dev" : "default-Dockerfile.preview.prod";
|
|
17956
|
-
return
|
|
18278
|
+
return path41.join(here, "preview-build-templates", file);
|
|
17957
18279
|
}
|
|
17958
18280
|
function required(name) {
|
|
17959
18281
|
const v = (process.env[name] ?? "").trim();
|
|
@@ -18204,10 +18526,10 @@ var init_runPreviewBuild = __esm({
|
|
|
18204
18526
|
console.log(`[preview-build] vault: ${Object.keys(buildEnv).length} secrets, mode=${buildMode}`);
|
|
18205
18527
|
if (Object.keys(buildEnv).length > 0) {
|
|
18206
18528
|
const lines = Object.entries(buildEnv).map(([k, v]) => `${k}=${JSON.stringify(v)}`);
|
|
18207
|
-
await writeFile(
|
|
18529
|
+
await writeFile(path41.join(ctx.cwd, ".env.production.local"), `${lines.join("\n")}
|
|
18208
18530
|
`, "utf8");
|
|
18209
18531
|
}
|
|
18210
|
-
const consumerDockerfile =
|
|
18532
|
+
const consumerDockerfile = path41.join(ctx.cwd, "Dockerfile.preview");
|
|
18211
18533
|
const { stat } = await import("fs/promises");
|
|
18212
18534
|
let hasConsumerDockerfile = false;
|
|
18213
18535
|
try {
|
|
@@ -18391,8 +18713,8 @@ var init_tickShellRunner = __esm({
|
|
|
18391
18713
|
});
|
|
18392
18714
|
|
|
18393
18715
|
// src/scripts/runScheduledImplementationTick.ts
|
|
18394
|
-
import * as
|
|
18395
|
-
import * as
|
|
18716
|
+
import * as fs43 from "fs";
|
|
18717
|
+
import * as path42 from "path";
|
|
18396
18718
|
var runScheduledImplementationTick;
|
|
18397
18719
|
var init_runScheduledImplementationTick = __esm({
|
|
18398
18720
|
"src/scripts/runScheduledImplementationTick.ts"() {
|
|
@@ -18412,14 +18734,14 @@ var init_runScheduledImplementationTick = __esm({
|
|
|
18412
18734
|
ctx.output.reason = `runScheduledImplementationTick: args.slug or ctx.args.${slugArg} must be non-empty capability slug`;
|
|
18413
18735
|
return;
|
|
18414
18736
|
}
|
|
18415
|
-
const capability = resolveCapabilityFolder(slug,
|
|
18737
|
+
const capability = resolveCapabilityFolder(slug, path42.join(ctx.cwd, jobsDir));
|
|
18416
18738
|
if (!capability) {
|
|
18417
18739
|
ctx.output.exitCode = 99;
|
|
18418
18740
|
ctx.output.reason = `runScheduledImplementationTick: capability folder not found or incomplete: ${slug} (searched ${jobsDir} and company store)`;
|
|
18419
18741
|
return;
|
|
18420
18742
|
}
|
|
18421
|
-
const shellPath =
|
|
18422
|
-
if (!
|
|
18743
|
+
const shellPath = path42.join(profile.dir, shell);
|
|
18744
|
+
if (!fs43.existsSync(shellPath)) {
|
|
18423
18745
|
ctx.output.exitCode = 99;
|
|
18424
18746
|
ctx.output.reason = `runScheduledImplementationTick: shell not found: ${shell} (looked in ${profile.dir})`;
|
|
18425
18747
|
return;
|
|
@@ -18450,8 +18772,8 @@ var init_runScheduledImplementationTick = __esm({
|
|
|
18450
18772
|
});
|
|
18451
18773
|
|
|
18452
18774
|
// src/scripts/runTickScript.ts
|
|
18453
|
-
import * as
|
|
18454
|
-
import * as
|
|
18775
|
+
import * as fs44 from "fs";
|
|
18776
|
+
import * as path43 from "path";
|
|
18455
18777
|
var runTickScript;
|
|
18456
18778
|
var init_runTickScript = __esm({
|
|
18457
18779
|
"src/scripts/runTickScript.ts"() {
|
|
@@ -18470,10 +18792,10 @@ var init_runTickScript = __esm({
|
|
|
18470
18792
|
ctx.output.reason = `runTickScript: ctx.args.${slugArg} must be a non-empty slug`;
|
|
18471
18793
|
return;
|
|
18472
18794
|
}
|
|
18473
|
-
const capability = readCapabilityFolder(
|
|
18795
|
+
const capability = readCapabilityFolder(path43.join(ctx.cwd, jobsDir), slug);
|
|
18474
18796
|
if (!capability) {
|
|
18475
18797
|
ctx.output.exitCode = 99;
|
|
18476
|
-
ctx.output.reason = `runTickScript: capability folder not found or incomplete: ${
|
|
18798
|
+
ctx.output.reason = `runTickScript: capability folder not found or incomplete: ${path43.join(ctx.cwd, jobsDir, slug)}`;
|
|
18477
18799
|
return;
|
|
18478
18800
|
}
|
|
18479
18801
|
const tickScript = capability.config.tickScript;
|
|
@@ -18482,8 +18804,8 @@ var init_runTickScript = __esm({
|
|
|
18482
18804
|
ctx.output.reason = `runTickScript: capability ${slug} has no \`tickScript\` in profile.json \u2014 route via capability-tick instead`;
|
|
18483
18805
|
return;
|
|
18484
18806
|
}
|
|
18485
|
-
const scriptPath =
|
|
18486
|
-
if (!
|
|
18807
|
+
const scriptPath = path43.isAbsolute(tickScript) ? tickScript : path43.join(ctx.cwd, tickScript);
|
|
18808
|
+
if (!fs44.existsSync(scriptPath)) {
|
|
18487
18809
|
ctx.output.exitCode = 99;
|
|
18488
18810
|
ctx.output.reason = `runTickScript: tickScript not found: ${scriptPath}`;
|
|
18489
18811
|
return;
|
|
@@ -18765,7 +19087,7 @@ var init_syncFlow = __esm({
|
|
|
18765
19087
|
});
|
|
18766
19088
|
|
|
18767
19089
|
// src/scripts/validateAgencyModelProposal.ts
|
|
18768
|
-
import * as
|
|
19090
|
+
import * as path44 from "path";
|
|
18769
19091
|
function validateModelBundle(bundle, expectedKind, options = {}) {
|
|
18770
19092
|
const failures = [];
|
|
18771
19093
|
validateOneModel(bundle.model, bundle.files, "model", true, failures, expectedKind, options);
|
|
@@ -18862,8 +19184,11 @@ function validateFilesForKind(kind, slug, files, strictSingleModel, failures, op
|
|
|
18862
19184
|
const known = options.capabilityRoot ? getCapabilityRoots(options.capabilityRoot).flatMap((root) => listCapabilityFolderSlugs(root)) : [];
|
|
18863
19185
|
const uniqueKnown = [...new Set(known)];
|
|
18864
19186
|
const capabilityInputs = /* @__PURE__ */ new Map();
|
|
19187
|
+
const capabilityOutputs = /* @__PURE__ */ new Map();
|
|
18865
19188
|
if (options.capabilityRoot) {
|
|
18866
19189
|
for (const capability of uniqueKnown) {
|
|
19190
|
+
const folder = getCapabilityRoots(options.capabilityRoot).map((root) => readCapabilityFolder(root, capability)).find((entry) => entry !== null);
|
|
19191
|
+
capabilityOutputs.set(capability, folder ? capabilityOutputConditionPaths(folder.config) : /* @__PURE__ */ new Set());
|
|
18867
19192
|
const inputs = getCapabilityActionInputs(capability, options.capabilityRoot);
|
|
18868
19193
|
if (inputs) {
|
|
18869
19194
|
capabilityInputs.set(
|
|
@@ -18877,7 +19202,8 @@ function validateFilesForKind(kind, slug, files, strictSingleModel, failures, op
|
|
|
18877
19202
|
...formatWorkflowValidationIssues(
|
|
18878
19203
|
validateWorkflow(workflow, {
|
|
18879
19204
|
...uniqueKnown.length > 0 ? { knownCapabilities: new Set(uniqueKnown) } : {},
|
|
18880
|
-
...capabilityInputs.size > 0 ? { capabilityInputs } : {}
|
|
19205
|
+
...capabilityInputs.size > 0 ? { capabilityInputs } : {},
|
|
19206
|
+
...capabilityOutputs.size > 0 ? { capabilityOutputs } : {}
|
|
18881
19207
|
})
|
|
18882
19208
|
)
|
|
18883
19209
|
);
|
|
@@ -19087,7 +19413,7 @@ var init_validateAgencyModelProposal = __esm({
|
|
|
19087
19413
|
const bundle = parseAgencyModelProposal(raw);
|
|
19088
19414
|
const expectedKind = readExpectedModelKind(args);
|
|
19089
19415
|
const failures = validateModelBundle(bundle, expectedKind, {
|
|
19090
|
-
capabilityRoot:
|
|
19416
|
+
capabilityRoot: path44.join(ctx.cwd, ".kody", "capabilities")
|
|
19091
19417
|
});
|
|
19092
19418
|
if (failures.length > 0) {
|
|
19093
19419
|
throw new Error(`validateAgencyModelProposal: ${failures.join("; ")}`);
|
|
@@ -19638,7 +19964,7 @@ var init_warmupMcp = __esm({
|
|
|
19638
19964
|
});
|
|
19639
19965
|
|
|
19640
19966
|
// src/scripts/writeAgentRunSummary.ts
|
|
19641
|
-
import * as
|
|
19967
|
+
import * as fs45 from "fs";
|
|
19642
19968
|
var writeAgentRunSummary;
|
|
19643
19969
|
var init_writeAgentRunSummary = __esm({
|
|
19644
19970
|
"src/scripts/writeAgentRunSummary.ts"() {
|
|
@@ -19664,7 +19990,7 @@ var init_writeAgentRunSummary = __esm({
|
|
|
19664
19990
|
if (reason) lines.push(`- **Reason:** ${reason}`);
|
|
19665
19991
|
lines.push("");
|
|
19666
19992
|
try {
|
|
19667
|
-
|
|
19993
|
+
fs45.appendFileSync(summaryPath, `${lines.join("\n")}
|
|
19668
19994
|
`);
|
|
19669
19995
|
} catch {
|
|
19670
19996
|
}
|
|
@@ -19843,6 +20169,7 @@ var init_scripts = __esm({
|
|
|
19843
20169
|
init_postPlanComment();
|
|
19844
20170
|
init_postResearchComment();
|
|
19845
20171
|
init_postReviewResult();
|
|
20172
|
+
init_prepareBrowserAuth();
|
|
19846
20173
|
init_promoteQaGoal();
|
|
19847
20174
|
init_publishReport();
|
|
19848
20175
|
init_recordClassification();
|
|
@@ -19901,6 +20228,7 @@ var init_scripts = __esm({
|
|
|
19901
20228
|
loadMemoryContext,
|
|
19902
20229
|
loadPriorArt,
|
|
19903
20230
|
loadQaContext,
|
|
20231
|
+
prepareBrowserAuth,
|
|
19904
20232
|
buildSyntheticPlugin,
|
|
19905
20233
|
resolveArtifacts,
|
|
19906
20234
|
discoverQaContext,
|
|
@@ -19987,53 +20315,53 @@ var init_scripts = __esm({
|
|
|
19987
20315
|
// src/stateWorkspace.ts
|
|
19988
20316
|
import { execFileSync as execFileSync24 } from "child_process";
|
|
19989
20317
|
import * as crypto3 from "crypto";
|
|
19990
|
-
import * as
|
|
19991
|
-
import * as
|
|
19992
|
-
import * as
|
|
20318
|
+
import * as fs46 from "fs";
|
|
20319
|
+
import * as os8 from "os";
|
|
20320
|
+
import * as path45 from "path";
|
|
19993
20321
|
function writeLocalFile(cwd, relativePath, content) {
|
|
19994
|
-
const fullPath =
|
|
19995
|
-
|
|
19996
|
-
|
|
20322
|
+
const fullPath = path45.join(cwd, relativePath);
|
|
20323
|
+
fs46.mkdirSync(path45.dirname(fullPath), { recursive: true });
|
|
20324
|
+
fs46.writeFileSync(fullPath, content);
|
|
19997
20325
|
}
|
|
19998
20326
|
function copyPath(source, target) {
|
|
19999
|
-
const st =
|
|
20000
|
-
|
|
20327
|
+
const st = fs46.lstatSync(source);
|
|
20328
|
+
fs46.rmSync(target, { recursive: true, force: true });
|
|
20001
20329
|
if (st.isSymbolicLink()) return;
|
|
20002
|
-
|
|
20003
|
-
|
|
20330
|
+
fs46.mkdirSync(path45.dirname(target), { recursive: true });
|
|
20331
|
+
fs46.cpSync(source, target, { recursive: true, force: true });
|
|
20004
20332
|
}
|
|
20005
20333
|
function overlayDirectoryChildren(cwd, sourceDir, localDir) {
|
|
20006
|
-
if (!
|
|
20007
|
-
for (const entry of
|
|
20008
|
-
const source =
|
|
20009
|
-
const target =
|
|
20334
|
+
if (!fs46.existsSync(sourceDir)) return;
|
|
20335
|
+
for (const entry of fs46.readdirSync(sourceDir, { withFileTypes: true })) {
|
|
20336
|
+
const source = path45.join(sourceDir, entry.name);
|
|
20337
|
+
const target = path45.join(cwd, localDir, entry.name);
|
|
20010
20338
|
copyPath(source, target);
|
|
20011
20339
|
}
|
|
20012
20340
|
}
|
|
20013
20341
|
function hydrateStateWorkspace(config, cwd) {
|
|
20014
20342
|
if (process.env.VITEST && process.env[TEST_FETCH_ENV] !== "1") return;
|
|
20015
20343
|
const parsed = parseStateRepo(config);
|
|
20016
|
-
const hydrateKey = `${
|
|
20344
|
+
const hydrateKey = `${path45.resolve(cwd)}|${parsed.owner}/${parsed.repo}|${parsed.basePath}|${parsed.branch}`;
|
|
20017
20345
|
if (hydratedWorkspaces.has(hydrateKey)) return;
|
|
20018
20346
|
const snapshotRoot = fetchStateSnapshot(parsed);
|
|
20019
20347
|
for (const mapping of DIR_MAPPINGS) {
|
|
20020
|
-
overlayDirectoryChildren(cwd,
|
|
20348
|
+
overlayDirectoryChildren(cwd, path45.join(snapshotRoot, mapping.stateDir), mapping.localDir);
|
|
20021
20349
|
}
|
|
20022
20350
|
for (const mapping of FILE_MAPPINGS) {
|
|
20023
|
-
const source =
|
|
20024
|
-
if (
|
|
20025
|
-
writeLocalFile(cwd, mapping.localPath,
|
|
20351
|
+
const source = path45.join(snapshotRoot, mapping.statePath);
|
|
20352
|
+
if (fs46.existsSync(source) && !fs46.lstatSync(source).isSymbolicLink() && fs46.statSync(source).isFile()) {
|
|
20353
|
+
writeLocalFile(cwd, mapping.localPath, fs46.readFileSync(source, "utf-8"));
|
|
20026
20354
|
}
|
|
20027
20355
|
}
|
|
20028
20356
|
hydratedWorkspaces.add(hydrateKey);
|
|
20029
20357
|
}
|
|
20030
20358
|
function fetchStateSnapshot(parsed) {
|
|
20031
|
-
const cacheDir =
|
|
20359
|
+
const cacheDir = path45.join(cacheRoot2(), cacheKey3(parsed));
|
|
20032
20360
|
const url = `https://github.com/${parsed.owner}/${parsed.repo}.git`;
|
|
20033
20361
|
try {
|
|
20034
|
-
|
|
20035
|
-
if (!
|
|
20036
|
-
|
|
20362
|
+
fs46.mkdirSync(path45.dirname(cacheDir), { recursive: true });
|
|
20363
|
+
if (!fs46.existsSync(path45.join(cacheDir, ".git"))) {
|
|
20364
|
+
fs46.rmSync(cacheDir, { recursive: true, force: true });
|
|
20037
20365
|
runGit3(["clone", "--no-checkout", "--filter=blob:none", url, cacheDir]);
|
|
20038
20366
|
}
|
|
20039
20367
|
runGit3(["-C", cacheDir, "remote", "set-url", "origin", url]);
|
|
@@ -20048,10 +20376,10 @@ function fetchStateSnapshot(parsed) {
|
|
|
20048
20376
|
`stateWorkspace: failed to fetch ${parsed.owner}/${parsed.repo}:${parsed.basePath}@${parsed.branch}: ${msg}`
|
|
20049
20377
|
);
|
|
20050
20378
|
}
|
|
20051
|
-
return
|
|
20379
|
+
return path45.join(cacheDir, parsed.basePath);
|
|
20052
20380
|
}
|
|
20053
20381
|
function cacheRoot2() {
|
|
20054
|
-
return process.env[CACHE_ENV2]?.trim() ||
|
|
20382
|
+
return process.env[CACHE_ENV2]?.trim() || path45.join(os8.homedir(), ".cache", "kody", "state-repo");
|
|
20055
20383
|
}
|
|
20056
20384
|
function cacheKey3(parsed) {
|
|
20057
20385
|
return crypto3.createHash("sha256").update(`${parsed.owner}/${parsed.repo}#${parsed.branch}#${parsed.basePath}`).digest("hex").slice(0, 24);
|
|
@@ -20091,16 +20419,16 @@ var init_stateWorkspace = __esm({
|
|
|
20091
20419
|
"use strict";
|
|
20092
20420
|
init_stateRepo();
|
|
20093
20421
|
DIR_MAPPINGS = [
|
|
20094
|
-
{ stateDir: "capabilities", localDir:
|
|
20095
|
-
{ stateDir: "agents", localDir:
|
|
20096
|
-
{ stateDir: "context", localDir:
|
|
20097
|
-
{ stateDir: "memory", localDir:
|
|
20422
|
+
{ stateDir: "capabilities", localDir: path45.join(".kody", "capabilities") },
|
|
20423
|
+
{ stateDir: "agents", localDir: path45.join(".kody", "agents") },
|
|
20424
|
+
{ stateDir: "context", localDir: path45.join(".kody", "context") },
|
|
20425
|
+
{ stateDir: "memory", localDir: path45.join(".kody", "memory") }
|
|
20098
20426
|
];
|
|
20099
20427
|
FILE_MAPPINGS = [
|
|
20100
|
-
{ statePath: "instructions.md", localPath:
|
|
20101
|
-
{ statePath: "system-prompt.md", localPath:
|
|
20102
|
-
{ statePath: "variables.json", localPath:
|
|
20103
|
-
{ statePath: "secrets.enc", localPath:
|
|
20428
|
+
{ statePath: "instructions.md", localPath: path45.join(".kody", "instructions.md") },
|
|
20429
|
+
{ statePath: "system-prompt.md", localPath: path45.join(".kody", "system-prompt.md") },
|
|
20430
|
+
{ statePath: "variables.json", localPath: path45.join(".kody", "variables.json") },
|
|
20431
|
+
{ statePath: "secrets.enc", localPath: path45.join(".kody", "secrets.enc") }
|
|
20104
20432
|
];
|
|
20105
20433
|
CACHE_ENV2 = "KODY_STATE_REPO_CACHE";
|
|
20106
20434
|
TEST_FETCH_ENV = "KODY_STATE_WORKSPACE_FETCH_FOR_TESTS";
|
|
@@ -20174,9 +20502,9 @@ var init_tools = __esm({
|
|
|
20174
20502
|
|
|
20175
20503
|
// src/executor.ts
|
|
20176
20504
|
import { spawn as spawn7 } from "child_process";
|
|
20177
|
-
import * as
|
|
20178
|
-
import * as
|
|
20179
|
-
import * as
|
|
20505
|
+
import * as fs47 from "fs";
|
|
20506
|
+
import * as os9 from "os";
|
|
20507
|
+
import * as path46 from "path";
|
|
20180
20508
|
function isMutatingPostflight(scriptName) {
|
|
20181
20509
|
return MUTATING_POSTFLIGHTS.has(scriptName ?? "");
|
|
20182
20510
|
}
|
|
@@ -20399,7 +20727,7 @@ async function runImplementation(profileName, input) {
|
|
|
20399
20727
|
const jobWhyBlock = typeof ctx.data.jobWhy === "string" ? operatorRequestBlock(ctx.data.jobWhy) : null;
|
|
20400
20728
|
const jobRefBlock = jobReferenceBlock(profileName, profile, ctx.data);
|
|
20401
20729
|
const invokeAgent = async (prompt) => {
|
|
20402
|
-
const externalPlugins = (profile.claudeCode.plugins ?? []).map((p) =>
|
|
20730
|
+
const externalPlugins = (profile.claudeCode.plugins ?? []).map((p) => path46.isAbsolute(p) ? p : path46.resolve(profile.dir, p)).filter((p) => p.length > 0);
|
|
20403
20731
|
const syntheticPath = ctx.data.syntheticPluginPath;
|
|
20404
20732
|
const pluginPaths = [...externalPlugins, ...syntheticPath ? [syntheticPath] : []];
|
|
20405
20733
|
const agents = loadSubagents(profile);
|
|
@@ -20655,6 +20983,7 @@ async function runImplementation(profileName, input) {
|
|
|
20655
20983
|
const msg = err instanceof Error ? err.message : String(err);
|
|
20656
20984
|
return finishAndEnd({ exitCode: 99, reason: ctx.output.reason ?? msg });
|
|
20657
20985
|
} finally {
|
|
20986
|
+
runRuntimeCleanup(ctx);
|
|
20658
20987
|
clearStampedLifecycleLabels(profile, ctx);
|
|
20659
20988
|
if (taskArtifacts) {
|
|
20660
20989
|
try {
|
|
@@ -20837,17 +21166,17 @@ function clearStampedLifecycleLabels(profile, ctx) {
|
|
|
20837
21166
|
function resolveProfilePath(profileName) {
|
|
20838
21167
|
const found = resolveImplementation(profileName);
|
|
20839
21168
|
if (found) return found;
|
|
20840
|
-
const here =
|
|
21169
|
+
const here = path46.dirname(new URL(import.meta.url).pathname);
|
|
20841
21170
|
const candidates = [
|
|
20842
|
-
|
|
21171
|
+
path46.join(here, "implementations", profileName, "profile.json"),
|
|
20843
21172
|
// same-dir sibling (dev)
|
|
20844
|
-
|
|
21173
|
+
path46.join(here, "..", "implementations", profileName, "profile.json"),
|
|
20845
21174
|
// up one (prod: dist/bin → dist/implementations)
|
|
20846
|
-
|
|
21175
|
+
path46.join(here, "..", "src", "implementations", profileName, "profile.json")
|
|
20847
21176
|
// fallback
|
|
20848
21177
|
];
|
|
20849
21178
|
for (const c of candidates) {
|
|
20850
|
-
if (
|
|
21179
|
+
if (fs47.existsSync(c)) return c;
|
|
20851
21180
|
}
|
|
20852
21181
|
return candidates[0];
|
|
20853
21182
|
}
|
|
@@ -20962,16 +21291,16 @@ function resolveShellTimeoutMs(entry) {
|
|
|
20962
21291
|
}
|
|
20963
21292
|
async function runShellEntry(entry, ctx, profile) {
|
|
20964
21293
|
const shellName = entry.shell;
|
|
20965
|
-
const shellPath =
|
|
20966
|
-
if (!
|
|
21294
|
+
const shellPath = path46.join(profile.dir, shellName);
|
|
21295
|
+
if (!fs47.existsSync(shellPath)) {
|
|
20967
21296
|
ctx.skipAgent = true;
|
|
20968
21297
|
ctx.output.exitCode = 99;
|
|
20969
21298
|
ctx.output.reason = `shell script not found: ${shellName} (looked in ${profile.dir})`;
|
|
20970
21299
|
return;
|
|
20971
21300
|
}
|
|
20972
21301
|
const positional = entry.with ? Object.values(entry.with).map((v) => String(v)) : [];
|
|
20973
|
-
const outputFile =
|
|
20974
|
-
|
|
21302
|
+
const outputFile = path46.join(
|
|
21303
|
+
os9.tmpdir(),
|
|
20975
21304
|
`kody-shell-output-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`
|
|
20976
21305
|
);
|
|
20977
21306
|
const env = { ...process.env, HUSKY: "0", SKIP_HOOKS: "1", KODY_OUTPUT: outputFile };
|
|
@@ -21042,9 +21371,9 @@ async function runShellEntry(entry, ctx, profile) {
|
|
|
21042
21371
|
}
|
|
21043
21372
|
let sideChannelText = "";
|
|
21044
21373
|
try {
|
|
21045
|
-
if (
|
|
21046
|
-
sideChannelText =
|
|
21047
|
-
|
|
21374
|
+
if (fs47.existsSync(outputFile)) {
|
|
21375
|
+
sideChannelText = fs47.readFileSync(outputFile, "utf-8");
|
|
21376
|
+
fs47.rmSync(outputFile, { force: true });
|
|
21048
21377
|
}
|
|
21049
21378
|
} catch {
|
|
21050
21379
|
}
|
|
@@ -21117,6 +21446,7 @@ var init_executor = __esm({
|
|
|
21117
21446
|
init_profile();
|
|
21118
21447
|
init_registry();
|
|
21119
21448
|
init_runIndex();
|
|
21449
|
+
init_runtimeCleanup();
|
|
21120
21450
|
init_runtimePaths();
|
|
21121
21451
|
init_evaluateAgencyBoundaries();
|
|
21122
21452
|
init_scripts();
|
|
@@ -21181,11 +21511,11 @@ function readWorkflowRunState(config, cwd, workflowId, runId) {
|
|
|
21181
21511
|
}
|
|
21182
21512
|
}
|
|
21183
21513
|
function writeWorkflowRunState(config, cwd, workflowId, runId, state) {
|
|
21184
|
-
const
|
|
21514
|
+
const path53 = workflowRunStatePath(workflowId, runId);
|
|
21185
21515
|
upsertStateText(
|
|
21186
21516
|
config,
|
|
21187
21517
|
cwd,
|
|
21188
|
-
|
|
21518
|
+
path53,
|
|
21189
21519
|
`${JSON.stringify(state, null, 2)}
|
|
21190
21520
|
`,
|
|
21191
21521
|
`chore(workflows): update ${workflowId} run ${runId}`
|
|
@@ -21212,7 +21542,7 @@ __export(job_exports, {
|
|
|
21212
21542
|
stableJobKey: () => stableJobKey,
|
|
21213
21543
|
validateJob: () => validateJob
|
|
21214
21544
|
});
|
|
21215
|
-
import * as
|
|
21545
|
+
import * as path47 from "path";
|
|
21216
21546
|
function newJobId(flavor) {
|
|
21217
21547
|
localJobSeq += 1;
|
|
21218
21548
|
const runId = process.env.GITHUB_RUN_ID;
|
|
@@ -21280,7 +21610,7 @@ function parseJobEvidence(job) {
|
|
|
21280
21610
|
async function runJob(job, base) {
|
|
21281
21611
|
const valid = validateJob(job);
|
|
21282
21612
|
const action = valid.action ?? valid.capability;
|
|
21283
|
-
const projectCapabilitiesRoot =
|
|
21613
|
+
const projectCapabilitiesRoot = path47.join(base.cwd, ".kody", "capabilities");
|
|
21284
21614
|
const resolvedCapability = !valid.workflow && action ? resolveCapabilityAction(action, projectCapabilitiesRoot) : null;
|
|
21285
21615
|
const capabilityIdentity = valid.capability ?? resolvedCapability?.capability;
|
|
21286
21616
|
const capabilityContext = valid.workflow ? null : loadCapabilityContext(capabilityIdentity, base.cwd);
|
|
@@ -21471,15 +21801,20 @@ function isGraphWorkflow(workflow) {
|
|
|
21471
21801
|
return workflow.startAt !== void 0 || workflow.steps.some((step) => step.id !== void 0 || step.next !== void 0 || step.inputs !== void 0);
|
|
21472
21802
|
}
|
|
21473
21803
|
function workflowError(workflow, base) {
|
|
21474
|
-
const projectCapabilitiesRoot =
|
|
21804
|
+
const projectCapabilitiesRoot = path47.join(base.cwd, ".kody", "capabilities");
|
|
21475
21805
|
const knownCapabilities = /* @__PURE__ */ new Set();
|
|
21476
21806
|
const capabilityInputs = /* @__PURE__ */ new Map();
|
|
21807
|
+
const capabilityOutputs = /* @__PURE__ */ new Map();
|
|
21477
21808
|
for (const step of workflow.steps) {
|
|
21478
21809
|
const action = step.action ?? step.capability;
|
|
21479
21810
|
const resolvedAction = resolveCapabilityAction(action, projectCapabilitiesRoot);
|
|
21480
21811
|
const resolvedFolder = resolveCapabilityFolder(step.capability, projectCapabilitiesRoot);
|
|
21481
21812
|
if (!resolvedAction && !resolvedFolder) continue;
|
|
21482
21813
|
knownCapabilities.add(step.capability);
|
|
21814
|
+
capabilityOutputs.set(
|
|
21815
|
+
step.capability,
|
|
21816
|
+
resolvedFolder ? capabilityOutputConditionPaths(resolvedFolder.config) : /* @__PURE__ */ new Set()
|
|
21817
|
+
);
|
|
21483
21818
|
const inputs = getCapabilityActionInputs(action, projectCapabilitiesRoot);
|
|
21484
21819
|
if (inputs) {
|
|
21485
21820
|
capabilityInputs.set(
|
|
@@ -21488,7 +21823,9 @@ function workflowError(workflow, base) {
|
|
|
21488
21823
|
);
|
|
21489
21824
|
}
|
|
21490
21825
|
}
|
|
21491
|
-
return formatWorkflowValidationIssues(
|
|
21826
|
+
return formatWorkflowValidationIssues(
|
|
21827
|
+
validateWorkflow(workflow, { knownCapabilities, capabilityInputs, capabilityOutputs })
|
|
21828
|
+
)[0] ?? null;
|
|
21492
21829
|
}
|
|
21493
21830
|
function initialWorkflowState(parent, workflow) {
|
|
21494
21831
|
const prior = parent.workflowState;
|
|
@@ -21611,6 +21948,14 @@ async function runGraphCapabilityWorkflow(parent, workflow, capability, base, ch
|
|
|
21611
21948
|
checkpoint?.(state);
|
|
21612
21949
|
return withWorkflowBoundaryEval(capability, { ...result, workflowState: state });
|
|
21613
21950
|
}
|
|
21951
|
+
const resultConditionPaths = workflowResultConditionPaths(step.next);
|
|
21952
|
+
if (resultConditionPaths.length > 0 && !result.capabilityResults?.at(-1)) {
|
|
21953
|
+
const reason = `workflow step ${step.id} did not emit the structured result required by its conditions: ${resultConditionPaths.join(", ")}`;
|
|
21954
|
+
state.status = "blocked";
|
|
21955
|
+
state.blocker = reason;
|
|
21956
|
+
checkpoint?.(state);
|
|
21957
|
+
return { ...result, exitCode: 64, reason, workflowState: state };
|
|
21958
|
+
}
|
|
21614
21959
|
const transition = selectWorkflowTransition(step, chainData, state.transitionCounts);
|
|
21615
21960
|
if (!transition) {
|
|
21616
21961
|
const reason = `workflow step ${step.id} has no available connection`;
|
|
@@ -21658,8 +22003,13 @@ function selectWorkflowTransition(step, data, counts) {
|
|
|
21658
22003
|
}
|
|
21659
22004
|
return fallback;
|
|
21660
22005
|
}
|
|
22006
|
+
function workflowResultConditionPaths(transitions) {
|
|
22007
|
+
return transitions.flatMap(
|
|
22008
|
+
(transition) => Object.keys(transition.when ?? {}).filter((path53) => path53.startsWith("result."))
|
|
22009
|
+
);
|
|
22010
|
+
}
|
|
21661
22011
|
function conditionMatches(condition, context) {
|
|
21662
|
-
return Object.entries(condition).every(([
|
|
22012
|
+
return Object.entries(condition).every(([path53, expected]) => valueMatches(resolveDottedPath2(context, path53), expected));
|
|
21663
22013
|
}
|
|
21664
22014
|
function withWorkflowBoundaryEval(capability, result) {
|
|
21665
22015
|
const capabilityKind = capability.config.capabilityKind;
|
|
@@ -21755,14 +22105,10 @@ function workflowConditionContext(data) {
|
|
|
21755
22105
|
};
|
|
21756
22106
|
}
|
|
21757
22107
|
function resolveDottedPath2(root, dotted) {
|
|
21758
|
-
|
|
22108
|
+
return dotted.split(".").reduce((cur, part) => {
|
|
21759
22109
|
if (!cur || typeof cur !== "object") return void 0;
|
|
21760
22110
|
return cur[part];
|
|
21761
22111
|
}, root);
|
|
21762
|
-
if (direct !== void 0 || !dotted.startsWith("result.")) return direct;
|
|
21763
|
-
const result = resolveDottedPath2(root, "result");
|
|
21764
|
-
if (!result || typeof result !== "object" || Array.isArray(result)) return direct;
|
|
21765
|
-
return resolveDottedPath2(result.facts, dotted.slice("result.".length));
|
|
21766
22112
|
}
|
|
21767
22113
|
function valueMatches(actual, expected) {
|
|
21768
22114
|
if (Array.isArray(expected)) return expected.some((entry) => valueMatches(actual, entry));
|
|
@@ -21818,7 +22164,7 @@ function composeStepWhy(parentWhy, step) {
|
|
|
21818
22164
|
}
|
|
21819
22165
|
function loadCapabilityContext(slug, cwd) {
|
|
21820
22166
|
if (!slug) return null;
|
|
21821
|
-
return resolveCapabilityFolder(slug,
|
|
22167
|
+
return resolveCapabilityFolder(slug, path47.join(cwd, ".kody", "capabilities"));
|
|
21822
22168
|
}
|
|
21823
22169
|
function loadWorkflowContext(slug, base) {
|
|
21824
22170
|
if (!slug || !base.config || !isWorkflowDefinitionId(slug)) return null;
|
|
@@ -21854,6 +22200,7 @@ var init_job = __esm({
|
|
|
21854
22200
|
"src/job.ts"() {
|
|
21855
22201
|
"use strict";
|
|
21856
22202
|
init_agencyBoundaryEval();
|
|
22203
|
+
init_capabilityFolders();
|
|
21857
22204
|
init_executor();
|
|
21858
22205
|
init_registry();
|
|
21859
22206
|
init_workflowDefinitions();
|
|
@@ -21979,9 +22326,9 @@ function translateOpenAISseToBrain(opts) {
|
|
|
21979
22326
|
}
|
|
21980
22327
|
|
|
21981
22328
|
// src/servers/brain-serve.ts
|
|
21982
|
-
import * as
|
|
22329
|
+
import * as fs50 from "fs";
|
|
21983
22330
|
import { createServer as createServer2 } from "http";
|
|
21984
|
-
import * as
|
|
22331
|
+
import * as path50 from "path";
|
|
21985
22332
|
|
|
21986
22333
|
// src/chat/loop.ts
|
|
21987
22334
|
init_agent();
|
|
@@ -22117,6 +22464,61 @@ function makeRunId(sessionId, suffix) {
|
|
|
22117
22464
|
return `chat-${sessionId}-${suffix}`;
|
|
22118
22465
|
}
|
|
22119
22466
|
|
|
22467
|
+
// src/chat/session-store.ts
|
|
22468
|
+
import { anyApi } from "convex/server";
|
|
22469
|
+
|
|
22470
|
+
// src/chat/convex-client.ts
|
|
22471
|
+
import { ConvexHttpClient } from "convex/browser";
|
|
22472
|
+
var ESCAPE_CHAR = "~";
|
|
22473
|
+
var NEEDS_ESCAPE = /^[$_~]/;
|
|
22474
|
+
function isPlainObject2(value) {
|
|
22475
|
+
if (value === null || typeof value !== "object") return false;
|
|
22476
|
+
const proto = Object.getPrototypeOf(value);
|
|
22477
|
+
return proto === Object.prototype || proto === null;
|
|
22478
|
+
}
|
|
22479
|
+
function deepMapKeys(value, mapKey) {
|
|
22480
|
+
if (Array.isArray(value)) return value.map((item) => deepMapKeys(item, mapKey));
|
|
22481
|
+
if (isPlainObject2(value)) {
|
|
22482
|
+
return Object.fromEntries(Object.entries(value).map(([key, item]) => [mapKey(key), deepMapKeys(item, mapKey)]));
|
|
22483
|
+
}
|
|
22484
|
+
return value;
|
|
22485
|
+
}
|
|
22486
|
+
function deepEscapeKeys(value) {
|
|
22487
|
+
return deepMapKeys(value, (k) => NEEDS_ESCAPE.test(k) ? `${ESCAPE_CHAR}${k}` : k);
|
|
22488
|
+
}
|
|
22489
|
+
function deepUnescapeKeys(value) {
|
|
22490
|
+
return deepMapKeys(value, (k) => k.startsWith(ESCAPE_CHAR) ? k.slice(1) : k);
|
|
22491
|
+
}
|
|
22492
|
+
var CALL_METHODS = ["query", "mutation", "action"];
|
|
22493
|
+
function injectServiceKey(args) {
|
|
22494
|
+
const serviceKey = process.env.KODY_SERVICE_KEY;
|
|
22495
|
+
if (!serviceKey) return args;
|
|
22496
|
+
if (args === void 0) return { serviceKey };
|
|
22497
|
+
if (typeof args !== "object" || args === null || Array.isArray(args)) return args;
|
|
22498
|
+
return { ...args, serviceKey };
|
|
22499
|
+
}
|
|
22500
|
+
function withEscapedKeys(client) {
|
|
22501
|
+
return new Proxy(client, {
|
|
22502
|
+
get(target, prop, receiver) {
|
|
22503
|
+
if (CALL_METHODS.includes(prop)) {
|
|
22504
|
+
const method = Reflect.get(target, prop, target);
|
|
22505
|
+
return async (fn, args) => {
|
|
22506
|
+
const authed = injectServiceKey(args);
|
|
22507
|
+
const result = await method.call(target, fn, authed === void 0 ? void 0 : deepEscapeKeys(authed));
|
|
22508
|
+
return deepUnescapeKeys(result);
|
|
22509
|
+
};
|
|
22510
|
+
}
|
|
22511
|
+
const value = Reflect.get(target, prop, receiver);
|
|
22512
|
+
return typeof value === "function" ? value.bind(target) : value;
|
|
22513
|
+
}
|
|
22514
|
+
});
|
|
22515
|
+
}
|
|
22516
|
+
function createConvexClientFromEnv(env = process.env) {
|
|
22517
|
+
const url = env.CONVEX_URL?.trim();
|
|
22518
|
+
if (!url) return null;
|
|
22519
|
+
return withEscapedKeys(new ConvexHttpClient(url));
|
|
22520
|
+
}
|
|
22521
|
+
|
|
22120
22522
|
// src/chat/session.ts
|
|
22121
22523
|
import * as fs13 from "fs";
|
|
22122
22524
|
import * as path15 from "path";
|
|
@@ -22182,6 +22584,103 @@ function seedInitialMessage(file, message) {
|
|
|
22182
22584
|
return true;
|
|
22183
22585
|
}
|
|
22184
22586
|
|
|
22587
|
+
// src/chat/session-store.ts
|
|
22588
|
+
function isChatTurn(value) {
|
|
22589
|
+
if (!value || typeof value !== "object") return false;
|
|
22590
|
+
const t = value;
|
|
22591
|
+
return (t.role === "user" || t.role === "assistant") && typeof t.content === "string";
|
|
22592
|
+
}
|
|
22593
|
+
function createSessionStore(opts) {
|
|
22594
|
+
const logger = opts.logger ?? {
|
|
22595
|
+
info: (m) => process.stdout.write(`[kody:chat:store] ${m}
|
|
22596
|
+
`),
|
|
22597
|
+
warn: (m) => process.stderr.write(`[kody:chat:store] ${m}
|
|
22598
|
+
`)
|
|
22599
|
+
};
|
|
22600
|
+
const client = opts.client !== void 0 ? opts.client : createConvexClientFromEnv();
|
|
22601
|
+
const tenantId = opts.tenantId ?? process.env.GITHUB_REPOSITORY ?? "";
|
|
22602
|
+
if (client && tenantId) {
|
|
22603
|
+
logger.info(`session ${opts.sessionId}: using Convex transcript store (tenant ${tenantId})`);
|
|
22604
|
+
return createConvexStore({
|
|
22605
|
+
client,
|
|
22606
|
+
tenantId,
|
|
22607
|
+
sessionId: opts.sessionId,
|
|
22608
|
+
sessionFile: opts.sessionFile,
|
|
22609
|
+
logger
|
|
22610
|
+
});
|
|
22611
|
+
}
|
|
22612
|
+
if (client && !tenantId) {
|
|
22613
|
+
logger.warn(`session ${opts.sessionId}: CONVEX_URL set but no tenant (GITHUB_REPOSITORY unset) \u2014 using JSONL`);
|
|
22614
|
+
} else {
|
|
22615
|
+
logger.info(`session ${opts.sessionId}: CONVEX_URL unset \u2014 using legacy state-repo JSONL store`);
|
|
22616
|
+
}
|
|
22617
|
+
return createJsonlStore(opts.sessionFile);
|
|
22618
|
+
}
|
|
22619
|
+
function createJsonlStore(sessionFile) {
|
|
22620
|
+
return {
|
|
22621
|
+
backend: "jsonl",
|
|
22622
|
+
readTurns: async () => readSession(sessionFile),
|
|
22623
|
+
appendTurn: async (turn) => {
|
|
22624
|
+
appendTurn(sessionFile, turn);
|
|
22625
|
+
}
|
|
22626
|
+
};
|
|
22627
|
+
}
|
|
22628
|
+
function normalizeTurn(turn) {
|
|
22629
|
+
return {
|
|
22630
|
+
role: turn.role,
|
|
22631
|
+
content: turn.content,
|
|
22632
|
+
timestamp: turn.timestamp,
|
|
22633
|
+
toolCalls: turn.toolCalls ?? []
|
|
22634
|
+
};
|
|
22635
|
+
}
|
|
22636
|
+
function createConvexStore(args) {
|
|
22637
|
+
const { client, tenantId, sessionId, sessionFile, logger } = args;
|
|
22638
|
+
let sessionUpserted = false;
|
|
22639
|
+
const appendToConvex = async (turn) => {
|
|
22640
|
+
if (!sessionUpserted) {
|
|
22641
|
+
try {
|
|
22642
|
+
const meta = readMeta(sessionFile) ?? { type: "meta", mode: "one-shot" };
|
|
22643
|
+
await client.mutation(anyApi.chatSessions.upsert, {
|
|
22644
|
+
tenantId,
|
|
22645
|
+
sessionId,
|
|
22646
|
+
meta,
|
|
22647
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
22648
|
+
});
|
|
22649
|
+
sessionUpserted = true;
|
|
22650
|
+
} catch (err) {
|
|
22651
|
+
logger.warn(`session ${sessionId}: chatSessions.upsert failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
22652
|
+
}
|
|
22653
|
+
}
|
|
22654
|
+
await client.mutation(anyApi.chatTurns.append, { tenantId, sessionId, turn });
|
|
22655
|
+
};
|
|
22656
|
+
return {
|
|
22657
|
+
backend: "convex",
|
|
22658
|
+
readTurns: async () => {
|
|
22659
|
+
const docs = await client.query(anyApi.chatTurns.list, { tenantId, sessionId });
|
|
22660
|
+
const convexTurns = [...docs].sort((a, b) => a.seq - b.seq).map((doc) => doc.turn).filter(isChatTurn);
|
|
22661
|
+
const localTurns = readSession(sessionFile);
|
|
22662
|
+
if (localTurns.length <= convexTurns.length) return convexTurns;
|
|
22663
|
+
const tail = localTurns.slice(convexTurns.length).map(normalizeTurn);
|
|
22664
|
+
logger.info(
|
|
22665
|
+
`session ${sessionId}: backfilling ${tail.length} local JSONL turn(s) into Convex (convex=${convexTurns.length}, local=${localTurns.length})`
|
|
22666
|
+
);
|
|
22667
|
+
for (const turn of tail) {
|
|
22668
|
+
await appendToConvex(turn);
|
|
22669
|
+
}
|
|
22670
|
+
return [...convexTurns, ...tail];
|
|
22671
|
+
},
|
|
22672
|
+
appendTurn: async (turn) => {
|
|
22673
|
+
const normalized = normalizeTurn(turn);
|
|
22674
|
+
await appendToConvex(normalized);
|
|
22675
|
+
try {
|
|
22676
|
+
appendTurn(sessionFile, normalized);
|
|
22677
|
+
} catch (err) {
|
|
22678
|
+
logger.warn(`session ${sessionId}: local JSONL mirror failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
22679
|
+
}
|
|
22680
|
+
}
|
|
22681
|
+
};
|
|
22682
|
+
}
|
|
22683
|
+
|
|
22185
22684
|
// src/chat/loop.ts
|
|
22186
22685
|
var CHAT_SYSTEM_PROMPT = [
|
|
22187
22686
|
"You are Kody, an AI assistant for the Kody Operations Dashboard. Reply to the",
|
|
@@ -22324,7 +22823,8 @@ function buildImplementationCatalog() {
|
|
|
22324
22823
|
return lines.join("\n");
|
|
22325
22824
|
}
|
|
22326
22825
|
async function runChatTurn(opts) {
|
|
22327
|
-
const
|
|
22826
|
+
const store = opts.store ?? createSessionStore({ sessionId: opts.sessionId, sessionFile: opts.sessionFile });
|
|
22827
|
+
const turns = await store.readTurns();
|
|
22328
22828
|
if (turns.length === 0) {
|
|
22329
22829
|
const error = "session file is empty \u2014 nothing to reply to";
|
|
22330
22830
|
await emit(opts.sink, "chat.error", opts.sessionId, "error", { error });
|
|
@@ -22379,7 +22879,7 @@ async function runChatTurn(opts) {
|
|
|
22379
22879
|
opts,
|
|
22380
22880
|
turns: promptTurns,
|
|
22381
22881
|
systemPrompt,
|
|
22382
|
-
|
|
22882
|
+
store
|
|
22383
22883
|
});
|
|
22384
22884
|
}
|
|
22385
22885
|
let progressSeq = 0;
|
|
@@ -22455,7 +22955,7 @@ async function runChatTurn(opts) {
|
|
|
22455
22955
|
return { exitCode: 99, error };
|
|
22456
22956
|
}
|
|
22457
22957
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
22458
|
-
appendTurn(
|
|
22958
|
+
await store.appendTurn({
|
|
22459
22959
|
role: "assistant",
|
|
22460
22960
|
content: reply,
|
|
22461
22961
|
timestamp: now
|
|
@@ -22491,7 +22991,7 @@ function readAgentIdentityBlock(cwd, agentIdentity) {
|
|
|
22491
22991
|
return frameAgentIdentity(slug, body);
|
|
22492
22992
|
}
|
|
22493
22993
|
async function runOpenAIChatTurn(args) {
|
|
22494
|
-
const { opts, turns, systemPrompt,
|
|
22994
|
+
const { opts, turns, systemPrompt, store } = args;
|
|
22495
22995
|
const doFetch = opts.fetchImpl ?? fetch;
|
|
22496
22996
|
const url = `${opts.litellmUrl.replace(/\/+$/, "")}/v1/chat/completions`;
|
|
22497
22997
|
try {
|
|
@@ -22524,7 +23024,7 @@ async function runOpenAIChatTurn(args) {
|
|
|
22524
23024
|
return { exitCode: 99, error };
|
|
22525
23025
|
}
|
|
22526
23026
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
22527
|
-
appendTurn(
|
|
23027
|
+
await store.appendTurn({
|
|
22528
23028
|
role: "assistant",
|
|
22529
23029
|
content: reply,
|
|
22530
23030
|
timestamp: now
|
|
@@ -22668,8 +23168,8 @@ init_config();
|
|
|
22668
23168
|
|
|
22669
23169
|
// src/kody-cli.ts
|
|
22670
23170
|
import { execFileSync as execFileSync26 } from "child_process";
|
|
22671
|
-
import * as
|
|
22672
|
-
import * as
|
|
23171
|
+
import * as fs48 from "fs";
|
|
23172
|
+
import * as path48 from "path";
|
|
22673
23173
|
|
|
22674
23174
|
// src/app-auth.ts
|
|
22675
23175
|
import { createSign } from "crypto";
|
|
@@ -23467,9 +23967,9 @@ async function resolveAuthToken(env = process.env) {
|
|
|
23467
23967
|
return void 0;
|
|
23468
23968
|
}
|
|
23469
23969
|
function detectPackageManager2(cwd) {
|
|
23470
|
-
if (
|
|
23471
|
-
if (
|
|
23472
|
-
if (
|
|
23970
|
+
if (fs48.existsSync(path48.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
|
|
23971
|
+
if (fs48.existsSync(path48.join(cwd, "yarn.lock"))) return "yarn";
|
|
23972
|
+
if (fs48.existsSync(path48.join(cwd, "bun.lockb"))) return "bun";
|
|
23473
23973
|
return "npm";
|
|
23474
23974
|
}
|
|
23475
23975
|
function shouldChainScheduledWatch(match) {
|
|
@@ -23562,8 +24062,8 @@ function postFailureTail(issueNumber, cwd, reason) {
|
|
|
23562
24062
|
const logPath = lastRunLogPath(cwd);
|
|
23563
24063
|
let tail = "";
|
|
23564
24064
|
try {
|
|
23565
|
-
if (
|
|
23566
|
-
const content =
|
|
24065
|
+
if (fs48.existsSync(logPath)) {
|
|
24066
|
+
const content = fs48.readFileSync(logPath, "utf-8");
|
|
23567
24067
|
tail = content.slice(-3e3);
|
|
23568
24068
|
}
|
|
23569
24069
|
} catch {
|
|
@@ -23592,7 +24092,7 @@ async function runCi(argv) {
|
|
|
23592
24092
|
return 0;
|
|
23593
24093
|
}
|
|
23594
24094
|
const args = parseCiArgs(argv);
|
|
23595
|
-
const cwd = args.cwd ?
|
|
24095
|
+
const cwd = args.cwd ? path48.resolve(args.cwd) : process.cwd();
|
|
23596
24096
|
try {
|
|
23597
24097
|
const n = unpackAllSecrets();
|
|
23598
24098
|
if (n > 0) process.stdout.write(`\u2192 kody: unpacked ${n} secret(s) from ALL_SECRETS
|
|
@@ -23652,9 +24152,9 @@ async function runCi(argv) {
|
|
|
23652
24152
|
forceRunCliArgs = { goal: envForceMessage };
|
|
23653
24153
|
}
|
|
23654
24154
|
}
|
|
23655
|
-
if (!args.issueNumber && !autoFallback && !forceRunAction && !runRequestFanOut && eventName === "workflow_dispatch" && dispatchEventPath &&
|
|
24155
|
+
if (!args.issueNumber && !autoFallback && !forceRunAction && !runRequestFanOut && eventName === "workflow_dispatch" && dispatchEventPath && fs48.existsSync(dispatchEventPath)) {
|
|
23656
24156
|
try {
|
|
23657
|
-
const evt = JSON.parse(
|
|
24157
|
+
const evt = JSON.parse(fs48.readFileSync(dispatchEventPath, "utf-8"));
|
|
23658
24158
|
const inputs = objectValue2(evt.inputs);
|
|
23659
24159
|
applyCompanyStoreRuntimeConfig(inputs);
|
|
23660
24160
|
const issueInput = parseInt(String(inputs?.issue_number ?? ""), 10);
|
|
@@ -24028,8 +24528,8 @@ init_repoWorkspace();
|
|
|
24028
24528
|
|
|
24029
24529
|
// src/scripts/brainTurnLog.ts
|
|
24030
24530
|
init_runtimePaths();
|
|
24031
|
-
import * as
|
|
24032
|
-
import * as
|
|
24531
|
+
import * as fs49 from "fs";
|
|
24532
|
+
import * as path49 from "path";
|
|
24033
24533
|
import posixPath4 from "path/posix";
|
|
24034
24534
|
var live = /* @__PURE__ */ new Map();
|
|
24035
24535
|
function brainEventsFilePath(dir, chatId) {
|
|
@@ -24040,8 +24540,8 @@ function brainEventsStatePath(chatId) {
|
|
|
24040
24540
|
}
|
|
24041
24541
|
function lastPersistedSeq(dir, chatId) {
|
|
24042
24542
|
const p = brainEventsFilePath(dir, chatId);
|
|
24043
|
-
if (!
|
|
24044
|
-
const lines =
|
|
24543
|
+
if (!fs49.existsSync(p)) return 0;
|
|
24544
|
+
const lines = fs49.readFileSync(p, "utf-8").split("\n").filter(Boolean);
|
|
24045
24545
|
if (lines.length === 0) return 0;
|
|
24046
24546
|
try {
|
|
24047
24547
|
return JSON.parse(lines[lines.length - 1]).seq || 0;
|
|
@@ -24051,9 +24551,9 @@ function lastPersistedSeq(dir, chatId) {
|
|
|
24051
24551
|
}
|
|
24052
24552
|
function readSince(dir, chatId, since) {
|
|
24053
24553
|
const p = brainEventsFilePath(dir, chatId);
|
|
24054
|
-
if (!
|
|
24554
|
+
if (!fs49.existsSync(p)) return [];
|
|
24055
24555
|
const out = [];
|
|
24056
|
-
for (const line of
|
|
24556
|
+
for (const line of fs49.readFileSync(p, "utf-8").split("\n")) {
|
|
24057
24557
|
if (!line) continue;
|
|
24058
24558
|
try {
|
|
24059
24559
|
const rec = JSON.parse(line);
|
|
@@ -24079,12 +24579,12 @@ function beginTurn(dir, chatId) {
|
|
|
24079
24579
|
};
|
|
24080
24580
|
live.set(chatId, state);
|
|
24081
24581
|
const p = brainEventsFilePath(dir, chatId);
|
|
24082
|
-
|
|
24582
|
+
fs49.mkdirSync(path49.dirname(p), { recursive: true });
|
|
24083
24583
|
return (event) => {
|
|
24084
24584
|
state.seq += 1;
|
|
24085
24585
|
const rec = { seq: state.seq, turn, ts: Date.now(), event };
|
|
24086
24586
|
try {
|
|
24087
|
-
|
|
24587
|
+
fs49.appendFileSync(p, `${JSON.stringify(rec)}
|
|
24088
24588
|
`);
|
|
24089
24589
|
} catch (err) {
|
|
24090
24590
|
process.stderr.write(
|
|
@@ -24123,7 +24623,7 @@ function endTurnIfUnterminated(dir, chatId, errMessage) {
|
|
|
24123
24623
|
event: { type: "error", error: errMessage || "turn ended unexpectedly", chatId }
|
|
24124
24624
|
};
|
|
24125
24625
|
try {
|
|
24126
|
-
|
|
24626
|
+
fs49.appendFileSync(brainEventsFilePath(dir, chatId), `${JSON.stringify(rec)}
|
|
24127
24627
|
`);
|
|
24128
24628
|
} catch {
|
|
24129
24629
|
}
|
|
@@ -24457,7 +24957,7 @@ async function handleChatTurn(req, res, chatId, opts) {
|
|
|
24457
24957
|
);
|
|
24458
24958
|
}
|
|
24459
24959
|
}
|
|
24460
|
-
|
|
24960
|
+
fs50.mkdirSync(path50.dirname(sessionFile), { recursive: true });
|
|
24461
24961
|
appendTurn(sessionFile, {
|
|
24462
24962
|
role: "user",
|
|
24463
24963
|
content: message,
|
|
@@ -24532,7 +25032,7 @@ async function handleChatTurn(req, res, chatId, opts) {
|
|
|
24532
25032
|
function buildServer(opts) {
|
|
24533
25033
|
const runTurn = opts.runTurn ?? runChatTurn;
|
|
24534
25034
|
const cloneRepo = opts.cloneRepo ?? defaultCloneRepo;
|
|
24535
|
-
const reposRoot = opts.reposRoot ??
|
|
25035
|
+
const reposRoot = opts.reposRoot ?? path50.join(path50.dirname(path50.resolve(opts.cwd)), "repos");
|
|
24536
25036
|
return createServer2(async (req, res) => {
|
|
24537
25037
|
if (!req.method || !req.url) {
|
|
24538
25038
|
sendJson(res, 400, { error: "bad request" });
|
|
@@ -25135,8 +25635,8 @@ async function loadConfigSafe() {
|
|
|
25135
25635
|
}
|
|
25136
25636
|
|
|
25137
25637
|
// src/chat-cli.ts
|
|
25138
|
-
import * as
|
|
25139
|
-
import * as
|
|
25638
|
+
import * as fs52 from "fs";
|
|
25639
|
+
import * as path52 from "path";
|
|
25140
25640
|
|
|
25141
25641
|
// src/chat/inbox.ts
|
|
25142
25642
|
import { execFileSync as execFileSync27 } from "child_process";
|
|
@@ -25152,7 +25652,8 @@ async function waitForNextUserMessage(opts) {
|
|
|
25152
25652
|
const now = Date.now();
|
|
25153
25653
|
if (now >= opts.deadlineMs) return { kind: "deadline" };
|
|
25154
25654
|
if (now - idleStart >= opts.idleTimeoutMs) return { kind: "idle-timeout" };
|
|
25155
|
-
if (opts.
|
|
25655
|
+
if (opts.readTurns) {
|
|
25656
|
+
} else if (opts.sync && !opts.skipPull) {
|
|
25156
25657
|
try {
|
|
25157
25658
|
opts.sync();
|
|
25158
25659
|
} catch (err) {
|
|
@@ -25176,7 +25677,18 @@ async function waitForNextUserMessage(opts) {
|
|
|
25176
25677
|
logger.warn(`git pull failed (will retry): ${msg}`);
|
|
25177
25678
|
}
|
|
25178
25679
|
}
|
|
25179
|
-
|
|
25680
|
+
let turns;
|
|
25681
|
+
if (opts.readTurns) {
|
|
25682
|
+
try {
|
|
25683
|
+
turns = await opts.readTurns();
|
|
25684
|
+
} catch (err) {
|
|
25685
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
25686
|
+
logger.warn(`convex transcript poll failed (will retry): ${msg}`);
|
|
25687
|
+
turns = [];
|
|
25688
|
+
}
|
|
25689
|
+
} else {
|
|
25690
|
+
turns = readSession(opts.sessionFile);
|
|
25691
|
+
}
|
|
25180
25692
|
for (let i = opts.watermark; i < turns.length; i++) {
|
|
25181
25693
|
const t = turns[i];
|
|
25182
25694
|
if (t.role === "user") {
|
|
@@ -25208,8 +25720,8 @@ function currentBranch(cwd) {
|
|
|
25208
25720
|
|
|
25209
25721
|
// src/chat/state-sync.ts
|
|
25210
25722
|
init_stateRepo();
|
|
25211
|
-
import * as
|
|
25212
|
-
import * as
|
|
25723
|
+
import * as fs51 from "fs";
|
|
25724
|
+
import * as path51 from "path";
|
|
25213
25725
|
function jsonlLines2(text2) {
|
|
25214
25726
|
return text2.split("\n").filter((line) => line.length > 0);
|
|
25215
25727
|
}
|
|
@@ -25226,15 +25738,15 @@ function mergeJsonl2(localText, remoteText) {
|
|
|
25226
25738
|
function syncJsonlFileFromState(opts) {
|
|
25227
25739
|
const remote = readStateText(opts.config, opts.cwd, opts.statePath);
|
|
25228
25740
|
if (!remote) return;
|
|
25229
|
-
const local =
|
|
25741
|
+
const local = fs51.existsSync(opts.localPath) ? fs51.readFileSync(opts.localPath, "utf-8") : "";
|
|
25230
25742
|
const next = mergeJsonl2(local, remote.content);
|
|
25231
25743
|
if (next === local) return;
|
|
25232
|
-
|
|
25233
|
-
|
|
25744
|
+
fs51.mkdirSync(path51.dirname(opts.localPath), { recursive: true });
|
|
25745
|
+
fs51.writeFileSync(opts.localPath, next);
|
|
25234
25746
|
}
|
|
25235
25747
|
function persistJsonlFileToState(opts) {
|
|
25236
|
-
if (!
|
|
25237
|
-
const localText =
|
|
25748
|
+
if (!fs51.existsSync(opts.localPath)) return;
|
|
25749
|
+
const localText = fs51.readFileSync(opts.localPath, "utf-8");
|
|
25238
25750
|
for (let attempt = 1; attempt <= 3; attempt += 1) {
|
|
25239
25751
|
const remote = readStateText(opts.config, opts.cwd, opts.statePath);
|
|
25240
25752
|
const body = mergeJsonl2(localText, remote?.content ?? "");
|
|
@@ -25293,6 +25805,7 @@ var DEFAULT_HARD_CAP_MS = 30 * 6e4;
|
|
|
25293
25805
|
var DEFAULT_POLL_MS2 = 3e3;
|
|
25294
25806
|
async function runInteractiveMode(opts) {
|
|
25295
25807
|
const sessionFile = sessionFilePath(opts.cwd, opts.sessionId);
|
|
25808
|
+
const store = opts.store ?? createSessionStore({ sessionId: opts.sessionId, sessionFile });
|
|
25296
25809
|
const idleExitMs = opts.meta.idleExitMs ?? DEFAULT_IDLE_EXIT_MS;
|
|
25297
25810
|
const hardCapMs = opts.meta.hardCapMs ?? DEFAULT_HARD_CAP_MS;
|
|
25298
25811
|
const startedAt = Date.now();
|
|
@@ -25323,10 +25836,10 @@ async function runInteractiveMode(opts) {
|
|
|
25323
25836
|
let watermark = 0;
|
|
25324
25837
|
let turnsCompleted = 0;
|
|
25325
25838
|
while (true) {
|
|
25326
|
-
if (opts.stateConfig && !opts.skipGit) {
|
|
25839
|
+
if (store.backend !== "convex" && opts.stateConfig && !opts.skipGit) {
|
|
25327
25840
|
syncChatSessionFromState(opts.stateConfig, opts.cwd, opts.sessionId);
|
|
25328
25841
|
}
|
|
25329
|
-
const turns =
|
|
25842
|
+
const turns = await store.readTurns();
|
|
25330
25843
|
const pendingIdx = findNextUserTurn(turns, watermark);
|
|
25331
25844
|
if (pendingIdx === -1) {
|
|
25332
25845
|
const result = await waitForNextUserMessage({
|
|
@@ -25337,7 +25850,7 @@ async function runInteractiveMode(opts) {
|
|
|
25337
25850
|
deadlineMs,
|
|
25338
25851
|
pollIntervalMs: opts.pollIntervalMs ?? DEFAULT_POLL_MS2,
|
|
25339
25852
|
skipPull: opts.skipGit,
|
|
25340
|
-
...opts.stateConfig ? {
|
|
25853
|
+
...store.backend === "convex" ? { readTurns: () => store.readTurns() } : opts.stateConfig ? {
|
|
25341
25854
|
sync: () => syncChatSessionFromState(opts.stateConfig, opts.cwd, opts.sessionId)
|
|
25342
25855
|
} : {}
|
|
25343
25856
|
});
|
|
@@ -25365,6 +25878,7 @@ async function runInteractiveMode(opts) {
|
|
|
25365
25878
|
quiet: opts.quiet,
|
|
25366
25879
|
invokeAgent: opts.invokeAgent,
|
|
25367
25880
|
stateConfig: opts.stateConfig,
|
|
25881
|
+
store,
|
|
25368
25882
|
...opts.reasoningEffort ? { reasoningEffort: opts.reasoningEffort } : {}
|
|
25369
25883
|
});
|
|
25370
25884
|
} catch (err) {
|
|
@@ -25380,7 +25894,7 @@ async function runInteractiveMode(opts) {
|
|
|
25380
25894
|
turnsCompleted += 1;
|
|
25381
25895
|
if (!opts.skipGit) commitTurn(opts.cwd, opts.sessionId, opts.verbose ?? false, opts.stateConfig ?? null);
|
|
25382
25896
|
}
|
|
25383
|
-
watermark =
|
|
25897
|
+
watermark = (await store.readTurns()).length;
|
|
25384
25898
|
}
|
|
25385
25899
|
}
|
|
25386
25900
|
function findNextUserTurn(turns, fromIdx) {
|
|
@@ -25499,7 +26013,7 @@ async function runChat(argv) {
|
|
|
25499
26013
|
${CHAT_HELP}`);
|
|
25500
26014
|
return 64;
|
|
25501
26015
|
}
|
|
25502
|
-
const cwd = args.cwd ?
|
|
26016
|
+
const cwd = args.cwd ? path52.resolve(args.cwd) : process.cwd();
|
|
25503
26017
|
const sessionId = args.sessionId;
|
|
25504
26018
|
const runRequest = readRunRequestFromEnv();
|
|
25505
26019
|
if (runRequest && "request" in runRequest) {
|
|
@@ -25564,7 +26078,7 @@ ${CHAT_HELP}`);
|
|
|
25564
26078
|
const sink = buildSink(cwd, sessionId, args.dashboardUrl);
|
|
25565
26079
|
const meta = readMeta(sessionFile);
|
|
25566
26080
|
process.stdout.write(
|
|
25567
|
-
`\u2192 kody:chat: session file=${sessionFile} exists=${
|
|
26081
|
+
`\u2192 kody:chat: session file=${sessionFile} exists=${fs52.existsSync(sessionFile)} meta=${meta ? meta.mode : "none"}
|
|
25568
26082
|
`
|
|
25569
26083
|
);
|
|
25570
26084
|
try {
|
|
@@ -25679,8 +26193,8 @@ var FlyClient = class {
|
|
|
25679
26193
|
get fetch() {
|
|
25680
26194
|
return this.opts.fetchImpl ?? fetch;
|
|
25681
26195
|
}
|
|
25682
|
-
async call(
|
|
25683
|
-
const res = await this.fetch(`${FLY_API_BASE}${
|
|
26196
|
+
async call(path53, init = {}) {
|
|
26197
|
+
const res = await this.fetch(`${FLY_API_BASE}${path53}`, {
|
|
25684
26198
|
method: init.method ?? "GET",
|
|
25685
26199
|
headers: {
|
|
25686
26200
|
Authorization: `Bearer ${this.opts.token}`,
|
|
@@ -25691,7 +26205,7 @@ var FlyClient = class {
|
|
|
25691
26205
|
if (res.status === 404 && init.allow404) return null;
|
|
25692
26206
|
if (!res.ok) {
|
|
25693
26207
|
const text2 = await res.text().catch(() => "");
|
|
25694
|
-
throw new Error(`Fly API ${res.status} on ${
|
|
26208
|
+
throw new Error(`Fly API ${res.status} on ${path53}: ${text2.slice(0, 200) || res.statusText}`);
|
|
25695
26209
|
}
|
|
25696
26210
|
if (res.status === 204) return null;
|
|
25697
26211
|
const raw = await res.text();
|
|
@@ -26447,7 +26961,7 @@ async function poolServe() {
|
|
|
26447
26961
|
|
|
26448
26962
|
// src/servers/runner-serve.ts
|
|
26449
26963
|
import { spawn as spawn8 } from "child_process";
|
|
26450
|
-
import * as
|
|
26964
|
+
import * as fs53 from "fs";
|
|
26451
26965
|
import { createServer as createServer6 } from "http";
|
|
26452
26966
|
var DEFAULT_PORT2 = 8080;
|
|
26453
26967
|
var DEFAULT_WORKDIR = "/workspace/repo";
|
|
@@ -26582,8 +27096,8 @@ async function defaultRunJob(job) {
|
|
|
26582
27096
|
const workdir = process.env.RUNNER_WORKDIR ?? DEFAULT_WORKDIR;
|
|
26583
27097
|
const branch = job.ref ?? "main";
|
|
26584
27098
|
const authUrl = `https://x-access-token:${job.githubToken}@github.com/${job.repo}.git`;
|
|
26585
|
-
|
|
26586
|
-
|
|
27099
|
+
fs53.rmSync(workdir, { recursive: true, force: true });
|
|
27100
|
+
fs53.mkdirSync(workdir, { recursive: true });
|
|
26587
27101
|
const allSecrets = typeof job.allSecrets === "string" ? job.allSecrets : JSON.stringify(job.allSecrets ?? {});
|
|
26588
27102
|
const target = job.runRequest.target;
|
|
26589
27103
|
const interactive = target.type === "chat";
|