@kontextmind/kxm 0.7.38 → 0.7.40

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.
@@ -11,7 +11,7 @@
11
11
  "name": "kxm",
12
12
  "source": "./plugins/kxm",
13
13
  "description": "Durable workflows, peer agents, and kxm tui",
14
- "version": "0.7.38",
14
+ "version": "0.7.40",
15
15
  "category": "development",
16
16
  "tags": ["kxm", "multi-agent", "workflows", "mcp"]
17
17
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kontextmind/kxm",
3
- "version": "0.7.38",
3
+ "version": "0.7.40",
4
4
  "description": "KXM local-first multi-agent orchestration and operator dashboard",
5
5
  "type": "module",
6
6
  "author": "KontextMind",
@@ -2,7 +2,7 @@
2
2
  "$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json",
3
3
  "name": "kxm",
4
4
  "displayName": "KXM",
5
- "version": "0.7.38",
5
+ "version": "0.7.40",
6
6
  "description": "Headless multi-agent orchestration, durable workflows, and a live operator dashboard for Pi and Claude Code",
7
7
  "author": {
8
8
  "name": "KontextMind",
@@ -17121,7 +17121,7 @@ async function deliverInboxNotification(messageId, delivered, notify) {
17121
17121
  }
17122
17122
 
17123
17123
  // plugins/kxm/src/mcp-server.ts
17124
- var VERSION = "0.7.38";
17124
+ var VERSION = "0.7.40";
17125
17125
  var inbox = /* @__PURE__ */ new Map();
17126
17126
  var notifiedInbox = /* @__PURE__ */ new Set();
17127
17127
  var meshClient;
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kxm-claude-plugin",
3
- "version": "0.7.38",
3
+ "version": "0.7.40",
4
4
  "private": true,
5
5
  "type": "module",
6
6
  "engines": {
@@ -8,7 +8,7 @@ import { AGENT_COMMANDS_MAP, enforceToolPolicy, getMcpTools, reconcileInbox } fr
8
8
  import { deliverInboxNotification } from "./inbox.ts";
9
9
  import type { HubEvent, MessageRecord } from "./protocol.ts";
10
10
 
11
- const VERSION = "0.7.38";
11
+ const VERSION = "0.7.40";
12
12
  const inbox = new Map<string, MessageRecord>();
13
13
  const notifiedInbox = new Set<string>();
14
14
  let meshClient: HubClient | undefined;
@@ -97,9 +97,11 @@ export function validateRosterDocument(policy, readBlob) {
97
97
  text(model, 'origin model'); keys(origin, ['vendor', 'evidence'], 'origin');
98
98
  text(origin.vendor, 'origin vendor');
99
99
  if (PI_ALLOWED_PROVIDERS.includes(canonical(origin.vendor.toLowerCase()))) refuse('origin/vendor must name the model vendor, not billing provider');
100
- keys(origin.evidence, ['source', 'sha256'], 'origin evidence'); digest(origin.evidence.sha256);
100
+ const evidenceKeys = origin.evidence.commit === undefined ? ['source', 'sha256'] : ['source', 'commit', 'sha256'];
101
+ keys(origin.evidence, evidenceKeys, 'origin evidence'); digest(origin.evidence.sha256);
101
102
  sourcePath(origin.evidence.source);
102
- const bytes = readBlob(origin.evidence.source);
103
+ if (origin.evidence.commit !== undefined) objectId(origin.evidence.commit, 'origin evidence commit');
104
+ const bytes = readBlob(origin.evidence.source, origin.evidence.commit);
103
105
  if (bytes == null || sha256(bytes) !== origin.evidence.sha256) refuse('origin evidence hash mismatch');
104
106
  }
105
107
  for (const [id, route] of Object.entries(policy.routes)) {
@@ -160,17 +162,24 @@ export function validateRosterDocument(policy, readBlob) {
160
162
  }
161
163
  return policy;
162
164
  }
163
- function validate(bytes, commit) {
165
+ function validate(bytes, commit, trusted) {
164
166
  let policy;
165
167
  try { policy = YAML.parse(bytes.toString('utf8')); } catch { refuse('invalid policy YAML'); }
166
168
  if (policy === undefined || policy === null || typeof policy !== 'object' || Array.isArray(policy)) refuse('invalid policy YAML');
167
- return validateRosterDocument(policy, source => blobAt(commit, source).bytes);
169
+ const readBlob = (source, pinnedCommit) => {
170
+ if (pinnedCommit === undefined) return blobAt(commit, source).bytes;
171
+ // A pinned evidence commit decouples the digest from later edits to the
172
+ // evidence file; the pinned commit itself must be part of trusted history.
173
+ git('merge-base', '--is-ancestor', pinnedCommit, trusted);
174
+ return blobAt(pinnedCommit, source).bytes;
175
+ };
176
+ return validateRosterDocument(policy, readBlob);
168
177
  }
169
178
  export function loadTrustedRosterPolicy() {
170
179
  const snapshot = control();
171
180
  const { blob, bytes } = blobAt(snapshot.head, POLICY);
172
181
  if (!workingBytes(POLICY).equals(bytes)) refuse('working policy differs from committed bytes');
173
- const policy = validate(bytes, snapshot.head);
182
+ const policy = validate(bytes, snapshot.head, snapshot.trusted);
174
183
  unchanged(snapshot);
175
184
  return frozen({ identity: { commit: snapshot.head, blob, sha256: sha256(bytes) }, policy });
176
185
  }
@@ -181,7 +190,7 @@ export function resolveBoundPolicy(identity) {
181
190
  git('merge-base', '--is-ancestor', identity.commit, snapshot.trusted);
182
191
  const { blob, bytes } = blobAt(identity.commit, POLICY);
183
192
  if (blob !== identity.blob || sha256(bytes) !== identity.sha256) refuse('bound identity mismatch');
184
- const policy = validate(bytes, identity.commit);
193
+ const policy = validate(bytes, identity.commit, snapshot.trusted);
185
194
  unchanged(snapshot);
186
195
  return frozen({ identity: { ...identity }, policy });
187
196
  }