@peers-app/peers-sdk 0.25.21 → 0.26.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/dist/contracts/__tests__/builder.test.js +0 -2
- package/dist/contracts/__tests__/contract-provider-router.test.js +51 -0
- package/dist/contracts/__tests__/contract-proxy.test.js +21 -0
- package/dist/contracts/__tests__/contract-resolvers.test.js +84 -0
- package/dist/contracts/__tests__/legacy-removal.test.d.ts +1 -0
- package/dist/contracts/__tests__/legacy-removal.test.js +90 -0
- package/dist/contracts/__tests__/system-contracts.test.js +88 -0
- package/dist/contracts/__tests__/tool-catalog-inventory.test.d.ts +1 -0
- package/dist/contracts/__tests__/tool-catalog-inventory.test.js +44 -0
- package/dist/contracts/__tests__/tool-catalog-service.test.d.ts +1 -0
- package/dist/contracts/__tests__/tool-catalog-service.test.js +446 -0
- package/dist/contracts/__tests__/tool-catalog.test.d.ts +1 -0
- package/dist/contracts/__tests__/tool-catalog.test.js +924 -0
- package/dist/contracts/__tests__/workflow-context.test.d.ts +1 -0
- package/dist/contracts/__tests__/workflow-context.test.js +61 -0
- package/dist/contracts/builder.d.ts +9 -8
- package/dist/contracts/builder.js +34 -16
- package/dist/contracts/contract-provider-router.d.ts +12 -1
- package/dist/contracts/contract-provider-router.js +40 -2
- package/dist/contracts/contract-proxy.d.ts +20 -6
- package/dist/contracts/contract-proxy.js +23 -12
- package/dist/contracts/contract-resolvers.d.ts +22 -3
- package/dist/contracts/contract-resolvers.js +86 -7
- package/dist/contracts/extract.d.ts +7 -0
- package/dist/contracts/extract.js +3 -0
- package/dist/contracts/index.d.ts +11 -4
- package/dist/contracts/index.js +14 -2
- package/dist/contracts/registry.d.ts +3 -0
- package/dist/contracts/registry.js +2 -0
- package/dist/contracts/system/desktop-tools-contract.d.ts +5 -0
- package/dist/contracts/system/desktop-tools-contract.js +88 -0
- package/dist/contracts/system/host-tools-contract.d.ts +5 -0
- package/dist/contracts/system/host-tools-contract.js +76 -0
- package/dist/contracts/system/index.d.ts +3 -0
- package/dist/contracts/system/index.js +3 -0
- package/dist/contracts/system/tool-catalog-contract.d.ts +73 -0
- package/dist/contracts/system/tool-catalog-contract.js +70 -0
- package/dist/contracts/system-contracts.d.ts +3 -1
- package/dist/contracts/system-contracts.js +20 -6
- package/dist/contracts/tool-catalog-identity.d.ts +35 -0
- package/dist/contracts/tool-catalog-identity.js +88 -0
- package/dist/contracts/tool-catalog-migrations.d.ts +12 -0
- package/dist/contracts/tool-catalog-migrations.js +179 -0
- package/dist/contracts/tool-catalog-service.d.ts +30 -0
- package/dist/contracts/tool-catalog-service.js +287 -0
- package/dist/contracts/tool-catalog.d.ts +96 -0
- package/dist/contracts/tool-catalog.js +542 -0
- package/dist/contracts/types.d.ts +7 -5
- package/dist/data/messages.d.ts +3 -3
- package/dist/data/orm/sql.data-source.d.ts +5 -0
- package/dist/data/orm/sql.data-source.js +28 -10
- package/dist/data/orm/sql.data-source.test.d.ts +1 -1
- package/dist/data/orm/sql.data-source.test.js +160 -0
- package/dist/data/orm/table-container.d.ts +14 -0
- package/dist/data/orm/table-container.js +20 -0
- package/dist/data/tool-access-approvals.d.ts +6 -6
- package/dist/data/tool-catalog-fields.d.ts +31 -0
- package/dist/data/tool-catalog-fields.js +67 -0
- package/dist/data/tools.d.ts +38 -10
- package/dist/data/tools.js +31 -8
- package/dist/data/workflow-logs.d.ts +6 -2
- package/dist/data/workflow-logs.js +7 -5
- package/dist/data/workflow-runs.d.ts +6 -6
- package/dist/index.d.ts +0 -2
- package/dist/index.js +1 -3
- package/dist/package-loader/contract-package-loader.d.ts +12 -3
- package/dist/package-loader/contract-package-loader.js +54 -50
- package/dist/package-loader/contract-package-loader.test.d.ts +1 -1
- package/dist/package-loader/contract-package-loader.test.js +172 -13
- package/dist/package-loader/host-authority-loading.test.js +16 -3
- package/dist/package-loader/isolated-runtime.d.ts +14 -2
- package/dist/package-loader/isolated-table-gateway.d.ts +3 -1
- package/dist/package-loader/isolated-table-gateway.js +54 -5
- package/dist/package-loader/isolated-table-gateway.test.js +123 -0
- package/dist/package-loader/package-loader.d.ts +14 -2
- package/dist/package-loader/package-loader.js +184 -62
- package/dist/package-loader/package-loader.test.js +674 -1
- package/dist/package-loader/resolve-consumed-contracts.js +1 -1
- package/dist/system-ids.d.ts +6 -0
- package/dist/system-ids.js +7 -1
- package/dist/types/peers-package.d.ts +3 -2
- package/dist/types/workflow-run-context.d.ts +12 -2
- package/package.json +1 -1
- package/dist/tools/index.d.ts +0 -1
- package/dist/tools/index.js +0 -17
- package/dist/tools/tools-factory.d.ts +0 -5
- package/dist/tools/tools-factory.js +0 -34
|
@@ -6,9 +6,11 @@ exports.sqlValueToJsValue = sqlValueToJsValue;
|
|
|
6
6
|
exports.globalDefaultValues = globalDefaultValues;
|
|
7
7
|
const serial_json_1 = require("../../serial-json");
|
|
8
8
|
const utils_1 = require("../../utils");
|
|
9
|
+
const tool_catalog_fields_1 = require("../tool-catalog-fields");
|
|
9
10
|
const data_query_1 = require("./data-query");
|
|
10
11
|
const data_query_sqlite_1 = require("./data-query.sqlite");
|
|
11
12
|
const types_1 = require("./types");
|
|
13
|
+
/** SQLite-backed table storage with additive schema migration and serialized initialization. */
|
|
12
14
|
class SQLDataSource {
|
|
13
15
|
db;
|
|
14
16
|
metaData;
|
|
@@ -116,7 +118,8 @@ class SQLDataSource {
|
|
|
116
118
|
}
|
|
117
119
|
async ensureTableIsNewest() {
|
|
118
120
|
const now = new Date();
|
|
119
|
-
|
|
121
|
+
// Indexes referencing new columns must wait until the table has been rebuilt.
|
|
122
|
+
await this.db.exec(this.buildTableSql(), []);
|
|
120
123
|
let newestTableDef = this.buildTableSql();
|
|
121
124
|
newestTableDef = newestTableDef.replace(/CREATE TABLE IF NOT EXISTS/, "CREATE TABLE");
|
|
122
125
|
const sqlGetTableRecord = `SELECT * FROM sqlite_master WHERE type = 'table' AND name = '${this.tableName}'`;
|
|
@@ -128,8 +131,8 @@ class SQLDataSource {
|
|
|
128
131
|
const tempTableName = `${this.tableName}_old_${now.getTime()}`;
|
|
129
132
|
const renameSql = `ALTER TABLE "${this.tableName}" RENAME TO "${tempTableName}"`;
|
|
130
133
|
await this.db.exec(renameSql, []);
|
|
131
|
-
//
|
|
132
|
-
await this.
|
|
134
|
+
// The renamed table still owns the old index names until it is dropped.
|
|
135
|
+
await this.db.exec(this.buildTableSql(), []);
|
|
133
136
|
// sql query to copy existing data and set new required fields to default values
|
|
134
137
|
const defaults = this.initRecord();
|
|
135
138
|
const fieldsInsert = this.metaData.fields.map((f) => `"${f.name}"`).join(", ");
|
|
@@ -158,22 +161,30 @@ class SQLDataSource {
|
|
|
158
161
|
// drop old table
|
|
159
162
|
const dropSql = `DROP TABLE IF EXISTS "${tempTableName}"`;
|
|
160
163
|
await this.db.exec(dropSql, []);
|
|
164
|
+
await this.ensureTableAndIndexesExist();
|
|
161
165
|
return true;
|
|
162
166
|
}
|
|
167
|
+
await this.ensureTableAndIndexesExist();
|
|
163
168
|
return false;
|
|
164
169
|
}
|
|
165
170
|
tableBuild;
|
|
171
|
+
/** Share schema initialization; a failed attempt is evicted so later calls can retry. */
|
|
166
172
|
async initTable(force = false) {
|
|
167
173
|
if (!this.tableBuild || force) {
|
|
168
|
-
|
|
174
|
+
const build = this.ensureTableIsNewest().catch((error) => {
|
|
175
|
+
// A failed older forced attempt must not evict a newer initialization.
|
|
176
|
+
if (this.tableBuild === build)
|
|
177
|
+
this.tableBuild = undefined;
|
|
178
|
+
throw error;
|
|
179
|
+
});
|
|
180
|
+
this.tableBuild = build;
|
|
169
181
|
}
|
|
170
182
|
return this.tableBuild;
|
|
171
183
|
}
|
|
172
184
|
async get(id) {
|
|
173
|
-
let record;
|
|
174
185
|
await this.initTable();
|
|
175
186
|
const sqlRecord = await this.db.get(`SELECT * FROM "${this.tableName}" WHERE ${this.metaData.primaryKeyName} = ?`, [id]);
|
|
176
|
-
record = this.sqlRecordToJsRecord(sqlRecord);
|
|
187
|
+
const record = this.sqlRecordToJsRecord(sqlRecord);
|
|
177
188
|
return record;
|
|
178
189
|
}
|
|
179
190
|
async list(filter = {}, opts = {}) {
|
|
@@ -261,11 +272,15 @@ class SQLDataSource {
|
|
|
261
272
|
// @ts-expect-error - need to figure out how to get typescript to understand what's going on here
|
|
262
273
|
record[this.metaData.primaryKeyName] = (0, utils_1.newid)();
|
|
263
274
|
}
|
|
264
|
-
this.schema.parse(record);
|
|
265
275
|
const primaryKey = record[this.metaData.primaryKeyName];
|
|
266
|
-
|
|
276
|
+
// Explicit catalog patches write only their own columns. Keep ordinary row-save
|
|
277
|
+
// semantics (omitted optional fields clear) unchanged for all other writes.
|
|
278
|
+
const catalogPatch = this.tableName === "Tools" && (0, tool_catalog_fields_1.isToolCatalogPatch)(record);
|
|
279
|
+
const updated = catalogPatch ? { ...(await this.get(primaryKey)), ...record } : record;
|
|
280
|
+
this.schema.parse(updated);
|
|
281
|
+
const updateFields = this.fieldsForUpdate(record).filter((field) => !catalogPatch || Object.hasOwn(record, field.name));
|
|
267
282
|
if (updateFields.length === 0) {
|
|
268
|
-
return
|
|
283
|
+
return updated;
|
|
269
284
|
}
|
|
270
285
|
const fields = updateFields.map((field) => `"${field.name}" = ?`).join(",\n");
|
|
271
286
|
const values = updateFields.map((field) => jsValueToSqlValue(record[field.name]));
|
|
@@ -275,7 +290,7 @@ class SQLDataSource {
|
|
|
275
290
|
`WHERE "${this.metaData.primaryKeyName}" = ?`,
|
|
276
291
|
].join("\n");
|
|
277
292
|
await this.db.exec(sql, [...values, primaryKey]);
|
|
278
|
-
return
|
|
293
|
+
return updated;
|
|
279
294
|
}
|
|
280
295
|
async delete(idOrRecord) {
|
|
281
296
|
await this.initTable();
|
|
@@ -476,6 +491,7 @@ class SQLDataSource {
|
|
|
476
491
|
}
|
|
477
492
|
}
|
|
478
493
|
exports.SQLDataSource = SQLDataSource;
|
|
494
|
+
/** Encode a JavaScript field for SQLite parameters or an escaped migration literal. */
|
|
479
495
|
function jsValueToSqlValue(value, escapeQuote = false) {
|
|
480
496
|
if (value === undefined)
|
|
481
497
|
return null;
|
|
@@ -493,6 +509,7 @@ function jsValueToSqlValue(value, escapeQuote = false) {
|
|
|
493
509
|
}
|
|
494
510
|
return value;
|
|
495
511
|
}
|
|
512
|
+
/** Restore a SQLite field according to its declared Peers type. */
|
|
496
513
|
function sqlValueToJsValue(value, fieldType, isArray) {
|
|
497
514
|
if (value === null)
|
|
498
515
|
return undefined;
|
|
@@ -518,6 +535,7 @@ function sqlValueToJsValue(value, fieldType, isArray) {
|
|
|
518
535
|
}
|
|
519
536
|
return value;
|
|
520
537
|
}
|
|
538
|
+
/** Default value used when adding a required field to an existing table. */
|
|
521
539
|
function globalDefaultValues(field) {
|
|
522
540
|
const fieldType = field.type;
|
|
523
541
|
const isArray = field.isArray ?? false;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
import "../../index";
|
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
require("../../index");
|
|
3
4
|
const SQLiteDB = require("better-sqlite3");
|
|
4
5
|
const lodash_1 = require("lodash");
|
|
5
6
|
const zod_1 = require("zod");
|
|
7
|
+
const registry_1 = require("../../contracts/registry");
|
|
8
|
+
const tool_catalog_1 = require("../../contracts/tool-catalog");
|
|
6
9
|
const field_type_1 = require("../../types/field-type");
|
|
7
10
|
const zod_types_1 = require("../../types/zod-types");
|
|
8
11
|
const utils_1 = require("../../utils");
|
|
12
|
+
const tool_catalog_fields_1 = require("../tool-catalog-fields");
|
|
9
13
|
const sql_data_source_1 = require("./sql.data-source");
|
|
14
|
+
const subscribable_data_source_1 = require("./subscribable.data-source");
|
|
10
15
|
const types_1 = require("./types");
|
|
11
16
|
class DBHarness {
|
|
12
17
|
_db = null;
|
|
@@ -427,3 +432,158 @@ describe("table", () => {
|
|
|
427
432
|
});
|
|
428
433
|
});
|
|
429
434
|
});
|
|
435
|
+
describe("Tools additive catalog migration", () => {
|
|
436
|
+
test("catalog readiness retries failed SQLite schema initialization on the same source", async () => {
|
|
437
|
+
const { toolSchema } = await Promise.resolve().then(() => require("../tools"));
|
|
438
|
+
const harness = new DBHarness();
|
|
439
|
+
const source = new sql_data_source_1.SQLDataSource(harness, {
|
|
440
|
+
name: "Tools",
|
|
441
|
+
description: "",
|
|
442
|
+
primaryKeyName: "toolId",
|
|
443
|
+
fields: (0, types_1.schemaToFields)(toolSchema),
|
|
444
|
+
indexes: [{ fields: ["contractId", "contractVersion", "toolName"] }],
|
|
445
|
+
versionNumber: 2,
|
|
446
|
+
}, toolSchema);
|
|
447
|
+
const table = new subscribable_data_source_1.SubscribableDataSource(source);
|
|
448
|
+
const catalog = new tool_catalog_1.ToolCatalog({
|
|
449
|
+
dataContextId: (0, utils_1.newid)(),
|
|
450
|
+
tableContainer: { getTable: () => table },
|
|
451
|
+
}, new registry_1.ContractRegistry());
|
|
452
|
+
const exec = jest
|
|
453
|
+
.spyOn(harness, "exec")
|
|
454
|
+
.mockRejectedValueOnce(new Error("schema temporarily unavailable"));
|
|
455
|
+
try {
|
|
456
|
+
const first = catalog.ready();
|
|
457
|
+
expect(catalog.ready()).toBe(first);
|
|
458
|
+
await expect(first).rejects.toThrow("schema temporarily unavailable");
|
|
459
|
+
expect(exec).toHaveBeenCalledTimes(1);
|
|
460
|
+
await catalog.ready();
|
|
461
|
+
const rows = await table.list();
|
|
462
|
+
expect(rows.length).toBeGreaterThan(0);
|
|
463
|
+
expect(rows.every((row) => row.catalogState === "resolved")).toBe(true);
|
|
464
|
+
expect(await harness.all('PRAGMA index_list("Tools")')).toEqual(expect.arrayContaining([
|
|
465
|
+
expect.objectContaining({
|
|
466
|
+
name: expect.stringContaining("_contractId_contractVersion_toolName_"),
|
|
467
|
+
}),
|
|
468
|
+
]));
|
|
469
|
+
expect(catalog.registry.getProviderPackageId(rows[0].contractId, rows[0].contractVersion)).toBeDefined();
|
|
470
|
+
}
|
|
471
|
+
finally {
|
|
472
|
+
await catalog.dispose();
|
|
473
|
+
await harness.close();
|
|
474
|
+
}
|
|
475
|
+
});
|
|
476
|
+
test("an older failed initialization cannot evict a newer forced initialization", async () => {
|
|
477
|
+
const { toolSchema } = await Promise.resolve().then(() => require("../tools"));
|
|
478
|
+
const harness = new DBHarness();
|
|
479
|
+
const source = new sql_data_source_1.SQLDataSource(harness, {
|
|
480
|
+
name: "Tools",
|
|
481
|
+
description: "",
|
|
482
|
+
primaryKeyName: "toolId",
|
|
483
|
+
fields: (0, types_1.schemaToFields)(toolSchema),
|
|
484
|
+
}, toolSchema);
|
|
485
|
+
let reject;
|
|
486
|
+
const exec = jest.spyOn(harness, "exec").mockImplementationOnce(() => new Promise((_, fail) => {
|
|
487
|
+
reject = fail;
|
|
488
|
+
}));
|
|
489
|
+
try {
|
|
490
|
+
const first = source.initTable();
|
|
491
|
+
const failed = expect(first).rejects.toThrow("older failure");
|
|
492
|
+
await source.initTable(true);
|
|
493
|
+
const attempts = exec.mock.calls.length;
|
|
494
|
+
reject(new Error("older failure"));
|
|
495
|
+
await failed;
|
|
496
|
+
await source.initTable();
|
|
497
|
+
expect(exec).toHaveBeenCalledTimes(attempts);
|
|
498
|
+
}
|
|
499
|
+
finally {
|
|
500
|
+
await harness.close();
|
|
501
|
+
}
|
|
502
|
+
});
|
|
503
|
+
test("preserves legacy values and recreates all indexes after table-copy migration", async () => {
|
|
504
|
+
const { toolSchema } = await Promise.resolve().then(() => require("../tools"));
|
|
505
|
+
const harness = new DBHarness();
|
|
506
|
+
const oldSchema = toolSchema.omit({
|
|
507
|
+
contractId: true,
|
|
508
|
+
contractVersion: true,
|
|
509
|
+
toolName: true,
|
|
510
|
+
catalogIdentity: true,
|
|
511
|
+
catalogState: true,
|
|
512
|
+
catalogReason: true,
|
|
513
|
+
});
|
|
514
|
+
const metadata = {
|
|
515
|
+
name: "Tools",
|
|
516
|
+
description: "",
|
|
517
|
+
primaryKeyName: "toolId",
|
|
518
|
+
fields: (0, types_1.schemaToFields)(oldSchema),
|
|
519
|
+
indexes: [{ fields: ["name"] }, { fields: ["isAssistantRunner"] }],
|
|
520
|
+
versionNumber: 1,
|
|
521
|
+
};
|
|
522
|
+
const old = new sql_data_source_1.SQLDataSource(harness, metadata, oldSchema);
|
|
523
|
+
const row = oldSchema.parse({
|
|
524
|
+
toolId: (0, utils_1.newid)(),
|
|
525
|
+
name: "preserve me",
|
|
526
|
+
usageDescription: "legacy",
|
|
527
|
+
code: "executable",
|
|
528
|
+
inputSchema: { type: "none" },
|
|
529
|
+
outputSchema: { type: "none" },
|
|
530
|
+
accessLevel: 70,
|
|
531
|
+
});
|
|
532
|
+
try {
|
|
533
|
+
await old.save(row);
|
|
534
|
+
const upgraded = new sql_data_source_1.SQLDataSource(harness, {
|
|
535
|
+
...metadata,
|
|
536
|
+
fields: (0, types_1.schemaToFields)(toolSchema),
|
|
537
|
+
versionNumber: 2,
|
|
538
|
+
indexes: [...metadata.indexes, { fields: ["contractId", "contractVersion", "toolName"] }],
|
|
539
|
+
}, toolSchema);
|
|
540
|
+
await upgraded.initTable();
|
|
541
|
+
expect(await upgraded.get(row.toolId)).toMatchObject(row);
|
|
542
|
+
// Runtime backfill owns only the new columns. Updating them must work
|
|
543
|
+
// without resending (and potentially overwriting) legacy provider metadata.
|
|
544
|
+
await upgraded.update((0, tool_catalog_fields_1.toolCatalogPatch)({ toolId: row.toolId, catalogState: "unresolved" }));
|
|
545
|
+
expect(await upgraded.get(row.toolId)).toMatchObject({ ...row, catalogState: "unresolved" });
|
|
546
|
+
// A descriptive refresh patch keeps policy; an unmarked full row still replaces it.
|
|
547
|
+
await upgraded.update((0, tool_catalog_fields_1.toolCatalogPatch)({
|
|
548
|
+
toolId: row.toolId,
|
|
549
|
+
usageDescription: "refreshed",
|
|
550
|
+
detailedDescription: "new",
|
|
551
|
+
}));
|
|
552
|
+
expect(await upgraded.get(row.toolId)).toMatchObject({
|
|
553
|
+
...row,
|
|
554
|
+
usageDescription: "refreshed",
|
|
555
|
+
detailedDescription: "new",
|
|
556
|
+
accessLevel: 70,
|
|
557
|
+
});
|
|
558
|
+
const { accessLevel: _policy, ...withoutPolicy } = row;
|
|
559
|
+
await upgraded.update({ ...withoutPolicy, catalogState: "unresolved" });
|
|
560
|
+
expect((await upgraded.get(row.toolId))?.accessLevel).toBeUndefined();
|
|
561
|
+
await upgraded.update({ ...row, catalogState: "unresolved" });
|
|
562
|
+
expect(() => (0, tool_catalog_fields_1.toolCatalogPatch)({ toolId: row.toolId, accessLevel: 0 })).toThrow(/accessLevel/);
|
|
563
|
+
for (const field of ["code", "codeRepository", "packageId"])
|
|
564
|
+
expect(() => (0, tool_catalog_fields_1.toolCatalogPatch)({ toolId: row.toolId, [field]: "historical" })).toThrow(field);
|
|
565
|
+
const indexes = await harness.all('PRAGMA index_list("Tools")');
|
|
566
|
+
expect(indexes
|
|
567
|
+
.filter((index) => !index.name.startsWith("sqlite_autoindex_"))
|
|
568
|
+
.map((index) => index.name)).toEqual(expect.arrayContaining([
|
|
569
|
+
expect.stringContaining("_name_"),
|
|
570
|
+
expect.stringContaining("_isAssistantRunner_"),
|
|
571
|
+
expect.stringContaining("_contractId_contractVersion_toolName_"),
|
|
572
|
+
]));
|
|
573
|
+
expect(indexes
|
|
574
|
+
.filter((index) => index.name.includes("contractId"))
|
|
575
|
+
.every((index) => index.unique === 0)).toBe(true);
|
|
576
|
+
const target = {
|
|
577
|
+
contractId: "00muxgugn6xvs8f2r80ru205t",
|
|
578
|
+
contractVersion: 1,
|
|
579
|
+
toolName: "invoke",
|
|
580
|
+
};
|
|
581
|
+
await upgraded.save({ ...row, ...target, toolId: "00muxoa8xeet2lpi8bdq55q03" });
|
|
582
|
+
await upgraded.save({ ...row, ...target, toolId: "00muxob3hbo3f6g49mipy1hyt" });
|
|
583
|
+
expect(await upgraded.count()).toBe(3);
|
|
584
|
+
}
|
|
585
|
+
finally {
|
|
586
|
+
await harness.close();
|
|
587
|
+
}
|
|
588
|
+
});
|
|
589
|
+
});
|
|
@@ -9,6 +9,7 @@ import { type ITableMetaData } from "./types";
|
|
|
9
9
|
* Logs an error if same version but different definitions (caller should bump versionNumber).
|
|
10
10
|
*/
|
|
11
11
|
export declare function checkVersionedUpdate(existingVersion: number, incomingVersion: number, existingMetaData: ITableMetaData, incomingMetaData: ITableMetaData): "update" | "skip";
|
|
12
|
+
/** Owns code-defined and synced fallback table bindings for one data context. */
|
|
12
13
|
export declare class TableContainer {
|
|
13
14
|
readonly tableFactory: TableFactory;
|
|
14
15
|
readonly groupId?: string | undefined;
|
|
@@ -19,11 +20,21 @@ export declare class TableContainer {
|
|
|
19
20
|
private fallbackInstances;
|
|
20
21
|
private fallbackSubscribed;
|
|
21
22
|
constructor(tableFactory: TableFactory, groupId?: string | undefined);
|
|
23
|
+
/** Register a code-owned table, preserving an existing binding unless overwrite is set. */
|
|
22
24
|
registerTableDefinition<T extends {
|
|
23
25
|
[key: string]: any;
|
|
24
26
|
}>(tableDefinition: ITableDefinition<T>, opts?: {
|
|
25
27
|
overwrite?: boolean;
|
|
26
28
|
}): void;
|
|
29
|
+
/**
|
|
30
|
+
* Validate and stage the entire batch before changing any live table binding.
|
|
31
|
+
* An invalid later definition leaves earlier definitions and instances intact.
|
|
32
|
+
* No table factories or schema persistence run during registration.
|
|
33
|
+
*/
|
|
34
|
+
registerTableDefinitions(definitions: readonly ITableDefinition<any>[], opts?: {
|
|
35
|
+
overwrite?: boolean;
|
|
36
|
+
}): void;
|
|
37
|
+
private stageTableDefinition;
|
|
27
38
|
/**
|
|
28
39
|
* This shouldn't be used unless you have a good reason to bypass the normal table creation process.
|
|
29
40
|
* The normal process is to register the table definition and let the factory create the instance.
|
|
@@ -31,9 +42,11 @@ export declare class TableContainer {
|
|
|
31
42
|
setTable(table: Table<any>, opts?: {
|
|
32
43
|
overwrite?: boolean;
|
|
33
44
|
}): void;
|
|
45
|
+
/** Resolve a table by its full registered name. */
|
|
34
46
|
getTable<T extends {
|
|
35
47
|
[key: string]: any;
|
|
36
48
|
}>(tableName: string): Table<T>;
|
|
49
|
+
/** Lazily create a table from metadata, using a registered constructor when available. */
|
|
37
50
|
getTable<T extends {
|
|
38
51
|
[key: string]: any;
|
|
39
52
|
}>(metaData: ITableMetaData, schema?: z.AnyZodObject, tableConstructor?: TableConstructor<T>): Table<T>;
|
|
@@ -43,6 +56,7 @@ export declare class TableContainer {
|
|
|
43
56
|
* and skips saving the TableDefinitions table itself to avoid recursion.
|
|
44
57
|
*/
|
|
45
58
|
private saveTableDefinition;
|
|
59
|
+
/** Resolve a live, code-defined or synced fallback binding by its full name. */
|
|
46
60
|
getTableByName<T extends {
|
|
47
61
|
[key: string]: any;
|
|
48
62
|
}>(tableName: string): Table<T>;
|
|
@@ -54,6 +54,7 @@ function checkVersionedUpdate(existingVersion, incomingVersion, existingMetaData
|
|
|
54
54
|
// }
|
|
55
55
|
// return 'update'; // incoming is strictly newer
|
|
56
56
|
}
|
|
57
|
+
/** Owns code-defined and synced fallback table bindings for one data context. */
|
|
57
58
|
class TableContainer {
|
|
58
59
|
tableFactory;
|
|
59
60
|
groupId;
|
|
@@ -69,7 +70,25 @@ class TableContainer {
|
|
|
69
70
|
this.tableFactory = tableFactory;
|
|
70
71
|
this.groupId = groupId;
|
|
71
72
|
}
|
|
73
|
+
/** Register a code-owned table, preserving an existing binding unless overwrite is set. */
|
|
72
74
|
registerTableDefinition(tableDefinition, opts) {
|
|
75
|
+
this.registerTableDefinitions([tableDefinition], opts);
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Validate and stage the entire batch before changing any live table binding.
|
|
79
|
+
* An invalid later definition leaves earlier definitions and instances intact.
|
|
80
|
+
* No table factories or schema persistence run during registration.
|
|
81
|
+
*/
|
|
82
|
+
registerTableDefinitions(definitions, opts) {
|
|
83
|
+
const candidate = new TableContainer(this.tableFactory, this.groupId);
|
|
84
|
+
Object.assign(candidate.tableDefinitions, this.tableDefinitions);
|
|
85
|
+
Object.assign(candidate.tableInstances, this.tableInstances);
|
|
86
|
+
for (const definition of definitions)
|
|
87
|
+
candidate.stageTableDefinition(definition, opts);
|
|
88
|
+
Object.assign(this.tableDefinitions, candidate.tableDefinitions);
|
|
89
|
+
this.tableInstances = candidate.tableInstances;
|
|
90
|
+
}
|
|
91
|
+
stageTableDefinition(tableDefinition, opts) {
|
|
73
92
|
const tableId = tableDefinition.metaData.tableId;
|
|
74
93
|
if (!(0, utils_1.isid)(tableId)) {
|
|
75
94
|
throw new Error(`Invalid tableId ${tableId} for table ${tableDefinition.metaData.name}. Table IDs must be valid IDs.`);
|
|
@@ -186,6 +205,7 @@ class TableContainer {
|
|
|
186
205
|
this.savingDefinitions = false;
|
|
187
206
|
}
|
|
188
207
|
}
|
|
208
|
+
/** Resolve a live, code-defined or synced fallback binding by its full name. */
|
|
189
209
|
getTableByName(tableName) {
|
|
190
210
|
// 1. Check for a full instance (from code/packages)
|
|
191
211
|
if (this.tableInstances[tableName]) {
|
|
@@ -47,12 +47,12 @@ export declare const toolAccessApprovalSchema: z.ZodObject<{
|
|
|
47
47
|
nonce: string;
|
|
48
48
|
status: ToolAccessApprovalStatus;
|
|
49
49
|
userId: string;
|
|
50
|
+
toolName: string;
|
|
50
51
|
toolId: string;
|
|
51
52
|
scope: ToolAccessApprovalScope;
|
|
52
53
|
dataContextId: string;
|
|
53
54
|
createdAt: Date;
|
|
54
55
|
expiresAt: Date;
|
|
55
|
-
toolName: string;
|
|
56
56
|
toolAccessApprovalId: string;
|
|
57
57
|
argsHash: string;
|
|
58
58
|
argsPreview: string;
|
|
@@ -61,19 +61,19 @@ export declare const toolAccessApprovalSchema: z.ZodObject<{
|
|
|
61
61
|
};
|
|
62
62
|
requiredAccessLevel: number;
|
|
63
63
|
assistantId?: string | undefined;
|
|
64
|
-
threadMessageId?: string | undefined;
|
|
65
64
|
workflowRunId?: string | undefined;
|
|
66
65
|
workflowLogId?: string | undefined;
|
|
66
|
+
threadMessageId?: string | undefined;
|
|
67
67
|
decidedAt?: Date | undefined;
|
|
68
68
|
executedAt?: Date | undefined;
|
|
69
69
|
resultSummary?: string | undefined;
|
|
70
70
|
}, {
|
|
71
71
|
nonce: string;
|
|
72
72
|
userId: string;
|
|
73
|
+
toolName: string;
|
|
73
74
|
toolId: string;
|
|
74
75
|
dataContextId: string;
|
|
75
76
|
expiresAt: Date;
|
|
76
|
-
toolName: string;
|
|
77
77
|
toolAccessApprovalId: string;
|
|
78
78
|
argsHash: string;
|
|
79
79
|
frozenArgs: {} & {
|
|
@@ -83,9 +83,9 @@ export declare const toolAccessApprovalSchema: z.ZodObject<{
|
|
|
83
83
|
scope?: ToolAccessApprovalScope | undefined;
|
|
84
84
|
assistantId?: string | undefined;
|
|
85
85
|
createdAt?: Date | undefined;
|
|
86
|
-
threadMessageId?: string | undefined;
|
|
87
86
|
workflowRunId?: string | undefined;
|
|
88
87
|
workflowLogId?: string | undefined;
|
|
88
|
+
threadMessageId?: string | undefined;
|
|
89
89
|
argsPreview?: string | undefined;
|
|
90
90
|
requiredAccessLevel?: number | undefined;
|
|
91
91
|
decidedAt?: Date | undefined;
|
|
@@ -103,12 +103,12 @@ export declare function ToolAccessApprovals(dataContext?: DataContext): import("
|
|
|
103
103
|
nonce: string;
|
|
104
104
|
status: ToolAccessApprovalStatus;
|
|
105
105
|
userId: string;
|
|
106
|
+
toolName: string;
|
|
106
107
|
toolId: string;
|
|
107
108
|
scope: ToolAccessApprovalScope;
|
|
108
109
|
dataContextId: string;
|
|
109
110
|
createdAt: Date;
|
|
110
111
|
expiresAt: Date;
|
|
111
|
-
toolName: string;
|
|
112
112
|
toolAccessApprovalId: string;
|
|
113
113
|
argsHash: string;
|
|
114
114
|
argsPreview: string;
|
|
@@ -117,9 +117,9 @@ export declare function ToolAccessApprovals(dataContext?: DataContext): import("
|
|
|
117
117
|
};
|
|
118
118
|
requiredAccessLevel: number;
|
|
119
119
|
assistantId?: string | undefined;
|
|
120
|
-
threadMessageId?: string | undefined;
|
|
121
120
|
workflowRunId?: string | undefined;
|
|
122
121
|
workflowLogId?: string | undefined;
|
|
122
|
+
threadMessageId?: string | undefined;
|
|
123
123
|
decidedAt?: Date | undefined;
|
|
124
124
|
executedAt?: Date | undefined;
|
|
125
125
|
resultSummary?: string | undefined;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/** Catalog-owned identity and state columns. Runtimes before Tools v2 do not know them. */
|
|
2
|
+
export declare const toolCatalogIdentityFields: readonly ["contractId", "contractVersion", "toolName", "catalogIdentity", "catalogState", "catalogReason"];
|
|
3
|
+
/**
|
|
4
|
+
* Presentation fields described by a public contract member. The catalog refreshes
|
|
5
|
+
* them from a published definition when provenance is absent or matches its provider.
|
|
6
|
+
*/
|
|
7
|
+
export declare const toolCatalogContractDescriptiveFields: readonly ["name", "usageDescription", "inputSchema", "outputSchema"];
|
|
8
|
+
/** Supplemental presentation and runner configuration, independent of executable code. */
|
|
9
|
+
export declare const toolCatalogSupplementalFields: readonly ["configSchema", "isAssistantRunner", "detailedDescription"];
|
|
10
|
+
/**
|
|
11
|
+
* Columns a catalog patch may write. Authorization fields (`accessLevel`,
|
|
12
|
+
* `remoteAccessLevel`, `authorityPropagation`) deliberately remain outside this list:
|
|
13
|
+
* catalog backfill and metadata refresh must never clear or rewrite live access policy.
|
|
14
|
+
*/
|
|
15
|
+
export declare const toolCatalogPatchFields: readonly string[];
|
|
16
|
+
/**
|
|
17
|
+
* Mark a restricted Tools update written by the tool catalog. SQL merges a marked
|
|
18
|
+
* update into the stored row and tracked storage records only the paths it changes,
|
|
19
|
+
* so a concurrent policy change cannot be reverted by a stale snapshot. Unmarked
|
|
20
|
+
* updates keep row-replacement semantics: a full save that omits access policy
|
|
21
|
+
* still clears it. Identity is validated against the merged row in both cases.
|
|
22
|
+
*
|
|
23
|
+
* @param patch - Primary key plus fields from {@link toolCatalogPatchFields}.
|
|
24
|
+
* @returns The same object, marked.
|
|
25
|
+
* @throws If the patch names a field outside {@link toolCatalogPatchFields}.
|
|
26
|
+
*/
|
|
27
|
+
export declare function toolCatalogPatch<T extends {
|
|
28
|
+
toolId: string;
|
|
29
|
+
}>(patch: T): T;
|
|
30
|
+
/** Whether `record` was created by {@link toolCatalogPatch}. */
|
|
31
|
+
export declare function isToolCatalogPatch(record: object): boolean;
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.toolCatalogPatchFields = exports.toolCatalogSupplementalFields = exports.toolCatalogContractDescriptiveFields = exports.toolCatalogIdentityFields = void 0;
|
|
4
|
+
exports.toolCatalogPatch = toolCatalogPatch;
|
|
5
|
+
exports.isToolCatalogPatch = isToolCatalogPatch;
|
|
6
|
+
/** Catalog-owned identity and state columns. Runtimes before Tools v2 do not know them. */
|
|
7
|
+
exports.toolCatalogIdentityFields = [
|
|
8
|
+
"contractId",
|
|
9
|
+
"contractVersion",
|
|
10
|
+
"toolName",
|
|
11
|
+
"catalogIdentity",
|
|
12
|
+
"catalogState",
|
|
13
|
+
"catalogReason",
|
|
14
|
+
];
|
|
15
|
+
/**
|
|
16
|
+
* Presentation fields described by a public contract member. The catalog refreshes
|
|
17
|
+
* them from a published definition when provenance is absent or matches its provider.
|
|
18
|
+
*/
|
|
19
|
+
exports.toolCatalogContractDescriptiveFields = [
|
|
20
|
+
"name",
|
|
21
|
+
"usageDescription",
|
|
22
|
+
"inputSchema",
|
|
23
|
+
"outputSchema",
|
|
24
|
+
];
|
|
25
|
+
/** Supplemental presentation and runner configuration, independent of executable code. */
|
|
26
|
+
exports.toolCatalogSupplementalFields = [
|
|
27
|
+
"configSchema",
|
|
28
|
+
"isAssistantRunner",
|
|
29
|
+
"detailedDescription",
|
|
30
|
+
];
|
|
31
|
+
/**
|
|
32
|
+
* Columns a catalog patch may write. Authorization fields (`accessLevel`,
|
|
33
|
+
* `remoteAccessLevel`, `authorityPropagation`) deliberately remain outside this list:
|
|
34
|
+
* catalog backfill and metadata refresh must never clear or rewrite live access policy.
|
|
35
|
+
*/
|
|
36
|
+
exports.toolCatalogPatchFields = [
|
|
37
|
+
"toolId",
|
|
38
|
+
...exports.toolCatalogIdentityFields,
|
|
39
|
+
...exports.toolCatalogContractDescriptiveFields,
|
|
40
|
+
...exports.toolCatalogSupplementalFields,
|
|
41
|
+
];
|
|
42
|
+
// Registered globally so a duplicated SDK module still recognizes the marker. The
|
|
43
|
+
// property is non-enumerable: it is never copied by spreads, persisted or synced.
|
|
44
|
+
const toolCatalogPatchMarker = Symbol.for("peers.tools-catalog.patch");
|
|
45
|
+
/**
|
|
46
|
+
* Mark a restricted Tools update written by the tool catalog. SQL merges a marked
|
|
47
|
+
* update into the stored row and tracked storage records only the paths it changes,
|
|
48
|
+
* so a concurrent policy change cannot be reverted by a stale snapshot. Unmarked
|
|
49
|
+
* updates keep row-replacement semantics: a full save that omits access policy
|
|
50
|
+
* still clears it. Identity is validated against the merged row in both cases.
|
|
51
|
+
*
|
|
52
|
+
* @param patch - Primary key plus fields from {@link toolCatalogPatchFields}.
|
|
53
|
+
* @returns The same object, marked.
|
|
54
|
+
* @throws If the patch names a field outside {@link toolCatalogPatchFields}.
|
|
55
|
+
*/
|
|
56
|
+
function toolCatalogPatch(patch) {
|
|
57
|
+
for (const key of Object.keys(patch)) {
|
|
58
|
+
if (!exports.toolCatalogPatchFields.includes(key))
|
|
59
|
+
throw new Error(`Tool catalog patches cannot write ${key}`);
|
|
60
|
+
}
|
|
61
|
+
Object.defineProperty(patch, toolCatalogPatchMarker, { value: true });
|
|
62
|
+
return patch;
|
|
63
|
+
}
|
|
64
|
+
/** Whether `record` was created by {@link toolCatalogPatch}. */
|
|
65
|
+
function isToolCatalogPatch(record) {
|
|
66
|
+
return record[toolCatalogPatchMarker] === true;
|
|
67
|
+
}
|