@kontextmind/kxm 0.6.0 → 0.7.0

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 (117) 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 +5 -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 +2 -0
  14. package/docs/agent-skills.md +118 -0
  15. package/docs/architecture.md +1 -1
  16. package/docs/assignment-runner.md +21 -8
  17. package/docs/configuration.md +2 -2
  18. package/docs/kxm-handbook.md +3 -3
  19. package/docs/operator-pi-packages.md +63 -0
  20. package/docs/skills/repo-work-delivery.md +102 -0
  21. package/docs/test-matrix.md +4 -3
  22. package/docs/troubleshooting.md +19 -0
  23. package/docs/vnext/validation.md +9 -0
  24. package/docs/webhook-workflows.md +2 -2
  25. package/examples/README.md +1 -1
  26. package/package.json +16 -17
  27. package/plugins/kxm/.claude-plugin/plugin.json +1 -1
  28. package/plugins/kxm/README.md +1 -1
  29. package/plugins/kxm/dist/cli.js +6875 -3672
  30. package/plugins/kxm/dist/core.js +214 -34
  31. package/plugins/kxm/dist/extension.js +7721 -85
  32. package/plugins/kxm/dist/mcp-server.js +75 -21
  33. package/plugins/kxm/dist/runtime.js +5403 -1014
  34. package/plugins/kxm/dist/server.js +3008 -2268
  35. package/plugins/kxm/dist/vnext-runtime-supervisor.js +5751 -631
  36. package/plugins/kxm/package.json +1 -1
  37. package/plugins/kxm/skills/SUITE.md +5 -0
  38. package/plugins/kxm/skills/hints.json +73 -0
  39. package/plugins/kxm/skills/kxm/SKILL.md +30 -83
  40. package/plugins/kxm/skills/kxm-context-memory/SKILL.md +69 -0
  41. package/plugins/kxm/skills/kxm-definitions/SKILL.md +65 -0
  42. package/plugins/kxm/skills/kxm-harness-auth/SKILL.md +34 -0
  43. package/plugins/kxm/skills/kxm-harvest/SKILL.md +48 -0
  44. package/plugins/kxm/skills/kxm-hub-ops/SKILL.md +34 -0
  45. package/plugins/kxm/skills/kxm-insights/SKILL.md +48 -0
  46. package/plugins/kxm/skills/kxm-mind/SKILL.md +59 -0
  47. package/plugins/kxm/skills/kxm-peer/SKILL.md +110 -0
  48. package/plugins/kxm/skills/kxm-project-setup/SKILL.md +35 -0
  49. package/plugins/kxm/skills/kxm-projects/SKILL.md +43 -0
  50. package/plugins/kxm/skills/kxm-protocol/SKILL.md +66 -0
  51. package/plugins/kxm/skills/kxm-query/SKILL.md +45 -0
  52. package/plugins/kxm/skills/kxm-routing-improve/SKILL.md +30 -0
  53. package/plugins/kxm/skills/kxm-runs/SKILL.md +29 -0
  54. package/plugins/kxm/skills/kxm-setup/SKILL.md +55 -0
  55. package/plugins/kxm/skills/kxm-skill-lifecycle/SKILL.md +31 -0
  56. package/plugins/kxm/skills/kxm-tasks/SKILL.md +33 -0
  57. package/plugins/kxm/skills/kxm-triage/SKILL.md +47 -0
  58. package/plugins/kxm/skills/kxm-work/SKILL.md +44 -0
  59. package/plugins/kxm/skills/kxm-workflow/SKILL.md +45 -0
  60. package/plugins/kxm/src/autocomplete.ts +9 -3
  61. package/plugins/kxm/src/cli.ts +1077 -11
  62. package/plugins/kxm/src/commands.ts +150 -8
  63. package/plugins/kxm/src/config.ts +7 -4
  64. package/plugins/kxm/src/context-packet.ts +172 -0
  65. package/plugins/kxm/src/extension.ts +36 -1
  66. package/plugins/kxm/src/external-effects.ts +356 -7
  67. package/plugins/kxm/src/hub.ts +2 -4
  68. package/plugins/kxm/src/improve.ts +72 -0
  69. package/plugins/kxm/src/mcp-server.ts +1 -1
  70. package/plugins/kxm/src/model-inventory.ts +127 -0
  71. package/plugins/kxm/src/policy-draft.d.mts +55 -0
  72. package/plugins/kxm/src/policy-draft.mjs +565 -0
  73. package/plugins/kxm/src/price-calc.ts +17 -18
  74. package/plugins/kxm/src/prices.ts +32 -16
  75. package/plugins/kxm/src/producers.ts +71 -0
  76. package/plugins/kxm/src/restricted-yaml.d.mts +31 -0
  77. package/plugins/kxm/src/restricted-yaml.mjs +145 -0
  78. package/plugins/kxm/src/role.ts +375 -0
  79. package/plugins/kxm/src/routing.ts +99 -1
  80. package/plugins/kxm/src/session-work.ts +9 -2
  81. package/plugins/kxm/src/studio-layout.ts +660 -17
  82. package/plugins/kxm/src/suggest.ts +7 -13
  83. package/plugins/kxm/src/telemetry.ts +82 -0
  84. package/plugins/kxm/src/tui.ts +140 -0
  85. package/plugins/kxm/src/vnext-config.ts +15 -110
  86. package/plugins/kxm/src/vnext-engine.ts +198 -62
  87. package/plugins/kxm/src/vnext-harness.ts +263 -81
  88. package/plugins/kxm/src/vnext-oneshot-evidence.ts +85 -0
  89. package/plugins/kxm/src/vnext-oneshot-process.ts +149 -0
  90. package/plugins/kxm/src/vnext-oneshot-producer.ts +170 -233
  91. package/plugins/kxm/src/vnext-runtime-store.ts +35 -1
  92. package/plugins/kxm/src/vnext-runtime-supervisor.ts +120 -5
  93. package/plugins/kxm/src/vnext-runtime.ts +14 -0
  94. package/plugins/kxm/src/workflow-manager.ts +392 -0
  95. package/plugins/kxm/src/workflow-tui.ts +255 -0
  96. package/schemas/policy-draft/README.md +17 -0
  97. package/schemas/policy-draft/model.v2.schema.json +140 -0
  98. package/schemas/policy-draft/role.v2.schema.json +91 -0
  99. package/schemas/vnext/role.schema.json +76 -0
  100. package/schemas/vnext/run-event.schema.json +1 -0
  101. package/scripts/assignment-run.d.mts +1 -1
  102. package/scripts/assignment-run.mjs +44 -35
  103. package/scripts/check-generated.mjs +33 -9
  104. package/scripts/emit-codex-artifacts.mjs +255 -11
  105. package/scripts/harness-run.d.mts +12 -4
  106. package/scripts/harness-run.mjs +65 -17
  107. package/scripts/kxm-hub.mjs +6 -0
  108. package/scripts/native-critic.d.mts +5 -0
  109. package/scripts/native-critic.mjs +60 -0
  110. package/.kxm/config/README.md +0 -5
  111. package/.kxm/config/agents.json +0 -43
  112. package/.kxm/config/env.example +0 -56
  113. package/.kxm/config/update.example.yaml +0 -9
  114. package/.kxm/config/workflows/fix.json +0 -160
  115. package/.kxm/config/workflows/jira-development.json +0 -116
  116. package/.kxm/config/workflows/provenance-quorum.json +0 -150
  117. package/.kxm/config/workflows/v04-dogfood.json +0 -72
