@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.
Files changed (51) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.kxm/roles/writer.yaml +2 -0
  3. package/docs/README.md +2 -0
  4. package/docs/agent-skills.md +5 -2
  5. package/docs/configuration.md +10 -1
  6. package/docs/getting-started.md +21 -0
  7. package/docs/operations.md +24 -0
  8. package/docs/skills/repo-work-delivery.md +5 -0
  9. package/docs/skills.md +2 -0
  10. package/docs/troubleshooting.md +22 -1
  11. package/package.json +1 -1
  12. package/plugins/kxm/.claude-plugin/plugin.json +1 -1
  13. package/plugins/kxm/dist/cli.js +2257 -439
  14. package/plugins/kxm/dist/core.js +57 -0
  15. package/plugins/kxm/dist/extension.js +40 -3
  16. package/plugins/kxm/dist/mcp-server.js +1 -1
  17. package/plugins/kxm/dist/runtime.js +140 -17
  18. package/plugins/kxm/dist/server.js +129 -4
  19. package/plugins/kxm/dist/vnext-runtime-supervisor.js +136 -13
  20. package/plugins/kxm/package.json +1 -1
  21. package/plugins/kxm/skills/kxm-hub-ops/SKILL.md +9 -0
  22. package/plugins/kxm/skills/kxm-project-setup/SKILL.md +9 -2
  23. package/plugins/kxm/src/autocomplete.ts +1 -1
  24. package/plugins/kxm/src/cli.ts +507 -9
  25. package/plugins/kxm/src/completion-install.ts +223 -0
  26. package/plugins/kxm/src/database.ts +1 -1
  27. package/plugins/kxm/src/external-effects.ts +1 -1
  28. package/plugins/kxm/src/hub-env.ts +193 -0
  29. package/plugins/kxm/src/init-guide-setup.ts +547 -0
  30. package/plugins/kxm/src/local-snapshot.ts +1 -1
  31. package/plugins/kxm/src/mcp-server.ts +1 -1
  32. package/plugins/kxm/src/protocol.ts +111 -0
  33. package/plugins/kxm/src/role.ts +335 -0
  34. package/plugins/kxm/src/safety-integrity.ts +76 -0
  35. package/plugins/kxm/src/sqlite.ts +76 -0
  36. package/plugins/kxm/src/store.ts +1 -1
  37. package/plugins/kxm/src/vnext-bindings.ts +1 -1
  38. package/plugins/kxm/src/vnext-config.ts +38 -1
  39. package/plugins/kxm/src/vnext-engine-command.ts +2 -0
  40. package/plugins/kxm/src/vnext-engine.ts +16 -0
  41. package/plugins/kxm/src/vnext-harness.ts +4 -2
  42. package/plugins/kxm/src/vnext-oneshot-process.ts +21 -4
  43. package/plugins/kxm/src/vnext-oneshot-producer.ts +18 -0
  44. package/plugins/kxm/src/vnext-runtime-store.ts +1 -1
  45. package/plugins/kxm/src/workflow-tui.ts +1 -1
  46. package/plugins/kxm/src/workflow.ts +144 -0
  47. package/scripts/kxm-bump-version.mjs +146 -0
  48. package/scripts/kxm-hub.mjs +145 -3
  49. package/scripts/kxm-publish-npm.mjs +3 -1
  50. package/scripts/kxm-release-github.mjs +3 -1
  51. package/scripts/kxm.mjs +0 -0
@@ -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
- import { DatabaseSync } from "node:sqlite";
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 DatabaseSync2(dbPath, { readOnly: true });
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.0";
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
- try {
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
- import { DatabaseSync } from "node:sqlite";
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 DatabaseSync2(file);
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 DatabaseSync2(resolvedSource);
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 DatabaseSync2(resolvedTarget);
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 DatabaseSync2(resolvedBackup);
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 DatabaseSync2(resolvedTarget);
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
- import { DatabaseSync } from "node:sqlite";
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