@kody-ade/kody-engine 0.4.607 → 0.4.610
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/README.md +1 -1
- package/dist/bin/kody.js +266 -214
- package/dist/capabilities/live-agent/capability.md +9 -0
- package/dist/capabilities/live-agent/definition.json +32 -0
- package/dist/implementations/live-agent/profile.json +30 -0
- package/dist/implementations/live-agent/prompt.md +38 -0
- package/package.json +1 -1
- package/templates/kody.yml +0 -2
package/README.md
CHANGED
|
@@ -113,7 +113,7 @@ model key at runtime; user secrets are not copied into Actions. `KODY_TOKEN`
|
|
|
113
113
|
remains an optional GitHub authentication fallback when Kody must trigger
|
|
114
114
|
downstream workflows or modify `.github/workflows/*`.
|
|
115
115
|
|
|
116
|
-
The consumer workflow listens on `issue_comment` for `@kody ...` dispatch and `workflow_dispatch` for manual runs
|
|
116
|
+
The consumer workflow listens on `issue_comment` for `@kody ...` dispatch and `workflow_dispatch` for manual runs and chat mode. Convex owns scheduled Loop wakeups.
|
|
117
117
|
|
|
118
118
|
## Commands
|
|
119
119
|
|
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.610",
|
|
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",
|
|
@@ -3158,6 +3158,13 @@ function createStateBackendFromEnv(env = process.env, client) {
|
|
|
3158
3158
|
});
|
|
3159
3159
|
return Array.isArray(result) ? result : [];
|
|
3160
3160
|
},
|
|
3161
|
+
async replaceLoopWakeRegistrations(tenantId2, loopIds, updatedAt) {
|
|
3162
|
+
await transport.mutation(anyApi.loopWakes.replaceRegistrations, {
|
|
3163
|
+
tenantId: requireTenant(tenantId2),
|
|
3164
|
+
loopIds: [...new Set(loopIds.map((id) => requireNonEmpty(id, "loopId")))],
|
|
3165
|
+
updatedAt: requireNonEmpty(updatedAt, "updatedAt")
|
|
3166
|
+
});
|
|
3167
|
+
},
|
|
3161
3168
|
async get(tenantId2, taskKey, kind) {
|
|
3162
3169
|
const result = await transport.query(anyApi.taskState.get, {
|
|
3163
3170
|
tenantId: requireTenant(tenantId2),
|
|
@@ -3176,6 +3183,26 @@ function createStateBackendFromEnv(env = process.env, client) {
|
|
|
3176
3183
|
...expectedUpdatedAt ? { expectedUpdatedAt } : {}
|
|
3177
3184
|
});
|
|
3178
3185
|
},
|
|
3186
|
+
async getAgentState(tenantId2, agent) {
|
|
3187
|
+
const result = await transport.query(anyApi.agentStates.get, {
|
|
3188
|
+
tenantId: requireTenant(tenantId2),
|
|
3189
|
+
agent: requireNonEmpty(agent, "agent")
|
|
3190
|
+
});
|
|
3191
|
+
return result ?? null;
|
|
3192
|
+
},
|
|
3193
|
+
async saveAgentState(tenantId2, state, expectedRevision) {
|
|
3194
|
+
await transport.mutation(anyApi.agentStates.save, {
|
|
3195
|
+
tenantId: requireTenant(tenantId2),
|
|
3196
|
+
state,
|
|
3197
|
+
...expectedRevision === void 0 ? {} : { expectedRevision }
|
|
3198
|
+
});
|
|
3199
|
+
},
|
|
3200
|
+
async resetAgentState(tenantId2, agent) {
|
|
3201
|
+
await transport.mutation(anyApi.agentStates.reset, {
|
|
3202
|
+
tenantId: requireTenant(tenantId2),
|
|
3203
|
+
agent: requireNonEmpty(agent, "agent")
|
|
3204
|
+
});
|
|
3205
|
+
},
|
|
3179
3206
|
async getRepoDoc(tenantId2, kind) {
|
|
3180
3207
|
const result = await transport.query(anyApi.repoDocs.get, {
|
|
3181
3208
|
tenantId: requireTenant(tenantId2),
|
|
@@ -14586,6 +14613,18 @@ function mergeLoopDefinitions(repositoryLoops, runtimeLoops) {
|
|
|
14586
14613
|
}
|
|
14587
14614
|
return [...byId.values()].sort((left, right) => left.id.localeCompare(right.id));
|
|
14588
14615
|
}
|
|
14616
|
+
function loopWakeRegistrationIds(loops) {
|
|
14617
|
+
return loops.filter((loop) => loop.enabled && loop.trigger.type === "schedule").map((loop) => loop.id).sort();
|
|
14618
|
+
}
|
|
14619
|
+
async function syncLoopWakeRegistrations(backend, tenantId2, loops, updatedAt, log2) {
|
|
14620
|
+
try {
|
|
14621
|
+
await backend.replaceLoopWakeRegistrations(tenantId2, loopWakeRegistrationIds(loops), updatedAt);
|
|
14622
|
+
return true;
|
|
14623
|
+
} catch {
|
|
14624
|
+
log2("\u2192 kody: Convex Loop wake registration backfill skipped; existing Loop execution continues");
|
|
14625
|
+
return false;
|
|
14626
|
+
}
|
|
14627
|
+
}
|
|
14589
14628
|
function loopDispatchSlot(loop, now, force, nonce) {
|
|
14590
14629
|
return force ? `manual:${now.toISOString()}:${nonce}` : dueSlot(loop, now);
|
|
14591
14630
|
}
|
|
@@ -14647,6 +14686,14 @@ var init_dispatchLoops = __esm({
|
|
|
14647
14686
|
const requestedLoopId = typeof ctx.args.loop === "string" ? ctx.args.loop.trim() : "";
|
|
14648
14687
|
const backend = createStateBackendFromEnv();
|
|
14649
14688
|
const loops = mergeLoopDefinitions(listLoopDefinitions(ctx.cwd), await backend.listLoops(tenantId2));
|
|
14689
|
+
await syncLoopWakeRegistrations(
|
|
14690
|
+
backend,
|
|
14691
|
+
tenantId2,
|
|
14692
|
+
loops,
|
|
14693
|
+
now.toISOString(),
|
|
14694
|
+
(message) => process.stderr.write(`${message}
|
|
14695
|
+
`)
|
|
14696
|
+
);
|
|
14650
14697
|
const due = selectRunnableLoops(loops, now, {
|
|
14651
14698
|
force,
|
|
14652
14699
|
...requestedLoopId ? { loopId: requestedLoopId } : {}
|
|
@@ -16442,14 +16489,105 @@ ${truncate2(issue2.body, FINDING_BODY_MAX_BYTES)}`;
|
|
|
16442
16489
|
}
|
|
16443
16490
|
});
|
|
16444
16491
|
|
|
16445
|
-
// src/scripts/
|
|
16492
|
+
// src/scripts/loadLiveAgent.ts
|
|
16446
16493
|
import * as fs43 from "fs";
|
|
16494
|
+
function tenant(config) {
|
|
16495
|
+
const [envOwner, envRepo] = (process.env.GITHUB_REPOSITORY ?? "").split("/");
|
|
16496
|
+
const owner = config.github?.owner?.trim() || envOwner;
|
|
16497
|
+
const repo = config.github?.repo?.trim() || envRepo;
|
|
16498
|
+
if (!owner || !repo) throw new Error("Repository identity is required for live Agent execution");
|
|
16499
|
+
return `${owner}/${repo}`;
|
|
16500
|
+
}
|
|
16501
|
+
function frontmatter(raw) {
|
|
16502
|
+
const match = /^---\n([\s\S]*?)\n---/.exec(raw);
|
|
16503
|
+
if (!match) return {};
|
|
16504
|
+
const result = {};
|
|
16505
|
+
for (const line of match[1].split("\n")) {
|
|
16506
|
+
const at = line.indexOf(":");
|
|
16507
|
+
if (at < 0) continue;
|
|
16508
|
+
const key = line.slice(0, at).trim();
|
|
16509
|
+
const value = line.slice(at + 1).trim();
|
|
16510
|
+
result[key] = value.startsWith("[") ? value.slice(1, -1).split(",").map((entry) => entry.trim()).filter(Boolean) : value;
|
|
16511
|
+
}
|
|
16512
|
+
return result;
|
|
16513
|
+
}
|
|
16514
|
+
function guidanceBody(row, agent) {
|
|
16515
|
+
if (typeof row.doc?.body !== "string") return null;
|
|
16516
|
+
const metadata = frontmatter(row.doc.body);
|
|
16517
|
+
const audience = Array.isArray(metadata.agent) ? metadata.agent : [metadata.agent ?? "*"];
|
|
16518
|
+
if (!audience.includes("*") && !audience.includes(agent)) return null;
|
|
16519
|
+
return row.doc.body.replace(/^---\n[\s\S]*?\n---\n?/, "").trim();
|
|
16520
|
+
}
|
|
16521
|
+
function intentBody(row) {
|
|
16522
|
+
if (typeof row.doc?.body !== "string") return null;
|
|
16523
|
+
return row.doc.body.replace(/^---\n[\s\S]*?\n---\n?/, "").trim() || null;
|
|
16524
|
+
}
|
|
16525
|
+
var loadLiveAgent;
|
|
16526
|
+
var init_loadLiveAgent = __esm({
|
|
16527
|
+
"src/scripts/loadLiveAgent.ts"() {
|
|
16528
|
+
"use strict";
|
|
16529
|
+
init_definition_paths();
|
|
16530
|
+
init_agents();
|
|
16531
|
+
init_state_backend();
|
|
16532
|
+
loadLiveAgent = async (ctx, profile) => {
|
|
16533
|
+
const agent = String(ctx.args.agent ?? ctx.data.jobAgent ?? "").trim();
|
|
16534
|
+
if (!agent) throw new Error("loadLiveAgent: agent is required");
|
|
16535
|
+
const file = resolveAgentFile2(ctx.cwd, agent, agentsRoot(ctx.cwd));
|
|
16536
|
+
const raw = fs43.existsSync(file) ? fs43.readFileSync(file, "utf8") : "";
|
|
16537
|
+
const metadata = frontmatter(raw);
|
|
16538
|
+
const assignedIntent = typeof metadata.primaryIntent === "string" ? metadata.primaryIntent : "";
|
|
16539
|
+
const requestedIntent = String(ctx.args.intent ?? "").trim();
|
|
16540
|
+
const intent = requestedIntent || assignedIntent;
|
|
16541
|
+
if (!intent || requestedIntent && assignedIntent !== requestedIntent) {
|
|
16542
|
+
throw new Error(`Live Agent '${agent}' does not have the requested primary Intent`);
|
|
16543
|
+
}
|
|
16544
|
+
const backend = createStateBackendFromEnv();
|
|
16545
|
+
const tenantId2 = tenant(ctx.config);
|
|
16546
|
+
const [stateRow, intentRow, policies, constraints, context] = await Promise.all([
|
|
16547
|
+
backend.getAgentState(tenantId2, agent),
|
|
16548
|
+
backend.getRepoDoc(tenantId2, `intent:${intent}`),
|
|
16549
|
+
backend.listRepoDocs(tenantId2, "policy:"),
|
|
16550
|
+
backend.listRepoDocs(tenantId2, "constraint:"),
|
|
16551
|
+
backend.listRepoDocs(tenantId2, "context:")
|
|
16552
|
+
]);
|
|
16553
|
+
if (!stateRow) throw new Error(`Live Agent '${agent}' has no AgentState`);
|
|
16554
|
+
const state = stateRow.state;
|
|
16555
|
+
const selectedIntentBody = intentRow ? intentBody(intentRow) : null;
|
|
16556
|
+
if (!selectedIntentBody) throw new Error(`Primary Intent '${intent}' is missing`);
|
|
16557
|
+
const render = (rows) => rows.map((row) => guidanceBody(row, agent)).filter(Boolean).join("\n\n") || "None assigned.";
|
|
16558
|
+
ctx.data.agentIdentity = loadAgentIdentity(ctx.cwd, agent);
|
|
16559
|
+
ctx.data.liveAgentIntent = selectedIntentBody;
|
|
16560
|
+
ctx.data.liveAgentPolicies = render(policies);
|
|
16561
|
+
ctx.data.liveAgentConstraints = render(constraints);
|
|
16562
|
+
ctx.data.liveAgentContext = render(context);
|
|
16563
|
+
ctx.data.liveAgentCapabilities = Array.isArray(metadata.capabilities) ? metadata.capabilities.map((slug) => `- ${slug}`).join("\n") : "None assigned.";
|
|
16564
|
+
ctx.data.liveAgentSlug = agent;
|
|
16565
|
+
ctx.data.liveAgentPreviousRevision = Number(state.revision ?? 0);
|
|
16566
|
+
ctx.data.jobState = {
|
|
16567
|
+
state: {
|
|
16568
|
+
version: 1,
|
|
16569
|
+
rev: Number(state.revision ?? 0),
|
|
16570
|
+
cursor: String(state.cursor ?? "idle"),
|
|
16571
|
+
data: state.data ?? {},
|
|
16572
|
+
done: false
|
|
16573
|
+
}
|
|
16574
|
+
};
|
|
16575
|
+
ctx.data.jobStateJson = JSON.stringify(state, null, 2);
|
|
16576
|
+
ctx.data.capabilityTools = ["start_capability"];
|
|
16577
|
+
ctx.data.capabilityToolMode = "lock";
|
|
16578
|
+
profile.claudeCode.enableSubmitTool = true;
|
|
16579
|
+
};
|
|
16580
|
+
}
|
|
16581
|
+
});
|
|
16582
|
+
|
|
16583
|
+
// src/scripts/kodyVariables.ts
|
|
16584
|
+
import * as fs44 from "fs";
|
|
16447
16585
|
import * as path41 from "path";
|
|
16448
16586
|
function readKodyVariables(cwd) {
|
|
16449
16587
|
const full = path41.join(cwd, KODY_VARIABLES_REL_PATH);
|
|
16450
16588
|
let raw;
|
|
16451
16589
|
try {
|
|
16452
|
-
raw =
|
|
16590
|
+
raw = fs44.readFileSync(full, "utf-8");
|
|
16453
16591
|
} catch {
|
|
16454
16592
|
return {};
|
|
16455
16593
|
}
|
|
@@ -16474,7 +16612,7 @@ var init_kodyVariables = __esm({
|
|
|
16474
16612
|
});
|
|
16475
16613
|
|
|
16476
16614
|
// src/scripts/loadQaContext.ts
|
|
16477
|
-
import * as
|
|
16615
|
+
import * as fs45 from "fs";
|
|
16478
16616
|
import * as path42 from "path";
|
|
16479
16617
|
function parseSlugList(value) {
|
|
16480
16618
|
const inner = value.startsWith("[") && value.endsWith("]") ? value.slice(1, -1) : value;
|
|
@@ -16505,17 +16643,17 @@ function readProfileAgents(raw) {
|
|
|
16505
16643
|
}
|
|
16506
16644
|
function readProfile(cwd) {
|
|
16507
16645
|
const dir = path42.join(cwd, CONTEXT_DIR_REL_PATH);
|
|
16508
|
-
if (!
|
|
16646
|
+
if (!fs45.existsSync(dir)) return "";
|
|
16509
16647
|
let entries;
|
|
16510
16648
|
try {
|
|
16511
|
-
entries =
|
|
16649
|
+
entries = fs45.readdirSync(dir).filter((f) => f.endsWith(".md")).sort();
|
|
16512
16650
|
} catch {
|
|
16513
16651
|
return "";
|
|
16514
16652
|
}
|
|
16515
16653
|
const blocks = [];
|
|
16516
16654
|
for (const file of entries) {
|
|
16517
16655
|
try {
|
|
16518
|
-
const raw =
|
|
16656
|
+
const raw = fs45.readFileSync(path42.join(dir, file), "utf-8");
|
|
16519
16657
|
const { agent, body } = readProfileAgents(raw);
|
|
16520
16658
|
if (!agent.includes(QA_AGENT) && !agent.includes(ALL_AGENTS)) continue;
|
|
16521
16659
|
blocks.push(`## ${file}
|
|
@@ -16565,7 +16703,7 @@ var init_loadQaContext = __esm({
|
|
|
16565
16703
|
|
|
16566
16704
|
// src/scripts/loadSimpleCapability.ts
|
|
16567
16705
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
16568
|
-
import * as
|
|
16706
|
+
import * as fs46 from "fs";
|
|
16569
16707
|
import * as os6 from "os";
|
|
16570
16708
|
import * as path43 from "path";
|
|
16571
16709
|
function registerCapabilitySubagents(profile, toolRoot, toolFiles) {
|
|
@@ -16580,7 +16718,7 @@ function registerCapabilitySubagents(profile, toolRoot, toolFiles) {
|
|
|
16580
16718
|
profile.subagentTemplates = {
|
|
16581
16719
|
...profile.subagentTemplates ?? {},
|
|
16582
16720
|
...Object.fromEntries(
|
|
16583
|
-
subagentFiles.map(({ name, file }) => [name,
|
|
16721
|
+
subagentFiles.map(({ name, file }) => [name, fs46.readFileSync(path43.join(toolRoot, file), "utf-8")])
|
|
16584
16722
|
)
|
|
16585
16723
|
};
|
|
16586
16724
|
if (!profile.claudeCode.tools.includes("Agent")) {
|
|
@@ -16621,10 +16759,10 @@ function scalar(value) {
|
|
|
16621
16759
|
return value;
|
|
16622
16760
|
}
|
|
16623
16761
|
function listFiles(root) {
|
|
16624
|
-
if (!
|
|
16762
|
+
if (!fs46.existsSync(root)) return [];
|
|
16625
16763
|
const files = [];
|
|
16626
16764
|
const visit = (dir) => {
|
|
16627
|
-
for (const entry of
|
|
16765
|
+
for (const entry of fs46.readdirSync(dir, { withFileTypes: true })) {
|
|
16628
16766
|
const absolute = path43.join(dir, entry.name);
|
|
16629
16767
|
if (entry.isSymbolicLink()) continue;
|
|
16630
16768
|
if (entry.isDirectory()) visit(absolute);
|
|
@@ -16716,7 +16854,7 @@ var init_loadSimpleCapability = __esm({
|
|
|
16716
16854
|
...skillFiles.flatMap((file) => [
|
|
16717
16855
|
`### ${file}`,
|
|
16718
16856
|
"",
|
|
16719
|
-
|
|
16857
|
+
fs46.readFileSync(path43.join(skillRoot, file), "utf-8"),
|
|
16720
16858
|
""
|
|
16721
16859
|
])
|
|
16722
16860
|
] : [],
|
|
@@ -16756,7 +16894,7 @@ var init_loadSimpleCapability = __esm({
|
|
|
16756
16894
|
});
|
|
16757
16895
|
|
|
16758
16896
|
// src/taskContext.ts
|
|
16759
|
-
import * as
|
|
16897
|
+
import * as fs47 from "fs";
|
|
16760
16898
|
import * as path44 from "path";
|
|
16761
16899
|
function buildTaskContext(args) {
|
|
16762
16900
|
return {
|
|
@@ -16773,9 +16911,9 @@ function buildTaskContext(args) {
|
|
|
16773
16911
|
function persistTaskContext(cwd, ctx) {
|
|
16774
16912
|
try {
|
|
16775
16913
|
const dir = runtimeStatePath(cwd, "agent-runs", ctx.runId);
|
|
16776
|
-
|
|
16914
|
+
fs47.mkdirSync(dir, { recursive: true });
|
|
16777
16915
|
const file = path44.join(dir, "task-context.json");
|
|
16778
|
-
|
|
16916
|
+
fs47.writeFileSync(file, `${JSON.stringify(ctx, null, 2)}
|
|
16779
16917
|
`);
|
|
16780
16918
|
return file;
|
|
16781
16919
|
} catch (err) {
|
|
@@ -17696,7 +17834,7 @@ var init_parseReproOutput = __esm({
|
|
|
17696
17834
|
});
|
|
17697
17835
|
|
|
17698
17836
|
// src/scripts/parseSimpleCapabilityOutput.ts
|
|
17699
|
-
import * as
|
|
17837
|
+
import * as fs48 from "fs";
|
|
17700
17838
|
function acceptAuthoritativeCapabilityOutput(ctx, profile, output) {
|
|
17701
17839
|
ctx.data.agentDone = true;
|
|
17702
17840
|
delete ctx.data.agentFailureReason;
|
|
@@ -17713,11 +17851,11 @@ function stringList2(value) {
|
|
|
17713
17851
|
return Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
|
|
17714
17852
|
}
|
|
17715
17853
|
function readOutputFile(outputPath) {
|
|
17716
|
-
if (!outputPath || !
|
|
17854
|
+
if (!outputPath || !fs48.existsSync(outputPath)) return { found: false };
|
|
17717
17855
|
try {
|
|
17718
|
-
return { found: true, value: JSON.parse(
|
|
17856
|
+
return { found: true, value: JSON.parse(fs48.readFileSync(outputPath, "utf-8")) };
|
|
17719
17857
|
} finally {
|
|
17720
|
-
|
|
17858
|
+
fs48.rmSync(outputPath, { force: true });
|
|
17721
17859
|
}
|
|
17722
17860
|
}
|
|
17723
17861
|
function parseOutput(text2) {
|
|
@@ -18339,7 +18477,7 @@ var init_postResearchComment = __esm({
|
|
|
18339
18477
|
});
|
|
18340
18478
|
|
|
18341
18479
|
// src/scripts/prepareBrowserAuth.ts
|
|
18342
|
-
import * as
|
|
18480
|
+
import * as fs49 from "fs";
|
|
18343
18481
|
import * as os7 from "os";
|
|
18344
18482
|
import * as path45 from "path";
|
|
18345
18483
|
function appendAuthMessage(ctx, message) {
|
|
@@ -18385,8 +18523,8 @@ async function githubJson(url, token, checkName) {
|
|
|
18385
18523
|
throw new Error(`GitHub ${checkName} check failed`);
|
|
18386
18524
|
}
|
|
18387
18525
|
function writeKodyStorageState(input) {
|
|
18388
|
-
const directory =
|
|
18389
|
-
|
|
18526
|
+
const directory = fs49.mkdtempSync(path45.join(os7.tmpdir(), "kody-browser-auth-"));
|
|
18527
|
+
fs49.chmodSync(directory, 448);
|
|
18390
18528
|
const file = path45.join(directory, "storage-state.json");
|
|
18391
18529
|
const now = Date.now();
|
|
18392
18530
|
const repoEntry = {
|
|
@@ -18418,7 +18556,7 @@ function writeKodyStorageState(input) {
|
|
|
18418
18556
|
}
|
|
18419
18557
|
]
|
|
18420
18558
|
};
|
|
18421
|
-
|
|
18559
|
+
fs49.writeFileSync(file, JSON.stringify(storageState), { mode: 384 });
|
|
18422
18560
|
return { directory, file, auth };
|
|
18423
18561
|
}
|
|
18424
18562
|
function parseSetCookie(value, hostname) {
|
|
@@ -18448,10 +18586,10 @@ function parseSetCookie(value, hostname) {
|
|
|
18448
18586
|
}
|
|
18449
18587
|
function writeCookieStorageState(targetUrl, setCookies) {
|
|
18450
18588
|
const target = new URL(targetUrl);
|
|
18451
|
-
const directory =
|
|
18452
|
-
|
|
18589
|
+
const directory = fs49.mkdtempSync(path45.join(os7.tmpdir(), "kody-browser-auth-"));
|
|
18590
|
+
fs49.chmodSync(directory, 448);
|
|
18453
18591
|
const file = path45.join(directory, "storage-state.json");
|
|
18454
|
-
|
|
18592
|
+
fs49.writeFileSync(
|
|
18455
18593
|
file,
|
|
18456
18594
|
JSON.stringify({
|
|
18457
18595
|
cookies: setCookies.map((cookie) => parseSetCookie(cookie, target.hostname)),
|
|
@@ -18472,9 +18610,9 @@ function currentStorageStatePath(args) {
|
|
|
18472
18610
|
function browserSessionCookieHeader(profile, targetUrl) {
|
|
18473
18611
|
const playwright = profile.claudeCode.mcpServers.find((server) => server.name === "playwright");
|
|
18474
18612
|
const storagePath = currentStorageStatePath(playwright?.args ?? []);
|
|
18475
|
-
if (!storagePath || !
|
|
18613
|
+
if (!storagePath || !fs49.existsSync(storagePath)) return void 0;
|
|
18476
18614
|
const hostname = new URL(targetUrl).hostname;
|
|
18477
|
-
const state = JSON.parse(
|
|
18615
|
+
const state = JSON.parse(fs49.readFileSync(storagePath, "utf-8"));
|
|
18478
18616
|
const cookies = (state.cookies ?? []).filter((cookie) => {
|
|
18479
18617
|
const domain = cookie.domain.replace(/^\./, "");
|
|
18480
18618
|
return hostname === domain || hostname.endsWith(`.${domain}`);
|
|
@@ -18541,9 +18679,9 @@ async function prepareAccountModelSettings(ctx, profile, input) {
|
|
|
18541
18679
|
return true;
|
|
18542
18680
|
}
|
|
18543
18681
|
function mergeStorageStates(existingPath, nextPath) {
|
|
18544
|
-
if (existingPath === nextPath || !
|
|
18545
|
-
const existing = JSON.parse(
|
|
18546
|
-
const next = JSON.parse(
|
|
18682
|
+
if (existingPath === nextPath || !fs49.existsSync(existingPath)) return;
|
|
18683
|
+
const existing = JSON.parse(fs49.readFileSync(existingPath, "utf-8"));
|
|
18684
|
+
const next = JSON.parse(fs49.readFileSync(nextPath, "utf-8"));
|
|
18547
18685
|
const cookies = /* @__PURE__ */ new Map();
|
|
18548
18686
|
for (const cookie of [...existing.cookies ?? [], ...next.cookies ?? []]) {
|
|
18549
18687
|
cookies.set(`${cookie.name}\0${cookie.domain}\0${cookie.path}`, cookie);
|
|
@@ -18557,7 +18695,7 @@ function mergeStorageStates(existingPath, nextPath) {
|
|
|
18557
18695
|
}
|
|
18558
18696
|
origins.set(entry.origin, { origin: entry.origin, localStorage: [...localStorage.values()] });
|
|
18559
18697
|
}
|
|
18560
|
-
|
|
18698
|
+
fs49.writeFileSync(
|
|
18561
18699
|
nextPath,
|
|
18562
18700
|
JSON.stringify({ cookies: [...cookies.values()], origins: [...origins.values()] }),
|
|
18563
18701
|
{ mode: 384 }
|
|
@@ -18654,7 +18792,7 @@ async function prepareKodyRepositoryBrowserAuth(ctx, profile, input) {
|
|
|
18654
18792
|
configurePlaywright(profile, state.file);
|
|
18655
18793
|
const authDirectory = state.directory;
|
|
18656
18794
|
registerRuntimeCleanup(ctx, () => {
|
|
18657
|
-
|
|
18795
|
+
fs49.rmSync(authDirectory, { recursive: true, force: true });
|
|
18658
18796
|
});
|
|
18659
18797
|
appendAuthMessage(
|
|
18660
18798
|
ctx,
|
|
@@ -18662,7 +18800,7 @@ async function prepareKodyRepositoryBrowserAuth(ctx, profile, input) {
|
|
|
18662
18800
|
);
|
|
18663
18801
|
return true;
|
|
18664
18802
|
} catch (error) {
|
|
18665
|
-
if (state)
|
|
18803
|
+
if (state) fs49.rmSync(state.directory, { recursive: true, force: true });
|
|
18666
18804
|
const reason = error instanceof Error ? error.message : String(error);
|
|
18667
18805
|
appendAuthMessage(
|
|
18668
18806
|
ctx,
|
|
@@ -18689,11 +18827,11 @@ async function prepareEmailPasswordBrowserAuth(ctx, profile, input) {
|
|
|
18689
18827
|
state = writeCookieStorageState(input.targetUrl, cookies);
|
|
18690
18828
|
configurePlaywright(profile, state.file);
|
|
18691
18829
|
const authDirectory = state.directory;
|
|
18692
|
-
registerRuntimeCleanup(ctx, () =>
|
|
18830
|
+
registerRuntimeCleanup(ctx, () => fs49.rmSync(authDirectory, { recursive: true, force: true }));
|
|
18693
18831
|
ctx.data.qaAuthBlock = "Auth: the app is already signed in through an engine-provided browser session. The login credentials are not available to you; never request, reveal, or report them.";
|
|
18694
18832
|
return true;
|
|
18695
18833
|
} catch (error) {
|
|
18696
|
-
if (state)
|
|
18834
|
+
if (state) fs49.rmSync(state.directory, { recursive: true, force: true });
|
|
18697
18835
|
const reason = error instanceof Error ? error.message : String(error);
|
|
18698
18836
|
ctx.data.qaAuthBlock = `Auth: the engine could not prepare the app login (${reason}). Note this authenticated surface as a gap.`;
|
|
18699
18837
|
return false;
|
|
@@ -20607,7 +20745,7 @@ var init_tickShellRunner = __esm({
|
|
|
20607
20745
|
});
|
|
20608
20746
|
|
|
20609
20747
|
// src/scripts/runScheduledImplementationTick.ts
|
|
20610
|
-
import * as
|
|
20748
|
+
import * as fs50 from "fs";
|
|
20611
20749
|
import * as path48 from "path";
|
|
20612
20750
|
var runScheduledImplementationTick;
|
|
20613
20751
|
var init_runScheduledImplementationTick = __esm({
|
|
@@ -20636,7 +20774,7 @@ var init_runScheduledImplementationTick = __esm({
|
|
|
20636
20774
|
return;
|
|
20637
20775
|
}
|
|
20638
20776
|
const shellPath = path48.join(profile.dir, shell);
|
|
20639
|
-
if (!
|
|
20777
|
+
if (!fs50.existsSync(shellPath)) {
|
|
20640
20778
|
ctx.output.exitCode = 99;
|
|
20641
20779
|
ctx.output.reason = `runScheduledImplementationTick: shell not found: ${shell} (looked in ${profile.dir})`;
|
|
20642
20780
|
return;
|
|
@@ -20668,13 +20806,13 @@ var init_runScheduledImplementationTick = __esm({
|
|
|
20668
20806
|
|
|
20669
20807
|
// src/scripts/runSimpleCapabilityScript.ts
|
|
20670
20808
|
import { spawnSync as spawnSync3 } from "child_process";
|
|
20671
|
-
import * as
|
|
20809
|
+
import * as fs51 from "fs";
|
|
20672
20810
|
function formatDuration2(timeoutMs) {
|
|
20673
20811
|
return timeoutMs % 6e4 === 0 ? `${timeoutMs / 6e4} minutes` : `${timeoutMs}ms`;
|
|
20674
20812
|
}
|
|
20675
20813
|
function isRegularFile2(filePath) {
|
|
20676
20814
|
try {
|
|
20677
|
-
const stat =
|
|
20815
|
+
const stat = fs51.lstatSync(filePath);
|
|
20678
20816
|
return stat.isFile() && !stat.isSymbolicLink();
|
|
20679
20817
|
} catch {
|
|
20680
20818
|
return false;
|
|
@@ -20753,7 +20891,7 @@ var init_runSimpleCapabilityScript = __esm({
|
|
|
20753
20891
|
});
|
|
20754
20892
|
|
|
20755
20893
|
// src/scripts/runTickScript.ts
|
|
20756
|
-
import * as
|
|
20894
|
+
import * as fs52 from "fs";
|
|
20757
20895
|
import * as path49 from "path";
|
|
20758
20896
|
var runTickScript;
|
|
20759
20897
|
var init_runTickScript = __esm({
|
|
@@ -20787,7 +20925,7 @@ var init_runTickScript = __esm({
|
|
|
20787
20925
|
return;
|
|
20788
20926
|
}
|
|
20789
20927
|
const scriptPath = path49.isAbsolute(tickScript) ? tickScript : path49.join(ctx.cwd, tickScript);
|
|
20790
|
-
if (!
|
|
20928
|
+
if (!fs52.existsSync(scriptPath)) {
|
|
20791
20929
|
ctx.output.exitCode = 99;
|
|
20792
20930
|
ctx.output.reason = `runTickScript: tickScript not found: ${scriptPath}`;
|
|
20793
20931
|
return;
|
|
@@ -20835,6 +20973,41 @@ var init_saveManagedGoalState = __esm({
|
|
|
20835
20973
|
}
|
|
20836
20974
|
});
|
|
20837
20975
|
|
|
20976
|
+
// src/scripts/saveLiveAgentState.ts
|
|
20977
|
+
var saveLiveAgentState;
|
|
20978
|
+
var init_saveLiveAgentState = __esm({
|
|
20979
|
+
"src/scripts/saveLiveAgentState.ts"() {
|
|
20980
|
+
"use strict";
|
|
20981
|
+
init_state_backend();
|
|
20982
|
+
saveLiveAgentState = async (ctx, _profile, agentResult) => {
|
|
20983
|
+
const agent = String(ctx.data.liveAgentSlug ?? "");
|
|
20984
|
+
const previousRevision = Number(ctx.data.liveAgentPreviousRevision ?? 0);
|
|
20985
|
+
const next = ctx.data.nextJobState;
|
|
20986
|
+
if (!agent || !next || typeof next.cursor !== "string" || !next.cursor) {
|
|
20987
|
+
throw new Error(String(ctx.data.nextStateParseError ?? "Live Agent did not submit valid continuation state"));
|
|
20988
|
+
}
|
|
20989
|
+
const [envOwner, envRepo] = (process.env.GITHUB_REPOSITORY ?? "").split("/");
|
|
20990
|
+
const owner = ctx.config.github?.owner?.trim() || envOwner;
|
|
20991
|
+
const repo = ctx.config.github?.repo?.trim() || envRepo;
|
|
20992
|
+
if (!owner || !repo) throw new Error("Repository identity is required for live Agent state");
|
|
20993
|
+
const summary = (agentResult?.finalText ?? "").trim().slice(0, 1e3);
|
|
20994
|
+
await createStateBackendFromEnv().saveAgentState(
|
|
20995
|
+
`${owner}/${repo}`,
|
|
20996
|
+
{
|
|
20997
|
+
version: 1,
|
|
20998
|
+
agent,
|
|
20999
|
+
revision: previousRevision + 1,
|
|
21000
|
+
cursor: next.cursor,
|
|
21001
|
+
summary,
|
|
21002
|
+
data: next.data && typeof next.data === "object" ? next.data : {},
|
|
21003
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
21004
|
+
},
|
|
21005
|
+
previousRevision
|
|
21006
|
+
);
|
|
21007
|
+
};
|
|
21008
|
+
}
|
|
21009
|
+
});
|
|
21010
|
+
|
|
20838
21011
|
// src/scripts/setCommentTarget.ts
|
|
20839
21012
|
var setCommentTarget;
|
|
20840
21013
|
var init_setCommentTarget = __esm({
|
|
@@ -21952,7 +22125,7 @@ var init_warmupMcp = __esm({
|
|
|
21952
22125
|
});
|
|
21953
22126
|
|
|
21954
22127
|
// src/scripts/writeAgentRunSummary.ts
|
|
21955
|
-
import * as
|
|
22128
|
+
import * as fs53 from "fs";
|
|
21956
22129
|
var writeAgentRunSummary;
|
|
21957
22130
|
var init_writeAgentRunSummary = __esm({
|
|
21958
22131
|
"src/scripts/writeAgentRunSummary.ts"() {
|
|
@@ -21978,7 +22151,7 @@ var init_writeAgentRunSummary = __esm({
|
|
|
21978
22151
|
if (reason) lines.push(`- **Reason:** ${reason}`);
|
|
21979
22152
|
lines.push("");
|
|
21980
22153
|
try {
|
|
21981
|
-
|
|
22154
|
+
fs53.appendFileSync(summaryPath, `${lines.join("\n")}
|
|
21982
22155
|
`);
|
|
21983
22156
|
} catch {
|
|
21984
22157
|
}
|
|
@@ -22135,6 +22308,7 @@ var init_scripts = __esm({
|
|
|
22135
22308
|
init_loadIssueStateComment();
|
|
22136
22309
|
init_loadJobFromFile();
|
|
22137
22310
|
init_loadLinkedFinding();
|
|
22311
|
+
init_loadLiveAgent();
|
|
22138
22312
|
init_loadMemoryContext();
|
|
22139
22313
|
init_loadPriorArt();
|
|
22140
22314
|
init_loadQaContext();
|
|
@@ -22183,6 +22357,7 @@ var init_scripts = __esm({
|
|
|
22183
22357
|
init_runSimpleCapabilityScript();
|
|
22184
22358
|
init_runTickScript();
|
|
22185
22359
|
init_saveManagedGoalState();
|
|
22360
|
+
init_saveLiveAgentState();
|
|
22186
22361
|
init_saveTaskState();
|
|
22187
22362
|
init_setCommentTarget();
|
|
22188
22363
|
init_setLifecycleLabel();
|
|
@@ -22222,6 +22397,7 @@ var init_scripts = __esm({
|
|
|
22222
22397
|
loadConventions,
|
|
22223
22398
|
loadCoverageRules,
|
|
22224
22399
|
loadLinkedFinding,
|
|
22400
|
+
loadLiveAgent,
|
|
22225
22401
|
loadMemoryContext,
|
|
22226
22402
|
loadPriorArt,
|
|
22227
22403
|
loadQaContext,
|
|
@@ -22257,6 +22433,7 @@ var init_scripts = __esm({
|
|
|
22257
22433
|
saveManagedGoalState
|
|
22258
22434
|
};
|
|
22259
22435
|
postflightScripts = {
|
|
22436
|
+
saveLiveAgentState,
|
|
22260
22437
|
parseSimpleCapabilityOutput,
|
|
22261
22438
|
parseAgentResult: parseAgentResult2,
|
|
22262
22439
|
parseIssueStateFromAgentResult,
|
|
@@ -22316,7 +22493,7 @@ var init_scripts = __esm({
|
|
|
22316
22493
|
});
|
|
22317
22494
|
|
|
22318
22495
|
// src/stateWorkspace.ts
|
|
22319
|
-
import * as
|
|
22496
|
+
import * as fs54 from "fs";
|
|
22320
22497
|
import * as path51 from "path";
|
|
22321
22498
|
function tenantId(config) {
|
|
22322
22499
|
const owner = config.github?.owner?.trim() || process.env.GITHUB_REPOSITORY?.split("/")[0]?.trim();
|
|
@@ -22325,8 +22502,8 @@ function tenantId(config) {
|
|
|
22325
22502
|
}
|
|
22326
22503
|
function writeRuntimeFile(cwd, relativePath, content) {
|
|
22327
22504
|
const target = path51.join(cwd, RUNTIME_ROOT, relativePath);
|
|
22328
|
-
|
|
22329
|
-
|
|
22505
|
+
fs54.mkdirSync(path51.dirname(target), { recursive: true });
|
|
22506
|
+
fs54.writeFileSync(target, content, "utf8");
|
|
22330
22507
|
}
|
|
22331
22508
|
function record(value) {
|
|
22332
22509
|
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
@@ -22347,8 +22524,8 @@ function memoryIndex(docs) {
|
|
|
22347
22524
|
if (lines.length === 0) return "";
|
|
22348
22525
|
return ["# Kody memory index", "", "One line per backend memory document.", "", ...lines, ""].join("\n");
|
|
22349
22526
|
}
|
|
22350
|
-
async function hydratePrefix(backend,
|
|
22351
|
-
const docs = await backend.listRepoDocs(
|
|
22527
|
+
async function hydratePrefix(backend, tenant2, cwd, prefix) {
|
|
22528
|
+
const docs = await backend.listRepoDocs(tenant2, prefix);
|
|
22352
22529
|
for (const doc of docs) {
|
|
22353
22530
|
const slug = doc.kind.slice(prefix.length);
|
|
22354
22531
|
if (!/^[a-z0-9][a-z0-9_-]{0,63}$/.test(slug)) continue;
|
|
@@ -22361,8 +22538,8 @@ async function hydratePrefix(backend, tenant, cwd, prefix) {
|
|
|
22361
22538
|
if (index) writeRuntimeFile(cwd, "memory/INDEX.md", index);
|
|
22362
22539
|
}
|
|
22363
22540
|
}
|
|
22364
|
-
async function hydrateSingleton(backend,
|
|
22365
|
-
const doc = await backend.getRepoDoc(
|
|
22541
|
+
async function hydrateSingleton(backend, tenant2, cwd, kind, relativePath) {
|
|
22542
|
+
const doc = await backend.getRepoDoc(tenant2, kind);
|
|
22366
22543
|
if (!doc) return;
|
|
22367
22544
|
const body = stringField6(doc.doc, "body");
|
|
22368
22545
|
if (body !== null) {
|
|
@@ -22372,8 +22549,8 @@ async function hydrateSingleton(backend, tenant, cwd, kind, relativePath) {
|
|
|
22372
22549
|
writeRuntimeFile(cwd, relativePath, `${JSON.stringify(doc.doc, null, 2)}
|
|
22373
22550
|
`);
|
|
22374
22551
|
}
|
|
22375
|
-
async function hydrateWorkflows(backend,
|
|
22376
|
-
for (const workflow of await backend.listWorkflows(
|
|
22552
|
+
async function hydrateWorkflows(backend, tenant2, cwd) {
|
|
22553
|
+
for (const workflow of await backend.listWorkflows(tenant2)) {
|
|
22377
22554
|
if (!/^[a-z0-9][a-z0-9_-]{0,79}$/.test(workflow.workflowId)) continue;
|
|
22378
22555
|
writeRuntimeFile(
|
|
22379
22556
|
cwd,
|
|
@@ -22384,25 +22561,25 @@ async function hydrateWorkflows(backend, tenant, cwd) {
|
|
|
22384
22561
|
}
|
|
22385
22562
|
}
|
|
22386
22563
|
async function hydrateStateWorkspace(config, cwd, backendOverride) {
|
|
22387
|
-
const
|
|
22564
|
+
const tenant2 = tenantId(config);
|
|
22388
22565
|
const configured = hasStateBackendConfig();
|
|
22389
|
-
if (!
|
|
22566
|
+
if (!tenant2 || !configured) {
|
|
22390
22567
|
if (process.env.GITHUB_ACTIONS === "true")
|
|
22391
22568
|
throw new Error("Kody backend access is required for runtime workspace documents");
|
|
22392
22569
|
return;
|
|
22393
22570
|
}
|
|
22394
|
-
const key = `${path51.resolve(cwd)}|${
|
|
22571
|
+
const key = `${path51.resolve(cwd)}|${tenant2}`;
|
|
22395
22572
|
if (hydratedWorkspaces.has(key)) return;
|
|
22396
22573
|
const backend = backendOverride ?? createStateBackendFromEnv();
|
|
22397
22574
|
const root = path51.join(cwd, RUNTIME_ROOT);
|
|
22398
|
-
|
|
22575
|
+
fs54.rmSync(root, { recursive: true, force: true });
|
|
22399
22576
|
await Promise.all([
|
|
22400
|
-
hydratePrefix(backend,
|
|
22401
|
-
hydratePrefix(backend,
|
|
22402
|
-
hydrateSingleton(backend,
|
|
22403
|
-
hydrateSingleton(backend,
|
|
22404
|
-
hydrateSingleton(backend,
|
|
22405
|
-
hydrateWorkflows(backend,
|
|
22577
|
+
hydratePrefix(backend, tenant2, cwd, "context:"),
|
|
22578
|
+
hydratePrefix(backend, tenant2, cwd, "memory:"),
|
|
22579
|
+
hydrateSingleton(backend, tenant2, cwd, "instructions", "instructions.md"),
|
|
22580
|
+
hydrateSingleton(backend, tenant2, cwd, "system-prompt", "system-prompt.md"),
|
|
22581
|
+
hydrateSingleton(backend, tenant2, cwd, "variables", "variables.json"),
|
|
22582
|
+
hydrateWorkflows(backend, tenant2, cwd)
|
|
22406
22583
|
]);
|
|
22407
22584
|
hydratedWorkspaces.add(key);
|
|
22408
22585
|
}
|
|
@@ -22482,7 +22659,7 @@ var init_tools = __esm({
|
|
|
22482
22659
|
|
|
22483
22660
|
// src/executor.ts
|
|
22484
22661
|
import { spawn as spawn8 } from "child_process";
|
|
22485
|
-
import * as
|
|
22662
|
+
import * as fs55 from "fs";
|
|
22486
22663
|
import * as os8 from "os";
|
|
22487
22664
|
import * as path52 from "path";
|
|
22488
22665
|
function isMutatingPostflight(scriptName) {
|
|
@@ -23269,7 +23446,7 @@ function resolveProfilePath(profileName, cwd = process.cwd()) {
|
|
|
23269
23446
|
// fallback
|
|
23270
23447
|
];
|
|
23271
23448
|
for (const c of candidates) {
|
|
23272
|
-
if (
|
|
23449
|
+
if (fs55.existsSync(c)) return c;
|
|
23273
23450
|
}
|
|
23274
23451
|
return candidates[0];
|
|
23275
23452
|
}
|
|
@@ -23385,7 +23562,7 @@ function resolveShellTimeoutMs(entry) {
|
|
|
23385
23562
|
async function runShellEntry(entry, ctx, profile) {
|
|
23386
23563
|
const shellName = entry.shell;
|
|
23387
23564
|
const shellPath = path52.join(profile.dir, shellName);
|
|
23388
|
-
if (!
|
|
23565
|
+
if (!fs55.existsSync(shellPath)) {
|
|
23389
23566
|
ctx.skipAgent = true;
|
|
23390
23567
|
ctx.output.exitCode = 99;
|
|
23391
23568
|
ctx.output.reason = `shell script not found: ${shellName} (looked in ${profile.dir})`;
|
|
@@ -23459,9 +23636,9 @@ async function runShellEntry(entry, ctx, profile) {
|
|
|
23459
23636
|
}
|
|
23460
23637
|
let sideChannelText = "";
|
|
23461
23638
|
try {
|
|
23462
|
-
if (
|
|
23463
|
-
sideChannelText =
|
|
23464
|
-
|
|
23639
|
+
if (fs55.existsSync(outputFile)) {
|
|
23640
|
+
sideChannelText = fs55.readFileSync(outputFile, "utf-8");
|
|
23641
|
+
fs55.rmSync(outputFile, { force: true });
|
|
23465
23642
|
}
|
|
23466
23643
|
} catch {
|
|
23467
23644
|
}
|
|
@@ -26194,7 +26371,7 @@ async function hydrateDefinitionsFromEnv(cwd = process.cwd(), env = process.env)
|
|
|
26194
26371
|
|
|
26195
26372
|
// src/kody-cli.ts
|
|
26196
26373
|
import { execFileSync as execFileSync24 } from "child_process";
|
|
26197
|
-
import * as
|
|
26374
|
+
import * as fs56 from "fs";
|
|
26198
26375
|
import * as path53 from "path";
|
|
26199
26376
|
|
|
26200
26377
|
// src/app-auth.ts
|
|
@@ -26242,26 +26419,6 @@ async function ghApp(jwt, apiPath, method = "GET") {
|
|
|
26242
26419
|
}
|
|
26243
26420
|
return await res.json();
|
|
26244
26421
|
}
|
|
26245
|
-
async function ghAppPage(authToken, apiPath) {
|
|
26246
|
-
const res = await fetch(`${GH_API}${apiPath}`, {
|
|
26247
|
-
headers: {
|
|
26248
|
-
Authorization: `Bearer ${authToken}`,
|
|
26249
|
-
Accept: "application/vnd.github+json",
|
|
26250
|
-
"X-GitHub-Api-Version": "2022-11-28",
|
|
26251
|
-
"User-Agent": "kody-engine"
|
|
26252
|
-
}
|
|
26253
|
-
});
|
|
26254
|
-
if (!res.ok) {
|
|
26255
|
-
const body = await res.text().catch(() => "");
|
|
26256
|
-
throw new Error(
|
|
26257
|
-
`GitHub App API GET ${apiPath} \u2192 ${res.status} ${res.statusText}${body ? `: ${body.slice(0, 200)}` : ""}`
|
|
26258
|
-
);
|
|
26259
|
-
}
|
|
26260
|
-
return {
|
|
26261
|
-
data: await res.json(),
|
|
26262
|
-
hasNext: /rel="next"/.test(res.headers.get("link") ?? "")
|
|
26263
|
-
};
|
|
26264
|
-
}
|
|
26265
26422
|
function readAppCreds(env = process.env) {
|
|
26266
26423
|
const appId = env.KODY_APP_ID?.trim();
|
|
26267
26424
|
const privateKey = env.KODY_APP_PRIVATE_KEY;
|
|
@@ -26286,36 +26443,6 @@ async function mintAppInstallationToken(creds) {
|
|
|
26286
26443
|
const tok = await ghApp(jwt, `/app/installations/${installationId}/access_tokens`, "POST");
|
|
26287
26444
|
return tok.token;
|
|
26288
26445
|
}
|
|
26289
|
-
async function discoverAppRepositories(creds) {
|
|
26290
|
-
const jwt = buildAppJwt(creds.appId, creds.privateKey);
|
|
26291
|
-
const installations = [];
|
|
26292
|
-
for (let page = 1; ; page++) {
|
|
26293
|
-
const result = await ghAppPage(jwt, `/app/installations?per_page=100&page=${page}`);
|
|
26294
|
-
installations.push(...result.data.filter((item) => Number.isInteger(item.id) && item.id > 0));
|
|
26295
|
-
if (!result.hasNext) break;
|
|
26296
|
-
}
|
|
26297
|
-
const byRepo = /* @__PURE__ */ new Map();
|
|
26298
|
-
for (const installation of installations) {
|
|
26299
|
-
const token = await mintAppInstallationToken({
|
|
26300
|
-
appId: creds.appId,
|
|
26301
|
-
privateKey: creds.privateKey,
|
|
26302
|
-
installationId: String(installation.id)
|
|
26303
|
-
});
|
|
26304
|
-
for (let page = 1; ; page++) {
|
|
26305
|
-
const result = await ghAppPage(
|
|
26306
|
-
token,
|
|
26307
|
-
`/installation/repositories?per_page=100&page=${page}`
|
|
26308
|
-
);
|
|
26309
|
-
for (const repository of result.data.repositories ?? []) {
|
|
26310
|
-
const repo = repository.full_name?.trim();
|
|
26311
|
-
if (!repo || !/^[^/\s]+\/[^/\s]+$/.test(repo)) continue;
|
|
26312
|
-
byRepo.set(repo.toLowerCase(), { repo, token });
|
|
26313
|
-
}
|
|
26314
|
-
if (!result.hasNext) break;
|
|
26315
|
-
}
|
|
26316
|
-
}
|
|
26317
|
-
return [...byRepo.values()].sort((left, right) => left.repo.localeCompare(right.repo));
|
|
26318
|
-
}
|
|
26319
26446
|
|
|
26320
26447
|
// src/kody-cli.ts
|
|
26321
26448
|
init_capabilityFolders();
|
|
@@ -26997,9 +27124,9 @@ async function resolveAuthToken(env = process.env) {
|
|
|
26997
27124
|
return void 0;
|
|
26998
27125
|
}
|
|
26999
27126
|
function detectPackageManager2(cwd) {
|
|
27000
|
-
if (
|
|
27001
|
-
if (
|
|
27002
|
-
if (
|
|
27127
|
+
if (fs56.existsSync(path53.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
|
|
27128
|
+
if (fs56.existsSync(path53.join(cwd, "yarn.lock"))) return "yarn";
|
|
27129
|
+
if (fs56.existsSync(path53.join(cwd, "bun.lockb"))) return "bun";
|
|
27003
27130
|
return "npm";
|
|
27004
27131
|
}
|
|
27005
27132
|
function shouldChainScheduledWatch(match) {
|
|
@@ -27102,8 +27229,8 @@ function postFailureTail(issueNumber, cwd, reason) {
|
|
|
27102
27229
|
const logPath = lastRunLogPath(cwd);
|
|
27103
27230
|
let tail = "";
|
|
27104
27231
|
try {
|
|
27105
|
-
if (
|
|
27106
|
-
const content =
|
|
27232
|
+
if (fs56.existsSync(logPath)) {
|
|
27233
|
+
const content = fs56.readFileSync(logPath, "utf-8");
|
|
27107
27234
|
tail = content.slice(-3e3);
|
|
27108
27235
|
}
|
|
27109
27236
|
} catch {
|
|
@@ -27198,9 +27325,9 @@ async function runCi(argv) {
|
|
|
27198
27325
|
forceRunCliArgs = { goal: envForceMessage };
|
|
27199
27326
|
}
|
|
27200
27327
|
}
|
|
27201
|
-
if (!args.issueNumber && !autoFallback && !forceRunAction && !runRequestFanOut && eventName === "workflow_dispatch" && dispatchEventPath &&
|
|
27328
|
+
if (!args.issueNumber && !autoFallback && !forceRunAction && !runRequestFanOut && eventName === "workflow_dispatch" && dispatchEventPath && fs56.existsSync(dispatchEventPath)) {
|
|
27202
27329
|
try {
|
|
27203
|
-
const evt = JSON.parse(
|
|
27330
|
+
const evt = JSON.parse(fs56.readFileSync(dispatchEventPath, "utf-8"));
|
|
27204
27331
|
const inputs = objectValue2(evt.inputs);
|
|
27205
27332
|
const issueInput = parseInt(String(inputs?.issue_number ?? ""), 10);
|
|
27206
27333
|
const sessionInput = String(inputs?.sessionId ?? "");
|
|
@@ -27615,7 +27742,7 @@ init_repoWorkspace();
|
|
|
27615
27742
|
|
|
27616
27743
|
// src/scripts/brainTurnLog.ts
|
|
27617
27744
|
init_runtimePaths();
|
|
27618
|
-
import * as
|
|
27745
|
+
import * as fs57 from "fs";
|
|
27619
27746
|
import * as path54 from "path";
|
|
27620
27747
|
import posixPath4 from "path/posix";
|
|
27621
27748
|
var live = /* @__PURE__ */ new Map();
|
|
@@ -27624,8 +27751,8 @@ function brainEventsFilePath(dir, chatId) {
|
|
|
27624
27751
|
}
|
|
27625
27752
|
function lastPersistedSeq(dir, chatId) {
|
|
27626
27753
|
const p = brainEventsFilePath(dir, chatId);
|
|
27627
|
-
if (!
|
|
27628
|
-
const lines =
|
|
27754
|
+
if (!fs57.existsSync(p)) return 0;
|
|
27755
|
+
const lines = fs57.readFileSync(p, "utf-8").split("\n").filter(Boolean);
|
|
27629
27756
|
if (lines.length === 0) return 0;
|
|
27630
27757
|
try {
|
|
27631
27758
|
return JSON.parse(lines[lines.length - 1]).seq || 0;
|
|
@@ -27635,9 +27762,9 @@ function lastPersistedSeq(dir, chatId) {
|
|
|
27635
27762
|
}
|
|
27636
27763
|
function readSince(dir, chatId, since) {
|
|
27637
27764
|
const p = brainEventsFilePath(dir, chatId);
|
|
27638
|
-
if (!
|
|
27765
|
+
if (!fs57.existsSync(p)) return [];
|
|
27639
27766
|
const out = [];
|
|
27640
|
-
for (const line of
|
|
27767
|
+
for (const line of fs57.readFileSync(p, "utf-8").split("\n")) {
|
|
27641
27768
|
if (!line) continue;
|
|
27642
27769
|
try {
|
|
27643
27770
|
const rec = JSON.parse(line);
|
|
@@ -27663,12 +27790,12 @@ function beginTurn(dir, chatId) {
|
|
|
27663
27790
|
};
|
|
27664
27791
|
live.set(chatId, state);
|
|
27665
27792
|
const p = brainEventsFilePath(dir, chatId);
|
|
27666
|
-
|
|
27793
|
+
fs57.mkdirSync(path54.dirname(p), { recursive: true });
|
|
27667
27794
|
return (event) => {
|
|
27668
27795
|
state.seq += 1;
|
|
27669
27796
|
const rec = { seq: state.seq, turn, ts: Date.now(), event };
|
|
27670
27797
|
try {
|
|
27671
|
-
|
|
27798
|
+
fs57.appendFileSync(p, `${JSON.stringify(rec)}
|
|
27672
27799
|
`);
|
|
27673
27800
|
} catch (err) {
|
|
27674
27801
|
process.stderr.write(
|
|
@@ -27707,7 +27834,7 @@ function endTurnIfUnterminated(dir, chatId, errMessage) {
|
|
|
27707
27834
|
event: { type: "error", error: errMessage || "turn ended unexpectedly", chatId }
|
|
27708
27835
|
};
|
|
27709
27836
|
try {
|
|
27710
|
-
|
|
27837
|
+
fs57.appendFileSync(brainEventsFilePath(dir, chatId), `${JSON.stringify(rec)}
|
|
27711
27838
|
`);
|
|
27712
27839
|
} catch {
|
|
27713
27840
|
}
|
|
@@ -29694,55 +29821,6 @@ async function brainTerminalAgent(options) {
|
|
|
29694
29821
|
|
|
29695
29822
|
// src/servers/pool-serve.ts
|
|
29696
29823
|
import { createServer as createServer5 } from "http";
|
|
29697
|
-
|
|
29698
|
-
// src/pool/agency-loop-tick.ts
|
|
29699
|
-
function normalizeRepositories(repositories) {
|
|
29700
|
-
const unique = /* @__PURE__ */ new Set();
|
|
29701
|
-
for (const raw of repositories) {
|
|
29702
|
-
const repo = raw.trim().toLowerCase();
|
|
29703
|
-
if (/^[^/\s]+\/[^/\s]+$/.test(repo)) unique.add(repo);
|
|
29704
|
-
}
|
|
29705
|
-
return [...unique].sort();
|
|
29706
|
-
}
|
|
29707
|
-
async function runAgencyLoopTick(deps) {
|
|
29708
|
-
const repositories = normalizeRepositories(await deps.discover());
|
|
29709
|
-
if (repositories.length === 0) {
|
|
29710
|
-
deps.log("no consumer agencies discovered \u2014 nothing to tick");
|
|
29711
|
-
return { discovered: 0, claimed: 0 };
|
|
29712
|
-
}
|
|
29713
|
-
deps.log(
|
|
29714
|
-
`running scheduled fan-out for ${repositories.length} consumer agenc${repositories.length === 1 ? "y" : "ies"}`
|
|
29715
|
-
);
|
|
29716
|
-
const clock = deps.now ?? Date.now;
|
|
29717
|
-
let claimed = 0;
|
|
29718
|
-
for (const repository of repositories) {
|
|
29719
|
-
const [owner, repo] = repository.split("/");
|
|
29720
|
-
try {
|
|
29721
|
-
const jobId = `sched-${owner}-${repo}-${clock()}`;
|
|
29722
|
-
const result = await deps.claim(owner, repo, {
|
|
29723
|
-
jobId,
|
|
29724
|
-
repo: repository,
|
|
29725
|
-
runRequest: {
|
|
29726
|
-
requestId: jobId,
|
|
29727
|
-
target: { type: "workflow", id: "scheduled-fanout" },
|
|
29728
|
-
intent: "tick",
|
|
29729
|
-
source: "schedule"
|
|
29730
|
-
}
|
|
29731
|
-
});
|
|
29732
|
-
if (result.ok) {
|
|
29733
|
-
claimed++;
|
|
29734
|
-
deps.log(`[${repository}] scheduled fan-out claimed ${result.machineId}`);
|
|
29735
|
-
} else {
|
|
29736
|
-
deps.log(`[${repository}] scheduled fan-out skipped: ${result.reason ?? "runner unavailable"}`);
|
|
29737
|
-
}
|
|
29738
|
-
} catch (error) {
|
|
29739
|
-
deps.log(`[${repository}] scheduled fan-out error: ${error instanceof Error ? error.message : String(error)}`);
|
|
29740
|
-
}
|
|
29741
|
-
}
|
|
29742
|
-
return { discovered: repositories.length, claimed };
|
|
29743
|
-
}
|
|
29744
|
-
|
|
29745
|
-
// src/servers/pool-serve.ts
|
|
29746
29824
|
init_keys();
|
|
29747
29825
|
|
|
29748
29826
|
// src/pool/registry.ts
|
|
@@ -30378,30 +30456,6 @@ async function poolServe() {
|
|
|
30378
30456
|
const tick = setInterval(() => {
|
|
30379
30457
|
registry.resyncAll().catch((err) => log(`resync tick failed: ${err instanceof Error ? err.message : String(err)}`));
|
|
30380
30458
|
}, refillMs);
|
|
30381
|
-
const discoverAgencies = async () => {
|
|
30382
|
-
if (!appCreds) return registry.activeRepos();
|
|
30383
|
-
const repositories = await discoverAppRepositories(appCreds);
|
|
30384
|
-
for (const access of repositories) repoTokens.set(access.repo.toLowerCase(), access.token);
|
|
30385
|
-
return [.../* @__PURE__ */ new Set([...repositories.map((access) => access.repo), ...registry.activeRepos()])];
|
|
30386
|
-
};
|
|
30387
|
-
let agencyTickInFlight = null;
|
|
30388
|
-
const runLoopTick = () => {
|
|
30389
|
-
if (agencyTickInFlight) return agencyTickInFlight;
|
|
30390
|
-
agencyTickInFlight = runAgencyLoopTick({
|
|
30391
|
-
discover: discoverAgencies,
|
|
30392
|
-
claim: (owner, repo, req) => registry.claim(owner, repo, req),
|
|
30393
|
-
log
|
|
30394
|
-
}).catch((err) => log(`agency Loop tick failed: ${err instanceof Error ? err.message : String(err)}`)).finally(() => {
|
|
30395
|
-
agencyTickInFlight = null;
|
|
30396
|
-
});
|
|
30397
|
-
return agencyTickInFlight;
|
|
30398
|
-
};
|
|
30399
|
-
const loopTickEnabled = (process.env.POOL_LOOP_TICK ?? process.env.POOL_CAPABILITY_TICK ?? "1") !== "0";
|
|
30400
|
-
const loopTickMs = envInt2(
|
|
30401
|
-
process.env.POOL_LOOP_TICK_MS ? "POOL_LOOP_TICK_MS" : "POOL_CAPABILITY_TICK_MS",
|
|
30402
|
-
15 * 6e4
|
|
30403
|
-
);
|
|
30404
|
-
const loopTick = loopTickEnabled ? setInterval(() => void runLoopTick(), loopTickMs) : null;
|
|
30405
30459
|
const server = createServer5(async (req, res) => {
|
|
30406
30460
|
try {
|
|
30407
30461
|
if (!req.method || !req.url) return sendJson2(res, 400, { error: "bad request" });
|
|
@@ -30455,11 +30509,9 @@ async function poolServe() {
|
|
|
30455
30509
|
resolve24();
|
|
30456
30510
|
});
|
|
30457
30511
|
});
|
|
30458
|
-
if (loopTickEnabled) void runLoopTick();
|
|
30459
30512
|
const shutdown = (signal) => {
|
|
30460
30513
|
log(`${signal} \u2014 shutting down`);
|
|
30461
30514
|
clearInterval(tick);
|
|
30462
|
-
if (loopTick) clearInterval(loopTick);
|
|
30463
30515
|
server.close(() => process.exit(0));
|
|
30464
30516
|
};
|
|
30465
30517
|
process.once("SIGINT", () => shutdown("SIGINT"));
|
|
@@ -30471,7 +30523,7 @@ async function poolServe() {
|
|
|
30471
30523
|
|
|
30472
30524
|
// src/servers/runner-serve.ts
|
|
30473
30525
|
import { spawn as spawn10 } from "child_process";
|
|
30474
|
-
import * as
|
|
30526
|
+
import * as fs58 from "fs";
|
|
30475
30527
|
import { createServer as createServer6 } from "http";
|
|
30476
30528
|
var DEFAULT_PORT2 = 8080;
|
|
30477
30529
|
var DEFAULT_WORKDIR = "/workspace/repo";
|
|
@@ -30547,8 +30599,8 @@ async function defaultRunJob(job) {
|
|
|
30547
30599
|
const workdir = process.env.RUNNER_WORKDIR ?? DEFAULT_WORKDIR;
|
|
30548
30600
|
const branch = job.ref ?? "main";
|
|
30549
30601
|
const authUrl = `https://x-access-token:${job.githubToken}@github.com/${job.repo}.git`;
|
|
30550
|
-
|
|
30551
|
-
|
|
30602
|
+
fs58.rmSync(workdir, { recursive: true, force: true });
|
|
30603
|
+
fs58.mkdirSync(workdir, { recursive: true });
|
|
30552
30604
|
const allSecrets = typeof job.allSecrets === "string" ? job.allSecrets : JSON.stringify(job.allSecrets ?? {});
|
|
30553
30605
|
const target = job.runRequest.target;
|
|
30554
30606
|
const interactive = target.type === "chat";
|
|
@@ -31136,7 +31188,7 @@ async function main(argv = process.argv.slice(2)) {
|
|
|
31136
31188
|
const args = parseArgs(argv);
|
|
31137
31189
|
const cwdFlag = argv.indexOf("--cwd");
|
|
31138
31190
|
const definitionCwd = cwdFlag >= 0 && argv[cwdFlag + 1] ? argv[cwdFlag + 1] : process.cwd();
|
|
31139
|
-
const shouldHydrate = !(args.command === "server" && args.serverName === "brain-serve") && !hasExplicitDefinitionsRoot(definitionCwd) && (Boolean(process.env.CONVEX_URL?.trim()) || process.env.GITHUB_ACTIONS === "true" && Boolean(process.env.GITHUB_EVENT_NAME));
|
|
31191
|
+
const shouldHydrate = args.command !== "help" && args.command !== "version" && !(args.command === "server" && args.serverName === "brain-serve") && !hasExplicitDefinitionsRoot(definitionCwd) && (Boolean(process.env.CONVEX_URL?.trim()) || process.env.GITHUB_ACTIONS === "true" && Boolean(process.env.GITHUB_EVENT_NAME));
|
|
31140
31192
|
if (shouldHydrate) {
|
|
31141
31193
|
try {
|
|
31142
31194
|
await hydrateDefinitionsFromEnv(definitionCwd);
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "live-agent",
|
|
3
|
+
"action": "live-agent",
|
|
4
|
+
"purpose": "Run one continuation cycle for a selected persistent Agent.",
|
|
5
|
+
"inputSchema": {
|
|
6
|
+
"type": "object",
|
|
7
|
+
"properties": {
|
|
8
|
+
"agent": {
|
|
9
|
+
"type": "string",
|
|
10
|
+
"description": "Selected Agent slug."
|
|
11
|
+
},
|
|
12
|
+
"intent": {
|
|
13
|
+
"type": "string",
|
|
14
|
+
"description": "Primary Intent slug recorded by the Loop."
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"required": ["agent"],
|
|
18
|
+
"additionalProperties": false
|
|
19
|
+
},
|
|
20
|
+
"outputSchema": {
|
|
21
|
+
"type": "object",
|
|
22
|
+
"properties": {
|
|
23
|
+
"cursor": { "type": "string" },
|
|
24
|
+
"data": { "type": "object", "additionalProperties": true }
|
|
25
|
+
},
|
|
26
|
+
"additionalProperties": true
|
|
27
|
+
},
|
|
28
|
+
"effects": ["agent-state"],
|
|
29
|
+
"permissions": [],
|
|
30
|
+
"success": "The Agent completes one cycle and persists its continuation state.",
|
|
31
|
+
"failure": "The Agent cycle fails or its continuation state cannot be persisted."
|
|
32
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "live-agent",
|
|
3
|
+
"role": "primitive",
|
|
4
|
+
"kind": "oneshot",
|
|
5
|
+
"inputs": [
|
|
6
|
+
{ "name": "agent", "flag": "--agent", "type": "string", "required": true, "describe": "Live Agent slug." },
|
|
7
|
+
{ "name": "intent", "flag": "--intent", "type": "string", "required": false, "describe": "Primary Intent slug recorded by the Loop." }
|
|
8
|
+
],
|
|
9
|
+
"claudeCode": {
|
|
10
|
+
"model": "inherit",
|
|
11
|
+
"permissionMode": "default",
|
|
12
|
+
"maxTurns": 100,
|
|
13
|
+
"maxThinkingTokens": null,
|
|
14
|
+
"systemPromptAppend": null,
|
|
15
|
+
"enableSubmitTool": true,
|
|
16
|
+
"tools": ["mcp__kody-capability__start_capability", "mcp__kody-submit__submit_state"],
|
|
17
|
+
"hooks": [], "skills": [], "commands": [], "subagents": [], "plugins": [], "mcpServers": []
|
|
18
|
+
},
|
|
19
|
+
"cliTools": [],
|
|
20
|
+
"scripts": {
|
|
21
|
+
"preflight": [{ "script": "loadLiveAgent" }, { "script": "composePrompt" }],
|
|
22
|
+
"postflight": [
|
|
23
|
+
{ "script": "parseJobStateFromAgentResult", "with": { "fenceLabel": "kody-agent-next-state" } },
|
|
24
|
+
{ "script": "saveLiveAgentState" },
|
|
25
|
+
{ "script": "appendCompanyActivity" }
|
|
26
|
+
]
|
|
27
|
+
},
|
|
28
|
+
"inputArtifacts": [],
|
|
29
|
+
"outputArtifacts": []
|
|
30
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
You are a persistent Kody Agent completing one scheduled cycle.
|
|
2
|
+
|
|
3
|
+
## Identity
|
|
4
|
+
|
|
5
|
+
{{agentIdentity}}
|
|
6
|
+
|
|
7
|
+
## Primary Intent
|
|
8
|
+
|
|
9
|
+
{{liveAgentIntent}}
|
|
10
|
+
|
|
11
|
+
## Policies
|
|
12
|
+
|
|
13
|
+
{{liveAgentPolicies}}
|
|
14
|
+
|
|
15
|
+
## Constraints
|
|
16
|
+
|
|
17
|
+
{{liveAgentConstraints}}
|
|
18
|
+
|
|
19
|
+
## Context
|
|
20
|
+
|
|
21
|
+
{{liveAgentContext}}
|
|
22
|
+
|
|
23
|
+
## Assigned Capabilities
|
|
24
|
+
|
|
25
|
+
{{liveAgentCapabilities}}
|
|
26
|
+
|
|
27
|
+
## Previous continuation
|
|
28
|
+
|
|
29
|
+
```json
|
|
30
|
+
{{jobStateJson}}
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Inspect current conditions, decide the best next action toward the Intent, and use only assigned capabilities. Do not create work merely to appear active. If waiting is correct, record what is being awaited.
|
|
34
|
+
|
|
35
|
+
As your final action, call `submit_state` exactly once with:
|
|
36
|
+
- `cursor`: the next continuation cursor;
|
|
37
|
+
- `data`: compact durable continuation data;
|
|
38
|
+
- `done`: always `false` for a live Agent.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kody-ade/kody-engine",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.610",
|
|
4
4
|
"description": "kody — autonomous development engine. Single-session Claude Code agent behind a generic executor + declarative implementation profiles.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|