@kontextmind/kxm 0.7.0 → 0.7.4
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/.kxm/roles/writer.yaml +2 -0
- package/docs/README.md +2 -0
- package/docs/agent-skills.md +5 -2
- package/docs/configuration.md +10 -1
- package/docs/getting-started.md +21 -0
- package/docs/operations.md +24 -0
- package/docs/skills/repo-work-delivery.md +5 -0
- package/docs/skills.md +2 -0
- package/docs/troubleshooting.md +22 -1
- package/package.json +1 -1
- package/plugins/kxm/.claude-plugin/plugin.json +1 -1
- package/plugins/kxm/dist/cli.js +2257 -439
- package/plugins/kxm/dist/core.js +57 -0
- package/plugins/kxm/dist/extension.js +40 -3
- package/plugins/kxm/dist/mcp-server.js +1 -1
- package/plugins/kxm/dist/runtime.js +140 -17
- package/plugins/kxm/dist/server.js +129 -4
- package/plugins/kxm/dist/vnext-runtime-supervisor.js +136 -13
- package/plugins/kxm/package.json +1 -1
- package/plugins/kxm/skills/kxm-hub-ops/SKILL.md +9 -0
- package/plugins/kxm/skills/kxm-project-setup/SKILL.md +9 -2
- package/plugins/kxm/src/autocomplete.ts +1 -1
- package/plugins/kxm/src/cli.ts +507 -9
- package/plugins/kxm/src/completion-install.ts +223 -0
- package/plugins/kxm/src/database.ts +1 -1
- package/plugins/kxm/src/external-effects.ts +1 -1
- package/plugins/kxm/src/hub-env.ts +193 -0
- package/plugins/kxm/src/init-guide-setup.ts +547 -0
- package/plugins/kxm/src/local-snapshot.ts +1 -1
- package/plugins/kxm/src/mcp-server.ts +1 -1
- package/plugins/kxm/src/protocol.ts +111 -0
- package/plugins/kxm/src/role.ts +335 -0
- package/plugins/kxm/src/safety-integrity.ts +76 -0
- package/plugins/kxm/src/sqlite.ts +76 -0
- package/plugins/kxm/src/store.ts +1 -1
- package/plugins/kxm/src/vnext-bindings.ts +1 -1
- package/plugins/kxm/src/vnext-config.ts +38 -1
- package/plugins/kxm/src/vnext-engine-command.ts +2 -0
- package/plugins/kxm/src/vnext-engine.ts +16 -0
- package/plugins/kxm/src/vnext-harness.ts +4 -2
- package/plugins/kxm/src/vnext-oneshot-process.ts +21 -4
- package/plugins/kxm/src/vnext-oneshot-producer.ts +18 -0
- package/plugins/kxm/src/vnext-runtime-store.ts +1 -1
- package/plugins/kxm/src/workflow-tui.ts +1 -1
- package/plugins/kxm/src/workflow.ts +144 -0
- package/scripts/kxm-bump-version.mjs +146 -0
- package/scripts/kxm-hub.mjs +145 -3
- package/scripts/kxm-publish-npm.mjs +3 -1
- package/scripts/kxm-release-github.mjs +3 -1
- package/scripts/kxm.mjs +0 -0
package/plugins/kxm/dist/core.js
CHANGED
|
@@ -59,6 +59,61 @@ function parseBoundedInteger(value, field, fallback, min, max) {
|
|
|
59
59
|
}
|
|
60
60
|
return value;
|
|
61
61
|
}
|
|
62
|
+
var TERMINAL_RECEIPT_SCHEMA = "kxm.terminal-receipt.v1";
|
|
63
|
+
var VALID_TERMINAL_STATUSES = /* @__PURE__ */ new Set(["accepted", "audit_escalation", "rejected", "error"]);
|
|
64
|
+
function validateTerminalReceipt(value) {
|
|
65
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
66
|
+
throw new ProtocolError(400, "terminal receipt must be an object", "invalid_terminal_receipt");
|
|
67
|
+
}
|
|
68
|
+
const record = value;
|
|
69
|
+
if (record.schema !== void 0 && record.schema !== TERMINAL_RECEIPT_SCHEMA) {
|
|
70
|
+
throw new ProtocolError(400, `terminal receipt schema must be ${TERMINAL_RECEIPT_SCHEMA}`, "invalid_terminal_receipt");
|
|
71
|
+
}
|
|
72
|
+
const status = record.status;
|
|
73
|
+
if (!status || !VALID_TERMINAL_STATUSES.has(status)) {
|
|
74
|
+
throw new ProtocolError(
|
|
75
|
+
400,
|
|
76
|
+
`terminal receipt status must be one of: ${Array.from(VALID_TERMINAL_STATUSES).join(", ")}`,
|
|
77
|
+
"invalid_terminal_receipt"
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
const seat = requireString(record.seat, "seat", { max: 64 });
|
|
81
|
+
const runId = requireString(record.runId, "runId", { max: 128 });
|
|
82
|
+
const stageId = requireString(record.stageId, "stageId", { max: 128 });
|
|
83
|
+
const timestamp = requireString(record.timestamp, "timestamp", { max: 64 });
|
|
84
|
+
const host = requireString(record.host, "host", { max: 64 });
|
|
85
|
+
const model = requireString(record.model, "model", { max: 128 });
|
|
86
|
+
let evidence;
|
|
87
|
+
if (record.evidence !== void 0) {
|
|
88
|
+
if (!record.evidence || typeof record.evidence !== "object" || Array.isArray(record.evidence)) {
|
|
89
|
+
throw new ProtocolError(400, "terminal receipt evidence must be an object", "invalid_terminal_receipt");
|
|
90
|
+
}
|
|
91
|
+
evidence = record.evidence;
|
|
92
|
+
}
|
|
93
|
+
let metrics;
|
|
94
|
+
if (record.metrics !== void 0) {
|
|
95
|
+
if (!record.metrics || typeof record.metrics !== "object" || Array.isArray(record.metrics)) {
|
|
96
|
+
throw new ProtocolError(400, "terminal receipt metrics must be an object", "invalid_terminal_receipt");
|
|
97
|
+
}
|
|
98
|
+
metrics = record.metrics;
|
|
99
|
+
}
|
|
100
|
+
const escalationReason = optionalString(record.escalationReason, "escalationReason", 1024);
|
|
101
|
+
const ruling = optionalString(record.ruling, "ruling", 2048);
|
|
102
|
+
return {
|
|
103
|
+
schema: TERMINAL_RECEIPT_SCHEMA,
|
|
104
|
+
status,
|
|
105
|
+
seat,
|
|
106
|
+
runId,
|
|
107
|
+
stageId,
|
|
108
|
+
timestamp,
|
|
109
|
+
host,
|
|
110
|
+
model,
|
|
111
|
+
...evidence ? { evidence } : {},
|
|
112
|
+
...metrics ? { metrics } : {},
|
|
113
|
+
...escalationReason ? { escalationReason } : {},
|
|
114
|
+
...ruling ? { ruling } : {}
|
|
115
|
+
};
|
|
116
|
+
}
|
|
62
117
|
|
|
63
118
|
// plugins/kxm/src/routing.ts
|
|
64
119
|
import { createHash } from "node:crypto";
|
|
@@ -1957,6 +2012,7 @@ export {
|
|
|
1957
2012
|
ROUTING_RECORD_SCHEMA,
|
|
1958
2013
|
ROUTING_RECORD_V2_SCHEMA,
|
|
1959
2014
|
ROUTING_REPORT_SCHEMA,
|
|
2015
|
+
TERMINAL_RECEIPT_SCHEMA,
|
|
1960
2016
|
WORKER_RESULT_SCHEMA,
|
|
1961
2017
|
WORKER_SCHEMA,
|
|
1962
2018
|
agentWorker,
|
|
@@ -1999,5 +2055,6 @@ export {
|
|
|
1999
2055
|
rotateLogFiles,
|
|
2000
2056
|
sessionTokenPath,
|
|
2001
2057
|
timingSafeStringCompare,
|
|
2058
|
+
validateTerminalReceipt,
|
|
2002
2059
|
workerResult
|
|
2003
2060
|
};
|
|
@@ -9647,7 +9647,45 @@ import { join as join7 } from "node:path";
|
|
|
9647
9647
|
import { existsSync as existsSync2, readdirSync, readFileSync as readFileSync4 } from "node:fs";
|
|
9648
9648
|
import { homedir as homedir2 } from "node:os";
|
|
9649
9649
|
import { isAbsolute, join as join4, resolve as resolve2 } from "node:path";
|
|
9650
|
-
|
|
9650
|
+
|
|
9651
|
+
// plugins/kxm/src/sqlite.ts
|
|
9652
|
+
import { createRequire } from "node:module";
|
|
9653
|
+
var requireFromHere = createRequire(import.meta.url);
|
|
9654
|
+
function loadNative() {
|
|
9655
|
+
try {
|
|
9656
|
+
const mod = requireFromHere("node:sqlite");
|
|
9657
|
+
if (mod.DatabaseSync) return { Ctor: mod.DatabaseSync, bun: false };
|
|
9658
|
+
} catch {
|
|
9659
|
+
}
|
|
9660
|
+
try {
|
|
9661
|
+
const mod = requireFromHere("bun:sqlite");
|
|
9662
|
+
const Ctor = mod?.DatabaseSync ?? mod?.Database;
|
|
9663
|
+
if (Ctor) return { Ctor, bun: true };
|
|
9664
|
+
} catch {
|
|
9665
|
+
}
|
|
9666
|
+
throw new Error("kxm: no supported sqlite module found (need node:sqlite or bun:sqlite)");
|
|
9667
|
+
}
|
|
9668
|
+
var native = loadNative();
|
|
9669
|
+
var DatabaseSync = class {
|
|
9670
|
+
inner;
|
|
9671
|
+
constructor(path, options) {
|
|
9672
|
+
let normalized = options;
|
|
9673
|
+
if (native.bun && options) {
|
|
9674
|
+
const { readOnly, ...rest } = options;
|
|
9675
|
+
normalized = readOnly === void 0 ? rest : { ...rest, readonly: readOnly };
|
|
9676
|
+
}
|
|
9677
|
+
this.inner = normalized === void 0 ? new native.Ctor(path) : new native.Ctor(path, normalized);
|
|
9678
|
+
}
|
|
9679
|
+
prepare(sql) {
|
|
9680
|
+
return this.inner.prepare(sql);
|
|
9681
|
+
}
|
|
9682
|
+
exec(sql) {
|
|
9683
|
+
return this.inner.exec(sql);
|
|
9684
|
+
}
|
|
9685
|
+
close() {
|
|
9686
|
+
this.inner.close();
|
|
9687
|
+
}
|
|
9688
|
+
};
|
|
9651
9689
|
|
|
9652
9690
|
// plugins/kxm/src/telemetry.ts
|
|
9653
9691
|
import { appendFileSync, mkdirSync as mkdirSync2, readFileSync as readFileSync3 } from "node:fs";
|
|
@@ -10469,7 +10507,6 @@ function kxmSlashCompletions(prefix) {
|
|
|
10469
10507
|
// plugins/kxm/src/workflow-tui.ts
|
|
10470
10508
|
import { existsSync as existsSync6, readFileSync as readFileSync8 } from "node:fs";
|
|
10471
10509
|
import { join as join9 } from "node:path";
|
|
10472
|
-
import { DatabaseSync as DatabaseSync2 } from "node:sqlite";
|
|
10473
10510
|
|
|
10474
10511
|
// plugins/kxm/src/config.ts
|
|
10475
10512
|
var import_yaml = __toESM(require_dist(), 1);
|
|
@@ -10484,7 +10521,7 @@ function loadActiveWorkflowProgress(repoRoot = process.cwd(), targetRunId) {
|
|
|
10484
10521
|
if (!existsSync6(dbPath)) return void 0;
|
|
10485
10522
|
let db;
|
|
10486
10523
|
try {
|
|
10487
|
-
db = new
|
|
10524
|
+
db = new DatabaseSync(dbPath, { readOnly: true });
|
|
10488
10525
|
let row;
|
|
10489
10526
|
if (targetRunId) {
|
|
10490
10527
|
row = db.prepare("SELECT record FROM workflow_runs WHERE id = ?").get(targetRunId);
|
|
@@ -17036,7 +17036,7 @@ async function deliverInboxNotification(messageId, delivered, notify) {
|
|
|
17036
17036
|
}
|
|
17037
17037
|
|
|
17038
17038
|
// plugins/kxm/src/mcp-server.ts
|
|
17039
|
-
var VERSION = "0.7.
|
|
17039
|
+
var VERSION = "0.7.4";
|
|
17040
17040
|
var inbox = /* @__PURE__ */ new Map();
|
|
17041
17041
|
var notifiedInbox = /* @__PURE__ */ new Set();
|
|
17042
17042
|
var meshClient;
|
|
@@ -15122,6 +15122,19 @@ var OUTPUT_LIMIT = 8 * 1024 * 1024;
|
|
|
15122
15122
|
var KILL_GRACE_MS = 250;
|
|
15123
15123
|
var DRAIN_GRACE_MS = 500;
|
|
15124
15124
|
var REAP_GRACE_MS = 1e3;
|
|
15125
|
+
function killProcessTree(child, signal = "SIGKILL") {
|
|
15126
|
+
if (process.platform !== "win32" && child.pid) {
|
|
15127
|
+
try {
|
|
15128
|
+
process.kill(-child.pid, signal);
|
|
15129
|
+
return;
|
|
15130
|
+
} catch {
|
|
15131
|
+
}
|
|
15132
|
+
}
|
|
15133
|
+
try {
|
|
15134
|
+
child.kill(signal);
|
|
15135
|
+
} catch {
|
|
15136
|
+
}
|
|
15137
|
+
}
|
|
15125
15138
|
function defaultSpawn(command, args, options) {
|
|
15126
15139
|
if (options.signal?.aborted) {
|
|
15127
15140
|
return Promise.resolve({ stdout: "", stderr: "", code: null, started: false, observedChildExit: false, error: new Error("process_aborted") });
|
|
@@ -15158,11 +15171,7 @@ function defaultSpawn(command, args, options) {
|
|
|
15158
15171
|
resolve8({ stdout: Buffer.concat(stdout).toString("utf8"), stderr: Buffer.concat(stderr).toString("utf8"), code, signal, started, observedChildExit, terminationRequested: stopping, ...error ? { error } : {} });
|
|
15159
15172
|
};
|
|
15160
15173
|
const kill = (requested) => {
|
|
15161
|
-
|
|
15162
|
-
if (process.platform !== "win32" && child.pid) process.kill(-child.pid, requested);
|
|
15163
|
-
else child.kill(requested);
|
|
15164
|
-
} catch {
|
|
15165
|
-
}
|
|
15174
|
+
killProcessTree(child, requested);
|
|
15166
15175
|
};
|
|
15167
15176
|
const stop = (reason) => {
|
|
15168
15177
|
if (finished) return;
|
|
@@ -15489,7 +15498,9 @@ function parseAgyOneShotUsage(stdout, _stderr) {
|
|
|
15489
15498
|
var READ_ONLY_ONESHOT_ARGS = Object.freeze({
|
|
15490
15499
|
claude: Object.freeze(["--tools", "Read,Glob,Grep", "--restricted", "--safe-mode", "--strict-mcp-config", "--mcp-config", '{"mcpServers":{}}', "--disable-slash-commands", "--no-session-persistence"]),
|
|
15491
15500
|
codex: Object.freeze(["--sandbox", "read-only", "--ignore-user-config", "-c", 'approval_policy="never"']),
|
|
15492
|
-
grok: Object.freeze(["--sandbox", "read-only", "--permission-mode", "plan", "--tools", "Read,Glob,Grep", "--no-subagents", "--disable-web-search"])
|
|
15501
|
+
grok: Object.freeze(["--sandbox", "read-only", "--permission-mode", "plan", "--tools", "Read,Glob,Grep", "--no-subagents", "--disable-web-search"]),
|
|
15502
|
+
agy: Object.freeze(["--mode", "plan", "--sandbox", "--disable-slash-commands"]),
|
|
15503
|
+
kimi: Object.freeze(["--plan"])
|
|
15493
15504
|
});
|
|
15494
15505
|
function oneShotReadOnlyArgs(harness) {
|
|
15495
15506
|
return Object.hasOwn(READ_ONLY_ONESHOT_ARGS, harness) ? READ_ONLY_ONESHOT_ARGS[harness] : void 0;
|
|
@@ -15543,7 +15554,7 @@ var BUILTIN_HARNESSES = Object.freeze([
|
|
|
15543
15554
|
authArgs: ["provider", "list"],
|
|
15544
15555
|
update: { self: ["upgrade"] },
|
|
15545
15556
|
oneShot: {
|
|
15546
|
-
argv: ["--output-format", "stream-json", "-p"],
|
|
15557
|
+
argv: [...oneShotReadOnlyArgs("kimi"), "--output-format", "stream-json", "-p"],
|
|
15547
15558
|
promptVia: "arg",
|
|
15548
15559
|
outputFormat: "stream-json",
|
|
15549
15560
|
usageParser: parseKimiOneShotUsage
|
|
@@ -15606,7 +15617,7 @@ var BUILTIN_HARNESSES = Object.freeze([
|
|
|
15606
15617
|
authArgs: ["models"],
|
|
15607
15618
|
update: { self: ["update"] },
|
|
15608
15619
|
oneShot: {
|
|
15609
|
-
argv: ["--output-format", "json", "-p"],
|
|
15620
|
+
argv: [...oneShotReadOnlyArgs("agy"), "--output-format", "json", "-p"],
|
|
15610
15621
|
promptVia: "arg",
|
|
15611
15622
|
outputFormat: "json",
|
|
15612
15623
|
usageParser: parseAgyOneShotUsage
|
|
@@ -17060,7 +17071,7 @@ function validateModelReferences(models, issues) {
|
|
|
17060
17071
|
};
|
|
17061
17072
|
for (const id of models.keys()) walk(id, []);
|
|
17062
17073
|
}
|
|
17063
|
-
function validateBundle(project, repositories, agents, models, workflows, environments, options, gateRegistry) {
|
|
17074
|
+
function validateBundle(project, repositories, agents, models, workflows, environments, options, gateRegistry, projectRoot) {
|
|
17064
17075
|
const issues = [];
|
|
17065
17076
|
const entries = valuesOf(project.value, "repositories").map((candidate) => objectValue(candidate)).filter((candidate) => Boolean(candidate));
|
|
17066
17077
|
const repositoryIds = /* @__PURE__ */ new Set();
|
|
@@ -17128,6 +17139,33 @@ function validateBundle(project, repositories, agents, models, workflows, enviro
|
|
|
17128
17139
|
const defaultWorkflow = stringValue(project.value.defaultWorkflow) ?? "default";
|
|
17129
17140
|
if (!workflows.has(defaultWorkflow)) issues.push(issue2("reference", "default_workflow_unknown", project.logicalPath, `default workflow ${defaultWorkflow} does not exist`));
|
|
17130
17141
|
for (const workflow of workflows.values()) validateWorkflow(workflow, agents, models, repositoryIds, gates, issues);
|
|
17142
|
+
if (projectRoot) {
|
|
17143
|
+
const writerRolePath = join(projectRoot, ".kxm", "roles", "writer.yaml");
|
|
17144
|
+
const implementerAgent = agents.get("implementer") ?? agents.get("writer");
|
|
17145
|
+
if (existsSync2(writerRolePath) && implementerAgent) {
|
|
17146
|
+
try {
|
|
17147
|
+
const rawRole = parseRestrictedYaml2(readFileSync(writerRolePath, "utf8"));
|
|
17148
|
+
const roleObj = objectValue(rawRole);
|
|
17149
|
+
const rosterEntries = valuesOf(roleObj ?? {}, "roster").map((candidate) => objectValue(candidate)).filter((entry) => Boolean(entry));
|
|
17150
|
+
const enabledRosterModels = rosterEntries.filter((entry) => entry.enabled !== false).map((entry) => stringValue(entry.model)).filter((m) => Boolean(m));
|
|
17151
|
+
const agentModelObj = objectValue(implementerAgent.value.model);
|
|
17152
|
+
const agentModelStr = stringValue(implementerAgent.value.model);
|
|
17153
|
+
const agentProvider = agentModelObj ? stringValue(agentModelObj.provider) : void 0;
|
|
17154
|
+
const agentModel = agentModelObj ? stringValue(agentModelObj.model) : agentModelStr;
|
|
17155
|
+
const canonicalAgentModel = agentProvider && agentModel ? `${agentProvider}/${agentModel}` : agentModel;
|
|
17156
|
+
if (enabledRosterModels.length > 0 && canonicalAgentModel) {
|
|
17157
|
+
const matches = enabledRosterModels.some((rm) => rm === canonicalAgentModel || rm === agentModel || rm.endsWith(`/${agentModel}`));
|
|
17158
|
+
if (!matches) {
|
|
17159
|
+
issues.push(issue2("semantic", "role_roster_conflicts_with_agent", ".kxm/roles/writer.yaml", `role roster in .kxm/roles/writer.yaml does not include agent model ${canonicalAgentModel} from ${implementerAgent.logicalPath}`));
|
|
17160
|
+
}
|
|
17161
|
+
}
|
|
17162
|
+
} catch (error) {
|
|
17163
|
+
if (error instanceof VnextConfigError) {
|
|
17164
|
+
issues.push(...error.issues);
|
|
17165
|
+
}
|
|
17166
|
+
}
|
|
17167
|
+
}
|
|
17168
|
+
}
|
|
17131
17169
|
return sortIssues2(issues);
|
|
17132
17170
|
}
|
|
17133
17171
|
function vnextCanonicalJson(value) {
|
|
@@ -17308,7 +17346,7 @@ function loadVnextProject(projectRoot, options = {}) {
|
|
|
17308
17346
|
}
|
|
17309
17347
|
}
|
|
17310
17348
|
if (loadIssues.length > 0) throw new VnextConfigError(loadIssues);
|
|
17311
|
-
const issues = validateBundle(project, repositories, agents, models, workflows, environments, options, gateRegistry);
|
|
17349
|
+
const issues = validateBundle(project, repositories, agents, models, workflows, environments, options, gateRegistry, root);
|
|
17312
17350
|
if (issues.length > 0) throw new VnextConfigError(issues);
|
|
17313
17351
|
const resources = [project, ...repositories.values(), ...agents.values(), ...models.values(), ...workflows.values(), ...environments, ...gateRegistry ? [gateRegistry] : []].sort((left, right) => compareCodeUnits3(left.logicalPath, right.logicalPath));
|
|
17314
17352
|
return {
|
|
@@ -17420,7 +17458,47 @@ import { dirname as dirname4, join as join4, resolve as resolve4 } from "node:pa
|
|
|
17420
17458
|
|
|
17421
17459
|
// plugins/kxm/src/vnext-bindings.ts
|
|
17422
17460
|
import { homedir } from "node:os";
|
|
17423
|
-
|
|
17461
|
+
|
|
17462
|
+
// plugins/kxm/src/sqlite.ts
|
|
17463
|
+
import { createRequire } from "node:module";
|
|
17464
|
+
var requireFromHere = createRequire(import.meta.url);
|
|
17465
|
+
function loadNative() {
|
|
17466
|
+
try {
|
|
17467
|
+
const mod = requireFromHere("node:sqlite");
|
|
17468
|
+
if (mod.DatabaseSync) return { Ctor: mod.DatabaseSync, bun: false };
|
|
17469
|
+
} catch {
|
|
17470
|
+
}
|
|
17471
|
+
try {
|
|
17472
|
+
const mod = requireFromHere("bun:sqlite");
|
|
17473
|
+
const Ctor = mod?.DatabaseSync ?? mod?.Database;
|
|
17474
|
+
if (Ctor) return { Ctor, bun: true };
|
|
17475
|
+
} catch {
|
|
17476
|
+
}
|
|
17477
|
+
throw new Error("kxm: no supported sqlite module found (need node:sqlite or bun:sqlite)");
|
|
17478
|
+
}
|
|
17479
|
+
var native = loadNative();
|
|
17480
|
+
var DatabaseSync = class {
|
|
17481
|
+
inner;
|
|
17482
|
+
constructor(path, options) {
|
|
17483
|
+
let normalized = options;
|
|
17484
|
+
if (native.bun && options) {
|
|
17485
|
+
const { readOnly, ...rest } = options;
|
|
17486
|
+
normalized = readOnly === void 0 ? rest : { ...rest, readonly: readOnly };
|
|
17487
|
+
}
|
|
17488
|
+
this.inner = normalized === void 0 ? new native.Ctor(path) : new native.Ctor(path, normalized);
|
|
17489
|
+
}
|
|
17490
|
+
prepare(sql) {
|
|
17491
|
+
return this.inner.prepare(sql);
|
|
17492
|
+
}
|
|
17493
|
+
exec(sql) {
|
|
17494
|
+
return this.inner.exec(sql);
|
|
17495
|
+
}
|
|
17496
|
+
close() {
|
|
17497
|
+
this.inner.close();
|
|
17498
|
+
}
|
|
17499
|
+
};
|
|
17500
|
+
|
|
17501
|
+
// plugins/kxm/src/vnext-bindings.ts
|
|
17424
17502
|
import { dirname as dirname2, isAbsolute as isAbsolute2, join as join2, parse, relative as relative2, resolve as resolve2, sep as sep2 } from "node:path";
|
|
17425
17503
|
var MAX_BINDING_RECORD_BYTES = 256 * 1024;
|
|
17426
17504
|
var BINDING_LABEL = "Runtime-local repository bindings";
|
|
@@ -17464,7 +17542,6 @@ import {
|
|
|
17464
17542
|
writeFileSync
|
|
17465
17543
|
} from "node:fs";
|
|
17466
17544
|
import { basename as basename2, dirname as dirname3, join as join3, resolve as resolve3 } from "node:path";
|
|
17467
|
-
import { DatabaseSync as DatabaseSync2 } from "node:sqlite";
|
|
17468
17545
|
function databaseError(code, file, message) {
|
|
17469
17546
|
const issue3 = { phase: "semantic", code, file, message };
|
|
17470
17547
|
return new VnextConfigError([issue3]);
|
|
@@ -17544,7 +17621,7 @@ function openDatabase(file, description, spec) {
|
|
|
17544
17621
|
}
|
|
17545
17622
|
}
|
|
17546
17623
|
}
|
|
17547
|
-
const database = new
|
|
17624
|
+
const database = new DatabaseSync(file);
|
|
17548
17625
|
let transaction = false;
|
|
17549
17626
|
try {
|
|
17550
17627
|
database.exec(`PRAGMA busy_timeout = ${spec.timeoutMs ?? 5e3}`);
|
|
@@ -17648,7 +17725,7 @@ function backupDatabaseFile(sourcePath, targetPath, storeId) {
|
|
|
17648
17725
|
if (existsSync3(resolvedTarget)) {
|
|
17649
17726
|
unlinkSync(resolvedTarget);
|
|
17650
17727
|
}
|
|
17651
|
-
const sourceDb = new
|
|
17728
|
+
const sourceDb = new DatabaseSync(resolvedSource);
|
|
17652
17729
|
let schemaVersion = 0;
|
|
17653
17730
|
try {
|
|
17654
17731
|
sourceDb.exec("PRAGMA busy_timeout = 5000");
|
|
@@ -17663,7 +17740,7 @@ function backupDatabaseFile(sourcePath, targetPath, storeId) {
|
|
|
17663
17740
|
} finally {
|
|
17664
17741
|
sourceDb.close();
|
|
17665
17742
|
}
|
|
17666
|
-
const targetDb = new
|
|
17743
|
+
const targetDb = new DatabaseSync(resolvedTarget);
|
|
17667
17744
|
try {
|
|
17668
17745
|
targetDb.exec("PRAGMA busy_timeout = 5000");
|
|
17669
17746
|
if (!checkIntegrity(targetDb)) {
|
|
@@ -17695,7 +17772,7 @@ function restoreDatabaseFile(backupPath, targetPath, storeId, expectedSchemaVers
|
|
|
17695
17772
|
if (!backupStat || !backupStat.isFile() || backupStat.isSymbolicLink()) {
|
|
17696
17773
|
throw databaseError("runtime_path_invalid", resolvedBackup, `backup database ${resolvedBackup} must be a regular file, not a link or directory`);
|
|
17697
17774
|
}
|
|
17698
|
-
const backupDb = new
|
|
17775
|
+
const backupDb = new DatabaseSync(resolvedBackup);
|
|
17699
17776
|
let schemaVersion = 0;
|
|
17700
17777
|
try {
|
|
17701
17778
|
backupDb.exec("PRAGMA busy_timeout = 5000");
|
|
@@ -17735,7 +17812,7 @@ function restoreDatabaseFile(backupPath, targetPath, storeId, expectedSchemaVers
|
|
|
17735
17812
|
chmodSync(resolvedTarget, 384);
|
|
17736
17813
|
} catch {
|
|
17737
17814
|
}
|
|
17738
|
-
const targetDb = new
|
|
17815
|
+
const targetDb = new DatabaseSync(resolvedTarget);
|
|
17739
17816
|
try {
|
|
17740
17817
|
targetDb.exec("PRAGMA busy_timeout = 5000");
|
|
17741
17818
|
if (!checkIntegrity(targetDb)) {
|
|
@@ -22912,6 +22989,32 @@ function evaluateArtifactsGate(context, definition) {
|
|
|
22912
22989
|
// plugins/kxm/src/vnext-engine-command.ts
|
|
22913
22990
|
import { spawn as spawn2 } from "node:child_process";
|
|
22914
22991
|
import { createHash as createHash9 } from "node:crypto";
|
|
22992
|
+
|
|
22993
|
+
// plugins/kxm/src/safety-integrity.ts
|
|
22994
|
+
var DESTRUCTIVE_COMMAND_PATTERNS = Object.freeze([
|
|
22995
|
+
/^\s*rm\s+.*-[a-zA-Z]*r[a-zA-Z]*f/i,
|
|
22996
|
+
/^\s*rm\s+.*-[a-zA-Z]*f[a-zA-Z]*r/i,
|
|
22997
|
+
/^\s*rm\s+.*(-[a-zA-Z]*r[a-zA-Z]*\s+.*-[a-zA-Z]*f[a-zA-Z]*|-[a-zA-Z]*f[a-zA-Z]*\s+.*-[a-zA-Z]*r[a-zA-Z]*)/i,
|
|
22998
|
+
/^\s*git\s+reset\s+--hard/i,
|
|
22999
|
+
/^\s*git\s+clean\s+-[a-zA-Z]*f/i,
|
|
23000
|
+
/^\s*git\s+checkout\s+--\s+/i,
|
|
23001
|
+
/^\s*git\s+restore\s+(\.|\*|--staged\s+(\.|\*))/i
|
|
23002
|
+
]);
|
|
23003
|
+
function assertCommandSeatbelt(command) {
|
|
23004
|
+
for (const pattern of DESTRUCTIVE_COMMAND_PATTERNS) {
|
|
23005
|
+
if (pattern.test(command)) {
|
|
23006
|
+
throw new Error(
|
|
23007
|
+
`Command blocked by KXM safety seatbelt: "${command}". Destructive workspace mutations require explicit operator override.`
|
|
23008
|
+
);
|
|
23009
|
+
}
|
|
23010
|
+
}
|
|
23011
|
+
}
|
|
23012
|
+
var INSECURE_SSH_HOST_KEY_PATTERNS = Object.freeze([
|
|
23013
|
+
/StrictHostKeyChecking=(accept-new|no|off)/i,
|
|
23014
|
+
/UserKnownHostsFile=\/dev\/null/i
|
|
23015
|
+
]);
|
|
23016
|
+
|
|
23017
|
+
// plugins/kxm/src/vnext-engine-command.ts
|
|
22915
23018
|
var MAX_DIRECT_TIMER_MS = 2147483647;
|
|
22916
23019
|
var COMMAND_TERM_GRACE_MS = 2e3;
|
|
22917
23020
|
var COMMAND_FINAL_WAIT_MS = 2e3;
|
|
@@ -23028,6 +23131,7 @@ var CommandObserver = class {
|
|
|
23028
23131
|
if (!Number.isInteger(this.definition.timeoutMs) || this.definition.timeoutMs < 1 || this.definition.timeoutMs > MAX_DIRECT_TIMER_MS) {
|
|
23029
23132
|
throw runtimeError("run_events_illegal", "command", "command timeoutMs exceeds the direct timer bound");
|
|
23030
23133
|
}
|
|
23134
|
+
assertCommandSeatbelt(this.definition.argv.join(" "));
|
|
23031
23135
|
this.startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
23032
23136
|
let child;
|
|
23033
23137
|
try {
|
|
@@ -24153,6 +24257,7 @@ async function stepLocked(context, runId, producer, token) {
|
|
|
24153
24257
|
return drivePanel(context, prepared.panel, producer);
|
|
24154
24258
|
}
|
|
24155
24259
|
async function runPreparedCommandGate(context, prepared, definition, token) {
|
|
24260
|
+
assertCommandSeatbelt(definition.argv.join(" "));
|
|
24156
24261
|
const storePath = context.eventStore.path;
|
|
24157
24262
|
const observer = createCommandObserver({
|
|
24158
24263
|
definition,
|
|
@@ -25930,6 +26035,24 @@ function createVnextOneShotProducer(options = {}) {
|
|
|
25930
26035
|
"--output-format",
|
|
25931
26036
|
"json"
|
|
25932
26037
|
];
|
|
26038
|
+
} else if (harness === "agy") {
|
|
26039
|
+
args = [
|
|
26040
|
+
"-m",
|
|
26041
|
+
resolved.model,
|
|
26042
|
+
...permissionArgs,
|
|
26043
|
+
"--output-format",
|
|
26044
|
+
"json",
|
|
26045
|
+
"-p"
|
|
26046
|
+
];
|
|
26047
|
+
} else if (harness === "kimi") {
|
|
26048
|
+
args = [
|
|
26049
|
+
"-m",
|
|
26050
|
+
resolved.model,
|
|
26051
|
+
...permissionArgs,
|
|
26052
|
+
"--output-format",
|
|
26053
|
+
"stream-json",
|
|
26054
|
+
"-p"
|
|
26055
|
+
];
|
|
25933
26056
|
} else {
|
|
25934
26057
|
throw new Error(`oneshot_harness_unsupported: ${harness} permission_profile_unaudited`);
|
|
25935
26058
|
}
|
|
@@ -7431,6 +7431,61 @@ function parseBoundedInteger(value, field, fallback, min, max) {
|
|
|
7431
7431
|
}
|
|
7432
7432
|
return value;
|
|
7433
7433
|
}
|
|
7434
|
+
var TERMINAL_RECEIPT_SCHEMA = "kxm.terminal-receipt.v1";
|
|
7435
|
+
var VALID_TERMINAL_STATUSES = /* @__PURE__ */ new Set(["accepted", "audit_escalation", "rejected", "error"]);
|
|
7436
|
+
function validateTerminalReceipt(value) {
|
|
7437
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
7438
|
+
throw new ProtocolError(400, "terminal receipt must be an object", "invalid_terminal_receipt");
|
|
7439
|
+
}
|
|
7440
|
+
const record = value;
|
|
7441
|
+
if (record.schema !== void 0 && record.schema !== TERMINAL_RECEIPT_SCHEMA) {
|
|
7442
|
+
throw new ProtocolError(400, `terminal receipt schema must be ${TERMINAL_RECEIPT_SCHEMA}`, "invalid_terminal_receipt");
|
|
7443
|
+
}
|
|
7444
|
+
const status = record.status;
|
|
7445
|
+
if (!status || !VALID_TERMINAL_STATUSES.has(status)) {
|
|
7446
|
+
throw new ProtocolError(
|
|
7447
|
+
400,
|
|
7448
|
+
`terminal receipt status must be one of: ${Array.from(VALID_TERMINAL_STATUSES).join(", ")}`,
|
|
7449
|
+
"invalid_terminal_receipt"
|
|
7450
|
+
);
|
|
7451
|
+
}
|
|
7452
|
+
const seat = requireString(record.seat, "seat", { max: 64 });
|
|
7453
|
+
const runId = requireString(record.runId, "runId", { max: 128 });
|
|
7454
|
+
const stageId = requireString(record.stageId, "stageId", { max: 128 });
|
|
7455
|
+
const timestamp = requireString(record.timestamp, "timestamp", { max: 64 });
|
|
7456
|
+
const host2 = requireString(record.host, "host", { max: 64 });
|
|
7457
|
+
const model = requireString(record.model, "model", { max: 128 });
|
|
7458
|
+
let evidence;
|
|
7459
|
+
if (record.evidence !== void 0) {
|
|
7460
|
+
if (!record.evidence || typeof record.evidence !== "object" || Array.isArray(record.evidence)) {
|
|
7461
|
+
throw new ProtocolError(400, "terminal receipt evidence must be an object", "invalid_terminal_receipt");
|
|
7462
|
+
}
|
|
7463
|
+
evidence = record.evidence;
|
|
7464
|
+
}
|
|
7465
|
+
let metrics;
|
|
7466
|
+
if (record.metrics !== void 0) {
|
|
7467
|
+
if (!record.metrics || typeof record.metrics !== "object" || Array.isArray(record.metrics)) {
|
|
7468
|
+
throw new ProtocolError(400, "terminal receipt metrics must be an object", "invalid_terminal_receipt");
|
|
7469
|
+
}
|
|
7470
|
+
metrics = record.metrics;
|
|
7471
|
+
}
|
|
7472
|
+
const escalationReason = optionalString(record.escalationReason, "escalationReason", 1024);
|
|
7473
|
+
const ruling = optionalString(record.ruling, "ruling", 2048);
|
|
7474
|
+
return {
|
|
7475
|
+
schema: TERMINAL_RECEIPT_SCHEMA,
|
|
7476
|
+
status,
|
|
7477
|
+
seat,
|
|
7478
|
+
runId,
|
|
7479
|
+
stageId,
|
|
7480
|
+
timestamp,
|
|
7481
|
+
host: host2,
|
|
7482
|
+
model,
|
|
7483
|
+
...evidence ? { evidence } : {},
|
|
7484
|
+
...metrics ? { metrics } : {},
|
|
7485
|
+
...escalationReason ? { escalationReason } : {},
|
|
7486
|
+
...ruling ? { ruling } : {}
|
|
7487
|
+
};
|
|
7488
|
+
}
|
|
7434
7489
|
|
|
7435
7490
|
// plugins/kxm/src/diagnostics.ts
|
|
7436
7491
|
function workflowScopeExtras(operation, assignedCoordinatorName) {
|
|
@@ -7982,12 +8037,17 @@ function parseWorkflowDefinitions(raw, environment = process.env, onWarning) {
|
|
|
7982
8037
|
if (stageMaxTransitions !== void 0 && (!Number.isInteger(stageMaxTransitions) || stageMaxTransitions < 1 || stageMaxTransitions > 100)) {
|
|
7983
8038
|
throw new Error(`stage ${stageId} maxTransitions must be an integer between 1 and 100`);
|
|
7984
8039
|
}
|
|
8040
|
+
const autoResumeLimit = stage.autoResumeLimit;
|
|
8041
|
+
if (autoResumeLimit !== void 0 && (!Number.isInteger(autoResumeLimit) || autoResumeLimit < 1 || autoResumeLimit > 20)) {
|
|
8042
|
+
throw new Error(`stage ${stageId} autoResumeLimit must be an integer between 1 and 20`);
|
|
8043
|
+
}
|
|
7985
8044
|
return {
|
|
7986
8045
|
id: stageId,
|
|
7987
8046
|
label: requireString(stage.label ?? stageId, "stage.label", { max: 128 }),
|
|
7988
8047
|
instructions: requireString(stage.instructions, "stage.instructions", { max: 4e3 }),
|
|
7989
8048
|
requiredEvidence,
|
|
7990
8049
|
maxAttempts,
|
|
8050
|
+
...autoResumeLimit !== void 0 ? { autoResumeLimit } : {},
|
|
7991
8051
|
...area ? { area } : {},
|
|
7992
8052
|
...evidencePolicies ? { evidencePolicies } : {},
|
|
7993
8053
|
...on ? { on } : {},
|
|
@@ -8266,6 +8326,31 @@ function checkpointRun(run, stageId, status, summary, evidence, timestamp, verif
|
|
|
8266
8326
|
delete run.currentStage;
|
|
8267
8327
|
return { retry: false, completed: false, run };
|
|
8268
8328
|
}
|
|
8329
|
+
if (stage.autoResumeLimit !== void 0 && stage.attempts >= stage.autoResumeLimit) {
|
|
8330
|
+
stage.status = "in_progress";
|
|
8331
|
+
const reason = summary || `autoResumeLimit of ${stage.autoResumeLimit} reached on stage ${stage.id}`;
|
|
8332
|
+
const receipt = validateTerminalReceipt({
|
|
8333
|
+
schema: TERMINAL_RECEIPT_SCHEMA,
|
|
8334
|
+
status: "audit_escalation",
|
|
8335
|
+
seat: stage.id,
|
|
8336
|
+
runId: run.id,
|
|
8337
|
+
stageId: stage.id,
|
|
8338
|
+
timestamp,
|
|
8339
|
+
host: "pi",
|
|
8340
|
+
model: "default",
|
|
8341
|
+
escalationReason: reason
|
|
8342
|
+
});
|
|
8343
|
+
const expiresAt = new Date(Date.parse(timestamp) + 24 * 60 * 60 * 1e3).toISOString();
|
|
8344
|
+
waitForWorkflowSignal(run, stage.id, "audit_escalation", reason, timestamp, expiresAt);
|
|
8345
|
+
stage.receipt = receipt;
|
|
8346
|
+
stage.auditEscalation = {
|
|
8347
|
+
reason,
|
|
8348
|
+
timestamp,
|
|
8349
|
+
receipt
|
|
8350
|
+
};
|
|
8351
|
+
return { retry: false, completed: false, run };
|
|
8352
|
+
}
|
|
8353
|
+
stage.status = status;
|
|
8269
8354
|
const outcomeKey = outcome ?? status;
|
|
8270
8355
|
const rule = resolveOutcomeRule(stage, outcomeKey);
|
|
8271
8356
|
if (rule && stage.attempts < stage.maxAttempts) {
|
|
@@ -10833,7 +10918,45 @@ import {
|
|
|
10833
10918
|
writeFileSync as writeFileSync4
|
|
10834
10919
|
} from "node:fs";
|
|
10835
10920
|
import { basename as basename3, dirname as dirname3, join as join4, resolve as resolve4 } from "node:path";
|
|
10836
|
-
|
|
10921
|
+
|
|
10922
|
+
// plugins/kxm/src/sqlite.ts
|
|
10923
|
+
import { createRequire } from "node:module";
|
|
10924
|
+
var requireFromHere = createRequire(import.meta.url);
|
|
10925
|
+
function loadNative() {
|
|
10926
|
+
try {
|
|
10927
|
+
const mod = requireFromHere("node:sqlite");
|
|
10928
|
+
if (mod.DatabaseSync) return { Ctor: mod.DatabaseSync, bun: false };
|
|
10929
|
+
} catch {
|
|
10930
|
+
}
|
|
10931
|
+
try {
|
|
10932
|
+
const mod = requireFromHere("bun:sqlite");
|
|
10933
|
+
const Ctor = mod?.DatabaseSync ?? mod?.Database;
|
|
10934
|
+
if (Ctor) return { Ctor, bun: true };
|
|
10935
|
+
} catch {
|
|
10936
|
+
}
|
|
10937
|
+
throw new Error("kxm: no supported sqlite module found (need node:sqlite or bun:sqlite)");
|
|
10938
|
+
}
|
|
10939
|
+
var native = loadNative();
|
|
10940
|
+
var DatabaseSync = class {
|
|
10941
|
+
inner;
|
|
10942
|
+
constructor(path, options) {
|
|
10943
|
+
let normalized = options;
|
|
10944
|
+
if (native.bun && options) {
|
|
10945
|
+
const { readOnly, ...rest } = options;
|
|
10946
|
+
normalized = readOnly === void 0 ? rest : { ...rest, readonly: readOnly };
|
|
10947
|
+
}
|
|
10948
|
+
this.inner = normalized === void 0 ? new native.Ctor(path) : new native.Ctor(path, normalized);
|
|
10949
|
+
}
|
|
10950
|
+
prepare(sql) {
|
|
10951
|
+
return this.inner.prepare(sql);
|
|
10952
|
+
}
|
|
10953
|
+
exec(sql) {
|
|
10954
|
+
return this.inner.exec(sql);
|
|
10955
|
+
}
|
|
10956
|
+
close() {
|
|
10957
|
+
this.inner.close();
|
|
10958
|
+
}
|
|
10959
|
+
};
|
|
10837
10960
|
|
|
10838
10961
|
// plugins/kxm/src/vnext-config.ts
|
|
10839
10962
|
import { basename as basename2, dirname as dirname2, extname as extname2, isAbsolute, join as join3, relative, resolve as resolve3, sep } from "node:path";
|
|
@@ -11090,7 +11213,9 @@ function parseAgyOneShotUsage(stdout, _stderr) {
|
|
|
11090
11213
|
var READ_ONLY_ONESHOT_ARGS = Object.freeze({
|
|
11091
11214
|
claude: Object.freeze(["--tools", "Read,Glob,Grep", "--restricted", "--safe-mode", "--strict-mcp-config", "--mcp-config", '{"mcpServers":{}}', "--disable-slash-commands", "--no-session-persistence"]),
|
|
11092
11215
|
codex: Object.freeze(["--sandbox", "read-only", "--ignore-user-config", "-c", 'approval_policy="never"']),
|
|
11093
|
-
grok: Object.freeze(["--sandbox", "read-only", "--permission-mode", "plan", "--tools", "Read,Glob,Grep", "--no-subagents", "--disable-web-search"])
|
|
11216
|
+
grok: Object.freeze(["--sandbox", "read-only", "--permission-mode", "plan", "--tools", "Read,Glob,Grep", "--no-subagents", "--disable-web-search"]),
|
|
11217
|
+
agy: Object.freeze(["--mode", "plan", "--sandbox", "--disable-slash-commands"]),
|
|
11218
|
+
kimi: Object.freeze(["--plan"])
|
|
11094
11219
|
});
|
|
11095
11220
|
function oneShotReadOnlyArgs(harness) {
|
|
11096
11221
|
return Object.hasOwn(READ_ONLY_ONESHOT_ARGS, harness) ? READ_ONLY_ONESHOT_ARGS[harness] : void 0;
|
|
@@ -11144,7 +11269,7 @@ var BUILTIN_HARNESSES = Object.freeze([
|
|
|
11144
11269
|
authArgs: ["provider", "list"],
|
|
11145
11270
|
update: { self: ["upgrade"] },
|
|
11146
11271
|
oneShot: {
|
|
11147
|
-
argv: ["--output-format", "stream-json", "-p"],
|
|
11272
|
+
argv: [...oneShotReadOnlyArgs("kimi"), "--output-format", "stream-json", "-p"],
|
|
11148
11273
|
promptVia: "arg",
|
|
11149
11274
|
outputFormat: "stream-json",
|
|
11150
11275
|
usageParser: parseKimiOneShotUsage
|
|
@@ -11207,7 +11332,7 @@ var BUILTIN_HARNESSES = Object.freeze([
|
|
|
11207
11332
|
authArgs: ["models"],
|
|
11208
11333
|
update: { self: ["update"] },
|
|
11209
11334
|
oneShot: {
|
|
11210
|
-
argv: ["--output-format", "json", "-p"],
|
|
11335
|
+
argv: [...oneShotReadOnlyArgs("agy"), "--output-format", "json", "-p"],
|
|
11211
11336
|
promptVia: "arg",
|
|
11212
11337
|
outputFormat: "json",
|
|
11213
11338
|
usageParser: parseAgyOneShotUsage
|