@kontextmind/kxm 0.6.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 (136) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.kxm/agents/coordinator.yaml +9 -0
  3. package/.kxm/agents/critic-arch.yaml +13 -0
  4. package/.kxm/agents/critic-cli.yaml +13 -0
  5. package/.kxm/agents/implementer.yaml +13 -0
  6. package/.kxm/gates.yaml +8 -0
  7. package/.kxm/producers.yaml +22 -0
  8. package/.kxm/project.yaml +15 -0
  9. package/.kxm/roles/writer.yaml +7 -0
  10. package/.kxm/workflows/default.yaml +47 -0
  11. package/CHANGELOG.md +39 -7
  12. package/README.md +1 -0
  13. package/docs/README.md +4 -0
  14. package/docs/agent-skills.md +121 -0
  15. package/docs/architecture.md +1 -1
  16. package/docs/assignment-runner.md +21 -8
  17. package/docs/configuration.md +11 -2
  18. package/docs/getting-started.md +21 -0
  19. package/docs/kxm-handbook.md +3 -3
  20. package/docs/operations.md +24 -0
  21. package/docs/operator-pi-packages.md +63 -0
  22. package/docs/skills/repo-work-delivery.md +107 -0
  23. package/docs/skills.md +2 -0
  24. package/docs/test-matrix.md +4 -3
  25. package/docs/troubleshooting.md +41 -1
  26. package/docs/vnext/validation.md +9 -0
  27. package/docs/webhook-workflows.md +2 -2
  28. package/examples/README.md +1 -1
  29. package/package.json +16 -17
  30. package/plugins/kxm/.claude-plugin/plugin.json +1 -1
  31. package/plugins/kxm/README.md +1 -1
  32. package/plugins/kxm/dist/cli.js +8955 -3934
  33. package/plugins/kxm/dist/core.js +271 -34
  34. package/plugins/kxm/dist/extension.js +7759 -86
  35. package/plugins/kxm/dist/mcp-server.js +75 -21
  36. package/plugins/kxm/dist/runtime.js +5637 -1125
  37. package/plugins/kxm/dist/server.js +3125 -2260
  38. package/plugins/kxm/dist/vnext-runtime-supervisor.js +5808 -565
  39. package/plugins/kxm/package.json +1 -1
  40. package/plugins/kxm/skills/SUITE.md +5 -0
  41. package/plugins/kxm/skills/hints.json +73 -0
  42. package/plugins/kxm/skills/kxm/SKILL.md +30 -83
  43. package/plugins/kxm/skills/kxm-context-memory/SKILL.md +69 -0
  44. package/plugins/kxm/skills/kxm-definitions/SKILL.md +65 -0
  45. package/plugins/kxm/skills/kxm-harness-auth/SKILL.md +34 -0
  46. package/plugins/kxm/skills/kxm-harvest/SKILL.md +48 -0
  47. package/plugins/kxm/skills/kxm-hub-ops/SKILL.md +43 -0
  48. package/plugins/kxm/skills/kxm-insights/SKILL.md +48 -0
  49. package/plugins/kxm/skills/kxm-mind/SKILL.md +59 -0
  50. package/plugins/kxm/skills/kxm-peer/SKILL.md +110 -0
  51. package/plugins/kxm/skills/kxm-project-setup/SKILL.md +42 -0
  52. package/plugins/kxm/skills/kxm-projects/SKILL.md +43 -0
  53. package/plugins/kxm/skills/kxm-protocol/SKILL.md +66 -0
  54. package/plugins/kxm/skills/kxm-query/SKILL.md +45 -0
  55. package/plugins/kxm/skills/kxm-routing-improve/SKILL.md +30 -0
  56. package/plugins/kxm/skills/kxm-runs/SKILL.md +29 -0
  57. package/plugins/kxm/skills/kxm-setup/SKILL.md +55 -0
  58. package/plugins/kxm/skills/kxm-skill-lifecycle/SKILL.md +31 -0
  59. package/plugins/kxm/skills/kxm-tasks/SKILL.md +33 -0
  60. package/plugins/kxm/skills/kxm-triage/SKILL.md +47 -0
  61. package/plugins/kxm/skills/kxm-work/SKILL.md +44 -0
  62. package/plugins/kxm/skills/kxm-workflow/SKILL.md +45 -0
  63. package/plugins/kxm/src/autocomplete.ts +9 -3
  64. package/plugins/kxm/src/cli.ts +1637 -73
  65. package/plugins/kxm/src/commands.ts +150 -8
  66. package/plugins/kxm/src/completion-install.ts +223 -0
  67. package/plugins/kxm/src/config.ts +7 -4
  68. package/plugins/kxm/src/context-packet.ts +172 -0
  69. package/plugins/kxm/src/database.ts +1 -1
  70. package/plugins/kxm/src/extension.ts +36 -1
  71. package/plugins/kxm/src/external-effects.ts +357 -8
  72. package/plugins/kxm/src/hub-env.ts +193 -0
  73. package/plugins/kxm/src/hub.ts +2 -4
  74. package/plugins/kxm/src/improve.ts +72 -0
  75. package/plugins/kxm/src/init-guide-setup.ts +547 -0
  76. package/plugins/kxm/src/local-snapshot.ts +1 -1
  77. package/plugins/kxm/src/mcp-server.ts +1 -1
  78. package/plugins/kxm/src/model-inventory.ts +127 -0
  79. package/plugins/kxm/src/policy-draft.d.mts +55 -0
  80. package/plugins/kxm/src/policy-draft.mjs +565 -0
  81. package/plugins/kxm/src/price-calc.ts +17 -18
  82. package/plugins/kxm/src/prices.ts +32 -16
  83. package/plugins/kxm/src/producers.ts +71 -0
  84. package/plugins/kxm/src/protocol.ts +111 -0
  85. package/plugins/kxm/src/restricted-yaml.d.mts +31 -0
  86. package/plugins/kxm/src/restricted-yaml.mjs +145 -0
  87. package/plugins/kxm/src/role.ts +710 -0
  88. package/plugins/kxm/src/routing.ts +99 -1
  89. package/plugins/kxm/src/safety-integrity.ts +76 -0
  90. package/plugins/kxm/src/session-work.ts +9 -2
  91. package/plugins/kxm/src/sqlite.ts +76 -0
  92. package/plugins/kxm/src/store.ts +1 -1
  93. package/plugins/kxm/src/studio-layout.ts +660 -17
  94. package/plugins/kxm/src/suggest.ts +7 -13
  95. package/plugins/kxm/src/telemetry.ts +82 -0
  96. package/plugins/kxm/src/tui.ts +140 -0
  97. package/plugins/kxm/src/vnext-bindings.ts +1 -1
  98. package/plugins/kxm/src/vnext-config.ts +53 -111
  99. package/plugins/kxm/src/vnext-engine-command.ts +2 -0
  100. package/plugins/kxm/src/vnext-engine.ts +214 -62
  101. package/plugins/kxm/src/vnext-harness.ts +267 -83
  102. package/plugins/kxm/src/vnext-oneshot-evidence.ts +85 -0
  103. package/plugins/kxm/src/vnext-oneshot-process.ts +166 -0
  104. package/plugins/kxm/src/vnext-oneshot-producer.ts +179 -224
  105. package/plugins/kxm/src/vnext-runtime-store.ts +36 -2
  106. package/plugins/kxm/src/vnext-runtime-supervisor.ts +120 -5
  107. package/plugins/kxm/src/vnext-runtime.ts +14 -0
  108. package/plugins/kxm/src/workflow-manager.ts +392 -0
  109. package/plugins/kxm/src/workflow-tui.ts +255 -0
  110. package/plugins/kxm/src/workflow.ts +144 -0
  111. package/schemas/policy-draft/README.md +17 -0
  112. package/schemas/policy-draft/model.v2.schema.json +140 -0
  113. package/schemas/policy-draft/role.v2.schema.json +91 -0
  114. package/schemas/vnext/role.schema.json +76 -0
  115. package/schemas/vnext/run-event.schema.json +1 -0
  116. package/scripts/assignment-run.d.mts +1 -1
  117. package/scripts/assignment-run.mjs +44 -35
  118. package/scripts/check-generated.mjs +33 -9
  119. package/scripts/emit-codex-artifacts.mjs +255 -11
  120. package/scripts/harness-run.d.mts +12 -4
  121. package/scripts/harness-run.mjs +65 -17
  122. package/scripts/kxm-bump-version.mjs +146 -0
  123. package/scripts/kxm-hub.mjs +150 -2
  124. package/scripts/kxm-publish-npm.mjs +3 -1
  125. package/scripts/kxm-release-github.mjs +3 -1
  126. package/scripts/kxm.mjs +0 -0
  127. package/scripts/native-critic.d.mts +5 -0
  128. package/scripts/native-critic.mjs +60 -0
  129. package/.kxm/config/README.md +0 -5
  130. package/.kxm/config/agents.json +0 -43
  131. package/.kxm/config/env.example +0 -56
  132. package/.kxm/config/update.example.yaml +0 -9
  133. package/.kxm/config/workflows/fix.json +0 -160
  134. package/.kxm/config/workflows/jira-development.json +0 -116
  135. package/.kxm/config/workflows/provenance-quorum.json +0 -150
  136. package/.kxm/config/workflows/v04-dogfood.json +0 -72
