@kontextmind/kxm 0.7.180 → 0.7.182
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/.claude-plugin/marketplace.json +1 -1
- package/CHANGELOG.md +23 -0
- package/docs/architecture/authentication.md +11 -2
- package/docs/architecture/hosted-direction.md +7 -5
- package/docs/architecture/inventory.md +5 -3
- package/docs/architecture/platform.md +18 -4
- package/docs/contributing/ci-and-release.md +18 -9
- package/docs/contributing/decisions.md +31 -0
- package/docs/contributing/development.md +39 -1
- package/docs/contributing/test-matrix.md +2 -1
- package/docs/reference/cli-reference.md +9 -9
- package/docs/reference/configuration.md +22 -0
- package/package.json +1 -1
- package/plugins/kxm/.claude-plugin/plugin.json +1 -1
- package/plugins/kxm/dist/cli.js +148 -42
- package/plugins/kxm/dist/mcp-server.js +1 -1
- package/plugins/kxm/package.json +1 -1
- package/plugins/kxm/src/improve-sources.ts +99 -13
- package/plugins/kxm/src/mcp-server.ts +1 -1
- package/plugins/kxm/src/studio-layout.ts +50 -0
- package/scripts/ci-postgres-17.sh +144 -0
package/plugins/kxm/dist/cli.js
CHANGED
|
@@ -20760,8 +20760,8 @@ function validateWorkflow(workflow, agents, models, repositories, gates, issues)
|
|
|
20760
20760
|
const writable = Object.values(objectValue(step.repositories) ?? {}).filter((access) => access === "write").length;
|
|
20761
20761
|
if (maxWriteRepositories > writable) issues.push(issue3("semantic", "write_repository_bound_invalid", file, `${stepId} maxWriteRepositories exceeds writable repository scope`));
|
|
20762
20762
|
}
|
|
20763
|
-
const
|
|
20764
|
-
const minimumPassed =
|
|
20763
|
+
const join61 = objectValue(step.join);
|
|
20764
|
+
const minimumPassed = join61 && typeof join61.minimumPassed === "number" ? join61.minimumPassed : void 0;
|
|
20765
20765
|
if (minimumPassed !== void 0 && minimumPassed > maximum) issues.push(issue3("semantic", "join_impossible", file, `${stepId} minimumPassed exceeds assignment maximum`));
|
|
20766
20766
|
const distinctBy = names(assignment?.distinctBy);
|
|
20767
20767
|
if (distinctBy.length > 0) {
|
|
@@ -25101,7 +25101,7 @@ function compileStep(step, index, stepIndex, requirePlanHash, sink) {
|
|
|
25101
25101
|
const maxAttempts = compileCountField(step.maxAttempts, 1, `${id}.maxAttempts`, id, sink);
|
|
25102
25102
|
const timeoutMs = compileOptionalDuration(step.timeoutMs, `${id}.timeoutMs`, id, sink);
|
|
25103
25103
|
const assignments = compileAssignments(step, id, agent, sink);
|
|
25104
|
-
const
|
|
25104
|
+
const join61 = compileJoin(step, id, sink);
|
|
25105
25105
|
const requiredEvidence = compileEvidence(step, id, sink);
|
|
25106
25106
|
const transitions = compileTransitions(step, id, index, stepIndex, sink);
|
|
25107
25107
|
const outcomes = Object.keys(transitions).sort(compareCodeUnits5);
|
|
@@ -25129,7 +25129,7 @@ function compileStep(step, index, stepIndex, requirePlanHash, sink) {
|
|
|
25129
25129
|
transitions: orderedTransitions,
|
|
25130
25130
|
requiresPlanHash: requirePlanHash.includes(id),
|
|
25131
25131
|
assignments,
|
|
25132
|
-
join:
|
|
25132
|
+
join: join61
|
|
25133
25133
|
};
|
|
25134
25134
|
if (kind === "agent" || kind === "moa") {
|
|
25135
25135
|
if (!agent) return void 0;
|
|
@@ -25179,15 +25179,15 @@ function compileAssignments(step, stepId, primaryAgentId, sink) {
|
|
|
25179
25179
|
};
|
|
25180
25180
|
}
|
|
25181
25181
|
function compileJoin(step, stepId, sink) {
|
|
25182
|
-
const
|
|
25183
|
-
if (!
|
|
25184
|
-
const declared = stringValue2(
|
|
25182
|
+
const join61 = objectValue2(step.join);
|
|
25183
|
+
if (!join61) return { strategy: "all" };
|
|
25184
|
+
const declared = stringValue2(join61.strategy);
|
|
25185
25185
|
const strategy = declared && JOIN_STRATEGIES.has(declared) ? declared : "all";
|
|
25186
|
-
const minimumPassed = compileOptionalCount(
|
|
25186
|
+
const minimumPassed = compileOptionalCount(join61.minimumPassed, `${stepId}.join.minimumPassed`, stepId, sink);
|
|
25187
25187
|
const compiled = {
|
|
25188
25188
|
strategy,
|
|
25189
25189
|
...minimumPassed !== void 0 ? { minimumPassed } : {},
|
|
25190
|
-
...typeof
|
|
25190
|
+
...typeof join61.cancelRemaining === "boolean" ? { cancelRemaining: join61.cancelRemaining } : {}
|
|
25191
25191
|
};
|
|
25192
25192
|
return compiled;
|
|
25193
25193
|
}
|
|
@@ -38995,6 +38995,11 @@ function generateStudioLayout(plan, state) {
|
|
|
38995
38995
|
};
|
|
38996
38996
|
}
|
|
38997
38997
|
var DEFAULT_STUDIO_PORT = 4242;
|
|
38998
|
+
function isKxmdAccountView(value) {
|
|
38999
|
+
if (typeof value !== "object" || value === null) return false;
|
|
39000
|
+
const account = value;
|
|
39001
|
+
return account.account === "kxmd" && account.database === "kxmd" && account.namespace === "kxmd" && account.role === "kxmd";
|
|
39002
|
+
}
|
|
38998
39003
|
function createStudioServer(options = {}) {
|
|
38999
39004
|
const targetPort = options.port ?? DEFAULT_STUDIO_PORT;
|
|
39000
39005
|
const host = options.host ?? "127.0.0.1";
|
|
@@ -39013,6 +39018,46 @@ function createStudioServer(options = {}) {
|
|
|
39013
39018
|
res.end(JSON.stringify({ ok: true, studio: true, port: targetPort }));
|
|
39014
39019
|
return;
|
|
39015
39020
|
}
|
|
39021
|
+
if (url.pathname === "/api/control/account" && req.method === "GET") {
|
|
39022
|
+
const base = options.controlUrl ?? process.env.KXM_CONTROL_URL;
|
|
39023
|
+
if (!base) {
|
|
39024
|
+
res.writeHead(503, { "Content-Type": "application/json" });
|
|
39025
|
+
res.end(JSON.stringify({ ok: false, error: "control_unconfigured" }));
|
|
39026
|
+
return;
|
|
39027
|
+
}
|
|
39028
|
+
let upstream;
|
|
39029
|
+
try {
|
|
39030
|
+
upstream = await fetch(new URL("/v1/account", base), {
|
|
39031
|
+
signal: AbortSignal.timeout(3e3)
|
|
39032
|
+
});
|
|
39033
|
+
} catch {
|
|
39034
|
+
res.writeHead(502, { "Content-Type": "application/json" });
|
|
39035
|
+
res.end(JSON.stringify({ ok: false, error: "control_unreachable" }));
|
|
39036
|
+
return;
|
|
39037
|
+
}
|
|
39038
|
+
const contentType = upstream.headers.get("content-type");
|
|
39039
|
+
if (!contentType?.toLowerCase().startsWith("application/json")) {
|
|
39040
|
+
res.writeHead(502, { "Content-Type": "application/json" });
|
|
39041
|
+
res.end(JSON.stringify({ ok: false, error: "control_response_invalid" }));
|
|
39042
|
+
return;
|
|
39043
|
+
}
|
|
39044
|
+
let body;
|
|
39045
|
+
try {
|
|
39046
|
+
body = await upstream.json();
|
|
39047
|
+
} catch {
|
|
39048
|
+
res.writeHead(502, { "Content-Type": "application/json" });
|
|
39049
|
+
res.end(JSON.stringify({ ok: false, error: "control_response_invalid" }));
|
|
39050
|
+
return;
|
|
39051
|
+
}
|
|
39052
|
+
if (upstream.ok && !isKxmdAccountView(body)) {
|
|
39053
|
+
res.writeHead(502, { "Content-Type": "application/json" });
|
|
39054
|
+
res.end(JSON.stringify({ ok: false, error: "control_response_invalid" }));
|
|
39055
|
+
return;
|
|
39056
|
+
}
|
|
39057
|
+
res.writeHead(upstream.status, { "Content-Type": "application/json" });
|
|
39058
|
+
res.end(JSON.stringify(body));
|
|
39059
|
+
return;
|
|
39060
|
+
}
|
|
39016
39061
|
if (url.pathname === "/api/layout" && req.method === "GET") {
|
|
39017
39062
|
const plan = options.planProvider?.();
|
|
39018
39063
|
if (!plan) {
|
|
@@ -54567,7 +54612,7 @@ import { spawnSync as spawnSync15 } from "node:child_process";
|
|
|
54567
54612
|
import { createHash as createHash19 } from "node:crypto";
|
|
54568
54613
|
import { existsSync as existsSync50, mkdtempSync as mkdtempSync3, readFileSync as readFileSync44, rmSync as rmSync13 } from "node:fs";
|
|
54569
54614
|
import { tmpdir as tmpdir3 } from "node:os";
|
|
54570
|
-
import { basename as basename11, extname as extname3, join as
|
|
54615
|
+
import { basename as basename11, extname as extname3, join as join59, resolve as resolve33 } from "node:path";
|
|
54571
54616
|
import { createInterface as createInterface3 } from "node:readline";
|
|
54572
54617
|
|
|
54573
54618
|
// plugins/kxm/src/improve.ts
|
|
@@ -54992,13 +55037,15 @@ function evaluatePromotionPolicy(candidate, policy = "manual_pr", options = {})
|
|
|
54992
55037
|
// plugins/kxm/src/improve-sources.ts
|
|
54993
55038
|
init_sqlite();
|
|
54994
55039
|
init_project_config();
|
|
54995
|
-
|
|
55040
|
+
init_runtime_paths();
|
|
54996
55041
|
init_route_switch();
|
|
54997
55042
|
init_routing();
|
|
54998
55043
|
init_telemetry();
|
|
54999
|
-
import { existsSync as existsSync44 } from "node:fs";
|
|
55000
|
-
import { resolve as resolve30 } from "node:path";
|
|
55044
|
+
import { existsSync as existsSync44, lstatSync as lstatSync8 } from "node:fs";
|
|
55045
|
+
import { join as join54, resolve as resolve30 } from "node:path";
|
|
55001
55046
|
var ENGINE_EVENTS_SQL = "SELECT run_id, sequence, event_type, payload FROM events WHERE event_type IN ('routing.attempt.recorded','routing.route_switched','step.entered','run.status_changed') ORDER BY run_id, sequence";
|
|
55047
|
+
var REGISTRY_SIBLING_SQL = "SELECT sibling.project_key AS project_key, sibling.lane_of AS lane_of FROM projects AS current JOIN projects AS sibling ON sibling.project_id = current.project_id WHERE current.project_key = ? ORDER BY CASE WHEN sibling.lane_of IS NULL THEN 0 ELSE 1 END, sibling.registered_at, sibling.project_key";
|
|
55048
|
+
var PROJECT_KEY = /^[0-9a-f]{24}$/;
|
|
55002
55049
|
var SIMULATED_HARNESS = "driver-simulated";
|
|
55003
55050
|
function parsePayload(text) {
|
|
55004
55051
|
try {
|
|
@@ -55104,6 +55151,61 @@ function readEngineRoutingRecords(path4) {
|
|
|
55104
55151
|
source: { ...source, records: records.length, skippedInvalid, excludedSimulated, undecided }
|
|
55105
55152
|
};
|
|
55106
55153
|
}
|
|
55154
|
+
function registryUnreadable(registryDb, detail) {
|
|
55155
|
+
return new Error(`improve_source_unreadable: ${registryDb}: ${detail}`);
|
|
55156
|
+
}
|
|
55157
|
+
function discoverProjectEventStores(projectRoot, env) {
|
|
55158
|
+
const paths = kxmRuntimePaths({ env });
|
|
55159
|
+
const ownStore = join54(paths.projectsDir, projectRuntimeKey(projectRoot), "run-events.db");
|
|
55160
|
+
const registryDb = paths.registryDb;
|
|
55161
|
+
const info = lstatSync8(registryDb, { throwIfNoEntry: false });
|
|
55162
|
+
if (!info) return [ownStore];
|
|
55163
|
+
if (!info.isFile()) throw registryUnreadable(registryDb, "runtime registry must be a regular database file");
|
|
55164
|
+
for (const suffix of ["-wal", "-shm"]) {
|
|
55165
|
+
const sidecar = lstatSync8(`${registryDb}${suffix}`, { throwIfNoEntry: false });
|
|
55166
|
+
if (sidecar?.isSymbolicLink()) throw registryUnreadable(registryDb, "runtime registry sidecar must not be a link");
|
|
55167
|
+
}
|
|
55168
|
+
let database;
|
|
55169
|
+
try {
|
|
55170
|
+
database = openReadOnlyDatabase(registryDb);
|
|
55171
|
+
database.exec("PRAGMA busy_timeout = 5000");
|
|
55172
|
+
const versionRow = database.prepare("PRAGMA user_version").get();
|
|
55173
|
+
const version = versionRow?.user_version ?? 0;
|
|
55174
|
+
if (version !== 2) throw registryUnreadable(registryDb, `registry schema version ${version}; this reader requires 2`);
|
|
55175
|
+
const rows = database.prepare(REGISTRY_SIBLING_SQL).all(projectRuntimeKey(projectRoot));
|
|
55176
|
+
if (rows.length === 0) return [ownStore];
|
|
55177
|
+
return eventStoresFromRegistryRows(registryDb, paths.projectsDir, rows);
|
|
55178
|
+
} catch (error) {
|
|
55179
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
55180
|
+
if (message.startsWith("improve_source_unreadable:")) throw error;
|
|
55181
|
+
throw new Error(`improve_source_unreadable: ${registryDb}: ${message}`, { cause: error });
|
|
55182
|
+
} finally {
|
|
55183
|
+
try {
|
|
55184
|
+
database?.close();
|
|
55185
|
+
} catch {
|
|
55186
|
+
}
|
|
55187
|
+
}
|
|
55188
|
+
}
|
|
55189
|
+
function eventStoresFromRegistryRows(registryDb, projectsDir, rows) {
|
|
55190
|
+
const seen = /* @__PURE__ */ new Set();
|
|
55191
|
+
const parsed = [];
|
|
55192
|
+
for (const row of rows) {
|
|
55193
|
+
const projectKey = typeof row.project_key === "string" ? row.project_key : "";
|
|
55194
|
+
const laneRaw = row.lane_of;
|
|
55195
|
+
const laneOf = laneRaw === null || laneRaw === void 0 ? null : typeof laneRaw === "string" ? laneRaw : "";
|
|
55196
|
+
if (!PROJECT_KEY.test(projectKey) || seen.has(projectKey) || laneOf !== null && !PROJECT_KEY.test(laneOf)) {
|
|
55197
|
+
throw registryUnreadable(registryDb, "registry identity is corrupt");
|
|
55198
|
+
}
|
|
55199
|
+
seen.add(projectKey);
|
|
55200
|
+
parsed.push({ projectKey, laneOf });
|
|
55201
|
+
}
|
|
55202
|
+
const homes = parsed.filter((row) => row.laneOf === null);
|
|
55203
|
+
const homeKey = homes[0]?.projectKey;
|
|
55204
|
+
if (homes.length !== 1 || homeKey === void 0 || parsed.some((row) => row.laneOf !== null && row.laneOf !== homeKey)) {
|
|
55205
|
+
throw registryUnreadable(registryDb, "registry identity is corrupt");
|
|
55206
|
+
}
|
|
55207
|
+
return parsed.map((row) => join54(projectsDir, row.projectKey, "run-events.db"));
|
|
55208
|
+
}
|
|
55107
55209
|
function readJsonlSource(kind, path4) {
|
|
55108
55210
|
const exists = existsSync44(path4);
|
|
55109
55211
|
const records = exists ? readRoutingRecords(path4).map((entry) => entry.routing) : [];
|
|
@@ -55120,7 +55222,11 @@ function loadRoutingSources(options) {
|
|
|
55120
55222
|
if (options.file !== void 0) {
|
|
55121
55223
|
reads.push(readJsonlSource("file", resolve30(options.cwd, options.file)));
|
|
55122
55224
|
} else {
|
|
55123
|
-
if (projectRoot !== void 0)
|
|
55225
|
+
if (projectRoot !== void 0) {
|
|
55226
|
+
for (const path4 of discoverProjectEventStores(projectRoot, options.env)) {
|
|
55227
|
+
reads.push(readEngineRoutingRecords(path4));
|
|
55228
|
+
}
|
|
55229
|
+
}
|
|
55124
55230
|
reads.push(readJsonlSource("telemetry", telemetryPath(options.logsDir)));
|
|
55125
55231
|
}
|
|
55126
55232
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -55152,7 +55258,7 @@ function loadRoutingSources(options) {
|
|
|
55152
55258
|
var import_yaml21 = __toESM(require_dist(), 1);
|
|
55153
55259
|
init_prices();
|
|
55154
55260
|
import { existsSync as existsSync45, readFileSync as readFileSync40 } from "node:fs";
|
|
55155
|
-
import { join as
|
|
55261
|
+
import { join as join55 } from "node:path";
|
|
55156
55262
|
var DEFAULT_MODES_CONFIG = Object.freeze({
|
|
55157
55263
|
schema: "kxm.modes.v1",
|
|
55158
55264
|
majorModes: {
|
|
@@ -55215,7 +55321,7 @@ function loadModesConfig(projectRoot) {
|
|
|
55215
55321
|
if (!projectRoot) {
|
|
55216
55322
|
return DEFAULT_MODES_CONFIG;
|
|
55217
55323
|
}
|
|
55218
|
-
const modesPath =
|
|
55324
|
+
const modesPath = join55(projectRoot, ".kxm", "modes.yaml");
|
|
55219
55325
|
if (!existsSync45(modesPath)) {
|
|
55220
55326
|
return DEFAULT_MODES_CONFIG;
|
|
55221
55327
|
}
|
|
@@ -55306,7 +55412,7 @@ function calculatePromptFootprint(resolved, projectRoot = process.cwd(), catalog
|
|
|
55306
55412
|
estimatedTokens: estimateTokens(baseSystemPromptChars)
|
|
55307
55413
|
});
|
|
55308
55414
|
for (const relPath of resolved.contextFiles) {
|
|
55309
|
-
const fullPath =
|
|
55415
|
+
const fullPath = join55(projectRoot, relPath);
|
|
55310
55416
|
let chars = 0;
|
|
55311
55417
|
if (existsSync45(fullPath)) {
|
|
55312
55418
|
try {
|
|
@@ -55445,7 +55551,7 @@ init_safety_integrity();
|
|
|
55445
55551
|
import { spawnSync as spawnSync14 } from "node:child_process";
|
|
55446
55552
|
import { existsSync as existsSync46, mkdirSync as mkdirSync32, readFileSync as readFileSync41, readdirSync as readdirSync14, rmSync as rmSync12, statSync as statSync7 } from "node:fs";
|
|
55447
55553
|
import { homedir as homedir8 } from "node:os";
|
|
55448
|
-
import { join as
|
|
55554
|
+
import { join as join56, resolve as resolve31 } from "node:path";
|
|
55449
55555
|
var MAX_SSH_OUTPUT_BYTES = 50 * 1024;
|
|
55450
55556
|
var MAX_SSH_OUTPUT_LINES = 2e3;
|
|
55451
55557
|
var DEFAULT_SOCKET_DIR = ".kxm/run/ssh-sockets";
|
|
@@ -55471,7 +55577,7 @@ function truncateSshOutput(raw) {
|
|
|
55471
55577
|
return { text, truncated };
|
|
55472
55578
|
}
|
|
55473
55579
|
function parseSshConfig(configPath) {
|
|
55474
|
-
const targetPath = configPath ??
|
|
55580
|
+
const targetPath = configPath ?? join56(homedir8(), ".ssh", "config");
|
|
55475
55581
|
if (!existsSync46(targetPath)) {
|
|
55476
55582
|
return [];
|
|
55477
55583
|
}
|
|
@@ -55561,7 +55667,7 @@ function ensureSocketDir(socketDir = DEFAULT_SOCKET_DIR) {
|
|
|
55561
55667
|
}
|
|
55562
55668
|
function buildSshArgs(options) {
|
|
55563
55669
|
const socketDir = ensureSocketDir(options.socketDir ?? DEFAULT_SOCKET_DIR);
|
|
55564
|
-
const controlPath =
|
|
55670
|
+
const controlPath = join56(socketDir, "%C");
|
|
55565
55671
|
const persist = options.controlPersist ?? DEFAULT_CONTROL_PERSIST;
|
|
55566
55672
|
const args = [
|
|
55567
55673
|
"-o",
|
|
@@ -55586,7 +55692,7 @@ function buildSshArgs(options) {
|
|
|
55586
55692
|
}
|
|
55587
55693
|
function checkControlSocket(host, socketDir = DEFAULT_SOCKET_DIR, execFn = spawnSync14) {
|
|
55588
55694
|
const resolvedDir = ensureSocketDir(socketDir);
|
|
55589
|
-
const controlPath =
|
|
55695
|
+
const controlPath = join56(resolvedDir, "%C");
|
|
55590
55696
|
try {
|
|
55591
55697
|
const result = execFn("ssh", ["-O", "check", "-o", `ControlPath=${controlPath}`, host], {
|
|
55592
55698
|
encoding: "utf-8"
|
|
@@ -55598,7 +55704,7 @@ function checkControlSocket(host, socketDir = DEFAULT_SOCKET_DIR, execFn = spawn
|
|
|
55598
55704
|
}
|
|
55599
55705
|
function closeControlSocket(host, socketDir = DEFAULT_SOCKET_DIR, execFn = spawnSync14) {
|
|
55600
55706
|
const resolvedDir = ensureSocketDir(socketDir);
|
|
55601
|
-
const controlPath =
|
|
55707
|
+
const controlPath = join56(resolvedDir, "%C");
|
|
55602
55708
|
try {
|
|
55603
55709
|
const result = execFn("ssh", ["-O", "stop", "-o", `ControlPath=${controlPath}`, host], {
|
|
55604
55710
|
encoding: "utf-8"
|
|
@@ -56349,7 +56455,7 @@ complete -c kxm -n "__fish_seen_subcommand_from goal" -a "create list get"
|
|
|
56349
56455
|
// plugins/kxm/src/completion-install.ts
|
|
56350
56456
|
import { existsSync as existsSync48, mkdirSync as mkdirSync33, readFileSync as readFileSync43, writeFileSync as writeFileSync28 } from "node:fs";
|
|
56351
56457
|
import { homedir as homedir9 } from "node:os";
|
|
56352
|
-
import { basename as basename10, delimiter, dirname as dirname24, isAbsolute as isAbsolute12, join as
|
|
56458
|
+
import { basename as basename10, delimiter, dirname as dirname24, isAbsolute as isAbsolute12, join as join57, resolve as resolve32 } from "node:path";
|
|
56353
56459
|
var COMPLETION_MARKER = "# kxm completion";
|
|
56354
56460
|
var PATH_MARKER = "# kxm path";
|
|
56355
56461
|
function detectShell(env = process.env, platform = process.platform) {
|
|
@@ -56371,25 +56477,25 @@ function completionScriptPath(shell, options = {}) {
|
|
|
56371
56477
|
const env = options.env ?? process.env;
|
|
56372
56478
|
const explicit = options.configDir ?? env.KXM_USER_CONFIG_DIR?.trim();
|
|
56373
56479
|
const base = explicit && explicit.length > 0 ? resolve32(explicit) : resolve32(effectiveHome(options), ".config", "kxm");
|
|
56374
|
-
return
|
|
56480
|
+
return join57(base, "completions", `kxm.${shell}`);
|
|
56375
56481
|
}
|
|
56376
56482
|
function bashRcCandidate(options) {
|
|
56377
56483
|
const home = effectiveHome(options);
|
|
56378
|
-
const candidates = [
|
|
56484
|
+
const candidates = [join57(home, ".bashrc"), join57(home, ".bash_profile")];
|
|
56379
56485
|
const existing = candidates.find((candidate) => existsSync48(candidate));
|
|
56380
56486
|
return existing ?? candidates[0];
|
|
56381
56487
|
}
|
|
56382
56488
|
function zshRcCandidate(options) {
|
|
56383
56489
|
const env = options.env ?? process.env;
|
|
56384
56490
|
const home = effectiveHome(options);
|
|
56385
|
-
if (env.ZDOTDIR?.trim()) return
|
|
56386
|
-
return
|
|
56491
|
+
if (env.ZDOTDIR?.trim()) return join57(resolve32(env.ZDOTDIR.trim()), ".zshrc");
|
|
56492
|
+
return join57(home, ".zshrc");
|
|
56387
56493
|
}
|
|
56388
56494
|
function fishCompletionTarget(shell, options) {
|
|
56389
56495
|
const env = options.env ?? process.env;
|
|
56390
56496
|
const home = effectiveHome(options);
|
|
56391
|
-
if (env.XDG_CONFIG_HOME?.trim()) return
|
|
56392
|
-
return
|
|
56497
|
+
if (env.XDG_CONFIG_HOME?.trim()) return join57(resolve32(env.XDG_CONFIG_HOME.trim()), "fish", "completions", "kxm.fish");
|
|
56498
|
+
return join57(home, ".config", "fish", "completions", "kxm.fish");
|
|
56393
56499
|
}
|
|
56394
56500
|
function completionRcTarget(shell, options = {}) {
|
|
56395
56501
|
if (shell === "fish") {
|
|
@@ -56477,7 +56583,7 @@ function kxmBinDir(env = process.env) {
|
|
|
56477
56583
|
const argv1 = env.KXM_ENTRY ?? process.argv[1];
|
|
56478
56584
|
if (argv1 && isAbsolute12(argv1)) {
|
|
56479
56585
|
const dir = dirname24(argv1);
|
|
56480
|
-
if (existsSync48(
|
|
56586
|
+
if (existsSync48(join57(dir, process.platform === "win32" ? "kxm.cmd" : "kxm"))) return dir;
|
|
56481
56587
|
}
|
|
56482
56588
|
const pathEnv = env.PATH ?? "";
|
|
56483
56589
|
for (const part of pathEnv.split(delimiter)) {
|
|
@@ -56517,7 +56623,7 @@ ${line}
|
|
|
56517
56623
|
var import_yaml23 = __toESM(require_dist(), 1);
|
|
56518
56624
|
init_routes();
|
|
56519
56625
|
import { existsSync as existsSync49, mkdirSync as mkdirSync34, writeFileSync as writeFileSync29 } from "node:fs";
|
|
56520
|
-
import { dirname as dirname25, join as
|
|
56626
|
+
import { dirname as dirname25, join as join58 } from "node:path";
|
|
56521
56627
|
var ADMITTED_GUIDE_BINDINGS = Object.freeze({
|
|
56522
56628
|
"anthropic/claude-fable-5.1": { harness: "claude", provider: "anthropic", model: "fable" },
|
|
56523
56629
|
"anthropic/fable": { harness: "claude", provider: "anthropic", model: "fable" },
|
|
@@ -56948,7 +57054,7 @@ function renderGuideSetupFiles(projectRoot, plan) {
|
|
|
56948
57054
|
const piMatch = binding.harness === "pi" && guidePiNoteMatches(binding);
|
|
56949
57055
|
if (binding.harness === "pi" && !piMatch) {
|
|
56950
57056
|
files.push({
|
|
56951
|
-
path:
|
|
57057
|
+
path: join58(projectRoot, ".kxm", "agents", `${role}.yaml`),
|
|
56952
57058
|
content: (0, import_yaml23.stringify)(agentDocument(role, stage))
|
|
56953
57059
|
});
|
|
56954
57060
|
continue;
|
|
@@ -56966,22 +57072,22 @@ function renderGuideSetupFiles(projectRoot, plan) {
|
|
|
56966
57072
|
modelDocument.origin = { source: GUIDE_PI_EVIDENCE_SOURCE, sha256: GUIDE_PI_EVIDENCE_SHA256 };
|
|
56967
57073
|
if (!evidenceQueued) {
|
|
56968
57074
|
files.push({
|
|
56969
|
-
path:
|
|
57075
|
+
path: join58(projectRoot, GUIDE_PI_EVIDENCE_SOURCE),
|
|
56970
57076
|
content: GUIDE_PI_EVIDENCE_NOTE
|
|
56971
57077
|
});
|
|
56972
57078
|
evidenceQueued = true;
|
|
56973
57079
|
}
|
|
56974
57080
|
}
|
|
56975
57081
|
files.push({
|
|
56976
|
-
path:
|
|
57082
|
+
path: join58(projectRoot, ".kxm", "agents", `${role}.yaml`),
|
|
56977
57083
|
content: (0, import_yaml23.stringify)(agentDocument(role, stage))
|
|
56978
57084
|
});
|
|
56979
57085
|
files.push({
|
|
56980
|
-
path:
|
|
57086
|
+
path: join58(projectRoot, ".kxm", "models", `${routeId2}.yaml`),
|
|
56981
57087
|
content: (0, import_yaml23.stringify)(modelDocument)
|
|
56982
57088
|
});
|
|
56983
57089
|
files.push({
|
|
56984
|
-
path:
|
|
57090
|
+
path: join58(projectRoot, ".kxm", "roles", `${role}.yaml`),
|
|
56985
57091
|
content: (0, import_yaml23.stringify)({
|
|
56986
57092
|
schema: "kxm.role.v2",
|
|
56987
57093
|
id: role,
|
|
@@ -56994,7 +57100,7 @@ function renderGuideSetupFiles(projectRoot, plan) {
|
|
|
56994
57100
|
}
|
|
56995
57101
|
for (const workflow of plan.workflows) {
|
|
56996
57102
|
files.push({
|
|
56997
|
-
path:
|
|
57103
|
+
path: join58(projectRoot, ".kxm", "workflows", `${workflow.slug}.yaml`),
|
|
56998
57104
|
content: (0, import_yaml23.stringify)(workflowDocument(workflow))
|
|
56999
57105
|
});
|
|
57000
57106
|
}
|
|
@@ -57011,7 +57117,7 @@ function mergeGuideRouteAdmission(projectRoot, plan) {
|
|
|
57011
57117
|
}
|
|
57012
57118
|
if (added.length === 0) return added;
|
|
57013
57119
|
policy.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
57014
|
-
const path4 =
|
|
57120
|
+
const path4 = join58(projectRoot, ".kxm", "routes.yaml");
|
|
57015
57121
|
mkdirSync34(dirname25(path4), { recursive: true });
|
|
57016
57122
|
writeFileSync29(path4, (0, import_yaml23.stringify)(policy), "utf8");
|
|
57017
57123
|
return added;
|
|
@@ -57091,7 +57197,7 @@ function applyKxmPackageUpdate(runtime, notice) {
|
|
|
57091
57197
|
detail: `release v${notice.latest} has no sha256 digest for ${name}; refusing to install`
|
|
57092
57198
|
};
|
|
57093
57199
|
}
|
|
57094
|
-
const releaseDir = mkdtempSync3(
|
|
57200
|
+
const releaseDir = mkdtempSync3(join59(tmpdir3(), "kxm-pkg-update-"));
|
|
57095
57201
|
try {
|
|
57096
57202
|
const downloader = notice.source === "github" && !ghReleaseDownloadAvailable(runtime.env) ? "curl" : "gh";
|
|
57097
57203
|
const planned = planKxmPackageUpdate(notice.source, notice.latest, releaseDir, notice.asset, downloader, notice.localRoot);
|
|
@@ -57529,7 +57635,7 @@ async function cmdImprove(runtime, options = {}) {
|
|
|
57529
57635
|
print(runtime.io, runtime.json, { ok: false, command: "improve", error: "config_invalid", detail: message }, `config_invalid: ${message}`);
|
|
57530
57636
|
return 1;
|
|
57531
57637
|
}
|
|
57532
|
-
const candidatesDir = options.outDir ? resolve33(runtime.cwd, options.outDir) :
|
|
57638
|
+
const candidatesDir = options.outDir ? resolve33(runtime.cwd, options.outDir) : join59(root, ".kxm", "candidates");
|
|
57533
57639
|
const report2 = buildImprovementReport(loaded2.records, {
|
|
57534
57640
|
candidatesDir,
|
|
57535
57641
|
projectRoot: root,
|
|
@@ -57539,7 +57645,7 @@ async function cmdImprove(runtime, options = {}) {
|
|
|
57539
57645
|
halfLifeDays: config.improvement.telemetryHalfLifeDays,
|
|
57540
57646
|
routeSwitches: loaded2.routeSwitches
|
|
57541
57647
|
});
|
|
57542
|
-
const reportDir =
|
|
57648
|
+
const reportDir = join59(runtime.dirs.assets, "improvements");
|
|
57543
57649
|
const reportPath = writeImprovementReport(reportDir, report2, runtime.dryRun);
|
|
57544
57650
|
const text = [
|
|
57545
57651
|
"Sources:",
|
|
@@ -57881,7 +57987,7 @@ async function cmdRoutingReport(runtime, options) {
|
|
|
57881
57987
|
let catalog;
|
|
57882
57988
|
if (includeEquivalentListCost) {
|
|
57883
57989
|
try {
|
|
57884
|
-
const pricesPath = options.prices ? resolve33(runtime.cwd, options.prices) :
|
|
57990
|
+
const pricesPath = options.prices ? resolve33(runtime.cwd, options.prices) : join59(runtime.dirs.workspace, "prices.yaml");
|
|
57885
57991
|
catalog = loadPriceCatalog(pricesPath);
|
|
57886
57992
|
} catch {
|
|
57887
57993
|
}
|
|
@@ -25462,7 +25462,7 @@ function sessionTokenFixHint(policy) {
|
|
|
25462
25462
|
}
|
|
25463
25463
|
|
|
25464
25464
|
// plugins/kxm/src/mcp-server.ts
|
|
25465
|
-
var VERSION = "0.7.
|
|
25465
|
+
var VERSION = "0.7.182";
|
|
25466
25466
|
var CONFIGURE_PLUGIN = "/plugin configure kxm@kxm";
|
|
25467
25467
|
var inbox = /* @__PURE__ */ new Map();
|
|
25468
25468
|
var notifiedInbox = /* @__PURE__ */ new Set();
|
package/plugins/kxm/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { existsSync } from "node:fs";
|
|
2
|
-
import { resolve } from "node:path";
|
|
3
|
-
import { DatabaseSync } from "./sqlite.ts";
|
|
1
|
+
import { existsSync, lstatSync } from "node:fs";
|
|
2
|
+
import { join, resolve } from "node:path";
|
|
3
|
+
import { DatabaseSync, openReadOnlyDatabase } from "./sqlite.ts";
|
|
4
4
|
import { discoverKxmProjectRoot } from "./project-config.ts";
|
|
5
|
-
import {
|
|
5
|
+
import { kxmRuntimePaths, projectRuntimeKey } from "./runtime-paths.ts";
|
|
6
6
|
import { parseRouteSwitchRecord, type RouteSwitchRecord } from "./route-switch.ts";
|
|
7
7
|
import { parseRoutingRecordV2, ROUTING_RECORD_V2_SCHEMA, type RoutingRecord, type RoutingRecordV2 } from "./routing.ts";
|
|
8
8
|
import { readRoutingRecords, telemetryPath } from "./telemetry.ts";
|
|
@@ -10,10 +10,14 @@ import { readRoutingRecords, telemetryPath } from "./telemetry.ts";
|
|
|
10
10
|
/**
|
|
11
11
|
* Routing-record sources for `kxm improve` and `kxm routing report`.
|
|
12
12
|
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
13
|
+
* Each Runtime event store is read read-only: one SELECT over the events
|
|
14
|
+
* table, never the runs table, the run plans, or the prompts sidecar, and
|
|
15
|
+
* never a write or a migration. When the user-state registry is present and
|
|
16
|
+
* this checkout is registered, the home store and every lane store for that
|
|
17
|
+
* project id are read. The registry itself is opened read-only at schema
|
|
18
|
+
* version 2: no constructor, migration, write lock, directory, or sidecar.
|
|
19
|
+
* Each settled attempt's outcome is resolved in memory from the event log;
|
|
20
|
+
* nothing resolved here is stored.
|
|
17
21
|
*/
|
|
18
22
|
|
|
19
23
|
export interface RoutingSourceSummary {
|
|
@@ -45,6 +49,15 @@ const ENGINE_EVENTS_SQL = "SELECT run_id, sequence, event_type, payload FROM eve
|
|
|
45
49
|
+ " WHERE event_type IN ('routing.attempt.recorded','routing.route_switched','step.entered','run.status_changed')"
|
|
46
50
|
+ " ORDER BY run_id, sequence";
|
|
47
51
|
|
|
52
|
+
/** Home row first, then lanes by registration time and project key. One query. */
|
|
53
|
+
const REGISTRY_SIBLING_SQL = "SELECT sibling.project_key AS project_key, sibling.lane_of AS lane_of"
|
|
54
|
+
+ " FROM projects AS current"
|
|
55
|
+
+ " JOIN projects AS sibling ON sibling.project_id = current.project_id"
|
|
56
|
+
+ " WHERE current.project_key = ?"
|
|
57
|
+
+ " ORDER BY CASE WHEN sibling.lane_of IS NULL THEN 0 ELSE 1 END, sibling.registered_at, sibling.project_key";
|
|
58
|
+
|
|
59
|
+
const PROJECT_KEY = /^[0-9a-f]{24}$/;
|
|
60
|
+
|
|
48
61
|
/** The simulated producer's harness label; its attempts measure nothing. */
|
|
49
62
|
const SIMULATED_HARNESS = "driver-simulated";
|
|
50
63
|
|
|
@@ -175,6 +188,75 @@ export function readEngineRoutingRecords(path: string): EngineRoutingRead {
|
|
|
175
188
|
};
|
|
176
189
|
}
|
|
177
190
|
|
|
191
|
+
function registryUnreadable(registryDb: string, detail: string): Error {
|
|
192
|
+
return new Error(`improve_source_unreadable: ${registryDb}: ${detail}`);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Event stores for this checkout.
|
|
197
|
+
*
|
|
198
|
+
* An absent registry, or a registry that has no row for this checkout's
|
|
199
|
+
* project key, keeps the single store derived from the checkout path. A
|
|
200
|
+
* present registry must be a regular schema-v2 database. Paths then come
|
|
201
|
+
* from the stored keys and the Runtime projects directory, so a stale
|
|
202
|
+
* checkout path in the registry cannot choose the store.
|
|
203
|
+
*/
|
|
204
|
+
function discoverProjectEventStores(projectRoot: string, env: NodeJS.ProcessEnv): string[] {
|
|
205
|
+
const paths = kxmRuntimePaths({ env });
|
|
206
|
+
const ownStore = join(paths.projectsDir, projectRuntimeKey(projectRoot), "run-events.db");
|
|
207
|
+
const registryDb = paths.registryDb;
|
|
208
|
+
const info = lstatSync(registryDb, { throwIfNoEntry: false });
|
|
209
|
+
if (!info) return [ownStore];
|
|
210
|
+
if (!info.isFile()) throw registryUnreadable(registryDb, "runtime registry must be a regular database file");
|
|
211
|
+
for (const suffix of ["-wal", "-shm"]) {
|
|
212
|
+
const sidecar = lstatSync(`${registryDb}${suffix}`, { throwIfNoEntry: false });
|
|
213
|
+
if (sidecar?.isSymbolicLink()) throw registryUnreadable(registryDb, "runtime registry sidecar must not be a link");
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
let database: DatabaseSync | undefined;
|
|
217
|
+
try {
|
|
218
|
+
database = openReadOnlyDatabase(registryDb);
|
|
219
|
+
database.exec("PRAGMA busy_timeout = 5000");
|
|
220
|
+
const versionRow = database.prepare("PRAGMA user_version").get() as { user_version?: number } | undefined;
|
|
221
|
+
const version = versionRow?.user_version ?? 0;
|
|
222
|
+
if (version !== 2) throw registryUnreadable(registryDb, `registry schema version ${version}; this reader requires 2`);
|
|
223
|
+
const rows = database.prepare(REGISTRY_SIBLING_SQL).all(projectRuntimeKey(projectRoot));
|
|
224
|
+
if (rows.length === 0) return [ownStore];
|
|
225
|
+
return eventStoresFromRegistryRows(registryDb, paths.projectsDir, rows);
|
|
226
|
+
} catch (error) {
|
|
227
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
228
|
+
if (message.startsWith("improve_source_unreadable:")) throw error;
|
|
229
|
+
throw new Error(`improve_source_unreadable: ${registryDb}: ${message}`, { cause: error });
|
|
230
|
+
} finally {
|
|
231
|
+
try {
|
|
232
|
+
database?.close();
|
|
233
|
+
} catch {
|
|
234
|
+
// Closing a read-only handle has nothing to flush.
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
function eventStoresFromRegistryRows(registryDb: string, projectsDir: string, rows: Array<Record<string, unknown>>): string[] {
|
|
240
|
+
const seen = new Set<string>();
|
|
241
|
+
const parsed: Array<{ projectKey: string; laneOf: string | null }> = [];
|
|
242
|
+
for (const row of rows) {
|
|
243
|
+
const projectKey = typeof row.project_key === "string" ? row.project_key : "";
|
|
244
|
+
const laneRaw = row.lane_of;
|
|
245
|
+
const laneOf = laneRaw === null || laneRaw === undefined ? null : typeof laneRaw === "string" ? laneRaw : "";
|
|
246
|
+
if (!PROJECT_KEY.test(projectKey) || seen.has(projectKey) || (laneOf !== null && !PROJECT_KEY.test(laneOf))) {
|
|
247
|
+
throw registryUnreadable(registryDb, "registry identity is corrupt");
|
|
248
|
+
}
|
|
249
|
+
seen.add(projectKey);
|
|
250
|
+
parsed.push({ projectKey, laneOf });
|
|
251
|
+
}
|
|
252
|
+
const homes = parsed.filter((row) => row.laneOf === null);
|
|
253
|
+
const homeKey = homes[0]?.projectKey;
|
|
254
|
+
if (homes.length !== 1 || homeKey === undefined || parsed.some((row) => row.laneOf !== null && row.laneOf !== homeKey)) {
|
|
255
|
+
throw registryUnreadable(registryDb, "registry identity is corrupt");
|
|
256
|
+
}
|
|
257
|
+
return parsed.map((row) => join(projectsDir, row.projectKey, "run-events.db"));
|
|
258
|
+
}
|
|
259
|
+
|
|
178
260
|
function readJsonlSource(kind: "telemetry" | "file", path: string): { records: Array<RoutingRecord | RoutingRecordV2>; source: RoutingSourceSummary } {
|
|
179
261
|
const exists = existsSync(path);
|
|
180
262
|
const records = exists ? readRoutingRecords(path).map((entry) => entry.routing) : [];
|
|
@@ -190,10 +272,10 @@ function attemptKey(record: RoutingRecord | RoutingRecordV2): string | undefined
|
|
|
190
272
|
/**
|
|
191
273
|
* Load routing records for improvement and routing reports.
|
|
192
274
|
*
|
|
193
|
-
* With `file`, only that file is read. Otherwise
|
|
194
|
-
* store (when cwd is inside a KXM project) comes first, then
|
|
195
|
-
* v2 records are de-duplicated by attemptId: the first
|
|
196
|
-
* later source counts the drop.
|
|
275
|
+
* With `file`, only that file is read. Otherwise every discovered Runtime
|
|
276
|
+
* event store (when cwd is inside a KXM project) comes first, then
|
|
277
|
+
* telemetry.jsonl. v2 records are de-duplicated by attemptId: the first
|
|
278
|
+
* occurrence wins and the later source counts the drop.
|
|
197
279
|
*/
|
|
198
280
|
export function loadRoutingSources(options: {
|
|
199
281
|
cwd: string;
|
|
@@ -206,7 +288,11 @@ export function loadRoutingSources(options: {
|
|
|
206
288
|
if (options.file !== undefined) {
|
|
207
289
|
reads.push(readJsonlSource("file", resolve(options.cwd, options.file)));
|
|
208
290
|
} else {
|
|
209
|
-
if (projectRoot !== undefined)
|
|
291
|
+
if (projectRoot !== undefined) {
|
|
292
|
+
for (const path of discoverProjectEventStores(projectRoot, options.env)) {
|
|
293
|
+
reads.push(readEngineRoutingRecords(path));
|
|
294
|
+
}
|
|
295
|
+
}
|
|
210
296
|
reads.push(readJsonlSource("telemetry", telemetryPath(options.logsDir)));
|
|
211
297
|
}
|
|
212
298
|
|
|
@@ -11,7 +11,7 @@ import { deliverInboxNotification } from "./inbox.ts";
|
|
|
11
11
|
import type { HubEvent, MessageRecord } from "./protocol.ts";
|
|
12
12
|
import { sessionTokenFixHint } from "./session-token-hint.ts";
|
|
13
13
|
|
|
14
|
-
const VERSION = "0.7.
|
|
14
|
+
const VERSION = "0.7.182";
|
|
15
15
|
const CONFIGURE_PLUGIN = "/plugin configure kxm@kxm";
|
|
16
16
|
const inbox = new Map<string, MessageRecord>();
|
|
17
17
|
const notifiedInbox = new Set<string>();
|