@@ -68,10 +68,7 @@ function findModelPrice(catalog, model, provider) {
68
68
  const normalizedModel = model.trim().toLowerCase();
69
69
  const normalizedProvider = provider?.trim().toLowerCase();
70
70
  for (const entry of catalog.models) {
71
- if (normalizedProvider && entry.provider.toLowerCase() !== normalizedProvider) {
72
- const matchesAlias = entry.aliases?.some((a) => a.toLowerCase() === normalizedModel);
73
- if (!matchesAlias) continue;
74
- }
71
+ if (normalizedProvider && entry.provider.toLowerCase() !== normalizedProvider) continue;
75
72
  if (entry.id.toLowerCase() === normalizedModel || entry.model.toLowerCase() === normalizedModel) {
76
73
  return entry;
77
74
  }
@@ -84,20 +81,19 @@ function findModelPrice(catalog, model, provider) {
84
81
  function calculateModelCost(catalog, params) {
85
82
  const row = findModelPrice(catalog, params.model, params.provider);
86
83
  if (!row || row.tiers.length === 0) return void 0;
87
- const context = params.contextTokens ?? params.tokensIn ?? 0;
88
- let selectedTier = row.tiers[0];
89
- for (const tier of row.tiers) {
90
- if (tier.upToContextTokens !== void 0 && tier.upToContextTokens !== null && context > tier.upToContextTokens) {
91
- continue;
92
- }
93
- selectedTier = tier;
94
- break;
95
- }
96
- const tokensIn = params.tokensIn ?? 0;
97
- const tokensOut = params.tokensOut ?? 0;
98
- const cacheRead = params.cacheReadTokens ?? 0;
99
- const cacheWrite = params.cacheWriteTokens ?? 0;
84
+ if (catalog.currency !== void 0 && catalog.currency !== "USD") return void 0;
85
+ const validCount = (n) => typeof n === "number" && Number.isSafeInteger(n) && n >= 0;
86
+ const tokensIn = params.tokensIn;
87
+ const tokensOut = params.tokensOut;
88
+ const cacheRead = params.cacheReadTokens;
89
+ const cacheWrite = params.cacheWriteTokens;
90
+ if (!validCount(tokensIn) || !validCount(tokensOut) || !validCount(cacheRead) || !validCount(cacheWrite)) return void 0;
91
+ const context = params.contextTokens;
92
+ if (context != null && !validCount(context)) return void 0;
93
+ const selectedTier = context == null ? row.tiers.length === 1 && row.tiers[0].upToContextTokens == null ? row.tiers[0] : void 0 : row.tiers.find((tier) => tier.upToContextTokens == null || context <= tier.upToContextTokens);
94
+ if (!selectedTier || cacheRead > 0 && selectedTier.cacheReadPerMillion == null || cacheWrite > 0 && selectedTier.cacheWritePerMillion == null) return void 0;
100
95
  const cost = tokensIn / 1e6 * selectedTier.inputPerMillion + tokensOut / 1e6 * selectedTier.outputPerMillion + cacheRead / 1e6 * (selectedTier.cacheReadPerMillion ?? 0) + cacheWrite / 1e6 * (selectedTier.cacheWritePerMillion ?? 0);
96
+ if (!Number.isFinite(cost) || cost < 0) return void 0;
101
97
  const priceRef = `${catalog.date}#${row.id}`;
102
98
  return {
103
99
  costUsd: Math.round(cost * 1e6) / 1e6,
@@ -435,6 +431,63 @@ function computePercentile(sorted, p) {
435
431
  }
436
432
  return sorted[base];
437
433
  }
434
+ function computeDecayedWeight(recordedAt, halfLifeDays = 14, now = Date.now()) {
435
+ const ts = typeof recordedAt === "number" ? recordedAt : Date.parse(recordedAt);
436
+ if (!Number.isFinite(ts)) return 1;
437
+ const deltaMs = Math.max(0, now - ts);
438
+ const halfLifeMs = halfLifeDays * 24 * 60 * 60 * 1e3;
439
+ return Math.pow(2, -deltaMs / halfLifeMs);
440
+ }
441
+ function evaluateCircuitBreaker(records, route, config, now = Date.now()) {
442
+ const mode = config?.mode ?? "soft_demotion";
443
+ const failureThreshold = config?.failureThreshold ?? 3;
444
+ const windowSeconds = config?.windowSeconds ?? 3600;
445
+ const penaltyMultiplier = config?.penaltyMultiplier ?? 5;
446
+ const windowMs = windowSeconds * 1e3;
447
+ const matching = records.filter((r) => {
448
+ const isV2 = r.schema === ROUTING_RECORD_V2_SCHEMA;
449
+ const harness = isV2 ? r.harness : r.providerMetadata?.harness;
450
+ const model = isV2 ? r.effectiveModel || r.requestedModel : r.effectiveModel || r.requestedModel;
451
+ if (harness !== route.harness || model !== route.model) return false;
452
+ const recAt = r.recordedAt;
453
+ const ts = typeof recAt === "string" ? Date.parse(recAt) : 0;
454
+ return ts >= now - windowMs;
455
+ });
456
+ matching.sort((a, b) => {
457
+ const ta = typeof a.recordedAt === "string" ? Date.parse(a.recordedAt) : 0;
458
+ const tb = typeof b.recordedAt === "string" ? Date.parse(b.recordedAt) : 0;
459
+ return tb - ta;
460
+ });
461
+ let consecutiveFailures = 0;
462
+ for (const r of matching) {
463
+ const passed = r.verifierOutcome === "passed" || r.finalOutcome === "accepted" || r.finalOutcome === "completed";
464
+ if (passed && (r.retries ?? 0) === 0) {
465
+ break;
466
+ }
467
+ consecutiveFailures++;
468
+ }
469
+ if (consecutiveFailures >= failureThreshold) {
470
+ if (mode === "quarantine") {
471
+ return {
472
+ status: "quarantined",
473
+ consecutiveFailures,
474
+ penaltyMultiplier,
475
+ reason: `Route quarantined after ${consecutiveFailures} consecutive failures within ${windowSeconds}s`
476
+ };
477
+ }
478
+ return {
479
+ status: "demoted",
480
+ consecutiveFailures,
481
+ penaltyMultiplier,
482
+ reason: `Route demoted with ${penaltyMultiplier}x penalty after ${consecutiveFailures} consecutive failures`
483
+ };
484
+ }
485
+ return {
486
+ status: "healthy",
487
+ consecutiveFailures,
488
+ penaltyMultiplier: 1
489
+ };
490
+ }
438
491
  function generateRoutingReport(records, options = {}) {
439
492
  const generatedAt = options.now ? options.now() : (/* @__PURE__ */ new Date()).toISOString();
440
493
  if (records.length === 0) {
@@ -538,7 +591,8 @@ function generateRoutingReport(records, options = {}) {
538
591
  tokensIn: r.tokensIn ?? null,
539
592
  tokensOut: r.tokensOut ?? null,
540
593
  cacheReadTokens: r.cacheReadTokens ?? null,
541
- contextTokens: v2?.contextTokens ?? r.tokensIn ?? null
594
+ cacheWriteTokens: v2?.cacheWriteTokens ?? null,
595
+ contextTokens: v2?.contextTokens ?? null
542
596
  });
543
597
  if (calc) {
544
598
  equivTotal += calc.costUsd;
@@ -742,7 +796,10 @@ function redactStringList(values, maxItems = 32) {
742
796
  }
743
797
 
744
798
  // plugins/kxm/src/commands.ts
745
- import { randomUUID as randomUUID2 } from "node:crypto";
799
+ import { createHash as createHash2, randomUUID as randomUUID2, timingSafeEqual } from "node:crypto";
800
+ import { chmodSync, existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from "node:fs";
801
+ import { homedir } from "node:os";
802
+ import { dirname, join, resolve } from "node:path";
746
803
 
747
804
  // plugins/kxm/src/client.ts
748
805
  var HubHttpError = class extends Error {
@@ -1508,26 +1565,60 @@ function parseAttemptToken(token) {
1508
1565
  }
1509
1566
  return void 0;
1510
1567
  }
1568
+ function timingSafeStringCompare(a, b) {
1569
+ if (typeof a !== "string" || typeof b !== "string") return false;
1570
+ const hashA = createHash2("sha256").update(a).digest();
1571
+ const hashB = createHash2("sha256").update(b).digest();
1572
+ return timingSafeEqual(hashA, hashB);
1573
+ }
1511
1574
  function mintSessionToken(input) {
1512
1575
  const toolPolicy = input?.toolPolicy ?? (input?.allowedTools || input?.deniedTools || input?.preset ? {
1513
1576
  ...input?.preset ? { preset: input.preset } : {},
1514
- ...input?.allowedTools ? { allow: input.allowedTools } : {},
1515
- ...input?.deniedTools ? { deny: input.deniedTools } : {}
1577
+ ...input?.allowedTools ? { allow: input.allowedTools, allowedTools: input.allowedTools } : {},
1578
+ ...input?.deniedTools ? { deny: input.deniedTools, deniedTools: input.deniedTools } : {}
1516
1579
  } : void 0);
1580
+ const issuedAt = (/* @__PURE__ */ new Date()).toISOString();
1581
+ const defaultTtlMs = 24 * 60 * 60 * 1e3;
1582
+ const ttlMs = typeof input?.ttlMs === "number" && input.ttlMs > 0 ? input.ttlMs : defaultTtlMs;
1583
+ const expiresAt = input?.expiresAt ?? new Date(Date.now() + ttlMs).toISOString();
1517
1584
  const payload = {
1518
1585
  schema: "kxm.session-token.v1",
1519
1586
  sessionId: input?.sessionId ?? `session-${randomUUID2()}`,
1520
- issuedAt: (/* @__PURE__ */ new Date()).toISOString(),
1587
+ issuedAt,
1588
+ expiresAt,
1521
1589
  ...input?.agentName ? { agentName: input.agentName } : {},
1522
1590
  ...toolPolicy ? { toolPolicy } : {}
1523
1591
  };
1524
1592
  return Buffer.from(JSON.stringify(payload)).toString("base64url");
1525
1593
  }
1594
+ function isSessionTokenExpired(payloadOrToken) {
1595
+ let payload;
1596
+ if (typeof payloadOrToken === "string") {
1597
+ try {
1598
+ const raw = Buffer.from(payloadOrToken.trim(), "base64url").toString("utf8");
1599
+ payload = JSON.parse(raw);
1600
+ } catch {
1601
+ return true;
1602
+ }
1603
+ } else {
1604
+ payload = payloadOrToken;
1605
+ }
1606
+ if (!payload || !payload.expiresAt) return false;
1607
+ const expiryTime = new Date(payload.expiresAt).getTime();
1608
+ if (Number.isNaN(expiryTime)) return true;
1609
+ return Date.now() >= expiryTime;
1610
+ }
1526
1611
  function parseSessionToken(token) {
1527
1612
  try {
1528
1613
  const raw = Buffer.from(token.trim(), "base64url").toString("utf8");
1529
1614
  const parsed = JSON.parse(raw);
1530
1615
  if (parsed && typeof parsed === "object" && parsed.schema === "kxm.session-token.v1" && typeof parsed.sessionId === "string") {
1616
+ if (parsed.expiresAt) {
1617
+ const expiryTime = new Date(parsed.expiresAt).getTime();
1618
+ if (!Number.isNaN(expiryTime) && Date.now() >= expiryTime) {
1619
+ return void 0;
1620
+ }
1621
+ }
1531
1622
  return parsed;
1532
1623
  }
1533
1624
  } catch {
@@ -1535,6 +1626,49 @@ function parseSessionToken(token) {
1535
1626
  }
1536
1627
  return void 0;
1537
1628
  }
1629
+ function resolveUserConfigDirectory(overrideDir) {
1630
+ if (overrideDir) return resolve(overrideDir);
1631
+ return resolve(process.env.KXM_USER_CONFIG_DIR?.trim() || join(homedir(), ".config", "kxm"));
1632
+ }
1633
+ function sessionTokenPath(userConfigDir) {
1634
+ return join(resolveUserConfigDirectory(userConfigDir), "session.token");
1635
+ }
1636
+ function persistSessionTokenToDisk(token, options) {
1637
+ const filePath = sessionTokenPath(options?.userConfigDir);
1638
+ const dir = dirname(filePath);
1639
+ mkdirSync(dir, { recursive: true, mode: 448 });
1640
+ const mode = options?.mode ?? 384;
1641
+ writeFileSync(filePath, `${token.trim()}
1642
+ `, { encoding: "utf8", mode });
1643
+ try {
1644
+ chmodSync(filePath, mode);
1645
+ } catch {
1646
+ }
1647
+ return filePath;
1648
+ }
1649
+ function readSessionTokenFromDisk(options) {
1650
+ const filePath = sessionTokenPath(options?.userConfigDir);
1651
+ if (!existsSync(filePath)) return void 0;
1652
+ try {
1653
+ const token = readFileSync(filePath, "utf8").trim();
1654
+ if (!token) return void 0;
1655
+ const payload = parseSessionToken(token);
1656
+ if (!payload) return void 0;
1657
+ return { token, payload };
1658
+ } catch {
1659
+ return void 0;
1660
+ }
1661
+ }
1662
+ function clearSessionTokenFromDisk(options) {
1663
+ const filePath = sessionTokenPath(options?.userConfigDir);
1664
+ if (!existsSync(filePath)) return false;
1665
+ try {
1666
+ unlinkSync(filePath);
1667
+ return true;
1668
+ } catch {
1669
+ return false;
1670
+ }
1671
+ }
1538
1672
  function matchToolPattern(pattern, toolName) {
1539
1673
  if (pattern === "*" || pattern === toolName) return true;
1540
1674
  if (pattern.endsWith("*")) {
@@ -1577,7 +1711,7 @@ function isToolAllowed(commandName, policy) {
1577
1711
  }
1578
1712
  return true;
1579
1713
  }
1580
- function enforceToolPolicy(commandName, env = process.env) {
1714
+ function enforceToolPolicy(commandName, env = process.env, options) {
1581
1715
  const attemptTokenRaw = env.KXM_ATTEMPT_TOKEN?.trim();
1582
1716
  if (attemptTokenRaw) {
1583
1717
  const attempt = parseAttemptToken(attemptTokenRaw);
@@ -1591,13 +1725,51 @@ function enforceToolPolicy(commandName, env = process.env) {
1591
1725
  detail: `command ${commandName} is denied by attempt tool policy`
1592
1726
  };
1593
1727
  }
1728
+ if (commandName === "kxm_promote" || commandName === "promote") {
1729
+ const explicitAllow = attempt.toolPolicy?.allow ?? attempt.toolPolicy?.allowedTools;
1730
+ if (!Array.isArray(explicitAllow) || !explicitAllow.includes("kxm_promote") && !explicitAllow.includes("promote") && !explicitAllow.includes("*")) {
1731
+ return {
1732
+ allowed: false,
1733
+ error: "attempt_token_admin_denied",
1734
+ detail: "AttemptToken worker cannot perform operator state promotion without explicit policy grant"
1735
+ };
1736
+ }
1737
+ }
1738
+ if (options?.runId && attempt.runId && options.runId !== attempt.runId) {
1739
+ return {
1740
+ allowed: false,
1741
+ error: "attempt_token_scope_violation",
1742
+ detail: `attempt token runId ${attempt.runId} does not match request runId ${options.runId}`
1743
+ };
1744
+ }
1594
1745
  return { allowed: true };
1595
1746
  }
1596
- const sessionTokenRaw = env.KXM_SESSION_TOKEN?.trim();
1597
- if (sessionTokenRaw) {
1598
- const session = parseSessionToken(sessionTokenRaw);
1747
+ const envSessionRaw = env.KXM_SESSION_TOKEN?.trim();
1748
+ if (envSessionRaw) {
1749
+ const session = parseSessionToken(envSessionRaw);
1750
+ if (!session) {
1751
+ return { allowed: false, error: "session_token_invalid", detail: "KXM_SESSION_TOKEN is malformed or expired" };
1752
+ }
1753
+ if (!isToolAllowed(commandName, session.toolPolicy)) {
1754
+ return {
1755
+ allowed: false,
1756
+ error: "tool_policy_denied",
1757
+ detail: `command ${commandName} is denied by session tool policy`
1758
+ };
1759
+ }
1760
+ return { allowed: true };
1761
+ }
1762
+ const tokenFile = sessionTokenPath(env.KXM_USER_CONFIG_DIR);
1763
+ if (existsSync(tokenFile)) {
1764
+ let tokenRaw;
1765
+ try {
1766
+ tokenRaw = readFileSync(tokenFile, "utf8").trim();
1767
+ } catch {
1768
+ return { allowed: false, error: "session_token_invalid", detail: "Session token file on disk could not be read" };
1769
+ }
1770
+ const session = tokenRaw ? parseSessionToken(tokenRaw) : void 0;
1599
1771
  if (!session) {
1600
- return { allowed: false, error: "session_token_invalid", detail: "KXM_SESSION_TOKEN is malformed" };
1772
+ return { allowed: false, error: "session_token_invalid", detail: "Session token on disk is malformed or expired" };
1601
1773
  }
1602
1774
  if (!isToolAllowed(commandName, session.toolPolicy)) {
1603
1775
  return {
@@ -1612,8 +1784,8 @@ function enforceToolPolicy(commandName, env = process.env) {
1612
1784
  }
1613
1785
 
1614
1786
  // plugins/kxm/src/logger.ts
1615
- import { appendFileSync, existsSync, mkdirSync, renameSync, statSync, unlinkSync } from "node:fs";
1616
- import { dirname } from "node:path";
1787
+ import { appendFileSync, existsSync as existsSync2, mkdirSync as mkdirSync2, renameSync, statSync, unlinkSync as unlinkSync2 } from "node:fs";
1788
+ import { dirname as dirname2 } from "node:path";
1617
1789
  var LOG_LEVEL_PRIORITY = {
1618
1790
  debug: 10,
1619
1791
  info: 20,
@@ -1650,10 +1822,10 @@ function redactLogValue(val, key) {
1650
1822
  function rotateLogFiles(filePath, maxFiles) {
1651
1823
  for (let i = maxFiles; i >= 1; i--) {
1652
1824
  const current = `${filePath}.${i}`;
1653
- if (existsSync(current)) {
1825
+ if (existsSync2(current)) {
1654
1826
  if (i >= maxFiles) {
1655
1827
  try {
1656
- unlinkSync(current);
1828
+ unlinkSync2(current);
1657
1829
  } catch {
1658
1830
  }
1659
1831
  } else {
@@ -1664,7 +1836,7 @@ function rotateLogFiles(filePath, maxFiles) {
1664
1836
  }
1665
1837
  }
1666
1838
  }
1667
- if (existsSync(filePath)) {
1839
+ if (existsSync2(filePath)) {
1668
1840
  try {
1669
1841
  renameSync(filePath, `${filePath}.1`);
1670
1842
  } catch {
@@ -1681,7 +1853,7 @@ function createLogger(options) {
1681
1853
  const shouldStdout = options.stdout ?? !isDaemon;
1682
1854
  const correlationDefaults = options.correlation ?? {};
1683
1855
  let currentSize = 0;
1684
- if (filePath && existsSync(filePath)) {
1856
+ if (filePath && existsSync2(filePath)) {
1685
1857
  try {
1686
1858
  currentSize = statSync(filePath).size;
1687
1859
  } catch {
@@ -1719,7 +1891,7 @@ function createLogger(options) {
1719
1891
  currentSize = 0;
1720
1892
  }
1721
1893
  try {
1722
- mkdirSync(dirname(filePath), { recursive: true });
1894
+ mkdirSync2(dirname2(filePath), { recursive: true });
1723
1895
  appendFileSync(filePath, line, { encoding: "utf8", mode: 384 });
1724
1896
  currentSize += lineBytes;
1725
1897
  } catch {
@@ -1789,9 +1961,12 @@ export {
1789
1961
  WORKER_SCHEMA,
1790
1962
  agentWorker,
1791
1963
  behavioralConfigHash,
1964
+ clearSessionTokenFromDisk,
1792
1965
  compareRoutingRecords,
1966
+ computeDecayedWeight,
1793
1967
  createLogger,
1794
1968
  enforceToolPolicy,
1969
+ evaluateCircuitBreaker,
1795
1970
  formatRoutingReport,
1796
1971
  gateWorker,
1797
1972
  generateRoutingReport,
@@ -1800,6 +1975,7 @@ export {
1800
1975
  getPiToolDefinitions,
1801
1976
  groupByBehavior,
1802
1977
  isQuotaExhausted,
1978
+ isSessionTokenExpired,
1803
1979
  isToolAllowed,
1804
1980
  looksLikeSecret,
1805
1981
  mintAttemptToken,
@@ -1813,11 +1989,15 @@ export {
1813
1989
  parseRoutingRecord,
1814
1990
  parseRoutingRecordV2,
1815
1991
  parseSessionToken,
1992
+ persistSessionTokenToDisk,
1993
+ readSessionTokenFromDisk,
1816
1994
  reconcileInbox,
1817
1995
  redactLogValue,
1818
1996
  redactSecrets,
1819
1997
  redactStringList,
1820
1998
  requireString,
1821
1999
  rotateLogFiles,
2000
+ sessionTokenPath,
2001
+ timingSafeStringCompare,
1822
2002
  workerResult
1823
2003
  };