@kontextmind/kxm 0.7.73 → 0.7.74

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.73",
14
+ "version": "0.7.74",
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.73",
3
+ "version": "0.7.74",
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.73",
5
+ "version": "0.7.74",
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",
@@ -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.73";
17185
+ var VERSION = "0.7.74";
17186
17186
  var inbox = /* @__PURE__ */ new Map();
17187
17187
  var notifiedInbox = /* @__PURE__ */ new Set();
17188
17188
  var meshClient;
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-plugin",
3
- "version": "0.7.73",
3
+ "version": "0.7.74",
4
4
  "private": true,
5
5
  "type": "module",
6
6
  "engines": {
@@ -32,7 +32,7 @@ export type ExternalActionKind =
32
32
  | "tracker-issue"
33
33
  | "webhook";
34
34
 
35
- export type ExternalEffectStatus = "in-flight" | "committed" | "failed" | "aborted";
35
+ export type ExternalEffectStatus = "in-flight" | "committed" | "failed" | "aborted" | "uncertain";
36
36
 
37
37
  export interface ExternalEffectReceipt {
38
38
  schema: typeof EXTERNAL_EFFECT_SCHEMA;
@@ -233,6 +233,13 @@ export class ExternalEffectsLedger {
233
233
 
234
234
  const existing = this.getReceipt(effectKey);
235
235
  if (existing) {
236
+ if (existing.status === "uncertain") {
237
+ return {
238
+ ok: false,
239
+ error: `effect_uncertain: ${input.actionKind} on ${input.targetRef} is parked as uncertain after a fencing conflict and requires explicit recovery`,
240
+ existing,
241
+ };
242
+ }
236
243
  if (existing.status === "committed") {
237
244
  return {
238
245
  ok: false,
@@ -315,6 +322,12 @@ export class ExternalEffectsLedger {
315
322
  return { ok: true, lastHeartbeatAt: now };
316
323
  }
317
324
 
325
+ /** Park an effect as uncertain: a fencing conflict means the outside world
326
+ * may have been touched, and only explicit recovery can unblock it. */
327
+ markUncertain(effectKey: string): void {
328
+ this.db.prepare("UPDATE external_effects SET status = 'uncertain' WHERE effect_key = ? AND status = 'in-flight'").run(effectKey);
329
+ }
330
+
318
331
  commitEffect(
319
332
  effectKey: string,
320
333
  receiptPayload: Record<string, unknown>,
@@ -392,6 +405,8 @@ export type SharedEffectRefusalCode =
392
405
  | "effect_lease_superseded"
393
406
  | "effect_already_committed"
394
407
  | "effect_in_flight"
408
+ /** The effect is parked as uncertain after a fencing conflict. */
409
+ | "effect_uncertain"
395
410
  | "effect_not_in_flight"
396
411
  | "effect_not_found";
397
412
 
@@ -494,6 +509,7 @@ export async function claimSharedEffect(input: {
494
509
  }): Promise<SharedEffectClaim | SharedEffectRefusal> {
495
510
  const needsLease = effectRequiresHubLease(input.actionKind, input.targetRef, input.runId);
496
511
  let lease: EffectLease | undefined;
512
+ let leaseRenewed = false;
497
513
 
498
514
  if (needsLease) {
499
515
  if (!input.lease) {
@@ -506,6 +522,7 @@ export async function claimSharedEffect(input: {
506
522
  try {
507
523
  const acquired = await input.lease.acquireLease(input.targetRef, input.leaseTtlMs ?? DEFAULT_LEASE_TIMEOUT_MS);
508
524
  lease = acquired.lease;
525
+ leaseRenewed = acquired.renewed === true;
509
526
  } catch (error) {
510
527
  const code = errorCodeOf(error);
511
528
  if (code === "lease_held") {
@@ -535,12 +552,17 @@ export async function claimSharedEffect(input: {
535
552
  });
536
553
 
537
554
  if (!claimed.ok) {
538
- // The ledger refused after the lease was taken, so give the resource back
539
- // rather than parking it until the TTL runs out.
540
- if (lease && input.lease) await releaseQuietly(input.lease, input.targetRef, lease.fencingToken);
555
+ // Only hand back a lease this caller newly acquired. A renewal of an
556
+ // existing holder's lease must survive a claim refusal: the original
557
+ // effect is still in-flight and still needs the fence.
558
+ if (lease && input.lease && !leaseRenewed) {
559
+ await releaseQuietly(input.lease, input.targetRef, lease.fencingToken);
560
+ }
541
561
  return {
542
562
  ok: false,
543
- code: claimed.error.startsWith("effect_already_committed") ? "effect_already_committed" : "effect_in_flight",
563
+ code: claimed.error.startsWith("effect_already_committed")
564
+ ? "effect_already_committed"
565
+ : claimed.error.startsWith("effect_uncertain") ? "effect_uncertain" : "effect_in_flight",
544
566
  error: claimed.error,
545
567
  existing: claimed.existing,
546
568
  };
@@ -653,6 +675,9 @@ export async function commitSharedEffect(input: {
653
675
  await input.lease.renewLease(receipt.targetRef, receipt.fencingToken!, input.leaseTtlMs ?? DEFAULT_LEASE_TIMEOUT_MS);
654
676
  } catch (error) {
655
677
  const code = errorCodeOf(error);
678
+ if (isLeaseLost(code)) {
679
+ input.ledger.markUncertain(input.effectKey);
680
+ }
656
681
  return {
657
682
  ok: false,
658
683
  code: isLeaseLost(code) ? "effect_lease_superseded" : "effect_lease_unavailable",
@@ -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.73";
11
+ const VERSION = "0.7.74";
12
12
  const inbox = new Map<string, MessageRecord>();
13
13
  const notifiedInbox = new Set<string>();
14
14
  let meshClient: HubClient | undefined;