@kontextmind/kxm 0.7.87 → 0.7.89

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.87",
14
+ "version": "0.7.89",
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.87",
3
+ "version": "0.7.89",
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.87",
5
+ "version": "0.7.89",
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",
@@ -20436,8 +20436,8 @@ var AGENT_COMMANDS = [
20436
20436
  summary: { type: "string", description: "Promotion summary" },
20437
20437
  authority: {
20438
20438
  type: "string",
20439
- enum: ["policy", "instruction", "evidence", "hypothesis"],
20440
- description: "Authority class"
20439
+ enum: ["evidence", "hypothesis"],
20440
+ description: "Authority class; an agent's proposal is peer origin, so evidence at most"
20441
20441
  },
20442
20442
  confidence: {
20443
20443
  type: "string",
@@ -1567,8 +1567,8 @@ var AGENT_COMMANDS = [
1567
1567
  summary: { type: "string", description: "Promotion summary" },
1568
1568
  authority: {
1569
1569
  type: "string",
1570
- enum: ["policy", "instruction", "evidence", "hypothesis"],
1571
- description: "Authority class"
1570
+ enum: ["evidence", "hypothesis"],
1571
+ description: "Authority class; an agent's proposal is peer origin, so evidence at most"
1572
1572
  },
1573
1573
  confidence: {
1574
1574
  type: "string",
@@ -15670,8 +15670,8 @@ var AGENT_COMMANDS = [
15670
15670
  summary: { type: "string", description: "Promotion summary" },
15671
15671
  authority: {
15672
15672
  type: "string",
15673
- enum: ["policy", "instruction", "evidence", "hypothesis"],
15674
- description: "Authority class"
15673
+ enum: ["evidence", "hypothesis"],
15674
+ description: "Authority class; an agent's proposal is peer origin, so evidence at most"
15675
15675
  },
15676
15676
  confidence: {
15677
15677
  type: "string",
@@ -16981,8 +16981,8 @@ var AGENT_COMMANDS = [
16981
16981
  summary: { type: "string", description: "Promotion summary" },
16982
16982
  authority: {
16983
16983
  type: "string",
16984
- enum: ["policy", "instruction", "evidence", "hypothesis"],
16985
- description: "Authority class"
16984
+ enum: ["evidence", "hypothesis"],
16985
+ description: "Authority class; an agent's proposal is peer origin, so evidence at most"
16986
16986
  },
16987
16987
  confidence: {
16988
16988
  type: "string",
@@ -17182,7 +17182,7 @@ async function deliverInboxNotification(messageId, delivered, notify) {
17182
17182
  }
17183
17183
 
17184
17184
  // plugins/kxm/src/mcp-server.ts
17185
- var VERSION = "0.7.87";
17185
+ var VERSION = "0.7.89";
17186
17186
  var inbox = /* @__PURE__ */ new Map();
17187
17187
  var notifiedInbox = /* @__PURE__ */ new Set();
17188
17188
  var meshClient;
@@ -17303,8 +17303,8 @@ var AGENT_COMMANDS = [
17303
17303
  summary: { type: "string", description: "Promotion summary" },
17304
17304
  authority: {
17305
17305
  type: "string",
17306
- enum: ["policy", "instruction", "evidence", "hypothesis"],
17307
- description: "Authority class"
17306
+ enum: ["evidence", "hypothesis"],
17307
+ description: "Authority class; an agent's proposal is peer origin, so evidence at most"
17308
17308
  },
17309
17309
  confidence: {
17310
17310
  type: "string",
@@ -18082,6 +18082,9 @@ var NativeStateProvider = class {
18082
18082
  if (change?.schema !== "kxm.state-change-proposal.v1") {
18083
18083
  throw new ProtocolError(400, "invalid state change proposal schema", "invalid_state_proposal");
18084
18084
  }
18085
+ if (change.origin !== "peer" && change.origin !== "human") {
18086
+ throw new ProtocolError(400, "state change proposal origin must be peer or human", "invalid_state_proposal");
18087
+ }
18085
18088
  const project = requireNonEmpty(change.project, "proposal project");
18086
18089
  const key = requireNonEmpty(change.key, "proposal key");
18087
18090
  const evidenceRefs = boundedRefs(change.evidenceRefs, "proposal evidenceRefs");
@@ -18091,7 +18094,7 @@ var NativeStateProvider = class {
18091
18094
  project,
18092
18095
  summary: change.summary,
18093
18096
  provenance: {
18094
- sourceType: change.proposedBy.startsWith("agent_") ? "peer" : "human",
18097
+ sourceType: change.origin,
18095
18098
  sourceRef: `proposed-by:${change.proposedBy}`
18096
18099
  },
18097
18100
  authority: change.authority,
@@ -20481,12 +20484,12 @@ function createMeshHub(options = {}) {
20481
20484
  if (agent.project !== project) {
20482
20485
  throw new ProtocolError(403, "context requests are limited to the agent's project", "context_isolation_violation");
20483
20486
  }
20484
- return { project, caller: agent.id };
20487
+ return { project, caller: agent.id, credential: "agent" };
20485
20488
  }
20486
20489
  requireAdminAuth(request);
20487
20490
  const callerHeader = request.headers["x-kxm-caller-id"];
20488
20491
  const caller = typeof callerHeader === "string" && callerHeader.trim() ? callerHeader.trim() : "kxm-admin";
20489
- return { project, caller };
20492
+ return { project, caller, credential: "admin" };
20490
20493
  }
20491
20494
  function requireAdminAuth(request) {
20492
20495
  if (!authToken2 && isLoopback(host2)) return;
@@ -21593,7 +21596,17 @@ data: ${JSON.stringify({ type: "ops", project, topic: "agents", at: nowIso() })}
21593
21596
  const contextStateProposeMatch = url.pathname.match(/^\/v1\/context\/state\/propose$/);
21594
21597
  if (method === "POST" && contextStateProposeMatch) {
21595
21598
  const body = await readJson(request);
21596
- const { project: callerProject, caller: callerId } = contextCallerProject(request, body.project);
21599
+ const { project: callerProject, caller: callerId, credential } = contextCallerProject(request, body.project);
21600
+ if (credential === "admin") requireConfiguredAdminAuth(request, "human-origin state proposals");
21601
+ if (body.proposedBy !== void 0 && body.proposedBy !== callerId) {
21602
+ logger({ event: "security_alert", alert: "state_proposer_mismatch", project: callerProject, callerId, credential });
21603
+ throw new ProtocolError(
21604
+ 403,
21605
+ "proposedBy must name the authenticated caller",
21606
+ "state_proposer_mismatch"
21607
+ );
21608
+ }
21609
+ const origin = credential === "agent" ? "peer" : "human";
21597
21610
  const proposalId = await stateProvider.propose({
21598
21611
  schema: "kxm.state-change-proposal.v1",
21599
21612
  project: callerProject,
@@ -21602,11 +21615,12 @@ data: ${JSON.stringify({ type: "ops", project, topic: "agents", at: nowIso() })}
21602
21615
  authority: parseContextAuthority(body.authority),
21603
21616
  confidence: parseContextConfidence(body.confidence),
21604
21617
  evidenceRefs: boundedStringList(body.evidenceRefs, "evidenceRefs", 32),
21605
- proposedBy: typeof body.proposedBy === "string" && body.proposedBy.trim() ? body.proposedBy.trim() : callerId
21618
+ proposedBy: callerId,
21619
+ origin
21606
21620
  });
21607
21621
  counters.contextRequests += 1;
21608
21622
  publishOps(callerProject, "workflows");
21609
- logger({ event: "context_state_proposed", project: callerProject, proposalId, proposedBy: callerId });
21623
+ logger({ event: "context_state_proposed", project: callerProject, proposalId, proposedBy: callerId, origin });
21610
21624
  json(response, 201, { proposalId });
21611
21625
  return;
21612
21626
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-plugin",
3
- "version": "0.7.87",
3
+ "version": "0.7.89",
4
4
  "private": true,
5
5
  "type": "module",
6
6
  "engines": {
@@ -823,8 +823,8 @@ export const AGENT_COMMANDS: readonly AgentCommand[] = [
823
823
  summary: { type: "string", description: "Promotion summary" },
824
824
  authority: {
825
825
  type: "string",
826
- enum: ["policy", "instruction", "evidence", "hypothesis"],
827
- description: "Authority class",
826
+ enum: ["evidence", "hypothesis"],
827
+ description: "Authority class; an agent's proposal is peer origin, so evidence at most",
828
828
  },
829
829
  confidence: {
830
830
  type: "string",
@@ -44,6 +44,10 @@ export interface StateChangeProposal {
44
44
  confidence: ContextItem["confidence"];
45
45
  evidenceRefs: string[];
46
46
  proposedBy: string;
47
+ /** Origin of the credential the hub verified for the proposer: an agent
48
+ * key is `peer` (evidence at most), the administrative token `human`.
49
+ * Never inferred from `proposedBy`, which is a label, not a credential. */
50
+ origin: "peer" | "human";
47
51
  supersedes?: string[];
48
52
  }
49
53
 
@@ -601,9 +601,12 @@ export function createMeshHub(options: MeshHubOptions = {}): MeshHub {
601
601
 
602
602
  /** Context callers authenticate either as a registered agent (project-
603
603
  * scoped to their own project) or with the administrative token (single
604
- * explicit project scope per request). Returns the validated project and
605
- * a stable caller identity for provenance. */
606
- function contextCallerProject(request: IncomingMessage, requested: unknown): { project: string; caller: string } {
604
+ * explicit project scope per request). Returns the validated project, a
605
+ * stable caller identity for provenance, and which credential proved it. */
606
+ function contextCallerProject(
607
+ request: IncomingMessage,
608
+ requested: unknown,
609
+ ): { project: string; caller: string; credential: "agent" | "admin" } {
607
610
  const project = requireString(requested, "project", { max: 200 });
608
611
  const agentHeader = request.headers["x-kxm-agent-id"];
609
612
  if (typeof agentHeader === "string" && agentHeader.trim()) {
@@ -612,14 +615,14 @@ export function createMeshHub(options: MeshHubOptions = {}): MeshHub {
612
615
  if (agent.project !== project) {
613
616
  throw new ProtocolError(403, "context requests are limited to the agent's project", "context_isolation_violation");
614
617
  }
615
- return { project, caller: agent.id };
618
+ return { project, caller: agent.id, credential: "agent" };
616
619
  }
617
620
  requireAdminAuth(request);
618
621
  const callerHeader = request.headers["x-kxm-caller-id"];
619
622
  const caller = typeof callerHeader === "string" && callerHeader.trim()
620
623
  ? callerHeader.trim()
621
624
  : "kxm-admin";
622
- return { project, caller };
625
+ return { project, caller, credential: "admin" };
623
626
  }
624
627
 
625
628
  function requireAdminAuth(request: IncomingMessage): void {
@@ -1829,7 +1832,21 @@ export function createMeshHub(options: MeshHubOptions = {}): MeshHub {
1829
1832
  const contextStateProposeMatch = url.pathname.match(/^\/v1\/context\/state\/propose$/);
1830
1833
  if (method === "POST" && contextStateProposeMatch) {
1831
1834
  const body = await readJson(request);
1832
- const { project: callerProject, caller: callerId } = contextCallerProject(request, body.project);
1835
+ const { project: callerProject, caller: callerId, credential } = contextCallerProject(request, body.project);
1836
+ // Origin follows the credential, never a name: an agent key proposes as
1837
+ // a peer, capped at evidence by the grant floor. Human origin can claim
1838
+ // policy, so it takes the configured administrative token with no
1839
+ // loopback bypass, as promotion does.
1840
+ if (credential === "admin") requireConfiguredAdminAuth(request, "human-origin state proposals");
1841
+ if (body.proposedBy !== undefined && body.proposedBy !== callerId) {
1842
+ logger({ event: "security_alert", alert: "state_proposer_mismatch", project: callerProject, callerId, credential });
1843
+ throw new ProtocolError(
1844
+ 403,
1845
+ "proposedBy must name the authenticated caller",
1846
+ "state_proposer_mismatch",
1847
+ );
1848
+ }
1849
+ const origin = credential === "agent" ? "peer" : "human";
1833
1850
  const proposalId = await stateProvider.propose({
1834
1851
  schema: "kxm.state-change-proposal.v1",
1835
1852
  project: callerProject,
@@ -1838,11 +1855,12 @@ export function createMeshHub(options: MeshHubOptions = {}): MeshHub {
1838
1855
  authority: parseContextAuthority(body.authority),
1839
1856
  confidence: parseContextConfidence(body.confidence),
1840
1857
  evidenceRefs: boundedStringList(body.evidenceRefs, "evidenceRefs", 32),
1841
- proposedBy: (typeof body.proposedBy === "string" && body.proposedBy.trim()) ? body.proposedBy.trim() : callerId,
1858
+ proposedBy: callerId,
1859
+ origin,
1842
1860
  });
1843
1861
  counters.contextRequests += 1;
1844
1862
  publishOps(callerProject, "workflows");
1845
- logger({ event: "context_state_proposed", project: callerProject, proposalId, proposedBy: callerId });
1863
+ logger({ event: "context_state_proposed", project: callerProject, proposalId, proposedBy: callerId, origin });
1846
1864
  json(response, 201, { proposalId });
1847
1865
  return;
1848
1866
  }
@@ -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.87";
11
+ const VERSION = "0.7.89";
12
12
  const inbox = new Map<string, MessageRecord>();
13
13
  const notifiedInbox = new Set<string>();
14
14
  let meshClient: HubClient | undefined;
@@ -184,6 +184,9 @@ export class NativeStateProvider implements StateProvider {
184
184
  if (change?.schema !== "kxm.state-change-proposal.v1") {
185
185
  throw new ProtocolError(400, "invalid state change proposal schema", "invalid_state_proposal");
186
186
  }
187
+ if (change.origin !== "peer" && change.origin !== "human") {
188
+ throw new ProtocolError(400, "state change proposal origin must be peer or human", "invalid_state_proposal");
189
+ }
187
190
  const project = requireNonEmpty(change.project, "proposal project");
188
191
  const key = requireNonEmpty(change.key, "proposal key");
189
192
  const evidenceRefs = boundedRefs(change.evidenceRefs, "proposal evidenceRefs");
@@ -193,7 +196,7 @@ export class NativeStateProvider implements StateProvider {
193
196
  project,
194
197
  summary: change.summary,
195
198
  provenance: {
196
- sourceType: change.proposedBy.startsWith("agent_") ? "peer" : "human",
199
+ sourceType: change.origin,
197
200
  sourceRef: `proposed-by:${change.proposedBy}`,
198
201
  },
199
202
  authority: change.authority,