@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.
- package/.claude-plugin/marketplace.json +1 -1
- package/package.json +1 -1
- package/plugins/kxm/.claude-plugin/plugin.json +1 -1
- package/plugins/kxm/dist/mcp-server.js +1 -1
- package/plugins/kxm/package.json +1 -1
- package/plugins/kxm/src/external-effects.ts +30 -5
- package/plugins/kxm/src/mcp-server.ts +1 -1
package/package.json
CHANGED
|
@@ -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.
|
|
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.
|
|
17185
|
+
var VERSION = "0.7.74";
|
|
17186
17186
|
var inbox = /* @__PURE__ */ new Map();
|
|
17187
17187
|
var notifiedInbox = /* @__PURE__ */ new Set();
|
|
17188
17188
|
var meshClient;
|
package/plugins/kxm/package.json
CHANGED
|
@@ -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
|
-
//
|
|
539
|
-
//
|
|
540
|
-
|
|
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")
|
|
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.
|
|
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;
|