@@ -532,9 +532,106 @@ export interface RoutingReport {
532
532
  export interface GenerateRoutingReportOptions {
533
533
  catalog?: PriceCatalog | undefined;
534
534
  includeEquivalentListCost?: boolean | undefined;
535
+ halfLifeDays?: number | undefined;
535
536
  now?: () => string;
536
537
  }
537
538
 
539
+ /**
540
+ * Calculates exponential decay weight for historical telemetry (Decision Q14).
541
+ * Weight w = 2^(-delta_t / halfLifeDays).
542
+ */
543
+ export function computeDecayedWeight(
544
+ recordedAt: string | number,
545
+ halfLifeDays: number = 14,
546
+ now: number = Date.now(),
547
+ ): number {
548
+ const ts = typeof recordedAt === "number" ? recordedAt : Date.parse(recordedAt);
549
+ if (!Number.isFinite(ts)) return 1.0;
550
+ const deltaMs = Math.max(0, now - ts);
551
+ const halfLifeMs = halfLifeDays * 24 * 60 * 60 * 1000;
552
+ return Math.pow(2, -deltaMs / halfLifeMs);
553
+ }
554
+
555
+ export interface RouteCircuitStatus {
556
+ status: "healthy" | "demoted" | "quarantined";
557
+ consecutiveFailures: number;
558
+ penaltyMultiplier: number;
559
+ reason?: string | undefined;
560
+ }
561
+
562
+ /**
563
+ * Evaluates route health against the circuit breaker policy (Decision Q13).
564
+ * Detects failure streaks in the observation window and applies soft demotion or quarantine.
565
+ */
566
+ export function evaluateCircuitBreaker(
567
+ records: Array<RoutingRecord | RoutingRecordV2>,
568
+ route: { harness: string; model: string },
569
+ config?: {
570
+ mode?: "soft_demotion" | "quarantine" | undefined;
571
+ failureThreshold?: number | undefined;
572
+ windowSeconds?: number | undefined;
573
+ penaltyMultiplier?: number | undefined;
574
+ },
575
+ now: number = Date.now(),
576
+ ): RouteCircuitStatus {
577
+ const mode = config?.mode ?? "soft_demotion";
578
+ const failureThreshold = config?.failureThreshold ?? 3;
579
+ const windowSeconds = config?.windowSeconds ?? 3600;
580
+ const penaltyMultiplier = config?.penaltyMultiplier ?? 5.0;
581
+ const windowMs = windowSeconds * 1000;
582
+
583
+ const matching = records.filter((r) => {
584
+ const isV2 = r.schema === ROUTING_RECORD_V2_SCHEMA;
585
+ const harness = isV2 ? (r as RoutingRecordV2).harness : (r.providerMetadata?.harness as string | undefined);
586
+ const model = isV2
587
+ ? ((r as RoutingRecordV2).effectiveModel || (r as RoutingRecordV2).requestedModel)
588
+ : (r.effectiveModel || r.requestedModel);
589
+ if (harness !== route.harness || model !== route.model) return false;
590
+
591
+ const recAt = (r as any).recordedAt;
592
+ const ts = typeof recAt === "string" ? Date.parse(recAt) : 0;
593
+ return ts >= (now - windowMs);
594
+ });
595
+
596
+ matching.sort((a, b) => {
597
+ const ta = typeof (a as any).recordedAt === "string" ? Date.parse((a as any).recordedAt) : 0;
598
+ const tb = typeof (b as any).recordedAt === "string" ? Date.parse((b as any).recordedAt) : 0;
599
+ return tb - ta;
600
+ });
601
+
602
+ let consecutiveFailures = 0;
603
+ for (const r of matching) {
604
+ const passed = r.verifierOutcome === "passed" || r.finalOutcome === "accepted" || r.finalOutcome === "completed";
605
+ if (passed && (r.retries ?? 0) === 0) {
606
+ break;
607
+ }
608
+ consecutiveFailures++;
609
+ }
610
+
611
+ if (consecutiveFailures >= failureThreshold) {
612
+ if (mode === "quarantine") {
613
+ return {
614
+ status: "quarantined",
615
+ consecutiveFailures,
616
+ penaltyMultiplier,
617
+ reason: `Route quarantined after ${consecutiveFailures} consecutive failures within ${windowSeconds}s`,
618
+ };
619
+ }
620
+ return {
621
+ status: "demoted",
622
+ consecutiveFailures,
623
+ penaltyMultiplier,
624
+ reason: `Route demoted with ${penaltyMultiplier}x penalty after ${consecutiveFailures} consecutive failures`,
625
+ };
626
+ }
627
+
628
+ return {
629
+ status: "healthy",
630
+ consecutiveFailures,
631
+ penaltyMultiplier: 1.0,
632
+ };
633
+ }
634
+
538
635
  export function generateRoutingReport(
539
636
  records: Array<RoutingRecord | RoutingRecordV2>,
540
637
  options: GenerateRoutingReportOptions = {},
@@ -675,7 +772,8 @@ export function generateRoutingReport(
675
772
  tokensIn: r.tokensIn ?? null,
676
773
  tokensOut: r.tokensOut ?? null,
677
774
  cacheReadTokens: r.cacheReadTokens ?? null,
678
- contextTokens: (v2?.contextTokens ?? r.tokensIn) ?? null,
775
+ cacheWriteTokens: v2?.cacheWriteTokens ?? null,
776
+ contextTokens: v2?.contextTokens ?? null,
679
777
  });
680
778
  if (calc) {
681
779
  equivTotal += calc.costUsd;
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Safety, security, process integrity, and command seatbelts.
3
+ *
4
+ * Implements deterministic safety invariants:
5
+ * - Literal blocklist intercepting destructive shell mutations (rm -rf, git reset --hard, etc.)
6
+ * - Unbypassable raw byte fidelity brake for verification gates and critic reviews (bypassing lossy compression)
7
+ * - Pinned SSH host key verification policy for headless remote execution
8
+ */
9
+
10
+ export const DESTRUCTIVE_COMMAND_PATTERNS: readonly RegExp[] = Object.freeze([
11
+ /^\s*rm\s+.*-[a-zA-Z]*r[a-zA-Z]*f/i,
12
+ /^\s*rm\s+.*-[a-zA-Z]*f[a-zA-Z]*r/i,
13
+ /^\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,
14
+ /^\s*git\s+reset\s+--hard/i,
15
+ /^\s*git\s+clean\s+-[a-zA-Z]*f/i,
16
+ /^\s*git\s+checkout\s+--\s+/i,
17
+ /^\s*git\s+restore\s+(\.|\*|--staged\s+(\.|\*))/i,
18
+ ]);
19
+
20
+ /**
21
+ * Literal command seatbelt: blocks destructive mutations from autonomous agents.
22
+ * Throws a descriptive error if command matches any destructive pattern.
23
+ */
24
+ export function assertCommandSeatbelt(command: string): void {
25
+ for (const pattern of DESTRUCTIVE_COMMAND_PATTERNS) {
26
+ if (pattern.test(command)) {
27
+ throw new Error(
28
+ `Command blocked by KXM safety seatbelt: "${command}". Destructive workspace mutations require explicit operator override.`,
29
+ );
30
+ }
31
+ }
32
+ }
33
+
34
+ export interface RtkBypassContext {
35
+ stepKind?: string | undefined;
36
+ agentRole?: string | undefined;
37
+ preset?: string | undefined;
38
+ }
39
+
40
+ const CRITIC_PRESETS = new Set(["critic-arch", "critic-cli", "auditor"]);
41
+ const CRITIC_ROLES = new Set(["critic-arch", "critic-cli", "auditor", "reviewer-arch", "reviewer-cli"]);
42
+
43
+ /**
44
+ * Gate and Critic RTK Brake:
45
+ * Hardcode an unbypassable check ensuring lossy command output compression (RTK)
46
+ * is strictly prohibited for:
47
+ * 1. Verification gates (stepKind === "gate")
48
+ * 2. Critic and auditor agents (critic-arch, critic-cli, auditor)
49
+ */
50
+ export function isRtkBypassRequired(context: RtkBypassContext): boolean {
51
+ if (context.stepKind === "gate") return true;
52
+ if (context.agentRole && CRITIC_ROLES.has(context.agentRole)) return true;
53
+ if (context.preset && CRITIC_PRESETS.has(context.preset)) return true;
54
+ return false;
55
+ }
56
+
57
+ const INSECURE_SSH_HOST_KEY_PATTERNS: readonly RegExp[] = Object.freeze([
58
+ /StrictHostKeyChecking=(accept-new|no|off)/i,
59
+ /UserKnownHostsFile=\/dev\/null/i,
60
+ ]);
61
+
62
+ /**
63
+ * Pinned host key verification policy for headless SSH remote execution.
64
+ * Rejects TOFU (accept-new) and disabled host key checking.
65
+ */
66
+ export function assertPinnedSshHostKeyPolicy(sshArgs: readonly string[]): void {
67
+ for (const arg of sshArgs) {
68
+ for (const pattern of INSECURE_SSH_HOST_KEY_PATTERNS) {
69
+ if (pattern.test(arg)) {
70
+ throw new Error(
71
+ `Insecure SSH host key policy rejected: "${arg}". KXM headless SSH requires pinned host keys with StrictHostKeyChecking=yes and BatchMode=yes.`,
72
+ );
73
+ }
74
+ }
75
+ }
76
+ }
@@ -553,13 +553,20 @@ export function sessionBriefPickerEnabled(input: { env?: NodeJS.ProcessEnv; mode
553
553
  return reason === "startup" || reason === "new" || reason === "fork";
554
554
  }
555
555
 
556
- export const KXM_SLASH_SUBCOMMANDS = ["status", "hub", "memory", "help"] as const;
556
+ export const KXM_SLASH_SUBCOMMANDS = ["status", "hub", "memory", "progress", "workflow", "help"] as const;
557
557
  export type KxmSlashCommand = "brief" | typeof KXM_SLASH_SUBCOMMANDS[number];
558
558
 
559
559
  export function parseKxmSlashArgs(args: string | undefined): KxmSlashCommand {
560
560
  const raw = String(args ?? "").trim().toLowerCase();
561
561
  if (raw === "" || raw === "brief") return "brief";
562
- if (raw === "status" || raw === "hub" || raw === "memory" || raw === "help") return raw;
562
+ if (
563
+ raw === "status"
564
+ || raw === "hub"
565
+ || raw === "memory"
566
+ || raw === "progress"
567
+ || raw === "workflow"
568
+ || raw === "help"
569
+ ) return raw;
563
570
  return "help";
564
571
  }
565
572
 
@@ -0,0 +1,76 @@
1
+ /** Runtime-portable SQLite constructor.
2
+ *
3
+ * Prefers Node's built-in `node:sqlite` (the supported runtime for the kxm
4
+ * CLI). Pi loads extensions inside its embedded Bun runtime, which does not
5
+ * provide `node:sqlite`, so fall back to Bun's `bun:sqlite` there.
6
+ *
7
+ * The two runtimes differ in one option this codebase uses: Node spells the
8
+ * flag `readOnly`, Bun spells it `readonly`. Options are normalized here so
9
+ * callers keep using the Node spelling everywhere.
10
+ */
11
+ import { createRequire } from "node:module";
12
+
13
+ export interface DatabaseSyncOptions {
14
+ readOnly?: boolean;
15
+ [key: string]: unknown;
16
+ }
17
+
18
+ export interface StatementSync {
19
+ run(...params: unknown[]): { changes: number | bigint; lastInsertRowid: number | bigint };
20
+ get(...params: unknown[]): Record<string, unknown> | undefined;
21
+ all(...params: unknown[]): Array<Record<string, unknown>>;
22
+ }
23
+
24
+ type NativeDatabase = {
25
+ prepare(sql: string): StatementSync;
26
+ exec(sql: string): unknown;
27
+ close(): void;
28
+ };
29
+
30
+ const requireFromHere = createRequire(import.meta.url);
31
+
32
+ function loadNative(): { Ctor: new (path: string, options?: object) => NativeDatabase; bun: boolean } {
33
+ try {
34
+ const mod = requireFromHere("node:sqlite") as { DatabaseSync?: new (path: string, options?: object) => NativeDatabase };
35
+ if (mod.DatabaseSync) return { Ctor: mod.DatabaseSync, bun: false };
36
+ } catch {
37
+ // Not a Node runtime exposing node:sqlite (e.g. pi's embedded Bun).
38
+ }
39
+ try {
40
+ const mod = requireFromHere("bun:sqlite") as
41
+ | { DatabaseSync?: new (path: string, options?: object) => NativeDatabase; Database?: new (path: string, options?: object) => NativeDatabase }
42
+ | undefined;
43
+ const Ctor = mod?.DatabaseSync ?? mod?.Database;
44
+ if (Ctor) return { Ctor, bun: true };
45
+ } catch {
46
+ // No bun:sqlite either.
47
+ }
48
+ throw new Error("kxm: no supported sqlite module found (need node:sqlite or bun:sqlite)");
49
+ }
50
+
51
+ const native = loadNative();
52
+
53
+ export class DatabaseSync {
54
+ private readonly inner: NativeDatabase;
55
+
56
+ constructor(path: string, options?: DatabaseSyncOptions) {
57
+ let normalized: object | undefined = options;
58
+ if (native.bun && options) {
59
+ const { readOnly, ...rest } = options;
60
+ normalized = readOnly === undefined ? rest : { ...rest, readonly: readOnly };
61
+ }
62
+ this.inner = normalized === undefined ? new native.Ctor(path) : new native.Ctor(path, normalized);
63
+ }
64
+
65
+ prepare(sql: string): StatementSync {
66
+ return this.inner.prepare(sql);
67
+ }
68
+
69
+ exec(sql: string): unknown {
70
+ return this.inner.exec(sql);
71
+ }
72
+
73
+ close(): void {
74
+ this.inner.close();
75
+ }
76
+ }
@@ -1,5 +1,5 @@
1
1
  import { resolve } from "node:path";
2
- import type { DatabaseSync } from "node:sqlite";
2
+ import type { DatabaseSync } from "./sqlite.ts";
3
3
  import type { AgentRecord, MessageRecord } from "./protocol.ts";
4
4
  import type { ContextItem } from "./context.ts";
5
5
  import type { WorkflowJournalEntry, WorkflowRun } from "./workflow.ts";