@kontextmind/kxm 0.7.71 → 0.7.73
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/CHANGELOG.md +22 -0
- package/docs/contracts/synchronization.md +6 -3
- package/docs/operations.md +68 -5
- package/docs/test-matrix.md +1 -0
- package/package.json +1 -1
- package/plugins/kxm/.claude-plugin/plugin.json +1 -1
- package/plugins/kxm/dist/cli.js +346 -234
- package/plugins/kxm/dist/client.js +100 -35
- package/plugins/kxm/dist/core.js +10 -0
- package/plugins/kxm/dist/extension.js +73 -36
- package/plugins/kxm/dist/mcp-server.js +73 -36
- package/plugins/kxm/dist/runtime-supervisor.js +917 -141
- package/plugins/kxm/dist/runtime.js +977 -235
- package/plugins/kxm/dist/server.js +18584 -10365
- package/plugins/kxm/package.json +1 -1
- package/plugins/kxm/skills/kxm/references/protocol.md +18 -0
- package/plugins/kxm/src/client.ts +124 -4
- package/plugins/kxm/src/database.ts +8 -4
- package/plugins/kxm/src/external-effects.ts +390 -66
- package/plugins/kxm/src/hub.ts +358 -1
- package/plugins/kxm/src/mcp-server.ts +1 -1
- package/plugins/kxm/src/project-config.ts +15 -0
- package/plugins/kxm/src/protocol.ts +52 -0
- package/plugins/kxm/src/runtime-store.ts +109 -1
- package/plugins/kxm/src/runtime-supervisor.ts +150 -0
- package/plugins/kxm/src/store.ts +416 -5
- package/plugins/kxm/src/sync-transform.ts +380 -0
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,21 @@ All notable user-facing changes are documented here. The project follows [Semant
|
|
|
6
6
|
|
|
7
7
|
### Added
|
|
8
8
|
|
|
9
|
+
- **Fenced hub leases, and shared external effects that will not run without one.**
|
|
10
|
+
`POST /v1/leases/:resource/acquire|renew|release` are agent-authenticated and
|
|
11
|
+
project-scoped (the hub prefixes the caller's project onto the resource name). Each call
|
|
12
|
+
is a compare-and-set inside one store transaction on the **hub clock**, with TTLs bounded
|
|
13
|
+
to 5 s–10 min. The fencing token starts at 1, survives renewal unchanged, and increments
|
|
14
|
+
only when a new holder takes over an expired lease, so a holder that returns after its
|
|
15
|
+
deadline is told its token was superseded instead of writing behind its replacement.
|
|
16
|
+
Shared external effects (`git-push` to a ref the run does not own, `pr-create`,
|
|
17
|
+
`tracker-issue`, `webhook`) acquire a lease keyed by `targetRef` before executing, record
|
|
18
|
+
`{leaseResource, fencingToken}` in the receipt, renew on the existing Q6 heartbeat, and
|
|
19
|
+
re-present the token at commit; a superseded token leaves the effect `in-flight` and the
|
|
20
|
+
attempt `blocked_uncertain` with nothing retrying. An unreachable hub refuses the effect
|
|
21
|
+
(`effect_lease_unavailable`) rather than executing it unfenced. Unique-namespace kinds
|
|
22
|
+
(`git-branch`, `git-commit`, and a push to the run's own branch) stay lease-free.
|
|
23
|
+
|
|
9
24
|
- **`kxm peer send --allow-offline` queues to a registered offline peer.**
|
|
10
25
|
`POST /v1/messages` accepts `allowOffline: true` (also `kxm_send.allowOffline`): a
|
|
11
26
|
registered agent in the same project is stored `queued` instead of `target_not_found`,
|
|
@@ -42,6 +57,13 @@ All notable user-facing changes are documented here. The project follows [Semant
|
|
|
42
57
|
|
|
43
58
|
### Changed
|
|
44
59
|
|
|
60
|
+
- **Hub store schema v3 → v4, external-effects ledger v1 → v2.** The hub store gains a
|
|
61
|
+
`leases` table and the ledger gains `lease_resource`/`fencing_token` columns. Neither has
|
|
62
|
+
a migration lane: an older file is refused at open with `runtime_schema_outdated`, and the
|
|
63
|
+
hub backup/restore ceiling in `database.ts` moves to 4 with the bump, so a v3 backup must
|
|
64
|
+
be restored with the release that produced it. Delete the state file to start fresh and
|
|
65
|
+
let `kxm hub start` recreate it.
|
|
66
|
+
|
|
45
67
|
- **`kxm tenant status`: one composed read for the portal, with the authorities labelled.**
|
|
46
68
|
The portal needs hub metadata (roster, message queue, the hub's run projection) *and* the
|
|
47
69
|
Runtime's authoritative run state, and the failure mode S2 exists to prevent is rendering
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
# Hub synchronization contract
|
|
2
2
|
|
|
3
|
-
> **Status.** `kxm.sync-event.v1` is a **schema-tested contract
|
|
4
|
-
>
|
|
5
|
-
>
|
|
3
|
+
> **Status.** `kxm.sync-event.v1` is a **schema-tested contract with an
|
|
4
|
+
> implementation** (cross-host P5): the event store `outbox` (v6), the
|
|
5
|
+
> `sync-transform.ts` derivation under the default policy, supervisor push to
|
|
6
|
+
> `POST /v1/sync/events`, and hub ingestion into `sync_events` (hub store v5).
|
|
7
|
+
> Custom project policies and on-demand content transfer remain unimplemented.
|
|
8
|
+
> Phase 8 gate: [implementation plan](../../plans/implementation-plan.md#phase-8-multi-project-hub-kxm).
|
|
6
9
|
|
|
7
10
|
Synchronization is summary-first, project-scoped, at-least-once, and
|
|
8
11
|
allowlist-based. The full local event is never placed directly in the outbox.
|
package/docs/operations.md
CHANGED
|
@@ -315,7 +315,11 @@ restore lands somewhere the running service will not look.
|
|
|
315
315
|
copies, not the file copy**: configuration, repository bindings, prompt sidecars,
|
|
316
316
|
routing manifests and update configuration are not databases, so a snapshot-only backup
|
|
317
317
|
reproduces exactly the failure this section exists to remove. The hub's own backup path already writes a hashed manifest and records
|
|
318
|
-
a schema version ceiling; keep that manifest with the files.
|
|
318
|
+
a schema version ceiling; keep that manifest with the files. That ceiling is
|
|
319
|
+
**hub store v5 and event store v6** as of the sync-outbox release: a backup taken by
|
|
320
|
+
an earlier build records hub v4 or event store v5 and is refused by this one, because
|
|
321
|
+
there is no migration lane.
|
|
322
|
+
Restore such a backup with the release that produced it, or start fresh.
|
|
319
323
|
3. Record the package version, configuration revision and schema versions beside the copy.
|
|
320
324
|
A restore that cannot state which release produced it is not a restore path.
|
|
321
325
|
4. Keep at least one rotation, and bound retention explicitly — run events and prompt
|
|
@@ -392,10 +396,11 @@ Broader deployments need shared state and coordination, external identity and fi
|
|
|
392
396
|
|
|
393
397
|
## v0.5 context/state storage
|
|
394
398
|
|
|
395
|
-
The hub database (schema version
|
|
396
|
-
agents, messages, workflow runs,
|
|
397
|
-
|
|
398
|
-
|
|
399
|
+
The hub database (schema version 5) carries `context_items`, `leases`,
|
|
400
|
+
`sync_events` and `runtime_presence` alongside agents, messages, workflow runs,
|
|
401
|
+
and the journal. Temporal state,
|
|
402
|
+
knowledge records, and their audit trails live in the same SQLite file and
|
|
403
|
+
upgrade in place from v0.4 databases.
|
|
399
404
|
|
|
400
405
|
- **Backup and restore**: include the hub database file and, if used, the
|
|
401
406
|
`.kxm/skills/` and `.kxm/knowledge/` trees. The wiki is a compiled view and
|
|
@@ -412,6 +417,64 @@ place from v0.4 databases.
|
|
|
412
417
|
to open on an older runtime). Restore a database backup taken before the
|
|
413
418
|
upgrade instead.
|
|
414
419
|
|
|
420
|
+
### Fenced leases over shared resources
|
|
421
|
+
|
|
422
|
+
`leases` holds one row per project-scoped resource: the holder, a monotonic
|
|
423
|
+
fencing token, and a deadline. `POST /v1/leases/:resource/acquire|renew|release`
|
|
424
|
+
are agent-authenticated and scoped to the caller's project, which the hub
|
|
425
|
+
prefixes onto the resource name — two projects naming the same branch never
|
|
426
|
+
contend. TTLs are bounded to 5 s–10 min and every decision is made on the **hub
|
|
427
|
+
clock** inside one store transaction, so a skewed client cannot extend its own
|
|
428
|
+
grip.
|
|
429
|
+
|
|
430
|
+
The token is the safety property. It starts at 1, stays put across renewals, and
|
|
431
|
+
increments only when a new holder takes over an expired lease. A holder that
|
|
432
|
+
comes back after its deadline is therefore told its token was superseded rather
|
|
433
|
+
than allowed to write behind whoever replaced it. Shared external effects
|
|
434
|
+
(`git-push` to a ref the run does not own, `pr-create`, `tracker-issue`,
|
|
435
|
+
`webhook`) take a lease before executing and re-present the token at commit; a
|
|
436
|
+
superseded token leaves the effect `in-flight` and the attempt
|
|
437
|
+
`blocked_uncertain` for an operator to resolve, and nothing retries it. An
|
|
438
|
+
unreachable hub refuses the effect (`effect_lease_unavailable`) rather than
|
|
439
|
+
running it unfenced.
|
|
440
|
+
|
|
441
|
+
Expired rows are **not** reaped immediately — their token is what the next
|
|
442
|
+
takeover has to increment past. The retention sweep drops rows whose deadline is
|
|
443
|
+
older than the run-retention window (7 days by default), far beyond any live
|
|
444
|
+
holder. `kxm_leases_granted_total`, `kxm_leases_refused_total` and
|
|
445
|
+
`kxm_leases_released_total` in `/metrics` report contention;
|
|
446
|
+
`lease_acquired`, `lease_renewed`, `lease_released`, `lease_denied` and
|
|
447
|
+
`lease_purged` are the structured log events.
|
|
448
|
+
|
|
449
|
+
### Runtime → hub run-fact sync
|
|
450
|
+
|
|
451
|
+
Every event the Runtime commits also writes one row to the event store's
|
|
452
|
+
`outbox` (event store v6), in the same transaction. The row holds only a derived
|
|
453
|
+
`kxm.sync-event.v1` object — allowlisted fields, registered secret values and
|
|
454
|
+
credential shapes replaced, absolute paths removed, text bounded, the default
|
|
455
|
+
sync policy revision recorded — never the local event. A field the allowlist
|
|
456
|
+
does not name is listed by name in `redaction.fieldsOmitted` and its value is
|
|
457
|
+
dropped.
|
|
458
|
+
|
|
459
|
+
The supervisor pushes outbound only: every `KXM_RUNTIME_SYNC_INTERVAL_MS` (10 s
|
|
460
|
+
by default) it posts `POST /v1/runtime/presence` for each open project, then
|
|
461
|
+
`POST /v1/sync/events` in outbox order, to `KXM_SERVER_URL` or the `kxm hub bind`
|
|
462
|
+
URL with that project's token. No bound hub means nothing is sent and rows stay
|
|
463
|
+
pending; local execution never waits on sync. The hub accepts each event once by
|
|
464
|
+
`{projectId, runId, sequence}`: the same bytes again are an idempotent
|
|
465
|
+
`duplicate`; different bytes under a used sequence, a run claimed by another
|
|
466
|
+
project, or a push for another Runtime's events are refused and logged as
|
|
467
|
+
`security_alert`. Out-of-order events are held, and the per-run cursor is the
|
|
468
|
+
gapless prefix, so a gap stays pending until it is filled.
|
|
469
|
+
|
|
470
|
+
`GET /v1/ops/snapshot` adds `homeRuntimes`: synchronized runs grouped by home
|
|
471
|
+
Runtime, each with its bounded title/status, `lastSequence`, `pendingGap`, and
|
|
472
|
+
`orphaned` once the Runtime's presence lease (`heartbeatAt + staleAfterMs`, hub
|
|
473
|
+
clock) has lapsed. Orphaned is view state; nothing is migrated.
|
|
474
|
+
`kxm_sync_events_accepted_total`, `kxm_sync_events_duplicate_total`,
|
|
475
|
+
`kxm_sync_events_refused_total`, `kxm_sync_conflicts_total` and
|
|
476
|
+
`kxm_runtime_heartbeats_total` are in `/metrics`.
|
|
477
|
+
|
|
415
478
|
## Hub Q&A / knowledge base
|
|
416
479
|
|
|
417
480
|
- [What is all stored on the hub?](kb/qa-what-the-hub-stores.md)
|
package/docs/test-matrix.md
CHANGED
|
@@ -23,6 +23,7 @@ npm run verify
|
|
|
23
23
|
| Queue, acknowledgement, visibility, reply, and authorization | `test/core/hub-api.test.ts`, `test/core/hub.test.ts` |
|
|
24
24
|
| Queued/delivered replay after recipient restart reuses one message record | `test/core/hub.test.ts`, `test/core/extension.test.ts`, `test/core/mcp.test.ts` |
|
|
25
25
|
| Known-offline peer send with `allowOffline` queues, delivers once on resumption, and expires unread by TTL | `test/core/hub-api.test.ts` |
|
|
26
|
+
| Fenced hub leases: CAS acquire/renew/release, monotonic token on takeover only, hub-clocked expiry, and a shared external effect refused at commit under a superseded token | `test/core/hub-api.test.ts`, `test/core/store.test.ts`, `test/core/external-effects.test.ts` |
|
|
26
27
|
| One-to-three-peer fanout, recoverable local timeouts/aborts, exact retries, and partial-error collection | `test/core/client.test.ts`, `test/core/hub-api.test.ts`, `test/core/extension.test.ts`, `test/core/mcp.test.ts` |
|
|
27
28
|
| TTL expiry, sender cancellation, and terminal retention | `test/core/hub-api.test.ts` |
|
|
28
29
|
| Terminal inbound cleanup and next-request activation | `test/core/extension.test.ts`, `test/core/mcp.test.ts` |
|
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.73",
|
|
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",
|