@kody-ade/kody-engine 0.4.291 → 0.4.293
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 +279 -155
- package/dist/executables/types.ts +10 -0
- package/package.json +1 -1
package/dist/bin/kody.js
CHANGED
|
@@ -15,7 +15,7 @@ var init_package = __esm({
|
|
|
15
15
|
"package.json"() {
|
|
16
16
|
package_default = {
|
|
17
17
|
name: "@kody-ade/kody-engine",
|
|
18
|
-
version: "0.4.
|
|
18
|
+
version: "0.4.293",
|
|
19
19
|
description: "kody \u2014 autonomous development engine. Single-session Claude Code agent behind a generic executor + declarative executable profiles.",
|
|
20
20
|
license: "MIT",
|
|
21
21
|
type: "module",
|
|
@@ -667,7 +667,7 @@ function parseReasoningEffort(raw) {
|
|
|
667
667
|
function parseProviderModel(s) {
|
|
668
668
|
const slash = s.indexOf("/");
|
|
669
669
|
if (slash <= 0 || slash === s.length - 1) {
|
|
670
|
-
throw new Error(`Invalid model spec '${s}' \u2014 expected 'provider/model' (e.g. 'minimax/MiniMax-
|
|
670
|
+
throw new Error(`Invalid model spec '${s}' \u2014 expected 'provider/model' (e.g. 'minimax/MiniMax-M3')`);
|
|
671
671
|
}
|
|
672
672
|
return { provider: s.slice(0, slash), model: s.slice(slash + 1) };
|
|
673
673
|
}
|
|
@@ -710,6 +710,9 @@ function parseModelRuntimeConfig(modelSpec, rawConfig) {
|
|
|
710
710
|
if (protocol === "openai") out.litellmProvider = "openai";
|
|
711
711
|
return out;
|
|
712
712
|
}
|
|
713
|
+
function litellmModelGroup(model) {
|
|
714
|
+
return model.spec?.trim() || model.model;
|
|
715
|
+
}
|
|
713
716
|
function providerApiKeyEnvVar(provider) {
|
|
714
717
|
if (provider === "anthropic" || provider === "claude") return "ANTHROPIC_API_KEY";
|
|
715
718
|
return `${provider.toUpperCase()}_API_KEY`;
|
|
@@ -735,7 +738,7 @@ function loadConfig(projectDir = process.cwd()) {
|
|
|
735
738
|
const github = recordValue(raw.github) ?? {};
|
|
736
739
|
const agent = recordValue(raw.agent) ?? {};
|
|
737
740
|
if (!agent.model || typeof agent.model !== "string") {
|
|
738
|
-
throw new Error(`kody.config.json: agent.model is required (e.g. "minimax/MiniMax-
|
|
741
|
+
throw new Error(`kody.config.json: agent.model is required (e.g. "minimax/MiniMax-M3")`);
|
|
739
742
|
}
|
|
740
743
|
if (!github.owner || !github.repo) {
|
|
741
744
|
throw new Error(`kody.config.json: github.owner and github.repo are required`);
|
|
@@ -836,10 +839,7 @@ function parseCompanyConfig(raw) {
|
|
|
836
839
|
const r = raw;
|
|
837
840
|
const out = {};
|
|
838
841
|
if (r.activeCapabilities !== void 0)
|
|
839
|
-
out.activeCapabilities = parseSlugArray(
|
|
840
|
-
r.activeCapabilities,
|
|
841
|
-
"company.activeCapabilities"
|
|
842
|
-
);
|
|
842
|
+
out.activeCapabilities = parseSlugArray(r.activeCapabilities, "company.activeCapabilities");
|
|
843
843
|
if (r.activeGoals !== void 0) out.activeGoals = parseGoalActivations(r.activeGoals);
|
|
844
844
|
return Object.keys(out).length > 0 ? out : void 0;
|
|
845
845
|
}
|
|
@@ -2022,12 +2022,16 @@ function getCompanyStoreRoot() {
|
|
|
2022
2022
|
function getCompanyStoreAssetRoot(kind) {
|
|
2023
2023
|
const root = getCompanyStoreRoot();
|
|
2024
2024
|
if (!root) return null;
|
|
2025
|
+
const manifestRoot = resolveManifestAssetRoot(root, kind);
|
|
2026
|
+
if (manifestRoot) return manifestRoot;
|
|
2025
2027
|
const folderByKind = {
|
|
2026
2028
|
capabilities: "capabilities",
|
|
2027
2029
|
goals: "goals",
|
|
2028
2030
|
agents: "agents",
|
|
2029
2031
|
workflows: "workflows"
|
|
2030
2032
|
};
|
|
2033
|
+
const rootLayoutPath = path7.join(root, folderByKind[kind]);
|
|
2034
|
+
if (fs5.existsSync(rootLayoutPath)) return rootLayoutPath;
|
|
2031
2035
|
return path7.join(root, ".kody", folderByKind[kind]);
|
|
2032
2036
|
}
|
|
2033
2037
|
function resolveCompanyStore() {
|
|
@@ -2065,9 +2069,27 @@ function fetchCompanyStore(repo, ref) {
|
|
|
2065
2069
|
function localStoreRoot(repo) {
|
|
2066
2070
|
if (!path7.isAbsolute(repo) && !repo.startsWith(".")) return null;
|
|
2067
2071
|
const root = path7.resolve(repo);
|
|
2068
|
-
if (!fs5.existsSync(path7.join(root, ".kody"))) return null;
|
|
2072
|
+
if (!fs5.existsSync(path7.join(root, ".kody")) && !fs5.existsSync(path7.join(root, STORE_MANIFEST))) return null;
|
|
2069
2073
|
return root;
|
|
2070
2074
|
}
|
|
2075
|
+
function resolveManifestAssetRoot(root, kind) {
|
|
2076
|
+
const manifestPath = path7.join(root, STORE_MANIFEST);
|
|
2077
|
+
if (!fs5.existsSync(manifestPath)) return null;
|
|
2078
|
+
try {
|
|
2079
|
+
const raw = JSON.parse(fs5.readFileSync(manifestPath, "utf8"));
|
|
2080
|
+
const candidates = kind === "agents" ? ["agents", "agent"] : [kind];
|
|
2081
|
+
for (const key of candidates) {
|
|
2082
|
+
const value = raw.assetRoots?.[key];
|
|
2083
|
+
if (typeof value !== "string" || !value.trim()) continue;
|
|
2084
|
+
return path7.resolve(root, value);
|
|
2085
|
+
}
|
|
2086
|
+
} catch (err) {
|
|
2087
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
2088
|
+
process.stderr.write(`[company-store] failed to read ${manifestPath}: ${msg}
|
|
2089
|
+
`);
|
|
2090
|
+
}
|
|
2091
|
+
return null;
|
|
2092
|
+
}
|
|
2071
2093
|
function repoToGitUrl(repo) {
|
|
2072
2094
|
if (/^(https?:|ssh:|git@|file:)/.test(repo)) return repo;
|
|
2073
2095
|
if (isGitHubShorthand(repo)) {
|
|
@@ -2098,7 +2120,7 @@ function setupGithubGitAuth() {
|
|
|
2098
2120
|
} catch {
|
|
2099
2121
|
}
|
|
2100
2122
|
}
|
|
2101
|
-
var DEFAULT_COMPANY_STORE, DEFAULT_COMPANY_STORE_REF, STORE_ENV, REF_ENV, CACHE_ENV, memo;
|
|
2123
|
+
var DEFAULT_COMPANY_STORE, DEFAULT_COMPANY_STORE_REF, STORE_ENV, REF_ENV, CACHE_ENV, STORE_MANIFEST, memo;
|
|
2102
2124
|
var init_companyStore = __esm({
|
|
2103
2125
|
"src/companyStore.ts"() {
|
|
2104
2126
|
"use strict";
|
|
@@ -2107,6 +2129,7 @@ var init_companyStore = __esm({
|
|
|
2107
2129
|
STORE_ENV = "KODY_COMPANY_STORE";
|
|
2108
2130
|
REF_ENV = "KODY_COMPANY_STORE_REF";
|
|
2109
2131
|
CACHE_ENV = "KODY_COMPANY_STORE_CACHE";
|
|
2132
|
+
STORE_MANIFEST = "kody-store.json";
|
|
2110
2133
|
memo = null;
|
|
2111
2134
|
}
|
|
2112
2135
|
});
|
|
@@ -2153,11 +2176,7 @@ function getBuiltinCapabilitiesRoot() {
|
|
|
2153
2176
|
function getExecutableRoots() {
|
|
2154
2177
|
const projectCapabilitiesRoot = getProjectCapabilitiesRoot();
|
|
2155
2178
|
const storeCapabilitiesRoot = getCompanyStoreCapabilitiesRoot();
|
|
2156
|
-
return [
|
|
2157
|
-
projectCapabilitiesRoot,
|
|
2158
|
-
...storeCapabilitiesRoot ? [storeCapabilitiesRoot] : [],
|
|
2159
|
-
getExecutablesRoot()
|
|
2160
|
-
];
|
|
2179
|
+
return [projectCapabilitiesRoot, ...storeCapabilitiesRoot ? [storeCapabilitiesRoot] : [], getExecutablesRoot()];
|
|
2161
2180
|
}
|
|
2162
2181
|
function getCapabilityRoots(projectCapabilitiesRoot = getProjectCapabilitiesRoot()) {
|
|
2163
2182
|
const storeCapabilitiesRoot = getCompanyStoreCapabilitiesRoot();
|
|
@@ -2207,8 +2226,7 @@ function listCapabilityActions(projectCapabilitiesRoot = getProjectCapabilitiesR
|
|
|
2207
2226
|
seen.add(action.action);
|
|
2208
2227
|
out.push(action);
|
|
2209
2228
|
};
|
|
2210
|
-
for (const action of listFolderCapabilityActions(projectCapabilitiesRoot, "project-folder"))
|
|
2211
|
-
add(action);
|
|
2229
|
+
for (const action of listFolderCapabilityActions(projectCapabilitiesRoot, "project-folder")) add(action);
|
|
2212
2230
|
const storeCapabilitiesRoot = getCompanyStoreCapabilitiesRoot();
|
|
2213
2231
|
if (storeCapabilitiesRoot) {
|
|
2214
2232
|
for (const action of listFolderCapabilityActions(storeCapabilitiesRoot, "company-store")) add(action);
|
|
@@ -2265,7 +2283,9 @@ function isSafeName(name) {
|
|
|
2265
2283
|
}
|
|
2266
2284
|
function isCapabilityRoot(root) {
|
|
2267
2285
|
const normalized = path8.normalize(root);
|
|
2268
|
-
|
|
2286
|
+
if (path8.basename(normalized) === "capabilities") return true;
|
|
2287
|
+
const knownRoots = [getProjectCapabilitiesRoot(), getCompanyStoreCapabilitiesRoot(), getBuiltinCapabilitiesRoot()];
|
|
2288
|
+
return knownRoots.some((candidate) => candidate && path8.normalize(candidate) === normalized);
|
|
2269
2289
|
}
|
|
2270
2290
|
function isImplementationProfile(profilePath, requireImplementationProfile) {
|
|
2271
2291
|
if (!requireImplementationProfile) return true;
|
|
@@ -3124,7 +3144,7 @@ async function runAgent(opts) {
|
|
|
3124
3144
|
let noWorkSuccess = false;
|
|
3125
3145
|
try {
|
|
3126
3146
|
const queryOptions = {
|
|
3127
|
-
model: opts.model.model,
|
|
3147
|
+
model: opts.litellmUrl ? litellmModelGroup(opts.model) : opts.model.model,
|
|
3128
3148
|
cwd: opts.cwd,
|
|
3129
3149
|
// Fresh array (never mutate the shared DEFAULT_ALLOWED_TOOLS const) so
|
|
3130
3150
|
// opt-in tools like fetch_repo can be appended below.
|
|
@@ -3470,23 +3490,77 @@ var init_agent = __esm({
|
|
|
3470
3490
|
}
|
|
3471
3491
|
});
|
|
3472
3492
|
|
|
3493
|
+
// src/agents.ts
|
|
3494
|
+
import * as fs9 from "fs";
|
|
3495
|
+
import * as path11 from "path";
|
|
3496
|
+
function stripFrontmatter(raw) {
|
|
3497
|
+
const match = /^---\n[\s\S]*?\n---\n?([\s\S]*)$/.exec(raw);
|
|
3498
|
+
return (match ? match[1] : raw).trim();
|
|
3499
|
+
}
|
|
3500
|
+
function loadAgentIdentity(cwd, slug2, agentsDir = DEFAULT_AGENT_DIR) {
|
|
3501
|
+
const trimmed = slug2.trim();
|
|
3502
|
+
if (!trimmed) throw new Error("loadAgentIdentity: empty agent slug");
|
|
3503
|
+
const agentPath = resolveAgentFile(cwd, trimmed, agentsDir);
|
|
3504
|
+
if (fs9.existsSync(agentPath)) {
|
|
3505
|
+
const body = stripFrontmatter(fs9.readFileSync(agentPath, "utf-8"));
|
|
3506
|
+
if (body) return body;
|
|
3507
|
+
const builtinForEmpty = BUILTIN_AGENTS[trimmed];
|
|
3508
|
+
if (builtinForEmpty) return builtinForEmpty;
|
|
3509
|
+
throw new Error(`loadAgentIdentity: agent '${trimmed}' agent identity body is empty (${agentPath})`);
|
|
3510
|
+
}
|
|
3511
|
+
const builtin = BUILTIN_AGENTS[trimmed];
|
|
3512
|
+
if (builtin) return builtin;
|
|
3513
|
+
throw new Error(`loadAgentIdentity: agent '${trimmed}' declared but ${agentPath} does not exist`);
|
|
3514
|
+
}
|
|
3515
|
+
function resolveAgentFile(cwd, slug2, agentsDir = DEFAULT_AGENT_DIR) {
|
|
3516
|
+
const localPath = path11.join(cwd, agentsDir, `${slug2}.md`);
|
|
3517
|
+
if (fs9.existsSync(localPath)) return localPath;
|
|
3518
|
+
const storeAgentRoot = getCompanyStoreAssetRoot("agents");
|
|
3519
|
+
if (storeAgentRoot) {
|
|
3520
|
+
const storePath = path11.join(storeAgentRoot, `${slug2}.md`);
|
|
3521
|
+
if (fs9.existsSync(storePath)) return storePath;
|
|
3522
|
+
}
|
|
3523
|
+
return localPath;
|
|
3524
|
+
}
|
|
3525
|
+
function frameAgentIdentity(slug2, agent) {
|
|
3526
|
+
return [
|
|
3527
|
+
`## Who you are \u2014 agent identity (authoritative identity)`,
|
|
3528
|
+
``,
|
|
3529
|
+
`You are operating as agent \`${slug2}\`. This identity defines *who* you are:`,
|
|
3530
|
+
`your authority, doctrine, voice, and hard limits. Honour it exactly. Where the`,
|
|
3531
|
+
`this identity's restrictions are stricter than the task, **the agent wins** \u2014 a task`,
|
|
3532
|
+
`can never grant you authority your agent withholds.`,
|
|
3533
|
+
``,
|
|
3534
|
+
agent
|
|
3535
|
+
].join("\n");
|
|
3536
|
+
}
|
|
3537
|
+
var DEFAULT_AGENT_DIR, BUILTIN_AGENTS;
|
|
3538
|
+
var init_agents = __esm({
|
|
3539
|
+
"src/agents.ts"() {
|
|
3540
|
+
"use strict";
|
|
3541
|
+
init_companyStore();
|
|
3542
|
+
DEFAULT_AGENT_DIR = ".kody/agents";
|
|
3543
|
+
BUILTIN_AGENTS = {};
|
|
3544
|
+
}
|
|
3545
|
+
});
|
|
3546
|
+
|
|
3473
3547
|
// src/task-artifacts.ts
|
|
3474
|
-
import
|
|
3475
|
-
import
|
|
3548
|
+
import fs10 from "fs";
|
|
3549
|
+
import path12 from "path";
|
|
3476
3550
|
import posixPath from "path/posix";
|
|
3477
3551
|
function prepareTaskArtifactsDir(cwd, taskId) {
|
|
3478
3552
|
const safeId = String(taskId).replace(/[^a-zA-Z0-9._-]/g, "_");
|
|
3479
3553
|
const absDir = runtimeStatePath(cwd, "task-artifacts", safeId);
|
|
3480
3554
|
const relDir = absDir;
|
|
3481
|
-
|
|
3555
|
+
fs10.mkdirSync(absDir, { recursive: true });
|
|
3482
3556
|
return { taskId: safeId, absDir, relDir };
|
|
3483
3557
|
}
|
|
3484
3558
|
function verifyTaskArtifacts(absDir) {
|
|
3485
3559
|
const missing = [];
|
|
3486
3560
|
for (const name of TASK_ARTIFACT_FILES) {
|
|
3487
|
-
const full =
|
|
3561
|
+
const full = path12.join(absDir, name);
|
|
3488
3562
|
try {
|
|
3489
|
-
const stat =
|
|
3563
|
+
const stat = fs10.statSync(full);
|
|
3490
3564
|
if (!stat.isFile() || stat.size === 0) missing.push(name);
|
|
3491
3565
|
} catch {
|
|
3492
3566
|
missing.push(name);
|
|
@@ -3499,11 +3573,11 @@ function taskArtifactStatePath(taskId, file) {
|
|
|
3499
3573
|
}
|
|
3500
3574
|
function persistTaskArtifactsToState(config, cwd, artifacts) {
|
|
3501
3575
|
for (const file of TASK_ARTIFACT_FILES) {
|
|
3502
|
-
const full =
|
|
3503
|
-
if (!
|
|
3504
|
-
const stat =
|
|
3576
|
+
const full = path12.join(artifacts.absDir, file);
|
|
3577
|
+
if (!fs10.existsSync(full)) continue;
|
|
3578
|
+
const stat = fs10.statSync(full);
|
|
3505
3579
|
if (!stat.isFile() || stat.size === 0) continue;
|
|
3506
|
-
const content =
|
|
3580
|
+
const content = fs10.readFileSync(full, "utf-8");
|
|
3507
3581
|
upsertStateText(
|
|
3508
3582
|
config,
|
|
3509
3583
|
cwd,
|
|
@@ -3583,7 +3657,7 @@ var init_task_artifacts = __esm({
|
|
|
3583
3657
|
|
|
3584
3658
|
// src/gha.ts
|
|
3585
3659
|
import { execFileSync as execFileSync3 } from "child_process";
|
|
3586
|
-
import * as
|
|
3660
|
+
import * as fs16 from "fs";
|
|
3587
3661
|
function getRunUrl() {
|
|
3588
3662
|
const server = process.env.GITHUB_SERVER_URL;
|
|
3589
3663
|
const repo = process.env.GITHUB_REPOSITORY;
|
|
@@ -3594,10 +3668,10 @@ function getRunUrl() {
|
|
|
3594
3668
|
function reactToTriggerComment(cwd) {
|
|
3595
3669
|
if (process.env.GITHUB_EVENT_NAME !== "issue_comment") return;
|
|
3596
3670
|
const eventPath = process.env.GITHUB_EVENT_PATH;
|
|
3597
|
-
if (!eventPath || !
|
|
3671
|
+
if (!eventPath || !fs16.existsSync(eventPath)) return;
|
|
3598
3672
|
let event = null;
|
|
3599
3673
|
try {
|
|
3600
|
-
event = JSON.parse(
|
|
3674
|
+
event = JSON.parse(fs16.readFileSync(eventPath, "utf-8"));
|
|
3601
3675
|
} catch {
|
|
3602
3676
|
return;
|
|
3603
3677
|
}
|
|
@@ -3648,60 +3722,6 @@ var init_gha = __esm({
|
|
|
3648
3722
|
}
|
|
3649
3723
|
});
|
|
3650
3724
|
|
|
3651
|
-
// src/agents.ts
|
|
3652
|
-
import * as fs16 from "fs";
|
|
3653
|
-
import * as path16 from "path";
|
|
3654
|
-
function stripFrontmatter(raw) {
|
|
3655
|
-
const match = /^---\n[\s\S]*?\n---\n?([\s\S]*)$/.exec(raw);
|
|
3656
|
-
return (match ? match[1] : raw).trim();
|
|
3657
|
-
}
|
|
3658
|
-
function loadAgentIdentity(cwd, slug2, agentsDir = DEFAULT_AGENT_DIR) {
|
|
3659
|
-
const trimmed = slug2.trim();
|
|
3660
|
-
if (!trimmed) throw new Error("loadAgentIdentity: empty agent slug");
|
|
3661
|
-
const agentPath = resolveAgentFile(cwd, trimmed, agentsDir);
|
|
3662
|
-
if (fs16.existsSync(agentPath)) {
|
|
3663
|
-
const body = stripFrontmatter(fs16.readFileSync(agentPath, "utf-8"));
|
|
3664
|
-
if (body) return body;
|
|
3665
|
-
const builtinForEmpty = BUILTIN_AGENTS[trimmed];
|
|
3666
|
-
if (builtinForEmpty) return builtinForEmpty;
|
|
3667
|
-
throw new Error(`loadAgentIdentity: agent '${trimmed}' agent identity body is empty (${agentPath})`);
|
|
3668
|
-
}
|
|
3669
|
-
const builtin = BUILTIN_AGENTS[trimmed];
|
|
3670
|
-
if (builtin) return builtin;
|
|
3671
|
-
throw new Error(`loadAgentIdentity: agent '${trimmed}' declared but ${agentPath} does not exist`);
|
|
3672
|
-
}
|
|
3673
|
-
function resolveAgentFile(cwd, slug2, agentsDir = DEFAULT_AGENT_DIR) {
|
|
3674
|
-
const localPath = path16.join(cwd, agentsDir, `${slug2}.md`);
|
|
3675
|
-
if (fs16.existsSync(localPath)) return localPath;
|
|
3676
|
-
const storeAgentRoot = getCompanyStoreAssetRoot("agents");
|
|
3677
|
-
if (storeAgentRoot) {
|
|
3678
|
-
const storePath = path16.join(storeAgentRoot, `${slug2}.md`);
|
|
3679
|
-
if (fs16.existsSync(storePath)) return storePath;
|
|
3680
|
-
}
|
|
3681
|
-
return localPath;
|
|
3682
|
-
}
|
|
3683
|
-
function frameAgentIdentity(slug2, agent) {
|
|
3684
|
-
return [
|
|
3685
|
-
`## Who you are \u2014 agent identity (authoritative identity)`,
|
|
3686
|
-
``,
|
|
3687
|
-
`You are operating as agent \`${slug2}\`. This identity defines *who* you are:`,
|
|
3688
|
-
`your authority, doctrine, voice, and hard limits. Honour it exactly. Where the`,
|
|
3689
|
-
`this identity's restrictions are stricter than the task, **the agent wins** \u2014 a task`,
|
|
3690
|
-
`can never grant you authority your agent withholds.`,
|
|
3691
|
-
``,
|
|
3692
|
-
agent
|
|
3693
|
-
].join("\n");
|
|
3694
|
-
}
|
|
3695
|
-
var DEFAULT_AGENT_DIR, BUILTIN_AGENTS;
|
|
3696
|
-
var init_agents = __esm({
|
|
3697
|
-
"src/agents.ts"() {
|
|
3698
|
-
"use strict";
|
|
3699
|
-
init_companyStore();
|
|
3700
|
-
DEFAULT_AGENT_DIR = ".kody/agents";
|
|
3701
|
-
BUILTIN_AGENTS = {};
|
|
3702
|
-
}
|
|
3703
|
-
});
|
|
3704
|
-
|
|
3705
3725
|
// src/capabilityReport.ts
|
|
3706
3726
|
function parseCapabilityReportsFromText(text) {
|
|
3707
3727
|
const reports = [];
|
|
@@ -5899,6 +5919,7 @@ var init_lifecycleLabels = __esm({
|
|
|
5899
5919
|
// src/litellm.ts
|
|
5900
5920
|
import { execFileSync as execFileSync5, spawn as spawn3 } from "child_process";
|
|
5901
5921
|
import * as fs23 from "fs";
|
|
5922
|
+
import * as net from "net";
|
|
5902
5923
|
import * as os5 from "os";
|
|
5903
5924
|
import * as path22 from "path";
|
|
5904
5925
|
async function checkLitellmHealth(url) {
|
|
@@ -5917,10 +5938,11 @@ function resolveLitellmTimeoutMs() {
|
|
|
5917
5938
|
function generateLitellmConfigYaml(model) {
|
|
5918
5939
|
const apiKeyVar = model.apiKeyEnvVar ?? providerApiKeyEnvVar(model.provider);
|
|
5919
5940
|
const litellmProvider = model.litellmProvider ?? model.provider;
|
|
5941
|
+
const modelGroup = litellmModelGroup(model);
|
|
5920
5942
|
const providerParams = model.baseURL ? [["api_base", model.baseURL]] : [];
|
|
5921
5943
|
return [
|
|
5922
5944
|
"model_list:",
|
|
5923
|
-
` - model_name: ${
|
|
5945
|
+
` - model_name: ${modelGroup}`,
|
|
5924
5946
|
` litellm_params:`,
|
|
5925
5947
|
` model: ${litellmProvider}/${model.model}`,
|
|
5926
5948
|
` api_key: os.environ/${apiKeyVar}`,
|
|
@@ -5931,6 +5953,16 @@ function generateLitellmConfigYaml(model) {
|
|
|
5931
5953
|
""
|
|
5932
5954
|
].join("\n");
|
|
5933
5955
|
}
|
|
5956
|
+
async function litellmServesModel(url, modelGroup) {
|
|
5957
|
+
try {
|
|
5958
|
+
const response = await fetch(`${url.replace(/\/+$/, "")}/v1/models`, { signal: AbortSignal.timeout(3e3) });
|
|
5959
|
+
if (!response.ok) return false;
|
|
5960
|
+
const payload = await response.json();
|
|
5961
|
+
return (payload.data ?? []).some((entry) => entry.id === modelGroup);
|
|
5962
|
+
} catch {
|
|
5963
|
+
return false;
|
|
5964
|
+
}
|
|
5965
|
+
}
|
|
5934
5966
|
function litellmImportable() {
|
|
5935
5967
|
try {
|
|
5936
5968
|
execFileSync5("python3", ["-c", "import litellm"], { timeout: 1e4, stdio: "pipe" });
|
|
@@ -5982,12 +6014,14 @@ function resolveLitellmCommand() {
|
|
|
5982
6014
|
async function startLitellmIfNeeded(model, projectDir, url = LITELLM_DEFAULT_URL) {
|
|
5983
6015
|
if (!needsLitellmProxy(model)) return null;
|
|
5984
6016
|
const cmd = resolveLitellmCommand();
|
|
5985
|
-
|
|
5986
|
-
const
|
|
6017
|
+
let activeUrl = url.replace(/\/+$/, "");
|
|
6018
|
+
const modelGroup = litellmModelGroup(model);
|
|
5987
6019
|
const childEnv = stripBlockingEnv({ ...process.env, ...readDotenvApiKeys(projectDir) });
|
|
5988
6020
|
let child;
|
|
5989
6021
|
let logPath;
|
|
5990
6022
|
const spawnProxy = () => {
|
|
6023
|
+
const portMatch = activeUrl.match(/:(\d+)/);
|
|
6024
|
+
const port = portMatch ? portMatch[1] : "4000";
|
|
5991
6025
|
const configPath = path22.join(os5.tmpdir(), `kody-local-litellm-${Date.now()}.yaml`);
|
|
5992
6026
|
fs23.writeFileSync(configPath, generateLitellmConfigYaml(model));
|
|
5993
6027
|
const args = ["--config", configPath, "--port", port];
|
|
@@ -6001,7 +6035,7 @@ async function startLitellmIfNeeded(model, projectDir, url = LITELLM_DEFAULT_URL
|
|
|
6001
6035
|
const deadline = Date.now() + resolveLitellmTimeoutMs();
|
|
6002
6036
|
while (Date.now() < deadline) {
|
|
6003
6037
|
await new Promise((r) => setTimeout(r, LITELLM_HEALTH_POLL_INTERVAL_MS));
|
|
6004
|
-
if (await checkLitellmHealth(
|
|
6038
|
+
if (await checkLitellmHealth(activeUrl)) return true;
|
|
6005
6039
|
}
|
|
6006
6040
|
return false;
|
|
6007
6041
|
};
|
|
@@ -6032,7 +6066,7 @@ async function startLitellmIfNeeded(model, projectDir, url = LITELLM_DEFAULT_URL
|
|
|
6032
6066
|
}, 2e3).unref?.();
|
|
6033
6067
|
};
|
|
6034
6068
|
const ensureHealthy = async () => {
|
|
6035
|
-
if (await checkLitellmHealth(
|
|
6069
|
+
if (await checkLitellmHealth(activeUrl)) return true;
|
|
6036
6070
|
const tail = readLogTail();
|
|
6037
6071
|
process.stderr.write(
|
|
6038
6072
|
`[kody litellm] proxy unreachable mid-run; restarting.${tail ? ` Last log:
|
|
@@ -6043,9 +6077,17 @@ ${tail}
|
|
|
6043
6077
|
spawnProxy();
|
|
6044
6078
|
return waitForHealth();
|
|
6045
6079
|
};
|
|
6046
|
-
const isHealthy = () => checkLitellmHealth(
|
|
6047
|
-
if (await checkLitellmHealth(
|
|
6048
|
-
|
|
6080
|
+
const isHealthy = () => checkLitellmHealth(activeUrl);
|
|
6081
|
+
if (await checkLitellmHealth(activeUrl)) {
|
|
6082
|
+
if (await litellmServesModel(activeUrl, modelGroup)) {
|
|
6083
|
+
return { url: activeUrl, kill: killChild, isHealthy, ensureHealthy };
|
|
6084
|
+
}
|
|
6085
|
+
const staleUrl = activeUrl;
|
|
6086
|
+
activeUrl = await nextAvailableLitellmUrl(activeUrl);
|
|
6087
|
+
process.stderr.write(
|
|
6088
|
+
`[kody litellm] existing proxy at ${staleUrl} does not serve ${modelGroup}; starting a separate proxy at ${activeUrl}
|
|
6089
|
+
`
|
|
6090
|
+
);
|
|
6049
6091
|
}
|
|
6050
6092
|
spawnProxy();
|
|
6051
6093
|
if (!await waitForHealth()) {
|
|
@@ -6057,7 +6099,30 @@ ${tail}
|
|
|
6057
6099
|
${tail}`
|
|
6058
6100
|
);
|
|
6059
6101
|
}
|
|
6060
|
-
return { url, kill: killChild, isHealthy, ensureHealthy };
|
|
6102
|
+
return { url: activeUrl, kill: killChild, isHealthy, ensureHealthy };
|
|
6103
|
+
}
|
|
6104
|
+
async function nextAvailableLitellmUrl(url) {
|
|
6105
|
+
const parsed = new URL(url);
|
|
6106
|
+
const startPort = Number(parsed.port || "4000");
|
|
6107
|
+
const host = parsed.hostname === "localhost" ? "127.0.0.1" : parsed.hostname;
|
|
6108
|
+
for (let offset = 1; offset <= 50; offset++) {
|
|
6109
|
+
const port = startPort + offset;
|
|
6110
|
+
if (await canListen(port, host)) {
|
|
6111
|
+
parsed.port = String(port);
|
|
6112
|
+
return parsed.origin;
|
|
6113
|
+
}
|
|
6114
|
+
}
|
|
6115
|
+
throw new Error(`no free LiteLLM port found after ${startPort}`);
|
|
6116
|
+
}
|
|
6117
|
+
function canListen(port, host) {
|
|
6118
|
+
return new Promise((resolve10) => {
|
|
6119
|
+
const server = net.createServer();
|
|
6120
|
+
server.once("error", () => resolve10(false));
|
|
6121
|
+
server.once("listening", () => {
|
|
6122
|
+
server.close(() => resolve10(true));
|
|
6123
|
+
});
|
|
6124
|
+
server.listen(port, host);
|
|
6125
|
+
});
|
|
6061
6126
|
}
|
|
6062
6127
|
function readDotenvApiKeys(projectDir) {
|
|
6063
6128
|
const dotenvPath = path22.join(projectDir, ".env");
|
|
@@ -7638,8 +7703,7 @@ var init_typeDefinitions = __esm({
|
|
|
7638
7703
|
stage: "publish",
|
|
7639
7704
|
evidence: "productionDeployed",
|
|
7640
7705
|
capability: "vercel-production-deploy",
|
|
7641
|
-
executable: "vercel-production-deploy"
|
|
7642
|
-
args: { goal: { fact: "goalId" } }
|
|
7706
|
+
executable: "vercel-production-deploy"
|
|
7643
7707
|
}
|
|
7644
7708
|
]
|
|
7645
7709
|
},
|
|
@@ -8655,7 +8719,8 @@ var init_advanceManagedGoal = __esm({
|
|
|
8655
8719
|
capability: decision.capability,
|
|
8656
8720
|
cliArgs: decision.cliArgs,
|
|
8657
8721
|
...decision.executable ? { executable: decision.executable } : {},
|
|
8658
|
-
...decision.saveReport === true ? { saveReport: true } : {}
|
|
8722
|
+
...decision.saveReport === true ? { saveReport: true } : {},
|
|
8723
|
+
resultTarget: { type: "goal", id: goal.id, evidence: decision.evidence }
|
|
8659
8724
|
};
|
|
8660
8725
|
ctx.output.reason = `dispatch ${decision.capability} for ${decision.evidence}`;
|
|
8661
8726
|
};
|
|
@@ -9632,6 +9697,18 @@ function collectResults(raw, agentResult) {
|
|
|
9632
9697
|
if (agentResult?.finalText) out.push(...parseCapabilityResultsFromText(agentResult.finalText));
|
|
9633
9698
|
return out;
|
|
9634
9699
|
}
|
|
9700
|
+
function parseResultTarget(raw) {
|
|
9701
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return null;
|
|
9702
|
+
const target = raw;
|
|
9703
|
+
if (target.type !== "goal") return null;
|
|
9704
|
+
if (typeof target.id !== "string" || target.id.trim().length === 0) return null;
|
|
9705
|
+
const evidence = typeof target.evidence === "string" && target.evidence.trim().length > 0 ? target.evidence.trim() : void 0;
|
|
9706
|
+
return {
|
|
9707
|
+
type: "goal",
|
|
9708
|
+
id: target.id.trim(),
|
|
9709
|
+
...evidence ? { evidence } : {}
|
|
9710
|
+
};
|
|
9711
|
+
}
|
|
9635
9712
|
function collectGoalCapabilityEvidence(reports, results, fallbackGoalId, explicitEvidence) {
|
|
9636
9713
|
const items = [];
|
|
9637
9714
|
for (const report of reports) {
|
|
@@ -9725,8 +9802,9 @@ var init_applyCapabilityReports = __esm({
|
|
|
9725
9802
|
applyCapabilityReports = async (ctx, _profile, agentResult) => {
|
|
9726
9803
|
const reports = collectReports(ctx.data.capabilityReports, agentResult);
|
|
9727
9804
|
const results = collectResults(ctx.data.capabilityResults ?? ctx.data.dutyResults, agentResult);
|
|
9728
|
-
const
|
|
9729
|
-
const
|
|
9805
|
+
const resultTarget = parseResultTarget(ctx.data.capabilityResultTarget);
|
|
9806
|
+
const resultGoalId = resultTarget?.id ?? (typeof ctx.args.goal === "string" && ctx.args.goal.length > 0 ? ctx.args.goal : null);
|
|
9807
|
+
const explicitEvidence = resultTarget?.evidence ?? (typeof ctx.args.evidence === "string" && ctx.args.evidence.length > 0 ? ctx.args.evidence : void 0);
|
|
9730
9808
|
const evidenceItems = collectGoalCapabilityEvidence(reports, results, resultGoalId, explicitEvidence);
|
|
9731
9809
|
if (evidenceItems.length === 0) return;
|
|
9732
9810
|
const evidenceByGoal = groupGoalEvidence(evidenceItems);
|
|
@@ -12566,7 +12644,7 @@ function makeConfig(pm, ownerRepo, defaultBranch) {
|
|
|
12566
12644
|
repo: ownerRepo?.repo ?? "REPO"
|
|
12567
12645
|
},
|
|
12568
12646
|
agent: {
|
|
12569
|
-
model: "minimax/MiniMax-
|
|
12647
|
+
model: "minimax/MiniMax-M3"
|
|
12570
12648
|
}
|
|
12571
12649
|
};
|
|
12572
12650
|
}
|
|
@@ -18337,7 +18415,8 @@ function handoffToJob(handoff) {
|
|
|
18337
18415
|
executable: handoff.executable,
|
|
18338
18416
|
cliArgs: handoff.cliArgs,
|
|
18339
18417
|
flavor: "instant",
|
|
18340
|
-
saveReport: handoff.saveReport === true
|
|
18418
|
+
saveReport: handoff.saveReport === true,
|
|
18419
|
+
resultTarget: handoff.resultTarget
|
|
18341
18420
|
};
|
|
18342
18421
|
}
|
|
18343
18422
|
function clearStampedLifecycleLabels(profile, ctx) {
|
|
@@ -18752,7 +18831,20 @@ function validateJob(input) {
|
|
|
18752
18831
|
cliArgs: j.cliArgs ?? {},
|
|
18753
18832
|
flavor: j.flavor,
|
|
18754
18833
|
force: j.force === true,
|
|
18755
|
-
saveReport: j.saveReport === true
|
|
18834
|
+
saveReport: j.saveReport === true,
|
|
18835
|
+
resultTarget: parseCapabilityResultTarget(j.resultTarget)
|
|
18836
|
+
};
|
|
18837
|
+
}
|
|
18838
|
+
function parseCapabilityResultTarget(raw) {
|
|
18839
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return void 0;
|
|
18840
|
+
const target = raw;
|
|
18841
|
+
if (target.type !== "goal") return void 0;
|
|
18842
|
+
if (typeof target.id !== "string" || target.id.trim().length === 0) return void 0;
|
|
18843
|
+
const evidence = typeof target.evidence === "string" && target.evidence.trim().length > 0 ? target.evidence.trim() : void 0;
|
|
18844
|
+
return {
|
|
18845
|
+
type: "goal",
|
|
18846
|
+
id: target.id.trim(),
|
|
18847
|
+
...evidence ? { evidence } : {}
|
|
18756
18848
|
};
|
|
18757
18849
|
}
|
|
18758
18850
|
async function runJob(job, base) {
|
|
@@ -18812,6 +18904,7 @@ async function runCapabilityImplementationStep(valid, profileName, capabilityIde
|
|
|
18812
18904
|
preloadedData.jobExecutable = profileName;
|
|
18813
18905
|
if (valid.schedule !== void 0 && valid.schedule.length > 0) preloadedData.jobSchedule = valid.schedule;
|
|
18814
18906
|
if (valid.saveReport === true) preloadedData.jobSaveReport = true;
|
|
18907
|
+
if (valid.resultTarget) preloadedData.capabilityResultTarget = valid.resultTarget;
|
|
18815
18908
|
if (capabilityContext) {
|
|
18816
18909
|
preloadedData.capabilitySlug = capabilityContext.slug;
|
|
18817
18910
|
preloadedData.capabilityTitle = capabilityContext.title;
|
|
@@ -19067,7 +19160,7 @@ var init_job = __esm({
|
|
|
19067
19160
|
init_package();
|
|
19068
19161
|
|
|
19069
19162
|
// src/servers/brain-proxy.ts
|
|
19070
|
-
import { createServer as
|
|
19163
|
+
import { createServer as createServer3 } from "http";
|
|
19071
19164
|
import { URL as URL2 } from "url";
|
|
19072
19165
|
|
|
19073
19166
|
// src/servers/brain-protocol.ts
|
|
@@ -19171,20 +19264,22 @@ function translateOpenAISseToBrain(opts) {
|
|
|
19171
19264
|
|
|
19172
19265
|
// src/servers/brain-serve.ts
|
|
19173
19266
|
import * as fs48 from "fs";
|
|
19174
|
-
import { createServer } from "http";
|
|
19267
|
+
import { createServer as createServer2 } from "http";
|
|
19175
19268
|
import * as path47 from "path";
|
|
19176
19269
|
|
|
19177
19270
|
// src/chat/loop.ts
|
|
19178
19271
|
init_agent();
|
|
19272
|
+
init_agents();
|
|
19273
|
+
init_config();
|
|
19179
19274
|
init_registry();
|
|
19180
19275
|
init_task_artifacts();
|
|
19181
|
-
import * as
|
|
19182
|
-
import * as
|
|
19276
|
+
import * as fs14 from "fs";
|
|
19277
|
+
import * as path16 from "path";
|
|
19183
19278
|
|
|
19184
19279
|
// src/chat/attachments.ts
|
|
19185
19280
|
init_runtimePaths();
|
|
19186
|
-
import * as
|
|
19187
|
-
import * as
|
|
19281
|
+
import * as fs11 from "fs";
|
|
19282
|
+
import * as path13 from "path";
|
|
19188
19283
|
var INLINE_ATTACHMENT_RE = /(?:\[(?:Image|File): ([^\]]*)\]\n)?data:([\w.+-]+\/[\w.+-]+);base64,([A-Za-z0-9+/=]+)/g;
|
|
19189
19284
|
var EXT_BY_MIME = {
|
|
19190
19285
|
"image/png": "png",
|
|
@@ -19217,11 +19312,11 @@ function prepareAttachments(turns, cwd, sessionId) {
|
|
|
19217
19312
|
if (!isImage) return `[File: ${name}]`;
|
|
19218
19313
|
try {
|
|
19219
19314
|
if (!dirEnsured) {
|
|
19220
|
-
|
|
19315
|
+
fs11.mkdirSync(dir, { recursive: true });
|
|
19221
19316
|
dirEnsured = true;
|
|
19222
19317
|
}
|
|
19223
|
-
const filePath =
|
|
19224
|
-
|
|
19318
|
+
const filePath = path13.join(dir, `${imageCounter}.${extFor(mime)}`);
|
|
19319
|
+
fs11.writeFileSync(filePath, Buffer.from(data, "base64"));
|
|
19225
19320
|
imageCounter += 1;
|
|
19226
19321
|
imagePaths.push(filePath);
|
|
19227
19322
|
return `[Image "${name}" is attached \u2014 saved to ${filePath}. Use the Read tool on that exact path to view it.]`;
|
|
@@ -19237,11 +19332,11 @@ function prepareAttachments(turns, cwd, sessionId) {
|
|
|
19237
19332
|
}
|
|
19238
19333
|
|
|
19239
19334
|
// src/chat/events.ts
|
|
19240
|
-
import * as
|
|
19241
|
-
import * as
|
|
19335
|
+
import * as fs12 from "fs";
|
|
19336
|
+
import * as path14 from "path";
|
|
19242
19337
|
import posixPath2 from "path/posix";
|
|
19243
19338
|
function eventsFilePath(cwd, sessionId) {
|
|
19244
|
-
return
|
|
19339
|
+
return path14.join(cwd, ".kody", "events", `${sessionId}.jsonl`);
|
|
19245
19340
|
}
|
|
19246
19341
|
function eventsStatePath(sessionId) {
|
|
19247
19342
|
return posixPath2.join("events", `${sessionId}.jsonl`);
|
|
@@ -19252,8 +19347,8 @@ var FileSink = class {
|
|
|
19252
19347
|
}
|
|
19253
19348
|
file;
|
|
19254
19349
|
async emit(event) {
|
|
19255
|
-
|
|
19256
|
-
|
|
19350
|
+
fs12.mkdirSync(path14.dirname(this.file), { recursive: true });
|
|
19351
|
+
fs12.appendFileSync(this.file, `${JSON.stringify(event)}
|
|
19257
19352
|
`);
|
|
19258
19353
|
}
|
|
19259
19354
|
};
|
|
@@ -19307,18 +19402,18 @@ function makeRunId(sessionId, suffix) {
|
|
|
19307
19402
|
}
|
|
19308
19403
|
|
|
19309
19404
|
// src/chat/session.ts
|
|
19310
|
-
import * as
|
|
19311
|
-
import * as
|
|
19405
|
+
import * as fs13 from "fs";
|
|
19406
|
+
import * as path15 from "path";
|
|
19312
19407
|
import posixPath3 from "path/posix";
|
|
19313
19408
|
function sessionFilePath(cwd, sessionId) {
|
|
19314
|
-
return
|
|
19409
|
+
return path15.join(cwd, ".kody", "sessions", `${sessionId}.jsonl`);
|
|
19315
19410
|
}
|
|
19316
19411
|
function sessionStatePath(sessionId) {
|
|
19317
19412
|
return posixPath3.join("sessions", `${sessionId}.jsonl`);
|
|
19318
19413
|
}
|
|
19319
19414
|
function readMeta(file) {
|
|
19320
|
-
if (!
|
|
19321
|
-
const raw =
|
|
19415
|
+
if (!fs13.existsSync(file)) return null;
|
|
19416
|
+
const raw = fs13.readFileSync(file, "utf-8");
|
|
19322
19417
|
const firstLine2 = raw.split("\n", 1)[0]?.trim();
|
|
19323
19418
|
if (!firstLine2) return null;
|
|
19324
19419
|
try {
|
|
@@ -19331,8 +19426,8 @@ function readMeta(file) {
|
|
|
19331
19426
|
}
|
|
19332
19427
|
}
|
|
19333
19428
|
function readSession(file) {
|
|
19334
|
-
if (!
|
|
19335
|
-
const raw =
|
|
19429
|
+
if (!fs13.existsSync(file)) return [];
|
|
19430
|
+
const raw = fs13.readFileSync(file, "utf-8").trim();
|
|
19336
19431
|
if (!raw) return [];
|
|
19337
19432
|
const turns = [];
|
|
19338
19433
|
for (const line of raw.split("\n")) {
|
|
@@ -19348,14 +19443,14 @@ function readSession(file) {
|
|
|
19348
19443
|
return turns;
|
|
19349
19444
|
}
|
|
19350
19445
|
function appendTurn(file, turn) {
|
|
19351
|
-
|
|
19446
|
+
fs13.mkdirSync(path15.dirname(file), { recursive: true });
|
|
19352
19447
|
const line = JSON.stringify({
|
|
19353
19448
|
role: turn.role,
|
|
19354
19449
|
content: turn.content,
|
|
19355
19450
|
timestamp: turn.timestamp,
|
|
19356
19451
|
toolCalls: turn.toolCalls ?? []
|
|
19357
19452
|
});
|
|
19358
|
-
|
|
19453
|
+
fs13.appendFileSync(file, `${line}
|
|
19359
19454
|
`);
|
|
19360
19455
|
}
|
|
19361
19456
|
function seedInitialMessage(file, message) {
|
|
@@ -19490,7 +19585,7 @@ function buildExecutableCatalog() {
|
|
|
19490
19585
|
const entries = [];
|
|
19491
19586
|
for (const { name, profilePath } of discovered) {
|
|
19492
19587
|
try {
|
|
19493
|
-
const raw = JSON.parse(
|
|
19588
|
+
const raw = JSON.parse(fs14.readFileSync(profilePath, "utf-8"));
|
|
19494
19589
|
const describe = typeof raw.describe === "string" ? raw.describe : "";
|
|
19495
19590
|
const firstSentence = describe.split(/(?<=[.!?])\s+/, 1)[0] ?? "";
|
|
19496
19591
|
entries.push({ name, describe: firstSentence.trim() });
|
|
@@ -19527,6 +19622,7 @@ async function runChatTurn(opts) {
|
|
|
19527
19622
|
}
|
|
19528
19623
|
const { turns: promptTurns, imagePaths } = prepareAttachments(turns, opts.cwd, opts.sessionId);
|
|
19529
19624
|
const basePrompt = opts.systemPrompt ?? (opts.model.protocol === "openai" ? OPENAI_CHAT_SYSTEM_PROMPT : CHAT_SYSTEM_PROMPT);
|
|
19625
|
+
const agentIdentityBlock = readAgentIdentityBlock(opts.cwd, opts.agentIdentity);
|
|
19530
19626
|
const catalog = buildExecutableCatalog();
|
|
19531
19627
|
const taskArtifactsPaths = prepareTaskArtifactsDir(opts.cwd, opts.sessionId);
|
|
19532
19628
|
const artifactAddendum = taskArtifactsPromptAddendum({
|
|
@@ -19537,7 +19633,8 @@ async function runChatTurn(opts) {
|
|
|
19537
19633
|
const contextBlock = readContextBlock(opts.cwd);
|
|
19538
19634
|
const memoryBlock = readMemoryIndexBlock(opts.cwd);
|
|
19539
19635
|
const instructionsBlock = readInstructionsBlock(opts.cwd);
|
|
19540
|
-
const
|
|
19636
|
+
const fetchRepoEnabled = Boolean(opts.enableFetchRepoTool && opts.reposRoot);
|
|
19637
|
+
const crossRepoBlock = fetchRepoEnabled ? CROSS_REPO_PROMPT : null;
|
|
19541
19638
|
const dashboardCmsEnabled = Boolean(opts.cmsDashboardUrl && opts.cmsRepoSlug && opts.cmsToken);
|
|
19542
19639
|
const dashboardCmsBlock = dashboardCmsEnabled ? DASHBOARD_CMS_PROMPT : null;
|
|
19543
19640
|
const imageBlock = imagePaths.length > 0 ? [
|
|
@@ -19550,6 +19647,7 @@ async function runChatTurn(opts) {
|
|
|
19550
19647
|
].join("\n") : null;
|
|
19551
19648
|
const systemPrompt = [
|
|
19552
19649
|
basePrompt,
|
|
19650
|
+
agentIdentityBlock,
|
|
19553
19651
|
contextBlock,
|
|
19554
19652
|
memoryBlock,
|
|
19555
19653
|
instructionsBlock,
|
|
@@ -19578,14 +19676,14 @@ async function runChatTurn(opts) {
|
|
|
19578
19676
|
quiet: opts.quiet,
|
|
19579
19677
|
additionalDirectories: [
|
|
19580
19678
|
taskArtifactsPaths.absDir,
|
|
19581
|
-
...Array.from(new Set(imagePaths.map((p2) =>
|
|
19679
|
+
...Array.from(new Set(imagePaths.map((p2) => path16.dirname(p2))))
|
|
19582
19680
|
],
|
|
19583
19681
|
systemPromptAppend: systemPrompt,
|
|
19584
19682
|
...opts.reasoningEffort ? { reasoningEffort: opts.reasoningEffort } : {},
|
|
19585
|
-
//
|
|
19586
|
-
//
|
|
19587
|
-
//
|
|
19588
|
-
...
|
|
19683
|
+
// Cross-repo work is opt-in. Repo Brain's default path remains focused
|
|
19684
|
+
// on the selected repo even though the server stores clones under
|
|
19685
|
+
// reposRoot.
|
|
19686
|
+
...fetchRepoEnabled ? {
|
|
19589
19687
|
enableFetchRepoTool: true,
|
|
19590
19688
|
reposRoot: opts.reposRoot,
|
|
19591
19689
|
repoToken: opts.repoToken
|
|
@@ -19670,6 +19768,12 @@ async function runChatTurn(opts) {
|
|
|
19670
19768
|
}
|
|
19671
19769
|
return { exitCode: 0, reply };
|
|
19672
19770
|
}
|
|
19771
|
+
function readAgentIdentityBlock(cwd, agentIdentity) {
|
|
19772
|
+
const slug2 = agentIdentity?.slug?.trim();
|
|
19773
|
+
if (!slug2) return null;
|
|
19774
|
+
const body = agentIdentity?.body?.trim() || loadAgentIdentity(cwd, slug2);
|
|
19775
|
+
return frameAgentIdentity(slug2, body);
|
|
19776
|
+
}
|
|
19673
19777
|
async function runOpenAIChatTurn(args) {
|
|
19674
19778
|
const { opts, turns, systemPrompt, sessionFile } = args;
|
|
19675
19779
|
const doFetch = opts.fetchImpl ?? fetch;
|
|
@@ -19679,7 +19783,7 @@ async function runOpenAIChatTurn(args) {
|
|
|
19679
19783
|
method: "POST",
|
|
19680
19784
|
headers: { "Content-Type": "application/json" },
|
|
19681
19785
|
body: JSON.stringify({
|
|
19682
|
-
model: opts.model
|
|
19786
|
+
model: litellmModelGroup(opts.model),
|
|
19683
19787
|
messages: [
|
|
19684
19788
|
{ role: "system", content: systemPrompt },
|
|
19685
19789
|
...turns.map((turn) => ({
|
|
@@ -19755,10 +19859,10 @@ async function emit(sink, type, sessionId, suffix, payload) {
|
|
|
19755
19859
|
var MEMORY_INDEX_REL = ".kody/memory/INDEX.md";
|
|
19756
19860
|
var MAX_INDEX_BYTES = 8e3;
|
|
19757
19861
|
function readMemoryIndexBlock(cwd) {
|
|
19758
|
-
const indexPath =
|
|
19862
|
+
const indexPath = path16.join(cwd, MEMORY_INDEX_REL);
|
|
19759
19863
|
let raw;
|
|
19760
19864
|
try {
|
|
19761
|
-
raw =
|
|
19865
|
+
raw = fs14.readFileSync(indexPath, "utf-8");
|
|
19762
19866
|
} catch {
|
|
19763
19867
|
return "";
|
|
19764
19868
|
}
|
|
@@ -19778,17 +19882,17 @@ _\u2026 (memory index truncated; use recall_search to read more)_` : trimmed;
|
|
|
19778
19882
|
var CONTEXT_DIR_REL = ".kody/context";
|
|
19779
19883
|
var MAX_CONTEXT_BYTES = 12e3;
|
|
19780
19884
|
function readContextBlock(cwd) {
|
|
19781
|
-
const dir =
|
|
19885
|
+
const dir = path16.join(cwd, CONTEXT_DIR_REL);
|
|
19782
19886
|
let files;
|
|
19783
19887
|
try {
|
|
19784
|
-
files =
|
|
19888
|
+
files = fs14.readdirSync(dir).filter((f) => f.endsWith(".md")).sort();
|
|
19785
19889
|
} catch {
|
|
19786
19890
|
return "";
|
|
19787
19891
|
}
|
|
19788
19892
|
const sections = [];
|
|
19789
19893
|
for (const file of files) {
|
|
19790
19894
|
try {
|
|
19791
|
-
const content =
|
|
19895
|
+
const content = fs14.readFileSync(path16.join(dir, file), "utf-8").trim();
|
|
19792
19896
|
if (content) sections.push(`### ${file.replace(/\.md$/, "")}
|
|
19793
19897
|
|
|
19794
19898
|
${content}`);
|
|
@@ -19811,10 +19915,10 @@ _\u2026 (context truncated; use the state repo context files for the full text)_
|
|
|
19811
19915
|
var INSTRUCTIONS_REL = ".kody/instructions.md";
|
|
19812
19916
|
var MAX_INSTRUCTIONS_BYTES = 8e3;
|
|
19813
19917
|
function readInstructionsBlock(cwd) {
|
|
19814
|
-
const instructionsPath =
|
|
19918
|
+
const instructionsPath = path16.join(cwd, INSTRUCTIONS_REL);
|
|
19815
19919
|
let raw;
|
|
19816
19920
|
try {
|
|
19817
|
-
raw =
|
|
19921
|
+
raw = fs14.readFileSync(instructionsPath, "utf-8");
|
|
19818
19922
|
} catch {
|
|
19819
19923
|
return "";
|
|
19820
19924
|
}
|
|
@@ -19915,7 +20019,7 @@ init_config();
|
|
|
19915
20019
|
|
|
19916
20020
|
// src/dispatch.ts
|
|
19917
20021
|
init_config();
|
|
19918
|
-
import * as
|
|
20022
|
+
import * as fs15 from "fs";
|
|
19919
20023
|
|
|
19920
20024
|
// src/cron-match.ts
|
|
19921
20025
|
var FIELD_BOUNDS = [
|
|
@@ -20017,10 +20121,10 @@ function autoDispatch(opts) {
|
|
|
20017
20121
|
}
|
|
20018
20122
|
const eventName = process.env.GITHUB_EVENT_NAME;
|
|
20019
20123
|
const eventPath = process.env.GITHUB_EVENT_PATH;
|
|
20020
|
-
if (!eventName || !eventPath || !
|
|
20124
|
+
if (!eventName || !eventPath || !fs15.existsSync(eventPath)) return null;
|
|
20021
20125
|
let event = {};
|
|
20022
20126
|
try {
|
|
20023
|
-
event = JSON.parse(
|
|
20127
|
+
event = JSON.parse(fs15.readFileSync(eventPath, "utf-8"));
|
|
20024
20128
|
} catch {
|
|
20025
20129
|
return null;
|
|
20026
20130
|
}
|
|
@@ -20131,7 +20235,7 @@ function autoDispatchTyped(opts) {
|
|
|
20131
20235
|
if (legacy) return { kind: "route", ...legacy };
|
|
20132
20236
|
const eventName = process.env.GITHUB_EVENT_NAME;
|
|
20133
20237
|
const eventPath = process.env.GITHUB_EVENT_PATH;
|
|
20134
|
-
if (!eventName || !eventPath || !
|
|
20238
|
+
if (!eventName || !eventPath || !fs15.existsSync(eventPath)) {
|
|
20135
20239
|
return { kind: "silent", reason: "no GHA event context" };
|
|
20136
20240
|
}
|
|
20137
20241
|
if (eventName !== "issue_comment") {
|
|
@@ -20139,7 +20243,7 @@ function autoDispatchTyped(opts) {
|
|
|
20139
20243
|
}
|
|
20140
20244
|
let event = {};
|
|
20141
20245
|
try {
|
|
20142
|
-
event = JSON.parse(
|
|
20246
|
+
event = JSON.parse(fs15.readFileSync(eventPath, "utf-8"));
|
|
20143
20247
|
} catch {
|
|
20144
20248
|
return { kind: "silent", reason: "GHA event payload unreadable" };
|
|
20145
20249
|
}
|
|
@@ -20183,7 +20287,7 @@ function dispatchScheduledWatches(opts) {
|
|
|
20183
20287
|
for (const exe of listExecutables()) {
|
|
20184
20288
|
let raw;
|
|
20185
20289
|
try {
|
|
20186
|
-
raw =
|
|
20290
|
+
raw = fs15.readFileSync(exe.profilePath, "utf-8");
|
|
20187
20291
|
} catch {
|
|
20188
20292
|
continue;
|
|
20189
20293
|
}
|
|
@@ -21266,6 +21370,19 @@ function strField(body, key) {
|
|
|
21266
21370
|
}
|
|
21267
21371
|
return void 0;
|
|
21268
21372
|
}
|
|
21373
|
+
function boolField(body, key) {
|
|
21374
|
+
return typeof body === "object" && body !== null && body[key] === true;
|
|
21375
|
+
}
|
|
21376
|
+
function agentIdentityField(body) {
|
|
21377
|
+
if (typeof body !== "object" || body === null || !("agentIdentity" in body)) return void 0;
|
|
21378
|
+
const value = body.agentIdentity;
|
|
21379
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) return void 0;
|
|
21380
|
+
const record2 = value;
|
|
21381
|
+
const slug2 = typeof record2.slug === "string" ? record2.slug.trim() : "";
|
|
21382
|
+
const bodyText = typeof record2.body === "string" ? record2.body.trim() : "";
|
|
21383
|
+
if (!slug2 || !bodyText) return void 0;
|
|
21384
|
+
return { slug: slug2, body: bodyText };
|
|
21385
|
+
}
|
|
21269
21386
|
function sendJson(res, status, body) {
|
|
21270
21387
|
res.writeHead(status, { "content-type": "application/json" });
|
|
21271
21388
|
res.end(JSON.stringify(body));
|
|
@@ -21400,6 +21517,8 @@ async function handleChatTurn(req, res, chatId, opts) {
|
|
|
21400
21517
|
const dashboardUrl = strField(body, "dashboardUrl");
|
|
21401
21518
|
const storeRepoUrl = strField(body, "storeRepoUrl");
|
|
21402
21519
|
const storeRef = strField(body, "storeRef");
|
|
21520
|
+
const allowCrossRepo = boolField(body, "allowCrossRepo");
|
|
21521
|
+
const agentIdentity = agentIdentityField(body);
|
|
21403
21522
|
let agentCwd;
|
|
21404
21523
|
try {
|
|
21405
21524
|
agentCwd = await ensureRepoCwd({
|
|
@@ -21474,9 +21593,12 @@ async function handleChatTurn(req, res, chatId, opts) {
|
|
|
21474
21593
|
model: opts.model,
|
|
21475
21594
|
litellmUrl: opts.litellmUrl,
|
|
21476
21595
|
sink,
|
|
21477
|
-
// Let the agent clone + work on OTHER repos via the fetch_repo tool.
|
|
21478
21596
|
reposRoot: opts.reposRoot,
|
|
21597
|
+
// Repo Brain is selected-repo focused by default. Higher-level
|
|
21598
|
+
// coordinator flows can opt into fetch_repo explicitly.
|
|
21599
|
+
...allowCrossRepo ? { enableFetchRepoTool: true } : {},
|
|
21479
21600
|
repoToken,
|
|
21601
|
+
...agentIdentity ? { agentIdentity } : {},
|
|
21480
21602
|
...dashboardUrl && repo && stateToken ? {
|
|
21481
21603
|
cmsDashboardUrl: dashboardUrl,
|
|
21482
21604
|
cmsRepoSlug: repo,
|
|
@@ -21529,7 +21651,7 @@ function buildServer(opts) {
|
|
|
21529
21651
|
const runTurn = opts.runTurn ?? runChatTurn;
|
|
21530
21652
|
const cloneRepo = opts.cloneRepo ?? defaultCloneRepo;
|
|
21531
21653
|
const reposRoot = opts.reposRoot ?? path47.join(path47.dirname(path47.resolve(opts.cwd)), "repos");
|
|
21532
|
-
return
|
|
21654
|
+
return createServer2(async (req, res) => {
|
|
21533
21655
|
if (!req.method || !req.url) {
|
|
21534
21656
|
sendJson(res, 400, { error: "bad request" });
|
|
21535
21657
|
return;
|
|
@@ -21727,7 +21849,7 @@ function buildBrainProxy(opts) {
|
|
|
21727
21849
|
res.writeHead(404, { "Content-Type": "application/json" });
|
|
21728
21850
|
res.end(JSON.stringify({ error: "not found" }));
|
|
21729
21851
|
};
|
|
21730
|
-
const httpServer =
|
|
21852
|
+
const httpServer = createServer3((req, res) => {
|
|
21731
21853
|
void handler(req, res);
|
|
21732
21854
|
});
|
|
21733
21855
|
return { httpServer, handler };
|
|
@@ -21746,7 +21868,9 @@ async function proxyToBrainServe(args) {
|
|
|
21746
21868
|
...args.body.repoToken ? { repoToken: args.body.repoToken } : {},
|
|
21747
21869
|
...args.body.dashboardUrl ? { dashboardUrl: args.body.dashboardUrl } : {},
|
|
21748
21870
|
...args.body.storeRepoUrl ? { storeRepoUrl: args.body.storeRepoUrl } : {},
|
|
21749
|
-
...args.body.storeRef ? { storeRef: args.body.storeRef } : {}
|
|
21871
|
+
...args.body.storeRef ? { storeRef: args.body.storeRef } : {},
|
|
21872
|
+
...args.body.allowCrossRepo === true ? { allowCrossRepo: true } : {},
|
|
21873
|
+
...args.body.agentIdentity ? { agentIdentity: args.body.agentIdentity } : {}
|
|
21750
21874
|
})
|
|
21751
21875
|
});
|
|
21752
21876
|
if (!upstream.ok || !upstream.body) {
|
|
@@ -21948,7 +22072,7 @@ init_fetchRepoMcp();
|
|
|
21948
22072
|
|
|
21949
22073
|
// src/servers/mcpHttpServer.ts
|
|
21950
22074
|
import { randomUUID } from "crypto";
|
|
21951
|
-
import { createServer as
|
|
22075
|
+
import { createServer as createServer4 } from "http";
|
|
21952
22076
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
21953
22077
|
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
21954
22078
|
function buildMcpHttpServer(opts) {
|
|
@@ -22007,7 +22131,7 @@ function buildMcpHttpServer(opts) {
|
|
|
22007
22131
|
res.end(JSON.stringify({ error: "internal error" }));
|
|
22008
22132
|
}
|
|
22009
22133
|
};
|
|
22010
|
-
const httpServer =
|
|
22134
|
+
const httpServer = createServer4((req, res) => {
|
|
22011
22135
|
if (req.url === "/healthz" || req.url === "/health") {
|
|
22012
22136
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
22013
22137
|
res.end(JSON.stringify({ ok: true, routes: Array.from(routes.keys()) }));
|
|
@@ -22600,7 +22724,7 @@ init_job();
|
|
|
22600
22724
|
init_registry();
|
|
22601
22725
|
|
|
22602
22726
|
// src/servers/pool-serve.ts
|
|
22603
|
-
import { createServer as
|
|
22727
|
+
import { createServer as createServer5 } from "http";
|
|
22604
22728
|
|
|
22605
22729
|
// src/github-health.ts
|
|
22606
22730
|
var STATUS_URL = "https://www.githubstatus.com/api/v2/components.json";
|
|
@@ -23430,7 +23554,7 @@ async function poolServe() {
|
|
|
23430
23554
|
(err) => log(`capability fallback tick failed: ${err instanceof Error ? err.message : String(err)}`)
|
|
23431
23555
|
);
|
|
23432
23556
|
}, capabilityTickMs) : null;
|
|
23433
|
-
const server =
|
|
23557
|
+
const server = createServer5(async (req, res) => {
|
|
23434
23558
|
try {
|
|
23435
23559
|
if (!req.method || !req.url) return sendJson2(res, 400, { error: "bad request" });
|
|
23436
23560
|
const url = new URL(req.url, "http://localhost");
|
|
@@ -23499,7 +23623,7 @@ async function poolServe() {
|
|
|
23499
23623
|
// src/servers/runner-serve.ts
|
|
23500
23624
|
import { spawn as spawn8 } from "child_process";
|
|
23501
23625
|
import * as fs51 from "fs";
|
|
23502
|
-
import { createServer as
|
|
23626
|
+
import { createServer as createServer6 } from "http";
|
|
23503
23627
|
var DEFAULT_PORT2 = 8080;
|
|
23504
23628
|
var DEFAULT_WORKDIR = "/workspace/repo";
|
|
23505
23629
|
function getApiKey3() {
|
|
@@ -23696,7 +23820,7 @@ async function defaultRunJob(job) {
|
|
|
23696
23820
|
function buildServer2(opts) {
|
|
23697
23821
|
const runJob2 = opts.runJob ?? defaultRunJob;
|
|
23698
23822
|
let busy = false;
|
|
23699
|
-
return
|
|
23823
|
+
return createServer6(async (req, res) => {
|
|
23700
23824
|
if (!req.method || !req.url) {
|
|
23701
23825
|
sendJson3(res, 400, { error: "bad request" });
|
|
23702
23826
|
return;
|
|
@@ -394,6 +394,12 @@ export interface OutputContract {
|
|
|
394
394
|
}
|
|
395
395
|
}
|
|
396
396
|
|
|
397
|
+
export interface CapabilityResultTarget {
|
|
398
|
+
type: "goal"
|
|
399
|
+
id: string
|
|
400
|
+
evidence?: string
|
|
401
|
+
}
|
|
402
|
+
|
|
397
403
|
// ────────────────────────────────────────────────────────────────────────────
|
|
398
404
|
// Run-time context passed to every script.
|
|
399
405
|
// ────────────────────────────────────────────────────────────────────────────
|
|
@@ -429,6 +435,7 @@ export interface Context {
|
|
|
429
435
|
executable?: string
|
|
430
436
|
cliArgs: Record<string, unknown>
|
|
431
437
|
saveReport?: boolean
|
|
438
|
+
resultTarget?: CapabilityResultTarget
|
|
432
439
|
}
|
|
433
440
|
/** In-process hand-off to a full Job, preserving job identity in task state. */
|
|
434
441
|
nextJob?: Job
|
|
@@ -440,6 +447,7 @@ export interface Context {
|
|
|
440
447
|
executable?: string
|
|
441
448
|
cliArgs: Record<string, unknown>
|
|
442
449
|
saveReport?: boolean
|
|
450
|
+
resultTarget?: CapabilityResultTarget
|
|
443
451
|
}
|
|
444
452
|
}
|
|
445
453
|
/**
|
|
@@ -512,4 +520,6 @@ export interface Job {
|
|
|
512
520
|
force?: boolean
|
|
513
521
|
/** Ask the owning goal/loop to write a report run after its persisted decision. */
|
|
514
522
|
saveReport?: boolean
|
|
523
|
+
/** Internal parent context used by postflights to attach neutral capability output. */
|
|
524
|
+
resultTarget?: CapabilityResultTarget
|
|
515
525
|
}
|
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.293",
|
|
4
4
|
"description": "kody — autonomous development engine. Single-session Claude Code agent behind a generic executor + declarative executable profiles.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|