@kody-ade/kody-engine 0.4.378 → 0.4.380
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 +663 -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.380",
|
|
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,87 @@ 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 createConvexStore(args) {
|
|
22629
|
+
const { client, tenantId, sessionId, sessionFile, logger } = args;
|
|
22630
|
+
let sessionUpserted = false;
|
|
22631
|
+
return {
|
|
22632
|
+
backend: "convex",
|
|
22633
|
+
readTurns: async () => {
|
|
22634
|
+
const docs = await client.query(anyApi.chatTurns.list, { tenantId, sessionId });
|
|
22635
|
+
return [...docs].sort((a, b) => a.seq - b.seq).map((doc) => doc.turn).filter(isChatTurn);
|
|
22636
|
+
},
|
|
22637
|
+
appendTurn: async (turn) => {
|
|
22638
|
+
const normalized = {
|
|
22639
|
+
role: turn.role,
|
|
22640
|
+
content: turn.content,
|
|
22641
|
+
timestamp: turn.timestamp,
|
|
22642
|
+
toolCalls: turn.toolCalls ?? []
|
|
22643
|
+
};
|
|
22644
|
+
if (!sessionUpserted) {
|
|
22645
|
+
try {
|
|
22646
|
+
const meta = readMeta(sessionFile) ?? { type: "meta", mode: "one-shot" };
|
|
22647
|
+
await client.mutation(anyApi.chatSessions.upsert, {
|
|
22648
|
+
tenantId,
|
|
22649
|
+
sessionId,
|
|
22650
|
+
meta,
|
|
22651
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
22652
|
+
});
|
|
22653
|
+
sessionUpserted = true;
|
|
22654
|
+
} catch (err) {
|
|
22655
|
+
logger.warn(`session ${sessionId}: chatSessions.upsert failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
22656
|
+
}
|
|
22657
|
+
}
|
|
22658
|
+
await client.mutation(anyApi.chatTurns.append, { tenantId, sessionId, turn: normalized });
|
|
22659
|
+
try {
|
|
22660
|
+
appendTurn(sessionFile, normalized);
|
|
22661
|
+
} catch (err) {
|
|
22662
|
+
logger.warn(`session ${sessionId}: local JSONL mirror failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
22663
|
+
}
|
|
22664
|
+
}
|
|
22665
|
+
};
|
|
22666
|
+
}
|
|
22667
|
+
|
|
22185
22668
|
// src/chat/loop.ts
|
|
22186
22669
|
var CHAT_SYSTEM_PROMPT = [
|
|
22187
22670
|
"You are Kody, an AI assistant for the Kody Operations Dashboard. Reply to the",
|
|
@@ -22324,7 +22807,8 @@ function buildImplementationCatalog() {
|
|
|
22324
22807
|
return lines.join("\n");
|
|
22325
22808
|
}
|
|
22326
22809
|
async function runChatTurn(opts) {
|
|
22327
|
-
const
|
|
22810
|
+
const store = opts.store ?? createSessionStore({ sessionId: opts.sessionId, sessionFile: opts.sessionFile });
|
|
22811
|
+
const turns = await store.readTurns();
|
|
22328
22812
|
if (turns.length === 0) {
|
|
22329
22813
|
const error = "session file is empty \u2014 nothing to reply to";
|
|
22330
22814
|
await emit(opts.sink, "chat.error", opts.sessionId, "error", { error });
|
|
@@ -22379,7 +22863,7 @@ async function runChatTurn(opts) {
|
|
|
22379
22863
|
opts,
|
|
22380
22864
|
turns: promptTurns,
|
|
22381
22865
|
systemPrompt,
|
|
22382
|
-
|
|
22866
|
+
store
|
|
22383
22867
|
});
|
|
22384
22868
|
}
|
|
22385
22869
|
let progressSeq = 0;
|
|
@@ -22455,7 +22939,7 @@ async function runChatTurn(opts) {
|
|
|
22455
22939
|
return { exitCode: 99, error };
|
|
22456
22940
|
}
|
|
22457
22941
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
22458
|
-
appendTurn(
|
|
22942
|
+
await store.appendTurn({
|
|
22459
22943
|
role: "assistant",
|
|
22460
22944
|
content: reply,
|
|
22461
22945
|
timestamp: now
|
|
@@ -22491,7 +22975,7 @@ function readAgentIdentityBlock(cwd, agentIdentity) {
|
|
|
22491
22975
|
return frameAgentIdentity(slug, body);
|
|
22492
22976
|
}
|
|
22493
22977
|
async function runOpenAIChatTurn(args) {
|
|
22494
|
-
const { opts, turns, systemPrompt,
|
|
22978
|
+
const { opts, turns, systemPrompt, store } = args;
|
|
22495
22979
|
const doFetch = opts.fetchImpl ?? fetch;
|
|
22496
22980
|
const url = `${opts.litellmUrl.replace(/\/+$/, "")}/v1/chat/completions`;
|
|
22497
22981
|
try {
|
|
@@ -22524,7 +23008,7 @@ async function runOpenAIChatTurn(args) {
|
|
|
22524
23008
|
return { exitCode: 99, error };
|
|
22525
23009
|
}
|
|
22526
23010
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
22527
|
-
appendTurn(
|
|
23011
|
+
await store.appendTurn({
|
|
22528
23012
|
role: "assistant",
|
|
22529
23013
|
content: reply,
|
|
22530
23014
|
timestamp: now
|
|
@@ -22668,8 +23152,8 @@ init_config();
|
|
|
22668
23152
|
|
|
22669
23153
|
// src/kody-cli.ts
|
|
22670
23154
|
import { execFileSync as execFileSync26 } from "child_process";
|
|
22671
|
-
import * as
|
|
22672
|
-
import * as
|
|
23155
|
+
import * as fs48 from "fs";
|
|
23156
|
+
import * as path48 from "path";
|
|
22673
23157
|
|
|
22674
23158
|
// src/app-auth.ts
|
|
22675
23159
|
import { createSign } from "crypto";
|
|
@@ -23467,9 +23951,9 @@ async function resolveAuthToken(env = process.env) {
|
|
|
23467
23951
|
return void 0;
|
|
23468
23952
|
}
|
|
23469
23953
|
function detectPackageManager2(cwd) {
|
|
23470
|
-
if (
|
|
23471
|
-
if (
|
|
23472
|
-
if (
|
|
23954
|
+
if (fs48.existsSync(path48.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
|
|
23955
|
+
if (fs48.existsSync(path48.join(cwd, "yarn.lock"))) return "yarn";
|
|
23956
|
+
if (fs48.existsSync(path48.join(cwd, "bun.lockb"))) return "bun";
|
|
23473
23957
|
return "npm";
|
|
23474
23958
|
}
|
|
23475
23959
|
function shouldChainScheduledWatch(match) {
|
|
@@ -23562,8 +24046,8 @@ function postFailureTail(issueNumber, cwd, reason) {
|
|
|
23562
24046
|
const logPath = lastRunLogPath(cwd);
|
|
23563
24047
|
let tail = "";
|
|
23564
24048
|
try {
|
|
23565
|
-
if (
|
|
23566
|
-
const content =
|
|
24049
|
+
if (fs48.existsSync(logPath)) {
|
|
24050
|
+
const content = fs48.readFileSync(logPath, "utf-8");
|
|
23567
24051
|
tail = content.slice(-3e3);
|
|
23568
24052
|
}
|
|
23569
24053
|
} catch {
|
|
@@ -23592,7 +24076,7 @@ async function runCi(argv) {
|
|
|
23592
24076
|
return 0;
|
|
23593
24077
|
}
|
|
23594
24078
|
const args = parseCiArgs(argv);
|
|
23595
|
-
const cwd = args.cwd ?
|
|
24079
|
+
const cwd = args.cwd ? path48.resolve(args.cwd) : process.cwd();
|
|
23596
24080
|
try {
|
|
23597
24081
|
const n = unpackAllSecrets();
|
|
23598
24082
|
if (n > 0) process.stdout.write(`\u2192 kody: unpacked ${n} secret(s) from ALL_SECRETS
|
|
@@ -23652,9 +24136,9 @@ async function runCi(argv) {
|
|
|
23652
24136
|
forceRunCliArgs = { goal: envForceMessage };
|
|
23653
24137
|
}
|
|
23654
24138
|
}
|
|
23655
|
-
if (!args.issueNumber && !autoFallback && !forceRunAction && !runRequestFanOut && eventName === "workflow_dispatch" && dispatchEventPath &&
|
|
24139
|
+
if (!args.issueNumber && !autoFallback && !forceRunAction && !runRequestFanOut && eventName === "workflow_dispatch" && dispatchEventPath && fs48.existsSync(dispatchEventPath)) {
|
|
23656
24140
|
try {
|
|
23657
|
-
const evt = JSON.parse(
|
|
24141
|
+
const evt = JSON.parse(fs48.readFileSync(dispatchEventPath, "utf-8"));
|
|
23658
24142
|
const inputs = objectValue2(evt.inputs);
|
|
23659
24143
|
applyCompanyStoreRuntimeConfig(inputs);
|
|
23660
24144
|
const issueInput = parseInt(String(inputs?.issue_number ?? ""), 10);
|
|
@@ -24028,8 +24512,8 @@ init_repoWorkspace();
|
|
|
24028
24512
|
|
|
24029
24513
|
// src/scripts/brainTurnLog.ts
|
|
24030
24514
|
init_runtimePaths();
|
|
24031
|
-
import * as
|
|
24032
|
-
import * as
|
|
24515
|
+
import * as fs49 from "fs";
|
|
24516
|
+
import * as path49 from "path";
|
|
24033
24517
|
import posixPath4 from "path/posix";
|
|
24034
24518
|
var live = /* @__PURE__ */ new Map();
|
|
24035
24519
|
function brainEventsFilePath(dir, chatId) {
|
|
@@ -24040,8 +24524,8 @@ function brainEventsStatePath(chatId) {
|
|
|
24040
24524
|
}
|
|
24041
24525
|
function lastPersistedSeq(dir, chatId) {
|
|
24042
24526
|
const p = brainEventsFilePath(dir, chatId);
|
|
24043
|
-
if (!
|
|
24044
|
-
const lines =
|
|
24527
|
+
if (!fs49.existsSync(p)) return 0;
|
|
24528
|
+
const lines = fs49.readFileSync(p, "utf-8").split("\n").filter(Boolean);
|
|
24045
24529
|
if (lines.length === 0) return 0;
|
|
24046
24530
|
try {
|
|
24047
24531
|
return JSON.parse(lines[lines.length - 1]).seq || 0;
|
|
@@ -24051,9 +24535,9 @@ function lastPersistedSeq(dir, chatId) {
|
|
|
24051
24535
|
}
|
|
24052
24536
|
function readSince(dir, chatId, since) {
|
|
24053
24537
|
const p = brainEventsFilePath(dir, chatId);
|
|
24054
|
-
if (!
|
|
24538
|
+
if (!fs49.existsSync(p)) return [];
|
|
24055
24539
|
const out = [];
|
|
24056
|
-
for (const line of
|
|
24540
|
+
for (const line of fs49.readFileSync(p, "utf-8").split("\n")) {
|
|
24057
24541
|
if (!line) continue;
|
|
24058
24542
|
try {
|
|
24059
24543
|
const rec = JSON.parse(line);
|
|
@@ -24079,12 +24563,12 @@ function beginTurn(dir, chatId) {
|
|
|
24079
24563
|
};
|
|
24080
24564
|
live.set(chatId, state);
|
|
24081
24565
|
const p = brainEventsFilePath(dir, chatId);
|
|
24082
|
-
|
|
24566
|
+
fs49.mkdirSync(path49.dirname(p), { recursive: true });
|
|
24083
24567
|
return (event) => {
|
|
24084
24568
|
state.seq += 1;
|
|
24085
24569
|
const rec = { seq: state.seq, turn, ts: Date.now(), event };
|
|
24086
24570
|
try {
|
|
24087
|
-
|
|
24571
|
+
fs49.appendFileSync(p, `${JSON.stringify(rec)}
|
|
24088
24572
|
`);
|
|
24089
24573
|
} catch (err) {
|
|
24090
24574
|
process.stderr.write(
|
|
@@ -24123,7 +24607,7 @@ function endTurnIfUnterminated(dir, chatId, errMessage) {
|
|
|
24123
24607
|
event: { type: "error", error: errMessage || "turn ended unexpectedly", chatId }
|
|
24124
24608
|
};
|
|
24125
24609
|
try {
|
|
24126
|
-
|
|
24610
|
+
fs49.appendFileSync(brainEventsFilePath(dir, chatId), `${JSON.stringify(rec)}
|
|
24127
24611
|
`);
|
|
24128
24612
|
} catch {
|
|
24129
24613
|
}
|
|
@@ -24457,7 +24941,7 @@ async function handleChatTurn(req, res, chatId, opts) {
|
|
|
24457
24941
|
);
|
|
24458
24942
|
}
|
|
24459
24943
|
}
|
|
24460
|
-
|
|
24944
|
+
fs50.mkdirSync(path50.dirname(sessionFile), { recursive: true });
|
|
24461
24945
|
appendTurn(sessionFile, {
|
|
24462
24946
|
role: "user",
|
|
24463
24947
|
content: message,
|
|
@@ -24532,7 +25016,7 @@ async function handleChatTurn(req, res, chatId, opts) {
|
|
|
24532
25016
|
function buildServer(opts) {
|
|
24533
25017
|
const runTurn = opts.runTurn ?? runChatTurn;
|
|
24534
25018
|
const cloneRepo = opts.cloneRepo ?? defaultCloneRepo;
|
|
24535
|
-
const reposRoot = opts.reposRoot ??
|
|
25019
|
+
const reposRoot = opts.reposRoot ?? path50.join(path50.dirname(path50.resolve(opts.cwd)), "repos");
|
|
24536
25020
|
return createServer2(async (req, res) => {
|
|
24537
25021
|
if (!req.method || !req.url) {
|
|
24538
25022
|
sendJson(res, 400, { error: "bad request" });
|
|
@@ -25135,8 +25619,8 @@ async function loadConfigSafe() {
|
|
|
25135
25619
|
}
|
|
25136
25620
|
|
|
25137
25621
|
// src/chat-cli.ts
|
|
25138
|
-
import * as
|
|
25139
|
-
import * as
|
|
25622
|
+
import * as fs52 from "fs";
|
|
25623
|
+
import * as path52 from "path";
|
|
25140
25624
|
|
|
25141
25625
|
// src/chat/inbox.ts
|
|
25142
25626
|
import { execFileSync as execFileSync27 } from "child_process";
|
|
@@ -25152,7 +25636,8 @@ async function waitForNextUserMessage(opts) {
|
|
|
25152
25636
|
const now = Date.now();
|
|
25153
25637
|
if (now >= opts.deadlineMs) return { kind: "deadline" };
|
|
25154
25638
|
if (now - idleStart >= opts.idleTimeoutMs) return { kind: "idle-timeout" };
|
|
25155
|
-
if (opts.
|
|
25639
|
+
if (opts.readTurns) {
|
|
25640
|
+
} else if (opts.sync && !opts.skipPull) {
|
|
25156
25641
|
try {
|
|
25157
25642
|
opts.sync();
|
|
25158
25643
|
} catch (err) {
|
|
@@ -25176,7 +25661,18 @@ async function waitForNextUserMessage(opts) {
|
|
|
25176
25661
|
logger.warn(`git pull failed (will retry): ${msg}`);
|
|
25177
25662
|
}
|
|
25178
25663
|
}
|
|
25179
|
-
|
|
25664
|
+
let turns;
|
|
25665
|
+
if (opts.readTurns) {
|
|
25666
|
+
try {
|
|
25667
|
+
turns = await opts.readTurns();
|
|
25668
|
+
} catch (err) {
|
|
25669
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
25670
|
+
logger.warn(`convex transcript poll failed (will retry): ${msg}`);
|
|
25671
|
+
turns = [];
|
|
25672
|
+
}
|
|
25673
|
+
} else {
|
|
25674
|
+
turns = readSession(opts.sessionFile);
|
|
25675
|
+
}
|
|
25180
25676
|
for (let i = opts.watermark; i < turns.length; i++) {
|
|
25181
25677
|
const t = turns[i];
|
|
25182
25678
|
if (t.role === "user") {
|
|
@@ -25208,8 +25704,8 @@ function currentBranch(cwd) {
|
|
|
25208
25704
|
|
|
25209
25705
|
// src/chat/state-sync.ts
|
|
25210
25706
|
init_stateRepo();
|
|
25211
|
-
import * as
|
|
25212
|
-
import * as
|
|
25707
|
+
import * as fs51 from "fs";
|
|
25708
|
+
import * as path51 from "path";
|
|
25213
25709
|
function jsonlLines2(text2) {
|
|
25214
25710
|
return text2.split("\n").filter((line) => line.length > 0);
|
|
25215
25711
|
}
|
|
@@ -25226,15 +25722,15 @@ function mergeJsonl2(localText, remoteText) {
|
|
|
25226
25722
|
function syncJsonlFileFromState(opts) {
|
|
25227
25723
|
const remote = readStateText(opts.config, opts.cwd, opts.statePath);
|
|
25228
25724
|
if (!remote) return;
|
|
25229
|
-
const local =
|
|
25725
|
+
const local = fs51.existsSync(opts.localPath) ? fs51.readFileSync(opts.localPath, "utf-8") : "";
|
|
25230
25726
|
const next = mergeJsonl2(local, remote.content);
|
|
25231
25727
|
if (next === local) return;
|
|
25232
|
-
|
|
25233
|
-
|
|
25728
|
+
fs51.mkdirSync(path51.dirname(opts.localPath), { recursive: true });
|
|
25729
|
+
fs51.writeFileSync(opts.localPath, next);
|
|
25234
25730
|
}
|
|
25235
25731
|
function persistJsonlFileToState(opts) {
|
|
25236
|
-
if (!
|
|
25237
|
-
const localText =
|
|
25732
|
+
if (!fs51.existsSync(opts.localPath)) return;
|
|
25733
|
+
const localText = fs51.readFileSync(opts.localPath, "utf-8");
|
|
25238
25734
|
for (let attempt = 1; attempt <= 3; attempt += 1) {
|
|
25239
25735
|
const remote = readStateText(opts.config, opts.cwd, opts.statePath);
|
|
25240
25736
|
const body = mergeJsonl2(localText, remote?.content ?? "");
|
|
@@ -25293,6 +25789,7 @@ var DEFAULT_HARD_CAP_MS = 30 * 6e4;
|
|
|
25293
25789
|
var DEFAULT_POLL_MS2 = 3e3;
|
|
25294
25790
|
async function runInteractiveMode(opts) {
|
|
25295
25791
|
const sessionFile = sessionFilePath(opts.cwd, opts.sessionId);
|
|
25792
|
+
const store = opts.store ?? createSessionStore({ sessionId: opts.sessionId, sessionFile });
|
|
25296
25793
|
const idleExitMs = opts.meta.idleExitMs ?? DEFAULT_IDLE_EXIT_MS;
|
|
25297
25794
|
const hardCapMs = opts.meta.hardCapMs ?? DEFAULT_HARD_CAP_MS;
|
|
25298
25795
|
const startedAt = Date.now();
|
|
@@ -25323,10 +25820,10 @@ async function runInteractiveMode(opts) {
|
|
|
25323
25820
|
let watermark = 0;
|
|
25324
25821
|
let turnsCompleted = 0;
|
|
25325
25822
|
while (true) {
|
|
25326
|
-
if (opts.stateConfig && !opts.skipGit) {
|
|
25823
|
+
if (store.backend !== "convex" && opts.stateConfig && !opts.skipGit) {
|
|
25327
25824
|
syncChatSessionFromState(opts.stateConfig, opts.cwd, opts.sessionId);
|
|
25328
25825
|
}
|
|
25329
|
-
const turns =
|
|
25826
|
+
const turns = await store.readTurns();
|
|
25330
25827
|
const pendingIdx = findNextUserTurn(turns, watermark);
|
|
25331
25828
|
if (pendingIdx === -1) {
|
|
25332
25829
|
const result = await waitForNextUserMessage({
|
|
@@ -25337,7 +25834,7 @@ async function runInteractiveMode(opts) {
|
|
|
25337
25834
|
deadlineMs,
|
|
25338
25835
|
pollIntervalMs: opts.pollIntervalMs ?? DEFAULT_POLL_MS2,
|
|
25339
25836
|
skipPull: opts.skipGit,
|
|
25340
|
-
...opts.stateConfig ? {
|
|
25837
|
+
...store.backend === "convex" ? { readTurns: () => store.readTurns() } : opts.stateConfig ? {
|
|
25341
25838
|
sync: () => syncChatSessionFromState(opts.stateConfig, opts.cwd, opts.sessionId)
|
|
25342
25839
|
} : {}
|
|
25343
25840
|
});
|
|
@@ -25365,6 +25862,7 @@ async function runInteractiveMode(opts) {
|
|
|
25365
25862
|
quiet: opts.quiet,
|
|
25366
25863
|
invokeAgent: opts.invokeAgent,
|
|
25367
25864
|
stateConfig: opts.stateConfig,
|
|
25865
|
+
store,
|
|
25368
25866
|
...opts.reasoningEffort ? { reasoningEffort: opts.reasoningEffort } : {}
|
|
25369
25867
|
});
|
|
25370
25868
|
} catch (err) {
|
|
@@ -25380,7 +25878,7 @@ async function runInteractiveMode(opts) {
|
|
|
25380
25878
|
turnsCompleted += 1;
|
|
25381
25879
|
if (!opts.skipGit) commitTurn(opts.cwd, opts.sessionId, opts.verbose ?? false, opts.stateConfig ?? null);
|
|
25382
25880
|
}
|
|
25383
|
-
watermark =
|
|
25881
|
+
watermark = (await store.readTurns()).length;
|
|
25384
25882
|
}
|
|
25385
25883
|
}
|
|
25386
25884
|
function findNextUserTurn(turns, fromIdx) {
|
|
@@ -25499,7 +25997,7 @@ async function runChat(argv) {
|
|
|
25499
25997
|
${CHAT_HELP}`);
|
|
25500
25998
|
return 64;
|
|
25501
25999
|
}
|
|
25502
|
-
const cwd = args.cwd ?
|
|
26000
|
+
const cwd = args.cwd ? path52.resolve(args.cwd) : process.cwd();
|
|
25503
26001
|
const sessionId = args.sessionId;
|
|
25504
26002
|
const runRequest = readRunRequestFromEnv();
|
|
25505
26003
|
if (runRequest && "request" in runRequest) {
|
|
@@ -25564,7 +26062,7 @@ ${CHAT_HELP}`);
|
|
|
25564
26062
|
const sink = buildSink(cwd, sessionId, args.dashboardUrl);
|
|
25565
26063
|
const meta = readMeta(sessionFile);
|
|
25566
26064
|
process.stdout.write(
|
|
25567
|
-
`\u2192 kody:chat: session file=${sessionFile} exists=${
|
|
26065
|
+
`\u2192 kody:chat: session file=${sessionFile} exists=${fs52.existsSync(sessionFile)} meta=${meta ? meta.mode : "none"}
|
|
25568
26066
|
`
|
|
25569
26067
|
);
|
|
25570
26068
|
try {
|
|
@@ -25679,8 +26177,8 @@ var FlyClient = class {
|
|
|
25679
26177
|
get fetch() {
|
|
25680
26178
|
return this.opts.fetchImpl ?? fetch;
|
|
25681
26179
|
}
|
|
25682
|
-
async call(
|
|
25683
|
-
const res = await this.fetch(`${FLY_API_BASE}${
|
|
26180
|
+
async call(path53, init = {}) {
|
|
26181
|
+
const res = await this.fetch(`${FLY_API_BASE}${path53}`, {
|
|
25684
26182
|
method: init.method ?? "GET",
|
|
25685
26183
|
headers: {
|
|
25686
26184
|
Authorization: `Bearer ${this.opts.token}`,
|
|
@@ -25691,7 +26189,7 @@ var FlyClient = class {
|
|
|
25691
26189
|
if (res.status === 404 && init.allow404) return null;
|
|
25692
26190
|
if (!res.ok) {
|
|
25693
26191
|
const text2 = await res.text().catch(() => "");
|
|
25694
|
-
throw new Error(`Fly API ${res.status} on ${
|
|
26192
|
+
throw new Error(`Fly API ${res.status} on ${path53}: ${text2.slice(0, 200) || res.statusText}`);
|
|
25695
26193
|
}
|
|
25696
26194
|
if (res.status === 204) return null;
|
|
25697
26195
|
const raw = await res.text();
|
|
@@ -26447,7 +26945,7 @@ async function poolServe() {
|
|
|
26447
26945
|
|
|
26448
26946
|
// src/servers/runner-serve.ts
|
|
26449
26947
|
import { spawn as spawn8 } from "child_process";
|
|
26450
|
-
import * as
|
|
26948
|
+
import * as fs53 from "fs";
|
|
26451
26949
|
import { createServer as createServer6 } from "http";
|
|
26452
26950
|
var DEFAULT_PORT2 = 8080;
|
|
26453
26951
|
var DEFAULT_WORKDIR = "/workspace/repo";
|
|
@@ -26582,8 +27080,8 @@ async function defaultRunJob(job) {
|
|
|
26582
27080
|
const workdir = process.env.RUNNER_WORKDIR ?? DEFAULT_WORKDIR;
|
|
26583
27081
|
const branch = job.ref ?? "main";
|
|
26584
27082
|
const authUrl = `https://x-access-token:${job.githubToken}@github.com/${job.repo}.git`;
|
|
26585
|
-
|
|
26586
|
-
|
|
27083
|
+
fs53.rmSync(workdir, { recursive: true, force: true });
|
|
27084
|
+
fs53.mkdirSync(workdir, { recursive: true });
|
|
26587
27085
|
const allSecrets = typeof job.allSecrets === "string" ? job.allSecrets : JSON.stringify(job.allSecrets ?? {});
|
|
26588
27086
|
const target = job.runRequest.target;
|
|
26589
27087
|
const interactive = target.type === "chat";
|