@peers-app/peers-sdk 0.25.20 → 0.26.0

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.
Files changed (94) hide show
  1. package/dist/contracts/__tests__/builder.test.js +0 -2
  2. package/dist/contracts/__tests__/contract-provider-router.test.js +51 -0
  3. package/dist/contracts/__tests__/contract-proxy.test.js +21 -0
  4. package/dist/contracts/__tests__/contract-resolvers.test.js +84 -0
  5. package/dist/contracts/__tests__/legacy-removal.test.d.ts +1 -0
  6. package/dist/contracts/__tests__/legacy-removal.test.js +90 -0
  7. package/dist/contracts/__tests__/system-contracts.test.js +88 -0
  8. package/dist/contracts/__tests__/tool-catalog-inventory.test.d.ts +1 -0
  9. package/dist/contracts/__tests__/tool-catalog-inventory.test.js +44 -0
  10. package/dist/contracts/__tests__/tool-catalog-service.test.d.ts +1 -0
  11. package/dist/contracts/__tests__/tool-catalog-service.test.js +446 -0
  12. package/dist/contracts/__tests__/tool-catalog.test.d.ts +1 -0
  13. package/dist/contracts/__tests__/tool-catalog.test.js +649 -0
  14. package/dist/contracts/__tests__/workflow-context.test.d.ts +1 -0
  15. package/dist/contracts/__tests__/workflow-context.test.js +61 -0
  16. package/dist/contracts/builder.d.ts +9 -8
  17. package/dist/contracts/builder.js +34 -16
  18. package/dist/contracts/contract-provider-router.d.ts +12 -1
  19. package/dist/contracts/contract-provider-router.js +40 -2
  20. package/dist/contracts/contract-proxy.d.ts +20 -6
  21. package/dist/contracts/contract-proxy.js +23 -12
  22. package/dist/contracts/contract-resolvers.d.ts +22 -3
  23. package/dist/contracts/contract-resolvers.js +86 -7
  24. package/dist/contracts/extract.d.ts +7 -0
  25. package/dist/contracts/extract.js +3 -0
  26. package/dist/contracts/index.d.ts +11 -4
  27. package/dist/contracts/index.js +14 -2
  28. package/dist/contracts/registry.d.ts +3 -0
  29. package/dist/contracts/registry.js +2 -0
  30. package/dist/contracts/system/desktop-tools-contract.d.ts +5 -0
  31. package/dist/contracts/system/desktop-tools-contract.js +88 -0
  32. package/dist/contracts/system/host-tools-contract.d.ts +5 -0
  33. package/dist/contracts/system/host-tools-contract.js +76 -0
  34. package/dist/contracts/system/index.d.ts +3 -0
  35. package/dist/contracts/system/index.js +3 -0
  36. package/dist/contracts/system/tool-catalog-contract.d.ts +73 -0
  37. package/dist/contracts/system/tool-catalog-contract.js +70 -0
  38. package/dist/contracts/system-contracts.d.ts +3 -1
  39. package/dist/contracts/system-contracts.js +20 -6
  40. package/dist/contracts/tool-catalog-identity.d.ts +35 -0
  41. package/dist/contracts/tool-catalog-identity.js +88 -0
  42. package/dist/contracts/tool-catalog-migrations.d.ts +12 -0
  43. package/dist/contracts/tool-catalog-migrations.js +179 -0
  44. package/dist/contracts/tool-catalog-service.d.ts +30 -0
  45. package/dist/contracts/tool-catalog-service.js +287 -0
  46. package/dist/contracts/tool-catalog.d.ts +91 -0
  47. package/dist/contracts/tool-catalog.js +490 -0
  48. package/dist/contracts/types.d.ts +7 -5
  49. package/dist/data/messages.d.ts +3 -3
  50. package/dist/data/orm/sql.data-source.d.ts +5 -0
  51. package/dist/data/orm/sql.data-source.js +28 -10
  52. package/dist/data/orm/sql.data-source.test.d.ts +1 -1
  53. package/dist/data/orm/sql.data-source.test.js +160 -0
  54. package/dist/data/orm/table-container.d.ts +14 -0
  55. package/dist/data/orm/table-container.js +20 -0
  56. package/dist/data/tool-access-approvals.d.ts +6 -6
  57. package/dist/data/tool-catalog-fields.d.ts +31 -0
  58. package/dist/data/tool-catalog-fields.js +67 -0
  59. package/dist/data/tools.d.ts +38 -10
  60. package/dist/data/tools.js +31 -8
  61. package/dist/data/workflow-logs.d.ts +6 -2
  62. package/dist/data/workflow-logs.js +7 -5
  63. package/dist/data/workflow-runs.d.ts +6 -6
  64. package/dist/index.d.ts +0 -2
  65. package/dist/index.js +1 -3
  66. package/dist/package-installer/package-creator.d.ts +14 -5
  67. package/dist/package-installer/package-creator.js +92 -37
  68. package/dist/package-installer/package-creator.test.js +308 -23
  69. package/dist/package-installer/types.d.ts +8 -0
  70. package/dist/package-loader/contract-package-loader.d.ts +12 -3
  71. package/dist/package-loader/contract-package-loader.js +54 -50
  72. package/dist/package-loader/contract-package-loader.test.d.ts +1 -1
  73. package/dist/package-loader/contract-package-loader.test.js +172 -13
  74. package/dist/package-loader/host-authority-loading.test.js +16 -3
  75. package/dist/package-loader/isolated-artifact.d.ts +14 -2
  76. package/dist/package-loader/isolated-artifact.js +6 -0
  77. package/dist/package-loader/isolated-artifact.test.js +34 -0
  78. package/dist/package-loader/isolated-runtime.d.ts +14 -2
  79. package/dist/package-loader/isolated-table-gateway.d.ts +3 -1
  80. package/dist/package-loader/isolated-table-gateway.js +54 -5
  81. package/dist/package-loader/isolated-table-gateway.test.js +123 -0
  82. package/dist/package-loader/package-loader.d.ts +14 -2
  83. package/dist/package-loader/package-loader.js +184 -62
  84. package/dist/package-loader/package-loader.test.js +695 -2
  85. package/dist/package-loader/resolve-consumed-contracts.js +1 -1
  86. package/dist/system-ids.d.ts +6 -0
  87. package/dist/system-ids.js +7 -1
  88. package/dist/types/peers-package.d.ts +3 -2
  89. package/dist/types/workflow-run-context.d.ts +12 -2
  90. package/package.json +1 -1
  91. package/dist/tools/index.d.ts +0 -1
  92. package/dist/tools/index.js +0 -17
  93. package/dist/tools/tools-factory.d.ts +0 -5
  94. 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
- await this.ensureTableAndIndexesExist();
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
- // create new table
132
- await this.ensureTableAndIndexesExist();
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
- this.tableBuild = this.ensureTableIsNewest();
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
- const updateFields = this.fieldsForUpdate(record);
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 record;
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 record;
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
- export {};
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
+ }