@byok-sdk/server 0.24.0 → 0.25.0-rc.1
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/README.md +10 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +36 -14
- package/dist/index.js.map +1 -1
- package/dist/sqlite-support.d.ts +20 -0
- package/dist/types.d.ts +3 -4
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -15,7 +15,6 @@ const task = await server.dispatch({
|
|
|
15
15
|
deviceId,
|
|
16
16
|
instruction: 'Find five qualified prospects and draft follow-ups.',
|
|
17
17
|
runtime: 'claude',
|
|
18
|
-
policy: { mode: 'auto' },
|
|
19
18
|
requiredToolsets: ['salesko.prospecting'],
|
|
20
19
|
});
|
|
21
20
|
```
|
|
@@ -59,6 +58,16 @@ explicit adoption. An already-open old writer is not stopped by the version fenc
|
|
|
59
58
|
Preserve rejected databases for separate reconciliation; do not delete history,
|
|
60
59
|
reset cursors or edit version markers to make adoption pass.
|
|
61
60
|
|
|
61
|
+
Every refusal at open is a `SqliteSchemaError` with `foundVersion` and
|
|
62
|
+
`requiredVersion`, so a host can check the file at startup and branch on `code`:
|
|
63
|
+
|
|
64
|
+
- `SQLITE_SCHEMA_UNSUPPORTED`: the file needs an explicit `migration` selector
|
|
65
|
+
(older version) or a newer build (newer version).
|
|
66
|
+
- `SQLITE_MIGRATION_REFUSED`: the selected migration would lose history;
|
|
67
|
+
preserve the file.
|
|
68
|
+
- `SQLITE_SCHEMA_INVALID`: the file contradicts its declared schema or is not a
|
|
69
|
+
BYOK database.
|
|
70
|
+
|
|
62
71
|
```ts
|
|
63
72
|
const server = createByokServer({
|
|
64
73
|
productId,
|
package/dist/index.d.ts
CHANGED
|
@@ -35,7 +35,8 @@ export type { AccessTokenClaims, DeviceRecord, PairingCodeInfo, TenantId, TokenS
|
|
|
35
35
|
export { createHmacTokenSigner } from '@byok-sdk/cloud';
|
|
36
36
|
/** Cutoffs and result of {@link ByokServer.mailbox.collectRetired}, owned by `@byok-sdk/core`. */
|
|
37
37
|
export type { MailboxRetentionInput, MailboxRetentionResult } from '@byok-sdk/core';
|
|
38
|
-
export { SqliteUnavailableError } from './sqlite-support';
|
|
38
|
+
export { SqliteSchemaError, SqliteUnavailableError } from './sqlite-support';
|
|
39
|
+
export type { SqliteSchemaErrorCode } from './sqlite-support';
|
|
39
40
|
export type { RateLimiterOptions } from './rate-limiter';
|
|
40
41
|
export { DEFAULT_TASK_EVENT_BUFFER_LIMIT, DEFAULT_TASK_EVENT_RETENTION_MS } from './relay';
|
|
41
42
|
/** Page size `tasks.list()` uses when the caller names none. */
|
package/dist/index.js
CHANGED
|
@@ -654,6 +654,20 @@ var SqliteUnavailableError = class extends Error {
|
|
|
654
654
|
this.cause = cause;
|
|
655
655
|
}
|
|
656
656
|
};
|
|
657
|
+
var SqliteSchemaError = class extends Error {
|
|
658
|
+
code;
|
|
659
|
+
/** The stored `schema_version`; `undefined` when the file has none. */
|
|
660
|
+
foundVersion;
|
|
661
|
+
/** The schema version this build writes. */
|
|
662
|
+
requiredVersion;
|
|
663
|
+
constructor(code, message, foundVersion, requiredVersion, options) {
|
|
664
|
+
super(message, options);
|
|
665
|
+
this.name = "SqliteSchemaError";
|
|
666
|
+
this.code = code;
|
|
667
|
+
this.foundVersion = foundVersion;
|
|
668
|
+
this.requiredVersion = requiredVersion;
|
|
669
|
+
}
|
|
670
|
+
};
|
|
657
671
|
var MIN_NODE_MAJOR = 22;
|
|
658
672
|
var MIN_NODE_MINOR = 5;
|
|
659
673
|
function isSqliteCapableNodeVersion(nodeVersion) {
|
|
@@ -982,14 +996,24 @@ var SqliteCoordinator = class {
|
|
|
982
996
|
#closePromise;
|
|
983
997
|
constructor(path, migration) {
|
|
984
998
|
this.db = openSqliteDatabase(path);
|
|
999
|
+
let foundVersion;
|
|
1000
|
+
const schemaError = (code, message, options) => new SqliteSchemaError(code, message, foundVersion, SQLITE_SCHEMA_VERSION, options);
|
|
1001
|
+
const assertProjection = (projection) => {
|
|
1002
|
+
try {
|
|
1003
|
+
this.db.prepare(`SELECT ${projection} LIMIT 0`);
|
|
1004
|
+
} catch (error) {
|
|
1005
|
+
throw schemaError("SQLITE_SCHEMA_INVALID", `Missing BYOK SQLite durable authority: SELECT ${projection}`, { cause: error });
|
|
1006
|
+
}
|
|
1007
|
+
};
|
|
985
1008
|
try {
|
|
986
1009
|
this.db.exec("BEGIN IMMEDIATE");
|
|
987
1010
|
try {
|
|
988
1011
|
const hasMetadata = this.db.prepare("SELECT 1 AS present FROM sqlite_master WHERE type = 'table' AND name = 'byok_sqlite_meta'").get();
|
|
989
1012
|
if (hasMetadata !== void 0) {
|
|
990
1013
|
const version = this.db.prepare("SELECT value FROM byok_sqlite_meta WHERE key = 'schema_version'").get();
|
|
1014
|
+
foundVersion = version?.value;
|
|
991
1015
|
if (version?.value !== SQLITE_SCHEMA_VERSION && !(version?.value === "1" && migration === "v1-to-v4" || version?.value === "2" && migration === "v2-to-v4" || version?.value === "3" && migration === "v3-to-v4")) {
|
|
992
|
-
throw
|
|
1016
|
+
throw schemaError("SQLITE_SCHEMA_UNSUPPORTED", `Unsupported BYOK SQLite schema version ${JSON.stringify(version?.value)}; this build requires ${SQLITE_SCHEMA_VERSION}. Stop all writers, back up the database and explicitly select migration: 'v1-to-v4' or 'v2-to-v4' for a receipt-free legacy database without task history, or 'v3-to-v4' for a database without ambiguous claimed identity. Target-v3 migration selectors are no longer supported.`);
|
|
993
1017
|
}
|
|
994
1018
|
for (const projection of [
|
|
995
1019
|
"tenant_id, device_id, next_seq, delivered_seq, acked_seq, updated_at FROM mailbox_cursor",
|
|
@@ -1000,48 +1024,48 @@ var SqliteCoordinator = class {
|
|
|
1000
1024
|
"tenant_id, hash, ref_kind, ref_id, created_at FROM object_reference",
|
|
1001
1025
|
"blob_id, tenant_id, reservation_id, content_hash, byte_size, content_type, uploaded, data FROM blob"
|
|
1002
1026
|
]) {
|
|
1003
|
-
|
|
1027
|
+
assertProjection(projection);
|
|
1004
1028
|
}
|
|
1005
1029
|
if (version?.value === "1" || version?.value === "2") {
|
|
1006
1030
|
for (const table of ["task_attempt", "mailbox_message", "agent_message_admission"]) {
|
|
1007
1031
|
if (this.db.prepare(`SELECT 1 FROM ${table} LIMIT 1`).get() !== void 0) {
|
|
1008
|
-
throw
|
|
1032
|
+
throw schemaError("SQLITE_MIGRATION_REFUSED", "Cannot migrate BYOK SQLite database with task history: historical receipts are unavailable; preserve this database for reconciliation");
|
|
1009
1033
|
}
|
|
1010
1034
|
}
|
|
1011
1035
|
if (this.db.prepare("SELECT 1 FROM mailbox_cursor WHERE next_seq <> 1 OR delivered_seq <> 0 OR acked_seq <> 0 LIMIT 1").get() !== void 0) {
|
|
1012
|
-
throw
|
|
1036
|
+
throw schemaError("SQLITE_MIGRATION_REFUSED", "Cannot migrate BYOK SQLite database with delivery history: historical receipts are unavailable; preserve this database for reconciliation");
|
|
1013
1037
|
}
|
|
1014
1038
|
if (this.db.prepare("SELECT 1 FROM sqlite_master WHERE name = 'request_receipt'").get() !== void 0) {
|
|
1015
|
-
throw
|
|
1039
|
+
throw schemaError("SQLITE_SCHEMA_INVALID", "Unexpected request receipt authority in legacy BYOK SQLite schema");
|
|
1016
1040
|
}
|
|
1017
1041
|
this.db.exec(RECEIPT_SCHEMA);
|
|
1018
1042
|
}
|
|
1019
1043
|
if (version?.value === "1") {
|
|
1020
1044
|
const unexpected = this.db.prepare("SELECT 1 FROM sqlite_master WHERE name = 'device_directory'").get();
|
|
1021
|
-
if (unexpected !== void 0) throw
|
|
1045
|
+
if (unexpected !== void 0) throw schemaError("SQLITE_SCHEMA_INVALID", "Unexpected device directory in BYOK SQLite schema v1");
|
|
1022
1046
|
this.db.exec(DEVICE_SCHEMA);
|
|
1023
1047
|
}
|
|
1024
1048
|
if (version?.value !== SQLITE_SCHEMA_VERSION) {
|
|
1025
1049
|
if (this.db.prepare("SELECT 1 FROM task_attempt WHERE owner_device_id IS NOT NULL AND claimed_runtime IS NULL LIMIT 1").get() !== void 0) {
|
|
1026
|
-
throw
|
|
1050
|
+
throw schemaError("SQLITE_MIGRATION_REFUSED", "Cannot migrate BYOK SQLite database with ambiguous claimed identity: historical harness identities are unavailable; preserve this database for reconciliation");
|
|
1027
1051
|
}
|
|
1028
1052
|
this.db.exec("ALTER TABLE task_attempt ADD COLUMN claimed_harness_id TEXT");
|
|
1029
1053
|
this.db.prepare("UPDATE byok_sqlite_meta SET value = ? WHERE key = 'schema_version'").run(SQLITE_SCHEMA_VERSION);
|
|
1030
1054
|
}
|
|
1031
1055
|
} else {
|
|
1032
1056
|
const existing = this.db.prepare("SELECT 1 FROM sqlite_master WHERE type = 'table' AND name NOT LIKE 'sqlite_%'").get();
|
|
1033
|
-
if (existing !== void 0) throw
|
|
1057
|
+
if (existing !== void 0) throw schemaError("SQLITE_SCHEMA_INVALID", "Existing SQLite database has no BYOK schema metadata");
|
|
1034
1058
|
this.db.exec(SCHEMA);
|
|
1035
1059
|
this.db.exec(RECEIPT_SCHEMA);
|
|
1036
1060
|
this.db.exec(DEVICE_SCHEMA);
|
|
1037
1061
|
this.db.prepare("INSERT INTO byok_sqlite_meta (key, value) VALUES ('schema_version', ?)").run(SQLITE_SCHEMA_VERSION);
|
|
1038
1062
|
}
|
|
1039
|
-
|
|
1040
|
-
|
|
1063
|
+
assertProjection("claimed_harness_id FROM task_attempt");
|
|
1064
|
+
assertProjection("tenant_id, device_id, product_id, machine_id, device_name, device_public_key, proof_key_id, proof_key_epoch, capabilities_json, harnesses_json FROM device_directory");
|
|
1041
1065
|
const receiptColumns = this.db.prepare("PRAGMA table_info(request_receipt)").all();
|
|
1042
1066
|
const expectedColumns = ["tenant_id", "key", "body", "recorded_at"];
|
|
1043
1067
|
if (receiptColumns.length !== expectedColumns.length || receiptColumns.some((column, index) => column.name !== expectedColumns[index] || column.type !== "TEXT" || column.notnull !== 1 || column.pk !== (index < 2 ? index + 1 : 0))) {
|
|
1044
|
-
throw
|
|
1068
|
+
throw schemaError("SQLITE_SCHEMA_INVALID", "Invalid BYOK SQLite request receipt schema");
|
|
1045
1069
|
}
|
|
1046
1070
|
this.db.exec("COMMIT");
|
|
1047
1071
|
} catch (error) {
|
|
@@ -2180,12 +2204,10 @@ function createByokServer(opts) {
|
|
|
2180
2204
|
`device ${deviceId} advertises strict-agent-only; legacy task dispatch is refused before enqueue`
|
|
2181
2205
|
);
|
|
2182
2206
|
}
|
|
2183
|
-
const policy = input.policy ?? { mode: "confirm" };
|
|
2184
2207
|
const runtime = dispatchSelection?.runtimeId ?? input.runtime;
|
|
2185
2208
|
const common = {
|
|
2186
2209
|
...input.harnessId === void 0 ? {} : { harnessId: input.harnessId },
|
|
2187
2210
|
instruction: input.instruction,
|
|
2188
|
-
policy,
|
|
2189
2211
|
...runtime === void 0 ? {} : { runtime },
|
|
2190
2212
|
...dispatchSelection === void 0 ? {} : { dispatchSelection }
|
|
2191
2213
|
};
|
|
@@ -2453,6 +2475,6 @@ function longPollInterval(holdMs) {
|
|
|
2453
2475
|
return Math.max(MIN_LONG_POLL_INTERVAL_MS, Math.min(MAX_LONG_POLL_INTERVAL_MS, derived));
|
|
2454
2476
|
}
|
|
2455
2477
|
|
|
2456
|
-
export { DEFAULT_TASK_EVENT_BUFFER_LIMIT, DEFAULT_TASK_EVENT_RETENTION_MS, DEFAULT_TASK_PAGE_LIMIT, SqliteUnavailableError, SteerRejectedError, createByokServer };
|
|
2478
|
+
export { DEFAULT_TASK_EVENT_BUFFER_LIMIT, DEFAULT_TASK_EVENT_RETENTION_MS, DEFAULT_TASK_PAGE_LIMIT, SqliteSchemaError, SqliteUnavailableError, SteerRejectedError, createByokServer };
|
|
2457
2479
|
//# sourceMappingURL=index.js.map
|
|
2458
2480
|
//# sourceMappingURL=index.js.map
|