@kontextmind/kxm 0.7.52 → 0.7.53
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 +17 -0
- package/docs/contracts/migration.md +39 -1
- package/package.json +1 -1
- package/plugins/kxm/.claude-plugin/plugin.json +1 -1
- package/plugins/kxm/dist/cli.js +5 -14
- package/plugins/kxm/dist/mcp-server.js +1 -1
- package/plugins/kxm/dist/runtime-supervisor.js +6 -81
- package/plugins/kxm/dist/runtime.js +6 -81
- package/plugins/kxm/dist/server.js +6 -105
- package/plugins/kxm/package.json +1 -1
- package/plugins/kxm/src/database.ts +9 -21
- package/plugins/kxm/src/external-effects.ts +37 -27
- package/plugins/kxm/src/intake.ts +6 -11
- package/plugins/kxm/src/mcp-server.ts +1 -1
- package/plugins/kxm/src/runtime-store.ts +0 -69
- package/plugins/kxm/src/store.ts +0 -92
package/CHANGELOG.md
CHANGED
|
@@ -35,6 +35,23 @@ All notable user-facing changes are documented here. The project follows [Semant
|
|
|
35
35
|
|
|
36
36
|
### Changed
|
|
37
37
|
|
|
38
|
+
- **No schema migration lanes, no legacy stamp tolerance (single-operator tool).** Stepwise
|
|
39
|
+
schema migration lanes are removed from the hub store and the per-project event store, and
|
|
40
|
+
the external-effects store no longer runs an in-place `ALTER TABLE ... ADD COLUMN` whose
|
|
41
|
+
failure it swallowed (that column is declared in the fresh schema, so the lane could only
|
|
42
|
+
ever fire on an older store; the store has no production caller yet, so this closes the
|
|
43
|
+
lane rather than fixing a live upgrade).
|
|
44
|
+
A database stamped behind this build now fails closed with `runtime_schema_outdated` and
|
|
45
|
+
a message that says to delete the state file or re-run `kxm init` — and the refusal never
|
|
46
|
+
advances `user_version`, so the store stays identifiably old (WAL sidecars may still be
|
|
47
|
+
checkpointed by opening the file, so the whole state set remains the backup unit — see
|
|
48
|
+
[`docs/operations.md`](docs/operations.md)). Relabelling a store it refused to open would
|
|
49
|
+
only hide the problem until a query hit a missing column. The coordinator fingerprint no
|
|
50
|
+
longer recomputes over stored authority to forgive rows written before set canonicalisation:
|
|
51
|
+
a stale coordinator is re-bound. Intake tests go from 23 to 22; the two forced-race tests
|
|
52
|
+
now win against ordinary current-format rows, and one refusal test asserts the untouched
|
|
53
|
+
stamp.
|
|
54
|
+
|
|
38
55
|
- **`kxm hub bind` no longer stores a remote URL it cannot authenticate to.** A remote
|
|
39
56
|
binding is a deliberate network decision, so it is now refused when no credential resolves
|
|
40
57
|
(explicit `KXM_AUTH_TOKEN`, or the persisted hub record's admin or project tokens) —
|
|
@@ -16,7 +16,7 @@ Presence of KXM documents does not activate new behavior.
|
|
|
16
16
|
| Long-lived manually started Pi workers | Runtime-managed run-scoped sessions | Existing worker mode remains available during compatibility release |
|
|
17
17
|
| Shared/off workflow Pi history | `{run, agent, instance, scopeEpoch}` sessions | Never import shared conversation history into a narrower run scope |
|
|
18
18
|
| Hub-owned workflow state | Home Runtime event log with hub projection | Import completed history as legacy records; active-run cutover requires quiescence |
|
|
19
|
-
| SQLite schema v3 `kxm.db` | Runtime registry, per-project event stores, hub registry/project stores |
|
|
19
|
+
| SQLite schema v3 `kxm.db` | Runtime registry, per-project event stores, hub registry/project stores | **No in-place schema migration.** A store stamped behind the current build fails closed with `runtime_schema_outdated` and the refusal never advances `user_version` (opening the file may still checkpoint WAL sidecars, so the whole state set is the backup unit); re-init instead. Stepwise lanes were removed 2026-09-20 under the single-operator decision from the hub store, the per-project event store, and the external-effects `ALTER TABLE` add-column; the Runtime registry never carried a stepwise lane (see [implementation-plan.md](../../plans/implementation-plan.md) → Decided) |
|
|
20
20
|
| Full peer message bodies in hub DB | Summary-first sync events | Existing bodies remain protected legacy data and are not re-emitted automatically |
|
|
21
21
|
| Project tokens/manual environment auth | Runtime enrollment and scoped credentials | Preserve current mode until enrollment is confirmed; never copy tokens into Git |
|
|
22
22
|
| `.kxm/config/env.example` | Built-in defaults plus optional scoped env YAML | Import only explicit portable differences; secrets become references |
|
|
@@ -26,12 +26,27 @@ Presence of KXM documents does not activate new behavior.
|
|
|
26
26
|
|
|
27
27
|
## Compatibility releases and activation
|
|
28
28
|
|
|
29
|
+
> **Scope note (2026-09-20).** This matrix documents the Mesh/v0.5 → KXM cutover. KXM has
|
|
30
|
+
> one operator and no external installs, so **schema migration and old-state tolerance are
|
|
31
|
+
> out of scope** and the lanes that existed are gone: stores refuse an older stamp rather
|
|
32
|
+
> than upgrading, the external-effects store no longer adds a column in place, and the
|
|
33
|
+
> coordinator fingerprint no longer recomputes to forgive pre-canonicalisation rows. What
|
|
34
|
+
> remains here describes the **project/content** cutover, which the follow-up cut removes
|
|
35
|
+
> along with `kxm migrate`. Nothing here promises that the runtime will read an old
|
|
36
|
+
> database.
|
|
37
|
+
|
|
29
38
|
Local Runtime support may ship publicly before hub KXM, but it remains beside
|
|
30
39
|
existing hub contracts and stores. Old command names are not preserved. A project
|
|
31
40
|
activates `kxm.*.v1` only by an explicit successful `kxm init`/migration receipt;
|
|
32
41
|
file presence alone never activates it. Legacy hub runs continue on the legacy
|
|
33
42
|
engine.
|
|
34
43
|
|
|
44
|
+
> **Superseded (2026-09-20).** The single-operator decision removes legacy readers
|
|
45
|
+
> **without** a compatibility release, so this transition-release list is no longer a plan of
|
|
46
|
+
> record. It stays here to name what was given up: no dual-read window, no `mesh_*` shim, and
|
|
47
|
+
> no period where legacy JSON stays loadable while KXM writes YAML. Anything still holding
|
|
48
|
+
> legacy state is refused rather than served from both shapes.
|
|
49
|
+
|
|
35
50
|
When Phase 8 activates hub KXM, at least one hub transition release provides:
|
|
36
51
|
|
|
37
52
|
- current `mesh_*` peer tools;
|
|
@@ -58,6 +73,12 @@ kxm migrate verify
|
|
|
58
73
|
|
|
59
74
|
`kxm init` invokes the planning flow when it detects legacy state.
|
|
60
75
|
|
|
76
|
+
> **Superseded (2026-09-20).** The single-operator decision removed every schema
|
|
77
|
+
> migration lane, so "database/WAL migration … remain later-phase work" below is
|
|
78
|
+
> no longer the plan: there will be none. The `kxm migrate` commands described on
|
|
79
|
+
> this page are deleted in the follow-up cut, and a tree still holding legacy JSON
|
|
80
|
+
> fails closed at load instead of being converted.
|
|
81
|
+
|
|
61
82
|
**Implementation status (Phase 1 slice):** the commands above are implemented
|
|
62
83
|
for **configuration migration only** — legacy `agents.json`, `gates.json`, and
|
|
63
84
|
workflow-definition JSON under `.kxm/config/`. Database/WAL migration,
|
|
@@ -120,6 +141,18 @@ installed resource bytes against the receipt. It performs no writes.
|
|
|
120
141
|
|
|
121
142
|
## Database migration
|
|
122
143
|
|
|
144
|
+
> **Superseded in part (2026-09-20).** What the code guarantees today is narrower than the
|
|
145
|
+
> checklist below and belongs to two different moments:
|
|
146
|
+
>
|
|
147
|
+
> - **Opening a store** verifies `user_version`, refuses a newer-than-known version, and
|
|
148
|
+
> enables WAL. That is implemented.
|
|
149
|
+
> - **Backing a store up** is where checkpointing, `-wal` handling, integrity verification,
|
|
150
|
+
> and hash recording happen — see the whole-state-set recipe in
|
|
151
|
+
> [`docs/operations.md`](../operations.md). Those are **not** properties of an ordinary open.
|
|
152
|
+
>
|
|
153
|
+
> The legacy-record import paragraphs describe a cutover that will not happen: this build
|
|
154
|
+
> migrates no database, and an older stamp is refused outright.
|
|
155
|
+
|
|
123
156
|
Before any database operation:
|
|
124
157
|
|
|
125
158
|
- verify SQLite `user_version`;
|
|
@@ -211,6 +244,11 @@ workflow history.
|
|
|
211
244
|
|
|
212
245
|
## Removal gate
|
|
213
246
|
|
|
247
|
+
> **Superseded (2026-09-20).** The operator decided removal happens **without** a
|
|
248
|
+
> compatibility release, so the list below no longer gates removal — it is kept only
|
|
249
|
+
> to record why the gate existed. Legacy readers are being deleted now, and old names
|
|
250
|
+
> fail closed by brake rather than alias.
|
|
251
|
+
|
|
214
252
|
Legacy readers and command aliases are removed only after:
|
|
215
253
|
|
|
216
254
|
- at least one compatibility release;
|
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.53",
|
|
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",
|
package/plugins/kxm/dist/cli.js
CHANGED
|
@@ -23767,20 +23767,11 @@ function openDatabase(file, description, spec) {
|
|
|
23767
23767
|
database.exec(spec.schema);
|
|
23768
23768
|
database.exec(`PRAGMA user_version = ${spec.version}`);
|
|
23769
23769
|
} else if (version < spec.version) {
|
|
23770
|
-
|
|
23771
|
-
|
|
23772
|
-
|
|
23773
|
-
|
|
23774
|
-
|
|
23775
|
-
"runtime_schema_outdated",
|
|
23776
|
-
file,
|
|
23777
|
-
`${description} schema version ${version} is older than ${spec.version}; no migration lane, backup and restore remain E6`
|
|
23778
|
-
);
|
|
23779
|
-
}
|
|
23780
|
-
step.migrate(database);
|
|
23781
|
-
currentVersion = step.toVersion;
|
|
23782
|
-
database.exec(`PRAGMA user_version = ${currentVersion}`);
|
|
23783
|
-
}
|
|
23770
|
+
throw databaseError(
|
|
23771
|
+
"runtime_schema_outdated",
|
|
23772
|
+
file,
|
|
23773
|
+
`${description} is schema version ${version}; this build requires ${spec.version}. Delete the state file (or re-run \`kxm init\`) to start fresh \u2014 upgrading old state in place is deliberately unsupported`
|
|
23774
|
+
);
|
|
23784
23775
|
}
|
|
23785
23776
|
if (spec.tables) {
|
|
23786
23777
|
verifyExpectedTables(database, file, description, spec.tables);
|
|
@@ -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.
|
|
17124
|
+
var VERSION = "0.7.53";
|
|
17125
17125
|
var inbox = /* @__PURE__ */ new Map();
|
|
17126
17126
|
var notifiedInbox = /* @__PURE__ */ new Set();
|
|
17127
17127
|
var meshClient;
|
|
@@ -17612,20 +17612,11 @@ function openDatabase(file, description, spec) {
|
|
|
17612
17612
|
database.exec(spec.schema);
|
|
17613
17613
|
database.exec(`PRAGMA user_version = ${spec.version}`);
|
|
17614
17614
|
} else if (version < spec.version) {
|
|
17615
|
-
|
|
17616
|
-
|
|
17617
|
-
|
|
17618
|
-
|
|
17619
|
-
|
|
17620
|
-
"runtime_schema_outdated",
|
|
17621
|
-
file,
|
|
17622
|
-
`${description} schema version ${version} is older than ${spec.version}; no migration lane, backup and restore remain E6`
|
|
17623
|
-
);
|
|
17624
|
-
}
|
|
17625
|
-
step.migrate(database);
|
|
17626
|
-
currentVersion = step.toVersion;
|
|
17627
|
-
database.exec(`PRAGMA user_version = ${currentVersion}`);
|
|
17628
|
-
}
|
|
17615
|
+
throw databaseError(
|
|
17616
|
+
"runtime_schema_outdated",
|
|
17617
|
+
file,
|
|
17618
|
+
`${description} is schema version ${version}; this build requires ${spec.version}. Delete the state file (or re-run \`kxm init\`) to start fresh \u2014 upgrading old state in place is deliberately unsupported`
|
|
17619
|
+
);
|
|
17629
17620
|
}
|
|
17630
17621
|
if (spec.tables) {
|
|
17631
17622
|
verifyExpectedTables(database, file, description, spec.tables);
|
|
@@ -18270,71 +18261,6 @@ CREATE TABLE project_controls (
|
|
|
18270
18261
|
record TEXT NOT NULL
|
|
18271
18262
|
) STRICT;
|
|
18272
18263
|
`;
|
|
18273
|
-
var COORDINATOR_INTAKE_DDL = `
|
|
18274
|
-
CREATE TABLE IF NOT EXISTS coordinators (
|
|
18275
|
-
coordinator_id TEXT PRIMARY KEY,
|
|
18276
|
-
project_id TEXT NOT NULL,
|
|
18277
|
-
role TEXT NOT NULL,
|
|
18278
|
-
channel TEXT NOT NULL,
|
|
18279
|
-
ceiling_hash TEXT NOT NULL,
|
|
18280
|
-
config_revision TEXT NOT NULL,
|
|
18281
|
-
bound_at TEXT NOT NULL,
|
|
18282
|
-
schema TEXT NOT NULL,
|
|
18283
|
-
record TEXT NOT NULL
|
|
18284
|
-
) STRICT;
|
|
18285
|
-
CREATE UNIQUE INDEX IF NOT EXISTS coordinators_slot ON coordinators(project_id, role, channel);
|
|
18286
|
-
CREATE TABLE IF NOT EXISTS intake_messages (
|
|
18287
|
-
message_id TEXT PRIMARY KEY,
|
|
18288
|
-
project_id TEXT NOT NULL,
|
|
18289
|
-
coordinator_id TEXT NOT NULL,
|
|
18290
|
-
idempotency_key TEXT NOT NULL,
|
|
18291
|
-
content_hash TEXT NOT NULL,
|
|
18292
|
-
received_at TEXT NOT NULL,
|
|
18293
|
-
dispatch_state TEXT NOT NULL CHECK (dispatch_state IN ('ready','held_paused','admitted','refused')),
|
|
18294
|
-
schema TEXT NOT NULL,
|
|
18295
|
-
record TEXT NOT NULL
|
|
18296
|
-
) STRICT;
|
|
18297
|
-
CREATE UNIQUE INDEX IF NOT EXISTS intake_idempotency ON intake_messages(project_id, coordinator_id, idempotency_key);
|
|
18298
|
-
CREATE INDEX IF NOT EXISTS intake_dispatch ON intake_messages(project_id, dispatch_state, received_at, message_id);
|
|
18299
|
-
CREATE TABLE IF NOT EXISTS project_controls (
|
|
18300
|
-
project_id TEXT PRIMARY KEY,
|
|
18301
|
-
paused INTEGER NOT NULL CHECK (paused IN (0, 1)),
|
|
18302
|
-
reason TEXT,
|
|
18303
|
-
updated_at TEXT NOT NULL,
|
|
18304
|
-
actor TEXT NOT NULL,
|
|
18305
|
-
schema TEXT NOT NULL,
|
|
18306
|
-
record TEXT NOT NULL
|
|
18307
|
-
) STRICT;
|
|
18308
|
-
`;
|
|
18309
|
-
var DRIVE_RECEIPTS_DDL = `
|
|
18310
|
-
CREATE TABLE IF NOT EXISTS drive_receipts (
|
|
18311
|
-
drive_id TEXT PRIMARY KEY,
|
|
18312
|
-
run_id TEXT NOT NULL,
|
|
18313
|
-
project_id TEXT NOT NULL,
|
|
18314
|
-
opened_sequence INTEGER NOT NULL,
|
|
18315
|
-
last_sequence INTEGER NOT NULL,
|
|
18316
|
-
closed_at TEXT NOT NULL,
|
|
18317
|
-
schema TEXT NOT NULL,
|
|
18318
|
-
receipt TEXT NOT NULL
|
|
18319
|
-
) STRICT;
|
|
18320
|
-
CREATE INDEX IF NOT EXISTS drive_receipts_run ON drive_receipts(run_id);
|
|
18321
|
-
`;
|
|
18322
|
-
var EVENT_STORE_MIGRATIONS = [
|
|
18323
|
-
{
|
|
18324
|
-
fromVersion: 3,
|
|
18325
|
-
toVersion: 4,
|
|
18326
|
-
migrate(database) {
|
|
18327
|
-
database.exec(DRIVE_RECEIPTS_DDL);
|
|
18328
|
-
}
|
|
18329
|
-
},
|
|
18330
|
-
{
|
|
18331
|
-
fromVersion: 4,
|
|
18332
|
-
toVersion: 5,
|
|
18333
|
-
migrate(database) {
|
|
18334
|
-
database.exec(COORDINATOR_INTAKE_DDL);
|
|
18335
|
-
}
|
|
18336
|
-
}
|
|
18337
|
-
];
|
|
18338
18264
|
var KxmRunEventStore = class {
|
|
18339
18265
|
path;
|
|
18340
18266
|
database;
|
|
@@ -18343,8 +18269,7 @@ var KxmRunEventStore = class {
|
|
|
18343
18269
|
this.database = openDatabase(this.path, "run event store", {
|
|
18344
18270
|
schema: EVENT_STORE_SCHEMA,
|
|
18345
18271
|
version: KXM_EVENT_STORE_SCHEMA_VERSION,
|
|
18346
|
-
tables: EVENT_STORE_TABLES
|
|
18347
|
-
migrations: EVENT_STORE_MIGRATIONS
|
|
18272
|
+
tables: EVENT_STORE_TABLES
|
|
18348
18273
|
});
|
|
18349
18274
|
}
|
|
18350
18275
|
close() {
|
|
@@ -17783,20 +17783,11 @@ function openDatabase(file, description, spec) {
|
|
|
17783
17783
|
database.exec(spec.schema);
|
|
17784
17784
|
database.exec(`PRAGMA user_version = ${spec.version}`);
|
|
17785
17785
|
} else if (version < spec.version) {
|
|
17786
|
-
|
|
17787
|
-
|
|
17788
|
-
|
|
17789
|
-
|
|
17790
|
-
|
|
17791
|
-
"runtime_schema_outdated",
|
|
17792
|
-
file,
|
|
17793
|
-
`${description} schema version ${version} is older than ${spec.version}; no migration lane, backup and restore remain E6`
|
|
17794
|
-
);
|
|
17795
|
-
}
|
|
17796
|
-
step.migrate(database);
|
|
17797
|
-
currentVersion = step.toVersion;
|
|
17798
|
-
database.exec(`PRAGMA user_version = ${currentVersion}`);
|
|
17799
|
-
}
|
|
17786
|
+
throw databaseError(
|
|
17787
|
+
"runtime_schema_outdated",
|
|
17788
|
+
file,
|
|
17789
|
+
`${description} is schema version ${version}; this build requires ${spec.version}. Delete the state file (or re-run \`kxm init\`) to start fresh \u2014 upgrading old state in place is deliberately unsupported`
|
|
17790
|
+
);
|
|
17800
17791
|
}
|
|
17801
17792
|
if (spec.tables) {
|
|
17802
17793
|
verifyExpectedTables(database, file, description, spec.tables);
|
|
@@ -18710,71 +18701,6 @@ CREATE TABLE project_controls (
|
|
|
18710
18701
|
record TEXT NOT NULL
|
|
18711
18702
|
) STRICT;
|
|
18712
18703
|
`;
|
|
18713
|
-
var COORDINATOR_INTAKE_DDL = `
|
|
18714
|
-
CREATE TABLE IF NOT EXISTS coordinators (
|
|
18715
|
-
coordinator_id TEXT PRIMARY KEY,
|
|
18716
|
-
project_id TEXT NOT NULL,
|
|
18717
|
-
role TEXT NOT NULL,
|
|
18718
|
-
channel TEXT NOT NULL,
|
|
18719
|
-
ceiling_hash TEXT NOT NULL,
|
|
18720
|
-
config_revision TEXT NOT NULL,
|
|
18721
|
-
bound_at TEXT NOT NULL,
|
|
18722
|
-
schema TEXT NOT NULL,
|
|
18723
|
-
record TEXT NOT NULL
|
|
18724
|
-
) STRICT;
|
|
18725
|
-
CREATE UNIQUE INDEX IF NOT EXISTS coordinators_slot ON coordinators(project_id, role, channel);
|
|
18726
|
-
CREATE TABLE IF NOT EXISTS intake_messages (
|
|
18727
|
-
message_id TEXT PRIMARY KEY,
|
|
18728
|
-
project_id TEXT NOT NULL,
|
|
18729
|
-
coordinator_id TEXT NOT NULL,
|
|
18730
|
-
idempotency_key TEXT NOT NULL,
|
|
18731
|
-
content_hash TEXT NOT NULL,
|
|
18732
|
-
received_at TEXT NOT NULL,
|
|
18733
|
-
dispatch_state TEXT NOT NULL CHECK (dispatch_state IN ('ready','held_paused','admitted','refused')),
|
|
18734
|
-
schema TEXT NOT NULL,
|
|
18735
|
-
record TEXT NOT NULL
|
|
18736
|
-
) STRICT;
|
|
18737
|
-
CREATE UNIQUE INDEX IF NOT EXISTS intake_idempotency ON intake_messages(project_id, coordinator_id, idempotency_key);
|
|
18738
|
-
CREATE INDEX IF NOT EXISTS intake_dispatch ON intake_messages(project_id, dispatch_state, received_at, message_id);
|
|
18739
|
-
CREATE TABLE IF NOT EXISTS project_controls (
|
|
18740
|
-
project_id TEXT PRIMARY KEY,
|
|
18741
|
-
paused INTEGER NOT NULL CHECK (paused IN (0, 1)),
|
|
18742
|
-
reason TEXT,
|
|
18743
|
-
updated_at TEXT NOT NULL,
|
|
18744
|
-
actor TEXT NOT NULL,
|
|
18745
|
-
schema TEXT NOT NULL,
|
|
18746
|
-
record TEXT NOT NULL
|
|
18747
|
-
) STRICT;
|
|
18748
|
-
`;
|
|
18749
|
-
var DRIVE_RECEIPTS_DDL = `
|
|
18750
|
-
CREATE TABLE IF NOT EXISTS drive_receipts (
|
|
18751
|
-
drive_id TEXT PRIMARY KEY,
|
|
18752
|
-
run_id TEXT NOT NULL,
|
|
18753
|
-
project_id TEXT NOT NULL,
|
|
18754
|
-
opened_sequence INTEGER NOT NULL,
|
|
18755
|
-
last_sequence INTEGER NOT NULL,
|
|
18756
|
-
closed_at TEXT NOT NULL,
|
|
18757
|
-
schema TEXT NOT NULL,
|
|
18758
|
-
receipt TEXT NOT NULL
|
|
18759
|
-
) STRICT;
|
|
18760
|
-
CREATE INDEX IF NOT EXISTS drive_receipts_run ON drive_receipts(run_id);
|
|
18761
|
-
`;
|
|
18762
|
-
var EVENT_STORE_MIGRATIONS = [
|
|
18763
|
-
{
|
|
18764
|
-
fromVersion: 3,
|
|
18765
|
-
toVersion: 4,
|
|
18766
|
-
migrate(database) {
|
|
18767
|
-
database.exec(DRIVE_RECEIPTS_DDL);
|
|
18768
|
-
}
|
|
18769
|
-
},
|
|
18770
|
-
{
|
|
18771
|
-
fromVersion: 4,
|
|
18772
|
-
toVersion: 5,
|
|
18773
|
-
migrate(database) {
|
|
18774
|
-
database.exec(COORDINATOR_INTAKE_DDL);
|
|
18775
|
-
}
|
|
18776
|
-
}
|
|
18777
|
-
];
|
|
18778
18704
|
var KxmRunEventStore = class {
|
|
18779
18705
|
path;
|
|
18780
18706
|
database;
|
|
@@ -18783,8 +18709,7 @@ var KxmRunEventStore = class {
|
|
|
18783
18709
|
this.database = openDatabase(this.path, "run event store", {
|
|
18784
18710
|
schema: EVENT_STORE_SCHEMA,
|
|
18785
18711
|
version: KXM_EVENT_STORE_SCHEMA_VERSION,
|
|
18786
|
-
tables: EVENT_STORE_TABLES
|
|
18787
|
-
migrations: EVENT_STORE_MIGRATIONS
|
|
18712
|
+
tables: EVENT_STORE_TABLES
|
|
18788
18713
|
});
|
|
18789
18714
|
}
|
|
18790
18715
|
close() {
|
|
@@ -11504,20 +11504,11 @@ function openDatabase(file, description, spec) {
|
|
|
11504
11504
|
database.exec(spec.schema);
|
|
11505
11505
|
database.exec(`PRAGMA user_version = ${spec.version}`);
|
|
11506
11506
|
} else if (version < spec.version) {
|
|
11507
|
-
|
|
11508
|
-
|
|
11509
|
-
|
|
11510
|
-
|
|
11511
|
-
|
|
11512
|
-
"runtime_schema_outdated",
|
|
11513
|
-
file,
|
|
11514
|
-
`${description} schema version ${version} is older than ${spec.version}; no migration lane, backup and restore remain E6`
|
|
11515
|
-
);
|
|
11516
|
-
}
|
|
11517
|
-
step.migrate(database);
|
|
11518
|
-
currentVersion = step.toVersion;
|
|
11519
|
-
database.exec(`PRAGMA user_version = ${currentVersion}`);
|
|
11520
|
-
}
|
|
11507
|
+
throw databaseError(
|
|
11508
|
+
"runtime_schema_outdated",
|
|
11509
|
+
file,
|
|
11510
|
+
`${description} is schema version ${version}; this build requires ${spec.version}. Delete the state file (or re-run \`kxm init\`) to start fresh \u2014 upgrading old state in place is deliberately unsupported`
|
|
11511
|
+
);
|
|
11521
11512
|
}
|
|
11522
11513
|
if (spec.tables) {
|
|
11523
11514
|
verifyExpectedTables(database, file, description, spec.tables);
|
|
@@ -11683,100 +11674,10 @@ var HUB_STORE_SCHEMA_V3 = `
|
|
|
11683
11674
|
) STRICT;
|
|
11684
11675
|
CREATE INDEX IF NOT EXISTS context_items_project ON context_items(project);
|
|
11685
11676
|
`;
|
|
11686
|
-
var HUB_STORE_MIGRATIONS = Object.freeze([
|
|
11687
|
-
{
|
|
11688
|
-
fromVersion: 1,
|
|
11689
|
-
toVersion: 2,
|
|
11690
|
-
migrate: (db) => {
|
|
11691
|
-
db.exec(`
|
|
11692
|
-
CREATE TABLE IF NOT EXISTS agents (
|
|
11693
|
-
id TEXT PRIMARY KEY,
|
|
11694
|
-
record TEXT NOT NULL
|
|
11695
|
-
) STRICT;
|
|
11696
|
-
CREATE TABLE IF NOT EXISTS messages (
|
|
11697
|
-
id TEXT PRIMARY KEY,
|
|
11698
|
-
record TEXT NOT NULL
|
|
11699
|
-
) STRICT;
|
|
11700
|
-
CREATE TABLE IF NOT EXISTS workflow_runs (
|
|
11701
|
-
id TEXT PRIMARY KEY,
|
|
11702
|
-
definition_id TEXT NOT NULL,
|
|
11703
|
-
delivery_id TEXT NOT NULL,
|
|
11704
|
-
record TEXT NOT NULL,
|
|
11705
|
-
UNIQUE(definition_id, delivery_id)
|
|
11706
|
-
) STRICT;
|
|
11707
|
-
CREATE TABLE IF NOT EXISTS workflow_journal (
|
|
11708
|
-
id TEXT PRIMARY KEY,
|
|
11709
|
-
run_id TEXT NOT NULL,
|
|
11710
|
-
category TEXT NOT NULL,
|
|
11711
|
-
area TEXT NOT NULL,
|
|
11712
|
-
record TEXT NOT NULL
|
|
11713
|
-
) STRICT;
|
|
11714
|
-
CREATE INDEX IF NOT EXISTS workflow_journal_run_id ON workflow_journal(run_id);
|
|
11715
|
-
`);
|
|
11716
|
-
}
|
|
11717
|
-
},
|
|
11718
|
-
{
|
|
11719
|
-
fromVersion: 2,
|
|
11720
|
-
toVersion: 3,
|
|
11721
|
-
migrate: (db) => {
|
|
11722
|
-
db.exec(`
|
|
11723
|
-
CREATE TABLE IF NOT EXISTS agents (
|
|
11724
|
-
id TEXT PRIMARY KEY,
|
|
11725
|
-
record TEXT NOT NULL
|
|
11726
|
-
) STRICT;
|
|
11727
|
-
CREATE TABLE IF NOT EXISTS messages (
|
|
11728
|
-
id TEXT PRIMARY KEY,
|
|
11729
|
-
record TEXT NOT NULL
|
|
11730
|
-
) STRICT;
|
|
11731
|
-
CREATE TABLE IF NOT EXISTS workflow_runs (
|
|
11732
|
-
id TEXT PRIMARY KEY,
|
|
11733
|
-
definition_id TEXT NOT NULL,
|
|
11734
|
-
delivery_id TEXT NOT NULL,
|
|
11735
|
-
record TEXT NOT NULL,
|
|
11736
|
-
UNIQUE(definition_id, delivery_id)
|
|
11737
|
-
) STRICT;
|
|
11738
|
-
CREATE TABLE IF NOT EXISTS workflow_journal (
|
|
11739
|
-
id TEXT PRIMARY KEY,
|
|
11740
|
-
run_id TEXT NOT NULL,
|
|
11741
|
-
category TEXT NOT NULL,
|
|
11742
|
-
area TEXT NOT NULL,
|
|
11743
|
-
record TEXT NOT NULL
|
|
11744
|
-
) STRICT;
|
|
11745
|
-
CREATE INDEX IF NOT EXISTS workflow_journal_run_id ON workflow_journal(run_id);
|
|
11746
|
-
CREATE TABLE IF NOT EXISTS context_items (
|
|
11747
|
-
id TEXT PRIMARY KEY,
|
|
11748
|
-
project TEXT NOT NULL,
|
|
11749
|
-
kind TEXT NOT NULL,
|
|
11750
|
-
record TEXT NOT NULL
|
|
11751
|
-
) STRICT;
|
|
11752
|
-
CREATE INDEX IF NOT EXISTS context_items_project ON context_items(project);
|
|
11753
|
-
CREATE UNIQUE INDEX IF NOT EXISTS messages_from_idempotency
|
|
11754
|
-
ON messages(
|
|
11755
|
-
json_extract(record, '$.from'),
|
|
11756
|
-
json_extract(record, '$.idempotencyKey')
|
|
11757
|
-
) WHERE json_extract(record, '$.idempotencyKey') IS NOT NULL;
|
|
11758
|
-
CREATE INDEX IF NOT EXISTS messages_to_seq
|
|
11759
|
-
ON messages(
|
|
11760
|
-
json_extract(record, '$.to'),
|
|
11761
|
-
COALESCE(json_extract(record, '$.seq'), 0)
|
|
11762
|
-
);
|
|
11763
|
-
CREATE TABLE IF NOT EXISTS consumer_cursors (
|
|
11764
|
-
agent_id TEXT PRIMARY KEY,
|
|
11765
|
-
cursor INTEGER NOT NULL
|
|
11766
|
-
) STRICT;
|
|
11767
|
-
CREATE TABLE IF NOT EXISTS agent_sequences (
|
|
11768
|
-
agent_id TEXT PRIMARY KEY,
|
|
11769
|
-
next_seq INTEGER NOT NULL
|
|
11770
|
-
) STRICT;
|
|
11771
|
-
`);
|
|
11772
|
-
}
|
|
11773
|
-
}
|
|
11774
|
-
]);
|
|
11775
11677
|
var HUB_STORE_SCHEMA_SPEC = Object.freeze({
|
|
11776
11678
|
schema: HUB_STORE_SCHEMA_V3,
|
|
11777
11679
|
version: HUB_STORE_SCHEMA_VERSION,
|
|
11778
|
-
tables: HUB_STORE_TABLES
|
|
11779
|
-
migrations: HUB_STORE_MIGRATIONS
|
|
11680
|
+
tables: HUB_STORE_TABLES
|
|
11780
11681
|
});
|
|
11781
11682
|
var MessageMap = class extends Map {
|
|
11782
11683
|
store;
|
package/plugins/kxm/package.json
CHANGED
|
@@ -14,17 +14,10 @@ import { basename, dirname, join, resolve } from "node:path";
|
|
|
14
14
|
import { DatabaseSync } from "./sqlite.ts";
|
|
15
15
|
import { KxmConfigError, type KxmConfigIssue } from "./project-config.ts";
|
|
16
16
|
|
|
17
|
-
export interface DatabaseMigrationStep {
|
|
18
|
-
fromVersion: number;
|
|
19
|
-
toVersion: number;
|
|
20
|
-
migrate: (database: DatabaseSync) => void;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
17
|
export interface DatabaseSchemaSpec {
|
|
24
18
|
schema: string;
|
|
25
19
|
version: number;
|
|
26
20
|
tables?: Readonly<Record<string, readonly string[]>>;
|
|
27
|
-
migrations?: readonly DatabaseMigrationStep[];
|
|
28
21
|
timeoutMs?: number;
|
|
29
22
|
}
|
|
30
23
|
|
|
@@ -179,20 +172,15 @@ export function openDatabase(file: string, description: string, spec: DatabaseSc
|
|
|
179
172
|
database.exec(spec.schema);
|
|
180
173
|
database.exec(`PRAGMA user_version = ${spec.version}`);
|
|
181
174
|
} else if (version < spec.version) {
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
}
|
|
192
|
-
step.migrate(database);
|
|
193
|
-
currentVersion = step.toVersion;
|
|
194
|
-
database.exec(`PRAGMA user_version = ${currentVersion}`);
|
|
195
|
-
}
|
|
175
|
+
// No migration lanes. This is a single-operator tool: an older database is
|
|
176
|
+
// re-initialised, not upgraded in place, and the code never carries two schema
|
|
177
|
+
// shapes at once. Silently accepting an older file would mean every query has to
|
|
178
|
+
// work against shapes it no longer tests.
|
|
179
|
+
throw databaseError(
|
|
180
|
+
"runtime_schema_outdated",
|
|
181
|
+
file,
|
|
182
|
+
`${description} is schema version ${version}; this build requires ${spec.version}. Delete the state file (or re-run \`kxm init\`) to start fresh — upgrading old state in place is deliberately unsupported`,
|
|
183
|
+
);
|
|
196
184
|
}
|
|
197
185
|
|
|
198
186
|
if (spec.tables) {
|
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
lstatSync,
|
|
17
17
|
} from "node:fs";
|
|
18
18
|
import { dirname, join, resolve } from "node:path";
|
|
19
|
+
import { openDatabase } from "./database.ts";
|
|
19
20
|
import { spawnSync } from "node:child_process";
|
|
20
21
|
|
|
21
22
|
export const EXTERNAL_EFFECT_SCHEMA = "kxm.external-effect-receipt.v1" as const;
|
|
@@ -114,6 +115,33 @@ export function computeEffectKey(
|
|
|
114
115
|
return `eff_${createHash("sha256").update(raw).digest("hex").slice(0, 16)}`;
|
|
115
116
|
}
|
|
116
117
|
|
|
118
|
+
const EXTERNAL_EFFECTS_SCHEMA_VERSION = 1;
|
|
119
|
+
|
|
120
|
+
const EXTERNAL_EFFECTS_DDL = `
|
|
121
|
+
CREATE TABLE IF NOT EXISTS external_effects (
|
|
122
|
+
effect_key TEXT PRIMARY KEY,
|
|
123
|
+
run_id TEXT NOT NULL,
|
|
124
|
+
step_id TEXT NOT NULL,
|
|
125
|
+
attempt_id TEXT NOT NULL,
|
|
126
|
+
action_kind TEXT NOT NULL,
|
|
127
|
+
target_ref TEXT NOT NULL,
|
|
128
|
+
status TEXT NOT NULL,
|
|
129
|
+
payload_hash TEXT NOT NULL,
|
|
130
|
+
receipt_payload TEXT NOT NULL,
|
|
131
|
+
executed_at TEXT NOT NULL,
|
|
132
|
+
last_heartbeat_at TEXT,
|
|
133
|
+
completed_at TEXT
|
|
134
|
+
);
|
|
135
|
+
CREATE INDEX IF NOT EXISTS idx_ext_effects_run ON external_effects(run_id);
|
|
136
|
+
`;
|
|
137
|
+
|
|
138
|
+
const EXTERNAL_EFFECTS_SHAPE = {
|
|
139
|
+
external_effects: [
|
|
140
|
+
"effect_key", "run_id", "step_id", "attempt_id", "action_kind", "target_ref",
|
|
141
|
+
"status", "payload_hash", "receipt_payload", "executed_at", "last_heartbeat_at", "completed_at",
|
|
142
|
+
],
|
|
143
|
+
} as const;
|
|
144
|
+
|
|
117
145
|
export class ExternalEffectsLedger {
|
|
118
146
|
private db: DatabaseSync;
|
|
119
147
|
|
|
@@ -121,33 +149,15 @@ export class ExternalEffectsLedger {
|
|
|
121
149
|
if (dbPath !== ":memory:") {
|
|
122
150
|
mkdirSync(dirname(dbPath), { recursive: true });
|
|
123
151
|
}
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
step_id TEXT NOT NULL,
|
|
134
|
-
attempt_id TEXT NOT NULL,
|
|
135
|
-
action_kind TEXT NOT NULL,
|
|
136
|
-
target_ref TEXT NOT NULL,
|
|
137
|
-
status TEXT NOT NULL,
|
|
138
|
-
payload_hash TEXT NOT NULL,
|
|
139
|
-
receipt_payload TEXT NOT NULL,
|
|
140
|
-
executed_at TEXT NOT NULL,
|
|
141
|
-
last_heartbeat_at TEXT,
|
|
142
|
-
completed_at TEXT
|
|
143
|
-
);
|
|
144
|
-
CREATE INDEX IF NOT EXISTS idx_ext_effects_run ON external_effects(run_id);
|
|
145
|
-
`);
|
|
146
|
-
try {
|
|
147
|
-
this.db.exec(`ALTER TABLE external_effects ADD COLUMN last_heartbeat_at TEXT;`);
|
|
148
|
-
} catch {
|
|
149
|
-
// Column already present in schema
|
|
150
|
-
}
|
|
152
|
+
// Goes through the shared opener, not a bare `new DatabaseSync`: that is what gives this
|
|
153
|
+
// store the same contract as every other one — path checks, WAL, the `user_version` gate
|
|
154
|
+
// that refuses an older stamp outright, and the column-shape check that turns a missing
|
|
155
|
+
// column into `runtime_schema_shape_invalid` here instead of a bare SQL error on first use.
|
|
156
|
+
this.db = openDatabase(dbPath, "external effects ledger", {
|
|
157
|
+
schema: EXTERNAL_EFFECTS_DDL,
|
|
158
|
+
version: EXTERNAL_EFFECTS_SCHEMA_VERSION,
|
|
159
|
+
tables: EXTERNAL_EFFECTS_SHAPE,
|
|
160
|
+
});
|
|
151
161
|
}
|
|
152
162
|
|
|
153
163
|
/**
|
|
@@ -102,20 +102,15 @@ export function kxmCeilingHash(authority: KxmCoordinatorAuthority): string {
|
|
|
102
102
|
}
|
|
103
103
|
|
|
104
104
|
/**
|
|
105
|
-
* Does
|
|
105
|
+
* Does a stored coordinator already express the ceiling being requested?
|
|
106
106
|
*
|
|
107
|
-
*
|
|
108
|
-
*
|
|
109
|
-
*
|
|
110
|
-
*
|
|
111
|
-
* go through here, or an upgrade makes the same row equivalent on lookup and a
|
|
112
|
-
* `coordinator_write_lost` conflict on the race path.
|
|
113
|
-
*
|
|
114
|
-
* A legacy row is returned as stored, so its `ceilingHash` is historical: a
|
|
115
|
-
* caller must not assume every persisted hash uses today's algorithm.
|
|
107
|
+
* Stored fingerprint only. Recomputing the fingerprint over the stored authority — the
|
|
108
|
+
* previous behaviour — existed to tolerate rows written before set canonicalisation, and
|
|
109
|
+
* that compatibility is gone with the single-operator decision: no migration lanes, no
|
|
110
|
+
* second schema or hash shape carried forward. A stale row is re-bound, not forgiven.
|
|
116
111
|
*/
|
|
117
112
|
function ceilingsMatch(stored: KxmCoordinatorRecord, ceilingHash: string): boolean {
|
|
118
|
-
return stored.ceilingHash === ceilingHash
|
|
113
|
+
return stored.ceilingHash === ceilingHash;
|
|
119
114
|
}
|
|
120
115
|
|
|
121
116
|
function normalizeAuthority(authority: KxmCoordinatorAuthority): KxmCoordinatorAuthority {
|
|
@@ -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.53";
|
|
12
12
|
const inbox = new Map<string, MessageRecord>();
|
|
13
13
|
const notifiedInbox = new Set<string>();
|
|
14
14
|
let meshClient: HubClient | undefined;
|
|
@@ -782,74 +782,6 @@ CREATE TABLE project_controls (
|
|
|
782
782
|
) STRICT;
|
|
783
783
|
`;
|
|
784
784
|
|
|
785
|
-
const COORDINATOR_INTAKE_DDL = `
|
|
786
|
-
CREATE TABLE IF NOT EXISTS coordinators (
|
|
787
|
-
coordinator_id TEXT PRIMARY KEY,
|
|
788
|
-
project_id TEXT NOT NULL,
|
|
789
|
-
role TEXT NOT NULL,
|
|
790
|
-
channel TEXT NOT NULL,
|
|
791
|
-
ceiling_hash TEXT NOT NULL,
|
|
792
|
-
config_revision TEXT NOT NULL,
|
|
793
|
-
bound_at TEXT NOT NULL,
|
|
794
|
-
schema TEXT NOT NULL,
|
|
795
|
-
record TEXT NOT NULL
|
|
796
|
-
) STRICT;
|
|
797
|
-
CREATE UNIQUE INDEX IF NOT EXISTS coordinators_slot ON coordinators(project_id, role, channel);
|
|
798
|
-
CREATE TABLE IF NOT EXISTS intake_messages (
|
|
799
|
-
message_id TEXT PRIMARY KEY,
|
|
800
|
-
project_id TEXT NOT NULL,
|
|
801
|
-
coordinator_id TEXT NOT NULL,
|
|
802
|
-
idempotency_key TEXT NOT NULL,
|
|
803
|
-
content_hash TEXT NOT NULL,
|
|
804
|
-
received_at TEXT NOT NULL,
|
|
805
|
-
dispatch_state TEXT NOT NULL CHECK (dispatch_state IN ('ready','held_paused','admitted','refused')),
|
|
806
|
-
schema TEXT NOT NULL,
|
|
807
|
-
record TEXT NOT NULL
|
|
808
|
-
) STRICT;
|
|
809
|
-
CREATE UNIQUE INDEX IF NOT EXISTS intake_idempotency ON intake_messages(project_id, coordinator_id, idempotency_key);
|
|
810
|
-
CREATE INDEX IF NOT EXISTS intake_dispatch ON intake_messages(project_id, dispatch_state, received_at, message_id);
|
|
811
|
-
CREATE TABLE IF NOT EXISTS project_controls (
|
|
812
|
-
project_id TEXT PRIMARY KEY,
|
|
813
|
-
paused INTEGER NOT NULL CHECK (paused IN (0, 1)),
|
|
814
|
-
reason TEXT,
|
|
815
|
-
updated_at TEXT NOT NULL,
|
|
816
|
-
actor TEXT NOT NULL,
|
|
817
|
-
schema TEXT NOT NULL,
|
|
818
|
-
record TEXT NOT NULL
|
|
819
|
-
) STRICT;
|
|
820
|
-
`;
|
|
821
|
-
|
|
822
|
-
const DRIVE_RECEIPTS_DDL = `
|
|
823
|
-
CREATE TABLE IF NOT EXISTS drive_receipts (
|
|
824
|
-
drive_id TEXT PRIMARY KEY,
|
|
825
|
-
run_id TEXT NOT NULL,
|
|
826
|
-
project_id TEXT NOT NULL,
|
|
827
|
-
opened_sequence INTEGER NOT NULL,
|
|
828
|
-
last_sequence INTEGER NOT NULL,
|
|
829
|
-
closed_at TEXT NOT NULL,
|
|
830
|
-
schema TEXT NOT NULL,
|
|
831
|
-
receipt TEXT NOT NULL
|
|
832
|
-
) STRICT;
|
|
833
|
-
CREATE INDEX IF NOT EXISTS drive_receipts_run ON drive_receipts(run_id);
|
|
834
|
-
`;
|
|
835
|
-
|
|
836
|
-
const EVENT_STORE_MIGRATIONS = [
|
|
837
|
-
{
|
|
838
|
-
fromVersion: 3,
|
|
839
|
-
toVersion: 4,
|
|
840
|
-
migrate(database: DatabaseSync): void {
|
|
841
|
-
database.exec(DRIVE_RECEIPTS_DDL);
|
|
842
|
-
},
|
|
843
|
-
},
|
|
844
|
-
{
|
|
845
|
-
fromVersion: 4,
|
|
846
|
-
toVersion: 5,
|
|
847
|
-
migrate(database: DatabaseSync): void {
|
|
848
|
-
database.exec(COORDINATOR_INTAKE_DDL);
|
|
849
|
-
},
|
|
850
|
-
},
|
|
851
|
-
];
|
|
852
|
-
|
|
853
785
|
/** One persisted coordinator identity (`kxm.coordinator.v1`). */
|
|
854
786
|
export interface KxmCoordinatorRow {
|
|
855
787
|
coordinatorId: string;
|
|
@@ -894,7 +826,6 @@ export class KxmRunEventStore {
|
|
|
894
826
|
schema: EVENT_STORE_SCHEMA,
|
|
895
827
|
version: KXM_EVENT_STORE_SCHEMA_VERSION,
|
|
896
828
|
tables: EVENT_STORE_TABLES,
|
|
897
|
-
migrations: EVENT_STORE_MIGRATIONS,
|
|
898
829
|
});
|
|
899
830
|
}
|
|
900
831
|
|
package/plugins/kxm/src/store.ts
CHANGED
|
@@ -6,7 +6,6 @@ import type { WorkflowJournalEntry, WorkflowRun } from "./workflow.ts";
|
|
|
6
6
|
import {
|
|
7
7
|
openDatabase,
|
|
8
8
|
withDatabaseTransaction,
|
|
9
|
-
type DatabaseMigrationStep,
|
|
10
9
|
type DatabaseSchemaSpec,
|
|
11
10
|
} from "./database.ts";
|
|
12
11
|
|
|
@@ -73,101 +72,10 @@ export const HUB_STORE_SCHEMA_V3 = `
|
|
|
73
72
|
CREATE INDEX IF NOT EXISTS context_items_project ON context_items(project);
|
|
74
73
|
`;
|
|
75
74
|
|
|
76
|
-
export const HUB_STORE_MIGRATIONS: readonly DatabaseMigrationStep[] = Object.freeze([
|
|
77
|
-
{
|
|
78
|
-
fromVersion: 1,
|
|
79
|
-
toVersion: 2,
|
|
80
|
-
migrate: (db: DatabaseSync) => {
|
|
81
|
-
db.exec(`
|
|
82
|
-
CREATE TABLE IF NOT EXISTS agents (
|
|
83
|
-
id TEXT PRIMARY KEY,
|
|
84
|
-
record TEXT NOT NULL
|
|
85
|
-
) STRICT;
|
|
86
|
-
CREATE TABLE IF NOT EXISTS messages (
|
|
87
|
-
id TEXT PRIMARY KEY,
|
|
88
|
-
record TEXT NOT NULL
|
|
89
|
-
) STRICT;
|
|
90
|
-
CREATE TABLE IF NOT EXISTS workflow_runs (
|
|
91
|
-
id TEXT PRIMARY KEY,
|
|
92
|
-
definition_id TEXT NOT NULL,
|
|
93
|
-
delivery_id TEXT NOT NULL,
|
|
94
|
-
record TEXT NOT NULL,
|
|
95
|
-
UNIQUE(definition_id, delivery_id)
|
|
96
|
-
) STRICT;
|
|
97
|
-
CREATE TABLE IF NOT EXISTS workflow_journal (
|
|
98
|
-
id TEXT PRIMARY KEY,
|
|
99
|
-
run_id TEXT NOT NULL,
|
|
100
|
-
category TEXT NOT NULL,
|
|
101
|
-
area TEXT NOT NULL,
|
|
102
|
-
record TEXT NOT NULL
|
|
103
|
-
) STRICT;
|
|
104
|
-
CREATE INDEX IF NOT EXISTS workflow_journal_run_id ON workflow_journal(run_id);
|
|
105
|
-
`);
|
|
106
|
-
},
|
|
107
|
-
},
|
|
108
|
-
{
|
|
109
|
-
fromVersion: 2,
|
|
110
|
-
toVersion: 3,
|
|
111
|
-
migrate: (db: DatabaseSync) => {
|
|
112
|
-
db.exec(`
|
|
113
|
-
CREATE TABLE IF NOT EXISTS agents (
|
|
114
|
-
id TEXT PRIMARY KEY,
|
|
115
|
-
record TEXT NOT NULL
|
|
116
|
-
) STRICT;
|
|
117
|
-
CREATE TABLE IF NOT EXISTS messages (
|
|
118
|
-
id TEXT PRIMARY KEY,
|
|
119
|
-
record TEXT NOT NULL
|
|
120
|
-
) STRICT;
|
|
121
|
-
CREATE TABLE IF NOT EXISTS workflow_runs (
|
|
122
|
-
id TEXT PRIMARY KEY,
|
|
123
|
-
definition_id TEXT NOT NULL,
|
|
124
|
-
delivery_id TEXT NOT NULL,
|
|
125
|
-
record TEXT NOT NULL,
|
|
126
|
-
UNIQUE(definition_id, delivery_id)
|
|
127
|
-
) STRICT;
|
|
128
|
-
CREATE TABLE IF NOT EXISTS workflow_journal (
|
|
129
|
-
id TEXT PRIMARY KEY,
|
|
130
|
-
run_id TEXT NOT NULL,
|
|
131
|
-
category TEXT NOT NULL,
|
|
132
|
-
area TEXT NOT NULL,
|
|
133
|
-
record TEXT NOT NULL
|
|
134
|
-
) STRICT;
|
|
135
|
-
CREATE INDEX IF NOT EXISTS workflow_journal_run_id ON workflow_journal(run_id);
|
|
136
|
-
CREATE TABLE IF NOT EXISTS context_items (
|
|
137
|
-
id TEXT PRIMARY KEY,
|
|
138
|
-
project TEXT NOT NULL,
|
|
139
|
-
kind TEXT NOT NULL,
|
|
140
|
-
record TEXT NOT NULL
|
|
141
|
-
) STRICT;
|
|
142
|
-
CREATE INDEX IF NOT EXISTS context_items_project ON context_items(project);
|
|
143
|
-
CREATE UNIQUE INDEX IF NOT EXISTS messages_from_idempotency
|
|
144
|
-
ON messages(
|
|
145
|
-
json_extract(record, '$.from'),
|
|
146
|
-
json_extract(record, '$.idempotencyKey')
|
|
147
|
-
) WHERE json_extract(record, '$.idempotencyKey') IS NOT NULL;
|
|
148
|
-
CREATE INDEX IF NOT EXISTS messages_to_seq
|
|
149
|
-
ON messages(
|
|
150
|
-
json_extract(record, '$.to'),
|
|
151
|
-
COALESCE(json_extract(record, '$.seq'), 0)
|
|
152
|
-
);
|
|
153
|
-
CREATE TABLE IF NOT EXISTS consumer_cursors (
|
|
154
|
-
agent_id TEXT PRIMARY KEY,
|
|
155
|
-
cursor INTEGER NOT NULL
|
|
156
|
-
) STRICT;
|
|
157
|
-
CREATE TABLE IF NOT EXISTS agent_sequences (
|
|
158
|
-
agent_id TEXT PRIMARY KEY,
|
|
159
|
-
next_seq INTEGER NOT NULL
|
|
160
|
-
) STRICT;
|
|
161
|
-
`);
|
|
162
|
-
},
|
|
163
|
-
},
|
|
164
|
-
]);
|
|
165
|
-
|
|
166
75
|
export const HUB_STORE_SCHEMA_SPEC: DatabaseSchemaSpec = Object.freeze({
|
|
167
76
|
schema: HUB_STORE_SCHEMA_V3,
|
|
168
77
|
version: HUB_STORE_SCHEMA_VERSION,
|
|
169
78
|
tables: HUB_STORE_TABLES,
|
|
170
|
-
migrations: HUB_STORE_MIGRATIONS,
|
|
171
79
|
});
|
|
172
80
|
|
|
173
81
|
export interface StoredAgent extends AgentRecord {
|